diff --git a/config/neovim/config.lua b/config/neovim/config.lua index 89a173f5..0c4e04bf 100644 --- a/config/neovim/config.lua +++ b/config/neovim/config.lua @@ -60,397 +60,24 @@ do end -- }}} -vim.loader.disable() +vim.loader.enable() vim.cmd([[ ]]) require("lazy").setup({ dev = { - path = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins", + path = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins", patterns = { "." }, fallback = false, }, spec = { - { - "catppuccin", - ["config"] = function(_, opts) - require("catppuccin").setup(opts) - - vim.cmd([[ - let $BAT_THEME = "catppuccin" - colorscheme catppuccin - ]]) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/catppuccin-nvim", - ["lazy"] = false, - ["name"] = "catppuccin", - ["opts"] = { ["background"] = { ["dark"] = "mocha", ["light"] = "latte" }, ["flavour"] = "latte" }, - ["priority"] = 1000, - }, - { - "lspconfig", - ["config"] = function(_, opts) - local __lspOnAttach = function(client, bufnr) end - - local __lspCapabilities = function() - capabilities = vim.lsp.protocol.make_client_capabilities() - capabilities = - vim.tbl_deep_extend("force", capabilities, require("cmp_nvim_lsp").default_capabilities()) - return capabilities - end - - local __setup = { - on_attach = __lspOnAttach, - capabilities = __lspCapabilities(), - } - - for i, server in ipairs({ - { ["name"] = "cmake" }, - { ["name"] = "clojure_lsp" }, - { ["name"] = "clangd" }, - { ["name"] = "texlab" }, - { ["name"] = "rust_analyzer" }, - { ["name"] = "pyright" }, - { ["name"] = "nil_ls" }, - { ["cmd"] = { "haskell-language-server-wrapper", "--lsp" }, ["name"] = "hls" }, - }) do - if type(server) == "string" then - require("lspconfig")[server].setup(__setup) - else - local options = server.extraOptions - - if options == nil then - options = __setup - else - options = vim.tbl_extend("keep", options, __setup) - end - - require("lspconfig")[server.name].setup(options) - end - end - end, - ["dependencies"] = { - { - "cmp", - ["config"] = function(_, opts) - require("cmp").setup(opts) - end, - ["dependencies"] = { - { - "cmp-async-path", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/cmp-async-path", - ["name"] = "cmp-async-path", - }, - { - "cmp-buffer", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/cmp-buffer", - ["enabled"] = false, - ["name"] = "cmp-buffer", - }, - { - "cmp-cmdline", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/cmp-cmdline", - ["enabled"] = false, - ["name"] = "cmp-cmdline", - }, - { - "cmp-emoji", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/cmp-emoji", - ["name"] = "cmp-emoji", - }, - { - "cmp-nvim-lsp", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/cmp-nvim-lsp", - ["name"] = "cmp-nvim-lsp", - }, - { - "cmp-nvim-lsp-signature-help", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/cmp-nvim-lsp-signature-help", - ["name"] = "cmp-nvim-lsp-signature-help", - }, - { - "cmp-luasnip", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/cmp_luasnip", - ["name"] = "cmp-luasnip", - }, - }, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-cmp", - ["lazy"] = false, - ["name"] = "cmp", - ["opts"] = function() - local cmp = require("cmp") - local luasnip = require("luasnip") - - local has_words_before = function() - unpack = unpack or table.unpack - local line, col = unpack(vim.api.nvim_win_get_cursor(0)) - return col ~= 0 - and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") - == nil - end - - return { - sources = cmp.config.sources({ - { ["name"] = "async_path" }, - { ["name"] = "emoji" }, - { ["name"] = "nvim_lsp" }, - { ["name"] = "nvim_lsp_signature_help" }, - { ["name"] = "luasnip" }, - }), - - snippet = { - expand = function(args) - require("luasnip").lsp_expand(args.body) - end, - }, - - window = { - completion = cmp.config.window.bordered(), - documentation = cmp.config.window.bordered(), - -- completion.border = "rounded", - -- documentation.border = "rounded", - }, - - mapping = cmp.mapping.preset.insert({ - [""] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Select }), - [""] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Select }), - [""] = cmp.mapping.abort(), - [""] = cmp.mapping.abort(), - [""] = cmp.mapping.scroll_docs(-4), - [""] = cmp.mapping.scroll_docs(4), - [""] = cmp.mapping.complete({}), - - [""] = cmp.mapping.confirm({ select = true }), - - [""] = cmp.mapping(function(fallback) - if cmp.visible() then - cmp.select_next_item() - elseif require("luasnip").expand_or_jumpable() then - require("luasnip").expand_or_jump() - elseif has_words_before() then - cmp.complete() - else - fallback() - end - end, { "i", "s" }), - - [""] = cmp.mapping(function(fallback) - if cmp.visible() then - cmp.select_prev_item() - elseif luasnip.jumpable(-1) then - luasnip.jump(-1) - else - fallback() - end - end, { "i", "s" }), - }), - } - end, - }, - { - "illuminate", - ["config"] = function(_, opts) - require("illuminate").configure(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/vim-illuminate", - ["lazy"] = false, - ["name"] = "illuminate", - ["opts"] = { - ["filetypesDenylist"] = { - "DressingSelect", - "Outline", - "TelescopePrompt", - "alpha", - "harpoon", - "toggleterm", - "neo-tree", - "Spectre", - "reason", - }, - }, - }, - }, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-lspconfig", - ["lazy"] = false, - ["name"] = "lspconfig", - }, - { - "web-devicons", - ["config"] = function(_, opts) - require("nvim-web-devicons").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-web-devicons", - ["lazy"] = false, - ["name"] = "web-devicons", - }, - { - "better-escape", - ["config"] = function(_, opts) - require("better_escape").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/better-escape.nvim", - ["lazy"] = false, - ["name"] = "better-escape", - ["opts"] = { ["mapping"] = { "jk" }, ["timeout"] = 200 }, - }, - { - "chadtree", - ["config"] = function(_, opts) - vim.api.nvim_set_var("chadtree_settings", opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/chadtree", - ["name"] = "chadtree", - ["opts"] = { ["theme"] = { ["text_colour_set"] = "nerdtree_syntax_dark" }, ["xdg"] = true }, - }, - { - "comment", - ["config"] = function(_, opts) - require("Comment").setup(opts) - end, - ["dependencies"] = { - { - "ts-context-commentstring", - ["config"] = function(_, opts) - vim.g.skip_ts_context_commentstring_module = true -- Skip compatibility checks - - require("ts_context_commentstring").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-ts-context-commentstring", - ["lazy"] = false, - ["name"] = "ts-context-commentstring", - }, - }, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/comment.nvim", - ["name"] = "comment", - ["opts"] = { - ["mappings"] = { ["basic"] = true, ["extra"] = false }, - ["opleader"] = { ["block"] = "", ["line"] = "" }, - ["pre_hook"] = function() - require("ts_context_commentstring.integrations.comment_nvim").create_pre_hook() - end, - ["toggler"] = { ["block"] = "", ["line"] = "" }, - }, - }, - { - "conform", - ["config"] = function(_, opts) - require("conform").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/conform.nvim", - ["name"] = "conform", - ["opts"] = { - ["formatters_by_ft"] = { - ["c"] = { "clang-format" }, - ["cpp"] = { "clang-format" }, - ["css"] = { { "prettierd", "prettier" } }, - ["h"] = { "clang-format" }, - ["hpp"] = { "clang-format" }, - ["html"] = { { "prettierd", "prettier" } }, - ["java"] = { "google-java-format" }, - ["javascript"] = { { "prettierd", "prettier" } }, - ["markdown"] = { { "prettierd", "prettier" } }, - ["nix"] = { "alejandra" }, - ["python"] = { "black" }, - ["rust"] = { "rustfmt" }, - }, - }, - }, - { - "flash", - ["config"] = function(_, opts) - require("flash").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/flash.nvim", - ["name"] = "flash", - }, - { - "gitmessenger", - ["config"] = function(_, opts) - for k, v in pairs(opts) do - vim.g[k] = v - end - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/git-messenger.vim", - ["name"] = "gitmessenger", - ["opts"] = { - ["git_messenger_floating_win_opts"] = { ["border"] = "rounded" }, - ["git_messenger_no_default_mappings"] = true, - }, - }, - { - "gitsigns", - ["config"] = function(_, opts) - require("gitsigns").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/gitsigns.nvim", - ["lazy"] = false, - ["name"] = "gitsigns", - ["opts"] = { ["current_line_blame"] = false }, - }, - { - "headlines", - ["config"] = function(_, opts) - require("headlines").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/headlines.nvim", - ["name"] = "headlines", - }, - { - "intellitab", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/intellitab.nvim", - ["lazy"] = false, - ["name"] = "intellitab", - }, - { - "lastplace", - ["config"] = function(_, opts) - require("nvim-lastplace").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-lastplace", - ["lazy"] = false, - ["name"] = "lastplace", - }, - { - "lazygit", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/lazygit.nvim", - ["name"] = "lazygit", - }, - { - "lint", - ["config"] = function(_, opts) - local lint = require("lint") - - for k, v in pairs(opts) do - lint[k] = v - end - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-lint", - ["lazy"] = false, - ["name"] = "lint", - ["opts"] = { - ["linters_by_ft"] = { - ["c"] = { "clang-tidy" }, - ["clojure"] = { "clj-kondo" }, - ["cpp"] = { "clang-tidy" }, - ["h"] = { "clang-tidy" }, - ["hpp"] = { "clang-tidy" }, - ["java"] = { "checkstyle" }, - ["javascript"] = { "eslint_d" }, - ["markdown"] = { "vale" }, - ["nix"] = { "statix" }, - ["python"] = { "flake8" }, - ["rust"] = { "clippy" }, - ["text"] = { "vale" }, - }, - }, - }, { "lualine", ["config"] = function(_, opts) require("lualine").setup(opts) end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/lualine.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/lualine.nvim", ["lazy"] = false, ["name"] = "lualine", ["opts"] = { @@ -473,33 +100,6 @@ require("lazy").setup({ ["tabline"] = { ["lualine_a"] = { "buffers" }, ["lualine_z"] = { "tabs" } }, }, }, - { - "luasnip", - ["config"] = function(_, opts) - require("luasnip").config.set_config(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/luasnip", - ["name"] = "luasnip", - }, - { - "navbuddy", - ["config"] = function(_, opts) - local actions = require("nvim-navbuddy.actions") -- ? - require("nvim-navbuddy").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-navbuddy", - ["name"] = "navbuddy", - ["opts"] = { ["lsp"] = { ["auto_attach"] = true }, ["window"] = { ["border"] = "rounded" } }, - }, - { - "navic", - ["config"] = function(_, opts) - require("nvim-navic").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-navic", - ["name"] = "navic", - ["opts"] = { ["click"] = true, ["highlight"] = true, ["lsp"] = { ["auto_attach"] = true } }, - }, { "noice", ["config"] = function(_, opts) @@ -508,12 +108,12 @@ require("lazy").setup({ ["dependencies"] = { { "nui", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nui.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nui.nvim", ["lazy"] = false, ["name"] = "nui", }, }, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/noice.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/noice.nvim", ["lazy"] = false, ["name"] = "noice", ["opts"] = { @@ -555,61 +155,10 @@ require("lazy").setup({ vim.notify = require("notify") require("notify").setup(opts) end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-notify", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-notify", ["lazy"] = false, ["name"] = "notify", }, - { - "autopairs", - ["config"] = function(_, opts) - require("nvim-autopairs").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-autopairs", - ["lazy"] = false, - ["name"] = "autopairs", - }, - { - "colorizer", - ["config"] = function(_, opts) - require("colorizer").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-colorizer.lua", - ["lazy"] = false, - ["name"] = "colorizer", - }, - { - "ufo", - ["config"] = function(_, opts) - require("ufo").setup(opts) - end, - ["dependencies"] = { - { - "promise", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/promise-async", - ["name"] = "promise", - }, - }, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-ufo", - ["name"] = "ufo", - }, - { - "rainbow-delimiters", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/rainbow-delimiters.nvim", - ["lazy"] = false, - ["name"] = "rainbow-delimiters", - }, - { - "sandwich", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/vim-sandwich", - ["lazy"] = false, - ["name"] = "sandwich", - }, - { - "sleuth", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/vim-sleuth", - ["lazy"] = false, - ["name"] = "sleuth", - }, { "telescope", ["config"] = function(_, opts) @@ -623,26 +172,26 @@ require("lazy").setup({ ["dependencies"] = { { "plenary", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/plenary.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/plenary.nvim", ["name"] = "plenary", }, { "telescope-undo", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/telescope-undo.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/telescope-undo.nvim", ["name"] = "telescope-undo", }, { "telescope-ui-select", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/telescope-ui-select.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/telescope-ui-select.nvim", ["name"] = "telescope-ui-select", }, { "telescope-fzf-native", - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/telescope-fzf-native.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim", ["name"] = "telescope-fzf-native", }, }, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/telescope.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/telescope.nvim", ["lazy"] = false, ["name"] = "telescope", ["opts"] = { @@ -658,37 +207,159 @@ require("lazy").setup({ }, }, { - "toggleterm", + "which-key", ["config"] = function(_, opts) - require("toggleterm").setup(opts) + require("which-key").setup(opts) end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/toggleterm.nvim", - ["name"] = "toggleterm", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/which-key.nvim", + ["lazy"] = false, + ["name"] = "which-key", + ["priority"] = 100, + }, + { + "clangd-extensions", + ["config"] = function(_, opts) + require("clangd_extensions").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/clangd_extensions.nvim", + ["name"] = "clangd-extensions", + }, + { + "conform", + ["config"] = function(_, opts) + require("conform").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/conform.nvim", + ["name"] = "conform", ["opts"] = { - ["auto_scroll"] = true, - ["close_on_exit"] = true, - ["direction"] = "horizontal", - ["float_opts"] = { ["border"] = "curved", ["height"] = 20, ["width"] = 80, ["winblend"] = 0 }, - ["hide_numbers"] = true, - ["insert_mappings"] = true, - ["open_mapping"] = "[[]]", - ["persist_mode"] = true, - ["shade_terminals"] = true, - ["shell"] = "fish", - ["start_in_insert"] = true, - ["terminal_mappings"] = true, + ["formatters_by_ft"] = { + ["c"] = { "clang-format" }, + ["cpp"] = { "clang-format" }, + ["css"] = { { "prettierd", "prettier" } }, + ["h"] = { "clang-format" }, + ["hpp"] = { "clang-format" }, + ["html"] = { { "prettierd", "prettier" } }, + ["java"] = { "google-java-format" }, + ["javascript"] = { { "prettierd", "prettier" } }, + ["lua"] = { "stylua" }, + ["markdown"] = { { "prettierd", "prettier" } }, + ["nix"] = { "alejandra" }, + ["python"] = { "black" }, + ["rust"] = { "rustfmt" }, + }, }, }, + { + "haskell-tools", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/haskell-tools.nvim", + ["name"] = "haskell-tools", + }, + { + "lint", + ["config"] = function(_, opts) + local lint = require("lint") + + for k, v in pairs(opts) do + lint[k] = v + end + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-lint", + ["lazy"] = false, + ["name"] = "lint", + ["opts"] = { + ["linters_by_ft"] = { + ["c"] = { "clang-tidy" }, + ["clojure"] = { "clj-kondo" }, + ["cpp"] = { "clang-tidy" }, + ["h"] = { "clang-tidy" }, + ["hpp"] = { "clang-tidy" }, + ["java"] = { "checkstyle" }, + ["javascript"] = { "eslint_d" }, + ["lua"] = { "luacheck" }, + ["markdown"] = { "vale" }, + ["nix"] = { "statix" }, + ["python"] = { "flake8" }, + ["rust"] = { "clippy" }, + ["text"] = { "vale" }, + }, + }, + }, + { + "lspconfig", + ["config"] = function(_, opts) + local __lspOnAttach = function(client, bufnr) end + + local __lspCapabilities = function() + capabilities = vim.lsp.protocol.make_client_capabilities() + capabilities = + vim.tbl_deep_extend("force", capabilities, require("cmp_nvim_lsp").default_capabilities()) + return capabilities + end + + local __setup = { + on_attach = __lspOnAttach, + capabilities = __lspCapabilities(), + } + + for i, server in ipairs({ + { ["name"] = "clangd" }, + { ["name"] = "clojure_lsp" }, + { ["name"] = "cmake" }, + { ["name"] = "lua_ls" }, + { ["name"] = "nil_ls" }, + { ["name"] = "pyright" }, + { ["name"] = "texlab" }, + }) do + if type(server) == "string" then + require("lspconfig")[server].setup(__setup) + else + local options = server.extraOptions + + if options == nil then + options = __setup + else + options = vim.tbl_extend("keep", options, __setup) + end + + require("lspconfig")[server.name].setup(options) + end + end + end, + ["dependencies"] = { + { + "neodev", + ["config"] = function(_, opts) + require("neodev").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/neodev.nvim", + ["name"] = "neodev", + }, + }, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-lspconfig", + ["lazy"] = false, + ["name"] = "lspconfig", + }, + { + "rustaceanvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/rustaceanvim", + ["name"] = "rustaceanvim", + }, { "treesitter", ["config"] = function(_, opts) + vim.opt.runtimepath:append( + "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/nvim-treesitter" + ) + vim.opt.runtimepath:append("/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/treesitter-parsers") + require("nvim-treesitter.configs").setup(opts) end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/nvim-treesitter", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-treesitter", ["lazy"] = false, ["name"] = "treesitter", ["opts"] = { - ["highlight"] = { ["enable"] = true }, + ["auto_install"] = false, + ["highlight"] = { ["additional_vim_regex_highlighting"] = false, ["enable"] = true }, ["incremental_selection"] = { ["enable"] = true, ["keymaps"] = { @@ -699,6 +370,371 @@ require("lazy").setup({ }, }, ["indent"] = { ["enable"] = true }, + ["parser_install_dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/treesitter-parsers", + }, + }, + { + "catppuccin", + ["config"] = function(_, opts) + require("catppuccin").setup(opts) + + vim.cmd([[ + let $BAT_THEME = "catppuccin" + colorscheme catppuccin + ]]) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/catppuccin-nvim", + ["lazy"] = false, + ["name"] = "catppuccin", + ["opts"] = { ["background"] = { ["dark"] = "mocha", ["light"] = "latte" }, ["flavour"] = "mocha" }, + ["priority"] = 1000, + }, + { + "web-devicons", + ["config"] = function(_, opts) + require("nvim-web-devicons").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-web-devicons", + ["lazy"] = false, + ["name"] = "web-devicons", + }, + { + "better-escape", + ["config"] = function(_, opts) + require("better_escape").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/better-escape.nvim", + ["lazy"] = false, + ["name"] = "better-escape", + ["opts"] = { ["mapping"] = { "jk" }, ["timeout"] = 200 }, + }, + { + "chadtree", + ["config"] = function(_, opts) + vim.api.nvim_set_var("chadtree_settings", opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/chadtree", + ["name"] = "chadtree", + ["opts"] = { ["theme"] = { ["text_colour_set"] = "nerdtree_syntax_dark" }, ["xdg"] = true }, + }, + { + "cmp", + ["config"] = function(_, opts) + require("cmp").setup(opts) + end, + ["dependencies"] = { + { + "cmp-async-path", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/cmp-async-path", + ["name"] = "cmp-async-path", + }, + { + "cmp-buffer", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/cmp-buffer", + ["enabled"] = false, + ["name"] = "cmp-buffer", + }, + { + "cmp-cmdline", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/cmp-cmdline", + ["enabled"] = false, + ["name"] = "cmp-cmdline", + }, + { + "cmp-emoji", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/cmp-emoji", + ["name"] = "cmp-emoji", + }, + { + "cmp-nvim-lsp", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/cmp-nvim-lsp", + ["name"] = "cmp-nvim-lsp", + }, + { + "cmp-nvim-lsp-signature-help", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/cmp-nvim-lsp-signature-help", + ["name"] = "cmp-nvim-lsp-signature-help", + }, + { + "cmp-luasnip", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/cmp_luasnip", + ["name"] = "cmp-luasnip", + }, + }, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-cmp", + ["lazy"] = false, + ["name"] = "cmp", + ["opts"] = function() + local cmp = require("cmp") + local luasnip = require("luasnip") + + local has_words_before = function() + unpack = unpack or table.unpack + local line, col = unpack(vim.api.nvim_win_get_cursor(0)) + return col ~= 0 + and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil + end + + return { + sources = cmp.config.sources({ + { ["name"] = "async_path" }, + { ["name"] = "emoji" }, + { ["name"] = "nvim_lsp" }, + { ["name"] = "nvim_lsp_signature_help" }, + { ["name"] = "luasnip" }, + }), + + snippet = { + expand = function(args) + require("luasnip").lsp_expand(args.body) + end, + }, + + window = { + completion = cmp.config.window.bordered(), + documentation = cmp.config.window.bordered(), + -- completion.border = "rounded", + -- documentation.border = "rounded", + }, + + mapping = cmp.mapping.preset.insert({ + [""] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Select }), + [""] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Select }), + [""] = cmp.mapping.abort(), + [""] = cmp.mapping.abort(), + [""] = cmp.mapping.scroll_docs(-4), + [""] = cmp.mapping.scroll_docs(4), + [""] = cmp.mapping.complete({}), + + [""] = cmp.mapping.confirm({ select = true }), + + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_next_item() + elseif require("luasnip").expand_or_jumpable() then + require("luasnip").expand_or_jump() + elseif has_words_before() then + cmp.complete() + else + fallback() + end + end, { "i", "s" }), + + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_prev_item() + elseif luasnip.jumpable(-1) then + luasnip.jump(-1) + else + fallback() + end + end, { "i", "s" }), + }), + } + end, + }, + { + "comment", + ["config"] = function(_, opts) + require("Comment").setup(opts) + end, + ["dependencies"] = { + { + "ts-context-commentstring", + ["config"] = function(_, opts) + vim.g.skip_ts_context_commentstring_module = true -- Skip compatibility checks + + require("ts_context_commentstring").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring", + ["lazy"] = false, + ["name"] = "ts-context-commentstring", + }, + }, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/comment.nvim", + ["name"] = "comment", + ["opts"] = { + ["mappings"] = { ["basic"] = true, ["extra"] = false }, + ["opleader"] = { ["block"] = "", ["line"] = "" }, + ["pre_hook"] = function() + require("ts_context_commentstring.integrations.comment_nvim").create_pre_hook() + end, + ["toggler"] = { ["block"] = "", ["line"] = "" }, + }, + }, + { + "flash", + ["config"] = function(_, opts) + require("flash").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/flash.nvim", + ["name"] = "flash", + }, + { + "gitmessenger", + ["config"] = function(_, opts) + for k, v in pairs(opts) do + vim.g[k] = v + end + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/git-messenger.vim", + ["name"] = "gitmessenger", + ["opts"] = { + ["git_messenger_floating_win_opts"] = { ["border"] = "rounded" }, + ["git_messenger_no_default_mappings"] = true, + }, + }, + { + "gitsigns", + ["config"] = function(_, opts) + require("gitsigns").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/gitsigns.nvim", + ["lazy"] = false, + ["name"] = "gitsigns", + ["opts"] = { ["current_line_blame"] = false }, + }, + { + "illuminate", + ["config"] = function(_, opts) + require("illuminate").configure(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/vim-illuminate", + ["lazy"] = false, + ["name"] = "illuminate", + ["opts"] = { + ["filetypesDenylist"] = { + "DressingSelect", + "Outline", + "TelescopePrompt", + "alpha", + "harpoon", + "toggleterm", + "neo-tree", + "Spectre", + "reason", + }, + }, + }, + { + "intellitab", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/intellitab.nvim", + ["lazy"] = false, + ["name"] = "intellitab", + }, + { + "lastplace", + ["config"] = function(_, opts) + require("nvim-lastplace").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-lastplace", + ["lazy"] = false, + ["name"] = "lastplace", + }, + { + "lazygit", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/lazygit.nvim", + ["name"] = "lazygit", + }, + { + "luasnip", + ["config"] = function(_, opts) + require("luasnip").config.set_config(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/luasnip", + ["name"] = "luasnip", + }, + { + "navbuddy", + ["config"] = function(_, opts) + local actions = require("nvim-navbuddy.actions") -- ? + require("nvim-navbuddy").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-navbuddy", + ["name"] = "navbuddy", + ["opts"] = { ["lsp"] = { ["auto_attach"] = true }, ["window"] = { ["border"] = "rounded" } }, + }, + { + "navic", + ["config"] = function(_, opts) + require("nvim-navic").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-navic", + ["name"] = "navic", + ["opts"] = { ["click"] = true, ["highlight"] = true, ["lsp"] = { ["auto_attach"] = true } }, + }, + { + "autopairs", + ["config"] = function(_, opts) + require("nvim-autopairs").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-autopairs", + ["lazy"] = false, + ["name"] = "autopairs", + }, + { + "colorizer", + ["config"] = function(_, opts) + require("colorizer").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-colorizer.lua", + ["lazy"] = false, + ["name"] = "colorizer", + }, + { + "ufo", + ["config"] = function(_, opts) + require("ufo").setup(opts) + end, + ["dependencies"] = { + { + "promise", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/promise-async", + ["name"] = "promise", + }, + }, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/nvim-ufo", + ["name"] = "ufo", + }, + { + "rainbow-delimiters", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim", + ["lazy"] = false, + ["name"] = "rainbow-delimiters", + }, + { + "sandwich", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/vim-sandwich", + ["lazy"] = false, + ["name"] = "sandwich", + }, + { + "sleuth", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/vim-sleuth", + ["lazy"] = false, + ["name"] = "sleuth", + }, + { + "toggleterm", + ["config"] = function(_, opts) + require("toggleterm").setup(opts) + end, + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/toggleterm.nvim", + ["lazy"] = false, + ["name"] = "toggleterm", + ["opts"] = { + ["auto_scroll"] = true, + ["close_on_exit"] = true, + ["direction"] = "horizontal", + ["float_opts"] = { ["border"] = "curved", ["height"] = 20, ["width"] = 80, ["winblend"] = 0 }, + ["hide_numbers"] = true, + ["insert_mappings"] = true, + ["open_mapping"] = [[]], + ["persist_mode"] = true, + ["shade_terminals"] = true, + ["shell"] = "fish", + ["start_in_insert"] = true, + ["terminal_mappings"] = true, }, }, { @@ -706,7 +742,7 @@ require("lazy").setup({ ["config"] = function(_, opts) require("trim").setup(opts) end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/trim.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/trim.nvim", ["lazy"] = false, ["name"] = "trim", }, @@ -715,26 +751,16 @@ require("lazy").setup({ ["config"] = function(_, opts) require("trouble").setup(opts) end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/trouble.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/trouble.nvim", ["name"] = "trouble", }, - { "bbye", ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/vim-bbye", ["name"] = "bbye" }, - { - "which-key", - ["config"] = function(_, opts) - require("which-key").setup(opts) - end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/which-key.nvim", - ["lazy"] = false, - ["name"] = "which-key", - ["priority"] = 100, - }, + { "bbye", ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/vim-bbye", ["name"] = "bbye" }, { "yanky", ["config"] = function(_, opts) require("yanky").setup(opts) end, - ["dir"] = "/nix/store/q4kqqb39sgasra0y3v6ci25605wqx6id-lazy-plugins/yanky.nvim", + ["dir"] = "/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store/lazy-plugins/yanky.nvim", ["lazy"] = false, ["name"] = "yanky", }, @@ -886,6 +912,7 @@ do ["mode"] = "n", ["options"] = { ["desc"] = "Show help tags" }, }, + { ["action"] = "+quit", ["key"] = "q", ["mode"] = "n" }, { ["action"] = "+buffers", ["key"] = "b", ["mode"] = "n" }, { ["action"] = "Telescope buffers", @@ -1090,13 +1117,20 @@ end -- Set up autocommands {{ do - local __nixvim_autocommands = - { { + local __nixvim_autocommands = { + { ["callback"] = function() require("lint").try_lint() end, ["event"] = { "BufWritePost" }, - } } + }, + { + ["callback"] = function() + require("conform").format() + end, + ["event"] = { "BufWritePre" }, + }, + } for _, autocmd in ipairs(__nixvim_autocommands) do vim.api.nvim_create_autocmd(autocmd.event, { @@ -1119,21 +1153,21 @@ local o = vim.o -- Neovide if g.neovide then - require("notify").notify("Running in NeoVide") + -- require("notify").notify("Running in NeoVide") g.neovide_cursor_animate_command_line = true g.neovide_cursor_animate_in_insert_mode = true - g.neovide_fullscreen = false - g.neovide_hide_mouse_when_typing = false + -- g.neovide_fullscreen = false + g.neovide_hide_mouse_when_typing = true g.neovide_padding_top = 0 g.neovide_padding_bottom = 0 g.neovide_padding_right = 0 g.neovide_padding_left = 0 g.neovide_refresh_rate = 144 - g.neovide_theme = "light" + -- g.neovide_theme = "light" -- Neovide Fonts o.guifont = "JetBrainsMono Nerd Font:h13:Medium" else - require("notify").notify("Not running in NeoVide") + -- require("notify").notify("Not running in NeoVide") end diff --git a/config/neovim/refresh_config.sh b/config/neovim/refresh_config.sh index bf1cbe86..71a84f66 100755 --- a/config/neovim/refresh_config.sh +++ b/config/neovim/refresh_config.sh @@ -1,3 +1,51 @@ #!/usr/bin/env bash -cp -f $(readlink -f ~/.config/nvim/init.lua) ./config.lua +INIT_PATH="$(readlink -f ~/.config/nvim/init.lua)" +cp -f "$INIT_PATH" ./config.lua +echo "Copied $INIT_PATH to ./config.lua" + +chmod +w ./config.lua +echo "Fixed permission for ./config.lua" +echo "" + +rm -rf ./store/* +echo "Cleared ./store/" + +STORE_PATHS=$(rg -oN "\"/nix/store/.*?(lazy-plugins|vimplugin-nvim-treesitter-.*?|treesitter-parsers)\"" config.lua | uniq | sd "\"" "") +for STORE_PATH in $STORE_PATHS +do + cp -Lr "$STORE_PATH" ./store/ + echo "Copied $STORE_PATH to ./store/" +done + +chmod -R +w ./store/* +echo "Fixed permissions for ./store" +echo "" + +for IDENTIFIER in "treesitter-parsers" "lazy-plugins" "nvim-treesitter" +do + CURRENT_PATH=$(eza -1 ./store | grep $IDENTIFIER) + mv "./store/$CURRENT_PATH" "./store/$IDENTIFIER" + echo "Moved ./store/$CURRENT_PATH to ./store/$IDENTIFIER" +done +echo "" + +BASE_PATH="/home/lab/smchurla/Downloads/flake-nixinator/config/neovim/store" +for IDENTIFIER in "treesitter-parsers" "lazy-plugins" "nvim-treesitter" +do + REPLACE_STRINGS=$(rg -oN "\"/nix/store/.*?$IDENTIFIER.*?\"" ./config.lua | uniq | sd "\"" "") + for REPLACE_STRING in $REPLACE_STRINGS + do + if [[ $REPLACE_STRING =~ .*$IDENTIFIER/.* ]]; + then + # Trailing / means not the entire string can be replaced + REPLACE_STRING=$(dirname "$REPLACE_STRING") + sd "$REPLACE_STRING" "$BASE_PATH/$IDENTIFIER" ./config.lua + echo "Substituted $REPLACE_STRING with $BASE_PATH/$IDENTIFIER" + else + # No trailing / means the entire string can be replaced + sd "$REPLACE_STRING" "$BASE_PATH/$IDENTIFIER" ./config.lua + echo "Substituted $REPLACE_STRING with $BASE_PATH/$IDENTIFIER" + fi + done +done diff --git a/config/neovim/store/lazy-plugins/better-escape.nvim/.github/FUNDING.yml b/config/neovim/store/lazy-plugins/better-escape.nvim/.github/FUNDING.yml new file mode 100644 index 00000000..3a51704d --- /dev/null +++ b/config/neovim/store/lazy-plugins/better-escape.nvim/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: 'max397574' +custom: [ 'https://buymeacoffee.com/max397574' ] diff --git a/config/neovim/store/lazy-plugins/better-escape.nvim/.gitignore b/config/neovim/store/lazy-plugins/better-escape.nvim/.gitignore new file mode 100644 index 00000000..e43b0f98 --- /dev/null +++ b/config/neovim/store/lazy-plugins/better-escape.nvim/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/config/neovim/store/lazy-plugins/better-escape.nvim/LICENSE b/config/neovim/store/lazy-plugins/better-escape.nvim/LICENSE new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/config/neovim/store/lazy-plugins/better-escape.nvim/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/config/neovim/store/lazy-plugins/better-escape.nvim/lua/better_escape.lua b/config/neovim/store/lazy-plugins/better-escape.nvim/lua/better_escape.lua new file mode 100644 index 00000000..fe576ea6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/better-escape.nvim/lua/better_escape.lua @@ -0,0 +1,175 @@ +local M = {} + +local api = vim.api + +local settings = { + timeout = vim.o.timeoutlen, + mapping = { "jk", "jj" }, + clear_empty_lines = false, + ---@type string|function + keys = "", +} + +local first_chars = {} +local second_chars = {} + +---@class State +---@field char string +---@field modified boolean + +local timer +local waiting = false +---@type State[] +local input_states = {} + +---@param tbl table table to search through +---@param element any element to search in tbl +---@return table indices +--- Search for indices in tbl where element occurs +local function get_indices(tbl, element) + local indices = {} + for idx, value in ipairs(tbl) do + if element == value then + table.insert(indices, idx) + end + end + return indices +end + +---@param keys string keys to feed +--- Replace keys with termcodes and feed them +local function feed(keys, mode) + api.nvim_feedkeys( + api.nvim_replace_termcodes(keys, true, true, true), + mode or "n", + false + ) +end + +local function start_timer() + waiting = true + + if timer then + timer:stop() + end + + timer = vim.defer_fn(function() + waiting = false + end, settings.timeout) +end + +local function get_keys() + -- if keys is string use it, else use it as a function + return type(settings.keys) == "string" and settings.keys or settings.keys() +end + +local function check_timeout() + if waiting then + local current_line = api.nvim_get_current_line() + if settings.clear_empty_lines and current_line:match("^%s+j$") then + vim.schedule(function() + api.nvim_set_current_line("") + feed(get_keys(), "in") + end) + else + feed("" .. get_keys(), "in") -- delete the characters from the mapping + end + + waiting = false -- more timely + return true + end + return false +end + +function M.check_charaters() + local char = vim.v.char + table.insert(input_states, { char = char, modified = vim.bo.modified }) + + local matched = false + if #input_states >= 2 then + ---@type State + local prev_state = input_states[#input_states - 1] + local indices = get_indices(second_chars, char) + -- if char == second_chars[idx] and prev_char == first_chars[idx] as well + -- then matched = true + for _, idx in ipairs(indices) do + if first_chars[idx] == prev_state.char then + matched = check_timeout() + break + end + end + + if matched then + input_states = {} + vim.schedule(function() + vim.bo.modified = prev_state.modified + end) + end + end + + -- if can't find a match, and the typed char is first in a mapping, start the timeout + if not matched and vim.tbl_contains(first_chars, char) then + start_timer() + end +end + +local function char_at(str, pos) + return vim.fn.nr2char(vim.fn.strgetchar(str, pos)) +end + +local function validate_settings() + assert(type(settings.mapping) == "table", "Mapping must be a table.") + + for _, mapping in ipairs(settings.mapping) do + -- replace all multibyte chars to `A` char + local length = #vim.fn.substitute(mapping, ".", "A", "g") + assert(length == 2, "Mapping must be 2 keys.") + end + + if settings.timeout then + assert(type(settings.timeout) == "number", "Timeout must be a number.") + assert( + settings.timeout >= 100, + "Timeout must be greater than or equal to 100." + ) + end + + assert( + vim.tbl_contains({ "string", "function" }, type(settings.keys)), + "Keys must be a function or string." + ) +end + +function M.setup(update) + settings = vim.tbl_deep_extend("force", settings, update or {}) + -- if mapping is a string (single mapping) make it a table + if type(settings.mapping) == "string" then + settings.mapping = { settings.mapping } + end + local ok, msg = pcall(validate_settings) + if ok then + -- create tables with the first and seconds chars of the mappings + for _, shortcut in ipairs(settings.mapping) do + vim.cmd("silent! iunmap " .. shortcut) + table.insert(first_chars, char_at(shortcut, 0)) + table.insert(second_chars, char_at(shortcut, 1)) + end + + vim.cmd([[ + augroup better_escape + autocmd! + autocmd InsertCharPre * lua require"better_escape".check_charaters() + augroup END + ]]) + else + vim.notify("Error(better-escape.nvim): " .. msg, vim.log.levels.ERROR) + end +end + +return setmetatable(M, { + __index = function(_, k) + if k == "waiting" then + return waiting + end + end, +}) diff --git a/config/neovim/store/lazy-plugins/better-escape.nvim/readme.md b/config/neovim/store/lazy-plugins/better-escape.nvim/readme.md new file mode 100644 index 00000000..e8562569 --- /dev/null +++ b/config/neovim/store/lazy-plugins/better-escape.nvim/readme.md @@ -0,0 +1,95 @@ +# 🚪better-escape.nvim + +This plugin is the lua version of [better_escape.vim](https://github.com/jdhao/better-escape.vim), +with some additional features and optimizations + +A lot of people have mappings like `jk` or `jj` to escape insert mode. +The problem with this mappings is that whenever you type a `j`, neovim wait about 100-500ms (depending on your timeoutlen) to see, if you type a `j` or a `k` because these are mapped. +Only after that time the `j` will be inserted. +Then you always get a delay when typing a `j`. + +This looks like this (see below for a gif): + +![Screen Shot 2021-10-08 at 16 21 23](https://user-images.githubusercontent.com/81827001/136576543-c8b4e802-84a8-4087-a7a4-f7d069931885.png) + +## ✨Features + +- Escape without getting delay when typing in insert mode +- Customizable mapping and timeout +- Use multiple mappings +- Really small and fast + +## 📦Installation + +Use your favourite package manager and call the setup function. + +```lua +-- lua with packer.nvim +use { + "max397574/better-escape.nvim", + config = function() + require("better_escape").setup() + end, +} +``` + +## ⚙️Customization + +Call the setup function with your options as arguments. + +```lua +-- lua, default settings +require("better_escape").setup { + mapping = {"jk", "jj"}, -- a table with mappings to use + timeout = vim.o.timeoutlen, -- the time in which the keys must be hit in ms. Use option timeoutlen by default + clear_empty_lines = false, -- clear line after escaping if there is only whitespace + keys = "", -- keys used for escaping, if it is a function will use the result everytime + -- example(recommended) + -- keys = function() + -- return vim.api.nvim_win_get_cursor(0)[2] > 1 and 'l' or '' + -- end, +} +``` + +## API + +`require("better_escape").waiting` is a boolean indicating that it's waiting for +a mapped sequence to complete. + +
+statusline example + +```lua +function escape_status() + local ok, m = pcall(require, 'better_escape') + return ok and m.waiting and '✺' or "" +end +``` + +
+ +## 👀Demo + +![mapping](https://user-images.githubusercontent.com/81827001/135870002-07c1dc41-f3e7-4ece-af6f-50e9b0711a66.gif) + +![plugin](https://user-images.githubusercontent.com/81827001/135870101-febf3507-9327-4b80-aa9a-ba08bff6b8d4.gif) + +## 🎓How it works + +With the mappings there are two tables created. +One contains all first characters and one all second characters. +Whenever you type a character the plugin checks if it's in any of the two tables +If it is in the first one, the plugin starts a timer. +If it is in the second, the plugin checks whether the character you typed before is in the table with the first characters. + +If this is the case the plugin gets all the indices where the characters are in the tables, then is searches for matches. +If there is a match, that means that there is a mapping which has the typed character as second and the previous typed character as first character. +The plugin then checks if the time passed since the first character was types is smaller than `timoutlen`. +If this is the case the two characters get deleted and `keys` get feed or executed. + +Like this it is possible that the characters really get inserted and therefore you have no delay after typing one of the characters of your mapping. +With the `timeoutlen` it's still possible to type the characters of your mappings. + +## ❤️ Support +If you like the projects I do and they can help you in your life you can support my work with [github sponsors](https://github.com/sponsors/max397574). +Every support motivates me to continue working on my open source projects. diff --git a/config/neovim/store/lazy-plugins/better-escape.nvim/stylua.toml b/config/neovim/store/lazy-plugins/better-escape.nvim/stylua.toml new file mode 100644 index 00000000..ddca0551 --- /dev/null +++ b/config/neovim/store/lazy-plugins/better-escape.nvim/stylua.toml @@ -0,0 +1,5 @@ +column_width = 80 +indent_type = "Spaces" +indent_width = 4 +quote_style = "AutoPreferDouble" +no_call_parentheses = false diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.editorconfig b/config/neovim/store/lazy-plugins/catppuccin-nvim/.editorconfig new file mode 100644 index 00000000..fe7a099f --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.editorconfig @@ -0,0 +1,18 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# EditorConfig is awesome: https://EditorConfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +insert_final_newline = true + +[*.lua] +indent_style = tab + +[*.{diff,md}] +trim_trailing_whitespace = false +indent_style = space +indent_size = 4 diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/ISSUE_TEMPLATE/bug_report.yml b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..58434501 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,93 @@ +name: Bug report +description: Report a problem with Catppuccin +labels: [bug] +body: + - type: textarea + attributes: + label: "Description" + description: "A short description of the problem you are reporting." + validations: + required: true + - type: textarea + attributes: + label: "Neovim version" + description: "Output of `nvim --version` (Catppuccin requires neovim >= 0.8)" + render: markdown + placeholder: | + NVIM v0.9.0 + Build type: Release + LuaJIT 2.1.0-beta3 + validations: + required: true + - type: input + attributes: + label: "Terminal and multiplexer" + placeholder: "kitty 0.29.2 with tmux 3.3a" + validations: + required: true + - type: markdown + attributes: + value: | + # FOR TMUX USERS + FOLLOW THESE GIST BEFORE OPENING THE ISSUE + - [Enable true color support](https://gist.github.com/andersevenrud/015e61af2fd264371032763d4ed965b6) to fix the [abnormal colors](https://github.com/catppuccin/nvim/issues/415) + - [Enable italic font support](https://gist.github.com/gyribeiro/4192af1aced7a1b555df06bd3781a722) to fix the [incorrect if, then, else, end highlights](https://github.com/catppuccin/nvim/issues/428) + - type: input + attributes: + label: "Catppuccin version / branch / rev" + placeholder: "catppuccin v1.4.0" + validations: + required: true + - type: textarea + attributes: + label: "Steps to reproduce" + description: "Steps to reproduce using the minimal config provided below." + placeholder: | + 1. `nvim -u repro.lua` + 2. ... + validations: + required: true + - type: textarea + attributes: + label: "Expected behavior" + description: "A description of the behavior you expected:" + validations: + required: true + - type: textarea + attributes: + label: "Actual behavior" + description: "Observed behavior (may optionally include logs, images, or videos)." + validations: + required: true + - type: textarea + attributes: + label: Repro + description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua` + value: | + -- DO NOT change the paths and don't remove the colorscheme + local root = vim.fn.fnamemodify("./.repro", ":p") + + -- set stdpaths to use .repro + for _, name in ipairs({ "config", "data", "state", "cache" }) do + vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name + end + + -- bootstrap lazy + local lazypath = root .. "/plugins/lazy.nvim" + if not vim.loop.fs_stat(lazypath) then + vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath }) + end + vim.opt.runtimepath:prepend(lazypath) + + -- install plugins + local plugins = { + "catppuccin/nvim", + -- add any other plugins here + } + require("lazy").setup(plugins, { + root = root .. "/plugins", + }) + + vim.cmd.colorscheme("catppuccin") + -- add anything else here + render: Lua diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/ISSUE_TEMPLATE/config.yml b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..133a9bb2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Question + url: https://discord.com/servers/catppuccin-907385605422448742 + about: Join our discord server for real-time answers and more! diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/ISSUE_TEMPLATE/feature_request.md b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..8341b4ad --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: "Suggest an idea for the project" +title: "" +labels: enhancement +assignees: "" +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. +Ex. I'm always frustrated when [...]. My workflow is like this [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/PULL_REQUEST_TEMPLATE.md b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..fe96cc9e --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ +🎉 First off, thanks for taking the time to contribute! 🎉 + +Here are some guidelines: +- Format code using [stylua](https://github.com/johnnymorganz/stylua). +- New plugin integration should be added in alphabetical order: + - to the [README](https://github.com/catppuccin/nvim#integrations) (vimdoc is auto-generated). + - to [types.lua](https://github.com/catppuccin/nvim/blob/main/lua/catppuccin/types.lua) +- Create a topic branch on your fork for your specific PR. +- Use [conventionalcommits.org's](https://www.conventionalcommits.org/en/v1.0.0/) + rules for explicit and meaningful commit messages. +- If it's your first time contributing to a project, then read + [About pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) + on Github's docs. + +Here are some tips: +- Use `vim.g.catppuccin_debug = true` to get live config re-loading diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/scripts/update-palette.py b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/scripts/update-palette.py new file mode 100644 index 00000000..dbf773fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/scripts/update-palette.py @@ -0,0 +1,14 @@ +import json +import urllib.request + +url = "https://github.com/catppuccin/palette/raw/main/palette.json" + +with urllib.request.urlopen(url) as response: + flavors = json.loads(response.read().decode()) + +for flavor in flavors: + with open(f"lua/catppuccin/palettes/{flavor}.lua", "w") as f: + f.write("return {\n") + for color in flavors[flavor]["colors"]: + f.write(f'\t{color} = "{flavors[flavor]["colors"][color]["hex"]}",\n') + f.write("}\n") diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/formatting.yml b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/formatting.yml new file mode 100644 index 00000000..fba8cdf4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/formatting.yml @@ -0,0 +1,20 @@ +name: StyLua +on: + pull_request: + push: + paths-ignore: + - "*.md" + branches: + - main + +jobs: + stylua: + name: StyLua + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: JohnnyMorganz/stylua-action@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + args: --check --config-path=stylua.toml . diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/neovim.yml b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/neovim.yml new file mode 100644 index 00000000..53ddbc59 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/neovim.yml @@ -0,0 +1,55 @@ +--- +name: Neovim +on: + pull_request: + push: + paths-ignore: + - "*.md" + branches: + - main + +jobs: + ubuntu: + name: Ubuntu + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Install Neovim + uses: MunifTanjim/setup-neovim-action@v1 + with: + tag: nightly + - name: Run neovim + run: | + nvim --version + nvim --headless -u tests/init.lua +q + macos: + name: Macos + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + - name: Install Neovim + run: | + wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim-macos-x86_64.tar.gz + xattr -c ./nvim-macos-x86_64.tar.gz + tar xzvf nvim-macos-x86_64.tar.gz &> /dev/null + ln -s $(pwd)/nvim-macos-x86_64/bin/nvim /usr/local/bin/nvim + - name: Run neovim + run: | + nvim --version + nvim --headless -u tests/init.lua +q + windows: + name: Windows + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Install Neovim + run: | + C:\msys64\usr\bin\wget.exe -q https://github.com/neovim/neovim/releases/download/nightly/nvim-win64.zip + 7z x nvim-win64.zip + Add-Content $env:GITHUB_PATH ".\nvim-win64\bin\" + - name: Run neovim + run: | + nvim --version + nvim --headless -u tests/init.lua +q diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/palette.yml b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/palette.yml new file mode 100644 index 00000000..31aa5cb3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/palette.yml @@ -0,0 +1,30 @@ +name: Palette + +on: + push: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' # Run every day at midnight UTC + +jobs: + createPullRequest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Make changes to pull request + run: python3 ./.github/scripts/update-palette.py + + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v5 + with: + commit-message: 'feat: auto-sync upstream palettes' + committer: GitHub + author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + signoff: false + branch: new-palettes + delete-branch: true + title: 'feat: auto-sync upstream palettes' + body: | + Auto-update `lua/catppuccin/palettes/` based on https://github.com/catppuccin/palette/blob/main/palette.json diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/pandocvim.yml b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/pandocvim.yml new file mode 100644 index 00000000..789a326b --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/pandocvim.yml @@ -0,0 +1,26 @@ +name: panvimdoc + +on: + push: + paths: + - '**.md' + +jobs: + docs: + runs-on: ubuntu-latest + name: pandoc to vimdoc + steps: + - uses: actions/checkout@v4 + - name: panvimdoc + uses: kdheepak/panvimdoc@main + with: + vimdoc: catppuccin + description: "Soothing pastel theme for NeoVim" + pandoc: "README.md" + toc: true + version: "nvim >= 0.8.0" + treesitter: true + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "docs: auto generate vimdoc" + branch: ${{ github.head_ref }} diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/release.yml b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/release.yml new file mode 100644 index 00000000..f7ed8fa3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: release + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + release: + name: release + if: ${{ github.ref == 'refs/heads/main' }} + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v3 + id: release + with: + release-type: simple + package-name: catppuccin + - uses: actions/checkout@v4 + - name: tag stable versions + if: ${{ steps.release.outputs.release_created }} + run: | + git config user.name github-actions[bot] + git config user.email github-actions[bot]@users.noreply.github.com + git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" + git tag -d stable || true + git push origin :stable || true + git tag -a stable -m "Last Stable Release" + git push origin stable diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/tests.yml b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/tests.yml new file mode 100644 index 00000000..f87e5c05 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/tests.yml @@ -0,0 +1,42 @@ +--- +name: Tests +on: + pull_request: + push: + paths-ignore: + - "*.md" + branches: + - main + workflow_dispatch: + schedule: + - cron: '0 0 * * *' # Run every day at midnight UTC + +jobs: + ubuntu: + name: Plenary + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + path: nvim + - uses: actions/checkout@v4 + with: + ref: user-configs + path: user-configs + - run: | + mv user-configs/tests/* nvim/tests/ + - name: Install Neovim + uses: MunifTanjim/setup-neovim-action@v1 + with: + tag: nightly + - name: Fetch dependencies + run: | + git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim + ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start + - name: Run tests + working-directory: ./nvim + run: | + nvim --version + [ ! -d tests ] && exit 0 + nvim --headless -u tests/minimal_init.vim -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/minimal_init.vim', sequential = true}" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/vim.yml b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/vim.yml new file mode 100644 index 00000000..469a6fea --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.github/workflows/vim.yml @@ -0,0 +1,58 @@ +--- +name: Vim +on: + pull_request: + push: + paths-ignore: + - "*.md" + branches: + - main + +jobs: + ubuntu: + name: Ubuntu + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Install Vim + shell: bash + run: | + sudo apt-get update + sudo apt-get install vim-gtk lua5.4 + curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim + - name: Run vim + run: | + vim --version + vim -u tests/init.vim + macos: + name: Macos + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + - name: Install Vim + run: | + brew install vim + curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim + - name: Run vim + run: | + vim --version + vim -u tests/init.vim + windows: + name: Windows + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Install Vim + run: | + choco install vim + C:\msys64\usr\bin\wget.exe -q https://downloads.sourceforge.net/project/luabinaries/5.4.2/Tools%20Executables/lua-5.4.2_Win64_bin.zip + 7z x lua-5.4.2_Win64_bin.zip + move lua54.dll C:\Windows\System32\ + iwr -useb https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim |` + ni $HOME/vimfiles/autoload/plug.vim -Force + - name: Run vim + run: | + vim --version + gvim -u tests/init.vim diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/.gitignore b/config/neovim/store/lazy-plugins/catppuccin-nvim/.gitignore new file mode 100644 index 00000000..4ebdfaa8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/.gitignore @@ -0,0 +1,4 @@ +doc/tags +.vscode/ +.DS_Store +.repro diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/CHANGELOG.md b/config/neovim/store/lazy-plugins/catppuccin-nvim/CHANGELOG.md new file mode 100644 index 00000000..b717dca3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/CHANGELOG.md @@ -0,0 +1,166 @@ +# Changelog + +## [1.7.0](https://github.com/catppuccin/nvim/compare/v1.6.0...v1.7.0) (2024-04-13) + + +### Features + +* add ability to toggle default integrations ([#687](https://github.com/catppuccin/nvim/issues/687)) ([e60e400](https://github.com/catppuccin/nvim/commit/e60e400c411519f29e203185ddda121d4ec8ef57)) +* add support for `outline.nvim` plugin ([#647](https://github.com/catppuccin/nvim/issues/647)) ([048c18f](https://github.com/catppuccin/nvim/commit/048c18fc531703815f5e10765ea46ce9b2c75ae4)) +* **defaults:** enable neotree ([c536623](https://github.com/catppuccin/nvim/commit/c536623eac60f8443c93ae4ca0e03b51574b5f50)) +* **defaults:** enable treesitter context ([dc392c0](https://github.com/catppuccin/nvim/commit/dc392c067739326c3cff380a8c52b0f31319e6dd)), closes [#683](https://github.com/catppuccin/nvim/issues/683) +* **feline:** fix feline integration ([#685](https://github.com/catppuccin/nvim/issues/685)) ([07679af](https://github.com/catppuccin/nvim/commit/07679af1af4f446655682ee2557b5840ac551504)) +* **feline:** improve feline lsp display ([#688](https://github.com/catppuccin/nvim/issues/688)) ([f66654d](https://github.com/catppuccin/nvim/commit/f66654d5d5190865333e8e46474c1593302c558e)) +* **integration:** add reactive.nvim integration ([#654](https://github.com/catppuccin/nvim/issues/654)) ([151e478](https://github.com/catppuccin/nvim/commit/151e478edf8108cfd451a3cbd44d0a20503e7b42)) +* **lsp:** add highlight for `LspCodeLensSeparator` ([#693](https://github.com/catppuccin/nvim/issues/693)) ([02bdd74](https://github.com/catppuccin/nvim/commit/02bdd749931a5d739063562e57531c118e081882)) +* set `[@comment](https://github.com/comment).warning` that does not affect readability in gitcommit ([#675](https://github.com/catppuccin/nvim/issues/675)) ([045e349](https://github.com/catppuccin/nvim/commit/045e3499d9ec8d84635fb08877ae44fd33f6a38d)) +* **treesitter:** add styles.miscs to disable hardcoded italics ([#659](https://github.com/catppuccin/nvim/issues/659)) ([c0de3b4](https://github.com/catppuccin/nvim/commit/c0de3b46811fe1ce3912e2245a9dfbea6b41c300)) +* **treesitter:** follow upstream captures ([#630](https://github.com/catppuccin/nvim/issues/630)) ([f288876](https://github.com/catppuccin/nvim/commit/f288876c6d05d3bb91b0e72b8031fe9e26ef05b8)) +* **treesitter:** follow upstream captures ([#694](https://github.com/catppuccin/nvim/issues/694)) ([08c6417](https://github.com/catppuccin/nvim/commit/08c6417bdc3b29e5f8c53e2cfe4067f288d49a54)) +* use a more distinguishable color for todos ([#645](https://github.com/catppuccin/nvim/issues/645)) ([657cc4f](https://github.com/catppuccin/nvim/commit/657cc4f35cf193cadac7e5471eb802c97e7a1b59)) + + +### Bug Fixes + +* **compile:** string.dump isn't deterministic ([836de8b](https://github.com/catppuccin/nvim/commit/836de8bc1898250b69332e66cbe993058870f849)), closes [#664](https://github.com/catppuccin/nvim/issues/664) +* **dropbar:** correct keyword highlight link ([#652](https://github.com/catppuccin/nvim/issues/652)) ([afab7ec](https://github.com/catppuccin/nvim/commit/afab7ec2a79c7127627dede79c0018b6e45663d0)) +* ensure consistency between JSX and HTML markup ([#660](https://github.com/catppuccin/nvim/issues/660)) ([9703f22](https://github.com/catppuccin/nvim/commit/9703f227bfab20d04bcee62d2f08f1795723b4ae)) +* **flavour:** g:catppuccin_flavour backwards compatibility ([fc98570](https://github.com/catppuccin/nvim/commit/fc98570d85ae772e56dc42cf8d7d6a497a909bdb)) +* **flavour:** respect terminal's background ([#696](https://github.com/catppuccin/nvim/issues/696)) ([d5760c5](https://github.com/catppuccin/nvim/commit/d5760c53ae3b48f0f539298ec4165adc5c0afb19)) +* **illuminate:** update type ([#690](https://github.com/catppuccin/nvim/issues/690)) ([30930f9](https://github.com/catppuccin/nvim/commit/30930f9656cffd068bcf52ced70cdfffd1e83a44)) +* **integrations:** respect default options ([c2e6f8e](https://github.com/catppuccin/nvim/commit/c2e6f8e7eb8d0ebf55700c89bdf842809aeecf09)) +* **neotree:** add `NeoTreeModified` ([#642](https://github.com/catppuccin/nvim/issues/642)) ([6853cc8](https://github.com/catppuccin/nvim/commit/6853cc8e6efc76e85e10ec153d05fc2520653508)) +* **neotree:** blend sidebar with win separator ([56fb982](https://github.com/catppuccin/nvim/commit/56fb98218d22d5c326387bf9e4076227e7372e6b)), closes [#670](https://github.com/catppuccin/nvim/issues/670) +* repair treesitter underlined text (`Underline` -> `Underlined`) ([#663](https://github.com/catppuccin/nvim/issues/663)) ([42b687c](https://github.com/catppuccin/nvim/commit/42b687c42a35633366ed45e562bf921fb914048b)) +* respect background variable on startup ([6b7a4df](https://github.com/catppuccin/nvim/commit/6b7a4dfdf241c8be0af6ec691b302e85cce03cab)) +* **semantic_tokens:** namespace -> module ([196f301](https://github.com/catppuccin/nvim/commit/196f301de06090c40d7f98297675ac38ae7d6675)) +* **treesitter:** some captures missing leading `@` ([#650](https://github.com/catppuccin/nvim/issues/650)) ([bc1f215](https://github.com/catppuccin/nvim/commit/bc1f2151f23227ba02ac203c2c59ad693352a741)) +* use external index for lsp counting ([c3572a9](https://github.com/catppuccin/nvim/commit/c3572a968a79b64bd0ef16f2c3e93014f112e66d)) +* wrong color shown when using color_overrides ([#658](https://github.com/catppuccin/nvim/issues/658)) ([b76ada8](https://github.com/catppuccin/nvim/commit/b76ada82bf2019d5e343018b4104cc9266900c16)), closes [#657](https://github.com/catppuccin/nvim/issues/657) + +## [1.6.0](https://github.com/catppuccin/nvim/compare/v1.5.0...v1.6.0) (2023-12-28) + + +### Features + +* add `WinSeparator` highlight group ([#623](https://github.com/catppuccin/nvim/issues/623)) ([988c0b2](https://github.com/catppuccin/nvim/commit/988c0b2dde4140572ed37c6b8b5d5deac0219f9f)) +* **bufferline:** support `no_underline` option ([#601](https://github.com/catppuccin/nvim/issues/601)) ([f7638a1](https://github.com/catppuccin/nvim/commit/f7638a1a65cbffdd01a9ddac0018a20ec4be29e2)) +* **dashboard:** add highlight groups for `doom` theme ([#593](https://github.com/catppuccin/nvim/issues/593)) ([3bdd5e8](https://github.com/catppuccin/nvim/commit/3bdd5e8296971f8c7ba5e499dac8247c3d621508)) +* **integrations:** enable dap & dap_ui by default ([64dc309](https://github.com/catppuccin/nvim/commit/64dc309bc157779691be38bbfc5123584e0a4a85)) +* **lualine:** darken lualine `b` section for better readability ([#606](https://github.com/catppuccin/nvim/issues/606)) ([32ee05d](https://github.com/catppuccin/nvim/commit/32ee05d014a4611555c7f56a73283efb4718d9c5)) +* **mini.indentscope:** add scope color ([#592](https://github.com/catppuccin/nvim/issues/592)) ([795f639](https://github.com/catppuccin/nvim/commit/795f639ac50d6b8400c1d5868fca54844d579f37)) +* **neogit:** support new highlight groups ([#610](https://github.com/catppuccin/nvim/issues/610)) ([f90c7c0](https://github.com/catppuccin/nvim/commit/f90c7c0c467722dc7acacbae3c3904720e09efb6)) +* **notify:** add `NotifyBackground` hl group ([#637](https://github.com/catppuccin/nvim/issues/637)) ([c7cf3af](https://github.com/catppuccin/nvim/commit/c7cf3afe2eb6d9058eec4abb3ace2c1da006478a)) + + +### Bug Fixes + +* **airline:** missing refresh function ([dcef0a0](https://github.com/catppuccin/nvim/commit/dcef0a062de380885193fb0f919217d58b979753)), closes [#594](https://github.com/catppuccin/nvim/issues/594) +* **dashboard:** `orange` -> `peach` ([54002a1](https://github.com/catppuccin/nvim/commit/54002a1adfd543f54352b3ec79d4e62c4163e9ee)) +* **flash:** link `FlashPrompt` to `NormalFloat` ([#605](https://github.com/catppuccin/nvim/issues/605)) ([40dc9f0](https://github.com/catppuccin/nvim/commit/40dc9f0621c55bd40da4ad0731fac44d15bb393a)) +* **lualine:** match lualine mode colors for insert and terminal ([#597](https://github.com/catppuccin/nvim/issues/597)) ([ea52fe8](https://github.com/catppuccin/nvim/commit/ea52fe8a0b1e4a820df0d0cf9a6a5a0e18c3eaa0)) +* **neogit:** remove `NeogitCursorLine` from integration ([#613](https://github.com/catppuccin/nvim/issues/613)) ([5e4be43](https://github.com/catppuccin/nvim/commit/5e4be43e1a6acb044d5c55cd10f22461c40656ed)) +* **neogit:** remove diff context highlight fg ([1b40f07](https://github.com/catppuccin/nvim/commit/1b40f072305be71b73c730ff5c7d881e638fd040)), closes [#627](https://github.com/catppuccin/nvim/issues/627) +* **neogit:** tweak diff context highlighting ([#614](https://github.com/catppuccin/nvim/issues/614)) ([cc717ac](https://github.com/catppuccin/nvim/commit/cc717acba29259d578548973c41448b092453c52)) +* **neotree:** change color of untracked files ([#608](https://github.com/catppuccin/nvim/issues/608)) ([d7521f6](https://github.com/catppuccin/nvim/commit/d7521f6050b94cb0e23067f63829d86886f870fe)) +* **neotree:** make popup titlebar text visible ([#618](https://github.com/catppuccin/nvim/issues/618)) ([919d1f7](https://github.com/catppuccin/nvim/commit/919d1f786338ebeced798afbf28cd085cd54542a)) +* **noice:** respect transparency ([#632](https://github.com/catppuccin/nvim/issues/632)) ([4fbab1f](https://github.com/catppuccin/nvim/commit/4fbab1f01488718c3d54034a473d0346346b90e3)) +* **selene:** allow mixed tables ([#611](https://github.com/catppuccin/nvim/issues/611)) ([9f3c13b](https://github.com/catppuccin/nvim/commit/9f3c13bbcf16fcaec3a429c03743a13e5923f3e3)) +* sync focused and unfocused winbars ([#628](https://github.com/catppuccin/nvim/issues/628)) ([079500a](https://github.com/catppuccin/nvim/commit/079500a625f3ae5aa6efb758f1a17fe4c7a57e52)) +* **vim:** resolve deprecation of nested [[ ([7a4bcda](https://github.com/catppuccin/nvim/commit/7a4bcdadafc59a5bedbd866c643fa486d8cca4a1)) + +## [1.5.0](https://github.com/catppuccin/nvim/compare/v1.4.0...v1.5.0) (2023-09-29) + + +### Features + +* add kitty detection ([d3da439](https://github.com/catppuccin/nvim/commit/d3da43907d1896ba3e68a62f18820d1d12574317)) +* add ufo integration ([1f53686](https://github.com/catppuccin/nvim/commit/1f536869b1a2ca1710fc892db84d7e8bbc6ad8d9)) +* add workaround for kitty transparent issue ([#579](https://github.com/catppuccin/nvim/issues/579)) ([f36fa5c](https://github.com/catppuccin/nvim/commit/f36fa5cdce162450df88298a16631eeed16b68a3)) +* **compile:** use indexed cmd ([85e9360](https://github.com/catppuccin/nvim/commit/85e93601e0f0b48aa2c6bbfae4d0e9d7a1898280)) +* **illuminate:** enabled by default and optional lsp option ([5b44baa](https://github.com/catppuccin/nvim/commit/5b44baa4aff0ff45c042620ee960d283a79807a1)), closes [#571](https://github.com/catppuccin/nvim/issues/571) +* **indent-blankline:** update to v3, add scope color ([#585](https://github.com/catppuccin/nvim/issues/585)) ([f04336b](https://github.com/catppuccin/nvim/commit/f04336ba4a2400ee2c5250068b39541652c0962f)) +* **integrations:** add NormalNvim ([0e3c128](https://github.com/catppuccin/nvim/commit/0e3c128eea8a7de692778d52b8429817df5c9040)), closes [#580](https://github.com/catppuccin/nvim/issues/580) +* **integrations:** add notifier.nvim ([d029098](https://github.com/catppuccin/nvim/commit/d029098e124f6201a07298c0c1c499ed8d5aef76)), closes [#574](https://github.com/catppuccin/nvim/issues/574) +* **lib:** soft deprecate highlighter ([8202348](https://github.com/catppuccin/nvim/commit/82023485fec1703d6f700a4b2a92fd431d4882f4)) +* **syntax:** respect style guide part 3 ([#576](https://github.com/catppuccin/nvim/issues/576)) ([81096ca](https://github.com/catppuccin/nvim/commit/81096cabe67f360acb06d64c0f7db8dd840afeba)) + + +### Bug Fixes + +* **coc:** improve inlay hints ([#582](https://github.com/catppuccin/nvim/issues/582)) ([3d9a5ed](https://github.com/catppuccin/nvim/commit/3d9a5ed556e289bce6c1fb0af89ec838360641b2)) +* **editor:** invisible fold with transparent ([1c15c5e](https://github.com/catppuccin/nvim/commit/1c15c5e51a998c9198d63c6d2b75e9d1e4a84541)), closes [#577](https://github.com/catppuccin/nvim/issues/577) +* **template:** broken tmux italic gist link ([128e0d2](https://github.com/catppuccin/nvim/commit/128e0d27946491da979e2e04f5a4acf330ccdefd)) +* **treesitter:** invalid string in type builtin ([135f9b0](https://github.com/catppuccin/nvim/commit/135f9b01386fa18da6d75c16ceb83e1aa3669430)) +* **ufo:** use folded ellipsis ([846388d](https://github.com/catppuccin/nvim/commit/846388d137590e653390ce2f84fea5351a7516ac)) +* **vim:** add vim.env index ([1786287](https://github.com/catppuccin/nvim/commit/17862877792db104d48c3260aec0ace92d55f863)) + + +### Performance Improvements + +* **compile:** reduce else statement ([a937d54](https://github.com/catppuccin/nvim/commit/a937d546f4783a1ff67f84043d2d7871ad4ecd83)) + +## [1.4.0](https://github.com/catppuccin/nvim/compare/v1.3.0...v1.4.0) (2023-08-21) + + +### Features + +* add ability to enable/disable all integrations by default ([#552](https://github.com/catppuccin/nvim/issues/552)) ([737f60a](https://github.com/catppuccin/nvim/commit/737f60a3a25c79d9bb9574092f6c6c958a3d747a)) +* add flash.nvim integration ([#550](https://github.com/catppuccin/nvim/issues/550)) ([381eddd](https://github.com/catppuccin/nvim/commit/381edddc4ad12126cfa7276818bca07c3d5606ed)) +* enable neogit by default ([91f9f6f](https://github.com/catppuccin/nvim/commit/91f9f6fb413caff2bd06e326ec174deee1c1b7a9)), closes [#568](https://github.com/catppuccin/nvim/issues/568) +* **flash:** enable by default ([#551](https://github.com/catppuccin/nvim/issues/551)) ([a84ee18](https://github.com/catppuccin/nvim/commit/a84ee1848bfac4601771805396552bdbaa0a0e91)) +* **gitsigns:** Support GitSignsCurrentLineBlame highlights ([#567](https://github.com/catppuccin/nvim/issues/567)) ([3fdd394](https://github.com/catppuccin/nvim/commit/3fdd3942567503d23b65ccc21e7d7757334defd5)) +* **lspsaga:** support v0.3 ([#543](https://github.com/catppuccin/nvim/issues/543)) ([3ffd2f5](https://github.com/catppuccin/nvim/commit/3ffd2f511f3dc6c01258923d7170ccaf1445634b)) +* **lspsaga:** upstream new hl groups ([#544](https://github.com/catppuccin/nvim/issues/544)) ([e0dd3f9](https://github.com/catppuccin/nvim/commit/e0dd3f9bb1513c98ab4ef9404ea26e18babf858a)) +* **neogit:** update highlights ([#545](https://github.com/catppuccin/nvim/issues/545)) ([#549](https://github.com/catppuccin/nvim/issues/549)) ([371430f](https://github.com/catppuccin/nvim/commit/371430f32f2637d2dd5796399b3982d4cada61d8)) +* **telescope:** make nvchad style great again ([#538](https://github.com/catppuccin/nvim/issues/538)) ([51961da](https://github.com/catppuccin/nvim/commit/51961da41e8189ca6f9ed73f37dfa83087b4e65c)) +* **treesitter-context:** add color for normal background ([#564](https://github.com/catppuccin/nvim/issues/564)) ([b1caff9](https://github.com/catppuccin/nvim/commit/b1caff988fb395c0aae585cecff58b1ffa0a21c6)) + + +### Bug Fixes + +* don't highlight fg of `PmenuSel` ([#554](https://github.com/catppuccin/nvim/issues/554)) ([6425df1](https://github.com/catppuccin/nvim/commit/6425df128d46f2db2cccf9aa7a66ca2823c1d153)) +* highlight NonText characters ([#547](https://github.com/catppuccin/nvim/issues/547)) ([bfe91df](https://github.com/catppuccin/nvim/commit/bfe91dfb3a19ffd4445e43611fcde68acbb3fed4)) +* **integration_default:** hotfix for [#559](https://github.com/catppuccin/nvim/issues/559) ([4913a8b](https://github.com/catppuccin/nvim/commit/4913a8b47554a89a71ed44da39fc1f6e5c2841c3)) +* **integration_default:** override `enabled` key only if integration has one ([#559](https://github.com/catppuccin/nvim/issues/559)) ([9709f82](https://github.com/catppuccin/nvim/commit/9709f8251a40e874238d6f9436cf4fba654b60e1)) +* **noice:** set background blend to 0 for mini popups ([#556](https://github.com/catppuccin/nvim/issues/556)) ([2d50a4e](https://github.com/catppuccin/nvim/commit/2d50a4e3aecffea4144801bb3c0a3cf7b88fdd6b)) +* **nvim-window-picker:** missing table keys ([#569](https://github.com/catppuccin/nvim/issues/569)) ([b9e4dae](https://github.com/catppuccin/nvim/commit/b9e4dae160bf9bc28d4ceb6d29a7e0134b107724)) +* **options:** disable deprecated ts_rainbow and ts_rainbow2 by default ([096385d](https://github.com/catppuccin/nvim/commit/096385dd024ecd1332659916fd7f09d7d18d7374)) +* **telescope:** keep consistency between the two styles ([#540](https://github.com/catppuccin/nvim/issues/540)) ([dfbc8e2](https://github.com/catppuccin/nvim/commit/dfbc8e2b478a65104d34556698067f2d40f1c227)) +* **telescope:** respect transparency ([#542](https://github.com/catppuccin/nvim/issues/542)) ([f36af06](https://github.com/catppuccin/nvim/commit/f36af062e3242f333b12fe9b730053fdda36e000)) +* **treesitter:** avoid possible nil ([17ae783](https://github.com/catppuccin/nvim/commit/17ae783b88bb7ae73dc004370473138d9d43ee46)) +* **types:** make all options besides nested `enabled` optional ([#565](https://github.com/catppuccin/nvim/issues/565)) ([490078b](https://github.com/catppuccin/nvim/commit/490078b1593c6609e6a50ad5001e7902ea601824)) + +## [1.3.0](https://github.com/catppuccin/nvim/compare/v1.2.0...v1.3.0) (2023-07-10) + + +### Features + +* auto-sync upstream palettes ([#507](https://github.com/catppuccin/nvim/issues/507)) ([8426d3b](https://github.com/catppuccin/nvim/commit/8426d3bfd55f4dc68ae451a82927d2ff88e47e95)) +* **debug:** add auto compile on save ([c9cc5a9](https://github.com/catppuccin/nvim/commit/c9cc5a997f1dae3f35b4bdd62f35958fee363ab4)) +* **dropbar:** add new highlight groups ([4f22a1e](https://github.com/catppuccin/nvim/commit/4f22a1e78460ae06e78a1085a8e0e6cc8027aef2)), closes [#503](https://github.com/catppuccin/nvim/issues/503) +* **integration:** add dropbar.nvim ([#499](https://github.com/catppuccin/nvim/issues/499)) ([e86aeb8](https://github.com/catppuccin/nvim/commit/e86aeb8ca0f03e97192074fba9dc6c836f953a83)) +* **integrations:** accept both boolean and table config ([#534](https://github.com/catppuccin/nvim/issues/534)) ([f0b947a](https://github.com/catppuccin/nvim/commit/f0b947ab8cfdb9ca7ba6230b30bbc1ed48dd30a1)) +* **integrations:** add rainbow_delimiters.nvim support ([#530](https://github.com/catppuccin/nvim/issues/530)) ([cc8d3ab](https://github.com/catppuccin/nvim/commit/cc8d3abc944d78cb6bf2a4cc88871ab383c4da62)) +* **markdown:** add rainbow headlines ([#493](https://github.com/catppuccin/nvim/issues/493)) ([cc517bd](https://github.com/catppuccin/nvim/commit/cc517bdcb66a0f8dee90bab10ccdd651fa967bbe)) +* **native_lsp:** add ability to disable background for inlay hints ([#518](https://github.com/catppuccin/nvim/issues/518)) ([b032ced](https://github.com/catppuccin/nvim/commit/b032cedb90c42a7bfbfbe2f91479505330f4a396)) +* **native_lsp:** support inlay hints ([#516](https://github.com/catppuccin/nvim/issues/516)) ([d32b0bb](https://github.com/catppuccin/nvim/commit/d32b0bb5b1033920de5026e326869838aba856ee)) +* **navic:** change text color ([278bfeb](https://github.com/catppuccin/nvim/commit/278bfeb61bd627dc2a8885180a0441a1ebe65a41)) +* **semantic_tokens:** add some lsp semantic tokens ([#512](https://github.com/catppuccin/nvim/issues/512)) ([506a4aa](https://github.com/catppuccin/nvim/commit/506a4aa13443e0104ea49b99947cc09488d0791d)) +* **telescope:** telescope flat style support ([#521](https://github.com/catppuccin/nvim/issues/521)) ([fc73faa](https://github.com/catppuccin/nvim/commit/fc73faa37bda393e3c4f846fb3e810a6ac8aae16)) +* **types:** add type annotations ([#495](https://github.com/catppuccin/nvim/issues/495)) ([1d3eda1](https://github.com/catppuccin/nvim/commit/1d3eda15703ba70f57e94e6451db55914ff7017f)) +* **workflows:** auto-sync upstream palettes ([e9fbeec](https://github.com/catppuccin/nvim/commit/e9fbeec106562475e82bae79304b6a421eee73f3)) + + +### Bug Fixes + +* calling palette before setup ([841d8ab](https://github.com/catppuccin/nvim/commit/841d8abf3be39de833d95a592a1fbbb1b9851296)) +* **feline:** disable lsp status on nightly ([#510](https://github.com/catppuccin/nvim/issues/510)) ([9aaf5b4](https://github.com/catppuccin/nvim/commit/9aaf5b4ce5cd256695d8bbddb65869d19919abde)) +* **feline:** use new `vim.lsp.status()` method ([#509](https://github.com/catppuccin/nvim/issues/509)) ([57ee09d](https://github.com/catppuccin/nvim/commit/57ee09dd532bd442b53d65c2b2f35550960981ed)) +* **lsp:** do not link `LspInlayHint` to `Comment` directly ([#517](https://github.com/catppuccin/nvim/issues/517)) ([5dc566c](https://github.com/catppuccin/nvim/commit/5dc566c4206f383657d67500253559d3be82c421)) +* **mapper:** remove unnecessary globals ([#529](https://github.com/catppuccin/nvim/issues/529)) ([c75562c](https://github.com/catppuccin/nvim/commit/c75562cbc954136f279ced91661251543b6f2a20)) +* **native_lsp:** boolean logic ([#526](https://github.com/catppuccin/nvim/issues/526)) ([8d02781](https://github.com/catppuccin/nvim/commit/8d02781a638123394f9bc160aad47a9560a113f9)) +* **tests:** shadowing variable ([15043d3](https://github.com/catppuccin/nvim/commit/15043d363729f1ef20e615c41bbd8b7e92c1453e)) +* **treesitter_context:** underline content if `transparent_background` is true ([#519](https://github.com/catppuccin/nvim/issues/519)) ([6ecc158](https://github.com/catppuccin/nvim/commit/6ecc158dbf365d2cd290b58993296c42b3111965)) +* **which-key:** wrong separator highlight group ([d438c01](https://github.com/catppuccin/nvim/commit/d438c0141609338140b18363a9a1e8eb8bb17130)) +* **workflows:** stylua format ([2df7036](https://github.com/catppuccin/nvim/commit/2df7036c5c303c9184869936e40ca18935e4afcb)) diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/LICENSE.md b/config/neovim/store/lazy-plugins/catppuccin-nvim/LICENSE.md new file mode 100644 index 00000000..006383b8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Catppuccin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/README.md b/config/neovim/store/lazy-plugins/catppuccin-nvim/README.md new file mode 100644 index 00000000..afe32b39 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/README.md @@ -0,0 +1,1551 @@ + + +

+ Logo
+ + Catppuccin for (Neo)vim + +

+ +

+ + + +

+ +

+This port of Catppuccin is special because it was the first one and the one that originated the project itself. Given this, it's important to acknowledge that it all didn't come to be what it is now out of nowhere. So, if you are interested in knowing more about the initial stages of the theme, you can find it under the v0.1 tag +

+ +

+ +

+ +# Flavours + +
+Latte + +
+
+Frappe + +
+
+Macchiato + +
+
+Mocha + +
+ +**[Bake your own flavour!](https://github.com/catppuccin/nvim/#overwriting-colors)** Here are some **[config from our community](https://github.com/catppuccin/nvim/discussions/323)**: (background source) +

+ +![nvimwalk-custom](https://user-images.githubusercontent.com/56817415/213480149-6ba92b81-1ada-46a4-89bd-4e2bb25d19c3.png) +

+ + + +# Features + +- Supports both vim and neovim (Requires [neovim](https://github.com/neovim/neovim/) >= 0.8 or [vim](https://github.com/vim/vim) >= 9 compiled with [lua](https://github.com/lua/lua) >= 5.1) +- Highly configurable with 4 different flavours and [ability to create your own!](https://github.com/catppuccin/nvim/discussions/323) +- [Compile](https://github.com/catppuccin/nvim#Compile) user config for [fastest startuptime](https://www.reddit.com/r/neovim/comments/xxfpt3/catppuccinnvim_now_startup_in_1ms/) +- Integrations with lsp, treesitter and [a bunch of plugins](https://github.com/catppuccin/nvim#integrations) +- Supports for [many other applications](https://github.com/catppuccin/catppuccin) + +# Installation + +[lazy.nvim](https://github.com/folke/lazy.nvim) +```lua +{ "catppuccin/nvim", name = "catppuccin", priority = 1000 } +``` + +[packer.nvim](https://github.com/wbthomason/packer.nvim) +```lua +use { "catppuccin/nvim", as = "catppuccin" } +``` + +[vim-plug](https://github.com/junegunn/vim-plug) +```vim +Plug 'catppuccin/nvim', { 'as': 'catppuccin' } +``` + +# Usage + +```vim +colorscheme catppuccin " catppuccin-latte, catppuccin-frappe, catppuccin-macchiato, catppuccin-mocha +``` + +```lua +vim.cmd.colorscheme "catppuccin" +``` + +# Configuration + +There is no need to call `setup` if you don't want to change the default options and settings. + +```lua +require("catppuccin").setup({ + flavour = "auto", -- latte, frappe, macchiato, mocha + background = { -- :h background + light = "latte", + dark = "mocha", + }, + transparent_background = false, -- disables setting the background color. + show_end_of_buffer = false, -- shows the '~' characters after the end of buffers + term_colors = false, -- sets terminal colors (e.g. `g:terminal_color_0`) + dim_inactive = { + enabled = false, -- dims the background color of inactive window + shade = "dark", + percentage = 0.15, -- percentage of the shade to apply to the inactive window + }, + no_italic = false, -- Force no italic + no_bold = false, -- Force no bold + no_underline = false, -- Force no underline + styles = { -- Handles the styles of general hi groups (see `:h highlight-args`): + comments = { "italic" }, -- Change the style of comments + conditionals = { "italic" }, + loops = {}, + functions = {}, + keywords = {}, + strings = {}, + variables = {}, + numbers = {}, + booleans = {}, + properties = {}, + types = {}, + operators = {}, + -- miscs = {}, -- Uncomment to turn off hard-coded styles + }, + color_overrides = {}, + custom_highlights = {}, + default_integrations = true, + integrations = { + cmp = true, + gitsigns = true, + nvimtree = true, + treesitter = true, + notify = false, + mini = { + enabled = true, + indentscope_color = "", + }, + -- For more plugins integrations please scroll down (https://github.com/catppuccin/nvim#integrations) + }, +}) + +-- setup must be called before loading +vim.cmd.colorscheme "catppuccin" +``` + +# Customize highlights + +## Get catppuccin colors + +```lua +local latte = require("catppuccin.palettes").get_palette "latte" +local frappe = require("catppuccin.palettes").get_palette "frappe" +local macchiato = require("catppuccin.palettes").get_palette "macchiato" +local mocha = require("catppuccin.palettes").get_palette "mocha" +``` + +Returns a table where the key is the name of the color and the value is its hex value corresponding to each flavour. + +## Overwriting colors + +Colors can be overwritten using `color_overrides` in the setting, checkout https://github.com/catppuccin/nvim/discussions/323 for inspirations: + +```lua +require("catppuccin").setup { + color_overrides = { + all = { + text = "#ffffff", + }, + latte = { + base = "#ff0000", + mantle = "#242424", + crust = "#474747", + }, + frappe = {}, + macchiato = {}, + mocha = {}, + } +} +``` + +> [!Note] +> For more information check out our [style-guide](https://github.com/catppuccin/catppuccin/blob/main/docs/style-guide.md) + +## Overwriting highlight groups + +Global highlight groups can be overwritten in the setting, for example: + +```lua +require("catppuccin").setup { + custom_highlights = function(colors) + return { + Comment = { fg = colors.flamingo }, + TabLineSel = { bg = colors.pink }, + CmpBorder = { fg = colors.surface2 }, + Pmenu = { bg = colors.none }, + } + end +} +``` + +Per flavour highlight groups can also be overwritten in the setting, for example: + +```lua +require("catppuccin").setup { + highlight_overrides = { + all = function(colors) + return { + NvimTreeNormal = { fg = colors.none }, + CmpBorder = { fg = "#3e4145" }, + } + end, + latte = function(latte) + return { + Normal = { fg = latte.base }, + } + end, + frappe = function(frappe) + return { + ["@comment"] = { fg = frappe.surface2, style = { "italic" } }, + } + end, + macchiato = function(macchiato) + return { + LineNr = { fg = macchiato.overlay1 }, + } + end, + mocha = function(mocha) + return { + Comment = { fg = mocha.flamingo }, + } + end, + }, +} +``` + +# Integrations + +Catppuccin provides theme support for other plugins in the Neovim ecosystem and extended Neovim functionality through _integrations_. + +To enable/disable an integration you just need to set it to true/false, for example: + +```lua +require("catppuccin").setup({ + integrations = { + cmp = true, + gitsigns = true, + nvimtree = true, + treesitter = true, + notify = false, + mini = { + enabled = true, + indentscope_color = "", + }, + } +}) +``` + +Some integrations are enabled by default, you can control this behaviour with `default_integrations` option. + +```lua +require("catppuccin").setup({ + default_integrations = false, +}) +``` + +Below is a list of supported plugins and their corresponding integration module. + +> [!Important] +> If you'd like to know which highlight groups are being affected by catppuccin, check out this directory: [`lua/catppuccin/groups/integrations/`](https://github.com/catppuccin/nvim/tree/main/lua/catppuccin/groups/integrations). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Plugin Default
aerial.nvim + +```lua +aerial = false +``` + +
alpha-nvim + +```lua +alpha = true +``` + +
barbar.nvim + +```lua +barbar = false +``` + +
barbecue.nvim + +```lua +barbecue = { + dim_dirname = true, -- directory name is dimmed by default + bold_basename = true, + dim_context = false, + alt_background = false, +}, +``` + +
Special + +Use this to set it up: + +```lua +require("barbecue").setup { + theme = "catppuccin", -- catppuccin-latte, catppuccin-frappe, catppuccin-macchiato, catppuccin-mocha +} +``` +
+ +
beacon.nvim + +```lua +beacon = false +``` + +
bufferline.nvim + +
Special + +Update your bufferline config to use the Catppuccin components: + +> [!NOTE] +> bufferline needs to be loaded after setting up catppuccin or it will highlight incorrectly + +```lua +use "akinsho/bufferline.nvim" { + after = "catppuccin", + config = function() + require("bufferline").setup { + highlights = require("catppuccin.groups.integrations.bufferline").get() + } + end +} +``` + +Configurations are self-explanatory, see `:h bufferline-highlights` for detailed explanations: + +```lua +local mocha = require("catppuccin.palettes").get_palette "mocha" +bufferline.setup { + highlights = require("catppuccin.groups.integrations.bufferline").get { + styles = { "italic", "bold" }, + custom = { + all = { + fill = { bg = "#000000" }, + }, + mocha = { + background = { fg = mocha.text }, + }, + latte = { + background = { fg = "#000000" }, + }, + }, + }, +} +``` + +
+ +
coc.nvim + +```lua +coc_nvim = false +``` + +
Special + +Setting `enabled` to `true` enables this integration. + +```lua +coc_nvim = true, +``` +> [!Note] +> coc.nvim by default link to native lsp highlight groups so config from `native_lsp` will also apply to coc + +In the inners tables you can set the style for the diagnostics, both `virtual_text` (what you see on the side) and `underlines` (what points directly at the thing (e.g. an error)). + +```lua +native_lsp = { + enabled = true, + virtual_text = { + errors = { "italic" }, + hints = { "italic" }, + warnings = { "italic" }, + information = { "italic" }, + }, + underlines = { + errors = { "underline" }, + hints = { "underline" }, + warnings = { "underline" }, + information = { "underline" }, + }, + inlay_hints = { + background = true, + }, +}, +``` + +
+ +
colorful-winsep.nvim + + +```lua +colorful_winsep = { + enabled = false, + color = "red", +} +``` +
dashboard-nvim + +```lua +dashboard = true +``` + +
diffview.nvim + +```lua +diffview = false +``` + +
dropbar.nvim + +```lua +dropbar = { + enabled = false, + color_mode = false, -- enable color for kind's texts, not just kind's icons +}, +``` + +
feline.nvim + + + +
Special + +Update your Feline config to use the Catppuccin components: + +```lua +local ctp_feline = require('catppuccin.groups.integrations.feline') + +ctp_feline.setup() + +require("feline").setup({ + components = ctp_feline.get(), +}) +``` + +Notice that calling `setup()` is optional. You may pass a lua table in order to change assets, settings and the colors per vim mode. + +Here are the defaults: + +```lua +local clrs = require("catppuccin.palettes").get_palette() +local ctp_feline = require('catppuccin.groups.integrations.feline') +local U = require "catppuccin.utils.colors" + +ctp_feline.setup({ + assets = { + left_separator = "", + right_separator = "", + mode_icon = "", + dir = "󰉖", + file = "󰈙", + lsp = { + server = "󰅡", + error = "", + warning = "", + info = "", + hint = "", + }, + git = { + branch = "", + added = "", + changed = "", + removed = "", + }, + }, + sett = { + text = U.vary_color({ latte = latte.base }, clrs.surface0), + bkg = U.vary_color({ latte = latte.crust }, clrs.surface0), + diffs = clrs.mauve, + extras = clrs.overlay1, + curr_file = clrs.maroon, + curr_dir = clrs.flamingo, + show_modified = true -- show if the file has been modified + }, + mode_colors = { + ["n"] = { "NORMAL", clrs.lavender }, + ["no"] = { "N-PENDING", clrs.lavender }, + ["i"] = { "INSERT", clrs.green }, + ["ic"] = { "INSERT", clrs.green }, + ["t"] = { "TERMINAL", clrs.green }, + ["v"] = { "VISUAL", clrs.flamingo }, + ["V"] = { "V-LINE", clrs.flamingo }, + [""] = { "V-BLOCK", clrs.flamingo }, + ["R"] = { "REPLACE", clrs.maroon }, + ["Rv"] = { "V-REPLACE", clrs.maroon }, + ["s"] = { "SELECT", clrs.maroon }, + ["S"] = { "S-LINE", clrs.maroon }, + [""] = { "S-BLOCK", clrs.maroon }, + ["c"] = { "COMMAND", clrs.peach }, + ["cv"] = { "COMMAND", clrs.peach }, + ["ce"] = { "COMMAND", clrs.peach }, + ["r"] = { "PROMPT", clrs.teal }, + ["rm"] = { "MORE", clrs.teal }, + ["r?"] = { "CONFIRM", clrs.mauve }, + ["!"] = { "SHELL", clrs.green }, + }, + view = { + lsp = { + progress = true, -- if true the status bar will display an lsp progress indicator + name = false, -- if true the status bar will display the lsp servers name, otherwise it will display the text "Lsp" + exclude_lsp_names = {}, -- lsp server names that should not be displayed when name is set to true + separator = "|", -- the separator used when there are multiple lsp servers + }, + } +}) +``` + +> [!Warning] +> Currently feline [doesn't officially support custom themes](https://github.com/feline-nvim/feline.nvim/issues/302). In order for `:colorscheme catppuccin-` to work you could add this autocmd as a workaround: + +```lua +vim.api.nvim_create_autocmd("ColorScheme", { + pattern = "*", + callback = function() + package.loaded["feline"] = nil + package.loaded["catppuccin.groups.integrations.feline"] = nil + require("feline").setup { + components = require("catppuccin.groups.integrations.feline").get(), + } + end, +}) +``` + +
+ +
fern.vim + +```lua +fern = false +``` + +
fidget.nvim + +```lua +fidget = false +``` + +
Special +Set `notification.window.winblend` to `0`: + +```lua +require("fidget").setup { + notification = { + window = { + winblend = 0, + }, + } + -- ... the rest of your fidget config +} +``` + +
+ +
flash.nvim + +```lua +flash = true +``` + + + +
gitsigns.nvim + +```lua +gitsigns = true +``` + + + +
harpoon + +```lua +harpoon = false +``` + + + +
headlines.nvim + +```lua +headlines = false +``` + + + +
hop.nvim + +```lua +hop = false +``` + + + +
indent-blankline.nvim + +```lua +indent_blankline = { + enabled = true, + scope_color = "", -- catppuccin color (eg. `lavender`) Default: text + colored_indent_levels = false, +}, + +``` + +
Special + +`colored_indent_levels` enables char highlights per indent level. Follow the instructions [here](https://github.com/lukas-reineke/indent-blankline.nvim#multiple-indent-colors) to set the latter up. + +
+ + + + +
leap.nvim + +```lua +leap = false +``` + + + +
lightline.vim + +
Special + +```vim +let g:lightline = {'colorscheme': 'catppuccin'} +``` + +
+ + + +
lightspeed.nvim + +```lua +lightspeed = false +``` + + + +
lspsaga.nvim + +```lua +lsp_saga = false +``` + +
Special + +For custom Lsp Kind Icon and Color + +```lua +require("lspsaga").setup { + ui = { + kind = require("catppuccin.groups.integrations.lsp_saga").custom_kind(), + }, +} +``` + +
+
lualine.nvim + +
Special + +```lua +require('lualine').setup { + options = { + theme = "catppuccin" + -- ... the rest of your lualine config + } +} +``` + +
+ + + + +
markdown + +```lua +markdown = true +``` + +
mason.nvim + +```lua +mason = false +``` + +
mini.nvim + +```lua +mini = { + enabled = true, + indentscope_color = "", -- catppuccin color (eg. `lavender`) Default: text +}, +``` + +
neo-tree.nvim + +```lua +neotree = false +``` + +
neogit + +```lua +neogit = true +``` + +
neotest + +```lua +neotest = false +``` + +
noice.nvim + +```lua +noice = false +``` + +
NormalNvim + +```lua +NormalNvim = false +``` + +
notifier.nvim + +```lua +notifier = false +``` + +
nvim-cmp + +```lua +cmp = true +``` + +
nvim-dap + +```lua +dap = true +``` + +
Special + +```lua +local sign = vim.fn.sign_define + +sign("DapBreakpoint", { text = "●", texthl = "DapBreakpoint", linehl = "", numhl = ""}) +sign("DapBreakpointCondition", { text = "●", texthl = "DapBreakpointCondition", linehl = "", numhl = ""}) +sign("DapLogPoint", { text = "◆", texthl = "DapLogPoint", linehl = "", numhl = ""}) +``` + +
+ +
nvim-dap-ui + +```lua +dap_ui = true +``` + +
nvim-lspconfig + +```lua +native_lsp = { + enabled = true, + virtual_text = { + errors = { "italic" }, + hints = { "italic" }, + warnings = { "italic" }, + information = { "italic" }, + }, + underlines = { + errors = { "underline" }, + hints = { "underline" }, + warnings = { "underline" }, + information = { "underline" }, + }, + inlay_hints = { + background = true, + }, +}, +``` + +
Special + +In the inners tables you can set the style for the diagnostics, both `virtual_text` (what you see on the side) and `underlines` (what points directly at the thing (e.g. an error)). + +
+ +
navic + +```lua +navic = { + enabled = false, + custom_bg = "NONE", -- "lualine" will set background to mantle +}, + +``` +
Special + +```lua +-- You NEED to enable highlight in nvim-navic setting or it won't work +require("nvim-navic").setup { + highlight = true +} +``` + +
+ +
nvim-notify + +```lua +notify = false +``` + +
nvim-semantic-tokens + +```lua +semantic_tokens = true +``` + +
nvim-tree.lua + +```lua +nvimtree = true +``` + +
nvim-treesitter-context + +```lua +treesitter_context = true +``` + +
nvim-treesitter + +```lua +treesitter = true +``` + +
nvim-ts-rainbow2 + +```lua +ts_rainbow2 = false +``` + +
nvim-ts-rainbow + +```lua +ts_rainbow = false +``` + +
nvim-ufo + +```lua +ufo = true +``` + +
nvim-window-picker + +```lua +window_picker = false +``` + + + +
octo.nvim + +```lua +octo = false +``` + +
overseer.nvim + +```lua +overseer = false +``` + +
pounce.nvim + +```lua +pounce = false +``` + +
rainbow-delimiters.nvim + +```lua +rainbow_delimiters = true +``` + +
reactive.nvim + +
Special + +There're 2 available presets (`cursor` and `cursorline`) for every flavour. + +Here is how you can use them. + +```lua +require('reactive').setup { + load = { 'catppuccin-mocha-cursor', 'catppuccin-mocha-cursorline' } +} +``` + +To use another flavour just replace `mocha` with the one you want to use. + +
+ +
symbols-outline.nvim + +> [!NOTE] +> This plugin has been archived by the author, consider using [outline.nvim](https://github.com/hedyhli/outline.nvim) + +```lua +symbols_outline = false +``` + +
telekasten.nvim + +```lua +telekasten = false +``` + +
telescope.nvim + +```lua +telescope = { + enabled = true, + -- style = "nvchad" +} +``` + +
trouble.nvim + +```lua +lsp_trouble = false +``` + +
vim-airline + +
Special + +```vim +let g:airline_theme = 'catppuccin' +``` + +
+ +
vim-clap + +
Special + +Use this to set it up: + +```vim +let g:clap_theme = 'catppuccin' +``` + +
+ +
vim-gitgutter + +```lua +gitgutter = false +``` + +
vim-illuminate + +```lua +illuminate = { + enabled = true, + lsp = false +} +``` + +
vim-sandwich + +```lua +sandwich = false +``` + +
vim-sneak + +```lua +vim_sneak = false +``` + +
vimwiki + +```lua +vimwiki = false +``` + +
which-key.nvim + +```lua +which_key = false +``` + +
+ +# Compile + +> **Important** +> As of 7/10/2022, catppuccin should be able to automatically recompile when the setup table changed. + +Catppuccin is a highly customizable and configurable colorscheme. This does however come at the cost of complexity and execution time. Catppuccin can pre compute the results of your configuration and store the results in a compiled lua file. We use these precached values to set it's highlights. + +By default catppuccin writes the compiled results into the system's cache directory. You can change the cache dir using: + +```lua +require("catppuccin").setup({ -- Note: On windows we replace `/` with `\` by default + compile_path = vim.fn.stdpath "cache" .. "/catppuccin" +}) +``` + +# FAQ + +## Wrong treesitter highlights + +Please disable `additional_vim_regex_highlighting` + +```lua +require("nvim-treesitter.configs").setup { + highlight = { + enable = true, + additional_vim_regex_highlighting = false + }, +} +``` + +## Colors doesn't match preview screenshots + +Catppuccin requires true color support AKA terminals support the full range of 16 million colors + +- Supported: iterm2 (macOS), kitty, wezterm, alacritty, tmux, ... + +Full list of support terminals can be found here: + +- Unsupported terminal: Terminal.app (macOS), Terminus, Terminology, ... + +Full list of Unsupported terminals can be found here: + +### For tmux users + +- [Enable true color support](https://gist.github.com/andersevenrud/015e61af2fd264371032763d4ed965b6) to fix the following [abnormal colors](https://github.com/catppuccin/nvim/issues/415): + +![image](https://user-images.githubusercontent.com/1941785/220280749-c3ab52fb-9b8a-4f04-ab98-f8c1bb41f84b.png) + +- [Enable italic font support](https://gist.github.com/gyribeiro/4192af1aced7a1b555df06bd3781a722) to fix the following [incorrect if, then, else, end highlights](https://github.com/catppuccin/nvim/issues/428): + +![image](https://user-images.githubusercontent.com/13246770/224011118-dcf0f567-650a-4eb2-8be6-0af5cf435501.png) + +# Thanks to + +- [Pocco81](https://github.com/Pocco81) +- [nullchilly](https://github.com/nullchilly) + + + +  + +

+

Copyright © 2021-present Catppuccin Org +

+ + diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/after/queries/css/highlights.scm b/config/neovim/store/lazy-plugins/catppuccin-nvim/after/queries/css/highlights.scm new file mode 100644 index 00000000..7dc8b31f --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/after/queries/css/highlights.scm @@ -0,0 +1,16 @@ +;; extends +[ +(class_name) +] @property.class + +[ + (id_name) + ] @property.id + +[ + (declaration + (plain_value) @string.plain) + ] +[ + (tag_name) + ] @type.tag diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/after/queries/javascript/highlights.scm b/config/neovim/store/lazy-plugins/catppuccin-nvim/after/queries/javascript/highlights.scm new file mode 100644 index 00000000..1ce3d330 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/after/queries/javascript/highlights.scm @@ -0,0 +1,4 @@ +;; extends +[ +"export" +] @keyword.export diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/after/queries/typescript/highlights.scm b/config/neovim/store/lazy-plugins/catppuccin-nvim/after/queries/typescript/highlights.scm new file mode 100644 index 00000000..1ce3d330 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/after/queries/typescript/highlights.scm @@ -0,0 +1,4 @@ +;; extends +[ +"export" +] @keyword.export diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/autoload/airline/themes/catppuccin.vim b/config/neovim/store/lazy-plugins/catppuccin-nvim/autoload/airline/themes/catppuccin.vim new file mode 100644 index 00000000..542c8e67 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/autoload/airline/themes/catppuccin.vim @@ -0,0 +1,72 @@ +let g:airline#themes#catppuccin#palette = {} + +function! airline#themes#catppuccin#refresh() + let s:c = has("nvim") == 1 ? luaeval('require("catppuccin.palettes").get_palette()') : luaeval('vim.dict(require("catppuccin.palettes").get_palette())') + + " Normal mode + " (Dark) + let s:N1 = [ s:c.mantle, s:c.blue, 59, 149 ] " guifg guibg ctermfg ctermbg + let s:N2 = [ s:c.blue, s:c.surface0, 149, 59 ] " guifg guibg ctermfg ctermbg + let s:N3 = [ s:c.text, s:c.mantle, 145, 16 ] " guifg guibg ctermfg ctermbg + + " Insert mode + let s:I1 = [ s:c.mantle, s:c.teal, 59, 74 ] " guifg guibg ctermfg ctermbg + let s:I2 = [ s:c.teal, s:c.surface0, 74, 59 ] " guifg guibg ctermfg ctermbg + let s:I3 = [ s:c.text, s:c.mantle, 145, 16 ] " guifg guibg ctermfg ctermbg + + " Visual mode + let s:V1 = [ s:c.mantle, s:c.mauve, 59, 209 ] " guifg guibg ctermfg ctermbg + let s:V2 = [ s:c.mauve, s:c.surface0, 209, 59 ] " guifg guibg ctermfg ctermbg + let s:V3 = [ s:c.text, s:c.mantle, 145, 16 ] " guifg guibg ctermfg ctermbg + + " Replace mode + let s:R1 = [ s:c.mantle, s:c.red, 59, 203 ] " guifg guibg ctermfg ctermbg + let s:R2 = [ s:c.red, s:c.surface0, 203, 59 ] " guifg guibg ctermfg ctermbg + + " Command mode + let s:C1 = [ s:c.base, s:c.peach, 59, 166 ] " guifg guibg ctermfg ctermbg + let s:C2 = [ s:c.peach, s:c.surface0, 166, 59 ] " guifg guibg ctermfg ctermbg + + " Warning section + let s:WR = [s:c.mantle, s:c.peach, 232, 166 ] + + " Error section + let s:ER = [s:c.mantle, s:c.red, 232, 166 ] + + + let g:airline#themes#catppuccin#palette.normal = airline#themes#generate_color_map(s:N1, s:N2, s:N3) + + let g:airline#themes#catppuccin#palette.insert = airline#themes#generate_color_map(s:I1, s:I2, s:I3) + + let g:airline#themes#catppuccin#palette.visual = airline#themes#generate_color_map(s:V1, s:V2, s:V3) + + let s:IA = [ s:N1[1], s:N3[1], s:N1[3], s:N3[3], '' ] + let g:airline#themes#catppuccin#palette.inactive = airline#themes#generate_color_map(s:IA, s:IA, s:IA) + + let g:airline#themes#catppuccin#palette.normal.airline_warning = s:WR + let g:airline#themes#catppuccin#palette.insert.airline_warning = s:WR + let g:airline#themes#catppuccin#palette.visual.airline_warning = s:WR + + let g:airline#themes#catppuccin#palette.normal.airline_warning_to_airline_error = s:WR + let g:airline#themes#catppuccin#palette.insert.airline_warning_to_airline_error = s:WR + let g:airline#themes#catppuccin#palette.visual.airline_warning_to_airline_error = s:WR + + let g:airline#themes#catppuccin#palette.normal.airline_error = s:ER + let g:airline#themes#catppuccin#palette.insert.airline_error = s:ER + let g:airline#themes#catppuccin#palette.visual.airline_error = s:ER + + " Fork replace mode from insert mode + let g:airline#themes#catppuccin#palette.replace = copy(g:airline#themes#catppuccin#palette.insert) + let g:airline#themes#catppuccin#palette.replace.airline_a = [ s:R1[0], s:R1[1], s:R1[2], s:R1[3], '' ] + let g:airline#themes#catppuccin#palette.replace.airline_b = [ s:R2[0], s:R2[1], s:R2[2], s:R2[3], '' ] + + " Terminal mode is same as insert mode + let g:airline#themes#catppuccin#palette.terminal = copy(g:airline#themes#catppuccin#palette.insert) + + " Fork command mode from normal mode + let g:airline#themes#catppuccin#palette.commandline = copy(g:airline#themes#catppuccin#palette.normal) + let g:airline#themes#catppuccin#palette.commandline.airline_a = [ s:C1[0], s:C1[1], s:C1[2], s:C1[3], '' ] + let g:airline#themes#catppuccin#palette.commandline.airline_b = [ s:C2[0], s:C2[1], s:C2[2], s:C2[3], '' ] +endfunction + +call airline#themes#catppuccin#refresh() diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/autoload/clap/themes/catppuccin.vim b/config/neovim/store/lazy-plugins/catppuccin-nvim/autoload/clap/themes/catppuccin.vim new file mode 100644 index 00000000..16efd509 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/autoload/clap/themes/catppuccin.vim @@ -0,0 +1,20 @@ +let s:c = has("nvim") == 1 ? luaeval('require("catppuccin.palettes").get_palette()') : luaeval('vim.dict(require("catppuccin.palettes").get_palette())') + +let s:p = {} + +let s:p.display = { 'guibg': s:c.mantle } + +let s:p.input = s:p.display +let s:p.indicator = { 'guifg': s:c.subtext1, 'guibg': s:c.mantle } +let s:p.spinner = { 'guifg': s:c.yellow, 'guibg': s:c.mantle, 'gui': "bold" } +let s:p.search_text = { 'guifg': s:c.text, 'guibg': s:c.mantle, 'gui': "bold" } + +let s:p.preview = { 'guibg': s:c.base } + +let s:p.selected = { 'guifg': s:c.sapphire, 'gui': "bold,underline" } +let s:p.current_selection = { 'guibg': s:c.surface0, 'gui': "bold" } + +let s:p.selected_sign = { 'guifg': s:c.red } +let s:p.current_selection_sign = copy(s:p.selected_sign) + +let g:clap#themes#catppuccin#palette = s:p diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/autoload/lightline/colorscheme/catppuccin.vim b/config/neovim/store/lazy-plugins/catppuccin-nvim/autoload/lightline/colorscheme/catppuccin.vim new file mode 100644 index 00000000..aeb778eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/autoload/lightline/colorscheme/catppuccin.vim @@ -0,0 +1,22 @@ +let s:c = has("nvim") == 1 ? luaeval('require("catppuccin.palettes").get_palette()') : luaeval('vim.dict(require("catppuccin.palettes").get_palette())') + +let s:p = {'normal': {}, 'inactive': {}, 'insert': {}, 'replace': {}, 'visual': {}, 'tabline': {}} +let s:p.normal.left = [ [ s:c.mantle, s:c.blue ], [ s:c.blue, s:c.base ] ] +let s:p.normal.middle = [ [ s:c.blue, s:c.mantle ] ] +let s:p.normal.right = [ [ s:c.overlay0, s:c.base ], [ s:c.blue, s:c.surface0 ] ] +let s:p.insert.left = [ [ s:c.mantle, s:c.teal ], [ s:c.blue, s:c.base ] ] +let s:p.visual.left = [ [ s:c.mantle, s:c.mauve ], [ s:c.blue, s:c.base ] ] +let s:p.replace.left = [ [ s:c.mantle, s:c.red ], [ s:c.blue, s:c.base ] ] + +let s:p.inactive.left = [ [ s:c.blue, s:c.base ], [ s:c.overlay0, s:c.base ] ] +let s:p.inactive.middle = [ [ s:c.surface1, s:c.base ] ] +let s:p.inactive.right = [ [ s:c.surface1, s:c.base ], [ s:c.overlay0, s:c.base ] ] + +let s:p.tabline.left = [ [ s:c.overlay0, s:c.base ], [ s:c.overlay0, s:c.base ] ] +let s:p.tabline.tabsel = [ [ s:c.blue, s:c.surface1 ], [ s:c.overlay0, s:c.base] ] +let s:p.tabline.middle = [ [ s:c.surface1, s:c.base ] ] +let s:p.tabline.right = copy(s:p.inactive.right) +let s:p.normal.error = [ [ s:c.mantle, s:c.red ] ] +let s:p.normal.warning = [ [ s:c.mantle, s:c.yellow ] ] + +let g:lightline#colorscheme#catppuccin#palette = lightline#colorscheme#fill(s:p) diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-frappe.vim b/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-frappe.vim new file mode 100644 index 00000000..c9fd8d5c --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-frappe.vim @@ -0,0 +1 @@ +lua require("catppuccin").load "frappe" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-latte.vim b/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-latte.vim new file mode 100644 index 00000000..6d877828 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-latte.vim @@ -0,0 +1 @@ +lua require("catppuccin").load "latte" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-macchiato.vim b/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-macchiato.vim new file mode 100644 index 00000000..cc4f0056 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-macchiato.vim @@ -0,0 +1 @@ +lua require("catppuccin").load "macchiato" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-mocha.vim b/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-mocha.vim new file mode 100644 index 00000000..4e6a3006 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin-mocha.vim @@ -0,0 +1 @@ +lua require("catppuccin").load "mocha" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin.vim b/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin.vim new file mode 100644 index 00000000..916847b5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/colors/catppuccin.vim @@ -0,0 +1 @@ +lua require("catppuccin").load() diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/doc/catppuccin.txt b/config/neovim/store/lazy-plugins/catppuccin-nvim/doc/catppuccin.txt new file mode 100644 index 00000000..14d2276d --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/doc/catppuccin.txt @@ -0,0 +1,918 @@ +*catppuccin.txt* Soothing pastel theme for NeoVim + +============================================================================== +Table of Contents *catppuccin-table-of-contents* + +1. Features |catppuccin-features| +2. Installation |catppuccin-installation| +3. Usage |catppuccin-usage| +4. Configuration |catppuccin-configuration| +5. Customize highlights |catppuccin-customize-highlights| + - Get catppuccin colors|catppuccin-customize-highlights-get-catppuccin-colors| + - Overwriting colors |catppuccin-customize-highlights-overwriting-colors| + - Overwriting highlight groups|catppuccin-customize-highlights-overwriting-highlight-groups| +6. Integrations |catppuccin-integrations| +7. Compile |catppuccin-compile| +8. FAQ |catppuccin-faq| + - Wrong treesitter highlights |catppuccin-faq-wrong-treesitter-highlights| + - Colors doesn’t match preview screenshots|catppuccin-faq-colors-doesn’t-match-preview-screenshots| +9. Thanks to |catppuccin-thanks-to| +10. Links |catppuccin-links| + +============================================================================== +1. Features *catppuccin-features* + +- Supports both vim and neovim (Requires neovim >= 0.8 or vim >= 9 compiled with lua >= 5.1) +- Highly configurable with 4 different flavours and ability to create your own! +- Compile user config for fastest startuptime +- Integrations with lsp, treesitter and a bunch of plugins +- Supports for many other applications + + +============================================================================== +2. Installation *catppuccin-installation* + +lazy.nvim + +>lua + { "catppuccin/nvim", name = "catppuccin", priority = 1000 } +< + +packer.nvim + +>lua + use { "catppuccin/nvim", as = "catppuccin" } +< + +vim-plug + +>vim + Plug 'catppuccin/nvim', { 'as': 'catppuccin' } +< + + +============================================================================== +3. Usage *catppuccin-usage* + +>vim + colorscheme catppuccin " catppuccin-latte, catppuccin-frappe, catppuccin-macchiato, catppuccin-mocha +< + +>lua + vim.cmd.colorscheme "catppuccin" +< + + +============================================================================== +4. Configuration *catppuccin-configuration* + +There is no need to call `setup` if you don’t want to change the default +options and settings. + +>lua + require("catppuccin").setup({ + flavour = "auto", -- latte, frappe, macchiato, mocha + background = { -- :h background + light = "latte", + dark = "mocha", + }, + transparent_background = false, -- disables setting the background color. + show_end_of_buffer = false, -- shows the '~' characters after the end of buffers + term_colors = false, -- sets terminal colors (e.g. `g:terminal_color_0`) + dim_inactive = { + enabled = false, -- dims the background color of inactive window + shade = "dark", + percentage = 0.15, -- percentage of the shade to apply to the inactive window + }, + no_italic = false, -- Force no italic + no_bold = false, -- Force no bold + no_underline = false, -- Force no underline + styles = { -- Handles the styles of general hi groups (see `:h highlight-args`): + comments = { "italic" }, -- Change the style of comments + conditionals = { "italic" }, + loops = {}, + functions = {}, + keywords = {}, + strings = {}, + variables = {}, + numbers = {}, + booleans = {}, + properties = {}, + types = {}, + operators = {}, + -- miscs = {}, -- Uncomment to turn off hard-coded styles + }, + color_overrides = {}, + custom_highlights = {}, + default_integrations = true, + integrations = { + cmp = true, + gitsigns = true, + nvimtree = true, + treesitter = true, + notify = false, + mini = { + enabled = true, + indentscope_color = "", + }, + -- For more plugins integrations please scroll down (https://github.com/catppuccin/nvim#integrations) + }, + }) + + -- setup must be called before loading + vim.cmd.colorscheme "catppuccin" +< + + +============================================================================== +5. Customize highlights *catppuccin-customize-highlights* + + +GET CATPPUCCIN COLORS *catppuccin-customize-highlights-get-catppuccin-colors* + +>lua + local latte = require("catppuccin.palettes").get_palette "latte" + local frappe = require("catppuccin.palettes").get_palette "frappe" + local macchiato = require("catppuccin.palettes").get_palette "macchiato" + local mocha = require("catppuccin.palettes").get_palette "mocha" +< + +Returns a table where the key is the name of the color and the value is its hex +value corresponding to each flavour. + + +OVERWRITING COLORS *catppuccin-customize-highlights-overwriting-colors* + +Colors can be overwritten using `color_overrides` in the setting, checkout +https://github.com/catppuccin/nvim/discussions/323 for inspirations: + +>lua + require("catppuccin").setup { + color_overrides = { + all = { + text = "#ffffff", + }, + latte = { + base = "#ff0000", + mantle = "#242424", + crust = "#474747", + }, + frappe = {}, + macchiato = {}, + mocha = {}, + } + } +< + + + [!Note] For more information check out our style-guide + + +OVERWRITING HIGHLIGHT GROUPS*catppuccin-customize-highlights-overwriting-highlight-groups* + +Global highlight groups can be overwritten in the setting, for example: + +>lua + require("catppuccin").setup { + custom_highlights = function(colors) + return { + Comment = { fg = colors.flamingo }, + TabLineSel = { bg = colors.pink }, + CmpBorder = { fg = colors.surface2 }, + Pmenu = { bg = colors.none }, + } + end + } +< + +Per flavour highlight groups can also be overwritten in the setting, for +example: + +>lua + require("catppuccin").setup { + highlight_overrides = { + all = function(colors) + return { + NvimTreeNormal = { fg = colors.none }, + CmpBorder = { fg = "#3e4145" }, + } + end, + latte = function(latte) + return { + Normal = { fg = latte.base }, + } + end, + frappe = function(frappe) + return { + ["@comment"] = { fg = frappe.surface2, style = { "italic" } }, + } + end, + macchiato = function(macchiato) + return { + LineNr = { fg = macchiato.overlay1 }, + } + end, + mocha = function(mocha) + return { + Comment = { fg = mocha.flamingo }, + } + end, + }, + } +< + + +============================================================================== +6. Integrations *catppuccin-integrations* + +Catppuccin provides theme support for other plugins in the Neovim ecosystem and +extended Neovim functionality through _integrations_. + +To enable/disable an integration you just need to set it to true/false, for +example: + +>lua + require("catppuccin").setup({ + integrations = { + cmp = true, + gitsigns = true, + nvimtree = true, + treesitter = true, + notify = false, + mini = { + enabled = true, + indentscope_color = "", + }, + } + }) +< + +Some integrations are enabled by default, you can control this behaviour with +`default_integrations` option. + +>lua + require("catppuccin").setup({ + default_integrations = false, + }) +< + +Below is a list of supported plugins and their corresponding integration +module. + + + [!Important] If you’d like to know which highlight groups are being affected + by catppuccin, check out this directory: `lua/catppuccin/groups/integrations/` + . +PluginDefaultaerial.nvim>lua + aerial = false +< + +alpha-nvim>lua + alpha = true +< + +barbar.nvim>lua + barbar = false +< + +barbecue.nvim>lua + barbecue = { + dim_dirname = true, -- directory name is dimmed by default + bold_basename = true, + dim_context = false, + alt_background = false, + }, +< + +Special ~ + +Use this to set it up: + +>lua + require("barbecue").setup { + theme = "catppuccin", -- catppuccin-latte, catppuccin-frappe, catppuccin-macchiato, catppuccin-mocha + } +< + +beacon.nvim>lua + beacon = false +< + +bufferline.nvimSpecial ~ + +Update your bufferline config to use the Catppuccin components: + + + [!NOTE] bufferline needs to be loaded after setting up catppuccin or it will + highlight incorrectly +>lua + use "akinsho/bufferline.nvim" { + after = "catppuccin", + config = function() + require("bufferline").setup { + highlights = require("catppuccin.groups.integrations.bufferline").get() + } + end + } +< + +Configurations are self-explanatory, see |bufferline-highlights| for detailed +explanations: + +>lua + local mocha = require("catppuccin.palettes").get_palette "mocha" + bufferline.setup { + highlights = require("catppuccin.groups.integrations.bufferline").get { + styles = { "italic", "bold" }, + custom = { + all = { + fill = { bg = "#000000" }, + }, + mocha = { + background = { fg = mocha.text }, + }, + latte = { + background = { fg = "#000000" }, + }, + }, + }, + } +< + +coc.nvim>lua + coc_nvim = false +< + +Special ~ + +Setting `enabled` to `true` enables this integration. + +>lua + coc_nvim = true, +< + + + [!Note] coc.nvim by default link to native lsp highlight groups so config from + `native_lsp` will also apply to coc +In the inners tables you can set the style for the diagnostics, both +`virtual_text` (what you see on the side) and `underlines` (what points +directly at the thing (e.g. an error)). + +>lua + native_lsp = { + enabled = true, + virtual_text = { + errors = { "italic" }, + hints = { "italic" }, + warnings = { "italic" }, + information = { "italic" }, + }, + underlines = { + errors = { "underline" }, + hints = { "underline" }, + warnings = { "underline" }, + information = { "underline" }, + }, + inlay_hints = { + background = true, + }, + }, +< + +colorful-winsep.nvim>lua + colorful_winsep = { + enabled = false, + color = "red", + } +< + +dashboard-nvim>lua + dashboard = true +< + +diffview.nvim>lua + diffview = false +< + +dropbar.nvim>lua + dropbar = { + enabled = false, + color_mode = false, -- enable color for kind's texts, not just kind's icons + }, +< + +feline.nvimSpecial ~ + +Update your Feline config to use the Catppuccin components: + +>lua + local ctp_feline = require('catppuccin.groups.integrations.feline') + + ctp_feline.setup() + + require("feline").setup({ + components = ctp_feline.get(), + }) +< + +Notice that calling `setup()` is optional. You may pass a lua table in order to +change assets, settings and the colors per vim mode. + +Here are the defaults: + +>lua + local clrs = require("catppuccin.palettes").get_palette() + local ctp_feline = require('catppuccin.groups.integrations.feline') + local U = require "catppuccin.utils.colors" + + ctp_feline.setup({ + assets = { + left_separator = "", + right_separator = "", + mode_icon = "", + dir = "󰉖", + file = "󰈙", + lsp = { + server = "󰅡", + error = "", + warning = "", + info = "", + hint = "", + }, + git = { + branch = "", + added = "", + changed = "", + removed = "", + }, + }, + sett = { + text = U.vary_color({ latte = latte.base }, clrs.surface0), + bkg = U.vary_color({ latte = latte.crust }, clrs.surface0), + diffs = clrs.mauve, + extras = clrs.overlay1, + curr_file = clrs.maroon, + curr_dir = clrs.flamingo, + show_modified = true -- show if the file has been modified + }, + mode_colors = { + ["n"] = { "NORMAL", clrs.lavender }, + ["no"] = { "N-PENDING", clrs.lavender }, + ["i"] = { "INSERT", clrs.green }, + ["ic"] = { "INSERT", clrs.green }, + ["t"] = { "TERMINAL", clrs.green }, + ["v"] = { "VISUAL", clrs.flamingo }, + ["V"] = { "V-LINE", clrs.flamingo }, + [""] = { "V-BLOCK", clrs.flamingo }, + ["R"] = { "REPLACE", clrs.maroon }, + ["Rv"] = { "V-REPLACE", clrs.maroon }, + ["s"] = { "SELECT", clrs.maroon }, + ["S"] = { "S-LINE", clrs.maroon }, + [""] = { "S-BLOCK", clrs.maroon }, + ["c"] = { "COMMAND", clrs.peach }, + ["cv"] = { "COMMAND", clrs.peach }, + ["ce"] = { "COMMAND", clrs.peach }, + ["r"] = { "PROMPT", clrs.teal }, + ["rm"] = { "MORE", clrs.teal }, + ["r?"] = { "CONFIRM", clrs.mauve }, + ["!"] = { "SHELL", clrs.green }, + }, + view = { + lsp = { + progress = true, -- if true the status bar will display an lsp progress indicator + name = false, -- if true the status bar will display the lsp servers name, otherwise it will display the text "Lsp" + exclude_lsp_names = {}, -- lsp server names that should not be displayed when name is set to true + separator = "|", -- the separator used when there are multiple lsp servers + }, + } + }) +< + + + [!Warning] Currently feline doesn’t officially support custom themes + . In order for + `:colorscheme catppuccin-` to work you could add this autocmd as a + workaround: +>lua + vim.api.nvim_create_autocmd("ColorScheme", { + pattern = "*", + callback = function() + package.loaded["feline"] = nil + package.loaded["catppuccin.groups.integrations.feline"] = nil + require("feline").setup { + components = require("catppuccin.groups.integrations.feline").get(), + } + end, + }) +< + +fern.vim>lua + fern = false +< + +fidget.nvim>lua + fidget = false +< + +Special ~ + +Set `notification.window.winblend` to `0`: + +>lua + require("fidget").setup { + notification = { + window = { + winblend = 0, + }, + } + -- ... the rest of your fidget config + } +< + +flash.nvim>lua + flash = true +< + +gitsigns.nvim>lua + gitsigns = true +< + +harpoon>lua + harpoon = false +< + +headlines.nvim>lua + headlines = false +< + +hop.nvim>lua + hop = false +< + +indent-blankline.nvim>lua + indent_blankline = { + enabled = true, + scope_color = "", -- catppuccin color (eg. `lavender`) Default: text + colored_indent_levels = false, + }, +< + +Special ~ + +`colored_indent_levels` enables char highlights per indent level. Follow the +instructions here + +to set the latter up. + +leap.nvim>lua + leap = false +< + +lightline.vimSpecial ~ + +>vim + let g:lightline = {'colorscheme': 'catppuccin'} +< + +lightspeed.nvim>lua + lightspeed = false +< + +lspsaga.nvim>lua + lsp_saga = false +< + +Special ~ + +For custom Lsp Kind Icon and Color + +>lua + require("lspsaga").setup { + ui = { + kind = require("catppuccin.groups.integrations.lsp_saga").custom_kind(), + }, + } +< + +lualine.nvimSpecial ~ + +>lua + require('lualine').setup { + options = { + theme = "catppuccin" + -- ... the rest of your lualine config + } + } +< + +markdown>lua + markdown = true +< + +mason.nvim>lua + mason = false +< + +mini.nvim>lua + mini = { + enabled = true, + indentscope_color = "", -- catppuccin color (eg. `lavender`) Default: text + }, +< + +neo-tree.nvim>lua + neotree = false +< + +neogit>lua + neogit = true +< + +neotest>lua + neotest = false +< + +noice.nvim>lua + noice = false +< + +NormalNvim>lua + NormalNvim = false +< + +notifier.nvim>lua + notifier = false +< + +nvim-cmp>lua + cmp = true +< + +nvim-dap>lua + dap = true +< + +Special ~ + +>lua + local sign = vim.fn.sign_define + + sign("DapBreakpoint", { text = "●", texthl = "DapBreakpoint", linehl = "", numhl = ""}) + sign("DapBreakpointCondition", { text = "●", texthl = "DapBreakpointCondition", linehl = "", numhl = ""}) + sign("DapLogPoint", { text = "◆", texthl = "DapLogPoint", linehl = "", numhl = ""}) +< + +nvim-dap-ui>lua + dap_ui = true +< + +nvim-lspconfig>lua + native_lsp = { + enabled = true, + virtual_text = { + errors = { "italic" }, + hints = { "italic" }, + warnings = { "italic" }, + information = { "italic" }, + }, + underlines = { + errors = { "underline" }, + hints = { "underline" }, + warnings = { "underline" }, + information = { "underline" }, + }, + inlay_hints = { + background = true, + }, + }, +< + +Special ~ + +In the inners tables you can set the style for the diagnostics, both +`virtual_text` (what you see on the side) and `underlines` (what points +directly at the thing (e.g. an error)). + +navic>lua + navic = { + enabled = false, + custom_bg = "NONE", -- "lualine" will set background to mantle + }, +< + +Special ~ + +>lua + -- You NEED to enable highlight in nvim-navic setting or it won't work + require("nvim-navic").setup { + highlight = true + } +< + +nvim-notify>lua + notify = false +< + +nvim-semantic-tokens>lua + semantic_tokens = true +< + +nvim-tree.lua>lua + nvimtree = true +< + +nvim-treesitter-context>lua + treesitter_context = true +< + +nvim-treesitter>lua + treesitter = true +< + +nvim-ts-rainbow2>lua + ts_rainbow2 = false +< + +nvim-ts-rainbow>lua + ts_rainbow = false +< + +nvim-ufo>lua + ufo = true +< + +nvim-window-picker>lua + window_picker = false +< + +octo.nvim>lua + octo = false +< + +overseer.nvim>lua + overseer = false +< + +pounce.nvim>lua + pounce = false +< + +rainbow-delimiters.nvim>lua + rainbow_delimiters = true +< + +reactive.nvimSpecial ~ + +There’re 2 available presets (`cursor` and `cursorline`) for every flavour. + +Here is how you can use them. + +>lua + require('reactive').setup { + load = { 'catppuccin-mocha-cursor', 'catppuccin-mocha-cursorline' } + } +< + +To use another flavour just replace `mocha` with the one you want to use. + +symbols-outline.nvim + [!NOTE] This plugin has been archived by the author, consider using + outline.nvim +>lua + symbols_outline = false +< + +telekasten.nvim>lua + telekasten = false +< + +telescope.nvim>lua + telescope = { + enabled = true, + -- style = "nvchad" + } +< + +trouble.nvim>lua + lsp_trouble = false +< + +vim-airlineSpecial ~ + +>vim + let g:airline_theme = 'catppuccin' +< + +vim-clapSpecial ~ + +Use this to set it up: + +>vim + let g:clap_theme = 'catppuccin' +< + +vim-gitgutter>lua + gitgutter = false +< + +vim-illuminate>lua + illuminate = { + enabled = true, + lsp = false + } +< + +vim-sandwich>lua + sandwich = false +< + +vim-sneak>lua + vim_sneak = false +< + +vimwiki>lua + vimwiki = false +< + +which-key.nvim>lua + which_key = false +< + + +============================================================================== +7. Compile *catppuccin-compile* + + + **Important** As of 7/10/2022, catppuccin should be able to automatically + recompile when the setup table changed. +Catppuccin is a highly customizable and configurable colorscheme. This does +however come at the cost of complexity and execution time. Catppuccin can pre +compute the results of your configuration and store the results in a compiled +lua file. We use these precached values to set it’s highlights. + +By default catppuccin writes the compiled results into the system’s cache +directory. You can change the cache dir using: + +>lua + require("catppuccin").setup({ -- Note: On windows we replace `/` with `\` by default + compile_path = vim.fn.stdpath "cache" .. "/catppuccin" + }) +< + + +============================================================================== +8. FAQ *catppuccin-faq* + + +WRONG TREESITTER HIGHLIGHTS *catppuccin-faq-wrong-treesitter-highlights* + +Please disable `additional_vim_regex_highlighting` + +>lua + require("nvim-treesitter.configs").setup { + highlight = { + enable = true, + additional_vim_regex_highlighting = false + }, + } +< + + +COLORS DOESN’T MATCH PREVIEW SCREENSHOTS*catppuccin-faq-colors-doesn’t-match-preview-screenshots* + +Catppuccin requires true color support AKA terminals support the full range of +16 million colors + +- Supported: iterm2 (macOS), kitty, wezterm, alacritty, tmux, … + +Full list of support terminals can be found here: + + +- Unsupported terminal: Terminal.app (macOS), Terminus, Terminology, … + +Full list of Unsupported terminals can be found here: + + + +FOR TMUX USERS ~ + +- Enable true color support to fix the following abnormal colors : + +- Enable italic font support to fix the following incorrect if, then, else, end highlights : + + +============================================================================== +9. Thanks to *catppuccin-thanks-to* + +- Pocco81 +- nullchilly + +============================================================================== +10. Links *catppuccin-links* + +1. *image*: https://user-images.githubusercontent.com/1941785/220280749-c3ab52fb-9b8a-4f04-ab98-f8c1bb41f84b.png +2. *image*: https://user-images.githubusercontent.com/13246770/224011118-dcf0f567-650a-4eb2-8be6-0af5cf435501.png + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-frappe.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-frappe.lua new file mode 100644 index 00000000..ecebd5ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-frappe.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.barbecue" "frappe" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-latte.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-latte.lua new file mode 100644 index 00000000..e52461d8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-latte.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.barbecue" "latte" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-macchiato.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-macchiato.lua new file mode 100644 index 00000000..5778cea3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-macchiato.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.barbecue" "macchiato" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-mocha.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-mocha.lua new file mode 100644 index 00000000..044f8716 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin-mocha.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.barbecue" "mocha" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin.lua new file mode 100644 index 00000000..b5848cf7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/barbecue/theme/catppuccin.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.barbecue"() diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/editor.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/editor.lua new file mode 100644 index 00000000..6a12ef66 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/editor.lua @@ -0,0 +1,77 @@ +local M = {} + +function M.get() + return { + ColorColumn = { bg = C.surface0 }, -- used for the columns set with 'colorcolumn' + Conceal = { fg = C.overlay1 }, -- placeholder characters substituted for concealed text (see 'conceallevel') + Cursor = { fg = C.base, bg = C.text }, -- character under the cursor + lCursor = { fg = C.base, bg = C.text }, -- the character under the cursor when |language-mapping| is used (see 'guicursor') + CursorIM = { fg = C.base, bg = C.text }, -- like Cursor, but used when in IME mode |CursorIM| + CursorColumn = { bg = C.mantle }, -- Screen-column at the cursor, when 'cursorcolumn' is set. + CursorLine = { + bg = U.vary_color({ latte = U.lighten(C.mantle, 0.70, C.base) }, U.darken(C.surface0, 0.64, C.base)), + }, -- Screen-line at the cursor, when 'cursorline' is set. Low-priority if forecrust (ctermfg OR guifg) is not set. + Directory = { fg = C.blue }, -- directory names (and other special names in listings) + EndOfBuffer = { fg = O.show_end_of_buffer and C.surface1 or C.base }, -- filler lines (~) after the end of the buffer. By default, this is highlighted like |hl-NonText|. + ErrorMsg = { fg = C.red, style = { "bold", "italic" } }, -- error messages on the command line + VertSplit = { fg = O.transparent_background and C.surface1 or C.crust }, -- the column separating vertically split windows + Folded = { fg = C.blue, bg = O.transparent_background and C.none or C.surface1 }, -- line used for closed folds + FoldColumn = { fg = C.overlay0 }, -- 'foldcolumn' + SignColumn = { fg = C.surface1 }, -- column where |signs| are displayed + SignColumnSB = { bg = C.crust, fg = C.surface1 }, -- column where |signs| are displayed + Substitute = { bg = C.surface1, fg = U.vary_color({ latte = C.red }, C.pink) }, -- |:substitute| replacement text highlighting + LineNr = { fg = C.surface1 }, -- Line number for ":number" and ":#" commands, and when 'number' or 'relativenumber' option is set. + CursorLineNr = { fg = C.lavender }, -- Like LineNr when 'cursorline' or 'relativenumber' is set for the cursor line. highlights the number in numberline. + MatchParen = { fg = C.peach, bg = C.surface1, style = { "bold" } }, -- The character under the cursor or just before it, if it is a paired bracket, and its match. |pi_paren.txt| + ModeMsg = { fg = C.text, style = { "bold" } }, -- 'showmode' message (e.g., "-- INSERT -- ") + -- MsgArea = { fg = C.text }, -- Area for messages and cmdline, don't set this highlight because of https://github.com/neovim/neovim/issues/17832 + MsgSeparator = {}, -- Separator for scrolled messages, `msgsep` flag of 'display' + MoreMsg = { fg = C.blue }, -- |more-prompt| + NonText = { fg = C.overlay0 }, -- '@' at the end of the window, characters from 'showbreak' and other characters that do not really exist in the text (e.g., ">" displayed when a double-wide character doesn't fit at the end of the line). See also |hl-EndOfBuffer|. + Normal = { fg = C.text, bg = O.transparent_background and C.none or C.base }, -- normal text + NormalNC = { + fg = C.text, + bg = (O.transparent_background and O.dim_inactive.enabled and C.dim) + or (O.dim_inactive.enabled and C.dim) + or (O.transparent_background and C.none) + or C.base, + }, -- normal text in non-current windows + NormalSB = { fg = C.text, bg = C.crust }, -- normal text in non-current windows + NormalFloat = { fg = C.text, bg = (O.transparent_background and vim.o.winblend == 0) and C.none or C.mantle }, -- Normal text in floating windows. + FloatBorder = { fg = C.blue }, + FloatTitle = { fg = C.subtext0 }, -- Title of floating windows + Pmenu = { + bg = (O.transparent_background and vim.o.pumblend == 0) and C.none or U.darken(C.surface0, 0.8, C.crust), + fg = C.overlay2, + }, -- Popup menu: normal item. + PmenuSel = { bg = C.surface1, style = { "bold" } }, -- Popup menu: selected item. + PmenuSbar = { bg = C.surface1 }, -- Popup menu: scrollbar. + PmenuThumb = { bg = C.overlay0 }, -- Popup menu: Thumb of the scrollbar. + Question = { fg = C.blue }, -- |hit-enter| prompt and yes/no questions + QuickFixLine = { bg = C.surface1, style = { "bold" } }, -- Current |quickfix| item in the quickfix window. Combined with |hl-CursorLine| when the cursor is there. + Search = { bg = U.darken(C.sky, 0.30, C.base), fg = C.text }, -- Last search pattern highlighting (see 'hlsearch'). Also used for similar items that need to stand out. + IncSearch = { bg = U.darken(C.sky, 0.90, C.base), fg = C.mantle }, -- 'incsearch' highlighting; also used for the text replaced with ":s///c" + CurSearch = { bg = C.red, fg = C.mantle }, -- 'cursearch' highlighting: highlights the current search you're on differently + SpecialKey = { link = "NonText" }, -- Unprintable characters: text displayed differently from what it really is. But not 'listchars' textspace. |hl-Whitespace| + SpellBad = { sp = C.red, style = { "undercurl" } }, -- Word that is not recognized by the spellchecker. |spell| Combined with the highlighting used otherwise. + SpellCap = { sp = C.yellow, style = { "undercurl" } }, -- Word that should start with a capital. |spell| Combined with the highlighting used otherwise. + SpellLocal = { sp = C.blue, style = { "undercurl" } }, -- Word that is recognized by the spellchecker as one that is used in another region. |spell| Combined with the highlighting used otherwise. + SpellRare = { sp = C.green, style = { "undercurl" } }, -- Word that is recognized by the spellchecker as one that is hardly ever used. |spell| Combined with the highlighting used otherwise. + StatusLine = { fg = C.text, bg = O.transparent_background and C.none or C.mantle }, -- status line of current window + StatusLineNC = { fg = C.surface1, bg = O.transparent_background and C.none or C.mantle }, -- status lines of not-current windows Note: if this is equal to "StatusLine" Vim will use "^^^" in the status line of the current window. + TabLine = { bg = C.mantle, fg = C.surface1 }, -- tab pages line, not active tab page label + TabLineFill = {}, -- tab pages line, where there are no labels + TabLineSel = { fg = C.green, bg = C.surface1 }, -- tab pages line, active tab page label + Title = { fg = C.blue, style = { "bold" } }, -- titles for output from ":set all", ":autocmd" etc. + Visual = { bg = C.surface1, style = { "bold" } }, -- Visual mode selection + VisualNOS = { bg = C.surface1, style = { "bold" } }, -- Visual mode selection when vim is "Not Owning the Selection". + WarningMsg = { fg = C.yellow }, -- warning messages + Whitespace = { fg = C.surface1 }, -- "nbsp", "space", "tab" and "trail" in 'listchars' + WildMenu = { bg = C.overlay0 }, -- current match in 'wildmenu' completion + WinBar = { fg = C.rosewater }, + WinBarNC = { link = "WinBar" }, + WinSeparator = { fg = O.transparent_background and C.surface1 or C.crust }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/NormalNvim.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/NormalNvim.lua new file mode 100644 index 00000000..7e711efd --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/NormalNvim.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.get() + return { + MarkdownTask = { fg = C.teal, style = { "bold" } }, + MarkdownTodo = { fg = C.flamingo, style = { "bold" } }, + MarkdownNote = { fg = C.red, style = { "bold" } }, + MarkdownSee = { fg = C.blue, style = { "bold" } }, + MarkdownCheck = { fg = C.green, style = { "bold" } }, + MarkdownURL = { fg = C.lavender, style = { "bold" } }, + MarkdownExample = { fg = C.mauve, style = { "bold" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/aerial.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/aerial.lua new file mode 100644 index 00000000..a0f5b0cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/aerial.lua @@ -0,0 +1,36 @@ +local M = {} + +function M.get() + return { + AerialLine = { fg = C.yellow, bg = C.none }, + AerialGuide = { fg = C.overlay2 }, + AerialBooleanIcon = { link = "@boolean" }, + AerialClassIcon = { link = "@type" }, + AerialConstantIcon = { link = "@constant" }, + AerialConstructorIcon = { link = "@constructor" }, + AerialFieldIcon = { link = "@field" }, + AerialFunctionIcon = { link = "@function" }, + AerialMethodIcon = { link = "@method" }, + AerialNamespaceIcon = { link = "@namespace" }, + AerialNumberIcon = { link = "@number" }, + AerialOperatorIcon = { link = "@operator" }, + AerialTypeParameterIcon = { link = "@type" }, + AerialPropertyIcon = { link = "@property" }, + AerialStringIcon = { link = "@string" }, + AerialVariableIcon = { link = "@constant" }, + AerialEnumMemberIcon = { link = "@field" }, + AerialEnumIcon = { link = "@type" }, + AerialFileIcon = { link = "@text.uri" }, + AerialModuleIcon = { link = "@namespace" }, + AerialPackageIcon = { link = "@namespace" }, + AerialInterfaceIcon = { link = "@type" }, + AerialStructIcon = { link = "@type" }, + AerialEventIcon = { link = "@type" }, + AerialArrayIcon = { link = "@constant" }, + AerialObjectIcon = { link = "@type" }, + AerialKeyIcon = { link = "@type" }, + AerialNullIcon = { link = "@type" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/alpha.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/alpha.lua new file mode 100644 index 00000000..8839b11f --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/alpha.lua @@ -0,0 +1,13 @@ +local M = {} + +function M.get() + return { + AlphaShortcut = { fg = C.green }, + AlphaHeader = { fg = C.blue }, + AlphaHeaderLabel = { fg = C.peach }, + AlphaButtons = { fg = C.lavender }, + AlphaFooter = { fg = C.yellow, style = { "italic" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/barbar.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/barbar.lua new file mode 100644 index 00000000..6926ccf8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/barbar.lua @@ -0,0 +1,25 @@ +local M = {} + +function M.get() + return { + BufferCurrent = { bg = C.surface1, fg = C.text }, + BufferCurrentIndex = { bg = C.surface1, fg = C.blue }, + BufferCurrentMod = { bg = C.surface1, fg = C.yellow }, + BufferCurrentSign = { bg = C.surface1, fg = C.blue }, + BufferCurrentTarget = { bg = C.surface1, fg = C.red }, + BufferVisible = { bg = C.mantle, fg = C.text }, + BufferVisibleIndex = { bg = C.mantle, fg = C.blue }, + BufferVisibleMod = { bg = C.mantle, fg = C.yellow }, + BufferVisibleSign = { bg = C.mantle, fg = C.blue }, + BufferVisibleTarget = { bg = C.mantle, fg = C.red }, + BufferInactive = { bg = C.mantle, fg = C.overlay0 }, + BufferInactiveIndex = { bg = C.mantle, fg = C.overlay0 }, + BufferInactiveMod = { bg = C.mantle, fg = C.yellow }, + BufferInactiveSign = { bg = C.mantle, fg = C.blue }, + BufferInactiveTarget = { bg = C.mantle, fg = C.red }, + BufferTabpages = { bg = C.mantle, fg = C.none }, + BufferTabpage = { bg = C.mantle, fg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/beacon.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/beacon.lua new file mode 100644 index 00000000..5f8280c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/beacon.lua @@ -0,0 +1,9 @@ +local M = {} + +function M.get() + return { + Beacon = { bg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/bufferline.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/bufferline.lua new file mode 100644 index 00000000..454fe0d2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/bufferline.lua @@ -0,0 +1,117 @@ +local M = {} +local ctp = require "catppuccin" +local O = ctp.options + +function M.get(user_config) + user_config = user_config or {} + -- Backward compatibility + if O.integrations.bufferline then return {} end + return function() + local C = require("catppuccin.palettes").get_palette() + local transparent_background = O.transparent_background + local bg_highlight = (transparent_background and O.dim_inactive.enabled and C.dim) + or (transparent_background and "NONE") + or (O.dim_inactive.enabled and C.dim) + or C.crust + + local active_bg = transparent_background and "NONE" or C.base + local inactive_bg = transparent_background and "NONE" or C.mantle + + local separator_fg = O.transparent_background and C.surface1 or C.crust + + local styles = user_config.styles or { "bold", "italic" } + + local highlights = { + -- buffers + background = { bg = inactive_bg }, + buffer_visible = { fg = C.surface1, bg = inactive_bg }, + buffer_selected = { fg = C.text, bg = active_bg, style = styles }, -- current + -- Duplicate + duplicate_selected = { fg = C.text, bg = active_bg, style = styles }, + duplicate_visible = { fg = C.surface1, bg = inactive_bg, style = styles }, + duplicate = { fg = C.surface1, bg = inactive_bg, style = styles }, + -- tabs + tab = { fg = C.surface1, bg = inactive_bg }, + tab_selected = { fg = C.sky, bg = active_bg, bold = true }, + tab_separator = { fg = separator_fg, bg = inactive_bg }, + tab_separator_selected = { fg = separator_fg, bg = active_bg }, + + tab_close = { fg = C.red, bg = inactive_bg }, + indicator_selected = { fg = C.peach, bg = active_bg, style = styles }, + -- separators + separator = { fg = separator_fg, bg = inactive_bg }, + separator_visible = { fg = separator_fg, bg = inactive_bg }, + separator_selected = { fg = separator_fg, bg = active_bg }, + offset_separator = { fg = separator_fg, bg = active_bg }, + -- close buttons + close_button = { fg = C.surface1, bg = inactive_bg }, + close_button_visible = { fg = C.surface1, bg = inactive_bg }, + close_button_selected = { fg = C.red, bg = active_bg }, + -- Empty fill + fill = { bg = bg_highlight }, + -- Numbers + numbers = { fg = C.subtext0, bg = inactive_bg }, + numbers_visible = { fg = C.subtext0, bg = inactive_bg }, + numbers_selected = { fg = C.subtext0, bg = active_bg, style = styles }, + -- Errors + error = { fg = C.red, bg = inactive_bg }, + error_visible = { fg = C.red, bg = inactive_bg }, + error_selected = { fg = C.red, bg = active_bg, style = styles }, + error_diagnostic = { fg = C.red, bg = inactive_bg }, + error_diagnostic_visible = { fg = C.red, bg = inactive_bg }, + error_diagnostic_selected = { fg = C.red, bg = active_bg }, + -- Warnings + warning = { fg = C.yellow, bg = inactive_bg }, + warning_visible = { fg = C.yellow, bg = inactive_bg }, + warning_selected = { fg = C.yellow, bg = active_bg, style = styles }, + warning_diagnostic = { fg = C.yellow, bg = inactive_bg }, + warning_diagnostic_visible = { fg = C.yellow, bg = inactive_bg }, + warning_diagnostic_selected = { fg = C.yellow, bg = active_bg }, + -- Infos + info = { fg = C.sky, bg = inactive_bg }, + info_visible = { fg = C.sky, bg = inactive_bg }, + info_selected = { fg = C.sky, bg = active_bg, style = styles }, + info_diagnostic = { fg = C.sky, bg = inactive_bg }, + info_diagnostic_visible = { fg = C.sky, bg = inactive_bg }, + info_diagnostic_selected = { fg = C.sky, bg = active_bg }, + -- Hint + hint = { fg = C.teal, bg = inactive_bg }, + hint_visible = { fg = C.teal, bg = inactive_bg }, + hint_selected = { fg = C.teal, bg = active_bg, style = styles }, + hint_diagnostic = { fg = C.teal, bg = inactive_bg }, + hint_diagnostic_visible = { fg = C.teal, bg = inactive_bg }, + hint_diagnostic_selected = { fg = C.teal, bg = active_bg }, + -- Diagnostics + diagnostic = { fg = C.subtext0, bg = inactive_bg }, + diagnostic_visible = { fg = C.subtext0, bg = inactive_bg }, + diagnostic_selected = { fg = C.subtext0, bg = active_bg, style = styles }, + -- Modified + modified = { fg = C.peach, bg = inactive_bg }, + modified_selected = { fg = C.peach, bg = active_bg }, + } + + local user_highlights = user_config.custom or {} + highlights = + vim.tbl_deep_extend("keep", user_highlights[ctp.flavour] or {}, user_highlights.all or {}, highlights) + + for _, color in pairs(highlights) do + -- Because default is gui=bold,italic + color.italic = false + color.bold = false + + if color.style then + for _, style in pairs(color.style) do + color[style] = true + if O.no_italic and style == "italic" then color[style] = false end + if O.no_bold and style == "bold" then color[style] = false end + if O.no_underline and style == "underline" then color[style] = false end + end + end + color.style = nil + end + + return highlights + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/cmp.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/cmp.lua new file mode 100644 index 00000000..677a90f1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/cmp.lua @@ -0,0 +1,42 @@ +local M = {} + +function M.get() + return { + CmpItemAbbr = { fg = C.overlay2 }, + CmpItemAbbrDeprecated = { fg = C.overlay0, style = { "strikethrough" } }, + CmpItemKind = { fg = C.blue }, + CmpItemMenu = { fg = C.text }, + CmpItemAbbrMatch = { fg = C.text, style = { "bold" } }, + CmpItemAbbrMatchFuzzy = { fg = C.text, style = { "bold" } }, + + -- kind support + CmpItemKindSnippet = { fg = C.mauve }, + CmpItemKindKeyword = { fg = C.red }, + CmpItemKindText = { fg = C.teal }, + CmpItemKindMethod = { fg = C.blue }, + CmpItemKindConstructor = { fg = C.blue }, + CmpItemKindFunction = { fg = C.blue }, + CmpItemKindFolder = { fg = C.blue }, + CmpItemKindModule = { fg = C.blue }, + CmpItemKindConstant = { fg = C.peach }, + CmpItemKindField = { fg = C.green }, + CmpItemKindProperty = { fg = C.green }, + CmpItemKindEnum = { fg = C.green }, + CmpItemKindUnit = { fg = C.green }, + CmpItemKindClass = { fg = C.yellow }, + CmpItemKindVariable = { fg = C.flamingo }, + CmpItemKindFile = { fg = C.blue }, + CmpItemKindInterface = { fg = C.yellow }, + CmpItemKindColor = { fg = C.red }, + CmpItemKindReference = { fg = C.red }, + CmpItemKindEnumMember = { fg = C.red }, + CmpItemKindStruct = { fg = C.blue }, + CmpItemKindValue = { fg = C.peach }, + CmpItemKindEvent = { fg = C.blue }, + CmpItemKindOperator = { fg = C.blue }, + CmpItemKindTypeParameter = { fg = C.blue }, + CmpItemKindCopilot = { fg = C.teal }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/coc_nvim.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/coc_nvim.lua new file mode 100644 index 00000000..d9d8c3a4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/coc_nvim.lua @@ -0,0 +1,19 @@ +local M = {} + +function M.get() + return { + -- These groups are for the coc.nvim, see ":h coc-highlights" + -- Custom popup menu since coc v0.0.82 + CocMenuSel = { link = "PmenuSel" }, + CocPumSearch = { fg = C.sky, style = { "bold" } }, + + -- Inlay hints + CocInlayHint = { + -- fg of `Comment` + fg = C.overlay0, + style = { "italic" }, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/colorful_winsep.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/colorful_winsep.lua new file mode 100644 index 00000000..d4d5f885 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/colorful_winsep.lua @@ -0,0 +1,9 @@ +local M = {} + +function M.get() + return { + NvimSeparator = { bg = C.base, fg = C[O.integrations.colorful_winsep.color] }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dap.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dap.lua new file mode 100644 index 00000000..94c5f297 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dap.lua @@ -0,0 +1,13 @@ +local M = {} + +function M.get() + return { + DapBreakpoint = { fg = C.red }, + DapBreakpointCondition = { fg = C.yellow }, + DapBreakpointRejected = { fg = C.mauve }, + DapLogPoint = { fg = C.sky }, + DapStopped = { fg = C.maroon }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dap_ui.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dap_ui.lua new file mode 100644 index 00000000..88643e96 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dap_ui.lua @@ -0,0 +1,39 @@ +local M = {} + +function M.get() + return { + DapUIScope = { fg = C.sky }, + DapUIType = { fg = C.mauve }, + DapUIValue = { fg = C.sky }, + DapUIVariable = { fg = C.text }, + DapUIModifiedValue = { fg = C.peach }, + DapUIDecoration = { fg = C.sky }, + DapUIThread = { fg = C.green }, + DapUIStoppedThread = { fg = C.sky }, + DapUISource = { fg = C.lavender }, + DapUILineNumber = { fg = C.sky }, + DapUIFloatBorder = { fg = C.sky }, + + DapUIWatchesEmpty = { fg = C.maroon }, + DapUIWatchesValue = { fg = C.green }, + DapUIWatchesError = { fg = C.maroon }, + + DapUIBreakpointsPath = { fg = C.sky }, + DapUIBreakpointsInfo = { fg = C.green }, + DapUIBreakpointsCurrentLine = { fg = C.green, style = { "bold" } }, + DapUIBreakpointsDisabledLine = { fg = C.surface2 }, + + DapUIStepOver = { fg = C.blue }, + DapUIStepInto = { fg = C.blue }, + DapUIStepBack = { fg = C.blue }, + DapUIStepOut = { fg = C.blue }, + DapUIStop = { fg = C.red }, + DapUIPlayPause = { fg = C.green }, + DapUIRestart = { fg = C.green }, + DapUIUnavailable = { fg = C.surface1 }, + + DapUIWinSelect = { fg = C.peach }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dashboard.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dashboard.lua new file mode 100644 index 00000000..52953db4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dashboard.lua @@ -0,0 +1,18 @@ +local M = {} + +function M.get() + return { + DashboardShortCut = { fg = C.pink }, + DashboardHeader = { fg = C.blue }, + DashboardCenter = { fg = C.green }, + DashboardFooter = { fg = C.yellow, style = { "italic" } }, + DashboardMruTitle = { fg = C.sky }, + DashboardProjectTitle = { fg = C.sky }, + DashboardFiles = { fg = C.lavender }, + DashboardKey = { fg = C.peach }, + DashboardDesc = { fg = C.blue }, + DashboardIcon = { fg = C.pink, bold = true }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/diffview.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/diffview.lua new file mode 100644 index 00000000..b14b482d --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/diffview.lua @@ -0,0 +1,41 @@ +local M = {} + +function M.get() + return { + DiffviewDim1 = { link = "Comment" }, + DiffviewPrimary = { fg = C.blue }, + DiffviewSecondary = { fg = C.green }, + DiffviewNormal = { fg = C.text, bg = O.transparent_background and C.none or C.mantle }, + DiffviewWinSeparator = { + fg = O.transparent_background and C.surface1 or C.base, + bg = O.transparent_background and C.none or C.base, + }, + DiffviewFilePanelTitle = { fg = C.blue, style = { "bold" } }, + DiffviewFilePanelCounter = { fg = C.text }, + DiffviewFilePanelRootPath = { fg = C.lavender, style = { "bold" } }, + DiffviewFilePanelFileName = { fg = C.text }, + DiffviewFilePanelSelected = { fg = C.yellow }, + DiffviewFilePanelPath = { link = "Comment" }, + DiffviewFilePanelInsertions = { fg = C.green }, + DiffviewFilePanelDeletions = { fg = C.red }, + DiffviewFilePanelConflicts = { fg = C.yellow }, + DiffviewFolderName = { fg = C.blue, style = { "bold" } }, + DiffviewFolderSign = { fg = C.blue }, + DiffviewHash = { fg = C.flamingo }, + DiffviewReference = { fg = C.blue, style = { "bold" } }, + DiffviewReflogSelector = { fg = C.pink }, + DiffviewStatusAdded = { fg = C.green }, + DiffviewStatusUntracked = { fg = C.green }, + DiffviewStatusModified = { fg = C.yellow }, + DiffviewStatusRenamed = { fg = C.yellow }, + DiffviewStatusCopied = { fg = C.yellow }, + DiffviewStatusTypeChange = { fg = C.yellow }, + DiffviewStatusUnmerged = { fg = C.yellow }, + DiffviewStatusUnknown = { fg = C.red }, + DiffviewStatusDeleted = { fg = C.red }, + DiffviewStatusBroken = { fg = C.red }, + DiffviewStatusIgnored = { fg = C.overlay0 }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dropbar.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dropbar.lua new file mode 100644 index 00000000..712a9148 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/dropbar.lua @@ -0,0 +1,69 @@ +local M = {} + +function M.get() + local color = O.integrations.dropbar.color_mode + return { + DropBarMenuHoverEntry = { link = "Visual" }, + DropBarMenuHoverIcon = { reverse = true }, + DropBarMenuHoverSymbol = { bold = true }, + DropBarIconUISeparator = { fg = C.overlay1 }, + DropBarKindArray = color and { link = "DropBarIconKindArray" } or { fg = C.text }, + DropBarKindBoolean = color and { link = "DropBarIconKindBoolean" } or { fg = C.text }, + DropBarKindBreakStatement = color and { link = "DropBarIconKindBreakStatement" } or { fg = C.text }, + DropBarKindCall = color and { link = "DropBarIconKindCall" } or { fg = C.text }, + DropBarKindCaseStatement = color and { link = "DropBarIconKindCaseStatement" } or { fg = C.text }, + DropBarKindClass = color and { link = "DropBarIconKindClass" } or { fg = C.text }, + DropBarKindConstant = color and { link = "DropBarIconKindConstant" } or { fg = C.text }, + DropBarKindConstructor = color and { link = "DropBarIconKindConstructor" } or { fg = C.text }, + DropBarKindContinueStatement = color and { link = "DropBarIconKindContinueStatement" } or { fg = C.text }, + DropBarKindDeclaration = color and { link = "DropBarIconKindDeclaration" } or { fg = C.text }, + DropBarKindDelete = color and { link = "DropBarIconKindDelete" } or { fg = C.text }, + DropBarKindDoStatement = color and { link = "DropBarIconKindDoStatement" } or { fg = C.text }, + DropBarKindElseStatement = color and { link = "DropBarIconKindElseStatement" } or { fg = C.text }, + DropBarKindEnum = color and { link = "DropBarIconKindEnum" } or { fg = C.text }, + DropBarKindEnumMember = color and { link = "DropBarIconKindEnumMember" } or { fg = C.text }, + DropBarKindEvent = color and { link = "DropBarIconKindEvent" } or { fg = C.text }, + DropBarKindField = color and { link = "DropBarIconKindField" } or { fg = C.text }, + DropBarKindFile = color and { link = "DropBarIconKindFile" } or { fg = C.text }, + DropBarKindFolder = color and { link = "DropBarIconKindFolder" } or { fg = C.text }, + DropBarKindForStatement = color and { link = "DropBarIconKindForStatement" } or { fg = C.text }, + DropBarKindFunction = color and { link = "DropBarIconKindFunction" } or { fg = C.text }, + DropBarKindIdentifier = color and { link = "DropBarIconKindIdentifier" } or { fg = C.text }, + DropBarKindIfStatement = color and { link = "DropBarIconKindIfStatement" } or { fg = C.text }, + DropBarKindInterface = color and { link = "DropBarIconKindInterface" } or { fg = C.text }, + DropBarKindKeyword = color and { link = "DropBarIconKindKeyword" } or { fg = C.text }, + DropBarKindList = color and { link = "DropBarIconKindList" } or { fg = C.text }, + DropBarKindMacro = color and { link = "DropBarIconKindMacro" } or { fg = C.text }, + DropBarKindMarkdownH1 = color and { link = "DropBarIconKindMarkdownH1" } or { fg = C.text }, + DropBarKindMarkdownH2 = color and { link = "DropBarIconKindMarkdownH2" } or { fg = C.text }, + DropBarKindMarkdownH3 = color and { link = "DropBarIconKindMarkdownH3" } or { fg = C.text }, + DropBarKindMarkdownH4 = color and { link = "DropBarIconKindMarkdownH4" } or { fg = C.text }, + DropBarKindMarkdownH5 = color and { link = "DropBarIconKindMarkdownH5" } or { fg = C.text }, + DropBarKindMarkdownH6 = color and { link = "DropBarIconKindMarkdownH6" } or { fg = C.text }, + DropBarKindMethod = color and { link = "DropBarIconKindMethod" } or { fg = C.text }, + DropBarKindModule = color and { link = "DropBarIconKindModule" } or { fg = C.text }, + DropBarKindNamespace = color and { link = "DropBarIconKindNamespace" } or { fg = C.text }, + DropBarKindNull = color and { link = "DropBarIconKindNull" } or { fg = C.text }, + DropBarKindNumber = color and { link = "DropBarIconKindNumber" } or { fg = C.text }, + DropBarKindObject = color and { link = "DropBarIconKindObject" } or { fg = C.text }, + DropBarKindOperator = color and { link = "DropBarIconKindOperator" } or { fg = C.text }, + DropBarKindPackage = color and { link = "DropBarIconKindPackage" } or { fg = C.text }, + DropBarKindProperty = color and { link = "DropBarIconKindProperty" } or { fg = C.text }, + DropBarKindReference = color and { link = "DropBarIconKindReference" } or { fg = C.text }, + DropBarKindRepeat = color and { link = "DropBarIconKindRepeat" } or { fg = C.text }, + DropBarKindScope = color and { link = "DropBarIconKindScope" } or { fg = C.text }, + DropBarKindSpecifier = color and { link = "DropBarIconKindSpecifier" } or { fg = C.text }, + DropBarKindStatement = color and { link = "DropBarIconKindStatement" } or { fg = C.text }, + DropBarKindString = color and { link = "DropBarIconKindString" } or { fg = C.text }, + DropBarKindStruct = color and { link = "DropBarIconKindStruct" } or { fg = C.text }, + DropBarKindSwitchStatement = color and { link = "DropBarIconKindSwitchStatement" } or { fg = C.text }, + DropBarKindType = color and { link = "DropBarIconKindType" } or { fg = C.text }, + DropBarKindTypeParameter = color and { link = "DropBarIconKindTypeParameter" } or { fg = C.text }, + DropBarKindUnit = color and { link = "DropBarIconKindUnit" } or { fg = C.text }, + DropBarKindValue = color and { link = "DropBarIconKindValue" } or { fg = C.text }, + DropBarKindVariable = color and { link = "DropBarIconKindVariable" } or { fg = C.text }, + DropBarKindWhileStatement = color and { link = "DropBarIconKindWhileStatement" } or { fg = C.text }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/feline.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/feline.lua new file mode 100644 index 00000000..f0d9c024 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/feline.lua @@ -0,0 +1,501 @@ +local M = {} + +local C = require("catppuccin.palettes").get_palette() +local lsp = require "feline.providers.lsp" + +local assets = { + left_separator = "", + right_separator = "", + mode_icon = "", + dir = "󰉖", + file = "󰈙", + lsp = { + server = "󰅡", + error = "", + warning = "", + info = "", + hint = "", + }, + git = { + branch = "", + added = "", + changed = "", + removed = "", + }, +} + +local sett = { + text = C.mantle, + bkg = C.crust, + diffs = C.mauve, + extras = C.overlay1, + curr_file = C.maroon, + curr_dir = C.flamingo, + show_modified = false, +} + +if require("catppuccin").flavour == "latte" then + local latte = require("catppuccin.palettes").get_palette "latte" + sett.text = latte.base + sett.bkg = latte.crust +end + +if require("catppuccin").options.transparent_background then sett.bkg = "NONE" end + +local mode_colors = { + ["n"] = { "NORMAL", C.lavender }, + ["no"] = { "N-PENDING", C.lavender }, + ["i"] = { "INSERT", C.green }, + ["ic"] = { "INSERT", C.green }, + ["t"] = { "TERMINAL", C.green }, + ["v"] = { "VISUAL", C.flamingo }, + ["V"] = { "V-LINE", C.flamingo }, + [""] = { "V-BLOCK", C.flamingo }, + ["R"] = { "REPLACE", C.maroon }, + ["Rv"] = { "V-REPLACE", C.maroon }, + ["s"] = { "SELECT", C.maroon }, + ["S"] = { "S-LINE", C.maroon }, + [""] = { "S-BLOCK", C.maroon }, + ["c"] = { "COMMAND", C.peach }, + ["cv"] = { "COMMAND", C.peach }, + ["ce"] = { "COMMAND", C.peach }, + ["r"] = { "PROMPT", C.teal }, + ["rm"] = { "MORE", C.teal }, + ["r?"] = { "CONFIRM", C.mauve }, + ["!"] = { "SHELL", C.green }, +} + +local view = { + lsp = { + progress = true, + name = false, + exclude_lsp_names = {}, + separator = "|", + }, +} + +local is_lsp_in_excluded_list = function(lsp_name) + for _, excluded_lsp in ipairs(view.lsp.exclude_lsp_names) do + if lsp_name == excluded_lsp then return true end + end + return false +end + +function M.setup(opts) + if opts then + opts.assets = opts.assets or {} + opts.sett = opts.sett or {} + opts.mode_colors = opts.mode_colors or {} + opts.view = opts.view or {} + else + opts = { + assets = {}, + sett = {}, + mode_colors = {}, + view = {}, + } + end + assets = vim.tbl_deep_extend("force", assets, opts.assets) + sett = vim.tbl_deep_extend("force", sett, opts.sett) + mode_colors = vim.tbl_deep_extend("force", mode_colors, opts.mode_colors) + view = vim.tbl_deep_extend("force", view, opts.view) +end + +function M.get() + local shortline = false + + local components = { + active = { {}, {}, {} }, -- left, center, right + inactive = { {} }, + } + + local function is_enabled(min_width) + if shortline then return true end + + return vim.api.nvim_win_get_width(0) > min_width + end + + -- global components + local invi_sep = { + str = " ", + hl = { + fg = sett.bkg, + bg = sett.bkg, + }, + } + + -- helpers + local function any_git_changes() + local gst = vim.b.gitsigns_status_dict -- git stats + if gst then + if + gst["added"] and gst["added"] > 0 + or gst["removed"] and gst["removed"] > 0 + or gst["changed"] and gst["changed"] > 0 + then + return true + end + end + return false + end + + -- #################### STATUSLINE -> + + -- ######## Left + + -- Current vi mode ------> + local vi_mode_hl = function() + return { + fg = sett.text, + bg = mode_colors[vim.fn.mode()][2], + style = "bold", + } + end + + components.active[1][1] = { + provider = " " .. assets.mode_icon .. " ", + hl = function() + return { + fg = sett.text, + bg = mode_colors[vim.fn.mode()][2], + } + end, + } + + components.active[1][2] = { + provider = function() return mode_colors[vim.fn.mode()][1] .. " " end, + hl = vi_mode_hl, + } + + -- there is a dilemma: we need to hide Diffs if there is no git info. We can do that, but this will + -- leave the right_separator colored with purple, and since we can't change the color conditonally + -- then the solution is to create two right_separators: one with a mauve sett.bkg and the other one normal + -- sett.bkg; both have the same fg (vi mode). The mauve one appears if there is git info, else the one with + -- the normal sett.bkg appears. Fixed :) + + -- enable if git diffs are not available + components.active[1][3] = { + provider = assets.right_separator, + hl = function() + return { + fg = mode_colors[vim.fn.mode()][2], + bg = sett.bkg, + } + end, + enabled = function() return not any_git_changes() end, + } + + -- enable if git diffs are available + components.active[1][4] = { + provider = assets.right_separator, + hl = function() + return { + fg = mode_colors[vim.fn.mode()][2], + bg = sett.diffs, + } + end, + enabled = function() return any_git_changes() end, + } + -- Current vi mode ------> + + -- Diffs ------> + components.active[1][5] = { + provider = "git_diff_added", + hl = { + fg = sett.text, + bg = sett.diffs, + }, + icon = " " .. assets.git.added .. " ", + } + + components.active[1][6] = { + provider = "git_diff_changed", + hl = { + fg = sett.text, + bg = sett.diffs, + }, + icon = " " .. assets.git.changed .. " ", + } + + components.active[1][7] = { + provider = "git_diff_removed", + hl = { + fg = sett.text, + bg = sett.diffs, + }, + icon = " " .. assets.git.removed .. " ", + } + + components.active[1][8] = { + provider = " ", + hl = { + fg = sett.bkg, + bg = sett.diffs, + }, + enabled = function() return any_git_changes() end, + } + + components.active[1][9] = { + provider = assets.right_separator, + hl = { + fg = sett.diffs, + bg = sett.bkg, + }, + enabled = function() return any_git_changes() end, + } + -- Diffs ------> + + -- Extras ------> + + -- file progress + components.active[1][10] = { + provider = function() + local current_line = vim.fn.line "." + local total_line = vim.fn.line "$" + + if current_line == 1 then + return "Top" + elseif current_line == vim.fn.line "$" then + return "Bot" + end + local result, _ = math.modf((current_line / total_line) * 100) + return result .. "%%" + end, + -- enabled = shortline or function(winid) + -- return vim.api.nvim_win_get_width(winid) > 90 + -- end, + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + left_sep = invi_sep, + } + + -- position + components.active[1][11] = { + provider = "position", + -- enabled = shortline or function(winid) + -- return vim.api.nvim_win_get_width(winid) > 90 + -- end, + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + left_sep = invi_sep, + } + + -- macro + components.active[1][12] = { + provider = "macro", + enabled = function() return vim.api.nvim_get_option "cmdheight" == 0 end, + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + left_sep = invi_sep, + } + + -- search count + components.active[1][13] = { + provider = "search_count", + enabled = function() return vim.api.nvim_get_option "cmdheight" == 0 end, + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + left_sep = invi_sep, + } + -- Extras ------> + + -- ######## Left + + -- ######## Center + + -- Diagnostics ------> + -- workspace loader + components.active[2][1] = { + provider = function() + if vim.lsp.status then return "" end + local Lsp = vim.lsp.util.get_progress_messages()[1] + + if Lsp then + local msg = Lsp.message or "" + local percentage = Lsp.percentage + if not percentage then return "" end + local title = Lsp.title or "" + local spinners = { + "", + "󰀚", + "", + } + local success_icon = { + "", + "", + "", + } + local ms = vim.loop.hrtime() / 1000000 + local frame = math.floor(ms / 120) % #spinners + + if percentage >= 70 then + return string.format(" %%<%s %s %s (%s%%%%) ", success_icon[frame + 1], title, msg, percentage) + end + + return string.format(" %%<%s %s %s (%s%%%%) ", spinners[frame + 1], title, msg, percentage) + end + + return "" + end, + enabled = is_enabled(80) and view.lsp.progress == true, + hl = { + fg = C.rosewater, + bg = sett.bkg, + }, + } + + -- general diagnostics (errors, warnings. info and hints) + components.active[2][2] = { + provider = "diagnostic_errors", + enabled = function() return lsp.diagnostics_exist(vim.diagnostic.severity.ERROR) end, + + hl = { + fg = C.red, + bg = sett.bkg, + }, + icon = " " .. assets.lsp.error .. " ", + } + + components.active[2][3] = { + provider = "diagnostic_warnings", + enabled = function() return lsp.diagnostics_exist(vim.diagnostic.severity.WARN) end, + hl = { + fg = C.yellow, + bg = sett.bkg, + }, + icon = " " .. assets.lsp.warning .. " ", + } + + components.active[2][4] = { + provider = "diagnostic_info", + enabled = function() return lsp.diagnostics_exist(vim.diagnostic.severity.INFO) end, + hl = { + fg = C.sky, + bg = sett.bkg, + }, + icon = " " .. assets.lsp.info .. " ", + } + + components.active[2][5] = { + provider = "diagnostic_hints", + enabled = function() return lsp.diagnostics_exist(vim.diagnostic.severity.HINT) end, + hl = { + fg = C.rosewater, + bg = sett.bkg, + }, + icon = " " .. assets.lsp.hint .. " ", + } + -- Diagnostics ------> + + -- ######## Center + + -- ######## Right + + components.active[3][1] = { + provider = "git_branch", + enabled = is_enabled(70), + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + icon = assets.git.branch .. " ", + right_sep = invi_sep, + } + + components.active[3][2] = { + provider = function() + local active_clients = vim.lsp.get_active_clients { bufnr = 0 } + + -- show an indicator that we have running lsps + if view.lsp.name == false and next(active_clients) ~= nil then return assets.lsp.server .. " " .. "Lsp" end + + -- show the actual name of the runing lsps + local index = 0 + local lsp_names = "" + for _, lsp_config in ipairs(active_clients) do + if is_lsp_in_excluded_list(lsp_config.name) then goto continue end + + index = index + 1 + if index == 1 then + lsp_names = assets.lsp.server .. " " .. lsp_config.name + else + lsp_names = lsp_names .. view.lsp.separator .. lsp_config.name + end + + ::continue:: + end + + return lsp_names + end, + + hl = { + fg = sett.extras, + bg = sett.bkg, + }, + + right_sep = invi_sep, + } + + components.active[3][3] = { + provider = function() + local filename = vim.fn.expand "%:t" + local extension = vim.fn.expand "%:e" + local present, icons = pcall(require, "nvim-web-devicons") + local icon = present and icons.get_icon(filename, extension) or assets.file + return (sett.show_modified and "%m" or "") .. " " .. icon .. " " .. filename .. " " + end, + enabled = is_enabled(70), + hl = { + fg = sett.text, + bg = sett.curr_file, + }, + left_sep = { + str = assets.left_separator, + hl = { + fg = sett.curr_file, + bg = sett.bkg, + }, + }, + } + + components.active[3][4] = { + provider = function() + local dir_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":t") + return " " .. assets.dir .. " " .. dir_name .. " " + end, + enabled = is_enabled(80), + hl = { + fg = sett.text, + bg = sett.curr_dir, + }, + left_sep = { + str = assets.left_separator, + hl = { + fg = sett.curr_dir, + bg = sett.curr_file, + }, + }, + } + -- ######## Right + + -- Inanctive components + components.inactive[1][1] = { + provider = function() return " " .. string.upper(vim.bo.ft) .. " " end, + hl = { + fg = C.overlay2, + bg = C.mantle, + }, + } + + return components +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/fern.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/fern.lua new file mode 100644 index 00000000..4e951d9d --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/fern.lua @@ -0,0 +1,9 @@ +local M = {} + +function M.get() + return { + FernBranchText = { fg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/fidget.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/fidget.lua new file mode 100644 index 00000000..90882323 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/fidget.lua @@ -0,0 +1,13 @@ +local M = {} + +function M.get() + return { + FidgetTask = { + bg = O.transparent_background and C.none or C.mantle, + fg = C.text, + }, + FidgetTitle = { fg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/flash.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/flash.lua new file mode 100644 index 00000000..1a746b6b --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/flash.lua @@ -0,0 +1,14 @@ +local M = {} + +function M.get() + local bg = O.transparent_background and C.none or C.base + return { + FlashBackdrop = { fg = C.overlay0 }, + FlashLabel = { fg = C.green, bg = bg, style = { "bold" } }, + FlashMatch = { fg = C.lavender, bg = bg }, + FlashCurrent = { fg = C.peach, bg = bg }, + FlashPrompt = { link = "NormalFloat" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/gitgutter.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/gitgutter.lua new file mode 100644 index 00000000..6a8c4fc8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/gitgutter.lua @@ -0,0 +1,11 @@ +local M = {} + +function M.get() + return { + GitGutterAdd = { fg = C.green }, + GitGutterChange = { fg = C.yellow }, + GitGutterDelete = { fg = C.red }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/gitsigns.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/gitsigns.lua new file mode 100644 index 00000000..6bc89403 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/gitsigns.lua @@ -0,0 +1,53 @@ +local M = {} + +function M.get() + if type(O.integrations.gitsigns) == "boolean" then + O.integrations.gitsigns = { enabled = true, transparent = false } + end + + if O.transparent_background then + return { + GitSignsAdd = { fg = C.green }, -- diff mode: Added line |diff.txt| + GitSignsChange = { fg = C.yellow }, -- diff mode: Changed line |diff.txt| + GitSignsDelete = { fg = C.red }, -- diff mode: Deleted line |diff.txt| + + GitSignsCurrentLineBlame = { fg = C.surface1 }, + + GitSignsAddPreview = O.transparent_background and { fg = U.darken(C.green, 0.72, C.base), bg = C.none } + or { link = "DiffAdd" }, + GitSignsDeletePreview = O.transparent_background and { fg = U.darken(C.red, 0.72, C.base), bg = C.none } + or { link = "DiffDelete" }, + -- for word diff in previews + GitSignsAddInline = O.transparent_background and { + fg = C.green, + bg = C.none, + style = { "bold" }, + } or { link = "DiffAdd" }, + GitSignsDeleteInline = O.transparent_background and { + fg = C.red, + bg = C.none, + style = { "bold" }, + } or { link = "DiffDelete" }, + GitSignsChangeInline = O.transparent_background and { + fg = C.yellow, + bg = C.none, + style = { "bold" }, + } or { link = "DiffChange" }, + + GitSignsDeleteVirtLn = O.transparent_background and { bg = C.none, fg = C.red } or { link = "DiffDelete" }, + } + else + return { + GitSignsAdd = { fg = C.green }, -- diff mode: Added line |diff.txt| + GitSignsChange = { fg = C.yellow }, -- diff mode: Changed line |diff.txt| + GitSignsDelete = { fg = C.red }, -- diff mode: Deleted line |diff.txt| + + GitSignsCurrentLineBlame = { fg = C.surface1 }, + + GitSignsAddPreview = O.transparent_background and { fg = C.green, bg = C.none } or { link = "DiffAdd" }, + GitSignsDeletePreview = O.transparent_background and { fg = C.red, bg = C.none } or { link = "DiffDelete" }, + } + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/harpoon.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/harpoon.lua new file mode 100644 index 00000000..29bc2078 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/harpoon.lua @@ -0,0 +1,10 @@ +local M = {} + +function M.get() + return { + HarpoonWindow = { fg = C.text, bg = O.transparent_background and C.none or C.base }, + HarpoonBorder = { fg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/headlines.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/headlines.lua new file mode 100644 index 00000000..2b4f49c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/headlines.lua @@ -0,0 +1,18 @@ +local M = {} + +function M.get() + return { + Dash = { fg = C.overlay2, style = { "bold" } }, + Quote = { link = "@markup.strong" }, + CodeBlock = { bg = C.mantle }, + Headline = { link = "Headline1" }, + Headline1 = { bg = C.surface0, fg = C.red }, + Headline2 = { bg = C.surface0, fg = C.peach }, + Headline3 = { bg = C.surface0, fg = C.yellow }, + Headline4 = { bg = C.surface0, fg = C.green }, + Headline5 = { bg = C.surface0, fg = C.sapphire }, + Headline6 = { bg = C.surface0, fg = C.lavender }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/hop.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/hop.lua new file mode 100644 index 00000000..1f989837 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/hop.lua @@ -0,0 +1,13 @@ +local M = {} + +function M.get() + local bg = O.transparent_background and C.none or C.base + return { + HopNextKey = { bg = bg, fg = C.peach, style = { "bold", "underline" } }, + HopNextKey1 = { bg = bg, fg = C.blue, style = { "bold" } }, + HopNextKey2 = { bg = bg, fg = C.teal, style = { "bold", "italic" } }, + HopUnmatched = { bg = bg, fg = C.overlay0 }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/illuminate.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/illuminate.lua new file mode 100644 index 00000000..f214affe --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/illuminate.lua @@ -0,0 +1,14 @@ +local M = {} + +function M.get() + return { + IlluminatedWordText = { bg = U.darken(C.surface1, 0.7, C.base) }, + IlluminatedWordRead = { bg = U.darken(C.surface1, 0.7, C.base) }, + IlluminatedWordWrite = { + bg = U.darken(C.surface1, 0.7, C.base), + style = O.integrations.illuminate.lsp and { "standout" } or {}, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/indent_blankline.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/indent_blankline.lua new file mode 100644 index 00000000..15b94e69 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/indent_blankline.lua @@ -0,0 +1,24 @@ +local M = {} + +function M.get() + local scope_color = O.integrations.indent_blankline.scope_color + + local hi = { + IblIndent = { fg = C.surface0 }, + IblScope = { fg = C[scope_color] or C.text }, + } + + if O.integrations.indent_blankline.colored_indent_levels then + hi["RainbowRed"] = { blend = 0, fg = C.red } + hi["RainbowYellow"] = { blend = 0, fg = C.yellow } + hi["RainbowBlue"] = { blend = 0, fg = C.blue } + hi["RainbowOrange"] = { blend = 0, fg = C.peach } + hi["RainbowGreen"] = { blend = 0, fg = C.green } + hi["RainbowViolet"] = { blend = 0, fg = C.mauve } + hi["RainbowCyan"] = { blend = 0, fg = C.teal } + end + + return hi +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/leap.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/leap.lua new file mode 100644 index 00000000..b9bb2943 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/leap.lua @@ -0,0 +1,24 @@ +local M = {} + +function M.get() + return { + LeapMatch = { + fg = O.transparent_background and C.pink or U.vary_color({ latte = "#222222" }, U.brighten(C.green, 0.3)), + style = { "underline", "nocombine", O.transparent_background and "bold" or nil }, + }, + LeapLabelPrimary = { + fg = O.transparent_background and C.green or U.vary_color({ latte = "#222222" }, C.base), + bg = O.transparent_background and C.none + or U.vary_color({ latte = U.brighten(C.red, 0.4) }, U.brighten(C.green, 0.3)), + style = { "nocombine", O.transparent_background and "bold" or nil }, + }, + LeapLabelSecondary = { + fg = O.transparent_background and C.blue or U.vary_color({ latte = "#222222" }, C.base), + bg = O.transparent_background and C.none or U.vary_color({ latte = U.brighten(C.sky, 0.3) }, C.sky), + style = { "nocombine", O.transparent_background and "bold" or nil }, + }, + LeapBackdrop = { fg = O.transparent_background and C.overlay0 or C.none }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/lightspeed.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/lightspeed.lua new file mode 100644 index 00000000..c7039ee1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/lightspeed.lua @@ -0,0 +1,27 @@ +local M = {} + +function M.get() + return { + LightspeedLabel = { fg = C.red, style = { "bold", "underline" } }, + LightspeedLabelDistant = { + fg = U.vary_color({ latte = C.blue }, C.sky), + style = { "bold", "underline" }, + }, + LightspeedShortcut = { + fg = "#ffffff", + bg = U.increase_saturation(C.red, 0.7), + style = { "bold" }, + }, + LightspeedMaskedChar = { + fg = U.vary_color({ latte = U.lighten(C.pink, 0.7, C.text) }, U.darken(C.pink, 0.7, C.base)), + }, + LightspeedUnlabeledMatch = { fg = C.text, style = { "bold" } }, + LightspeedGreyWash = { link = "Comment" }, + LightspeedUniqueChar = { link = "LightspeedUnlabeledMatch" }, + LightspeedOneCharMatch = { link = "LightspeedShortcut" }, + LightspeedPendingOpArea = { link = "IncSearch" }, + LightspeedCursor = { link = "Cursor" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/lsp_saga.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/lsp_saga.lua new file mode 100644 index 00000000..28d71212 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/lsp_saga.lua @@ -0,0 +1,132 @@ +local M = {} + +function M.get() + return { + SagaTitle = { fg = C.teal, style = { "bold" } }, + SagaBorder = { fg = C.blue, bg = C.none }, + SagaNormal = { bg = O.transparent_background and C.none or C.base }, + SagaToggle = { fg = C.green }, + SagaCount = { fg = C.overlay1 }, + SagaBeacon = { bg = U.darken(C.surface0, 0.8, C.crust) }, + SagaVirtLine = { fg = C.surface2 }, + SagaSpinner = { fg = C.mauve, style = { "bold" } }, + SagaSpinnerTitle = { fg = C.mauve, style = { "bold" } }, + SagaText = { fg = C.text }, + SagaSelect = { fg = C.blue, style = { "bold" } }, + SagaSearch = { link = "Search" }, + SagaFinderFname = { fg = C.subtext0, style = { "bold" } }, + ActionFix = { fg = C.pink }, + ActionPreviewTitle = { fg = C.mauve, bg = O.transparent_background and C.none or C.base }, + CodeActionText = { fg = C.green }, + CodeActionNumber = { fg = C.pink }, + SagaImpIcon = { fg = C.pink }, + SagaLightBulb = { link = "DiagnosticSignHint" }, + RenameNormal = { fg = C.text }, + RenameMatch = { link = "Search" }, + DiagnosticText = { fg = C.text }, + SagaWinbarSep = { fg = C.flamingo }, + SagaDetail = { link = "Comment" }, + SagaFileName = { fg = C.overlay2, style = { "bold" } }, + SagaFolderName = { fg = C.overlay2, style = { "bold" } }, + SagaInCurrent = { fg = C.peach }, + + -- Lspkind icons support + LspKindClass = { fg = C.yellow }, + LspKindConstant = { fg = C.peach }, + LspKindConstructor = { fg = C.sapphire }, + LspKindEnum = { fg = C.yellow }, + LspKindEnumMember = { fg = C.teal }, + LspKindEvent = { fg = C.yellow }, + LspKindField = { fg = C.teal }, + LspKindFile = { fg = C.rosewater }, + LspKindFunction = { fg = C.blue }, + LspKindInterface = { fg = C.yellow }, + LspKindKey = { fg = C.red }, + LspKindMethod = { fg = C.blue }, + LspKindModule = { fg = C.blue }, + LspKindNamespace = { fg = C.blue }, + LspKindNumber = { fg = C.peach }, + LspKindOperator = { fg = C.sky }, + LspKindPackage = { fg = C.blue }, + LspKindProperty = { fg = C.teal }, + LspKindStruct = { fg = C.yellow }, + LspKindTypeParameter = { fg = C.blue }, + LspKindVariable = { fg = C.peach }, + LspKindArray = { fg = C.peach }, + LspKindBoolean = { fg = C.peach }, + LspKindNull = { fg = C.yellow }, + LspKindObject = { fg = C.yellow }, + LspKindString = { fg = C.green }, + -- ccls-specific icons. + LspKindTypeAlias = { fg = C.green }, + LspKindParameter = { fg = C.blue }, + LspKindStaticMethod = { fg = C.peach }, + -- Microsoft-specific icons. + LspKindText = { fg = C.green }, + LspKindSnippet = { fg = C.mauve }, + LspKindFolder = { fg = C.blue }, + LspKindUnit = { fg = C.green }, + LspKindValue = { fg = C.peach }, + } +end + +function M.custom_kind() + return { + File = { "󰈙 ", "LspKindFile" }, + Module = { " ", "LspKindModule" }, + Namespace = { "󰌗 ", "LspKindNamespace" }, + Package = { "󰏖 ", "LspKindPackage" }, + Class = { "󰌗 ", "LspKindClass" }, + Method = { " ", "LspKindMethod" }, + Property = { "󰆧 ", "LspKindProperty" }, + Field = { " ", "LspKindField" }, + Constructor = { " ", "LspKindConstructor" }, + Enum = { "󰒻", "LspKindEnum" }, + Interface = { " ", "LspKindInterface" }, + Function = { "󰊕 ", "LspKindFunction" }, + Variable = { " ", "LspKindVariable" }, + Constant = { " ", "LspKindConstant" }, + String = { "󰀬 ", "LspKindString" }, + Number = { "󰎠 ", "LspKindNumber" }, + Boolean = { " ", "LspKindBoolean" }, + Array = { "󰅪 ", "LspKindArray" }, + Object = { "󰅩 ", "LspKindObject" }, + Key = { "󰌋 ", "LspKindKey" }, + Null = { " ", "LspKindNull" }, + EnumMember = { " ", "LspKindEnumMember" }, + Struct = { "󰌗 ", "LspKindStruct" }, + Event = { " ", "LspKindEvent" }, + Operator = { "󰆕 ", "LspKindOperator" }, + TypeParameter = { "󰊄 ", "LspKindTypeParameter" }, + TypeAlias = { " ", "LspKindTypeAlias" }, + Parameter = { " ", "LspKindParameter" }, + StaticMethod = { "󰠄 ", "LspKindStaticMethod" }, + Macro = { " ", "LspKindMacro" }, + Text = { "󰉿 ", "LspKindText" }, + Snippet = { " ", "LspKindSnippet" }, + Folder = { " ", "LspKindFolder" }, + Unit = { "󰉻 ", "LspKindUnit" }, + Value = { " ", "LspKindValue" }, + } +end + +-- Backwards compatibility +function M.custom_colors() + local C = require("catppuccin.palettes").get_palette() + return { + normal_bg = C.base, + title_bg = C.green, + red = C.red, + magenta = C.maroon, + orange = C.peach, + yellow = C.yellow, + green = C.green, + cyan = C.sky, + blue = C.blue, + purple = C.mauve, + white = C.text, + black = C.crust, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/lsp_trouble.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/lsp_trouble.lua new file mode 100644 index 00000000..4a5005ab --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/lsp_trouble.lua @@ -0,0 +1,11 @@ +local M = {} + +function M.get() + return { + TroubleText = { fg = C.green }, + TroubleCount = { fg = C.pink, bg = O.transparent_background and C.none or C.surface1 }, + TroubleNormal = { fg = C.text, bg = O.transparent_background and C.none or C.crust }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/markdown.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/markdown.lua new file mode 100644 index 00000000..a852f709 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/markdown.lua @@ -0,0 +1,18 @@ +local M = {} + +function M.get() + return { + markdownHeadingDelimiter = { fg = C.peach, style = { "bold" } }, + markdownCode = { fg = C.flamingo }, + markdownCodeBlock = { fg = C.flamingo }, + markdownLinkText = { fg = C.blue, style = { "underline" } }, + markdownH1 = { link = "rainbow1" }, + markdownH2 = { link = "rainbow2" }, + markdownH3 = { link = "rainbow3" }, + markdownH4 = { link = "rainbow4" }, + markdownH5 = { link = "rainbow5" }, + markdownH6 = { link = "rainbow6" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/mason.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/mason.lua new file mode 100644 index 00000000..ea510773 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/mason.lua @@ -0,0 +1,38 @@ +local M = {} + +function M.get() + local bg = O.transparent_background and C.none or C.lavender + local fg = O.transparent_background and C.lavender or C.base + + local secondary_bg = O.transparent_background and C.none or C.blue + local secondary_fg = O.transparent_background and C.blue or C.base + + local muted_bg = O.transparent_background and C.none or C.overlay0 + local muted_fg = O.transparent_background and C.overlay0 or C.base + + return { + MasonHeader = { fg = fg, bg = bg, style = { "bold" } }, + MasonHeaderSecondary = { fg = secondary_fg, bg = secondary_bg, style = { "bold" } }, + + MasonHighlight = { fg = C.green }, + MasonHighlightBlock = { + bg = O.transparent_background and C.none or C.green, + fg = O.transparent_background and C.green or C.base, + }, + MasonHighlightBlockBold = { bg = secondary_bg, fg = secondary_fg, bold = true }, + + MasonHighlightSecondary = { fg = C.mauve }, + MasonHighlightBlockSecondary = { fg = secondary_fg, bg = secondary_bg }, + MasonHighlightBlockBoldSecondary = { fg = fg, bg = bg, bold = true }, + + MasonMuted = { fg = C.overlay0 }, + MasonMutedBlock = { bg = muted_bg, fg = muted_fg }, + MasonMutedBlockBold = { bg = C.yellow, fg = C.base, bold = true }, + + MasonError = { fg = C.red }, + + MasonHeading = { fg = C.lavender, bold = true }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/mini.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/mini.lua new file mode 100644 index 00000000..07596da0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/mini.lua @@ -0,0 +1,63 @@ +local M = {} + +function M.get() + local transparent_background = require("catppuccin").options.transparent_background + local bg_highlight = transparent_background and "NONE" or C.base + + local inactive_bg = transparent_background and "NONE" or C.mantle + + local indentscope_color = O.integrations.mini.indentscope_color + return { + MiniCompletionActiveParameter = { style = { "underline" } }, + + MiniCursorword = { style = { "underline" } }, + MiniCursorwordCurrent = { style = { "underline" } }, + + MiniIndentscopeSymbol = { fg = C[indentscope_color] or C.text }, + MiniIndentscopePrefix = { style = { "nocombine" } }, -- Make it invisible + + MiniJump = { fg = C.overlay2, bg = C.pink }, + + MiniJump2dSpot = { bg = C.base, fg = C.peach, style = { "bold", "underline" } }, + + MiniStarterCurrent = {}, + MiniStarterFooter = { fg = C.yellow, style = { "italic" } }, + MiniStarterHeader = { fg = C.blue }, + MiniStarterInactive = { fg = C.surface2, style = O.styles.comments }, + MiniStarterItem = { fg = C.text }, + MiniStarterItemBullet = { fg = C.blue }, + MiniStarterItemPrefix = { fg = C.pink }, + MiniStarterSection = { fg = C.flamingo }, + MiniStarterQuery = { fg = C.green }, + + MiniStatuslineDevinfo = { fg = C.subtext1, bg = C.surface1 }, + MiniStatuslineFileinfo = { fg = C.subtext1, bg = C.surface1 }, + MiniStatuslineFilename = { fg = C.text, bg = C.mantle }, + MiniStatuslineInactive = { fg = C.blue, bg = C.mantle }, + MiniStatuslineModeCommand = { fg = C.base, bg = C.peach, style = { "bold" } }, + MiniStatuslineModeInsert = { fg = C.base, bg = C.green, style = { "bold" } }, + MiniStatuslineModeNormal = { fg = C.mantle, bg = C.blue, style = { "bold" } }, + MiniStatuslineModeOther = { fg = C.base, bg = C.teal, style = { "bold" } }, + MiniStatuslineModeReplace = { fg = C.base, bg = C.red, style = { "bold" } }, + MiniStatuslineModeVisual = { fg = C.base, bg = C.mauve, style = { "bold" } }, + + MiniSurround = { bg = C.pink, fg = C.surface1 }, + + MiniTablineCurrent = { fg = C.text, bg = C.base, sp = C.red, style = { "bold", "italic", "underline" } }, + MiniTablineFill = { bg = bg_highlight }, + MiniTablineHidden = { fg = C.text, bg = inactive_bg }, + MiniTablineModifiedCurrent = { fg = C.red, bg = C.none, style = { "bold", "italic" } }, + MiniTablineModifiedHidden = { fg = C.red, bg = C.none }, + MiniTablineModifiedVisible = { fg = C.red, bg = C.none }, + MiniTablineTabpagesection = { fg = C.surface1, bg = C.base }, + MiniTablineVisible = { bg = C.none }, + + MiniTestEmphasis = { style = { "bold" } }, + MiniTestFail = { fg = C.red, style = { "bold" } }, + MiniTestPass = { fg = C.green, style = { "bold" } }, + + MiniTrailspace = { bg = C.red }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/native_lsp.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/native_lsp.lua new file mode 100644 index 00000000..e4ae10a7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/native_lsp.lua @@ -0,0 +1,100 @@ +local M = {} + +function M.get() + local virtual_text = O.integrations.native_lsp.virtual_text + local underlines = O.integrations.native_lsp.underlines + local inlay_hints = O.integrations.native_lsp.inlay_hints + + local error = C.red + local warning = C.yellow + local info = C.sky + local hint = C.teal + local darkening_percentage = 0.095 + + return { + -- These groups are for the native LSP client. Some other LSP clients may + -- use these groups, or use their own. Consult your LSP client's + -- documentation. + LspReferenceText = { bg = C.surface1 }, -- used for highlighting "text" references + LspReferenceRead = { bg = C.surface1 }, -- used for highlighting "read" references + LspReferenceWrite = { bg = C.surface1 }, -- used for highlighting "write" references + -- highlight diagnostics in numberline + + DiagnosticVirtualTextError = { + bg = O.transparent_background and C.none or U.darken(error, darkening_percentage, C.base), + fg = error, + style = virtual_text.errors, + }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticVirtualTextWarn = { + bg = O.transparent_background and C.none or U.darken(warning, darkening_percentage, C.base), + fg = warning, + style = virtual_text.warnings, + }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticVirtualTextInfo = { + bg = O.transparent_background and C.none or U.darken(info, darkening_percentage, C.base), + fg = info, + style = virtual_text.information, + }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticVirtualTextHint = { + bg = O.transparent_background and C.none or U.darken(hint, darkening_percentage, C.base), + fg = hint, + style = virtual_text.hints, + }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + + DiagnosticError = { bg = C.none, fg = error, style = virtual_text.errors }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticWarn = { bg = C.none, fg = warning, style = virtual_text.warnings }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticInfo = { bg = C.none, fg = info, style = virtual_text.information }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + DiagnosticHint = { bg = C.none, fg = hint, style = virtual_text.hints }, -- Used as the mantle highlight group. Other Diagnostic highlights link to this by default + + -- for nvim nightly + DiagnosticUnderlineError = { style = underlines.errors, sp = error }, -- Used to underline "Error" diagnostics + DiagnosticUnderlineWarn = { style = underlines.warnings, sp = warning }, -- Used to underline "Warn" diagnostics + DiagnosticUnderlineInfo = { style = underlines.information, sp = info }, -- Used to underline "Info" diagnostics + DiagnosticUnderlineHint = { style = underlines.hints, sp = hint }, -- Used to underline "Hint" diagnostics + + DiagnosticFloatingError = { fg = error }, -- Used to color "Error" diagnostic messages in diagnostics float + DiagnosticFloatingWarn = { fg = warning }, -- Used to color "Warn" diagnostic messages in diagnostics float + DiagnosticFloatingInfo = { fg = info }, -- Used to color "Info" diagnostic messages in diagnostics float + DiagnosticFloatingHint = { fg = hint }, -- Used to color "Hint" diagnostic messages in diagnostics float + + DiagnosticSignError = { fg = error }, -- Used for "Error" signs in sign column + DiagnosticSignWarn = { fg = warning }, -- Used for "Warn" signs in sign column + DiagnosticSignInfo = { fg = info }, -- Used for "Info" signs in sign column + DiagnosticSignHint = { fg = hint }, -- Used for "Hint" signs in sign column + + LspDiagnosticsDefaultError = { fg = error }, -- Used as the mantle highlight group. Other LspDiagnostic highlights link to this by default (except Underline) + LspDiagnosticsDefaultWarning = { fg = warning }, -- Used as the mantle highlight group. Other LspDiagnostic highlights link to this by default (except Underline) + LspDiagnosticsDefaultInformation = { fg = info }, -- Used as the mantle highlight group. Other LspDiagnostic highlights link to this by default (except Underline) + LspDiagnosticsDefaultHint = { fg = hint }, -- Used as the mantle highlight group. Other LspDiagnostic highlights link to this by default (except Underline) + LspSignatureActiveParameter = { fg = C.peach }, + -- LspDiagnosticsFloatingError = { }, -- Used to color "Error" diagnostic messages in diagnostics float + -- LspDiagnosticsFloatingWarning = { }, -- Used to color "Warning" diagnostic messages in diagnostics float + -- LspDiagnosticsFloatingInformation = { }, -- Used to color "Information" diagnostic messages in diagnostics float + -- LspDiagnosticsFloatingHint = { }, -- Used to color "Hint" diagnostic messages in diagnostics float + + LspDiagnosticsError = { fg = error }, + LspDiagnosticsWarning = { fg = warning }, + LspDiagnosticsInformation = { fg = info }, + LspDiagnosticsHint = { fg = hint }, + LspDiagnosticsVirtualTextError = { fg = error, style = virtual_text.errors }, -- Used for "Error" diagnostic virtual text + LspDiagnosticsVirtualTextWarning = { fg = warning, style = virtual_text.warnings }, -- Used for "Warning" diagnostic virtual text + LspDiagnosticsVirtualTextInformation = { fg = info, style = virtual_text.warnings }, -- Used for "Information" diagnostic virtual text + LspDiagnosticsVirtualTextHint = { fg = hint, style = virtual_text.hints }, -- Used for "Hint" diagnostic virtual text + LspDiagnosticsUnderlineError = { style = underlines.errors, sp = error }, -- Used to underline "Error" diagnostics + LspDiagnosticsUnderlineWarning = { style = underlines.warnings, sp = warning }, -- Used to underline "Warning" diagnostics + LspDiagnosticsUnderlineInformation = { style = underlines.information, sp = info }, -- Used to underline "Information" diagnostics + LspDiagnosticsUnderlineHint = { style = underlines.hints, sp = hint }, -- Used to underline "Hint" diagnostics + LspCodeLens = { fg = C.overlay0 }, -- virtual text of the codelens + LspCodeLensSeparator = { link = "LspCodeLens" }, -- virtual text of the codelens separators + LspInlayHint = { + -- fg of `Comment` + fg = C.overlay0, + -- bg of `CursorLine` + bg = (O.transparent_background or not inlay_hints.background) and C.none + or U.vary_color({ latte = U.lighten(C.mantle, 0.70, C.base) }, U.darken(C.surface0, 0.64, C.base)), + }, -- virtual text of the inlay hints + LspInfoBorder = { link = "FloatBorder" }, -- LspInfo border + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/navic.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/navic.lua new file mode 100644 index 00000000..da6bcbcf --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/navic.lua @@ -0,0 +1,39 @@ +local M = {} + +function M.get() + local background = O.integrations.navic.custom_bg and O.integrations.navic.custom_bg or C.none + if O.integrations.navic.custom_bg == "lualine" then background = C.mantle end + + return { + NavicIconsFile = { fg = C.blue, bg = background }, + NavicIconsModule = { fg = C.blue, bg = background }, + NavicIconsNamespace = { fg = C.blue, bg = background }, + NavicIconsPackage = { fg = C.blue, bg = background }, + NavicIconsClass = { fg = C.yellow, bg = background }, + NavicIconsMethod = { fg = C.blue, bg = background }, + NavicIconsProperty = { fg = C.green, bg = background }, + NavicIconsField = { fg = C.green, bg = background }, + NavicIconsConstructor = { fg = C.blue, bg = background }, + NavicIconsEnum = { fg = C.green, bg = background }, + NavicIconsInterface = { fg = C.yellow, bg = background }, + NavicIconsFunction = { fg = C.blue, bg = background }, + NavicIconsVariable = { fg = C.flamingo, bg = background }, + NavicIconsConstant = { fg = C.peach, bg = background }, + NavicIconsString = { fg = C.green, style = O.styles.strings, bg = background }, + NavicIconsNumber = { fg = C.peach, bg = background }, + NavicIconsBoolean = { fg = C.peach, bg = background }, + NavicIconsArray = { fg = C.peach, bg = background }, + NavicIconsObject = { fg = C.peach, bg = background }, + NavicIconsKey = { fg = C.pink, style = O.styles.keywords, bg = background }, + NavicIconsNull = { fg = C.peach, bg = background }, + NavicIconsEnumMember = { fg = C.red, bg = background }, + NavicIconsStruct = { fg = C.blue, bg = background }, + NavicIconsEvent = { fg = C.blue, bg = background }, + NavicIconsOperator = { fg = C.sky, bg = background }, + NavicIconsTypeParameter = { fg = C.blue, bg = background }, + NavicText = { fg = C.sapphire, bg = background }, + NavicSeparator = { fg = C.text, bg = background }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/neogit.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/neogit.lua new file mode 100644 index 00000000..b0499d60 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/neogit.lua @@ -0,0 +1,244 @@ +local M = {} + +function M.get() + return { + NeogitBranch = { + fg = C.peach, + style = { "bold" }, + }, + NeogitRemote = { + fg = C.green, + style = { "bold" }, + }, + NeogitUnmergedInto = { + link = "Function", + }, + NeogitUnpulledFrom = { + link = "Function", + }, + NeogitObjectId = { + link = "Comment", + }, + NeogitStash = { + link = "Comment", + }, + NeogitRebaseDone = { + link = "Comment", + }, + NeogitHunkHeader = { + bg = U.darken(C.blue, 0.095, C.base), + fg = U.darken(C.blue, 0.5, C.base), + }, + NeogitHunkHeaderHighlight = { + bg = U.darken(C.blue, 0.215, C.base), + fg = C.blue, + }, + NeogitDiffContextHighlight = { + bg = C.surface0, + }, + NeogitDiffDeleteHighlight = { + bg = U.darken(C.red, 0.345, C.base), + fg = U.lighten(C.red, 0.850, C.text), + }, + NeogitDiffAddHighlight = { + bg = U.darken(C.green, 0.345, C.base), + fg = U.lighten(C.green, 0.850, C.text), + }, + NeogitDiffDelete = { + bg = U.darken(C.red, 0.095, C.base), + fg = U.darken(C.red, 0.800, C.base), + }, + NeogitDiffAdd = { + bg = U.darken(C.green, 0.095, C.base), + fg = U.darken(C.green, 0.800, C.base), + }, + NeogitCommitViewHeader = { + bg = U.darken(C.blue, 0.300, C.base), + fg = U.lighten(C.blue, 0.800, C.text), + }, + NeogitChangeModified = { + fg = C.blue, + style = { "bold" }, + }, + NeogitChangeDeleted = { + fg = C.red, + style = { "bold" }, + }, + NeogitChangeAdded = { + fg = C.green, + style = { "bold" }, + }, + NeogitChangeRenamed = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitChangeUpdated = { + fg = C.peach, + style = { "bold" }, + }, + NeogitChangeCopied = { + fg = C.pink, + style = { "bold" }, + }, + NeogitChangeBothModified = { + fg = C.yellow, + style = { "bold" }, + }, + NeogitChangeNewFile = { + fg = C.green, + style = { "bold" }, + }, + NeogitUntrackedfiles = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitUnstagedchanges = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitUnmergedchanges = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitUnpulledchanges = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitRecentcommits = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitStagedchanges = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitStashes = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitRebasing = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitNotificationInfo = { + fg = C.blue, + }, + NeogitNotificationWarning = { + fg = C.yellow, + }, + NeogitNotificationError = { + fg = C.red, + }, + NeogitGraphRed = { + fg = C.red, + }, + NeogitGraphWhite = { + fg = C.base, + }, + NeogitGraphYellow = { + fg = C.yellow, + }, + NeogitGraphGreen = { + fg = C.green, + }, + NeogitGraphCyan = { + fg = C.blue, + }, + NeogitGraphBlue = { + fg = C.blue, + }, + NeogitGraphPurple = { + fg = C.lavender, + }, + NeogitGraphGray = { + fg = C.subtext1, + }, + NeogitGraphOrange = { + fg = C.peach, + }, + NeogitGraphBoldRed = { + fg = C.red, + style = { "bold" }, + }, + NeogitGraphBoldWhite = { + fg = C.white, + style = { "bold" }, + }, + NeogitGraphBoldYellow = { + fg = C.yellow, + style = { "bold" }, + }, + NeogitGraphBoldGreen = { + fg = C.green, + style = { "bold" }, + }, + NeogitGraphBoldCyan = { + fg = C.blue, + style = { "bold" }, + }, + NeogitGraphBoldBlue = { + fg = C.blue, + style = { "bold" }, + }, + NeogitGraphBoldPurple = { + fg = C.lavender, + style = { "bold" }, + }, + NeogitGraphBoldGray = { + fg = C.subtext1, + style = { "bold" }, + }, + NeogitDiffContext = { + bg = C.base, + }, + NeogitPopupBold = { + style = { "bold" }, + }, + NeogitPopupSwitchKey = { + fg = C.lavender, + }, + NeogitPopupOptionKey = { + fg = C.lavender, + }, + NeogitPopupConfigKey = { + fg = C.lavender, + }, + NeogitPopupActionKey = { + fg = C.lavender, + }, + NeogitFilePath = { + fg = C.blue, + style = { "italic" }, + }, + NeogitDiffHeader = { + bg = C.base, + fg = C.blue, + style = { "bold" }, + }, + NeogitDiffHeaderHighlight = { + bg = C.base, + fg = C.peach, + style = { "bold" }, + }, + NeogitUnpushedTo = { + fg = C.lavender, + style = { "bold" }, + }, + NeogitFold = { + fg = C.none, + bg = C.none, + }, + NeogitSectionHeader = { + fg = C.mauve, + style = { "bold" }, + }, + NeogitTagName = { + fg = C.yellow, + }, + NeogitTagDistance = { + fg = C.blue, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/neotest.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/neotest.lua new file mode 100644 index 00000000..5385cc09 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/neotest.lua @@ -0,0 +1,24 @@ +local M = {} + +function M.get() + return { + NeotestPassed = { fg = C.green }, + NeotestFailed = { fg = C.red }, + NeotestRunning = { fg = C.yellow }, + NeotestSkipped = { fg = C.blue }, + NeotestTest = { fg = C.text }, + NeotestNamespace = { fg = C.mauve }, + NeotestFocused = { style = { "bold", "underline" } }, + NeotestFile = { fg = C.blue }, + NeotestDir = { fg = C.blue }, + NeotestIndent = { fg = C.overlay1 }, + NeotestExpandMarker = { fg = C.overlay1 }, + NeotestAdapterName = { fg = C.maroon }, + NeotestWinSelect = { fg = C.blue, style = { "bold" } }, + NeotestMarked = { fg = C.peach, style = { "bold" } }, + NeotestTarget = { fg = C.red }, + NeotestUnknown = { fg = C.text }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/neotree.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/neotree.lua new file mode 100644 index 00000000..c5e7f0fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/neotree.lua @@ -0,0 +1,46 @@ +local M = {} + +function M.get() + local active_bg = O.transparent_background and C.none or C.mantle + local inactive_bg = O.transparent_background and C.none or C.base + return { + NeoTreeDirectoryName = { fg = C.blue }, + NeoTreeDirectoryIcon = { fg = C.blue }, + NeoTreeNormal = { fg = C.text, bg = active_bg }, + NeoTreeNormalNC = { fg = C.text, bg = active_bg }, + NeoTreeExpander = { fg = C.overlay0 }, + NeoTreeIndentMarker = { fg = C.overlay0 }, + NeoTreeRootName = { fg = C.blue, style = { "bold" } }, + NeoTreeSymbolicLinkTarget = { fg = C.pink }, + NeoTreeModified = { fg = C.peach }, + + NeoTreeGitAdded = { fg = C.green }, + NeoTreeGitConflict = { fg = C.red }, + NeoTreeGitDeleted = { fg = C.red }, + NeoTreeGitIgnored = { fg = C.overlay0 }, + NeoTreeGitModified = { fg = C.yellow }, + NeoTreeGitUnstaged = { fg = C.red }, + NeoTreeGitUntracked = { fg = C.mauve }, + NeoTreeGitStaged = { fg = C.green }, + + NeoTreeFloatBorder = { link = "FloatBorder" }, + NeoTreeFloatTitle = { link = "FloatTitle" }, + NeoTreeTitleBar = { fg = C.mantle, bg = C.blue }, + + NeoTreeFileNameOpened = { fg = C.pink }, + NeoTreeDimText = { fg = C.overlay1 }, + NeoTreeFilterTerm = { fg = C.green, style = { "bold" } }, + NeoTreeTabActive = { bg = active_bg, fg = C.lavender, style = { "bold" } }, + NeoTreeTabInactive = { bg = inactive_bg, fg = C.overlay0 }, + NeoTreeTabSeparatorActive = { fg = active_bg, bg = active_bg }, + NeoTreeTabSeparatorInactive = { fg = inactive_bg, bg = inactive_bg }, + NeoTreeVertSplit = { fg = C.base, bg = inactive_bg }, + NeoTreeWinSeparator = { + fg = O.transparent_background and C.surface1 or C.base, + bg = O.transparent_background and C.none or C.base, + }, + NeoTreeStatusLineNC = { fg = C.mantle, bg = C.mantle }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/noice.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/noice.lua new file mode 100644 index 00000000..45bf6b55 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/noice.lua @@ -0,0 +1,26 @@ +local M = {} + +local virtual_text = O.integrations.native_lsp.virtual_text + +function M.get() + return { -- Personally the default integration is already pretty good + NoiceCmdline = { fg = C.text }, + NoiceCmdlineIcon = { fg = C.sky, style = virtual_text.information }, + NoiceCmdlineIconSearch = { fg = C.yellow }, + NoiceCmdlinePopupBorder = { fg = C.lavender }, + NoiceCmdlinePopupBorderSearch = { fg = C.yellow }, + NoiceConfirmBorder = { fg = C.blue }, + NoiceMini = { fg = C.subtext0, blend = 0 }, + NoiceFormatProgressDone = { + bg = O.transparent_background and C.none or U.darken(C.sky, 0.30, C.base), + fg = C.subtext0, + }, + NoiceFormatProgressTodo = { + bg = O.transparent_background and C.none + or U.vary_color({ latte = U.lighten(C.mantle, 0.70, C.base) }, U.darken(C.surface0, 0.64, C.base)), + fg = C.subtext0, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/notifier.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/notifier.lua new file mode 100644 index 00000000..12ecfe40 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/notifier.lua @@ -0,0 +1,11 @@ +local M = {} + +function M.get() + return { + NotifierIcon = { fg = C.mauve }, + NotifierContent = { fg = C.overlay2, blend = 0, style = { "italic" } }, + NotifierContentDim = { fg = C.overlay1, blend = 0, style = { "italic" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/notify.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/notify.lua new file mode 100644 index 00000000..fcd5002c --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/notify.lua @@ -0,0 +1,25 @@ +local M = {} + +function M.get() + return { + NotifyBackground = { bg = (O.transparent_background and vim.o.winblend == 0) and "#000000" or C.mantle }, + + NotifyERRORBorder = { fg = C.red }, + NotifyERRORIcon = { fg = C.red }, + NotifyERRORTitle = { fg = C.red, style = { "italic" } }, + NotifyWARNBorder = { fg = C.yellow }, + NotifyWARNIcon = { fg = C.yellow }, + NotifyWARNTitle = { fg = C.yellow, style = { "italic" } }, + NotifyINFOBorder = { fg = C.blue }, + NotifyINFOIcon = { fg = C.blue }, + NotifyINFOTitle = { fg = C.blue, style = { "italic" } }, + NotifyDEBUGBorder = { fg = C.peach }, + NotifyDEBUGIcon = { fg = C.peach }, + NotifyDEBUGTitle = { fg = C.peach, style = { "italic" } }, + NotifyTRACEBorder = { fg = C.rosewater }, + NotifyTRACEIcon = { fg = C.rosewater }, + NotifyTRACETitle = { fg = C.rosewater, style = { "italic" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/nvimtree.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/nvimtree.lua new file mode 100644 index 00000000..725196ed --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/nvimtree.lua @@ -0,0 +1,27 @@ +local M = {} + +function M.get() + return { + NvimTreeFolderName = { fg = C.blue }, + NvimTreeFolderIcon = { fg = C.blue }, + NvimTreeNormal = { fg = C.text, bg = O.transparent_background and C.none or C.mantle }, + NvimTreeOpenedFolderName = { fg = C.blue }, + NvimTreeEmptyFolderName = { fg = C.blue }, + NvimTreeIndentMarker = { fg = C.overlay0 }, + NvimTreeWinSeparator = { + fg = O.transparent_background and C.surface1 or C.base, + bg = O.transparent_background and C.none or C.base, + }, + NvimTreeRootFolder = { fg = C.lavender, style = { "bold" } }, + NvimTreeSymlink = { fg = C.pink }, + NvimTreeStatuslineNc = { fg = C.mantle, bg = C.mantle }, + NvimTreeGitDirty = { fg = C.yellow }, + NvimTreeGitNew = { fg = C.blue }, + NvimTreeGitDeleted = { fg = C.red }, + NvimTreeSpecialFile = { fg = C.flamingo }, + NvimTreeImageFile = { fg = C.text }, + NvimTreeOpenedFile = { fg = C.pink }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/octo.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/octo.lua new file mode 100644 index 00000000..cd1fd64f --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/octo.lua @@ -0,0 +1,78 @@ +local M = {} + +function M.get() + return { + -- colors + OctoGrey = { fg = C.base }, + OctoBlue = { fg = C.blue }, + OctoGreen = { fg = C.green }, + OctoRed = { fg = C.red }, + OctoPurple = { fg = C.mauve }, + OctoYellow = { fg = C.yellow }, + -- highlight groups + OctoDirty = { link = "ErrorMsg" }, + OctoIssueTitle = { link = "PreProc" }, + OctoIssueId = { link = "Question" }, + OctoEmpty = { link = "Comment" }, + OctoFloat = { link = "NormalNC" }, + OctoDate = { link = "Comment" }, + OctoSymbol = { link = "Comment" }, + OctoTimelineItemHeading = { link = "Comment" }, + OctoDetailsLabel = { link = "Title" }, + OctoMissingDetails = { link = "Comment" }, + OctoDetailsValue = { link = "Identifier" }, + OctoDiffHunkPosition = { link = "NormalFloat" }, + OctoCommentLine = { link = "TabLineSel" }, + OctoViewer = { fg = C.base, bg = C.blue }, + OctoBubble = { fg = C.text, bg = C.mantle }, + OctoBubbleGrey = { fg = C.text, bg = C.mantle }, + OctoBubbleDelimiterGrey = { fg = C.mantle }, + OctoBubbleGreen = { fg = C.base, bg = C.green }, + OctoBubbleDelimiterGreen = { fg = C.green }, + OctoBubbleBlue = { fg = C.base, bg = C.sky }, + OctoBubbleDelimiterBlue = { fg = C.sky }, + OctoBubbleYellow = { fg = C.base, bg = C.yellow }, + OctoBubbleDelimiterYellow = { fg = C.yellow }, + OctoBubbleRed = { fg = C.base, bg = C.red }, + OctoBubbleDelimiterRed = { fg = C.red }, + OctoBubblePurple = { fg = C.base, bg = C.mauve }, + OctoBubbleDelimiterPurple = { fg = C.mauve }, + OctoUser = { link = "OctoBubble" }, + OctoUserViewer = { link = "OctoViewer" }, + OctoReaction = { link = "OctoBubble" }, + OctoReactionViewer = { link = "OctoViewer" }, + OctoPassingTest = { link = "OctoGreen" }, + OctoFailingTest = { link = "OctoRed" }, + OctoPullAdditions = { link = "OctoGreen" }, + OctoPullDeletions = { link = "OctoRed" }, + OctoPullModifications = { fg = C.peach }, + OctoStateOpen = { fg = C.green, bg = C.mantle }, + OctoStateOpenFloat = { fg = C.green, bg = C.mantle }, + OctoStateClosed = { fg = C.red, bg = C.mantle }, + OctoStateClosedFloat = { fg = C.red, bg = C.mantle }, + OctoStateMerged = { fg = C.mauve, bg = C.mantle }, + OctoStateMergedFloat = { fg = C.mauve, bg = C.mantle }, + OctoStatePending = { fg = C.peach, bg = C.mantle }, + OctoStatePendingFloat = { fg = C.peach, bg = C.mantle }, + OctoStateApproved = { link = "OctoStateOpen" }, + OctoStateApprovedFloat = { link = "OctoStateOpen" }, + OctoStateChangesRequested = { fg = C.maroon, bg = C.mantle }, + OctoStateChangesRequestedFloat = { fg = C.maroon, bg = C.mantle }, + OctoStateCommented = { link = "Normal" }, + OctoStateCommentedFloat = { link = "Normal" }, + OctoStateDismissed = { link = "OctoStateClosed" }, + OctoStateDismissedFloat = { link = "OctoStateClosed" }, + OctoStateSubmittedBubble = { link = "OctoBubbleGreen" }, + OctoFilePanelCounter = { fg = C.blue, style = { "bold" } }, + OctoFilePanelTitle = { fg = C.blue, style = { "bold" } }, + OctoNormalFront = { fg = C.text }, + OctoYellowFloat = { fg = C.yellow, bg = C.mantle }, + OctoPurpleFloat = { fg = C.mauve, bg = C.mantle }, + OctoRedFloat = { fg = C.red, bg = C.mantle }, + OctoGreenFloat = { fg = C.green, bg = C.mantle }, + OctoGreyFloat = { fg = C.text, bg = C.mantle }, + OctoBlueFloat = { fg = C.blue, bg = C.mantle }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/overseer.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/overseer.lua new file mode 100644 index 00000000..20f47b7b --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/overseer.lua @@ -0,0 +1,18 @@ +local M = {} + +function M.get() + return { + OverseerPENDING = { fg = C.grey }, + OverseerRUNNING = { fg = C.yellow }, + OverseerSUCCESS = { fg = C.green }, + OverseerCANCELED = { fg = C.overlay2 }, + OverseerFAILURE = { fg = C.red }, + OverseerTask = { fg = C.blue }, + OverseerTaskBorder = { fg = C.sky }, + OverseerOutput = { fg = C.text }, + OverseerComponent = { fg = C.yellow }, + OverseerField = { fg = C.green }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/pounce.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/pounce.lua new file mode 100644 index 00000000..608c99f6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/pounce.lua @@ -0,0 +1,12 @@ +local M = {} + +function M.get() + return { + PounceMatch = { bg = U.lighten(C.green, 0.7), fg = C.base, style = { "bold" } }, + PounceGap = { bg = U.darken(C.green, 0.7), fg = C.base, style = { "bold" } }, + PounceAccept = { bg = C.peach, fg = C.base, style = { "bold" } }, + PounceAcceptBest = { bg = C.teal, fg = C.base, style = { "bold" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/rainbow_delimiters.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/rainbow_delimiters.lua new file mode 100644 index 00000000..bfaeecf7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/rainbow_delimiters.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.get() + return { + RainbowDelimiterRed = { fg = C.red }, + RainbowDelimiterYellow = { fg = C.yellow }, + RainbowDelimiterBlue = { fg = C.blue }, + RainbowDelimiterOrange = { fg = C.peach }, + RainbowDelimiterGreen = { fg = C.green }, + RainbowDelimiterViolet = { fg = C.mauve }, + RainbowDelimiterCyan = { fg = C.teal }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/sandwich.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/sandwich.lua new file mode 100644 index 00000000..e366c845 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/sandwich.lua @@ -0,0 +1,12 @@ +local M = {} + +function M.get() + return { + OperatorSandwichAdd = { bg = C.blue, fg = C.base }, + OperatorSandwichBuns = { bg = C.blue, fg = C.base }, + OperatorSandwichChange = { bg = C.blue, fg = C.base }, + OperatorSandwichDelete = { bg = C.blue, fg = C.base }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/semantic_tokens.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/semantic_tokens.lua new file mode 100644 index 00000000..6d9094b6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/semantic_tokens.lua @@ -0,0 +1,48 @@ +local M = {} + +function M.get() + if vim.treesitter.highlighter.hl_map then + vim.notify_once( + [[Catppuccin (info): +semantic_tokens integration requires neovim 0.8 +If you want to stay on nvim 0.7, disable the integration. +]], + vim.log.levels.INFO + ) + return {} + end + return { + ["@lsp.type.boolean"] = { link = "@boolean" }, + ["@lsp.type.builtinType"] = { link = "@type.builtin" }, + ["@lsp.type.comment"] = { link = "@comment" }, + ["@lsp.type.enum"] = { link = "@type" }, + ["@lsp.type.enumMember"] = { link = "@constant" }, + ["@lsp.type.escapeSequence"] = { link = "@string.escape" }, + ["@lsp.type.formatSpecifier"] = { link = "@punctuation.special" }, + ["@lsp.type.interface"] = { fg = C.flamingo }, + ["@lsp.type.keyword"] = { link = "@keyword" }, + ["@lsp.type.namespace"] = { link = "@module" }, + ["@lsp.type.number"] = { link = "@number" }, + ["@lsp.type.operator"] = { link = "@operator" }, + ["@lsp.type.parameter"] = { link = "@parameter" }, + ["@lsp.type.property"] = { link = "@property" }, + ["@lsp.type.selfKeyword"] = { link = "@variable.builtin" }, + ["@lsp.type.typeAlias"] = { link = "@type.definition" }, + ["@lsp.type.unresolvedReference"] = { link = "@error" }, + ["@lsp.type.variable"] = {}, -- use treesitter styles for regular variables + ["@lsp.typemod.class.defaultLibrary"] = { link = "@type.builtin" }, + ["@lsp.typemod.enum.defaultLibrary"] = { link = "@type.builtin" }, + ["@lsp.typemod.enumMember.defaultLibrary"] = { link = "@constant.builtin" }, + ["@lsp.typemod.function.defaultLibrary"] = { link = "@function.builtin" }, + ["@lsp.typemod.keyword.async"] = { link = "@keyword.coroutine" }, + ["@lsp.typemod.macro.defaultLibrary"] = { link = "@function.builtin" }, + ["@lsp.typemod.method.defaultLibrary"] = { link = "@function.builtin" }, + ["@lsp.typemod.operator.injected"] = { link = "@operator" }, + ["@lsp.typemod.string.injected"] = { link = "@string" }, + ["@lsp.typemod.type.defaultLibrary"] = { link = "@type.builtin" }, + ["@lsp.typemod.variable.defaultLibrary"] = { link = "@variable.builtin" }, + ["@lsp.typemod.variable.injected"] = { link = "@variable" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/symbols_outline.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/symbols_outline.lua new file mode 100644 index 00000000..f16b6c28 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/symbols_outline.lua @@ -0,0 +1,13 @@ +local M = {} + +function M.get() + return { + -- https://github.com/simrat39/symbols-outline.nvim + FocusedSymbol = { fg = C.yellow, bg = C.none }, + + -- https://github.com/hedyhli/outline.nvim + OutlineCurrent = { link = "FocusedSymbol" }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/telekasten.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/telekasten.lua new file mode 100644 index 00000000..9af7a855 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/telekasten.lua @@ -0,0 +1,11 @@ +local M = {} + +function M.get() + return { + tkLink = { fg = C.blue }, + tkBrackets = { fg = C.pink }, + tkTag = { fg = C.sky }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/telescope.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/telescope.lua new file mode 100644 index 00000000..58401704 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/telescope.lua @@ -0,0 +1,60 @@ +local M = {} + +function M.get() + if O.integrations.telescope.style == "nvchad" then + return { + TelescopeBorder = { + fg = O.transparent_background and C.blue or C.mantle, + bg = O.transparent_background and C.none or C.mantle, + }, + TelescopeMatching = { fg = C.blue }, + TelescopeNormal = { + bg = O.transparent_background and C.none or C.mantle, + }, + TelescopePromptBorder = { + fg = O.transparent_background and C.blue or C.surface0, + bg = O.transparent_background and C.none or C.surface0, + }, + TelescopePromptNormal = { + fg = C.text, + bg = O.transparent_background and C.none or C.surface0, + }, + TelescopePromptPrefix = { + fg = C.flamingo, + bg = O.transparent_background and C.none or C.surface0, + }, + TelescopePreviewTitle = { + fg = O.transparent_background and C.green or C.base, + bg = O.transparent_background and C.none or C.green, + }, + TelescopePromptTitle = { + fg = O.transparent_background and C.red or C.base, + bg = O.transparent_background and C.none or C.red, + }, + TelescopeResultsTitle = { + fg = O.transparent_background and C.lavender or C.mantle, + bg = O.transparent_background and C.none or C.lavender, + }, + TelescopeSelection = { + fg = O.transparent_background and C.flamingo or C.text, + bg = O.transparent_background and C.none or C.surface0, + style = { "bold" }, + }, + TelescopeSelectionCaret = { fg = C.flamingo }, + } + end + + return { + -- TelescopeNormal = { link = "NormalFloat" }, -- Respect telescope's default float bg + TelescopeBorder = { link = "FloatBorder" }, + TelescopeSelectionCaret = { fg = C.flamingo }, + TelescopeSelection = { + fg = O.transparent_background and C.flamingo or C.text, + bg = O.transparent_background and C.none or C.surface0, + style = { "bold" }, + }, + TelescopeMatching = { fg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/treesitter.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/treesitter.lua new file mode 100644 index 00000000..cd4981ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/treesitter.lua @@ -0,0 +1,274 @@ +local M = {} + +function M.get() + if vim.treesitter.highlighter.hl_map then + vim.notify_once( + [[Catppuccin (info): +nvim-treesitter integration requires neovim 0.8 +If you want to stay on nvim 0.7, either disable the integration or pin catppuccin tag to v0.2.4 and nvim-treesitter commit to 4cccb6f494eb255b32a290d37c35ca12584c74d0. +]], + vim.log.levels.INFO + ) + return {} + end + + local colors = { -- Reference: https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md + -- Identifiers + ["@variable"] = { fg = C.text, style = O.styles.variables or {} }, -- Any variable name that does not have another highlight. + ["@variable.builtin"] = { fg = C.red, style = O.styles.properties or {} }, -- Variable names that are defined by the languages, like this or self. + ["@variable.parameter"] = { fg = C.maroon, style = O.styles.variables or {} }, -- For parameters of a function. + ["@variable.member"] = { fg = C.lavender }, -- For fields. + + ["@constant"] = { link = "Constant" }, -- For constants + ["@constant.builtin"] = { fg = C.peach, style = O.styles.keywords or {} }, -- For constant that are built in the language: nil in Lua. + ["@constant.macro"] = { link = "Macro" }, -- For constants that are defined by macros: NULL in C. + + ["@module"] = { fg = C.lavender, style = O.styles.miscs or { "italic" } }, -- For identifiers referring to modules and namespaces. + ["@label"] = { link = "Label" }, -- For labels: label: in C and :label: in Lua. + + -- Literals + ["@string"] = { link = "String" }, -- For strings. + ["@string.documentation"] = { fg = C.teal, style = O.styles.strings or {} }, -- For strings documenting code (e.g. Python docstrings). + ["@string.regexp"] = { fg = C.peach, style = O.styles.strings or {} }, -- For regexes. + ["@string.escape"] = { fg = C.pink, style = O.styles.strings or {} }, -- For escape characters within a string. + ["@string.special"] = { link = "Special" }, -- other special strings (e.g. dates) + ["@string.special.path"] = { link = "Special" }, -- filenames + ["@string.special.symbol"] = { fg = C.flamingo }, -- symbols or atoms + ["@string.special.url"] = { fg = C.rosewater, style = { "italic", "underline" } }, -- urls, links and emails + + ["@character"] = { link = "Character" }, -- character literals + ["@character.special"] = { link = "SpecialChar" }, -- special characters (e.g. wildcards) + + ["@boolean"] = { link = "Boolean" }, -- For booleans. + ["@number"] = { link = "Number" }, -- For all numbers + ["@number.float"] = { link = "Float" }, -- For floats. + + -- Types + ["@type"] = { link = "Type" }, -- For types. + ["@type.builtin"] = { fg = C.yellow, style = O.styles.properties or { "italic" } }, -- For builtin types. + ["@type.definition"] = { link = "Type" }, -- type definitions (e.g. `typedef` in C) + + ["@attribute"] = { link = "Constant" }, -- attribute annotations (e.g. Python decorators) + ["@property"] = { fg = C.lavender, style = O.styles.properties or {} }, -- Same as TSField. + + -- Functions + ["@function"] = { link = "Function" }, -- For function (calls and definitions). + ["@function.builtin"] = { fg = C.peach, style = O.styles.functions or {} }, -- For builtin functions: table.insert in Lua. + ["@function.call"] = { link = "Function" }, -- function calls + ["@function.macro"] = { fg = C.teal, style = O.styles.functions or {} }, -- For macro defined functions (calls and definitions): each macro_rules in Rust. + + ["@function.method"] = { link = "Function" }, -- For method definitions. + ["@function.method.call"] = { link = "Function" }, -- For method calls. + + ["@constructor"] = { fg = C.sapphire }, -- For constructor calls and definitions: = { } in Lua, and Java constructors. + ["@operator"] = { link = "Operator" }, -- For any operator: +, but also -> and * in C. + + -- Keywords + ["@keyword"] = { link = "Keyword" }, -- For keywords that don't fall in previous categories. + ["@keyword.modifier"] = { link = "Keyword" }, -- For keywords modifying other constructs (e.g. `const`, `static`, `public`) + ["@keyword.type"] = { link = "Keyword" }, -- For keywords describing composite types (e.g. `struct`, `enum`) + ["@keyword.coroutine"] = { link = "Keyword" }, -- For keywords related to coroutines (e.g. `go` in Go, `async/await` in Python) + ["@keyword.function"] = { fg = C.mauve, style = O.styles.keywords or {} }, -- For keywords used to define a function. + ["@keyword.operator"] = { link = "Operator" }, -- For new keyword operator + ["@keyword.import"] = { link = "Include" }, -- For includes: #include in C, use or extern crate in Rust, or require in Lua. + ["@keyword.repeat"] = { link = "Repeat" }, -- For keywords related to loops. + ["@keyword.return"] = { fg = C.mauve, style = O.styles.keywords or {} }, + ["@keyword.debug"] = { link = "Exception" }, -- For keywords related to debugging + ["@keyword.exception"] = { link = "Exception" }, -- For exception related keywords. + + ["@keyword.conditional"] = { link = "Conditional" }, -- For keywords related to conditionnals. + ["@keyword.conditional.ternary"] = { link = "Operator" }, -- For ternary operators (e.g. `?` / `:`) + + ["@keyword.directive"] = { link = "PreProc" }, -- various preprocessor directives & shebangs + ["@keyword.directive.define"] = { link = "Define" }, -- preprocessor definition directives + -- JS & derivative + ["@keyword.export"] = { fg = C.sky, style = O.styles.keywords }, + + -- Punctuation + ["@punctuation.delimiter"] = { link = "Delimiter" }, -- For delimiters (e.g. `;` / `.` / `,`). + ["@punctuation.bracket"] = { fg = C.overlay2 }, -- For brackets and parenthesis. + ["@punctuation.special"] = { link = "Special" }, -- For special punctuation that does not fall in the categories before (e.g. `{}` in string interpolation). + + -- Comment + ["@comment"] = { link = "Comment" }, + ["@comment.documentation"] = { link = "Comment" }, -- For comments documenting code + + ["@comment.error"] = { fg = C.base, bg = C.red }, + ["@comment.warning"] = { fg = C.base, bg = C.yellow }, + ["@comment.hint"] = { fg = C.base, bg = C.blue }, + ["@comment.todo"] = { fg = C.base, bg = C.flamingo }, + ["@comment.note"] = { fg = C.base, bg = C.rosewater }, + + -- Markup + ["@markup"] = { fg = C.text }, -- For strings considerated text in a markup language. + ["@markup.strong"] = { fg = C.maroon, style = { "bold" } }, -- bold + ["@markup.italic"] = { fg = C.maroon, style = { "italic" } }, -- italic + ["@markup.strikethrough"] = { fg = C.text, style = { "strikethrough" } }, -- strikethrough text + ["@markup.underline"] = { link = "Underlined" }, -- underlined text + + ["@markup.heading"] = { fg = C.blue, style = { "bold" } }, -- titles like: # Example + + ["@markup.math"] = { fg = C.blue }, -- math environments (e.g. `$ ... $` in LaTeX) + ["@markup.quote"] = { fg = C.maroon, style = { "bold" } }, -- block quotes + ["@markup.environment"] = { fg = C.pink }, -- text environments of markup languages + ["@markup.environment.name"] = { fg = C.blue }, -- text indicating the type of an environment + + ["@markup.link"] = { link = "Tag" }, -- text references, footnotes, citations, etc. + ["@markup.link.label"] = { link = "Label" }, -- link, reference descriptions + ["@markup.link.url"] = { fg = C.rosewater, style = { "italic", "underline" } }, -- urls, links and emails + + ["@markup.raw"] = { fg = C.teal }, -- used for inline code in markdown and for doc in python (""") + + ["@markup.list"] = { link = "Special" }, + ["@markup.list.checked"] = { fg = C.green }, -- todo notes + ["@markup.list.unchecked"] = { fg = C.overlay1 }, -- todo notes + + -- Diff + ["@diff.plus"] = { link = "diffAdded" }, -- added text (for diff files) + ["@diff.minus"] = { link = "diffRemoved" }, -- deleted text (for diff files) + ["@diff.delta"] = { link = "diffChanged" }, -- deleted text (for diff files) + + -- Tags + ["@tag"] = { fg = C.mauve }, -- Tags like html tag names. + ["@tag.attribute"] = { fg = C.teal, style = O.styles.miscs or { "italic" } }, -- Tags like html tag names. + ["@tag.delimiter"] = { fg = C.sky }, -- Tag delimiter like < > / + + -- Misc + ["@error"] = { link = "Error" }, + + -- Language specific: + -- bash + ["@function.builtin.bash"] = { fg = C.red, style = O.styles.miscs or { "italic" } }, + + -- markdown + ["@markup.heading.1.markdown"] = { link = "rainbow1" }, + ["@markup.heading.2.markdown"] = { link = "rainbow2" }, + ["@markup.heading.3.markdown"] = { link = "rainbow3" }, + ["@markup.heading.4.markdown"] = { link = "rainbow4" }, + ["@markup.heading.5.markdown"] = { link = "rainbow5" }, + ["@markup.heading.6.markdown"] = { link = "rainbow6" }, + + -- java + ["@constant.java"] = { fg = C.teal }, + + -- css + ["@property.css"] = { fg = C.lavender }, + ["@property.id.css"] = { fg = C.blue }, + ["@property.class.css"] = { fg = C.yellow }, + ["@type.css"] = { fg = C.lavender }, + ["@type.tag.css"] = { fg = C.mauve }, + ["@string.plain.css"] = { fg = C.peach }, + ["@number.css"] = { fg = C.peach }, + + -- toml + ["@property.toml"] = { fg = C.blue }, -- Differentiates between string and properties + + -- json + ["@label.json"] = { fg = C.blue }, -- For labels: label: in C and :label: in Lua. + + -- lua + ["@constructor.lua"] = { fg = C.flamingo }, -- For constructor calls and definitions: = { } in Lua. + + -- typescript + ["@property.typescript"] = { fg = C.lavender, style = O.styles.properties or {} }, + ["@constructor.typescript"] = { fg = C.lavender }, + + -- TSX (Typescript React) + ["@constructor.tsx"] = { fg = C.lavender }, + ["@tag.attribute.tsx"] = { fg = C.teal, style = O.styles.miscs or { "italic" } }, + + -- yaml + ["@variable.member.yaml"] = { fg = C.blue }, -- For fields. + + -- Ruby + ["@string.special.symbol.ruby"] = { fg = C.flamingo }, + + -- PHP + ["@function.method.php"] = { link = "Function" }, + ["@function.method.call.php"] = { link = "Function" }, + + -- C/CPP + ["@type.builtin.c"] = { fg = C.yellow, style = {} }, + ["@property.cpp"] = { fg = C.text }, + ["@type.builtin.cpp"] = { fg = C.yellow, style = {} }, + + -- gitcommit + ["@comment.warning.gitcommit"] = { fg = C.yellow }, + + -- Misc + gitcommitSummary = { fg = C.rosewater, style = O.styles.miscs or { "italic" } }, + zshKSHFunction = { link = "Function" }, + } + + -- Legacy highlights + colors["@parameter"] = colors["@variable.parameter"] + colors["@field"] = colors["@variable.member"] + colors["@namespace"] = colors["@module"] + colors["@float"] = colors["@number.float"] + colors["@symbol"] = colors["@string.special.symbol"] + colors["@string.regex"] = colors["@string.regexp"] + + colors["@text"] = colors["@markup"] + colors["@text.strong"] = colors["@markup.strong"] + colors["@text.emphasis"] = colors["@markup.italic"] + colors["@text.underline"] = colors["@markup.underline"] + colors["@text.strike"] = colors["@markup.strikethrough"] + colors["@text.uri"] = colors["@markup.link.url"] + colors["@text.math"] = colors["@markup.math"] + colors["@text.environment"] = colors["@markup.environment"] + colors["@text.environment.name"] = colors["@markup.environment.name"] + + colors["@text.title"] = colors["@markup.heading"] + colors["@text.literal"] = colors["@markup.raw"] + colors["@text.reference"] = colors["@markup.link"] + + colors["@text.todo.checked"] = colors["@markup.list.checked"] + colors["@text.todo.unchecked"] = colors["@markup.list.unchecked"] + + colors["@comment.note"] = colors["@comment.hint"] + + -- @text.todo is now for todo comments, not todo notes like in markdown + colors["@text.todo"] = colors["@comment.todo"] + colors["@text.warning"] = colors["@comment.warning"] + colors["@text.note"] = colors["@comment.note"] + colors["@text.danger"] = colors["@comment.error"] + + -- @text.uri is now + -- > @markup.link.url in markup links + -- > @string.special.url outside of markup + colors["@text.uri"] = colors["@markup.link.uri"] + + colors["@method"] = colors["@function.method"] + colors["@method.call"] = colors["@function.method.call"] + + colors["@text.diff.add"] = colors["@diff.plus"] + colors["@text.diff.delete"] = colors["@diff.minus"] + + colors["@type.qualifier"] = colors["@keyword.modifier"] + colors["@keyword.storage"] = colors["@keyword.modifier"] + colors["@define"] = colors["@keyword.directive.define"] + colors["@preproc"] = colors["@keyword.directive"] + colors["@storageclass"] = colors["@keyword.storage"] + colors["@conditional"] = colors["@keyword.conditional"] + colors["@exception"] = colors["@keyword.exception"] + colors["@include"] = colors["@keyword.import"] + colors["@repeat"] = colors["@keyword.repeat"] + + colors["@symbol.ruby"] = colors["@string.special.symbol.ruby"] + + colors["@variable.member.yaml"] = colors["@field.yaml"] + + colors["@text.title.1.markdown"] = colors["@markup.heading.1.markdown"] + colors["@text.title.2.markdown"] = colors["@markup.heading.2.markdown"] + colors["@text.title.3.markdown"] = colors["@markup.heading.3.markdown"] + colors["@text.title.4.markdown"] = colors["@markup.heading.4.markdown"] + colors["@text.title.5.markdown"] = colors["@markup.heading.5.markdown"] + colors["@text.title.6.markdown"] = colors["@markup.heading.6.markdown"] + + colors["@method.php"] = colors["@function.method.php"] + colors["@method.call.php"] = colors["@function.method.call.php"] + + return colors +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/treesitter_context.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/treesitter_context.lua new file mode 100644 index 00000000..a21518ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/treesitter_context.lua @@ -0,0 +1,18 @@ +local M = {} + +function M.get() + return O.transparent_background and { + TreesitterContextBottom = { sp = C.dim, style = { "underline" } }, + } or { + TreesitterContextBottom = { + sp = C.surface0, + style = { "underline" }, + }, + TreesitterContextLineNumber = { + fg = C.surface1, + bg = C.mantle, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/ts_rainbow.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/ts_rainbow.lua new file mode 100644 index 00000000..c2827dd2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/ts_rainbow.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.get() + return { + rainbowcol1 = { fg = C.red }, + rainbowcol2 = { fg = C.teal }, + rainbowcol3 = { fg = C.yellow }, + rainbowcol4 = { fg = C.blue }, + rainbowcol5 = { fg = C.pink }, + rainbowcol6 = { fg = C.flamingo }, + rainbowcol7 = { fg = C.green }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/ts_rainbow2.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/ts_rainbow2.lua new file mode 100644 index 00000000..651ea848 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/ts_rainbow2.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.get() + return { + TSRainbowRed = { fg = C.red }, + TSRainbowYellow = { fg = C.yellow }, + TSRainbowBlue = { fg = C.blue }, + TSRainbowOrange = { fg = C.peach }, + TSRainbowGreen = { fg = C.green }, + TSRainbowViolet = { fg = C.mauve }, + TSRainbowCyan = { fg = C.teal }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/ufo.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/ufo.lua new file mode 100644 index 00000000..9e823a6d --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/ufo.lua @@ -0,0 +1,10 @@ +local M = {} + +M.get = function() + return { + UfoFoldedFg = { fg = C.lavender }, + UfoFoldedEllipsis = { fg = C.crust, bg = C.blue }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/vim_sneak.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/vim_sneak.lua new file mode 100644 index 00000000..fa09bca2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/vim_sneak.lua @@ -0,0 +1,10 @@ +local M = {} + +function M.get() + return { + Sneak = { fg = C.overlay2, bg = C.pink }, + SneakScope = { bg = C.text }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/vimwiki.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/vimwiki.lua new file mode 100644 index 00000000..b7009716 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/vimwiki.lua @@ -0,0 +1,20 @@ +local M = {} + +function M.get() + return { + VimwikiLink = { fg = C.sky, bg = C.none }, + VimwikiHeaderChar = { fg = C.grey, bg = C.none }, + VimwikiHR = { fg = C.yellow, bg = C.none }, + VimwikiList = { fg = C.peach, bg = C.none }, + VimwikiTag = { fg = C.peach, bg = C.none }, + VimwikiMarkers = { fg = C.subtext0, bg = C.none }, + VimwikiHeader1 = { fg = C.peach, bg = C.none, style = { "bold" } }, + VimwikiHeader2 = { fg = C.green, bg = C.none, style = { "bold" } }, + VimwikiHeader3 = { fg = C.blue, bg = C.none, style = { "bold" } }, + VimwikiHeader4 = { fg = C.sky, bg = C.none, style = { "bold" } }, + VimwikiHeader5 = { fg = C.yellow, bg = C.none, style = { "bold" } }, + VimwikiHeader6 = { fg = C.mauve, bg = C.none, style = { "bold" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/which_key.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/which_key.lua new file mode 100644 index 00000000..3983d449 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/which_key.lua @@ -0,0 +1,15 @@ +local M = {} + +function M.get() + return { + WhichKey = { link = "NormalFloat" }, + WhichKeyBorder = { link = "FloatBorder" }, + + WhichKeyGroup = { fg = C.blue }, + WhichKeySeparator = { fg = C.overlay0 }, + WhichKeyDesc = { fg = C.pink }, + WhichKeyValue = { fg = C.overlay0 }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/window_picker.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/window_picker.lua new file mode 100644 index 00000000..06af3e56 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/integrations/window_picker.lua @@ -0,0 +1,12 @@ +local M = {} + +function M.get() + return { + WindowPickerStatusLine = { fg = C.red, style = { "bold" } }, + WindowPickerStatusLineNC = { fg = C.red, style = { "bold" } }, + WindowPickerWinBar = { fg = C.red, style = { "bold" } }, + WindowPickerWinBarNC = { fg = C.red, style = { "bold" } }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/syntax.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/syntax.lua new file mode 100644 index 00000000..055ddfe1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/syntax.lua @@ -0,0 +1,102 @@ +local M = {} + +function M.get() + return { + Comment = { fg = C.overlay0, style = O.styles.comments }, -- just comments + SpecialComment = { link = "Special" }, -- special things inside a comment + Constant = { fg = C.peach }, -- (preferred) any constant + String = { fg = C.green, style = O.styles.strings or {} }, -- a string constant: "this is a string" + Character = { fg = C.teal }, -- a character constant: 'c', '\n' + Number = { fg = C.peach, style = O.styles.numbers or {} }, -- a number constant: 234, 0xff + Float = { link = "Number" }, -- a floating point constant: 2.3e10 + Boolean = { fg = C.peach, style = O.styles.booleans or {} }, -- a boolean constant: TRUE, false + Identifier = { fg = C.flamingo, style = O.styles.variables or {} }, -- (preferred) any variable name + Function = { fg = C.blue, style = O.styles.functions or {} }, -- function name (also: methods for classes) + Statement = { fg = C.mauve }, -- (preferred) any statement + Conditional = { fg = C.mauve, style = O.styles.conditionals or {} }, -- if, then, else, endif, switch, etc. + Repeat = { fg = C.mauve, style = O.styles.loops or {} }, -- for, do, while, etc. + Label = { fg = C.sapphire }, -- case, default, etc. + Operator = { fg = C.sky, style = O.styles.operators or {} }, -- "sizeof", "+", "*", etc. + Keyword = { fg = C.mauve, style = O.styles.keywords or {} }, -- any other keyword + Exception = { fg = C.mauve, style = O.styles.keywords or {} }, -- try, catch, throw + + PreProc = { fg = C.pink }, -- (preferred) generic Preprocessor + Include = { fg = C.mauve, style = O.styles.keywords or {} }, -- preprocessor #include + Define = { link = "PreProc" }, -- preprocessor #define + Macro = { fg = C.mauve }, -- same as Define + PreCondit = { link = "PreProc" }, -- preprocessor #if, #else, #endif, etc. + + StorageClass = { fg = C.yellow }, -- static, register, volatile, etc. + Structure = { fg = C.yellow }, -- struct, union, enum, etc. + Special = { fg = C.pink }, -- (preferred) any special symbol + Type = { fg = C.yellow, style = O.styles.types or {} }, -- (preferred) int, long, char, etc. + Typedef = { link = "Type" }, -- A typedef + SpecialChar = { link = "Special" }, -- special character in a constant + Tag = { fg = C.lavender, style = { "bold" } }, -- you can use CTRL-] on this + Delimiter = { fg = C.overlay2 }, -- character that needs attention + Debug = { link = "Special" }, -- debugging statements + + Underlined = { style = { "underline" } }, -- (preferred) text that stands out, HTML links + Bold = { style = { "bold" } }, + Italic = { style = { "italic" } }, + -- ("Ignore", below, may be invisible...) + -- Ignore = { }, -- (preferred) left blank, hidden |hl-Ignore| + + Error = { fg = C.red }, -- (preferred) any erroneous construct + Todo = { bg = C.flamingo, fg = C.base, style = { "bold" } }, -- (preferred) anything that needs extra attention; mostly the keywords TODO FIXME and XXX + qfLineNr = { fg = C.yellow }, + qfFileName = { fg = C.blue }, + htmlH1 = { fg = C.pink, style = { "bold" } }, + htmlH2 = { fg = C.blue, style = { "bold" } }, + -- mkdHeading = { fg = C.peach, style = { "bold" } }, + -- mkdCode = { bg = C.terminal_black, fg = C.text }, + mkdCodeDelimiter = { bg = C.base, fg = C.text }, + mkdCodeStart = { fg = C.flamingo, style = { "bold" } }, + mkdCodeEnd = { fg = C.flamingo, style = { "bold" } }, + -- mkdLink = { fg = C.blue, style = { "underline" } }, + + -- debugging + debugPC = { bg = O.transparent_background and C.none or C.crust }, -- used for highlighting the current line in terminal-debug + debugBreakpoint = { bg = C.base, fg = C.overlay0 }, -- used for breakpoint colors in terminal-debug + -- illuminate + illuminatedWord = { bg = C.surface1 }, + illuminatedCurWord = { bg = C.surface1 }, + -- diff + diffAdded = { fg = C.green }, + diffRemoved = { fg = C.red }, + diffChanged = { fg = C.blue }, + diffOldFile = { fg = C.yellow }, + diffNewFile = { fg = C.peach }, + diffFile = { fg = C.blue }, + diffLine = { fg = C.overlay0 }, + diffIndexLine = { fg = C.teal }, + DiffAdd = { bg = U.darken(C.green, 0.18, C.base) }, -- diff mode: Added line |diff.txt| + DiffChange = { bg = U.darken(C.blue, 0.07, C.base) }, -- diff mode: Changed line |diff.txt| + DiffDelete = { bg = U.darken(C.red, 0.18, C.base) }, -- diff mode: Deleted line |diff.txt| + DiffText = { bg = U.darken(C.blue, 0.30, C.base) }, -- diff mode: Changed text within a changed line |diff.txt| + -- NeoVim + healthError = { fg = C.red }, + healthSuccess = { fg = C.teal }, + healthWarning = { fg = C.yellow }, + -- misc + + -- glyphs + GlyphPalette1 = { fg = C.red }, + GlyphPalette2 = { fg = C.teal }, + GlyphPalette3 = { fg = C.yellow }, + GlyphPalette4 = { fg = C.blue }, + GlyphPalette6 = { fg = C.teal }, + GlyphPalette7 = { fg = C.text }, + GlyphPalette9 = { fg = C.red }, + + -- rainbow + rainbow1 = { fg = C.red }, + rainbow2 = { fg = C.peach }, + rainbow3 = { fg = C.yellow }, + rainbow4 = { fg = C.green }, + rainbow5 = { fg = C.sapphire }, + rainbow6 = { fg = C.lavender }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/terminal.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/terminal.lua new file mode 100644 index 00000000..26f4fd30 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/groups/terminal.lua @@ -0,0 +1,31 @@ +local M = {} + +function M.get() + return { + terminal_color_0 = C.overlay0, + terminal_color_8 = C.overlay1, + + terminal_color_1 = C.red, + terminal_color_9 = C.red, + + terminal_color_2 = C.green, + terminal_color_10 = C.green, + + terminal_color_3 = C.yellow, + terminal_color_11 = C.yellow, + + terminal_color_4 = C.blue, + terminal_color_12 = C.blue, + + terminal_color_5 = C.pink, + terminal_color_13 = C.pink, + + terminal_color_6 = C.sky, + terminal_color_14 = C.sky, + + terminal_color_7 = C.text, + terminal_color_15 = C.text, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/init.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/init.lua new file mode 100644 index 00000000..ad205f95 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/init.lua @@ -0,0 +1,231 @@ +local is_vim = vim.fn.has "nvim" ~= 1 +if is_vim then require "catppuccin.lib.vim" end + +---@type Catppuccin +local M = { + default_options = { + flavour = "auto", + background = { + light = "latte", + dark = "mocha", + }, + compile_path = vim.fn.stdpath "cache" .. "/catppuccin", + transparent_background = false, + show_end_of_buffer = false, + term_colors = false, + kitty = vim.env.KITTY_WINDOW_ID and true or false, + dim_inactive = { + enabled = false, + shade = "dark", + percentage = 0.15, + }, + no_italic = false, + no_bold = false, + no_underline = false, + styles = { + comments = { "italic" }, + conditionals = { "italic" }, + loops = {}, + functions = {}, + keywords = {}, + strings = {}, + variables = {}, + numbers = {}, + booleans = {}, + properties = {}, + types = {}, + operators = {}, + }, + default_integrations = true, + integrations = { + alpha = true, + cmp = true, + dap = true, + dap_ui = true, + dashboard = true, + diffview = false, + flash = true, + gitsigns = true, + markdown = true, + neogit = true, + neotree = true, + nvimtree = true, + ufo = true, + rainbow_delimiters = true, + semantic_tokens = not is_vim, + telescope = { enabled = true }, + treesitter = not is_vim, + treesitter_context = true, + barbecue = { + dim_dirname = true, + bold_basename = true, + dim_context = false, + alt_background = false, + }, + illuminate = { + enabled = true, + lsp = false, + }, + indent_blankline = { + enabled = true, + scope_color = "", + colored_indent_levels = false, + }, + native_lsp = { + enabled = true, + virtual_text = { + errors = { "italic" }, + hints = { "italic" }, + warnings = { "italic" }, + information = { "italic" }, + }, + underlines = { + errors = { "underline" }, + hints = { "underline" }, + warnings = { "underline" }, + information = { "underline" }, + }, + inlay_hints = { + background = true, + }, + }, + navic = { + enabled = false, + custom_bg = "NONE", + }, + dropbar = { + enabled = true, + color_mode = false, + }, + colorful_winsep = { + enabled = false, + color = "red", + }, + }, + color_overrides = {}, + highlight_overrides = {}, + }, + flavours = { latte = 1, frappe = 2, macchiato = 3, mocha = 4 }, + path_sep = jit and (jit.os == "Windows" and "\\" or "/") or package.config:sub(1, 1), +} + +M.options = M.default_options + +function M.compile() + local user_flavour = M.flavour + for flavour, _ in pairs(M.flavours) do + M.flavour = flavour + require("catppuccin.lib." .. (is_vim and "vim." or "") .. "compiler").compile(flavour) + end + M.flavour = user_flavour -- Restore user flavour after compile +end + +local function get_flavour(default) + local flavour + if default and default == M.flavour and vim.o.background ~= (M.flavour == "latte" and "light" or "dark") then + flavour = M.options.background[vim.o.background] + else + flavour = default + end + + if flavour and not M.flavours[flavour] then + vim.notify( + string.format( + "Catppuccin (error): Invalid flavour '%s', flavour must be 'latte', 'frappe', 'macchiato', 'mocha' or 'auto'", + flavour + ), + vim.log.levels.ERROR + ) + flavour = nil + end + return flavour or M.options.flavour or vim.g.catppuccin_flavour or M.options.background[vim.o.background] +end + +local did_setup = false + +function M.load(flavour) + if M.options.flavour == "auto" then -- set colorscheme based on o:background + M.options.flavour = nil -- ensure that this will only run once on startup + end + if not did_setup then M.setup() end + M.flavour = get_flavour(flavour) + local compiled_path = M.options.compile_path .. M.path_sep .. M.flavour + local f = loadfile(compiled_path) + if not f then + M.compile() + f = assert(loadfile(compiled_path), "could not load cache") + end + f(flavour or M.options.flavour or vim.g.catppuccin_flavour) +end + +---@type fun(user_conf: CatppuccinOptions?) +function M.setup(user_conf) + did_setup = true + -- Parsing user config + user_conf = user_conf or {} + + if user_conf.default_integrations == false then M.default_options.integrations = {} end + + M.options = vim.tbl_deep_extend("keep", user_conf, M.default_options) + M.options.highlight_overrides.all = user_conf.custom_highlights or M.options.highlight_overrides.all + + -- Get cached hash + local cached_path = M.options.compile_path .. M.path_sep .. "cached" + local file = io.open(cached_path) + local cached = nil + if file then + cached = file:read() + file:close() + end + + -- Get current hash + local git_path = debug.getinfo(1).source:sub(2, -24) .. ".git" + local git = vim.fn.getftime(git_path) -- 2x faster vim.loop.fs_stat + local hash = require("catppuccin.lib.hashing").hash(user_conf) + .. (git == -1 and git_path or git) -- no .git in /nix/store -> cache path + .. (vim.o.winblend == 0 and 1 or 0) -- :h winblend + .. (vim.o.pumblend == 0 and 1 or 0) -- :h pumblend + + -- Recompile if hash changed + if cached ~= hash then + M.compile() + file = io.open(cached_path, "wb") + if file then + file:write(hash) + file:close() + end + end +end + +if is_vim then return M end + +vim.api.nvim_create_user_command( + "Catppuccin", + function(inp) vim.api.nvim_command("colorscheme catppuccin-" .. get_flavour(inp.args)) end, + { + nargs = 1, + complete = function(line) + return vim.tbl_filter(function(val) return vim.startswith(val, line) end, vim.tbl_keys(M.flavours)) + end, + } +) + +vim.api.nvim_create_user_command("CatppuccinCompile", function() + for name, _ in pairs(package.loaded) do + if name:match "^catppuccin." then package.loaded[name] = nil end + end + M.compile() + vim.notify("Catppuccin (info): compiled cache!", vim.log.levels.INFO) + vim.cmd.colorscheme "catppuccin" +end, {}) + +if vim.g.catppuccin_debug then + vim.api.nvim_create_autocmd("BufWritePost", { + pattern = "*/catppuccin/*", + callback = function() + vim.schedule(function() vim.cmd "CatppuccinCompile" end) + end, + }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/compiler.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/compiler.lua new file mode 100644 index 00000000..5c844567 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/compiler.lua @@ -0,0 +1,104 @@ +local path_sep = require("catppuccin").path_sep +local O = require("catppuccin").options +local M = {} + +-- Credit: https://github.com/EdenEast/nightfox.nvim +local fmt = string.format + +local function inspect(t) + local list = {} + for k, v in pairs(t) do + local tv = type(v) + if tv == "string" then + table.insert(list, fmt([[%s = "%s"]], k, v)) + elseif tv == "table" then + table.insert(list, fmt([[%s = %s]], k, inspect(v))) + else + table.insert(list, fmt([[%s = %s]], k, tostring(v))) + end + end + return fmt([[{ %s }]], table.concat(list, ", ")) +end + +function M.compile(flavour) + local theme = require("catppuccin.lib.mapper").apply(flavour) + local lines = { + string.format( + [[ +return string.dump(function(flavour) +vim.o.termguicolors = true +if vim.g.colors_name then vim.cmd("hi clear") end +if flavour then vim.o.background = "%s" end +vim.g.colors_name = "catppuccin-%s" +local h = vim.api.nvim_set_hl]], + flavour == "latte" and "light" or "dark", + flavour + ), + } + if path_sep == "\\" then O.compile_path = O.compile_path:gsub("/", "\\") end + + local tbl = vim.tbl_deep_extend("keep", theme.custom_highlights, theme.integrations, theme.syntax, theme.editor) + + if O.term_colors == true then + for k, v in pairs(theme.terminal) do + table.insert(lines, fmt('vim.g.%s = "%s"', k, v)) + end + end + + for group, color in pairs(tbl) do + if color.style then + for _, style in pairs(color.style) do + color[style] = true + if O.no_italic and style == "italic" then color[style] = false end + if O.no_bold and style == "bold" then color[style] = false end + if O.no_underline and style == "underline" then color[style] = false end + end + end + color.style = nil + if color.link and (theme.custom_highlights[group] and not theme.custom_highlights[group].link) then + color.link = nil + end + table.insert(lines, fmt([[h(0, "%s", %s)]], group, inspect(color))) + end + table.insert(lines, "end, true)") + if vim.fn.isdirectory(O.compile_path) == 0 then vim.fn.mkdir(O.compile_path, "p") end + + if vim.g.catppuccin_debug then -- Debugging purpose + local deb = io.open(O.compile_path .. path_sep .. flavour .. ".lua", "wb") + if deb then + deb:write(table.concat(lines, "\n")) + deb:close() + end + end + + local f = loadstring(table.concat(lines, "\n")) + if not f then + local err_path = (path_sep == "/" and "/tmp" or os.getenv "TMP") .. "/catppuccin_error.lua" + print(string.format( + [[Catppuccin (error): Most likely some mistake made in your catppuccin config +You can open %s for debugging + +If you think this is a bug, kindly open an issue and attach %s file +Below is the error message that we captured: +]], + err_path, + err_path + )) + local err = io.open(err_path, "wb") + if err then + err:write(table.concat(lines, "\n")) + err:close() + end + dofile(err_path) + return + end + + local file = assert( + io.open(O.compile_path .. path_sep .. flavour, "wb"), + "Permission denied while writing compiled file to " .. O.compile_path .. path_sep .. flavour + ) + file:write(f()) + file:close() +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/hashing.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/hashing.lua new file mode 100644 index 00000000..d2272f15 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/hashing.lua @@ -0,0 +1,26 @@ +local M = {} +local B = bit or bit32 or require "catppuccin.lib.vim.bit" + +local hash_str = function(str) -- djb2, https://theartincode.stanis.me/008-djb2/ + local hash = 5381 + for i = 1, #str do + hash = B.lshift(hash, 5) + hash + string.byte(str, i) + end + return hash +end + +function M.hash(v) -- Xor hashing: https://codeforces.com/blog/entry/85900 + local t = type(v) + if t == "table" then + local hash = 0 + for p, u in next, v do + hash = B.bxor(hash, hash_str(p .. M.hash(u))) + end + return hash + elseif t == "function" then + return M.hash(v(require("catppuccin.palettes").get_palette())) + end + return tostring(v) +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/highlighter.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/highlighter.lua new file mode 100644 index 00000000..d6887437 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/highlighter.lua @@ -0,0 +1,20 @@ +-- DEPRECATED MODULE +local M = {} + +function M.highlight(group, color) + if color.style then + for _, style in ipairs(color.style) do + color[style] = true + end + end + color.style = nil + vim.api.nvim_set_hl(0, group, color) +end + +function M.syntax(tbl) + for group, colors in pairs(tbl) do + M.highlight(group, colors) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/hsluv.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/hsluv.lua new file mode 100644 index 00000000..36354164 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/hsluv.lua @@ -0,0 +1,290 @@ +--[[ +Lua implementation of HSLuv and HPLuv color spaces +Homepage: http://www.hsluv.org/ + +Copyright (C) 2019 Alexei Boronine + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] +local hsluv = {} + +hsluv.hexChars = "0123456789abcdef" + +local distance_line_from_origin = function(line) return math.abs(line.intercept) / math.sqrt((line.slope ^ 2) + 1) end + +local length_of_ray_until_intersect = function(theta, line) + return line.intercept / (math.sin(theta) - line.slope * math.cos(theta)) +end + +hsluv.get_bounds = function(l) + local result = {} + local sub2 + local sub1 = ((l + 16) ^ 3) / 1560896 + if sub1 > hsluv.epsilon then + sub2 = sub1 + else + sub2 = l / hsluv.kappa + end + + for i = 1, 3 do + local m1 = hsluv.m[i][1] + local m2 = hsluv.m[i][2] + local m3 = hsluv.m[i][3] + + for t = 0, 1 do + local top1 = (284517 * m1 - 94839 * m3) * sub2 + local top2 = (838422 * m3 + 769860 * m2 + 731718 * m1) * l * sub2 - 769860 * t * l + local bottom = (632260 * m3 - 126452 * m2) * sub2 + 126452 * t + table.insert(result, { slope = top1 / bottom, intercept = top2 / bottom }) + end + end + return result +end + +hsluv.max_safe_chroma_for_l = function(l) + local bounds = hsluv.get_bounds(l) + local min = 1.7976931348623157e+308 + + for i = 1, 6 do + local length = distance_line_from_origin(bounds[i]) + if length >= 0 then min = math.min(min, length) end + end + return min +end + +hsluv.max_safe_chroma_for_lh = function(l, h) + local hrad = h / 360 * math.pi * 2 + local bounds = hsluv.get_bounds(l) + local min = 1.7976931348623157e+308 + + for i = 1, 6 do + local bound = bounds[i] + local length = length_of_ray_until_intersect(hrad, bound) + if length >= 0 then min = math.min(min, length) end + end + return min +end + +hsluv.dot_product = function(a, b) + local sum = 0 + for i = 1, 3 do + sum = sum + a[i] * b[i] + end + return sum +end + +hsluv.from_linear = function(c) + if c <= 0.0031308 then + return 12.92 * c + else + return 1.055 * (c ^ 0.416666666666666685) - 0.055 + end +end + +hsluv.to_linear = function(c) + if c > 0.04045 then + return ((c + 0.055) / 1.055) ^ 2.4 + else + return c / 12.92 + end +end + +hsluv.xyz_to_rgb = function(tuple) + return { + hsluv.from_linear(hsluv.dot_product(hsluv.m[1], tuple)), + hsluv.from_linear(hsluv.dot_product(hsluv.m[2], tuple)), + hsluv.from_linear(hsluv.dot_product(hsluv.m[3], tuple)), + } +end + +hsluv.rgb_to_xyz = function(tuple) + local rgbl = { hsluv.to_linear(tuple[1]), hsluv.to_linear(tuple[2]), hsluv.to_linear(tuple[3]) } + return { + hsluv.dot_product(hsluv.minv[1], rgbl), + hsluv.dot_product(hsluv.minv[2], rgbl), + hsluv.dot_product(hsluv.minv[3], rgbl), + } +end + +hsluv.y_to_l = function(Y) + if Y <= hsluv.epsilon then + return Y / hsluv.refY * hsluv.kappa + else + return 116 * ((Y / hsluv.refY) ^ 0.333333333333333315) - 16 + end +end + +hsluv.l_to_y = function(L) + if L <= 8 then + return hsluv.refY * L / hsluv.kappa + else + return hsluv.refY * (((L + 16) / 116) ^ 3) + end +end + +hsluv.xyz_to_luv = function(tuple) + local X = tuple[1] + local Y = tuple[2] + local divider = X + 15 * Y + 3 * tuple[3] + local varU = 4 * X + local varV = 9 * Y + if divider ~= 0 then + varU = varU / divider + varV = varV / divider + else + varU = 0 + varV = 0 + end + local L = hsluv.y_to_l(Y) + if L == 0 then return { 0, 0, 0 } end + return { L, 13 * L * (varU - hsluv.refU), 13 * L * (varV - hsluv.refV) } +end + +hsluv.luv_to_xyz = function(tuple) + local L = tuple[1] + local U = tuple[2] + local V = tuple[3] + if L == 0 then return { 0, 0, 0 } end + local varU = U / (13 * L) + hsluv.refU + local varV = V / (13 * L) + hsluv.refV + local Y = hsluv.l_to_y(L) + local X = 0 - (9 * Y * varU) / (((varU - 4) * varV) - varU * varV) + return { X, Y, (9 * Y - 15 * varV * Y - varV * X) / (3 * varV) } +end + +hsluv.luv_to_lch = function(tuple) + local L = tuple[1] + local U = tuple[2] + local V = tuple[3] + local C = math.sqrt(U * U + V * V) + local H + if C < 0.00000001 then + H = 0 + else + H = math.atan2(V, U) * 180.0 / 3.1415926535897932 + if H < 0 then H = 360 + H end + end + return { L, C, H } +end + +hsluv.lch_to_luv = function(tuple) + local L = tuple[1] + local C = tuple[2] + local Hrad = tuple[3] / 360.0 * 2 * math.pi + return { L, math.cos(Hrad) * C, math.sin(Hrad) * C } +end + +hsluv.hsluv_to_lch = function(tuple) + local H = tuple[1] + local S = tuple[2] + local L = tuple[3] + if L > 99.9999999 then return { 100, 0, H } end + if L < 0.00000001 then return { 0, 0, H } end + return { L, hsluv.max_safe_chroma_for_lh(L, H) / 100 * S, H } +end + +hsluv.lch_to_hsluv = function(tuple) + local L = tuple[1] + local C = tuple[2] + local H = tuple[3] + local max_chroma = hsluv.max_safe_chroma_for_lh(L, H) + if L > 99.9999999 then return { H, 0, 100 } end + if L < 0.00000001 then return { H, 0, 0 } end + + return { H, C / max_chroma * 100, L } +end + +hsluv.hpluv_to_lch = function(tuple) + local H = tuple[1] + local S = tuple[2] + local L = tuple[3] + if L > 99.9999999 then return { 100, 0, H } end + if L < 0.00000001 then return { 0, 0, H } end + return { L, hsluv.max_safe_chroma_for_l(L) / 100 * S, H } +end + +hsluv.lch_to_hpluv = function(tuple) + local L = tuple[1] + local C = tuple[2] + local H = tuple[3] + if L > 99.9999999 then return { H, 0, 100 } end + if L < 0.00000001 then return { H, 0, 0 } end + return { H, C / hsluv.max_safe_chroma_for_l(L) * 100, L } +end + +hsluv.rgb_to_hex = function(tuple) + local h = "#" + for i = 1, 3 do + local c = math.floor(tuple[i] * 255 + 0.5) + local digit2 = math.fmod(c, 16) + local x = (c - digit2) / 16 + local digit1 = math.floor(x) + h = h .. string.sub(hsluv.hexChars, digit1 + 1, digit1 + 1) + h = h .. string.sub(hsluv.hexChars, digit2 + 1, digit2 + 1) + end + return h +end + +hsluv.hex_to_rgb = function(hex) + hex = string.lower(hex) + local ret = {} + for i = 0, 2 do + local char1 = string.sub(hex, i * 2 + 2, i * 2 + 2) + local char2 = string.sub(hex, i * 2 + 3, i * 2 + 3) + local digit1 = string.find(hsluv.hexChars, char1) - 1 + local digit2 = string.find(hsluv.hexChars, char2) - 1 + ret[i + 1] = (digit1 * 16 + digit2) / 255.0 + end + return ret +end + +hsluv.lch_to_rgb = function(tuple) return hsluv.xyz_to_rgb(hsluv.luv_to_xyz(hsluv.lch_to_luv(tuple))) end + +hsluv.rgb_to_lch = function(tuple) return hsluv.luv_to_lch(hsluv.xyz_to_luv(hsluv.rgb_to_xyz(tuple))) end + +hsluv.hsluv_to_rgb = function(tuple) return hsluv.lch_to_rgb(hsluv.hsluv_to_lch(tuple)) end + +hsluv.rgb_to_hsluv = function(tuple) return hsluv.lch_to_hsluv(hsluv.rgb_to_lch(tuple)) end + +hsluv.hpluv_to_rgb = function(tuple) return hsluv.lch_to_rgb(hsluv.hpluv_to_lch(tuple)) end + +hsluv.rgb_to_hpluv = function(tuple) return hsluv.lch_to_hpluv(hsluv.rgb_to_lch(tuple)) end + +hsluv.hsluv_to_hex = function(tuple) return hsluv.rgb_to_hex(hsluv.hsluv_to_rgb(tuple)) end + +hsluv.hpluv_to_hex = function(tuple) return hsluv.rgb_to_hex(hsluv.hpluv_to_rgb(tuple)) end + +hsluv.hex_to_hsluv = function(s) return hsluv.rgb_to_hsluv(hsluv.hex_to_rgb(s)) end + +hsluv.hex_to_hpluv = function(s) return hsluv.rgb_to_hpluv(hsluv.hex_to_rgb(s)) end + +hsluv.m = { + { 3.240969941904521, -1.537383177570093, -0.498610760293 }, + { -0.96924363628087, 1.87596750150772, 0.041555057407175 }, + { 0.055630079696993, -0.20397695888897, 1.056971514242878 }, +} +hsluv.minv = { + { 0.41239079926595, 0.35758433938387, 0.18048078840183 }, + { 0.21263900587151, 0.71516867876775, 0.072192315360733 }, + { 0.019330818715591, 0.11919477979462, 0.95053215224966 }, +} +hsluv.refY = 1.0 +hsluv.refU = 0.19783000664283 +hsluv.refV = 0.46831999493879 +hsluv.kappa = 903.2962962 +hsluv.epsilon = 0.0088564516 + +return hsluv diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/mapper.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/mapper.lua new file mode 100644 index 00000000..2f60323d --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/mapper.lua @@ -0,0 +1,72 @@ +local M = {} + +function M.apply(flavour) + flavour = flavour or require("catppuccin").flavour + + local _O, _C, _U = O, C, U -- Borrowing global var (setfenv doesn't work with require) + O = require("catppuccin").options + C = require("catppuccin.palettes").get_palette(flavour) + U = require "catppuccin.utils.colors" + + C.none = "NONE" + + local dim_percentage = O.dim_inactive.percentage + C.dim = O.dim_inactive.shade == "dark" + and U.vary_color( + { latte = U.darken(C.base, dim_percentage, C.mantle) }, + U.darken(C.base, dim_percentage, C.mantle) + ) + or U.vary_color( + { latte = U.lighten("#FBFCFD", dim_percentage, C.base) }, + U.lighten(C.surface0, dim_percentage, C.base) + ) + + local theme = {} + theme.syntax = require("catppuccin.groups.syntax").get() + theme.editor = require("catppuccin.groups.editor").get() + local final_integrations = {} + + -- https://github.com/catppuccin/nvim/pull/624 + if type(O.integrations.dap) == "table" and O.integrations.dap.enable_ui ~= nil then + O.integrations.dap_ui = O.integrations.dap.enable_ui + O.integrations.dap.enable_ui = nil + end + + for integration in pairs(O.integrations) do + local cot = false + if type(O.integrations[integration]) == "table" then + if O.integrations[integration].enabled == true then cot = true end + else + if O.integrations[integration] == true then + local default = require("catppuccin").default_options.integrations[integration] + O.integrations[integration] = type(default) == "table" and default or {} + O.integrations[integration].enabled = true + cot = true + end + end + + if cot then + final_integrations = vim.tbl_deep_extend( + "force", + final_integrations, + require("catppuccin.groups.integrations." .. integration).get() + ) + end + end + + theme.integrations = final_integrations -- plugins + theme.terminal = require("catppuccin.groups.terminal").get() -- terminal colors + local user_highlights = O.highlight_overrides + if type(user_highlights[flavour]) == "function" then user_highlights[flavour] = user_highlights[flavour](C) end + theme.custom_highlights = vim.tbl_deep_extend( + "keep", + user_highlights[flavour] or {}, + type(user_highlights.all) == "function" and user_highlights.all(C) or user_highlights.all or {} + ) + + O, C, U = _O, _C, _U -- Returning global var + + return theme +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/vim/bit.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/vim/bit.lua new file mode 100644 index 00000000..ea565fe5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/vim/bit.lua @@ -0,0 +1,79 @@ +-- Reference(stripped down): https://github.com/davidm/lua-bit-numberlua/blob/master/lmod/bit/numberlua.lua +local M = {} + +local floor = math.floor + +local MOD = 2 ^ 32 + +local function memoize(f) + local mt = {} + local t = setmetatable({}, mt) + function mt:__index(k) + local v = f(k) + t[k] = v + return v + end + + return t +end + +local function make_bitop_uncached(t, m) + local function bitop(a, b) + local res, p = 0, 1 + while a ~= 0 and b ~= 0 do + local am, bm = a % m, b % m + res = res + t[am][bm] * p + a = (a - am) / m + b = (b - bm) / m + p = p * m + end + res = res + (a + b) * p + return res + end + + return bitop +end + +local function make_bitop(t) + local op1 = make_bitop_uncached(t, 2 ^ 1) + local op2 = memoize(function(a) + return memoize(function(b) return op1(a, b) end) + end) + return make_bitop_uncached(op2, 2 ^ (t.n or 1)) +end + +M.bxor = make_bitop { [0] = { [0] = 0, [1] = 1 }, [1] = { [0] = 1, [1] = 0 }, n = 4 } +local bxor = M.bxor + +local lshift, rshift + +lshift = function(a, disp) -- Lua5.2 inspired + if disp < 0 then return rshift(a, -disp) end + return (a * 2 ^ disp) % 2 ^ 32 +end + +rshift = function(a, disp) -- Lua5.2 inspired + if disp < 0 then return lshift(a, -disp) end + return floor(a % 2 ^ 32 / 2 ^ disp) +end + +local function bit_tobit(x) + x = x % MOD + if x >= 0x80000000 then x = x - MOD end + return x +end + +local function bit_bxor(a, b, c, ...) + if c then + return bit_bxor(bit_bxor(a, b), c, ...) + elseif b then + return bit_tobit(bxor(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end + +return { + bxor = bit_bxor, + lshift = function(x, n) return bit_tobit(lshift(x % MOD, n % 32)) end, +} diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/vim/compiler.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/vim/compiler.lua new file mode 100644 index 00000000..cf2bbdd2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/vim/compiler.lua @@ -0,0 +1,107 @@ +local path_sep = require("catppuccin").path_sep +local O = require("catppuccin").options +local M = {} + +-- Reference: https://github.com/EdenEast/nightfox.nvim +local fmt = string.format + +function M.compile(flavour) + local theme = require("catppuccin.lib.mapper").apply(flavour) + local lines = { + string.format( + [=[ +return string.dump(function() +vim.command[[ +if exists("colors_name") + hi clear +endif +set termguicolors +set background=%s +let g:colors_name = "catppuccin-%s"]=], + (flavour == "latte" and "light" or "dark"), + flavour + ), + } + + local tbl = vim.tbl_deep_extend("keep", theme.custom_highlights, theme.integrations, theme.syntax, theme.editor) + + if O.term_colors == true then + for k, v in pairs(theme.terminal) do + table.insert(lines, fmt("let g:%s = '%s'", k, v)) + end + end + + for group, color in pairs(tbl) do + if color.link and (theme.custom_highlights[group] and not theme.custom_highlights[group].link) then + color.link = nil + end + if color.link then + table.insert(lines, fmt([[highlight! link %s %s]], group, color.link)) + else + if color.style then + local rstyle = {} + for _, style in pairs(color.style) do + if O.no_italic and style == "italic" then style = nil end + if O.no_bold and style == "bold" then style = nil end + if O.no_underline and style == "underline" then style = nil end + if style then rstyle[#rstyle + 1] = style end + end + color.style = table.concat(rstyle, ",") + end + if color.style == "" then color.style = nil end + table.insert( + lines, + fmt( + [[highlight %s guifg=%s guibg=%s gui=%s guisp=%s]], + group, + color.fg or "NONE", + color.bg or "NONE", + color.style or "NONE", + color.sp or "NONE" + ) + ) + end + end + table.insert(lines, "]]end, true)") + if vim.fn.isdirectory(O.compile_path) == 0 then vim.fn.mkdir(O.compile_path, "p") end + local ls = loadstring or load + + if vim.g.catppuccin_debug then -- Debugging purpose + local deb = io.open(O.compile_path .. path_sep .. flavour .. ".lua", "wb") + if deb then + deb:write(table.concat(lines, "\n")) + deb:close() + end + end + + local f = ls(table.concat(lines, "\n")) + if not f then + local err_path = (path_sep == "/" and "/tmp" or os.getenv "TMP") .. "/catppuccin_error.lua" + print(string.format( + [[Catppuccin (error): Most likely some mistake made in your catppuccin config +You can open %s for debugging + +If you think this is a bug, kindly open an issue and attach %s file +Below is the error message that we captured: +]], + err_path, + err_path + )) + local err = io.open(err_path, "wb") + if err then + err:write(table.concat(lines, "\n")) + err:close() + end + dofile(err_path) + return + end + + local file = assert( + io.open(O.compile_path .. path_sep .. flavour, "wb"), + "Permission denied while writing compiled file to " .. O.compile_path .. path_sep .. flavour + ) + file:write(f()) + file:close() +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/vim/init.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/vim/init.lua new file mode 100644 index 00000000..3ea1a1b7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/lib/vim/init.lua @@ -0,0 +1,83 @@ +-- TODO: private _G.vim +vim.command [[command! CatppuccinCompile lua require('catppuccin').compile() print("Catppuccin (info): compiled cache!")]] + +vim.o = setmetatable({}, { + __index = function(_, k) + if k == "background" then return vim.eval "&background" end + end, +}) + +vim.fn.stdpath = function(what) + if what ~= "cache" then return end + if package.config:sub(1, 1) == "\\" then + return vim.fn.expand "%localappdata%" .. [[Temp\vim]] + else + return (os.getenv "XDG_CACHE_HOME" or vim.fn.expand "$HOME/.cache") .. "/vim" + end +end + +vim.env = setmetatable({}, { + __index = function(_, k) return os.getenv(k) end, +}) + +-- Reference: https://github.com/neovim/neovim/blob/master/runtime/lua/vim/shared.lua +local function tbl_isempty(t) + assert(type(t) == "table", string.format("Expected table, got %s", type(t))) + return next(t) == nil +end + +local function tbl_islist(t) + if type(t) ~= "table" then return false end + + local count = 0 + + for k, _ in pairs(t) do + if type(k) == "number" then + count = count + 1 + else + return false + end + end + + if count > 0 then + return true + else + -- TODO(bfredl): in the future, we will always be inside nvim + -- then this check can be deleted. + if vim._empty_dict_mt == nil then return false end + return getmetatable(t) ~= vim._empty_dict_mt + end +end + +local function can_merge(v) return type(v) == "table" and (tbl_isempty(v) or not tbl_islist(v)) end + +local function tbl_extend(behavior, deep_extend, ...) + if behavior ~= "error" and behavior ~= "keep" and behavior ~= "force" then + error('invalid "behavior": ' .. tostring(behavior)) + end + + if select("#", ...) < 2 then + error("wrong number of arguments (given " .. tostring(1 + select("#", ...)) .. ", expected at least 3)") + end + + local ret = {} + if vim._empty_dict_mt ~= nil and getmetatable(select(1, ...)) == vim._empty_dict_mt then ret = vim.empty_dict() end + + for i = 1, select("#", ...) do + local tbl = select(i, ...) + if tbl then + for k, v in pairs(tbl) do + if deep_extend and can_merge(v) and can_merge(ret[k]) then + ret[k] = tbl_extend(behavior, true, ret[k], v) + elseif behavior ~= "force" and ret[k] ~= nil then + if behavior == "error" then error("key found in more than one map: " .. k) end -- Else behavior is "keep". + else + ret[k] = v + end + end + end + end + return ret +end + +function vim.tbl_deep_extend(behavior, ...) return tbl_extend(behavior, true, ...) end diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua new file mode 100644 index 00000000..911d0af4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua @@ -0,0 +1,28 @@ +return { + rosewater = "#f2d5cf", + flamingo = "#eebebe", + pink = "#f4b8e4", + mauve = "#ca9ee6", + red = "#e78284", + maroon = "#ea999c", + peach = "#ef9f76", + yellow = "#e5c890", + green = "#a6d189", + teal = "#81c8be", + sky = "#99d1db", + sapphire = "#85c1dc", + blue = "#8caaee", + lavender = "#babbf1", + text = "#c6d0f5", + subtext1 = "#b5bfe2", + subtext0 = "#a5adce", + overlay2 = "#949cbb", + overlay1 = "#838ba7", + overlay0 = "#737994", + surface2 = "#626880", + surface1 = "#51576d", + surface0 = "#414559", + base = "#303446", + mantle = "#292c3c", + crust = "#232634", +} diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/init.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/init.lua new file mode 100644 index 00000000..68030c45 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/init.lua @@ -0,0 +1,21 @@ +local M = {} + +function M.get_palette(flavour) + local flvr = flavour or require("catppuccin").flavour or vim.g.catppuccin_flavour or "mocha" + local _, palette = pcall(require, "catppuccin.palettes." .. flvr) + local O = require("catppuccin").options + local ans = vim.tbl_deep_extend("keep", O.color_overrides.all or {}, O.color_overrides[flvr] or {}, palette or {}) + if O.kitty then -- https://github.com/kovidgoyal/kitty/issues/2917 + for accent, hex in pairs(ans) do + local red_green_string = hex:sub(1, 5) + local blue_value = tonumber(hex:sub(6, 7), 16) + + -- Slightly increase or decrease brightness of the blue channel + blue_value = blue_value == 255 and blue_value - 1 or blue_value + 1 + ans[accent] = string.format("%s%.2x", red_green_string, blue_value) + end + end + return ans +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/latte.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/latte.lua new file mode 100644 index 00000000..d03f661e --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/latte.lua @@ -0,0 +1,28 @@ +return { + rosewater = "#dc8a78", + flamingo = "#dd7878", + pink = "#ea76cb", + mauve = "#8839ef", + red = "#d20f39", + maroon = "#e64553", + peach = "#fe640b", + yellow = "#df8e1d", + green = "#40a02b", + teal = "#179299", + sky = "#04a5e5", + sapphire = "#209fb5", + blue = "#1e66f5", + lavender = "#7287fd", + text = "#4c4f69", + subtext1 = "#5c5f77", + subtext0 = "#6c6f85", + overlay2 = "#7c7f93", + overlay1 = "#8c8fa1", + overlay0 = "#9ca0b0", + surface2 = "#acb0be", + surface1 = "#bcc0cc", + surface0 = "#ccd0da", + base = "#eff1f5", + mantle = "#e6e9ef", + crust = "#dce0e8", +} diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua new file mode 100644 index 00000000..f5d95c02 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua @@ -0,0 +1,28 @@ +return { + rosewater = "#f4dbd6", + flamingo = "#f0c6c6", + pink = "#f5bde6", + mauve = "#c6a0f6", + red = "#ed8796", + maroon = "#ee99a0", + peach = "#f5a97f", + yellow = "#eed49f", + green = "#a6da95", + teal = "#8bd5ca", + sky = "#91d7e3", + sapphire = "#7dc4e4", + blue = "#8aadf4", + lavender = "#b7bdf8", + text = "#cad3f5", + subtext1 = "#b8c0e0", + subtext0 = "#a5adcb", + overlay2 = "#939ab7", + overlay1 = "#8087a2", + overlay0 = "#6e738d", + surface2 = "#5b6078", + surface1 = "#494d64", + surface0 = "#363a4f", + base = "#24273a", + mantle = "#1e2030", + crust = "#181926", +} diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua new file mode 100644 index 00000000..296c0008 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua @@ -0,0 +1,28 @@ +return { + rosewater = "#f5e0dc", + flamingo = "#f2cdcd", + pink = "#f5c2e7", + mauve = "#cba6f7", + red = "#f38ba8", + maroon = "#eba0ac", + peach = "#fab387", + yellow = "#f9e2af", + green = "#a6e3a1", + teal = "#94e2d5", + sky = "#89dceb", + sapphire = "#74c7ec", + blue = "#89b4fa", + lavender = "#b4befe", + text = "#cdd6f4", + subtext1 = "#bac2de", + subtext0 = "#a6adc8", + overlay2 = "#9399b2", + overlay1 = "#7f849c", + overlay0 = "#6c7086", + surface2 = "#585b70", + surface1 = "#45475a", + surface0 = "#313244", + base = "#1e1e2e", + mantle = "#181825", + crust = "#11111b", +} diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/types.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/types.lua new file mode 100644 index 00000000..8b98a293 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/types.lua @@ -0,0 +1,278 @@ +---@class Catppuccin +---@field options CatppuccinOptions +---@field setup fun(opts: CatppuccinOptions?) + +---@alias CtpFlavor "mocha" | "macchiato" | "frappe" | "latte" | "auto" +---@alias CtpColor "rosewater" | "flamingo" | "pink" | "mauve" | "red" | "maroon" | "peach" | "yellow" | "green" | "teal" | "sky" | "sapphire" | "blue" | "lavender" | "text" | "subtext1" | "subtext0" | "overlay2" | "overlay1" | "overlay0" | "surface2" | "surface1" | "surface0" | "base" | "mantle" | "crust" +---@class CtpFlavors: {all: T, mocha: T, macchiato: T, frappe: T, latte: T } +---@class CtpColors: {rosewater: T, flamingo: T, pink: T, mauve: T, red: T, maroon: T, peach: T, yellow: T, green: T, teal: T, sky: T, sapphire: T, blue: T, lavender: T, text: T, subtext1: T, subtext0: T, overlay2: T, overlay1: T, overlay0: T, surface2: T, surface1: T, surface0: T, base: T, mantle: T, crust: T, none: T } + +---@class CatppuccinOptions +-- Changes the flavor based on the background. See `:h background` for more info. +---@field background CtpBackground? +-- By default catppuccin writes the compiled results into the system's cache directory. +-- You can change the cache dir by changing this value. +---@field compile_path string? +-- Whether to enable transparency. +---@field transparent_background boolean? +-- Toggle the `~` characters after the end of buffers. +---@field show_end_of_buffer boolean? +-- If true, sets terminal colors (e.g. `g:terminal_color_0`). +---@field term_colors boolean? +-- Workaround for kitty transparency issue: https://github.com/kovidgoyal/kitty/issues/2917 +---@field kitty boolean? +-- Settings for dimming of inactive windows. +---@field dim_inactive CtpDimInactive? +-- Disables all italic styles. +---@field no_italic boolean? +-- Disables all bold styles. +---@field no_bold boolean? +-- Disables all underline styles. +---@field no_underline boolean? +-- Handles the style of general hl groups (see `:h highlight-groups`). +---@field styles CtpStyles? +-- Should default integrations be used. +---@field default_integrations boolean? +-- Toggle integrations. Integrations allow Catppuccin to set the theme of various plugins. +---@field integrations CtpIntegrations? +-- Catppuccin colors can be overwritten here. +---@field color_overrides CtpColors | CtpFlavors> | nil +-- Catppuccin highlights can be overwritten here. +---@field highlight_overrides CtpHighlightOverrides? +-- Global highlight overrides. +---@field custom_highlights CtpHighlightOverrideFn | {[string]: CtpHighlight} | nil +-- The default flavor to use on startup. +---@field flavour CtpFlavor? + +---@class CtpBackground +-- Catppuccin flavor to use when `:set background=dark` is set. +---@field dark CtpFlavor? +-- Catppuccin flavor to use when `:set background=light` is set. +---@field light CtpFlavor? + +---@class CtpDimInactive +-- Whether to dim inactive windows. +---@field enabled boolean +-- Whether to darken or lighten inactive windows. +---@field shade "dark" | "light" | nil +-- Percentage of the shade to apply to the inactive window +---@field percentage number? + +---@class CtpStyles +-- Change the style of comments. +---@field comments CtpHighlightArgs[]? +-- Change the style of conditionals. +---@field conditionals CtpHighlightArgs[]? +-- Change the style of loops. +---@field loops CtpHighlightArgs[]? +-- Change the style of functions. +---@field functions CtpHighlightArgs[]? +-- Change the style of keywords. +---@field keywords CtpHighlightArgs[]? +-- Change the style of strings. +---@field strings CtpHighlightArgs[]? +-- Change the style of variables. +---@field variables CtpHighlightArgs[]? +-- Change the style of numbers. +---@field numbers CtpHighlightArgs[]? +-- Change the style of booleans. +---@field booleans CtpHighlightArgs[]? +-- Change the style of properties. +---@field properties CtpHighlightArgs[]? +-- Change the style of types. +---@field types CtpHighlightArgs[]? +-- Change the style of operators. +---@field operators CtpHighlightArgs[]? +-- Change the style of miscs. +---@field miscs CtpHighlightArgs[]? + +---@class CtpNativeLspStyles +-- Change the style of LSP errors. +---@field errors CtpHighlightArgs[]? +-- Change the style of LSP hints. +---@field hints CtpHighlightArgs[]? +-- Change the style of LSP warnings. +---@field warnings CtpHighlightArgs[]? +-- Change the style of LSP information. +---@field information CtpHighlightArgs[]? + +---@class CtpNativeLspInlayHints +-- Toggle the background of inlay hints. +---@field background boolean? + +---@class CtpIntegrations +---@field aerial boolean? +---@field alpha boolean? +---@field barbar boolean? +-- Use this to set it up: +-- +-- ```lua +-- require("barbecue").setup { +-- -- valid options: +-- -- "catppuccin-latte" +-- -- "catppuccin-frappe" +-- -- "catppuccin-macchiato" +-- -- "catppuccin-mocha" +-- theme = "catppuccin", +-- } +---``` +---@field barbecue CtpIntegrationBarbecue | boolean? +---@field beacon boolean? +---@field colorful_winsep CtpIntegrationColorfulWinsep | boolean? +---@field cmp boolean? +-- `coc.nvim` links to `native_lsp` highlight groups, so you can use +-- `native_lsp.virtual_text` and `native_lsp.underlines` to style diagnostics. +---@field coc_nvim boolean? +-- ```lua +-- local sign = vim.fn.sign_define +-- +-- sign("DapBreakpoint", { text = "●", texthl = "DapBreakpoint", linehl = "", numhl = ""}) +-- sign("DapBreakpointCondition", { text = "●", texthl = "DapBreakpointCondition", linehl = "", numhl = ""}) +-- sign("DapLogPoint", { text = "◆", texthl = "DapLogPoint", linehl = "", numhl = ""}) +-- ``` +---@field dap boolean? +---@field dap_ui boolean? +---@field dashboard boolean? +---@field diffview boolean? +---@field dropbar CtpIntegrationDropbar | boolean? +---@field fern boolean? +-- Set `notification.window.winblend` to `0` in your `fidget` config: +-- +-- ```lua +-- require("fidget").setup { +-- notification = { +-- window = { winblend = 0 }, +-- } +-- } +-- ``` +---@field fidget boolean? +---@field flash boolean? +---@field gitgutter boolean? +---@field gitsigns boolean? +---@field harpoon boolean? +---@field headlines boolean? +---@field hop boolean? +---@field illuminate CtpIntegrationIlluminate | boolean? +---@field indent_blankline CtpIntegrationIndentBlankline | boolean? +---@field leap boolean? +---@field lightspeed boolean? +-- For custom Lsp kind icon and colors, adjust your `lspsaga` config: +-- +-- ```lua +-- require("lspsaga").setup { +-- ui = { +-- kind = require("catppuccin.groups.integrations.lsp_saga").custom_kind(), +-- }, +-- } +-- ``` +---@field lsp_saga boolean? +---@field lsp_trouble boolean? +---@field markdown boolean? +---@field mason boolean? +---@field native_lsp CtpIntegrationNativeLsp | boolean? +-- You **NEED** to enable highlight in your `nvim-navic` config or it won't work: +-- +-- ```lua +-- require("nvim-navic").setup { +-- highlight = true +-- } +-- ``` +---@field navic CtpIntegrationNavic | boolean? +---@field neogit boolean? +---@field neotest boolean? +---@field neotree boolean? +---@field noice boolean? +---@field notify boolean? +---@field nvimtree boolean? +---@field octo boolean? +---@field overseer boolean? +---@field pounce boolean? +---@field rainbow_delimiters boolean? +---@field sandwich boolean? +---@field semantic_tokens boolean? +---@field symbols_outline boolean? +---@field telekasten boolean? +---@field telescope CtpIntegrationTelescope | boolean? +---@field treesitter boolean? +---@field treesitter_context boolean? +---@field ts_rainbow boolean? +---@field ts_rainbow2 boolean? +---@field vim_sneak boolean? +---@field vimwiki boolean? +---@field which_key boolean? +---@field window_picker boolean? + +---@class CtpIntegrationBarbecue +-- Whether to use the alternative background. +---@field alt_background boolean? +-- Whether the basename should be bold. +---@field bold_basename boolean? +-- Whether the context should be dimmed. +---@field dim_context boolean? +-- Whether the directory name should be dimmed. +---@field dim_dirname boolean? + +---@class CtpIntegrationColorfulWinsep +-- Whether to enable the colorful-winsep integration. +---@field enabled boolean +-- Set to a Catppuccin color name to use for the split separator. +---@field color CtpColor? + +---@class CtpIntegrationDropbar +-- Whether to enable the dropbar integration. +---@field enabled boolean +-- Set to true to apply color to the text in dropbar, false to only apply it to the icons. +---@field color_mode boolean? + +---@class CtpIntegrationIndentBlankline +-- Whether to enable the integration. +---@field enabled boolean +-- Sets the color of the scope line +---@field scope_color CtpColor? +-- Enables char highlights per indent level. +-- Follow the instructions on the plugins GitHub repo to set it up. +---@field colored_indent_levels boolean? + +---@class CtpIntegrationMini +-- Whether to enable the integration. +---@field enabled boolean +-- Sets the color of the scope line +---@field indentscope_color CtpColor? + +---@class CtpIntegrationNativeLsp +-- Whether to enable the Native LSP integration. +---@field enabled boolean +-- Styles to apply to virtual text. +---@field virtual_text CtpNativeLspStyles? +-- Styles to apply to underlines. +---@field underlines CtpNativeLspStyles? +-- Inlay hints options. +---@field inlay_hints CtpNativeLspInlayHints? + +---@class CtpIntegrationNavic +-- Whether to enable the navic integration. +---@field enabled boolean +-- Override the background color for navic. +---@field custom_bg CtpColor | "NONE" | "lualine" | nil + +---@class CtpIntegrationTelescope +-- Whether to enable the telescope integration +---@field enabled boolean? +-- The style of Telescope +---@field style "classic" | "nvchad" | nil + +---@class CtpIntegrationIlluminate +-- Whether to enable the vim-illuminate integration +---@field enabled boolean? +-- Whether to standout IlluminatedWordWrite hl group +---@field lsp boolean? + +---@alias CtpHighlightArgs "bold" | "underline" | "undercurl" | "underdouble" | "underdotted" | "underdashed" | "strikethrough" | "reverse" | "inverse" | "italic" | "standout" | "altfont" | "nocombine" | "NONE" +---@alias CtpHighlightOverrideFn fun(colors: CtpColors): { [string]: CtpHighlight} +---@alias CtpHighlightOverrides CtpFlavors + +---@class CtpHighlight +---@field fg string? +---@field bg string? +---@field style CtpHighlightArgs[]? +---@field link string? diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/barbecue.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/barbecue.lua new file mode 100644 index 00000000..cfef00b9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/barbecue.lua @@ -0,0 +1,53 @@ +return function(flavour) + local C = require("catppuccin.palettes").get_palette(flavour) + local O = require("catppuccin").options + + local transparent_bg = O.transparent_background and C.none or C.mantle + + local dirname_color = O.integrations.barbecue.dim_dirname and C.overlay1 or C.text + local basename_bold = O.integrations.barbecue.bold_basename + local context_color = O.integrations.barbecue.dim_context and C.overlay1 or C.text + local background_color = O.integrations.barbecue.alt_background and transparent_bg or C.none + + local M = { + normal = { fg = C.text, bg = background_color }, + + ellipsis = { fg = C.overlay1 }, + separator = { fg = C.overlay1 }, + modified = { fg = C.peach }, + + dirname = { fg = dirname_color }, + basename = { fg = C.text, bold = basename_bold }, + context = { fg = context_color }, + + -- Same keys as navic + context_file = { fg = C.blue }, + context_module = { fg = C.blue }, + context_namespace = { fg = C.blue }, + context_package = { fg = C.blue }, + context_class = { fg = C.yellow }, + context_method = { fg = C.blue }, + context_property = { fg = C.green }, + context_field = { fg = C.green }, + context_constructor = { fg = C.blue }, + context_enum = { fg = C.green }, + context_interface = { fg = C.yellow }, + context_function = { fg = C.blue }, + context_variable = { fg = C.flamingo }, + context_constant = { fg = C.peach }, + context_string = { fg = C.green }, + context_number = { fg = C.peach }, + context_boolean = { fg = C.peach }, + context_array = { fg = C.blue }, + context_object = { fg = C.blue }, + context_key = { fg = C.flamingo }, + context_null = { fg = C.peach }, + context_enum_member = { fg = C.red }, + context_struct = { fg = C.blue }, + context_event = { fg = C.blue }, + context_operator = { fg = C.blue }, + context_type_parameter = { fg = C.blue }, + } + + return M +end diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/colors.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/colors.lua new file mode 100644 index 00000000..ddf0ff17 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/colors.lua @@ -0,0 +1,170 @@ +local M = {} + +local hsluv = require "catppuccin.lib.hsluv" + +M.bg = "#000000" +M.fg = "#ffffff" +M.day_brightness = 0.3 + +---@param hex_str string hexadecimal value of a color +local hex_to_rgb = function(hex_str) + local hex = "[abcdef0-9][abcdef0-9]" + local pat = "^#(" .. hex .. ")(" .. hex .. ")(" .. hex .. ")$" + hex_str = string.lower(hex_str) + + assert(string.find(hex_str, pat) ~= nil, "hex_to_rgb: invalid hex_str: " .. tostring(hex_str)) + + local red, green, blue = string.match(hex_str, pat) + return { tonumber(red, 16), tonumber(green, 16), tonumber(blue, 16) } +end + +---@param fg string forecrust color +---@param bg string background color +---@param alpha number number between 0 and 1. 0 results in bg, 1 results in fg +function M.blend(fg, bg, alpha) + bg = hex_to_rgb(bg) + fg = hex_to_rgb(fg) + + local blendChannel = function(i) + local ret = (alpha * fg[i] + ((1 - alpha) * bg[i])) + return math.floor(math.min(math.max(0, ret), 255) + 0.5) + end + + return string.format("#%02X%02X%02X", blendChannel(1), blendChannel(2), blendChannel(3)) +end + +function M.darken(hex, amount, bg) return M.blend(hex, bg or M.bg, math.abs(amount)) end + +function M.lighten(hex, amount, fg) return M.blend(hex, fg or M.fg, math.abs(amount)) end + +function M.brighten(color, percentage) + local hsl = hsluv.hex_to_hsluv(color) + local larpSpace = 100 - hsl[3] + if percentage < 0 then larpSpace = hsl[3] end + hsl[3] = hsl[3] + larpSpace * percentage + return hsluv.hsluv_to_hex(hsl) +end + +function M.invertColor(color) + if color ~= "NONE" then + local hsl = hsluv.hex_to_hsluv(color) + hsl[3] = 100 - hsl[3] + if hsl[3] < 40 then hsl[3] = hsl[3] + (100 - hsl[3]) * M.day_brightness end + return hsluv.hsluv_to_hex(hsl) + end + return color +end + +function M.string_to_color(colors, value, default) + if not value or value == "" then return default end + + -- If the value is a hex color code then return it + local hex = "[abcdef0-9][abcdef0-9]" + local pat = "^#" .. hex .. hex .. hex .. "$" + if string.match(value, pat) then return value end + + local acceptable_colors = { "black", "red", "green", "blue", "magenta", "cyan", "text", "orange", "pink" } + for _, ac in ipairs(acceptable_colors) do + if string.match(value, ac) then return colors[value] end + end + + -- Did not match anything to return default + return default +end + +function M.color_is_bright(r, g, b) + -- Counting the perceptive luminance - human eye favors green color + local luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 + if luminance > 0.5 then + return true -- Bright colors, black font + else + return false -- Dark colors, text font + end +end + +function M.hex2rgb(hex) + return tonumber("0x" .. hex:sub(1, 2)), tonumber("0x" .. hex:sub(3, 4)), tonumber("0x" .. hex:sub(5, 6)) +end + +function M.assert_brightness(color) + local hex = color:gsub("#", "") + local r = M.hex2rgb(string.sub(hex, 1, 2)) + local g = M.hex2rgb(string.sub(hex, 3, 4)) + local b = M.hex2rgb(string.sub(hex, 5, 6)) + + if M.color_is_bright(tonumber(r), tonumber(g), tonumber(b)) == true then + return true -- bright + end + + return false -- dull +end + +function M.vary_color(palettes, default) + local flvr = require("catppuccin").flavour + + if palettes[flvr] ~= nil then return palettes[flvr] end + return default +end + +local function rgb2Hex(rgb) + local hexadecimal = "#" + + for _, value in pairs(rgb) do + local hex = "" + + while value > 0 do + local index = math.fmod(value, 16) + 1 + value = math.floor(value / 16) + hex = string.sub(hsluv.hexChars, index, index) .. hex + end + + if string.len(hex) == 0 then + hex = "00" + elseif string.len(hex) == 1 then + hex = "0" .. hex + end + + hexadecimal = hexadecimal .. hex + end + + return hexadecimal +end + +function M.increase_saturation(hex, percentage) + local rgb = hex_to_rgb(hex) + + local saturation_float = percentage + + table.sort(rgb) + local rgb_intensity = { + min = rgb[1] / 255, + mid = rgb[2] / 255, + max = rgb[3] / 255, + } + + if rgb_intensity.max == rgb_intensity.min then + -- all colors have same intensity, which means + -- the original color is gray, so we can't change saturation. + return hex + end + + local new_intensities = {} + new_intensities.max = rgb_intensity.max + new_intensities.min = rgb_intensity.max * (1 - saturation_float) + + if rgb_intensity.mid == rgb_intensity.min then + new_intensities.mid = new_intensities.min + else + local intensity_proportion = (rgb_intensity.max - rgb_intensity.mid) / (rgb_intensity.mid - rgb_intensity.min) + new_intensities.mid = (intensity_proportion * new_intensities.min + rgb_intensity.max) + / (intensity_proportion + 1) + end + + for i, v in pairs(new_intensities) do + new_intensities[i] = math.floor(v * 255) + end + table.sort(new_intensities) + return (rgb2Hex { new_intensities.max, new_intensities.min, new_intensities.mid }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/lualine.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/lualine.lua new file mode 100644 index 00000000..8f0b1f0b --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/lualine.lua @@ -0,0 +1,46 @@ +return function(flavour) + local C = require("catppuccin.palettes").get_palette(flavour) + local O = require("catppuccin").options + local catppuccin = {} + + local transparent_bg = O.transparent_background and "NONE" or C.mantle + + catppuccin.normal = { + a = { bg = C.blue, fg = C.mantle, gui = "bold" }, + b = { bg = C.surface0, fg = C.blue }, + c = { bg = transparent_bg, fg = C.text }, + } + + catppuccin.insert = { + a = { bg = C.green, fg = C.base, gui = "bold" }, + b = { bg = C.surface0, fg = C.green }, + } + + catppuccin.terminal = { + a = { bg = C.green, fg = C.base, gui = "bold" }, + b = { bg = C.surface0, fg = C.green }, + } + + catppuccin.command = { + a = { bg = C.peach, fg = C.base, gui = "bold" }, + b = { bg = C.surface0, fg = C.peach }, + } + + catppuccin.visual = { + a = { bg = C.mauve, fg = C.base, gui = "bold" }, + b = { bg = C.surface0, fg = C.mauve }, + } + + catppuccin.replace = { + a = { bg = C.red, fg = C.base, gui = "bold" }, + b = { bg = C.surface0, fg = C.red }, + } + + catppuccin.inactive = { + a = { bg = transparent_bg, fg = C.blue }, + b = { bg = transparent_bg, fg = C.surface1, gui = "bold" }, + c = { bg = transparent_bg, fg = C.overlay0 }, + } + + return catppuccin +end diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/reactive.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/reactive.lua new file mode 100644 index 00000000..ca8bfc26 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/catppuccin/utils/reactive.lua @@ -0,0 +1,135 @@ +local M = {} + +function M.cursor(name, palette) + return { + name = name, + init = function() vim.opt.guicursor:append "a:ReactiveCursor" end, + modes = { + i = { + hl = { + ReactiveCursor = { bg = palette.teal }, + }, + }, + no = { + operators = { + d = { + hl = { + ReactiveCursor = { bg = palette.red }, + }, + }, + y = { + hl = { + ReactiveCursor = { bg = palette.peach }, + }, + }, + c = { + hl = { + ReactiveCursor = { bg = palette.blue }, + }, + }, + }, + }, + R = { + hl = { + ReactiveCursor = { bg = palette.sapphire }, + }, + }, + -- visual + [{ "v", "V", "\x16" }] = { + hl = { + ReactiveCursor = { bg = palette.mauve }, + }, + }, + -- select + [{ "s", "S", "\x13" }] = { + hl = { + ReactiveCursor = { bg = palette.pink }, + }, + }, + }, + } +end + +function M.cursorline(name, palette) + local darken = require("catppuccin.utils.colors").darken + + return { + name = name, + static = { + winhl = { + inactive = { + CursorLine = { bg = darken(palette.surface0, 0.75) }, + CursorLineNr = { bg = darken(palette.surface0, 0.75) }, + }, + }, + }, + modes = { + -- insert mode + i = { + winhl = { + CursorLine = { bg = darken(palette.sky, 0.3) }, + CursorLineNr = { bg = darken(palette.sky, 0.3) }, + }, + }, + -- normal mode + n = { + winhl = { + CursorLine = { bg = palette.surface0 }, + CursorLineNr = { bg = palette.surface0 }, + }, + }, + -- operator-pending mode + no = { + operators = { + [{ "gu", "gU", "g~", "~" }] = { + winhl = { + CursorLine = { bg = palette.surface2 }, + CursorLineNr = { bg = palette.surface2 }, + }, + }, + -- delete operator + d = { + winhl = { + CursorLine = { bg = darken(palette.red, 0.4) }, + CursorLineNr = { bg = darken(palette.red, 0.4) }, + }, + }, + -- yank operator + y = { + winhl = { + CursorLine = { bg = darken(palette.peach, 0.3) }, + CursorLineNr = { bg = darken(palette.peach, 0.3) }, + }, + }, + -- change operator + c = { + winhl = { + CursorLine = { bg = darken(palette.blue, 0.4) }, + CursorLineNr = { bg = darken(palette.blue, 0.4) }, + }, + }, + }, + }, + R = { + winhl = { + CursorLine = { bg = darken(palette.sapphire, 0.4) }, + CursorLineNr = { bg = darken(palette.sapphire, 0.4) }, + }, + }, + -- visual mode + [{ "v", "V", "\x16" }] = { + winhl = { + Visual = { bg = darken(palette.mauve, 0.4) }, + }, + }, + -- select mode + [{ "s", "S", "\x13" }] = { + winhl = { + Visual = { bg = darken(palette.pink, 0.4) }, + }, + }, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-frappe.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-frappe.lua new file mode 100644 index 00000000..aafd00c3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-frappe.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.lualine" "frappe" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-latte.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-latte.lua new file mode 100644 index 00000000..2b11e17f --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-latte.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.lualine" "latte" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-macchiato.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-macchiato.lua new file mode 100644 index 00000000..c8102ca3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-macchiato.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.lualine" "macchiato" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-mocha.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-mocha.lua new file mode 100644 index 00000000..d5b190cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin-mocha.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.lualine" "mocha" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin.lua new file mode 100644 index 00000000..4e18b44c --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/lualine/themes/catppuccin.lua @@ -0,0 +1 @@ +return require "catppuccin.utils.lualine"() diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-frappe-cursor.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-frappe-cursor.lua new file mode 100644 index 00000000..3cbc8c67 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-frappe-cursor.lua @@ -0,0 +1,4 @@ +local palette = require("catppuccin.palettes").get_palette "frappe" +local presets = require "catppuccin.utils.reactive" + +return presets.cursor("catppuccin-frappe-cursor", palette) diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-frappe-cursorline.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-frappe-cursorline.lua new file mode 100644 index 00000000..4e8a1b44 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-frappe-cursorline.lua @@ -0,0 +1,78 @@ +local darken = require("catppuccin.utils.colors").darken +local palette = require("catppuccin.palettes").get_palette "frappe" + +return { + name = "catppuccin-frappe-cursorline", + static = { + winhl = { + inactive = { + CursorLine = { bg = darken(palette.surface0, 0.85) }, + CursorLineNr = { bg = darken(palette.surface0, 0.85) }, + }, + }, + }, + modes = { + i = { + winhl = { + CursorLine = { bg = darken(palette.sky, 0.4) }, + CursorLineNr = { bg = darken(palette.sky, 0.4) }, + }, + }, + n = { + winhl = { + CursorLine = { bg = palette.surface0 }, + CursorLineNr = { bg = palette.surface0 }, + }, + }, + no = { + operators = { + [{ "gu", "gU", "g~", "~" }] = { + winhl = { + CursorLine = { bg = palette.surface2 }, + CursorLineNr = { bg = palette.surface2 }, + }, + }, + -- delete operator + d = { + winhl = { + CursorLine = { bg = darken(palette.red, 0.4) }, + CursorLineNr = { bg = darken(palette.red, 0.4) }, + }, + }, + -- yank operator + y = { + winhl = { + CursorLine = { bg = darken(palette.peach, 0.4) }, + CursorLineNr = { bg = darken(palette.peach, 0.4) }, + }, + }, + -- change operator + c = { + winhl = { + CursorLine = { bg = darken(palette.blue, 0.5) }, + CursorLineNr = { bg = darken(palette.blue, 0.5) }, + }, + }, + }, + }, + -- replace mode + R = { + winhl = { + CursorLine = { bg = darken(palette.sapphire, 0.5) }, + CursorLineNr = { bg = darken(palette.sapphire, 0.5) }, + }, + }, + -- visual mode + [{ "v", "V", "\x16" }] = { + winhl = { + Visual = { bg = darken(palette.mauve, 0.4) }, + }, + }, + -- select mode + [{ "s", "S", "\x13" }] = { + winhl = { + Visual = { bg = darken(palette.pink, 0.4) }, + }, + }, + }, +} diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-latte-cursor.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-latte-cursor.lua new file mode 100644 index 00000000..3c9a660e --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-latte-cursor.lua @@ -0,0 +1,8 @@ +local palette = require("catppuccin.palettes").get_palette "latte" +local presets = require "catppuccin.utils.reactive" + +local preset = presets.cursor("catppuccin-latte-cursor", palette) + +preset.modes.R.hl.ReactiveCursor = { bg = palette.flamingo } + +return preset diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-latte-cursorline.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-latte-cursorline.lua new file mode 100644 index 00000000..77e647bf --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-latte-cursorline.lua @@ -0,0 +1,77 @@ +local lighten = require("catppuccin.utils.colors").lighten +local palette = require("catppuccin.palettes").get_palette "latte" + +return { + name = "catppuccin-latte-cursorline", + static = { + winhl = { + inactive = { + CursorLine = { bg = lighten(palette.surface0, 0.5) }, + CursorLineNr = { bg = lighten(palette.surface0, 0.5) }, + }, + }, + }, + modes = { + i = { + winhl = { + CursorLine = { bg = lighten(palette.teal, 0.4) }, + CursorLineNr = { bg = lighten(palette.teal, 0.4) }, + }, + }, + n = { + winhl = { + CursorLine = { bg = palette.surface0 }, + CursorLineNr = { bg = palette.surface0 }, + }, + }, + no = { + operators = { + [{ "gu", "gU", "g~", "~" }] = { + winhl = { + CursorLine = { bg = palette.surface2 }, + CursorLineNr = { bg = palette.surface2 }, + }, + }, + -- delete operator + d = { + winhl = { + CursorLine = { bg = lighten(palette.red, 0.3) }, + CursorLineNr = { bg = lighten(palette.red, 0.3) }, + }, + }, + -- yank operator + y = { + winhl = { + CursorLine = { bg = lighten(palette.peach, 0.3) }, + CursorLineNr = { bg = lighten(palette.peach, 0.3) }, + }, + }, + -- change operator + c = { + winhl = { + CursorLine = { bg = lighten(palette.blue, 0.3) }, + CursorLineNr = { bg = lighten(palette.blue, 0.3) }, + }, + }, + }, + }, + -- visual + [{ "v", "V", "\x16" }] = { + winhl = { + Visual = { bg = lighten(palette.mauve, 0.3) }, + }, + }, + -- select + [{ "s", "S", "\x13" }] = { + winhl = { + Visual = { bg = lighten(palette.pink, 0.3) }, + }, + }, + R = { + winhl = { + CursorLine = { bg = lighten(palette.flamingo, 0.2) }, + CursorLineNr = { bg = lighten(palette.flamingo, 0.2) }, + }, + }, + }, +} diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-macchiato-cursor.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-macchiato-cursor.lua new file mode 100644 index 00000000..73d3b3a1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-macchiato-cursor.lua @@ -0,0 +1,4 @@ +local palette = require("catppuccin.palettes").get_palette "macchiato" +local presets = require "catppuccin.utils.reactive" + +return presets.cursor("catppuccin-macchiato-cursor", palette) diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-macchiato-cursorline.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-macchiato-cursorline.lua new file mode 100644 index 00000000..a38c3fd1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-macchiato-cursorline.lua @@ -0,0 +1,10 @@ +local palette = require("catppuccin.palettes").get_palette "macchiato" +local presets = require "catppuccin.utils.reactive" +local darken = require("catppuccin.utils.colors").darken + +local preset = presets.cursorline("catppuccin-macchiato-cursorline", palette) + +preset.static.winhl.inactive.CursorLine = { bg = darken(palette.surface0, 0.8) } +preset.static.winhl.inactive.CursorLineNr = { bg = darken(palette.surface0, 0.8) } + +return preset diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-mocha-cursor.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-mocha-cursor.lua new file mode 100644 index 00000000..c4e47939 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-mocha-cursor.lua @@ -0,0 +1,4 @@ +local palette = require("catppuccin.palettes").get_palette "mocha" +local presets = require "catppuccin.utils.reactive" + +return presets.cursor("catppuccin-mocha-cursor", palette) diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-mocha-cursorline.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-mocha-cursorline.lua new file mode 100644 index 00000000..d511dc9f --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/lua/reactive/presets/catppuccin-mocha-cursorline.lua @@ -0,0 +1,4 @@ +local palette = require("catppuccin.palettes").get_palette "mocha" +local presets = require "catppuccin.utils.reactive" + +return presets.cursorline("catppuccin-mocha-cursorline", palette) diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/stylua.toml b/config/neovim/store/lazy-plugins/catppuccin-nvim/stylua.toml new file mode 100644 index 00000000..7a77a3f5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/stylua.toml @@ -0,0 +1,7 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Tabs" +indent_width = 4 +quote_style = "AutoPreferDouble" +collapse_simple_statement = "Always" +call_parentheses = "None" diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/flavour_spec.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/flavour_spec.lua new file mode 100644 index 00000000..2d069775 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/flavour_spec.lua @@ -0,0 +1,104 @@ +local function reload() + for name, _ in pairs(package.loaded) do + if name:match "^catppuccin" then package.loaded[name] = nil end + end + vim.g.catppuccin_flavour = nil + vim.cmd [[highlight clear]] +end + +describe("set background to", function() + before_each(function() + reload() + vim.cmd.colorscheme "catppuccin" + end) + it("light", function() + vim.o.background = "light" + assert.equals("catppuccin-latte", vim.g.colors_name) + end) + it("dark", function() + vim.o.background = "dark" + assert.equals("catppuccin-mocha", vim.g.colors_name) + end) +end) + +describe("respect vim.o.background =", function() + before_each(function() reload() end) + it("light", function() + vim.o.background = "light" + vim.cmd.colorscheme "catppuccin" + assert.equals("catppuccin-latte", vim.g.colors_name) + end) + it("dark", function() + vim.o.background = "dark" + vim.cmd.colorscheme "catppuccin" + assert.equals("catppuccin-mocha", vim.g.colors_name) + end) +end) + +describe("change flavour to", function() + before_each(function() reload() end) + it("latte", function() + vim.cmd.colorscheme "catppuccin-latte" + assert.equals("catppuccin-latte", vim.g.colors_name) + end) + it("frappe", function() + vim.cmd.colorscheme "catppuccin-frappe" + assert.equals("catppuccin-frappe", vim.g.colors_name) + end) + it("macchiato", function() + vim.cmd.colorscheme "catppuccin-macchiato" + assert.equals("catppuccin-macchiato", vim.g.colors_name) + end) + it("mocha", function() + vim.cmd.colorscheme "catppuccin-mocha" + assert.equals("catppuccin-mocha", vim.g.colors_name) + end) +end) + +describe("respect setup flavour =", function() + before_each(function() reload() end) + it("latte", function() + require("catppuccin").setup { flavour = "latte" } + vim.cmd.colorscheme "catppuccin" + assert.equals("catppuccin-latte", vim.g.colors_name) + end) + it("frappe", function() + require("catppuccin").setup { flavour = "frappe" } + vim.cmd.colorscheme "catppuccin" + assert.equals("catppuccin-frappe", vim.g.colors_name) + end) + it("macchiato", function() + require("catppuccin").setup { flavour = "macchiato" } + vim.cmd.colorscheme "catppuccin" + assert.equals("catppuccin-macchiato", vim.g.colors_name) + end) + it("mocha", function() + require("catppuccin").setup { flavour = "mocha" } + vim.cmd.colorscheme "catppuccin" + assert.equals("catppuccin-mocha", vim.g.colors_name) + end) +end) + +describe("(deprecated) respect vim.g.catppuccin_flavour =", function() + before_each(function() reload() end) + it("latte", function() + vim.g.catppuccin_flavour = "latte" + vim.cmd.colorscheme "catppuccin" + assert.equals("catppuccin-latte", vim.g.colors_name) + end) + it("frappe", function() + vim.g.catppuccin_flavour = "frappe" + vim.cmd.colorscheme "catppuccin" + assert.equals("catppuccin-frappe", vim.g.colors_name) + end) + it("macchiato", function() + vim.g.catppuccin_flavour = "macchiato" + vim.cmd.colorscheme "catppuccin" + assert.equals("catppuccin-macchiato", vim.g.colors_name) + end) + it("mocha", function() + vim.g.catppuccin_flavour = "mocha" + vim.cmd.colorscheme "catppuccin" + assert.equals("catppuccin-mocha", vim.g.colors_name) + end) +end) diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/hash_spec.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/hash_spec.lua new file mode 100644 index 00000000..dc475098 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/hash_spec.lua @@ -0,0 +1,41 @@ +describe("hash", function() + local hash = require("catppuccin.lib.hashing").hash + it("typo", function() assert.are_not.equals(hash { custom_highlight = {} }, hash { ustom_highlight = {} }) end) + it( + "when table order is shuffled", + function() + assert.equals( + hash { + custom_highlight = { + Search = { fg = "#F5C2E7", bg = "#45475A", style = { "bold" } }, + IncSearch = { fg = "#45475A", bg = "#F5C2E7" }, + }, + }, + hash { + custom_highlight = { + Search = { style = { "bold" }, bg = "#45475A", fg = "#F5C2E7" }, + IncSearch = { bg = "#F5C2E7", fg = "#45475A" }, + }, + } + ) + end + ) + it( + "when toggle true/false", + function() + assert.are_not.equals({ + integrations = { + navic = true, + noice = true, + fidget = true, + }, + }, { + integrations = { + navic = true, + noice = false, + fidget = false, + }, + }) + end + ) +end) diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/init.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/init.lua new file mode 100644 index 00000000..6db3de31 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/init.lua @@ -0,0 +1,27 @@ +local status, error = pcall(function() + local root = vim.fn.fnamemodify(".repro", ":p") + for _, name in ipairs { "config", "data", "state", "cache" } do + vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name + end + + local lazypath = root .. "/plugins/lazy.nvim" + if not vim.loop.fs_stat(lazypath) then + vim.fn.system { "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath } + end + vim.opt.runtimepath:prepend(lazypath) + + require("lazy").setup({ + { "catppuccin/nvim", dev = true }, + }, { + root = root .. "/plugins", + dev = { + path = debug.getinfo(1).source:sub(2, -21), + }, + }) + + require("catppuccin").setup() + vim.cmd.colorscheme "catppuccin" +end) + +if error then print(error) end +vim.cmd(status and "0cq" or "1cq") diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/init.vim b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/init.vim new file mode 100644 index 00000000..e0b4e9c2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/init.vim @@ -0,0 +1,13 @@ +try + call plug#begin() + Plug expand('')[0:-16] + call plug#end() + + lua require("catppuccin").setup {} + colorscheme catppuccin +catch + echo v:exception + 1cq +finally + 0cq +endtry diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/minimal_init.vim b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/minimal_init.vim new file mode 100644 index 00000000..a22651af --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/minimal_init.vim @@ -0,0 +1,3 @@ +set rtp+=. + +runtime! plugin/plenary.vim diff --git a/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/palette_spec.lua b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/palette_spec.lua new file mode 100644 index 00000000..a411cbf0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/catppuccin-nvim/tests/palette_spec.lua @@ -0,0 +1,19 @@ +local function reload() + for name, _ in pairs(package.loaded) do + if name:match "^catppuccin" then package.loaded[name] = nil end + end + vim.g.catppuccin_flavour = nil + vim.cmd [[highlight clear]] +end + +-- TODO: Move this to setup_spec +describe("get palette", function() + before_each(function() reload() end) + it("before setup", function() + assert.equals(pcall(function() require("catppuccin.palettes").get_palette() end), true) + end) + it("after setup", function() + require("catppuccin").setup() + assert.equals(pcall(function() require("catppuccin.palettes").get_palette() end), true) + end) +end) diff --git a/config/neovim/store/lazy-plugins/chadtree/.dockerignore b/config/neovim/store/lazy-plugins/chadtree/.dockerignore new file mode 100644 index 00000000..706a2a11 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/.dockerignore @@ -0,0 +1,7 @@ +/.git/ +/.venv/ +__pycache__/ +.mypy_cache/ +/.vars/ +/temp/ +/.vscode/ diff --git a/config/neovim/store/lazy-plugins/chadtree/.github/workflows/artifacts.yml b/config/neovim/store/lazy-plugins/chadtree/.github/workflows/artifacts.yml new file mode 100644 index 00000000..97a657fe --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/.github/workflows/artifacts.yml @@ -0,0 +1,25 @@ +--- +name: Artifacts + +on: + push: + branches: + - chad + schedule: + - cron: "0 0 * * *" # daily + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v3 + + - env: + CI_TOKEN: ${{ secrets.CI_TOKEN }} + run: |- + make build diff --git a/config/neovim/store/lazy-plugins/chadtree/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/chadtree/.github/workflows/ci.yml new file mode 100644 index 00000000..8051a9b2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +--- +name: CI + +on: + push: + schedule: + - cron: "0 0 * * *" # daily + +jobs: + mypy: + strategy: + matrix: + python_ver: + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "3" + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python_ver }} + + - run: |- + make lint diff --git a/config/neovim/store/lazy-plugins/chadtree/.github/workflows/codeql-analysis.yml b/config/neovim/store/lazy-plugins/chadtree/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..85042a63 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/.github/workflows/codeql-analysis.yml @@ -0,0 +1,57 @@ +--- +name: "CodeQL" + +on: + push: + schedule: + - cron: "0 0 * * *" # daily + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/config/neovim/store/lazy-plugins/chadtree/.gitignore b/config/neovim/store/lazy-plugins/chadtree/.gitignore new file mode 100644 index 00000000..706a2a11 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/.gitignore @@ -0,0 +1,7 @@ +/.git/ +/.venv/ +__pycache__/ +.mypy_cache/ +/.vars/ +/temp/ +/.vscode/ diff --git a/config/neovim/store/lazy-plugins/chadtree/.hadolint.yaml b/config/neovim/store/lazy-plugins/chadtree/.hadolint.yaml new file mode 100644 index 00000000..eb3743d0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/.hadolint.yaml @@ -0,0 +1,6 @@ +--- +ignored: + # Allow Latest Dockerfile + - DL3007 + # Allow Unpinned Apt Packages + - DL3008 diff --git a/config/neovim/store/lazy-plugins/chadtree/Dockerfile b/config/neovim/store/lazy-plugins/chadtree/Dockerfile new file mode 100644 index 00000000..5489d0d7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/Dockerfile @@ -0,0 +1,13 @@ +FROM ubuntu:focal + +ENV TERM=xterm-256color +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends -- python3-venv neovim git ca-certificates && \ + rm -rf -- /var/lib/apt/lists/* + + +COPY ./docker / +WORKDIR /root/.config/nvim/pack/modules/start/chadtree +COPY . . + +RUN python3 -m chadtree deps --xdg ~/.local/share/nvim diff --git a/config/neovim/store/lazy-plugins/chadtree/Makefile b/config/neovim/store/lazy-plugins/chadtree/Makefile new file mode 100644 index 00000000..be5d2b6d --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/Makefile @@ -0,0 +1,60 @@ +MAKEFLAGS += --check-symlink-times +MAKEFLAGS += --jobs +MAKEFLAGS += --no-builtin-rules +MAKEFLAGS += --no-builtin-variables +MAKEFLAGS += --shuffle +MAKEFLAGS += --warn-undefined-variables +SHELL := bash +.DELETE_ON_ERROR: +.ONESHELL: +.SHELLFLAGS := --norc --noprofile -Eeuo pipefail -O dotglob -O nullglob -O extglob -O failglob -O globstar -c + +.DEFAULT_GOAL := help + +.PHONY: clean clobber build lint fmt + +clean: + rm -v -rf -- .mypy_cache/ .venv/ + +clobber: clean + rm -v -rf -- .vars/ + +.venv/bin/python3: + python3 -m venv -- .venv + +define PYDEPS +from itertools import chain +from os import execl +from sys import executable + +from tomli import load + +toml = load(open("pyproject.toml", "rb")) + +project = toml["project"] +execl( + executable, + executable, + "-m", + "pip", + "install", + "--upgrade", + "--", + *project.get("dependencies", ()), + *chain.from_iterable(project["optional-dependencies"].values()), +) +endef + +.venv/bin/mypy: .venv/bin/python3 + '$<' -m pip install --requirement requirements.txt -- tomli + '$<' <<< '$(PYDEPS)' + +lint: .venv/bin/mypy + '$<' -- . + +build: .venv/bin/mypy + .venv/bin/python3 -- ci/prepare.py + +fmt: .venv/bin/mypy + .venv/bin/isort --profile=black --gitignore -- . + .venv/bin/black -- . diff --git a/config/neovim/store/lazy-plugins/chadtree/README.md b/config/neovim/store/lazy-plugins/chadtree/README.md new file mode 100644 index 00000000..54338f09 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/README.md @@ -0,0 +1,157 @@ +# [CHADTree](https://ms-jpq.github.io/chadtree) + +File Manager for Neovim, Better than NERDTree. + +## Features Illustrated + +**See full list of screen captures [here](https://github.com/ms-jpq/chadtree/tree/chad/docs/FEATURES.md)** + +### I like speed + +- **Parallel** Filesystem Scan + +- **[React Like](https://reactjs.org/docs/reconciliation.html)** Reconciling Difference Minimizing Rendering engine + +- **Never** blocks + +_You can read more about my [performance optimization](https://github.com/ms-jpq/chadtree/tree/chad/docs/ARCHITECTURE.md) here._ + +### I like power + +- Visual mode selections + +- Create, Copy, Paste, Delete, Rename, gotta do them all + +- Quickfix integration + +- [Bookmarks](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/bookmarks.png) + +![visual_select.gif](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/visual_select.gif) + +### I like 21st century + +- Filtering by glob + +- Follow mode + +- Session support (save open folders to disk, pick up where you left off) + +- Trash support (requires [`trash`](https://formulae.brew.sh/formula/trash) or [`trash-cli`](https://github.com/andreafrancia/trash-cli)) + +- `ls -l` statistics + +- Correct! handling of symlinks + +![filtering.gif](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/filtering.gif) + +### I like version control + +- Asynchronous parse git status (untracked, modified, staged) + +- Full support for git submodules + +![git.gif](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/git_showcase.gif) + +### I like colours + +- Full `$LS_COLOR` support! (shows same colours as unix `ls` & `tree` commands) + +- [Github coloured](https://github.com/github/linguist) icons (over 600 colours!) + +- Three different sets of icons out of the box + +- Four built-in themes - nord, solarized, trapdoor, vim-syntax + +![ls_colours.png](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/ls_colours.png) + +![github_colours.png](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/github_colours.png) + +### I like refinement + +- Maintain cursor position on relevant files even when during movements. + +- Maintain selection when copying, moving files + +- Mimetype warning (so you don't accidentally open an image) + +- Validating config parser **(notice, I added an extra `"dog"` param)** + +![mime warn.png](https://github.com/ms-jpq/chadtree/raw/chad/docs/img/mimetype.png) + +![schema error.png](https://github.com/ms-jpq/chadtree/raw/chad/docs/img/schema_error.png) + +### I like documentation + +- Built-in help command in a floating window! + +- Over 1000 lines of meticulous docs covering every option / function! + +**Use `:CHADhelp` to view [documentation](https://github.com/ms-jpq/chadtree/tree/chad/docs)** + +**Use `:CHADhelp --web` to open documentation in your browser!** (If you have one installed) + +## Install + +**Minimum version**: `python`: 3.8.2, `nvim`: `0.4.3`, make sure to have `virtualenv` installed (e.g.: `sudo apt install --yes -- python3-venv`) + +Install the usual way, ie. [VimPlug](https://github.com/junegunn/vim-plug), [Vundle](https://github.com/VundleVim/Vundle.vim), etc + +```vim +Plug 'ms-jpq/chadtree', {'branch': 'chad', 'do': 'python3 -m chadtree deps'} +``` + +You will have to run `:CHADdeps` when installing / updating. This will install CHADTree's dependencies locally inside `chadtree/.vars/runtime`. + +doing `rm -rf chadtree/` will cleanly remove everything CHADTree uses on your computer. + +## Usage + +To toggle CHADTree run command `:CHADopen`. Set it to a hotkey for convenience. + +```vimL +nnoremap v CHADopen +``` + +To see a list of hot keys: + +Either use `:CHADhelp keybind` or open in browser using [`:CHADhelp keybind --web`](https://github.com/ms-jpq/chadtree/tree/chad/docs/KEYBIND.md) + +### FAQ + +Q: Sometimes Windows will get stuck with CHADTree decorations when I do not want them to be, how do I resolve this? + +A: Run `:CHADrestore` + +### Recommendations + +Add a hotkey to clear quickfix list: + +```vimL +nnoremap l call setqflist([]) +``` + +## If you like this... + +Also check out + +- [`sad`](https://github.com/ms-jpq/sad), its a modern `sed` that does previews with syntax highlighting, and lets you pick and choose which chunks to edit. + +- [`coq.nvim`](https://github.com/ms-jpq/coq_nvim), it's a FAST AS FUCK completion client with shit tons of features. + +- [isomorphic-copy](https://github.com/ms-jpq/isomorphic-copy), it's a cross platform clipboard that is daemonless, and does not require third party support. + +## Special Thanks + +CHADTree does not define it's own colours beyond some minimal defaults, all themes are imported from other open source projects. + +> The base icons are imported from the [vim-devicon](https://github.com/ryanoasis/vim-devicons) + +> All emoji icons are imported from the [vim-emoji-icon-theme](https://github.com/adelarsq/vim-emoji-icon-theme) + +> Some themes are imported from [dircolors-solarized](https://github.com/seebi/dircolors-solarized) + +> Some themes are imported from [nord-dircolors](https://github.com/arcticicestudio/nord-dircolors) + +> Some themes are imported from [LS_COLORS](https://github.com/trapd00r/LS_COLORS) + +> Some themes are imported from [vim-nerdtree-syntax-highlight](https://github.com/tiagofumo/vim-nerdtree-syntax-highlight) diff --git a/config/neovim/store/lazy-plugins/chadtree/_config.yml b/config/neovim/store/lazy-plugins/chadtree/_config.yml new file mode 100644 index 00000000..6a42506f --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/_config.yml @@ -0,0 +1,7 @@ +--- +title: CHADTree + +showcase: True + +images: + - https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/visual_select.gif diff --git a/config/neovim/store/lazy-plugins/chadtree/artifacts/README.md b/config/neovim/store/lazy-plugins/chadtree/artifacts/README.md new file mode 100644 index 00000000..56e33c08 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/artifacts/README.md @@ -0,0 +1,3 @@ +# DO NOT EDIT + +These are code generated jsons. diff --git a/config/neovim/store/lazy-plugins/chadtree/artifacts/artifact.json b/config/neovim/store/lazy-plugins/chadtree/artifacts/artifact.json new file mode 100644 index 00000000..8cf4d557 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/artifacts/artifact.json @@ -0,0 +1,2310 @@ +{ + "icon_colours": { + "github": { + ".1": "#ecdebe", + ".1in": "#ecdebe", + ".1m": "#ecdebe", + ".1x": "#ecdebe", + ".2": "#ecdebe", + ".2da": "#38761D", + ".3": "#ecdebe", + ".3in": "#ecdebe", + ".3m": "#ecdebe", + ".3p": "#ecdebe", + ".3pm": "#ecdebe", + ".3qt": "#ecdebe", + ".3x": "#ecdebe", + ".4": "#ecdebe", + ".4DForm": "#292929", + ".4DProject": "#292929", + ".4dm": "#004289", + ".4gl": "#63408e", + ".4th": "#341708", + ".5": "#ecdebe", + ".6": "#ecdebe", + ".6pl": "#0000fb", + ".6pm": "#0000fb", + ".7": "#ecdebe", + ".8": "#ecdebe", + ".8xk": "#A0AA87", + ".8xk.txt": "#A0AA87", + ".8xp": "#A0AA87", + ".8xp.txt": "#A0AA87", + ".9": "#ecdebe", + ".Dsr": "#2c6353", + ".JSON-tmLanguage": "#292929", + ".OutJob": "#A89663", + ".PcbDoc": "#A89663", + ".PrjPCB": "#A89663", + ".SchDoc": "#A89663", + "._coffee": "#244776", + "._js": "#f1e05a", + "._ls": "#499886", + ".a51": "#6E4C13", + ".abap": "#E8274B", + ".ada": "#02f88c", + ".adb": "#02f88c", + ".adml": "#0060ac", + ".admx": "#0060ac", + ".ado": "#1a5f91", + ".adoc": "#73a0c5", + ".adp": "#e4cc98", + ".ads": "#02f88c", + ".afm": "#fa0f00", + ".agc": "#0B3D91", + ".agda": "#315665", + ".ahk": "#6594b9", + ".ahkl": "#6594b9", + ".aidl": "#34EB6B", + ".aj": "#a957b0", + ".al": "#0298c3", + ".als": "#64C800", + ".ampl": "#E6EFBB", + ".angelscript": "#C7D7DC", + ".anim": "#222c37", + ".ant": "#0060ac", + ".antlers.html": "#ff269e", + ".antlers.php": "#ff269e", + ".antlers.xml": "#ff269e", + ".apacheconf": "#d12127", + ".apib": "#2ACCA8", + ".apl": "#5A8164", + ".app": "#B83998", + ".app.src": "#B83998", + ".applescript": "#101F1F", + ".arc": "#aa2afe", + ".arr": "#ee1e10", + ".as": "#C7D7DC", + ".asax": "#9400ff", + ".asc": "#73a0c5", + ".asciidoc": "#73a0c5", + ".ascx": "#9400ff", + ".asd": "#3fb68b", + ".asddls": "#555e25", + ".ash": "#B9D9FF", + ".ashx": "#9400ff", + ".asm": "#005daa", + ".asmx": "#9400ff", + ".asp": "#6a40fd", + ".aspx": "#9400ff", + ".asset": "#222c37", + ".astro": "#ff5a03", + ".asy": "#ff0000", + ".au3": "#1C3552", + ".aug": "#9CC134", + ".auk": "#c30e9b", + ".aux": "#3D6117", + ".avdl": "#0040FF", + ".avsc": "#292929", + ".aw": "#4F5D95", + ".awk": "#c30e9b", + ".axaml": "#0060ac", + ".axd": "#9400ff", + ".axi": "#0aa0ff", + ".axi.erb": "#747faa", + ".axml": "#0060ac", + ".axs": "#0aa0ff", + ".axs.erb": "#747faa", + ".b": "#2F2530", + ".bal": "#FF5000", + ".bas": "#2c6353", + ".bash": "#89e051", + ".bat": "#C1F12E", + ".bats": "#89e051", + ".bb": "#db5855", + ".bbx": "#3D6117", + ".bdy": "#dad8d8", + ".be": "#15A13C", + ".bf": "#2F2530", + ".bi": "#141AC9", + ".bib": "#778899", + ".bibtex": "#778899", + ".bicep": "#519aba", + ".bison": "#6A463F", + ".blade": "#f7523f", + ".blade.php": "#f7523f", + ".bmx": "#cd6400", + ".bones": "#f1e05a", + ".boo": "#d4bec1", + ".boot": "#db5855", + ".bpl": "#c80fa0", + ".brd": "#2f4aab", + ".brs": "#662D91", + ".bs": "#66AABB", + ".bsl": "#814CCC", + ".bsv": "#12223c", + ".builder": "#701516", + ".builds": "#0060ac", + ".bzl": "#76d275", + ".c": "#555555", + ".c++": "#f34b7d", + ".cabal": "#483465", + ".cairo": "#ff4a48", + ".cake": "#244776", + ".capnp": "#c42727", + ".cats": "#555555", + ".cbx": "#3D6117", + ".cc": "#f34b7d", + ".ccproj": "#0060ac", + ".ccxml": "#0060ac", + ".cdc": "#00ef8b", + ".cdf": "#dd1100", + ".cds": "#0092d1", + ".ceylon": "#dfa535", + ".cfc": "#ed2cd6", + ".cfg": "#d1dbe0", + ".cfm": "#ed2cd6", + ".cfml": "#ed2cd6", + ".cgi": "#89e051", + ".cginc": "#aace60", + ".ch": "#403a40", + ".chpl": "#8dc63f", + ".circom": "#707575", + ".cirru": "#ccccff", + ".cjs": "#f1e05a", + ".cjsx": "#244776", + ".ck": "#3f8000", + ".cl": "#ed2e2d", + ".cl2": "#db5855", + ".clar": "#5546ff", + ".click": "#E4E6F3", + ".clixml": "#0060ac", + ".clj": "#db5855", + ".cljc": "#db5855", + ".cljs": "#db5855", + ".cljs.hl": "#db5855", + ".cljscm": "#db5855", + ".cljx": "#db5855", + ".clp": "#00A300", + ".cls": "#2c6353", + ".clw": "#db901e", + ".cmake": "#DA3434", + ".cmake.in": "#DA3434", + ".cmd": "#C1F12E", + ".cmp": "#d20b00", + ".cnc": "#D08CF2", + ".cnf": "#d1dbe0", + ".cocci": "#c94949", + ".code-snippets": "#292929", + ".code-workspace": "#292929", + ".coffee": "#244776", + ".coffee.md": "#244776", + ".command": "#89e051", + ".coq": "#d0b68c", + ".cp": "#B0CE4E", + ".cpp": "#f34b7d", + ".cppm": "#f34b7d", + ".cproject": "#0060ac", + ".cps": "#B0CE4E", + ".cql": "#e38c00", + ".cr": "#000100", + ".cs": "#596706", + ".csc": "#FF6800", + ".cscfg": "#0060ac", + ".csd": "#1a1a1a", + ".csdef": "#0060ac", + ".cshtml": "#512be4", + ".csl": "#0060ac", + ".cson": "#244776", + ".csproj": "#0060ac", + ".css": "#563d7c", + ".csv": "#237346", + ".csx": "#178600", + ".ct": "#0060ac", + ".ctl": "#2c6353", + ".ctp": "#4F5D95", + ".cts": "#3178c6", + ".cu": "#3A4E3A", + ".cue": "#5886E1", + ".cuh": "#3A4E3A", + ".curry": "#531242", + ".cwl": "#B5314C", + ".cxx": "#f34b7d", + ".cyp": "#34c0eb", + ".cypher": "#34c0eb", + ".d": "#427819", + ".d2": "#526ee8", + ".dae": "#F1A42B", + ".darcspatch": "#8eff23", + ".dart": "#00B4AB", + ".dats": "#1ac620", + ".db2": "#e38c00", + ".dcl": "#3F85AF", + ".ddl": "#e38c00", + ".decls": "#00FFAE", + ".depproj": "#0060ac", + ".dfm": "#E3F171", + ".dfy": "#FFEC25", + ".dhall": "#dfafff", + ".di": "#ba595e", + ".dita": "#0060ac", + ".ditamap": "#0060ac", + ".ditaval": "#0060ac", + ".djs": "#cca760", + ".dll.config": "#0060ac", + ".dlm": "#a3522f", + ".dm": "#447265", + ".do": "#1a5f91", + ".dockerfile": "#384d54", + ".dof": "#d1dbe0", + ".doh": "#1a5f91", + ".dot": "#2596be", + ".dotsettings": "#0060ac", + ".dpatch": "#8eff23", + ".dpr": "#E3F171", + ".druby": "#c7a938", + ".dsc": "#FBEE96", + ".dsp": "#c37240", + ".dtx": "#3D6117", + ".duby": "#c7a938", + ".dwl": "#003a52", + ".dyalog": "#5A8164", + ".dyl": "#6c616e", + ".dylan": "#6c616e", + ".e": "#FF790B", + ".eb": "#069406", + ".ebuild": "#9400ff", + ".ec": "#913960", + ".ecl": "#001d9d", + ".eclass": "#9400ff", + ".eclxml": "#8a1267", + ".ecr": "#2e1052", + ".ect": "#a91e50", + ".edgeql": "#31A7FF", + ".editorconfig": "#fff1f2", + ".eex": "#6e4a7e", + ".eh": "#913960", + ".ejs": "#a91e50", + ".ejs.t": "#a91e50", + ".el": "#c065db", + ".eliom": "#ef7a08", + ".eliomi": "#ef7a08", + ".elm": "#60B5CC", + ".elv": "#55BB55", + ".em": "#FFF4F3", + ".emacs": "#c065db", + ".emacs.desktop": "#c065db", + ".emberscript": "#FFF4F3", + ".env": "#e5d559", + ".epj": "#913960", + ".eps": "#da291c", + ".epsi": "#da291c", + ".eq": "#a78649", + ".erb": "#701516", + ".erb.deface": "#701516", + ".erl": "#B83998", + ".es": "#f1e05a", + ".es6": "#f1e05a", + ".escript": "#B83998", + ".esdl": "#31A7FF", + ".ex": "#FF790B", + ".exs": "#6e4a7e", + ".eye": "#701516", + ".f": "#4d41b1", + ".f03": "#4d41b1", + ".f08": "#4d41b1", + ".f77": "#4d41b1", + ".f90": "#4d41b1", + ".f95": "#4d41b1", + ".factor": "#636746", + ".fan": "#14253c", + ".fancypack": "#7b9db4", + ".fcgi": "#89e051", + ".feature": "#5B2063", + ".filters": "#0060ac", + ".fish": "#4aae47", + ".flex": "#DBCA00", + ".flf": "#FFDDBB", + ".flux": "#88ccff", + ".fnc": "#dad8d8", + ".fnl": "#fff3d7", + ".for": "#4d41b1", + ".forth": "#341708", + ".fp": "#5686a5", + ".fpp": "#4d41b1", + ".fr": "#00cafe", + ".frag": "#f1e05a", + ".frg": "#5686a5", + ".frm": "#2c6353", + ".frt": "#341708", + ".fs": "#5686a5", + ".fsh": "#5686a5", + ".fshader": "#5686a5", + ".fsi": "#b845fc", + ".fsproj": "#0060ac", + ".fst": "#572e30", + ".fsti": "#572e30", + ".fsx": "#b845fc", + ".fth": "#341708", + ".ftl": "#0050b2", + ".fun": "#dc566d", + ".fut": "#5f021f", + ".fx": "#aace60", + ".fxh": "#aace60", + ".fxml": "#0060ac", + ".fy": "#7b9db4", + ".g": "#0000cc", + ".g4": "#9DC3FF", + ".gaml": "#FFC766", + ".gap": "#0000cc", + ".gawk": "#c30e9b", + ".gbl": "#d20b00", + ".gbo": "#d20b00", + ".gbp": "#d20b00", + ".gbr": "#d20b00", + ".gbs": "#d20b00", + ".gco": "#D08CF2", + ".gcode": "#D08CF2", + ".gd": "#355570", + ".gdnlib": "#355570", + ".gdns": "#355570", + ".ged": "#003058", + ".gemspec": "#701516", + ".geo": "#5686a5", + ".geojson": "#292929", + ".geom": "#5686a5", + ".gf": "#ff0000", + ".gi": "#0000cc", + ".gitconfig": "#F44D27", + ".gitignore": "#000000", + ".gjs": "#F5835F", + ".gko": "#d20b00", + ".glade": "#0060ac", + ".gleam": "#ffaff3", + ".glf": "#c1ac7f", + ".glsl": "#5686a5", + ".glslf": "#5686a5", + ".glslv": "#5686a5", + ".gltf": "#292929", + ".gmi": "#ff6900", + ".gml": "#0060ac", + ".gms": "#f49a22", + ".gmx": "#0060ac", + ".gnu": "#f0a9f0", + ".gnuplot": "#f0a9f0", + ".go": "#00ADD8", + ".god": "#701516", + ".golo": "#88562A", + ".gp": "#f0a9f0", + ".gpb": "#d20b00", + ".gpt": "#d20b00", + ".gql": "#e10098", + ".grace": "#615f8b", + ".gradle": "#02303a", + ".gradle.kts": "#02303a", + ".graphql": "#e10098", + ".graphqls": "#e10098", + ".groovy": "#4298b8", + ".grt": "#4298b8", + ".grxml": "#0060ac", + ".gs": "#f1e05a", + ".gsc": "#FF6800", + ".gsh": "#FF6800", + ".gshader": "#5686a5", + ".gsp": "#4298b8", + ".gst": "#0060ac", + ".gsx": "#82937f", + ".gtl": "#d20b00", + ".gto": "#d20b00", + ".gtp": "#d20b00", + ".gtpl": "#4298b8", + ".gts": "#d20b00", + ".gv": "#2596be", + ".gvy": "#4298b8", + ".gyp": "#3572A5", + ".gypi": "#3572A5", + ".h": "#438eff", + ".h++": "#f34b7d", + ".hack": "#878787", + ".haml": "#ece2a9", + ".haml.deface": "#ece2a9", + ".handlebars": "#f7931e", + ".har": "#292929", + ".hats": "#1ac620", + ".hb": "#0e60e3", + ".hbs": "#f7931e", + ".hc": "#ffefaf", + ".hcl": "#844FBA", + ".hh": "#878787", + ".hhi": "#878787", + ".hic": "#db5855", + ".hlsl": "#aace60", + ".hlsli": "#aace60", + ".hocon": "#9ff8ee", + ".hoon": "#00b171", + ".hpp": "#f34b7d", + ".hqf": "#3F3F3F", + ".hql": "#dce200", + ".hrl": "#B83998", + ".hs": "#5e5086", + ".hs-boot": "#5e5086", + ".hsc": "#5e5086", + ".hta": "#e34c26", + ".htm": "#e34c26", + ".html": "#e34c26", + ".html.heex": "#6e4a7e", + ".html.hl": "#e34c26", + ".html.leex": "#6e4a7e", + ".http": "#005C9C", + ".hx": "#df7900", + ".hxml": "#f68712", + ".hxsl": "#df7900", + ".hxx": "#f34b7d", + ".hy": "#7790B2", + ".hzp": "#0060ac", + ".i": "#005daa", + ".i3": "#223388", + ".ice": "#003fa2", + ".iced": "#244776", + ".icl": "#3F85AF", + ".idc": "#555555", + ".idr": "#b30000", + ".ig": "#223388", + ".ihlp": "#1a5f91", + ".ijm": "#99AAFF", + ".ijs": "#9EEDFF", + ".ik": "#078193", + ".ily": "#9ccc7c", + ".imba": "#16cec6", + ".iml": "#0060ac", + ".inc": "#f69e1d", + ".ini": "#d1dbe0", + ".inl": "#f34b7d", + ".ino": "#f34b7d", + ".ins": "#3D6117", + ".intr": "#6c616e", + ".io": "#a9188d", + ".iol": "#843179", + ".ipf": "#0000cc", + ".ipp": "#f34b7d", + ".ipynb": "#DA5B0B", + ".isl": "#264b99", + ".iss": "#264b99", + ".iuml": "#fbbd16", + ".ivy": "#0060ac", + ".ixx": "#f34b7d", + ".j": "#ff0c5a", + ".j2": "#a52a22", + ".jade": "#a86454", + ".jake": "#f1e05a", + ".janet": "#0886a5", + ".jav": "#b07219", + ".java": "#b07219", + ".javascript": "#f1e05a", + ".jbuilder": "#701516", + ".jcl": "#d90e09", + ".jelly": "#0060ac", + ".jflex": "#DBCA00", + ".jinja": "#a52a22", + ".jinja2": "#a52a22", + ".jison": "#56b3cb", + ".jisonlex": "#56b3cb", + ".jl": "#a270ba", + ".jq": "#c7254e", + ".js": "#f1e05a", + ".js.erb": "#f1e05a", + ".jsb": "#f1e05a", + ".jscad": "#f1e05a", + ".jsfl": "#f1e05a", + ".jsh": "#b07219", + ".jslib": "#f1e05a", + ".jsm": "#f1e05a", + ".json": "#85ea2d", + ".json5": "#267CB9", + ".jsonc": "#292929", + ".jsonl": "#292929", + ".jsonld": "#0c479c", + ".jsonnet": "#0064bd", + ".jsp": "#2A6277", + ".jspre": "#f1e05a", + ".jsproj": "#0060ac", + ".jss": "#f1e05a", + ".jst": "#a91e50", + ".jsx": "#f1e05a", + ".kak": "#6f8042", + ".kicad_mod": "#2f4aab", + ".kicad_pcb": "#2f4aab", + ".kicad_sch": "#2f4aab", + ".kicad_wks": "#2f4aab", + ".kid": "#951531", + ".kml": "#0060ac", + ".kojo": "#c22d40", + ".krl": "#28430A", + ".ks": "#41adf0", + ".ksh": "#89e051", + ".ksy": "#773b37", + ".kt": "#A97BFF", + ".ktm": "#A97BFF", + ".kts": "#A97BFF", + ".kv": "#1da6e0", + ".l": "#ecdebe", + ".lagda": "#315665", + ".lark": "#2980B9", + ".las": "#999999", + ".lasso": "#999999", + ".lasso8": "#999999", + ".lasso9": "#999999", + ".latte": "#f2a542", + ".launch": "#0060ac", + ".lbx": "#3D6117", + ".lektorproject": "#d1dbe0", + ".less": "#1d365d", + ".lex": "#DBCA00", + ".lfe": "#4C3023", + ".lgt": "#295b9a", + ".lhs": "#5e5086", + ".libsonnet": "#0064bd", + ".lid": "#6c616e", + ".lidr": "#b30000", + ".ligo": "#0e74ff", + ".linq": "#178600", + ".liquid": "#67b8de", + ".lisp": "#87AED7", + ".litcoffee": "#244776", + ".livemd": "#083fa1", + ".lkml": "#652B81", + ".ll": "#185619", + ".lmi": "#3572A5", + ".logtalk": "#295b9a", + ".lol": "#cc9900", + ".lookml": "#652B81", + ".lpr": "#E3F171", + ".ls": "#499886", + ".lsl": "#3d9970", + ".lslp": "#3d9970", + ".lsp": "#87AED7", + ".ltx": "#3D6117", + ".lua": "#000080", + ".lvclass": "#fede06", + ".lvlib": "#fede06", + ".lvproj": "#fede06", + ".ly": "#9ccc7c", + ".m": "#438eff", + ".m2": "#d8ffff", + ".m3": "#223388", + ".ma": "#dd1100", + ".mak": "#427819", + ".make": "#427819", + ".makefile": "#427819", + ".mako": "#7e858d", + ".man": "#ecdebe", + ".mao": "#7e858d", + ".markdown": "#083fa1", + ".marko": "#42bff2", + ".mask": "#222c37", + ".mat": "#222c37", + ".mata": "#1a5f91", + ".matah": "#1a5f91", + ".mathematica": "#dd1100", + ".matlab": "#e16737", + ".mawk": "#c30e9b", + ".maxhelp": "#c4a79c", + ".maxpat": "#c4a79c", + ".maxproj": "#c4a79c", + ".mc": "#8D6747", + ".mcfunction": "#E22837", + ".mcmeta": "#292929", + ".mcr": "#00a6a6", + ".md": "#083fa1", + ".mdoc": "#ecdebe", + ".mdown": "#083fa1", + ".mdpolicy": "#0060ac", + ".mdwn": "#083fa1", + ".mdx": "#fcb32c", + ".me": "#ecdebe", + ".mediawiki": "#fc5757", + ".mermaid": "#ff3670", + ".meta": "#222c37", + ".metal": "#8f14e9", + ".mg": "#223388", + ".mint": "#02b046", + ".mir": "#cb171e", + ".mirah": "#c7a938", + ".mjml": "#0060ac", + ".mjs": "#f1e05a", + ".mk": "#427819", + ".mkd": "#083fa1", + ".mkdn": "#083fa1", + ".mkdown": "#083fa1", + ".mkfile": "#427819", + ".mkii": "#3D6117", + ".mkiv": "#3D6117", + ".mkvi": "#3D6117", + ".ml": "#dc566d", + ".ml4": "#ef7a08", + ".mli": "#ef7a08", + ".mligo": "#3be133", + ".mlir": "#5EC8DB", + ".mll": "#ef7a08", + ".mly": "#ef7a08", + ".mm": "#0060ac", + ".mmd": "#ff3670", + ".mo": "#fbb03b", + ".mod": "#0060ac", + ".moo": "#ff2b2b", + ".moon": "#ff4585", + ".move": "#4a137a", + ".mpl": "#21D789", + ".mps": "#21D789", + ".mq4": "#62A8D6", + ".mq5": "#4A76B8", + ".mqh": "#4A76B8", + ".mrc": "#3d57c3", + ".ms": "#ecdebe", + ".msd": "#21D789", + ".mspec": "#701516", + ".mt": "#dd1100", + ".mtml": "#b7e1f4", + ".mts": "#3178c6", + ".mu": "#244963", + ".mud": "#dc75e5", + ".mustache": "#724b3b", + ".mxml": "#0060ac", + ".mxt": "#c4a79c", + ".mysql": "#e38c00", + ".n": "#ecdebe", + ".nanorc": "#2d004d", + ".nas": "#1d2c4e", + ".nasm": "#6E4C13", + ".natvis": "#0060ac", + ".nawk": "#c30e9b", + ".nb": "#dd1100", + ".nbp": "#dd1100", + ".nc": "#94B0C7", + ".ncl": "#0060ac", + ".ndproj": "#0060ac", + ".ne": "#990000", + ".nearley": "#990000", + ".nf": "#3ac486", + ".nginx": "#009639", + ".nginxconf": "#009639", + ".nim": "#ffc200", + ".nim.cfg": "#ffc200", + ".nimble": "#ffc200", + ".nimrod": "#ffc200", + ".nims": "#ffc200", + ".nit": "#009917", + ".nix": "#7e7eff", + ".njk": "#3d8137", + ".njs": "#f1e05a", + ".nl": "#87AED7", + ".nlogo": "#ff6375", + ".nomad": "#844FBA", + ".nproj": "#0060ac", + ".nqp": "#0000fb", + ".nr": "#ecdebe", + ".nse": "#000080", + ".nss": "#111522", + ".nu": "#4E9906", + ".numpy": "#9C8AF9", + ".numpyw": "#9C8AF9", + ".numsc": "#9C8AF9", + ".nuspec": "#0060ac", + ".nut": "#800000", + ".ny": "#3fb68b", + ".odd": "#0060ac", + ".odin": "#60AFFE", + ".ol": "#843179", + ".omgrofl": "#cabbff", + ".ooc": "#b0b77e", + ".opal": "#f7ede0", + ".opencl": "#ed2e2d", + ".orc": "#1a1a1a", + ".org": "#77aa99", + ".os": "#814CCC", + ".osm": "#0060ac", + ".owl": "#5b70bd", + ".oxygene": "#cdd0e3", + ".oz": "#fab738", + ".p": "#5ce600", + ".p4": "#7055b5", + ".p6": "#0000fb", + ".p6l": "#0000fb", + ".p6m": "#0000fb", + ".p8": "#000080", + ".pac": "#f1e05a", + ".pact": "#F7A8B8", + ".pan": "#cc0000", + ".parrot": "#f3ca0a", + ".pas": "#E3F171", + ".pascal": "#E3F171", + ".pat": "#c4a79c", + ".pb": "#5a6986", + ".pbi": "#5a6986", + ".pbt": "#8f0f8d", + ".pck": "#dad8d8", + ".pcss": "#dc3a0c", + ".pd_lua": "#000080", + ".pddl": "#0d00ff", + ".pde": "#0096D8", + ".pegjs": "#234d6b", + ".pep": "#C76F5B", + ".per": "#d8df39", + ".perl": "#0298c3", + ".pfa": "#da291c", + ".pgsql": "#336790", + ".ph": "#0298c3", + ".php": "#4F5D95", + ".php3": "#4F5D95", + ".php4": "#4F5D95", + ".php5": "#4F5D95", + ".phps": "#4F5D95", + ".phpt": "#4F5D95", + ".phtml": "#4f5d95", + ".pig": "#fcd7de", + ".pike": "#005390", + ".pkb": "#dad8d8", + ".pkgproj": "#0060ac", + ".pks": "#dad8d8", + ".pl": "#0000fb", + ".pl6": "#0000fb", + ".plantuml": "#fbbd16", + ".plb": "#dad8d8", + ".plist": "#0060ac", + ".plot": "#f0a9f0", + ".pls": "#dad8d8", + ".plsql": "#dad8d8", + ".plt": "#74283c", + ".pluginspec": "#0060ac", + ".plx": "#0298c3", + ".pm": "#0000fb", + ".pm6": "#0000fb", + ".pml": "#de0000", + ".pmod": "#005390", + ".podsl": "#3fb68b", + ".podspec": "#701516", + ".pogo": "#d80074", + ".polar": "#ae81ff", + ".por": "#f8bd00", + ".postcss": "#dc3a0c", + ".pov": "#6bac65", + ".pp": "#302B6D", + ".pprx": "#d90e09", + ".praat": "#c8506d", + ".prawn": "#701516", + ".prc": "#e38c00", + ".prefab": "#222c37", + ".prefs": "#d1dbe0", + ".prg": "#403a40", + ".prisma": "#0c344b", + ".pro": "#74283c", + ".proj": "#0060ac", + ".prolog": "#74283c", + ".properties": "#2A6277", + ".props": "#0060ac", + ".prw": "#403a40", + ".ps": "#da291c", + ".ps1": "#012456", + ".ps1xml": "#0060ac", + ".psc": "#6600cc", + ".psc1": "#0060ac", + ".psd1": "#012456", + ".psgi": "#0298c3", + ".psm1": "#012456", + ".pt": "#0060ac", + ".pug": "#a86454", + ".puml": "#fbbd16", + ".purs": "#1D222D", + ".pwn": "#dbb284", + ".pxd": "#fedf5b", + ".pxi": "#fedf5b", + ".py": "#3572A5", + ".py3": "#3572A5", + ".pyde": "#3572A5", + ".pyi": "#3572A5", + ".pyp": "#3572A5", + ".pyt": "#3572A5", + ".pytb": "#3572A5", + ".pyw": "#3572A5", + ".pyx": "#fedf5b", + ".q": "#0040cd", + ".qasm": "#AA70FF", + ".qbs": "#44a51c", + ".qhelp": "#0060ac", + ".ql": "#140f46", + ".qll": "#140f46", + ".qmd": "#198ce7", + ".qml": "#44a51c", + ".qs": "#00b841", + ".r": "#FFDAB3", + ".r2": "#358a5b", + ".r3": "#358a5b", + ".rabl": "#701516", + ".rake": "#701516", + ".raku": "#0000fb", + ".rakumod": "#0000fb", + ".raml": "#77d9fb", + ".razor": "#512be4", + ".rb": "#701516", + ".rbi": "#701516", + ".rbs": "#701516", + ".rbuild": "#701516", + ".rbw": "#701516", + ".rbx": "#701516", + ".rbxs": "#000080", + ".rchit": "#5686a5", + ".rd": "#198CE7", + ".rdf": "#0060ac", + ".rdoc": "#701516", + ".re": "#ff5847", + ".reb": "#358a5b", + ".rebol": "#358a5b", + ".red": "#f50000", + ".reds": "#f50000", + ".reek": "#cb171e", + ".reg": "#52d5ff", + ".regex": "#009a00", + ".regexp": "#009a00", + ".rego": "#7d9199", + ".rei": "#ff5847", + ".religo": "#ff5847", + ".res": "#0060ac", + ".rest": "#141414", + ".rest.txt": "#141414", + ".resx": "#0060ac", + ".rex": "#d90e09", + ".rexx": "#d90e09", + ".rg": "#cc0088", + ".rhtml": "#701516", + ".ring": "#2D54CB", + ".riot": "#A71E49", + ".rkt": "#3c5caa", + ".rktd": "#3c5caa", + ".rktl": "#3c5caa", + ".rl": "#9d5200", + ".rmd": "#198ce7", + ".rmiss": "#5686a5", + ".rnh": "#665a4e", + ".rno": "#ecdebe", + ".rnw": "#198ce7", + ".robot": "#00c0b5", + ".rockspec": "#000080", + ".roff": "#ecdebe", + ".ronn": "#083fa1", + ".rpgle": "#2BDE21", + ".rpy": "#ff7f7f", + ".rq": "#0C4597", + ".rs": "#0060ac", + ".rs.in": "#dea584", + ".rsc": "#DE3941", + ".rss": "#0060ac", + ".rst": "#141414", + ".rst.txt": "#141414", + ".rsx": "#198CE7", + ".ru": "#701516", + ".ruby": "#701516", + ".rviz": "#cb171e", + ".s": "#005daa", + ".sas": "#B34936", + ".sass": "#a53b70", + ".sats": "#1ac620", + ".sbt": "#c22d40", + ".sc": "#46390b", + ".scad": "#e5cd45", + ".scala": "#c22d40", + ".scaml": "#bd181a", + ".scd": "#46390b", + ".sce": "#ca0f21", + ".scenic": "#fdc700", + ".sch": "#0060ac", + ".sci": "#ca0f21", + ".scm": "#1e4aec", + ".sco": "#1a1a1a", + ".scpt": "#101F1F", + ".scrbl": "#3c5caa", + ".scss": "#c6538c", + ".scxml": "#0060ac", + ".sdc": "#e4cc98", + ".sed": "#64b970", + ".self": "#0579aa", + ".sexp": "#3fb68b", + ".sfproj": "#0060ac", + ".sfv": "#C9BFED", + ".sh": "#89e051", + ".sh.in": "#89e051", + ".shader": "#222c37", + ".shen": "#120F14", + ".shproj": "#0060ac", + ".sig": "#dc566d", + ".sj": "#ff0c5a", + ".sjs": "#f1e05a", + ".sl": "#007eff", + ".sld": "#1e4aec", + ".slim": "#2b2b2b", + ".sls": "#1e4aec", + ".sma": "#dbb284", + ".smithy": "#c44536", + ".smk": "#419179", + ".sml": "#dc566d", + ".snakefile": "#419179", + ".snap": "#15c213", + ".snip": "#199f4b", + ".snippet": "#199f4b", + ".snippets": "#199f4b", + ".sol": "#AA6746", + ".soy": "#0d948f", + ".sp": "#f69e1d", + ".sparql": "#0C4597", + ".spc": "#dad8d8", + ".spec": "#701516", + ".spin": "#7fa2a7", + ".sps": "#1e4aec", + ".sqf": "#3F3F3F", + ".sql": "#e38c00", + ".sqlrpgle": "#2BDE21", + ".sra": "#8f0f8d", + ".srdf": "#0060ac", + ".srt": "#9e0101", + ".sru": "#8f0f8d", + ".srw": "#8f0f8d", + ".ss": "#1e4aec", + ".ssjs": "#f1e05a", + ".sss": "#2fcc9f", + ".st": "#3fb34f", + ".stTheme": "#0060ac", + ".stan": "#b2011d", + ".star": "#76d275", + ".sthlp": "#1a5f91", + ".stl": "#373b5e", + ".story": "#5B2063", + ".storyboard": "#0060ac", + ".sty": "#3D6117", + ".styl": "#ff6347", + ".sublime-build": "#292929", + ".sublime-commands": "#292929", + ".sublime-completions": "#292929", + ".sublime-keymap": "#292929", + ".sublime-macro": "#292929", + ".sublime-menu": "#292929", + ".sublime-mousemap": "#292929", + ".sublime-project": "#292929", + ".sublime-settings": "#292929", + ".sublime-snippet": "#0060ac", + ".sublime-syntax": "#cb171e", + ".sublime-theme": "#292929", + ".sublime-workspace": "#292929", + ".sublime_metrics": "#292929", + ".sublime_session": "#292929", + ".sv": "#DAE1C2", + ".svelte": "#ff3e00", + ".svg": "#ff9900", + ".svh": "#DAE1C2", + ".sw": "#0060ac", + ".swift": "#F05138", + ".syntax": "#cb171e", + ".t": "#cf142b", + ".tab": "#e38c00", + ".tac": "#3572A5", + ".tag": "#2A6277", + ".talon": "#333333", + ".targets": "#0060ac", + ".tcc": "#f34b7d", + ".tcl": "#e4cc98", + ".tcl.in": "#e4cc98", + ".tesc": "#5686a5", + ".tese": "#5686a5", + ".tex": "#3D6117", + ".textile": "#ffe7ac", + ".tf": "#844FBA", + ".tfstate": "#292929", + ".tfstate.backup": "#292929", + ".tftpl": "#7b42bb", + ".tfvars": "#844FBA", + ".thor": "#701516", + ".thrift": "#D12127", + ".thy": "#FEFE00", + ".tla": "#4b0079", + ".tlv": "#C40023", + ".tm": "#e4cc98", + ".tmCommand": "#0060ac", + ".tmLanguage": "#0060ac", + ".tmPreferences": "#0060ac", + ".tmSnippet": "#0060ac", + ".tmTheme": "#0060ac", + ".tmac": "#ecdebe", + ".tml": "#0060ac", + ".tmux": "#89e051", + ".toc": "#f7e43f", + ".toit": "#c2c9fb", + ".toml": "#9c4221", + ".tool": "#89e051", + ".topojson": "#292929", + ".tpb": "#dad8d8", + ".tpl": "#f0c040", + ".tpp": "#f34b7d", + ".tps": "#dad8d8", + ".tres": "#355570", + ".trg": "#dad8d8", + ".trigger": "#89e051", + ".ts": "#0060ac", + ".tscn": "#355570", + ".tst": "#ca0f21", + ".tsv": "#237346", + ".tsx": "#0060ac", + ".tu": "#cf142b", + ".twig": "#c1d026", + ".txl": "#0178b8", + ".txt": "#199f4b", + ".txx": "#f34b7d", + ".typ": "#0060ac", + ".uc": "#a54c4d", + ".udf": "#e38c00", + ".udo": "#1a1a1a", + ".ui": "#0060ac", + ".unity": "#222c37", + ".uno": "#9933cc", + ".upc": "#4e3617", + ".ur": "#ccccee", + ".urdf": "#0060ac", + ".url": "#d1dbe0", + ".urs": "#ccccee", + ".ux": "#0060ac", + ".v": "#b2b7f8", + ".vala": "#a56de2", + ".vapi": "#a56de2", + ".vark": "#82937f", + ".vb": "#945db7", + ".vba": "#199f4b", + ".vbhtml": "#945db7", + ".vbproj": "#0060ac", + ".vbs": "#15dcdc", + ".vcl": "#148AA8", + ".vcxproj": "#0060ac", + ".vdf": "#f26025", + ".veo": "#b2b7f8", + ".vert": "#5686a5", + ".vh": "#DAE1C2", + ".vhd": "#adb2cb", + ".vhdl": "#adb2cb", + ".vhf": "#adb2cb", + ".vhi": "#adb2cb", + ".vho": "#adb2cb", + ".vhost": "#009639", + ".vhs": "#adb2cb", + ".vht": "#adb2cb", + ".vhw": "#adb2cb", + ".vim": "#199f4b", + ".vimrc": "#199f4b", + ".viw": "#e38c00", + ".vmb": "#199f4b", + ".volt": "#1F1F1F", + ".vrx": "#5686a5", + ".vs": "#5686a5", + ".vsh": "#5686a5", + ".vshader": "#5686a5", + ".vsixmanifest": "#0060ac", + ".vssettings": "#0060ac", + ".vstemplate": "#0060ac", + ".vtl": "#507cff", + ".vue": "#41b883", + ".vw": "#dad8d8", + ".vxml": "#0060ac", + ".vy": "#2980b9", + ".w": "#5ce600", + ".wast": "#04133b", + ".wat": "#04133b", + ".watchr": "#701516", + ".wdl": "#42f1f4", + ".webapp": "#292929", + ".webmanifest": "#292929", + ".wgsl": "#1a5e9a", + ".whiley": "#d5c397", + ".wiki": "#fc5757", + ".wikitext": "#fc5757", + ".wisp": "#7582D1", + ".wit": "#6250e7", + ".wixproj": "#0060ac", + ".wl": "#dd1100", + ".wlk": "#a23738", + ".wlt": "#dd1100", + ".wlua": "#000080", + ".workbook": "#083fa1", + ".workflow": "#0060ac", + ".wren": "#383838", + ".ws": "#ff0000", + ".wsdl": "#0060ac", + ".wsf": "#0060ac", + ".wsgi": "#3572A5", + ".wxi": "#0060ac", + ".wxl": "#0060ac", + ".wxs": "#0060ac", + ".x": "#aace60", + ".x10": "#4B6BEF", + ".x3d": "#0060ac", + ".x68": "#005daa", + ".xacro": "#0060ac", + ".xaml": "#0060ac", + ".xc": "#99DA07", + ".xdc": "#e4cc98", + ".xht": "#e34c26", + ".xhtml": "#e34c26", + ".xib": "#0060ac", + ".xlf": "#0060ac", + ".xliff": "#0060ac", + ".xmi": "#0060ac", + ".xml": "#0060ac", + ".xml.dist": "#0060ac", + ".xmp": "#0060ac", + ".xojo_code": "#81bd41", + ".xojo_menu": "#81bd41", + ".xojo_report": "#81bd41", + ".xojo_script": "#81bd41", + ".xojo_toolbar": "#81bd41", + ".xojo_window": "#81bd41", + ".xproj": "#0060ac", + ".xpy": "#3572A5", + ".xq": "#5232e7", + ".xql": "#5232e7", + ".xqm": "#5232e7", + ".xquery": "#5232e7", + ".xqy": "#5232e7", + ".xrl": "#B83998", + ".xsd": "#0060ac", + ".xsh": "#285EEF", + ".xsjs": "#f1e05a", + ".xsjslib": "#f1e05a", + ".xsl": "#EB8CEB", + ".xslt": "#EB8CEB", + ".xspec": "#0060ac", + ".xtend": "#24255d", + ".xul": "#0060ac", + ".xzap": "#0d665e", + ".y": "#4B6C4B", + ".yacc": "#4B6C4B", + ".yaml": "#cb171e", + ".yaml-tmlanguage": "#cb171e", + ".yaml.sed": "#cb171e", + ".yap": "#74283c", + ".yar": "#220000", + ".yara": "#220000", + ".yasnippet": "#32AB90", + ".yml": "#cb171e", + ".yml.mysql": "#cb171e", + ".yrl": "#B83998", + ".yul": "#794932", + ".yy": "#4B6C4B", + ".yyp": "#292929", + ".zap": "#0d665e", + ".zcml": "#0060ac", + ".zep": "#118f9e", + ".zig": "#ec915c", + ".zil": "#dc75e5", + ".zimpl": "#d67711", + ".zmpl": "#d67711", + ".zpl": "#d67711", + ".zs": "#00BCD1", + ".zsh": "#89e051", + ".zsh-theme": "#89e051" + } + }, + "icons": { + "ascii": { + "default_icon": "●", + "ext_exact": {}, + "folder": { + "closed": "▶", + "open": "▼" + }, + "link": { + "broken": "-/->", + "normal": "->" + }, + "name_exact": {}, + "name_glob": {}, + "status": { + "active": ">", + "inactive": " ", + "not_selected": " ", + "selected": "*" + } + }, + "ascii_hollow": { + "default_icon": "○", + "ext_exact": {}, + "folder": { + "closed": "▷", + "open": "▽" + }, + "link": { + "broken": "-/->", + "normal": "->" + }, + "name_exact": {}, + "name_glob": {}, + "status": { + "active": ">", + "inactive": " ", + "not_selected": " ", + "selected": "*" + } + }, + "devicons": { + "default_icon": "", + "ext_exact": { + ".ai": "", + ".awk": "", + ".bash": "", + ".bat": "", + ".bmp": "", + ".c": "", + ".c++": "", + ".cc": "", + ".clj": "", + ".cljc": "", + ".cljs": "", + ".coffee": "", + ".conf": "", + ".cp": "", + ".cpp": "", + ".cs": "", + ".csh": "", + ".css": "", + ".cxx": "", + ".d": "", + ".dart": "", + ".db": "", + ".diff": "", + ".dump": "", + ".edn": "", + ".eex": "", + ".ejs": "", + ".elm": "", + ".erl": "", + ".ex": "", + ".exs": "", + ".f#": "", + ".fish": "", + ".fs": "", + ".fsi": "", + ".fsscript": "", + ".fsx": "", + ".gemspec": "", + ".gif": "", + ".go": "", + ".h": "", + ".haml": "", + ".hbs": "", + ".heex": "", + ".hh": "", + ".hpp": "", + ".hrl": "", + ".hs": "", + ".htm": "", + ".html": "", + ".hxx": "", + ".ico": "", + ".ini": "", + ".java": "", + ".jl": "", + ".jpeg": "", + ".jpg": "", + ".js": "", + ".json": "", + ".jsx": "", + ".ksh": "", + ".leex": "", + ".less": "", + ".lhs": "", + ".lua": "", + ".markdown": "", + ".md": "", + ".mdx": "", + ".mjs": "", + ".mk": "", + ".ml": "λ", + ".mli": "λ", + ".mustache": "", + ".nix": "", + ".pem": "", + ".php": "", + ".pl": "", + ".pm": "", + ".png": "", + ".pp": "", + ".ps1": "", + ".psb": "", + ".psd": "", + ".py": "", + ".pyc": "", + ".pyd": "", + ".pyo": "", + ".r": "ﳒ", + ".rake": "", + ".rb": "", + ".rlib": "", + ".rmd": "", + ".rproj": "鉶", + ".rs": "", + ".rss": "", + ".sass": "", + ".scala": "", + ".scss": "", + ".sh": "", + ".slim": "", + ".sln": "", + ".sol": "ﲹ", + ".sql": "", + ".styl": "", + ".suo": "", + ".swift": "", + ".t": "", + ".tex": "ﭨ", + ".toml": "", + ".ts": "", + ".tsx": "", + ".twig": "", + ".vim": "", + ".vue": "﵂", + ".webmanifest": "", + ".webp": "", + ".xcplayground": "", + ".xul": "", + ".yaml": "", + ".yml": "", + ".zsh": "" + }, + "folder": { + "closed": "", + "open": "" + }, + "link": { + "broken": "󰌸", + "normal": "󰲔" + }, + "name_exact": { + ".bashprofile": "", + ".bashrc": "", + ".ds_store": "", + ".gitattributes": "", + ".gitconfig": "", + ".gitignore": "", + ".gitlab-ci.yml": "", + ".gvimrc": "", + ".vimrc": "", + ".zprofile": "", + ".zshenv": "", + ".zshrc": "", + "_gvimrc": "", + "_vimrc": "", + "cmakelists.txt": "", + "config.ru": "", + "docker-compose.yml": "", + "dockerfile": "", + "dropbox": "", + "exact-match-case-sensitive-1.txt": "1", + "exact-match-case-sensitive-2": "2", + "favicon.ico": "", + "gemfile": "", + "gruntfile.coffee": "", + "gruntfile.js": "", + "gruntfile.ls": "", + "gulpfile.coffee": "", + "gulpfile.js": "", + "gulpfile.ls": "", + "license": "", + "makefile": "", + "mix.lock": "", + "node_modules": "", + "procfile": "", + "rakefile": "", + "react.jsx": "", + "robots.txt": "ﮧ" + }, + "name_glob": { + ".*angular.*.js": "", + ".*backbone.*.js": "", + ".*jquery.*.js": "", + ".*materialize.*.css": "", + ".*materialize.*.js": "", + ".*mootools.*.js": "", + ".*require.*.js": "", + ".*vimrc.*": "", + "Vagrantfile": "" + }, + "status": { + "active": "▶", + "inactive": " ", + "not_selected": " ", + "selected": "✸" + } + }, + "emoji": { + "default_icon": "📄", + "ext_exact": { + ".3ds": "🦖", + ".5vw": "🔬", + ".60": "🦋", + ".7z": "📦", + ".a80": "🧩", + ".aar": "📦", + ".acp": "🔬", + ".ahk": "🔨", + ".ai": "🎨", + ".alda": "🎶", + ".ani": "🦖", + ".apc": "🔬", + ".apk": "📦", + ".arb": "🔶", + ".as": "👼🏻", + ".asm": "🧩", + ".asta": "🔀", + ".astro": "🔶", + ".atc": "🔬", + ".atom": "💢", + ".avi": "🎞 ", + ".axml": "💢", + ".bat": "🦇", + ".bb": "🐚", + ".beam": "🔟", + ".bf": "🥩", + ".bfr": "🔬", + ".bib": "📜", + ".bin": "🔟", + ".blend": "🦖", + ".blob": "🔟", + ".bmp": "🎨", + ".bpg": "🎨", + ".bsdl": "🅱️ ", + ".bsl": "🐲", + ".c": "🐮", + ".cap": "🔬", + ".carbon": "🐮", + ".carp": "🎏", + ".cbl": "⚙️ ", + ".cer": "🔑", + ".cff": "📜", + ".cfg": "🔨", + ".chs": "🦥", + ".cjs": "🔶", + ".class": "🔟", + ".clj": "🦚", + ".cmd": "🦇", + ".cmi": "🔟", + ".coffee": "☕️", + ".conf": "🔨", + ".config": "🔨", + ".cpp": "🐮", + ".cql": "🎲", + ".cr": "💎", + ".crt": "🔑", + ".cs": "☪️ ", + ".cshtml": "☪️ ", + ".csproj": "🔨", + ".css": "🌸", + ".csv": "📊", + ".csx": "☪️ ", + ".cu": "🦖", + ".cuh": "🦖", + ".d": "🆔", + ".dae": "🦖", + ".dart": "🎯", + ".dat": "🔟", + ".data": "🔟", + ".db": "🎲", + ".dds": "🎨", + ".deb": "📦", + ".dib": "📝", + ".diff": "💊", + ".dll": "🔟", + ".dmg": "📦", + ".doc": "📘", + ".docx": "📘", + ".dpk": "🥏", + ".dproj": "🥏", + ".drc": "🎞 ", + ".drcs": "🎞 ", + ".dtd": "💢", + ".duel": "⚔️ ", + ".dump": "💣", + ".dxf": "🦖", + ".dylib": "🔟", + ".ear": "📦", + ".ebnf": "🐲", + ".eex": "💧", + ".elm": "🔰", + ".elmi": "🔟", + ".elmo": "🔟", + ".enc": "🔬", + ".eot": "🀄️", + ".epp": "👻", + ".epub": "📙", + ".erb": "🔻", + ".erf": "🔬", + ".erl": "📡", + ".erm": "💢", + ".erwin": "🎲", + ".etlua": "🌏", + ".ex": "💧", + ".exe": "🚀", + ".exs": "💧", + ".ez": "📦", + ".fbx": "🦖", + ".fdc": "🔬", + ".fish": "🐚", + ".flr": "🎆", + ".fnl": "🌱", + ".frxml": "💢", + ".fs": "🔷", + ".fsi": "🔷", + ".fsl": "🔷", + ".fsproj": "🔨", + ".fsx": "🔷", + ".fsy": "🔷", + ".functions": "🐚", + ".fxml": "💢", + ".gdb": "🐞", + ".gif": "🎨", + ".gift": "📝", + ".git": "🐙", + ".glb": "🦖", + ".gleam": "✨", + ".glsl": "🦖", + ".gltf": "🦖", + ".go": "🌰", + ".gql": "🏀", + ".gr": "🌾", + ".gradle": "🐘", + ".graphql": "🏀", + ".graphqls": "🏀", + ".gravity": "🔵", + ".groovy": "🌟", + ".gvdesign": "🔀", + ".gz": "📦", + ".gzip": "📦", + ".h": "🧩", + ".hbs": "🈴", + ".hdr": "🎨", + ".heex": "🌏", + ".hive": "🔟", + ".hs": "🦥", + ".hs-boot": "🦥", + ".hsc": "🦥", + ".hsig": "🦥", + ".hss": "🌸", + ".htm": "🌏", + ".html": "🌏", + ".http": "🌐", + ".hvm": "🦥", + ".hx": "🏀", + ".icns": "🎨", + ".ico": "🎨", + ".ics": "⏳", + ".idl": "🧩", + ".imba": "🔶", + ".iml": "🔨", + ".inc": "🧩", + ".info": "📜", + ".ini": "🔨", + ".ino": "📟", + ".ipa": "📦", + ".ipfix": "🔬", + ".ipynb": "📝", + ".iso": "💿", + ".jade": "🈴", + ".jar": "📦", + ".jasper": "💹", + ".java": "☕️", + ".jenkinsfile": "🎩", + ".jks": "🔑", + ".jl": "🍡", + ".jmx": "💢", + ".jnlp": "💢", + ".jpeg": "🎨", + ".jpg": "🎨", + ".jrxml": "💢", + ".js": "🔶", + ".json": "🔶", + ".jsx": "🐟", + ".jws": "🔨", + ".jxl": "🎨", + ".key": "🔑", + ".kind": "🔶", + ".kind2": "🔶", + ".kt": "🗼", + ".kts": "🗼", + ".latex": "📜", + ".lc": "🦥", + ".lcap": "🔬", + ".leex": "💧", + ".less": "🌸", + ".lhs": "🦥", + ".lis": "🧩", + ".list": "🧩", + ".lock": "🔐", + ".log": "🦎", + ".lua": "🌕", + ".lz4": "📦", + ".m": "🐄", + ".mak": "🐃", + ".map": "🌐", + ".markdown": "⬇️ ", + ".marko": "⬇️ ", + ".md": "⬇️ ", + ".mdj": "🔀", + ".mdl": "🦖", + ".mint": "🍃", + ".mjs": "🔶", + ".mk": "🐃", + ".mkd": "⬇️ ", + ".mkv": "🎞 ", + ".ml": "🐫", + ".mli": "🧩", + ".mlt": "🎞 ", + ".mm": "🐄", + ".mo": "🗽", + ".mod": "🔨", + ".moon": "🌕", + ".mov": "🎞 ", + ".mp3": "🎶", + ".mp4": "🎞 ", + ".mpeg": "🎞 ", + ".mpg": "🎞 ", + ".mplog": "🔬", + ".mq4": "💰", + ".mq5": "💰", + ".mqlh": "🧩", + ".nelua": "🌕", + ".nib": "🧩", + ".nim": "👑", + ".norg": "🦄", + ".nupkg": "📦", + ".nuspec": "💢", + ".obj": "🦖", + ".odin": "🔵", + ".odp": "🎭", + ".ods": "📊", + ".odt": "📘", + ".ogg": "🎶", + ".org": "🦄", + ".os": "🐲", + ".out": "🔬", + ".ovpn": "🔑", + ".pages": "📘", + ".parquet": "🟫", + ".pas": "🌡 ", + ".pcap": "🔬", + ".pcapng": "🔬", + ".pdb": "🔟", + ".pde": "📟", + ".pdf": "🅿️ ", + ".pem": "🔑", + ".pepk": "🔑", + ".pfx": "🔑", + ".pgsql": "🎲", + ".php": "🐘", + ".pid": "🔟", + ".pkg": "📦", + ".pkl": "🔨", + ".pklg": "🔬", + ".pkt": "🔬", + ".pl": "🍐", + ".plist": "💢", + ".png": "🎨", + ".po": "🗽", + ".pony": "🐴", + ".pot": "🗽", + ".pp": "👻", + ".ppk": "🔑", + ".ppt": "🎭", + ".pptx": "🎭", + ".prefs": "🔨", + ".prisma": "🔷", + ".pro": "🔨", + ".profile": "👤", + ".properties": "🔨", + ".props": "💢", + ".proto": "⚛️ ", + ".ps1": "🐚", + ".psb": "🎨", + ".psd": "🎨", + ".psl": "🖌 ", + ".pug": "🐶", + ".puml": "🔀", + ".py": "🐍", + ".pyc": "🐍", + ".pyd": "🐍", + ".pyo": "🐍", + ".python": "🐍", + ".r": "🧮", + ".rar": "📦", + ".razor": "🟣", + ".rb": "🔻", + ".rc": "🥏", + ".re": "🐫", + ".reality": "🦖", + ".reg": "🔐", + ".rei": "🧩", + ".res": "🐫", + ".resi": "🧩", + ".rest": "🌐", + ".resx": "💢", + ".rf5": "🔬", + ".rh": "🐚", + ".rlib": "🔟", + ".rmd": "🎭", + ".rock": "🌕", + ".rockspec": "🌕", + ".rpm": "📦", + ".rproj": "🔨", + ".rs": "🦀", + ".rss": "💢", + ".rst": "🍇", + ".ru": "🔻", + ".s": "🧩", + ".sar": "📦", + ".sass": "👓", + ".sbt": "🔨", + ".sc": "💈", + ".scala": "💈", + ".scd": "🎶", + ".scm": "🌻", + ".scss": "🌸", + ".sfb": "🦖", + ".sfz": "🎶", + ".sh": "🐚", + ".slime": "🍨", + ".sln": "🔨", + ".snap": "📦", + ".snoop": "🔬", + ".so": "🔟", + ".socket": "🌐", + ".sol": "🐲", + ".spi": "🌀", + ".spiproj": "🌀", + ".spir": "🌀", + ".sql": "🎲", + ".sqlite": "🎲", + ".storyboard": "💢", + ".strings": "💢", + ".styl": "🌸", + ".suo": "☪️ ", + ".svelte": "💃🏻", + ".svg": "🎨", + ".swift": "🐦", + ".swo": "⏳", + ".swp": "⏳", + ".syc": "🔬", + ".t": "🌷", + ".tar": "📦", + ".tera": "🌏", + ".tflite": "🤖", + ".tga": "🎨", + ".tgz": "📦", + ".tif": "🎨", + ".tiff": "🀄️", + ".tlb": "🧩", + ".toml": "🔨", + ".tpc": "🔬", + ".tpl": "🈴", + ".tr1": "🔬", + ".trace": "🔬", + ".trc": "🔬", + ".ts": "🔷", + ".tsx": "🔷", + ".ttf": "🀄️", + ".tup": "🐃", + ".txt": "📝", + ".uml": "🔀", + ".usd": "🦖", + ".usda": "🦖", + ".usdc": "🦖", + ".usdz": "🦖", + ".userprefs": "🔨", + ".v": "✅", + ".val": "✔️", + ".vbs": "🌀", + ".vcxproj": "💢", + ".vdf": "🔨", + ".vgb": "🔨", + ".vh": "🧩", + ".vim": "🍃", + ".vimrc": "🍃", + ".vimspec": "🍃", + ".vmb": "📦", + ".vpp": "🔀", + ".vrx": "🦖", + ".vsh": "✅", + ".vue": "🌲", + ".vv": "✅", + ".vwr": "🔬", + ".wad": "🔟", + ".war": "📦", + ".wasm": "🔟", + ".wast": "🔟", + ".wat": "🔟", + ".wav": "🎶", + ".wbmp": "🎨", + ".webp": "🎨", + ".woff": "🀄️", + ".wpc": "🔬", + ".wpz": "🔬", + ".wscene": "🎞 ", + ".wsdl": "💢", + ".xaml": "💢", + ".xcf": "🎨", + ".xdata": "💢", + ".xhtml": "🌏", + ".xib": "💢", + ".xjb": "💢", + ".xlf": "💢", + ".xls": "📊", + ".xlsx": "📊", + ".xml": "💢", + ".xsd": "💢", + ".xsl": "💢", + ".xz": "📦", + ".yaml": "🎴", + ".yml": "🎴", + ".z80": "🧩", + ".zig": "⚡️", + ".zip": "📦", + ".zsh": "🐚", + ".zst": "📦" + }, + "folder": { + "closed": "📁", + "open": "📂" + }, + "link": { + "broken": "󰌸", + "normal": "󰲔" + }, + "name_exact": { + ".bashprofile": "🐚", + ".bashrc": "🐚", + ".cabal": "🔨", + ".dart_tool": "🔨", + ".dockerignore": "🐳", + ".ds_store": "📌", + ".editorconfig": "🐭", + ".env": "🏕 ", + ".envrc": "🔨", + ".git": "🐙", + ".gitattributes": "🐙", + ".gitconfig": "🐙", + ".gitignore": "🐙", + ".gitlab-ci.yml": "🎴", + ".gqlconfig": "🔨", + ".hackernews": "📰", + ".idea": "🔨", + ".merlin": "🧙", + ".svn": "🐢", + ".tool-versions": "🔨", + ".vimspector.json": "🐞", + ".vscode": "🆚", + ".zprofile": "🔨", + ".zshenv": "🔨", + ".zshrc": "🐚", + "MANIFEST.MF": "🔨", + "android": "🤖", + "cabal.config": "🔨", + "cabal.project": "🔨", + "changelog.md": "🍁", + "config": "🔨", + "deps": "🚼", + "docker-compose-single-broker.yml": "🐳", + "docker-compose-swarm.yml": "🐳", + "docker-compose.yml": "🐳", + "dockerfile": "🐳", + "dropbox": "📪", + "elm-package.json": "🔰", + "elm.json": "🔰", + "favicon.ico": "🎨", + "gemfile": "🔨", + "gemfile.lock": "🔐", + "gradlew": "🐚", + "gruntfile.coffee": "🔨", + "gruntfile.js": "🔶", + "gruntfile.ls": "🔶", + "guardfile": "💂", + "gulpfile.coffee": "🔨", + "gulpfile.js": "🔶", + "gulpfile.ls": "🔶", + "ios": "📱", + "jenkinsfile": "🎩", + "justfile": "🔨", + "lib": "📚", + "license": "📜", + "license.txt": "📜", + "makefile": "🐃", + "mix.lock": "🔐", + "node_modules": "🔶", + "paket.dependencies": "🔨", + "paket.references": "🔨", + "podfile": "🔨", + "pom.xml": "🔨", + "procfile": "🔨", + "rakefile": "🔨", + "react.jsx": "💢", + "routes": "🔨", + "spec.ts": "🔷", + "tags": "📌", + "test": "🧪", + "tests": "🧪", + "todo": "🚧", + "todo.md": "🚧", + "yarn.lock": "🐈" + }, + "name_glob": { + ".*.UnitDoc": "🥏", + ".*.config.js": "🔨", + ".*.config.ts": "🔨", + ".*.csproj.user": "💢", + ".*.css.map": "🌸", + ".*.db-journal": "🎲", + ".*.dockerfile": "🐳", + ".*.gr.wasm": "🌾", + ".*.js.map": "🔶", + ".*.mobileprovision": "📱", + ".*.podspec": "🔨", + ".*.xaml": "🔨", + ".*.xaml.cs": "🔨", + ".*LICENSE.*": "📜", + ".*angular.*.js": "🔶", + ".*backbone.*.js": "🔶", + ".*jquery.*.js": "🔶", + ".*materialize.*.css": "🔶", + ".*materialize.*.js": "🔶", + ".*mootools.*.js": "🔶", + ".*require.*.js": "🔶", + ".*vcxproj.filters": "💢", + ".*vimrc.*": "🍃", + ".dropbox": "📪", + ".gitconfig": "🐙", + ".npmrc": "🔨", + "commit_editmsg": "🐙", + "dockerfile..*": "🐳" + }, + "status": { + "active": "▶", + "inactive": " ", + "not_selected": " ", + "selected": "✸" + } + } + }, + "ls_colours": { + "nord": "no=00:rs=0:fi=00:di=01;34:ln=36:mh=04;36:pi=04;01;36:so=04;33:do=04;01;36:bd=01;33:cd=33:or=31:mi=01;37;41:ex=01;36:su=01;04;37:sg=01;04;37:ca=01;37:tw=01;37;44:ow=01;04;34:st=04;37;44:*.7z=01;32:*.ace=01;32:*.alz=01;32:*.arc=01;32:*.arj=01;32:*.bz=01;32:*.bz2=01;32:*.cab=01;32:*.cpio=01;32:*.deb=01;32:*.dz=01;32:*.ear=01;32:*.gz=01;32:*.jar=01;32:*.lha=01;32:*.lrz=01;32:*.lz=01;32:*.lz4=01;32:*.lzh=01;32:*.lzma=01;32:*.lzo=01;32:*.rar=01;32:*.rpm=01;32:*.rz=01;32:*.sar=01;32:*.t7z=01;32:*.tar=01;32:*.taz=01;32:*.tbz=01;32:*.tbz2=01;32:*.tgz=01;32:*.tlz=01;32:*.txz=01;32:*.tz=01;32:*.tzo=01;32:*.tzst=01;32:*.war=01;32:*.xz=01;32:*.z=01;32:*.Z=01;32:*.zip=01;32:*.zoo=01;32:*.zst=01;32:*.aac=32:*.au=32:*.flac=32:*.m4a=32:*.mid=32:*.midi=32:*.mka=32:*.mp3=32:*.mpa=32:*.mpeg=32:*.mpg=32:*.ogg=32:*.opus=32:*.ra=32:*.wav=32:*.3des=01;35:*.aes=01;35:*.gpg=01;35:*.pgp=01;35:*.doc=32:*.docx=32:*.dot=32:*.odg=32:*.odp=32:*.ods=32:*.odt=32:*.otg=32:*.otp=32:*.ots=32:*.ott=32:*.pdf=32:*.ppt=32:*.pptx=32:*.xls=32:*.xlsx=32:*.app=01;36:*.bat=01;36:*.btm=01;36:*.cmd=01;36:*.com=01;36:*.exe=01;36:*.reg=01;36:*~=02;37:*.bak=02;37:*.BAK=02;37:*.log=02;37:*.log=02;37:*.old=02;37:*.OLD=02;37:*.orig=02;37:*.ORIG=02;37:*.swo=02;37:*.swp=02;37:*.bmp=32:*.cgm=32:*.dl=32:*.dvi=32:*.emf=32:*.eps=32:*.gif=32:*.jpeg=32:*.jpg=32:*.JPG=32:*.mng=32:*.pbm=32:*.pcx=32:*.pgm=32:*.png=32:*.PNG=32:*.ppm=32:*.pps=32:*.ppsx=32:*.ps=32:*.svg=32:*.svgz=32:*.tga=32:*.tif=32:*.tiff=32:*.xbm=32:*.xcf=32:*.xpm=32:*.xwd=32:*.xwd=32:*.yuv=32:*.anx=32:*.asf=32:*.avi=32:*.axv=32:*.flc=32:*.fli=32:*.flv=32:*.gl=32:*.m2v=32:*.m4v=32:*.mkv=32:*.mov=32:*.MOV=32:*.mp4=32:*.mpeg=32:*.mpg=32:*.nuv=32:*.ogm=32:*.ogv=32:*.ogx=32:*.qt=32:*.rm=32:*.rmvb=32:*.swf=32:*.vob=32:*.webm=32:*.wmv=32:", + "solarized_dark": "no=00:fi=00:di=34:ow=34;40:ln=35:pi=30;44:so=35;44:do=35;44:bd=33;44:cd=37;44:or=05;37;41:mi=05;37;41:ex=01;31:*.cmd=01;31:*.exe=01;31:*.com=01;31:*.bat=01;31:*.reg=01;31:*.app=01;31:*.txt=32:*.org=32:*.md=32:*.mkd=32:*.h=32:*.hpp=32:*.c=32:*.C=32:*.cc=32:*.cpp=32:*.cxx=32:*.objc=32:*.cl=32:*.sh=32:*.bash=32:*.csh=32:*.zsh=32:*.el=32:*.vim=32:*.java=32:*.pl=32:*.pm=32:*.py=32:*.rb=32:*.hs=32:*.php=32:*.htm=32:*.html=32:*.shtml=32:*.erb=32:*.haml=32:*.xml=32:*.rdf=32:*.css=32:*.sass=32:*.scss=32:*.less=32:*.js=32:*.coffee=32:*.man=32:*.0=32:*.1=32:*.2=32:*.3=32:*.4=32:*.5=32:*.6=32:*.7=32:*.8=32:*.9=32:*.l=32:*.n=32:*.p=32:*.pod=32:*.tex=32:*.go=32:*.sql=32:*.csv=32:*.sv=32:*.svh=32:*.v=32:*.vh=32:*.vhd=32:*.bmp=33:*.cgm=33:*.dl=33:*.dvi=33:*.emf=33:*.eps=33:*.gif=33:*.jpeg=33:*.jpg=33:*.JPG=33:*.mng=33:*.pbm=33:*.pcx=33:*.pgm=33:*.png=33:*.PNG=33:*.ppm=33:*.pps=33:*.ppsx=33:*.ps=33:*.svg=33:*.svgz=33:*.tga=33:*.tif=33:*.tiff=33:*.xbm=33:*.xcf=33:*.xpm=33:*.xwd=33:*.xwd=33:*.yuv=33:*.nef=33:*.NEF=33:*.webp=33:*.heic=33:*.HEIC=33:*.avif=33:*.aac=33:*.au=33:*.flac=33:*.m4a=33:*.mid=33:*.midi=33:*.mka=33:*.mp3=33:*.mpa=33:*.mpeg=33:*.mpg=33:*.ogg=33:*.opus=33:*.ra=33:*.wav=33:*.anx=33:*.asf=33:*.avi=33:*.axv=33:*.flc=33:*.fli=33:*.flv=33:*.gl=33:*.m2v=33:*.m4v=33:*.mkv=33:*.mov=33:*.MOV=33:*.mp4=33:*.mp4v=33:*.mpeg=33:*.mpg=33:*.nuv=33:*.ogm=33:*.ogv=33:*.ogx=33:*.qt=33:*.rm=33:*.rmvb=33:*.swf=33:*.vob=33:*.webm=33:*.wmv=33:*.doc=31:*.docx=31:*.rtf=31:*.odt=31:*.dot=31:*.dotx=31:*.ott=31:*.xls=31:*.xlsx=31:*.ods=31:*.ots=31:*.ppt=31:*.pptx=31:*.odp=31:*.otp=31:*.fla=31:*.psd=31:*.pdf=31:*.7z=1;35:*.apk=1;35:*.arj=1;35:*.bin=1;35:*.bz=1;35:*.bz2=1;35:*.cab=1;35:*.deb=1;35:*.dmg=1;35:*.gem=1;35:*.gz=1;35:*.iso=1;35:*.jar=1;35:*.msi=1;35:*.rar=1;35:*.rpm=1;35:*.tar=1;35:*.tbz=1;35:*.tbz2=1;35:*.tgz=1;35:*.tx=1;35:*.war=1;35:*.xpi=1;35:*.xz=1;35:*.z=1;35:*.Z=1;35:*.zip=1;35:*.zst=1;35:*.ANSI-30-black=30:*.ANSI-01;30-brblack=01;30:*.ANSI-31-red=31:*.ANSI-01;31-brred=01;31:*.ANSI-32-green=32:*.ANSI-01;32-brgreen=01;32:*.ANSI-33-yellow=33:*.ANSI-01;33-bryellow=01;33:*.ANSI-34-blue=34:*.ANSI-01;34-brblue=01;34:*.ANSI-35-magenta=35:*.ANSI-01;35-brmagenta=01;35:*.ANSI-36-cyan=36:*.ANSI-01;36-brcyan=01;36:*.ANSI-37-white=37:*.ANSI-01;37-brwhite=01;37:*.log=01;32:*~=01;32:*#=01;32:*.bak=01;33:*.BAK=01;33:*.old=01;33:*.OLD=01;33:*.org_archive=01;33:*.off=01;33:*.OFF=01;33:*.dist=01;33:*.DIST=01;33:*.orig=01;33:*.ORIG=01;33:*.swp=01;33:*.swo=01;33:*.v=01;33:*.gpg=34:*.pgp=34:*.asc=34:*.3des=34:*.aes=34:*.enc=34:*.sqlite=34:", + "solarized_dark_256": "no=00;38;5;244:rs=0:di=00;38;5;33:ln=01;38;5;37:mh=00:pi=48;5;230;38;5;136;01:so=48;5;230;38;5;136;01:do=48;5;230;38;5;136;01:bd=48;5;230;38;5;244;01:cd=48;5;230;38;5;244;01:or=48;5;235;38;5;160:su=48;5;160;38;5;230:sg=48;5;136;38;5;230:ca=30;41:tw=48;5;64;38;5;230:ow=48;5;235;38;5;33:st=48;5;33;38;5;230:ex=01;38;5;64:*.tar=00;38;5;61:*.tgz=01;38;5;61:*.arj=01;38;5;61:*.taz=01;38;5;61:*.lzh=01;38;5;61:*.lzma=01;38;5;61:*.tlz=01;38;5;61:*.txz=01;38;5;61:*.zip=01;38;5;61:*.zst=01;38;5;61:*.z=01;38;5;61:*.Z=01;38;5;61:*.dz=01;38;5;61:*.gz=01;38;5;61:*.lz=01;38;5;61:*.xz=01;38;5;61:*.bz2=01;38;5;61:*.bz=01;38;5;61:*.tbz=01;38;5;61:*.tbz2=01;38;5;61:*.tz=01;38;5;61:*.deb=01;38;5;61:*.rpm=01;38;5;61:*.jar=01;38;5;61:*.rar=01;38;5;61:*.ace=01;38;5;61:*.zoo=01;38;5;61:*.cpio=01;38;5;61:*.7z=01;38;5;61:*.rz=01;38;5;61:*.apk=01;38;5;61:*.gem=01;38;5;61:*.jpg=00;38;5;136:*.JPG=00;38;5;136:*.jpeg=00;38;5;136:*.gif=00;38;5;136:*.bmp=00;38;5;136:*.pbm=00;38;5;136:*.pgm=00;38;5;136:*.ppm=00;38;5;136:*.tga=00;38;5;136:*.xbm=00;38;5;136:*.xpm=00;38;5;136:*.tif=00;38;5;136:*.tiff=00;38;5;136:*.png=00;38;5;136:*.PNG=00;38;5;136:*.svg=00;38;5;136:*.svgz=00;38;5;136:*.mng=00;38;5;136:*.pcx=00;38;5;136:*.dl=00;38;5;136:*.xcf=00;38;5;136:*.xwd=00;38;5;136:*.yuv=00;38;5;136:*.cgm=00;38;5;136:*.emf=00;38;5;136:*.eps=00;38;5;136:*.CR2=00;38;5;136:*.ico=00;38;5;136:*.nef=00;38;5;136:*.NEF=00;38;5;136:*.webp=00;38;5;136:*.heic=00;38;5;136:*.HEIC=00;38;5;136:*.avif=00;38;5;136:*.tex=01;38;5;245:*.rdf=01;38;5;245:*.owl=01;38;5;245:*.n3=01;38;5;245:*.ttl=01;38;5;245:*.nt=01;38;5;245:*.torrent=01;38;5;245:*.xml=01;38;5;245:*Makefile=01;38;5;245:*Rakefile=01;38;5;245:*Dockerfile=01;38;5;245:*build.xml=01;38;5;245:*rc=01;38;5;245:*1=01;38;5;245:*.nfo=01;38;5;245:*README=01;38;5;245:*README.txt=01;38;5;245:*readme.txt=01;38;5;245:*.md=01;38;5;245:*README.markdown=01;38;5;245:*.ini=01;38;5;245:*.yml=01;38;5;245:*.cfg=01;38;5;245:*.conf=01;38;5;245:*.h=01;38;5;245:*.hpp=01;38;5;245:*.c=01;38;5;245:*.cpp=01;38;5;245:*.cxx=01;38;5;245:*.cc=01;38;5;245:*.objc=01;38;5;245:*.sqlite=01;38;5;245:*.go=01;38;5;245:*.sql=01;38;5;245:*.csv=01;38;5;245:*.log=00;38;5;240:*.bak=00;38;5;240:*.aux=00;38;5;240:*.lof=00;38;5;240:*.lol=00;38;5;240:*.lot=00;38;5;240:*.out=00;38;5;240:*.toc=00;38;5;240:*.bbl=00;38;5;240:*.blg=00;38;5;240:*~=00;38;5;240:*#=00;38;5;240:*.part=00;38;5;240:*.incomplete=00;38;5;240:*.swp=00;38;5;240:*.tmp=00;38;5;240:*.temp=00;38;5;240:*.o=00;38;5;240:*.pyc=00;38;5;240:*.class=00;38;5;240:*.cache=00;38;5;240:*.aac=00;38;5;166:*.au=00;38;5;166:*.flac=00;38;5;166:*.mid=00;38;5;166:*.midi=00;38;5;166:*.mka=00;38;5;166:*.mp3=00;38;5;166:*.mpc=00;38;5;166:*.ogg=00;38;5;166:*.opus=00;38;5;166:*.ra=00;38;5;166:*.wav=00;38;5;166:*.m4a=00;38;5;166:*.axa=00;38;5;166:*.oga=00;38;5;166:*.spx=00;38;5;166:*.xspf=00;38;5;166:*.mov=01;38;5;166:*.MOV=01;38;5;166:*.mpg=01;38;5;166:*.mpeg=01;38;5;166:*.m2v=01;38;5;166:*.mkv=01;38;5;166:*.ogm=01;38;5;166:*.mp4=01;38;5;166:*.m4v=01;38;5;166:*.mp4v=01;38;5;166:*.vob=01;38;5;166:*.qt=01;38;5;166:*.nuv=01;38;5;166:*.wmv=01;38;5;166:*.asf=01;38;5;166:*.rm=01;38;5;166:*.rmvb=01;38;5;166:*.flc=01;38;5;166:*.avi=01;38;5;166:*.fli=01;38;5;166:*.flv=01;38;5;166:*.gl=01;38;5;166:*.m2ts=01;38;5;166:*.divx=01;38;5;166:*.webm=01;38;5;166:*.axv=01;38;5;166:*.anx=01;38;5;166:*.ogv=01;38;5;166:*.ogx=01;38;5;166:", + "solarized_light": "no=00:fi=00:di=36:ow=34;47:ln=35:pi=30;44:so=35;44:do=35;44:bd=33;44:cd=37;44:or=05;37;41:mi=05;37;41:ex=01;31:*.cmd=01;31:*.exe=01;31:*.com=01;31:*.bat=01;31:*.reg=01;31:*.app=01;31:*.txt=32:*.org=32:*.md=32:*.mkd=32:*.h=32:*.hpp=32:*.c=32:*.C=32:*.cc=32:*.cpp=32:*.cxx=32:*.objc=32:*.cl=32:*.sh=32:*.bash=32:*.csh=32:*.zsh=32:*.el=32:*.vim=32:*.java=32:*.pl=32:*.pm=32:*.py=32:*.rb=32:*.hs=32:*.php=32:*.htm=32:*.html=32:*.shtml=32:*.erb=32:*.haml=32:*.xml=32:*.rdf=32:*.css=32:*.sass=32:*.scss=32:*.less=32:*.js=32:*.coffee=32:*.man=32:*.0=32:*.1=32:*.2=32:*.3=32:*.4=32:*.5=32:*.6=32:*.7=32:*.8=32:*.9=32:*.l=32:*.n=32:*.p=32:*.pod=32:*.tex=32:*.go=32:*.sql=32:*.csv=32:*.sv=32:*.svh=32:*.v=32:*.vh=32:*.vhd=32:*.bmp=33:*.cgm=33:*.dl=33:*.dvi=33:*.emf=33:*.eps=33:*.gif=33:*.jpeg=33:*.jpg=33:*.JPG=33:*.mng=33:*.pbm=33:*.pcx=33:*.pgm=33:*.png=33:*.PNG=33:*.ppm=33:*.pps=33:*.ppsx=33:*.ps=33:*.svg=33:*.svgz=33:*.tga=33:*.tif=33:*.tiff=33:*.xbm=33:*.xcf=33:*.xpm=33:*.xwd=33:*.xwd=33:*.yuv=33:*.nef=33:*.NEF=33:*.webp=33:*.heic=33:*.HEIC=33:*.avif=33:*.aac=33:*.au=33:*.flac=33:*.m4a=33:*.mid=33:*.midi=33:*.mka=33:*.mp3=33:*.mpa=33:*.mpeg=33:*.mpg=33:*.ogg=33:*.opus=33:*.ra=33:*.wav=33:*.anx=33:*.asf=33:*.avi=33:*.axv=33:*.flc=33:*.fli=33:*.flv=33:*.gl=33:*.m2v=33:*.m4v=33:*.mkv=33:*.mov=33:*.MOV=33:*.mp4=33:*.mp4v=33:*.mpeg=33:*.mpg=33:*.nuv=33:*.ogm=33:*.ogv=33:*.ogx=33:*.qt=33:*.rm=33:*.rmvb=33:*.swf=33:*.vob=33:*.webm=33:*.wmv=33:*.doc=31:*.docx=31:*.rtf=31:*.odt=31:*.dot=31:*.dotx=31:*.ott=31:*.xls=31:*.xlsx=31:*.ods=31:*.ots=31:*.ppt=31:*.pptx=31:*.odp=31:*.otp=31:*.fla=31:*.psd=31:*.pdf=31:*.7z=1;35:*.apk=1;35:*.arj=1;35:*.bin=1;35:*.bz=1;35:*.bz2=1;35:*.cab=1;35:*.deb=1;35:*.dmg=1;35:*.gem=1;35:*.gz=1;35:*.iso=1;35:*.jar=1;35:*.msi=1;35:*.rar=1;35:*.rpm=1;35:*.tar=1;35:*.tbz=1;35:*.tbz2=1;35:*.tgz=1;35:*.tx=1;35:*.war=1;35:*.xpi=1;35:*.xz=1;35:*.z=1;35:*.Z=1;35:*.zip=1;35:*.zst=1;35:*.ANSI-30-black=30:*.ANSI-01;30-brblack=01;30:*.ANSI-31-red=31:*.ANSI-01;31-brred=01;31:*.ANSI-32-green=32:*.ANSI-01;32-brgreen=01;32:*.ANSI-33-yellow=33:*.ANSI-01;33-bryellow=01;33:*.ANSI-34-blue=34:*.ANSI-01;34-brblue=01;34:*.ANSI-35-magenta=35:*.ANSI-01;35-brmagenta=01;35:*.ANSI-36-cyan=36:*.ANSI-01;36-brcyan=01;36:*.ANSI-37-white=37:*.ANSI-01;37-brwhite=01;37:*.log=01;34:*~=01;34:*#=01;34:*.bak=01;36:*.BAK=01;36:*.old=01;36:*.OLD=01;36:*.org_archive=01;36:*.off=01;36:*.OFF=01;36:*.dist=01;36:*.DIST=01;36:*.orig=01;36:*.ORIG=01;36:*.swp=01;36:*.swo=01;36:*.v=01;36:*.gpg=34:*.pgp=34:*.asc=34:*.3des=34:*.aes=34:*.enc=34:*.sqlite=34:", + "solarized_universal": "no=00:fi=00:di=36:ln=35:pi=30;44:so=35;44:do=35;44:bd=33;44:cd=37;44:or=05;37;41:mi=05;37;41:ex=01;31:*.cmd=01;31:*.exe=01;31:*.com=01;31:*.bat=01;31:*.reg=01;31:*.app=01;31:*.txt=32:*.org=32:*.md=32:*.mkd=32:*.bib=32:*.h=32:*.hpp=32:*.c=32:*.C=32:*.cc=32:*.cpp=32:*.cxx=32:*.objc=32:*.cl=32:*.sh=32:*.bash=32:*.csh=32:*.zsh=32:*.el=32:*.vim=32:*.java=32:*.pl=32:*.pm=32:*.py=32:*.rb=32:*.hs=32:*.php=32:*.htm=32:*.html=32:*.shtml=32:*.erb=32:*.haml=32:*.xml=32:*.rdf=32:*.css=32:*.sass=32:*.scss=32:*.less=32:*.js=32:*.coffee=32:*.man=32:*.0=32:*.1=32:*.2=32:*.3=32:*.4=32:*.5=32:*.6=32:*.7=32:*.8=32:*.9=32:*.l=32:*.n=32:*.p=32:*.pod=32:*.tex=32:*.go=32:*.sql=32:*.csv=32:*.sv=32:*.svh=32:*.v=32:*.vh=32:*.vhd=32:*.bmp=33:*.cgm=33:*.dl=33:*.dvi=33:*.emf=33:*.eps=33:*.gif=33:*.jpeg=33:*.jpg=33:*.JPG=33:*.mng=33:*.pbm=33:*.pcx=33:*.pgm=33:*.png=33:*.PNG=33:*.ppm=33:*.pps=33:*.ppsx=33:*.ps=33:*.svg=33:*.svgz=33:*.tga=33:*.tif=33:*.tiff=33:*.xbm=33:*.xcf=33:*.xpm=33:*.xwd=33:*.xwd=33:*.yuv=33:*.NEF=33:*.nef=33:*.webp=33:*.heic=33:*.HEIC=33:*.avif=33:*.aac=33:*.au=33:*.flac=33:*.m4a=33:*.mid=33:*.midi=33:*.mka=33:*.mp3=33:*.mpa=33:*.mpeg=33:*.mpg=33:*.ogg=33:*.opus=33:*.ra=33:*.wav=33:*.anx=33:*.asf=33:*.avi=33:*.axv=33:*.flc=33:*.fli=33:*.flv=33:*.gl=33:*.m2v=33:*.m4v=33:*.mkv=33:*.mov=33:*.MOV=33:*.mp4=33:*.mp4v=33:*.mpeg=33:*.mpg=33:*.nuv=33:*.ogm=33:*.ogv=33:*.ogx=33:*.qt=33:*.rm=33:*.rmvb=33:*.swf=33:*.vob=33:*.webm=33:*.wmv=33:*.doc=31:*.docx=31:*.rtf=31:*.odt=31:*.dot=31:*.dotx=31:*.ott=31:*.xls=31:*.xlsx=31:*.ods=31:*.ots=31:*.ppt=31:*.pptx=31:*.odp=31:*.otp=31:*.fla=31:*.psd=31:*.pdf=31:*.7z=1;35:*.apk=1;35:*.arj=1;35:*.bin=1;35:*.bz=1;35:*.bz2=1;35:*.cab=1;35:*.deb=1;35:*.dmg=1;35:*.gem=1;35:*.gz=1;35:*.iso=1;35:*.jar=1;35:*.msi=1;35:*.rar=1;35:*.rpm=1;35:*.tar=1;35:*.tbz=1;35:*.tbz2=1;35:*.tgz=1;35:*.tx=1;35:*.war=1;35:*.xpi=1;35:*.xz=1;35:*.z=1;35:*.Z=1;35:*.zip=1;35:*.zst=1;35:*.ANSI-30-black=30:*.ANSI-01;30-brblack=01;30:*.ANSI-31-red=31:*.ANSI-01;31-brred=01;31:*.ANSI-32-green=32:*.ANSI-01;32-brgreen=01;32:*.ANSI-33-yellow=33:*.ANSI-01;33-bryellow=01;33:*.ANSI-34-blue=34:*.ANSI-01;34-brblue=01;34:*.ANSI-35-magenta=35:*.ANSI-01;35-brmagenta=01;35:*.ANSI-36-cyan=36:*.ANSI-01;36-brcyan=01;36:*.ANSI-37-white=37:*.ANSI-01;37-brwhite=01;37:*.log=01;32:*~=01;32:*#=01;32:*.bak=01;36:*.BAK=01;36:*.old=01;36:*.OLD=01;36:*.org_archive=01;36:*.off=01;36:*.OFF=01;36:*.dist=01;36:*.DIST=01;36:*.orig=01;36:*.ORIG=01;36:*.swp=01;36:*.swo=01;36:*.v=01;36:*.gpg=34:*.pgp=34:*.asc=34:*.3des=34:*.aes=34:*.enc=34:*.sqlite=34:*.db=34:", + "trapdoor": "bd=38;5;68:ca=38;5;17:cd=38;5;113;1:di=38;5;30:do=38;5;127:ex=38;5;208;1:pi=38;5;126:fi=0:ln=target:mh=38;5;222;1:no=0:or=48;5;196;38;5;232;1:ow=38;5;220;1:sg=48;5;3;38;5;0:su=38;5;220;1;3;100;1:so=38;5;197:st=38;5;86;48;5;234:tw=48;5;235;38;5;139;3:*LS_COLORS=48;5;89;38;5;197;1;3;4;7:*.txt=38;5;253:*README=38;5;220;1:*README.rst=38;5;220;1:*README.md=38;5;220;1:*LICENSE=38;5;220;1:*LICENSE.md=38;5;220;1:*COPYING=38;5;220;1:*INSTALL=38;5;220;1:*COPYRIGHT=38;5;220;1:*AUTHORS=38;5;220;1:*HISTORY=38;5;220;1:*CONTRIBUTORS=38;5;220;1:*CONTRIBUTING=38;5;220;1:*CONTRIBUTING.md=38;5;220;1:*CHANGELOG=38;5;220;1:*CHANGELOG.md=38;5;220;1:*CODEOWNERS=38;5;220;1:*PATENTS=38;5;220;1:*VERSION=38;5;220;1:*NOTICE=38;5;220;1:*CHANGES=38;5;220;1:*.log=38;5;190:*.adoc=38;5;184:*.asciidoc=38;5;184:*.etx=38;5;184:*.info=38;5;184:*.markdown=38;5;184:*.md=38;5;184:*.mkd=38;5;184:*.mdx=38;5;184:*.nfo=38;5;184:*.org=38;5;184:*.norg=38;5;184:*.pod=38;5;184:*.rst=38;5;184:*.tex=38;5;184:*.textile=38;5;184:*.bib=38;5;178:*.json=38;5;178:*.jsonc=38;5;178:*.json5=38;5;178:*.hjson=38;5;178:*.jsonl=38;5;178:*.jsonnet=38;5;178:*.libsonnet=38;5;142:*.ndjson=38;5;178:*.msg=38;5;178:*.pgn=38;5;178:*.rss=38;5;178:*.xml=38;5;178:*.fxml=38;5;178:*.toml=38;5;178:*.yaml=38;5;178:*.yml=38;5;178:*.RData=38;5;178:*.rdata=38;5;178:*.xsd=38;5;178:*.dtd=38;5;178:*.sgml=38;5;178:*.rng=38;5;178:*.rnc=38;5;178:*.accdb=38;5;60:*.accde=38;5;60:*.accdr=38;5;60:*.accdt=38;5;60:*.db=38;5;60:*.fmp12=38;5;60:*.fp7=38;5;60:*.localstorage=38;5;60:*.mdb=38;5;60:*.mde=38;5;60:*.sqlite=38;5;60:*.typelib=38;5;60:*.nc=38;5;60:*.cbr=38;5;141:*.cbz=38;5;141:*.chm=38;5;141:*.djvu=38;5;141:*.pdf=38;5;141:*.PDF=38;5;141:*.mobi=38;5;141:*.epub=38;5;141:*.docm=38;5;111;4:*.doc=38;5;111:*.docx=38;5;111:*.odb=38;5;111:*.odt=38;5;111:*.rtf=38;5;111:*.pages=38;5;111:*.odp=38;5;166:*.pps=38;5;166:*.ppt=38;5;166:*.pptx=38;5;166:*.ppts=38;5;166:*.pptxm=38;5;166;4:*.pptsm=38;5;166;4:*.prisma=38;5;222:*.csv=38;5;78:*.tsv=38;5;78:*.numbers=38;5;112:*.ods=38;5;112:*.xla=38;5;76:*.xls=38;5;112:*.xlsx=38;5;112:*.xlsxm=38;5;112;4:*.xltm=38;5;73;4:*.xltx=38;5;73:*.key=38;5;166:*config=1:*cfg=1:*conf=1:*rc=1:*authorized_keys=1:*known_hosts=1:*.ini=1:*.plist=1:*.profile=1:*.bash_profile=1:*.bash_login=1:*.bash_logout=1:*.zshenv=1:*.zprofile=1:*.zlogin=1:*.zlogout=1:*.viminfo=1:*.pcf=1:*.psf=1:*.hidden-color-scheme=1:*.hidden-tmTheme=1:*.last-run=1:*.merged-ca-bundle=1:*.sublime-build=1:*.sublime-commands=1:*.sublime-keymap=1:*.sublime-settings=1:*.sublime-snippet=1:*.sublime-project=1:*.sublime-workspace=1:*.tmTheme=1:*.user-ca-bundle=1:*.rstheme=1:*.epf=1:*.git=38;5;197:*.github=38;5;197:*.gitignore=38;5;240:*.gitattributes=38;5;240:*.gitmodules=38;5;240:*.awk=38;5;172:*.bash=38;5;172:*.bat=38;5;172:*.BAT=38;5;172:*.sed=38;5;172:*.sh=38;5;172:*.zsh=38;5;172:*.fish=38;5;172:*.vim=38;5;172:*.kak=38;5;172:*.ahk=38;5;41:*.py=38;5;41:*.ipynb=38;5;41:*.xsh=38;5;41:*.rb=38;5;41:*.gemspec=38;5;41:*.pl=38;5;208:*.PL=38;5;160:*.pm=38;5;203:*.t=38;5;114:*.msql=38;5;222:*.mysql=38;5;222:*.prql=38;5;222:*.pgsql=38;5;222:*.sql=38;5;222:*.tcl=38;5;64;1:*.r=38;5;49:*.R=38;5;49:*.gs=38;5;81:*.clj=38;5;41:*.cljs=38;5;41:*.cljc=38;5;41:*.cljw=38;5;41:*.scala=38;5;41:*.sc=38;5;41:*.dart=38;5;51:*.asm=38;5;81:*.cl=38;5;81:*.ml=38;5;81:*.lisp=38;5;81:*.rkt=38;5;81:*.el=38;5;81:*.elc=38;5;241:*.eln=38;5;241:*.lua=38;5;81:*.moon=38;5;81:*.c=38;5;81:*.C=38;5;81:*.h=38;5;110:*.H=38;5;110:*.tcc=38;5;110:*.c++=38;5;81:*.h++=38;5;110:*.hpp=38;5;110:*.hxx=38;5;110:*.ii=38;5;110:*.M=38;5;110:*.m=38;5;110:*.cc=38;5;81:*.cs=38;5;81:*.cp=38;5;81:*.cpp=38;5;81:*.cxx=38;5;81:*.cr=38;5;81:*.go=38;5;81:*.f=38;5;81:*.F=38;5;81:*.for=38;5;81:*.ftn=38;5;81:*.f90=38;5;81:*.F90=38;5;81:*.f95=38;5;81:*.F95=38;5;81:*.f03=38;5;81:*.F03=38;5;81:*.f08=38;5;81:*.F08=38;5;81:*.nim=38;5;81:*.nimble=38;5;81:*.s=38;5;110:*.S=38;5;110:*.rs=38;5;81:*.scpt=38;5;219:*.swift=38;5;219:*.sx=38;5;81:*.vala=38;5;81:*.vapi=38;5;81:*.hi=38;5;110:*.hs=38;5;81:*.lhs=38;5;81:*.agda=38;5;81:*.lagda=38;5;81:*.lagda.tex=38;5;81:*.lagda.rst=38;5;81:*.lagda.md=38;5;81:*.agdai=38;5;110:*.zig=38;5;81:*.v=38;5;81:*.pyc=38;5;240:*.tf=38;5;168:*.tfstate=38;5;168:*.tfvars=38;5;168:*.http=38;5;90;1:*.eml=38;5;90;1:*.css=38;5;105;1:*.less=38;5;105;1:*.sass=38;5;105;1:*.scss=38;5;105;1:*.htm=38;5;125;1:*.html=38;5;125;1:*.jhtm=38;5;125;1:*.mht=38;5;125;1:*.mustache=38;5;135;1:*.ejs=38;5;135;1:*.pug=38;5;135;1:*.svelte=38;5;135;1:*.vue=38;5;135;1:*.astro=38;5;135;1:*.js=38;5;074;1:*.jsx=38;5;074;1:*.ts=38;5;074;1:*.tsx=38;5;074;1:*.mjs=38;5;074;1:*.cjs=38;5;074;1:*.coffee=38;5;079;1:*.java=38;5;079;1:*.jsm=38;5;079;1:*.jsp=38;5;079;1:*.php=38;5;81:*.ctp=38;5;81:*.twig=38;5;81:*.vb=38;5;81:*.vba=38;5;81:*.vbs=38;5;81:*Containerfile=38;5;155:*.containerignore=38;5;240:*Dockerfile=38;5;155:*.dockerignore=38;5;240:*Makefile=38;5;155:*MANIFEST=38;5;243:*pm_to_blib=38;5;240:*.nix=38;5;155:*.dhall=38;5;178:*.rake=38;5;155:*.am=38;5;242:*.in=38;5;242:*.hin=38;5;242:*.scan=38;5;242:*.m4=38;5;242:*.old=38;5;242:*.out=38;5;242:*.SKIP=38;5;244:*.diff=48;5;197;38;5;232:*.patch=48;5;197;38;5;232;1:*.bmp=38;5;97:*.dicom=38;5;97:*.tiff=38;5;97:*.tif=38;5;97:*.TIFF=38;5;97:*.cdr=38;5;97:*.flif=38;5;97:*.gif=38;5;97:*.icns=38;5;97:*.ico=38;5;97:*.jpeg=38;5;97:*.JPG=38;5;97:*.jpg=38;5;97:*.jxl=38;5;97:*.nth=38;5;97:*.png=38;5;97:*.psd=38;5;97:*.pxd=38;5;97:*.pxm=38;5;97:*.xpm=38;5;97:*.webp=38;5;97:*.ai=38;5;99:*.eps=38;5;99:*.epsf=38;5;99:*.drw=38;5;99:*.ps=38;5;99:*.svg=38;5;99:*.avi=38;5;114:*.divx=38;5;114:*.IFO=38;5;114:*.m2v=38;5;114:*.m4v=38;5;114:*.mkv=38;5;114:*.MOV=38;5;114:*.mov=38;5;114:*.mp4=38;5;114:*.mpeg=38;5;114:*.mpg=38;5;114:*.ogm=38;5;114:*.rmvb=38;5;114:*.sample=38;5;114:*.wmv=38;5;114:*.3g2=38;5;115:*.3gp=38;5;115:*.gp3=38;5;115:*.webm=38;5;115:*.gp4=38;5;115:*.asf=38;5;115:*.flv=38;5;115:*.ogv=38;5;115:*.f4v=38;5;115:*.VOB=38;5;115;1:*.vob=38;5;115;1:*.ass=38;5;117:*.srt=38;5;117:*.ssa=38;5;117:*.sub=38;5;117:*.sup=38;5;117:*.vtt=38;5;117:*.3ga=38;5;137;1:*.S3M=38;5;137;1:*.aac=38;5;137;1:*.amr=38;5;137;1:*.au=38;5;137;1:*.caf=38;5;137;1:*.dat=38;5;137;1:*.dts=38;5;137;1:*.fcm=38;5;137;1:*.m4a=38;5;137;1:*.mod=38;5;137;1:*.mp3=38;5;137;1:*.mp4a=38;5;137;1:*.oga=38;5;137;1:*.ogg=38;5;137;1:*.opus=38;5;137;1:*.s3m=38;5;137;1:*.sid=38;5;137;1:*.wma=38;5;137;1:*.ape=38;5;136;1:*.aiff=38;5;136;1:*.cda=38;5;136;1:*.flac=38;5;136;1:*.alac=38;5;136;1:*.mid=38;5;136;1:*.midi=38;5;136;1:*.pcm=38;5;136;1:*.wav=38;5;136;1:*.wv=38;5;136;1:*.wvc=38;5;136;1:*.afm=38;5;66:*.fon=38;5;66:*.fnt=38;5;66:*.pfb=38;5;66:*.pfm=38;5;66:*.ttf=38;5;66:*.otf=38;5;66:*.woff=38;5;66:*.woff2=38;5;66:*.PFA=38;5;66:*.pfa=38;5;66:*.7z=38;5;40:*.a=38;5;40:*.arj=38;5;40:*.br=38;5;40:*.bz2=38;5;40:*.cpio=38;5;40:*.gz=38;5;40:*.lrz=38;5;40:*.lz=38;5;40:*.lzma=38;5;40:*.lzo=38;5;40:*.rar=38;5;40:*.s7z=38;5;40:*.sz=38;5;40:*.tar=38;5;40:*.tbz=38;5;40:*.tgz=38;5;40:*.warc=38;5;40:*.WARC=38;5;40:*.xz=38;5;40:*.z=38;5;40:*.zip=38;5;40:*.zipx=38;5;40:*.zoo=38;5;40:*.zpaq=38;5;40:*.zst=38;5;40:*.zstd=38;5;40:*.zz=38;5;40:*.apk=38;5;215:*.ipa=38;5;215:*.deb=38;5;215:*.rpm=38;5;215:*.jad=38;5;215:*.jar=38;5;215:*.ear=38;5;215:*.war=38;5;215:*.cab=38;5;215:*.pak=38;5;215:*.pk3=38;5;215:*.vdf=38;5;215:*.vpk=38;5;215:*.bsp=38;5;215:*.dmg=38;5;215:*.crx=38;5;215:*.xpi=38;5;215:*.iso=38;5;124:*.img=38;5;124:*.bin=38;5;124:*.nrg=38;5;124:*.qcow=38;5;124:*.fvd=38;5;124:*.sparseimage=38;5;124:*.toast=38;5;124:*.vcd=38;5;124:*.vdi=38;5;124:*.vhd=38;5;124:*.vhdx=38;5;124:*.vfd=38;5;124:*.vmdk=38;5;124:*.swp=38;5;244:*.swo=38;5;244:*.tmp=38;5;244:*.sassc=38;5;244:*.pacnew=38;5;33:*.un~=38;5;241:*.orig=38;5;241:*.BUP=38;5;241:*.bak=38;5;241:*.o=38;5;241:*core=38;5;241:*.mdump=38;5;241:*.rlib=38;5;241:*.dll=38;5;241:*.aria2=38;5;241:*.dump=38;5;241:*.stackdump=38;5;241:*.zcompdump=38;5;241:*.zwc=38;5;241:*.part=38;5;239:*.r[0-9]{0,2}=38;5;239:*.zx[0-9]{0,2}=38;5;239:*.z[0-9]{0,2}=38;5;239:*.pid=38;5;248:*.state=38;5;248:*lockfile=38;5;248:*lock=38;5;248:*.err=38;5;160;1:*.error=38;5;160;1:*.stderr=38;5;160;1:*.pcap=38;5;29:*.cap=38;5;29:*.dmp=38;5;29:*.allow=38;5;112:*.deny=38;5;196:*.service=38;5;45:*@.service=38;5;45:*.socket=38;5;45:*.swap=38;5;45:*.device=38;5;45:*.mount=38;5;45:*.automount=38;5;45:*.target=38;5;45:*.path=38;5;45:*.timer=38;5;45:*.snapshot=38;5;45:*.lnk=38;5;39:*.application=38;5;116:*.cue=38;5;116:*.description=38;5;116:*.directory=38;5;116:*.m3u=38;5;116:*.m3u8=38;5;116:*.md5=38;5;116:*.properties=38;5;116:*.sfv=38;5;116:*.theme=38;5;116:*.torrent=38;5;116:*.urlview=38;5;116:*.webloc=38;5;116:*.asc=38;5;192;3:*.bfe=38;5;192;3:*.enc=38;5;192;3:*.gpg=38;5;192;3:*.signature=38;5;192;3:*.sig=38;5;192;3:*.p12=38;5;192;3:*.pem=38;5;192;3:*.pgp=38;5;192;3:*.p7s=38;5;192;3:*id_dsa=38;5;192;3:*id_rsa=38;5;192;3:*id_ecdsa=38;5;192;3:*id_ed25519=38;5;192;3:*.32x=38;5;213:*.cdi=38;5;213:*.fm2=38;5;213:*.rom=38;5;213:*.sav=38;5;213:*.st=38;5;213:*.a00=38;5;213:*.a52=38;5;213:*.A64=38;5;213:*.a64=38;5;213:*.a78=38;5;213:*.adf=38;5;213:*.atr=38;5;213:*.gb=38;5;213:*.gba=38;5;213:*.gbc=38;5;213:*.gel=38;5;213:*.gg=38;5;213:*.ggl=38;5;213:*.ipk=38;5;213:*.j64=38;5;213:*.nds=38;5;213:*.nes=38;5;213:*.sms=38;5;213:*.8xp=38;5;121:*.8eu=38;5;121:*.82p=38;5;121:*.83p=38;5;121:*.8xe=38;5;121:*.stl=38;5;216:*.dwg=38;5;216:*.ply=38;5;216:*.wrl=38;5;216:*.vert=38;5;136:*.comp=38;5;136:*.frag=38;5;136:*.spv=38;5;217:*.wgsl=38;5;97:*.xib=38;5;208:*.iml=38;5;166:*.DS_Store=38;5;239:*.localized=38;5;239:*.CFUserTextEncoding=38;5;239:*CodeResources=38;5;239:*PkgInfo=38;5;239:*.nib=38;5;57:*.car=38;5;57:*.dylib=38;5;241:*.entitlements=1:*.pbxproj=1:*.strings=1:*.storyboard=38;5;196:*.xcconfig=1:*.xcsettings=1:*.xcuserstate=1:*.xcworkspacedata=1:*.pot=38;5;7:*.pcb=38;5;7:*.mm=38;5;7:*.gbr=38;5;7:*.scm=38;5;7:*.xcf=38;5;7:*.spl=38;5;7:*.Rproj=38;5;11:*.sis=38;5;7:*.1p=38;5;7:*.3p=38;5;7:*.cnc=38;5;7:*.def=38;5;7:*.ex=38;5;7:*.example=38;5;7:*.feature=38;5;7:*.ger=38;5;7:*.ics=38;5;7:*.map=38;5;7:*.mf=38;5;7:*.mfasl=38;5;7:*.mi=38;5;7:*.mtx=38;5;7:*.pc=38;5;7:*.pi=38;5;7:*.plt=38;5;7:*.rdf=38;5;7:*.ru=38;5;7:*.sch=38;5;7:*.sty=38;5;7:*.sug=38;5;7:*.tdy=38;5;7:*.tfm=38;5;7:*.tfnt=38;5;7:*.tg=38;5;7:*.vcard=38;5;7:*.vcf=38;5;7:*.xln=38;5;7:" + }, + "text_colours": { + "nerdtree_syntax_dark": { + "ext_exact": { + ".ai": "#F16529", + ".awk": "#FFFFFF", + ".bash": "#834F79", + ".bat": "#FFFFFF", + ".bmp": "#3AFFDB", + ".c": "#689FB6", + ".c++": "#689FB6", + ".cc": "#689FB6", + ".clj": "#8FAA54", + ".cljc": "#8FAA54", + ".cljs": "#8FAA54", + ".coffee": "#905532", + ".conf": "#FFFFFF", + ".cp": "#689FB6", + ".cpp": "#689FB6", + ".cs": "#689FB6", + ".csh": "#FFFFFF", + ".css": "#689FB6", + ".cxx": "#689FB6", + ".d": "#AE403F", + ".dart": "#689FB6", + ".db": "#689FB6", + ".diff": "#FFFFFF", + ".dump": "#689FB6", + ".edn": "#8FAA54", + ".eex": "#834F79", + ".ejs": "#F09F17", + ".elm": "#FFFFFF", + ".erb": "#AE403F", + ".erl": "#834F79", + ".ex": "#834F79", + ".exs": "#834F79", + ".f#": "#44788E", + ".fish": "#8FAA54", + ".fs": "#689FB6", + ".fsi": "#689FB6", + ".fsscript": "#689FB6", + ".fsx": "#689FB6", + ".gif": "#3AFFDB", + ".go": "#F5C06F", + ".h": "#689FB6", + ".hbs": "#D4843E", + ".hh": "#689FB6", + ".hpp": "#689FB6", + ".hrl": "#CB6F6F", + ".hs": "#F5C06F", + ".htm": "#F16529", + ".html": "#F16529", + ".hxx": "#689FB6", + ".ico": "#3AFFDB", + ".ini": "#FFFFFF", + ".java": "#834F79", + ".jl": "#9558B2", + ".jpeg": "#3AFFDB", + ".jpg": "#3AFFDB", + ".js": "#F5C06F", + ".json": "#F5C06F", + ".jsx": "#689FB6", + ".ksh": "#FFFFFF", + ".leex": "#FFFFFF", + ".less": "#44788E", + ".lhs": "#F5C06F", + ".lua": "#834F79", + ".markdown": "#F09F17", + ".md": "#F09F17", + ".mdx": "#F09F17", + ".mjs": "#F5C06F", + ".ml": "#F09F17", + ".mli": "#F09F17", + ".mustache": "#D4843E", + ".php": "#834F79", + ".pl": "#689FB6", + ".pm": "#689FB6", + ".png": "#3AFFDB", + ".pp": "#FFFFFF", + ".ps1": "#689FB6", + ".psb": "#44788E", + ".psd": "#44788E", + ".py": "#F09F17", + ".pyc": "#F09F17", + ".pyd": "#F09F17", + ".pyo": "#F09F17", + ".rb": "#AE403F", + ".rlib": "#F16529", + ".rmd": "#F09F17", + ".rs": "#F16529", + ".rss": "#F16529", + ".sass": "#CB6F6F", + ".scala": "#AE403F", + ".scss": "#CB6F6F", + ".sh": "#834F79", + ".slim": "#D4843E", + ".sln": "#834F79", + ".sql": "#44788E", + ".styl": "#8FAA54", + ".suo": "#834F79", + ".swift": "#D4843E", + ".t": "#689FB6", + ".toml": "#FFFFFF", + ".ts": "#689FB6", + ".tsx": "#689FB6", + ".twig": "#8FAA54", + ".vim": "#8FAA54", + ".vue": "#42B883", + ".webp": "#3AFFDB", + ".xcplayground": "#D4843E", + ".xul": "#F16529", + ".yaml": "#FFFFFF", + ".yml": "#FFFFFF", + ".zsh": "#FFFFFF" + }, + "name_exact": { + ".bashprofile": "#FFFFFF", + ".bashrc": "#FFFFFF", + ".ds_store": "#FFFFFF", + ".gitconfig": "#FFFFFF", + ".gitignore": "#FFFFFF", + ".gitlab-ci.yml": "#D4843E", + ".gvimrc": "#8FAA54", + ".vimrc": "#8FAA54", + ".zshrc": "#FFFFFF", + "_gvimrc": "#8FAA54", + "_vimrc": "#8FAA54", + "cmakelists.txt": "#FFFFFF", + "docker-compose.yml": "#689FB6", + "dockerfile": "#689FB6", + "dropbox": "#689FB6", + "favicon.ico": "#F09F17", + "gruntfile.coffee": "#F09F17", + "gruntfile.js": "#F09F17", + "gruntfile.ls": "#F09F17", + "gulpfile.coffee": "#CB6F6F", + "gulpfile.js": "#CB6F6F", + "gulpfile.ls": "#CB6F6F", + "license": "#FFFFFF", + "makefile": "#FFFFFF", + "mix.lock": "#FFFFFF", + "node_modules": "#8FAA54", + "procfile": "#834F79", + "react.jsx": "#689FB6", + "typescript.jsx": "#689FB6", + "typescript.tsx": "#689FB6" + }, + "name_glob": { + ".*angular.*.js": "#AE403F", + ".*backbone.*.js": "#44788E", + ".*jquery.*.js": "#689FB6", + ".*materialize.*.css": "#EE6E73", + ".*materialize.*.js": "#EE6E73", + ".*mootools.*.js": "#FFFFFF", + ".*require.*.js": "#689FB6", + ".*vimrc.*": "#8FAA54", + "Vagrantfile": "#689FB6" + } + }, + "nerdtree_syntax_light": { + "ext_exact": { + ".ai": "#0e9ad6", + ".awk": "#000000", + ".bash": "#7cb086", + ".bat": "#000000", + ".bmp": "#c50024", + ".c": "#976049", + ".c++": "#976049", + ".cc": "#976049", + ".clj": "#7055ab", + ".cljc": "#7055ab", + ".cljs": "#7055ab", + ".coffee": "#6faacd", + ".conf": "#000000", + ".cp": "#976049", + ".cpp": "#976049", + ".cs": "#976049", + ".csh": "#000000", + ".css": "#976049", + ".cxx": "#976049", + ".d": "#51bfc0", + ".dart": "#976049", + ".db": "#976049", + ".diff": "#000000", + ".dump": "#976049", + ".edn": "#7055ab", + ".eex": "#7cb086", + ".ejs": "#0f60e8", + ".elm": "#000000", + ".erb": "#51bfc0", + ".erl": "#7cb086", + ".ex": "#7cb086", + ".exs": "#7cb086", + ".f#": "#bb8771", + ".fish": "#7055ab", + ".fs": "#976049", + ".fsi": "#976049", + ".fsscript": "#976049", + ".fsx": "#976049", + ".gif": "#c50024", + ".go": "#0a3f90", + ".h": "#976049", + ".hbs": "#2b7bc1", + ".hh": "#976049", + ".hpp": "#976049", + ".hrl": "#349090", + ".hs": "#0a3f90", + ".htm": "#0e9ad6", + ".html": "#0e9ad6", + ".hxx": "#976049", + ".ico": "#c50024", + ".ini": "#000000", + ".java": "#7cb086", + ".jl": "#6aa74d", + ".jpeg": "#c50024", + ".jpg": "#c50024", + ".js": "#0a3f90", + ".json": "#0a3f90", + ".jsx": "#976049", + ".ksh": "#000000", + ".leex": "#000000", + ".less": "#bb8771", + ".lhs": "#0a3f90", + ".lua": "#7cb086", + ".markdown": "#0f60e8", + ".md": "#0f60e8", + ".mdx": "#0f60e8", + ".mjs": "#0a3f90", + ".ml": "#0f60e8", + ".mli": "#0f60e8", + ".mustache": "#2b7bc1", + ".php": "#7cb086", + ".pl": "#976049", + ".pm": "#976049", + ".png": "#c50024", + ".pp": "#000000", + ".ps1": "#976049", + ".psb": "#bb8771", + ".psd": "#bb8771", + ".py": "#0f60e8", + ".pyc": "#0f60e8", + ".pyd": "#0f60e8", + ".pyo": "#0f60e8", + ".rb": "#51bfc0", + ".rlib": "#0e9ad6", + ".rmd": "#0f60e8", + ".rs": "#0e9ad6", + ".rss": "#0e9ad6", + ".sass": "#349090", + ".scala": "#51bfc0", + ".scss": "#349090", + ".sh": "#7cb086", + ".slim": "#2b7bc1", + ".sln": "#7cb086", + ".sql": "#bb8771", + ".styl": "#7055ab", + ".suo": "#7cb086", + ".swift": "#2b7bc1", + ".t": "#976049", + ".toml": "#000000", + ".ts": "#976049", + ".tsx": "#976049", + ".twig": "#7055ab", + ".vim": "#7055ab", + ".vue": "#bd477c", + ".webp": "#c50024", + ".xcplayground": "#2b7bc1", + ".xul": "#0e9ad6", + ".yaml": "#000000", + ".yml": "#000000", + ".zsh": "#000000" + }, + "name_exact": { + ".bashprofile": "#000000", + ".bashrc": "#000000", + ".ds_store": "#000000", + ".gitconfig": "#000000", + ".gitignore": "#000000", + ".gitlab-ci.yml": "#2b7bc1", + ".gvimrc": "#7055ab", + ".vimrc": "#7055ab", + ".zshrc": "#000000", + "_gvimrc": "#7055ab", + "_vimrc": "#7055ab", + "cmakelists.txt": "#000000", + "docker-compose.yml": "#976049", + "dockerfile": "#976049", + "dropbox": "#976049", + "favicon.ico": "#0f60e8", + "gruntfile.coffee": "#0f60e8", + "gruntfile.js": "#0f60e8", + "gruntfile.ls": "#0f60e8", + "gulpfile.coffee": "#349090", + "gulpfile.js": "#349090", + "gulpfile.ls": "#349090", + "license": "#000000", + "makefile": "#000000", + "mix.lock": "#000000", + "node_modules": "#7055ab", + "procfile": "#7cb086", + "react.jsx": "#976049", + "typescript.jsx": "#976049", + "typescript.tsx": "#976049" + }, + "name_glob": { + ".*angular.*.js": "#51bfc0", + ".*backbone.*.js": "#bb8771", + ".*jquery.*.js": "#976049", + ".*materialize.*.css": "#11918c", + ".*materialize.*.js": "#11918c", + ".*mootools.*.js": "#000000", + ".*require.*.js": "#976049", + ".*vimrc.*": "#7055ab", + "Vagrantfile": "#976049" + } + } + } +} \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/chadtree/assets/README.md b/config/neovim/store/lazy-plugins/chadtree/assets/README.md new file mode 100644 index 00000000..044d273c --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/assets/README.md @@ -0,0 +1 @@ +# Used to Generate Artifacts \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/chadtree/assets/aliases.yml b/config/neovim/store/lazy-plugins/chadtree/assets/aliases.yml new file mode 100644 index 00000000..c6105699 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/assets/aliases.yml @@ -0,0 +1,6 @@ +--- +icon_colours: {} +text_colours: + ext_exact: {} + name_exact: {} + name_glob: {} diff --git a/config/neovim/store/lazy-plugins/chadtree/assets/icon_base.yml b/config/neovim/store/lazy-plugins/chadtree/assets/icon_base.yml new file mode 100644 index 00000000..e90c764f --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/assets/icon_base.yml @@ -0,0 +1,36 @@ +--- +ascii: &ascii + default_icon: ● + ext_exact: {} + folder: + closed: ▶ + open: ▼ + link: + broken: -/-> + normal: -> + name_exact: {} + name_glob: {} + status: + active: ">" + inactive: " " + not_selected: " " + selected: "*" + +ascii_hollow: + <<: *ascii + default_icon: ○ + folder: + closed: ▷ + open: ▽ + +devicons: &devicons + link: + broken: 󰌸 + normal: 󰲔 + status: + active: ▶ + inactive: " " + not_selected: " " + selected: ✸ + +emoji: *devicons diff --git a/config/neovim/store/lazy-plugins/chadtree/chad_types.py b/config/neovim/store/lazy-plugins/chadtree/chad_types.py new file mode 100644 index 00000000..9113fb20 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chad_types.py @@ -0,0 +1,146 @@ +from dataclasses import dataclass +from enum import Enum, auto +from pathlib import Path +from typing import Mapping + +TOP_LEVEL = Path(__file__).resolve(strict=True).parent +ASSETS = TOP_LEVEL / "assets" +ARTIFACT = TOP_LEVEL / "artifacts" / "artifact.json" + + +""" +Icons +""" + + +Icon = str + + +@dataclass(frozen=True) +class _FolderIcons: + open: Icon + closed: Icon + + +@dataclass(frozen=True) +class _LinkIcons: + normal: Icon + broken: Icon + + +@dataclass(frozen=True) +class _StatusIcons: + active: Icon + inactive: Icon + selected: Icon + not_selected: Icon + + +@dataclass(frozen=True) +class IconGlyphs: + default_icon: Icon + folder: _FolderIcons + link: _LinkIcons + status: _StatusIcons + + ext_exact: Mapping[str, Icon] + name_exact: Mapping[str, Icon] + name_glob: Mapping[str, Icon] + + +@dataclass(frozen=True) +class IconGlyphSet: + ascii_hollow: IconGlyphs + ascii: IconGlyphs + devicons: IconGlyphs + emoji: IconGlyphs + + +class IconGlyphSetEnum(Enum): + ascii_hollow = auto() + ascii = auto() + devicons = auto() + emoji = auto() + + +""" +Icon Colours +""" + + +Hex = str +IconColours = Mapping[str, Hex] + + +@dataclass(frozen=True) +class IconColourSet: + github: IconColours + + +class IconColourSetEnum(Enum): + github = auto() + none = auto() + + +""" +LS Colours +""" + + +LS_COLOR = str + + +@dataclass(frozen=True) +class LSColourSet: + solarized_dark_256: LS_COLOR + solarized_dark: LS_COLOR + solarized_light: LS_COLOR + solarized_universal: LS_COLOR + nord: LS_COLOR + trapdoor: LS_COLOR + + +class LSColoursEnum(Enum): + env = auto() + solarized_dark_256 = auto() + solarized_dark = auto() + solarized_light = auto() + solarized_universal = auto() + nord = auto() + trapdoor = auto() + + +""" +Text Colours +""" + + +@dataclass(frozen=True) +class TextColours: + ext_exact: Mapping[str, Hex] + name_exact: Mapping[str, Hex] + name_glob: Mapping[str, Hex] + + +@dataclass(frozen=True) +class TextColourSet: + nerdtree_syntax_light: TextColours + nerdtree_syntax_dark: TextColours + + +class TextColourSetEnum(Enum): + nerdtree_syntax_light = auto() + nerdtree_syntax_dark = auto() + + +""" +Artifact +""" + + +@dataclass(frozen=True) +class Artifact: + icons: IconGlyphSet + ls_colours: LSColourSet + icon_colours: IconColourSet + text_colours: TextColourSet diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/__init__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/__main__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/__main__.py new file mode 100644 index 00000000..5c2399b1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/__main__.py @@ -0,0 +1,158 @@ +from argparse import ArgumentParser, Namespace +from asyncio import run as arun +from concurrent.futures import ThreadPoolExecutor +from contextlib import nullcontext, redirect_stderr, redirect_stdout +from io import StringIO +from pathlib import Path, PurePath +from subprocess import DEVNULL, STDOUT, CalledProcessError, run +from sys import ( + executable, + exit, + getswitchinterval, + setswitchinterval, + stderr, + version_info, +) +from textwrap import dedent +from typing import Any, Union +from webbrowser import open as open_w + +from .consts import GIL_SWITCH, IS_WIN, MIGRATION_URI, REQUIREMENTS, RT_DIR, RT_PY + +setswitchinterval(min(getswitchinterval(), GIL_SWITCH)) + +try: + from typing import Literal + + if version_info < (3, 8, 2): + raise ImportError() +except ImportError: + msg = "For python < 3.8.2 please install using the branch -- legacy" + print(msg, file=stderr) + open_w(MIGRATION_URI) + exit(1) + + +def _socket(arg: str) -> Any: + if arg.startswith("localhost:"): + host, _, port = arg.rpartition(":") + return host, int(port) + else: + return PurePath(arg) + + +def parse_args() -> Namespace: + parser = ArgumentParser() + + sub_parsers = parser.add_subparsers(dest="command", required=True) + + with nullcontext(sub_parsers.add_parser("run")) as p: + p.add_argument("--ppid", type=int) + p.add_argument("--socket", required=True, type=_socket) + p.add_argument("--xdg") + + with nullcontext(sub_parsers.add_parser("deps")) as p: + p.add_argument("--nvim", action="store_true") + p.add_argument("--xdg", nargs="?") + + return parser.parse_args() + + +args = parse_args() +command: Union[Literal["deps"], Literal["run"]] = args.command + +_XDG = Path(args.xdg) if args.xdg is not None else None + +_RT_DIR = _XDG / "chadrt" if _XDG else RT_DIR +_RT_PY = ( + (_RT_DIR / "Scripts" / "python.exe" if IS_WIN else _RT_DIR / "bin" / "python3") + if _XDG + else RT_PY +) +_LOCK_FILE = _RT_DIR / "requirements.lock" +_EXEC_PATH = Path(executable) +_EXEC_PATH = _EXEC_PATH.parent.resolve(strict=True) / _EXEC_PATH.name +_REQ = REQUIREMENTS.read_text() + +_IN_VENV = True + + +if command == "deps": + assert not _IN_VENV + + io_out = StringIO() + try: + from venv import EnvBuilder + + print("...", flush=True) + with redirect_stdout(io_out), redirect_stderr(io_out): + EnvBuilder( + system_site_packages=False, + with_pip=True, + upgrade=True, + symlinks=not IS_WIN, + clear=True, + ).create(_RT_DIR) + except (ImportError, CalledProcessError): + msg = "Please install python3-venv separately. (apt, yum, apk, etc)" + print(msg, io_out.getvalue(), file=stderr) + exit(1) + else: + proc = run( + ( + _RT_PY, + "-m", + "pip", + "install", + "--upgrade", + "--requirement", + REQUIREMENTS, + ), + stdin=DEVNULL, + stderr=STDOUT, + ) + if proc.returncode: + print("Installation failed, check :message", file=stderr) + exit(proc.returncode) + else: + _LOCK_FILE.write_text(_REQ) + msg = """ + --- + You can now use :CHADopen + """ + print(dedent(msg), file=stderr) + +elif command == "run": + try: + lock = _LOCK_FILE.read_text() + except Exception: + lock = "" + try: + if not _IN_VENV: + raise ImportError() + elif False: + raise ImportError() + else: + import pynvim_pp + import yaml + + from .client import init + except ImportError as e: + print(e) + msg = """ + Please update dependencies using :CHADdeps + - + - + Dependencies will be installed privately inside `chadtree/.vars` + `rm -rf chadtree/` will cleanly remove everything + """ + msg = dedent(msg) + print(msg, end="", file=stderr) + exit(1) + else: + with ThreadPoolExecutor() as th: + arun(init(args.socket, ppid=args.ppid, th=th)) + + +else: + assert False diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/__main__.py.orig b/config/neovim/store/lazy-plugins/chadtree/chadtree/__main__.py.orig new file mode 100644 index 00000000..aede27b6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/__main__.py.orig @@ -0,0 +1,158 @@ +from argparse import ArgumentParser, Namespace +from asyncio import run as arun +from concurrent.futures import ThreadPoolExecutor +from contextlib import nullcontext, redirect_stderr, redirect_stdout +from io import StringIO +from pathlib import Path, PurePath +from subprocess import DEVNULL, STDOUT, CalledProcessError, run +from sys import ( + executable, + exit, + getswitchinterval, + setswitchinterval, + stderr, + version_info, +) +from textwrap import dedent +from typing import Any, Union +from webbrowser import open as open_w + +from .consts import GIL_SWITCH, IS_WIN, MIGRATION_URI, REQUIREMENTS, RT_DIR, RT_PY + +setswitchinterval(min(getswitchinterval(), GIL_SWITCH)) + +try: + from typing import Literal + + if version_info < (3, 8, 2): + raise ImportError() +except ImportError: + msg = "For python < 3.8.2 please install using the branch -- legacy" + print(msg, file=stderr) + open_w(MIGRATION_URI) + exit(1) + + +def _socket(arg: str) -> Any: + if arg.startswith("localhost:"): + host, _, port = arg.rpartition(":") + return host, int(port) + else: + return PurePath(arg) + + +def parse_args() -> Namespace: + parser = ArgumentParser() + + sub_parsers = parser.add_subparsers(dest="command", required=True) + + with nullcontext(sub_parsers.add_parser("run")) as p: + p.add_argument("--ppid", type=int) + p.add_argument("--socket", required=True, type=_socket) + p.add_argument("--xdg") + + with nullcontext(sub_parsers.add_parser("deps")) as p: + p.add_argument("--nvim", action="store_true") + p.add_argument("--xdg", nargs="?") + + return parser.parse_args() + + +args = parse_args() +command: Union[Literal["deps"], Literal["run"]] = args.command + +_XDG = Path(args.xdg) if args.xdg is not None else None + +_RT_DIR = _XDG / "chadrt" if _XDG else RT_DIR +_RT_PY = ( + (_RT_DIR / "Scripts" / "python.exe" if IS_WIN else _RT_DIR / "bin" / "python3") + if _XDG + else RT_PY +) +_LOCK_FILE = _RT_DIR / "requirements.lock" +_EXEC_PATH = Path(executable) +_EXEC_PATH = _EXEC_PATH.parent.resolve(strict=True) / _EXEC_PATH.name +_REQ = REQUIREMENTS.read_text() + +_IN_VENV = _RT_PY == _EXEC_PATH + + +if command == "deps": + assert not _IN_VENV + + io_out = StringIO() + try: + from venv import EnvBuilder + + print("...", flush=True) + with redirect_stdout(io_out), redirect_stderr(io_out): + EnvBuilder( + system_site_packages=False, + with_pip=True, + upgrade=True, + symlinks=not IS_WIN, + clear=True, + ).create(_RT_DIR) + except (ImportError, CalledProcessError): + msg = "Please install python3-venv separately. (apt, yum, apk, etc)" + print(msg, io_out.getvalue(), file=stderr) + exit(1) + else: + proc = run( + ( + _RT_PY, + "-m", + "pip", + "install", + "--upgrade", + "--requirement", + REQUIREMENTS, + ), + stdin=DEVNULL, + stderr=STDOUT, + ) + if proc.returncode: + print("Installation failed, check :message", file=stderr) + exit(proc.returncode) + else: + _LOCK_FILE.write_text(_REQ) + msg = """ + --- + You can now use :CHADopen + """ + print(dedent(msg), file=stderr) + +elif command == "run": + try: + lock = _LOCK_FILE.read_text() + except Exception: + lock = "" + try: + if not _IN_VENV: + raise ImportError() + elif lock != _REQ: + raise ImportError() + else: + import pynvim_pp + import yaml + + from .client import init + except ImportError as e: + print(e) + msg = """ + Please update dependencies using :CHADdeps + - + - + Dependencies will be installed privately inside `chadtree/.vars` + `rm -rf chadtree/` will cleanly remove everything + """ + msg = dedent(msg) + print(msg, end="", file=stderr) + exit(1) + else: + with ThreadPoolExecutor() as th: + arun(init(args.socket, ppid=args.ppid, th=th)) + + +else: + assert False diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/_registry.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/_registry.py new file mode 100644 index 00000000..2d1f4873 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/_registry.py @@ -0,0 +1,54 @@ +from .transitions import ( + autocmds, + click, + collapse, + copy_name, + cut_copy, + delete, + filter, + focus, + help, + link, + marks, + new, + noop, + open_system, + quit, + refresh, + rename, + resize, + schedule_update, + selection, + stat, + toggle_exec, + toggle_open, + toggles, +) + +assert autocmds +assert click +assert collapse +assert copy_name +assert cut_copy +assert delete +assert filter +assert focus +assert help +assert link +assert marks +assert new +assert noop +assert open_system +assert quit +assert refresh +assert refresh +assert rename +assert resize +assert schedule_update +assert selection +assert stat +assert toggle_exec +assert toggle_open +assert toggles + +____ = None diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/client.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/client.py new file mode 100644 index 00000000..b5086908 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/client.py @@ -0,0 +1,217 @@ +from asyncio import ( + FIRST_COMPLETED, + AbstractEventLoop, + Event, + Lock, + Task, + create_task, + gather, + get_running_loop, + wait, + wrap_future, +) +from concurrent.futures import Future, ThreadPoolExecutor +from contextlib import AbstractAsyncContextManager, suppress +from functools import wraps +from logging import DEBUG as DEBUG_LVL +from logging import INFO +from multiprocessing import cpu_count +from pathlib import Path +from platform import uname +from string import Template +from sys import executable, exit +from textwrap import dedent +from time import monotonic +from typing import Any, Optional, Sequence, cast + +from pynvim_pp.highlight import highlight +from pynvim_pp.logging import log, suppress_and_log +from pynvim_pp.nvim import Nvim, conn +from pynvim_pp.rpc_types import ( + Method, + MsgType, + NvimError, + RPCallable, + RPClient, + ServerAddr, +) +from pynvim_pp.types import NoneType +from std2.asyncio import cancel +from std2.cell import RefCell +from std2.contextlib import nullacontext +from std2.pickle.types import DecodeError +from std2.platform import OS, os +from std2.sched import aticker +from std2.sys import autodie + +from ._registry import ____ +from .consts import DEBUG, RENDER_RETRIES +from .registry import autocmd, dequeue_event, enqueue_event, rpc +from .settings.load import initial as initial_settings +from .settings.localization import init as init_locale +from .state.load import initial as initial_state +from .state.types import State +from .timeit import timeit +from .transitions.autocmds import setup +from .transitions.redraw import redraw +from .transitions.schedule_update import scheduled_update +from .transitions.types import Stage + +assert ____ or True + +_CB = RPCallable[Optional[Stage]] + + +def _autodie(ppid: int) -> AbstractAsyncContextManager: + if os is OS.windows: + return nullacontext(None) + else: + return autodie(ppid) + + +async def _profile(t1: float) -> None: + t2 = monotonic() + info = uname() + msg = f""" + First msg {int((t2 - t1) * 1000)}ms + Arch {info.machine} + Processor {info.processor} + Cores {cpu_count()} + System {info.system} + Version {info.version} + Python {Path(executable).resolve(strict=True)} + """ + await Nvim.write(dedent(msg)) + + +async def _sched(ref: RefCell[State]) -> None: + await enqueue_event(False, method=scheduled_update.method, params=(True,)) + + async for _ in aticker(ref.val.settings.polling_rate, immediately=False): + if ref.val.vim_focus: + await enqueue_event(False, method=scheduled_update.method) + + +def _trans(handler: _CB) -> _CB: + @wraps(handler) + async def f(*params: Any) -> None: + await enqueue_event(True, method=handler.method, params=params) + + return cast(_CB, f) + + +async def _default(_: MsgType, method: Method, params: Sequence[Any]) -> None: + await enqueue_event(True, method=method, params=params) + + +async def _go(loop: AbstractEventLoop, client: RPClient) -> None: + th = ThreadPoolExecutor() + atomic, handlers = rpc.drain() + try: + settings = await initial_settings(handlers.values()) + except DecodeError as e: + tpl = """ + Some options may have changed. + See help doc on Github under [docs/CONFIGURATION.md] + + + ${e} + """ + ms = Template(dedent(tpl)).substitute(e=e) + await Nvim.write(ms, error=True) + exit(1) + else: + hl = highlight(*settings.view.hl_context.groups) + await (atomic + autocmd.drain() + hl).commit(NoneType) + state = RefCell(await initial_state(settings, th=th)) + + init_locale(settings.lang) + with suppress_and_log(): + await setup(settings) + + for f in handlers.values(): + ff = _trans(f) + client.register(ff) + + staged = RefCell[Optional[Stage]](None) + event = Event() + lock = Lock() + + async def step(method: Method, params: Sequence[Any]) -> None: + if handler := cast(Optional[_CB], handlers.get(method)): + with suppress_and_log(): + async with lock: + if stage := await handler(state.val, *params): + state.val = stage.state + staged.val = stage + event.set() + else: + assert False, (method, params) + + async def c1() -> None: + transcient: Optional[Task] = None + get: Optional[Task] = None + try: + while True: + with suppress_and_log(): + get = create_task(dequeue_event()) + if transcient: + await wait((transcient, get), return_when=FIRST_COMPLETED) + if not transcient.done(): + with timeit("transcient"): + await cancel(transcient) + transcient = None + + sync, method, params = await get + task = step(method, params=params) + if sync: + with timeit(method): + await task + else: + transcient = create_task(task) + finally: + await cancel( + *(get or loop.create_future(), transcient or loop.create_future()) + ) + + async def c2() -> None: + t1, has_drawn = monotonic(), False + + while True: + await event.wait() + with suppress_and_log(): + try: + if stage := staged.val: + state = stage.state + + for _ in range(RENDER_RETRIES - 1): + with suppress(NvimError): + await redraw(state, focus=stage.focus) + break + else: + try: + await redraw(state, focus=stage.focus) + except NvimError as e: + log.warn("%s", e) + + if settings.profiling and not has_drawn: + has_drawn = True + await _profile(t1=t1) + finally: + event.clear() + + await gather(c1(), c2(), _sched(state)) + + +async def init(socket: ServerAddr, ppid: int, th: ThreadPoolExecutor) -> None: + loop = get_running_loop() + loop.set_default_executor(th) + log.setLevel(DEBUG_LVL if DEBUG else INFO) + + die: Future = Future() + + async def cont() -> None: + async with conn(die, socket=socket, default=_default) as client: + await _go(loop, client=client) + + await gather(wrap_future(die), cont()) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/consts.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/consts.py new file mode 100644 index 00000000..1fff3629 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/consts.py @@ -0,0 +1,65 @@ +from os import environ, name +from pathlib import Path + +from chad_types import TOP_LEVEL + +GIL_SWITCH = 1 / 1000 +IS_WIN = name == "nt" +REQUIREMENTS = TOP_LEVEL / "requirements.txt" + +DEBUG = "CHADTREE_DEBUG" in environ + +BATCH_FACTOR = 88 +RENDER_RETRIES = 3 + +FM_FILETYPE = "CHADTree" +FM_HL_PREFIX = "chadtree" +URI_SCHEME = FM_FILETYPE.casefold() + +DEFAULT_LANG = "en" +LANG_ROOT = TOP_LEVEL / "locale" +CONFIG_YML = TOP_LEVEL / "config" / "defaults.yml" +SETTINGS_VAR = "chadtree_settings" + +""" +STORAGE +""" + +_VARS = Path.home() / ".cache/chadtree/vars" +RT_DIR = _VARS / "runtime" +RT_PY = RT_DIR / "Scripts" / "python.exe" if IS_WIN else RT_DIR / "bin" / "python3" +SESSION_DIR = _VARS / "sessions" + + +""" +Docs +""" + + +_DOCS_URI_BASE = "https://github.com/ms-jpq/chadtree/blob/chad/docs" +_DOCS_DIR = TOP_LEVEL / "docs" + + +_README_md = "README.md" +README_MD = _DOCS_DIR / _README_md +README_URI = f"{_DOCS_URI_BASE}" + +_FEATURES_md = "FEATURES.md" +FEATURES_MD = _DOCS_DIR / _FEATURES_md +FEATURES_URI = f"{_DOCS_URI_BASE}/{_FEATURES_md}" + +_KEYBIND_md = "KEYBIND.md" +KEYBIND_MD = _DOCS_DIR / _KEYBIND_md +KEYBIND_URI = f"{_DOCS_URI_BASE}/{_KEYBIND_md}" + +_CONFIGURATION_md = "CONFIGURATION.md" +CONFIGURATION_MD = _DOCS_DIR / _CONFIGURATION_md +CONFIGURATION_URI = f"{_DOCS_URI_BASE}/{_CONFIGURATION_md}" + +_THEME_md = "THEME.md" +THEME_MD = _DOCS_DIR / _THEME_md +THEME_URI = f"{_DOCS_URI_BASE}/{_THEME_md}" + +_MIGRATION_md = "MIGRATION.md" +MIGRATION_MD = _DOCS_DIR / _MIGRATION_md +MIGRATION_URI = f"{_DOCS_URI_BASE}/{_MIGRATION_md}" diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/__init__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/cartographer.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/cartographer.py new file mode 100644 index 00000000..06734879 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/cartographer.py @@ -0,0 +1,230 @@ +from asyncio import sleep +from concurrent.futures import ThreadPoolExecutor +from contextlib import suppress +from fnmatch import fnmatch +from os import DirEntry, scandir, stat, stat_result +from os.path import normcase +from pathlib import Path, PurePath +from stat import ( + S_IFDOOR, + S_ISBLK, + S_ISCHR, + S_ISDIR, + S_ISFIFO, + S_ISGID, + S_ISLNK, + S_ISREG, + S_ISSOCK, + S_ISUID, + S_ISVTX, + S_IWOTH, + S_IXUSR, +) +from typing import ( + AbstractSet, + Iterator, + Mapping, + MutableMapping, + Optional, + Tuple, + Union, + cast, +) + +from std2.itertools import batched +from std2.pathlib import is_relative_to + +from ..consts import BATCH_FACTOR +from ..state.executor import AsyncExecutor +from ..state.types import Index +from ..timeit import timeit +from .nt import is_junction +from .types import Ignored, Mode, Node + +_FILE_MODES: Mapping[int, Mode] = { + S_IXUSR: Mode.executable, + S_IFDOOR: Mode.door, + S_ISGID: Mode.set_gid, + S_ISUID: Mode.set_uid, + S_ISVTX: Mode.sticky, + S_IWOTH: Mode.other_writable, + S_IWOTH | S_ISVTX: Mode.sticky_other_writable, +} + + +def _iter( + dirent: Union[PurePath, DirEntry[str]], follow: bool, index: Index, lv: int = 0 +) -> Iterator[PurePath]: + if not lv: + yield PurePath(dirent) + with suppress(NotADirectoryError, FileNotFoundError, PermissionError): + with scandir(dirent) as dirents: + for child in dirents: + yield (path := PurePath(child)) + if child.is_dir(follow_symlinks=follow) and path in index: + yield from _iter(child, follow=follow, index=index, lv=lv + 1) + + +def _fs_modes(stat: stat_result) -> Iterator[Mode]: + st_mode = stat.st_mode + if S_ISDIR(st_mode): + yield Mode.folder + if S_ISREG(st_mode): + yield Mode.file + if S_ISFIFO(st_mode): + yield Mode.pipe + if S_ISSOCK(st_mode): + yield Mode.socket + if S_ISCHR(st_mode): + yield Mode.char_device + if S_ISBLK(st_mode): + yield Mode.block_device + if stat.st_nlink > 1: + yield Mode.multi_hardlink + for bit, mode in _FILE_MODES.items(): + if bit and st_mode & bit == bit: + yield mode + + +def _fs_stat(path: PurePath) -> Tuple[AbstractSet[Mode], Optional[PurePath]]: + try: + info = stat(path, follow_symlinks=False) + except (FileNotFoundError, PermissionError): + return {Mode.orphan_link}, None + else: + if S_ISLNK(info.st_mode) or is_junction(info): + try: + pointed = Path(path).resolve(strict=True) + link_info = stat(pointed, follow_symlinks=False) + except (FileNotFoundError, NotADirectoryError, RuntimeError): + return {Mode.orphan_link}, None + else: + mode = {*_fs_modes(link_info)} + return mode | {Mode.link}, pointed + else: + mode = {*_fs_modes(info)} + return mode, None + + +def _fs_node(path: PurePath) -> Node: + mode, pointed = _fs_stat(path) + node = Node( + path=path, + mode=mode, + pointed=pointed, + children={}, + ) + return node + + +def _iter_single_nodes( + th: ThreadPoolExecutor, root: PurePath, follow: bool, index: Index +) -> Iterator[Node]: + with timeit("fs->_iter"): + dir_stream = batched(_iter(root, index=index, follow=follow), n=BATCH_FACTOR) + for seq in th.map(lambda x: tuple(map(_fs_node, x)), dir_stream): + yield from seq + + +async def _new( + th: ThreadPoolExecutor, root: PurePath, follow_links: bool, index: Index +) -> Node: + nodes: MutableMapping[PurePath, Node] = {} + + for idx, node in enumerate( + _iter_single_nodes(th, root=root, follow=follow_links, index=index), start=1 + ): + if idx % BATCH_FACTOR == 0: + await sleep(0) + nodes[node.path] = node + if parent := nodes.get(node.path.parent): + if parent == node: + continue + cast(MutableMapping[PurePath, Node], parent.children)[node.path] = node + + return nodes[root] + + +def _cross_over(root: PurePath, invalid: PurePath) -> bool: + return is_relative_to(root, invalid) or is_relative_to(invalid, root) + + +async def _update( + th: ThreadPoolExecutor, + root: Node, + follow_links: bool, + index: Index, + invalidate_dirs: AbstractSet[PurePath], +) -> Node: + if any((_cross_over(root.path, invalid=invalid) for invalid in invalidate_dirs)): + return await _new(th, root=root.path, follow_links=follow_links, index=index) + else: + children: MutableMapping[PurePath, Node] = {} + for path, node in root.children.items(): + new_node = await _update( + th, + root=node, + follow_links=follow_links, + index=index, + invalidate_dirs=invalidate_dirs, + ) + children[path] = new_node + return Node( + path=root.path, + mode=root.mode, + pointed=root.pointed, + children=children, + ) + + +async def new( + exec: AsyncExecutor, root: PurePath, follow_links: bool, index: Index +) -> Node: + with timeit("fs->new"): + return await exec.submit( + _new(exec.threadpool, root=root, follow_links=follow_links, index=index) + ) + + +async def update( + exec: AsyncExecutor, + root: Node, + *, + follow_links: bool, + index: Index, + invalidate_dirs: AbstractSet[PurePath], +) -> Node: + with timeit("fs->_update"): + try: + return await exec.submit( + _update( + exec.threadpool, + root=root, + follow_links=follow_links, + index=index, + invalidate_dirs=invalidate_dirs, + ) + ) + except FileNotFoundError: + return await new( + exec, follow_links=follow_links, root=root.path, index=index + ) + + +def user_ignored(node: Node, ignores: Ignored) -> bool: + return ( + node.path.name in ignores.name_exact + or any(fnmatch(node.path.name, pattern) for pattern in ignores.name_glob) + or any(fnmatch(normcase(node.path), pattern) for pattern in ignores.path_glob) + ) + + +def is_dir(node: Node) -> bool: + return Mode.folder in node.mode + + +def act_like_dir(node: Node, follow_links: bool) -> bool: + if node.pointed and not follow_links: + return False + else: + return is_dir(node) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/nt.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/nt.py new file mode 100644 index 00000000..a5d687c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/nt.py @@ -0,0 +1,13 @@ +import sys +from os import stat_result +from stat import S_ISDIR + +if sys.platform == "win32": + + def is_junction(st: stat_result) -> bool: + return bool(S_ISDIR(st.st_mode) and st.st_reparse_tag) + +else: + + def is_junction(st: stat_result) -> bool: + return False diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/ops.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/ops.py new file mode 100644 index 00000000..551b41d2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/ops.py @@ -0,0 +1,242 @@ +from asyncio import Lock, gather +from dataclasses import dataclass +from datetime import datetime +from functools import lru_cache +from itertools import chain +from os import makedirs, readlink +from os import remove as rm +from os import stat, symlink +from os.path import isdir, isfile, normpath +from pathlib import Path, PurePath +from shutil import copy2, copytree +from shutil import move as mv +from shutil import rmtree +from shutil import which as _which +from stat import S_ISDIR, S_ISLNK, filemode +from typing import AbstractSet, Iterable, Mapping, Optional + +from std2.asyncio import to_thread +from std2.stat import RW_R__R__, RWXR_XR_X + +from .nt import is_junction + +_FOLDER_MODE = RWXR_XR_X +_FILE_MODE = RW_R__R__ + + +def ancestors(*paths: PurePath) -> AbstractSet[PurePath]: + return {*chain.from_iterable(path.parents for path in paths)} + + +def unify_ancestors(paths: AbstractSet[PurePath]) -> AbstractSet[PurePath]: + return {p for p in paths if ancestors(p).isdisjoint(paths)} + + +@dataclass(frozen=True) +class FSstat: + permissions: str + user: str + group: str + date_mod: datetime + size: int + link: Optional[PurePath] + + +@lru_cache(maxsize=None) +def lock() -> Lock: + return Lock() + + +try: + from grp import getgrgid + from pwd import getpwuid + + def _get_username(uid: int) -> str: + try: + return getpwuid(uid).pw_name + except KeyError: + return str(uid) + + def _get_groupname(gid: int) -> str: + try: + return getgrgid(gid).gr_name + except KeyError: + return str(gid) + +except ImportError: + + def _get_username(uid: int) -> str: + return str(uid) + + def _get_groupname(gid: int) -> str: + return str(gid) + + +@lru_cache(maxsize=None) +def which(path: PurePath) -> Optional[PurePath]: + if bin := _which(path): + return PurePath(bin) + else: + return None + + +async def fs_stat(path: PurePath) -> FSstat: + def cont() -> FSstat: + stats = stat(path, follow_symlinks=False) + permissions = filemode(stats.st_mode) + user = _get_username(stats.st_uid) + group = _get_groupname(stats.st_gid) + date_mod = datetime.fromtimestamp(stats.st_mtime) + size = stats.st_size + try: + link = ( + readlink(path) if S_ISLNK(stats.st_mode) or is_junction(stats) else None + ) + except OSError: + plink = None + else: + plink = PurePath(link) if link else None + fs_stat = FSstat( + permissions=permissions, + user=user, + group=group, + date_mod=date_mod, + size=size, + link=plink, + ) + return fs_stat + + return await to_thread(cont) + + +async def resolve(path: PurePath, strict: bool) -> Path: + def cont() -> Path: + return Path(path).resolve(strict=strict) + + return await to_thread(cont) + + +async def exists(path: PurePath, follow: bool) -> bool: + def cont() -> bool: + try: + stat(path, follow_symlinks=follow) + except (OSError, ValueError): + return False + else: + return True + + return await to_thread(cont) + + +async def exists_many( + paths: Iterable[PurePath], follow: bool +) -> Mapping[PurePath, bool]: + existence = await gather(*(exists(path, follow=follow) for path in paths)) + return {path: exi for path, exi in zip(paths, existence)} + + +async def is_dir(path: PurePath) -> bool: + return await to_thread(lambda: isdir(path)) + + +async def is_file(path: PurePath) -> bool: + return await to_thread(lambda: isfile(path)) + + +def _mkdir_p(path: PurePath) -> None: + makedirs(path, mode=_FOLDER_MODE, exist_ok=True) + + +async def _mkdir(path: PurePath) -> None: + def cont() -> None: + _mkdir_p(path) + + await to_thread(cont) + + +async def mkdir(paths: Iterable[PurePath]) -> None: + await gather(*map(_mkdir, paths)) + + +async def _new(path: PurePath) -> None: + def cont() -> None: + makedirs(path.parent, mode=_FOLDER_MODE, exist_ok=True) + Path(path).touch(mode=_FILE_MODE, exist_ok=True) + + await to_thread(cont) + + +async def new(paths: Iterable[PurePath]) -> None: + async with lock(): + await gather(*map(_new, paths)) + + +async def _rename(src: PurePath, dst: PurePath) -> None: + def cont() -> None: + makedirs(dst.parent, mode=_FOLDER_MODE, exist_ok=True) + mv(normpath(src), normpath(dst)) + + await to_thread(cont) + + +async def rename(operations: Mapping[PurePath, PurePath]) -> None: + async with lock(): + await gather(*(_rename(src, dst) for src, dst in operations.items())) + + +async def _remove(path: PurePath) -> None: + def cont() -> None: + stats = stat(path, follow_symlinks=False) + if S_ISDIR(stats.st_mode): + rmtree(path) + else: + rm(path) + + await to_thread(cont) + + +async def remove(paths: Iterable[PurePath]) -> None: + async with lock(): + await gather(*map(_remove, paths)) + + +async def _cut(src: PurePath, dst: PurePath) -> None: + def cont() -> None: + mv(normpath(src), normpath(dst)) + + await to_thread(cont) + + +async def cut(operations: Mapping[PurePath, PurePath]) -> None: + async with lock(): + await gather(*(_cut(src, dst) for src, dst in operations.items())) + + +async def _copy(src: PurePath, dst: PurePath) -> None: + def cont() -> None: + stats = stat(src, follow_symlinks=False) + if S_ISDIR(stats.st_mode): + copytree(src, dst, symlinks=True, dirs_exist_ok=True) + else: + copy2(src, dst, follow_symlinks=False) + + await to_thread(cont) + + +async def copy(operations: Mapping[PurePath, PurePath]) -> None: + async with lock(): + await gather(*(_copy(src, dst) for src, dst in operations.items())) + + +async def _link(src: PurePath, dst: PurePath) -> None: + def cont() -> None: + target_is_directory = isdir(src) + _mkdir_p(dst.parent) + symlink(normpath(src), normpath(dst), target_is_directory=target_is_directory) + + await to_thread(cont) + + +async def link(operations: Mapping[PurePath, PurePath]) -> None: + async with lock(): + await gather(*(_link(src, dst) for dst, src in operations.items())) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/types.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/types.py new file mode 100644 index 00000000..808513c1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/fs/types.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from enum import IntEnum, auto, unique +from pathlib import PurePath +from typing import AbstractSet, Any, Mapping, Optional, Sequence + + +# https://github.com/coreutils/coreutils/blob/master/src/ls.c +@unique +class Mode(IntEnum): + orphan_link = auto() + link = auto() + + pipe = auto() + socket = auto() + block_device = auto() + char_device = auto() + door = auto() + + sticky_other_writable = auto() + other_writable = auto() + sticky = auto() + folder = auto() + + set_uid = auto() + set_gid = auto() + file_w_capacity = auto() + executable = auto() + multi_hardlink = auto() + file = auto() + + +@dataclass +class _RenderCache: + sort_by: Optional[Sequence[Any]] = None + + +@dataclass(frozen=True) +class Node: + mode: AbstractSet[Mode] + path: PurePath + pointed: Optional[PurePath] + children: Mapping[PurePath, Node] + cache: _RenderCache = field(default_factory=_RenderCache) + + +@dataclass(frozen=True) +class Ignored: + name_exact: AbstractSet[str] + name_glob: Sequence[str] + path_glob: Sequence[str] diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/__init__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/diagnostics.lua b/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/diagnostics.lua new file mode 100644 index 00000000..ea70dabd --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/diagnostics.lua @@ -0,0 +1,30 @@ +(function(_) + if vim.diagnostic then + local diagnostics = vim.diagnostic.get(nil, nil) + vim.validate({diagnostics = {diagnostics, "table"}}) + local acc = {} + for _, row in pairs(diagnostics) do + local buf = row.bufnr + local severity = tostring(row.severity) + vim.validate( + { + buf = {buf, "number"}, + row_severity = {row.severity, "number"} + } + ) + if not acc[buf] then + acc[buf] = {} + end + if not acc[buf][severity] then + acc[buf][severity] = 0 + end + acc[buf][severity] = acc[buf][severity] + 1 + end + local acc2 = {} + for buf, warnings in pairs(acc) do + local path = vim.api.nvim_buf_get_name(buf) + acc2[path] = warnings + end + return acc2 + end +end)(...) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/diagnostics.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/diagnostics.py new file mode 100644 index 00000000..979d6874 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/diagnostics.py @@ -0,0 +1,44 @@ +import sys +from collections import Counter +from pathlib import Path, PurePath +from typing import Mapping, MutableMapping, cast + +from pynvim_pp.nvim import Nvim +from pynvim_pp.types import NoneType + +from ..fs.ops import ancestors +from ..state.types import Diagnostics + +_LUA = ( + Path(__file__).resolve(strict=True).with_name("diagnostics.lua").read_text("UTF-8") +) + +if sys.version_info < (3, 9): + _C = Counter +else: + _C = Counter[int] + + +async def poll(min_severity: int) -> Diagnostics: + diagnostics: Mapping[str, Mapping[str, int]] = cast( + Mapping[str, Mapping[str, int]], await Nvim.fn.luaeval(NoneType, _LUA, ()) + ) + + raw = { + PurePath(path): Counter( + { + s: count + for severity, count in (counts or {}).items() + if (s := int(severity)) <= min_severity + } + ) + for path, counts in (diagnostics or {}).items() + } + + acc: MutableMapping[PurePath, _C] = {} + for path, counts in raw.items(): + for parent in ancestors(path): + c = acc.setdefault(parent, Counter()) + c += counts + + return {**acc, **raw} diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/notify.lua b/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/notify.lua new file mode 100644 index 00000000..cd53990b --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/notify.lua @@ -0,0 +1,9 @@ +(function(args) + local method, params = unpack(args) + if vim.lsp then + local clients = (vim.lsp.get_clients or vim.lsp.get_active_clients)() + for _, client in pairs(clients) do + client.notify(method, params) + end + end +end)(...) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/notify.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/notify.py new file mode 100644 index 00000000..9d3025ba --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/lsp/notify.py @@ -0,0 +1,31 @@ +from pathlib import Path, PurePath +from typing import Any, Iterable, Mapping + +from pynvim_pp.nvim import Nvim +from pynvim_pp.types import NoneType + +_LUA = Path(__file__).resolve(strict=True).with_name("notify.lua").read_text("UTF-8") + + +async def _notify(method: str, params: Any) -> None: + await Nvim.fn.luaeval(NoneType, _LUA, (method, params)) + + +async def lsp_created(paths: Iterable[PurePath]) -> None: + params = {"files": tuple({"uri": path.as_uri()} for path in paths)} + await _notify("workspace/didCreateFiles", params=params) + + +async def lsp_removed(paths: Iterable[PurePath]) -> None: + params = {"files": tuple({"uri": path.as_uri()} for path in paths)} + await _notify("workspace/didDeleteFiles", params=params) + + +async def lsp_moved(paths: Mapping[PurePath, PurePath]) -> None: + params = { + "files": tuple( + {"oldUri": old.as_uri(), "newUri": new.as_uri()} + for old, new in paths.items() + ) + } + await _notify("workspace/didRenameFiles", params=params) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/nvim/__init__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/nvim/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/nvim/markers.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/nvim/markers.py new file mode 100644 index 00000000..efb03d72 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/nvim/markers.py @@ -0,0 +1,48 @@ +from asyncio import gather +from collections import Counter +from itertools import chain +from pathlib import PurePath +from typing import AbstractSet, Mapping, MutableMapping, MutableSet, Sequence, cast + +from pynvim_pp.atomic import Atomic +from pynvim_pp.buffer import Buffer +from pynvim_pp.nvim import Marker, Nvim +from pynvim_pp.types import NoneType + +from ..fs.ops import ancestors +from .types import Markers + + +async def _bookmarks() -> Mapping[PurePath, AbstractSet[Marker]]: + acc: MutableMapping[PurePath, MutableSet[Marker]] = {} + bookmarks = await Nvim.list_bookmarks() + for marker, (path, _, _) in bookmarks.items(): + if path: + for marked_path in chain((path,), ancestors(path)): + marks = acc.setdefault(marked_path, set()) + marks.add(marker) + + return acc + + +async def _quickfix() -> Mapping[PurePath, int]: + qflist = cast(Sequence[Mapping[str, int]], await Nvim.fn.getqflist(NoneType)) + + atomic = Atomic() + for q in qflist: + bufnr = q["bufnr"] + buf = Buffer.from_int(bufnr) + atomic.buf_get_name(buf) + + bufnames = cast(Sequence[str], await atomic.commit(NoneType)) + filenames = tuple(map(PurePath, bufnames)) + parents = (ancestor for fullname in filenames for ancestor in ancestors(fullname)) + locations = Counter(chain(filenames, parents)) + + return locations + + +async def markers() -> Markers: + qf, bm = await gather(_quickfix(), _bookmarks()) + markers = Markers(quick_fix=qf, bookmarks=bm) + return markers diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/nvim/types.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/nvim/types.py new file mode 100644 index 00000000..5a321540 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/nvim/types.py @@ -0,0 +1,11 @@ +from dataclasses import dataclass +from pathlib import PurePath +from typing import AbstractSet, Mapping + +from pynvim_pp.nvim import Marker + + +@dataclass(frozen=True) +class Markers: + quick_fix: Mapping[PurePath, int] + bookmarks: Mapping[PurePath, AbstractSet[Marker]] diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/registry.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/registry.py new file mode 100644 index 00000000..e6e43b61 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/registry.py @@ -0,0 +1,34 @@ +from asyncio import Queue +from functools import lru_cache +from typing import Any, Awaitable, Callable, Sequence, Tuple + +from pynvim_pp.autocmd import AutoCMD +from pynvim_pp.handler import RPC +from pynvim_pp.rpc_types import Method + +_MSG = Tuple[bool, Method, Sequence[Any]] + +NAMESPACE = "CHAD" + + +def _name_gen(fn: Callable[..., Awaitable[Any]]) -> str: + return fn.__qualname__.lstrip("_").capitalize() + + +@lru_cache(maxsize=None) +def queue() -> Queue: + return Queue() + + +autocmd = AutoCMD() +rpc = RPC(NAMESPACE, name_gen=_name_gen) + + +async def enqueue_event(sync: bool, method: Method, params: Sequence[Any] = ()) -> None: + msg = (sync, method, params) + await queue().put(msg) + + +async def dequeue_event() -> _MSG: + msg: _MSG = await queue().get() + return msg diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/__init__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/load.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/load.py new file mode 100644 index 00000000..9b17d889 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/load.py @@ -0,0 +1,178 @@ +from dataclasses import dataclass +from enum import Enum, auto +from locale import strxfrm +from typing import ( + AbstractSet, + Any, + Iterable, + Mapping, + Optional, + Sequence, + SupportsFloat, + Union, + cast, +) + +from pynvim_pp.atomic import Atomic +from pynvim_pp.nvim import Nvim +from pynvim_pp.rpc_types import RPCallable +from pynvim_pp.types import NoneType +from pynvim_pp.window import Window +from std2.configparser import hydrate +from std2.graphlib import merge +from std2.pickle.decoder import new_decoder +from std2.pickle.types import DecodeError +from yaml import safe_load + +from chad_types import ( + ARTIFACT, + Artifact, + IconColourSetEnum, + IconGlyphSetEnum, + LSColoursEnum, + TextColourSetEnum, +) + +from ..consts import CONFIG_YML, SETTINGS_VAR +from ..fs.types import Ignored +from ..registry import NAMESPACE +from ..view.load import load_theme +from ..view.types import HLGroups, Sortby, ViewOptions +from .types import MimetypeOptions, Settings, VersionCtlOpts + + +class _OpenDirection(Enum): + left = auto() + right = auto() + + +@dataclass(frozen=True) +class _UserOptions: + close_on_open: bool + follow: bool + follow_links: bool + follow_ignore: bool + lang: Optional[str] + mimetypes: MimetypeOptions + page_increment: int + polling_rate: SupportsFloat + session: bool + show_hidden: bool + min_diagnostics_severity: int + version_control: VersionCtlOpts + + +@dataclass(frozen=True) +class _UserTheme: + highlights: HLGroups + icon_glyph_set: IconGlyphSetEnum + icon_colour_set: IconColourSetEnum + text_colour_set: Union[LSColoursEnum, TextColourSetEnum] + discrete_colour_map: Mapping[str, str] + + +@dataclass(frozen=True) +class _UserView: + open_direction: _OpenDirection + width: int + sort_by: Sequence[Sortby] + time_format: str + window_options: Mapping[str, Union[bool, str]] + + +@dataclass(frozen=True) +class _UserConfig: + keymap: Mapping[str, AbstractSet[str]] + options: _UserOptions + idle_timeout: SupportsFloat + ignore: Ignored + view: _UserView + theme: _UserTheme + xdg: bool + profiling: bool + + +async def initial(specs: Iterable[RPCallable]) -> Settings: + a_decode = new_decoder[Artifact](Artifact) + c_decode = new_decoder[_UserConfig](_UserConfig) + + win = await Window.get_current() + artifacts = a_decode(safe_load(ARTIFACT.read_text("UTF-8"))) + + user_config = cast( + Mapping[str, Any], await Nvim.vars.get(NoneType, SETTINGS_VAR) or {} + ) + config = c_decode( + merge( + safe_load(CONFIG_YML.read_text("UTF-8")), hydrate(user_config), replace=True + ) + ) + options, view, theme = config.options, config.view, config.theme + + atomic = Atomic() + for opt in view.window_options: + atomic.win_get_option(win, opt) + win_opts = cast(Sequence[Union[bool, str]], await atomic.commit(NoneType)) + win_actual_opts = {k: v for k, v in zip(view.window_options, win_opts)} + + icons, hl_context = load_theme( + artifact=artifacts, + particular_mappings=theme.highlights, + discrete_colours=theme.discrete_colour_map, + icon_set=theme.icon_glyph_set, + icon_colour_set=theme.icon_colour_set, + text_colour_set=theme.text_colour_set, + ) + + use_icons = theme.icon_glyph_set not in { + IconGlyphSetEnum.ascii, + IconGlyphSetEnum.ascii_hollow, + } + + view_opts = ViewOptions( + hl_context=hl_context, + icons=icons, + sort_by=tuple(view.sort_by), + use_icons=use_icons, + time_fmt=view.time_format, + ) + + keymap = {f"{NAMESPACE}.{k.capitalize()}": v for k, v in config.keymap.items()} + legal_keys = {f"{NAMESPACE}.{spec.method.capitalize()}" for spec in specs} + extra_keys = keymap.keys() - legal_keys + + if extra_keys: + raise DecodeError( + path=(_UserOptions, sorted(legal_keys, key=strxfrm)), + actual=None, + missing_keys=(), + extra_keys=sorted(extra_keys, key=strxfrm), + ) + + else: + settings = Settings( + close_on_open=options.close_on_open, + follow=options.follow, + follow_links=options.follow_links, + follow_ignore=options.follow_ignore, + ignores=config.ignore, + idle_timeout=float(config.idle_timeout), + keymap=keymap, + lang=options.lang, + mime=options.mimetypes, + min_diagnostics_severity=options.min_diagnostics_severity, + open_left=view.open_direction is _OpenDirection.left, + page_increment=options.page_increment, + polling_rate=float(options.polling_rate), + session=options.session, + show_hidden=options.show_hidden, + version_ctl=options.version_control, + view=view_opts, + width=view.width, + win_actual_opts=win_actual_opts, + win_local_opts=view.window_options, + xdg=config.xdg, + profiling=config.profiling, + ) + + return settings diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/localization.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/localization.py new file mode 100644 index 00000000..dc8b058f --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/localization.py @@ -0,0 +1,46 @@ +from locale import getlocale +from string import Template +from typing import Mapping, MutableMapping, Optional, Union + +from std2.pickle.decoder import new_decoder +from yaml import safe_load + +from ..consts import DEFAULT_LANG, LANG_ROOT + + +def _get_lang(code: Optional[str], fallback: str) -> str: + if code: + return code.casefold() + else: + tag, _ = getlocale() + tag = (tag or fallback).casefold() + primary, _, _ = tag.partition("-") + lang, _, _ = primary.partition("_") + return lang + + +class _Lang: + def __init__(self, specs: MutableMapping[str, str]) -> None: + self._specs = specs + + def __call__(self, key: str, **kwds: Union[int, float, str]) -> str: + spec = self._specs[key] + return Template(spec).substitute(kwds) + + +LANG = _Lang({}) + + +def init(code: Optional[str]) -> None: + decode = new_decoder[Mapping[str, str]](Mapping[str, str]) + + lang = _get_lang(code, fallback=DEFAULT_LANG) + lang_path = (LANG_ROOT / lang).with_suffix(".yml") + yml_path = ( + lang_path + if lang_path.exists() + else (LANG_ROOT / DEFAULT_LANG).with_suffix(".yml") + ) + + specs = decode(safe_load(yml_path.read_text("UTF-8"))) + LANG._specs.update(specs) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/types.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/types.py new file mode 100644 index 00000000..c2ba04b5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/settings/types.py @@ -0,0 +1,42 @@ +from dataclasses import dataclass +from typing import AbstractSet, Mapping, Optional, Union + +from ..fs.types import Ignored +from ..view.types import ViewOptions + + +@dataclass(frozen=True) +class VersionCtlOpts: + enable: bool + + +@dataclass(frozen=True) +class MimetypeOptions: + warn: AbstractSet[str] + allow_exts: AbstractSet[str] + + +@dataclass(frozen=True) +class Settings: + close_on_open: bool + follow: bool + follow_links: bool + follow_ignore: bool + ignores: Ignored + keymap: Mapping[str, AbstractSet[str]] + lang: Optional[str] + mime: MimetypeOptions + open_left: bool + page_increment: int + polling_rate: float + idle_timeout: float + profiling: bool + session: bool + show_hidden: bool + version_ctl: VersionCtlOpts + view: ViewOptions + width: int + win_actual_opts: Mapping[str, Union[bool, str]] + win_local_opts: Mapping[str, Union[bool, str]] + min_diagnostics_severity: int + xdg: bool diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/state/__init__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/state/cache.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/cache.py new file mode 100644 index 00000000..c89f0650 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/cache.py @@ -0,0 +1,25 @@ +from functools import cached_property + +from ..timeit import timeit +from ..view.render import render +from ..view.types import Derived +from .types import State + + +class DeepState(State): + @cached_property + def derived(self) -> Derived: + with timeit("render"): + return render( + self.root, + settings=self.settings, + index=self.index, + selection=self.selection, + filter_pattern=self.filter_pattern, + markers=self.markers, + diagnostics=self.diagnostics, + vc=self.vc, + follow_links=self.follow_links, + show_hidden=self.show_hidden, + current=self.current, + ) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/state/executor.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/executor.py new file mode 100644 index 00000000..b299d61c --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/executor.py @@ -0,0 +1,56 @@ +from asyncio import ( + AbstractEventLoop, + get_running_loop, + run, + run_coroutine_threadsafe, + wrap_future, +) +from concurrent.futures import Future, InvalidStateError, ThreadPoolExecutor +from contextlib import suppress +from threading import Thread +from typing import Any, Awaitable, Callable, Coroutine, TypeVar + +_T = TypeVar("_T") + + +class AsyncExecutor: + def __init__(self, threadpool: ThreadPoolExecutor) -> None: + f: Future = Future() + self._fut: Future = Future() + + async def cont() -> None: + loop = get_running_loop() + if threadpool: + loop.set_default_executor(threadpool) + f.set_result(loop) + main: Coroutine = await wrap_future(self._fut) + await main + + self._th = Thread(daemon=True, target=lambda: run(cont())) + self._th.start() + self.threadpool = threadpool + self.loop: AbstractEventLoop = f.result() + + def run(self, main: Awaitable[Any]) -> None: + self._fut.set_result(main) + + def fsubmit(self, f: Callable[..., Any], *args: Any, **kwargs: Any) -> Future: + fut: Future = Future() + + def cont() -> None: + if fut.set_running_or_notify_cancel(): + try: + ret = f(*args, **kwargs) + except BaseException as e: + with suppress(InvalidStateError): + fut.set_exception(e) + else: + with suppress(InvalidStateError): + fut.set_result(ret) + + self.loop.call_soon_threadsafe(cont) + return fut + + def submit(self, co: Awaitable[_T]) -> Awaitable[_T]: + f = run_coroutine_threadsafe(co, loop=self.loop) + return wrap_future(f) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/state/load.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/load.py new file mode 100644 index 00000000..996520c3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/load.py @@ -0,0 +1,72 @@ +from asyncio import gather +from concurrent.futures import ThreadPoolExecutor +from pathlib import Path + +from pynvim_pp.nvim import Nvim + +from ..consts import SESSION_DIR +from ..fs.cartographer import new +from ..nvim.markers import markers +from ..settings.types import Settings +from ..version_ctl.types import VCStatus +from .cache import DeepState +from .executor import AsyncExecutor +from .ops import load_session +from .types import Selection, Session, State + + +async def initial(settings: Settings, th: ThreadPoolExecutor) -> State: + executor = AsyncExecutor(threadpool=th) + cwd, marks = await gather(Nvim.getcwd(), markers()) + storage = ( + Path(await Nvim.fn.stdpath(str, "cache")) / "chad_sessions" + if settings.xdg + else SESSION_DIR + ) + + session = Session(workdir=cwd, storage=storage) + stored = await load_session(session) if settings.session else None + index = {cwd} | (stored.index if stored else frozenset()) + + show_hidden = ( + stored.show_hidden + if stored and stored.show_hidden is not None + else settings.show_hidden + ) + enable_vc = ( + stored.enable_vc + if stored and stored.enable_vc is not None + else settings.version_ctl.enable + ) + + selection: Selection = frozenset() + node = await new( + executor, follow_links=settings.follow_links, root=cwd, index=index + ) + vc = VCStatus() + + current = None + filter_pattern = None + + state = DeepState( + executor=executor, + settings=settings, + session=session, + vim_focus=True, + index=index, + selection=selection, + filter_pattern=filter_pattern, + show_hidden=show_hidden, + follow=settings.follow, + follow_links=settings.follow_links, + follow_ignore=settings.follow_ignore, + enable_vc=enable_vc, + width=settings.width, + root=node, + markers=marks, + diagnostics={}, + vc=vc, + current=current, + window_order={}, + ) + return state diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/state/next.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/next.py new file mode 100644 index 00000000..61f9ff68 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/next.py @@ -0,0 +1,85 @@ +from pathlib import PurePath +from typing import AbstractSet, Mapping, Optional, Union, cast + +from pynvim_pp.rpc_types import ExtData +from std2.types import Void, VoidType, or_else + +from ..fs.cartographer import update +from ..fs.types import Node +from ..nvim.types import Markers +from ..version_ctl.types import VCStatus +from .cache import DeepState +from .types import Diagnostics, FilterPattern, Index, Selection, Session, State + + +async def forward( + state: State, + *, + root: Union[Node, VoidType] = Void, + index: Union[Index, VoidType] = Void, + selection: Union[Selection, VoidType] = Void, + filter_pattern: Union[Optional[FilterPattern], VoidType] = Void, + show_hidden: Union[bool, VoidType] = Void, + follow: Union[bool, VoidType] = Void, + follow_links: Union[bool, VoidType] = Void, + follow_ignore: Union[bool, VoidType] = Void, + enable_vc: Union[bool, VoidType] = Void, + width: Union[int, VoidType] = Void, + markers: Union[Markers, VoidType] = Void, + diagnostics: Union[Diagnostics, VoidType] = Void, + vc: Union[VCStatus, VoidType] = Void, + current: Union[PurePath, VoidType] = Void, + invalidate_dirs: Union[AbstractSet[PurePath], VoidType] = Void, + window_order: Union[Mapping[ExtData, None], VoidType] = Void, + session: Union[Session, VoidType] = Void, + vim_focus: Union[bool, VoidType] = Void, + trace: bool = True, +) -> State: + new_index = or_else(index, state.index) + new_selection = or_else(selection, state.selection) + new_filter_pattern = or_else(filter_pattern, state.filter_pattern) + new_current = or_else(current, state.current) + new_follow_links = or_else(follow_links, state.follow_links) + new_root = cast( + Node, + root + or ( + await update( + state.executor, + root=state.root, + follow_links=new_follow_links, + index=new_index, + invalidate_dirs=invalidate_dirs, + ) + if not isinstance(invalidate_dirs, VoidType) + else state.root + ), + ) + new_markers = or_else(markers, state.markers) + new_vc = or_else(vc, state.vc) + new_hidden = or_else(show_hidden, state.show_hidden) + new_vim_focus = or_else(vim_focus, state.vim_focus) + + new_state = DeepState( + executor=state.executor, + settings=state.settings, + session=or_else(session, state.session), + vim_focus=new_vim_focus, + index=new_index, + selection=new_selection, + filter_pattern=new_filter_pattern, + show_hidden=new_hidden, + follow=or_else(follow, state.follow), + follow_links=new_follow_links, + follow_ignore=or_else(follow_ignore, state.follow_ignore), + enable_vc=or_else(enable_vc, state.enable_vc), + width=or_else(width, state.width), + root=new_root, + markers=new_markers, + diagnostics=or_else(diagnostics, state.diagnostics), + vc=new_vc, + current=new_current, + window_order=or_else(window_order, state.window_order), + ) + + return new_state diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/state/ops.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/ops.py new file mode 100644 index 00000000..c860e9c9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/ops.py @@ -0,0 +1,69 @@ +from hashlib import sha1 +from json import dumps, loads +from os.path import normcase +from pathlib import Path, PurePath +from tempfile import NamedTemporaryFile +from typing import Any, MutableMapping, Optional + +from pynvim_pp.lib import decode, encode +from std2.asyncio import to_thread +from std2.pickle.decoder import new_decoder +from std2.pickle.encoder import new_encoder + +from .types import Session, State, StoredSession + +_DECODER = new_decoder[StoredSession](StoredSession) +_ENCODER = new_encoder[StoredSession](StoredSession) + + +def _session_path(cwd: PurePath, storage: Path) -> Path: + hashed = sha1(normcase(cwd).encode()).hexdigest() + part = storage / hashed + return part.with_suffix(".json") + + +async def _load_json(path: Path) -> Optional[Any]: + def cont() -> Optional[Any]: + try: + json = decode(path.read_bytes()) + except FileNotFoundError: + return None + else: + return loads(json) + + return await to_thread(cont) + + +async def load_session(session: Session) -> StoredSession: + load_path = _session_path(session.workdir, storage=session.storage) + try: + json = await _load_json(load_path) + if isinstance(json, MutableMapping): + json.pop("bookmarks", None) + sessions = _DECODER(json) + except Exception: + return StoredSession(index=frozenset(), show_hidden=None, enable_vc=None) + else: + return sessions + + +async def dump_session(state: State) -> None: + stored = StoredSession( + index=state.index, + show_hidden=state.show_hidden, + enable_vc=state.enable_vc, + ) + + json = _ENCODER(stored) + path = _session_path(state.session.workdir, storage=state.session.storage) + parent = path.parent + dumped = encode(dumps(json, ensure_ascii=False, check_circular=False, indent=2)) + + def cont() -> None: + parent.mkdir(parents=True, exist_ok=True) + with NamedTemporaryFile(dir=parent, delete=False) as f: + f.write(dumped) + + Path(f.name).replace(path) + + await to_thread(cont) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/state/types.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/types.py new file mode 100644 index 00000000..09980a87 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/state/types.py @@ -0,0 +1,63 @@ +from dataclasses import dataclass +from pathlib import Path, PurePath +from typing import AbstractSet, Mapping, Optional + +from pynvim_pp.rpc_types import ExtData + +from ..fs.types import Node +from ..nvim.types import Markers +from ..settings.types import Settings +from ..version_ctl.types import VCStatus +from ..view.types import Derived +from .executor import AsyncExecutor + +Index = AbstractSet[PurePath] +Selection = Index +Diagnostics = Mapping[PurePath, Mapping[int, int]] + + +@dataclass(frozen=True) +class FilterPattern: + pattern: str + + +@dataclass(frozen=True) +class Session: + workdir: PurePath + storage: Path + + +@dataclass(frozen=True) +class State: + executor: AsyncExecutor + settings: Settings + session: Session + follow_links: bool + follow_ignore: bool + vim_focus: bool + current: Optional[PurePath] + enable_vc: bool + filter_pattern: Optional[FilterPattern] + follow: bool + index: Index + markers: Markers + root: Node + selection: Selection + show_hidden: bool + vc: VCStatus + width: int + diagnostics: Diagnostics + window_order: Mapping[ExtData, None] + + @property + def derived(self) -> Derived: + raise NotImplementedError() + + +@dataclass(frozen=True) +class StoredSession: + # TODO: sync across sessions + # pid: int + index: Index + show_hidden: Optional[bool] + enable_vc: Optional[bool] diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/timeit.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/timeit.py new file mode 100644 index 00000000..c1112de2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/timeit.py @@ -0,0 +1,35 @@ +from contextlib import contextmanager +from typing import Any, Iterator, MutableMapping, Optional, Tuple + +from pynvim_pp.logging import log +from std2.locale import si_prefixed_smol +from std2.timeit import timeit as _timeit + +from .consts import DEBUG + +_RECORDS: MutableMapping[str, Tuple[int, float]] = {} + + +@contextmanager +def timeit( + name: str, *args: Any, force: bool = False, warn: Optional[float] = None +) -> Iterator[None]: + if DEBUG or force or warn is not None: + with _timeit() as t: + yield None + delta = t().total_seconds() + if DEBUG or force or delta >= (warn or 0): + times, cum = _RECORDS.get(name, (0, 0)) + tt, c = times + 1, cum + delta + _RECORDS[name] = tt, c + + label = name.ljust(50) + time = f"{si_prefixed_smol(delta, precision=0)}s".ljust(8) + ttime = f"{si_prefixed_smol(c / tt, precision=0)}s".ljust(8) + msg = f"TIME -- {label} :: {time} @ {ttime} {' '.join(map(str, args))}" + if force: + log.info("%s", msg) + else: + log.debug("%s", msg) + else: + yield None diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/__init__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/autocmds.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/autocmds.py new file mode 100644 index 00000000..95e78554 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/autocmds.py @@ -0,0 +1,199 @@ +from asyncio import Task, create_task, sleep +from collections.abc import Sequence +from itertools import chain +from typing import Optional + +from pynvim_pp.buffer import Buffer +from pynvim_pp.nvim import Nvim +from pynvim_pp.rpc_types import NvimError +from pynvim_pp.window import Window +from std2.asyncio import cancel +from std2.cell import RefCell + +from ..consts import FM_FILETYPE, URI_SCHEME +from ..fs.ops import ancestors, is_file +from ..lsp.diagnostics import poll +from ..nvim.markers import markers +from ..registry import NAMESPACE, autocmd, rpc +from ..settings.types import Settings +from ..state.next import forward +from ..state.ops import dump_session +from ..state.types import State +from .shared.current import new_current_file, new_root +from .shared.wm import ( + find_current_buffer_path, + find_fm_buffers, + is_fm_buf_name, + is_fm_buffer, + restore_non_fm_win, + setup_fm_buf, +) +from .types import Stage + +_CELL = RefCell[Optional[Task]](None) + + +async def _setup_fm_win(settings: Settings, win: Window) -> None: + for key, val in settings.win_local_opts.items(): + await win.opts.set(key, val=val) + + +async def setup(settings: Settings) -> None: + async for buf in find_fm_buffers(): + await setup_fm_buf(settings, buf=buf) + for win in await Window.list(): + buf = await win.get_buf() + if await is_fm_buffer(buf): + await _setup_fm_win(settings, win=win) + + +@rpc(blocking=False) +async def _when_idle(state: State) -> None: + if task := _CELL.val: + _CELL.val = None + await cancel(task) + + async def cont() -> None: + await sleep(state.settings.idle_timeout) + diagnostics = await poll(state.settings.min_diagnostics_severity) + await forward(state, diagnostics=diagnostics) + + _CELL.val = create_task(cont()) + + +_ = autocmd("CursorHold", "CursorHoldI") << f"lua {NAMESPACE}.{_when_idle.method}()" + + +@rpc(blocking=False) +async def save_session(state: State) -> None: + """ + Save CHADTree state + """ + + await dump_session(state) + + +_ = autocmd("ExitPre") << f"lua {NAMESPACE}.{save_session.method}()" +_ = ( + autocmd("User", modifiers=("CHADSave",)) + << f"lua {NAMESPACE}.{save_session.method}()" +) + + +@rpc(blocking=False) +async def focus_lost(state: State) -> Stage: + """ + Save CHADTree state + """ + + await dump_session(state) + new_state = await forward(state, vim_focus=False) + return Stage(new_state) + + +_ = autocmd("FocusLost") << f"lua {NAMESPACE}.{focus_lost.method}()" + + +@rpc(blocking=False) +async def _focus_gained(state: State) -> Stage: + """ """ + + new_state = await forward(state, vim_focus=True) + return Stage(new_state) + + +_ = autocmd("FocusGained") << f"lua {NAMESPACE}.{_focus_gained.method}()" + + +@rpc(blocking=False) +async def _record_win_pos(state: State) -> Stage: + """ + Record last windows + """ + + win = await Window.get_current() + win_id = win.data + + window_order = { + wid: None + for wid in chain( + (wid for wid in state.window_order if wid != win_id), (win_id,) + ) + } + new_state = await forward(state, window_order=window_order) + return Stage(new_state) + + +_ = autocmd("WinEnter") << f"lua {NAMESPACE}.{_record_win_pos.method}()" + + +@rpc(blocking=False) +async def _changedir(state: State) -> Stage: + """ + Follow cwd update + """ + + cwd = await Nvim.getcwd() + new_state = await new_root(state, new_cwd=cwd, indices=frozenset()) + return Stage(new_state) + + +_ = autocmd("DirChanged") << f"lua {NAMESPACE}.{_changedir.method}()" + + +@rpc(blocking=False) +async def _restore(state: State, args: Sequence[str]) -> None: + win = await Window.get_current() + await restore_non_fm_win(state.settings.win_actual_opts, win=win) + + +@rpc(blocking=False) +async def _update_follow(state: State) -> Optional[Stage]: + """ + Follow buffer + """ + + win = await Window.get_current() + buf = await Buffer.get_current() + name = await buf.get_name() + is_fm_win = await win.vars.get(bool, URI_SCHEME) + is_fm_buf = await buf.filetype() == FM_FILETYPE + is_fm_uri = name and is_fm_buf_name(name) + + if is_fm_win and not is_fm_buf: + await restore_non_fm_win(state.settings.win_actual_opts, win=win) + + if is_fm_uri or is_fm_buf and not is_fm_win: + await _setup_fm_win(state.settings, win=win) + + if is_fm_uri and not is_fm_buf: + await setup_fm_buf(state.settings, buf=buf) + + try: + if (current := await find_current_buffer_path(name)) and await is_file(current): + if state.vc.ignored & {current, *ancestors(current)}: + return None + else: + stage = await new_current_file(state, current=current) + return stage + else: + return None + except NvimError: + return None + + +_ = autocmd("BufEnter") << f"lua {NAMESPACE}.{_update_follow.method}()" + + +@rpc(blocking=False) +async def _update_markers(state: State) -> Stage: + """ + Update markers + """ + + mks = await markers() + new_state = await forward(state, markers=mks) + return Stage(new_state) + + +_ = autocmd("QuickfixCmdPost") << f"lua {NAMESPACE}.{_update_markers.method}()" diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/click.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/click.py new file mode 100644 index 00000000..5d585854 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/click.py @@ -0,0 +1,108 @@ +from typing import Optional + +from pynvim_pp.nvim import Nvim +from std2 import anext + +from ..fs.cartographer import is_dir +from ..fs.types import Mode +from ..registry import rpc +from ..settings.localization import LANG +from ..state.next import forward +from ..state.types import State +from .shared.index import indices +from .shared.open_file import open_file +from .shared.wm import find_fm_windows +from .types import ClickType, Stage + + +async def _click( + state: State, is_visual: bool, click_type: ClickType +) -> Optional[Stage]: + node = await anext(indices(state, is_visual=is_visual), None) + + if not node: + return None + else: + if Mode.orphan_link in node.mode: + await Nvim.write(LANG("dead_link", name=node.path.name), error=True) + return None + else: + if is_dir(node): + if node.path == state.root.path: + return None + elif node.pointed and not state.follow_links: + return None + elif state.filter_pattern: + await Nvim.write(LANG("filter_click")) + return None + else: + index = state.index ^ {node.path} + invalidate_dirs = {node.path} + new_state = await forward( + state, + index=index, + invalidate_dirs=invalidate_dirs, + ) + return Stage(new_state) + else: + nxt = await open_file( + state, + path=node.path, + click_type=click_type, + ) + + if state.settings.close_on_open and click_type != ClickType.secondary: + async for win, _ in find_fm_windows(): + await win.close() + + return nxt + + +@rpc(blocking=False) +async def _primary(state: State, is_visual: bool) -> Optional[Stage]: + """ + Folders -> toggle + File -> open + """ + + return await _click(state, is_visual=is_visual, click_type=ClickType.primary) + + +@rpc(blocking=False) +async def _secondary(state: State, is_visual: bool) -> Optional[Stage]: + """ + Folders -> toggle + File -> preview + """ + + return await _click(state, is_visual=is_visual, click_type=ClickType.secondary) + + +@rpc(blocking=False) +async def _tertiary(state: State, is_visual: bool) -> Optional[Stage]: + """ + Folders -> toggle + File -> open in new tab + """ + + return await _click(state, is_visual=is_visual, click_type=ClickType.tertiary) + + +@rpc(blocking=False) +async def _v_split(state: State, is_visual: bool) -> Optional[Stage]: + """ + Folders -> toggle + File -> open in vertical split + """ + + return await _click(state, is_visual=is_visual, click_type=ClickType.v_split) + + +@rpc(blocking=False) +async def _h_split(state: State, is_visual: bool) -> Optional[Stage]: + """ + Folders -> toggle + File -> open in horizontal split + """ + + return await _click(state, is_visual=is_visual, click_type=ClickType.h_split) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/collapse.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/collapse.py new file mode 100644 index 00000000..4d21342b --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/collapse.py @@ -0,0 +1,38 @@ +from typing import Optional + +from std2 import anext + +from ..fs.cartographer import act_like_dir +from ..fs.ops import ancestors +from ..registry import rpc +from ..state.next import forward +from ..state.types import State +from .shared.index import indices +from .types import Stage + + +@rpc(blocking=False) +async def _collapse(state: State, is_visual: bool) -> Optional[Stage]: + """ + Collapse folder + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if not node: + return None + else: + if act_like_dir(node, follow_links=state.follow_links): + path = node.path if node.path in state.index else node.path.parent + else: + path = node.path.parent + + paths = { + indexed + for indexed in state.index + if path in (ancestors(indexed) | {indexed}) + } + + index = (state.index - paths) | {state.root.path} + invalidate_dirs = {path} + new_state = await forward(state, index=index, invalidate_dirs=invalidate_dirs) + return Stage(new_state, focus=path) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/copy_name.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/copy_name.py new file mode 100644 index 00000000..fcf5abee --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/copy_name.py @@ -0,0 +1,75 @@ +from locale import strxfrm +from os import linesep +from os.path import normpath, sep +from pathlib import PurePath +from typing import AsyncIterator, Callable + +from pynvim_pp.nvim import Nvim +from pynvim_pp.types import NoneType + +from ..fs.cartographer import is_dir +from ..fs.ops import is_dir as iis_dir +from ..registry import rpc +from ..settings.localization import LANG +from ..state.types import State +from .shared.index import indices + + +async def _cn(state: State, is_visual: bool, proc: Callable[[PurePath], str]) -> None: + async def gen_paths() -> AsyncIterator[str]: + if selection := state.selection: + for path in selection: + suffix = sep if await iis_dir(path) else "" + yield proc(path) + suffix + else: + nodes = indices(state, is_visual=is_visual) + async for node in nodes: + suffix = sep if is_dir(node) else "" + yield proc(node.path) + suffix + + paths = sorted([path async for path in gen_paths()], key=strxfrm) + clip = linesep.join(paths) + copied_paths = ", ".join(paths) + + await Nvim.fn.setreg(NoneType, "+", clip) + await Nvim.fn.setreg(NoneType, "*", clip) + await Nvim.write(LANG("copy_paths", copied_paths=copied_paths)) + + +@rpc(blocking=False) +async def _copy_name(state: State, is_visual: bool) -> None: + """ + Copy dirname / filename + """ + + await _cn( + state, + is_visual=is_visual, + proc=lambda p: normpath(p.name), + ) + + +@rpc(blocking=False) +async def _copy_basename(state: State, is_visual: bool) -> None: + """ + Copy basename of dirname / filename + """ + + await _cn( + state, + is_visual=is_visual, + proc=normpath, + ) + + +@rpc(blocking=False) +async def _copy_relname(state: State, is_visual: bool) -> None: + """ + Copy relname of dirname / filename + """ + + await _cn( + state, + is_visual=is_visual, + proc=lambda p: normpath(p.relative_to(state.root.path)), + ) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/cut_copy.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/cut_copy.py new file mode 100644 index 00000000..eee7de95 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/cut_copy.py @@ -0,0 +1,171 @@ +from itertools import chain +from os import linesep +from pathlib import PurePath +from typing import AbstractSet, Awaitable, Callable, Mapping, MutableMapping, Optional + +from pynvim_pp.nvim import Nvim +from std2 import anext +from std2.locale import pathsort_key + +from ..fs.cartographer import act_like_dir +from ..fs.ops import ancestors, copy, cut, exists, unify_ancestors +from ..fs.types import Node +from ..lsp.notify import lsp_created, lsp_moved +from ..registry import rpc +from ..settings.localization import LANG +from ..state.next import forward +from ..state.types import State +from ..view.ops import display_path +from .shared.index import indices +from .shared.refresh import refresh +from .shared.wm import kill_buffers +from .types import Stage + + +def _find_dest(src: PurePath, node: Node, follow_links: bool) -> PurePath: + parent = ( + node.path if act_like_dir(node, follow_links=follow_links) else node.path.parent + ) + dst = parent / src.name + return dst + + +async def _operation( + *, + state: State, + is_visual: bool, + nono: AbstractSet[PurePath], + op_name: str, + is_move: bool, + action: Callable[[Mapping[PurePath, PurePath]], Awaitable[None]], +) -> Optional[Stage]: + node = await anext(indices(state, is_visual=is_visual), None) + selection = state.selection + unified = unify_ancestors(selection) + + if not unified or not node: + await Nvim.write(LANG("nothing_select"), error=True) + return None + elif not unified.isdisjoint(nono): + await Nvim.write(LANG("operation not permitted on root"), error=True) + return None + else: + pre_operations = { + src: _find_dest(src, node=node, follow_links=state.follow_links) + for src in unified + } + pre_existing = { + s: d for s, d in pre_operations.items() if await exists(d, follow=False) + } + + new_operations: MutableMapping[PurePath, PurePath] = {} + while pre_existing: + source, dest = pre_existing.popitem() + resp = await Nvim.input(question=LANG("path_exists_err"), default=dest.name) + new_dest = dest.parent / resp if resp else None + + if not new_dest: + pre_existing[source] = dest + break + elif await exists(new_dest, follow=False): + pre_existing[source] = new_dest + else: + new_operations[source] = new_dest + + if pre_existing: + msg = linesep.join( + f"{display_path(s, state=state)} -> {display_path(d, state=state)}" + for s, d in sorted( + pre_existing.items(), key=lambda t: pathsort_key(t[0]) + ) + ) + await Nvim.write( + LANG("paths already exist", operation=op_name, paths=msg), + error=True, + ) + return None + + else: + operations = {**pre_operations, **new_operations} + msg = linesep.join( + f"{display_path(s, state=state)} -> {display_path(d, state=state)}" + for s, d in sorted(operations.items(), key=lambda t: pathsort_key(t[0])) + ) + + question = LANG("confirm op", operation=op_name, paths=msg) + ans = await Nvim.confirm( + question=question, + answers=LANG("ask_yesno"), + answer_key={1: True, 2: False}, + ) + + if not ans: + return None + else: + try: + await action(operations) + except Exception as e: + await Nvim.write(e, error=True) + return await refresh(state) + else: + parents = { + p.parent for p in chain(operations.keys(), operations.values()) + } + invalidate_dirs = parents + index = state.index | parents + new_selection = {*operations.values()} + new_state = await forward( + state, + index=index, + selection=new_selection if is_move else selection, + invalidate_dirs=invalidate_dirs, + ) + focus = next( + iter(sorted(new_selection, key=pathsort_key)), + None, + ) + + if is_move: + await kill_buffers( + last_used=new_state.window_order, + paths=selection, + reopen={}, + ) + await lsp_moved(operations) + else: + await lsp_created(new_selection) + return Stage(new_state, focus=focus) + + +@rpc(blocking=False) +async def _cut(state: State, is_visual: bool) -> Optional[Stage]: + """ + Cut selected + """ + + cwd, root = await Nvim.getcwd(), state.root.path + nono = {cwd, root} | ancestors(cwd, root) + return await _operation( + state=state, + is_visual=is_visual, + nono=nono, + op_name=LANG("cut"), + action=cut, + is_move=True, + ) + + +@rpc(blocking=False) +async def _copy(state: State, is_visual: bool) -> Optional[Stage]: + """ + Copy selected + """ + + return await _operation( + state=state, + is_visual=is_visual, + nono=frozenset(), + op_name=LANG("copy"), + action=copy, + is_move=False, + ) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/delete.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/delete.py new file mode 100644 index 00000000..ff71d75b --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/delete.py @@ -0,0 +1,102 @@ +from locale import strxfrm +from os import linesep +from pathlib import PurePath +from subprocess import CalledProcessError +from typing import Awaitable, Callable, Iterable, Optional + +from pynvim_pp.nvim import Nvim +from std2.asyncio.subprocess import call + +from ..fs.ops import ancestors, remove, unify_ancestors, which +from ..lsp.notify import lsp_removed +from ..registry import rpc +from ..settings.localization import LANG +from ..state.next import forward +from ..state.types import State +from ..view.ops import display_path +from .shared.index import indices +from .shared.refresh import refresh +from .shared.wm import kill_buffers +from .types import Stage + + +async def _remove( + state: State, + is_visual: bool, + yeet: Callable[[Iterable[PurePath]], Awaitable[None]], +) -> Optional[Stage]: + cwd, root = await Nvim.getcwd(), state.root.path + nono = {cwd, root} | ancestors(cwd, root) + + selection = state.selection or { + node.path async for node in indices(state, is_visual=is_visual) + } + unified = unify_ancestors(selection) + + if not unified: + return None + elif not unified.isdisjoint(nono): + await Nvim.write(LANG("operation not permitted on root"), error=True) + return None + else: + display_paths = linesep.join( + sorted((display_path(path, state=state) for path in unified), key=strxfrm) + ) + + question = LANG("ask_trash", display_paths=display_paths) + ans = await Nvim.confirm( + question=question, + answers=LANG("ask_yesno"), + answer_key={1: True, 2: False}, + ) + + if not ans: + return None + else: + try: + await yeet(unified) + except Exception as e: + await Nvim.write(e, error=True) + return await refresh(state) + else: + invalidate_dirs = {path.parent for path in unified} + new_state = await forward( + state, selection=frozenset(), invalidate_dirs=invalidate_dirs + ) + + await kill_buffers( + last_used=new_state.window_order, paths=selection, reopen={} + ) + await lsp_removed(unified) + return Stage(new_state) + + +@rpc(blocking=False) +async def _delete(state: State, is_visual: bool) -> Optional[Stage]: + """ + Delete selected + """ + + return await _remove(state, is_visual=is_visual, yeet=remove) + + +async def _sys_trash(paths: Iterable[PurePath]) -> None: + cwd = await Nvim.getcwd() + + if arg0 := which("trash"): + try: + await call(arg0, "--", *map(str, paths), cwd=cwd) + except CalledProcessError as e: + await Nvim.write(e, e.stderr, e.stdout, error=True) + + else: + await Nvim.write(LANG("sys_trash_err"), error=True) + + +@rpc(blocking=False) +async def _trash(state: State, is_visual: bool) -> Optional[Stage]: + """ + Delete selected + """ + + return await _remove(state, is_visual=is_visual, yeet=_sys_trash) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/filter.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/filter.py new file mode 100644 index 00000000..a74fde4e --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/filter.py @@ -0,0 +1,48 @@ +from typing import Optional + +from pynvim_pp.nvim import Nvim +from std2 import anext + +from ..registry import rpc +from ..settings.localization import LANG +from ..state.next import forward +from ..state.types import FilterPattern, Selection, State +from .shared.index import indices +from .types import Stage + + +@rpc(blocking=False) +async def _clear_filter(state: State, is_visual: bool) -> Optional[Stage]: + """ + Clear filter + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if not node: + return None + else: + focus = node.path + new_state = await forward(state, filter_pattern=None) + return Stage(new_state, focus=focus) + + +@rpc(blocking=False) +async def _filter(state: State, is_visual: bool) -> Optional[Stage]: + """ + Update filter + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if not node: + return None + else: + focus = node.path + old_p = state.filter_pattern.pattern if state.filter_pattern else "" + pattern = await Nvim.input(question=LANG("new_filter"), default=old_p) + + filter_pattern = FilterPattern(pattern=pattern) if pattern else None + selection: Selection = state.selection if filter_pattern else frozenset() + new_state = await forward( + state, selection=selection, filter_pattern=filter_pattern + ) + return Stage(new_state, focus=focus) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/focus.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/focus.py new file mode 100644 index 00000000..307def10 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/focus.py @@ -0,0 +1,105 @@ +from os.path import normcase, normpath +from pathlib import PurePath +from typing import Optional + +from pynvim_pp.nvim import Nvim +from std2 import anext + +from ..fs.cartographer import act_like_dir +from ..registry import rpc +from ..settings.localization import LANG +from ..state.types import State +from .shared.current import maybe_path_above, new_current_file, new_root +from .shared.index import indices +from .types import Stage + + +async def _jump(state: State, path: PurePath) -> Optional[Stage]: + if new_state := await maybe_path_above(state, paths={path}): + return Stage(new_state, focus=path) + elif stage := await new_current_file(state, current=path): + return Stage(stage.state, focus=path) + else: + return None + + +@rpc(blocking=False) +async def _jump_to_current(state: State, is_visual: bool) -> Optional[Stage]: + """ + Jump to active file + """ + + if not (curr := state.current): + return None + else: + return await _jump(state, path=curr) + + +@rpc(blocking=False) +async def _refocus(state: State, is_visual: bool) -> Stage: + """ + Follow cwd update + """ + + cwd = await Nvim.getcwd() + new_state = await new_root(state, new_cwd=cwd, indices=frozenset()) + focus = new_state.root.path + return Stage(new_state, focus=focus) + + +@rpc(blocking=False) +async def _change_dir(state: State, is_visual: bool) -> Optional[Stage]: + """ + Change root directory + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if not node: + return None + else: + cwd = ( + node.path + if act_like_dir(node, follow_links=state.follow_links) + else node.path.parent + ) + new_state = await new_root(state, new_cwd=cwd, indices=frozenset()) + escaped = await Nvim.fn.fnameescape(str, normcase(new_state.root.path)) + await Nvim.exec(f"chdir {escaped}") + await Nvim.write(LANG("new cwd", cwd=normpath(new_state.root.path))) + return Stage(new_state, focus=new_state.root.path) + + +@rpc(blocking=False) +async def _change_focus(state: State, is_visual: bool) -> Optional[Stage]: + """ + Refocus root directory + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if not node: + return None + else: + new_base = ( + node.path + if act_like_dir(node, follow_links=state.follow_links) + else node.path.parent + ) + new_state = await new_root(state, new_cwd=new_base, indices=frozenset()) + focus = node.path + return Stage(new_state, focus=focus) + + +@rpc(blocking=False) +async def _change_focus_up(state: State, is_visual: bool) -> Optional[Stage]: + """ + Refocus root directory up + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if not node: + return None + else: + new_state = await new_root( + state, new_cwd=state.root.path.parent, indices=frozenset() + ) + return Stage(new_state, focus=node.path) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/help.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/help.py new file mode 100644 index 00000000..00347dae --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/help.py @@ -0,0 +1,95 @@ +from enum import Enum, auto +from pathlib import Path +from typing import Sequence, Tuple +from uuid import uuid4 +from webbrowser import open as open_w + +from pynvim_pp.buffer import Buffer +from pynvim_pp.float_win import list_floatwins, open_float_win +from pynvim_pp.nvim import Nvim +from std2.argparse import ArgparseError, ArgParser +from std2.types import never + +from ..consts import ( + CONFIGURATION_MD, + CONFIGURATION_URI, + FEATURES_MD, + FEATURES_URI, + KEYBIND_MD, + KEYBIND_URI, + MIGRATION_MD, + MIGRATION_URI, + README_MD, + README_URI, + THEME_MD, + THEME_URI, +) +from ..registry import rpc +from ..state.types import State + +_NS = uuid4() + + +class _Topics(Enum): + index = auto() + features = auto() + keybind = auto() + config = auto() + theme = auto() + migration = auto() + + +def _directory(topic: _Topics) -> Tuple[Path, str]: + if topic is _Topics.index: + return README_MD, README_URI + elif topic is _Topics.features: + return FEATURES_MD, FEATURES_URI + elif topic is _Topics.keybind: + return KEYBIND_MD, KEYBIND_URI + elif topic is _Topics.config: + return CONFIGURATION_MD, CONFIGURATION_URI + elif topic is _Topics.theme: + return THEME_MD, THEME_URI + elif topic is _Topics.migration: + return MIGRATION_MD, MIGRATION_URI + else: + never(topic) + + +def _parse_args(args: Sequence[str]) -> Tuple[_Topics, bool]: + parser = ArgParser() + parser.add_argument( + "topic", + nargs="?", + choices=tuple(topic.name for topic in _Topics), + default=_Topics.index.name, + ) + parser.add_argument("-w", "--web", action="store_true", default=False) + ns = parser.parse_args(args) + return _Topics[ns.topic], ns.web + + +@rpc(blocking=False) +async def _help(state: State, args: Sequence[str]) -> None: + """ + Open help doc + """ + + try: + topic, use_web = _parse_args(args) + except ArgparseError as e: + await Nvim.write(e, error=True) + else: + md, uri = _directory(topic) + web_d = open_w(uri) if use_web else False + if not web_d: + async for win in list_floatwins(_NS): + await win.close() + lines = md.read_text("UTF-8").splitlines() + buf = await Buffer.create( + listed=False, scratch=True, wipe=True, nofile=True, noswap=True + ) + await buf.set_lines(lines=lines) + await buf.opts.set("modifiable", val=False) + await buf.opts.set("syntax", val="markdown") + await open_float_win(_NS, margin=0, relsize=0.95, buf=buf, border="rounded") diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/link.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/link.py new file mode 100644 index 00000000..5c9d4ca6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/link.py @@ -0,0 +1,79 @@ +from os.path import normpath, relpath +from pathlib import PurePath +from typing import MutableMapping, Optional + +from pynvim_pp.nvim import Nvim +from std2 import anext +from std2.locale import pathsort_key + +from ..fs.cartographer import act_like_dir +from ..fs.ops import ancestors, exists, link, resolve +from ..lsp.notify import lsp_created +from ..registry import rpc +from ..settings.localization import LANG +from ..state.next import forward +from ..state.types import State +from ..view.ops import display_path +from .shared.current import maybe_path_above +from .shared.index import indices +from .shared.refresh import refresh +from .types import Stage + + +@rpc(blocking=False) +async def _link(state: State, is_visual: bool) -> Optional[Stage]: + """ + Symlink selected + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if node is None: + return None + else: + parent = ( + node.path + if act_like_dir(node, follow_links=state.follow_links) + else node.path.parent + ) + selection = state.selection or {node.path} + operations: MutableMapping[PurePath, PurePath] = {} + for selected in selection: + display = display_path(selected, state=state) + if child := await Nvim.input( + question=LANG("link", src=display), default="" + ): + try: + dst = await resolve(parent / child, strict=False) + except Exception as e: + await Nvim.write(e, error=True) + return None + else: + if dst in operations or await exists(dst, follow=False): + await Nvim.write( + LANG("already_exists", name=normpath(dst)), error=True + ) + return None + else: + src = PurePath(relpath(selected, start=dst.parent)) + operations[dst] = src + else: + return None + + try: + await link(operations) + except Exception as e: + await Nvim.write(e, error=True) + return await refresh(state) + else: + paths = operations.keys() + new_state = await maybe_path_above(state, paths=paths) or state + await lsp_created(paths) + focus, *_ = sorted(paths, key=pathsort_key) + invalidate_dirs = {path.parent for path in paths} + index = state.index | ancestors(*paths) + next_state = await forward( + new_state, + index=index, + invalidate_dirs=invalidate_dirs, + ) + return Stage(next_state, focus=focus) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/marks.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/marks.py new file mode 100644 index 00000000..a299e89d --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/marks.py @@ -0,0 +1,57 @@ +from locale import strxfrm +from pathlib import PurePath +from typing import Any, Iterator, MutableMapping, MutableSet, Optional, Tuple + +from pynvim_pp.nvim import Nvim +from std2.locale import pathsort_key +from std2.pathlib import is_relative_to + +from ..registry import rpc +from ..settings.localization import LANG +from ..state.types import State +from ..view.ops import display_path +from .focus import _jump +from .types import Stage + + +def _order(root: PurePath, marks: str, path: PurePath) -> Tuple[bool, str, Any]: + return not is_relative_to(path, root), "", pathsort_key(path) + + +def _display_path(state: State, marks: str, path: PurePath, idx: int) -> str: + display = display_path(path, state=state) + return f"{idx}. [{marks}] {display}" + + +@rpc(blocking=False) +async def _bookmark_goto(state: State, is_visual: bool) -> Optional[Stage]: + """ + Goto bookmark + """ + + def cont() -> Iterator[Tuple[str, PurePath]]: + root = state.root.path + markers: MutableMapping[str, PurePath] = {} + for path, marks in state.markers.bookmarks.items(): + for mark in marks: + if m := markers.get(mark): + markers[mark] = max(m, path) + else: + markers[mark] = path + + seen: MutableSet[PurePath] = set() + for _, path in sorted(markers.items(), key=lambda kv: _order(root, *kv)): + if path not in seen: + ms = sorted(state.markers.bookmarks.get(path, ()), key=strxfrm) + yield "".join(ms), path + + opts = { + _display_path(state, marks=marks, path=path, idx=idx): path + for idx, (marks, path) in enumerate(cont(), start=1) + } + + if mark := await Nvim.input_list(opts): + return await _jump(state, path=mark) + else: + await Nvim.write(LANG("no_bookmarks"), error=True) + return None diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/new.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/new.py new file mode 100644 index 00000000..9c183cd2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/new.py @@ -0,0 +1,68 @@ +from os import sep +from os.path import abspath, normpath +from pathlib import PurePath +from typing import Optional + +from pynvim_pp.nvim import Nvim +from std2 import anext + +from ..fs.cartographer import act_like_dir +from ..fs.ops import ancestors, exists, mkdir, new +from ..lsp.notify import lsp_created +from ..registry import rpc +from ..settings.localization import LANG +from ..state.next import forward +from ..state.types import State +from .shared.current import maybe_path_above +from .shared.index import indices +from .shared.refresh import refresh +from .types import Stage + + +@rpc(blocking=False) +async def _new(state: State, is_visual: bool) -> Optional[Stage]: + """ + new file / folder + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if not node: + return None + else: + parent = ( + node.path + if act_like_dir(node, follow_links=state.follow_links) + else node.path.parent + ) + + child = await Nvim.input(question=LANG("pencil"), default="") + + if not child: + return None + else: + path = PurePath(abspath(parent / child)) + if await exists(path, follow=False): + await Nvim.write( + LANG("already_exists", name=normpath(path)), error=True + ) + return None + else: + try: + if child.endswith(sep): + await mkdir((path,)) + else: + await new((path,)) + except Exception as e: + await Nvim.write(e, error=True) + return await refresh(state=state) + else: + new_state = await maybe_path_above(state, paths={path}) or state + invalidate_dirs = {path.parent} + index = state.index | ancestors(path) + next_state = await forward( + new_state, + index=index, + invalidate_dirs=invalidate_dirs, + ) + await lsp_created((path,)) + return Stage(next_state, focus=path) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/noop.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/noop.py new file mode 100644 index 00000000..707de3fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/noop.py @@ -0,0 +1,11 @@ +from typing import Any + +from ..registry import rpc +from ..state.types import State + + +@rpc(blocking=False) +async def _noop(state: State, *_: Any) -> None: + """ + NOOP + """ diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/open_system.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/open_system.py new file mode 100644 index 00000000..50418fe4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/open_system.py @@ -0,0 +1,52 @@ +import sys +from asyncio import create_task +from pathlib import PurePath +from subprocess import CalledProcessError + +from pynvim_pp.logging import suppress_and_log +from pynvim_pp.nvim import Nvim +from std2 import anext +from std2.asyncio.subprocess import call +from std2.pathlib import AnyPath +from std2.platform import OS, os + +from ..fs.ops import which +from ..registry import rpc +from ..settings.localization import LANG +from ..state.types import State +from .shared.index import indices + + +async def _call(cwd: PurePath, arg0: AnyPath, *args: AnyPath) -> None: + try: + await call(arg0, *args, cwd=cwd) + except CalledProcessError as e: + await Nvim.write(e, e.stderr, e.stdout, error=True) + + +async def _open_gui(path: PurePath, cwd: PurePath) -> None: + with suppress_and_log(): + if sys.platform == "win32": + from os import startfile + + startfile(path, cwd=cwd) + elif os is OS.macos and (arg0 := which("open")): + await _call(cwd, arg0, "--", path) + elif os is OS.linux and (arg0 := which("xdg-open")): + await _call(cwd, arg0, path) + else: + await Nvim.write(LANG("sys_open_err")) + + +@rpc(blocking=False) +async def _open_sys(state: State, is_visual: bool) -> None: + """ + Open using finder / dolphin, etc + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if not node: + return None + else: + cwd = await Nvim.getcwd() + create_task(_open_gui(node.path, cwd=cwd)) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/quit.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/quit.py new file mode 100644 index 00000000..5c82b3bc --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/quit.py @@ -0,0 +1,20 @@ +from pynvim_pp.nvim import Nvim +from pynvim_pp.window import Window + +from ..registry import rpc +from ..state.types import State +from .shared.wm import find_fm_windows_in_tab + + +@rpc(blocking=False) +async def _quit(state: State, is_visual: bool) -> None: + """ + Close sidebar + """ + + wins = await Window.list() + if len(wins) <= 1: + await Nvim.exec("quit") + else: + async for win in find_fm_windows_in_tab(state.window_order): + await win.close() diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/redraw.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/redraw.py new file mode 100644 index 00000000..b555a3d2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/redraw.py @@ -0,0 +1,145 @@ +from pathlib import Path, PurePath +from posixpath import sep +from typing import Optional, Sequence +from uuid import uuid4 + +from pynvim_pp.atomic import Atomic +from pynvim_pp.buffer import Buffer +from pynvim_pp.nvim import Nvim +from pynvim_pp.operators import operator_marks +from pynvim_pp.rpc_types import NvimError +from pynvim_pp.types import NoneType +from std2.difflib import trans_inplace +from std2.pickle.decoder import new_decoder +from std2.pickle.types import DecodeError + +from ..consts import URI_SCHEME +from ..state.types import State +from ..view.types import Derived +from .shared.wm import find_fm_windows + + +class UnrecoverableError(Exception): + ... + + +_NS = uuid4() + + +_DECODER = new_decoder[Sequence[str]](Sequence[str]) +_HOME = Path.home() + + +def _buf_name(root: PurePath) -> str: + try: + rel = root.relative_to(_HOME) + except ValueError: + name = root.as_posix() + else: + name = f"~{sep}{rel.as_posix()}" + + return name + + +def _update( + use_extmarks: bool, + buf: Buffer, + ns: int, + derived: Derived, + hashed_lines: Sequence[str], +) -> Atomic: + atomic = Atomic() + for (i1, i2), (j1, j2) in trans_inplace( + src=hashed_lines, dest=derived.hashed, unifying=10 + ): + atomic.buf_clear_namespace(buf, ns, i1, i2) + atomic.buf_set_lines(buf, i1, i2, True, derived.lines[j1:j2]) + + for idx, highlights in enumerate(derived.highlights[j1:j2], start=i1): + for hl in highlights: + atomic.buf_add_highlight(buf, ns, hl.group, idx, hl.begin, hl.end) + + for idx, badges in enumerate(derived.badges[j1:j2], start=i1): + vtxt = tuple((bdg.text, bdg.group) for bdg in badges) + if use_extmarks: + atomic.buf_set_extmark( + buf, ns, idx, -1, {"virt_text": vtxt, "hl_mode": "combine"} + ) + else: + atomic.buf_set_virtual_text(buf, ns, idx, vtxt, {}) + + atomic.buf_set_var(buf, str(_NS), derived.hashed) + return atomic + + +async def redraw(state: State, focus: Optional[PurePath]) -> None: + focus_row = state.derived.path_row_lookup.get(focus) if focus else None + buf_name = _buf_name(state.root.path) + + ns = await Nvim.create_namespace(_NS) + use_extmarks = await Nvim.api.has("nvim-0.6") + + async for win, buf in find_fm_windows(): + is_fm_win = await win.vars.get(bool, URI_SCHEME) + p_count = await buf.line_count() + n_count = len(state.derived.lines) + row, col = await win.get_cursor() + (r1, c1), (r2, c2) = await operator_marks(buf, visual_type=None) + buf_var = await buf.vars.get(NoneType, str(_NS)) + + try: + hashed_lines = _DECODER(buf_var) + except DecodeError: + hashed_lines = ("",) + + if focus_row is not None: + new_row: Optional[int] = focus_row + 1 + elif row >= n_count: + new_row = n_count + elif p_count != n_count: + new_row = row + 1 + else: + new_row = None + + a1 = Atomic() + a1.buf_set_option(buf, "modifiable", True) + + a2 = _update( + use_extmarks, + buf=buf, + ns=ns, + derived=state.derived, + hashed_lines=hashed_lines, + ) + + a3 = Atomic() + a3.buf_set_option(buf, "modifiable", False) + a3.call_function("setpos", ("'<", (buf.number, r1 + 1, c1 + 1, 0))) + a3.call_function("setpos", ("'>", (buf.number, r2 + 1, c2, 0))) + if new_row is not None: + win_height = await win.get_height() + win_lo = await Nvim.fn.line(int, "w0", win) + win_hi = await Nvim.fn.line(int, "w$", win) + lo = max(1, new_row - win_height // 2) + hi = min(n_count, new_row + win_height // 2) + + if new_row < win_lo or new_row > win_hi: + a3.win_set_cursor(win, (lo, 0)) + a3.win_set_cursor(win, (hi, 0)) + a3.win_set_cursor(win, (lo, 0)) + a3.win_set_cursor(win, (hi, 0)) + + a3.win_set_cursor(win, (new_row, col)) + + a3.buf_set_name(buf, f"{URI_SCHEME}://{buf_name}") + a3.win_set_var(win, URI_SCHEME, True) + + if not is_fm_win: + for key, val in state.settings.win_local_opts.items(): + a3.win_set_option(win, key, val) + + a4 = a1 + a2 + a3 + try: + await a4.commit(NoneType) + except NvimError as e: + raise UnrecoverableError(e) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/refresh.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/refresh.py new file mode 100644 index 00000000..e90a610c --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/refresh.py @@ -0,0 +1,23 @@ +from contextlib import asynccontextmanager +from typing import AsyncIterator + +from pynvim_pp.nvim import Nvim + +from ..registry import rpc +from ..settings.localization import LANG +from ..state.types import State +from .shared.refresh import refresh as _refresh +from .types import Stage + + +@asynccontextmanager +async def with_manual() -> AsyncIterator[None]: + await Nvim.write(LANG("hourglass")) + yield None + await Nvim.write(LANG("ok_sym")) + + +@rpc(blocking=False) +async def refresh(state: State, is_visual: bool) -> Stage: + async with with_manual(): + return await _refresh(state) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/rename.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/rename.py new file mode 100644 index 00000000..533f7442 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/rename.py @@ -0,0 +1,75 @@ +from os.path import abspath, normpath +from pathlib import PurePath +from typing import Optional + +from pynvim_pp.hold import hold_win +from pynvim_pp.nvim import Nvim +from pynvim_pp.window import Window +from std2 import anext + +from ..fs.ops import ancestors, exists, rename +from ..lsp.notify import lsp_moved +from ..registry import rpc +from ..settings.localization import LANG +from ..state.next import forward +from ..state.types import State +from .shared.current import maybe_path_above +from .shared.index import indices +from .shared.refresh import refresh +from .shared.wm import kill_buffers +from .types import Stage + + +@rpc(blocking=False) +async def _rename(state: State, is_visual: bool) -> Optional[Stage]: + """ + rename file / folder + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if not node: + return None + else: + old_path = node.path + child = await Nvim.input(question=LANG("pencil"), default=old_path.name) + if not child: + return None + else: + new_path = PurePath(abspath(old_path.parent / child)) + operations = {old_path: new_path} + if await exists(new_path, follow=False): + await Nvim.write( + LANG("already_exists", name=normpath(new_path)), error=True + ) + return None + else: + killed = await kill_buffers( + last_used=state.window_order, + paths={old_path}, + reopen={old_path: new_path}, + ) + try: + await rename(operations) + except Exception as e: + await Nvim.write(e, error=True) + return await refresh(state=state) + else: + async with hold_win(win=None): + for win, new_path in killed.items(): + await Window.set_current(win) + escaped = await Nvim.fn.fnameescape(str, normpath(new_path)) + await Nvim.exec(f"edit! {escaped}") + + new_state = await maybe_path_above(state, paths={new_path}) or state + parents = ancestors(new_path) + invalidate_dirs = {old_path.parent, new_path.parent} + index = state.index | parents + new_selection = {new_path} if state.selection else frozenset() + next_state = await forward( + new_state, + index=index, + invalidate_dirs=invalidate_dirs, + selection=new_selection, + ) + await lsp_moved(operations) + return Stage(next_state, focus=new_path) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/resize.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/resize.py new file mode 100644 index 00000000..15f94afc --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/resize.py @@ -0,0 +1,42 @@ +from operator import add, sub +from typing import Callable, Optional + +from pynvim_pp.window import Window + +from ..registry import rpc +from ..state.next import forward +from ..state.types import State +from .shared.wm import is_fm_window, resize_fm_windows +from .types import Stage + + +async def _resize( + state: State, direction: Callable[[int, int], int] +) -> Optional[Stage]: + win = await Window.get_current() + if not await is_fm_window(win): + return None + else: + old_width = await win.get_width() + new_width = max(direction(old_width, 10), 1) + new_state = await forward(state, width=new_width) + await resize_fm_windows(new_state.window_order, width=new_state.width) + return Stage(new_state) + + +@rpc(blocking=False) +async def _bigger(state: State, is_visual: bool) -> Optional[Stage]: + """ + Bigger sidebar + """ + + return await _resize(state, direction=add) + + +@rpc(blocking=False) +async def _smaller(state: State, is_visual: bool) -> Optional[Stage]: + """ + Smaller sidebar + """ + + return await _resize(state, direction=sub) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/schedule_update.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/schedule_update.py new file mode 100644 index 00000000..7abe265e --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/schedule_update.py @@ -0,0 +1,37 @@ +from asyncio import gather +from typing import Optional + +from pynvim_pp.nvim import Nvim +from pynvim_pp.rpc_types import NvimError +from std2.asyncio import pure + +from ..lsp.diagnostics import poll +from ..registry import rpc +from ..state.next import forward +from ..state.ops import dump_session +from ..state.types import State +from ..version_ctl.git import status +from ..version_ctl.types import VCStatus +from .shared.refresh import refresh +from .types import Stage + + +@rpc(blocking=False) +async def scheduled_update(state: State, init: bool = False) -> Optional[Stage]: + cwd = await Nvim.getcwd() + store = dump_session(state) if state.vim_focus else pure(None) + + try: + stage, diagnostics, vc, _ = await gather( + refresh(state=state), + poll(state.settings.min_diagnostics_severity), + status(cwd, prev=state.vc) + if not init and state.enable_vc + else pure(VCStatus()), + store, + ) + except NvimError: + return None + else: + new_state = await forward(stage.state, diagnostics=diagnostics, vc=vc) + return Stage(new_state, focus=stage.focus) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/selection.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/selection.py new file mode 100644 index 00000000..12e6f051 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/selection.py @@ -0,0 +1,27 @@ +from ..registry import rpc +from ..state.next import forward +from ..state.types import State +from .shared.index import indices +from .types import Stage + + +@rpc(blocking=False) +async def _clear_selection(state: State, is_visual: bool) -> Stage: + """ + Clear selected + """ + + new_state = await forward(state, selection=frozenset()) + return Stage(new_state) + + +@rpc(blocking=False) +async def _select(state: State, is_visual: bool) -> Stage: + """ + Folder / File -> select + """ + + nodes = indices(state, is_visual=is_visual) + selection = state.selection ^ {node.path async for node in nodes} + new_state = await forward(state, selection=selection) + return Stage(new_state) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/__init__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/current.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/current.py new file mode 100644 index 00000000..d48f325d --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/current.py @@ -0,0 +1,65 @@ +from pathlib import PurePath +from typing import AbstractSet, Iterator, Optional + +from std2.locale import pathsort_key +from std2.pathlib import is_relative_to, longest_common_path + +from ...fs.cartographer import new +from ...fs.ops import ancestors +from ...state.next import forward +from ...state.types import State +from ..types import Stage + + +async def new_current_file(state: State, current: PurePath) -> Stage: + """ + New file focused in buf + """ + + parents = ancestors(current) + if state.root.path in parents: + invalidate_dirs = {current.parent} + index = state.index | parents + new_state = await forward( + state, + index=index, + invalidate_dirs=invalidate_dirs, + current=current, + ) + else: + new_state = await forward(state, current=current) + + focus = current if state.follow else None + return Stage(new_state, focus=focus) + + +async def new_root( + state: State, + new_cwd: PurePath, + indices: AbstractSet[PurePath], +) -> State: + index = state.index | ancestors(new_cwd) | {new_cwd} | indices + root = await new( + state.executor, follow_links=state.follow_links, root=new_cwd, index=index + ) + selection = {path for path in state.selection if root.path in ancestors(path)} + return await forward(state, root=root, selection=selection, index=index) + + +async def maybe_path_above( + state: State, paths: AbstractSet[PurePath] +) -> Optional[State]: + root = state.root.path + if all(is_relative_to(path, root) for path in paths): + return None + else: + + def cont() -> Iterator[PurePath]: + for path in paths: + lcp = longest_common_path(path, root) + yield lcp if lcp else path.parent + + ordered = sorted(cont(), key=pathsort_key) + indices = ancestors(*ordered) + new_cwd, *_ = ordered + return await new_root(state=state, new_cwd=new_cwd, indices=indices) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/index.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/index.py new file mode 100644 index 00000000..bb19675b --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/index.py @@ -0,0 +1,35 @@ +from typing import AsyncIterator, Optional + +from pynvim_pp.operators import operator_marks +from pynvim_pp.window import Window + +from ...fs.types import Node +from ...state.types import State +from .wm import is_fm_buffer + + +def _row_index(state: State, row: int) -> Optional[Node]: + if (row >= 0) and (row < len(state.derived.node_row_lookup)): + return state.derived.node_row_lookup[row] + else: + return None + + +async def indices(state: State, is_visual: bool) -> AsyncIterator[Node]: + win = await Window.get_current() + buf = await win.get_buf() + + if not await is_fm_buffer(buf): + return + else: + row, _ = await win.get_cursor() + if node := _row_index(state, row): + yield node + + if is_visual: + (row1, _), (row2, _) = await operator_marks(buf, visual_type=None) + + for r in range(row1, row2 + 1): + if r != row: + if node := _row_index(state, r): + yield node diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/open_file.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/open_file.py new file mode 100644 index 00000000..819e6ee1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/open_file.py @@ -0,0 +1,117 @@ +from contextlib import suppress +from mimetypes import guess_type +from os.path import getsize, normpath +from pathlib import PurePath +from posixpath import sep +from typing import AsyncContextManager, Optional, cast + +from pynvim_pp.buffer import Buffer +from pynvim_pp.hold import hold_win +from pynvim_pp.nvim import Nvim +from pynvim_pp.window import Window +from std2 import anext +from std2.aitertools import achain, to_async +from std2.contextlib import nullacontext + +from ...settings.localization import LANG +from ...state.next import forward +from ...state.types import State +from ..types import ClickType, Stage +from .wm import ( + find_buffers_with_file, + find_non_fm_windows_in_tab, + find_window_with_file_in_tab, + new_window, + resize_fm_windows, +) + + +async def _show_file(*, state: State, click_type: ClickType) -> None: + if click_type is ClickType.tertiary: + await Nvim.exec("tabnew") + win = await Window.get_current() + for key, val in state.settings.win_actual_opts.items(): + await win.opts.set(key, val=val) + + if path := state.current: + mgr = ( + cast(AsyncContextManager[None], hold_win(win=None)) + if click_type is ClickType.secondary + else nullacontext(None) + ) + async with mgr: + non_fm_windows = [ + win + async for win in find_non_fm_windows_in_tab( + last_used=state.window_order + ) + ] + buf = await anext(find_buffers_with_file(file=path), None) + win = await anext( + achain( + find_window_with_file_in_tab( + last_used=state.window_order, file=path + ), + to_async(non_fm_windows), + ), + cast(Window, None), + ) or await new_window( + last_used=state.window_order, + win_local=state.settings.win_actual_opts, + open_left=not state.settings.open_left, + width=None + if len(non_fm_windows) + else await Nvim.opts.get(int, "columns") - state.width - 1, + ) + + await Window.set_current(win) + non_fm_count = len(non_fm_windows) + + if click_type is ClickType.v_split and non_fm_count: + await Nvim.exec("vnew") + temp_buf = await Buffer.get_current() + await temp_buf.opts.set("bufhidden", val="wipe") + elif click_type is ClickType.h_split and non_fm_count: + await Nvim.exec("new") + temp_buf = await Buffer.get_current() + await temp_buf.opts.set("bufhidden", val="wipe") + + win = await Window.get_current() + + if buf: + await win.set_buf(buf) + else: + escaped = await Nvim.fn.fnameescape(str, normpath(path)) + await Nvim.exec(f"edit! {escaped}") + + await resize_fm_windows(last_used=state.window_order, width=state.width) + + +async def open_file( + state: State, path: PurePath, click_type: ClickType +) -> Optional[Stage]: + mime, _ = guess_type(path.name, strict=False) + m_type, _, _ = (mime or "").partition(sep) + + with suppress(OSError): + size = getsize(path) + + question = LANG("mime_warn", name=path.name, mime=str(mime)) + + go = ( + await Nvim.confirm( + question=question, + answers=LANG("ask_yesno"), + answer_key={1: True, 2: False}, + ) + if m_type in state.settings.mime.warn + and path.suffix not in state.settings.mime.allow_exts + else True + ) + + if go: + new_state = await forward(state, current=path) + await _show_file(state=new_state, click_type=click_type) + return Stage(new_state, focus=path) + else: + return None diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/refresh.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/refresh.py new file mode 100644 index 00000000..51202266 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/refresh.py @@ -0,0 +1,78 @@ +from asyncio import gather +from pathlib import PurePath +from typing import AbstractSet, Mapping + +from pynvim_pp.rpc_types import ExtData +from pynvim_pp.window import Window +from std2.types import Void + +from ...fs.ops import ancestors, exists_many +from ...nvim.markers import markers +from ...state.next import forward +from ...state.types import State +from ..shared.wm import find_current_buffer_path +from ..types import Stage + + +async def _index(state: State, paths: AbstractSet[PurePath]) -> AbstractSet[PurePath]: + index = { + path + for path, exists in (await exists_many(state.index, follow=True)).items() + if exists + } | paths + + return index + + +async def _selection(state: State) -> AbstractSet[PurePath]: + selection = { + selected + for selected, exists in ( + await exists_many(state.selection, follow=False) + ).items() + if exists + } + + return selection + + +async def _window_order(state: State) -> Mapping[ExtData, None]: + window_ids = {w.data for w in await Window.list()} + window_order = { + win_id: None for win_id in state.window_order if win_id in window_ids + } + return window_order + + +async def refresh(state: State) -> Stage: + cwd = state.root.path + invalidate_dirs = {cwd} + + current, index, selection, window_order, mks = await gather( + find_current_buffer_path(), + _index(state, paths=invalidate_dirs), + _selection(state), + _window_order(state), + markers(), + ) + current_ancestors = ancestors(current) if current else frozenset() + new_current = current if cwd in current_ancestors else None + + parent_paths: AbstractSet[PurePath] = ( + current_ancestors if state.follow else frozenset() + ) + new_index = index if new_current else index | parent_paths + focus = current if state.follow else None + + new_state = await forward( + state, + index=new_index, + selection=selection, + markers=mks, + invalidate_dirs=invalidate_dirs, + current=new_current or Void, + window_order=window_order, + trace=False, + ) + + return Stage(new_state, focus=focus) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/wm.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/wm.py new file mode 100644 index 00000000..db509e93 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/shared/wm.py @@ -0,0 +1,254 @@ +from contextlib import suppress +from math import inf +from pathlib import PurePath +from typing import ( + AbstractSet, + AsyncIterator, + Mapping, + Optional, + Sequence, + Tuple, + Union, + cast, +) +from urllib.parse import urlsplit + +from pynvim_pp.atomic import Atomic +from pynvim_pp.buffer import Buffer +from pynvim_pp.keymap import Keymap +from pynvim_pp.lib import resolve_path +from pynvim_pp.nvim import Nvim +from pynvim_pp.rpc_types import ExtData +from pynvim_pp.tabpage import Tabpage +from pynvim_pp.types import NoneType +from pynvim_pp.window import Window + +from ...consts import FM_FILETYPE, URI_SCHEME +from ...fs.ops import ancestors +from ...settings.types import Settings + + +def is_fm_buf_name(name: str) -> bool: + with suppress(ValueError): + uri = urlsplit(name) + return uri.scheme == URI_SCHEME + return False + + +async def is_fm_buffer(buf: Buffer) -> bool: + ft = await buf.filetype() + if ft == FM_FILETYPE: + return True + elif name := await buf.get_name(): + return is_fm_buf_name(name) + + return False + + +async def is_fm_window(win: Window) -> bool: + buf = await win.get_buf() + return await is_fm_buffer(buf) + + +async def find_windows_in_tab( + last_used: Mapping[ExtData, None], no_secondary: bool +) -> AsyncIterator[Window]: + ordering = {win_id: idx for idx, win_id in enumerate(reversed(last_used.keys()))} + tab = await Tabpage.get_current() + wins = await tab.list_wins() + + atomic = Atomic() + for win in wins: + atomic.win_get_position(win) + pos = cast(Sequence[Tuple[int, int]], await atomic.commit(NoneType)) + positions = {win.data: rc for win, rc in zip(wins, pos)} + + def key_by(win: Window) -> Tuple[float, float, float]: + """ + -> sort by last_used, then row, then col + """ + + order = ordering.get(win.data, inf) + row, col = positions.get(win.data, (inf, inf)) + return order, col, row + + ordered = sorted(wins, key=key_by) + + for win in ordered: + is_preview = await win.opts.get(bool, "previewwindow") + buf = await win.get_buf() + ft = await buf.filetype() + is_secondary = is_preview or ft == "qf" + if not is_secondary or not no_secondary: + yield win + + +async def find_fm_windows() -> AsyncIterator[Tuple[Window, Buffer]]: + for win in await Window.list(): + buf = await win.get_buf() + if await is_fm_buffer(buf): + yield win, buf + + +async def find_fm_windows_in_tab( + last_used: Mapping[ExtData, None] +) -> AsyncIterator[Window]: + async for win in find_windows_in_tab(last_used, no_secondary=True): + buf = await win.get_buf() + if await is_fm_buffer(buf): + yield win + + +async def find_non_fm_windows_in_tab( + last_used: Mapping[ExtData, None] +) -> AsyncIterator[Window]: + async for win in find_windows_in_tab(last_used, no_secondary=True): + buf = await win.get_buf() + if not await is_fm_buffer(buf): + yield win + + +async def find_window_with_file_in_tab( + last_used: Mapping[ExtData, None], file: PurePath +) -> AsyncIterator[Window]: + async for win in find_windows_in_tab(last_used, no_secondary=True): + buf = await win.get_buf() + if name := await buf.get_name(): + if PurePath(name) == file: + yield win + + +async def find_fm_buffers() -> AsyncIterator[Buffer]: + for buf in await Buffer.list(listed=True): + if await is_fm_buffer(buf): + yield buf + + +async def find_buffers_with_file(file: PurePath) -> AsyncIterator[Buffer]: + for buf in await Buffer.list(listed=True): + if name := await buf.get_name(): + if PurePath(name) == file: + yield buf + + +async def find_current_buffer_path( + buf_name: Optional[str] = None, +) -> Optional[PurePath]: + if buf_name is None: + buf = await Buffer.get_current() + buf_name = await buf.get_name() + + if buf_name and not is_fm_buf_name(buf_name): + return await resolve_path(None, path=buf_name) + + return None + + +async def setup_fm_buf(settings: Settings, buf: Buffer) -> None: + await buf.opts.set("modifiable", val=False) + await buf.opts.set("filetype", val=FM_FILETYPE) + await buf.opts.set("undolevels", val=-1) + + km = Keymap() + _ = km.n("{") << f"{settings.page_increment}g" + _ = km.n("}") << f"{settings.page_increment}g" + for function, mappings in settings.keymap.items(): + for mapping in mappings: + _ = ( + km.n(mapping, noremap=True, silent=True, nowait=True) + << f"lua {function}(false)" + ) + _ = ( + km.v(mapping, noremap=True, silent=True, nowait=True) + << rf"lua {function}(true)" + ) + + await km.drain(buf=buf).commit(NoneType) + + +async def new_fm_buffer(settings: Settings) -> Buffer: + buf = await Buffer.create( + listed=False, scratch=True, wipe=False, nofile=True, noswap=True + ) + await setup_fm_buf(settings, buf=buf) + return buf + + +async def restore_non_fm_win( + win_local: Mapping[str, Union[bool, str]], win: Window +) -> None: + await win.vars.set(URI_SCHEME, False) + for key, val in win_local.items(): + await win.opts.set(key, val=val) + + +async def new_window( + *, + last_used: Mapping[ExtData, None], + win_local: Mapping[str, Union[bool, str]], + open_left: bool, + width: Optional[int], +) -> Window: + split_r = await Nvim.opts.get(bool, "splitright") + + wins = [win async for win in find_windows_in_tab(last_used, no_secondary=False)] + focus_win = wins[0] if open_left else wins[-1] + direction = False if open_left else True + + await Nvim.opts.set("splitright", val=direction) + await Window.set_current(focus_win) + await Nvim.exec(f"{width}vnew" if width else "vnew") + await Nvim.opts.set("splitright", val=split_r) + + win = await Window.get_current() + buf = await win.get_buf() + await restore_non_fm_win(win_local, win=win) + await buf.opts.set("bufhidden", val="wipe") + return win + + +async def resize_fm_windows(last_used: Mapping[ExtData, None], width: int) -> None: + async for win in find_fm_windows_in_tab(last_used): + await win.set_width(width) + + +async def kill_buffers( + last_used: Mapping[ExtData, None], + paths: AbstractSet[PurePath], + reopen: Mapping[PurePath, PurePath], +) -> Mapping[Window, PurePath]: + active = ( + { + await win.get_buf(): win + async for win in find_non_fm_windows_in_tab( + last_used, + ) + } + if reopen + else {} + ) + + async def cont() -> AsyncIterator[Tuple[Window, PurePath]]: + for buf in await Buffer.list(listed=True): + if bufname := await buf.get_name(): + name = PurePath(bufname) + buf_paths = ancestors(name) | {name} + + if not buf_paths.isdisjoint(paths): + if ( + reopen + and (win := active.get(buf)) + and (new_path := reopen.get(name)) + ): + tmp = await Buffer.create( + listed=False, + scratch=True, + wipe=True, + nofile=True, + noswap=True, + ) + await win.set_buf(tmp) + yield win, new_path + await buf.delete() + + return {win: path async for win, path in cont()} diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/stat.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/stat.py new file mode 100644 index 00000000..30c5c124 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/stat.py @@ -0,0 +1,35 @@ +from pynvim_pp.nvim import Nvim +from std2 import anext +from std2.locale import si_prefixed + +from ..fs.ops import fs_stat +from ..registry import rpc +from ..state.types import State +from ..view.ops import display_path +from .shared.index import indices + + +@rpc(blocking=False) +async def _stat(state: State, is_visual: bool) -> None: + """ + Print file stat to cmdline + """ + + node = await anext(indices(state, is_visual=is_visual), None) + if not node: + return None + else: + try: + stat = await fs_stat(node.path) + except Exception as e: + await Nvim.write(e, error=True) + else: + permissions = stat.permissions + size = si_prefixed(stat.size, precision=2) + user = stat.user + group = stat.group + mtime = stat.date_mod.strftime(state.settings.view.time_fmt) + name = display_path(node.path, state=state) + full_name = f"{name} -> {stat.link}" if stat.link else name + mode_line = f"{permissions} {size}b {user} {group} {mtime} {full_name}" + await Nvim.write(mode_line) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/toggle_exec.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/toggle_exec.py new file mode 100644 index 00000000..be3f64d7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/toggle_exec.py @@ -0,0 +1,43 @@ +from os import chmod, stat, stat_result +from pathlib import PurePath +from stat import S_ISDIR, S_IXGRP, S_IXOTH, S_IXUSR +from typing import Iterator, Tuple + +from ..fs.cartographer import act_like_dir +from ..registry import rpc +from ..state.next import forward +from ..state.types import State +from .shared.index import indices +from .types import Stage + + +@rpc(blocking=False) +async def _toggle_exec(state: State, is_visual: bool) -> Stage: + """ + Toggle chmod +-x + """ + + selected = state.selection or { + node.path + async for node in indices(state, is_visual=is_visual) + if not act_like_dir(node, follow_links=state.follow_links) + } + + def cont() -> Iterator[Tuple[PurePath, stat_result]]: + for path in selected: + try: + st = stat(path) + except FileNotFoundError: + pass + else: + if not S_ISDIR(st.st_mode): + yield path, st + + stats = {path: st for path, st in cont()} + + for path, st in stats.items(): + chmod(path, st.st_mode ^ S_IXUSR ^ S_IXGRP ^ S_IXOTH) + + invalidate_dirs = {path.parent for path in stats.keys()} + new_state = await forward(state, invalidate_dirs=invalidate_dirs) + return Stage(state=new_state) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/toggle_open.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/toggle_open.py new file mode 100644 index 00000000..2e1b46fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/toggle_open.py @@ -0,0 +1,184 @@ +from dataclasses import dataclass +from pathlib import PurePath +from subprocess import CalledProcessError +from typing import Mapping, Optional, Sequence + +from pynvim_pp.nvim import Nvim +from pynvim_pp.rpc_types import ExtData +from pynvim_pp.window import Window +from std2 import anext +from std2.argparse import ArgparseError, ArgParser + +from ..fs.ops import exists, new, which +from ..registry import rpc +from ..settings.localization import LANG +from ..settings.types import Settings +from ..state.types import State +from ..version_ctl.git import root as version_ctl_toplv +from .shared.current import maybe_path_above, new_current_file, new_root +from .shared.open_file import open_file +from .shared.wm import ( + find_current_buffer_path, + find_fm_buffers, + find_fm_windows_in_tab, + find_windows_in_tab, + new_fm_buffer, + new_window, + resize_fm_windows, +) +from .types import ClickType, Stage + + +@dataclass(frozen=True) +class _Args: + path: Optional[PurePath] + version_ctl: bool + toggle: bool + focus: bool + + +def _parse_args(args: Sequence[str]) -> _Args: + parser = ArgParser() + parser.add_argument("path", nargs="?", type=PurePath) + parser.add_argument("--version-ctl", action="store_true") + + focus_group = parser.add_mutually_exclusive_group() + focus_group.add_argument( + "--always-focus", dest="toggle", action="store_false", default=True + ) + focus_group.add_argument( + "--nofocus", dest="focus", action="store_false", default=True + ) + + ns = parser.parse_args(args=args) + opts = _Args( + path=ns.path, + version_ctl=ns.version_ctl, + toggle=False if ns.version_ctl or ns.path else ns.toggle, + focus=ns.focus, + ) + return opts + + +async def _ensure_side_window( + *, + settings: Settings, + window_order: Mapping[ExtData, None], + width: int, + window: Window, +) -> None: + open_left = settings.open_left + windows = [ + win + async for win in find_windows_in_tab(last_used=window_order, no_secondary=False) + ] + target = windows[0] if open_left else windows[-1] + if window.data != target.data: + if open_left: + await Nvim.exec("wincmd H") + else: + await Nvim.exec("wincmd L") + await resize_fm_windows(last_used=window_order, width=width) + + +async def _open_fm_window( + settings: Settings, + window_order: Mapping[ExtData, None], + opts: _Args, + width: int, +) -> None: + cwin = await Window.get_current() + win = await anext(find_fm_windows_in_tab(last_used=window_order), None) + if win: + if opts.toggle: + wins = await Window.list() + if len(wins) > 1: + await win.close() + else: + await Window.set_current(win) + else: + buf = await anext(find_fm_buffers(), None) + if not buf: + buf = await new_fm_buffer(settings=settings) + + win = await new_window( + last_used=window_order, + win_local=settings.win_actual_opts, + open_left=settings.open_left, + width=width, + ) + + await win.set_buf(buf) + + await _ensure_side_window( + window=win, settings=settings, window_order=window_order, width=width + ) + if not opts.focus: + await Window.set_current(cwin) + + +@rpc(blocking=False) +async def _open(state: State, args: Sequence[str]) -> Optional[Stage]: + """ + Toggle sidebar + """ + + try: + opts = _parse_args(args) + except ArgparseError as e: + await Nvim.write(e, error=True) + return None + else: + if opts.version_ctl: + if git := which("git"): + try: + cwd = await version_ctl_toplv(git, cwd=state.root.path) + new_state = await new_root( + state=state, new_cwd=cwd, indices=frozenset() + ) + except CalledProcessError: + await Nvim.write(LANG("cannot find version ctl root"), error=True) + return None + else: + await Nvim.write(LANG("cannot find version ctl root"), error=True) + return None + else: + new_state = state + + if opts.path: + path = ( + opts.path + if opts.path.is_absolute() + else await Nvim.getcwd() / opts.path + ) + if not await exists(path, follow=True): + await new((path,)) + next_state = await maybe_path_above(new_state, paths={path}) or new_state + await _open_fm_window( + state.settings, + opts=opts, + window_order=new_state.window_order, + width=next_state.width, + ) + await open_file( + state=state, + path=path, + click_type=ClickType.primary, + ) + return Stage(next_state, focus=path) + else: + curr = await find_current_buffer_path() + stage = ( + await new_current_file(state=new_state, current=curr) if curr else None + ) + await _open_fm_window( + state.settings, + opts=opts, + window_order=new_state.window_order, + width=new_state.width, + ) + return ( + Stage(stage.state, focus=curr) + if stage + else Stage(new_state, focus=curr) + ) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/toggles.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/toggles.py new file mode 100644 index 00000000..13478416 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/toggles.py @@ -0,0 +1,78 @@ +from typing import Optional, Union + +from pynvim_pp.nvim import Nvim +from std2 import anext +from std2.types import Void, VoidType + +from ..registry import rpc +from ..settings.localization import LANG +from ..state.next import forward +from ..state.types import Selection, State +from ..version_ctl.types import VCStatus +from .shared.index import indices +from .types import Stage + + +@rpc(blocking=False) +async def _toggle_hidden(state: State, is_visual: bool) -> Optional[Stage]: + """ + Toggle hidden + """ + + node = await anext(indices(state, is_visual=is_visual)) + if not node: + return None + else: + focus = node.path + show_hidden = not state.show_hidden + selection: Selection = state.selection if show_hidden else frozenset() + new_state = await forward(state, show_hidden=show_hidden, selection=selection) + return Stage(new_state, focus=focus) + + +@rpc(blocking=False) +async def _toggle_follow(state: State, is_visual: bool) -> Stage: + """ + Toggle follow + """ + + new_state = await forward(state, follow=not state.follow) + await Nvim.write(LANG("follow_mode_indi", follow=str(new_state.follow))) + return Stage(new_state) + + +@rpc(blocking=False) +async def _toggle_follow_links(state: State, is_visual: bool) -> Stage: + """ + Toggle --follow + """ + + follow_links = not state.follow_links + new_state = await forward(state, follow_links=follow_links) + await Nvim.write(LANG("follow_links_indi", follow=str(new_state.follow_links))) + return Stage(new_state) + + +@rpc(blocking=False) +async def _toggle_follow_ignore(state: State, is_visual: bool) -> Stage: + """ + Toggle --follow + """ + + follow_links = not state.follow_links + new_state = await forward(state, follow_links=follow_links) + await Nvim.write(LANG("follow_ignore_indi", follow=str(new_state.follow_links))) + return Stage(new_state) + + +@rpc(blocking=False) +async def _toggle_version_control(state: State, is_visual: bool) -> Stage: + """ + Toggle version control + """ + + enable_vc = not state.enable_vc + vc: Union[VoidType, VCStatus] = Void if enable_vc else VCStatus() + new_state = await forward(state, enable_vc=enable_vc, vc=vc) + await Nvim.write(LANG("version_control_indi", enable_vc=str(new_state.enable_vc))) + return Stage(new_state) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/types.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/types.py new file mode 100644 index 00000000..abc8e376 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/transitions/types.py @@ -0,0 +1,20 @@ +from dataclasses import dataclass +from enum import Enum, auto +from pathlib import PurePath +from typing import Optional + +from ..state.types import State + + +class ClickType(Enum): + primary = auto() + secondary = auto() + tertiary = auto() + v_split = auto() + h_split = auto() + + +@dataclass(frozen=True) +class Stage: + state: State + focus: Optional[PurePath] = None diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/__init__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/git.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/git.py new file mode 100644 index 00000000..ca2c9540 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/git.py @@ -0,0 +1,189 @@ +from asyncio import gather +from functools import lru_cache +from itertools import chain +from locale import strxfrm +from os import linesep +from os.path import normpath +from pathlib import PurePath +from string import whitespace +from subprocess import CalledProcessError +from typing import ( + Iterable, + Iterator, + MutableMapping, + MutableSequence, + MutableSet, + Sequence, + Tuple, +) + +from std2.asyncio import to_thread +from std2.pathlib import ROOT +from std2.string import removeprefix, removesuffix + +from ..fs.ops import ancestors, which +from .nice import nice_call +from .types import VCStatus + +_Stats = Iterable[Tuple[str, PurePath]] + +_WHITE_SPACES = {*whitespace} +_GIT_LIST_CMD = ( + "--no-optional-locks", + "status", + "--ignored", + "--renames", + "--porcelain", + "-z", +) + + +_GIT_SUBMODULE_MARKER = "Entering " +_SUBMODULE_MARKER = "S" +_IGNORED_MARKER = "I" +_UNTRACKED_MARKER = "?" + + +async def root(git: PurePath, cwd: PurePath) -> PurePath: + stdout = await nice_call( + ( + git, + "--no-optional-locks", + "rev-parse", + "--path-format=relative", + "--show-toplevel", + ), + cwd=cwd, + ) + return PurePath(normpath(cwd / stdout.rstrip())) + + +@lru_cache(maxsize=1) +def _parse_stats_main(stdout: str) -> Sequence[Tuple[str, PurePath]]: + def cont() -> Iterator[Tuple[str, PurePath]]: + it = iter(stdout.split("\0")) + for line in it: + prefix, file = line[:2], line[3:] + yield prefix, PurePath(file) + + if "R" in prefix: + next(it, None) + + return tuple(cont()) + + +async def _stat_main(git: PurePath, cwd: PurePath) -> str: + stdout = await nice_call((git, *_GIT_LIST_CMD), cwd=cwd) + return stdout + + +@lru_cache(maxsize=1) +def _parse_sub_modules(stdout: str) -> Sequence[Tuple[str, PurePath]]: + def cont() -> Iterator[Tuple[str, PurePath]]: + it = iter(stdout) + sub_module = ROOT + acc: MutableSequence[str] = [] + + for char in it: + if char == linesep: + line = "".join(acc) + acc.clear() + + if not line.startswith(_GIT_SUBMODULE_MARKER): + raise ValueError(stdout) + else: + quoted = removeprefix(line, prefix=_GIT_SUBMODULE_MARKER) + if not (quoted.startswith("'") and quoted.endswith("'")): + raise ValueError(stdout) + else: + sub_module = PurePath( + removesuffix(removeprefix(quoted, prefix="'"), suffix="'") + ) + yield _SUBMODULE_MARKER, sub_module + + elif char == "\0": + line = "".join(acc) + acc.clear() + + if not sub_module: + raise ValueError(stdout) + else: + prefix, file = line[:2], line[3:] + yield prefix, sub_module / file + + if "R" in prefix: + next(it, None) + else: + acc.append(char) + + return tuple(cont()) + + +async def _stat_sub_modules(git: PurePath, cwd: PurePath) -> str: + stdout = await nice_call( + ( + git, + "--no-optional-locks", + "submodule", + "foreach", + "--recursive", + git, + *_GIT_LIST_CMD, + ), + cwd=cwd, + ) + return stdout + + +def _stat_name(stat: str) -> str: + markers = { + "!!": _IGNORED_MARKER, + "??": _UNTRACKED_MARKER, + } + return markers.get(stat, stat) + + +def _parse(root: PurePath, stats: _Stats) -> VCStatus: + above = ancestors(root) + ignored: MutableSet[PurePath] = set() + status: MutableMapping[PurePath, str] = {} + directories: MutableMapping[PurePath, MutableSet[str]] = {} + + for stat, name in stats: + path = root / name + status[path] = _stat_name(stat) + if "!" in stat: + ignored.add(path) + else: + for ancestor in ancestors(path): + parents = directories.setdefault(ancestor, set()) + if stat != _SUBMODULE_MARKER: + for sym in stat: + parents.add(sym) + + for directory, syms in directories.items(): + pre_existing = {*status.get(directory, "")} + symbols = pre_existing | syms - _WHITE_SPACES + consoildated = sorted(symbols, key=strxfrm) + status[directory] = "".join(consoildated) + + trimmed = {path: stat for path, stat in status.items() if path not in above} + return VCStatus(ignored=ignored, status=trimmed) + + +async def status(cwd: PurePath, prev: VCStatus) -> VCStatus: + if git := which("git"): + bin = PurePath(git) + try: + raw_root, main, submodules = await gather( + root(bin, cwd=cwd), + _stat_main(bin, cwd=cwd), + _stat_sub_modules(bin, cwd=cwd), + ) + stats = chain((_parse_stats_main(main)), _parse_sub_modules(submodules)) + except CalledProcessError: + return VCStatus() + else: + return await to_thread(lambda: _parse(raw_root, stats=stats)) + else: + return VCStatus() diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/nice.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/nice.py new file mode 100644 index 00000000..de1c3472 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/nice.py @@ -0,0 +1,42 @@ +import sys +from contextlib import suppress +from os import environ +from pathlib import PurePath +from typing import Optional, Sequence + +from pynvim_pp.lib import decode +from std2.asyncio.subprocess import call +from std2.pathlib import AnyPath + +if sys.platform == "win32": + from subprocess import BELOW_NORMAL_PRIORITY_CLASS + + nice = lambda _: None +else: + from os import nice + + BELOW_NORMAL_PRIORITY_CLASS = 0 + + +_ENV = {**environ, "LC_ALL": "C"} + + +def _nice() -> None: + with suppress(PermissionError): + nice(19) + + +async def nice_call( + argv: Sequence[AnyPath], + stdin: Optional[bytes] = None, + cwd: Optional[PurePath] = None, +) -> str: + proc = await call( + *argv, + cwd=cwd, + stdin=stdin, + env=_ENV, + preexec_fn=_nice, + creationflags=BELOW_NORMAL_PRIORITY_CLASS, + ) + return decode(proc.stdout) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/types.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/types.py new file mode 100644 index 00000000..aa9378d3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/version_ctl/types.py @@ -0,0 +1,12 @@ +from dataclasses import dataclass, field +from pathlib import PurePath +from typing import AbstractSet, Mapping, MutableMapping + + +@dataclass(frozen=True) +class VCStatus: + main: str = "" + submodules: str = "" + ignored: AbstractSet[PurePath] = frozenset() + status: Mapping[PurePath, str] = field(default_factory=dict) + ignore_cache: MutableMapping[PurePath, bool] = field(default_factory=dict) diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/view/__init__.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/view/highlight.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/highlight.py new file mode 100644 index 00000000..301e5f47 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/highlight.py @@ -0,0 +1,27 @@ +from typing import Iterator, Mapping, Tuple +from uuid import uuid4 + +from pynvim_pp.highlight import HLgroup + +from ..consts import FM_HL_PREFIX + +LEGAL_CTERM = { + "bold", + "underline", + "undercurl", + "strikethrough", + "reverse", + "italic", + "standout", +} + +LEGAL_CTERM_COLOURS = range(8) + + +def gen_hl(name_prefix: str, mapping: Mapping[str, str]) -> Mapping[str, HLgroup]: + def cont() -> Iterator[Tuple[str, HLgroup]]: + for key, val in mapping.items(): + name = f"{FM_HL_PREFIX}_{name_prefix}_{uuid4().hex}" + yield key, HLgroup(name=name, guifg=val) + + return {k: v for k, v in cont()} diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/view/load.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/load.py new file mode 100644 index 00000000..ae7b8032 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/load.py @@ -0,0 +1,118 @@ +from itertools import chain +from os import environ +from typing import Mapping, Tuple, TypeVar, Union + +from pynvim_pp.highlight import HLgroup +from std2.types import never + +from chad_types import ( + Artifact, + IconColourSetEnum, + IconGlyphs, + IconGlyphSetEnum, + LSColoursEnum, + TextColourSetEnum, +) + +from ..consts import FM_HL_PREFIX +from .highlight import gen_hl +from .ls_colours import parse_lsc +from .types import HLcontext, HLGroups + +T = TypeVar("T") + + +def _trans(mapping: Mapping[T, HLgroup]) -> Mapping[T, str]: + return {k: v.name for k, v in mapping.items()} + + +def load_theme( + artifact: Artifact, + particular_mappings: HLGroups, + discrete_colours: Mapping[str, str], + icon_set: IconGlyphSetEnum, + icon_colour_set: IconColourSetEnum, + text_colour_set: Union[LSColoursEnum, TextColourSetEnum], +) -> Tuple[IconGlyphs, HLcontext]: + if icon_set is IconGlyphSetEnum.ascii: + icons = artifact.icons.ascii + elif icon_set is IconGlyphSetEnum.ascii_hollow: + icons = artifact.icons.ascii_hollow + elif icon_set is IconGlyphSetEnum.devicons: + icons = artifact.icons.devicons + elif icon_set is IconGlyphSetEnum.emoji: + icons = artifact.icons.emoji + else: + never(icon_set) + + if text_colour_set is LSColoursEnum.env and "LS_COLORS" not in environ: + text_colour_set = LSColoursEnum.solarized_dark_256 + + if isinstance(text_colour_set, LSColoursEnum): + if text_colour_set is LSColoursEnum.env: + _lsc = environ.get("LS_COLORS", "") + elif text_colour_set is LSColoursEnum.solarized_dark_256: + _lsc = artifact.ls_colours.solarized_dark_256 + elif text_colour_set is LSColoursEnum.solarized_light: + _lsc = artifact.ls_colours.solarized_light + elif text_colour_set is LSColoursEnum.solarized_dark: + _lsc = artifact.ls_colours.solarized_dark + elif text_colour_set is LSColoursEnum.solarized_universal: + _lsc = artifact.ls_colours.solarized_universal + elif text_colour_set is LSColoursEnum.nord: + _lsc = artifact.ls_colours.nord + elif text_colour_set is LSColoursEnum.trapdoor: + _lsc = artifact.ls_colours.trapdoor + else: + never(text_colour_set) + + lsc = parse_lsc(_lsc, discrete_colours=discrete_colours) + mode_pre = lsc.mode_pre + mode_post = lsc.mode_post + ext_exact = lsc.exts + name_exact: Mapping[str, HLgroup] = {} + name_glob = lsc.name_glob + else: + if text_colour_set is TextColourSetEnum.nerdtree_syntax_light: + text_colour = artifact.text_colours.nerdtree_syntax_light + elif text_colour_set is TextColourSetEnum.nerdtree_syntax_dark: + text_colour = artifact.text_colours.nerdtree_syntax_dark + else: + never(text_colour_set) + + mode_pre = {} + mode_post = {} + ext_exact = gen_hl(FM_HL_PREFIX, mapping=text_colour.ext_exact) + name_exact = gen_hl(FM_HL_PREFIX, mapping=text_colour.name_exact) + name_glob = gen_hl(FM_HL_PREFIX, mapping=text_colour.name_glob) + + if icon_colour_set is IconColourSetEnum.github: + icon_exts = gen_hl(FM_HL_PREFIX, mapping=artifact.icon_colours.github) + elif icon_colour_set is IconColourSetEnum.none: + icon_exts = gen_hl(FM_HL_PREFIX, mapping={}) + else: + never(icon_colour_set) + + groups = tuple( + chain( + icon_exts.values(), + mode_pre.values(), + mode_post.values(), + ext_exact.values(), + name_exact.values(), + name_glob.values(), + ), + ) + + context = HLcontext( + groups=groups, + icon_exts=_trans(icon_exts), + mode_pre=_trans(mode_pre), + mode_post=_trans(mode_post), + ext_exact=_trans(ext_exact), + name_exact=_trans(name_exact), + name_glob=_trans(name_glob), + particular_mappings=particular_mappings, + ) + + return icons, context diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/view/ls_colours.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/ls_colours.py new file mode 100644 index 00000000..815b6f8f --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/ls_colours.py @@ -0,0 +1,303 @@ +from dataclasses import dataclass +from enum import Enum, IntEnum, auto +from itertools import chain, repeat +from typing import ( + AbstractSet, + Callable, + Iterator, + Mapping, + MutableMapping, + MutableSet, + Optional, + Tuple, + Union, +) +from uuid import uuid4 + +from pynvim_pp.highlight import HLgroup +from std2.coloursys import rgb_to_hex + +from ..consts import FM_HL_PREFIX +from ..fs.types import Mode + + +class _Style(IntEnum): + bold = auto() + dimmed = auto() + italic = auto() + underline = auto() + blink = auto() + blink_fast = auto() + reverse = auto() + hidden = auto() + strikethrough = auto() + + +class _Ground(Enum): + fore = auto() + back = auto() + + +class _AnsiColour(IntEnum): + black = auto() + red = auto() + green = auto() + yellow = auto() + blue = auto() + magenta = auto() + cyan = auto() + white = auto() + + bright_black = auto() + bright_red = auto() + bright_green = auto() + bright_yellow = auto() + bright_blue = auto() + bright_magenta = auto() + bright_cyan = auto() + bright_white = auto() + + +@dataclass(frozen=True) +class _Colour: + r: int + g: int + b: int + + +@dataclass(frozen=True) +class _Styling: + styles: AbstractSet[_Style] + foreground: Union[_AnsiColour, _Colour, None] + background: Union[_AnsiColour, _Colour, None] + + +@dataclass(frozen=True) +class LSC: + mode_pre: Mapping[Mode, HLgroup] + mode_post: Mapping[Optional[Mode], HLgroup] + exts: Mapping[str, HLgroup] + name_glob: Mapping[str, HLgroup] + + +_ANSI_RANGE = range(256) +_RGB_RANGE = range(256) + +_STYLE_TABLE: Mapping[str, _Style] = {str(code + 0): code for code in _Style} + +_GROUND_TABLE: Mapping[str, _Ground] = { + str(code): ground + for code, ground in chain( + zip(chain(range(30, 39), range(90, 98)), repeat(_Ground.fore)), + zip(chain(range(40, 49), range(100, 108)), repeat(_Ground.back)), + ) +} + +_COLOUR_TABLE: Mapping[str, _AnsiColour] = { + str(code): colour + for code, colour in chain( + ((c + 29 if c <= 8 else c + 31, c) for c in _AnsiColour), + ((c + 89 if c <= 8 else c + 91, c) for c in _AnsiColour), + ) +} + +_RGB_TABLE: AbstractSet[str] = {"38", "48"} + +_E_BASIC_TABLE: Mapping[int, _AnsiColour] = {i: c for i, c in enumerate(_AnsiColour)} + +_E_GREY_TABLE: Mapping[int, _Colour] = { + i: _Colour(r=s, g=s, b=s) + for i, s in enumerate((round(step / 23 * 255) for step in range(24)), 232) +} + + +def _parse_8(codes: Iterator[str]) -> Union[_AnsiColour, _Colour, None]: + try: + ansi_code = int(next(codes, "")) + except ValueError: + return None + else: + if ansi_code in _ANSI_RANGE: + basic = _E_BASIC_TABLE.get(ansi_code) + if basic: + return basic + grey = _E_GREY_TABLE.get(ansi_code) + if grey: + return grey + ratio = 255 / 5 + code = ansi_code - 16 + r = code // 36 + g = code % 36 // 6 + b = code % 36 % 6 + return _Colour(r=round(r * ratio), g=round(g * ratio), b=round(b * ratio)) + else: + return None + + +def _parse_24(codes: Iterator[str]) -> Optional[_Colour]: + try: + r, g, b = int(next(codes, "")), int(next(codes, "")), int(next(codes, "")) + except ValueError: + return None + else: + if r in _RGB_RANGE and g in _RGB_RANGE and b in _RGB_RANGE: + return _Colour(r=r, g=g, b=b) + else: + return None + + +_PARSE_TABLE: Mapping[ + str, Callable[[Iterator[str]], Union[_AnsiColour, _Colour, None]] +] = { + "5": _parse_8, + "2": _parse_24, +} + + +_SPECIAL_PRE_TABLE: Mapping[str, Mode] = { + "bd": Mode.block_device, + "ca": Mode.file_w_capacity, + "cd": Mode.char_device, + "di": Mode.folder, + "do": Mode.door, + "ex": Mode.executable, + "ln": Mode.link, + "mh": Mode.multi_hardlink, + "or": Mode.orphan_link, + "ow": Mode.other_writable, + "pi": Mode.pipe, + "sg": Mode.set_gid, + "so": Mode.socket, + "st": Mode.sticky, + "su": Mode.set_uid, + "tw": Mode.sticky_other_writable, +} + + +_SPECIAL_POST_TABLE: Mapping[str, Optional[Mode]] = { + "fi": Mode.file, + "no": None, +} + +_UNUSED = { + "mi": "colour of missing symlink pointee", + "cl": "ANSI clear", + "ec": "ANSI end_code", + "lc": "ANSI left_code", + "rc": "ANSI right_code", + "rs": "ANSI reset", +} + +assert _UNUSED + + +_HL_STYLE_TABLE: Mapping[_Style, Optional[str]] = { + _Style.bold: "bold", + _Style.dimmed: None, + _Style.italic: "italic", + _Style.underline: "underline", + _Style.blink: None, + _Style.blink_fast: None, + _Style.reverse: "reverse", + _Style.hidden: None, + _Style.strikethrough: "strikethrough", +} + + +def _parse_codes( + codes: str, +) -> Iterator[Union[_Style, Tuple[_Ground, Union[_AnsiColour, _Colour]]]]: + it = (code.lstrip("0") for code in codes.split(";")) + for code in it: + style = _STYLE_TABLE.get(code) + if style: + yield style + continue + ground = _GROUND_TABLE.get(code) + ansi_colour = _COLOUR_TABLE.get(code) + if ground and ansi_colour: + yield ground, ansi_colour + elif ground and code in _RGB_TABLE: + code = next(it, "") + parse = _PARSE_TABLE.get(code) + if parse: + colour = parse(it) + if colour: + yield ground, colour + + +def _parse_styling(codes: str) -> _Styling: + styles: MutableSet[_Style] = set() + colours: MutableMapping[_Ground, Union[_AnsiColour, _Colour]] = {} + for ret in _parse_codes(codes): + if isinstance(ret, _Style): + styles.add(ret) + elif isinstance(ret, tuple): + ground, colour = ret + colours[ground] = colour + + styling = _Styling( + styles=styles, + foreground=colours.get(_Ground.fore), + background=colours.get(_Ground.back), + ) + return styling + + +def _parseHLGroup(styling: _Styling, discrete_colours: Mapping[str, str]) -> HLgroup: + fg, bg = styling.foreground, styling.background + name = f"{FM_HL_PREFIX}_ls_{uuid4().hex}" + cterm = { + style + for style in (_HL_STYLE_TABLE.get(style) for style in styling.styles) + if style + } + ctermfg = fg.value - 1 if isinstance(fg, _AnsiColour) else None + ctermbg = bg.value - 1 if isinstance(bg, _AnsiColour) else None + guifg = ( + rgb_to_hex(fg.r, fg.g, fg.b) + if isinstance(fg, _Colour) + else (discrete_colours.get(fg.name) if isinstance(fg, _AnsiColour) else None) + ) + guibg = ( + rgb_to_hex(bg.r, bg.g, bg.b) + if isinstance(bg, _Colour) + else (discrete_colours.get(bg.name) if isinstance(bg, _AnsiColour) else None) + ) + group = HLgroup( + name=name, + cterm=cterm, + ctermfg=ctermfg, + ctermbg=ctermbg, + guifg=guifg, + guibg=guibg, + ) + return group + + +def parse_lsc(ls_colours: str, discrete_colours: Mapping[str, str]) -> LSC: + hl_lookup = { + key: _parseHLGroup(_parse_styling(val), discrete_colours=discrete_colours) + for key, _, val in ( + segment.partition("=") for segment in ls_colours.strip(":").split(":") + ) + } + + mode_pre = { + mode: hl + for indicator, mode in _SPECIAL_PRE_TABLE.items() + if (hl := hl_lookup.pop(indicator, None)) + } + mode_post = { + mode: hl + for indicator, mode in _SPECIAL_POST_TABLE.items() + if (hl := hl_lookup.pop(indicator, None)) + } + + _ext_keys = tuple( + key for key in hl_lookup if key.startswith("*.") and key.count(".") == 1 + ) + exts = {key[1:]: hl_lookup.pop(key) for key in _ext_keys} + + lsc = LSC(exts=exts, mode_pre=mode_pre, mode_post=mode_post, name_glob=hl_lookup) + return lsc diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/view/ops.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/ops.py new file mode 100644 index 00000000..73f78dd9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/ops.py @@ -0,0 +1,25 @@ +from os import sep +from os.path import relpath +from pathlib import Path, PurePath +from string import whitespace + +from ..state.types import State + +_WS = {*whitespace} - {"\t"} + + +def encode_for_display(text: str) -> str: + encoded = "".join( + char.encode("unicode_escape").decode("utf-8") if char in _WS else char + for char in text + ) + return encoded + + +def display_path(path: PurePath, state: State) -> str: + raw = relpath(path, start=state.root.path) + name = encode_for_display(raw) + if Path(path).is_dir(): + return f"{name}{sep}" + else: + return name diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/view/render.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/render.py new file mode 100644 index 00000000..093d4496 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/render.py @@ -0,0 +1,345 @@ +from collections import UserString +from enum import IntEnum, auto +from fnmatch import fnmatch +from functools import lru_cache +from locale import strxfrm +from os.path import extsep, sep +from pathlib import PurePath +from typing import Any, Callable, Iterator, Optional, Sequence, Tuple, Union, cast + +from pynvim_pp.lib import encode +from std2.platform import OS, os +from std2.types import never + +from ..fs.cartographer import is_dir, user_ignored +from ..fs.types import Mode, Node +from ..nvim.types import Markers +from ..settings.types import Settings +from ..state.types import Diagnostics, FilterPattern, Index, Selection +from ..version_ctl.types import VCStatus +from .ops import encode_for_display +from .types import Badge, Derived, Highlight, Sortby + + +class _CompVals(IntEnum): + FOLDER = auto() + FILE = auto() + + +_Str = Union[str, UserString] +_Render = Tuple[str, Sequence[Highlight], Sequence[Badge]] +_NRender = Tuple[Node, str, Sequence[Highlight], Sequence[Badge]] + + +class _str(UserString): + def __lt__(self, _: _Str) -> bool: + return False + + def __gt__(self, _: _Str) -> bool: + return True + + +_EMPTY = _str("") + + +def _suffixx(path: PurePath) -> _Str: + if path.suffix: + return strxfrm(path.suffix) + elif path.stem.startswith(extsep): + return strxfrm(path.stem) + else: + return _EMPTY + + +def _lax_suffix(path: PurePath) -> str: + return path.suffix or path.name + + +@lru_cache(maxsize=None) +def _gen_comp(sortby: Sequence[Sortby]) -> Callable[[Node], Any]: + def comp(node: Node) -> Sequence[Any]: + if node.cache.sort_by is None: + + def cont() -> Iterator[Any]: + for sb in sortby: + if sb is Sortby.is_folder: + yield _CompVals.FOLDER if is_dir(node) else _CompVals.FILE + elif sb is Sortby.ext: + yield "" if is_dir(node) else _suffixx(node.path) + elif sb is Sortby.file_name_lower: + yield strxfrm(node.path.name.casefold()) + elif sb is Sortby.file_name: + yield strxfrm(node.path.name) + else: + never(sb) + + node.cache.sort_by = tuple(cont()) + return node.cache.sort_by + + return comp + + +def _vc_ignored(node: Node, vc: VCStatus) -> bool: + path = node.path + if (ignored := vc.ignore_cache.get(path, None)) is not None: + return ignored + else: + ignored = not vc.ignored.isdisjoint({path} | {*map(PurePath, path.parents)}) + vc.ignore_cache[path] = ignored + return ignored + + +def _gen_spacer(depth: int) -> str: + return (depth * 2 - 1) * " " + + +def _paint( + settings: Settings, + index: Index, + selection: Selection, + markers: Markers, + diagnostics: Diagnostics, + vc: VCStatus, + follow_links: bool, + show_hidden: bool, + current: Optional[PurePath], +) -> Callable[[Node, int], Optional[_Render]]: + icons = settings.view.icons + context = settings.view.hl_context + + def search_icon_hl(node: Node, ignored: bool) -> Optional[str]: + if ignored: + return context.particular_mappings.ignored + else: + return context.icon_exts.get(_lax_suffix(node.path)) + + def search_text_hl(node: Node, ignored: bool) -> Optional[str]: + if ignored: + return context.particular_mappings.ignored + + s_modes = sorted(node.mode) + for mode in s_modes: + if os is OS.windows and mode is Mode.other_writable: + pass + elif hl := context.mode_pre.get(mode): + return hl + + if hl := context.name_exact.get(node.path.name): + return hl + + for pattern, hl in context.name_glob.items(): + if fnmatch(node.path.name, pattern): + return hl + + if hl := context.ext_exact.get(_lax_suffix(node.path)): + return hl + + for mode in s_modes: + if hl := context.mode_post.get(mode): + return hl + else: + return context.mode_post.get(None) + + def gen_status(path: PurePath) -> str: + selected = ( + icons.status.selected if path in selection else icons.status.not_selected + ) + active = icons.status.active if path == current else icons.status.inactive + return f"{selected}{active}" + + def gen_decor_pre(node: Node, depth: int) -> Iterator[str]: + yield _gen_spacer(depth) + yield gen_status(node.path) + + def gen_icon(node: Node) -> Iterator[str]: + yield " " + if is_dir(node): + if node.pointed and not follow_links: + yield icons.link.normal + elif node.path in index: + yield icons.folder.open + else: + yield icons.folder.closed + else: + yield ( + ( + icons.name_exact.get(node.path.name, "") + or icons.ext_exact.get(_lax_suffix(node.path), "") + or next( + ( + v + for k, v in icons.name_glob.items() + if fnmatch(node.path.name, k) + ), + icons.default_icon, + ) + ) + if settings.view.use_icons + else icons.default_icon + ) + yield " " + + def gen_name(node: Node) -> Iterator[str]: + yield encode_for_display(node.path.name) + if not settings.view.use_icons and is_dir(node): + yield sep + + def gen_decor_post(node: Node) -> Iterator[str]: + mode = node.mode + if Mode.orphan_link in mode: + yield " " + yield icons.link.broken + elif Mode.link in mode: + yield " " + if is_dir(node) and not follow_links: + if node.path in index: + yield icons.folder.open + else: + yield icons.folder.closed + else: + yield icons.link.normal + + def gen_badges(path: PurePath) -> Iterator[Badge]: + l = "" + if diagnostic := diagnostics.get(path, {}): + l = " " + dl = len(diagnostic) + for idx, (severity, count) in enumerate(sorted(diagnostic.items())): + group = context.particular_mappings.diagnostics.get( + severity, context.particular_mappings.diagnostic_unknown + ) + lhs, rhs = not idx, idx + 1 == dl + r = " " if dl > 1 and not rhs else "" + if lhs: + yield Badge( + text="{", group=context.particular_mappings.diagnostic_context + ) + yield Badge(text=f"{count}{r}", group=group) + if rhs: + yield Badge( + text="}", group=context.particular_mappings.diagnostic_context + ) + + if marks := markers.bookmarks.get(path): + ordered = "".join(sorted(marks)) + yield Badge( + text=f"{l}<{ordered}>", + group=context.particular_mappings.bookmarks, + ) + + if qf_count := markers.quick_fix.get(path): + yield Badge( + text=f"{l}({qf_count})", + group=context.particular_mappings.quickfix, + ) + + if stat := vc.status.get(path): + yield Badge( + text=f"{l}[{stat}]", + group=context.particular_mappings.version_control, + ) + + def gen_highlights( + node: Node, pre: str, icon: str, name: str, ignored: bool + ) -> Iterator[Highlight]: + icon_begin = len(encode(pre)) + icon_end = icon_begin + len(encode(icon)) + text_begin = icon_end + text_end = len(encode(name)) + text_begin + + if icon_group := search_icon_hl(node, ignored=ignored): + hl = Highlight(group=icon_group, begin=icon_begin, end=icon_end) + yield hl + + if text_group := search_text_hl(node, ignored=ignored): + hl = Highlight(group=text_group, begin=text_begin, end=text_end) + yield hl + + def show(node: Node, depth: int) -> Optional[_Render]: + _user_ignored = user_ignored(node, ignores=settings.ignores) + vc_ignored = _vc_ignored(node, vc=vc) + ignored = vc_ignored or _user_ignored + + if depth and _user_ignored and not show_hidden: + return None + else: + pre = "".join(gen_decor_pre(node, depth=depth)) + icon = "".join(gen_icon(node)) + name = "".join(gen_name(node)) + post = "".join(gen_decor_post(node)) + + line = f"{pre}{icon}{name}{post}" + badges = tuple(gen_badges(node.path)) + highlights = tuple( + gen_highlights(node, pre=pre, icon=icon, name=name, ignored=ignored) + ) + return line, highlights, badges + + return show + + +def render( + node: Node, + *, + settings: Settings, + index: Index, + selection: Selection, + filter_pattern: Optional[FilterPattern], + markers: Markers, + diagnostics: Diagnostics, + vc: VCStatus, + follow_links: bool, + show_hidden: bool, + current: Optional[PurePath], +) -> Derived: + show = _paint( + settings, + index=index, + selection=selection, + markers=markers, + diagnostics=diagnostics, + vc=vc, + follow_links=follow_links, + show_hidden=show_hidden, + current=current, + ) + comp = _gen_comp(settings.view.sort_by) + keep_open = {node.path} + + def render(node: Node, *, depth: int, cleared: bool) -> Iterator[_NRender]: + clear = ( + cleared + or not filter_pattern + or fnmatch(node.path.name, filter_pattern.pattern) + ) + + if rend := show(node, depth): + + def gen_children() -> Iterator[_NRender]: + for child in sorted(node.children.values(), key=comp): + yield from render(child, depth=depth + 1, cleared=clear) + + children = tuple(gen_children()) + if clear or children or node.path in keep_open: + yield (node, *rend) + yield from iter(children) + + rendered = render(node, depth=0, cleared=False) + _nodes, _lines, _highlights, _badges = zip(*rendered) + nodes, lines, highlights, badges = ( + cast(Sequence[Node], _nodes), + cast(Sequence[str], _lines), + cast(Sequence[Sequence[Highlight]], _highlights), + cast(Sequence[Sequence[Badge]], _badges), + ) + hashed = tuple(str(hash(zipped)) for zipped in zip(lines, highlights, badges)) + path_row_lookup = {node.path: idx for idx, node in enumerate(nodes)} + derived = Derived( + lines=lines, + highlights=highlights, + badges=badges, + hashed=hashed, + node_row_lookup=nodes, + path_row_lookup=path_row_lookup, + ) + return derived diff --git a/config/neovim/store/lazy-plugins/chadtree/chadtree/view/types.py b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/types.py new file mode 100644 index 00000000..1c11037a --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/chadtree/view/types.py @@ -0,0 +1,74 @@ +from dataclasses import dataclass +from enum import Enum, auto +from pathlib import PurePath +from typing import Mapping, Optional, Sequence + +from pynvim_pp.highlight import HLgroup + +from chad_types import IconGlyphs + +from ..fs.types import Mode, Node + + +@dataclass(frozen=True) +class HLGroups: + bookmarks: str + ignored: str + marks: str + quickfix: str + diagnostics: Mapping[int, str] + diagnostic_unknown: str + diagnostic_context: str + version_control: str + + +@dataclass(frozen=True) +class HLcontext: + groups: Sequence[HLgroup] + icon_exts: Mapping[str, str] + mode_pre: Mapping[Mode, str] + mode_post: Mapping[Optional[Mode], str] + name_exact: Mapping[str, str] + name_glob: Mapping[str, str] + ext_exact: Mapping[str, str] + particular_mappings: HLGroups + + +class Sortby(Enum): + is_folder = auto() + ext = auto() + file_name_lower = auto() + file_name = auto() + + +@dataclass(frozen=True) +class ViewOptions: + hl_context: HLcontext + icons: IconGlyphs + sort_by: Sequence[Sortby] + time_fmt: str + use_icons: bool + + +@dataclass(frozen=True) +class Badge: + text: str + group: str + + +@dataclass(frozen=True) +class Highlight: + begin: int + end: int + group: str + + +@dataclass(frozen=True) +class Derived: + lines: Sequence[str] + highlights: Sequence[Sequence[Highlight]] + badges: Sequence[Sequence[Badge]] + + hashed: Sequence[str] + node_row_lookup: Sequence[Node] + path_row_lookup: Mapping[PurePath, int] diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/__init__.py b/config/neovim/store/lazy-plugins/chadtree/ci/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/__main__.py b/config/neovim/store/lazy-plugins/chadtree/ci/__main__.py new file mode 100644 index 00000000..eef3012a --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/__main__.py @@ -0,0 +1,31 @@ +from json import dump + +from std2.graphlib import recur_sort +from std2.pickle.encoder import new_encoder + +from chad_types import ARTIFACT, Artifact + +from .icon_colours import load_icon_colours +from .ls_colours import load_ls_colours +from .text_decorations import load_text_decors + + +def main() -> None: + encode = new_encoder[Artifact](Artifact) + ls_colours = load_ls_colours() + icon_colours = load_icon_colours() + icons, text_colours = load_text_decors() + + artifact = Artifact( + icons=icons, + ls_colours=ls_colours, + icon_colours=icon_colours, + text_colours=text_colours, + ) + + json = recur_sort(encode(artifact)) + with ARTIFACT.open("w") as fd: + dump(json, fd, ensure_ascii=False, check_circular=False, indent=2) + + +main() diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/icon_colours/__init__.py b/config/neovim/store/lazy-plugins/chadtree/ci/icon_colours/__init__.py new file mode 100644 index 00000000..b516da4d --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/icon_colours/__init__.py @@ -0,0 +1,47 @@ +from dataclasses import dataclass +from typing import Mapping, Optional, Sequence + +from std2.pickle.decoder import new_decoder +from std2.urllib import urlopen +from yaml import safe_load + +from chad_types import Hex, IconColours, IconColourSet + +_LINGUIST = """ +https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml +""" + + +@dataclass(frozen=True) +class _GithubColours: + extensions: Sequence[str] = () + color: Optional[Hex] = None + + +_GithubSpec = Mapping[str, _GithubColours] + + +def _fetch(uri: str) -> str: + with urlopen(uri) as resp: + code = resp.getcode() + body = resp.read() + if code != 200: + raise Exception(resp.headers, body) + else: + return body.decode() + + +def load_icon_colours() -> IconColourSet: + decode = new_decoder[_GithubSpec](_GithubSpec, strict=False) + + rawGH = _fetch(_LINGUIST) + yamlGH = decode(safe_load(rawGH)) + + github: IconColours = { + ext: spec.color + for spec in yamlGH.values() + for ext in spec.extensions + if spec.color + } + colours = IconColourSet(github=github) + return colours diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/Dockerfile b/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/Dockerfile new file mode 100644 index 00000000..a6aa4fe8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/Dockerfile @@ -0,0 +1,19 @@ +FROM ubuntu:latest + + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends -- python3 git ca-certificates && \ + rm -rf -- /var/lib/apt/lists/* && \ + update-ca-certificates + + +WORKDIR /root + + +RUN git clone --depth=1 https://github.com/seebi/dircolors-solarized.git && \ + git clone --depth=1 https://github.com/arcticicestudio/nord-dircolors.git && \ + git clone --depth=1 https://github.com/trapd00r/LS_COLORS +COPY . / + + +ENTRYPOINT ["./lsc.py"] diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/__init__.py b/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/__init__.py new file mode 100644 index 00000000..044afb39 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/__init__.py @@ -0,0 +1,18 @@ +from json import loads +from pathlib import Path + +from std2.pickle.decoder import new_decoder + +from chad_types import LSColourSet + +from ..run import docker_run + +_DOCKERFILE = Path(__file__).resolve(strict=True).with_name("Dockerfile") + + +def load_ls_colours() -> LSColourSet: + decode = new_decoder[LSColourSet](LSColourSet) + + json = loads(docker_run(_DOCKERFILE)) + lsc = decode(json) + return lsc diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/root/lsc.py b/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/root/lsc.py new file mode 100755 index 00000000..a030131b --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/root/lsc.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +from json import dump +from os.path import normcase +from pathlib import Path +from subprocess import check_output +from sys import stdout + +_LSC_SH = Path(__file__).resolve(strict=True).with_name("lsc.sh") + +_SOLARIZED = Path("dircolors-solarized").resolve(strict=True) +_NORD = Path("nord-dircolors").resolve(strict=True) +_TRAP_DOOR = Path("LS_COLORS").resolve(strict=True) + +_PARSING = { + _SOLARIZED / "dircolors.256dark": "solarized_dark_256", + _SOLARIZED / "dircolors.ansi-dark": "solarized_dark", + _SOLARIZED / "dircolors.ansi-light": "solarized_light", + _SOLARIZED / "dircolors.ansi-universal": "solarized_universal", + _NORD / "src" / "dir_colors": "nord", + _TRAP_DOOR / "LS_COLORS": "trapdoor", +} + + +def main() -> None: + lsc = { + dest: check_output((str(_LSC_SH), normcase(file_name)), text=True) + for file_name, dest in _PARSING.items() + } + dump(lsc, stdout, ensure_ascii=False, check_circular=False) + + +main() diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/root/lsc.sh b/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/root/lsc.sh new file mode 100755 index 00000000..ce0ce121 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/ls_colours/root/lsc.sh @@ -0,0 +1,9 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/bash + +set -eu +set -o pipefail + +FILE="$1" +export TERM=xterm-256color +eval "$(dircolors -b "$FILE")" +printf '%s' "$LS_COLORS" diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/prepare.py b/config/neovim/store/lazy-plugins/chadtree/ci/prepare.py new file mode 100755 index 00000000..3669d087 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/prepare.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +from datetime import datetime, timezone +from os import environ, sep +from pathlib import Path +from subprocess import check_call, check_output, run +from sys import executable +from typing import Iterator + +_TOP_LV = Path(__file__).resolve(strict=True).parent.parent + + +def _git_identity() -> None: + email = "ci@ci.ci" + username = "ci-bot" + check_call(("git", "config", "--global", "user.email", email)) + check_call(("git", "config", "--global", "user.name", username)) + + +def _get_branch() -> str: + if ref := environ.get("GITHUB_REF"): + return ref.replace("refs/heads/", "") + else: + br = check_output(("git", "branch", "--show-current"), text=True, cwd=_TOP_LV) + return br.strip() + + +def _git_clone(path: Path) -> None: + if not path.is_dir(): + if token := environ.get("CI_TOKEN"): + uri = f"https://ms-jpq:{token}@github.com/ms-jpq/chadtree.git" + else: + uri = "git@github.com:ms-jpq/chadtree.git" + + branch = _get_branch() + check_call(("git", "clone", "--branch", branch, uri, path)) + + +def _build() -> None: + check_call((executable, "-m", "ci"), cwd=_TOP_LV) + + +def _git_alert(cwd: Path) -> None: + prefix = "ci" + remote_brs = check_output(("git", "branch", "--remotes"), text=True, cwd=cwd) + + def cont() -> Iterator[str]: + for br in remote_brs.splitlines(): + b = br.strip() + if b and "->" not in b: + _, _, name = b.partition(sep) + if name.startswith(prefix): + yield name + + refs = tuple(cont()) + + if refs: + check_call(("git", "push", "--delete", "origin", *refs), cwd=cwd) + + proc = run(("git", "diff", "--exit-code"), cwd=cwd) + print(proc) + if proc.returncode: + time = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d") + brname = f"{prefix}--{time}" + check_call(("git", "checkout", "-b", brname), cwd=cwd) + check_call(("git", "add", "."), cwd=cwd) + check_call(("git", "commit", "-m", f"update_icons: {time}"), cwd=cwd) + check_call( + ("git", "push", "--force", "--set-upstream", "origin", brname), cwd=cwd + ) + + +def main() -> None: + cwd = Path() / "temp" + if "CI" in environ: + _git_identity() + _git_clone(cwd) + _build() + _git_alert(_TOP_LV) + + +main() diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/run.py b/config/neovim/store/lazy-plugins/chadtree/ci/run.py new file mode 100644 index 00000000..6ae694e5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/run.py @@ -0,0 +1,25 @@ +from pathlib import Path +from subprocess import check_call, check_output + + +def docker_run(dockerfile: Path) -> str: + parent = dockerfile.parent + name = f"chad_{parent.name}" + check_call( + ( + "docker", + "buildx", + "build", + "--progress", + "plain", + "--tag", + name, + "--file", + dockerfile, + "--", + ".", + ), + cwd=parent, + ) + output = check_output(("docker", "run", "--rm", name), text=True) + return output diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/Dockerfile b/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/Dockerfile new file mode 100644 index 00000000..1aafae84 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/Dockerfile @@ -0,0 +1,21 @@ +FROM ubuntu:latest + + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends -- neovim git ca-certificates && \ + rm -rf -- /var/lib/apt/lists/* + + +WORKDIR /root + + +RUN git clone --depth=1 https://github.com/ryanoasis/vim-devicons.git && \ + git clone --depth=1 https://github.com/adelarsq/vim-emoji-icon-theme.git && \ + git clone --depth=1 https://github.com/tiagofumo/vim-nerdtree-syntax-highlight.git + + +COPY . / + + +RUN nvim --headless +ENTRYPOINT ["cat", "exports.json"] diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/__init__.py b/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/__init__.py new file mode 100644 index 00000000..8d82ae6e --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/__init__.py @@ -0,0 +1,104 @@ +from dataclasses import dataclass +from json import loads +from pathlib import Path +from typing import Mapping, Tuple + +from std2.coloursys import hex_inverse +from std2.graphlib import merge +from std2.pickle.decoder import new_decoder +from yaml import safe_load + +from chad_types import ASSETS, Hex, IconGlyphs, IconGlyphSet, TextColours, TextColourSet + +from ..run import docker_run + + +@dataclass(frozen=True) +class _TCAliases: + ext_exact: Mapping[str, str] + name_exact: Mapping[str, str] + name_glob: Mapping[str, str] + + +@dataclass(frozen=True) +class _Aliases: + icon_colours: Mapping[str, str] + text_colours: _TCAliases + + +_DOCKERFILE = Path(__file__).resolve(strict=True).with_name("Dockerfile") +_ICON_BASE = ASSETS / "icon_base.yml" +_ALIASES = ASSETS / "aliases.yml" + + +def _process_exts(exts: Mapping[str, str]) -> Mapping[str, str]: + return {f".{k}": v for k, v in exts.items()} + + +def _process_glob(glob: Mapping[str, str]) -> Mapping[str, str]: + return {k.rstrip("$").replace(r"\.", "."): v for k, v in glob.items()} + + +def _process_hexcode(colours: Mapping[str, str]) -> Mapping[str, Hex]: + return {k: f"#{v}" for k, v in colours.items()} + + +def _process_inverse(colours: Mapping[str, str]) -> Mapping[str, str]: + return {k: hex_inverse(v) for k, v in colours.items()} + + +def _process_icons(icons: IconGlyphs) -> IconGlyphs: + return IconGlyphs( + default_icon=icons.default_icon, + folder=icons.folder, + link=icons.link, + status=icons.status, + ext_exact=_process_exts(icons.ext_exact), + name_exact=icons.name_exact, + name_glob=_process_glob(icons.name_glob), + ) + + +def _process_colours(colours: TextColours) -> TextColours: + return TextColours( + ext_exact=_process_hexcode(_process_exts(colours.ext_exact)), + name_exact=_process_hexcode(colours.name_exact), + name_glob=_process_hexcode(_process_glob(colours.name_glob)), + ) + + +def _make_lightmode(colours: TextColours) -> TextColours: + return TextColours( + ext_exact=_process_inverse(colours.ext_exact), + name_exact=_process_inverse(colours.name_exact), + name_glob=_process_inverse(colours.name_glob), + ) + + +def load_text_decors() -> Tuple[IconGlyphSet, TextColourSet]: + i_decode = new_decoder[IconGlyphSet](IconGlyphSet, strict=False) + c_decode = new_decoder[TextColourSet](TextColourSet, strict=False) + a_decode = new_decoder[_Aliases](_Aliases) + + icon_base = safe_load(_ICON_BASE.read_text("UTF-8")) + aliases = safe_load(_ALIASES.read_text("UTF-8")) + + json = loads(docker_run(_DOCKERFILE)) + data = merge(json, icon_base) + icon_spec = i_decode(data) + ali = a_decode(aliases) + + icon_set = IconGlyphSet( + ascii=_process_icons(icon_spec.ascii), + ascii_hollow=_process_icons(icon_spec.ascii_hollow), + devicons=_process_icons(icon_spec.devicons), + emoji=_process_icons(icon_spec.emoji), + ) + colour_spec = c_decode(data) + colour_set = TextColourSet( + nerdtree_syntax_light=_make_lightmode( + _process_colours(colour_spec.nerdtree_syntax_light) + ), + nerdtree_syntax_dark=_process_colours(colour_spec.nerdtree_syntax_dark), + ) + return icon_set, colour_set diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/root/.config/nvim/init.vim b/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/root/.config/nvim/init.vim new file mode 100644 index 00000000..8c161e57 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/root/.config/nvim/init.vim @@ -0,0 +1 @@ +lua require 'chad' diff --git a/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/root/.config/nvim/lua/chad.lua b/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/root/.config/nvim/lua/chad.lua new file mode 100644 index 00000000..a490321c --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/ci/text_decorations/root/.config/nvim/lua/chad.lua @@ -0,0 +1,49 @@ +local export_icons = function() + return { + ext_exact = vim.g.WebDevIconsUnicodeDecorateFileNodesExtensionSymbols, + name_exact = vim.g.WebDevIconsUnicodeDecorateFileNodesExactSymbols, + name_glob = vim.g.WebDevIconsUnicodeDecorateFileNodesPatternSymbols, + default_icon = vim.g.WebDevIconsUnicodeDecorateFileNodesDefaultSymbol, + folder = { + open = vim.g.DevIconsDefaultFolderOpenSymbol, + closed = vim.g.WebDevIconsUnicodeDecorateFolderNodesDefaultSymbol + } + } +end + +local export_colours = function() + return { + ext_exact = vim.g.NERDTreeExtensionHighlightColor, + name_exact = vim.g.NERDTreeExactMatchHighlightColor, + name_glob = vim.g.NERDTreePatternMatchHighlightColor + } +end + +local load_rtp = function(src) + vim.o.runtimepath = vim.o.runtimepath .. "," .. "/root/" .. src +end + +local load_viml = function(src) + vim.cmd("source " .. "/root/" .. src) +end + +load_viml "vim-devicons/plugin/webdevicons.vim" +local devicons = export_icons() + +load_viml "vim-emoji-icon-theme/plugin/vim-emoji-icon-theme.vim" +local emoji = export_icons() + +load_viml "vim-nerdtree-syntax-highlight/after/syntax/nerdtree.vim" +local nerdtree_syntax = export_colours() + +local exports = { + devicons = devicons, + emoji = emoji, + nerdtree_syntax_light = nerdtree_syntax, + nerdtree_syntax_dark = nerdtree_syntax +} +local json = vim.fn.json_encode(exports) + +vim.fn.writefile({json}, "exports.json") + +os.exit(0) diff --git a/config/neovim/store/lazy-plugins/chadtree/config/defaults.yml b/config/neovim/store/lazy-plugins/chadtree/config/defaults.yml new file mode 100644 index 00000000..8ddb9096 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/config/defaults.yml @@ -0,0 +1,171 @@ +--- +idle_timeout: 1.0 +ignore: + name_exact: + - .DS_Store + - .directory + - .git + - .localized + - thumbs.db + name_glob: [] + path_glob: [] +keymap: + bigger: + - + + - "=" + bookmark_goto: + - m + change_dir: + - b + change_focus: + - c + change_focus_up: + - C + clear_filter: + - F + clear_selection: + - S + collapse: + - + - "`" + copy: + - p + copy_basename: + - Y + copy_name: + - y + copy_relname: + - + cut: + - x + delete: + - d + filter: + - f + h_split: + - W + jump_to_current: + - J + link: + - A + new: + - a + open_sys: + - o + primary: + - + quit: + - q + refocus: + - "~" + refresh: + - + rename: + - r + secondary: + - + - <2-leftmouse> + select: + - s + smaller: + - "-" + - _ + stat: + - K + tertiary: + - + - + toggle_exec: + - X + toggle_follow: + - u + toggle_follow_links: + - U + toggle_follow_ignore: + - T + toggle_hidden: + - . + toggle_version_control: + - i + trash: + - t + v_split: + - w +options: + close_on_open: false + follow: true + follow_links: true + follow_ignore: false + lang: null + mimetypes: + allow_exts: + - .svg + - .ts + warn: + - audio + - font + - image + - video + min_diagnostics_severity: 2 + page_increment: 5 + polling_rate: 2.0 + session: true + show_hidden: false + version_control: + enable: true +profiling: false +theme: + discrete_colour_map: + black: "#202020" + blue: "#4eb5e0" + bright_black: "#000000" + bright_blue: "#58c7ff" + bright_cyan: "#5ac8a9" + bright_green: "#8dc437" + bright_magenta: "#ffa3cc" + bright_red: "#ff8883" + bright_white: "#feffff" + bright_yellow: "#ffa84f" + cyan: "#52bcaf" + green: "#96bc00" + magenta: "#f587c5" + red: "#ff4c41" + white: "#feffff" + yellow: "#f6a311" + highlights: + bookmarks: Title + ignored: Comment + marks: Keyword + quickfix: Label + version_control: Comment + diagnostics: + 1: DiagnosticError + 2: DiagnosticWarn + 3: DiagnosticInfo + 4: DiagnosticHint + 5: DiagnosticOk + diagnostic_unknown: DiagnosticOk + diagnostic_context: Menu + + icon_colour_set: github + icon_glyph_set: devicons + text_colour_set: env + +view: + open_direction: left + sort_by: + - is_folder + - ext + - file_name_lower + - file_name + time_format: "%Y-%m-%d %H:%M" + width: 40 + window_options: + cursorline: true + foldenable: false + number: false + relativenumber: false + signcolumn: "no" + winfixwidth: true + wrap: false +xdg: false diff --git a/config/neovim/store/lazy-plugins/chadtree/docker-compose.yml b/config/neovim/store/lazy-plugins/chadtree/docker-compose.yml new file mode 100644 index 00000000..ae67fb03 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3.7" + +services: + chadtree: + restart: always + build: . + environment: + - TZ + diff --git a/config/neovim/store/lazy-plugins/chadtree/docker/root/.config/nvim/init.vim b/config/neovim/store/lazy-plugins/chadtree/docker/root/.config/nvim/init.vim new file mode 100644 index 00000000..066355e7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/docker/root/.config/nvim/init.vim @@ -0,0 +1,17 @@ +nnoremap Q +nnoremap QQ quitall! +vnoremap Q +vnoremap QQ quitall! + +filetype on +set nomodeline +set secure +set termguicolors +set shortmess+=I + +let g:python3_host_prog = '/usr/bin/python3' +let g:chadtree_settings = {'profiling': v:true, 'xdg': v:true} +let mapleader=' ' +nnoremap v CHADopen + +packloadall diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/ARCHITECTURE.md b/config/neovim/store/lazy-plugins/chadtree/docs/ARCHITECTURE.md new file mode 100644 index 00000000..39712218 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/docs/ARCHITECTURE.md @@ -0,0 +1,41 @@ +# Architecture + +## Asynchronous Event Loop + +CHADTree uses it's own event loop aside from the `asyncio` one defined by the [`pynvim` client](https://github.com/neovim/pynvim). + +In fact, `pynvim` doesn't even run in the main thread. + +All RPC notifications from the `nvim` server are sent to a global message queue, which is then processed in order of arrival after initialization code. + +No further messages can be processed until the previous ones have. + +`nvim` never blocks on the notifications. The CHADTree client has no blocking API. + +## Parallelism + +CHADTree uses a traditional threadpool for parallelizable operations, this includes querying for `git` status and file system walking, as well as other minor ones such as `mv` or `cp`. + +The fs walk is done using a native parallel BFS strategy with a chunking step to avoid flooding the thread pool. This is not optimal since a [Fork Join](https://en.wikipedia.org/wiki/Fork%E2%80%93join_model) model should be more efficient. + +However, as benchmarked, the performance bottleneck is in fact not the filesystem, but text & decorations rendering. + +## Virtual Rendering + +It turns out, if you have thousands lines of text with decorations such as colour or virtual text, `nvim` struggles to update buffers, even if you batch the render in a single call. + +The answer is to have a virtual render target, and to compute the minimal necessary render instructions. + +Previously I had written [Noact](https://github.com/ms-jpq/noact), a 70 line React like virtual dom engine. CHADTree works similarly, except with a more sophisticated diff algorithm, since the native approach does not cope with flat lists. (A flat list is a degenerate tree) + +Instead of Virtual DOM nodes, a hash is used for each desired line of the render target. + +## Memorylessness + +CHADTree is designed with [Memorylessness](https://en.wikipedia.org/wiki/Memorylessness) in mind. For the most part the state transitions in CHADTree follow the Markov Property in that each successive state is independent from history. + +## Pipelining + +Broadly speaking, CHADTree has a two stage pipeline. The first stage processes messages, and generates render and cursor placement instructions for the second stage. + +Ideally the first stage should be referentially transparent, with zero side effects, while the second stage executes all of the side effects. However, this is too tedious, a memoryless approach is taken for the two stages instead. \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/CONFIGURATION.md b/config/neovim/store/lazy-plugins/chadtree/docs/CONFIGURATION.md new file mode 100644 index 00000000..6c584998 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/docs/CONFIGURATION.md @@ -0,0 +1,326 @@ +# Configurations + +All configurations are under the global variable **`chadtree_settings`**. + +VimL: + +```vim +let g:chadtree_settings = { ... } +``` + +Lua: + +```lua +local chadtree_settings = { ... } +vim.api.nvim_set_var("chadtree_settings", chadtree_settings) +``` + +--- + +## Shorthand + +Dictionary keys will be automatically expanded with the `.` notation. This works recursively. + +ie. The following are equivalent + +```json +{ "dog.puppy": 2 } +``` + +```json +{ "dog": { "puppy": 2 } } +``` + +Note in lua, you will need to quote your keys like so: + +```lua +{ ["dog.puppy"] = 2 } +``` + +Note in VimL, to specify `True` and `False`, you need to use the following: + +```vim +v:true +v:false +``` + +--- + +## Validation + +Variables will be validated against a schema. + +ie. + +```vim +let g:chadtree_settings = { 'ignore.dog': 'scratch, stratch' } +``` + +Will give you the following error message: + +![schema error.png](https://github.com/ms-jpq/chadtree/raw/chad/docs/img/schema_error.png) + +**Notice it says `Extra keys: {dog}`** + +--- + +## Specifics + +The default configuration can be found under an [`yaml` file](https://github.com/ms-jpq/chadtree/tree/chad/config/defaults.yml) + +--- + +### chadtree_settings.xdg + +Use `XDG` specifications for storing the CHADTree runtime and session files. + +If set to false, will store everything under repo location. + +**default:** + +```json +false +``` + +--- + +### chadtree_settings.keymap + +See help docs on [keybind](https://github.com/ms-jpq/chadtree/tree/chad/docs/KEYBIND.md) + +--- + +### chadtree_settings.options + +#### `chadtree_settings.options.follow` + +CHADTree will highlight currently open file, and open all its parents. + +**default:** + +```json +true +``` + +#### `chadtree_settings.options.follow_links` + +CHADTree will follow symlinks + +**default:** + +```json +true +``` + +#### `chadtree_settings.options.lang` + +CHADTree will guess your locale from [unix environmental variables](https://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html). + +Set to `c` to disable emojis. + +**default:** + +```json +null +``` + +**note:** + +I only wrote localization for `en`. `zh` will be coming, and maybe `fr` if I can get my girlfriend to help. + +#### `chadtree_settings.options.mimetypes` + +CHADTree will attempt to warn you when you try to open say an image. This is done via the [Internet Assigned Numbers Authority](https://www.iana.org/assignments/media-types/media-types.xhtml)'s mimetype database. + +##### `chadtree_settings.options.mimetypes.warn` + +Show a warning before opening these datatypes + +**default:** + +```json +["audio", "font", "image", "video"] +``` + +##### `chadtree_settings.options.mimetypes.allow_exts` + +Skip warning for these extensions + +**default:** + +```json +[".ts"] +``` + +#### `chadtree_settings.options.page_increment` + +Change how many lines `{` and `}` scroll + +**default:** + +```json +5 +``` + +#### `chadtree_settings.options.min_diagnostics_severity` + +Lower is more severe. + +**default:** + +```json +2 +``` + +#### `chadtree_settings.options.polling_rate` + +CHADTree's background refresh rate + +**default:** + +```json +2.0 +``` + +#### `chadtree_settings.options.session` + +Save & restore currently open folders + +**default:** + +```json +true +``` + +#### `chadtree_settings.options.show_hidden` + +Hide some files and folders by default. By default this can be toggled using the `.` key. + +see `chadtree_settings.ignore` for more details + +**default:** + +```json +false +``` + +#### `chadtree_settings.options.version_control` + +##### `chadtree_settings.options.version_control.enable` + +Enable version control. This can also be toggled. But unlike `show_hidden`, does not have a default keybind. + +**default:** + +```json +true +``` + +--- + +### chadtree_settings.ignore + +CHADTree can ignore showing some files. This is toggable by default using the `.` key. + +#### `chadtree_settings.ignore.name_exact` + +Files whose name match these exactly will be ignored. + +**default:** + +```json +[".DS_Store", ".directory", "thumbs.db", ".git"] +``` + +#### `chadtree_settings.ignore.name_glob` + +Files whose name match these [glob patterns](https://en.wikipedia.org/wiki/Glob_%28programming%29) will be ignored. + +ie. `*.py` will match all python files + +**default:** + +```json +[] +``` + +#### `chadtree_settings.ignore.path_glob` + +Files whose full path match these [glob patterns](https://en.wikipedia.org/wiki/Glob_%28programming%29) will be ignored. + +**default:** + +```json +[] +``` + +--- + +### chadtree_settings.view + +Some options to change CHADTree's appearance + +#### `chadtree_settings.view.open_direction` + +Which way does CHADTree open? + +**legal keys: one of** + +```json +["left", "right"] +``` + +**default:** + +```json +"left" +``` + +#### `chadtree_settings.view.sort_by` + +CHADTree can sort by the following criterion. Reorder them if you want a different sorting order. + +**legal keys: some of** + +```json +["is_folder", "ext", "file_name_lower", "file_name"] +``` + +**default:** + +```json +["is_folder", "ext", "file_name_lower", "file_name"] +``` + +#### `chadtree_settings.view.width` + +How big is CHADTree when initially opened? + +**default:** + +```json +40 +``` + +#### `chadtree_settings.view.window_options` + +Set of window local options to for CHADTree windows + +**default:** + +```json +{ + "cursorline": true, + "number": false, + "relativenumber": false, + "signcolumn": "no", + "winfixwidth": true, + "wrap": false +} +``` + +--- + +### chadtree_settings.theme + +See help docs on [themes](https://github.com/ms-jpq/chadtree/tree/chad/docs/THEME.md) diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/FEATURES.md b/config/neovim/store/lazy-plugins/chadtree/docs/FEATURES.md new file mode 100644 index 00000000..da7a8cb9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/docs/FEATURES.md @@ -0,0 +1,41 @@ +# Features + +## Filtering + +![filtering.gif](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/filtering.gif) + +## Follow Mode + +![follow.gif](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/follow.gif) + +## Git Integrations + +![git.gif](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/git_showcase.gif) + +## Quickfix + +![quickfix.gif](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/quickfix.gif) + +## Sessions + +![session.gif](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/session.gif) + +## Visual Select + +![visual_select.gif](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/visual_select.gif) + +## Github Colours + +![github_colours.png](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/github_colours.png) + +## LS_COLORS + +![ls_colours.png](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/ls_colours.png) + +## LS -l statistics + +![ls_l.png](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/ls_l.png) + +## MimeType warning + +![mimetype.png](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/mimetype.png) diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/KEYBIND.md b/config/neovim/store/lazy-plugins/chadtree/docs/KEYBIND.md new file mode 100644 index 00000000..6633dd46 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/docs/KEYBIND.md @@ -0,0 +1,433 @@ +# Keybinds + +Keybinds can be customized under `chadtree_settings.keymap.` with a set of keys. + +--- + +## Window management + +##### `chadtree_settings.keymap.quit` + +Close CHADTree window, quit if it is the last window. + +**default:** + +```json +["q"] +``` + +##### `chadtree_settings.keymap.bigger` + +Resize CHADTree window bigger. + +**default:** + +```json +["+", "="] +``` + +##### `chadtree_settings.keymap.smaller` + +Resize CHADTree window smaller. + +**default:** + +```json +["-", "_"] +``` + +##### `chadtree_settings.keymap.refresh` + +Refresh CHADTree. + +**default:** + +```json +[""] +``` + +--- + +## Rerooting CHADTree + +##### `chadtree_settings.keymap.change_dir` + +Change vim's working directory. + +**default:** + +```json +["b"] +``` + +##### `chadtree_settings.keymap.change_focus` + +Set CHADTree's root to folder at cursor. Does not change working directory. + +**default:** + +```json +["c"] +``` + +##### `chadtree_settings.keymap.change_focus_up` + +Set CHADTree's root one level up. + +**default:** + +```json +["C"] +``` + +--- + +## Open file / folder + +Any of the keys that open files will double as a open / close toggle on folders. + +##### `chadtree_settings.keymap.primary` + +Open file at cursor. + +**default:** + +```json +[""] +``` + +##### `chadtree_settings.keymap.secondary` + +Open file at cursor, keep cursor in CHADTree's window. + +**default:** + +```json +["", "<2-leftmouse>"] +``` + +##### `chadtree_settings.keymap.tertiary` + +Open file at cursor in a new tab. + +**default:** + +```json +["", ""] +``` + +##### `chadtree_settings.keymap.v_split` + +Open file at cursor in vertical split. + +**default:** + +```json +["w"] +``` + +##### `chadtree_settings.keymap.h_split` + +Open file at cursor in horizontal split. + +**default:** + +```json +["W"] +``` + +##### `chadtree_settings.keymap.open_sys` + +Open file with GUI tools using `open` or `xdg open`. This will open third party tools such as `Finder` or `KDE Dolphin` or `GNOME nautilus`, etc. Depends on platform and user setup. + +**default:** + +```json +["o"] +``` + +##### `chadtree_settings.keymap.collapse` + +Collapse all subdirectories for directory at cursor. + +**default:** + +```json +["", "`"] +``` + +--- + +## Doing things with cursor + +##### `chadtree_settings.keymap.refocus` + +Put cursor at the root of CHADTree + +**default:** + +```json +["~"] +``` + +##### `chadtree_settings.keymap.jump_to_current` + +Position cursor in CHADTree at currently open buffer, if the buffer points to a location visible under CHADTree. + +**default:** + +```json +["J"] +``` + +##### `chadtree_settings.keymap.stat` + +Print `ls --long` stat for file under cursor. + +**default:** + +```json +["K"] +``` + +##### `chadtree_settings.keymap.copy_name` + +Copy paths of files under cursor or visual block. + +**default:** + +```json +["Y"] +``` + +##### `chadtree_settings.keymap.copy_basename` + +Copy names of files under cursor or visual block. + +**default:** + +```json +["y"] +``` + +##### `chadtree_settings.keymap.copy_relname` + +Copy relative paths of files under cursor or visual block. + +**default:** + +```json +[""] +``` + +--- + +## Filtering + +##### `chadtree_settings.keymap.filter` + +Set a glob pattern to narrow down visible files. + +**default:** + +```json +["f"] +``` + +##### `chadtree_settings.keymap.clear_filter` + +Clear filter. + +**default:** + +```json +["F"] +``` + +--- + +## Bookmarks + +##### `chadtree_settings.keymap.bookmark_goto` + +Goto bookmark `A-Z`. + +**default:** + +```json +["m"] +``` + +--- + +## Selecting + +##### `chadtree_settings.keymap.select` + +Select files under cursor or visual block. + +**default:** + +```json +["s"] +``` + +##### `chadtree_settings.keymap.clear_selection` + +Clear selection. + +**default:** + +```json +["S"] +``` + +--- + +## File operations + +##### `chadtree_settings.keymap.new` + +Create new file at location under cursor. Files ending with platform specific path separator will be folders. + +Intermediary folders are created automatically. + +ie. `uwu/owo/` under `unix` will create `uwu/` then `owo/` under it. Both are folders. + +**default:** + +```json +["a"] +``` + +##### `chadtree_settings.keymap.link` + +Create links at location under cursor from selection. + +Links are always relative. + +Intermediary folders are created automatically. + +**default:** + +```json +["A"] +``` + +##### `chadtree_settings.keymap.rename` + +Rename file under cursor. + +**default:** + +```json +["r"] +``` + +##### `chadtree_settings.keymap.toggle_exec` + +Toggle all the `+x` bits of the selected / highlighted files. + +Except for directories, where `-x` will prevent reading. + +**default:** + +```json +["X"] +``` + +##### `chadtree_settings.keymap.copy` + +Copy the selected files to location under cursor. + +**default:** + +```json +["p"] +``` + +##### `chadtree_settings.keymap.cut` + +Move the selected files to location under cursor. + +**default:** + +```json +["x"] +``` + +##### `chadtree_settings.keymap.delete` + +Delete the selected files. Items deleted cannot be recovered. + +**default:** + +```json +["d"] +``` + +##### `chadtree_settings.keymap.trash` + +Trash the selected files using platform specific `trash` command, if they are available. Items trashed may be recovered. + +You need [`brew install trash`](https://formulae.brew.sh/formula/trash) for MacOS and [`pip3 install trash-cli`](https://github.com/andreafrancia/trash-cli) on Linux. + +**default:** + +```json +["t"] +``` + +--- + +## Toggle settings on / off + +##### `chadtree_settings.keymap.toggle_hidden` + +Toggle `show_hidden` on and off. See `chadtree_settings.options.show_hidden` for details. + +**default:** + +```json +["."] +``` + +##### `chadtree_settings.keymap.toggle_follow` + +Toggle `follow` on and off. See `chadtree_settings.options.follow` for details. + +**default:** + +```json +["u"] +``` + +##### `chadtree_settings.keymap.toggle_follow_links` + +Toggle `follow_links` on and off. See `chadtree_settings.options.follow_links` for details. + +**default:** + +```json +["U"] +``` + +##### `chadtree_settings.keymap.toggle_follow_ignore` + +Toggle `follow_ignore` on and off. See `chadtree_settings.options.follow_ignore` for details. + +**default:** + +```json +["T"] +``` + +##### `chadtree_settings.keymap.toggle_version_control` + +Toggle version control integration on and off + +**default:** + +```json +["i"] +``` diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/MIGRATION.md b/config/neovim/store/lazy-plugins/chadtree/docs/MIGRATION.md new file mode 100644 index 00000000..02d2dadf --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/docs/MIGRATION.md @@ -0,0 +1,116 @@ +# Migration + +Hello everybody, I am dropping support for `python_version < 3.8.2` for the main branch. + +Please use the `legacy` branch if you cannot use newer versions of `python`. + +I am very sorry about this, but I am doing this in order to support more awesome features. + +## What you need to do: + +Run the following once after updating your git repo to latest + +```vim +:UpdateRemotePlugins +``` + +Change your extension manager to use the following: + +```vim +Plug 'ms-jpq/chadtree', {'branch': 'chad', 'do': 'python3 -m chadtree deps'} + +``` + +Run `:CHADdeps` the first time before you use `:CHADopen` + +**Check out [`new configuration`](https://github.com/ms-jpq/chadtree/blob/chad/docs/CONFIGURATION.md)**. It is incompatible with the old one, BUT comes with a new parser and vaildator so the migration will be mostly just renaming one or two keys. + +If you make a typo, CHADTree will tell you so! + +## Why? + +Several reasons: + +Python `3.8.2` is the version of `python` on the latest Ubuntu LTS. + +There are some features I wanted to add that strictly cannot be supported below `python 3.8`. For example, I wanted to include a spec validator, but `python 3.7` lacks support for `Literal` in the `typing` module, and therefore could introduce ambiguities in the parser. + +The old CHADTree ran by `nvim`'s default extension implementation, which had major short comings: + +1. Everything ran in the same process. + +2. The user needs to call `:UpdateRemotePlugins` each time I add a new RPC end point, or else they will get a random confusing error. + +3. [`pynvim`](https://github.com/neovim/pynvim) needed to be installed, For most users who aren't familiar with how `pip` and python modules work. This will either mess up their usage of `virtualenv`, or require a global or user level `pip` package just to use CHADTree. + +In order to fix these issues, I will have to make breaking changes anyways, why not now? + +## Solutions + +At the cost of one time migration, which means users need to update their configs and perhaps python version. I will deilver enough features to warrant the upgrade. + +## New Features + +### Independent package management + +CHADTree now will use all local dependencies. Which means `pynvim` can be installed under a subdirectory to `chadtree`. Doing a `rm -rf` on CHADTree will cleanly remove everything it brings in. + +Nothing will pollute the global namespace for python. + +### Isolated Process + +CHADTree now runs inside an isolated process! Not only will it start faster, it will also be isolated from your other python plugins. In case of errors or crashes, they will not affect each other nearly as much! + +### New Vaildating Config Parser + +CHADTree will now validate your typos and misunderstandings on how to configure it! No more silent failures. If you make a typo in the config, it will tell you loud and clear! + +New `property.sub_property` syntax also supported on a recursive level. + +### Faster startup + +CHADTree started up kinda of slowly before. I have made it perceptibly faster through various marginal improvements. + +### Bigly Improved Documentation + +CHADTree now comes with it's own help command! + +Use `:CHADhelp {topic}` to open up built-in help docs in a floating window. + +Use `:CHADhelp {topic} --web` to open up the same help docs in a browser. + +### Parallel File System Operations + +Previously CHADTree was fast because it was async. + +Now CHADTree can be even faster because it does things in parallel. + +See [design document here](https://github.com/ms-jpq/chadtree/tree/chad/docs/ARCHITECTURE.md) for details. + +### Vastly Improved Rendering Speed + +You know how React is famous because it only renders what needs to be changed? + +CHADTree now uses a React-like virtual rendering target. It only re-renders the minimal amount of lines. CHADTree can now handle thousands of visible files and still be reasonably performant! + +_This is only visible when you have 1000+ files visible. The old ways was fast enough for most tasks._ + +### Theming + +Yub, this is yuge. The #1 request was for more themes. They have came! + +Go see `:CHADhelp theme` for [details](https://github.com/ms-jpq/chadtree/tree/chad/docs/THEME.md). + +### Even more Polish + +- Maintain cursor position in many circumstances, ie. move root up / down, filtering for files, renaming, creating files, etc + +- Selection of hidden / invisible files no longer possible. + +- Retain selection when copying or moving files. + +- Now shows `git submodules` + +### Even Higher Quality Code + +Yes the quality of code is a feature. The better the code, the easier it is for me and other people to add in future improvements. diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/README.md b/config/neovim/store/lazy-plugins/chadtree/docs/README.md new file mode 100644 index 00000000..96472235 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/docs/README.md @@ -0,0 +1,43 @@ +# Docs + +Use `:CHADhelp` to open up a list of help pages! + +Help docs are written in `markdown` because a picture is worth a thousand words. + +Use `:CHADhelp -w` or `:CHADhelp --web` to open help pages in a browser window if possible. + +Use `:CHADhelp {topic}` or `:CHADhelp {topic} --web` to visit a particular topic for more information + +- [:CHADhelp features](https://github.com/ms-jpq/chadtree/tree/chad/docs/FEATURES.md) + +- [:CHADhelp keybind](https://github.com/ms-jpq/chadtree/tree/chad/docs/KEYBIND.md) + +- [:CHADhelp config](https://github.com/ms-jpq/chadtree/tree/chad/docs/CONFIGURATION.md) + +- [:CHADhelp theme](https://github.com/ms-jpq/chadtree/tree/chad/docs/THEME.md) + +- [:CHADhelp migration](https://github.com/ms-jpq/chadtree/tree/chad/docs/MIGRATION.md) + +--- + +## Commands + +### `CHADopen` + +`:CHADopen` will toggle CHADTree open / close + +`:CHADopen ` will open at `` + +`:CHADopen --always-focus` will disable toggle if already opened + +`:CHADopen --nofocus` will open CHADTree without giving the sidebar focus + +`:CHADopen --version-ctl` will open CHADTree at version control top level. + +### `CHADdeps` + +`:CHADdeps` will install all of CHADTree's dependencies locally. + +Dependencies will be privately installed inside CHADTree's git root under `.vars/runtime`. + +Running `rm -rf` on `chadtree/` will cleanly remove everything CHADTree installs to your local system. diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/THEME.md b/config/neovim/store/lazy-plugins/chadtree/docs/THEME.md new file mode 100644 index 00000000..e9f42b0c --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/docs/THEME.md @@ -0,0 +1,154 @@ +# Theme + +CHADTree does not define it's own theme, outside of some minimal defaults. + +All themes are imported from other open source projects. + +You can customize themes using the `chadtree_settings.theme` settings. + +--- + +### `chadtree_settings.theme.highlights` + +Vim comes with some built-in highlight groups, these are used to colour things which I cannot find good imports for. + +see `:help highlight-groups` + +#### `chadtree_settings.theme.highlights.ignored` + +These are used for files that are ignored by user supplied pattern in `chadtree_settings.ignore` and by version control. + +**default:** + +```json +"Comment" +``` + +#### `chadtree_settings.theme.highlights.bookmarks` + +These are used to show bookmarks. + +**default:** + +```json +"Title" +``` + +#### `chadtree_settings.theme.highlights.quickfix` + +These are used to notify the number of times a file / folder appears in the `quickfix` list. + +**default:** + +```json +"Label" +``` + +#### `chadtree_settings.theme.highlights.version_control` + +These are used to put a version control status beside each file. + +**default:** + +```json +"Comment" +``` + +--- + +### `chadtree_settings.theme.icon_glyph_set` + +To use **devicons**, you will need [supported fonts](https://github.com/ryanoasis/nerd-fonts#font-installation) + +**devicons:** + +Imported from [vim-devicons](https://github.com/ryanoasis/vim-devicons) + +![devicons.png](https://github.com/ms-jpq/chadtree/raw/chad/docs/img/icons_devicons.png) + +**emoji:** + +Imported from [vim-emoji-icon-theme](https://github.com/adelarsq/vim-emoji-icon-theme) + +![emojicons.png](https://github.com/ms-jpq/chadtree/raw/chad/docs/img/icons_emoji.png) + +**ascii:** + +![asciicons.png](https://github.com/ms-jpq/chadtree/raw/chad/docs/img/icons_ascii.png) + +**ascii_hollow:** + +![ascii_hollow_icons.png](https://github.com/ms-jpq/chadtree/raw/chad/docs/img/icons_ascii_hollow.png) + + +**default:** + +```json +"devicons" +``` + +--- + +### `chadtree_settings.theme.text_colour_set` + +On `unix`, the command `ls` can produce coloured results based on the `LS_COLORS` environmental variable. + +CHADTree can pretend it's `ls` by setting `chadtree_settings.theme.text_colour_set` to `env`. + +If you are not happy with that, you can choose one of the many others: + +- [dircolors-solarized](https://github.com/seebi/dircolors-solarized) + +- [nord-dircolors](https://github.com/arcticicestudio/nord-dircolors) + +- [trapd00r](https://github.com/trapd00r/LS_COLORS) + +- [vim-nerdtree-syntax-highlight](https://github.com/tiagofumo/vim-nerdtree-syntax-highlight) + +**legal keys: one of** + +```json +[ + "env", + "solarized_dark_256", + "solarized_dark", + "solarized_light", + "solarized_universal", + "nord", + "trapdoor", + "nerdtree_syntax_light", + "nerdtree_syntax_dark" +] +``` + +**default:** + +```json +"env" +``` + +--- + +### `chadtree_settings.theme.icon_colour_set` + +Right now you all the file icons are coloured according to [Github colours](https://github.com/github/linguist). + +You may also disable colouring if you wish. + +**github:** + +![github_colours.png](https://raw.githubusercontent.com/ms-jpq/chadtree/chad/docs/img/github_colours.png) + +**legal keys: one of** + +```json +["github", "none"] +``` + +**default:** + +```json +"github" +``` + +--- diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/bookmarks.png b/config/neovim/store/lazy-plugins/chadtree/docs/img/bookmarks.png new file mode 100644 index 00000000..8c126f0d Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/bookmarks.png differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/filtering.gif b/config/neovim/store/lazy-plugins/chadtree/docs/img/filtering.gif new file mode 100644 index 00000000..c6dbf0af Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/filtering.gif differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/follow.gif b/config/neovim/store/lazy-plugins/chadtree/docs/img/follow.gif new file mode 100644 index 00000000..b7c46fd7 Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/follow.gif differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/git_showcase.gif b/config/neovim/store/lazy-plugins/chadtree/docs/img/git_showcase.gif new file mode 100644 index 00000000..dd88c987 Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/git_showcase.gif differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/github_colours.png b/config/neovim/store/lazy-plugins/chadtree/docs/img/github_colours.png new file mode 100644 index 00000000..6cb955c0 Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/github_colours.png differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_ascii.png b/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_ascii.png new file mode 100644 index 00000000..3d4ecc91 Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_ascii.png differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_ascii_hollow.png b/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_ascii_hollow.png new file mode 100644 index 00000000..434768f7 Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_ascii_hollow.png differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_devicons.png b/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_devicons.png new file mode 100644 index 00000000..67a20905 Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_devicons.png differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_emoji.png b/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_emoji.png new file mode 100644 index 00000000..bb18846d Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/icons_emoji.png differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/ls_colours.png b/config/neovim/store/lazy-plugins/chadtree/docs/img/ls_colours.png new file mode 100644 index 00000000..c363118f Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/ls_colours.png differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/ls_l.png b/config/neovim/store/lazy-plugins/chadtree/docs/img/ls_l.png new file mode 100644 index 00000000..4ca323b3 Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/ls_l.png differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/mimetype.png b/config/neovim/store/lazy-plugins/chadtree/docs/img/mimetype.png new file mode 100644 index 00000000..14d1b7a0 Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/mimetype.png differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/quickfix.gif b/config/neovim/store/lazy-plugins/chadtree/docs/img/quickfix.gif new file mode 100644 index 00000000..f13b9df8 Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/quickfix.gif differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/schema_error.png b/config/neovim/store/lazy-plugins/chadtree/docs/img/schema_error.png new file mode 100644 index 00000000..eb424039 Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/schema_error.png differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/session.gif b/config/neovim/store/lazy-plugins/chadtree/docs/img/session.gif new file mode 100644 index 00000000..25e46daa Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/session.gif differ diff --git a/config/neovim/store/lazy-plugins/chadtree/docs/img/visual_select.gif b/config/neovim/store/lazy-plugins/chadtree/docs/img/visual_select.gif new file mode 100644 index 00000000..62684e2d Binary files /dev/null and b/config/neovim/store/lazy-plugins/chadtree/docs/img/visual_select.gif differ diff --git a/config/neovim/store/lazy-plugins/chadtree/locale/c.yml b/config/neovim/store/lazy-plugins/chadtree/locale/c.yml new file mode 100644 index 00000000..20713ce3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/locale/c.yml @@ -0,0 +1,97 @@ +--- +"already_exists": |- + !! Exists: ${name} + +"paths already exist": |- + !! -- ${operation}: path(s) already exist! :: + ${paths} + +"new cwd": |- + $$PWD=${cwd} + +"ask_trash": |- + Trash? + ${display_paths} + +"ask_yesno": |- + &Yes + &No + +"cannot find version ctl root": |- + !! Cannot find version control top level + +"operation not permitted on root": |- + Operation not permitted on root or parent(s) of root + +"render time warning": |- + !! Warning: Render time is slow @${duration}s, consider closing some folders + +"copy": |- + Copy + +"copy_paths": |- + Copied ${copied_paths} + +"cut": |- + Cut + +"confirm op": |- + ${operation} ? + ${paths} + +"dead_link": |- + !! cannot open dead link: ${name} + +"filter_click": |- + !! cannot click on folders while filtering + +"follow_mode_indi": |- + !! follow mode: ${follow} + +"follow_links_indi": |- + !! follow links: ${follow} + +"follow_ignore_indi": |- + !! follow ignore: ${follow} + +"hourglass": |- + Wait... + +"link": |- + Link ${src} ->: + +"mime_warn": |- + ${name} have possible mimetype ${mime}, continue? + +"new_filter": |- + New Filter: + +"new_search": |- + New Search: + +"nothing_select": |- + !! -- nothing selected! + +"ok_sym": |- + Ok + +"path_exists_err": |- + !! Path exist - Rename: + +"pencil": |- + Input: + +"clear_bookmarks": |- + ${idx}. Clear! + +"no_bookmarks": |- + !! Error - no bookmarks found + +"sys_open_err": |- + !! Error -- cannot find system opener + +"sys_trash_err": |- + !! Error -- cannot find trash program + +"version_control_indi": |- + 🐶 enable version control: ${enable_vc} diff --git a/config/neovim/store/lazy-plugins/chadtree/locale/en.yml b/config/neovim/store/lazy-plugins/chadtree/locale/en.yml new file mode 100644 index 00000000..e45eb637 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/locale/en.yml @@ -0,0 +1,97 @@ +--- +"already_exists": |- + ⚠️ Exists: ${name} + +"paths already exist": |- + ⚠️ -- ${operation} : path(s) already exist! :: + ${paths} + +"new cwd": |- + 🏠 =${cwd} + +"ask_trash": |- + 🗑 ? + ${display_paths} + +"ask_yesno": |- + &Yes + &No + +"cannot find version ctl root": |- + ⚠️ Cannot find version control top level + +"operation not permitted on root": |- + ⚠️ Operation not permitted on root or parent(s) of root + +"render time warning": |- + ⚠️ Warning: Render time is slow @${duration}s, consider closing some folders + +"copy": |- + 🖇 + +"copy_paths": |- + 📎 ${copied_paths} + +"cut": |- + ✂️ + +"confirm op": |- + ${operation} ? + ${paths} + +"dead_link": |- + ⚠️ cannot open dead link: ${name} + +"filter_click": |- + ⚠️ cannot click on folders while filtering + +"follow_mode_indi": |- + 🐶 follow mode: ${follow} + +"follow_links_indi": |- + 🔗 follow mode: ${follow} + +"follow_ignore_indi": |- + 🌫️ follow mode: ${follow} + +"hourglass": |- + ⏳...⌛️ + +"link": |- + 🔗 ${src} 👉 : + +"mime_warn": |- + ${name} have possible mimetype ${mime}, continue? + +"new_filter": |- + New Filter: + +"new_search": |- + 🔍: + +"nothing_select": |- + ⚠️ -- nothing selected! + +"ok_sym": |- + ✅ + +"path_exists_err": |- + ⚠️ Path exist!!! Rename: + +"pencil": |- + ✏️ : + +"clear_bookmarks": |- + ${idx}. 🔖 Clear! + +"no_bookmarks": |- + ⚠️ Error - no bookmarks found + +"sys_open_err": |- + ⚠️ Error -- cannot find system opener + +"sys_trash_err": |- + ⚠️ Error -- cannot find trash program + +"version_control_indi": |- + 🐶 enable version control: ${enable_vc} diff --git a/config/neovim/store/lazy-plugins/chadtree/locale/zh.yml b/config/neovim/store/lazy-plugins/chadtree/locale/zh.yml new file mode 100644 index 00000000..6c5bc50b --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/locale/zh.yml @@ -0,0 +1,97 @@ +--- +"already_exists": |- + ⚠️ 已存在: ${name} + +"paths already exist": |- + ⚠️ -- ${operation} : 路径已存在! :: + ${paths} + +"new cwd": |- + 🏠 =${cwd} + +"ask_trash": |- + 🗑 ? + ${display_paths} + +"ask_yesno": |- + &Y (确定) + &N (取消) + +"cannot find version ctl root": |- + ⚠️ 文件系统顶层找不到版本控制 + +"operation not permitted on root": |- + ⚠️ 此操作不可文件系统顶层(以上) + +"render time warning": |- + ⚠️ 警告: 渲染时间有点慢 @${duration}s, 考虑关闭一些文件夹 + +"copy": |- + 🖇 + +"copy_paths": |- + 📎 ${copied_paths} + +"cut": |- + ✂️ + +"confirm op": |- + ${operation} ? + ${paths} + +"dead_link": |- + ⚠️ 打不开死链接: ${name} + +"filter_click": |- + ⚠️ 不可在过滤时点击文件夹 + +"follow_mode_indi": |- + 🐶 跟随模式: ${follow} + +"follow_links_indi": |- + 🔗 跟随模式: ${follow} + +"follow_ignore_indi": |- + 🌫️ 跟随模式: ${follow} + +"hourglass": |- + ⏳...⌛️ + +"link": |- + 🔗 ${src} 👉 : + +"mime_warn": |- + ${name} 文件猜到 mimetype ${mime}, 继续? + +"new_filter": |- + 新过滤: + +"new_search": |- + 🔍: + +"nothing_select": |- + ⚠️ -- 选择是空的! + +"ok_sym": |- + ✅ + +"path_exists_err": |- + ⚠️ 路径已存在!!! 重新命名: + +"pencil": |- + ✏️ : + +"clear_bookmarks": |- + ${idx}. 🔖 清除 + +"no_bookmarks": |- + ⚠️ 错误 -- 找不到书签 + +"sys_open_err": |- + ⚠️ 错误 -- 找不到系统开启程序 + +"sys_trash_err": |- + ⚠️ 错误 -- 找不到垃圾(trash)程序 + +"version_control_indi": |- + 🐶 版本控制: ${enable_vc} diff --git a/config/neovim/store/lazy-plugins/chadtree/lua/chadtree.lua b/config/neovim/store/lazy-plugins/chadtree/lua/chadtree.lua new file mode 100644 index 00000000..82562bc4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/lua/chadtree.lua @@ -0,0 +1,212 @@ +CHAD = CHAD or {} +chad = chad or {} + +local linesep = "\n" +local POLLING_RATE = 10 +local is_win = vim.api.nvim_call_function("has", {"win32"}) == 1 + +local cwd = (function() + local source = debug.getinfo(2, "S").source + local file = string.match(source, "^@(.*)") + return vim.api.nvim_call_function("fnamemodify", {file, ":p:h:h"}) +end)() + +local function defer(timeout, callback) + local timer = vim.loop.new_timer() + timer:start( + timeout, + 0, + function() + timer:stop() + timer:close() + vim.schedule(callback) + end + ) + return timer +end + +local settings = function() + local go, _settings = pcall(vim.api.nvim_get_var, "chadtree_settings") + local settings = go and _settings or {} + return settings +end + +local job_id = nil +local err_exit = false + +chad.on_exit = function(args) + local code = unpack(args) + if not (code == 0 or code == 143) then + err_exit = true + vim.api.nvim_err_writeln("CHADTree EXITED - " .. code) + else + err_exit = false + end + job_id = nil +end + +chad.on_stdout = function(args) + local msg = unpack(args) + vim.api.nvim_out_write(table.concat(msg, linesep)) +end + +chad.on_stderr = function(args) + local msg = unpack(args) + if vim.api.nvim_call_function("has", {"nvim-0.5"}) == 1 then + vim.api.nvim_echo({{table.concat(msg, linesep), "ErrorMsg"}}, true, {}) + else + vim.api.nvim_err_write(table.concat(msg, linesep)) + end +end + +local go, _py3 = pcall(vim.api.nvim_get_var, "python3_host_prog") +local py3 = go and _py3 or (is_win and "python.exe" or "python3") +local xdg_dir = vim.api.nvim_call_function("stdpath", {"data"}) + +local main = function(is_xdg) + local v_py = + cwd .. + (is_win and [[/.vars/runtime/Scripts/python.exe]] or + "/.vars/runtime/bin/python3") + + if is_win then + local v_py_xdg = xdg_dir .. "/chadrt/Scripts/python" + local v_py = is_xdg and v_py_xdg or v_py + if vim.api.nvim_call_function("filereadable", {v_py}) == 1 then + return {v_py} + else + -- local win_proxy = cwd .. [[/win.cmd]] + return {py3} + end + else + local v_py_xdg = xdg_dir .. "/chadrt/bin/python3" + local v_py = is_xdg and v_py_xdg or v_py + if vim.api.nvim_call_function("filereadable", {v_py}) == 1 then + return {v_py} + else + return {py3} + end + end +end + +local start = function(deps, ...) + local is_xdg = settings().xdg + local args = + vim.tbl_flatten { + deps and py3 or main(is_xdg), + {"-s", "-u", "-m", "chadtree"}, + {...}, + (is_xdg and {"--xdg", xdg_dir} or {}) + } + local params = { + cwd = cwd, + env = {PYTHONSAFEPATH = "1", PYTHONPATH = cwd}, + on_exit = "CHADon_exit", + on_stdout = (function() + if deps then + return nil + else + return "CHADon_stdout" + end + end)(), + on_stderr = (function() + if deps then + return nil + else + return "CHADon_stderr" + end + end)() + } + if deps then + vim.api.nvim_command [[new]] + vim.api.nvim_call_function("termopen", {args, params}) + else + job_id = vim.api.nvim_call_function("jobstart", {args, params}) + return job_id + end +end + +chad.Deps = function() + start(true, "deps") +end + +vim.api.nvim_command [[command! -nargs=0 CHADdeps lua chad.Deps()]] + +local set_chad_call = function(cmd) + local t1 = 0 + chad[cmd] = function(...) + local args = {...} + if t1 == 0 then + t1 = vim.loop.now() + end + + if not job_id then + local srv = is_win and {"localhost:0"} or {} + local server = vim.api.nvim_call_function("serverstart", srv) + job_id = + start( + false, + "run", + "--ppid", + vim.api.nvim_call_function("getpid", {}), + "--socket", + server + ) + end + + if not err_exit and CHAD[cmd] then + CHAD[cmd](args) + local t2 = vim.loop.now() + if settings().profiling and t1 >= 0 then + print("Init " .. (t2 - t1) .. "ms") + end + t1 = -1 + else + defer( + POLLING_RATE, + function() + if err_exit then + return + else + chad[cmd](unpack(args)) + end + end + ) + end + end +end + +set_chad_call("Noop") + +set_chad_call("Open") +vim.api.nvim_command [[command! -nargs=* CHADopen lua chad.Open()]] + +set_chad_call("Help") +vim.api.nvim_command [[command! -nargs=* CHADhelp lua chad.Help()]] + +set_chad_call("Restore") +vim.api.nvim_command [[command! -nargs=0 CHADrestore lua chad.Restore()]] + +chad.lsp_ensure_capabilities = function(cfg) + local spec1 = { + capabilities = vim.lsp.protocol.make_client_capabilities() + } + local spec2 = { + capabilities = { + workspace = { + fileOperations = { + didCreate = true, + didRename = true, + didDelete = true + } + } + } + } + local maps = cfg.capabilities and {spec2} or {spec1, spec2} + local new = + vim.tbl_deep_extend("force", cfg or vim.empty_dict(), unpack(maps)) + return new +end + +chad.Noop() +return chad diff --git a/config/neovim/store/lazy-plugins/chadtree/mypy.ini b/config/neovim/store/lazy-plugins/chadtree/mypy.ini new file mode 100644 index 00000000..db81273c --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/mypy.ini @@ -0,0 +1,26 @@ +[mypy] +cache_dir = .vars/mypy +check_untyped_defs = true +disallow_any_generics = false +disallow_any_unimported = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +error_summary = true +extra_checks = true +implicit_reexport = false +no_implicit_optional = true +pretty = true +show_column_numbers = true +show_error_codes = true +show_error_context = true +strict = true +strict_equality = true +warn_incomplete_stub = true +warn_redundant_casts = true +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true +warn_unused_ignores = true diff --git a/config/neovim/store/lazy-plugins/chadtree/plugin/chadtree.vim b/config/neovim/store/lazy-plugins/chadtree/plugin/chadtree.vim new file mode 100644 index 00000000..3cd3300b --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/plugin/chadtree.vim @@ -0,0 +1,20 @@ +autocmd VimEnter * silent! autocmd! FileExplorer +silent! autocmd! FileExplorer + +augroup CHADTree + autocmd! + autocmd FileType CHADTree autocmd BufEnter,WinEnter stopinsert +augroup end + +function CHADon_exit(_, code, __) + call luaeval('chad.on_exit(...)', [a:code]) +endfunction +function CHADon_stdout(_, msg, __) + call luaeval('chad.on_stdout(...)', [a:msg]) +endfunction +function CHADon_stderr(_, msg, __) + call luaeval('chad.on_stderr(...)', [a:msg]) +endfunction + + +call luaeval('require("chadtree") and 0') diff --git a/config/neovim/store/lazy-plugins/chadtree/pyproject.toml b/config/neovim/store/lazy-plugins/chadtree/pyproject.toml new file mode 100644 index 00000000..57bd13e1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "chadtree" +requires-python = ">=3.8.0" +version = "0" + +[project.optional-dependencies] +dev = ["mypy", "types-PyYAML", "black", "isort"] diff --git a/config/neovim/store/lazy-plugins/chadtree/requirements.txt b/config/neovim/store/lazy-plugins/chadtree/requirements.txt new file mode 100644 index 00000000..9537cd0e --- /dev/null +++ b/config/neovim/store/lazy-plugins/chadtree/requirements.txt @@ -0,0 +1,3 @@ +std2@https://github.com/ms-jpq/std2/archive/d8ac12686cdf0d640aed7cee6a981b1835d1724e.tar.gz +pynvim_pp@https://github.com/ms-jpq/pynvim_pp/archive/34e3a027c595981886d7efd1c91071f3eaa4715d.tar.gz +PyYAML diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/LICENSE b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/LICENSE new file mode 100644 index 00000000..14c4587e --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022-2023 Chinmay Dalal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/README.md b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/README.md new file mode 100644 index 00000000..dd0cee97 --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/README.md @@ -0,0 +1,202 @@ +![clangd](https://user-images.githubusercontent.com/36493671/152692205-837ec826-54d0-4257-9894-cc1a7ac8a114.svg) + +Requires Neovim 0.7+ + +## Installation +Install this plugin using any plugin/package manager or see [`:h packages`](https://neovim.io/doc/user/repeat.html#packages) + +## Configuration: +Set up clangd via lspconfig/vim.lsp.start, as usual. +You don't need to call `require("clangd_extensions").setup` if you like the defaults: +```lua +require("clangd_extensions").setup({ + inlay_hints = { + inline = vim.fn.has("nvim-0.10") == 1, + -- Options other than `highlight' and `priority' only work + -- if `inline' is disabled + -- Only show inlay hints for the current line + only_current_line = false, + -- Event which triggers a refresh of the inlay hints. + -- You can make this { "CursorMoved" } or { "CursorMoved,CursorMovedI" } but + -- note that this may cause higher CPU usage. + -- This option is only respected when only_current_line is true. + only_current_line_autocmd = { "CursorHold" }, + -- whether to show parameter hints with the inlay hints or not + show_parameter_hints = true, + -- prefix for parameter hints + parameter_hints_prefix = "<- ", + -- prefix for all the other hints (type, chaining) + other_hints_prefix = "=> ", + -- whether to align to the length of the longest line in the file + max_len_align = false, + -- padding from the left if max_len_align is true + max_len_align_padding = 1, + -- whether to align to the extreme right or not + right_align = false, + -- padding from the right if right_align is true + right_align_padding = 7, + -- The color of the hints + highlight = "Comment", + -- The highlight group priority for extmark + priority = 100, + }, + ast = { + -- These are unicode, should be available in any font + role_icons = { + type = "🄣", + declaration = "🄓", + expression = "🄔", + statement = ";", + specifier = "🄢", + ["template argument"] = "🆃", + }, + kind_icons = { + Compound = "🄲", + Recovery = "🅁", + TranslationUnit = "🅄", + PackExpansion = "🄿", + TemplateTypeParm = "🅃", + TemplateTemplateParm = "🅃", + TemplateParamObject = "🅃", + }, + --[[ These require codicons (https://github.com/microsoft/vscode-codicons) + role_icons = { + type = "", + declaration = "", + expression = "", + specifier = "", + statement = "", + ["template argument"] = "", + }, + + kind_icons = { + Compound = "", + Recovery = "", + TranslationUnit = "", + PackExpansion = "", + TemplateTypeParm = "", + TemplateTemplateParm = "", + TemplateParamObject = "", + }, ]] + + highlights = { + detail = "Comment", + }, + }, + memory_usage = { + border = "none", + }, + symbol_info = { + border = "none", + }, +}) +``` +## Features: +### [Switch between source/header](https://clangd.llvm.org/extensions#switch-between-sourceheader) +### Usage +`:ClangdSwitchSourceHeader` +### [Inlay hints](https://clangd.llvm.org/extensions#inlay-hints) +![image](https://user-images.githubusercontent.com/36493671/152699601-61ad1640-96bf-4082-b553-75d4085c3496.png) +#### Usage +Add this to your nvim-lspconfig / `vim.lsp.start()`'s `on_attach`: +```lua +require("clangd_extensions.inlay_hints").setup_autocmd() +require("clangd_extensions.inlay_hints").set_inlay_hints() +``` + +You can also enable, disable or toggle the hints with `ClangdSetInlayHints`, `ClangdDisableInlayHints` and `ClangdToggleInlayHints`. +Toggling returns the current state of the hints, this is useful if you want to hook a callback when toggling inlay hints: +```lua +if require("clangd_extensions.inlay_hints").toggle_inlay_hints() then + -- Inlay hints are enabled +else + -- Inlay hints are disabled +end +``` +For example if you have autocommands related to Clangd inlay hints you might want to disable/enable them when toggling inlay hints: +```lua +on_attach = function(_, buf) + local group = vim.api.nvim_create_augroup("clangd_no_inlay_hints_in_insert", { clear = true }) + + vim.keymap.set("n", "lh", function() + if require("clangd_extensions.inlay_hints").toggle_inlay_hints() then + vim.api.nvim_create_autocmd("InsertEnter", { group = group, buffer = buf, + callback = require("clangd_extensions.inlay_hints").disable_inlay_hints + }) + vim.api.nvim_create_autocmd({ "TextChanged", "InsertLeave" }, { group = group, buffer = buf, + callback = require("clangd_extensions.inlay_hints").set_inlay_hints + }) + else + vim.api.nvim_clear_autocmds({ group = group, buffer = buf }) + end + end, { buffer = buf, desc = "[l]sp [h]ints toggle" }) +end, +} +``` + +### [View AST](https://clangd.llvm.org/extensions#ast) +![image](https://user-images.githubusercontent.com/36493671/255611133-35f397d3-02f8-4d14-b70a-126be6c098fa.gif) +You can fold nodes using `zc` and friends - the AST window has `shiftwidth=2` and `foldmethod=indent`. + +#### Usage +`:ClangdAST` to view the ast with the current line as the range, `:'<,'>ClangdAST` with a visual selection to view the ast with the selected lines as range. +See how ranges are handled at https://clangd.llvm.org/extensions#ast +### [Completion scores](https://clangd.llvm.org/extensions#code-completion-scores) +Usage: For nvim-cmp +```lua +local cmp = require "cmp" +cmp.setup { + -- ... rest of your cmp setup ... + + sorting = { + comparators = { + cmp.config.compare.offset, + cmp.config.compare.exact, + cmp.config.compare.recently_used, + require("clangd_extensions.cmp_scores"), + cmp.config.compare.kind, + cmp.config.compare.sort_text, + cmp.config.compare.length, + cmp.config.compare.order, + }, + }, +} +``` +### [Symbol info](https://clangd.llvm.org/extensions#symbol-info-request) +![image](https://user-images.githubusercontent.com/36493671/152699367-dc928adf-d3ed-4e8e-a9d0-ca573f01c008.png) +#### Usage +`:ClangdSymbolInfo` with the cursor at the desired symbol. +### [Type hierarchy](https://clangd.llvm.org/extensions#type-hierarchy) + +![image](https://user-images.githubusercontent.com/36493671/255609950-80bebd4a-9800-432d-9f0c-5e5519eeba6f.gif) +#### Usage +`:ClangdTypeHierarchy` with the cursor over the desired type or a symbol of that type. +`gd` with the cursor over a type in a window to go to its definition. +### [Memory usage](https://clangd.llvm.org/extensions#memory-usage) +You can fold items using `zc` and friends - the memory usage window has `shiftwidth=2` and `foldmethod=indent`. +![image](https://user-images.githubusercontent.com/36493671/152699322-9e537b1a-8253-45c1-ada3-752effeac39b.png) +#### Usage +`:ClangdMemoryUsage`. Preamble can be large so it is collapsed by default, to expand it use `:ClangdMemoryUsage expand_preamble` + +## Implementation status of [extensions](https://clangd.llvm.org/extensions) + ☑️ Memory usage + + ☑️ AST + + ☑️ Symbol info request + + ☑️ Type hierarchy + + ☑️ Inlay hints + + ☑️ Switch between source/header + + ☑️ File status (see lsp-status.nvim) + + ☑️ Compilation commands (can be specified in `vim.lsp.start()`/lspconfig `init_options` and `settings`) + + ☑️ Code completion scores + + ⬜ Force diagnostics generation (not sure) +## Credits +[simrat39](https://github.com/simrat39) - the code for inlay hints was taken from [rust-tools.nvim](https://github.com/simrat39/rust-tools.nvim) with very minor changes. diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/ast.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/ast.lua new file mode 100644 index 00000000..3083ec20 --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/ast.lua @@ -0,0 +1,183 @@ +local fmt = string.format +local api = vim.api +local conf = require("clangd_extensions.config").options.ast + +local M = {} +--- node_pos[source_buf][ast_buf][linenum] = { start = start, end = end } +--- position of node in `source_buf` corresponding to line no. `linenum` in `ast_buf` +M.node_pos = {} +--- detail_pos[ast_buf][linenum] = { start = start, end = end } +--- position of `detail` in line no. `linenum` of `ast_buf` +M.detail_pos = {} +M.nsid = vim.api.nvim_create_namespace("clangd_extensions") + +local function setup_hl_autocmd(source_buf, ast_buf) + local group = api.nvim_create_augroup("ClangdExtensions", {}) + api.nvim_create_autocmd("CursorMoved", { + group = group, + buffer = ast_buf, + callback = function() M.update_highlight(source_buf, ast_buf) end, + }) + api.nvim_create_autocmd("BufLeave", { + group = group, + buffer = ast_buf, + callback = function() M.clear_highlight(source_buf) end, + }) +end + +local function icon_prefix(role, kind) + if conf.kind_icons[kind] then + return conf.kind_icons[kind] .. " " + elseif conf.role_icons[role] then + return conf.role_icons[role] .. " " + else + return " " + end +end + +local function describe(role, kind, detail) + local str = "" + local icon = icon_prefix(role, kind) + local detailpos = nil + str = str .. kind + if + not ( + role == "expression" + or role == "statement" + or role == "declaration" + or role == "template name" + ) + then + str = str .. " " .. role + end + if detail then + detailpos = { + start = string.len(str) + vim.fn.strlen(icon) + 1, + ["end"] = string.len(str) + vim.fn.strlen(icon) + string.len( + detail + ) + 1, + } + str = str .. " " .. detail + end + return (icon .. str), detailpos +end + +local function walk_tree(node, visited, result, padding, hl_bufs) + visited[node] = true + local str, detpos = describe(node.role, node.kind, node.detail) + table.insert(result, padding .. str) + + if node.detail and detpos then + M.detail_pos[hl_bufs.ast_buf][#result] = { + start = string.len(padding) + detpos.start, + ["end"] = string.len(padding) + detpos["end"], + } + end + + if node.range then + M.node_pos[hl_bufs.source_buf][hl_bufs.ast_buf][#result] = { + start = { node.range.start.line, node.range.start.character }, + ["end"] = { node.range["end"].line, node.range["end"].character }, + } + end + + if node.children then + for _, child in pairs(node.children) do + if not visited[child] then + walk_tree(child, visited, result, padding .. " ", hl_bufs) + end + end + end + + return result +end + +local function highlight_detail(ast_buf) + for linenum, range in pairs(M.detail_pos[ast_buf]) do + vim.highlight.range( + ast_buf, + M.nsid, + conf.highlights.detail, + { linenum - 1, range.start }, + { linenum - 1, range["end"] }, + "v", + false, + 110 + ) + end +end + +local function handler(err, ASTNode) + if err or not ASTNode then + return + else + local source_buf = api.nvim_get_current_buf() + vim.cmd.vsplit(fmt("%s: AST", ASTNode.detail)) + local ast_buf = api.nvim_get_current_buf() + api.nvim_set_option_value("filetype", "ClangdAST", { buf = ast_buf }) + if not M.node_pos[source_buf] then M.node_pos[source_buf] = {} end + M.node_pos[source_buf][ast_buf] = {} + M.detail_pos[ast_buf] = {} + + local lines = walk_tree( + ASTNode, + {}, + {}, + "", + { source_buf = source_buf, ast_buf = ast_buf } + ) + api.nvim_buf_set_lines(ast_buf, 0, -1, true, lines) + vim.bo.buftype = "nofile" + vim.bo.bufhidden = "wipe" + vim.bo.modifiable = false + vim.bo.shiftwidth = 2 + vim.wo.foldmethod = "indent" + api.nvim_set_option_value("number", false, { scope = "local" }) + api.nvim_set_option_value("relativenumber", false, { scope = "local" }) + api.nvim_set_option_value("spell", false, { scope = "local" }) + api.nvim_set_option_value("cursorline", false, { scope = "local" }) + setup_hl_autocmd(source_buf, ast_buf) + highlight_detail(ast_buf) + end +end + +function M.clear_highlight(source_buf) + api.nvim_buf_clear_namespace(source_buf, M.nsid, 0, -1) +end + +function M.update_highlight(source_buf, ast_buf) + M.clear_highlight(source_buf) + if api.nvim_get_current_buf() ~= ast_buf then return end + local curline = vim.fn.getcurpos()[2] + local curline_ranges = M.node_pos[source_buf][ast_buf][curline] + if curline_ranges then + vim.highlight.range( + source_buf, + M.nsid, + "Search", + curline_ranges.start, + curline_ranges["end"], + "v", + false, + 110 + ) + end +end + +function M.display_ast(line1, line2) + vim.lsp.buf_request(0, "textDocument/ast", { + textDocument = { uri = vim.uri_from_bufnr(0) }, + range = { + start = { + line = line1 - 1, + character = 0, + }, + ["end"] = { + line = line2, + character = 0, + }, + }, + }, handler) +end + +return M diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/cmp_scores.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/cmp_scores.lua new file mode 100644 index 00000000..66885c60 --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/cmp_scores.lua @@ -0,0 +1,10 @@ +return function(entry1, entry2) + local diff + if entry1.completion_item.score and entry2.completion_item.score then + diff = (entry2.completion_item.score * entry2.score) + - (entry1.completion_item.score * entry1.score) + else + diff = entry2.score - entry1.score + end + return (diff < 0) +end diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/config.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/config.lua new file mode 100644 index 00000000..3c28c1f9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/config.lua @@ -0,0 +1,57 @@ +local M = {} + +M.options = { + inlay_hints = { + inline = vim.fn.has("nvim-0.10") == 1, + only_current_line = false, + only_current_line_autocmd = { "CursorHold" }, + show_parameter_hints = true, + parameter_hints_prefix = "<- ", + other_hints_prefix = "=> ", + max_len_align = false, + max_len_align_padding = 1, + right_align = false, + right_align_padding = 7, + highlight = "Comment", + priority = 100, + }, + + ast = { + role_icons = { + type = "🄣", + declaration = "🄓", + expression = "🄔", + statement = ";", + specifier = "🄢", + ["template argument"] = "🆃", + }, + + kind_icons = { + Compound = "🄲", + Recovery = "🅁", + TranslationUnit = "🅄", + PackExpansion = "🄿", + TemplateTypeParm = "🅃", + TemplateTemplateParm = "🅃", + TemplateParamObject = "🅃", + }, + + highlights = { + detail = "Comment", + }, + }, + + memory_usage = { + border = "none", + }, + + symbol_info = { + border = "none", + }, +} + +function M.setup(options) + M.options = vim.tbl_deep_extend("force", {}, M.options, options or {}) +end + +return M diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/init.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/init.lua new file mode 100644 index 00000000..b2cc377d --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/init.lua @@ -0,0 +1,5 @@ +return { + setup = function(options) + require("clangd_extensions.config").setup(options) + end, +} diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/inlay_hints.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/inlay_hints.lua new file mode 100644 index 00000000..e0ee08aa --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/inlay_hints.lua @@ -0,0 +1,311 @@ +-- MIT License +-- +-- Copyright (c) 2020 simrat39 +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in all +-- copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. + +local M = {} +local config = require("clangd_extensions.config") +local api = vim.api + +-- Update inlay hints when opening a new buffer and when writing a buffer to a +-- file +-- opts is a string representation of the table of options +function M.setup_autocmd() + local events = { "BufEnter", "BufWinEnter", "TabEnter", "BufWritePost" } + if config.options.inlay_hints.only_current_line then + vim.list_extend( + events, + config.options.inlay_hints.only_current_line_autocmd + ) + end + + local augroup = vim.api.nvim_create_augroup("ClangdInlayHints", {}) + local buffer = api.nvim_get_current_buf() + api.nvim_clear_autocmds({ + buffer = buffer, + group = augroup, + }) + api.nvim_create_autocmd(events, { + buffer = buffer, + group = augroup, + callback = M.set_inlay_hints, + }) +end + +local function get_inline_params() + return { + textDocument = vim.lsp.util.make_text_document_params(), + range = { + start = { + line = 0, + character = 0, + }, + ["end"] = { + line = vim.api.nvim_buf_line_count(0), + character = 0, + }, + }, + } +end + +local namespace = vim.api.nvim_create_namespace("clangd/inlayHints") +-- whether the hints are enabled or not +local enabled = nil + +-- parses the result into a easily parsable format +-- example: +-- { +-- ["12"] = { { +-- kind = "TypeHint", +-- label = "String" +-- } }, +-- ["13"] = { { +-- kind = "TypeHint", +-- label = "usize" +-- } }, +-- ["15"] = { { +-- kind = "ParameterHint", +-- label = "styles" +-- }, { +-- kind = "ParameterHint", +-- label = "len" +-- } }, +-- ["7"] = { { +-- kind = "ChainingHint", +-- label = "Result" +-- }, { +-- kind = "ParameterHint", +-- label = "key" +-- } }, +-- ["8"] = { { +-- kind = "ParameterHint", +-- label = "op" +-- } } +-- } +-- +local function parseHints(result) + local map = {} + local only_current_line = config.options.inlay_hints.only_current_line + + if type(result) ~= "table" then return {} end + for _, value in pairs(result) do + local line = tostring(value.range["end"].line) + local label = value.label + local kind = value.kind + local current_line = vim.api.nvim_win_get_cursor(0)[1] + + local function add_line() + if map[line] ~= nil then + table.insert(map[line], { label = label, kind = kind }) + else + map[line] = { { label = label, kind = kind } } + end + end + + if only_current_line then + if line == tostring(current_line - 1) then add_line() end + else + add_line() + end + end + return map +end + +local function handler(err, result, ctx) + if err then return end + local opts = config.options.inlay_hints + local bufnr = ctx.bufnr + + if vim.api.nvim_get_current_buf() ~= bufnr then return end + + -- clean it up at first + M.disable_inlay_hints() + + local ret = parseHints(result) + local max_len = -1 + + for key, _ in pairs(ret) do + local line = tonumber(key) + local current_line = + vim.api.nvim_buf_get_lines(bufnr, line, line + 1, false)[1] + if current_line then + local current_line_len = string.len(current_line) + max_len = math.max(max_len, current_line_len) + end + end + + for key, value in pairs(ret) do + local virt_text = "" + local line = tonumber(key) + + local current_line = + vim.api.nvim_buf_get_lines(bufnr, line, line + 1, false)[1] + + if current_line then + local current_line_len = string.len(current_line) + + local param_hints = {} + local other_hints = {} + + -- segregate paramter hints and other hints + for _, value_inner in ipairs(value) do + if value_inner.kind == "parameter" then + table.insert(param_hints, value_inner.label:sub(1, -3)) + else + local hint_text = value_inner.label + if hint_text:sub(1, 2) == ": " then + hint_text = hint_text:sub(3) + end + table.insert(other_hints, hint_text) + end + end + + -- show parameter hints inside brackets with commas and a thin arrow + if + not vim.tbl_isempty(param_hints) and opts.show_parameter_hints + then + virt_text = virt_text .. opts.parameter_hints_prefix .. "(" + for i, value_inner_inner in ipairs(param_hints) do + virt_text = virt_text .. value_inner_inner + if i ~= #param_hints then virt_text = virt_text .. ", " end + end + virt_text = virt_text .. ") " + end + + -- show other hints with commas and a thicc arrow + if not vim.tbl_isempty(other_hints) then + virt_text = virt_text .. opts.other_hints_prefix + for i, value_inner_inner in ipairs(other_hints) do + virt_text = virt_text .. value_inner_inner + if i ~= #other_hints then virt_text = virt_text .. ", " end + end + end + + if config.options.inlay_hints.right_align then + virt_text = virt_text + .. string.rep( + " ", + config.options.inlay_hints.right_align_padding + ) + end + + if config.options.inlay_hints.max_len_align then + virt_text = string.rep( + " ", + max_len + - current_line_len + + config.options.inlay_hints.max_len_align_padding + ) .. virt_text + end + + -- set the virtual text + vim.api.nvim_buf_set_extmark(bufnr, namespace, line, 0, { + virt_text_pos = config.options.inlay_hints.right_align + and "right_align" + or "eol", + virt_text = { + { virt_text, config.options.inlay_hints.highlight }, + }, + hl_mode = "combine", + priority = config.options.inlay_hints.priority, + }) + + -- update state + enabled = true + end + end +end + +local function inline_handler(err, result, ctx) + if err then return end + local bufnr = ctx.bufnr + + if vim.api.nvim_get_current_buf() ~= bufnr then return end + local current_line = vim.api.nvim_win_get_cursor(0)[1] + + -- clean it up first + M.disable_inlay_hints() + + for _, hint in pairs(result) do + local text = hint.label + if hint.paddingLeft then text = " " .. text end + if hint.paddingRight then text = text .. " " end + local line = hint.position.line + if + line == current_line - 1 + or not config.options.inlay_hints.only_current_line + then + local col = hint.position.character + vim.api.nvim_buf_set_extmark(bufnr, namespace, line, col, { + virt_text_pos = "inline", + virt_text = { + { text, config.options.inlay_hints.highlight }, + }, + hl_mode = "combine", + priority = config.options.inlay_hints.priority, + }) + end + end +end + +function M.toggle_inlay_hints() + if enabled then + M.disable_inlay_hints() + else + M.set_inlay_hints() + end + enabled = not enabled + return enabled +end + +function M.disable_inlay_hints() + -- clear namespace which clears the virtual text as well + vim.api.nvim_buf_clear_namespace(0, namespace, 0, -1) +end + +-- Sends the request to clangd to get the inlay hints and handle them +function M.set_inlay_hints() + local buf = vim.api.nvim_get_current_buf() + local clients = vim.lsp.get_clients and vim.lsp.get_clients({ bufnr = buf }) + or vim.lsp.buf_get_clients(buf) + -- ensure clangd is running and request doesn't cause error + for _, c in pairs(clients) do + if c.name == "clangd" then + if config.options.inlay_hints.inline then + vim.lsp.buf_request( + 0, + "textDocument/inlayHint", + get_inline_params(), + inline_handler + ) + else + vim.lsp.buf_request( + 0, + "clangd/inlayHints", + { textDocument = vim.lsp.util.make_text_document_params() }, + handler + ) + end + break + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/memory_usage.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/memory_usage.lua new file mode 100644 index 00000000..1efd52d8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/memory_usage.lua @@ -0,0 +1,112 @@ +local api = vim.api +local fmt = string.format + +local function display(lines) + for k, line in pairs(lines) do -- Pad lines + if k ~= 1 then lines[k] = " " .. line .. " " end + end + local vim_width = api.nvim_get_option("columns") + local vim_height = api.nvim_get_option("lines") + local height = math.ceil(vim_height * 0.7 - 4) + local width = math.ceil(vim_width * 0.7) + local row = math.ceil((vim_height - height) / 2 - 1) + local col = math.ceil((vim_width - width) / 2) + local buf = api.nvim_create_buf(false, true) + api.nvim_open_win(buf, true, { + style = "minimal", + relative = "editor", + width = width, + height = height, + row = row, + col = col, + border = require("clangd_extensions.config").options.memory_usage.border, + }) + vim.bo.shiftwidth = 2 + vim.wo.foldmethod = "indent" + api.nvim_buf_set_lines(buf, 0, -1, true, lines) + api.nvim_set_option_value("bufhidden", "wipe", { buf = buf }) + api.nvim_set_option_value("modifiable", false, { buf = buf }) + api.nvim_set_option_value("buftype", "nofile", { buf = buf }) + api.nvim_buf_set_keymap(buf, "n", "q", ":bd", { + noremap = true, + silent = true, + }) + api.nvim_buf_set_keymap(buf, "n", "", ":bd", { + noremap = true, + silent = true, + }) +end + +local function format_name(name) + if name:sub(1, 7) == "file://" then name = vim.uri_to_fname(name) end + local cwd = vim.fn.getcwd() + if name:sub(1, string.len(cwd)) == cwd then + name = name:sub(string.len(cwd) + 2, -1) + end + return name +end + +local function format_tree( + node, + visited, + result, + padding, + prefix, + expand_preamble +) + if padding == "" then + table.insert( + result, + fmt("Total: self = %s, total = %s", node._self, node._total) + ) + end + visited[prefix] = true + for child_name, child_node in pairs(node) do + if + child_name ~= "_self" + and child_name ~= "_total" + and not visited[prefix .. child_name] + then + child_name = format_name(child_name) + table.insert( + result, + padding + .. fmt( + "%s: self = %s, total = %s", + child_name, + child_node._self, + child_node._total + ) + ) + if child_name ~= "preamble" or expand_preamble then + format_tree( + child_node, + visited, + result, + padding .. " ", + prefix .. child_name, + expand_preamble + ) + end + end + end + return result +end + +local function handler(err, result, expand_preamble) + if err then return end + display(format_tree(result, {}, { "" }, "", "", expand_preamble)) +end + +local M = {} + +function M.show_memory_usage(expand_preamble) + vim.lsp.buf_request( + 0, + "$/memoryUsage", + nil, + function(err, result) handler(err, result, expand_preamble) end + ) +end + +return M diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/switch_source_header.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/switch_source_header.lua new file mode 100644 index 00000000..d19ae9c9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/switch_source_header.lua @@ -0,0 +1,19 @@ +local function handler(_err, uri) + if not uri or uri == "" then + vim.api.nvim_echo({ { "Corresponding file cannot be determined" } }, false, {}) + return + end + local file_name = vim.uri_to_fname(uri) + vim.api.nvim_cmd({ + cmd = "edit", + args = { file_name }, + }, {}) +end + +return { + switch_source_header = function() + vim.lsp.buf_request(0, "textDocument/switchSourceHeader", { + uri = vim.uri_from_bufnr(0), + }, handler) + end, +} diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/symbol_info.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/symbol_info.lua new file mode 100644 index 00000000..ad757fe2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/symbol_info.lua @@ -0,0 +1,29 @@ +local function handler(err, result) + if err or (#result == 0) then return end + local name_str = string.format("name: %s", result[1].name) + local container_str = + string.format("container: %s", result[1].containerName) + vim.lsp.util.open_floating_preview({ name_str, container_str }, "", { + height = 2, + width = math.max(string.len(name_str), string.len(container_str)), + focusable = false, + focus = false, + border = require("clangd_extensions.config").options.symbol_info.border, + }) +end + +local M = {} + +function M.show_symbol_info() + vim.lsp.buf_request(0, "textDocument/symbolInfo", { + textDocument = { + uri = vim.uri_from_bufnr(0), + }, + position = { + line = vim.fn.getcurpos()[2] - 1, + character = vim.fn.getcurpos()[3] - 1, + }, + }, handler) +end + +return M diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/symbol_kind.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/symbol_kind.lua new file mode 100644 index 00000000..74df28cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/symbol_kind.lua @@ -0,0 +1,28 @@ +return { + "File", + "Module", + "Namespace", + "Package", + "Class", + "Method", + "Property", + "Field", + "Constructor", + "Enum", + "Interface", + "Function", + "Variable", + "Constant", + "String", + "Number", + "Boolean", + "Array", + "Object", + "Key", + "Null", + "EnumMember", + "Struct", + "Event", + "Operator", + "TypeParameter", +} diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/type_hierarchy.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/type_hierarchy.lua new file mode 100644 index 00000000..3650d728 --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/lua/clangd_extensions/type_hierarchy.lua @@ -0,0 +1,144 @@ +local symbol_kind = require("clangd_extensions.symbol_kind") +local fmt = string.format +local api = vim.api +local type_hierarchy_augroup = + api.nvim_create_augroup("ClangdTypeHierarchy", {}) + +local M = {} +M.type_to_location = {} +M.offset_encoding = {} + +local function format_tree(node, visited, result, padding, type_to_location) + visited[node.data] = true + table.insert( + result, + padding .. fmt(" • %s: %s", node.name, symbol_kind[node.kind]) + ) + + type_to_location[node.name] = { uri = node.uri, range = node.range } + + if node.parents then + if #node.parents > 0 then + table.insert(result, padding .. " Parents:") + for _, parent in pairs(node.parents) do + if not visited[parent.data] then + format_tree( + parent, + visited, + result, + padding .. " ", + type_to_location + ) + end + end + end + end + + if node.children then + if #node.children > 0 then + table.insert(result, padding .. " Children:") + for _, child in pairs(node.children) do + if not visited[child.data] then + format_tree( + child, + visited, + result, + padding .. " ", + type_to_location + ) + end + end + end + end + + return result +end + +local function handler(err, TypeHierarchyItem, ctx) + if err or not TypeHierarchyItem then + return + else + -- Save old state + local source_win = api.nvim_get_current_win() + + -- Init + M.offset_encoding[ctx.client_id] = + vim.lsp.get_client_by_id(ctx.client_id).offset_encoding + vim.cmd.split(fmt("%s: type hierarchy", TypeHierarchyItem.name)) + local bufnr = vim.api.nvim_get_current_buf() + M.type_to_location[bufnr] = {} + + -- Set content + local lines = format_tree( + TypeHierarchyItem, + {}, + {}, + "", + M.type_to_location[bufnr] + ) + api.nvim_buf_set_lines(bufnr, 0, -1, true, lines) + + -- Set options + vim.bo.modifiable = false + vim.bo.filetype = "ClangdTypeHierarchy" + vim.bo.buftype = "nofile" + vim.bo.bufhidden = "wipe" + vim.bo.buflisted = true + api.nvim_set_option_value("number", false, { scope = "local" }) + api.nvim_set_option_value("relativenumber", false, { scope = "local" }) + api.nvim_set_option_value("spell", false, { scope = "local" }) + api.nvim_set_option_value("cursorline", false, { scope = "local" }) + local winbar = api.nvim_get_option_value("winbar", {}) + local numlines = winbar == "" and #lines or #lines + 1 + local winheight = math.min(numlines, 15) + api.nvim_win_set_height(0, winheight) + + -- Set highlights + vim.cmd([[ + syntax clear + syntax match ClangdTypeName "\( \{2,\}• \)\@<=\w\+\(:\)\@=" + ]]) + vim.api.nvim_set_hl(0, "ClangdTypeName", { link = "Underlined" }) + + -- Set keymap + vim.keymap.set("n", "gd", function() + local word = vim.fn.expand("") + word = string.gsub(word, ":$", "") + local location = M.type_to_location[bufnr][word] + if location ~= nil then + api.nvim_set_current_win(source_win) + vim.lsp.util.jump_to_location( + location, + M.offset_encoding[ctx.client_id] + ) + end + end, { + buffer = bufnr, + desc = "go to definition of type under cursor", + }) + + -- Clear `type_to_location` for this buffer when it is wiped out + api.nvim_create_autocmd("BufWipeOut", { + buffer = bufnr, + group = type_hierarchy_augroup, + callback = function() M.type_to_location[bufnr] = nil end, + }) + end +end + +function M.show_hierarchy() + vim.lsp.buf_request(0, "textDocument/typeHierarchy", { + textDocument = { + uri = vim.uri_from_bufnr(0), + }, + position = { + line = vim.fn.getcurpos()[2] - 1, + character = vim.fn.getcurpos()[3] - 1, + }, + -- TODO make these configurable (config + command args) + resolve = 3, + direction = 2, + }, handler) +end + +return M diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/nvim.yaml b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/nvim.yaml new file mode 100644 index 00000000..b6c8743d --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/nvim.yaml @@ -0,0 +1,5 @@ +--- +base: lua51 +globals: + vim: + any: true diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/plugin/clangd_extensions.lua b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/plugin/clangd_extensions.lua new file mode 100644 index 00000000..ca4a8fdc --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/plugin/clangd_extensions.lua @@ -0,0 +1,42 @@ +local create_command = vim.api.nvim_create_user_command + +create_command('ClangdSetInlayHints', function() + require("clangd_extensions.inlay_hints").set_inlay_hints() +end, {}) + +create_command('ClangdDisableInlayHints', function() + require("clangd_extensions.inlay_hints").disable_inlay_hints() +end, {}) + +create_command('ClangdToggleInlayHints', function() + require("clangd_extensions.inlay_hints").toggle_inlay_hints() +end, {}) + +create_command('ClangdAST', function(opts) + require("clangd_extensions.ast").display_ast(opts.line1, opts.line2) +end, { range = true }) + +create_command('ClangdTypeHierarchy', function() + require("clangd_extensions.type_hierarchy").show_hierarchy() +end, {}) + +create_command('ClangdSymbolInfo', function() + require("clangd_extensions.symbol_info").show_symbol_info() +end, {}) + +create_command( + 'ClangdMemoryUsage', + function(opts) + require("clangd_extensions.memory_usage").show_memory_usage(opts.args == 'expand_preamble') + end, + { + nargs = '?', + complete = function(_, _, _) + return { 'expand_preamble' } + end + } +) + +create_command('ClangdSwitchSourceHeader', function() + require("clangd_extensions.switch_source_header").switch_source_header() +end, {}) diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/selene.toml b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/selene.toml new file mode 100644 index 00000000..98b6ac72 --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/selene.toml @@ -0,0 +1 @@ +std = "nvim" diff --git a/config/neovim/store/lazy-plugins/clangd_extensions.nvim/stylua.toml b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/stylua.toml new file mode 100644 index 00000000..083b1c8a --- /dev/null +++ b/config/neovim/store/lazy-plugins/clangd_extensions.nvim/stylua.toml @@ -0,0 +1,4 @@ +indent_width = 4 +column_width = 80 +indent_type = "Spaces" +collapse_simple_statement = "Always" diff --git a/config/neovim/store/lazy-plugins/cmp-async-path/LICENSE b/config/neovim/store/lazy-plugins/cmp-async-path/LICENSE new file mode 100644 index 00000000..ae725ef1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-async-path/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 hrsh7th + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/cmp-async-path/README.md b/config/neovim/store/lazy-plugins/cmp-async-path/README.md new file mode 100644 index 00000000..36f0406e --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-async-path/README.md @@ -0,0 +1,54 @@ +https://codeberg.org/FelipeLema/cmp-async-path + +# cmp-async-path + +nvim-cmp source for filesystem paths with async processing (neovim won't block while reading from disk). + +forked from https://github.com/hrsh7th/cmp-path/ + +# Setup + +```lua +require'cmp'.setup { + sources = { + { name = 'async_path' } + } +} +``` + + +## Configuration + +The below source configuration options are available. To set any of these options, do: + +```lua +cmp.setup({ + sources = { + { + name = 'async_path', + option = { + -- Options go into this table + }, + }, + }, +}) +``` + + +### trailing_slash (type: boolean) + +_Default:_ `false` + +Specify if completed directory names should include a trailing slash. Enabling this option makes this source behave like Vim's built-in path completion. + +### label_trailing_slash (type: boolean) + +_Default:_ `true` + +Specify if directory names in the completion menu should include a trailing slash. + +### get_cwd (type: function) + +_Default:_ returns the current working directory of the current buffer + +Specifies the base directory for relative paths. diff --git a/config/neovim/store/lazy-plugins/cmp-async-path/after/plugin/cmp_async_path.lua b/config/neovim/store/lazy-plugins/cmp-async-path/after/plugin/cmp_async_path.lua new file mode 100644 index 00000000..ed3ec264 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-async-path/after/plugin/cmp_async_path.lua @@ -0,0 +1 @@ +require('cmp').register_source('async_path', require('cmp_async_path').new()) diff --git a/config/neovim/store/lazy-plugins/cmp-async-path/lua/cmp_async_path/init.lua b/config/neovim/store/lazy-plugins/cmp-async-path/lua/cmp_async_path/init.lua new file mode 100644 index 00000000..6c80b1b1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-async-path/lua/cmp_async_path/init.lua @@ -0,0 +1,249 @@ +local cmp = require 'cmp' +local NAME_REGEX = '\\%([^/\\\\:\\*?<>\'"`\\|]\\)' +local PATH_REGEX = assert(vim.regex( + ([[\%(\%(/PAT*[^/\\\\:\\*?<>\'"`\\| .~]\)\|\%(/\.\.\)\)*/\zePAT*$]]):gsub( + 'PAT', NAME_REGEX))) + +local source = {} + +local constants = {max_lines = 20} + +---@class cmp_path.Option +---@field public trailing_slash boolean +---@field public label_trailing_slash boolean +---@field public get_cwd fun(table): string + +---@type cmp_path.Option +local defaults = { + trailing_slash = false, + label_trailing_slash = true, + get_cwd = function(params) + return vim.fn.expand(('#%d:p:h'):format(params.context.bufnr)) + end, +} + +source.new = function() return setmetatable({}, {__index = source}) end + +source.get_trigger_characters = function() return {'/', '.'} end + +source.get_keyword_pattern = function(_, _) return NAME_REGEX .. '*' end + +source.complete = function(self, params, callback) + local option = self:_validate_option(params) + + local dirname = self:_dirname(params, option) + if not dirname then + return callback() + end + + local include_hidden = string.sub(params.context.cursor_before_line, + params.offset, params.offset) == '.' + self:_candidates(dirname, include_hidden, option, function(err, candidates) + if err then + return callback() + end + callback(candidates) + end) +end + +source.resolve = function(self, completion_item, callback) + local data = completion_item.data + if data.stat and data.stat.type == 'file' then + local ok, documentation = pcall(function() + return self:_get_documentation(data.path, constants.max_lines) + end) + if ok then + completion_item.documentation = documentation + end + end + callback(completion_item) +end + +source._dirname = function(self, params, option) + local s = PATH_REGEX:match_str(params.context.cursor_before_line) + if not s then + return nil + end + + local dirname = string.gsub(string.sub(params.context.cursor_before_line, + s + 2), '%a*$', '') -- exclude '/' + local prefix = string.sub(params.context.cursor_before_line, 1, s + 1) -- include '/' + + local buf_dirname = option.get_cwd(params) + if vim.api.nvim_get_mode().mode == 'c' then + buf_dirname = vim.fn.getcwd() + end + if prefix:match('%.%./$') then + return vim.fn.resolve(buf_dirname .. '/../' .. dirname) + end + if (prefix:match('%./$') or prefix:match('"$') or prefix:match('\'$')) then + return vim.fn.resolve(buf_dirname .. '/' .. dirname) + end + if prefix:match('~/$') then + return vim.fn.resolve(vim.fn.expand('~') .. '/' .. dirname) + end + local env_var_name = prefix:match('%$([%a_]+)/$') + if env_var_name then + local env_var_value = vim.fn.getenv(env_var_name) + if env_var_value ~= vim.NIL then + return vim.fn.resolve(env_var_value .. '/' .. dirname) + end + end + if prefix:match('/$') then + local accept = true + -- Ignore URL components + accept = accept and not prefix:match('%a/$') + -- Ignore URL scheme + accept = accept and not prefix:match('%a+:/$') and + not prefix:match('%a+://$') + -- Ignore HTML closing tags + accept = accept and not prefix:match('= count then + break + end + end + + local filetype = vim.filetype.match({filename = filename}) + if not filetype then + return { + kind = cmp.lsp.MarkupKind.PlainText, + value = table.concat(contents, '\n'), + } + end + + table.insert(contents, 1, '```' .. filetype) + table.insert(contents, '```') + return { + kind = cmp.lsp.MarkupKind.Markdown, + value = table.concat(contents, '\n'), + } +end + +return source diff --git a/config/neovim/store/lazy-plugins/cmp-buffer/LICENSE b/config/neovim/store/lazy-plugins/cmp-buffer/LICENSE new file mode 100644 index 00000000..ae725ef1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-buffer/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 hrsh7th + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/cmp-buffer/README.md b/config/neovim/store/lazy-plugins/cmp-buffer/README.md new file mode 100644 index 00000000..4d0f5611 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-buffer/README.md @@ -0,0 +1,226 @@ +# cmp-buffer + +nvim-cmp source for buffer words. + +## Setup + +```lua +require('cmp').setup({ + sources = { + { name = 'buffer' }, + }, +}) +``` + +## Configuration + +The below source configuration are available. To set any of these options, do: + +```lua +cmp.setup({ + sources = { + { + name = 'buffer', + option = { + -- Options go into this table + }, + }, + }, +}) +``` + + +### keyword_length (type: number) + +_Default:_ `3` + +The number of characters that need to be typed to trigger auto-completion. + + +### keyword_pattern (type: string) + +_Default:_ `[[\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%([\-.]\w*\)*\)]]` + +A vim's regular expression for creating a word list from buffer content. + +You can set this to `[[\k\+]]` if you want to use the `iskeyword` option for recognizing words. +Lua's `[[ ]]` string literals are particularly useful here to avoid escaping all of the backslash +(`\`) characters used for writing regular expressions. + +**NOTE:** Be careful with where you set this option! You must do this: + +```lua +cmp.setup({ + sources = { + { + name = 'buffer', + -- Correct: + option = { + keyword_pattern = [[\k\+]], + } + }, + }, +}) +``` + +Instead of this: + +```lua +cmp.setup({ + sources = { + { + name = 'buffer', + -- Wrong: + keyword_pattern = [[\k\+]], + }, + }, +}) +``` + +The second notation is allowed by nvim-cmp (documented [here](https://github.com/hrsh7th/nvim-cmp#sourcesnumberkeyword_pattern-type-string)), but it is meant for a different purpose and will not be detected by this plugin as the pattern for searching words. + + +### get_bufnrs (type: fun(): number[]) + +_Default:_ `function() return { vim.api.nvim_get_current_buf() } end` + +A function that specifies the buffer numbers to complete. + +You can use the following pre-defined recipes. + +##### All buffers + +```lua +cmp.setup { + sources = { + { + name = 'buffer', + option = { + get_bufnrs = function() + return vim.api.nvim_list_bufs() + end + } + } + } +} +``` + +##### Visible buffers + +```lua +cmp.setup { + sources = { + { + name = 'buffer', + option = { + get_bufnrs = function() + local bufs = {} + for _, win in ipairs(vim.api.nvim_list_wins()) do + bufs[vim.api.nvim_win_get_buf(win)] = true + end + return vim.tbl_keys(bufs) + end + } + } + } +} + +``` + + +### indexing_interval (type: number) + +_Default:_ `100` + +Optimization option. See the section [Indexing](#indexing-and-how-to-optimize-it). + + +### indexing_batch_size (type: number) + +_Default:_ `1000` + +Optimization option. See the section [Indexing](#indexing-and-how-to-optimize-it). + + +### max_indexed_line_length (type: number) + +_Default:_ `1024 * 40` (40 Kilobytes) + +Optimization option. See the section [Indexing](#indexing-and-how-to-optimize-it). + + +## Locality bonus comparator (distance-based sorting) + +This source also provides a comparator function which uses information from the word indexer +to sort completion results based on the distance of the word from the cursor line. It will also +sort completion results coming from other sources, such as Language Servers, which might improve +accuracy of their suggestions too. The usage is as follows: + +```lua +local cmp = require('cmp') +local cmp_buffer = require('cmp_buffer') + +cmp.setup({ + sources = { + { name = 'buffer' }, + -- The rest of your sources... + }, + sorting = { + comparators = { + function(...) return cmp_buffer:compare_locality(...) end, + -- The rest of your comparators... + } + } +}) +``` + + +## Indexing and how to optimize it + +When a buffer is opened, this source first has to scan all lines in the buffer, match all words +and store all of their occurrences. This process is called _indexing_. When actually editing the +text in the buffer, the index of words is kept up-to-date with changes to the buffer's contents, +this is called _watching_. It is done by re-running the indexer on just the changed lines. +Indexing happens completely asynchronously in background, unlike watching, which must be performed +synchronously to ensure that the index of words is kept perfectly in-sync with the lines in the +buffer. However, most of the time this will not be a problem since many typical text edit +operations affect only one or two lines, unless you are pasting a 1000-line snippet. + +_Note that you can freely edit the buffer while it is being indexed_, the underlying algorithm is +written in such a way that your changes will not break the index or cause errors. If a crash does +happen - it is a bug, so please report it. + +The speed of indexing is configurable with two options: `indexing_interval` and +`indexing_batch_size`. Essentially, when indexing, a timer is started, which pulls a batch of +`indexing_batch_size` lines from the buffer, scans them for words, and repeats after +`indexing_interval` milliseconds. Decreasing interval and/or increasing the batch size will make +the indexer faster, but at the expense of higher CPU usage and more lag when editing the file +while indexing is still in progress. Setting `indexing_batch_size` to a negative value will switch +the indexer to the "synchronous" mode: this will process all lines in one go, take less time in +total (since no other code will be running on the Lua thread), but with the obvious downside that +the editor UI will be blocked. + +The option `max_indexed_line_length` controls plugin's behavior in files with very long lines. +This is known to slow this source down significantly (see issue [#13](https://github.com/hrsh7th/cmp-buffer/issues/13)), +so by default it will take only the first few kilobytes of the line it is currently on. In other +words, very long lines are not ignored, but only a part of them is indexed. + +### Performance on large text files + +This source has been tested on code files of a few megabytes in size (5-10) and contains +optimizations for them, however, the indexed words can still take up tens of megabytes of RAM if +the file is large. So, if you wish to avoid accidentally running this source on big files, you +can tweak `get_bufnrs`, for example like this: + +```lua +get_bufnrs = function() + local buf = vim.api.nvim_get_current_buf() + local byte_size = vim.api.nvim_buf_get_offset(buf, vim.api.nvim_buf_line_count(buf)) + if byte_size > 1024 * 1024 then -- 1 Megabyte max + return {} + end + return { buf } +end +``` + +Of course, this snippet can be combined with any other recipes for `get_bufnrs`. diff --git a/config/neovim/store/lazy-plugins/cmp-buffer/after/plugin/cmp_buffer.lua b/config/neovim/store/lazy-plugins/cmp-buffer/after/plugin/cmp_buffer.lua new file mode 100644 index 00000000..a66afbd8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-buffer/after/plugin/cmp_buffer.lua @@ -0,0 +1 @@ +require('cmp').register_source('buffer', require('cmp_buffer')) diff --git a/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/buffer.lua b/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/buffer.lua new file mode 100644 index 00000000..d0ed2858 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/buffer.lua @@ -0,0 +1,380 @@ +local timer = require('cmp_buffer.timer') + +local function clear_table(tbl) + for k in pairs(tbl) do + tbl[k] = nil + end +end + +---@class cmp_buffer.Buffer +---@field public bufnr number +---@field public opts cmp_buffer.Options +---@field public regex any +---@field public timer cmp_buffer.Timer +---@field public lines_count number +---@field public timer_current_line number +---@field public lines_words table +---@field public unique_words_curr_line table +---@field public unique_words_other_lines table +---@field public unique_words_curr_line_dirty boolean +---@field public unique_words_other_lines_dirty boolean +---@field public last_edit_first_line number +---@field public last_edit_last_line number +---@field public closed boolean +---@field public on_close_cb fun()|nil +---@field public words_distances table +---@field public words_distances_last_cursor_row number +---@field public words_distances_dirty boolean +local buffer = {} + +-- For some reason requesting this much lines multiple times in chunks leads to +-- much better memory usage than fetching the entire file in one go. +buffer.GET_LINES_CHUNK_SIZE = 1000 + +---Create new buffer object +---@param bufnr number +---@param opts cmp_buffer.Options +---@return cmp_buffer.Buffer +function buffer.new(bufnr, opts) + local self = setmetatable({}, { __index = buffer }) + + self.bufnr = bufnr + self.timer = timer.new() + self.closed = false + self.on_close_cb = nil + + self.opts = opts + self.regex = vim.regex(self.opts.keyword_pattern) + + self.lines_count = 0 + self.timer_current_line = -1 + self.lines_words = {} + + self.unique_words_curr_line = {} + self.unique_words_other_lines = {} + self.unique_words_curr_line_dirty = true + self.unique_words_other_lines_dirty = true + self.last_edit_first_line = 0 + self.last_edit_last_line = 0 + + self.words_distances = {} + self.words_distances_dirty = true + self.words_distances_last_cursor_row = 0 + + return self +end + +---Close buffer +function buffer.close(self) + self.closed = true + self:stop_indexing_timer() + self.timer:close() + self.timer = nil + + self.lines_count = 0 + self.timer_current_line = -1 + self.lines_words = {} + + self.unique_words_curr_line = {} + self.unique_words_other_lines = {} + self.unique_words_curr_line_dirty = false + self.unique_words_other_lines_dirty = false + self.last_edit_first_line = 0 + self.last_edit_last_line = 0 + + self.words_distances = {} + self.words_distances_dirty = false + self.words_distances_last_cursor_row = 0 + + if self.on_close_cb then + self.on_close_cb() + end +end + +function buffer.stop_indexing_timer(self) + self.timer:stop() + self.timer_current_line = -1 +end + +function buffer.mark_all_lines_dirty(self) + self.unique_words_curr_line_dirty = true + self.unique_words_other_lines_dirty = true + self.last_edit_first_line = 0 + self.last_edit_last_line = 0 + self.words_distances_dirty = true +end + +--- Workaround for https://github.com/neovim/neovim/issues/16729 +function buffer.safe_buf_call(self, callback) + if vim.api.nvim_get_current_buf() == self.bufnr then + callback() + else + vim.api.nvim_buf_call(self.bufnr, callback) + end +end + +function buffer.index_range(self, range_start, range_end, skip_already_indexed) + self:safe_buf_call(function() + local chunk_size = self.GET_LINES_CHUNK_SIZE + local chunk_start = range_start + while chunk_start < range_end do + local chunk_end = math.min(chunk_start + chunk_size, range_end) + local chunk_lines = vim.api.nvim_buf_get_lines(self.bufnr, chunk_start, chunk_end, true) + for i, line in ipairs(chunk_lines) do + if not skip_already_indexed or not self.lines_words[chunk_start + i] then + self:index_line(chunk_start + i, line) + end + end + chunk_start = chunk_end + end + end) +end + +function buffer.start_indexing_timer(self) + self.lines_count = vim.api.nvim_buf_line_count(self.bufnr) + self.timer_current_line = 0 + + -- Negative values result in an integer overflow in luv (vim.loop), and zero + -- disables timer repeat, so only intervals larger than 1 are valid. + local interval = math.max(1, self.opts.indexing_interval) + self.timer:start(0, interval, function() + if self.closed then + self:stop_indexing_timer() + return + end + + -- Note that the async indexer is designed to not break even if the user is + -- editing the file while it is in the process of being indexed. Because + -- the indexing in watcher must use the synchronous algorithm, we assume + -- that the data already present in self.lines_words to be correct and + -- doesn't need refreshing here because even if we do receive text from + -- nvim_buf_get_lines different from what the watcher has seen so far, it + -- (the watcher) will catch up on the next on_lines event. + + -- Skip over the already indexed lines + while self.lines_words[self.timer_current_line + 1] do + self.timer_current_line = self.timer_current_line + 1 + end + + local batch_start = self.timer_current_line + local batch_size = self.opts.indexing_batch_size + -- NOTE: self.lines_count may be modified by the indexer. + local batch_end = batch_size >= 1 and math.min(batch_start + batch_size, self.lines_count) or self.lines_count + if batch_end >= self.lines_count then + self:stop_indexing_timer() + end + self.timer_current_line = batch_end + self:mark_all_lines_dirty() + + self:index_range(batch_start, batch_end, true) + end) +end + +--- watch +function buffer.watch(self) + self.lines_count = vim.api.nvim_buf_line_count(self.bufnr) + + -- NOTE: As far as I know, indexing in watching can't be done asynchronously + -- because even built-in commands generate multiple consequent `on_lines` + -- events, and I'm not even mentioning plugins here. To get accurate results + -- we would have to either re-index the entire file on throttled events (slow + -- and looses the benefit of on_lines watching), or put the events in a + -- queue, which would complicate the plugin a lot. Plus, most changes which + -- trigger this event will be from regular editing, and so 99% of the time + -- they will affect only 1-2 lines. + vim.api.nvim_buf_attach(self.bufnr, false, { + -- NOTE: line indexes are 0-based and the last line is not inclusive. + on_lines = function(_, _, _, first_line, old_last_line, new_last_line, _, _, _) + if self.closed then + return true + end + + if old_last_line == new_last_line and first_line == new_last_line then + -- This condition is really intended as a workaround for + -- https://github.com/hrsh7th/cmp-buffer/issues/28, but it will also + -- protect us from completely empty text edits. + return + end + + local delta = new_last_line - old_last_line + local old_lines_count = self.lines_count + local new_lines_count = old_lines_count + delta + if new_lines_count == 0 then -- clear + -- This branch protects against bugs after full-file deletion. If you + -- do, for example, gdGG, the new_last_line of the event will be zero. + -- Which is not true, a buffer always contains at least one empty line, + -- only unloaded buffers contain zero lines. + new_lines_count = 1 + for i = old_lines_count, 2, -1 do + self.lines_words[i] = nil + end + self.lines_words[1] = {} + elseif delta > 0 then -- append + -- Explicitly reserve more slots in the array part of the lines table, + -- all of them will be filled in the next loop, but in reverse order + -- (which is why I am concerned about preallocation). Why is there no + -- built-in function to do this in Lua??? + for i = old_lines_count + 1, new_lines_count do + self.lines_words[i] = false + end + -- Move forwards the unchanged elements in the tail part. + for i = old_lines_count, old_last_line + 1, -1 do + self.lines_words[i + delta] = self.lines_words[i] + end + -- Fill in new tables for the added lines. + for i = old_last_line + 1, new_last_line do + self.lines_words[i] = {} + end + elseif delta < 0 then -- remove + -- Move backwards the unchanged elements in the tail part. + for i = old_last_line + 1, old_lines_count do + self.lines_words[i + delta] = self.lines_words[i] + end + -- Remove (already copied) tables from the end, in reverse order, so + -- that we don't make holes in the lines table. + for i = old_lines_count, new_lines_count + 1, -1 do + self.lines_words[i] = nil + end + end + self.lines_count = new_lines_count + + -- This branch is support code for handling cases when the user is + -- editing the buffer while the async indexer is running. It solves the + -- problem that if new lines are inserted or old lines are deleted, the + -- indexes of each subsequent line will change, and so the indexer + -- current position must be adjusted to not accidentally skip any lines. + if self.timer:is_active() then + if first_line <= self.timer_current_line and self.timer_current_line < old_last_line then + -- The indexer was in the area of the current text edit. We will + -- synchronously index this area it in a moment, so the indexer + -- should resume from right after the edit range. + self.timer_current_line = new_last_line + elseif self.timer_current_line >= old_last_line then + -- The indexer was somewhere past the current text edit. This means + -- that the line numbers could have changed, and the indexing + -- position must be adjusted accordingly. + self.timer_current_line = self.timer_current_line + delta + end + end + + if first_line == self.last_edit_first_line and old_last_line == self.last_edit_last_line and new_last_line == self.last_edit_last_line then + self.unique_words_curr_line_dirty = true + else + self.unique_words_curr_line_dirty = true + self.unique_words_other_lines_dirty = true + end + self.last_edit_first_line = first_line + self.last_edit_last_line = new_last_line + + self.words_distances_dirty = true + + -- replace lines + self:index_range(first_line, new_last_line) + end, + + on_reload = function(_, _) + if self.closed then + return true + end + + clear_table(self.lines_words) + + self:stop_indexing_timer() + self:start_indexing_timer() + end, + + on_detach = function(_, _) + if self.closed then + return true + end + self:close() + end, + }) +end + +---@param linenr number +---@param line string +function buffer.index_line(self, linenr, line) + local words = self.lines_words[linenr] + if not words then + words = {} + self.lines_words[linenr] = words + else + clear_table(words) + end + local word_i = 1 + + local remaining = line + -- The if statement checks the number of bytes in the line string, but slices + -- it on the number of characters. This is not a problem because the number + -- of characters is always equal to (if only ASCII characters are used) or + -- smaller than (if multibyte Unicode characters are used) the number of bytes. + -- In other words, if the line contains more characters than the max limit, + -- then it will always contain more bytes than the same limit. + -- This check is here because calling a Vimscript function is relatively slow. + if #remaining > self.opts.max_indexed_line_length then + remaining = vim.fn.strcharpart(line, 0, self.opts.max_indexed_line_length) + end + while #remaining > 0 do + -- NOTE: Both start and end indexes here are 0-based (unlike Lua strings), + -- and the end index is not inclusive. + local match_start, match_end = self.regex:match_str(remaining) + if match_start and match_end then + local word = remaining:sub(match_start + 1, match_end) + if #word >= self.opts.keyword_length then + words[word_i] = word + word_i = word_i + 1 + end + remaining = remaining:sub(match_end + 1) + else + break + end + end +end + +function buffer.get_words(self) + -- NOTE: unique_words are rebuilt on-demand because it is common for the + -- watcher callback to be fired VERY frequently, and a rebuild needs to go + -- over ALL lines, not just the changed ones. + if self.unique_words_other_lines_dirty then + clear_table(self.unique_words_other_lines) + self:rebuild_unique_words(self.unique_words_other_lines, 0, self.last_edit_first_line) + self:rebuild_unique_words(self.unique_words_other_lines, self.last_edit_last_line, self.lines_count) + self.unique_words_other_lines_dirty = false + end + if self.unique_words_curr_line_dirty then + clear_table(self.unique_words_curr_line) + self:rebuild_unique_words(self.unique_words_curr_line, self.last_edit_first_line, self.last_edit_last_line) + self.unique_words_curr_line_dirty = false + end + return { self.unique_words_other_lines, self.unique_words_curr_line } +end + +--- rebuild_unique_words +function buffer.rebuild_unique_words(self, words_table, range_start, range_end) + for i = range_start + 1, range_end do + for _, w in ipairs(self.lines_words[i] or {}) do + words_table[w] = true + end + end +end + +---@param cursor_row number +---@return table +function buffer.get_words_distances(self, cursor_row) + if self.words_distances_dirty or cursor_row ~= self.words_distances_last_cursor_row then + local distances = self.words_distances + clear_table(distances) + for i = 1, self.lines_count do + for _, w in ipairs(self.lines_words[i] or {}) do + local dist = math.abs(cursor_row - i) + distances[w] = distances[w] and math.min(distances[w], dist) or dist + end + end + self.words_distances_last_cursor_row = cursor_row + self.words_distances_dirty = false + end + return self.words_distances +end + +return buffer diff --git a/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/init.lua b/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/init.lua new file mode 100644 index 00000000..1f10b87e --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/init.lua @@ -0,0 +1 @@ +return require('cmp_buffer.source').new() diff --git a/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/source.lua b/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/source.lua new file mode 100644 index 00000000..9be673cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/source.lua @@ -0,0 +1,125 @@ +local buffer = require('cmp_buffer.buffer') + +---@class cmp_buffer.Options +---@field public keyword_length number +---@field public keyword_pattern string +---@field public get_bufnrs fun(): number[] +---@field public indexing_batch_size number +---@field public indexing_interval number +---@field public max_indexed_line_length number + +---@type cmp_buffer.Options +local defaults = { + keyword_length = 3, + keyword_pattern = [[\%(-\?\d\+\%(\.\d\+\)\?\|\h\%(\w\|á\|Á\|é\|É\|í\|Í\|ó\|Ó\|ú\|Ú\)*\%(-\%(\w\|á\|Á\|é\|É\|í\|Í\|ó\|Ó\|ú\|Ú\)*\)*\)]], + get_bufnrs = function() + return { vim.api.nvim_get_current_buf() } + end, + indexing_batch_size = 1000, + indexing_interval = 100, + max_indexed_line_length = 1024 * 40, +} + +local source = {} + +source.new = function() + local self = setmetatable({}, { __index = source }) + self.buffers = {} + return self +end + +---@return cmp_buffer.Options +source._validate_options = function(_, params) + local opts = vim.tbl_deep_extend('keep', params.option, defaults) + vim.validate({ + keyword_length = { opts.keyword_length, 'number' }, + keyword_pattern = { opts.keyword_pattern, 'string' }, + get_bufnrs = { opts.get_bufnrs, 'function' }, + indexing_batch_size = { opts.indexing_batch_size, 'number' }, + indexing_interval = { opts.indexing_interval, 'number' }, + }) + return opts +end + +source.get_keyword_pattern = function(self, params) + local opts = self:_validate_options(params) + return opts.keyword_pattern +end + +source.complete = function(self, params, callback) + local opts = self:_validate_options(params) + + local processing = false + local bufs = self:_get_buffers(opts) + for _, buf in ipairs(bufs) do + if buf.timer:is_active() then + processing = true + break + end + end + + vim.defer_fn(function() + local input = string.sub(params.context.cursor_before_line, params.offset) + local items = {} + local words = {} + for _, buf in ipairs(bufs) do + for _, word_list in ipairs(buf:get_words()) do + for word, _ in pairs(word_list) do + if not words[word] and input ~= word then + words[word] = true + table.insert(items, { + label = word, + dup = 0, + }) + end + end + end + end + + callback({ + items = items, + isIncomplete = processing, + }) + end, processing and 100 or 0) +end + +---@param opts cmp_buffer.Options +---@return cmp_buffer.Buffer[] +source._get_buffers = function(self, opts) + local buffers = {} + for _, bufnr in ipairs(opts.get_bufnrs()) do + if not self.buffers[bufnr] then + local new_buf = buffer.new(bufnr, opts) + new_buf.on_close_cb = function() + self.buffers[bufnr] = nil + end + new_buf:start_indexing_timer() + new_buf:watch() + self.buffers[bufnr] = new_buf + end + table.insert(buffers, self.buffers[bufnr]) + end + + return buffers +end + +source._get_distance_from_entry = function(self, entry) + local buf = self.buffers[entry.context.bufnr] + if buf then + local distances = buf:get_words_distances(entry.context.cursor.line + 1) + return distances[entry.completion_item.filterText] or distances[entry.completion_item.label] + end +end + +source.compare_locality = function(self, entry1, entry2) + if entry1.context ~= entry2.context then + return + end + local dist1 = self:_get_distance_from_entry(entry1) or math.huge + local dist2 = self:_get_distance_from_entry(entry2) or math.huge + if dist1 ~= dist2 then + return dist1 < dist2 + end +end + +return source diff --git a/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/timer.lua b/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/timer.lua new file mode 100644 index 00000000..3ed83c79 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-buffer/lua/cmp_buffer/timer.lua @@ -0,0 +1,68 @@ +---This timer matches the semantics of setInterval and clearInterval of +---Javascript. It provides a more reliable alternative to vim.loop.timer_start +---with a callback wrapped into a vim.schedule call by addressing two problems: +---1. Scheduled callbacks are invoked less frequently than a libuv timer with a +--- small interval (1-5ms). This causes those callbacks to fill up the queue +--- in the event loop, and so the callback function may get invoked multiple +--- times on one event loop tick. In contrast, Javascript's setInterval +--- guarantees that the callback is not invoked more frequently than the +--- interval. +---2. When a libuv timer is stopped with vim.loop.timer_stop, it doesn't affect +--- the callbacks that have already been scheduled. So timer_stop will not +--- immediately stop the timer, the actual callback function will run one +--- more time until it is finally stopped. This implementation ensures that +--- timer_stop prevents any subsequent invocations of the callback. +--- +---@class cmp_buffer.Timer +---@field public handle any +---@field private callback_wrapper_instance fun()|nil +local timer = {} + +function timer.new() + local self = setmetatable({}, { __index = timer }) + self.handle = vim.loop.new_timer() + self.callback_wrapper_instance = nil + return self +end + +---@param timeout_ms number +---@param repeat_ms number +---@param callback fun() +function timer:start(timeout_ms, repeat_ms, callback) + -- This is the flag that fixes problem 1. + local scheduled = false + -- Creating a function on every call to timer_start ensures that we can always + -- detect when a different callback is set by calling timer_start and prevent + -- the old one from being invoked. + local function callback_wrapper() + if scheduled then + return + end + scheduled = true + vim.schedule(function() + scheduled = false + -- Either a different callback was set, or the timer has been stopped. + if self.callback_wrapper_instance ~= callback_wrapper then + return + end + callback() + end) + end + self.handle:start(timeout_ms, repeat_ms, callback_wrapper) + self.callback_wrapper_instance = callback_wrapper +end + +function timer:stop() + self.handle:stop() + self.callback_wrapper_instance = nil +end + +function timer:is_active() + return self.handle:is_active() +end + +function timer:close() + self.handle:close() +end + +return timer diff --git a/config/neovim/store/lazy-plugins/cmp-cmdline/LICENSE b/config/neovim/store/lazy-plugins/cmp-cmdline/LICENSE new file mode 100644 index 00000000..3a4ecf60 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-cmdline/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 hrsh7th + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/cmp-cmdline/README.md b/config/neovim/store/lazy-plugins/cmp-cmdline/README.md new file mode 100644 index 00000000..9107a98e --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-cmdline/README.md @@ -0,0 +1,52 @@ +# cmp-cmdline + +nvim-cmp source for vim's cmdline. + +# Setup + +Completions for `/` search based on current buffer: +```lua + -- `/` cmdline setup. + cmp.setup.cmdline('/', { + mapping = cmp.mapping.preset.cmdline(), + sources = { + { name = 'buffer' } + } + }) +``` + +Completions for command mode: +```lua + -- `:` cmdline setup. + cmp.setup.cmdline(':', { + mapping = cmp.mapping.preset.cmdline(), + sources = cmp.config.sources({ + { name = 'path' } + }, { + { + name = 'cmdline', + option = { + ignore_cmds = { 'Man', '!' } + } + } + }) + }) +``` + +For the buffer source to work, [cmp-buffer](https://github.com/hrsh7th/cmp-buffer) is needed. + + +# Option + +### ignore_cmds: string[] +Default: `{ "Man", "!" }` + +You can specify ignore command name. + +### treat_trailing_slash: boolean +Default: `true` + +`vim.fn.getcompletion` can return path items. +unfortunately, that items has trailing slash so we don't narrowing with next directory with pressing `/`. + +if you turnd on this option, `cmp-cmdline` removes trailing slash automatically. diff --git a/config/neovim/store/lazy-plugins/cmp-cmdline/after/plugin/cmp_cmdline.lua b/config/neovim/store/lazy-plugins/cmp-cmdline/after/plugin/cmp_cmdline.lua new file mode 100644 index 00000000..936e662f --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-cmdline/after/plugin/cmp_cmdline.lua @@ -0,0 +1 @@ +require('cmp').register_source('cmdline', require('cmp_cmdline').new()) diff --git a/config/neovim/store/lazy-plugins/cmp-cmdline/lua/cmp_cmdline/init.lua b/config/neovim/store/lazy-plugins/cmp-cmdline/lua/cmp_cmdline/init.lua new file mode 100644 index 00000000..53e75935 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-cmdline/lua/cmp_cmdline/init.lua @@ -0,0 +1,248 @@ +local cmp = require('cmp') + +---@param patterns string[] +---@param head boolean +---@return table #regex object +local function create_regex(patterns, head) + local pattern = [[\%(]] .. table.concat(patterns, [[\|]]) .. [[\)]] + if head then + pattern = '^' .. pattern + end + return vim.regex(pattern) +end + +---@class cmp-cmdline.Option +---@field treat_trailing_slash boolean +---@field ignore_cmds string[] +local DEFAULT_OPTION = { + treat_trailing_slash = true, + ignore_cmds = { 'Man', '!' } +} + +local MODIFIER_REGEX = create_regex({ + [=[\s*abo\%[veleft]\s*]=], + [=[\s*bel\%[owright]\s*]=], + [=[\s*bo\%[tright]\s*]=], + [=[\s*bro\%[wse]\s*]=], + [=[\s*conf\%[irm]\s*]=], + [=[\s*hid\%[e]\s*]=], + [=[\s*keepal\s*t]=], + [=[\s*keeppa\%[tterns]\s*]=], + [=[\s*lefta\%[bove]\s*]=], + [=[\s*loc\%[kmarks]\s*]=], + [=[\s*nos\%[wapfile]\s*]=], + [=[\s*rightb\%[elow]\s*]=], + [=[\s*sil\%[ent]\s*]=], + [=[\s*tab\s*]=], + [=[\s*to\%[pleft]\s*]=], + [=[\s*verb\%[ose]\s*]=], + [=[\s*vert\%[ical]\s*]=], +}, true) + +local COUNT_RANGE_REGEX = create_regex({ + [=[\s*\%(\d\+\|\$\)\%[,\%(\d\+\|\$\)]\s*]=], + [=[\s*'\%[<,'>]\s*]=], + [=[\s*\%(\d\+\|\$\)\s*]=], +}, true) + +local ONLY_RANGE_REGEX = create_regex({ + [=[^\s*\%(\d\+\|\$\)\%[,\%(\d\+\|\$\)]\s*$]=], + [=[^\s*'\%[<,'>]\s*$]=], + [=[^\s*\%(\d\+\|\$\)\s*$]=], +}, true) + +local OPTION_NAME_COMPLETION_REGEX = create_regex({ + [=[se\%[tlocal][^=]*$]=], +}, true) + +---@param word string +---@return boolean? +local function is_boolean_option(word) + local ok, opt = pcall(function() + return vim.opt[word]:get() + end) + if ok then + return type(opt) == 'boolean' + end +end + +---@class cmp.Cmdline.Definition +---@field ctype string +---@field regex string +---@field kind lsp.CompletionItemKind +---@field isIncomplete boolean +---@field exec fun(option: table, arglead: string, cmdline: string, force: boolean): lsp.CompletionItem[] +---@field fallback boolean? + +---@type cmp.Cmdline.Definition[] +local definitions = { + { + ctype = 'cmdline', + regex = [=[[^[:blank:]]*$]=], + kind = cmp.lsp.CompletionItemKind.Variable, + isIncomplete = true, + ---@param option cmp-cmdline.Option + exec = function(option, arglead, cmdline, force) + -- Ignore range only cmdline. (e.g.: 4, '<,'>) + if not force and ONLY_RANGE_REGEX:match_str(cmdline) then + return {} + end + + local _, parsed = pcall(function() + local target = cmdline + local s, e = COUNT_RANGE_REGEX:match_str(target) + if s and e then + target = target:sub(e + 1) + end + -- nvim_parse_cmd throw error when the cmdline contains range specifier. + return vim.api.nvim_parse_cmd(target, {}) or {} + end) + parsed = parsed or {} + + -- Check ignore cmd. + if vim.tbl_contains(option.ignore_cmds, parsed.cmd) then + return {} + end + + -- Cleanup modifiers. + -- We can just remove modifiers because modifiers is always separated by space. + if arglead ~= cmdline then + while true do + local s, e = MODIFIER_REGEX:match_str(cmdline) + if s == nil then + break + end + cmdline = string.sub(cmdline, e + 1) + end + end + + -- Support `lua vim.treesitter._get|` or `'<,'>del|` completion. + -- In this case, the `vim.fn.getcompletion` will return only `get_query` for `vim.treesitter.get_|`. + -- We should detect `vim.treesitter.` and `get_query` separately. + -- TODO: The `\h\w*` was choosed by huristic. We should consider more suitable detection. + local fixed_input + do + local suffix_pos = vim.regex([[\h\w*$]]):match_str(arglead) + fixed_input = string.sub(arglead, 1, suffix_pos or #arglead) + end + + -- The `vim.fn.getcompletion` does not return `*no*cursorline` option. + -- cmp-cmdline corrects `no` prefix for option name. + local is_option_name_completion = OPTION_NAME_COMPLETION_REGEX:match_str(cmdline) ~= nil + + --- create items. + local items = {} + local escaped = cmdline:gsub([[\\]], [[\\\\]]); + for _, word_or_item in ipairs(vim.fn.getcompletion(escaped, 'cmdline')) do + local word = type(word_or_item) == 'string' and word_or_item or word_or_item.word + local item = { label = word } + table.insert(items, item) + if is_option_name_completion and is_boolean_option(word) then + table.insert(items, vim.tbl_deep_extend('force', {}, item, { + label = 'no' .. word, + filterText = word, + })) + end + end + + -- fix label with `fixed_input` + for _, item in ipairs(items) do + if not string.find(item.label, fixed_input, 1, true) then + item.label = fixed_input .. item.label + end + end + + -- fix trailing slash for path like item + if option.treat_trailing_slash then + for _, item in ipairs(items) do + local is_target = string.match(item.label, [[/$]]) + is_target = is_target and not (string.match(item.label, [[~/$]])) + is_target = is_target and not (string.match(item.label, [[%./$]])) + is_target = is_target and not (string.match(item.label, [[%.%./$]])) + if is_target then + item.label = item.label:sub(1, -2) + end + end + end + return items + end + }, +} + +local source = {} + +source.new = function() + return setmetatable({ + before_line = '', + offset = -1, + ctype = '', + items = {}, + }, { __index = source }) +end + +source.get_keyword_pattern = function() + return [=[[^[:blank:]]*]=] +end + +source.get_trigger_characters = function() + return { ' ', '.', '#', '-' } +end + +source.complete = function(self, params, callback) + local offset = 0 + local ctype = '' + local items = {} + local kind + local isIncomplete = false + for _, def in ipairs(definitions) do + local s, e = vim.regex(def.regex):match_str(params.context.cursor_before_line) + if s and e then + offset = s + ctype = def.ctype + items = def.exec( + vim.tbl_deep_extend('keep', params.option or {}, DEFAULT_OPTION), + string.sub(params.context.cursor_before_line, s + 1), + params.context.cursor_before_line, + params.context:get_reason() == cmp.ContextReason.Manual + ) + kind = def.kind + isIncomplete = def.isIncomplete + if not (#items == 0 and def.fallback) then + break + end + end + end + + local labels = {} + for _, item in ipairs(items) do + item.kind = kind + labels[item.label] = true + end + + -- `vim.fn.getcompletion` does not handle fuzzy matches. So, we must return all items, including items that were matched in the previous input. + local should_merge_previous_items = false + if #params.context.cursor_before_line > #self.before_line then + should_merge_previous_items = string.find(params.context.cursor_before_line, self.before_line, 1, true) == 1 + elseif #params.context.cursor_before_line < #self.before_line then + should_merge_previous_items = string.find(self.before_line, params.context.cursor_before_line, 1, true) == 1 + end + + if should_merge_previous_items and self.offset == offset and self.ctype == ctype then + for _, item in ipairs(self.items) do + if not labels[item.label] then + table.insert(items, item) + end + end + end + self.before_line = params.context.cursor_before_line + self.offset = offset + self.ctype = ctype + self.items = items + + callback({ + isIncomplete = isIncomplete, + items = items, + }) +end + +return source diff --git a/config/neovim/store/lazy-plugins/cmp-emoji/README.md b/config/neovim/store/lazy-plugins/cmp-emoji/README.md new file mode 100644 index 00000000..853f0370 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-emoji/README.md @@ -0,0 +1,22 @@ +# cmp-emoji + +nvim-cmp source for emojis. + +# Setup + +```lua +require'cmp'.setup { + sources = { + { name = 'emoji' } + } +} +``` + +# Option + +#### insert (type: boolean) + +Speficy emoji should be insert or not. + +Default: `false` + diff --git a/config/neovim/store/lazy-plugins/cmp-emoji/after/plugin/cmp_emoji.lua b/config/neovim/store/lazy-plugins/cmp-emoji/after/plugin/cmp_emoji.lua new file mode 100644 index 00000000..ca2fa187 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-emoji/after/plugin/cmp_emoji.lua @@ -0,0 +1,2 @@ +require'cmp'.register_source('emoji', require'cmp_emoji'.new()) + diff --git a/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/emoji.json b/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/emoji.json new file mode 100755 index 00000000..cab15c80 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/emoji.json @@ -0,0 +1 @@ +[{"name":"HASH KEY","unified":"0023-FE0F-20E3","non_qualified":"0023-20E3","docomo":"E6E0","au":"EB84","softbank":"E210","google":"FE82C","image":"0023-fe0f-20e3.png","sheet_x":0,"sheet_y":0,"short_name":"hash","short_names":["hash"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1549,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP: *","unified":"002A-FE0F-20E3","non_qualified":"002A-20E3","docomo":null,"au":null,"softbank":null,"google":null,"image":"002a-fe0f-20e3.png","sheet_x":0,"sheet_y":1,"short_name":"keycap_star","short_names":["keycap_star"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1550,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP 0","unified":"0030-FE0F-20E3","non_qualified":"0030-20E3","docomo":"E6EB","au":"E5AC","softbank":"E225","google":"FE837","image":"0030-fe0f-20e3.png","sheet_x":0,"sheet_y":2,"short_name":"zero","short_names":["zero"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1551,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP 1","unified":"0031-FE0F-20E3","non_qualified":"0031-20E3","docomo":"E6E2","au":"E522","softbank":"E21C","google":"FE82E","image":"0031-fe0f-20e3.png","sheet_x":0,"sheet_y":3,"short_name":"one","short_names":["one"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1552,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP 2","unified":"0032-FE0F-20E3","non_qualified":"0032-20E3","docomo":"E6E3","au":"E523","softbank":"E21D","google":"FE82F","image":"0032-fe0f-20e3.png","sheet_x":0,"sheet_y":4,"short_name":"two","short_names":["two"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1553,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP 3","unified":"0033-FE0F-20E3","non_qualified":"0033-20E3","docomo":"E6E4","au":"E524","softbank":"E21E","google":"FE830","image":"0033-fe0f-20e3.png","sheet_x":0,"sheet_y":5,"short_name":"three","short_names":["three"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1554,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP 4","unified":"0034-FE0F-20E3","non_qualified":"0034-20E3","docomo":"E6E5","au":"E525","softbank":"E21F","google":"FE831","image":"0034-fe0f-20e3.png","sheet_x":0,"sheet_y":6,"short_name":"four","short_names":["four"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1555,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP 5","unified":"0035-FE0F-20E3","non_qualified":"0035-20E3","docomo":"E6E6","au":"E526","softbank":"E220","google":"FE832","image":"0035-fe0f-20e3.png","sheet_x":0,"sheet_y":7,"short_name":"five","short_names":["five"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1556,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP 6","unified":"0036-FE0F-20E3","non_qualified":"0036-20E3","docomo":"E6E7","au":"E527","softbank":"E221","google":"FE833","image":"0036-fe0f-20e3.png","sheet_x":0,"sheet_y":8,"short_name":"six","short_names":["six"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1557,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP 7","unified":"0037-FE0F-20E3","non_qualified":"0037-20E3","docomo":"E6E8","au":"E528","softbank":"E222","google":"FE834","image":"0037-fe0f-20e3.png","sheet_x":0,"sheet_y":9,"short_name":"seven","short_names":["seven"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1558,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP 8","unified":"0038-FE0F-20E3","non_qualified":"0038-20E3","docomo":"E6E9","au":"E529","softbank":"E223","google":"FE835","image":"0038-fe0f-20e3.png","sheet_x":0,"sheet_y":10,"short_name":"eight","short_names":["eight"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1559,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"KEYCAP 9","unified":"0039-FE0F-20E3","non_qualified":"0039-20E3","docomo":"E6EA","au":"E52A","softbank":"E224","google":"FE836","image":"0039-fe0f-20e3.png","sheet_x":0,"sheet_y":11,"short_name":"nine","short_names":["nine"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1560,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"COPYRIGHT SIGN","unified":"00A9-FE0F","non_qualified":"00A9","docomo":"E731","au":"E558","softbank":"E24E","google":"FEB29","image":"00a9-fe0f.png","sheet_x":0,"sheet_y":12,"short_name":"copyright","short_names":["copyright"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1546,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"REGISTERED SIGN","unified":"00AE-FE0F","non_qualified":"00AE","docomo":"E736","au":"E559","softbank":"E24F","google":"FEB2D","image":"00ae-fe0f.png","sheet_x":0,"sheet_y":13,"short_name":"registered","short_names":["registered"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1547,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"MAHJONG TILE RED DRAGON","unified":"1F004","non_qualified":null,"docomo":null,"au":"E5D1","softbank":"E12D","google":"FE80B","image":"1f004.png","sheet_x":0,"sheet_y":14,"short_name":"mahjong","short_names":["mahjong"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1141,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PLAYING CARD BLACK JOKER","unified":"1F0CF","non_qualified":null,"docomo":null,"au":"EB6F","softbank":null,"google":"FE812","image":"1f0cf.png","sheet_x":0,"sheet_y":15,"short_name":"black_joker","short_names":["black_joker"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1140,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEGATIVE SQUARED LATIN CAPITAL LETTER A","unified":"1F170-FE0F","non_qualified":"1F170","docomo":null,"au":"EB26","softbank":"E532","google":"FE50B","image":"1f170-fe0f.png","sheet_x":0,"sheet_y":16,"short_name":"a","short_names":["a"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1567,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEGATIVE SQUARED LATIN CAPITAL LETTER B","unified":"1F171-FE0F","non_qualified":"1F171","docomo":null,"au":"EB27","softbank":"E533","google":"FE50C","image":"1f171-fe0f.png","sheet_x":0,"sheet_y":17,"short_name":"b","short_names":["b"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1569,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEGATIVE SQUARED LATIN CAPITAL LETTER O","unified":"1F17E-FE0F","non_qualified":"1F17E","docomo":null,"au":"EB28","softbank":"E535","google":"FE50E","image":"1f17e-fe0f.png","sheet_x":0,"sheet_y":18,"short_name":"o2","short_names":["o2"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1578,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEGATIVE SQUARED LATIN CAPITAL LETTER P","unified":"1F17F-FE0F","non_qualified":"1F17F","docomo":"E66C","au":"E4A6","softbank":"E14F","google":"FE7F6","image":"1f17f-fe0f.png","sheet_x":0,"sheet_y":19,"short_name":"parking","short_names":["parking"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1580,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEGATIVE SQUARED AB","unified":"1F18E","non_qualified":null,"docomo":null,"au":"EB29","softbank":"E534","google":"FE50D","image":"1f18e.png","sheet_x":0,"sheet_y":20,"short_name":"ab","short_names":["ab"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1568,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CL","unified":"1F191","non_qualified":null,"docomo":"E6DB","au":"E5AB","softbank":null,"google":"FEB84","image":"1f191.png","sheet_x":0,"sheet_y":21,"short_name":"cl","short_names":["cl"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1570,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED COOL","unified":"1F192","non_qualified":null,"docomo":null,"au":"EA85","softbank":"E214","google":"FEB38","image":"1f192.png","sheet_x":0,"sheet_y":22,"short_name":"cool","short_names":["cool"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1571,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED FREE","unified":"1F193","non_qualified":null,"docomo":"E6D7","au":"E578","softbank":null,"google":"FEB21","image":"1f193.png","sheet_x":0,"sheet_y":23,"short_name":"free","short_names":["free"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1572,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED ID","unified":"1F194","non_qualified":null,"docomo":"E6D8","au":"EA88","softbank":"E229","google":"FEB81","image":"1f194.png","sheet_x":0,"sheet_y":24,"short_name":"id","short_names":["id"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1574,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED NEW","unified":"1F195","non_qualified":null,"docomo":"E6DD","au":"E5B5","softbank":"E212","google":"FEB36","image":"1f195.png","sheet_x":0,"sheet_y":25,"short_name":"new","short_names":["new"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1576,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED NG","unified":"1F196","non_qualified":null,"docomo":"E72F","au":null,"softbank":null,"google":"FEB28","image":"1f196.png","sheet_x":0,"sheet_y":26,"short_name":"ng","short_names":["ng"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1577,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED OK","unified":"1F197","non_qualified":null,"docomo":"E70B","au":"E5AD","softbank":"E24D","google":"FEB27","image":"1f197.png","sheet_x":0,"sheet_y":27,"short_name":"ok","short_names":["ok"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1579,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED SOS","unified":"1F198","non_qualified":null,"docomo":null,"au":"E4E8","softbank":null,"google":"FEB4F","image":"1f198.png","sheet_x":0,"sheet_y":28,"short_name":"sos","short_names":["sos"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1581,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED UP WITH EXCLAMATION MARK","unified":"1F199","non_qualified":null,"docomo":null,"au":"E50F","softbank":"E213","google":"FEB37","image":"1f199.png","sheet_x":0,"sheet_y":29,"short_name":"up","short_names":["up"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1582,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED VS","unified":"1F19A","non_qualified":null,"docomo":null,"au":"E5D2","softbank":"E12E","google":"FEB32","image":"1f19a.png","sheet_x":0,"sheet_y":30,"short_name":"vs","short_names":["vs"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1583,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Ascension Island Flag","unified":"1F1E6-1F1E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1e8.png","sheet_x":0,"sheet_y":31,"short_name":"flag-ac","short_names":["flag-ac"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1643,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Andorra Flag","unified":"1F1E6-1F1E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1e9.png","sheet_x":0,"sheet_y":32,"short_name":"flag-ad","short_names":["flag-ad"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1644,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"United Arab Emirates Flag","unified":"1F1E6-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1ea.png","sheet_x":0,"sheet_y":33,"short_name":"flag-ae","short_names":["flag-ae"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1645,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Afghanistan Flag","unified":"1F1E6-1F1EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1eb.png","sheet_x":0,"sheet_y":34,"short_name":"flag-af","short_names":["flag-af"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1646,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Antigua & Barbuda Flag","unified":"1F1E6-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1ec.png","sheet_x":0,"sheet_y":35,"short_name":"flag-ag","short_names":["flag-ag"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1647,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Anguilla Flag","unified":"1F1E6-1F1EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1ee.png","sheet_x":0,"sheet_y":36,"short_name":"flag-ai","short_names":["flag-ai"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1648,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Albania Flag","unified":"1F1E6-1F1F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f1.png","sheet_x":0,"sheet_y":37,"short_name":"flag-al","short_names":["flag-al"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1649,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Armenia Flag","unified":"1F1E6-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f2.png","sheet_x":0,"sheet_y":38,"short_name":"flag-am","short_names":["flag-am"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1650,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Angola Flag","unified":"1F1E6-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f4.png","sheet_x":0,"sheet_y":39,"short_name":"flag-ao","short_names":["flag-ao"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1651,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Antarctica Flag","unified":"1F1E6-1F1F6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f6.png","sheet_x":0,"sheet_y":40,"short_name":"flag-aq","short_names":["flag-aq"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1652,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Argentina Flag","unified":"1F1E6-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f7.png","sheet_x":0,"sheet_y":41,"short_name":"flag-ar","short_names":["flag-ar"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1653,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"American Samoa Flag","unified":"1F1E6-1F1F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f8.png","sheet_x":0,"sheet_y":42,"short_name":"flag-as","short_names":["flag-as"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1654,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Austria Flag","unified":"1F1E6-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f9.png","sheet_x":0,"sheet_y":43,"short_name":"flag-at","short_names":["flag-at"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1655,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Australia Flag","unified":"1F1E6-1F1FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1fa.png","sheet_x":0,"sheet_y":44,"short_name":"flag-au","short_names":["flag-au"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1656,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Aruba Flag","unified":"1F1E6-1F1FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1fc.png","sheet_x":0,"sheet_y":45,"short_name":"flag-aw","short_names":["flag-aw"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1657,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"\u00c5land Islands Flag","unified":"1F1E6-1F1FD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1fd.png","sheet_x":0,"sheet_y":46,"short_name":"flag-ax","short_names":["flag-ax"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1658,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Azerbaijan Flag","unified":"1F1E6-1F1FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1ff.png","sheet_x":0,"sheet_y":47,"short_name":"flag-az","short_names":["flag-az"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1659,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Bosnia & Herzegovina Flag","unified":"1F1E7-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1e6.png","sheet_x":0,"sheet_y":48,"short_name":"flag-ba","short_names":["flag-ba"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1660,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Barbados Flag","unified":"1F1E7-1F1E7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1e7.png","sheet_x":0,"sheet_y":49,"short_name":"flag-bb","short_names":["flag-bb"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1661,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Bangladesh Flag","unified":"1F1E7-1F1E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1e9.png","sheet_x":0,"sheet_y":50,"short_name":"flag-bd","short_names":["flag-bd"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1662,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Belgium Flag","unified":"1F1E7-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ea.png","sheet_x":0,"sheet_y":51,"short_name":"flag-be","short_names":["flag-be"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1663,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Burkina Faso Flag","unified":"1F1E7-1F1EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1eb.png","sheet_x":0,"sheet_y":52,"short_name":"flag-bf","short_names":["flag-bf"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1664,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Bulgaria Flag","unified":"1F1E7-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ec.png","sheet_x":0,"sheet_y":53,"short_name":"flag-bg","short_names":["flag-bg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1665,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Bahrain Flag","unified":"1F1E7-1F1ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ed.png","sheet_x":0,"sheet_y":54,"short_name":"flag-bh","short_names":["flag-bh"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1666,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Burundi Flag","unified":"1F1E7-1F1EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ee.png","sheet_x":0,"sheet_y":55,"short_name":"flag-bi","short_names":["flag-bi"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1667,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Benin Flag","unified":"1F1E7-1F1EF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ef.png","sheet_x":0,"sheet_y":56,"short_name":"flag-bj","short_names":["flag-bj"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1668,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"St. Barth\u00e9lemy Flag","unified":"1F1E7-1F1F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f1.png","sheet_x":0,"sheet_y":57,"short_name":"flag-bl","short_names":["flag-bl"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1669,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Bermuda Flag","unified":"1F1E7-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f2.png","sheet_x":0,"sheet_y":58,"short_name":"flag-bm","short_names":["flag-bm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1670,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Brunei Flag","unified":"1F1E7-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f3.png","sheet_x":0,"sheet_y":59,"short_name":"flag-bn","short_names":["flag-bn"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1671,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Bolivia Flag","unified":"1F1E7-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f4.png","sheet_x":0,"sheet_y":60,"short_name":"flag-bo","short_names":["flag-bo"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1672,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Caribbean Netherlands Flag","unified":"1F1E7-1F1F6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f6.png","sheet_x":0,"sheet_y":61,"short_name":"flag-bq","short_names":["flag-bq"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1673,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Brazil Flag","unified":"1F1E7-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f7.png","sheet_x":1,"sheet_y":0,"short_name":"flag-br","short_names":["flag-br"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1674,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Bahamas Flag","unified":"1F1E7-1F1F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f8.png","sheet_x":1,"sheet_y":1,"short_name":"flag-bs","short_names":["flag-bs"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1675,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Bhutan Flag","unified":"1F1E7-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f9.png","sheet_x":1,"sheet_y":2,"short_name":"flag-bt","short_names":["flag-bt"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1676,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Bouvet Island Flag","unified":"1F1E7-1F1FB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1fb.png","sheet_x":1,"sheet_y":3,"short_name":"flag-bv","short_names":["flag-bv"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1677,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Botswana Flag","unified":"1F1E7-1F1FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1fc.png","sheet_x":1,"sheet_y":4,"short_name":"flag-bw","short_names":["flag-bw"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1678,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Belarus Flag","unified":"1F1E7-1F1FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1fe.png","sheet_x":1,"sheet_y":5,"short_name":"flag-by","short_names":["flag-by"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1679,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Belize Flag","unified":"1F1E7-1F1FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ff.png","sheet_x":1,"sheet_y":6,"short_name":"flag-bz","short_names":["flag-bz"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1680,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Canada Flag","unified":"1F1E8-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1e6.png","sheet_x":1,"sheet_y":7,"short_name":"flag-ca","short_names":["flag-ca"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1681,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Cocos (Keeling) Islands Flag","unified":"1F1E8-1F1E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1e8.png","sheet_x":1,"sheet_y":8,"short_name":"flag-cc","short_names":["flag-cc"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1682,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Congo - Kinshasa Flag","unified":"1F1E8-1F1E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1e9.png","sheet_x":1,"sheet_y":9,"short_name":"flag-cd","short_names":["flag-cd"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1683,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Central African Republic Flag","unified":"1F1E8-1F1EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1eb.png","sheet_x":1,"sheet_y":10,"short_name":"flag-cf","short_names":["flag-cf"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1684,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Congo - Brazzaville Flag","unified":"1F1E8-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1ec.png","sheet_x":1,"sheet_y":11,"short_name":"flag-cg","short_names":["flag-cg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1685,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Switzerland Flag","unified":"1F1E8-1F1ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1ed.png","sheet_x":1,"sheet_y":12,"short_name":"flag-ch","short_names":["flag-ch"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1686,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"C\u00f4te d\u2019Ivoire Flag","unified":"1F1E8-1F1EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1ee.png","sheet_x":1,"sheet_y":13,"short_name":"flag-ci","short_names":["flag-ci"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1687,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Cook Islands Flag","unified":"1F1E8-1F1F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f0.png","sheet_x":1,"sheet_y":14,"short_name":"flag-ck","short_names":["flag-ck"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1688,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Chile Flag","unified":"1F1E8-1F1F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f1.png","sheet_x":1,"sheet_y":15,"short_name":"flag-cl","short_names":["flag-cl"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1689,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Cameroon Flag","unified":"1F1E8-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f2.png","sheet_x":1,"sheet_y":16,"short_name":"flag-cm","short_names":["flag-cm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1690,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"China Flag","unified":"1F1E8-1F1F3","non_qualified":null,"docomo":null,"au":"EB11","softbank":"E513","google":"FE4ED","image":"1f1e8-1f1f3.png","sheet_x":1,"sheet_y":17,"short_name":"cn","short_names":["cn","flag-cn"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1691,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Colombia Flag","unified":"1F1E8-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f4.png","sheet_x":1,"sheet_y":18,"short_name":"flag-co","short_names":["flag-co"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1692,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Clipperton Island Flag","unified":"1F1E8-1F1F5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f5.png","sheet_x":1,"sheet_y":19,"short_name":"flag-cp","short_names":["flag-cp"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1693,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Costa Rica Flag","unified":"1F1E8-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f7.png","sheet_x":1,"sheet_y":20,"short_name":"flag-cr","short_names":["flag-cr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1694,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Cuba Flag","unified":"1F1E8-1F1FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1fa.png","sheet_x":1,"sheet_y":21,"short_name":"flag-cu","short_names":["flag-cu"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1695,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Cape Verde Flag","unified":"1F1E8-1F1FB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1fb.png","sheet_x":1,"sheet_y":22,"short_name":"flag-cv","short_names":["flag-cv"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1696,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Cura\u00e7ao Flag","unified":"1F1E8-1F1FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1fc.png","sheet_x":1,"sheet_y":23,"short_name":"flag-cw","short_names":["flag-cw"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1697,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Christmas Island Flag","unified":"1F1E8-1F1FD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1fd.png","sheet_x":1,"sheet_y":24,"short_name":"flag-cx","short_names":["flag-cx"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1698,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Cyprus Flag","unified":"1F1E8-1F1FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1fe.png","sheet_x":1,"sheet_y":25,"short_name":"flag-cy","short_names":["flag-cy"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1699,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Czechia Flag","unified":"1F1E8-1F1FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1ff.png","sheet_x":1,"sheet_y":26,"short_name":"flag-cz","short_names":["flag-cz"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1700,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Germany Flag","unified":"1F1E9-1F1EA","non_qualified":null,"docomo":null,"au":"EB0E","softbank":"E50E","google":"FE4E8","image":"1f1e9-1f1ea.png","sheet_x":1,"sheet_y":27,"short_name":"de","short_names":["de","flag-de"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1701,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Diego Garcia Flag","unified":"1F1E9-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1ec.png","sheet_x":1,"sheet_y":28,"short_name":"flag-dg","short_names":["flag-dg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1702,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Djibouti Flag","unified":"1F1E9-1F1EF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1ef.png","sheet_x":1,"sheet_y":29,"short_name":"flag-dj","short_names":["flag-dj"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1703,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Denmark Flag","unified":"1F1E9-1F1F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1f0.png","sheet_x":1,"sheet_y":30,"short_name":"flag-dk","short_names":["flag-dk"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1704,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Dominica Flag","unified":"1F1E9-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1f2.png","sheet_x":1,"sheet_y":31,"short_name":"flag-dm","short_names":["flag-dm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1705,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Dominican Republic Flag","unified":"1F1E9-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1f4.png","sheet_x":1,"sheet_y":32,"short_name":"flag-do","short_names":["flag-do"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1706,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Algeria Flag","unified":"1F1E9-1F1FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1ff.png","sheet_x":1,"sheet_y":33,"short_name":"flag-dz","short_names":["flag-dz"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1707,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Ceuta & Melilla Flag","unified":"1F1EA-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1e6.png","sheet_x":1,"sheet_y":34,"short_name":"flag-ea","short_names":["flag-ea"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1708,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Ecuador Flag","unified":"1F1EA-1F1E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1e8.png","sheet_x":1,"sheet_y":35,"short_name":"flag-ec","short_names":["flag-ec"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1709,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Estonia Flag","unified":"1F1EA-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1ea.png","sheet_x":1,"sheet_y":36,"short_name":"flag-ee","short_names":["flag-ee"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1710,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Egypt Flag","unified":"1F1EA-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1ec.png","sheet_x":1,"sheet_y":37,"short_name":"flag-eg","short_names":["flag-eg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1711,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Western Sahara Flag","unified":"1F1EA-1F1ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1ed.png","sheet_x":1,"sheet_y":38,"short_name":"flag-eh","short_names":["flag-eh"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1712,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Eritrea Flag","unified":"1F1EA-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1f7.png","sheet_x":1,"sheet_y":39,"short_name":"flag-er","short_names":["flag-er"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1713,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Spain Flag","unified":"1F1EA-1F1F8","non_qualified":null,"docomo":null,"au":"E5D5","softbank":"E511","google":"FE4EB","image":"1f1ea-1f1f8.png","sheet_x":1,"sheet_y":40,"short_name":"es","short_names":["es","flag-es"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1714,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Ethiopia Flag","unified":"1F1EA-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1f9.png","sheet_x":1,"sheet_y":41,"short_name":"flag-et","short_names":["flag-et"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1715,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"European Union Flag","unified":"1F1EA-1F1FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1fa.png","sheet_x":1,"sheet_y":42,"short_name":"flag-eu","short_names":["flag-eu"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1716,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Finland Flag","unified":"1F1EB-1F1EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1eb-1f1ee.png","sheet_x":1,"sheet_y":43,"short_name":"flag-fi","short_names":["flag-fi"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1717,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Fiji Flag","unified":"1F1EB-1F1EF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1eb-1f1ef.png","sheet_x":1,"sheet_y":44,"short_name":"flag-fj","short_names":["flag-fj"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1718,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Falkland Islands Flag","unified":"1F1EB-1F1F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1eb-1f1f0.png","sheet_x":1,"sheet_y":45,"short_name":"flag-fk","short_names":["flag-fk"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1719,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Micronesia Flag","unified":"1F1EB-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1eb-1f1f2.png","sheet_x":1,"sheet_y":46,"short_name":"flag-fm","short_names":["flag-fm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1720,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Faroe Islands Flag","unified":"1F1EB-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1eb-1f1f4.png","sheet_x":1,"sheet_y":47,"short_name":"flag-fo","short_names":["flag-fo"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1721,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"France Flag","unified":"1F1EB-1F1F7","non_qualified":null,"docomo":null,"au":"EAFA","softbank":"E50D","google":"FE4E7","image":"1f1eb-1f1f7.png","sheet_x":1,"sheet_y":48,"short_name":"fr","short_names":["fr","flag-fr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1722,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Gabon Flag","unified":"1F1EC-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1e6.png","sheet_x":1,"sheet_y":49,"short_name":"flag-ga","short_names":["flag-ga"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1723,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"United Kingdom Flag","unified":"1F1EC-1F1E7","non_qualified":null,"docomo":null,"au":"EB10","softbank":"E510","google":"FE4EA","image":"1f1ec-1f1e7.png","sheet_x":1,"sheet_y":50,"short_name":"gb","short_names":["gb","uk","flag-gb"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1724,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Grenada Flag","unified":"1F1EC-1F1E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1e9.png","sheet_x":1,"sheet_y":51,"short_name":"flag-gd","short_names":["flag-gd"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1725,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Georgia Flag","unified":"1F1EC-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1ea.png","sheet_x":1,"sheet_y":52,"short_name":"flag-ge","short_names":["flag-ge"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1726,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"French Guiana Flag","unified":"1F1EC-1F1EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1eb.png","sheet_x":1,"sheet_y":53,"short_name":"flag-gf","short_names":["flag-gf"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1727,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Guernsey Flag","unified":"1F1EC-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1ec.png","sheet_x":1,"sheet_y":54,"short_name":"flag-gg","short_names":["flag-gg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1728,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Ghana Flag","unified":"1F1EC-1F1ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1ed.png","sheet_x":1,"sheet_y":55,"short_name":"flag-gh","short_names":["flag-gh"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1729,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Gibraltar Flag","unified":"1F1EC-1F1EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1ee.png","sheet_x":1,"sheet_y":56,"short_name":"flag-gi","short_names":["flag-gi"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1730,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Greenland Flag","unified":"1F1EC-1F1F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f1.png","sheet_x":1,"sheet_y":57,"short_name":"flag-gl","short_names":["flag-gl"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1731,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Gambia Flag","unified":"1F1EC-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f2.png","sheet_x":1,"sheet_y":58,"short_name":"flag-gm","short_names":["flag-gm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1732,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Guinea Flag","unified":"1F1EC-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f3.png","sheet_x":1,"sheet_y":59,"short_name":"flag-gn","short_names":["flag-gn"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1733,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Guadeloupe Flag","unified":"1F1EC-1F1F5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f5.png","sheet_x":1,"sheet_y":60,"short_name":"flag-gp","short_names":["flag-gp"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1734,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Equatorial Guinea Flag","unified":"1F1EC-1F1F6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f6.png","sheet_x":1,"sheet_y":61,"short_name":"flag-gq","short_names":["flag-gq"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1735,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Greece Flag","unified":"1F1EC-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f7.png","sheet_x":2,"sheet_y":0,"short_name":"flag-gr","short_names":["flag-gr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1736,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"South Georgia & South Sandwich Islands Flag","unified":"1F1EC-1F1F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f8.png","sheet_x":2,"sheet_y":1,"short_name":"flag-gs","short_names":["flag-gs"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1737,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Guatemala Flag","unified":"1F1EC-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f9.png","sheet_x":2,"sheet_y":2,"short_name":"flag-gt","short_names":["flag-gt"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1738,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Guam Flag","unified":"1F1EC-1F1FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1fa.png","sheet_x":2,"sheet_y":3,"short_name":"flag-gu","short_names":["flag-gu"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1739,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Guinea-Bissau Flag","unified":"1F1EC-1F1FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1fc.png","sheet_x":2,"sheet_y":4,"short_name":"flag-gw","short_names":["flag-gw"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1740,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Guyana Flag","unified":"1F1EC-1F1FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1fe.png","sheet_x":2,"sheet_y":5,"short_name":"flag-gy","short_names":["flag-gy"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1741,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Hong Kong SAR China Flag","unified":"1F1ED-1F1F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1f0.png","sheet_x":2,"sheet_y":6,"short_name":"flag-hk","short_names":["flag-hk"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1742,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Heard & McDonald Islands Flag","unified":"1F1ED-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1f2.png","sheet_x":2,"sheet_y":7,"short_name":"flag-hm","short_names":["flag-hm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1743,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Honduras Flag","unified":"1F1ED-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1f3.png","sheet_x":2,"sheet_y":8,"short_name":"flag-hn","short_names":["flag-hn"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1744,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Croatia Flag","unified":"1F1ED-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1f7.png","sheet_x":2,"sheet_y":9,"short_name":"flag-hr","short_names":["flag-hr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1745,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Haiti Flag","unified":"1F1ED-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1f9.png","sheet_x":2,"sheet_y":10,"short_name":"flag-ht","short_names":["flag-ht"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1746,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Hungary Flag","unified":"1F1ED-1F1FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1fa.png","sheet_x":2,"sheet_y":11,"short_name":"flag-hu","short_names":["flag-hu"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1747,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Canary Islands Flag","unified":"1F1EE-1F1E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1e8.png","sheet_x":2,"sheet_y":12,"short_name":"flag-ic","short_names":["flag-ic"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1748,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Indonesia Flag","unified":"1F1EE-1F1E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1e9.png","sheet_x":2,"sheet_y":13,"short_name":"flag-id","short_names":["flag-id"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1749,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Ireland Flag","unified":"1F1EE-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1ea.png","sheet_x":2,"sheet_y":14,"short_name":"flag-ie","short_names":["flag-ie"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1750,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Israel Flag","unified":"1F1EE-1F1F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f1.png","sheet_x":2,"sheet_y":15,"short_name":"flag-il","short_names":["flag-il"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1751,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Isle of Man Flag","unified":"1F1EE-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f2.png","sheet_x":2,"sheet_y":16,"short_name":"flag-im","short_names":["flag-im"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1752,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"India Flag","unified":"1F1EE-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f3.png","sheet_x":2,"sheet_y":17,"short_name":"flag-in","short_names":["flag-in"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1753,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"British Indian Ocean Territory Flag","unified":"1F1EE-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f4.png","sheet_x":2,"sheet_y":18,"short_name":"flag-io","short_names":["flag-io"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1754,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Iraq Flag","unified":"1F1EE-1F1F6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f6.png","sheet_x":2,"sheet_y":19,"short_name":"flag-iq","short_names":["flag-iq"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1755,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Iran Flag","unified":"1F1EE-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f7.png","sheet_x":2,"sheet_y":20,"short_name":"flag-ir","short_names":["flag-ir"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1756,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Iceland Flag","unified":"1F1EE-1F1F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f8.png","sheet_x":2,"sheet_y":21,"short_name":"flag-is","short_names":["flag-is"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1757,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Italy Flag","unified":"1F1EE-1F1F9","non_qualified":null,"docomo":null,"au":"EB0F","softbank":"E50F","google":"FE4E9","image":"1f1ee-1f1f9.png","sheet_x":2,"sheet_y":22,"short_name":"it","short_names":["it","flag-it"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1758,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Jersey Flag","unified":"1F1EF-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ef-1f1ea.png","sheet_x":2,"sheet_y":23,"short_name":"flag-je","short_names":["flag-je"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1759,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Jamaica Flag","unified":"1F1EF-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ef-1f1f2.png","sheet_x":2,"sheet_y":24,"short_name":"flag-jm","short_names":["flag-jm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1760,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Jordan Flag","unified":"1F1EF-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ef-1f1f4.png","sheet_x":2,"sheet_y":25,"short_name":"flag-jo","short_names":["flag-jo"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1761,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Japan Flag","unified":"1F1EF-1F1F5","non_qualified":null,"docomo":null,"au":"E4CC","softbank":"E50B","google":"FE4E5","image":"1f1ef-1f1f5.png","sheet_x":2,"sheet_y":26,"short_name":"jp","short_names":["jp","flag-jp"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1762,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Kenya Flag","unified":"1F1F0-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1ea.png","sheet_x":2,"sheet_y":27,"short_name":"flag-ke","short_names":["flag-ke"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1763,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Kyrgyzstan Flag","unified":"1F1F0-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1ec.png","sheet_x":2,"sheet_y":28,"short_name":"flag-kg","short_names":["flag-kg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1764,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Cambodia Flag","unified":"1F1F0-1F1ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1ed.png","sheet_x":2,"sheet_y":29,"short_name":"flag-kh","short_names":["flag-kh"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1765,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Kiribati Flag","unified":"1F1F0-1F1EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1ee.png","sheet_x":2,"sheet_y":30,"short_name":"flag-ki","short_names":["flag-ki"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1766,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Comoros Flag","unified":"1F1F0-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1f2.png","sheet_x":2,"sheet_y":31,"short_name":"flag-km","short_names":["flag-km"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1767,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"St. Kitts & Nevis Flag","unified":"1F1F0-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1f3.png","sheet_x":2,"sheet_y":32,"short_name":"flag-kn","short_names":["flag-kn"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1768,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"North Korea Flag","unified":"1F1F0-1F1F5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1f5.png","sheet_x":2,"sheet_y":33,"short_name":"flag-kp","short_names":["flag-kp"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1769,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"South Korea Flag","unified":"1F1F0-1F1F7","non_qualified":null,"docomo":null,"au":"EB12","softbank":"E514","google":"FE4EE","image":"1f1f0-1f1f7.png","sheet_x":2,"sheet_y":34,"short_name":"kr","short_names":["kr","flag-kr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1770,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Kuwait Flag","unified":"1F1F0-1F1FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1fc.png","sheet_x":2,"sheet_y":35,"short_name":"flag-kw","short_names":["flag-kw"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1771,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Cayman Islands Flag","unified":"1F1F0-1F1FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1fe.png","sheet_x":2,"sheet_y":36,"short_name":"flag-ky","short_names":["flag-ky"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1772,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Kazakhstan Flag","unified":"1F1F0-1F1FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1ff.png","sheet_x":2,"sheet_y":37,"short_name":"flag-kz","short_names":["flag-kz"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1773,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Laos Flag","unified":"1F1F1-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1e6.png","sheet_x":2,"sheet_y":38,"short_name":"flag-la","short_names":["flag-la"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1774,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Lebanon Flag","unified":"1F1F1-1F1E7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1e7.png","sheet_x":2,"sheet_y":39,"short_name":"flag-lb","short_names":["flag-lb"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1775,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"St. Lucia Flag","unified":"1F1F1-1F1E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1e8.png","sheet_x":2,"sheet_y":40,"short_name":"flag-lc","short_names":["flag-lc"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1776,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Liechtenstein Flag","unified":"1F1F1-1F1EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1ee.png","sheet_x":2,"sheet_y":41,"short_name":"flag-li","short_names":["flag-li"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1777,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Sri Lanka Flag","unified":"1F1F1-1F1F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1f0.png","sheet_x":2,"sheet_y":42,"short_name":"flag-lk","short_names":["flag-lk"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1778,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Liberia Flag","unified":"1F1F1-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1f7.png","sheet_x":2,"sheet_y":43,"short_name":"flag-lr","short_names":["flag-lr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1779,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Lesotho Flag","unified":"1F1F1-1F1F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1f8.png","sheet_x":2,"sheet_y":44,"short_name":"flag-ls","short_names":["flag-ls"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1780,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Lithuania Flag","unified":"1F1F1-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1f9.png","sheet_x":2,"sheet_y":45,"short_name":"flag-lt","short_names":["flag-lt"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1781,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Luxembourg Flag","unified":"1F1F1-1F1FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1fa.png","sheet_x":2,"sheet_y":46,"short_name":"flag-lu","short_names":["flag-lu"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1782,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Latvia Flag","unified":"1F1F1-1F1FB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1fb.png","sheet_x":2,"sheet_y":47,"short_name":"flag-lv","short_names":["flag-lv"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1783,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Libya Flag","unified":"1F1F1-1F1FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1fe.png","sheet_x":2,"sheet_y":48,"short_name":"flag-ly","short_names":["flag-ly"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1784,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Morocco Flag","unified":"1F1F2-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1e6.png","sheet_x":2,"sheet_y":49,"short_name":"flag-ma","short_names":["flag-ma"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1785,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Monaco Flag","unified":"1F1F2-1F1E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1e8.png","sheet_x":2,"sheet_y":50,"short_name":"flag-mc","short_names":["flag-mc"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1786,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Moldova Flag","unified":"1F1F2-1F1E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1e9.png","sheet_x":2,"sheet_y":51,"short_name":"flag-md","short_names":["flag-md"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1787,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Montenegro Flag","unified":"1F1F2-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1ea.png","sheet_x":2,"sheet_y":52,"short_name":"flag-me","short_names":["flag-me"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1788,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"St. Martin Flag","unified":"1F1F2-1F1EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1eb.png","sheet_x":2,"sheet_y":53,"short_name":"flag-mf","short_names":["flag-mf"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1789,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Madagascar Flag","unified":"1F1F2-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1ec.png","sheet_x":2,"sheet_y":54,"short_name":"flag-mg","short_names":["flag-mg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1790,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Marshall Islands Flag","unified":"1F1F2-1F1ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1ed.png","sheet_x":2,"sheet_y":55,"short_name":"flag-mh","short_names":["flag-mh"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1791,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"North Macedonia Flag","unified":"1F1F2-1F1F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f0.png","sheet_x":2,"sheet_y":56,"short_name":"flag-mk","short_names":["flag-mk"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1792,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Mali Flag","unified":"1F1F2-1F1F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f1.png","sheet_x":2,"sheet_y":57,"short_name":"flag-ml","short_names":["flag-ml"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1793,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Myanmar (Burma) Flag","unified":"1F1F2-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f2.png","sheet_x":2,"sheet_y":58,"short_name":"flag-mm","short_names":["flag-mm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1794,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Mongolia Flag","unified":"1F1F2-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f3.png","sheet_x":2,"sheet_y":59,"short_name":"flag-mn","short_names":["flag-mn"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1795,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Macao SAR China Flag","unified":"1F1F2-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f4.png","sheet_x":2,"sheet_y":60,"short_name":"flag-mo","short_names":["flag-mo"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1796,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Northern Mariana Islands Flag","unified":"1F1F2-1F1F5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f5.png","sheet_x":2,"sheet_y":61,"short_name":"flag-mp","short_names":["flag-mp"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1797,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Martinique Flag","unified":"1F1F2-1F1F6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f6.png","sheet_x":3,"sheet_y":0,"short_name":"flag-mq","short_names":["flag-mq"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1798,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Mauritania Flag","unified":"1F1F2-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f7.png","sheet_x":3,"sheet_y":1,"short_name":"flag-mr","short_names":["flag-mr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1799,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Montserrat Flag","unified":"1F1F2-1F1F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f8.png","sheet_x":3,"sheet_y":2,"short_name":"flag-ms","short_names":["flag-ms"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1800,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Malta Flag","unified":"1F1F2-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f9.png","sheet_x":3,"sheet_y":3,"short_name":"flag-mt","short_names":["flag-mt"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1801,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Mauritius Flag","unified":"1F1F2-1F1FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1fa.png","sheet_x":3,"sheet_y":4,"short_name":"flag-mu","short_names":["flag-mu"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1802,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Maldives Flag","unified":"1F1F2-1F1FB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1fb.png","sheet_x":3,"sheet_y":5,"short_name":"flag-mv","short_names":["flag-mv"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1803,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Malawi Flag","unified":"1F1F2-1F1FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1fc.png","sheet_x":3,"sheet_y":6,"short_name":"flag-mw","short_names":["flag-mw"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1804,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Mexico Flag","unified":"1F1F2-1F1FD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1fd.png","sheet_x":3,"sheet_y":7,"short_name":"flag-mx","short_names":["flag-mx"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1805,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Malaysia Flag","unified":"1F1F2-1F1FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1fe.png","sheet_x":3,"sheet_y":8,"short_name":"flag-my","short_names":["flag-my"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1806,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Mozambique Flag","unified":"1F1F2-1F1FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1ff.png","sheet_x":3,"sheet_y":9,"short_name":"flag-mz","short_names":["flag-mz"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1807,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Namibia Flag","unified":"1F1F3-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1e6.png","sheet_x":3,"sheet_y":10,"short_name":"flag-na","short_names":["flag-na"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1808,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"New Caledonia Flag","unified":"1F1F3-1F1E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1e8.png","sheet_x":3,"sheet_y":11,"short_name":"flag-nc","short_names":["flag-nc"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1809,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Niger Flag","unified":"1F1F3-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1ea.png","sheet_x":3,"sheet_y":12,"short_name":"flag-ne","short_names":["flag-ne"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1810,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Norfolk Island Flag","unified":"1F1F3-1F1EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1eb.png","sheet_x":3,"sheet_y":13,"short_name":"flag-nf","short_names":["flag-nf"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1811,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Nigeria Flag","unified":"1F1F3-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1ec.png","sheet_x":3,"sheet_y":14,"short_name":"flag-ng","short_names":["flag-ng"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1812,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Nicaragua Flag","unified":"1F1F3-1F1EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1ee.png","sheet_x":3,"sheet_y":15,"short_name":"flag-ni","short_names":["flag-ni"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1813,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Netherlands Flag","unified":"1F1F3-1F1F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1f1.png","sheet_x":3,"sheet_y":16,"short_name":"flag-nl","short_names":["flag-nl"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1814,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Norway Flag","unified":"1F1F3-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1f4.png","sheet_x":3,"sheet_y":17,"short_name":"flag-no","short_names":["flag-no"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1815,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Nepal Flag","unified":"1F1F3-1F1F5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1f5.png","sheet_x":3,"sheet_y":18,"short_name":"flag-np","short_names":["flag-np"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1816,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Nauru Flag","unified":"1F1F3-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1f7.png","sheet_x":3,"sheet_y":19,"short_name":"flag-nr","short_names":["flag-nr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1817,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Niue Flag","unified":"1F1F3-1F1FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1fa.png","sheet_x":3,"sheet_y":20,"short_name":"flag-nu","short_names":["flag-nu"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1818,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"New Zealand Flag","unified":"1F1F3-1F1FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1ff.png","sheet_x":3,"sheet_y":21,"short_name":"flag-nz","short_names":["flag-nz"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1819,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Oman Flag","unified":"1F1F4-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f4-1f1f2.png","sheet_x":3,"sheet_y":22,"short_name":"flag-om","short_names":["flag-om"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1820,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Panama Flag","unified":"1F1F5-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1e6.png","sheet_x":3,"sheet_y":23,"short_name":"flag-pa","short_names":["flag-pa"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1821,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Peru Flag","unified":"1F1F5-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1ea.png","sheet_x":3,"sheet_y":24,"short_name":"flag-pe","short_names":["flag-pe"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1822,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"French Polynesia Flag","unified":"1F1F5-1F1EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1eb.png","sheet_x":3,"sheet_y":25,"short_name":"flag-pf","short_names":["flag-pf"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1823,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Papua New Guinea Flag","unified":"1F1F5-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1ec.png","sheet_x":3,"sheet_y":26,"short_name":"flag-pg","short_names":["flag-pg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1824,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Philippines Flag","unified":"1F1F5-1F1ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1ed.png","sheet_x":3,"sheet_y":27,"short_name":"flag-ph","short_names":["flag-ph"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1825,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Pakistan Flag","unified":"1F1F5-1F1F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f0.png","sheet_x":3,"sheet_y":28,"short_name":"flag-pk","short_names":["flag-pk"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1826,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Poland Flag","unified":"1F1F5-1F1F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f1.png","sheet_x":3,"sheet_y":29,"short_name":"flag-pl","short_names":["flag-pl"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1827,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"St. Pierre & Miquelon Flag","unified":"1F1F5-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f2.png","sheet_x":3,"sheet_y":30,"short_name":"flag-pm","short_names":["flag-pm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1828,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Pitcairn Islands Flag","unified":"1F1F5-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f3.png","sheet_x":3,"sheet_y":31,"short_name":"flag-pn","short_names":["flag-pn"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1829,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Puerto Rico Flag","unified":"1F1F5-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f7.png","sheet_x":3,"sheet_y":32,"short_name":"flag-pr","short_names":["flag-pr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1830,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Palestinian Territories Flag","unified":"1F1F5-1F1F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f8.png","sheet_x":3,"sheet_y":33,"short_name":"flag-ps","short_names":["flag-ps"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1831,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Portugal Flag","unified":"1F1F5-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f9.png","sheet_x":3,"sheet_y":34,"short_name":"flag-pt","short_names":["flag-pt"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1832,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Palau Flag","unified":"1F1F5-1F1FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1fc.png","sheet_x":3,"sheet_y":35,"short_name":"flag-pw","short_names":["flag-pw"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1833,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Paraguay Flag","unified":"1F1F5-1F1FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1fe.png","sheet_x":3,"sheet_y":36,"short_name":"flag-py","short_names":["flag-py"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1834,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Qatar Flag","unified":"1F1F6-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f6-1f1e6.png","sheet_x":3,"sheet_y":37,"short_name":"flag-qa","short_names":["flag-qa"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1835,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"R\u00e9union Flag","unified":"1F1F7-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f7-1f1ea.png","sheet_x":3,"sheet_y":38,"short_name":"flag-re","short_names":["flag-re"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1836,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Romania Flag","unified":"1F1F7-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f7-1f1f4.png","sheet_x":3,"sheet_y":39,"short_name":"flag-ro","short_names":["flag-ro"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1837,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Serbia Flag","unified":"1F1F7-1F1F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f7-1f1f8.png","sheet_x":3,"sheet_y":40,"short_name":"flag-rs","short_names":["flag-rs"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1838,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Russia Flag","unified":"1F1F7-1F1FA","non_qualified":null,"docomo":null,"au":"E5D6","softbank":"E512","google":"FE4EC","image":"1f1f7-1f1fa.png","sheet_x":3,"sheet_y":41,"short_name":"ru","short_names":["ru","flag-ru"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1839,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Rwanda Flag","unified":"1F1F7-1F1FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f7-1f1fc.png","sheet_x":3,"sheet_y":42,"short_name":"flag-rw","short_names":["flag-rw"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1840,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Saudi Arabia Flag","unified":"1F1F8-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1e6.png","sheet_x":3,"sheet_y":43,"short_name":"flag-sa","short_names":["flag-sa"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1841,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Solomon Islands Flag","unified":"1F1F8-1F1E7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1e7.png","sheet_x":3,"sheet_y":44,"short_name":"flag-sb","short_names":["flag-sb"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1842,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Seychelles Flag","unified":"1F1F8-1F1E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1e8.png","sheet_x":3,"sheet_y":45,"short_name":"flag-sc","short_names":["flag-sc"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1843,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Sudan Flag","unified":"1F1F8-1F1E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1e9.png","sheet_x":3,"sheet_y":46,"short_name":"flag-sd","short_names":["flag-sd"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1844,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Sweden Flag","unified":"1F1F8-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ea.png","sheet_x":3,"sheet_y":47,"short_name":"flag-se","short_names":["flag-se"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1845,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Singapore Flag","unified":"1F1F8-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ec.png","sheet_x":3,"sheet_y":48,"short_name":"flag-sg","short_names":["flag-sg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1846,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"St. Helena Flag","unified":"1F1F8-1F1ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ed.png","sheet_x":3,"sheet_y":49,"short_name":"flag-sh","short_names":["flag-sh"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1847,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Slovenia Flag","unified":"1F1F8-1F1EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ee.png","sheet_x":3,"sheet_y":50,"short_name":"flag-si","short_names":["flag-si"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1848,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Svalbard & Jan Mayen Flag","unified":"1F1F8-1F1EF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ef.png","sheet_x":3,"sheet_y":51,"short_name":"flag-sj","short_names":["flag-sj"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1849,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Slovakia Flag","unified":"1F1F8-1F1F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f0.png","sheet_x":3,"sheet_y":52,"short_name":"flag-sk","short_names":["flag-sk"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1850,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Sierra Leone Flag","unified":"1F1F8-1F1F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f1.png","sheet_x":3,"sheet_y":53,"short_name":"flag-sl","short_names":["flag-sl"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1851,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"San Marino Flag","unified":"1F1F8-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f2.png","sheet_x":3,"sheet_y":54,"short_name":"flag-sm","short_names":["flag-sm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1852,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Senegal Flag","unified":"1F1F8-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f3.png","sheet_x":3,"sheet_y":55,"short_name":"flag-sn","short_names":["flag-sn"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1853,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Somalia Flag","unified":"1F1F8-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f4.png","sheet_x":3,"sheet_y":56,"short_name":"flag-so","short_names":["flag-so"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1854,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Suriname Flag","unified":"1F1F8-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f7.png","sheet_x":3,"sheet_y":57,"short_name":"flag-sr","short_names":["flag-sr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1855,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"South Sudan Flag","unified":"1F1F8-1F1F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f8.png","sheet_x":3,"sheet_y":58,"short_name":"flag-ss","short_names":["flag-ss"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1856,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"S\u00e3o Tom\u00e9 & Pr\u00edncipe Flag","unified":"1F1F8-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f9.png","sheet_x":3,"sheet_y":59,"short_name":"flag-st","short_names":["flag-st"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1857,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"El Salvador Flag","unified":"1F1F8-1F1FB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1fb.png","sheet_x":3,"sheet_y":60,"short_name":"flag-sv","short_names":["flag-sv"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1858,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Sint Maarten Flag","unified":"1F1F8-1F1FD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1fd.png","sheet_x":3,"sheet_y":61,"short_name":"flag-sx","short_names":["flag-sx"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1859,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Syria Flag","unified":"1F1F8-1F1FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1fe.png","sheet_x":4,"sheet_y":0,"short_name":"flag-sy","short_names":["flag-sy"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1860,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Eswatini Flag","unified":"1F1F8-1F1FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ff.png","sheet_x":4,"sheet_y":1,"short_name":"flag-sz","short_names":["flag-sz"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1861,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Tristan da Cunha Flag","unified":"1F1F9-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1e6.png","sheet_x":4,"sheet_y":2,"short_name":"flag-ta","short_names":["flag-ta"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1862,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Turks & Caicos Islands Flag","unified":"1F1F9-1F1E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1e8.png","sheet_x":4,"sheet_y":3,"short_name":"flag-tc","short_names":["flag-tc"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1863,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Chad Flag","unified":"1F1F9-1F1E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1e9.png","sheet_x":4,"sheet_y":4,"short_name":"flag-td","short_names":["flag-td"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1864,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"French Southern Territories Flag","unified":"1F1F9-1F1EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1eb.png","sheet_x":4,"sheet_y":5,"short_name":"flag-tf","short_names":["flag-tf"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1865,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Togo Flag","unified":"1F1F9-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1ec.png","sheet_x":4,"sheet_y":6,"short_name":"flag-tg","short_names":["flag-tg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1866,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Thailand Flag","unified":"1F1F9-1F1ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1ed.png","sheet_x":4,"sheet_y":7,"short_name":"flag-th","short_names":["flag-th"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1867,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Tajikistan Flag","unified":"1F1F9-1F1EF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1ef.png","sheet_x":4,"sheet_y":8,"short_name":"flag-tj","short_names":["flag-tj"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1868,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Tokelau Flag","unified":"1F1F9-1F1F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f0.png","sheet_x":4,"sheet_y":9,"short_name":"flag-tk","short_names":["flag-tk"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1869,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Timor-Leste Flag","unified":"1F1F9-1F1F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f1.png","sheet_x":4,"sheet_y":10,"short_name":"flag-tl","short_names":["flag-tl"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1870,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Turkmenistan Flag","unified":"1F1F9-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f2.png","sheet_x":4,"sheet_y":11,"short_name":"flag-tm","short_names":["flag-tm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1871,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Tunisia Flag","unified":"1F1F9-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f3.png","sheet_x":4,"sheet_y":12,"short_name":"flag-tn","short_names":["flag-tn"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1872,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Tonga Flag","unified":"1F1F9-1F1F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f4.png","sheet_x":4,"sheet_y":13,"short_name":"flag-to","short_names":["flag-to"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1873,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"T\u00fcrkiye Flag","unified":"1F1F9-1F1F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f7.png","sheet_x":4,"sheet_y":14,"short_name":"flag-tr","short_names":["flag-tr"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1874,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Trinidad & Tobago Flag","unified":"1F1F9-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f9.png","sheet_x":4,"sheet_y":15,"short_name":"flag-tt","short_names":["flag-tt"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1875,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Tuvalu Flag","unified":"1F1F9-1F1FB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1fb.png","sheet_x":4,"sheet_y":16,"short_name":"flag-tv","short_names":["flag-tv"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1876,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Taiwan Flag","unified":"1F1F9-1F1FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1fc.png","sheet_x":4,"sheet_y":17,"short_name":"flag-tw","short_names":["flag-tw"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1877,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Tanzania Flag","unified":"1F1F9-1F1FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1ff.png","sheet_x":4,"sheet_y":18,"short_name":"flag-tz","short_names":["flag-tz"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1878,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Ukraine Flag","unified":"1F1FA-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1e6.png","sheet_x":4,"sheet_y":19,"short_name":"flag-ua","short_names":["flag-ua"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1879,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Uganda Flag","unified":"1F1FA-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1ec.png","sheet_x":4,"sheet_y":20,"short_name":"flag-ug","short_names":["flag-ug"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1880,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"U.S. Outlying Islands Flag","unified":"1F1FA-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1f2.png","sheet_x":4,"sheet_y":21,"short_name":"flag-um","short_names":["flag-um"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1881,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"United Nations Flag","unified":"1F1FA-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1f3.png","sheet_x":4,"sheet_y":22,"short_name":"flag-un","short_names":["flag-un"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1882,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"United States Flag","unified":"1F1FA-1F1F8","non_qualified":null,"docomo":null,"au":"E573","softbank":"E50C","google":"FE4E6","image":"1f1fa-1f1f8.png","sheet_x":4,"sheet_y":23,"short_name":"us","short_names":["us","flag-us"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1883,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Uruguay Flag","unified":"1F1FA-1F1FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1fe.png","sheet_x":4,"sheet_y":24,"short_name":"flag-uy","short_names":["flag-uy"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1884,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Uzbekistan Flag","unified":"1F1FA-1F1FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1ff.png","sheet_x":4,"sheet_y":25,"short_name":"flag-uz","short_names":["flag-uz"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1885,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Vatican City Flag","unified":"1F1FB-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1e6.png","sheet_x":4,"sheet_y":26,"short_name":"flag-va","short_names":["flag-va"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1886,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"St. Vincent & Grenadines Flag","unified":"1F1FB-1F1E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1e8.png","sheet_x":4,"sheet_y":27,"short_name":"flag-vc","short_names":["flag-vc"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1887,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Venezuela Flag","unified":"1F1FB-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1ea.png","sheet_x":4,"sheet_y":28,"short_name":"flag-ve","short_names":["flag-ve"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1888,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"British Virgin Islands Flag","unified":"1F1FB-1F1EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1ec.png","sheet_x":4,"sheet_y":29,"short_name":"flag-vg","short_names":["flag-vg"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1889,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"U.S. Virgin Islands Flag","unified":"1F1FB-1F1EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1ee.png","sheet_x":4,"sheet_y":30,"short_name":"flag-vi","short_names":["flag-vi"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1890,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Vietnam Flag","unified":"1F1FB-1F1F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1f3.png","sheet_x":4,"sheet_y":31,"short_name":"flag-vn","short_names":["flag-vn"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1891,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Vanuatu Flag","unified":"1F1FB-1F1FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1fa.png","sheet_x":4,"sheet_y":32,"short_name":"flag-vu","short_names":["flag-vu"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1892,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Wallis & Futuna Flag","unified":"1F1FC-1F1EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fc-1f1eb.png","sheet_x":4,"sheet_y":33,"short_name":"flag-wf","short_names":["flag-wf"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1893,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Samoa Flag","unified":"1F1FC-1F1F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fc-1f1f8.png","sheet_x":4,"sheet_y":34,"short_name":"flag-ws","short_names":["flag-ws"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1894,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Kosovo Flag","unified":"1F1FD-1F1F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fd-1f1f0.png","sheet_x":4,"sheet_y":35,"short_name":"flag-xk","short_names":["flag-xk"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1895,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Yemen Flag","unified":"1F1FE-1F1EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fe-1f1ea.png","sheet_x":4,"sheet_y":36,"short_name":"flag-ye","short_names":["flag-ye"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1896,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Mayotte Flag","unified":"1F1FE-1F1F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fe-1f1f9.png","sheet_x":4,"sheet_y":37,"short_name":"flag-yt","short_names":["flag-yt"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1897,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"South Africa Flag","unified":"1F1FF-1F1E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ff-1f1e6.png","sheet_x":4,"sheet_y":38,"short_name":"flag-za","short_names":["flag-za"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1898,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Zambia Flag","unified":"1F1FF-1F1F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ff-1f1f2.png","sheet_x":4,"sheet_y":39,"short_name":"flag-zm","short_names":["flag-zm"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1899,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Zimbabwe Flag","unified":"1F1FF-1F1FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ff-1f1fc.png","sheet_x":4,"sheet_y":40,"short_name":"flag-zw","short_names":["flag-zw"],"text":null,"texts":null,"category":"Flags","subcategory":"country-flag","sort_order":1900,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED KATAKANA KOKO","unified":"1F201","non_qualified":null,"docomo":null,"au":null,"softbank":"E203","google":"FEB24","image":"1f201.png","sheet_x":4,"sheet_y":41,"short_name":"koko","short_names":["koko"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1584,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED KATAKANA SA","unified":"1F202-FE0F","non_qualified":"1F202","docomo":null,"au":"EA87","softbank":"E228","google":"FEB3F","image":"1f202-fe0f.png","sheet_x":4,"sheet_y":42,"short_name":"sa","short_names":["sa"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1585,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-7121","unified":"1F21A","non_qualified":null,"docomo":null,"au":null,"softbank":"E216","google":"FEB3A","image":"1f21a.png","sheet_x":4,"sheet_y":43,"short_name":"u7121","short_names":["u7121"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1591,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-6307","unified":"1F22F","non_qualified":null,"docomo":null,"au":"EA8B","softbank":"E22C","google":"FEB40","image":"1f22f.png","sheet_x":4,"sheet_y":44,"short_name":"u6307","short_names":["u6307"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1588,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-7981","unified":"1F232","non_qualified":null,"docomo":"E738","au":null,"softbank":null,"google":"FEB2E","image":"1f232.png","sheet_x":4,"sheet_y":45,"short_name":"u7981","short_names":["u7981"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1592,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-7A7A","unified":"1F233","non_qualified":null,"docomo":"E739","au":"EA8A","softbank":"E22B","google":"FEB2F","image":"1f233.png","sheet_x":4,"sheet_y":46,"short_name":"u7a7a","short_names":["u7a7a"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1596,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-5408","unified":"1F234","non_qualified":null,"docomo":"E73A","au":null,"softbank":null,"google":"FEB30","image":"1f234.png","sheet_x":4,"sheet_y":47,"short_name":"u5408","short_names":["u5408"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1595,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-6E80","unified":"1F235","non_qualified":null,"docomo":"E73B","au":"EA89","softbank":"E22A","google":"FEB31","image":"1f235.png","sheet_x":4,"sheet_y":48,"short_name":"u6e80","short_names":["u6e80"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1600,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-6709","unified":"1F236","non_qualified":null,"docomo":null,"au":null,"softbank":"E215","google":"FEB39","image":"1f236.png","sheet_x":4,"sheet_y":49,"short_name":"u6709","short_names":["u6709"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1587,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-6708","unified":"1F237-FE0F","non_qualified":"1F237","docomo":null,"au":null,"softbank":"E217","google":"FEB3B","image":"1f237-fe0f.png","sheet_x":4,"sheet_y":50,"short_name":"u6708","short_names":["u6708"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1586,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-7533","unified":"1F238","non_qualified":null,"docomo":null,"au":null,"softbank":"E218","google":"FEB3C","image":"1f238.png","sheet_x":4,"sheet_y":51,"short_name":"u7533","short_names":["u7533"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1594,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-5272","unified":"1F239","non_qualified":null,"docomo":null,"au":"EA86","softbank":"E227","google":"FEB3E","image":"1f239.png","sheet_x":4,"sheet_y":52,"short_name":"u5272","short_names":["u5272"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1590,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-55B6","unified":"1F23A","non_qualified":null,"docomo":null,"au":"EA8C","softbank":"E22D","google":"FEB41","image":"1f23a.png","sheet_x":4,"sheet_y":53,"short_name":"u55b6","short_names":["u55b6"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1599,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CIRCLED IDEOGRAPH ADVANTAGE","unified":"1F250","non_qualified":null,"docomo":null,"au":"E4F7","softbank":"E226","google":"FEB3D","image":"1f250.png","sheet_x":4,"sheet_y":54,"short_name":"ideograph_advantage","short_names":["ideograph_advantage"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1589,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CIRCLED IDEOGRAPH ACCEPT","unified":"1F251","non_qualified":null,"docomo":null,"au":"EB01","softbank":null,"google":"FEB50","image":"1f251.png","sheet_x":4,"sheet_y":55,"short_name":"accept","short_names":["accept"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1593,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CYCLONE","unified":"1F300","non_qualified":null,"docomo":"E643","au":"E469","softbank":"E443","google":"FE005","image":"1f300.png","sheet_x":4,"sheet_y":56,"short_name":"cyclone","short_names":["cyclone"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1051,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FOGGY","unified":"1F301","non_qualified":null,"docomo":"E644","au":"E598","softbank":null,"google":"FE006","image":"1f301.png","sheet_x":4,"sheet_y":57,"short_name":"foggy","short_names":["foggy"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":898,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOSED UMBRELLA","unified":"1F302","non_qualified":null,"docomo":"E645","au":"EAE8","softbank":"E43C","google":"FE007","image":"1f302.png","sheet_x":4,"sheet_y":58,"short_name":"closed_umbrella","short_names":["closed_umbrella"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1053,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NIGHT WITH STARS","unified":"1F303","non_qualified":null,"docomo":"E6B3","au":"EAF1","softbank":"E44B","google":"FE008","image":"1f303.png","sheet_x":4,"sheet_y":59,"short_name":"night_with_stars","short_names":["night_with_stars"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":899,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUNRISE OVER MOUNTAINS","unified":"1F304","non_qualified":null,"docomo":"E63E","au":"EAF4","softbank":"E04D","google":"FE009","image":"1f304.png","sheet_x":4,"sheet_y":60,"short_name":"sunrise_over_mountains","short_names":["sunrise_over_mountains"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":901,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUNRISE","unified":"1F305","non_qualified":null,"docomo":"E63E","au":"EAF4","softbank":"E449","google":"FE00A","image":"1f305.png","sheet_x":4,"sheet_y":61,"short_name":"sunrise","short_names":["sunrise"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":902,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CITYSCAPE AT DUSK","unified":"1F306","non_qualified":null,"docomo":null,"au":"E5DA","softbank":"E146","google":"FE00B","image":"1f306.png","sheet_x":5,"sheet_y":0,"short_name":"city_sunset","short_names":["city_sunset"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":903,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUNSET OVER BUILDINGS","unified":"1F307","non_qualified":null,"docomo":"E63E","au":"E5DA","softbank":"E44A","google":"FE00C","image":"1f307.png","sheet_x":5,"sheet_y":1,"short_name":"city_sunrise","short_names":["city_sunrise"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":904,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RAINBOW","unified":"1F308","non_qualified":null,"docomo":null,"au":"EAF2","softbank":"E44C","google":"FE00D","image":"1f308.png","sheet_x":5,"sheet_y":2,"short_name":"rainbow","short_names":["rainbow"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1052,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BRIDGE AT NIGHT","unified":"1F309","non_qualified":null,"docomo":"E6B3","au":"E4BF","softbank":null,"google":"FE010","image":"1f309.png","sheet_x":5,"sheet_y":3,"short_name":"bridge_at_night","short_names":["bridge_at_night"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":905,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WATER WAVE","unified":"1F30A","non_qualified":null,"docomo":"E73F","au":"EB7C","softbank":"E43E","google":"FE038","image":"1f30a.png","sheet_x":5,"sheet_y":4,"short_name":"ocean","short_names":["ocean"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1064,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"VOLCANO","unified":"1F30B","non_qualified":null,"docomo":null,"au":"EB53","softbank":null,"google":"FE03A","image":"1f30b.png","sheet_x":5,"sheet_y":5,"short_name":"volcano","short_names":["volcano"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-geographic","sort_order":856,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MILKY WAY","unified":"1F30C","non_qualified":null,"docomo":"E6B3","au":"EB5F","softbank":null,"google":"FE03B","image":"1f30c.png","sheet_x":5,"sheet_y":6,"short_name":"milky_way","short_names":["milky_way"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1038,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EARTH GLOBE EUROPE-AFRICA","unified":"1F30D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f30d.png","sheet_x":5,"sheet_y":7,"short_name":"earth_africa","short_names":["earth_africa"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-map","sort_order":847,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EARTH GLOBE AMERICAS","unified":"1F30E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f30e.png","sheet_x":5,"sheet_y":8,"short_name":"earth_americas","short_names":["earth_americas"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-map","sort_order":848,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EARTH GLOBE ASIA-AUSTRALIA","unified":"1F30F","non_qualified":null,"docomo":null,"au":"E5B3","softbank":null,"google":"FE039","image":"1f30f.png","sheet_x":5,"sheet_y":9,"short_name":"earth_asia","short_names":["earth_asia"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-map","sort_order":849,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GLOBE WITH MERIDIANS","unified":"1F310","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f310.png","sheet_x":5,"sheet_y":10,"short_name":"globe_with_meridians","short_names":["globe_with_meridians"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-map","sort_order":850,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEW MOON SYMBOL","unified":"1F311","non_qualified":null,"docomo":"E69C","au":"E5A8","softbank":null,"google":"FE011","image":"1f311.png","sheet_x":5,"sheet_y":11,"short_name":"new_moon","short_names":["new_moon"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1018,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WAXING CRESCENT MOON SYMBOL","unified":"1F312","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f312.png","sheet_x":5,"sheet_y":12,"short_name":"waxing_crescent_moon","short_names":["waxing_crescent_moon"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1019,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FIRST QUARTER MOON SYMBOL","unified":"1F313","non_qualified":null,"docomo":"E69E","au":"E5AA","softbank":null,"google":"FE013","image":"1f313.png","sheet_x":5,"sheet_y":13,"short_name":"first_quarter_moon","short_names":["first_quarter_moon"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1020,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WAXING GIBBOUS MOON SYMBOL","unified":"1F314","non_qualified":null,"docomo":"E69D","au":"E5A9","softbank":null,"google":"FE012","image":"1f314.png","sheet_x":5,"sheet_y":14,"short_name":"moon","short_names":["moon","waxing_gibbous_moon"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1021,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FULL MOON SYMBOL","unified":"1F315","non_qualified":null,"docomo":"E6A0","au":null,"softbank":null,"google":"FE015","image":"1f315.png","sheet_x":5,"sheet_y":15,"short_name":"full_moon","short_names":["full_moon"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1022,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WANING GIBBOUS MOON SYMBOL","unified":"1F316","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f316.png","sheet_x":5,"sheet_y":16,"short_name":"waning_gibbous_moon","short_names":["waning_gibbous_moon"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1023,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LAST QUARTER MOON SYMBOL","unified":"1F317","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f317.png","sheet_x":5,"sheet_y":17,"short_name":"last_quarter_moon","short_names":["last_quarter_moon"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1024,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WANING CRESCENT MOON SYMBOL","unified":"1F318","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f318.png","sheet_x":5,"sheet_y":18,"short_name":"waning_crescent_moon","short_names":["waning_crescent_moon"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1025,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CRESCENT MOON","unified":"1F319","non_qualified":null,"docomo":"E69F","au":"E486","softbank":"E04C","google":"FE014","image":"1f319.png","sheet_x":5,"sheet_y":19,"short_name":"crescent_moon","short_names":["crescent_moon"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1026,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEW MOON WITH FACE","unified":"1F31A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f31a.png","sheet_x":5,"sheet_y":20,"short_name":"new_moon_with_face","short_names":["new_moon_with_face"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1027,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FIRST QUARTER MOON WITH FACE","unified":"1F31B","non_qualified":null,"docomo":"E69E","au":"E489","softbank":null,"google":"FE016","image":"1f31b.png","sheet_x":5,"sheet_y":21,"short_name":"first_quarter_moon_with_face","short_names":["first_quarter_moon_with_face"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1028,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LAST QUARTER MOON WITH FACE","unified":"1F31C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f31c.png","sheet_x":5,"sheet_y":22,"short_name":"last_quarter_moon_with_face","short_names":["last_quarter_moon_with_face"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1029,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FULL MOON WITH FACE","unified":"1F31D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f31d.png","sheet_x":5,"sheet_y":23,"short_name":"full_moon_with_face","short_names":["full_moon_with_face"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1032,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUN WITH FACE","unified":"1F31E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f31e.png","sheet_x":5,"sheet_y":24,"short_name":"sun_with_face","short_names":["sun_with_face"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1033,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GLOWING STAR","unified":"1F31F","non_qualified":null,"docomo":null,"au":"E48B","softbank":"E335","google":"FEB69","image":"1f31f.png","sheet_x":5,"sheet_y":25,"short_name":"star2","short_names":["star2"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1036,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHOOTING STAR","unified":"1F320","non_qualified":null,"docomo":null,"au":"E468","softbank":null,"google":"FEB6A","image":"1f320.png","sheet_x":5,"sheet_y":26,"short_name":"stars","short_names":["stars"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1037,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"THERMOMETER","unified":"1F321-FE0F","non_qualified":"1F321","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f321-fe0f.png","sheet_x":5,"sheet_y":27,"short_name":"thermometer","short_names":["thermometer"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1030,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUN BEHIND SMALL CLOUD","unified":"1F324-FE0F","non_qualified":"1F324","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f324-fe0f.png","sheet_x":5,"sheet_y":28,"short_name":"mostly_sunny","short_names":["mostly_sunny","sun_small_cloud"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1042,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUN BEHIND LARGE CLOUD","unified":"1F325-FE0F","non_qualified":"1F325","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f325-fe0f.png","sheet_x":5,"sheet_y":29,"short_name":"barely_sunny","short_names":["barely_sunny","sun_behind_cloud"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1043,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUN BEHIND RAIN CLOUD","unified":"1F326-FE0F","non_qualified":"1F326","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f326-fe0f.png","sheet_x":5,"sheet_y":30,"short_name":"partly_sunny_rain","short_names":["partly_sunny_rain","sun_behind_rain_cloud"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1044,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOUD WITH RAIN","unified":"1F327-FE0F","non_qualified":"1F327","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f327-fe0f.png","sheet_x":5,"sheet_y":31,"short_name":"rain_cloud","short_names":["rain_cloud"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1045,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOUD WITH SNOW","unified":"1F328-FE0F","non_qualified":"1F328","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f328-fe0f.png","sheet_x":5,"sheet_y":32,"short_name":"snow_cloud","short_names":["snow_cloud"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1046,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOUD WITH LIGHTNING","unified":"1F329-FE0F","non_qualified":"1F329","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f329-fe0f.png","sheet_x":5,"sheet_y":33,"short_name":"lightning","short_names":["lightning","lightning_cloud"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1047,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TORNADO","unified":"1F32A-FE0F","non_qualified":"1F32A","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32a-fe0f.png","sheet_x":5,"sheet_y":34,"short_name":"tornado","short_names":["tornado","tornado_cloud"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1048,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FOG","unified":"1F32B-FE0F","non_qualified":"1F32B","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32b-fe0f.png","sheet_x":5,"sheet_y":35,"short_name":"fog","short_names":["fog"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1049,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WIND FACE","unified":"1F32C-FE0F","non_qualified":"1F32C","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32c-fe0f.png","sheet_x":5,"sheet_y":36,"short_name":"wind_blowing_face","short_names":["wind_blowing_face"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1050,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOT DOG","unified":"1F32D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32d.png","sheet_x":5,"sheet_y":37,"short_name":"hotdog","short_names":["hotdog"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":766,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TACO","unified":"1F32E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32e.png","sheet_x":5,"sheet_y":38,"short_name":"taco","short_names":["taco"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":768,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BURRITO","unified":"1F32F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32f.png","sheet_x":5,"sheet_y":39,"short_name":"burrito","short_names":["burrito"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":769,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHESTNUT","unified":"1F330","non_qualified":null,"docomo":null,"au":"EB38","softbank":null,"google":"FE04C","image":"1f330.png","sheet_x":5,"sheet_y":40,"short_name":"chestnut","short_names":["chestnut"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":746,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SEEDLING","unified":"1F331","non_qualified":null,"docomo":"E746","au":"EB7D","softbank":null,"google":"FE03E","image":"1f331.png","sheet_x":5,"sheet_y":41,"short_name":"seedling","short_names":["seedling"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":696,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EVERGREEN TREE","unified":"1F332","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f332.png","sheet_x":5,"sheet_y":42,"short_name":"evergreen_tree","short_names":["evergreen_tree"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":698,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DECIDUOUS TREE","unified":"1F333","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f333.png","sheet_x":5,"sheet_y":43,"short_name":"deciduous_tree","short_names":["deciduous_tree"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":699,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PALM TREE","unified":"1F334","non_qualified":null,"docomo":null,"au":"E4E2","softbank":"E307","google":"FE047","image":"1f334.png","sheet_x":5,"sheet_y":44,"short_name":"palm_tree","short_names":["palm_tree"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":700,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CACTUS","unified":"1F335","non_qualified":null,"docomo":null,"au":"EA96","softbank":"E308","google":"FE048","image":"1f335.png","sheet_x":5,"sheet_y":45,"short_name":"cactus","short_names":["cactus"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":701,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOT PEPPER","unified":"1F336-FE0F","non_qualified":"1F336","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f336-fe0f.png","sheet_x":5,"sheet_y":46,"short_name":"hot_pepper","short_names":["hot_pepper"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":737,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TULIP","unified":"1F337","non_qualified":null,"docomo":"E743","au":"E4E4","softbank":"E304","google":"FE03D","image":"1f337.png","sheet_x":5,"sheet_y":47,"short_name":"tulip","short_names":["tulip"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":694,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHERRY BLOSSOM","unified":"1F338","non_qualified":null,"docomo":"E748","au":"E4CA","softbank":"E030","google":"FE040","image":"1f338.png","sheet_x":5,"sheet_y":48,"short_name":"cherry_blossom","short_names":["cherry_blossom"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":685,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROSE","unified":"1F339","non_qualified":null,"docomo":null,"au":"E5BA","softbank":"E032","google":"FE041","image":"1f339.png","sheet_x":5,"sheet_y":49,"short_name":"rose","short_names":["rose"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":689,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HIBISCUS","unified":"1F33A","non_qualified":null,"docomo":null,"au":"EA94","softbank":"E303","google":"FE045","image":"1f33a.png","sheet_x":5,"sheet_y":50,"short_name":"hibiscus","short_names":["hibiscus"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":691,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUNFLOWER","unified":"1F33B","non_qualified":null,"docomo":null,"au":"E4E3","softbank":"E305","google":"FE046","image":"1f33b.png","sheet_x":5,"sheet_y":51,"short_name":"sunflower","short_names":["sunflower"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":692,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLOSSOM","unified":"1F33C","non_qualified":null,"docomo":null,"au":"EB49","softbank":null,"google":"FE04D","image":"1f33c.png","sheet_x":5,"sheet_y":52,"short_name":"blossom","short_names":["blossom"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":693,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EAR OF MAIZE","unified":"1F33D","non_qualified":null,"docomo":null,"au":"EB36","softbank":null,"google":"FE04A","image":"1f33d.png","sheet_x":5,"sheet_y":53,"short_name":"corn","short_names":["corn"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":736,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EAR OF RICE","unified":"1F33E","non_qualified":null,"docomo":null,"au":null,"softbank":"E444","google":"FE049","image":"1f33e.png","sheet_x":5,"sheet_y":54,"short_name":"ear_of_rice","short_names":["ear_of_rice"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":702,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HERB","unified":"1F33F","non_qualified":null,"docomo":"E741","au":"EB82","softbank":null,"google":"FE04E","image":"1f33f.png","sheet_x":5,"sheet_y":55,"short_name":"herb","short_names":["herb"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":703,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FOUR LEAF CLOVER","unified":"1F340","non_qualified":null,"docomo":"E741","au":"E513","softbank":"E110","google":"FE03C","image":"1f340.png","sheet_x":5,"sheet_y":56,"short_name":"four_leaf_clover","short_names":["four_leaf_clover"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":705,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MAPLE LEAF","unified":"1F341","non_qualified":null,"docomo":"E747","au":"E4CE","softbank":"E118","google":"FE03F","image":"1f341.png","sheet_x":5,"sheet_y":57,"short_name":"maple_leaf","short_names":["maple_leaf"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":706,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FALLEN LEAF","unified":"1F342","non_qualified":null,"docomo":"E747","au":"E5CD","softbank":"E119","google":"FE042","image":"1f342.png","sheet_x":5,"sheet_y":58,"short_name":"fallen_leaf","short_names":["fallen_leaf"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":707,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEAF FLUTTERING IN WIND","unified":"1F343","non_qualified":null,"docomo":null,"au":"E5CD","softbank":"E447","google":"FE043","image":"1f343.png","sheet_x":5,"sheet_y":59,"short_name":"leaves","short_names":["leaves"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":708,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BROWN MUSHROOM","unified":"1F344-200D-1F7EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f344-200d-1f7eb.png","sheet_x":5,"sheet_y":60,"short_name":"brown_mushroom","short_names":["brown_mushroom"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":749,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},{"name":"MUSHROOM","unified":"1F344","non_qualified":null,"docomo":null,"au":"EB37","softbank":null,"google":"FE04B","image":"1f344.png","sheet_x":5,"sheet_y":61,"short_name":"mushroom","short_names":["mushroom"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":711,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TOMATO","unified":"1F345","non_qualified":null,"docomo":null,"au":"EABB","softbank":"E349","google":"FE055","image":"1f345.png","sheet_x":6,"sheet_y":0,"short_name":"tomato","short_names":["tomato"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":729,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AUBERGINE","unified":"1F346","non_qualified":null,"docomo":null,"au":"EABC","softbank":"E34A","google":"FE056","image":"1f346.png","sheet_x":6,"sheet_y":1,"short_name":"eggplant","short_names":["eggplant"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":733,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GRAPES","unified":"1F347","non_qualified":null,"docomo":null,"au":"EB34","softbank":null,"google":"FE059","image":"1f347.png","sheet_x":6,"sheet_y":2,"short_name":"grapes","short_names":["grapes"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":712,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MELON","unified":"1F348","non_qualified":null,"docomo":null,"au":"EB32","softbank":null,"google":"FE057","image":"1f348.png","sheet_x":6,"sheet_y":3,"short_name":"melon","short_names":["melon"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":713,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WATERMELON","unified":"1F349","non_qualified":null,"docomo":null,"au":"E4CD","softbank":"E348","google":"FE054","image":"1f349.png","sheet_x":6,"sheet_y":4,"short_name":"watermelon","short_names":["watermelon"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":714,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TANGERINE","unified":"1F34A","non_qualified":null,"docomo":null,"au":"EABA","softbank":"E346","google":"FE052","image":"1f34a.png","sheet_x":6,"sheet_y":5,"short_name":"tangerine","short_names":["tangerine"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":715,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LIME","unified":"1F34B-200D-1F7E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f34b-200d-1f7e9.png","sheet_x":6,"sheet_y":6,"short_name":"lime","short_names":["lime"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":717,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},{"name":"LEMON","unified":"1F34B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f34b.png","sheet_x":6,"sheet_y":7,"short_name":"lemon","short_names":["lemon"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":716,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BANANA","unified":"1F34C","non_qualified":null,"docomo":"E744","au":"EB35","softbank":null,"google":"FE050","image":"1f34c.png","sheet_x":6,"sheet_y":8,"short_name":"banana","short_names":["banana"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":718,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PINEAPPLE","unified":"1F34D","non_qualified":null,"docomo":null,"au":"EB33","softbank":null,"google":"FE058","image":"1f34d.png","sheet_x":6,"sheet_y":9,"short_name":"pineapple","short_names":["pineapple"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":719,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RED APPLE","unified":"1F34E","non_qualified":null,"docomo":"E745","au":"EAB9","softbank":"E345","google":"FE051","image":"1f34e.png","sheet_x":6,"sheet_y":10,"short_name":"apple","short_names":["apple"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":721,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GREEN APPLE","unified":"1F34F","non_qualified":null,"docomo":"E745","au":"EB5A","softbank":null,"google":"FE05B","image":"1f34f.png","sheet_x":6,"sheet_y":11,"short_name":"green_apple","short_names":["green_apple"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":722,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PEAR","unified":"1F350","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f350.png","sheet_x":6,"sheet_y":12,"short_name":"pear","short_names":["pear"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":723,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PEACH","unified":"1F351","non_qualified":null,"docomo":null,"au":"EB39","softbank":null,"google":"FE05A","image":"1f351.png","sheet_x":6,"sheet_y":13,"short_name":"peach","short_names":["peach"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":724,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHERRIES","unified":"1F352","non_qualified":null,"docomo":"E742","au":"E4D2","softbank":null,"google":"FE04F","image":"1f352.png","sheet_x":6,"sheet_y":14,"short_name":"cherries","short_names":["cherries"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":725,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STRAWBERRY","unified":"1F353","non_qualified":null,"docomo":null,"au":"E4D4","softbank":"E347","google":"FE053","image":"1f353.png","sheet_x":6,"sheet_y":15,"short_name":"strawberry","short_names":["strawberry"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":726,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HAMBURGER","unified":"1F354","non_qualified":null,"docomo":"E673","au":"E4D6","softbank":"E120","google":"FE960","image":"1f354.png","sheet_x":6,"sheet_y":16,"short_name":"hamburger","short_names":["hamburger"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":763,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SLICE OF PIZZA","unified":"1F355","non_qualified":null,"docomo":null,"au":"EB3B","softbank":null,"google":"FE975","image":"1f355.png","sheet_x":6,"sheet_y":17,"short_name":"pizza","short_names":["pizza"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":765,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MEAT ON BONE","unified":"1F356","non_qualified":null,"docomo":null,"au":"E4C4","softbank":null,"google":"FE972","image":"1f356.png","sheet_x":6,"sheet_y":18,"short_name":"meat_on_bone","short_names":["meat_on_bone"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":759,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POULTRY LEG","unified":"1F357","non_qualified":null,"docomo":null,"au":"EB3C","softbank":null,"google":"FE976","image":"1f357.png","sheet_x":6,"sheet_y":19,"short_name":"poultry_leg","short_names":["poultry_leg"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":760,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RICE CRACKER","unified":"1F358","non_qualified":null,"docomo":null,"au":"EAB3","softbank":"E33D","google":"FE969","image":"1f358.png","sheet_x":6,"sheet_y":20,"short_name":"rice_cracker","short_names":["rice_cracker"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":785,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RICE BALL","unified":"1F359","non_qualified":null,"docomo":"E749","au":"E4D5","softbank":"E342","google":"FE961","image":"1f359.png","sheet_x":6,"sheet_y":21,"short_name":"rice_ball","short_names":["rice_ball"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":786,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COOKED RICE","unified":"1F35A","non_qualified":null,"docomo":"E74C","au":"EAB4","softbank":"E33E","google":"FE96A","image":"1f35a.png","sheet_x":6,"sheet_y":22,"short_name":"rice","short_names":["rice"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":787,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CURRY AND RICE","unified":"1F35B","non_qualified":null,"docomo":null,"au":"EAB6","softbank":"E341","google":"FE96C","image":"1f35b.png","sheet_x":6,"sheet_y":23,"short_name":"curry","short_names":["curry"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":788,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STEAMING BOWL","unified":"1F35C","non_qualified":null,"docomo":"E74C","au":"E5B4","softbank":"E340","google":"FE963","image":"1f35c.png","sheet_x":6,"sheet_y":24,"short_name":"ramen","short_names":["ramen"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":789,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPAGHETTI","unified":"1F35D","non_qualified":null,"docomo":null,"au":"EAB5","softbank":"E33F","google":"FE96B","image":"1f35d.png","sheet_x":6,"sheet_y":25,"short_name":"spaghetti","short_names":["spaghetti"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":790,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BREAD","unified":"1F35E","non_qualified":null,"docomo":"E74D","au":"EAAF","softbank":"E339","google":"FE964","image":"1f35e.png","sheet_x":6,"sheet_y":26,"short_name":"bread","short_names":["bread"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":750,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FRENCH FRIES","unified":"1F35F","non_qualified":null,"docomo":null,"au":"EAB1","softbank":"E33B","google":"FE967","image":"1f35f.png","sheet_x":6,"sheet_y":27,"short_name":"fries","short_names":["fries"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":764,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROASTED SWEET POTATO","unified":"1F360","non_qualified":null,"docomo":null,"au":"EB3A","softbank":null,"google":"FE974","image":"1f360.png","sheet_x":6,"sheet_y":28,"short_name":"sweet_potato","short_names":["sweet_potato"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":791,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DANGO","unified":"1F361","non_qualified":null,"docomo":null,"au":"EAB2","softbank":"E33C","google":"FE968","image":"1f361.png","sheet_x":6,"sheet_y":29,"short_name":"dango","short_names":["dango"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":797,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ODEN","unified":"1F362","non_qualified":null,"docomo":null,"au":"EAB7","softbank":"E343","google":"FE96D","image":"1f362.png","sheet_x":6,"sheet_y":30,"short_name":"oden","short_names":["oden"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":792,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUSHI","unified":"1F363","non_qualified":null,"docomo":null,"au":"EAB8","softbank":"E344","google":"FE96E","image":"1f363.png","sheet_x":6,"sheet_y":31,"short_name":"sushi","short_names":["sushi"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":793,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FRIED SHRIMP","unified":"1F364","non_qualified":null,"docomo":null,"au":"EB70","softbank":null,"google":"FE97F","image":"1f364.png","sheet_x":6,"sheet_y":32,"short_name":"fried_shrimp","short_names":["fried_shrimp"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":794,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FISH CAKE WITH SWIRL DESIGN","unified":"1F365","non_qualified":null,"docomo":"E643","au":"E4ED","softbank":null,"google":"FE973","image":"1f365.png","sheet_x":6,"sheet_y":33,"short_name":"fish_cake","short_names":["fish_cake"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":795,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SOFT ICE CREAM","unified":"1F366","non_qualified":null,"docomo":null,"au":"EAB0","softbank":"E33A","google":"FE966","image":"1f366.png","sheet_x":6,"sheet_y":34,"short_name":"icecream","short_names":["icecream"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":806,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHAVED ICE","unified":"1F367","non_qualified":null,"docomo":null,"au":"EAEA","softbank":"E43F","google":"FE971","image":"1f367.png","sheet_x":6,"sheet_y":35,"short_name":"shaved_ice","short_names":["shaved_ice"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":807,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ICE CREAM","unified":"1F368","non_qualified":null,"docomo":null,"au":"EB4A","softbank":null,"google":"FE977","image":"1f368.png","sheet_x":6,"sheet_y":36,"short_name":"ice_cream","short_names":["ice_cream"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":808,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOUGHNUT","unified":"1F369","non_qualified":null,"docomo":null,"au":"EB4B","softbank":null,"google":"FE978","image":"1f369.png","sheet_x":6,"sheet_y":37,"short_name":"doughnut","short_names":["doughnut"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":809,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COOKIE","unified":"1F36A","non_qualified":null,"docomo":null,"au":"EB4C","softbank":null,"google":"FE979","image":"1f36a.png","sheet_x":6,"sheet_y":38,"short_name":"cookie","short_names":["cookie"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":810,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHOCOLATE BAR","unified":"1F36B","non_qualified":null,"docomo":null,"au":"EB4D","softbank":null,"google":"FE97A","image":"1f36b.png","sheet_x":6,"sheet_y":39,"short_name":"chocolate_bar","short_names":["chocolate_bar"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":815,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CANDY","unified":"1F36C","non_qualified":null,"docomo":null,"au":"EB4E","softbank":null,"google":"FE97B","image":"1f36c.png","sheet_x":6,"sheet_y":40,"short_name":"candy","short_names":["candy"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":816,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOLLIPOP","unified":"1F36D","non_qualified":null,"docomo":null,"au":"EB4F","softbank":null,"google":"FE97C","image":"1f36d.png","sheet_x":6,"sheet_y":41,"short_name":"lollipop","short_names":["lollipop"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":817,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CUSTARD","unified":"1F36E","non_qualified":null,"docomo":null,"au":"EB56","softbank":null,"google":"FE97D","image":"1f36e.png","sheet_x":6,"sheet_y":42,"short_name":"custard","short_names":["custard"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":818,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HONEY POT","unified":"1F36F","non_qualified":null,"docomo":null,"au":"EB59","softbank":null,"google":"FE97E","image":"1f36f.png","sheet_x":6,"sheet_y":43,"short_name":"honey_pot","short_names":["honey_pot"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":819,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHORTCAKE","unified":"1F370","non_qualified":null,"docomo":"E74A","au":"E4D0","softbank":"E046","google":"FE962","image":"1f370.png","sheet_x":6,"sheet_y":44,"short_name":"cake","short_names":["cake"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":812,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BENTO BOX","unified":"1F371","non_qualified":null,"docomo":null,"au":"EABD","softbank":"E34C","google":"FE96F","image":"1f371.png","sheet_x":6,"sheet_y":45,"short_name":"bento","short_names":["bento"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":784,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POT OF FOOD","unified":"1F372","non_qualified":null,"docomo":null,"au":"EABE","softbank":"E34D","google":"FE970","image":"1f372.png","sheet_x":6,"sheet_y":46,"short_name":"stew","short_names":["stew"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":776,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COOKING","unified":"1F373","non_qualified":null,"docomo":null,"au":"E4D1","softbank":"E147","google":"FE965","image":"1f373.png","sheet_x":6,"sheet_y":47,"short_name":"fried_egg","short_names":["fried_egg","cooking"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":774,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FORK AND KNIFE","unified":"1F374","non_qualified":null,"docomo":"E66F","au":"E4AC","softbank":"E043","google":"FE980","image":"1f374.png","sheet_x":6,"sheet_y":48,"short_name":"fork_and_knife","short_names":["fork_and_knife"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"dishware","sort_order":842,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TEACUP WITHOUT HANDLE","unified":"1F375","non_qualified":null,"docomo":"E71E","au":"EAAE","softbank":"E338","google":"FE984","image":"1f375.png","sheet_x":6,"sheet_y":49,"short_name":"tea","short_names":["tea"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":824,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SAKE BOTTLE AND CUP","unified":"1F376","non_qualified":null,"docomo":"E74B","au":"EA97","softbank":"E30B","google":"FE985","image":"1f376.png","sheet_x":6,"sheet_y":50,"short_name":"sake","short_names":["sake"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":825,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WINE GLASS","unified":"1F377","non_qualified":null,"docomo":"E756","au":"E4C1","softbank":null,"google":"FE986","image":"1f377.png","sheet_x":6,"sheet_y":51,"short_name":"wine_glass","short_names":["wine_glass"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":827,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COCKTAIL GLASS","unified":"1F378","non_qualified":null,"docomo":"E671","au":"E4C2","softbank":"E044","google":"FE982","image":"1f378.png","sheet_x":6,"sheet_y":52,"short_name":"cocktail","short_names":["cocktail"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":828,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TROPICAL DRINK","unified":"1F379","non_qualified":null,"docomo":"E671","au":"EB3E","softbank":null,"google":"FE988","image":"1f379.png","sheet_x":6,"sheet_y":53,"short_name":"tropical_drink","short_names":["tropical_drink"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":829,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BEER MUG","unified":"1F37A","non_qualified":null,"docomo":"E672","au":"E4C3","softbank":"E047","google":"FE983","image":"1f37a.png","sheet_x":6,"sheet_y":54,"short_name":"beer","short_names":["beer"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":830,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLINKING BEER MUGS","unified":"1F37B","non_qualified":null,"docomo":"E672","au":"EA98","softbank":"E30C","google":"FE987","image":"1f37b.png","sheet_x":6,"sheet_y":55,"short_name":"beers","short_names":["beers"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":831,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BABY BOTTLE","unified":"1F37C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f37c.png","sheet_x":6,"sheet_y":56,"short_name":"baby_bottle","short_names":["baby_bottle"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":820,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FORK AND KNIFE WITH PLATE","unified":"1F37D-FE0F","non_qualified":"1F37D","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f37d-fe0f.png","sheet_x":6,"sheet_y":57,"short_name":"knife_fork_plate","short_names":["knife_fork_plate"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"dishware","sort_order":841,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOTTLE WITH POPPING CORK","unified":"1F37E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f37e.png","sheet_x":6,"sheet_y":58,"short_name":"champagne","short_names":["champagne"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":826,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POPCORN","unified":"1F37F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f37f.png","sheet_x":6,"sheet_y":59,"short_name":"popcorn","short_names":["popcorn"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":780,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RIBBON","unified":"1F380","non_qualified":null,"docomo":"E684","au":"E59F","softbank":"E314","google":"FE50F","image":"1f380.png","sheet_x":6,"sheet_y":60,"short_name":"ribbon","short_names":["ribbon"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1081,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WRAPPED PRESENT","unified":"1F381","non_qualified":null,"docomo":"E685","au":"E4CF","softbank":"E112","google":"FE510","image":"1f381.png","sheet_x":6,"sheet_y":61,"short_name":"gift","short_names":["gift"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1082,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BIRTHDAY CAKE","unified":"1F382","non_qualified":null,"docomo":"E686","au":"E5A0","softbank":"E34B","google":"FE511","image":"1f382.png","sheet_x":7,"sheet_y":0,"short_name":"birthday","short_names":["birthday"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":811,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JACK-O-LANTERN","unified":"1F383","non_qualified":null,"docomo":null,"au":"EAEE","softbank":"E445","google":"FE51F","image":"1f383.png","sheet_x":7,"sheet_y":1,"short_name":"jack_o_lantern","short_names":["jack_o_lantern"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1065,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHRISTMAS TREE","unified":"1F384","non_qualified":null,"docomo":"E6A4","au":"E4C9","softbank":"E033","google":"FE512","image":"1f384.png","sheet_x":7,"sheet_y":2,"short_name":"christmas_tree","short_names":["christmas_tree"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1066,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FATHER CHRISTMAS","unified":"1F385","non_qualified":null,"docomo":null,"au":"EAF0","softbank":"E448","google":"FE513","image":"1f385.png","sheet_x":7,"sheet_y":3,"short_name":"santa","short_names":["santa"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":371,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F385-1F3FB","non_qualified":null,"image":"1f385-1f3fb.png","sheet_x":7,"sheet_y":4,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F385-1F3FC","non_qualified":null,"image":"1f385-1f3fc.png","sheet_x":7,"sheet_y":5,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F385-1F3FD","non_qualified":null,"image":"1f385-1f3fd.png","sheet_x":7,"sheet_y":6,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F385-1F3FE","non_qualified":null,"image":"1f385-1f3fe.png","sheet_x":7,"sheet_y":7,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F385-1F3FF","non_qualified":null,"image":"1f385-1f3ff.png","sheet_x":7,"sheet_y":8,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FIREWORKS","unified":"1F386","non_qualified":null,"docomo":null,"au":"E5CC","softbank":"E117","google":"FE515","image":"1f386.png","sheet_x":7,"sheet_y":9,"short_name":"fireworks","short_names":["fireworks"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1067,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FIREWORK SPARKLER","unified":"1F387","non_qualified":null,"docomo":null,"au":"EAEB","softbank":"E440","google":"FE51D","image":"1f387.png","sheet_x":7,"sheet_y":10,"short_name":"sparkler","short_names":["sparkler"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1068,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BALLOON","unified":"1F388","non_qualified":null,"docomo":null,"au":"EA9B","softbank":"E310","google":"FE516","image":"1f388.png","sheet_x":7,"sheet_y":11,"short_name":"balloon","short_names":["balloon"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1071,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PARTY POPPER","unified":"1F389","non_qualified":null,"docomo":null,"au":"EA9C","softbank":"E312","google":"FE517","image":"1f389.png","sheet_x":7,"sheet_y":12,"short_name":"tada","short_names":["tada"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1072,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CONFETTI BALL","unified":"1F38A","non_qualified":null,"docomo":null,"au":"E46F","softbank":null,"google":"FE520","image":"1f38a.png","sheet_x":7,"sheet_y":13,"short_name":"confetti_ball","short_names":["confetti_ball"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1073,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TANABATA TREE","unified":"1F38B","non_qualified":null,"docomo":null,"au":"EB3D","softbank":null,"google":"FE521","image":"1f38b.png","sheet_x":7,"sheet_y":14,"short_name":"tanabata_tree","short_names":["tanabata_tree"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1074,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CROSSED FLAGS","unified":"1F38C","non_qualified":null,"docomo":null,"au":"E5D9","softbank":"E143","google":"FE514","image":"1f38c.png","sheet_x":7,"sheet_y":15,"short_name":"crossed_flags","short_names":["crossed_flags"],"text":null,"texts":null,"category":"Flags","subcategory":"flag","sort_order":1637,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PINE DECORATION","unified":"1F38D","non_qualified":null,"docomo":null,"au":"EAE3","softbank":"E436","google":"FE518","image":"1f38d.png","sheet_x":7,"sheet_y":16,"short_name":"bamboo","short_names":["bamboo"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1075,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JAPANESE DOLLS","unified":"1F38E","non_qualified":null,"docomo":null,"au":"EAE4","softbank":"E438","google":"FE519","image":"1f38e.png","sheet_x":7,"sheet_y":17,"short_name":"dolls","short_names":["dolls"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1076,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CARP STREAMER","unified":"1F38F","non_qualified":null,"docomo":null,"au":"EAE7","softbank":"E43B","google":"FE51C","image":"1f38f.png","sheet_x":7,"sheet_y":18,"short_name":"flags","short_names":["flags"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1077,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WIND CHIME","unified":"1F390","non_qualified":null,"docomo":null,"au":"EAED","softbank":"E442","google":"FE51E","image":"1f390.png","sheet_x":7,"sheet_y":19,"short_name":"wind_chime","short_names":["wind_chime"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1078,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOON VIEWING CEREMONY","unified":"1F391","non_qualified":null,"docomo":null,"au":"EAEF","softbank":"E446","google":"FE017","image":"1f391.png","sheet_x":7,"sheet_y":20,"short_name":"rice_scene","short_names":["rice_scene"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1079,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SCHOOL SATCHEL","unified":"1F392","non_qualified":null,"docomo":null,"au":"EAE6","softbank":"E43A","google":"FE51B","image":"1f392.png","sheet_x":7,"sheet_y":21,"short_name":"school_satchel","short_names":["school_satchel"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1175,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GRADUATION CAP","unified":"1F393","non_qualified":null,"docomo":null,"au":"EAE5","softbank":"E439","google":"FE51A","image":"1f393.png","sheet_x":7,"sheet_y":22,"short_name":"mortar_board","short_names":["mortar_board"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1189,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MILITARY MEDAL","unified":"1F396-FE0F","non_qualified":"1F396","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f396-fe0f.png","sheet_x":7,"sheet_y":23,"short_name":"medal","short_names":["medal"],"text":null,"texts":null,"category":"Activities","subcategory":"award-medal","sort_order":1086,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"REMINDER RIBBON","unified":"1F397-FE0F","non_qualified":"1F397","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f397-fe0f.png","sheet_x":7,"sheet_y":24,"short_name":"reminder_ribbon","short_names":["reminder_ribbon"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1083,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STUDIO MICROPHONE","unified":"1F399-FE0F","non_qualified":"1F399","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f399-fe0f.png","sheet_x":7,"sheet_y":25,"short_name":"studio_microphone","short_names":["studio_microphone"],"text":null,"texts":null,"category":"Objects","subcategory":"music","sort_order":1209,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEVEL SLIDER","unified":"1F39A-FE0F","non_qualified":"1F39A","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f39a-fe0f.png","sheet_x":7,"sheet_y":26,"short_name":"level_slider","short_names":["level_slider"],"text":null,"texts":null,"category":"Objects","subcategory":"music","sort_order":1210,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CONTROL KNOBS","unified":"1F39B-FE0F","non_qualified":"1F39B","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f39b-fe0f.png","sheet_x":7,"sheet_y":27,"short_name":"control_knobs","short_names":["control_knobs"],"text":null,"texts":null,"category":"Objects","subcategory":"music","sort_order":1211,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FILM FRAMES","unified":"1F39E-FE0F","non_qualified":"1F39E","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f39e-fe0f.png","sheet_x":7,"sheet_y":28,"short_name":"film_frames","short_names":["film_frames"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1247,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ADMISSION TICKETS","unified":"1F39F-FE0F","non_qualified":"1F39F","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f39f-fe0f.png","sheet_x":7,"sheet_y":29,"short_name":"admission_tickets","short_names":["admission_tickets"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1084,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CAROUSEL HORSE","unified":"1F3A0","non_qualified":null,"docomo":"E679","au":null,"softbank":null,"google":"FE7FC","image":"1f3a0.png","sheet_x":7,"sheet_y":30,"short_name":"carousel_horse","short_names":["carousel_horse"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":907,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FERRIS WHEEL","unified":"1F3A1","non_qualified":null,"docomo":null,"au":"E46D","softbank":"E124","google":"FE7FD","image":"1f3a1.png","sheet_x":7,"sheet_y":31,"short_name":"ferris_wheel","short_names":["ferris_wheel"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":909,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROLLER COASTER","unified":"1F3A2","non_qualified":null,"docomo":null,"au":"EAE2","softbank":"E433","google":"FE7FE","image":"1f3a2.png","sheet_x":7,"sheet_y":32,"short_name":"roller_coaster","short_names":["roller_coaster"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":910,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FISHING POLE AND FISH","unified":"1F3A3","non_qualified":null,"docomo":"E751","au":"EB42","softbank":null,"google":"FE7FF","image":"1f3a3.png","sheet_x":7,"sheet_y":33,"short_name":"fishing_pole_and_fish","short_names":["fishing_pole_and_fish"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1113,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MICROPHONE","unified":"1F3A4","non_qualified":null,"docomo":"E676","au":"E503","softbank":"E03C","google":"FE800","image":"1f3a4.png","sheet_x":7,"sheet_y":34,"short_name":"microphone","short_names":["microphone"],"text":null,"texts":null,"category":"Objects","subcategory":"music","sort_order":1212,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOVIE CAMERA","unified":"1F3A5","non_qualified":null,"docomo":"E677","au":"E517","softbank":"E03D","google":"FE801","image":"1f3a5.png","sheet_x":7,"sheet_y":35,"short_name":"movie_camera","short_names":["movie_camera"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1246,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CINEMA","unified":"1F3A6","non_qualified":null,"docomo":"E677","au":"E517","softbank":"E507","google":"FE802","image":"1f3a6.png","sheet_x":7,"sheet_y":36,"short_name":"cinema","short_names":["cinema"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1503,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEADPHONE","unified":"1F3A7","non_qualified":null,"docomo":"E67A","au":"E508","softbank":"E30A","google":"FE803","image":"1f3a7.png","sheet_x":7,"sheet_y":37,"short_name":"headphones","short_names":["headphones"],"text":null,"texts":null,"category":"Objects","subcategory":"music","sort_order":1213,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ARTIST PALETTE","unified":"1F3A8","non_qualified":null,"docomo":"E67B","au":"E59C","softbank":"E502","google":"FE804","image":"1f3a8.png","sheet_x":7,"sheet_y":38,"short_name":"art","short_names":["art"],"text":null,"texts":null,"category":"Activities","subcategory":"arts & crafts","sort_order":1145,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TOP HAT","unified":"1F3A9","non_qualified":null,"docomo":"E67C","au":"EAF5","softbank":"E503","google":"FE805","image":"1f3a9.png","sheet_x":7,"sheet_y":39,"short_name":"tophat","short_names":["tophat"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1188,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CIRCUS TENT","unified":"1F3AA","non_qualified":null,"docomo":"E67D","au":"E59E","softbank":null,"google":"FE806","image":"1f3aa.png","sheet_x":7,"sheet_y":40,"short_name":"circus_tent","short_names":["circus_tent"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":912,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TICKET","unified":"1F3AB","non_qualified":null,"docomo":"E67E","au":"E49E","softbank":"E125","google":"FE807","image":"1f3ab.png","sheet_x":7,"sheet_y":41,"short_name":"ticket","short_names":["ticket"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1085,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLAPPER BOARD","unified":"1F3AC","non_qualified":null,"docomo":"E6AC","au":"E4BE","softbank":"E324","google":"FE808","image":"1f3ac.png","sheet_x":7,"sheet_y":42,"short_name":"clapper","short_names":["clapper"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1249,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PERFORMING ARTS","unified":"1F3AD","non_qualified":null,"docomo":null,"au":"E59D","softbank":null,"google":"FE809","image":"1f3ad.png","sheet_x":7,"sheet_y":43,"short_name":"performing_arts","short_names":["performing_arts"],"text":null,"texts":null,"category":"Activities","subcategory":"arts & crafts","sort_order":1143,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"VIDEO GAME","unified":"1F3AE","non_qualified":null,"docomo":"E68B","au":"E4C6","softbank":null,"google":"FE80A","image":"1f3ae.png","sheet_x":7,"sheet_y":44,"short_name":"video_game","short_names":["video_game"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1126,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DIRECT HIT","unified":"1F3AF","non_qualified":null,"docomo":null,"au":"E4C5","softbank":"E130","google":"FE80C","image":"1f3af.png","sheet_x":7,"sheet_y":45,"short_name":"dart","short_names":["dart"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1119,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SLOT MACHINE","unified":"1F3B0","non_qualified":null,"docomo":null,"au":"E46E","softbank":"E133","google":"FE80D","image":"1f3b0.png","sheet_x":7,"sheet_y":46,"short_name":"slot_machine","short_names":["slot_machine"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1128,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BILLIARDS","unified":"1F3B1","non_qualified":null,"docomo":null,"au":"EADD","softbank":"E42C","google":"FE80E","image":"1f3b1.png","sheet_x":7,"sheet_y":47,"short_name":"8ball","short_names":["8ball"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1123,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GAME DIE","unified":"1F3B2","non_qualified":null,"docomo":null,"au":"E4C8","softbank":null,"google":"FE80F","image":"1f3b2.png","sheet_x":7,"sheet_y":48,"short_name":"game_die","short_names":["game_die"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1129,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOWLING","unified":"1F3B3","non_qualified":null,"docomo":null,"au":"EB43","softbank":null,"google":"FE810","image":"1f3b3.png","sheet_x":7,"sheet_y":49,"short_name":"bowling","short_names":["bowling"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1101,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLOWER PLAYING CARDS","unified":"1F3B4","non_qualified":null,"docomo":null,"au":"EB6E","softbank":null,"google":"FE811","image":"1f3b4.png","sheet_x":7,"sheet_y":50,"short_name":"flower_playing_cards","short_names":["flower_playing_cards"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1142,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MUSICAL NOTE","unified":"1F3B5","non_qualified":null,"docomo":"E6F6","au":"E5BE","softbank":"E03E","google":"FE813","image":"1f3b5.png","sheet_x":7,"sheet_y":51,"short_name":"musical_note","short_names":["musical_note"],"text":null,"texts":null,"category":"Objects","subcategory":"music","sort_order":1207,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MULTIPLE MUSICAL NOTES","unified":"1F3B6","non_qualified":null,"docomo":"E6FF","au":"E505","softbank":"E326","google":"FE814","image":"1f3b6.png","sheet_x":7,"sheet_y":52,"short_name":"notes","short_names":["notes"],"text":null,"texts":null,"category":"Objects","subcategory":"music","sort_order":1208,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SAXOPHONE","unified":"1F3B7","non_qualified":null,"docomo":null,"au":null,"softbank":"E040","google":"FE815","image":"1f3b7.png","sheet_x":7,"sheet_y":53,"short_name":"saxophone","short_names":["saxophone"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1215,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GUITAR","unified":"1F3B8","non_qualified":null,"docomo":null,"au":"E506","softbank":"E041","google":"FE816","image":"1f3b8.png","sheet_x":7,"sheet_y":54,"short_name":"guitar","short_names":["guitar"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1217,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MUSICAL KEYBOARD","unified":"1F3B9","non_qualified":null,"docomo":null,"au":"EB40","softbank":null,"google":"FE817","image":"1f3b9.png","sheet_x":7,"sheet_y":55,"short_name":"musical_keyboard","short_names":["musical_keyboard"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1218,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRUMPET","unified":"1F3BA","non_qualified":null,"docomo":null,"au":"EADC","softbank":"E042","google":"FE818","image":"1f3ba.png","sheet_x":7,"sheet_y":56,"short_name":"trumpet","short_names":["trumpet"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1219,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"VIOLIN","unified":"1F3BB","non_qualified":null,"docomo":null,"au":"E507","softbank":null,"google":"FE819","image":"1f3bb.png","sheet_x":7,"sheet_y":57,"short_name":"violin","short_names":["violin"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1220,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MUSICAL SCORE","unified":"1F3BC","non_qualified":null,"docomo":"E6FF","au":"EACC","softbank":null,"google":"FE81A","image":"1f3bc.png","sheet_x":7,"sheet_y":58,"short_name":"musical_score","short_names":["musical_score"],"text":null,"texts":null,"category":"Objects","subcategory":"music","sort_order":1206,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RUNNING SHIRT WITH SASH","unified":"1F3BD","non_qualified":null,"docomo":"E652","au":null,"softbank":null,"google":"FE7D0","image":"1f3bd.png","sheet_x":7,"sheet_y":59,"short_name":"running_shirt_with_sash","short_names":["running_shirt_with_sash"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1115,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TENNIS RACQUET AND BALL","unified":"1F3BE","non_qualified":null,"docomo":"E655","au":"E4B7","softbank":"E015","google":"FE7D3","image":"1f3be.png","sheet_x":7,"sheet_y":60,"short_name":"tennis","short_names":["tennis"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1099,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SKI AND SKI BOOT","unified":"1F3BF","non_qualified":null,"docomo":"E657","au":"EAAC","softbank":"E013","google":"FE7D5","image":"1f3bf.png","sheet_x":7,"sheet_y":61,"short_name":"ski","short_names":["ski"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1116,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BASKETBALL AND HOOP","unified":"1F3C0","non_qualified":null,"docomo":"E658","au":"E59A","softbank":"E42A","google":"FE7D6","image":"1f3c0.png","sheet_x":8,"sheet_y":0,"short_name":"basketball","short_names":["basketball"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1095,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHEQUERED FLAG","unified":"1F3C1","non_qualified":null,"docomo":"E659","au":"E4B9","softbank":"E132","google":"FE7D7","image":"1f3c1.png","sheet_x":8,"sheet_y":1,"short_name":"checkered_flag","short_names":["checkered_flag"],"text":null,"texts":null,"category":"Flags","subcategory":"flag","sort_order":1635,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SNOWBOARDER","unified":"1F3C2","non_qualified":null,"docomo":"E712","au":"E4B8","softbank":null,"google":"FE7D8","image":"1f3c2.png","sheet_x":8,"sheet_y":2,"short_name":"snowboarder","short_names":["snowboarder"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":462,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3C2-1F3FB","non_qualified":null,"image":"1f3c2-1f3fb.png","sheet_x":8,"sheet_y":3,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3C2-1F3FC","non_qualified":null,"image":"1f3c2-1f3fc.png","sheet_x":8,"sheet_y":4,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3C2-1F3FD","non_qualified":null,"image":"1f3c2-1f3fd.png","sheet_x":8,"sheet_y":5,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3C2-1F3FE","non_qualified":null,"image":"1f3c2-1f3fe.png","sheet_x":8,"sheet_y":6,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3C2-1F3FF","non_qualified":null,"image":"1f3c2-1f3ff.png","sheet_x":8,"sheet_y":7,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN RUNNING","unified":"1F3C3-200D-2640-FE0F","non_qualified":"1F3C3-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c3-200d-2640-fe0f.png","sheet_x":8,"sheet_y":8,"short_name":"woman-running","short_names":["woman-running"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":443,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3C3-1F3FB-200D-2640-FE0F","non_qualified":"1F3C3-1F3FB-200D-2640","image":"1f3c3-1f3fb-200d-2640-fe0f.png","sheet_x":8,"sheet_y":9,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3C3-1F3FC-200D-2640-FE0F","non_qualified":"1F3C3-1F3FC-200D-2640","image":"1f3c3-1f3fc-200d-2640-fe0f.png","sheet_x":8,"sheet_y":10,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3C3-1F3FD-200D-2640-FE0F","non_qualified":"1F3C3-1F3FD-200D-2640","image":"1f3c3-1f3fd-200d-2640-fe0f.png","sheet_x":8,"sheet_y":11,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3C3-1F3FE-200D-2640-FE0F","non_qualified":"1F3C3-1F3FE-200D-2640","image":"1f3c3-1f3fe-200d-2640-fe0f.png","sheet_x":8,"sheet_y":12,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3C3-1F3FF-200D-2640-FE0F","non_qualified":"1F3C3-1F3FF-200D-2640","image":"1f3c3-1f3ff-200d-2640-fe0f.png","sheet_x":8,"sheet_y":13,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN RUNNING FACING RIGHT","unified":"1F3C3-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-200D-2640-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c3-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":14,"short_name":"woman_running_facing_right","short_names":["woman_running_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":445,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F3C3-1F3FB-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FB-200D-2640-200D-27A1","image":"1f3c3-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":15,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F3C3-1F3FC-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FC-200D-2640-200D-27A1","image":"1f3c3-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":16,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F3C3-1F3FD-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FD-200D-2640-200D-27A1","image":"1f3c3-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":17,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F3C3-1F3FE-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FE-200D-2640-200D-27A1","image":"1f3c3-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":18,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F3C3-1F3FF-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FF-200D-2640-200D-27A1","image":"1f3c3-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":19,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"MAN RUNNING","unified":"1F3C3-200D-2642-FE0F","non_qualified":"1F3C3-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c3-200d-2642-fe0f.png","sheet_x":8,"sheet_y":20,"short_name":"man-running","short_names":["man-running"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":442,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3C3-1F3FB-200D-2642-FE0F","non_qualified":"1F3C3-1F3FB-200D-2642","image":"1f3c3-1f3fb-200d-2642-fe0f.png","sheet_x":8,"sheet_y":21,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3C3-1F3FC-200D-2642-FE0F","non_qualified":"1F3C3-1F3FC-200D-2642","image":"1f3c3-1f3fc-200d-2642-fe0f.png","sheet_x":8,"sheet_y":22,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3C3-1F3FD-200D-2642-FE0F","non_qualified":"1F3C3-1F3FD-200D-2642","image":"1f3c3-1f3fd-200d-2642-fe0f.png","sheet_x":8,"sheet_y":23,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3C3-1F3FE-200D-2642-FE0F","non_qualified":"1F3C3-1F3FE-200D-2642","image":"1f3c3-1f3fe-200d-2642-fe0f.png","sheet_x":8,"sheet_y":24,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3C3-1F3FF-200D-2642-FE0F","non_qualified":"1F3C3-1F3FF-200D-2642","image":"1f3c3-1f3ff-200d-2642-fe0f.png","sheet_x":8,"sheet_y":25,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F3C3"},{"name":"MAN RUNNING FACING RIGHT","unified":"1F3C3-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-200D-2642-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c3-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":26,"short_name":"man_running_facing_right","short_names":["man_running_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":446,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F3C3-1F3FB-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FB-200D-2642-200D-27A1","image":"1f3c3-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":27,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F3C3-1F3FC-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FC-200D-2642-200D-27A1","image":"1f3c3-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":28,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F3C3-1F3FD-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FD-200D-2642-200D-27A1","image":"1f3c3-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":29,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F3C3-1F3FE-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FE-200D-2642-200D-27A1","image":"1f3c3-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":30,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F3C3-1F3FF-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FF-200D-2642-200D-27A1","image":"1f3c3-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":31,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"PERSON RUNNING FACING RIGHT","unified":"1F3C3-200D-27A1-FE0F","non_qualified":"1F3C3-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c3-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":32,"short_name":"person_running_facing_right","short_names":["person_running_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":444,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F3C3-1F3FB-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FB-200D-27A1","image":"1f3c3-1f3fb-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":33,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F3C3-1F3FC-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FC-200D-27A1","image":"1f3c3-1f3fc-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":34,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F3C3-1F3FD-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FD-200D-27A1","image":"1f3c3-1f3fd-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":35,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F3C3-1F3FE-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FE-200D-27A1","image":"1f3c3-1f3fe-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":36,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F3C3-1F3FF-200D-27A1-FE0F","non_qualified":"1F3C3-1F3FF-200D-27A1","image":"1f3c3-1f3ff-200d-27a1-fe0f.png","sheet_x":8,"sheet_y":37,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"RUNNER","unified":"1F3C3","non_qualified":null,"docomo":"E733","au":"E46B","softbank":"E115","google":"FE7D9","image":"1f3c3.png","sheet_x":8,"sheet_y":38,"short_name":"runner","short_names":["runner","running"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":441,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3C3-1F3FB","non_qualified":null,"image":"1f3c3-1f3fb.png","sheet_x":8,"sheet_y":39,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3C3-1F3FC","non_qualified":null,"image":"1f3c3-1f3fc.png","sheet_x":8,"sheet_y":40,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3C3-1F3FD","non_qualified":null,"image":"1f3c3-1f3fd.png","sheet_x":8,"sheet_y":41,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3C3-1F3FE","non_qualified":null,"image":"1f3c3-1f3fe.png","sheet_x":8,"sheet_y":42,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3C3-1F3FF","non_qualified":null,"image":"1f3c3-1f3ff.png","sheet_x":8,"sheet_y":43,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F3C3-200D-2642-FE0F"},{"name":"WOMAN SURFING","unified":"1F3C4-200D-2640-FE0F","non_qualified":"1F3C4-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c4-200d-2640-fe0f.png","sheet_x":8,"sheet_y":44,"short_name":"woman-surfing","short_names":["woman-surfing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":468,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3C4-1F3FB-200D-2640-FE0F","non_qualified":"1F3C4-1F3FB-200D-2640","image":"1f3c4-1f3fb-200d-2640-fe0f.png","sheet_x":8,"sheet_y":45,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3C4-1F3FC-200D-2640-FE0F","non_qualified":"1F3C4-1F3FC-200D-2640","image":"1f3c4-1f3fc-200d-2640-fe0f.png","sheet_x":8,"sheet_y":46,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3C4-1F3FD-200D-2640-FE0F","non_qualified":"1F3C4-1F3FD-200D-2640","image":"1f3c4-1f3fd-200d-2640-fe0f.png","sheet_x":8,"sheet_y":47,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3C4-1F3FE-200D-2640-FE0F","non_qualified":"1F3C4-1F3FE-200D-2640","image":"1f3c4-1f3fe-200d-2640-fe0f.png","sheet_x":8,"sheet_y":48,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3C4-1F3FF-200D-2640-FE0F","non_qualified":"1F3C4-1F3FF-200D-2640","image":"1f3c4-1f3ff-200d-2640-fe0f.png","sheet_x":8,"sheet_y":49,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN SURFING","unified":"1F3C4-200D-2642-FE0F","non_qualified":"1F3C4-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c4-200d-2642-fe0f.png","sheet_x":8,"sheet_y":50,"short_name":"man-surfing","short_names":["man-surfing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":467,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3C4-1F3FB-200D-2642-FE0F","non_qualified":"1F3C4-1F3FB-200D-2642","image":"1f3c4-1f3fb-200d-2642-fe0f.png","sheet_x":8,"sheet_y":51,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3C4-1F3FC-200D-2642-FE0F","non_qualified":"1F3C4-1F3FC-200D-2642","image":"1f3c4-1f3fc-200d-2642-fe0f.png","sheet_x":8,"sheet_y":52,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3C4-1F3FD-200D-2642-FE0F","non_qualified":"1F3C4-1F3FD-200D-2642","image":"1f3c4-1f3fd-200d-2642-fe0f.png","sheet_x":8,"sheet_y":53,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3C4-1F3FE-200D-2642-FE0F","non_qualified":"1F3C4-1F3FE-200D-2642","image":"1f3c4-1f3fe-200d-2642-fe0f.png","sheet_x":8,"sheet_y":54,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3C4-1F3FF-200D-2642-FE0F","non_qualified":"1F3C4-1F3FF-200D-2642","image":"1f3c4-1f3ff-200d-2642-fe0f.png","sheet_x":8,"sheet_y":55,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F3C4"},{"name":"SURFER","unified":"1F3C4","non_qualified":null,"docomo":"E712","au":"EB41","softbank":"E017","google":"FE7DA","image":"1f3c4.png","sheet_x":8,"sheet_y":56,"short_name":"surfer","short_names":["surfer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":466,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3C4-1F3FB","non_qualified":null,"image":"1f3c4-1f3fb.png","sheet_x":8,"sheet_y":57,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3C4-1F3FC","non_qualified":null,"image":"1f3c4-1f3fc.png","sheet_x":8,"sheet_y":58,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3C4-1F3FD","non_qualified":null,"image":"1f3c4-1f3fd.png","sheet_x":8,"sheet_y":59,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3C4-1F3FE","non_qualified":null,"image":"1f3c4-1f3fe.png","sheet_x":8,"sheet_y":60,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3C4-1F3FF","non_qualified":null,"image":"1f3c4-1f3ff.png","sheet_x":8,"sheet_y":61,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F3C4-200D-2642-FE0F"},{"name":"SPORTS MEDAL","unified":"1F3C5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c5.png","sheet_x":9,"sheet_y":0,"short_name":"sports_medal","short_names":["sports_medal"],"text":null,"texts":null,"category":"Activities","subcategory":"award-medal","sort_order":1088,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TROPHY","unified":"1F3C6","non_qualified":null,"docomo":null,"au":"E5D3","softbank":"E131","google":"FE7DB","image":"1f3c6.png","sheet_x":9,"sheet_y":1,"short_name":"trophy","short_names":["trophy"],"text":null,"texts":null,"category":"Activities","subcategory":"award-medal","sort_order":1087,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HORSE RACING","unified":"1F3C7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c7.png","sheet_x":9,"sheet_y":2,"short_name":"horse_racing","short_names":["horse_racing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":460,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3C7-1F3FB","non_qualified":null,"image":"1f3c7-1f3fb.png","sheet_x":9,"sheet_y":3,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3C7-1F3FC","non_qualified":null,"image":"1f3c7-1f3fc.png","sheet_x":9,"sheet_y":4,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3C7-1F3FD","non_qualified":null,"image":"1f3c7-1f3fd.png","sheet_x":9,"sheet_y":5,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3C7-1F3FE","non_qualified":null,"image":"1f3c7-1f3fe.png","sheet_x":9,"sheet_y":6,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3C7-1F3FF","non_qualified":null,"image":"1f3c7-1f3ff.png","sheet_x":9,"sheet_y":7,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"AMERICAN FOOTBALL","unified":"1F3C8","non_qualified":null,"docomo":null,"au":"E4BB","softbank":"E42B","google":"FE7DD","image":"1f3c8.png","sheet_x":9,"sheet_y":8,"short_name":"football","short_names":["football"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1097,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RUGBY FOOTBALL","unified":"1F3C9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c9.png","sheet_x":9,"sheet_y":9,"short_name":"rugby_football","short_names":["rugby_football"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1098,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN SWIMMING","unified":"1F3CA-200D-2640-FE0F","non_qualified":"1F3CA-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3ca-200d-2640-fe0f.png","sheet_x":9,"sheet_y":10,"short_name":"woman-swimming","short_names":["woman-swimming"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":474,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3CA-1F3FB-200D-2640-FE0F","non_qualified":"1F3CA-1F3FB-200D-2640","image":"1f3ca-1f3fb-200d-2640-fe0f.png","sheet_x":9,"sheet_y":11,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3CA-1F3FC-200D-2640-FE0F","non_qualified":"1F3CA-1F3FC-200D-2640","image":"1f3ca-1f3fc-200d-2640-fe0f.png","sheet_x":9,"sheet_y":12,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3CA-1F3FD-200D-2640-FE0F","non_qualified":"1F3CA-1F3FD-200D-2640","image":"1f3ca-1f3fd-200d-2640-fe0f.png","sheet_x":9,"sheet_y":13,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3CA-1F3FE-200D-2640-FE0F","non_qualified":"1F3CA-1F3FE-200D-2640","image":"1f3ca-1f3fe-200d-2640-fe0f.png","sheet_x":9,"sheet_y":14,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3CA-1F3FF-200D-2640-FE0F","non_qualified":"1F3CA-1F3FF-200D-2640","image":"1f3ca-1f3ff-200d-2640-fe0f.png","sheet_x":9,"sheet_y":15,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN SWIMMING","unified":"1F3CA-200D-2642-FE0F","non_qualified":"1F3CA-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3ca-200d-2642-fe0f.png","sheet_x":9,"sheet_y":16,"short_name":"man-swimming","short_names":["man-swimming"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":473,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3CA-1F3FB-200D-2642-FE0F","non_qualified":"1F3CA-1F3FB-200D-2642","image":"1f3ca-1f3fb-200d-2642-fe0f.png","sheet_x":9,"sheet_y":17,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3CA-1F3FC-200D-2642-FE0F","non_qualified":"1F3CA-1F3FC-200D-2642","image":"1f3ca-1f3fc-200d-2642-fe0f.png","sheet_x":9,"sheet_y":18,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3CA-1F3FD-200D-2642-FE0F","non_qualified":"1F3CA-1F3FD-200D-2642","image":"1f3ca-1f3fd-200d-2642-fe0f.png","sheet_x":9,"sheet_y":19,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3CA-1F3FE-200D-2642-FE0F","non_qualified":"1F3CA-1F3FE-200D-2642","image":"1f3ca-1f3fe-200d-2642-fe0f.png","sheet_x":9,"sheet_y":20,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3CA-1F3FF-200D-2642-FE0F","non_qualified":"1F3CA-1F3FF-200D-2642","image":"1f3ca-1f3ff-200d-2642-fe0f.png","sheet_x":9,"sheet_y":21,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F3CA"},{"name":"SWIMMER","unified":"1F3CA","non_qualified":null,"docomo":null,"au":"EADE","softbank":"E42D","google":"FE7DE","image":"1f3ca.png","sheet_x":9,"sheet_y":22,"short_name":"swimmer","short_names":["swimmer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":472,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3CA-1F3FB","non_qualified":null,"image":"1f3ca-1f3fb.png","sheet_x":9,"sheet_y":23,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3CA-1F3FC","non_qualified":null,"image":"1f3ca-1f3fc.png","sheet_x":9,"sheet_y":24,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3CA-1F3FD","non_qualified":null,"image":"1f3ca-1f3fd.png","sheet_x":9,"sheet_y":25,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3CA-1F3FE","non_qualified":null,"image":"1f3ca-1f3fe.png","sheet_x":9,"sheet_y":26,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3CA-1F3FF","non_qualified":null,"image":"1f3ca-1f3ff.png","sheet_x":9,"sheet_y":27,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F3CA-200D-2642-FE0F"},{"name":"WOMAN LIFTING WEIGHTS","unified":"1F3CB-FE0F-200D-2640-FE0F","non_qualified":"1F3CB-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cb-fe0f-200d-2640-fe0f.png","sheet_x":9,"sheet_y":28,"short_name":"woman-lifting-weights","short_names":["woman-lifting-weights"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":480,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F3CB-1F3FB-200D-2640-FE0F","non_qualified":"1F3CB-1F3FB-200D-2640","image":"1f3cb-1f3fb-200d-2640-fe0f.png","sheet_x":9,"sheet_y":29,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3CB-1F3FC-200D-2640-FE0F","non_qualified":"1F3CB-1F3FC-200D-2640","image":"1f3cb-1f3fc-200d-2640-fe0f.png","sheet_x":9,"sheet_y":30,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3CB-1F3FD-200D-2640-FE0F","non_qualified":"1F3CB-1F3FD-200D-2640","image":"1f3cb-1f3fd-200d-2640-fe0f.png","sheet_x":9,"sheet_y":31,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3CB-1F3FE-200D-2640-FE0F","non_qualified":"1F3CB-1F3FE-200D-2640","image":"1f3cb-1f3fe-200d-2640-fe0f.png","sheet_x":9,"sheet_y":32,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3CB-1F3FF-200D-2640-FE0F","non_qualified":"1F3CB-1F3FF-200D-2640","image":"1f3cb-1f3ff-200d-2640-fe0f.png","sheet_x":9,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN LIFTING WEIGHTS","unified":"1F3CB-FE0F-200D-2642-FE0F","non_qualified":"1F3CB-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cb-fe0f-200d-2642-fe0f.png","sheet_x":9,"sheet_y":34,"short_name":"man-lifting-weights","short_names":["man-lifting-weights"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":479,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F3CB-1F3FB-200D-2642-FE0F","non_qualified":"1F3CB-1F3FB-200D-2642","image":"1f3cb-1f3fb-200d-2642-fe0f.png","sheet_x":9,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3CB-1F3FC-200D-2642-FE0F","non_qualified":"1F3CB-1F3FC-200D-2642","image":"1f3cb-1f3fc-200d-2642-fe0f.png","sheet_x":9,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3CB-1F3FD-200D-2642-FE0F","non_qualified":"1F3CB-1F3FD-200D-2642","image":"1f3cb-1f3fd-200d-2642-fe0f.png","sheet_x":9,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3CB-1F3FE-200D-2642-FE0F","non_qualified":"1F3CB-1F3FE-200D-2642","image":"1f3cb-1f3fe-200d-2642-fe0f.png","sheet_x":9,"sheet_y":38,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3CB-1F3FF-200D-2642-FE0F","non_qualified":"1F3CB-1F3FF-200D-2642","image":"1f3cb-1f3ff-200d-2642-fe0f.png","sheet_x":9,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F3CB-FE0F"},{"name":"PERSON LIFTING WEIGHTS","unified":"1F3CB-FE0F","non_qualified":"1F3CB","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cb-fe0f.png","sheet_x":9,"sheet_y":40,"short_name":"weight_lifter","short_names":["weight_lifter"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":478,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3CB-1F3FB","non_qualified":null,"image":"1f3cb-1f3fb.png","sheet_x":9,"sheet_y":41,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3CB-1F3FC","non_qualified":null,"image":"1f3cb-1f3fc.png","sheet_x":9,"sheet_y":42,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3CB-1F3FD","non_qualified":null,"image":"1f3cb-1f3fd.png","sheet_x":9,"sheet_y":43,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3CB-1F3FE","non_qualified":null,"image":"1f3cb-1f3fe.png","sheet_x":9,"sheet_y":44,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3CB-1F3FF","non_qualified":null,"image":"1f3cb-1f3ff.png","sheet_x":9,"sheet_y":45,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F3CB-FE0F-200D-2642-FE0F"},{"name":"WOMAN GOLFING","unified":"1F3CC-FE0F-200D-2640-FE0F","non_qualified":"1F3CC-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cc-fe0f-200d-2640-fe0f.png","sheet_x":9,"sheet_y":46,"short_name":"woman-golfing","short_names":["woman-golfing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":465,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F3CC-1F3FB-200D-2640-FE0F","non_qualified":"1F3CC-1F3FB-200D-2640","image":"1f3cc-1f3fb-200d-2640-fe0f.png","sheet_x":9,"sheet_y":47,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3CC-1F3FC-200D-2640-FE0F","non_qualified":"1F3CC-1F3FC-200D-2640","image":"1f3cc-1f3fc-200d-2640-fe0f.png","sheet_x":9,"sheet_y":48,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3CC-1F3FD-200D-2640-FE0F","non_qualified":"1F3CC-1F3FD-200D-2640","image":"1f3cc-1f3fd-200d-2640-fe0f.png","sheet_x":9,"sheet_y":49,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3CC-1F3FE-200D-2640-FE0F","non_qualified":"1F3CC-1F3FE-200D-2640","image":"1f3cc-1f3fe-200d-2640-fe0f.png","sheet_x":9,"sheet_y":50,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3CC-1F3FF-200D-2640-FE0F","non_qualified":"1F3CC-1F3FF-200D-2640","image":"1f3cc-1f3ff-200d-2640-fe0f.png","sheet_x":9,"sheet_y":51,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN GOLFING","unified":"1F3CC-FE0F-200D-2642-FE0F","non_qualified":"1F3CC-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cc-fe0f-200d-2642-fe0f.png","sheet_x":9,"sheet_y":52,"short_name":"man-golfing","short_names":["man-golfing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":464,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F3CC-1F3FB-200D-2642-FE0F","non_qualified":"1F3CC-1F3FB-200D-2642","image":"1f3cc-1f3fb-200d-2642-fe0f.png","sheet_x":9,"sheet_y":53,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3CC-1F3FC-200D-2642-FE0F","non_qualified":"1F3CC-1F3FC-200D-2642","image":"1f3cc-1f3fc-200d-2642-fe0f.png","sheet_x":9,"sheet_y":54,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3CC-1F3FD-200D-2642-FE0F","non_qualified":"1F3CC-1F3FD-200D-2642","image":"1f3cc-1f3fd-200d-2642-fe0f.png","sheet_x":9,"sheet_y":55,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3CC-1F3FE-200D-2642-FE0F","non_qualified":"1F3CC-1F3FE-200D-2642","image":"1f3cc-1f3fe-200d-2642-fe0f.png","sheet_x":9,"sheet_y":56,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3CC-1F3FF-200D-2642-FE0F","non_qualified":"1F3CC-1F3FF-200D-2642","image":"1f3cc-1f3ff-200d-2642-fe0f.png","sheet_x":9,"sheet_y":57,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F3CC-FE0F"},{"name":"PERSON GOLFING","unified":"1F3CC-FE0F","non_qualified":"1F3CC","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cc-fe0f.png","sheet_x":9,"sheet_y":58,"short_name":"golfer","short_names":["golfer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":463,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F3CC-1F3FB","non_qualified":null,"image":"1f3cc-1f3fb.png","sheet_x":9,"sheet_y":59,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F3CC-1F3FC","non_qualified":null,"image":"1f3cc-1f3fc.png","sheet_x":9,"sheet_y":60,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F3CC-1F3FD","non_qualified":null,"image":"1f3cc-1f3fd.png","sheet_x":9,"sheet_y":61,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F3CC-1F3FE","non_qualified":null,"image":"1f3cc-1f3fe.png","sheet_x":10,"sheet_y":0,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F3CC-1F3FF","non_qualified":null,"image":"1f3cc-1f3ff.png","sheet_x":10,"sheet_y":1,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F3CC-FE0F-200D-2642-FE0F"},{"name":"MOTORCYCLE","unified":"1F3CD-FE0F","non_qualified":"1F3CD","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cd-fe0f.png","sheet_x":10,"sheet_y":2,"short_name":"racing_motorcycle","short_names":["racing_motorcycle"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":943,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RACING CAR","unified":"1F3CE-FE0F","non_qualified":"1F3CE","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3ce-fe0f.png","sheet_x":10,"sheet_y":3,"short_name":"racing_car","short_names":["racing_car"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":942,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CRICKET BAT AND BALL","unified":"1F3CF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cf.png","sheet_x":10,"sheet_y":4,"short_name":"cricket_bat_and_ball","short_names":["cricket_bat_and_ball"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1102,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"VOLLEYBALL","unified":"1F3D0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d0.png","sheet_x":10,"sheet_y":5,"short_name":"volleyball","short_names":["volleyball"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1096,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FIELD HOCKEY STICK AND BALL","unified":"1F3D1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d1.png","sheet_x":10,"sheet_y":6,"short_name":"field_hockey_stick_and_ball","short_names":["field_hockey_stick_and_ball"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1103,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ICE HOCKEY STICK AND PUCK","unified":"1F3D2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d2.png","sheet_x":10,"sheet_y":7,"short_name":"ice_hockey_stick_and_puck","short_names":["ice_hockey_stick_and_puck"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1104,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TABLE TENNIS PADDLE AND BALL","unified":"1F3D3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d3.png","sheet_x":10,"sheet_y":8,"short_name":"table_tennis_paddle_and_ball","short_names":["table_tennis_paddle_and_ball"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1106,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SNOW-CAPPED MOUNTAIN","unified":"1F3D4-FE0F","non_qualified":"1F3D4","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d4-fe0f.png","sheet_x":10,"sheet_y":9,"short_name":"snow_capped_mountain","short_names":["snow_capped_mountain"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-geographic","sort_order":854,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CAMPING","unified":"1F3D5-FE0F","non_qualified":"1F3D5","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d5-fe0f.png","sheet_x":10,"sheet_y":10,"short_name":"camping","short_names":["camping"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-geographic","sort_order":858,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BEACH WITH UMBRELLA","unified":"1F3D6-FE0F","non_qualified":"1F3D6","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d6-fe0f.png","sheet_x":10,"sheet_y":11,"short_name":"beach_with_umbrella","short_names":["beach_with_umbrella"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-geographic","sort_order":859,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUILDING CONSTRUCTION","unified":"1F3D7-FE0F","non_qualified":"1F3D7","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d7-fe0f.png","sheet_x":10,"sheet_y":12,"short_name":"building_construction","short_names":["building_construction"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":865,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOUSES","unified":"1F3D8-FE0F","non_qualified":"1F3D8","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d8-fe0f.png","sheet_x":10,"sheet_y":13,"short_name":"house_buildings","short_names":["house_buildings"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":870,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CITYSCAPE","unified":"1F3D9-FE0F","non_qualified":"1F3D9","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d9-fe0f.png","sheet_x":10,"sheet_y":14,"short_name":"cityscape","short_names":["cityscape"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":900,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DERELICT HOUSE","unified":"1F3DA-FE0F","non_qualified":"1F3DA","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3da-fe0f.png","sheet_x":10,"sheet_y":15,"short_name":"derelict_house_building","short_names":["derelict_house_building"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":871,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLASSICAL BUILDING","unified":"1F3DB-FE0F","non_qualified":"1F3DB","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3db-fe0f.png","sheet_x":10,"sheet_y":16,"short_name":"classical_building","short_names":["classical_building"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":864,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DESERT","unified":"1F3DC-FE0F","non_qualified":"1F3DC","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3dc-fe0f.png","sheet_x":10,"sheet_y":17,"short_name":"desert","short_names":["desert"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-geographic","sort_order":860,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DESERT ISLAND","unified":"1F3DD-FE0F","non_qualified":"1F3DD","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3dd-fe0f.png","sheet_x":10,"sheet_y":18,"short_name":"desert_island","short_names":["desert_island"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-geographic","sort_order":861,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NATIONAL PARK","unified":"1F3DE-FE0F","non_qualified":"1F3DE","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3de-fe0f.png","sheet_x":10,"sheet_y":19,"short_name":"national_park","short_names":["national_park"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-geographic","sort_order":862,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STADIUM","unified":"1F3DF-FE0F","non_qualified":"1F3DF","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3df-fe0f.png","sheet_x":10,"sheet_y":20,"short_name":"stadium","short_names":["stadium"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":863,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOUSE BUILDING","unified":"1F3E0","non_qualified":null,"docomo":"E663","au":"E4AB","softbank":"E036","google":"FE4B0","image":"1f3e0.png","sheet_x":10,"sheet_y":21,"short_name":"house","short_names":["house"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":872,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOUSE WITH GARDEN","unified":"1F3E1","non_qualified":null,"docomo":"E663","au":"EB09","softbank":null,"google":"FE4B1","image":"1f3e1.png","sheet_x":10,"sheet_y":22,"short_name":"house_with_garden","short_names":["house_with_garden"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":873,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OFFICE BUILDING","unified":"1F3E2","non_qualified":null,"docomo":"E664","au":"E4AD","softbank":"E038","google":"FE4B2","image":"1f3e2.png","sheet_x":10,"sheet_y":23,"short_name":"office","short_names":["office"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":874,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JAPANESE POST OFFICE","unified":"1F3E3","non_qualified":null,"docomo":"E665","au":"E5DE","softbank":"E153","google":"FE4B3","image":"1f3e3.png","sheet_x":10,"sheet_y":24,"short_name":"post_office","short_names":["post_office"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":875,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EUROPEAN POST OFFICE","unified":"1F3E4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3e4.png","sheet_x":10,"sheet_y":25,"short_name":"european_post_office","short_names":["european_post_office"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":876,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOSPITAL","unified":"1F3E5","non_qualified":null,"docomo":"E666","au":"E5DF","softbank":"E155","google":"FE4B4","image":"1f3e5.png","sheet_x":10,"sheet_y":26,"short_name":"hospital","short_names":["hospital"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":877,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BANK","unified":"1F3E6","non_qualified":null,"docomo":"E667","au":"E4AA","softbank":"E14D","google":"FE4B5","image":"1f3e6.png","sheet_x":10,"sheet_y":27,"short_name":"bank","short_names":["bank"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":878,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AUTOMATED TELLER MACHINE","unified":"1F3E7","non_qualified":null,"docomo":"E668","au":"E4A3","softbank":"E154","google":"FE4B6","image":"1f3e7.png","sheet_x":10,"sheet_y":28,"short_name":"atm","short_names":["atm"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1412,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOTEL","unified":"1F3E8","non_qualified":null,"docomo":"E669","au":"EA81","softbank":"E158","google":"FE4B7","image":"1f3e8.png","sheet_x":10,"sheet_y":29,"short_name":"hotel","short_names":["hotel"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":879,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOVE HOTEL","unified":"1F3E9","non_qualified":null,"docomo":"E669-E6EF","au":"EAF3","softbank":"E501","google":"FE4B8","image":"1f3e9.png","sheet_x":10,"sheet_y":30,"short_name":"love_hotel","short_names":["love_hotel"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":880,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CONVENIENCE STORE","unified":"1F3EA","non_qualified":null,"docomo":"E66A","au":"E4A4","softbank":"E156","google":"FE4B9","image":"1f3ea.png","sheet_x":10,"sheet_y":31,"short_name":"convenience_store","short_names":["convenience_store"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":881,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SCHOOL","unified":"1F3EB","non_qualified":null,"docomo":"E73E","au":"EA80","softbank":"E157","google":"FE4BA","image":"1f3eb.png","sheet_x":10,"sheet_y":32,"short_name":"school","short_names":["school"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":882,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DEPARTMENT STORE","unified":"1F3EC","non_qualified":null,"docomo":null,"au":"EAF6","softbank":"E504","google":"FE4BD","image":"1f3ec.png","sheet_x":10,"sheet_y":33,"short_name":"department_store","short_names":["department_store"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":883,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACTORY","unified":"1F3ED","non_qualified":null,"docomo":null,"au":"EAF9","softbank":"E508","google":"FE4C0","image":"1f3ed.png","sheet_x":10,"sheet_y":34,"short_name":"factory","short_names":["factory"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":884,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"IZAKAYA LANTERN","unified":"1F3EE","non_qualified":null,"docomo":"E74B","au":"E4BD","softbank":null,"google":"FE4C2","image":"1f3ee.png","sheet_x":10,"sheet_y":35,"short_name":"izakaya_lantern","short_names":["izakaya_lantern","lantern"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1260,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JAPANESE CASTLE","unified":"1F3EF","non_qualified":null,"docomo":null,"au":"EAF7","softbank":"E505","google":"FE4BE","image":"1f3ef.png","sheet_x":10,"sheet_y":36,"short_name":"japanese_castle","short_names":["japanese_castle"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":885,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EUROPEAN CASTLE","unified":"1F3F0","non_qualified":null,"docomo":null,"au":"EAF8","softbank":"E506","google":"FE4BF","image":"1f3f0.png","sheet_x":10,"sheet_y":37,"short_name":"european_castle","short_names":["european_castle"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":886,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RAINBOW FLAG","unified":"1F3F3-FE0F-200D-1F308","non_qualified":"1F3F3-200D-1F308","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f3-fe0f-200d-1f308.png","sheet_x":10,"sheet_y":38,"short_name":"rainbow-flag","short_names":["rainbow-flag"],"text":null,"texts":null,"category":"Flags","subcategory":"flag","sort_order":1640,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRANSGENDER FLAG","unified":"1F3F3-FE0F-200D-26A7-FE0F","non_qualified":"1F3F3-200D-26A7","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f3-fe0f-200d-26a7-fe0f.png","sheet_x":10,"sheet_y":39,"short_name":"transgender_flag","short_names":["transgender_flag"],"text":null,"texts":null,"category":"Flags","subcategory":"flag","sort_order":1641,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"WHITE FLAG","unified":"1F3F3-FE0F","non_qualified":"1F3F3","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f3-fe0f.png","sheet_x":10,"sheet_y":40,"short_name":"waving_white_flag","short_names":["waving_white_flag"],"text":null,"texts":null,"category":"Flags","subcategory":"flag","sort_order":1639,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PIRATE FLAG","unified":"1F3F4-200D-2620-FE0F","non_qualified":"1F3F4-200D-2620","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f4-200d-2620-fe0f.png","sheet_x":10,"sheet_y":41,"short_name":"pirate_flag","short_names":["pirate_flag"],"text":null,"texts":null,"category":"Flags","subcategory":"flag","sort_order":1642,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"England Flag","unified":"1F3F4-E0067-E0062-E0065-E006E-E0067-E007F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png","sheet_x":10,"sheet_y":42,"short_name":"flag-england","short_names":["flag-england"],"text":null,"texts":null,"category":"Flags","subcategory":"subdivision-flag","sort_order":1901,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Scotland Flag","unified":"1F3F4-E0067-E0062-E0073-E0063-E0074-E007F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png","sheet_x":10,"sheet_y":43,"short_name":"flag-scotland","short_names":["flag-scotland"],"text":null,"texts":null,"category":"Flags","subcategory":"subdivision-flag","sort_order":1902,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"Wales Flag","unified":"1F3F4-E0067-E0062-E0077-E006C-E0073-E007F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png","sheet_x":10,"sheet_y":44,"short_name":"flag-wales","short_names":["flag-wales"],"text":null,"texts":null,"category":"Flags","subcategory":"subdivision-flag","sort_order":1903,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WAVING BLACK FLAG","unified":"1F3F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f4.png","sheet_x":10,"sheet_y":45,"short_name":"waving_black_flag","short_names":["waving_black_flag"],"text":null,"texts":null,"category":"Flags","subcategory":"flag","sort_order":1638,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROSETTE","unified":"1F3F5-FE0F","non_qualified":"1F3F5","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f5-fe0f.png","sheet_x":10,"sheet_y":46,"short_name":"rosette","short_names":["rosette"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":688,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LABEL","unified":"1F3F7-FE0F","non_qualified":"1F3F7","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f7-fe0f.png","sheet_x":10,"sheet_y":47,"short_name":"label","short_names":["label"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1278,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BADMINTON RACQUET AND SHUTTLECOCK","unified":"1F3F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f8.png","sheet_x":10,"sheet_y":48,"short_name":"badminton_racquet_and_shuttlecock","short_names":["badminton_racquet_and_shuttlecock"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1107,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOW AND ARROW","unified":"1F3F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f9.png","sheet_x":10,"sheet_y":49,"short_name":"bow_and_arrow","short_names":["bow_and_arrow"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1347,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AMPHORA","unified":"1F3FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3fa.png","sheet_x":10,"sheet_y":50,"short_name":"amphora","short_names":["amphora"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"dishware","sort_order":846,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EMOJI MODIFIER FITZPATRICK TYPE-1-2","unified":"1F3FB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3fb.png","sheet_x":10,"sheet_y":51,"short_name":"skin-tone-2","short_names":["skin-tone-2"],"text":null,"texts":null,"category":"Component","subcategory":"skin-tone","sort_order":554,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EMOJI MODIFIER FITZPATRICK TYPE-3","unified":"1F3FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3fc.png","sheet_x":10,"sheet_y":52,"short_name":"skin-tone-3","short_names":["skin-tone-3"],"text":null,"texts":null,"category":"Component","subcategory":"skin-tone","sort_order":555,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EMOJI MODIFIER FITZPATRICK TYPE-4","unified":"1F3FD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3fd.png","sheet_x":10,"sheet_y":53,"short_name":"skin-tone-4","short_names":["skin-tone-4"],"text":null,"texts":null,"category":"Component","subcategory":"skin-tone","sort_order":556,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EMOJI MODIFIER FITZPATRICK TYPE-5","unified":"1F3FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3fe.png","sheet_x":10,"sheet_y":54,"short_name":"skin-tone-5","short_names":["skin-tone-5"],"text":null,"texts":null,"category":"Component","subcategory":"skin-tone","sort_order":557,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EMOJI MODIFIER FITZPATRICK TYPE-6","unified":"1F3FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3ff.png","sheet_x":10,"sheet_y":55,"short_name":"skin-tone-6","short_names":["skin-tone-6"],"text":null,"texts":null,"category":"Component","subcategory":"skin-tone","sort_order":558,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RAT","unified":"1F400","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f400.png","sheet_x":10,"sheet_y":56,"short_name":"rat","short_names":["rat"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":607,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOUSE","unified":"1F401","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f401.png","sheet_x":10,"sheet_y":57,"short_name":"mouse2","short_names":["mouse2"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":606,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OX","unified":"1F402","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f402.png","sheet_x":10,"sheet_y":58,"short_name":"ox","short_names":["ox"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":587,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WATER BUFFALO","unified":"1F403","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f403.png","sheet_x":10,"sheet_y":59,"short_name":"water_buffalo","short_names":["water_buffalo"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":588,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COW","unified":"1F404","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f404.png","sheet_x":10,"sheet_y":60,"short_name":"cow2","short_names":["cow2"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":589,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TIGER","unified":"1F405","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f405.png","sheet_x":10,"sheet_y":61,"short_name":"tiger2","short_names":["tiger2"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":576,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEOPARD","unified":"1F406","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f406.png","sheet_x":11,"sheet_y":0,"short_name":"leopard","short_names":["leopard"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":577,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RABBIT","unified":"1F407","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f407.png","sheet_x":11,"sheet_y":1,"short_name":"rabbit2","short_names":["rabbit2"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":610,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK CAT","unified":"1F408-200D-2B1B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f408-200d-2b1b.png","sheet_x":11,"sheet_y":2,"short_name":"black_cat","short_names":["black_cat"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":573,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CAT","unified":"1F408","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f408.png","sheet_x":11,"sheet_y":3,"short_name":"cat2","short_names":["cat2"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":572,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DRAGON","unified":"1F409","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f409.png","sheet_x":11,"sheet_y":4,"short_name":"dragon","short_names":["dragon"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-reptile","sort_order":653,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CROCODILE","unified":"1F40A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f40a.png","sheet_x":11,"sheet_y":5,"short_name":"crocodile","short_names":["crocodile"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-reptile","sort_order":648,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHALE","unified":"1F40B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f40b.png","sheet_x":11,"sheet_y":6,"short_name":"whale2","short_names":["whale2"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":657,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SNAIL","unified":"1F40C","non_qualified":null,"docomo":"E74E","au":"EB7E","softbank":null,"google":"FE1B9","image":"1f40c.png","sheet_x":11,"sheet_y":7,"short_name":"snail","short_names":["snail"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":668,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SNAKE","unified":"1F40D","non_qualified":null,"docomo":null,"au":"EB22","softbank":"E52D","google":"FE1D3","image":"1f40d.png","sheet_x":11,"sheet_y":8,"short_name":"snake","short_names":["snake"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-reptile","sort_order":651,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HORSE","unified":"1F40E","non_qualified":null,"docomo":"E754","au":"E4D8","softbank":"E134","google":"FE7DC","image":"1f40e.png","sheet_x":11,"sheet_y":9,"short_name":"racehorse","short_names":["racehorse"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":581,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RAM","unified":"1F40F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f40f.png","sheet_x":11,"sheet_y":10,"short_name":"ram","short_names":["ram"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":594,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GOAT","unified":"1F410","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f410.png","sheet_x":11,"sheet_y":11,"short_name":"goat","short_names":["goat"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":596,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHEEP","unified":"1F411","non_qualified":null,"docomo":null,"au":"E48F","softbank":"E529","google":"FE1CF","image":"1f411.png","sheet_x":11,"sheet_y":12,"short_name":"sheep","short_names":["sheep"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":595,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MONKEY","unified":"1F412","non_qualified":null,"docomo":null,"au":"E4D9","softbank":"E528","google":"FE1CE","image":"1f412.png","sheet_x":11,"sheet_y":13,"short_name":"monkey","short_names":["monkey"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":560,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROOSTER","unified":"1F413","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f413.png","sheet_x":11,"sheet_y":14,"short_name":"rooster","short_names":["rooster"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":627,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHICKEN","unified":"1F414","non_qualified":null,"docomo":null,"au":"EB23","softbank":"E52E","google":"FE1D4","image":"1f414.png","sheet_x":11,"sheet_y":15,"short_name":"chicken","short_names":["chicken"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":626,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SERVICE DOG","unified":"1F415-200D-1F9BA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f415-200d-1f9ba.png","sheet_x":11,"sheet_y":16,"short_name":"service_dog","short_names":["service_dog"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":566,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOG","unified":"1F415","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f415.png","sheet_x":11,"sheet_y":17,"short_name":"dog2","short_names":["dog2"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":564,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PIG","unified":"1F416","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f416.png","sheet_x":11,"sheet_y":18,"short_name":"pig2","short_names":["pig2"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":591,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOAR","unified":"1F417","non_qualified":null,"docomo":null,"au":"EB24","softbank":"E52F","google":"FE1D5","image":"1f417.png","sheet_x":11,"sheet_y":19,"short_name":"boar","short_names":["boar"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":592,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ELEPHANT","unified":"1F418","non_qualified":null,"docomo":null,"au":"EB1F","softbank":"E526","google":"FE1CC","image":"1f418.png","sheet_x":11,"sheet_y":20,"short_name":"elephant","short_names":["elephant"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":601,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OCTOPUS","unified":"1F419","non_qualified":null,"docomo":null,"au":"E5C7","softbank":"E10A","google":"FE1C5","image":"1f419.png","sheet_x":11,"sheet_y":21,"short_name":"octopus","short_names":["octopus"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":664,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPIRAL SHELL","unified":"1F41A","non_qualified":null,"docomo":null,"au":"EAEC","softbank":"E441","google":"FE1C6","image":"1f41a.png","sheet_x":11,"sheet_y":22,"short_name":"shell","short_names":["shell"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":665,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUG","unified":"1F41B","non_qualified":null,"docomo":null,"au":"EB1E","softbank":"E525","google":"FE1CB","image":"1f41b.png","sheet_x":11,"sheet_y":23,"short_name":"bug","short_names":["bug"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":670,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ANT","unified":"1F41C","non_qualified":null,"docomo":null,"au":"E4DD","softbank":null,"google":"FE1DA","image":"1f41c.png","sheet_x":11,"sheet_y":24,"short_name":"ant","short_names":["ant"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":671,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HONEYBEE","unified":"1F41D","non_qualified":null,"docomo":null,"au":"EB57","softbank":null,"google":"FE1E1","image":"1f41d.png","sheet_x":11,"sheet_y":25,"short_name":"bee","short_names":["bee","honeybee"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":672,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LADY BEETLE","unified":"1F41E","non_qualified":null,"docomo":null,"au":"EB58","softbank":null,"google":"FE1E2","image":"1f41e.png","sheet_x":11,"sheet_y":26,"short_name":"ladybug","short_names":["ladybug","lady_beetle"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":674,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FISH","unified":"1F41F","non_qualified":null,"docomo":"E751","au":"E49A","softbank":"E019","google":"FE1BD","image":"1f41f.png","sheet_x":11,"sheet_y":27,"short_name":"fish","short_names":["fish"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":660,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TROPICAL FISH","unified":"1F420","non_qualified":null,"docomo":"E751","au":"EB1D","softbank":"E522","google":"FE1C9","image":"1f420.png","sheet_x":11,"sheet_y":28,"short_name":"tropical_fish","short_names":["tropical_fish"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":661,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLOWFISH","unified":"1F421","non_qualified":null,"docomo":"E751","au":"E4D3","softbank":null,"google":"FE1D9","image":"1f421.png","sheet_x":11,"sheet_y":29,"short_name":"blowfish","short_names":["blowfish"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":662,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TURTLE","unified":"1F422","non_qualified":null,"docomo":null,"au":"E5D4","softbank":null,"google":"FE1DC","image":"1f422.png","sheet_x":11,"sheet_y":30,"short_name":"turtle","short_names":["turtle"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-reptile","sort_order":649,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HATCHING CHICK","unified":"1F423","non_qualified":null,"docomo":"E74F","au":"E5DB","softbank":null,"google":"FE1DD","image":"1f423.png","sheet_x":11,"sheet_y":31,"short_name":"hatching_chick","short_names":["hatching_chick"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":628,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BABY CHICK","unified":"1F424","non_qualified":null,"docomo":"E74F","au":"E4E0","softbank":"E523","google":"FE1BA","image":"1f424.png","sheet_x":11,"sheet_y":32,"short_name":"baby_chick","short_names":["baby_chick"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":629,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FRONT-FACING BABY CHICK","unified":"1F425","non_qualified":null,"docomo":"E74F","au":"EB76","softbank":null,"google":"FE1BB","image":"1f425.png","sheet_x":11,"sheet_y":33,"short_name":"hatched_chick","short_names":["hatched_chick"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":630,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PHOENIX","unified":"1F426-200D-1F525","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f426-200d-1f525.png","sheet_x":11,"sheet_y":34,"short_name":"phoenix","short_names":["phoenix"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":646,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},{"name":"BLACK BIRD","unified":"1F426-200D-2B1B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f426-200d-2b1b.png","sheet_x":11,"sheet_y":35,"short_name":"black_bird","short_names":["black_bird"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":644,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BIRD","unified":"1F426","non_qualified":null,"docomo":"E74F","au":"E4E0","softbank":"E521","google":"FE1C8","image":"1f426.png","sheet_x":11,"sheet_y":36,"short_name":"bird","short_names":["bird"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":631,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PENGUIN","unified":"1F427","non_qualified":null,"docomo":"E750","au":"E4DC","softbank":"E055","google":"FE1BC","image":"1f427.png","sheet_x":11,"sheet_y":37,"short_name":"penguin","short_names":["penguin"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":632,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KOALA","unified":"1F428","non_qualified":null,"docomo":null,"au":"EB20","softbank":"E527","google":"FE1CD","image":"1f428.png","sheet_x":11,"sheet_y":38,"short_name":"koala","short_names":["koala"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":617,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POODLE","unified":"1F429","non_qualified":null,"docomo":"E6A1","au":"E4DF","softbank":null,"google":"FE1D8","image":"1f429.png","sheet_x":11,"sheet_y":39,"short_name":"poodle","short_names":["poodle"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":567,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DROMEDARY CAMEL","unified":"1F42A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f42a.png","sheet_x":11,"sheet_y":40,"short_name":"dromedary_camel","short_names":["dromedary_camel"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":597,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BACTRIAN CAMEL","unified":"1F42B","non_qualified":null,"docomo":null,"au":"EB25","softbank":"E530","google":"FE1D6","image":"1f42b.png","sheet_x":11,"sheet_y":41,"short_name":"camel","short_names":["camel"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":598,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOLPHIN","unified":"1F42C","non_qualified":null,"docomo":null,"au":"EB1B","softbank":"E520","google":"FE1C7","image":"1f42c.png","sheet_x":11,"sheet_y":42,"short_name":"dolphin","short_names":["dolphin","flipper"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":658,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOUSE FACE","unified":"1F42D","non_qualified":null,"docomo":null,"au":"E5C2","softbank":"E053","google":"FE1C2","image":"1f42d.png","sheet_x":11,"sheet_y":43,"short_name":"mouse","short_names":["mouse"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":605,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COW FACE","unified":"1F42E","non_qualified":null,"docomo":null,"au":"EB21","softbank":"E52B","google":"FE1D1","image":"1f42e.png","sheet_x":11,"sheet_y":44,"short_name":"cow","short_names":["cow"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":586,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TIGER FACE","unified":"1F42F","non_qualified":null,"docomo":null,"au":"E5C0","softbank":"E050","google":"FE1C0","image":"1f42f.png","sheet_x":11,"sheet_y":45,"short_name":"tiger","short_names":["tiger"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":575,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RABBIT FACE","unified":"1F430","non_qualified":null,"docomo":null,"au":"E4D7","softbank":"E52C","google":"FE1D2","image":"1f430.png","sheet_x":11,"sheet_y":46,"short_name":"rabbit","short_names":["rabbit"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":609,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CAT FACE","unified":"1F431","non_qualified":null,"docomo":"E6A2","au":"E4DB","softbank":"E04F","google":"FE1B8","image":"1f431.png","sheet_x":11,"sheet_y":47,"short_name":"cat","short_names":["cat"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":571,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DRAGON FACE","unified":"1F432","non_qualified":null,"docomo":null,"au":"EB3F","softbank":null,"google":"FE1DE","image":"1f432.png","sheet_x":11,"sheet_y":48,"short_name":"dragon_face","short_names":["dragon_face"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-reptile","sort_order":652,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPOUTING WHALE","unified":"1F433","non_qualified":null,"docomo":null,"au":"E470","softbank":"E054","google":"FE1C3","image":"1f433.png","sheet_x":11,"sheet_y":49,"short_name":"whale","short_names":["whale"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":656,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HORSE FACE","unified":"1F434","non_qualified":null,"docomo":"E754","au":"E4D8","softbank":"E01A","google":"FE1BE","image":"1f434.png","sheet_x":11,"sheet_y":50,"short_name":"horse","short_names":["horse"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":578,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MONKEY FACE","unified":"1F435","non_qualified":null,"docomo":null,"au":"E4D9","softbank":"E109","google":"FE1C4","image":"1f435.png","sheet_x":11,"sheet_y":51,"short_name":"monkey_face","short_names":["monkey_face"],"text":null,"texts":[":o)"],"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":559,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOG FACE","unified":"1F436","non_qualified":null,"docomo":"E6A1","au":"E4E1","softbank":"E052","google":"FE1B7","image":"1f436.png","sheet_x":11,"sheet_y":52,"short_name":"dog","short_names":["dog"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":563,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PIG FACE","unified":"1F437","non_qualified":null,"docomo":"E755","au":"E4DE","softbank":"E10B","google":"FE1BF","image":"1f437.png","sheet_x":11,"sheet_y":53,"short_name":"pig","short_names":["pig"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":590,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FROG FACE","unified":"1F438","non_qualified":null,"docomo":null,"au":"E4DA","softbank":"E531","google":"FE1D7","image":"1f438.png","sheet_x":11,"sheet_y":54,"short_name":"frog","short_names":["frog"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-amphibian","sort_order":647,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HAMSTER FACE","unified":"1F439","non_qualified":null,"docomo":null,"au":null,"softbank":"E524","google":"FE1CA","image":"1f439.png","sheet_x":11,"sheet_y":55,"short_name":"hamster","short_names":["hamster"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":608,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOLF FACE","unified":"1F43A","non_qualified":null,"docomo":"E6A1","au":"E4E1","softbank":"E52A","google":"FE1D0","image":"1f43a.png","sheet_x":11,"sheet_y":56,"short_name":"wolf","short_names":["wolf"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":568,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POLAR BEAR","unified":"1F43B-200D-2744-FE0F","non_qualified":"1F43B-200D-2744","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f43b-200d-2744-fe0f.png","sheet_x":11,"sheet_y":57,"short_name":"polar_bear","short_names":["polar_bear"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":616,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BEAR FACE","unified":"1F43B","non_qualified":null,"docomo":null,"au":"E5C1","softbank":"E051","google":"FE1C1","image":"1f43b.png","sheet_x":11,"sheet_y":58,"short_name":"bear","short_names":["bear"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":615,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PANDA FACE","unified":"1F43C","non_qualified":null,"docomo":null,"au":"EB46","softbank":null,"google":"FE1DF","image":"1f43c.png","sheet_x":11,"sheet_y":59,"short_name":"panda_face","short_names":["panda_face"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":618,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PIG NOSE","unified":"1F43D","non_qualified":null,"docomo":"E755","au":"EB48","softbank":null,"google":"FE1E0","image":"1f43d.png","sheet_x":11,"sheet_y":60,"short_name":"pig_nose","short_names":["pig_nose"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":593,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PAW PRINTS","unified":"1F43E","non_qualified":null,"docomo":"E698","au":"E4EE","softbank":null,"google":"FE1DB","image":"1f43e.png","sheet_x":11,"sheet_y":61,"short_name":"feet","short_names":["feet","paw_prints"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":624,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHIPMUNK","unified":"1F43F-FE0F","non_qualified":"1F43F","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f43f-fe0f.png","sheet_x":12,"sheet_y":0,"short_name":"chipmunk","short_names":["chipmunk"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":611,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EYES","unified":"1F440","non_qualified":null,"docomo":"E691","au":"E5A4","softbank":"E419","google":"FE190","image":"1f440.png","sheet_x":12,"sheet_y":1,"short_name":"eyes","short_names":["eyes"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":225,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EYE IN SPEECH BUBBLE","unified":"1F441-FE0F-200D-1F5E8-FE0F","non_qualified":"1F441-200D-1F5E8","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f441-fe0f-200d-1f5e8-fe0f.png","sheet_x":12,"sheet_y":2,"short_name":"eye-in-speech-bubble","short_names":["eye-in-speech-bubble"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":164,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false},{"name":"EYE","unified":"1F441-FE0F","non_qualified":"1F441","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f441-fe0f.png","sheet_x":12,"sheet_y":3,"short_name":"eye","short_names":["eye"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":226,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EAR","unified":"1F442","non_qualified":null,"docomo":"E692","au":"E5A5","softbank":"E41B","google":"FE191","image":"1f442.png","sheet_x":12,"sheet_y":4,"short_name":"ear","short_names":["ear"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":217,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F442-1F3FB","non_qualified":null,"image":"1f442-1f3fb.png","sheet_x":12,"sheet_y":5,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F442-1F3FC","non_qualified":null,"image":"1f442-1f3fc.png","sheet_x":12,"sheet_y":6,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F442-1F3FD","non_qualified":null,"image":"1f442-1f3fd.png","sheet_x":12,"sheet_y":7,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F442-1F3FE","non_qualified":null,"image":"1f442-1f3fe.png","sheet_x":12,"sheet_y":8,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F442-1F3FF","non_qualified":null,"image":"1f442-1f3ff.png","sheet_x":12,"sheet_y":9,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"NOSE","unified":"1F443","non_qualified":null,"docomo":null,"au":"EAD0","softbank":"E41A","google":"FE192","image":"1f443.png","sheet_x":12,"sheet_y":10,"short_name":"nose","short_names":["nose"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":219,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F443-1F3FB","non_qualified":null,"image":"1f443-1f3fb.png","sheet_x":12,"sheet_y":11,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F443-1F3FC","non_qualified":null,"image":"1f443-1f3fc.png","sheet_x":12,"sheet_y":12,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F443-1F3FD","non_qualified":null,"image":"1f443-1f3fd.png","sheet_x":12,"sheet_y":13,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F443-1F3FE","non_qualified":null,"image":"1f443-1f3fe.png","sheet_x":12,"sheet_y":14,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F443-1F3FF","non_qualified":null,"image":"1f443-1f3ff.png","sheet_x":12,"sheet_y":15,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MOUTH","unified":"1F444","non_qualified":null,"docomo":"E6F9","au":"EAD1","softbank":"E41C","google":"FE193","image":"1f444.png","sheet_x":12,"sheet_y":16,"short_name":"lips","short_names":["lips"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":228,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TONGUE","unified":"1F445","non_qualified":null,"docomo":"E728","au":"EB47","softbank":null,"google":"FE194","image":"1f445.png","sheet_x":12,"sheet_y":17,"short_name":"tongue","short_names":["tongue"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":227,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE UP POINTING BACKHAND INDEX","unified":"1F446","non_qualified":null,"docomo":null,"au":"EA8D","softbank":"E22E","google":"FEB99","image":"1f446.png","sheet_x":12,"sheet_y":18,"short_name":"point_up_2","short_names":["point_up_2"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-single-finger","sort_order":191,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F446-1F3FB","non_qualified":null,"image":"1f446-1f3fb.png","sheet_x":12,"sheet_y":19,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F446-1F3FC","non_qualified":null,"image":"1f446-1f3fc.png","sheet_x":12,"sheet_y":20,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F446-1F3FD","non_qualified":null,"image":"1f446-1f3fd.png","sheet_x":12,"sheet_y":21,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F446-1F3FE","non_qualified":null,"image":"1f446-1f3fe.png","sheet_x":12,"sheet_y":22,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F446-1F3FF","non_qualified":null,"image":"1f446-1f3ff.png","sheet_x":12,"sheet_y":23,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WHITE DOWN POINTING BACKHAND INDEX","unified":"1F447","non_qualified":null,"docomo":null,"au":"EA8E","softbank":"E22F","google":"FEB9A","image":"1f447.png","sheet_x":12,"sheet_y":24,"short_name":"point_down","short_names":["point_down"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-single-finger","sort_order":193,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F447-1F3FB","non_qualified":null,"image":"1f447-1f3fb.png","sheet_x":12,"sheet_y":25,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F447-1F3FC","non_qualified":null,"image":"1f447-1f3fc.png","sheet_x":12,"sheet_y":26,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F447-1F3FD","non_qualified":null,"image":"1f447-1f3fd.png","sheet_x":12,"sheet_y":27,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F447-1F3FE","non_qualified":null,"image":"1f447-1f3fe.png","sheet_x":12,"sheet_y":28,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F447-1F3FF","non_qualified":null,"image":"1f447-1f3ff.png","sheet_x":12,"sheet_y":29,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WHITE LEFT POINTING BACKHAND INDEX","unified":"1F448","non_qualified":null,"docomo":null,"au":"E4FF","softbank":"E230","google":"FEB9B","image":"1f448.png","sheet_x":12,"sheet_y":30,"short_name":"point_left","short_names":["point_left"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-single-finger","sort_order":189,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F448-1F3FB","non_qualified":null,"image":"1f448-1f3fb.png","sheet_x":12,"sheet_y":31,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F448-1F3FC","non_qualified":null,"image":"1f448-1f3fc.png","sheet_x":12,"sheet_y":32,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F448-1F3FD","non_qualified":null,"image":"1f448-1f3fd.png","sheet_x":12,"sheet_y":33,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F448-1F3FE","non_qualified":null,"image":"1f448-1f3fe.png","sheet_x":12,"sheet_y":34,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F448-1F3FF","non_qualified":null,"image":"1f448-1f3ff.png","sheet_x":12,"sheet_y":35,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WHITE RIGHT POINTING BACKHAND INDEX","unified":"1F449","non_qualified":null,"docomo":null,"au":"E500","softbank":"E231","google":"FEB9C","image":"1f449.png","sheet_x":12,"sheet_y":36,"short_name":"point_right","short_names":["point_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-single-finger","sort_order":190,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F449-1F3FB","non_qualified":null,"image":"1f449-1f3fb.png","sheet_x":12,"sheet_y":37,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F449-1F3FC","non_qualified":null,"image":"1f449-1f3fc.png","sheet_x":12,"sheet_y":38,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F449-1F3FD","non_qualified":null,"image":"1f449-1f3fd.png","sheet_x":12,"sheet_y":39,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F449-1F3FE","non_qualified":null,"image":"1f449-1f3fe.png","sheet_x":12,"sheet_y":40,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F449-1F3FF","non_qualified":null,"image":"1f449-1f3ff.png","sheet_x":12,"sheet_y":41,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FISTED HAND SIGN","unified":"1F44A","non_qualified":null,"docomo":"E6FD","au":"E4F3","softbank":"E00D","google":"FEB96","image":"1f44a.png","sheet_x":12,"sheet_y":42,"short_name":"facepunch","short_names":["facepunch","punch"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-closed","sort_order":199,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F44A-1F3FB","non_qualified":null,"image":"1f44a-1f3fb.png","sheet_x":12,"sheet_y":43,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F44A-1F3FC","non_qualified":null,"image":"1f44a-1f3fc.png","sheet_x":12,"sheet_y":44,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F44A-1F3FD","non_qualified":null,"image":"1f44a-1f3fd.png","sheet_x":12,"sheet_y":45,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F44A-1F3FE","non_qualified":null,"image":"1f44a-1f3fe.png","sheet_x":12,"sheet_y":46,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F44A-1F3FF","non_qualified":null,"image":"1f44a-1f3ff.png","sheet_x":12,"sheet_y":47,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WAVING HAND SIGN","unified":"1F44B","non_qualified":null,"docomo":"E695","au":"EAD6","softbank":"E41E","google":"FEB9D","image":"1f44b.png","sheet_x":12,"sheet_y":48,"short_name":"wave","short_names":["wave"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":169,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F44B-1F3FB","non_qualified":null,"image":"1f44b-1f3fb.png","sheet_x":12,"sheet_y":49,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F44B-1F3FC","non_qualified":null,"image":"1f44b-1f3fc.png","sheet_x":12,"sheet_y":50,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F44B-1F3FD","non_qualified":null,"image":"1f44b-1f3fd.png","sheet_x":12,"sheet_y":51,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F44B-1F3FE","non_qualified":null,"image":"1f44b-1f3fe.png","sheet_x":12,"sheet_y":52,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F44B-1F3FF","non_qualified":null,"image":"1f44b-1f3ff.png","sheet_x":12,"sheet_y":53,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"OK HAND SIGN","unified":"1F44C","non_qualified":null,"docomo":"E70B","au":"EAD4","softbank":"E420","google":"FEB9F","image":"1f44c.png","sheet_x":12,"sheet_y":54,"short_name":"ok_hand","short_names":["ok_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-partial","sort_order":180,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F44C-1F3FB","non_qualified":null,"image":"1f44c-1f3fb.png","sheet_x":12,"sheet_y":55,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F44C-1F3FC","non_qualified":null,"image":"1f44c-1f3fc.png","sheet_x":12,"sheet_y":56,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F44C-1F3FD","non_qualified":null,"image":"1f44c-1f3fd.png","sheet_x":12,"sheet_y":57,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F44C-1F3FE","non_qualified":null,"image":"1f44c-1f3fe.png","sheet_x":12,"sheet_y":58,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F44C-1F3FF","non_qualified":null,"image":"1f44c-1f3ff.png","sheet_x":12,"sheet_y":59,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"THUMBS UP SIGN","unified":"1F44D","non_qualified":null,"docomo":"E727","au":"E4F9","softbank":"E00E","google":"FEB97","image":"1f44d.png","sheet_x":12,"sheet_y":60,"short_name":"+1","short_names":["+1","thumbsup"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-closed","sort_order":196,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F44D-1F3FB","non_qualified":null,"image":"1f44d-1f3fb.png","sheet_x":12,"sheet_y":61,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F44D-1F3FC","non_qualified":null,"image":"1f44d-1f3fc.png","sheet_x":13,"sheet_y":0,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F44D-1F3FD","non_qualified":null,"image":"1f44d-1f3fd.png","sheet_x":13,"sheet_y":1,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F44D-1F3FE","non_qualified":null,"image":"1f44d-1f3fe.png","sheet_x":13,"sheet_y":2,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F44D-1F3FF","non_qualified":null,"image":"1f44d-1f3ff.png","sheet_x":13,"sheet_y":3,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"THUMBS DOWN SIGN","unified":"1F44E","non_qualified":null,"docomo":"E700","au":"EAD5","softbank":"E421","google":"FEBA0","image":"1f44e.png","sheet_x":13,"sheet_y":4,"short_name":"-1","short_names":["-1","thumbsdown"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-closed","sort_order":197,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F44E-1F3FB","non_qualified":null,"image":"1f44e-1f3fb.png","sheet_x":13,"sheet_y":5,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F44E-1F3FC","non_qualified":null,"image":"1f44e-1f3fc.png","sheet_x":13,"sheet_y":6,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F44E-1F3FD","non_qualified":null,"image":"1f44e-1f3fd.png","sheet_x":13,"sheet_y":7,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F44E-1F3FE","non_qualified":null,"image":"1f44e-1f3fe.png","sheet_x":13,"sheet_y":8,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F44E-1F3FF","non_qualified":null,"image":"1f44e-1f3ff.png","sheet_x":13,"sheet_y":9,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"CLAPPING HANDS SIGN","unified":"1F44F","non_qualified":null,"docomo":null,"au":"EAD3","softbank":"E41F","google":"FEB9E","image":"1f44f.png","sheet_x":13,"sheet_y":10,"short_name":"clap","short_names":["clap"],"text":null,"texts":null,"category":"People & Body","subcategory":"hands","sort_order":202,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F44F-1F3FB","non_qualified":null,"image":"1f44f-1f3fb.png","sheet_x":13,"sheet_y":11,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F44F-1F3FC","non_qualified":null,"image":"1f44f-1f3fc.png","sheet_x":13,"sheet_y":12,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F44F-1F3FD","non_qualified":null,"image":"1f44f-1f3fd.png","sheet_x":13,"sheet_y":13,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F44F-1F3FE","non_qualified":null,"image":"1f44f-1f3fe.png","sheet_x":13,"sheet_y":14,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F44F-1F3FF","non_qualified":null,"image":"1f44f-1f3ff.png","sheet_x":13,"sheet_y":15,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"OPEN HANDS SIGN","unified":"1F450","non_qualified":null,"docomo":"E695","au":"EAD6","softbank":"E422","google":"FEBA1","image":"1f450.png","sheet_x":13,"sheet_y":16,"short_name":"open_hands","short_names":["open_hands"],"text":null,"texts":null,"category":"People & Body","subcategory":"hands","sort_order":205,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F450-1F3FB","non_qualified":null,"image":"1f450-1f3fb.png","sheet_x":13,"sheet_y":17,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F450-1F3FC","non_qualified":null,"image":"1f450-1f3fc.png","sheet_x":13,"sheet_y":18,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F450-1F3FD","non_qualified":null,"image":"1f450-1f3fd.png","sheet_x":13,"sheet_y":19,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F450-1F3FE","non_qualified":null,"image":"1f450-1f3fe.png","sheet_x":13,"sheet_y":20,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F450-1F3FF","non_qualified":null,"image":"1f450-1f3ff.png","sheet_x":13,"sheet_y":21,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"CROWN","unified":"1F451","non_qualified":null,"docomo":"E71A","au":"E5C9","softbank":"E10E","google":"FE4D1","image":"1f451.png","sheet_x":13,"sheet_y":22,"short_name":"crown","short_names":["crown"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1186,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMANS HAT","unified":"1F452","non_qualified":null,"docomo":null,"au":"EA9E","softbank":"E318","google":"FE4D4","image":"1f452.png","sheet_x":13,"sheet_y":23,"short_name":"womans_hat","short_names":["womans_hat"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1187,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EYEGLASSES","unified":"1F453","non_qualified":null,"docomo":"E69A","au":"E4FE","softbank":null,"google":"FE4CE","image":"1f453.png","sheet_x":13,"sheet_y":24,"short_name":"eyeglasses","short_names":["eyeglasses"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1150,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NECKTIE","unified":"1F454","non_qualified":null,"docomo":null,"au":"EA93","softbank":"E302","google":"FE4D3","image":"1f454.png","sheet_x":13,"sheet_y":25,"short_name":"necktie","short_names":["necktie"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1155,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"T-SHIRT","unified":"1F455","non_qualified":null,"docomo":"E70E","au":"E5B6","softbank":"E006","google":"FE4CF","image":"1f455.png","sheet_x":13,"sheet_y":26,"short_name":"shirt","short_names":["shirt","tshirt"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1156,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JEANS","unified":"1F456","non_qualified":null,"docomo":"E711","au":"EB77","softbank":null,"google":"FE4D0","image":"1f456.png","sheet_x":13,"sheet_y":27,"short_name":"jeans","short_names":["jeans"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1157,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DRESS","unified":"1F457","non_qualified":null,"docomo":null,"au":"EB6B","softbank":"E319","google":"FE4D5","image":"1f457.png","sheet_x":13,"sheet_y":28,"short_name":"dress","short_names":["dress"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1162,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KIMONO","unified":"1F458","non_qualified":null,"docomo":null,"au":"EAA3","softbank":"E321","google":"FE4D9","image":"1f458.png","sheet_x":13,"sheet_y":29,"short_name":"kimono","short_names":["kimono"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1163,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BIKINI","unified":"1F459","non_qualified":null,"docomo":null,"au":"EAA4","softbank":"E322","google":"FE4DA","image":"1f459.png","sheet_x":13,"sheet_y":30,"short_name":"bikini","short_names":["bikini"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1168,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMANS CLOTHES","unified":"1F45A","non_qualified":null,"docomo":"E70E","au":"E50D","softbank":null,"google":"FE4DB","image":"1f45a.png","sheet_x":13,"sheet_y":31,"short_name":"womans_clothes","short_names":["womans_clothes"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1169,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PURSE","unified":"1F45B","non_qualified":null,"docomo":"E70F","au":"E504","softbank":null,"google":"FE4DC","image":"1f45b.png","sheet_x":13,"sheet_y":32,"short_name":"purse","short_names":["purse"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1171,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HANDBAG","unified":"1F45C","non_qualified":null,"docomo":"E682","au":"E49C","softbank":"E323","google":"FE4F0","image":"1f45c.png","sheet_x":13,"sheet_y":33,"short_name":"handbag","short_names":["handbag"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1172,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POUCH","unified":"1F45D","non_qualified":null,"docomo":"E6AD","au":null,"softbank":null,"google":"FE4F1","image":"1f45d.png","sheet_x":13,"sheet_y":34,"short_name":"pouch","short_names":["pouch"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1173,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MANS SHOE","unified":"1F45E","non_qualified":null,"docomo":"E699","au":"E5B7","softbank":null,"google":"FE4CC","image":"1f45e.png","sheet_x":13,"sheet_y":35,"short_name":"mans_shoe","short_names":["mans_shoe","shoe"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1177,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ATHLETIC SHOE","unified":"1F45F","non_qualified":null,"docomo":"E699","au":"EB2B","softbank":"E007","google":"FE4CD","image":"1f45f.png","sheet_x":13,"sheet_y":36,"short_name":"athletic_shoe","short_names":["athletic_shoe"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1178,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HIGH-HEELED SHOE","unified":"1F460","non_qualified":null,"docomo":"E674","au":"E51A","softbank":"E13E","google":"FE4D6","image":"1f460.png","sheet_x":13,"sheet_y":37,"short_name":"high_heel","short_names":["high_heel"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1181,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMANS SANDAL","unified":"1F461","non_qualified":null,"docomo":"E674","au":"E51A","softbank":"E31A","google":"FE4D7","image":"1f461.png","sheet_x":13,"sheet_y":38,"short_name":"sandal","short_names":["sandal"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1182,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMANS BOOTS","unified":"1F462","non_qualified":null,"docomo":null,"au":"EA9F","softbank":"E31B","google":"FE4D8","image":"1f462.png","sheet_x":13,"sheet_y":39,"short_name":"boot","short_names":["boot"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1184,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FOOTPRINTS","unified":"1F463","non_qualified":null,"docomo":"E698","au":"EB2A","softbank":"E536","google":"FE553","image":"1f463.png","sheet_x":13,"sheet_y":40,"short_name":"footprints","short_names":["footprints"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-symbol","sort_order":553,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUST IN SILHOUETTE","unified":"1F464","non_qualified":null,"docomo":"E6B1","au":null,"softbank":null,"google":"FE19A","image":"1f464.png","sheet_x":13,"sheet_y":41,"short_name":"bust_in_silhouette","short_names":["bust_in_silhouette"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-symbol","sort_order":545,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUSTS IN SILHOUETTE","unified":"1F465","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f465.png","sheet_x":13,"sheet_y":42,"short_name":"busts_in_silhouette","short_names":["busts_in_silhouette"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-symbol","sort_order":546,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOY","unified":"1F466","non_qualified":null,"docomo":"E6F0","au":"E4FC","softbank":"E001","google":"FE19B","image":"1f466.png","sheet_x":13,"sheet_y":43,"short_name":"boy","short_names":["boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":232,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F466-1F3FB","non_qualified":null,"image":"1f466-1f3fb.png","sheet_x":13,"sheet_y":44,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F466-1F3FC","non_qualified":null,"image":"1f466-1f3fc.png","sheet_x":13,"sheet_y":45,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F466-1F3FD","non_qualified":null,"image":"1f466-1f3fd.png","sheet_x":13,"sheet_y":46,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F466-1F3FE","non_qualified":null,"image":"1f466-1f3fe.png","sheet_x":13,"sheet_y":47,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F466-1F3FF","non_qualified":null,"image":"1f466-1f3ff.png","sheet_x":13,"sheet_y":48,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"GIRL","unified":"1F467","non_qualified":null,"docomo":"E6F0","au":"E4FA","softbank":"E002","google":"FE19C","image":"1f467.png","sheet_x":13,"sheet_y":49,"short_name":"girl","short_names":["girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":233,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F467-1F3FB","non_qualified":null,"image":"1f467-1f3fb.png","sheet_x":13,"sheet_y":50,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F467-1F3FC","non_qualified":null,"image":"1f467-1f3fc.png","sheet_x":13,"sheet_y":51,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F467-1F3FD","non_qualified":null,"image":"1f467-1f3fd.png","sheet_x":13,"sheet_y":52,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F467-1F3FE","non_qualified":null,"image":"1f467-1f3fe.png","sheet_x":13,"sheet_y":53,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F467-1F3FF","non_qualified":null,"image":"1f467-1f3ff.png","sheet_x":13,"sheet_y":54,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN FARMER","unified":"1F468-200D-1F33E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f33e.png","sheet_x":13,"sheet_y":55,"short_name":"male-farmer","short_names":["male-farmer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":301,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F33E","non_qualified":null,"image":"1f468-1f3fb-200d-1f33e.png","sheet_x":13,"sheet_y":56,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F33E","non_qualified":null,"image":"1f468-1f3fc-200d-1f33e.png","sheet_x":13,"sheet_y":57,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F33E","non_qualified":null,"image":"1f468-1f3fd-200d-1f33e.png","sheet_x":13,"sheet_y":58,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F33E","non_qualified":null,"image":"1f468-1f3fe-200d-1f33e.png","sheet_x":13,"sheet_y":59,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F33E","non_qualified":null,"image":"1f468-1f3ff-200d-1f33e.png","sheet_x":13,"sheet_y":60,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN COOK","unified":"1F468-200D-1F373","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f373.png","sheet_x":13,"sheet_y":61,"short_name":"male-cook","short_names":["male-cook"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":304,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F373","non_qualified":null,"image":"1f468-1f3fb-200d-1f373.png","sheet_x":14,"sheet_y":0,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F373","non_qualified":null,"image":"1f468-1f3fc-200d-1f373.png","sheet_x":14,"sheet_y":1,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F373","non_qualified":null,"image":"1f468-1f3fd-200d-1f373.png","sheet_x":14,"sheet_y":2,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F373","non_qualified":null,"image":"1f468-1f3fe-200d-1f373.png","sheet_x":14,"sheet_y":3,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F373","non_qualified":null,"image":"1f468-1f3ff-200d-1f373.png","sheet_x":14,"sheet_y":4,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN FEEDING BABY","unified":"1F468-200D-1F37C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f37c.png","sheet_x":14,"sheet_y":5,"short_name":"man_feeding_baby","short_names":["man_feeding_baby"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":368,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F37C","non_qualified":null,"image":"1f468-1f3fb-200d-1f37c.png","sheet_x":14,"sheet_y":6,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F37C","non_qualified":null,"image":"1f468-1f3fc-200d-1f37c.png","sheet_x":14,"sheet_y":7,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F37C","non_qualified":null,"image":"1f468-1f3fd-200d-1f37c.png","sheet_x":14,"sheet_y":8,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F37C","non_qualified":null,"image":"1f468-1f3fe-200d-1f37c.png","sheet_x":14,"sheet_y":9,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F37C","non_qualified":null,"image":"1f468-1f3ff-200d-1f37c.png","sheet_x":14,"sheet_y":10,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN STUDENT","unified":"1F468-200D-1F393","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f393.png","sheet_x":14,"sheet_y":11,"short_name":"male-student","short_names":["male-student"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":292,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F393","non_qualified":null,"image":"1f468-1f3fb-200d-1f393.png","sheet_x":14,"sheet_y":12,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F393","non_qualified":null,"image":"1f468-1f3fc-200d-1f393.png","sheet_x":14,"sheet_y":13,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F393","non_qualified":null,"image":"1f468-1f3fd-200d-1f393.png","sheet_x":14,"sheet_y":14,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F393","non_qualified":null,"image":"1f468-1f3fe-200d-1f393.png","sheet_x":14,"sheet_y":15,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F393","non_qualified":null,"image":"1f468-1f3ff-200d-1f393.png","sheet_x":14,"sheet_y":16,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN SINGER","unified":"1F468-200D-1F3A4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f3a4.png","sheet_x":14,"sheet_y":17,"short_name":"male-singer","short_names":["male-singer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":322,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F3A4","non_qualified":null,"image":"1f468-1f3fb-200d-1f3a4.png","sheet_x":14,"sheet_y":18,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F3A4","non_qualified":null,"image":"1f468-1f3fc-200d-1f3a4.png","sheet_x":14,"sheet_y":19,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F3A4","non_qualified":null,"image":"1f468-1f3fd-200d-1f3a4.png","sheet_x":14,"sheet_y":20,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F3A4","non_qualified":null,"image":"1f468-1f3fe-200d-1f3a4.png","sheet_x":14,"sheet_y":21,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F3A4","non_qualified":null,"image":"1f468-1f3ff-200d-1f3a4.png","sheet_x":14,"sheet_y":22,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN ARTIST","unified":"1F468-200D-1F3A8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f3a8.png","sheet_x":14,"sheet_y":23,"short_name":"male-artist","short_names":["male-artist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":325,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F3A8","non_qualified":null,"image":"1f468-1f3fb-200d-1f3a8.png","sheet_x":14,"sheet_y":24,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F3A8","non_qualified":null,"image":"1f468-1f3fc-200d-1f3a8.png","sheet_x":14,"sheet_y":25,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F3A8","non_qualified":null,"image":"1f468-1f3fd-200d-1f3a8.png","sheet_x":14,"sheet_y":26,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F3A8","non_qualified":null,"image":"1f468-1f3fe-200d-1f3a8.png","sheet_x":14,"sheet_y":27,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F3A8","non_qualified":null,"image":"1f468-1f3ff-200d-1f3a8.png","sheet_x":14,"sheet_y":28,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN TEACHER","unified":"1F468-200D-1F3EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f3eb.png","sheet_x":14,"sheet_y":29,"short_name":"male-teacher","short_names":["male-teacher"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":295,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F3EB","non_qualified":null,"image":"1f468-1f3fb-200d-1f3eb.png","sheet_x":14,"sheet_y":30,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F3EB","non_qualified":null,"image":"1f468-1f3fc-200d-1f3eb.png","sheet_x":14,"sheet_y":31,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F3EB","non_qualified":null,"image":"1f468-1f3fd-200d-1f3eb.png","sheet_x":14,"sheet_y":32,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F3EB","non_qualified":null,"image":"1f468-1f3fe-200d-1f3eb.png","sheet_x":14,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F3EB","non_qualified":null,"image":"1f468-1f3ff-200d-1f3eb.png","sheet_x":14,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN FACTORY WORKER","unified":"1F468-200D-1F3ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f3ed.png","sheet_x":14,"sheet_y":35,"short_name":"male-factory-worker","short_names":["male-factory-worker"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":310,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F3ED","non_qualified":null,"image":"1f468-1f3fb-200d-1f3ed.png","sheet_x":14,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F3ED","non_qualified":null,"image":"1f468-1f3fc-200d-1f3ed.png","sheet_x":14,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F3ED","non_qualified":null,"image":"1f468-1f3fd-200d-1f3ed.png","sheet_x":14,"sheet_y":38,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F3ED","non_qualified":null,"image":"1f468-1f3fe-200d-1f3ed.png","sheet_x":14,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F3ED","non_qualified":null,"image":"1f468-1f3ff-200d-1f3ed.png","sheet_x":14,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FAMILY: MAN, BOY, BOY","unified":"1F468-200D-1F466-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f466-200d-1f466.png","sheet_x":14,"sheet_y":41,"short_name":"man-boy-boy","short_names":["man-boy-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":535,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, BOY","unified":"1F468-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f466.png","sheet_x":14,"sheet_y":42,"short_name":"man-boy","short_names":["man-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":534,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, GIRL, BOY","unified":"1F468-200D-1F467-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f467-200d-1f466.png","sheet_x":14,"sheet_y":43,"short_name":"man-girl-boy","short_names":["man-girl-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":537,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, GIRL, GIRL","unified":"1F468-200D-1F467-200D-1F467","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f467-200d-1f467.png","sheet_x":14,"sheet_y":44,"short_name":"man-girl-girl","short_names":["man-girl-girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":538,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, GIRL","unified":"1F468-200D-1F467","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f467.png","sheet_x":14,"sheet_y":45,"short_name":"man-girl","short_names":["man-girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":536,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, MAN, BOY","unified":"1F468-200D-1F468-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f468-200d-1f466.png","sheet_x":14,"sheet_y":46,"short_name":"man-man-boy","short_names":["man-man-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":524,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, MAN, BOY, BOY","unified":"1F468-200D-1F468-200D-1F466-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f468-200d-1f466-200d-1f466.png","sheet_x":14,"sheet_y":47,"short_name":"man-man-boy-boy","short_names":["man-man-boy-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":527,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, MAN, GIRL","unified":"1F468-200D-1F468-200D-1F467","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f468-200d-1f467.png","sheet_x":14,"sheet_y":48,"short_name":"man-man-girl","short_names":["man-man-girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":525,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, MAN, GIRL, BOY","unified":"1F468-200D-1F468-200D-1F467-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f468-200d-1f467-200d-1f466.png","sheet_x":14,"sheet_y":49,"short_name":"man-man-girl-boy","short_names":["man-man-girl-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":526,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, MAN, GIRL, GIRL","unified":"1F468-200D-1F468-200D-1F467-200D-1F467","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f468-200d-1f467-200d-1f467.png","sheet_x":14,"sheet_y":50,"short_name":"man-man-girl-girl","short_names":["man-man-girl-girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":528,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, WOMAN, BOY","unified":"1F468-200D-1F469-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f469-200d-1f466.png","sheet_x":14,"sheet_y":51,"short_name":"man-woman-boy","short_names":["man-woman-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":519,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F46A"},{"name":"FAMILY: MAN, WOMAN, BOY, BOY","unified":"1F468-200D-1F469-200D-1F466-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f469-200d-1f466-200d-1f466.png","sheet_x":14,"sheet_y":52,"short_name":"man-woman-boy-boy","short_names":["man-woman-boy-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":522,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, WOMAN, GIRL","unified":"1F468-200D-1F469-200D-1F467","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f469-200d-1f467.png","sheet_x":14,"sheet_y":53,"short_name":"man-woman-girl","short_names":["man-woman-girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":520,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, WOMAN, GIRL, BOY","unified":"1F468-200D-1F469-200D-1F467-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f469-200d-1f467-200d-1f466.png","sheet_x":14,"sheet_y":54,"short_name":"man-woman-girl-boy","short_names":["man-woman-girl-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":521,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: MAN, WOMAN, GIRL, GIRL","unified":"1F468-200D-1F469-200D-1F467-200D-1F467","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f469-200d-1f467-200d-1f467.png","sheet_x":14,"sheet_y":55,"short_name":"man-woman-girl-girl","short_names":["man-woman-girl-girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":523,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MAN TECHNOLOGIST","unified":"1F468-200D-1F4BB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f4bb.png","sheet_x":14,"sheet_y":56,"short_name":"male-technologist","short_names":["male-technologist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":319,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F4BB","non_qualified":null,"image":"1f468-1f3fb-200d-1f4bb.png","sheet_x":14,"sheet_y":57,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F4BB","non_qualified":null,"image":"1f468-1f3fc-200d-1f4bb.png","sheet_x":14,"sheet_y":58,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F4BB","non_qualified":null,"image":"1f468-1f3fd-200d-1f4bb.png","sheet_x":14,"sheet_y":59,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F4BB","non_qualified":null,"image":"1f468-1f3fe-200d-1f4bb.png","sheet_x":14,"sheet_y":60,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F4BB","non_qualified":null,"image":"1f468-1f3ff-200d-1f4bb.png","sheet_x":14,"sheet_y":61,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN OFFICE WORKER","unified":"1F468-200D-1F4BC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f4bc.png","sheet_x":15,"sheet_y":0,"short_name":"male-office-worker","short_names":["male-office-worker"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":313,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F4BC","non_qualified":null,"image":"1f468-1f3fb-200d-1f4bc.png","sheet_x":15,"sheet_y":1,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F4BC","non_qualified":null,"image":"1f468-1f3fc-200d-1f4bc.png","sheet_x":15,"sheet_y":2,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F4BC","non_qualified":null,"image":"1f468-1f3fd-200d-1f4bc.png","sheet_x":15,"sheet_y":3,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F4BC","non_qualified":null,"image":"1f468-1f3fe-200d-1f4bc.png","sheet_x":15,"sheet_y":4,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F4BC","non_qualified":null,"image":"1f468-1f3ff-200d-1f4bc.png","sheet_x":15,"sheet_y":5,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN MECHANIC","unified":"1F468-200D-1F527","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f527.png","sheet_x":15,"sheet_y":6,"short_name":"male-mechanic","short_names":["male-mechanic"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":307,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F527","non_qualified":null,"image":"1f468-1f3fb-200d-1f527.png","sheet_x":15,"sheet_y":7,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F527","non_qualified":null,"image":"1f468-1f3fc-200d-1f527.png","sheet_x":15,"sheet_y":8,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F527","non_qualified":null,"image":"1f468-1f3fd-200d-1f527.png","sheet_x":15,"sheet_y":9,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F527","non_qualified":null,"image":"1f468-1f3fe-200d-1f527.png","sheet_x":15,"sheet_y":10,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F527","non_qualified":null,"image":"1f468-1f3ff-200d-1f527.png","sheet_x":15,"sheet_y":11,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN SCIENTIST","unified":"1F468-200D-1F52C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f52c.png","sheet_x":15,"sheet_y":12,"short_name":"male-scientist","short_names":["male-scientist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":316,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F52C","non_qualified":null,"image":"1f468-1f3fb-200d-1f52c.png","sheet_x":15,"sheet_y":13,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F52C","non_qualified":null,"image":"1f468-1f3fc-200d-1f52c.png","sheet_x":15,"sheet_y":14,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F52C","non_qualified":null,"image":"1f468-1f3fd-200d-1f52c.png","sheet_x":15,"sheet_y":15,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F52C","non_qualified":null,"image":"1f468-1f3fe-200d-1f52c.png","sheet_x":15,"sheet_y":16,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F52C","non_qualified":null,"image":"1f468-1f3ff-200d-1f52c.png","sheet_x":15,"sheet_y":17,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN ASTRONAUT","unified":"1F468-200D-1F680","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f680.png","sheet_x":15,"sheet_y":18,"short_name":"male-astronaut","short_names":["male-astronaut"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":331,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F680","non_qualified":null,"image":"1f468-1f3fb-200d-1f680.png","sheet_x":15,"sheet_y":19,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F680","non_qualified":null,"image":"1f468-1f3fc-200d-1f680.png","sheet_x":15,"sheet_y":20,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F680","non_qualified":null,"image":"1f468-1f3fd-200d-1f680.png","sheet_x":15,"sheet_y":21,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F680","non_qualified":null,"image":"1f468-1f3fe-200d-1f680.png","sheet_x":15,"sheet_y":22,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F680","non_qualified":null,"image":"1f468-1f3ff-200d-1f680.png","sheet_x":15,"sheet_y":23,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN FIREFIGHTER","unified":"1F468-200D-1F692","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f692.png","sheet_x":15,"sheet_y":24,"short_name":"male-firefighter","short_names":["male-firefighter"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":334,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F692","non_qualified":null,"image":"1f468-1f3fb-200d-1f692.png","sheet_x":15,"sheet_y":25,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F692","non_qualified":null,"image":"1f468-1f3fc-200d-1f692.png","sheet_x":15,"sheet_y":26,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F692","non_qualified":null,"image":"1f468-1f3fd-200d-1f692.png","sheet_x":15,"sheet_y":27,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F692","non_qualified":null,"image":"1f468-1f3fe-200d-1f692.png","sheet_x":15,"sheet_y":28,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F692","non_qualified":null,"image":"1f468-1f3ff-200d-1f692.png","sheet_x":15,"sheet_y":29,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN WITH WHITE CANE FACING RIGHT","unified":"1F468-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F468-200D-1F9AF-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f9af-200d-27a1-fe0f.png","sheet_x":15,"sheet_y":30,"short_name":"man_with_white_cane_facing_right","short_names":["man_with_white_cane_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":426,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F468-1F3FB-200D-1F9AF-200D-27A1","image":"1f468-1f3fb-200d-1f9af-200d-27a1-fe0f.png","sheet_x":15,"sheet_y":31,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F468-1F3FC-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F468-1F3FC-200D-1F9AF-200D-27A1","image":"1f468-1f3fc-200d-1f9af-200d-27a1-fe0f.png","sheet_x":15,"sheet_y":32,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F468-1F3FD-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F468-1F3FD-200D-1F9AF-200D-27A1","image":"1f468-1f3fd-200d-1f9af-200d-27a1-fe0f.png","sheet_x":15,"sheet_y":33,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F468-1F3FE-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F468-1F3FE-200D-1F9AF-200D-27A1","image":"1f468-1f3fe-200d-1f9af-200d-27a1-fe0f.png","sheet_x":15,"sheet_y":34,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F468-1F3FF-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F468-1F3FF-200D-1F9AF-200D-27A1","image":"1f468-1f3ff-200d-1f9af-200d-27a1-fe0f.png","sheet_x":15,"sheet_y":35,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"MAN WITH WHITE CANE","unified":"1F468-200D-1F9AF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f9af.png","sheet_x":15,"sheet_y":36,"short_name":"man_with_probing_cane","short_names":["man_with_probing_cane"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":425,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F9AF","non_qualified":null,"image":"1f468-1f3fb-200d-1f9af.png","sheet_x":15,"sheet_y":37,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F9AF","non_qualified":null,"image":"1f468-1f3fc-200d-1f9af.png","sheet_x":15,"sheet_y":38,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F9AF","non_qualified":null,"image":"1f468-1f3fd-200d-1f9af.png","sheet_x":15,"sheet_y":39,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F9AF","non_qualified":null,"image":"1f468-1f3fe-200d-1f9af.png","sheet_x":15,"sheet_y":40,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F9AF","non_qualified":null,"image":"1f468-1f3ff-200d-1f9af.png","sheet_x":15,"sheet_y":41,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN: RED HAIR","unified":"1F468-200D-1F9B0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f9b0.png","sheet_x":15,"sheet_y":42,"short_name":"red_haired_man","short_names":["red_haired_man"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":240,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F9B0","non_qualified":null,"image":"1f468-1f3fb-200d-1f9b0.png","sheet_x":15,"sheet_y":43,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F9B0","non_qualified":null,"image":"1f468-1f3fc-200d-1f9b0.png","sheet_x":15,"sheet_y":44,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F9B0","non_qualified":null,"image":"1f468-1f3fd-200d-1f9b0.png","sheet_x":15,"sheet_y":45,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F9B0","non_qualified":null,"image":"1f468-1f3fe-200d-1f9b0.png","sheet_x":15,"sheet_y":46,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F9B0","non_qualified":null,"image":"1f468-1f3ff-200d-1f9b0.png","sheet_x":15,"sheet_y":47,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN: CURLY HAIR","unified":"1F468-200D-1F9B1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f9b1.png","sheet_x":15,"sheet_y":48,"short_name":"curly_haired_man","short_names":["curly_haired_man"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":241,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F9B1","non_qualified":null,"image":"1f468-1f3fb-200d-1f9b1.png","sheet_x":15,"sheet_y":49,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F9B1","non_qualified":null,"image":"1f468-1f3fc-200d-1f9b1.png","sheet_x":15,"sheet_y":50,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F9B1","non_qualified":null,"image":"1f468-1f3fd-200d-1f9b1.png","sheet_x":15,"sheet_y":51,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F9B1","non_qualified":null,"image":"1f468-1f3fe-200d-1f9b1.png","sheet_x":15,"sheet_y":52,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F9B1","non_qualified":null,"image":"1f468-1f3ff-200d-1f9b1.png","sheet_x":15,"sheet_y":53,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN: BALD","unified":"1F468-200D-1F9B2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f9b2.png","sheet_x":15,"sheet_y":54,"short_name":"bald_man","short_names":["bald_man"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":243,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F9B2","non_qualified":null,"image":"1f468-1f3fb-200d-1f9b2.png","sheet_x":15,"sheet_y":55,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F9B2","non_qualified":null,"image":"1f468-1f3fc-200d-1f9b2.png","sheet_x":15,"sheet_y":56,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F9B2","non_qualified":null,"image":"1f468-1f3fd-200d-1f9b2.png","sheet_x":15,"sheet_y":57,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F9B2","non_qualified":null,"image":"1f468-1f3fe-200d-1f9b2.png","sheet_x":15,"sheet_y":58,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F9B2","non_qualified":null,"image":"1f468-1f3ff-200d-1f9b2.png","sheet_x":15,"sheet_y":59,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN: WHITE HAIR","unified":"1F468-200D-1F9B3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f9b3.png","sheet_x":15,"sheet_y":60,"short_name":"white_haired_man","short_names":["white_haired_man"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":242,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F9B3","non_qualified":null,"image":"1f468-1f3fb-200d-1f9b3.png","sheet_x":15,"sheet_y":61,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F9B3","non_qualified":null,"image":"1f468-1f3fc-200d-1f9b3.png","sheet_x":16,"sheet_y":0,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F9B3","non_qualified":null,"image":"1f468-1f3fd-200d-1f9b3.png","sheet_x":16,"sheet_y":1,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F9B3","non_qualified":null,"image":"1f468-1f3fe-200d-1f9b3.png","sheet_x":16,"sheet_y":2,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F9B3","non_qualified":null,"image":"1f468-1f3ff-200d-1f9b3.png","sheet_x":16,"sheet_y":3,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN IN MOTORIZED WHEELCHAIR FACING RIGHT","unified":"1F468-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F468-200D-1F9BC-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":4,"short_name":"man_in_motorized_wheelchair_facing_right","short_names":["man_in_motorized_wheelchair_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":432,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F468-1F3FB-200D-1F9BC-200D-27A1","image":"1f468-1f3fb-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":5,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F468-1F3FC-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F468-1F3FC-200D-1F9BC-200D-27A1","image":"1f468-1f3fc-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":6,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F468-1F3FD-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F468-1F3FD-200D-1F9BC-200D-27A1","image":"1f468-1f3fd-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":7,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F468-1F3FE-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F468-1F3FE-200D-1F9BC-200D-27A1","image":"1f468-1f3fe-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":8,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F468-1F3FF-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F468-1F3FF-200D-1F9BC-200D-27A1","image":"1f468-1f3ff-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":9,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"MAN IN MOTORIZED WHEELCHAIR","unified":"1F468-200D-1F9BC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f9bc.png","sheet_x":16,"sheet_y":10,"short_name":"man_in_motorized_wheelchair","short_names":["man_in_motorized_wheelchair"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":431,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F9BC","non_qualified":null,"image":"1f468-1f3fb-200d-1f9bc.png","sheet_x":16,"sheet_y":11,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F9BC","non_qualified":null,"image":"1f468-1f3fc-200d-1f9bc.png","sheet_x":16,"sheet_y":12,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F9BC","non_qualified":null,"image":"1f468-1f3fd-200d-1f9bc.png","sheet_x":16,"sheet_y":13,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F9BC","non_qualified":null,"image":"1f468-1f3fe-200d-1f9bc.png","sheet_x":16,"sheet_y":14,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F9BC","non_qualified":null,"image":"1f468-1f3ff-200d-1f9bc.png","sheet_x":16,"sheet_y":15,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN IN MANUAL WHEELCHAIR FACING RIGHT","unified":"1F468-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F468-200D-1F9BD-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":16,"short_name":"man_in_manual_wheelchair_facing_right","short_names":["man_in_manual_wheelchair_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":438,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F468-1F3FB-200D-1F9BD-200D-27A1","image":"1f468-1f3fb-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":17,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F468-1F3FC-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F468-1F3FC-200D-1F9BD-200D-27A1","image":"1f468-1f3fc-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":18,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F468-1F3FD-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F468-1F3FD-200D-1F9BD-200D-27A1","image":"1f468-1f3fd-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":19,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F468-1F3FE-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F468-1F3FE-200D-1F9BD-200D-27A1","image":"1f468-1f3fe-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":20,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F468-1F3FF-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F468-1F3FF-200D-1F9BD-200D-27A1","image":"1f468-1f3ff-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":16,"sheet_y":21,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"MAN IN MANUAL WHEELCHAIR","unified":"1F468-200D-1F9BD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f9bd.png","sheet_x":16,"sheet_y":22,"short_name":"man_in_manual_wheelchair","short_names":["man_in_manual_wheelchair"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":437,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-1F9BD","non_qualified":null,"image":"1f468-1f3fb-200d-1f9bd.png","sheet_x":16,"sheet_y":23,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-1F9BD","non_qualified":null,"image":"1f468-1f3fc-200d-1f9bd.png","sheet_x":16,"sheet_y":24,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-1F9BD","non_qualified":null,"image":"1f468-1f3fd-200d-1f9bd.png","sheet_x":16,"sheet_y":25,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-1F9BD","non_qualified":null,"image":"1f468-1f3fe-200d-1f9bd.png","sheet_x":16,"sheet_y":26,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-1F9BD","non_qualified":null,"image":"1f468-1f3ff-200d-1f9bd.png","sheet_x":16,"sheet_y":27,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN HEALTH WORKER","unified":"1F468-200D-2695-FE0F","non_qualified":"1F468-200D-2695","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-2695-fe0f.png","sheet_x":16,"sheet_y":28,"short_name":"male-doctor","short_names":["male-doctor"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":289,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-2695-FE0F","non_qualified":"1F468-1F3FB-200D-2695","image":"1f468-1f3fb-200d-2695-fe0f.png","sheet_x":16,"sheet_y":29,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-2695-FE0F","non_qualified":"1F468-1F3FC-200D-2695","image":"1f468-1f3fc-200d-2695-fe0f.png","sheet_x":16,"sheet_y":30,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-2695-FE0F","non_qualified":"1F468-1F3FD-200D-2695","image":"1f468-1f3fd-200d-2695-fe0f.png","sheet_x":16,"sheet_y":31,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-2695-FE0F","non_qualified":"1F468-1F3FE-200D-2695","image":"1f468-1f3fe-200d-2695-fe0f.png","sheet_x":16,"sheet_y":32,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-2695-FE0F","non_qualified":"1F468-1F3FF-200D-2695","image":"1f468-1f3ff-200d-2695-fe0f.png","sheet_x":16,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN JUDGE","unified":"1F468-200D-2696-FE0F","non_qualified":"1F468-200D-2696","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-2696-fe0f.png","sheet_x":16,"sheet_y":34,"short_name":"male-judge","short_names":["male-judge"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":298,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-2696-FE0F","non_qualified":"1F468-1F3FB-200D-2696","image":"1f468-1f3fb-200d-2696-fe0f.png","sheet_x":16,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-2696-FE0F","non_qualified":"1F468-1F3FC-200D-2696","image":"1f468-1f3fc-200d-2696-fe0f.png","sheet_x":16,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-2696-FE0F","non_qualified":"1F468-1F3FD-200D-2696","image":"1f468-1f3fd-200d-2696-fe0f.png","sheet_x":16,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-2696-FE0F","non_qualified":"1F468-1F3FE-200D-2696","image":"1f468-1f3fe-200d-2696-fe0f.png","sheet_x":16,"sheet_y":38,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-2696-FE0F","non_qualified":"1F468-1F3FF-200D-2696","image":"1f468-1f3ff-200d-2696-fe0f.png","sheet_x":16,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN PILOT","unified":"1F468-200D-2708-FE0F","non_qualified":"1F468-200D-2708","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-2708-fe0f.png","sheet_x":16,"sheet_y":40,"short_name":"male-pilot","short_names":["male-pilot"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":328,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB-200D-2708-FE0F","non_qualified":"1F468-1F3FB-200D-2708","image":"1f468-1f3fb-200d-2708-fe0f.png","sheet_x":16,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC-200D-2708-FE0F","non_qualified":"1F468-1F3FC-200D-2708","image":"1f468-1f3fc-200d-2708-fe0f.png","sheet_x":16,"sheet_y":42,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD-200D-2708-FE0F","non_qualified":"1F468-1F3FD-200D-2708","image":"1f468-1f3fd-200d-2708-fe0f.png","sheet_x":16,"sheet_y":43,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE-200D-2708-FE0F","non_qualified":"1F468-1F3FE-200D-2708","image":"1f468-1f3fe-200d-2708-fe0f.png","sheet_x":16,"sheet_y":44,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF-200D-2708-FE0F","non_qualified":"1F468-1F3FF-200D-2708","image":"1f468-1f3ff-200d-2708-fe0f.png","sheet_x":16,"sheet_y":45,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"COUPLE WITH HEART: MAN, MAN","unified":"1F468-200D-2764-FE0F-200D-1F468","non_qualified":"1F468-200D-2764-200D-1F468","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-2764-fe0f-200d-1f468.png","sheet_x":16,"sheet_y":46,"short_name":"man-heart-man","short_names":["man-heart-man"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":517,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB-1F3FB":{"unified":"1F468-1F3FB-200D-2764-FE0F-200D-1F468-1F3FB","non_qualified":"1F468-1F3FB-200D-2764-200D-1F468-1F3FB","image":"1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fb.png","sheet_x":16,"sheet_y":47,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F468-1F3FB-200D-2764-FE0F-200D-1F468-1F3FC","non_qualified":"1F468-1F3FB-200D-2764-200D-1F468-1F3FC","image":"1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fc.png","sheet_x":16,"sheet_y":48,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F468-1F3FB-200D-2764-FE0F-200D-1F468-1F3FD","non_qualified":"1F468-1F3FB-200D-2764-200D-1F468-1F3FD","image":"1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fd.png","sheet_x":16,"sheet_y":49,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F468-1F3FB-200D-2764-FE0F-200D-1F468-1F3FE","non_qualified":"1F468-1F3FB-200D-2764-200D-1F468-1F3FE","image":"1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3fe.png","sheet_x":16,"sheet_y":50,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F468-1F3FB-200D-2764-FE0F-200D-1F468-1F3FF","non_qualified":"1F468-1F3FB-200D-2764-200D-1F468-1F3FF","image":"1f468-1f3fb-200d-2764-fe0f-200d-1f468-1f3ff.png","sheet_x":16,"sheet_y":51,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F468-1F3FC-200D-2764-FE0F-200D-1F468-1F3FB","non_qualified":"1F468-1F3FC-200D-2764-200D-1F468-1F3FB","image":"1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fb.png","sheet_x":16,"sheet_y":52,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FC":{"unified":"1F468-1F3FC-200D-2764-FE0F-200D-1F468-1F3FC","non_qualified":"1F468-1F3FC-200D-2764-200D-1F468-1F3FC","image":"1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fc.png","sheet_x":16,"sheet_y":53,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F468-1F3FC-200D-2764-FE0F-200D-1F468-1F3FD","non_qualified":"1F468-1F3FC-200D-2764-200D-1F468-1F3FD","image":"1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fd.png","sheet_x":16,"sheet_y":54,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F468-1F3FC-200D-2764-FE0F-200D-1F468-1F3FE","non_qualified":"1F468-1F3FC-200D-2764-200D-1F468-1F3FE","image":"1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3fe.png","sheet_x":16,"sheet_y":55,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F468-1F3FC-200D-2764-FE0F-200D-1F468-1F3FF","non_qualified":"1F468-1F3FC-200D-2764-200D-1F468-1F3FF","image":"1f468-1f3fc-200d-2764-fe0f-200d-1f468-1f3ff.png","sheet_x":16,"sheet_y":56,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F468-1F3FD-200D-2764-FE0F-200D-1F468-1F3FB","non_qualified":"1F468-1F3FD-200D-2764-200D-1F468-1F3FB","image":"1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fb.png","sheet_x":16,"sheet_y":57,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F468-1F3FD-200D-2764-FE0F-200D-1F468-1F3FC","non_qualified":"1F468-1F3FD-200D-2764-200D-1F468-1F3FC","image":"1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fc.png","sheet_x":16,"sheet_y":58,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FD":{"unified":"1F468-1F3FD-200D-2764-FE0F-200D-1F468-1F3FD","non_qualified":"1F468-1F3FD-200D-2764-200D-1F468-1F3FD","image":"1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fd.png","sheet_x":16,"sheet_y":59,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F468-1F3FD-200D-2764-FE0F-200D-1F468-1F3FE","non_qualified":"1F468-1F3FD-200D-2764-200D-1F468-1F3FE","image":"1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3fe.png","sheet_x":16,"sheet_y":60,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F468-1F3FD-200D-2764-FE0F-200D-1F468-1F3FF","non_qualified":"1F468-1F3FD-200D-2764-200D-1F468-1F3FF","image":"1f468-1f3fd-200d-2764-fe0f-200d-1f468-1f3ff.png","sheet_x":16,"sheet_y":61,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F468-1F3FE-200D-2764-FE0F-200D-1F468-1F3FB","non_qualified":"1F468-1F3FE-200D-2764-200D-1F468-1F3FB","image":"1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fb.png","sheet_x":17,"sheet_y":0,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F468-1F3FE-200D-2764-FE0F-200D-1F468-1F3FC","non_qualified":"1F468-1F3FE-200D-2764-200D-1F468-1F3FC","image":"1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fc.png","sheet_x":17,"sheet_y":1,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F468-1F3FE-200D-2764-FE0F-200D-1F468-1F3FD","non_qualified":"1F468-1F3FE-200D-2764-200D-1F468-1F3FD","image":"1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fd.png","sheet_x":17,"sheet_y":2,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FE":{"unified":"1F468-1F3FE-200D-2764-FE0F-200D-1F468-1F3FE","non_qualified":"1F468-1F3FE-200D-2764-200D-1F468-1F3FE","image":"1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3fe.png","sheet_x":17,"sheet_y":3,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F468-1F3FE-200D-2764-FE0F-200D-1F468-1F3FF","non_qualified":"1F468-1F3FE-200D-2764-200D-1F468-1F3FF","image":"1f468-1f3fe-200d-2764-fe0f-200d-1f468-1f3ff.png","sheet_x":17,"sheet_y":4,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F468-1F3FF-200D-2764-FE0F-200D-1F468-1F3FB","non_qualified":"1F468-1F3FF-200D-2764-200D-1F468-1F3FB","image":"1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fb.png","sheet_x":17,"sheet_y":5,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F468-1F3FF-200D-2764-FE0F-200D-1F468-1F3FC","non_qualified":"1F468-1F3FF-200D-2764-200D-1F468-1F3FC","image":"1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fc.png","sheet_x":17,"sheet_y":6,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F468-1F3FF-200D-2764-FE0F-200D-1F468-1F3FD","non_qualified":"1F468-1F3FF-200D-2764-200D-1F468-1F3FD","image":"1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fd.png","sheet_x":17,"sheet_y":7,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F468-1F3FF-200D-2764-FE0F-200D-1F468-1F3FE","non_qualified":"1F468-1F3FF-200D-2764-200D-1F468-1F3FE","image":"1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3fe.png","sheet_x":17,"sheet_y":8,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FF":{"unified":"1F468-1F3FF-200D-2764-FE0F-200D-1F468-1F3FF","non_qualified":"1F468-1F3FF-200D-2764-200D-1F468-1F3FF","image":"1f468-1f3ff-200d-2764-fe0f-200d-1f468-1f3ff.png","sheet_x":17,"sheet_y":9,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"KISS: MAN, MAN","unified":"1F468-200D-2764-FE0F-200D-1F48B-200D-1F468","non_qualified":"1F468-200D-2764-200D-1F48B-200D-1F468","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.png","sheet_x":17,"sheet_y":10,"short_name":"man-kiss-man","short_names":["man-kiss-man"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":513,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB-1F3FB":{"unified":"1F468-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB","non_qualified":"1F468-1F3FB-200D-2764-200D-1F48B-200D-1F468-1F3FB","image":"1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png","sheet_x":17,"sheet_y":11,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F468-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FC","non_qualified":"1F468-1F3FB-200D-2764-200D-1F48B-200D-1F468-1F3FC","image":"1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png","sheet_x":17,"sheet_y":12,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F468-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FD","non_qualified":"1F468-1F3FB-200D-2764-200D-1F48B-200D-1F468-1F3FD","image":"1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png","sheet_x":17,"sheet_y":13,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F468-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FE","non_qualified":"1F468-1F3FB-200D-2764-200D-1F48B-200D-1F468-1F3FE","image":"1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png","sheet_x":17,"sheet_y":14,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F468-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FF","non_qualified":"1F468-1F3FB-200D-2764-200D-1F48B-200D-1F468-1F3FF","image":"1f468-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png","sheet_x":17,"sheet_y":15,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F468-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB","non_qualified":"1F468-1F3FC-200D-2764-200D-1F48B-200D-1F468-1F3FB","image":"1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png","sheet_x":17,"sheet_y":16,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FC":{"unified":"1F468-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FC","non_qualified":"1F468-1F3FC-200D-2764-200D-1F48B-200D-1F468-1F3FC","image":"1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png","sheet_x":17,"sheet_y":17,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F468-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FD","non_qualified":"1F468-1F3FC-200D-2764-200D-1F48B-200D-1F468-1F3FD","image":"1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png","sheet_x":17,"sheet_y":18,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F468-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FE","non_qualified":"1F468-1F3FC-200D-2764-200D-1F48B-200D-1F468-1F3FE","image":"1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png","sheet_x":17,"sheet_y":19,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F468-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FF","non_qualified":"1F468-1F3FC-200D-2764-200D-1F48B-200D-1F468-1F3FF","image":"1f468-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png","sheet_x":17,"sheet_y":20,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F468-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB","non_qualified":"1F468-1F3FD-200D-2764-200D-1F48B-200D-1F468-1F3FB","image":"1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png","sheet_x":17,"sheet_y":21,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F468-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FC","non_qualified":"1F468-1F3FD-200D-2764-200D-1F48B-200D-1F468-1F3FC","image":"1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png","sheet_x":17,"sheet_y":22,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FD":{"unified":"1F468-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FD","non_qualified":"1F468-1F3FD-200D-2764-200D-1F48B-200D-1F468-1F3FD","image":"1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png","sheet_x":17,"sheet_y":23,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F468-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FE","non_qualified":"1F468-1F3FD-200D-2764-200D-1F48B-200D-1F468-1F3FE","image":"1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png","sheet_x":17,"sheet_y":24,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F468-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FF","non_qualified":"1F468-1F3FD-200D-2764-200D-1F48B-200D-1F468-1F3FF","image":"1f468-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png","sheet_x":17,"sheet_y":25,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F468-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB","non_qualified":"1F468-1F3FE-200D-2764-200D-1F48B-200D-1F468-1F3FB","image":"1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png","sheet_x":17,"sheet_y":26,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F468-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FC","non_qualified":"1F468-1F3FE-200D-2764-200D-1F48B-200D-1F468-1F3FC","image":"1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png","sheet_x":17,"sheet_y":27,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F468-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FD","non_qualified":"1F468-1F3FE-200D-2764-200D-1F48B-200D-1F468-1F3FD","image":"1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png","sheet_x":17,"sheet_y":28,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FE":{"unified":"1F468-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FE","non_qualified":"1F468-1F3FE-200D-2764-200D-1F48B-200D-1F468-1F3FE","image":"1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png","sheet_x":17,"sheet_y":29,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F468-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FF","non_qualified":"1F468-1F3FE-200D-2764-200D-1F48B-200D-1F468-1F3FF","image":"1f468-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png","sheet_x":17,"sheet_y":30,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F468-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB","non_qualified":"1F468-1F3FF-200D-2764-200D-1F48B-200D-1F468-1F3FB","image":"1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png","sheet_x":17,"sheet_y":31,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F468-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FC","non_qualified":"1F468-1F3FF-200D-2764-200D-1F48B-200D-1F468-1F3FC","image":"1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png","sheet_x":17,"sheet_y":32,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F468-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FD","non_qualified":"1F468-1F3FF-200D-2764-200D-1F48B-200D-1F468-1F3FD","image":"1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png","sheet_x":17,"sheet_y":33,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F468-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FE","non_qualified":"1F468-1F3FF-200D-2764-200D-1F48B-200D-1F468-1F3FE","image":"1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png","sheet_x":17,"sheet_y":34,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FF":{"unified":"1F468-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FF","non_qualified":"1F468-1F3FF-200D-2764-200D-1F48B-200D-1F468-1F3FF","image":"1f468-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png","sheet_x":17,"sheet_y":35,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN","unified":"1F468","non_qualified":null,"docomo":"E6F0","au":"E4FC","softbank":"E004","google":"FE19D","image":"1f468.png","sheet_x":17,"sheet_y":36,"short_name":"man","short_names":["man"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":236,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F468-1F3FB","non_qualified":null,"image":"1f468-1f3fb.png","sheet_x":17,"sheet_y":37,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F468-1F3FC","non_qualified":null,"image":"1f468-1f3fc.png","sheet_x":17,"sheet_y":38,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F468-1F3FD","non_qualified":null,"image":"1f468-1f3fd.png","sheet_x":17,"sheet_y":39,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F468-1F3FE","non_qualified":null,"image":"1f468-1f3fe.png","sheet_x":17,"sheet_y":40,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F468-1F3FF","non_qualified":null,"image":"1f468-1f3ff.png","sheet_x":17,"sheet_y":41,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN FARMER","unified":"1F469-200D-1F33E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f33e.png","sheet_x":17,"sheet_y":42,"short_name":"female-farmer","short_names":["female-farmer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":302,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F33E","non_qualified":null,"image":"1f469-1f3fb-200d-1f33e.png","sheet_x":17,"sheet_y":43,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F33E","non_qualified":null,"image":"1f469-1f3fc-200d-1f33e.png","sheet_x":17,"sheet_y":44,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F33E","non_qualified":null,"image":"1f469-1f3fd-200d-1f33e.png","sheet_x":17,"sheet_y":45,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F33E","non_qualified":null,"image":"1f469-1f3fe-200d-1f33e.png","sheet_x":17,"sheet_y":46,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F33E","non_qualified":null,"image":"1f469-1f3ff-200d-1f33e.png","sheet_x":17,"sheet_y":47,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN COOK","unified":"1F469-200D-1F373","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f373.png","sheet_x":17,"sheet_y":48,"short_name":"female-cook","short_names":["female-cook"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":305,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F373","non_qualified":null,"image":"1f469-1f3fb-200d-1f373.png","sheet_x":17,"sheet_y":49,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F373","non_qualified":null,"image":"1f469-1f3fc-200d-1f373.png","sheet_x":17,"sheet_y":50,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F373","non_qualified":null,"image":"1f469-1f3fd-200d-1f373.png","sheet_x":17,"sheet_y":51,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F373","non_qualified":null,"image":"1f469-1f3fe-200d-1f373.png","sheet_x":17,"sheet_y":52,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F373","non_qualified":null,"image":"1f469-1f3ff-200d-1f373.png","sheet_x":17,"sheet_y":53,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN FEEDING BABY","unified":"1F469-200D-1F37C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f37c.png","sheet_x":17,"sheet_y":54,"short_name":"woman_feeding_baby","short_names":["woman_feeding_baby"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":367,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F37C","non_qualified":null,"image":"1f469-1f3fb-200d-1f37c.png","sheet_x":17,"sheet_y":55,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F37C","non_qualified":null,"image":"1f469-1f3fc-200d-1f37c.png","sheet_x":17,"sheet_y":56,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F37C","non_qualified":null,"image":"1f469-1f3fd-200d-1f37c.png","sheet_x":17,"sheet_y":57,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F37C","non_qualified":null,"image":"1f469-1f3fe-200d-1f37c.png","sheet_x":17,"sheet_y":58,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F37C","non_qualified":null,"image":"1f469-1f3ff-200d-1f37c.png","sheet_x":17,"sheet_y":59,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN STUDENT","unified":"1F469-200D-1F393","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f393.png","sheet_x":17,"sheet_y":60,"short_name":"female-student","short_names":["female-student"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":293,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F393","non_qualified":null,"image":"1f469-1f3fb-200d-1f393.png","sheet_x":17,"sheet_y":61,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F393","non_qualified":null,"image":"1f469-1f3fc-200d-1f393.png","sheet_x":18,"sheet_y":0,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F393","non_qualified":null,"image":"1f469-1f3fd-200d-1f393.png","sheet_x":18,"sheet_y":1,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F393","non_qualified":null,"image":"1f469-1f3fe-200d-1f393.png","sheet_x":18,"sheet_y":2,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F393","non_qualified":null,"image":"1f469-1f3ff-200d-1f393.png","sheet_x":18,"sheet_y":3,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN SINGER","unified":"1F469-200D-1F3A4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f3a4.png","sheet_x":18,"sheet_y":4,"short_name":"female-singer","short_names":["female-singer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":323,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F3A4","non_qualified":null,"image":"1f469-1f3fb-200d-1f3a4.png","sheet_x":18,"sheet_y":5,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F3A4","non_qualified":null,"image":"1f469-1f3fc-200d-1f3a4.png","sheet_x":18,"sheet_y":6,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F3A4","non_qualified":null,"image":"1f469-1f3fd-200d-1f3a4.png","sheet_x":18,"sheet_y":7,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F3A4","non_qualified":null,"image":"1f469-1f3fe-200d-1f3a4.png","sheet_x":18,"sheet_y":8,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F3A4","non_qualified":null,"image":"1f469-1f3ff-200d-1f3a4.png","sheet_x":18,"sheet_y":9,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN ARTIST","unified":"1F469-200D-1F3A8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f3a8.png","sheet_x":18,"sheet_y":10,"short_name":"female-artist","short_names":["female-artist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":326,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F3A8","non_qualified":null,"image":"1f469-1f3fb-200d-1f3a8.png","sheet_x":18,"sheet_y":11,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F3A8","non_qualified":null,"image":"1f469-1f3fc-200d-1f3a8.png","sheet_x":18,"sheet_y":12,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F3A8","non_qualified":null,"image":"1f469-1f3fd-200d-1f3a8.png","sheet_x":18,"sheet_y":13,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F3A8","non_qualified":null,"image":"1f469-1f3fe-200d-1f3a8.png","sheet_x":18,"sheet_y":14,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F3A8","non_qualified":null,"image":"1f469-1f3ff-200d-1f3a8.png","sheet_x":18,"sheet_y":15,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN TEACHER","unified":"1F469-200D-1F3EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f3eb.png","sheet_x":18,"sheet_y":16,"short_name":"female-teacher","short_names":["female-teacher"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":296,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F3EB","non_qualified":null,"image":"1f469-1f3fb-200d-1f3eb.png","sheet_x":18,"sheet_y":17,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F3EB","non_qualified":null,"image":"1f469-1f3fc-200d-1f3eb.png","sheet_x":18,"sheet_y":18,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F3EB","non_qualified":null,"image":"1f469-1f3fd-200d-1f3eb.png","sheet_x":18,"sheet_y":19,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F3EB","non_qualified":null,"image":"1f469-1f3fe-200d-1f3eb.png","sheet_x":18,"sheet_y":20,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F3EB","non_qualified":null,"image":"1f469-1f3ff-200d-1f3eb.png","sheet_x":18,"sheet_y":21,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN FACTORY WORKER","unified":"1F469-200D-1F3ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f3ed.png","sheet_x":18,"sheet_y":22,"short_name":"female-factory-worker","short_names":["female-factory-worker"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":311,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F3ED","non_qualified":null,"image":"1f469-1f3fb-200d-1f3ed.png","sheet_x":18,"sheet_y":23,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F3ED","non_qualified":null,"image":"1f469-1f3fc-200d-1f3ed.png","sheet_x":18,"sheet_y":24,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F3ED","non_qualified":null,"image":"1f469-1f3fd-200d-1f3ed.png","sheet_x":18,"sheet_y":25,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F3ED","non_qualified":null,"image":"1f469-1f3fe-200d-1f3ed.png","sheet_x":18,"sheet_y":26,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F3ED","non_qualified":null,"image":"1f469-1f3ff-200d-1f3ed.png","sheet_x":18,"sheet_y":27,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FAMILY: WOMAN, BOY, BOY","unified":"1F469-200D-1F466-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f466-200d-1f466.png","sheet_x":18,"sheet_y":28,"short_name":"woman-boy-boy","short_names":["woman-boy-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":540,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: WOMAN, BOY","unified":"1F469-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f466.png","sheet_x":18,"sheet_y":29,"short_name":"woman-boy","short_names":["woman-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":539,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: WOMAN, GIRL, BOY","unified":"1F469-200D-1F467-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f467-200d-1f466.png","sheet_x":18,"sheet_y":30,"short_name":"woman-girl-boy","short_names":["woman-girl-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":542,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: WOMAN, GIRL, GIRL","unified":"1F469-200D-1F467-200D-1F467","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f467-200d-1f467.png","sheet_x":18,"sheet_y":31,"short_name":"woman-girl-girl","short_names":["woman-girl-girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":543,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: WOMAN, GIRL","unified":"1F469-200D-1F467","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f467.png","sheet_x":18,"sheet_y":32,"short_name":"woman-girl","short_names":["woman-girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":541,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: WOMAN, WOMAN, BOY","unified":"1F469-200D-1F469-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f469-200d-1f466.png","sheet_x":18,"sheet_y":33,"short_name":"woman-woman-boy","short_names":["woman-woman-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":529,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: WOMAN, WOMAN, BOY, BOY","unified":"1F469-200D-1F469-200D-1F466-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f469-200d-1f466-200d-1f466.png","sheet_x":18,"sheet_y":34,"short_name":"woman-woman-boy-boy","short_names":["woman-woman-boy-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":532,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: WOMAN, WOMAN, GIRL","unified":"1F469-200D-1F469-200D-1F467","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f469-200d-1f467.png","sheet_x":18,"sheet_y":35,"short_name":"woman-woman-girl","short_names":["woman-woman-girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":530,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: WOMAN, WOMAN, GIRL, BOY","unified":"1F469-200D-1F469-200D-1F467-200D-1F466","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f469-200d-1f467-200d-1f466.png","sheet_x":18,"sheet_y":36,"short_name":"woman-woman-girl-boy","short_names":["woman-woman-girl-boy"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":531,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAMILY: WOMAN, WOMAN, GIRL, GIRL","unified":"1F469-200D-1F469-200D-1F467-200D-1F467","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f469-200d-1f467-200d-1f467.png","sheet_x":18,"sheet_y":37,"short_name":"woman-woman-girl-girl","short_names":["woman-woman-girl-girl"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":533,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN TECHNOLOGIST","unified":"1F469-200D-1F4BB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f4bb.png","sheet_x":18,"sheet_y":38,"short_name":"female-technologist","short_names":["female-technologist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":320,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F4BB","non_qualified":null,"image":"1f469-1f3fb-200d-1f4bb.png","sheet_x":18,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F4BB","non_qualified":null,"image":"1f469-1f3fc-200d-1f4bb.png","sheet_x":18,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F4BB","non_qualified":null,"image":"1f469-1f3fd-200d-1f4bb.png","sheet_x":18,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F4BB","non_qualified":null,"image":"1f469-1f3fe-200d-1f4bb.png","sheet_x":18,"sheet_y":42,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F4BB","non_qualified":null,"image":"1f469-1f3ff-200d-1f4bb.png","sheet_x":18,"sheet_y":43,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN OFFICE WORKER","unified":"1F469-200D-1F4BC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f4bc.png","sheet_x":18,"sheet_y":44,"short_name":"female-office-worker","short_names":["female-office-worker"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":314,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F4BC","non_qualified":null,"image":"1f469-1f3fb-200d-1f4bc.png","sheet_x":18,"sheet_y":45,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F4BC","non_qualified":null,"image":"1f469-1f3fc-200d-1f4bc.png","sheet_x":18,"sheet_y":46,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F4BC","non_qualified":null,"image":"1f469-1f3fd-200d-1f4bc.png","sheet_x":18,"sheet_y":47,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F4BC","non_qualified":null,"image":"1f469-1f3fe-200d-1f4bc.png","sheet_x":18,"sheet_y":48,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F4BC","non_qualified":null,"image":"1f469-1f3ff-200d-1f4bc.png","sheet_x":18,"sheet_y":49,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN MECHANIC","unified":"1F469-200D-1F527","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f527.png","sheet_x":18,"sheet_y":50,"short_name":"female-mechanic","short_names":["female-mechanic"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":308,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F527","non_qualified":null,"image":"1f469-1f3fb-200d-1f527.png","sheet_x":18,"sheet_y":51,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F527","non_qualified":null,"image":"1f469-1f3fc-200d-1f527.png","sheet_x":18,"sheet_y":52,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F527","non_qualified":null,"image":"1f469-1f3fd-200d-1f527.png","sheet_x":18,"sheet_y":53,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F527","non_qualified":null,"image":"1f469-1f3fe-200d-1f527.png","sheet_x":18,"sheet_y":54,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F527","non_qualified":null,"image":"1f469-1f3ff-200d-1f527.png","sheet_x":18,"sheet_y":55,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN SCIENTIST","unified":"1F469-200D-1F52C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f52c.png","sheet_x":18,"sheet_y":56,"short_name":"female-scientist","short_names":["female-scientist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":317,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F52C","non_qualified":null,"image":"1f469-1f3fb-200d-1f52c.png","sheet_x":18,"sheet_y":57,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F52C","non_qualified":null,"image":"1f469-1f3fc-200d-1f52c.png","sheet_x":18,"sheet_y":58,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F52C","non_qualified":null,"image":"1f469-1f3fd-200d-1f52c.png","sheet_x":18,"sheet_y":59,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F52C","non_qualified":null,"image":"1f469-1f3fe-200d-1f52c.png","sheet_x":18,"sheet_y":60,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F52C","non_qualified":null,"image":"1f469-1f3ff-200d-1f52c.png","sheet_x":18,"sheet_y":61,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN ASTRONAUT","unified":"1F469-200D-1F680","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f680.png","sheet_x":19,"sheet_y":0,"short_name":"female-astronaut","short_names":["female-astronaut"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":332,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F680","non_qualified":null,"image":"1f469-1f3fb-200d-1f680.png","sheet_x":19,"sheet_y":1,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F680","non_qualified":null,"image":"1f469-1f3fc-200d-1f680.png","sheet_x":19,"sheet_y":2,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F680","non_qualified":null,"image":"1f469-1f3fd-200d-1f680.png","sheet_x":19,"sheet_y":3,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F680","non_qualified":null,"image":"1f469-1f3fe-200d-1f680.png","sheet_x":19,"sheet_y":4,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F680","non_qualified":null,"image":"1f469-1f3ff-200d-1f680.png","sheet_x":19,"sheet_y":5,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN FIREFIGHTER","unified":"1F469-200D-1F692","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f692.png","sheet_x":19,"sheet_y":6,"short_name":"female-firefighter","short_names":["female-firefighter"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":335,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F692","non_qualified":null,"image":"1f469-1f3fb-200d-1f692.png","sheet_x":19,"sheet_y":7,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F692","non_qualified":null,"image":"1f469-1f3fc-200d-1f692.png","sheet_x":19,"sheet_y":8,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F692","non_qualified":null,"image":"1f469-1f3fd-200d-1f692.png","sheet_x":19,"sheet_y":9,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F692","non_qualified":null,"image":"1f469-1f3fe-200d-1f692.png","sheet_x":19,"sheet_y":10,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F692","non_qualified":null,"image":"1f469-1f3ff-200d-1f692.png","sheet_x":19,"sheet_y":11,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN WITH WHITE CANE FACING RIGHT","unified":"1F469-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F469-200D-1F9AF-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f9af-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":12,"short_name":"woman_with_white_cane_facing_right","short_names":["woman_with_white_cane_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":428,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F469-1F3FB-200D-1F9AF-200D-27A1","image":"1f469-1f3fb-200d-1f9af-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":13,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F469-1F3FC-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F469-1F3FC-200D-1F9AF-200D-27A1","image":"1f469-1f3fc-200d-1f9af-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":14,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F469-1F3FD-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F469-1F3FD-200D-1F9AF-200D-27A1","image":"1f469-1f3fd-200d-1f9af-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":15,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F469-1F3FE-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F469-1F3FE-200D-1F9AF-200D-27A1","image":"1f469-1f3fe-200d-1f9af-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":16,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F469-1F3FF-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F469-1F3FF-200D-1F9AF-200D-27A1","image":"1f469-1f3ff-200d-1f9af-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":17,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"WOMAN WITH WHITE CANE","unified":"1F469-200D-1F9AF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f9af.png","sheet_x":19,"sheet_y":18,"short_name":"woman_with_probing_cane","short_names":["woman_with_probing_cane"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":427,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F9AF","non_qualified":null,"image":"1f469-1f3fb-200d-1f9af.png","sheet_x":19,"sheet_y":19,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F9AF","non_qualified":null,"image":"1f469-1f3fc-200d-1f9af.png","sheet_x":19,"sheet_y":20,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F9AF","non_qualified":null,"image":"1f469-1f3fd-200d-1f9af.png","sheet_x":19,"sheet_y":21,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F9AF","non_qualified":null,"image":"1f469-1f3fe-200d-1f9af.png","sheet_x":19,"sheet_y":22,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F9AF","non_qualified":null,"image":"1f469-1f3ff-200d-1f9af.png","sheet_x":19,"sheet_y":23,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN: RED HAIR","unified":"1F469-200D-1F9B0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f9b0.png","sheet_x":19,"sheet_y":24,"short_name":"red_haired_woman","short_names":["red_haired_woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":245,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F9B0","non_qualified":null,"image":"1f469-1f3fb-200d-1f9b0.png","sheet_x":19,"sheet_y":25,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F9B0","non_qualified":null,"image":"1f469-1f3fc-200d-1f9b0.png","sheet_x":19,"sheet_y":26,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F9B0","non_qualified":null,"image":"1f469-1f3fd-200d-1f9b0.png","sheet_x":19,"sheet_y":27,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F9B0","non_qualified":null,"image":"1f469-1f3fe-200d-1f9b0.png","sheet_x":19,"sheet_y":28,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F9B0","non_qualified":null,"image":"1f469-1f3ff-200d-1f9b0.png","sheet_x":19,"sheet_y":29,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN: CURLY HAIR","unified":"1F469-200D-1F9B1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f9b1.png","sheet_x":19,"sheet_y":30,"short_name":"curly_haired_woman","short_names":["curly_haired_woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":247,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F9B1","non_qualified":null,"image":"1f469-1f3fb-200d-1f9b1.png","sheet_x":19,"sheet_y":31,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F9B1","non_qualified":null,"image":"1f469-1f3fc-200d-1f9b1.png","sheet_x":19,"sheet_y":32,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F9B1","non_qualified":null,"image":"1f469-1f3fd-200d-1f9b1.png","sheet_x":19,"sheet_y":33,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F9B1","non_qualified":null,"image":"1f469-1f3fe-200d-1f9b1.png","sheet_x":19,"sheet_y":34,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F9B1","non_qualified":null,"image":"1f469-1f3ff-200d-1f9b1.png","sheet_x":19,"sheet_y":35,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN: BALD","unified":"1F469-200D-1F9B2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f9b2.png","sheet_x":19,"sheet_y":36,"short_name":"bald_woman","short_names":["bald_woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":251,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F9B2","non_qualified":null,"image":"1f469-1f3fb-200d-1f9b2.png","sheet_x":19,"sheet_y":37,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F9B2","non_qualified":null,"image":"1f469-1f3fc-200d-1f9b2.png","sheet_x":19,"sheet_y":38,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F9B2","non_qualified":null,"image":"1f469-1f3fd-200d-1f9b2.png","sheet_x":19,"sheet_y":39,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F9B2","non_qualified":null,"image":"1f469-1f3fe-200d-1f9b2.png","sheet_x":19,"sheet_y":40,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F9B2","non_qualified":null,"image":"1f469-1f3ff-200d-1f9b2.png","sheet_x":19,"sheet_y":41,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN: WHITE HAIR","unified":"1F469-200D-1F9B3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f9b3.png","sheet_x":19,"sheet_y":42,"short_name":"white_haired_woman","short_names":["white_haired_woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":249,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F9B3","non_qualified":null,"image":"1f469-1f3fb-200d-1f9b3.png","sheet_x":19,"sheet_y":43,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F9B3","non_qualified":null,"image":"1f469-1f3fc-200d-1f9b3.png","sheet_x":19,"sheet_y":44,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F9B3","non_qualified":null,"image":"1f469-1f3fd-200d-1f9b3.png","sheet_x":19,"sheet_y":45,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F9B3","non_qualified":null,"image":"1f469-1f3fe-200d-1f9b3.png","sheet_x":19,"sheet_y":46,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F9B3","non_qualified":null,"image":"1f469-1f3ff-200d-1f9b3.png","sheet_x":19,"sheet_y":47,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN IN MOTORIZED WHEELCHAIR FACING RIGHT","unified":"1F469-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F469-200D-1F9BC-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":48,"short_name":"woman_in_motorized_wheelchair_facing_right","short_names":["woman_in_motorized_wheelchair_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":434,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F469-1F3FB-200D-1F9BC-200D-27A1","image":"1f469-1f3fb-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":49,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F469-1F3FC-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F469-1F3FC-200D-1F9BC-200D-27A1","image":"1f469-1f3fc-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":50,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F469-1F3FD-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F469-1F3FD-200D-1F9BC-200D-27A1","image":"1f469-1f3fd-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":51,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F469-1F3FE-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F469-1F3FE-200D-1F9BC-200D-27A1","image":"1f469-1f3fe-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":52,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F469-1F3FF-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F469-1F3FF-200D-1F9BC-200D-27A1","image":"1f469-1f3ff-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":53,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"WOMAN IN MOTORIZED WHEELCHAIR","unified":"1F469-200D-1F9BC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f9bc.png","sheet_x":19,"sheet_y":54,"short_name":"woman_in_motorized_wheelchair","short_names":["woman_in_motorized_wheelchair"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":433,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F9BC","non_qualified":null,"image":"1f469-1f3fb-200d-1f9bc.png","sheet_x":19,"sheet_y":55,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F9BC","non_qualified":null,"image":"1f469-1f3fc-200d-1f9bc.png","sheet_x":19,"sheet_y":56,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F9BC","non_qualified":null,"image":"1f469-1f3fd-200d-1f9bc.png","sheet_x":19,"sheet_y":57,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F9BC","non_qualified":null,"image":"1f469-1f3fe-200d-1f9bc.png","sheet_x":19,"sheet_y":58,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F9BC","non_qualified":null,"image":"1f469-1f3ff-200d-1f9bc.png","sheet_x":19,"sheet_y":59,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN IN MANUAL WHEELCHAIR FACING RIGHT","unified":"1F469-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F469-200D-1F9BD-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":60,"short_name":"woman_in_manual_wheelchair_facing_right","short_names":["woman_in_manual_wheelchair_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":440,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F469-1F3FB-200D-1F9BD-200D-27A1","image":"1f469-1f3fb-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":19,"sheet_y":61,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F469-1F3FC-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F469-1F3FC-200D-1F9BD-200D-27A1","image":"1f469-1f3fc-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":20,"sheet_y":0,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F469-1F3FD-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F469-1F3FD-200D-1F9BD-200D-27A1","image":"1f469-1f3fd-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":20,"sheet_y":1,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F469-1F3FE-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F469-1F3FE-200D-1F9BD-200D-27A1","image":"1f469-1f3fe-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":20,"sheet_y":2,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F469-1F3FF-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F469-1F3FF-200D-1F9BD-200D-27A1","image":"1f469-1f3ff-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":20,"sheet_y":3,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"WOMAN IN MANUAL WHEELCHAIR","unified":"1F469-200D-1F9BD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f9bd.png","sheet_x":20,"sheet_y":4,"short_name":"woman_in_manual_wheelchair","short_names":["woman_in_manual_wheelchair"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":439,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-1F9BD","non_qualified":null,"image":"1f469-1f3fb-200d-1f9bd.png","sheet_x":20,"sheet_y":5,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-1F9BD","non_qualified":null,"image":"1f469-1f3fc-200d-1f9bd.png","sheet_x":20,"sheet_y":6,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-1F9BD","non_qualified":null,"image":"1f469-1f3fd-200d-1f9bd.png","sheet_x":20,"sheet_y":7,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-1F9BD","non_qualified":null,"image":"1f469-1f3fe-200d-1f9bd.png","sheet_x":20,"sheet_y":8,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-1F9BD","non_qualified":null,"image":"1f469-1f3ff-200d-1f9bd.png","sheet_x":20,"sheet_y":9,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN HEALTH WORKER","unified":"1F469-200D-2695-FE0F","non_qualified":"1F469-200D-2695","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-2695-fe0f.png","sheet_x":20,"sheet_y":10,"short_name":"female-doctor","short_names":["female-doctor"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":290,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-2695-FE0F","non_qualified":"1F469-1F3FB-200D-2695","image":"1f469-1f3fb-200d-2695-fe0f.png","sheet_x":20,"sheet_y":11,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-2695-FE0F","non_qualified":"1F469-1F3FC-200D-2695","image":"1f469-1f3fc-200d-2695-fe0f.png","sheet_x":20,"sheet_y":12,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-2695-FE0F","non_qualified":"1F469-1F3FD-200D-2695","image":"1f469-1f3fd-200d-2695-fe0f.png","sheet_x":20,"sheet_y":13,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-2695-FE0F","non_qualified":"1F469-1F3FE-200D-2695","image":"1f469-1f3fe-200d-2695-fe0f.png","sheet_x":20,"sheet_y":14,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-2695-FE0F","non_qualified":"1F469-1F3FF-200D-2695","image":"1f469-1f3ff-200d-2695-fe0f.png","sheet_x":20,"sheet_y":15,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN JUDGE","unified":"1F469-200D-2696-FE0F","non_qualified":"1F469-200D-2696","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-2696-fe0f.png","sheet_x":20,"sheet_y":16,"short_name":"female-judge","short_names":["female-judge"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":299,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-2696-FE0F","non_qualified":"1F469-1F3FB-200D-2696","image":"1f469-1f3fb-200d-2696-fe0f.png","sheet_x":20,"sheet_y":17,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-2696-FE0F","non_qualified":"1F469-1F3FC-200D-2696","image":"1f469-1f3fc-200d-2696-fe0f.png","sheet_x":20,"sheet_y":18,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-2696-FE0F","non_qualified":"1F469-1F3FD-200D-2696","image":"1f469-1f3fd-200d-2696-fe0f.png","sheet_x":20,"sheet_y":19,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-2696-FE0F","non_qualified":"1F469-1F3FE-200D-2696","image":"1f469-1f3fe-200d-2696-fe0f.png","sheet_x":20,"sheet_y":20,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-2696-FE0F","non_qualified":"1F469-1F3FF-200D-2696","image":"1f469-1f3ff-200d-2696-fe0f.png","sheet_x":20,"sheet_y":21,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN PILOT","unified":"1F469-200D-2708-FE0F","non_qualified":"1F469-200D-2708","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-2708-fe0f.png","sheet_x":20,"sheet_y":22,"short_name":"female-pilot","short_names":["female-pilot"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":329,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB-200D-2708-FE0F","non_qualified":"1F469-1F3FB-200D-2708","image":"1f469-1f3fb-200d-2708-fe0f.png","sheet_x":20,"sheet_y":23,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC-200D-2708-FE0F","non_qualified":"1F469-1F3FC-200D-2708","image":"1f469-1f3fc-200d-2708-fe0f.png","sheet_x":20,"sheet_y":24,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD-200D-2708-FE0F","non_qualified":"1F469-1F3FD-200D-2708","image":"1f469-1f3fd-200d-2708-fe0f.png","sheet_x":20,"sheet_y":25,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE-200D-2708-FE0F","non_qualified":"1F469-1F3FE-200D-2708","image":"1f469-1f3fe-200d-2708-fe0f.png","sheet_x":20,"sheet_y":26,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF-200D-2708-FE0F","non_qualified":"1F469-1F3FF-200D-2708","image":"1f469-1f3ff-200d-2708-fe0f.png","sheet_x":20,"sheet_y":27,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"COUPLE WITH HEART: WOMAN, MAN","unified":"1F469-200D-2764-FE0F-200D-1F468","non_qualified":"1F469-200D-2764-200D-1F468","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-2764-fe0f-200d-1f468.png","sheet_x":20,"sheet_y":28,"short_name":"woman-heart-man","short_names":["woman-heart-man"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":516,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB-1F3FB":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F468-1F3FB","non_qualified":"1F469-1F3FB-200D-2764-200D-1F468-1F3FB","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fb.png","sheet_x":20,"sheet_y":29,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F468-1F3FC","non_qualified":"1F469-1F3FB-200D-2764-200D-1F468-1F3FC","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fc.png","sheet_x":20,"sheet_y":30,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F468-1F3FD","non_qualified":"1F469-1F3FB-200D-2764-200D-1F468-1F3FD","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fd.png","sheet_x":20,"sheet_y":31,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F468-1F3FE","non_qualified":"1F469-1F3FB-200D-2764-200D-1F468-1F3FE","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3fe.png","sheet_x":20,"sheet_y":32,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F468-1F3FF","non_qualified":"1F469-1F3FB-200D-2764-200D-1F468-1F3FF","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f468-1f3ff.png","sheet_x":20,"sheet_y":33,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F468-1F3FB","non_qualified":"1F469-1F3FC-200D-2764-200D-1F468-1F3FB","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fb.png","sheet_x":20,"sheet_y":34,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FC":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F468-1F3FC","non_qualified":"1F469-1F3FC-200D-2764-200D-1F468-1F3FC","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fc.png","sheet_x":20,"sheet_y":35,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F468-1F3FD","non_qualified":"1F469-1F3FC-200D-2764-200D-1F468-1F3FD","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fd.png","sheet_x":20,"sheet_y":36,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F468-1F3FE","non_qualified":"1F469-1F3FC-200D-2764-200D-1F468-1F3FE","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3fe.png","sheet_x":20,"sheet_y":37,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F468-1F3FF","non_qualified":"1F469-1F3FC-200D-2764-200D-1F468-1F3FF","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f468-1f3ff.png","sheet_x":20,"sheet_y":38,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F468-1F3FB","non_qualified":"1F469-1F3FD-200D-2764-200D-1F468-1F3FB","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fb.png","sheet_x":20,"sheet_y":39,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F468-1F3FC","non_qualified":"1F469-1F3FD-200D-2764-200D-1F468-1F3FC","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fc.png","sheet_x":20,"sheet_y":40,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FD":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F468-1F3FD","non_qualified":"1F469-1F3FD-200D-2764-200D-1F468-1F3FD","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fd.png","sheet_x":20,"sheet_y":41,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F468-1F3FE","non_qualified":"1F469-1F3FD-200D-2764-200D-1F468-1F3FE","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3fe.png","sheet_x":20,"sheet_y":42,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F468-1F3FF","non_qualified":"1F469-1F3FD-200D-2764-200D-1F468-1F3FF","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f468-1f3ff.png","sheet_x":20,"sheet_y":43,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F468-1F3FB","non_qualified":"1F469-1F3FE-200D-2764-200D-1F468-1F3FB","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fb.png","sheet_x":20,"sheet_y":44,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F468-1F3FC","non_qualified":"1F469-1F3FE-200D-2764-200D-1F468-1F3FC","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fc.png","sheet_x":20,"sheet_y":45,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F468-1F3FD","non_qualified":"1F469-1F3FE-200D-2764-200D-1F468-1F3FD","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fd.png","sheet_x":20,"sheet_y":46,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FE":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F468-1F3FE","non_qualified":"1F469-1F3FE-200D-2764-200D-1F468-1F3FE","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3fe.png","sheet_x":20,"sheet_y":47,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F468-1F3FF","non_qualified":"1F469-1F3FE-200D-2764-200D-1F468-1F3FF","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f468-1f3ff.png","sheet_x":20,"sheet_y":48,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F468-1F3FB","non_qualified":"1F469-1F3FF-200D-2764-200D-1F468-1F3FB","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fb.png","sheet_x":20,"sheet_y":49,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F468-1F3FC","non_qualified":"1F469-1F3FF-200D-2764-200D-1F468-1F3FC","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fc.png","sheet_x":20,"sheet_y":50,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F468-1F3FD","non_qualified":"1F469-1F3FF-200D-2764-200D-1F468-1F3FD","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fd.png","sheet_x":20,"sheet_y":51,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F468-1F3FE","non_qualified":"1F469-1F3FF-200D-2764-200D-1F468-1F3FE","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3fe.png","sheet_x":20,"sheet_y":52,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FF":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F468-1F3FF","non_qualified":"1F469-1F3FF-200D-2764-200D-1F468-1F3FF","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f468-1f3ff.png","sheet_x":20,"sheet_y":53,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"COUPLE WITH HEART: WOMAN, WOMAN","unified":"1F469-200D-2764-FE0F-200D-1F469","non_qualified":"1F469-200D-2764-200D-1F469","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-2764-fe0f-200d-1f469.png","sheet_x":20,"sheet_y":54,"short_name":"woman-heart-woman","short_names":["woman-heart-woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":518,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB-1F3FB":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F469-1F3FB","non_qualified":"1F469-1F3FB-200D-2764-200D-1F469-1F3FB","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fb.png","sheet_x":20,"sheet_y":55,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F469-1F3FC","non_qualified":"1F469-1F3FB-200D-2764-200D-1F469-1F3FC","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fc.png","sheet_x":20,"sheet_y":56,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F469-1F3FD","non_qualified":"1F469-1F3FB-200D-2764-200D-1F469-1F3FD","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fd.png","sheet_x":20,"sheet_y":57,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F469-1F3FE","non_qualified":"1F469-1F3FB-200D-2764-200D-1F469-1F3FE","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3fe.png","sheet_x":20,"sheet_y":58,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F469-1F3FF","non_qualified":"1F469-1F3FB-200D-2764-200D-1F469-1F3FF","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f469-1f3ff.png","sheet_x":20,"sheet_y":59,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F469-1F3FB","non_qualified":"1F469-1F3FC-200D-2764-200D-1F469-1F3FB","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fb.png","sheet_x":20,"sheet_y":60,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FC":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F469-1F3FC","non_qualified":"1F469-1F3FC-200D-2764-200D-1F469-1F3FC","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fc.png","sheet_x":20,"sheet_y":61,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F469-1F3FD","non_qualified":"1F469-1F3FC-200D-2764-200D-1F469-1F3FD","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fd.png","sheet_x":21,"sheet_y":0,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F469-1F3FE","non_qualified":"1F469-1F3FC-200D-2764-200D-1F469-1F3FE","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3fe.png","sheet_x":21,"sheet_y":1,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F469-1F3FF","non_qualified":"1F469-1F3FC-200D-2764-200D-1F469-1F3FF","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f469-1f3ff.png","sheet_x":21,"sheet_y":2,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F469-1F3FB","non_qualified":"1F469-1F3FD-200D-2764-200D-1F469-1F3FB","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fb.png","sheet_x":21,"sheet_y":3,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F469-1F3FC","non_qualified":"1F469-1F3FD-200D-2764-200D-1F469-1F3FC","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fc.png","sheet_x":21,"sheet_y":4,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FD":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F469-1F3FD","non_qualified":"1F469-1F3FD-200D-2764-200D-1F469-1F3FD","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fd.png","sheet_x":21,"sheet_y":5,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F469-1F3FE","non_qualified":"1F469-1F3FD-200D-2764-200D-1F469-1F3FE","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3fe.png","sheet_x":21,"sheet_y":6,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F469-1F3FF","non_qualified":"1F469-1F3FD-200D-2764-200D-1F469-1F3FF","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f469-1f3ff.png","sheet_x":21,"sheet_y":7,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F469-1F3FB","non_qualified":"1F469-1F3FE-200D-2764-200D-1F469-1F3FB","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fb.png","sheet_x":21,"sheet_y":8,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F469-1F3FC","non_qualified":"1F469-1F3FE-200D-2764-200D-1F469-1F3FC","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fc.png","sheet_x":21,"sheet_y":9,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F469-1F3FD","non_qualified":"1F469-1F3FE-200D-2764-200D-1F469-1F3FD","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fd.png","sheet_x":21,"sheet_y":10,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FE":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F469-1F3FE","non_qualified":"1F469-1F3FE-200D-2764-200D-1F469-1F3FE","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3fe.png","sheet_x":21,"sheet_y":11,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F469-1F3FF","non_qualified":"1F469-1F3FE-200D-2764-200D-1F469-1F3FF","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f469-1f3ff.png","sheet_x":21,"sheet_y":12,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F469-1F3FB","non_qualified":"1F469-1F3FF-200D-2764-200D-1F469-1F3FB","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fb.png","sheet_x":21,"sheet_y":13,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F469-1F3FC","non_qualified":"1F469-1F3FF-200D-2764-200D-1F469-1F3FC","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fc.png","sheet_x":21,"sheet_y":14,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F469-1F3FD","non_qualified":"1F469-1F3FF-200D-2764-200D-1F469-1F3FD","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fd.png","sheet_x":21,"sheet_y":15,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F469-1F3FE","non_qualified":"1F469-1F3FF-200D-2764-200D-1F469-1F3FE","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3fe.png","sheet_x":21,"sheet_y":16,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FF":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F469-1F3FF","non_qualified":"1F469-1F3FF-200D-2764-200D-1F469-1F3FF","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f469-1f3ff.png","sheet_x":21,"sheet_y":17,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"KISS: WOMAN, MAN","unified":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F468","non_qualified":"1F469-200D-2764-200D-1F48B-200D-1F468","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-2764-fe0f-200d-1f48b-200d-1f468.png","sheet_x":21,"sheet_y":18,"short_name":"woman-kiss-man","short_names":["woman-kiss-man"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":512,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB-1F3FB":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB","non_qualified":"1F469-1F3FB-200D-2764-200D-1F48B-200D-1F468-1F3FB","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png","sheet_x":21,"sheet_y":19,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FC","non_qualified":"1F469-1F3FB-200D-2764-200D-1F48B-200D-1F468-1F3FC","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png","sheet_x":21,"sheet_y":20,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FD","non_qualified":"1F469-1F3FB-200D-2764-200D-1F48B-200D-1F468-1F3FD","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png","sheet_x":21,"sheet_y":21,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FE","non_qualified":"1F469-1F3FB-200D-2764-200D-1F48B-200D-1F468-1F3FE","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png","sheet_x":21,"sheet_y":22,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FF","non_qualified":"1F469-1F3FB-200D-2764-200D-1F48B-200D-1F468-1F3FF","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png","sheet_x":21,"sheet_y":23,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB","non_qualified":"1F469-1F3FC-200D-2764-200D-1F48B-200D-1F468-1F3FB","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png","sheet_x":21,"sheet_y":24,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FC":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FC","non_qualified":"1F469-1F3FC-200D-2764-200D-1F48B-200D-1F468-1F3FC","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png","sheet_x":21,"sheet_y":25,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FD","non_qualified":"1F469-1F3FC-200D-2764-200D-1F48B-200D-1F468-1F3FD","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png","sheet_x":21,"sheet_y":26,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FE","non_qualified":"1F469-1F3FC-200D-2764-200D-1F48B-200D-1F468-1F3FE","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png","sheet_x":21,"sheet_y":27,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FF","non_qualified":"1F469-1F3FC-200D-2764-200D-1F48B-200D-1F468-1F3FF","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png","sheet_x":21,"sheet_y":28,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB","non_qualified":"1F469-1F3FD-200D-2764-200D-1F48B-200D-1F468-1F3FB","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png","sheet_x":21,"sheet_y":29,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FC","non_qualified":"1F469-1F3FD-200D-2764-200D-1F48B-200D-1F468-1F3FC","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png","sheet_x":21,"sheet_y":30,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FD":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FD","non_qualified":"1F469-1F3FD-200D-2764-200D-1F48B-200D-1F468-1F3FD","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png","sheet_x":21,"sheet_y":31,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FE","non_qualified":"1F469-1F3FD-200D-2764-200D-1F48B-200D-1F468-1F3FE","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png","sheet_x":21,"sheet_y":32,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FF","non_qualified":"1F469-1F3FD-200D-2764-200D-1F48B-200D-1F468-1F3FF","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png","sheet_x":21,"sheet_y":33,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB","non_qualified":"1F469-1F3FE-200D-2764-200D-1F48B-200D-1F468-1F3FB","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png","sheet_x":21,"sheet_y":34,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FC","non_qualified":"1F469-1F3FE-200D-2764-200D-1F48B-200D-1F468-1F3FC","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png","sheet_x":21,"sheet_y":35,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FD","non_qualified":"1F469-1F3FE-200D-2764-200D-1F48B-200D-1F468-1F3FD","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png","sheet_x":21,"sheet_y":36,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FE":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FE","non_qualified":"1F469-1F3FE-200D-2764-200D-1F48B-200D-1F468-1F3FE","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png","sheet_x":21,"sheet_y":37,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FF","non_qualified":"1F469-1F3FE-200D-2764-200D-1F48B-200D-1F468-1F3FF","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png","sheet_x":21,"sheet_y":38,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FB","non_qualified":"1F469-1F3FF-200D-2764-200D-1F48B-200D-1F468-1F3FB","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fb.png","sheet_x":21,"sheet_y":39,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FC","non_qualified":"1F469-1F3FF-200D-2764-200D-1F48B-200D-1F468-1F3FC","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fc.png","sheet_x":21,"sheet_y":40,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FD","non_qualified":"1F469-1F3FF-200D-2764-200D-1F48B-200D-1F468-1F3FD","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fd.png","sheet_x":21,"sheet_y":41,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FE","non_qualified":"1F469-1F3FF-200D-2764-200D-1F48B-200D-1F468-1F3FE","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3fe.png","sheet_x":21,"sheet_y":42,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FF":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F468-1F3FF","non_qualified":"1F469-1F3FF-200D-2764-200D-1F48B-200D-1F468-1F3FF","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f468-1f3ff.png","sheet_x":21,"sheet_y":43,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"KISS: WOMAN, WOMAN","unified":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F469","non_qualified":"1F469-200D-2764-200D-1F48B-200D-1F469","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.png","sheet_x":21,"sheet_y":44,"short_name":"woman-kiss-woman","short_names":["woman-kiss-woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":514,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB-1F3FB":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FB","non_qualified":"1F469-1F3FB-200D-2764-200D-1F48B-200D-1F469-1F3FB","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png","sheet_x":21,"sheet_y":45,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FC","non_qualified":"1F469-1F3FB-200D-2764-200D-1F48B-200D-1F469-1F3FC","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png","sheet_x":21,"sheet_y":46,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FD","non_qualified":"1F469-1F3FB-200D-2764-200D-1F48B-200D-1F469-1F3FD","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png","sheet_x":21,"sheet_y":47,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FE","non_qualified":"1F469-1F3FB-200D-2764-200D-1F48B-200D-1F469-1F3FE","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png","sheet_x":21,"sheet_y":48,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F469-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FF","non_qualified":"1F469-1F3FB-200D-2764-200D-1F48B-200D-1F469-1F3FF","image":"1f469-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png","sheet_x":21,"sheet_y":49,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FB","non_qualified":"1F469-1F3FC-200D-2764-200D-1F48B-200D-1F469-1F3FB","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png","sheet_x":21,"sheet_y":50,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FC":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FC","non_qualified":"1F469-1F3FC-200D-2764-200D-1F48B-200D-1F469-1F3FC","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png","sheet_x":21,"sheet_y":51,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FD","non_qualified":"1F469-1F3FC-200D-2764-200D-1F48B-200D-1F469-1F3FD","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png","sheet_x":21,"sheet_y":52,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FE","non_qualified":"1F469-1F3FC-200D-2764-200D-1F48B-200D-1F469-1F3FE","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png","sheet_x":21,"sheet_y":53,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F469-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FF","non_qualified":"1F469-1F3FC-200D-2764-200D-1F48B-200D-1F469-1F3FF","image":"1f469-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png","sheet_x":21,"sheet_y":54,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FB","non_qualified":"1F469-1F3FD-200D-2764-200D-1F48B-200D-1F469-1F3FB","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png","sheet_x":21,"sheet_y":55,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FC","non_qualified":"1F469-1F3FD-200D-2764-200D-1F48B-200D-1F469-1F3FC","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png","sheet_x":21,"sheet_y":56,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FD":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FD","non_qualified":"1F469-1F3FD-200D-2764-200D-1F48B-200D-1F469-1F3FD","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png","sheet_x":21,"sheet_y":57,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FE","non_qualified":"1F469-1F3FD-200D-2764-200D-1F48B-200D-1F469-1F3FE","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png","sheet_x":21,"sheet_y":58,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F469-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FF","non_qualified":"1F469-1F3FD-200D-2764-200D-1F48B-200D-1F469-1F3FF","image":"1f469-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png","sheet_x":21,"sheet_y":59,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FB","non_qualified":"1F469-1F3FE-200D-2764-200D-1F48B-200D-1F469-1F3FB","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png","sheet_x":21,"sheet_y":60,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FC","non_qualified":"1F469-1F3FE-200D-2764-200D-1F48B-200D-1F469-1F3FC","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png","sheet_x":21,"sheet_y":61,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FD","non_qualified":"1F469-1F3FE-200D-2764-200D-1F48B-200D-1F469-1F3FD","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png","sheet_x":22,"sheet_y":0,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FE":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FE","non_qualified":"1F469-1F3FE-200D-2764-200D-1F48B-200D-1F469-1F3FE","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png","sheet_x":22,"sheet_y":1,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F469-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FF","non_qualified":"1F469-1F3FE-200D-2764-200D-1F48B-200D-1F469-1F3FF","image":"1f469-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png","sheet_x":22,"sheet_y":2,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FB","non_qualified":"1F469-1F3FF-200D-2764-200D-1F48B-200D-1F469-1F3FB","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fb.png","sheet_x":22,"sheet_y":3,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FC","non_qualified":"1F469-1F3FF-200D-2764-200D-1F48B-200D-1F469-1F3FC","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fc.png","sheet_x":22,"sheet_y":4,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FD","non_qualified":"1F469-1F3FF-200D-2764-200D-1F48B-200D-1F469-1F3FD","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fd.png","sheet_x":22,"sheet_y":5,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FE","non_qualified":"1F469-1F3FF-200D-2764-200D-1F48B-200D-1F469-1F3FE","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3fe.png","sheet_x":22,"sheet_y":6,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FF":{"unified":"1F469-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F469-1F3FF","non_qualified":"1F469-1F3FF-200D-2764-200D-1F48B-200D-1F469-1F3FF","image":"1f469-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f469-1f3ff.png","sheet_x":22,"sheet_y":7,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN","unified":"1F469","non_qualified":null,"docomo":"E6F0","au":"E4FA","softbank":"E005","google":"FE19E","image":"1f469.png","sheet_x":22,"sheet_y":8,"short_name":"woman","short_names":["woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":244,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F469-1F3FB","non_qualified":null,"image":"1f469-1f3fb.png","sheet_x":22,"sheet_y":9,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F469-1F3FC","non_qualified":null,"image":"1f469-1f3fc.png","sheet_x":22,"sheet_y":10,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F469-1F3FD","non_qualified":null,"image":"1f469-1f3fd.png","sheet_x":22,"sheet_y":11,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F469-1F3FE","non_qualified":null,"image":"1f469-1f3fe.png","sheet_x":22,"sheet_y":12,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F469-1F3FF","non_qualified":null,"image":"1f469-1f3ff.png","sheet_x":22,"sheet_y":13,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FAMILY","unified":"1F46A","non_qualified":null,"docomo":null,"au":"E501","softbank":null,"google":"FE19F","image":"1f46a.png","sheet_x":22,"sheet_y":14,"short_name":"family","short_names":["family"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-symbol","sort_order":548,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F468-200D-1F469-200D-1F466"},{"name":"MAN AND WOMAN HOLDING HANDS","unified":"1F46B","non_qualified":null,"docomo":null,"au":null,"softbank":"E428","google":"FE1A0","image":"1f46b.png","sheet_x":22,"sheet_y":15,"short_name":"man_and_woman_holding_hands","short_names":["man_and_woman_holding_hands","woman_and_man_holding_hands","couple"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":509,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F46B-1F3FB","non_qualified":null,"image":"1f46b-1f3fb.png","sheet_x":22,"sheet_y":16,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F46B-1F3FC","non_qualified":null,"image":"1f46b-1f3fc.png","sheet_x":22,"sheet_y":17,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F46B-1F3FD","non_qualified":null,"image":"1f46b-1f3fd.png","sheet_x":22,"sheet_y":18,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F46B-1F3FE","non_qualified":null,"image":"1f46b-1f3fe.png","sheet_x":22,"sheet_y":19,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F46B-1F3FF","non_qualified":null,"image":"1f46b-1f3ff.png","sheet_x":22,"sheet_y":20,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F469-1F3FB-200D-1F91D-200D-1F468-1F3FC","non_qualified":null,"image":"1f469-1f3fb-200d-1f91d-200d-1f468-1f3fc.png","sheet_x":22,"sheet_y":21,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F469-1F3FB-200D-1F91D-200D-1F468-1F3FD","non_qualified":null,"image":"1f469-1f3fb-200d-1f91d-200d-1f468-1f3fd.png","sheet_x":22,"sheet_y":22,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F469-1F3FB-200D-1F91D-200D-1F468-1F3FE","non_qualified":null,"image":"1f469-1f3fb-200d-1f91d-200d-1f468-1f3fe.png","sheet_x":22,"sheet_y":23,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F469-1F3FB-200D-1F91D-200D-1F468-1F3FF","non_qualified":null,"image":"1f469-1f3fb-200d-1f91d-200d-1f468-1f3ff.png","sheet_x":22,"sheet_y":24,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F469-1F3FC-200D-1F91D-200D-1F468-1F3FB","non_qualified":null,"image":"1f469-1f3fc-200d-1f91d-200d-1f468-1f3fb.png","sheet_x":22,"sheet_y":25,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F469-1F3FC-200D-1F91D-200D-1F468-1F3FD","non_qualified":null,"image":"1f469-1f3fc-200d-1f91d-200d-1f468-1f3fd.png","sheet_x":22,"sheet_y":26,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F469-1F3FC-200D-1F91D-200D-1F468-1F3FE","non_qualified":null,"image":"1f469-1f3fc-200d-1f91d-200d-1f468-1f3fe.png","sheet_x":22,"sheet_y":27,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F469-1F3FC-200D-1F91D-200D-1F468-1F3FF","non_qualified":null,"image":"1f469-1f3fc-200d-1f91d-200d-1f468-1f3ff.png","sheet_x":22,"sheet_y":28,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F469-1F3FD-200D-1F91D-200D-1F468-1F3FB","non_qualified":null,"image":"1f469-1f3fd-200d-1f91d-200d-1f468-1f3fb.png","sheet_x":22,"sheet_y":29,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F469-1F3FD-200D-1F91D-200D-1F468-1F3FC","non_qualified":null,"image":"1f469-1f3fd-200d-1f91d-200d-1f468-1f3fc.png","sheet_x":22,"sheet_y":30,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F469-1F3FD-200D-1F91D-200D-1F468-1F3FE","non_qualified":null,"image":"1f469-1f3fd-200d-1f91d-200d-1f468-1f3fe.png","sheet_x":22,"sheet_y":31,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F469-1F3FD-200D-1F91D-200D-1F468-1F3FF","non_qualified":null,"image":"1f469-1f3fd-200d-1f91d-200d-1f468-1f3ff.png","sheet_x":22,"sheet_y":32,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F469-1F3FE-200D-1F91D-200D-1F468-1F3FB","non_qualified":null,"image":"1f469-1f3fe-200d-1f91d-200d-1f468-1f3fb.png","sheet_x":22,"sheet_y":33,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F469-1F3FE-200D-1F91D-200D-1F468-1F3FC","non_qualified":null,"image":"1f469-1f3fe-200d-1f91d-200d-1f468-1f3fc.png","sheet_x":22,"sheet_y":34,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F469-1F3FE-200D-1F91D-200D-1F468-1F3FD","non_qualified":null,"image":"1f469-1f3fe-200d-1f91d-200d-1f468-1f3fd.png","sheet_x":22,"sheet_y":35,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F469-1F3FE-200D-1F91D-200D-1F468-1F3FF","non_qualified":null,"image":"1f469-1f3fe-200d-1f91d-200d-1f468-1f3ff.png","sheet_x":22,"sheet_y":36,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F469-1F3FF-200D-1F91D-200D-1F468-1F3FB","non_qualified":null,"image":"1f469-1f3ff-200d-1f91d-200d-1f468-1f3fb.png","sheet_x":22,"sheet_y":37,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F469-1F3FF-200D-1F91D-200D-1F468-1F3FC","non_qualified":null,"image":"1f469-1f3ff-200d-1f91d-200d-1f468-1f3fc.png","sheet_x":22,"sheet_y":38,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F469-1F3FF-200D-1F91D-200D-1F468-1F3FD","non_qualified":null,"image":"1f469-1f3ff-200d-1f91d-200d-1f468-1f3fd.png","sheet_x":22,"sheet_y":39,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F469-1F3FF-200D-1F91D-200D-1F468-1F3FE","non_qualified":null,"image":"1f469-1f3ff-200d-1f91d-200d-1f468-1f3fe.png","sheet_x":22,"sheet_y":40,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"TWO MEN HOLDING HANDS","unified":"1F46C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f46c.png","sheet_x":22,"sheet_y":41,"short_name":"two_men_holding_hands","short_names":["two_men_holding_hands","men_holding_hands"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":510,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F46C-1F3FB","non_qualified":null,"image":"1f46c-1f3fb.png","sheet_x":22,"sheet_y":42,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F46C-1F3FC","non_qualified":null,"image":"1f46c-1f3fc.png","sheet_x":22,"sheet_y":43,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F46C-1F3FD","non_qualified":null,"image":"1f46c-1f3fd.png","sheet_x":22,"sheet_y":44,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F46C-1F3FE","non_qualified":null,"image":"1f46c-1f3fe.png","sheet_x":22,"sheet_y":45,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F46C-1F3FF","non_qualified":null,"image":"1f46c-1f3ff.png","sheet_x":22,"sheet_y":46,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F468-1F3FB-200D-1F91D-200D-1F468-1F3FC","non_qualified":null,"image":"1f468-1f3fb-200d-1f91d-200d-1f468-1f3fc.png","sheet_x":22,"sheet_y":47,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F468-1F3FB-200D-1F91D-200D-1F468-1F3FD","non_qualified":null,"image":"1f468-1f3fb-200d-1f91d-200d-1f468-1f3fd.png","sheet_x":22,"sheet_y":48,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F468-1F3FB-200D-1F91D-200D-1F468-1F3FE","non_qualified":null,"image":"1f468-1f3fb-200d-1f91d-200d-1f468-1f3fe.png","sheet_x":22,"sheet_y":49,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F468-1F3FB-200D-1F91D-200D-1F468-1F3FF","non_qualified":null,"image":"1f468-1f3fb-200d-1f91d-200d-1f468-1f3ff.png","sheet_x":22,"sheet_y":50,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F468-1F3FC-200D-1F91D-200D-1F468-1F3FB","non_qualified":null,"image":"1f468-1f3fc-200d-1f91d-200d-1f468-1f3fb.png","sheet_x":22,"sheet_y":51,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F468-1F3FC-200D-1F91D-200D-1F468-1F3FD","non_qualified":null,"image":"1f468-1f3fc-200d-1f91d-200d-1f468-1f3fd.png","sheet_x":22,"sheet_y":52,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F468-1F3FC-200D-1F91D-200D-1F468-1F3FE","non_qualified":null,"image":"1f468-1f3fc-200d-1f91d-200d-1f468-1f3fe.png","sheet_x":22,"sheet_y":53,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F468-1F3FC-200D-1F91D-200D-1F468-1F3FF","non_qualified":null,"image":"1f468-1f3fc-200d-1f91d-200d-1f468-1f3ff.png","sheet_x":22,"sheet_y":54,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F468-1F3FD-200D-1F91D-200D-1F468-1F3FB","non_qualified":null,"image":"1f468-1f3fd-200d-1f91d-200d-1f468-1f3fb.png","sheet_x":22,"sheet_y":55,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F468-1F3FD-200D-1F91D-200D-1F468-1F3FC","non_qualified":null,"image":"1f468-1f3fd-200d-1f91d-200d-1f468-1f3fc.png","sheet_x":22,"sheet_y":56,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F468-1F3FD-200D-1F91D-200D-1F468-1F3FE","non_qualified":null,"image":"1f468-1f3fd-200d-1f91d-200d-1f468-1f3fe.png","sheet_x":22,"sheet_y":57,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F468-1F3FD-200D-1F91D-200D-1F468-1F3FF","non_qualified":null,"image":"1f468-1f3fd-200d-1f91d-200d-1f468-1f3ff.png","sheet_x":22,"sheet_y":58,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F468-1F3FE-200D-1F91D-200D-1F468-1F3FB","non_qualified":null,"image":"1f468-1f3fe-200d-1f91d-200d-1f468-1f3fb.png","sheet_x":22,"sheet_y":59,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F468-1F3FE-200D-1F91D-200D-1F468-1F3FC","non_qualified":null,"image":"1f468-1f3fe-200d-1f91d-200d-1f468-1f3fc.png","sheet_x":22,"sheet_y":60,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F468-1F3FE-200D-1F91D-200D-1F468-1F3FD","non_qualified":null,"image":"1f468-1f3fe-200d-1f91d-200d-1f468-1f3fd.png","sheet_x":22,"sheet_y":61,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F468-1F3FE-200D-1F91D-200D-1F468-1F3FF","non_qualified":null,"image":"1f468-1f3fe-200d-1f91d-200d-1f468-1f3ff.png","sheet_x":23,"sheet_y":0,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F468-1F3FF-200D-1F91D-200D-1F468-1F3FB","non_qualified":null,"image":"1f468-1f3ff-200d-1f91d-200d-1f468-1f3fb.png","sheet_x":23,"sheet_y":1,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F468-1F3FF-200D-1F91D-200D-1F468-1F3FC","non_qualified":null,"image":"1f468-1f3ff-200d-1f91d-200d-1f468-1f3fc.png","sheet_x":23,"sheet_y":2,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F468-1F3FF-200D-1F91D-200D-1F468-1F3FD","non_qualified":null,"image":"1f468-1f3ff-200d-1f91d-200d-1f468-1f3fd.png","sheet_x":23,"sheet_y":3,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F468-1F3FF-200D-1F91D-200D-1F468-1F3FE","non_qualified":null,"image":"1f468-1f3ff-200d-1f91d-200d-1f468-1f3fe.png","sheet_x":23,"sheet_y":4,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"TWO WOMEN HOLDING HANDS","unified":"1F46D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f46d.png","sheet_x":23,"sheet_y":5,"short_name":"two_women_holding_hands","short_names":["two_women_holding_hands","women_holding_hands"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":508,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F46D-1F3FB","non_qualified":null,"image":"1f46d-1f3fb.png","sheet_x":23,"sheet_y":6,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F46D-1F3FC","non_qualified":null,"image":"1f46d-1f3fc.png","sheet_x":23,"sheet_y":7,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F46D-1F3FD","non_qualified":null,"image":"1f46d-1f3fd.png","sheet_x":23,"sheet_y":8,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F46D-1F3FE","non_qualified":null,"image":"1f46d-1f3fe.png","sheet_x":23,"sheet_y":9,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F46D-1F3FF","non_qualified":null,"image":"1f46d-1f3ff.png","sheet_x":23,"sheet_y":10,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F469-1F3FB-200D-1F91D-200D-1F469-1F3FC","non_qualified":null,"image":"1f469-1f3fb-200d-1f91d-200d-1f469-1f3fc.png","sheet_x":23,"sheet_y":11,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F469-1F3FB-200D-1F91D-200D-1F469-1F3FD","non_qualified":null,"image":"1f469-1f3fb-200d-1f91d-200d-1f469-1f3fd.png","sheet_x":23,"sheet_y":12,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F469-1F3FB-200D-1F91D-200D-1F469-1F3FE","non_qualified":null,"image":"1f469-1f3fb-200d-1f91d-200d-1f469-1f3fe.png","sheet_x":23,"sheet_y":13,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F469-1F3FB-200D-1F91D-200D-1F469-1F3FF","non_qualified":null,"image":"1f469-1f3fb-200d-1f91d-200d-1f469-1f3ff.png","sheet_x":23,"sheet_y":14,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F469-1F3FC-200D-1F91D-200D-1F469-1F3FB","non_qualified":null,"image":"1f469-1f3fc-200d-1f91d-200d-1f469-1f3fb.png","sheet_x":23,"sheet_y":15,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F469-1F3FC-200D-1F91D-200D-1F469-1F3FD","non_qualified":null,"image":"1f469-1f3fc-200d-1f91d-200d-1f469-1f3fd.png","sheet_x":23,"sheet_y":16,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F469-1F3FC-200D-1F91D-200D-1F469-1F3FE","non_qualified":null,"image":"1f469-1f3fc-200d-1f91d-200d-1f469-1f3fe.png","sheet_x":23,"sheet_y":17,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F469-1F3FC-200D-1F91D-200D-1F469-1F3FF","non_qualified":null,"image":"1f469-1f3fc-200d-1f91d-200d-1f469-1f3ff.png","sheet_x":23,"sheet_y":18,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F469-1F3FD-200D-1F91D-200D-1F469-1F3FB","non_qualified":null,"image":"1f469-1f3fd-200d-1f91d-200d-1f469-1f3fb.png","sheet_x":23,"sheet_y":19,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F469-1F3FD-200D-1F91D-200D-1F469-1F3FC","non_qualified":null,"image":"1f469-1f3fd-200d-1f91d-200d-1f469-1f3fc.png","sheet_x":23,"sheet_y":20,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F469-1F3FD-200D-1F91D-200D-1F469-1F3FE","non_qualified":null,"image":"1f469-1f3fd-200d-1f91d-200d-1f469-1f3fe.png","sheet_x":23,"sheet_y":21,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F469-1F3FD-200D-1F91D-200D-1F469-1F3FF","non_qualified":null,"image":"1f469-1f3fd-200d-1f91d-200d-1f469-1f3ff.png","sheet_x":23,"sheet_y":22,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F469-1F3FE-200D-1F91D-200D-1F469-1F3FB","non_qualified":null,"image":"1f469-1f3fe-200d-1f91d-200d-1f469-1f3fb.png","sheet_x":23,"sheet_y":23,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F469-1F3FE-200D-1F91D-200D-1F469-1F3FC","non_qualified":null,"image":"1f469-1f3fe-200d-1f91d-200d-1f469-1f3fc.png","sheet_x":23,"sheet_y":24,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F469-1F3FE-200D-1F91D-200D-1F469-1F3FD","non_qualified":null,"image":"1f469-1f3fe-200d-1f91d-200d-1f469-1f3fd.png","sheet_x":23,"sheet_y":25,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F469-1F3FE-200D-1F91D-200D-1F469-1F3FF","non_qualified":null,"image":"1f469-1f3fe-200d-1f91d-200d-1f469-1f3ff.png","sheet_x":23,"sheet_y":26,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F469-1F3FF-200D-1F91D-200D-1F469-1F3FB","non_qualified":null,"image":"1f469-1f3ff-200d-1f91d-200d-1f469-1f3fb.png","sheet_x":23,"sheet_y":27,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F469-1F3FF-200D-1F91D-200D-1F469-1F3FC","non_qualified":null,"image":"1f469-1f3ff-200d-1f91d-200d-1f469-1f3fc.png","sheet_x":23,"sheet_y":28,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F469-1F3FF-200D-1F91D-200D-1F469-1F3FD","non_qualified":null,"image":"1f469-1f3ff-200d-1f91d-200d-1f469-1f3fd.png","sheet_x":23,"sheet_y":29,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F469-1F3FF-200D-1F91D-200D-1F469-1F3FE","non_qualified":null,"image":"1f469-1f3ff-200d-1f91d-200d-1f469-1f3fe.png","sheet_x":23,"sheet_y":30,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN POLICE OFFICER","unified":"1F46E-200D-2640-FE0F","non_qualified":"1F46E-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f46e-200d-2640-fe0f.png","sheet_x":23,"sheet_y":31,"short_name":"female-police-officer","short_names":["female-police-officer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":338,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F46E-1F3FB-200D-2640-FE0F","non_qualified":"1F46E-1F3FB-200D-2640","image":"1f46e-1f3fb-200d-2640-fe0f.png","sheet_x":23,"sheet_y":32,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F46E-1F3FC-200D-2640-FE0F","non_qualified":"1F46E-1F3FC-200D-2640","image":"1f46e-1f3fc-200d-2640-fe0f.png","sheet_x":23,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F46E-1F3FD-200D-2640-FE0F","non_qualified":"1F46E-1F3FD-200D-2640","image":"1f46e-1f3fd-200d-2640-fe0f.png","sheet_x":23,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F46E-1F3FE-200D-2640-FE0F","non_qualified":"1F46E-1F3FE-200D-2640","image":"1f46e-1f3fe-200d-2640-fe0f.png","sheet_x":23,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F46E-1F3FF-200D-2640-FE0F","non_qualified":"1F46E-1F3FF-200D-2640","image":"1f46e-1f3ff-200d-2640-fe0f.png","sheet_x":23,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN POLICE OFFICER","unified":"1F46E-200D-2642-FE0F","non_qualified":"1F46E-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f46e-200d-2642-fe0f.png","sheet_x":23,"sheet_y":37,"short_name":"male-police-officer","short_names":["male-police-officer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":337,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F46E-1F3FB-200D-2642-FE0F","non_qualified":"1F46E-1F3FB-200D-2642","image":"1f46e-1f3fb-200d-2642-fe0f.png","sheet_x":23,"sheet_y":38,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F46E-1F3FC-200D-2642-FE0F","non_qualified":"1F46E-1F3FC-200D-2642","image":"1f46e-1f3fc-200d-2642-fe0f.png","sheet_x":23,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F46E-1F3FD-200D-2642-FE0F","non_qualified":"1F46E-1F3FD-200D-2642","image":"1f46e-1f3fd-200d-2642-fe0f.png","sheet_x":23,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F46E-1F3FE-200D-2642-FE0F","non_qualified":"1F46E-1F3FE-200D-2642","image":"1f46e-1f3fe-200d-2642-fe0f.png","sheet_x":23,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F46E-1F3FF-200D-2642-FE0F","non_qualified":"1F46E-1F3FF-200D-2642","image":"1f46e-1f3ff-200d-2642-fe0f.png","sheet_x":23,"sheet_y":42,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F46E"},{"name":"POLICE OFFICER","unified":"1F46E","non_qualified":null,"docomo":null,"au":"E5DD","softbank":"E152","google":"FE1A1","image":"1f46e.png","sheet_x":23,"sheet_y":43,"short_name":"cop","short_names":["cop"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":336,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F46E-1F3FB","non_qualified":null,"image":"1f46e-1f3fb.png","sheet_x":23,"sheet_y":44,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F46E-1F3FC","non_qualified":null,"image":"1f46e-1f3fc.png","sheet_x":23,"sheet_y":45,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F46E-1F3FD","non_qualified":null,"image":"1f46e-1f3fd.png","sheet_x":23,"sheet_y":46,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F46E-1F3FE","non_qualified":null,"image":"1f46e-1f3fe.png","sheet_x":23,"sheet_y":47,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F46E-1F3FF","non_qualified":null,"image":"1f46e-1f3ff.png","sheet_x":23,"sheet_y":48,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F46E-200D-2642-FE0F"},{"name":"WOMEN WITH BUNNY EARS","unified":"1F46F-200D-2640-FE0F","non_qualified":"1F46F-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f46f-200d-2640-fe0f.png","sheet_x":23,"sheet_y":49,"short_name":"women-with-bunny-ears-partying","short_names":["women-with-bunny-ears-partying","woman-with-bunny-ears-partying"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":452,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F46F"},{"name":"MEN WITH BUNNY EARS","unified":"1F46F-200D-2642-FE0F","non_qualified":"1F46F-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f46f-200d-2642-fe0f.png","sheet_x":23,"sheet_y":50,"short_name":"men-with-bunny-ears-partying","short_names":["men-with-bunny-ears-partying","man-with-bunny-ears-partying"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":451,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN WITH BUNNY EARS","unified":"1F46F","non_qualified":null,"docomo":null,"au":"EADB","softbank":"E429","google":"FE1A2","image":"1f46f.png","sheet_x":23,"sheet_y":51,"short_name":"dancers","short_names":["dancers"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":450,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F46F-200D-2640-FE0F"},{"name":"WOMAN WITH VEIL","unified":"1F470-200D-2640-FE0F","non_qualified":"1F470-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f470-200d-2640-fe0f.png","sheet_x":23,"sheet_y":52,"short_name":"woman_with_veil","short_names":["woman_with_veil"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":362,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F470-1F3FB-200D-2640-FE0F","non_qualified":"1F470-1F3FB-200D-2640","image":"1f470-1f3fb-200d-2640-fe0f.png","sheet_x":23,"sheet_y":53,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F470-1F3FC-200D-2640-FE0F","non_qualified":"1F470-1F3FC-200D-2640","image":"1f470-1f3fc-200d-2640-fe0f.png","sheet_x":23,"sheet_y":54,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F470-1F3FD-200D-2640-FE0F","non_qualified":"1F470-1F3FD-200D-2640","image":"1f470-1f3fd-200d-2640-fe0f.png","sheet_x":23,"sheet_y":55,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F470-1F3FE-200D-2640-FE0F","non_qualified":"1F470-1F3FE-200D-2640","image":"1f470-1f3fe-200d-2640-fe0f.png","sheet_x":23,"sheet_y":56,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F470-1F3FF-200D-2640-FE0F","non_qualified":"1F470-1F3FF-200D-2640","image":"1f470-1f3ff-200d-2640-fe0f.png","sheet_x":23,"sheet_y":57,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN WITH VEIL","unified":"1F470-200D-2642-FE0F","non_qualified":"1F470-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f470-200d-2642-fe0f.png","sheet_x":23,"sheet_y":58,"short_name":"man_with_veil","short_names":["man_with_veil"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":361,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F470-1F3FB-200D-2642-FE0F","non_qualified":"1F470-1F3FB-200D-2642","image":"1f470-1f3fb-200d-2642-fe0f.png","sheet_x":23,"sheet_y":59,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F470-1F3FC-200D-2642-FE0F","non_qualified":"1F470-1F3FC-200D-2642","image":"1f470-1f3fc-200d-2642-fe0f.png","sheet_x":23,"sheet_y":60,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F470-1F3FD-200D-2642-FE0F","non_qualified":"1F470-1F3FD-200D-2642","image":"1f470-1f3fd-200d-2642-fe0f.png","sheet_x":23,"sheet_y":61,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F470-1F3FE-200D-2642-FE0F","non_qualified":"1F470-1F3FE-200D-2642","image":"1f470-1f3fe-200d-2642-fe0f.png","sheet_x":24,"sheet_y":0,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F470-1F3FF-200D-2642-FE0F","non_qualified":"1F470-1F3FF-200D-2642","image":"1f470-1f3ff-200d-2642-fe0f.png","sheet_x":24,"sheet_y":1,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"BRIDE WITH VEIL","unified":"1F470","non_qualified":null,"docomo":null,"au":"EAE9","softbank":null,"google":"FE1A3","image":"1f470.png","sheet_x":24,"sheet_y":2,"short_name":"bride_with_veil","short_names":["bride_with_veil"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":360,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F470-1F3FB","non_qualified":null,"image":"1f470-1f3fb.png","sheet_x":24,"sheet_y":3,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F470-1F3FC","non_qualified":null,"image":"1f470-1f3fc.png","sheet_x":24,"sheet_y":4,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F470-1F3FD","non_qualified":null,"image":"1f470-1f3fd.png","sheet_x":24,"sheet_y":5,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F470-1F3FE","non_qualified":null,"image":"1f470-1f3fe.png","sheet_x":24,"sheet_y":6,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F470-1F3FF","non_qualified":null,"image":"1f470-1f3ff.png","sheet_x":24,"sheet_y":7,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN: BLOND HAIR","unified":"1F471-200D-2640-FE0F","non_qualified":"1F471-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f471-200d-2640-fe0f.png","sheet_x":24,"sheet_y":8,"short_name":"blond-haired-woman","short_names":["blond-haired-woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":253,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F471-1F3FB-200D-2640-FE0F","non_qualified":"1F471-1F3FB-200D-2640","image":"1f471-1f3fb-200d-2640-fe0f.png","sheet_x":24,"sheet_y":9,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F471-1F3FC-200D-2640-FE0F","non_qualified":"1F471-1F3FC-200D-2640","image":"1f471-1f3fc-200d-2640-fe0f.png","sheet_x":24,"sheet_y":10,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F471-1F3FD-200D-2640-FE0F","non_qualified":"1F471-1F3FD-200D-2640","image":"1f471-1f3fd-200d-2640-fe0f.png","sheet_x":24,"sheet_y":11,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F471-1F3FE-200D-2640-FE0F","non_qualified":"1F471-1F3FE-200D-2640","image":"1f471-1f3fe-200d-2640-fe0f.png","sheet_x":24,"sheet_y":12,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F471-1F3FF-200D-2640-FE0F","non_qualified":"1F471-1F3FF-200D-2640","image":"1f471-1f3ff-200d-2640-fe0f.png","sheet_x":24,"sheet_y":13,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN: BLOND HAIR","unified":"1F471-200D-2642-FE0F","non_qualified":"1F471-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f471-200d-2642-fe0f.png","sheet_x":24,"sheet_y":14,"short_name":"blond-haired-man","short_names":["blond-haired-man"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":254,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F471-1F3FB-200D-2642-FE0F","non_qualified":"1F471-1F3FB-200D-2642","image":"1f471-1f3fb-200d-2642-fe0f.png","sheet_x":24,"sheet_y":15,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F471-1F3FC-200D-2642-FE0F","non_qualified":"1F471-1F3FC-200D-2642","image":"1f471-1f3fc-200d-2642-fe0f.png","sheet_x":24,"sheet_y":16,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F471-1F3FD-200D-2642-FE0F","non_qualified":"1F471-1F3FD-200D-2642","image":"1f471-1f3fd-200d-2642-fe0f.png","sheet_x":24,"sheet_y":17,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F471-1F3FE-200D-2642-FE0F","non_qualified":"1F471-1F3FE-200D-2642","image":"1f471-1f3fe-200d-2642-fe0f.png","sheet_x":24,"sheet_y":18,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F471-1F3FF-200D-2642-FE0F","non_qualified":"1F471-1F3FF-200D-2642","image":"1f471-1f3ff-200d-2642-fe0f.png","sheet_x":24,"sheet_y":19,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F471"},{"name":"PERSON WITH BLOND HAIR","unified":"1F471","non_qualified":null,"docomo":null,"au":"EB13","softbank":"E515","google":"FE1A4","image":"1f471.png","sheet_x":24,"sheet_y":20,"short_name":"person_with_blond_hair","short_names":["person_with_blond_hair"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":235,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F471-1F3FB","non_qualified":null,"image":"1f471-1f3fb.png","sheet_x":24,"sheet_y":21,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F471-1F3FC","non_qualified":null,"image":"1f471-1f3fc.png","sheet_x":24,"sheet_y":22,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F471-1F3FD","non_qualified":null,"image":"1f471-1f3fd.png","sheet_x":24,"sheet_y":23,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F471-1F3FE","non_qualified":null,"image":"1f471-1f3fe.png","sheet_x":24,"sheet_y":24,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F471-1F3FF","non_qualified":null,"image":"1f471-1f3ff.png","sheet_x":24,"sheet_y":25,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F471-200D-2642-FE0F"},{"name":"MAN WITH GUA PI MAO","unified":"1F472","non_qualified":null,"docomo":null,"au":"EB14","softbank":"E516","google":"FE1A5","image":"1f472.png","sheet_x":24,"sheet_y":26,"short_name":"man_with_gua_pi_mao","short_names":["man_with_gua_pi_mao"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":355,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F472-1F3FB","non_qualified":null,"image":"1f472-1f3fb.png","sheet_x":24,"sheet_y":27,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F472-1F3FC","non_qualified":null,"image":"1f472-1f3fc.png","sheet_x":24,"sheet_y":28,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F472-1F3FD","non_qualified":null,"image":"1f472-1f3fd.png","sheet_x":24,"sheet_y":29,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F472-1F3FE","non_qualified":null,"image":"1f472-1f3fe.png","sheet_x":24,"sheet_y":30,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F472-1F3FF","non_qualified":null,"image":"1f472-1f3ff.png","sheet_x":24,"sheet_y":31,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN WEARING TURBAN","unified":"1F473-200D-2640-FE0F","non_qualified":"1F473-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f473-200d-2640-fe0f.png","sheet_x":24,"sheet_y":32,"short_name":"woman-wearing-turban","short_names":["woman-wearing-turban"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":354,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F473-1F3FB-200D-2640-FE0F","non_qualified":"1F473-1F3FB-200D-2640","image":"1f473-1f3fb-200d-2640-fe0f.png","sheet_x":24,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F473-1F3FC-200D-2640-FE0F","non_qualified":"1F473-1F3FC-200D-2640","image":"1f473-1f3fc-200d-2640-fe0f.png","sheet_x":24,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F473-1F3FD-200D-2640-FE0F","non_qualified":"1F473-1F3FD-200D-2640","image":"1f473-1f3fd-200d-2640-fe0f.png","sheet_x":24,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F473-1F3FE-200D-2640-FE0F","non_qualified":"1F473-1F3FE-200D-2640","image":"1f473-1f3fe-200d-2640-fe0f.png","sheet_x":24,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F473-1F3FF-200D-2640-FE0F","non_qualified":"1F473-1F3FF-200D-2640","image":"1f473-1f3ff-200d-2640-fe0f.png","sheet_x":24,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN WEARING TURBAN","unified":"1F473-200D-2642-FE0F","non_qualified":"1F473-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f473-200d-2642-fe0f.png","sheet_x":24,"sheet_y":38,"short_name":"man-wearing-turban","short_names":["man-wearing-turban"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":353,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F473-1F3FB-200D-2642-FE0F","non_qualified":"1F473-1F3FB-200D-2642","image":"1f473-1f3fb-200d-2642-fe0f.png","sheet_x":24,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F473-1F3FC-200D-2642-FE0F","non_qualified":"1F473-1F3FC-200D-2642","image":"1f473-1f3fc-200d-2642-fe0f.png","sheet_x":24,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F473-1F3FD-200D-2642-FE0F","non_qualified":"1F473-1F3FD-200D-2642","image":"1f473-1f3fd-200d-2642-fe0f.png","sheet_x":24,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F473-1F3FE-200D-2642-FE0F","non_qualified":"1F473-1F3FE-200D-2642","image":"1f473-1f3fe-200d-2642-fe0f.png","sheet_x":24,"sheet_y":42,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F473-1F3FF-200D-2642-FE0F","non_qualified":"1F473-1F3FF-200D-2642","image":"1f473-1f3ff-200d-2642-fe0f.png","sheet_x":24,"sheet_y":43,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F473"},{"name":"MAN WITH TURBAN","unified":"1F473","non_qualified":null,"docomo":null,"au":"EB15","softbank":"E517","google":"FE1A6","image":"1f473.png","sheet_x":24,"sheet_y":44,"short_name":"man_with_turban","short_names":["man_with_turban"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":352,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F473-1F3FB","non_qualified":null,"image":"1f473-1f3fb.png","sheet_x":24,"sheet_y":45,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F473-1F3FC","non_qualified":null,"image":"1f473-1f3fc.png","sheet_x":24,"sheet_y":46,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F473-1F3FD","non_qualified":null,"image":"1f473-1f3fd.png","sheet_x":24,"sheet_y":47,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F473-1F3FE","non_qualified":null,"image":"1f473-1f3fe.png","sheet_x":24,"sheet_y":48,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F473-1F3FF","non_qualified":null,"image":"1f473-1f3ff.png","sheet_x":24,"sheet_y":49,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F473-200D-2642-FE0F"},{"name":"OLDER MAN","unified":"1F474","non_qualified":null,"docomo":null,"au":"EB16","softbank":"E518","google":"FE1A7","image":"1f474.png","sheet_x":24,"sheet_y":50,"short_name":"older_man","short_names":["older_man"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":256,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F474-1F3FB","non_qualified":null,"image":"1f474-1f3fb.png","sheet_x":24,"sheet_y":51,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F474-1F3FC","non_qualified":null,"image":"1f474-1f3fc.png","sheet_x":24,"sheet_y":52,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F474-1F3FD","non_qualified":null,"image":"1f474-1f3fd.png","sheet_x":24,"sheet_y":53,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F474-1F3FE","non_qualified":null,"image":"1f474-1f3fe.png","sheet_x":24,"sheet_y":54,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F474-1F3FF","non_qualified":null,"image":"1f474-1f3ff.png","sheet_x":24,"sheet_y":55,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"OLDER WOMAN","unified":"1F475","non_qualified":null,"docomo":null,"au":"EB17","softbank":"E519","google":"FE1A8","image":"1f475.png","sheet_x":24,"sheet_y":56,"short_name":"older_woman","short_names":["older_woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":257,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F475-1F3FB","non_qualified":null,"image":"1f475-1f3fb.png","sheet_x":24,"sheet_y":57,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F475-1F3FC","non_qualified":null,"image":"1f475-1f3fc.png","sheet_x":24,"sheet_y":58,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F475-1F3FD","non_qualified":null,"image":"1f475-1f3fd.png","sheet_x":24,"sheet_y":59,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F475-1F3FE","non_qualified":null,"image":"1f475-1f3fe.png","sheet_x":24,"sheet_y":60,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F475-1F3FF","non_qualified":null,"image":"1f475-1f3ff.png","sheet_x":24,"sheet_y":61,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"BABY","unified":"1F476","non_qualified":null,"docomo":null,"au":"EB18","softbank":"E51A","google":"FE1A9","image":"1f476.png","sheet_x":25,"sheet_y":0,"short_name":"baby","short_names":["baby"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":230,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F476-1F3FB","non_qualified":null,"image":"1f476-1f3fb.png","sheet_x":25,"sheet_y":1,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F476-1F3FC","non_qualified":null,"image":"1f476-1f3fc.png","sheet_x":25,"sheet_y":2,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F476-1F3FD","non_qualified":null,"image":"1f476-1f3fd.png","sheet_x":25,"sheet_y":3,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F476-1F3FE","non_qualified":null,"image":"1f476-1f3fe.png","sheet_x":25,"sheet_y":4,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F476-1F3FF","non_qualified":null,"image":"1f476-1f3ff.png","sheet_x":25,"sheet_y":5,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN CONSTRUCTION WORKER","unified":"1F477-200D-2640-FE0F","non_qualified":"1F477-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f477-200d-2640-fe0f.png","sheet_x":25,"sheet_y":6,"short_name":"female-construction-worker","short_names":["female-construction-worker"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":348,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F477-1F3FB-200D-2640-FE0F","non_qualified":"1F477-1F3FB-200D-2640","image":"1f477-1f3fb-200d-2640-fe0f.png","sheet_x":25,"sheet_y":7,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F477-1F3FC-200D-2640-FE0F","non_qualified":"1F477-1F3FC-200D-2640","image":"1f477-1f3fc-200d-2640-fe0f.png","sheet_x":25,"sheet_y":8,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F477-1F3FD-200D-2640-FE0F","non_qualified":"1F477-1F3FD-200D-2640","image":"1f477-1f3fd-200d-2640-fe0f.png","sheet_x":25,"sheet_y":9,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F477-1F3FE-200D-2640-FE0F","non_qualified":"1F477-1F3FE-200D-2640","image":"1f477-1f3fe-200d-2640-fe0f.png","sheet_x":25,"sheet_y":10,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F477-1F3FF-200D-2640-FE0F","non_qualified":"1F477-1F3FF-200D-2640","image":"1f477-1f3ff-200d-2640-fe0f.png","sheet_x":25,"sheet_y":11,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN CONSTRUCTION WORKER","unified":"1F477-200D-2642-FE0F","non_qualified":"1F477-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f477-200d-2642-fe0f.png","sheet_x":25,"sheet_y":12,"short_name":"male-construction-worker","short_names":["male-construction-worker"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":347,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F477-1F3FB-200D-2642-FE0F","non_qualified":"1F477-1F3FB-200D-2642","image":"1f477-1f3fb-200d-2642-fe0f.png","sheet_x":25,"sheet_y":13,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F477-1F3FC-200D-2642-FE0F","non_qualified":"1F477-1F3FC-200D-2642","image":"1f477-1f3fc-200d-2642-fe0f.png","sheet_x":25,"sheet_y":14,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F477-1F3FD-200D-2642-FE0F","non_qualified":"1F477-1F3FD-200D-2642","image":"1f477-1f3fd-200d-2642-fe0f.png","sheet_x":25,"sheet_y":15,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F477-1F3FE-200D-2642-FE0F","non_qualified":"1F477-1F3FE-200D-2642","image":"1f477-1f3fe-200d-2642-fe0f.png","sheet_x":25,"sheet_y":16,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F477-1F3FF-200D-2642-FE0F","non_qualified":"1F477-1F3FF-200D-2642","image":"1f477-1f3ff-200d-2642-fe0f.png","sheet_x":25,"sheet_y":17,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F477"},{"name":"CONSTRUCTION WORKER","unified":"1F477","non_qualified":null,"docomo":null,"au":"EB19","softbank":"E51B","google":"FE1AA","image":"1f477.png","sheet_x":25,"sheet_y":18,"short_name":"construction_worker","short_names":["construction_worker"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":346,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F477-1F3FB","non_qualified":null,"image":"1f477-1f3fb.png","sheet_x":25,"sheet_y":19,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F477-1F3FC","non_qualified":null,"image":"1f477-1f3fc.png","sheet_x":25,"sheet_y":20,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F477-1F3FD","non_qualified":null,"image":"1f477-1f3fd.png","sheet_x":25,"sheet_y":21,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F477-1F3FE","non_qualified":null,"image":"1f477-1f3fe.png","sheet_x":25,"sheet_y":22,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F477-1F3FF","non_qualified":null,"image":"1f477-1f3ff.png","sheet_x":25,"sheet_y":23,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F477-200D-2642-FE0F"},{"name":"PRINCESS","unified":"1F478","non_qualified":null,"docomo":null,"au":"EB1A","softbank":"E51C","google":"FE1AB","image":"1f478.png","sheet_x":25,"sheet_y":24,"short_name":"princess","short_names":["princess"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":351,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F478-1F3FB","non_qualified":null,"image":"1f478-1f3fb.png","sheet_x":25,"sheet_y":25,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F478-1F3FC","non_qualified":null,"image":"1f478-1f3fc.png","sheet_x":25,"sheet_y":26,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F478-1F3FD","non_qualified":null,"image":"1f478-1f3fd.png","sheet_x":25,"sheet_y":27,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F478-1F3FE","non_qualified":null,"image":"1f478-1f3fe.png","sheet_x":25,"sheet_y":28,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F478-1F3FF","non_qualified":null,"image":"1f478-1f3ff.png","sheet_x":25,"sheet_y":29,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"JAPANESE OGRE","unified":"1F479","non_qualified":null,"docomo":null,"au":"EB44","softbank":null,"google":"FE1AC","image":"1f479.png","sheet_x":25,"sheet_y":30,"short_name":"japanese_ogre","short_names":["japanese_ogre"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-costume","sort_order":112,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JAPANESE GOBLIN","unified":"1F47A","non_qualified":null,"docomo":null,"au":"EB45","softbank":null,"google":"FE1AD","image":"1f47a.png","sheet_x":25,"sheet_y":31,"short_name":"japanese_goblin","short_names":["japanese_goblin"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-costume","sort_order":113,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GHOST","unified":"1F47B","non_qualified":null,"docomo":null,"au":"E4CB","softbank":"E11B","google":"FE1AE","image":"1f47b.png","sheet_x":25,"sheet_y":32,"short_name":"ghost","short_names":["ghost"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-costume","sort_order":114,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BABY ANGEL","unified":"1F47C","non_qualified":null,"docomo":null,"au":"E5BF","softbank":"E04E","google":"FE1AF","image":"1f47c.png","sheet_x":25,"sheet_y":33,"short_name":"angel","short_names":["angel"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":370,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F47C-1F3FB","non_qualified":null,"image":"1f47c-1f3fb.png","sheet_x":25,"sheet_y":34,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F47C-1F3FC","non_qualified":null,"image":"1f47c-1f3fc.png","sheet_x":25,"sheet_y":35,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F47C-1F3FD","non_qualified":null,"image":"1f47c-1f3fd.png","sheet_x":25,"sheet_y":36,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F47C-1F3FE","non_qualified":null,"image":"1f47c-1f3fe.png","sheet_x":25,"sheet_y":37,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F47C-1F3FF","non_qualified":null,"image":"1f47c-1f3ff.png","sheet_x":25,"sheet_y":38,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"EXTRATERRESTRIAL ALIEN","unified":"1F47D","non_qualified":null,"docomo":null,"au":"E50E","softbank":"E10C","google":"FE1B0","image":"1f47d.png","sheet_x":25,"sheet_y":39,"short_name":"alien","short_names":["alien"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-costume","sort_order":115,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ALIEN MONSTER","unified":"1F47E","non_qualified":null,"docomo":null,"au":"E4EC","softbank":"E12B","google":"FE1B1","image":"1f47e.png","sheet_x":25,"sheet_y":40,"short_name":"space_invader","short_names":["space_invader"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-costume","sort_order":116,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"IMP","unified":"1F47F","non_qualified":null,"docomo":null,"au":"E4EF","softbank":"E11A","google":"FE1B2","image":"1f47f.png","sheet_x":25,"sheet_y":41,"short_name":"imp","short_names":["imp"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-negative","sort_order":107,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SKULL","unified":"1F480","non_qualified":null,"docomo":null,"au":"E4F8","softbank":"E11C","google":"FE1B3","image":"1f480.png","sheet_x":25,"sheet_y":42,"short_name":"skull","short_names":["skull"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-negative","sort_order":108,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN TIPPING HAND","unified":"1F481-200D-2640-FE0F","non_qualified":"1F481-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f481-200d-2640-fe0f.png","sheet_x":25,"sheet_y":43,"short_name":"woman-tipping-hand","short_names":["woman-tipping-hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":272,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F481-1F3FB-200D-2640-FE0F","non_qualified":"1F481-1F3FB-200D-2640","image":"1f481-1f3fb-200d-2640-fe0f.png","sheet_x":25,"sheet_y":44,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F481-1F3FC-200D-2640-FE0F","non_qualified":"1F481-1F3FC-200D-2640","image":"1f481-1f3fc-200d-2640-fe0f.png","sheet_x":25,"sheet_y":45,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F481-1F3FD-200D-2640-FE0F","non_qualified":"1F481-1F3FD-200D-2640","image":"1f481-1f3fd-200d-2640-fe0f.png","sheet_x":25,"sheet_y":46,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F481-1F3FE-200D-2640-FE0F","non_qualified":"1F481-1F3FE-200D-2640","image":"1f481-1f3fe-200d-2640-fe0f.png","sheet_x":25,"sheet_y":47,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F481-1F3FF-200D-2640-FE0F","non_qualified":"1F481-1F3FF-200D-2640","image":"1f481-1f3ff-200d-2640-fe0f.png","sheet_x":25,"sheet_y":48,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F481"},{"name":"MAN TIPPING HAND","unified":"1F481-200D-2642-FE0F","non_qualified":"1F481-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f481-200d-2642-fe0f.png","sheet_x":25,"sheet_y":49,"short_name":"man-tipping-hand","short_names":["man-tipping-hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":271,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F481-1F3FB-200D-2642-FE0F","non_qualified":"1F481-1F3FB-200D-2642","image":"1f481-1f3fb-200d-2642-fe0f.png","sheet_x":25,"sheet_y":50,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F481-1F3FC-200D-2642-FE0F","non_qualified":"1F481-1F3FC-200D-2642","image":"1f481-1f3fc-200d-2642-fe0f.png","sheet_x":25,"sheet_y":51,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F481-1F3FD-200D-2642-FE0F","non_qualified":"1F481-1F3FD-200D-2642","image":"1f481-1f3fd-200d-2642-fe0f.png","sheet_x":25,"sheet_y":52,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F481-1F3FE-200D-2642-FE0F","non_qualified":"1F481-1F3FE-200D-2642","image":"1f481-1f3fe-200d-2642-fe0f.png","sheet_x":25,"sheet_y":53,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F481-1F3FF-200D-2642-FE0F","non_qualified":"1F481-1F3FF-200D-2642","image":"1f481-1f3ff-200d-2642-fe0f.png","sheet_x":25,"sheet_y":54,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"INFORMATION DESK PERSON","unified":"1F481","non_qualified":null,"docomo":null,"au":null,"softbank":"E253","google":"FE1B4","image":"1f481.png","sheet_x":25,"sheet_y":55,"short_name":"information_desk_person","short_names":["information_desk_person"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":270,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F481-1F3FB","non_qualified":null,"image":"1f481-1f3fb.png","sheet_x":25,"sheet_y":56,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F481-1F3FC","non_qualified":null,"image":"1f481-1f3fc.png","sheet_x":25,"sheet_y":57,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F481-1F3FD","non_qualified":null,"image":"1f481-1f3fd.png","sheet_x":25,"sheet_y":58,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F481-1F3FE","non_qualified":null,"image":"1f481-1f3fe.png","sheet_x":25,"sheet_y":59,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F481-1F3FF","non_qualified":null,"image":"1f481-1f3ff.png","sheet_x":25,"sheet_y":60,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F481-200D-2640-FE0F"},{"name":"WOMAN GUARD","unified":"1F482-200D-2640-FE0F","non_qualified":"1F482-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f482-200d-2640-fe0f.png","sheet_x":25,"sheet_y":61,"short_name":"female-guard","short_names":["female-guard"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":344,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F482-1F3FB-200D-2640-FE0F","non_qualified":"1F482-1F3FB-200D-2640","image":"1f482-1f3fb-200d-2640-fe0f.png","sheet_x":26,"sheet_y":0,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F482-1F3FC-200D-2640-FE0F","non_qualified":"1F482-1F3FC-200D-2640","image":"1f482-1f3fc-200d-2640-fe0f.png","sheet_x":26,"sheet_y":1,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F482-1F3FD-200D-2640-FE0F","non_qualified":"1F482-1F3FD-200D-2640","image":"1f482-1f3fd-200d-2640-fe0f.png","sheet_x":26,"sheet_y":2,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F482-1F3FE-200D-2640-FE0F","non_qualified":"1F482-1F3FE-200D-2640","image":"1f482-1f3fe-200d-2640-fe0f.png","sheet_x":26,"sheet_y":3,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F482-1F3FF-200D-2640-FE0F","non_qualified":"1F482-1F3FF-200D-2640","image":"1f482-1f3ff-200d-2640-fe0f.png","sheet_x":26,"sheet_y":4,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN GUARD","unified":"1F482-200D-2642-FE0F","non_qualified":"1F482-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f482-200d-2642-fe0f.png","sheet_x":26,"sheet_y":5,"short_name":"male-guard","short_names":["male-guard"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":343,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F482-1F3FB-200D-2642-FE0F","non_qualified":"1F482-1F3FB-200D-2642","image":"1f482-1f3fb-200d-2642-fe0f.png","sheet_x":26,"sheet_y":6,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F482-1F3FC-200D-2642-FE0F","non_qualified":"1F482-1F3FC-200D-2642","image":"1f482-1f3fc-200d-2642-fe0f.png","sheet_x":26,"sheet_y":7,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F482-1F3FD-200D-2642-FE0F","non_qualified":"1F482-1F3FD-200D-2642","image":"1f482-1f3fd-200d-2642-fe0f.png","sheet_x":26,"sheet_y":8,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F482-1F3FE-200D-2642-FE0F","non_qualified":"1F482-1F3FE-200D-2642","image":"1f482-1f3fe-200d-2642-fe0f.png","sheet_x":26,"sheet_y":9,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F482-1F3FF-200D-2642-FE0F","non_qualified":"1F482-1F3FF-200D-2642","image":"1f482-1f3ff-200d-2642-fe0f.png","sheet_x":26,"sheet_y":10,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F482"},{"name":"GUARDSMAN","unified":"1F482","non_qualified":null,"docomo":null,"au":null,"softbank":"E51E","google":"FE1B5","image":"1f482.png","sheet_x":26,"sheet_y":11,"short_name":"guardsman","short_names":["guardsman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":342,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F482-1F3FB","non_qualified":null,"image":"1f482-1f3fb.png","sheet_x":26,"sheet_y":12,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F482-1F3FC","non_qualified":null,"image":"1f482-1f3fc.png","sheet_x":26,"sheet_y":13,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F482-1F3FD","non_qualified":null,"image":"1f482-1f3fd.png","sheet_x":26,"sheet_y":14,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F482-1F3FE","non_qualified":null,"image":"1f482-1f3fe.png","sheet_x":26,"sheet_y":15,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F482-1F3FF","non_qualified":null,"image":"1f482-1f3ff.png","sheet_x":26,"sheet_y":16,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F482-200D-2642-FE0F"},{"name":"DANCER","unified":"1F483","non_qualified":null,"docomo":null,"au":"EB1C","softbank":"E51F","google":"FE1B6","image":"1f483.png","sheet_x":26,"sheet_y":17,"short_name":"dancer","short_names":["dancer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":447,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F483-1F3FB","non_qualified":null,"image":"1f483-1f3fb.png","sheet_x":26,"sheet_y":18,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F483-1F3FC","non_qualified":null,"image":"1f483-1f3fc.png","sheet_x":26,"sheet_y":19,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F483-1F3FD","non_qualified":null,"image":"1f483-1f3fd.png","sheet_x":26,"sheet_y":20,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F483-1F3FE","non_qualified":null,"image":"1f483-1f3fe.png","sheet_x":26,"sheet_y":21,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F483-1F3FF","non_qualified":null,"image":"1f483-1f3ff.png","sheet_x":26,"sheet_y":22,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"LIPSTICK","unified":"1F484","non_qualified":null,"docomo":"E710","au":"E509","softbank":"E31C","google":"FE195","image":"1f484.png","sheet_x":26,"sheet_y":23,"short_name":"lipstick","short_names":["lipstick"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1194,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NAIL POLISH","unified":"1F485","non_qualified":null,"docomo":null,"au":"EAA0","softbank":"E31D","google":"FE196","image":"1f485.png","sheet_x":26,"sheet_y":24,"short_name":"nail_care","short_names":["nail_care"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-prop","sort_order":210,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F485-1F3FB","non_qualified":null,"image":"1f485-1f3fb.png","sheet_x":26,"sheet_y":25,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F485-1F3FC","non_qualified":null,"image":"1f485-1f3fc.png","sheet_x":26,"sheet_y":26,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F485-1F3FD","non_qualified":null,"image":"1f485-1f3fd.png","sheet_x":26,"sheet_y":27,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F485-1F3FE","non_qualified":null,"image":"1f485-1f3fe.png","sheet_x":26,"sheet_y":28,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F485-1F3FF","non_qualified":null,"image":"1f485-1f3ff.png","sheet_x":26,"sheet_y":29,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN GETTING MASSAGE","unified":"1F486-200D-2640-FE0F","non_qualified":"1F486-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f486-200d-2640-fe0f.png","sheet_x":26,"sheet_y":30,"short_name":"woman-getting-massage","short_names":["woman-getting-massage"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":404,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F486-1F3FB-200D-2640-FE0F","non_qualified":"1F486-1F3FB-200D-2640","image":"1f486-1f3fb-200d-2640-fe0f.png","sheet_x":26,"sheet_y":31,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F486-1F3FC-200D-2640-FE0F","non_qualified":"1F486-1F3FC-200D-2640","image":"1f486-1f3fc-200d-2640-fe0f.png","sheet_x":26,"sheet_y":32,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F486-1F3FD-200D-2640-FE0F","non_qualified":"1F486-1F3FD-200D-2640","image":"1f486-1f3fd-200d-2640-fe0f.png","sheet_x":26,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F486-1F3FE-200D-2640-FE0F","non_qualified":"1F486-1F3FE-200D-2640","image":"1f486-1f3fe-200d-2640-fe0f.png","sheet_x":26,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F486-1F3FF-200D-2640-FE0F","non_qualified":"1F486-1F3FF-200D-2640","image":"1f486-1f3ff-200d-2640-fe0f.png","sheet_x":26,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F486"},{"name":"MAN GETTING MASSAGE","unified":"1F486-200D-2642-FE0F","non_qualified":"1F486-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f486-200d-2642-fe0f.png","sheet_x":26,"sheet_y":36,"short_name":"man-getting-massage","short_names":["man-getting-massage"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":403,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F486-1F3FB-200D-2642-FE0F","non_qualified":"1F486-1F3FB-200D-2642","image":"1f486-1f3fb-200d-2642-fe0f.png","sheet_x":26,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F486-1F3FC-200D-2642-FE0F","non_qualified":"1F486-1F3FC-200D-2642","image":"1f486-1f3fc-200d-2642-fe0f.png","sheet_x":26,"sheet_y":38,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F486-1F3FD-200D-2642-FE0F","non_qualified":"1F486-1F3FD-200D-2642","image":"1f486-1f3fd-200d-2642-fe0f.png","sheet_x":26,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F486-1F3FE-200D-2642-FE0F","non_qualified":"1F486-1F3FE-200D-2642","image":"1f486-1f3fe-200d-2642-fe0f.png","sheet_x":26,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F486-1F3FF-200D-2642-FE0F","non_qualified":"1F486-1F3FF-200D-2642","image":"1f486-1f3ff-200d-2642-fe0f.png","sheet_x":26,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FACE MASSAGE","unified":"1F486","non_qualified":null,"docomo":null,"au":"E50B","softbank":"E31E","google":"FE197","image":"1f486.png","sheet_x":26,"sheet_y":42,"short_name":"massage","short_names":["massage"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":402,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F486-1F3FB","non_qualified":null,"image":"1f486-1f3fb.png","sheet_x":26,"sheet_y":43,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F486-1F3FC","non_qualified":null,"image":"1f486-1f3fc.png","sheet_x":26,"sheet_y":44,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F486-1F3FD","non_qualified":null,"image":"1f486-1f3fd.png","sheet_x":26,"sheet_y":45,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F486-1F3FE","non_qualified":null,"image":"1f486-1f3fe.png","sheet_x":26,"sheet_y":46,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F486-1F3FF","non_qualified":null,"image":"1f486-1f3ff.png","sheet_x":26,"sheet_y":47,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F486-200D-2640-FE0F"},{"name":"WOMAN GETTING HAIRCUT","unified":"1F487-200D-2640-FE0F","non_qualified":"1F487-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f487-200d-2640-fe0f.png","sheet_x":26,"sheet_y":48,"short_name":"woman-getting-haircut","short_names":["woman-getting-haircut"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":407,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F487-1F3FB-200D-2640-FE0F","non_qualified":"1F487-1F3FB-200D-2640","image":"1f487-1f3fb-200d-2640-fe0f.png","sheet_x":26,"sheet_y":49,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F487-1F3FC-200D-2640-FE0F","non_qualified":"1F487-1F3FC-200D-2640","image":"1f487-1f3fc-200d-2640-fe0f.png","sheet_x":26,"sheet_y":50,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F487-1F3FD-200D-2640-FE0F","non_qualified":"1F487-1F3FD-200D-2640","image":"1f487-1f3fd-200d-2640-fe0f.png","sheet_x":26,"sheet_y":51,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F487-1F3FE-200D-2640-FE0F","non_qualified":"1F487-1F3FE-200D-2640","image":"1f487-1f3fe-200d-2640-fe0f.png","sheet_x":26,"sheet_y":52,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F487-1F3FF-200D-2640-FE0F","non_qualified":"1F487-1F3FF-200D-2640","image":"1f487-1f3ff-200d-2640-fe0f.png","sheet_x":26,"sheet_y":53,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F487"},{"name":"MAN GETTING HAIRCUT","unified":"1F487-200D-2642-FE0F","non_qualified":"1F487-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f487-200d-2642-fe0f.png","sheet_x":26,"sheet_y":54,"short_name":"man-getting-haircut","short_names":["man-getting-haircut"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":406,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F487-1F3FB-200D-2642-FE0F","non_qualified":"1F487-1F3FB-200D-2642","image":"1f487-1f3fb-200d-2642-fe0f.png","sheet_x":26,"sheet_y":55,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F487-1F3FC-200D-2642-FE0F","non_qualified":"1F487-1F3FC-200D-2642","image":"1f487-1f3fc-200d-2642-fe0f.png","sheet_x":26,"sheet_y":56,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F487-1F3FD-200D-2642-FE0F","non_qualified":"1F487-1F3FD-200D-2642","image":"1f487-1f3fd-200d-2642-fe0f.png","sheet_x":26,"sheet_y":57,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F487-1F3FE-200D-2642-FE0F","non_qualified":"1F487-1F3FE-200D-2642","image":"1f487-1f3fe-200d-2642-fe0f.png","sheet_x":26,"sheet_y":58,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F487-1F3FF-200D-2642-FE0F","non_qualified":"1F487-1F3FF-200D-2642","image":"1f487-1f3ff-200d-2642-fe0f.png","sheet_x":26,"sheet_y":59,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"HAIRCUT","unified":"1F487","non_qualified":null,"docomo":"E675","au":"EAA1","softbank":"E31F","google":"FE198","image":"1f487.png","sheet_x":26,"sheet_y":60,"short_name":"haircut","short_names":["haircut"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":405,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F487-1F3FB","non_qualified":null,"image":"1f487-1f3fb.png","sheet_x":26,"sheet_y":61,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F487-1F3FC","non_qualified":null,"image":"1f487-1f3fc.png","sheet_x":27,"sheet_y":0,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F487-1F3FD","non_qualified":null,"image":"1f487-1f3fd.png","sheet_x":27,"sheet_y":1,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F487-1F3FE","non_qualified":null,"image":"1f487-1f3fe.png","sheet_x":27,"sheet_y":2,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F487-1F3FF","non_qualified":null,"image":"1f487-1f3ff.png","sheet_x":27,"sheet_y":3,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F487-200D-2640-FE0F"},{"name":"BARBER POLE","unified":"1F488","non_qualified":null,"docomo":null,"au":"EAA2","softbank":"E320","google":"FE199","image":"1f488.png","sheet_x":27,"sheet_y":4,"short_name":"barber","short_names":["barber"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":911,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SYRINGE","unified":"1F489","non_qualified":null,"docomo":null,"au":"E510","softbank":"E13B","google":"FE509","image":"1f489.png","sheet_x":27,"sheet_y":5,"short_name":"syringe","short_names":["syringe"],"text":null,"texts":null,"category":"Objects","subcategory":"medical","sort_order":1371,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PILL","unified":"1F48A","non_qualified":null,"docomo":null,"au":"EA9A","softbank":"E30F","google":"FE50A","image":"1f48a.png","sheet_x":27,"sheet_y":6,"short_name":"pill","short_names":["pill"],"text":null,"texts":null,"category":"Objects","subcategory":"medical","sort_order":1373,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KISS MARK","unified":"1F48B","non_qualified":null,"docomo":"E6F9","au":"E4EB","softbank":"E003","google":"FE823","image":"1f48b.png","sheet_x":27,"sheet_y":7,"short_name":"kiss","short_names":["kiss"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":155,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOVE LETTER","unified":"1F48C","non_qualified":null,"docomo":"E717","au":"EB78","softbank":null,"google":"FE824","image":"1f48c.png","sheet_x":27,"sheet_y":8,"short_name":"love_letter","short_names":["love_letter"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":130,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RING","unified":"1F48D","non_qualified":null,"docomo":"E71B","au":"E514","softbank":"E034","google":"FE825","image":"1f48d.png","sheet_x":27,"sheet_y":9,"short_name":"ring","short_names":["ring"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1195,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GEM STONE","unified":"1F48E","non_qualified":null,"docomo":"E71B","au":"E514","softbank":"E035","google":"FE826","image":"1f48e.png","sheet_x":27,"sheet_y":10,"short_name":"gem","short_names":["gem"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1196,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KISS","unified":"1F48F","non_qualified":null,"docomo":"E6F9","au":"E5CA","softbank":"E111","google":"FE827","image":"1f48f.png","sheet_x":27,"sheet_y":11,"short_name":"couplekiss","short_names":["couplekiss"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":511,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F48F-1F3FB","non_qualified":null,"image":"1f48f-1f3fb.png","sheet_x":27,"sheet_y":12,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F48F-1F3FC","non_qualified":null,"image":"1f48f-1f3fc.png","sheet_x":27,"sheet_y":13,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F48F-1F3FD","non_qualified":null,"image":"1f48f-1f3fd.png","sheet_x":27,"sheet_y":14,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F48F-1F3FE","non_qualified":null,"image":"1f48f-1f3fe.png","sheet_x":27,"sheet_y":15,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F48F-1F3FF","non_qualified":null,"image":"1f48f-1f3ff.png","sheet_x":27,"sheet_y":16,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F9D1-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FC","non_qualified":"1F9D1-1F3FB-200D-2764-200D-1F48B-200D-1F9D1-1F3FC","image":"1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png","sheet_x":27,"sheet_y":17,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F9D1-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FD","non_qualified":"1F9D1-1F3FB-200D-2764-200D-1F48B-200D-1F9D1-1F3FD","image":"1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png","sheet_x":27,"sheet_y":18,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F9D1-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FE","non_qualified":"1F9D1-1F3FB-200D-2764-200D-1F48B-200D-1F9D1-1F3FE","image":"1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png","sheet_x":27,"sheet_y":19,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F9D1-1F3FB-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FF","non_qualified":"1F9D1-1F3FB-200D-2764-200D-1F48B-200D-1F9D1-1F3FF","image":"1f9d1-1f3fb-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png","sheet_x":27,"sheet_y":20,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F9D1-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FB","non_qualified":"1F9D1-1F3FC-200D-2764-200D-1F48B-200D-1F9D1-1F3FB","image":"1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png","sheet_x":27,"sheet_y":21,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F9D1-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FD","non_qualified":"1F9D1-1F3FC-200D-2764-200D-1F48B-200D-1F9D1-1F3FD","image":"1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png","sheet_x":27,"sheet_y":22,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F9D1-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FE","non_qualified":"1F9D1-1F3FC-200D-2764-200D-1F48B-200D-1F9D1-1F3FE","image":"1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png","sheet_x":27,"sheet_y":23,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F9D1-1F3FC-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FF","non_qualified":"1F9D1-1F3FC-200D-2764-200D-1F48B-200D-1F9D1-1F3FF","image":"1f9d1-1f3fc-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png","sheet_x":27,"sheet_y":24,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F9D1-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FB","non_qualified":"1F9D1-1F3FD-200D-2764-200D-1F48B-200D-1F9D1-1F3FB","image":"1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png","sheet_x":27,"sheet_y":25,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F9D1-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FC","non_qualified":"1F9D1-1F3FD-200D-2764-200D-1F48B-200D-1F9D1-1F3FC","image":"1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png","sheet_x":27,"sheet_y":26,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F9D1-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FE","non_qualified":"1F9D1-1F3FD-200D-2764-200D-1F48B-200D-1F9D1-1F3FE","image":"1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png","sheet_x":27,"sheet_y":27,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F9D1-1F3FD-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FF","non_qualified":"1F9D1-1F3FD-200D-2764-200D-1F48B-200D-1F9D1-1F3FF","image":"1f9d1-1f3fd-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png","sheet_x":27,"sheet_y":28,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F9D1-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FB","non_qualified":"1F9D1-1F3FE-200D-2764-200D-1F48B-200D-1F9D1-1F3FB","image":"1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png","sheet_x":27,"sheet_y":29,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F9D1-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FC","non_qualified":"1F9D1-1F3FE-200D-2764-200D-1F48B-200D-1F9D1-1F3FC","image":"1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png","sheet_x":27,"sheet_y":30,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F9D1-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FD","non_qualified":"1F9D1-1F3FE-200D-2764-200D-1F48B-200D-1F9D1-1F3FD","image":"1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png","sheet_x":27,"sheet_y":31,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F9D1-1F3FE-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FF","non_qualified":"1F9D1-1F3FE-200D-2764-200D-1F48B-200D-1F9D1-1F3FF","image":"1f9d1-1f3fe-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3ff.png","sheet_x":27,"sheet_y":32,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F9D1-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FB","non_qualified":"1F9D1-1F3FF-200D-2764-200D-1F48B-200D-1F9D1-1F3FB","image":"1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fb.png","sheet_x":27,"sheet_y":33,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F9D1-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FC","non_qualified":"1F9D1-1F3FF-200D-2764-200D-1F48B-200D-1F9D1-1F3FC","image":"1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fc.png","sheet_x":27,"sheet_y":34,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F9D1-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FD","non_qualified":"1F9D1-1F3FF-200D-2764-200D-1F48B-200D-1F9D1-1F3FD","image":"1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fd.png","sheet_x":27,"sheet_y":35,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F9D1-1F3FF-200D-2764-FE0F-200D-1F48B-200D-1F9D1-1F3FE","non_qualified":"1F9D1-1F3FF-200D-2764-200D-1F48B-200D-1F9D1-1F3FE","image":"1f9d1-1f3ff-200d-2764-fe0f-200d-1f48b-200d-1f9d1-1f3fe.png","sheet_x":27,"sheet_y":36,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"BOUQUET","unified":"1F490","non_qualified":null,"docomo":null,"au":"EA95","softbank":"E306","google":"FE828","image":"1f490.png","sheet_x":27,"sheet_y":37,"short_name":"bouquet","short_names":["bouquet"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":684,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COUPLE WITH HEART","unified":"1F491","non_qualified":null,"docomo":"E6ED","au":"EADA","softbank":"E425","google":"FE829","image":"1f491.png","sheet_x":27,"sheet_y":38,"short_name":"couple_with_heart","short_names":["couple_with_heart"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":515,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F491-1F3FB","non_qualified":null,"image":"1f491-1f3fb.png","sheet_x":27,"sheet_y":39,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F491-1F3FC","non_qualified":null,"image":"1f491-1f3fc.png","sheet_x":27,"sheet_y":40,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F491-1F3FD","non_qualified":null,"image":"1f491-1f3fd.png","sheet_x":27,"sheet_y":41,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F491-1F3FE","non_qualified":null,"image":"1f491-1f3fe.png","sheet_x":27,"sheet_y":42,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F491-1F3FF","non_qualified":null,"image":"1f491-1f3ff.png","sheet_x":27,"sheet_y":43,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F9D1-1F3FB-200D-2764-FE0F-200D-1F9D1-1F3FC","non_qualified":"1F9D1-1F3FB-200D-2764-200D-1F9D1-1F3FC","image":"1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fc.png","sheet_x":27,"sheet_y":44,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F9D1-1F3FB-200D-2764-FE0F-200D-1F9D1-1F3FD","non_qualified":"1F9D1-1F3FB-200D-2764-200D-1F9D1-1F3FD","image":"1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fd.png","sheet_x":27,"sheet_y":45,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F9D1-1F3FB-200D-2764-FE0F-200D-1F9D1-1F3FE","non_qualified":"1F9D1-1F3FB-200D-2764-200D-1F9D1-1F3FE","image":"1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3fe.png","sheet_x":27,"sheet_y":46,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F9D1-1F3FB-200D-2764-FE0F-200D-1F9D1-1F3FF","non_qualified":"1F9D1-1F3FB-200D-2764-200D-1F9D1-1F3FF","image":"1f9d1-1f3fb-200d-2764-fe0f-200d-1f9d1-1f3ff.png","sheet_x":27,"sheet_y":47,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F9D1-1F3FC-200D-2764-FE0F-200D-1F9D1-1F3FB","non_qualified":"1F9D1-1F3FC-200D-2764-200D-1F9D1-1F3FB","image":"1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fb.png","sheet_x":27,"sheet_y":48,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F9D1-1F3FC-200D-2764-FE0F-200D-1F9D1-1F3FD","non_qualified":"1F9D1-1F3FC-200D-2764-200D-1F9D1-1F3FD","image":"1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fd.png","sheet_x":27,"sheet_y":49,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F9D1-1F3FC-200D-2764-FE0F-200D-1F9D1-1F3FE","non_qualified":"1F9D1-1F3FC-200D-2764-200D-1F9D1-1F3FE","image":"1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3fe.png","sheet_x":27,"sheet_y":50,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F9D1-1F3FC-200D-2764-FE0F-200D-1F9D1-1F3FF","non_qualified":"1F9D1-1F3FC-200D-2764-200D-1F9D1-1F3FF","image":"1f9d1-1f3fc-200d-2764-fe0f-200d-1f9d1-1f3ff.png","sheet_x":27,"sheet_y":51,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F9D1-1F3FD-200D-2764-FE0F-200D-1F9D1-1F3FB","non_qualified":"1F9D1-1F3FD-200D-2764-200D-1F9D1-1F3FB","image":"1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fb.png","sheet_x":27,"sheet_y":52,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F9D1-1F3FD-200D-2764-FE0F-200D-1F9D1-1F3FC","non_qualified":"1F9D1-1F3FD-200D-2764-200D-1F9D1-1F3FC","image":"1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fc.png","sheet_x":27,"sheet_y":53,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F9D1-1F3FD-200D-2764-FE0F-200D-1F9D1-1F3FE","non_qualified":"1F9D1-1F3FD-200D-2764-200D-1F9D1-1F3FE","image":"1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3fe.png","sheet_x":27,"sheet_y":54,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F9D1-1F3FD-200D-2764-FE0F-200D-1F9D1-1F3FF","non_qualified":"1F9D1-1F3FD-200D-2764-200D-1F9D1-1F3FF","image":"1f9d1-1f3fd-200d-2764-fe0f-200d-1f9d1-1f3ff.png","sheet_x":27,"sheet_y":55,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F9D1-1F3FE-200D-2764-FE0F-200D-1F9D1-1F3FB","non_qualified":"1F9D1-1F3FE-200D-2764-200D-1F9D1-1F3FB","image":"1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fb.png","sheet_x":27,"sheet_y":56,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F9D1-1F3FE-200D-2764-FE0F-200D-1F9D1-1F3FC","non_qualified":"1F9D1-1F3FE-200D-2764-200D-1F9D1-1F3FC","image":"1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fc.png","sheet_x":27,"sheet_y":57,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F9D1-1F3FE-200D-2764-FE0F-200D-1F9D1-1F3FD","non_qualified":"1F9D1-1F3FE-200D-2764-200D-1F9D1-1F3FD","image":"1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3fd.png","sheet_x":27,"sheet_y":58,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F9D1-1F3FE-200D-2764-FE0F-200D-1F9D1-1F3FF","non_qualified":"1F9D1-1F3FE-200D-2764-200D-1F9D1-1F3FF","image":"1f9d1-1f3fe-200d-2764-fe0f-200d-1f9d1-1f3ff.png","sheet_x":27,"sheet_y":59,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F9D1-1F3FF-200D-2764-FE0F-200D-1F9D1-1F3FB","non_qualified":"1F9D1-1F3FF-200D-2764-200D-1F9D1-1F3FB","image":"1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fb.png","sheet_x":27,"sheet_y":60,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F9D1-1F3FF-200D-2764-FE0F-200D-1F9D1-1F3FC","non_qualified":"1F9D1-1F3FF-200D-2764-200D-1F9D1-1F3FC","image":"1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fc.png","sheet_x":27,"sheet_y":61,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F9D1-1F3FF-200D-2764-FE0F-200D-1F9D1-1F3FD","non_qualified":"1F9D1-1F3FF-200D-2764-200D-1F9D1-1F3FD","image":"1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fd.png","sheet_x":28,"sheet_y":0,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F9D1-1F3FF-200D-2764-FE0F-200D-1F9D1-1F3FE","non_qualified":"1F9D1-1F3FF-200D-2764-200D-1F9D1-1F3FE","image":"1f9d1-1f3ff-200d-2764-fe0f-200d-1f9d1-1f3fe.png","sheet_x":28,"sheet_y":1,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WEDDING","unified":"1F492","non_qualified":null,"docomo":null,"au":"E5BB","softbank":"E43D","google":"FE82A","image":"1f492.png","sheet_x":28,"sheet_y":2,"short_name":"wedding","short_names":["wedding"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":887,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BEATING HEART","unified":"1F493","non_qualified":null,"docomo":"E6ED","au":"EB75","softbank":"E327","google":"FEB0D","image":"1f493.png","sheet_x":28,"sheet_y":3,"short_name":"heartbeat","short_names":["heartbeat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":135,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BROKEN HEART","unified":"1F494","non_qualified":null,"docomo":"E6EE","au":"E477","softbank":"E023","google":"FEB0E","image":"1f494.png","sheet_x":28,"sheet_y":4,"short_name":"broken_heart","short_names":["broken_heart"],"text":"<\/3","texts":["<\/3"],"category":"Smileys & Emotion","subcategory":"heart","sort_order":140,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TWO HEARTS","unified":"1F495","non_qualified":null,"docomo":"E6EF","au":"E478","softbank":null,"google":"FEB0F","image":"1f495.png","sheet_x":28,"sheet_y":5,"short_name":"two_hearts","short_names":["two_hearts"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":137,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPARKLING HEART","unified":"1F496","non_qualified":null,"docomo":"E6EC","au":"EAA6","softbank":null,"google":"FEB10","image":"1f496.png","sheet_x":28,"sheet_y":6,"short_name":"sparkling_heart","short_names":["sparkling_heart"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":133,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GROWING HEART","unified":"1F497","non_qualified":null,"docomo":"E6ED","au":"EB75","softbank":"E328","google":"FEB11","image":"1f497.png","sheet_x":28,"sheet_y":7,"short_name":"heartpulse","short_names":["heartpulse"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":134,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEART WITH ARROW","unified":"1F498","non_qualified":null,"docomo":"E6EC","au":"E4EA","softbank":"E329","google":"FEB12","image":"1f498.png","sheet_x":28,"sheet_y":8,"short_name":"cupid","short_names":["cupid"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":131,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLUE HEART","unified":"1F499","non_qualified":null,"docomo":"E6EC","au":"EAA7","softbank":"E32A","google":"FEB13","image":"1f499.png","sheet_x":28,"sheet_y":9,"short_name":"blue_heart","short_names":["blue_heart"],"text":"<3","texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":148,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GREEN HEART","unified":"1F49A","non_qualified":null,"docomo":"E6EC","au":"EAA8","softbank":"E32B","google":"FEB14","image":"1f49a.png","sheet_x":28,"sheet_y":10,"short_name":"green_heart","short_names":["green_heart"],"text":"<3","texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":147,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"YELLOW HEART","unified":"1F49B","non_qualified":null,"docomo":"E6EC","au":"EAA9","softbank":"E32C","google":"FEB15","image":"1f49b.png","sheet_x":28,"sheet_y":11,"short_name":"yellow_heart","short_names":["yellow_heart"],"text":"<3","texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":146,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PURPLE HEART","unified":"1F49C","non_qualified":null,"docomo":"E6EC","au":"EAAA","softbank":"E32D","google":"FEB16","image":"1f49c.png","sheet_x":28,"sheet_y":12,"short_name":"purple_heart","short_names":["purple_heart"],"text":"<3","texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":150,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEART WITH RIBBON","unified":"1F49D","non_qualified":null,"docomo":"E6EC","au":"EB54","softbank":"E437","google":"FEB17","image":"1f49d.png","sheet_x":28,"sheet_y":13,"short_name":"gift_heart","short_names":["gift_heart"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":132,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"REVOLVING HEARTS","unified":"1F49E","non_qualified":null,"docomo":"E6ED","au":"E5AF","softbank":null,"google":"FEB18","image":"1f49e.png","sheet_x":28,"sheet_y":14,"short_name":"revolving_hearts","short_names":["revolving_hearts"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":136,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEART DECORATION","unified":"1F49F","non_qualified":null,"docomo":"E6F8","au":"E595","softbank":"E204","google":"FEB19","image":"1f49f.png","sheet_x":28,"sheet_y":15,"short_name":"heart_decoration","short_names":["heart_decoration"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":138,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DIAMOND SHAPE WITH A DOT INSIDE","unified":"1F4A0","non_qualified":null,"docomo":"E6F8","au":null,"softbank":null,"google":"FEB55","image":"1f4a0.png","sheet_x":28,"sheet_y":16,"short_name":"diamond_shape_with_a_dot_inside","short_names":["diamond_shape_with_a_dot_inside"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1631,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ELECTRIC LIGHT BULB","unified":"1F4A1","non_qualified":null,"docomo":"E6FB","au":"E476","softbank":"E10F","google":"FEB56","image":"1f4a1.png","sheet_x":28,"sheet_y":17,"short_name":"bulb","short_names":["bulb"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1258,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ANGER SYMBOL","unified":"1F4A2","non_qualified":null,"docomo":"E6FC","au":"E4E5","softbank":"E334","google":"FEB57","image":"1f4a2.png","sheet_x":28,"sheet_y":18,"short_name":"anger","short_names":["anger"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":157,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOMB","unified":"1F4A3","non_qualified":null,"docomo":"E6FE","au":"E47A","softbank":"E311","google":"FEB58","image":"1f4a3.png","sheet_x":28,"sheet_y":19,"short_name":"bomb","short_names":["bomb"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1345,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SLEEPING SYMBOL","unified":"1F4A4","non_qualified":null,"docomo":"E701","au":"E475","softbank":"E13C","google":"FEB59","image":"1f4a4.png","sheet_x":28,"sheet_y":20,"short_name":"zzz","short_names":["zzz"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":168,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COLLISION SYMBOL","unified":"1F4A5","non_qualified":null,"docomo":"E705","au":"E5B0","softbank":null,"google":"FEB5A","image":"1f4a5.png","sheet_x":28,"sheet_y":21,"short_name":"boom","short_names":["boom","collision"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":158,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPLASHING SWEAT SYMBOL","unified":"1F4A6","non_qualified":null,"docomo":"E706","au":"E5B1","softbank":"E331","google":"FEB5B","image":"1f4a6.png","sheet_x":28,"sheet_y":22,"short_name":"sweat_drops","short_names":["sweat_drops"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":160,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DROPLET","unified":"1F4A7","non_qualified":null,"docomo":"E707","au":"E4E6","softbank":null,"google":"FEB5C","image":"1f4a7.png","sheet_x":28,"sheet_y":23,"short_name":"droplet","short_names":["droplet"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1063,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DASH SYMBOL","unified":"1F4A8","non_qualified":null,"docomo":"E708","au":"E4F4","softbank":"E330","google":"FEB5D","image":"1f4a8.png","sheet_x":28,"sheet_y":24,"short_name":"dash","short_names":["dash"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":161,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PILE OF POO","unified":"1F4A9","non_qualified":null,"docomo":null,"au":"E4F5","softbank":"E05A","google":"FE4F4","image":"1f4a9.png","sheet_x":28,"sheet_y":25,"short_name":"hankey","short_names":["hankey","poop","shit"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-costume","sort_order":110,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLEXED BICEPS","unified":"1F4AA","non_qualified":null,"docomo":null,"au":"E4E9","softbank":"E14C","google":"FEB5E","image":"1f4aa.png","sheet_x":28,"sheet_y":26,"short_name":"muscle","short_names":["muscle"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":212,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F4AA-1F3FB","non_qualified":null,"image":"1f4aa-1f3fb.png","sheet_x":28,"sheet_y":27,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F4AA-1F3FC","non_qualified":null,"image":"1f4aa-1f3fc.png","sheet_x":28,"sheet_y":28,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F4AA-1F3FD","non_qualified":null,"image":"1f4aa-1f3fd.png","sheet_x":28,"sheet_y":29,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F4AA-1F3FE","non_qualified":null,"image":"1f4aa-1f3fe.png","sheet_x":28,"sheet_y":30,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F4AA-1F3FF","non_qualified":null,"image":"1f4aa-1f3ff.png","sheet_x":28,"sheet_y":31,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"DIZZY SYMBOL","unified":"1F4AB","non_qualified":null,"docomo":null,"au":"EB5C","softbank":null,"google":"FEB5F","image":"1f4ab.png","sheet_x":28,"sheet_y":32,"short_name":"dizzy","short_names":["dizzy"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":159,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPEECH BALLOON","unified":"1F4AC","non_qualified":null,"docomo":null,"au":"E4FD","softbank":null,"google":"FE532","image":"1f4ac.png","sheet_x":28,"sheet_y":33,"short_name":"speech_balloon","short_names":["speech_balloon"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":163,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"THOUGHT BALLOON","unified":"1F4AD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4ad.png","sheet_x":28,"sheet_y":34,"short_name":"thought_balloon","short_names":["thought_balloon"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":167,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE FLOWER","unified":"1F4AE","non_qualified":null,"docomo":null,"au":"E4F0","softbank":null,"google":"FEB7A","image":"1f4ae.png","sheet_x":28,"sheet_y":35,"short_name":"white_flower","short_names":["white_flower"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":686,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HUNDRED POINTS SYMBOL","unified":"1F4AF","non_qualified":null,"docomo":null,"au":"E4F2","softbank":null,"google":"FEB7B","image":"1f4af.png","sheet_x":28,"sheet_y":36,"short_name":"100","short_names":["100"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":156,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MONEY BAG","unified":"1F4B0","non_qualified":null,"docomo":"E715","au":"E4C7","softbank":"E12F","google":"FE4DD","image":"1f4b0.png","sheet_x":28,"sheet_y":37,"short_name":"moneybag","short_names":["moneybag"],"text":null,"texts":null,"category":"Objects","subcategory":"money","sort_order":1279,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CURRENCY EXCHANGE","unified":"1F4B1","non_qualified":null,"docomo":null,"au":null,"softbank":"E149","google":"FE4DE","image":"1f4b1.png","sheet_x":28,"sheet_y":38,"short_name":"currency_exchange","short_names":["currency_exchange"],"text":null,"texts":null,"category":"Symbols","subcategory":"currency","sort_order":1526,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAVY DOLLAR SIGN","unified":"1F4B2","non_qualified":null,"docomo":"E715","au":"E579","softbank":null,"google":"FE4E0","image":"1f4b2.png","sheet_x":28,"sheet_y":39,"short_name":"heavy_dollar_sign","short_names":["heavy_dollar_sign"],"text":null,"texts":null,"category":"Symbols","subcategory":"currency","sort_order":1527,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CREDIT CARD","unified":"1F4B3","non_qualified":null,"docomo":null,"au":"E57C","softbank":null,"google":"FE4E1","image":"1f4b3.png","sheet_x":28,"sheet_y":40,"short_name":"credit_card","short_names":["credit_card"],"text":null,"texts":null,"category":"Objects","subcategory":"money","sort_order":1286,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BANKNOTE WITH YEN SIGN","unified":"1F4B4","non_qualified":null,"docomo":"E6D6","au":"E57D","softbank":null,"google":"FE4E2","image":"1f4b4.png","sheet_x":28,"sheet_y":41,"short_name":"yen","short_names":["yen"],"text":null,"texts":null,"category":"Objects","subcategory":"money","sort_order":1281,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BANKNOTE WITH DOLLAR SIGN","unified":"1F4B5","non_qualified":null,"docomo":"E715","au":"E585","softbank":null,"google":"FE4E3","image":"1f4b5.png","sheet_x":28,"sheet_y":42,"short_name":"dollar","short_names":["dollar"],"text":null,"texts":null,"category":"Objects","subcategory":"money","sort_order":1282,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BANKNOTE WITH EURO SIGN","unified":"1F4B6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4b6.png","sheet_x":28,"sheet_y":43,"short_name":"euro","short_names":["euro"],"text":null,"texts":null,"category":"Objects","subcategory":"money","sort_order":1283,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BANKNOTE WITH POUND SIGN","unified":"1F4B7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4b7.png","sheet_x":28,"sheet_y":44,"short_name":"pound","short_names":["pound"],"text":null,"texts":null,"category":"Objects","subcategory":"money","sort_order":1284,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MONEY WITH WINGS","unified":"1F4B8","non_qualified":null,"docomo":null,"au":"EB5B","softbank":null,"google":"FE4E4","image":"1f4b8.png","sheet_x":28,"sheet_y":45,"short_name":"money_with_wings","short_names":["money_with_wings"],"text":null,"texts":null,"category":"Objects","subcategory":"money","sort_order":1285,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHART WITH UPWARDS TREND AND YEN SIGN","unified":"1F4B9","non_qualified":null,"docomo":null,"au":"E5DC","softbank":"E14A","google":"FE4DF","image":"1f4b9.png","sheet_x":28,"sheet_y":46,"short_name":"chart","short_names":["chart"],"text":null,"texts":null,"category":"Objects","subcategory":"money","sort_order":1288,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SEAT","unified":"1F4BA","non_qualified":null,"docomo":"E6B2","au":null,"softbank":"E11F","google":"FE537","image":"1f4ba.png","sheet_x":28,"sheet_y":47,"short_name":"seat","short_names":["seat"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":977,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PERSONAL COMPUTER","unified":"1F4BB","non_qualified":null,"docomo":"E716","au":"E5B8","softbank":"E00C","google":"FE538","image":"1f4bb.png","sheet_x":28,"sheet_y":48,"short_name":"computer","short_names":["computer"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1235,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BRIEFCASE","unified":"1F4BC","non_qualified":null,"docomo":"E682","au":"E5CE","softbank":"E11E","google":"FE53B","image":"1f4bc.png","sheet_x":28,"sheet_y":49,"short_name":"briefcase","short_names":["briefcase"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1309,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MINIDISC","unified":"1F4BD","non_qualified":null,"docomo":null,"au":"E582","softbank":"E316","google":"FE53C","image":"1f4bd.png","sheet_x":28,"sheet_y":50,"short_name":"minidisc","short_names":["minidisc"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1241,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLOPPY DISK","unified":"1F4BE","non_qualified":null,"docomo":null,"au":"E562","softbank":null,"google":"FE53D","image":"1f4be.png","sheet_x":28,"sheet_y":51,"short_name":"floppy_disk","short_names":["floppy_disk"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1242,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OPTICAL DISC","unified":"1F4BF","non_qualified":null,"docomo":"E68C","au":"E50C","softbank":"E126","google":"FE81D","image":"1f4bf.png","sheet_x":28,"sheet_y":52,"short_name":"cd","short_names":["cd"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1243,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DVD","unified":"1F4C0","non_qualified":null,"docomo":"E68C","au":"E50C","softbank":"E127","google":"FE81E","image":"1f4c0.png","sheet_x":28,"sheet_y":53,"short_name":"dvd","short_names":["dvd"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1244,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FILE FOLDER","unified":"1F4C1","non_qualified":null,"docomo":null,"au":"E58F","softbank":null,"google":"FE543","image":"1f4c1.png","sheet_x":28,"sheet_y":54,"short_name":"file_folder","short_names":["file_folder"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1310,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OPEN FILE FOLDER","unified":"1F4C2","non_qualified":null,"docomo":null,"au":"E590","softbank":null,"google":"FE544","image":"1f4c2.png","sheet_x":28,"sheet_y":55,"short_name":"open_file_folder","short_names":["open_file_folder"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1311,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PAGE WITH CURL","unified":"1F4C3","non_qualified":null,"docomo":"E689","au":"E561","softbank":null,"google":"FE540","image":"1f4c3.png","sheet_x":28,"sheet_y":56,"short_name":"page_with_curl","short_names":["page_with_curl"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1271,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PAGE FACING UP","unified":"1F4C4","non_qualified":null,"docomo":"E689","au":"E569","softbank":null,"google":"FE541","image":"1f4c4.png","sheet_x":28,"sheet_y":57,"short_name":"page_facing_up","short_names":["page_facing_up"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1273,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CALENDAR","unified":"1F4C5","non_qualified":null,"docomo":null,"au":"E563","softbank":null,"google":"FE542","image":"1f4c5.png","sheet_x":28,"sheet_y":58,"short_name":"date","short_names":["date"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1313,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TEAR-OFF CALENDAR","unified":"1F4C6","non_qualified":null,"docomo":null,"au":"E56A","softbank":null,"google":"FE549","image":"1f4c6.png","sheet_x":28,"sheet_y":59,"short_name":"calendar","short_names":["calendar"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1314,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CARD INDEX","unified":"1F4C7","non_qualified":null,"docomo":"E683","au":"E56C","softbank":null,"google":"FE54D","image":"1f4c7.png","sheet_x":28,"sheet_y":60,"short_name":"card_index","short_names":["card_index"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1317,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHART WITH UPWARDS TREND","unified":"1F4C8","non_qualified":null,"docomo":null,"au":"E575","softbank":null,"google":"FE54B","image":"1f4c8.png","sheet_x":28,"sheet_y":61,"short_name":"chart_with_upwards_trend","short_names":["chart_with_upwards_trend"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1318,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHART WITH DOWNWARDS TREND","unified":"1F4C9","non_qualified":null,"docomo":null,"au":"E576","softbank":null,"google":"FE54C","image":"1f4c9.png","sheet_x":29,"sheet_y":0,"short_name":"chart_with_downwards_trend","short_names":["chart_with_downwards_trend"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1319,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BAR CHART","unified":"1F4CA","non_qualified":null,"docomo":null,"au":"E574","softbank":null,"google":"FE54A","image":"1f4ca.png","sheet_x":29,"sheet_y":1,"short_name":"bar_chart","short_names":["bar_chart"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1320,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLIPBOARD","unified":"1F4CB","non_qualified":null,"docomo":"E689","au":"E564","softbank":null,"google":"FE548","image":"1f4cb.png","sheet_x":29,"sheet_y":2,"short_name":"clipboard","short_names":["clipboard"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1321,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PUSHPIN","unified":"1F4CC","non_qualified":null,"docomo":null,"au":"E56D","softbank":null,"google":"FE54E","image":"1f4cc.png","sheet_x":29,"sheet_y":3,"short_name":"pushpin","short_names":["pushpin"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1322,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROUND PUSHPIN","unified":"1F4CD","non_qualified":null,"docomo":null,"au":"E560","softbank":null,"google":"FE53F","image":"1f4cd.png","sheet_x":29,"sheet_y":4,"short_name":"round_pushpin","short_names":["round_pushpin"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1323,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PAPERCLIP","unified":"1F4CE","non_qualified":null,"docomo":"E730","au":"E4A0","softbank":null,"google":"FE53A","image":"1f4ce.png","sheet_x":29,"sheet_y":5,"short_name":"paperclip","short_names":["paperclip"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1324,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STRAIGHT RULER","unified":"1F4CF","non_qualified":null,"docomo":null,"au":"E570","softbank":null,"google":"FE550","image":"1f4cf.png","sheet_x":29,"sheet_y":6,"short_name":"straight_ruler","short_names":["straight_ruler"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1326,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRIANGULAR RULER","unified":"1F4D0","non_qualified":null,"docomo":null,"au":"E4A2","softbank":null,"google":"FE551","image":"1f4d0.png","sheet_x":29,"sheet_y":7,"short_name":"triangular_ruler","short_names":["triangular_ruler"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1327,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOOKMARK TABS","unified":"1F4D1","non_qualified":null,"docomo":"E689","au":"EB0B","softbank":null,"google":"FE552","image":"1f4d1.png","sheet_x":29,"sheet_y":8,"short_name":"bookmark_tabs","short_names":["bookmark_tabs"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1276,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEDGER","unified":"1F4D2","non_qualified":null,"docomo":"E683","au":"E56E","softbank":null,"google":"FE54F","image":"1f4d2.png","sheet_x":29,"sheet_y":9,"short_name":"ledger","short_names":["ledger"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1270,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NOTEBOOK","unified":"1F4D3","non_qualified":null,"docomo":"E683","au":"E56B","softbank":null,"google":"FE545","image":"1f4d3.png","sheet_x":29,"sheet_y":10,"short_name":"notebook","short_names":["notebook"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1269,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NOTEBOOK WITH DECORATIVE COVER","unified":"1F4D4","non_qualified":null,"docomo":"E683","au":"E49D","softbank":null,"google":"FE547","image":"1f4d4.png","sheet_x":29,"sheet_y":11,"short_name":"notebook_with_decorative_cover","short_names":["notebook_with_decorative_cover"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1262,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOSED BOOK","unified":"1F4D5","non_qualified":null,"docomo":"E683","au":"E568","softbank":null,"google":"FE502","image":"1f4d5.png","sheet_x":29,"sheet_y":12,"short_name":"closed_book","short_names":["closed_book"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1263,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OPEN BOOK","unified":"1F4D6","non_qualified":null,"docomo":"E683","au":"E49F","softbank":"E148","google":"FE546","image":"1f4d6.png","sheet_x":29,"sheet_y":13,"short_name":"book","short_names":["book","open_book"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1264,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GREEN BOOK","unified":"1F4D7","non_qualified":null,"docomo":"E683","au":"E565","softbank":null,"google":"FE4FF","image":"1f4d7.png","sheet_x":29,"sheet_y":14,"short_name":"green_book","short_names":["green_book"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1265,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLUE BOOK","unified":"1F4D8","non_qualified":null,"docomo":"E683","au":"E566","softbank":null,"google":"FE500","image":"1f4d8.png","sheet_x":29,"sheet_y":15,"short_name":"blue_book","short_names":["blue_book"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1266,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ORANGE BOOK","unified":"1F4D9","non_qualified":null,"docomo":"E683","au":"E567","softbank":null,"google":"FE501","image":"1f4d9.png","sheet_x":29,"sheet_y":16,"short_name":"orange_book","short_names":["orange_book"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1267,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOOKS","unified":"1F4DA","non_qualified":null,"docomo":"E683","au":"E56F","softbank":null,"google":"FE503","image":"1f4da.png","sheet_x":29,"sheet_y":17,"short_name":"books","short_names":["books"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1268,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NAME BADGE","unified":"1F4DB","non_qualified":null,"docomo":null,"au":"E51D","softbank":null,"google":"FE504","image":"1f4db.png","sheet_x":29,"sheet_y":18,"short_name":"name_badge","short_names":["name_badge"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1532,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SCROLL","unified":"1F4DC","non_qualified":null,"docomo":"E70A","au":"E55F","softbank":null,"google":"FE4FD","image":"1f4dc.png","sheet_x":29,"sheet_y":19,"short_name":"scroll","short_names":["scroll"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1272,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MEMO","unified":"1F4DD","non_qualified":null,"docomo":"E689","au":"EA92","softbank":"E301","google":"FE527","image":"1f4dd.png","sheet_x":29,"sheet_y":20,"short_name":"memo","short_names":["memo","pencil"],"text":null,"texts":null,"category":"Objects","subcategory":"writing","sort_order":1308,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TELEPHONE RECEIVER","unified":"1F4DE","non_qualified":null,"docomo":"E687","au":"E51E","softbank":null,"google":"FE524","image":"1f4de.png","sheet_x":29,"sheet_y":21,"short_name":"telephone_receiver","short_names":["telephone_receiver"],"text":null,"texts":null,"category":"Objects","subcategory":"phone","sort_order":1229,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PAGER","unified":"1F4DF","non_qualified":null,"docomo":"E65A","au":"E59B","softbank":null,"google":"FE522","image":"1f4df.png","sheet_x":29,"sheet_y":22,"short_name":"pager","short_names":["pager"],"text":null,"texts":null,"category":"Objects","subcategory":"phone","sort_order":1230,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FAX MACHINE","unified":"1F4E0","non_qualified":null,"docomo":"E6D0","au":"E520","softbank":"E00B","google":"FE528","image":"1f4e0.png","sheet_x":29,"sheet_y":23,"short_name":"fax","short_names":["fax"],"text":null,"texts":null,"category":"Objects","subcategory":"phone","sort_order":1231,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SATELLITE ANTENNA","unified":"1F4E1","non_qualified":null,"docomo":null,"au":"E4A8","softbank":"E14B","google":"FE531","image":"1f4e1.png","sheet_x":29,"sheet_y":24,"short_name":"satellite_antenna","short_names":["satellite_antenna"],"text":null,"texts":null,"category":"Objects","subcategory":"science","sort_order":1370,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PUBLIC ADDRESS LOUDSPEAKER","unified":"1F4E2","non_qualified":null,"docomo":null,"au":"E511","softbank":"E142","google":"FE52F","image":"1f4e2.png","sheet_x":29,"sheet_y":25,"short_name":"loudspeaker","short_names":["loudspeaker"],"text":null,"texts":null,"category":"Objects","subcategory":"sound","sort_order":1201,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHEERING MEGAPHONE","unified":"1F4E3","non_qualified":null,"docomo":null,"au":"E511","softbank":"E317","google":"FE530","image":"1f4e3.png","sheet_x":29,"sheet_y":26,"short_name":"mega","short_names":["mega"],"text":null,"texts":null,"category":"Objects","subcategory":"sound","sort_order":1202,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OUTBOX TRAY","unified":"1F4E4","non_qualified":null,"docomo":null,"au":"E592","softbank":null,"google":"FE533","image":"1f4e4.png","sheet_x":29,"sheet_y":27,"short_name":"outbox_tray","short_names":["outbox_tray"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1293,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"INBOX TRAY","unified":"1F4E5","non_qualified":null,"docomo":null,"au":"E593","softbank":null,"google":"FE534","image":"1f4e5.png","sheet_x":29,"sheet_y":28,"short_name":"inbox_tray","short_names":["inbox_tray"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1294,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PACKAGE","unified":"1F4E6","non_qualified":null,"docomo":"E685","au":"E51F","softbank":null,"google":"FE535","image":"1f4e6.png","sheet_x":29,"sheet_y":29,"short_name":"package","short_names":["package"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1295,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"E-MAIL SYMBOL","unified":"1F4E7","non_qualified":null,"docomo":"E6D3","au":"EB71","softbank":null,"google":"FEB92","image":"1f4e7.png","sheet_x":29,"sheet_y":30,"short_name":"e-mail","short_names":["e-mail"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1290,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"INCOMING ENVELOPE","unified":"1F4E8","non_qualified":null,"docomo":"E6CF","au":"E591","softbank":null,"google":"FE52A","image":"1f4e8.png","sheet_x":29,"sheet_y":31,"short_name":"incoming_envelope","short_names":["incoming_envelope"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1291,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ENVELOPE WITH DOWNWARDS ARROW ABOVE","unified":"1F4E9","non_qualified":null,"docomo":"E6CF","au":"EB62","softbank":"E103","google":"FE52B","image":"1f4e9.png","sheet_x":29,"sheet_y":32,"short_name":"envelope_with_arrow","short_names":["envelope_with_arrow"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1292,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOSED MAILBOX WITH LOWERED FLAG","unified":"1F4EA","non_qualified":null,"docomo":"E665","au":"E51B","softbank":null,"google":"FE52C","image":"1f4ea.png","sheet_x":29,"sheet_y":33,"short_name":"mailbox_closed","short_names":["mailbox_closed"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1297,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOSED MAILBOX WITH RAISED FLAG","unified":"1F4EB","non_qualified":null,"docomo":"E665","au":"EB0A","softbank":"E101","google":"FE52D","image":"1f4eb.png","sheet_x":29,"sheet_y":34,"short_name":"mailbox","short_names":["mailbox"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1296,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OPEN MAILBOX WITH RAISED FLAG","unified":"1F4EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4ec.png","sheet_x":29,"sheet_y":35,"short_name":"mailbox_with_mail","short_names":["mailbox_with_mail"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1298,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OPEN MAILBOX WITH LOWERED FLAG","unified":"1F4ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4ed.png","sheet_x":29,"sheet_y":36,"short_name":"mailbox_with_no_mail","short_names":["mailbox_with_no_mail"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1299,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POSTBOX","unified":"1F4EE","non_qualified":null,"docomo":"E665","au":"E51B","softbank":"E102","google":"FE52E","image":"1f4ee.png","sheet_x":29,"sheet_y":37,"short_name":"postbox","short_names":["postbox"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1300,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POSTAL HORN","unified":"1F4EF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4ef.png","sheet_x":29,"sheet_y":38,"short_name":"postal_horn","short_names":["postal_horn"],"text":null,"texts":null,"category":"Objects","subcategory":"sound","sort_order":1203,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEWSPAPER","unified":"1F4F0","non_qualified":null,"docomo":null,"au":"E58B","softbank":null,"google":"FE822","image":"1f4f0.png","sheet_x":29,"sheet_y":39,"short_name":"newspaper","short_names":["newspaper"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1274,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOBILE PHONE","unified":"1F4F1","non_qualified":null,"docomo":"E688","au":"E588","softbank":"E00A","google":"FE525","image":"1f4f1.png","sheet_x":29,"sheet_y":40,"short_name":"iphone","short_names":["iphone"],"text":null,"texts":null,"category":"Objects","subcategory":"phone","sort_order":1226,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOBILE PHONE WITH RIGHTWARDS ARROW AT LEFT","unified":"1F4F2","non_qualified":null,"docomo":"E6CE","au":"EB08","softbank":"E104","google":"FE526","image":"1f4f2.png","sheet_x":29,"sheet_y":41,"short_name":"calling","short_names":["calling"],"text":null,"texts":null,"category":"Objects","subcategory":"phone","sort_order":1227,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"VIBRATION MODE","unified":"1F4F3","non_qualified":null,"docomo":null,"au":"EA90","softbank":"E250","google":"FE839","image":"1f4f3.png","sheet_x":29,"sheet_y":42,"short_name":"vibration_mode","short_names":["vibration_mode"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1508,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOBILE PHONE OFF","unified":"1F4F4","non_qualified":null,"docomo":null,"au":"EA91","softbank":"E251","google":"FE83A","image":"1f4f4.png","sheet_x":29,"sheet_y":43,"short_name":"mobile_phone_off","short_names":["mobile_phone_off"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1509,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NO MOBILE PHONES","unified":"1F4F5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4f5.png","sheet_x":29,"sheet_y":44,"short_name":"no_mobile_phones","short_names":["no_mobile_phones"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1434,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ANTENNA WITH BARS","unified":"1F4F6","non_qualified":null,"docomo":null,"au":"EA84","softbank":"E20B","google":"FE838","image":"1f4f6.png","sheet_x":29,"sheet_y":45,"short_name":"signal_strength","short_names":["signal_strength"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1506,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CAMERA","unified":"1F4F7","non_qualified":null,"docomo":"E681","au":"E515","softbank":"E008","google":"FE4EF","image":"1f4f7.png","sheet_x":29,"sheet_y":46,"short_name":"camera","short_names":["camera"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1251,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CAMERA WITH FLASH","unified":"1F4F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4f8.png","sheet_x":29,"sheet_y":47,"short_name":"camera_with_flash","short_names":["camera_with_flash"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1252,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"VIDEO CAMERA","unified":"1F4F9","non_qualified":null,"docomo":"E677","au":"E57E","softbank":null,"google":"FE4F9","image":"1f4f9.png","sheet_x":29,"sheet_y":48,"short_name":"video_camera","short_names":["video_camera"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1253,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TELEVISION","unified":"1F4FA","non_qualified":null,"docomo":"E68A","au":"E502","softbank":"E12A","google":"FE81C","image":"1f4fa.png","sheet_x":29,"sheet_y":49,"short_name":"tv","short_names":["tv"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1250,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RADIO","unified":"1F4FB","non_qualified":null,"docomo":null,"au":"E5B9","softbank":"E128","google":"FE81F","image":"1f4fb.png","sheet_x":29,"sheet_y":50,"short_name":"radio","short_names":["radio"],"text":null,"texts":null,"category":"Objects","subcategory":"music","sort_order":1214,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"VIDEOCASSETTE","unified":"1F4FC","non_qualified":null,"docomo":null,"au":"E580","softbank":"E129","google":"FE820","image":"1f4fc.png","sheet_x":29,"sheet_y":51,"short_name":"vhs","short_names":["vhs"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1254,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FILM PROJECTOR","unified":"1F4FD-FE0F","non_qualified":"1F4FD","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4fd-fe0f.png","sheet_x":29,"sheet_y":52,"short_name":"film_projector","short_names":["film_projector"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1248,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PRAYER BEADS","unified":"1F4FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4ff.png","sheet_x":29,"sheet_y":53,"short_name":"prayer_beads","short_names":["prayer_beads"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1193,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TWISTED RIGHTWARDS ARROWS","unified":"1F500","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f500.png","sheet_x":29,"sheet_y":54,"short_name":"twisted_rightwards_arrows","short_names":["twisted_rightwards_arrows"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1485,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS","unified":"1F501","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f501.png","sheet_x":29,"sheet_y":55,"short_name":"repeat","short_names":["repeat"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1486,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY","unified":"1F502","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f502.png","sheet_x":29,"sheet_y":56,"short_name":"repeat_one","short_names":["repeat_one"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1487,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS","unified":"1F503","non_qualified":null,"docomo":"E735","au":"EB0D","softbank":null,"google":"FEB91","image":"1f503.png","sheet_x":29,"sheet_y":57,"short_name":"arrows_clockwise","short_names":["arrows_clockwise"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1452,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ANTICLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS","unified":"1F504","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f504.png","sheet_x":29,"sheet_y":58,"short_name":"arrows_counterclockwise","short_names":["arrows_counterclockwise"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1453,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOW BRIGHTNESS SYMBOL","unified":"1F505","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f505.png","sheet_x":29,"sheet_y":59,"short_name":"low_brightness","short_names":["low_brightness"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1504,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HIGH BRIGHTNESS SYMBOL","unified":"1F506","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f506.png","sheet_x":29,"sheet_y":60,"short_name":"high_brightness","short_names":["high_brightness"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1505,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPEAKER WITH CANCELLATION STROKE","unified":"1F507","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f507.png","sheet_x":29,"sheet_y":61,"short_name":"mute","short_names":["mute"],"text":null,"texts":null,"category":"Objects","subcategory":"sound","sort_order":1197,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPEAKER","unified":"1F508","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f508.png","sheet_x":30,"sheet_y":0,"short_name":"speaker","short_names":["speaker"],"text":null,"texts":null,"category":"Objects","subcategory":"sound","sort_order":1198,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPEAKER WITH ONE SOUND WAVE","unified":"1F509","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f509.png","sheet_x":30,"sheet_y":1,"short_name":"sound","short_names":["sound"],"text":null,"texts":null,"category":"Objects","subcategory":"sound","sort_order":1199,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPEAKER WITH THREE SOUND WAVES","unified":"1F50A","non_qualified":null,"docomo":null,"au":"E511","softbank":"E141","google":"FE821","image":"1f50a.png","sheet_x":30,"sheet_y":2,"short_name":"loud_sound","short_names":["loud_sound"],"text":null,"texts":null,"category":"Objects","subcategory":"sound","sort_order":1200,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BATTERY","unified":"1F50B","non_qualified":null,"docomo":null,"au":"E584","softbank":null,"google":"FE4FC","image":"1f50b.png","sheet_x":30,"sheet_y":3,"short_name":"battery","short_names":["battery"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1232,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ELECTRIC PLUG","unified":"1F50C","non_qualified":null,"docomo":null,"au":"E589","softbank":null,"google":"FE4FE","image":"1f50c.png","sheet_x":30,"sheet_y":4,"short_name":"electric_plug","short_names":["electric_plug"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1234,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEFT-POINTING MAGNIFYING GLASS","unified":"1F50D","non_qualified":null,"docomo":"E6DC","au":"E518","softbank":"E114","google":"FEB85","image":"1f50d.png","sheet_x":30,"sheet_y":5,"short_name":"mag","short_names":["mag"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1255,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RIGHT-POINTING MAGNIFYING GLASS","unified":"1F50E","non_qualified":null,"docomo":"E6DC","au":"EB05","softbank":null,"google":"FEB8D","image":"1f50e.png","sheet_x":30,"sheet_y":6,"short_name":"mag_right","short_names":["mag_right"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1256,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOCK WITH INK PEN","unified":"1F50F","non_qualified":null,"docomo":"E6D9","au":"EB0C","softbank":null,"google":"FEB90","image":"1f50f.png","sheet_x":30,"sheet_y":7,"short_name":"lock_with_ink_pen","short_names":["lock_with_ink_pen"],"text":null,"texts":null,"category":"Objects","subcategory":"lock","sort_order":1334,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOSED LOCK WITH KEY","unified":"1F510","non_qualified":null,"docomo":"E6D9","au":"EAFC","softbank":null,"google":"FEB8A","image":"1f510.png","sheet_x":30,"sheet_y":8,"short_name":"closed_lock_with_key","short_names":["closed_lock_with_key"],"text":null,"texts":null,"category":"Objects","subcategory":"lock","sort_order":1335,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KEY","unified":"1F511","non_qualified":null,"docomo":"E6D9","au":"E519","softbank":"E03F","google":"FEB82","image":"1f511.png","sheet_x":30,"sheet_y":9,"short_name":"key","short_names":["key"],"text":null,"texts":null,"category":"Objects","subcategory":"lock","sort_order":1336,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOCK","unified":"1F512","non_qualified":null,"docomo":"E6D9","au":"E51C","softbank":"E144","google":"FEB86","image":"1f512.png","sheet_x":30,"sheet_y":10,"short_name":"lock","short_names":["lock"],"text":null,"texts":null,"category":"Objects","subcategory":"lock","sort_order":1332,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OPEN LOCK","unified":"1F513","non_qualified":null,"docomo":"E6D9","au":"E51C","softbank":"E145","google":"FEB87","image":"1f513.png","sheet_x":30,"sheet_y":11,"short_name":"unlock","short_names":["unlock"],"text":null,"texts":null,"category":"Objects","subcategory":"lock","sort_order":1333,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BELL","unified":"1F514","non_qualified":null,"docomo":"E713","au":"E512","softbank":"E325","google":"FE4F2","image":"1f514.png","sheet_x":30,"sheet_y":12,"short_name":"bell","short_names":["bell"],"text":null,"texts":null,"category":"Objects","subcategory":"sound","sort_order":1204,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BELL WITH CANCELLATION STROKE","unified":"1F515","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f515.png","sheet_x":30,"sheet_y":13,"short_name":"no_bell","short_names":["no_bell"],"text":null,"texts":null,"category":"Objects","subcategory":"sound","sort_order":1205,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOOKMARK","unified":"1F516","non_qualified":null,"docomo":null,"au":"EB07","softbank":null,"google":"FEB8F","image":"1f516.png","sheet_x":30,"sheet_y":14,"short_name":"bookmark","short_names":["bookmark"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1277,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LINK SYMBOL","unified":"1F517","non_qualified":null,"docomo":null,"au":"E58A","softbank":null,"google":"FEB4B","image":"1f517.png","sheet_x":30,"sheet_y":15,"short_name":"link","short_names":["link"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1357,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RADIO BUTTON","unified":"1F518","non_qualified":null,"docomo":null,"au":"EB04","softbank":null,"google":"FEB8C","image":"1f518.png","sheet_x":30,"sheet_y":16,"short_name":"radio_button","short_names":["radio_button"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1632,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BACK WITH LEFTWARDS ARROW ABOVE","unified":"1F519","non_qualified":null,"docomo":null,"au":"EB06","softbank":null,"google":"FEB8E","image":"1f519.png","sheet_x":30,"sheet_y":17,"short_name":"back","short_names":["back"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1454,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"END WITH LEFTWARDS ARROW ABOVE","unified":"1F51A","non_qualified":null,"docomo":"E6B9","au":null,"softbank":null,"google":"FE01A","image":"1f51a.png","sheet_x":30,"sheet_y":18,"short_name":"end","short_names":["end"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1455,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ON WITH EXCLAMATION MARK WITH LEFT RIGHT ARROW ABOVE","unified":"1F51B","non_qualified":null,"docomo":"E6B8","au":null,"softbank":null,"google":"FE019","image":"1f51b.png","sheet_x":30,"sheet_y":19,"short_name":"on","short_names":["on"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1456,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SOON WITH RIGHTWARDS ARROW ABOVE","unified":"1F51C","non_qualified":null,"docomo":"E6B7","au":null,"softbank":null,"google":"FE018","image":"1f51c.png","sheet_x":30,"sheet_y":20,"short_name":"soon","short_names":["soon"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1457,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TOP WITH UPWARDS ARROW ABOVE","unified":"1F51D","non_qualified":null,"docomo":null,"au":null,"softbank":"E24C","google":"FEB42","image":"1f51d.png","sheet_x":30,"sheet_y":21,"short_name":"top","short_names":["top"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1458,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NO ONE UNDER EIGHTEEN SYMBOL","unified":"1F51E","non_qualified":null,"docomo":null,"au":"EA83","softbank":"E207","google":"FEB25","image":"1f51e.png","sheet_x":30,"sheet_y":22,"short_name":"underage","short_names":["underage"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1435,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KEYCAP TEN","unified":"1F51F","non_qualified":null,"docomo":null,"au":"E52B","softbank":null,"google":"FE83B","image":"1f51f.png","sheet_x":30,"sheet_y":23,"short_name":"keycap_ten","short_names":["keycap_ten"],"text":null,"texts":null,"category":"Symbols","subcategory":"keycap","sort_order":1561,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"INPUT SYMBOL FOR LATIN CAPITAL LETTERS","unified":"1F520","non_qualified":null,"docomo":null,"au":"EAFD","softbank":null,"google":"FEB7C","image":"1f520.png","sheet_x":30,"sheet_y":24,"short_name":"capital_abcd","short_names":["capital_abcd"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1562,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"INPUT SYMBOL FOR LATIN SMALL LETTERS","unified":"1F521","non_qualified":null,"docomo":null,"au":"EAFE","softbank":null,"google":"FEB7D","image":"1f521.png","sheet_x":30,"sheet_y":25,"short_name":"abcd","short_names":["abcd"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1563,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"INPUT SYMBOL FOR NUMBERS","unified":"1F522","non_qualified":null,"docomo":null,"au":"EAFF","softbank":null,"google":"FEB7E","image":"1f522.png","sheet_x":30,"sheet_y":26,"short_name":"1234","short_names":["1234"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1564,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"INPUT SYMBOL FOR SYMBOLS","unified":"1F523","non_qualified":null,"docomo":null,"au":"EB00","softbank":null,"google":"FEB7F","image":"1f523.png","sheet_x":30,"sheet_y":27,"short_name":"symbols","short_names":["symbols"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1565,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"INPUT SYMBOL FOR LATIN LETTERS","unified":"1F524","non_qualified":null,"docomo":null,"au":"EB55","softbank":null,"google":"FEB80","image":"1f524.png","sheet_x":30,"sheet_y":28,"short_name":"abc","short_names":["abc"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1566,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FIRE","unified":"1F525","non_qualified":null,"docomo":null,"au":"E47B","softbank":"E11D","google":"FE4F6","image":"1f525.png","sheet_x":30,"sheet_y":29,"short_name":"fire","short_names":["fire"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1062,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ELECTRIC TORCH","unified":"1F526","non_qualified":null,"docomo":"E6FB","au":"E583","softbank":null,"google":"FE4FB","image":"1f526.png","sheet_x":30,"sheet_y":30,"short_name":"flashlight","short_names":["flashlight"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1259,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WRENCH","unified":"1F527","non_qualified":null,"docomo":"E718","au":"E587","softbank":null,"google":"FE4C9","image":"1f527.png","sheet_x":30,"sheet_y":31,"short_name":"wrench","short_names":["wrench"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1350,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HAMMER","unified":"1F528","non_qualified":null,"docomo":null,"au":"E5CB","softbank":"E116","google":"FE4CA","image":"1f528.png","sheet_x":30,"sheet_y":32,"short_name":"hammer","short_names":["hammer"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1338,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NUT AND BOLT","unified":"1F529","non_qualified":null,"docomo":null,"au":"E581","softbank":null,"google":"FE4CB","image":"1f529.png","sheet_x":30,"sheet_y":33,"short_name":"nut_and_bolt","short_names":["nut_and_bolt"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1352,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOCHO","unified":"1F52A","non_qualified":null,"docomo":null,"au":"E57F","softbank":null,"google":"FE4FA","image":"1f52a.png","sheet_x":30,"sheet_y":34,"short_name":"hocho","short_names":["hocho","knife"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"dishware","sort_order":844,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PISTOL","unified":"1F52B","non_qualified":null,"docomo":null,"au":"E50A","softbank":"E113","google":"FE4F5","image":"1f52b.png","sheet_x":30,"sheet_y":35,"short_name":"gun","short_names":["gun"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1122,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MICROSCOPE","unified":"1F52C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f52c.png","sheet_x":30,"sheet_y":36,"short_name":"microscope","short_names":["microscope"],"text":null,"texts":null,"category":"Objects","subcategory":"science","sort_order":1368,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TELESCOPE","unified":"1F52D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f52d.png","sheet_x":30,"sheet_y":37,"short_name":"telescope","short_names":["telescope"],"text":null,"texts":null,"category":"Objects","subcategory":"science","sort_order":1369,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CRYSTAL BALL","unified":"1F52E","non_qualified":null,"docomo":null,"au":"EA8F","softbank":null,"google":"FE4F7","image":"1f52e.png","sheet_x":30,"sheet_y":38,"short_name":"crystal_ball","short_names":["crystal_ball"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1124,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SIX POINTED STAR WITH MIDDLE DOT","unified":"1F52F","non_qualified":null,"docomo":null,"au":"EA8F","softbank":"E23E","google":"FE4F8","image":"1f52f.png","sheet_x":30,"sheet_y":39,"short_name":"six_pointed_star","short_names":["six_pointed_star"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1470,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JAPANESE SYMBOL FOR BEGINNER","unified":"1F530","non_qualified":null,"docomo":null,"au":"E480","softbank":"E209","google":"FE044","image":"1f530.png","sheet_x":30,"sheet_y":40,"short_name":"beginner","short_names":["beginner"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1533,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRIDENT EMBLEM","unified":"1F531","non_qualified":null,"docomo":"E71A","au":"E5C9","softbank":"E031","google":"FE4D2","image":"1f531.png","sheet_x":30,"sheet_y":41,"short_name":"trident","short_names":["trident"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1531,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK SQUARE BUTTON","unified":"1F532","non_qualified":null,"docomo":"E69C","au":"E54B","softbank":"E21A","google":"FEB64","image":"1f532.png","sheet_x":30,"sheet_y":42,"short_name":"black_square_button","short_names":["black_square_button"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1634,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE SQUARE BUTTON","unified":"1F533","non_qualified":null,"docomo":"E69C","au":"E54B","softbank":"E21B","google":"FEB67","image":"1f533.png","sheet_x":30,"sheet_y":43,"short_name":"white_square_button","short_names":["white_square_button"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1633,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE RED CIRCLE","unified":"1F534","non_qualified":null,"docomo":"E69C","au":"E54A","softbank":"E219","google":"FEB63","image":"1f534.png","sheet_x":30,"sheet_y":44,"short_name":"red_circle","short_names":["red_circle"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1601,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE BLUE CIRCLE","unified":"1F535","non_qualified":null,"docomo":"E69C","au":"E54B","softbank":null,"google":"FEB64","image":"1f535.png","sheet_x":30,"sheet_y":45,"short_name":"large_blue_circle","short_names":["large_blue_circle"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1605,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE ORANGE DIAMOND","unified":"1F536","non_qualified":null,"docomo":null,"au":"E546","softbank":null,"google":"FEB73","image":"1f536.png","sheet_x":30,"sheet_y":46,"short_name":"large_orange_diamond","short_names":["large_orange_diamond"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1625,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE BLUE DIAMOND","unified":"1F537","non_qualified":null,"docomo":null,"au":"E547","softbank":null,"google":"FEB74","image":"1f537.png","sheet_x":30,"sheet_y":47,"short_name":"large_blue_diamond","short_names":["large_blue_diamond"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1626,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMALL ORANGE DIAMOND","unified":"1F538","non_qualified":null,"docomo":null,"au":"E536","softbank":null,"google":"FEB75","image":"1f538.png","sheet_x":30,"sheet_y":48,"short_name":"small_orange_diamond","short_names":["small_orange_diamond"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1627,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMALL BLUE DIAMOND","unified":"1F539","non_qualified":null,"docomo":null,"au":"E537","softbank":null,"google":"FEB76","image":"1f539.png","sheet_x":30,"sheet_y":49,"short_name":"small_blue_diamond","short_names":["small_blue_diamond"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1628,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"UP-POINTING RED TRIANGLE","unified":"1F53A","non_qualified":null,"docomo":null,"au":"E55A","softbank":null,"google":"FEB78","image":"1f53a.png","sheet_x":30,"sheet_y":50,"short_name":"small_red_triangle","short_names":["small_red_triangle"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1629,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOWN-POINTING RED TRIANGLE","unified":"1F53B","non_qualified":null,"docomo":null,"au":"E55B","softbank":null,"google":"FEB79","image":"1f53b.png","sheet_x":30,"sheet_y":51,"short_name":"small_red_triangle_down","short_names":["small_red_triangle_down"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1630,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"UP-POINTING SMALL RED TRIANGLE","unified":"1F53C","non_qualified":null,"docomo":null,"au":"E543","softbank":null,"google":"FEB01","image":"1f53c.png","sheet_x":30,"sheet_y":52,"short_name":"arrow_up_small","short_names":["arrow_up_small"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1495,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOWN-POINTING SMALL RED TRIANGLE","unified":"1F53D","non_qualified":null,"docomo":null,"au":"E542","softbank":null,"google":"FEB00","image":"1f53d.png","sheet_x":30,"sheet_y":53,"short_name":"arrow_down_small","short_names":["arrow_down_small"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1497,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OM","unified":"1F549-FE0F","non_qualified":"1F549","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f549-fe0f.png","sheet_x":30,"sheet_y":54,"short_name":"om_symbol","short_names":["om_symbol"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1461,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOVE","unified":"1F54A-FE0F","non_qualified":"1F54A","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f54a-fe0f.png","sheet_x":30,"sheet_y":55,"short_name":"dove_of_peace","short_names":["dove_of_peace"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":633,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KAABA","unified":"1F54B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f54b.png","sheet_x":30,"sheet_y":56,"short_name":"kaaba","short_names":["kaaba"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-religious","sort_order":895,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOSQUE","unified":"1F54C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f54c.png","sheet_x":30,"sheet_y":57,"short_name":"mosque","short_names":["mosque"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-religious","sort_order":891,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SYNAGOGUE","unified":"1F54D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f54d.png","sheet_x":30,"sheet_y":58,"short_name":"synagogue","short_names":["synagogue"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-religious","sort_order":893,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MENORAH WITH NINE BRANCHES","unified":"1F54E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f54e.png","sheet_x":30,"sheet_y":59,"short_name":"menorah_with_nine_branches","short_names":["menorah_with_nine_branches"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1469,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE ONE OCLOCK","unified":"1F550","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E024","google":"FE01E","image":"1f550.png","sheet_x":30,"sheet_y":60,"short_name":"clock1","short_names":["clock1"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":996,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE TWO OCLOCK","unified":"1F551","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E025","google":"FE01F","image":"1f551.png","sheet_x":30,"sheet_y":61,"short_name":"clock2","short_names":["clock2"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":998,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE THREE OCLOCK","unified":"1F552","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E026","google":"FE020","image":"1f552.png","sheet_x":31,"sheet_y":0,"short_name":"clock3","short_names":["clock3"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1000,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE FOUR OCLOCK","unified":"1F553","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E027","google":"FE021","image":"1f553.png","sheet_x":31,"sheet_y":1,"short_name":"clock4","short_names":["clock4"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1002,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE FIVE OCLOCK","unified":"1F554","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E028","google":"FE022","image":"1f554.png","sheet_x":31,"sheet_y":2,"short_name":"clock5","short_names":["clock5"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1004,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE SIX OCLOCK","unified":"1F555","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E029","google":"FE023","image":"1f555.png","sheet_x":31,"sheet_y":3,"short_name":"clock6","short_names":["clock6"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1006,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE SEVEN OCLOCK","unified":"1F556","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E02A","google":"FE024","image":"1f556.png","sheet_x":31,"sheet_y":4,"short_name":"clock7","short_names":["clock7"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1008,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE EIGHT OCLOCK","unified":"1F557","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E02B","google":"FE025","image":"1f557.png","sheet_x":31,"sheet_y":5,"short_name":"clock8","short_names":["clock8"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1010,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE NINE OCLOCK","unified":"1F558","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E02C","google":"FE026","image":"1f558.png","sheet_x":31,"sheet_y":6,"short_name":"clock9","short_names":["clock9"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1012,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE TEN OCLOCK","unified":"1F559","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E02D","google":"FE027","image":"1f559.png","sheet_x":31,"sheet_y":7,"short_name":"clock10","short_names":["clock10"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1014,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE ELEVEN OCLOCK","unified":"1F55A","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E02E","google":"FE028","image":"1f55a.png","sheet_x":31,"sheet_y":8,"short_name":"clock11","short_names":["clock11"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1016,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE TWELVE OCLOCK","unified":"1F55B","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":"E02F","google":"FE029","image":"1f55b.png","sheet_x":31,"sheet_y":9,"short_name":"clock12","short_names":["clock12"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":994,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE ONE-THIRTY","unified":"1F55C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f55c.png","sheet_x":31,"sheet_y":10,"short_name":"clock130","short_names":["clock130"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":997,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE TWO-THIRTY","unified":"1F55D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f55d.png","sheet_x":31,"sheet_y":11,"short_name":"clock230","short_names":["clock230"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":999,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE THREE-THIRTY","unified":"1F55E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f55e.png","sheet_x":31,"sheet_y":12,"short_name":"clock330","short_names":["clock330"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1001,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE FOUR-THIRTY","unified":"1F55F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f55f.png","sheet_x":31,"sheet_y":13,"short_name":"clock430","short_names":["clock430"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1003,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE FIVE-THIRTY","unified":"1F560","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f560.png","sheet_x":31,"sheet_y":14,"short_name":"clock530","short_names":["clock530"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1005,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE SIX-THIRTY","unified":"1F561","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f561.png","sheet_x":31,"sheet_y":15,"short_name":"clock630","short_names":["clock630"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1007,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE SEVEN-THIRTY","unified":"1F562","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f562.png","sheet_x":31,"sheet_y":16,"short_name":"clock730","short_names":["clock730"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1009,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE EIGHT-THIRTY","unified":"1F563","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f563.png","sheet_x":31,"sheet_y":17,"short_name":"clock830","short_names":["clock830"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1011,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE NINE-THIRTY","unified":"1F564","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f564.png","sheet_x":31,"sheet_y":18,"short_name":"clock930","short_names":["clock930"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1013,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE TEN-THIRTY","unified":"1F565","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f565.png","sheet_x":31,"sheet_y":19,"short_name":"clock1030","short_names":["clock1030"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1015,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE ELEVEN-THIRTY","unified":"1F566","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f566.png","sheet_x":31,"sheet_y":20,"short_name":"clock1130","short_names":["clock1130"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":1017,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOCK FACE TWELVE-THIRTY","unified":"1F567","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f567.png","sheet_x":31,"sheet_y":21,"short_name":"clock1230","short_names":["clock1230"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":995,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CANDLE","unified":"1F56F-FE0F","non_qualified":"1F56F","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f56f-fe0f.png","sheet_x":31,"sheet_y":22,"short_name":"candle","short_names":["candle"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1257,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MANTELPIECE CLOCK","unified":"1F570-FE0F","non_qualified":"1F570","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f570-fe0f.png","sheet_x":31,"sheet_y":23,"short_name":"mantelpiece_clock","short_names":["mantelpiece_clock"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":993,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOLE","unified":"1F573-FE0F","non_qualified":"1F573","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f573-fe0f.png","sheet_x":31,"sheet_y":24,"short_name":"hole","short_names":["hole"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":162,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PERSON IN SUIT LEVITATING","unified":"1F574-FE0F","non_qualified":"1F574","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f574-fe0f.png","sheet_x":31,"sheet_y":25,"short_name":"man_in_business_suit_levitating","short_names":["man_in_business_suit_levitating"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":449,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F574-1F3FB","non_qualified":null,"image":"1f574-1f3fb.png","sheet_x":31,"sheet_y":26,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F574-1F3FC","non_qualified":null,"image":"1f574-1f3fc.png","sheet_x":31,"sheet_y":27,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F574-1F3FD","non_qualified":null,"image":"1f574-1f3fd.png","sheet_x":31,"sheet_y":28,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F574-1F3FE","non_qualified":null,"image":"1f574-1f3fe.png","sheet_x":31,"sheet_y":29,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F574-1F3FF","non_qualified":null,"image":"1f574-1f3ff.png","sheet_x":31,"sheet_y":30,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN DETECTIVE","unified":"1F575-FE0F-200D-2640-FE0F","non_qualified":"1F575-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f575-fe0f-200d-2640-fe0f.png","sheet_x":31,"sheet_y":31,"short_name":"female-detective","short_names":["female-detective"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":341,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F575-1F3FB-200D-2640-FE0F","non_qualified":"1F575-1F3FB-200D-2640","image":"1f575-1f3fb-200d-2640-fe0f.png","sheet_x":31,"sheet_y":32,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F575-1F3FC-200D-2640-FE0F","non_qualified":"1F575-1F3FC-200D-2640","image":"1f575-1f3fc-200d-2640-fe0f.png","sheet_x":31,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F575-1F3FD-200D-2640-FE0F","non_qualified":"1F575-1F3FD-200D-2640","image":"1f575-1f3fd-200d-2640-fe0f.png","sheet_x":31,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F575-1F3FE-200D-2640-FE0F","non_qualified":"1F575-1F3FE-200D-2640","image":"1f575-1f3fe-200d-2640-fe0f.png","sheet_x":31,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F575-1F3FF-200D-2640-FE0F","non_qualified":"1F575-1F3FF-200D-2640","image":"1f575-1f3ff-200d-2640-fe0f.png","sheet_x":31,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN DETECTIVE","unified":"1F575-FE0F-200D-2642-FE0F","non_qualified":"1F575-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f575-fe0f-200d-2642-fe0f.png","sheet_x":31,"sheet_y":37,"short_name":"male-detective","short_names":["male-detective"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":340,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F575-1F3FB-200D-2642-FE0F","non_qualified":"1F575-1F3FB-200D-2642","image":"1f575-1f3fb-200d-2642-fe0f.png","sheet_x":31,"sheet_y":38,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F575-1F3FC-200D-2642-FE0F","non_qualified":"1F575-1F3FC-200D-2642","image":"1f575-1f3fc-200d-2642-fe0f.png","sheet_x":31,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F575-1F3FD-200D-2642-FE0F","non_qualified":"1F575-1F3FD-200D-2642","image":"1f575-1f3fd-200d-2642-fe0f.png","sheet_x":31,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F575-1F3FE-200D-2642-FE0F","non_qualified":"1F575-1F3FE-200D-2642","image":"1f575-1f3fe-200d-2642-fe0f.png","sheet_x":31,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F575-1F3FF-200D-2642-FE0F","non_qualified":"1F575-1F3FF-200D-2642","image":"1f575-1f3ff-200d-2642-fe0f.png","sheet_x":31,"sheet_y":42,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F575-FE0F"},{"name":"DETECTIVE","unified":"1F575-FE0F","non_qualified":"1F575","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f575-fe0f.png","sheet_x":31,"sheet_y":43,"short_name":"sleuth_or_spy","short_names":["sleuth_or_spy"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":339,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F575-1F3FB","non_qualified":null,"image":"1f575-1f3fb.png","sheet_x":31,"sheet_y":44,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F575-1F3FC","non_qualified":null,"image":"1f575-1f3fc.png","sheet_x":31,"sheet_y":45,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F575-1F3FD","non_qualified":null,"image":"1f575-1f3fd.png","sheet_x":31,"sheet_y":46,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F575-1F3FE","non_qualified":null,"image":"1f575-1f3fe.png","sheet_x":31,"sheet_y":47,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F575-1F3FF","non_qualified":null,"image":"1f575-1f3ff.png","sheet_x":31,"sheet_y":48,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F575-FE0F-200D-2642-FE0F"},{"name":"SUNGLASSES","unified":"1F576-FE0F","non_qualified":"1F576","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f576-fe0f.png","sheet_x":31,"sheet_y":49,"short_name":"dark_sunglasses","short_names":["dark_sunglasses"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1151,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPIDER","unified":"1F577-FE0F","non_qualified":"1F577","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f577-fe0f.png","sheet_x":31,"sheet_y":50,"short_name":"spider","short_names":["spider"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":677,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPIDER WEB","unified":"1F578-FE0F","non_qualified":"1F578","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f578-fe0f.png","sheet_x":31,"sheet_y":51,"short_name":"spider_web","short_names":["spider_web"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":678,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JOYSTICK","unified":"1F579-FE0F","non_qualified":"1F579","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f579-fe0f.png","sheet_x":31,"sheet_y":52,"short_name":"joystick","short_names":["joystick"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1127,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MAN DANCING","unified":"1F57A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f57a.png","sheet_x":31,"sheet_y":53,"short_name":"man_dancing","short_names":["man_dancing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":448,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F57A-1F3FB","non_qualified":null,"image":"1f57a-1f3fb.png","sheet_x":31,"sheet_y":54,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F57A-1F3FC","non_qualified":null,"image":"1f57a-1f3fc.png","sheet_x":31,"sheet_y":55,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F57A-1F3FD","non_qualified":null,"image":"1f57a-1f3fd.png","sheet_x":31,"sheet_y":56,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F57A-1F3FE","non_qualified":null,"image":"1f57a-1f3fe.png","sheet_x":31,"sheet_y":57,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F57A-1F3FF","non_qualified":null,"image":"1f57a-1f3ff.png","sheet_x":31,"sheet_y":58,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"LINKED PAPERCLIPS","unified":"1F587-FE0F","non_qualified":"1F587","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f587-fe0f.png","sheet_x":31,"sheet_y":59,"short_name":"linked_paperclips","short_names":["linked_paperclips"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1325,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PEN","unified":"1F58A-FE0F","non_qualified":"1F58A","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f58a-fe0f.png","sheet_x":31,"sheet_y":60,"short_name":"lower_left_ballpoint_pen","short_names":["lower_left_ballpoint_pen"],"text":null,"texts":null,"category":"Objects","subcategory":"writing","sort_order":1305,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FOUNTAIN PEN","unified":"1F58B-FE0F","non_qualified":"1F58B","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f58b-fe0f.png","sheet_x":31,"sheet_y":61,"short_name":"lower_left_fountain_pen","short_names":["lower_left_fountain_pen"],"text":null,"texts":null,"category":"Objects","subcategory":"writing","sort_order":1304,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PAINTBRUSH","unified":"1F58C-FE0F","non_qualified":"1F58C","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f58c-fe0f.png","sheet_x":32,"sheet_y":0,"short_name":"lower_left_paintbrush","short_names":["lower_left_paintbrush"],"text":null,"texts":null,"category":"Objects","subcategory":"writing","sort_order":1306,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CRAYON","unified":"1F58D-FE0F","non_qualified":"1F58D","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f58d-fe0f.png","sheet_x":32,"sheet_y":1,"short_name":"lower_left_crayon","short_names":["lower_left_crayon"],"text":null,"texts":null,"category":"Objects","subcategory":"writing","sort_order":1307,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HAND WITH FINGERS SPLAYED","unified":"1F590-FE0F","non_qualified":"1F590","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f590-fe0f.png","sheet_x":32,"sheet_y":2,"short_name":"raised_hand_with_fingers_splayed","short_names":["raised_hand_with_fingers_splayed"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":171,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F590-1F3FB","non_qualified":null,"image":"1f590-1f3fb.png","sheet_x":32,"sheet_y":3,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F590-1F3FC","non_qualified":null,"image":"1f590-1f3fc.png","sheet_x":32,"sheet_y":4,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F590-1F3FD","non_qualified":null,"image":"1f590-1f3fd.png","sheet_x":32,"sheet_y":5,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F590-1F3FE","non_qualified":null,"image":"1f590-1f3fe.png","sheet_x":32,"sheet_y":6,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F590-1F3FF","non_qualified":null,"image":"1f590-1f3ff.png","sheet_x":32,"sheet_y":7,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"REVERSED HAND WITH MIDDLE FINGER EXTENDED","unified":"1F595","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f595.png","sheet_x":32,"sheet_y":8,"short_name":"middle_finger","short_names":["middle_finger","reversed_hand_with_middle_finger_extended"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-single-finger","sort_order":192,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F595-1F3FB","non_qualified":null,"image":"1f595-1f3fb.png","sheet_x":32,"sheet_y":9,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F595-1F3FC","non_qualified":null,"image":"1f595-1f3fc.png","sheet_x":32,"sheet_y":10,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F595-1F3FD","non_qualified":null,"image":"1f595-1f3fd.png","sheet_x":32,"sheet_y":11,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F595-1F3FE","non_qualified":null,"image":"1f595-1f3fe.png","sheet_x":32,"sheet_y":12,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F595-1F3FF","non_qualified":null,"image":"1f595-1f3ff.png","sheet_x":32,"sheet_y":13,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS","unified":"1F596","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f596.png","sheet_x":32,"sheet_y":14,"short_name":"spock-hand","short_names":["spock-hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":173,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F596-1F3FB","non_qualified":null,"image":"1f596-1f3fb.png","sheet_x":32,"sheet_y":15,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F596-1F3FC","non_qualified":null,"image":"1f596-1f3fc.png","sheet_x":32,"sheet_y":16,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F596-1F3FD","non_qualified":null,"image":"1f596-1f3fd.png","sheet_x":32,"sheet_y":17,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F596-1F3FE","non_qualified":null,"image":"1f596-1f3fe.png","sheet_x":32,"sheet_y":18,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F596-1F3FF","non_qualified":null,"image":"1f596-1f3ff.png","sheet_x":32,"sheet_y":19,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"BLACK HEART","unified":"1F5A4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5a4.png","sheet_x":32,"sheet_y":20,"short_name":"black_heart","short_names":["black_heart"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":152,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DESKTOP COMPUTER","unified":"1F5A5-FE0F","non_qualified":"1F5A5","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5a5-fe0f.png","sheet_x":32,"sheet_y":21,"short_name":"desktop_computer","short_names":["desktop_computer"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1236,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PRINTER","unified":"1F5A8-FE0F","non_qualified":"1F5A8","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5a8-fe0f.png","sheet_x":32,"sheet_y":22,"short_name":"printer","short_names":["printer"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1237,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COMPUTER MOUSE","unified":"1F5B1-FE0F","non_qualified":"1F5B1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5b1-fe0f.png","sheet_x":32,"sheet_y":23,"short_name":"three_button_mouse","short_names":["three_button_mouse"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1239,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRACKBALL","unified":"1F5B2-FE0F","non_qualified":"1F5B2","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5b2-fe0f.png","sheet_x":32,"sheet_y":24,"short_name":"trackball","short_names":["trackball"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1240,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FRAMED PICTURE","unified":"1F5BC-FE0F","non_qualified":"1F5BC","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5bc-fe0f.png","sheet_x":32,"sheet_y":25,"short_name":"frame_with_picture","short_names":["frame_with_picture"],"text":null,"texts":null,"category":"Activities","subcategory":"arts & crafts","sort_order":1144,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CARD INDEX DIVIDERS","unified":"1F5C2-FE0F","non_qualified":"1F5C2","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5c2-fe0f.png","sheet_x":32,"sheet_y":26,"short_name":"card_index_dividers","short_names":["card_index_dividers"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1312,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CARD FILE BOX","unified":"1F5C3-FE0F","non_qualified":"1F5C3","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5c3-fe0f.png","sheet_x":32,"sheet_y":27,"short_name":"card_file_box","short_names":["card_file_box"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1329,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FILE CABINET","unified":"1F5C4-FE0F","non_qualified":"1F5C4","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5c4-fe0f.png","sheet_x":32,"sheet_y":28,"short_name":"file_cabinet","short_names":["file_cabinet"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1330,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WASTEBASKET","unified":"1F5D1-FE0F","non_qualified":"1F5D1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5d1-fe0f.png","sheet_x":32,"sheet_y":29,"short_name":"wastebasket","short_names":["wastebasket"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1331,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPIRAL NOTEPAD","unified":"1F5D2-FE0F","non_qualified":"1F5D2","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5d2-fe0f.png","sheet_x":32,"sheet_y":30,"short_name":"spiral_note_pad","short_names":["spiral_note_pad"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1315,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPIRAL CALENDAR","unified":"1F5D3-FE0F","non_qualified":"1F5D3","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5d3-fe0f.png","sheet_x":32,"sheet_y":31,"short_name":"spiral_calendar_pad","short_names":["spiral_calendar_pad"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1316,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLAMP","unified":"1F5DC-FE0F","non_qualified":"1F5DC","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5dc-fe0f.png","sheet_x":32,"sheet_y":32,"short_name":"compression","short_names":["compression"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1354,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OLD KEY","unified":"1F5DD-FE0F","non_qualified":"1F5DD","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5dd-fe0f.png","sheet_x":32,"sheet_y":33,"short_name":"old_key","short_names":["old_key"],"text":null,"texts":null,"category":"Objects","subcategory":"lock","sort_order":1337,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROLLED-UP NEWSPAPER","unified":"1F5DE-FE0F","non_qualified":"1F5DE","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5de-fe0f.png","sheet_x":32,"sheet_y":34,"short_name":"rolled_up_newspaper","short_names":["rolled_up_newspaper"],"text":null,"texts":null,"category":"Objects","subcategory":"book-paper","sort_order":1275,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DAGGER","unified":"1F5E1-FE0F","non_qualified":"1F5E1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5e1-fe0f.png","sheet_x":32,"sheet_y":35,"short_name":"dagger_knife","short_names":["dagger_knife"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1343,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPEAKING HEAD","unified":"1F5E3-FE0F","non_qualified":"1F5E3","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5e3-fe0f.png","sheet_x":32,"sheet_y":36,"short_name":"speaking_head_in_silhouette","short_names":["speaking_head_in_silhouette"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-symbol","sort_order":544,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEFT SPEECH BUBBLE","unified":"1F5E8-FE0F","non_qualified":"1F5E8","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5e8-fe0f.png","sheet_x":32,"sheet_y":37,"short_name":"left_speech_bubble","short_names":["left_speech_bubble"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":165,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RIGHT ANGER BUBBLE","unified":"1F5EF-FE0F","non_qualified":"1F5EF","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5ef-fe0f.png","sheet_x":32,"sheet_y":38,"short_name":"right_anger_bubble","short_names":["right_anger_bubble"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"emotion","sort_order":166,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BALLOT BOX WITH BALLOT","unified":"1F5F3-FE0F","non_qualified":"1F5F3","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5f3-fe0f.png","sheet_x":32,"sheet_y":39,"short_name":"ballot_box_with_ballot","short_names":["ballot_box_with_ballot"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1301,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WORLD MAP","unified":"1F5FA-FE0F","non_qualified":"1F5FA","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5fa-fe0f.png","sheet_x":32,"sheet_y":40,"short_name":"world_map","short_names":["world_map"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-map","sort_order":851,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOUNT FUJI","unified":"1F5FB","non_qualified":null,"docomo":"E740","au":"E5BD","softbank":"E03B","google":"FE4C3","image":"1f5fb.png","sheet_x":32,"sheet_y":41,"short_name":"mount_fuji","short_names":["mount_fuji"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-geographic","sort_order":857,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TOKYO TOWER","unified":"1F5FC","non_qualified":null,"docomo":null,"au":"E4C0","softbank":"E509","google":"FE4C4","image":"1f5fc.png","sheet_x":32,"sheet_y":42,"short_name":"tokyo_tower","short_names":["tokyo_tower"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":888,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STATUE OF LIBERTY","unified":"1F5FD","non_qualified":null,"docomo":null,"au":null,"softbank":"E51D","google":"FE4C6","image":"1f5fd.png","sheet_x":32,"sheet_y":43,"short_name":"statue_of_liberty","short_names":["statue_of_liberty"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":889,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SILHOUETTE OF JAPAN","unified":"1F5FE","non_qualified":null,"docomo":null,"au":"E572","softbank":null,"google":"FE4C7","image":"1f5fe.png","sheet_x":32,"sheet_y":44,"short_name":"japan","short_names":["japan"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-map","sort_order":852,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOYAI","unified":"1F5FF","non_qualified":null,"docomo":null,"au":"EB6C","softbank":null,"google":"FE4C8","image":"1f5ff.png","sheet_x":32,"sheet_y":45,"short_name":"moyai","short_names":["moyai"],"text":null,"texts":null,"category":"Objects","subcategory":"other-object","sort_order":1409,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GRINNING FACE","unified":"1F600","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f600.png","sheet_x":32,"sheet_y":46,"short_name":"grinning","short_names":["grinning"],"text":":D","texts":null,"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":1,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GRINNING FACE WITH SMILING EYES","unified":"1F601","non_qualified":null,"docomo":"E753","au":"EB80","softbank":"E404","google":"FE333","image":"1f601.png","sheet_x":32,"sheet_y":47,"short_name":"grin","short_names":["grin"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":4,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH TEARS OF JOY","unified":"1F602","non_qualified":null,"docomo":"E72A","au":"EB64","softbank":"E412","google":"FE334","image":"1f602.png","sheet_x":32,"sheet_y":48,"short_name":"joy","short_names":["joy"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":8,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH OPEN MOUTH","unified":"1F603","non_qualified":null,"docomo":"E6F0","au":"E471","softbank":"E057","google":"FE330","image":"1f603.png","sheet_x":32,"sheet_y":49,"short_name":"smiley","short_names":["smiley"],"text":":)","texts":["=)","=-)"],"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":2,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH OPEN MOUTH AND SMILING EYES","unified":"1F604","non_qualified":null,"docomo":"E6F0","au":"E471","softbank":"E415","google":"FE338","image":"1f604.png","sheet_x":32,"sheet_y":50,"short_name":"smile","short_names":["smile"],"text":":)","texts":["C:","c:",":D",":-D"],"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":3,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH OPEN MOUTH AND COLD SWEAT","unified":"1F605","non_qualified":null,"docomo":"E722","au":"E471-E5B1","softbank":null,"google":"FE331","image":"1f605.png","sheet_x":32,"sheet_y":51,"short_name":"sweat_smile","short_names":["sweat_smile"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":6,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES","unified":"1F606","non_qualified":null,"docomo":"E72A","au":"EAC5","softbank":null,"google":"FE332","image":"1f606.png","sheet_x":32,"sheet_y":52,"short_name":"laughing","short_names":["laughing","satisfied"],"text":null,"texts":[":>",":->"],"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":5,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH HALO","unified":"1F607","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f607.png","sheet_x":32,"sheet_y":53,"short_name":"innocent","short_names":["innocent"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":14,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH HORNS","unified":"1F608","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f608.png","sheet_x":32,"sheet_y":54,"short_name":"smiling_imp","short_names":["smiling_imp"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-negative","sort_order":106,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WINKING FACE","unified":"1F609","non_qualified":null,"docomo":"E729","au":"E5C3","softbank":"E405","google":"FE347","image":"1f609.png","sheet_x":32,"sheet_y":55,"short_name":"wink","short_names":["wink"],"text":";)","texts":[";)",";-)"],"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":12,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH SMILING EYES","unified":"1F60A","non_qualified":null,"docomo":"E6F0","au":"EACD","softbank":"E056","google":"FE335","image":"1f60a.png","sheet_x":32,"sheet_y":56,"short_name":"blush","short_names":["blush"],"text":":)","texts":null,"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":13,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE SAVOURING DELICIOUS FOOD","unified":"1F60B","non_qualified":null,"docomo":"E752","au":"EACD","softbank":null,"google":"FE32B","image":"1f60b.png","sheet_x":32,"sheet_y":57,"short_name":"yum","short_names":["yum"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-tongue","sort_order":24,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RELIEVED FACE","unified":"1F60C","non_qualified":null,"docomo":"E721","au":"EAC5","softbank":"E40A","google":"FE33E","image":"1f60c.png","sheet_x":32,"sheet_y":58,"short_name":"relieved","short_names":["relieved"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-sleepy","sort_order":53,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH HEART-SHAPED EYES","unified":"1F60D","non_qualified":null,"docomo":"E726","au":"E5C4","softbank":"E106","google":"FE327","image":"1f60d.png","sheet_x":32,"sheet_y":59,"short_name":"heart_eyes","short_names":["heart_eyes"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-affection","sort_order":16,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH SUNGLASSES","unified":"1F60E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f60e.png","sheet_x":32,"sheet_y":60,"short_name":"sunglasses","short_names":["sunglasses"],"text":null,"texts":["8)"],"category":"Smileys & Emotion","subcategory":"face-glasses","sort_order":73,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMIRKING FACE","unified":"1F60F","non_qualified":null,"docomo":"E72C","au":"EABF","softbank":"E402","google":"FE343","image":"1f60f.png","sheet_x":32,"sheet_y":61,"short_name":"smirk","short_names":["smirk"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":44,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEUTRAL FACE","unified":"1F610","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f610.png","sheet_x":33,"sheet_y":0,"short_name":"neutral_face","short_names":["neutral_face"],"text":null,"texts":[":|",":-|"],"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":39,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EXPRESSIONLESS FACE","unified":"1F611","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f611.png","sheet_x":33,"sheet_y":1,"short_name":"expressionless","short_names":["expressionless"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":40,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"UNAMUSED FACE","unified":"1F612","non_qualified":null,"docomo":"E725","au":"EAC9","softbank":"E40E","google":"FE326","image":"1f612.png","sheet_x":33,"sheet_y":2,"short_name":"unamused","short_names":["unamused"],"text":":(","texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":45,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH COLD SWEAT","unified":"1F613","non_qualified":null,"docomo":"E723","au":"E5C6","softbank":"E108","google":"FE344","image":"1f613.png","sheet_x":33,"sheet_y":3,"short_name":"sweat","short_names":["sweat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":98,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PENSIVE FACE","unified":"1F614","non_qualified":null,"docomo":"E720","au":"EAC0","softbank":"E403","google":"FE340","image":"1f614.png","sheet_x":33,"sheet_y":4,"short_name":"pensive","short_names":["pensive"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-sleepy","sort_order":54,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CONFUSED FACE","unified":"1F615","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f615.png","sheet_x":33,"sheet_y":5,"short_name":"confused","short_names":["confused"],"text":null,"texts":[":\\",":-\\",":\/",":-\/"],"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":76,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CONFOUNDED FACE","unified":"1F616","non_qualified":null,"docomo":"E6F3","au":"EAC3","softbank":"E407","google":"FE33F","image":"1f616.png","sheet_x":33,"sheet_y":6,"short_name":"confounded","short_names":["confounded"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":95,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KISSING FACE","unified":"1F617","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f617.png","sheet_x":33,"sheet_y":7,"short_name":"kissing","short_names":["kissing"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-affection","sort_order":19,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE THROWING A KISS","unified":"1F618","non_qualified":null,"docomo":"E726","au":"EACF","softbank":"E418","google":"FE32C","image":"1f618.png","sheet_x":33,"sheet_y":8,"short_name":"kissing_heart","short_names":["kissing_heart"],"text":null,"texts":[":*",":-*"],"category":"Smileys & Emotion","subcategory":"face-affection","sort_order":18,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KISSING FACE WITH SMILING EYES","unified":"1F619","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f619.png","sheet_x":33,"sheet_y":9,"short_name":"kissing_smiling_eyes","short_names":["kissing_smiling_eyes"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-affection","sort_order":22,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KISSING FACE WITH CLOSED EYES","unified":"1F61A","non_qualified":null,"docomo":"E726","au":"EACE","softbank":"E417","google":"FE32D","image":"1f61a.png","sheet_x":33,"sheet_y":10,"short_name":"kissing_closed_eyes","short_names":["kissing_closed_eyes"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-affection","sort_order":21,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH STUCK-OUT TONGUE","unified":"1F61B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f61b.png","sheet_x":33,"sheet_y":11,"short_name":"stuck_out_tongue","short_names":["stuck_out_tongue"],"text":":p","texts":[":p",":-p",":P",":-P",":b",":-b"],"category":"Smileys & Emotion","subcategory":"face-tongue","sort_order":25,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH STUCK-OUT TONGUE AND WINKING EYE","unified":"1F61C","non_qualified":null,"docomo":"E728","au":"E4E7","softbank":"E105","google":"FE329","image":"1f61c.png","sheet_x":33,"sheet_y":12,"short_name":"stuck_out_tongue_winking_eye","short_names":["stuck_out_tongue_winking_eye"],"text":";p","texts":[";p",";-p",";b",";-b",";P",";-P"],"category":"Smileys & Emotion","subcategory":"face-tongue","sort_order":26,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES","unified":"1F61D","non_qualified":null,"docomo":"E728","au":"E4E7","softbank":"E409","google":"FE32A","image":"1f61d.png","sheet_x":33,"sheet_y":13,"short_name":"stuck_out_tongue_closed_eyes","short_names":["stuck_out_tongue_closed_eyes"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-tongue","sort_order":28,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DISAPPOINTED FACE","unified":"1F61E","non_qualified":null,"docomo":"E6F2","au":"EAC0","softbank":"E058","google":"FE323","image":"1f61e.png","sheet_x":33,"sheet_y":14,"short_name":"disappointed","short_names":["disappointed"],"text":":(","texts":["):",":(",":-("],"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":97,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WORRIED FACE","unified":"1F61F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f61f.png","sheet_x":33,"sheet_y":15,"short_name":"worried","short_names":["worried"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":78,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ANGRY FACE","unified":"1F620","non_qualified":null,"docomo":"E6F1","au":"E472","softbank":"E059","google":"FE320","image":"1f620.png","sheet_x":33,"sheet_y":16,"short_name":"angry","short_names":["angry"],"text":null,"texts":[">:(",">:-("],"category":"Smileys & Emotion","subcategory":"face-negative","sort_order":104,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POUTING FACE","unified":"1F621","non_qualified":null,"docomo":"E724","au":"EB5D","softbank":"E416","google":"FE33D","image":"1f621.png","sheet_x":33,"sheet_y":17,"short_name":"rage","short_names":["rage"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-negative","sort_order":103,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CRYING FACE","unified":"1F622","non_qualified":null,"docomo":"E72E","au":"EB69","softbank":"E413","google":"FE339","image":"1f622.png","sheet_x":33,"sheet_y":18,"short_name":"cry","short_names":["cry"],"text":":'(","texts":[":'("],"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":92,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PERSEVERING FACE","unified":"1F623","non_qualified":null,"docomo":"E72B","au":"EAC2","softbank":"E406","google":"FE33C","image":"1f623.png","sheet_x":33,"sheet_y":19,"short_name":"persevere","short_names":["persevere"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":96,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH LOOK OF TRIUMPH","unified":"1F624","non_qualified":null,"docomo":"E753","au":"EAC1","softbank":null,"google":"FE328","image":"1f624.png","sheet_x":33,"sheet_y":20,"short_name":"triumph","short_names":["triumph"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-negative","sort_order":102,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DISAPPOINTED BUT RELIEVED FACE","unified":"1F625","non_qualified":null,"docomo":"E723","au":"E5C6","softbank":"E401","google":"FE345","image":"1f625.png","sheet_x":33,"sheet_y":21,"short_name":"disappointed_relieved","short_names":["disappointed_relieved"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":91,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FROWNING FACE WITH OPEN MOUTH","unified":"1F626","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f626.png","sheet_x":33,"sheet_y":22,"short_name":"frowning","short_names":["frowning"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":87,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ANGUISHED FACE","unified":"1F627","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f627.png","sheet_x":33,"sheet_y":23,"short_name":"anguished","short_names":["anguished"],"text":null,"texts":["D:"],"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":88,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FEARFUL FACE","unified":"1F628","non_qualified":null,"docomo":"E757","au":"EAC6","softbank":"E40B","google":"FE33B","image":"1f628.png","sheet_x":33,"sheet_y":24,"short_name":"fearful","short_names":["fearful"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":89,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WEARY FACE","unified":"1F629","non_qualified":null,"docomo":"E6F3","au":"EB67","softbank":null,"google":"FE321","image":"1f629.png","sheet_x":33,"sheet_y":25,"short_name":"weary","short_names":["weary"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":99,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SLEEPY FACE","unified":"1F62A","non_qualified":null,"docomo":"E701","au":"EAC4","softbank":"E408","google":"FE342","image":"1f62a.png","sheet_x":33,"sheet_y":26,"short_name":"sleepy","short_names":["sleepy"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-sleepy","sort_order":55,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TIRED FACE","unified":"1F62B","non_qualified":null,"docomo":"E72B","au":"E474","softbank":null,"google":"FE346","image":"1f62b.png","sheet_x":33,"sheet_y":27,"short_name":"tired_face","short_names":["tired_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":100,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GRIMACING FACE","unified":"1F62C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f62c.png","sheet_x":33,"sheet_y":28,"short_name":"grimacing","short_names":["grimacing"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":47,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOUDLY CRYING FACE","unified":"1F62D","non_qualified":null,"docomo":"E72D","au":"E473","softbank":"E411","google":"FE33A","image":"1f62d.png","sheet_x":33,"sheet_y":29,"short_name":"sob","short_names":["sob"],"text":":'(","texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":93,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE EXHALING","unified":"1F62E-200D-1F4A8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f62e-200d-1f4a8.png","sheet_x":33,"sheet_y":30,"short_name":"face_exhaling","short_names":["face_exhaling"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":48,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH OPEN MOUTH","unified":"1F62E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f62e.png","sheet_x":33,"sheet_y":31,"short_name":"open_mouth","short_names":["open_mouth"],"text":null,"texts":[":o",":-o",":O",":-O"],"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":81,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HUSHED FACE","unified":"1F62F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f62f.png","sheet_x":33,"sheet_y":32,"short_name":"hushed","short_names":["hushed"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":82,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH OPEN MOUTH AND COLD SWEAT","unified":"1F630","non_qualified":null,"docomo":"E723","au":"EACB","softbank":"E40F","google":"FE325","image":"1f630.png","sheet_x":33,"sheet_y":33,"short_name":"cold_sweat","short_names":["cold_sweat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":90,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE SCREAMING IN FEAR","unified":"1F631","non_qualified":null,"docomo":"E757","au":"E5C5","softbank":"E107","google":"FE341","image":"1f631.png","sheet_x":33,"sheet_y":34,"short_name":"scream","short_names":["scream"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":94,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ASTONISHED FACE","unified":"1F632","non_qualified":null,"docomo":"E6F4","au":"EACA","softbank":"E410","google":"FE322","image":"1f632.png","sheet_x":33,"sheet_y":35,"short_name":"astonished","short_names":["astonished"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":83,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLUSHED FACE","unified":"1F633","non_qualified":null,"docomo":"E72A","au":"EAC8","softbank":"E40D","google":"FE32F","image":"1f633.png","sheet_x":33,"sheet_y":36,"short_name":"flushed","short_names":["flushed"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":84,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SLEEPING FACE","unified":"1F634","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f634.png","sheet_x":33,"sheet_y":37,"short_name":"sleeping","short_names":["sleeping"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-sleepy","sort_order":57,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH SPIRAL EYES","unified":"1F635-200D-1F4AB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f635-200d-1f4ab.png","sheet_x":33,"sheet_y":38,"short_name":"face_with_spiral_eyes","short_names":["face_with_spiral_eyes"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":68,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DIZZY FACE","unified":"1F635","non_qualified":null,"docomo":"E6F4","au":"E5AE","softbank":null,"google":"FE324","image":"1f635.png","sheet_x":33,"sheet_y":39,"short_name":"dizzy_face","short_names":["dizzy_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":67,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE IN CLOUDS","unified":"1F636-200D-1F32B-FE0F","non_qualified":"1F636-200D-1F32B","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f636-200d-1f32b-fe0f.png","sheet_x":33,"sheet_y":40,"short_name":"face_in_clouds","short_names":["face_in_clouds"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":43,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITHOUT MOUTH","unified":"1F636","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f636.png","sheet_x":33,"sheet_y":41,"short_name":"no_mouth","short_names":["no_mouth"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":41,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH MEDICAL MASK","unified":"1F637","non_qualified":null,"docomo":null,"au":"EAC7","softbank":"E40C","google":"FE32E","image":"1f637.png","sheet_x":33,"sheet_y":42,"short_name":"mask","short_names":["mask"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":58,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GRINNING CAT FACE WITH SMILING EYES","unified":"1F638","non_qualified":null,"docomo":"E753","au":"EB7F","softbank":null,"google":"FE349","image":"1f638.png","sheet_x":33,"sheet_y":43,"short_name":"smile_cat","short_names":["smile_cat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"cat-face","sort_order":119,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CAT FACE WITH TEARS OF JOY","unified":"1F639","non_qualified":null,"docomo":"E72A","au":"EB63","softbank":null,"google":"FE34A","image":"1f639.png","sheet_x":33,"sheet_y":44,"short_name":"joy_cat","short_names":["joy_cat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"cat-face","sort_order":120,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING CAT FACE WITH OPEN MOUTH","unified":"1F63A","non_qualified":null,"docomo":"E6F0","au":"EB61","softbank":null,"google":"FE348","image":"1f63a.png","sheet_x":33,"sheet_y":45,"short_name":"smiley_cat","short_names":["smiley_cat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"cat-face","sort_order":118,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING CAT FACE WITH HEART-SHAPED EYES","unified":"1F63B","non_qualified":null,"docomo":"E726","au":"EB65","softbank":null,"google":"FE34C","image":"1f63b.png","sheet_x":33,"sheet_y":46,"short_name":"heart_eyes_cat","short_names":["heart_eyes_cat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"cat-face","sort_order":121,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CAT FACE WITH WRY SMILE","unified":"1F63C","non_qualified":null,"docomo":"E753","au":"EB6A","softbank":null,"google":"FE34F","image":"1f63c.png","sheet_x":33,"sheet_y":47,"short_name":"smirk_cat","short_names":["smirk_cat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"cat-face","sort_order":122,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KISSING CAT FACE WITH CLOSED EYES","unified":"1F63D","non_qualified":null,"docomo":"E726","au":"EB60","softbank":null,"google":"FE34B","image":"1f63d.png","sheet_x":33,"sheet_y":48,"short_name":"kissing_cat","short_names":["kissing_cat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"cat-face","sort_order":123,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POUTING CAT FACE","unified":"1F63E","non_qualified":null,"docomo":"E724","au":"EB5E","softbank":null,"google":"FE34E","image":"1f63e.png","sheet_x":33,"sheet_y":49,"short_name":"pouting_cat","short_names":["pouting_cat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"cat-face","sort_order":126,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CRYING CAT FACE","unified":"1F63F","non_qualified":null,"docomo":"E72E","au":"EB68","softbank":null,"google":"FE34D","image":"1f63f.png","sheet_x":33,"sheet_y":50,"short_name":"crying_cat_face","short_names":["crying_cat_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"cat-face","sort_order":125,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WEARY CAT FACE","unified":"1F640","non_qualified":null,"docomo":"E6F3","au":"EB66","softbank":null,"google":"FE350","image":"1f640.png","sheet_x":33,"sheet_y":51,"short_name":"scream_cat","short_names":["scream_cat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"cat-face","sort_order":124,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SLIGHTLY FROWNING FACE","unified":"1F641","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f641.png","sheet_x":33,"sheet_y":52,"short_name":"slightly_frowning_face","short_names":["slightly_frowning_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":79,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAD SHAKING HORIZONTALLY","unified":"1F642-200D-2194-FE0F","non_qualified":"1F642-200D-2194","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f642-200d-2194-fe0f.png","sheet_x":33,"sheet_y":53,"short_name":"head_shaking_horizontally","short_names":["head_shaking_horizontally"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":51,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},{"name":"HEAD SHAKING VERTICALLY","unified":"1F642-200D-2195-FE0F","non_qualified":"1F642-200D-2195","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f642-200d-2195-fe0f.png","sheet_x":33,"sheet_y":54,"short_name":"head_shaking_vertically","short_names":["head_shaking_vertically"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":52,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},{"name":"SLIGHTLY SMILING FACE","unified":"1F642","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f642.png","sheet_x":33,"sheet_y":55,"short_name":"slightly_smiling_face","short_names":["slightly_smiling_face"],"text":null,"texts":[":)","(:",":-)"],"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":9,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"UPSIDE-DOWN FACE","unified":"1F643","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f643.png","sheet_x":33,"sheet_y":56,"short_name":"upside_down_face","short_names":["upside_down_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":10,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH ROLLING EYES","unified":"1F644","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f644.png","sheet_x":33,"sheet_y":57,"short_name":"face_with_rolling_eyes","short_names":["face_with_rolling_eyes"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":46,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN GESTURING NO","unified":"1F645-200D-2640-FE0F","non_qualified":"1F645-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f645-200d-2640-fe0f.png","sheet_x":33,"sheet_y":58,"short_name":"woman-gesturing-no","short_names":["woman-gesturing-no"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":266,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F645-1F3FB-200D-2640-FE0F","non_qualified":"1F645-1F3FB-200D-2640","image":"1f645-1f3fb-200d-2640-fe0f.png","sheet_x":33,"sheet_y":59,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F645-1F3FC-200D-2640-FE0F","non_qualified":"1F645-1F3FC-200D-2640","image":"1f645-1f3fc-200d-2640-fe0f.png","sheet_x":33,"sheet_y":60,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F645-1F3FD-200D-2640-FE0F","non_qualified":"1F645-1F3FD-200D-2640","image":"1f645-1f3fd-200d-2640-fe0f.png","sheet_x":33,"sheet_y":61,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F645-1F3FE-200D-2640-FE0F","non_qualified":"1F645-1F3FE-200D-2640","image":"1f645-1f3fe-200d-2640-fe0f.png","sheet_x":34,"sheet_y":0,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F645-1F3FF-200D-2640-FE0F","non_qualified":"1F645-1F3FF-200D-2640","image":"1f645-1f3ff-200d-2640-fe0f.png","sheet_x":34,"sheet_y":1,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F645"},{"name":"MAN GESTURING NO","unified":"1F645-200D-2642-FE0F","non_qualified":"1F645-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f645-200d-2642-fe0f.png","sheet_x":34,"sheet_y":2,"short_name":"man-gesturing-no","short_names":["man-gesturing-no"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":265,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F645-1F3FB-200D-2642-FE0F","non_qualified":"1F645-1F3FB-200D-2642","image":"1f645-1f3fb-200d-2642-fe0f.png","sheet_x":34,"sheet_y":3,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F645-1F3FC-200D-2642-FE0F","non_qualified":"1F645-1F3FC-200D-2642","image":"1f645-1f3fc-200d-2642-fe0f.png","sheet_x":34,"sheet_y":4,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F645-1F3FD-200D-2642-FE0F","non_qualified":"1F645-1F3FD-200D-2642","image":"1f645-1f3fd-200d-2642-fe0f.png","sheet_x":34,"sheet_y":5,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F645-1F3FE-200D-2642-FE0F","non_qualified":"1F645-1F3FE-200D-2642","image":"1f645-1f3fe-200d-2642-fe0f.png","sheet_x":34,"sheet_y":6,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F645-1F3FF-200D-2642-FE0F","non_qualified":"1F645-1F3FF-200D-2642","image":"1f645-1f3ff-200d-2642-fe0f.png","sheet_x":34,"sheet_y":7,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FACE WITH NO GOOD GESTURE","unified":"1F645","non_qualified":null,"docomo":"E72F","au":"EAD7","softbank":"E423","google":"FE351","image":"1f645.png","sheet_x":34,"sheet_y":8,"short_name":"no_good","short_names":["no_good"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":264,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F645-1F3FB","non_qualified":null,"image":"1f645-1f3fb.png","sheet_x":34,"sheet_y":9,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F645-1F3FC","non_qualified":null,"image":"1f645-1f3fc.png","sheet_x":34,"sheet_y":10,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F645-1F3FD","non_qualified":null,"image":"1f645-1f3fd.png","sheet_x":34,"sheet_y":11,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F645-1F3FE","non_qualified":null,"image":"1f645-1f3fe.png","sheet_x":34,"sheet_y":12,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F645-1F3FF","non_qualified":null,"image":"1f645-1f3ff.png","sheet_x":34,"sheet_y":13,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F645-200D-2640-FE0F"},{"name":"WOMAN GESTURING OK","unified":"1F646-200D-2640-FE0F","non_qualified":"1F646-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f646-200d-2640-fe0f.png","sheet_x":34,"sheet_y":14,"short_name":"woman-gesturing-ok","short_names":["woman-gesturing-ok"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":269,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F646-1F3FB-200D-2640-FE0F","non_qualified":"1F646-1F3FB-200D-2640","image":"1f646-1f3fb-200d-2640-fe0f.png","sheet_x":34,"sheet_y":15,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F646-1F3FC-200D-2640-FE0F","non_qualified":"1F646-1F3FC-200D-2640","image":"1f646-1f3fc-200d-2640-fe0f.png","sheet_x":34,"sheet_y":16,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F646-1F3FD-200D-2640-FE0F","non_qualified":"1F646-1F3FD-200D-2640","image":"1f646-1f3fd-200d-2640-fe0f.png","sheet_x":34,"sheet_y":17,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F646-1F3FE-200D-2640-FE0F","non_qualified":"1F646-1F3FE-200D-2640","image":"1f646-1f3fe-200d-2640-fe0f.png","sheet_x":34,"sheet_y":18,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F646-1F3FF-200D-2640-FE0F","non_qualified":"1F646-1F3FF-200D-2640","image":"1f646-1f3ff-200d-2640-fe0f.png","sheet_x":34,"sheet_y":19,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F646"},{"name":"MAN GESTURING OK","unified":"1F646-200D-2642-FE0F","non_qualified":"1F646-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f646-200d-2642-fe0f.png","sheet_x":34,"sheet_y":20,"short_name":"man-gesturing-ok","short_names":["man-gesturing-ok"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":268,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F646-1F3FB-200D-2642-FE0F","non_qualified":"1F646-1F3FB-200D-2642","image":"1f646-1f3fb-200d-2642-fe0f.png","sheet_x":34,"sheet_y":21,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F646-1F3FC-200D-2642-FE0F","non_qualified":"1F646-1F3FC-200D-2642","image":"1f646-1f3fc-200d-2642-fe0f.png","sheet_x":34,"sheet_y":22,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F646-1F3FD-200D-2642-FE0F","non_qualified":"1F646-1F3FD-200D-2642","image":"1f646-1f3fd-200d-2642-fe0f.png","sheet_x":34,"sheet_y":23,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F646-1F3FE-200D-2642-FE0F","non_qualified":"1F646-1F3FE-200D-2642","image":"1f646-1f3fe-200d-2642-fe0f.png","sheet_x":34,"sheet_y":24,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F646-1F3FF-200D-2642-FE0F","non_qualified":"1F646-1F3FF-200D-2642","image":"1f646-1f3ff-200d-2642-fe0f.png","sheet_x":34,"sheet_y":25,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FACE WITH OK GESTURE","unified":"1F646","non_qualified":null,"docomo":"E70B","au":"EAD8","softbank":"E424","google":"FE352","image":"1f646.png","sheet_x":34,"sheet_y":26,"short_name":"ok_woman","short_names":["ok_woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":267,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F646-1F3FB","non_qualified":null,"image":"1f646-1f3fb.png","sheet_x":34,"sheet_y":27,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F646-1F3FC","non_qualified":null,"image":"1f646-1f3fc.png","sheet_x":34,"sheet_y":28,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F646-1F3FD","non_qualified":null,"image":"1f646-1f3fd.png","sheet_x":34,"sheet_y":29,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F646-1F3FE","non_qualified":null,"image":"1f646-1f3fe.png","sheet_x":34,"sheet_y":30,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F646-1F3FF","non_qualified":null,"image":"1f646-1f3ff.png","sheet_x":34,"sheet_y":31,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F646-200D-2640-FE0F"},{"name":"WOMAN BOWING","unified":"1F647-200D-2640-FE0F","non_qualified":"1F647-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f647-200d-2640-fe0f.png","sheet_x":34,"sheet_y":32,"short_name":"woman-bowing","short_names":["woman-bowing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":281,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F647-1F3FB-200D-2640-FE0F","non_qualified":"1F647-1F3FB-200D-2640","image":"1f647-1f3fb-200d-2640-fe0f.png","sheet_x":34,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F647-1F3FC-200D-2640-FE0F","non_qualified":"1F647-1F3FC-200D-2640","image":"1f647-1f3fc-200d-2640-fe0f.png","sheet_x":34,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F647-1F3FD-200D-2640-FE0F","non_qualified":"1F647-1F3FD-200D-2640","image":"1f647-1f3fd-200d-2640-fe0f.png","sheet_x":34,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F647-1F3FE-200D-2640-FE0F","non_qualified":"1F647-1F3FE-200D-2640","image":"1f647-1f3fe-200d-2640-fe0f.png","sheet_x":34,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F647-1F3FF-200D-2640-FE0F","non_qualified":"1F647-1F3FF-200D-2640","image":"1f647-1f3ff-200d-2640-fe0f.png","sheet_x":34,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN BOWING","unified":"1F647-200D-2642-FE0F","non_qualified":"1F647-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f647-200d-2642-fe0f.png","sheet_x":34,"sheet_y":38,"short_name":"man-bowing","short_names":["man-bowing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":280,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F647-1F3FB-200D-2642-FE0F","non_qualified":"1F647-1F3FB-200D-2642","image":"1f647-1f3fb-200d-2642-fe0f.png","sheet_x":34,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F647-1F3FC-200D-2642-FE0F","non_qualified":"1F647-1F3FC-200D-2642","image":"1f647-1f3fc-200d-2642-fe0f.png","sheet_x":34,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F647-1F3FD-200D-2642-FE0F","non_qualified":"1F647-1F3FD-200D-2642","image":"1f647-1f3fd-200d-2642-fe0f.png","sheet_x":34,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F647-1F3FE-200D-2642-FE0F","non_qualified":"1F647-1F3FE-200D-2642","image":"1f647-1f3fe-200d-2642-fe0f.png","sheet_x":34,"sheet_y":42,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F647-1F3FF-200D-2642-FE0F","non_qualified":"1F647-1F3FF-200D-2642","image":"1f647-1f3ff-200d-2642-fe0f.png","sheet_x":34,"sheet_y":43,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON BOWING DEEPLY","unified":"1F647","non_qualified":null,"docomo":null,"au":"EAD9","softbank":"E426","google":"FE353","image":"1f647.png","sheet_x":34,"sheet_y":44,"short_name":"bow","short_names":["bow"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":279,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F647-1F3FB","non_qualified":null,"image":"1f647-1f3fb.png","sheet_x":34,"sheet_y":45,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F647-1F3FC","non_qualified":null,"image":"1f647-1f3fc.png","sheet_x":34,"sheet_y":46,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F647-1F3FD","non_qualified":null,"image":"1f647-1f3fd.png","sheet_x":34,"sheet_y":47,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F647-1F3FE","non_qualified":null,"image":"1f647-1f3fe.png","sheet_x":34,"sheet_y":48,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F647-1F3FF","non_qualified":null,"image":"1f647-1f3ff.png","sheet_x":34,"sheet_y":49,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SEE-NO-EVIL MONKEY","unified":"1F648","non_qualified":null,"docomo":null,"au":"EB50","softbank":null,"google":"FE354","image":"1f648.png","sheet_x":34,"sheet_y":50,"short_name":"see_no_evil","short_names":["see_no_evil"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"monkey-face","sort_order":127,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAR-NO-EVIL MONKEY","unified":"1F649","non_qualified":null,"docomo":null,"au":"EB52","softbank":null,"google":"FE356","image":"1f649.png","sheet_x":34,"sheet_y":51,"short_name":"hear_no_evil","short_names":["hear_no_evil"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"monkey-face","sort_order":128,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPEAK-NO-EVIL MONKEY","unified":"1F64A","non_qualified":null,"docomo":null,"au":"EB51","softbank":null,"google":"FE355","image":"1f64a.png","sheet_x":34,"sheet_y":52,"short_name":"speak_no_evil","short_names":["speak_no_evil"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"monkey-face","sort_order":129,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN RAISING HAND","unified":"1F64B-200D-2640-FE0F","non_qualified":"1F64B-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f64b-200d-2640-fe0f.png","sheet_x":34,"sheet_y":53,"short_name":"woman-raising-hand","short_names":["woman-raising-hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":275,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64B-1F3FB-200D-2640-FE0F","non_qualified":"1F64B-1F3FB-200D-2640","image":"1f64b-1f3fb-200d-2640-fe0f.png","sheet_x":34,"sheet_y":54,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64B-1F3FC-200D-2640-FE0F","non_qualified":"1F64B-1F3FC-200D-2640","image":"1f64b-1f3fc-200d-2640-fe0f.png","sheet_x":34,"sheet_y":55,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64B-1F3FD-200D-2640-FE0F","non_qualified":"1F64B-1F3FD-200D-2640","image":"1f64b-1f3fd-200d-2640-fe0f.png","sheet_x":34,"sheet_y":56,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64B-1F3FE-200D-2640-FE0F","non_qualified":"1F64B-1F3FE-200D-2640","image":"1f64b-1f3fe-200d-2640-fe0f.png","sheet_x":34,"sheet_y":57,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64B-1F3FF-200D-2640-FE0F","non_qualified":"1F64B-1F3FF-200D-2640","image":"1f64b-1f3ff-200d-2640-fe0f.png","sheet_x":34,"sheet_y":58,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F64B"},{"name":"MAN RAISING HAND","unified":"1F64B-200D-2642-FE0F","non_qualified":"1F64B-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f64b-200d-2642-fe0f.png","sheet_x":34,"sheet_y":59,"short_name":"man-raising-hand","short_names":["man-raising-hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":274,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64B-1F3FB-200D-2642-FE0F","non_qualified":"1F64B-1F3FB-200D-2642","image":"1f64b-1f3fb-200d-2642-fe0f.png","sheet_x":34,"sheet_y":60,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64B-1F3FC-200D-2642-FE0F","non_qualified":"1F64B-1F3FC-200D-2642","image":"1f64b-1f3fc-200d-2642-fe0f.png","sheet_x":34,"sheet_y":61,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64B-1F3FD-200D-2642-FE0F","non_qualified":"1F64B-1F3FD-200D-2642","image":"1f64b-1f3fd-200d-2642-fe0f.png","sheet_x":35,"sheet_y":0,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64B-1F3FE-200D-2642-FE0F","non_qualified":"1F64B-1F3FE-200D-2642","image":"1f64b-1f3fe-200d-2642-fe0f.png","sheet_x":35,"sheet_y":1,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64B-1F3FF-200D-2642-FE0F","non_qualified":"1F64B-1F3FF-200D-2642","image":"1f64b-1f3ff-200d-2642-fe0f.png","sheet_x":35,"sheet_y":2,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"HAPPY PERSON RAISING ONE HAND","unified":"1F64B","non_qualified":null,"docomo":null,"au":"EB85","softbank":null,"google":"FE357","image":"1f64b.png","sheet_x":35,"sheet_y":3,"short_name":"raising_hand","short_names":["raising_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":273,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64B-1F3FB","non_qualified":null,"image":"1f64b-1f3fb.png","sheet_x":35,"sheet_y":4,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64B-1F3FC","non_qualified":null,"image":"1f64b-1f3fc.png","sheet_x":35,"sheet_y":5,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64B-1F3FD","non_qualified":null,"image":"1f64b-1f3fd.png","sheet_x":35,"sheet_y":6,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64B-1F3FE","non_qualified":null,"image":"1f64b-1f3fe.png","sheet_x":35,"sheet_y":7,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64B-1F3FF","non_qualified":null,"image":"1f64b-1f3ff.png","sheet_x":35,"sheet_y":8,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F64B-200D-2640-FE0F"},{"name":"PERSON RAISING BOTH HANDS IN CELEBRATION","unified":"1F64C","non_qualified":null,"docomo":null,"au":"EB86","softbank":"E427","google":"FE358","image":"1f64c.png","sheet_x":35,"sheet_y":9,"short_name":"raised_hands","short_names":["raised_hands"],"text":null,"texts":null,"category":"People & Body","subcategory":"hands","sort_order":203,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64C-1F3FB","non_qualified":null,"image":"1f64c-1f3fb.png","sheet_x":35,"sheet_y":10,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64C-1F3FC","non_qualified":null,"image":"1f64c-1f3fc.png","sheet_x":35,"sheet_y":11,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64C-1F3FD","non_qualified":null,"image":"1f64c-1f3fd.png","sheet_x":35,"sheet_y":12,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64C-1F3FE","non_qualified":null,"image":"1f64c-1f3fe.png","sheet_x":35,"sheet_y":13,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64C-1F3FF","non_qualified":null,"image":"1f64c-1f3ff.png","sheet_x":35,"sheet_y":14,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN FROWNING","unified":"1F64D-200D-2640-FE0F","non_qualified":"1F64D-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f64d-200d-2640-fe0f.png","sheet_x":35,"sheet_y":15,"short_name":"woman-frowning","short_names":["woman-frowning"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":260,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64D-1F3FB-200D-2640-FE0F","non_qualified":"1F64D-1F3FB-200D-2640","image":"1f64d-1f3fb-200d-2640-fe0f.png","sheet_x":35,"sheet_y":16,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64D-1F3FC-200D-2640-FE0F","non_qualified":"1F64D-1F3FC-200D-2640","image":"1f64d-1f3fc-200d-2640-fe0f.png","sheet_x":35,"sheet_y":17,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64D-1F3FD-200D-2640-FE0F","non_qualified":"1F64D-1F3FD-200D-2640","image":"1f64d-1f3fd-200d-2640-fe0f.png","sheet_x":35,"sheet_y":18,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64D-1F3FE-200D-2640-FE0F","non_qualified":"1F64D-1F3FE-200D-2640","image":"1f64d-1f3fe-200d-2640-fe0f.png","sheet_x":35,"sheet_y":19,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64D-1F3FF-200D-2640-FE0F","non_qualified":"1F64D-1F3FF-200D-2640","image":"1f64d-1f3ff-200d-2640-fe0f.png","sheet_x":35,"sheet_y":20,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F64D"},{"name":"MAN FROWNING","unified":"1F64D-200D-2642-FE0F","non_qualified":"1F64D-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f64d-200d-2642-fe0f.png","sheet_x":35,"sheet_y":21,"short_name":"man-frowning","short_names":["man-frowning"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":259,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64D-1F3FB-200D-2642-FE0F","non_qualified":"1F64D-1F3FB-200D-2642","image":"1f64d-1f3fb-200d-2642-fe0f.png","sheet_x":35,"sheet_y":22,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64D-1F3FC-200D-2642-FE0F","non_qualified":"1F64D-1F3FC-200D-2642","image":"1f64d-1f3fc-200d-2642-fe0f.png","sheet_x":35,"sheet_y":23,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64D-1F3FD-200D-2642-FE0F","non_qualified":"1F64D-1F3FD-200D-2642","image":"1f64d-1f3fd-200d-2642-fe0f.png","sheet_x":35,"sheet_y":24,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64D-1F3FE-200D-2642-FE0F","non_qualified":"1F64D-1F3FE-200D-2642","image":"1f64d-1f3fe-200d-2642-fe0f.png","sheet_x":35,"sheet_y":25,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64D-1F3FF-200D-2642-FE0F","non_qualified":"1F64D-1F3FF-200D-2642","image":"1f64d-1f3ff-200d-2642-fe0f.png","sheet_x":35,"sheet_y":26,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON FROWNING","unified":"1F64D","non_qualified":null,"docomo":"E6F3","au":"EB87","softbank":null,"google":"FE359","image":"1f64d.png","sheet_x":35,"sheet_y":27,"short_name":"person_frowning","short_names":["person_frowning"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":258,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64D-1F3FB","non_qualified":null,"image":"1f64d-1f3fb.png","sheet_x":35,"sheet_y":28,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64D-1F3FC","non_qualified":null,"image":"1f64d-1f3fc.png","sheet_x":35,"sheet_y":29,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64D-1F3FD","non_qualified":null,"image":"1f64d-1f3fd.png","sheet_x":35,"sheet_y":30,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64D-1F3FE","non_qualified":null,"image":"1f64d-1f3fe.png","sheet_x":35,"sheet_y":31,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64D-1F3FF","non_qualified":null,"image":"1f64d-1f3ff.png","sheet_x":35,"sheet_y":32,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F64D-200D-2640-FE0F"},{"name":"WOMAN POUTING","unified":"1F64E-200D-2640-FE0F","non_qualified":"1F64E-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f64e-200d-2640-fe0f.png","sheet_x":35,"sheet_y":33,"short_name":"woman-pouting","short_names":["woman-pouting"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":263,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64E-1F3FB-200D-2640-FE0F","non_qualified":"1F64E-1F3FB-200D-2640","image":"1f64e-1f3fb-200d-2640-fe0f.png","sheet_x":35,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64E-1F3FC-200D-2640-FE0F","non_qualified":"1F64E-1F3FC-200D-2640","image":"1f64e-1f3fc-200d-2640-fe0f.png","sheet_x":35,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64E-1F3FD-200D-2640-FE0F","non_qualified":"1F64E-1F3FD-200D-2640","image":"1f64e-1f3fd-200d-2640-fe0f.png","sheet_x":35,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64E-1F3FE-200D-2640-FE0F","non_qualified":"1F64E-1F3FE-200D-2640","image":"1f64e-1f3fe-200d-2640-fe0f.png","sheet_x":35,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64E-1F3FF-200D-2640-FE0F","non_qualified":"1F64E-1F3FF-200D-2640","image":"1f64e-1f3ff-200d-2640-fe0f.png","sheet_x":35,"sheet_y":38,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F64E"},{"name":"MAN POUTING","unified":"1F64E-200D-2642-FE0F","non_qualified":"1F64E-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f64e-200d-2642-fe0f.png","sheet_x":35,"sheet_y":39,"short_name":"man-pouting","short_names":["man-pouting"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":262,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64E-1F3FB-200D-2642-FE0F","non_qualified":"1F64E-1F3FB-200D-2642","image":"1f64e-1f3fb-200d-2642-fe0f.png","sheet_x":35,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64E-1F3FC-200D-2642-FE0F","non_qualified":"1F64E-1F3FC-200D-2642","image":"1f64e-1f3fc-200d-2642-fe0f.png","sheet_x":35,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64E-1F3FD-200D-2642-FE0F","non_qualified":"1F64E-1F3FD-200D-2642","image":"1f64e-1f3fd-200d-2642-fe0f.png","sheet_x":35,"sheet_y":42,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64E-1F3FE-200D-2642-FE0F","non_qualified":"1F64E-1F3FE-200D-2642","image":"1f64e-1f3fe-200d-2642-fe0f.png","sheet_x":35,"sheet_y":43,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64E-1F3FF-200D-2642-FE0F","non_qualified":"1F64E-1F3FF-200D-2642","image":"1f64e-1f3ff-200d-2642-fe0f.png","sheet_x":35,"sheet_y":44,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON WITH POUTING FACE","unified":"1F64E","non_qualified":null,"docomo":"E6F1","au":"EB88","softbank":null,"google":"FE35A","image":"1f64e.png","sheet_x":35,"sheet_y":45,"short_name":"person_with_pouting_face","short_names":["person_with_pouting_face"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":261,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64E-1F3FB","non_qualified":null,"image":"1f64e-1f3fb.png","sheet_x":35,"sheet_y":46,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64E-1F3FC","non_qualified":null,"image":"1f64e-1f3fc.png","sheet_x":35,"sheet_y":47,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64E-1F3FD","non_qualified":null,"image":"1f64e-1f3fd.png","sheet_x":35,"sheet_y":48,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64E-1F3FE","non_qualified":null,"image":"1f64e-1f3fe.png","sheet_x":35,"sheet_y":49,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64E-1F3FF","non_qualified":null,"image":"1f64e-1f3ff.png","sheet_x":35,"sheet_y":50,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F64E-200D-2640-FE0F"},{"name":"PERSON WITH FOLDED HANDS","unified":"1F64F","non_qualified":null,"docomo":null,"au":"EAD2","softbank":"E41D","google":"FE35B","image":"1f64f.png","sheet_x":35,"sheet_y":51,"short_name":"pray","short_names":["pray"],"text":null,"texts":null,"category":"People & Body","subcategory":"hands","sort_order":208,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F64F-1F3FB","non_qualified":null,"image":"1f64f-1f3fb.png","sheet_x":35,"sheet_y":52,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F64F-1F3FC","non_qualified":null,"image":"1f64f-1f3fc.png","sheet_x":35,"sheet_y":53,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F64F-1F3FD","non_qualified":null,"image":"1f64f-1f3fd.png","sheet_x":35,"sheet_y":54,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F64F-1F3FE","non_qualified":null,"image":"1f64f-1f3fe.png","sheet_x":35,"sheet_y":55,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F64F-1F3FF","non_qualified":null,"image":"1f64f-1f3ff.png","sheet_x":35,"sheet_y":56,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"ROCKET","unified":"1F680","non_qualified":null,"docomo":null,"au":"E5C8","softbank":"E10D","google":"FE7ED","image":"1f680.png","sheet_x":35,"sheet_y":57,"short_name":"rocket","short_names":["rocket"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":983,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HELICOPTER","unified":"1F681","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f681.png","sheet_x":35,"sheet_y":58,"short_name":"helicopter","short_names":["helicopter"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":978,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STEAM LOCOMOTIVE","unified":"1F682","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f682.png","sheet_x":35,"sheet_y":59,"short_name":"steam_locomotive","short_names":["steam_locomotive"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":913,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RAILWAY CAR","unified":"1F683","non_qualified":null,"docomo":"E65B","au":"E4B5","softbank":"E01E","google":"FE7DF","image":"1f683.png","sheet_x":35,"sheet_y":60,"short_name":"railway_car","short_names":["railway_car"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":914,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HIGH-SPEED TRAIN","unified":"1F684","non_qualified":null,"docomo":"E65D","au":"E4B0","softbank":"E435","google":"FE7E2","image":"1f684.png","sheet_x":35,"sheet_y":61,"short_name":"bullettrain_side","short_names":["bullettrain_side"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":915,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HIGH-SPEED TRAIN WITH BULLET NOSE","unified":"1F685","non_qualified":null,"docomo":"E65D","au":"E4B0","softbank":"E01F","google":"FE7E3","image":"1f685.png","sheet_x":36,"sheet_y":0,"short_name":"bullettrain_front","short_names":["bullettrain_front"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":916,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRAIN","unified":"1F686","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f686.png","sheet_x":36,"sheet_y":1,"short_name":"train2","short_names":["train2"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":917,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"METRO","unified":"1F687","non_qualified":null,"docomo":"E65C","au":"E5BC","softbank":"E434","google":"FE7E0","image":"1f687.png","sheet_x":36,"sheet_y":2,"short_name":"metro","short_names":["metro"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":918,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LIGHT RAIL","unified":"1F688","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f688.png","sheet_x":36,"sheet_y":3,"short_name":"light_rail","short_names":["light_rail"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":919,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STATION","unified":"1F689","non_qualified":null,"docomo":null,"au":"EB6D","softbank":"E039","google":"FE7EC","image":"1f689.png","sheet_x":36,"sheet_y":4,"short_name":"station","short_names":["station"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":920,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRAM","unified":"1F68A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f68a.png","sheet_x":36,"sheet_y":5,"short_name":"tram","short_names":["tram"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":921,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRAM CAR","unified":"1F68B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f68b.png","sheet_x":36,"sheet_y":6,"short_name":"train","short_names":["train"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":924,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUS","unified":"1F68C","non_qualified":null,"docomo":"E660","au":"E4AF","softbank":"E159","google":"FE7E6","image":"1f68c.png","sheet_x":36,"sheet_y":7,"short_name":"bus","short_names":["bus"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":925,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ONCOMING BUS","unified":"1F68D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f68d.png","sheet_x":36,"sheet_y":8,"short_name":"oncoming_bus","short_names":["oncoming_bus"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":926,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TROLLEYBUS","unified":"1F68E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f68e.png","sheet_x":36,"sheet_y":9,"short_name":"trolleybus","short_names":["trolleybus"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":927,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUS STOP","unified":"1F68F","non_qualified":null,"docomo":null,"au":"E4A7","softbank":"E150","google":"FE7E7","image":"1f68f.png","sheet_x":36,"sheet_y":10,"short_name":"busstop","short_names":["busstop"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":952,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MINIBUS","unified":"1F690","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f690.png","sheet_x":36,"sheet_y":11,"short_name":"minibus","short_names":["minibus"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":928,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AMBULANCE","unified":"1F691","non_qualified":null,"docomo":null,"au":"EAE0","softbank":"E431","google":"FE7F3","image":"1f691.png","sheet_x":36,"sheet_y":12,"short_name":"ambulance","short_names":["ambulance"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":929,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FIRE ENGINE","unified":"1F692","non_qualified":null,"docomo":null,"au":"EADF","softbank":"E430","google":"FE7F2","image":"1f692.png","sheet_x":36,"sheet_y":13,"short_name":"fire_engine","short_names":["fire_engine"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":930,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POLICE CAR","unified":"1F693","non_qualified":null,"docomo":null,"au":"EAE1","softbank":"E432","google":"FE7F4","image":"1f693.png","sheet_x":36,"sheet_y":14,"short_name":"police_car","short_names":["police_car"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":931,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ONCOMING POLICE CAR","unified":"1F694","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f694.png","sheet_x":36,"sheet_y":15,"short_name":"oncoming_police_car","short_names":["oncoming_police_car"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":932,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TAXI","unified":"1F695","non_qualified":null,"docomo":"E65E","au":"E4B1","softbank":"E15A","google":"FE7EF","image":"1f695.png","sheet_x":36,"sheet_y":16,"short_name":"taxi","short_names":["taxi"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":933,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ONCOMING TAXI","unified":"1F696","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f696.png","sheet_x":36,"sheet_y":17,"short_name":"oncoming_taxi","short_names":["oncoming_taxi"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":934,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AUTOMOBILE","unified":"1F697","non_qualified":null,"docomo":"E65E","au":"E4B1","softbank":"E01B","google":"FE7E4","image":"1f697.png","sheet_x":36,"sheet_y":18,"short_name":"car","short_names":["car","red_car"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":935,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ONCOMING AUTOMOBILE","unified":"1F698","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f698.png","sheet_x":36,"sheet_y":19,"short_name":"oncoming_automobile","short_names":["oncoming_automobile"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":936,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RECREATIONAL VEHICLE","unified":"1F699","non_qualified":null,"docomo":"E65F","au":"E4B1","softbank":"E42E","google":"FE7E5","image":"1f699.png","sheet_x":36,"sheet_y":20,"short_name":"blue_car","short_names":["blue_car"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":937,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DELIVERY TRUCK","unified":"1F69A","non_qualified":null,"docomo":null,"au":"E4B2","softbank":"E42F","google":"FE7F1","image":"1f69a.png","sheet_x":36,"sheet_y":21,"short_name":"truck","short_names":["truck"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":939,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ARTICULATED LORRY","unified":"1F69B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f69b.png","sheet_x":36,"sheet_y":22,"short_name":"articulated_lorry","short_names":["articulated_lorry"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":940,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRACTOR","unified":"1F69C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f69c.png","sheet_x":36,"sheet_y":23,"short_name":"tractor","short_names":["tractor"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":941,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MONORAIL","unified":"1F69D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f69d.png","sheet_x":36,"sheet_y":24,"short_name":"monorail","short_names":["monorail"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":922,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOUNTAIN RAILWAY","unified":"1F69E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f69e.png","sheet_x":36,"sheet_y":25,"short_name":"mountain_railway","short_names":["mountain_railway"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":923,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUSPENSION RAILWAY","unified":"1F69F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f69f.png","sheet_x":36,"sheet_y":26,"short_name":"suspension_railway","short_names":["suspension_railway"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":979,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOUNTAIN CABLEWAY","unified":"1F6A0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6a0.png","sheet_x":36,"sheet_y":27,"short_name":"mountain_cableway","short_names":["mountain_cableway"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":980,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AERIAL TRAMWAY","unified":"1F6A1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6a1.png","sheet_x":36,"sheet_y":28,"short_name":"aerial_tramway","short_names":["aerial_tramway"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":981,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHIP","unified":"1F6A2","non_qualified":null,"docomo":"E661","au":"EA82","softbank":"E202","google":"FE7E8","image":"1f6a2.png","sheet_x":36,"sheet_y":29,"short_name":"ship","short_names":["ship"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-water","sort_order":971,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN ROWING BOAT","unified":"1F6A3-200D-2640-FE0F","non_qualified":"1F6A3-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6a3-200d-2640-fe0f.png","sheet_x":36,"sheet_y":30,"short_name":"woman-rowing-boat","short_names":["woman-rowing-boat"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":471,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6A3-1F3FB-200D-2640-FE0F","non_qualified":"1F6A3-1F3FB-200D-2640","image":"1f6a3-1f3fb-200d-2640-fe0f.png","sheet_x":36,"sheet_y":31,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6A3-1F3FC-200D-2640-FE0F","non_qualified":"1F6A3-1F3FC-200D-2640","image":"1f6a3-1f3fc-200d-2640-fe0f.png","sheet_x":36,"sheet_y":32,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6A3-1F3FD-200D-2640-FE0F","non_qualified":"1F6A3-1F3FD-200D-2640","image":"1f6a3-1f3fd-200d-2640-fe0f.png","sheet_x":36,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6A3-1F3FE-200D-2640-FE0F","non_qualified":"1F6A3-1F3FE-200D-2640","image":"1f6a3-1f3fe-200d-2640-fe0f.png","sheet_x":36,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6A3-1F3FF-200D-2640-FE0F","non_qualified":"1F6A3-1F3FF-200D-2640","image":"1f6a3-1f3ff-200d-2640-fe0f.png","sheet_x":36,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN ROWING BOAT","unified":"1F6A3-200D-2642-FE0F","non_qualified":"1F6A3-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6a3-200d-2642-fe0f.png","sheet_x":36,"sheet_y":36,"short_name":"man-rowing-boat","short_names":["man-rowing-boat"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":470,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6A3-1F3FB-200D-2642-FE0F","non_qualified":"1F6A3-1F3FB-200D-2642","image":"1f6a3-1f3fb-200d-2642-fe0f.png","sheet_x":36,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6A3-1F3FC-200D-2642-FE0F","non_qualified":"1F6A3-1F3FC-200D-2642","image":"1f6a3-1f3fc-200d-2642-fe0f.png","sheet_x":36,"sheet_y":38,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6A3-1F3FD-200D-2642-FE0F","non_qualified":"1F6A3-1F3FD-200D-2642","image":"1f6a3-1f3fd-200d-2642-fe0f.png","sheet_x":36,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6A3-1F3FE-200D-2642-FE0F","non_qualified":"1F6A3-1F3FE-200D-2642","image":"1f6a3-1f3fe-200d-2642-fe0f.png","sheet_x":36,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6A3-1F3FF-200D-2642-FE0F","non_qualified":"1F6A3-1F3FF-200D-2642","image":"1f6a3-1f3ff-200d-2642-fe0f.png","sheet_x":36,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F6A3"},{"name":"ROWBOAT","unified":"1F6A3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6a3.png","sheet_x":36,"sheet_y":42,"short_name":"rowboat","short_names":["rowboat"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":469,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6A3-1F3FB","non_qualified":null,"image":"1f6a3-1f3fb.png","sheet_x":36,"sheet_y":43,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6A3-1F3FC","non_qualified":null,"image":"1f6a3-1f3fc.png","sheet_x":36,"sheet_y":44,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6A3-1F3FD","non_qualified":null,"image":"1f6a3-1f3fd.png","sheet_x":36,"sheet_y":45,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6A3-1F3FE","non_qualified":null,"image":"1f6a3-1f3fe.png","sheet_x":36,"sheet_y":46,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6A3-1F3FF","non_qualified":null,"image":"1f6a3-1f3ff.png","sheet_x":36,"sheet_y":47,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F6A3-200D-2642-FE0F"},{"name":"SPEEDBOAT","unified":"1F6A4","non_qualified":null,"docomo":"E6A3","au":"E4B4","softbank":"E135","google":"FE7EE","image":"1f6a4.png","sheet_x":36,"sheet_y":48,"short_name":"speedboat","short_names":["speedboat"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-water","sort_order":967,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HORIZONTAL TRAFFIC LIGHT","unified":"1F6A5","non_qualified":null,"docomo":"E66D","au":"E46A","softbank":"E14E","google":"FE7F7","image":"1f6a5.png","sheet_x":36,"sheet_y":49,"short_name":"traffic_light","short_names":["traffic_light"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":959,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"VERTICAL TRAFFIC LIGHT","unified":"1F6A6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6a6.png","sheet_x":36,"sheet_y":50,"short_name":"vertical_traffic_light","short_names":["vertical_traffic_light"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":960,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CONSTRUCTION SIGN","unified":"1F6A7","non_qualified":null,"docomo":null,"au":"E5D7","softbank":"E137","google":"FE7F8","image":"1f6a7.png","sheet_x":36,"sheet_y":51,"short_name":"construction","short_names":["construction"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":962,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POLICE CARS REVOLVING LIGHT","unified":"1F6A8","non_qualified":null,"docomo":null,"au":"EB73","softbank":null,"google":"FE7F9","image":"1f6a8.png","sheet_x":36,"sheet_y":52,"short_name":"rotating_light","short_names":["rotating_light"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":958,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRIANGULAR FLAG ON POST","unified":"1F6A9","non_qualified":null,"docomo":"E6DE","au":"EB2C","softbank":null,"google":"FEB22","image":"1f6a9.png","sheet_x":36,"sheet_y":53,"short_name":"triangular_flag_on_post","short_names":["triangular_flag_on_post"],"text":null,"texts":null,"category":"Flags","subcategory":"flag","sort_order":1636,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOOR","unified":"1F6AA","non_qualified":null,"docomo":"E714","au":null,"softbank":null,"google":"FE4F3","image":"1f6aa.png","sheet_x":36,"sheet_y":54,"short_name":"door","short_names":["door"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1378,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NO ENTRY SIGN","unified":"1F6AB","non_qualified":null,"docomo":"E738","au":"E541","softbank":null,"google":"FEB48","image":"1f6ab.png","sheet_x":36,"sheet_y":55,"short_name":"no_entry_sign","short_names":["no_entry_sign"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1428,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMOKING SYMBOL","unified":"1F6AC","non_qualified":null,"docomo":"E67F","au":"E47D","softbank":"E30E","google":"FEB1E","image":"1f6ac.png","sheet_x":36,"sheet_y":56,"short_name":"smoking","short_names":["smoking"],"text":null,"texts":null,"category":"Objects","subcategory":"other-object","sort_order":1403,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NO SMOKING SYMBOL","unified":"1F6AD","non_qualified":null,"docomo":"E680","au":"E47E","softbank":"E208","google":"FEB1F","image":"1f6ad.png","sheet_x":36,"sheet_y":57,"short_name":"no_smoking","short_names":["no_smoking"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1430,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PUT LITTER IN ITS PLACE SYMBOL","unified":"1F6AE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6ae.png","sheet_x":36,"sheet_y":58,"short_name":"put_litter_in_its_place","short_names":["put_litter_in_its_place"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1413,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DO NOT LITTER SYMBOL","unified":"1F6AF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6af.png","sheet_x":36,"sheet_y":59,"short_name":"do_not_litter","short_names":["do_not_litter"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1431,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POTABLE WATER SYMBOL","unified":"1F6B0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b0.png","sheet_x":36,"sheet_y":60,"short_name":"potable_water","short_names":["potable_water"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1414,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NON-POTABLE WATER SYMBOL","unified":"1F6B1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b1.png","sheet_x":36,"sheet_y":61,"short_name":"non-potable_water","short_names":["non-potable_water"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1432,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BICYCLE","unified":"1F6B2","non_qualified":null,"docomo":"E71D","au":"E4AE","softbank":"E136","google":"FE7EB","image":"1f6b2.png","sheet_x":37,"sheet_y":0,"short_name":"bike","short_names":["bike"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":948,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NO BICYCLES","unified":"1F6B3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b3.png","sheet_x":37,"sheet_y":1,"short_name":"no_bicycles","short_names":["no_bicycles"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1429,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN BIKING","unified":"1F6B4-200D-2640-FE0F","non_qualified":"1F6B4-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b4-200d-2640-fe0f.png","sheet_x":37,"sheet_y":2,"short_name":"woman-biking","short_names":["woman-biking"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":483,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6B4-1F3FB-200D-2640-FE0F","non_qualified":"1F6B4-1F3FB-200D-2640","image":"1f6b4-1f3fb-200d-2640-fe0f.png","sheet_x":37,"sheet_y":3,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6B4-1F3FC-200D-2640-FE0F","non_qualified":"1F6B4-1F3FC-200D-2640","image":"1f6b4-1f3fc-200d-2640-fe0f.png","sheet_x":37,"sheet_y":4,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6B4-1F3FD-200D-2640-FE0F","non_qualified":"1F6B4-1F3FD-200D-2640","image":"1f6b4-1f3fd-200d-2640-fe0f.png","sheet_x":37,"sheet_y":5,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6B4-1F3FE-200D-2640-FE0F","non_qualified":"1F6B4-1F3FE-200D-2640","image":"1f6b4-1f3fe-200d-2640-fe0f.png","sheet_x":37,"sheet_y":6,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6B4-1F3FF-200D-2640-FE0F","non_qualified":"1F6B4-1F3FF-200D-2640","image":"1f6b4-1f3ff-200d-2640-fe0f.png","sheet_x":37,"sheet_y":7,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN BIKING","unified":"1F6B4-200D-2642-FE0F","non_qualified":"1F6B4-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b4-200d-2642-fe0f.png","sheet_x":37,"sheet_y":8,"short_name":"man-biking","short_names":["man-biking"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":482,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6B4-1F3FB-200D-2642-FE0F","non_qualified":"1F6B4-1F3FB-200D-2642","image":"1f6b4-1f3fb-200d-2642-fe0f.png","sheet_x":37,"sheet_y":9,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6B4-1F3FC-200D-2642-FE0F","non_qualified":"1F6B4-1F3FC-200D-2642","image":"1f6b4-1f3fc-200d-2642-fe0f.png","sheet_x":37,"sheet_y":10,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6B4-1F3FD-200D-2642-FE0F","non_qualified":"1F6B4-1F3FD-200D-2642","image":"1f6b4-1f3fd-200d-2642-fe0f.png","sheet_x":37,"sheet_y":11,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6B4-1F3FE-200D-2642-FE0F","non_qualified":"1F6B4-1F3FE-200D-2642","image":"1f6b4-1f3fe-200d-2642-fe0f.png","sheet_x":37,"sheet_y":12,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6B4-1F3FF-200D-2642-FE0F","non_qualified":"1F6B4-1F3FF-200D-2642","image":"1f6b4-1f3ff-200d-2642-fe0f.png","sheet_x":37,"sheet_y":13,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F6B4"},{"name":"BICYCLIST","unified":"1F6B4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b4.png","sheet_x":37,"sheet_y":14,"short_name":"bicyclist","short_names":["bicyclist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":481,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6B4-1F3FB","non_qualified":null,"image":"1f6b4-1f3fb.png","sheet_x":37,"sheet_y":15,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6B4-1F3FC","non_qualified":null,"image":"1f6b4-1f3fc.png","sheet_x":37,"sheet_y":16,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6B4-1F3FD","non_qualified":null,"image":"1f6b4-1f3fd.png","sheet_x":37,"sheet_y":17,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6B4-1F3FE","non_qualified":null,"image":"1f6b4-1f3fe.png","sheet_x":37,"sheet_y":18,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6B4-1F3FF","non_qualified":null,"image":"1f6b4-1f3ff.png","sheet_x":37,"sheet_y":19,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F6B4-200D-2642-FE0F"},{"name":"WOMAN MOUNTAIN BIKING","unified":"1F6B5-200D-2640-FE0F","non_qualified":"1F6B5-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b5-200d-2640-fe0f.png","sheet_x":37,"sheet_y":20,"short_name":"woman-mountain-biking","short_names":["woman-mountain-biking"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":486,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6B5-1F3FB-200D-2640-FE0F","non_qualified":"1F6B5-1F3FB-200D-2640","image":"1f6b5-1f3fb-200d-2640-fe0f.png","sheet_x":37,"sheet_y":21,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6B5-1F3FC-200D-2640-FE0F","non_qualified":"1F6B5-1F3FC-200D-2640","image":"1f6b5-1f3fc-200d-2640-fe0f.png","sheet_x":37,"sheet_y":22,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6B5-1F3FD-200D-2640-FE0F","non_qualified":"1F6B5-1F3FD-200D-2640","image":"1f6b5-1f3fd-200d-2640-fe0f.png","sheet_x":37,"sheet_y":23,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6B5-1F3FE-200D-2640-FE0F","non_qualified":"1F6B5-1F3FE-200D-2640","image":"1f6b5-1f3fe-200d-2640-fe0f.png","sheet_x":37,"sheet_y":24,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6B5-1F3FF-200D-2640-FE0F","non_qualified":"1F6B5-1F3FF-200D-2640","image":"1f6b5-1f3ff-200d-2640-fe0f.png","sheet_x":37,"sheet_y":25,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN MOUNTAIN BIKING","unified":"1F6B5-200D-2642-FE0F","non_qualified":"1F6B5-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b5-200d-2642-fe0f.png","sheet_x":37,"sheet_y":26,"short_name":"man-mountain-biking","short_names":["man-mountain-biking"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":485,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6B5-1F3FB-200D-2642-FE0F","non_qualified":"1F6B5-1F3FB-200D-2642","image":"1f6b5-1f3fb-200d-2642-fe0f.png","sheet_x":37,"sheet_y":27,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6B5-1F3FC-200D-2642-FE0F","non_qualified":"1F6B5-1F3FC-200D-2642","image":"1f6b5-1f3fc-200d-2642-fe0f.png","sheet_x":37,"sheet_y":28,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6B5-1F3FD-200D-2642-FE0F","non_qualified":"1F6B5-1F3FD-200D-2642","image":"1f6b5-1f3fd-200d-2642-fe0f.png","sheet_x":37,"sheet_y":29,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6B5-1F3FE-200D-2642-FE0F","non_qualified":"1F6B5-1F3FE-200D-2642","image":"1f6b5-1f3fe-200d-2642-fe0f.png","sheet_x":37,"sheet_y":30,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6B5-1F3FF-200D-2642-FE0F","non_qualified":"1F6B5-1F3FF-200D-2642","image":"1f6b5-1f3ff-200d-2642-fe0f.png","sheet_x":37,"sheet_y":31,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F6B5"},{"name":"MOUNTAIN BICYCLIST","unified":"1F6B5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b5.png","sheet_x":37,"sheet_y":32,"short_name":"mountain_bicyclist","short_names":["mountain_bicyclist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":484,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6B5-1F3FB","non_qualified":null,"image":"1f6b5-1f3fb.png","sheet_x":37,"sheet_y":33,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6B5-1F3FC","non_qualified":null,"image":"1f6b5-1f3fc.png","sheet_x":37,"sheet_y":34,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6B5-1F3FD","non_qualified":null,"image":"1f6b5-1f3fd.png","sheet_x":37,"sheet_y":35,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6B5-1F3FE","non_qualified":null,"image":"1f6b5-1f3fe.png","sheet_x":37,"sheet_y":36,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6B5-1F3FF","non_qualified":null,"image":"1f6b5-1f3ff.png","sheet_x":37,"sheet_y":37,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F6B5-200D-2642-FE0F"},{"name":"WOMAN WALKING","unified":"1F6B6-200D-2640-FE0F","non_qualified":"1F6B6-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b6-200d-2640-fe0f.png","sheet_x":37,"sheet_y":38,"short_name":"woman-walking","short_names":["woman-walking"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":410,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6B6-1F3FB-200D-2640-FE0F","non_qualified":"1F6B6-1F3FB-200D-2640","image":"1f6b6-1f3fb-200d-2640-fe0f.png","sheet_x":37,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6B6-1F3FC-200D-2640-FE0F","non_qualified":"1F6B6-1F3FC-200D-2640","image":"1f6b6-1f3fc-200d-2640-fe0f.png","sheet_x":37,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6B6-1F3FD-200D-2640-FE0F","non_qualified":"1F6B6-1F3FD-200D-2640","image":"1f6b6-1f3fd-200d-2640-fe0f.png","sheet_x":37,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6B6-1F3FE-200D-2640-FE0F","non_qualified":"1F6B6-1F3FE-200D-2640","image":"1f6b6-1f3fe-200d-2640-fe0f.png","sheet_x":37,"sheet_y":42,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6B6-1F3FF-200D-2640-FE0F","non_qualified":"1F6B6-1F3FF-200D-2640","image":"1f6b6-1f3ff-200d-2640-fe0f.png","sheet_x":37,"sheet_y":43,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN WALKING FACING RIGHT","unified":"1F6B6-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-200D-2640-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b6-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":44,"short_name":"woman_walking_facing_right","short_names":["woman_walking_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":412,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F6B6-1F3FB-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FB-200D-2640-200D-27A1","image":"1f6b6-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":45,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F6B6-1F3FC-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FC-200D-2640-200D-27A1","image":"1f6b6-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":46,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F6B6-1F3FD-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FD-200D-2640-200D-27A1","image":"1f6b6-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":47,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F6B6-1F3FE-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FE-200D-2640-200D-27A1","image":"1f6b6-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":48,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F6B6-1F3FF-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FF-200D-2640-200D-27A1","image":"1f6b6-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":49,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"MAN WALKING","unified":"1F6B6-200D-2642-FE0F","non_qualified":"1F6B6-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b6-200d-2642-fe0f.png","sheet_x":37,"sheet_y":50,"short_name":"man-walking","short_names":["man-walking"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":409,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6B6-1F3FB-200D-2642-FE0F","non_qualified":"1F6B6-1F3FB-200D-2642","image":"1f6b6-1f3fb-200d-2642-fe0f.png","sheet_x":37,"sheet_y":51,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6B6-1F3FC-200D-2642-FE0F","non_qualified":"1F6B6-1F3FC-200D-2642","image":"1f6b6-1f3fc-200d-2642-fe0f.png","sheet_x":37,"sheet_y":52,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6B6-1F3FD-200D-2642-FE0F","non_qualified":"1F6B6-1F3FD-200D-2642","image":"1f6b6-1f3fd-200d-2642-fe0f.png","sheet_x":37,"sheet_y":53,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6B6-1F3FE-200D-2642-FE0F","non_qualified":"1F6B6-1F3FE-200D-2642","image":"1f6b6-1f3fe-200d-2642-fe0f.png","sheet_x":37,"sheet_y":54,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6B6-1F3FF-200D-2642-FE0F","non_qualified":"1F6B6-1F3FF-200D-2642","image":"1f6b6-1f3ff-200d-2642-fe0f.png","sheet_x":37,"sheet_y":55,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"1F6B6"},{"name":"MAN WALKING FACING RIGHT","unified":"1F6B6-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-200D-2642-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b6-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":56,"short_name":"man_walking_facing_right","short_names":["man_walking_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":413,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F6B6-1F3FB-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FB-200D-2642-200D-27A1","image":"1f6b6-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":57,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F6B6-1F3FC-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FC-200D-2642-200D-27A1","image":"1f6b6-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":58,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F6B6-1F3FD-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FD-200D-2642-200D-27A1","image":"1f6b6-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":59,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F6B6-1F3FE-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FE-200D-2642-200D-27A1","image":"1f6b6-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":60,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F6B6-1F3FF-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FF-200D-2642-200D-27A1","image":"1f6b6-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":37,"sheet_y":61,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"PERSON WALKING FACING RIGHT","unified":"1F6B6-200D-27A1-FE0F","non_qualified":"1F6B6-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b6-200d-27a1-fe0f.png","sheet_x":38,"sheet_y":0,"short_name":"person_walking_facing_right","short_names":["person_walking_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":411,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F6B6-1F3FB-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FB-200D-27A1","image":"1f6b6-1f3fb-200d-27a1-fe0f.png","sheet_x":38,"sheet_y":1,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F6B6-1F3FC-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FC-200D-27A1","image":"1f6b6-1f3fc-200d-27a1-fe0f.png","sheet_x":38,"sheet_y":2,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F6B6-1F3FD-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FD-200D-27A1","image":"1f6b6-1f3fd-200d-27a1-fe0f.png","sheet_x":38,"sheet_y":3,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F6B6-1F3FE-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FE-200D-27A1","image":"1f6b6-1f3fe-200d-27a1-fe0f.png","sheet_x":38,"sheet_y":4,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F6B6-1F3FF-200D-27A1-FE0F","non_qualified":"1F6B6-1F3FF-200D-27A1","image":"1f6b6-1f3ff-200d-27a1-fe0f.png","sheet_x":38,"sheet_y":5,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"PEDESTRIAN","unified":"1F6B6","non_qualified":null,"docomo":"E733","au":"EB72","softbank":"E201","google":"FE7F0","image":"1f6b6.png","sheet_x":38,"sheet_y":6,"short_name":"walking","short_names":["walking"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":408,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6B6-1F3FB","non_qualified":null,"image":"1f6b6-1f3fb.png","sheet_x":38,"sheet_y":7,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6B6-1F3FC","non_qualified":null,"image":"1f6b6-1f3fc.png","sheet_x":38,"sheet_y":8,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6B6-1F3FD","non_qualified":null,"image":"1f6b6-1f3fd.png","sheet_x":38,"sheet_y":9,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6B6-1F3FE","non_qualified":null,"image":"1f6b6-1f3fe.png","sheet_x":38,"sheet_y":10,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6B6-1F3FF","non_qualified":null,"image":"1f6b6-1f3ff.png","sheet_x":38,"sheet_y":11,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"1F6B6-200D-2642-FE0F"},{"name":"NO PEDESTRIANS","unified":"1F6B7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b7.png","sheet_x":38,"sheet_y":12,"short_name":"no_pedestrians","short_names":["no_pedestrians"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1433,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHILDREN CROSSING","unified":"1F6B8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b8.png","sheet_x":38,"sheet_y":13,"short_name":"children_crossing","short_names":["children_crossing"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1426,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MENS SYMBOL","unified":"1F6B9","non_qualified":null,"docomo":null,"au":null,"softbank":"E138","google":"FEB33","image":"1f6b9.png","sheet_x":38,"sheet_y":14,"short_name":"mens","short_names":["mens"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1416,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMENS SYMBOL","unified":"1F6BA","non_qualified":null,"docomo":null,"au":null,"softbank":"E139","google":"FEB34","image":"1f6ba.png","sheet_x":38,"sheet_y":15,"short_name":"womens","short_names":["womens"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1417,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RESTROOM","unified":"1F6BB","non_qualified":null,"docomo":"E66E","au":"E4A5","softbank":"E151","google":"FE506","image":"1f6bb.png","sheet_x":38,"sheet_y":16,"short_name":"restroom","short_names":["restroom"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1418,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BABY SYMBOL","unified":"1F6BC","non_qualified":null,"docomo":null,"au":"EB18","softbank":"E13A","google":"FEB35","image":"1f6bc.png","sheet_x":38,"sheet_y":17,"short_name":"baby_symbol","short_names":["baby_symbol"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1419,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TOILET","unified":"1F6BD","non_qualified":null,"docomo":"E66E","au":"E4A5","softbank":"E140","google":"FE507","image":"1f6bd.png","sheet_x":38,"sheet_y":18,"short_name":"toilet","short_names":["toilet"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1385,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WATER CLOSET","unified":"1F6BE","non_qualified":null,"docomo":"E66E","au":"E4A5","softbank":"E309","google":"FE508","image":"1f6be.png","sheet_x":38,"sheet_y":19,"short_name":"wc","short_names":["wc"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1420,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHOWER","unified":"1F6BF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6bf.png","sheet_x":38,"sheet_y":20,"short_name":"shower","short_names":["shower"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1387,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BATH","unified":"1F6C0","non_qualified":null,"docomo":"E6F7","au":"E5D8","softbank":"E13F","google":"FE505","image":"1f6c0.png","sheet_x":38,"sheet_y":21,"short_name":"bath","short_names":["bath"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-resting","sort_order":505,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6C0-1F3FB","non_qualified":null,"image":"1f6c0-1f3fb.png","sheet_x":38,"sheet_y":22,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6C0-1F3FC","non_qualified":null,"image":"1f6c0-1f3fc.png","sheet_x":38,"sheet_y":23,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6C0-1F3FD","non_qualified":null,"image":"1f6c0-1f3fd.png","sheet_x":38,"sheet_y":24,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6C0-1F3FE","non_qualified":null,"image":"1f6c0-1f3fe.png","sheet_x":38,"sheet_y":25,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6C0-1F3FF","non_qualified":null,"image":"1f6c0-1f3ff.png","sheet_x":38,"sheet_y":26,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"BATHTUB","unified":"1F6C1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6c1.png","sheet_x":38,"sheet_y":27,"short_name":"bathtub","short_names":["bathtub"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1388,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PASSPORT CONTROL","unified":"1F6C2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6c2.png","sheet_x":38,"sheet_y":28,"short_name":"passport_control","short_names":["passport_control"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1421,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CUSTOMS","unified":"1F6C3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6c3.png","sheet_x":38,"sheet_y":29,"short_name":"customs","short_names":["customs"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1422,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BAGGAGE CLAIM","unified":"1F6C4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6c4.png","sheet_x":38,"sheet_y":30,"short_name":"baggage_claim","short_names":["baggage_claim"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1423,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEFT LUGGAGE","unified":"1F6C5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6c5.png","sheet_x":38,"sheet_y":31,"short_name":"left_luggage","short_names":["left_luggage"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1424,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COUCH AND LAMP","unified":"1F6CB-FE0F","non_qualified":"1F6CB","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6cb-fe0f.png","sheet_x":38,"sheet_y":32,"short_name":"couch_and_lamp","short_names":["couch_and_lamp"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1383,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SLEEPING ACCOMMODATION","unified":"1F6CC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6cc.png","sheet_x":38,"sheet_y":33,"short_name":"sleeping_accommodation","short_names":["sleeping_accommodation"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-resting","sort_order":506,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F6CC-1F3FB","non_qualified":null,"image":"1f6cc-1f3fb.png","sheet_x":38,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F6CC-1F3FC","non_qualified":null,"image":"1f6cc-1f3fc.png","sheet_x":38,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F6CC-1F3FD","non_qualified":null,"image":"1f6cc-1f3fd.png","sheet_x":38,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F6CC-1F3FE","non_qualified":null,"image":"1f6cc-1f3fe.png","sheet_x":38,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F6CC-1F3FF","non_qualified":null,"image":"1f6cc-1f3ff.png","sheet_x":38,"sheet_y":38,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SHOPPING BAGS","unified":"1F6CD-FE0F","non_qualified":"1F6CD","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6cd-fe0f.png","sheet_x":38,"sheet_y":39,"short_name":"shopping_bags","short_names":["shopping_bags"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1174,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BELLHOP BELL","unified":"1F6CE-FE0F","non_qualified":"1F6CE","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6ce-fe0f.png","sheet_x":38,"sheet_y":40,"short_name":"bellhop_bell","short_names":["bellhop_bell"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"hotel","sort_order":985,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BED","unified":"1F6CF-FE0F","non_qualified":"1F6CF","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6cf-fe0f.png","sheet_x":38,"sheet_y":41,"short_name":"bed","short_names":["bed"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1382,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PLACE OF WORSHIP","unified":"1F6D0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6d0.png","sheet_x":38,"sheet_y":42,"short_name":"place_of_worship","short_names":["place_of_worship"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1459,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OCTAGONAL SIGN","unified":"1F6D1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6d1.png","sheet_x":38,"sheet_y":43,"short_name":"octagonal_sign","short_names":["octagonal_sign"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":961,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHOPPING TROLLEY","unified":"1F6D2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6d2.png","sheet_x":38,"sheet_y":44,"short_name":"shopping_trolley","short_names":["shopping_trolley"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1402,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HINDU TEMPLE","unified":"1F6D5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6d5.png","sheet_x":38,"sheet_y":45,"short_name":"hindu_temple","short_names":["hindu_temple"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-religious","sort_order":892,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HUT","unified":"1F6D6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6d6.png","sheet_x":38,"sheet_y":46,"short_name":"hut","short_names":["hut"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":869,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ELEVATOR","unified":"1F6D7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6d7.png","sheet_x":38,"sheet_y":47,"short_name":"elevator","short_names":["elevator"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1379,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WIRELESS","unified":"1F6DC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6dc.png","sheet_x":38,"sheet_y":48,"short_name":"wireless","short_names":["wireless"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1507,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PLAYGROUND SLIDE","unified":"1F6DD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6dd.png","sheet_x":38,"sheet_y":49,"short_name":"playground_slide","short_names":["playground_slide"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":908,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHEEL","unified":"1F6DE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6de.png","sheet_x":38,"sheet_y":50,"short_name":"wheel","short_names":["wheel"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":957,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RING BUOY","unified":"1F6DF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6df.png","sheet_x":38,"sheet_y":51,"short_name":"ring_buoy","short_names":["ring_buoy"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-water","sort_order":964,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HAMMER AND WRENCH","unified":"1F6E0-FE0F","non_qualified":"1F6E0","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e0-fe0f.png","sheet_x":38,"sheet_y":52,"short_name":"hammer_and_wrench","short_names":["hammer_and_wrench"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1342,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHIELD","unified":"1F6E1-FE0F","non_qualified":"1F6E1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e1-fe0f.png","sheet_x":38,"sheet_y":53,"short_name":"shield","short_names":["shield"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1348,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OIL DRUM","unified":"1F6E2-FE0F","non_qualified":"1F6E2","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e2-fe0f.png","sheet_x":38,"sheet_y":54,"short_name":"oil_drum","short_names":["oil_drum"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":955,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOTORWAY","unified":"1F6E3-FE0F","non_qualified":"1F6E3","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e3-fe0f.png","sheet_x":38,"sheet_y":55,"short_name":"motorway","short_names":["motorway"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":953,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RAILWAY TRACK","unified":"1F6E4-FE0F","non_qualified":"1F6E4","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e4-fe0f.png","sheet_x":38,"sheet_y":56,"short_name":"railway_track","short_names":["railway_track"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":954,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOTOR BOAT","unified":"1F6E5-FE0F","non_qualified":"1F6E5","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e5-fe0f.png","sheet_x":38,"sheet_y":57,"short_name":"motor_boat","short_names":["motor_boat"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-water","sort_order":970,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMALL AIRPLANE","unified":"1F6E9-FE0F","non_qualified":"1F6E9","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e9-fe0f.png","sheet_x":38,"sheet_y":58,"short_name":"small_airplane","short_names":["small_airplane"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":973,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AIRPLANE DEPARTURE","unified":"1F6EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6eb.png","sheet_x":38,"sheet_y":59,"short_name":"airplane_departure","short_names":["airplane_departure"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":974,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AIRPLANE ARRIVING","unified":"1F6EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6ec.png","sheet_x":38,"sheet_y":60,"short_name":"airplane_arriving","short_names":["airplane_arriving"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":975,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SATELLITE","unified":"1F6F0-FE0F","non_qualified":"1F6F0","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6f0-fe0f.png","sheet_x":38,"sheet_y":61,"short_name":"satellite","short_names":["satellite"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":982,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PASSENGER SHIP","unified":"1F6F3-FE0F","non_qualified":"1F6F3","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6f3-fe0f.png","sheet_x":39,"sheet_y":0,"short_name":"passenger_ship","short_names":["passenger_ship"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-water","sort_order":968,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SCOOTER","unified":"1F6F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6f4.png","sheet_x":39,"sheet_y":1,"short_name":"scooter","short_names":["scooter"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":949,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOTOR SCOOTER","unified":"1F6F5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6f5.png","sheet_x":39,"sheet_y":2,"short_name":"motor_scooter","short_names":["motor_scooter"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":944,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CANOE","unified":"1F6F6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6f6.png","sheet_x":39,"sheet_y":3,"short_name":"canoe","short_names":["canoe"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-water","sort_order":966,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SLED","unified":"1F6F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6f7.png","sheet_x":39,"sheet_y":4,"short_name":"sled","short_names":["sled"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1117,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLYING SAUCER","unified":"1F6F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6f8.png","sheet_x":39,"sheet_y":5,"short_name":"flying_saucer","short_names":["flying_saucer"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":984,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SKATEBOARD","unified":"1F6F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6f9.png","sheet_x":39,"sheet_y":6,"short_name":"skateboard","short_names":["skateboard"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":950,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AUTO RICKSHAW","unified":"1F6FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6fa.png","sheet_x":39,"sheet_y":7,"short_name":"auto_rickshaw","short_names":["auto_rickshaw"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":947,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PICKUP TRUCK","unified":"1F6FB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6fb.png","sheet_x":39,"sheet_y":8,"short_name":"pickup_truck","short_names":["pickup_truck"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":938,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROLLER SKATE","unified":"1F6FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6fc.png","sheet_x":39,"sheet_y":9,"short_name":"roller_skate","short_names":["roller_skate"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":951,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE ORANGE CIRCLE","unified":"1F7E0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7e0.png","sheet_x":39,"sheet_y":10,"short_name":"large_orange_circle","short_names":["large_orange_circle"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1602,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE YELLOW CIRCLE","unified":"1F7E1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7e1.png","sheet_x":39,"sheet_y":11,"short_name":"large_yellow_circle","short_names":["large_yellow_circle"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1603,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE GREEN CIRCLE","unified":"1F7E2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7e2.png","sheet_x":39,"sheet_y":12,"short_name":"large_green_circle","short_names":["large_green_circle"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1604,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE PURPLE CIRCLE","unified":"1F7E3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7e3.png","sheet_x":39,"sheet_y":13,"short_name":"large_purple_circle","short_names":["large_purple_circle"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1606,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE BROWN CIRCLE","unified":"1F7E4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7e4.png","sheet_x":39,"sheet_y":14,"short_name":"large_brown_circle","short_names":["large_brown_circle"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1607,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE RED SQUARE","unified":"1F7E5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7e5.png","sheet_x":39,"sheet_y":15,"short_name":"large_red_square","short_names":["large_red_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1610,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE BLUE SQUARE","unified":"1F7E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7e6.png","sheet_x":39,"sheet_y":16,"short_name":"large_blue_square","short_names":["large_blue_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1614,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE ORANGE SQUARE","unified":"1F7E7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7e7.png","sheet_x":39,"sheet_y":17,"short_name":"large_orange_square","short_names":["large_orange_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1611,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE YELLOW SQUARE","unified":"1F7E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7e8.png","sheet_x":39,"sheet_y":18,"short_name":"large_yellow_square","short_names":["large_yellow_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1612,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE GREEN SQUARE","unified":"1F7E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7e9.png","sheet_x":39,"sheet_y":19,"short_name":"large_green_square","short_names":["large_green_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1613,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE PURPLE SQUARE","unified":"1F7EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7ea.png","sheet_x":39,"sheet_y":20,"short_name":"large_purple_square","short_names":["large_purple_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1615,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LARGE BROWN SQUARE","unified":"1F7EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7eb.png","sheet_x":39,"sheet_y":21,"short_name":"large_brown_square","short_names":["large_brown_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1616,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAVY EQUALS SIGN","unified":"1F7F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f7f0.png","sheet_x":39,"sheet_y":22,"short_name":"heavy_equals_sign","short_names":["heavy_equals_sign"],"text":null,"texts":null,"category":"Symbols","subcategory":"math","sort_order":1517,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PINCHED FINGERS","unified":"1F90C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f90c.png","sheet_x":39,"sheet_y":23,"short_name":"pinched_fingers","short_names":["pinched_fingers"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-partial","sort_order":181,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F90C-1F3FB","non_qualified":null,"image":"1f90c-1f3fb.png","sheet_x":39,"sheet_y":24,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F90C-1F3FC","non_qualified":null,"image":"1f90c-1f3fc.png","sheet_x":39,"sheet_y":25,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F90C-1F3FD","non_qualified":null,"image":"1f90c-1f3fd.png","sheet_x":39,"sheet_y":26,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F90C-1F3FE","non_qualified":null,"image":"1f90c-1f3fe.png","sheet_x":39,"sheet_y":27,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F90C-1F3FF","non_qualified":null,"image":"1f90c-1f3ff.png","sheet_x":39,"sheet_y":28,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WHITE HEART","unified":"1F90D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f90d.png","sheet_x":39,"sheet_y":29,"short_name":"white_heart","short_names":["white_heart"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":154,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BROWN HEART","unified":"1F90E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f90e.png","sheet_x":39,"sheet_y":30,"short_name":"brown_heart","short_names":["brown_heart"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":151,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PINCHING HAND","unified":"1F90F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f90f.png","sheet_x":39,"sheet_y":31,"short_name":"pinching_hand","short_names":["pinching_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-partial","sort_order":182,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F90F-1F3FB","non_qualified":null,"image":"1f90f-1f3fb.png","sheet_x":39,"sheet_y":32,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F90F-1F3FC","non_qualified":null,"image":"1f90f-1f3fc.png","sheet_x":39,"sheet_y":33,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F90F-1F3FD","non_qualified":null,"image":"1f90f-1f3fd.png","sheet_x":39,"sheet_y":34,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F90F-1F3FE","non_qualified":null,"image":"1f90f-1f3fe.png","sheet_x":39,"sheet_y":35,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F90F-1F3FF","non_qualified":null,"image":"1f90f-1f3ff.png","sheet_x":39,"sheet_y":36,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"ZIPPER-MOUTH FACE","unified":"1F910","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f910.png","sheet_x":39,"sheet_y":37,"short_name":"zipper_mouth_face","short_names":["zipper_mouth_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":37,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MONEY-MOUTH FACE","unified":"1F911","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f911.png","sheet_x":39,"sheet_y":38,"short_name":"money_mouth_face","short_names":["money_mouth_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-tongue","sort_order":29,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH THERMOMETER","unified":"1F912","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f912.png","sheet_x":39,"sheet_y":39,"short_name":"face_with_thermometer","short_names":["face_with_thermometer"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":59,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NERD FACE","unified":"1F913","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f913.png","sheet_x":39,"sheet_y":40,"short_name":"nerd_face","short_names":["nerd_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-glasses","sort_order":74,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"THINKING FACE","unified":"1F914","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f914.png","sheet_x":39,"sheet_y":41,"short_name":"thinking_face","short_names":["thinking_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-hand","sort_order":35,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH HEAD-BANDAGE","unified":"1F915","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f915.png","sheet_x":39,"sheet_y":42,"short_name":"face_with_head_bandage","short_names":["face_with_head_bandage"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":60,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROBOT FACE","unified":"1F916","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f916.png","sheet_x":39,"sheet_y":43,"short_name":"robot_face","short_names":["robot_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-costume","sort_order":117,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HUGGING FACE","unified":"1F917","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f917.png","sheet_x":39,"sheet_y":44,"short_name":"hugging_face","short_names":["hugging_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-hand","sort_order":30,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SIGN OF THE HORNS","unified":"1F918","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f918.png","sheet_x":39,"sheet_y":45,"short_name":"the_horns","short_names":["the_horns","sign_of_the_horns"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-partial","sort_order":187,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F918-1F3FB","non_qualified":null,"image":"1f918-1f3fb.png","sheet_x":39,"sheet_y":46,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F918-1F3FC","non_qualified":null,"image":"1f918-1f3fc.png","sheet_x":39,"sheet_y":47,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F918-1F3FD","non_qualified":null,"image":"1f918-1f3fd.png","sheet_x":39,"sheet_y":48,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F918-1F3FE","non_qualified":null,"image":"1f918-1f3fe.png","sheet_x":39,"sheet_y":49,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F918-1F3FF","non_qualified":null,"image":"1f918-1f3ff.png","sheet_x":39,"sheet_y":50,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"CALL ME HAND","unified":"1F919","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f919.png","sheet_x":39,"sheet_y":51,"short_name":"call_me_hand","short_names":["call_me_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-partial","sort_order":188,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F919-1F3FB","non_qualified":null,"image":"1f919-1f3fb.png","sheet_x":39,"sheet_y":52,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F919-1F3FC","non_qualified":null,"image":"1f919-1f3fc.png","sheet_x":39,"sheet_y":53,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F919-1F3FD","non_qualified":null,"image":"1f919-1f3fd.png","sheet_x":39,"sheet_y":54,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F919-1F3FE","non_qualified":null,"image":"1f919-1f3fe.png","sheet_x":39,"sheet_y":55,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F919-1F3FF","non_qualified":null,"image":"1f919-1f3ff.png","sheet_x":39,"sheet_y":56,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"RAISED BACK OF HAND","unified":"1F91A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f91a.png","sheet_x":39,"sheet_y":57,"short_name":"raised_back_of_hand","short_names":["raised_back_of_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":170,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F91A-1F3FB","non_qualified":null,"image":"1f91a-1f3fb.png","sheet_x":39,"sheet_y":58,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F91A-1F3FC","non_qualified":null,"image":"1f91a-1f3fc.png","sheet_x":39,"sheet_y":59,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F91A-1F3FD","non_qualified":null,"image":"1f91a-1f3fd.png","sheet_x":39,"sheet_y":60,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F91A-1F3FE","non_qualified":null,"image":"1f91a-1f3fe.png","sheet_x":39,"sheet_y":61,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F91A-1F3FF","non_qualified":null,"image":"1f91a-1f3ff.png","sheet_x":40,"sheet_y":0,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"LEFT-FACING FIST","unified":"1F91B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f91b.png","sheet_x":40,"sheet_y":1,"short_name":"left-facing_fist","short_names":["left-facing_fist"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-closed","sort_order":200,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F91B-1F3FB","non_qualified":null,"image":"1f91b-1f3fb.png","sheet_x":40,"sheet_y":2,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F91B-1F3FC","non_qualified":null,"image":"1f91b-1f3fc.png","sheet_x":40,"sheet_y":3,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F91B-1F3FD","non_qualified":null,"image":"1f91b-1f3fd.png","sheet_x":40,"sheet_y":4,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F91B-1F3FE","non_qualified":null,"image":"1f91b-1f3fe.png","sheet_x":40,"sheet_y":5,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F91B-1F3FF","non_qualified":null,"image":"1f91b-1f3ff.png","sheet_x":40,"sheet_y":6,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"RIGHT-FACING FIST","unified":"1F91C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f91c.png","sheet_x":40,"sheet_y":7,"short_name":"right-facing_fist","short_names":["right-facing_fist"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-closed","sort_order":201,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F91C-1F3FB","non_qualified":null,"image":"1f91c-1f3fb.png","sheet_x":40,"sheet_y":8,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F91C-1F3FC","non_qualified":null,"image":"1f91c-1f3fc.png","sheet_x":40,"sheet_y":9,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F91C-1F3FD","non_qualified":null,"image":"1f91c-1f3fd.png","sheet_x":40,"sheet_y":10,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F91C-1F3FE","non_qualified":null,"image":"1f91c-1f3fe.png","sheet_x":40,"sheet_y":11,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F91C-1F3FF","non_qualified":null,"image":"1f91c-1f3ff.png","sheet_x":40,"sheet_y":12,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"HANDSHAKE","unified":"1F91D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f91d.png","sheet_x":40,"sheet_y":13,"short_name":"handshake","short_names":["handshake"],"text":null,"texts":null,"category":"People & Body","subcategory":"hands","sort_order":207,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F91D-1F3FB","non_qualified":null,"image":"1f91d-1f3fb.png","sheet_x":40,"sheet_y":14,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F91D-1F3FC","non_qualified":null,"image":"1f91d-1f3fc.png","sheet_x":40,"sheet_y":15,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F91D-1F3FD","non_qualified":null,"image":"1f91d-1f3fd.png","sheet_x":40,"sheet_y":16,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F91D-1F3FE","non_qualified":null,"image":"1f91d-1f3fe.png","sheet_x":40,"sheet_y":17,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F91D-1F3FF","non_qualified":null,"image":"1f91d-1f3ff.png","sheet_x":40,"sheet_y":18,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1FAF1-1F3FB-200D-1FAF2-1F3FC","non_qualified":null,"image":"1faf1-1f3fb-200d-1faf2-1f3fc.png","sheet_x":40,"sheet_y":19,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1FAF1-1F3FB-200D-1FAF2-1F3FD","non_qualified":null,"image":"1faf1-1f3fb-200d-1faf2-1f3fd.png","sheet_x":40,"sheet_y":20,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1FAF1-1F3FB-200D-1FAF2-1F3FE","non_qualified":null,"image":"1faf1-1f3fb-200d-1faf2-1f3fe.png","sheet_x":40,"sheet_y":21,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1FAF1-1F3FB-200D-1FAF2-1F3FF","non_qualified":null,"image":"1faf1-1f3fb-200d-1faf2-1f3ff.png","sheet_x":40,"sheet_y":22,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1FAF1-1F3FC-200D-1FAF2-1F3FB","non_qualified":null,"image":"1faf1-1f3fc-200d-1faf2-1f3fb.png","sheet_x":40,"sheet_y":23,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1FAF1-1F3FC-200D-1FAF2-1F3FD","non_qualified":null,"image":"1faf1-1f3fc-200d-1faf2-1f3fd.png","sheet_x":40,"sheet_y":24,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1FAF1-1F3FC-200D-1FAF2-1F3FE","non_qualified":null,"image":"1faf1-1f3fc-200d-1faf2-1f3fe.png","sheet_x":40,"sheet_y":25,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1FAF1-1F3FC-200D-1FAF2-1F3FF","non_qualified":null,"image":"1faf1-1f3fc-200d-1faf2-1f3ff.png","sheet_x":40,"sheet_y":26,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1FAF1-1F3FD-200D-1FAF2-1F3FB","non_qualified":null,"image":"1faf1-1f3fd-200d-1faf2-1f3fb.png","sheet_x":40,"sheet_y":27,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1FAF1-1F3FD-200D-1FAF2-1F3FC","non_qualified":null,"image":"1faf1-1f3fd-200d-1faf2-1f3fc.png","sheet_x":40,"sheet_y":28,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1FAF1-1F3FD-200D-1FAF2-1F3FE","non_qualified":null,"image":"1faf1-1f3fd-200d-1faf2-1f3fe.png","sheet_x":40,"sheet_y":29,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1FAF1-1F3FD-200D-1FAF2-1F3FF","non_qualified":null,"image":"1faf1-1f3fd-200d-1faf2-1f3ff.png","sheet_x":40,"sheet_y":30,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1FAF1-1F3FE-200D-1FAF2-1F3FB","non_qualified":null,"image":"1faf1-1f3fe-200d-1faf2-1f3fb.png","sheet_x":40,"sheet_y":31,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1FAF1-1F3FE-200D-1FAF2-1F3FC","non_qualified":null,"image":"1faf1-1f3fe-200d-1faf2-1f3fc.png","sheet_x":40,"sheet_y":32,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1FAF1-1F3FE-200D-1FAF2-1F3FD","non_qualified":null,"image":"1faf1-1f3fe-200d-1faf2-1f3fd.png","sheet_x":40,"sheet_y":33,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1FAF1-1F3FE-200D-1FAF2-1F3FF","non_qualified":null,"image":"1faf1-1f3fe-200d-1faf2-1f3ff.png","sheet_x":40,"sheet_y":34,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1FAF1-1F3FF-200D-1FAF2-1F3FB","non_qualified":null,"image":"1faf1-1f3ff-200d-1faf2-1f3fb.png","sheet_x":40,"sheet_y":35,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1FAF1-1F3FF-200D-1FAF2-1F3FC","non_qualified":null,"image":"1faf1-1f3ff-200d-1faf2-1f3fc.png","sheet_x":40,"sheet_y":36,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1FAF1-1F3FF-200D-1FAF2-1F3FD","non_qualified":null,"image":"1faf1-1f3ff-200d-1faf2-1f3fd.png","sheet_x":40,"sheet_y":37,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1FAF1-1F3FF-200D-1FAF2-1F3FE","non_qualified":null,"image":"1faf1-1f3ff-200d-1faf2-1f3fe.png","sheet_x":40,"sheet_y":38,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"HAND WITH INDEX AND MIDDLE FINGERS CROSSED","unified":"1F91E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f91e.png","sheet_x":40,"sheet_y":39,"short_name":"crossed_fingers","short_names":["crossed_fingers","hand_with_index_and_middle_fingers_crossed"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-partial","sort_order":184,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F91E-1F3FB","non_qualified":null,"image":"1f91e-1f3fb.png","sheet_x":40,"sheet_y":40,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F91E-1F3FC","non_qualified":null,"image":"1f91e-1f3fc.png","sheet_x":40,"sheet_y":41,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F91E-1F3FD","non_qualified":null,"image":"1f91e-1f3fd.png","sheet_x":40,"sheet_y":42,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F91E-1F3FE","non_qualified":null,"image":"1f91e-1f3fe.png","sheet_x":40,"sheet_y":43,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F91E-1F3FF","non_qualified":null,"image":"1f91e-1f3ff.png","sheet_x":40,"sheet_y":44,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"I LOVE YOU HAND SIGN","unified":"1F91F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f91f.png","sheet_x":40,"sheet_y":45,"short_name":"i_love_you_hand_sign","short_names":["i_love_you_hand_sign"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-partial","sort_order":186,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F91F-1F3FB","non_qualified":null,"image":"1f91f-1f3fb.png","sheet_x":40,"sheet_y":46,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F91F-1F3FC","non_qualified":null,"image":"1f91f-1f3fc.png","sheet_x":40,"sheet_y":47,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F91F-1F3FD","non_qualified":null,"image":"1f91f-1f3fd.png","sheet_x":40,"sheet_y":48,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F91F-1F3FE","non_qualified":null,"image":"1f91f-1f3fe.png","sheet_x":40,"sheet_y":49,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F91F-1F3FF","non_qualified":null,"image":"1f91f-1f3ff.png","sheet_x":40,"sheet_y":50,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FACE WITH COWBOY HAT","unified":"1F920","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f920.png","sheet_x":40,"sheet_y":51,"short_name":"face_with_cowboy_hat","short_names":["face_with_cowboy_hat"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-hat","sort_order":70,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOWN FACE","unified":"1F921","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f921.png","sheet_x":40,"sheet_y":52,"short_name":"clown_face","short_names":["clown_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-costume","sort_order":111,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NAUSEATED FACE","unified":"1F922","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f922.png","sheet_x":40,"sheet_y":53,"short_name":"nauseated_face","short_names":["nauseated_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":61,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROLLING ON THE FLOOR LAUGHING","unified":"1F923","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f923.png","sheet_x":40,"sheet_y":54,"short_name":"rolling_on_the_floor_laughing","short_names":["rolling_on_the_floor_laughing"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":7,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DROOLING FACE","unified":"1F924","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f924.png","sheet_x":40,"sheet_y":55,"short_name":"drooling_face","short_names":["drooling_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-sleepy","sort_order":56,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LYING FACE","unified":"1F925","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f925.png","sheet_x":40,"sheet_y":56,"short_name":"lying_face","short_names":["lying_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":49,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN FACEPALMING","unified":"1F926-200D-2640-FE0F","non_qualified":"1F926-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f926-200d-2640-fe0f.png","sheet_x":40,"sheet_y":57,"short_name":"woman-facepalming","short_names":["woman-facepalming"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":284,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F926-1F3FB-200D-2640-FE0F","non_qualified":"1F926-1F3FB-200D-2640","image":"1f926-1f3fb-200d-2640-fe0f.png","sheet_x":40,"sheet_y":58,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F926-1F3FC-200D-2640-FE0F","non_qualified":"1F926-1F3FC-200D-2640","image":"1f926-1f3fc-200d-2640-fe0f.png","sheet_x":40,"sheet_y":59,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F926-1F3FD-200D-2640-FE0F","non_qualified":"1F926-1F3FD-200D-2640","image":"1f926-1f3fd-200d-2640-fe0f.png","sheet_x":40,"sheet_y":60,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F926-1F3FE-200D-2640-FE0F","non_qualified":"1F926-1F3FE-200D-2640","image":"1f926-1f3fe-200d-2640-fe0f.png","sheet_x":40,"sheet_y":61,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F926-1F3FF-200D-2640-FE0F","non_qualified":"1F926-1F3FF-200D-2640","image":"1f926-1f3ff-200d-2640-fe0f.png","sheet_x":41,"sheet_y":0,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN FACEPALMING","unified":"1F926-200D-2642-FE0F","non_qualified":"1F926-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f926-200d-2642-fe0f.png","sheet_x":41,"sheet_y":1,"short_name":"man-facepalming","short_names":["man-facepalming"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":283,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F926-1F3FB-200D-2642-FE0F","non_qualified":"1F926-1F3FB-200D-2642","image":"1f926-1f3fb-200d-2642-fe0f.png","sheet_x":41,"sheet_y":2,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F926-1F3FC-200D-2642-FE0F","non_qualified":"1F926-1F3FC-200D-2642","image":"1f926-1f3fc-200d-2642-fe0f.png","sheet_x":41,"sheet_y":3,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F926-1F3FD-200D-2642-FE0F","non_qualified":"1F926-1F3FD-200D-2642","image":"1f926-1f3fd-200d-2642-fe0f.png","sheet_x":41,"sheet_y":4,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F926-1F3FE-200D-2642-FE0F","non_qualified":"1F926-1F3FE-200D-2642","image":"1f926-1f3fe-200d-2642-fe0f.png","sheet_x":41,"sheet_y":5,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F926-1F3FF-200D-2642-FE0F","non_qualified":"1F926-1F3FF-200D-2642","image":"1f926-1f3ff-200d-2642-fe0f.png","sheet_x":41,"sheet_y":6,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FACE PALM","unified":"1F926","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f926.png","sheet_x":41,"sheet_y":7,"short_name":"face_palm","short_names":["face_palm"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":282,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F926-1F3FB","non_qualified":null,"image":"1f926-1f3fb.png","sheet_x":41,"sheet_y":8,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F926-1F3FC","non_qualified":null,"image":"1f926-1f3fc.png","sheet_x":41,"sheet_y":9,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F926-1F3FD","non_qualified":null,"image":"1f926-1f3fd.png","sheet_x":41,"sheet_y":10,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F926-1F3FE","non_qualified":null,"image":"1f926-1f3fe.png","sheet_x":41,"sheet_y":11,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F926-1F3FF","non_qualified":null,"image":"1f926-1f3ff.png","sheet_x":41,"sheet_y":12,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SNEEZING FACE","unified":"1F927","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f927.png","sheet_x":41,"sheet_y":13,"short_name":"sneezing_face","short_names":["sneezing_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":63,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH ONE EYEBROW RAISED","unified":"1F928","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f928.png","sheet_x":41,"sheet_y":14,"short_name":"face_with_raised_eyebrow","short_names":["face_with_raised_eyebrow","face_with_one_eyebrow_raised"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":38,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GRINNING FACE WITH STAR EYES","unified":"1F929","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f929.png","sheet_x":41,"sheet_y":15,"short_name":"star-struck","short_names":["star-struck","grinning_face_with_star_eyes"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-affection","sort_order":17,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GRINNING FACE WITH ONE LARGE AND ONE SMALL EYE","unified":"1F92A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f92a.png","sheet_x":41,"sheet_y":16,"short_name":"zany_face","short_names":["zany_face","grinning_face_with_one_large_and_one_small_eye"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-tongue","sort_order":27,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH FINGER COVERING CLOSED LIPS","unified":"1F92B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f92b.png","sheet_x":41,"sheet_y":17,"short_name":"shushing_face","short_names":["shushing_face","face_with_finger_covering_closed_lips"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-hand","sort_order":34,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SERIOUS FACE WITH SYMBOLS COVERING MOUTH","unified":"1F92C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f92c.png","sheet_x":41,"sheet_y":18,"short_name":"face_with_symbols_on_mouth","short_names":["face_with_symbols_on_mouth","serious_face_with_symbols_covering_mouth"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-negative","sort_order":105,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH SMILING EYES AND HAND COVERING MOUTH","unified":"1F92D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f92d.png","sheet_x":41,"sheet_y":19,"short_name":"face_with_hand_over_mouth","short_names":["face_with_hand_over_mouth","smiling_face_with_smiling_eyes_and_hand_covering_mouth"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-hand","sort_order":31,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH OPEN MOUTH VOMITING","unified":"1F92E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f92e.png","sheet_x":41,"sheet_y":20,"short_name":"face_vomiting","short_names":["face_vomiting","face_with_open_mouth_vomiting"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":62,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHOCKED FACE WITH EXPLODING HEAD","unified":"1F92F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f92f.png","sheet_x":41,"sheet_y":21,"short_name":"exploding_head","short_names":["exploding_head","shocked_face_with_exploding_head"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":69,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PREGNANT WOMAN","unified":"1F930","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f930.png","sheet_x":41,"sheet_y":22,"short_name":"pregnant_woman","short_names":["pregnant_woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":363,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F930-1F3FB","non_qualified":null,"image":"1f930-1f3fb.png","sheet_x":41,"sheet_y":23,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F930-1F3FC","non_qualified":null,"image":"1f930-1f3fc.png","sheet_x":41,"sheet_y":24,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F930-1F3FD","non_qualified":null,"image":"1f930-1f3fd.png","sheet_x":41,"sheet_y":25,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F930-1F3FE","non_qualified":null,"image":"1f930-1f3fe.png","sheet_x":41,"sheet_y":26,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F930-1F3FF","non_qualified":null,"image":"1f930-1f3ff.png","sheet_x":41,"sheet_y":27,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"BREAST-FEEDING","unified":"1F931","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f931.png","sheet_x":41,"sheet_y":28,"short_name":"breast-feeding","short_names":["breast-feeding"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":366,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F931-1F3FB","non_qualified":null,"image":"1f931-1f3fb.png","sheet_x":41,"sheet_y":29,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F931-1F3FC","non_qualified":null,"image":"1f931-1f3fc.png","sheet_x":41,"sheet_y":30,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F931-1F3FD","non_qualified":null,"image":"1f931-1f3fd.png","sheet_x":41,"sheet_y":31,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F931-1F3FE","non_qualified":null,"image":"1f931-1f3fe.png","sheet_x":41,"sheet_y":32,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F931-1F3FF","non_qualified":null,"image":"1f931-1f3ff.png","sheet_x":41,"sheet_y":33,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PALMS UP TOGETHER","unified":"1F932","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f932.png","sheet_x":41,"sheet_y":34,"short_name":"palms_up_together","short_names":["palms_up_together"],"text":null,"texts":null,"category":"People & Body","subcategory":"hands","sort_order":206,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F932-1F3FB","non_qualified":null,"image":"1f932-1f3fb.png","sheet_x":41,"sheet_y":35,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F932-1F3FC","non_qualified":null,"image":"1f932-1f3fc.png","sheet_x":41,"sheet_y":36,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F932-1F3FD","non_qualified":null,"image":"1f932-1f3fd.png","sheet_x":41,"sheet_y":37,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F932-1F3FE","non_qualified":null,"image":"1f932-1f3fe.png","sheet_x":41,"sheet_y":38,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F932-1F3FF","non_qualified":null,"image":"1f932-1f3ff.png","sheet_x":41,"sheet_y":39,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SELFIE","unified":"1F933","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f933.png","sheet_x":41,"sheet_y":40,"short_name":"selfie","short_names":["selfie"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-prop","sort_order":211,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F933-1F3FB","non_qualified":null,"image":"1f933-1f3fb.png","sheet_x":41,"sheet_y":41,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F933-1F3FC","non_qualified":null,"image":"1f933-1f3fc.png","sheet_x":41,"sheet_y":42,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F933-1F3FD","non_qualified":null,"image":"1f933-1f3fd.png","sheet_x":41,"sheet_y":43,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F933-1F3FE","non_qualified":null,"image":"1f933-1f3fe.png","sheet_x":41,"sheet_y":44,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F933-1F3FF","non_qualified":null,"image":"1f933-1f3ff.png","sheet_x":41,"sheet_y":45,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PRINCE","unified":"1F934","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f934.png","sheet_x":41,"sheet_y":46,"short_name":"prince","short_names":["prince"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":350,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F934-1F3FB","non_qualified":null,"image":"1f934-1f3fb.png","sheet_x":41,"sheet_y":47,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F934-1F3FC","non_qualified":null,"image":"1f934-1f3fc.png","sheet_x":41,"sheet_y":48,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F934-1F3FD","non_qualified":null,"image":"1f934-1f3fd.png","sheet_x":41,"sheet_y":49,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F934-1F3FE","non_qualified":null,"image":"1f934-1f3fe.png","sheet_x":41,"sheet_y":50,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F934-1F3FF","non_qualified":null,"image":"1f934-1f3ff.png","sheet_x":41,"sheet_y":51,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN IN TUXEDO","unified":"1F935-200D-2640-FE0F","non_qualified":"1F935-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f935-200d-2640-fe0f.png","sheet_x":41,"sheet_y":52,"short_name":"woman_in_tuxedo","short_names":["woman_in_tuxedo"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":359,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F935-1F3FB-200D-2640-FE0F","non_qualified":"1F935-1F3FB-200D-2640","image":"1f935-1f3fb-200d-2640-fe0f.png","sheet_x":41,"sheet_y":53,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F935-1F3FC-200D-2640-FE0F","non_qualified":"1F935-1F3FC-200D-2640","image":"1f935-1f3fc-200d-2640-fe0f.png","sheet_x":41,"sheet_y":54,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F935-1F3FD-200D-2640-FE0F","non_qualified":"1F935-1F3FD-200D-2640","image":"1f935-1f3fd-200d-2640-fe0f.png","sheet_x":41,"sheet_y":55,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F935-1F3FE-200D-2640-FE0F","non_qualified":"1F935-1F3FE-200D-2640","image":"1f935-1f3fe-200d-2640-fe0f.png","sheet_x":41,"sheet_y":56,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F935-1F3FF-200D-2640-FE0F","non_qualified":"1F935-1F3FF-200D-2640","image":"1f935-1f3ff-200d-2640-fe0f.png","sheet_x":41,"sheet_y":57,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN IN TUXEDO","unified":"1F935-200D-2642-FE0F","non_qualified":"1F935-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f935-200d-2642-fe0f.png","sheet_x":41,"sheet_y":58,"short_name":"man_in_tuxedo","short_names":["man_in_tuxedo"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":358,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F935-1F3FB-200D-2642-FE0F","non_qualified":"1F935-1F3FB-200D-2642","image":"1f935-1f3fb-200d-2642-fe0f.png","sheet_x":41,"sheet_y":59,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F935-1F3FC-200D-2642-FE0F","non_qualified":"1F935-1F3FC-200D-2642","image":"1f935-1f3fc-200d-2642-fe0f.png","sheet_x":41,"sheet_y":60,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F935-1F3FD-200D-2642-FE0F","non_qualified":"1F935-1F3FD-200D-2642","image":"1f935-1f3fd-200d-2642-fe0f.png","sheet_x":41,"sheet_y":61,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F935-1F3FE-200D-2642-FE0F","non_qualified":"1F935-1F3FE-200D-2642","image":"1f935-1f3fe-200d-2642-fe0f.png","sheet_x":42,"sheet_y":0,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F935-1F3FF-200D-2642-FE0F","non_qualified":"1F935-1F3FF-200D-2642","image":"1f935-1f3ff-200d-2642-fe0f.png","sheet_x":42,"sheet_y":1,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN IN TUXEDO","unified":"1F935","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f935.png","sheet_x":42,"sheet_y":2,"short_name":"person_in_tuxedo","short_names":["person_in_tuxedo"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":357,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F935-1F3FB","non_qualified":null,"image":"1f935-1f3fb.png","sheet_x":42,"sheet_y":3,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F935-1F3FC","non_qualified":null,"image":"1f935-1f3fc.png","sheet_x":42,"sheet_y":4,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F935-1F3FD","non_qualified":null,"image":"1f935-1f3fd.png","sheet_x":42,"sheet_y":5,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F935-1F3FE","non_qualified":null,"image":"1f935-1f3fe.png","sheet_x":42,"sheet_y":6,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F935-1F3FF","non_qualified":null,"image":"1f935-1f3ff.png","sheet_x":42,"sheet_y":7,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MOTHER CHRISTMAS","unified":"1F936","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f936.png","sheet_x":42,"sheet_y":8,"short_name":"mrs_claus","short_names":["mrs_claus","mother_christmas"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":372,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F936-1F3FB","non_qualified":null,"image":"1f936-1f3fb.png","sheet_x":42,"sheet_y":9,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F936-1F3FC","non_qualified":null,"image":"1f936-1f3fc.png","sheet_x":42,"sheet_y":10,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F936-1F3FD","non_qualified":null,"image":"1f936-1f3fd.png","sheet_x":42,"sheet_y":11,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F936-1F3FE","non_qualified":null,"image":"1f936-1f3fe.png","sheet_x":42,"sheet_y":12,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F936-1F3FF","non_qualified":null,"image":"1f936-1f3ff.png","sheet_x":42,"sheet_y":13,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN SHRUGGING","unified":"1F937-200D-2640-FE0F","non_qualified":"1F937-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f937-200d-2640-fe0f.png","sheet_x":42,"sheet_y":14,"short_name":"woman-shrugging","short_names":["woman-shrugging"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":287,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F937-1F3FB-200D-2640-FE0F","non_qualified":"1F937-1F3FB-200D-2640","image":"1f937-1f3fb-200d-2640-fe0f.png","sheet_x":42,"sheet_y":15,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F937-1F3FC-200D-2640-FE0F","non_qualified":"1F937-1F3FC-200D-2640","image":"1f937-1f3fc-200d-2640-fe0f.png","sheet_x":42,"sheet_y":16,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F937-1F3FD-200D-2640-FE0F","non_qualified":"1F937-1F3FD-200D-2640","image":"1f937-1f3fd-200d-2640-fe0f.png","sheet_x":42,"sheet_y":17,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F937-1F3FE-200D-2640-FE0F","non_qualified":"1F937-1F3FE-200D-2640","image":"1f937-1f3fe-200d-2640-fe0f.png","sheet_x":42,"sheet_y":18,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F937-1F3FF-200D-2640-FE0F","non_qualified":"1F937-1F3FF-200D-2640","image":"1f937-1f3ff-200d-2640-fe0f.png","sheet_x":42,"sheet_y":19,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN SHRUGGING","unified":"1F937-200D-2642-FE0F","non_qualified":"1F937-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f937-200d-2642-fe0f.png","sheet_x":42,"sheet_y":20,"short_name":"man-shrugging","short_names":["man-shrugging"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":286,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F937-1F3FB-200D-2642-FE0F","non_qualified":"1F937-1F3FB-200D-2642","image":"1f937-1f3fb-200d-2642-fe0f.png","sheet_x":42,"sheet_y":21,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F937-1F3FC-200D-2642-FE0F","non_qualified":"1F937-1F3FC-200D-2642","image":"1f937-1f3fc-200d-2642-fe0f.png","sheet_x":42,"sheet_y":22,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F937-1F3FD-200D-2642-FE0F","non_qualified":"1F937-1F3FD-200D-2642","image":"1f937-1f3fd-200d-2642-fe0f.png","sheet_x":42,"sheet_y":23,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F937-1F3FE-200D-2642-FE0F","non_qualified":"1F937-1F3FE-200D-2642","image":"1f937-1f3fe-200d-2642-fe0f.png","sheet_x":42,"sheet_y":24,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F937-1F3FF-200D-2642-FE0F","non_qualified":"1F937-1F3FF-200D-2642","image":"1f937-1f3ff-200d-2642-fe0f.png","sheet_x":42,"sheet_y":25,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SHRUG","unified":"1F937","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f937.png","sheet_x":42,"sheet_y":26,"short_name":"shrug","short_names":["shrug"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":285,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F937-1F3FB","non_qualified":null,"image":"1f937-1f3fb.png","sheet_x":42,"sheet_y":27,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F937-1F3FC","non_qualified":null,"image":"1f937-1f3fc.png","sheet_x":42,"sheet_y":28,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F937-1F3FD","non_qualified":null,"image":"1f937-1f3fd.png","sheet_x":42,"sheet_y":29,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F937-1F3FE","non_qualified":null,"image":"1f937-1f3fe.png","sheet_x":42,"sheet_y":30,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F937-1F3FF","non_qualified":null,"image":"1f937-1f3ff.png","sheet_x":42,"sheet_y":31,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN CARTWHEELING","unified":"1F938-200D-2640-FE0F","non_qualified":"1F938-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f938-200d-2640-fe0f.png","sheet_x":42,"sheet_y":32,"short_name":"woman-cartwheeling","short_names":["woman-cartwheeling"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":489,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F938-1F3FB-200D-2640-FE0F","non_qualified":"1F938-1F3FB-200D-2640","image":"1f938-1f3fb-200d-2640-fe0f.png","sheet_x":42,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F938-1F3FC-200D-2640-FE0F","non_qualified":"1F938-1F3FC-200D-2640","image":"1f938-1f3fc-200d-2640-fe0f.png","sheet_x":42,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F938-1F3FD-200D-2640-FE0F","non_qualified":"1F938-1F3FD-200D-2640","image":"1f938-1f3fd-200d-2640-fe0f.png","sheet_x":42,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F938-1F3FE-200D-2640-FE0F","non_qualified":"1F938-1F3FE-200D-2640","image":"1f938-1f3fe-200d-2640-fe0f.png","sheet_x":42,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F938-1F3FF-200D-2640-FE0F","non_qualified":"1F938-1F3FF-200D-2640","image":"1f938-1f3ff-200d-2640-fe0f.png","sheet_x":42,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN CARTWHEELING","unified":"1F938-200D-2642-FE0F","non_qualified":"1F938-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f938-200d-2642-fe0f.png","sheet_x":42,"sheet_y":38,"short_name":"man-cartwheeling","short_names":["man-cartwheeling"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":488,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F938-1F3FB-200D-2642-FE0F","non_qualified":"1F938-1F3FB-200D-2642","image":"1f938-1f3fb-200d-2642-fe0f.png","sheet_x":42,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F938-1F3FC-200D-2642-FE0F","non_qualified":"1F938-1F3FC-200D-2642","image":"1f938-1f3fc-200d-2642-fe0f.png","sheet_x":42,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F938-1F3FD-200D-2642-FE0F","non_qualified":"1F938-1F3FD-200D-2642","image":"1f938-1f3fd-200d-2642-fe0f.png","sheet_x":42,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F938-1F3FE-200D-2642-FE0F","non_qualified":"1F938-1F3FE-200D-2642","image":"1f938-1f3fe-200d-2642-fe0f.png","sheet_x":42,"sheet_y":42,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F938-1F3FF-200D-2642-FE0F","non_qualified":"1F938-1F3FF-200D-2642","image":"1f938-1f3ff-200d-2642-fe0f.png","sheet_x":42,"sheet_y":43,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON DOING CARTWHEEL","unified":"1F938","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f938.png","sheet_x":42,"sheet_y":44,"short_name":"person_doing_cartwheel","short_names":["person_doing_cartwheel"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":487,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F938-1F3FB","non_qualified":null,"image":"1f938-1f3fb.png","sheet_x":42,"sheet_y":45,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F938-1F3FC","non_qualified":null,"image":"1f938-1f3fc.png","sheet_x":42,"sheet_y":46,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F938-1F3FD","non_qualified":null,"image":"1f938-1f3fd.png","sheet_x":42,"sheet_y":47,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F938-1F3FE","non_qualified":null,"image":"1f938-1f3fe.png","sheet_x":42,"sheet_y":48,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F938-1F3FF","non_qualified":null,"image":"1f938-1f3ff.png","sheet_x":42,"sheet_y":49,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN JUGGLING","unified":"1F939-200D-2640-FE0F","non_qualified":"1F939-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f939-200d-2640-fe0f.png","sheet_x":42,"sheet_y":50,"short_name":"woman-juggling","short_names":["woman-juggling"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":501,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F939-1F3FB-200D-2640-FE0F","non_qualified":"1F939-1F3FB-200D-2640","image":"1f939-1f3fb-200d-2640-fe0f.png","sheet_x":42,"sheet_y":51,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F939-1F3FC-200D-2640-FE0F","non_qualified":"1F939-1F3FC-200D-2640","image":"1f939-1f3fc-200d-2640-fe0f.png","sheet_x":42,"sheet_y":52,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F939-1F3FD-200D-2640-FE0F","non_qualified":"1F939-1F3FD-200D-2640","image":"1f939-1f3fd-200d-2640-fe0f.png","sheet_x":42,"sheet_y":53,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F939-1F3FE-200D-2640-FE0F","non_qualified":"1F939-1F3FE-200D-2640","image":"1f939-1f3fe-200d-2640-fe0f.png","sheet_x":42,"sheet_y":54,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F939-1F3FF-200D-2640-FE0F","non_qualified":"1F939-1F3FF-200D-2640","image":"1f939-1f3ff-200d-2640-fe0f.png","sheet_x":42,"sheet_y":55,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN JUGGLING","unified":"1F939-200D-2642-FE0F","non_qualified":"1F939-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f939-200d-2642-fe0f.png","sheet_x":42,"sheet_y":56,"short_name":"man-juggling","short_names":["man-juggling"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":500,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F939-1F3FB-200D-2642-FE0F","non_qualified":"1F939-1F3FB-200D-2642","image":"1f939-1f3fb-200d-2642-fe0f.png","sheet_x":42,"sheet_y":57,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F939-1F3FC-200D-2642-FE0F","non_qualified":"1F939-1F3FC-200D-2642","image":"1f939-1f3fc-200d-2642-fe0f.png","sheet_x":42,"sheet_y":58,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F939-1F3FD-200D-2642-FE0F","non_qualified":"1F939-1F3FD-200D-2642","image":"1f939-1f3fd-200d-2642-fe0f.png","sheet_x":42,"sheet_y":59,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F939-1F3FE-200D-2642-FE0F","non_qualified":"1F939-1F3FE-200D-2642","image":"1f939-1f3fe-200d-2642-fe0f.png","sheet_x":42,"sheet_y":60,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F939-1F3FF-200D-2642-FE0F","non_qualified":"1F939-1F3FF-200D-2642","image":"1f939-1f3ff-200d-2642-fe0f.png","sheet_x":42,"sheet_y":61,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"JUGGLING","unified":"1F939","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f939.png","sheet_x":43,"sheet_y":0,"short_name":"juggling","short_names":["juggling"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":499,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F939-1F3FB","non_qualified":null,"image":"1f939-1f3fb.png","sheet_x":43,"sheet_y":1,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F939-1F3FC","non_qualified":null,"image":"1f939-1f3fc.png","sheet_x":43,"sheet_y":2,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F939-1F3FD","non_qualified":null,"image":"1f939-1f3fd.png","sheet_x":43,"sheet_y":3,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F939-1F3FE","non_qualified":null,"image":"1f939-1f3fe.png","sheet_x":43,"sheet_y":4,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F939-1F3FF","non_qualified":null,"image":"1f939-1f3ff.png","sheet_x":43,"sheet_y":5,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FENCER","unified":"1F93A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93a.png","sheet_x":43,"sheet_y":6,"short_name":"fencer","short_names":["fencer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":459,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMEN WRESTLING","unified":"1F93C-200D-2640-FE0F","non_qualified":"1F93C-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93c-200d-2640-fe0f.png","sheet_x":43,"sheet_y":7,"short_name":"woman-wrestling","short_names":["woman-wrestling"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":492,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MEN WRESTLING","unified":"1F93C-200D-2642-FE0F","non_qualified":"1F93C-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93c-200d-2642-fe0f.png","sheet_x":43,"sheet_y":8,"short_name":"man-wrestling","short_names":["man-wrestling"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":491,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WRESTLERS","unified":"1F93C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93c.png","sheet_x":43,"sheet_y":9,"short_name":"wrestlers","short_names":["wrestlers"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":490,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN PLAYING WATER POLO","unified":"1F93D-200D-2640-FE0F","non_qualified":"1F93D-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93d-200d-2640-fe0f.png","sheet_x":43,"sheet_y":10,"short_name":"woman-playing-water-polo","short_names":["woman-playing-water-polo"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":495,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F93D-1F3FB-200D-2640-FE0F","non_qualified":"1F93D-1F3FB-200D-2640","image":"1f93d-1f3fb-200d-2640-fe0f.png","sheet_x":43,"sheet_y":11,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F93D-1F3FC-200D-2640-FE0F","non_qualified":"1F93D-1F3FC-200D-2640","image":"1f93d-1f3fc-200d-2640-fe0f.png","sheet_x":43,"sheet_y":12,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F93D-1F3FD-200D-2640-FE0F","non_qualified":"1F93D-1F3FD-200D-2640","image":"1f93d-1f3fd-200d-2640-fe0f.png","sheet_x":43,"sheet_y":13,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F93D-1F3FE-200D-2640-FE0F","non_qualified":"1F93D-1F3FE-200D-2640","image":"1f93d-1f3fe-200d-2640-fe0f.png","sheet_x":43,"sheet_y":14,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F93D-1F3FF-200D-2640-FE0F","non_qualified":"1F93D-1F3FF-200D-2640","image":"1f93d-1f3ff-200d-2640-fe0f.png","sheet_x":43,"sheet_y":15,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN PLAYING WATER POLO","unified":"1F93D-200D-2642-FE0F","non_qualified":"1F93D-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93d-200d-2642-fe0f.png","sheet_x":43,"sheet_y":16,"short_name":"man-playing-water-polo","short_names":["man-playing-water-polo"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":494,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F93D-1F3FB-200D-2642-FE0F","non_qualified":"1F93D-1F3FB-200D-2642","image":"1f93d-1f3fb-200d-2642-fe0f.png","sheet_x":43,"sheet_y":17,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F93D-1F3FC-200D-2642-FE0F","non_qualified":"1F93D-1F3FC-200D-2642","image":"1f93d-1f3fc-200d-2642-fe0f.png","sheet_x":43,"sheet_y":18,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F93D-1F3FD-200D-2642-FE0F","non_qualified":"1F93D-1F3FD-200D-2642","image":"1f93d-1f3fd-200d-2642-fe0f.png","sheet_x":43,"sheet_y":19,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F93D-1F3FE-200D-2642-FE0F","non_qualified":"1F93D-1F3FE-200D-2642","image":"1f93d-1f3fe-200d-2642-fe0f.png","sheet_x":43,"sheet_y":20,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F93D-1F3FF-200D-2642-FE0F","non_qualified":"1F93D-1F3FF-200D-2642","image":"1f93d-1f3ff-200d-2642-fe0f.png","sheet_x":43,"sheet_y":21,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WATER POLO","unified":"1F93D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93d.png","sheet_x":43,"sheet_y":22,"short_name":"water_polo","short_names":["water_polo"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":493,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F93D-1F3FB","non_qualified":null,"image":"1f93d-1f3fb.png","sheet_x":43,"sheet_y":23,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F93D-1F3FC","non_qualified":null,"image":"1f93d-1f3fc.png","sheet_x":43,"sheet_y":24,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F93D-1F3FD","non_qualified":null,"image":"1f93d-1f3fd.png","sheet_x":43,"sheet_y":25,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F93D-1F3FE","non_qualified":null,"image":"1f93d-1f3fe.png","sheet_x":43,"sheet_y":26,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F93D-1F3FF","non_qualified":null,"image":"1f93d-1f3ff.png","sheet_x":43,"sheet_y":27,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN PLAYING HANDBALL","unified":"1F93E-200D-2640-FE0F","non_qualified":"1F93E-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93e-200d-2640-fe0f.png","sheet_x":43,"sheet_y":28,"short_name":"woman-playing-handball","short_names":["woman-playing-handball"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":498,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F93E-1F3FB-200D-2640-FE0F","non_qualified":"1F93E-1F3FB-200D-2640","image":"1f93e-1f3fb-200d-2640-fe0f.png","sheet_x":43,"sheet_y":29,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F93E-1F3FC-200D-2640-FE0F","non_qualified":"1F93E-1F3FC-200D-2640","image":"1f93e-1f3fc-200d-2640-fe0f.png","sheet_x":43,"sheet_y":30,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F93E-1F3FD-200D-2640-FE0F","non_qualified":"1F93E-1F3FD-200D-2640","image":"1f93e-1f3fd-200d-2640-fe0f.png","sheet_x":43,"sheet_y":31,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F93E-1F3FE-200D-2640-FE0F","non_qualified":"1F93E-1F3FE-200D-2640","image":"1f93e-1f3fe-200d-2640-fe0f.png","sheet_x":43,"sheet_y":32,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F93E-1F3FF-200D-2640-FE0F","non_qualified":"1F93E-1F3FF-200D-2640","image":"1f93e-1f3ff-200d-2640-fe0f.png","sheet_x":43,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN PLAYING HANDBALL","unified":"1F93E-200D-2642-FE0F","non_qualified":"1F93E-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93e-200d-2642-fe0f.png","sheet_x":43,"sheet_y":34,"short_name":"man-playing-handball","short_names":["man-playing-handball"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":497,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F93E-1F3FB-200D-2642-FE0F","non_qualified":"1F93E-1F3FB-200D-2642","image":"1f93e-1f3fb-200d-2642-fe0f.png","sheet_x":43,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F93E-1F3FC-200D-2642-FE0F","non_qualified":"1F93E-1F3FC-200D-2642","image":"1f93e-1f3fc-200d-2642-fe0f.png","sheet_x":43,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F93E-1F3FD-200D-2642-FE0F","non_qualified":"1F93E-1F3FD-200D-2642","image":"1f93e-1f3fd-200d-2642-fe0f.png","sheet_x":43,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F93E-1F3FE-200D-2642-FE0F","non_qualified":"1F93E-1F3FE-200D-2642","image":"1f93e-1f3fe-200d-2642-fe0f.png","sheet_x":43,"sheet_y":38,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F93E-1F3FF-200D-2642-FE0F","non_qualified":"1F93E-1F3FF-200D-2642","image":"1f93e-1f3ff-200d-2642-fe0f.png","sheet_x":43,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"HANDBALL","unified":"1F93E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93e.png","sheet_x":43,"sheet_y":40,"short_name":"handball","short_names":["handball"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":496,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F93E-1F3FB","non_qualified":null,"image":"1f93e-1f3fb.png","sheet_x":43,"sheet_y":41,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F93E-1F3FC","non_qualified":null,"image":"1f93e-1f3fc.png","sheet_x":43,"sheet_y":42,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F93E-1F3FD","non_qualified":null,"image":"1f93e-1f3fd.png","sheet_x":43,"sheet_y":43,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F93E-1F3FE","non_qualified":null,"image":"1f93e-1f3fe.png","sheet_x":43,"sheet_y":44,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F93E-1F3FF","non_qualified":null,"image":"1f93e-1f3ff.png","sheet_x":43,"sheet_y":45,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"DIVING MASK","unified":"1F93F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f93f.png","sheet_x":43,"sheet_y":46,"short_name":"diving_mask","short_names":["diving_mask"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1114,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WILTED FLOWER","unified":"1F940","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f940.png","sheet_x":43,"sheet_y":47,"short_name":"wilted_flower","short_names":["wilted_flower"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":690,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DRUM WITH DRUMSTICKS","unified":"1F941","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f941.png","sheet_x":43,"sheet_y":48,"short_name":"drum_with_drumsticks","short_names":["drum_with_drumsticks"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1222,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLINKING GLASSES","unified":"1F942","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f942.png","sheet_x":43,"sheet_y":49,"short_name":"clinking_glasses","short_names":["clinking_glasses"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":832,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TUMBLER GLASS","unified":"1F943","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f943.png","sheet_x":43,"sheet_y":50,"short_name":"tumbler_glass","short_names":["tumbler_glass"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":833,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPOON","unified":"1F944","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f944.png","sheet_x":43,"sheet_y":51,"short_name":"spoon","short_names":["spoon"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"dishware","sort_order":843,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GOAL NET","unified":"1F945","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f945.png","sheet_x":43,"sheet_y":52,"short_name":"goal_net","short_names":["goal_net"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1110,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FIRST PLACE MEDAL","unified":"1F947","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f947.png","sheet_x":43,"sheet_y":53,"short_name":"first_place_medal","short_names":["first_place_medal"],"text":null,"texts":null,"category":"Activities","subcategory":"award-medal","sort_order":1089,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SECOND PLACE MEDAL","unified":"1F948","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f948.png","sheet_x":43,"sheet_y":54,"short_name":"second_place_medal","short_names":["second_place_medal"],"text":null,"texts":null,"category":"Activities","subcategory":"award-medal","sort_order":1090,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"THIRD PLACE MEDAL","unified":"1F949","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f949.png","sheet_x":43,"sheet_y":55,"short_name":"third_place_medal","short_names":["third_place_medal"],"text":null,"texts":null,"category":"Activities","subcategory":"award-medal","sort_order":1091,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOXING GLOVE","unified":"1F94A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f94a.png","sheet_x":43,"sheet_y":56,"short_name":"boxing_glove","short_names":["boxing_glove"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1108,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MARTIAL ARTS UNIFORM","unified":"1F94B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f94b.png","sheet_x":43,"sheet_y":57,"short_name":"martial_arts_uniform","short_names":["martial_arts_uniform"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1109,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CURLING STONE","unified":"1F94C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f94c.png","sheet_x":43,"sheet_y":58,"short_name":"curling_stone","short_names":["curling_stone"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1118,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LACROSSE STICK AND BALL","unified":"1F94D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f94d.png","sheet_x":43,"sheet_y":59,"short_name":"lacrosse","short_names":["lacrosse"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1105,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SOFTBALL","unified":"1F94E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f94e.png","sheet_x":43,"sheet_y":60,"short_name":"softball","short_names":["softball"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1094,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLYING DISC","unified":"1F94F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f94f.png","sheet_x":43,"sheet_y":61,"short_name":"flying_disc","short_names":["flying_disc"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1100,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CROISSANT","unified":"1F950","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f950.png","sheet_x":44,"sheet_y":0,"short_name":"croissant","short_names":["croissant"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":751,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AVOCADO","unified":"1F951","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f951.png","sheet_x":44,"sheet_y":1,"short_name":"avocado","short_names":["avocado"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":732,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CUCUMBER","unified":"1F952","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f952.png","sheet_x":44,"sheet_y":2,"short_name":"cucumber","short_names":["cucumber"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":739,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BACON","unified":"1F953","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f953.png","sheet_x":44,"sheet_y":3,"short_name":"bacon","short_names":["bacon"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":762,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POTATO","unified":"1F954","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f954.png","sheet_x":44,"sheet_y":4,"short_name":"potato","short_names":["potato"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":734,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CARROT","unified":"1F955","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f955.png","sheet_x":44,"sheet_y":5,"short_name":"carrot","short_names":["carrot"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":735,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BAGUETTE BREAD","unified":"1F956","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f956.png","sheet_x":44,"sheet_y":6,"short_name":"baguette_bread","short_names":["baguette_bread"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":752,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GREEN SALAD","unified":"1F957","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f957.png","sheet_x":44,"sheet_y":7,"short_name":"green_salad","short_names":["green_salad"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":779,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHALLOW PAN OF FOOD","unified":"1F958","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f958.png","sheet_x":44,"sheet_y":8,"short_name":"shallow_pan_of_food","short_names":["shallow_pan_of_food"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":775,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STUFFED FLATBREAD","unified":"1F959","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f959.png","sheet_x":44,"sheet_y":9,"short_name":"stuffed_flatbread","short_names":["stuffed_flatbread"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":771,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EGG","unified":"1F95A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f95a.png","sheet_x":44,"sheet_y":10,"short_name":"egg","short_names":["egg"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":773,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GLASS OF MILK","unified":"1F95B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f95b.png","sheet_x":44,"sheet_y":11,"short_name":"glass_of_milk","short_names":["glass_of_milk"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":821,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PEANUTS","unified":"1F95C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f95c.png","sheet_x":44,"sheet_y":12,"short_name":"peanuts","short_names":["peanuts"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":744,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KIWIFRUIT","unified":"1F95D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f95d.png","sheet_x":44,"sheet_y":13,"short_name":"kiwifruit","short_names":["kiwifruit"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":728,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PANCAKES","unified":"1F95E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f95e.png","sheet_x":44,"sheet_y":14,"short_name":"pancakes","short_names":["pancakes"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":756,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DUMPLING","unified":"1F95F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f95f.png","sheet_x":44,"sheet_y":15,"short_name":"dumpling","short_names":["dumpling"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":798,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FORTUNE COOKIE","unified":"1F960","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f960.png","sheet_x":44,"sheet_y":16,"short_name":"fortune_cookie","short_names":["fortune_cookie"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":799,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TAKEOUT BOX","unified":"1F961","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f961.png","sheet_x":44,"sheet_y":17,"short_name":"takeout_box","short_names":["takeout_box"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":800,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHOPSTICKS","unified":"1F962","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f962.png","sheet_x":44,"sheet_y":18,"short_name":"chopsticks","short_names":["chopsticks"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"dishware","sort_order":840,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOWL WITH SPOON","unified":"1F963","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f963.png","sheet_x":44,"sheet_y":19,"short_name":"bowl_with_spoon","short_names":["bowl_with_spoon"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":778,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CUP WITH STRAW","unified":"1F964","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f964.png","sheet_x":44,"sheet_y":20,"short_name":"cup_with_straw","short_names":["cup_with_straw"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":835,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COCONUT","unified":"1F965","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f965.png","sheet_x":44,"sheet_y":21,"short_name":"coconut","short_names":["coconut"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":731,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BROCCOLI","unified":"1F966","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f966.png","sheet_x":44,"sheet_y":22,"short_name":"broccoli","short_names":["broccoli"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":741,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PIE","unified":"1F967","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f967.png","sheet_x":44,"sheet_y":23,"short_name":"pie","short_names":["pie"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":814,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PRETZEL","unified":"1F968","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f968.png","sheet_x":44,"sheet_y":24,"short_name":"pretzel","short_names":["pretzel"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":754,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CUT OF MEAT","unified":"1F969","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f969.png","sheet_x":44,"sheet_y":25,"short_name":"cut_of_meat","short_names":["cut_of_meat"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":761,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SANDWICH","unified":"1F96A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f96a.png","sheet_x":44,"sheet_y":26,"short_name":"sandwich","short_names":["sandwich"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":767,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CANNED FOOD","unified":"1F96B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f96b.png","sheet_x":44,"sheet_y":27,"short_name":"canned_food","short_names":["canned_food"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":783,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEAFY GREEN","unified":"1F96C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f96c.png","sheet_x":44,"sheet_y":28,"short_name":"leafy_green","short_names":["leafy_green"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":740,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MANGO","unified":"1F96D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f96d.png","sheet_x":44,"sheet_y":29,"short_name":"mango","short_names":["mango"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":720,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOON CAKE","unified":"1F96E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f96e.png","sheet_x":44,"sheet_y":30,"short_name":"moon_cake","short_names":["moon_cake"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-asian","sort_order":796,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BAGEL","unified":"1F96F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f96f.png","sheet_x":44,"sheet_y":31,"short_name":"bagel","short_names":["bagel"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":755,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH SMILING EYES AND THREE HEARTS","unified":"1F970","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f970.png","sheet_x":44,"sheet_y":32,"short_name":"smiling_face_with_3_hearts","short_names":["smiling_face_with_3_hearts"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-affection","sort_order":15,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"YAWNING FACE","unified":"1F971","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f971.png","sheet_x":44,"sheet_y":33,"short_name":"yawning_face","short_names":["yawning_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":101,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SMILING FACE WITH TEAR","unified":"1F972","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f972.png","sheet_x":44,"sheet_y":34,"short_name":"smiling_face_with_tear","short_names":["smiling_face_with_tear"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-affection","sort_order":23,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH PARTY HORN AND PARTY HAT","unified":"1F973","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f973.png","sheet_x":44,"sheet_y":35,"short_name":"partying_face","short_names":["partying_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-hat","sort_order":71,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH UNEVEN EYES AND WAVY MOUTH","unified":"1F974","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f974.png","sheet_x":44,"sheet_y":36,"short_name":"woozy_face","short_names":["woozy_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":66,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OVERHEATED FACE","unified":"1F975","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f975.png","sheet_x":44,"sheet_y":37,"short_name":"hot_face","short_names":["hot_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":64,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FREEZING FACE","unified":"1F976","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f976.png","sheet_x":44,"sheet_y":38,"short_name":"cold_face","short_names":["cold_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-unwell","sort_order":65,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NINJA","unified":"1F977","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f977.png","sheet_x":44,"sheet_y":39,"short_name":"ninja","short_names":["ninja"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":345,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F977-1F3FB","non_qualified":null,"image":"1f977-1f3fb.png","sheet_x":44,"sheet_y":40,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F977-1F3FC","non_qualified":null,"image":"1f977-1f3fc.png","sheet_x":44,"sheet_y":41,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F977-1F3FD","non_qualified":null,"image":"1f977-1f3fd.png","sheet_x":44,"sheet_y":42,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F977-1F3FE","non_qualified":null,"image":"1f977-1f3fe.png","sheet_x":44,"sheet_y":43,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F977-1F3FF","non_qualified":null,"image":"1f977-1f3ff.png","sheet_x":44,"sheet_y":44,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"DISGUISED FACE","unified":"1F978","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f978.png","sheet_x":44,"sheet_y":45,"short_name":"disguised_face","short_names":["disguised_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-hat","sort_order":72,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE HOLDING BACK TEARS","unified":"1F979","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f979.png","sheet_x":44,"sheet_y":46,"short_name":"face_holding_back_tears","short_names":["face_holding_back_tears"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":86,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH PLEADING EYES","unified":"1F97A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f97a.png","sheet_x":44,"sheet_y":47,"short_name":"pleading_face","short_names":["pleading_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":85,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SARI","unified":"1F97B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f97b.png","sheet_x":44,"sheet_y":48,"short_name":"sari","short_names":["sari"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1164,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LAB COAT","unified":"1F97C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f97c.png","sheet_x":44,"sheet_y":49,"short_name":"lab_coat","short_names":["lab_coat"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1153,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GOGGLES","unified":"1F97D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f97d.png","sheet_x":44,"sheet_y":50,"short_name":"goggles","short_names":["goggles"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1152,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HIKING BOOT","unified":"1F97E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f97e.png","sheet_x":44,"sheet_y":51,"short_name":"hiking_boot","short_names":["hiking_boot"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1179,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLAT SHOE","unified":"1F97F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f97f.png","sheet_x":44,"sheet_y":52,"short_name":"womans_flat_shoe","short_names":["womans_flat_shoe"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1180,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CRAB","unified":"1F980","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f980.png","sheet_x":44,"sheet_y":53,"short_name":"crab","short_names":["crab"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-marine","sort_order":801,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LION FACE","unified":"1F981","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f981.png","sheet_x":44,"sheet_y":54,"short_name":"lion_face","short_names":["lion_face"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":574,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SCORPION","unified":"1F982","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f982.png","sheet_x":44,"sheet_y":55,"short_name":"scorpion","short_names":["scorpion"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":679,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TURKEY","unified":"1F983","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f983.png","sheet_x":44,"sheet_y":56,"short_name":"turkey","short_names":["turkey"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":625,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"UNICORN FACE","unified":"1F984","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f984.png","sheet_x":44,"sheet_y":57,"short_name":"unicorn_face","short_names":["unicorn_face"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":582,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EAGLE","unified":"1F985","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f985.png","sheet_x":44,"sheet_y":58,"short_name":"eagle","short_names":["eagle"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":634,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DUCK","unified":"1F986","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f986.png","sheet_x":44,"sheet_y":59,"short_name":"duck","short_names":["duck"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":635,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BAT","unified":"1F987","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f987.png","sheet_x":44,"sheet_y":60,"short_name":"bat","short_names":["bat"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":614,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHARK","unified":"1F988","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f988.png","sheet_x":44,"sheet_y":61,"short_name":"shark","short_names":["shark"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":663,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OWL","unified":"1F989","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f989.png","sheet_x":45,"sheet_y":0,"short_name":"owl","short_names":["owl"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":637,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FOX FACE","unified":"1F98A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f98a.png","sheet_x":45,"sheet_y":1,"short_name":"fox_face","short_names":["fox_face"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":569,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUTTERFLY","unified":"1F98B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f98b.png","sheet_x":45,"sheet_y":2,"short_name":"butterfly","short_names":["butterfly"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":669,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DEER","unified":"1F98C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f98c.png","sheet_x":45,"sheet_y":3,"short_name":"deer","short_names":["deer"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":584,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GORILLA","unified":"1F98D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f98d.png","sheet_x":45,"sheet_y":4,"short_name":"gorilla","short_names":["gorilla"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":561,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LIZARD","unified":"1F98E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f98e.png","sheet_x":45,"sheet_y":5,"short_name":"lizard","short_names":["lizard"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-reptile","sort_order":650,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RHINOCEROS","unified":"1F98F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f98f.png","sheet_x":45,"sheet_y":6,"short_name":"rhinoceros","short_names":["rhinoceros"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":603,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHRIMP","unified":"1F990","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f990.png","sheet_x":45,"sheet_y":7,"short_name":"shrimp","short_names":["shrimp"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-marine","sort_order":803,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SQUID","unified":"1F991","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f991.png","sheet_x":45,"sheet_y":8,"short_name":"squid","short_names":["squid"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-marine","sort_order":804,"added_in":"3.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GIRAFFE FACE","unified":"1F992","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f992.png","sheet_x":45,"sheet_y":9,"short_name":"giraffe_face","short_names":["giraffe_face"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":600,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ZEBRA FACE","unified":"1F993","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f993.png","sheet_x":45,"sheet_y":10,"short_name":"zebra_face","short_names":["zebra_face"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":583,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEDGEHOG","unified":"1F994","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f994.png","sheet_x":45,"sheet_y":11,"short_name":"hedgehog","short_names":["hedgehog"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":613,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SAUROPOD","unified":"1F995","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f995.png","sheet_x":45,"sheet_y":12,"short_name":"sauropod","short_names":["sauropod"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-reptile","sort_order":654,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"T-REX","unified":"1F996","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f996.png","sheet_x":45,"sheet_y":13,"short_name":"t-rex","short_names":["t-rex"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-reptile","sort_order":655,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CRICKET","unified":"1F997","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f997.png","sheet_x":45,"sheet_y":14,"short_name":"cricket","short_names":["cricket"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":675,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KANGAROO","unified":"1F998","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f998.png","sheet_x":45,"sheet_y":15,"short_name":"kangaroo","short_names":["kangaroo"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":622,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LLAMA","unified":"1F999","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f999.png","sheet_x":45,"sheet_y":16,"short_name":"llama","short_names":["llama"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":599,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PEACOCK","unified":"1F99A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f99a.png","sheet_x":45,"sheet_y":17,"short_name":"peacock","short_names":["peacock"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":641,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HIPPOPOTAMUS","unified":"1F99B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f99b.png","sheet_x":45,"sheet_y":18,"short_name":"hippopotamus","short_names":["hippopotamus"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":604,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PARROT","unified":"1F99C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f99c.png","sheet_x":45,"sheet_y":19,"short_name":"parrot","short_names":["parrot"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":642,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RACCOON","unified":"1F99D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f99d.png","sheet_x":45,"sheet_y":20,"short_name":"raccoon","short_names":["raccoon"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":570,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOBSTER","unified":"1F99E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f99e.png","sheet_x":45,"sheet_y":21,"short_name":"lobster","short_names":["lobster"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-marine","sort_order":802,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOSQUITO","unified":"1F99F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f99f.png","sheet_x":45,"sheet_y":22,"short_name":"mosquito","short_names":["mosquito"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":680,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MICROBE","unified":"1F9A0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9a0.png","sheet_x":45,"sheet_y":23,"short_name":"microbe","short_names":["microbe"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":683,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BADGER","unified":"1F9A1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9a1.png","sheet_x":45,"sheet_y":24,"short_name":"badger","short_names":["badger"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":623,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SWAN","unified":"1F9A2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9a2.png","sheet_x":45,"sheet_y":25,"short_name":"swan","short_names":["swan"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":636,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MAMMOTH","unified":"1F9A3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9a3.png","sheet_x":45,"sheet_y":26,"short_name":"mammoth","short_names":["mammoth"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":602,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DODO","unified":"1F9A4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9a4.png","sheet_x":45,"sheet_y":27,"short_name":"dodo","short_names":["dodo"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":638,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SLOTH","unified":"1F9A5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9a5.png","sheet_x":45,"sheet_y":28,"short_name":"sloth","short_names":["sloth"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":619,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OTTER","unified":"1F9A6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9a6.png","sheet_x":45,"sheet_y":29,"short_name":"otter","short_names":["otter"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":620,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ORANGUTAN","unified":"1F9A7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9a7.png","sheet_x":45,"sheet_y":30,"short_name":"orangutan","short_names":["orangutan"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":562,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SKUNK","unified":"1F9A8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9a8.png","sheet_x":45,"sheet_y":31,"short_name":"skunk","short_names":["skunk"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":621,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLAMINGO","unified":"1F9A9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9a9.png","sheet_x":45,"sheet_y":32,"short_name":"flamingo","short_names":["flamingo"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":640,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OYSTER","unified":"1F9AA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9aa.png","sheet_x":45,"sheet_y":33,"short_name":"oyster","short_names":["oyster"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-marine","sort_order":805,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BEAVER","unified":"1F9AB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ab.png","sheet_x":45,"sheet_y":34,"short_name":"beaver","short_names":["beaver"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":612,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BISON","unified":"1F9AC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ac.png","sheet_x":45,"sheet_y":35,"short_name":"bison","short_names":["bison"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":585,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SEAL","unified":"1F9AD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ad.png","sheet_x":45,"sheet_y":36,"short_name":"seal","short_names":["seal"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":659,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GUIDE DOG","unified":"1F9AE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ae.png","sheet_x":45,"sheet_y":37,"short_name":"guide_dog","short_names":["guide_dog"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":565,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PROBING CANE","unified":"1F9AF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9af.png","sheet_x":45,"sheet_y":38,"short_name":"probing_cane","short_names":["probing_cane"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1356,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BONE","unified":"1F9B4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9b4.png","sheet_x":45,"sheet_y":39,"short_name":"bone","short_names":["bone"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":224,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEG","unified":"1F9B5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9b5.png","sheet_x":45,"sheet_y":40,"short_name":"leg","short_names":["leg"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":215,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9B5-1F3FB","non_qualified":null,"image":"1f9b5-1f3fb.png","sheet_x":45,"sheet_y":41,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9B5-1F3FC","non_qualified":null,"image":"1f9b5-1f3fc.png","sheet_x":45,"sheet_y":42,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9B5-1F3FD","non_qualified":null,"image":"1f9b5-1f3fd.png","sheet_x":45,"sheet_y":43,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9B5-1F3FE","non_qualified":null,"image":"1f9b5-1f3fe.png","sheet_x":45,"sheet_y":44,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9B5-1F3FF","non_qualified":null,"image":"1f9b5-1f3ff.png","sheet_x":45,"sheet_y":45,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FOOT","unified":"1F9B6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9b6.png","sheet_x":45,"sheet_y":46,"short_name":"foot","short_names":["foot"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":216,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9B6-1F3FB","non_qualified":null,"image":"1f9b6-1f3fb.png","sheet_x":45,"sheet_y":47,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9B6-1F3FC","non_qualified":null,"image":"1f9b6-1f3fc.png","sheet_x":45,"sheet_y":48,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9B6-1F3FD","non_qualified":null,"image":"1f9b6-1f3fd.png","sheet_x":45,"sheet_y":49,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9B6-1F3FE","non_qualified":null,"image":"1f9b6-1f3fe.png","sheet_x":45,"sheet_y":50,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9B6-1F3FF","non_qualified":null,"image":"1f9b6-1f3ff.png","sheet_x":45,"sheet_y":51,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"TOOTH","unified":"1F9B7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9b7.png","sheet_x":45,"sheet_y":52,"short_name":"tooth","short_names":["tooth"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":223,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN SUPERHERO","unified":"1F9B8-200D-2640-FE0F","non_qualified":"1F9B8-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9b8-200d-2640-fe0f.png","sheet_x":45,"sheet_y":53,"short_name":"female_superhero","short_names":["female_superhero"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":376,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9B8-1F3FB-200D-2640-FE0F","non_qualified":"1F9B8-1F3FB-200D-2640","image":"1f9b8-1f3fb-200d-2640-fe0f.png","sheet_x":45,"sheet_y":54,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9B8-1F3FC-200D-2640-FE0F","non_qualified":"1F9B8-1F3FC-200D-2640","image":"1f9b8-1f3fc-200d-2640-fe0f.png","sheet_x":45,"sheet_y":55,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9B8-1F3FD-200D-2640-FE0F","non_qualified":"1F9B8-1F3FD-200D-2640","image":"1f9b8-1f3fd-200d-2640-fe0f.png","sheet_x":45,"sheet_y":56,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9B8-1F3FE-200D-2640-FE0F","non_qualified":"1F9B8-1F3FE-200D-2640","image":"1f9b8-1f3fe-200d-2640-fe0f.png","sheet_x":45,"sheet_y":57,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9B8-1F3FF-200D-2640-FE0F","non_qualified":"1F9B8-1F3FF-200D-2640","image":"1f9b8-1f3ff-200d-2640-fe0f.png","sheet_x":45,"sheet_y":58,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN SUPERHERO","unified":"1F9B8-200D-2642-FE0F","non_qualified":"1F9B8-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9b8-200d-2642-fe0f.png","sheet_x":45,"sheet_y":59,"short_name":"male_superhero","short_names":["male_superhero"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":375,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9B8-1F3FB-200D-2642-FE0F","non_qualified":"1F9B8-1F3FB-200D-2642","image":"1f9b8-1f3fb-200d-2642-fe0f.png","sheet_x":45,"sheet_y":60,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9B8-1F3FC-200D-2642-FE0F","non_qualified":"1F9B8-1F3FC-200D-2642","image":"1f9b8-1f3fc-200d-2642-fe0f.png","sheet_x":45,"sheet_y":61,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9B8-1F3FD-200D-2642-FE0F","non_qualified":"1F9B8-1F3FD-200D-2642","image":"1f9b8-1f3fd-200d-2642-fe0f.png","sheet_x":46,"sheet_y":0,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9B8-1F3FE-200D-2642-FE0F","non_qualified":"1F9B8-1F3FE-200D-2642","image":"1f9b8-1f3fe-200d-2642-fe0f.png","sheet_x":46,"sheet_y":1,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9B8-1F3FF-200D-2642-FE0F","non_qualified":"1F9B8-1F3FF-200D-2642","image":"1f9b8-1f3ff-200d-2642-fe0f.png","sheet_x":46,"sheet_y":2,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SUPERHERO","unified":"1F9B8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9b8.png","sheet_x":46,"sheet_y":3,"short_name":"superhero","short_names":["superhero"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":374,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9B8-1F3FB","non_qualified":null,"image":"1f9b8-1f3fb.png","sheet_x":46,"sheet_y":4,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9B8-1F3FC","non_qualified":null,"image":"1f9b8-1f3fc.png","sheet_x":46,"sheet_y":5,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9B8-1F3FD","non_qualified":null,"image":"1f9b8-1f3fd.png","sheet_x":46,"sheet_y":6,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9B8-1F3FE","non_qualified":null,"image":"1f9b8-1f3fe.png","sheet_x":46,"sheet_y":7,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9B8-1F3FF","non_qualified":null,"image":"1f9b8-1f3ff.png","sheet_x":46,"sheet_y":8,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN SUPERVILLAIN","unified":"1F9B9-200D-2640-FE0F","non_qualified":"1F9B9-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9b9-200d-2640-fe0f.png","sheet_x":46,"sheet_y":9,"short_name":"female_supervillain","short_names":["female_supervillain"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":379,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9B9-1F3FB-200D-2640-FE0F","non_qualified":"1F9B9-1F3FB-200D-2640","image":"1f9b9-1f3fb-200d-2640-fe0f.png","sheet_x":46,"sheet_y":10,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9B9-1F3FC-200D-2640-FE0F","non_qualified":"1F9B9-1F3FC-200D-2640","image":"1f9b9-1f3fc-200d-2640-fe0f.png","sheet_x":46,"sheet_y":11,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9B9-1F3FD-200D-2640-FE0F","non_qualified":"1F9B9-1F3FD-200D-2640","image":"1f9b9-1f3fd-200d-2640-fe0f.png","sheet_x":46,"sheet_y":12,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9B9-1F3FE-200D-2640-FE0F","non_qualified":"1F9B9-1F3FE-200D-2640","image":"1f9b9-1f3fe-200d-2640-fe0f.png","sheet_x":46,"sheet_y":13,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9B9-1F3FF-200D-2640-FE0F","non_qualified":"1F9B9-1F3FF-200D-2640","image":"1f9b9-1f3ff-200d-2640-fe0f.png","sheet_x":46,"sheet_y":14,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN SUPERVILLAIN","unified":"1F9B9-200D-2642-FE0F","non_qualified":"1F9B9-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9b9-200d-2642-fe0f.png","sheet_x":46,"sheet_y":15,"short_name":"male_supervillain","short_names":["male_supervillain"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":378,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9B9-1F3FB-200D-2642-FE0F","non_qualified":"1F9B9-1F3FB-200D-2642","image":"1f9b9-1f3fb-200d-2642-fe0f.png","sheet_x":46,"sheet_y":16,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9B9-1F3FC-200D-2642-FE0F","non_qualified":"1F9B9-1F3FC-200D-2642","image":"1f9b9-1f3fc-200d-2642-fe0f.png","sheet_x":46,"sheet_y":17,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9B9-1F3FD-200D-2642-FE0F","non_qualified":"1F9B9-1F3FD-200D-2642","image":"1f9b9-1f3fd-200d-2642-fe0f.png","sheet_x":46,"sheet_y":18,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9B9-1F3FE-200D-2642-FE0F","non_qualified":"1F9B9-1F3FE-200D-2642","image":"1f9b9-1f3fe-200d-2642-fe0f.png","sheet_x":46,"sheet_y":19,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9B9-1F3FF-200D-2642-FE0F","non_qualified":"1F9B9-1F3FF-200D-2642","image":"1f9b9-1f3ff-200d-2642-fe0f.png","sheet_x":46,"sheet_y":20,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SUPERVILLAIN","unified":"1F9B9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9b9.png","sheet_x":46,"sheet_y":21,"short_name":"supervillain","short_names":["supervillain"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":377,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9B9-1F3FB","non_qualified":null,"image":"1f9b9-1f3fb.png","sheet_x":46,"sheet_y":22,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9B9-1F3FC","non_qualified":null,"image":"1f9b9-1f3fc.png","sheet_x":46,"sheet_y":23,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9B9-1F3FD","non_qualified":null,"image":"1f9b9-1f3fd.png","sheet_x":46,"sheet_y":24,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9B9-1F3FE","non_qualified":null,"image":"1f9b9-1f3fe.png","sheet_x":46,"sheet_y":25,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9B9-1F3FF","non_qualified":null,"image":"1f9b9-1f3ff.png","sheet_x":46,"sheet_y":26,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SAFETY VEST","unified":"1F9BA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ba.png","sheet_x":46,"sheet_y":27,"short_name":"safety_vest","short_names":["safety_vest"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1154,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EAR WITH HEARING AID","unified":"1F9BB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9bb.png","sheet_x":46,"sheet_y":28,"short_name":"ear_with_hearing_aid","short_names":["ear_with_hearing_aid"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":218,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9BB-1F3FB","non_qualified":null,"image":"1f9bb-1f3fb.png","sheet_x":46,"sheet_y":29,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9BB-1F3FC","non_qualified":null,"image":"1f9bb-1f3fc.png","sheet_x":46,"sheet_y":30,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9BB-1F3FD","non_qualified":null,"image":"1f9bb-1f3fd.png","sheet_x":46,"sheet_y":31,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9BB-1F3FE","non_qualified":null,"image":"1f9bb-1f3fe.png","sheet_x":46,"sheet_y":32,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9BB-1F3FF","non_qualified":null,"image":"1f9bb-1f3ff.png","sheet_x":46,"sheet_y":33,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MOTORIZED WHEELCHAIR","unified":"1F9BC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9bc.png","sheet_x":46,"sheet_y":34,"short_name":"motorized_wheelchair","short_names":["motorized_wheelchair"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":946,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MANUAL WHEELCHAIR","unified":"1F9BD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9bd.png","sheet_x":46,"sheet_y":35,"short_name":"manual_wheelchair","short_names":["manual_wheelchair"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":945,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MECHANICAL ARM","unified":"1F9BE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9be.png","sheet_x":46,"sheet_y":36,"short_name":"mechanical_arm","short_names":["mechanical_arm"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":213,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MECHANICAL LEG","unified":"1F9BF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9bf.png","sheet_x":46,"sheet_y":37,"short_name":"mechanical_leg","short_names":["mechanical_leg"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":214,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHEESE WEDGE","unified":"1F9C0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c0.png","sheet_x":46,"sheet_y":38,"short_name":"cheese_wedge","short_names":["cheese_wedge"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":758,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CUPCAKE","unified":"1F9C1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c1.png","sheet_x":46,"sheet_y":39,"short_name":"cupcake","short_names":["cupcake"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-sweet","sort_order":813,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SALT SHAKER","unified":"1F9C2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c2.png","sheet_x":46,"sheet_y":40,"short_name":"salt","short_names":["salt"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":782,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BEVERAGE BOX","unified":"1F9C3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c3.png","sheet_x":46,"sheet_y":41,"short_name":"beverage_box","short_names":["beverage_box"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":837,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GARLIC","unified":"1F9C4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c4.png","sheet_x":46,"sheet_y":42,"short_name":"garlic","short_names":["garlic"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":742,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ONION","unified":"1F9C5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c5.png","sheet_x":46,"sheet_y":43,"short_name":"onion","short_names":["onion"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":743,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FALAFEL","unified":"1F9C6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c6.png","sheet_x":46,"sheet_y":44,"short_name":"falafel","short_names":["falafel"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":772,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WAFFLE","unified":"1F9C7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c7.png","sheet_x":46,"sheet_y":45,"short_name":"waffle","short_names":["waffle"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":757,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUTTER","unified":"1F9C8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c8.png","sheet_x":46,"sheet_y":46,"short_name":"butter","short_names":["butter"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":781,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MATE DRINK","unified":"1F9C9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c9.png","sheet_x":46,"sheet_y":47,"short_name":"mate_drink","short_names":["mate_drink"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":838,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ICE CUBE","unified":"1F9CA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ca.png","sheet_x":46,"sheet_y":48,"short_name":"ice_cube","short_names":["ice_cube"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":839,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUBBLE TEA","unified":"1F9CB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9cb.png","sheet_x":46,"sheet_y":49,"short_name":"bubble_tea","short_names":["bubble_tea"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":836,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TROLL","unified":"1F9CC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9cc.png","sheet_x":46,"sheet_y":50,"short_name":"troll","short_names":["troll"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":401,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN STANDING","unified":"1F9CD-200D-2640-FE0F","non_qualified":"1F9CD-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9cd-200d-2640-fe0f.png","sheet_x":46,"sheet_y":51,"short_name":"woman_standing","short_names":["woman_standing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":416,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9CD-1F3FB-200D-2640-FE0F","non_qualified":"1F9CD-1F3FB-200D-2640","image":"1f9cd-1f3fb-200d-2640-fe0f.png","sheet_x":46,"sheet_y":52,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9CD-1F3FC-200D-2640-FE0F","non_qualified":"1F9CD-1F3FC-200D-2640","image":"1f9cd-1f3fc-200d-2640-fe0f.png","sheet_x":46,"sheet_y":53,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9CD-1F3FD-200D-2640-FE0F","non_qualified":"1F9CD-1F3FD-200D-2640","image":"1f9cd-1f3fd-200d-2640-fe0f.png","sheet_x":46,"sheet_y":54,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9CD-1F3FE-200D-2640-FE0F","non_qualified":"1F9CD-1F3FE-200D-2640","image":"1f9cd-1f3fe-200d-2640-fe0f.png","sheet_x":46,"sheet_y":55,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9CD-1F3FF-200D-2640-FE0F","non_qualified":"1F9CD-1F3FF-200D-2640","image":"1f9cd-1f3ff-200d-2640-fe0f.png","sheet_x":46,"sheet_y":56,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN STANDING","unified":"1F9CD-200D-2642-FE0F","non_qualified":"1F9CD-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9cd-200d-2642-fe0f.png","sheet_x":46,"sheet_y":57,"short_name":"man_standing","short_names":["man_standing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":415,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9CD-1F3FB-200D-2642-FE0F","non_qualified":"1F9CD-1F3FB-200D-2642","image":"1f9cd-1f3fb-200d-2642-fe0f.png","sheet_x":46,"sheet_y":58,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9CD-1F3FC-200D-2642-FE0F","non_qualified":"1F9CD-1F3FC-200D-2642","image":"1f9cd-1f3fc-200d-2642-fe0f.png","sheet_x":46,"sheet_y":59,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9CD-1F3FD-200D-2642-FE0F","non_qualified":"1F9CD-1F3FD-200D-2642","image":"1f9cd-1f3fd-200d-2642-fe0f.png","sheet_x":46,"sheet_y":60,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9CD-1F3FE-200D-2642-FE0F","non_qualified":"1F9CD-1F3FE-200D-2642","image":"1f9cd-1f3fe-200d-2642-fe0f.png","sheet_x":46,"sheet_y":61,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9CD-1F3FF-200D-2642-FE0F","non_qualified":"1F9CD-1F3FF-200D-2642","image":"1f9cd-1f3ff-200d-2642-fe0f.png","sheet_x":47,"sheet_y":0,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"STANDING PERSON","unified":"1F9CD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9cd.png","sheet_x":47,"sheet_y":1,"short_name":"standing_person","short_names":["standing_person"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":414,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9CD-1F3FB","non_qualified":null,"image":"1f9cd-1f3fb.png","sheet_x":47,"sheet_y":2,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9CD-1F3FC","non_qualified":null,"image":"1f9cd-1f3fc.png","sheet_x":47,"sheet_y":3,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9CD-1F3FD","non_qualified":null,"image":"1f9cd-1f3fd.png","sheet_x":47,"sheet_y":4,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9CD-1F3FE","non_qualified":null,"image":"1f9cd-1f3fe.png","sheet_x":47,"sheet_y":5,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9CD-1F3FF","non_qualified":null,"image":"1f9cd-1f3ff.png","sheet_x":47,"sheet_y":6,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN KNEELING","unified":"1F9CE-200D-2640-FE0F","non_qualified":"1F9CE-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ce-200d-2640-fe0f.png","sheet_x":47,"sheet_y":7,"short_name":"woman_kneeling","short_names":["woman_kneeling"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":419,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9CE-1F3FB-200D-2640-FE0F","non_qualified":"1F9CE-1F3FB-200D-2640","image":"1f9ce-1f3fb-200d-2640-fe0f.png","sheet_x":47,"sheet_y":8,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9CE-1F3FC-200D-2640-FE0F","non_qualified":"1F9CE-1F3FC-200D-2640","image":"1f9ce-1f3fc-200d-2640-fe0f.png","sheet_x":47,"sheet_y":9,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9CE-1F3FD-200D-2640-FE0F","non_qualified":"1F9CE-1F3FD-200D-2640","image":"1f9ce-1f3fd-200d-2640-fe0f.png","sheet_x":47,"sheet_y":10,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9CE-1F3FE-200D-2640-FE0F","non_qualified":"1F9CE-1F3FE-200D-2640","image":"1f9ce-1f3fe-200d-2640-fe0f.png","sheet_x":47,"sheet_y":11,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9CE-1F3FF-200D-2640-FE0F","non_qualified":"1F9CE-1F3FF-200D-2640","image":"1f9ce-1f3ff-200d-2640-fe0f.png","sheet_x":47,"sheet_y":12,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN KNEELING FACING RIGHT","unified":"1F9CE-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-200D-2640-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ce-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":13,"short_name":"woman_kneeling_facing_right","short_names":["woman_kneeling_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":421,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F9CE-1F3FB-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FB-200D-2640-200D-27A1","image":"1f9ce-1f3fb-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":14,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F9CE-1F3FC-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FC-200D-2640-200D-27A1","image":"1f9ce-1f3fc-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":15,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F9CE-1F3FD-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FD-200D-2640-200D-27A1","image":"1f9ce-1f3fd-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":16,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F9CE-1F3FE-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FE-200D-2640-200D-27A1","image":"1f9ce-1f3fe-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":17,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F9CE-1F3FF-200D-2640-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FF-200D-2640-200D-27A1","image":"1f9ce-1f3ff-200d-2640-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":18,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"MAN KNEELING","unified":"1F9CE-200D-2642-FE0F","non_qualified":"1F9CE-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ce-200d-2642-fe0f.png","sheet_x":47,"sheet_y":19,"short_name":"man_kneeling","short_names":["man_kneeling"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":418,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9CE-1F3FB-200D-2642-FE0F","non_qualified":"1F9CE-1F3FB-200D-2642","image":"1f9ce-1f3fb-200d-2642-fe0f.png","sheet_x":47,"sheet_y":20,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9CE-1F3FC-200D-2642-FE0F","non_qualified":"1F9CE-1F3FC-200D-2642","image":"1f9ce-1f3fc-200d-2642-fe0f.png","sheet_x":47,"sheet_y":21,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9CE-1F3FD-200D-2642-FE0F","non_qualified":"1F9CE-1F3FD-200D-2642","image":"1f9ce-1f3fd-200d-2642-fe0f.png","sheet_x":47,"sheet_y":22,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9CE-1F3FE-200D-2642-FE0F","non_qualified":"1F9CE-1F3FE-200D-2642","image":"1f9ce-1f3fe-200d-2642-fe0f.png","sheet_x":47,"sheet_y":23,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9CE-1F3FF-200D-2642-FE0F","non_qualified":"1F9CE-1F3FF-200D-2642","image":"1f9ce-1f3ff-200d-2642-fe0f.png","sheet_x":47,"sheet_y":24,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN KNEELING FACING RIGHT","unified":"1F9CE-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-200D-2642-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ce-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":25,"short_name":"man_kneeling_facing_right","short_names":["man_kneeling_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":422,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F9CE-1F3FB-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FB-200D-2642-200D-27A1","image":"1f9ce-1f3fb-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":26,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F9CE-1F3FC-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FC-200D-2642-200D-27A1","image":"1f9ce-1f3fc-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":27,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F9CE-1F3FD-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FD-200D-2642-200D-27A1","image":"1f9ce-1f3fd-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":28,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F9CE-1F3FE-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FE-200D-2642-200D-27A1","image":"1f9ce-1f3fe-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":29,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F9CE-1F3FF-200D-2642-FE0F-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FF-200D-2642-200D-27A1","image":"1f9ce-1f3ff-200d-2642-fe0f-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":30,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"PERSON KNEELING FACING RIGHT","unified":"1F9CE-200D-27A1-FE0F","non_qualified":"1F9CE-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ce-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":31,"short_name":"person_kneeling_facing_right","short_names":["person_kneeling_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":420,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F9CE-1F3FB-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FB-200D-27A1","image":"1f9ce-1f3fb-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":32,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F9CE-1F3FC-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FC-200D-27A1","image":"1f9ce-1f3fc-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":33,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F9CE-1F3FD-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FD-200D-27A1","image":"1f9ce-1f3fd-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":34,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F9CE-1F3FE-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FE-200D-27A1","image":"1f9ce-1f3fe-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":35,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F9CE-1F3FF-200D-27A1-FE0F","non_qualified":"1F9CE-1F3FF-200D-27A1","image":"1f9ce-1f3ff-200d-27a1-fe0f.png","sheet_x":47,"sheet_y":36,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"KNEELING PERSON","unified":"1F9CE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ce.png","sheet_x":47,"sheet_y":37,"short_name":"kneeling_person","short_names":["kneeling_person"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":417,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9CE-1F3FB","non_qualified":null,"image":"1f9ce-1f3fb.png","sheet_x":47,"sheet_y":38,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9CE-1F3FC","non_qualified":null,"image":"1f9ce-1f3fc.png","sheet_x":47,"sheet_y":39,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9CE-1F3FD","non_qualified":null,"image":"1f9ce-1f3fd.png","sheet_x":47,"sheet_y":40,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9CE-1F3FE","non_qualified":null,"image":"1f9ce-1f3fe.png","sheet_x":47,"sheet_y":41,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9CE-1F3FF","non_qualified":null,"image":"1f9ce-1f3ff.png","sheet_x":47,"sheet_y":42,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"DEAF WOMAN","unified":"1F9CF-200D-2640-FE0F","non_qualified":"1F9CF-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9cf-200d-2640-fe0f.png","sheet_x":47,"sheet_y":43,"short_name":"deaf_woman","short_names":["deaf_woman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":278,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9CF-1F3FB-200D-2640-FE0F","non_qualified":"1F9CF-1F3FB-200D-2640","image":"1f9cf-1f3fb-200d-2640-fe0f.png","sheet_x":47,"sheet_y":44,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9CF-1F3FC-200D-2640-FE0F","non_qualified":"1F9CF-1F3FC-200D-2640","image":"1f9cf-1f3fc-200d-2640-fe0f.png","sheet_x":47,"sheet_y":45,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9CF-1F3FD-200D-2640-FE0F","non_qualified":"1F9CF-1F3FD-200D-2640","image":"1f9cf-1f3fd-200d-2640-fe0f.png","sheet_x":47,"sheet_y":46,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9CF-1F3FE-200D-2640-FE0F","non_qualified":"1F9CF-1F3FE-200D-2640","image":"1f9cf-1f3fe-200d-2640-fe0f.png","sheet_x":47,"sheet_y":47,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9CF-1F3FF-200D-2640-FE0F","non_qualified":"1F9CF-1F3FF-200D-2640","image":"1f9cf-1f3ff-200d-2640-fe0f.png","sheet_x":47,"sheet_y":48,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"DEAF MAN","unified":"1F9CF-200D-2642-FE0F","non_qualified":"1F9CF-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9cf-200d-2642-fe0f.png","sheet_x":47,"sheet_y":49,"short_name":"deaf_man","short_names":["deaf_man"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":277,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9CF-1F3FB-200D-2642-FE0F","non_qualified":"1F9CF-1F3FB-200D-2642","image":"1f9cf-1f3fb-200d-2642-fe0f.png","sheet_x":47,"sheet_y":50,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9CF-1F3FC-200D-2642-FE0F","non_qualified":"1F9CF-1F3FC-200D-2642","image":"1f9cf-1f3fc-200d-2642-fe0f.png","sheet_x":47,"sheet_y":51,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9CF-1F3FD-200D-2642-FE0F","non_qualified":"1F9CF-1F3FD-200D-2642","image":"1f9cf-1f3fd-200d-2642-fe0f.png","sheet_x":47,"sheet_y":52,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9CF-1F3FE-200D-2642-FE0F","non_qualified":"1F9CF-1F3FE-200D-2642","image":"1f9cf-1f3fe-200d-2642-fe0f.png","sheet_x":47,"sheet_y":53,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9CF-1F3FF-200D-2642-FE0F","non_qualified":"1F9CF-1F3FF-200D-2642","image":"1f9cf-1f3ff-200d-2642-fe0f.png","sheet_x":47,"sheet_y":54,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"DEAF PERSON","unified":"1F9CF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9cf.png","sheet_x":47,"sheet_y":55,"short_name":"deaf_person","short_names":["deaf_person"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-gesture","sort_order":276,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9CF-1F3FB","non_qualified":null,"image":"1f9cf-1f3fb.png","sheet_x":47,"sheet_y":56,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9CF-1F3FC","non_qualified":null,"image":"1f9cf-1f3fc.png","sheet_x":47,"sheet_y":57,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9CF-1F3FD","non_qualified":null,"image":"1f9cf-1f3fd.png","sheet_x":47,"sheet_y":58,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9CF-1F3FE","non_qualified":null,"image":"1f9cf-1f3fe.png","sheet_x":47,"sheet_y":59,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9CF-1F3FF","non_qualified":null,"image":"1f9cf-1f3ff.png","sheet_x":47,"sheet_y":60,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FACE WITH MONOCLE","unified":"1F9D0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d0.png","sheet_x":47,"sheet_y":61,"short_name":"face_with_monocle","short_names":["face_with_monocle"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-glasses","sort_order":75,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FARMER","unified":"1F9D1-200D-1F33E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f33e.png","sheet_x":48,"sheet_y":0,"short_name":"farmer","short_names":["farmer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":300,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F33E","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f33e.png","sheet_x":48,"sheet_y":1,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F33E","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f33e.png","sheet_x":48,"sheet_y":2,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F33E","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f33e.png","sheet_x":48,"sheet_y":3,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F33E","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f33e.png","sheet_x":48,"sheet_y":4,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F33E","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f33e.png","sheet_x":48,"sheet_y":5,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"COOK","unified":"1F9D1-200D-1F373","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f373.png","sheet_x":48,"sheet_y":6,"short_name":"cook","short_names":["cook"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":303,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F373","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f373.png","sheet_x":48,"sheet_y":7,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F373","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f373.png","sheet_x":48,"sheet_y":8,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F373","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f373.png","sheet_x":48,"sheet_y":9,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F373","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f373.png","sheet_x":48,"sheet_y":10,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F373","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f373.png","sheet_x":48,"sheet_y":11,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON FEEDING BABY","unified":"1F9D1-200D-1F37C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f37c.png","sheet_x":48,"sheet_y":12,"short_name":"person_feeding_baby","short_names":["person_feeding_baby"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":369,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F37C","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f37c.png","sheet_x":48,"sheet_y":13,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F37C","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f37c.png","sheet_x":48,"sheet_y":14,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F37C","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f37c.png","sheet_x":48,"sheet_y":15,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F37C","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f37c.png","sheet_x":48,"sheet_y":16,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F37C","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f37c.png","sheet_x":48,"sheet_y":17,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MX CLAUS","unified":"1F9D1-200D-1F384","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f384.png","sheet_x":48,"sheet_y":18,"short_name":"mx_claus","short_names":["mx_claus"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":373,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F384","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f384.png","sheet_x":48,"sheet_y":19,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F384","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f384.png","sheet_x":48,"sheet_y":20,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F384","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f384.png","sheet_x":48,"sheet_y":21,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F384","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f384.png","sheet_x":48,"sheet_y":22,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F384","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f384.png","sheet_x":48,"sheet_y":23,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"STUDENT","unified":"1F9D1-200D-1F393","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f393.png","sheet_x":48,"sheet_y":24,"short_name":"student","short_names":["student"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":291,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F393","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f393.png","sheet_x":48,"sheet_y":25,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F393","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f393.png","sheet_x":48,"sheet_y":26,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F393","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f393.png","sheet_x":48,"sheet_y":27,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F393","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f393.png","sheet_x":48,"sheet_y":28,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F393","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f393.png","sheet_x":48,"sheet_y":29,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SINGER","unified":"1F9D1-200D-1F3A4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f3a4.png","sheet_x":48,"sheet_y":30,"short_name":"singer","short_names":["singer"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":321,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F3A4","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f3a4.png","sheet_x":48,"sheet_y":31,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F3A4","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f3a4.png","sheet_x":48,"sheet_y":32,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F3A4","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f3a4.png","sheet_x":48,"sheet_y":33,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F3A4","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f3a4.png","sheet_x":48,"sheet_y":34,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F3A4","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f3a4.png","sheet_x":48,"sheet_y":35,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"ARTIST","unified":"1F9D1-200D-1F3A8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f3a8.png","sheet_x":48,"sheet_y":36,"short_name":"artist","short_names":["artist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":324,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F3A8","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f3a8.png","sheet_x":48,"sheet_y":37,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F3A8","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f3a8.png","sheet_x":48,"sheet_y":38,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F3A8","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f3a8.png","sheet_x":48,"sheet_y":39,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F3A8","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f3a8.png","sheet_x":48,"sheet_y":40,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F3A8","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f3a8.png","sheet_x":48,"sheet_y":41,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"TEACHER","unified":"1F9D1-200D-1F3EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f3eb.png","sheet_x":48,"sheet_y":42,"short_name":"teacher","short_names":["teacher"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":294,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F3EB","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f3eb.png","sheet_x":48,"sheet_y":43,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F3EB","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f3eb.png","sheet_x":48,"sheet_y":44,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F3EB","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f3eb.png","sheet_x":48,"sheet_y":45,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F3EB","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f3eb.png","sheet_x":48,"sheet_y":46,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F3EB","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f3eb.png","sheet_x":48,"sheet_y":47,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FACTORY WORKER","unified":"1F9D1-200D-1F3ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f3ed.png","sheet_x":48,"sheet_y":48,"short_name":"factory_worker","short_names":["factory_worker"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":309,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F3ED","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f3ed.png","sheet_x":48,"sheet_y":49,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F3ED","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f3ed.png","sheet_x":48,"sheet_y":50,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F3ED","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f3ed.png","sheet_x":48,"sheet_y":51,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F3ED","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f3ed.png","sheet_x":48,"sheet_y":52,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F3ED","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f3ed.png","sheet_x":48,"sheet_y":53,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"TECHNOLOGIST","unified":"1F9D1-200D-1F4BB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f4bb.png","sheet_x":48,"sheet_y":54,"short_name":"technologist","short_names":["technologist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":318,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F4BB","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f4bb.png","sheet_x":48,"sheet_y":55,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F4BB","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f4bb.png","sheet_x":48,"sheet_y":56,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F4BB","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f4bb.png","sheet_x":48,"sheet_y":57,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F4BB","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f4bb.png","sheet_x":48,"sheet_y":58,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F4BB","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f4bb.png","sheet_x":48,"sheet_y":59,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"OFFICE WORKER","unified":"1F9D1-200D-1F4BC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f4bc.png","sheet_x":48,"sheet_y":60,"short_name":"office_worker","short_names":["office_worker"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":312,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F4BC","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f4bc.png","sheet_x":48,"sheet_y":61,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F4BC","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f4bc.png","sheet_x":49,"sheet_y":0,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F4BC","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f4bc.png","sheet_x":49,"sheet_y":1,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F4BC","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f4bc.png","sheet_x":49,"sheet_y":2,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F4BC","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f4bc.png","sheet_x":49,"sheet_y":3,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MECHANIC","unified":"1F9D1-200D-1F527","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f527.png","sheet_x":49,"sheet_y":4,"short_name":"mechanic","short_names":["mechanic"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":306,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F527","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f527.png","sheet_x":49,"sheet_y":5,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F527","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f527.png","sheet_x":49,"sheet_y":6,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F527","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f527.png","sheet_x":49,"sheet_y":7,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F527","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f527.png","sheet_x":49,"sheet_y":8,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F527","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f527.png","sheet_x":49,"sheet_y":9,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SCIENTIST","unified":"1F9D1-200D-1F52C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f52c.png","sheet_x":49,"sheet_y":10,"short_name":"scientist","short_names":["scientist"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":315,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F52C","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f52c.png","sheet_x":49,"sheet_y":11,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F52C","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f52c.png","sheet_x":49,"sheet_y":12,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F52C","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f52c.png","sheet_x":49,"sheet_y":13,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F52C","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f52c.png","sheet_x":49,"sheet_y":14,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F52C","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f52c.png","sheet_x":49,"sheet_y":15,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"ASTRONAUT","unified":"1F9D1-200D-1F680","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f680.png","sheet_x":49,"sheet_y":16,"short_name":"astronaut","short_names":["astronaut"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":330,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F680","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f680.png","sheet_x":49,"sheet_y":17,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F680","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f680.png","sheet_x":49,"sheet_y":18,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F680","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f680.png","sheet_x":49,"sheet_y":19,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F680","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f680.png","sheet_x":49,"sheet_y":20,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F680","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f680.png","sheet_x":49,"sheet_y":21,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FIREFIGHTER","unified":"1F9D1-200D-1F692","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f692.png","sheet_x":49,"sheet_y":22,"short_name":"firefighter","short_names":["firefighter"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":333,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F692","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f692.png","sheet_x":49,"sheet_y":23,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F692","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f692.png","sheet_x":49,"sheet_y":24,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F692","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f692.png","sheet_x":49,"sheet_y":25,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F692","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f692.png","sheet_x":49,"sheet_y":26,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F692","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f692.png","sheet_x":49,"sheet_y":27,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PEOPLE HOLDING HANDS","unified":"1F9D1-200D-1F91D-200D-1F9D1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f91d-200d-1f9d1.png","sheet_x":49,"sheet_y":28,"short_name":"people_holding_hands","short_names":["people_holding_hands"],"text":null,"texts":null,"category":"People & Body","subcategory":"family","sort_order":507,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB-1F3FB":{"unified":"1F9D1-1F3FB-200D-1F91D-200D-1F9D1-1F3FB","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fb.png","sheet_x":49,"sheet_y":29,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FC":{"unified":"1F9D1-1F3FB-200D-1F91D-200D-1F9D1-1F3FC","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fc.png","sheet_x":49,"sheet_y":30,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FD":{"unified":"1F9D1-1F3FB-200D-1F91D-200D-1F9D1-1F3FD","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fd.png","sheet_x":49,"sheet_y":31,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FE":{"unified":"1F9D1-1F3FB-200D-1F91D-200D-1F9D1-1F3FE","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fe.png","sheet_x":49,"sheet_y":32,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FB-1F3FF":{"unified":"1F9D1-1F3FB-200D-1F91D-200D-1F9D1-1F3FF","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3ff.png","sheet_x":49,"sheet_y":33,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FB":{"unified":"1F9D1-1F3FC-200D-1F91D-200D-1F9D1-1F3FB","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fb.png","sheet_x":49,"sheet_y":34,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FC":{"unified":"1F9D1-1F3FC-200D-1F91D-200D-1F9D1-1F3FC","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fc.png","sheet_x":49,"sheet_y":35,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FD":{"unified":"1F9D1-1F3FC-200D-1F91D-200D-1F9D1-1F3FD","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fd.png","sheet_x":49,"sheet_y":36,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FE":{"unified":"1F9D1-1F3FC-200D-1F91D-200D-1F9D1-1F3FE","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fe.png","sheet_x":49,"sheet_y":37,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC-1F3FF":{"unified":"1F9D1-1F3FC-200D-1F91D-200D-1F9D1-1F3FF","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3ff.png","sheet_x":49,"sheet_y":38,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FB":{"unified":"1F9D1-1F3FD-200D-1F91D-200D-1F9D1-1F3FB","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fb.png","sheet_x":49,"sheet_y":39,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FC":{"unified":"1F9D1-1F3FD-200D-1F91D-200D-1F9D1-1F3FC","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fc.png","sheet_x":49,"sheet_y":40,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FD":{"unified":"1F9D1-1F3FD-200D-1F91D-200D-1F9D1-1F3FD","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fd.png","sheet_x":49,"sheet_y":41,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FE":{"unified":"1F9D1-1F3FD-200D-1F91D-200D-1F9D1-1F3FE","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fe.png","sheet_x":49,"sheet_y":42,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD-1F3FF":{"unified":"1F9D1-1F3FD-200D-1F91D-200D-1F9D1-1F3FF","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3ff.png","sheet_x":49,"sheet_y":43,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FB":{"unified":"1F9D1-1F3FE-200D-1F91D-200D-1F9D1-1F3FB","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fb.png","sheet_x":49,"sheet_y":44,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FC":{"unified":"1F9D1-1F3FE-200D-1F91D-200D-1F9D1-1F3FC","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fc.png","sheet_x":49,"sheet_y":45,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FD":{"unified":"1F9D1-1F3FE-200D-1F91D-200D-1F9D1-1F3FD","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fd.png","sheet_x":49,"sheet_y":46,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FE":{"unified":"1F9D1-1F3FE-200D-1F91D-200D-1F9D1-1F3FE","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fe.png","sheet_x":49,"sheet_y":47,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE-1F3FF":{"unified":"1F9D1-1F3FE-200D-1F91D-200D-1F9D1-1F3FF","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3ff.png","sheet_x":49,"sheet_y":48,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FB":{"unified":"1F9D1-1F3FF-200D-1F91D-200D-1F9D1-1F3FB","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fb.png","sheet_x":49,"sheet_y":49,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FC":{"unified":"1F9D1-1F3FF-200D-1F91D-200D-1F9D1-1F3FC","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fc.png","sheet_x":49,"sheet_y":50,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FD":{"unified":"1F9D1-1F3FF-200D-1F91D-200D-1F9D1-1F3FD","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fd.png","sheet_x":49,"sheet_y":51,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FE":{"unified":"1F9D1-1F3FF-200D-1F91D-200D-1F9D1-1F3FE","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fe.png","sheet_x":49,"sheet_y":52,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF-1F3FF":{"unified":"1F9D1-1F3FF-200D-1F91D-200D-1F9D1-1F3FF","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3ff.png","sheet_x":49,"sheet_y":53,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON WITH WHITE CANE FACING RIGHT","unified":"1F9D1-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F9D1-200D-1F9AF-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9af-200d-27a1-fe0f.png","sheet_x":49,"sheet_y":54,"short_name":"person_with_white_cane_facing_right","short_names":["person_with_white_cane_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":424,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FB-200D-1F9AF-200D-27A1","image":"1f9d1-1f3fb-200d-1f9af-200d-27a1-fe0f.png","sheet_x":49,"sheet_y":55,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FC-200D-1F9AF-200D-27A1","image":"1f9d1-1f3fc-200d-1f9af-200d-27a1-fe0f.png","sheet_x":49,"sheet_y":56,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FD-200D-1F9AF-200D-27A1","image":"1f9d1-1f3fd-200d-1f9af-200d-27a1-fe0f.png","sheet_x":49,"sheet_y":57,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FE-200D-1F9AF-200D-27A1","image":"1f9d1-1f3fe-200d-1f9af-200d-27a1-fe0f.png","sheet_x":49,"sheet_y":58,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F9AF-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FF-200D-1F9AF-200D-27A1","image":"1f9d1-1f3ff-200d-1f9af-200d-27a1-fe0f.png","sheet_x":49,"sheet_y":59,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"PERSON WITH WHITE CANE","unified":"1F9D1-200D-1F9AF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9af.png","sheet_x":49,"sheet_y":60,"short_name":"person_with_probing_cane","short_names":["person_with_probing_cane"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":423,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F9AF","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f9af.png","sheet_x":49,"sheet_y":61,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F9AF","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f9af.png","sheet_x":50,"sheet_y":0,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F9AF","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f9af.png","sheet_x":50,"sheet_y":1,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F9AF","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f9af.png","sheet_x":50,"sheet_y":2,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F9AF","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f9af.png","sheet_x":50,"sheet_y":3,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON: RED HAIR","unified":"1F9D1-200D-1F9B0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9b0.png","sheet_x":50,"sheet_y":4,"short_name":"red_haired_person","short_names":["red_haired_person"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":246,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F9B0","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f9b0.png","sheet_x":50,"sheet_y":5,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F9B0","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f9b0.png","sheet_x":50,"sheet_y":6,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F9B0","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f9b0.png","sheet_x":50,"sheet_y":7,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F9B0","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f9b0.png","sheet_x":50,"sheet_y":8,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F9B0","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f9b0.png","sheet_x":50,"sheet_y":9,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON: CURLY HAIR","unified":"1F9D1-200D-1F9B1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9b1.png","sheet_x":50,"sheet_y":10,"short_name":"curly_haired_person","short_names":["curly_haired_person"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":248,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F9B1","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f9b1.png","sheet_x":50,"sheet_y":11,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F9B1","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f9b1.png","sheet_x":50,"sheet_y":12,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F9B1","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f9b1.png","sheet_x":50,"sheet_y":13,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F9B1","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f9b1.png","sheet_x":50,"sheet_y":14,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F9B1","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f9b1.png","sheet_x":50,"sheet_y":15,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON: BALD","unified":"1F9D1-200D-1F9B2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9b2.png","sheet_x":50,"sheet_y":16,"short_name":"bald_person","short_names":["bald_person"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":252,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F9B2","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f9b2.png","sheet_x":50,"sheet_y":17,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F9B2","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f9b2.png","sheet_x":50,"sheet_y":18,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F9B2","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f9b2.png","sheet_x":50,"sheet_y":19,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F9B2","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f9b2.png","sheet_x":50,"sheet_y":20,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F9B2","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f9b2.png","sheet_x":50,"sheet_y":21,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON: WHITE HAIR","unified":"1F9D1-200D-1F9B3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9b3.png","sheet_x":50,"sheet_y":22,"short_name":"white_haired_person","short_names":["white_haired_person"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":250,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F9B3","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f9b3.png","sheet_x":50,"sheet_y":23,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F9B3","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f9b3.png","sheet_x":50,"sheet_y":24,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F9B3","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f9b3.png","sheet_x":50,"sheet_y":25,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F9B3","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f9b3.png","sheet_x":50,"sheet_y":26,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F9B3","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f9b3.png","sheet_x":50,"sheet_y":27,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON IN MOTORIZED WHEELCHAIR FACING RIGHT","unified":"1F9D1-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F9D1-200D-1F9BC-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":28,"short_name":"person_in_motorized_wheelchair_facing_right","short_names":["person_in_motorized_wheelchair_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":430,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FB-200D-1F9BC-200D-27A1","image":"1f9d1-1f3fb-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":29,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FC-200D-1F9BC-200D-27A1","image":"1f9d1-1f3fc-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":30,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FD-200D-1F9BC-200D-27A1","image":"1f9d1-1f3fd-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":31,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FE-200D-1F9BC-200D-27A1","image":"1f9d1-1f3fe-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":32,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F9BC-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FF-200D-1F9BC-200D-27A1","image":"1f9d1-1f3ff-200d-1f9bc-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":33,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"PERSON IN MOTORIZED WHEELCHAIR","unified":"1F9D1-200D-1F9BC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9bc.png","sheet_x":50,"sheet_y":34,"short_name":"person_in_motorized_wheelchair","short_names":["person_in_motorized_wheelchair"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":429,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F9BC","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f9bc.png","sheet_x":50,"sheet_y":35,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F9BC","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f9bc.png","sheet_x":50,"sheet_y":36,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F9BC","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f9bc.png","sheet_x":50,"sheet_y":37,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F9BC","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f9bc.png","sheet_x":50,"sheet_y":38,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F9BC","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f9bc.png","sheet_x":50,"sheet_y":39,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON IN MANUAL WHEELCHAIR FACING RIGHT","unified":"1F9D1-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F9D1-200D-1F9BD-200D-27A1","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":40,"short_name":"person_in_manual_wheelchair_facing_right","short_names":["person_in_manual_wheelchair_facing_right"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":436,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FB-200D-1F9BD-200D-27A1","image":"1f9d1-1f3fb-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":41,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FC-200D-1F9BD-200D-27A1","image":"1f9d1-1f3fc-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":42,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FD-200D-1F9BD-200D-27A1","image":"1f9d1-1f3fd-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":43,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FE-200D-1F9BD-200D-27A1","image":"1f9d1-1f3fe-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":44,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F9BD-200D-27A1-FE0F","non_qualified":"1F9D1-1F3FF-200D-1F9BD-200D-27A1","image":"1f9d1-1f3ff-200d-1f9bd-200d-27a1-fe0f.png","sheet_x":50,"sheet_y":45,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false}}},{"name":"PERSON IN MANUAL WHEELCHAIR","unified":"1F9D1-200D-1F9BD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9bd.png","sheet_x":50,"sheet_y":46,"short_name":"person_in_manual_wheelchair","short_names":["person_in_manual_wheelchair"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":435,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-1F9BD","non_qualified":null,"image":"1f9d1-1f3fb-200d-1f9bd.png","sheet_x":50,"sheet_y":47,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-1F9BD","non_qualified":null,"image":"1f9d1-1f3fc-200d-1f9bd.png","sheet_x":50,"sheet_y":48,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-1F9BD","non_qualified":null,"image":"1f9d1-1f3fd-200d-1f9bd.png","sheet_x":50,"sheet_y":49,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-1F9BD","non_qualified":null,"image":"1f9d1-1f3fe-200d-1f9bd.png","sheet_x":50,"sheet_y":50,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-1F9BD","non_qualified":null,"image":"1f9d1-1f3ff-200d-1f9bd.png","sheet_x":50,"sheet_y":51,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FAMILY: ADULT, ADULT, CHILD","unified":"1F9D1-200D-1F9D1-200D-1F9D2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9d1-200d-1f9d2.png","sheet_x":50,"sheet_y":52,"short_name":"family_adult_adult_child","short_names":["family_adult_adult_child"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-symbol","sort_order":549,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},{"name":"FAMILY: ADULT, ADULT, CHILD, CHILD","unified":"1F9D1-200D-1F9D1-200D-1F9D2-200D-1F9D2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9d1-200d-1f9d2-200d-1f9d2.png","sheet_x":50,"sheet_y":53,"short_name":"family_adult_adult_child_child","short_names":["family_adult_adult_child_child"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-symbol","sort_order":550,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},{"name":"FAMILY: ADULT, CHILD, CHILD","unified":"1F9D1-200D-1F9D2-200D-1F9D2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9d2-200d-1f9d2.png","sheet_x":50,"sheet_y":54,"short_name":"family_adult_child_child","short_names":["family_adult_child_child"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-symbol","sort_order":552,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},{"name":"FAMILY: ADULT, CHILD","unified":"1F9D1-200D-1F9D2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-1f9d2.png","sheet_x":50,"sheet_y":55,"short_name":"family_adult_child","short_names":["family_adult_child"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-symbol","sort_order":551,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},{"name":"HEALTH WORKER","unified":"1F9D1-200D-2695-FE0F","non_qualified":"1F9D1-200D-2695","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-2695-fe0f.png","sheet_x":50,"sheet_y":56,"short_name":"health_worker","short_names":["health_worker"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":288,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-2695-FE0F","non_qualified":"1F9D1-1F3FB-200D-2695","image":"1f9d1-1f3fb-200d-2695-fe0f.png","sheet_x":50,"sheet_y":57,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-2695-FE0F","non_qualified":"1F9D1-1F3FC-200D-2695","image":"1f9d1-1f3fc-200d-2695-fe0f.png","sheet_x":50,"sheet_y":58,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-2695-FE0F","non_qualified":"1F9D1-1F3FD-200D-2695","image":"1f9d1-1f3fd-200d-2695-fe0f.png","sheet_x":50,"sheet_y":59,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-2695-FE0F","non_qualified":"1F9D1-1F3FE-200D-2695","image":"1f9d1-1f3fe-200d-2695-fe0f.png","sheet_x":50,"sheet_y":60,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-2695-FE0F","non_qualified":"1F9D1-1F3FF-200D-2695","image":"1f9d1-1f3ff-200d-2695-fe0f.png","sheet_x":50,"sheet_y":61,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"JUDGE","unified":"1F9D1-200D-2696-FE0F","non_qualified":"1F9D1-200D-2696","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-2696-fe0f.png","sheet_x":51,"sheet_y":0,"short_name":"judge","short_names":["judge"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":297,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-2696-FE0F","non_qualified":"1F9D1-1F3FB-200D-2696","image":"1f9d1-1f3fb-200d-2696-fe0f.png","sheet_x":51,"sheet_y":1,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-2696-FE0F","non_qualified":"1F9D1-1F3FC-200D-2696","image":"1f9d1-1f3fc-200d-2696-fe0f.png","sheet_x":51,"sheet_y":2,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-2696-FE0F","non_qualified":"1F9D1-1F3FD-200D-2696","image":"1f9d1-1f3fd-200d-2696-fe0f.png","sheet_x":51,"sheet_y":3,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-2696-FE0F","non_qualified":"1F9D1-1F3FE-200D-2696","image":"1f9d1-1f3fe-200d-2696-fe0f.png","sheet_x":51,"sheet_y":4,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-2696-FE0F","non_qualified":"1F9D1-1F3FF-200D-2696","image":"1f9d1-1f3ff-200d-2696-fe0f.png","sheet_x":51,"sheet_y":5,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PILOT","unified":"1F9D1-200D-2708-FE0F","non_qualified":"1F9D1-200D-2708","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1-200d-2708-fe0f.png","sheet_x":51,"sheet_y":6,"short_name":"pilot","short_names":["pilot"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":327,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB-200D-2708-FE0F","non_qualified":"1F9D1-1F3FB-200D-2708","image":"1f9d1-1f3fb-200d-2708-fe0f.png","sheet_x":51,"sheet_y":7,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC-200D-2708-FE0F","non_qualified":"1F9D1-1F3FC-200D-2708","image":"1f9d1-1f3fc-200d-2708-fe0f.png","sheet_x":51,"sheet_y":8,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD-200D-2708-FE0F","non_qualified":"1F9D1-1F3FD-200D-2708","image":"1f9d1-1f3fd-200d-2708-fe0f.png","sheet_x":51,"sheet_y":9,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE-200D-2708-FE0F","non_qualified":"1F9D1-1F3FE-200D-2708","image":"1f9d1-1f3fe-200d-2708-fe0f.png","sheet_x":51,"sheet_y":10,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF-200D-2708-FE0F","non_qualified":"1F9D1-1F3FF-200D-2708","image":"1f9d1-1f3ff-200d-2708-fe0f.png","sheet_x":51,"sheet_y":11,"added_in":"12.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"ADULT","unified":"1F9D1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d1.png","sheet_x":51,"sheet_y":12,"short_name":"adult","short_names":["adult"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":234,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D1-1F3FB","non_qualified":null,"image":"1f9d1-1f3fb.png","sheet_x":51,"sheet_y":13,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D1-1F3FC","non_qualified":null,"image":"1f9d1-1f3fc.png","sheet_x":51,"sheet_y":14,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D1-1F3FD","non_qualified":null,"image":"1f9d1-1f3fd.png","sheet_x":51,"sheet_y":15,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D1-1F3FE","non_qualified":null,"image":"1f9d1-1f3fe.png","sheet_x":51,"sheet_y":16,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D1-1F3FF","non_qualified":null,"image":"1f9d1-1f3ff.png","sheet_x":51,"sheet_y":17,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"CHILD","unified":"1F9D2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d2.png","sheet_x":51,"sheet_y":18,"short_name":"child","short_names":["child"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":231,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D2-1F3FB","non_qualified":null,"image":"1f9d2-1f3fb.png","sheet_x":51,"sheet_y":19,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D2-1F3FC","non_qualified":null,"image":"1f9d2-1f3fc.png","sheet_x":51,"sheet_y":20,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D2-1F3FD","non_qualified":null,"image":"1f9d2-1f3fd.png","sheet_x":51,"sheet_y":21,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D2-1F3FE","non_qualified":null,"image":"1f9d2-1f3fe.png","sheet_x":51,"sheet_y":22,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D2-1F3FF","non_qualified":null,"image":"1f9d2-1f3ff.png","sheet_x":51,"sheet_y":23,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"OLDER ADULT","unified":"1F9D3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d3.png","sheet_x":51,"sheet_y":24,"short_name":"older_adult","short_names":["older_adult"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":255,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D3-1F3FB","non_qualified":null,"image":"1f9d3-1f3fb.png","sheet_x":51,"sheet_y":25,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D3-1F3FC","non_qualified":null,"image":"1f9d3-1f3fc.png","sheet_x":51,"sheet_y":26,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D3-1F3FD","non_qualified":null,"image":"1f9d3-1f3fd.png","sheet_x":51,"sheet_y":27,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D3-1F3FE","non_qualified":null,"image":"1f9d3-1f3fe.png","sheet_x":51,"sheet_y":28,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D3-1F3FF","non_qualified":null,"image":"1f9d3-1f3ff.png","sheet_x":51,"sheet_y":29,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN: BEARD","unified":"1F9D4-200D-2640-FE0F","non_qualified":"1F9D4-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d4-200d-2640-fe0f.png","sheet_x":51,"sheet_y":30,"short_name":"woman_with_beard","short_names":["woman_with_beard"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":239,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D4-1F3FB-200D-2640-FE0F","non_qualified":"1F9D4-1F3FB-200D-2640","image":"1f9d4-1f3fb-200d-2640-fe0f.png","sheet_x":51,"sheet_y":31,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D4-1F3FC-200D-2640-FE0F","non_qualified":"1F9D4-1F3FC-200D-2640","image":"1f9d4-1f3fc-200d-2640-fe0f.png","sheet_x":51,"sheet_y":32,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D4-1F3FD-200D-2640-FE0F","non_qualified":"1F9D4-1F3FD-200D-2640","image":"1f9d4-1f3fd-200d-2640-fe0f.png","sheet_x":51,"sheet_y":33,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D4-1F3FE-200D-2640-FE0F","non_qualified":"1F9D4-1F3FE-200D-2640","image":"1f9d4-1f3fe-200d-2640-fe0f.png","sheet_x":51,"sheet_y":34,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D4-1F3FF-200D-2640-FE0F","non_qualified":"1F9D4-1F3FF-200D-2640","image":"1f9d4-1f3ff-200d-2640-fe0f.png","sheet_x":51,"sheet_y":35,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN: BEARD","unified":"1F9D4-200D-2642-FE0F","non_qualified":"1F9D4-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d4-200d-2642-fe0f.png","sheet_x":51,"sheet_y":36,"short_name":"man_with_beard","short_names":["man_with_beard"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":238,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D4-1F3FB-200D-2642-FE0F","non_qualified":"1F9D4-1F3FB-200D-2642","image":"1f9d4-1f3fb-200d-2642-fe0f.png","sheet_x":51,"sheet_y":37,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D4-1F3FC-200D-2642-FE0F","non_qualified":"1F9D4-1F3FC-200D-2642","image":"1f9d4-1f3fc-200d-2642-fe0f.png","sheet_x":51,"sheet_y":38,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D4-1F3FD-200D-2642-FE0F","non_qualified":"1F9D4-1F3FD-200D-2642","image":"1f9d4-1f3fd-200d-2642-fe0f.png","sheet_x":51,"sheet_y":39,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D4-1F3FE-200D-2642-FE0F","non_qualified":"1F9D4-1F3FE-200D-2642","image":"1f9d4-1f3fe-200d-2642-fe0f.png","sheet_x":51,"sheet_y":40,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D4-1F3FF-200D-2642-FE0F","non_qualified":"1F9D4-1F3FF-200D-2642","image":"1f9d4-1f3ff-200d-2642-fe0f.png","sheet_x":51,"sheet_y":41,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"BEARDED PERSON","unified":"1F9D4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d4.png","sheet_x":51,"sheet_y":42,"short_name":"bearded_person","short_names":["bearded_person"],"text":null,"texts":null,"category":"People & Body","subcategory":"person","sort_order":237,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D4-1F3FB","non_qualified":null,"image":"1f9d4-1f3fb.png","sheet_x":51,"sheet_y":43,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D4-1F3FC","non_qualified":null,"image":"1f9d4-1f3fc.png","sheet_x":51,"sheet_y":44,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D4-1F3FD","non_qualified":null,"image":"1f9d4-1f3fd.png","sheet_x":51,"sheet_y":45,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D4-1F3FE","non_qualified":null,"image":"1f9d4-1f3fe.png","sheet_x":51,"sheet_y":46,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D4-1F3FF","non_qualified":null,"image":"1f9d4-1f3ff.png","sheet_x":51,"sheet_y":47,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON WITH HEADSCARF","unified":"1F9D5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d5.png","sheet_x":51,"sheet_y":48,"short_name":"person_with_headscarf","short_names":["person_with_headscarf"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":356,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D5-1F3FB","non_qualified":null,"image":"1f9d5-1f3fb.png","sheet_x":51,"sheet_y":49,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D5-1F3FC","non_qualified":null,"image":"1f9d5-1f3fc.png","sheet_x":51,"sheet_y":50,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D5-1F3FD","non_qualified":null,"image":"1f9d5-1f3fd.png","sheet_x":51,"sheet_y":51,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D5-1F3FE","non_qualified":null,"image":"1f9d5-1f3fe.png","sheet_x":51,"sheet_y":52,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D5-1F3FF","non_qualified":null,"image":"1f9d5-1f3ff.png","sheet_x":51,"sheet_y":53,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WOMAN IN STEAMY ROOM","unified":"1F9D6-200D-2640-FE0F","non_qualified":"1F9D6-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d6-200d-2640-fe0f.png","sheet_x":51,"sheet_y":54,"short_name":"woman_in_steamy_room","short_names":["woman_in_steamy_room"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":455,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D6-1F3FB-200D-2640-FE0F","non_qualified":"1F9D6-1F3FB-200D-2640","image":"1f9d6-1f3fb-200d-2640-fe0f.png","sheet_x":51,"sheet_y":55,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D6-1F3FC-200D-2640-FE0F","non_qualified":"1F9D6-1F3FC-200D-2640","image":"1f9d6-1f3fc-200d-2640-fe0f.png","sheet_x":51,"sheet_y":56,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D6-1F3FD-200D-2640-FE0F","non_qualified":"1F9D6-1F3FD-200D-2640","image":"1f9d6-1f3fd-200d-2640-fe0f.png","sheet_x":51,"sheet_y":57,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D6-1F3FE-200D-2640-FE0F","non_qualified":"1F9D6-1F3FE-200D-2640","image":"1f9d6-1f3fe-200d-2640-fe0f.png","sheet_x":51,"sheet_y":58,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D6-1F3FF-200D-2640-FE0F","non_qualified":"1F9D6-1F3FF-200D-2640","image":"1f9d6-1f3ff-200d-2640-fe0f.png","sheet_x":51,"sheet_y":59,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN IN STEAMY ROOM","unified":"1F9D6-200D-2642-FE0F","non_qualified":"1F9D6-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d6-200d-2642-fe0f.png","sheet_x":51,"sheet_y":60,"short_name":"man_in_steamy_room","short_names":["man_in_steamy_room"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":454,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D6-1F3FB-200D-2642-FE0F","non_qualified":"1F9D6-1F3FB-200D-2642","image":"1f9d6-1f3fb-200d-2642-fe0f.png","sheet_x":51,"sheet_y":61,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D6-1F3FB"},"1F3FC":{"unified":"1F9D6-1F3FC-200D-2642-FE0F","non_qualified":"1F9D6-1F3FC-200D-2642","image":"1f9d6-1f3fc-200d-2642-fe0f.png","sheet_x":52,"sheet_y":0,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D6-1F3FC"},"1F3FD":{"unified":"1F9D6-1F3FD-200D-2642-FE0F","non_qualified":"1F9D6-1F3FD-200D-2642","image":"1f9d6-1f3fd-200d-2642-fe0f.png","sheet_x":52,"sheet_y":1,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D6-1F3FD"},"1F3FE":{"unified":"1F9D6-1F3FE-200D-2642-FE0F","non_qualified":"1F9D6-1F3FE-200D-2642","image":"1f9d6-1f3fe-200d-2642-fe0f.png","sheet_x":52,"sheet_y":2,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D6-1F3FE"},"1F3FF":{"unified":"1F9D6-1F3FF-200D-2642-FE0F","non_qualified":"1F9D6-1F3FF-200D-2642","image":"1f9d6-1f3ff-200d-2642-fe0f.png","sheet_x":52,"sheet_y":3,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D6-1F3FF"}},"obsoletes":"1F9D6"},{"name":"PERSON IN STEAMY ROOM","unified":"1F9D6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d6.png","sheet_x":52,"sheet_y":4,"short_name":"person_in_steamy_room","short_names":["person_in_steamy_room"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":453,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D6-1F3FB","non_qualified":null,"image":"1f9d6-1f3fb.png","sheet_x":52,"sheet_y":5,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D6-1F3FB-200D-2642-FE0F"},"1F3FC":{"unified":"1F9D6-1F3FC","non_qualified":null,"image":"1f9d6-1f3fc.png","sheet_x":52,"sheet_y":6,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D6-1F3FC-200D-2642-FE0F"},"1F3FD":{"unified":"1F9D6-1F3FD","non_qualified":null,"image":"1f9d6-1f3fd.png","sheet_x":52,"sheet_y":7,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D6-1F3FD-200D-2642-FE0F"},"1F3FE":{"unified":"1F9D6-1F3FE","non_qualified":null,"image":"1f9d6-1f3fe.png","sheet_x":52,"sheet_y":8,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D6-1F3FE-200D-2642-FE0F"},"1F3FF":{"unified":"1F9D6-1F3FF","non_qualified":null,"image":"1f9d6-1f3ff.png","sheet_x":52,"sheet_y":9,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D6-1F3FF-200D-2642-FE0F"}},"obsoleted_by":"1F9D6-200D-2642-FE0F"},{"name":"WOMAN CLIMBING","unified":"1F9D7-200D-2640-FE0F","non_qualified":"1F9D7-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d7-200d-2640-fe0f.png","sheet_x":52,"sheet_y":10,"short_name":"woman_climbing","short_names":["woman_climbing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":458,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D7-1F3FB-200D-2640-FE0F","non_qualified":"1F9D7-1F3FB-200D-2640","image":"1f9d7-1f3fb-200d-2640-fe0f.png","sheet_x":52,"sheet_y":11,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D7-1F3FB"},"1F3FC":{"unified":"1F9D7-1F3FC-200D-2640-FE0F","non_qualified":"1F9D7-1F3FC-200D-2640","image":"1f9d7-1f3fc-200d-2640-fe0f.png","sheet_x":52,"sheet_y":12,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D7-1F3FC"},"1F3FD":{"unified":"1F9D7-1F3FD-200D-2640-FE0F","non_qualified":"1F9D7-1F3FD-200D-2640","image":"1f9d7-1f3fd-200d-2640-fe0f.png","sheet_x":52,"sheet_y":13,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D7-1F3FD"},"1F3FE":{"unified":"1F9D7-1F3FE-200D-2640-FE0F","non_qualified":"1F9D7-1F3FE-200D-2640","image":"1f9d7-1f3fe-200d-2640-fe0f.png","sheet_x":52,"sheet_y":14,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D7-1F3FE"},"1F3FF":{"unified":"1F9D7-1F3FF-200D-2640-FE0F","non_qualified":"1F9D7-1F3FF-200D-2640","image":"1f9d7-1f3ff-200d-2640-fe0f.png","sheet_x":52,"sheet_y":15,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D7-1F3FF"}},"obsoletes":"1F9D7"},{"name":"MAN CLIMBING","unified":"1F9D7-200D-2642-FE0F","non_qualified":"1F9D7-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d7-200d-2642-fe0f.png","sheet_x":52,"sheet_y":16,"short_name":"man_climbing","short_names":["man_climbing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":457,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D7-1F3FB-200D-2642-FE0F","non_qualified":"1F9D7-1F3FB-200D-2642","image":"1f9d7-1f3fb-200d-2642-fe0f.png","sheet_x":52,"sheet_y":17,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D7-1F3FC-200D-2642-FE0F","non_qualified":"1F9D7-1F3FC-200D-2642","image":"1f9d7-1f3fc-200d-2642-fe0f.png","sheet_x":52,"sheet_y":18,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D7-1F3FD-200D-2642-FE0F","non_qualified":"1F9D7-1F3FD-200D-2642","image":"1f9d7-1f3fd-200d-2642-fe0f.png","sheet_x":52,"sheet_y":19,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D7-1F3FE-200D-2642-FE0F","non_qualified":"1F9D7-1F3FE-200D-2642","image":"1f9d7-1f3fe-200d-2642-fe0f.png","sheet_x":52,"sheet_y":20,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D7-1F3FF-200D-2642-FE0F","non_qualified":"1F9D7-1F3FF-200D-2642","image":"1f9d7-1f3ff-200d-2642-fe0f.png","sheet_x":52,"sheet_y":21,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON CLIMBING","unified":"1F9D7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d7.png","sheet_x":52,"sheet_y":22,"short_name":"person_climbing","short_names":["person_climbing"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-activity","sort_order":456,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D7-1F3FB","non_qualified":null,"image":"1f9d7-1f3fb.png","sheet_x":52,"sheet_y":23,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D7-1F3FB-200D-2640-FE0F"},"1F3FC":{"unified":"1F9D7-1F3FC","non_qualified":null,"image":"1f9d7-1f3fc.png","sheet_x":52,"sheet_y":24,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D7-1F3FC-200D-2640-FE0F"},"1F3FD":{"unified":"1F9D7-1F3FD","non_qualified":null,"image":"1f9d7-1f3fd.png","sheet_x":52,"sheet_y":25,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D7-1F3FD-200D-2640-FE0F"},"1F3FE":{"unified":"1F9D7-1F3FE","non_qualified":null,"image":"1f9d7-1f3fe.png","sheet_x":52,"sheet_y":26,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D7-1F3FE-200D-2640-FE0F"},"1F3FF":{"unified":"1F9D7-1F3FF","non_qualified":null,"image":"1f9d7-1f3ff.png","sheet_x":52,"sheet_y":27,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D7-1F3FF-200D-2640-FE0F"}},"obsoleted_by":"1F9D7-200D-2640-FE0F"},{"name":"WOMAN IN LOTUS POSITION","unified":"1F9D8-200D-2640-FE0F","non_qualified":"1F9D8-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d8-200d-2640-fe0f.png","sheet_x":52,"sheet_y":28,"short_name":"woman_in_lotus_position","short_names":["woman_in_lotus_position"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-resting","sort_order":504,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D8-1F3FB-200D-2640-FE0F","non_qualified":"1F9D8-1F3FB-200D-2640","image":"1f9d8-1f3fb-200d-2640-fe0f.png","sheet_x":52,"sheet_y":29,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D8-1F3FB"},"1F3FC":{"unified":"1F9D8-1F3FC-200D-2640-FE0F","non_qualified":"1F9D8-1F3FC-200D-2640","image":"1f9d8-1f3fc-200d-2640-fe0f.png","sheet_x":52,"sheet_y":30,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D8-1F3FC"},"1F3FD":{"unified":"1F9D8-1F3FD-200D-2640-FE0F","non_qualified":"1F9D8-1F3FD-200D-2640","image":"1f9d8-1f3fd-200d-2640-fe0f.png","sheet_x":52,"sheet_y":31,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D8-1F3FD"},"1F3FE":{"unified":"1F9D8-1F3FE-200D-2640-FE0F","non_qualified":"1F9D8-1F3FE-200D-2640","image":"1f9d8-1f3fe-200d-2640-fe0f.png","sheet_x":52,"sheet_y":32,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D8-1F3FE"},"1F3FF":{"unified":"1F9D8-1F3FF-200D-2640-FE0F","non_qualified":"1F9D8-1F3FF-200D-2640","image":"1f9d8-1f3ff-200d-2640-fe0f.png","sheet_x":52,"sheet_y":33,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D8-1F3FF"}},"obsoletes":"1F9D8"},{"name":"MAN IN LOTUS POSITION","unified":"1F9D8-200D-2642-FE0F","non_qualified":"1F9D8-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d8-200d-2642-fe0f.png","sheet_x":52,"sheet_y":34,"short_name":"man_in_lotus_position","short_names":["man_in_lotus_position"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-resting","sort_order":503,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D8-1F3FB-200D-2642-FE0F","non_qualified":"1F9D8-1F3FB-200D-2642","image":"1f9d8-1f3fb-200d-2642-fe0f.png","sheet_x":52,"sheet_y":35,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D8-1F3FC-200D-2642-FE0F","non_qualified":"1F9D8-1F3FC-200D-2642","image":"1f9d8-1f3fc-200d-2642-fe0f.png","sheet_x":52,"sheet_y":36,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D8-1F3FD-200D-2642-FE0F","non_qualified":"1F9D8-1F3FD-200D-2642","image":"1f9d8-1f3fd-200d-2642-fe0f.png","sheet_x":52,"sheet_y":37,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D8-1F3FE-200D-2642-FE0F","non_qualified":"1F9D8-1F3FE-200D-2642","image":"1f9d8-1f3fe-200d-2642-fe0f.png","sheet_x":52,"sheet_y":38,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D8-1F3FF-200D-2642-FE0F","non_qualified":"1F9D8-1F3FF-200D-2642","image":"1f9d8-1f3ff-200d-2642-fe0f.png","sheet_x":52,"sheet_y":39,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON IN LOTUS POSITION","unified":"1F9D8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d8.png","sheet_x":52,"sheet_y":40,"short_name":"person_in_lotus_position","short_names":["person_in_lotus_position"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-resting","sort_order":502,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D8-1F3FB","non_qualified":null,"image":"1f9d8-1f3fb.png","sheet_x":52,"sheet_y":41,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D8-1F3FB-200D-2640-FE0F"},"1F3FC":{"unified":"1F9D8-1F3FC","non_qualified":null,"image":"1f9d8-1f3fc.png","sheet_x":52,"sheet_y":42,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D8-1F3FC-200D-2640-FE0F"},"1F3FD":{"unified":"1F9D8-1F3FD","non_qualified":null,"image":"1f9d8-1f3fd.png","sheet_x":52,"sheet_y":43,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D8-1F3FD-200D-2640-FE0F"},"1F3FE":{"unified":"1F9D8-1F3FE","non_qualified":null,"image":"1f9d8-1f3fe.png","sheet_x":52,"sheet_y":44,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D8-1F3FE-200D-2640-FE0F"},"1F3FF":{"unified":"1F9D8-1F3FF","non_qualified":null,"image":"1f9d8-1f3ff.png","sheet_x":52,"sheet_y":45,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D8-1F3FF-200D-2640-FE0F"}},"obsoleted_by":"1F9D8-200D-2640-FE0F"},{"name":"WOMAN MAGE","unified":"1F9D9-200D-2640-FE0F","non_qualified":"1F9D9-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d9-200d-2640-fe0f.png","sheet_x":52,"sheet_y":46,"short_name":"female_mage","short_names":["female_mage"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":382,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D9-1F3FB-200D-2640-FE0F","non_qualified":"1F9D9-1F3FB-200D-2640","image":"1f9d9-1f3fb-200d-2640-fe0f.png","sheet_x":52,"sheet_y":47,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D9-1F3FB"},"1F3FC":{"unified":"1F9D9-1F3FC-200D-2640-FE0F","non_qualified":"1F9D9-1F3FC-200D-2640","image":"1f9d9-1f3fc-200d-2640-fe0f.png","sheet_x":52,"sheet_y":48,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D9-1F3FC"},"1F3FD":{"unified":"1F9D9-1F3FD-200D-2640-FE0F","non_qualified":"1F9D9-1F3FD-200D-2640","image":"1f9d9-1f3fd-200d-2640-fe0f.png","sheet_x":52,"sheet_y":49,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D9-1F3FD"},"1F3FE":{"unified":"1F9D9-1F3FE-200D-2640-FE0F","non_qualified":"1F9D9-1F3FE-200D-2640","image":"1f9d9-1f3fe-200d-2640-fe0f.png","sheet_x":52,"sheet_y":50,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D9-1F3FE"},"1F3FF":{"unified":"1F9D9-1F3FF-200D-2640-FE0F","non_qualified":"1F9D9-1F3FF-200D-2640","image":"1f9d9-1f3ff-200d-2640-fe0f.png","sheet_x":52,"sheet_y":51,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9D9-1F3FF"}},"obsoletes":"1F9D9"},{"name":"MAN MAGE","unified":"1F9D9-200D-2642-FE0F","non_qualified":"1F9D9-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d9-200d-2642-fe0f.png","sheet_x":52,"sheet_y":52,"short_name":"male_mage","short_names":["male_mage"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":381,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D9-1F3FB-200D-2642-FE0F","non_qualified":"1F9D9-1F3FB-200D-2642","image":"1f9d9-1f3fb-200d-2642-fe0f.png","sheet_x":52,"sheet_y":53,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9D9-1F3FC-200D-2642-FE0F","non_qualified":"1F9D9-1F3FC-200D-2642","image":"1f9d9-1f3fc-200d-2642-fe0f.png","sheet_x":52,"sheet_y":54,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9D9-1F3FD-200D-2642-FE0F","non_qualified":"1F9D9-1F3FD-200D-2642","image":"1f9d9-1f3fd-200d-2642-fe0f.png","sheet_x":52,"sheet_y":55,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9D9-1F3FE-200D-2642-FE0F","non_qualified":"1F9D9-1F3FE-200D-2642","image":"1f9d9-1f3fe-200d-2642-fe0f.png","sheet_x":52,"sheet_y":56,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9D9-1F3FF-200D-2642-FE0F","non_qualified":"1F9D9-1F3FF-200D-2642","image":"1f9d9-1f3ff-200d-2642-fe0f.png","sheet_x":52,"sheet_y":57,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAGE","unified":"1F9D9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9d9.png","sheet_x":52,"sheet_y":58,"short_name":"mage","short_names":["mage"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":380,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9D9-1F3FB","non_qualified":null,"image":"1f9d9-1f3fb.png","sheet_x":52,"sheet_y":59,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D9-1F3FB-200D-2640-FE0F"},"1F3FC":{"unified":"1F9D9-1F3FC","non_qualified":null,"image":"1f9d9-1f3fc.png","sheet_x":52,"sheet_y":60,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D9-1F3FC-200D-2640-FE0F"},"1F3FD":{"unified":"1F9D9-1F3FD","non_qualified":null,"image":"1f9d9-1f3fd.png","sheet_x":52,"sheet_y":61,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D9-1F3FD-200D-2640-FE0F"},"1F3FE":{"unified":"1F9D9-1F3FE","non_qualified":null,"image":"1f9d9-1f3fe.png","sheet_x":53,"sheet_y":0,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D9-1F3FE-200D-2640-FE0F"},"1F3FF":{"unified":"1F9D9-1F3FF","non_qualified":null,"image":"1f9d9-1f3ff.png","sheet_x":53,"sheet_y":1,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9D9-1F3FF-200D-2640-FE0F"}},"obsoleted_by":"1F9D9-200D-2640-FE0F"},{"name":"WOMAN FAIRY","unified":"1F9DA-200D-2640-FE0F","non_qualified":"1F9DA-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9da-200d-2640-fe0f.png","sheet_x":53,"sheet_y":2,"short_name":"female_fairy","short_names":["female_fairy"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":385,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DA-1F3FB-200D-2640-FE0F","non_qualified":"1F9DA-1F3FB-200D-2640","image":"1f9da-1f3fb-200d-2640-fe0f.png","sheet_x":53,"sheet_y":3,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DA-1F3FB"},"1F3FC":{"unified":"1F9DA-1F3FC-200D-2640-FE0F","non_qualified":"1F9DA-1F3FC-200D-2640","image":"1f9da-1f3fc-200d-2640-fe0f.png","sheet_x":53,"sheet_y":4,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DA-1F3FC"},"1F3FD":{"unified":"1F9DA-1F3FD-200D-2640-FE0F","non_qualified":"1F9DA-1F3FD-200D-2640","image":"1f9da-1f3fd-200d-2640-fe0f.png","sheet_x":53,"sheet_y":5,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DA-1F3FD"},"1F3FE":{"unified":"1F9DA-1F3FE-200D-2640-FE0F","non_qualified":"1F9DA-1F3FE-200D-2640","image":"1f9da-1f3fe-200d-2640-fe0f.png","sheet_x":53,"sheet_y":6,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DA-1F3FE"},"1F3FF":{"unified":"1F9DA-1F3FF-200D-2640-FE0F","non_qualified":"1F9DA-1F3FF-200D-2640","image":"1f9da-1f3ff-200d-2640-fe0f.png","sheet_x":53,"sheet_y":7,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DA-1F3FF"}},"obsoletes":"1F9DA"},{"name":"MAN FAIRY","unified":"1F9DA-200D-2642-FE0F","non_qualified":"1F9DA-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9da-200d-2642-fe0f.png","sheet_x":53,"sheet_y":8,"short_name":"male_fairy","short_names":["male_fairy"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":384,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DA-1F3FB-200D-2642-FE0F","non_qualified":"1F9DA-1F3FB-200D-2642","image":"1f9da-1f3fb-200d-2642-fe0f.png","sheet_x":53,"sheet_y":9,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9DA-1F3FC-200D-2642-FE0F","non_qualified":"1F9DA-1F3FC-200D-2642","image":"1f9da-1f3fc-200d-2642-fe0f.png","sheet_x":53,"sheet_y":10,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9DA-1F3FD-200D-2642-FE0F","non_qualified":"1F9DA-1F3FD-200D-2642","image":"1f9da-1f3fd-200d-2642-fe0f.png","sheet_x":53,"sheet_y":11,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9DA-1F3FE-200D-2642-FE0F","non_qualified":"1F9DA-1F3FE-200D-2642","image":"1f9da-1f3fe-200d-2642-fe0f.png","sheet_x":53,"sheet_y":12,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9DA-1F3FF-200D-2642-FE0F","non_qualified":"1F9DA-1F3FF-200D-2642","image":"1f9da-1f3ff-200d-2642-fe0f.png","sheet_x":53,"sheet_y":13,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"FAIRY","unified":"1F9DA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9da.png","sheet_x":53,"sheet_y":14,"short_name":"fairy","short_names":["fairy"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":383,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DA-1F3FB","non_qualified":null,"image":"1f9da-1f3fb.png","sheet_x":53,"sheet_y":15,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DA-1F3FB-200D-2640-FE0F"},"1F3FC":{"unified":"1F9DA-1F3FC","non_qualified":null,"image":"1f9da-1f3fc.png","sheet_x":53,"sheet_y":16,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DA-1F3FC-200D-2640-FE0F"},"1F3FD":{"unified":"1F9DA-1F3FD","non_qualified":null,"image":"1f9da-1f3fd.png","sheet_x":53,"sheet_y":17,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DA-1F3FD-200D-2640-FE0F"},"1F3FE":{"unified":"1F9DA-1F3FE","non_qualified":null,"image":"1f9da-1f3fe.png","sheet_x":53,"sheet_y":18,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DA-1F3FE-200D-2640-FE0F"},"1F3FF":{"unified":"1F9DA-1F3FF","non_qualified":null,"image":"1f9da-1f3ff.png","sheet_x":53,"sheet_y":19,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DA-1F3FF-200D-2640-FE0F"}},"obsoleted_by":"1F9DA-200D-2640-FE0F"},{"name":"WOMAN VAMPIRE","unified":"1F9DB-200D-2640-FE0F","non_qualified":"1F9DB-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9db-200d-2640-fe0f.png","sheet_x":53,"sheet_y":20,"short_name":"female_vampire","short_names":["female_vampire"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":388,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DB-1F3FB-200D-2640-FE0F","non_qualified":"1F9DB-1F3FB-200D-2640","image":"1f9db-1f3fb-200d-2640-fe0f.png","sheet_x":53,"sheet_y":21,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DB-1F3FB"},"1F3FC":{"unified":"1F9DB-1F3FC-200D-2640-FE0F","non_qualified":"1F9DB-1F3FC-200D-2640","image":"1f9db-1f3fc-200d-2640-fe0f.png","sheet_x":53,"sheet_y":22,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DB-1F3FC"},"1F3FD":{"unified":"1F9DB-1F3FD-200D-2640-FE0F","non_qualified":"1F9DB-1F3FD-200D-2640","image":"1f9db-1f3fd-200d-2640-fe0f.png","sheet_x":53,"sheet_y":23,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DB-1F3FD"},"1F3FE":{"unified":"1F9DB-1F3FE-200D-2640-FE0F","non_qualified":"1F9DB-1F3FE-200D-2640","image":"1f9db-1f3fe-200d-2640-fe0f.png","sheet_x":53,"sheet_y":24,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DB-1F3FE"},"1F3FF":{"unified":"1F9DB-1F3FF-200D-2640-FE0F","non_qualified":"1F9DB-1F3FF-200D-2640","image":"1f9db-1f3ff-200d-2640-fe0f.png","sheet_x":53,"sheet_y":25,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DB-1F3FF"}},"obsoletes":"1F9DB"},{"name":"MAN VAMPIRE","unified":"1F9DB-200D-2642-FE0F","non_qualified":"1F9DB-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9db-200d-2642-fe0f.png","sheet_x":53,"sheet_y":26,"short_name":"male_vampire","short_names":["male_vampire"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":387,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DB-1F3FB-200D-2642-FE0F","non_qualified":"1F9DB-1F3FB-200D-2642","image":"1f9db-1f3fb-200d-2642-fe0f.png","sheet_x":53,"sheet_y":27,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9DB-1F3FC-200D-2642-FE0F","non_qualified":"1F9DB-1F3FC-200D-2642","image":"1f9db-1f3fc-200d-2642-fe0f.png","sheet_x":53,"sheet_y":28,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9DB-1F3FD-200D-2642-FE0F","non_qualified":"1F9DB-1F3FD-200D-2642","image":"1f9db-1f3fd-200d-2642-fe0f.png","sheet_x":53,"sheet_y":29,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9DB-1F3FE-200D-2642-FE0F","non_qualified":"1F9DB-1F3FE-200D-2642","image":"1f9db-1f3fe-200d-2642-fe0f.png","sheet_x":53,"sheet_y":30,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9DB-1F3FF-200D-2642-FE0F","non_qualified":"1F9DB-1F3FF-200D-2642","image":"1f9db-1f3ff-200d-2642-fe0f.png","sheet_x":53,"sheet_y":31,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"VAMPIRE","unified":"1F9DB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9db.png","sheet_x":53,"sheet_y":32,"short_name":"vampire","short_names":["vampire"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":386,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DB-1F3FB","non_qualified":null,"image":"1f9db-1f3fb.png","sheet_x":53,"sheet_y":33,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DB-1F3FB-200D-2640-FE0F"},"1F3FC":{"unified":"1F9DB-1F3FC","non_qualified":null,"image":"1f9db-1f3fc.png","sheet_x":53,"sheet_y":34,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DB-1F3FC-200D-2640-FE0F"},"1F3FD":{"unified":"1F9DB-1F3FD","non_qualified":null,"image":"1f9db-1f3fd.png","sheet_x":53,"sheet_y":35,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DB-1F3FD-200D-2640-FE0F"},"1F3FE":{"unified":"1F9DB-1F3FE","non_qualified":null,"image":"1f9db-1f3fe.png","sheet_x":53,"sheet_y":36,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DB-1F3FE-200D-2640-FE0F"},"1F3FF":{"unified":"1F9DB-1F3FF","non_qualified":null,"image":"1f9db-1f3ff.png","sheet_x":53,"sheet_y":37,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DB-1F3FF-200D-2640-FE0F"}},"obsoleted_by":"1F9DB-200D-2640-FE0F"},{"name":"MERMAID","unified":"1F9DC-200D-2640-FE0F","non_qualified":"1F9DC-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9dc-200d-2640-fe0f.png","sheet_x":53,"sheet_y":38,"short_name":"mermaid","short_names":["mermaid"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":391,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DC-1F3FB-200D-2640-FE0F","non_qualified":"1F9DC-1F3FB-200D-2640","image":"1f9dc-1f3fb-200d-2640-fe0f.png","sheet_x":53,"sheet_y":39,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9DC-1F3FC-200D-2640-FE0F","non_qualified":"1F9DC-1F3FC-200D-2640","image":"1f9dc-1f3fc-200d-2640-fe0f.png","sheet_x":53,"sheet_y":40,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9DC-1F3FD-200D-2640-FE0F","non_qualified":"1F9DC-1F3FD-200D-2640","image":"1f9dc-1f3fd-200d-2640-fe0f.png","sheet_x":53,"sheet_y":41,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9DC-1F3FE-200D-2640-FE0F","non_qualified":"1F9DC-1F3FE-200D-2640","image":"1f9dc-1f3fe-200d-2640-fe0f.png","sheet_x":53,"sheet_y":42,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9DC-1F3FF-200D-2640-FE0F","non_qualified":"1F9DC-1F3FF-200D-2640","image":"1f9dc-1f3ff-200d-2640-fe0f.png","sheet_x":53,"sheet_y":43,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MERMAN","unified":"1F9DC-200D-2642-FE0F","non_qualified":"1F9DC-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9dc-200d-2642-fe0f.png","sheet_x":53,"sheet_y":44,"short_name":"merman","short_names":["merman"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":390,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DC-1F3FB-200D-2642-FE0F","non_qualified":"1F9DC-1F3FB-200D-2642","image":"1f9dc-1f3fb-200d-2642-fe0f.png","sheet_x":53,"sheet_y":45,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DC-1F3FB"},"1F3FC":{"unified":"1F9DC-1F3FC-200D-2642-FE0F","non_qualified":"1F9DC-1F3FC-200D-2642","image":"1f9dc-1f3fc-200d-2642-fe0f.png","sheet_x":53,"sheet_y":46,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DC-1F3FC"},"1F3FD":{"unified":"1F9DC-1F3FD-200D-2642-FE0F","non_qualified":"1F9DC-1F3FD-200D-2642","image":"1f9dc-1f3fd-200d-2642-fe0f.png","sheet_x":53,"sheet_y":47,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DC-1F3FD"},"1F3FE":{"unified":"1F9DC-1F3FE-200D-2642-FE0F","non_qualified":"1F9DC-1F3FE-200D-2642","image":"1f9dc-1f3fe-200d-2642-fe0f.png","sheet_x":53,"sheet_y":48,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DC-1F3FE"},"1F3FF":{"unified":"1F9DC-1F3FF-200D-2642-FE0F","non_qualified":"1F9DC-1F3FF-200D-2642","image":"1f9dc-1f3ff-200d-2642-fe0f.png","sheet_x":53,"sheet_y":49,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DC-1F3FF"}},"obsoletes":"1F9DC"},{"name":"MERPERSON","unified":"1F9DC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9dc.png","sheet_x":53,"sheet_y":50,"short_name":"merperson","short_names":["merperson"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":389,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DC-1F3FB","non_qualified":null,"image":"1f9dc-1f3fb.png","sheet_x":53,"sheet_y":51,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DC-1F3FB-200D-2642-FE0F"},"1F3FC":{"unified":"1F9DC-1F3FC","non_qualified":null,"image":"1f9dc-1f3fc.png","sheet_x":53,"sheet_y":52,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DC-1F3FC-200D-2642-FE0F"},"1F3FD":{"unified":"1F9DC-1F3FD","non_qualified":null,"image":"1f9dc-1f3fd.png","sheet_x":53,"sheet_y":53,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DC-1F3FD-200D-2642-FE0F"},"1F3FE":{"unified":"1F9DC-1F3FE","non_qualified":null,"image":"1f9dc-1f3fe.png","sheet_x":53,"sheet_y":54,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DC-1F3FE-200D-2642-FE0F"},"1F3FF":{"unified":"1F9DC-1F3FF","non_qualified":null,"image":"1f9dc-1f3ff.png","sheet_x":53,"sheet_y":55,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DC-1F3FF-200D-2642-FE0F"}},"obsoleted_by":"1F9DC-200D-2642-FE0F"},{"name":"WOMAN ELF","unified":"1F9DD-200D-2640-FE0F","non_qualified":"1F9DD-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9dd-200d-2640-fe0f.png","sheet_x":53,"sheet_y":56,"short_name":"female_elf","short_names":["female_elf"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":394,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DD-1F3FB-200D-2640-FE0F","non_qualified":"1F9DD-1F3FB-200D-2640","image":"1f9dd-1f3fb-200d-2640-fe0f.png","sheet_x":53,"sheet_y":57,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1F9DD-1F3FC-200D-2640-FE0F","non_qualified":"1F9DD-1F3FC-200D-2640","image":"1f9dd-1f3fc-200d-2640-fe0f.png","sheet_x":53,"sheet_y":58,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1F9DD-1F3FD-200D-2640-FE0F","non_qualified":"1F9DD-1F3FD-200D-2640","image":"1f9dd-1f3fd-200d-2640-fe0f.png","sheet_x":53,"sheet_y":59,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1F9DD-1F3FE-200D-2640-FE0F","non_qualified":"1F9DD-1F3FE-200D-2640","image":"1f9dd-1f3fe-200d-2640-fe0f.png","sheet_x":53,"sheet_y":60,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1F9DD-1F3FF-200D-2640-FE0F","non_qualified":"1F9DD-1F3FF-200D-2640","image":"1f9dd-1f3ff-200d-2640-fe0f.png","sheet_x":53,"sheet_y":61,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN ELF","unified":"1F9DD-200D-2642-FE0F","non_qualified":"1F9DD-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9dd-200d-2642-fe0f.png","sheet_x":54,"sheet_y":0,"short_name":"male_elf","short_names":["male_elf"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":393,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DD-1F3FB-200D-2642-FE0F","non_qualified":"1F9DD-1F3FB-200D-2642","image":"1f9dd-1f3fb-200d-2642-fe0f.png","sheet_x":54,"sheet_y":1,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DD-1F3FB"},"1F3FC":{"unified":"1F9DD-1F3FC-200D-2642-FE0F","non_qualified":"1F9DD-1F3FC-200D-2642","image":"1f9dd-1f3fc-200d-2642-fe0f.png","sheet_x":54,"sheet_y":2,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DD-1F3FC"},"1F3FD":{"unified":"1F9DD-1F3FD-200D-2642-FE0F","non_qualified":"1F9DD-1F3FD-200D-2642","image":"1f9dd-1f3fd-200d-2642-fe0f.png","sheet_x":54,"sheet_y":3,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DD-1F3FD"},"1F3FE":{"unified":"1F9DD-1F3FE-200D-2642-FE0F","non_qualified":"1F9DD-1F3FE-200D-2642","image":"1f9dd-1f3fe-200d-2642-fe0f.png","sheet_x":54,"sheet_y":4,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DD-1F3FE"},"1F3FF":{"unified":"1F9DD-1F3FF-200D-2642-FE0F","non_qualified":"1F9DD-1F3FF-200D-2642","image":"1f9dd-1f3ff-200d-2642-fe0f.png","sheet_x":54,"sheet_y":5,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DD-1F3FF"}},"obsoletes":"1F9DD"},{"name":"ELF","unified":"1F9DD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9dd.png","sheet_x":54,"sheet_y":6,"short_name":"elf","short_names":["elf"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":392,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1F9DD-1F3FB","non_qualified":null,"image":"1f9dd-1f3fb.png","sheet_x":54,"sheet_y":7,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DD-1F3FB-200D-2642-FE0F"},"1F3FC":{"unified":"1F9DD-1F3FC","non_qualified":null,"image":"1f9dd-1f3fc.png","sheet_x":54,"sheet_y":8,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DD-1F3FC-200D-2642-FE0F"},"1F3FD":{"unified":"1F9DD-1F3FD","non_qualified":null,"image":"1f9dd-1f3fd.png","sheet_x":54,"sheet_y":9,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DD-1F3FD-200D-2642-FE0F"},"1F3FE":{"unified":"1F9DD-1F3FE","non_qualified":null,"image":"1f9dd-1f3fe.png","sheet_x":54,"sheet_y":10,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DD-1F3FE-200D-2642-FE0F"},"1F3FF":{"unified":"1F9DD-1F3FF","non_qualified":null,"image":"1f9dd-1f3ff.png","sheet_x":54,"sheet_y":11,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DD-1F3FF-200D-2642-FE0F"}},"obsoleted_by":"1F9DD-200D-2642-FE0F"},{"name":"WOMAN GENIE","unified":"1F9DE-200D-2640-FE0F","non_qualified":"1F9DE-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9de-200d-2640-fe0f.png","sheet_x":54,"sheet_y":12,"short_name":"female_genie","short_names":["female_genie"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":397,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MAN GENIE","unified":"1F9DE-200D-2642-FE0F","non_qualified":"1F9DE-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9de-200d-2642-fe0f.png","sheet_x":54,"sheet_y":13,"short_name":"male_genie","short_names":["male_genie"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":396,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DE"},{"name":"GENIE","unified":"1F9DE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9de.png","sheet_x":54,"sheet_y":14,"short_name":"genie","short_names":["genie"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":395,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DE-200D-2642-FE0F"},{"name":"WOMAN ZOMBIE","unified":"1F9DF-200D-2640-FE0F","non_qualified":"1F9DF-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9df-200d-2640-fe0f.png","sheet_x":54,"sheet_y":15,"short_name":"female_zombie","short_names":["female_zombie"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":400,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MAN ZOMBIE","unified":"1F9DF-200D-2642-FE0F","non_qualified":"1F9DF-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9df-200d-2642-fe0f.png","sheet_x":54,"sheet_y":16,"short_name":"male_zombie","short_names":["male_zombie"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":399,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoletes":"1F9DF"},{"name":"ZOMBIE","unified":"1F9DF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9df.png","sheet_x":54,"sheet_y":17,"short_name":"zombie","short_names":["zombie"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-fantasy","sort_order":398,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"obsoleted_by":"1F9DF-200D-2642-FE0F"},{"name":"BRAIN","unified":"1F9E0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9e0.png","sheet_x":54,"sheet_y":18,"short_name":"brain","short_names":["brain"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":220,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ORANGE HEART","unified":"1F9E1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9e1.png","sheet_x":54,"sheet_y":19,"short_name":"orange_heart","short_names":["orange_heart"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":145,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BILLED CAP","unified":"1F9E2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9e2.png","sheet_x":54,"sheet_y":20,"short_name":"billed_cap","short_names":["billed_cap"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1190,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SCARF","unified":"1F9E3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9e3.png","sheet_x":54,"sheet_y":21,"short_name":"scarf","short_names":["scarf"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1158,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GLOVES","unified":"1F9E4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9e4.png","sheet_x":54,"sheet_y":22,"short_name":"gloves","short_names":["gloves"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1159,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COAT","unified":"1F9E5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9e5.png","sheet_x":54,"sheet_y":23,"short_name":"coat","short_names":["coat"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1160,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SOCKS","unified":"1F9E6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9e6.png","sheet_x":54,"sheet_y":24,"short_name":"socks","short_names":["socks"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1161,"added_in":"5.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RED GIFT ENVELOPE","unified":"1F9E7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9e7.png","sheet_x":54,"sheet_y":25,"short_name":"red_envelope","short_names":["red_envelope"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1080,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FIRECRACKER","unified":"1F9E8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9e8.png","sheet_x":54,"sheet_y":26,"short_name":"firecracker","short_names":["firecracker"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1069,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JIGSAW PUZZLE PIECE","unified":"1F9E9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9e9.png","sheet_x":54,"sheet_y":27,"short_name":"jigsaw","short_names":["jigsaw"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1130,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TEST TUBE","unified":"1F9EA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ea.png","sheet_x":54,"sheet_y":28,"short_name":"test_tube","short_names":["test_tube"],"text":null,"texts":null,"category":"Objects","subcategory":"science","sort_order":1365,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PETRI DISH","unified":"1F9EB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9eb.png","sheet_x":54,"sheet_y":29,"short_name":"petri_dish","short_names":["petri_dish"],"text":null,"texts":null,"category":"Objects","subcategory":"science","sort_order":1366,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DNA DOUBLE HELIX","unified":"1F9EC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ec.png","sheet_x":54,"sheet_y":30,"short_name":"dna","short_names":["dna"],"text":null,"texts":null,"category":"Objects","subcategory":"science","sort_order":1367,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COMPASS","unified":"1F9ED","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ed.png","sheet_x":54,"sheet_y":31,"short_name":"compass","short_names":["compass"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-map","sort_order":853,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ABACUS","unified":"1F9EE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ee.png","sheet_x":54,"sheet_y":32,"short_name":"abacus","short_names":["abacus"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1245,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FIRE EXTINGUISHER","unified":"1F9EF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ef.png","sheet_x":54,"sheet_y":33,"short_name":"fire_extinguisher","short_names":["fire_extinguisher"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1401,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TOOLBOX","unified":"1F9F0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9f0.png","sheet_x":54,"sheet_y":34,"short_name":"toolbox","short_names":["toolbox"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1361,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BRICK","unified":"1F9F1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9f1.png","sheet_x":54,"sheet_y":35,"short_name":"bricks","short_names":["bricks"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":866,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MAGNET","unified":"1F9F2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9f2.png","sheet_x":54,"sheet_y":36,"short_name":"magnet","short_names":["magnet"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1362,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LUGGAGE","unified":"1F9F3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9f3.png","sheet_x":54,"sheet_y":37,"short_name":"luggage","short_names":["luggage"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"hotel","sort_order":986,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOTION BOTTLE","unified":"1F9F4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9f4.png","sheet_x":54,"sheet_y":38,"short_name":"lotion_bottle","short_names":["lotion_bottle"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1391,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPOOL OF THREAD","unified":"1F9F5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9f5.png","sheet_x":54,"sheet_y":39,"short_name":"thread","short_names":["thread"],"text":null,"texts":null,"category":"Activities","subcategory":"arts & crafts","sort_order":1146,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BALL OF YARN","unified":"1F9F6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9f6.png","sheet_x":54,"sheet_y":40,"short_name":"yarn","short_names":["yarn"],"text":null,"texts":null,"category":"Activities","subcategory":"arts & crafts","sort_order":1148,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SAFETY PIN","unified":"1F9F7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9f7.png","sheet_x":54,"sheet_y":41,"short_name":"safety_pin","short_names":["safety_pin"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1392,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TEDDY BEAR","unified":"1F9F8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9f8.png","sheet_x":54,"sheet_y":42,"short_name":"teddy_bear","short_names":["teddy_bear"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1131,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BROOM","unified":"1F9F9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9f9.png","sheet_x":54,"sheet_y":43,"short_name":"broom","short_names":["broom"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1393,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BASKET","unified":"1F9FA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9fa.png","sheet_x":54,"sheet_y":44,"short_name":"basket","short_names":["basket"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1394,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROLL OF PAPER","unified":"1F9FB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9fb.png","sheet_x":54,"sheet_y":45,"short_name":"roll_of_paper","short_names":["roll_of_paper"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1395,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BAR OF SOAP","unified":"1F9FC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9fc.png","sheet_x":54,"sheet_y":46,"short_name":"soap","short_names":["soap"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1397,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPONGE","unified":"1F9FD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9fd.png","sheet_x":54,"sheet_y":47,"short_name":"sponge","short_names":["sponge"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1400,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RECEIPT","unified":"1F9FE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9fe.png","sheet_x":54,"sheet_y":48,"short_name":"receipt","short_names":["receipt"],"text":null,"texts":null,"category":"Objects","subcategory":"money","sort_order":1287,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NAZAR AMULET","unified":"1F9FF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9ff.png","sheet_x":54,"sheet_y":49,"short_name":"nazar_amulet","short_names":["nazar_amulet"],"text":null,"texts":null,"category":"Objects","subcategory":"other-object","sort_order":1407,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BALLET SHOES","unified":"1FA70","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa70.png","sheet_x":54,"sheet_y":50,"short_name":"ballet_shoes","short_names":["ballet_shoes"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1183,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ONE-PIECE SWIMSUIT","unified":"1FA71","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa71.png","sheet_x":54,"sheet_y":51,"short_name":"one-piece_swimsuit","short_names":["one-piece_swimsuit"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1165,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BRIEFS","unified":"1FA72","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa72.png","sheet_x":54,"sheet_y":52,"short_name":"briefs","short_names":["briefs"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1166,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHORTS","unified":"1FA73","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa73.png","sheet_x":54,"sheet_y":53,"short_name":"shorts","short_names":["shorts"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1167,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"THONG SANDAL","unified":"1FA74","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa74.png","sheet_x":54,"sheet_y":54,"short_name":"thong_sandal","short_names":["thong_sandal"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1176,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LIGHT BLUE HEART","unified":"1FA75","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa75.png","sheet_x":54,"sheet_y":55,"short_name":"light_blue_heart","short_names":["light_blue_heart"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":149,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GREY HEART","unified":"1FA76","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa76.png","sheet_x":54,"sheet_y":56,"short_name":"grey_heart","short_names":["grey_heart"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":153,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PINK HEART","unified":"1FA77","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa77.png","sheet_x":54,"sheet_y":57,"short_name":"pink_heart","short_names":["pink_heart"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":144,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DROP OF BLOOD","unified":"1FA78","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa78.png","sheet_x":54,"sheet_y":58,"short_name":"drop_of_blood","short_names":["drop_of_blood"],"text":null,"texts":null,"category":"Objects","subcategory":"medical","sort_order":1372,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ADHESIVE BANDAGE","unified":"1FA79","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa79.png","sheet_x":54,"sheet_y":59,"short_name":"adhesive_bandage","short_names":["adhesive_bandage"],"text":null,"texts":null,"category":"Objects","subcategory":"medical","sort_order":1374,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STETHOSCOPE","unified":"1FA7A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa7a.png","sheet_x":54,"sheet_y":60,"short_name":"stethoscope","short_names":["stethoscope"],"text":null,"texts":null,"category":"Objects","subcategory":"medical","sort_order":1376,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"X-RAY","unified":"1FA7B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa7b.png","sheet_x":54,"sheet_y":61,"short_name":"x-ray","short_names":["x-ray"],"text":null,"texts":null,"category":"Objects","subcategory":"medical","sort_order":1377,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CRUTCH","unified":"1FA7C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa7c.png","sheet_x":55,"sheet_y":0,"short_name":"crutch","short_names":["crutch"],"text":null,"texts":null,"category":"Objects","subcategory":"medical","sort_order":1375,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"YO-YO","unified":"1FA80","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa80.png","sheet_x":55,"sheet_y":1,"short_name":"yo-yo","short_names":["yo-yo"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1120,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KITE","unified":"1FA81","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa81.png","sheet_x":55,"sheet_y":2,"short_name":"kite","short_names":["kite"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1121,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PARACHUTE","unified":"1FA82","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa82.png","sheet_x":55,"sheet_y":3,"short_name":"parachute","short_names":["parachute"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":976,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BOOMERANG","unified":"1FA83","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa83.png","sheet_x":55,"sheet_y":4,"short_name":"boomerang","short_names":["boomerang"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1346,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MAGIC WAND","unified":"1FA84","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa84.png","sheet_x":55,"sheet_y":5,"short_name":"magic_wand","short_names":["magic_wand"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1125,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PINATA","unified":"1FA85","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa85.png","sheet_x":55,"sheet_y":6,"short_name":"pinata","short_names":["pinata"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1132,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NESTING DOLLS","unified":"1FA86","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa86.png","sheet_x":55,"sheet_y":7,"short_name":"nesting_dolls","short_names":["nesting_dolls"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1134,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MARACAS","unified":"1FA87","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa87.png","sheet_x":55,"sheet_y":8,"short_name":"maracas","short_names":["maracas"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1224,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLUTE","unified":"1FA88","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa88.png","sheet_x":55,"sheet_y":9,"short_name":"flute","short_names":["flute"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1225,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RINGED PLANET","unified":"1FA90","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa90.png","sheet_x":55,"sheet_y":10,"short_name":"ringed_planet","short_names":["ringed_planet"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1034,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHAIR","unified":"1FA91","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa91.png","sheet_x":55,"sheet_y":11,"short_name":"chair","short_names":["chair"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1384,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RAZOR","unified":"1FA92","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa92.png","sheet_x":55,"sheet_y":12,"short_name":"razor","short_names":["razor"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1390,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AXE","unified":"1FA93","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa93.png","sheet_x":55,"sheet_y":13,"short_name":"axe","short_names":["axe"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1339,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DIYA LAMP","unified":"1FA94","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa94.png","sheet_x":55,"sheet_y":14,"short_name":"diya_lamp","short_names":["diya_lamp"],"text":null,"texts":null,"category":"Objects","subcategory":"light & video","sort_order":1261,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BANJO","unified":"1FA95","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa95.png","sheet_x":55,"sheet_y":15,"short_name":"banjo","short_names":["banjo"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1221,"added_in":"12.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MILITARY HELMET","unified":"1FA96","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa96.png","sheet_x":55,"sheet_y":16,"short_name":"military_helmet","short_names":["military_helmet"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1191,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ACCORDION","unified":"1FA97","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa97.png","sheet_x":55,"sheet_y":17,"short_name":"accordion","short_names":["accordion"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1216,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LONG DRUM","unified":"1FA98","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa98.png","sheet_x":55,"sheet_y":18,"short_name":"long_drum","short_names":["long_drum"],"text":null,"texts":null,"category":"Objects","subcategory":"musical-instrument","sort_order":1223,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COIN","unified":"1FA99","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa99.png","sheet_x":55,"sheet_y":19,"short_name":"coin","short_names":["coin"],"text":null,"texts":null,"category":"Objects","subcategory":"money","sort_order":1280,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CARPENTRY SAW","unified":"1FA9A","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa9a.png","sheet_x":55,"sheet_y":20,"short_name":"carpentry_saw","short_names":["carpentry_saw"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1349,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SCREWDRIVER","unified":"1FA9B","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa9b.png","sheet_x":55,"sheet_y":21,"short_name":"screwdriver","short_names":["screwdriver"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1351,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LADDER","unified":"1FA9C","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa9c.png","sheet_x":55,"sheet_y":22,"short_name":"ladder","short_names":["ladder"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1363,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOOK","unified":"1FA9D","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa9d.png","sheet_x":55,"sheet_y":23,"short_name":"hook","short_names":["hook"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1360,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MIRROR","unified":"1FA9E","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa9e.png","sheet_x":55,"sheet_y":24,"short_name":"mirror","short_names":["mirror"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1380,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WINDOW","unified":"1FA9F","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fa9f.png","sheet_x":55,"sheet_y":25,"short_name":"window","short_names":["window"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1381,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PLUNGER","unified":"1FAA0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faa0.png","sheet_x":55,"sheet_y":26,"short_name":"plunger","short_names":["plunger"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1386,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SEWING NEEDLE","unified":"1FAA1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faa1.png","sheet_x":55,"sheet_y":27,"short_name":"sewing_needle","short_names":["sewing_needle"],"text":null,"texts":null,"category":"Activities","subcategory":"arts & crafts","sort_order":1147,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KNOT","unified":"1FAA2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faa2.png","sheet_x":55,"sheet_y":28,"short_name":"knot","short_names":["knot"],"text":null,"texts":null,"category":"Activities","subcategory":"arts & crafts","sort_order":1149,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUCKET","unified":"1FAA3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faa3.png","sheet_x":55,"sheet_y":29,"short_name":"bucket","short_names":["bucket"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1396,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOUSE TRAP","unified":"1FAA4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faa4.png","sheet_x":55,"sheet_y":30,"short_name":"mouse_trap","short_names":["mouse_trap"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1389,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TOOTHBRUSH","unified":"1FAA5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faa5.png","sheet_x":55,"sheet_y":31,"short_name":"toothbrush","short_names":["toothbrush"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1399,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEADSTONE","unified":"1FAA6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faa6.png","sheet_x":55,"sheet_y":32,"short_name":"headstone","short_names":["headstone"],"text":null,"texts":null,"category":"Objects","subcategory":"other-object","sort_order":1405,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PLACARD","unified":"1FAA7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faa7.png","sheet_x":55,"sheet_y":33,"short_name":"placard","short_names":["placard"],"text":null,"texts":null,"category":"Objects","subcategory":"other-object","sort_order":1410,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ROCK","unified":"1FAA8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faa8.png","sheet_x":55,"sheet_y":34,"short_name":"rock","short_names":["rock"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":867,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MIRROR BALL","unified":"1FAA9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faa9.png","sheet_x":55,"sheet_y":35,"short_name":"mirror_ball","short_names":["mirror_ball"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1133,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"IDENTIFICATION CARD","unified":"1FAAA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faaa.png","sheet_x":55,"sheet_y":36,"short_name":"identification_card","short_names":["identification_card"],"text":null,"texts":null,"category":"Objects","subcategory":"other-object","sort_order":1411,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOW BATTERY","unified":"1FAAB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faab.png","sheet_x":55,"sheet_y":37,"short_name":"low_battery","short_names":["low_battery"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1233,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HAMSA","unified":"1FAAC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faac.png","sheet_x":55,"sheet_y":38,"short_name":"hamsa","short_names":["hamsa"],"text":null,"texts":null,"category":"Objects","subcategory":"other-object","sort_order":1408,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FOLDING HAND FAN","unified":"1FAAD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faad.png","sheet_x":55,"sheet_y":39,"short_name":"folding_hand_fan","short_names":["folding_hand_fan"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1170,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HAIR PICK","unified":"1FAAE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faae.png","sheet_x":55,"sheet_y":40,"short_name":"hair_pick","short_names":["hair_pick"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1185,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KHANDA","unified":"1FAAF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faaf.png","sheet_x":55,"sheet_y":41,"short_name":"khanda","short_names":["khanda"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1471,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLY","unified":"1FAB0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fab0.png","sheet_x":55,"sheet_y":42,"short_name":"fly","short_names":["fly"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":681,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WORM","unified":"1FAB1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fab1.png","sheet_x":55,"sheet_y":43,"short_name":"worm","short_names":["worm"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":682,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BEETLE","unified":"1FAB2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fab2.png","sheet_x":55,"sheet_y":44,"short_name":"beetle","short_names":["beetle"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":673,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COCKROACH","unified":"1FAB3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fab3.png","sheet_x":55,"sheet_y":45,"short_name":"cockroach","short_names":["cockroach"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bug","sort_order":676,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POTTED PLANT","unified":"1FAB4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fab4.png","sheet_x":55,"sheet_y":46,"short_name":"potted_plant","short_names":["potted_plant"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":697,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOOD","unified":"1FAB5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fab5.png","sheet_x":55,"sheet_y":47,"short_name":"wood","short_names":["wood"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-building","sort_order":868,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FEATHER","unified":"1FAB6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fab6.png","sheet_x":55,"sheet_y":48,"short_name":"feather","short_names":["feather"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":639,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LOTUS","unified":"1FAB7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fab7.png","sheet_x":55,"sheet_y":49,"short_name":"lotus","short_names":["lotus"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":687,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CORAL","unified":"1FAB8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fab8.png","sheet_x":55,"sheet_y":50,"short_name":"coral","short_names":["coral"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":666,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EMPTY NEST","unified":"1FAB9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fab9.png","sheet_x":55,"sheet_y":51,"short_name":"empty_nest","short_names":["empty_nest"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":709,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEST WITH EGGS","unified":"1FABA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faba.png","sheet_x":55,"sheet_y":52,"short_name":"nest_with_eggs","short_names":["nest_with_eggs"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":710,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HYACINTH","unified":"1FABB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fabb.png","sheet_x":55,"sheet_y":53,"short_name":"hyacinth","short_names":["hyacinth"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-flower","sort_order":695,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JELLYFISH","unified":"1FABC","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fabc.png","sheet_x":55,"sheet_y":54,"short_name":"jellyfish","short_names":["jellyfish"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-marine","sort_order":667,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WING","unified":"1FABD","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fabd.png","sheet_x":55,"sheet_y":55,"short_name":"wing","short_names":["wing"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":643,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GOOSE","unified":"1FABF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fabf.png","sheet_x":55,"sheet_y":56,"short_name":"goose","short_names":["goose"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-bird","sort_order":645,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ANATOMICAL HEART","unified":"1FAC0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fac0.png","sheet_x":55,"sheet_y":57,"short_name":"anatomical_heart","short_names":["anatomical_heart"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":221,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LUNGS","unified":"1FAC1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fac1.png","sheet_x":55,"sheet_y":58,"short_name":"lungs","short_names":["lungs"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":222,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PEOPLE HUGGING","unified":"1FAC2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fac2.png","sheet_x":55,"sheet_y":59,"short_name":"people_hugging","short_names":["people_hugging"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-symbol","sort_order":547,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PREGNANT MAN","unified":"1FAC3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fac3.png","sheet_x":55,"sheet_y":60,"short_name":"pregnant_man","short_names":["pregnant_man"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":364,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAC3-1F3FB","non_qualified":null,"image":"1fac3-1f3fb.png","sheet_x":55,"sheet_y":61,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAC3-1F3FC","non_qualified":null,"image":"1fac3-1f3fc.png","sheet_x":56,"sheet_y":0,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAC3-1F3FD","non_qualified":null,"image":"1fac3-1f3fd.png","sheet_x":56,"sheet_y":1,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAC3-1F3FE","non_qualified":null,"image":"1fac3-1f3fe.png","sheet_x":56,"sheet_y":2,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAC3-1F3FF","non_qualified":null,"image":"1fac3-1f3ff.png","sheet_x":56,"sheet_y":3,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PREGNANT PERSON","unified":"1FAC4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fac4.png","sheet_x":56,"sheet_y":4,"short_name":"pregnant_person","short_names":["pregnant_person"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":365,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAC4-1F3FB","non_qualified":null,"image":"1fac4-1f3fb.png","sheet_x":56,"sheet_y":5,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAC4-1F3FC","non_qualified":null,"image":"1fac4-1f3fc.png","sheet_x":56,"sheet_y":6,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAC4-1F3FD","non_qualified":null,"image":"1fac4-1f3fd.png","sheet_x":56,"sheet_y":7,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAC4-1F3FE","non_qualified":null,"image":"1fac4-1f3fe.png","sheet_x":56,"sheet_y":8,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAC4-1F3FF","non_qualified":null,"image":"1fac4-1f3ff.png","sheet_x":56,"sheet_y":9,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PERSON WITH CROWN","unified":"1FAC5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fac5.png","sheet_x":56,"sheet_y":10,"short_name":"person_with_crown","short_names":["person_with_crown"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-role","sort_order":349,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAC5-1F3FB","non_qualified":null,"image":"1fac5-1f3fb.png","sheet_x":56,"sheet_y":11,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAC5-1F3FC","non_qualified":null,"image":"1fac5-1f3fc.png","sheet_x":56,"sheet_y":12,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAC5-1F3FD","non_qualified":null,"image":"1fac5-1f3fd.png","sheet_x":56,"sheet_y":13,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAC5-1F3FE","non_qualified":null,"image":"1fac5-1f3fe.png","sheet_x":56,"sheet_y":14,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAC5-1F3FF","non_qualified":null,"image":"1fac5-1f3ff.png","sheet_x":56,"sheet_y":15,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MOOSE","unified":"1FACE","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1face.png","sheet_x":56,"sheet_y":16,"short_name":"moose","short_names":["moose"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":579,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DONKEY","unified":"1FACF","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1facf.png","sheet_x":56,"sheet_y":17,"short_name":"donkey","short_names":["donkey"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"animal-mammal","sort_order":580,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLUEBERRIES","unified":"1FAD0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fad0.png","sheet_x":56,"sheet_y":18,"short_name":"blueberries","short_names":["blueberries"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":727,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BELL PEPPER","unified":"1FAD1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fad1.png","sheet_x":56,"sheet_y":19,"short_name":"bell_pepper","short_names":["bell_pepper"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":738,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OLIVE","unified":"1FAD2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fad2.png","sheet_x":56,"sheet_y":20,"short_name":"olive","short_names":["olive"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-fruit","sort_order":730,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLATBREAD","unified":"1FAD3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fad3.png","sheet_x":56,"sheet_y":21,"short_name":"flatbread","short_names":["flatbread"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":753,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TAMALE","unified":"1FAD4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fad4.png","sheet_x":56,"sheet_y":22,"short_name":"tamale","short_names":["tamale"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":770,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FONDUE","unified":"1FAD5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fad5.png","sheet_x":56,"sheet_y":23,"short_name":"fondue","short_names":["fondue"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-prepared","sort_order":777,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TEAPOT","unified":"1FAD6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fad6.png","sheet_x":56,"sheet_y":24,"short_name":"teapot","short_names":["teapot"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":823,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"POURING LIQUID","unified":"1FAD7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fad7.png","sheet_x":56,"sheet_y":25,"short_name":"pouring_liquid","short_names":["pouring_liquid"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":834,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BEANS","unified":"1FAD8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fad8.png","sheet_x":56,"sheet_y":26,"short_name":"beans","short_names":["beans"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":745,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"JAR","unified":"1FAD9","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fad9.png","sheet_x":56,"sheet_y":27,"short_name":"jar","short_names":["jar"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"dishware","sort_order":845,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GINGER ROOT","unified":"1FADA","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fada.png","sheet_x":56,"sheet_y":28,"short_name":"ginger_root","short_names":["ginger_root"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":747,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PEA POD","unified":"1FADB","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fadb.png","sheet_x":56,"sheet_y":29,"short_name":"pea_pod","short_names":["pea_pod"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"food-vegetable","sort_order":748,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MELTING FACE","unified":"1FAE0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fae0.png","sheet_x":56,"sheet_y":30,"short_name":"melting_face","short_names":["melting_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-smiling","sort_order":11,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SALUTING FACE","unified":"1FAE1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fae1.png","sheet_x":56,"sheet_y":31,"short_name":"saluting_face","short_names":["saluting_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-hand","sort_order":36,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH OPEN EYES AND HAND OVER MOUTH","unified":"1FAE2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fae2.png","sheet_x":56,"sheet_y":32,"short_name":"face_with_open_eyes_and_hand_over_mouth","short_names":["face_with_open_eyes_and_hand_over_mouth"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-hand","sort_order":32,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH PEEKING EYE","unified":"1FAE3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fae3.png","sheet_x":56,"sheet_y":33,"short_name":"face_with_peeking_eye","short_names":["face_with_peeking_eye"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-hand","sort_order":33,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FACE WITH DIAGONAL MOUTH","unified":"1FAE4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fae4.png","sheet_x":56,"sheet_y":34,"short_name":"face_with_diagonal_mouth","short_names":["face_with_diagonal_mouth"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":77,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOTTED LINE FACE","unified":"1FAE5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fae5.png","sheet_x":56,"sheet_y":35,"short_name":"dotted_line_face","short_names":["dotted_line_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":42,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BITING LIP","unified":"1FAE6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fae6.png","sheet_x":56,"sheet_y":36,"short_name":"biting_lip","short_names":["biting_lip"],"text":null,"texts":null,"category":"People & Body","subcategory":"body-parts","sort_order":229,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BUBBLES","unified":"1FAE7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fae7.png","sheet_x":56,"sheet_y":37,"short_name":"bubbles","short_names":["bubbles"],"text":null,"texts":null,"category":"Objects","subcategory":"household","sort_order":1398,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHAKING FACE","unified":"1FAE8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1fae8.png","sheet_x":56,"sheet_y":38,"short_name":"shaking_face","short_names":["shaking_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-neutral-skeptical","sort_order":50,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HAND WITH INDEX FINGER AND THUMB CROSSED","unified":"1FAF0","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faf0.png","sheet_x":56,"sheet_y":39,"short_name":"hand_with_index_finger_and_thumb_crossed","short_names":["hand_with_index_finger_and_thumb_crossed"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-partial","sort_order":185,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAF0-1F3FB","non_qualified":null,"image":"1faf0-1f3fb.png","sheet_x":56,"sheet_y":40,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAF0-1F3FC","non_qualified":null,"image":"1faf0-1f3fc.png","sheet_x":56,"sheet_y":41,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAF0-1F3FD","non_qualified":null,"image":"1faf0-1f3fd.png","sheet_x":56,"sheet_y":42,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAF0-1F3FE","non_qualified":null,"image":"1faf0-1f3fe.png","sheet_x":56,"sheet_y":43,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAF0-1F3FF","non_qualified":null,"image":"1faf0-1f3ff.png","sheet_x":56,"sheet_y":44,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"RIGHTWARDS HAND","unified":"1FAF1","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faf1.png","sheet_x":56,"sheet_y":45,"short_name":"rightwards_hand","short_names":["rightwards_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":174,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAF1-1F3FB","non_qualified":null,"image":"1faf1-1f3fb.png","sheet_x":56,"sheet_y":46,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAF1-1F3FC","non_qualified":null,"image":"1faf1-1f3fc.png","sheet_x":56,"sheet_y":47,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAF1-1F3FD","non_qualified":null,"image":"1faf1-1f3fd.png","sheet_x":56,"sheet_y":48,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAF1-1F3FE","non_qualified":null,"image":"1faf1-1f3fe.png","sheet_x":56,"sheet_y":49,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAF1-1F3FF","non_qualified":null,"image":"1faf1-1f3ff.png","sheet_x":56,"sheet_y":50,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"LEFTWARDS HAND","unified":"1FAF2","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faf2.png","sheet_x":56,"sheet_y":51,"short_name":"leftwards_hand","short_names":["leftwards_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":175,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAF2-1F3FB","non_qualified":null,"image":"1faf2-1f3fb.png","sheet_x":56,"sheet_y":52,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAF2-1F3FC","non_qualified":null,"image":"1faf2-1f3fc.png","sheet_x":56,"sheet_y":53,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAF2-1F3FD","non_qualified":null,"image":"1faf2-1f3fd.png","sheet_x":56,"sheet_y":54,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAF2-1F3FE","non_qualified":null,"image":"1faf2-1f3fe.png","sheet_x":56,"sheet_y":55,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAF2-1F3FF","non_qualified":null,"image":"1faf2-1f3ff.png","sheet_x":56,"sheet_y":56,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PALM DOWN HAND","unified":"1FAF3","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faf3.png","sheet_x":56,"sheet_y":57,"short_name":"palm_down_hand","short_names":["palm_down_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":176,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAF3-1F3FB","non_qualified":null,"image":"1faf3-1f3fb.png","sheet_x":56,"sheet_y":58,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAF3-1F3FC","non_qualified":null,"image":"1faf3-1f3fc.png","sheet_x":56,"sheet_y":59,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAF3-1F3FD","non_qualified":null,"image":"1faf3-1f3fd.png","sheet_x":56,"sheet_y":60,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAF3-1F3FE","non_qualified":null,"image":"1faf3-1f3fe.png","sheet_x":56,"sheet_y":61,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAF3-1F3FF","non_qualified":null,"image":"1faf3-1f3ff.png","sheet_x":57,"sheet_y":0,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PALM UP HAND","unified":"1FAF4","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faf4.png","sheet_x":57,"sheet_y":1,"short_name":"palm_up_hand","short_names":["palm_up_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":177,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAF4-1F3FB","non_qualified":null,"image":"1faf4-1f3fb.png","sheet_x":57,"sheet_y":2,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAF4-1F3FC","non_qualified":null,"image":"1faf4-1f3fc.png","sheet_x":57,"sheet_y":3,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAF4-1F3FD","non_qualified":null,"image":"1faf4-1f3fd.png","sheet_x":57,"sheet_y":4,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAF4-1F3FE","non_qualified":null,"image":"1faf4-1f3fe.png","sheet_x":57,"sheet_y":5,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAF4-1F3FF","non_qualified":null,"image":"1faf4-1f3ff.png","sheet_x":57,"sheet_y":6,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"INDEX POINTING AT THE VIEWER","unified":"1FAF5","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faf5.png","sheet_x":57,"sheet_y":7,"short_name":"index_pointing_at_the_viewer","short_names":["index_pointing_at_the_viewer"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-single-finger","sort_order":195,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAF5-1F3FB","non_qualified":null,"image":"1faf5-1f3fb.png","sheet_x":57,"sheet_y":8,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAF5-1F3FC","non_qualified":null,"image":"1faf5-1f3fc.png","sheet_x":57,"sheet_y":9,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAF5-1F3FD","non_qualified":null,"image":"1faf5-1f3fd.png","sheet_x":57,"sheet_y":10,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAF5-1F3FE","non_qualified":null,"image":"1faf5-1f3fe.png","sheet_x":57,"sheet_y":11,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAF5-1F3FF","non_qualified":null,"image":"1faf5-1f3ff.png","sheet_x":57,"sheet_y":12,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"HEART HANDS","unified":"1FAF6","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faf6.png","sheet_x":57,"sheet_y":13,"short_name":"heart_hands","short_names":["heart_hands"],"text":null,"texts":null,"category":"People & Body","subcategory":"hands","sort_order":204,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAF6-1F3FB","non_qualified":null,"image":"1faf6-1f3fb.png","sheet_x":57,"sheet_y":14,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAF6-1F3FC","non_qualified":null,"image":"1faf6-1f3fc.png","sheet_x":57,"sheet_y":15,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAF6-1F3FD","non_qualified":null,"image":"1faf6-1f3fd.png","sheet_x":57,"sheet_y":16,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAF6-1F3FE","non_qualified":null,"image":"1faf6-1f3fe.png","sheet_x":57,"sheet_y":17,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAF6-1F3FF","non_qualified":null,"image":"1faf6-1f3ff.png","sheet_x":57,"sheet_y":18,"added_in":"14.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"LEFTWARDS PUSHING HAND","unified":"1FAF7","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faf7.png","sheet_x":57,"sheet_y":19,"short_name":"leftwards_pushing_hand","short_names":["leftwards_pushing_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":178,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAF7-1F3FB","non_qualified":null,"image":"1faf7-1f3fb.png","sheet_x":57,"sheet_y":20,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAF7-1F3FC","non_qualified":null,"image":"1faf7-1f3fc.png","sheet_x":57,"sheet_y":21,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAF7-1F3FD","non_qualified":null,"image":"1faf7-1f3fd.png","sheet_x":57,"sheet_y":22,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAF7-1F3FE","non_qualified":null,"image":"1faf7-1f3fe.png","sheet_x":57,"sheet_y":23,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAF7-1F3FF","non_qualified":null,"image":"1faf7-1f3ff.png","sheet_x":57,"sheet_y":24,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"RIGHTWARDS PUSHING HAND","unified":"1FAF8","non_qualified":null,"docomo":null,"au":null,"softbank":null,"google":null,"image":"1faf8.png","sheet_x":57,"sheet_y":25,"short_name":"rightwards_pushing_hand","short_names":["rightwards_pushing_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":179,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"1FAF8-1F3FB","non_qualified":null,"image":"1faf8-1f3fb.png","sheet_x":57,"sheet_y":26,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"1FAF8-1F3FC","non_qualified":null,"image":"1faf8-1f3fc.png","sheet_x":57,"sheet_y":27,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"1FAF8-1F3FD","non_qualified":null,"image":"1faf8-1f3fd.png","sheet_x":57,"sheet_y":28,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"1FAF8-1F3FE","non_qualified":null,"image":"1faf8-1f3fe.png","sheet_x":57,"sheet_y":29,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"1FAF8-1F3FF","non_qualified":null,"image":"1faf8-1f3ff.png","sheet_x":57,"sheet_y":30,"added_in":"15.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"DOUBLE EXCLAMATION MARK","unified":"203C-FE0F","non_qualified":"203C","docomo":"E704","au":"EB30","softbank":null,"google":"FEB06","image":"203c-fe0f.png","sheet_x":57,"sheet_y":31,"short_name":"bangbang","short_names":["bangbang"],"text":null,"texts":null,"category":"Symbols","subcategory":"punctuation","sort_order":1519,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EXCLAMATION QUESTION MARK","unified":"2049-FE0F","non_qualified":"2049","docomo":"E703","au":"EB2F","softbank":null,"google":"FEB05","image":"2049-fe0f.png","sheet_x":57,"sheet_y":32,"short_name":"interrobang","short_names":["interrobang"],"text":null,"texts":null,"category":"Symbols","subcategory":"punctuation","sort_order":1520,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRADE MARK SIGN","unified":"2122-FE0F","non_qualified":"2122","docomo":"E732","au":"E54E","softbank":"E537","google":"FEB2A","image":"2122-fe0f.png","sheet_x":57,"sheet_y":33,"short_name":"tm","short_names":["tm"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1548,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"INFORMATION SOURCE","unified":"2139-FE0F","non_qualified":"2139","docomo":null,"au":"E533","softbank":null,"google":"FEB47","image":"2139-fe0f.png","sheet_x":57,"sheet_y":34,"short_name":"information_source","short_names":["information_source"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1573,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEFT RIGHT ARROW","unified":"2194-FE0F","non_qualified":"2194","docomo":"E73C","au":"EB7A","softbank":null,"google":"FEAF6","image":"2194-fe0f.png","sheet_x":57,"sheet_y":35,"short_name":"left_right_arrow","short_names":["left_right_arrow"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1447,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"UP DOWN ARROW","unified":"2195-FE0F","non_qualified":"2195","docomo":"E73D","au":"EB7B","softbank":null,"google":"FEAF7","image":"2195-fe0f.png","sheet_x":57,"sheet_y":36,"short_name":"arrow_up_down","short_names":["arrow_up_down"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1446,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NORTH WEST ARROW","unified":"2196-FE0F","non_qualified":"2196","docomo":"E697","au":"E54C","softbank":"E237","google":"FEAF2","image":"2196-fe0f.png","sheet_x":57,"sheet_y":37,"short_name":"arrow_upper_left","short_names":["arrow_upper_left"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1445,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NORTH EAST ARROW","unified":"2197-FE0F","non_qualified":"2197","docomo":"E678","au":"E555","softbank":"E236","google":"FEAF0","image":"2197-fe0f.png","sheet_x":57,"sheet_y":38,"short_name":"arrow_upper_right","short_names":["arrow_upper_right"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1439,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SOUTH EAST ARROW","unified":"2198-FE0F","non_qualified":"2198","docomo":"E696","au":"E54D","softbank":"E238","google":"FEAF1","image":"2198-fe0f.png","sheet_x":57,"sheet_y":39,"short_name":"arrow_lower_right","short_names":["arrow_lower_right"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1441,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SOUTH WEST ARROW","unified":"2199-FE0F","non_qualified":"2199","docomo":"E6A5","au":"E556","softbank":"E239","google":"FEAF3","image":"2199-fe0f.png","sheet_x":57,"sheet_y":40,"short_name":"arrow_lower_left","short_names":["arrow_lower_left"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1443,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEFTWARDS ARROW WITH HOOK","unified":"21A9-FE0F","non_qualified":"21A9","docomo":"E6DA","au":"E55D","softbank":null,"google":"FEB83","image":"21a9-fe0f.png","sheet_x":57,"sheet_y":41,"short_name":"leftwards_arrow_with_hook","short_names":["leftwards_arrow_with_hook"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1448,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RIGHTWARDS ARROW WITH HOOK","unified":"21AA-FE0F","non_qualified":"21AA","docomo":null,"au":"E55C","softbank":null,"google":"FEB88","image":"21aa-fe0f.png","sheet_x":57,"sheet_y":42,"short_name":"arrow_right_hook","short_names":["arrow_right_hook"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1449,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WATCH","unified":"231A","non_qualified":null,"docomo":"E71F","au":"E57A","softbank":null,"google":"FE01D","image":"231a.png","sheet_x":57,"sheet_y":43,"short_name":"watch","short_names":["watch"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":989,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOURGLASS","unified":"231B","non_qualified":null,"docomo":"E71C","au":"E57B","softbank":null,"google":"FE01C","image":"231b.png","sheet_x":57,"sheet_y":44,"short_name":"hourglass","short_names":["hourglass"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":987,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"KEYBOARD","unified":"2328-FE0F","non_qualified":"2328","docomo":null,"au":null,"softbank":null,"google":null,"image":"2328-fe0f.png","sheet_x":57,"sheet_y":45,"short_name":"keyboard","short_names":["keyboard"],"text":null,"texts":null,"category":"Objects","subcategory":"computer","sort_order":1238,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EJECT BUTTON","unified":"23CF-FE0F","non_qualified":"23CF","docomo":null,"au":null,"softbank":null,"google":null,"image":"23cf-fe0f.png","sheet_x":57,"sheet_y":46,"short_name":"eject","short_names":["eject"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1502,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK RIGHT-POINTING DOUBLE TRIANGLE","unified":"23E9","non_qualified":null,"docomo":null,"au":"E530","softbank":"E23C","google":"FEAFE","image":"23e9.png","sheet_x":57,"sheet_y":47,"short_name":"fast_forward","short_names":["fast_forward"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1489,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK LEFT-POINTING DOUBLE TRIANGLE","unified":"23EA","non_qualified":null,"docomo":null,"au":"E52F","softbank":"E23D","google":"FEAFF","image":"23ea.png","sheet_x":57,"sheet_y":48,"short_name":"rewind","short_names":["rewind"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1493,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK UP-POINTING DOUBLE TRIANGLE","unified":"23EB","non_qualified":null,"docomo":null,"au":"E545","softbank":null,"google":"FEB03","image":"23eb.png","sheet_x":57,"sheet_y":49,"short_name":"arrow_double_up","short_names":["arrow_double_up"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1496,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK DOWN-POINTING DOUBLE TRIANGLE","unified":"23EC","non_qualified":null,"docomo":null,"au":"E544","softbank":null,"google":"FEB02","image":"23ec.png","sheet_x":57,"sheet_y":50,"short_name":"arrow_double_down","short_names":["arrow_double_down"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1498,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEXT TRACK BUTTON","unified":"23ED-FE0F","non_qualified":"23ED","docomo":null,"au":null,"softbank":null,"google":null,"image":"23ed-fe0f.png","sheet_x":57,"sheet_y":51,"short_name":"black_right_pointing_double_triangle_with_vertical_bar","short_names":["black_right_pointing_double_triangle_with_vertical_bar"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1490,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LAST TRACK BUTTON","unified":"23EE-FE0F","non_qualified":"23EE","docomo":null,"au":null,"softbank":null,"google":null,"image":"23ee-fe0f.png","sheet_x":57,"sheet_y":52,"short_name":"black_left_pointing_double_triangle_with_vertical_bar","short_names":["black_left_pointing_double_triangle_with_vertical_bar"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1494,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PLAY OR PAUSE BUTTON","unified":"23EF-FE0F","non_qualified":"23EF","docomo":null,"au":null,"softbank":null,"google":null,"image":"23ef-fe0f.png","sheet_x":57,"sheet_y":53,"short_name":"black_right_pointing_triangle_with_double_vertical_bar","short_names":["black_right_pointing_triangle_with_double_vertical_bar"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1491,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ALARM CLOCK","unified":"23F0","non_qualified":null,"docomo":"E6BA","au":"E594","softbank":null,"google":"FE02A","image":"23f0.png","sheet_x":57,"sheet_y":54,"short_name":"alarm_clock","short_names":["alarm_clock"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":990,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STOPWATCH","unified":"23F1-FE0F","non_qualified":"23F1","docomo":null,"au":null,"softbank":null,"google":null,"image":"23f1-fe0f.png","sheet_x":57,"sheet_y":55,"short_name":"stopwatch","short_names":["stopwatch"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":991,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TIMER CLOCK","unified":"23F2-FE0F","non_qualified":"23F2","docomo":null,"au":null,"softbank":null,"google":null,"image":"23f2-fe0f.png","sheet_x":57,"sheet_y":56,"short_name":"timer_clock","short_names":["timer_clock"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":992,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOURGLASS WITH FLOWING SAND","unified":"23F3","non_qualified":null,"docomo":"E71C","au":"E47C","softbank":null,"google":"FE01B","image":"23f3.png","sheet_x":57,"sheet_y":57,"short_name":"hourglass_flowing_sand","short_names":["hourglass_flowing_sand"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"time","sort_order":988,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PAUSE BUTTON","unified":"23F8-FE0F","non_qualified":"23F8","docomo":null,"au":null,"softbank":null,"google":null,"image":"23f8-fe0f.png","sheet_x":57,"sheet_y":58,"short_name":"double_vertical_bar","short_names":["double_vertical_bar"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1499,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STOP BUTTON","unified":"23F9-FE0F","non_qualified":"23F9","docomo":null,"au":null,"softbank":null,"google":null,"image":"23f9-fe0f.png","sheet_x":57,"sheet_y":59,"short_name":"black_square_for_stop","short_names":["black_square_for_stop"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1500,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RECORD BUTTON","unified":"23FA-FE0F","non_qualified":"23FA","docomo":null,"au":null,"softbank":null,"google":null,"image":"23fa-fe0f.png","sheet_x":57,"sheet_y":60,"short_name":"black_circle_for_record","short_names":["black_circle_for_record"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1501,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CIRCLED LATIN CAPITAL LETTER M","unified":"24C2-FE0F","non_qualified":"24C2","docomo":"E65C","au":"E5BC","softbank":null,"google":"FE7E1","image":"24c2-fe0f.png","sheet_x":57,"sheet_y":61,"short_name":"m","short_names":["m"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1575,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK SMALL SQUARE","unified":"25AA-FE0F","non_qualified":"25AA","docomo":null,"au":"E532","softbank":null,"google":"FEB6E","image":"25aa-fe0f.png","sheet_x":58,"sheet_y":0,"short_name":"black_small_square","short_names":["black_small_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1623,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE SMALL SQUARE","unified":"25AB-FE0F","non_qualified":"25AB","docomo":null,"au":"E531","softbank":null,"google":"FEB6D","image":"25ab-fe0f.png","sheet_x":58,"sheet_y":1,"short_name":"white_small_square","short_names":["white_small_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1624,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK RIGHT-POINTING TRIANGLE","unified":"25B6-FE0F","non_qualified":"25B6","docomo":null,"au":"E52E","softbank":"E23A","google":"FEAFC","image":"25b6-fe0f.png","sheet_x":58,"sheet_y":2,"short_name":"arrow_forward","short_names":["arrow_forward"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1488,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK LEFT-POINTING TRIANGLE","unified":"25C0-FE0F","non_qualified":"25C0","docomo":null,"au":"E52D","softbank":"E23B","google":"FEAFD","image":"25c0-fe0f.png","sheet_x":58,"sheet_y":3,"short_name":"arrow_backward","short_names":["arrow_backward"],"text":null,"texts":null,"category":"Symbols","subcategory":"av-symbol","sort_order":1492,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE MEDIUM SQUARE","unified":"25FB-FE0F","non_qualified":"25FB","docomo":null,"au":"E538","softbank":null,"google":"FEB71","image":"25fb-fe0f.png","sheet_x":58,"sheet_y":4,"short_name":"white_medium_square","short_names":["white_medium_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1620,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK MEDIUM SQUARE","unified":"25FC-FE0F","non_qualified":"25FC","docomo":null,"au":"E539","softbank":null,"google":"FEB72","image":"25fc-fe0f.png","sheet_x":58,"sheet_y":5,"short_name":"black_medium_square","short_names":["black_medium_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1619,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE MEDIUM SMALL SQUARE","unified":"25FD","non_qualified":null,"docomo":null,"au":"E534","softbank":null,"google":"FEB6F","image":"25fd.png","sheet_x":58,"sheet_y":6,"short_name":"white_medium_small_square","short_names":["white_medium_small_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1622,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK MEDIUM SMALL SQUARE","unified":"25FE","non_qualified":null,"docomo":null,"au":"E535","softbank":null,"google":"FEB70","image":"25fe.png","sheet_x":58,"sheet_y":7,"short_name":"black_medium_small_square","short_names":["black_medium_small_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1621,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK SUN WITH RAYS","unified":"2600-FE0F","non_qualified":"2600","docomo":"E63E","au":"E488","softbank":"E04A","google":"FE000","image":"2600-fe0f.png","sheet_x":58,"sheet_y":8,"short_name":"sunny","short_names":["sunny"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1031,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOUD","unified":"2601-FE0F","non_qualified":"2601","docomo":"E63F","au":"E48D","softbank":"E049","google":"FE001","image":"2601-fe0f.png","sheet_x":58,"sheet_y":9,"short_name":"cloud","short_names":["cloud"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1039,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"UMBRELLA","unified":"2602-FE0F","non_qualified":"2602","docomo":null,"au":null,"softbank":null,"google":null,"image":"2602-fe0f.png","sheet_x":58,"sheet_y":10,"short_name":"umbrella","short_names":["umbrella"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1054,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SNOWMAN","unified":"2603-FE0F","non_qualified":"2603","docomo":null,"au":null,"softbank":null,"google":null,"image":"2603-fe0f.png","sheet_x":58,"sheet_y":11,"short_name":"snowman","short_names":["snowman"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1059,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COMET","unified":"2604-FE0F","non_qualified":"2604","docomo":null,"au":null,"softbank":null,"google":null,"image":"2604-fe0f.png","sheet_x":58,"sheet_y":12,"short_name":"comet","short_names":["comet"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1061,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK TELEPHONE","unified":"260E-FE0F","non_qualified":"260E","docomo":"E687","au":"E596","softbank":"E009","google":"FE523","image":"260e-fe0f.png","sheet_x":58,"sheet_y":13,"short_name":"phone","short_names":["phone","telephone"],"text":null,"texts":null,"category":"Objects","subcategory":"phone","sort_order":1228,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BALLOT BOX WITH CHECK","unified":"2611-FE0F","non_qualified":"2611","docomo":null,"au":"EB02","softbank":null,"google":"FEB8B","image":"2611-fe0f.png","sheet_x":58,"sheet_y":14,"short_name":"ballot_box_with_check","short_names":["ballot_box_with_check"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1536,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"UMBRELLA WITH RAIN DROPS","unified":"2614","non_qualified":null,"docomo":"E640","au":"E48C","softbank":"E04B","google":"FE002","image":"2614.png","sheet_x":58,"sheet_y":15,"short_name":"umbrella_with_rain_drops","short_names":["umbrella_with_rain_drops"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1055,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOT BEVERAGE","unified":"2615","non_qualified":null,"docomo":"E670","au":"E597","softbank":"E045","google":"FE981","image":"2615.png","sheet_x":58,"sheet_y":16,"short_name":"coffee","short_names":["coffee"],"text":null,"texts":null,"category":"Food & Drink","subcategory":"drink","sort_order":822,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHAMROCK","unified":"2618-FE0F","non_qualified":"2618","docomo":null,"au":null,"softbank":null,"google":null,"image":"2618-fe0f.png","sheet_x":58,"sheet_y":17,"short_name":"shamrock","short_names":["shamrock"],"text":null,"texts":null,"category":"Animals & Nature","subcategory":"plant-other","sort_order":704,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE UP POINTING INDEX","unified":"261D-FE0F","non_qualified":"261D","docomo":null,"au":"E4F6","softbank":"E00F","google":"FEB98","image":"261d-fe0f.png","sheet_x":58,"sheet_y":18,"short_name":"point_up","short_names":["point_up"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-single-finger","sort_order":194,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"261D-1F3FB","non_qualified":null,"image":"261d-1f3fb.png","sheet_x":58,"sheet_y":19,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"261D-1F3FC","non_qualified":null,"image":"261d-1f3fc.png","sheet_x":58,"sheet_y":20,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"261D-1F3FD","non_qualified":null,"image":"261d-1f3fd.png","sheet_x":58,"sheet_y":21,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"261D-1F3FE","non_qualified":null,"image":"261d-1f3fe.png","sheet_x":58,"sheet_y":22,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"261D-1F3FF","non_qualified":null,"image":"261d-1f3ff.png","sheet_x":58,"sheet_y":23,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"SKULL AND CROSSBONES","unified":"2620-FE0F","non_qualified":"2620","docomo":null,"au":null,"softbank":null,"google":null,"image":"2620-fe0f.png","sheet_x":58,"sheet_y":24,"short_name":"skull_and_crossbones","short_names":["skull_and_crossbones"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-negative","sort_order":109,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RADIOACTIVE","unified":"2622-FE0F","non_qualified":"2622","docomo":null,"au":null,"softbank":null,"google":null,"image":"2622-fe0f.png","sheet_x":58,"sheet_y":25,"short_name":"radioactive_sign","short_names":["radioactive_sign"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1436,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BIOHAZARD","unified":"2623-FE0F","non_qualified":"2623","docomo":null,"au":null,"softbank":null,"google":null,"image":"2623-fe0f.png","sheet_x":58,"sheet_y":26,"short_name":"biohazard_sign","short_names":["biohazard_sign"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1437,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ORTHODOX CROSS","unified":"2626-FE0F","non_qualified":"2626","docomo":null,"au":null,"softbank":null,"google":null,"image":"2626-fe0f.png","sheet_x":58,"sheet_y":27,"short_name":"orthodox_cross","short_names":["orthodox_cross"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1466,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STAR AND CRESCENT","unified":"262A-FE0F","non_qualified":"262A","docomo":null,"au":null,"softbank":null,"google":null,"image":"262a-fe0f.png","sheet_x":58,"sheet_y":28,"short_name":"star_and_crescent","short_names":["star_and_crescent"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1467,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PEACE SYMBOL","unified":"262E-FE0F","non_qualified":"262E","docomo":null,"au":null,"softbank":null,"google":null,"image":"262e-fe0f.png","sheet_x":58,"sheet_y":29,"short_name":"peace_symbol","short_names":["peace_symbol"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1468,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"YIN YANG","unified":"262F-FE0F","non_qualified":"262F","docomo":null,"au":null,"softbank":null,"google":null,"image":"262f-fe0f.png","sheet_x":58,"sheet_y":30,"short_name":"yin_yang","short_names":["yin_yang"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1464,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHEEL OF DHARMA","unified":"2638-FE0F","non_qualified":"2638","docomo":null,"au":null,"softbank":null,"google":null,"image":"2638-fe0f.png","sheet_x":58,"sheet_y":31,"short_name":"wheel_of_dharma","short_names":["wheel_of_dharma"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1463,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FROWNING FACE","unified":"2639-FE0F","non_qualified":"2639","docomo":null,"au":null,"softbank":null,"google":null,"image":"2639-fe0f.png","sheet_x":58,"sheet_y":32,"short_name":"white_frowning_face","short_names":["white_frowning_face"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-concerned","sort_order":80,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE SMILING FACE","unified":"263A-FE0F","non_qualified":"263A","docomo":"E6F0","au":"E4FB","softbank":"E414","google":"FE336","image":"263a-fe0f.png","sheet_x":58,"sheet_y":33,"short_name":"relaxed","short_names":["relaxed"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"face-affection","sort_order":20,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FEMALE SIGN","unified":"2640-FE0F","non_qualified":"2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"2640-fe0f.png","sheet_x":58,"sheet_y":34,"short_name":"female_sign","short_names":["female_sign"],"text":null,"texts":null,"category":"Symbols","subcategory":"gender","sort_order":1510,"added_in":"4.0","has_img_apple":false,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MALE SIGN","unified":"2642-FE0F","non_qualified":"2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"2642-fe0f.png","sheet_x":58,"sheet_y":35,"short_name":"male_sign","short_names":["male_sign"],"text":null,"texts":null,"category":"Symbols","subcategory":"gender","sort_order":1511,"added_in":"4.0","has_img_apple":false,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ARIES","unified":"2648","non_qualified":null,"docomo":"E646","au":"E48F","softbank":"E23F","google":"FE02B","image":"2648.png","sheet_x":58,"sheet_y":36,"short_name":"aries","short_names":["aries"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1472,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TAURUS","unified":"2649","non_qualified":null,"docomo":"E647","au":"E490","softbank":"E240","google":"FE02C","image":"2649.png","sheet_x":58,"sheet_y":37,"short_name":"taurus","short_names":["taurus"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1473,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GEMINI","unified":"264A","non_qualified":null,"docomo":"E648","au":"E491","softbank":"E241","google":"FE02D","image":"264a.png","sheet_x":58,"sheet_y":38,"short_name":"gemini","short_names":["gemini"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1474,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CANCER","unified":"264B","non_qualified":null,"docomo":"E649","au":"E492","softbank":"E242","google":"FE02E","image":"264b.png","sheet_x":58,"sheet_y":39,"short_name":"cancer","short_names":["cancer"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1475,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEO","unified":"264C","non_qualified":null,"docomo":"E64A","au":"E493","softbank":"E243","google":"FE02F","image":"264c.png","sheet_x":58,"sheet_y":40,"short_name":"leo","short_names":["leo"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1476,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"VIRGO","unified":"264D","non_qualified":null,"docomo":"E64B","au":"E494","softbank":"E244","google":"FE030","image":"264d.png","sheet_x":58,"sheet_y":41,"short_name":"virgo","short_names":["virgo"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1477,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LIBRA","unified":"264E","non_qualified":null,"docomo":"E64C","au":"E495","softbank":"E245","google":"FE031","image":"264e.png","sheet_x":58,"sheet_y":42,"short_name":"libra","short_names":["libra"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1478,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SCORPIUS","unified":"264F","non_qualified":null,"docomo":"E64D","au":"E496","softbank":"E246","google":"FE032","image":"264f.png","sheet_x":58,"sheet_y":43,"short_name":"scorpius","short_names":["scorpius"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1479,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SAGITTARIUS","unified":"2650","non_qualified":null,"docomo":"E64E","au":"E497","softbank":"E247","google":"FE033","image":"2650.png","sheet_x":58,"sheet_y":44,"short_name":"sagittarius","short_names":["sagittarius"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1480,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CAPRICORN","unified":"2651","non_qualified":null,"docomo":"E64F","au":"E498","softbank":"E248","google":"FE034","image":"2651.png","sheet_x":58,"sheet_y":45,"short_name":"capricorn","short_names":["capricorn"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1481,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AQUARIUS","unified":"2652","non_qualified":null,"docomo":"E650","au":"E499","softbank":"E249","google":"FE035","image":"2652.png","sheet_x":58,"sheet_y":46,"short_name":"aquarius","short_names":["aquarius"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1482,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PISCES","unified":"2653","non_qualified":null,"docomo":"E651","au":"E49A","softbank":"E24A","google":"FE036","image":"2653.png","sheet_x":58,"sheet_y":47,"short_name":"pisces","short_names":["pisces"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1483,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHESS PAWN","unified":"265F-FE0F","non_qualified":"265F","docomo":null,"au":null,"softbank":null,"google":null,"image":"265f-fe0f.png","sheet_x":58,"sheet_y":48,"short_name":"chess_pawn","short_names":["chess_pawn"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1139,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK SPADE SUIT","unified":"2660-FE0F","non_qualified":"2660","docomo":"E68E","au":"E5A1","softbank":"E20E","google":"FEB1B","image":"2660-fe0f.png","sheet_x":58,"sheet_y":49,"short_name":"spades","short_names":["spades"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1135,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK CLUB SUIT","unified":"2663-FE0F","non_qualified":"2663","docomo":"E690","au":"E5A3","softbank":"E20F","google":"FEB1D","image":"2663-fe0f.png","sheet_x":58,"sheet_y":50,"short_name":"clubs","short_names":["clubs"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1138,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK HEART SUIT","unified":"2665-FE0F","non_qualified":"2665","docomo":"E68D","au":"EAA5","softbank":"E20C","google":"FEB1A","image":"2665-fe0f.png","sheet_x":58,"sheet_y":51,"short_name":"hearts","short_names":["hearts"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1136,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK DIAMOND SUIT","unified":"2666-FE0F","non_qualified":"2666","docomo":"E68F","au":"E5A2","softbank":"E20D","google":"FEB1C","image":"2666-fe0f.png","sheet_x":58,"sheet_y":52,"short_name":"diamonds","short_names":["diamonds"],"text":null,"texts":null,"category":"Activities","subcategory":"game","sort_order":1137,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HOT SPRINGS","unified":"2668-FE0F","non_qualified":"2668","docomo":"E6F7","au":"E4BC","softbank":"E123","google":"FE7FA","image":"2668-fe0f.png","sheet_x":58,"sheet_y":53,"short_name":"hotsprings","short_names":["hotsprings"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":906,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK UNIVERSAL RECYCLING SYMBOL","unified":"267B-FE0F","non_qualified":"267B","docomo":"E735","au":"EB79","softbank":null,"google":"FEB2C","image":"267b-fe0f.png","sheet_x":58,"sheet_y":54,"short_name":"recycle","short_names":["recycle"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1529,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"INFINITY","unified":"267E-FE0F","non_qualified":"267E","docomo":null,"au":null,"softbank":null,"google":null,"image":"267e-fe0f.png","sheet_x":58,"sheet_y":55,"short_name":"infinity","short_names":["infinity"],"text":null,"texts":null,"category":"Symbols","subcategory":"math","sort_order":1518,"added_in":"11.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHEELCHAIR SYMBOL","unified":"267F","non_qualified":null,"docomo":"E69B","au":"E47F","softbank":"E20A","google":"FEB20","image":"267f.png","sheet_x":58,"sheet_y":56,"short_name":"wheelchair","short_names":["wheelchair"],"text":null,"texts":null,"category":"Symbols","subcategory":"transport-sign","sort_order":1415,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HAMMER AND PICK","unified":"2692-FE0F","non_qualified":"2692","docomo":null,"au":null,"softbank":null,"google":null,"image":"2692-fe0f.png","sheet_x":58,"sheet_y":57,"short_name":"hammer_and_pick","short_names":["hammer_and_pick"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1341,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ANCHOR","unified":"2693","non_qualified":null,"docomo":"E661","au":"E4A9","softbank":null,"google":"FE4C1","image":"2693.png","sheet_x":58,"sheet_y":58,"short_name":"anchor","short_names":["anchor"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-water","sort_order":963,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CROSSED SWORDS","unified":"2694-FE0F","non_qualified":"2694","docomo":null,"au":null,"softbank":null,"google":null,"image":"2694-fe0f.png","sheet_x":58,"sheet_y":59,"short_name":"crossed_swords","short_names":["crossed_swords"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1344,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MEDICAL SYMBOL","unified":"2695-FE0F","non_qualified":"2695","docomo":null,"au":null,"softbank":null,"google":null,"image":"2695-fe0f.png","sheet_x":58,"sheet_y":60,"short_name":"medical_symbol","short_names":["medical_symbol","staff_of_aesculapius"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1528,"added_in":"4.0","has_img_apple":false,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BALANCE SCALE","unified":"2696-FE0F","non_qualified":"2696","docomo":null,"au":null,"softbank":null,"google":null,"image":"2696-fe0f.png","sheet_x":58,"sheet_y":61,"short_name":"scales","short_names":["scales"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1355,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ALEMBIC","unified":"2697-FE0F","non_qualified":"2697","docomo":null,"au":null,"softbank":null,"google":null,"image":"2697-fe0f.png","sheet_x":59,"sheet_y":0,"short_name":"alembic","short_names":["alembic"],"text":null,"texts":null,"category":"Objects","subcategory":"science","sort_order":1364,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"GEAR","unified":"2699-FE0F","non_qualified":"2699","docomo":null,"au":null,"softbank":null,"google":null,"image":"2699-fe0f.png","sheet_x":59,"sheet_y":1,"short_name":"gear","short_names":["gear"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1353,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ATOM SYMBOL","unified":"269B-FE0F","non_qualified":"269B","docomo":null,"au":null,"softbank":null,"google":null,"image":"269b-fe0f.png","sheet_x":59,"sheet_y":2,"short_name":"atom_symbol","short_names":["atom_symbol"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1460,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLEUR-DE-LIS","unified":"269C-FE0F","non_qualified":"269C","docomo":null,"au":null,"softbank":null,"google":null,"image":"269c-fe0f.png","sheet_x":59,"sheet_y":3,"short_name":"fleur_de_lis","short_names":["fleur_de_lis"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1530,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WARNING SIGN","unified":"26A0-FE0F","non_qualified":"26A0","docomo":"E737","au":"E481","softbank":"E252","google":"FEB23","image":"26a0-fe0f.png","sheet_x":59,"sheet_y":4,"short_name":"warning","short_names":["warning"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1425,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HIGH VOLTAGE SIGN","unified":"26A1","non_qualified":null,"docomo":"E642","au":"E487","softbank":"E13D","google":"FE004","image":"26a1.png","sheet_x":59,"sheet_y":5,"short_name":"zap","short_names":["zap"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1057,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"TRANSGENDER SYMBOL","unified":"26A7-FE0F","non_qualified":"26A7","docomo":null,"au":null,"softbank":null,"google":null,"image":"26a7-fe0f.png","sheet_x":59,"sheet_y":6,"short_name":"transgender_symbol","short_names":["transgender_symbol"],"text":null,"texts":null,"category":"Symbols","subcategory":"gender","sort_order":1512,"added_in":"13.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MEDIUM WHITE CIRCLE","unified":"26AA","non_qualified":null,"docomo":"E69C","au":"E53A","softbank":null,"google":"FEB65","image":"26aa.png","sheet_x":59,"sheet_y":7,"short_name":"white_circle","short_names":["white_circle"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1609,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MEDIUM BLACK CIRCLE","unified":"26AB","non_qualified":null,"docomo":"E69C","au":"E53B","softbank":null,"google":"FEB66","image":"26ab.png","sheet_x":59,"sheet_y":8,"short_name":"black_circle","short_names":["black_circle"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1608,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"COFFIN","unified":"26B0-FE0F","non_qualified":"26B0","docomo":null,"au":null,"softbank":null,"google":null,"image":"26b0-fe0f.png","sheet_x":59,"sheet_y":9,"short_name":"coffin","short_names":["coffin"],"text":null,"texts":null,"category":"Objects","subcategory":"other-object","sort_order":1404,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FUNERAL URN","unified":"26B1-FE0F","non_qualified":"26B1","docomo":null,"au":null,"softbank":null,"google":null,"image":"26b1-fe0f.png","sheet_x":59,"sheet_y":10,"short_name":"funeral_urn","short_names":["funeral_urn"],"text":null,"texts":null,"category":"Objects","subcategory":"other-object","sort_order":1406,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SOCCER BALL","unified":"26BD","non_qualified":null,"docomo":"E656","au":"E4B6","softbank":"E018","google":"FE7D4","image":"26bd.png","sheet_x":59,"sheet_y":11,"short_name":"soccer","short_names":["soccer"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1092,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BASEBALL","unified":"26BE","non_qualified":null,"docomo":"E653","au":"E4BA","softbank":"E016","google":"FE7D1","image":"26be.png","sheet_x":59,"sheet_y":12,"short_name":"baseball","short_names":["baseball"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1093,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SNOWMAN WITHOUT SNOW","unified":"26C4","non_qualified":null,"docomo":"E641","au":"E485","softbank":"E048","google":"FE003","image":"26c4.png","sheet_x":59,"sheet_y":13,"short_name":"snowman_without_snow","short_names":["snowman_without_snow"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1060,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SUN BEHIND CLOUD","unified":"26C5","non_qualified":null,"docomo":"E63E-E63F","au":"E48E","softbank":null,"google":"FE00F","image":"26c5.png","sheet_x":59,"sheet_y":14,"short_name":"partly_sunny","short_names":["partly_sunny"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1040,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CLOUD WITH LIGHTNING AND RAIN","unified":"26C8-FE0F","non_qualified":"26C8","docomo":null,"au":null,"softbank":null,"google":null,"image":"26c8-fe0f.png","sheet_x":59,"sheet_y":15,"short_name":"thunder_cloud_and_rain","short_names":["thunder_cloud_and_rain"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1041,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"OPHIUCHUS","unified":"26CE","non_qualified":null,"docomo":null,"au":"E49B","softbank":"E24B","google":"FE037","image":"26ce.png","sheet_x":59,"sheet_y":16,"short_name":"ophiuchus","short_names":["ophiuchus"],"text":null,"texts":null,"category":"Symbols","subcategory":"zodiac","sort_order":1484,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PICK","unified":"26CF-FE0F","non_qualified":"26CF","docomo":null,"au":null,"softbank":null,"google":null,"image":"26cf-fe0f.png","sheet_x":59,"sheet_y":17,"short_name":"pick","short_names":["pick"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1340,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RESCUE WORKER\u2019S HELMET","unified":"26D1-FE0F","non_qualified":"26D1","docomo":null,"au":null,"softbank":null,"google":null,"image":"26d1-fe0f.png","sheet_x":59,"sheet_y":18,"short_name":"helmet_with_white_cross","short_names":["helmet_with_white_cross"],"text":null,"texts":null,"category":"Objects","subcategory":"clothing","sort_order":1192,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BROKEN CHAIN","unified":"26D3-FE0F-200D-1F4A5","non_qualified":"26D3-200D-1F4A5","docomo":null,"au":null,"softbank":null,"google":null,"image":"26d3-fe0f-200d-1f4a5.png","sheet_x":59,"sheet_y":19,"short_name":"broken_chain","short_names":["broken_chain"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1358,"added_in":"15.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_facebook":false},{"name":"CHAINS","unified":"26D3-FE0F","non_qualified":"26D3","docomo":null,"au":null,"softbank":null,"google":null,"image":"26d3-fe0f.png","sheet_x":59,"sheet_y":20,"short_name":"chains","short_names":["chains"],"text":null,"texts":null,"category":"Objects","subcategory":"tool","sort_order":1359,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NO ENTRY","unified":"26D4","non_qualified":null,"docomo":"E72F","au":"E484","softbank":null,"google":"FEB26","image":"26d4.png","sheet_x":59,"sheet_y":21,"short_name":"no_entry","short_names":["no_entry"],"text":null,"texts":null,"category":"Symbols","subcategory":"warning","sort_order":1427,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SHINTO SHRINE","unified":"26E9-FE0F","non_qualified":"26E9","docomo":null,"au":null,"softbank":null,"google":null,"image":"26e9-fe0f.png","sheet_x":59,"sheet_y":22,"short_name":"shinto_shrine","short_names":["shinto_shrine"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-religious","sort_order":894,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CHURCH","unified":"26EA","non_qualified":null,"docomo":null,"au":"E5BB","softbank":"E037","google":"FE4BB","image":"26ea.png","sheet_x":59,"sheet_y":23,"short_name":"church","short_names":["church"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-religious","sort_order":890,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MOUNTAIN","unified":"26F0-FE0F","non_qualified":"26F0","docomo":null,"au":null,"softbank":null,"google":null,"image":"26f0-fe0f.png","sheet_x":59,"sheet_y":24,"short_name":"mountain","short_names":["mountain"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-geographic","sort_order":855,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"UMBRELLA ON GROUND","unified":"26F1-FE0F","non_qualified":"26F1","docomo":null,"au":null,"softbank":null,"google":null,"image":"26f1-fe0f.png","sheet_x":59,"sheet_y":25,"short_name":"umbrella_on_ground","short_names":["umbrella_on_ground"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1056,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FOUNTAIN","unified":"26F2","non_qualified":null,"docomo":null,"au":"E5CF","softbank":"E121","google":"FE4BC","image":"26f2.png","sheet_x":59,"sheet_y":26,"short_name":"fountain","short_names":["fountain"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":896,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FLAG IN HOLE","unified":"26F3","non_qualified":null,"docomo":"E654","au":"E599","softbank":"E014","google":"FE7D2","image":"26f3.png","sheet_x":59,"sheet_y":27,"short_name":"golf","short_names":["golf"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1111,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FERRY","unified":"26F4-FE0F","non_qualified":"26F4","docomo":null,"au":null,"softbank":null,"google":null,"image":"26f4-fe0f.png","sheet_x":59,"sheet_y":28,"short_name":"ferry","short_names":["ferry"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-water","sort_order":969,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SAILBOAT","unified":"26F5","non_qualified":null,"docomo":"E6A3","au":"E4B4","softbank":"E01C","google":"FE7EA","image":"26f5.png","sheet_x":59,"sheet_y":29,"short_name":"boat","short_names":["boat","sailboat"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-water","sort_order":965,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SKIER","unified":"26F7-FE0F","non_qualified":"26F7","docomo":null,"au":null,"softbank":null,"google":null,"image":"26f7-fe0f.png","sheet_x":59,"sheet_y":30,"short_name":"skier","short_names":["skier"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":461,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ICE SKATE","unified":"26F8-FE0F","non_qualified":"26F8","docomo":null,"au":null,"softbank":null,"google":null,"image":"26f8-fe0f.png","sheet_x":59,"sheet_y":31,"short_name":"ice_skate","short_names":["ice_skate"],"text":null,"texts":null,"category":"Activities","subcategory":"sport","sort_order":1112,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WOMAN BOUNCING BALL","unified":"26F9-FE0F-200D-2640-FE0F","non_qualified":"26F9-200D-2640","docomo":null,"au":null,"softbank":null,"google":null,"image":"26f9-fe0f-200d-2640-fe0f.png","sheet_x":59,"sheet_y":32,"short_name":"woman-bouncing-ball","short_names":["woman-bouncing-ball"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":477,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"26F9-1F3FB-200D-2640-FE0F","non_qualified":"26F9-1F3FB-200D-2640","image":"26f9-1f3fb-200d-2640-fe0f.png","sheet_x":59,"sheet_y":33,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"26F9-1F3FC-200D-2640-FE0F","non_qualified":"26F9-1F3FC-200D-2640","image":"26f9-1f3fc-200d-2640-fe0f.png","sheet_x":59,"sheet_y":34,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"26F9-1F3FD-200D-2640-FE0F","non_qualified":"26F9-1F3FD-200D-2640","image":"26f9-1f3fd-200d-2640-fe0f.png","sheet_x":59,"sheet_y":35,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"26F9-1F3FE-200D-2640-FE0F","non_qualified":"26F9-1F3FE-200D-2640","image":"26f9-1f3fe-200d-2640-fe0f.png","sheet_x":59,"sheet_y":36,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"26F9-1F3FF-200D-2640-FE0F","non_qualified":"26F9-1F3FF-200D-2640","image":"26f9-1f3ff-200d-2640-fe0f.png","sheet_x":59,"sheet_y":37,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"MAN BOUNCING BALL","unified":"26F9-FE0F-200D-2642-FE0F","non_qualified":"26F9-200D-2642","docomo":null,"au":null,"softbank":null,"google":null,"image":"26f9-fe0f-200d-2642-fe0f.png","sheet_x":59,"sheet_y":38,"short_name":"man-bouncing-ball","short_names":["man-bouncing-ball"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":476,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":false,"skin_variations":{"1F3FB":{"unified":"26F9-1F3FB-200D-2642-FE0F","non_qualified":"26F9-1F3FB-200D-2642","image":"26f9-1f3fb-200d-2642-fe0f.png","sheet_x":59,"sheet_y":39,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"26F9-1F3FC-200D-2642-FE0F","non_qualified":"26F9-1F3FC-200D-2642","image":"26f9-1f3fc-200d-2642-fe0f.png","sheet_x":59,"sheet_y":40,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"26F9-1F3FD-200D-2642-FE0F","non_qualified":"26F9-1F3FD-200D-2642","image":"26f9-1f3fd-200d-2642-fe0f.png","sheet_x":59,"sheet_y":41,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"26F9-1F3FE-200D-2642-FE0F","non_qualified":"26F9-1F3FE-200D-2642","image":"26f9-1f3fe-200d-2642-fe0f.png","sheet_x":59,"sheet_y":42,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"26F9-1F3FF-200D-2642-FE0F","non_qualified":"26F9-1F3FF-200D-2642","image":"26f9-1f3ff-200d-2642-fe0f.png","sheet_x":59,"sheet_y":43,"added_in":"4.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoletes":"26F9-FE0F"},{"name":"PERSON BOUNCING BALL","unified":"26F9-FE0F","non_qualified":"26F9","docomo":null,"au":null,"softbank":null,"google":null,"image":"26f9-fe0f.png","sheet_x":59,"sheet_y":44,"short_name":"person_with_ball","short_names":["person_with_ball"],"text":null,"texts":null,"category":"People & Body","subcategory":"person-sport","sort_order":475,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"26F9-1F3FB","non_qualified":null,"image":"26f9-1f3fb.png","sheet_x":59,"sheet_y":45,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"26F9-1F3FC","non_qualified":null,"image":"26f9-1f3fc.png","sheet_x":59,"sheet_y":46,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"26F9-1F3FD","non_qualified":null,"image":"26f9-1f3fd.png","sheet_x":59,"sheet_y":47,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"26F9-1F3FE","non_qualified":null,"image":"26f9-1f3fe.png","sheet_x":59,"sheet_y":48,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"26F9-1F3FF","non_qualified":null,"image":"26f9-1f3ff.png","sheet_x":59,"sheet_y":49,"added_in":"2.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}},"obsoleted_by":"26F9-FE0F-200D-2642-FE0F"},{"name":"TENT","unified":"26FA","non_qualified":null,"docomo":null,"au":"E5D0","softbank":"E122","google":"FE7FB","image":"26fa.png","sheet_x":59,"sheet_y":50,"short_name":"tent","short_names":["tent"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"place-other","sort_order":897,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"FUEL PUMP","unified":"26FD","non_qualified":null,"docomo":"E66B","au":"E571","softbank":"E03A","google":"FE7F5","image":"26fd.png","sheet_x":59,"sheet_y":51,"short_name":"fuelpump","short_names":["fuelpump"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-ground","sort_order":956,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK SCISSORS","unified":"2702-FE0F","non_qualified":"2702","docomo":"E675","au":"E516","softbank":"E313","google":"FE53E","image":"2702-fe0f.png","sheet_x":59,"sheet_y":52,"short_name":"scissors","short_names":["scissors"],"text":null,"texts":null,"category":"Objects","subcategory":"office","sort_order":1328,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE HEAVY CHECK MARK","unified":"2705","non_qualified":null,"docomo":null,"au":"E55E","softbank":null,"google":"FEB4A","image":"2705.png","sheet_x":59,"sheet_y":53,"short_name":"white_check_mark","short_names":["white_check_mark"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1535,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"AIRPLANE","unified":"2708-FE0F","non_qualified":"2708","docomo":"E662","au":"E4B3","softbank":"E01D","google":"FE7E9","image":"2708-fe0f.png","sheet_x":59,"sheet_y":54,"short_name":"airplane","short_names":["airplane"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"transport-air","sort_order":972,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ENVELOPE","unified":"2709-FE0F","non_qualified":"2709","docomo":"E6D3","au":"E521","softbank":null,"google":"FE529","image":"2709-fe0f.png","sheet_x":59,"sheet_y":55,"short_name":"email","short_names":["email","envelope"],"text":null,"texts":null,"category":"Objects","subcategory":"mail","sort_order":1289,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"RAISED FIST","unified":"270A","non_qualified":null,"docomo":"E693","au":"EB83","softbank":"E010","google":"FEB93","image":"270a.png","sheet_x":59,"sheet_y":56,"short_name":"fist","short_names":["fist"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-closed","sort_order":198,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"270A-1F3FB","non_qualified":null,"image":"270a-1f3fb.png","sheet_x":59,"sheet_y":57,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"270A-1F3FC","non_qualified":null,"image":"270a-1f3fc.png","sheet_x":59,"sheet_y":58,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"270A-1F3FD","non_qualified":null,"image":"270a-1f3fd.png","sheet_x":59,"sheet_y":59,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"270A-1F3FE","non_qualified":null,"image":"270a-1f3fe.png","sheet_x":59,"sheet_y":60,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"270A-1F3FF","non_qualified":null,"image":"270a-1f3ff.png","sheet_x":59,"sheet_y":61,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"RAISED HAND","unified":"270B","non_qualified":null,"docomo":"E695","au":"E5A7","softbank":"E012","google":"FEB95","image":"270b.png","sheet_x":60,"sheet_y":0,"short_name":"hand","short_names":["hand","raised_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-open","sort_order":172,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"270B-1F3FB","non_qualified":null,"image":"270b-1f3fb.png","sheet_x":60,"sheet_y":1,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"270B-1F3FC","non_qualified":null,"image":"270b-1f3fc.png","sheet_x":60,"sheet_y":2,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"270B-1F3FD","non_qualified":null,"image":"270b-1f3fd.png","sheet_x":60,"sheet_y":3,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"270B-1F3FE","non_qualified":null,"image":"270b-1f3fe.png","sheet_x":60,"sheet_y":4,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"270B-1F3FF","non_qualified":null,"image":"270b-1f3ff.png","sheet_x":60,"sheet_y":5,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"VICTORY HAND","unified":"270C-FE0F","non_qualified":"270C","docomo":"E694","au":"E5A6","softbank":"E011","google":"FEB94","image":"270c-fe0f.png","sheet_x":60,"sheet_y":6,"short_name":"v","short_names":["v"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-fingers-partial","sort_order":183,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"270C-1F3FB","non_qualified":null,"image":"270c-1f3fb.png","sheet_x":60,"sheet_y":7,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"270C-1F3FC","non_qualified":null,"image":"270c-1f3fc.png","sheet_x":60,"sheet_y":8,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"270C-1F3FD","non_qualified":null,"image":"270c-1f3fd.png","sheet_x":60,"sheet_y":9,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"270C-1F3FE","non_qualified":null,"image":"270c-1f3fe.png","sheet_x":60,"sheet_y":10,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"270C-1F3FF","non_qualified":null,"image":"270c-1f3ff.png","sheet_x":60,"sheet_y":11,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"WRITING HAND","unified":"270D-FE0F","non_qualified":"270D","docomo":null,"au":null,"softbank":null,"google":null,"image":"270d-fe0f.png","sheet_x":60,"sheet_y":12,"short_name":"writing_hand","short_names":["writing_hand"],"text":null,"texts":null,"category":"People & Body","subcategory":"hand-prop","sort_order":209,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true,"skin_variations":{"1F3FB":{"unified":"270D-1F3FB","non_qualified":null,"image":"270d-1f3fb.png","sheet_x":60,"sheet_y":13,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FC":{"unified":"270D-1F3FC","non_qualified":null,"image":"270d-1f3fc.png","sheet_x":60,"sheet_y":14,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FD":{"unified":"270D-1F3FD","non_qualified":null,"image":"270d-1f3fd.png","sheet_x":60,"sheet_y":15,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FE":{"unified":"270D-1F3FE","non_qualified":null,"image":"270d-1f3fe.png","sheet_x":60,"sheet_y":16,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},"1F3FF":{"unified":"270D-1F3FF","non_qualified":null,"image":"270d-1f3ff.png","sheet_x":60,"sheet_y":17,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}}},{"name":"PENCIL","unified":"270F-FE0F","non_qualified":"270F","docomo":"E719","au":"E4A1","softbank":null,"google":"FE539","image":"270f-fe0f.png","sheet_x":60,"sheet_y":18,"short_name":"pencil2","short_names":["pencil2"],"text":null,"texts":null,"category":"Objects","subcategory":"writing","sort_order":1302,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK NIB","unified":"2712-FE0F","non_qualified":"2712","docomo":"E6AE","au":"EB03","softbank":null,"google":"FE536","image":"2712-fe0f.png","sheet_x":60,"sheet_y":19,"short_name":"black_nib","short_names":["black_nib"],"text":null,"texts":null,"category":"Objects","subcategory":"writing","sort_order":1303,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAVY CHECK MARK","unified":"2714-FE0F","non_qualified":"2714","docomo":null,"au":"E557","softbank":null,"google":"FEB49","image":"2714-fe0f.png","sheet_x":60,"sheet_y":20,"short_name":"heavy_check_mark","short_names":["heavy_check_mark"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1537,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAVY MULTIPLICATION X","unified":"2716-FE0F","non_qualified":"2716","docomo":null,"au":"E54F","softbank":null,"google":"FEB53","image":"2716-fe0f.png","sheet_x":60,"sheet_y":21,"short_name":"heavy_multiplication_x","short_names":["heavy_multiplication_x"],"text":null,"texts":null,"category":"Symbols","subcategory":"math","sort_order":1513,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LATIN CROSS","unified":"271D-FE0F","non_qualified":"271D","docomo":null,"au":null,"softbank":null,"google":null,"image":"271d-fe0f.png","sheet_x":60,"sheet_y":22,"short_name":"latin_cross","short_names":["latin_cross"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1465,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"STAR OF DAVID","unified":"2721-FE0F","non_qualified":"2721","docomo":null,"au":null,"softbank":null,"google":null,"image":"2721-fe0f.png","sheet_x":60,"sheet_y":23,"short_name":"star_of_david","short_names":["star_of_david"],"text":null,"texts":null,"category":"Symbols","subcategory":"religion","sort_order":1462,"added_in":"0.7","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPARKLES","unified":"2728","non_qualified":null,"docomo":"E6FA","au":"EAAB","softbank":"E32E","google":"FEB60","image":"2728.png","sheet_x":60,"sheet_y":24,"short_name":"sparkles","short_names":["sparkles"],"text":null,"texts":null,"category":"Activities","subcategory":"event","sort_order":1070,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EIGHT SPOKED ASTERISK","unified":"2733-FE0F","non_qualified":"2733","docomo":"E6F8","au":"E53E","softbank":"E206","google":"FEB62","image":"2733-fe0f.png","sheet_x":60,"sheet_y":25,"short_name":"eight_spoked_asterisk","short_names":["eight_spoked_asterisk"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1543,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"EIGHT POINTED BLACK STAR","unified":"2734-FE0F","non_qualified":"2734","docomo":"E6F8","au":"E479","softbank":"E205","google":"FEB61","image":"2734-fe0f.png","sheet_x":60,"sheet_y":26,"short_name":"eight_pointed_black_star","short_names":["eight_pointed_black_star"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1544,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SNOWFLAKE","unified":"2744-FE0F","non_qualified":"2744","docomo":null,"au":"E48A","softbank":null,"google":"FE00E","image":"2744-fe0f.png","sheet_x":60,"sheet_y":27,"short_name":"snowflake","short_names":["snowflake"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1058,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"SPARKLE","unified":"2747-FE0F","non_qualified":"2747","docomo":"E6FA","au":"E46C","softbank":null,"google":"FEB77","image":"2747-fe0f.png","sheet_x":60,"sheet_y":28,"short_name":"sparkle","short_names":["sparkle"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1545,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CROSS MARK","unified":"274C","non_qualified":null,"docomo":null,"au":"E550","softbank":"E333","google":"FEB45","image":"274c.png","sheet_x":60,"sheet_y":29,"short_name":"x","short_names":["x"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1538,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"NEGATIVE SQUARED CROSS MARK","unified":"274E","non_qualified":null,"docomo":null,"au":"E551","softbank":null,"google":"FEB46","image":"274e.png","sheet_x":60,"sheet_y":30,"short_name":"negative_squared_cross_mark","short_names":["negative_squared_cross_mark"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1539,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK QUESTION MARK ORNAMENT","unified":"2753","non_qualified":null,"docomo":null,"au":"E483","softbank":"E020","google":"FEB09","image":"2753.png","sheet_x":60,"sheet_y":31,"short_name":"question","short_names":["question"],"text":null,"texts":null,"category":"Symbols","subcategory":"punctuation","sort_order":1521,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE QUESTION MARK ORNAMENT","unified":"2754","non_qualified":null,"docomo":null,"au":"E483","softbank":"E336","google":"FEB0A","image":"2754.png","sheet_x":60,"sheet_y":32,"short_name":"grey_question","short_names":["grey_question"],"text":null,"texts":null,"category":"Symbols","subcategory":"punctuation","sort_order":1522,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE EXCLAMATION MARK ORNAMENT","unified":"2755","non_qualified":null,"docomo":"E702","au":"E482","softbank":"E337","google":"FEB0B","image":"2755.png","sheet_x":60,"sheet_y":33,"short_name":"grey_exclamation","short_names":["grey_exclamation"],"text":null,"texts":null,"category":"Symbols","subcategory":"punctuation","sort_order":1523,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAVY EXCLAMATION MARK SYMBOL","unified":"2757","non_qualified":null,"docomo":"E702","au":"E482","softbank":"E021","google":"FEB04","image":"2757.png","sheet_x":60,"sheet_y":34,"short_name":"exclamation","short_names":["exclamation","heavy_exclamation_mark"],"text":null,"texts":null,"category":"Symbols","subcategory":"punctuation","sort_order":1524,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEART EXCLAMATION","unified":"2763-FE0F","non_qualified":"2763","docomo":null,"au":null,"softbank":null,"google":null,"image":"2763-fe0f.png","sheet_x":60,"sheet_y":35,"short_name":"heavy_heart_exclamation_mark_ornament","short_names":["heavy_heart_exclamation_mark_ornament"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":139,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEART ON FIRE","unified":"2764-FE0F-200D-1F525","non_qualified":"2764-200D-1F525","docomo":null,"au":null,"softbank":null,"google":null,"image":"2764-fe0f-200d-1f525.png","sheet_x":60,"sheet_y":36,"short_name":"heart_on_fire","short_names":["heart_on_fire"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":141,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"MENDING HEART","unified":"2764-FE0F-200D-1FA79","non_qualified":"2764-200D-1FA79","docomo":null,"au":null,"softbank":null,"google":null,"image":"2764-fe0f-200d-1fa79.png","sheet_x":60,"sheet_y":37,"short_name":"mending_heart","short_names":["mending_heart"],"text":null,"texts":null,"category":"Smileys & Emotion","subcategory":"heart","sort_order":142,"added_in":"13.1","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAVY BLACK HEART","unified":"2764-FE0F","non_qualified":"2764","docomo":"E6EC","au":"E595","softbank":"E022","google":"FEB0C","image":"2764-fe0f.png","sheet_x":60,"sheet_y":38,"short_name":"heart","short_names":["heart"],"text":"<3","texts":["<3"],"category":"Smileys & Emotion","subcategory":"heart","sort_order":143,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAVY PLUS SIGN","unified":"2795","non_qualified":null,"docomo":null,"au":"E53C","softbank":null,"google":"FEB51","image":"2795.png","sheet_x":60,"sheet_y":39,"short_name":"heavy_plus_sign","short_names":["heavy_plus_sign"],"text":null,"texts":null,"category":"Symbols","subcategory":"math","sort_order":1514,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAVY MINUS SIGN","unified":"2796","non_qualified":null,"docomo":null,"au":"E53D","softbank":null,"google":"FEB52","image":"2796.png","sheet_x":60,"sheet_y":40,"short_name":"heavy_minus_sign","short_names":["heavy_minus_sign"],"text":null,"texts":null,"category":"Symbols","subcategory":"math","sort_order":1515,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAVY DIVISION SIGN","unified":"2797","non_qualified":null,"docomo":null,"au":"E554","softbank":null,"google":"FEB54","image":"2797.png","sheet_x":60,"sheet_y":41,"short_name":"heavy_division_sign","short_names":["heavy_division_sign"],"text":null,"texts":null,"category":"Symbols","subcategory":"math","sort_order":1516,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK RIGHTWARDS ARROW","unified":"27A1-FE0F","non_qualified":"27A1","docomo":null,"au":"E552","softbank":"E234","google":"FEAFA","image":"27a1-fe0f.png","sheet_x":60,"sheet_y":42,"short_name":"arrow_right","short_names":["arrow_right"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1440,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CURLY LOOP","unified":"27B0","non_qualified":null,"docomo":"E70A","au":"EB31","softbank":null,"google":"FEB08","image":"27b0.png","sheet_x":60,"sheet_y":43,"short_name":"curly_loop","short_names":["curly_loop"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1540,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOUBLE CURLY LOOP","unified":"27BF","non_qualified":null,"docomo":"E6DF","au":null,"softbank":"E211","google":"FE82B","image":"27bf.png","sheet_x":60,"sheet_y":44,"short_name":"loop","short_names":["loop"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1541,"added_in":"1.0","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS","unified":"2934-FE0F","non_qualified":"2934","docomo":"E6F5","au":"EB2D","softbank":null,"google":"FEAF4","image":"2934-fe0f.png","sheet_x":60,"sheet_y":45,"short_name":"arrow_heading_up","short_names":["arrow_heading_up"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1450,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS","unified":"2935-FE0F","non_qualified":"2935","docomo":"E700","au":"EB2E","softbank":null,"google":"FEAF5","image":"2935-fe0f.png","sheet_x":60,"sheet_y":46,"short_name":"arrow_heading_down","short_names":["arrow_heading_down"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1451,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"LEFTWARDS BLACK ARROW","unified":"2B05-FE0F","non_qualified":"2B05","docomo":null,"au":"E553","softbank":"E235","google":"FEAFB","image":"2b05-fe0f.png","sheet_x":60,"sheet_y":47,"short_name":"arrow_left","short_names":["arrow_left"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1444,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"UPWARDS BLACK ARROW","unified":"2B06-FE0F","non_qualified":"2B06","docomo":null,"au":"E53F","softbank":"E232","google":"FEAF8","image":"2b06-fe0f.png","sheet_x":60,"sheet_y":48,"short_name":"arrow_up","short_names":["arrow_up"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1438,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"DOWNWARDS BLACK ARROW","unified":"2B07-FE0F","non_qualified":"2B07","docomo":null,"au":"E540","softbank":"E233","google":"FEAF9","image":"2b07-fe0f.png","sheet_x":60,"sheet_y":49,"short_name":"arrow_down","short_names":["arrow_down"],"text":null,"texts":null,"category":"Symbols","subcategory":"arrow","sort_order":1442,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"BLACK LARGE SQUARE","unified":"2B1B","non_qualified":null,"docomo":null,"au":"E549","softbank":null,"google":"FEB6C","image":"2b1b.png","sheet_x":60,"sheet_y":50,"short_name":"black_large_square","short_names":["black_large_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1617,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE LARGE SQUARE","unified":"2B1C","non_qualified":null,"docomo":null,"au":"E548","softbank":null,"google":"FEB6B","image":"2b1c.png","sheet_x":60,"sheet_y":51,"short_name":"white_large_square","short_names":["white_large_square"],"text":null,"texts":null,"category":"Symbols","subcategory":"geometric","sort_order":1618,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WHITE MEDIUM STAR","unified":"2B50","non_qualified":null,"docomo":null,"au":"E48B","softbank":"E32F","google":"FEB68","image":"2b50.png","sheet_x":60,"sheet_y":52,"short_name":"star","short_names":["star"],"text":null,"texts":null,"category":"Travel & Places","subcategory":"sky & weather","sort_order":1035,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"HEAVY LARGE CIRCLE","unified":"2B55","non_qualified":null,"docomo":"E6A0","au":"EAAD","softbank":"E332","google":"FEB44","image":"2b55.png","sheet_x":60,"sheet_y":53,"short_name":"o","short_names":["o"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1534,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"WAVY DASH","unified":"3030-FE0F","non_qualified":"3030","docomo":"E709","au":null,"softbank":null,"google":"FEB07","image":"3030-fe0f.png","sheet_x":60,"sheet_y":54,"short_name":"wavy_dash","short_names":["wavy_dash"],"text":null,"texts":null,"category":"Symbols","subcategory":"punctuation","sort_order":1525,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"PART ALTERNATION MARK","unified":"303D-FE0F","non_qualified":"303D","docomo":null,"au":null,"softbank":"E12C","google":"FE81B","image":"303d-fe0f.png","sheet_x":60,"sheet_y":55,"short_name":"part_alternation_mark","short_names":["part_alternation_mark"],"text":null,"texts":null,"category":"Symbols","subcategory":"other-symbol","sort_order":1542,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CIRCLED IDEOGRAPH CONGRATULATION","unified":"3297-FE0F","non_qualified":"3297","docomo":null,"au":"EA99","softbank":"E30D","google":"FEB43","image":"3297-fe0f.png","sheet_x":60,"sheet_y":56,"short_name":"congratulations","short_names":["congratulations"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1597,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true},{"name":"CIRCLED IDEOGRAPH SECRET","unified":"3299-FE0F","non_qualified":"3299","docomo":"E734","au":"E4F1","softbank":"E315","google":"FEB2B","image":"3299-fe0f.png","sheet_x":60,"sheet_y":57,"short_name":"secret","short_names":["secret"],"text":null,"texts":null,"category":"Symbols","subcategory":"alphanum","sort_order":1598,"added_in":"0.6","has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_facebook":true}] \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/init.lua b/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/init.lua new file mode 100644 index 00000000..11791d68 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/init.lua @@ -0,0 +1,47 @@ +local source = {} + +source.new = function() + local self = setmetatable({}, { __index = source }) + self.commit_items = nil + self.insert_items = nil + return self +end + +source.get_trigger_characters = function() + return { ':' } +end + +source.get_keyword_pattern = function() + return [=[\%([[:space:]"'`]\|^\)\zs:[[:alnum:]_\-\+]*:\?]=] +end + +source.complete = function(self, params, callback) + -- Avoid unexpected completion. + if not vim.regex(self.get_keyword_pattern() .. '$'):match_str(params.context.cursor_before_line) then + return callback() + end + + if self:option(params).insert then + if not self.insert_items then + self.insert_items = vim.tbl_map(function(item) + item.word = nil + return item + end, require('cmp_emoji.items')()) + end + callback(self.insert_items) + else + if not self.commit_items then + self.commit_items = require('cmp_emoji.items')() + end + callback(self.commit_items) + end +end + +source.option = function(_, params) + return vim.tbl_extend('force', { + insert = false, + }, params.option) +end + +return source + diff --git a/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/items.lua b/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/items.lua new file mode 100644 index 00000000..9ced1c83 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/items.lua @@ -0,0 +1,1444 @@ +return function() return { +{ word = ':hash:'; label = '#️⃣ :hash:'; insertText = '#️⃣'; filterText = ':hash:' }; +{ word = ':keycap_star:'; label = '*️⃣ :keycap_star:'; insertText = '*️⃣'; filterText = ':keycap_star:' }; +{ word = ':zero:'; label = '0️⃣ :zero:'; insertText = '0️⃣'; filterText = ':zero:' }; +{ word = ':one:'; label = '1️⃣ :one:'; insertText = '1️⃣'; filterText = ':one:' }; +{ word = ':two:'; label = '2️⃣ :two:'; insertText = '2️⃣'; filterText = ':two:' }; +{ word = ':three:'; label = '3️⃣ :three:'; insertText = '3️⃣'; filterText = ':three:' }; +{ word = ':four:'; label = '4️⃣ :four:'; insertText = '4️⃣'; filterText = ':four:' }; +{ word = ':five:'; label = '5️⃣ :five:'; insertText = '5️⃣'; filterText = ':five:' }; +{ word = ':six:'; label = '6️⃣ :six:'; insertText = '6️⃣'; filterText = ':six:' }; +{ word = ':seven:'; label = '7️⃣ :seven:'; insertText = '7️⃣'; filterText = ':seven:' }; +{ word = ':eight:'; label = '8️⃣ :eight:'; insertText = '8️⃣'; filterText = ':eight:' }; +{ word = ':nine:'; label = '9️⃣ :nine:'; insertText = '9️⃣'; filterText = ':nine:' }; +{ word = ':copyright:'; label = '©️ :copyright:'; insertText = '©️'; filterText = ':copyright:' }; +{ word = ':registered:'; label = '®️ :registered:'; insertText = '®️'; filterText = ':registered:' }; +{ word = ':mahjong:'; label = '🀄 :mahjong:'; insertText = '🀄'; filterText = ':mahjong:' }; +{ word = ':black_joker:'; label = '🃏 :black_joker:'; insertText = '🃏'; filterText = ':black_joker:' }; +{ word = ':a:'; label = '🅰️ :a:'; insertText = '🅰️'; filterText = ':a:' }; +{ word = ':b:'; label = '🅱️ :b:'; insertText = '🅱️'; filterText = ':b:' }; +{ word = ':o2:'; label = '🅾️ :o2:'; insertText = '🅾️'; filterText = ':o2:' }; +{ word = ':parking:'; label = '🅿️ :parking:'; insertText = '🅿️'; filterText = ':parking:' }; +{ word = ':ab:'; label = '🆎 :ab:'; insertText = '🆎'; filterText = ':ab:' }; +{ word = ':cl:'; label = '🆑 :cl:'; insertText = '🆑'; filterText = ':cl:' }; +{ word = ':cool:'; label = '🆒 :cool:'; insertText = '🆒'; filterText = ':cool:' }; +{ word = ':free:'; label = '🆓 :free:'; insertText = '🆓'; filterText = ':free:' }; +{ word = ':id:'; label = '🆔 :id:'; insertText = '🆔'; filterText = ':id:' }; +{ word = ':new:'; label = '🆕 :new:'; insertText = '🆕'; filterText = ':new:' }; +{ word = ':ng:'; label = '🆖 :ng:'; insertText = '🆖'; filterText = ':ng:' }; +{ word = ':ok:'; label = '🆗 :ok:'; insertText = '🆗'; filterText = ':ok:' }; +{ word = ':sos:'; label = '🆘 :sos:'; insertText = '🆘'; filterText = ':sos:' }; +{ word = ':up:'; label = '🆙 :up:'; insertText = '🆙'; filterText = ':up:' }; +{ word = ':vs:'; label = '🆚 :vs:'; insertText = '🆚'; filterText = ':vs:' }; +{ word = ':koko:'; label = '🈁 :koko:'; insertText = '🈁'; filterText = ':koko:' }; +{ word = ':sa:'; label = '🈂️ :sa:'; insertText = '🈂️'; filterText = ':sa:' }; +{ word = ':u7121:'; label = '🈚 :u7121:'; insertText = '🈚'; filterText = ':u7121:' }; +{ word = ':u6307:'; label = '🈯 :u6307:'; insertText = '🈯'; filterText = ':u6307:' }; +{ word = ':u7981:'; label = '🈲 :u7981:'; insertText = '🈲'; filterText = ':u7981:' }; +{ word = ':u7a7a:'; label = '🈳 :u7a7a:'; insertText = '🈳'; filterText = ':u7a7a:' }; +{ word = ':u5408:'; label = '🈴 :u5408:'; insertText = '🈴'; filterText = ':u5408:' }; +{ word = ':u6e80:'; label = '🈵 :u6e80:'; insertText = '🈵'; filterText = ':u6e80:' }; +{ word = ':u6709:'; label = '🈶 :u6709:'; insertText = '🈶'; filterText = ':u6709:' }; +{ word = ':u6708:'; label = '🈷️ :u6708:'; insertText = '🈷️'; filterText = ':u6708:' }; +{ word = ':u7533:'; label = '🈸 :u7533:'; insertText = '🈸'; filterText = ':u7533:' }; +{ word = ':u5272:'; label = '🈹 :u5272:'; insertText = '🈹'; filterText = ':u5272:' }; +{ word = ':u55b6:'; label = '🈺 :u55b6:'; insertText = '🈺'; filterText = ':u55b6:' }; +{ word = ':ideograph_advantage:'; label = '🉐 :ideograph_advantage:'; insertText = '🉐'; filterText = ':ideograph_advantage:' }; +{ word = ':accept:'; label = '🉑 :accept:'; insertText = '🉑'; filterText = ':accept:' }; +{ word = ':cyclone:'; label = '🌀 :cyclone:'; insertText = '🌀'; filterText = ':cyclone:' }; +{ word = ':foggy:'; label = '🌁 :foggy:'; insertText = '🌁'; filterText = ':foggy:' }; +{ word = ':closed_umbrella:'; label = '🌂 :closed_umbrella:'; insertText = '🌂'; filterText = ':closed_umbrella:' }; +{ word = ':night_with_stars:'; label = '🌃 :night_with_stars:'; insertText = '🌃'; filterText = ':night_with_stars:' }; +{ word = ':sunrise_over_mountains:'; label = '🌄 :sunrise_over_mountains:'; insertText = '🌄'; filterText = ':sunrise_over_mountains:' }; +{ word = ':sunrise:'; label = '🌅 :sunrise:'; insertText = '🌅'; filterText = ':sunrise:' }; +{ word = ':city_sunset:'; label = '🌆 :city_sunset:'; insertText = '🌆'; filterText = ':city_sunset:' }; +{ word = ':city_sunrise:'; label = '🌇 :city_sunrise:'; insertText = '🌇'; filterText = ':city_sunrise:' }; +{ word = ':rainbow:'; label = '🌈 :rainbow:'; insertText = '🌈'; filterText = ':rainbow:' }; +{ word = ':bridge_at_night:'; label = '🌉 :bridge_at_night:'; insertText = '🌉'; filterText = ':bridge_at_night:' }; +{ word = ':ocean:'; label = '🌊 :ocean:'; insertText = '🌊'; filterText = ':ocean:' }; +{ word = ':volcano:'; label = '🌋 :volcano:'; insertText = '🌋'; filterText = ':volcano:' }; +{ word = ':milky_way:'; label = '🌌 :milky_way:'; insertText = '🌌'; filterText = ':milky_way:' }; +{ word = ':earth_africa:'; label = '🌍 :earth_africa:'; insertText = '🌍'; filterText = ':earth_africa:' }; +{ word = ':earth_americas:'; label = '🌎 :earth_americas:'; insertText = '🌎'; filterText = ':earth_americas:' }; +{ word = ':earth_asia:'; label = '🌏 :earth_asia:'; insertText = '🌏'; filterText = ':earth_asia:' }; +{ word = ':globe_with_meridians:'; label = '🌐 :globe_with_meridians:'; insertText = '🌐'; filterText = ':globe_with_meridians:' }; +{ word = ':new_moon:'; label = '🌑 :new_moon:'; insertText = '🌑'; filterText = ':new_moon:' }; +{ word = ':waxing_crescent_moon:'; label = '🌒 :waxing_crescent_moon:'; insertText = '🌒'; filterText = ':waxing_crescent_moon:' }; +{ word = ':first_quarter_moon:'; label = '🌓 :first_quarter_moon:'; insertText = '🌓'; filterText = ':first_quarter_moon:' }; +{ word = ':moon:'; label = '🌔 :moon:'; insertText = '🌔'; filterText = ':moon:' }; +{ word = ':waxing_gibbous_moon:'; label = '🌔 :waxing_gibbous_moon:'; insertText = '🌔'; filterText = ':waxing_gibbous_moon:' }; +{ word = ':full_moon:'; label = '🌕 :full_moon:'; insertText = '🌕'; filterText = ':full_moon:' }; +{ word = ':waning_gibbous_moon:'; label = '🌖 :waning_gibbous_moon:'; insertText = '🌖'; filterText = ':waning_gibbous_moon:' }; +{ word = ':last_quarter_moon:'; label = '🌗 :last_quarter_moon:'; insertText = '🌗'; filterText = ':last_quarter_moon:' }; +{ word = ':waning_crescent_moon:'; label = '🌘 :waning_crescent_moon:'; insertText = '🌘'; filterText = ':waning_crescent_moon:' }; +{ word = ':crescent_moon:'; label = '🌙 :crescent_moon:'; insertText = '🌙'; filterText = ':crescent_moon:' }; +{ word = ':new_moon_with_face:'; label = '🌚 :new_moon_with_face:'; insertText = '🌚'; filterText = ':new_moon_with_face:' }; +{ word = ':first_quarter_moon_with_face:'; label = '🌛 :first_quarter_moon_with_face:'; insertText = '🌛'; filterText = ':first_quarter_moon_with_face:' }; +{ word = ':last_quarter_moon_with_face:'; label = '🌜 :last_quarter_moon_with_face:'; insertText = '🌜'; filterText = ':last_quarter_moon_with_face:' }; +{ word = ':full_moon_with_face:'; label = '🌝 :full_moon_with_face:'; insertText = '🌝'; filterText = ':full_moon_with_face:' }; +{ word = ':sun_with_face:'; label = '🌞 :sun_with_face:'; insertText = '🌞'; filterText = ':sun_with_face:' }; +{ word = ':star2:'; label = '🌟 :star2:'; insertText = '🌟'; filterText = ':star2:' }; +{ word = ':stars:'; label = '🌠 :stars:'; insertText = '🌠'; filterText = ':stars:' }; +{ word = ':thermometer:'; label = '🌡️ :thermometer:'; insertText = '🌡️'; filterText = ':thermometer:' }; +{ word = ':mostly_sunny:'; label = '🌤️ :mostly_sunny:'; insertText = '🌤️'; filterText = ':mostly_sunny:' }; +{ word = ':sun_small_cloud:'; label = '🌤️ :sun_small_cloud:'; insertText = '🌤️'; filterText = ':sun_small_cloud:' }; +{ word = ':barely_sunny:'; label = '🌥️ :barely_sunny:'; insertText = '🌥️'; filterText = ':barely_sunny:' }; +{ word = ':sun_behind_cloud:'; label = '🌥️ :sun_behind_cloud:'; insertText = '🌥️'; filterText = ':sun_behind_cloud:' }; +{ word = ':partly_sunny_rain:'; label = '🌦️ :partly_sunny_rain:'; insertText = '🌦️'; filterText = ':partly_sunny_rain:' }; +{ word = ':sun_behind_rain_cloud:'; label = '🌦️ :sun_behind_rain_cloud:'; insertText = '🌦️'; filterText = ':sun_behind_rain_cloud:' }; +{ word = ':rain_cloud:'; label = '🌧️ :rain_cloud:'; insertText = '🌧️'; filterText = ':rain_cloud:' }; +{ word = ':snow_cloud:'; label = '🌨️ :snow_cloud:'; insertText = '🌨️'; filterText = ':snow_cloud:' }; +{ word = ':lightning:'; label = '🌩️ :lightning:'; insertText = '🌩️'; filterText = ':lightning:' }; +{ word = ':lightning_cloud:'; label = '🌩️ :lightning_cloud:'; insertText = '🌩️'; filterText = ':lightning_cloud:' }; +{ word = ':tornado:'; label = '🌪️ :tornado:'; insertText = '🌪️'; filterText = ':tornado:' }; +{ word = ':tornado_cloud:'; label = '🌪️ :tornado_cloud:'; insertText = '🌪️'; filterText = ':tornado_cloud:' }; +{ word = ':fog:'; label = '🌫️ :fog:'; insertText = '🌫️'; filterText = ':fog:' }; +{ word = ':wind_blowing_face:'; label = '🌬️ :wind_blowing_face:'; insertText = '🌬️'; filterText = ':wind_blowing_face:' }; +{ word = ':hotdog:'; label = '🌭 :hotdog:'; insertText = '🌭'; filterText = ':hotdog:' }; +{ word = ':taco:'; label = '🌮 :taco:'; insertText = '🌮'; filterText = ':taco:' }; +{ word = ':burrito:'; label = '🌯 :burrito:'; insertText = '🌯'; filterText = ':burrito:' }; +{ word = ':chestnut:'; label = '🌰 :chestnut:'; insertText = '🌰'; filterText = ':chestnut:' }; +{ word = ':seedling:'; label = '🌱 :seedling:'; insertText = '🌱'; filterText = ':seedling:' }; +{ word = ':evergreen_tree:'; label = '🌲 :evergreen_tree:'; insertText = '🌲'; filterText = ':evergreen_tree:' }; +{ word = ':deciduous_tree:'; label = '🌳 :deciduous_tree:'; insertText = '🌳'; filterText = ':deciduous_tree:' }; +{ word = ':palm_tree:'; label = '🌴 :palm_tree:'; insertText = '🌴'; filterText = ':palm_tree:' }; +{ word = ':cactus:'; label = '🌵 :cactus:'; insertText = '🌵'; filterText = ':cactus:' }; +{ word = ':hot_pepper:'; label = '🌶️ :hot_pepper:'; insertText = '🌶️'; filterText = ':hot_pepper:' }; +{ word = ':tulip:'; label = '🌷 :tulip:'; insertText = '🌷'; filterText = ':tulip:' }; +{ word = ':cherry_blossom:'; label = '🌸 :cherry_blossom:'; insertText = '🌸'; filterText = ':cherry_blossom:' }; +{ word = ':rose:'; label = '🌹 :rose:'; insertText = '🌹'; filterText = ':rose:' }; +{ word = ':hibiscus:'; label = '🌺 :hibiscus:'; insertText = '🌺'; filterText = ':hibiscus:' }; +{ word = ':sunflower:'; label = '🌻 :sunflower:'; insertText = '🌻'; filterText = ':sunflower:' }; +{ word = ':blossom:'; label = '🌼 :blossom:'; insertText = '🌼'; filterText = ':blossom:' }; +{ word = ':corn:'; label = '🌽 :corn:'; insertText = '🌽'; filterText = ':corn:' }; +{ word = ':ear_of_rice:'; label = '🌾 :ear_of_rice:'; insertText = '🌾'; filterText = ':ear_of_rice:' }; +{ word = ':herb:'; label = '🌿 :herb:'; insertText = '🌿'; filterText = ':herb:' }; +{ word = ':four_leaf_clover:'; label = '🍀 :four_leaf_clover:'; insertText = '🍀'; filterText = ':four_leaf_clover:' }; +{ word = ':maple_leaf:'; label = '🍁 :maple_leaf:'; insertText = '🍁'; filterText = ':maple_leaf:' }; +{ word = ':fallen_leaf:'; label = '🍂 :fallen_leaf:'; insertText = '🍂'; filterText = ':fallen_leaf:' }; +{ word = ':leaves:'; label = '🍃 :leaves:'; insertText = '🍃'; filterText = ':leaves:' }; +{ word = ':mushroom:'; label = '🍄 :mushroom:'; insertText = '🍄'; filterText = ':mushroom:' }; +{ word = ':tomato:'; label = '🍅 :tomato:'; insertText = '🍅'; filterText = ':tomato:' }; +{ word = ':eggplant:'; label = '🍆 :eggplant:'; insertText = '🍆'; filterText = ':eggplant:' }; +{ word = ':grapes:'; label = '🍇 :grapes:'; insertText = '🍇'; filterText = ':grapes:' }; +{ word = ':melon:'; label = '🍈 :melon:'; insertText = '🍈'; filterText = ':melon:' }; +{ word = ':watermelon:'; label = '🍉 :watermelon:'; insertText = '🍉'; filterText = ':watermelon:' }; +{ word = ':tangerine:'; label = '🍊 :tangerine:'; insertText = '🍊'; filterText = ':tangerine:' }; +{ word = ':lemon:'; label = '🍋 :lemon:'; insertText = '🍋'; filterText = ':lemon:' }; +{ word = ':banana:'; label = '🍌 :banana:'; insertText = '🍌'; filterText = ':banana:' }; +{ word = ':pineapple:'; label = '🍍 :pineapple:'; insertText = '🍍'; filterText = ':pineapple:' }; +{ word = ':apple:'; label = '🍎 :apple:'; insertText = '🍎'; filterText = ':apple:' }; +{ word = ':green_apple:'; label = '🍏 :green_apple:'; insertText = '🍏'; filterText = ':green_apple:' }; +{ word = ':pear:'; label = '🍐 :pear:'; insertText = '🍐'; filterText = ':pear:' }; +{ word = ':peach:'; label = '🍑 :peach:'; insertText = '🍑'; filterText = ':peach:' }; +{ word = ':cherries:'; label = '🍒 :cherries:'; insertText = '🍒'; filterText = ':cherries:' }; +{ word = ':strawberry:'; label = '🍓 :strawberry:'; insertText = '🍓'; filterText = ':strawberry:' }; +{ word = ':hamburger:'; label = '🍔 :hamburger:'; insertText = '🍔'; filterText = ':hamburger:' }; +{ word = ':pizza:'; label = '🍕 :pizza:'; insertText = '🍕'; filterText = ':pizza:' }; +{ word = ':meat_on_bone:'; label = '🍖 :meat_on_bone:'; insertText = '🍖'; filterText = ':meat_on_bone:' }; +{ word = ':poultry_leg:'; label = '🍗 :poultry_leg:'; insertText = '🍗'; filterText = ':poultry_leg:' }; +{ word = ':rice_cracker:'; label = '🍘 :rice_cracker:'; insertText = '🍘'; filterText = ':rice_cracker:' }; +{ word = ':rice_ball:'; label = '🍙 :rice_ball:'; insertText = '🍙'; filterText = ':rice_ball:' }; +{ word = ':rice:'; label = '🍚 :rice:'; insertText = '🍚'; filterText = ':rice:' }; +{ word = ':curry:'; label = '🍛 :curry:'; insertText = '🍛'; filterText = ':curry:' }; +{ word = ':ramen:'; label = '🍜 :ramen:'; insertText = '🍜'; filterText = ':ramen:' }; +{ word = ':spaghetti:'; label = '🍝 :spaghetti:'; insertText = '🍝'; filterText = ':spaghetti:' }; +{ word = ':bread:'; label = '🍞 :bread:'; insertText = '🍞'; filterText = ':bread:' }; +{ word = ':fries:'; label = '🍟 :fries:'; insertText = '🍟'; filterText = ':fries:' }; +{ word = ':sweet_potato:'; label = '🍠 :sweet_potato:'; insertText = '🍠'; filterText = ':sweet_potato:' }; +{ word = ':dango:'; label = '🍡 :dango:'; insertText = '🍡'; filterText = ':dango:' }; +{ word = ':oden:'; label = '🍢 :oden:'; insertText = '🍢'; filterText = ':oden:' }; +{ word = ':sushi:'; label = '🍣 :sushi:'; insertText = '🍣'; filterText = ':sushi:' }; +{ word = ':fried_shrimp:'; label = '🍤 :fried_shrimp:'; insertText = '🍤'; filterText = ':fried_shrimp:' }; +{ word = ':fish_cake:'; label = '🍥 :fish_cake:'; insertText = '🍥'; filterText = ':fish_cake:' }; +{ word = ':icecream:'; label = '🍦 :icecream:'; insertText = '🍦'; filterText = ':icecream:' }; +{ word = ':shaved_ice:'; label = '🍧 :shaved_ice:'; insertText = '🍧'; filterText = ':shaved_ice:' }; +{ word = ':ice_cream:'; label = '🍨 :ice_cream:'; insertText = '🍨'; filterText = ':ice_cream:' }; +{ word = ':doughnut:'; label = '🍩 :doughnut:'; insertText = '🍩'; filterText = ':doughnut:' }; +{ word = ':cookie:'; label = '🍪 :cookie:'; insertText = '🍪'; filterText = ':cookie:' }; +{ word = ':chocolate_bar:'; label = '🍫 :chocolate_bar:'; insertText = '🍫'; filterText = ':chocolate_bar:' }; +{ word = ':candy:'; label = '🍬 :candy:'; insertText = '🍬'; filterText = ':candy:' }; +{ word = ':lollipop:'; label = '🍭 :lollipop:'; insertText = '🍭'; filterText = ':lollipop:' }; +{ word = ':custard:'; label = '🍮 :custard:'; insertText = '🍮'; filterText = ':custard:' }; +{ word = ':honey_pot:'; label = '🍯 :honey_pot:'; insertText = '🍯'; filterText = ':honey_pot:' }; +{ word = ':cake:'; label = '🍰 :cake:'; insertText = '🍰'; filterText = ':cake:' }; +{ word = ':bento:'; label = '🍱 :bento:'; insertText = '🍱'; filterText = ':bento:' }; +{ word = ':stew:'; label = '🍲 :stew:'; insertText = '🍲'; filterText = ':stew:' }; +{ word = ':fried_egg:'; label = '🍳 :fried_egg:'; insertText = '🍳'; filterText = ':fried_egg:' }; +{ word = ':cooking:'; label = '🍳 :cooking:'; insertText = '🍳'; filterText = ':cooking:' }; +{ word = ':fork_and_knife:'; label = '🍴 :fork_and_knife:'; insertText = '🍴'; filterText = ':fork_and_knife:' }; +{ word = ':tea:'; label = '🍵 :tea:'; insertText = '🍵'; filterText = ':tea:' }; +{ word = ':sake:'; label = '🍶 :sake:'; insertText = '🍶'; filterText = ':sake:' }; +{ word = ':wine_glass:'; label = '🍷 :wine_glass:'; insertText = '🍷'; filterText = ':wine_glass:' }; +{ word = ':cocktail:'; label = '🍸 :cocktail:'; insertText = '🍸'; filterText = ':cocktail:' }; +{ word = ':tropical_drink:'; label = '🍹 :tropical_drink:'; insertText = '🍹'; filterText = ':tropical_drink:' }; +{ word = ':beer:'; label = '🍺 :beer:'; insertText = '🍺'; filterText = ':beer:' }; +{ word = ':beers:'; label = '🍻 :beers:'; insertText = '🍻'; filterText = ':beers:' }; +{ word = ':baby_bottle:'; label = '🍼 :baby_bottle:'; insertText = '🍼'; filterText = ':baby_bottle:' }; +{ word = ':knife_fork_plate:'; label = '🍽️ :knife_fork_plate:'; insertText = '🍽️'; filterText = ':knife_fork_plate:' }; +{ word = ':champagne:'; label = '🍾 :champagne:'; insertText = '🍾'; filterText = ':champagne:' }; +{ word = ':popcorn:'; label = '🍿 :popcorn:'; insertText = '🍿'; filterText = ':popcorn:' }; +{ word = ':ribbon:'; label = '🎀 :ribbon:'; insertText = '🎀'; filterText = ':ribbon:' }; +{ word = ':gift:'; label = '🎁 :gift:'; insertText = '🎁'; filterText = ':gift:' }; +{ word = ':birthday:'; label = '🎂 :birthday:'; insertText = '🎂'; filterText = ':birthday:' }; +{ word = ':jack_o_lantern:'; label = '🎃 :jack_o_lantern:'; insertText = '🎃'; filterText = ':jack_o_lantern:' }; +{ word = ':christmas_tree:'; label = '🎄 :christmas_tree:'; insertText = '🎄'; filterText = ':christmas_tree:' }; +{ word = ':santa:'; label = '🎅 :santa:'; insertText = '🎅'; filterText = ':santa:' }; +{ word = ':fireworks:'; label = '🎆 :fireworks:'; insertText = '🎆'; filterText = ':fireworks:' }; +{ word = ':sparkler:'; label = '🎇 :sparkler:'; insertText = '🎇'; filterText = ':sparkler:' }; +{ word = ':balloon:'; label = '🎈 :balloon:'; insertText = '🎈'; filterText = ':balloon:' }; +{ word = ':tada:'; label = '🎉 :tada:'; insertText = '🎉'; filterText = ':tada:' }; +{ word = ':confetti_ball:'; label = '🎊 :confetti_ball:'; insertText = '🎊'; filterText = ':confetti_ball:' }; +{ word = ':tanabata_tree:'; label = '🎋 :tanabata_tree:'; insertText = '🎋'; filterText = ':tanabata_tree:' }; +{ word = ':crossed_flags:'; label = '🎌 :crossed_flags:'; insertText = '🎌'; filterText = ':crossed_flags:' }; +{ word = ':bamboo:'; label = '🎍 :bamboo:'; insertText = '🎍'; filterText = ':bamboo:' }; +{ word = ':dolls:'; label = '🎎 :dolls:'; insertText = '🎎'; filterText = ':dolls:' }; +{ word = ':flags:'; label = '🎏 :flags:'; insertText = '🎏'; filterText = ':flags:' }; +{ word = ':wind_chime:'; label = '🎐 :wind_chime:'; insertText = '🎐'; filterText = ':wind_chime:' }; +{ word = ':rice_scene:'; label = '🎑 :rice_scene:'; insertText = '🎑'; filterText = ':rice_scene:' }; +{ word = ':school_satchel:'; label = '🎒 :school_satchel:'; insertText = '🎒'; filterText = ':school_satchel:' }; +{ word = ':mortar_board:'; label = '🎓 :mortar_board:'; insertText = '🎓'; filterText = ':mortar_board:' }; +{ word = ':medal:'; label = '🎖️ :medal:'; insertText = '🎖️'; filterText = ':medal:' }; +{ word = ':reminder_ribbon:'; label = '🎗️ :reminder_ribbon:'; insertText = '🎗️'; filterText = ':reminder_ribbon:' }; +{ word = ':studio_microphone:'; label = '🎙️ :studio_microphone:'; insertText = '🎙️'; filterText = ':studio_microphone:' }; +{ word = ':level_slider:'; label = '🎚️ :level_slider:'; insertText = '🎚️'; filterText = ':level_slider:' }; +{ word = ':control_knobs:'; label = '🎛️ :control_knobs:'; insertText = '🎛️'; filterText = ':control_knobs:' }; +{ word = ':film_frames:'; label = '🎞️ :film_frames:'; insertText = '🎞️'; filterText = ':film_frames:' }; +{ word = ':admission_tickets:'; label = '🎟️ :admission_tickets:'; insertText = '🎟️'; filterText = ':admission_tickets:' }; +{ word = ':carousel_horse:'; label = '🎠 :carousel_horse:'; insertText = '🎠'; filterText = ':carousel_horse:' }; +{ word = ':ferris_wheel:'; label = '🎡 :ferris_wheel:'; insertText = '🎡'; filterText = ':ferris_wheel:' }; +{ word = ':roller_coaster:'; label = '🎢 :roller_coaster:'; insertText = '🎢'; filterText = ':roller_coaster:' }; +{ word = ':fishing_pole_and_fish:'; label = '🎣 :fishing_pole_and_fish:'; insertText = '🎣'; filterText = ':fishing_pole_and_fish:' }; +{ word = ':microphone:'; label = '🎤 :microphone:'; insertText = '🎤'; filterText = ':microphone:' }; +{ word = ':movie_camera:'; label = '🎥 :movie_camera:'; insertText = '🎥'; filterText = ':movie_camera:' }; +{ word = ':cinema:'; label = '🎦 :cinema:'; insertText = '🎦'; filterText = ':cinema:' }; +{ word = ':headphones:'; label = '🎧 :headphones:'; insertText = '🎧'; filterText = ':headphones:' }; +{ word = ':art:'; label = '🎨 :art:'; insertText = '🎨'; filterText = ':art:' }; +{ word = ':tophat:'; label = '🎩 :tophat:'; insertText = '🎩'; filterText = ':tophat:' }; +{ word = ':circus_tent:'; label = '🎪 :circus_tent:'; insertText = '🎪'; filterText = ':circus_tent:' }; +{ word = ':ticket:'; label = '🎫 :ticket:'; insertText = '🎫'; filterText = ':ticket:' }; +{ word = ':clapper:'; label = '🎬 :clapper:'; insertText = '🎬'; filterText = ':clapper:' }; +{ word = ':performing_arts:'; label = '🎭 :performing_arts:'; insertText = '🎭'; filterText = ':performing_arts:' }; +{ word = ':video_game:'; label = '🎮 :video_game:'; insertText = '🎮'; filterText = ':video_game:' }; +{ word = ':dart:'; label = '🎯 :dart:'; insertText = '🎯'; filterText = ':dart:' }; +{ word = ':slot_machine:'; label = '🎰 :slot_machine:'; insertText = '🎰'; filterText = ':slot_machine:' }; +{ word = ':8ball:'; label = '🎱 :8ball:'; insertText = '🎱'; filterText = ':8ball:' }; +{ word = ':game_die:'; label = '🎲 :game_die:'; insertText = '🎲'; filterText = ':game_die:' }; +{ word = ':bowling:'; label = '🎳 :bowling:'; insertText = '🎳'; filterText = ':bowling:' }; +{ word = ':flower_playing_cards:'; label = '🎴 :flower_playing_cards:'; insertText = '🎴'; filterText = ':flower_playing_cards:' }; +{ word = ':musical_note:'; label = '🎵 :musical_note:'; insertText = '🎵'; filterText = ':musical_note:' }; +{ word = ':notes:'; label = '🎶 :notes:'; insertText = '🎶'; filterText = ':notes:' }; +{ word = ':saxophone:'; label = '🎷 :saxophone:'; insertText = '🎷'; filterText = ':saxophone:' }; +{ word = ':guitar:'; label = '🎸 :guitar:'; insertText = '🎸'; filterText = ':guitar:' }; +{ word = ':musical_keyboard:'; label = '🎹 :musical_keyboard:'; insertText = '🎹'; filterText = ':musical_keyboard:' }; +{ word = ':trumpet:'; label = '🎺 :trumpet:'; insertText = '🎺'; filterText = ':trumpet:' }; +{ word = ':violin:'; label = '🎻 :violin:'; insertText = '🎻'; filterText = ':violin:' }; +{ word = ':musical_score:'; label = '🎼 :musical_score:'; insertText = '🎼'; filterText = ':musical_score:' }; +{ word = ':running_shirt_with_sash:'; label = '🎽 :running_shirt_with_sash:'; insertText = '🎽'; filterText = ':running_shirt_with_sash:' }; +{ word = ':tennis:'; label = '🎾 :tennis:'; insertText = '🎾'; filterText = ':tennis:' }; +{ word = ':ski:'; label = '🎿 :ski:'; insertText = '🎿'; filterText = ':ski:' }; +{ word = ':basketball:'; label = '🏀 :basketball:'; insertText = '🏀'; filterText = ':basketball:' }; +{ word = ':checkered_flag:'; label = '🏁 :checkered_flag:'; insertText = '🏁'; filterText = ':checkered_flag:' }; +{ word = ':snowboarder:'; label = '🏂 :snowboarder:'; insertText = '🏂'; filterText = ':snowboarder:' }; +{ word = ':runner:'; label = '🏃 :runner:'; insertText = '🏃'; filterText = ':runner:' }; +{ word = ':running:'; label = '🏃 :running:'; insertText = '🏃'; filterText = ':running:' }; +{ word = ':surfer:'; label = '🏄 :surfer:'; insertText = '🏄'; filterText = ':surfer:' }; +{ word = ':sports_medal:'; label = '🏅 :sports_medal:'; insertText = '🏅'; filterText = ':sports_medal:' }; +{ word = ':trophy:'; label = '🏆 :trophy:'; insertText = '🏆'; filterText = ':trophy:' }; +{ word = ':horse_racing:'; label = '🏇 :horse_racing:'; insertText = '🏇'; filterText = ':horse_racing:' }; +{ word = ':football:'; label = '🏈 :football:'; insertText = '🏈'; filterText = ':football:' }; +{ word = ':rugby_football:'; label = '🏉 :rugby_football:'; insertText = '🏉'; filterText = ':rugby_football:' }; +{ word = ':swimmer:'; label = '🏊 :swimmer:'; insertText = '🏊'; filterText = ':swimmer:' }; +{ word = ':weight_lifter:'; label = '🏋️ :weight_lifter:'; insertText = '🏋️'; filterText = ':weight_lifter:' }; +{ word = ':golfer:'; label = '🏌️ :golfer:'; insertText = '🏌️'; filterText = ':golfer:' }; +{ word = ':racing_motorcycle:'; label = '🏍️ :racing_motorcycle:'; insertText = '🏍️'; filterText = ':racing_motorcycle:' }; +{ word = ':racing_car:'; label = '🏎️ :racing_car:'; insertText = '🏎️'; filterText = ':racing_car:' }; +{ word = ':cricket_bat_and_ball:'; label = '🏏 :cricket_bat_and_ball:'; insertText = '🏏'; filterText = ':cricket_bat_and_ball:' }; +{ word = ':volleyball:'; label = '🏐 :volleyball:'; insertText = '🏐'; filterText = ':volleyball:' }; +{ word = ':field_hockey_stick_and_ball:'; label = '🏑 :field_hockey_stick_and_ball:'; insertText = '🏑'; filterText = ':field_hockey_stick_and_ball:' }; +{ word = ':ice_hockey_stick_and_puck:'; label = '🏒 :ice_hockey_stick_and_puck:'; insertText = '🏒'; filterText = ':ice_hockey_stick_and_puck:' }; +{ word = ':table_tennis_paddle_and_ball:'; label = '🏓 :table_tennis_paddle_and_ball:'; insertText = '🏓'; filterText = ':table_tennis_paddle_and_ball:' }; +{ word = ':snow_capped_mountain:'; label = '🏔️ :snow_capped_mountain:'; insertText = '🏔️'; filterText = ':snow_capped_mountain:' }; +{ word = ':camping:'; label = '🏕️ :camping:'; insertText = '🏕️'; filterText = ':camping:' }; +{ word = ':beach_with_umbrella:'; label = '🏖️ :beach_with_umbrella:'; insertText = '🏖️'; filterText = ':beach_with_umbrella:' }; +{ word = ':building_construction:'; label = '🏗️ :building_construction:'; insertText = '🏗️'; filterText = ':building_construction:' }; +{ word = ':house_buildings:'; label = '🏘️ :house_buildings:'; insertText = '🏘️'; filterText = ':house_buildings:' }; +{ word = ':cityscape:'; label = '🏙️ :cityscape:'; insertText = '🏙️'; filterText = ':cityscape:' }; +{ word = ':derelict_house_building:'; label = '🏚️ :derelict_house_building:'; insertText = '🏚️'; filterText = ':derelict_house_building:' }; +{ word = ':classical_building:'; label = '🏛️ :classical_building:'; insertText = '🏛️'; filterText = ':classical_building:' }; +{ word = ':desert:'; label = '🏜️ :desert:'; insertText = '🏜️'; filterText = ':desert:' }; +{ word = ':desert_island:'; label = '🏝️ :desert_island:'; insertText = '🏝️'; filterText = ':desert_island:' }; +{ word = ':national_park:'; label = '🏞️ :national_park:'; insertText = '🏞️'; filterText = ':national_park:' }; +{ word = ':stadium:'; label = '🏟️ :stadium:'; insertText = '🏟️'; filterText = ':stadium:' }; +{ word = ':house:'; label = '🏠 :house:'; insertText = '🏠'; filterText = ':house:' }; +{ word = ':house_with_garden:'; label = '🏡 :house_with_garden:'; insertText = '🏡'; filterText = ':house_with_garden:' }; +{ word = ':office:'; label = '🏢 :office:'; insertText = '🏢'; filterText = ':office:' }; +{ word = ':post_office:'; label = '🏣 :post_office:'; insertText = '🏣'; filterText = ':post_office:' }; +{ word = ':european_post_office:'; label = '🏤 :european_post_office:'; insertText = '🏤'; filterText = ':european_post_office:' }; +{ word = ':hospital:'; label = '🏥 :hospital:'; insertText = '🏥'; filterText = ':hospital:' }; +{ word = ':bank:'; label = '🏦 :bank:'; insertText = '🏦'; filterText = ':bank:' }; +{ word = ':atm:'; label = '🏧 :atm:'; insertText = '🏧'; filterText = ':atm:' }; +{ word = ':hotel:'; label = '🏨 :hotel:'; insertText = '🏨'; filterText = ':hotel:' }; +{ word = ':love_hotel:'; label = '🏩 :love_hotel:'; insertText = '🏩'; filterText = ':love_hotel:' }; +{ word = ':convenience_store:'; label = '🏪 :convenience_store:'; insertText = '🏪'; filterText = ':convenience_store:' }; +{ word = ':school:'; label = '🏫 :school:'; insertText = '🏫'; filterText = ':school:' }; +{ word = ':department_store:'; label = '🏬 :department_store:'; insertText = '🏬'; filterText = ':department_store:' }; +{ word = ':factory:'; label = '🏭 :factory:'; insertText = '🏭'; filterText = ':factory:' }; +{ word = ':izakaya_lantern:'; label = '🏮 :izakaya_lantern:'; insertText = '🏮'; filterText = ':izakaya_lantern:' }; +{ word = ':lantern:'; label = '🏮 :lantern:'; insertText = '🏮'; filterText = ':lantern:' }; +{ word = ':japanese_castle:'; label = '🏯 :japanese_castle:'; insertText = '🏯'; filterText = ':japanese_castle:' }; +{ word = ':european_castle:'; label = '🏰 :european_castle:'; insertText = '🏰'; filterText = ':european_castle:' }; +{ word = ':waving_white_flag:'; label = '🏳️ :waving_white_flag:'; insertText = '🏳️'; filterText = ':waving_white_flag:' }; +{ word = ':waving_black_flag:'; label = '🏴 :waving_black_flag:'; insertText = '🏴'; filterText = ':waving_black_flag:' }; +{ word = ':rosette:'; label = '🏵️ :rosette:'; insertText = '🏵️'; filterText = ':rosette:' }; +{ word = ':label:'; label = '🏷️ :label:'; insertText = '🏷️'; filterText = ':label:' }; +{ word = ':badminton_racquet_and_shuttlecock:'; label = '🏸 :badminton_racquet_and_shuttlecock:'; insertText = '🏸'; filterText = ':badminton_racquet_and_shuttlecock:' }; +{ word = ':bow_and_arrow:'; label = '🏹 :bow_and_arrow:'; insertText = '🏹'; filterText = ':bow_and_arrow:' }; +{ word = ':amphora:'; label = '🏺 :amphora:'; insertText = '🏺'; filterText = ':amphora:' }; +{ word = ':skin-tone-2:'; label = '🏻 :skin-tone-2:'; insertText = '🏻'; filterText = ':skin-tone-2:' }; +{ word = ':skin-tone-3:'; label = '🏼 :skin-tone-3:'; insertText = '🏼'; filterText = ':skin-tone-3:' }; +{ word = ':skin-tone-4:'; label = '🏽 :skin-tone-4:'; insertText = '🏽'; filterText = ':skin-tone-4:' }; +{ word = ':skin-tone-5:'; label = '🏾 :skin-tone-5:'; insertText = '🏾'; filterText = ':skin-tone-5:' }; +{ word = ':skin-tone-6:'; label = '🏿 :skin-tone-6:'; insertText = '🏿'; filterText = ':skin-tone-6:' }; +{ word = ':rat:'; label = '🐀 :rat:'; insertText = '🐀'; filterText = ':rat:' }; +{ word = ':mouse2:'; label = '🐁 :mouse2:'; insertText = '🐁'; filterText = ':mouse2:' }; +{ word = ':ox:'; label = '🐂 :ox:'; insertText = '🐂'; filterText = ':ox:' }; +{ word = ':water_buffalo:'; label = '🐃 :water_buffalo:'; insertText = '🐃'; filterText = ':water_buffalo:' }; +{ word = ':cow2:'; label = '🐄 :cow2:'; insertText = '🐄'; filterText = ':cow2:' }; +{ word = ':tiger2:'; label = '🐅 :tiger2:'; insertText = '🐅'; filterText = ':tiger2:' }; +{ word = ':leopard:'; label = '🐆 :leopard:'; insertText = '🐆'; filterText = ':leopard:' }; +{ word = ':rabbit2:'; label = '🐇 :rabbit2:'; insertText = '🐇'; filterText = ':rabbit2:' }; +{ word = ':cat2:'; label = '🐈 :cat2:'; insertText = '🐈'; filterText = ':cat2:' }; +{ word = ':dragon:'; label = '🐉 :dragon:'; insertText = '🐉'; filterText = ':dragon:' }; +{ word = ':crocodile:'; label = '🐊 :crocodile:'; insertText = '🐊'; filterText = ':crocodile:' }; +{ word = ':whale2:'; label = '🐋 :whale2:'; insertText = '🐋'; filterText = ':whale2:' }; +{ word = ':snail:'; label = '🐌 :snail:'; insertText = '🐌'; filterText = ':snail:' }; +{ word = ':snake:'; label = '🐍 :snake:'; insertText = '🐍'; filterText = ':snake:' }; +{ word = ':racehorse:'; label = '🐎 :racehorse:'; insertText = '🐎'; filterText = ':racehorse:' }; +{ word = ':ram:'; label = '🐏 :ram:'; insertText = '🐏'; filterText = ':ram:' }; +{ word = ':goat:'; label = '🐐 :goat:'; insertText = '🐐'; filterText = ':goat:' }; +{ word = ':sheep:'; label = '🐑 :sheep:'; insertText = '🐑'; filterText = ':sheep:' }; +{ word = ':monkey:'; label = '🐒 :monkey:'; insertText = '🐒'; filterText = ':monkey:' }; +{ word = ':rooster:'; label = '🐓 :rooster:'; insertText = '🐓'; filterText = ':rooster:' }; +{ word = ':chicken:'; label = '🐔 :chicken:'; insertText = '🐔'; filterText = ':chicken:' }; +{ word = ':dog2:'; label = '🐕 :dog2:'; insertText = '🐕'; filterText = ':dog2:' }; +{ word = ':pig2:'; label = '🐖 :pig2:'; insertText = '🐖'; filterText = ':pig2:' }; +{ word = ':boar:'; label = '🐗 :boar:'; insertText = '🐗'; filterText = ':boar:' }; +{ word = ':elephant:'; label = '🐘 :elephant:'; insertText = '🐘'; filterText = ':elephant:' }; +{ word = ':octopus:'; label = '🐙 :octopus:'; insertText = '🐙'; filterText = ':octopus:' }; +{ word = ':shell:'; label = '🐚 :shell:'; insertText = '🐚'; filterText = ':shell:' }; +{ word = ':bug:'; label = '🐛 :bug:'; insertText = '🐛'; filterText = ':bug:' }; +{ word = ':ant:'; label = '🐜 :ant:'; insertText = '🐜'; filterText = ':ant:' }; +{ word = ':bee:'; label = '🐝 :bee:'; insertText = '🐝'; filterText = ':bee:' }; +{ word = ':honeybee:'; label = '🐝 :honeybee:'; insertText = '🐝'; filterText = ':honeybee:' }; +{ word = ':ladybug:'; label = '🐞 :ladybug:'; insertText = '🐞'; filterText = ':ladybug:' }; +{ word = ':lady_beetle:'; label = '🐞 :lady_beetle:'; insertText = '🐞'; filterText = ':lady_beetle:' }; +{ word = ':fish:'; label = '🐟 :fish:'; insertText = '🐟'; filterText = ':fish:' }; +{ word = ':tropical_fish:'; label = '🐠 :tropical_fish:'; insertText = '🐠'; filterText = ':tropical_fish:' }; +{ word = ':blowfish:'; label = '🐡 :blowfish:'; insertText = '🐡'; filterText = ':blowfish:' }; +{ word = ':turtle:'; label = '🐢 :turtle:'; insertText = '🐢'; filterText = ':turtle:' }; +{ word = ':hatching_chick:'; label = '🐣 :hatching_chick:'; insertText = '🐣'; filterText = ':hatching_chick:' }; +{ word = ':baby_chick:'; label = '🐤 :baby_chick:'; insertText = '🐤'; filterText = ':baby_chick:' }; +{ word = ':hatched_chick:'; label = '🐥 :hatched_chick:'; insertText = '🐥'; filterText = ':hatched_chick:' }; +{ word = ':bird:'; label = '🐦 :bird:'; insertText = '🐦'; filterText = ':bird:' }; +{ word = ':penguin:'; label = '🐧 :penguin:'; insertText = '🐧'; filterText = ':penguin:' }; +{ word = ':koala:'; label = '🐨 :koala:'; insertText = '🐨'; filterText = ':koala:' }; +{ word = ':poodle:'; label = '🐩 :poodle:'; insertText = '🐩'; filterText = ':poodle:' }; +{ word = ':dromedary_camel:'; label = '🐪 :dromedary_camel:'; insertText = '🐪'; filterText = ':dromedary_camel:' }; +{ word = ':camel:'; label = '🐫 :camel:'; insertText = '🐫'; filterText = ':camel:' }; +{ word = ':dolphin:'; label = '🐬 :dolphin:'; insertText = '🐬'; filterText = ':dolphin:' }; +{ word = ':flipper:'; label = '🐬 :flipper:'; insertText = '🐬'; filterText = ':flipper:' }; +{ word = ':mouse:'; label = '🐭 :mouse:'; insertText = '🐭'; filterText = ':mouse:' }; +{ word = ':cow:'; label = '🐮 :cow:'; insertText = '🐮'; filterText = ':cow:' }; +{ word = ':tiger:'; label = '🐯 :tiger:'; insertText = '🐯'; filterText = ':tiger:' }; +{ word = ':rabbit:'; label = '🐰 :rabbit:'; insertText = '🐰'; filterText = ':rabbit:' }; +{ word = ':cat:'; label = '🐱 :cat:'; insertText = '🐱'; filterText = ':cat:' }; +{ word = ':dragon_face:'; label = '🐲 :dragon_face:'; insertText = '🐲'; filterText = ':dragon_face:' }; +{ word = ':whale:'; label = '🐳 :whale:'; insertText = '🐳'; filterText = ':whale:' }; +{ word = ':horse:'; label = '🐴 :horse:'; insertText = '🐴'; filterText = ':horse:' }; +{ word = ':monkey_face:'; label = '🐵 :monkey_face:'; insertText = '🐵'; filterText = ':monkey_face:' }; +{ word = ':dog:'; label = '🐶 :dog:'; insertText = '🐶'; filterText = ':dog:' }; +{ word = ':pig:'; label = '🐷 :pig:'; insertText = '🐷'; filterText = ':pig:' }; +{ word = ':frog:'; label = '🐸 :frog:'; insertText = '🐸'; filterText = ':frog:' }; +{ word = ':hamster:'; label = '🐹 :hamster:'; insertText = '🐹'; filterText = ':hamster:' }; +{ word = ':wolf:'; label = '🐺 :wolf:'; insertText = '🐺'; filterText = ':wolf:' }; +{ word = ':bear:'; label = '🐻 :bear:'; insertText = '🐻'; filterText = ':bear:' }; +{ word = ':panda_face:'; label = '🐼 :panda_face:'; insertText = '🐼'; filterText = ':panda_face:' }; +{ word = ':pig_nose:'; label = '🐽 :pig_nose:'; insertText = '🐽'; filterText = ':pig_nose:' }; +{ word = ':feet:'; label = '🐾 :feet:'; insertText = '🐾'; filterText = ':feet:' }; +{ word = ':paw_prints:'; label = '🐾 :paw_prints:'; insertText = '🐾'; filterText = ':paw_prints:' }; +{ word = ':chipmunk:'; label = '🐿️ :chipmunk:'; insertText = '🐿️'; filterText = ':chipmunk:' }; +{ word = ':eyes:'; label = '👀 :eyes:'; insertText = '👀'; filterText = ':eyes:' }; +{ word = ':eye:'; label = '👁️ :eye:'; insertText = '👁️'; filterText = ':eye:' }; +{ word = ':ear:'; label = '👂 :ear:'; insertText = '👂'; filterText = ':ear:' }; +{ word = ':nose:'; label = '👃 :nose:'; insertText = '👃'; filterText = ':nose:' }; +{ word = ':lips:'; label = '👄 :lips:'; insertText = '👄'; filterText = ':lips:' }; +{ word = ':tongue:'; label = '👅 :tongue:'; insertText = '👅'; filterText = ':tongue:' }; +{ word = ':point_up_2:'; label = '👆 :point_up_2:'; insertText = '👆'; filterText = ':point_up_2:' }; +{ word = ':point_down:'; label = '👇 :point_down:'; insertText = '👇'; filterText = ':point_down:' }; +{ word = ':point_left:'; label = '👈 :point_left:'; insertText = '👈'; filterText = ':point_left:' }; +{ word = ':point_right:'; label = '👉 :point_right:'; insertText = '👉'; filterText = ':point_right:' }; +{ word = ':facepunch:'; label = '👊 :facepunch:'; insertText = '👊'; filterText = ':facepunch:' }; +{ word = ':punch:'; label = '👊 :punch:'; insertText = '👊'; filterText = ':punch:' }; +{ word = ':wave:'; label = '👋 :wave:'; insertText = '👋'; filterText = ':wave:' }; +{ word = ':ok_hand:'; label = '👌 :ok_hand:'; insertText = '👌'; filterText = ':ok_hand:' }; +{ word = ':+1:'; label = '👍 :+1:'; insertText = '👍'; filterText = ':+1:' }; +{ word = ':thumbsup:'; label = '👍 :thumbsup:'; insertText = '👍'; filterText = ':thumbsup:' }; +{ word = ':-1:'; label = '👎 :-1:'; insertText = '👎'; filterText = ':-1:' }; +{ word = ':thumbsdown:'; label = '👎 :thumbsdown:'; insertText = '👎'; filterText = ':thumbsdown:' }; +{ word = ':clap:'; label = '👏 :clap:'; insertText = '👏'; filterText = ':clap:' }; +{ word = ':open_hands:'; label = '👐 :open_hands:'; insertText = '👐'; filterText = ':open_hands:' }; +{ word = ':crown:'; label = '👑 :crown:'; insertText = '👑'; filterText = ':crown:' }; +{ word = ':womans_hat:'; label = '👒 :womans_hat:'; insertText = '👒'; filterText = ':womans_hat:' }; +{ word = ':eyeglasses:'; label = '👓 :eyeglasses:'; insertText = '👓'; filterText = ':eyeglasses:' }; +{ word = ':necktie:'; label = '👔 :necktie:'; insertText = '👔'; filterText = ':necktie:' }; +{ word = ':shirt:'; label = '👕 :shirt:'; insertText = '👕'; filterText = ':shirt:' }; +{ word = ':tshirt:'; label = '👕 :tshirt:'; insertText = '👕'; filterText = ':tshirt:' }; +{ word = ':jeans:'; label = '👖 :jeans:'; insertText = '👖'; filterText = ':jeans:' }; +{ word = ':dress:'; label = '👗 :dress:'; insertText = '👗'; filterText = ':dress:' }; +{ word = ':kimono:'; label = '👘 :kimono:'; insertText = '👘'; filterText = ':kimono:' }; +{ word = ':bikini:'; label = '👙 :bikini:'; insertText = '👙'; filterText = ':bikini:' }; +{ word = ':womans_clothes:'; label = '👚 :womans_clothes:'; insertText = '👚'; filterText = ':womans_clothes:' }; +{ word = ':purse:'; label = '👛 :purse:'; insertText = '👛'; filterText = ':purse:' }; +{ word = ':handbag:'; label = '👜 :handbag:'; insertText = '👜'; filterText = ':handbag:' }; +{ word = ':pouch:'; label = '👝 :pouch:'; insertText = '👝'; filterText = ':pouch:' }; +{ word = ':mans_shoe:'; label = '👞 :mans_shoe:'; insertText = '👞'; filterText = ':mans_shoe:' }; +{ word = ':shoe:'; label = '👞 :shoe:'; insertText = '👞'; filterText = ':shoe:' }; +{ word = ':athletic_shoe:'; label = '👟 :athletic_shoe:'; insertText = '👟'; filterText = ':athletic_shoe:' }; +{ word = ':high_heel:'; label = '👠 :high_heel:'; insertText = '👠'; filterText = ':high_heel:' }; +{ word = ':sandal:'; label = '👡 :sandal:'; insertText = '👡'; filterText = ':sandal:' }; +{ word = ':boot:'; label = '👢 :boot:'; insertText = '👢'; filterText = ':boot:' }; +{ word = ':footprints:'; label = '👣 :footprints:'; insertText = '👣'; filterText = ':footprints:' }; +{ word = ':bust_in_silhouette:'; label = '👤 :bust_in_silhouette:'; insertText = '👤'; filterText = ':bust_in_silhouette:' }; +{ word = ':busts_in_silhouette:'; label = '👥 :busts_in_silhouette:'; insertText = '👥'; filterText = ':busts_in_silhouette:' }; +{ word = ':boy:'; label = '👦 :boy:'; insertText = '👦'; filterText = ':boy:' }; +{ word = ':girl:'; label = '👧 :girl:'; insertText = '👧'; filterText = ':girl:' }; +{ word = ':man:'; label = '👨 :man:'; insertText = '👨'; filterText = ':man:' }; +{ word = ':woman:'; label = '👩 :woman:'; insertText = '👩'; filterText = ':woman:' }; +{ word = ':family:'; label = '👪 :family:'; insertText = '👪'; filterText = ':family:' }; +{ word = ':man_and_woman_holding_hands:'; label = '👫 :man_and_woman_holding_hands:'; insertText = '👫'; filterText = ':man_and_woman_holding_hands:' }; +{ word = ':woman_and_man_holding_hands:'; label = '👫 :woman_and_man_holding_hands:'; insertText = '👫'; filterText = ':woman_and_man_holding_hands:' }; +{ word = ':couple:'; label = '👫 :couple:'; insertText = '👫'; filterText = ':couple:' }; +{ word = ':two_men_holding_hands:'; label = '👬 :two_men_holding_hands:'; insertText = '👬'; filterText = ':two_men_holding_hands:' }; +{ word = ':men_holding_hands:'; label = '👬 :men_holding_hands:'; insertText = '👬'; filterText = ':men_holding_hands:' }; +{ word = ':two_women_holding_hands:'; label = '👭 :two_women_holding_hands:'; insertText = '👭'; filterText = ':two_women_holding_hands:' }; +{ word = ':women_holding_hands:'; label = '👭 :women_holding_hands:'; insertText = '👭'; filterText = ':women_holding_hands:' }; +{ word = ':cop:'; label = '👮 :cop:'; insertText = '👮'; filterText = ':cop:' }; +{ word = ':dancers:'; label = '👯 :dancers:'; insertText = '👯'; filterText = ':dancers:' }; +{ word = ':bride_with_veil:'; label = '👰 :bride_with_veil:'; insertText = '👰'; filterText = ':bride_with_veil:' }; +{ word = ':person_with_blond_hair:'; label = '👱 :person_with_blond_hair:'; insertText = '👱'; filterText = ':person_with_blond_hair:' }; +{ word = ':man_with_gua_pi_mao:'; label = '👲 :man_with_gua_pi_mao:'; insertText = '👲'; filterText = ':man_with_gua_pi_mao:' }; +{ word = ':man_with_turban:'; label = '👳 :man_with_turban:'; insertText = '👳'; filterText = ':man_with_turban:' }; +{ word = ':older_man:'; label = '👴 :older_man:'; insertText = '👴'; filterText = ':older_man:' }; +{ word = ':older_woman:'; label = '👵 :older_woman:'; insertText = '👵'; filterText = ':older_woman:' }; +{ word = ':baby:'; label = '👶 :baby:'; insertText = '👶'; filterText = ':baby:' }; +{ word = ':construction_worker:'; label = '👷 :construction_worker:'; insertText = '👷'; filterText = ':construction_worker:' }; +{ word = ':princess:'; label = '👸 :princess:'; insertText = '👸'; filterText = ':princess:' }; +{ word = ':japanese_ogre:'; label = '👹 :japanese_ogre:'; insertText = '👹'; filterText = ':japanese_ogre:' }; +{ word = ':japanese_goblin:'; label = '👺 :japanese_goblin:'; insertText = '👺'; filterText = ':japanese_goblin:' }; +{ word = ':ghost:'; label = '👻 :ghost:'; insertText = '👻'; filterText = ':ghost:' }; +{ word = ':angel:'; label = '👼 :angel:'; insertText = '👼'; filterText = ':angel:' }; +{ word = ':alien:'; label = '👽 :alien:'; insertText = '👽'; filterText = ':alien:' }; +{ word = ':space_invader:'; label = '👾 :space_invader:'; insertText = '👾'; filterText = ':space_invader:' }; +{ word = ':imp:'; label = '👿 :imp:'; insertText = '👿'; filterText = ':imp:' }; +{ word = ':skull:'; label = '💀 :skull:'; insertText = '💀'; filterText = ':skull:' }; +{ word = ':information_desk_person:'; label = '💁 :information_desk_person:'; insertText = '💁'; filterText = ':information_desk_person:' }; +{ word = ':guardsman:'; label = '💂 :guardsman:'; insertText = '💂'; filterText = ':guardsman:' }; +{ word = ':dancer:'; label = '💃 :dancer:'; insertText = '💃'; filterText = ':dancer:' }; +{ word = ':lipstick:'; label = '💄 :lipstick:'; insertText = '💄'; filterText = ':lipstick:' }; +{ word = ':nail_care:'; label = '💅 :nail_care:'; insertText = '💅'; filterText = ':nail_care:' }; +{ word = ':massage:'; label = '💆 :massage:'; insertText = '💆'; filterText = ':massage:' }; +{ word = ':haircut:'; label = '💇 :haircut:'; insertText = '💇'; filterText = ':haircut:' }; +{ word = ':barber:'; label = '💈 :barber:'; insertText = '💈'; filterText = ':barber:' }; +{ word = ':syringe:'; label = '💉 :syringe:'; insertText = '💉'; filterText = ':syringe:' }; +{ word = ':pill:'; label = '💊 :pill:'; insertText = '💊'; filterText = ':pill:' }; +{ word = ':kiss:'; label = '💋 :kiss:'; insertText = '💋'; filterText = ':kiss:' }; +{ word = ':love_letter:'; label = '💌 :love_letter:'; insertText = '💌'; filterText = ':love_letter:' }; +{ word = ':ring:'; label = '💍 :ring:'; insertText = '💍'; filterText = ':ring:' }; +{ word = ':gem:'; label = '💎 :gem:'; insertText = '💎'; filterText = ':gem:' }; +{ word = ':couplekiss:'; label = '💏 :couplekiss:'; insertText = '💏'; filterText = ':couplekiss:' }; +{ word = ':bouquet:'; label = '💐 :bouquet:'; insertText = '💐'; filterText = ':bouquet:' }; +{ word = ':couple_with_heart:'; label = '💑 :couple_with_heart:'; insertText = '💑'; filterText = ':couple_with_heart:' }; +{ word = ':wedding:'; label = '💒 :wedding:'; insertText = '💒'; filterText = ':wedding:' }; +{ word = ':heartbeat:'; label = '💓 :heartbeat:'; insertText = '💓'; filterText = ':heartbeat:' }; +{ word = ':broken_heart:'; label = '💔 :broken_heart:'; insertText = '💔'; filterText = ':broken_heart:' }; +{ word = ':two_hearts:'; label = '💕 :two_hearts:'; insertText = '💕'; filterText = ':two_hearts:' }; +{ word = ':sparkling_heart:'; label = '💖 :sparkling_heart:'; insertText = '💖'; filterText = ':sparkling_heart:' }; +{ word = ':heartpulse:'; label = '💗 :heartpulse:'; insertText = '💗'; filterText = ':heartpulse:' }; +{ word = ':cupid:'; label = '💘 :cupid:'; insertText = '💘'; filterText = ':cupid:' }; +{ word = ':blue_heart:'; label = '💙 :blue_heart:'; insertText = '💙'; filterText = ':blue_heart:' }; +{ word = ':green_heart:'; label = '💚 :green_heart:'; insertText = '💚'; filterText = ':green_heart:' }; +{ word = ':yellow_heart:'; label = '💛 :yellow_heart:'; insertText = '💛'; filterText = ':yellow_heart:' }; +{ word = ':purple_heart:'; label = '💜 :purple_heart:'; insertText = '💜'; filterText = ':purple_heart:' }; +{ word = ':gift_heart:'; label = '💝 :gift_heart:'; insertText = '💝'; filterText = ':gift_heart:' }; +{ word = ':revolving_hearts:'; label = '💞 :revolving_hearts:'; insertText = '💞'; filterText = ':revolving_hearts:' }; +{ word = ':heart_decoration:'; label = '💟 :heart_decoration:'; insertText = '💟'; filterText = ':heart_decoration:' }; +{ word = ':diamond_shape_with_a_dot_inside:'; label = '💠 :diamond_shape_with_a_dot_inside:'; insertText = '💠'; filterText = ':diamond_shape_with_a_dot_inside:' }; +{ word = ':bulb:'; label = '💡 :bulb:'; insertText = '💡'; filterText = ':bulb:' }; +{ word = ':anger:'; label = '💢 :anger:'; insertText = '💢'; filterText = ':anger:' }; +{ word = ':bomb:'; label = '💣 :bomb:'; insertText = '💣'; filterText = ':bomb:' }; +{ word = ':zzz:'; label = '💤 :zzz:'; insertText = '💤'; filterText = ':zzz:' }; +{ word = ':boom:'; label = '💥 :boom:'; insertText = '💥'; filterText = ':boom:' }; +{ word = ':collision:'; label = '💥 :collision:'; insertText = '💥'; filterText = ':collision:' }; +{ word = ':sweat_drops:'; label = '💦 :sweat_drops:'; insertText = '💦'; filterText = ':sweat_drops:' }; +{ word = ':droplet:'; label = '💧 :droplet:'; insertText = '💧'; filterText = ':droplet:' }; +{ word = ':dash:'; label = '💨 :dash:'; insertText = '💨'; filterText = ':dash:' }; +{ word = ':hankey:'; label = '💩 :hankey:'; insertText = '💩'; filterText = ':hankey:' }; +{ word = ':poop:'; label = '💩 :poop:'; insertText = '💩'; filterText = ':poop:' }; +{ word = ':shit:'; label = '💩 :shit:'; insertText = '💩'; filterText = ':shit:' }; +{ word = ':muscle:'; label = '💪 :muscle:'; insertText = '💪'; filterText = ':muscle:' }; +{ word = ':dizzy:'; label = '💫 :dizzy:'; insertText = '💫'; filterText = ':dizzy:' }; +{ word = ':speech_balloon:'; label = '💬 :speech_balloon:'; insertText = '💬'; filterText = ':speech_balloon:' }; +{ word = ':thought_balloon:'; label = '💭 :thought_balloon:'; insertText = '💭'; filterText = ':thought_balloon:' }; +{ word = ':white_flower:'; label = '💮 :white_flower:'; insertText = '💮'; filterText = ':white_flower:' }; +{ word = ':100:'; label = '💯 :100:'; insertText = '💯'; filterText = ':100:' }; +{ word = ':moneybag:'; label = '💰 :moneybag:'; insertText = '💰'; filterText = ':moneybag:' }; +{ word = ':currency_exchange:'; label = '💱 :currency_exchange:'; insertText = '💱'; filterText = ':currency_exchange:' }; +{ word = ':heavy_dollar_sign:'; label = '💲 :heavy_dollar_sign:'; insertText = '💲'; filterText = ':heavy_dollar_sign:' }; +{ word = ':credit_card:'; label = '💳 :credit_card:'; insertText = '💳'; filterText = ':credit_card:' }; +{ word = ':yen:'; label = '💴 :yen:'; insertText = '💴'; filterText = ':yen:' }; +{ word = ':dollar:'; label = '💵 :dollar:'; insertText = '💵'; filterText = ':dollar:' }; +{ word = ':euro:'; label = '💶 :euro:'; insertText = '💶'; filterText = ':euro:' }; +{ word = ':pound:'; label = '💷 :pound:'; insertText = '💷'; filterText = ':pound:' }; +{ word = ':money_with_wings:'; label = '💸 :money_with_wings:'; insertText = '💸'; filterText = ':money_with_wings:' }; +{ word = ':chart:'; label = '💹 :chart:'; insertText = '💹'; filterText = ':chart:' }; +{ word = ':seat:'; label = '💺 :seat:'; insertText = '💺'; filterText = ':seat:' }; +{ word = ':computer:'; label = '💻 :computer:'; insertText = '💻'; filterText = ':computer:' }; +{ word = ':briefcase:'; label = '💼 :briefcase:'; insertText = '💼'; filterText = ':briefcase:' }; +{ word = ':minidisc:'; label = '💽 :minidisc:'; insertText = '💽'; filterText = ':minidisc:' }; +{ word = ':floppy_disk:'; label = '💾 :floppy_disk:'; insertText = '💾'; filterText = ':floppy_disk:' }; +{ word = ':cd:'; label = '💿 :cd:'; insertText = '💿'; filterText = ':cd:' }; +{ word = ':dvd:'; label = '📀 :dvd:'; insertText = '📀'; filterText = ':dvd:' }; +{ word = ':file_folder:'; label = '📁 :file_folder:'; insertText = '📁'; filterText = ':file_folder:' }; +{ word = ':open_file_folder:'; label = '📂 :open_file_folder:'; insertText = '📂'; filterText = ':open_file_folder:' }; +{ word = ':page_with_curl:'; label = '📃 :page_with_curl:'; insertText = '📃'; filterText = ':page_with_curl:' }; +{ word = ':page_facing_up:'; label = '📄 :page_facing_up:'; insertText = '📄'; filterText = ':page_facing_up:' }; +{ word = ':date:'; label = '📅 :date:'; insertText = '📅'; filterText = ':date:' }; +{ word = ':calendar:'; label = '📆 :calendar:'; insertText = '📆'; filterText = ':calendar:' }; +{ word = ':card_index:'; label = '📇 :card_index:'; insertText = '📇'; filterText = ':card_index:' }; +{ word = ':chart_with_upwards_trend:'; label = '📈 :chart_with_upwards_trend:'; insertText = '📈'; filterText = ':chart_with_upwards_trend:' }; +{ word = ':chart_with_downwards_trend:'; label = '📉 :chart_with_downwards_trend:'; insertText = '📉'; filterText = ':chart_with_downwards_trend:' }; +{ word = ':bar_chart:'; label = '📊 :bar_chart:'; insertText = '📊'; filterText = ':bar_chart:' }; +{ word = ':clipboard:'; label = '📋 :clipboard:'; insertText = '📋'; filterText = ':clipboard:' }; +{ word = ':pushpin:'; label = '📌 :pushpin:'; insertText = '📌'; filterText = ':pushpin:' }; +{ word = ':round_pushpin:'; label = '📍 :round_pushpin:'; insertText = '📍'; filterText = ':round_pushpin:' }; +{ word = ':paperclip:'; label = '📎 :paperclip:'; insertText = '📎'; filterText = ':paperclip:' }; +{ word = ':straight_ruler:'; label = '📏 :straight_ruler:'; insertText = '📏'; filterText = ':straight_ruler:' }; +{ word = ':triangular_ruler:'; label = '📐 :triangular_ruler:'; insertText = '📐'; filterText = ':triangular_ruler:' }; +{ word = ':bookmark_tabs:'; label = '📑 :bookmark_tabs:'; insertText = '📑'; filterText = ':bookmark_tabs:' }; +{ word = ':ledger:'; label = '📒 :ledger:'; insertText = '📒'; filterText = ':ledger:' }; +{ word = ':notebook:'; label = '📓 :notebook:'; insertText = '📓'; filterText = ':notebook:' }; +{ word = ':notebook_with_decorative_cover:'; label = '📔 :notebook_with_decorative_cover:'; insertText = '📔'; filterText = ':notebook_with_decorative_cover:' }; +{ word = ':closed_book:'; label = '📕 :closed_book:'; insertText = '📕'; filterText = ':closed_book:' }; +{ word = ':book:'; label = '📖 :book:'; insertText = '📖'; filterText = ':book:' }; +{ word = ':open_book:'; label = '📖 :open_book:'; insertText = '📖'; filterText = ':open_book:' }; +{ word = ':green_book:'; label = '📗 :green_book:'; insertText = '📗'; filterText = ':green_book:' }; +{ word = ':blue_book:'; label = '📘 :blue_book:'; insertText = '📘'; filterText = ':blue_book:' }; +{ word = ':orange_book:'; label = '📙 :orange_book:'; insertText = '📙'; filterText = ':orange_book:' }; +{ word = ':books:'; label = '📚 :books:'; insertText = '📚'; filterText = ':books:' }; +{ word = ':name_badge:'; label = '📛 :name_badge:'; insertText = '📛'; filterText = ':name_badge:' }; +{ word = ':scroll:'; label = '📜 :scroll:'; insertText = '📜'; filterText = ':scroll:' }; +{ word = ':memo:'; label = '📝 :memo:'; insertText = '📝'; filterText = ':memo:' }; +{ word = ':pencil:'; label = '📝 :pencil:'; insertText = '📝'; filterText = ':pencil:' }; +{ word = ':telephone_receiver:'; label = '📞 :telephone_receiver:'; insertText = '📞'; filterText = ':telephone_receiver:' }; +{ word = ':pager:'; label = '📟 :pager:'; insertText = '📟'; filterText = ':pager:' }; +{ word = ':fax:'; label = '📠 :fax:'; insertText = '📠'; filterText = ':fax:' }; +{ word = ':satellite_antenna:'; label = '📡 :satellite_antenna:'; insertText = '📡'; filterText = ':satellite_antenna:' }; +{ word = ':loudspeaker:'; label = '📢 :loudspeaker:'; insertText = '📢'; filterText = ':loudspeaker:' }; +{ word = ':mega:'; label = '📣 :mega:'; insertText = '📣'; filterText = ':mega:' }; +{ word = ':outbox_tray:'; label = '📤 :outbox_tray:'; insertText = '📤'; filterText = ':outbox_tray:' }; +{ word = ':inbox_tray:'; label = '📥 :inbox_tray:'; insertText = '📥'; filterText = ':inbox_tray:' }; +{ word = ':package:'; label = '📦 :package:'; insertText = '📦'; filterText = ':package:' }; +{ word = ':e-mail:'; label = '📧 :e-mail:'; insertText = '📧'; filterText = ':e-mail:' }; +{ word = ':incoming_envelope:'; label = '📨 :incoming_envelope:'; insertText = '📨'; filterText = ':incoming_envelope:' }; +{ word = ':envelope_with_arrow:'; label = '📩 :envelope_with_arrow:'; insertText = '📩'; filterText = ':envelope_with_arrow:' }; +{ word = ':mailbox_closed:'; label = '📪 :mailbox_closed:'; insertText = '📪'; filterText = ':mailbox_closed:' }; +{ word = ':mailbox:'; label = '📫 :mailbox:'; insertText = '📫'; filterText = ':mailbox:' }; +{ word = ':mailbox_with_mail:'; label = '📬 :mailbox_with_mail:'; insertText = '📬'; filterText = ':mailbox_with_mail:' }; +{ word = ':mailbox_with_no_mail:'; label = '📭 :mailbox_with_no_mail:'; insertText = '📭'; filterText = ':mailbox_with_no_mail:' }; +{ word = ':postbox:'; label = '📮 :postbox:'; insertText = '📮'; filterText = ':postbox:' }; +{ word = ':postal_horn:'; label = '📯 :postal_horn:'; insertText = '📯'; filterText = ':postal_horn:' }; +{ word = ':newspaper:'; label = '📰 :newspaper:'; insertText = '📰'; filterText = ':newspaper:' }; +{ word = ':iphone:'; label = '📱 :iphone:'; insertText = '📱'; filterText = ':iphone:' }; +{ word = ':calling:'; label = '📲 :calling:'; insertText = '📲'; filterText = ':calling:' }; +{ word = ':vibration_mode:'; label = '📳 :vibration_mode:'; insertText = '📳'; filterText = ':vibration_mode:' }; +{ word = ':mobile_phone_off:'; label = '📴 :mobile_phone_off:'; insertText = '📴'; filterText = ':mobile_phone_off:' }; +{ word = ':no_mobile_phones:'; label = '📵 :no_mobile_phones:'; insertText = '📵'; filterText = ':no_mobile_phones:' }; +{ word = ':signal_strength:'; label = '📶 :signal_strength:'; insertText = '📶'; filterText = ':signal_strength:' }; +{ word = ':camera:'; label = '📷 :camera:'; insertText = '📷'; filterText = ':camera:' }; +{ word = ':camera_with_flash:'; label = '📸 :camera_with_flash:'; insertText = '📸'; filterText = ':camera_with_flash:' }; +{ word = ':video_camera:'; label = '📹 :video_camera:'; insertText = '📹'; filterText = ':video_camera:' }; +{ word = ':tv:'; label = '📺 :tv:'; insertText = '📺'; filterText = ':tv:' }; +{ word = ':radio:'; label = '📻 :radio:'; insertText = '📻'; filterText = ':radio:' }; +{ word = ':vhs:'; label = '📼 :vhs:'; insertText = '📼'; filterText = ':vhs:' }; +{ word = ':film_projector:'; label = '📽️ :film_projector:'; insertText = '📽️'; filterText = ':film_projector:' }; +{ word = ':prayer_beads:'; label = '📿 :prayer_beads:'; insertText = '📿'; filterText = ':prayer_beads:' }; +{ word = ':twisted_rightwards_arrows:'; label = '🔀 :twisted_rightwards_arrows:'; insertText = '🔀'; filterText = ':twisted_rightwards_arrows:' }; +{ word = ':repeat:'; label = '🔁 :repeat:'; insertText = '🔁'; filterText = ':repeat:' }; +{ word = ':repeat_one:'; label = '🔂 :repeat_one:'; insertText = '🔂'; filterText = ':repeat_one:' }; +{ word = ':arrows_clockwise:'; label = '🔃 :arrows_clockwise:'; insertText = '🔃'; filterText = ':arrows_clockwise:' }; +{ word = ':arrows_counterclockwise:'; label = '🔄 :arrows_counterclockwise:'; insertText = '🔄'; filterText = ':arrows_counterclockwise:' }; +{ word = ':low_brightness:'; label = '🔅 :low_brightness:'; insertText = '🔅'; filterText = ':low_brightness:' }; +{ word = ':high_brightness:'; label = '🔆 :high_brightness:'; insertText = '🔆'; filterText = ':high_brightness:' }; +{ word = ':mute:'; label = '🔇 :mute:'; insertText = '🔇'; filterText = ':mute:' }; +{ word = ':speaker:'; label = '🔈 :speaker:'; insertText = '🔈'; filterText = ':speaker:' }; +{ word = ':sound:'; label = '🔉 :sound:'; insertText = '🔉'; filterText = ':sound:' }; +{ word = ':loud_sound:'; label = '🔊 :loud_sound:'; insertText = '🔊'; filterText = ':loud_sound:' }; +{ word = ':battery:'; label = '🔋 :battery:'; insertText = '🔋'; filterText = ':battery:' }; +{ word = ':electric_plug:'; label = '🔌 :electric_plug:'; insertText = '🔌'; filterText = ':electric_plug:' }; +{ word = ':mag:'; label = '🔍 :mag:'; insertText = '🔍'; filterText = ':mag:' }; +{ word = ':mag_right:'; label = '🔎 :mag_right:'; insertText = '🔎'; filterText = ':mag_right:' }; +{ word = ':lock_with_ink_pen:'; label = '🔏 :lock_with_ink_pen:'; insertText = '🔏'; filterText = ':lock_with_ink_pen:' }; +{ word = ':closed_lock_with_key:'; label = '🔐 :closed_lock_with_key:'; insertText = '🔐'; filterText = ':closed_lock_with_key:' }; +{ word = ':key:'; label = '🔑 :key:'; insertText = '🔑'; filterText = ':key:' }; +{ word = ':lock:'; label = '🔒 :lock:'; insertText = '🔒'; filterText = ':lock:' }; +{ word = ':unlock:'; label = '🔓 :unlock:'; insertText = '🔓'; filterText = ':unlock:' }; +{ word = ':bell:'; label = '🔔 :bell:'; insertText = '🔔'; filterText = ':bell:' }; +{ word = ':no_bell:'; label = '🔕 :no_bell:'; insertText = '🔕'; filterText = ':no_bell:' }; +{ word = ':bookmark:'; label = '🔖 :bookmark:'; insertText = '🔖'; filterText = ':bookmark:' }; +{ word = ':link:'; label = '🔗 :link:'; insertText = '🔗'; filterText = ':link:' }; +{ word = ':radio_button:'; label = '🔘 :radio_button:'; insertText = '🔘'; filterText = ':radio_button:' }; +{ word = ':back:'; label = '🔙 :back:'; insertText = '🔙'; filterText = ':back:' }; +{ word = ':end:'; label = '🔚 :end:'; insertText = '🔚'; filterText = ':end:' }; +{ word = ':on:'; label = '🔛 :on:'; insertText = '🔛'; filterText = ':on:' }; +{ word = ':soon:'; label = '🔜 :soon:'; insertText = '🔜'; filterText = ':soon:' }; +{ word = ':top:'; label = '🔝 :top:'; insertText = '🔝'; filterText = ':top:' }; +{ word = ':underage:'; label = '🔞 :underage:'; insertText = '🔞'; filterText = ':underage:' }; +{ word = ':keycap_ten:'; label = '🔟 :keycap_ten:'; insertText = '🔟'; filterText = ':keycap_ten:' }; +{ word = ':capital_abcd:'; label = '🔠 :capital_abcd:'; insertText = '🔠'; filterText = ':capital_abcd:' }; +{ word = ':abcd:'; label = '🔡 :abcd:'; insertText = '🔡'; filterText = ':abcd:' }; +{ word = ':1234:'; label = '🔢 :1234:'; insertText = '🔢'; filterText = ':1234:' }; +{ word = ':symbols:'; label = '🔣 :symbols:'; insertText = '🔣'; filterText = ':symbols:' }; +{ word = ':abc:'; label = '🔤 :abc:'; insertText = '🔤'; filterText = ':abc:' }; +{ word = ':fire:'; label = '🔥 :fire:'; insertText = '🔥'; filterText = ':fire:' }; +{ word = ':flashlight:'; label = '🔦 :flashlight:'; insertText = '🔦'; filterText = ':flashlight:' }; +{ word = ':wrench:'; label = '🔧 :wrench:'; insertText = '🔧'; filterText = ':wrench:' }; +{ word = ':hammer:'; label = '🔨 :hammer:'; insertText = '🔨'; filterText = ':hammer:' }; +{ word = ':nut_and_bolt:'; label = '🔩 :nut_and_bolt:'; insertText = '🔩'; filterText = ':nut_and_bolt:' }; +{ word = ':hocho:'; label = '🔪 :hocho:'; insertText = '🔪'; filterText = ':hocho:' }; +{ word = ':knife:'; label = '🔪 :knife:'; insertText = '🔪'; filterText = ':knife:' }; +{ word = ':gun:'; label = '🔫 :gun:'; insertText = '🔫'; filterText = ':gun:' }; +{ word = ':microscope:'; label = '🔬 :microscope:'; insertText = '🔬'; filterText = ':microscope:' }; +{ word = ':telescope:'; label = '🔭 :telescope:'; insertText = '🔭'; filterText = ':telescope:' }; +{ word = ':crystal_ball:'; label = '🔮 :crystal_ball:'; insertText = '🔮'; filterText = ':crystal_ball:' }; +{ word = ':six_pointed_star:'; label = '🔯 :six_pointed_star:'; insertText = '🔯'; filterText = ':six_pointed_star:' }; +{ word = ':beginner:'; label = '🔰 :beginner:'; insertText = '🔰'; filterText = ':beginner:' }; +{ word = ':trident:'; label = '🔱 :trident:'; insertText = '🔱'; filterText = ':trident:' }; +{ word = ':black_square_button:'; label = '🔲 :black_square_button:'; insertText = '🔲'; filterText = ':black_square_button:' }; +{ word = ':white_square_button:'; label = '🔳 :white_square_button:'; insertText = '🔳'; filterText = ':white_square_button:' }; +{ word = ':red_circle:'; label = '🔴 :red_circle:'; insertText = '🔴'; filterText = ':red_circle:' }; +{ word = ':large_blue_circle:'; label = '🔵 :large_blue_circle:'; insertText = '🔵'; filterText = ':large_blue_circle:' }; +{ word = ':large_orange_diamond:'; label = '🔶 :large_orange_diamond:'; insertText = '🔶'; filterText = ':large_orange_diamond:' }; +{ word = ':large_blue_diamond:'; label = '🔷 :large_blue_diamond:'; insertText = '🔷'; filterText = ':large_blue_diamond:' }; +{ word = ':small_orange_diamond:'; label = '🔸 :small_orange_diamond:'; insertText = '🔸'; filterText = ':small_orange_diamond:' }; +{ word = ':small_blue_diamond:'; label = '🔹 :small_blue_diamond:'; insertText = '🔹'; filterText = ':small_blue_diamond:' }; +{ word = ':small_red_triangle:'; label = '🔺 :small_red_triangle:'; insertText = '🔺'; filterText = ':small_red_triangle:' }; +{ word = ':small_red_triangle_down:'; label = '🔻 :small_red_triangle_down:'; insertText = '🔻'; filterText = ':small_red_triangle_down:' }; +{ word = ':arrow_up_small:'; label = '🔼 :arrow_up_small:'; insertText = '🔼'; filterText = ':arrow_up_small:' }; +{ word = ':arrow_down_small:'; label = '🔽 :arrow_down_small:'; insertText = '🔽'; filterText = ':arrow_down_small:' }; +{ word = ':om_symbol:'; label = '🕉️ :om_symbol:'; insertText = '🕉️'; filterText = ':om_symbol:' }; +{ word = ':dove_of_peace:'; label = '🕊️ :dove_of_peace:'; insertText = '🕊️'; filterText = ':dove_of_peace:' }; +{ word = ':kaaba:'; label = '🕋 :kaaba:'; insertText = '🕋'; filterText = ':kaaba:' }; +{ word = ':mosque:'; label = '🕌 :mosque:'; insertText = '🕌'; filterText = ':mosque:' }; +{ word = ':synagogue:'; label = '🕍 :synagogue:'; insertText = '🕍'; filterText = ':synagogue:' }; +{ word = ':menorah_with_nine_branches:'; label = '🕎 :menorah_with_nine_branches:'; insertText = '🕎'; filterText = ':menorah_with_nine_branches:' }; +{ word = ':clock1:'; label = '🕐 :clock1:'; insertText = '🕐'; filterText = ':clock1:' }; +{ word = ':clock2:'; label = '🕑 :clock2:'; insertText = '🕑'; filterText = ':clock2:' }; +{ word = ':clock3:'; label = '🕒 :clock3:'; insertText = '🕒'; filterText = ':clock3:' }; +{ word = ':clock4:'; label = '🕓 :clock4:'; insertText = '🕓'; filterText = ':clock4:' }; +{ word = ':clock5:'; label = '🕔 :clock5:'; insertText = '🕔'; filterText = ':clock5:' }; +{ word = ':clock6:'; label = '🕕 :clock6:'; insertText = '🕕'; filterText = ':clock6:' }; +{ word = ':clock7:'; label = '🕖 :clock7:'; insertText = '🕖'; filterText = ':clock7:' }; +{ word = ':clock8:'; label = '🕗 :clock8:'; insertText = '🕗'; filterText = ':clock8:' }; +{ word = ':clock9:'; label = '🕘 :clock9:'; insertText = '🕘'; filterText = ':clock9:' }; +{ word = ':clock10:'; label = '🕙 :clock10:'; insertText = '🕙'; filterText = ':clock10:' }; +{ word = ':clock11:'; label = '🕚 :clock11:'; insertText = '🕚'; filterText = ':clock11:' }; +{ word = ':clock12:'; label = '🕛 :clock12:'; insertText = '🕛'; filterText = ':clock12:' }; +{ word = ':clock130:'; label = '🕜 :clock130:'; insertText = '🕜'; filterText = ':clock130:' }; +{ word = ':clock230:'; label = '🕝 :clock230:'; insertText = '🕝'; filterText = ':clock230:' }; +{ word = ':clock330:'; label = '🕞 :clock330:'; insertText = '🕞'; filterText = ':clock330:' }; +{ word = ':clock430:'; label = '🕟 :clock430:'; insertText = '🕟'; filterText = ':clock430:' }; +{ word = ':clock530:'; label = '🕠 :clock530:'; insertText = '🕠'; filterText = ':clock530:' }; +{ word = ':clock630:'; label = '🕡 :clock630:'; insertText = '🕡'; filterText = ':clock630:' }; +{ word = ':clock730:'; label = '🕢 :clock730:'; insertText = '🕢'; filterText = ':clock730:' }; +{ word = ':clock830:'; label = '🕣 :clock830:'; insertText = '🕣'; filterText = ':clock830:' }; +{ word = ':clock930:'; label = '🕤 :clock930:'; insertText = '🕤'; filterText = ':clock930:' }; +{ word = ':clock1030:'; label = '🕥 :clock1030:'; insertText = '🕥'; filterText = ':clock1030:' }; +{ word = ':clock1130:'; label = '🕦 :clock1130:'; insertText = '🕦'; filterText = ':clock1130:' }; +{ word = ':clock1230:'; label = '🕧 :clock1230:'; insertText = '🕧'; filterText = ':clock1230:' }; +{ word = ':candle:'; label = '🕯️ :candle:'; insertText = '🕯️'; filterText = ':candle:' }; +{ word = ':mantelpiece_clock:'; label = '🕰️ :mantelpiece_clock:'; insertText = '🕰️'; filterText = ':mantelpiece_clock:' }; +{ word = ':hole:'; label = '🕳️ :hole:'; insertText = '🕳️'; filterText = ':hole:' }; +{ word = ':man_in_business_suit_levitating:'; label = '🕴️ :man_in_business_suit_levitating:'; insertText = '🕴️'; filterText = ':man_in_business_suit_levitating:' }; +{ word = ':sleuth_or_spy:'; label = '🕵️ :sleuth_or_spy:'; insertText = '🕵️'; filterText = ':sleuth_or_spy:' }; +{ word = ':dark_sunglasses:'; label = '🕶️ :dark_sunglasses:'; insertText = '🕶️'; filterText = ':dark_sunglasses:' }; +{ word = ':spider:'; label = '🕷️ :spider:'; insertText = '🕷️'; filterText = ':spider:' }; +{ word = ':spider_web:'; label = '🕸️ :spider_web:'; insertText = '🕸️'; filterText = ':spider_web:' }; +{ word = ':joystick:'; label = '🕹️ :joystick:'; insertText = '🕹️'; filterText = ':joystick:' }; +{ word = ':man_dancing:'; label = '🕺 :man_dancing:'; insertText = '🕺'; filterText = ':man_dancing:' }; +{ word = ':linked_paperclips:'; label = '🖇️ :linked_paperclips:'; insertText = '🖇️'; filterText = ':linked_paperclips:' }; +{ word = ':lower_left_ballpoint_pen:'; label = '🖊️ :lower_left_ballpoint_pen:'; insertText = '🖊️'; filterText = ':lower_left_ballpoint_pen:' }; +{ word = ':lower_left_fountain_pen:'; label = '🖋️ :lower_left_fountain_pen:'; insertText = '🖋️'; filterText = ':lower_left_fountain_pen:' }; +{ word = ':lower_left_paintbrush:'; label = '🖌️ :lower_left_paintbrush:'; insertText = '🖌️'; filterText = ':lower_left_paintbrush:' }; +{ word = ':lower_left_crayon:'; label = '🖍️ :lower_left_crayon:'; insertText = '🖍️'; filterText = ':lower_left_crayon:' }; +{ word = ':raised_hand_with_fingers_splayed:'; label = '🖐️ :raised_hand_with_fingers_splayed:'; insertText = '🖐️'; filterText = ':raised_hand_with_fingers_splayed:' }; +{ word = ':middle_finger:'; label = '🖕 :middle_finger:'; insertText = '🖕'; filterText = ':middle_finger:' }; +{ word = ':reversed_hand_with_middle_finger_extended:'; label = '🖕 :reversed_hand_with_middle_finger_extended:'; insertText = '🖕'; filterText = ':reversed_hand_with_middle_finger_extended:' }; +{ word = ':spock-hand:'; label = '🖖 :spock-hand:'; insertText = '🖖'; filterText = ':spock-hand:' }; +{ word = ':black_heart:'; label = '🖤 :black_heart:'; insertText = '🖤'; filterText = ':black_heart:' }; +{ word = ':desktop_computer:'; label = '🖥️ :desktop_computer:'; insertText = '🖥️'; filterText = ':desktop_computer:' }; +{ word = ':printer:'; label = '🖨️ :printer:'; insertText = '🖨️'; filterText = ':printer:' }; +{ word = ':three_button_mouse:'; label = '🖱️ :three_button_mouse:'; insertText = '🖱️'; filterText = ':three_button_mouse:' }; +{ word = ':trackball:'; label = '🖲️ :trackball:'; insertText = '🖲️'; filterText = ':trackball:' }; +{ word = ':frame_with_picture:'; label = '🖼️ :frame_with_picture:'; insertText = '🖼️'; filterText = ':frame_with_picture:' }; +{ word = ':card_index_dividers:'; label = '🗂️ :card_index_dividers:'; insertText = '🗂️'; filterText = ':card_index_dividers:' }; +{ word = ':card_file_box:'; label = '🗃️ :card_file_box:'; insertText = '🗃️'; filterText = ':card_file_box:' }; +{ word = ':file_cabinet:'; label = '🗄️ :file_cabinet:'; insertText = '🗄️'; filterText = ':file_cabinet:' }; +{ word = ':wastebasket:'; label = '🗑️ :wastebasket:'; insertText = '🗑️'; filterText = ':wastebasket:' }; +{ word = ':spiral_note_pad:'; label = '🗒️ :spiral_note_pad:'; insertText = '🗒️'; filterText = ':spiral_note_pad:' }; +{ word = ':spiral_calendar_pad:'; label = '🗓️ :spiral_calendar_pad:'; insertText = '🗓️'; filterText = ':spiral_calendar_pad:' }; +{ word = ':compression:'; label = '🗜️ :compression:'; insertText = '🗜️'; filterText = ':compression:' }; +{ word = ':old_key:'; label = '🗝️ :old_key:'; insertText = '🗝️'; filterText = ':old_key:' }; +{ word = ':rolled_up_newspaper:'; label = '🗞️ :rolled_up_newspaper:'; insertText = '🗞️'; filterText = ':rolled_up_newspaper:' }; +{ word = ':dagger_knife:'; label = '🗡️ :dagger_knife:'; insertText = '🗡️'; filterText = ':dagger_knife:' }; +{ word = ':speaking_head_in_silhouette:'; label = '🗣️ :speaking_head_in_silhouette:'; insertText = '🗣️'; filterText = ':speaking_head_in_silhouette:' }; +{ word = ':left_speech_bubble:'; label = '🗨️ :left_speech_bubble:'; insertText = '🗨️'; filterText = ':left_speech_bubble:' }; +{ word = ':right_anger_bubble:'; label = '🗯️ :right_anger_bubble:'; insertText = '🗯️'; filterText = ':right_anger_bubble:' }; +{ word = ':ballot_box_with_ballot:'; label = '🗳️ :ballot_box_with_ballot:'; insertText = '🗳️'; filterText = ':ballot_box_with_ballot:' }; +{ word = ':world_map:'; label = '🗺️ :world_map:'; insertText = '🗺️'; filterText = ':world_map:' }; +{ word = ':mount_fuji:'; label = '🗻 :mount_fuji:'; insertText = '🗻'; filterText = ':mount_fuji:' }; +{ word = ':tokyo_tower:'; label = '🗼 :tokyo_tower:'; insertText = '🗼'; filterText = ':tokyo_tower:' }; +{ word = ':statue_of_liberty:'; label = '🗽 :statue_of_liberty:'; insertText = '🗽'; filterText = ':statue_of_liberty:' }; +{ word = ':japan:'; label = '🗾 :japan:'; insertText = '🗾'; filterText = ':japan:' }; +{ word = ':moyai:'; label = '🗿 :moyai:'; insertText = '🗿'; filterText = ':moyai:' }; +{ word = ':grinning:'; label = '😀 :grinning:'; insertText = '😀'; filterText = ':grinning:' }; +{ word = ':grin:'; label = '😁 :grin:'; insertText = '😁'; filterText = ':grin:' }; +{ word = ':joy:'; label = '😂 :joy:'; insertText = '😂'; filterText = ':joy:' }; +{ word = ':smiley:'; label = '😃 :smiley:'; insertText = '😃'; filterText = ':smiley:' }; +{ word = ':smile:'; label = '😄 :smile:'; insertText = '😄'; filterText = ':smile:' }; +{ word = ':sweat_smile:'; label = '😅 :sweat_smile:'; insertText = '😅'; filterText = ':sweat_smile:' }; +{ word = ':laughing:'; label = '😆 :laughing:'; insertText = '😆'; filterText = ':laughing:' }; +{ word = ':satisfied:'; label = '😆 :satisfied:'; insertText = '😆'; filterText = ':satisfied:' }; +{ word = ':innocent:'; label = '😇 :innocent:'; insertText = '😇'; filterText = ':innocent:' }; +{ word = ':smiling_imp:'; label = '😈 :smiling_imp:'; insertText = '😈'; filterText = ':smiling_imp:' }; +{ word = ':wink:'; label = '😉 :wink:'; insertText = '😉'; filterText = ':wink:' }; +{ word = ':blush:'; label = '😊 :blush:'; insertText = '😊'; filterText = ':blush:' }; +{ word = ':yum:'; label = '😋 :yum:'; insertText = '😋'; filterText = ':yum:' }; +{ word = ':relieved:'; label = '😌 :relieved:'; insertText = '😌'; filterText = ':relieved:' }; +{ word = ':heart_eyes:'; label = '😍 :heart_eyes:'; insertText = '😍'; filterText = ':heart_eyes:' }; +{ word = ':sunglasses:'; label = '😎 :sunglasses:'; insertText = '😎'; filterText = ':sunglasses:' }; +{ word = ':smirk:'; label = '😏 :smirk:'; insertText = '😏'; filterText = ':smirk:' }; +{ word = ':neutral_face:'; label = '😐 :neutral_face:'; insertText = '😐'; filterText = ':neutral_face:' }; +{ word = ':expressionless:'; label = '😑 :expressionless:'; insertText = '😑'; filterText = ':expressionless:' }; +{ word = ':unamused:'; label = '😒 :unamused:'; insertText = '😒'; filterText = ':unamused:' }; +{ word = ':sweat:'; label = '😓 :sweat:'; insertText = '😓'; filterText = ':sweat:' }; +{ word = ':pensive:'; label = '😔 :pensive:'; insertText = '😔'; filterText = ':pensive:' }; +{ word = ':confused:'; label = '😕 :confused:'; insertText = '😕'; filterText = ':confused:' }; +{ word = ':confounded:'; label = '😖 :confounded:'; insertText = '😖'; filterText = ':confounded:' }; +{ word = ':kissing:'; label = '😗 :kissing:'; insertText = '😗'; filterText = ':kissing:' }; +{ word = ':kissing_heart:'; label = '😘 :kissing_heart:'; insertText = '😘'; filterText = ':kissing_heart:' }; +{ word = ':kissing_smiling_eyes:'; label = '😙 :kissing_smiling_eyes:'; insertText = '😙'; filterText = ':kissing_smiling_eyes:' }; +{ word = ':kissing_closed_eyes:'; label = '😚 :kissing_closed_eyes:'; insertText = '😚'; filterText = ':kissing_closed_eyes:' }; +{ word = ':stuck_out_tongue:'; label = '😛 :stuck_out_tongue:'; insertText = '😛'; filterText = ':stuck_out_tongue:' }; +{ word = ':stuck_out_tongue_winking_eye:'; label = '😜 :stuck_out_tongue_winking_eye:'; insertText = '😜'; filterText = ':stuck_out_tongue_winking_eye:' }; +{ word = ':stuck_out_tongue_closed_eyes:'; label = '😝 :stuck_out_tongue_closed_eyes:'; insertText = '😝'; filterText = ':stuck_out_tongue_closed_eyes:' }; +{ word = ':disappointed:'; label = '😞 :disappointed:'; insertText = '😞'; filterText = ':disappointed:' }; +{ word = ':worried:'; label = '😟 :worried:'; insertText = '😟'; filterText = ':worried:' }; +{ word = ':angry:'; label = '😠 :angry:'; insertText = '😠'; filterText = ':angry:' }; +{ word = ':rage:'; label = '😡 :rage:'; insertText = '😡'; filterText = ':rage:' }; +{ word = ':cry:'; label = '😢 :cry:'; insertText = '😢'; filterText = ':cry:' }; +{ word = ':persevere:'; label = '😣 :persevere:'; insertText = '😣'; filterText = ':persevere:' }; +{ word = ':triumph:'; label = '😤 :triumph:'; insertText = '😤'; filterText = ':triumph:' }; +{ word = ':disappointed_relieved:'; label = '😥 :disappointed_relieved:'; insertText = '😥'; filterText = ':disappointed_relieved:' }; +{ word = ':frowning:'; label = '😦 :frowning:'; insertText = '😦'; filterText = ':frowning:' }; +{ word = ':anguished:'; label = '😧 :anguished:'; insertText = '😧'; filterText = ':anguished:' }; +{ word = ':fearful:'; label = '😨 :fearful:'; insertText = '😨'; filterText = ':fearful:' }; +{ word = ':weary:'; label = '😩 :weary:'; insertText = '😩'; filterText = ':weary:' }; +{ word = ':sleepy:'; label = '😪 :sleepy:'; insertText = '😪'; filterText = ':sleepy:' }; +{ word = ':tired_face:'; label = '😫 :tired_face:'; insertText = '😫'; filterText = ':tired_face:' }; +{ word = ':grimacing:'; label = '😬 :grimacing:'; insertText = '😬'; filterText = ':grimacing:' }; +{ word = ':sob:'; label = '😭 :sob:'; insertText = '😭'; filterText = ':sob:' }; +{ word = ':open_mouth:'; label = '😮 :open_mouth:'; insertText = '😮'; filterText = ':open_mouth:' }; +{ word = ':hushed:'; label = '😯 :hushed:'; insertText = '😯'; filterText = ':hushed:' }; +{ word = ':cold_sweat:'; label = '😰 :cold_sweat:'; insertText = '😰'; filterText = ':cold_sweat:' }; +{ word = ':scream:'; label = '😱 :scream:'; insertText = '😱'; filterText = ':scream:' }; +{ word = ':astonished:'; label = '😲 :astonished:'; insertText = '😲'; filterText = ':astonished:' }; +{ word = ':flushed:'; label = '😳 :flushed:'; insertText = '😳'; filterText = ':flushed:' }; +{ word = ':sleeping:'; label = '😴 :sleeping:'; insertText = '😴'; filterText = ':sleeping:' }; +{ word = ':dizzy_face:'; label = '😵 :dizzy_face:'; insertText = '😵'; filterText = ':dizzy_face:' }; +{ word = ':no_mouth:'; label = '😶 :no_mouth:'; insertText = '😶'; filterText = ':no_mouth:' }; +{ word = ':mask:'; label = '😷 :mask:'; insertText = '😷'; filterText = ':mask:' }; +{ word = ':smile_cat:'; label = '😸 :smile_cat:'; insertText = '😸'; filterText = ':smile_cat:' }; +{ word = ':joy_cat:'; label = '😹 :joy_cat:'; insertText = '😹'; filterText = ':joy_cat:' }; +{ word = ':smiley_cat:'; label = '😺 :smiley_cat:'; insertText = '😺'; filterText = ':smiley_cat:' }; +{ word = ':heart_eyes_cat:'; label = '😻 :heart_eyes_cat:'; insertText = '😻'; filterText = ':heart_eyes_cat:' }; +{ word = ':smirk_cat:'; label = '😼 :smirk_cat:'; insertText = '😼'; filterText = ':smirk_cat:' }; +{ word = ':kissing_cat:'; label = '😽 :kissing_cat:'; insertText = '😽'; filterText = ':kissing_cat:' }; +{ word = ':pouting_cat:'; label = '😾 :pouting_cat:'; insertText = '😾'; filterText = ':pouting_cat:' }; +{ word = ':crying_cat_face:'; label = '😿 :crying_cat_face:'; insertText = '😿'; filterText = ':crying_cat_face:' }; +{ word = ':scream_cat:'; label = '🙀 :scream_cat:'; insertText = '🙀'; filterText = ':scream_cat:' }; +{ word = ':slightly_frowning_face:'; label = '🙁 :slightly_frowning_face:'; insertText = '🙁'; filterText = ':slightly_frowning_face:' }; +{ word = ':slightly_smiling_face:'; label = '🙂 :slightly_smiling_face:'; insertText = '🙂'; filterText = ':slightly_smiling_face:' }; +{ word = ':upside_down_face:'; label = '🙃 :upside_down_face:'; insertText = '🙃'; filterText = ':upside_down_face:' }; +{ word = ':face_with_rolling_eyes:'; label = '🙄 :face_with_rolling_eyes:'; insertText = '🙄'; filterText = ':face_with_rolling_eyes:' }; +{ word = ':no_good:'; label = '🙅 :no_good:'; insertText = '🙅'; filterText = ':no_good:' }; +{ word = ':ok_woman:'; label = '🙆 :ok_woman:'; insertText = '🙆'; filterText = ':ok_woman:' }; +{ word = ':bow:'; label = '🙇 :bow:'; insertText = '🙇'; filterText = ':bow:' }; +{ word = ':see_no_evil:'; label = '🙈 :see_no_evil:'; insertText = '🙈'; filterText = ':see_no_evil:' }; +{ word = ':hear_no_evil:'; label = '🙉 :hear_no_evil:'; insertText = '🙉'; filterText = ':hear_no_evil:' }; +{ word = ':speak_no_evil:'; label = '🙊 :speak_no_evil:'; insertText = '🙊'; filterText = ':speak_no_evil:' }; +{ word = ':raising_hand:'; label = '🙋 :raising_hand:'; insertText = '🙋'; filterText = ':raising_hand:' }; +{ word = ':raised_hands:'; label = '🙌 :raised_hands:'; insertText = '🙌'; filterText = ':raised_hands:' }; +{ word = ':person_frowning:'; label = '🙍 :person_frowning:'; insertText = '🙍'; filterText = ':person_frowning:' }; +{ word = ':person_with_pouting_face:'; label = '🙎 :person_with_pouting_face:'; insertText = '🙎'; filterText = ':person_with_pouting_face:' }; +{ word = ':pray:'; label = '🙏 :pray:'; insertText = '🙏'; filterText = ':pray:' }; +{ word = ':rocket:'; label = '🚀 :rocket:'; insertText = '🚀'; filterText = ':rocket:' }; +{ word = ':helicopter:'; label = '🚁 :helicopter:'; insertText = '🚁'; filterText = ':helicopter:' }; +{ word = ':steam_locomotive:'; label = '🚂 :steam_locomotive:'; insertText = '🚂'; filterText = ':steam_locomotive:' }; +{ word = ':railway_car:'; label = '🚃 :railway_car:'; insertText = '🚃'; filterText = ':railway_car:' }; +{ word = ':bullettrain_side:'; label = '🚄 :bullettrain_side:'; insertText = '🚄'; filterText = ':bullettrain_side:' }; +{ word = ':bullettrain_front:'; label = '🚅 :bullettrain_front:'; insertText = '🚅'; filterText = ':bullettrain_front:' }; +{ word = ':train2:'; label = '🚆 :train2:'; insertText = '🚆'; filterText = ':train2:' }; +{ word = ':metro:'; label = '🚇 :metro:'; insertText = '🚇'; filterText = ':metro:' }; +{ word = ':light_rail:'; label = '🚈 :light_rail:'; insertText = '🚈'; filterText = ':light_rail:' }; +{ word = ':station:'; label = '🚉 :station:'; insertText = '🚉'; filterText = ':station:' }; +{ word = ':tram:'; label = '🚊 :tram:'; insertText = '🚊'; filterText = ':tram:' }; +{ word = ':train:'; label = '🚋 :train:'; insertText = '🚋'; filterText = ':train:' }; +{ word = ':bus:'; label = '🚌 :bus:'; insertText = '🚌'; filterText = ':bus:' }; +{ word = ':oncoming_bus:'; label = '🚍 :oncoming_bus:'; insertText = '🚍'; filterText = ':oncoming_bus:' }; +{ word = ':trolleybus:'; label = '🚎 :trolleybus:'; insertText = '🚎'; filterText = ':trolleybus:' }; +{ word = ':busstop:'; label = '🚏 :busstop:'; insertText = '🚏'; filterText = ':busstop:' }; +{ word = ':minibus:'; label = '🚐 :minibus:'; insertText = '🚐'; filterText = ':minibus:' }; +{ word = ':ambulance:'; label = '🚑 :ambulance:'; insertText = '🚑'; filterText = ':ambulance:' }; +{ word = ':fire_engine:'; label = '🚒 :fire_engine:'; insertText = '🚒'; filterText = ':fire_engine:' }; +{ word = ':police_car:'; label = '🚓 :police_car:'; insertText = '🚓'; filterText = ':police_car:' }; +{ word = ':oncoming_police_car:'; label = '🚔 :oncoming_police_car:'; insertText = '🚔'; filterText = ':oncoming_police_car:' }; +{ word = ':taxi:'; label = '🚕 :taxi:'; insertText = '🚕'; filterText = ':taxi:' }; +{ word = ':oncoming_taxi:'; label = '🚖 :oncoming_taxi:'; insertText = '🚖'; filterText = ':oncoming_taxi:' }; +{ word = ':car:'; label = '🚗 :car:'; insertText = '🚗'; filterText = ':car:' }; +{ word = ':red_car:'; label = '🚗 :red_car:'; insertText = '🚗'; filterText = ':red_car:' }; +{ word = ':oncoming_automobile:'; label = '🚘 :oncoming_automobile:'; insertText = '🚘'; filterText = ':oncoming_automobile:' }; +{ word = ':blue_car:'; label = '🚙 :blue_car:'; insertText = '🚙'; filterText = ':blue_car:' }; +{ word = ':truck:'; label = '🚚 :truck:'; insertText = '🚚'; filterText = ':truck:' }; +{ word = ':articulated_lorry:'; label = '🚛 :articulated_lorry:'; insertText = '🚛'; filterText = ':articulated_lorry:' }; +{ word = ':tractor:'; label = '🚜 :tractor:'; insertText = '🚜'; filterText = ':tractor:' }; +{ word = ':monorail:'; label = '🚝 :monorail:'; insertText = '🚝'; filterText = ':monorail:' }; +{ word = ':mountain_railway:'; label = '🚞 :mountain_railway:'; insertText = '🚞'; filterText = ':mountain_railway:' }; +{ word = ':suspension_railway:'; label = '🚟 :suspension_railway:'; insertText = '🚟'; filterText = ':suspension_railway:' }; +{ word = ':mountain_cableway:'; label = '🚠 :mountain_cableway:'; insertText = '🚠'; filterText = ':mountain_cableway:' }; +{ word = ':aerial_tramway:'; label = '🚡 :aerial_tramway:'; insertText = '🚡'; filterText = ':aerial_tramway:' }; +{ word = ':ship:'; label = '🚢 :ship:'; insertText = '🚢'; filterText = ':ship:' }; +{ word = ':rowboat:'; label = '🚣 :rowboat:'; insertText = '🚣'; filterText = ':rowboat:' }; +{ word = ':speedboat:'; label = '🚤 :speedboat:'; insertText = '🚤'; filterText = ':speedboat:' }; +{ word = ':traffic_light:'; label = '🚥 :traffic_light:'; insertText = '🚥'; filterText = ':traffic_light:' }; +{ word = ':vertical_traffic_light:'; label = '🚦 :vertical_traffic_light:'; insertText = '🚦'; filterText = ':vertical_traffic_light:' }; +{ word = ':construction:'; label = '🚧 :construction:'; insertText = '🚧'; filterText = ':construction:' }; +{ word = ':rotating_light:'; label = '🚨 :rotating_light:'; insertText = '🚨'; filterText = ':rotating_light:' }; +{ word = ':triangular_flag_on_post:'; label = '🚩 :triangular_flag_on_post:'; insertText = '🚩'; filterText = ':triangular_flag_on_post:' }; +{ word = ':door:'; label = '🚪 :door:'; insertText = '🚪'; filterText = ':door:' }; +{ word = ':no_entry_sign:'; label = '🚫 :no_entry_sign:'; insertText = '🚫'; filterText = ':no_entry_sign:' }; +{ word = ':smoking:'; label = '🚬 :smoking:'; insertText = '🚬'; filterText = ':smoking:' }; +{ word = ':no_smoking:'; label = '🚭 :no_smoking:'; insertText = '🚭'; filterText = ':no_smoking:' }; +{ word = ':put_litter_in_its_place:'; label = '🚮 :put_litter_in_its_place:'; insertText = '🚮'; filterText = ':put_litter_in_its_place:' }; +{ word = ':do_not_litter:'; label = '🚯 :do_not_litter:'; insertText = '🚯'; filterText = ':do_not_litter:' }; +{ word = ':potable_water:'; label = '🚰 :potable_water:'; insertText = '🚰'; filterText = ':potable_water:' }; +{ word = ':non-potable_water:'; label = '🚱 :non-potable_water:'; insertText = '🚱'; filterText = ':non-potable_water:' }; +{ word = ':bike:'; label = '🚲 :bike:'; insertText = '🚲'; filterText = ':bike:' }; +{ word = ':no_bicycles:'; label = '🚳 :no_bicycles:'; insertText = '🚳'; filterText = ':no_bicycles:' }; +{ word = ':bicyclist:'; label = '🚴 :bicyclist:'; insertText = '🚴'; filterText = ':bicyclist:' }; +{ word = ':mountain_bicyclist:'; label = '🚵 :mountain_bicyclist:'; insertText = '🚵'; filterText = ':mountain_bicyclist:' }; +{ word = ':walking:'; label = '🚶 :walking:'; insertText = '🚶'; filterText = ':walking:' }; +{ word = ':no_pedestrians:'; label = '🚷 :no_pedestrians:'; insertText = '🚷'; filterText = ':no_pedestrians:' }; +{ word = ':children_crossing:'; label = '🚸 :children_crossing:'; insertText = '🚸'; filterText = ':children_crossing:' }; +{ word = ':mens:'; label = '🚹 :mens:'; insertText = '🚹'; filterText = ':mens:' }; +{ word = ':womens:'; label = '🚺 :womens:'; insertText = '🚺'; filterText = ':womens:' }; +{ word = ':restroom:'; label = '🚻 :restroom:'; insertText = '🚻'; filterText = ':restroom:' }; +{ word = ':baby_symbol:'; label = '🚼 :baby_symbol:'; insertText = '🚼'; filterText = ':baby_symbol:' }; +{ word = ':toilet:'; label = '🚽 :toilet:'; insertText = '🚽'; filterText = ':toilet:' }; +{ word = ':wc:'; label = '🚾 :wc:'; insertText = '🚾'; filterText = ':wc:' }; +{ word = ':shower:'; label = '🚿 :shower:'; insertText = '🚿'; filterText = ':shower:' }; +{ word = ':bath:'; label = '🛀 :bath:'; insertText = '🛀'; filterText = ':bath:' }; +{ word = ':bathtub:'; label = '🛁 :bathtub:'; insertText = '🛁'; filterText = ':bathtub:' }; +{ word = ':passport_control:'; label = '🛂 :passport_control:'; insertText = '🛂'; filterText = ':passport_control:' }; +{ word = ':customs:'; label = '🛃 :customs:'; insertText = '🛃'; filterText = ':customs:' }; +{ word = ':baggage_claim:'; label = '🛄 :baggage_claim:'; insertText = '🛄'; filterText = ':baggage_claim:' }; +{ word = ':left_luggage:'; label = '🛅 :left_luggage:'; insertText = '🛅'; filterText = ':left_luggage:' }; +{ word = ':couch_and_lamp:'; label = '🛋️ :couch_and_lamp:'; insertText = '🛋️'; filterText = ':couch_and_lamp:' }; +{ word = ':sleeping_accommodation:'; label = '🛌 :sleeping_accommodation:'; insertText = '🛌'; filterText = ':sleeping_accommodation:' }; +{ word = ':shopping_bags:'; label = '🛍️ :shopping_bags:'; insertText = '🛍️'; filterText = ':shopping_bags:' }; +{ word = ':bellhop_bell:'; label = '🛎️ :bellhop_bell:'; insertText = '🛎️'; filterText = ':bellhop_bell:' }; +{ word = ':bed:'; label = '🛏️ :bed:'; insertText = '🛏️'; filterText = ':bed:' }; +{ word = ':place_of_worship:'; label = '🛐 :place_of_worship:'; insertText = '🛐'; filterText = ':place_of_worship:' }; +{ word = ':octagonal_sign:'; label = '🛑 :octagonal_sign:'; insertText = '🛑'; filterText = ':octagonal_sign:' }; +{ word = ':shopping_trolley:'; label = '🛒 :shopping_trolley:'; insertText = '🛒'; filterText = ':shopping_trolley:' }; +{ word = ':hindu_temple:'; label = '🛕 :hindu_temple:'; insertText = '🛕'; filterText = ':hindu_temple:' }; +{ word = ':hut:'; label = '🛖 :hut:'; insertText = '🛖'; filterText = ':hut:' }; +{ word = ':elevator:'; label = '🛗 :elevator:'; insertText = '🛗'; filterText = ':elevator:' }; +{ word = ':wireless:'; label = '🛜 :wireless:'; insertText = '🛜'; filterText = ':wireless:' }; +{ word = ':playground_slide:'; label = '🛝 :playground_slide:'; insertText = '🛝'; filterText = ':playground_slide:' }; +{ word = ':wheel:'; label = '🛞 :wheel:'; insertText = '🛞'; filterText = ':wheel:' }; +{ word = ':ring_buoy:'; label = '🛟 :ring_buoy:'; insertText = '🛟'; filterText = ':ring_buoy:' }; +{ word = ':hammer_and_wrench:'; label = '🛠️ :hammer_and_wrench:'; insertText = '🛠️'; filterText = ':hammer_and_wrench:' }; +{ word = ':shield:'; label = '🛡️ :shield:'; insertText = '🛡️'; filterText = ':shield:' }; +{ word = ':oil_drum:'; label = '🛢️ :oil_drum:'; insertText = '🛢️'; filterText = ':oil_drum:' }; +{ word = ':motorway:'; label = '🛣️ :motorway:'; insertText = '🛣️'; filterText = ':motorway:' }; +{ word = ':railway_track:'; label = '🛤️ :railway_track:'; insertText = '🛤️'; filterText = ':railway_track:' }; +{ word = ':motor_boat:'; label = '🛥️ :motor_boat:'; insertText = '🛥️'; filterText = ':motor_boat:' }; +{ word = ':small_airplane:'; label = '🛩️ :small_airplane:'; insertText = '🛩️'; filterText = ':small_airplane:' }; +{ word = ':airplane_departure:'; label = '🛫 :airplane_departure:'; insertText = '🛫'; filterText = ':airplane_departure:' }; +{ word = ':airplane_arriving:'; label = '🛬 :airplane_arriving:'; insertText = '🛬'; filterText = ':airplane_arriving:' }; +{ word = ':satellite:'; label = '🛰️ :satellite:'; insertText = '🛰️'; filterText = ':satellite:' }; +{ word = ':passenger_ship:'; label = '🛳️ :passenger_ship:'; insertText = '🛳️'; filterText = ':passenger_ship:' }; +{ word = ':scooter:'; label = '🛴 :scooter:'; insertText = '🛴'; filterText = ':scooter:' }; +{ word = ':motor_scooter:'; label = '🛵 :motor_scooter:'; insertText = '🛵'; filterText = ':motor_scooter:' }; +{ word = ':canoe:'; label = '🛶 :canoe:'; insertText = '🛶'; filterText = ':canoe:' }; +{ word = ':sled:'; label = '🛷 :sled:'; insertText = '🛷'; filterText = ':sled:' }; +{ word = ':flying_saucer:'; label = '🛸 :flying_saucer:'; insertText = '🛸'; filterText = ':flying_saucer:' }; +{ word = ':skateboard:'; label = '🛹 :skateboard:'; insertText = '🛹'; filterText = ':skateboard:' }; +{ word = ':auto_rickshaw:'; label = '🛺 :auto_rickshaw:'; insertText = '🛺'; filterText = ':auto_rickshaw:' }; +{ word = ':pickup_truck:'; label = '🛻 :pickup_truck:'; insertText = '🛻'; filterText = ':pickup_truck:' }; +{ word = ':roller_skate:'; label = '🛼 :roller_skate:'; insertText = '🛼'; filterText = ':roller_skate:' }; +{ word = ':large_orange_circle:'; label = '🟠 :large_orange_circle:'; insertText = '🟠'; filterText = ':large_orange_circle:' }; +{ word = ':large_yellow_circle:'; label = '🟡 :large_yellow_circle:'; insertText = '🟡'; filterText = ':large_yellow_circle:' }; +{ word = ':large_green_circle:'; label = '🟢 :large_green_circle:'; insertText = '🟢'; filterText = ':large_green_circle:' }; +{ word = ':large_purple_circle:'; label = '🟣 :large_purple_circle:'; insertText = '🟣'; filterText = ':large_purple_circle:' }; +{ word = ':large_brown_circle:'; label = '🟤 :large_brown_circle:'; insertText = '🟤'; filterText = ':large_brown_circle:' }; +{ word = ':large_red_square:'; label = '🟥 :large_red_square:'; insertText = '🟥'; filterText = ':large_red_square:' }; +{ word = ':large_blue_square:'; label = '🟦 :large_blue_square:'; insertText = '🟦'; filterText = ':large_blue_square:' }; +{ word = ':large_orange_square:'; label = '🟧 :large_orange_square:'; insertText = '🟧'; filterText = ':large_orange_square:' }; +{ word = ':large_yellow_square:'; label = '🟨 :large_yellow_square:'; insertText = '🟨'; filterText = ':large_yellow_square:' }; +{ word = ':large_green_square:'; label = '🟩 :large_green_square:'; insertText = '🟩'; filterText = ':large_green_square:' }; +{ word = ':large_purple_square:'; label = '🟪 :large_purple_square:'; insertText = '🟪'; filterText = ':large_purple_square:' }; +{ word = ':large_brown_square:'; label = '🟫 :large_brown_square:'; insertText = '🟫'; filterText = ':large_brown_square:' }; +{ word = ':heavy_equals_sign:'; label = '🟰 :heavy_equals_sign:'; insertText = '🟰'; filterText = ':heavy_equals_sign:' }; +{ word = ':pinched_fingers:'; label = '🤌 :pinched_fingers:'; insertText = '🤌'; filterText = ':pinched_fingers:' }; +{ word = ':white_heart:'; label = '🤍 :white_heart:'; insertText = '🤍'; filterText = ':white_heart:' }; +{ word = ':brown_heart:'; label = '🤎 :brown_heart:'; insertText = '🤎'; filterText = ':brown_heart:' }; +{ word = ':pinching_hand:'; label = '🤏 :pinching_hand:'; insertText = '🤏'; filterText = ':pinching_hand:' }; +{ word = ':zipper_mouth_face:'; label = '🤐 :zipper_mouth_face:'; insertText = '🤐'; filterText = ':zipper_mouth_face:' }; +{ word = ':money_mouth_face:'; label = '🤑 :money_mouth_face:'; insertText = '🤑'; filterText = ':money_mouth_face:' }; +{ word = ':face_with_thermometer:'; label = '🤒 :face_with_thermometer:'; insertText = '🤒'; filterText = ':face_with_thermometer:' }; +{ word = ':nerd_face:'; label = '🤓 :nerd_face:'; insertText = '🤓'; filterText = ':nerd_face:' }; +{ word = ':thinking_face:'; label = '🤔 :thinking_face:'; insertText = '🤔'; filterText = ':thinking_face:' }; +{ word = ':face_with_head_bandage:'; label = '🤕 :face_with_head_bandage:'; insertText = '🤕'; filterText = ':face_with_head_bandage:' }; +{ word = ':robot_face:'; label = '🤖 :robot_face:'; insertText = '🤖'; filterText = ':robot_face:' }; +{ word = ':hugging_face:'; label = '🤗 :hugging_face:'; insertText = '🤗'; filterText = ':hugging_face:' }; +{ word = ':the_horns:'; label = '🤘 :the_horns:'; insertText = '🤘'; filterText = ':the_horns:' }; +{ word = ':sign_of_the_horns:'; label = '🤘 :sign_of_the_horns:'; insertText = '🤘'; filterText = ':sign_of_the_horns:' }; +{ word = ':call_me_hand:'; label = '🤙 :call_me_hand:'; insertText = '🤙'; filterText = ':call_me_hand:' }; +{ word = ':raised_back_of_hand:'; label = '🤚 :raised_back_of_hand:'; insertText = '🤚'; filterText = ':raised_back_of_hand:' }; +{ word = ':left-facing_fist:'; label = '🤛 :left-facing_fist:'; insertText = '🤛'; filterText = ':left-facing_fist:' }; +{ word = ':right-facing_fist:'; label = '🤜 :right-facing_fist:'; insertText = '🤜'; filterText = ':right-facing_fist:' }; +{ word = ':handshake:'; label = '🤝 :handshake:'; insertText = '🤝'; filterText = ':handshake:' }; +{ word = ':crossed_fingers:'; label = '🤞 :crossed_fingers:'; insertText = '🤞'; filterText = ':crossed_fingers:' }; +{ word = ':hand_with_index_and_middle_fingers_crossed:'; label = '🤞 :hand_with_index_and_middle_fingers_crossed:'; insertText = '🤞'; filterText = ':hand_with_index_and_middle_fingers_crossed:' }; +{ word = ':i_love_you_hand_sign:'; label = '🤟 :i_love_you_hand_sign:'; insertText = '🤟'; filterText = ':i_love_you_hand_sign:' }; +{ word = ':face_with_cowboy_hat:'; label = '🤠 :face_with_cowboy_hat:'; insertText = '🤠'; filterText = ':face_with_cowboy_hat:' }; +{ word = ':clown_face:'; label = '🤡 :clown_face:'; insertText = '🤡'; filterText = ':clown_face:' }; +{ word = ':nauseated_face:'; label = '🤢 :nauseated_face:'; insertText = '🤢'; filterText = ':nauseated_face:' }; +{ word = ':rolling_on_the_floor_laughing:'; label = '🤣 :rolling_on_the_floor_laughing:'; insertText = '🤣'; filterText = ':rolling_on_the_floor_laughing:' }; +{ word = ':drooling_face:'; label = '🤤 :drooling_face:'; insertText = '🤤'; filterText = ':drooling_face:' }; +{ word = ':lying_face:'; label = '🤥 :lying_face:'; insertText = '🤥'; filterText = ':lying_face:' }; +{ word = ':face_palm:'; label = '🤦 :face_palm:'; insertText = '🤦'; filterText = ':face_palm:' }; +{ word = ':sneezing_face:'; label = '🤧 :sneezing_face:'; insertText = '🤧'; filterText = ':sneezing_face:' }; +{ word = ':face_with_raised_eyebrow:'; label = '🤨 :face_with_raised_eyebrow:'; insertText = '🤨'; filterText = ':face_with_raised_eyebrow:' }; +{ word = ':face_with_one_eyebrow_raised:'; label = '🤨 :face_with_one_eyebrow_raised:'; insertText = '🤨'; filterText = ':face_with_one_eyebrow_raised:' }; +{ word = ':star-struck:'; label = '🤩 :star-struck:'; insertText = '🤩'; filterText = ':star-struck:' }; +{ word = ':grinning_face_with_star_eyes:'; label = '🤩 :grinning_face_with_star_eyes:'; insertText = '🤩'; filterText = ':grinning_face_with_star_eyes:' }; +{ word = ':zany_face:'; label = '🤪 :zany_face:'; insertText = '🤪'; filterText = ':zany_face:' }; +{ word = ':grinning_face_with_one_large_and_one_small_eye:'; label = '🤪 :grinning_face_with_one_large_and_one_small_eye:'; insertText = '🤪'; filterText = ':grinning_face_with_one_large_and_one_small_eye:' }; +{ word = ':shushing_face:'; label = '🤫 :shushing_face:'; insertText = '🤫'; filterText = ':shushing_face:' }; +{ word = ':face_with_finger_covering_closed_lips:'; label = '🤫 :face_with_finger_covering_closed_lips:'; insertText = '🤫'; filterText = ':face_with_finger_covering_closed_lips:' }; +{ word = ':face_with_symbols_on_mouth:'; label = '🤬 :face_with_symbols_on_mouth:'; insertText = '🤬'; filterText = ':face_with_symbols_on_mouth:' }; +{ word = ':serious_face_with_symbols_covering_mouth:'; label = '🤬 :serious_face_with_symbols_covering_mouth:'; insertText = '🤬'; filterText = ':serious_face_with_symbols_covering_mouth:' }; +{ word = ':face_with_hand_over_mouth:'; label = '🤭 :face_with_hand_over_mouth:'; insertText = '🤭'; filterText = ':face_with_hand_over_mouth:' }; +{ word = ':smiling_face_with_smiling_eyes_and_hand_covering_mouth:'; label = '🤭 :smiling_face_with_smiling_eyes_and_hand_covering_mouth:'; insertText = '🤭'; filterText = ':smiling_face_with_smiling_eyes_and_hand_covering_mouth:' }; +{ word = ':face_vomiting:'; label = '🤮 :face_vomiting:'; insertText = '🤮'; filterText = ':face_vomiting:' }; +{ word = ':face_with_open_mouth_vomiting:'; label = '🤮 :face_with_open_mouth_vomiting:'; insertText = '🤮'; filterText = ':face_with_open_mouth_vomiting:' }; +{ word = ':exploding_head:'; label = '🤯 :exploding_head:'; insertText = '🤯'; filterText = ':exploding_head:' }; +{ word = ':shocked_face_with_exploding_head:'; label = '🤯 :shocked_face_with_exploding_head:'; insertText = '🤯'; filterText = ':shocked_face_with_exploding_head:' }; +{ word = ':pregnant_woman:'; label = '🤰 :pregnant_woman:'; insertText = '🤰'; filterText = ':pregnant_woman:' }; +{ word = ':breast-feeding:'; label = '🤱 :breast-feeding:'; insertText = '🤱'; filterText = ':breast-feeding:' }; +{ word = ':palms_up_together:'; label = '🤲 :palms_up_together:'; insertText = '🤲'; filterText = ':palms_up_together:' }; +{ word = ':selfie:'; label = '🤳 :selfie:'; insertText = '🤳'; filterText = ':selfie:' }; +{ word = ':prince:'; label = '🤴 :prince:'; insertText = '🤴'; filterText = ':prince:' }; +{ word = ':person_in_tuxedo:'; label = '🤵 :person_in_tuxedo:'; insertText = '🤵'; filterText = ':person_in_tuxedo:' }; +{ word = ':mrs_claus:'; label = '🤶 :mrs_claus:'; insertText = '🤶'; filterText = ':mrs_claus:' }; +{ word = ':mother_christmas:'; label = '🤶 :mother_christmas:'; insertText = '🤶'; filterText = ':mother_christmas:' }; +{ word = ':shrug:'; label = '🤷 :shrug:'; insertText = '🤷'; filterText = ':shrug:' }; +{ word = ':person_doing_cartwheel:'; label = '🤸 :person_doing_cartwheel:'; insertText = '🤸'; filterText = ':person_doing_cartwheel:' }; +{ word = ':juggling:'; label = '🤹 :juggling:'; insertText = '🤹'; filterText = ':juggling:' }; +{ word = ':fencer:'; label = '🤺 :fencer:'; insertText = '🤺'; filterText = ':fencer:' }; +{ word = ':wrestlers:'; label = '🤼 :wrestlers:'; insertText = '🤼'; filterText = ':wrestlers:' }; +{ word = ':water_polo:'; label = '🤽 :water_polo:'; insertText = '🤽'; filterText = ':water_polo:' }; +{ word = ':handball:'; label = '🤾 :handball:'; insertText = '🤾'; filterText = ':handball:' }; +{ word = ':diving_mask:'; label = '🤿 :diving_mask:'; insertText = '🤿'; filterText = ':diving_mask:' }; +{ word = ':wilted_flower:'; label = '🥀 :wilted_flower:'; insertText = '🥀'; filterText = ':wilted_flower:' }; +{ word = ':drum_with_drumsticks:'; label = '🥁 :drum_with_drumsticks:'; insertText = '🥁'; filterText = ':drum_with_drumsticks:' }; +{ word = ':clinking_glasses:'; label = '🥂 :clinking_glasses:'; insertText = '🥂'; filterText = ':clinking_glasses:' }; +{ word = ':tumbler_glass:'; label = '🥃 :tumbler_glass:'; insertText = '🥃'; filterText = ':tumbler_glass:' }; +{ word = ':spoon:'; label = '🥄 :spoon:'; insertText = '🥄'; filterText = ':spoon:' }; +{ word = ':goal_net:'; label = '🥅 :goal_net:'; insertText = '🥅'; filterText = ':goal_net:' }; +{ word = ':first_place_medal:'; label = '🥇 :first_place_medal:'; insertText = '🥇'; filterText = ':first_place_medal:' }; +{ word = ':second_place_medal:'; label = '🥈 :second_place_medal:'; insertText = '🥈'; filterText = ':second_place_medal:' }; +{ word = ':third_place_medal:'; label = '🥉 :third_place_medal:'; insertText = '🥉'; filterText = ':third_place_medal:' }; +{ word = ':boxing_glove:'; label = '🥊 :boxing_glove:'; insertText = '🥊'; filterText = ':boxing_glove:' }; +{ word = ':martial_arts_uniform:'; label = '🥋 :martial_arts_uniform:'; insertText = '🥋'; filterText = ':martial_arts_uniform:' }; +{ word = ':curling_stone:'; label = '🥌 :curling_stone:'; insertText = '🥌'; filterText = ':curling_stone:' }; +{ word = ':lacrosse:'; label = '🥍 :lacrosse:'; insertText = '🥍'; filterText = ':lacrosse:' }; +{ word = ':softball:'; label = '🥎 :softball:'; insertText = '🥎'; filterText = ':softball:' }; +{ word = ':flying_disc:'; label = '🥏 :flying_disc:'; insertText = '🥏'; filterText = ':flying_disc:' }; +{ word = ':croissant:'; label = '🥐 :croissant:'; insertText = '🥐'; filterText = ':croissant:' }; +{ word = ':avocado:'; label = '🥑 :avocado:'; insertText = '🥑'; filterText = ':avocado:' }; +{ word = ':cucumber:'; label = '🥒 :cucumber:'; insertText = '🥒'; filterText = ':cucumber:' }; +{ word = ':bacon:'; label = '🥓 :bacon:'; insertText = '🥓'; filterText = ':bacon:' }; +{ word = ':potato:'; label = '🥔 :potato:'; insertText = '🥔'; filterText = ':potato:' }; +{ word = ':carrot:'; label = '🥕 :carrot:'; insertText = '🥕'; filterText = ':carrot:' }; +{ word = ':baguette_bread:'; label = '🥖 :baguette_bread:'; insertText = '🥖'; filterText = ':baguette_bread:' }; +{ word = ':green_salad:'; label = '🥗 :green_salad:'; insertText = '🥗'; filterText = ':green_salad:' }; +{ word = ':shallow_pan_of_food:'; label = '🥘 :shallow_pan_of_food:'; insertText = '🥘'; filterText = ':shallow_pan_of_food:' }; +{ word = ':stuffed_flatbread:'; label = '🥙 :stuffed_flatbread:'; insertText = '🥙'; filterText = ':stuffed_flatbread:' }; +{ word = ':egg:'; label = '🥚 :egg:'; insertText = '🥚'; filterText = ':egg:' }; +{ word = ':glass_of_milk:'; label = '🥛 :glass_of_milk:'; insertText = '🥛'; filterText = ':glass_of_milk:' }; +{ word = ':peanuts:'; label = '🥜 :peanuts:'; insertText = '🥜'; filterText = ':peanuts:' }; +{ word = ':kiwifruit:'; label = '🥝 :kiwifruit:'; insertText = '🥝'; filterText = ':kiwifruit:' }; +{ word = ':pancakes:'; label = '🥞 :pancakes:'; insertText = '🥞'; filterText = ':pancakes:' }; +{ word = ':dumpling:'; label = '🥟 :dumpling:'; insertText = '🥟'; filterText = ':dumpling:' }; +{ word = ':fortune_cookie:'; label = '🥠 :fortune_cookie:'; insertText = '🥠'; filterText = ':fortune_cookie:' }; +{ word = ':takeout_box:'; label = '🥡 :takeout_box:'; insertText = '🥡'; filterText = ':takeout_box:' }; +{ word = ':chopsticks:'; label = '🥢 :chopsticks:'; insertText = '🥢'; filterText = ':chopsticks:' }; +{ word = ':bowl_with_spoon:'; label = '🥣 :bowl_with_spoon:'; insertText = '🥣'; filterText = ':bowl_with_spoon:' }; +{ word = ':cup_with_straw:'; label = '🥤 :cup_with_straw:'; insertText = '🥤'; filterText = ':cup_with_straw:' }; +{ word = ':coconut:'; label = '🥥 :coconut:'; insertText = '🥥'; filterText = ':coconut:' }; +{ word = ':broccoli:'; label = '🥦 :broccoli:'; insertText = '🥦'; filterText = ':broccoli:' }; +{ word = ':pie:'; label = '🥧 :pie:'; insertText = '🥧'; filterText = ':pie:' }; +{ word = ':pretzel:'; label = '🥨 :pretzel:'; insertText = '🥨'; filterText = ':pretzel:' }; +{ word = ':cut_of_meat:'; label = '🥩 :cut_of_meat:'; insertText = '🥩'; filterText = ':cut_of_meat:' }; +{ word = ':sandwich:'; label = '🥪 :sandwich:'; insertText = '🥪'; filterText = ':sandwich:' }; +{ word = ':canned_food:'; label = '🥫 :canned_food:'; insertText = '🥫'; filterText = ':canned_food:' }; +{ word = ':leafy_green:'; label = '🥬 :leafy_green:'; insertText = '🥬'; filterText = ':leafy_green:' }; +{ word = ':mango:'; label = '🥭 :mango:'; insertText = '🥭'; filterText = ':mango:' }; +{ word = ':moon_cake:'; label = '🥮 :moon_cake:'; insertText = '🥮'; filterText = ':moon_cake:' }; +{ word = ':bagel:'; label = '🥯 :bagel:'; insertText = '🥯'; filterText = ':bagel:' }; +{ word = ':smiling_face_with_3_hearts:'; label = '🥰 :smiling_face_with_3_hearts:'; insertText = '🥰'; filterText = ':smiling_face_with_3_hearts:' }; +{ word = ':yawning_face:'; label = '🥱 :yawning_face:'; insertText = '🥱'; filterText = ':yawning_face:' }; +{ word = ':smiling_face_with_tear:'; label = '🥲 :smiling_face_with_tear:'; insertText = '🥲'; filterText = ':smiling_face_with_tear:' }; +{ word = ':partying_face:'; label = '🥳 :partying_face:'; insertText = '🥳'; filterText = ':partying_face:' }; +{ word = ':woozy_face:'; label = '🥴 :woozy_face:'; insertText = '🥴'; filterText = ':woozy_face:' }; +{ word = ':hot_face:'; label = '🥵 :hot_face:'; insertText = '🥵'; filterText = ':hot_face:' }; +{ word = ':cold_face:'; label = '🥶 :cold_face:'; insertText = '🥶'; filterText = ':cold_face:' }; +{ word = ':ninja:'; label = '🥷 :ninja:'; insertText = '🥷'; filterText = ':ninja:' }; +{ word = ':disguised_face:'; label = '🥸 :disguised_face:'; insertText = '🥸'; filterText = ':disguised_face:' }; +{ word = ':face_holding_back_tears:'; label = '🥹 :face_holding_back_tears:'; insertText = '🥹'; filterText = ':face_holding_back_tears:' }; +{ word = ':pleading_face:'; label = '🥺 :pleading_face:'; insertText = '🥺'; filterText = ':pleading_face:' }; +{ word = ':sari:'; label = '🥻 :sari:'; insertText = '🥻'; filterText = ':sari:' }; +{ word = ':lab_coat:'; label = '🥼 :lab_coat:'; insertText = '🥼'; filterText = ':lab_coat:' }; +{ word = ':goggles:'; label = '🥽 :goggles:'; insertText = '🥽'; filterText = ':goggles:' }; +{ word = ':hiking_boot:'; label = '🥾 :hiking_boot:'; insertText = '🥾'; filterText = ':hiking_boot:' }; +{ word = ':womans_flat_shoe:'; label = '🥿 :womans_flat_shoe:'; insertText = '🥿'; filterText = ':womans_flat_shoe:' }; +{ word = ':crab:'; label = '🦀 :crab:'; insertText = '🦀'; filterText = ':crab:' }; +{ word = ':lion_face:'; label = '🦁 :lion_face:'; insertText = '🦁'; filterText = ':lion_face:' }; +{ word = ':scorpion:'; label = '🦂 :scorpion:'; insertText = '🦂'; filterText = ':scorpion:' }; +{ word = ':turkey:'; label = '🦃 :turkey:'; insertText = '🦃'; filterText = ':turkey:' }; +{ word = ':unicorn_face:'; label = '🦄 :unicorn_face:'; insertText = '🦄'; filterText = ':unicorn_face:' }; +{ word = ':eagle:'; label = '🦅 :eagle:'; insertText = '🦅'; filterText = ':eagle:' }; +{ word = ':duck:'; label = '🦆 :duck:'; insertText = '🦆'; filterText = ':duck:' }; +{ word = ':bat:'; label = '🦇 :bat:'; insertText = '🦇'; filterText = ':bat:' }; +{ word = ':shark:'; label = '🦈 :shark:'; insertText = '🦈'; filterText = ':shark:' }; +{ word = ':owl:'; label = '🦉 :owl:'; insertText = '🦉'; filterText = ':owl:' }; +{ word = ':fox_face:'; label = '🦊 :fox_face:'; insertText = '🦊'; filterText = ':fox_face:' }; +{ word = ':butterfly:'; label = '🦋 :butterfly:'; insertText = '🦋'; filterText = ':butterfly:' }; +{ word = ':deer:'; label = '🦌 :deer:'; insertText = '🦌'; filterText = ':deer:' }; +{ word = ':gorilla:'; label = '🦍 :gorilla:'; insertText = '🦍'; filterText = ':gorilla:' }; +{ word = ':lizard:'; label = '🦎 :lizard:'; insertText = '🦎'; filterText = ':lizard:' }; +{ word = ':rhinoceros:'; label = '🦏 :rhinoceros:'; insertText = '🦏'; filterText = ':rhinoceros:' }; +{ word = ':shrimp:'; label = '🦐 :shrimp:'; insertText = '🦐'; filterText = ':shrimp:' }; +{ word = ':squid:'; label = '🦑 :squid:'; insertText = '🦑'; filterText = ':squid:' }; +{ word = ':giraffe_face:'; label = '🦒 :giraffe_face:'; insertText = '🦒'; filterText = ':giraffe_face:' }; +{ word = ':zebra_face:'; label = '🦓 :zebra_face:'; insertText = '🦓'; filterText = ':zebra_face:' }; +{ word = ':hedgehog:'; label = '🦔 :hedgehog:'; insertText = '🦔'; filterText = ':hedgehog:' }; +{ word = ':sauropod:'; label = '🦕 :sauropod:'; insertText = '🦕'; filterText = ':sauropod:' }; +{ word = ':t-rex:'; label = '🦖 :t-rex:'; insertText = '🦖'; filterText = ':t-rex:' }; +{ word = ':cricket:'; label = '🦗 :cricket:'; insertText = '🦗'; filterText = ':cricket:' }; +{ word = ':kangaroo:'; label = '🦘 :kangaroo:'; insertText = '🦘'; filterText = ':kangaroo:' }; +{ word = ':llama:'; label = '🦙 :llama:'; insertText = '🦙'; filterText = ':llama:' }; +{ word = ':peacock:'; label = '🦚 :peacock:'; insertText = '🦚'; filterText = ':peacock:' }; +{ word = ':hippopotamus:'; label = '🦛 :hippopotamus:'; insertText = '🦛'; filterText = ':hippopotamus:' }; +{ word = ':parrot:'; label = '🦜 :parrot:'; insertText = '🦜'; filterText = ':parrot:' }; +{ word = ':raccoon:'; label = '🦝 :raccoon:'; insertText = '🦝'; filterText = ':raccoon:' }; +{ word = ':lobster:'; label = '🦞 :lobster:'; insertText = '🦞'; filterText = ':lobster:' }; +{ word = ':mosquito:'; label = '🦟 :mosquito:'; insertText = '🦟'; filterText = ':mosquito:' }; +{ word = ':microbe:'; label = '🦠 :microbe:'; insertText = '🦠'; filterText = ':microbe:' }; +{ word = ':badger:'; label = '🦡 :badger:'; insertText = '🦡'; filterText = ':badger:' }; +{ word = ':swan:'; label = '🦢 :swan:'; insertText = '🦢'; filterText = ':swan:' }; +{ word = ':mammoth:'; label = '🦣 :mammoth:'; insertText = '🦣'; filterText = ':mammoth:' }; +{ word = ':dodo:'; label = '🦤 :dodo:'; insertText = '🦤'; filterText = ':dodo:' }; +{ word = ':sloth:'; label = '🦥 :sloth:'; insertText = '🦥'; filterText = ':sloth:' }; +{ word = ':otter:'; label = '🦦 :otter:'; insertText = '🦦'; filterText = ':otter:' }; +{ word = ':orangutan:'; label = '🦧 :orangutan:'; insertText = '🦧'; filterText = ':orangutan:' }; +{ word = ':skunk:'; label = '🦨 :skunk:'; insertText = '🦨'; filterText = ':skunk:' }; +{ word = ':flamingo:'; label = '🦩 :flamingo:'; insertText = '🦩'; filterText = ':flamingo:' }; +{ word = ':oyster:'; label = '🦪 :oyster:'; insertText = '🦪'; filterText = ':oyster:' }; +{ word = ':beaver:'; label = '🦫 :beaver:'; insertText = '🦫'; filterText = ':beaver:' }; +{ word = ':bison:'; label = '🦬 :bison:'; insertText = '🦬'; filterText = ':bison:' }; +{ word = ':seal:'; label = '🦭 :seal:'; insertText = '🦭'; filterText = ':seal:' }; +{ word = ':guide_dog:'; label = '🦮 :guide_dog:'; insertText = '🦮'; filterText = ':guide_dog:' }; +{ word = ':probing_cane:'; label = '🦯 :probing_cane:'; insertText = '🦯'; filterText = ':probing_cane:' }; +{ word = ':bone:'; label = '🦴 :bone:'; insertText = '🦴'; filterText = ':bone:' }; +{ word = ':leg:'; label = '🦵 :leg:'; insertText = '🦵'; filterText = ':leg:' }; +{ word = ':foot:'; label = '🦶 :foot:'; insertText = '🦶'; filterText = ':foot:' }; +{ word = ':tooth:'; label = '🦷 :tooth:'; insertText = '🦷'; filterText = ':tooth:' }; +{ word = ':superhero:'; label = '🦸 :superhero:'; insertText = '🦸'; filterText = ':superhero:' }; +{ word = ':supervillain:'; label = '🦹 :supervillain:'; insertText = '🦹'; filterText = ':supervillain:' }; +{ word = ':safety_vest:'; label = '🦺 :safety_vest:'; insertText = '🦺'; filterText = ':safety_vest:' }; +{ word = ':ear_with_hearing_aid:'; label = '🦻 :ear_with_hearing_aid:'; insertText = '🦻'; filterText = ':ear_with_hearing_aid:' }; +{ word = ':motorized_wheelchair:'; label = '🦼 :motorized_wheelchair:'; insertText = '🦼'; filterText = ':motorized_wheelchair:' }; +{ word = ':manual_wheelchair:'; label = '🦽 :manual_wheelchair:'; insertText = '🦽'; filterText = ':manual_wheelchair:' }; +{ word = ':mechanical_arm:'; label = '🦾 :mechanical_arm:'; insertText = '🦾'; filterText = ':mechanical_arm:' }; +{ word = ':mechanical_leg:'; label = '🦿 :mechanical_leg:'; insertText = '🦿'; filterText = ':mechanical_leg:' }; +{ word = ':cheese_wedge:'; label = '🧀 :cheese_wedge:'; insertText = '🧀'; filterText = ':cheese_wedge:' }; +{ word = ':cupcake:'; label = '🧁 :cupcake:'; insertText = '🧁'; filterText = ':cupcake:' }; +{ word = ':salt:'; label = '🧂 :salt:'; insertText = '🧂'; filterText = ':salt:' }; +{ word = ':beverage_box:'; label = '🧃 :beverage_box:'; insertText = '🧃'; filterText = ':beverage_box:' }; +{ word = ':garlic:'; label = '🧄 :garlic:'; insertText = '🧄'; filterText = ':garlic:' }; +{ word = ':onion:'; label = '🧅 :onion:'; insertText = '🧅'; filterText = ':onion:' }; +{ word = ':falafel:'; label = '🧆 :falafel:'; insertText = '🧆'; filterText = ':falafel:' }; +{ word = ':waffle:'; label = '🧇 :waffle:'; insertText = '🧇'; filterText = ':waffle:' }; +{ word = ':butter:'; label = '🧈 :butter:'; insertText = '🧈'; filterText = ':butter:' }; +{ word = ':mate_drink:'; label = '🧉 :mate_drink:'; insertText = '🧉'; filterText = ':mate_drink:' }; +{ word = ':ice_cube:'; label = '🧊 :ice_cube:'; insertText = '🧊'; filterText = ':ice_cube:' }; +{ word = ':bubble_tea:'; label = '🧋 :bubble_tea:'; insertText = '🧋'; filterText = ':bubble_tea:' }; +{ word = ':troll:'; label = '🧌 :troll:'; insertText = '🧌'; filterText = ':troll:' }; +{ word = ':standing_person:'; label = '🧍 :standing_person:'; insertText = '🧍'; filterText = ':standing_person:' }; +{ word = ':kneeling_person:'; label = '🧎 :kneeling_person:'; insertText = '🧎'; filterText = ':kneeling_person:' }; +{ word = ':deaf_person:'; label = '🧏 :deaf_person:'; insertText = '🧏'; filterText = ':deaf_person:' }; +{ word = ':face_with_monocle:'; label = '🧐 :face_with_monocle:'; insertText = '🧐'; filterText = ':face_with_monocle:' }; +{ word = ':adult:'; label = '🧑 :adult:'; insertText = '🧑'; filterText = ':adult:' }; +{ word = ':child:'; label = '🧒 :child:'; insertText = '🧒'; filterText = ':child:' }; +{ word = ':older_adult:'; label = '🧓 :older_adult:'; insertText = '🧓'; filterText = ':older_adult:' }; +{ word = ':bearded_person:'; label = '🧔 :bearded_person:'; insertText = '🧔'; filterText = ':bearded_person:' }; +{ word = ':person_with_headscarf:'; label = '🧕 :person_with_headscarf:'; insertText = '🧕'; filterText = ':person_with_headscarf:' }; +{ word = ':person_in_steamy_room:'; label = '🧖 :person_in_steamy_room:'; insertText = '🧖'; filterText = ':person_in_steamy_room:' }; +{ word = ':person_climbing:'; label = '🧗 :person_climbing:'; insertText = '🧗'; filterText = ':person_climbing:' }; +{ word = ':person_in_lotus_position:'; label = '🧘 :person_in_lotus_position:'; insertText = '🧘'; filterText = ':person_in_lotus_position:' }; +{ word = ':mage:'; label = '🧙 :mage:'; insertText = '🧙'; filterText = ':mage:' }; +{ word = ':fairy:'; label = '🧚 :fairy:'; insertText = '🧚'; filterText = ':fairy:' }; +{ word = ':vampire:'; label = '🧛 :vampire:'; insertText = '🧛'; filterText = ':vampire:' }; +{ word = ':merperson:'; label = '🧜 :merperson:'; insertText = '🧜'; filterText = ':merperson:' }; +{ word = ':elf:'; label = '🧝 :elf:'; insertText = '🧝'; filterText = ':elf:' }; +{ word = ':genie:'; label = '🧞 :genie:'; insertText = '🧞'; filterText = ':genie:' }; +{ word = ':zombie:'; label = '🧟 :zombie:'; insertText = '🧟'; filterText = ':zombie:' }; +{ word = ':brain:'; label = '🧠 :brain:'; insertText = '🧠'; filterText = ':brain:' }; +{ word = ':orange_heart:'; label = '🧡 :orange_heart:'; insertText = '🧡'; filterText = ':orange_heart:' }; +{ word = ':billed_cap:'; label = '🧢 :billed_cap:'; insertText = '🧢'; filterText = ':billed_cap:' }; +{ word = ':scarf:'; label = '🧣 :scarf:'; insertText = '🧣'; filterText = ':scarf:' }; +{ word = ':gloves:'; label = '🧤 :gloves:'; insertText = '🧤'; filterText = ':gloves:' }; +{ word = ':coat:'; label = '🧥 :coat:'; insertText = '🧥'; filterText = ':coat:' }; +{ word = ':socks:'; label = '🧦 :socks:'; insertText = '🧦'; filterText = ':socks:' }; +{ word = ':red_envelope:'; label = '🧧 :red_envelope:'; insertText = '🧧'; filterText = ':red_envelope:' }; +{ word = ':firecracker:'; label = '🧨 :firecracker:'; insertText = '🧨'; filterText = ':firecracker:' }; +{ word = ':jigsaw:'; label = '🧩 :jigsaw:'; insertText = '🧩'; filterText = ':jigsaw:' }; +{ word = ':test_tube:'; label = '🧪 :test_tube:'; insertText = '🧪'; filterText = ':test_tube:' }; +{ word = ':petri_dish:'; label = '🧫 :petri_dish:'; insertText = '🧫'; filterText = ':petri_dish:' }; +{ word = ':dna:'; label = '🧬 :dna:'; insertText = '🧬'; filterText = ':dna:' }; +{ word = ':compass:'; label = '🧭 :compass:'; insertText = '🧭'; filterText = ':compass:' }; +{ word = ':abacus:'; label = '🧮 :abacus:'; insertText = '🧮'; filterText = ':abacus:' }; +{ word = ':fire_extinguisher:'; label = '🧯 :fire_extinguisher:'; insertText = '🧯'; filterText = ':fire_extinguisher:' }; +{ word = ':toolbox:'; label = '🧰 :toolbox:'; insertText = '🧰'; filterText = ':toolbox:' }; +{ word = ':bricks:'; label = '🧱 :bricks:'; insertText = '🧱'; filterText = ':bricks:' }; +{ word = ':magnet:'; label = '🧲 :magnet:'; insertText = '🧲'; filterText = ':magnet:' }; +{ word = ':luggage:'; label = '🧳 :luggage:'; insertText = '🧳'; filterText = ':luggage:' }; +{ word = ':lotion_bottle:'; label = '🧴 :lotion_bottle:'; insertText = '🧴'; filterText = ':lotion_bottle:' }; +{ word = ':thread:'; label = '🧵 :thread:'; insertText = '🧵'; filterText = ':thread:' }; +{ word = ':yarn:'; label = '🧶 :yarn:'; insertText = '🧶'; filterText = ':yarn:' }; +{ word = ':safety_pin:'; label = '🧷 :safety_pin:'; insertText = '🧷'; filterText = ':safety_pin:' }; +{ word = ':teddy_bear:'; label = '🧸 :teddy_bear:'; insertText = '🧸'; filterText = ':teddy_bear:' }; +{ word = ':broom:'; label = '🧹 :broom:'; insertText = '🧹'; filterText = ':broom:' }; +{ word = ':basket:'; label = '🧺 :basket:'; insertText = '🧺'; filterText = ':basket:' }; +{ word = ':roll_of_paper:'; label = '🧻 :roll_of_paper:'; insertText = '🧻'; filterText = ':roll_of_paper:' }; +{ word = ':soap:'; label = '🧼 :soap:'; insertText = '🧼'; filterText = ':soap:' }; +{ word = ':sponge:'; label = '🧽 :sponge:'; insertText = '🧽'; filterText = ':sponge:' }; +{ word = ':receipt:'; label = '🧾 :receipt:'; insertText = '🧾'; filterText = ':receipt:' }; +{ word = ':nazar_amulet:'; label = '🧿 :nazar_amulet:'; insertText = '🧿'; filterText = ':nazar_amulet:' }; +{ word = ':ballet_shoes:'; label = '🩰 :ballet_shoes:'; insertText = '🩰'; filterText = ':ballet_shoes:' }; +{ word = ':one-piece_swimsuit:'; label = '🩱 :one-piece_swimsuit:'; insertText = '🩱'; filterText = ':one-piece_swimsuit:' }; +{ word = ':briefs:'; label = '🩲 :briefs:'; insertText = '🩲'; filterText = ':briefs:' }; +{ word = ':shorts:'; label = '🩳 :shorts:'; insertText = '🩳'; filterText = ':shorts:' }; +{ word = ':thong_sandal:'; label = '🩴 :thong_sandal:'; insertText = '🩴'; filterText = ':thong_sandal:' }; +{ word = ':light_blue_heart:'; label = '🩵 :light_blue_heart:'; insertText = '🩵'; filterText = ':light_blue_heart:' }; +{ word = ':grey_heart:'; label = '🩶 :grey_heart:'; insertText = '🩶'; filterText = ':grey_heart:' }; +{ word = ':pink_heart:'; label = '🩷 :pink_heart:'; insertText = '🩷'; filterText = ':pink_heart:' }; +{ word = ':drop_of_blood:'; label = '🩸 :drop_of_blood:'; insertText = '🩸'; filterText = ':drop_of_blood:' }; +{ word = ':adhesive_bandage:'; label = '🩹 :adhesive_bandage:'; insertText = '🩹'; filterText = ':adhesive_bandage:' }; +{ word = ':stethoscope:'; label = '🩺 :stethoscope:'; insertText = '🩺'; filterText = ':stethoscope:' }; +{ word = ':x-ray:'; label = '🩻 :x-ray:'; insertText = '🩻'; filterText = ':x-ray:' }; +{ word = ':crutch:'; label = '🩼 :crutch:'; insertText = '🩼'; filterText = ':crutch:' }; +{ word = ':yo-yo:'; label = '🪀 :yo-yo:'; insertText = '🪀'; filterText = ':yo-yo:' }; +{ word = ':kite:'; label = '🪁 :kite:'; insertText = '🪁'; filterText = ':kite:' }; +{ word = ':parachute:'; label = '🪂 :parachute:'; insertText = '🪂'; filterText = ':parachute:' }; +{ word = ':boomerang:'; label = '🪃 :boomerang:'; insertText = '🪃'; filterText = ':boomerang:' }; +{ word = ':magic_wand:'; label = '🪄 :magic_wand:'; insertText = '🪄'; filterText = ':magic_wand:' }; +{ word = ':pinata:'; label = '🪅 :pinata:'; insertText = '🪅'; filterText = ':pinata:' }; +{ word = ':nesting_dolls:'; label = '🪆 :nesting_dolls:'; insertText = '🪆'; filterText = ':nesting_dolls:' }; +{ word = ':maracas:'; label = '🪇 :maracas:'; insertText = '🪇'; filterText = ':maracas:' }; +{ word = ':flute:'; label = '🪈 :flute:'; insertText = '🪈'; filterText = ':flute:' }; +{ word = ':ringed_planet:'; label = '🪐 :ringed_planet:'; insertText = '🪐'; filterText = ':ringed_planet:' }; +{ word = ':chair:'; label = '🪑 :chair:'; insertText = '🪑'; filterText = ':chair:' }; +{ word = ':razor:'; label = '🪒 :razor:'; insertText = '🪒'; filterText = ':razor:' }; +{ word = ':axe:'; label = '🪓 :axe:'; insertText = '🪓'; filterText = ':axe:' }; +{ word = ':diya_lamp:'; label = '🪔 :diya_lamp:'; insertText = '🪔'; filterText = ':diya_lamp:' }; +{ word = ':banjo:'; label = '🪕 :banjo:'; insertText = '🪕'; filterText = ':banjo:' }; +{ word = ':military_helmet:'; label = '🪖 :military_helmet:'; insertText = '🪖'; filterText = ':military_helmet:' }; +{ word = ':accordion:'; label = '🪗 :accordion:'; insertText = '🪗'; filterText = ':accordion:' }; +{ word = ':long_drum:'; label = '🪘 :long_drum:'; insertText = '🪘'; filterText = ':long_drum:' }; +{ word = ':coin:'; label = '🪙 :coin:'; insertText = '🪙'; filterText = ':coin:' }; +{ word = ':carpentry_saw:'; label = '🪚 :carpentry_saw:'; insertText = '🪚'; filterText = ':carpentry_saw:' }; +{ word = ':screwdriver:'; label = '🪛 :screwdriver:'; insertText = '🪛'; filterText = ':screwdriver:' }; +{ word = ':ladder:'; label = '🪜 :ladder:'; insertText = '🪜'; filterText = ':ladder:' }; +{ word = ':hook:'; label = '🪝 :hook:'; insertText = '🪝'; filterText = ':hook:' }; +{ word = ':mirror:'; label = '🪞 :mirror:'; insertText = '🪞'; filterText = ':mirror:' }; +{ word = ':window:'; label = '🪟 :window:'; insertText = '🪟'; filterText = ':window:' }; +{ word = ':plunger:'; label = '🪠 :plunger:'; insertText = '🪠'; filterText = ':plunger:' }; +{ word = ':sewing_needle:'; label = '🪡 :sewing_needle:'; insertText = '🪡'; filterText = ':sewing_needle:' }; +{ word = ':knot:'; label = '🪢 :knot:'; insertText = '🪢'; filterText = ':knot:' }; +{ word = ':bucket:'; label = '🪣 :bucket:'; insertText = '🪣'; filterText = ':bucket:' }; +{ word = ':mouse_trap:'; label = '🪤 :mouse_trap:'; insertText = '🪤'; filterText = ':mouse_trap:' }; +{ word = ':toothbrush:'; label = '🪥 :toothbrush:'; insertText = '🪥'; filterText = ':toothbrush:' }; +{ word = ':headstone:'; label = '🪦 :headstone:'; insertText = '🪦'; filterText = ':headstone:' }; +{ word = ':placard:'; label = '🪧 :placard:'; insertText = '🪧'; filterText = ':placard:' }; +{ word = ':rock:'; label = '🪨 :rock:'; insertText = '🪨'; filterText = ':rock:' }; +{ word = ':mirror_ball:'; label = '🪩 :mirror_ball:'; insertText = '🪩'; filterText = ':mirror_ball:' }; +{ word = ':identification_card:'; label = '🪪 :identification_card:'; insertText = '🪪'; filterText = ':identification_card:' }; +{ word = ':low_battery:'; label = '🪫 :low_battery:'; insertText = '🪫'; filterText = ':low_battery:' }; +{ word = ':hamsa:'; label = '🪬 :hamsa:'; insertText = '🪬'; filterText = ':hamsa:' }; +{ word = ':folding_hand_fan:'; label = '🪭 :folding_hand_fan:'; insertText = '🪭'; filterText = ':folding_hand_fan:' }; +{ word = ':hair_pick:'; label = '🪮 :hair_pick:'; insertText = '🪮'; filterText = ':hair_pick:' }; +{ word = ':khanda:'; label = '🪯 :khanda:'; insertText = '🪯'; filterText = ':khanda:' }; +{ word = ':fly:'; label = '🪰 :fly:'; insertText = '🪰'; filterText = ':fly:' }; +{ word = ':worm:'; label = '🪱 :worm:'; insertText = '🪱'; filterText = ':worm:' }; +{ word = ':beetle:'; label = '🪲 :beetle:'; insertText = '🪲'; filterText = ':beetle:' }; +{ word = ':cockroach:'; label = '🪳 :cockroach:'; insertText = '🪳'; filterText = ':cockroach:' }; +{ word = ':potted_plant:'; label = '🪴 :potted_plant:'; insertText = '🪴'; filterText = ':potted_plant:' }; +{ word = ':wood:'; label = '🪵 :wood:'; insertText = '🪵'; filterText = ':wood:' }; +{ word = ':feather:'; label = '🪶 :feather:'; insertText = '🪶'; filterText = ':feather:' }; +{ word = ':lotus:'; label = '🪷 :lotus:'; insertText = '🪷'; filterText = ':lotus:' }; +{ word = ':coral:'; label = '🪸 :coral:'; insertText = '🪸'; filterText = ':coral:' }; +{ word = ':empty_nest:'; label = '🪹 :empty_nest:'; insertText = '🪹'; filterText = ':empty_nest:' }; +{ word = ':nest_with_eggs:'; label = '🪺 :nest_with_eggs:'; insertText = '🪺'; filterText = ':nest_with_eggs:' }; +{ word = ':hyacinth:'; label = '🪻 :hyacinth:'; insertText = '🪻'; filterText = ':hyacinth:' }; +{ word = ':jellyfish:'; label = '🪼 :jellyfish:'; insertText = '🪼'; filterText = ':jellyfish:' }; +{ word = ':wing:'; label = '🪽 :wing:'; insertText = '🪽'; filterText = ':wing:' }; +{ word = ':goose:'; label = '🪿 :goose:'; insertText = '🪿'; filterText = ':goose:' }; +{ word = ':anatomical_heart:'; label = '🫀 :anatomical_heart:'; insertText = '🫀'; filterText = ':anatomical_heart:' }; +{ word = ':lungs:'; label = '🫁 :lungs:'; insertText = '🫁'; filterText = ':lungs:' }; +{ word = ':people_hugging:'; label = '🫂 :people_hugging:'; insertText = '🫂'; filterText = ':people_hugging:' }; +{ word = ':pregnant_man:'; label = '🫃 :pregnant_man:'; insertText = '🫃'; filterText = ':pregnant_man:' }; +{ word = ':pregnant_person:'; label = '🫄 :pregnant_person:'; insertText = '🫄'; filterText = ':pregnant_person:' }; +{ word = ':person_with_crown:'; label = '🫅 :person_with_crown:'; insertText = '🫅'; filterText = ':person_with_crown:' }; +{ word = ':moose:'; label = '🫎 :moose:'; insertText = '🫎'; filterText = ':moose:' }; +{ word = ':donkey:'; label = '🫏 :donkey:'; insertText = '🫏'; filterText = ':donkey:' }; +{ word = ':blueberries:'; label = '🫐 :blueberries:'; insertText = '🫐'; filterText = ':blueberries:' }; +{ word = ':bell_pepper:'; label = '🫑 :bell_pepper:'; insertText = '🫑'; filterText = ':bell_pepper:' }; +{ word = ':olive:'; label = '🫒 :olive:'; insertText = '🫒'; filterText = ':olive:' }; +{ word = ':flatbread:'; label = '🫓 :flatbread:'; insertText = '🫓'; filterText = ':flatbread:' }; +{ word = ':tamale:'; label = '🫔 :tamale:'; insertText = '🫔'; filterText = ':tamale:' }; +{ word = ':fondue:'; label = '🫕 :fondue:'; insertText = '🫕'; filterText = ':fondue:' }; +{ word = ':teapot:'; label = '🫖 :teapot:'; insertText = '🫖'; filterText = ':teapot:' }; +{ word = ':pouring_liquid:'; label = '🫗 :pouring_liquid:'; insertText = '🫗'; filterText = ':pouring_liquid:' }; +{ word = ':beans:'; label = '🫘 :beans:'; insertText = '🫘'; filterText = ':beans:' }; +{ word = ':jar:'; label = '🫙 :jar:'; insertText = '🫙'; filterText = ':jar:' }; +{ word = ':ginger_root:'; label = '🫚 :ginger_root:'; insertText = '🫚'; filterText = ':ginger_root:' }; +{ word = ':pea_pod:'; label = '🫛 :pea_pod:'; insertText = '🫛'; filterText = ':pea_pod:' }; +{ word = ':melting_face:'; label = '🫠 :melting_face:'; insertText = '🫠'; filterText = ':melting_face:' }; +{ word = ':saluting_face:'; label = '🫡 :saluting_face:'; insertText = '🫡'; filterText = ':saluting_face:' }; +{ word = ':face_with_open_eyes_and_hand_over_mouth:'; label = '🫢 :face_with_open_eyes_and_hand_over_mouth:'; insertText = '🫢'; filterText = ':face_with_open_eyes_and_hand_over_mouth:' }; +{ word = ':face_with_peeking_eye:'; label = '🫣 :face_with_peeking_eye:'; insertText = '🫣'; filterText = ':face_with_peeking_eye:' }; +{ word = ':face_with_diagonal_mouth:'; label = '🫤 :face_with_diagonal_mouth:'; insertText = '🫤'; filterText = ':face_with_diagonal_mouth:' }; +{ word = ':dotted_line_face:'; label = '🫥 :dotted_line_face:'; insertText = '🫥'; filterText = ':dotted_line_face:' }; +{ word = ':biting_lip:'; label = '🫦 :biting_lip:'; insertText = '🫦'; filterText = ':biting_lip:' }; +{ word = ':bubbles:'; label = '🫧 :bubbles:'; insertText = '🫧'; filterText = ':bubbles:' }; +{ word = ':shaking_face:'; label = '🫨 :shaking_face:'; insertText = '🫨'; filterText = ':shaking_face:' }; +{ word = ':hand_with_index_finger_and_thumb_crossed:'; label = '🫰 :hand_with_index_finger_and_thumb_crossed:'; insertText = '🫰'; filterText = ':hand_with_index_finger_and_thumb_crossed:' }; +{ word = ':rightwards_hand:'; label = '🫱 :rightwards_hand:'; insertText = '🫱'; filterText = ':rightwards_hand:' }; +{ word = ':leftwards_hand:'; label = '🫲 :leftwards_hand:'; insertText = '🫲'; filterText = ':leftwards_hand:' }; +{ word = ':palm_down_hand:'; label = '🫳 :palm_down_hand:'; insertText = '🫳'; filterText = ':palm_down_hand:' }; +{ word = ':palm_up_hand:'; label = '🫴 :palm_up_hand:'; insertText = '🫴'; filterText = ':palm_up_hand:' }; +{ word = ':index_pointing_at_the_viewer:'; label = '🫵 :index_pointing_at_the_viewer:'; insertText = '🫵'; filterText = ':index_pointing_at_the_viewer:' }; +{ word = ':heart_hands:'; label = '🫶 :heart_hands:'; insertText = '🫶'; filterText = ':heart_hands:' }; +{ word = ':leftwards_pushing_hand:'; label = '🫷 :leftwards_pushing_hand:'; insertText = '🫷'; filterText = ':leftwards_pushing_hand:' }; +{ word = ':rightwards_pushing_hand:'; label = '🫸 :rightwards_pushing_hand:'; insertText = '🫸'; filterText = ':rightwards_pushing_hand:' }; +{ word = ':bangbang:'; label = '‼️ :bangbang:'; insertText = '‼️'; filterText = ':bangbang:' }; +{ word = ':interrobang:'; label = '⁉️ :interrobang:'; insertText = '⁉️'; filterText = ':interrobang:' }; +{ word = ':tm:'; label = '™️ :tm:'; insertText = '™️'; filterText = ':tm:' }; +{ word = ':information_source:'; label = 'ℹ️ :information_source:'; insertText = 'ℹ️'; filterText = ':information_source:' }; +{ word = ':left_right_arrow:'; label = '↔️ :left_right_arrow:'; insertText = '↔️'; filterText = ':left_right_arrow:' }; +{ word = ':arrow_up_down:'; label = '↕️ :arrow_up_down:'; insertText = '↕️'; filterText = ':arrow_up_down:' }; +{ word = ':arrow_upper_left:'; label = '↖️ :arrow_upper_left:'; insertText = '↖️'; filterText = ':arrow_upper_left:' }; +{ word = ':arrow_upper_right:'; label = '↗️ :arrow_upper_right:'; insertText = '↗️'; filterText = ':arrow_upper_right:' }; +{ word = ':arrow_lower_right:'; label = '↘️ :arrow_lower_right:'; insertText = '↘️'; filterText = ':arrow_lower_right:' }; +{ word = ':arrow_lower_left:'; label = '↙️ :arrow_lower_left:'; insertText = '↙️'; filterText = ':arrow_lower_left:' }; +{ word = ':leftwards_arrow_with_hook:'; label = '↩️ :leftwards_arrow_with_hook:'; insertText = '↩️'; filterText = ':leftwards_arrow_with_hook:' }; +{ word = ':arrow_right_hook:'; label = '↪️ :arrow_right_hook:'; insertText = '↪️'; filterText = ':arrow_right_hook:' }; +{ word = ':watch:'; label = '⌚ :watch:'; insertText = '⌚'; filterText = ':watch:' }; +{ word = ':hourglass:'; label = '⌛ :hourglass:'; insertText = '⌛'; filterText = ':hourglass:' }; +{ word = ':keyboard:'; label = '⌨️ :keyboard:'; insertText = '⌨️'; filterText = ':keyboard:' }; +{ word = ':eject:'; label = '⏏️ :eject:'; insertText = '⏏️'; filterText = ':eject:' }; +{ word = ':fast_forward:'; label = '⏩ :fast_forward:'; insertText = '⏩'; filterText = ':fast_forward:' }; +{ word = ':rewind:'; label = '⏪ :rewind:'; insertText = '⏪'; filterText = ':rewind:' }; +{ word = ':arrow_double_up:'; label = '⏫ :arrow_double_up:'; insertText = '⏫'; filterText = ':arrow_double_up:' }; +{ word = ':arrow_double_down:'; label = '⏬ :arrow_double_down:'; insertText = '⏬'; filterText = ':arrow_double_down:' }; +{ word = ':black_right_pointing_double_triangle_with_vertical_bar:'; label = '⏭️ :black_right_pointing_double_triangle_with_vertical_bar:'; insertText = '⏭️'; filterText = ':black_right_pointing_double_triangle_with_vertical_bar:' }; +{ word = ':black_left_pointing_double_triangle_with_vertical_bar:'; label = '⏮️ :black_left_pointing_double_triangle_with_vertical_bar:'; insertText = '⏮️'; filterText = ':black_left_pointing_double_triangle_with_vertical_bar:' }; +{ word = ':black_right_pointing_triangle_with_double_vertical_bar:'; label = '⏯️ :black_right_pointing_triangle_with_double_vertical_bar:'; insertText = '⏯️'; filterText = ':black_right_pointing_triangle_with_double_vertical_bar:' }; +{ word = ':alarm_clock:'; label = '⏰ :alarm_clock:'; insertText = '⏰'; filterText = ':alarm_clock:' }; +{ word = ':stopwatch:'; label = '⏱️ :stopwatch:'; insertText = '⏱️'; filterText = ':stopwatch:' }; +{ word = ':timer_clock:'; label = '⏲️ :timer_clock:'; insertText = '⏲️'; filterText = ':timer_clock:' }; +{ word = ':hourglass_flowing_sand:'; label = '⏳ :hourglass_flowing_sand:'; insertText = '⏳'; filterText = ':hourglass_flowing_sand:' }; +{ word = ':double_vertical_bar:'; label = '⏸️ :double_vertical_bar:'; insertText = '⏸️'; filterText = ':double_vertical_bar:' }; +{ word = ':black_square_for_stop:'; label = '⏹️ :black_square_for_stop:'; insertText = '⏹️'; filterText = ':black_square_for_stop:' }; +{ word = ':black_circle_for_record:'; label = '⏺️ :black_circle_for_record:'; insertText = '⏺️'; filterText = ':black_circle_for_record:' }; +{ word = ':m:'; label = 'Ⓜ️ :m:'; insertText = 'Ⓜ️'; filterText = ':m:' }; +{ word = ':black_small_square:'; label = '▪️ :black_small_square:'; insertText = '▪️'; filterText = ':black_small_square:' }; +{ word = ':white_small_square:'; label = '▫️ :white_small_square:'; insertText = '▫️'; filterText = ':white_small_square:' }; +{ word = ':arrow_forward:'; label = '▶️ :arrow_forward:'; insertText = '▶️'; filterText = ':arrow_forward:' }; +{ word = ':arrow_backward:'; label = '◀️ :arrow_backward:'; insertText = '◀️'; filterText = ':arrow_backward:' }; +{ word = ':white_medium_square:'; label = '◻️ :white_medium_square:'; insertText = '◻️'; filterText = ':white_medium_square:' }; +{ word = ':black_medium_square:'; label = '◼️ :black_medium_square:'; insertText = '◼️'; filterText = ':black_medium_square:' }; +{ word = ':white_medium_small_square:'; label = '◽ :white_medium_small_square:'; insertText = '◽'; filterText = ':white_medium_small_square:' }; +{ word = ':black_medium_small_square:'; label = '◾ :black_medium_small_square:'; insertText = '◾'; filterText = ':black_medium_small_square:' }; +{ word = ':sunny:'; label = '☀️ :sunny:'; insertText = '☀️'; filterText = ':sunny:' }; +{ word = ':cloud:'; label = '☁️ :cloud:'; insertText = '☁️'; filterText = ':cloud:' }; +{ word = ':umbrella:'; label = '☂️ :umbrella:'; insertText = '☂️'; filterText = ':umbrella:' }; +{ word = ':snowman:'; label = '☃️ :snowman:'; insertText = '☃️'; filterText = ':snowman:' }; +{ word = ':comet:'; label = '☄️ :comet:'; insertText = '☄️'; filterText = ':comet:' }; +{ word = ':phone:'; label = '☎️ :phone:'; insertText = '☎️'; filterText = ':phone:' }; +{ word = ':telephone:'; label = '☎️ :telephone:'; insertText = '☎️'; filterText = ':telephone:' }; +{ word = ':ballot_box_with_check:'; label = '☑️ :ballot_box_with_check:'; insertText = '☑️'; filterText = ':ballot_box_with_check:' }; +{ word = ':umbrella_with_rain_drops:'; label = '☔ :umbrella_with_rain_drops:'; insertText = '☔'; filterText = ':umbrella_with_rain_drops:' }; +{ word = ':coffee:'; label = '☕ :coffee:'; insertText = '☕'; filterText = ':coffee:' }; +{ word = ':shamrock:'; label = '☘️ :shamrock:'; insertText = '☘️'; filterText = ':shamrock:' }; +{ word = ':point_up:'; label = '☝️ :point_up:'; insertText = '☝️'; filterText = ':point_up:' }; +{ word = ':skull_and_crossbones:'; label = '☠️ :skull_and_crossbones:'; insertText = '☠️'; filterText = ':skull_and_crossbones:' }; +{ word = ':radioactive_sign:'; label = '☢️ :radioactive_sign:'; insertText = '☢️'; filterText = ':radioactive_sign:' }; +{ word = ':biohazard_sign:'; label = '☣️ :biohazard_sign:'; insertText = '☣️'; filterText = ':biohazard_sign:' }; +{ word = ':orthodox_cross:'; label = '☦️ :orthodox_cross:'; insertText = '☦️'; filterText = ':orthodox_cross:' }; +{ word = ':star_and_crescent:'; label = '☪️ :star_and_crescent:'; insertText = '☪️'; filterText = ':star_and_crescent:' }; +{ word = ':peace_symbol:'; label = '☮️ :peace_symbol:'; insertText = '☮️'; filterText = ':peace_symbol:' }; +{ word = ':yin_yang:'; label = '☯️ :yin_yang:'; insertText = '☯️'; filterText = ':yin_yang:' }; +{ word = ':wheel_of_dharma:'; label = '☸️ :wheel_of_dharma:'; insertText = '☸️'; filterText = ':wheel_of_dharma:' }; +{ word = ':white_frowning_face:'; label = '☹️ :white_frowning_face:'; insertText = '☹️'; filterText = ':white_frowning_face:' }; +{ word = ':relaxed:'; label = '☺️ :relaxed:'; insertText = '☺️'; filterText = ':relaxed:' }; +{ word = ':female_sign:'; label = '♀️ :female_sign:'; insertText = '♀️'; filterText = ':female_sign:' }; +{ word = ':male_sign:'; label = '♂️ :male_sign:'; insertText = '♂️'; filterText = ':male_sign:' }; +{ word = ':aries:'; label = '♈ :aries:'; insertText = '♈'; filterText = ':aries:' }; +{ word = ':taurus:'; label = '♉ :taurus:'; insertText = '♉'; filterText = ':taurus:' }; +{ word = ':gemini:'; label = '♊ :gemini:'; insertText = '♊'; filterText = ':gemini:' }; +{ word = ':cancer:'; label = '♋ :cancer:'; insertText = '♋'; filterText = ':cancer:' }; +{ word = ':leo:'; label = '♌ :leo:'; insertText = '♌'; filterText = ':leo:' }; +{ word = ':virgo:'; label = '♍ :virgo:'; insertText = '♍'; filterText = ':virgo:' }; +{ word = ':libra:'; label = '♎ :libra:'; insertText = '♎'; filterText = ':libra:' }; +{ word = ':scorpius:'; label = '♏ :scorpius:'; insertText = '♏'; filterText = ':scorpius:' }; +{ word = ':sagittarius:'; label = '♐ :sagittarius:'; insertText = '♐'; filterText = ':sagittarius:' }; +{ word = ':capricorn:'; label = '♑ :capricorn:'; insertText = '♑'; filterText = ':capricorn:' }; +{ word = ':aquarius:'; label = '♒ :aquarius:'; insertText = '♒'; filterText = ':aquarius:' }; +{ word = ':pisces:'; label = '♓ :pisces:'; insertText = '♓'; filterText = ':pisces:' }; +{ word = ':chess_pawn:'; label = '♟️ :chess_pawn:'; insertText = '♟️'; filterText = ':chess_pawn:' }; +{ word = ':spades:'; label = '♠️ :spades:'; insertText = '♠️'; filterText = ':spades:' }; +{ word = ':clubs:'; label = '♣️ :clubs:'; insertText = '♣️'; filterText = ':clubs:' }; +{ word = ':hearts:'; label = '♥️ :hearts:'; insertText = '♥️'; filterText = ':hearts:' }; +{ word = ':diamonds:'; label = '♦️ :diamonds:'; insertText = '♦️'; filterText = ':diamonds:' }; +{ word = ':hotsprings:'; label = '♨️ :hotsprings:'; insertText = '♨️'; filterText = ':hotsprings:' }; +{ word = ':recycle:'; label = '♻️ :recycle:'; insertText = '♻️'; filterText = ':recycle:' }; +{ word = ':infinity:'; label = '♾️ :infinity:'; insertText = '♾️'; filterText = ':infinity:' }; +{ word = ':wheelchair:'; label = '♿ :wheelchair:'; insertText = '♿'; filterText = ':wheelchair:' }; +{ word = ':hammer_and_pick:'; label = '⚒️ :hammer_and_pick:'; insertText = '⚒️'; filterText = ':hammer_and_pick:' }; +{ word = ':anchor:'; label = '⚓ :anchor:'; insertText = '⚓'; filterText = ':anchor:' }; +{ word = ':crossed_swords:'; label = '⚔️ :crossed_swords:'; insertText = '⚔️'; filterText = ':crossed_swords:' }; +{ word = ':medical_symbol:'; label = '⚕️ :medical_symbol:'; insertText = '⚕️'; filterText = ':medical_symbol:' }; +{ word = ':staff_of_aesculapius:'; label = '⚕️ :staff_of_aesculapius:'; insertText = '⚕️'; filterText = ':staff_of_aesculapius:' }; +{ word = ':scales:'; label = '⚖️ :scales:'; insertText = '⚖️'; filterText = ':scales:' }; +{ word = ':alembic:'; label = '⚗️ :alembic:'; insertText = '⚗️'; filterText = ':alembic:' }; +{ word = ':gear:'; label = '⚙️ :gear:'; insertText = '⚙️'; filterText = ':gear:' }; +{ word = ':atom_symbol:'; label = '⚛️ :atom_symbol:'; insertText = '⚛️'; filterText = ':atom_symbol:' }; +{ word = ':fleur_de_lis:'; label = '⚜️ :fleur_de_lis:'; insertText = '⚜️'; filterText = ':fleur_de_lis:' }; +{ word = ':warning:'; label = '⚠️ :warning:'; insertText = '⚠️'; filterText = ':warning:' }; +{ word = ':zap:'; label = '⚡ :zap:'; insertText = '⚡'; filterText = ':zap:' }; +{ word = ':transgender_symbol:'; label = '⚧️ :transgender_symbol:'; insertText = '⚧️'; filterText = ':transgender_symbol:' }; +{ word = ':white_circle:'; label = '⚪ :white_circle:'; insertText = '⚪'; filterText = ':white_circle:' }; +{ word = ':black_circle:'; label = '⚫ :black_circle:'; insertText = '⚫'; filterText = ':black_circle:' }; +{ word = ':coffin:'; label = '⚰️ :coffin:'; insertText = '⚰️'; filterText = ':coffin:' }; +{ word = ':funeral_urn:'; label = '⚱️ :funeral_urn:'; insertText = '⚱️'; filterText = ':funeral_urn:' }; +{ word = ':soccer:'; label = '⚽ :soccer:'; insertText = '⚽'; filterText = ':soccer:' }; +{ word = ':baseball:'; label = '⚾ :baseball:'; insertText = '⚾'; filterText = ':baseball:' }; +{ word = ':snowman_without_snow:'; label = '⛄ :snowman_without_snow:'; insertText = '⛄'; filterText = ':snowman_without_snow:' }; +{ word = ':partly_sunny:'; label = '⛅ :partly_sunny:'; insertText = '⛅'; filterText = ':partly_sunny:' }; +{ word = ':thunder_cloud_and_rain:'; label = '⛈️ :thunder_cloud_and_rain:'; insertText = '⛈️'; filterText = ':thunder_cloud_and_rain:' }; +{ word = ':ophiuchus:'; label = '⛎ :ophiuchus:'; insertText = '⛎'; filterText = ':ophiuchus:' }; +{ word = ':pick:'; label = '⛏️ :pick:'; insertText = '⛏️'; filterText = ':pick:' }; +{ word = ':helmet_with_white_cross:'; label = '⛑️ :helmet_with_white_cross:'; insertText = '⛑️'; filterText = ':helmet_with_white_cross:' }; +{ word = ':chains:'; label = '⛓️ :chains:'; insertText = '⛓️'; filterText = ':chains:' }; +{ word = ':no_entry:'; label = '⛔ :no_entry:'; insertText = '⛔'; filterText = ':no_entry:' }; +{ word = ':shinto_shrine:'; label = '⛩️ :shinto_shrine:'; insertText = '⛩️'; filterText = ':shinto_shrine:' }; +{ word = ':church:'; label = '⛪ :church:'; insertText = '⛪'; filterText = ':church:' }; +{ word = ':mountain:'; label = '⛰️ :mountain:'; insertText = '⛰️'; filterText = ':mountain:' }; +{ word = ':umbrella_on_ground:'; label = '⛱️ :umbrella_on_ground:'; insertText = '⛱️'; filterText = ':umbrella_on_ground:' }; +{ word = ':fountain:'; label = '⛲ :fountain:'; insertText = '⛲'; filterText = ':fountain:' }; +{ word = ':golf:'; label = '⛳ :golf:'; insertText = '⛳'; filterText = ':golf:' }; +{ word = ':ferry:'; label = '⛴️ :ferry:'; insertText = '⛴️'; filterText = ':ferry:' }; +{ word = ':boat:'; label = '⛵ :boat:'; insertText = '⛵'; filterText = ':boat:' }; +{ word = ':sailboat:'; label = '⛵ :sailboat:'; insertText = '⛵'; filterText = ':sailboat:' }; +{ word = ':skier:'; label = '⛷️ :skier:'; insertText = '⛷️'; filterText = ':skier:' }; +{ word = ':ice_skate:'; label = '⛸️ :ice_skate:'; insertText = '⛸️'; filterText = ':ice_skate:' }; +{ word = ':person_with_ball:'; label = '⛹️ :person_with_ball:'; insertText = '⛹️'; filterText = ':person_with_ball:' }; +{ word = ':tent:'; label = '⛺ :tent:'; insertText = '⛺'; filterText = ':tent:' }; +{ word = ':fuelpump:'; label = '⛽ :fuelpump:'; insertText = '⛽'; filterText = ':fuelpump:' }; +{ word = ':scissors:'; label = '✂️ :scissors:'; insertText = '✂️'; filterText = ':scissors:' }; +{ word = ':white_check_mark:'; label = '✅ :white_check_mark:'; insertText = '✅'; filterText = ':white_check_mark:' }; +{ word = ':airplane:'; label = '✈️ :airplane:'; insertText = '✈️'; filterText = ':airplane:' }; +{ word = ':email:'; label = '✉️ :email:'; insertText = '✉️'; filterText = ':email:' }; +{ word = ':envelope:'; label = '✉️ :envelope:'; insertText = '✉️'; filterText = ':envelope:' }; +{ word = ':fist:'; label = '✊ :fist:'; insertText = '✊'; filterText = ':fist:' }; +{ word = ':hand:'; label = '✋ :hand:'; insertText = '✋'; filterText = ':hand:' }; +{ word = ':raised_hand:'; label = '✋ :raised_hand:'; insertText = '✋'; filterText = ':raised_hand:' }; +{ word = ':v:'; label = '✌️ :v:'; insertText = '✌️'; filterText = ':v:' }; +{ word = ':writing_hand:'; label = '✍️ :writing_hand:'; insertText = '✍️'; filterText = ':writing_hand:' }; +{ word = ':pencil2:'; label = '✏️ :pencil2:'; insertText = '✏️'; filterText = ':pencil2:' }; +{ word = ':black_nib:'; label = '✒️ :black_nib:'; insertText = '✒️'; filterText = ':black_nib:' }; +{ word = ':heavy_check_mark:'; label = '✔️ :heavy_check_mark:'; insertText = '✔️'; filterText = ':heavy_check_mark:' }; +{ word = ':heavy_multiplication_x:'; label = '✖️ :heavy_multiplication_x:'; insertText = '✖️'; filterText = ':heavy_multiplication_x:' }; +{ word = ':latin_cross:'; label = '✝️ :latin_cross:'; insertText = '✝️'; filterText = ':latin_cross:' }; +{ word = ':star_of_david:'; label = '✡️ :star_of_david:'; insertText = '✡️'; filterText = ':star_of_david:' }; +{ word = ':sparkles:'; label = '✨ :sparkles:'; insertText = '✨'; filterText = ':sparkles:' }; +{ word = ':eight_spoked_asterisk:'; label = '✳️ :eight_spoked_asterisk:'; insertText = '✳️'; filterText = ':eight_spoked_asterisk:' }; +{ word = ':eight_pointed_black_star:'; label = '✴️ :eight_pointed_black_star:'; insertText = '✴️'; filterText = ':eight_pointed_black_star:' }; +{ word = ':snowflake:'; label = '❄️ :snowflake:'; insertText = '❄️'; filterText = ':snowflake:' }; +{ word = ':sparkle:'; label = '❇️ :sparkle:'; insertText = '❇️'; filterText = ':sparkle:' }; +{ word = ':x:'; label = '❌ :x:'; insertText = '❌'; filterText = ':x:' }; +{ word = ':negative_squared_cross_mark:'; label = '❎ :negative_squared_cross_mark:'; insertText = '❎'; filterText = ':negative_squared_cross_mark:' }; +{ word = ':question:'; label = '❓ :question:'; insertText = '❓'; filterText = ':question:' }; +{ word = ':grey_question:'; label = '❔ :grey_question:'; insertText = '❔'; filterText = ':grey_question:' }; +{ word = ':grey_exclamation:'; label = '❕ :grey_exclamation:'; insertText = '❕'; filterText = ':grey_exclamation:' }; +{ word = ':exclamation:'; label = '❗ :exclamation:'; insertText = '❗'; filterText = ':exclamation:' }; +{ word = ':heavy_exclamation_mark:'; label = '❗ :heavy_exclamation_mark:'; insertText = '❗'; filterText = ':heavy_exclamation_mark:' }; +{ word = ':heavy_heart_exclamation_mark_ornament:'; label = '❣️ :heavy_heart_exclamation_mark_ornament:'; insertText = '❣️'; filterText = ':heavy_heart_exclamation_mark_ornament:' }; +{ word = ':heart:'; label = '❤️ :heart:'; insertText = '❤️'; filterText = ':heart:' }; +{ word = ':heavy_plus_sign:'; label = '➕ :heavy_plus_sign:'; insertText = '➕'; filterText = ':heavy_plus_sign:' }; +{ word = ':heavy_minus_sign:'; label = '➖ :heavy_minus_sign:'; insertText = '➖'; filterText = ':heavy_minus_sign:' }; +{ word = ':heavy_division_sign:'; label = '➗ :heavy_division_sign:'; insertText = '➗'; filterText = ':heavy_division_sign:' }; +{ word = ':arrow_right:'; label = '➡️ :arrow_right:'; insertText = '➡️'; filterText = ':arrow_right:' }; +{ word = ':curly_loop:'; label = '➰ :curly_loop:'; insertText = '➰'; filterText = ':curly_loop:' }; +{ word = ':loop:'; label = '➿ :loop:'; insertText = '➿'; filterText = ':loop:' }; +{ word = ':arrow_heading_up:'; label = '⤴️ :arrow_heading_up:'; insertText = '⤴️'; filterText = ':arrow_heading_up:' }; +{ word = ':arrow_heading_down:'; label = '⤵️ :arrow_heading_down:'; insertText = '⤵️'; filterText = ':arrow_heading_down:' }; +{ word = ':arrow_left:'; label = '⬅️ :arrow_left:'; insertText = '⬅️'; filterText = ':arrow_left:' }; +{ word = ':arrow_up:'; label = '⬆️ :arrow_up:'; insertText = '⬆️'; filterText = ':arrow_up:' }; +{ word = ':arrow_down:'; label = '⬇️ :arrow_down:'; insertText = '⬇️'; filterText = ':arrow_down:' }; +{ word = ':black_large_square:'; label = '⬛ :black_large_square:'; insertText = '⬛'; filterText = ':black_large_square:' }; +{ word = ':white_large_square:'; label = '⬜ :white_large_square:'; insertText = '⬜'; filterText = ':white_large_square:' }; +{ word = ':star:'; label = '⭐ :star:'; insertText = '⭐'; filterText = ':star:' }; +{ word = ':o:'; label = '⭕ :o:'; insertText = '⭕'; filterText = ':o:' }; +{ word = ':wavy_dash:'; label = '〰️ :wavy_dash:'; insertText = '〰️'; filterText = ':wavy_dash:' }; +{ word = ':part_alternation_mark:'; label = '〽️ :part_alternation_mark:'; insertText = '〽️'; filterText = ':part_alternation_mark:' }; +{ word = ':congratulations:'; label = '㊗️ :congratulations:'; insertText = '㊗️'; filterText = ':congratulations:' }; +{ word = ':secret:'; label = '㊙️ :secret:'; insertText = '㊙️'; filterText = ':secret:' }; +} end \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/update.lua b/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/update.lua new file mode 100644 index 00000000..2f4c7681 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-emoji/lua/cmp_emoji/update.lua @@ -0,0 +1,58 @@ +-- Generates the emoji data Lua file using this as a source: https://raw.githubusercontent.com/iamcal/emoji-data/master/emoji.json + +local M = {} + +M._read = function(path) + return vim.fn.json_decode(vim.fn.readfile(path)) +end + +M._write = function(path, data) + local h = io.open(path, 'w') + h:write(data) + io.close(h) +end + +M.to_string = function(chars) + local nrs = {} + for _, char in ipairs(chars) do + table.insert(nrs, vim.fn.eval(([[char2nr("\U%s")]]):format(char))) + end + return vim.fn.list2str(nrs, true) +end + +M.to_item = function(emoji, short_name) + short_name = ':' .. short_name .. ':' + return ("{ word = '%s'; label = '%s'; insertText = '%s'; filterText = '%s' };\n"):format( + short_name, + emoji .. ' ' .. short_name, + emoji, + short_name + ) +end + +M.to_items = function(emoji, short_names) + local variants = '' + + for _, short_name in ipairs(short_names) do + variants = variants .. M.to_item(emoji, short_name) + end + + return variants +end + +M.update = function() + local items = '' + for _, emoji in ipairs(M._read('./emoji.json')) do + local char = M.to_string(vim.split(emoji.unified, '-')) + + local valid = true + valid = valid and vim.fn.strdisplaywidth(char) <= 2 -- Ignore invalid ligatures + if valid then + items = items .. M.to_items(char, emoji.short_names) + end + end + M._write('./items.lua', ('return function() return {\n%s} end'):format(items)) +end + +return M + diff --git a/config/neovim/store/lazy-plugins/cmp-nvim-lsp-signature-help/README.md b/config/neovim/store/lazy-plugins/cmp-nvim-lsp-signature-help/README.md new file mode 100644 index 00000000..5d600322 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-nvim-lsp-signature-help/README.md @@ -0,0 +1,16 @@ +# cmp-nvim-lsp-signature-help + +nvim-cmp source for displaying function signatures with the current parameter emphasized: + +![Basic Example](https://user-images.githubusercontent.com/12832280/144246351-0604d8cb-40c5-437b-9ca1-f3d420539360.png) +# Setup + +```lua + +require'cmp'.setup { + sources = { + { name = 'nvim_lsp_signature_help' } + } +} +``` + diff --git a/config/neovim/store/lazy-plugins/cmp-nvim-lsp-signature-help/after/plugin/cmp_nvim_lsp_signature_help.lua b/config/neovim/store/lazy-plugins/cmp-nvim-lsp-signature-help/after/plugin/cmp_nvim_lsp_signature_help.lua new file mode 100644 index 00000000..6c30f657 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-nvim-lsp-signature-help/after/plugin/cmp_nvim_lsp_signature_help.lua @@ -0,0 +1 @@ +require('cmp').register_source('nvim_lsp_signature_help', require('cmp_nvim_lsp_signature_help').new()) diff --git a/config/neovim/store/lazy-plugins/cmp-nvim-lsp-signature-help/lua/cmp_nvim_lsp_signature_help/init.lua b/config/neovim/store/lazy-plugins/cmp-nvim-lsp-signature-help/lua/cmp_nvim_lsp_signature_help/init.lua new file mode 100644 index 00000000..57046414 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-nvim-lsp-signature-help/lua/cmp_nvim_lsp_signature_help/init.lua @@ -0,0 +1,218 @@ +local cmp = require('cmp') + +local source = {} + +source.new = function() + return setmetatable({ + signature_help = nil, + }, { __index = source }) +end + +source.is_available = function(self) + return self:_get_client() ~= nil +end + +source.get_keyword_pattern = function(self) + return ([=[\%%(\V%s\m\)\s*\zs]=]):format(table.concat(self:get_trigger_characters(), [[\m\|\V]])) +end + +source.get_trigger_characters = function(self) + local trigger_characters = {} + for _, c in ipairs(self:_get(self:_get_client().server_capabilities, { 'signatureHelpProvider', 'triggerCharacters' }) + or {}) do + table.insert(trigger_characters, c) + end + for _, c in ipairs(self:_get(self:_get_client().server_capabilities, { 'signatureHelpProvider', 'retriggerCharacters' }) + or {}) do + table.insert(trigger_characters, c) + end + table.insert(trigger_characters, ' ') + return trigger_characters +end + +source.complete = function(self, params, callback) + local client = self:_get_client() + local trigger_characters = {} + for _, c in ipairs(self:_get(client.server_capabilities, { 'signatureHelpProvider', 'triggerCharacters' }) or {}) do + table.insert(trigger_characters, c) + end + for _, c in ipairs(self:_get(client.server_capabilities, { 'signatureHelpProvider', 'retriggerCharacters' }) or {}) do + table.insert(trigger_characters, c) + end + + local trigger_character = nil + for _, c in ipairs(trigger_characters) do + local s, e = string.find(params.context.cursor_before_line, '(' .. vim.pesc(c) .. ')%s*$') + if s and e then + trigger_character = string.sub(params.context.cursor_before_line, s, s) + break + end + end + if not trigger_character then + return callback({ isIncomplete = true }) + end + + local request = vim.lsp.util.make_position_params(0, self:_get_client().offset_encoding) + request.context = { + triggerKind = 2, + triggerCharacter = trigger_character, + isRetrigger = not not self.signature_help, + activeSignatureHelp = self.signature_help, + } + client.request('textDocument/signatureHelp', request, function(_, signature_help) + self.signature_help = signature_help + + if not signature_help then + return callback({ isIncomplete = true }) + end + + self.signature_help.activeSignature = self.signature_help.activeSignature or 0 + callback({ + isIncomplete = true, + items = self:_items(self.signature_help), + }) + end) +end + +source._items = function(self, signature_help) + if not signature_help or not signature_help.signatures then + return {} + end + + local items = {} + for _, signature in ipairs(signature_help.signatures) do + local item = self:_item(signature, signature_help.activeParameter) + if item then + table.insert(items, item) + end + end + + return items +end + +source._item = function(self, signature, parameter_index) + local parameters = signature.parameters + if not parameters then + return nil + end + + parameter_index = (signature.activeParameter or parameter_index or 0) + 1 + + -- @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#signatureHelp + if #parameters < parameter_index or parameter_index < 1 then + parameter_index = 1 + end + + local arguments = {} + for i, parameter in ipairs(parameters) do + if i == parameter_index then + table.insert(arguments, self:_parameter_label(signature, parameter)) + end + end + + if #arguments == 0 then + return nil + end + + local label = table.concat(arguments, ', ') + return { + label = label, + filterText = ' ', + insertText = self:_matchstr(label, [[\k\+]]), + word = '', + preselect = true, + documentation = self:_docs(signature, parameter_index), + } +end + +source._docs = function(self, signature, parameter_index) + local documentation = {} + + -- signature label. + if signature.label then + table.insert(documentation, self:_signature_label(signature, parameter_index)) + end + + -- parameter docs. + local parameter = signature.parameters[parameter_index] + if parameter then + if parameter.documentation then + table.insert(documentation, '---') + if type(parameter.documentation) == 'table' then + table.insert(documentation, '```' .. parameter.documentation.kind) + table.insert(documentation, parameter.documentation.value) + table.insert(documentation, '```') + else + table.insert(documentation, parameter.documentation) + end + end + end + + -- signature docs. + if signature.documentation then + table.insert(documentation, '---') + if type(signature.documentation) == 'table' then + table.insert(documentation, '```' .. signature.documentation.kind) + table.insert(documentation, signature.documentation.value) + table.insert(documentation, '```') + else + table.insert(documentation, signature.documentation) + end + end + + return { kind = 'markdown', value = table.concat(documentation, '\n') } +end + +source._signature_label = function(self, signature, parameter_index) + local label = signature.label + if parameter_index then + local s, e = string.find(label, self:_parameter_label(signature, signature.parameters[parameter_index]), 1, true) + if s and e then + local active = string.sub(label, s, e) + label = string.gsub(label, vim.pesc(active), '***' .. active .. '***') + end + end + return label +end + +source._parameter_label = function(_, signature, parameter) + local label = parameter.label + if type(label) == 'table' then + label = signature.label:sub( + 1 + vim.str_byteindex(signature.label, label[1]), + vim.str_byteindex(signature.label, label[2]) + ) + end + return label +end + +source._get_client = function(self) + local get_clients = vim.lsp.get_clients or vim.lsp.buf_get_clients + for _, client in pairs(get_clients()) do + if self:_get(client.server_capabilities, { 'signatureHelpProvider' }) then + return client + end + end + return nil +end + +source._get = function(_, root, paths) + local c = root + for _, path in ipairs(paths) do + c = c[path] + if not c then + return nil + end + end + return c +end + +source._matchstr = function(_, str, pattern) + local s, e = vim.regex(pattern):match_str(str) + if s and e then + return string.sub(str, s+1, e) + end + return '' +end + +return source diff --git a/config/neovim/store/lazy-plugins/cmp-nvim-lsp/.gitignore b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/.gitignore new file mode 100644 index 00000000..0a56e3fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/.gitignore @@ -0,0 +1 @@ +/doc/tags diff --git a/config/neovim/store/lazy-plugins/cmp-nvim-lsp/LICENSE b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/LICENSE new file mode 100644 index 00000000..ae725ef1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 hrsh7th + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/cmp-nvim-lsp/README.md b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/README.md new file mode 100644 index 00000000..dba8c530 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/README.md @@ -0,0 +1,59 @@ +# cmp-nvim-lsp + +nvim-cmp source for neovim's built-in language server client. + +## Capabilities + +Language servers provide different completion results depending on the capabilities of the client. Neovim's default omnifunc has basic support for serving completion candidates. nvim-cmp supports more types of completion candidates, so users must override the capabilities sent to the server such that it can provide these candidates during a completion request. These capabilities are provided via the helper function `require('cmp_nvim_lsp').default_capabilities` + +As these candidates are sent on each request, **adding these capabilities will break the built-in omnifunc support for neovim's language server client**. `nvim-cmp` provides manually triggered completion that can replace omnifunc. See `:help cmp-faq` for more details. + +## Setup + +```lua + +require'cmp'.setup { + sources = { + { name = 'nvim_lsp' } + } +} + +-- The nvim-cmp almost supports LSP's capabilities so You should advertise it to LSP servers.. +local capabilities = require('cmp_nvim_lsp').default_capabilities() + +-- An example for configuring `clangd` LSP to use nvim-cmp as a completion engine +require('lspconfig').clangd.setup { + capabilities = capabilities, + ... -- other lspconfig configs +} +``` + +## Option + +`[%LSPCONFIG-NAME%].keyword_pattern` + +You can override keyword_pattern for specific language-server like this. + +```lua +cmp.setup { + ... + sources = { + { + name = 'nvim_lsp', + option = { + php = { + keyword_pattern = [=[[\%(\$\k*\)\|\k\+]]=], + } + } + } + } + ... +} +``` + + +## Readme! + +1. There is a Github issue that documents [breaking changes](https://github.com/hrsh7th/cmp-nvim-lsp/issues/38) for cmp-nvim-lsp. Subscribe to the issue to be notified of upcoming breaking changes. +2. This is my hobby project. You can support me via GitHub sponsors. +3. Bug reports are welcome, but don't expect a fix unless you provide minimal configuration and steps to reproduce your issue. diff --git a/config/neovim/store/lazy-plugins/cmp-nvim-lsp/after/plugin/cmp_nvim_lsp.lua b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/after/plugin/cmp_nvim_lsp.lua new file mode 100644 index 00000000..6d566fa0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/after/plugin/cmp_nvim_lsp.lua @@ -0,0 +1 @@ +require('cmp_nvim_lsp').setup() diff --git a/config/neovim/store/lazy-plugins/cmp-nvim-lsp/doc/cmp-nvim-lsp.txt b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/doc/cmp-nvim-lsp.txt new file mode 100644 index 00000000..28e3700a --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/doc/cmp-nvim-lsp.txt @@ -0,0 +1,29 @@ +*cmp-nvim-lsp* + +============================================================================== +CONTENTS *cmp-nvim-lsp-contents* + +FAQ |cmp-nvim-lsp-faq| + + + +============================================================================== +FAQ *cmp-nvim-lsp-faq* + +How to disable specific LSP server's cmpletion?~ + + You can disable specific LSP server's cmpletion by adding the following + +> + require('lspconfig')[%YOUR_LSP_SERVER%].setup { + on_attach = function(client) + client.server_capabilities.completionProvider = false + end + } +< + + + +============================================================================== + vim:tw=78:ts=2:et:ft=help:norl: + diff --git a/config/neovim/store/lazy-plugins/cmp-nvim-lsp/lua/cmp_nvim_lsp/init.lua b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/lua/cmp_nvim_lsp/init.lua new file mode 100644 index 00000000..23722af3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/lua/cmp_nvim_lsp/init.lua @@ -0,0 +1,142 @@ +local source = require('cmp_nvim_lsp.source') + +local M = {} + +---Registered client and source mapping. +M.client_source_map = {} + +---Setup cmp-nvim-lsp source. +M.setup = function() + vim.api.nvim_create_autocmd('InsertEnter', { + group = vim.api.nvim_create_augroup('cmp_nvim_lsp', { clear = true }), + pattern = '*', + callback = M._on_insert_enter + }) +end + +local if_nil = function(val, default) + if val == nil then return default end + return val +end + +-- Backported from vim.deprecate (0.9.0+) +local function deprecate(name, alternative, version, plugin, backtrace) + local message = name .. ' is deprecated' + plugin = plugin or 'Nvim' + message = alternative and (message .. ', use ' .. alternative .. ' instead.') or message + message = message + .. ' See :h deprecated\nThis function will be removed in ' + .. plugin + .. ' version ' + .. version + if vim.notify_once(message, vim.log.levels.WARN) and backtrace ~= false then + vim.notify(debug.traceback('', 2):sub(2), vim.log.levels.WARN) + end +end + +M.default_capabilities = function(override) + override = override or {} + + return { + textDocument = { + completion = { + dynamicRegistration = if_nil(override.dynamicRegistration, false), + completionItem = { + snippetSupport = if_nil(override.snippetSupport, true), + commitCharactersSupport = if_nil(override.commitCharactersSupport, true), + deprecatedSupport = if_nil(override.deprecatedSupport, true), + preselectSupport = if_nil(override.preselectSupport, true), + tagSupport = if_nil(override.tagSupport, { + valueSet = { + 1, -- Deprecated + } + }), + insertReplaceSupport = if_nil(override.insertReplaceSupport, true), + resolveSupport = if_nil(override.resolveSupport, { + properties = { + "documentation", + "detail", + "additionalTextEdits", + "sortText", + "filterText", + "insertText", + "textEdit", + "insertTextFormat", + "insertTextMode", + }, + }), + insertTextModeSupport = if_nil(override.insertTextModeSupport, { + valueSet = { + 1, -- asIs + 2, -- adjustIndentation + } + }), + labelDetailsSupport = if_nil(override.labelDetailsSupport, true), + }, + contextSupport = if_nil(override.snippetSupport, true), + insertTextMode = if_nil(override.insertTextMode, 1), + completionList = if_nil(override.completionList, { + itemDefaults = { + 'commitCharacters', + 'editRange', + 'insertTextFormat', + 'insertTextMode', + 'data', + } + }) + }, + }, + } +end + +---Backwards compatibility +M.update_capabilities = function(_, override) + local _deprecate = vim.deprecate or deprecate + _deprecate('cmp_nvim_lsp.update_capabilities', 'cmp_nvim_lsp.default_capabilities', '1.0.0', 'cmp-nvim-lsp') + return M.default_capabilities(override) +end + + +---Refresh sources on InsertEnter. +M._on_insert_enter = function() + local cmp = require('cmp') + + local allowed_clients = {} + + local get_clients = ( + vim.lsp.get_clients ~= nil and vim.lsp.get_clients -- nvim 0.10+ + or vim.lsp.get_active_clients + ) + + -- register all active clients. + for _, client in ipairs(get_clients()) do + allowed_clients[client.id] = client + if not M.client_source_map[client.id] then + local s = source.new(client) + if s:is_available() then + M.client_source_map[client.id] = cmp.register_source('nvim_lsp', s) + end + end + end + + -- register all buffer clients (early register before activation) + for _, client in ipairs(get_clients({ bufnr = 0 })) do + allowed_clients[client.id] = client + if not M.client_source_map[client.id] then + local s = source.new(client) + if s:is_available() then + M.client_source_map[client.id] = cmp.register_source('nvim_lsp', s) + end + end + end + + -- unregister stopped/detached clients. + for client_id, source_id in pairs(M.client_source_map) do + if not allowed_clients[client_id] or allowed_clients[client_id]:is_stopped() then + cmp.unregister_source(source_id) + M.client_source_map[client_id] = nil + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/cmp-nvim-lsp/lua/cmp_nvim_lsp/source.lua b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/lua/cmp_nvim_lsp/source.lua new file mode 100644 index 00000000..43ccac10 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp-nvim-lsp/lua/cmp_nvim_lsp/source.lua @@ -0,0 +1,160 @@ +local source = {} + +source.new = function(client) + local self = setmetatable({}, { __index = source }) + self.client = client + self.request_ids = {} + return self +end + +---Get debug name. +---@return string +source.get_debug_name = function(self) + return table.concat({ 'nvim_lsp', self.client.name }, ':') +end + +---Return the source is available. +---@return boolean +source.is_available = function(self) + -- client is stopped. + if self.client.is_stopped() then + return false + end + + -- client is not attached to current buffer. + local bufnr = vim.api.nvim_get_current_buf() + local get_clients = ( + vim.lsp.get_clients ~= nil and vim.lsp.get_clients -- nvim 0.10+ + or vim.lsp.get_active_clients + ) + if vim.tbl_isempty(get_clients({ bufnr = bufnr, id = self.client.id })) then + return false + end + + -- client has no completion capability. + if not self:_get(self.client.server_capabilities, { 'completionProvider' }) then + return false + end + return true; +end + +---Get LSP's PositionEncodingKind. +---@return lsp.PositionEncodingKind +source.get_position_encoding_kind = function(self) + return self:_get(self.client.server_capabilities, { 'positionEncoding' }) or self.client.offset_encoding or 'utf-16' +end + +---Get triggerCharacters. +---@return string[] +source.get_trigger_characters = function(self) + return self:_get(self.client.server_capabilities, { 'completionProvider', 'triggerCharacters' }) or {} +end + +---Get get_keyword_pattern. +---@param params cmp.SourceApiParams +---@return string +source.get_keyword_pattern = function(self, params) + local option + option = params.option or {} + option = option[self.client.name] or {} + return option.keyword_pattern or require('cmp').get_config().completion.keyword_pattern +end + +---Resolve LSP CompletionItem. +---@param params cmp.SourceCompletionApiParams +---@param callback function +source.complete = function(self, params, callback) + local lsp_params = vim.lsp.util.make_position_params(0, self.client.offset_encoding) + lsp_params.context = {} + lsp_params.context.triggerKind = params.completion_context.triggerKind + lsp_params.context.triggerCharacter = params.completion_context.triggerCharacter + self:_request('textDocument/completion', lsp_params, function(_, response) + callback(response) + end) +end + +---Resolve LSP CompletionItem. +---@param completion_item lsp.CompletionItem +---@param callback function +source.resolve = function(self, completion_item, callback) + -- client is stopped. + if self.client.is_stopped() then + return callback() + end + + -- client has no completion capability. + if not self:_get(self.client.server_capabilities, { 'completionProvider', 'resolveProvider' }) then + return callback() + end + + self:_request('completionItem/resolve', completion_item, function(_, response) + callback(response or completion_item) + end) +end + +---Execute LSP CompletionItem. +---@param completion_item lsp.CompletionItem +---@param callback function +source.execute = function(self, completion_item, callback) + -- client is stopped. + if self.client.is_stopped() then + return callback() + end + + -- completion_item has no command. + if not completion_item.command then + return callback() + end + + self:_request('workspace/executeCommand', completion_item.command, function(_, _) + callback() + end) +end + +---Get object path. +---@param root table +---@param paths string[] +---@return any +source._get = function(_, root, paths) + local c = root + for _, path in ipairs(paths) do + c = c[path] + if not c then + return nil + end + end + return c +end + +---Send request to nvim-lsp servers with backward compatibility. +---@param method string +---@param params table +---@param callback function +source._request = function(self, method, params, callback) + if self.request_ids[method] ~= nil then + self.client.cancel_request(self.request_ids[method]) + self.request_ids[method] = nil + end + local _, request_id + _, request_id = self.client.request(method, params, function(arg1, arg2, arg3) + if self.request_ids[method] ~= request_id then + return + end + self.request_ids[method] = nil + + -- Text changed, retry + if arg1 and arg1.code == -32801 then + self:_request(method, params, callback) + return + end + + if method == arg2 then + callback(arg1, arg3) -- old signature + else + callback(arg1, arg2) -- new signature + end + end) + self.request_ids[method] = request_id +end + +return source diff --git a/config/neovim/store/lazy-plugins/cmp_luasnip/LICENSE b/config/neovim/store/lazy-plugins/cmp_luasnip/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp_luasnip/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/config/neovim/store/lazy-plugins/cmp_luasnip/README.md b/config/neovim/store/lazy-plugins/cmp_luasnip/README.md new file mode 100644 index 00000000..eb692f3d --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp_luasnip/README.md @@ -0,0 +1,55 @@ +# cmp_luasnip + +[luasnip](https://github.com/L3MON4D3/LuaSnip) completion source for [nvim-cmp](https://github.com/hrsh7th/nvim-cmp) + +```lua +-- Installation +use { 'L3MON4D3/LuaSnip' } +use { + 'hrsh7th/nvim-cmp', + config = function () + require'cmp'.setup { + snippet = { + expand = function(args) + require'luasnip'.lsp_expand(args.body) + end + }, + + sources = { + { name = 'luasnip' }, + -- more sources + }, + } + end +} +use { 'saadparwaiz1/cmp_luasnip' } +``` + +To disable filtering completion candidates by snippet's `show_condition` +use the following options in `sources`: + +```lua +sources = { + { name = 'luasnip', option = { use_show_condition = false } }, + -- more sources +}, +``` + +This can also be configured on per-buffer basis as described in cmp's README +[here](https://github.com/hrsh7th/nvim-cmp#how-to-disable-nvim-cmp-on-the-specific-buffer) +and [here](https://github.com/hrsh7th/nvim-cmp#sources-type-tablecmpsourceconfig). + +The same way you can de-/activate whether autosnippets should be included in the +completion list or not (including them can be a bit problematic since when you +select the entry, the text gets inserted and the snippet automatically +triggers). This option defaults to `false` to be backwards compatible. Example: + +```lua +sources = { + { name = 'luasnip', option = { show_autosnippets = true } }, + -- more sources +}, +``` + +Hint: If you want to just hide some autosnippets consider the `hidden` option of +luaSnip diff --git a/config/neovim/store/lazy-plugins/cmp_luasnip/after/plugin/cmp_luasnip.lua b/config/neovim/store/lazy-plugins/cmp_luasnip/after/plugin/cmp_luasnip.lua new file mode 100644 index 00000000..0ddc5b6c --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp_luasnip/after/plugin/cmp_luasnip.lua @@ -0,0 +1,19 @@ +require("cmp").register_source("luasnip", require("cmp_luasnip").new()) + +local cmp_luasnip = vim.api.nvim_create_augroup("cmp_luasnip", {}) + +vim.api.nvim_create_autocmd("User", { + pattern = "LuasnipCleanup", + callback = function () + require("cmp_luasnip").clear_cache() + end, + group = cmp_luasnip +}) + +vim.api.nvim_create_autocmd("User", { + pattern = "LuasnipSnippetsAdded", + callback = function () + require("cmp_luasnip").refresh() + end, + group = cmp_luasnip +}) diff --git a/config/neovim/store/lazy-plugins/cmp_luasnip/lua/cmp_luasnip/init.lua b/config/neovim/store/lazy-plugins/cmp_luasnip/lua/cmp_luasnip/init.lua new file mode 100644 index 00000000..46415795 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp_luasnip/lua/cmp_luasnip/init.lua @@ -0,0 +1,178 @@ +local cmp = require("cmp") +local util = require("vim.lsp.util") + +local source = {} + +local defaults = { + use_show_condition = true, + show_autosnippets = false, +} + +-- the options are being passed via cmp.setup.sources, e.g. +-- require('cmp').setup { sources = { { name = 'luasnip', opts = {...} } } } +local function init_options(params) + params.option = vim.tbl_deep_extend('keep', params.option, defaults) + vim.validate({ + use_show_condition = { params.option.use_show_condition, 'boolean' }, + show_autosnippets = { params.option.show_autosnippets, 'boolean' }, + }) +end + +local snip_cache = {} +local doc_cache = {} + +source.clear_cache = function() + snip_cache = {} + doc_cache = {} +end + +source.refresh = function() + local ft = require("luasnip.session").latest_load_ft + snip_cache[ft] = nil + doc_cache[ft] = nil +end + +local function get_documentation(snip, data) + local header = (snip.name or "") .. " _ `[" .. data.filetype .. "]`\n" + local docstring = { "", "```" .. vim.bo.filetype, snip:get_docstring(), "```" } + local documentation = { header .. "---", (snip.dscr or ""), docstring } + documentation = util.convert_input_to_markdown_lines(documentation) + documentation = table.concat(documentation, "\n") + + doc_cache[data.filetype] = doc_cache[data.filetype] or {} + doc_cache[data.filetype][data.snip_id] = documentation + return documentation +end + +source.new = function() + return setmetatable({}, { __index = source }) +end + +source.get_keyword_pattern = function() + return "\\%([^[:alnum:][:blank:]]\\|\\w\\+\\)" +end + +function source:is_available() + local ok, _ = pcall(require, "luasnip") + return ok +end + +function source:get_debug_name() + return "luasnip" +end + +function source:complete(params, callback) + init_options(params) + + local filetypes = require("luasnip.util.util").get_snippet_filetypes() + local items = {} + + for i = 1, #filetypes do + local ft = filetypes[i] + if not snip_cache[ft] then + -- ft not yet in cache. + local ft_items = {} + local ft_table = require("luasnip").get_snippets(ft, {type = "snippets"}) + local iter_tab + if params.option.show_autosnippets then + local auto_table = require('luasnip').get_snippets(ft, {type="autosnippets"}) + iter_tab = {{ft_table, false}, {auto_table, true}} + else + iter_tab = {{ft_table, false}} + end + for _,ele in ipairs(iter_tab) do + local tab,auto = unpack(ele) + for j, snip in pairs(tab) do + if not snip.hidden then + ft_items[#ft_items + 1] = { + word = snip.trigger, + label = snip.trigger, + kind = cmp.lsp.CompletionItemKind.Snippet, + data = { + filetype = ft, + snip_id = snip.id, + show_condition = snip.show_condition, + auto = auto + }, + } + end + end + end + snip_cache[ft] = ft_items + end + vim.list_extend(items, snip_cache[ft]) + end + + if params.option.use_show_condition then + local line_to_cursor = params.context.cursor_before_line + items = vim.tbl_filter(function(i) + -- check if show_condition exists in case (somehow) user updated cmp_luasnip but not luasnip + return not i.data.show_condition or i.data.show_condition(line_to_cursor) + end, items) + end + + callback(items) +end + +function source:resolve(completion_item, callback) + local item_snip_id = completion_item.data.snip_id + local snip = require("luasnip").get_id_snippet(item_snip_id) + local doc_itm = doc_cache[completion_item.data.filetype] or {} + doc_itm = doc_itm[completion_item.data.snip_id] or get_documentation(snip, completion_item.data) + completion_item.documentation = { + kind = cmp.lsp.MarkupKind.Markdown, + value = doc_itm, + } + callback(completion_item) +end + +function source:execute(completion_item, callback) + local snip = require("luasnip").get_id_snippet(completion_item.data.snip_id) + + -- if trigger is a pattern, expand "pattern" instead of actual snippet. + if snip.regTrig then + snip = snip:get_pattern_expand_helper() + end + + local cursor = vim.api.nvim_win_get_cursor(0) + -- get_cursor returns (1,0)-indexed position, clear_region expects (0,0)-indexed. + cursor[1] = cursor[1] - 1 + local line = require("luasnip.util.util").get_current_line_to_cursor() + + local expand_params = snip:matches(line) + + local clear_region = { + from = { + cursor[1], + cursor[2] - #completion_item.word + }, + to = cursor + } + if expand_params ~= nil then + if expand_params.clear_region ~= nil then + clear_region = expand_params.clear_region + else + if expand_params.trigger ~= nil then + clear_region = { + from = { + cursor[1], + cursor[2] - #expand_params.trigger, + }, + to = cursor, + } + end + end + end + + -- text cannot be cleared before, as TM_CURRENT_LINE and + -- TM_CURRENT_WORD couldn't be set correctly. + require("luasnip").snip_expand(snip, { + -- clear word inserted into buffer by cmp. + -- cursor is currently behind word. + clear_region = clear_region, + expand_params = expand_params, + }) + callback(completion_item) +end + +return source diff --git a/config/neovim/store/lazy-plugins/cmp_luasnip/stylua.toml b/config/neovim/store/lazy-plugins/cmp_luasnip/stylua.toml new file mode 100644 index 00000000..25e10305 --- /dev/null +++ b/config/neovim/store/lazy-plugins/cmp_luasnip/stylua.toml @@ -0,0 +1,6 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Tabs" +indent_width = 4 +quote_style = "AutoPreferDouble" +no_call_parentheses = false diff --git a/config/neovim/store/lazy-plugins/comment.nvim/.github/workflows/ci.yaml b/config/neovim/store/lazy-plugins/comment.nvim/.github/workflows/ci.yaml new file mode 100644 index 00000000..0b734385 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/.github/workflows/ci.yaml @@ -0,0 +1,33 @@ +name: ci + +on: + push: + paths: + - "**.lua" + branches: + - master + +env: + PLUGIN_NAME: Comment + +jobs: + docs: + runs-on: ubuntu-latest + name: emmylua to help doc + steps: + - uses: actions/checkout@v2 + + - name: Generating help + shell: bash + run: | + curl -Lq https://github.com/numToStr/lemmy-help/releases/latest/download/lemmy-help-x86_64-unknown-linux-gnu.tar.gz | tar xz + ./lemmy-help -fact \ + lua/Comment/{init.lua,config.lua} plugin/Comment.lua \ + lua/Comment/{api.lua,ft.lua,utils.lua,opfunc.lua,extra.lua} > doc/${{env.PLUGIN_NAME}}.txt + + - name: Commit + uses: stefanzweifel/git-auto-commit-action@v4 + with: + branch: ${{ github.head_ref }} + commit_message: "chore(docs): auto-generate `:help` doc" + file_pattern: doc/*.txt diff --git a/config/neovim/store/lazy-plugins/comment.nvim/.gitignore b/config/neovim/store/lazy-plugins/comment.nvim/.gitignore new file mode 100644 index 00000000..8aa6b8d5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/.gitignore @@ -0,0 +1,46 @@ +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +tmp +scratch + +# ignore generated doc tags +doc/tags diff --git a/config/neovim/store/lazy-plugins/comment.nvim/LICENSE b/config/neovim/store/lazy-plugins/comment.nvim/LICENSE new file mode 100644 index 00000000..e7f6a020 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Vikas Raj + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/comment.nvim/README.md b/config/neovim/store/lazy-plugins/comment.nvim/README.md new file mode 100644 index 00000000..0f8454cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/README.md @@ -0,0 +1,360 @@ +

// Comment.nvim

+

⚡ Smart and Powerful commenting plugin for neovim ⚡

+ +![Comment.nvim](https://user-images.githubusercontent.com/42532967/136532939-926a8350-84b7-4e78-b045-fe21b5947388.gif "Commenting go brrrr") + +### ✨ Features + +- Supports treesitter. [Read more](#treesitter) +- Supports `commentstring`. Read `:h comment.commentstring` +- Supports line (`//`) and block (`/* */`) comments +- Dot (`.`) repeat support for `gcc`, `gbc` and friends +- Count support for `[count]gcc` and `[count]gbc` +- Left-right (`gcw` `gc$`) and Up-Down (`gc2j` `gc4k`) motions +- Use with text-objects (`gci{` `gbat`) +- Supports pre and post hooks +- Ignore certain lines, powered by Lua regex + +### 🚀 Installation + +- With [lazy.nvim](https://github.com/folke/lazy.nvim) + +```lua +-- add this to your lua/plugins.lua, lua/plugins/init.lua, or the file you keep your other plugins: +{ + 'numToStr/Comment.nvim', + opts = { + -- add any options here + }, + lazy = false, +} + +``` + +- With [packer.nvim](https://github.com/wbthomason/packer.nvim) + +```lua +use { + 'numToStr/Comment.nvim', + config = function() + require('Comment').setup() + end +} +``` + +- With [vim-plug](https://github.com/junegunn/vim-plug) + +```vim +Plug 'numToStr/Comment.nvim' + +" Somewhere after plug#end() +lua require('Comment').setup() +``` + +### 📖 Getting Help + +`Comment.nvim` provides help docs which can be accessed by running `:help comment-nvim` + + + +### ⚒️ Setup + +First you need to call the `setup()` method to create the default mappings. + +> **Note** - If you are facing **Keybindings are mapped but they are not working** issue then please try [this](https://github.com/numToStr/Comment.nvim/issues/115#issuecomment-1032290098) + +- Lua + +```lua +require('Comment').setup() +``` + +- VimL + +```vim +lua << EOF +require('Comment').setup() +EOF +``` + + + +#### Configuration (optional) + +Following are the **default** config for the [`setup()`](#setup). If you want to override, just modify the option that you want then it will be merged with the default config. Read `:h comment.config` for more info. + +```lua +{ + ---Add a space b/w comment and the line + padding = true, + ---Whether the cursor should stay at its position + sticky = true, + ---Lines to be ignored while (un)comment + ignore = nil, + ---LHS of toggle mappings in NORMAL mode + toggler = { + ---Line-comment toggle keymap + line = 'gcc', + ---Block-comment toggle keymap + block = 'gbc', + }, + ---LHS of operator-pending mappings in NORMAL and VISUAL mode + opleader = { + ---Line-comment keymap + line = 'gc', + ---Block-comment keymap + block = 'gb', + }, + ---LHS of extra mappings + extra = { + ---Add comment on the line above + above = 'gcO', + ---Add comment on the line below + below = 'gco', + ---Add comment at the end of line + eol = 'gcA', + }, + ---Enable keybindings + ---NOTE: If given `false` then the plugin won't create any mappings + mappings = { + ---Operator-pending mapping; `gcc` `gbc` `gc[count]{motion}` `gb[count]{motion}` + basic = true, + ---Extra mapping; `gco`, `gcO`, `gcA` + extra = true, + }, + ---Function to call before (un)comment + pre_hook = nil, + ---Function to call after (un)comment + post_hook = nil, +} +``` + +### 🔥 Usage + +When you call [`setup()`](#setup) method, `Comment.nvim` sets up some basic mapping which can be used in NORMAL and VISUAL mode to get you started with the pleasure of commenting stuff out. + + + +#### Basic mappings + +These mappings are enabled by default. (config: `mappings.basic`) + +- NORMAL mode + +```help +`gcc` - Toggles the current line using linewise comment +`gbc` - Toggles the current line using blockwise comment +`[count]gcc` - Toggles the number of line given as a prefix-count using linewise +`[count]gbc` - Toggles the number of line given as a prefix-count using blockwise +`gc[count]{motion}` - (Op-pending) Toggles the region using linewise comment +`gb[count]{motion}` - (Op-pending) Toggles the region using blockwise comment +``` + +- VISUAL mode + +```help +`gc` - Toggles the region using linewise comment +`gb` - Toggles the region using blockwise comment +``` + + + +#### Extra mappings + +These mappings are enabled by default. (config: `mappings.extra`) + +- NORMAL mode + +```help +`gco` - Insert comment to the next line and enters INSERT mode +`gcO` - Insert comment to the previous line and enters INSERT mode +`gcA` - Insert comment to end of the current line and enters INSERT mode +``` + +##### Examples + +```help +# Linewise + +`gcw` - Toggle from the current cursor position to the next word +`gc$` - Toggle from the current cursor position to the end of line +`gc}` - Toggle until the next blank line +`gc5j` - Toggle 5 lines after the current cursor position +`gc8k` - Toggle 8 lines before the current cursor position +`gcip` - Toggle inside of paragraph +`gca}` - Toggle around curly brackets + +# Blockwise + +`gb2}` - Toggle until the 2 next blank line +`gbaf` - Toggle comment around a function (w/ LSP/treesitter support) +`gbac` - Toggle comment around a class (w/ LSP/treesitter support) +``` + + + +### 🌳 Treesitter + +This plugin has native **treesitter** support for calculating `commentstring` which works for multiple (injected/embedded) languages like Vue or Markdown. But due to the nature of the parsed tree, this implementation has some known limitations. + +1. No `jsx/tsx` support. Its implementation was quite complicated. +2. Invalid comment on the region where one language ends and the other starts. [Read more](https://github.com/numToStr/Comment.nvim/pull/62#issuecomment-972790418) +3. Unexpected comment on a line with multiple languages. [#144](https://github.com/numToStr/Comment.nvim/issues/144) + +For advance use cases, use [nvim-ts-context-commentstring](https://github.com/JoosepAlviste/nvim-ts-context-commentstring). See [`pre_hook`](#pre-hook) section for the integration. + +> **Note** - This plugin does not depend on [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter) however it is recommended in order to easily install tree-sitter parsers. + + + +### 🎣 Hooks + +There are two hook methods i.e `pre_hook` and `post_hook` which are called before comment and after comment respectively. Both should be provided during [`setup()`](#setup). + + + +- `pre_hook` - Called with a `ctx` argument (Read `:h comment.utils.CommentCtx`) before (un)comment. Can optionally return a `commentstring` to be used for (un)commenting. + +```lua +{ + pre_hook = function(ctx) + if ctx.range.srow == ctx.range.erow then + -- do something with the current line + else + -- do something with lines range + end + end, +} +``` + +You can also integrate [nvim-ts-context-commentstring](https://github.com/JoosepAlviste/nvim-ts-context-commentstring#commentnvim) using `pre_hook` to easily comment `tsx/jsx` files. + +> **Note** - `Comment.nvim` already supports [`treesitter`](#treesitter) out-of-the-box for all the languages except `tsx/jsx`. + +```lua +{ + pre_hook = require('ts_context_commentstring.integrations.comment_nvim').create_pre_hook(), +} +``` + + + +- `post_hook` - This method is called after (un)commenting. It receives the same `ctx` (Read `:h comment.utils.CommentCtx`) argument as [`pre_hook`](#pre_hook). + +```lua +{ + post_hook = function(ctx) + if ctx.range.srow == ctx.range.erow then + -- do something with the current line + else + -- do something with lines range + end + end, +} +``` + +The `post_hook` can be implemented to cover some niche use cases like the following: + +- Using newlines instead of padding e.g. for commenting out code in C with `#if 0`. See an example [here](https://github.com/numToStr/Comment.nvim/issues/38#issuecomment-945082507). +- Duplicating the commented block (using `pre_hook`) and moving the cursor to the next block (using `post_hook`). See [this](https://github.com/numToStr/Comment.nvim/issues/70). + +> NOTE: When pressing `gc`, `gb` and friends, `cmode` (Comment mode) inside `pre_hook` will always be toggle because when pre-hook is called, in that moment we don't know whether `gc` or `gb` will comment or uncomment the lines. But luckily, we do know this before `post_hook` and this will always receive either comment or uncomment status + +### 🚫 Ignoring lines + +You can use `ignore` to ignore certain lines during comment/uncomment. It can takes lua regex string or a function that returns a regex string and should be provided during [`setup()`](#setup). + +> NOTE: Ignore only works when with linewise comment. This is by design. As ignoring lines in block comments doesn't make that much sense. + +- With `string` + +```lua +-- ignores empty lines +ignore = '^$' + +-- ignores line that starts with `local` (excluding any leading whitespace) +ignore = '^(%s*)local' + +-- ignores any lines similar to arrow function +ignore = '^const(.*)=(%s?)%((.*)%)(%s?)=>' +``` + +- With `function` + +```lua +{ + ignore = function() + -- Only ignore empty lines for lua files + if vim.bo.filetype == 'lua' then + return '^$' + end + end, +} +``` + + + +### 🗨️ Filetypes + Languages + +Most languages/filetypes have native support for comments via `commentstring` but there might be a filetype that is not supported. There are two ways to enable commenting for unsupported filetypes: + +1. You can set `commentstring` for that particular filetype like the following. Read `:h commentstring` for more info. + +```lua +vim.bo.commentstring = '//%s' + +-- or +vim.api.nvim_command('set commentstring=//%s') +``` + + + +2. You can also use this plugin interface to store both line and block commentstring for the filetype. You can treat this as a more powerful version of the `commentstring`. Read `:h comment.ft` for more info. + +```lua +local ft = require('Comment.ft') + +-- 1. Using set function + +ft + -- Set only line comment + .set('yaml', '#%s') + -- Or set both line and block commentstring + .set('javascript', {'//%s', '/*%s*/'}) + +-- 2. Metatable magic + +ft.javascript = {'//%s', '/*%s*/'} +ft.yaml = '#%s' + +-- Multiple filetypes +ft({'go', 'rust'}, ft.get('c')) +ft({'toml', 'graphql'}, '#%s') +``` + +> PR(s) are welcome to add more commentstring inside the plugin + +### 🤝 Contributing + +There are multiple ways to contribute reporting/fixing bugs, feature requests. You can also submit commentstring to this plugin by updating [ft.lua](./lua/Comment/ft.lua) and sending PR. + +### 📺 Videos + +- [TakeTuesday E02: Comment.nvim](https://www.youtube.com/watch?v=-InmtHhk2qM) by [TJ DeVries](https://github.com/tjdevries) + +### 💐 Credits + +- [tcomment](https://github.com/tomtom/tcomment_vim) - To be with me forever and motivated me to write this. +- [nvim-comment](https://github.com/terrortylor/nvim-comment) - Little and less powerful cousin. Also I took some code from it. +- [kommentary](https://github.com/b3nj5m1n/kommentary) - Nicely done plugin but lacks some features. But it helped me to design this plugin. + +### 🚗 Roadmap + +- Doc comment i.e `/**%s*/` (js), `///%s` (rust) +- Header comment + +```lua +---------------------- +-- This is a header -- +---------------------- +``` diff --git a/config/neovim/store/lazy-plugins/comment.nvim/doc/API.md b/config/neovim/store/lazy-plugins/comment.nvim/doc/API.md new file mode 100644 index 00000000..ecc574fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/doc/API.md @@ -0,0 +1 @@ +`Comment.nvim` now has `:help` docs 🎉. Read `:h comment.api` for the Lua API documentation and usage. diff --git a/config/neovim/store/lazy-plugins/comment.nvim/doc/Comment.txt b/config/neovim/store/lazy-plugins/comment.nvim/doc/Comment.txt new file mode 100644 index 00000000..2bf2b641 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/doc/Comment.txt @@ -0,0 +1,910 @@ +*comment-nvim.txt* For Neovim version 0.7 Last change: 2021 July 11 + + _____ _ _ + / ____/ / / (_) + / / ___ _ __ ___ _ __ ___ ___ _ __ / /_ _ ____ ___ _ __ ___ + / / / _ \/ '_ ` _ \/ '_ ` _ \ / _ \ '_ \/ __/ / '_ \ \ / / / '_ ` _ \ + / /___/ (_) / / / / / / / / / / / __/ / / / /_ _/ / / \ V // / / / / / / + \_____\___//_/ /_/ /_/_/ /_/ /_/\___/_/ /_/\__(_)_/ /_/\_/ /_/_/ /_/ /_/ + + · Smart and Powerful comment plugin · + + +============================================================================== +Table of Contents *comment.contents* + +Introduction ···················································· |comment-nvim| +Usage ·························································· |comment.usage| +Configuration ················································· |comment.config| +Keybindings ·············································· |comment.keybindings| +Plug Mappings ················································ |comment.plugmap| +Core Lua API ····················································· |comment.api| +Language/Filetype detection ······································· |comment.ft| +Utilities ······················································ |comment.utils| +Operator-mode API ············································· |comment.opfunc| +Extra API ······················································ |comment.extra| + +============================================================================== +Introduction *comment-nvim* + +Comment.nvim is a smart and powerful comment plugin for neovim. It supports +dot-repeat, counts, line ('//') and block ('/* */') comments, and can be used +with motion and text-objects. It has native integration with |treesitter| to +support embedded filetypes like html, vue, markdown with codeblocks etc. + + *comment.dotrepeat* +Comment.nvim uses |operatorfunc| combined with |g@| to support dot-repeat, and +various marks i.e., |'[| |']| |'<| |'>| to deduce the region with the {motion} +argument provided by 'operatorfunc'. See |comment.api.call| + + *comment.commentstring* +Comment.nvim picks commentstring, either linewise/blockwise, from one of the +following places + + 1. 'pre_hook' + If a string is returned from this function then it will be used for + (un)commenting. See |comment.config| + + 2. |comment.ft| + Using the commentstring table inside the plugin (using treesitter). + Fallback to |commentstring|, if not found. + + 3. |commentstring| - Neovim's native commentstring for the filetype + +Although Comment.nvim supports native 'commentstring' but unfortunately it has +the least priority. The caveat with this approach is that if someone sets the +`commentstring`, without returning it, from the 'pre_hook' and the current +filetype also exists in the |comment.ft| then the commenting will be done using +the string in |comment.ft| instead of using 'commentstring'. To override this +behavior, you have to manually return the 'commentstring' from 'pre_hook'. + + *comment.sourcecode* +Comment.nvim is FOSS and distributed under MIT license. All the source code is +available at https://github.com/numToStr/Comment.nvim + +============================================================================== +Usage *comment.usage* + +Before using the plugin, you need to call the `setup()` function to create the +default mappings. If you want, you can also override the default configuration +by giving it a partial 'comment.config.Config' object, it will then be merged +with the default configuration. + +C.setup({config?}) *comment.usage.setup* + Configures the plugin + + Parameters: ~ + {config?} (CommentConfig) User configuration + + Returns: ~ + (CommentConfig) Returns the modified config + + See: ~ + |comment.config| + + Usage: ~ +>lua + -- Use default configuration + require('Comment').setup() + + -- or with custom configuration + require('Comment').setup({ + ignore = '^$', + toggler = { + line = 'cc', + block = 'bc', + }, + opleader = { + line = 'c', + block = 'b', + }, + }) +< + + +============================================================================== +Configuration *comment.config* + + *comment.config.defaults* +Following is the default config for the |comment.usage.setup|. If you want to +override, just modify the option that you want, then it will be merged with the +default config. +>lua + { + padding = true, + sticky = true, + ignore = nil, + toggler = { line = 'gcc', block = 'gbc' }, + opleader = { line = 'gc', block = 'gb' }, + extra = { above = 'gcO', below = 'gco', eol = 'gcA' }, + mappings = { basic = true, extra = true }, + pre_hook = nil, + post_hook = nil, + } +< + +CommentConfig *comment.config.CommentConfig* + Plugin's configuration + + Fields: ~ + {padding} (boolean|fun():boolean) Controls space between the comment + and the line (default: 'true') + {sticky} (boolean) Whether cursor should stay at the + same position. Only works in NORMAL + mode mappings (default: 'true') + {ignore} (string|fun():string) Lua pattern used to ignore lines + during (un)comment (default: 'nil') + {mappings} (Mappings|false) Enables |comment.keybindings| + NOTE: If given 'false', then the + plugin won't create any mappings + {toggler} (Toggler) See |comment.config.Toggler| + {opleader} (Opleader) See |comment.config.Opleader| + {extra} (ExtraMapping) See |comment.config.ExtraMapping| + {pre_hook} (fun(c:CommentCtx):string) Function to call before (un)comment. + It is called with a {ctx} argument + of type |comment.utils.CommentCtx| + (default: 'nil') + {post_hook} (fun(c:CommentCtx)) Function to call after (un)comment. + It is called with a {ctx} argument + of type |comment.utils.CommentCtx| + (default: 'nil') + + +Mappings *comment.config.Mappings* + Create default mappings + + Fields: ~ + {basic} (boolean) Enables operator-pending mapping; `gcc`, `gbc`, + `gc{motion}` and `gb{motion}` (default: 'true') + {extra} (boolean) Enable extra mapping; `gco`, `gcO` and `gcA` + (default: 'true') + + +Toggler *comment.config.Toggler* + LHS of toggle mappings in NORMAL + + Fields: ~ + {line} (string) Linewise comment (default: 'gcc') + {block} (string) Blockwise comment (default: 'gbc') + + +Opleader *comment.config.Opleader* + LHS of operator-mode mappings in NORMAL and VISUAL mode + + Fields: ~ + {line} (string) Linewise comment (default: 'gc') + {block} (string) Blockwise comment (default: 'gb') + + +ExtraMapping *comment.config.ExtraMapping* + LHS of extra mappings + + Fields: ~ + {below} (string) Inserts comment below (default: 'gco') + {above} (string) Inserts comment above (default: 'gcO') + {eol} (string) Inserts comment at the end of line (default: 'gcA') + + +Config:get() *comment.config:get* + Get the config + + Returns: ~ + (CommentConfig) + + Usage: ~ +>lua + require('Comment.config'):get() +< + + +============================================================================== +Keybindings *comment.keybindings* + +Comment.nvim provides default keybindings for (un)comment your code. These +keybinds are enabled upon calling |comment.usage.setup| and can be configured +or disabled, if desired. + +Basic: ~ + + *gc* + *gb* + *gc[count]{motion}* + *gb[count]{motion}* + + Toggle comment on a region using linewise/blockwise comment. In 'NORMAL' + mode, it uses 'Operator-Pending' mode to listen for an operator/motion. + In 'VISUAL' mode it simply comment the selected region. + + *gcc* + *gbc* + *[count]gcc* + *[count]gbc* + + Toggle comment on the current line using linewise/blockwise comment. If + prefixed with a 'v:count' then it will comment over the number of lines + corresponding to the {count}. These are only available in 'NORMAL' mode. + + +Extra: ~ + + *gco* - Inserts comment below and enters INSERT mode + *gcO* - Inserts comment above and enters INSERT mode + *gcA* - Inserts comment at the end of line and enters INSERT mode + +============================================================================== +Plug Mappings *comment.plugmap* + +Comment.nvim provides mappings for most commonly used actions. These +are enabled by default and can be used to make custom keybindings. All plug +mappings have support for dot-repeat except VISUAL mode keybindings. To create +custom comment function, check out 'comment.api' section. + + *(comment_toggle_linewise)* + *(comment_toggle_blockwise)* + + Toggle comment on a region with linewise/blockwise comment in NORMAL mode. + using |Operator-Pending| mode (or |g@|) to get the region to comment. + These powers the |gc| and |gb| keybindings. + + *(comment_toggle_linewise_current)* + *(comment_toggle_blockwise_current)* + + Toggle comment on the current line with linewise/blockwise comment in + NORMAL mode. These powers the |gcc| and 'gbc' keybindings. + + *(comment_toggle_linewise_count)* + *(comment_toggle_blockwise_count)* + + Toggle comment on a region using 'v:count' with linewise/blockwise comment + in NORMAL mode. These powers the |[count]gcc| and |[count]gbc| keybindings. + + *(comment_toggle_linewise_visual)* + *(comment_toggle_blockwise_visual)* + + Toggle comment on the selected region with linewise/blockwise comment in + NORMAL mode. These powers the |{visual}gc| and |{visual}gb| keybindings. + +Usage: ~ +>lua + -- Toggle current line or with count + vim.keymap.set('n', 'gcc', function() + return vim.v.count == 0 + and '(comment_toggle_linewise_current)' + or '(comment_toggle_linewise_count)' + end, { expr = true }) + + -- Toggle in Op-pending mode + vim.keymap.set('n', 'gc', '(comment_toggle_linewise)') + + -- Toggle in VISUAL mode + vim.keymap.set('x', 'gc', '(comment_toggle_linewise_visual)') +< + +============================================================================== +Core Lua API *comment.api* + +This module provides the core lua APIs which is used by the default keybindings +and (Read |comment.plugmap|) mappings. These API can be used to setup your +own custom keybindings or to even make your (un)comment function. + + *comment.api.toggle.linewise* + *comment.api.toggle.blockwise* +api.toggle *comment.api.toggle* + Provides API to toggle comments over a region, on current-line, or with a + count using line or block comment string. + + Every function takes a {motion} argument, except '*.count()' function which + takes an {count} argument, and an optional {config} parameter. + + Type: ~ + (table) A metatable containing API functions + + See: ~ + |comment.opfunc.OpMotion| + |comment.config| + + Usage: ~ +>lua + local api = require('Comment.api') + local config = require('Comment.config'):get() + + api.toggle.linewise(motion, config?) + api.toggle.linewise.current(motion?, config?) + api.toggle.linewise.count(count, config?) + + api.toggle.blockwise(motion, config?) + api.toggle.blockwise.current(motion?, config?) + api.toggle.blockwise.count(count, config?) + + -- Toggle current line (linewise) using C-/ + vim.keymap.set('n', '', api.toggle.linewise.current) + + -- Toggle current line (blockwise) using C-\ + vim.keymap.set('n', '', api.toggle.blockwise.current) + + -- Toggle lines (linewise) with dot-repeat support + -- Example: gc3j will comment 4 lines + vim.keymap.set( + 'n', 'gc', api.call('toggle.linewise', 'g@'), + { expr = true } + ) + + -- Toggle lines (blockwise) with dot-repeat support + -- Example: gb3j will comment 4 lines + vim.keymap.set( + 'n', 'gb', api.call('toggle.blockwise', 'g@'), + { expr = true } + ) + + local esc = vim.api.nvim_replace_termcodes( + '', true, false, true + ) + + -- Toggle selection (linewise) + vim.keymap.set('x', 'c', function() + vim.api.nvim_feedkeys(esc, 'nx', false) + api.toggle.linewise(vim.fn.visualmode()) + end) + + -- Toggle selection (blockwise) + vim.keymap.set('x', 'b', function() + vim.api.nvim_feedkeys(esc, 'nx', false) + api.toggle.blockwise(vim.fn.visualmode()) + end) +< + + + *comment.api.comment.linewise* + *comment.api.comment.blockwise* +api.comment *comment.api.comment* + Provides API to (only) comment a region, on current-line, or with a + count using line or block comment string. + + Every function takes a {motion} argument, except '*.count()' function which + takes an {count} argument, and an optional {config} parameter. + + Type: ~ + (table) A metatable containing API functions + + See: ~ + |comment.opfunc.OpMotion| + |comment.config| + + Usage: ~ +>lua + local api = require('Comment.api') + local config = require('Comment.config'):get() + + api.comment.linewise(motion, config?) + api.comment.linewise.current(motion?, config?) + api.comment.linewise.count(count, config?) + + api.comment.blockwise(motion, config?) + api.comment.blockwise.current(motion?, config?) + api.comment.blockwise.count(count, config?) +< + + + *comment.api.uncomment.linewise* + *comment.api.uncomment.blockwise* +api.uncomment *comment.api.uncomment* + Provides API to (only) uncomment a region, on current-line, or with a + count using line or block comment string. + + Every function takes a {motion} argument, except '*.count()' function which + takes an {count} argument, and an optional {config} parameter. + + Type: ~ + (table) A metatable containing API functions + + See: ~ + |comment.opfunc.OpMotion| + |comment.config| + + Usage: ~ +>lua + local api = require('Comment.api') + local config = require('Comment.config'):get() + + api.uncomment.linewise(motion, config?) + api.uncomment.linewise.current(motion?, config?) + api.uncomment.linewise.count(count, config?) + + api.uncomment.blockwise(motion, config?) + api.uncomment.blockwise.current(motion?, config?) + api.uncomment.blockwise.count(count, config?) +< + + +api.insert *comment.api.insert* + Provides API to to insert comment on previous, next or at the end-of-line. + Every function takes an optional {config} parameter. + + Type: ~ + (table) A metatable containing API functions + + See: ~ + |comment.config| + + Usage: ~ +>lua + local api = require('Comment.api') + local config = require('Comment.config'):get() + + api.insert.linewise.above(config?) + api.insert.linewise.below(config?) + api.insert.linewise.eol(config?) + + api.insert.blockwise.above(config?) + api.insert.blockwise.below(config?) + api.insert.blockwise.eol(config?) +< + + +api.locked({cb}) *comment.api.locked* + Wraps the given API function with 'lockmarks' to preserve marks/jumps + + Parameters: ~ + {cb} (string) Name of API function + + Returns: ~ + (fun(motion:OpMotion)) Callback function + + See: ~ + |lockmarks| + |comment.opfunc.OpMotion| + + Usage: ~ +>lua + local api = require('Comment.api') + + vim.keymap.set( + 'n', 'c', api.locked('toggle.linewise.current') + ) + + local esc = vim.api.nvim_replace_termcodes( + '', true, false, true + ) + vim.keymap.set('x', 'c', function() + vim.api.nvim_feedkeys(esc, 'nx', false) + api.locked('toggle.linewise')(vim.fn.visualmode()) + end) + + -- NOTE: `locked` method is just a wrapper around `lockmarks` + vim.api.nvim_command([[ + lockmarks lua require('Comment.api').toggle.linewise.current() + ]]) +< + + +api.call({cb}, {op}) *comment.api.call* + Callback function which does the following + 1. Sets 'operatorfunc' for dot-repeat + 2. Preserves jumps and marks + 3. Stores last cursor position + + Parameters: ~ + {cb} (string) Name of the API function to call + {op} ("g@"|"g@$") Operator-mode expression + + Returns: ~ + (fun():string) Keymap RHS callback + + See: ~ + |g@| + |operatorfunc| + + Usage: ~ +>lua + local api = require('Comment.api') + vim.keymap.set( + 'n', 'gc', api.call('toggle.linewise', 'g@'), + { expr = true } + ) + vim.keymap.set( + 'n', 'gcc', api.call('toggle.linewise.current', 'g@$'), + { expr = true } + ) +< + + +============================================================================== +Language/Filetype detection *comment.ft* + +This module is the core of filetype and commentstring detection and uses the +|lua-treesitter| APIs to accurately detect filetype and gives the corresponding +commentstring, stored inside the plugin, for the filetype/langauge. + +Compound (dot-separated) filetypes are also supported i.e. 'ansible.yaml', +'ios.swift' etc. The commentstring resolution will be done from left to right. +For example, If the filetype is 'ansible.yaml' then 'ansible' commenstring will +be used if found otherwise it'll fallback to 'yaml'. Read `:h 'filetype'` + +ft.set({lang}, {val}) *comment.ft.set* + Sets a commentstring(s) for a filetype/language + + Parameters: ~ + {lang} (string) Filetype/Language of the buffer + {val} (string|string[]) + + Returns: ~ + (table) Returns itself + + Usage: ~ +>lua + local ft = require('Comment.ft') + + --1. Using method signature + -- Set only line comment or both + -- You can also chain the set calls + ft.set('yaml', '#%s').set('javascript', {'//%s', '/*%s*/'}) + + -- 2. Metatable magic + ft.javascript = {'//%s', '/*%s*/'} + ft.yaml = '#%s' + + -- 3. Multiple filetypes + ft({'go', 'rust'}, {'//%s', '/*%s*/'}) + ft({'toml', 'graphql'}, '#%s') +< + + +ft.get({lang}, {ctype?}) *comment.ft.get* + Get line/block/both commentstring(s) for a given filetype + + Parameters: ~ + {lang} (string) Filetype/Language of the buffer + {ctype?} (integer) See |comment.utils.ctype|. If given `nil`, it'll + return a copy of { line, block } commentstring. + + Returns: ~ + (nil|string|string[]) Returns stored commentstring + + Usage: ~ +>lua + local ft = require('Comment.ft') + local U = require('Comment.utils') + + -- 1. Primary filetype + ft.get('rust', U.ctype.linewise) -- `//%s` + ft.get('rust') -- `{ '//%s', '/*%s*/' }` + + -- 2. Compound filetype + -- NOTE: This will return `yaml` commenstring(s), + -- as `ansible` commentstring is not found. + ft.get('ansible.yaml', U.ctype.linewise) -- `#%s` + ft.get('ansible.yaml') -- { '#%s' } +< + + +ft.contains({tree}, {range}) *comment.ft.contains* + Get a language tree for a given range by walking the parse tree recursively. + This uses 'lua-treesitter' API under the hood. This can be used to calculate + language of a particular region which embedded multiple filetypes like html, + vue, markdown etc. + + NOTE: This ignores `tree-sitter-comment` parser, if installed. + + Parameters: ~ + {tree} (userdata) Parse tree to be walked + {range} (integer[]) Range to check + {start_row, start_col, end_row, end_col} + + Returns: ~ + (userdata) Returns a |treesitter-languagetree| + + See: ~ + |treesitter-languagetree| + |lua-treesitter-core| + + Usage: ~ +>lua + local ok, parser = pcall(vim.treesitter.get_parser, 0) + assert(ok, "No parser found!") + local tree = require('Comment.ft').contains(parser, {0, 0, -1, 0}) + print('Lang:', tree:lang()) +< + + +ft.calculate({ctx}) *comment.ft.calculate* + Calculate commentstring with the power of treesitter + + Parameters: ~ + {ctx} (CommentCtx) + + Returns: ~ + (nil|string) Commentstring + + See: ~ + |comment.utils.CommentCtx| + + +============================================================================== +Utilities *comment.utils* + +CommentCtx *comment.utils.CommentCtx* + Comment context + + Fields: ~ + {ctype} (integer) See |comment.utils.ctype| + {cmode} (integer) See |comment.utils.cmode| + {cmotion} (integer) See |comment.utils.cmotion| + {range} (CommentRange) + + +CommentRange *comment.utils.CommentRange* + Range of the selection that needs to be commented + + Fields: ~ + {srow} (integer) Starting row + {scol} (integer) Starting column + {erow} (integer) Ending row + {ecol} (integer) Ending column + + +CommentMode *comment.utils.CommentMode* + Comment modes - Can be manual or computed via operator-mode + + Fields: ~ + {toggle} (integer) Toggle action + {comment} (integer) Comment action + {uncomment} (integer) Uncomment action + + +U.cmode *comment.utils.cmode* + An object containing comment modes + + Type: ~ + (CommentMode) + + +CommentType *comment.utils.CommentType* + Comment types + + Fields: ~ + {linewise} (integer) Use linewise commentstring + {blockwise} (integer) Use blockwise commentstring + + +U.ctype *comment.utils.ctype* + An object containing comment types + + Type: ~ + (CommentType) + + +CommentMotion *comment.utils.CommentMotion* + Comment motion types + + Fields: ~ + {line} (integer) Line motion (ie. 'gc2j') + {char} (integer) Character/left-right motion (ie. 'gc2w') + {block} (integer) Visual operator-pending motion + {v} (integer) Visual motion (ie. 'v3jgc') + {V} (integer) Visual-line motion (ie. 'V10kgc') + + +U.cmotion *comment.utils.cmotion* + An object containing comment motions + + Type: ~ + (CommentMotion) + + +U.get_region({opmode?}) *comment.utils.get_region* + Get region for line movement or visual selection + NOTE: Returns the current line region, if `opmode` is not given. + + Parameters: ~ + {opmode?} (OpMotion) + + Returns: ~ + (CommentRange) + + +U.get_count_lines({count}) *comment.utils.get_count_lines* + Get lines from the current position to the given count + + Parameters: ~ + {count} (integer) Probably 'vim.v.count' + + Returns: ~ + (string[]) List of lines + (CommentRange) + + +U.get_lines({range}) *comment.utils.get_lines* + Get lines from a NORMAL/VISUAL mode + + Parameters: ~ + {range} (CommentRange) + + Returns: ~ + (string[]) List of lines + + +U.unwrap_cstr({cstr}) *comment.utils.unwrap_cstr* + Validates and unwraps the given commentstring + + Parameters: ~ + {cstr} (string) See 'commentstring' + + Returns: ~ + (string) Left side of the commentstring + (string) Right side of the commentstring + + +U.parse_cstr({cfg}, {ctx}) *comment.utils.parse_cstr* + Parses commentstring from the following places in the respective order + 1. pre_hook - commentstring returned from the function + 2. ft.lua - commentstring table bundled with the plugin + 3. commentstring - Neovim's native. See 'commentstring' + + Parameters: ~ + {cfg} (CommentConfig) + {ctx} (CommentCtx) + + Returns: ~ + (string) Left side of the commentstring + (string) Right side of the commentstring + + + *comment.utils.commenter* +U.commenter({left}, {right}, {padding}, {scol?}, {ecol?}, {tabbed?}) + Returns a closure which is used to do comments + + If given {string[]} to the closure then it will do blockwise comment + else linewise comment will be done with the given {string} + + Parameters: ~ + {left} (string) Left side of the commentstring + {right} (string) Right side of the commentstring + {padding} (boolean) Is padding enabled? + {scol?} (integer) Starting column + {ecol?} (integer) Ending column + {tabbed?} (boolean) Using tab indentation + + Returns: ~ + (fun(line:string|string[]):string|string[]) + + + *comment.utils.uncommenter* +U.uncommenter({left}, {right}, {padding}, {scol?}, {ecol?}) + Returns a closure which is used to uncomment a line + + If given {string[]} to the closure then it will block uncomment + else linewise uncomment will be done with the given {string} + + Parameters: ~ + {left} (string) Left side of the commentstring + {right} (string) Right side of the commentstring + {padding} (boolean) Is padding enabled? + {scol?} (integer) Starting column + {ecol?} (integer) Ending column + + Returns: ~ + (fun(line:string|string[]):string|string[]) + + + *comment.utils.is_commented* +U.is_commented({left}, {right}, {padding}, {scol?}, {ecol?}) + Check if the given string is commented or not + + If given {string[]} to the closure, it will check the first and last line + with LHS and RHS of commentstring respectively else it will check the given + line with LHS and RHS (if given) of the commenstring + + Parameters: ~ + {left} (string) Left side of the commentstring + {right} (string) Right side of the commentstring + {padding} (boolean) Is padding enabled? + {scol?} (integer) Starting column + {ecol?} (integer) Ending column + + Returns: ~ + (fun(line:string|string[]):boolean) + + +============================================================================== +Operator-mode API *comment.opfunc* + +Underlying functions that powers the |comment.api.toggle|, |comment.api.comment|, +and |comment.api.uncomment| lua API. + +OpMotion *comment.opfunc.OpMotion* + Vim operator-mode motion enum. Read |:map-operator| + + Variants: ~ + ("line") Vertical motion + ("char") Horizontal motion + ("v") Visual Block motion + ("V") Visual Line motion + + + *comment.opfunc.opfunc* +Op.opfunc({motion?}, {cfg}, {cmode}, {ctype}) + Common operatorfunc callback + This function contains the core logic for comment/uncomment + + Parameters: ~ + {motion?} (OpMotion) If given 'nil', it'll only (un)comment + the current line + {cfg} (CommentConfig) + {cmode} (integer) See |comment.utils.cmode| + {ctype} (integer) See |comment.utils.ctype| + + + *comment.opfunc.count* +Op.count({count}, {cfg}, {cmode}, {ctype}) + Line commenting with count + + Parameters: ~ + {count} (integer) Value of |v:count| + {cfg} (CommentConfig) + {cmode} (integer) See |comment.utils.cmode| + {ctype} (integer) See |comment.utils.ctype| + + +OpFnParams *comment.opfunc.OpFnParams* + Operator-mode function parameters + + Fields: ~ + {cfg} (CommentConfig) + {cmode} (integer) See |comment.utils.cmode| + {lines} (string[]) List of lines + {rcs} (string) RHS of commentstring + {lcs} (string) LHS of commentstring + {range} (CommentRange) + + +Op.linewise({param}) *comment.opfunc.linewise* + Line commenting + + Parameters: ~ + {param} (OpFnParams) + + Returns: ~ + (integer) Returns a calculated comment mode + + +Op.blockwise({param}, {partial?}) *comment.opfunc.blockwise* + Full/Partial/Current-Line Block commenting + + Parameters: ~ + {param} (OpFnParams) + {partial?} (boolean) Comment the partial region (visual mode) + + Returns: ~ + (integer) Returns a calculated comment mode + + +============================================================================== +Extra API *comment.extra* + +Underlying functions that powers the |comment.api.insert| lua API. + +extra.insert_below({ctype}, {cfg}) *comment.extra.insert_below* + Add a comment below the current line and goes to INSERT mode + + Parameters: ~ + {ctype} (integer) See |comment.utils.ctype| + {cfg} (CommentConfig) + + +extra.insert_above({ctype}, {cfg}) *comment.extra.insert_above* + Add a comment above the current line and goes to INSERT mode + + Parameters: ~ + {ctype} (integer) See |comment.utils.ctype| + {cfg} (CommentConfig) + + +extra.insert_eol({ctype}, {cfg}) *comment.extra.insert_eol* + Add a comment at the end of current line and goes to INSERT mode + + Parameters: ~ + {ctype} (integer) See |comment.utils.ctype| + {cfg} (CommentConfig) + + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/comment.nvim/doc/plugs.md b/config/neovim/store/lazy-plugins/comment.nvim/doc/plugs.md new file mode 100644 index 00000000..b1fdf315 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/doc/plugs.md @@ -0,0 +1 @@ +`Comment.nvim` now has `:help` docs 🎉. Read `:h comment.plugmap` for the `` mappings documentation and usage. diff --git a/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/api.lua b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/api.lua new file mode 100644 index 00000000..e92757f7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/api.lua @@ -0,0 +1,253 @@ +---@mod comment.api Core Lua API +---@brief [[ +---This module provides the core lua APIs which is used by the default keybindings +---and (Read |comment.plugmap|) mappings. These API can be used to setup your +---own custom keybindings or to even make your (un)comment function. +---@brief ]] + +local Config = require('Comment.config') +local U = require('Comment.utils') +local Op = require('Comment.opfunc') +local Ex = require('Comment.extra') +local A = vim.api + +local api, core = {}, {} + +---API metamethods +---@param that table +---@param ctype CommentType +---@return table +function core.__index(that, ctype) + local idxd = {} + local mode, type = that.cmode, U.ctype[ctype] + + ---To comment the current-line + ---NOTE: + ---In current-line linewise method, 'opmode' is not useful which is always equals to `char` + ---but we need 'nil' here which is used for current-line + function idxd.current(_, cfg) + U.catch(Op.opfunc, nil, cfg or Config:get(), mode, type) + end + + ---To comment lines with a count + function idxd.count(count, cfg) + U.catch(Op.count, count or A.nvim_get_vvar('count'), cfg or Config:get(), mode, type) + end + + ---@private + ---To comment lines with a count, also dot-repeatable + ---WARN: This is not part of the API but anyone case use it, if they want + function idxd.count_repeat(_, count, cfg) + idxd.count(count, cfg) + end + + return setmetatable({}, { + __index = idxd, + __call = function(_, motion, cfg) + U.catch(Op.opfunc, motion, cfg or Config:get(), mode, type) + end, + }) +end + +---@tag comment.api.toggle.linewise +---@tag comment.api.toggle.blockwise +---Provides API to toggle comments over a region, on current-line, or with a +---count using line or block comment string. +--- +---Every function takes a {motion} argument, except '*.count()' function which +---takes an {count} argument, and an optional {config} parameter. +---@type table A metatable containing API functions +---@see comment.opfunc.OpMotion +---@see comment.config +---@usage [[ +---local api = require('Comment.api') +---local config = require('Comment.config'):get() +--- +---api.toggle.linewise(motion, config?) +---api.toggle.linewise.current(motion?, config?) +---api.toggle.linewise.count(count, config?) +--- +---api.toggle.blockwise(motion, config?) +---api.toggle.blockwise.current(motion?, config?) +---api.toggle.blockwise.count(count, config?) +--- +----- Toggle current line (linewise) using C-/ +---vim.keymap.set('n', '', api.toggle.linewise.current) +--- +----- Toggle current line (blockwise) using C-\ +---vim.keymap.set('n', '', api.toggle.blockwise.current) +--- +----- Toggle lines (linewise) with dot-repeat support +----- Example: gc3j will comment 4 lines +---vim.keymap.set( +--- 'n', 'gc', api.call('toggle.linewise', 'g@'), +--- { expr = true } +---) +--- +----- Toggle lines (blockwise) with dot-repeat support +----- Example: gb3j will comment 4 lines +---vim.keymap.set( +--- 'n', 'gb', api.call('toggle.blockwise', 'g@'), +--- { expr = true } +---) +--- +---local esc = vim.api.nvim_replace_termcodes( +--- '', true, false, true +---) +--- +----- Toggle selection (linewise) +---vim.keymap.set('x', 'c', function() +--- vim.api.nvim_feedkeys(esc, 'nx', false) +--- api.toggle.linewise(vim.fn.visualmode()) +---end) +--- +----- Toggle selection (blockwise) +---vim.keymap.set('x', 'b', function() +--- vim.api.nvim_feedkeys(esc, 'nx', false) +--- api.toggle.blockwise(vim.fn.visualmode()) +---end) +---@usage ]] +api.toggle = setmetatable({ cmode = U.cmode.toggle }, core) + +---@tag comment.api.comment.linewise +---@tag comment.api.comment.blockwise +---Provides API to (only) comment a region, on current-line, or with a +---count using line or block comment string. +--- +---Every function takes a {motion} argument, except '*.count()' function which +---takes an {count} argument, and an optional {config} parameter. +---@type table A metatable containing API functions +---@see comment.opfunc.OpMotion +---@see comment.config +---@usage [[ +---local api = require('Comment.api') +---local config = require('Comment.config'):get() +--- +---api.comment.linewise(motion, config?) +---api.comment.linewise.current(motion?, config?) +---api.comment.linewise.count(count, config?) +--- +---api.comment.blockwise(motion, config?) +---api.comment.blockwise.current(motion?, config?) +---api.comment.blockwise.count(count, config?) +---@usage ]] +api.comment = setmetatable({ cmode = U.cmode.comment }, core) + +---@tag comment.api.uncomment.linewise +---@tag comment.api.uncomment.blockwise +---Provides API to (only) uncomment a region, on current-line, or with a +---count using line or block comment string. +--- +---Every function takes a {motion} argument, except '*.count()' function which +---takes an {count} argument, and an optional {config} parameter. +---@type table A metatable containing API functions +---@see comment.opfunc.OpMotion +---@see comment.config +---@usage [[ +---local api = require('Comment.api') +---local config = require('Comment.config'):get() +--- +---api.uncomment.linewise(motion, config?) +---api.uncomment.linewise.current(motion?, config?) +---api.uncomment.linewise.count(count, config?) +--- +---api.uncomment.blockwise(motion, config?) +---api.uncomment.blockwise.current(motion?, config?) +---api.uncomment.blockwise.count(count, config?) +---@usage ]] +api.uncomment = setmetatable({ cmode = U.cmode.uncomment }, core) + +---Provides API to to insert comment on previous, next or at the end-of-line. +---Every function takes an optional {config} parameter. +---@type table A metatable containing API functions +---@see comment.config +---@usage [[ +---local api = require('Comment.api') +---local config = require('Comment.config'):get() +--- +---api.insert.linewise.above(config?) +---api.insert.linewise.below(config?) +---api.insert.linewise.eol(config?) +--- +---api.insert.blockwise.above(config?) +---api.insert.blockwise.below(config?) +---api.insert.blockwise.eol(config?) +---@usage ]] +api.insert = setmetatable({}, { + __index = function(_, ctype) + return { + above = function(cfg) + U.catch(Ex.insert_above, U.ctype[ctype], cfg or Config:get()) + end, + below = function(cfg) + U.catch(Ex.insert_below, U.ctype[ctype], cfg or Config:get()) + end, + eol = function(cfg) + U.catch(Ex.insert_eol, U.ctype[ctype], cfg or Config:get()) + end, + } + end, +}) + +---Wraps the given API function with 'lockmarks' to preserve marks/jumps +---@param cb string Name of API function +---@return fun(motion:OpMotion) #Callback function +---@see lockmarks +---@see comment.opfunc.OpMotion +---@usage [[ +---local api = require('Comment.api') +--- +---vim.keymap.set( +--- 'n', 'c', api.locked('toggle.linewise.current') +---) +--- +---local esc = vim.api.nvim_replace_termcodes( +--- '', true, false, true +---) +---vim.keymap.set('x', 'c', function() +--- vim.api.nvim_feedkeys(esc, 'nx', false) +--- api.locked('toggle.linewise')(vim.fn.visualmode()) +---end) +--- +----- NOTE: `locked` method is just a wrapper around `lockmarks` +---vim.api.nvim_command([[ +--- lockmarks lua require('Comment.api').toggle.linewise.current() +---]]) +---@usage ]] +function api.locked(cb) + return function(motion) + return A.nvim_command( + ('lockmarks lua require("Comment.api").%s(%s)'):format(cb, motion and ('%q'):format(motion)) + ) + end +end + +---Callback function which does the following +--- 1. Sets 'operatorfunc' for dot-repeat +--- 2. Preserves jumps and marks +--- 3. Stores last cursor position +---@param cb string Name of the API function to call +---@param op '"g@"'|'"g@$"' Operator-mode expression +---@return fun():string #Keymap RHS callback +---@see g@ +---@see operatorfunc +---@usage [[ +---local api = require('Comment.api') +---vim.keymap.set( +--- 'n', 'gc', api.call('toggle.linewise', 'g@'), +--- { expr = true } +---) +---vim.keymap.set( +--- 'n', 'gcc', api.call('toggle.linewise.current', 'g@$'), +--- { expr = true } +---) +---@usage ]] +function api.call(cb, op) + return function() + A.nvim_set_option('operatorfunc', ("v:lua.require'Comment.api'.locked'%s'"):format(cb)) + Config.position = Config:get().sticky and A.nvim_win_get_cursor(0) or nil + return op + end +end + +return api diff --git a/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/config.lua b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/config.lua new file mode 100644 index 00000000..01be20f0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/config.lua @@ -0,0 +1,134 @@ +---@mod comment.config Configuration +---@tag comment.config.defaults +---@brief [[ +---Following is the default config for the |comment.usage.setup|. If you want to +---override, just modify the option that you want, then it will be merged with the +---default config. +--->lua +--- { +--- padding = true, +--- sticky = true, +--- ignore = nil, +--- toggler = { line = 'gcc', block = 'gbc' }, +--- opleader = { line = 'gc', block = 'gb' }, +--- extra = { above = 'gcO', below = 'gco', eol = 'gcA' }, +--- mappings = { basic = true, extra = true }, +--- pre_hook = nil, +--- post_hook = nil, +--- } +---< +---@brief ]] + +---Plugin's configuration +---@class CommentConfig +---Controls space between the comment +---and the line (default: 'true') +---@field padding boolean|fun():boolean +---Whether cursor should stay at the +---same position. Only works in NORMAL +---mode mappings (default: 'true') +---@field sticky boolean +---Lua pattern used to ignore lines +---during (un)comment (default: 'nil') +---@field ignore string|fun():string +---Enables |comment.keybindings| +---NOTE: If given 'false', then the +---plugin won't create any mappings +---@field mappings Mappings|false +---@field toggler Toggler See |comment.config.Toggler| +---@field opleader Opleader See |comment.config.Opleader| +---@field extra ExtraMapping See |comment.config.ExtraMapping| +---Function to call before (un)comment. +---It is called with a {ctx} argument +---of type |comment.utils.CommentCtx| +---(default: 'nil') +---@field pre_hook fun(c: CommentCtx): string +---Function to call after (un)comment. +---It is called with a {ctx} argument +---of type |comment.utils.CommentCtx| +---(default: 'nil') +---@field post_hook fun(c: CommentCtx) + +---Create default mappings +---@class Mappings +---Enables operator-pending mapping; `gcc`, `gbc`, +---`gc{motion}` and `gb{motion}` (default: 'true') +---@field basic boolean +---Enable extra mapping; `gco`, `gcO` and `gcA` +---(default: 'true') +---@field extra boolean + +---LHS of toggle mappings in NORMAL +---@class Toggler +---@field line string Linewise comment (default: 'gcc') +---@field block string Blockwise comment (default: 'gbc') + +---LHS of operator-mode mappings in NORMAL and VISUAL mode +---@class Opleader +---@field line string Linewise comment (default: 'gc') +---@field block string Blockwise comment (default: 'gb') + +---LHS of extra mappings +---@class ExtraMapping +---@field below string Inserts comment below (default: 'gco') +---@field above string Inserts comment above (default: 'gcO') +---@field eol string Inserts comment at the end of line (default: 'gcA') + +---@private +---@class RootConfig +---@field config CommentConfig +---@field position? integer[] To be used to restore cursor position +local Config = { + state = {}, + config = { + padding = true, + sticky = true, + mappings = { + basic = true, + extra = true, + }, + toggler = { + line = 'gcc', + block = 'gbc', + }, + opleader = { + line = 'gc', + block = 'gb', + }, + extra = { + above = 'gcO', + below = 'gco', + eol = 'gcA', + }, + }, +} + +---@package +---Updates the default config +---@param cfg? CommentConfig +---@return RootConfig +---@see comment.usage.setup +---@usage `require('Comment.config'):set({config})` +function Config:set(cfg) + if cfg then + self.config = vim.tbl_deep_extend('force', self.config, cfg) + end + return self +end + +---Get the config +---@return CommentConfig +---@usage `require('Comment.config'):get()` +function Config:get() + return self.config +end + +---@export Config +return setmetatable(Config, { + __index = function(this, k) + return this.state[k] + end, + __newindex = function(this, k, v) + this.state[k] = v + end, +}) diff --git a/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/extra.lua b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/extra.lua new file mode 100644 index 00000000..6c3dcc4a --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/extra.lua @@ -0,0 +1,105 @@ +---@mod comment.extra Extra API +---@brief [[ +---Underlying functions that powers the |comment.api.insert| lua API. +---@brief ]] + +local U = require('Comment.utils') +local A = vim.api + +local extra = {} + +-- FIXME This prints `a` in i_CTRL-o +---Moves the cursor and enters INSERT mode +---@param row integer Starting row +---@param col integer Ending column +local function move_n_insert(row, col) + A.nvim_win_set_cursor(0, { row, col }) + A.nvim_feedkeys('a', 'ni', true) +end + +---@param lnum integer Line index +---@param ctype integer +---@param cfg CommentConfig +local function ins_on_line(lnum, ctype, cfg) + local row, col = unpack(A.nvim_win_get_cursor(0)) + + ---@type CommentCtx + local ctx = { + cmode = U.cmode.comment, + cmotion = U.cmotion.line, + ctype = ctype, + range = { srow = row, scol = col, erow = row, ecol = col }, + } + + local srow = row + lnum + local lcs, rcs = U.parse_cstr(cfg, ctx) + local padding = U.get_pad(U.is_fn(cfg.padding)) + + -- We need RHS of cstr, if we are doing block comments or if RHS exists + -- because even in line comment RHS do exists for some filetypes like jsx_element, ocaml + local if_rcs = U.is_empty(rcs) and rcs or padding .. rcs + + A.nvim_buf_set_lines(0, srow, srow, false, { lcs .. padding .. if_rcs }) + A.nvim_win_set_cursor(0, { srow + 1, 0 }) + A.nvim_command('normal! ==') + move_n_insert(srow + 1, #A.nvim_get_current_line() - #if_rcs - 1) + U.is_fn(cfg.post_hook, ctx) +end + +---Add a comment below the current line and goes to INSERT mode +---@param ctype integer See |comment.utils.ctype| +---@param cfg CommentConfig +function extra.insert_below(ctype, cfg) + ins_on_line(0, ctype, cfg) +end + +---Add a comment above the current line and goes to INSERT mode +---@param ctype integer See |comment.utils.ctype| +---@param cfg CommentConfig +function extra.insert_above(ctype, cfg) + ins_on_line(-1, ctype, cfg) +end + +---Add a comment at the end of current line and goes to INSERT mode +---@param ctype integer See |comment.utils.ctype| +---@param cfg CommentConfig +function extra.insert_eol(ctype, cfg) + local srow, scol = unpack(A.nvim_win_get_cursor(0)) + + ---@type CommentCtx + local ctx = { + cmode = U.cmode.comment, + cmotion = U.cmotion.line, + ctype = ctype, + range = { srow = srow, scol = scol, erow = srow, ecol = scol }, + } + local lcs, rcs = U.parse_cstr(cfg, ctx) + + local line = A.nvim_get_current_line() + local padding = U.get_pad(U.is_fn(cfg.padding)) + + -- We need RHS of cstr, if we are doing block comments or if RHS exists + -- because even in line comment RHS do exists for some filetypes like jsx_element, ocaml + local if_rcs = U.is_empty(rcs) and rcs or padding .. rcs + + local ecol + if U.is_empty(line) then + -- If line is empty, start comment at the correct indentation level + A.nvim_set_current_line(lcs .. padding .. if_rcs) + A.nvim_command('normal! ==') + ecol = #A.nvim_get_current_line() - #if_rcs - 1 + else + -- NOTE: + -- 1. Python is the only language that recommends 2 spaces between the statement and the comment + -- 2. Other than that, I am assuming that the users wants a space b/w the end of line and start of the comment + local space = vim.bo.filetype == 'python' and ' ' or ' ' + local ll = line .. space .. lcs .. padding + A.nvim_set_current_line(ll .. if_rcs) + ecol = #ll - 1 + end + + move_n_insert(srow, ecol) + U.is_fn(cfg.post_hook, ctx) +end + +return extra diff --git a/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/ft.lua b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/ft.lua new file mode 100644 index 00000000..812eacd2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/ft.lua @@ -0,0 +1,297 @@ +---@mod comment.ft Language/Filetype detection +---@brief [[ +---This module is the core of filetype and commentstring detection and uses the +---|lua-treesitter| APIs to accurately detect filetype and gives the corresponding +---commentstring, stored inside the plugin, for the filetype/langauge. +--- +---Compound (dot-separated) filetypes are also supported i.e. 'ansible.yaml', +---'ios.swift' etc. The commentstring resolution will be done from left to right. +---For example, If the filetype is 'ansible.yaml' then 'ansible' commenstring will +---be used if found otherwise it'll fallback to 'yaml'. Read `:h 'filetype'` +---@brief ]] + +local A = vim.api + +---Common commentstring shared b/w multiple languages +local M = { + cxx_l = '//%s', + cxx_b = '/*%s*/', + dbl_hash = '##%s', + dash = '--%s', + dash_bracket = '--[[%s]]', + handlebars = '{{!--%s--}}', + hash = '#%s', + hash_bracket = '#[[%s]]', + haskell_b = '{-%s-}', + fsharp_b = '(*%s*)', + html = '', + latex = '%%s', + semicolon = ';%s', + lisp_l = ';;%s', + lisp_b = '#|%s|#', + twig = '{#%s#}', + vim = '"%s', + lean_b = '/-%s-/', +} + +---Lang table that contains commentstring (linewise/blockwise) for multiple filetypes +---Structure = { filetype = { linewise, blockwise } } +---@type table +local L = setmetatable({ + arduino = { M.cxx_l, M.cxx_b }, + applescript = { M.hash }, + astro = { M.html }, + autohotkey = { M.semicolon, M.cxx_b }, + bash = { M.hash }, + beancount = { M.semicolon }, + bib = { M.latex }, + c = { M.cxx_l, M.cxx_b }, + cabal = { M.dash }, + cmake = { M.hash, M.hash_bracket }, + conf = { M.hash }, + conkyrc = { M.dash, M.dash_bracket }, + coq = { M.fsharp_b }, + cpp = { M.cxx_l, M.cxx_b }, + cs = { M.cxx_l, M.cxx_b }, + css = { M.cxx_b, M.cxx_b }, + cuda = { M.cxx_l, M.cxx_b }, + dart = { M.cxx_l, M.cxx_b }, + dhall = { M.dash, M.haskell_b }, + dosbatch = { 'REM%s' }, + dot = { M.cxx_l, M.cxx_b }, + dts = { M.cxx_l, M.cxx_b }, + editorconfig = { M.hash }, + eelixir = { M.html, M.html }, + elixir = { M.hash }, + elm = { M.dash, M.haskell_b }, + elvish = { M.hash }, + faust = { M.cxx_l, M.cxx_b }, + fennel = { M.semicolon }, + fish = { M.hash }, + func = { M.lisp_l }, + fsharp = { M.cxx_l, M.fsharp_b }, + gdb = { M.hash }, + gdscript = { M.hash }, + gitignore = { M.hash }, + gleam = { M.cxx_l }, + glsl = { M.cxx_l, M.cxx_b }, + gnuplot = { M.hash, M.hash_bracket }, + go = { M.cxx_l, M.cxx_b }, + gomod = { M.cxx_l }, + graphql = { M.hash }, + groovy = { M.cxx_l, M.cxx_b }, + handlebars = { M.handlebars, M.handlebars }, + haskell = { M.dash, M.haskell_b }, + haxe = { M.cxx_l, M.cxx_b }, + heex = { M.html, M.html }, + html = { M.html, M.html }, + htmldjango = { M.html, M.html }, + idris = { M.dash, M.haskell_b }, + idris2 = { M.dash, M.haskell_b }, + ini = { M.hash }, + java = { M.cxx_l, M.cxx_b }, + javascript = { M.cxx_l, M.cxx_b }, + javascriptreact = { M.cxx_l, M.cxx_b }, + jsonc = { M.cxx_l }, + jsonnet = { M.cxx_l, M.cxx_b }, + julia = { M.hash, '#=%s=#' }, + kotlin = { M.cxx_l, M.cxx_b }, + lean = { M.dash, M.lean_b }, + lean3 = { M.dash, M.lean_b }, + lidris = { M.dash, M.haskell_b }, + lilypond = { M.latex, '%{%s%}' }, + lisp = { M.lisp_l, M.lisp_b }, + lua = { M.dash, M.dash_bracket }, + luau = { M.dash, M.dash_bracket }, + markdown = { M.html, M.html }, + make = { M.hash }, + mbsyncrc = { M.dbl_hash }, + mermaid = { '%%%s' }, + meson = { M.hash }, + nextflow = { M.cxx_l, M.cxx_b }, + nim = { M.hash, '#[%s]#' }, + nix = { M.hash, M.cxx_b }, + nu = { M.hash }, + ocaml = { M.fsharp_b, M.fsharp_b }, + odin = { M.cxx_l, M.cxx_b }, + plantuml = { "'%s", "/'%s'/" }, + purescript = { M.dash, M.haskell_b }, + python = { M.hash }, -- Python doesn't have block comments + php = { M.cxx_l, M.cxx_b }, + prisma = { M.cxx_l }, + proto = { M.cxx_l, M.cxx_b }, + quarto = { M.html, M.html }, + r = { M.hash }, -- R doesn't have block comments + racket = { M.lisp_l, M.lisp_b }, + rasi = { M.cxx_l, M.cxx_b }, + readline = { M.hash }, + rego = { M.hash }, + remind = { M.hash }, + rescript = { M.cxx_l, M.cxx_b }, + robot = { M.hash }, -- Robotframework doesn't have block comments + ron = { M.cxx_l, M.cxx_b }, + ruby = { M.hash }, + rust = { M.cxx_l, M.cxx_b }, + sbt = { M.cxx_l, M.cxx_b }, + scala = { M.cxx_l, M.cxx_b }, + scheme = { M.lisp_l, M.lisp_b }, + sh = { M.hash }, + solidity = { M.cxx_l, M.cxx_b }, + supercollider = { M.cxx_l, M.cxx_b }, + sql = { M.dash, M.cxx_b }, + stata = { M.cxx_l, M.cxx_b }, + svelte = { M.html, M.html }, + swift = { M.cxx_l, M.cxx_b }, + sxhkdrc = { M.hash }, + tablegen = { M.cxx_l, M.cxx_b }, + teal = { M.dash, M.dash_bracket }, + terraform = { M.hash, M.cxx_b }, + tex = { M.latex }, + template = { M.dbl_hash }, + tmux = { M.hash }, + toml = { M.hash }, + twig = { M.twig, M.twig }, + typescript = { M.cxx_l, M.cxx_b }, + typescriptreact = { M.cxx_l, M.cxx_b }, + typst = { M.cxx_l, M.cxx_b }, + v = { M.cxx_l, M.cxx_b }, + verilog = { M.cxx_l }, + vhdl = { M.dash }, + vim = { M.vim }, + vifm = { M.vim }, + vue = { M.html, M.html }, + xdefaults = { '!%s' }, + xml = { M.html, M.html }, + xonsh = { M.hash }, -- Xonsh doesn't have block comments + yaml = { M.hash }, + yuck = { M.lisp_l }, + zig = { M.cxx_l }, -- Zig doesn't have block comments +}, { + -- Support for compound filetype i.e. 'ios.swift', 'ansible.yaml' etc. + __index = function(this, k) + local base, fallback = string.match(k, '^(.-)%.(.*)') + if not (base or fallback) then + return nil + end + return this[base] or this[fallback] + end, +}) + +local ft = {} + +---Sets a commentstring(s) for a filetype/language +---@param lang string Filetype/Language of the buffer +---@param val string|string[] +---@return table self Returns itself +---@usage [[ +---local ft = require('Comment.ft') +--- +-----1. Using method signature +----- Set only line comment or both +----- You can also chain the set calls +---ft.set('yaml', '#%s').set('javascript', {'//%s', '/*%s*/'}) +--- +----- 2. Metatable magic +---ft.javascript = {'//%s', '/*%s*/'} +---ft.yaml = '#%s' +--- +----- 3. Multiple filetypes +---ft({'go', 'rust'}, {'//%s', '/*%s*/'}) +---ft({'toml', 'graphql'}, '#%s') +---@usage ]] +function ft.set(lang, val) + L[lang] = type(val) == 'string' and { val } or val --[[ @as string[] ]] + return ft +end + +---Get line/block/both commentstring(s) for a given filetype +---@param lang string Filetype/Language of the buffer +---@param ctype? integer See |comment.utils.ctype|. If given `nil`, it'll +---return a copy of { line, block } commentstring. +---@return nil|string|string[] #Returns stored commentstring +---@usage [[ +---local ft = require('Comment.ft') +---local U = require('Comment.utils') +--- +----- 1. Primary filetype +---ft.get('rust', U.ctype.linewise) -- `//%s` +---ft.get('rust') -- `{ '//%s', '/*%s*/' }` +--- +----- 2. Compound filetype +----- NOTE: This will return `yaml` commenstring(s), +----- as `ansible` commentstring is not found. +---ft.get('ansible.yaml', U.ctype.linewise) -- `#%s` +---ft.get('ansible.yaml') -- { '#%s' } +---@usage ]] +function ft.get(lang, ctype) + local tuple = L[lang] + if not tuple then + return nil + end + if not ctype then + return vim.deepcopy(tuple) + end + return tuple[ctype] +end + +---Get a language tree for a given range by walking the parse tree recursively. +---This uses 'lua-treesitter' API under the hood. This can be used to calculate +---language of a particular region which embedded multiple filetypes like html, +---vue, markdown etc. +--- +---NOTE: This ignores `tree-sitter-comment` parser, if installed. +---@param tree userdata Parse tree to be walked +---@param range integer[] Range to check +---{start_row, start_col, end_row, end_col} +---@return userdata #Returns a |treesitter-languagetree| +---@see treesitter-languagetree +---@see lua-treesitter-core +---@usage [[ +---local ok, parser = pcall(vim.treesitter.get_parser, 0) +---assert(ok, "No parser found!") +---local tree = require('Comment.ft').contains(parser, {0, 0, -1, 0}) +---print('Lang:', tree:lang()) +---@usage ]] +function ft.contains(tree, range) + for lang, child in pairs(tree:children()) do + if lang ~= 'comment' and child:contains(range) then + return ft.contains(child, range) + end + end + + return tree +end + +---Calculate commentstring with the power of treesitter +---@param ctx CommentCtx +---@return nil|string #Commentstring +---@see comment.utils.CommentCtx +function ft.calculate(ctx) + local ok, parser = pcall(vim.treesitter.get_parser, A.nvim_get_current_buf()) + + if not ok then + return ft.get(vim.bo.filetype, ctx.ctype) --[[ @as string ]] + end + + local lang = ft.contains(parser, { + ctx.range.srow - 1, + ctx.range.scol, + ctx.range.erow - 1, + ctx.range.ecol, + }):lang() + + return ft.get(lang, ctx.ctype) or ft.get(vim.bo.filetype, ctx.ctype) --[[ @as string ]] +end + +---@export ft +return setmetatable(ft, { + __newindex = function(this, k, v) + this.set(k, v) + end, + __call = function(this, langs, spec) + for _, lang in ipairs(langs) do + this.set(lang, spec) + end + return this + end, +}) diff --git a/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/init.lua b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/init.lua new file mode 100644 index 00000000..2cdfc7b8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/init.lua @@ -0,0 +1,138 @@ +---@brief [[ +---*comment-nvim.txt* For Neovim version 0.7 Last change: 2021 July 11 +--- +--- _____ _ _ +--- / ____/ / / (_) +--- / / ___ _ __ ___ _ __ ___ ___ _ __ / /_ _ ____ ___ _ __ ___ +--- / / / _ \/ '_ ` _ \/ '_ ` _ \ / _ \ '_ \/ __/ / '_ \ \ / / / '_ ` _ \ +--- / /___/ (_) / / / / / / / / / / / __/ / / / /_ _/ / / \ V // / / / / / / +--- \_____\___//_/ /_/ /_/_/ /_/ /_/\___/_/ /_/\__(_)_/ /_/\_/ /_/_/ /_/ /_/ +--- +--- · Smart and Powerful comment plugin · +--- +---@brief ]] + +---@toc comment.contents + +---@mod comment-nvim Introduction +---@brief [[ +---Comment.nvim is a smart and powerful comment plugin for neovim. It supports +---dot-repeat, counts, line ('//') and block ('/* */') comments, and can be used +---with motion and text-objects. It has native integration with |treesitter| to +---support embedded filetypes like html, vue, markdown with codeblocks etc. +---@brief ]] +---@tag comment.dotrepeat +---@brief [[ +---Comment.nvim uses |operatorfunc| combined with |g@| to support dot-repeat, and +---various marks i.e., |'[| |']| |'<| |'>| to deduce the region with the {motion} +---argument provided by 'operatorfunc'. See |comment.api.call| +---@brief ]] +---@tag comment.commentstring +---@brief [[ +---Comment.nvim picks commentstring, either linewise/blockwise, from one of the +---following places +--- +--- 1. 'pre_hook' +--- If a string is returned from this function then it will be used for +--- (un)commenting. See |comment.config| +--- +--- 2. |comment.ft| +--- Using the commentstring table inside the plugin (using treesitter). +--- Fallback to |commentstring|, if not found. +--- +--- 3. |commentstring| - Neovim's native commentstring for the filetype +--- +---Although Comment.nvim supports native 'commentstring' but unfortunately it has +---the least priority. The caveat with this approach is that if someone sets the +---`commentstring`, without returning it, from the 'pre_hook' and the current +---filetype also exists in the |comment.ft| then the commenting will be done using +---the string in |comment.ft| instead of using 'commentstring'. To override this +---behavior, you have to manually return the 'commentstring' from 'pre_hook'. +---@brief ]] +---@tag comment.sourcecode +---@brief [[ +---Comment.nvim is FOSS and distributed under MIT license. All the source code is +---available at https://github.com/numToStr/Comment.nvim +---@brief ]] + +---@mod comment.usage Usage +---@brief [[ +---Before using the plugin, you need to call the `setup()` function to create the +---default mappings. If you want, you can also override the default configuration +---by giving it a partial 'comment.config.Config' object, it will then be merged +---with the default configuration. +---@brief ]] + +local C = {} + +---Configures the plugin +---@param config? CommentConfig User configuration +---@return CommentConfig #Returns the modified config +---@see comment.config +---@usage [[ +----- Use default configuration +---require('Comment').setup() +--- +----- or with custom configuration +---require('Comment').setup({ +--- ignore = '^$', +--- toggler = { +--- line = 'cc', +--- block = 'bc', +--- }, +--- opleader = { +--- line = 'c', +--- block = 'b', +--- }, +---}) +---@usage ]] +function C.setup(config) + local cfg = require('Comment.config'):set(config):get() + + if cfg.mappings then + local api = require('Comment.api') + local vvar = vim.api.nvim_get_vvar + local K = vim.keymap.set + + -- Basic Mappings + if cfg.mappings.basic then + -- NORMAL mode mappings + K('n', cfg.opleader.line, '(comment_toggle_linewise)', { desc = 'Comment toggle linewise' }) + K('n', cfg.opleader.block, '(comment_toggle_blockwise)', { desc = 'Comment toggle blockwise' }) + + K('n', cfg.toggler.line, function() + return vvar('count') == 0 and '(comment_toggle_linewise_current)' + or '(comment_toggle_linewise_count)' + end, { expr = true, desc = 'Comment toggle current line' }) + K('n', cfg.toggler.block, function() + return vvar('count') == 0 and '(comment_toggle_blockwise_current)' + or '(comment_toggle_blockwise_count)' + end, { expr = true, desc = 'Comment toggle current block' }) + + -- VISUAL mode mappings + K( + 'x', + cfg.opleader.line, + '(comment_toggle_linewise_visual)', + { desc = 'Comment toggle linewise (visual)' } + ) + K( + 'x', + cfg.opleader.block, + '(comment_toggle_blockwise_visual)', + { desc = 'Comment toggle blockwise (visual)' } + ) + end + + -- Extra Mappings + if cfg.mappings.extra then + K('n', cfg.extra.below, api.insert.linewise.below, { desc = 'Comment insert below' }) + K('n', cfg.extra.above, api.insert.linewise.above, { desc = 'Comment insert above' }) + K('n', cfg.extra.eol, api.locked('insert.linewise.eol'), { desc = 'Comment insert end of line' }) + end + end + + return cfg +end + +return C diff --git a/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/opfunc.lua b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/opfunc.lua new file mode 100644 index 00000000..02585a63 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/opfunc.lua @@ -0,0 +1,228 @@ +---@mod comment.opfunc Operator-mode API +---@brief [[ +---Underlying functions that powers the |comment.api.toggle|, |comment.api.comment|, +---and |comment.api.uncomment| lua API. +---@brief ]] + +local U = require('Comment.utils') +local Config = require('Comment.config') +local A = vim.api + +local Op = {} + +---Vim operator-mode motion enum. Read |:map-operator| +---@alias OpMotion +---| '"line"' # Vertical motion +---| '"char"' # Horizontal motion +---| '"v"' # Visual Block motion +---| '"V"' # Visual Line motion + +---Common operatorfunc callback +---This function contains the core logic for comment/uncomment +---@param motion? OpMotion +---If given 'nil', it'll only (un)comment +---the current line +---@param cfg CommentConfig +---@param cmode integer See |comment.utils.cmode| +---@param ctype integer See |comment.utils.ctype| +function Op.opfunc(motion, cfg, cmode, ctype) + local range = U.get_region(motion) + local cmotion = motion == nil and U.cmotion.line or U.cmotion[motion] + + -- If we are doing char or visual motion on the same line + -- then we would probably want block comment instead of line comment + local is_partial = cmotion == U.cmotion.char or cmotion == U.cmotion.v + local is_blockx = is_partial and range.srow == range.erow + + local lines = U.get_lines(range) + + -- sometimes there might be a case when there are no lines + -- like, executing a text object returns nothing + if U.is_empty(lines) then + return + end + + ---@type CommentCtx + local ctx = { + cmode = cmode, + cmotion = cmotion, + ctype = is_blockx and U.ctype.blockwise or ctype, + range = range, + } + + local lcs, rcs = U.parse_cstr(cfg, ctx) + + ---@type OpFnParams + local params = { + cfg = cfg, + lines = lines, + lcs = lcs, + rcs = rcs, + cmode = cmode, + range = range, + } + + if motion ~= nil and (is_blockx or ctype == U.ctype.blockwise) then + ctx.cmode = Op.blockwise(params, is_partial) + else + ctx.cmode = Op.linewise(params) + end + + -- We only need to restore cursor if both sticky and position are available + -- As this function is also called for visual mapping where we are not storing the position + -- + -- And I found out that if someone presses `gc` but doesn't provide operators and + -- does visual comments then cursor jumps to previous stored position. Thus the check for visual modes + if cfg.sticky and Config.position and cmotion ~= U.cmotion.v and cmotion ~= U.cmotion.V then + A.nvim_win_set_cursor(0, Config.position) + Config.position = nil + end + + U.is_fn(cfg.post_hook, ctx) +end + +---Line commenting with count +---@param count integer Value of |v:count| +---@param cfg CommentConfig +---@param cmode integer See |comment.utils.cmode| +---@param ctype integer See |comment.utils.ctype| +function Op.count(count, cfg, cmode, ctype) + local lines, range = U.get_count_lines(count) + + ---@type CommentCtx + local ctx = { + cmode = cmode, + cmotion = U.cmotion.line, + ctype = ctype, + range = range, + } + local lcs, rcs = U.parse_cstr(cfg, ctx) + + ---@type OpFnParams + local params = { + cfg = cfg, + cmode = ctx.cmode, + lines = lines, + lcs = lcs, + rcs = rcs, + range = range, + } + + if ctype == U.ctype.blockwise then + ctx.cmode = Op.blockwise(params) + else + ctx.cmode = Op.linewise(params) + end + + U.is_fn(cfg.post_hook, ctx) +end + +---Operator-mode function parameters +---@class OpFnParams +---@field cfg CommentConfig +---@field cmode integer See |comment.utils.cmode| +---@field lines string[] List of lines +---@field rcs string RHS of commentstring +---@field lcs string LHS of commentstring +---@field range CommentRange + +---Line commenting +---@param param OpFnParams +---@return integer _ Returns a calculated comment mode +function Op.linewise(param) + local pattern = U.is_fn(param.cfg.ignore) + local padding = U.is_fn(param.cfg.padding) + local check_comment = U.is_commented(param.lcs, param.rcs, padding) + + -- While commenting a region, there could be lines being both commented and non-commented + -- So, if any line is uncommented then we should comment the whole block or vise-versa + local cmode = U.cmode.uncomment + + ---When commenting multiple line, it is to be expected that indentation should be preserved + ---So, When looping over multiple lines we need to store the indentation of the mininum length (except empty line) + ---Which will be used to semantically comment rest of the lines + local min_indent, tabbed = -1, false + + -- If the given cmode is uncomment then we actually don't want to compute the cmode or min_indent + if param.cmode ~= U.cmode.uncomment then + for _, line in ipairs(param.lines) do + -- I wish lua had `continue` statement [sad noises] + if not U.ignore(line, pattern) then + if cmode == U.cmode.uncomment and param.cmode == U.cmode.toggle and (not check_comment(line)) then + cmode = U.cmode.comment + end + + if not U.is_empty(line) and param.cmode ~= U.cmode.uncomment then + local _, len = string.find(line, '^%s*') + if min_indent == -1 or min_indent > len then + min_indent, tabbed = len, string.find(line, '^\t') ~= nil + end + end + end + end + end + + -- If the comment mode given is not toggle than force that mode + if param.cmode ~= U.cmode.toggle then + cmode = param.cmode + end + + if cmode == U.cmode.uncomment then + local uncomment = U.uncommenter(param.lcs, param.rcs, padding) + for i, line in ipairs(param.lines) do + if not U.ignore(line, pattern) then + param.lines[i] = uncomment(line) --[[@as string]] + end + end + else + local comment = U.commenter(param.lcs, param.rcs, padding, min_indent, nil, tabbed) + for i, line in ipairs(param.lines) do + if not U.ignore(line, pattern) then + param.lines[i] = comment(line) --[[@as string]] + end + end + end + + A.nvim_buf_set_lines(0, param.range.srow - 1, param.range.erow, false, param.lines) + + return cmode +end + +---Full/Partial/Current-Line Block commenting +---@param param OpFnParams +---@param partial? boolean Comment the partial region (visual mode) +---@return integer _ Returns a calculated comment mode +function Op.blockwise(param, partial) + local is_x = #param.lines == 1 -- current-line blockwise + local lines = is_x and param.lines[1] or param.lines + + local padding = U.is_fn(param.cfg.padding) + + local scol, ecol = nil, nil + if is_x or partial then + scol, ecol = param.range.scol, param.range.ecol + end + + -- If given mode is toggle then determine whether to comment or not + local cmode = param.cmode + if cmode == U.cmode.toggle then + local is_cmt = U.is_commented(param.lcs, param.rcs, padding, scol, ecol)(lines) + cmode = is_cmt and U.cmode.uncomment or U.cmode.comment + end + + if cmode == U.cmode.uncomment then + lines = U.uncommenter(param.lcs, param.rcs, padding, scol, ecol)(lines) + else + lines = U.commenter(param.lcs, param.rcs, padding, scol, ecol)(lines) + end + + if is_x then + A.nvim_set_current_line(lines) + else + A.nvim_buf_set_lines(0, param.range.srow - 1, param.range.erow, false, lines) + end + + return cmode +end + +return Op diff --git a/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/utils.lua b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/utils.lua new file mode 100644 index 00000000..e95ee2c3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/lua/Comment/utils.lua @@ -0,0 +1,376 @@ +---@mod comment.utils Utilities + +local F = require('Comment.ft') +local A = vim.api + +local U = {} + +---Comment context +---@class CommentCtx +---@field ctype integer See |comment.utils.ctype| +---@field cmode integer See |comment.utils.cmode| +---@field cmotion integer See |comment.utils.cmotion| +---@field range CommentRange + +---Range of the selection that needs to be commented +---@class CommentRange +---@field srow integer Starting row +---@field scol integer Starting column +---@field erow integer Ending row +---@field ecol integer Ending column + +---Comment modes - Can be manual or computed via operator-mode +---@class CommentMode +---@field toggle integer Toggle action +---@field comment integer Comment action +---@field uncomment integer Uncomment action + +---An object containing comment modes +---@type CommentMode +U.cmode = { + toggle = 0, + comment = 1, + uncomment = 2, +} + +---Comment types +---@class CommentType +---@field linewise integer Use linewise commentstring +---@field blockwise integer Use blockwise commentstring + +---An object containing comment types +---@type CommentType +U.ctype = { + linewise = 1, + blockwise = 2, +} + +---Comment motion types +---@class CommentMotion +---@field line integer Line motion (ie. 'gc2j') +---@field char integer Character/left-right motion (ie. 'gc2w') +---@field block integer Visual operator-pending motion +---@field v integer Visual motion (ie. 'v3jgc') +---@field V integer Visual-line motion (ie. 'V10kgc') + +---An object containing comment motions +---@type CommentMotion +U.cmotion = { + line = 1, + char = 2, + block = 3, + v = 4, + V = 5, +} + +---@private +---Check whether the line is empty +---@param iter string|string[] +---@return boolean +function U.is_empty(iter) + return #iter == 0 +end + +---@private +---Helper to get padding character +---@param flag boolean +---@return string string +function U.get_pad(flag) + return flag and ' ' or '' +end + +---@private +---Helper to get padding pattern +---@param flag boolean +---@return string string +function U.get_padpat(flag) + return flag and '%s?' or '' +end + +---@private +---Call a function if exists +---@param fn unknown|fun(...):unknown Wanna be function +---@return unknown +function U.is_fn(fn, ...) + if type(fn) == 'function' then + return fn(...) + end + return fn +end + +---@private +---Check if the given line is ignored or not with the given pattern +---@param ln string Line to be ignored +---@param pat string Lua regex +---@return boolean +function U.ignore(ln, pat) + return pat and string.find(ln, pat) ~= nil +end + +---Get region for line movement or visual selection +---NOTE: Returns the current line region, if `opmode` is not given. +---@param opmode? OpMotion +---@return CommentRange +function U.get_region(opmode) + if not opmode then + local row = unpack(A.nvim_win_get_cursor(0)) + return { srow = row, scol = 0, erow = row, ecol = 0 } + end + + local marks = string.match(opmode, '[vV]') and { '<', '>' } or { '[', ']' } + local sln, eln = A.nvim_buf_get_mark(0, marks[1]), A.nvim_buf_get_mark(0, marks[2]) + + return { srow = sln[1], scol = sln[2], erow = eln[1], ecol = eln[2] } +end + +---Get lines from the current position to the given count +---@param count integer Probably 'vim.v.count' +---@return string[] #List of lines +---@return CommentRange +function U.get_count_lines(count) + local srow = unpack(A.nvim_win_get_cursor(0)) + local erow = (srow + count) - 1 + local lines = A.nvim_buf_get_lines(0, srow - 1, erow, false) + + return lines, { srow = srow, scol = 0, erow = erow, ecol = 0 } +end + +---Get lines from a NORMAL/VISUAL mode +---@param range CommentRange +---@return string[] #List of lines +function U.get_lines(range) + -- If start and end is same, then just return the current line + if range.srow == range.erow then + return { A.nvim_get_current_line() } + end + + return A.nvim_buf_get_lines(0, range.srow - 1, range.erow, false) +end + +---Validates and unwraps the given commentstring +---@param cstr string See 'commentstring' +---@return string string Left side of the commentstring +---@return string string Right side of the commentstring +function U.unwrap_cstr(cstr) + local left, right = string.match(cstr, '(.*)%%s(.*)') + + assert( + (left or right), + { msg = string.format('Invalid commentstring for %s! Read `:h commentstring` for help.', vim.bo.filetype) } + ) + + return vim.trim(left), vim.trim(right) +end + +---Parses commentstring from the following places in the respective order +--- 1. pre_hook - commentstring returned from the function +--- 2. ft.lua - commentstring table bundled with the plugin +--- 3. commentstring - Neovim's native. See 'commentstring' +---@param cfg CommentConfig +---@param ctx CommentCtx +---@return string string Left side of the commentstring +---@return string string Right side of the commentstring +function U.parse_cstr(cfg, ctx) + -- 1. We ask `pre_hook` for a commentstring + local inbuilt = U.is_fn(cfg.pre_hook, ctx) + -- 2. Calculate w/ the help of treesitter + or F.calculate(ctx) + + assert(inbuilt or (ctx.ctype ~= U.ctype.blockwise), { + msg = vim.bo.filetype .. " doesn't support block comments!", + }) + + -- 3. Last resort to use native commentstring + return U.unwrap_cstr(inbuilt or vim.bo.commentstring) +end + +---Returns a closure which is used to do comments +--- +---If given {string[]} to the closure then it will do blockwise comment +---else linewise comment will be done with the given {string} +---@param left string Left side of the commentstring +---@param right string Right side of the commentstring +---@param padding boolean Is padding enabled? +---@param scol? integer Starting column +---@param ecol? integer Ending column +---@param tabbed? boolean Using tab indentation +---@return fun(line: string|string[]):string|string[] +function U.commenter(left, right, padding, scol, ecol, tabbed) + local pad = U.get_pad(padding) + local ll = U.is_empty(left) and left or (left .. pad) + local rr = U.is_empty(right) and right or (pad .. right) + local empty = string.rep(tabbed and '\t' or ' ', scol or 0) .. left .. right + local is_lw = scol and not ecol + + return function(line) + ------------------ + -- for linewise -- + ------------------ + if is_lw then + if U.is_empty(line) then + return empty + end + -- line == 0 -> start from 0 col + if scol == 0 then + return (ll .. line .. rr) + end + local first = string.sub(line --[[@as string]], 0, scol) + local last = string.sub(line --[[@as string]], scol + 1, -1) + return first .. ll .. last .. rr + end + + ------------------- + -- for blockwise -- + ------------------- + if type(line) == 'table' then + local first, last = line[1], line[#line] + -- If both columns are given then we can assume it's a partial block + if scol and ecol then + local sfirst = string.sub(first, 0, scol) + local slast = string.sub(first, scol + 1, -1) + local efirst = string.sub(last, 0, ecol + 1) + local elast = string.sub(last, ecol + 2, -1) + line[1] = sfirst .. ll .. slast + line[#line] = efirst .. rr .. elast + else + line[1] = U.is_empty(first) and left or string.gsub(first, '^(%s*)', '%1' .. vim.pesc(ll)) + line[#line] = U.is_empty(last) and right or (last .. rr) + end + return line + end + + -------------------------------- + -- for current-line blockwise -- + -------------------------------- + -- SOURCE: https://github.com/numToStr/Comment.nvim/issues/224 + if ecol > #line then + return ll .. line .. rr + end + local first = string.sub(line, 0, scol) + local mid = string.sub(line, scol + 1, ecol + 1) + local last = string.sub(line, ecol + 2, -1) + return first .. ll .. mid .. rr .. last + end +end + +---Returns a closure which is used to uncomment a line +--- +---If given {string[]} to the closure then it will block uncomment +---else linewise uncomment will be done with the given {string} +---@param left string Left side of the commentstring +---@param right string Right side of the commentstring +---@param padding boolean Is padding enabled? +---@param scol? integer Starting column +---@param ecol? integer Ending column +---@return fun(line: string|string[]):string|string[] +function U.uncommenter(left, right, padding, scol, ecol) + local pp, plen = U.get_padpat(padding), padding and 1 or 0 + local left_len, right_len = #left + plen, #right + plen + local ll = U.is_empty(left) and left or vim.pesc(left) .. pp + local rr = U.is_empty(right) and right or pp .. vim.pesc(right) + local is_lw = not (scol and scol) + local pattern = is_lw and '^(%s*)' .. ll .. '(.-)' .. rr .. '$' or '' + + return function(line) + ------------------- + -- for blockwise -- + ------------------- + if type(line) == 'table' then + local first, last = line[1], line[#line] + -- If both columns are given then we can assume it's a partial block + if scol and ecol then + local sfirst = string.sub(first, 0, scol) + local slast = string.sub(first, scol + left_len + 1, -1) + local efirst = string.sub(last, 0, ecol - right_len + 1) + local elast = string.sub(last, ecol + 2, -1) + line[1] = sfirst .. slast + line[#line] = efirst .. elast + else + line[1] = string.gsub(first, '^(%s*)' .. ll, '%1') + line[#line] = string.gsub(last, rr .. '$', '') + end + return line + end + + ------------------ + -- for linewise -- + ------------------ + if is_lw then + local a, b, c = string.match(line, pattern) + -- When user tries to uncomment when there is nothing to uncomment. See #221 + assert(a and b, { msg = 'Nothing to uncomment!' }) + -- If there is nothing after LHS then just return '' + -- bcz the line previously (before comment) was empty + return U.is_empty(b) and b or a .. b .. (c or '') + end + + -------------------------------- + -- for current-line blockwise -- + -------------------------------- + -- SOURCE: https://github.com/numToStr/Comment.nvim/issues/224 + if ecol > #line then + return string.sub(line, scol + left_len + 1, #line - right_len) + end + local first = string.sub(line, 0, scol) + local mid = string.sub(line, scol + left_len + 1, ecol - right_len + 1) + local last = string.sub(line, ecol + 2, -1) + return first .. mid .. last + end +end + +---Check if the given string is commented or not +--- +---If given {string[]} to the closure, it will check the first and last line +---with LHS and RHS of commentstring respectively else it will check the given +---line with LHS and RHS (if given) of the commenstring +---@param left string Left side of the commentstring +---@param right string Right side of the commentstring +---@param padding boolean Is padding enabled? +---@param scol? integer Starting column +---@param ecol? integer Ending column +---@return fun(line: string|string[]):boolean +function U.is_commented(left, right, padding, scol, ecol) + local pp = U.get_padpat(padding) + local ll = U.is_empty(left) and left or '^%s*' .. vim.pesc(left) .. pp + local rr = U.is_empty(right) and right or pp .. vim.pesc(right) .. '$' + local pattern = ll .. '.-' .. rr + local is_full = scol == nil or ecol == nil + + return function(line) + ------------------- + -- for blockwise -- + ------------------- + if type(line) == 'table' then + local first, last = line[1], line[#line] + if is_full then + return (string.find(first, ll) and string.find(last, rr)) ~= nil + end + return (string.find(string.sub(first, scol + 1, -1), ll) and string.find(string.sub(last, 0, ecol + 1), rr)) + ~= nil + end + + ------------------ + -- for linewise -- + ------------------ + if is_full then + return string.find(line, pattern) ~= nil + end + + -------------------------------- + -- for current-line blockwise -- + -------------------------------- + -- SOURCE: https://github.com/numToStr/Comment.nvim/issues/224 + return string.find(string.sub(line, scol + 1, (ecol > #line and #line or ecol + 1)), pattern) ~= nil + end +end + +---@private +---Error handler +---@param ... unknown +function U.catch(fn, ...) + xpcall(fn, function(err) + vim.notify(string.format('[Comment.nvim] %s', err.msg), vim.log.levels.WARN) + end, ...) +end + +return U diff --git a/config/neovim/store/lazy-plugins/comment.nvim/plugin/Comment.lua b/config/neovim/store/lazy-plugins/comment.nvim/plugin/Comment.lua new file mode 100644 index 00000000..485b97cb --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/plugin/Comment.lua @@ -0,0 +1,142 @@ +local K = vim.keymap.set +local call = require('Comment.api').call + +---@mod comment.keybindings Keybindings +---@brief [[ +---Comment.nvim provides default keybindings for (un)comment your code. These +---keybinds are enabled upon calling |comment.usage.setup| and can be configured +---or disabled, if desired. +--- +---Basic: ~ +--- +--- *gc* +--- *gb* +--- *gc[count]{motion}* +--- *gb[count]{motion}* +--- +--- Toggle comment on a region using linewise/blockwise comment. In 'NORMAL' +--- mode, it uses 'Operator-Pending' mode to listen for an operator/motion. +--- In 'VISUAL' mode it simply comment the selected region. +--- +--- *gcc* +--- *gbc* +--- *[count]gcc* +--- *[count]gbc* +--- +--- Toggle comment on the current line using linewise/blockwise comment. If +--- prefixed with a 'v:count' then it will comment over the number of lines +--- corresponding to the {count}. These are only available in 'NORMAL' mode. +--- +--- +---Extra: ~ +--- +--- *gco* - Inserts comment below and enters INSERT mode +--- *gcO* - Inserts comment above and enters INSERT mode +--- *gcA* - Inserts comment at the end of line and enters INSERT mode +---@brief ]] + +---@mod comment.plugmap Plug Mappings +---@brief [[ +---Comment.nvim provides mappings for most commonly used actions. These +---are enabled by default and can be used to make custom keybindings. All plug +---mappings have support for dot-repeat except VISUAL mode keybindings. To create +---custom comment function, check out 'comment.api' section. +--- +--- *(comment_toggle_linewise)* +--- *(comment_toggle_blockwise)* +--- +--- Toggle comment on a region with linewise/blockwise comment in NORMAL mode. +--- using |Operator-Pending| mode (or |g@|) to get the region to comment. +--- These powers the |gc| and |gb| keybindings. +--- +--- *(comment_toggle_linewise_current)* +--- *(comment_toggle_blockwise_current)* +--- +--- Toggle comment on the current line with linewise/blockwise comment in +--- NORMAL mode. These powers the |gcc| and 'gbc' keybindings. +--- +--- *(comment_toggle_linewise_count)* +--- *(comment_toggle_blockwise_count)* +--- +--- Toggle comment on a region using 'v:count' with linewise/blockwise comment +--- in NORMAL mode. These powers the |[count]gcc| and |[count]gbc| keybindings. +--- +--- *(comment_toggle_linewise_visual)* +--- *(comment_toggle_blockwise_visual)* +--- +--- Toggle comment on the selected region with linewise/blockwise comment in +--- NORMAL mode. These powers the |{visual}gc| and |{visual}gb| keybindings. +--- +---Usage: ~ +--->lua +--- -- Toggle current line or with count +--- vim.keymap.set('n', 'gcc', function() +--- return vim.v.count == 0 +--- and '(comment_toggle_linewise_current)' +--- or '(comment_toggle_linewise_count)' +--- end, { expr = true }) +--- +--- -- Toggle in Op-pending mode +--- vim.keymap.set('n', 'gc', '(comment_toggle_linewise)') +--- +--- -- Toggle in VISUAL mode +--- vim.keymap.set('x', 'gc', '(comment_toggle_linewise_visual)') +---< +---@brief ]] +---@export plugs + +-- Operator-Pending mappings +K( + 'n', + '(comment_toggle_linewise)', + call('toggle.linewise', 'g@'), + { expr = true, desc = 'Comment toggle linewise' } +) +K( + 'n', + '(comment_toggle_blockwise)', + call('toggle.blockwise', 'g@'), + { expr = true, desc = 'Comment toggle blockwise' } +) + +-- Toggle mappings +K( + 'n', + '(comment_toggle_linewise_current)', + call('toggle.linewise.current', 'g@$'), + { expr = true, desc = 'Comment toggle current line' } +) +K( + 'n', + '(comment_toggle_blockwise_current)', + call('toggle.blockwise.current', 'g@$'), + { expr = true, desc = 'Comment toggle current block' } +) + +-- Count mappings +K( + 'n', + '(comment_toggle_linewise_count)', + call('toggle.linewise.count_repeat', 'g@$'), + { expr = true, desc = 'Comment toggle linewise with count' } +) +K( + 'n', + '(comment_toggle_blockwise_count)', + call('toggle.blockwise.count_repeat', 'g@$'), + { expr = true, desc = 'Comment toggle blockwise with count' } +) + +-- Visual-Mode mappings +K( + 'x', + '(comment_toggle_linewise_visual)', + 'lua require("Comment.api").locked("toggle.linewise")(vim.fn.visualmode())', + { desc = 'Comment toggle linewise (visual)' } +) +K( + 'x', + '(comment_toggle_blockwise_visual)', + 'lua require("Comment.api").locked("toggle.blockwise")(vim.fn.visualmode())', + { desc = 'Comment toggle blockwise (visual)' } +) diff --git a/config/neovim/store/lazy-plugins/comment.nvim/stylua.toml b/config/neovim/store/lazy-plugins/comment.nvim/stylua.toml new file mode 100644 index 00000000..46b71c37 --- /dev/null +++ b/config/neovim/store/lazy-plugins/comment.nvim/stylua.toml @@ -0,0 +1,4 @@ +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 4 +quote_style = "AutoPreferSingle" diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.envrc b/config/neovim/store/lazy-plugins/conform.nvim/.envrc new file mode 100644 index 00000000..6a2e7a85 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.envrc @@ -0,0 +1,2 @@ +layout python +python -c 'import pyparsing' 2> /dev/null || pip install pyparsing==3.0.9 black isort mypy diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.github/ISSUE_TEMPLATE/bug_report.yml b/config/neovim/store/lazy-plugins/conform.nvim/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..f7c26327 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,129 @@ +name: Bug Report +description: File a bug/issue +title: "bug: " +labels: [bug] +body: + - type: markdown + attributes: + value: | + Before reporting a bug, make sure to search [existing issues](https://github.com/stevearc/conform.nvim/issues) + - type: input + attributes: + label: "Neovim version (nvim -v)" + placeholder: "0.8.0 commit db1b0ee3b30f" + validations: + required: true + - type: input + attributes: + label: "Operating system/version" + placeholder: "MacOS 11.5" + validations: + required: true + - type: checkboxes + attributes: + label: Add the debug logs + options: + - label: I have set `log_level = vim.log.levels.DEBUG` and pasted the log contents below. + required: true + - type: textarea + attributes: + label: "Log file" + description: "Recent contents from the log file. You can find it with :ConformInfo" + validations: + required: true + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + validations: + required: true + - type: dropdown + attributes: + label: What is the severity of this bug? + options: + - minor (annoyance) + - tolerable (can work around it) + - breaking (some functionality is broken) + - blocking (cannot use plugin) + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. nvim -u repro.lua + 2. + 3. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Minimal example file + description: A small example file you are editing that produces the issue + validations: + required: false + - type: textarea + attributes: + label: Minimal init.lua + description: + Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua` + This uses lazy.nvim (a plugin manager). + value: | + -- DO NOT change the paths and don't remove the colorscheme + local root = vim.fn.fnamemodify("./.repro", ":p") + + -- set stdpaths to use .repro + for _, name in ipairs({ "config", "data", "state", "cache" }) do + vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name + end + + -- bootstrap lazy + local lazypath = root .. "/plugins/lazy.nvim" + if not vim.loop.fs_stat(lazypath) then + vim.fn.system({ + "git", + "clone", + "--filter=blob:none", + "--single-branch", + "https://github.com/folke/lazy.nvim.git", + lazypath, + }) + end + vim.opt.runtimepath:prepend(lazypath) + + -- install plugins + local plugins = { + "folke/tokyonight.nvim", + { + "stevearc/conform.nvim", + config = function() + require("conform").setup({ + log_level = vim.log.levels.DEBUG, + -- add your config here + }) + end, + }, + -- add any other plugins here + } + require("lazy").setup(plugins, { + root = root .. "/plugins", + }) + + vim.cmd.colorscheme("tokyonight") + -- add anything else here + render: Lua + validations: + required: false + - type: textarea + attributes: + label: Additional context + description: Any additional information or screenshots you would like to provide + validations: + required: false diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.github/ISSUE_TEMPLATE/feature_request.yml b/config/neovim/store/lazy-plugins/conform.nvim/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..452e7f87 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,43 @@ +name: Feature Request +description: Submit a feature request +title: "feature request: " +labels: [enhancement] +body: + - type: markdown + attributes: + value: | + Before submitting a feature request, make sure to search for [existing requests](https://github.com/stevearc/conform.nvim/issues) + - type: checkboxes + attributes: + label: Did you check existing requests? + options: + - label: I have searched the existing issues + required: true + - type: textarea + attributes: + label: Describe the feature + description: A short summary of the feature you want + validations: + required: true + - type: textarea + attributes: + label: Provide background + description: Describe the reasoning behind why you want the feature. + placeholder: I am trying to do X. My current workflow is Y. + validations: + required: false + - type: dropdown + attributes: + label: What is the significance of this feature? + options: + - nice to have + - strongly desired + - cannot use this plugin without it + validations: + required: true + - type: textarea + attributes: + label: Additional details + description: Any additional information you would like to provide. Things you've tried, alternatives considered, examples from other plugins, etc. + validations: + required: false diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.github/pre-commit b/config/neovim/store/lazy-plugins/conform.nvim/.github/pre-commit new file mode 100755 index 00000000..a9dca986 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.github/pre-commit @@ -0,0 +1,3 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/bash +set -e +make fastlint diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.github/pre-push b/config/neovim/store/lazy-plugins/conform.nvim/.github/pre-push new file mode 100755 index 00000000..084e2873 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.github/pre-push @@ -0,0 +1,11 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/bash +set -e +IFS=' ' +while read local_ref _local_sha _remote_ref _remote_sha; do + remote_main=$( (git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null || echo "///master") | cut -f 4 -d / | tr -d "[:space:]") + local_ref_short=$(echo "$local_ref" | cut -f 3 -d / | tr -d "[:space:]") + if [ "$local_ref_short" = "$remote_main" ]; then + make lint + make test + fi +done diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/automation_remove_question_label_on_comment.yml b/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/automation_remove_question_label_on_comment.yml new file mode 100644 index 00000000..f99bba89 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/automation_remove_question_label_on_comment.yml @@ -0,0 +1,16 @@ +name: Remove Question Label on Issue Comment + +on: [issue_comment] + +jobs: + # Remove the "question" label when a new comment is added. + # This lets me ask a question, tag the issue with "question", and filter out all "question"-tagged + # issues in my "needs triage" filter. + remove_question: + runs-on: ubuntu-latest + if: github.event.sender.login != 'stevearc' + steps: + - uses: actions/checkout@v4 + - uses: actions-ecosystem/action-remove-labels@v1 + with: + labels: question diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/automation_request_review.yml b/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/automation_request_review.yml new file mode 100644 index 00000000..c31f5828 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/automation_request_review.yml @@ -0,0 +1,27 @@ +name: Request Review +permissions: + pull-requests: write +on: + pull_request_target: + types: [opened, reopened, ready_for_review, synchronize] + branches-ignore: + - "release-please--**" + +jobs: + # Request review automatically when PRs are opened + request_review: + runs-on: ubuntu-latest + steps: + - name: Request Review + uses: actions/github-script@v7 + if: github.actor != 'stevearc' + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const pr = context.payload.pull_request; + github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + reviewers: ['stevearc'] + }); diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/install_nvim.sh b/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/install_nvim.sh new file mode 100644 index 00000000..4c0203ce --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/install_nvim.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e +PLUGINS="$HOME/.local/share/nvim/site/pack/plugins/start" +mkdir -p "$PLUGINS" + +wget "https://github.com/neovim/neovim/releases/download/${NVIM_TAG-stable}/nvim.appimage" +chmod +x nvim.appimage +./nvim.appimage --appimage-extract >/dev/null +rm -f nvim.appimage +mkdir -p ~/.local/share/nvim +mv squashfs-root ~/.local/share/nvim/appimage +sudo ln -s "$HOME/.local/share/nvim/appimage/AppRun" /usr/bin/nvim diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/tests.yml b/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/tests.yml new file mode 100644 index 00000000..7e62300e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.github/workflows/tests.yml @@ -0,0 +1,124 @@ +name: Run tests + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + luacheck: + name: Luacheck + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: Prepare + run: | + sudo apt-get update + sudo add-apt-repository universe + sudo apt install luarocks -y + sudo luarocks install luacheck + + - name: Run Luacheck + run: luacheck lua tests + + typecheck: + name: typecheck + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: stevearc/nvim-typecheck-action@v1 + with: + path: lua + + stylua: + name: StyLua + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Stylua + uses: JohnnyMorganz/stylua-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: v0.20.0 + args: --check lua tests + + run_tests: + strategy: + matrix: + include: + - nvim_tag: v0.8.3 + - nvim_tag: v0.9.4 + - nvim_tag: v0.10.0 + + name: Run tests + runs-on: ubuntu-22.04 + env: + NVIM_TAG: ${{ matrix.nvim_tag }} + steps: + - uses: actions/checkout@v4 + + - name: Install Neovim and dependencies + run: | + bash ./.github/workflows/install_nvim.sh + + - name: Run tests + run: | + bash ./run_tests.sh + + update_docs: + name: Update docs + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: Install Neovim and dependencies + run: | + bash ./.github/workflows/install_nvim.sh + + - name: Update docs + run: | + python -m pip install pyparsing==3.0.9 + make doc + - name: Commit changes + if: ${{ github.ref == 'refs/heads/master' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMMIT_MSG: | + [docgen] Update docs + skip-checks: true + run: | + git config user.email "actions@github" + git config user.name "Github Actions" + git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git + git add README.md doc + # Only commit and push if we have changes + git diff --quiet && git diff --staged --quiet || (git commit -m "${COMMIT_MSG}"; git push origin HEAD:${GITHUB_REF}) + + release: + name: release + + if: ${{ github.ref == 'refs/heads/master' }} + needs: + - luacheck + - stylua + - typecheck + - run_tests + - update_docs + runs-on: ubuntu-22.04 + steps: + - uses: googleapis/release-please-action@v4 + id: release + with: + release-type: simple + - uses: actions/checkout@v4 + - uses: rickstaa/action-create-tag@v1 + if: ${{ steps.release.outputs.release_created }} + with: + tag: stable + message: "Current stable release: ${{ steps.release.outputs.tag_name }}" + tag_exists_error: false + force_push_tag: true diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.gitignore b/config/neovim/store/lazy-plugins/conform.nvim/.gitignore new file mode 100644 index 00000000..edbf0df5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.gitignore @@ -0,0 +1,48 @@ +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +.direnv/ +.testenv/ +doc/tags +tests/testfile.txt +tests/fake_formatter_output +scripts/nvim_doc_tools +scripts/nvim-typecheck-action diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.gitmodules b/config/neovim/store/lazy-plugins/conform.nvim/.gitmodules new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.luacheckrc b/config/neovim/store/lazy-plugins/conform.nvim/.luacheckrc new file mode 100644 index 00000000..7efefde4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.luacheckrc @@ -0,0 +1,19 @@ +max_comment_line_length = false +codes = true + +exclude_files = { + "tests/treesitter", +} + +ignore = { + "212", -- Unused argument + "631", -- Line is too long + "122", -- Setting a readonly global + "542", -- Empty if branch +} + +read_globals = { + "vim", + "a", + "assert", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/.stylua.toml b/config/neovim/store/lazy-plugins/conform.nvim/.stylua.toml new file mode 100644 index 00000000..020ce912 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/.stylua.toml @@ -0,0 +1,5 @@ +column_width = 100 +indent_type = "Spaces" +indent_width = 2 +[sort_requires] +enabled = true diff --git a/config/neovim/store/lazy-plugins/conform.nvim/CHANGELOG.md b/config/neovim/store/lazy-plugins/conform.nvim/CHANGELOG.md new file mode 100644 index 00000000..cde701cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/CHANGELOG.md @@ -0,0 +1,571 @@ +# Changelog + +## [5.8.0](https://github.com/stevearc/conform.nvim/compare/v5.7.0...v5.8.0) (2024-05-22) + + +### Features + +* add `ruff_organize_imports` formatter ([#418](https://github.com/stevearc/conform.nvim/issues/418)) ([184756b](https://github.com/stevearc/conform.nvim/commit/184756b7522f82ccdac0013adff1caa313cb897a)) +* add forge_fmt formatter support for solidity filetype ([#417](https://github.com/stevearc/conform.nvim/issues/417)) ([18a3fa4](https://github.com/stevearc/conform.nvim/commit/18a3fa45d841c941399b4559ef60b39f2e3ded7c)) +* add support for leptosfmt ([#415](https://github.com/stevearc/conform.nvim/issues/415)) ([b999fad](https://github.com/stevearc/conform.nvim/commit/b999fad66fd797a57d745c1a999d000b889bd587)) +* add support for typstyle ([#412](https://github.com/stevearc/conform.nvim/issues/412)) ([e47dcde](https://github.com/stevearc/conform.nvim/commit/e47dcde340c80645ab32b09da2c492174e6660c4)) + +## [5.7.0](https://github.com/stevearc/conform.nvim/compare/v5.6.0...v5.7.0) (2024-05-16) + + +### Features + +* add hcl formatter (hclfmt) ([#402](https://github.com/stevearc/conform.nvim/issues/402)) ([37cbcea](https://github.com/stevearc/conform.nvim/commit/37cbceab0a3b4979c4f0f1ae7aede0d0fa84c1d1)) +* add ktfmt formatter for Kotlin ([#392](https://github.com/stevearc/conform.nvim/issues/392)) ([b72f650](https://github.com/stevearc/conform.nvim/commit/b72f650206ddfeadd6c7df0f775a928e82ece353)) +* add ormolu formatter for Haskell ([#377](https://github.com/stevearc/conform.nvim/issues/377)) ([#397](https://github.com/stevearc/conform.nvim/issues/397)) ([6207f41](https://github.com/stevearc/conform.nvim/commit/6207f41e8f3813d72ef4499a8132c11d8baabe9f)) +* add snakefmt formatter for snakemake files ([#399](https://github.com/stevearc/conform.nvim/issues/399)) ([dc950e5](https://github.com/stevearc/conform.nvim/commit/dc950e5717f1da65b1fcd986b1bbff0d6bd0e2ee)) +* add support for bicep ([#368](https://github.com/stevearc/conform.nvim/issues/368)) ([588f357](https://github.com/stevearc/conform.nvim/commit/588f357d305943371de5c945aea65959fd4d80b9)) +* add support for bpfmt ([#405](https://github.com/stevearc/conform.nvim/issues/405)) ([3c278a7](https://github.com/stevearc/conform.nvim/commit/3c278a7e09e135e524545adcd725f89bfcc7ffbd)) +* add support for mdsf ([#380](https://github.com/stevearc/conform.nvim/issues/380)) ([34d3c5f](https://github.com/stevearc/conform.nvim/commit/34d3c5f58017b1a7e1cd23739b263d7af0f66d7c)) +* add support for yew-fmt ([#398](https://github.com/stevearc/conform.nvim/issues/398)) ([b52d462](https://github.com/stevearc/conform.nvim/commit/b52d462cb7bea5e81174ece43eb349357add2f11)) +* add verible formatter for SystemVerilog ([#391](https://github.com/stevearc/conform.nvim/issues/391)) ([fb2d35f](https://github.com/stevearc/conform.nvim/commit/fb2d35f2875967b92af9e1e7d31724ce0456fa83)) +* formatters can use $RELATIVE_FILEPATH in args ([#349](https://github.com/stevearc/conform.nvim/issues/349)) ([6dc1603](https://github.com/stevearc/conform.nvim/commit/6dc1603ea408f476a57937bbeaf7f86520a21a98)) + + +### Bug Fixes + +* **biome-check:** use safe fixes ([#373](https://github.com/stevearc/conform.nvim/issues/373)) ([500a6ae](https://github.com/stevearc/conform.nvim/commit/500a6ae6c10b2a96e85e64045ad9f3b16e2af7f8)) +* **biome:** support biome.jsonc file ([#394](https://github.com/stevearc/conform.nvim/issues/394)) ([3cd1135](https://github.com/stevearc/conform.nvim/commit/3cd1135cb2978c9d45b8dc6afc80045fb8a93157)) +* handle windows line ending when config.stdin is true ([#361](https://github.com/stevearc/conform.nvim/issues/361)) ([820eec9](https://github.com/stevearc/conform.nvim/commit/820eec990d5f332d30cf939954c8672a43a0459e)) +* **isort:** explicitly pass line endings ([#395](https://github.com/stevearc/conform.nvim/issues/395)) ([a3e3e0e](https://github.com/stevearc/conform.nvim/commit/a3e3e0e2966a9fa477bbc86487e920ee0c34f133)) +* lazily compute relative filepath ([40faaa8](https://github.com/stevearc/conform.nvim/commit/40faaa8fdd0b7f98f58070943306fd93abb5caad)) +* **mix:** allow mix formatter to format different filetypes ([#389](https://github.com/stevearc/conform.nvim/issues/389)) ([12b3995](https://github.com/stevearc/conform.nvim/commit/12b3995537f52ba2810a9857e8ca256881febbda)) +* **prettierd:** correctly find prettierd executable on windows ([#378](https://github.com/stevearc/conform.nvim/issues/378)) ([a6965ac](https://github.com/stevearc/conform.nvim/commit/a6965ac128eba75537ec2bc5ddd5d5e357062bdc)) +* refactor deprecated methods in neovim 0.10 ([7a205c9](https://github.com/stevearc/conform.nvim/commit/7a205c944d228ca0a5ec67656f59d20ba11ccca2)) +* **util:** new function throwing an error when the given extended value is nil ([#385](https://github.com/stevearc/conform.nvim/issues/385)) ([4660e53](https://github.com/stevearc/conform.nvim/commit/4660e534bf7678ee0f85879aa75fdcb6855612c2)) +* warning messages for improper async in format_on_save ([#401](https://github.com/stevearc/conform.nvim/issues/401)) ([59d0dd2](https://github.com/stevearc/conform.nvim/commit/59d0dd233a2cafacfa1235ab22054c4d80a72319)) +* **windows:** assertion failure when computing relative path ([#400](https://github.com/stevearc/conform.nvim/issues/400)) ([4f0cdf0](https://github.com/stevearc/conform.nvim/commit/4f0cdf07b5498935c34d6cfefde059a3a91584c4)) + +## [5.6.0](https://github.com/stevearc/conform.nvim/compare/v5.5.0...v5.6.0) (2024-03-28) + + +### Features + +* a formatter for SML (Standard ML) ([#353](https://github.com/stevearc/conform.nvim/issues/353)) ([ae6a069](https://github.com/stevearc/conform.nvim/commit/ae6a069e33027fc522151bf656ab06cf93abca46)) +* add formatter for Inko ([#351](https://github.com/stevearc/conform.nvim/issues/351)) ([6874087](https://github.com/stevearc/conform.nvim/commit/68740871bd1b7aecc3759136d576ecb704c4a636)) +* add ocp-indent for OCaml formatting ([#335](https://github.com/stevearc/conform.nvim/issues/335)) ([551d02f](https://github.com/stevearc/conform.nvim/commit/551d02f472b646cb82657700e3459f16d9933005)) +* add support for nimpretty ([#343](https://github.com/stevearc/conform.nvim/issues/343)) ([67f7fb2](https://github.com/stevearc/conform.nvim/commit/67f7fb2fe82d170c681e6c0da67aa848e6f5a742)) +* add support for purs-tidy ([#345](https://github.com/stevearc/conform.nvim/issues/345)) ([bf109f0](https://github.com/stevearc/conform.nvim/commit/bf109f061fc3cd75394b7823923187ae045cbf22)) +* add support for roc format ([#342](https://github.com/stevearc/conform.nvim/issues/342)) ([293236a](https://github.com/stevearc/conform.nvim/commit/293236aa7445fb24aba56d8e9a03be54d0c1c2e8)) +* **prettier, prettierd:** add mjs files to supported config files ([#350](https://github.com/stevearc/conform.nvim/issues/350)) ([ac4a022](https://github.com/stevearc/conform.nvim/commit/ac4a022c9e10e9697235f657f166372326139e8b)) +* support crystal tool format ([#344](https://github.com/stevearc/conform.nvim/issues/344)) ([f8c64b8](https://github.com/stevearc/conform.nvim/commit/f8c64b835f60a80639375500a80e8cd303d8eda6)) + + +### Bug Fixes + +* **injected:** ignore indentation of final whitespace line ([#340](https://github.com/stevearc/conform.nvim/issues/340)) ([0a530b3](https://github.com/stevearc/conform.nvim/commit/0a530b31acacf10eca9f9a74b2434ece4d232ca3)) +* **terraform_fmt:** do not output color escape codes ([#354](https://github.com/stevearc/conform.nvim/issues/354)) ([f3363ad](https://github.com/stevearc/conform.nvim/commit/f3363ad4b1453b0c9a591d2571c540ed145323d7)) +* use `--force-exclude` with Ruff ([#348](https://github.com/stevearc/conform.nvim/issues/348)) ([93f3d4c](https://github.com/stevearc/conform.nvim/commit/93f3d4cabe41473477a314c731e635175458f591)) + +## [5.5.0](https://github.com/stevearc/conform.nvim/compare/v5.4.0...v5.5.0) (2024-03-17) + + +### Features + +* add formatter config option to change name of temporary file ([#332](https://github.com/stevearc/conform.nvim/issues/332)) ([b059626](https://github.com/stevearc/conform.nvim/commit/b05962622d3eebeefe6b1a90deb9eb86947e0349)) + + +### Bug Fixes + +* **phpcbf:** use non-stdin formatting and customize tempfile name ([#333](https://github.com/stevearc/conform.nvim/issues/333)) ([67ee225](https://github.com/stevearc/conform.nvim/commit/67ee2258e08ccb91345d52f62484b657feccef25)) +* **rustfmt:** parse edition from Cargo.toml ([#330](https://github.com/stevearc/conform.nvim/issues/330)) ([a605ce4](https://github.com/stevearc/conform.nvim/commit/a605ce4b2db397c84ae6fa8bcfc85f00b985bc73)) +* **sqlfluff:** remove --force flag since it's default now ([#338](https://github.com/stevearc/conform.nvim/issues/338)) ([42f3d8e](https://github.com/stevearc/conform.nvim/commit/42f3d8e1c1a90e1114d12a49be838409cbbd1239)) + +## [5.4.0](https://github.com/stevearc/conform.nvim/compare/v5.3.0...v5.4.0) (2024-03-13) + + +### Features + +* add `gersemi` formatter ([#305](https://github.com/stevearc/conform.nvim/issues/305)) ([79d7fd9](https://github.com/stevearc/conform.nvim/commit/79d7fd9ee84e603bdb66038b1d1ed2703ec08d14)) +* add formatter sqlfmt ([#307](https://github.com/stevearc/conform.nvim/issues/307)) ([015f9e9](https://github.com/stevearc/conform.nvim/commit/015f9e90d945545b665dfda52e6c96590c3d1292)) +* add gleam formatter ([#327](https://github.com/stevearc/conform.nvim/issues/327)) ([2ebfcaa](https://github.com/stevearc/conform.nvim/commit/2ebfcaa4f2e85550d985eae8ed417401319ebccd)) +* add OpenTofu formatter ([#313](https://github.com/stevearc/conform.nvim/issues/313)) ([68dad93](https://github.com/stevearc/conform.nvim/commit/68dad93cde8d8b71e53d07ac43029d0fca06bf26)) +* add the cabal-fmt formatter ([#318](https://github.com/stevearc/conform.nvim/issues/318)) ([ea73026](https://github.com/stevearc/conform.nvim/commit/ea73026a163e124edb47fe48075091d924d139af)) +* **formatter:** add liquidsoap-prettier ([#312](https://github.com/stevearc/conform.nvim/issues/312)) ([dc873e9](https://github.com/stevearc/conform.nvim/commit/dc873e94f300cdadf0c1949c14b6e9137e7a9981)) + + +### Bug Fixes + +* add cwd to honor project php-cs-fixer ([#325](https://github.com/stevearc/conform.nvim/issues/325)) ([f5f8498](https://github.com/stevearc/conform.nvim/commit/f5f8498cf27931e06645c9fe020b9c28dce49d98)) +* **prettier:** Fix range formatting of buffer ([#322](https://github.com/stevearc/conform.nvim/issues/322)) ([bc93756](https://github.com/stevearc/conform.nvim/commit/bc937565f251866c0ff344fd13fe27f00a4c0d25)) +* remove call to deprecated tbl_add_reverse_lookup ([5a15cc4](https://github.com/stevearc/conform.nvim/commit/5a15cc46e75cad804fd51ec5af9227aeb1d1bdaa)) +* **rustfmt:** use Cargo.toml settings and default to recent edition ([#328](https://github.com/stevearc/conform.nvim/issues/328)) ([0ff1b7d](https://github.com/stevearc/conform.nvim/commit/0ff1b7d32fd3e8df194ca5ebec1dab9c61fb9911)) +* **swiftformat:** range formatting support and add cwd ([#326](https://github.com/stevearc/conform.nvim/issues/326)) ([db2c697](https://github.com/stevearc/conform.nvim/commit/db2c697fe8302f0328b50b480204be1b577a1e2f)) + +## [5.3.0](https://github.com/stevearc/conform.nvim/compare/v5.2.1...v5.3.0) (2024-02-20) + + +### Features + +* add awk formatter ([#286](https://github.com/stevearc/conform.nvim/issues/286)) ([338c307](https://github.com/stevearc/conform.nvim/commit/338c3070ae7f7028185ae6123541c2ca71cfe7ff)) +* add biome-check formatter ([#287](https://github.com/stevearc/conform.nvim/issues/287)) ([5a71b60](https://github.com/stevearc/conform.nvim/commit/5a71b6064ec6ecf0fff91af67e95200aae9e9562)) +* add fantomas formatter ([#302](https://github.com/stevearc/conform.nvim/issues/302)) ([0d99714](https://github.com/stevearc/conform.nvim/commit/0d997149a0472ab811bcfdca5dc45d9db483f949)) +* Add reorder-python-imports formatter ([#284](https://github.com/stevearc/conform.nvim/issues/284)) ([9a07f60](https://github.com/stevearc/conform.nvim/commit/9a07f60f7499cdc76ed40af62bb9a50ac928d7d2)) +* add ReScript formatter ([#293](https://github.com/stevearc/conform.nvim/issues/293)) ([a34b66f](https://github.com/stevearc/conform.nvim/commit/a34b66f9a4a8f4fb8e270ebfa9c8836fdb8381c1)) +* add terragrunt_hclfmt formatter ([#278](https://github.com/stevearc/conform.nvim/issues/278)) ([375258f](https://github.com/stevearc/conform.nvim/commit/375258f1fe1500f175d7135aef1dc6a87dbd83b2)) +* add twig-cs-fixer ([#304](https://github.com/stevearc/conform.nvim/issues/304)) ([766812b](https://github.com/stevearc/conform.nvim/commit/766812b0e830c2e40613f99f89102d8840431c6a)) +* add yq formatter ([#288](https://github.com/stevearc/conform.nvim/issues/288)) ([15c4a02](https://github.com/stevearc/conform.nvim/commit/15c4a0273bb5468004bb46f632dc5326bc5634d7)) + + +### Bug Fixes + +* `swift_format` doesn't respect `.swift-format` file ([#283](https://github.com/stevearc/conform.nvim/issues/283)) ([4588008](https://github.com/stevearc/conform.nvim/commit/4588008a7c5b57fbff97fdfb529c059235cdc7ee)) +* set a cwd for biome ([#282](https://github.com/stevearc/conform.nvim/issues/282)) ([03feeb5](https://github.com/stevearc/conform.nvim/commit/03feeb5024a4b44754d63dec55b79b8133a8ea9f)) + +## [5.2.1](https://github.com/stevearc/conform.nvim/compare/v5.2.0...v5.2.1) (2024-01-21) + + +### Bug Fixes + +* handle windows line endings ([#274](https://github.com/stevearc/conform.nvim/issues/274)) ([9a785eb](https://github.com/stevearc/conform.nvim/commit/9a785eb8f0199ac47ce8bb9e9b6103de5ad8e3a7)) + +## [5.2.0](https://github.com/stevearc/conform.nvim/compare/v5.1.0...v5.2.0) (2024-01-16) + + +### Features + +* add cue_fmt formatter ([#265](https://github.com/stevearc/conform.nvim/issues/265)) ([03de11a](https://github.com/stevearc/conform.nvim/commit/03de11a0dcf686fda58d64a895483e284dd0c5b6)) +* Add dry_run option and report if buffer was/would be changed by formatters ([#273](https://github.com/stevearc/conform.nvim/issues/273)) ([e0276bb](https://github.com/stevearc/conform.nvim/commit/e0276bb32e9b33ece11fef2a5cfc8fb2108df0df)) +* add opa_fmt formatter ([#267](https://github.com/stevearc/conform.nvim/issues/267)) ([a4e84d5](https://github.com/stevearc/conform.nvim/commit/a4e84d56d5959dae685c5e22db202cd86b5b322b)) +* add xmllint formatter ([#259](https://github.com/stevearc/conform.nvim/issues/259)) ([c50ba4b](https://github.com/stevearc/conform.nvim/commit/c50ba4baad90f02840cc31ee745b09078b7a1777)) +* **formatexpr:** don't require LSP range formatting if operating on whole file ([#272](https://github.com/stevearc/conform.nvim/issues/272)) ([47ceff6](https://github.com/stevearc/conform.nvim/commit/47ceff644e9d00872f410be374cc973eefa20ba9)) + + +### Bug Fixes + +* **black:** formatting excluded files results in blank buffer ([#254](https://github.com/stevearc/conform.nvim/issues/254)) ([c4b2efb](https://github.com/stevearc/conform.nvim/commit/c4b2efb8aee4af0ef179a9b49ba401de3c4ef5d2)) +* copy input parameters for will_fallback_lsp ([ad347d7](https://github.com/stevearc/conform.nvim/commit/ad347d70e66737a8b9d62c19df1c0e2c5b2cd008)) +* injected formatter works on nightly ([#270](https://github.com/stevearc/conform.nvim/issues/270)) ([229e9ab](https://github.com/stevearc/conform.nvim/commit/229e9ab5d6e90bc5e6d24141dce3cc28ba95293a)) +* LSP deprecated method warning on nvim nightly ([75e7c5c](https://github.com/stevearc/conform.nvim/commit/75e7c5c7eb5fbd53f8b12dc420b31ec70770b231)) +* pass explicit bufnr to avoid race conditions ([#260](https://github.com/stevearc/conform.nvim/issues/260)) ([a8e3935](https://github.com/stevearc/conform.nvim/commit/a8e39359814b7b5df5fac7423b4dc93826d64464)) +* set a cwd for djlint ([#264](https://github.com/stevearc/conform.nvim/issues/264)) ([0802406](https://github.com/stevearc/conform.nvim/commit/08024063232a7bd38ecdfaf89f06162a5ba2df91)) +* set a cwd for dprint ([#263](https://github.com/stevearc/conform.nvim/issues/263)) ([e6c1353](https://github.com/stevearc/conform.nvim/commit/e6c135338257f69c018e8351a6e5f63683f86318)) + +## [5.1.0](https://github.com/stevearc/conform.nvim/compare/v5.0.0...v5.1.0) (2023-12-26) + + +### Features + +* add fnlfmt formatter ([#247](https://github.com/stevearc/conform.nvim/issues/247)) ([af6643a](https://github.com/stevearc/conform.nvim/commit/af6643afa10e17c0228da97c84d4c32f144a6ad3)) +* ConformInfo shows path to executable ([#244](https://github.com/stevearc/conform.nvim/issues/244)) ([fb9b050](https://github.com/stevearc/conform.nvim/commit/fb9b0500270ba05b89cc27cd8b7762443bcfae22)) +* **prettier:** add `options` for configuring prettier parser based on filetype and extension ([#241](https://github.com/stevearc/conform.nvim/issues/241)) ([8df1bed](https://github.com/stevearc/conform.nvim/commit/8df1bed7b8de9cf40476996fb5ab73ed667aed35)) + + +### Bug Fixes + +* crash in error handling ([4185249](https://github.com/stevearc/conform.nvim/commit/41852493b5abd7b5a0fd61ff007994c777a08ec9)) +* **formatexpr:** does not fallback to the built-in formatexpr ([#238](https://github.com/stevearc/conform.nvim/issues/238)) ([48bc999](https://github.com/stevearc/conform.nvim/commit/48bc9996ebfe90e7766f46338360f75fd6ecb174)) +* **injected:** code block at end of markdown file ([9245b61](https://github.com/stevearc/conform.nvim/commit/9245b616d1edb159775a0832c03324bf92884494)) +* **injected:** handle inline injections ([#251](https://github.com/stevearc/conform.nvim/issues/251)) ([f245cca](https://github.com/stevearc/conform.nvim/commit/f245cca8ad42c9d344b53a18c3fc1a3c6724c2d4)) +* **prettier:** use correct prettier executable on windows ([#236](https://github.com/stevearc/conform.nvim/issues/236)) ([7396fc0](https://github.com/stevearc/conform.nvim/commit/7396fc0208539e2bd70e3e446f27529e28dba12b)) +* **rubocop:** pass --server for faster execution ([#246](https://github.com/stevearc/conform.nvim/issues/246)) ([0ec6edd](https://github.com/stevearc/conform.nvim/commit/0ec6edd67689e8df6726b83333106bcec13c36d4)) + +## [5.0.0](https://github.com/stevearc/conform.nvim/compare/v4.3.0...v5.0.0) (2023-12-07) + + +### ⚠ BREAKING CHANGES + +* formatter config functions take self as first argument ([#233](https://github.com/stevearc/conform.nvim/issues/233)) + +### Features + +* add asmfmt ([#239](https://github.com/stevearc/conform.nvim/issues/239)) ([a5ef494](https://github.com/stevearc/conform.nvim/commit/a5ef4943f6382f36a5a8d6e16eb0a0c60af5e7a5)) +* add joker for clojure formatting ([#240](https://github.com/stevearc/conform.nvim/issues/240)) ([6b13100](https://github.com/stevearc/conform.nvim/commit/6b1310014ceec5752fd5859f9cc62ef7c93d72b2)) + + +### Code Refactoring + +* formatter config functions take self as first argument ([#233](https://github.com/stevearc/conform.nvim/issues/233)) ([659838f](https://github.com/stevearc/conform.nvim/commit/659838ff4244ef6af095395ce68aaaf99fa8e696)) + +## [4.3.0](https://github.com/stevearc/conform.nvim/compare/v4.2.0...v4.3.0) (2023-12-07) + + +### Features + +* add `auto-optional` ([#196](https://github.com/stevearc/conform.nvim/issues/196)) ([9156364](https://github.com/stevearc/conform.nvim/commit/9156364c23cff19734a0055377321c22b1484c0f)) +* add `typos` ([#214](https://github.com/stevearc/conform.nvim/issues/214)) ([d86c186](https://github.com/stevearc/conform.nvim/commit/d86c186ba910d28a6266c4d6210578dca984f3e3)) +* add autocorrect ([#223](https://github.com/stevearc/conform.nvim/issues/223)) ([cd81d21](https://github.com/stevearc/conform.nvim/commit/cd81d215d39b16186186a1539c71b48705bb081d)) +* add beancount formatter ([#212](https://github.com/stevearc/conform.nvim/issues/212)) ([c0924a6](https://github.com/stevearc/conform.nvim/commit/c0924a61e079d94f0be40da2d4188210c6e4ffea)) +* add cbfmt ([#198](https://github.com/stevearc/conform.nvim/issues/198)) ([aa36bc0](https://github.com/stevearc/conform.nvim/commit/aa36bc05563d5390a2ef67956d72560048acdc2e)) +* add fourmolu support ([#209](https://github.com/stevearc/conform.nvim/issues/209)) ([e688864](https://github.com/stevearc/conform.nvim/commit/e688864883aa4f468cc73a4c1db661c7c94addc4)) +* add jsonnetfmt ([#230](https://github.com/stevearc/conform.nvim/issues/230)) ([769dde8](https://github.com/stevearc/conform.nvim/commit/769dde8ddccf8338c68da706e46fd2fb004e6455)) +* add packer formatter ([#202](https://github.com/stevearc/conform.nvim/issues/202)) ([a0cabaa](https://github.com/stevearc/conform.nvim/commit/a0cabaaf5c94137c8dc34043244a34b552860af6)) +* add pangu ([#188](https://github.com/stevearc/conform.nvim/issues/188)) ([f0780e2](https://github.com/stevearc/conform.nvim/commit/f0780e2231df2e4751e31db32c1545872412ba75)) +* add phpinsights ([#170](https://github.com/stevearc/conform.nvim/issues/170)) ([5235405](https://github.com/stevearc/conform.nvim/commit/5235405cc6d4ac98dc9008ffa850038e3325bbce)) +* add styler formatter for R ([#184](https://github.com/stevearc/conform.nvim/issues/184)) ([6afc64e](https://github.com/stevearc/conform.nvim/commit/6afc64e9f36cbae35c2a8b6852d0b91c9807a72a)) +* add support for buildifier ([#216](https://github.com/stevearc/conform.nvim/issues/216)) ([e478834](https://github.com/stevearc/conform.nvim/commit/e478834227e0958e21a54f31c9cd896a3a8bdde0)) +* add support for sqlfluff ([#213](https://github.com/stevearc/conform.nvim/issues/213)) ([e8c8683](https://github.com/stevearc/conform.nvim/commit/e8c8683a00fb932dfe669e1c96832da12b8054bd)) + + +### Bug Fixes + +* **biome:** perform formatting over stdin ([#220](https://github.com/stevearc/conform.nvim/issues/220)) ([eddd643](https://github.com/stevearc/conform.nvim/commit/eddd6431370814caacec1d1e3c7d6d95d41b133d)) +* **biome:** use binary from node_modules ([#226](https://github.com/stevearc/conform.nvim/issues/226)) ([5bf1405](https://github.com/stevearc/conform.nvim/commit/5bf1405fd234d469243ea6f394e0aeec9ea53bd8)) +* injected formatter adds language to file extension ([#199](https://github.com/stevearc/conform.nvim/issues/199)) ([e2b889e](https://github.com/stevearc/conform.nvim/commit/e2b889e26586acf30dda7b4a5c3f1a063bc18f18)) +* injected parser shouldn't format combined injections ([#205](https://github.com/stevearc/conform.nvim/issues/205)) ([eeef888](https://github.com/stevearc/conform.nvim/commit/eeef88849fb644d84a5856524adf10d0ad2d7cbe)) +* invalid prettier configuration in last commit ([e8ac7f1](https://github.com/stevearc/conform.nvim/commit/e8ac7f1a9a3973ecce6942b2f26d16e65902aa70)) +* range format method for async formatters and injected ([a36c68d](https://github.com/stevearc/conform.nvim/commit/a36c68d2cd551e49883ddb2492c178d915567f58)) +* respect excluded-files-config from `typos.toml` ([#219](https://github.com/stevearc/conform.nvim/issues/219)) ([db9da1a](https://github.com/stevearc/conform.nvim/commit/db9da1aa57e8be683ada1b1e5f8129c28d2576eb)) +* show more logs in ConformInfo when log level is TRACE ([0963118](https://github.com/stevearc/conform.nvim/commit/0963118e60e0895e2e4842aeffc67cdf9e2bcd10)) +* various fixes for the `injected` formatter ([#235](https://github.com/stevearc/conform.nvim/issues/235)) ([07fcbfc](https://github.com/stevearc/conform.nvim/commit/07fcbfc13490786f5983bce3f404643fcfd83775)) + +## [4.2.0](https://github.com/stevearc/conform.nvim/compare/v4.1.0...v4.2.0) (2023-11-09) + + +### Features + +* add typstfmt ([#180](https://github.com/stevearc/conform.nvim/issues/180)) ([b1f1194](https://github.com/stevearc/conform.nvim/commit/b1f1194338c96d385ec6370ac734ab63c0289776)) + + +### Bug Fixes + +* catch jobstart errors ([#183](https://github.com/stevearc/conform.nvim/issues/183)) ([dcbe650](https://github.com/stevearc/conform.nvim/commit/dcbe650bd4811cefe5a885fafb6309c7d592bda6)) +* injected formatter not working ([#187](https://github.com/stevearc/conform.nvim/issues/187)) ([68abada](https://github.com/stevearc/conform.nvim/commit/68abada5a348f448eabdbd7d71884c195969484f)) +* nonzero exit code on :wq ([#176](https://github.com/stevearc/conform.nvim/issues/176)) ([161d95b](https://github.com/stevearc/conform.nvim/commit/161d95bfbb1ad1a2b89ba2ea75ca1b5e012a111e)) +* rename `astgrep` to `ast-grep` ([#178](https://github.com/stevearc/conform.nvim/issues/178)) ([bfa69a9](https://github.com/stevearc/conform.nvim/commit/bfa69a942e19159d3a3e958a5be85cb7cdae19a7)) + +## [4.1.0](https://github.com/stevearc/conform.nvim/compare/v4.0.0...v4.1.0) (2023-11-05) + + +### Features + +* add `ast-grep` ([#177](https://github.com/stevearc/conform.nvim/issues/177)) ([fa3cf1c](https://github.com/stevearc/conform.nvim/commit/fa3cf1c40716492fd0df0c3dedd54c8018f9ea70)) +* add CSharpier ([#165](https://github.com/stevearc/conform.nvim/issues/165)) ([b2368ff](https://github.com/stevearc/conform.nvim/commit/b2368ff18a9dd9452170d3a6f41b1f872ae5d0b2)) +* add markdownlint-cli2 ([#171](https://github.com/stevearc/conform.nvim/issues/171)) ([9bb3a94](https://github.com/stevearc/conform.nvim/commit/9bb3a940389dda796192a477a016069472692526)) +* add mdslw markdown formatter ([#175](https://github.com/stevearc/conform.nvim/issues/175)) ([369c7fe](https://github.com/stevearc/conform.nvim/commit/369c7fe690b3fec0ecdd7c17faeebf3f8113a0f5)) +* add pretty-php ([#161](https://github.com/stevearc/conform.nvim/issues/161)) ([4653408](https://github.com/stevearc/conform.nvim/commit/4653408d5c270168e31ffd0585d1cf2de27fc827)) +* add puppet-lint formatter ([#153](https://github.com/stevearc/conform.nvim/issues/153)) ([0219648](https://github.com/stevearc/conform.nvim/commit/0219648cd9a2bafc13fda64903e49fda5db0016b)) +* add tlint ([#162](https://github.com/stevearc/conform.nvim/issues/162)) ([2538784](https://github.com/stevearc/conform.nvim/commit/253878436e2b6d73dfd91ccf0ac12d04cc683d34)) +* add usort ([#167](https://github.com/stevearc/conform.nvim/issues/167)) ([f7766d2](https://github.com/stevearc/conform.nvim/commit/f7766d2fbe23f0f22a3db1513beba7d03a8dc261)) +* allow formatters_by_ft to be a function ([#174](https://github.com/stevearc/conform.nvim/issues/174)) ([0bbe838](https://github.com/stevearc/conform.nvim/commit/0bbe83830be5a07a1161bb1a23d7280310656177)) +* gn build file format cmd ([#155](https://github.com/stevearc/conform.nvim/issues/155)) ([3716927](https://github.com/stevearc/conform.nvim/commit/37169273a0776752a3c01cbe01227e275b642b89)) +* zprint formatter for clojure ([#146](https://github.com/stevearc/conform.nvim/issues/146)) ([2800552](https://github.com/stevearc/conform.nvim/commit/280055248661a4fc7b692db2d5ee80a465ebb577)) + + +### Bug Fixes + +* **formatexpr:** use default formatexpr if no formatters or LSP clients ([#55](https://github.com/stevearc/conform.nvim/issues/55)) ([278bcd8](https://github.com/stevearc/conform.nvim/commit/278bcd8bf2017e187e963b515017341fdd87fe2f)) +* **rubyfmt:** exit code 1 should not be a success ([#157](https://github.com/stevearc/conform.nvim/issues/157)) ([e4ecb6e](https://github.com/stevearc/conform.nvim/commit/e4ecb6e8ed3163c86d7e647f1dc3d94de77ca687)) + +## [4.0.0](https://github.com/stevearc/conform.nvim/compare/v3.10.0...v4.0.0) (2023-10-16) + + +### ⚠ BREAKING CHANGES + +* merge configs in conform.formatters with defaults ([#140](https://github.com/stevearc/conform.nvim/issues/140)) + +### Features + +* add blade-formatter ([#136](https://github.com/stevearc/conform.nvim/issues/136)) ([f90b222](https://github.com/stevearc/conform.nvim/commit/f90b2229c481252c43a71a004972b473952c1c3c)) +* add blue formatter ([#142](https://github.com/stevearc/conform.nvim/issues/142)) ([a97ddff](https://github.com/stevearc/conform.nvim/commit/a97ddfff2d701245ad49daf24ef436a50ee72a50)) +* Add config for laravel/pint ([#144](https://github.com/stevearc/conform.nvim/issues/144)) ([43414c8](https://github.com/stevearc/conform.nvim/commit/43414c8ebd22921f44806fb9612a2f4f376419af)) +* add goimports-reviser ([#143](https://github.com/stevearc/conform.nvim/issues/143)) ([3fcebb0](https://github.com/stevearc/conform.nvim/commit/3fcebb0001e6d5b943dbb36fe5c035e3ef8c3509)) +* add ktlint ([#137](https://github.com/stevearc/conform.nvim/issues/137)) ([8b02f47](https://github.com/stevearc/conform.nvim/commit/8b02f478fefe93f76a7f57c983418744287f4c69)) +* add rufo support ([#132](https://github.com/stevearc/conform.nvim/issues/132)) ([aca5d30](https://github.com/stevearc/conform.nvim/commit/aca5d307232a22600bd0ab57571a8b6e2dc9a12c)) +* merge configs in conform.formatters with defaults ([#140](https://github.com/stevearc/conform.nvim/issues/140)) ([7027ebb](https://github.com/stevearc/conform.nvim/commit/7027ebbd772e2d3593f7dd566dea06d2d20622ee)) +* support for rubyfmt ([#139](https://github.com/stevearc/conform.nvim/issues/139)) ([ae33777](https://github.com/stevearc/conform.nvim/commit/ae337775e46804a8347ea7c3da92be5587e5850e)) + + +### Bug Fixes + +* prevent format-after-save autocmd from running on invalid buffers ([80f2f70](https://github.com/stevearc/conform.nvim/commit/80f2f70740431b07d725cc66f63abbfd66aaae6d)) +* prevent format-on-save autocmd from running on invalid buffers ([#128](https://github.com/stevearc/conform.nvim/issues/128)) ([69ee0bf](https://github.com/stevearc/conform.nvim/commit/69ee0bfde439e30344ae57de6227cb3a035dd0bb)) +* **shellcheck:** support filenames with spaces ([#135](https://github.com/stevearc/conform.nvim/issues/135)) ([64a8956](https://github.com/stevearc/conform.nvim/commit/64a89568925c3f62b7ecdcf60b612001d2749eb1)) + +## [3.10.0](https://github.com/stevearc/conform.nvim/compare/v3.9.0...v3.10.0) (2023-10-09) + + +### Features + +* add easy-coding-standard ([#121](https://github.com/stevearc/conform.nvim/issues/121)) ([e758196](https://github.com/stevearc/conform.nvim/commit/e75819642c36810a55a7235b6b5e16a5ce896ed3)) +* add fixjson ([#126](https://github.com/stevearc/conform.nvim/issues/126)) ([280360e](https://github.com/stevearc/conform.nvim/commit/280360eb019fe52433a68b7918790c9187076865)) +* add justfile formatter ([#114](https://github.com/stevearc/conform.nvim/issues/114)) ([4c91b52](https://github.com/stevearc/conform.nvim/commit/4c91b5270a6f741850de2eef3a804ff1dc6ec3ee)) +* errors do not stop formatting early ([a94f686](https://github.com/stevearc/conform.nvim/commit/a94f686986631d5b97bd75b3877813c39de55c47)) +* expose configuration options for injected formatter ([#118](https://github.com/stevearc/conform.nvim/issues/118)) ([ba1ca20](https://github.com/stevearc/conform.nvim/commit/ba1ca20bb5f89a8bdd94b268411263275550843a)) + + +### Bug Fixes + +* **biome:** do not use stdin due to biome bug ([#120](https://github.com/stevearc/conform.nvim/issues/120)) ([e5ed063](https://github.com/stevearc/conform.nvim/commit/e5ed0635d9aa66c6c2f7eac3235e6a8eb2de0653)) +* catch and fix more cases of bad-behaving LSP formatters ([#119](https://github.com/stevearc/conform.nvim/issues/119)) ([9bd1690](https://github.com/stevearc/conform.nvim/commit/9bd169029ac7fac5d0b3899a47556549d113a4c2)) +* handle one failure mode with range formatting ([#123](https://github.com/stevearc/conform.nvim/issues/123)) ([b5a2da9](https://github.com/stevearc/conform.nvim/commit/b5a2da9410d56bd7bc229d0185ad427a966cac50)) +* injected formatter handles markdown code blocks in blockquotes ([#117](https://github.com/stevearc/conform.nvim/issues/117)) ([0bffab5](https://github.com/stevearc/conform.nvim/commit/0bffab53672d62cbfe8fc450e78757982e656318)) +* move justfile formatter to correct directory ([8217144](https://github.com/stevearc/conform.nvim/commit/8217144491e8aba3a24828a71ee768b007a2ec43)) + +## [3.9.0](https://github.com/stevearc/conform.nvim/compare/v3.8.0...v3.9.0) (2023-10-04) + + +### Features + +* add phpcbf ([#103](https://github.com/stevearc/conform.nvim/issues/103)) ([db5af4b](https://github.com/stevearc/conform.nvim/commit/db5af4b04e5d61236a142ab78ec3f9416aab848c)) +* gci formatter for Go ([#109](https://github.com/stevearc/conform.nvim/issues/109)) ([362e4ec](https://github.com/stevearc/conform.nvim/commit/362e4ec709d241e47d6093dd4b030125ce214cfa)) + + +### Bug Fixes + +* format on save autocmds ignore nonstandard buffers ([cb87cab](https://github.com/stevearc/conform.nvim/commit/cb87cab7a6baa6192bf13123c2a5af6fd059d62c)) +* injected formatter silent failure on nvim nightly ([#100](https://github.com/stevearc/conform.nvim/issues/100)) ([0156beb](https://github.com/stevearc/conform.nvim/commit/0156beb8397169d7ec18d4f4ea8dd002ee9bcc96)) +* phpcbf invalid stdin-path arguments ([#108](https://github.com/stevearc/conform.nvim/issues/108)) ([ce427b0](https://github.com/stevearc/conform.nvim/commit/ce427b03b9cc428ee7a64cb77487ed19efec202d)) +* support for mix format ([#107](https://github.com/stevearc/conform.nvim/issues/107)) ([6836930](https://github.com/stevearc/conform.nvim/commit/6836930ed5a0ec6e8bb531116c62cc10f475c298)) + +## [3.8.0](https://github.com/stevearc/conform.nvim/compare/v3.7.2...v3.8.0) (2023-10-02) + + +### Features + +* add 'google-java-format' formatter ([#99](https://github.com/stevearc/conform.nvim/issues/99)) ([e887736](https://github.com/stevearc/conform.nvim/commit/e8877369df244515af20e18bf1307632fc638d2a)) +* add standardrb ([#91](https://github.com/stevearc/conform.nvim/issues/91)) ([37d0367](https://github.com/stevearc/conform.nvim/commit/37d036704a100ef6e6457be45b4dfc2f8e429572)) +* metatable to make accessing formatters a bit easier ([#89](https://github.com/stevearc/conform.nvim/issues/89)) ([d8170c1](https://github.com/stevearc/conform.nvim/commit/d8170c14db0f3c90fa799db3bca29d3fb3c089c3)) + + +### Bug Fixes + +* alternations follow notification rules ([3f89275](https://github.com/stevearc/conform.nvim/commit/3f8927532bc8ce4fc4b5b75eab1bf8f1fc83f6b9)) +* error handling for injected formatter ([f7b82fb](https://github.com/stevearc/conform.nvim/commit/f7b82fb395a4cd636a26ee879b5fd7690612e5a9)) +* injected formatter doesn't have interruption errors ([af3d59d](https://github.com/stevearc/conform.nvim/commit/af3d59da20d2bc37933df409f8fc9e24ec15e066)) +* injected formatter operates on input lines ([501319e](https://github.com/stevearc/conform.nvim/commit/501319eed2ff26f856ea91b5456bef1d00f77df7)) + +## [3.7.2](https://github.com/stevearc/conform.nvim/compare/v3.7.1...v3.7.2) (2023-09-29) + + +### Bug Fixes + +* injected formatter hangs on empty file ([671186e](https://github.com/stevearc/conform.nvim/commit/671186e4b29e26ee9fc0f1df4e529134bc334666)) +* injected formatter preserves indentation of code blocks ([470d419](https://github.com/stevearc/conform.nvim/commit/470d41988e83913df428c9e832c15b8bb84301ad)) +* lsp format calls method from wrong util file ([df69e3e](https://github.com/stevearc/conform.nvim/commit/df69e3ee61e1a0cbb960c8466ace74c696cc7830)) + +## [3.7.1](https://github.com/stevearc/conform.nvim/compare/v3.7.0...v3.7.1) (2023-09-29) + + +### Bug Fixes + +* format_after_save blocks on exit for lsp formatting ([0c52ee2](https://github.com/stevearc/conform.nvim/commit/0c52ee248245f40610a4957b6bc9515ce1fd9ab6)) + +## [3.7.0](https://github.com/stevearc/conform.nvim/compare/v3.6.0...v3.7.0) (2023-09-29) + + +### Features + +* add 'JavaScript Standard Style' formatter ([#82](https://github.com/stevearc/conform.nvim/issues/82)) ([971fa7f](https://github.com/stevearc/conform.nvim/commit/971fa7f2e4005454ce141ca8ee0462a3c34d2922)) +* add darker ([#80](https://github.com/stevearc/conform.nvim/issues/80)) ([e359687](https://github.com/stevearc/conform.nvim/commit/e359687e3684452ff45d7a5f1a59cd40b0bfa320)) +* format injected languages ([#83](https://github.com/stevearc/conform.nvim/issues/83)) ([a5526fb](https://github.com/stevearc/conform.nvim/commit/a5526fb2ee963cf426ab6d6ba1f3eb82887b1c22)) + + +### Bug Fixes + +* format_after_save autocmd blocks nvim exit until complete ([388d6e2](https://github.com/stevearc/conform.nvim/commit/388d6e2440bccded26d5e67ce6a7039c1953ae70)) +* only show "no formatters" warning if formatters passed in explicitly ([#85](https://github.com/stevearc/conform.nvim/issues/85)) ([45edf94](https://github.com/stevearc/conform.nvim/commit/45edf9462d06db0809d4a4a7afc6b7896b63fa35)) + +## [3.6.0](https://github.com/stevearc/conform.nvim/compare/v3.5.0...v3.6.0) (2023-09-27) + + +### Features + +* add `markdown-toc` ([#75](https://github.com/stevearc/conform.nvim/issues/75)) ([de58b06](https://github.com/stevearc/conform.nvim/commit/de58b06d434047c6ecd5ec2d52877335d37b05fd)) +* Add support for php-cs-fixer ([#78](https://github.com/stevearc/conform.nvim/issues/78)) ([e691eca](https://github.com/stevearc/conform.nvim/commit/e691ecaf41139a68ccb79fde824cb534ca11abd2)) +* add templ support ([#73](https://github.com/stevearc/conform.nvim/issues/73)) ([28ecd5c](https://github.com/stevearc/conform.nvim/commit/28ecd5cf9132213417bff41d79477354cb81f50c)) +* another utility for extending formatter arguments ([aada09c](https://github.com/stevearc/conform.nvim/commit/aada09c9cfea38187966ce47f34b9008e1104d21)) +* new utility function ([9e1fcd5](https://github.com/stevearc/conform.nvim/commit/9e1fcd5cafc42b5dfbe2e942ddbece0dada4e1d0)) + + +### Bug Fixes + +* rubocop succeeds even if some errors are not autocorrected ([#74](https://github.com/stevearc/conform.nvim/issues/74)) ([34daf23](https://github.com/stevearc/conform.nvim/commit/34daf23415e9d212697f79506039498db2b35240)) + +## [3.5.0](https://github.com/stevearc/conform.nvim/compare/v3.4.1...v3.5.0) (2023-09-22) + + +### Features + +* add `bibtex-tidy` ([#69](https://github.com/stevearc/conform.nvim/issues/69)) ([f5e7f84](https://github.com/stevearc/conform.nvim/commit/f5e7f84fb27f05d9a3f3893634cbb6c7f7f89056)) +* add dprint ([#71](https://github.com/stevearc/conform.nvim/issues/71)) ([0e2c97a](https://github.com/stevearc/conform.nvim/commit/0e2c97ab640f14f7da92278c731879efcb11f563)) +* add mdformat ([#68](https://github.com/stevearc/conform.nvim/issues/68)) ([4a4c927](https://github.com/stevearc/conform.nvim/commit/4a4c92715b174b847ba0fcdccf9dfea71c8ed33e)) +* add ruff formatter and improve ruff root finding ([#66](https://github.com/stevearc/conform.nvim/issues/66)) ([44e9e82](https://github.com/stevearc/conform.nvim/commit/44e9e8292d552f9a35498612a93dff934cc8802f)) + + +### Bug Fixes + +* `stylelint` and `markdownlint` when there are non-autofixable errors ([#70](https://github.com/stevearc/conform.nvim/issues/70)) ([5454fb5](https://github.com/stevearc/conform.nvim/commit/5454fb5a72a957b550fb7a0f5c4e84684c529920)) + +## [3.4.1](https://github.com/stevearc/conform.nvim/compare/v3.4.0...v3.4.1) (2023-09-19) + + +### Bug Fixes + +* range formatting for LSP formatters ([#63](https://github.com/stevearc/conform.nvim/issues/63)) ([52280f0](https://github.com/stevearc/conform.nvim/commit/52280f032653e98dd6ecbb61488afcca39671964)) + +## [3.4.0](https://github.com/stevearc/conform.nvim/compare/v3.3.0...v3.4.0) (2023-09-18) + + +### Features + +* add `squeeze_blanks` ([#62](https://github.com/stevearc/conform.nvim/issues/62)) ([3fa2a7b](https://github.com/stevearc/conform.nvim/commit/3fa2a7be8d91c3f0d7b79dde70d7849518cdc5bf)) +* make lsp_fallback behavior more intuitive ([#59](https://github.com/stevearc/conform.nvim/issues/59)) ([1abbb82](https://github.com/stevearc/conform.nvim/commit/1abbb82bb8e519e652d8b31b12a311872e9090d1)) + +## [3.3.0](https://github.com/stevearc/conform.nvim/compare/v3.2.0...v3.3.0) (2023-09-17) + + +### Features + +* '_' filetype to define fallback formatters ([a589750](https://github.com/stevearc/conform.nvim/commit/a589750635fcc5bb52c7e572cd853446c2c63855)) +* add GNU/BSD indent ([#54](https://github.com/stevearc/conform.nvim/issues/54)) ([5abf6c2](https://github.com/stevearc/conform.nvim/commit/5abf6c2c89ff6ed7d17285ec1da759013463bfc7)) +* Add rustywind formatter ([#56](https://github.com/stevearc/conform.nvim/issues/56)) ([a839ed1](https://github.com/stevearc/conform.nvim/commit/a839ed1384c21cbd8861f2850b552a4db10ead2f)) +* add shellcheck ([#44](https://github.com/stevearc/conform.nvim/issues/44)) ([508ec8a](https://github.com/stevearc/conform.nvim/commit/508ec8a899e039a56f9110011125ab56284db1fa)) +* alejandra formatter ([#52](https://github.com/stevearc/conform.nvim/issues/52)) ([e6552b5](https://github.com/stevearc/conform.nvim/commit/e6552b5c9b3a2b12bacb476b00c80c736b9f7963)) +* allow running commands in a shell ([#49](https://github.com/stevearc/conform.nvim/issues/49)) ([fbb18a5](https://github.com/stevearc/conform.nvim/commit/fbb18a5b92e2f11aaaef379d74d4a1132a138cb3)) +* format_on_save functions can return a callback as the second value ([1a568c6](https://github.com/stevearc/conform.nvim/commit/1a568c66f16650290fffcfbf5aefebe2d8254b83)) +* provide a formatexpr ([#55](https://github.com/stevearc/conform.nvim/issues/55)) ([aa38b05](https://github.com/stevearc/conform.nvim/commit/aa38b05575dab57b813ddcd14780f65ff20a6d49)) +* utility function to extend the built-in formatter args ([#50](https://github.com/stevearc/conform.nvim/issues/50)) ([cb5f939](https://github.com/stevearc/conform.nvim/commit/cb5f939ab27b2c2ef2e1d4ac6fe16c5ba6332f39)) + + +### Bug Fixes + +* `q` keymap in ConformInfo and `codespell` exit codes ([#53](https://github.com/stevearc/conform.nvim/issues/53)) ([d3fe431](https://github.com/stevearc/conform.nvim/commit/d3fe43167c7d96036c8c037ef1b4e03b448efbe7)) +* ConformInfo shows available LSP formatters ([3aa2fd5](https://github.com/stevearc/conform.nvim/commit/3aa2fd5f828f8fcabd65605a41953aba1f0f5cb0)) +* LSP formatter respects quiet = true ([5e4d258](https://github.com/stevearc/conform.nvim/commit/5e4d258f8eba4090b9a515ee9b77d8647394b2cd)) +* unify timeout error message format with LSP ([0d963f8](https://github.com/stevearc/conform.nvim/commit/0d963f82add9ca4faf49b54fc28f57038742ded3)) +* use non-deprecated health report functions if available ([#48](https://github.com/stevearc/conform.nvim/issues/48)) ([b436902](https://github.com/stevearc/conform.nvim/commit/b43690264ebcb152365d5b46faa6561f12ea062a)) + +## [3.2.0](https://github.com/stevearc/conform.nvim/compare/v3.1.0...v3.2.0) (2023-09-14) + + +### Features + +* add `markdownlint`, `stylelint`, `codespell`, and `biome` ([#45](https://github.com/stevearc/conform.nvim/issues/45)) ([580ab18](https://github.com/stevearc/conform.nvim/commit/580ab1880e740f4aebbc72a05350461f3cdef53d)) +* add buf as protobuf linter ([#43](https://github.com/stevearc/conform.nvim/issues/43)) ([2b73887](https://github.com/stevearc/conform.nvim/commit/2b73887fd75e1f6efc352cec6bd7e39157c3732e)) +* add deno fmt ([#46](https://github.com/stevearc/conform.nvim/issues/46)) ([db7461a](https://github.com/stevearc/conform.nvim/commit/db7461afcf751023adeb346d833f2e5d40a420c4)) +* add djlint ([#47](https://github.com/stevearc/conform.nvim/issues/47)) ([ead0257](https://github.com/stevearc/conform.nvim/commit/ead025784c8e31b8e45016e620c2f17a13ff741a)) +* latexindent ([#42](https://github.com/stevearc/conform.nvim/issues/42)) ([502a358](https://github.com/stevearc/conform.nvim/commit/502a3583663ede11c8db1e9980db342b117d79f2)) +* ruff ([#41](https://github.com/stevearc/conform.nvim/issues/41)) ([fdc4a0f](https://github.com/stevearc/conform.nvim/commit/fdc4a0f05c21012f2445a993ebdad700380dcfbf)) + + +### Bug Fixes + +* extra trailing newline for LSP formatters that replace entire file ([e18cdaf](https://github.com/stevearc/conform.nvim/commit/e18cdaf529b94465592d0c2afe1b62bc26155070)) + +## [3.1.0](https://github.com/stevearc/conform.nvim/compare/v3.0.0...v3.1.0) (2023-09-13) + + +### Features + +* format_on_save and format_after_save can be functions ([dd5b2f2](https://github.com/stevearc/conform.nvim/commit/dd5b2f2f7ca01c2f28239cbbc7f97e6f9024cd94)) + + +### Bug Fixes + +* modify diff calculation to handle end-of-file newlines better ([#35](https://github.com/stevearc/conform.nvim/issues/35)) ([00a5288](https://github.com/stevearc/conform.nvim/commit/00a528818463b10d84699b2e0f4a960d5a4aeb5c)) + +## [3.0.0](https://github.com/stevearc/conform.nvim/compare/v2.3.0...v3.0.0) (2023-09-08) + + +### ⚠ BREAKING CHANGES + +* remove run_all_formatters config option + +### Features + +* add beautysh, taplo, trim_newlines and trim_whitespace ([#29](https://github.com/stevearc/conform.nvim/issues/29)) ([37a2d65](https://github.com/stevearc/conform.nvim/commit/37a2d65bd2ee41540cc426d2cffef6d6f8648357)) +* format() can always fall back to LSP formatting ([c3028b3](https://github.com/stevearc/conform.nvim/commit/c3028b327bc44335cc2b5c3014cd6d5c12a54ee4)) +* syntax for using first available formatter ([2568d74](https://github.com/stevearc/conform.nvim/commit/2568d746abbadf66a03c62b568ee73d874cd8617)) + + +### Code Refactoring + +* remove run_all_formatters config option ([bd1aa02](https://github.com/stevearc/conform.nvim/commit/bd1aa02ef191410b2ea0b3ef5caabe06592d9c51)) + +## [2.3.0](https://github.com/stevearc/conform.nvim/compare/v2.2.0...v2.3.0) (2023-09-06) + + +### Features + +* format() takes an optional callback ([#21](https://github.com/stevearc/conform.nvim/issues/21)) ([3f34f2d](https://github.com/stevearc/conform.nvim/commit/3f34f2de48e393b2ee289f2c8fa613c7eabae6d8)) + + +### Bug Fixes + +* callback should always be called ([eb3ebb6](https://github.com/stevearc/conform.nvim/commit/eb3ebb6d2d114f6476a8f8d21d74f99c6d231a53)) + +## [2.2.0](https://github.com/stevearc/conform.nvim/compare/v2.1.0...v2.2.0) (2023-08-31) + + +### Features + +* apply changes as text edits using LSP utils ([#18](https://github.com/stevearc/conform.nvim/issues/18)) ([92393f0](https://github.com/stevearc/conform.nvim/commit/92393f02efadfb1d9f97c74c8feb853c1caea9de)) + +## [2.1.0](https://github.com/stevearc/conform.nvim/compare/v2.0.0...v2.1.0) (2023-08-30) + + +### Features + +* add golines ([#11](https://github.com/stevearc/conform.nvim/issues/11)) ([e1d68a5](https://github.com/stevearc/conform.nvim/commit/e1d68a58fa29d2a24f1a976c3c60521ffb31f32e)) +* add perlimports ([#13](https://github.com/stevearc/conform.nvim/issues/13)) ([e6e99af](https://github.com/stevearc/conform.nvim/commit/e6e99af64db3f364086aaf55b8b5854ccd62bac4)) +* add perltidy ([#12](https://github.com/stevearc/conform.nvim/issues/12)) ([882b759](https://github.com/stevearc/conform.nvim/commit/882b75994af34fed3c4fe6f1a97ad58b352ec25f)) +* add shellharden ([#14](https://github.com/stevearc/conform.nvim/issues/14)) ([863fb46](https://github.com/stevearc/conform.nvim/commit/863fb46fc7a7fa66fafb4bb8fd8093c700c472e5)) +* add support for environment variables ([#8](https://github.com/stevearc/conform.nvim/issues/8)) ([03a37f1](https://github.com/stevearc/conform.nvim/commit/03a37f1b53d83af7aee10fc3ffee9f3a05d09e2e)) +* display last few lines of the log file in :ConformInfo ([c9327f2](https://github.com/stevearc/conform.nvim/commit/c9327f2af541e4a17a6e2e05682122f8c8455d29)) +* formatter config function is passed the buffer number ([#9](https://github.com/stevearc/conform.nvim/issues/9)) ([8b2a574](https://github.com/stevearc/conform.nvim/commit/8b2a5741e07e2d6d5e8103e5e12356d3a9f0b8ba)) +* notify when formatter errors, and add notify_on_error config option ([#16](https://github.com/stevearc/conform.nvim/issues/16)) ([08dc913](https://github.com/stevearc/conform.nvim/commit/08dc913fb22d402a98d1d9733536f2876c6f6314)) + + +### Bug Fixes + +* shellharden ([#15](https://github.com/stevearc/conform.nvim/issues/15)) ([288068b](https://github.com/stevearc/conform.nvim/commit/288068b1b78c79e64054ef443afbf6f2f5145da4)) + +## [2.0.0](https://github.com/stevearc/conform.nvim/compare/v1.1.0...v2.0.0) (2023-08-29) + + +### ⚠ BREAKING CHANGES + +* remove ability for formatter list to disable autoformat + +### Features + +* can silence notification when running formatters ([#7](https://github.com/stevearc/conform.nvim/issues/7)) ([a4d793e](https://github.com/stevearc/conform.nvim/commit/a4d793e941e8e497ab9149ed09c946473d795c1b)) +* ConformInfo command for debugging formatter status ([1fd547f](https://github.com/stevearc/conform.nvim/commit/1fd547fe98a5100a041106e2bc353363ab0d5ad8)) +* range formatting ([cddd536](https://github.com/stevearc/conform.nvim/commit/cddd536e087a9fd3d2c9ea5b0a44e46c7b4b54c2)) + + +### Bug Fixes + +* don't show 'no formatters' warning if none configured ([9376d37](https://github.com/stevearc/conform.nvim/commit/9376d37bd7ab456b7df8e3d6f1ba75c05b4e5a8f)) +* keep window position stable when LSP formatting ([90e8a8d](https://github.com/stevearc/conform.nvim/commit/90e8a8d63c7d77d1872dca3da720abfa07271054)) +* remove unnecessary notify ([6082883](https://github.com/stevearc/conform.nvim/commit/6082883585a5c61c7a5c6697517931bc6e39f546)) +* stable ordering when specifying multiple formatters ([69c4495](https://github.com/stevearc/conform.nvim/commit/69c4495ab5ad3c07c3a4f3c2bcac2f070718b4cb)) + + +### Code Refactoring + +* remove ability for formatter list to disable autoformat ([d508ae8](https://github.com/stevearc/conform.nvim/commit/d508ae8f46b5b41e2806b412311719a941167c1a)) + +## [1.1.0](https://github.com/stevearc/conform.nvim/compare/v1.0.0...v1.1.0) (2023-08-28) + + +### Features + +* new formatter: fish_indent ([#5](https://github.com/stevearc/conform.nvim/issues/5)) ([446aa57](https://github.com/stevearc/conform.nvim/commit/446aa570048586f9c13f1ea88e280567f336691e)) + + +### Bug Fixes + +* gracefully handle another timeout case ([500d24d](https://github.com/stevearc/conform.nvim/commit/500d24dc1a2447a3c8f3f4f756f40bd27ff0b283)) +* no need to save/restore window view ([5bc69d5](https://github.com/stevearc/conform.nvim/commit/5bc69d500a14fb06bf8f36005f76a7825be25931)) + +## 1.0.0 (2023-08-25) + + +### Features + +* first working version ([eb5987e](https://github.com/stevearc/conform.nvim/commit/eb5987e9dd40ce1e27c9c07e41d09571f1bd876e)) + + +### Bug Fixes + +* don't modify files when no styling changes ([08b54ba](https://github.com/stevearc/conform.nvim/commit/08b54ba11e29e6df9f83c02539976331617a412c)) +* ensure real buffer numbers get logged ([33ee8ba](https://github.com/stevearc/conform.nvim/commit/33ee8ba8cb6f29caec1edf01fa4987bbae52f18b)) +* notification when no formatters available ([a757225](https://github.com/stevearc/conform.nvim/commit/a75722517d17d749a5ee86c8a3bbb098a61265fc)) +* set a cwd for stylua ([a22781e](https://github.com/stevearc/conform.nvim/commit/a22781e0c3b609a5f90095f388589744567476c7)) diff --git a/config/neovim/store/lazy-plugins/conform.nvim/LICENSE b/config/neovim/store/lazy-plugins/conform.nvim/LICENSE new file mode 100644 index 00000000..d6cdf38f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Steven Arcangeli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/conform.nvim/Makefile b/config/neovim/store/lazy-plugins/conform.nvim/Makefile new file mode 100644 index 00000000..71447f8f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/Makefile @@ -0,0 +1,27 @@ +.PHONY: all doc test lint fastlint clean + +all: doc lint test + +doc: scripts/nvim_doc_tools + python scripts/main.py generate + python scripts/main.py lint + +test: + ./run_tests.sh + +lint: scripts/nvim-typecheck-action fastlint + ./scripts/nvim-typecheck-action/typecheck.sh --workdir scripts/nvim-typecheck-action lua + +fastlint: scripts/nvim_doc_tools + python scripts/main.py lint + luacheck lua tests --formatter plain + stylua --check lua tests + +scripts/nvim_doc_tools: + git clone https://github.com/stevearc/nvim_doc_tools scripts/nvim_doc_tools + +scripts/nvim-typecheck-action: + git clone https://github.com/stevearc/nvim-typecheck-action scripts/nvim-typecheck-action + +clean: + rm -rf scripts/nvim_doc_tools scripts/nvim-typecheck-action diff --git a/config/neovim/store/lazy-plugins/conform.nvim/README.md b/config/neovim/store/lazy-plugins/conform.nvim/README.md new file mode 100644 index 00000000..5b3f2cf6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/README.md @@ -0,0 +1,620 @@ +# conform.nvim + +Lightweight yet powerful formatter plugin for Neovim + + + +- [Requirements](#requirements) +- [Features](#features) +- [Installation](#installation) +- [Setup](#setup) +- [Formatters](#formatters) +- [Customizing formatters](#customizing-formatters) +- [Recipes](#recipes) +- [Advanced topics](#advanced-topics) +- [Options](#options) +- [Formatter options](#formatter-options) +- [API](#api) + - [setup(opts)](#setupopts) + - [format(opts, callback)](#formatopts-callback) + - [list_formatters(bufnr)](#list_formattersbufnr) + - [list_all_formatters()](#list_all_formatters) + - [get_formatter_info(formatter, bufnr)](#get_formatter_infoformatter-bufnr) + - [will_fallback_lsp(options)](#will_fallback_lspoptions) +- [Acknowledgements](#acknowledgements) + + + +## Requirements + +- Neovim 0.8+ + +## Features + +- **Preserves extmarks and folds** - Most formatters replace the entire buffer, which clobbers extmarks and folds, and can cause the viewport and cursor to jump unexpectedly. Conform calculates minimal diffs and applies them using the built-in LSP format utilities. +- **Fixes bad-behaving LSP formatters** - Some LSP servers are lazy and simply replace the entire buffer, leading to the problems mentioned above. Conform hooks into the LSP handler and turns these responses into proper piecewise changes. +- **Enables range formatting for all formatters** - Since conform calculates minimal diffs, it can perform range formatting [even if the underlying formatter doesn't support it.](doc/advanced_topics.md#range-formatting) +- **Simple API** - Conform exposes a simple, imperative API modeled after `vim.lsp.buf.format()`. +- **Formats embedded code blocks** - Can format code blocks inside markdown files or similar (see [injected language formatting](doc/advanced_topics.md#injected-language-formatting-code-blocks)) + +## Installation + +conform.nvim supports all the usual plugin managers + +
+ lazy.nvim + +```lua +{ + 'stevearc/conform.nvim', + opts = {}, +} +``` + +For a more thorough configuration involving lazy-loading, see [Lazy loading with lazy.nvim](doc/recipes.md#lazy-loading-with-lazynvim). + +
+ +
+ Packer + +```lua +require("packer").startup(function() + use({ + "stevearc/conform.nvim", + config = function() + require("conform").setup() + end, + }) +end) +``` + +
+ +
+ Paq + +```lua +require("paq")({ + { "stevearc/conform.nvim" }, +}) +``` + +
+ +
+ vim-plug + +```vim +Plug 'stevearc/conform.nvim' +``` + +
+ +
+ dein + +```vim +call dein#add('stevearc/conform.nvim') +``` + +
+ +
+ Pathogen + +```sh +git clone --depth=1 https://github.com/stevearc/conform.nvim.git ~/.vim/bundle/ +``` + +
+ +
+ Neovim native package + +```sh +git clone --depth=1 https://github.com/stevearc/conform.nvim.git \ + "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/pack/conform/start/conform.nvim +``` + +
+ +## Setup + +At a minimum, you will need to set up some formatters by filetype + +```lua +require("conform").setup({ + formatters_by_ft = { + lua = { "stylua" }, + -- Conform will run multiple formatters sequentially + python = { "isort", "black" }, + -- Use a sub-list to run only the first available formatter + javascript = { { "prettierd", "prettier" } }, + }, +}) +``` + +Then you can use `conform.format()` just like you would `vim.lsp.buf.format()`. For example, to format on save: + +```lua +vim.api.nvim_create_autocmd("BufWritePre", { + pattern = "*", + callback = function(args) + require("conform").format({ bufnr = args.buf }) + end, +}) +``` + +As a shortcut, conform will optionally set up this format-on-save autocmd for you + +```lua +require("conform").setup({ + format_on_save = { + -- These options will be passed to conform.format() + timeout_ms = 500, + lsp_fallback = true, + }, +}) +``` + +See [conform.format()](#formatopts-callback) for more details about the parameters. + +Conform also provides a formatexpr, same as the LSP client: + +```lua +vim.o.formatexpr = "v:lua.require'conform'.formatexpr()" +``` + +To view configured and available formatters, as well as to see the log file, run `:ConformInfo` + +## Formatters + +You can view this list in vim with `:help conform-formatters` + +
+ Expand to see all formatters + + + +- [alejandra](https://kamadorueda.com/alejandra/) - The Uncompromising Nix Code Formatter. +- [asmfmt](https://github.com/klauspost/asmfmt) - Go Assembler Formatter +- [ast-grep](https://ast-grep.github.io/) - A CLI tool for code structural search, lint and rewriting. Written in Rust. +- [astyle](https://astyle.sourceforge.net/astyle.html) - A Free, Fast, and Small Automatic Formatter for C, C++, C++/CLI, Objective-C, C#, and Java Source Code. +- [auto_optional](https://auto-optional.daanluttik.nl/) - Adds the Optional type-hint to arguments where the default value is None. +- [autocorrect](https://github.com/huacnlee/autocorrect) - A linter and formatter to help you to improve copywriting, correct spaces, words, and punctuations between CJK. +- [autoflake](https://github.com/PyCQA/autoflake) - Removes unused imports and unused variables as reported by pyflakes. +- [autopep8](https://github.com/hhatto/autopep8) - A tool that automatically formats Python code to conform to the PEP 8 style guide. +- [awk](https://www.gnu.org/software/gawk/manual/gawk.html) - Format awk programs with awk +- [bean-format](https://beancount.github.io/docs/running_beancount_and_generating_reports.html#bean-format) - Reformat Beancount files to right-align all the numbers at the same, minimal column. +- [beautysh](https://github.com/lovesegfault/beautysh) - A Bash beautifier for the masses. +- [bibtex-tidy](https://github.com/FlamingTempura/bibtex-tidy) - Cleaner and Formatter for BibTeX files. +- [bicep](https://github.com/Azure/bicep) - Bicep is a Domain Specific Language (DSL) for deploying Azure resources declaratively. +- [biome](https://github.com/biomejs/biome) - A toolchain for web projects, aimed to provide functionalities to maintain them. +- [biome-check](https://github.com/biomejs/biome) - A toolchain for web projects, aimed to provide functionalities to maintain them. +- [black](https://github.com/psf/black) - The uncompromising Python code formatter. +- [blade-formatter](https://github.com/shufo/blade-formatter) - An opinionated blade template formatter for Laravel that respects readability. +- [blue](https://github.com/grantjenks/blue) - The slightly less uncompromising Python code formatter. +- [bpfmt](https://source.android.com/docs/setup/reference/androidbp) - Android Blueprint file formatter. +- [buf](https://buf.build/docs/reference/cli/buf/format) - A new way of working with Protocol Buffers. +- [buildifier](https://github.com/bazelbuild/buildtools/tree/master/buildifier) - buildifier is a tool for formatting bazel BUILD and .bzl files with a standard convention. +- [cabal_fmt](https://hackage.haskell.org/package/cabal-fmt) - Format cabal files with cabal-fmt +- [cbfmt](https://github.com/lukas-reineke/cbfmt) - A tool to format codeblocks inside markdown and org documents. +- [clang-format](https://www.kernel.org/doc/html/latest/process/clang-format.html) - Tool to format C/C++/… code according to a set of rules and heuristics. +- [cljstyle](https://github.com/greglook/cljstyle) - Formatter for Clojure code. +- [cmake_format](https://github.com/cheshirekow/cmake_format) - Parse cmake listfiles and format them nicely. +- [codespell](https://github.com/codespell-project/codespell) - Check code for common misspellings. +- [crystal](https://crystal-lang.org/) - Format Crystal code. +- [csharpier](https://github.com/belav/csharpier) - The opinionated C# code formatter. +- [cue_fmt](https://cuelang.org) - Format CUE files using `cue fmt` command. +- [darker](https://github.com/akaihola/darker) - Run black only on changed lines. +- [dart_format](https://dart.dev/tools/dart-format) - Replace the whitespace in your program with formatting that follows Dart guidelines. +- [deno_fmt](https://deno.land/manual/tools/formatter) - Use [Deno](https://deno.land/) to format TypeScript, JavaScript/JSON and markdown. +- [dfmt](https://github.com/dlang-community/dfmt) - Formatter for D source code. +- [djlint](https://github.com/Riverside-Healthcare/djLint) - ✨ HTML Template Linter and Formatter. Django - Jinja - Nunjucks - Handlebars - GoLang. +- [dprint](https://github.com/dprint/dprint) - Pluggable and configurable code formatting platform written in Rust. +- [easy-coding-standard](https://github.com/easy-coding-standard/easy-coding-standard) - ecs - Use Coding Standard with 0-knowledge of PHP-CS-Fixer and PHP_CodeSniffer. +- [elm_format](https://github.com/avh4/elm-format) - elm-format formats Elm source code according to a standard set of rules based on the official [Elm Style Guide](https://elm-lang.org/docs/style-guide). +- [erb_format](https://github.com/nebulab/erb-formatter) - Format ERB files with speed and precision. +- [eslint_d](https://github.com/mantoni/eslint_d.js/) - Like ESLint, but faster. +- [fantomas](https://github.com/fsprojects/fantomas) - F# source code formatter. +- [fish_indent](https://fishshell.com/docs/current/cmds/fish_indent.html) - Indent or otherwise prettify a piece of fish code. +- [fixjson](https://github.com/rhysd/fixjson) - JSON Fixer for Humans using (relaxed) JSON5. +- [fnlfmt](https://git.sr.ht/~technomancy/fnlfmt) - A formatter for Fennel code. +- [forge_fmt](https://github.com/foundry-rs/foundry) - Forge is a command-line tool that ships with Foundry. Forge tests, builds, and deploys your smart contracts. +- [fourmolu](https://hackage.haskell.org/package/fourmolu) - A fork of ormolu that uses four space indentation and allows arbitrary configuration. +- [gci](https://github.com/daixiang0/gci) - GCI, a tool that controls Go package import order and makes it always deterministic. +- [gdformat](https://github.com/Scony/godot-gdscript-toolkit) - A formatter for Godot's gdscript. +- [gersemi](https://github.com/BlankSpruce/gersemi) - A formatter to make your CMake code the real treasure. +- [gleam](https://github.com/gleam-lang/gleam) - ⭐️ A friendly language for building type-safe, scalable systems! +- [gn](https://gn.googlesource.com/gn/) - gn build system. +- [gofmt](https://pkg.go.dev/cmd/gofmt) - Formats go programs. +- [gofumpt](https://github.com/mvdan/gofumpt) - Enforce a stricter format than gofmt, while being backwards compatible. That is, gofumpt is happy with a subset of the formats that gofmt is happy with. +- [goimports](https://pkg.go.dev/golang.org/x/tools/cmd/goimports) - Updates your Go import lines, adding missing ones and removing unreferenced ones. +- [goimports-reviser](https://github.com/incu6us/goimports-reviser) - Right imports sorting & code formatting tool (goimports alternative). +- [golines](https://github.com/segmentio/golines) - A golang formatter that fixes long lines. +- [google-java-format](https://github.com/google/google-java-format) - Reformats Java source code according to Google Java Style. +- [hcl](https://github.com/hashicorp/hcl) - A formatter for HCL files. +- [htmlbeautifier](https://github.com/threedaymonk/htmlbeautifier) - A normaliser/beautifier for HTML that also understands embedded Ruby. Ideal for tidying up Rails templates. +- [indent](https://www.gnu.org/software/indent/) - GNU Indent. +- [injected](doc/advanced_topics.md#injected-language-formatting-code-blocks) - Format treesitter injected languages. +- [inko](https://inko-lang.org/) - A language for building concurrent software with confidence +- [isort](https://github.com/PyCQA/isort) - Python utility / library to sort imports alphabetically and automatically separate them into sections and by type. +- [joker](https://github.com/candid82/joker) - Small Clojure interpreter, linter and formatter. +- [jq](https://github.com/stedolan/jq) - Command-line JSON processor. +- [jsonnetfmt](https://github.com/google/go-jsonnet/tree/master/cmd/jsonnetfmt) - jsonnetfmt is a command line tool to format jsonnet files. +- [just](https://github.com/casey/just) - Format Justfile. +- [ktfmt](https://github.com/facebook/ktfmt) - Reformats Kotlin source code to comply with the common community standard conventions. +- [ktlint](https://ktlint.github.io/) - An anti-bikeshedding Kotlin linter with built-in formatter. +- [latexindent](https://github.com/cmhughes/latexindent.pl) - A perl script for formatting LaTeX files that is generally included in major TeX distributions. +- [leptosfmt](https://github.com/bram209/leptosfmt) - A formatter for the Leptos view! macro. +- [liquidsoap-prettier](https://github.com/savonet/liquidsoap-prettier) - A binary to format Liquidsoap scripts +- [markdown-toc](https://github.com/jonschlinkert/markdown-toc) - API and CLI for generating a markdown TOC (table of contents) for a README or any markdown files. +- [markdownlint](https://github.com/DavidAnson/markdownlint) - A Node.js style checker and lint tool for Markdown/CommonMark files. +- [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) - A fast, flexible, configuration-based command-line interface for linting Markdown/CommonMark files with the markdownlint library. +- [mdformat](https://github.com/executablebooks/mdformat) - An opinionated Markdown formatter. +- [mdsf](https://github.com/hougesen/mdsf) - Format markdown code blocks using your favorite code formatters. +- [mdslw](https://github.com/razziel89/mdslw) - Prepare your markdown for easy diff'ing by adding line breaks after every sentence. +- [mix](https://hexdocs.pm/mix/main/Mix.Tasks.Format.html) - Format Elixir files using the mix format command. +- [nimpretty](https://github.com/nim-lang/nim) - nimpretty is a Nim source code beautifier that follows the official style guide. +- [nixfmt](https://github.com/serokell/nixfmt) - nixfmt is a formatter for Nix code, intended to apply a uniform style. +- [nixpkgs_fmt](https://github.com/nix-community/nixpkgs-fmt) - nixpkgs-fmt is a Nix code formatter for nixpkgs. +- [ocamlformat](https://github.com/ocaml-ppx/ocamlformat) - Auto-formatter for OCaml code. +- [ocp-indent](https://github.com/OCamlPro/ocp-indent) - Automatic indentation of OCaml source files. +- [opa_fmt](https://www.openpolicyagent.org/docs/latest/cli/#opa-fmt) - Format Rego files using `opa fmt` command. +- [ormolu](https://hackage.haskell.org/package/ormolu) - A formatter for Haskell source code. +- [packer_fmt](https://developer.hashicorp.com/packer/docs/commands/fmt) - The packer fmt Packer command is used to format HCL2 configuration files to a canonical format and style. +- [pangu](https://github.com/vinta/pangu.py) - Insert whitespace between CJK and half-width characters. +- [perlimports](https://github.com/perl-ide/App-perlimports) - Make implicit Perl imports explicit. +- [perltidy](https://github.com/perltidy/perltidy) - Perl::Tidy, a source code formatter for Perl. +- [pg_format](https://github.com/darold/pgFormatter) - PostgreSQL SQL syntax beautifier. +- [php_cs_fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) - The PHP Coding Standards Fixer. +- [phpcbf](https://phpqa.io/projects/phpcbf.html) - PHP Code Beautifier and Fixer fixes violations of a defined coding standard. +- [phpinsights](https://github.com/nunomaduro/phpinsights) - The perfect starting point to analyze the code quality of your PHP projects. +- [pint](https://github.com/laravel/pint) - Laravel Pint is an opinionated PHP code style fixer for minimalists. +- [prettier](https://github.com/prettier/prettier) - Prettier is an opinionated code formatter. It enforces a consistent style by parsing your code and re-printing it with its own rules that take the maximum line length into account, wrapping code when necessary. +- [prettierd](https://github.com/fsouza/prettierd) - prettier, as a daemon, for ludicrous formatting speed. +- [pretty-php](https://github.com/lkrms/pretty-php) - The opinionated PHP code formatter. +- [puppet-lint](https://github.com/puppetlabs/puppet-lint) - Check that your Puppet manifests conform to the style guide. +- [purs-tidy](https://github.com/natefaubion/purescript-tidy) - A syntax tidy-upper for PureScript. +- [reorder-python-imports](https://github.com/asottile/reorder-python-imports) - Rewrites source to reorder python imports +- [rescript-format](https://rescript-lang.org/) - The built-in ReScript formatter. +- [roc](https://github.com/roc-lang/roc) - A fast, friendly, functional language. +- [rubocop](https://github.com/rubocop/rubocop) - Ruby static code analyzer and formatter, based on the community Ruby style guide. +- [rubyfmt](https://github.com/fables-tales/rubyfmt) - Ruby Autoformatter! (Written in Rust) +- [ruff_fix](https://docs.astral.sh/ruff/) - An extremely fast Python linter, written in Rust. Fix lint errors. +- [ruff_format](https://docs.astral.sh/ruff/) - An extremely fast Python linter, written in Rust. Formatter subcommand. +- [ruff_organize_imports](https://docs.astral.sh/ruff/) - An extremely fast Python linter, written in Rust. Organize imports. +- [rufo](https://github.com/ruby-formatter/rufo) - Rufo is an opinionated ruby formatter. +- [rustfmt](https://github.com/rust-lang/rustfmt) - A tool for formatting rust code according to style guidelines. +- [rustywind](https://github.com/avencera/rustywind) - A tool for formatting Tailwind CSS classes. +- [scalafmt](https://github.com/scalameta/scalafmt) - Code formatter for Scala. +- [shellcheck](https://github.com/koalaman/shellcheck) - A static analysis tool for shell scripts. +- [shellharden](https://github.com/anordal/shellharden) - The corrective bash syntax highlighter. +- [shfmt](https://github.com/mvdan/sh) - A shell parser, formatter, and interpreter with `bash` support. +- [smlfmt](https://github.com/shwestrick/smlfmt) - A custom parser and code formatter for Standard ML. +- [snakefmt](https://github.com/snakemake/snakefmt) - a formatting tool for Snakemake files following the design of Black. +- [sql_formatter](https://github.com/sql-formatter-org/sql-formatter) - A whitespace formatter for different query languages. +- [sqlfluff](https://github.com/sqlfluff/sqlfluff) - A modular SQL linter and auto-formatter with support for multiple dialects and templated code. +- [sqlfmt](https://docs.sqlfmt.com) - sqlfmt formats your dbt SQL files so you don't have to. It is similar in nature to Black, gofmt, and rustfmt (but for SQL) +- [squeeze_blanks](https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation) - Squeeze repeated blank lines into a single blank line via `cat -s`. +- [standardjs](https://standardjs.com) - JavaScript Standard style guide, linter, and formatter. +- [standardrb](https://github.com/standardrb/standard) - Ruby's bikeshed-proof linter and formatter. +- [stylelint](https://github.com/stylelint/stylelint) - A mighty CSS linter that helps you avoid errors and enforce conventions. +- [styler](https://github.com/devOpifex/r.nvim) - R formatter and linter. +- [stylua](https://github.com/JohnnyMorganz/StyLua) - An opinionated code formatter for Lua. +- [swift_format](https://github.com/apple/swift-format) - Swift formatter from apple. Requires building from source with `swift build`. +- [swiftformat](https://github.com/nicklockwood/SwiftFormat) - SwiftFormat is a code library and command-line tool for reformatting `swift` code on macOS or Linux. +- [taplo](https://github.com/tamasfe/taplo) - A TOML toolkit written in Rust. +- [templ](https://templ.guide/commands-and-tools/cli/#formatting-templ-files) - Formats templ template files. +- [terraform_fmt](https://www.terraform.io/docs/cli/commands/fmt.html) - The terraform-fmt command rewrites `terraform` configuration files to a canonical format and style. +- [terragrunt_hclfmt](https://terragrunt.gruntwork.io/docs/reference/cli-options/#hclfmt) - Format hcl files into a canonical format. +- [tlint](https://github.com/tighten/tlint) - Tighten linter for Laravel conventions with support for auto-formatting. +- [tofu_fmt](https://opentofu.org/docs/cli/commands/fmt/) - The tofu-fmt command rewrites OpenTofu configuration files to a canonical format and style. +- [trim_newlines](https://www.gnu.org/software/gawk/manual/gawk.html) - Trim new lines with awk. +- [trim_whitespace](https://www.gnu.org/software/gawk/manual/gawk.html) - Trim whitespaces with awk. +- [twig-cs-fixer](https://github.com/VincentLanglet/Twig-CS-Fixer) - Automatically fix Twig Coding Standards issues +- [typos](https://github.com/crate-ci/typos) - Source code spell checker +- [typstfmt](https://github.com/astrale-sharp/typstfmt) - Basic formatter for the Typst language with a future! +- [typstyle](https://github.com/Enter-tainer/typstyle) - Beautiful and reliable typst code formatter. +- [uncrustify](https://github.com/uncrustify/uncrustify) - A source code beautifier for C, C++, C#, ObjectiveC, D, Java, Pawn and Vala. +- [usort](https://github.com/facebook/usort) - Safe, minimal import sorting for Python projects. +- [verible](https://github.com/chipsalliance/verible/blob/master/verilog/tools/formatter/README.md) - The SystemVerilog formatter. +- [xmlformat](https://github.com/pamoller/xmlformatter) - xmlformatter is an Open Source Python package, which provides formatting of XML documents. +- [xmllint](http://xmlsoft.org/xmllint.html) - Despite the name, xmllint can be used to format XML files as well as lint them. +- [yamlfix](https://github.com/lyz-code/yamlfix) - A configurable YAML formatter that keeps comments. +- [yamlfmt](https://github.com/google/yamlfmt) - yamlfmt is an extensible command line tool or library to format yaml files. +- [yapf](https://github.com/google/yapf) - Yet Another Python Formatter. +- [yew-fmt](https://github.com/schvv31n/yew-fmt) - Code formatter for the Yew framework. +- [yq](https://github.com/mikefarah/yq) - YAML/JSON processor +- [zigfmt](https://github.com/ziglang/zig) - Reformat Zig source into canonical form. +- [zprint](https://github.com/kkinnear/zprint) - Formatter for Clojure and EDN. + + +
+ +## Customizing formatters + +You can override/add to the default values of formatters + +```lua +require("conform").setup({ + formatters = { + yamlfix = { + -- Change where to find the command + command = "local/path/yamlfix", + -- Adds environment args to the yamlfix formatter + env = { + YAMLFIX_SEQUENCE_STYLE = "block_style", + }, + }, + }, +}) + +-- These can also be set directly +require("conform").formatters.yamlfix = { + env = { + YAMLFIX_SEQUENCE_STYLE = "block_style", + }, +} + +-- This can also be a function that returns the config, +-- which can be useful if you're doing lazy loading +require("conform").formatters.yamlfix = function(bufnr) + return { + command = require("conform.util").find_executable({ + "local/path/yamlfix", + }, "yamlfix"), + } +end +``` + +In addition to being able to override any of the original properties on the formatter, there is another property for easily adding additional arguments to the format command + +```lua +require("conform").formatters.shfmt = { + prepend_args = { "-i", "2" }, + -- The base args are { "-filename", "$FILENAME" } so the final args will be + -- { "-i", "2", "-filename", "$FILENAME" } +} +-- prepend_args can be a function, just like args +require("conform").formatters.shfmt = { + prepend_args = function(self, ctx) + return { "-i", "2" } + end, +} +``` + +If you want to overwrite the entire formatter definition and _not_ merge with the default values, pass `inherit = false`. This is also the default behavior if there is no built-in formatter with the given name, which can be used to add your own custom formatters. + +```lua +require("conform").formatters.shfmt = { + inherit = false, + command = "shfmt", + args = { "-i", "2", "-filename", "$FILENAME" }, +} +``` + +## Recipes + + + +- [Format command](doc/recipes.md#format-command) +- [Autoformat with extra features](doc/recipes.md#autoformat-with-extra-features) +- [Command to toggle format-on-save](doc/recipes.md#command-to-toggle-format-on-save) +- [Automatically run slow formatters async](doc/recipes.md#automatically-run-slow-formatters-async) +- [Lazy loading with lazy.nvim](doc/recipes.md#lazy-loading-with-lazynvim) + + + +## Advanced topics + + + +- [Minimal format diffs](doc/advanced_topics.md#minimal-format-diffs) +- [Range formatting](doc/advanced_topics.md#range-formatting) +- [Injected language formatting (code blocks)](doc/advanced_topics.md#injected-language-formatting-code-blocks) + + + +## Options + +A complete list of all configuration options + + + +```lua +require("conform").setup({ + -- Map of filetype to formatters + formatters_by_ft = { + lua = { "stylua" }, + -- Conform will run multiple formatters sequentially + go = { "goimports", "gofmt" }, + -- Use a sub-list to run only the first available formatter + javascript = { { "prettierd", "prettier" } }, + -- You can use a function here to determine the formatters dynamically + python = function(bufnr) + if require("conform").get_formatter_info("ruff_format", bufnr).available then + return { "ruff_format" } + else + return { "isort", "black" } + end + end, + -- Use the "*" filetype to run formatters on all filetypes. + ["*"] = { "codespell" }, + -- Use the "_" filetype to run formatters on filetypes that don't + -- have other formatters configured. + ["_"] = { "trim_whitespace" }, + }, + -- If this is set, Conform will run the formatter on save. + -- It will pass the table to conform.format(). + -- This can also be a function that returns the table. + format_on_save = { + -- I recommend these options. See :help conform.format for details. + lsp_fallback = true, + timeout_ms = 500, + }, + -- If this is set, Conform will run the formatter asynchronously after save. + -- It will pass the table to conform.format(). + -- This can also be a function that returns the table. + format_after_save = { + lsp_fallback = true, + }, + -- Set the log level. Use `:ConformInfo` to see the location of the log file. + log_level = vim.log.levels.ERROR, + -- Conform will notify you when a formatter errors + notify_on_error = true, + -- Custom formatters and overrides for built-in formatters + formatters = { + my_formatter = { + -- This can be a string or a function that returns a string. + -- When defining a new formatter, this is the only field that is required + command = "my_cmd", + -- A list of strings, or a function that returns a list of strings + -- Return a single string instead of a list to run the command in a shell + args = { "--stdin-from-filename", "$FILENAME" }, + -- If the formatter supports range formatting, create the range arguments here + range_args = function(self, ctx) + return { "--line-start", ctx.range.start[1], "--line-end", ctx.range["end"][1] } + end, + -- Send file contents to stdin, read new contents from stdout (default true) + -- When false, will create a temp file (will appear in "$FILENAME" args). The temp + -- file is assumed to be modified in-place by the format command. + stdin = true, + -- A function that calculates the directory to run the command in + cwd = require("conform.util").root_file({ ".editorconfig", "package.json" }), + -- When cwd is not found, don't run the formatter (default false) + require_cwd = true, + -- When stdin=false, use this template to generate the temporary file that gets formatted + tmpfile_format = ".conform.$RANDOM.$FILENAME", + -- When returns false, the formatter will not be used + condition = function(self, ctx) + return vim.fs.basename(ctx.filename) ~= "README.md" + end, + -- Exit codes that indicate success (default { 0 }) + exit_codes = { 0, 1 }, + -- Environment variables. This can also be a function that returns a table. + env = { + VAR = "value", + }, + -- Set to false to disable merging the config with the base definition + inherit = true, + -- When inherit = true, add these additional arguments to the command. + -- This can also be a function, like args + prepend_args = { "--use-tabs" }, + }, + -- These can also be a function that returns the formatter + other_formatter = function(bufnr) + return { + command = "my_cmd", + } + end, + }, +}) + +-- You can set formatters_by_ft and formatters directly +require("conform").formatters_by_ft.lua = { "stylua" } +require("conform").formatters.my_formatter = { + command = "my_cmd", +} +``` + + + +## Formatter options + + + +- [injected](doc/formatter_options.md#injected) +- [prettier](doc/formatter_options.md#prettier) +- [rustfmt](doc/formatter_options.md#rustfmt) +- [yew-fmt](doc/formatter_options.md#yew-fmt) + + + +## API + + + +### setup(opts) + +`setup(opts)` + +| Param | Type | Desc | | +| ----- | ------------------------ | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| opts | `nil\|conform.setupOpts` | | | +| | formatters_by_ft | `nil\|table` | Map of filetype to formatters | +| | format_on_save | `nil\|conform.FormatOpts\|fun(bufnr: integer): conform.FormatOpts` | If this is set, Conform will run the formatter on save. It will pass the table to conform.format(). This can also be a function that returns the table. | +| | format_after_save | `nil\|conform.FormatOpts\|fun(bufnr: integer): conform.FormatOpts` | If this is set, Conform will run the formatter asynchronously after save. It will pass the table to conform.format(). This can also be a function that returns the table. | +| | log_level | `nil\|integer` | Set the log level (e.g. `vim.log.levels.DEBUG`). Use `:ConformInfo` to see the location of the log file. | +| | notify_on_error | `nil\|boolean` | Conform will notify you when a formatter errors (default true). | +| | formatters | `nil\|table` | Custom formatters and overrides for built-in formatters. | + +### format(opts, callback) + +`format(opts, callback): boolean` \ +Format a buffer + +| Param | Type | Desc | | +| -------- | ---------------------------------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| opts | `nil\|conform.FormatOpts` | | | +| | timeout_ms | `nil\|integer` | Time in milliseconds to block for formatting. Defaults to 1000. No effect if async = true. | +| | bufnr | `nil\|integer` | Format this buffer (default 0) | +| | async | `nil\|boolean` | If true the method won't block. Defaults to false. If the buffer is modified before the formatter completes, the formatting will be discarded. | +| | dry_run | `nil\|boolean` | If true don't apply formatting changes to the buffer | +| | formatters | `nil\|string[]` | List of formatters to run. Defaults to all formatters for the buffer filetype. | +| | lsp_fallback | `nil\|boolean\|"always"` | Attempt LSP formatting if no formatters are available. Defaults to false. If "always", will attempt LSP formatting even if formatters are available. | +| | quiet | `nil\|boolean` | Don't show any notifications for warnings or failures. Defaults to false. | +| | range | `nil\|table` | Range to format. Table must contain `start` and `end` keys with {row, col} tuples using (1,0) indexing. Defaults to current selection in visual mode | +| | id | `nil\|integer` | Passed to vim.lsp.buf.format when lsp_fallback = true | +| | name | `nil\|string` | Passed to vim.lsp.buf.format when lsp_fallback = true | +| | filter | `nil\|fun(client: table): boolean` | Passed to vim.lsp.buf.format when lsp_fallback = true | +| callback | `nil\|fun(err: nil\|string, did_edit: nil\|boolean)` | Called once formatting has completed | | + +Returns: + +| Type | Desc | +| ------- | ------------------------------------- | +| boolean | True if any formatters were attempted | + +### list_formatters(bufnr) + +`list_formatters(bufnr): conform.FormatterInfo[]` \ +Retrieve the available formatters for a buffer + +| Param | Type | Desc | +| ----- | -------------- | ---- | +| bufnr | `nil\|integer` | | + +### list_all_formatters() + +`list_all_formatters(): conform.FormatterInfo[]` \ +List information about all filetype-configured formatters + + +### get_formatter_info(formatter, bufnr) + +`get_formatter_info(formatter, bufnr): conform.FormatterInfo` \ +Get information about a formatter (including availability) + +| Param | Type | Desc | +| --------- | -------------- | ------------------------- | +| formatter | `string` | The name of the formatter | +| bufnr | `nil\|integer` | | + +### will_fallback_lsp(options) + +`will_fallback_lsp(options): boolean` \ +Check if the buffer will use LSP formatting when lsp_fallback = true + +| Param | Type | Desc | +| ------- | ------------ | ------------------------------------ | +| options | `nil\|table` | Options passed to vim.lsp.buf.format | + + +## Acknowledgements + +Thanks to + +- [nvim-lint](https://github.com/mfussenegger/nvim-lint) for providing inspiration for the config and API. It's an excellent plugin that balances power and simplicity. +- [null-ls](https://github.com/jose-elias-alvarez/null-ls.nvim) for formatter configurations and being my formatter/linter of choice for a long time. diff --git a/config/neovim/store/lazy-plugins/conform.nvim/doc/advanced_topics.md b/config/neovim/store/lazy-plugins/conform.nvim/doc/advanced_topics.md new file mode 100644 index 00000000..4dcc4212 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/doc/advanced_topics.md @@ -0,0 +1,31 @@ +# Advanced topics + + + +- [Minimal format diffs](#minimal-format-diffs) +- [Range formatting](#range-formatting) +- [Injected language formatting (code blocks)](#injected-language-formatting-code-blocks) + + + +## Minimal format diffs + +To understand why this is important and why conform.nvim is different we need a bit of historical context. Formatting tools work by taking in the current state of the file and outputting the same contents, with formatting applied. The way most formatting plugins work is they take the new content and replace the entire buffer. The benefit of this approach is that it's very simple. It's easy to code, it's easy to reason about, and it's easy to debug. + +What conform does differently is it leverages `:help vim.diff`, Neovim's lua bindings for xdiff. We use this to compare the formatted lines to the original content and calculate minimal chunks where changes need to be applied. From there, we convert these chunks into LSP TextEdit objects and use `vim.lsp.util.apply_text_edits()` to actually apply the changes. Since we're using the built-in LSP utility, we get the benefits of all the work that was put into improving the LSP formatting experience, such as the preservation of extmarks. The piecewise update also does a better job of preserving cursor position, folds, viewport position, etc. + +## Range formatting + +When a formatting tool doesn't have built-in support for range formatting, conform will attempt to "fake it" when requested. This is necessarily a **best effort** operation and is **not** guaranteed to be correct or error-free, however in _most_ cases it should produce acceptable results. + +The way this "aftermarket" range formatting works is conform will format the entire buffer as per usual, but during the diff process it will discard diffs that fall outside of the selected range. This usually approximates a correct result, but as you can guess it's possible for the formatting to exceed the range (if the diff covering the range is large) or for the results to be incorrect (if the formatting changes require two diffs in different locations to be semantically correct). + +## Injected language formatting (code blocks) + +Requires: Neovim 0.9+ + +Sometimes you may have a file that contains small chunks of code in another language. This is most common for markup formats like markdown and neorg, but can theoretically be present in any filetype (for example, embedded SQL queries in a host language). For files like this, it would be nice to be able to format these code chunks using their language-specific formatters. + +The way that conform supports this is via the `injected` formatter. If you run this formatter on a file, it will use treesitter to parse out the blocks in the file that have different languages and runs the formatters for that filetype (configured with `formatters_by_ft`). The formatters are run in parallel, one job for each language block. + +This formatter is experimental; the behavior and configuration options are still subject to change. The current list of configuration options can be found at [formatter options](formatter_options.md#injected) diff --git a/config/neovim/store/lazy-plugins/conform.nvim/doc/conform.txt b/config/neovim/store/lazy-plugins/conform.nvim/doc/conform.txt new file mode 100644 index 00000000..4271e950 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/doc/conform.txt @@ -0,0 +1,413 @@ +*conform.txt* +*Conform* *conform* *conform.nvim* +-------------------------------------------------------------------------------- +CONTENTS *conform-contents* + + 1. Options |conform-options| + 2. Api |conform-api| + 3. Formatters |conform-formatters| + +-------------------------------------------------------------------------------- +OPTIONS *conform-options* + +>lua + require("conform").setup({ + -- Map of filetype to formatters + formatters_by_ft = { + lua = { "stylua" }, + -- Conform will run multiple formatters sequentially + go = { "goimports", "gofmt" }, + -- Use a sub-list to run only the first available formatter + javascript = { { "prettierd", "prettier" } }, + -- You can use a function here to determine the formatters dynamically + python = function(bufnr) + if require("conform").get_formatter_info("ruff_format", bufnr).available then + return { "ruff_format" } + else + return { "isort", "black" } + end + end, + -- Use the "*" filetype to run formatters on all filetypes. + ["*"] = { "codespell" }, + -- Use the "_" filetype to run formatters on filetypes that don't + -- have other formatters configured. + ["_"] = { "trim_whitespace" }, + }, + -- If this is set, Conform will run the formatter on save. + -- It will pass the table to conform.format(). + -- This can also be a function that returns the table. + format_on_save = { + -- I recommend these options. See :help conform.format for details. + lsp_fallback = true, + timeout_ms = 500, + }, + -- If this is set, Conform will run the formatter asynchronously after save. + -- It will pass the table to conform.format(). + -- This can also be a function that returns the table. + format_after_save = { + lsp_fallback = true, + }, + -- Set the log level. Use `:ConformInfo` to see the location of the log file. + log_level = vim.log.levels.ERROR, + -- Conform will notify you when a formatter errors + notify_on_error = true, + -- Custom formatters and overrides for built-in formatters + formatters = { + my_formatter = { + -- This can be a string or a function that returns a string. + -- When defining a new formatter, this is the only field that is required + command = "my_cmd", + -- A list of strings, or a function that returns a list of strings + -- Return a single string instead of a list to run the command in a shell + args = { "--stdin-from-filename", "$FILENAME" }, + -- If the formatter supports range formatting, create the range arguments here + range_args = function(self, ctx) + return { "--line-start", ctx.range.start[1], "--line-end", ctx.range["end"][1] } + end, + -- Send file contents to stdin, read new contents from stdout (default true) + -- When false, will create a temp file (will appear in "$FILENAME" args). The temp + -- file is assumed to be modified in-place by the format command. + stdin = true, + -- A function that calculates the directory to run the command in + cwd = require("conform.util").root_file({ ".editorconfig", "package.json" }), + -- When cwd is not found, don't run the formatter (default false) + require_cwd = true, + -- When stdin=false, use this template to generate the temporary file that gets formatted + tmpfile_format = ".conform.$RANDOM.$FILENAME", + -- When returns false, the formatter will not be used + condition = function(self, ctx) + return vim.fs.basename(ctx.filename) ~= "README.md" + end, + -- Exit codes that indicate success (default { 0 }) + exit_codes = { 0, 1 }, + -- Environment variables. This can also be a function that returns a table. + env = { + VAR = "value", + }, + -- Set to false to disable merging the config with the base definition + inherit = true, + -- When inherit = true, add these additional arguments to the command. + -- This can also be a function, like args + prepend_args = { "--use-tabs" }, + }, + -- These can also be a function that returns the formatter + other_formatter = function(bufnr) + return { + command = "my_cmd", + } + end, + }, + }) + + -- You can set formatters_by_ft and formatters directly + require("conform").formatters_by_ft.lua = { "stylua" } + require("conform").formatters.my_formatter = { + command = "my_cmd", + } +< + +-------------------------------------------------------------------------------- +API *conform-api* + +setup({opts}) *conform.setup* + + Parameters: + {opts} `nil|conform.setupOpts` + {formatters_by_ft} `nil|table` Map + of filetype to formatters + {format_on_save} `nil|conform.FormatOpts|fun(bufnr: integer): conform.FormatOpts` I + f this is set, Conform will run the formatter on + save. It will pass the table to conform.format(). + This can also be a function that returns the table. + {format_after_save} `nil|conform.FormatOpts|fun(bufnr: integer): conform.FormatOpts` I + f this is set, Conform will run the formatter + asynchronously after save. It will pass the table + to conform.format(). This can also be a function + that returns the table. + {log_level} `nil|integer` Set the log level (e.g. + `vim.log.levels.DEBUG`). Use `:ConformInfo` to see + the location of the log file. + {notify_on_error} `nil|boolean` Conform will notify you when a + formatter errors (default true). + {formatters} `nil|table` C + ustom formatters and overrides for built-in + formatters. + +format({opts}, {callback}): boolean *conform.format* + Format a buffer + + Parameters: + {opts} `nil|conform.FormatOpts` + {timeout_ms} `nil|integer` Time in milliseconds to block for + formatting. Defaults to 1000. No effect if async = + true. + {bufnr} `nil|integer` Format this buffer (default 0) + {async} `nil|boolean` If true the method won't block. Defaults + to false. If the buffer is modified before the + formatter completes, the formatting will be discarded. + {dry_run} `nil|boolean` If true don't apply formatting changes to + the buffer + {formatters} `nil|string[]` List of formatters to run. Defaults to + all formatters for the buffer filetype. + {lsp_fallback} `nil|boolean|"always"` Attempt LSP formatting if no + formatters are available. Defaults to false. If + "always", will attempt LSP formatting even if + formatters are available. + {quiet} `nil|boolean` Don't show any notifications for warnings + or failures. Defaults to false. + {range} `nil|table` Range to format. Table must contain `start` + and `end` keys with {row, col} tuples using (1,0) + indexing. Defaults to current selection in visual mode + {id} `nil|integer` Passed to |vim.lsp.buf.format| when + lsp_fallback = true + {name} `nil|string` Passed to |vim.lsp.buf.format| when + lsp_fallback = true + {filter} `nil|fun(client: table): boolean` Passed to + |vim.lsp.buf.format| when lsp_fallback = true + {callback} `nil|fun(err: nil|string, did_edit: nil|boolean)` Called once + formatting has completed + Returns: + `boolean` True if any formatters were attempted + +list_formatters({bufnr}): conform.FormatterInfo[] *conform.list_formatters* + Retrieve the available formatters for a buffer + + Parameters: + {bufnr} `nil|integer` + +list_all_formatters(): conform.FormatterInfo[] *conform.list_all_formatters* + List information about all filetype-configured formatters + + +get_formatter_info({formatter}, {bufnr}): conform.FormatterInfo *conform.get_formatter_info* + Get information about a formatter (including availability) + + Parameters: + {formatter} `string` The name of the formatter + {bufnr} `nil|integer` + +will_fallback_lsp({options}): boolean *conform.will_fallback_lsp* + Check if the buffer will use LSP formatting when lsp_fallback = true + + Parameters: + {options} `nil|table` Options passed to |vim.lsp.buf.format| + +-------------------------------------------------------------------------------- +FORMATTERS *conform-formatters* + +`alejandra` - The Uncompromising Nix Code Formatter. +`asmfmt` - Go Assembler Formatter +`ast-grep` - A CLI tool for code structural search, lint and rewriting. Written + in Rust. +`astyle` - A Free, Fast, and Small Automatic Formatter for C, C++, C++/CLI, + Objective-C, C#, and Java Source Code. +`auto_optional` - Adds the Optional type-hint to arguments where the default + value is None. +`autocorrect` - A linter and formatter to help you to improve copywriting, + correct spaces, words, and punctuations between CJK. +`autoflake` - Removes unused imports and unused variables as reported by + pyflakes. +`autopep8` - A tool that automatically formats Python code to conform to the PEP + 8 style guide. +`awk` - Format awk programs with awk +`bean-format` - Reformat Beancount files to right-align all the numbers at the + same, minimal column. +`beautysh` - A Bash beautifier for the masses. +`bibtex-tidy` - Cleaner and Formatter for BibTeX files. +`bicep` - Bicep is a Domain Specific Language (DSL) for deploying Azure + resources declaratively. +`biome` - A toolchain for web projects, aimed to provide functionalities to + maintain them. +`biome-check` - A toolchain for web projects, aimed to provide functionalities + to maintain them. +`black` - The uncompromising Python code formatter. +`blade-formatter` - An opinionated blade template formatter for Laravel that + respects readability. +`blue` - The slightly less uncompromising Python code formatter. +`bpfmt` - Android Blueprint file formatter. +`buf` - A new way of working with Protocol Buffers. +`buildifier` - buildifier is a tool for formatting bazel BUILD and .bzl files + with a standard convention. +`cabal_fmt` - Format cabal files with cabal-fmt +`cbfmt` - A tool to format codeblocks inside markdown and org documents. +`clang-format` - Tool to format C/C++/… code according to a set of rules and + heuristics. +`cljstyle` - Formatter for Clojure code. +`cmake_format` - Parse cmake listfiles and format them nicely. +`codespell` - Check code for common misspellings. +`crystal` - Format Crystal code. +`csharpier` - The opinionated C# code formatter. +`cue_fmt` - Format CUE files using `cue fmt` command. +`darker` - Run black only on changed lines. +`dart_format` - Replace the whitespace in your program with formatting that + follows Dart guidelines. +`deno_fmt` - Use [Deno](https://deno.land/) to format TypeScript, + JavaScript/JSON and markdown. +`dfmt` - Formatter for D source code. +`djlint` - ✨ HTML Template Linter and Formatter. Django - Jinja - Nunjucks - + Handlebars - GoLang. +`dprint` - Pluggable and configurable code formatting platform written in Rust. +`easy-coding-standard` - ecs - Use Coding Standard with 0-knowledge of PHP-CS- + Fixer and PHP_CodeSniffer. +`elm_format` - elm-format formats Elm source code according to a standard set of + rules based on the official [Elm Style Guide](https://elm- + lang.org/docs/style-guide). +`erb_format` - Format ERB files with speed and precision. +`eslint_d` - Like ESLint, but faster. +`fantomas` - F# source code formatter. +`fish_indent` - Indent or otherwise prettify a piece of fish code. +`fixjson` - JSON Fixer for Humans using (relaxed) JSON5. +`fnlfmt` - A formatter for Fennel code. +`forge_fmt` - Forge is a command-line tool that ships with Foundry. Forge tests, + builds, and deploys your smart contracts. +`fourmolu` - A fork of ormolu that uses four space indentation and allows + arbitrary configuration. +`gci` - GCI, a tool that controls Go package import order and makes it always + deterministic. +`gdformat` - A formatter for Godot's gdscript. +`gersemi` - A formatter to make your CMake code the real treasure. +`gleam` - ⭐️ A friendly language for building type-safe, scalable systems! +`gn` - gn build system. +`gofmt` - Formats go programs. +`gofumpt` - Enforce a stricter format than gofmt, while being backwards + compatible. That is, gofumpt is happy with a subset of the formats + that gofmt is happy with. +`goimports` - Updates your Go import lines, adding missing ones and removing + unreferenced ones. +`goimports-reviser` - Right imports sorting & code formatting tool (goimports + alternative). +`golines` - A golang formatter that fixes long lines. +`google-java-format` - Reformats Java source code according to Google Java + Style. +`hcl` - A formatter for HCL files. +`htmlbeautifier` - A normaliser/beautifier for HTML that also understands + embedded Ruby. Ideal for tidying up Rails templates. +`indent` - GNU Indent. +`injected` - Format treesitter injected languages. +`inko` - A language for building concurrent software with confidence +`isort` - Python utility / library to sort imports alphabetically and + automatically separate them into sections and by type. +`joker` - Small Clojure interpreter, linter and formatter. +`jq` - Command-line JSON processor. +`jsonnetfmt` - jsonnetfmt is a command line tool to format jsonnet files. +`just` - Format Justfile. +`ktfmt` - Reformats Kotlin source code to comply with the common community + standard conventions. +`ktlint` - An anti-bikeshedding Kotlin linter with built-in formatter. +`latexindent` - A perl script for formatting LaTeX files that is generally + included in major TeX distributions. +`leptosfmt` - A formatter for the Leptos view! macro. +`liquidsoap-prettier` - A binary to format Liquidsoap scripts +`markdown-toc` - API and CLI for generating a markdown TOC (table of contents) + for a README or any markdown files. +`markdownlint` - A Node.js style checker and lint tool for Markdown/CommonMark + files. +`markdownlint-cli2` - A fast, flexible, configuration-based command-line + interface for linting Markdown/CommonMark files with the + markdownlint library. +`mdformat` - An opinionated Markdown formatter. +`mdsf` - Format markdown code blocks using your favorite code formatters. +`mdslw` - Prepare your markdown for easy diff'ing by adding line breaks after + every sentence. +`mix` - Format Elixir files using the mix format command. +`nimpretty` - nimpretty is a Nim source code beautifier that follows the + official style guide. +`nixfmt` - nixfmt is a formatter for Nix code, intended to apply a uniform + style. +`nixpkgs_fmt` - nixpkgs-fmt is a Nix code formatter for nixpkgs. +`ocamlformat` - Auto-formatter for OCaml code. +`ocp-indent` - Automatic indentation of OCaml source files. +`opa_fmt` - Format Rego files using `opa fmt` command. +`ormolu` - A formatter for Haskell source code. +`packer_fmt` - The packer fmt Packer command is used to format HCL2 + configuration files to a canonical format and style. +`pangu` - Insert whitespace between CJK and half-width characters. +`perlimports` - Make implicit Perl imports explicit. +`perltidy` - Perl::Tidy, a source code formatter for Perl. +`pg_format` - PostgreSQL SQL syntax beautifier. +`php_cs_fixer` - The PHP Coding Standards Fixer. +`phpcbf` - PHP Code Beautifier and Fixer fixes violations of a defined coding + standard. +`phpinsights` - The perfect starting point to analyze the code quality of your + PHP projects. +`pint` - Laravel Pint is an opinionated PHP code style fixer for minimalists. +`prettier` - Prettier is an opinionated code formatter. It enforces a consistent + style by parsing your code and re-printing it with its own rules that + take the maximum line length into account, wrapping code when + necessary. +`prettierd` - prettier, as a daemon, for ludicrous formatting speed. +`pretty-php` - The opinionated PHP code formatter. +`puppet-lint` - Check that your Puppet manifests conform to the style guide. +`purs-tidy` - A syntax tidy-upper for PureScript. +`reorder-python-imports` - Rewrites source to reorder python imports +`rescript-format` - The built-in ReScript formatter. +`roc` - A fast, friendly, functional language. +`rubocop` - Ruby static code analyzer and formatter, based on the community Ruby + style guide. +`rubyfmt` - Ruby Autoformatter! (Written in Rust) +`ruff_fix` - An extremely fast Python linter, written in Rust. Fix lint errors. +`ruff_format` - An extremely fast Python linter, written in Rust. Formatter + subcommand. +`ruff_organize_imports` - An extremely fast Python linter, written in Rust. + Organize imports. +`rufo` - Rufo is an opinionated ruby formatter. +`rustfmt` - A tool for formatting rust code according to style guidelines. +`rustywind` - A tool for formatting Tailwind CSS classes. +`scalafmt` - Code formatter for Scala. +`shellcheck` - A static analysis tool for shell scripts. +`shellharden` - The corrective bash syntax highlighter. +`shfmt` - A shell parser, formatter, and interpreter with `bash` support. +`smlfmt` - A custom parser and code formatter for Standard ML. +`snakefmt` - a formatting tool for Snakemake files following the design of + Black. +`sql_formatter` - A whitespace formatter for different query languages. +`sqlfluff` - A modular SQL linter and auto-formatter with support for multiple + dialects and templated code. +`sqlfmt` - sqlfmt formats your dbt SQL files so you don't have to. It is similar + in nature to Black, gofmt, and rustfmt (but for SQL) +`squeeze_blanks` - Squeeze repeated blank lines into a single blank line via + `cat -s`. +`standardjs` - JavaScript Standard style guide, linter, and formatter. +`standardrb` - Ruby's bikeshed-proof linter and formatter. +`stylelint` - A mighty CSS linter that helps you avoid errors and enforce + conventions. +`styler` - R formatter and linter. +`stylua` - An opinionated code formatter for Lua. +`swift_format` - Swift formatter from apple. Requires building from source with + `swift build`. +`swiftformat` - SwiftFormat is a code library and command-line tool for + reformatting `swift` code on macOS or Linux. +`taplo` - A TOML toolkit written in Rust. +`templ` - Formats templ template files. +`terraform_fmt` - The terraform-fmt command rewrites `terraform` configuration + files to a canonical format and style. +`terragrunt_hclfmt` - Format hcl files into a canonical format. +`tlint` - Tighten linter for Laravel conventions with support for auto- + formatting. +`tofu_fmt` - The tofu-fmt command rewrites OpenTofu configuration files to a + canonical format and style. +`trim_newlines` - Trim new lines with awk. +`trim_whitespace` - Trim whitespaces with awk. +`twig-cs-fixer` - Automatically fix Twig Coding Standards issues +`typos` - Source code spell checker +`typstfmt` - Basic formatter for the Typst language with a future! +`typstyle` - Beautiful and reliable typst code formatter. +`uncrustify` - A source code beautifier for C, C++, C#, ObjectiveC, D, Java, + Pawn and Vala. +`usort` - Safe, minimal import sorting for Python projects. +`verible` - The SystemVerilog formatter. +`xmlformat` - xmlformatter is an Open Source Python package, which provides + formatting of XML documents. +`xmllint` - Despite the name, xmllint can be used to format XML files as well as + lint them. +`yamlfix` - A configurable YAML formatter that keeps comments. +`yamlfmt` - yamlfmt is an extensible command line tool or library to format yaml + files. +`yapf` - Yet Another Python Formatter. +`yew-fmt` - Code formatter for the Yew framework. +`yq` - YAML/JSON processor +`zigfmt` - Reformat Zig source into canonical form. +`zprint` - Formatter for Clojure and EDN. + +================================================================================ +vim:tw=80:ts=2:ft=help:norl:syntax=help: diff --git a/config/neovim/store/lazy-plugins/conform.nvim/doc/formatter_options.md b/config/neovim/store/lazy-plugins/conform.nvim/doc/formatter_options.md new file mode 100644 index 00000000..5d3adb11 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/doc/formatter_options.md @@ -0,0 +1,108 @@ +# Formatter Options + + + +- [injected](#injected) +- [prettier](#prettier) +- [rustfmt](#rustfmt) +- [yew-fmt](#yew-fmt) + + + +All formatters can be customized by directly changing the command, args, or other values (see [customizing formatters](../README.md#customizing-formatters)). Some formatters have a bit more advanced logic built in to those functions and expose additional configuration options. You can pass these values in like so: + +```lua +-- Customize the "injected" formatter +require("conform").formatters.injected = { + -- Set the options field + options = { + -- Set individual option values + ignore_errors = true, + lang_to_formatters = { + json = { "jq" }, + }, + }, +} +``` + + + +## injected + +```lua +options = { + -- Set to true to ignore errors + ignore_errors = false, + -- Map of treesitter language to file extension + -- A temporary file name with this extension will be generated during formatting + -- because some formatters care about the filename. + lang_to_ext = { + bash = "sh", + c_sharp = "cs", + elixir = "exs", + javascript = "js", + julia = "jl", + latex = "tex", + markdown = "md", + python = "py", + ruby = "rb", + rust = "rs", + teal = "tl", + typescript = "ts", + }, + -- Map of treesitter language to formatters to use + -- (defaults to the value from formatters_by_ft) + lang_to_formatters = {}, +} +``` + +## prettier + +```lua +options = { + -- Use a specific prettier parser for a filetype + -- Otherwise, prettier will try to infer the parser from the file name + ft_parsers = { + -- javascript = "babel", + -- javascriptreact = "babel", + -- typescript = "typescript", + -- typescriptreact = "typescript", + -- vue = "vue", + -- css = "css", + -- scss = "scss", + -- less = "less", + -- html = "html", + -- json = "json", + -- jsonc = "json", + -- yaml = "yaml", + -- markdown = "markdown", + -- ["markdown.mdx"] = "mdx", + -- graphql = "graphql", + -- handlebars = "glimmer", + }, + -- Use a specific prettier parser for a file extension + ext_parsers = { + -- qmd = "markdown", + }, +} +``` + +## rustfmt + +```lua +options = { + -- The default edition of Rust to use when no Cargo.toml file is found + default_edition = "2021", +} +``` + +## yew-fmt + +```lua +options = { + -- The default edition of Rust to use when no Cargo.toml file is found + default_edition = "2021", +} +``` + + diff --git a/config/neovim/store/lazy-plugins/conform.nvim/doc/recipes.md b/config/neovim/store/lazy-plugins/conform.nvim/doc/recipes.md new file mode 100644 index 00000000..97eaa16e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/doc/recipes.md @@ -0,0 +1,180 @@ +# Recipes + + + +- [Format command](#format-command) +- [Autoformat with extra features](#autoformat-with-extra-features) +- [Command to toggle format-on-save](#command-to-toggle-format-on-save) +- [Automatically run slow formatters async](#automatically-run-slow-formatters-async) +- [Lazy loading with lazy.nvim](#lazy-loading-with-lazynvim) + + + +## Format command + +Define a command to run async formatting + +```lua +vim.api.nvim_create_user_command("Format", function(args) + local range = nil + if args.count ~= -1 then + local end_line = vim.api.nvim_buf_get_lines(0, args.line2 - 1, args.line2, true)[1] + range = { + start = { args.line1, 0 }, + ["end"] = { args.line2, end_line:len() }, + } + end + require("conform").format({ async = true, lsp_fallback = true, range = range }) +end, { range = true }) +``` + +## Autoformat with extra features + +If you want more complex logic than the basic `format_on_save` option allows, you can use a function instead. + + + +```lua +-- if format_on_save is a function, it will be called during BufWritePre +require("conform").setup({ + format_on_save = function(bufnr) + -- Disable autoformat on certain filetypes + local ignore_filetypes = { "sql", "java" } + if vim.tbl_contains(ignore_filetypes, vim.bo[bufnr].filetype) then + return + end + -- Disable with a global or buffer-local variable + if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then + return + end + -- Disable autoformat for files in a certain path + local bufname = vim.api.nvim_buf_get_name(bufnr) + if bufname:match("/node_modules/") then + return + end + -- ...additional logic... + return { timeout_ms = 500, lsp_fallback = true } + end, +}) + +-- There is a similar affordance for format_after_save, which uses BufWritePost. +-- This is good for formatters that are too slow to run synchronously. +require("conform").setup({ + format_after_save = function(bufnr) + if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then + return + end + -- ...additional logic... + return { lsp_fallback = true } + end, +}) +``` + + + +## Command to toggle format-on-save + +Create user commands to quickly enable/disable autoformatting + +```lua +require("conform").setup({ + format_on_save = function(bufnr) + -- Disable with a global or buffer-local variable + if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then + return + end + return { timeout_ms = 500, lsp_fallback = true } + end, +}) + +vim.api.nvim_create_user_command("FormatDisable", function(args) + if args.bang then + -- FormatDisable! will disable formatting just for this buffer + vim.b.disable_autoformat = true + else + vim.g.disable_autoformat = true + end +end, { + desc = "Disable autoformat-on-save", + bang = true, +}) +vim.api.nvim_create_user_command("FormatEnable", function() + vim.b.disable_autoformat = false + vim.g.disable_autoformat = false +end, { + desc = "Re-enable autoformat-on-save", +}) +``` + +## Automatically run slow formatters async + +This snippet will automatically detect which formatters take too long to run synchronously and will run them async on save instead. + +```lua +local slow_format_filetypes = {} +require("conform").setup({ + format_on_save = function(bufnr) + if slow_format_filetypes[vim.bo[bufnr].filetype] then + return + end + local function on_format(err) + if err and err:match("timeout$") then + slow_format_filetypes[vim.bo[bufnr].filetype] = true + end + end + + return { timeout_ms = 200, lsp_fallback = true }, on_format + end, + + format_after_save = function(bufnr) + if not slow_format_filetypes[vim.bo[bufnr].filetype] then + return + end + return { lsp_fallback = true } + end, +}) +``` + +## Lazy loading with lazy.nvim + +Here is the recommended config for lazy-loading using lazy.nvim + +```lua +return { + "stevearc/conform.nvim", + event = { "BufWritePre" }, + cmd = { "ConformInfo" }, + keys = { + { + -- Customize or remove this keymap to your liking + "f", + function() + require("conform").format({ async = true, lsp_fallback = true }) + end, + mode = "", + desc = "Format buffer", + }, + }, + -- Everything in opts will be passed to setup() + opts = { + -- Define your formatters + formatters_by_ft = { + lua = { "stylua" }, + python = { "isort", "black" }, + javascript = { { "prettierd", "prettier" } }, + }, + -- Set up format-on-save + format_on_save = { timeout_ms = 500, lsp_fallback = true }, + -- Customize formatters + formatters = { + shfmt = { + prepend_args = { "-i", "2" }, + }, + }, + }, + init = function() + -- If you want the formatexpr, here is the place to set it + vim.o.formatexpr = "v:lua.require'conform'.formatexpr()" + end, +} +``` diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/errors.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/errors.lua new file mode 100644 index 00000000..43e9a7b2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/errors.lua @@ -0,0 +1,66 @@ +local M = {} + +---@class conform.Error +---@field code conform.ERROR_CODE +---@field message string +---@field debounce_message? boolean + +---@enum conform.ERROR_CODE +M.ERROR_CODE = { + -- Command was passed invalid arguments + INVALID_ARGS = 1, + -- Command was not executable + NOT_EXECUTABLE = 2, + -- Error occurred during when calling jobstart + JOBSTART = 3, + -- Command timed out during execution + TIMEOUT = 4, + -- Command was pre-empted by another call to format + INTERRUPTED = 5, + -- Command produced an error during execution + RUNTIME = 6, + -- Asynchronous formatter results were discarded due to a concurrent modification + CONCURRENT_MODIFICATION = 7, +} + +---@param code conform.ERROR_CODE +---@return integer +M.level_for_code = function(code) + if code == M.ERROR_CODE.CONCURRENT_MODIFICATION then + return vim.log.levels.INFO + elseif code == M.ERROR_CODE.TIMEOUT or code == M.ERROR_CODE.INTERRUPTED then + return vim.log.levels.WARN + else + return vim.log.levels.ERROR + end +end + +---Returns true if the error occurred while attempting to run the formatter +---@param code conform.ERROR_CODE +---@return boolean +M.is_execution_error = function(code) + return code == M.ERROR_CODE.RUNTIME + or code == M.ERROR_CODE.NOT_EXECUTABLE + or code == M.ERROR_CODE.INVALID_ARGS + or code == M.ERROR_CODE.JOBSTART +end + +---@param err1? conform.Error +---@param err2? conform.Error +---@return nil|conform.Error +M.coalesce = function(err1, err2) + if not err1 then + return err2 + elseif not err2 then + return err1 + end + local level1 = M.level_for_code(err1.code) + local level2 = M.level_for_code(err2.code) + if level2 > level1 then + return err2 + else + return err1 + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/alejandra.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/alejandra.lua new file mode 100644 index 00000000..3a712b9f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/alejandra.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://kamadorueda.com/alejandra/", + description = "The Uncompromising Nix Code Formatter.", + }, + command = "alejandra", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/asmfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/asmfmt.lua new file mode 100644 index 00000000..5c77ac4b --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/asmfmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/klauspost/asmfmt", + description = "Go Assembler Formatter", + }, + command = "asmfmt", + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ast-grep.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ast-grep.lua new file mode 100644 index 00000000..0b47e397 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ast-grep.lua @@ -0,0 +1,11 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://ast-grep.github.io/", + description = "A CLI tool for code structural search, lint and rewriting. Written in Rust.", + }, + command = "ast-grep", + args = { "scan", "--update-all", "$FILENAME" }, + stdin = false, + exit_codes = { 0, 5 }, -- 5 = no config file exists +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/astyle.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/astyle.lua new file mode 100644 index 00000000..82743c4b --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/astyle.lua @@ -0,0 +1,14 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://astyle.sourceforge.net/astyle.html", + description = "A Free, Fast, and Small Automatic Formatter for C, C++, C++/CLI, Objective-C, C#, and Java Source Code.", + }, + command = "astyle", + args = { "--quiet" }, + cwd = util.root_file({ + ".astylerc", + "_astylerc", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/auto_optional.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/auto_optional.lua new file mode 100644 index 00000000..b0738dcc --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/auto_optional.lua @@ -0,0 +1,12 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://auto-optional.daanluttik.nl/", + description = "Adds the Optional type-hint to arguments where the default value is None.", + }, + command = "auto-optional", + args = { + "$FILENAME", + }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/autocorrect.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/autocorrect.lua new file mode 100644 index 00000000..b12e99ff --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/autocorrect.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/huacnlee/autocorrect", + description = "A linter and formatter to help you to improve copywriting, correct spaces, words, and punctuations between CJK.", + }, + command = "autocorrect", + args = { "--stdin" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/autoflake.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/autoflake.lua new file mode 100644 index 00000000..c7d0a3bf --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/autoflake.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/PyCQA/autoflake", + description = "Removes unused imports and unused variables as reported by pyflakes.", + }, + command = "autoflake", + args = { "--stdin-display-name", "$FILENAME", "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/autopep8.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/autopep8.lua new file mode 100644 index 00000000..3d5b015c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/autopep8.lua @@ -0,0 +1,12 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/hhatto/autopep8", + description = "A tool that automatically formats Python code to conform to the PEP 8 style guide.", + }, + command = "autopep8", + args = { "-" }, + range_args = function(self, ctx) + return { "-", "--line-range", tostring(ctx.range.start[1]), tostring(ctx.range["end"][1]) } + end, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/awk.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/awk.lua new file mode 100644 index 00000000..00df393e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/awk.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://www.gnu.org/software/gawk/manual/gawk.html", + description = "Format awk programs with awk", + }, + command = "awk", + args = { "-f", "-", "-o-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bean-format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bean-format.lua new file mode 100644 index 00000000..c985bfa5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bean-format.lua @@ -0,0 +1,11 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://beancount.github.io/docs/running_beancount_and_generating_reports.html#bean-format", + description = "Reformat Beancount files to right-align all the numbers at the same, minimal column.", + }, + command = "bean-format", + args = { + "-", + }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/beautysh.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/beautysh.lua new file mode 100644 index 00000000..4dac4e66 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/beautysh.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/lovesegfault/beautysh", + description = "A Bash beautifier for the masses.", + }, + command = "beautysh", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bibtex-tidy.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bibtex-tidy.lua new file mode 100644 index 00000000..e68a3816 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bibtex-tidy.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/FlamingTempura/bibtex-tidy", + description = "Cleaner and Formatter for BibTeX files.", + }, + command = "bibtex-tidy", + stdin = true, + args = { "--quiet" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bicep.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bicep.lua new file mode 100644 index 00000000..17ccbf1a --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bicep.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/Azure/bicep", + description = "Bicep is a Domain Specific Language (DSL) for deploying Azure resources declaratively.", + }, + command = "bicep", + args = { "format", "--stdout", "$FILENAME" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/biome-check.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/biome-check.lua new file mode 100644 index 00000000..5033cc32 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/biome-check.lua @@ -0,0 +1,15 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/biomejs/biome", + description = "A toolchain for web projects, aimed to provide functionalities to maintain them.", + }, + command = util.from_node_modules("biome"), + stdin = true, + args = { "check", "--apply", "--stdin-file-path", "$FILENAME" }, + cwd = util.root_file({ + "biome.json", + "biome.jsonc", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/biome.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/biome.lua new file mode 100644 index 00000000..99af89ed --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/biome.lua @@ -0,0 +1,15 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/biomejs/biome", + description = "A toolchain for web projects, aimed to provide functionalities to maintain them.", + }, + command = util.from_node_modules("biome"), + stdin = true, + args = { "format", "--stdin-file-path", "$FILENAME" }, + cwd = util.root_file({ + "biome.json", + "biome.jsonc", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/black.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/black.lua new file mode 100644 index 00000000..e27ed90d --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/black.lua @@ -0,0 +1,19 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/psf/black", + description = "The uncompromising Python code formatter.", + }, + command = "black", + args = { + "--stdin-filename", + "$FILENAME", + "--quiet", + "-", + }, + cwd = util.root_file({ + -- https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-via-a-file + "pyproject.toml", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/blade-formatter.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/blade-formatter.lua new file mode 100644 index 00000000..8b552d9c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/blade-formatter.lua @@ -0,0 +1,12 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/shufo/blade-formatter", + description = "An opinionated blade template formatter for Laravel that respects readability.", + }, + command = "blade-formatter", + args = { "--stdin" }, + stdin = true, + cwd = util.root_file({ "composer.json", "composer.lock" }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/blue.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/blue.lua new file mode 100644 index 00000000..6869339b --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/blue.lua @@ -0,0 +1,21 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/grantjenks/blue", + description = "The slightly less uncompromising Python code formatter.", + }, + command = "blue", + args = { + "--stdin-filename", + "$FILENAME", + "--quiet", + "-", + }, + cwd = util.root_file({ + "setup.cfg", + "pyproject.toml", + "tox.ini", + ".blue", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bpfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bpfmt.lua new file mode 100644 index 00000000..5f7c66d0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/bpfmt.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://source.android.com/docs/setup/reference/androidbp", + description = "Android Blueprint file formatter.", + }, + command = "bpfmt", + args = { "-w", "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/buf.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/buf.lua new file mode 100644 index 00000000..e430bf6d --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/buf.lua @@ -0,0 +1,11 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://buf.build/docs/reference/cli/buf/format", + description = "A new way of working with Protocol Buffers.", + }, + command = "buf", + args = { "format", "-w", "$FILENAME" }, + stdin = false, + cwd = require("conform.util").root_file({ "buf.yaml" }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/buildifier.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/buildifier.lua new file mode 100644 index 00000000..cd5e5cb6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/buildifier.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/bazelbuild/buildtools/tree/master/buildifier", + description = "buildifier is a tool for formatting bazel BUILD and .bzl files with a standard convention.", + }, + command = "buildifier", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cabal_fmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cabal_fmt.lua new file mode 100644 index 00000000..698e22c3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cabal_fmt.lua @@ -0,0 +1,8 @@ +return { + meta = { + url = "https://hackage.haskell.org/package/cabal-fmt", + description = "Format cabal files with cabal-fmt", + }, + command = "cabal-fmt", + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cbfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cbfmt.lua new file mode 100644 index 00000000..57c48e42 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cbfmt.lua @@ -0,0 +1,15 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/lukas-reineke/cbfmt", + description = "A tool to format codeblocks inside markdown and org documents.", + }, + command = "cbfmt", + args = { "--write", "--best-effort", "$FILENAME" }, + cwd = util.root_file({ + -- https://github.com/lukas-reineke/cbfmt#config + ".cbfmt.toml", + }), + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/clang-format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/clang-format.lua new file mode 100644 index 00000000..2e68fc1b --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/clang-format.lua @@ -0,0 +1,22 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://www.kernel.org/doc/html/latest/process/clang-format.html", + description = "Tool to format C/C++/… code according to a set of rules and heuristics.", + }, + command = "clang-format", + args = { "-assume-filename", "$FILENAME" }, + range_args = function(self, ctx) + local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range) + local length = end_offset - start_offset + return { + "-assume-filename", + "$FILENAME", + "--offset", + tostring(start_offset), + "--length", + tostring(length), + } + end, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/clang_format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/clang_format.lua new file mode 100644 index 00000000..72e545bb --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/clang_format.lua @@ -0,0 +1,4 @@ +-- This was renamed to clang-format +local conf = vim.deepcopy(require("conform.formatters.clang-format")) +conf.meta.deprecated = true +return conf diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cljstyle.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cljstyle.lua new file mode 100644 index 00000000..21205a5a --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cljstyle.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/greglook/cljstyle", + description = "Formatter for Clojure code.", + }, + command = "cljstyle", + args = { "pipe" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cmake_format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cmake_format.lua new file mode 100644 index 00000000..0e981083 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cmake_format.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/cheshirekow/cmake_format", + description = "Parse cmake listfiles and format them nicely.", + }, + command = "cmake-format", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/codespell.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/codespell.lua new file mode 100644 index 00000000..88e01091 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/codespell.lua @@ -0,0 +1,15 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/codespell-project/codespell", + description = "Check code for common misspellings.", + }, + command = "codespell", + stdin = false, + args = { + "$FILENAME", + "--write-changes", + "--check-hidden", -- conform's temp file is hidden + }, + exit_codes = { 0, 65 }, -- code 65 is given when trying to format an ambiguous misspelling +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/crystal.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/crystal.lua new file mode 100644 index 00000000..1f9e12bd --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/crystal.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://crystal-lang.org/", + description = "Format Crystal code.", + }, + command = "crystal", + args = { "tool", "format", "-" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/csharpier.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/csharpier.lua new file mode 100644 index 00000000..0da7da5d --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/csharpier.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/belav/csharpier", + description = "The opinionated C# code formatter.", + }, + command = "dotnet-csharpier", + args = { "--write-stdout" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cue_fmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cue_fmt.lua new file mode 100644 index 00000000..e9787d68 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/cue_fmt.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://cuelang.org", + description = "Format CUE files using `cue fmt` command.", + }, + command = "cue", + args = { "fmt", "-" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/darker.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/darker.lua new file mode 100644 index 00000000..9fe9b205 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/darker.lua @@ -0,0 +1,35 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/akaihola/darker", + description = "Run black only on changed lines.", + }, + command = "darker", + args = function(self, ctx) + -- make sure pre-save doesn't lose changes while post-save respects + -- the revision setting potentially set in pyproject.toml + if vim.bo[ctx.buf].modified then + return { + "--quiet", + "--no-color", + "--stdout", + "--revision", + "HEAD..:STDIN:", + "--stdin-filename", + "$FILENAME", + } + else + return { + "--quiet", + "--no-color", + "--stdout", + "$FILENAME", + } + end + end, + cwd = util.root_file({ + -- https://github.com/akaihola/darker#customizing-darker-black-isort-flynt-and-linter-behavior + "pyproject.toml", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/dart_format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/dart_format.lua new file mode 100644 index 00000000..e110b06e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/dart_format.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://dart.dev/tools/dart-format", + description = "Replace the whitespace in your program with formatting that follows Dart guidelines.", + }, + command = "dart", + args = { "format" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/deno_fmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/deno_fmt.lua new file mode 100644 index 00000000..5e7f4523 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/deno_fmt.lua @@ -0,0 +1,25 @@ +local extensions = { + javascript = "js", + javascriptreact = "jsx", + json = "json", + jsonc = "jsonc", + markdown = "md", + typescript = "ts", + typescriptreact = "tsx", +} +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://deno.land/manual/tools/formatter", + description = "Use [Deno](https://deno.land/) to format TypeScript, JavaScript/JSON and markdown.", + }, + command = "deno", + args = function(self, ctx) + return { + "fmt", + "-", + "--ext", + extensions[vim.bo[ctx.buf].filetype], + } + end, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/dfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/dfmt.lua new file mode 100644 index 00000000..41dd667e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/dfmt.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/dlang-community/dfmt", + description = "Formatter for D source code.", + }, + command = "dfmt", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/djlint.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/djlint.lua new file mode 100644 index 00000000..03922bdd --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/djlint.lua @@ -0,0 +1,16 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/Riverside-Healthcare/djLint", + description = "✨ HTML Template Linter and Formatter. Django - Jinja - Nunjucks - Handlebars - GoLang.", + }, + command = "djlint", + args = { + "--reformat", + "-", + }, + cwd = util.root_file({ + ".djlintrc", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/dprint.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/dprint.lua new file mode 100644 index 00000000..8c792216 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/dprint.lua @@ -0,0 +1,16 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/dprint/dprint", + description = "Pluggable and configurable code formatting platform written in Rust.", + }, + command = "dprint", + args = { "fmt", "--stdin", "$FILENAME" }, + cwd = util.root_file({ + "dprint.json", + ".dprint.json", + "dprint.jsonc", + ".dprint.jsonc", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/easy-coding-standard.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/easy-coding-standard.lua new file mode 100644 index 00000000..d1ed3a6c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/easy-coding-standard.lua @@ -0,0 +1,18 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/easy-coding-standard/easy-coding-standard", + description = "ecs - Use Coding Standard with 0-knowledge of PHP-CS-Fixer and PHP_CodeSniffer.", + }, + command = util.find_executable({ + "vendor/bin/ecs", + }, "ecs"), + args = { "check", "$FILENAME", "--fix", "--no-interaction" }, + cwd = util.root_file({ + "ecs.php", + }), + require_cwd = true, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/elm_format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/elm_format.lua new file mode 100644 index 00000000..5b0db5c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/elm_format.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/avh4/elm-format", + description = "elm-format formats Elm source code according to a standard set of rules based on the official [Elm Style Guide](https://elm-lang.org/docs/style-guide).", + }, + command = "elm-format", + args = { "--stdin" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/erb_format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/erb_format.lua new file mode 100644 index 00000000..65d7d10b --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/erb_format.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/nebulab/erb-formatter", + description = "Format ERB files with speed and precision.", + }, + command = "erb-format", + args = { "--stdin" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/eslint_d.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/eslint_d.lua new file mode 100644 index 00000000..e7a52277 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/eslint_d.lua @@ -0,0 +1,13 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/mantoni/eslint_d.js/", + description = "Like ESLint, but faster.", + }, + command = util.from_node_modules("eslint_d"), + args = { "--fix-to-stdout", "--stdin", "--stdin-filename", "$FILENAME" }, + cwd = util.root_file({ + "package.json", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fantomas.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fantomas.lua new file mode 100644 index 00000000..9244feb6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fantomas.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/fsprojects/fantomas", + description = "F# source code formatter.", + }, + command = "fantomas", + args = { "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fish_indent.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fish_indent.lua new file mode 100644 index 00000000..7e10ed47 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fish_indent.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://fishshell.com/docs/current/cmds/fish_indent.html", + description = "Indent or otherwise prettify a piece of fish code.", + }, + command = "fish_indent", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fixjson.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fixjson.lua new file mode 100644 index 00000000..fee611c9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fixjson.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/rhysd/fixjson", + description = "JSON Fixer for Humans using (relaxed) JSON5.", + }, + command = "fixjson", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fnlfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fnlfmt.lua new file mode 100644 index 00000000..3cc03b81 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fnlfmt.lua @@ -0,0 +1,9 @@ +--@type conform.FileFormatterConfig +return { + meta = { + url = "https://git.sr.ht/~technomancy/fnlfmt", + description = "A formatter for Fennel code.", + }, + command = "fnlfmt", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/forge_fmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/forge_fmt.lua new file mode 100644 index 00000000..c6bdafba --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/forge_fmt.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/foundry-rs/foundry", + description = "Forge is a command-line tool that ships with Foundry. Forge tests, builds, and deploys your smart contracts.", + }, + command = "forge", + args = { "fmt", "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fourmolu.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fourmolu.lua new file mode 100644 index 00000000..4e1db8ed --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/fourmolu.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://hackage.haskell.org/package/fourmolu", + description = "A fork of ormolu that uses four space indentation and allows arbitrary configuration.", + }, + command = "fourmolu", + args = { "--stdin-input-file", "$FILENAME" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gci.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gci.lua new file mode 100644 index 00000000..88293d78 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gci.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/daixiang0/gci", + description = "GCI, a tool that controls Go package import order and makes it always deterministic.", + }, + command = "gci", + args = { "write", "--skip-generated", "--skip-vendor", "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gdformat.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gdformat.lua new file mode 100644 index 00000000..d180f125 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gdformat.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/Scony/godot-gdscript-toolkit", + description = "A formatter for Godot's gdscript.", + }, + command = "gdformat", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gersemi.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gersemi.lua new file mode 100644 index 00000000..4c977170 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gersemi.lua @@ -0,0 +1,11 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/BlankSpruce/gersemi", + description = "A formatter to make your CMake code the real treasure.", + }, + command = "gersemi", + args = { "--quiet", "-" }, + cwd = util.root_file({ ".gersemirc" }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gleam.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gleam.lua new file mode 100644 index 00000000..a03afac1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gleam.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/gleam-lang/gleam", + description = "⭐️ A friendly language for building type-safe, scalable systems!", + }, + command = "gleam", + args = { "format", "--stdin" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gn.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gn.lua new file mode 100644 index 00000000..fd576d94 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gn.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://gn.googlesource.com/gn/", + description = "gn build system.", + }, + command = "gn", + args = { "format", "--stdin" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gofmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gofmt.lua new file mode 100644 index 00000000..2bfd6b4d --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gofmt.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://pkg.go.dev/cmd/gofmt", + description = "Formats go programs.", + }, + command = "gofmt", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gofumpt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gofumpt.lua new file mode 100644 index 00000000..2d4f9a5f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/gofumpt.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/mvdan/gofumpt", + description = "Enforce a stricter format than gofmt, while being backwards compatible. That is, gofumpt is happy with a subset of the formats that gofmt is happy with.", + }, + command = "gofumpt", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/goimports-reviser.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/goimports-reviser.lua new file mode 100644 index 00000000..9d28c07c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/goimports-reviser.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/incu6us/goimports-reviser", + description = "Right imports sorting & code formatting tool (goimports alternative).", + }, + command = "goimports-reviser", + args = { "-format", "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/goimports.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/goimports.lua new file mode 100644 index 00000000..64efea6a --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/goimports.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://pkg.go.dev/golang.org/x/tools/cmd/goimports", + description = "Updates your Go import lines, adding missing ones and removing unreferenced ones.", + }, + command = "goimports", + args = { "-srcdir", "$DIRNAME" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/golines.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/golines.lua new file mode 100644 index 00000000..1f33668d --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/golines.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/segmentio/golines", + description = "A golang formatter that fixes long lines.", + }, + command = "golines", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/google-java-format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/google-java-format.lua new file mode 100644 index 00000000..8bcb82a2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/google-java-format.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/google/google-java-format", + description = "Reformats Java source code according to Google Java Style.", + }, + command = "google-java-format", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/hcl.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/hcl.lua new file mode 100644 index 00000000..8cffc7fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/hcl.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/hashicorp/hcl", + description = "A formatter for HCL files.", + }, + command = "hclfmt", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/htmlbeautifier.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/htmlbeautifier.lua new file mode 100644 index 00000000..73b9275e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/htmlbeautifier.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/threedaymonk/htmlbeautifier", + description = "A normaliser/beautifier for HTML that also understands embedded Ruby. Ideal for tidying up Rails templates.", + }, + command = "htmlbeautifier", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/indent.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/indent.lua new file mode 100644 index 00000000..cb4631c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/indent.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://www.gnu.org/software/indent/", + description = "GNU Indent.", + }, + command = "indent", + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/init.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/init.lua new file mode 100644 index 00000000..237d39c0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/init.lua @@ -0,0 +1,37 @@ +local M = {} +local uv = vim.uv or vim.loop + +---@private +---This is used for documentation generation +M.list_all_formatters = function() + local ret = {} + for path in vim.gsplit(vim.o.runtimepath, ",", { plain = true }) do + local formatter_path = path .. "/lua/conform/formatters" + local formatter_dir = uv.fs_opendir(formatter_path) + if formatter_dir then + local entries = uv.fs_readdir(formatter_dir) + while entries do + for _, entry in ipairs(entries) do + if entry.name ~= "init.lua" then + local basename = string.match(entry.name, "^(.*)%.lua$") + local module = require("conform.formatters." .. basename) + local module_data = vim.deepcopy(module.meta) + module_data.has_options = module.options ~= nil + ret[basename] = module_data + end + end + entries = uv.fs_readdir(formatter_dir) + end + uv.fs_closedir(formatter_dir) + end + end + return ret +end + +-- A little metatable magic to allow accessing formatters like +-- require("conform.formatters").prettier +return setmetatable(M, { + __index = function(_, k) + return require("conform.formatters." .. k) + end, +}) diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/injected.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/injected.lua new file mode 100644 index 00000000..4dbf1eb3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/injected.lua @@ -0,0 +1,324 @@ +---@param range? conform.Range +---@param start_lnum integer +---@param end_lnum integer +---@return boolean +local function in_range(range, start_lnum, end_lnum) + return not range or (start_lnum <= range["end"][1] and range["start"][1] <= end_lnum) +end + +---@param lines string[] +---@param language? string The language of the buffer +---@return string? +local function get_indent(lines, language) + local indent = nil + -- Handle markdown code blocks that are inside blockquotes + -- > ```lua + -- > local x = 1 + -- > ``` + local pattern = language == "markdown" and "^>?%s*" or "^%s*" + for _, line in ipairs(lines) do + if line ~= "" then + local whitespace = line:match(pattern) + if whitespace == "" then + return nil + elseif not indent or whitespace:len() < indent:len() then + indent = whitespace + end + end + end + return indent +end + +---@class (exact) conform.Injected.Surrounding +---@field indent string? +---@field postfix string? + +---Remove leading indentation from lines and return the indentation string +---@param lines string[] +---@param language? string The language of the buffer +---@return conform.Injected.Surrounding +local function remove_surrounding(lines, language) + local surrounding = {} + if lines[#lines]:match("^%s*$") then + surrounding.postfix = lines[#lines] + table.remove(lines) + end + + local indent = get_indent(lines, language) + if not indent then + return surrounding + end + local sub_start = indent:len() + 1 + for i, line in ipairs(lines) do + if line ~= "" then + lines[i] = line:sub(sub_start) + end + end + surrounding.indent = indent + return surrounding +end + +---@param lines string[]? +---@param surrounding conform.Injected.Surrounding +local function restore_surrounding(lines, surrounding) + if not lines then + return + end + + local indent = surrounding.indent + if indent then + for i, line in ipairs(lines) do + if line ~= "" then + lines[i] = indent .. line + end + end + end + + local postfix = surrounding.postfix + if postfix then + table.insert(lines, postfix) + end +end + +---@class LangRange +---@field [1] string language +---@field [2] integer start lnum +---@field [3] integer start col +---@field [4] integer end lnum +---@field [5] integer end col + +---@param ranges LangRange[] +---@param range LangRange +local function accum_range(ranges, range) + local last_range = ranges[#ranges] + if last_range then + if last_range[1] == range[1] and last_range[4] == range[2] and last_range[5] == range[3] then + last_range[4] = range[4] + last_range[5] = range[5] + return + end + end + table.insert(ranges, range) +end + +---@class (exact) conform.InjectedFormatterOptions +---@field ignore_errors boolean +---@field lang_to_ext table +---@field lang_to_formatters table + +---@type conform.FileLuaFormatterConfig +return { + meta = { + url = "doc/advanced_topics.md#injected-language-formatting-code-blocks", + description = "Format treesitter injected languages.", + }, + options = { + -- Set to true to ignore errors + ignore_errors = false, + -- Map of treesitter language to file extension + -- A temporary file name with this extension will be generated during formatting + -- because some formatters care about the filename. + lang_to_ext = { + bash = "sh", + c_sharp = "cs", + elixir = "exs", + javascript = "js", + julia = "jl", + latex = "tex", + markdown = "md", + python = "py", + ruby = "rb", + rust = "rs", + teal = "tl", + typescript = "ts", + }, + -- Map of treesitter language to formatters to use + -- (defaults to the value from formatters_by_ft) + lang_to_formatters = {}, + }, + condition = function(self, ctx) + local ok, parser = pcall(vim.treesitter.get_parser, ctx.buf) + -- Require Neovim 0.9 because the treesitter API has changed significantly + ---@diagnostic disable-next-line: invisible + return ok and parser._injection_query and vim.fn.has("nvim-0.9") == 1 + end, + format = function(self, ctx, lines, callback) + local conform = require("conform") + local errors = require("conform.errors") + local log = require("conform.log") + local util = require("conform.util") + -- Need to add a trailing newline; some parsers need this. + -- For example, if a markdown code block ends at the end of the file, a trailing newline is + -- required otherwise the ``` will be grabbed as part of the injected block + local text = table.concat(lines, "\n") .. "\n" + local buf_lang = vim.treesitter.language.get_lang(vim.bo[ctx.buf].filetype) + local ok, parser = pcall(vim.treesitter.get_string_parser, text, buf_lang) + if not ok then + callback("No treesitter parser for buffer") + return + end + ---@type conform.InjectedFormatterOptions + local options = self.options + + ---@param lang string + ---@return nil|conform.FiletypeFormatter + local function get_formatters(lang) + return options.lang_to_formatters[lang] or conform.formatters_by_ft[lang] + end + + --- Disable diagnostic to pass the typecheck github action + --- This is available on nightly, but not on stable + --- Stable doesn't have any parameters, so it's safe + ---@diagnostic disable-next-line: redundant-parameter + parser:parse(true) + local root_lang = parser:lang() + ---@type LangRange[] + local regions = {} + + for lang, lang_tree in pairs(parser:children()) do + if lang ~= root_lang then + for _, ranges in ipairs(lang_tree:included_regions()) do + for _, region in ipairs(ranges) do + local formatters = get_formatters(lang) + if formatters ~= nil then + -- The types are wrong. included_regions should be Range[][] not integer[][] + ---@diagnostic disable-next-line: param-type-mismatch + local start_row, start_col, _, end_row, end_col, _ = unpack(region) + accum_range(regions, { lang, start_row + 1, start_col, end_row + 1, end_col }) + end + end + end + end + end + + if ctx.range then + regions = vim.tbl_filter(function(region) + return in_range(ctx.range, region[2], region[4]) + end, regions) + end + + -- Sort from largest start_lnum to smallest + table.sort(regions, function(a, b) + return a[2] > b[2] + end) + log.trace("Injected formatter regions %s", regions) + + local replacements = {} + local format_error = nil + + local function apply_format_results() + if format_error then + -- Find all of the conform errors in the replacements table and remove them + local i = 1 + while i <= #replacements do + if replacements[i].code then + table.remove(replacements, i) + else + i = i + 1 + end + end + if options.ignore_errors then + format_error = nil + end + end + + local formatted_lines = vim.deepcopy(lines) + for _, replacement in ipairs(replacements) do + local start_lnum, start_col, end_lnum, end_col, new_lines = unpack(replacement) + local prefix = formatted_lines[start_lnum]:sub(1, start_col) + local suffix = formatted_lines[end_lnum]:sub(end_col + 1) + new_lines[1] = prefix .. new_lines[1] + new_lines[#new_lines] = new_lines[#new_lines] .. suffix + for _ = start_lnum, end_lnum do + table.remove(formatted_lines, start_lnum) + end + for i = #new_lines, 1, -1 do + table.insert(formatted_lines, start_lnum, new_lines[i]) + end + end + callback(format_error, formatted_lines) + end + + local num_format = 0 + local tmp_bufs = {} + local formatter_cb = function(err, idx, region, input_lines, new_lines) + if err then + format_error = errors.coalesce(format_error, err) + replacements[idx] = err + else + -- If the original lines started/ended with a newline, preserve that newline. + -- Many formatters will trim them, but they're important for the document structure. + if input_lines[1] == "" and new_lines[1] ~= "" then + table.insert(new_lines, 1, "") + end + if input_lines[#input_lines] == "" and new_lines[#new_lines] ~= "" then + table.insert(new_lines, "") + end + replacements[idx] = { region[2], region[3], region[4], region[5], new_lines } + end + num_format = num_format - 1 + if num_format == 0 then + for buf in pairs(tmp_bufs) do + vim.api.nvim_buf_delete(buf, { force = true }) + end + apply_format_results() + end + end + local last_start_lnum = #lines + 1 + for i, region in ipairs(regions) do + local lang = region[1] + local start_lnum = region[2] + local start_col = region[3] + local end_lnum = region[4] + local end_col = region[5] + -- Ignore regions that overlap (contain) other regions + if end_lnum < last_start_lnum then + num_format = num_format + 1 + last_start_lnum = start_lnum + local input_lines = util.tbl_slice(lines, start_lnum, end_lnum) + input_lines[#input_lines] = input_lines[#input_lines]:sub(1, end_col) + if start_col > 0 then + input_lines[1] = input_lines[1]:sub(start_col + 1) + end + local ft_formatters = assert(get_formatters(lang)) + ---@type string[] + local formatter_names + if type(ft_formatters) == "function" then + formatter_names = ft_formatters(ctx.buf) + else + local formatters = require("conform").resolve_formatters(ft_formatters, ctx.buf, false) + formatter_names = vim.tbl_map(function(f) + return f.name + end, formatters) + end + local idx = num_format + log.debug("Injected format %s:%d:%d: %s", lang, start_lnum, end_lnum, formatter_names) + log.trace("Injected format lines %s", input_lines) + local surrounding = remove_surrounding(input_lines, buf_lang) + -- Create a temporary buffer. This is only needed because some formatters rely on the file + -- extension to determine a run mode (see https://github.com/stevearc/conform.nvim/issues/194) + -- This is using lang_to_ext to map the language name to the file extension, and falls back + -- to using the language name itself. + local extension = options.lang_to_ext[lang] or lang + local buf = + vim.fn.bufadd(string.format("%s.%d.%s", vim.api.nvim_buf_get_name(ctx.buf), i, extension)) + -- Actually load the buffer to set the buffer context which is required by some formatters such as `filetype` + vim.fn.bufload(buf) + tmp_bufs[buf] = true + local format_opts = { async = true, bufnr = buf, quiet = true } + conform.format_lines(formatter_names, input_lines, format_opts, function(err, new_lines) + log.trace("Injected %s:%d:%d formatted lines %s", lang, start_lnum, end_lnum, new_lines) + -- Preserve indentation in case the code block is indented + restore_surrounding(new_lines, surrounding) + vim.schedule_wrap(formatter_cb)(err, idx, region, input_lines, new_lines) + end) + end + end + if num_format == 0 then + apply_format_results() + end + end, + -- TODO this is kind of a hack. It's here to ensure all_support_range_formatting is set properly. + -- Should figure out a better way to do this. + range_args = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/inko.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/inko.lua new file mode 100644 index 00000000..4c501d07 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/inko.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://inko-lang.org/", + description = "A language for building concurrent software with confidence", + }, + command = "inko", + args = { "fmt", "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/isort.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/isort.lua new file mode 100644 index 00000000..72368736 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/isort.lua @@ -0,0 +1,38 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/PyCQA/isort", + description = "Python utility / library to sort imports alphabetically and automatically separate them into sections and by type.", + }, + command = "isort", + args = function(self, ctx) + -- isort doesn't do a good job of auto-detecting the line endings. + local line_ending + local file_format = vim.bo[ctx.buf].fileformat + if file_format == "dos" then + line_ending = "\r\n" + elseif file_format == "mac" then + line_ending = "\r" + else + line_ending = "\n" + end + return { + "--stdout", + "--line-ending", + line_ending, + "--filename", + "$FILENAME", + "-", + } + end, + cwd = util.root_file({ + -- https://pycqa.github.io/isort/docs/configuration/config_files.html + ".isort.cfg", + "pyproject.toml", + "setup.py", + "setup.cfg", + "tox.ini", + ".editorconfig", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/joker.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/joker.lua new file mode 100644 index 00000000..c87f4178 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/joker.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/candid82/joker", + description = "Small Clojure interpreter, linter and formatter.", + }, + command = "joker", + args = { "--format", "--write", "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/jq.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/jq.lua new file mode 100644 index 00000000..061ed032 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/jq.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/stedolan/jq", + description = "Command-line JSON processor.", + }, + command = "jq", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/jsonnetfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/jsonnetfmt.lua new file mode 100644 index 00000000..edddbb9c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/jsonnetfmt.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/google/go-jsonnet/tree/master/cmd/jsonnetfmt", + description = "jsonnetfmt is a command line tool to format jsonnet files.", + }, + command = "jsonnetfmt", + args = { "-" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/just.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/just.lua new file mode 100644 index 00000000..d1664843 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/just.lua @@ -0,0 +1,9 @@ +return { + meta = { + url = "https://github.com/casey/just", + description = "Format Justfile.", + }, + command = "just", + args = { "--fmt", "--unstable", "-f", "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ktfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ktfmt.lua new file mode 100644 index 00000000..7be580b7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ktfmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/facebook/ktfmt", + description = "Reformats Kotlin source code to comply with the common community standard conventions.", + }, + command = "ktfmt", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ktlint.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ktlint.lua new file mode 100644 index 00000000..886157fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ktlint.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://ktlint.github.io/", + description = "An anti-bikeshedding Kotlin linter with built-in formatter.", + }, + command = "ktlint", + args = { "--format", "--stdin", "--log-level=none" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/latexindent.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/latexindent.lua new file mode 100644 index 00000000..64451e9d --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/latexindent.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/cmhughes/latexindent.pl", + description = "A perl script for formatting LaTeX files that is generally included in major TeX distributions.", + }, + command = "latexindent", + args = { "-" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/leptosfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/leptosfmt.lua new file mode 100644 index 00000000..4fd2d982 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/leptosfmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/bram209/leptosfmt", + description = "A formatter for the Leptos view! macro.", + }, + command = "leptosfmt", + args = { "--stdin" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/liquidsoap-prettier.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/liquidsoap-prettier.lua new file mode 100644 index 00000000..e6ffb87e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/liquidsoap-prettier.lua @@ -0,0 +1,11 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/savonet/liquidsoap-prettier", + description = "A binary to format Liquidsoap scripts", + }, + command = util.from_node_modules("liquidsoap-prettier"), + args = { "-w", "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/markdown-toc.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/markdown-toc.lua new file mode 100644 index 00000000..a7a96945 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/markdown-toc.lua @@ -0,0 +1,15 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/jonschlinkert/markdown-toc", + description = "API and CLI for generating a markdown TOC (table of contents) for a README or any markdown files.", + }, + command = "markdown-toc", + stdin = false, + args = function(self, ctx) + -- use the indentation set in the current buffer, effectively allowing us to + -- use values from .editorconfig + local indent = vim.bo[ctx.buf].expandtab and (" "):rep(vim.bo[ctx.buf].tabstop) or "\t" + return { "--indent=" .. indent, "-i", "$FILENAME" } + end, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/markdownlint-cli2.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/markdownlint-cli2.lua new file mode 100644 index 00000000..b95c8cc9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/markdownlint-cli2.lua @@ -0,0 +1,11 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/DavidAnson/markdownlint-cli2", + description = "A fast, flexible, configuration-based command-line interface for linting Markdown/CommonMark files with the markdownlint library.", + }, + command = "markdownlint-cli2", + args = { "--fix", "$FILENAME" }, + exit_codes = { 0, 1 }, -- code 1 is returned when linting/formatter was successful and there were errors + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/markdownlint.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/markdownlint.lua new file mode 100644 index 00000000..40de29b5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/markdownlint.lua @@ -0,0 +1,11 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/DavidAnson/markdownlint", + description = "A Node.js style checker and lint tool for Markdown/CommonMark files.", + }, + command = "markdownlint", + args = { "--fix", "$FILENAME" }, + exit_codes = { 0, 1 }, -- code 1 is given when trying a file that includes non-autofixable errors + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mdformat.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mdformat.lua new file mode 100644 index 00000000..e291fafb --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mdformat.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/executablebooks/mdformat", + description = "An opinionated Markdown formatter.", + }, + command = "mdformat", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mdsf.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mdsf.lua new file mode 100644 index 00000000..98ceaefa --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mdsf.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/hougesen/mdsf", + description = "Format markdown code blocks using your favorite code formatters.", + }, + command = "mdsf", + args = { "format", "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mdslw.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mdslw.lua new file mode 100644 index 00000000..cab5347f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mdslw.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/razziel89/mdslw", + description = "Prepare your markdown for easy diff'ing by adding line breaks after every sentence.", + }, + command = "mdslw", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mix.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mix.lua new file mode 100644 index 00000000..970d0efd --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/mix.lua @@ -0,0 +1,13 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://hexdocs.pm/mix/main/Mix.Tasks.Format.html", + description = "Format Elixir files using the mix format command.", + }, + command = "mix", + args = { "format", "--stdin-filename", "$FILENAME", "-" }, + cwd = require("conform.util").root_file({ + ".formatter.exs", + "mix.exs", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/nimpretty.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/nimpretty.lua new file mode 100644 index 00000000..0cb5be2e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/nimpretty.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/nim-lang/nim", + description = "nimpretty is a Nim source code beautifier that follows the official style guide.", + }, + command = "nimpretty", + args = { "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/nixfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/nixfmt.lua new file mode 100644 index 00000000..27029d51 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/nixfmt.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/serokell/nixfmt", + description = "nixfmt is a formatter for Nix code, intended to apply a uniform style.", + }, + command = "nixfmt", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/nixpkgs_fmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/nixpkgs_fmt.lua new file mode 100644 index 00000000..9ca6a8ad --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/nixpkgs_fmt.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/nix-community/nixpkgs-fmt", + description = "nixpkgs-fmt is a Nix code formatter for nixpkgs.", + }, + command = "nixpkgs-fmt", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ocamlformat.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ocamlformat.lua new file mode 100644 index 00000000..0305083d --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ocamlformat.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/ocaml-ppx/ocamlformat", + description = "Auto-formatter for OCaml code.", + }, + command = "ocamlformat", + args = { "--enable-outside-detected-project", "--name", "$FILENAME", "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ocp-indent.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ocp-indent.lua new file mode 100644 index 00000000..ca069b9c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ocp-indent.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/OCamlPro/ocp-indent", + description = "Automatic indentation of OCaml source files.", + }, + command = "ocp-indent", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/opa_fmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/opa_fmt.lua new file mode 100644 index 00000000..b4f95dd1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/opa_fmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://www.openpolicyagent.org/docs/latest/cli/#opa-fmt", + description = "Format Rego files using `opa fmt` command.", + }, + command = "opa", + args = { "fmt" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ormolu.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ormolu.lua new file mode 100644 index 00000000..4c36929a --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ormolu.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://hackage.haskell.org/package/ormolu", + description = "A formatter for Haskell source code.", + }, + command = "ormolu", + args = { "--stdin-input-file", "$FILENAME" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/packer_fmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/packer_fmt.lua new file mode 100644 index 00000000..572b5f48 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/packer_fmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://developer.hashicorp.com/packer/docs/commands/fmt", + description = "The packer fmt Packer command is used to format HCL2 configuration files to a canonical format and style.", + }, + command = "packer", + args = { "fmt", "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pangu.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pangu.lua new file mode 100644 index 00000000..af10b8b9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pangu.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/vinta/pangu.py", + description = "Insert whitespace between CJK and half-width characters.", + }, + command = "pangu", + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/perlimports.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/perlimports.lua new file mode 100644 index 00000000..88405085 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/perlimports.lua @@ -0,0 +1,13 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/perl-ide/App-perlimports", + description = "Make implicit Perl imports explicit.", + }, + command = "perlimports", + args = { + "--read-stdin", + "--filename", + "$FILENAME", + }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/perltidy.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/perltidy.lua new file mode 100644 index 00000000..5f6d857c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/perltidy.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/perltidy/perltidy", + description = "Perl::Tidy, a source code formatter for Perl.", + }, + command = "perltidy", + args = { "--quiet" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pg_format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pg_format.lua new file mode 100644 index 00000000..f0164546 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pg_format.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/darold/pgFormatter", + description = "PostgreSQL SQL syntax beautifier.", + }, + command = "pg_format", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/php_cs_fixer.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/php_cs_fixer.lua new file mode 100644 index 00000000..bff5a621 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/php_cs_fixer.lua @@ -0,0 +1,16 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer", + description = "The PHP Coding Standards Fixer.", + }, + command = util.find_executable({ + "tools/php-cs-fixer/vendor/bin/php-cs-fixer", + "vendor/bin/php-cs-fixer", + }, "php-cs-fixer"), + args = { "fix", "$FILENAME" }, + stdin = false, + cwd = util.root_file({ "composer.json" }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/phpcbf.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/phpcbf.lua new file mode 100644 index 00000000..7f81b8b2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/phpcbf.lua @@ -0,0 +1,21 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://phpqa.io/projects/phpcbf.html", + description = "PHP Code Beautifier and Fixer fixes violations of a defined coding standard.", + }, + command = util.find_executable({ + "vendor/bin/phpcbf", + }, "phpcbf"), + args = { "$FILENAME" }, + stdin = false, + -- phpcbf ignores hidden files, so we have to override the default here + tmpfile_format = "conform.$RANDOM.$FILENAME", + -- 0: no errors found + -- 1: errors found + -- 2: fixable errors found + -- 3: processing error + exit_codes = { 0, 1, 2 }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/phpinsights.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/phpinsights.lua new file mode 100644 index 00000000..c2575d18 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/phpinsights.lua @@ -0,0 +1,17 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/nunomaduro/phpinsights", + description = "The perfect starting point to analyze the code quality of your PHP projects.", + }, + command = util.find_executable({ + "vendor/bin/phpinsights", + }, "phpinsights"), + args = { "fix", "$FILENAME", "--no-interaction", "--quiet" }, + cwd = util.root_file({ + "phpinsights.php", + }), + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pint.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pint.lua new file mode 100644 index 00000000..e0bffb46 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pint.lua @@ -0,0 +1,14 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/laravel/pint", + description = "Laravel Pint is an opinionated PHP code style fixer for minimalists.", + }, + command = util.find_executable({ + "vendor/bin/pint", + }, "pint"), + args = { "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/prettier.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/prettier.lua new file mode 100644 index 00000000..a72f1308 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/prettier.lua @@ -0,0 +1,79 @@ +local fs = require("conform.fs") +local util = require("conform.util") + +--- Helper function to parse options to into a parser if available +---@param self conform.JobFormatterConfig +---@param ctx conform.Context|conform.RangeContext +---@return string[]|nil args the arguments for setting a `prettier` parser if they exist in the options, nil otherwise +local function eval_parser(self, ctx) + local ft = vim.bo[ctx.buf].filetype + local ext = vim.fn.fnamemodify(ctx.filename, ":e") + local options = self.options + local parser = options + and ( + (options.ft_parsers and options.ft_parsers[ft]) + or (options.ext_parsers and options.ext_parsers[ext]) + ) + if parser then + return { "--parser", parser } + end +end + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/prettier/prettier", + description = [[Prettier is an opinionated code formatter. It enforces a consistent style by parsing your code and re-printing it with its own rules that take the maximum line length into account, wrapping code when necessary.]], + }, + options = { + -- Use a specific prettier parser for a filetype + -- Otherwise, prettier will try to infer the parser from the file name + ft_parsers = { + -- javascript = "babel", + -- javascriptreact = "babel", + -- typescript = "typescript", + -- typescriptreact = "typescript", + -- vue = "vue", + -- css = "css", + -- scss = "scss", + -- less = "less", + -- html = "html", + -- json = "json", + -- jsonc = "json", + -- yaml = "yaml", + -- markdown = "markdown", + -- ["markdown.mdx"] = "mdx", + -- graphql = "graphql", + -- handlebars = "glimmer", + }, + -- Use a specific prettier parser for a file extension + ext_parsers = { + -- qmd = "markdown", + }, + }, + command = util.from_node_modules(fs.is_windows and "prettier.cmd" or "prettier"), + args = function(self, ctx) + return eval_parser(self, ctx) or { "--stdin-filepath", "$FILENAME" } + end, + range_args = function(self, ctx) + local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range) + local args = eval_parser(self, ctx) or { "--stdin-filepath", "$FILENAME" } + return vim.list_extend(args, { "--range-start=" .. start_offset, "--range-end=" .. end_offset }) + end, + cwd = util.root_file({ + -- https://prettier.io/docs/en/configuration.html + ".prettierrc", + ".prettierrc.json", + ".prettierrc.yml", + ".prettierrc.yaml", + ".prettierrc.json5", + ".prettierrc.js", + ".prettierrc.cjs", + ".prettierrc.mjs", + ".prettierrc.toml", + "prettier.config.js", + "prettier.config.cjs", + "prettier.config.mjs", + "package.json", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/prettierd.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/prettierd.lua new file mode 100644 index 00000000..2dcd4977 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/prettierd.lua @@ -0,0 +1,31 @@ +local fs = require("conform.fs") +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/fsouza/prettierd", + description = "prettier, as a daemon, for ludicrous formatting speed.", + }, + command = util.from_node_modules(fs.is_windows and "prettierd.cmd" or "prettierd"), + args = { "$FILENAME" }, + range_args = function(self, ctx) + local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range) + return { "$FILENAME", "--range-start=" .. start_offset, "--range-end=" .. end_offset } + end, + cwd = util.root_file({ + -- https://prettier.io/docs/en/configuration.html + ".prettierrc", + ".prettierrc.json", + ".prettierrc.yml", + ".prettierrc.yaml", + ".prettierrc.json5", + ".prettierrc.js", + ".prettierrc.cjs", + ".prettierrc.mjs", + ".prettierrc.toml", + "prettier.config.js", + "prettier.config.cjs", + "prettier.config.mjs", + "package.json", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pretty-php.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pretty-php.lua new file mode 100644 index 00000000..270a8d65 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/pretty-php.lua @@ -0,0 +1,14 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/lkrms/pretty-php", + description = "The opinionated PHP code formatter.", + }, + command = util.find_executable({ + "vendor/bin/pretty-php", + }, "pretty-php"), + args = { "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/puppet-lint.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/puppet-lint.lua new file mode 100644 index 00000000..6cc231e9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/puppet-lint.lua @@ -0,0 +1,11 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/puppetlabs/puppet-lint", + description = "Check that your Puppet manifests conform to the style guide.", + }, + command = "puppet-lint", + args = { "--fix", "$FILENAME" }, + stdin = false, + exit_codes = { 0, 1 }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/purs-tidy.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/purs-tidy.lua new file mode 100644 index 00000000..a89ed07f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/purs-tidy.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/natefaubion/purescript-tidy", + description = "A syntax tidy-upper for PureScript.", + }, + command = "purs-tidy", + args = { "format" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/reorder-python-imports.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/reorder-python-imports.lua new file mode 100644 index 00000000..e8d1c754 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/reorder-python-imports.lua @@ -0,0 +1,10 @@ +--@type conform.FileFormatterConfig- +return { + meta = { + url = "https://github.com/asottile/reorder-python-imports", + description = "Rewrites source to reorder python imports", + }, + command = "reorder-python-imports", + args = { "--exit-zero-even-if-changed", "-" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rescript-format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rescript-format.lua new file mode 100644 index 00000000..806b3805 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rescript-format.lua @@ -0,0 +1,40 @@ +-- The formatter expects one of [.res | .resi | .ml | .mli] passed as +-- the value to the '-stdin' argument. +local valid_extensions = { + res = true, + resi = true, + ml = true, + mli = true, +} + +local default_extension = "res" + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://rescript-lang.org/", + description = "The built-in ReScript formatter.", + }, + command = "rescript", + args = function(self, ctx) + local extension = vim.fn.fnamemodify(ctx.filename, ":e") + + local is_invalid_extension = valid_extensions[extension] == nil + if is_invalid_extension then + extension = default_extension + end + + return { + "format", + "-stdin", + "." .. extension, + } + end, + stdin = true, + + require_cwd = true, + cwd = require("conform.util").root_file({ + "rescript.json", + "bsconfig.json", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/roc.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/roc.lua new file mode 100644 index 00000000..49fe6194 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/roc.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/roc-lang/roc", + description = "A fast, friendly, functional language.", + }, + command = "roc", + args = { "format", "--stdin", "--stdout" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rubocop.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rubocop.lua new file mode 100644 index 00000000..c8e027c3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rubocop.lua @@ -0,0 +1,18 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/rubocop/rubocop", + description = "Ruby static code analyzer and formatter, based on the community Ruby style guide.", + }, + command = "rubocop", + args = { + "--server", + "-a", + "-f", + "quiet", + "--stderr", + "--stdin", + "$FILENAME", + }, + exit_codes = { 0, 1 }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rubyfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rubyfmt.lua new file mode 100644 index 00000000..9c513db3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rubyfmt.lua @@ -0,0 +1,7 @@ +return { + meta = { + url = "https://github.com/fables-tales/rubyfmt", + description = "Ruby Autoformatter! (Written in Rust)", + }, + command = "rubyfmt", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff.lua new file mode 100644 index 00000000..9a1d780b --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff.lua @@ -0,0 +1,4 @@ +-- This was renamed to ruff_fix +local conf = vim.deepcopy(require("conform.formatters.ruff_fix")) +conf.meta.deprecated = true +return conf diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff_fix.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff_fix.lua new file mode 100644 index 00000000..ecf3ed7e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff_fix.lua @@ -0,0 +1,24 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://docs.astral.sh/ruff/", + description = "An extremely fast Python linter, written in Rust. Fix lint errors.", + }, + command = "ruff", + args = { + "check", + "--fix", + "--force-exclude", + "--exit-zero", + "--no-cache", + "--stdin-filename", + "$FILENAME", + "-", + }, + stdin = true, + cwd = require("conform.util").root_file({ + "pyproject.toml", + "ruff.toml", + ".ruff.toml", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff_format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff_format.lua new file mode 100644 index 00000000..20a2e952 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff_format.lua @@ -0,0 +1,21 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://docs.astral.sh/ruff/", + description = "An extremely fast Python linter, written in Rust. Formatter subcommand.", + }, + command = "ruff", + args = { + "format", + "--force-exclude", + "--stdin-filename", + "$FILENAME", + "-", + }, + stdin = true, + cwd = require("conform.util").root_file({ + "pyproject.toml", + "ruff.toml", + ".ruff.toml", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff_organize_imports.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff_organize_imports.lua new file mode 100644 index 00000000..03ab8421 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/ruff_organize_imports.lua @@ -0,0 +1,25 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://docs.astral.sh/ruff/", + description = "An extremely fast Python linter, written in Rust. Organize imports.", + }, + command = "ruff", + args = { + "check", + "--fix", + "--force-exclude", + "--select=I001", + "--exit-zero", + "--no-cache", + "--stdin-filename", + "$FILENAME", + "-", + }, + stdin = true, + cwd = require("conform.util").root_file({ + "pyproject.toml", + "ruff.toml", + ".ruff.toml", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rufo.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rufo.lua new file mode 100644 index 00000000..11eb20df --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rufo.lua @@ -0,0 +1,8 @@ +return { + meta = { + url = "https://github.com/ruby-formatter/rufo", + description = "Rufo is an opinionated ruby formatter.", + }, + command = "rufo", + exit_codes = { 0, 3 }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rustfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rustfmt.lua new file mode 100644 index 00000000..e33fb862 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rustfmt.lua @@ -0,0 +1,21 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/rust-lang/rustfmt", + description = "A tool for formatting rust code according to style guidelines.", + }, + command = "rustfmt", + options = { + -- The default edition of Rust to use when no Cargo.toml file is found + default_edition = "2021", + }, + args = function(self, ctx) + local args = { "--emit=stdout" } + local edition = util.parse_rust_edition(ctx.dirname) or self.options.default_edition + table.insert(args, "--edition=" .. edition) + + return args + end, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rustywind.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rustywind.lua new file mode 100644 index 00000000..d905212a --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/rustywind.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/avencera/rustywind", + description = "A tool for formatting Tailwind CSS classes.", + }, + command = "rustywind", + args = { "--stdin" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/scalafmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/scalafmt.lua new file mode 100644 index 00000000..ea82624d --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/scalafmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/scalameta/scalafmt", + description = "Code formatter for Scala.", + }, + command = "scalafmt", + args = { "--stdin" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/shellcheck.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/shellcheck.lua new file mode 100644 index 00000000..f478d52f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/shellcheck.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/koalaman/shellcheck", + description = "A static analysis tool for shell scripts.", + }, + command = "shellcheck", + args = "'$FILENAME' --format=diff | patch -p1 '$FILENAME'", + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/shellharden.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/shellharden.lua new file mode 100644 index 00000000..c775072c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/shellharden.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/anordal/shellharden", + description = "The corrective bash syntax highlighter.", + }, + command = "shellharden", + args = { "--transform", "" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/shfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/shfmt.lua new file mode 100644 index 00000000..29b8615e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/shfmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/mvdan/sh", + description = "A shell parser, formatter, and interpreter with `bash` support.", + }, + command = "shfmt", + args = { "-filename", "$FILENAME" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/smlfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/smlfmt.lua new file mode 100644 index 00000000..babd8f06 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/smlfmt.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/shwestrick/smlfmt", + description = "A custom parser and code formatter for Standard ML.", + }, + command = "smlfmt", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/snakefmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/snakefmt.lua new file mode 100644 index 00000000..fccdeaa5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/snakefmt.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/snakemake/snakefmt", + description = "a formatting tool for Snakemake files following the design of Black.", + }, + command = "snakefmt", + args = "$FILENAME", + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/sql_formatter.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/sql_formatter.lua new file mode 100644 index 00000000..9452a61c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/sql_formatter.lua @@ -0,0 +1,8 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/sql-formatter-org/sql-formatter", + description = "A whitespace formatter for different query languages.", + }, + command = "sql-formatter", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/sqlfluff.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/sqlfluff.lua new file mode 100644 index 00000000..23441c20 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/sqlfluff.lua @@ -0,0 +1,20 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/sqlfluff/sqlfluff", + description = "A modular SQL linter and auto-formatter with support for multiple dialects and templated code.", + }, + command = "sqlfluff", + args = { "fix", "--dialect=ansi", "-" }, + stdin = true, + cwd = util.root_file({ + ".sqlfluff", + "pep8.ini", + "pyproject.toml", + "setup.cfg", + "tox.ini", + }), + require_cwd = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/sqlfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/sqlfmt.lua new file mode 100644 index 00000000..83bcad39 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/sqlfmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://docs.sqlfmt.com", + description = "sqlfmt formats your dbt SQL files so you don't have to. It is similar in nature to Black, gofmt, and rustfmt (but for SQL)", + }, + command = "sqlfmt", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/squeeze_blanks.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/squeeze_blanks.lua new file mode 100644 index 00000000..591d5427 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/squeeze_blanks.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation", + description = "Squeeze repeated blank lines into a single blank line via `cat -s`.", + }, + command = "cat", + args = { "-s" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/standardjs.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/standardjs.lua new file mode 100644 index 00000000..365e738c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/standardjs.lua @@ -0,0 +1,11 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://standardjs.com", + description = "JavaScript Standard style guide, linter, and formatter.", + }, + command = util.from_node_modules("standard"), + args = { "--fix", "--stdin" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/standardrb.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/standardrb.lua new file mode 100644 index 00000000..ae860ea2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/standardrb.lua @@ -0,0 +1,17 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/standardrb/standard", + description = "Ruby's bikeshed-proof linter and formatter.", + }, + command = "standardrb", + args = { + "--fix", + "-f", + "quiet", + "--stderr", + "--stdin", + "$FILENAME", + }, + exit_codes = { 0, 1 }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/stylelint.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/stylelint.lua new file mode 100644 index 00000000..e102d84c --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/stylelint.lua @@ -0,0 +1,11 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/stylelint/stylelint", + description = "A mighty CSS linter that helps you avoid errors and enforce conventions.", + }, + command = "stylelint", + args = { "--stdin", "--fix" }, + exit_codes = { 0, 2 }, -- code 2 is given when trying file includees some non-autofixable errors + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/styler.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/styler.lua new file mode 100644 index 00000000..d9052c1f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/styler.lua @@ -0,0 +1,11 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/devOpifex/r.nvim", + description = "R formatter and linter.", + }, + command = util.find_executable({ "usr/bin/" }, "R"), + args = { "-s", "-e", "r.nvim::format()", "--args", "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/stylua.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/stylua.lua new file mode 100644 index 00000000..25012bfc --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/stylua.lua @@ -0,0 +1,27 @@ +local util = require("conform.util") +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/JohnnyMorganz/StyLua", + description = "An opinionated code formatter for Lua.", + }, + command = "stylua", + args = { "--search-parent-directories", "--stdin-filepath", "$FILENAME", "-" }, + range_args = function(self, ctx) + local start_offset, end_offset = util.get_offsets_from_range(ctx.buf, ctx.range) + return { + "--search-parent-directories", + "--stdin-filepath", + "$FILENAME", + "--range-start", + tostring(start_offset), + "--range-end", + tostring(end_offset), + "-", + } + end, + cwd = util.root_file({ + ".stylua.toml", + "stylua.toml", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/swift_format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/swift_format.lua new file mode 100644 index 00000000..1473c194 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/swift_format.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/apple/swift-format", + description = "Swift formatter from apple. Requires building from source with `swift build`.", + }, + command = "swift-format", + args = { "$FILENAME", "--in-place" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/swiftformat.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/swiftformat.lua new file mode 100644 index 00000000..adde245e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/swiftformat.lua @@ -0,0 +1,20 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/nicklockwood/SwiftFormat", + description = "SwiftFormat is a code library and command-line tool for reformatting `swift` code on macOS or Linux.", + }, + command = "swiftformat", + stdin = true, + args = { "--stdinpath", "$FILENAME" }, + range_args = function(self, ctx) + local startOffset = tonumber(ctx.range.start[1]) - 1 + local endOffset = tonumber(ctx.range["end"][1]) - 1 + + return { + "--linerange", + startOffset .. "," .. endOffset, + } + end, + cwd = require("conform.util").root_file({ ".swiftformat", "Package.swift", "buildServer.json" }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/taplo.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/taplo.lua new file mode 100644 index 00000000..fb2d67ac --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/taplo.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/tamasfe/taplo", + description = "A TOML toolkit written in Rust.", + }, + command = "taplo", + args = { "format", "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/templ.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/templ.lua new file mode 100644 index 00000000..74c6f7a5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/templ.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://templ.guide/commands-and-tools/cli/#formatting-templ-files", + description = "Formats templ template files.", + }, + command = "templ", + args = { "fmt" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/terraform_fmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/terraform_fmt.lua new file mode 100644 index 00000000..4bfd2f50 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/terraform_fmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://www.terraform.io/docs/cli/commands/fmt.html", + description = "The terraform-fmt command rewrites `terraform` configuration files to a canonical format and style.", + }, + command = "terraform", + args = { "fmt", "-no-color", "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/terragrunt_hclfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/terragrunt_hclfmt.lua new file mode 100644 index 00000000..71807466 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/terragrunt_hclfmt.lua @@ -0,0 +1,13 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://terragrunt.gruntwork.io/docs/reference/cli-options/#hclfmt", + description = "Format hcl files into a canonical format.", + }, + command = "terragrunt", + args = { "hclfmt", "--terragrunt-hclfmt-file", "$FILENAME" }, + stdin = false, + condition = function(self, ctx) + return vim.fs.basename(ctx.filename) ~= "terragrunt.hcl" + end, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/tlint.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/tlint.lua new file mode 100644 index 00000000..b1f34e6f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/tlint.lua @@ -0,0 +1,14 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/tighten/tlint", + description = "Tighten linter for Laravel conventions with support for auto-formatting.", + }, + command = util.find_executable({ + "vendor/bin/tlint", + }, "tlint"), + args = { "format", "$FILENAME" }, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/tofu_fmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/tofu_fmt.lua new file mode 100644 index 00000000..00a21082 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/tofu_fmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://opentofu.org/docs/cli/commands/fmt/", + description = "The tofu-fmt command rewrites OpenTofu configuration files to a canonical format and style.", + }, + command = "tofu", + args = { "fmt", "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/trim_newlines.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/trim_newlines.lua new file mode 100644 index 00000000..434f35bf --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/trim_newlines.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://www.gnu.org/software/gawk/manual/gawk.html", + description = "Trim new lines with awk.", + }, + command = "awk", + args = { 'NF{print s $0; s=""; next} {s=s ORS}' }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/trim_whitespace.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/trim_whitespace.lua new file mode 100644 index 00000000..3bbd89ea --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/trim_whitespace.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://www.gnu.org/software/gawk/manual/gawk.html", + description = "Trim whitespaces with awk.", + }, + command = "awk", + args = { '{ sub(/[ \t]+$/, ""); print }' }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/twig-cs-fixer.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/twig-cs-fixer.lua new file mode 100644 index 00000000..b248e6cb --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/twig-cs-fixer.lua @@ -0,0 +1,20 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/VincentLanglet/Twig-CS-Fixer", + description = "Automatically fix Twig Coding Standards issues", + }, + command = util.find_executable({ + "vendor/bin/twig-cs-fixer", + }, "twig-cs-fixer"), + args = { "lint", "$FILENAME", "--fix", "--no-interaction", "--quiet" }, + cwd = util.root_file({ + ".twig-cs-fixer.php", + ".twig-cs-fixer.dist.php", + "composer.json", + }), + require_cwd = false, + stdin = false, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/typos.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/typos.lua new file mode 100644 index 00000000..c5716295 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/typos.lua @@ -0,0 +1,17 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/crate-ci/typos", + description = "Source code spell checker", + }, + command = "typos", + -- cannot use stdin, as otherwise `typos` has no information on the filename, + -- making excluded-file-configs ineffective + stdin = false, + args = { + "--write-changes", + "--force-exclude", -- so excluded files in the config take effect + "$FILENAME", + }, + exit_codes = { 0, 2 }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/typstfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/typstfmt.lua new file mode 100644 index 00000000..e5d0e5fd --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/typstfmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/astrale-sharp/typstfmt", + description = "Basic formatter for the Typst language with a future!", + }, + command = "typstfmt", + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/typstyle.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/typstyle.lua new file mode 100644 index 00000000..eaae7b04 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/typstyle.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/Enter-tainer/typstyle", + description = "Beautiful and reliable typst code formatter.", + }, + command = "typstyle", + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/uncrustify.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/uncrustify.lua new file mode 100644 index 00000000..aec93c26 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/uncrustify.lua @@ -0,0 +1,11 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/uncrustify/uncrustify", + description = "A source code beautifier for C, C++, C#, ObjectiveC, D, Java, Pawn and Vala.", + }, + command = "uncrustify", + args = function(self, ctx) + return { "-q", "-l", vim.bo[ctx.buf].filetype:upper() } + end, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/usort.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/usort.lua new file mode 100644 index 00000000..f2a141ab --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/usort.lua @@ -0,0 +1,15 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/facebook/usort", + description = "Safe, minimal import sorting for Python projects.", + }, + command = "usort", + args = { "format", "-" }, + stdin = true, + cwd = util.root_file({ + "pyproject.toml", + }), +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/verible.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/verible.lua new file mode 100644 index 00000000..6a58d76f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/verible.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/chipsalliance/verible/blob/master/verilog/tools/formatter/README.md", + description = "The SystemVerilog formatter.", + }, + command = "verible-verilog-format", + args = { "--stdin_name", "$FILENAME", "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/xmlformat.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/xmlformat.lua new file mode 100644 index 00000000..d04c9cce --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/xmlformat.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/pamoller/xmlformatter", + description = "xmlformatter is an Open Source Python package, which provides formatting of XML documents.", + }, + command = "xmlformat", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/xmllint.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/xmllint.lua new file mode 100644 index 00000000..3e06400f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/xmllint.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "http://xmlsoft.org/xmllint.html", + description = "Despite the name, xmllint can be used to format XML files as well as lint them.", + }, + command = "xmllint", + args = { "--format", "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yamlfix.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yamlfix.lua new file mode 100644 index 00000000..75923400 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yamlfix.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/lyz-code/yamlfix", + description = "A configurable YAML formatter that keeps comments.", + }, + command = "yamlfix", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yamlfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yamlfmt.lua new file mode 100644 index 00000000..8f188ab7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yamlfmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/google/yamlfmt", + description = "yamlfmt is an extensible command line tool or library to format yaml files.", + }, + command = "yamlfmt", + args = { "-" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yapf.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yapf.lua new file mode 100644 index 00000000..63bff837 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yapf.lua @@ -0,0 +1,12 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/google/yapf", + description = "Yet Another Python Formatter.", + }, + command = "yapf", + args = { "--quiet" }, + range_args = function(self, ctx) + return { "--quiet", "--lines", string.format("%d-%d", ctx.range.start[1], ctx.range["end"][1]) } + end, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yew-fmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yew-fmt.lua new file mode 100644 index 00000000..f441fdff --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yew-fmt.lua @@ -0,0 +1,21 @@ +local util = require("conform.util") + +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/schvv31n/yew-fmt", + description = "Code formatter for the Yew framework.", + }, + command = "yew-fmt", + options = { + -- The default edition of Rust to use when no Cargo.toml file is found + default_edition = "2021", + }, + args = function(self, ctx) + local args = { "--emit=stdout" } + local edition = util.parse_rust_edition(ctx.dirname) or self.options.default_edition + table.insert(args, "--edition=" .. edition) + + return args + end, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yq.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yq.lua new file mode 100644 index 00000000..29cf9b46 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/yq.lua @@ -0,0 +1,10 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/mikefarah/yq", + description = "YAML/JSON processor", + }, + command = "yq", + args = { "-P", "-" }, + stdin = true, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/zigfmt.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/zigfmt.lua new file mode 100644 index 00000000..8097d9c3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/zigfmt.lua @@ -0,0 +1,9 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/ziglang/zig", + description = "Reformat Zig source into canonical form.", + }, + command = "zig", + args = { "fmt", "--stdin" }, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/zprint.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/zprint.lua new file mode 100644 index 00000000..240b7ed0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/formatters/zprint.lua @@ -0,0 +1,17 @@ +---@type conform.FileFormatterConfig +return { + meta = { + url = "https://github.com/kkinnear/zprint", + description = "Formatter for Clojure and EDN.", + }, + command = "zprint", + range_args = function(self, ctx) + return { + string.format( + "{:input {:range {:start %d :end %d :use-previous-!zprint? true :continue-after-!zprint-error? true}}}", + ctx.range.start[1] - 1, + ctx.range["end"][1] - 1 + ), + } + end, +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/fs.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/fs.lua new file mode 100644 index 00000000..d3b5fed9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/fs.lua @@ -0,0 +1,94 @@ +local M = {} + +local uv = vim.uv or vim.loop + +---@type boolean +M.is_windows = uv.os_uname().version:match("Windows") + +M.is_mac = uv.os_uname().sysname == "Darwin" + +---@type string +M.sep = M.is_windows and "\\" or "/" + +---@param ... string +M.join = function(...) + return table.concat({ ... }, M.sep) +end + +M.is_absolute = function(path) + if M.is_windows then + return path:lower():match("^%a:") + else + return vim.startswith(path, "/") + end +end + +M.abspath = function(path) + if not M.is_absolute(path) then + path = vim.fn.fnamemodify(path, ":p") + end + return path +end + +---Returns true if candidate is a subpath of root, or if they are the same path. +---@param root string +---@param candidate string +---@return boolean +M.is_subpath = function(root, candidate) + if candidate == "" then + return false + end + root = vim.fs.normalize(M.abspath(root)) + -- Trim trailing "/" from the root + if root:find("/", -1) then + root = root:sub(1, -2) + end + candidate = vim.fs.normalize(M.abspath(candidate)) + if M.is_windows then + root = root:lower() + candidate = candidate:lower() + end + if root == candidate then + return true + end + local prefix = candidate:sub(1, root:len()) + if prefix ~= root then + return false + end + + local candidate_starts_with_sep = candidate:find("/", root:len() + 1, true) == root:len() + 1 + local root_ends_with_sep = root:find("/", root:len(), true) == root:len() + + return candidate_starts_with_sep or root_ends_with_sep +end + +---Create a relative path from the source to the target +---@param source string +---@param target string +---@return string +M.relative_path = function(source, target) + source = M.abspath(source) + target = M.abspath(target) + local path = {} + while not M.is_subpath(source, target) do + table.insert(path, "..") + local new_source = vim.fs.dirname(source) + + -- If source is a root directory, we can't go up further so there is no relative path to the + -- target. This should only happen on Windows, which prohibits relative paths between drives. + if source == new_source then + local log = require("conform.log") + log.warn("Could not find relative path from %s to %s", source, target) + return target + end + + source = new_source + end + + local offset = vim.endswith(source, M.sep) and 1 or 2 + local rel_target = target:sub(source:len() + offset) + table.insert(path, rel_target) + return M.join(unpack(path)) +end + +return M diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/health.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/health.lua new file mode 100644 index 00000000..3ee75677 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/health.lua @@ -0,0 +1,210 @@ +local M = {} + +---@diagnostic disable: deprecated +-- The "report_" functions have been deprecated, so use the new ones if defined. +local health_start = vim.health.start or vim.health.report_start +local health_warn = vim.health.warn or vim.health.report_warn +local health_info = vim.health.info or vim.health.report_info +local health_ok = vim.health.ok or vim.health.report_ok +local islist = vim.islist or vim.tbl_islist + +---@param name string +---@return string[] +local function get_formatter_filetypes(name) + local conform = require("conform") + local filetypes = {} + for filetype, formatters in pairs(conform.formatters_by_ft) do + if type(formatters) == "function" then + formatters = formatters(0) + -- support the old structure where formatters could be a subkey + elseif not islist(formatters) then + vim.notify_once( + "Using deprecated structure for formatters_by_ft. See :help conform-options for details.", + vim.log.levels.ERROR + ) + ---@diagnostic disable-next-line: undefined-field + formatters = formatters.formatters + end + + for _, ft_name in ipairs(formatters) do + if type(ft_name) == "string" then + if ft_name == name then + table.insert(filetypes, filetype) + break + end + else + if vim.tbl_contains(ft_name, name) then + table.insert(filetypes, filetype) + break + end + end + end + end + return filetypes +end + +M.check = function() + local conform = require("conform") + health_start("conform.nvim report") + + local log = require("conform.log") + health_info(string.format("Log file: %s", log.get_logfile())) + + local all_formatters = conform.list_all_formatters() + for _, formatter in ipairs(all_formatters) do + if not formatter.available then + health_warn(string.format("%s unavailable: %s", formatter.name, formatter.available_msg)) + else + local filetypes = get_formatter_filetypes(formatter.name) + health_ok(string.format("%s ready (%s)", formatter.name, table.concat(filetypes, ", "))) + end + end +end + +---@param formatters conform.FormatterUnit[] +---@return string[] +local function flatten_formatters(formatters) + local flat = {} + for _, name in ipairs(formatters) do + if type(name) == "string" then + table.insert(flat, name) + else + for _, f in ipairs(flatten_formatters(name)) do + table.insert(flat, f) + end + end + end + return flat +end + +M.show_window = function() + local conform = require("conform") + local log = require("conform.log") + local lsp_format = require("conform.lsp_format") + local lines = {} + local highlights = {} + local logfile = log.get_logfile() + table.insert(lines, string.format("Log file: %s", logfile)) + table.insert(highlights, { "Title", #lines, 0, 10 }) + if vim.fn.filereadable(logfile) == 1 then + local f = io.open(logfile, "r") + if f then + local context = -1024 + -- Show more logs if the log level is set to trace. + if log.level == vim.log.levels.TRACE then + context = 3 * context + end + f:seek("end", context) + local text = f:read("*a") + f:close() + local log_lines = vim.split(text, "\r?\n", { trimempty = true }) + for i = 2, #log_lines do + table.insert(lines, string.rep(" ", 10) .. log_lines[i]) + end + end + end + table.insert(lines, "") + + ---@param formatter conform.FormatterInfo + local function append_formatter_info(formatter) + if not formatter.available then + local line = string.format("%s unavailable: %s", formatter.name, formatter.available_msg) + table.insert(lines, line) + table.insert( + highlights, + { "DiagnosticWarn", #lines, formatter.name:len(), formatter.name:len() + 12 } + ) + else + local filetypes = get_formatter_filetypes(formatter.name) + local filetypes_list = table.concat(filetypes, ", ") + local path = vim.fn.exepath(formatter.command) + local line = string.format("%s ready (%s) %s", formatter.name, filetypes_list, path) + table.insert(lines, line) + table.insert( + highlights, + { "DiagnosticInfo", #lines, formatter.name:len(), formatter.name:len() + 6 } + ) + table.insert(highlights, { + "DiagnosticInfo", + #lines, + formatter.name:len() + 7 + filetypes_list:len() + 3, + line:len(), + }) + end + end + + local seen = {} + ---@param formatters string[] + local function append_formatters(formatters) + for _, name in ipairs(formatters) do + if type(name) == "table" then + append_formatters(name) + else + seen[name] = true + local formatter = conform.get_formatter_info(name) + append_formatter_info(formatter) + end + end + end + + table.insert(lines, "Formatters for this buffer:") + table.insert(highlights, { "Title", #lines, 0, -1 }) + local lsp_clients = lsp_format.get_format_clients({ bufnr = vim.api.nvim_get_current_buf() }) + local has_lsp_formatter = not vim.tbl_isempty(lsp_clients) + if has_lsp_formatter then + table.insert(lines, "LSP: " .. table.concat( + vim.tbl_map(function(c) + return c.name + end, lsp_clients), + ", " + )) + end + local buf_formatters = flatten_formatters(conform.list_formatters_for_buffer()) + append_formatters(buf_formatters) + if vim.tbl_isempty(buf_formatters) and not has_lsp_formatter then + table.insert(lines, "") + end + + table.insert(lines, "") + table.insert(lines, "Other formatters:") + table.insert(highlights, { "Title", #lines, 0, -1 }) + for _, formatter in ipairs(conform.list_all_formatters()) do + if not seen[formatter.name] then + append_formatter_info(formatter) + end + end + + local bufnr = vim.api.nvim_create_buf(false, true) + local winid = vim.api.nvim_open_win(bufnr, true, { + relative = "editor", + border = "rounded", + width = vim.o.columns - 6, + height = vim.o.lines - 6, + col = 2, + row = 2, + style = "minimal", + }) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines) + vim.bo[bufnr].modifiable = false + vim.bo[bufnr].modified = false + vim.bo[bufnr].bufhidden = "wipe" + vim.keymap.set("n", "q", "close", { buffer = bufnr, nowait = true }) + vim.keymap.set("n", "", "close", { buffer = bufnr }) + vim.api.nvim_create_autocmd("BufLeave", { + desc = "Close info window when leaving buffer", + buffer = bufnr, + once = true, + nested = true, + callback = function() + if vim.api.nvim_win_is_valid(winid) then + vim.api.nvim_win_close(winid, true) + end + end, + }) + local ns = vim.api.nvim_create_namespace("conform") + for _, hl in ipairs(highlights) do + vim.api.nvim_buf_add_highlight(bufnr, ns, hl[1], hl[2] - 1, hl[3], hl[4]) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/init.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/init.lua new file mode 100644 index 00000000..705cf6ab --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/init.lua @@ -0,0 +1,711 @@ +---@diagnostic disable-next-line: deprecated +local islist = vim.islist or vim.tbl_islist +local M = {} + +---@type table +M.formatters_by_ft = {} + +---@type table +M.formatters = {} + +M.notify_on_error = true + +---@param opts? conform.setupOpts +M.setup = function(opts) + opts = opts or {} + + M.formatters = vim.tbl_extend("force", M.formatters, opts.formatters or {}) + M.formatters_by_ft = vim.tbl_extend("force", M.formatters_by_ft, opts.formatters_by_ft or {}) + + if opts.log_level then + require("conform.log").level = opts.log_level + end + local notify_on_error = opts.notify_on_error + if notify_on_error ~= nil then + M.notify_on_error = notify_on_error + end + + local aug = vim.api.nvim_create_augroup("Conform", { clear = true }) + if opts.format_on_save then + if type(opts.format_on_save) == "boolean" then + opts.format_on_save = {} + end + vim.api.nvim_create_autocmd("BufWritePre", { + desc = "Format on save", + pattern = "*", + group = aug, + callback = function(args) + if not vim.api.nvim_buf_is_valid(args.buf) or vim.bo[args.buf].buftype ~= "" then + return + end + local format_args, callback = opts.format_on_save, nil + if type(format_args) == "function" then + format_args, callback = format_args(args.buf) + end + if format_args then + if format_args.async then + vim.notify_once( + "Conform format_on_save cannot use async=true. Use format_after_save instead.", + vim.log.levels.ERROR + ) + end + M.format( + vim.tbl_deep_extend("force", format_args, { + buf = args.buf, + async = false, + }), + callback + ) + end + end, + }) + vim.api.nvim_create_autocmd("VimLeavePre", { + desc = "conform.nvim hack to work around Neovim bug", + pattern = "*", + group = aug, + callback = function() + -- HACK: Work around https://github.com/neovim/neovim/issues/21856 + -- causing exit code 134 on :wq + vim.cmd.sleep({ args = { "1m" } }) + end, + }) + end + + if opts.format_after_save then + if type(opts.format_after_save) == "boolean" then + opts.format_after_save = {} + end + local exit_timeout = 1000 + local num_running_format_jobs = 0 + vim.api.nvim_create_autocmd("BufWritePost", { + desc = "Format after save", + pattern = "*", + group = aug, + callback = function(args) + if + not vim.api.nvim_buf_is_valid(args.buf) + or vim.b[args.buf].conform_applying_formatting + or vim.bo[args.buf].buftype ~= "" + then + return + end + local format_args, callback = opts.format_after_save, nil + if type(format_args) == "function" then + format_args, callback = format_args(args.buf) + end + if format_args then + exit_timeout = format_args.timeout_ms or exit_timeout + num_running_format_jobs = num_running_format_jobs + 1 + if format_args.async == false then + vim.notify_once( + "Conform format_after_save cannot use async=false. Use format_on_save instead.", + vim.log.levels.ERROR + ) + end + M.format( + vim.tbl_deep_extend("force", format_args, { + buf = args.buf, + async = true, + }), + function(err) + num_running_format_jobs = num_running_format_jobs - 1 + if not err and vim.api.nvim_buf_is_valid(args.buf) then + vim.api.nvim_buf_call(args.buf, function() + vim.b[args.buf].conform_applying_formatting = true + vim.cmd.update() + vim.b[args.buf].conform_applying_formatting = false + end) + end + if callback then + callback(err) + end + end + ) + end + end, + }) + + vim.api.nvim_create_autocmd("BufWinLeave", { + desc = "conform.nvim store changedtick for use during Neovim exit", + pattern = "*", + group = aug, + callback = function(args) + -- We store this because when vim is exiting it will set changedtick = -1 for visible + -- buffers right after firing BufWinLeave + vim.b[args.buf].last_changedtick = vim.api.nvim_buf_get_changedtick(args.buf) + end, + }) + + vim.api.nvim_create_autocmd("VimLeavePre", { + desc = "conform.nvim wait for running formatters before exit", + pattern = "*", + group = aug, + callback = function() + if num_running_format_jobs == 0 then + return + end + local uv = vim.uv or vim.loop + local start = uv.hrtime() / 1e6 + vim.wait(exit_timeout, function() + return num_running_format_jobs == 0 + end, 10) + local elapsed = uv.hrtime() / 1e6 - start + if elapsed > 200 then + local log = require("conform.log") + log.warn("Delayed Neovim exit by %dms to wait for formatting to complete", elapsed) + end + -- HACK: Work around https://github.com/neovim/neovim/issues/21856 + -- causing exit code 134 on :wq + vim.cmd.sleep({ args = { "1m" } }) + end, + }) + end + + vim.api.nvim_create_user_command("ConformInfo", function() + require("conform.health").show_window() + end, { desc = "Show information about Conform formatters" }) +end + +---Get the configured formatter filetype for a buffer +---@param bufnr? integer +---@return nil|string filetype or nil if no formatter is configured +local function get_matching_filetype(bufnr) + if not bufnr or bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local filetypes = vim.split(vim.bo[bufnr].filetype, ".", { plain = true }) + table.insert(filetypes, "_") + for _, filetype in ipairs(filetypes) do + local ft_formatters = M.formatters_by_ft[filetype] + if ft_formatters then + return filetype + end + end +end + +---@private +---@param bufnr? integer +---@return conform.FormatterUnit[] +M.list_formatters_for_buffer = function(bufnr) + if not bufnr or bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local formatters = {} + local seen = {} + + local function dedupe_formatters(names, collect) + for _, name in ipairs(names) do + if type(name) == "table" then + local alternation = {} + dedupe_formatters(name, alternation) + if not vim.tbl_isempty(alternation) then + table.insert(collect, alternation) + end + elseif not seen[name] then + table.insert(collect, name) + seen[name] = true + end + end + end + + local filetypes = {} + local matching_filetype = get_matching_filetype(bufnr) + if matching_filetype then + table.insert(filetypes, matching_filetype) + end + table.insert(filetypes, "*") + for _, ft in ipairs(filetypes) do + local ft_formatters = M.formatters_by_ft[ft] + if ft_formatters then + if type(ft_formatters) == "function" then + dedupe_formatters(ft_formatters(bufnr), formatters) + else + -- support the old structure where formatters could be a subkey + if not islist(ft_formatters) then + vim.notify_once( + "Using deprecated structure for formatters_by_ft. See :help conform-options for details.", + vim.log.levels.ERROR + ) + ---@diagnostic disable-next-line: undefined-field + ft_formatters = ft_formatters.formatters + end + + dedupe_formatters(ft_formatters, formatters) + end + end + end + + return formatters +end + +---@param bufnr integer +---@param mode "v"|"V" +---@return table {start={row,col}, end={row,col}} using (1, 0) indexing +local function range_from_selection(bufnr, mode) + -- [bufnum, lnum, col, off]; both row and column 1-indexed + local start = vim.fn.getpos("v") + local end_ = vim.fn.getpos(".") + local start_row = start[2] + local start_col = start[3] + local end_row = end_[2] + local end_col = end_[3] + + -- A user can start visual selection at the end and move backwards + -- Normalize the range to start < end + if start_row == end_row and end_col < start_col then + end_col, start_col = start_col, end_col + elseif end_row < start_row then + start_row, end_row = end_row, start_row + start_col, end_col = end_col, start_col + end + if mode == "V" then + start_col = 1 + local lines = vim.api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true) + end_col = #lines[1] + end + return { + ["start"] = { start_row, start_col - 1 }, + ["end"] = { end_row, end_col - 1 }, + } +end + +---@private +---@param names conform.FormatterUnit[] +---@param bufnr integer +---@param warn_on_missing boolean +---@return conform.FormatterInfo[] +M.resolve_formatters = function(names, bufnr, warn_on_missing) + local all_info = {} + local function add_info(info, warn) + if info.available then + table.insert(all_info, info) + elseif warn then + vim.notify( + string.format("Formatter '%s' unavailable: %s", info.name, info.available_msg), + vim.log.levels.WARN + ) + end + return info.available + end + + for _, name in ipairs(names) do + if type(name) == "string" then + local info = M.get_formatter_info(name, bufnr) + add_info(info, warn_on_missing) + else + -- If this is an alternation, take the first one that's available + for i, v in ipairs(name) do + local info = M.get_formatter_info(v, bufnr) + if add_info(info, warn_on_missing and i == #name) then + break + end + end + end + end + return all_info +end + +---@class conform.FormatOpts +---@field timeout_ms nil|integer Time in milliseconds to block for formatting. Defaults to 1000. No effect if async = true. +---@field bufnr nil|integer Format this buffer (default 0) +---@field async nil|boolean If true the method won't block. Defaults to false. If the buffer is modified before the formatter completes, the formatting will be discarded. +---@field dry_run nil|boolean If true don't apply formatting changes to the buffer +---@field formatters nil|string[] List of formatters to run. Defaults to all formatters for the buffer filetype. +---@field lsp_fallback nil|boolean|"always" Attempt LSP formatting if no formatters are available. Defaults to false. If "always", will attempt LSP formatting even if formatters are available. +---@field quiet nil|boolean Don't show any notifications for warnings or failures. Defaults to false. +---@field range nil|table Range to format. Table must contain `start` and `end` keys with {row, col} tuples using (1,0) indexing. Defaults to current selection in visual mode +---@field id nil|integer Passed to |vim.lsp.buf.format| when lsp_fallback = true +---@field name nil|string Passed to |vim.lsp.buf.format| when lsp_fallback = true +---@field filter nil|fun(client: table): boolean Passed to |vim.lsp.buf.format| when lsp_fallback = true + +---Format a buffer +---@param opts? conform.FormatOpts +---@param callback? fun(err: nil|string, did_edit: nil|boolean) Called once formatting has completed +---@return boolean True if any formatters were attempted +M.format = function(opts, callback) + ---@type {timeout_ms: integer, bufnr: integer, async: boolean, dry_run: boolean, lsp_fallback: boolean|"always", quiet: boolean, formatters?: string[], range?: conform.Range} + opts = vim.tbl_extend("keep", opts or {}, { + timeout_ms = 1000, + bufnr = 0, + async = false, + dry_run = false, + lsp_fallback = false, + quiet = false, + }) + if opts.bufnr == 0 then + opts.bufnr = vim.api.nvim_get_current_buf() + end + local mode = vim.api.nvim_get_mode().mode + if not opts.range and mode == "v" or mode == "V" then + opts.range = range_from_selection(opts.bufnr, mode) + end + callback = callback or function(_err, _did_edit) end + local errors = require("conform.errors") + local log = require("conform.log") + local lsp_format = require("conform.lsp_format") + local runner = require("conform.runner") + + local explicit_formatters = opts.formatters ~= nil + local formatter_names = opts.formatters or M.list_formatters_for_buffer(opts.bufnr) + local formatters = + M.resolve_formatters(formatter_names, opts.bufnr, not opts.quiet and explicit_formatters) + + local any_formatters = not vim.tbl_isempty(formatters) + if not explicit_formatters and opts.lsp_fallback == true and M.will_fallback_lsp(opts) then + -- use the LSP formatter when the configured formatters are from the fallback "_" filetype + any_formatters = false + else + local resolved_names = vim.tbl_map(function(f) + return f.name + end, formatters) + log.debug("Running formatters on %s: %s", vim.api.nvim_buf_get_name(opts.bufnr), resolved_names) + end + + if any_formatters then + ---@param err? conform.Error + ---@param did_edit? boolean + local function handle_result(err, did_edit) + if err then + local level = errors.level_for_code(err.code) + log.log(level, err.message) + local should_notify = not opts.quiet and level >= vim.log.levels.WARN + -- Execution errors have special handling. Maybe should reconsider this. + local notify_msg = err.message + if errors.is_execution_error(err.code) then + should_notify = should_notify and M.notify_on_error and not err.debounce_message + notify_msg = "Formatter failed. See :ConformInfo for details" + end + if should_notify then + vim.notify(notify_msg, level) + end + end + local err_message = err and err.message + if not err_message and not vim.api.nvim_buf_is_valid(opts.bufnr) then + err_message = "buffer was deleted" + end + if err_message then + return callback(err_message) + end + + if opts.dry_run and did_edit then + callback(nil, true) + elseif + opts.lsp_fallback == "always" and not vim.tbl_isempty(lsp_format.get_format_clients(opts)) + then + log.debug("Running LSP formatter on %s", vim.api.nvim_buf_get_name(opts.bufnr)) + lsp_format.format(opts, callback) + else + callback(nil, did_edit) + end + end + + local run_opts = { exclusive = true, dry_run = opts.dry_run } + if opts.async then + runner.format_async(opts.bufnr, formatters, opts.range, run_opts, handle_result) + else + local err, did_edit = + runner.format_sync(opts.bufnr, formatters, opts.timeout_ms, opts.range, run_opts) + handle_result(err, did_edit) + end + return true + elseif opts.lsp_fallback and not vim.tbl_isempty(lsp_format.get_format_clients(opts)) then + log.debug("Running LSP formatter on %s", vim.api.nvim_buf_get_name(opts.bufnr)) + lsp_format.format(opts, callback) + return true + else + local level = vim.tbl_isempty(formatter_names) and "debug" or "warn" + log[level]("No formatters found for %s", vim.api.nvim_buf_get_name(opts.bufnr)) + callback("No formatters found for buffer") + return false + end +end + +---@class conform.FormatLinesOpts +---@field timeout_ms nil|integer Time in milliseconds to block for formatting. Defaults to 1000. No effect if async = true. +---@field bufnr nil|integer use this as the working buffer (default 0) +---@field async nil|boolean If true the method won't block. Defaults to false. If the buffer is modified before the formatter completes, the formatting will be discarded. +---@field quiet nil|boolean Don't show any notifications for warnings or failures. Defaults to false. + +---Process lines with formatters +---@private +---@param formatter_names string[] +---@param lines string[] +---@param opts? conform.FormatLinesOpts +---@param callback? fun(err: nil|conform.Error, lines: nil|string[]) Called once formatting has completed +---@return nil|conform.Error error Only present if async = false +---@return nil|string[] new_lines Only present if async = false +M.format_lines = function(formatter_names, lines, opts, callback) + ---@type {timeout_ms: integer, bufnr: integer, async: boolean, quiet: boolean} + opts = vim.tbl_extend("keep", opts or {}, { + timeout_ms = 1000, + bufnr = 0, + async = false, + quiet = false, + }) + callback = callback or function(_err, _lines) end + local errors = require("conform.errors") + local log = require("conform.log") + local runner = require("conform.runner") + local formatters = M.resolve_formatters(formatter_names, opts.bufnr, not opts.quiet) + if vim.tbl_isempty(formatters) then + callback(nil, lines) + return + end + + ---@param err? conform.Error + ---@param new_lines? string[] + local function handle_err(err, new_lines) + if err then + local level = errors.level_for_code(err.code) + log.log(level, err.message) + end + callback(err, new_lines) + end + + local run_opts = { exclusive = false, dry_run = false } + if opts.async then + runner.format_lines_async(opts.bufnr, formatters, nil, lines, run_opts, handle_err) + else + local err, new_lines = + runner.format_lines_sync(opts.bufnr, formatters, opts.timeout_ms, nil, lines, run_opts) + handle_err(err, new_lines) + return err, new_lines + end +end + +---Retrieve the available formatters for a buffer +---@param bufnr? integer +---@return conform.FormatterInfo[] +M.list_formatters = function(bufnr) + if not bufnr or bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local formatters = M.list_formatters_for_buffer(bufnr) + return M.resolve_formatters(formatters, bufnr, false) +end + +---List information about all filetype-configured formatters +---@return conform.FormatterInfo[] +M.list_all_formatters = function() + local formatters = {} + for _, ft_formatters in pairs(M.formatters_by_ft) do + if type(ft_formatters) == "function" then + ft_formatters = ft_formatters(0) + end + -- support the old structure where formatters could be a subkey + if not islist(ft_formatters) then + vim.notify_once( + "Using deprecated structure for formatters_by_ft. See :help conform-options for details.", + vim.log.levels.ERROR + ) + ---@diagnostic disable-next-line: undefined-field + ft_formatters = ft_formatters.formatters + end + + for _, formatter in ipairs(ft_formatters) do + if type(formatter) == "table" then + for _, v in ipairs(formatter) do + formatters[v] = true + end + else + formatters[formatter] = true + end + end + end + + ---@type conform.FormatterInfo[] + local all_info = {} + for formatter in pairs(formatters) do + local info = M.get_formatter_info(formatter) + table.insert(all_info, info) + end + + table.sort(all_info, function(a, b) + return a.name < b.name + end) + return all_info +end + +---@private +---@param formatter string +---@param bufnr? integer +---@return nil|conform.FormatterConfig +M.get_formatter_config = function(formatter, bufnr) + if not bufnr or bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + ---@type nil|conform.FormatterConfigOverride|fun(bufnr: integer): nil|conform.FormatterConfigOverride + local override = M.formatters[formatter] + if type(override) == "function" then + override = override(bufnr) + end + if override and override.command and override.format then + local msg = + string.format("Formatter '%s' cannot define both 'command' and 'format' function", formatter) + vim.notify_once(msg, vim.log.levels.ERROR) + return nil + end + + ---@type nil|conform.FormatterConfig + local config = override + if not override or override.inherit ~= false then + local ok, mod_config = pcall(require, "conform.formatters." .. formatter) + if ok then + if override then + config = require("conform.util").merge_formatter_configs(mod_config, override) + else + config = mod_config + end + elseif override then + if override.command or override.format then + config = override + else + local msg = string.format( + "Formatter '%s' missing built-in definition\nSet `command` to get rid of this error.", + formatter + ) + vim.notify_once(msg, vim.log.levels.ERROR) + return nil + end + else + return nil + end + end + + if config and config.stdin == nil then + config.stdin = true + end + return config +end + +---Get information about a formatter (including availability) +---@param formatter string The name of the formatter +---@param bufnr? integer +---@return conform.FormatterInfo +M.get_formatter_info = function(formatter, bufnr) + if not bufnr or bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local config = M.get_formatter_config(formatter, bufnr) + if not config then + return { + name = formatter, + command = formatter, + available = false, + available_msg = "No config found", + } + end + + local ctx = require("conform.runner").build_context(bufnr, config) + + local available = true + local available_msg = nil + if config.format then + ---@cast config conform.LuaFormatterConfig + if config.condition and not config:condition(ctx) then + available = false + available_msg = "Condition failed" + end + return { + name = formatter, + command = formatter, + available = available, + available_msg = available_msg, + } + end + + local command = config.command + if type(command) == "function" then + ---@cast config conform.JobFormatterConfig + command = command(config, ctx) + end + + if vim.fn.executable(command) == 0 then + available = false + available_msg = "Command not found" + elseif config.condition and not config.condition(config, ctx) then + available = false + available_msg = "Condition failed" + end + local cwd = nil + if config.cwd then + ---@cast config conform.JobFormatterConfig + cwd = config.cwd(config, ctx) + if available and not cwd and config.require_cwd then + available = false + available_msg = "Root directory not found" + end + end + + ---@type conform.FormatterInfo + return { + name = formatter, + command = command, + cwd = cwd, + available = available, + available_msg = available_msg, + } +end + +---Check if the buffer will use LSP formatting when lsp_fallback = true +---@param options? table Options passed to |vim.lsp.buf.format| +---@return boolean +M.will_fallback_lsp = function(options) + options = vim.tbl_deep_extend("keep", options or {}, { + bufnr = vim.api.nvim_get_current_buf(), + }) + if options.bufnr == 0 then + options.bufnr = vim.api.nvim_get_current_buf() + end + local matching_filetype = get_matching_filetype(options.bufnr) + local has_primary_formatters = matching_filetype and matching_filetype ~= "_" + local lsp_clients = require("conform.lsp_format").get_format_clients(options) + return not has_primary_formatters and not vim.tbl_isempty(lsp_clients) +end + +M.formatexpr = function(opts) + local lsp_format = require("conform.lsp_format") + -- Change the defaults slightly from conform.format + opts = vim.tbl_deep_extend("keep", opts or {}, { + timeout_ms = 500, + lsp_fallback = true, + }) + -- Force async = false + opts.async = false + if vim.tbl_contains({ "i", "R", "ic", "ix" }, vim.fn.mode()) then + -- `formatexpr` is also called when exceeding `textwidth` in insert mode + -- fall back to internal formatting + return 1 + end + + local start_lnum = vim.v.lnum + local end_lnum = start_lnum + vim.v.count - 1 + + if start_lnum <= 0 or end_lnum <= 0 then + return 0 + end + local end_line = vim.fn.getline(end_lnum) + local end_col = end_line:len() + + if vim.v.count == vim.fn.line("$") then + -- Whole buffer is selected; use buffer formatting + opts.range = nil + else + opts.range = { + start = { start_lnum, 0 }, + ["end"] = { end_lnum, end_col }, + } + end + + if M.format(opts) then + return 0 + elseif opts.lsp_fallback and not vim.tbl_isempty(lsp_format.get_format_clients(opts)) then + -- No formatters were available; fall back to lsp formatter + return vim.lsp.formatexpr({ timeout_ms = opts.timeout_ms }) + else + -- Do not fallback to built-in formatter. + return 0 + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/log.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/log.lua new file mode 100644 index 00000000..79ca15a0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/log.lua @@ -0,0 +1,124 @@ +local uv = vim.uv or vim.loop +local levels_reverse = {} +for k, v in pairs(vim.log.levels) do + levels_reverse[v] = k +end + +local Log = {} + +---@type integer +Log.level = vim.log.levels.WARN + +---@return string +Log.get_logfile = function() + local fs = require("conform.fs") + + local ok, stdpath = pcall(vim.fn.stdpath, "log") + if not ok then + stdpath = vim.fn.stdpath("cache") + end + assert(type(stdpath) == "string") + return fs.join(stdpath, "conform.log") +end + +---@param level integer +---@param msg string +---@param ... any[] +---@return string +local function format(level, msg, ...) + local args = vim.F.pack_len(...) + for i = 1, args.n do + local v = args[i] + if type(v) == "table" then + args[i] = vim.inspect(v) + elseif v == nil then + args[i] = "nil" + end + end + local ok, text = pcall(string.format, msg, vim.F.unpack_len(args)) + local timestr = vim.fn.strftime("%H:%M:%S") + if ok then + local str_level = levels_reverse[level] + return string.format("%s[%s] %s", timestr, str_level, text) + else + return string.format( + "%s[ERROR] error formatting log line: '%s' args %s", + timestr, + vim.inspect(msg), + vim.inspect(args) + ) + end +end + +---@param line string +local function write(line) + -- This will be replaced during initialization +end + +local initialized = false +local function initialize() + if initialized then + return + end + initialized = true + local filepath = Log.get_logfile() + + local stat = uv.fs_stat(filepath) + if stat and stat.size > 10 * 1024 * 1024 then + local backup = filepath .. ".1" + uv.fs_unlink(backup) + uv.fs_rename(filepath, backup) + end + + local parent = vim.fs.dirname(filepath) + vim.fn.mkdir(parent, "p") + + local logfile, openerr = io.open(filepath, "a+") + if not logfile then + local err_msg = string.format("Failed to open conform.nvim log file: %s", openerr) + vim.notify(err_msg, vim.log.levels.ERROR) + else + write = function(line) + logfile:write(line) + logfile:write("\n") + logfile:flush() + end + end +end + +---Override the file handler e.g. for tests +---@param handler fun(line: string) +function Log.set_handler(handler) + write = handler + initialized = true +end + +function Log.log(level, msg, ...) + if Log.level <= level then + initialize() + local text = format(level, msg, ...) + write(text) + end +end + +function Log.trace(...) + Log.log(vim.log.levels.TRACE, ...) +end + +function Log.debug(...) + Log.log(vim.log.levels.DEBUG, ...) +end + +function Log.info(...) + Log.log(vim.log.levels.INFO, ...) +end + +function Log.warn(...) + Log.log(vim.log.levels.WARN, ...) +end + +function Log.error(...) + Log.log(vim.log.levels.ERROR, ...) +end + +return Log diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/lsp_format.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/lsp_format.lua new file mode 100644 index 00000000..a4f95567 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/lsp_format.lua @@ -0,0 +1,167 @@ +---This module replaces the default vim.lsp.buf.format() so that we can inject our own logic +local log = require("conform.log") +local util = require("vim.lsp.util") + +local M = {} + +local function apply_text_edits(text_edits, bufnr, offset_encoding, dry_run) + if + #text_edits == 1 + and text_edits[1].range.start.line == 0 + and text_edits[1].range.start.character == 0 + and text_edits[1].range["end"].line >= vim.api.nvim_buf_line_count(bufnr) + and text_edits[1].range["end"].character == 0 + then + local original_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true) + local new_lines = vim.split(text_edits[1].newText, "\r?\n", {}) + -- If it had a trailing newline, remove it to make the lines match the expected vim format + if #new_lines > 1 and new_lines[#new_lines] == "" then + table.remove(new_lines) + end + log.debug("Converting full-file LSP format to piecewise format") + return require("conform.runner").apply_format( + bufnr, + original_lines, + new_lines, + nil, + false, + dry_run + ) + elseif dry_run then + return #text_edits > 0 + else + vim.lsp.util.apply_text_edits(text_edits, bufnr, offset_encoding) + return #text_edits > 0 + end +end + +---@param options table +---@return table[] clients +function M.get_format_clients(options) + local method = options.range and "textDocument/rangeFormatting" or "textDocument/formatting" + + local clients + if vim.lsp.get_clients then + clients = vim.lsp.get_clients({ + id = options.id, + bufnr = options.bufnr, + name = options.name, + method = method, + }) + else + ---@diagnostic disable-next-line: deprecated + clients = vim.lsp.get_active_clients({ + id = options.id, + bufnr = options.bufnr, + name = options.name, + }) + + clients = vim.tbl_filter(function(client) + return client.supports_method(method, { bufnr = options.bufnr }) + end, clients) + end + if options.filter then + clients = vim.tbl_filter(options.filter, clients) + end + return clients +end + +---@param options table +---@param callback fun(err?: string, did_edit?: boolean) +function M.format(options, callback) + options = options or {} + if not options.bufnr or options.bufnr == 0 then + options.bufnr = vim.api.nvim_get_current_buf() + end + local bufnr = options.bufnr + local range = options.range + local method = range and "textDocument/rangeFormatting" or "textDocument/formatting" + + local clients = M.get_format_clients(options) + + if #clients == 0 then + return callback("[LSP] Format request failed, no matching language servers.") + end + + local function set_range(client, params) + if range then + local range_params = + util.make_given_range_params(range.start, range["end"], bufnr, client.offset_encoding) + params.range = range_params.range + end + return params + end + + if options.async then + local changedtick = vim.b[bufnr].changedtick + local do_format + local did_edit = false + do_format = function(idx, client) + if not client then + return callback(nil, did_edit) + end + local params = set_range(client, util.make_formatting_params(options.formatting_options)) + local auto_id = vim.api.nvim_create_autocmd("LspDetach", { + buffer = bufnr, + callback = function(args) + if args.data.client_id == client.id then + log.warn("LSP %s detached during format request", client.name) + callback("LSP detached") + end + end, + }) + client.request(method, params, function(err, result, ctx, _) + vim.api.nvim_del_autocmd(auto_id) + if not result then + return callback(err or "No result returned from LSP formatter") + elseif not vim.api.nvim_buf_is_valid(bufnr) then + return callback("buffer was deleted") + elseif changedtick ~= require("conform.util").buf_get_changedtick(bufnr) then + return callback( + string.format( + "Async LSP formatter discarding changes for %s: concurrent modification", + vim.api.nvim_buf_get_name(bufnr) + ) + ) + else + local this_did_edit = + apply_text_edits(result, ctx.bufnr, client.offset_encoding, options.dry_run) + changedtick = vim.b[bufnr].changedtick + + if options.dry_run and this_did_edit then + callback(nil, true) + else + did_edit = did_edit or this_did_edit + do_format(next(clients, idx)) + end + end + end, bufnr) + end + do_format(next(clients)) + else + local timeout_ms = options.timeout_ms or 1000 + local did_edit = false + for _, client in pairs(clients) do + local params = set_range(client, util.make_formatting_params(options.formatting_options)) + local result, err = client.request_sync(method, params, timeout_ms, bufnr) + if result and result.result then + local this_did_edit = + apply_text_edits(result.result, bufnr, client.offset_encoding, options.dry_run) + did_edit = did_edit or this_did_edit + + if options.dry_run and did_edit then + callback(nil, true) + return true + end + elseif err then + if not options.quiet then + vim.notify(string.format("[LSP][%s] %s", client.name, err), vim.log.levels.WARN) + end + return callback(string.format("[LSP][%s] %s", client.name, err)) + end + end + callback(nil, did_edit) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/runner.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/runner.lua new file mode 100644 index 00000000..fd998077 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/runner.lua @@ -0,0 +1,684 @@ +local errors = require("conform.errors") +local fs = require("conform.fs") +local log = require("conform.log") +local util = require("conform.util") +local uv = vim.uv or vim.loop +local M = {} + +---@class (exact) conform.RunOpts +---@field exclusive boolean If true, ensure only a single formatter is running per buffer +---@field dry_run boolean If true, do not apply changes and stop after the first formatter attempts to do so + +---@param formatter_name string +---@param ctx conform.Context +---@param config conform.JobFormatterConfig +---@return string|string[] +M.build_cmd = function(formatter_name, ctx, config) + local command = config.command + if type(command) == "function" then + command = command(config, ctx) + end + ---@type string|string[] + local args = {} + if ctx.range and config.range_args then + ---@cast ctx conform.RangeContext + args = config.range_args(config, ctx) + elseif config.args then + local computed_args = config.args + if type(computed_args) == "function" then + args = computed_args(config, ctx) + else + ---@diagnostic disable-next-line: cast-local-type + args = computed_args + end + end + + local function compute_relative_filepath() + local cwd + if config.cwd then + cwd = config.cwd(config, ctx) + end + return fs.relative_path(cwd or vim.fn.getcwd(), ctx.filename) + end + + if type(args) == "string" then + local interpolated = args + :gsub("$FILENAME", ctx.filename) + :gsub("$DIRNAME", ctx.dirname) + :gsub("$RELATIVE_FILEPATH", compute_relative_filepath) + return command .. " " .. interpolated + else + local cmd = { command } + ---@diagnostic disable-next-line: param-type-mismatch + for _, v in ipairs(args) do + if v == "$FILENAME" then + v = ctx.filename + elseif v == "$DIRNAME" then + v = ctx.dirname + elseif v == "$RELATIVE_FILEPATH" then + v = compute_relative_filepath() + end + table.insert(cmd, v) + end + return cmd + end +end + +---@param value any +---@return boolean +local function truthy(value) + return value ~= nil and value ~= false +end + +---@param range conform.Range +---@param start_a integer +---@param end_a integer +---@return boolean +local function indices_in_range(range, start_a, end_a) + return start_a <= range["end"][1] and range["start"][1] <= end_a +end + +---@param a? string +---@param b? string +---@return integer +local function common_prefix_len(a, b) + if not a or not b then + return 0 + end + local min_len = math.min(#a, #b) + for i = 1, min_len do + if string.byte(a, i) ~= string.byte(b, i) then + return i - 1 + end + end + return min_len +end + +---@param a string +---@param b string +---@return integer +local function common_suffix_len(a, b) + local a_len = #a + local b_len = #b + local min_len = math.min(a_len, b_len) + for i = 0, min_len - 1 do + if string.byte(a, a_len - i) ~= string.byte(b, b_len - i) then + return i + end + end + return min_len +end + +local function create_text_edit( + original_lines, + replacement, + is_insert, + is_replace, + orig_line_start, + orig_line_end +) + local start_line, end_line = orig_line_start - 1, orig_line_end - 1 + local start_char, end_char = 0, 0 + if is_replace then + -- If we're replacing text, see if we can avoid replacing the entire line + start_char = common_prefix_len(original_lines[orig_line_start], replacement[1]) + if start_char > 0 then + replacement[1] = replacement[1]:sub(start_char + 1) + end + + if original_lines[orig_line_end] then + local last_line = replacement[#replacement] + local suffix = common_suffix_len(original_lines[orig_line_end], last_line) + -- If we're only replacing one line, make sure the prefix/suffix calculations don't overlap + if orig_line_end == orig_line_start then + suffix = math.min(suffix, original_lines[orig_line_end]:len() - start_char) + end + end_char = original_lines[orig_line_end]:len() - suffix + if suffix > 0 then + replacement[#replacement] = last_line:sub(1, last_line:len() - suffix) + end + end + end + -- If we're inserting text, make sure the text includes a newline at the end. + -- The one exception is if we're inserting at the end of the file, in which case the newline is + -- implicit + if is_insert and start_line < #original_lines then + table.insert(replacement, "") + end + local new_text = table.concat(replacement, "\n") + + return { + newText = new_text, + range = { + start = { + line = start_line, + character = start_char, + }, + ["end"] = { + line = end_line, + character = end_char, + }, + }, + } +end + +---@param bufnr integer +---@param original_lines string[] +---@param new_lines string[] +---@param range? conform.Range +---@param only_apply_range boolean +---@return boolean any_changes +M.apply_format = function(bufnr, original_lines, new_lines, range, only_apply_range, dry_run) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + if not vim.api.nvim_buf_is_valid(bufnr) then + return false + end + local bufname = vim.api.nvim_buf_get_name(bufnr) + log.trace("Applying formatting to %s", bufname) + -- The vim.diff algorithm doesn't handle changes in newline-at-end-of-file well. The unified + -- result_type has some text to indicate that the eol changed, but the indices result_type has no + -- such indication. To work around this, we just add a trailing newline to the end of both the old + -- and the new text. + table.insert(original_lines, "") + table.insert(new_lines, "") + local original_text = table.concat(original_lines, "\n") + local new_text = table.concat(new_lines, "\n") + table.remove(original_lines) + table.remove(new_lines) + + -- Abort if output is empty but input is not (i.e. has some non-whitespace characters). + -- This is to hack around oddly behaving formatters (e.g black outputs nothing for excluded files). + if new_text:match("^%s*$") and not original_text:match("^%s*$") then + log.warn("Aborting because a formatter returned empty output for buffer %s", bufname) + return false + end + + log.trace("Comparing lines %s and %s", original_lines, new_lines) + local indices = vim.diff(original_text, new_text, { + result_type = "indices", + algorithm = "histogram", + }) + assert(type(indices) == "table") + log.trace("Diff indices %s", indices) + local text_edits = {} + for _, idx in ipairs(indices) do + local orig_line_start, orig_line_count, new_line_start, new_line_count = unpack(idx) + local is_insert = orig_line_count == 0 + local is_delete = new_line_count == 0 + local is_replace = not is_insert and not is_delete + local orig_line_end = orig_line_start + orig_line_count + local new_line_end = new_line_start + new_line_count + + if is_insert then + -- When the diff is an insert, it actually means to insert after the mentioned line + orig_line_start = orig_line_start + 1 + orig_line_end = orig_line_end + 1 + end + + local replacement = util.tbl_slice(new_lines, new_line_start, new_line_end - 1) + + -- For replacement edits, convert the end line to be inclusive + if is_replace then + orig_line_end = orig_line_end - 1 + end + local should_apply_diff = not only_apply_range + or not range + or indices_in_range(range, orig_line_start, orig_line_end) + if should_apply_diff then + local text_edit = create_text_edit( + original_lines, + replacement, + is_insert, + is_replace, + orig_line_start, + orig_line_end + ) + table.insert(text_edits, text_edit) + + -- If we're using the aftermarket range formatting, diffs often have paired delete/insert + -- diffs. We should make sure that if one of them overlaps our selected range, extend the + -- range so that we pick up the other diff as well. + if range and only_apply_range then + range = vim.deepcopy(range) + range["end"][1] = math.max(range["end"][1], orig_line_end + 1) + end + end + end + + if not dry_run then + log.trace("Applying text edits: %s", text_edits) + vim.lsp.util.apply_text_edits(text_edits, bufnr, "utf-8") + log.trace("Done formatting %s", bufname) + end + + return not vim.tbl_isempty(text_edits) +end + +---Map of formatter name to if the last run of that formatter produced an error +---@type table +local last_run_errored = {} + +---@param bufnr integer +---@param formatter conform.FormatterInfo +---@param config conform.FormatterConfig +---@param ctx conform.Context +---@param input_lines string[] +---@param opts conform.RunOpts +---@param callback fun(err?: conform.Error, output?: string[]) +---@return integer? job_id +local function run_formatter(bufnr, formatter, config, ctx, input_lines, opts, callback) + log.info("Run %s on %s", formatter.name, vim.api.nvim_buf_get_name(bufnr)) + log.trace("Input lines: %s", input_lines) + callback = util.wrap_callback(callback, function(err) + if err then + if last_run_errored[formatter.name] then + err.debounce_message = true + end + last_run_errored[formatter.name] = true + else + last_run_errored[formatter.name] = false + end + end) + if config.format then + ---@cast config conform.LuaFormatterConfig + local ok, err = pcall(config.format, config, ctx, input_lines, callback) + if not ok then + callback({ + code = errors.ERROR_CODE.RUNTIME, + message = string.format("Formatter '%s' error: %s", formatter.name, err), + }) + end + return + end + ---@cast config conform.JobFormatterConfig + local cmd = M.build_cmd(formatter.name, ctx, config) + local cwd = nil + if config.cwd then + cwd = config.cwd(config, ctx) + end + local env = config.env + if type(env) == "function" then + env = env(config, ctx) + end + + local buffer_text + -- If the buffer has a newline at the end, make sure we include that in the input to the formatter + local add_extra_newline = vim.bo[bufnr].eol + if add_extra_newline then + table.insert(input_lines, "") + end + buffer_text = table.concat(input_lines, "\n") + if add_extra_newline then + table.remove(input_lines) + end + + if not config.stdin then + log.debug("Creating temp file %s", ctx.filename) + local fd = assert(uv.fs_open(ctx.filename, "w", 448)) -- 0700 + uv.fs_write(fd, buffer_text) + uv.fs_close(fd) + callback = util.wrap_callback(callback, function() + log.debug("Cleaning up temp file %s", ctx.filename) + uv.fs_unlink(ctx.filename) + end) + end + + log.debug("Run command: %s", cmd) + if cwd then + log.debug("Run CWD: %s", cwd) + end + if env then + log.debug("Run ENV: %s", env) + end + local stdout + local stderr + local exit_codes = config.exit_codes or { 0 } + local jid + local ok, jid_or_err = pcall(vim.fn.jobstart, cmd, { + cwd = cwd, + env = env, + stdout_buffered = true, + stderr_buffered = true, + stdin = config.stdin and "pipe" or "null", + on_stdout = function(_, data) + if config.stdin then + stdout = data + end + end, + on_stderr = function(_, data) + stderr = data + end, + on_exit = function(_, code) + if vim.tbl_contains(exit_codes, code) then + local output + if not config.stdin then + local fd = assert(uv.fs_open(ctx.filename, "r", 448)) -- 0700 + local stat = assert(uv.fs_fstat(fd)) + local content = assert(uv.fs_read(fd, stat.size)) + uv.fs_close(fd) + output = vim.split(content, "\r?\n", {}) + else + output = stdout + -- trim trailing \r in every line + -- so that both branches of this if block behaves the same + for i, line in ipairs(output) do + output[i] = string.gsub(line, "\r$", "") + end + end + -- Remove the trailing newline from the output to convert back to vim lines representation + if add_extra_newline and output[#output] == "" then + table.remove(output) + end + -- Vim will never let the lines array be empty. An empty file will still look like { "" } + if #output == 0 then + table.insert(output, "") + end + log.debug("%s exited with code %d", formatter.name, code) + log.trace("Output lines: %s", output) + log.trace("%s stderr: %s", formatter.name, stderr) + callback(nil, output) + else + log.info("%s exited with code %d", formatter.name, code) + log.debug("%s stdout: %s", formatter.name, stdout) + log.debug("%s stderr: %s", formatter.name, stderr) + local err_str + if stderr and not vim.tbl_isempty(stderr) then + err_str = table.concat(stderr, "\n") + elseif stdout and not vim.tbl_isempty(stdout) then + err_str = table.concat(stdout, "\n") + end + if + vim.api.nvim_buf_is_valid(bufnr) + and jid ~= vim.b[bufnr].conform_jid + and opts.exclusive + then + callback({ + code = errors.ERROR_CODE.INTERRUPTED, + message = string.format("Formatter '%s' was interrupted", formatter.name), + }) + else + callback({ + code = errors.ERROR_CODE.RUNTIME, + message = string.format("Formatter '%s' error: %s", formatter.name, err_str), + }) + end + end + end, + }) + if not ok then + callback({ + code = errors.ERROR_CODE.JOBSTART, + message = string.format("Formatter '%s' error in jobstart: %s", formatter.name, jid_or_err), + }) + return + end + jid = jid_or_err + if jid == 0 then + callback({ + code = errors.ERROR_CODE.INVALID_ARGS, + message = string.format("Formatter '%s' invalid arguments", formatter.name), + }) + elseif jid == -1 then + callback({ + code = errors.ERROR_CODE.NOT_EXECUTABLE, + message = string.format("Formatter '%s' command is not executable", formatter.name), + }) + elseif config.stdin then + vim.api.nvim_chan_send(jid, buffer_text) + vim.fn.chanclose(jid, "stdin") + end + if opts.exclusive then + vim.b[bufnr].conform_jid = jid + end + + return jid +end + +---@param bufnr integer +---@param config conform.FormatterConfig +---@param range? conform.Range +---@return conform.Context +M.build_context = function(bufnr, config, range) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local filename = vim.api.nvim_buf_get_name(bufnr) + + -- Hack around checkhealth. For buffers that are not files, we need to fabricate a filename + if vim.bo[bufnr].buftype ~= "" then + filename = "" + end + local dirname + if filename == "" then + dirname = vim.fn.getcwd() + filename = fs.join(dirname, "unnamed_temp") + local ft = vim.bo[bufnr].filetype + if ft and ft ~= "" then + filename = filename .. "." .. ft + end + else + dirname = vim.fs.dirname(filename) + end + + if not config.stdin then + local template = config.tmpfile_format + if not template then + template = ".conform.$RANDOM.$FILENAME" + end + local basename = vim.fs.basename(filename) + local tmpname = + template:gsub("$FILENAME", basename):gsub("$RANDOM", tostring(math.random(1000000, 9999999))) + local parent = vim.fs.dirname(filename) + filename = fs.join(parent, tmpname) + end + return { + buf = bufnr, + filename = filename, + dirname = dirname, + range = range, + } +end + +---@param bufnr integer +---@param formatters conform.FormatterInfo[] +---@param range? conform.Range +---@param opts conform.RunOpts +---@param callback fun(err?: conform.Error, did_edit?: boolean) +M.format_async = function(bufnr, formatters, range, opts, callback) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + + -- kill previous jobs for buffer + local prev_jid = vim.b[bufnr].conform_jid + if prev_jid and opts.exclusive then + if vim.fn.jobstop(prev_jid) == 1 then + log.info("Canceled previous format job for %s", vim.api.nvim_buf_get_name(bufnr)) + end + end + + local original_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + local changedtick = vim.b[bufnr].changedtick + M.format_lines_async( + bufnr, + formatters, + range, + original_lines, + opts, + function(err, output_lines, all_support_range_formatting) + local did_edit = nil + -- discard formatting if buffer has changed + if not vim.api.nvim_buf_is_valid(bufnr) or changedtick ~= util.buf_get_changedtick(bufnr) then + err = { + code = errors.ERROR_CODE.CONCURRENT_MODIFICATION, + message = string.format( + "Async formatter discarding changes for %d: concurrent modification", + bufnr + ), + } + else + did_edit = M.apply_format( + bufnr, + original_lines, + output_lines, + range, + not all_support_range_formatting, + opts.dry_run + ) + end + callback(err, did_edit) + end + ) +end + +---@param bufnr integer +---@param formatters conform.FormatterInfo[] +---@param range? conform.Range +---@param input_lines string[] +---@param opts conform.RunOpts +---@param callback fun(err?: conform.Error, output_lines: string[], all_support_range_formatting: boolean) +M.format_lines_async = function(bufnr, formatters, range, input_lines, opts, callback) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local idx = 1 + local all_support_range_formatting = true + local final_err = nil + + local function run_next_formatter() + local formatter = formatters[idx] + if not formatter then + callback(final_err, input_lines, all_support_range_formatting) + return + end + idx = idx + 1 + + local config = assert(require("conform").get_formatter_config(formatter.name, bufnr)) + local ctx = M.build_context(bufnr, config, range) + run_formatter(bufnr, formatter, config, ctx, input_lines, opts, function(err, output) + if err then + final_err = errors.coalesce(final_err, err) + end + input_lines = output or input_lines + all_support_range_formatting = all_support_range_formatting and truthy(config.range_args) + run_next_formatter() + end) + end + run_next_formatter() +end + +---@param bufnr integer +---@param formatters conform.FormatterInfo[] +---@param timeout_ms integer +---@param range? conform.Range +---@param opts conform.RunOpts +---@return conform.Error? error +---@return boolean did_edit +M.format_sync = function(bufnr, formatters, timeout_ms, range, opts) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local original_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + + -- kill previous jobs for buffer + local prev_jid = vim.b[bufnr].conform_jid + if prev_jid and opts.exclusive then + if vim.fn.jobstop(prev_jid) == 1 then + log.info("Canceled previous format job for %s", vim.api.nvim_buf_get_name(bufnr)) + end + end + + local err, final_result, all_support_range_formatting = + M.format_lines_sync(bufnr, formatters, timeout_ms, range, original_lines, opts) + + local did_edit = M.apply_format( + bufnr, + original_lines, + final_result, + range, + not all_support_range_formatting, + opts.dry_run + ) + return err, did_edit +end + +---@param bufnr integer +---@param formatters conform.FormatterInfo[] +---@param timeout_ms integer +---@param range? conform.Range +---@param opts conform.RunOpts +---@return conform.Error? error +---@return string[] output_lines +---@return boolean all_support_range_formatting +M.format_lines_sync = function(bufnr, formatters, timeout_ms, range, input_lines, opts) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local start = uv.hrtime() / 1e6 + + local all_support_range_formatting = true + local final_err = nil + for _, formatter in ipairs(formatters) do + local remaining = timeout_ms - (uv.hrtime() / 1e6 - start) + if remaining <= 0 then + return errors.coalesce(final_err, { + code = errors.ERROR_CODE.TIMEOUT, + message = string.format("Formatter '%s' timeout", formatter.name), + }), + input_lines, + all_support_range_formatting + end + local done = false + local result = nil + ---@type conform.FormatterConfig + local config = assert(require("conform").get_formatter_config(formatter.name, bufnr)) + local ctx = M.build_context(bufnr, config, range) + local jid = run_formatter( + bufnr, + formatter, + config, + ctx, + input_lines, + opts, + function(err, output) + final_err = errors.coalesce(final_err, err) + done = true + result = output + end + ) + all_support_range_formatting = all_support_range_formatting and truthy(config.range_args) + + local wait_result, wait_reason = vim.wait(remaining, function() + return done + end, 5) + + if not wait_result then + if jid then + vim.fn.jobstop(jid) + end + if wait_reason == -1 then + return errors.coalesce(final_err, { + code = errors.ERROR_CODE.TIMEOUT, + message = string.format("Formatter '%s' timeout", formatter.name), + }), + input_lines, + all_support_range_formatting + else + return errors.coalesce(final_err, { + code = errors.ERROR_CODE.INTERRUPTED, + message = string.format("Formatter '%s' was interrupted", formatter.name), + }), + input_lines, + all_support_range_formatting + end + end + + input_lines = result or input_lines + end + + return final_err, input_lines, all_support_range_formatting +end + +return M diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/types.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/types.lua new file mode 100644 index 00000000..bd984269 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/types.lua @@ -0,0 +1,68 @@ +---@class (exact) conform.FormatterInfo +---@field name string +---@field command string +---@field cwd? string +---@field available boolean +---@field available_msg? string + +---@class (exact) conform.JobFormatterConfig +---@field command string|fun(self: conform.JobFormatterConfig, ctx: conform.Context): string +---@field args? string|string[]|fun(self: conform.JobFormatterConfig, ctx: conform.Context): string|string[] +---@field range_args? fun(self: conform.JobFormatterConfig, ctx: conform.RangeContext): string|string[] +---@field cwd? fun(self: conform.JobFormatterConfig, ctx: conform.Context): nil|string +---@field require_cwd? boolean When cwd is not found, don't run the formatter (default false) +---@field stdin? boolean Send buffer contents to stdin (default true) +---@field tmpfile_format? string When stdin=false, use this format for temporary files (default ".conform.$RANDOM.$FILENAME") +---@field condition? fun(self: conform.JobFormatterConfig, ctx: conform.Context): boolean +---@field exit_codes? integer[] Exit codes that indicate success (default {0}) +---@field env? table|fun(self: conform.JobFormatterConfig, ctx: conform.Context): table +---@field options? table + +---@class (exact) conform.LuaFormatterConfig +---@field format fun(self: conform.LuaFormatterConfig, ctx: conform.Context, lines: string[], callback: fun(err: nil|string, new_lines: nil|string[])) +---@field condition? fun(self: conform.LuaFormatterConfig, ctx: conform.Context): boolean +---@field options? table + +---@class (exact) conform.FileLuaFormatterConfig : conform.LuaFormatterConfig +---@field meta conform.FormatterMeta + +---@class (exact) conform.FileFormatterConfig : conform.JobFormatterConfig +---@field meta conform.FormatterMeta + +---@alias conform.FormatterConfig conform.JobFormatterConfig|conform.LuaFormatterConfig + +---@class (exact) conform.FormatterConfigOverride : conform.JobFormatterConfig +---@field inherit? boolean +---@field command? string|fun(self: conform.FormatterConfig, ctx: conform.Context): string +---@field prepend_args? string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[] +---@field format? fun(self: conform.LuaFormatterConfig, ctx: conform.Context, lines: string[], callback: fun(err: nil|string, new_lines: nil|string[])) Mutually exclusive with command +---@field options? table + +---@class (exact) conform.FormatterMeta +---@field url string +---@field description string +---@field deprecated? boolean + +---@class (exact) conform.Context +---@field buf integer +---@field filename string +---@field dirname string +---@field range? conform.Range + +---@class (exact) conform.RangeContext : conform.Context +---@field range conform.Range + +---@class (exact) conform.Range +---@field start integer[] +---@field end integer[] + +---@alias conform.FormatterUnit string|string[] +---@alias conform.FiletypeFormatter conform.FormatterUnit[]|fun(bufnr: integer): string[] + +---@class (exact) conform.setupOpts +---@field formatters_by_ft? table Map of filetype to formatters +---@field format_on_save? conform.FormatOpts|fun(bufnr: integer): conform.FormatOpts If this is set, Conform will run the formatter on save. It will pass the table to conform.format(). This can also be a function that returns the table. +---@field format_after_save? conform.FormatOpts|fun(bufnr: integer): conform.FormatOpts If this is set, Conform will run the formatter asynchronously after save. It will pass the table to conform.format(). This can also be a function that returns the table. +---@field log_level? integer Set the log level (e.g. `vim.log.levels.DEBUG`). Use `:ConformInfo` to see the location of the log file. +---@field notify_on_error? boolean Conform will notify you when a formatter errors (default true). +---@field formatters? table Custom formatters and overrides for built-in formatters. diff --git a/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/util.lua b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/util.lua new file mode 100644 index 00000000..fe2a306e --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/lua/conform/util.lua @@ -0,0 +1,207 @@ +local M = {} + +---Find a command in node_modules +---@param cmd string +---@return fun(ctx: conform.Context): string +M.from_node_modules = function(cmd) + return M.find_executable({ "node_modules/.bin/" .. cmd }, cmd) +end + +---Search parent directories for a relative path to a command +---@param paths string[] +---@param default string +---@return fun(self: conform.FormatterConfig, ctx: conform.Context): string +---@example +--- local cmd = require("conform.util").find_executable({ "node_modules/.bin/prettier" }, "prettier") +M.find_executable = function(paths, default) + return function(self, ctx) + for _, path in ipairs(paths) do + local normpath = vim.fs.normalize(path) + local is_absolute = vim.startswith(normpath, "/") + if is_absolute and vim.fn.executable(normpath) then + return normpath + end + + local idx = normpath:find("/", 1, true) + local dir, subpath + if idx then + dir = normpath:sub(1, idx - 1) + subpath = normpath:sub(idx) + else + -- This is a bare relative-path executable + dir = normpath + subpath = "" + end + local results = vim.fs.find(dir, { upward = true, path = ctx.dirname, limit = math.huge }) + for _, result in ipairs(results) do + local fullpath = result .. subpath + if vim.fn.executable(fullpath) == 1 then + return fullpath + end + end + end + + return default + end +end + +---@param files string|string[] +---@return fun(self: conform.FormatterConfig, ctx: conform.Context): nil|string +M.root_file = function(files) + return function(self, ctx) + local found = vim.fs.find(files, { upward = true, path = ctx.dirname })[1] + if found then + return vim.fs.dirname(found) + end + end +end + +---@param bufnr integer +---@param range conform.Range +---@return integer start_offset +---@return integer end_offset +M.get_offsets_from_range = function(bufnr, range) + local row = range.start[1] - 1 + local end_row = range["end"][1] - 1 + local col = range.start[2] + local end_col = range["end"][2] + local start_offset = vim.api.nvim_buf_get_offset(bufnr, row) + col + local end_offset = vim.api.nvim_buf_get_offset(bufnr, end_row) + end_col + return start_offset, end_offset +end + +---@generic T : any +---@param tbl T[] +---@param start_idx? number +---@param end_idx? number +---@return T[] +M.tbl_slice = function(tbl, start_idx, end_idx) + local ret = {} + if not start_idx then + start_idx = 1 + end + if not end_idx then + end_idx = #tbl + end + for i = start_idx, end_idx do + table.insert(ret, tbl[i]) + end + return ret +end + +---@generic T : fun() +---@param cb T +---@param wrapper T +---@return T +M.wrap_callback = function(cb, wrapper) + return function(...) + wrapper(...) + cb(...) + end +end + +---Helper function to add to the default args of a formatter. +---@param args string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[] +---@param extra_args string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[] +---@param opts? { append?: boolean } +---@example +--- local util = require("conform.util") +--- local prettier = require("conform.formatters.prettier") +--- require("conform").formatters.prettier = vim.tbl_deep_extend("force", prettier, { +--- args = util.extend_args(prettier.args, { "--tab", "--indent", "2" }), +--- range_args = util.extend_args(prettier.range_args, { "--tab", "--indent", "2" }), +--- }) +M.extend_args = function(args, extra_args, opts) + opts = opts or {} + return function(self, ctx) + if type(args) == "function" then + args = args(self, ctx) + end + if type(extra_args) == "function" then + extra_args = extra_args(self, ctx) + end + if type(args) == "string" then + if type(extra_args) ~= "string" then + extra_args = table.concat(extra_args, " ") + end + if opts.append then + return args .. " " .. extra_args + else + return extra_args .. " " .. args + end + else + if type(extra_args) == "string" then + error("extra_args must be a table when args is a table") + end + local ret = {} + if opts.append then + vim.list_extend(ret, args or {}) + vim.list_extend(ret, extra_args or {}) + else + vim.list_extend(ret, extra_args or {}) + vim.list_extend(ret, args or {}) + end + return ret + end + end +end + +---@param formatter conform.FormatterConfig +---@param extra_args string|string[]|fun(self: conform.FormatterConfig, ctx: conform.Context): string|string[] +---@param opts? { append?: boolean } +---@example +--- local util = require("conform.util") +--- local prettier = require("conform.formatters.prettier") +--- util.add_formatter_args(prettier, { "--tab", "--indent", "2" }) +M.add_formatter_args = function(formatter, extra_args, opts) + formatter.args = M.extend_args(formatter.args, extra_args, opts) + if formatter.range_args then + formatter.range_args = M.extend_args(formatter.range_args, extra_args, opts) + end +end + +---@param config conform.FormatterConfig +---@param override conform.FormatterConfigOverride +---@return conform.FormatterConfig +M.merge_formatter_configs = function(config, override) + local ret = vim.tbl_deep_extend("force", config, override) + if override.prepend_args then + M.add_formatter_args(ret, override.prepend_args, { append = false }) + end + return ret +end + +---@param bufnr integer +---@return integer +M.buf_get_changedtick = function(bufnr) + if not vim.api.nvim_buf_is_valid(bufnr) then + return -2 + end + local changedtick = vim.b[bufnr].changedtick + -- changedtick gets set to -1 when vim is exiting. We have an autocmd that should store it in + -- last_changedtick before it is set to -1. + if changedtick == -1 then + return vim.b[bufnr].last_changedtick or -1 + else + return changedtick + end +end + +---Parse the rust edition from the Cargo.toml file +---@param dir string +---@return string? +M.parse_rust_edition = function(dir) + local manifest = vim.fs.find("Cargo.toml", { upward = true, path = dir })[1] + if manifest then + for line in io.lines(manifest) do + if line:match("^edition *=") then + local edition = line:match("%d+") + if edition then + return edition + end + end + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/conform.nvim/run_tests.sh b/config/neovim/store/lazy-plugins/conform.nvim/run_tests.sh new file mode 100755 index 00000000..b8ef9ed6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/run_tests.sh @@ -0,0 +1,30 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/bash +set -e + +mkdir -p ".testenv/config/nvim" +mkdir -p ".testenv/data/nvim" +mkdir -p ".testenv/state/nvim" +mkdir -p ".testenv/run/nvim" +mkdir -p ".testenv/cache/nvim" +PLUGINS=".testenv/data/nvim/site/pack/plugins/start" + +if [ ! -e "$PLUGINS/plenary.nvim" ]; then + git clone --depth=1 https://github.com/nvim-lua/plenary.nvim.git "$PLUGINS/plenary.nvim" +else + (cd "$PLUGINS/plenary.nvim" && git pull) +fi + +if [ ! -e "$PLUGINS/nvim-treesitter" ]; then + git clone --depth=1 https://github.com/nvim-treesitter/nvim-treesitter.git "$PLUGINS/nvim-treesitter" +else + (cd "$PLUGINS/nvim-treesitter" && git pull) +fi + +XDG_CONFIG_HOME=".testenv/config" \ + XDG_DATA_HOME=".testenv/data" \ + XDG_STATE_HOME=".testenv/state" \ + XDG_RUNTIME_DIR=".testenv/run" \ + XDG_CACHE_HOME=".testenv/cache" \ + nvim --headless -u tests/minimal_init.lua \ + -c "RunTests ${1-tests}" +echo "Success" diff --git a/config/neovim/store/lazy-plugins/conform.nvim/scripts/autoformat_doc.lua b/config/neovim/store/lazy-plugins/conform.nvim/scripts/autoformat_doc.lua new file mode 100644 index 00000000..f6476d47 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/scripts/autoformat_doc.lua @@ -0,0 +1,33 @@ +-- if format_on_save is a function, it will be called during BufWritePre +require("conform").setup({ + format_on_save = function(bufnr) + -- Disable autoformat on certain filetypes + local ignore_filetypes = { "sql", "java" } + if vim.tbl_contains(ignore_filetypes, vim.bo[bufnr].filetype) then + return + end + -- Disable with a global or buffer-local variable + if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then + return + end + -- Disable autoformat for files in a certain path + local bufname = vim.api.nvim_buf_get_name(bufnr) + if bufname:match("/node_modules/") then + return + end + -- ...additional logic... + return { timeout_ms = 500, lsp_fallback = true } + end, +}) + +-- There is a similar affordance for format_after_save, which uses BufWritePost. +-- This is good for formatters that are too slow to run synchronously. +require("conform").setup({ + format_after_save = function(bufnr) + if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then + return + end + -- ...additional logic... + return { lsp_fallback = true } + end, +}) diff --git a/config/neovim/store/lazy-plugins/conform.nvim/scripts/generate.py b/config/neovim/store/lazy-plugins/conform.nvim/scripts/generate.py new file mode 100755 index 00000000..30c3b125 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/scripts/generate.py @@ -0,0 +1,217 @@ +import os +import os.path +import re +from dataclasses import dataclass +from functools import lru_cache +from typing import List + +from nvim_doc_tools import ( + Vimdoc, + VimdocSection, + dedent, + generate_md_toc, + indent, + parse_directory, + read_nvim_json, + read_section, + render_md_api2, + render_vimdoc_api2, + replace_section, + wrap, +) + +HERE = os.path.dirname(__file__) +ROOT = os.path.abspath(os.path.join(HERE, os.path.pardir)) +README = os.path.join(ROOT, "README.md") +DOC = os.path.join(ROOT, "doc") +RECIPES = os.path.join(DOC, "recipes.md") +ADVANCED = os.path.join(DOC, "advanced_topics.md") +FORMATTER_OPTIONS = os.path.join(DOC, "formatter_options.md") +VIMDOC = os.path.join(DOC, "conform.txt") +OPTIONS = os.path.join(ROOT, "scripts", "options_doc.lua") +AUTOFORMAT = os.path.join(ROOT, "scripts", "autoformat_doc.lua") + + +@dataclass +class Formatter: + name: str + description: str + url: str + has_options: bool + deprecated: bool = False + + +@lru_cache +def get_all_formatters() -> List[Formatter]: + formatters = [] + formatter_map = read_nvim_json( + 'require("conform.formatters").list_all_formatters()' + ) + for name, meta in formatter_map.items(): + formatter = Formatter(name, **meta) + if not formatter.deprecated: + formatters.append(formatter) + formatters.sort(key=lambda f: f.name) + return formatters + + +def update_formatter_list(): + formatter_lines = ["\n"] + for formatter in get_all_formatters(): + formatter_lines.append( + f"- [{formatter.name}]({formatter.url}) - {formatter.description}\n" + ) + replace_section( + README, + r"^$", + r"^$", + formatter_lines, + ) + + +def update_options(): + option_lines = ["\n", "```lua\n"] + with open(OPTIONS, "r", encoding="utf-8") as f: + option_lines.extend(f.readlines()) + option_lines.extend(["```\n", "\n"]) + replace_section( + README, + r"^$", + r"^$", + option_lines, + ) + + +def update_autocmd_md(): + example_lines = ["\n", "```lua\n"] + with open(AUTOFORMAT, "r", encoding="utf-8") as f: + example_lines.extend(f.readlines()) + example_lines.extend(["```\n", "\n"]) + replace_section( + RECIPES, + r"^$", + r"^$", + example_lines, + ) + + +def update_formatter_options_md(): + lines = ["\n"] + for formatter in get_all_formatters(): + if formatter.has_options: + lines.extend([f"## {formatter.name}\n", "\n", "```lua\n", "options = {\n"]) + formatter_file = os.path.join( + ROOT, "lua", "conform", "formatters", f"{formatter.name}.lua" + ) + code = read_section(formatter_file, r"^ options = {$", r"^ },$") + lines.extend(dedent(code, 2)) + lines.extend(["}\n", "```\n", "\n"]) + replace_section( + FORMATTER_OPTIONS, + r"^$", + r"^$", + lines, + ) + + +def add_md_link_path(path: str, lines: List[str]) -> List[str]: + ret = [] + for line in lines: + ret.append(re.sub(r"(\(#)", "(" + path + "#", line)) + return ret + + +def update_md_api(): + types = parse_directory(os.path.join(ROOT, "lua")) + funcs = types.files["conform/init.lua"].functions + lines = ["\n"] + render_md_api2(funcs, types, 3)[:-1] # trim last newline + replace_section( + README, + r"^$", + r"^$", + lines, + ) + + +def update_readme_toc(): + toc = ["\n"] + generate_md_toc(README) + ["\n"] + replace_section( + README, + r"^$", + r"^$", + toc, + ) + + +def update_recipes_toc(): + toc = ["\n"] + generate_md_toc(RECIPES) + ["\n"] + replace_section(RECIPES, r"^$", r"^$", toc) + subtoc = add_md_link_path("doc/recipes.md", toc) + replace_section(README, r"^$", r"^$", subtoc) + + +def update_advanced_toc(): + toc = ["\n"] + generate_md_toc(ADVANCED) + ["\n"] + replace_section(ADVANCED, r"^$", r"^$", toc) + subtoc = add_md_link_path("doc/advanced_topics.md", toc) + replace_section(README, r"^$", r"^$", subtoc) + + +def update_formatter_options_toc(): + toc = ["\n"] + generate_md_toc(FORMATTER_OPTIONS) + ["\n"] + replace_section(FORMATTER_OPTIONS, r"^$", r"^$", toc) + subtoc = add_md_link_path("doc/formatter_options.md", toc) + replace_section( + README, + r"^$", + r"^$", + subtoc, + ) + + +def gen_options_vimdoc() -> VimdocSection: + section = VimdocSection("Options", "conform-options", ["\n", ">lua\n"]) + with open(OPTIONS, "r", encoding="utf-8") as f: + section.body.extend(indent(f.readlines(), 4)) + section.body.append("<\n") + return section + + +def gen_formatter_vimdoc() -> VimdocSection: + section = VimdocSection("Formatters", "conform-formatters", ["\n"]) + for formatter in get_all_formatters(): + line = f"`{formatter.name}` - {formatter.description}\n" + section.body.extend(wrap(line, sub_indent=len(formatter.name) + 3)) + return section + + +def generate_vimdoc(): + doc = Vimdoc("conform.txt", "conform") + types = parse_directory(os.path.join(ROOT, "lua")) + funcs = types.files["conform/init.lua"].functions + doc.sections.extend( + [ + gen_options_vimdoc(), + VimdocSection( + "API", "conform-api", render_vimdoc_api2("conform", funcs, types) + ), + gen_formatter_vimdoc(), + ] + ) + + with open(VIMDOC, "w", encoding="utf-8") as ofile: + ofile.writelines(doc.render()) + + +def main() -> None: + """Update the README""" + update_formatter_list() + update_options() + update_autocmd_md() + update_formatter_options_md() + update_md_api() + update_recipes_toc() + update_advanced_toc() + update_formatter_options_toc() + update_readme_toc() + generate_vimdoc() diff --git a/config/neovim/store/lazy-plugins/conform.nvim/scripts/main.py b/config/neovim/store/lazy-plugins/conform.nvim/scripts/main.py new file mode 100755 index 00000000..4dffddfb --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/scripts/main.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +import argparse +import os +import sys + +HERE = os.path.dirname(__file__) +ROOT = os.path.abspath(os.path.join(HERE, os.path.pardir)) +DOC = os.path.join(ROOT, "doc") + + +def main() -> None: + """Generate docs""" + sys.path.append(HERE) + parser = argparse.ArgumentParser(description=main.__doc__) + parser.add_argument("command", choices=["generate", "lint"]) + args = parser.parse_args() + if args.command == "generate": + import generate + + generate.main() + elif args.command == "lint": + from nvim_doc_tools import lint_md_links + + files = [os.path.join(ROOT, "README.md")] + [ + os.path.join(DOC, file) for file in os.listdir(DOC) if file.endswith(".md") + ] + lint_md_links.main(ROOT, files) + + +if __name__ == "__main__": + main() diff --git a/config/neovim/store/lazy-plugins/conform.nvim/scripts/options_doc.lua b/config/neovim/store/lazy-plugins/conform.nvim/scripts/options_doc.lua new file mode 100644 index 00000000..238cd33f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/scripts/options_doc.lua @@ -0,0 +1,93 @@ +require("conform").setup({ + -- Map of filetype to formatters + formatters_by_ft = { + lua = { "stylua" }, + -- Conform will run multiple formatters sequentially + go = { "goimports", "gofmt" }, + -- Use a sub-list to run only the first available formatter + javascript = { { "prettierd", "prettier" } }, + -- You can use a function here to determine the formatters dynamically + python = function(bufnr) + if require("conform").get_formatter_info("ruff_format", bufnr).available then + return { "ruff_format" } + else + return { "isort", "black" } + end + end, + -- Use the "*" filetype to run formatters on all filetypes. + ["*"] = { "codespell" }, + -- Use the "_" filetype to run formatters on filetypes that don't + -- have other formatters configured. + ["_"] = { "trim_whitespace" }, + }, + -- If this is set, Conform will run the formatter on save. + -- It will pass the table to conform.format(). + -- This can also be a function that returns the table. + format_on_save = { + -- I recommend these options. See :help conform.format for details. + lsp_fallback = true, + timeout_ms = 500, + }, + -- If this is set, Conform will run the formatter asynchronously after save. + -- It will pass the table to conform.format(). + -- This can also be a function that returns the table. + format_after_save = { + lsp_fallback = true, + }, + -- Set the log level. Use `:ConformInfo` to see the location of the log file. + log_level = vim.log.levels.ERROR, + -- Conform will notify you when a formatter errors + notify_on_error = true, + -- Custom formatters and overrides for built-in formatters + formatters = { + my_formatter = { + -- This can be a string or a function that returns a string. + -- When defining a new formatter, this is the only field that is required + command = "my_cmd", + -- A list of strings, or a function that returns a list of strings + -- Return a single string instead of a list to run the command in a shell + args = { "--stdin-from-filename", "$FILENAME" }, + -- If the formatter supports range formatting, create the range arguments here + range_args = function(self, ctx) + return { "--line-start", ctx.range.start[1], "--line-end", ctx.range["end"][1] } + end, + -- Send file contents to stdin, read new contents from stdout (default true) + -- When false, will create a temp file (will appear in "$FILENAME" args). The temp + -- file is assumed to be modified in-place by the format command. + stdin = true, + -- A function that calculates the directory to run the command in + cwd = require("conform.util").root_file({ ".editorconfig", "package.json" }), + -- When cwd is not found, don't run the formatter (default false) + require_cwd = true, + -- When stdin=false, use this template to generate the temporary file that gets formatted + tmpfile_format = ".conform.$RANDOM.$FILENAME", + -- When returns false, the formatter will not be used + condition = function(self, ctx) + return vim.fs.basename(ctx.filename) ~= "README.md" + end, + -- Exit codes that indicate success (default { 0 }) + exit_codes = { 0, 1 }, + -- Environment variables. This can also be a function that returns a table. + env = { + VAR = "value", + }, + -- Set to false to disable merging the config with the base definition + inherit = true, + -- When inherit = true, add these additional arguments to the command. + -- This can also be a function, like args + prepend_args = { "--use-tabs" }, + }, + -- These can also be a function that returns the formatter + other_formatter = function(bufnr) + return { + command = "my_cmd", + } + end, + }, +}) + +-- You can set formatters_by_ft and formatters directly +require("conform").formatters_by_ft.lua = { "stylua" } +require("conform").formatters.my_formatter = { + command = "my_cmd", +} diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/api_spec.lua b/config/neovim/store/lazy-plugins/conform.nvim/tests/api_spec.lua new file mode 100644 index 00000000..ea263438 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/api_spec.lua @@ -0,0 +1,98 @@ +require("plenary.async").tests.add_to_env() +local conform = require("conform") +local test_util = require("tests.test_util") + +describe("api", function() + after_each(function() + test_util.reset_editor() + end) + + it("retrieves info about a formatter", function() + local info = conform.get_formatter_info("stylua") + assert.equal("stylua", info.name) + assert.equal("stylua", info.command) + assert.equal("boolean", type(info.available)) + end) + + it("retrieves unavailable info if formatter does not exist", function() + local info = conform.get_formatter_info("asdf") + assert.equal("asdf", info.name) + assert.equal("asdf", info.command) + assert.falsy(info.available) + end) + + describe("list_formatters", function() + local get_formatter_info = conform.get_formatter_info + before_each(function() + conform.get_formatter_info = function(...) + local info = get_formatter_info(...) + info.available = true + return info + end + end) + after_each(function() + conform.get_formatter_info = get_formatter_info + end) + + it("lists all formatters configured for buffer", function() + conform.formatters_by_ft.lua = { "stylua", "lua-format" } + local bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_set_current_buf(bufnr) + vim.bo[bufnr].filetype = "lua" + local formatters = conform.list_formatters() + local formatter_names = vim.tbl_map(function(f) + return f.name + end, formatters) + assert.are.same({ "stylua", "lua-format" }, formatter_names) + end) + + it("merges formatters from mixed filetypes", function() + conform.formatters_by_ft.lua = { "stylua", "lua-format" } + conform.formatters_by_ft["*"] = { "trim_whitespace" } + local bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_set_current_buf(bufnr) + vim.bo[bufnr].filetype = "lua" + local formatters = conform.list_formatters() + local formatter_names = vim.tbl_map(function(f) + return f.name + end, formatters) + assert.are.same({ "stylua", "lua-format", "trim_whitespace" }, formatter_names) + end) + + it("flattens formatters in alternation groups", function() + conform.formatters_by_ft.lua = { { "stylua", "lua-format" }, "trim_whitespace" } + local bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_set_current_buf(bufnr) + vim.bo[bufnr].filetype = "lua" + local formatters = conform.list_formatters() + local formatter_names = vim.tbl_map(function(f) + return f.name + end, formatters) + assert.are.same({ "stylua", "trim_whitespace" }, formatter_names) + end) + end) + + describe("list_all_formatters", function() + it("lists all formatters configured for all buffers", function() + conform.formatters_by_ft.lua = { "stylua", "lua-format" } + conform.formatters_by_ft["*"] = { "trim_whitespace" } + local formatters = conform.list_all_formatters() + local formatter_names = vim.tbl_map(function(f) + return f.name + end, formatters) + table.sort(formatter_names) + assert.are.same({ "lua-format", "stylua", "trim_whitespace" }, formatter_names) + end) + + it("flattens formatters in alternation groups", function() + conform.formatters_by_ft.lua = { { "stylua", "lua-format" } } + conform.formatters_by_ft["*"] = { "trim_whitespace" } + local formatters = conform.list_all_formatters() + local formatter_names = vim.tbl_map(function(f) + return f.name + end, formatters) + table.sort(formatter_names) + assert.are.same({ "lua-format", "stylua", "trim_whitespace" }, formatter_names) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/fake_formatter.sh b/config/neovim/store/lazy-plugins/conform.nvim/tests/fake_formatter.sh new file mode 100755 index 00000000..9179c9c4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/fake_formatter.sh @@ -0,0 +1,25 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/bash + +set -e + +CODE=0 +if [ "$1" = "--fail" ]; then + shift + echo "failure" >&2 + CODE=1 +fi +if [ "$1" = "--timeout" ]; then + shift + echo "timeout" >&2 + sleep 4 +fi + +output_file="$1" + +if [ -n "$output_file" ] && [ -e "$output_file" ]; then + cat "$output_file" +else + cat +fi + +exit $CODE diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/fs_spec.lua b/config/neovim/store/lazy-plugins/conform.nvim/tests/fs_spec.lua new file mode 100644 index 00000000..2d788c96 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/fs_spec.lua @@ -0,0 +1,22 @@ +local fs = require("conform.fs") + +describe("fs", function() + local relative_paths = { + { "/home", "/home/file.txt", "file.txt" }, + { "/home/", "/home/file.txt", "file.txt" }, + { "/home", "/foo/file.txt", "../foo/file.txt" }, + { "/home/foo", "/home/bar/file.txt", "../bar/file.txt" }, + { "/home", "/file.txt", "../file.txt" }, + { "/home", "/home/foo/file.txt", "foo/file.txt" }, + { ".", "foo/file.txt", "foo/file.txt" }, + { "home", "home/file.txt", "file.txt" }, + { "home", "file.txt", "../file.txt" }, + } + + it("relative_path", function() + for _, paths in ipairs(relative_paths) do + local source, target, expected = unpack(paths) + assert.are.same(fs.relative_path(source, target), expected) + end + end) +end) diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/fuzzer_spec.lua b/config/neovim/store/lazy-plugins/conform.nvim/tests/fuzzer_spec.lua new file mode 100644 index 00000000..c47f1e50 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/fuzzer_spec.lua @@ -0,0 +1,130 @@ +require("plenary.async").tests.add_to_env() +local conform = require("conform") +local log = require("conform.log") +local runner = require("conform.runner") +local test_util = require("tests.test_util") + +describe("fuzzer", function() + before_each(function() + conform.formatters.test = { + meta = { url = "", description = "" }, + command = "tests/fake_formatter.sh", + } + end) + + after_each(function() + test_util.reset_editor() + end) + + ---@param buf_content string[] + ---@param expected string[] + ---@param opts? table + local function run_formatter(buf_content, expected, opts) + local bufnr = vim.fn.bufadd("testfile") + vim.fn.bufload(bufnr) + vim.api.nvim_set_current_buf(bufnr) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, buf_content) + vim.bo[bufnr].modified = false + runner.apply_format(0, buf_content, expected, nil, false) + assert.are.same(expected, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end + + local function make_word() + local chars = {} + for _ = 1, math.random(1, 10) do + table.insert(chars, string.char(math.random(97, 122))) + end + return table.concat(chars, "") + end + + local function make_line() + local words = {} + for _ = 1, math.random(0, 6) do + table.insert(words, make_word()) + end + return table.concat(words, " ") + end + + local function make_file(num_lines) + local lines = {} + for _ = 1, math.random(1, num_lines) do + table.insert(lines, make_line()) + end + return lines + end + + local function do_insert(lines) + local idx = math.random(1, #lines + 1) + for _ = 1, math.random(1, 3) do + table.insert(lines, idx, make_line()) + end + end + + local function do_replace(lines) + local num_lines = math.random(1, math.min(3, #lines)) + local idx = math.random(1, #lines - num_lines + 1) + local replacement = {} + local num_replace = math.random(1, 5) + for _ = 1, num_replace do + table.insert(replacement, make_line()) + end + local col = math.random(1, lines[idx]:len()) + replacement[1] = lines[idx]:sub(1, col) .. replacement[1] + col = math.random(1, lines[idx + num_lines - 1]:len()) + replacement[#replacement] = replacement[#replacement] .. lines[idx + num_lines - 1]:sub(col) + + for _ = 1, num_lines - num_replace do + table.remove(lines, idx) + end + for _ = 1, num_replace - num_lines do + table.insert(lines, idx, "") + end + for i = 1, num_replace do + lines[idx + i - 1] = replacement[i] + end + end + + local function do_delete(lines) + local num_lines = math.random(1, 3) + local idx = math.random(1, #lines - num_lines) + for _ = 1, num_lines do + table.remove(lines, idx) + end + -- vim will never let the lines be empty. An empty file has a single blank line. + if #lines == 0 then + table.insert(lines, "") + end + end + + local function make_edits(lines) + local was_empty = table.concat(lines):match("^%s*$") + lines = vim.deepcopy(lines) + for _ = 1, math.random(0, 3) do + do_insert(lines) + end + for _ = 1, math.random(0, 3) do + do_replace(lines) + end + for _ = 1, math.random(0, 3) do + do_delete(lines) + end + -- avoid blank output (whitepsace only) which is ignored when applying formatting + if not was_empty then + while table.concat(lines):match("^%s*$") do + do_replace(lines) + end + end + return lines + end + + it("formats correctly", function() + -- log.level = vim.log.levels.TRACE + for i = 1, 50000 do + math.randomseed(i) + log.info("Fuzz testing with seed %d", i) + local content = make_file(20) + local formatted = make_edits(content) + run_formatter(content, formatted) + end + end) +end) diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/block_quote.md b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/block_quote.md new file mode 100644 index 00000000..cd56ae88 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/block_quote.md @@ -0,0 +1,5 @@ +text + +> ```lua +> local foo = 'bar' +> ``` diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/block_quote.md.formatted b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/block_quote.md.formatted new file mode 100644 index 00000000..f7728013 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/block_quote.md.formatted @@ -0,0 +1,5 @@ +text + +> ```lua +> |local foo = 'bar'| +> ``` diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/combined_injections.md b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/combined_injections.md new file mode 100644 index 00000000..47aeeb49 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/combined_injections.md @@ -0,0 +1,14 @@ +text + + + +```lua +local foo = 'bar' +``` + + + + +```lua +local foo = 'bar' +``` diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/combined_injections.md.formatted b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/combined_injections.md.formatted new file mode 100644 index 00000000..50dea5fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/combined_injections.md.formatted @@ -0,0 +1,14 @@ +text + +|| + +```lua +|local foo = 'bar'| +``` + + +|| + +```lua +|local foo = 'bar'| +``` diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/inline.ts b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/inline.ts new file mode 100644 index 00000000..88478fd9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/inline.ts @@ -0,0 +1,9 @@ +foo.innerHTML = `
hello
`; + +bar.innerHTML = ` +
world
+`; + +baz.innerHTML = ` +
world
+ `; diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/inline.ts.formatted b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/inline.ts.formatted new file mode 100644 index 00000000..7763c1b9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/inline.ts.formatted @@ -0,0 +1,9 @@ +foo.innerHTML = `|
hello
|`; + +bar.innerHTML = ` +|
world
| +`; + +baz.innerHTML = ` + |
world
| + `; diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/simple.md b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/simple.md new file mode 100644 index 00000000..c9685d78 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/simple.md @@ -0,0 +1,5 @@ +text + +```lua +local foo = "bar" +``` diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/simple.md.formatted b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/simple.md.formatted new file mode 100644 index 00000000..4f68718a --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected/simple.md.formatted @@ -0,0 +1,5 @@ +text + +```lua +|local foo = "bar"| +``` diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/injected_spec.lua b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected_spec.lua new file mode 100644 index 00000000..f6c7175f --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/injected_spec.lua @@ -0,0 +1,88 @@ +require("plenary.async").tests.add_to_env() +local conform = require("conform") +local injected = require("conform.formatters.injected") +local runner = require("conform.runner") +local test_util = require("tests.test_util") + +-- injected formatter only supported on neovim 0.9+ +if vim.fn.has("nvim-0.9") == 0 then + return +end + +---@param dir string +---@return string[] +local function list_test_files(dir) + ---@diagnostic disable-next-line: param-type-mismatch + local fd = vim.loop.fs_opendir(dir, nil, 32) + ---@diagnostic disable-next-line: param-type-mismatch + local entries = vim.loop.fs_readdir(fd) + local ret = {} + while entries do + for _, entry in ipairs(entries) do + if entry.type == "file" and not vim.endswith(entry.name, ".formatted") then + table.insert(ret, entry.name) + end + end + ---@diagnostic disable-next-line: param-type-mismatch + entries = vim.loop.fs_readdir(fd) + end + ---@diagnostic disable-next-line: param-type-mismatch + vim.loop.fs_closedir(fd) + return ret +end + +describe("injected formatter", function() + before_each(function() + -- require("conform.log").level = vim.log.levels.TRACE + conform.formatters_by_ft = { + lua = { "test_mark" }, + html = { "test_mark" }, + } + -- A test formatter that bookends lines with "|" so we can check what was passed in + conform.formatters.test_mark = { + format = function(self, ctx, lines, callback) + local ret = {} + for i, line in ipairs(lines) do + if i == 1 and line == "" then + -- Simulate formatters removing starting newline + elseif i == #lines and line == "" then + -- Simulate formatters removing trailing newline + else + table.insert(ret, "|" .. line .. "|") + end + end + callback(nil, ret) + end, + } + end) + + after_each(function() + test_util.reset_editor() + end) + + for _, filename in ipairs(list_test_files("tests/injected")) do + local filepath = "./tests/injected/" .. filename + local formatted_file = filepath .. ".formatted" + it(filename, function() + local bufnr = vim.fn.bufadd(filepath) + vim.fn.bufload(bufnr) + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true) + local config = assert(conform.get_formatter_config("injected", bufnr)) + local ctx = runner.build_context(bufnr, config) + local err, new_lines, done + injected.format(injected, ctx, lines, function(e, formatted) + done = true + err = e + new_lines = formatted + end) + vim.wait(1000, function() + return done + end) + assert(err == nil, err) + local expected_bufnr = vim.fn.bufadd(formatted_file) + vim.fn.bufload(expected_bufnr) + local expected_lines = vim.api.nvim_buf_get_lines(expected_bufnr, 0, -1, true) + assert.are.same(expected_lines, new_lines) + end) + end +end) diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/minimal_init.lua b/config/neovim/store/lazy-plugins/conform.nvim/tests/minimal_init.lua new file mode 100644 index 00000000..0afbd90d --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/minimal_init.lua @@ -0,0 +1,21 @@ +vim.cmd([[set runtimepath+=.]]) + +vim.o.swapfile = false +vim.bo.swapfile = false +require("tests.test_util").reset_editor() + +local configs = require("nvim-treesitter.configs") +configs.setup({ + ensure_installed = { "markdown", "markdown_inline", "lua", "typescript", "html" }, + sync_install = true, +}) +-- this needs to be run a second time to make tests behave +require("nvim-treesitter").setup() + +vim.api.nvim_create_user_command("RunTests", function(opts) + local path = opts.fargs[1] or "tests" + require("plenary.test_harness").test_directory( + path, + { minimal_init = "./tests/minimal_init.lua" } + ) +end, { nargs = "?" }) diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/runner_spec.lua b/config/neovim/store/lazy-plugins/conform.nvim/tests/runner_spec.lua new file mode 100644 index 00000000..8c0dadfb --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/runner_spec.lua @@ -0,0 +1,389 @@ +require("plenary.async").tests.add_to_env() +local conform = require("conform") +local runner = require("conform.runner") +local test_util = require("tests.test_util") +local util = require("conform.util") + +describe("runner", function() + local OUTPUT_FILE + local CLEANUP_FILES = {} + + ---@param lines string[] + local function set_formatter_output(lines) + local fd, output_file = vim.loop.fs_mkstemp(".testenv/outputXXXXXXXXX") + assert(type(fd) == "number" and output_file, fd) + local content = table.concat(lines, "\n") + vim.loop.fs_write(fd, content) + -- Make sure we add the final newline + vim.loop.fs_write(fd, "\n") + vim.loop.fs_fsync(fd) + vim.loop.fs_close(fd) + OUTPUT_FILE = output_file + table.insert(CLEANUP_FILES, output_file) + end + + after_each(function() + test_util.reset_editor() + OUTPUT_FILE = nil + for _, file in ipairs(CLEANUP_FILES) do + if vim.loop.fs_stat(file) then + vim.loop.fs_unlink(file) + end + end + CLEANUP_FILES = {} + end) + + it("resolves config function", function() + conform.formatters.test = function() + return { + meta = { url = "", description = "" }, + command = "echo", + } + end + local config = assert(conform.get_formatter_config("test")) + assert.are.same({ + meta = { url = "", description = "" }, + command = "echo", + stdin = true, + }, config) + end) + + describe("build_context", function() + it("sets the filename and dirname", function() + vim.cmd.edit({ args = { "README.md" } }) + local bufnr = vim.api.nvim_get_current_buf() + conform.formatters.test = { + meta = { url = "", description = "" }, + command = "echo", + } + local config = assert(conform.get_formatter_config("test")) + local ctx = runner.build_context(0, config) + local filename = vim.api.nvim_buf_get_name(bufnr) + assert.are.same({ + buf = bufnr, + filename = filename, + dirname = vim.fs.dirname(filename), + }, ctx) + end) + + it("sets temp file when stdin = false", function() + vim.cmd.edit({ args = { "README.md" } }) + local bufnr = vim.api.nvim_get_current_buf() + conform.formatters.test = { + meta = { url = "", description = "" }, + command = "echo", + stdin = false, + } + local config = assert(conform.get_formatter_config("test")) + local ctx = runner.build_context(0, config) + local bufname = vim.api.nvim_buf_get_name(bufnr) + local dirname = vim.fs.dirname(bufname) + assert.equal(bufnr, ctx.buf) + assert.equal(dirname, ctx.dirname) + assert.truthy(ctx.filename:match(dirname .. "/.conform.%d+.README.md$")) + end) + end) + + describe("build_cmd", function() + it("replaces $FILENAME in args", function() + vim.cmd.edit({ args = { "README.md" } }) + local bufnr = vim.api.nvim_get_current_buf() + conform.formatters.test = { + meta = { url = "", description = "" }, + command = "echo", + args = { "$FILENAME" }, + } + local config = assert(conform.get_formatter_config("test")) + local ctx = runner.build_context(0, config) + local cmd = runner.build_cmd("", ctx, config) + assert.are.same({ "echo", vim.api.nvim_buf_get_name(bufnr) }, cmd) + end) + + it("replaces $DIRNAME in args", function() + vim.cmd.edit({ args = { "README.md" } }) + local bufnr = vim.api.nvim_get_current_buf() + conform.formatters.test = { + meta = { url = "", description = "" }, + command = "echo", + args = { "$DIRNAME" }, + } + local config = assert(conform.get_formatter_config("test")) + local ctx = runner.build_context(0, config) + local cmd = runner.build_cmd("", ctx, config) + assert.are.same({ "echo", vim.fs.dirname(vim.api.nvim_buf_get_name(bufnr)) }, cmd) + end) + + it("resolves arg function", function() + vim.cmd.edit({ args = { "README.md" } }) + conform.formatters.test = { + meta = { url = "", description = "" }, + command = "echo", + args = function() + return { "--stdin" } + end, + } + local config = assert(conform.get_formatter_config("test")) + local ctx = runner.build_context(0, config) + local cmd = runner.build_cmd("", ctx, config) + assert.are.same({ "echo", "--stdin" }, cmd) + end) + + it("replaces $FILENAME in string args", function() + vim.cmd.edit({ args = { "README.md" } }) + local bufnr = vim.api.nvim_get_current_buf() + conform.formatters.test = { + meta = { url = "", description = "" }, + command = "echo", + args = "$FILENAME | patch", + } + local config = assert(conform.get_formatter_config("test")) + local ctx = runner.build_context(0, config) + local cmd = runner.build_cmd("", ctx, config) + assert.equal("echo " .. vim.api.nvim_buf_get_name(bufnr) .. " | patch", cmd) + end) + + it("replaces $DIRNAME in string args", function() + vim.cmd.edit({ args = { "README.md" } }) + local bufnr = vim.api.nvim_get_current_buf() + conform.formatters.test = { + meta = { url = "", description = "" }, + command = "echo", + args = "$DIRNAME | patch", + } + local config = assert(conform.get_formatter_config("test")) + local ctx = runner.build_context(0, config) + local cmd = runner.build_cmd("", ctx, config) + assert.equal("echo " .. vim.fs.dirname(vim.api.nvim_buf_get_name(bufnr)) .. " | patch", cmd) + end) + + it("resolves arg function with string results", function() + vim.cmd.edit({ args = { "README.md" } }) + conform.formatters.test = { + meta = { url = "", description = "" }, + command = "echo", + args = function() + return "| patch" + end, + } + local config = assert(conform.get_formatter_config("test")) + local ctx = runner.build_context(0, config) + local cmd = runner.build_cmd("", ctx, config) + assert.equal("echo | patch", cmd) + end) + end) + + describe("e2e", function() + before_each(function() + conform.formatters.test = { + command = "tests/fake_formatter.sh", + args = function() + if OUTPUT_FILE then + return { OUTPUT_FILE } + end + return {} + end, + } + end) + + ---@param buf_content string + ---@param expected string + ---@param opts? table + local function run_formatter(buf_content, expected, opts) + local bufnr = vim.fn.bufadd("testfile") + vim.fn.bufload(bufnr) + vim.api.nvim_set_current_buf(bufnr) + local lines = vim.split(buf_content, "\n", { plain = true }) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines) + vim.bo[bufnr].modified = false + local expected_lines = vim.split(expected, "\n", { plain = true }) + set_formatter_output(expected_lines) + conform.format(vim.tbl_extend("keep", opts or {}, { formatters = { "test" }, quiet = true })) + return expected_lines + end + + ---@param buf_content string + ---@param new_content string + local function run_formatter_test(buf_content, new_content) + local lines = run_formatter(buf_content, new_content) + assert.are.same(lines, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end + + it("sets the correct output", function() + run_formatter_test( + [[ + if true { + print("hello") + }]], + [[ + if true { + print("hello") + }]] + ) + run_formatter_test( + [[ + if true { + print("hello") + }]], + [[ + if true { + print("goodbye") + }]] + ) + run_formatter_test( + [[ + if true { + print("hello") + }]], + [[ + if true { + print("hello world") + print("hello world") + print("hello world") + }]] + ) + run_formatter_test( + [[ +print("a") +print("b") +print("c") + ]], + [[ +print("c") +print("b") +print("a") + ]] + ) + run_formatter_test("hello\ngoodbye", "hello\n\n\ngoodbye") + run_formatter_test("hello", "hello\ngoodbye") + run_formatter_test("hello\ngoodbye", "hello") + run_formatter_test("", "hello") + run_formatter_test("\nfoo", "\nhello\nfoo") + run_formatter_test("hello", "hello\n") + run_formatter_test("hello", "hello\n\n") + run_formatter_test("hello\n", "hello") + run_formatter_test("hello\n ", "hello") + + -- These should generate no changes to the buffer + run_formatter_test("hello\n", "hello\n") + assert.falsy(vim.bo.modified) + run_formatter_test("hello", "hello") + assert.falsy(vim.bo.modified) + end) + + it("does not change output if formatter fails", function() + conform.formatters.test.args = util.extend_args(conform.formatters.test.args, { "--fail" }) + run_formatter("hello", "goodbye") + assert.are.same({ "hello" }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end) + + it("allows nonzero exit codes", function() + conform.formatters.test.args = util.extend_args(conform.formatters.test.args, { "--fail" }) + conform.formatters.test.exit_codes = { 0, 1 } + run_formatter_test("hello", "goodbye") + end) + + it("does not format if it times out", function() + conform.formatters.test.args = util.extend_args(conform.formatters.test.args, { "--timeout" }) + run_formatter("hello", "goodbye", { timeout_ms = 10 }) + assert.are.same({ "hello" }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end) + + it("can format async", function() + run_formatter("hello", "goodbye", { async = true }) + assert.are.same({ "hello" }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + vim.wait(1000, function() + return vim.api.nvim_buf_get_lines(0, 0, -1, false)[1] == "goodbye" + end) + assert.are.same({ "goodbye" }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end) + + it("discards formatting changes if buffer has been concurrently modified", function() + run_formatter("hello", "goodbye", { async = true }) + assert.are.same({ "hello" }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + vim.api.nvim_buf_set_lines(0, 0, -1, true, { "newcontent" }) + vim.wait(1000, function() + return vim.api.nvim_buf_get_lines(0, 0, -1, false)[1] == "newcontent" + end) + assert.are.same({ "newcontent" }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end) + + it("discards formatting changes if formatter output is empty /w non-empty input", function() + local bufnr = vim.fn.bufadd("testfile") + vim.fn.bufload(bufnr) + vim.api.nvim_set_current_buf(bufnr) + local original_lines = { "line one", "line two" } + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, original_lines) + vim.bo[bufnr].modified = false + set_formatter_output({ "" }) + conform.format({ formatters = { "test" }, quiet = true }) + local output_lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + assert.are.same(original_lines, output_lines) + end) + + it("formats on save", function() + conform.setup({ + formatters_by_ft = { ["*"] = { "test" } }, + format_on_save = true, + }) + vim.cmd.edit({ args = { "tests/testfile.txt" } }) + vim.api.nvim_buf_set_lines(0, 0, -1, true, { "hello" }) + set_formatter_output({ "goodbye" }) + vim.cmd.write() + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + vim.fn.delete("tests/testfile.txt") + assert.are.same({ "goodbye" }, lines) + end) + + it("formats file even if one formatter errors", function() + conform.formatters.test2 = { + command = "tests/fake_formatter.sh", + args = { "--fail" }, + } + local lines = run_formatter("hello", "goodbye", { formatters = { "test2", "test" } }) + assert.are.same(lines, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end) + + it("does not change output if dry_run is true", function() + run_formatter("hello", "foo", { dry_run = true }) + assert.are.same({ "hello" }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end) + + describe("range formatting", function() + it("applies edits that overlap the range start", function() + run_formatter( + "a\nb\nc", + "d\nb\nd", + { range = { + start = { 1, 0 }, + ["end"] = { 2, 0 }, + } } + ) + assert.are.same({ "d", "b", "c" }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end) + + it("applies edits that overlap the range end", function() + run_formatter( + "a\nb\nc", + "d\nb\nd", + { range = { + start = { 3, 0 }, + ["end"] = { 3, 1 }, + } } + ) + assert.are.same({ "a", "b", "d" }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end) + + it("applies edits that are completely contained by the range", function() + run_formatter( + "a\nb\nc", + "a\nd\nc", + { range = { + start = { 1, 0 }, + ["end"] = { 3, 0 }, + } } + ) + assert.are.same({ "a", "d", "c" }, vim.api.nvim_buf_get_lines(0, 0, -1, false)) + end) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/conform.nvim/tests/test_util.lua b/config/neovim/store/lazy-plugins/conform.nvim/tests/test_util.lua new file mode 100644 index 00000000..7e132388 --- /dev/null +++ b/config/neovim/store/lazy-plugins/conform.nvim/tests/test_util.lua @@ -0,0 +1,24 @@ +require("plenary.async").tests.add_to_env() +local conform = require("conform") +local log = require("conform.log") +local M = {} + +M.reset_editor = function() + vim.cmd.tabonly({ mods = { silent = true } }) + for i, winid in ipairs(vim.api.nvim_tabpage_list_wins(0)) do + if i > 1 then + vim.api.nvim_win_close(winid, true) + end + end + vim.api.nvim_win_set_buf(0, vim.api.nvim_create_buf(false, true)) + for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do + vim.api.nvim_buf_delete(bufnr, { force = true }) + end + conform.formatters = {} + conform.formatters_by_ft = {} + pcall(vim.api.nvim_del_augroup_by_name, "Conform") + log.level = vim.log.levels.ERROR + log.set_handler(print) +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/.github/ISSUE_TEMPLATE/bug_report.yml b/config/neovim/store/lazy-plugins/flash.nvim/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..72230e7f --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,89 @@ +name: Bug Report +description: File a bug/issue +title: "bug: " +labels: [bug] +body: + - type: markdown + attributes: + value: | + **Before** reporting an issue, make sure to read the [documentation](https://github.com/folke/flash.nvim) and search [existing issues](https://github.com/folke/flash.nvim/issues). Usage questions such as ***"How do I...?"*** belong in [Discussions](https://github.com/folke/flash.nvim/discussions) and will be closed. + - type: checkboxes + attributes: + label: Did you check docs and existing issues? + description: Make sure you checked all of the below before submitting an issue + options: + - label: I have read all the flash.nvim docs + required: true + - label: I have searched the existing issues of flash.nvim + required: true + - label: I have searched the existing issues of plugins related to this issue + required: true + - type: input + attributes: + label: "Neovim version (nvim -v)" + placeholder: "0.8.0 commit db1b0ee3b30f" + validations: + required: true + - type: input + attributes: + label: "Operating system/version" + placeholder: "MacOS 11.5" + validations: + required: true + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. Please include any related errors you see in Neovim. + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. + 2. + 3. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Repro + description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua` + value: | + -- DO NOT change the paths and don't remove the colorscheme + local root = vim.fn.fnamemodify("./.repro", ":p") + + -- set stdpaths to use .repro + for _, name in ipairs({ "config", "data", "state", "cache" }) do + vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name + end + + -- bootstrap lazy + local lazypath = root .. "/plugins/lazy.nvim" + if not vim.loop.fs_stat(lazypath) then + vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath, }) + end + vim.opt.runtimepath:prepend(lazypath) + + -- install plugins + local plugins = { + "folke/tokyonight.nvim", + { "folke/flash.nvim", opts = {} }, + -- add any other plugins here + } + require("lazy").setup(plugins, { + root = root .. "/plugins", + }) + + vim.cmd.colorscheme("tokyonight") + -- add anything else here + render: Lua + validations: + required: false diff --git a/config/neovim/store/lazy-plugins/flash.nvim/.github/ISSUE_TEMPLATE/feature_request.yml b/config/neovim/store/lazy-plugins/flash.nvim/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..b53d2593 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,36 @@ +name: Feature Request +description: Suggest a new feature +title: "feature: " +labels: [enhancement] +body: + - type: checkboxes + attributes: + label: Did you check the docs? + description: Make sure you read all the docs before submitting a feature request + options: + - label: I have read all the flash.nvim docs + required: true + - type: textarea + validations: + required: true + attributes: + label: Is your feature request related to a problem? Please describe. + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + - type: textarea + validations: + required: true + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + - type: textarea + validations: + required: true + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + - type: textarea + validations: + required: false + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. diff --git a/config/neovim/store/lazy-plugins/flash.nvim/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/flash.nvim/.github/workflows/ci.yml new file mode 100644 index 00000000..12ed564d --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +name: CI +on: + push: + pull_request: + +jobs: + tests: + strategy: + matrix: + # os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Install Neovim + shell: bash + run: | + mkdir -p /tmp/nvim + wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage + cd /tmp/nvim + chmod a+x ./nvim.appimage + ./nvim.appimage --appimage-extract + echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + - name: Run Tests + run: | + nvim --version + [ ! -d tests ] && exit 0 + nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua', sequential = true}" + docs: + runs-on: ubuntu-latest + needs: tests + if: ${{ github.ref == 'refs/heads/main' }} + steps: + - uses: actions/checkout@v3 + - name: panvimdoc + uses: kdheepak/panvimdoc@main + with: + vimdoc: flash.nvim + version: "Neovim >= 0.8.0" + demojify: true + treesitter: true + - name: Push changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "chore(build): auto-generate vimdoc" + commit_user_name: "github-actions[bot]" + commit_user_email: "github-actions[bot]@users.noreply.github.com" + commit_author: "github-actions[bot] " + release: + name: release + if: ${{ github.ref == 'refs/heads/main' }} + needs: + - docs + - tests + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v3 + id: release + with: + release-type: simple + package-name: flash.nvim + - uses: actions/checkout@v3 + - name: tag stable versions + if: ${{ steps.release.outputs.release_created }} + run: | + git config user.name github-actions[bot] + git config user.email github-actions[bot]@users.noreply.github.com + git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" + git tag -d stable || true + git push origin :stable || true + git tag -a stable -m "Last Stable Release" + git push origin stable diff --git a/config/neovim/store/lazy-plugins/flash.nvim/.gitignore b/config/neovim/store/lazy-plugins/flash.nvim/.gitignore new file mode 100644 index 00000000..cc5457ab --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/.gitignore @@ -0,0 +1,8 @@ +tt.* +.tests +doc/tags +debug +.repro +foo.* +*.log +data diff --git a/config/neovim/store/lazy-plugins/flash.nvim/.neoconf.json b/config/neovim/store/lazy-plugins/flash.nvim/.neoconf.json new file mode 100644 index 00000000..bbc416f2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/.neoconf.json @@ -0,0 +1,10 @@ +{ + "neodev": { + "library": { + "plugins": [ + "plenary.nvim", + "lazy.nvim" + ] + } + } +} diff --git a/config/neovim/store/lazy-plugins/flash.nvim/CHANGELOG.md b/config/neovim/store/lazy-plugins/flash.nvim/CHANGELOG.md new file mode 100644 index 00000000..9c0e3e19 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/CHANGELOG.md @@ -0,0 +1,474 @@ +# Changelog + +## [1.18.3](https://github.com/folke/flash.nvim/compare/v1.18.2...v1.18.3) (2024-05-03) + + +### Bug Fixes + +* **hacks:** use `vim.api.nvim__redraw` to fix the cursor instead of ffi. Fixes [#333](https://github.com/folke/flash.nvim/issues/333) ([1b128ff](https://github.com/folke/flash.nvim/commit/1b128ff527c3938460ef83fe6403ce6ce3f53b53)) + +## [1.18.2](https://github.com/folke/flash.nvim/compare/v1.18.1...v1.18.2) (2023-10-17) + + +### Bug Fixes + +* **treesitter:** show warning when treesitter not available. Fixes [#261](https://github.com/folke/flash.nvim/issues/261) ([77c66d8](https://github.com/folke/flash.nvim/commit/77c66d84be3e2a2ef2e6689de668fe156af74498)) + +## [1.18.1](https://github.com/folke/flash.nvim/compare/v1.18.0...v1.18.1) (2023-10-16) + + +### Bug Fixes + +* **char:** allow setting autohide=true for char mode. Fixes [#231](https://github.com/folke/flash.nvim/issues/231) ([71040c8](https://github.com/folke/flash.nvim/commit/71040c87bd64d2719727006f51f8679352eb6146)) +* **jump:** send `esc` when cancelling flash. Fixes [#212](https://github.com/folke/flash.nvim/issues/212). Fixes [#233](https://github.com/folke/flash.nvim/issues/233) ([677eb59](https://github.com/folke/flash.nvim/commit/677eb59f0a94ed3b735168d9e6738723fd44796d)) +* **treesitter:** include treesitter injections. Fixes [#242](https://github.com/folke/flash.nvim/issues/242) ([5fe47ba](https://github.com/folke/flash.nvim/commit/5fe47baf1be05ea34abb6912ed89a5a17cbf5661)) +* **treesitter:** keep treesitter sorting when doing ;,. Fixes [#219](https://github.com/folke/flash.nvim/issues/219) ([aae8352](https://github.com/folke/flash.nvim/commit/aae83521091fac904b8584bb2dffe13420b7adc7)) + +## [1.18.0](https://github.com/folke/flash.nvim/compare/v1.17.3...v1.18.0) (2023-10-02) + + +### Features + +* **char:** allow disabling clever-f motions. Fixes [#245](https://github.com/folke/flash.nvim/issues/245) ([bc1f49f](https://github.com/folke/flash.nvim/commit/bc1f49f428655b645948a3489bf0efcded6f46e6)) +* enable multi window in vscode ([#230](https://github.com/folke/flash.nvim/issues/230)) ([65bd3ee](https://github.com/folke/flash.nvim/commit/65bd3ee715229fecdb5a9727e8dcd099c187622b)) +* **highlight:** allow overriding flash cursor hl. Fixes [#228](https://github.com/folke/flash.nvim/issues/228) ([79d67c6](https://github.com/folke/flash.nvim/commit/79d67c6d29cd3d784eb5f1410ba057e1f1499fe9)) + + +### Bug Fixes + +* **char:** disable jump labels when reg recording/executing ([#226](https://github.com/folke/flash.nvim/issues/226)) ([503b0ab](https://github.com/folke/flash.nvim/commit/503b0ab0091776d2c40541507114ff4b2f24f5b9)) +* **jump:** only open folds containing match. Fixes [#224](https://github.com/folke/flash.nvim/issues/224). Fixes [#225](https://github.com/folke/flash.nvim/issues/225) ([a74d31f](https://github.com/folke/flash.nvim/commit/a74d31ffec4a6e9feb6adc33efdba247d5d912f0)) +* **search:** allow disabling multi window for search. Fixes [#198](https://github.com/folke/flash.nvim/issues/198). Fixes [#197](https://github.com/folke/flash.nvim/issues/197) ([0256d8e](https://github.com/folke/flash.nvim/commit/0256d8ecab33a9aa69fdaaf885db22e1103e2a3a)) +* **state:** use actions instead of opts.actions ([30442c8](https://github.com/folke/flash.nvim/commit/30442c88b817b5d00fcbe2f88977bbd5d0221a20)) + +## [1.17.3](https://github.com/folke/flash.nvim/compare/v1.17.2...v1.17.3) (2023-07-20) + + +### Bug Fixes + +* **jump:** disable operator keymaps when replaying remote. Fixes [#165](https://github.com/folke/flash.nvim/issues/165) ([9f30d48](https://github.com/folke/flash.nvim/commit/9f30d48e2f509723e59c5b0915f343ce297cf386)) + +## [1.17.2](https://github.com/folke/flash.nvim/compare/v1.17.1...v1.17.2) (2023-07-18) + + +### Bug Fixes + +* **char:** only use c for first search (of count) when current=true ([c92ecbf](https://github.com/folke/flash.nvim/commit/c92ecbff98fdc8770c283aa3934349e6889195dd)) +* **config:** run `setup` when using flash and it wasn't run yet. Fixes [#162](https://github.com/folke/flash.nvim/issues/162) ([c81e0d1](https://github.com/folke/flash.nvim/commit/c81e0d11b9e6e1279321e12a5d87dd3fac593854)) +* **state:** feed char when incremental and no match. Fixes [#57](https://github.com/folke/flash.nvim/issues/57) ([925f733](https://github.com/folke/flash.nvim/commit/925f733a731f8ed351e47d434e3a353995761012)) + +## [1.17.1](https://github.com/folke/flash.nvim/compare/v1.17.0...v1.17.1) (2023-07-16) + + +### Bug Fixes + +* **char:** fix current for tT when count=0. Fixes [#159](https://github.com/folke/flash.nvim/issues/159) ([8604b56](https://github.com/folke/flash.nvim/commit/8604b562d919772dc161ac831dd7bfa948833fdd)) +* **char:** never add mappings for mapleader and maplocalleader ([6e3dab6](https://github.com/folke/flash.nvim/commit/6e3dab6b011bb7661b16e14dd4aa4215894c9291)) +* **char:** never overwrite existing mappings for ; and , ([abda6b8](https://github.com/folke/flash.nvim/commit/abda6b848bb11051e6a789f8a8572da3d3840bf1)) +* **char:** reset including current for tT searches. Fixes [#152](https://github.com/folke/flash.nvim/issues/152) ([9c53dad](https://github.com/folke/flash.nvim/commit/9c53dad391801acb9ce9aa49820f15f6692aec91)) +* **highlight:** set hl of target to current if it's a single character only. See [#158](https://github.com/folke/flash.nvim/issues/158) ([47d147b](https://github.com/folke/flash.nvim/commit/47d147b9527025b2ee73631b098edb5798afef4b)) +* **remote:** properly pass register for remote ops. Fixes [#156](https://github.com/folke/flash.nvim/issues/156) ([34cf6f6](https://github.com/folke/flash.nvim/commit/34cf6f685d2eabc8de438fdbaa41c8c17e9da459)) + +## [1.17.0](https://github.com/folke/flash.nvim/compare/v1.16.0...v1.17.0) (2023-07-14) + + +### Features + +* **labels:** allow disabling reusing labels. Closes [#147](https://github.com/folke/flash.nvim/issues/147) ([4b73e61](https://github.com/folke/flash.nvim/commit/4b73e6124f4e9b44713cb85ec5db3809923d2374)) + + +### Bug Fixes + +* **char:** properly exit op mode when doing esc with ftFT and jump labels ([4731cc4](https://github.com/folke/flash.nvim/commit/4731cc47459f66f9a73d19e11ea157e105384fd6)) +* **char:** set inclusive=false for FT. Fixes [#149](https://github.com/folke/flash.nvim/issues/149) ([b1af2b7](https://github.com/folke/flash.nvim/commit/b1af2b78b30e814c08840a5bb7f7ccef726ea771)) +* **jump:** better way to cancel operator pending mode ([4a980ea](https://github.com/folke/flash.nvim/commit/4a980ea7fedf20c902375fe7aa1141d671b0ffa7)) + +## [1.16.0](https://github.com/folke/flash.nvim/compare/v1.15.0...v1.16.0) (2023-07-12) + + +### Features + +* **fold:** show first label inside a fold on the folded text line. Fixes [#39](https://github.com/folke/flash.nvim/issues/39) ([2846324](https://github.com/folke/flash.nvim/commit/28463247f21a6e0b5486dc6d31c7ace0e43a4877)) +* **jump:** open folds when jumping to a folded position. See [#39](https://github.com/folke/flash.nvim/issues/39) ([dcb494c](https://github.com/folke/flash.nvim/commit/dcb494cfa79aae32e17a44026591564793b75434)) +* **search:** when nohlsearch=false, matches will now be shown after jump. Fixes [#142](https://github.com/folke/flash.nvim/issues/142) ([6e7d6c2](https://github.com/folke/flash.nvim/commit/6e7d6c26a4528a8d6a17e2d23c3f5738491d736d)) + + +### Bug Fixes + +* **repeat:** no dot repeat inside macros. Fixes [#143](https://github.com/folke/flash.nvim/issues/143) ([f7218c2](https://github.com/folke/flash.nvim/commit/f7218c2d44a8d67c5c4b40edd569c55f95754354)) + +## [1.15.0](https://github.com/folke/flash.nvim/compare/v1.14.0...v1.15.0) (2023-07-07) + + +### Features + +* **search:** flash toggle in search is now permanent until you toggle again. Closes [#134](https://github.com/folke/flash.nvim/issues/134) ([7ceee0d](https://github.com/folke/flash.nvim/commit/7ceee0de7e96c7453d5f82dcfc938f08d8029703)) + + +### Bug Fixes + +* **char:** special handling for t/T at current position. Fixes [#137](https://github.com/folke/flash.nvim/issues/137) ([268bffe](https://github.com/folke/flash.nvim/commit/268bffe7b9b1b9a3a4bb64a5bc8ac0627b4b7c14)) + +## [1.14.0](https://github.com/folke/flash.nvim/compare/v1.13.2...v1.14.0) (2023-07-05) + + +### Features + +* **char:** added optional multi_line=false for ftFT motions. See [#102](https://github.com/folke/flash.nvim/issues/102) ([2f92418](https://github.com/folke/flash.nvim/commit/2f924186255a56cab4cf22e13b0bc1fb906b11fa)) +* **char:** option for behavior of ;, and char repeats. Closes [#124](https://github.com/folke/flash.nvim/issues/124) ([97eba7d](https://github.com/folke/flash.nvim/commit/97eba7df4454097c1f6cc447de2a4e9230831ffb)) +* **search:** allow finding current ([6659a94](https://github.com/folke/flash.nvim/commit/6659a94a033c2f6fec1e142451aa264f03e5da90)) +* **state:** added optional `filter` for matches by non-search matcher. See [#118](https://github.com/folke/flash.nvim/issues/118) ([780ad57](https://github.com/folke/flash.nvim/commit/780ad57dedb464bfe8361356959b3ac5aaed533d)) +* **treesitter:** added `node:TSNode` to ts `Flash.Match.TS` ([1cbaff4](https://github.com/folke/flash.nvim/commit/1cbaff4a7f074c1121c89207210e4588321acd40)) + + +### Bug Fixes + +* **char:** fixed tT at current. Fixes [#128](https://github.com/folke/flash.nvim/issues/128) ([a1c8aa6](https://github.com/folke/flash.nvim/commit/a1c8aa62204d5eb2036e819f5b919b1fe4b88918)) +* **jump:** move offset calc outside op mode ([69141ea](https://github.com/folke/flash.nvim/commit/69141ea571602a9202ad51fae1cfe7c1894fe036)) +* **search:** count=0 ([6d1d066](https://github.com/folke/flash.nvim/commit/6d1d066e6b5fcc2ed3ca446d229c0a0d306acf17)) +* take into count of multi-width characters on offset of highlights and jump ([#125](https://github.com/folke/flash.nvim/issues/125)) ([41c09fa](https://github.com/folke/flash.nvim/commit/41c09faf8588887c7c15d8ca63c9ede805437da2)) + +## [1.13.2](https://github.com/folke/flash.nvim/compare/v1.13.1...v1.13.2) (2023-07-02) + + +### Bug Fixes + +* **highlight:** dont use current when rainbow is used and match == target. Fixes [#109](https://github.com/folke/flash.nvim/issues/109) ([edb82f7](https://github.com/folke/flash.nvim/commit/edb82f763ac2b63006154e9da8b6629b570de551)) + +## [1.13.1](https://github.com/folke/flash.nvim/compare/v1.13.0...v1.13.1) (2023-07-02) + + +### Bug Fixes + +* **config:** dont show jumpt labels by default! Fixup. See [#103](https://github.com/folke/flash.nvim/issues/103) ([7bb89b2](https://github.com/folke/flash.nvim/commit/7bb89b20fd42037c1cd7ed8d3193081d86f8c39b)) +* **highlight:** don't show the label when at cursor in same window and not a range. See [#74](https://github.com/folke/flash.nvim/issues/74) ([7a8e07e](https://github.com/folke/flash.nvim/commit/7a8e07e62ad1a378d6eca958aad90fc071d14e9c)) +* **labeler:** don't label folded lines. Fixes [#39](https://github.com/folke/flash.nvim/issues/39). See [#106](https://github.com/folke/flash.nvim/issues/106) ([8af3773](https://github.com/folke/flash.nvim/commit/8af3773b7b960b053038868ea18867b94abae9c8)) + +## [1.13.0](https://github.com/folke/flash.nvim/compare/v1.12.0...v1.13.0) (2023-07-01) + + +### Features + +* **config:** added `opts.config` for dynamically configuring flash. Closes [#103](https://github.com/folke/flash.nvim/issues/103) ([3829d81](https://github.com/folke/flash.nvim/commit/3829d81fd6f5f6ca784bb9628a1b99298b88a3af)) + + +### Bug Fixes + +* **state:** use strchars instead of strcharlen for compat 0.8.2. Fixes [#105](https://github.com/folke/flash.nvim/issues/105) ([33e0793](https://github.com/folke/flash.nvim/commit/33e0793a614735a3fffb93763c4c9bd81b55433b)) + +## [1.12.0](https://github.com/folke/flash.nvim/compare/v1.11.0...v1.12.0) (2023-06-30) + + +### Features + +* **state:** added support for custom keymaps and lmap. See [#66](https://github.com/folke/flash.nvim/issues/66) ([9aa7805](https://github.com/folke/flash.nvim/commit/9aa78057cf13dde3d39bf25cfe5caf092083cc0c)) + + +### Bug Fixes + +* **labeler:** fixed calculating skip labels for mbyte keymaps. See [#66](https://github.com/folke/flash.nvim/issues/66) ([2da635f](https://github.com/folke/flash.nvim/commit/2da635f54b81538a1e12b4859bc292d7d3e5f1b9)) +* **treesitter:** added support for Nvim 0.8.0. Fixes [#100](https://github.com/folke/flash.nvim/issues/100) ([67ed44d](https://github.com/folke/flash.nvim/commit/67ed44d5efd2d05b49af861859740eedf3a076b6)) +* **treesitter:** some nodes were missing ([7f4e25f](https://github.com/folke/flash.nvim/commit/7f4e25fae0fa1d3adfeb3e3e87fba9ff914032a0)) + +## [1.11.0](https://github.com/folke/flash.nvim/compare/v1.10.1...v1.11.0) (2023-06-29) + + +### Features + +* **char:** hide flash when doing an ftFT search while yanking. Closes [#6](https://github.com/folke/flash.nvim/issues/6) ([feda1d5](https://github.com/folke/flash.nvim/commit/feda1d5a98a1705e86966e62a052661a7369b3c0)) +* **char:** optional jump labels for ftFT searches ([d2ad5e0](https://github.com/folke/flash.nvim/commit/d2ad5e0d776a89ee424a7e0cd4364ec5dbf11dc4)) +* **char:** support alternative f/F/t/T/;/, keymaps (fix [#96](https://github.com/folke/flash.nvim/issues/96)) ([#99](https://github.com/folke/flash.nvim/issues/99)) ([c0c006a](https://github.com/folke/flash.nvim/commit/c0c006a7bb694b4cec9a5f40e632f871b478e0d0)) +* **label:** added `opts.label.format` for formatting rendered labels. Closes [#84](https://github.com/folke/flash.nvim/issues/84) ([2d3e7b9](https://github.com/folke/flash.nvim/commit/2d3e7b90c568083e9857b100dc2570d269da0a0c)) +* **labeler:** allow excluding certain labels with a specific case ([6b255d3](https://github.com/folke/flash.nvim/commit/6b255d37505445da3db6fae5d79dff63529cd222)) +* **pos:** Pos can now be initialized with window or current window cursor ([7a05cd5](https://github.com/folke/flash.nvim/commit/7a05cd5dadb78b8d475526157e464f24d14ff5b2)) +* **search:** you can now `toggle` flash while using regular search ([e761182](https://github.com/folke/flash.nvim/commit/e761182f6c79ff5f88c877729465ece05b01c65a)) +* **state:** custom char actions ([4f44bb4](https://github.com/folke/flash.nvim/commit/4f44bb454df0c6f598e75cd8501a1eb8e1bd2df5)) + + +### Bug Fixes + +* **hacks:** make sure to render the cursor before getchar ([2b328d1](https://github.com/folke/flash.nvim/commit/2b328d121c2b56cf25e1eb9ba92c7459beb241be)) +* **highlight:** never put an extmark on the current cursor position ([8434130](https://github.com/folke/flash.nvim/commit/843413028843d1c3ce29449fe9ff62af8f642540)) +* **highlight:** use current hl if pos == label pos ([56531ee](https://github.com/folke/flash.nvim/commit/56531ee85d919e787dbb247aabedb5d3dd0b7bd1)) +* **jump:** replace opfunc by noop to properly cancel custom operators. Fixes [#93](https://github.com/folke/flash.nvim/issues/93) ([40b2bcb](https://github.com/folke/flash.nvim/commit/40b2bcbb05f1452f2ee7d21b79ce8ba77ea6cc94)) +* **jump:** temporarily set selection=inclusive. Closes [#81](https://github.com/folke/flash.nvim/issues/81) ([5c9505a](https://github.com/folke/flash.nvim/commit/5c9505a19edcbb236d367282584ed5f02ccd4fb4)) +* **labeler:** fixed label distance calculation ([1d941de](https://github.com/folke/flash.nvim/commit/1d941de722564a8ac2f07c2df262a48c49c1cdb9)) +* **labeler:** put original pattern in a `\%()` group. Fixes some skip label issues ([6102a7c](https://github.com/folke/flash.nvim/commit/6102a7c0e93dbcf592a7ed2b7a2a5c2a84c5033e)) +* **labeler:** skip all labels on invalid regex. Fixes [#94](https://github.com/folke/flash.nvim/issues/94) ([1fff746](https://github.com/folke/flash.nvim/commit/1fff746049253b10a008d60e1752065a98fd8614)) +* **remote:** use nvim_input instead of nvim_feedkeys for clearing op mode ([c90eae5](https://github.com/folke/flash.nvim/commit/c90eae5172a00551d51883cf8b67306a812a713f)) +* **search:** correctly set match end pos for multi byte characters. Fixes [#90](https://github.com/folke/flash.nvim/issues/90) ([0193d52](https://github.com/folke/flash.nvim/commit/0193d52af38d228b79569c62e06ee36b77a1a85e)) +* **treesitter:** ignore windows without ts parser. Fixes [#91](https://github.com/folke/flash.nvim/issues/91) ([13022c0](https://github.com/folke/flash.nvim/commit/13022c09fa30fb03d14110a380238f6a75b42ab4)) + +## [1.10.1](https://github.com/folke/flash.nvim/compare/v1.10.0...v1.10.1) (2023-06-27) + + +### Bug Fixes + +* **highlight:** apply after labels and then before ([4439fca](https://github.com/folke/flash.nvim/commit/4439fca240a54ef4d4537102668285e9cbb6f23c)) +* **highlight:** correctly order after labels at the same column ([b096797](https://github.com/folke/flash.nvim/commit/b096797b64f56357c40222f5a3cff6f25ac3b5dc)) +* **highlight:** make sure col is not negative with label.before = true ([cbce7f9](https://github.com/folke/flash.nvim/commit/cbce7f923c74fb75be030273c0d49f6a3447a95f)) +* **prompt:** never show the prompt when in regular search ([51149ba](https://github.com/folke/flash.nvim/commit/51149ba2e6bcba0a28e67b9654450835437a2914)) +* **rainbow:** stable rainbow label highlight groups ([937df4f](https://github.com/folke/flash.nvim/commit/937df4f097781e3e91594bf69425f3e74044b711)) + +## [1.10.0](https://github.com/folke/flash.nvim/compare/v1.9.0...v1.10.0) (2023-06-27) + + +### Features + +* **highlight:** added optional rainbow labels. Disabled by default. Useful for Treesitter ranges. ([#74](https://github.com/folke/flash.nvim/issues/74)) ([ffb865b](https://github.com/folke/flash.nvim/commit/ffb865b1a60732d9ce2c9bffe3fb6724e1004ebb)) + + +### Bug Fixes + +* **char:** force before=false with f, F motion ([#75](https://github.com/folke/flash.nvim/issues/75)) ([40313ec](https://github.com/folke/flash.nvim/commit/40313ecf3140264b6e9d9611a3832a32e5ab7a46)) +* **search:** fixup for search commmands ([0f2d53d](https://github.com/folke/flash.nvim/commit/0f2d53d63e9d90f7a310509fbf4e98fbe21be56e)) + +## [1.9.0](https://github.com/folke/flash.nvim/compare/v1.8.0...v1.9.0) (2023-06-26) + + +### Features + +* **treesitter:** added treesitter search to label ts nodes around search matches ([6f791d4](https://github.com/folke/flash.nvim/commit/6f791d4709a2c8ef2373302d3a067ae45fdc2f8d)) + + +### Bug Fixes + +* added unicode support for labels/skips and fuzzy search. See [#66](https://github.com/folke/flash.nvim/issues/66) ([2528752](https://github.com/folke/flash.nvim/commit/2528752b7efbf3f67cce8b9d0d75ee769f72c01e)) +* **state:** restore window views on esc or ctrl-c ([7b21dfd](https://github.com/folke/flash.nvim/commit/7b21dfddcf7ccc4fb665ca0db80810210f8cde7c)) +* **treesitter:** add incremental = false to default settings of treesitter ([1cf706f](https://github.com/folke/flash.nvim/commit/1cf706f342bea4447c2f8ac13c2fab9df060ce1e)) + +## [1.8.0](https://github.com/folke/flash.nvim/compare/v1.7.0...v1.8.0) (2023-06-26) + + +### Features + +* added prompt window that shows pattern during jump (can be disabled) ([3fff703](https://github.com/folke/flash.nvim/commit/3fff7033f53b8f0714efd0dd56b03aa3f22c6376)) +* **api:** allow a match to disable getting a label ([ea56cea](https://github.com/folke/flash.nvim/commit/ea56ceaea4760b2031719d8e5eb1b6231ef9f43c)) +* **api:** allow a match to enable/disable highlight ([38eca97](https://github.com/folke/flash.nvim/commit/38eca97c8bdbbbd7be64b562eeb9f964cf8bc145)) +* **ffi:** added `mappings_enabled` ([6f6af15](https://github.com/folke/flash.nvim/commit/6f6af15b491bee14460873fe63fc7b20e7c73dd8)) +* **hacks:** added support for detecting user input waiting ([81c610a](https://github.com/folke/flash.nvim/commit/81c610acd374b40fc7a7fa4b493b1b9783d3d52d)) +* **highlight:** added option to disable distance based labeling ([ad9212f](https://github.com/folke/flash.nvim/commit/ad9212f28ef37e893a5a4113f8757052b2035c36)) +* **highlight:** show fake cursor in all windows when flash is active ([471b165](https://github.com/folke/flash.nvim/commit/471b165722ae5db4ddad7cbaf1d351127fb55529)) +* **highlight:** when running in vscode, set default hl groups to something that works ([d4c30b1](https://github.com/folke/flash.nvim/commit/d4c30b169f01b8108c5bc38e230a975408133603)) +* **jump:** added jump offset ([0f2dfac](https://github.com/folke/flash.nvim/commit/0f2dfaca329ed9a7db9e5062d964492cf51765eb)) +* **jump:** added options for remote operator pending mode ([436d1f4](https://github.com/folke/flash.nvim/commit/436d1f402a696733b8a1512072bbd0ac8da72cea)) +* **jump:** remote operator pending operations will now always return to the original window ([c11d0d1](https://github.com/folke/flash.nvim/commit/c11d0d15660ce309c733982b2c34cd54c9c9d9f0)) +* **label:** minimum pattern length to show labels. Closes [#68](https://github.com/folke/flash.nvim/issues/68) ([2c2302a](https://github.com/folke/flash.nvim/commit/2c2302a3eae1dc72d2140c58974e2f73df41556d)) +* matcher function now has a from/to opts param ([1cb669d](https://github.com/folke/flash.nvim/commit/1cb669d2ce074ea39722da9fec6b0c2686b3b484)) +* **remote_op:** allow setting motion to `nil` to automatically start a new motion when needed ([259062d](https://github.com/folke/flash.nvim/commit/259062ddc47f9de11e0e498cd58040705d7b6f5c)) +* **remote:** implement remote using new `remote_op` options ([51f5c35](https://github.com/folke/flash.nvim/commit/51f5c352db8791f4218e19cc7fa40948cdda9647)) +* searches can now be continued. Closes [#54](https://github.com/folke/flash.nvim/issues/54) ([487aa52](https://github.com/folke/flash.nvim/commit/487aa52956fdf79ba545151227b0ad39c5276c69)) +* **state:** added support for restoring all window views and current window ([01736c0](https://github.com/folke/flash.nvim/commit/01736c01eb43dcf497a946689c7f434b1d13b4a8)) +* **util:** luv check that does something when something finishes ([a3643eb](https://github.com/folke/flash.nvim/commit/a3643eb5424c12b5abc7b08a74d0d53fa5a29af0)) +* **vscode:** make flash work properly in vscode by updating/changing the default config. Fixes [#58](https://github.com/folke/flash.nvim/issues/58) ([fa72836](https://github.com/folke/flash.nvim/commit/fa72836760417436cfe8e33ee74edaefd8ee9e00)) + + +### Bug Fixes + +* **config:** process modes in correct order. Fixes [#50](https://github.com/folke/flash.nvim/issues/50) again ([919cbe4](https://github.com/folke/flash.nvim/commit/919cbe49b66758cf57529847c396e718a9883de0)) +* disable prompt on vscode ([f93b33d](https://github.com/folke/flash.nvim/commit/f93b33d736fb2eb6f28526ab465cfe7f32e7d96f)) +* **jump:** fixup to always use a motion for remote ops ([11fa883](https://github.com/folke/flash.nvim/commit/11fa8833c62175a88fc35c50f1d23d5002d20fda)) +* **jump:** improved operator pending mode for jumps ([16f785f](https://github.com/folke/flash.nvim/commit/16f785f26e74b8f0b49901356c57cda2a06379f5)) +* **jump:** operator pending mode for remote jumps now behaves correctly ([cb24e66](https://github.com/folke/flash.nvim/commit/cb24e667ea58cfa7ea9df9fdf41bb6a26ea13da1)) +* **remote:** make sure opts always exists ([7083750](https://github.com/folke/flash.nvim/commit/7083750697dea16b3943ca8a92c958acd83c2126)) +* **search:** added support for search-commands. Fixes [#67](https://github.com/folke/flash.nvim/issues/67) ([7a59c42](https://github.com/folke/flash.nvim/commit/7a59c4239ed11ca3ec91cd7544535d836f09eb20)) + +## [1.7.0](https://github.com/folke/flash.nvim/compare/v1.6.0...v1.7.0) (2023-06-24) + + +### Features + +* **config:** allow mode inheritance. Closes [#50](https://github.com/folke/flash.nvim/issues/50) ([3deefe8](https://github.com/folke/flash.nvim/commit/3deefe88e02e68c163c320614be1727fa887cd65)) +* **jump:** added option to force inclusive/exclusive. Closes [#49](https://github.com/folke/flash.nvim/issues/49) ([e71efbf](https://github.com/folke/flash.nvim/commit/e71efbfbc73df21d3e79d30c4c27bd29892c216c)) +* **remote:** peoperly deal with c for remote. Will jump back when leaving insert mode ([1075013](https://github.com/folke/flash.nvim/commit/10750139d3d4f2fb6c7bb8cc33aef988a7b26b7c)) +* **state:** allow passing a callable object as matcher ([f49fa9c](https://github.com/folke/flash.nvim/commit/f49fa9cbddd6a30c59420892e09f57f391bd9516)) + + +### Bug Fixes + +* **cache:** allow current window to be excluded ([770763c](https://github.com/folke/flash.nvim/commit/770763ce2d2b4c340249cb7000de81c2085438c8)) +* **cache:** fixup for window selection ([ed3bec6](https://github.com/folke/flash.nvim/commit/ed3bec6da9b92cee4954bfb71c4e71d06406191c)) +* **char:** add group to autocmd ([fc08d27](https://github.com/folke/flash.nvim/commit/fc08d279ddb92ba2323684a2077aa7797384fc3c)) +* **remote:** properly restore remote window as well. Also remove the `normal! o` ([587a243](https://github.com/folke/flash.nvim/commit/587a2436f84301b84937242657dcc03be4a80702)) + + +### Performance Improvements + +* **remote:** restore views on TextYankPost ([d4dadc8](https://github.com/folke/flash.nvim/commit/d4dadc8fae53ded2a51a2ca0a9d82889e148e0b7)) + +## [1.6.0](https://github.com/folke/flash.nvim/compare/v1.5.0...v1.6.0) (2023-06-24) + + +### Features + +* **config:** pattern can now have a `max_length`. When length is reached, labels are no longer skipped. When it exceeds, either a jump is followed or the search is ended ([bd9dbee](https://github.com/folke/flash.nvim/commit/bd9dbee041296a582faa6dfe25e1af87d65614c7)) + + +### Bug Fixes + +* **config:** exclude noice by default ([bc9a599](https://github.com/folke/flash.nvim/commit/bc9a5992b947ae84b5c1458f0b117abda1b61154)) +* **repeat:** make sure repeat is enabled for char searches. Fixes [#40](https://github.com/folke/flash.nvim/issues/40) ([219f0c0](https://github.com/folke/flash.nvim/commit/219f0c09b664257a7d9b46023bcb24563ae49832)) +* **state:** always reposition the cursor on incremental mode ([81e38d6](https://github.com/folke/flash.nvim/commit/81e38d604d285d835a9186f82e28a302bc048128)) + +## [1.5.0](https://github.com/folke/flash.nvim/compare/v1.4.1...v1.5.0) (2023-06-23) + + +### Features + +* added remote plugin ([fb50450](https://github.com/folke/flash.nvim/commit/fb5045044f28caf08ca6d89e9fe40874138faeef)) +* flash remote. thank you [@max397574](https://github.com/max397574)! ([809ea4f](https://github.com/folke/flash.nvim/commit/809ea4f804d831ca5ff26c94b8d409ad9dfec8eb)) + + +### Bug Fixes + +* **char:** always stop highlights in insert mode ([64e5129](https://github.com/folke/flash.nvim/commit/64e51292e83e7ce409248fd07ff00b51a993a6c0)) + +## [1.4.1](https://github.com/folke/flash.nvim/compare/v1.4.0...v1.4.1) (2023-06-23) + + +### Bug Fixes + +* **char:** don't repeat on motion char when executing a macro. See [#34](https://github.com/folke/flash.nvim/issues/34) ([674cfb4](https://github.com/folke/flash.nvim/commit/674cfb43e5424a5405661ba632810bacfc0a9c37)) + +## [1.4.0](https://github.com/folke/flash.nvim/compare/v1.3.0...v1.4.0) (2023-06-23) + + +### Features + +* **char:** tfTF now behave like clever-f when repeating the motion. Fixes [#26](https://github.com/folke/flash.nvim/issues/26) ([97c3a99](https://github.com/folke/flash.nvim/commit/97c3a993e60ebdd42c7671af07620f705ee6378f)) +* **config:** allow custom window filters. Added non-focusable windows by default ([e6ee00d](https://github.com/folke/flash.nvim/commit/e6ee00d4e76edac8cbcabe0f442a5ec34450d1f6)) + + +### Bug Fixes + +* **config:** dont show flash in cmp_menu ([29c35de](https://github.com/folke/flash.nvim/commit/29c35dec5f81504ee63a39fec90597222620af0a)) +* **treesitter:** always disable incremental mode for treesitter. Fixes [#27](https://github.com/folke/flash.nvim/issues/27) ([6e84716](https://github.com/folke/flash.nvim/commit/6e8471673a7158a8820986f6aad770a912a66eed)) + +## [1.3.0](https://github.com/folke/flash.nvim/compare/v1.2.0...v1.3.0) (2023-06-22) + + +### Features + +* **char:** optionally disable some ftFT keymaps ([3e27d9a](https://github.com/folke/flash.nvim/commit/3e27d9ab07b9363b0ecb94645eae38909f7baa5a)) +* **config:** show labels for current jump target by default ([0dcc00e](https://github.com/folke/flash.nvim/commit/0dcc00ea6a3b312b8e081f3f582adc26a4721ac7)) +* **search:** optional trigger character. Not recommended. Fixes [#21](https://github.com/folke/flash.nvim/issues/21) ([cb0977c](https://github.com/folke/flash.nvim/commit/cb0977cd0f7cec4573ee1210edc2032739866b2b)) + + +### Bug Fixes + +* **char:** fixup for keys ([81469aa](https://github.com/folke/flash.nvim/commit/81469aaf3ccf15d7c942bbd9144f2c06f68fe1ee)) +* **treesitter:** properly deal with nodes ending at col 0. Fixes [#17](https://github.com/folke/flash.nvim/issues/17) ([6cd4414](https://github.com/folke/flash.nvim/commit/6cd44145f75392fbfe67700b59517dbf8324bd21)) +* **treesitter:** removed debug print ([0fabd1b](https://github.com/folke/flash.nvim/commit/0fabd1b4ddea5754576ccc09a515867a3ac129ce)) + +## [1.2.0](https://github.com/folke/flash.nvim/compare/v1.1.0...v1.2.0) (2023-06-21) + + +### Features + +* added example that matches beginning of words only ([1e2c61d](https://github.com/folke/flash.nvim/commit/1e2c61d8db882cc001fcebff9eba2549336ce87a)) +* **config:** setting to disable uppercase labels. Fixes [#11](https://github.com/folke/flash.nvim/issues/11) ([13d7b3e](https://github.com/folke/flash.nvim/commit/13d7b3e70cadc7e4d64f818a04fbca2b33ac1d4f)) +* **labeler:** reuse only lowercase labels by default. See [#11](https://github.com/folke/flash.nvim/issues/11) ([8f0b9ed](https://github.com/folke/flash.nvim/commit/8f0b9ed656d7b92eb0d60c34b6a5bd3803cc0e0b)) + +## [1.1.0](https://github.com/folke/flash.nvim/compare/v1.0.0...v1.1.0) (2023-06-21) + + +### Features + +* added config.jump.autojump. Fixes [#5](https://github.com/folke/flash.nvim/issues/5) ([1808d3e](https://github.com/folke/flash.nvim/commit/1808d3ebb6ea5810957b8f8e32aab8f4e9e7f14c)) +* added custom actions on label select ([eb0769f](https://github.com/folke/flash.nvim/commit/eb0769ff38001ed3eead9e54289b7f63387e1525)) +* added example plugin that shows a diagnostic at a certain label without moving the cursor ([7a9bd11](https://github.com/folke/flash.nvim/commit/7a9bd118a3b4d2829d4718c26d8af21b36ebfb87)) + + +### Bug Fixes + +* **config:** get mode opts from options instead of defaults. Fixes [#4](https://github.com/folke/flash.nvim/issues/4) ([41fab4c](https://github.com/folke/flash.nvim/commit/41fab4cb225d9233fec7987bb1445c9768d84caf)) +* **diag:** always hide when done ([226c634](https://github.com/folke/flash.nvim/commit/226c634e3db6f02eb734d37c16d729bae41a77ef)) +* **jump:** register and history should use pattern.search instead of pattern. Fixes [#7](https://github.com/folke/flash.nvim/issues/7) ([a11cf6a](https://github.com/folke/flash.nvim/commit/a11cf6ad205dd2493d2af6643bc20bef925004f5)) +* **treesitter:** make treesitter plugin work with custom labels. Fixes [#9](https://github.com/folke/flash.nvim/issues/9) ([3fac625](https://github.com/folke/flash.nvim/commit/3fac6253fd59e7c32300e6209c8f1e60ea8a3c81)) + +## 1.0.0 (2023-06-21) + + +### Features + +* abort_pattern can now be false ([e036667](https://github.com/folke/flash.nvim/commit/e0366678e337df4a93c0704e77a6909e617950c3)) +* add option to save loc to jumplist before jump ([0aae816](https://github.com/folke/flash.nvim/commit/0aae816ef419ad4554a784a07fe239aeee9a6934)) +* added char searches, f, F, t, T ([06839d8](https://github.com/folke/flash.nvim/commit/06839d8ac7f2ca42b639fc8f90e2c655234bba9a)) +* added config for forward/wrap ([b9649bd](https://github.com/folke/flash.nvim/commit/b9649bd226da89bcbef7fb6b27e5d3a08d0fe6b4)) +* added config.search.regex ([bda1be0](https://github.com/folke/flash.nvim/commit/bda1be00bca62d7ebd9de4c7848e7c70a65f2f91)) +* added ffi based searcher. Finally 100% correct end pos for matches ([46b41d1](https://github.com/folke/flash.nvim/commit/46b41d13d6943443c20b3bf87fdf8eb495fee4c2)) +* added option to label the first match ([63b75ed](https://github.com/folke/flash.nvim/commit/63b75ed8dcaec7efaf6e67e3913b59f2e614f043)) +* added optional backdrop ([2172a90](https://github.com/folke/flash.nvim/commit/2172a907aeba4a3961e399044a2f4ca1087e044d)) +* added support for label offsets and label styles ([3e9f630](https://github.com/folke/flash.nvim/commit/3e9f630ce04bdda14669592bc5d36af594077e95)) +* added treesitter command ([fd9bd80](https://github.com/folke/flash.nvim/commit/fd9bd8015a7df2b8aedc294bc517264837d218f9)) +* advance for results ([9d70126](https://github.com/folke/flash.nvim/commit/9d70126e09b20125752a43c1e26041eecc4f721c)) +* allow to always render search highlight to prevent flickering when updating ui ([ff0e25f](https://github.com/folke/flash.nvim/commit/ff0e25f63ae98f7ab2735293a40f02e8cfc85d2a)) +* **charsearch:** close on <esc> ([ee3228a](https://github.com/folke/flash.nvim/commit/ee3228af6b82204cb03c317526a0212229953272)) +* **charsearch:** make char search dot repeatable ([91485c1](https://github.com/folke/flash.nvim/commit/91485c12b2685bdde097b2351725e973cc2e1274)) +* dont stabalize labels for treesitter ([b20ad86](https://github.com/folke/flash.nvim/commit/b20ad8652f34a477f6bdab912258b176aeebdd0d)) +* expose commands on main module ([70130d2](https://github.com/folke/flash.nvim/commit/70130d29a3c4c8d90d96caae5871d0cc19e3f283)) +* fuzzy matching ([7407dd6](https://github.com/folke/flash.nvim/commit/7407dd679c90986dff09b22a690feb52aa5ea31a)) +* highlight groups config ([313e252](https://github.com/folke/flash.nvim/commit/313e252ecfd3252d2e39d7c012b0674388d65f8d)) +* **highlight:** added support for before/after labels ([d0133d2](https://github.com/folke/flash.nvim/commit/d0133d2966695f063f8909a0d80a97cd90d2848c)) +* **highlight:** allow diffrerent namespaces for highlight ([2649b18](https://github.com/folke/flash.nvim/commit/2649b1888fd84d1cee0ab3d5fdc5e82c8a5f391c)) +* initial version ([22913c6](https://github.com/folke/flash.nvim/commit/22913c65a1c960e3449c813824351abbdb327c7b)) +* jump position (start, end or range) ([335a5a9](https://github.com/folke/flash.nvim/commit/335a5a91222680f92c585c16d94d183a57b13c8d)) +* labels are now skipped based on regex searches to be able to fully support regex patterns ([e704d88](https://github.com/folke/flash.nvim/commit/e704d8846fd2d8189f127f2b080812ed2518fdc4)) +* lazy require ([171b9ff](https://github.com/folke/flash.nvim/commit/171b9ff3034b2afb5ad9a0420a906a8c597037ba)) +* make all the things repeatable without needing `expr=true` ([ec3a8ac](https://github.com/folke/flash.nvim/commit/ec3a8ac3ebfc9957c65620bcae7d91ed38a334b2)) +* much improved repeat api ([2f76471](https://github.com/folke/flash.nvim/commit/2f76471f3a178234a3b08a6ae5ca9f8082bacc46)) +* multiple modes ([ed1150f](https://github.com/folke/flash.nvim/commit/ed1150f2cabcca526894423de8fda74d756a0cff)) +* **pattern:** custom pattern functions ([b9e13f2](https://github.com/folke/flash.nvim/commit/b9e13f2c8cf603e70d7eff410ffbd88c8611d6d0)) +* **repeat:** show warning when keymap expr didn't execute. probably because expr=true was not used ([789d3b2](https://github.com/folke/flash.nvim/commit/789d3b22610fe8f45f7451afac5b1921db852dd6)) +* stable labels ([3e6b345](https://github.com/folke/flash.nvim/commit/3e6b345f590c70c83ccbe720afc268ba9ba3b442)) +* **state:** proper support for incremental search ([8a0fa11](https://github.com/folke/flash.nvim/commit/8a0fa1147cfad21b6576ee4d9320de6e78b1c24c)) +* **state:** state will now automatically updated on changedtick or when buf changes ([60193cb](https://github.com/folke/flash.nvim/commit/60193cb3aa384938bd7b9be8d5b594c0ebe0c867)) +* **state:** update matcher when view changed ([9f4dc50](https://github.com/folke/flash.nvim/commit/9f4dc506987a9381d67e3e602e9950a622c76276)) +* treesitter node jumping ([119643f](https://github.com/folke/flash.nvim/commit/119643fd672a959233da3b1c3b61de965dfe765b)) +* **treesitter:** ; & , to expand/descrease selection ([6551d97](https://github.com/folke/flash.nvim/commit/6551d970d270bda2b6bf9be09944196d8782a329)) +* **treesitter:** allow custom options ([d9d5e75](https://github.com/folke/flash.nvim/commit/d9d5e7558e11e1cdb9a48c87e442444664b3c0cf)) +* util module for dot-repeat ([e6f02b1](https://github.com/folke/flash.nvim/commit/e6f02b15608b625266f1564b8005c36d56f7fa71)) + + +### Bug Fixes + +* allow space in string ([f1b8691](https://github.com/folke/flash.nvim/commit/f1b86913daa85aef94fae07e03cab8ccf7f9137f)) +* calculate target in update ([f3f915a](https://github.com/folke/flash.nvim/commit/f3f915ac0b5c4ff4598dd73b65cff9f9c0d3e57b)) +* **charsearch:** inclusive/exclusive operator pending fix ([fb1867c](https://github.com/folke/flash.nvim/commit/fb1867c908e488a7dbe1a83f7cad57a826bf977f)) +* **charsearch:** mode ([b8c18ba](https://github.com/folke/flash.nvim/commit/b8c18baad82145fe097db4d13440d44a9005f30d)) +* **config:** register and nohlsearch are disables by default ([f20d2f8](https://github.com/folke/flash.nvim/commit/f20d2f8d34142ec1674284f582e57f6f66a99cd8)) +* dont set search register by default ([f7352f7](https://github.com/folke/flash.nvim/commit/f7352f7c7e90e3e0b5818b398d543e2146f045ad)) +* fixup for first -> current ([43b96c6](https://github.com/folke/flash.nvim/commit/43b96c69d7f7fd97f5c9ec316cf8ee3c30badc48)) +* **highlight:** highlight each line of the backdrop separately to fix extmark priorities ([08bf4f6](https://github.com/folke/flash.nvim/commit/08bf4f6fad136743c6791f6db4659f314fe69104)) +* **highlight:** proper nvim 0.10.0 check for inline extmarks ([6da8904](https://github.com/folke/flash.nvim/commit/6da8904ed698069395baab49b168b37b0a35b839)) +* **highlight:** set cursorline hl group ([8715685](https://github.com/folke/flash.nvim/commit/8715685cd24e5d5727442063ce7e347bb0b567b7)) +* **init:** pass opts to config ([0627e2f](https://github.com/folke/flash.nvim/commit/0627e2f09e9a7b26d8755d8e4994e38cfdd58ba5)) +* **jump:** check pattern for jump target ([d29d5fc](https://github.com/folke/flash.nvim/commit/d29d5fc41dcbe6e7c751c30d28b362400f45f870)) +* **jump:** dont change ordering of matches when calculating labels ([8611eab](https://github.com/folke/flash.nvim/commit/8611eaba93c080175026dbd41fac9a7a9e535637)) +* **jump:** fix inclusive/excusive for operator pending mode ([99c99a7](https://github.com/folke/flash.nvim/commit/99c99a75754f107eef0cbc23f4745e7c0d784848)) +* **jump:** make it all work in operator pending mode ([1005faa](https://github.com/folke/flash.nvim/commit/1005faa1c21dcaa37232fd93c2ef7c71fc3b3099)) +* **labeler:** dont include end_pos to re-use stable labels ([dadca0e](https://github.com/folke/flash.nvim/commit/dadca0e75335dd9e3083ea11cd41f1d197ebe1a7)) +* **labels:** fixed some edge cases regarding labels ([124d1b6](https://github.com/folke/flash.nvim/commit/124d1b6900b30f5a2e1c60bc6a4ac0e1a0de889a)) +* **matcher:** match end_pos when finding relative to another match ([0794ba2](https://github.com/folke/flash.nvim/commit/0794ba238ada4ab820940a63dbd54f29679d10be)) +* **matcher:** ordering ([e46a629](https://github.com/folke/flash.nvim/commit/e46a629c679a022e822a4243ad15ebcb1474412d)) +* **search:** added support for match ([e3e3958](https://github.com/folke/flash.nvim/commit/e3e3958c871bf46d808605afbdcf07cafb1e98e4)) +* **search:** cleanup and add search to history ([175ffd9](https://github.com/folke/flash.nvim/commit/175ffd9960fdaf65b00d00782fdc0505678e9162)) +* **search:** dont add labels if too many results ([959af4e](https://github.com/folke/flash.nvim/commit/959af4e095df35a62200a35b1f3aef2e652c8dd5)) +* **searcher:** don't use ignore case for labels and skip both upper/lower when needed ([1b48511](https://github.com/folke/flash.nvim/commit/1b48511efa0834deb07461b3e076c8bafb66d876)) +* **searcher:** finally was able to properly fix finding ends of matches ([4251741](https://github.com/folke/flash.nvim/commit/4251741114187823b94957dfad40e7dcfa82ac2d)) +* **searcher:** skip all labels when pattern ends with escape character ([530038d](https://github.com/folke/flash.nvim/commit/530038d05925373feddb4742dcf742401532ed69)) +* **searcher:** use vim.regex to get match end and added support for multi-line ([ffcdf20](https://github.com/folke/flash.nvim/commit/ffcdf20d7ff15117a984244e1258794fef10efe8)) +* **search:** properly deal with invalid patterns ([46d6655](https://github.com/folke/flash.nvim/commit/46d6655891238b569ffa8c0334f2bdae39adc21e)) +* **search:** skip all labels when pattern is invalid regex ([9bb8079](https://github.com/folke/flash.nvim/commit/9bb8079c82dccccc54ec107e243f845e996a492b)) +* **state:** better operator pending mode detection for search ([f53dd07](https://github.com/folke/flash.nvim/commit/f53dd076af1e2f6f9374f6c26c8f474c83c5815d)) +* **state:** force update when making visible ([ada913d](https://github.com/folke/flash.nvim/commit/ada913d2a1cbdb765493419202a48addaf2c873a)) +* **state:** keep states as a key in a table to prevent double work ([4a6ea98](https://github.com/folke/flash.nvim/commit/4a6ea985c88eb8503515131f422d4cb856db4b3b)) +* **state:** results sorting ([9da4d28](https://github.com/folke/flash.nvim/commit/9da4d285d0d453fc9eb0f3bfcebde68be334f066)) +* **state:** stop searching when max matches reached ([4245e49](https://github.com/folke/flash.nvim/commit/4245e49fb878459bb5a074c9c8023900baf321cd)) +* **treesitter:** use state.pos as cursor to get nodes ([d1185ad](https://github.com/folke/flash.nvim/commit/d1185add4a6f624b150896ba4eb32855ef9e35b7)) + + +### Performance Improvements + +* cache window matches ([678532a](https://github.com/folke/flash.nvim/commit/678532a956562a53887a5dda2e4513c3ba216de9)) +* lazy require/setup ([2bbf721](https://github.com/folke/flash.nvim/commit/2bbf72189c875509ac37130f56fc4cb6e0f65139)) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/LICENSE b/config/neovim/store/lazy-plugins/flash.nvim/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/config/neovim/store/lazy-plugins/flash.nvim/README.md b/config/neovim/store/lazy-plugins/flash.nvim/README.md new file mode 100644 index 00000000..0f83f756 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/README.md @@ -0,0 +1,705 @@ +# ⚡flash.nvim + +`flash.nvim` lets you navigate your code with search labels, +enhanced character motions, and Treesitter integration. + + + + + + + + + + + + + + + + + + +
Search IntegrationStandalone Jump
+ + + +
f, t, F, TTreesitter
+ + + +
+ +## ✨ Features + +- 🔍 **Search Integration**: integrate **flash.nvim** with your regular + search using `/` or `?`. Labels appear next to the matches, + allowing you to quickly jump to any location. Labels are + guaranteed not to exist as a continuation of the search pattern. +- ⌨️ **type as many characters as you want** before using a jump label. +- ⚡ **Enhanced `f`, `t`, `F`, `T` motions** +- 🌳 **Treesitter Integration**: all parents of the Treesitter node + under your cursor are highlighted with a label for quick selection + of a specific Treesitter node. +- 🎯 **Jump Mode**: a standalone jumping mode similar to search +- 🔎 **Search Modes**: `exact`, `search` (regex), and `fuzzy` search modes +- 🪟 **Multi Window** jumping +- 🌐 **Remote Actions**: perform motions in remote locations +- ⚫ **dot-repeatable** jumps +- 📡 **highly extensible**: check the [examples](https://github.com/folke/flash.nvim#-examples) + +## 📋 Requirements + +- Neovim >= **0.8.0** (needs to be built with **LuaJIT**) + +## 📦 Installation + +Install the plugin with your preferred package manager: + +[lazy.nvim](https://github.com/folke/lazy.nvim): + + + +```lua +{ + "folke/flash.nvim", + event = "VeryLazy", + ---@type Flash.Config + opts = {}, + -- stylua: ignore + keys = { + { "s", mode = { "n", "x", "o" }, function() require("flash").jump() end, desc = "Flash" }, + { "S", mode = { "n", "x", "o" }, function() require("flash").treesitter() end, desc = "Flash Treesitter" }, + { "r", mode = "o", function() require("flash").remote() end, desc = "Remote Flash" }, + { "R", mode = { "o", "x" }, function() require("flash").treesitter_search() end, desc = "Treesitter Search" }, + { "", mode = { "c" }, function() require("flash").toggle() end, desc = "Toggle Flash Search" }, + }, +} +``` + + + +> ⚠️ When creating the keymaps manually either use a lua function like +> `function() require("flash").jump() end` as the **rhs**, or a string +> like `lua require("flash").jump()`. +> **DO NOT** use `:lua`, since that will break **_dot-repeat_** + +## ⚙️ Configuration + +**flash.nvim** is highly configurable. Please refer to the default settings below. + +
Default Settings + + + +```lua +{ + -- labels = "abcdefghijklmnopqrstuvwxyz", + labels = "asdfghjklqwertyuiopzxcvbnm", + search = { + -- search/jump in all windows + multi_window = true, + -- search direction + forward = true, + -- when `false`, find only matches in the given direction + wrap = true, + ---@type Flash.Pattern.Mode + -- Each mode will take ignorecase and smartcase into account. + -- * exact: exact match + -- * search: regular search + -- * fuzzy: fuzzy search + -- * fun(str): custom function that returns a pattern + -- For example, to only match at the beginning of a word: + -- mode = function(str) + -- return "\\<" .. str + -- end, + mode = "exact", + -- behave like `incsearch` + incremental = false, + -- Excluded filetypes and custom window filters + ---@type (string|fun(win:window))[] + exclude = { + "notify", + "cmp_menu", + "noice", + "flash_prompt", + function(win) + -- exclude non-focusable windows + return not vim.api.nvim_win_get_config(win).focusable + end, + }, + -- Optional trigger character that needs to be typed before + -- a jump label can be used. It's NOT recommended to set this, + -- unless you know what you're doing + trigger = "", + -- max pattern length. If the pattern length is equal to this + -- labels will no longer be skipped. When it exceeds this length + -- it will either end in a jump or terminate the search + max_length = false, ---@type number|false + }, + jump = { + -- save location in the jumplist + jumplist = true, + -- jump position + pos = "start", ---@type "start" | "end" | "range" + -- add pattern to search history + history = false, + -- add pattern to search register + register = false, + -- clear highlight after jump + nohlsearch = false, + -- automatically jump when there is only one match + autojump = false, + -- You can force inclusive/exclusive jumps by setting the + -- `inclusive` option. By default it will be automatically + -- set based on the mode. + inclusive = nil, ---@type boolean? + -- jump position offset. Not used for range jumps. + -- 0: default + -- 1: when pos == "end" and pos < current position + offset = nil, ---@type number + }, + label = { + -- allow uppercase labels + uppercase = true, + -- add any labels with the correct case here, that you want to exclude + exclude = "", + -- add a label for the first match in the current window. + -- you can always jump to the first match with `` + current = true, + -- show the label after the match + after = true, ---@type boolean|number[] + -- show the label before the match + before = false, ---@type boolean|number[] + -- position of the label extmark + style = "overlay", ---@type "eol" | "overlay" | "right_align" | "inline" + -- flash tries to re-use labels that were already assigned to a position, + -- when typing more characters. By default only lower-case labels are re-used. + reuse = "lowercase", ---@type "lowercase" | "all" | "none" + -- for the current window, label targets closer to the cursor first + distance = true, + -- minimum pattern length to show labels + -- Ignored for custom labelers. + min_pattern_length = 0, + -- Enable this to use rainbow colors to highlight labels + -- Can be useful for visualizing Treesitter ranges. + rainbow = { + enabled = false, + -- number between 1 and 9 + shade = 5, + }, + -- With `format`, you can change how the label is rendered. + -- Should return a list of `[text, highlight]` tuples. + ---@class Flash.Format + ---@field state Flash.State + ---@field match Flash.Match + ---@field hl_group string + ---@field after boolean + ---@type fun(opts:Flash.Format): string[][] + format = function(opts) + return { { opts.match.label, opts.hl_group } } + end, + }, + highlight = { + -- show a backdrop with hl FlashBackdrop + backdrop = true, + -- Highlight the search matches + matches = true, + -- extmark priority + priority = 5000, + groups = { + match = "FlashMatch", + current = "FlashCurrent", + backdrop = "FlashBackdrop", + label = "FlashLabel", + }, + }, + -- action to perform when picking a label. + -- defaults to the jumping logic depending on the mode. + ---@type fun(match:Flash.Match, state:Flash.State)|nil + action = nil, + -- initial pattern to use when opening flash + pattern = "", + -- When `true`, flash will try to continue the last search + continue = false, + -- Set config to a function to dynamically change the config + config = nil, ---@type fun(opts:Flash.Config)|nil + -- You can override the default options for a specific mode. + -- Use it with `require("flash").jump({mode = "forward"})` + ---@type table + modes = { + -- options used when flash is activated through + -- a regular search with `/` or `?` + search = { + -- when `true`, flash will be activated during regular search by default. + -- You can always toggle when searching with `require("flash").toggle()` + enabled = false, + highlight = { backdrop = false }, + jump = { history = true, register = true, nohlsearch = true }, + search = { + -- `forward` will be automatically set to the search direction + -- `mode` is always set to `search` + -- `incremental` is set to `true` when `incsearch` is enabled + }, + }, + -- options used when flash is activated through + -- `f`, `F`, `t`, `T`, `;` and `,` motions + char = { + enabled = true, + -- dynamic configuration for ftFT motions + config = function(opts) + -- autohide flash when in operator-pending mode + opts.autohide = opts.autohide or (vim.fn.mode(true):find("no") and vim.v.operator == "y") + + -- disable jump labels when not enabled, when using a count, + -- or when recording/executing registers + opts.jump_labels = opts.jump_labels + and vim.v.count == 0 + and vim.fn.reg_executing() == "" + and vim.fn.reg_recording() == "" + + -- Show jump labels only in operator-pending mode + -- opts.jump_labels = vim.v.count == 0 and vim.fn.mode(true):find("o") + end, + -- hide after jump when not using jump labels + autohide = false, + -- show jump labels + jump_labels = false, + -- set to `false` to use the current line only + multi_line = true, + -- When using jump labels, don't use these keys + -- This allows using those keys directly after the motion + label = { exclude = "hjkliardc" }, + -- by default all keymaps are enabled, but you can disable some of them, + -- by removing them from the list. + -- If you rather use another key, you can map them + -- to something else, e.g., { [";"] = "L", [","] = H } + keys = { "f", "F", "t", "T", ";", "," }, + ---@alias Flash.CharActions table + -- The direction for `prev` and `next` is determined by the motion. + -- `left` and `right` are always left and right. + char_actions = function(motion) + return { + [";"] = "next", -- set to `right` to always go right + [","] = "prev", -- set to `left` to always go left + -- clever-f style + [motion:lower()] = "next", + [motion:upper()] = "prev", + -- jump2d style: same case goes next, opposite case goes prev + -- [motion] = "next", + -- [motion:match("%l") and motion:upper() or motion:lower()] = "prev", + } + end, + search = { wrap = false }, + highlight = { backdrop = true }, + jump = { register = false }, + }, + -- options used for treesitter selections + -- `require("flash").treesitter()` + treesitter = { + labels = "abcdefghijklmnopqrstuvwxyz", + jump = { pos = "range" }, + search = { incremental = false }, + label = { before = true, after = true, style = "inline" }, + highlight = { + backdrop = false, + matches = false, + }, + }, + treesitter_search = { + jump = { pos = "range" }, + search = { multi_window = true, wrap = true, incremental = false }, + remote_op = { restore = true }, + label = { before = true, after = true, style = "inline" }, + }, + -- options used for remote flash + remote = { + remote_op = { restore = true, motion = true }, + }, + }, + -- options for the floating window that shows the prompt, + -- for regular jumps + prompt = { + enabled = true, + prefix = { { "⚡", "FlashPromptIcon" } }, + win_config = { + relative = "editor", + width = 1, -- when <=1 it's a percentage of the editor width + height = 1, + row = -1, -- when negative it's an offset from the bottom + col = 0, -- when negative it's an offset from the right + zindex = 1000, + }, + }, + -- options for remote operator pending mode + remote_op = { + -- restore window views and cursor position + -- after doing a remote operation + restore = false, + -- For `jump.pos = "range"`, this setting is ignored. + -- `true`: always enter a new motion when doing a remote operation + -- `false`: use the window's cursor position and jump target + -- `nil`: act as `true` for remote windows, `false` for the current window + motion = false, + }, +} +``` + + + +
+ +## 🚀 Usage + +- **Treesitter**: `require("flash").treesitter(opts?)` opens **flash** in **Treesitter** mode + - use a jump label, or use `;` and `,` to increase/decrease the selection +- **regular search**: search as you normally do, but enhanced with jump labels. + You need to set `opts.modes.search.enabled = true`, or toggle it with `require("flash").toggle()` +- `f`, `t`, `F`, `T` motions: + - After typing `f{char}` or `F{char},` you can repeat the motion with `f` + or go to the previous match with `F` to undo a jump. + - Similarly, after typing `t{char}` or `T{char},` you can repeat the motion + with `t` or go to the previous match with `T`. + - You can also go to the next match with `;` or previous match with `,` + - Any highlights clear automatically when moving, changing buffers, + or pressing ``. +- **toggle Search**: `require("flash").toggle(boolean?)` + - toggles **flash** on or off while using regular search +- **Treesitter Search**: `require("flash").treesitter_search(opts?)` opens **flash** in **Treesitter Search** mode + - combination of **Treesitter** and **Search** modes + - do something like `yR` + - you can now start typing a search pattern. + - arround your matches, all the surrounding Treesitter nodes will be labeled. + - select a label to perform the operator on the new selection +- **remote**: `require("flash").remote(opts?)` opens **flash** in **remote** mode + + - equivalent to: + + ```lua + require("flash").jump({ + remote_op = { + restore = true, + motion = true, + }, + }) + ``` + + - this is only useful in operator pending mode. + - For example, press `yr` to start yanking and open flash + - select a label to set the cursor position + - perform any motion, like `iw` or even start flash Treesitter with `S` + - the yank will be performed on the new selection + - you'll be back in the original window / position + - You can also configure the `remote_op` options by default, so that `ys`, + behaves like `yr` for remote operations + + ```lua + require("flash").jump({ + remote_op = { + restore = true, + motion = nil, + }, + }) + ``` + +- **jump**: `require("flash").jump(opts?)` opens **flash** with the given options + - type any number of characters before typing a jump label +- **VS Code**: some functionality is changed/disabled when running **flash** in **VS Code**: + - `prompt` is disabled + - `highlights` are set to different defaults that will actually work in VS Code + +## 📡 API + +The options for `require("flash").jump(opts?)`, are the same as +those in the config section, but can additionally have the following fields: + +- `matcher`: a custom function that generates matches for a given window +- `labeler`: a custom function to label matches + +You can also add labels in the `matcher` function and then set `labeler` +to an empty function `labeler = function() end` + +
Type Definitions + +```typescript +type FlashMatcher = (win: number, state: FlashState) => FlashMatch[]; +type FlashLabeler = (matches: FlashMatch[], state: FlashState) => void; + +interface FlashMatch { + win: number; + pos: [number, number]; // (1,0)-indexed + end_pos: [number, number]; // (1,0)-indexed + label?: string | false; // set to false to never show a label for this match + highlight?: boolean; // override opts.highlight.matches for this match +} + +// Check the code for the full definition +// of Flash.State at `lua/flash/state.lua` +type FlashState = {}; +``` + +
+ +## 💡 Examples + +
Forward search only + +```lua +require("flash").jump({ + search = { forward = true, wrap = false, multi_window = false }, +}) +``` + +
+ +
Backward search only + +```lua +require("flash").jump({ + search = { forward = false, wrap = false, multi_window = false }, +}) +``` + +
+ +
Show diagnostics at target, without changing cursor position + +```lua +require("flash").jump({ + action = function(match, state) + vim.api.nvim_win_call(match.win, function() + vim.api.nvim_win_set_cursor(match.win, match.pos) + vim.diagnostic.open_float() + end) + state:restore() + end, +}) + +-- More advanced example that also highlights diagnostics: +require("flash").jump({ + matcher = function(win) + ---@param diag Diagnostic + return vim.tbl_map(function(diag) + return { + pos = { diag.lnum + 1, diag.col }, + end_pos = { diag.end_lnum + 1, diag.end_col - 1 }, + } + end, vim.diagnostic.get(vim.api.nvim_win_get_buf(win))) + end, + action = function(match, state) + vim.api.nvim_win_call(match.win, function() + vim.api.nvim_win_set_cursor(match.win, match.pos) + vim.diagnostic.open_float() + end) + state:restore() + end, +}) +``` + +
+ +
Match beginning of words only + +```lua +require("flash").jump({ + search = { + mode = function(str) + return "\\<" .. str + end, + }, +}) +``` + +
+ +
Initialize flash with the word under the cursor + +```lua +require("flash").jump({ + pattern = vim.fn.expand(""), +}) +``` + +
+ +
Jump to a line + +```lua +require("flash").jump({ + search = { mode = "search", max_length = 0 }, + label = { after = { 0, 0 } }, + pattern = "^" +}) +``` + +
+ +
Select any word + +```lua +require("flash").jump({ + pattern = ".", -- initialize pattern with any char + search = { + mode = function(pattern) + -- remove leading dot + if pattern:sub(1, 1) == "." then + pattern = pattern:sub(2) + end + -- return word pattern and proper skip pattern + return ([[\<%s\w*\>]]):format(pattern), ([[\<%s]]):format(pattern) + end, + }, + -- select the range + jump = { pos = "range" }, +}) +``` + +
+ +
f, t, F, T with labels + +Use the options below: + +```lua +{ + modes = { + char = { + jump_labels = true + } + } +} +``` + +
+ +
Telescope integration + +This will allow you to use `s` in normal mode +and `` in insert mode, to jump to a label in Telescope results. + +```lua +{ + "nvim-telescope/telescope.nvim", + optional = true, + opts = function(_, opts) + local function flash(prompt_bufnr) + require("flash").jump({ + pattern = "^", + label = { after = { 0, 0 } }, + search = { + mode = "search", + exclude = { + function(win) + return vim.bo[vim.api.nvim_win_get_buf(win)].filetype ~= "TelescopeResults" + end, + }, + }, + action = function(match) + local picker = require("telescope.actions.state").get_current_picker(prompt_bufnr) + picker:set_selection(match.pos[1] - 1) + end, + }) + end + opts.defaults = vim.tbl_deep_extend("force", opts.defaults or {}, { + mappings = { + n = { s = flash }, + i = { [""] = flash }, + }, + }) + end, + } +``` + +
+ +
Continue last search + +```lua +require("flash").jump({continue = true}) +``` + +
+ +
+ 2-char jump, similar to + + mini.jump2d + + or + + HopWord (hop.nvim) + + + +```lua +local Flash = require("flash") + +---@param opts Flash.Format +local function format(opts) + -- always show first and second label + return { + { opts.match.label1, "FlashMatch" }, + { opts.match.label2, "FlashLabel" }, + } +end + +Flash.jump({ + search = { mode = "search" }, + label = { after = false, before = { 0, 0 }, uppercase = false, format = format }, + pattern = [[\<]], + action = function(match, state) + state:hide() + Flash.jump({ + search = { max_length = 0 }, + highlight = { matches = false }, + label = { format = format }, + matcher = function(win) + -- limit matches to the current label + return vim.tbl_filter(function(m) + return m.label == match.label and m.win == win + end, state.results) + end, + labeler = function(matches) + for _, m in ipairs(matches) do + m.label = m.label2 -- use the second label + end + end, + }) + end, + labeler = function(matches, state) + local labels = state:labels() + for m, match in ipairs(matches) do + match.label1 = labels[math.floor((m - 1) / #labels) + 1] + match.label2 = labels[(m - 1) % #labels + 1] + match.label = match.label1 + end + end, +}) +``` + +
+ +## 🌈 Highlights + +| Group | Default | Description | +| ----------------- | ------------ | -------------- | +| `FlashBackdrop` | `Comment` | backdrop | +| `FlashMatch` | `Search` | search matches | +| `FlashCurrent` | `IncSearch` | current match | +| `FlashLabel` | `Substitute` | jump label | +| `FlashPrompt` | `MsgArea` | prompt | +| `FlashPromptIcon` | `Special` | prompt icon | +| `FlashCursor` | `Cursor` | cursor | + +## 📦 Alternatives + +- [leap.nvim](https://github.com/ggandor/leap.nvim) +- [lightspeed.nvim](https://github.com/ggandor/lightspeed.nvim) +- [vim-sneak](https://github.com/justinmk/vim-sneak) +- [mini.jump](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-jump.md) +- [mini.jump2d](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-jump2d.md) +- [hop.nvim](https://github.com/phaazon/hop.nvim) +- [pounce.nvim](https://github.com/rlane/pounce.nvim) +- [sj.nvim](https://github.com/woosaaahh/sj.nvim) +- [nvim-treehopper](https://github.com/mfussenegger/nvim-treehopper) +- [flit.nvim](https://github.com/ggandor/flit.nvim) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/doc/flash.nvim.txt b/config/neovim/store/lazy-plugins/flash.nvim/doc/flash.nvim.txt new file mode 100644 index 00000000..9692c2b2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/doc/flash.nvim.txt @@ -0,0 +1,671 @@ +*flash.nvim.txt* For Neovim >= 0.8.0 Last change: 2024 May 14 + +============================================================================== +Table of Contents *flash.nvim-table-of-contents* + +1. flash.nvim |flash.nvim-flash.nvim| + - Features |flash.nvim-flash.nvim-features| + - Requirements |flash.nvim-flash.nvim-requirements| + - Installation |flash.nvim-flash.nvim-installation| + - Configuration |flash.nvim-flash.nvim-configuration| + - Usage |flash.nvim-flash.nvim-usage| + - API |flash.nvim-flash.nvim-api| + - Examples |flash.nvim-flash.nvim-examples| + - Highlights |flash.nvim-flash.nvim-highlights| + - Alternatives |flash.nvim-flash.nvim-alternatives| + +============================================================================== +1. flash.nvim *flash.nvim-flash.nvim* + +`flash.nvim` lets you navigate your code with search labels, enhanced character +motions, and Treesitter integration. + +Search IntegrationStandalone Jumpf, t, F, TTreesitter +FEATURES *flash.nvim-flash.nvim-features* + +- **Search Integration**integrate **flash.nvim** with your regular + search using `/` or `?`. Labels appear next to the matches, + allowing you to quickly jump to any location. Labels are + guaranteed not to exist as a continuation of the search pattern. +- **type as many characters as you want** before using a jump label. +- **Enhanced f, t, F, T motions** +- **Treesitter Integration**all parents of the Treesitter node + under your cursor are highlighted with a label for quick selection + of a specific Treesitter node. +- **Jump Mode**a standalone jumping mode similar to search +- **Search Modes**`exact`, `search` (regex), and `fuzzy` search modes +- **Multi Window** jumping +- **Remote Actions**perform motions in remote locations +- **dot-repeatable** jumps +- **highly extensible**check the examples + + +REQUIREMENTS *flash.nvim-flash.nvim-requirements* + +- Neovim >= **0.8.0** (needs to be built with **LuaJIT**) + + +INSTALLATION *flash.nvim-flash.nvim-installation* + +Install the plugin with your preferred package manager: + +lazy.nvim + +>lua + { + "folke/flash.nvim", + event = "VeryLazy", + ---@type Flash.Config + opts = {}, + -- stylua: ignore + keys = { + { "s", mode = { "n", "x", "o" }, function() require("flash").jump() end, desc = "Flash" }, + { "S", mode = { "n", "x", "o" }, function() require("flash").treesitter() end, desc = "Flash Treesitter" }, + { "r", mode = "o", function() require("flash").remote() end, desc = "Remote Flash" }, + { "R", mode = { "o", "x" }, function() require("flash").treesitter_search() end, desc = "Treesitter Search" }, + { "", mode = { "c" }, function() require("flash").toggle() end, desc = "Toggle Flash Search" }, + }, + } +< + + + When creating the keymaps manually either use a lua function like `function() + require("flash").jump() end` as the **rhs**, or a string like `lua + require("flash").jump()`. **DO NOT** use `:lua`, since that will break + **dot-repeat** + +CONFIGURATION *flash.nvim-flash.nvim-configuration* + +**flash.nvim** is highly configurable. Please refer to the default settings +below. + +Default Settings ~ + +>lua + { + -- labels = "abcdefghijklmnopqrstuvwxyz", + labels = "asdfghjklqwertyuiopzxcvbnm", + search = { + -- search/jump in all windows + multi_window = true, + -- search direction + forward = true, + -- when `false`, find only matches in the given direction + wrap = true, + ---@type Flash.Pattern.Mode + -- Each mode will take ignorecase and smartcase into account. + -- * exact: exact match + -- * search: regular search + -- * fuzzy: fuzzy search + -- * fun(str): custom function that returns a pattern + -- For example, to only match at the beginning of a word: + -- mode = function(str) + -- return "\\<" .. str + -- end, + mode = "exact", + -- behave like `incsearch` + incremental = false, + -- Excluded filetypes and custom window filters + ---@type (string|fun(win:window))[] + exclude = { + "notify", + "cmp_menu", + "noice", + "flash_prompt", + function(win) + -- exclude non-focusable windows + return not vim.api.nvim_win_get_config(win).focusable + end, + }, + -- Optional trigger character that needs to be typed before + -- a jump label can be used. It's NOT recommended to set this, + -- unless you know what you're doing + trigger = "", + -- max pattern length. If the pattern length is equal to this + -- labels will no longer be skipped. When it exceeds this length + -- it will either end in a jump or terminate the search + max_length = false, ---@type number|false + }, + jump = { + -- save location in the jumplist + jumplist = true, + -- jump position + pos = "start", ---@type "start" | "end" | "range" + -- add pattern to search history + history = false, + -- add pattern to search register + register = false, + -- clear highlight after jump + nohlsearch = false, + -- automatically jump when there is only one match + autojump = false, + -- You can force inclusive/exclusive jumps by setting the + -- `inclusive` option. By default it will be automatically + -- set based on the mode. + inclusive = nil, ---@type boolean? + -- jump position offset. Not used for range jumps. + -- 0: default + -- 1: when pos == "end" and pos < current position + offset = nil, ---@type number + }, + label = { + -- allow uppercase labels + uppercase = true, + -- add any labels with the correct case here, that you want to exclude + exclude = "", + -- add a label for the first match in the current window. + -- you can always jump to the first match with `` + current = true, + -- show the label after the match + after = true, ---@type boolean|number[] + -- show the label before the match + before = false, ---@type boolean|number[] + -- position of the label extmark + style = "overlay", ---@type "eol" | "overlay" | "right_align" | "inline" + -- flash tries to re-use labels that were already assigned to a position, + -- when typing more characters. By default only lower-case labels are re-used. + reuse = "lowercase", ---@type "lowercase" | "all" | "none" + -- for the current window, label targets closer to the cursor first + distance = true, + -- minimum pattern length to show labels + -- Ignored for custom labelers. + min_pattern_length = 0, + -- Enable this to use rainbow colors to highlight labels + -- Can be useful for visualizing Treesitter ranges. + rainbow = { + enabled = false, + -- number between 1 and 9 + shade = 5, + }, + -- With `format`, you can change how the label is rendered. + -- Should return a list of `[text, highlight]` tuples. + ---@class Flash.Format + ---@field state Flash.State + ---@field match Flash.Match + ---@field hl_group string + ---@field after boolean + ---@type fun(opts:Flash.Format): string[][] + format = function(opts) + return { { opts.match.label, opts.hl_group } } + end, + }, + highlight = { + -- show a backdrop with hl FlashBackdrop + backdrop = true, + -- Highlight the search matches + matches = true, + -- extmark priority + priority = 5000, + groups = { + match = "FlashMatch", + current = "FlashCurrent", + backdrop = "FlashBackdrop", + label = "FlashLabel", + }, + }, + -- action to perform when picking a label. + -- defaults to the jumping logic depending on the mode. + ---@type fun(match:Flash.Match, state:Flash.State)|nil + action = nil, + -- initial pattern to use when opening flash + pattern = "", + -- When `true`, flash will try to continue the last search + continue = false, + -- Set config to a function to dynamically change the config + config = nil, ---@type fun(opts:Flash.Config)|nil + -- You can override the default options for a specific mode. + -- Use it with `require("flash").jump({mode = "forward"})` + ---@type table + modes = { + -- options used when flash is activated through + -- a regular search with `/` or `?` + search = { + -- when `true`, flash will be activated during regular search by default. + -- You can always toggle when searching with `require("flash").toggle()` + enabled = false, + highlight = { backdrop = false }, + jump = { history = true, register = true, nohlsearch = true }, + search = { + -- `forward` will be automatically set to the search direction + -- `mode` is always set to `search` + -- `incremental` is set to `true` when `incsearch` is enabled + }, + }, + -- options used when flash is activated through + -- `f`, `F`, `t`, `T`, `;` and `,` motions + char = { + enabled = true, + -- dynamic configuration for ftFT motions + config = function(opts) + -- autohide flash when in operator-pending mode + opts.autohide = opts.autohide or (vim.fn.mode(true):find("no") and vim.v.operator == "y") + + -- disable jump labels when not enabled, when using a count, + -- or when recording/executing registers + opts.jump_labels = opts.jump_labels + and vim.v.count == 0 + and vim.fn.reg_executing() == "" + and vim.fn.reg_recording() == "" + + -- Show jump labels only in operator-pending mode + -- opts.jump_labels = vim.v.count == 0 and vim.fn.mode(true):find("o") + end, + -- hide after jump when not using jump labels + autohide = false, + -- show jump labels + jump_labels = false, + -- set to `false` to use the current line only + multi_line = true, + -- When using jump labels, don't use these keys + -- This allows using those keys directly after the motion + label = { exclude = "hjkliardc" }, + -- by default all keymaps are enabled, but you can disable some of them, + -- by removing them from the list. + -- If you rather use another key, you can map them + -- to something else, e.g., { [";"] = "L", [","] = H } + keys = { "f", "F", "t", "T", ";", "," }, + ---@alias Flash.CharActions table + -- The direction for `prev` and `next` is determined by the motion. + -- `left` and `right` are always left and right. + char_actions = function(motion) + return { + [";"] = "next", -- set to `right` to always go right + [","] = "prev", -- set to `left` to always go left + -- clever-f style + [motion:lower()] = "next", + [motion:upper()] = "prev", + -- jump2d style: same case goes next, opposite case goes prev + -- [motion] = "next", + -- [motion:match("%l") and motion:upper() or motion:lower()] = "prev", + } + end, + search = { wrap = false }, + highlight = { backdrop = true }, + jump = { register = false }, + }, + -- options used for treesitter selections + -- `require("flash").treesitter()` + treesitter = { + labels = "abcdefghijklmnopqrstuvwxyz", + jump = { pos = "range" }, + search = { incremental = false }, + label = { before = true, after = true, style = "inline" }, + highlight = { + backdrop = false, + matches = false, + }, + }, + treesitter_search = { + jump = { pos = "range" }, + search = { multi_window = true, wrap = true, incremental = false }, + remote_op = { restore = true }, + label = { before = true, after = true, style = "inline" }, + }, + -- options used for remote flash + remote = { + remote_op = { restore = true, motion = true }, + }, + }, + -- options for the floating window that shows the prompt, + -- for regular jumps + prompt = { + enabled = true, + prefix = { { "⚡", "FlashPromptIcon" } }, + win_config = { + relative = "editor", + width = 1, -- when <=1 it's a percentage of the editor width + height = 1, + row = -1, -- when negative it's an offset from the bottom + col = 0, -- when negative it's an offset from the right + zindex = 1000, + }, + }, + -- options for remote operator pending mode + remote_op = { + -- restore window views and cursor position + -- after doing a remote operation + restore = false, + -- For `jump.pos = "range"`, this setting is ignored. + -- `true`: always enter a new motion when doing a remote operation + -- `false`: use the window's cursor position and jump target + -- `nil`: act as `true` for remote windows, `false` for the current window + motion = false, + }, + } +< + + +USAGE *flash.nvim-flash.nvim-usage* + +- **Treesitter**`require("flash").treesitter(opts?)` opens **flash** in + **Treesitter** mode + - use a jump label, or use `;` and `,` to increase/decrease the selection +- **regular search**search as you normally do, but enhanced with jump labels. You + need to set `opts.modes.search.enabled = true`, or toggle it with + `require("flash").toggle()` +- `f`, `t`, `F`, `T` motions: + - After typing `f{char}` or `F{char},` you can repeat the motion with `f` + or go to the previous match with `F` to undo a jump. + - Similarly, after typing `t{char}` or `T{char},` you can repeat the motion + with `t` or go to the previous match with `T`. + - You can also go to the next match with `;` or previous match with `,` + - Any highlights clear automatically when moving, changing buffers, + or pressing ``. +- **toggle Search**`require("flash").toggle(boolean?)` + - toggles **flash** on or off while using regular search +- **Treesitter Search**`require("flash").treesitter_search(opts?)` opens + **flash** in **Treesitter Search** mode + - combination of **Treesitter** and **Search** modes + - do something like `yR` + - you can now start typing a search pattern. + - arround your matches, all the surrounding Treesitter nodes will be labeled. + - select a label to perform the operator on the new selection +- **remote**`require("flash").remote(opts?)` opens **flash** in **remote** mode + - equivalent to: + >lua + require("flash").jump({ + remote_op = { + restore = true, + motion = true, + }, + }) + < + - this is only useful in operator pending mode. + - For example, press `yr` to start yanking and open flash + - select a label to set the cursor position + - perform any motion, like `iw` or even start flash Treesitter with `S` + - the yank will be performed on the new selection + - you’ll be back in the original window / position + - You can also configure the `remote_op` options by default, so that `ys`, + behaves like `yr` for remote operations + >lua + require("flash").jump({ + remote_op = { + restore = true, + motion = nil, + }, + }) + < +- **jump**`require("flash").jump(opts?)` opens **flash** with the given options + - type any number of characters before typing a jump label +- **VS Code**some functionality is changed/disabled when running **flash** in + **VS Code** + - `prompt`is disabled + - `highlights` are set to different defaults that will actually work in VS Code + + +API *flash.nvim-flash.nvim-api* + +The options for `require("flash").jump(opts?)`, are the same as those in the +config section, but can additionally have the following fields: + +- `matcher`a custom function that generates matches for a given window +- `labeler`a custom function to label matches + +You can also add labels in the `matcher` function and then set `labeler` to an +empty function `labeler = function() end` + +Type Definitions ~ + +>typescript + type FlashMatcher = (win: number, state: FlashState) => FlashMatch[]; + type FlashLabeler = (matches: FlashMatch[], state: FlashState) => void; + + interface FlashMatch { + win: number; + pos: [number, number]; // (1,0)-indexed + end_pos: [number, number]; // (1,0)-indexed + label?: string | false; // set to false to never show a label for this match + highlight?: boolean; // override opts.highlight.matches for this match + } + + // Check the code for the full definition + // of Flash.State at `lua/flash/state.lua` + type FlashState = {}; +< + + +EXAMPLES *flash.nvim-flash.nvim-examples* + +Forward search only ~ + +>lua + require("flash").jump({ + search = { forward = true, wrap = false, multi_window = false }, + }) +< + +Backward search only ~ + +>lua + require("flash").jump({ + search = { forward = false, wrap = false, multi_window = false }, + }) +< + +Show diagnostics at target, without changing cursor position ~ + +>lua + require("flash").jump({ + action = function(match, state) + vim.api.nvim_win_call(match.win, function() + vim.api.nvim_win_set_cursor(match.win, match.pos) + vim.diagnostic.open_float() + end) + state:restore() + end, + }) + + -- More advanced example that also highlights diagnostics: + require("flash").jump({ + matcher = function(win) + ---@param diag Diagnostic + return vim.tbl_map(function(diag) + return { + pos = { diag.lnum + 1, diag.col }, + end_pos = { diag.end_lnum + 1, diag.end_col - 1 }, + } + end, vim.diagnostic.get(vim.api.nvim_win_get_buf(win))) + end, + action = function(match, state) + vim.api.nvim_win_call(match.win, function() + vim.api.nvim_win_set_cursor(match.win, match.pos) + vim.diagnostic.open_float() + end) + state:restore() + end, + }) +< + +Match beginning of words only ~ + +>lua + require("flash").jump({ + search = { + mode = function(str) + return "\\<" .. str + end, + }, + }) +< + +Initialize flash with the word under the cursor ~ + +>lua + require("flash").jump({ + pattern = vim.fn.expand(""), + }) +< + +Jump to a line ~ + +>lua + require("flash").jump({ + search = { mode = "search", max_length = 0 }, + label = { after = { 0, 0 } }, + pattern = "^" + }) +< + +Select any word ~ + +>lua + require("flash").jump({ + pattern = ".", -- initialize pattern with any char + search = { + mode = function(pattern) + -- remove leading dot + if pattern:sub(1, 1) == "." then + pattern = pattern:sub(2) + end + -- return word pattern and proper skip pattern + return ([[\<%s\w*\>]]):format(pattern), ([[\<%s]]):format(pattern) + end, + }, + -- select the range + jump = { pos = "range" }, + }) +< + +f, t, F, T with labels ~ + +Use the options below: + +>lua + { + modes = { + char = { + jump_labels = true + } + } + } +< + +Telescope integration ~ + +This will allow you to use `s` in normal mode and `` in insert mode, to +jump to a label in Telescope results. + +>lua + { + "nvim-telescope/telescope.nvim", + optional = true, + opts = function(_, opts) + local function flash(prompt_bufnr) + require("flash").jump({ + pattern = "^", + label = { after = { 0, 0 } }, + search = { + mode = "search", + exclude = { + function(win) + return vim.bo[vim.api.nvim_win_get_buf(win)].filetype ~= "TelescopeResults" + end, + }, + }, + action = function(match) + local picker = require("telescope.actions.state").get_current_picker(prompt_bufnr) + picker:set_selection(match.pos[1] - 1) + end, + }) + end + opts.defaults = vim.tbl_deep_extend("force", opts.defaults or {}, { + mappings = { + n = { s = flash }, + i = { [""] = flash }, + }, + }) + end, + } +< + +Continue last search ~ + +>lua + require("flash").jump({continue = true}) +< + +2-char jump, similar to + +mini.jump2d + +or + +HopWord (hop.nvim) + ~ + +>lua + local Flash = require("flash") + + ---@param opts Flash.Format + local function format(opts) + -- always show first and second label + return { + { opts.match.label1, "FlashMatch" }, + { opts.match.label2, "FlashLabel" }, + } + end + + Flash.jump({ + search = { mode = "search" }, + label = { after = false, before = { 0, 0 }, uppercase = false, format = format }, + pattern = [[\<]], + action = function(match, state) + state:hide() + Flash.jump({ + search = { max_length = 0 }, + highlight = { matches = false }, + label = { format = format }, + matcher = function(win) + -- limit matches to the current label + return vim.tbl_filter(function(m) + return m.label == match.label and m.win == win + end, state.results) + end, + labeler = function(matches) + for _, m in ipairs(matches) do + m.label = m.label2 -- use the second label + end + end, + }) + end, + labeler = function(matches, state) + local labels = state:labels() + for m, match in ipairs(matches) do + match.label1 = labels[math.floor((m - 1) / #labels) + 1] + match.label2 = labels[(m - 1) % #labels + 1] + match.label = match.label1 + end + end, + }) +< + + +HIGHLIGHTS *flash.nvim-flash.nvim-highlights* + + Group Default Description + ----------------- ------------ ---------------- + FlashBackdrop Comment backdrop + FlashMatch Search search matches + FlashCurrent IncSearch current match + FlashLabel Substitute jump label + FlashPrompt MsgArea prompt + FlashPromptIcon Special prompt icon + FlashCursor Cursor cursor + +ALTERNATIVES *flash.nvim-flash.nvim-alternatives* + +- leap.nvim +- lightspeed.nvim +- vim-sneak +- mini.jump +- mini.jump2d +- hop.nvim +- pounce.nvim +- sj.nvim +- nvim-treehopper +- flit.nvim + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/cache.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/cache.lua new file mode 100644 index 00000000..3b4cb502 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/cache.lua @@ -0,0 +1,149 @@ +local Pattern = require("flash.search.pattern") +local Pos = require("flash.search.pos") +local Util = require("flash.util") + +---@class Flash.State.Window +---@field win number +---@field buf number +---@field topline number +---@field botline number +---@field changedtick number + +---@class Flash.Cache +---@field state Flash.State +---@field pattern Flash.Pattern +---@field wins Flash.State.Window[] +local M = {} +M.__index = M + +---@type table +M.cache = setmetatable({}, { __mode = "k" }) + +---@param state Flash.State +function M.new(state) + local self = setmetatable({}, M) + self.state = state + self.pattern = Pattern.new("", state.opts.search.mode, state.opts.search.trigger) + self.wins = {} + return self +end + +---@return boolean dirty Returns true when dirty +function M:update() + local dirty = false + + if self.pattern ~= self.state.pattern then + self.pattern = self.state.pattern:clone() + dirty = true + M.cache = {} + end + + local win = vim.api.nvim_get_current_win() + if self.state.win ~= win then + self.state.win = win + self.state.pos = Pos(win) + self.state.restore_windows = Util.save_layout() + M.cache = {} + dirty = true + end + + self:_update_wins() + + for _, w in ipairs(self.state.wins) do + if self:_dirty(w) then + dirty = true + end + end + return dirty +end + +---@param win window +function M:get_state(win) + local window = self:get(win) + if not window then + return + end + if M.cache[window] then + return M.cache[window] + end + + local from = Pos({ window.topline, 0 }) + local to = Pos({ window.botline + 1, 0 }) + + if not self.state.opts.search.wrap and win == self.state.win then + if self.state.opts.search.forward then + from = self.state.pos + else + to = self.state.pos + end + end + + local matcher = self.state:get_matcher(win) + if matcher.update then + matcher:update() + end + + M.cache[window] = { + matches = matcher:get({ from = from, to = to }), + } + return M.cache[window] +end + +---@param win window +---@return Flash.State.Window +function M:get(win) + return self.wins[win] +end + +function M:_update_wins() + -- prioritize current window + self.state.wins = { self.state.win } + + if self.state.opts.search.multi_window then + local keep_current = false + + ---@param w window + self.state.wins = vim.tbl_filter(function(w) + local buf = vim.api.nvim_win_get_buf(w) + local ft = vim.bo[buf].filetype + + for _, exclude in ipairs(self.state.opts.search.exclude) do + if type(exclude) == "string" and exclude == ft then + return false + elseif type(exclude) == "function" and exclude(w) then + return false + end + end + if w == self.state.win then + keep_current = true + return false + end + return true + end, vim.api.nvim_tabpage_list_wins(0)) + if keep_current then + table.insert(self.state.wins, 1, self.state.win) + end + end +end + +---@param win window +function M:_dirty(win) + local info = vim.fn.getwininfo(win)[1] + local buf = vim.api.nvim_win_get_buf(win) + + ---@type Flash.State.Window + local state = { + win = win, + buf = buf, + cursor = vim.api.nvim_win_get_cursor(win), + topline = info.topline, + botline = info.botline, + changedtick = vim.b[buf].changedtick, + } + if not vim.deep_equal(state, self.wins[win]) then + self.wins[win] = state + return true + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/commands.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/commands.lua new file mode 100644 index 00000000..6a7b1bce --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/commands.lua @@ -0,0 +1,36 @@ +local Repeat = require("flash.repeat") + +---@class Flash.Commands +local M = {} + +---@param opts? Flash.State.Config +function M.jump(opts) + local state = Repeat.get_state("jump", opts) + state:loop() + return state +end + +---@param opts? Flash.State.Config +function M.treesitter(opts) + return require("flash.plugins.treesitter").jump(opts) +end + +---@param opts? Flash.State.Config +function M.treesitter_search(opts) + return require("flash.plugins.treesitter").search(opts) +end + +---@param opts? Flash.State.Config +function M.remote(opts) + local Config = require("flash.config") + opts = Config.get({ mode = "remote" }, opts) + return M.jump(opts) +end + +---@param enabled? boolean +function M.toggle(enabled) + local Search = require("flash.plugins.search") + return Search.toggle(enabled) +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/config.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/config.lua new file mode 100644 index 00000000..06af1787 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/config.lua @@ -0,0 +1,345 @@ +---@type Flash.Config +local M = {} + +---@class Flash.Config +---@field mode? string +---@field enabled? boolean +---@field ns? string +---@field config? fun(opts:Flash.Config) +local defaults = { + -- labels = "abcdefghijklmnopqrstuvwxyz", + labels = "asdfghjklqwertyuiopzxcvbnm", + search = { + -- search/jump in all windows + multi_window = true, + -- search direction + forward = true, + -- when `false`, find only matches in the given direction + wrap = true, + ---@type Flash.Pattern.Mode + -- Each mode will take ignorecase and smartcase into account. + -- * exact: exact match + -- * search: regular search + -- * fuzzy: fuzzy search + -- * fun(str): custom function that returns a pattern + -- For example, to only match at the beginning of a word: + -- mode = function(str) + -- return "\\<" .. str + -- end, + mode = "exact", + -- behave like `incsearch` + incremental = false, + -- Excluded filetypes and custom window filters + ---@type (string|fun(win:window))[] + exclude = { + "notify", + "cmp_menu", + "noice", + "flash_prompt", + function(win) + -- exclude non-focusable windows + return not vim.api.nvim_win_get_config(win).focusable + end, + }, + -- Optional trigger character that needs to be typed before + -- a jump label can be used. It's NOT recommended to set this, + -- unless you know what you're doing + trigger = "", + -- max pattern length. If the pattern length is equal to this + -- labels will no longer be skipped. When it exceeds this length + -- it will either end in a jump or terminate the search + max_length = false, ---@type number|false + }, + jump = { + -- save location in the jumplist + jumplist = true, + -- jump position + pos = "start", ---@type "start" | "end" | "range" + -- add pattern to search history + history = false, + -- add pattern to search register + register = false, + -- clear highlight after jump + nohlsearch = false, + -- automatically jump when there is only one match + autojump = false, + -- You can force inclusive/exclusive jumps by setting the + -- `inclusive` option. By default it will be automatically + -- set based on the mode. + inclusive = nil, ---@type boolean? + -- jump position offset. Not used for range jumps. + -- 0: default + -- 1: when pos == "end" and pos < current position + offset = nil, ---@type number + }, + label = { + -- allow uppercase labels + uppercase = true, + -- add any labels with the correct case here, that you want to exclude + exclude = "", + -- add a label for the first match in the current window. + -- you can always jump to the first match with `` + current = true, + -- show the label after the match + after = true, ---@type boolean|number[] + -- show the label before the match + before = false, ---@type boolean|number[] + -- position of the label extmark + style = "overlay", ---@type "eol" | "overlay" | "right_align" | "inline" + -- flash tries to re-use labels that were already assigned to a position, + -- when typing more characters. By default only lower-case labels are re-used. + reuse = "lowercase", ---@type "lowercase" | "all" | "none" + -- for the current window, label targets closer to the cursor first + distance = true, + -- minimum pattern length to show labels + -- Ignored for custom labelers. + min_pattern_length = 0, + -- Enable this to use rainbow colors to highlight labels + -- Can be useful for visualizing Treesitter ranges. + rainbow = { + enabled = false, + -- number between 1 and 9 + shade = 5, + }, + -- With `format`, you can change how the label is rendered. + -- Should return a list of `[text, highlight]` tuples. + ---@class Flash.Format + ---@field state Flash.State + ---@field match Flash.Match + ---@field hl_group string + ---@field after boolean + ---@type fun(opts:Flash.Format): string[][] + format = function(opts) + return { { opts.match.label, opts.hl_group } } + end, + }, + highlight = { + -- show a backdrop with hl FlashBackdrop + backdrop = true, + -- Highlight the search matches + matches = true, + -- extmark priority + priority = 5000, + groups = { + match = "FlashMatch", + current = "FlashCurrent", + backdrop = "FlashBackdrop", + label = "FlashLabel", + }, + }, + -- action to perform when picking a label. + -- defaults to the jumping logic depending on the mode. + ---@type fun(match:Flash.Match, state:Flash.State)|nil + action = nil, + -- initial pattern to use when opening flash + pattern = "", + -- When `true`, flash will try to continue the last search + continue = false, + -- Set config to a function to dynamically change the config + config = nil, ---@type fun(opts:Flash.Config)|nil + -- You can override the default options for a specific mode. + -- Use it with `require("flash").jump({mode = "forward"})` + ---@type table + modes = { + -- options used when flash is activated through + -- a regular search with `/` or `?` + search = { + -- when `true`, flash will be activated during regular search by default. + -- You can always toggle when searching with `require("flash").toggle()` + enabled = false, + highlight = { backdrop = false }, + jump = { history = true, register = true, nohlsearch = true }, + search = { + -- `forward` will be automatically set to the search direction + -- `mode` is always set to `search` + -- `incremental` is set to `true` when `incsearch` is enabled + }, + }, + -- options used when flash is activated through + -- `f`, `F`, `t`, `T`, `;` and `,` motions + char = { + enabled = true, + -- dynamic configuration for ftFT motions + config = function(opts) + -- autohide flash when in operator-pending mode + opts.autohide = opts.autohide or (vim.fn.mode(true):find("no") and vim.v.operator == "y") + + -- disable jump labels when not enabled, when using a count, + -- or when recording/executing registers + opts.jump_labels = opts.jump_labels + and vim.v.count == 0 + and vim.fn.reg_executing() == "" + and vim.fn.reg_recording() == "" + + -- Show jump labels only in operator-pending mode + -- opts.jump_labels = vim.v.count == 0 and vim.fn.mode(true):find("o") + end, + -- hide after jump when not using jump labels + autohide = false, + -- show jump labels + jump_labels = false, + -- set to `false` to use the current line only + multi_line = true, + -- When using jump labels, don't use these keys + -- This allows using those keys directly after the motion + label = { exclude = "hjkliardc" }, + -- by default all keymaps are enabled, but you can disable some of them, + -- by removing them from the list. + -- If you rather use another key, you can map them + -- to something else, e.g., { [";"] = "L", [","] = H } + keys = { "f", "F", "t", "T", ";", "," }, + ---@alias Flash.CharActions table + -- The direction for `prev` and `next` is determined by the motion. + -- `left` and `right` are always left and right. + char_actions = function(motion) + return { + [";"] = "next", -- set to `right` to always go right + [","] = "prev", -- set to `left` to always go left + -- clever-f style + [motion:lower()] = "next", + [motion:upper()] = "prev", + -- jump2d style: same case goes next, opposite case goes prev + -- [motion] = "next", + -- [motion:match("%l") and motion:upper() or motion:lower()] = "prev", + } + end, + search = { wrap = false }, + highlight = { backdrop = true }, + jump = { register = false }, + }, + -- options used for treesitter selections + -- `require("flash").treesitter()` + treesitter = { + labels = "abcdefghijklmnopqrstuvwxyz", + jump = { pos = "range" }, + search = { incremental = false }, + label = { before = true, after = true, style = "inline" }, + highlight = { + backdrop = false, + matches = false, + }, + }, + treesitter_search = { + jump = { pos = "range" }, + search = { multi_window = true, wrap = true, incremental = false }, + remote_op = { restore = true }, + label = { before = true, after = true, style = "inline" }, + }, + -- options used for remote flash + remote = { + remote_op = { restore = true, motion = true }, + }, + }, + -- options for the floating window that shows the prompt, + -- for regular jumps + prompt = { + enabled = true, + prefix = { { "⚡", "FlashPromptIcon" } }, + win_config = { + relative = "editor", + width = 1, -- when <=1 it's a percentage of the editor width + height = 1, + row = -1, -- when negative it's an offset from the bottom + col = 0, -- when negative it's an offset from the right + zindex = 1000, + }, + }, + -- options for remote operator pending mode + remote_op = { + -- restore window views and cursor position + -- after doing a remote operation + restore = false, + -- For `jump.pos = "range"`, this setting is ignored. + -- `true`: always enter a new motion when doing a remote operation + -- `false`: use the window's cursor position and jump target + -- `nil`: act as `true` for remote windows, `false` for the current window + motion = false, + }, +} + +---@type Flash.Config +local options + +---@param opts? Flash.Config +function M.setup(opts) + opts = opts or {} + opts.mode = nil + options = {} + options = M.get(opts) + + require("flash.plugins.search").setup() + if options.modes.char.enabled then + require("flash.plugins.char").setup() + end +end + +---@param ... Flash.Config|Flash.State.Config|nil +---@return Flash.State.Config +function M.get(...) + if options == nil then + M.setup() + end + ---@type Flash.Config[] + local all = { {}, defaults, options or {} } + + ---@type table + local modes = {} + + for i = 1, select("#", ...) do + ---@type Flash.Config? + local opts = select(i, ...) + if type(opts) == "string" then + opts = options.modes[opts] + end + if opts then + table.insert(all, opts) + local idx = #all + while opts.mode and not modes[opts.mode] do + modes[opts.mode or ""] = true + opts = options.modes[opts.mode] or {} + table.insert(all, idx, opts) + end + end + end + + -- backward compatibility + for _, o in ipairs(all) do + if o.highlight and o.highlight.label then + o.label = vim.tbl_deep_extend("force", o.label or {}, o.highlight.label) + ---@diagnostic disable-next-line: no-unknown + o.highlight.label = nil + vim.notify_once( + "flash: `opts.highlight.label` is deprecated, use `opts.label` instead", + vim.log.levels.WARN + ) + end + for _, field in ipairs({ "autohide", "jump_labels" }) do + if type(o[field]) == "function" then + local motion = require("flash.plugins.char").motion + ---@diagnostic disable-next-line: no-unknown + o[field] = o[field](motion) + end + end + end + + local ret = vim.tbl_deep_extend("force", unpack(all)) + ---@cast ret Flash.State.Config + + if type(ret.config) == "function" then + ret.config(ret) + end + + if vim.g.vscode then + ret.prompt.enabled = false + end + return ret +end + +return setmetatable(M, { + __index = function(_, key) + if options == nil then + M.setup() + end + return options[key] + end, +}) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/docs.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/docs.lua new file mode 100644 index 00000000..6225e38d --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/docs.lua @@ -0,0 +1,32 @@ +local Docs = require("lazy.docs") + +local M = {} + +function M.update() + local config = Docs.extract("lua/flash/config.lua", "\nlocal defaults = ({.-\n})") + config = config:gsub("%s*debug = false.\n", "\n") + Docs.save({ + config = config, + setup = Docs.extract("lua/flash/docs.lua", "function M%.suggested%(%)\n%s*return (.-)\nend"), + }) +end + +function M.suggested() + return { + "folke/flash.nvim", + event = "VeryLazy", + ---@type Flash.Config + opts = {}, + -- stylua: ignore + keys = { + { "s", mode = { "n", "x", "o" }, function() require("flash").jump() end, desc = "Flash" }, + { "S", mode = { "n", "x", "o" }, function() require("flash").treesitter() end, desc = "Flash Treesitter" }, + { "r", mode = "o", function() require("flash").remote() end, desc = "Remote Flash" }, + { "R", mode = { "o", "x" }, function() require("flash").treesitter_search() end, desc = "Treesitter Search" }, + { "", mode = { "c" }, function() require("flash").toggle() end, desc = "Toggle Flash Search" }, + }, + } +end +M.update() + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/hacks.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/hacks.lua new file mode 100644 index 00000000..8ebe94f7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/hacks.lua @@ -0,0 +1,68 @@ +local Pos = require("flash.search.pos") + +local M = {} + +---@type ffi.namespace* +local C +local incsearch_state = {} + +local function _ffi() + if not C then + local ffi = require("ffi") + ffi.cdef([[ + int search_match_endcol; + int no_mapping; + unsigned int search_match_lines; + void setcursor_mayforce(bool force); + ]]) + C = ffi.C + end + return C +end + +---@private +---@param from Pos +function M.get_end_pos(from) + _ffi() + local ret = Pos({ + from[1] + C.search_match_lines, + math.max(0, C.search_match_endcol - 1), + }) + local line = vim.api.nvim_buf_get_lines(0, ret[1] - 1, ret[1], false)[1] + local char_idx = vim.fn.charidx(line, ret[2]) + ret[2] = vim.fn.byteidx(line, char_idx) + return ret +end + +function M.save_incsearch_state() + _ffi() + incsearch_state = { + match_endcol = C.search_match_endcol, + match_lines = C.search_match_lines, + } +end + +function M.mappings_enabled() + _ffi() + return C.no_mapping == 0 +end + +function M.setcursor(force) + if vim.api.nvim__redraw then + vim.api.nvim__redraw({ cursor = true }) + else + if force == nil then + force = false + end + _ffi() + return C.setcursor_mayforce(force) + end +end + +function M.restore_incsearch_state() + _ffi() + C.search_match_endcol = incsearch_state.match_endcol + C.search_match_lines = incsearch_state.match_lines +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/highlight.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/highlight.lua new file mode 100644 index 00000000..a666f285 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/highlight.lua @@ -0,0 +1,216 @@ +local M = {} + +function M.clear(ns) + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1) + end +end + +function M.setup() + if vim.g.vscode then + local hls = { + FlashBackdrop = { fg = "#545c7e" }, + FlashCurrent = { bg = "#ff966c", fg = "#1b1d2b" }, + FlashLabel = { bg = "#ff007c", bold = true, fg = "#c8d3f5" }, + FlashMatch = { bg = "#3e68d7", fg = "#c8d3f5" }, + FlashCursor = { reverse = true }, + } + for hl_group, hl in pairs(hls) do + hl.default = true + vim.api.nvim_set_hl(0, hl_group, hl) + end + else + local links = { + FlashBackdrop = "Comment", + FlashMatch = "Search", + FlashCurrent = "IncSearch", + FlashLabel = "Substitute", + FlashPrompt = "MsgArea", + FlashPromptIcon = "Special", + FlashCursor = "Cursor", + } + for hl_group, link in pairs(links) do + vim.api.nvim_set_hl(0, hl_group, { link = link, default = true }) + end + end +end +M.setup() + +---@param state Flash.State +function M.backdrop(state) + for _, win in ipairs(state.wins) do + local info = vim.fn.getwininfo(win)[1] + local buf = vim.api.nvim_win_get_buf(win) + local from = { info.topline, 0 } + local to = { info.botline + 1, 0 } + if state.win == win and not state.opts.search.wrap then + if state.opts.search.forward then + from = { state.pos[1], state.pos[2] + 1 } + else + to = state.pos + end + end + -- we need to create a backdrop for each line because of the way + -- extmarks priority rendering works + for line = from[1], to[1] do + vim.api.nvim_buf_set_extmark(buf, state.ns, line - 1, line == from[1] and from[2] or 0, { + hl_group = state.opts.highlight.groups.backdrop, + end_row = line == to[1] and line - 1 or line, + hl_eol = line ~= to[1], + end_col = line == to[1] and to[2] or from[2], + priority = state.opts.highlight.priority, + strict = false, + }) + end + end +end + +---@param state Flash.State +function M.cursor(state) + for _, win in ipairs(state.wins) do + local cursor = vim.api.nvim_win_get_cursor(win) + local buf = vim.api.nvim_win_get_buf(win) + vim.api.nvim_buf_set_extmark(buf, state.ns, cursor[1] - 1, cursor[2], { + hl_group = "FlashCursor", + end_col = cursor[2] + 1, + priority = state.opts.highlight.priority + 3, + strict = false, + }) + end +end + +---@param state Flash.State +function M.update(state) + M.clear(state.ns) + + if state.opts.highlight.backdrop then + M.backdrop(state) + end + + local style = state.opts.label.style + if style == "inline" and vim.fn.has("nvim-0.10.0") == 0 then + style = "overlay" + end + + local after = state.opts.label.after + after = after == true and { 0, 1 } or after + ---@cast after number[] + local before = state.opts.label.before + before = before == true and { 0, -1 } or before + ---@cast before number[] + + if style == "inline" and before then + before[2] = before[2] + 1 + end + + local target = state.target + + ---@type table + local extmarks = {} + + ---@param match Flash.Match + ---@param pos number[] + ---@param offset number[] + ---@param is_after boolean + local function label(match, pos, offset, is_after) + local buf = vim.api.nvim_win_get_buf(match.win) + local cursor = vim.api.nvim_win_get_cursor(match.win) + local pos2 = require("flash.util").offset_pos(buf, pos, offset) + local row, col = pos2[1] - 1, pos2[2] + -- dont show the label if the cursor is on the same position + -- in the same window + -- and the label is not a range + if + cursor[1] == row + 1 + and cursor[2] == col + and match.win == state.win + and state.opts.jump.pos ~= "range" + then + return + end + if match.fold then + -- set the row to the fold start + row = match.fold - 1 + col = 0 + end + + local hl_group = state.opts.highlight.groups.label + if state.rainbow then + hl_group = state.rainbow:get(match) + elseif + -- set hl_group to current if the match is the current target + -- and the target is a single character + target + and target.pos[1] == row + 1 + and target.pos[2] == col + and target.pos == target.end_pos + then + hl_group = state.opts.highlight.groups.current + end + if match.label == "" then + -- when empty label, highlight the position + vim.api.nvim_buf_set_extmark(buf, state.ns, row, col, { + hl_group = hl_group, + end_row = row, + end_col = col + 1, + strict = false, + priority = state.opts.highlight.priority + 2, + }) + else + -- else highlight the label + local key = buf .. ":" .. row .. ":" .. col + extmarks[key] = extmarks[key] or { buf = buf, row = row, col = col, text = {} } + local text = state.opts.label.format({ + state = state, + match = match, + hl_group = hl_group, + after = is_after, + }) + for i = #text, 1, -1 do + table.insert(extmarks[key].text, 1, text[i]) + end + end + end + + for _, match in ipairs(state.results) do + local buf = vim.api.nvim_win_get_buf(match.win) + + local highlight = state.opts.highlight.matches + if match.highlight ~= nil then + highlight = match.highlight + end + + if highlight then + vim.api.nvim_buf_set_extmark(buf, state.ns, match.pos[1] - 1, match.pos[2], { + end_row = match.end_pos[1] - 1, + end_col = match.end_pos[2] + 1, + hl_group = target and match.pos == target.pos and state.opts.highlight.groups.current + or state.opts.highlight.groups.match, + strict = false, + priority = state.opts.highlight.priority + 1, + }) + end + end + + for _, match in ipairs(state.results) do + if match.label and after then + label(match, match.end_pos, after, true) + end + if match.label and before then + label(match, match.pos, before, false) + end + end + + for _, extmark in pairs(extmarks) do + vim.api.nvim_buf_set_extmark(extmark.buf, state.ns, extmark.row, extmark.col, { + virt_text = extmark.text, + virt_text_pos = style, + strict = false, + priority = state.opts.highlight.priority + 2, + }) + end + + M.cursor(state) +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/init.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/init.lua new file mode 100644 index 00000000..42f1305d --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/init.lua @@ -0,0 +1,13 @@ +---@type Flash.Commands +local M = {} + +---@param opts? Flash.Config +function M.setup(opts) + require("flash.config").setup(opts) +end + +return setmetatable(M, { + __index = function(_, k) + return require("flash.commands")[k] + end, +}) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/jump.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/jump.lua new file mode 100644 index 00000000..b0735fad --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/jump.lua @@ -0,0 +1,253 @@ +local Hacks = require("flash.hacks") +local Pos = require("flash.search.pos") +local Util = require("flash.util") +local M = {} + +---@param match Flash.Match +---@param state Flash.State +---@return Flash.Match? +function M.jump(match, state) + local register = vim.v.register + -- add to jump list + if state.opts.jump.jumplist then + vim.cmd("normal! m'") + end + + local mode = vim.fn.mode(true) + local is_op = mode:sub(1, 2) == "no" + local is_visual = mode:sub(1, 1) == "v" + + if is_op and (state.opts.remote_op.motion or match.win ~= vim.api.nvim_get_current_win()) then + -- use our special logic for remote operator pending mode + return M.remote_op(match, state, register) + end + + -- change window if needed + if match.win ~= vim.api.nvim_get_current_win() then + if is_visual then + -- cancel visual mode in the current window, + -- to avoid issues with the remote window + vim.cmd("normal! v") + end + + vim.api.nvim_set_current_win(match.win) + + if is_visual then + -- enable visual mode in the remote window, + -- from its current cursor position + vim.cmd("normal! v") + end + end + + M._jump(match, state, { op = is_op }) +end + +function M.fix_selection() + local selection = vim.go.selection + vim.go.selection = "inclusive" + vim.schedule(function() + vim.go.selection = selection + end) +end + +-- Remote operator pending mode.Cancel the operator and +-- re-trigger the operator in the remote window. +---@param match Flash.Match +---@param state Flash.State +---@param register string +---@return Flash.Match? +function M.remote_op(match, state, register) + Util.exit() + + -- schedule this so that the active operator is properly cancelled + vim.schedule(function() + local motion = state.opts.remote_op.motion + if motion == nil then + motion = match.win ~= vim.api.nvim_get_current_win() + end + + vim.api.nvim_set_current_win(match.win) + + -- use a new motion to select the text-object to act on, + -- unless we're jumping to a range + if motion then + if vim.fn.mode() == "v" then + vim.cmd("normal! v") + end + + if state.opts.jump.pos == "range" then + vim.api.nvim_win_set_cursor(match.win, match.pos) + vim.cmd("normal! v") + vim.api.nvim_win_set_cursor(match.win, match.end_pos) + else + vim.api.nvim_win_set_cursor( + match.win, + state.opts.jump.pos == "start" and match.pos or match.end_pos + ) + end + + -- otherwise, use the remote window's cursor position + else + local from = vim.api.nvim_win_get_cursor(match.win) + M._jump(match, state, { op = true }) + local to = vim.api.nvim_win_get_cursor(match.win) + + -- if a range was selected, use that instead + if vim.fn.mode() == "v" then + vim.cmd("normal! v") -- end the selection + from = vim.api.nvim_buf_get_mark(0, "<") + to = vim.api.nvim_buf_get_mark(0, ">") + end + + -- vim.api.nvim_buf_set_mark(0, "[", from[1], from[2], {}) + -- vim.api.nvim_buf_set_mark(0, "]", to[1], to[2], {}) + + -- select the range for the operator + vim.api.nvim_win_set_cursor(0, from) + vim.cmd("normal! v") + vim.api.nvim_win_set_cursor(0, to) + end + + ---@diagnostic disable-next-line: param-type-mismatch + local opmap = vim.fn.maparg(vim.v.operator, "", false, true) --[[@as any]] + if not vim.tbl_isempty(opmap) then + vim.keymap.del("", vim.v.operator) + end + + -- re-trigger the operator + vim.api.nvim_input('"' .. register .. vim.v.operator) + if state.opts.remote_op.restore then + vim.schedule(function() + if not vim.tbl_isempty(opmap) then + vim.fn.mapset(opmap.mode, false, opmap) + end + M.restore_remote(state) + end) + end + end) +end + +-- Restore window views after the remote operation ends +---@param state Flash.State +function M.restore_remote(state) + local restore = vim.schedule_wrap(function() + state:restore() + end) + + -- wait till getting user input clears + if not Hacks.mappings_enabled() then + return Util.on_done(function() + return Hacks.mappings_enabled() + end, function() + M.restore_remote(state) + end) + + -- wait till operator pending mode ends + elseif vim.fn.mode(true):sub(1, 2) == "no" then + return Util.on_done(function() + return vim.fn.mode(true):sub(1, 2) ~= "no" + end, function() + M.restore_remote(state) + end) + + -- restore after making edits + elseif vim.fn.mode() == "i" and vim.v.operator == "c" then + vim.api.nvim_create_autocmd("InsertLeave", { + once = true, + callback = restore, + }) + else + restore() + end +end + +-- Performs the actual jump in the current window, +-- taking operator-pending mode into account. +---@param match Flash.Match +---@param state Flash.State +---@param opts? {op:boolean} +---@return Flash.Match? +function M._jump(match, state, opts) + opts = opts or {} + M.fix_selection() + M.open_folds(match) + -- select range + if state.opts.jump.pos == "range" then + if vim.fn.mode() == "v" then + vim.cmd("normal! v") + end + vim.api.nvim_win_set_cursor(match.win, match.pos) + vim.cmd("normal! v") + vim.api.nvim_win_set_cursor(match.win, match.end_pos) + else + local pos = state.opts.jump.pos == "start" and match.pos or match.end_pos + + if opts.op then + -- fix inclusive/exclusive + -- default is exclusive + if state.opts.jump.inclusive ~= false then + vim.cmd("normal! v") + end + end + local current = Pos(match.win) + local offset = state.opts.jump.offset + + if not offset and state.opts.jump.pos == "end" and pos < current then + offset = 1 + end + + pos = Pos( + require("flash.util").offset_pos(vim.api.nvim_win_get_buf(match.win), pos, { 0, offset or 0 }) + ) + pos[2] = math.max(0, pos[2]) + + vim.api.nvim_win_set_cursor(match.win, pos) + end +end + +---@param match Flash.Match +function M.open_folds(match) + local cursor = vim.api.nvim_win_get_cursor(match.win) + local from = match.pos[1] + local to = match.end_pos[1] + local is_visual = vim.fn.mode(true):find("v") + local opened = false + for line = from, to do + if vim.fn.foldclosed(line) ~= -1 then + vim.api.nvim_win_set_cursor(match.win, { line, 0 }) + vim.cmd("normal! zO") + opened = true + end + end + if opened then + vim.api.nvim_win_set_cursor(match.win, cursor) + if is_visual then + vim.cmd("normal! v") + end + end +end + +---@param state Flash.State +function M.on_jump(state) + -- fix or restore the search register + local sf = vim.v.searchforward + if state.opts.jump.register then + vim.fn.setreg("/", state.pattern.search) + end + vim.v.searchforward = sf + + -- add the real search pattern to the history + if state.opts.jump.history then + vim.fn.histadd("search", state.pattern.search) + end + + -- clear the highlight + if state.opts.jump.nohlsearch then + vim.cmd.nohlsearch() + elseif state.opts.jump.register then + -- this will show the search matches again + vim.cmd("set hlsearch") + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/labeler.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/labeler.lua new file mode 100644 index 00000000..35f7c17c --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/labeler.lua @@ -0,0 +1,228 @@ +---@class Flash.Labeler +---@field state Flash.State +---@field used table +---@field labels string[] +local M = {} +M.__index = M + +function M.new(state) + local self + self = setmetatable({}, M) + self.state = state + self.used = {} + self:reset() + return self +end + +function M:labeler() + return function() + return self:update() + end +end + +function M:update() + self:reset() + + if #self.state.pattern() < self.state.opts.label.min_pattern_length then + return + end + + local matches = self:filter() + + for _, match in ipairs(matches) do + self:label(match, true) + end + + for _, match in ipairs(matches) do + if not self:label(match) then + break + end + end +end + +function M:reset() + local skip = {} ---@type table + self.labels = {} + + for _, l in ipairs(self.state:labels()) do + if not skip[l] then + self.labels[#self.labels + 1] = l + skip[l] = true + end + end + if + not self.state.opts.search.max_length + or #self.state.pattern() < self.state.opts.search.max_length + then + for _, win in pairs(self.state.wins) do + self.labels = self:skip(win, self.labels) + end + end + for _, m in ipairs(self.state.results) do + if m.label ~= false then + m.label = nil + end + end +end + +function M:valid(label) + return vim.tbl_contains(self.labels, label) +end + +function M:use(label) + self.labels = vim.tbl_filter(function(c) + return c ~= label + end, self.labels) +end + +---@param m Flash.Match +---@param used boolean? +function M:label(m, used) + if m.label ~= nil then + return true + end + local pos = m.pos:id(m.win) + local label ---@type string? + if used then + label = self.used[pos] + else + label = self.labels[1] + end + if label and self:valid(label) then + self:use(label) + local reuse = self.state.opts.label.reuse == "all" + or (self.state.opts.label.reuse == "lowercase" and label:lower() == label) + + if reuse then + self.used[pos] = label + end + m.label = label + end + return #self.labels > 0 +end + +function M:filter() + ---@type Flash.Match[] + local ret = {} + + local target = self.state.target + + local from = vim.api.nvim_win_get_cursor(self.state.win) + ---@type table + local folds = {} + + -- only label visible matches + for _, match in ipairs(self.state.results) do + -- and don't label the first match in the current window + local skip = (target and match.pos == target.pos) + and not self.state.opts.label.current + and match.win == self.state.win + + -- Only label the first match in each fold + if not skip and match.fold then + if folds[match.fold] then + skip = true + else + folds[match.fold] = true + end + end + + if not skip then + table.insert(ret, match) + end + end + + -- sort by current win, other win, then by distance + table.sort(ret, function(a, b) + local use_distance = self.state.opts.label.distance and a.win == self.state.win + + if a.win ~= b.win then + local aw = a.win == self.state.win and 0 or a.win + local bw = b.win == self.state.win and 0 or b.win + return aw < bw + end + if use_distance then + local dfrom = from[1] * vim.go.columns + from[2] + local da = a.pos[1] * vim.go.columns + a.pos[2] + local db = b.pos[1] * vim.go.columns + b.pos[2] + return math.abs(dfrom - da) < math.abs(dfrom - db) + end + if a.pos[1] ~= b.pos[1] then + return a.pos[1] < b.pos[1] + end + return a.pos[2] < b.pos[2] + end) + return ret +end + +-- Returns valid labels for the current search pattern +-- in this window. +---@param labels string[] +---@return string[] returns labels to skip or `nil` when all labels should be skipped +function M:skip(win, labels) + local pattern = self.state.pattern.skip + + -- skip all labels if the pattern is empty + if pattern == "" then + return {} + end + + -- skip all labels if the pattern is invalid + local ok = pcall(vim.regex, pattern) + if not ok then + return {} + end + + -- skip all labels if the pattern ends with a backslash + -- except if it's escaped + if pattern:find("\\$") and not pattern:find("\\\\$") then + return {} + end + + vim.api.nvim_win_call(win, function() + while #labels > 0 do + -- this is needed, since an uppercase label would trigger smartcase + local label_group = table.concat(labels, "") + if vim.go.ignorecase then + label_group = label_group:lower() + end + + local p = "\\%(" .. pattern .. "\\)\\m\\zs[" .. label_group .. "]" + local pos + ok, pos = pcall(vim.fn.searchpos, p, "cnw") + + if not ok then + labels = {} + break + end + + -- not found, we're done + if pos[1] == 0 then + return + end + + local line = vim.api.nvim_buf_get_lines(0, pos[1] - 1, pos[1], false)[1] + local char = vim.fn.strpart(line, pos[2] - 1, 1, true) + + local label_count = #labels + labels = vim.tbl_filter(function(c) + -- when ignorecase is set, we need to skip + -- both the upper and lower case labels + if vim.go.ignorecase then + return c:lower() ~= char:lower() + end + return c ~= char + end, labels) + + -- HACK: this will fail if the pattern is an incomplete regex + -- In that case, we skip all labels + if label_count == #labels then + labels = {} + break + end + end + end) + return labels +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/plugins/char.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/plugins/char.lua new file mode 100644 index 00000000..30e72097 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/plugins/char.lua @@ -0,0 +1,299 @@ +local require = require("flash.require") + +local Config = require("flash.config") +local Labeler = require("flash.labeler") +local Repeat = require("flash.repeat") +local Util = require("flash.util") + +local M = {} + +---@alias Flash.Char.Motion "'f'" | "'F'" | "'t'" | "'T'" +M.motion = "f" ---@type Flash.Char.Motion +M.char = nil ---@type string? +M.jumping = false +M.state = nil ---@type Flash.State? +M.jump_labels = false + +---@type table +M.motions = { + f = { label = { after = { 0, 0 }, before = false } }, + t = {}, + F = { + jump = { inclusive = false }, + search = { forward = false }, + label = { after = { 0, 0 }, before = false }, + }, + T = { + jump = { inclusive = false }, + search = { forward = false }, + label = { before = true, after = false }, + }, +} + +function M.new() + local State = require("flash.state") + local opts = Config.get({ + mode = "char", + labeler = M.labeler, + search = { + multi_window = false, + mode = M.mode(M.motion), + max_length = 1, + }, + prompt = { + enabled = false, + }, + }, M.motions[M.motion]) + + -- never show the current match label + opts.highlight.groups.current = M.motion:lower() == "f" and opts.highlight.groups.label + or opts.highlight.groups.match + + -- exclude the motion labels so we can use them for next/prev + opts.labels = opts.labels:gsub(M.motion:lower(), "") + opts.labels = opts.labels:gsub(M.motion:upper(), "") + return State.new(opts) +end + +function M.labeler(matches, state) + if M.jump_labels then + if not state._labeler then + state._labeler = Labeler.new(state) + end + state._labeler:update() + else + -- set to empty label, so that the character will just be highlighted + for _, m in ipairs(matches) do + m.label = "" + end + end +end + +---@param motion Flash.Char.Motion +function M.mode(motion) + ---@param c string + return function(c) + c = c:gsub("\\", "\\\\") + local pattern ---@type string + if motion == "t" then + pattern = "\\m.\\ze\\V" .. c + elseif motion == "T" then + pattern = "\\V" .. c .. "\\zs\\m." + else + pattern = "\\V" .. c + end + if not Config.get("char").multi_line then + local pos = vim.api.nvim_win_get_cursor(0) + pattern = ("\\%%%dl"):format(pos[1]) .. pattern + end + + return pattern + end +end + +function M.visible() + return M.state and M.state.visible +end + +function M.setup() + Repeat.setup() + + local keys = {} + + for k, v in pairs(Config.modes.char.keys) do + if vim.g.mapleader ~= v and vim.g.maplocalleader ~= v then + keys[type(k) == "number" and v or k] = v + end + end + + -- don't override ;, mappings if they exist + for _, key in ipairs({ ";", "," }) do + local mapping = vim.fn.maparg(key, "n", false, false) + if keys[key] == key and mapping ~= "" then + keys[key] = nil + end + end + + for _, key in ipairs({ "f", "F", "t", "T", ";", "," }) do + if keys[key] then + vim.keymap.set({ "n", "x", "o" }, keys[key], function() + M.jumping = true + local autohide = Config.get("char").autohide + if Repeat.is_repeat then + M.jump_labels = false -- never show jump labels when repeating + M.state:jump({ count = vim.v.count1 }) + M.state:show() + else + M.jump(key) + end + vim.schedule(function() + M.jumping = false + if M.state and autohide then + M.state:hide() + end + end) + end, { + silent = true, + }) + end + end + + vim.api.nvim_create_autocmd({ "BufLeave", "CursorMoved", "InsertEnter" }, { + group = vim.api.nvim_create_augroup("flash_char", { clear = true }), + callback = function(event) + local hide = event.event == "InsertEnter" or not M.jumping + if hide and M.state then + M.state:hide() + end + end, + }) + + vim.on_key(function(key) + if M.state and key == Util.ESC and (vim.fn.mode() == "n" or vim.fn.mode() == "v") then + M.state:hide() + end + end) +end + +function M.parse(key) + ---@class Flash.Char.Parse + local ret = { + jump = M.next, + actions = {}, ---@type table + getchar = false, + } + -- repeat last search when hitting the same key + -- don't repeat when executing a macro + if M.visible() and vim.fn.reg_executing() == "" and M.motion:lower() == key:lower() then + ret.actions = M.actions(M.motion) + if ret.actions[key] then + ret.jump = ret.actions[key] + return ret + else + -- no action defined, so clear the state + M.motion = "" + end + end + + -- different motion, clear the state + if M.motions[key] and M.motion ~= key then + if M.state then + M.state:hide() + end + M.motion = key + end + + ret.actions = M.actions(M.motion) + + if M.motions[key] then + ret.getchar = true + else -- ;, + ret.jump = ret.actions[key] or M.next + end + + return ret +end + +---@param motion Flash.Char.Motion +---@return table +function M.actions(motion) + local ret = Config.get("char").char_actions(motion) + for key, value in pairs(ret) do + ret[key] = M[value] + end + return ret +end + +function M.jump(key) + local parsed = M.parse(key) + if not M.motion then + return + end + + local is_op = vim.fn.mode(true):sub(1, 2) == "no" + + -- always re-calculate when not visible + M.state = M.visible() and M.state or M.new() + + -- get a new target + if parsed.getchar or not M.char then + local char = M.state:get_char() + if char then + M.char = char + else + return M.state:hide() + end + end + + -- HACK: When the motion is t or T, we need to set the current position as a valid target + -- but only when we are not repeating + M.current = M.motion:lower() == "t" and parsed.getchar + + -- update the state when needed + if M.state.pattern:empty() then + M.state:update({ pattern = M.char }) + end + + local jump = parsed.jump + + M.jump_labels = Config.get("char").jump_labels + jump() + M.state:update({ force = true }) + + if M.jump_labels then + parsed.actions[Util.CR] = function() + return false + end + M.state:loop({ + restore = is_op, + abort = function() + Util.exit() + end, + jump_on_max_length = false, + actions = parsed.actions, + }) + end + + return M.state +end + +M.current = false + +function M.right() + return M.state.opts.search.forward and M.next() or M.prev() +end + +function M.left() + return M.state.opts.search.forward and M.prev() or M.next() +end + +function M.next() + M.state:jump({ + count = vim.v.count1, + forward = M.state.opts.search.forward, + current = M.current, + }) + M.current = false + return true +end + +function M.prev() + M.state:jump({ + count = vim.v.count1, + forward = not M.state.opts.search.forward, + current = M.current, + }) + M.current = false + -- check if we should enable wrapping. + if not M.state.opts.search.wrap then + local before = M.state:find({ count = 1, forward = false }) + if before and (before.pos < M.state.pos) == M.state.opts.search.forward then + M.state.opts.search.wrap = true + M.state._labeler = nil + M.state:update({ force = true }) + end + end + return true +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/plugins/search.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/plugins/search.lua new file mode 100644 index 00000000..ef7bf1b4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/plugins/search.lua @@ -0,0 +1,163 @@ +local require = require("flash.require") + +local Config = require("flash.config") +local Jump = require("flash.jump") +local State = require("flash.state") +local Util = require("flash.util") + +local M = {} + +---@type Flash.State? +M.state = nil +M.op = false +M.enabled = true + +---@param enabled? boolean +function M.toggle(enabled) + if enabled == nil then + enabled = not M.enabled + end + + if M.enabled == enabled then + return M.enabled + end + + M.enabled = enabled + + if State.is_search() then + if M.enabled then + M.start() + M.update(false) + elseif M.state then + M.state:hide() + M.state = nil + end + -- redraw to show the change + vim.cmd("redraw") + -- trigger incsearch to update the matches + vim.api.nvim_feedkeys(" " .. Util.BS, "n", true) + end + return M.enabled +end + +---@param check_jump? boolean +function M.update(check_jump) + if not M.state then + return + end + + local pattern = vim.fn.getcmdline() + + -- when doing // or ??, get the pattern from the search register + -- See :h search-commands + if pattern:sub(1, 1) == vim.fn.getcmdtype() then + pattern = vim.fn.getreg("/") .. pattern:sub(2) + end + M.state:update({ pattern = pattern, check_jump = check_jump }) +end + +function M.start() + M.state = State.new({ + mode = "search", + action = M.jump, + search = { + forward = vim.fn.getcmdtype() == "/", + mode = "search", + incremental = vim.go.incsearch, + }, + }) + if M.op then + M.state.opts.search.multi_window = false + end +end + +function M.setup() + local group = vim.api.nvim_create_augroup("flash", { clear = true }) + M.enabled = Config.modes.search.enabled or false + + local function wrap(fn) + return function(...) + if M.state then + return fn(...) + end + end + end + + vim.api.nvim_create_autocmd("CmdlineChanged", { + group = group, + callback = wrap(function() + M.update() + end), + }) + + vim.api.nvim_create_autocmd("CmdlineLeave", { + group = group, + callback = wrap(function() + M.state:hide() + M.state = nil + end), + }) + vim.api.nvim_create_autocmd("CmdlineEnter", { + group = group, + callback = function() + if State.is_search() and M.enabled then + M.start() + M.set_op(vim.fn.mode() == "v") + end + end, + }) + + vim.api.nvim_create_autocmd("ModeChanged", { + pattern = "*:c", + group = group, + callback = function() + M.set_op(vim.v.event.old_mode:sub(1, 2) == "no" or vim.fn.mode() == "v") + end, + }) +end + +function M.set_op(op) + M.op = op + if M.op and M.state then + M.state.opts.search.multi_window = false + end +end + +---@param self Flash.State +---@param match Flash.Match +function M.jump(match, self) + local pos = match.pos + local search_reg = vim.fn.getreg("/") + + -- For operator pending mode, set the search pattern to the + -- first character on the match position + if M.op then + local pos_pattern = ("\\%%%dl\\%%%dc."):format(pos[1], pos[2] + 1) + vim.fn.setcmdline(pos_pattern) + end + + -- schedule a input to trigger the search + vim.schedule(function() + vim.api.nvim_input(M.op and "" or "") + end) + + -- restore the real search pattern after the search + -- and perform the jump when not in operator pending mode + vim.api.nvim_create_autocmd("CmdlineLeave", { + once = true, + callback = vim.schedule_wrap(function() + -- delete the search pattern. + -- The correct one will be added in `on_jump` + vim.fn.histdel("search", -1) + if M.op then + -- restore original search pattern + vim.fn.setreg("/", search_reg) + else + Jump.jump(match, self) + end + Jump.on_jump(self) + end), + }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/plugins/treesitter.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/plugins/treesitter.lua new file mode 100644 index 00000000..223757bd --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/plugins/treesitter.lua @@ -0,0 +1,181 @@ +local Config = require("flash.config") +local Pos = require("flash.search.pos") +local Repeat = require("flash.repeat") +local Util = require("flash.util") + +local M = {} + +---@class Flash.Match.TS: Flash.Match +---@field node TSNode +---@field depth? number + +---@param win window +---@param pos? Pos +function M.get_nodes(win, pos) + local buf = vim.api.nvim_win_get_buf(win) + local line_count = vim.api.nvim_buf_line_count(buf) + pos = pos or Pos() + + local nodes = {} ---@type TSNode[] + + local ok, tree = pcall(vim.treesitter.get_parser, buf) + if not ok then + vim.notify( + "No treesitter parser for this buffer with filetype=" .. vim.bo[buf].filetype, + vim.log.levels.WARN, + { title = "flash.nvim" } + ) + vim.api.nvim_input("") + end + if not (ok and tree) then + return {} + end + + do + -- get all ranges of the current node and its parents + local node = tree:named_node_for_range({ pos[1] - 1, pos[2], pos[1] - 1, pos[2] }, { + ignore_injections = false, + }) + + while node do + nodes[#nodes + 1] = node + node = node:parent() ---@type TSNode + end + end + + -- convert ranges to matches + ---@type Flash.Match.TS[] + local ret = {} + local first = true + ---@type table + local done = {} + for _, node in ipairs(nodes) do + local range = { node:range() } + ---@type Flash.Match.TS + local match = { + node = node, + pos = { range[1] + 1, range[2] }, + end_pos = { range[3] + 1, range[4] - 1 }, + first = first, + } + first = false + -- If the match is at the end of the buffer, + -- then move it to the last character of the last line. + if match.end_pos[1] > line_count then + match.end_pos[1] = line_count + match.end_pos[2] = + #vim.api.nvim_buf_get_lines(buf, match.end_pos[1] - 1, match.end_pos[1], false)[1] + elseif match.end_pos[2] == -1 then + -- If the end points to the start of the next line, move it to the + -- end of the previous line. + -- Otherwise operations include the first character of the next line + local line = + vim.api.nvim_buf_get_lines(buf, match.end_pos[1] - 2, match.end_pos[1] - 1, false)[1] + match.end_pos[1] = match.end_pos[1] - 1 + match.end_pos[2] = #line + end + local id = table.concat(vim.tbl_flatten({ match.pos, match.end_pos }), ".") + if not done[id] then + done[id] = true + ret[#ret + 1] = match + end + end + + for m, match in ipairs(ret) do + match.pos = Pos(match.pos) + match.end_pos = Pos(match.end_pos) + match.win = win + match.depth = #ret - m + end + return ret +end + +---@param win window +---@param state Flash.State +function M.matcher(win, state) + local labels = state:labels() + local ret = M.get_nodes(win, state.pos) + + for i = 1, #ret do + ret[i].label = table.remove(labels, 1) + end + return ret +end + +---@param opts? Flash.Config +function M.jump(opts) + local state = Repeat.get_state( + "treesitter", + Config.get({ mode = "treesitter" }, opts, { + matcher = M.matcher, + labeler = function() end, + search = { multi_window = false, wrap = true, incremental = false, max_length = 0 }, + }) + ) + + ---@type Flash.Match? + local current + for _, m in ipairs(state.results) do + ---@cast m Flash.Match.TS + if not current or m.depth > current.depth then + current = m + end + end + current = state:jump(current) + + state:loop({ + abort = function() + vim.cmd([[normal! v]]) + end, + actions = { + [";"] = function() + current = state:jump({ match = current, forward = false }) + end, + [","] = function() + current = state:jump({ forward = true, match = current }) + end, + [Util.CR] = function() + state:jump(current and current.label or nil) + return false + end, + }, + jump_on_max_length = false, + }) + + return state +end + +---@param opts? Flash.Config +function M.search(opts) + opts = Config.get({ mode = "treesitter_search" }, opts, { + matcher = function(win, _state, _opts) + local Search = require("flash.search") + local search = Search.new(win, _state) + local matches = {} ---@type Flash.Match[] + for _, m in ipairs(search:get(_opts)) do + -- don't add labels to the search results + m.label = false + table.insert(matches, m) + for _, n in ipairs(M.get_nodes(win, m.pos)) do + -- don't highlight treesitter nodes. Use labels only + n.highlight = false + table.insert(matches, n) + end + end + return matches + end, + jump = { pos = "range" }, + }) + + opts.search.exclude = vim.deepcopy(opts.search.exclude) + table.insert(opts.search.exclude, function(win) + local buf = vim.api.nvim_win_get_buf(win) + return not pcall(vim.treesitter.get_parser, buf) + end) + + local state = Repeat.get_state("treesitter-search", opts) + state:loop() + return state +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/prompt.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/prompt.lua new file mode 100644 index 00000000..6a89c460 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/prompt.lua @@ -0,0 +1,85 @@ +local Config = require("flash.config") + +---@class Flash.Prompt +---@field win window +---@field buf buffer +local M = {} + +local ns = vim.api.nvim_create_namespace("flash_prompt") + +function M.visible() + return M.win and vim.api.nvim_win_is_valid(M.win) and M.buf and vim.api.nvim_buf_is_valid(M.buf) +end + +function M.show() + if M.visible() then + return + end + require("flash.highlight") + + M.buf = vim.api.nvim_create_buf(false, true) + vim.bo[M.buf].buftype = "nofile" + vim.bo[M.buf].bufhidden = "wipe" + vim.bo[M.buf].filetype = "flash_prompt" + + local config = vim.deepcopy(Config.prompt.win_config) + + if config.width <= 1 then + config.width = config.width * vim.go.columns + end + + if config.row < 0 then + config.row = vim.go.lines + config.row + end + + if config.col < 0 then + config.col = vim.go.columns + config.col + end + + config = vim.tbl_extend("force", config, { + style = "minimal", + focusable = false, + noautocmd = true, + }) + + M.win = vim.api.nvim_open_win(M.buf, false, config) + vim.wo[M.win].winhighlight = "Normal:FlashPrompt" +end + +function M.hide() + if M.win and vim.api.nvim_win_is_valid(M.win) then + vim.api.nvim_win_close(M.win, true) + M.win = nil + end + if M.buf and vim.api.nvim_buf_is_valid(M.buf) then + vim.api.nvim_buf_delete(M.buf, { force = true }) + M.buf = nil + end +end + +---@param pattern string +function M.set(pattern) + M.show() + local text = vim.deepcopy(Config.prompt.prefix) + text[#text + 1] = { pattern } + + local str = "" + for _, item in ipairs(text) do + str = str .. item[1] + end + vim.api.nvim_buf_set_lines(M.buf, 0, -1, false, { str }) + vim.api.nvim_buf_clear_namespace(M.buf, ns, 0, -1) + local col = 0 + for _, item in ipairs(text) do + local width = vim.fn.strlen(item[1]) + if item[2] then + vim.api.nvim_buf_set_extmark(M.buf, ns, 0, col, { + hl_group = item[2], + end_col = col + width, + }) + end + col = col + width + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/rainbow.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/rainbow.lua new file mode 100644 index 00000000..c48a1384 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/rainbow.lua @@ -0,0 +1,401 @@ +---@class Flash.Rainbow +---@field cache table +---@field count number +---@field shade number +local M = {} + +---@type table +M.hl = {} + +function M.setup() + if M.did_setup then + return + end + M.did_setup = true + vim.api.nvim_create_autocmd("ColorScheme", { + callback = function() + M.hl = {} + end, + }) +end + +---@param state Flash.State +function M.new(state) + local self = setmetatable({}, { __index = M }) + self.cache = {} + self.count = 0 + self.shade = state.opts.label.rainbow.shade + return self +end + +---@param match Flash.Match +function M:get(match) + local buf = vim.api.nvim_win_get_buf(match.win) + local id = match.pos:id(buf) + if match.depth then + id = id .. ":" .. tostring(match.depth) + end + + if not self.cache[id] then + self.count = self.count + 1 + self.cache[id] = M.get_color(self.count, self.shade) + end + return self.cache[id] +end + +---@param idx number +---@param shade number +function M.get_color(idx, shade) + M.setup() + idx = (idx - 1) % #M.rainbow + 1 + local color = M.rainbow[idx] + shade = (shade or 5) * 100 + local bg = vim.tbl_get(M.colors, color, shade) + if bg then + local hl = "FlashColor" .. color .. shade + if not M.hl[hl] then + M.hl[hl] = true + local bg_shade = shade == 500 and 950 or shade < 500 and 900 or 50 + local fg = vim.tbl_get(M.colors, color, bg_shade) + vim.api.nvim_set_hl(0, hl, { bg = "#" .. bg, fg = "#" .. fg, bold = true }) + end + return hl + end +end + +M.rainbow = { + -- "slate", + -- "gray", + -- "zinc", + -- "neutral", + -- "stone", + "red", + -- "orange", + "amber", + -- "yellow", + "lime", + "green", + -- "emerald", + "teal", + "cyan", + -- "sky", + "blue", + -- "indigo", + "violet", + -- "purple", + "fuchsia", + -- "pink", + "rose", +} + +M.colors = { + slate = { + [50] = "f8fafc", + [100] = "f1f5f9", + [200] = "e2e8f0", + [300] = "cbd5e1", + [400] = "94a3b8", + [500] = "64748b", + [600] = "475569", + [700] = "334155", + [800] = "1e293b", + [900] = "0f172a", + [950] = "020617", + }, + + gray = { + [50] = "f9fafb", + [100] = "f3f4f6", + [200] = "e5e7eb", + [300] = "d1d5db", + [400] = "9ca3af", + [500] = "6b7280", + [600] = "4b5563", + [700] = "374151", + [800] = "1f2937", + [900] = "111827", + [950] = "030712", + }, + + zinc = { + [50] = "fafafa", + [100] = "f4f4f5", + [200] = "e4e4e7", + [300] = "d4d4d8", + [400] = "a1a1aa", + [500] = "71717a", + [600] = "52525b", + [700] = "3f3f46", + [800] = "27272a", + [900] = "18181b", + [950] = "09090B", + }, + + neutral = { + [50] = "fafafa", + [100] = "f5f5f5", + [200] = "e5e5e5", + [300] = "d4d4d4", + [400] = "a3a3a3", + [500] = "737373", + [600] = "525252", + [700] = "404040", + [800] = "262626", + [900] = "171717", + [950] = "0a0a0a", + }, + + stone = { + [50] = "fafaf9", + [100] = "f5f5f4", + [200] = "e7e5e4", + [300] = "d6d3d1", + [400] = "a8a29e", + [500] = "78716c", + [600] = "57534e", + [700] = "44403c", + [800] = "292524", + [900] = "1c1917", + [950] = "0a0a0a", + }, + + red = { + [50] = "fef2f2", + [100] = "fee2e2", + [200] = "fecaca", + [300] = "fca5a5", + [400] = "f87171", + [500] = "ef4444", + [600] = "dc2626", + [700] = "b91c1c", + [800] = "991b1b", + [900] = "7f1d1d", + [950] = "450a0a", + }, + + orange = { + [50] = "fff7ed", + [100] = "ffedd5", + [200] = "fed7aa", + [300] = "fdba74", + [400] = "fb923c", + [500] = "f97316", + [600] = "ea580c", + [700] = "c2410c", + [800] = "9a3412", + [900] = "7c2d12", + [950] = "431407", + }, + + amber = { + [50] = "fffbeb", + [100] = "fef3c7", + [200] = "fde68a", + [300] = "fcd34d", + [400] = "fbbf24", + [500] = "f59e0b", + [600] = "d97706", + [700] = "b45309", + [800] = "92400e", + [900] = "78350f", + [950] = "451a03", + }, + + yellow = { + [50] = "fefce8", + [100] = "fef9c3", + [200] = "fef08a", + [300] = "fde047", + [400] = "facc15", + [500] = "eab308", + [600] = "ca8a04", + [700] = "a16207", + [800] = "854d0e", + [900] = "713f12", + [950] = "422006", + }, + + lime = { + [50] = "f7fee7", + [100] = "ecfccb", + [200] = "d9f99d", + [300] = "bef264", + [400] = "a3e635", + [500] = "84cc16", + [600] = "65a30d", + [700] = "4d7c0f", + [800] = "3f6212", + [900] = "365314", + [950] = "1a2e05", + }, + + green = { + [50] = "f0fdf4", + [100] = "dcfce7", + [200] = "bbf7d0", + [300] = "86efac", + [400] = "4ade80", + [500] = "22c55e", + [600] = "16a34a", + [700] = "15803d", + [800] = "166534", + [900] = "14532d", + [950] = "052e16", + }, + + emerald = { + [50] = "ecfdf5", + [100] = "d1fae5", + [200] = "a7f3d0", + [300] = "6ee7b7", + [400] = "34d399", + [500] = "10b981", + [600] = "059669", + [700] = "047857", + [800] = "065f46", + [900] = "064e3b", + [950] = "022c22", + }, + + teal = { + [50] = "f0fdfa", + [100] = "ccfbf1", + [200] = "99f6e4", + [300] = "5eead4", + [400] = "2dd4bf", + [500] = "14b8a6", + [600] = "0d9488", + [700] = "0f766e", + [800] = "115e59", + [900] = "134e4a", + [950] = "042f2e", + }, + + cyan = { + [50] = "ecfeff", + [100] = "cffafe", + [200] = "a5f3fc", + [300] = "67e8f9", + [400] = "22d3ee", + [500] = "06b6d4", + [600] = "0891b2", + [700] = "0e7490", + [800] = "155e75", + [900] = "164e63", + [950] = "083344", + }, + + sky = { + [50] = "f0f9ff", + [100] = "e0f2fe", + [200] = "bae6fd", + [300] = "7dd3fc", + [400] = "38bdf8", + [500] = "0ea5e9", + [600] = "0284c7", + [700] = "0369a1", + [800] = "075985", + [900] = "0c4a6e", + [950] = "082f49", + }, + + blue = { + [50] = "eff6ff", + [100] = "dbeafe", + [200] = "bfdbfe", + [300] = "93c5fd", + [400] = "60a5fa", + [500] = "3b82f6", + [600] = "2563eb", + [700] = "1d4ed8", + [800] = "1e40af", + [900] = "1e3a8a", + [950] = "172554", + }, + + indigo = { + [50] = "eef2ff", + [100] = "e0e7ff", + [200] = "c7d2fe", + [300] = "a5b4fc", + [400] = "818cf8", + [500] = "6366f1", + [600] = "4f46e5", + [700] = "4338ca", + [800] = "3730a3", + [900] = "312e81", + [950] = "1e1b4b", + }, + + violet = { + [50] = "f5f3ff", + [100] = "ede9fe", + [200] = "ddd6fe", + [300] = "c4b5fd", + [400] = "a78bfa", + [500] = "8b5cf6", + [600] = "7c3aed", + [700] = "6d28d9", + [800] = "5b21b6", + [900] = "4c1d95", + [950] = "2e1065", + }, + + purple = { + [50] = "faf5ff", + [100] = "f3e8ff", + [200] = "e9d5ff", + [300] = "d8b4fe", + [400] = "c084fc", + [500] = "a855f7", + [600] = "9333ea", + [700] = "7e22ce", + [800] = "6b21a8", + [900] = "581c87", + [950] = "3b0764", + }, + + fuchsia = { + [50] = "fdf4ff", + [100] = "fae8ff", + [200] = "f5d0fe", + [300] = "f0abfc", + [400] = "e879f9", + [500] = "d946ef", + [600] = "c026d3", + [700] = "a21caf", + [800] = "86198f", + [900] = "701a75", + [950] = "4a044e", + }, + + pink = { + [50] = "fdf2f8", + [100] = "fce7f3", + [200] = "fbcfe8", + [300] = "f9a8d4", + [400] = "f472b6", + [500] = "ec4899", + [600] = "db2777", + [700] = "be185d", + [800] = "9d174d", + [900] = "831843", + [950] = "500724", + }, + + rose = { + [50] = "fff1f2", + [100] = "ffe4e6", + [200] = "fecdd3", + [300] = "fda4af", + [400] = "fb7185", + [500] = "f43f5e", + [600] = "e11d48", + [700] = "be123c", + [800] = "9f1239", + [900] = "881337", + [950] = "4c0519", + }, +} + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/repeat.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/repeat.lua new file mode 100644 index 00000000..82a7c615 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/repeat.lua @@ -0,0 +1,55 @@ +local require = require("flash.require") + +local State = require("flash.state") + +local M = {} + +---@type {is_repeat:boolean, fn:fun()}[] +M._funcs = {} +M._repeat = nil + +-- Sets the current operatorfunc to the given function. +function M.set(fn) + vim.go.operatorfunc = [[{x -> x}]] + local visual = vim.fn.mode() == "v" + vim.cmd("normal! g@l") + if visual then + vim.cmd("normal! gv") + end + M._repeat = fn + vim.go.operatorfunc = [[v:lua.require'flash.repeat'._repeat]] +end + +M.is_repeat = false +function M.setup() + if M._did_setup then + return + end + M._did_setup = true + vim.on_key(function(key) + if key == "." and vim.fn.reg_executing() == "" and vim.fn.reg_recording() == "" then + M.is_repeat = true + vim.schedule(function() + M.is_repeat = false + end) + end + end) +end + +---@type table +M._states = {} + +---@param mode string +---@param opts? Flash.State.Config +function M.get_state(mode, opts) + M.setup() + local last = M._states[mode] + if (M.is_repeat or (opts and opts.continue)) and last then + last:show() + return last + end + M._states[mode] = State.new(opts) + return M._states[mode] +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/require.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/require.lua new file mode 100644 index 00000000..3a63d155 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/require.lua @@ -0,0 +1,25 @@ +return function(module) + local mod = nil + + local function load() + if not mod then + mod = require(module) + package.loaded[module] = mod + end + return mod + end + -- if already loaded, return the module + -- otherwise return a lazy module + return type(package.loaded[module]) == "table" and package.loaded[module] + or setmetatable({}, { + __index = function(_, key) + return load()[key] + end, + __newindex = function(_, key, value) + load()[key] = value + end, + __call = function(_, ...) + return load()(...) + end, + }) +end diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/init.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/init.lua new file mode 100644 index 00000000..865f1e5d --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/init.lua @@ -0,0 +1,117 @@ +local require = require("flash.require") + +local Hacks = require("flash.hacks") +local Matcher = require("flash.search.matcher") +local Pos = require("flash.search.pos") + +---@class Flash.Search: Flash.Matcher +---@field state Flash.State +---@field win window +local M = {} +M.__index = M + +---@param win number +---@param state Flash.State +function M.new(win, state) + local self = setmetatable({}, M) + self.state = state + self.win = win + return self +end + +---@param flags? string +---@return Flash.Match? +function M:_next(flags) + flags = flags or "" + local ok, pos = pcall(vim.fn.searchpos, self.state.pattern.search, flags or "") + -- incomplete or invalid pattern + if not ok then + return + end + if pos[1] == 0 then + return + end + pos = Pos({ pos[1], pos[2] - 1 }) + return { win = self.win, pos = pos, end_pos = Hacks.get_end_pos(pos) } +end + +---@param pos Pos +---@param fn function +function M:_call(pos, fn) + pos = Pos(pos) + + local view = vim.api.nvim_win_call(self.win, vim.fn.winsaveview) + local buf = vim.api.nvim_win_get_buf(self.win) + local line_count = vim.api.nvim_buf_line_count(buf) + if pos[1] > line_count then + pos[1] = line_count + local line = vim.api.nvim_buf_get_lines(buf, pos[1] - 1, pos[1], false)[1] + pos[2] = #line - 1 + end + vim.api.nvim_win_set_cursor(self.win, pos) + ---@type boolean, any? + local ok, err + vim.api.nvim_win_call(self.win, function() + ok, err = pcall(fn) + vim.fn.winrestview(view) + end) + return not ok and error(err) or err +end + +---@param opts? {from?:Pos, to?:Pos} +function M:get(opts) + if self.state.pattern:empty() then + return {} + end + + opts = opts or {} + opts.from = opts.from and Pos(opts.from) or nil + opts.to = opts.to and Pos(opts.to) or nil + + ---@type Flash.Match[] + local ret = {} + + self:_call(opts.from or { 1, 0 }, function() + local next = self:_next("cW") + while next and (not opts.to or next.pos <= opts.to) do + table.insert(ret, next) + next = self:_next("W") + end + end) + return ret +end + +-- Moves the results cursor by `amount` (default 1) and wraps around. +-- When forward is `nil` it uses the current search direction. +-- Otherwise it uses the given direction. +---@param opts? Flash.Match.Find +function M:find(opts) + if self.state.pattern:empty() then + return + end + + opts = Matcher.defaults(opts) + local flags = (opts.forward and "" or "b") + .. (opts.wrap and "w" or "W") + .. ((opts.count == 0 or opts.current) and "c" or "") + if opts.match then + opts.pos = opts.match.pos + end + + ---@type Flash.Match? + local ret + + self:_call(opts.pos, function() + for _ = 1, math.max(opts.count, 1) do + ret = self:_next(flags) + flags = flags:gsub("c", "") + end + end) + + if not ret or (opts.count == 0 and ret.pos ~= opts.pos) then + return + end + return ret +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/matcher.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/matcher.lua new file mode 100644 index 00000000..a76fadd9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/matcher.lua @@ -0,0 +1,179 @@ +local Pos = require("flash.search.pos") + +---@class Flash.Match +---@field win window +---@field pos Pos -- (1,0) indexed +---@field end_pos Pos -- (1,0) indexed +---@field label? string|false -- set to false to disable label +---@field highlight? boolean +---@field fold? number + +---@alias Flash.Match.Find {forward?:boolean, wrap?:boolean, count?:number, pos?: Pos, match?:Flash.Match, current?:boolean} + +---@class Flash.Matcher +---@field win window +---@field get fun(self, opts?: {from?:Pos, to?:Pos}): Flash.Match[] +---@field find fun(self, opts?: Flash.Match.Find): Flash.Match +---@field labels fun(self, labels: string[]): string[] +---@field update? fun(self) + +---@class Flash.Matcher.Custom: Flash.Matcher +---@field matches Flash.Match[] +local M = {} +M.__index = M + +function M.new(win) + local self = setmetatable({}, M) + self.matches = {} + self.win = win + return self +end + +---@param fn fun(win: window, state:Flash.State, opts: {from:Pos, to:Pos}): Flash.Match[] +function M.from(fn) + ---@param win window + ---@param state Flash.State + return function(win, state) + local ret = M.new(win) + ret.get = function(self, opts) + local matches = fn(win, state, opts) + if state.opts.filter then + matches = state.opts.filter(matches, state) or matches + end + self:set(matches) + return M.get(self, opts) + end + + return ret + end +end + +---@param ...? Flash.Match.Find +---@return Flash.Match.Find +function M.defaults(...) + local other = vim.tbl_filter(function(k) + return k ~= nil + end, { ... }) + + local opts = vim.tbl_extend("force", { + pos = vim.api.nvim_win_get_cursor(0), + forward = true, + wrap = true, + count = 1, + }, {}, unpack(other)) + opts.pos = Pos(opts.pos) + return opts +end + +---@param opts? Flash.Match.Find +function M:find(opts) + opts = M.defaults(opts) + + if opts.count == 0 then + for _, match in ipairs(self.matches) do + if match.pos == opts.pos then + return match + end + end + return + end + + ---@type number? + local idx + + if opts.match then + for m, match in ipairs(self.matches) do + if match.pos == opts.match.pos and match.end_pos == opts.match.end_pos then + idx = m + (opts.forward and 1 or -1) + break + end + end + elseif opts.forward then + for i = 1, #self.matches, 1 do + if self.matches[i].pos > opts.pos then + idx = i + break + end + end + else + for i = #self.matches, 1, -1 do + if self.matches[i].pos < opts.pos then + idx = i + break + end + end + end + + if not idx then + if not opts.wrap then + return + end + idx = opts.forward and 1 or #self.matches + end + + if opts.forward then + idx = idx + opts.count - 1 + else + idx = idx - opts.count + 1 + end + + if opts.wrap then + idx = (idx - 1) % #self.matches + 1 + end + return self.matches[idx] +end + +---@param labels string[] +function M:labels(labels) + return labels +end + +---@param opts? {from?:Pos, to?:Pos} +function M:get(opts) + return M.filter(self.matches, opts) +end + +---@param matches Flash.Match[] +---@param opts? {from?:Pos, to?:Pos} +function M.filter(matches, opts) + opts = opts or {} + opts.from = opts.from and Pos(opts.from) + opts.to = opts.to and Pos(opts.to) + ---@param match Flash.Match + return vim.tbl_filter(function(match) + if opts.from and match.end_pos < opts.from then + return false + end + if opts.to and match.pos > opts.to then + return false + end + return true + end, matches) +end + +---@param matches Flash.Match[] +function M:set(matches) + for _, match in ipairs(matches) do + match.pos = Pos(match.pos) + match.end_pos = Pos(match.end_pos) + match.win = match.win or self.win + end + + table.sort(matches, function(a, b) + if a.win ~= b.win then + return a.win < b.win + end + if a.pos ~= b.pos then + return a.pos < b.pos + end + local da = a.depth or 0 + local db = b.depth or 0 + if da ~= db then + return da < db + end + return a.end_pos < b.end_pos + end) + self.matches = matches +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/pattern.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/pattern.lua new file mode 100644 index 00000000..0bdf0d01 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/pattern.lua @@ -0,0 +1,108 @@ +local Util = require("flash.util") + +---@class Flash.Pattern +---@field pattern string +---@field search string +---@field skip string +---@field trigger string +---@field mode Flash.Pattern.Mode +---@operator call:string Returns the input pattern +local M = {} +M.__index = M + +---@alias Flash.Pattern.Mode "exact" | "fuzzy" | "search" | (fun(input:string):string,string?) + +---@param pattern string +---@param mode Flash.Pattern.Mode +---@param trigger string +function M.new(pattern, mode, trigger) + local self = setmetatable({}, M) + self.mode = mode + self.trigger = trigger or "" + self:set(pattern or "") + return self +end + +function M:__eq(other) + return other and other.pattern == self.pattern and other.mode == self.mode +end + +function M:clone() + return M.new(self.pattern, self.mode, self.trigger) +end + +function M:empty() + return self.pattern == "" +end + +---@param pattern string +---@return boolean updated +function M:set(pattern) + if pattern ~= self.pattern then + self.pattern = pattern + if pattern == "" then + self.search = "" + self.skip = "" + else + if self.trigger ~= "" and pattern:sub(-1) == self.trigger then + pattern = pattern:sub(1, -2) + end + self.search, self.skip = M._get(pattern, self.mode) + end + return false + end + return true +end + +---@param char string +function M:extend(char) + if char == Util.BS then + return self.pattern:sub(1, -2) + end + return self.pattern .. char +end + +---@return string the input pattern +function M:__call() + return self.pattern +end + +---@param pattern string +---@param mode Flash.Pattern.Mode +---@private +function M._get(pattern, mode) + local skip ---@type string? + if type(mode) == "function" then + pattern, skip = mode(pattern) + elseif mode == "exact" then + pattern, skip = M._exact(pattern) + elseif mode == "fuzzy" then + pattern, skip = M._fuzzy(pattern) + end + return pattern, skip or pattern +end + +---@param pattern string +function M._exact(pattern) + return "\\V" .. pattern:gsub("\\", "\\\\") +end + +---@param opts? {ignorecase: boolean, whitespace:boolean} +function M._fuzzy(pattern, opts) + opts = vim.tbl_deep_extend("force", { + ignorecase = vim.go.ignorecase, + whitespace = false, + }, opts or {}) + + local sep = opts.whitespace and ".\\{-}" or "\\[^\\ ]\\{-}" + + ---@param c string + local chars = vim.tbl_map(function(c) + return c == "\\" and "\\\\" or c + end, vim.fn.split(pattern, "\\zs")) + + local ret = "\\V" .. table.concat(chars, sep) .. (opts.ignorecase and "\\c" or "\\C") + return ret, ret .. sep +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/pos.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/pos.lua new file mode 100644 index 00000000..35045ba7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/search/pos.lua @@ -0,0 +1,85 @@ +---@class Pos +---@field row number +---@field col number +---@field [1] number +---@field [2] number +---@overload fun(pos?: number[] | { row: number, col: number } | number): Pos +local P = {} + +---@param pos? number[] | { row: number, col: number } | number +function P.new(pos) + if pos == nil then + pos = vim.api.nvim_win_get_cursor(0) + elseif type(pos) == "number" then + pos = vim.api.nvim_win_get_cursor(pos) + end + + if getmetatable(pos) == P then + return pos + end + local self = setmetatable({}, P) + self[1] = pos[1] or pos.row + self[2] = pos[2] or pos.col + return self +end + +function P:__index(key) + if key == "row" then + return rawget(self, 1) + elseif key == "col" then + return rawget(self, 2) + end + return P[key] +end + +function P:__newindex(key, value) + if key == "row" then + rawset(self, 1, value) + elseif key == "col" then + rawset(self, 2, value) + else + rawset(self, key, value) + end +end + +function P:__eq(other) + return self[1] == other[1] and self[2] == other[2] +end + +function P:__tostring() + return ("[%d, %d]"):format(self[1], self[2]) +end + +function P:id(buf) + return table.concat({ buf, self[1], self[2] }, ":") +end + +function P:dist(other) + return math.abs(self[1] - other[1]) + math.abs(self[2] - other[2]) +end + +function P:__add(other) + other = P(other) + return P.new({ self[1] + other[1], self[2] + other[2] }) +end + +function P:__sub(other) + other = P(other) + return P.new({ self[1] - other[1], self[2] - other[2] }) +end + +function P:__lt(other) + other = P(other) + return self[1] < other[1] or (self[1] == other[1] and self[2] < other[2]) +end + +function P:__le(other) + other = P(other) + return self < other or self == other +end + +return setmetatable(P, { + __call = function(_, pos) + return P.new(pos) + end, +}) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/state.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/state.lua new file mode 100644 index 00000000..82b8bb7a --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/state.lua @@ -0,0 +1,420 @@ +local require = require("flash.require") + +local Cache = require("flash.cache") +local Config = require("flash.config") +local Hacks = require("flash.hacks") +local Highlight = require("flash.highlight") +local Jump = require("flash.jump") +local Matcher = require("flash.search.matcher") +local Pattern = require("flash.search.pattern") +local Prompt = require("flash.prompt") +local Rainbow = require("flash.rainbow") +local Search = require("flash.search") +local Util = require("flash.util") + +---@class Flash.State.Config: Flash.Config +---@field matcher? fun(win: window, state:Flash.State, pos: {from:Pos, to:Pos}): Flash.Match[] +---@field filter? fun(matches:Flash.Match[], state:Flash.State): Flash.Match[] +---@field pattern? string +---@field labeler? fun(matches:Flash.Match[], state:Flash.State) +---@field actions? table + +---@class Flash.State +---@field win window +---@field wins window[] +---@field cache Flash.Cache +---@field pos Pos +---@field view any +---@field results Flash.Match[] +---@field target? Flash.Match +---@field pattern Flash.Pattern +---@field opts Flash.State.Config +---@field labeler fun(matches:Flash.Match[], state:Flash.State) +---@field visible boolean +---@field matcher fun(win: window, state:Flash.State): Flash.Matcher +---@field matchers Flash.Matcher[] +---@field restore_windows? fun() +---@field rainbow? Flash.Rainbow +---@field ns number +---@field langmap table +local M = {} +M.__index = M + +---@type table +M._states = setmetatable({}, { __mode = "k" }) + +function M.setup() + if M._did_setup then + return + end + M._did_setup = true + local ns = vim.api.nvim_create_namespace("flash") + vim.api.nvim_set_decoration_provider(ns, { + on_start = function() + for state in pairs(M._states) do + if state.visible then + local ok, err = pcall(state.update, state) + if not ok then + vim.schedule(function() + vim.notify( + "Flash error during redraw:\n" .. err, + vim.log.levels.ERROR, + { title = "flash.nvim" } + ) + end) + end + end + end + end, + }) +end + +---@param char string +function M:lmap(char) + return vim.bo.iminsert == 1 and self.langmap[char] or char +end + +function M:get_char() + local ret = Util.get_char() + return ret and self:lmap(ret) or nil +end + +function M:labels() + local labels = self.opts.labels + if self.opts.label.uppercase then + labels = labels .. self.opts.labels:upper() + end + local list = vim.fn.split(labels, "\\zs") + local ret = {} ---@type string[] + local added = {} ---@type table + for _, l in ipairs(vim.fn.split(self.opts.label.exclude, "\\zs")) do + added[l] = true + end + for _, l in ipairs(list) do + if not added[l] then + added[l] = true + ret[#ret + 1] = self:lmap(l) + end + end + return ret +end + +function M.is_search() + local t = vim.fn.getcmdtype() + return t == "/" or t == "?" +end + +---@param opts? Flash.State.Config +function M.new(opts) + M.setup() + local self = setmetatable({}, M) + self.opts = Config.get(opts) + self.langmap = {} + if vim.bo.iminsert == 1 then + local lmap = vim.api.nvim_buf_get_keymap(0, "l") + for _, m in ipairs(lmap) do + if m.lhs ~= "" then + self.langmap[m.lhs] = m.rhs + end + end + end + self.results = {} + self.matchers = {} + self.wins = {} + self.matcher = self.opts.matcher + if type(self.matcher) == "function" then + self.matcher = Matcher.from(self.opts.matcher) + elseif self.matcher == nil then + self.matcher = Search.new + end + self.pattern = Pattern.new(self.opts.pattern, self.opts.search.mode, self.opts.search.trigger) + self.visible = true + self.cache = Cache.new(self) + self.labeler = self.opts.labeler or require("flash.labeler").new(self):labeler() + self.ns = vim.api.nvim_create_namespace(self.opts.ns or "flash") + M._states[self] = true + if self.opts.label.rainbow.enabled then + self.rainbow = Rainbow.new(self) + end + + self:update() + return self +end + +---@param target? string|Flash.Match.Find|Flash.Match +---@return Flash.Match? +function M:jump(target) + local match ---@type Flash.Match? + if type(target) == "string" then + match = self:find({ label = target }) + elseif target and target.end_pos then + match = target + elseif target then + match = self:find(target) + else + match = self.target + end + if match then + if self.opts.action then + self.opts.action(match, self) + else + Jump.jump(match, self) + Jump.on_jump(self) + end + return match + end +end + +-- Will restore all window views +function M:restore() + if self.restore_windows then + self.restore_windows() + end +end + +function M:get_matcher(win) + self.matchers[win] = self.matchers[win] or self.matcher(win, self) + return self.matchers[win] +end + +---@param opts? Flash.Match.Find | {label?:string, pos?: Pos} +function M:find(opts) + if opts and opts.label then + for _, m in ipairs(self.results) do + if m.label == opts.label then + return m + end + end + return + end + + opts = Matcher.defaults({ + forward = self.opts.search.forward, + wrap = self.opts.search.wrap, + }, opts) + + local matcher = self:get_matcher(self.win) + local ret = matcher:find(opts) + + if ret then + for _, m in ipairs(self.results) do + if m.pos == ret.pos and m.end_pos == ret.end_pos then + return m + end + end + end + return ret +end + +-- Checks if the given pattern is a jump label and jumps to it. +---@param pattern string +function M:check_jump(pattern) + if not self.visible then + return + end + + if self.opts.search.trigger ~= "" and self.pattern():sub(-1) ~= self.opts.search.trigger then + return + end + local chars = vim.fn.strchars(pattern) + if + pattern:find(self.pattern(), 1, true) == 1 and chars == vim.fn.strchars(self.pattern()) + 1 + then + local label = vim.fn.strcharpart(pattern, chars - 1, 1) + if self:jump(label) then + return true + end + end +end + +---@param opts? {pattern:string, force:boolean, check_jump:boolean} +---@return boolean? abort `true` if the search was aborted +function M:update(opts) + opts = opts or {} + + if opts.pattern then + -- abort if pattern is a jump label + if opts.check_jump ~= false and self:check_jump(opts.pattern) then + return true + end + self.pattern:set(opts.pattern) + end + + if not self.visible then + return + end + + if self.cache:update() or opts.force then + self:_update() + end +end + +function M:hide() + if self.visible then + self.visible = false + Highlight.clear(self.ns) + end +end + +function M:show() + if not self.visible then + self.visible = true + -- force cache to update win and position + self.win = nil + self:update({ force = true }) + end +end + +function M:_update() + -- This is needed because we trigger searches during redraw. + -- We need to save the state of the incsearch so that current match + -- will still be displayed correctly. + if M.is_search() then + Hacks.save_incsearch_state() + end + + self.results = {} + local done = {} ---@type table + ---@type Flash.Matcher[] + local matchers = {} + for _, win in ipairs(self.wins) do + local buf = vim.api.nvim_win_get_buf(win) + matchers[win] = self:get_matcher(win) + local state = self.cache:get_state(win) + for _, m in ipairs(state and state.matches or {}) do + local id = m.pos:id(buf) .. m.end_pos:id(buf) + if not done[id] then + done[id] = true + table.insert(self.results, m) + end + end + end + self.matchers = matchers + + for _, match in ipairs(self.results) do + vim.api.nvim_win_call(match.win, function() + local fold = vim.fn.foldclosed(match.pos[1]) + match.fold = fold ~= -1 and fold or nil + end) + end + + self:update_target() + self.labeler(self.results, self) + + if M.is_search() then + Hacks.restore_incsearch_state() + end + + Highlight.update(self) +end + +function M:update_target() + -- set target to next match. + -- When not using incremental search, + -- we need to set the target to the previous match + self.target = self:find({ + pos = self.pos, + count = vim.v.count1, + }) + + local info = vim.fn.getwininfo(self.win)[1] + local function is_visible() + return self.target and self.target.pos[1] >= info.topline and self.target.pos[1] <= info.botline + end + + if self.opts.search.incremental then + -- only update cursor if the target is not visible + -- and we are not activated + if self.target and not self.is_search() then + vim.api.nvim_win_set_cursor(self.win, self.target.pos) + end + elseif not is_visible() then + self.target = self:find({ + pos = self.pos, + count = vim.v.count1, + forward = not self.opts.search.forward, + }) + if not is_visible() then + self.target = nil + end + end +end + +---@class Flash.Step.Options +---@field actions? table +---@field restore? boolean +---@field abort? fun() +---@field jump_on_max_length? boolean + +---@param opts? Flash.Step.Options +function M:step(opts) + opts = opts or {} + if self.opts.prompt.enabled and not M.is_search() then + Prompt.set(self.pattern()) + end + local actions = opts.actions or self.opts.actions or {} + local c = self:get_char() + if c == nil then + vim.api.nvim_input("") + if opts.restore ~= false then + self:restore() + end + if opts.abort then + opts.abort() + end + return + elseif actions[c] then + local ret = actions[c](self, c) + if ret == nil then + return true + end + return ret + -- jump to first + elseif c == Util.CR then + self:jump() + return + end + + local orig = self.pattern() + + -- break if we jumped + if self:update({ pattern = self.pattern:extend(c) }) then + return + end + + -- when we exceed max length, either jump to the label, + -- or input the last key and break + if self.opts.search.max_length and #self.pattern() > self.opts.search.max_length then + self:update({ pattern = orig }) + if opts.jump_on_max_length ~= false then + self:jump() + end + vim.api.nvim_input(c) + return + end + + -- exit if no results and not in regular search mode + if #self.results == 0 and not self.pattern:empty() and self.pattern.mode ~= 'search' then + if self.opts.search.incremental then + vim.api.nvim_input(c) + end + return + end + + -- autojump if only one result + if #self.results == 1 and self.opts.jump.autojump then + self:jump() + return + end + return true +end + +---@param opts? Flash.Step.Options +function M:loop(opts) + while self:step(opts) do + end + self:hide() + Prompt.hide() +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/util.lua b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/util.lua new file mode 100644 index 00000000..fe4c32b9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/lua/flash/util.lua @@ -0,0 +1,105 @@ +local Hacks = require("flash.hacks") +local require = require("flash.require") + +local M = {} + +function M.t(str) + return vim.api.nvim_replace_termcodes(str, true, true, true) +end + +M.CR = M.t("") +M.ESC = M.t("") +M.BS = M.t("") +M.EXIT = M.t("") +M.LUA_CALLBACK = "\x80\253g" +M.CMD = "\x80\253h" + +function M.exit() + vim.api.nvim_feedkeys(M.EXIT, "nx", false) + vim.api.nvim_feedkeys(M.ESC, "n", false) +end + +---@param buf number +---@param pos number[] (1,0)-indexed position +---@param offset number[] +---@return number[] (1,0)-indexed position +function M.offset_pos(buf, pos, offset) + local row = pos[1] + offset[1] + local ok, lines = pcall(vim.api.nvim_buf_get_lines, buf, row - 1, row, true) + if not ok or lines == nil then + -- fallback to old behavior if anything wrong happens + return { row, math.max(pos[2] + offset[2], 0) } + end + + local line = lines[1] + local charidx = vim.fn.charidx(line, pos[2]) + local col = vim.fn.byteidx(line, charidx + offset[2]) + + return { row, math.max(col, 0) } +end + +function M.get_char() + Hacks.setcursor() + vim.cmd("redraw") + local ok, ret = pcall(vim.fn.getcharstr) + return ok and ret ~= M.ESC and ret or nil +end + +function M.layout_wins() + local queue = { vim.fn.winlayout() } + ---@type table + local wins = {} + while #queue > 0 do + local node = table.remove(queue) + if node[1] == "leaf" then + wins[node[2]] = node[2] + else + vim.list_extend(queue, node[2]) + end + end + return wins +end + +function M.save_layout() + local current_win = vim.api.nvim_get_current_win() + local wins = M.layout_wins() + ---@type table + local state = {} + for _, win in pairs(wins) do + state[win] = vim.api.nvim_win_call(win, vim.fn.winsaveview) + end + return function() + for win, s in pairs(state) do + if vim.api.nvim_win_is_valid(win) then + local buf = vim.api.nvim_win_get_buf(win) + -- never restore terminal buffers to prevent flickering + if vim.bo[buf].buftype ~= "terminal" then + pcall(vim.api.nvim_win_call, win, function() + vim.fn.winrestview(s) + end) + end + end + end + vim.api.nvim_set_current_win(current_win) + state = {} + end +end + +---@param done fun():boolean +---@param on_done fun() +function M.on_done(done, on_done) + local check = assert(vim.loop.new_check()) + local fn = function() + if check:is_closing() then + return + end + if done() then + check:stop() + check:close() + on_done() + end + end + check:start(vim.schedule_wrap(fn)) +end + +return M diff --git a/config/neovim/store/lazy-plugins/flash.nvim/selene.toml b/config/neovim/store/lazy-plugins/flash.nvim/selene.toml new file mode 100644 index 00000000..7312a916 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/selene.toml @@ -0,0 +1 @@ +std="vim" diff --git a/config/neovim/store/lazy-plugins/flash.nvim/stylua.toml b/config/neovim/store/lazy-plugins/flash.nvim/stylua.toml new file mode 100644 index 00000000..d9ed7c42 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/stylua.toml @@ -0,0 +1,5 @@ +indent_type = "Spaces" +indent_width = 2 +column_width = 100 +[sort_requires] +enabled = true diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/char_spec.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/char_spec.lua new file mode 100644 index 00000000..41214b24 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/char_spec.lua @@ -0,0 +1,90 @@ +local Char = require("flash.plugins.char") +local assert = require("luassert") +require("flash").setup() + +describe("char", function() + local function set(text, pos) + local lines = vim.split(vim.trim(text), "\n") + lines = vim.tbl_map(function(line) + return vim.trim(line) + end, lines) + vim.api.nvim_buf_set_lines(1, 0, -1, false, lines) + vim.api.nvim_win_set_cursor(0, pos or { 1, 0 }) + end + + before_each(function() + set("abc_xyz", { 1, 3 }) + local state = require("flash.plugins.char").state + if state then + state:hide() + end + end) + + local function get() + return table.concat(vim.api.nvim_buf_get_lines(1, 0, -1, false), "\n") + end + + --- tests for deletes with ftFT motions + --- test always runs on input "abc_xyz" + --- with cursor at position { 1, 3 } + local tests = { + -- f + { motion = "dfx", result = "abcyz" }, + { motion = "dfz", result = "abc" }, + { motion = "df_", result = "abc_xyz" }, + { motion = "dfa", result = "abc_xyz" }, + -- t + { motion = "dtx", result = "abcxyz" }, + { motion = "dtz", result = "abcz" }, + { motion = "dt_", result = "abc_xyz" }, + { motion = "dta", result = "abc_xyz" }, + -- F + { motion = "dFa", result = "_xyz" }, + { motion = "dFc", result = "ab_xyz" }, + { motion = "dF_", result = "abc_xyz" }, + { motion = "dFx", result = "abc_xyz" }, + -- T + { motion = "dTa", result = "a_xyz" }, + { motion = "dTc", result = "abc_xyz" }, + { motion = "dT_", result = "abc_xyz" }, + { motion = "dTx", result = "abc_xyz" }, + } + + for _, test in ipairs(tests) do + it("works with " .. test.motion, function() + vim.cmd("norm! " .. test.motion) + assert.same(test.result, get()) + end) + end + for _, test in ipairs(tests) do + it("works with " .. test.motion .. " (flash)", function() + -- vim.api.nvim_feedkeys(test.motion, "mtx", false) + vim.cmd("norm " .. test.motion) + assert.same(test.result, get()) + end) + end + + local input = "abcd1abcd2abcd" + for _, motion in ipairs({ "f", "t", "F", "T" }) do + for col = 0, #input - 1 do + for count = -1, 3 do + count = count == -1 and "" or count + for _, char in ipairs({ "a", "b", "c", "d" }) do + local cmd = count .. "d" .. motion .. char + local pos = { 1, col } + it("works with " .. cmd .. " at " .. col, function() + set(input, pos) + vim.cmd("norm! " .. cmd) + local ret = get() + set(input, pos) + if Char.state then + Char.state:hide() + end + vim.cmd("norm " .. cmd) + assert.same(ret, get()) + end) + end + end + end + end +end) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/config_spec.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/config_spec.lua new file mode 100644 index 00000000..917805ae --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/config_spec.lua @@ -0,0 +1,38 @@ +local Config = require("flash.config") +local assert = require("luassert") + +describe("config", function() + before_each(function() + Config.setup() + end) + + it("processes modes", function() + Config.setup({ modes = { foo = { bar = true } } }) + assert.is_true(Config.modes.foo.bar) + assert.is_true(Config.get({ mode = "foo" }).bar) + end) + + it("processes modes recursively", function() + Config.setup({ + modes = { + foo = { mode = "bar" }, + bar = { field = true, mode = "foo" }, + }, + }) + assert.is_true(Config.get({ mode = "foo" }).field) + assert.is_true(Config.get({ mode = "bar" }).field) + end) + + it("processes modes recursively in correct order", function() + Config.setup({ + modes = { + a = { mode = "b", v = "a" }, + b = { mode = "c", v = "b" }, + c = { v = "c" }, + }, + }) + assert.same("d", Config.get({ mode = "a", v = "d" }).v) + assert.same("a", Config.get({ mode = "a" }).v) + assert.same("b", Config.get({ mode = "b" }).v) + end) +end) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/init.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/init.lua new file mode 100644 index 00000000..a72b101d --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/init.lua @@ -0,0 +1,36 @@ +local M = {} + +function M.root(root) + local f = debug.getinfo(1, "S").source:sub(2) + return vim.fn.fnamemodify(f, ":p:h:h") .. "/" .. (root or "") +end + +---@param plugin string +function M.load(plugin) + local name = plugin:match(".*/(.*)") + local package_root = M.root(".tests/site/pack/deps/start/") + if not vim.loop.fs_stat(package_root .. name) then + print("Installing " .. plugin) + vim.fn.mkdir(package_root, "p") + vim.fn.system({ + "git", + "clone", + "--depth=1", + "https://github.com/" .. plugin .. ".git", + package_root .. "/" .. name, + }) + end +end + +function M.setup() + vim.cmd([[set runtimepath=$VIMRUNTIME]]) + vim.opt.runtimepath:append(M.root()) + vim.opt.packpath = { M.root(".tests/site") } + M.load("nvim-lua/plenary.nvim") + vim.env.XDG_CONFIG_HOME = M.root(".tests/config") + vim.env.XDG_DATA_HOME = M.root(".tests/data") + vim.env.XDG_STATE_HOME = M.root(".tests/state") + vim.env.XDG_CACHE_HOME = M.root(".tests/cache") +end + +M.setup() diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/run b/config/neovim/store/lazy-plugins/flash.nvim/tests/run new file mode 100755 index 00000000..bec2227c --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/run @@ -0,0 +1,3 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/sh + +nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests {minimal_init = 'tests//init.lua', sequential = true}" diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/search/find_spec.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/find_spec.lua new file mode 100644 index 00000000..bf80f963 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/find_spec.lua @@ -0,0 +1,51 @@ +local Search = require("flash.search") +local State = require("flash.state") + +describe("search", function() + before_each(function() + vim.opt.ignorecase = true + vim.opt.smartcase = true + vim.api.nvim_buf_set_lines(1, 0, -1, false, {}) + end) + + ---@param opts? Flash.State.Config | string + local function get_search(opts) + if type(opts) == "string" then + opts = { pattern = opts } + end + local state = State.new(opts) + local win = vim.api.nvim_get_current_win() + return Search.new(win, state) + end + + local function set(text, pos) + local lines = vim.split(vim.trim(text), "\n") + lines = vim.tbl_map(function(line) + return vim.trim(line) + end, lines) + vim.api.nvim_buf_set_lines(1, 0, -1, false, lines) + vim.api.nvim_win_set_cursor(0, pos or { 1, 0 }) + end + + it("finds matches", function() + set([[ + foobar + line1 + line2 + ]]) + + local search = get_search("line") + local matches = search:get() + assert.same("\\Vline", search.state.pattern.search) + assert.same({ + { win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } }, + { win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } }, + }, matches) + assert.same({ win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } }, search:find()) + assert.same({ win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } }, search:find({ count = 2 })) + assert.same( + { win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } }, + search:find({ forward = false }) + ) + end) +end) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/search/jump_spec.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/jump_spec.lua new file mode 100644 index 00000000..29d28628 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/jump_spec.lua @@ -0,0 +1,85 @@ +local Jump = require("flash.jump") +local Pos = require("flash.search.pos") +local State = require("flash.state") +local assert = require("luassert") + +describe("jump", function() + local function set(text, pos) + local lines = vim.split(vim.trim(text), "\n") + lines = vim.tbl_map(function(line) + return vim.trim(line) + end, lines) + if vim.fn.mode() == "v" then + vim.cmd("normal! v") + end + vim.api.nvim_buf_set_lines(1, 0, -1, false, lines) + vim.api.nvim_win_set_cursor(0, pos or { 1, 0 }) + end + + ---@param match Flash.Match + ---@param opts? Flash.State.Config + local function jump(match, opts) + match.win = vim.api.nvim_get_current_win() + local state = State.new(opts) + Jump.jump(match, state) + end + + before_each(function() + vim.opt.ignorecase = true + vim.opt.smartcase = true + vim.api.nvim_buf_set_lines(0, 0, -1, false, {}) + set([[ + line1 foo + line2 foo + line3 foo + ]]) + end) + + it("jumps to start", function() + local match = { + pos = Pos({ 1, 6 }), + end_pos = Pos({ 1, 8 }), + } + jump(match) + assert.same({ 1, 6 }, vim.api.nvim_win_get_cursor(0)) + end) + + it("selects to start", function() + assert.same({ 1, 0 }, vim.api.nvim_win_get_cursor(0)) + local match = { + pos = Pos({ 1, 6 }), + end_pos = Pos({ 1, 8 }), + } + + assert.same("n", vim.fn.mode()) + vim.cmd("normal! v") + jump(match, { jump = { pos = "start", inclusive = false } }) + assert.same("v", vim.fn.mode()) + vim.cmd("normal! v") + + assert.same({ 1, 6 }, vim.api.nvim_win_get_cursor(0)) + assert.same({ 1, 0 }, vim.api.nvim_buf_get_mark(0, "<")) + assert.same({ 1, 6 }, vim.api.nvim_buf_get_mark(0, ">")) + end) + + -- it("yanks to start", function() + -- assert.same({ 1, 0 }, vim.api.nvim_win_get_cursor(0)) + -- local match = { + -- pos = Pos({ 1, 6 }), + -- end_pos = Pos({ 1, 8 }), + -- } + -- + -- assert.same("n", vim.fn.mode()) + -- -- vim.cmd("normal! y") + -- vim.api.nvim_feedkeys("y", "n", false) + -- assert.same("no", vim.fn.mode(true)) + -- jump(match, { jump = { pos = "start", inclusive = false } }) + -- assert.same("n", vim.fn.mode()) + -- + -- vim.print(vim.fn.getmarklist(vim.api.nvim_get_current_buf())) + -- + -- assert.same({ 1, 6 }, vim.api.nvim_win_get_cursor(0)) + -- assert.same({ 1, 0 }, vim.api.nvim_buf_get_mark(0, "[")) + -- assert.same({ 1, 6 }, vim.api.nvim_buf_get_mark(0, "]")) + -- end) +end) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/search/labels_spec.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/labels_spec.lua new file mode 100644 index 00000000..24dbfae0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/labels_spec.lua @@ -0,0 +1,103 @@ +local Labeler = require("flash.labeler") +local Search = require("flash.search") +local State = require("flash.state") +local assert = require("luassert") + +describe("labeler", function() + local function set(text, pos) + local lines = vim.split(vim.trim(text), "\n") + lines = vim.tbl_map(function(line) + return vim.trim(line) + end, lines) + vim.api.nvim_buf_set_lines(1, 0, -1, false, lines) + vim.api.nvim_win_set_cursor(0, pos or { 1, 0 }) + end + + local function search(pattern) + local state = State.new({ pattern = pattern, search = { + mode = "search", + } }) + return Labeler.new(state) + end + + before_each(function() + vim.opt.ignorecase = true + vim.opt.smartcase = true + end) + + it("skips labels", function() + set([[ + foo foo + bar + barfoo + ]]) + local labels = search("bar"):skip(1000, { "a", "b", "c", "f" }) + assert.same({ "a", "b", "c" }, labels) + end) + it("skips all labels for an empty pattern", function() + set([[ + test pattern + ]]) + local labels = search(""):skip(1000, { "a", "b", "c", "t" }) + assert.same({}, labels) + end) + + it("skips all labels for an invalid pattern", function() + set([[ + invalid pattern + ]]) + local labels = search("[i"):skip(1000, { "a", "b", "i", "v" }) + assert.same({}, labels) + end) + + it("skips all labels when pattern ends with unescaped backslash", function() + set([[ + pattern with backslash\ + ]]) + local labels = search("backslash\\"):skip(1000, { "a", "b", "s", "\\" }) + assert.same({}, labels) + end) + + it("skips label that matches pattern", function() + set([[ + pattern withc + ]]) + local labels = search("with"):skip(1000, { "a", "b", "c", "p", "w" }) + assert.same({ "a", "b", "p", "w" }, labels) + end) + + it("considers ignorecase when skipping labels", function() + set([[ + pattern withC + ]]) + vim.opt.ignorecase = true + local labels = search("with"):skip(1000, { "a", "b", "C", "p", "w" }) + assert.same({ "a", "b", "p", "w" }, labels) + end) + + it("considers ignorecase3 when skipping labels", function() + set([[ + pattern withC + ]]) + vim.opt.ignorecase = true + local labels = search("with"):skip(1000, { "a", "b", "c", "p", "w" }) + assert.same({ "a", "b", "p", "w" }, labels) + end) + + it("considers ignorecase2 when skipping labels", function() + set([[ + pattern withc + ]]) + vim.opt.ignorecase = true + local labels = search("with"):skip(1000, { "a", "b", "C", "p", "w" }) + assert.same({ "a", "b", "p", "w" }, labels) + end) + + it("skips all labels when pattern is an incomplete regex", function() + set([[ + pattern with incomplete regex ( + ]]) + local labels = search("regex \\("):skip(1000, { "a", "b", "i", "r", "(" }) + assert.same({}, labels) + end) +end) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/search/matcher_spec.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/matcher_spec.lua new file mode 100644 index 00000000..0356c45a --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/matcher_spec.lua @@ -0,0 +1,184 @@ +local Matcher = require("flash.search.matcher") +local assert = require("luassert") + +describe("search", function() + local matcher = Matcher.new(1000) + local matches = { + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } }, + { win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } }, + { win = 1000, pos = { 3, 4 }, end_pos = { 3, 6 } }, + } + matcher:set(matches) + + it("sets matches", function() + assert.same(matches, matcher:get()) + end) + + it("finds backward from after end", function() + assert.same( + matches[3], + matcher:find({ + forward = false, + pos = { 4, 6 }, + wrap = false, + }) + ) + end) + + it("handles count = 0", function() + assert.same( + matches[2], + matcher:find({ + pos = { 1, 7 }, + count = 0, + }) + ) + assert.is_nil(matcher:find({ + pos = { 2, 7 }, + count = 0, + })) + end) + + it("returns forward matches", function() + assert.same( + { matches[3] }, + matcher:get({ + from = { 2, 6 }, + }) + ) + end) + + it("returns forward matches", function() + assert.same( + { matches[3] }, + matcher:get({ + from = { 3, 4 }, + }) + ) + end) + + it("returns backward matches", function() + assert.same( + { matches[1] }, + matcher:get({ + to = { 1, 6 }, + }) + ) + end) + + it("returns backward matches", function() + assert.same( + { matches[1] }, + matcher:get({ + to = { 1, 0 }, + }) + ) + end) + + it("finds matcher", function() + assert.same({ win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } }, matcher:find()) + assert.same({ win = 1000, pos = { 3, 4 }, end_pos = { 3, 6 } }, matcher:find({ count = 2 })) + assert.same( + { win = 1000, pos = { 3, 4 }, end_pos = { 3, 6 } }, + matcher:find({ forward = false }) + ) + assert.same( + { win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } }, + matcher:find({ + forward = false, + pos = { 2, 7 }, + }) + ) + assert.same( + { win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } }, + matcher:find({ + forward = false, + pos = { 3, 4 }, + }) + ) + end) + + it("sorts matches", function() + local m = Matcher.new(1000) + local mm = { + { pos = { 3, 4 }, end_pos = { 3, 6 } }, + { pos = { 1, 0 }, end_pos = { 1, 2 } }, + { pos = { 1, 7 }, end_pos = { 1, 9 } }, + } + m:set(mm) + assert.same({ + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } }, + { win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } }, + { win = 1000, pos = { 3, 4 }, end_pos = { 3, 6 } }, + }, m:get()) + end) + it("finds forward skipping match at current position", function() + assert.same( + matches[2], + matcher:find({ + forward = true, + pos = { 1, 0 }, + wrap = false, + }) + ) + end) + + it("finds backward skipping match at current position", function() + assert.same( + matches[2], + matcher:find({ + forward = false, + pos = { 3, 4 }, + wrap = true, + }) + ) + end) + + it("finds forward from a non-match position", function() + assert.same( + matches[2], + matcher:find({ + forward = true, + pos = { 1, 3 }, + wrap = false, + }) + ) + end) + + it("finds backward from a non-match position", function() + assert.same( + matches[2], + matcher:find({ + forward = false, + pos = { 3, 2 }, + wrap = true, + }) + ) + end) + + it("returns nil when wrapping is disabled and no match is found forward", function() + assert.is_nil(matcher:find({ + forward = true, + pos = { 4, 0 }, + wrap = false, + })) + end) + + it("returns nil when wrapping is disabled and no match is found backward", function() + assert.is_nil(matcher:find({ + forward = false, + pos = { 0, 0 }, + wrap = false, + })) + end) + + it("can handle multiple matches on the same pos", function() + local mm = Matcher.new(1000) + mm:set({ + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 1 } }, + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } }, + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 3 } }, + }) + -- assert.same() + end) +end) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/search/pos_spec.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/pos_spec.lua new file mode 100644 index 00000000..3265d41b --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/pos_spec.lua @@ -0,0 +1,43 @@ +--# selene: allow(incorrect_standard_library_use) +local Pos = require("flash.search.pos") + +describe("pos", function() + it("handles new", function() + assert.same(Pos({ row = 1, col = 2 }), { 1, 2 }) + assert.same(Pos({ 1, 2 }), { 1, 2 }) + end) + + it("handles cmp", function() + assert(Pos({ 1, 2 }) < Pos({ 2, 2 })) + assert(Pos({ 1, 2 }) < Pos({ 1, 3 })) + assert(Pos({ 1, 2 }) <= Pos({ 1, 2 })) + assert(Pos({ 1, 2 }) <= Pos({ 2, 2 })) + assert(Pos({ 1, 2 }) <= Pos({ 1, 3 })) + assert(Pos({ 1, 2 }) == Pos({ 1, 2 })) + assert(Pos({ 1, 2 }) == Pos({ 1, 2 })) + assert(Pos({ 1, 2 }) ~= Pos({ 2, 2 })) + assert(Pos({ 1, 2 }) <= Pos({ 2, 2 })) + assert(Pos({ 1, 2 }) >= Pos({ 1, 2 })) + assert(Pos({ 1, 2 }) >= Pos({ 1, 1 })) + end) + + it("handles add", function() + assert.same(Pos({ 1, 2 }) + Pos({ 1, 2 }), { 2, 4 }) + assert.same(Pos({ 1, 2 }) + { 1, 2 }, { 2, 4 }) + assert.same(Pos({ 1, 2 }) + { row = 1, col = 2 }, { 2, 4 }) + end) + + it("handles sub", function() + assert.same(Pos({ 1, 2 }) - Pos({ 1, 2 }), { 0, 0 }) + assert.same(Pos({ 1, 2 }) - { 1, 2 }, { 0, 0 }) + assert.same(Pos({ 1, 2 }) - { row = 1, col = 2 }, { 0, 0 }) + end) + + it("handles tostring", function() + assert.same(tostring(Pos({ 1, 2 })), "[1, 2]") + end) + + it("handles id", function() + assert.same(Pos({ 1, 2 }):id(123), "123:1:2") + end) +end) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/search/search_spec.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/search_spec.lua new file mode 100644 index 00000000..edb9e857 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/search_spec.lua @@ -0,0 +1,182 @@ +local Search = require("flash.search") +local State = require("flash.state") +local assert = require("luassert") + +describe("search", function() + local function set(text, pos) + local lines = vim.split(vim.trim(text), "\n") + lines = vim.tbl_map(function(line) + return vim.trim(line) + end, lines) + vim.api.nvim_buf_set_lines(1, 0, -1, false, lines) + vim.api.nvim_win_set_cursor(0, pos or { 1, 0 }) + end + + before_each(function() + vim.opt.ignorecase = true + vim.opt.smartcase = true + vim.api.nvim_buf_set_lines(1, 0, -1, false, {}) + set([[ + foo foo + bar + barfoo + ]]) + end) + + local state = State.new({ pattern = "foo" }) + local search = Search.new(1000, state) + + local matches = { + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } }, + { win = 1000, pos = { 1, 4 }, end_pos = { 1, 6 } }, + { win = 1000, pos = { 3, 3 }, end_pos = { 3, 5 } }, + } + + it("sets matches", function() + assert.same(matches, search:get()) + end) + + it("finds backward from after end", function() + assert.same( + matches[3], + search:find({ + forward = false, + pos = { 4, 6 }, + wrap = false, + }) + ) + end) + + it("handles count = 0", function() + assert.same( + matches[2], + search:find({ + pos = { 1, 4 }, + count = 0, + }) + ) + assert.is_nil(search:find({ + pos = { 2, 7 }, + count = 0, + })) + end) + + it("returns forward matches", function() + assert.same( + { matches[3] }, + search:get({ + from = { 2, 6 }, + }) + ) + end) + + it("returns forward matches", function() + assert.same( + { matches[3] }, + search:get({ + from = { 3, 3 }, + }) + ) + end) + + it("returns backward matches", function() + assert.same( + { matches[1] }, + search:get({ + to = { 1, 3 }, + }) + ) + end) + + it("returns backward matches at pos", function() + assert.same( + { matches[1] }, + search:get({ + to = { 1, 0 }, + }) + ) + end) + + it("finds matcher", function() + assert.same({ win = 1000, pos = { 1, 4 }, end_pos = { 1, 6 } }, search:find()) + assert.same({ win = 1000, pos = { 3, 3 }, end_pos = { 3, 5 } }, search:find({ count = 2 })) + assert.same( + { win = 1000, pos = { 3, 3 }, end_pos = { 3, 5 } }, + search:find({ forward = false }) + ) + assert.same( + { win = 1000, pos = { 1, 4 }, end_pos = { 1, 6 } }, + search:find({ + forward = false, + pos = { 2, 7 }, + }) + ) + assert.same( + { win = 1000, pos = { 1, 4 }, end_pos = { 1, 6 } }, + search:find({ + forward = false, + pos = { 3, 2 }, + }) + ) + end) + + it("finds forward skipping match at current position", function() + assert.same( + matches[2], + search:find({ + forward = true, + pos = { 1, 0 }, + wrap = false, + }) + ) + end) + + it("finds backward skipping match at current position", function() + assert.same( + matches[2], + search:find({ + forward = false, + pos = { 3, 3 }, + wrap = true, + }) + ) + end) + + it("finds forward from a non-match position", function() + assert.same( + matches[2], + search:find({ + forward = true, + pos = { 1, 3 }, + wrap = false, + }) + ) + end) + + it("finds backward from a non-match position", function() + assert.same( + matches[2], + search:find({ + forward = false, + pos = { 3, 2 }, + wrap = true, + }) + ) + end) + + it("returns nil when wrapping is disabled and no match is found forward", function() + assert.is_nil(search:find({ + forward = true, + pos = { 4, 0 }, + wrap = false, + })) + end) + + it("returns nil when wrapping is disabled and no match is found backward", function() + assert.is_nil(search:find({ + forward = false, + pos = { 1, 0 }, + wrap = false, + })) + end) +end) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/search/view_spec.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/view_spec.lua new file mode 100644 index 00000000..c5b0c63a --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/search/view_spec.lua @@ -0,0 +1,190 @@ +local Search = require("flash.search") +local State = require("flash.state") + +describe("search.view", function() + before_each(function() + vim.opt.ignorecase = true + vim.opt.smartcase = true + vim.api.nvim_buf_set_lines(1, 0, -1, false, {}) + end) + + local function get_matches(pattern) + local state = State.new({ pattern = pattern, search = { + mode = "search", + } }) + local win = vim.api.nvim_get_current_win() + local search = Search.new(win, state) + return search:get() + end + + local function set(text, pos) + local lines = vim.split(vim.trim(text), "\n") + lines = vim.tbl_map(function(line) + return vim.trim(line) + end, lines) + vim.api.nvim_buf_set_lines(1, 0, -1, false, lines) + vim.api.nvim_win_set_cursor(0, pos or { 1, 0 }) + end + + it("finds matches", function() + set([[ + foobar + line1 + line2 + ]]) + + local matches = get_matches("line") + assert.same({ + { win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } }, + { win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } }, + }, matches) + end) + + it("finds multi matches on same line", function() + set([[ + foobar foobar + line1 + lineFoo + ]]) + + local matches = get_matches("foo") + assert.same({ + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } }, + { win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } }, + { win = 1000, pos = { 3, 4 }, end_pos = { 3, 6 } }, + }, matches) + end) + + it("deals with case", function() + set([[ + foobar + Line1 + line2 + ]]) + + local matches = get_matches("line") + assert.same({ + { win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } }, + { win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } }, + }, matches) + end) + + it("deals with smartcase", function() + set([[ + foobar + Line1 + line2 + ]]) + + local matches = get_matches("Line") + assert.same({ + { win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } }, + }, matches) + end) + + it("finds matches on each line", function() + set([[ + line1 + line2 + line3 + ]]) + + local matches = get_matches("line") + assert.same({ + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 3 } }, + { win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } }, + { win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } }, + }, matches) + end) + + it("handles '\\Vi\\zs\\.'", function() + set([[ + line1 + line2 + line3 + ]]) + + local matches = get_matches([[\Vi\zs\m.]]) + assert.same({ + { win = 1000, pos = { 1, 2 }, end_pos = { 1, 2 } }, + { win = 1000, pos = { 2, 2 }, end_pos = { 2, 2 } }, + { win = 1000, pos = { 3, 2 }, end_pos = { 3, 2 } }, + }, matches) + end) + + it("handles ^", function() + set([[ + foobar + line1 + line2 + ]]) + + local matches = get_matches("^") + assert.same({ + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 0 } }, + { win = 1000, pos = { 2, 0 }, end_pos = { 2, 0 } }, + { win = 1000, pos = { 3, 0 }, end_pos = { 3, 0 } }, + }, matches) + end) + + it("handles ^", function() + set([[ + foobar + line1 + line2 + ]]) + + local matches = get_matches("^") + assert.same({ + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 0 } }, + { win = 1000, pos = { 2, 0 }, end_pos = { 2, 0 } }, + { win = 1000, pos = { 3, 0 }, end_pos = { 3, 0 } }, + }, matches) + end) + + it("handles ^.\\?", function() + set([[ + foobar + line1 + line2 + ]]) + + local matches = get_matches("^.\\?") + assert.same({ + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 0 } }, + { win = 1000, pos = { 2, 0 }, end_pos = { 2, 0 } }, + { win = 1000, pos = { 3, 0 }, end_pos = { 3, 0 } }, + }, matches) + end) + + it("handles ^l", function() + set([[ + foobar + line1 + line2 + ]]) + + local matches = get_matches("^l") + assert.same({ + { win = 1000, pos = { 2, 0 }, end_pos = { 2, 0 } }, + { win = 1000, pos = { 3, 0 }, end_pos = { 3, 0 } }, + }, matches) + end) + + it("handles wrapping", function() + set( + [[ + foo + line1 + foo + ]], + { 3, 0 } + ) + + local matches = get_matches("foo") + assert.same({ + { win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } }, + { win = 1000, pos = { 3, 0 }, end_pos = { 3, 2 } }, + }, matches) + end) +end) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/tests/unicode_spec.lua b/config/neovim/store/lazy-plugins/flash.nvim/tests/unicode_spec.lua new file mode 100644 index 00000000..60985b53 --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/tests/unicode_spec.lua @@ -0,0 +1,11 @@ +local State = require("flash.state") +local assert = require("luassert") + +describe("unicode", function() + local labels = "😅😀🏇🐎🐴🐵🐒" + + it("splits labels", function() + local state = State.new({ labels = labels }) + assert.same({ "😅", "😀", "🏇", "🐎", "🐴", "🐵", "🐒" }, state:labels()) + end) +end) diff --git a/config/neovim/store/lazy-plugins/flash.nvim/vim.toml b/config/neovim/store/lazy-plugins/flash.nvim/vim.toml new file mode 100644 index 00000000..4206f6cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/flash.nvim/vim.toml @@ -0,0 +1,49 @@ +[selene] +base = "lua51" +name = "vim" + +[vim] +any = true + +[jit] +any = true + +[[describe.args]] +type = "string" +[[describe.args]] +type = "function" + +[[it.args]] +type = "string" +[[it.args]] +type = "function" + +[[before_each.args]] +type = "function" +[[after_each.args]] +type = "function" + +[assert.is_not] +any = true + +[[assert.equals.args]] +type = "any" +[[assert.equals.args]] +type = "any" +[[assert.equals.args]] +type = "any" +required = false + +[[assert.same.args]] +type = "any" +[[assert.same.args]] +type = "any" + +[[assert.truthy.args]] +type = "any" + +[[assert.spy.args]] +type = "any" + +[[assert.stub.args]] +type = "any" diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/.codecov.yml b/config/neovim/store/lazy-plugins/git-messenger.vim/.codecov.yml new file mode 100644 index 00000000..781ee249 --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/.codecov.yml @@ -0,0 +1,10 @@ +coverage: + status: + project: + default: + target: 0% + patch: + default: + target: 0% + +comment: false diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/.coveragerc b/config/neovim/store/lazy-plugins/git-messenger.vim/.coveragerc new file mode 100644 index 00000000..8cddf91c --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/.coveragerc @@ -0,0 +1,3 @@ +[run] +plugins = covimerage +data_file = .coverage_covimerage diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/git-messenger.vim/.github/workflows/ci.yml new file mode 100644 index 00000000..813a5785 --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/.github/workflows/ci.yml @@ -0,0 +1,90 @@ +name: CI +on: [push, pull_request] + +jobs: + test: + name: Unit tests + strategy: + matrix: + os: [macos-latest, ubuntu-latest] + neovim: [true, false] + version: [stable, nightly] + # Exclude Neovim nightly since Neovim's release workflow is fairly unstable: https://github.com/neovim/neovim/actions/workflows/release.yml + exclude: + - neovim: true + version: nightly + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - name: Set Git user for running Git commands in unit tests + run: | + git config --global user.email "users@noreply.github.com" + git config --global user.name "github-action" + - name: Fetch Git history for unit tests + run: git fetch --no-tags --prune --unshallow + - name: Checkout themis.vim + uses: actions/checkout@v2 + with: + repository: thinca/vim-themis + path: vim-themis + - uses: rhysd/action-setup-vim@v1 + id: vim + with: + version: ${{ matrix.version }} + neovim: ${{ matrix.neovim }} + - name: Run unit tests + env: + THEMIS_VIM: ${{ steps.vim.outputs.executable }} + THEMIS_PROFILE: profile.txt + run: ./vim-themis/bin/themis ./test/all.vimspec + - uses: actions/setup-python@v1 + - name: Report coverage + run: | + # https://github.com/Vimjas/covimerage/issues/95 + pip install 'click<8.0.0' + pip install covimerage + covimerage write_coverage profile.txt + coverage report + coverage xml + - name: Upload coverage to codecov + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + test-windows: + name: Unit tests on Windows + strategy: + matrix: + neovim: [true, false] + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Set Git user for running Git commands in unit tests + run: | + git config --global user.email "users@noreply.github.com" + git config --global user.name "github-action" + - name: Fetch Git history for unit tests + run: git fetch --no-tags --prune --unshallow + - name: Checkout themis.vim + uses: actions/checkout@v2 + with: + repository: thinca/vim-themis + path: vim-themis + - uses: rhysd/action-setup-vim@v1 + id: vim + with: + version: stable + neovim: ${{ matrix.neovim }} + - name: Run unit tests + env: + THEMIS_VIM: ${{ steps.vim.outputs.executable }} + run: ./vim-themis/bin/themis ./test/all.vimspec + + vint: + name: Run vint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + - run: pip install vim-vint + - run: vint --warning --verbose --enable-neovim ./autoload ./plugin diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/.gitignore b/config/neovim/store/lazy-plugins/git-messenger.vim/.gitignore new file mode 100644 index 00000000..172242ed --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/.gitignore @@ -0,0 +1,5 @@ +/vim-themis +/htmlcov +/coverage.xml +/.coverage_covimerage +/doc/tags diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/LICENSE b/config/neovim/store/lazy-plugins/git-messenger.vim/LICENSE new file mode 100644 index 00000000..01acc62d --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 rhysd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/README.md b/config/neovim/store/lazy-plugins/git-messenger.vim/README.md new file mode 100644 index 00000000..9b21d091 --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/README.md @@ -0,0 +1,357 @@ +git-messenger.vim +================= +[![Build Status][ci-badge]][ci] +[![codecov][codecov-badge]][codecov] + +[git-messenger.vim][repo] is a Vim/Neovim plugin to reveal the hidden message from Git under the +cursor quickly. It shows the history of commits under the cursor in popup window. + +This plugin shows the message of the last commit in a 'popup window'. If the last commit is not +convenient, you can explore older commits in the popup window. Additionally you can also check diff +of the commit. + +The popup window is implemented in + +- Floating window on Neovim (0.4 or later) +- Preview window on Vim (8 or later) or Neovim (0.3 or earlier) + +The floating window is definitely recommended since it can shows the information near the cursor. + +I wrote [a Japanese blogpost for this plugin](https://rhysd.hatenablog.com/entry/2019/03/10/230119). + + + +## Why? + +When you're modifying unfamiliar codes, you would sometimes wonder 'why was this line added?' or +'why this value was chosen?' in the source code. The answer sometimes lays in a commit message, +especially in message of the last commit which modifies the line. + + + +## Screenshot + +#### Show popup window with Neovim v0.4.0-dev + +main screenshot + +#### Exploring older commits + +history screenshot + +#### Exploring diff of the commit (you may be also interested in `g:git_messenger_include_diff`) + +diff screenshot + +#### Switching unified diffs and word diffs + +word diff screenshot + + +## Installation + +Please ensure the following requirement before installing this plugin. + +- Git v1.8.5 or later (for `-C` option of `git` command) + +If you use any package manager, please follow its instruction. + +With [vim-plug](https://github.com/junegunn/vim-plug): + +```vim +Plug 'rhysd/git-messenger.vim' +``` + +With [dein.vim](https://github.com/Shougo/dein.vim): + +```vim +call dein#add('rhysd/git-messenger.vim', { + \ 'lazy' : 1, + \ 'on_cmd' : 'GitMessenger', + \ 'on_map' : '(git-messenger)', + \ }) +``` + +With [minpac](https://github.com/k-takata/minpac): + +```vim +call minpac#add('rhysd/git-messenger.vim') +``` + +if you use Vim's builtin packager, please follow the instruction at `:help pack-add`. + +To enable a floating window support, you need to install Neovim 0.4 or later. +Please follow [the official instruction][nvim-install]. + +To check if Neovim's floating window feature is available, try `:checkhealth`. + + + +## Usage + +Only one mapping (or one command) provides all features of this plugin. Briefly, move cursor to +the position and run `:GitMessenger` or `gm`. If you see an error message, please try +[health check](#health-check). + +### Commands + +``` +:GitMessenger +``` + +Behavior of this command is depending on the situation. You can do every operations only with this +mapping. + +- Normally, it opens a popup window with the last commit message +- When a popup window is already open, it moves cursor into the window +- When a cursor is within a popup window, it closes the window + +It opens a popup window with the last commit message which modified the line at cursor. The popup +window shows following contents: + +- **History:** `History: {page number}` In popup window, `o`/`O` navigates to previous/next commit. +- **Commit:** `Commit: {hash}` The commit hash +- **Author:** `Author: {name}<{email}>` Author name and mail address of the commit +- **Committer:** `Committer: {name}<{email}>` Committer name and mail address of the commit when + committer is different from author +- **Date:** `Date: {date}` Author date of the commit in system format + - When a committer date and an author date are different (e.g. the commit was created again with + `git commit --amend`), both **Author Date:** and **Committer Date:** are shown. +- **Summary:** First line after `Date:` header line is a summary of commit +- **Body:** After summary, commit body is put (if the commit has body) + +The popup window will be automatically closed when you move the cursor so you don't need to close +it manually. + +Running this command while a popup window is open, it moves the cursor into the window. +This behavior is useful when the commit message is too long and window cannot show the whole content. +By moving the cursor into the popup window, you can see the rest of contents by scrolling it. +You can also see the older commits. + +Following mappings are defined within popup window. + +| Mapping | Description | +|---------|--------------------------------------------------------------| +| `q` | Close the popup window | +| `o` | **o**lder. Back to older commit at the line | +| `O` | Opposite to `o`. Forward to newer commit at the line | +| `d` | Toggle unified diff hunks only in current file of the commit | +| `D` | Toggle all unified diff hunks of the commit | +| `r` | Toggle word diff hunks only in current file of the commit | +| `R` | Toggle all word diff hunks of current commit | +| `?` | Show mappings help | + +### Mappings + +``` +(git-messenger) +``` + +The same as running `:GitMessenger` command. + +By default, this plugin defines following mapping. + +```vim +nmap gm (git-messenger) +``` + +If you don't like the default mapping, set `g:git_messenger_no_default_mappings` to `v:true` in +your `.vimrc` or `init.vim` and map the `` mapping to your favorite key sequence. + +For example: + +```vim +nmap m (git-messenger) +``` + +Some other additional `` mappings. Please read [`:help git-messenger`][doc]. + +### Variables + +Some global variables are available to configure the behavior of this plugin. + +#### `g:git_messenger_close_on_cursor_moved` (Default: `v:true`) + +When this value is set to `v:false`, a popup window is no longer closed automatically when moving a +cursor after the window is shown up. + +#### `g:git_messenger_include_diff` (Default: `"none"`) + +One of `"none"`, `"current"`, `"all"`. + +When this value is not set to `"none"`, a popup window includes diff hunks of the commit at showing +up. `"current"` includes diff hunks of only current file in the commit. `"all"` includes all diff +hunks in the commit. + +Please note that typing `d`/`D` or `r`/`R` in popup window toggle showing diff +hunks even if this value is set to `"none"`. + +#### `g:git_messenger_git_command` (Default: `"git"`) + +`git` command to retrieve commit messages. If your `git` executable is not in `$PATH` directories, +please specify the path to the executable. + +#### `g:git_messenger_no_default_mappings` (Default: `v:false`) + +When this value is set to `v:true`, it does not define any key mappings. `` mappings are still +defined since they don't make any conflicts with existing mappings. + +#### `g:git_messenger_into_popup_after_show` (Default: `v:true`) + +When this value is set to `v:false`, running `:GitMessenger` or `(git-messenger)` again after +showing a popup does not move the cursor in the window. + +#### `g:git_messenger_always_into_popup` (Default: `v:false`) + +When this value is set to `v:true`, the cursor goes into a popup window when running `:GitMessenger` +or `(git-messenger)`. + +#### `g:git_messenger_extra_blame_args` (Default: `""`) + +When this variable is set the contents will be appended to the git blame +command. Use it to add options (like -w). + +#### `g:git_messenger_preview_mods` (Default: `""`) + +This variable is effective only when opening preview window (on Neovim (0.3.0 or earlier) or Vim). + +Command modifiers for opening preview window. The value will be passed as prefix of `:pedit` command. +For example, setting `"botright"` to the variable opens a preview window at bottom of the current +window. Please see `:help ` for more details. + +#### `g:git_messenger_max_popup_height` (Default: `v:null`) + +Max lines of popup window in an integer value. Setting `v:null` means no limit. + +#### `g:git_messenger_max_popup_width` (Default: `v:null`) + +Max characters of popup window in an integer value. Setting `v:null` means no limit. + +#### `g:git_messenger_date_format` (Default: `"%c"`) + +String value to format dates in popup window. Please see `:help strftime()` to know the details of +the format. + +```vim +" Example: '2019 May 26 03:27:43' +let g:git_messenger_date_format = "%Y %b %d %X" +``` + +#### `g:git_messenger_conceal_word_diff_marker` (Default: `v:true`) + +When this value is set to `v:true`, markers for word diffs like `[-`, `-]`, `{+`, `+}` are concealed. +Set `v:false` when you don't want to hide them. + +Note: Word diff is enabled by typing "r" in a popup window. + +#### `g:git_messenger_floating_win_opts` (Default `{}`) + +Options passed to `nvim_open_win()` on opening a popup window. This is useful when you want to +override some window options. + +The following example will add single border line to the window. + +```vim +let g:git_messenger_floating_win_opts = { 'border': 'single' } +``` + +#### `g:git_messenger_popup_content_margins` (Default: `v:true`) + +Setting `v:true` means adding margins in popup window. Blank lines at the top and bottom of popup +content are inserted. And every line is indented with one whitespace character. +Setting `v:false` to this variable removes all the margins. + +### Popup Window Highlight + +This plugin uses color definitions from your colorscheme for highlighting stuffs in popup window by +default. This is done by linking highlight groups in [syntax/gitmessengerpopup.vim](syntax/gitmessengerpopup.vim) +on `gitmessengerpopup` filetype. Highlights for diff are common with normal `diff` filetype syntax +highlighting. + +If the groups don't fit, please rearrange the highlight with `:hi link`. For example: + +```vim +" Normal color in popup window with 'CursorLine' +hi link gitmessengerPopupNormal CursorLine + +" Header such as 'Commit:', 'Author:' with 'Statement' highlight group +hi link gitmessengerHeader Statement + +" Commit hash at 'Commit:' header with 'Special' highlight group +hi link gitmessengerHash Special + +" History number at 'History:' header with 'Title' highlight group +hi link gitmessengerHistory Title +``` + +For another example, if you want to define colors directly, defining the colors with `:hi` works +fine as follows. + +```vim +hi gitmessengerPopupNormal term=None guifg=#eeeeee guibg=#333333 ctermfg=255 ctermbg=234 +hi gitmessengerHeader term=None guifg=#88b8f6 ctermfg=111 +hi gitmessengerHash term=None guifg=#f0eaaa ctermfg=229 +hi gitmessengerHistory term=None guifg=#fd8489 ctermfg=210 +``` + +Note: If your colorscheme does not allocate proper color for `NormalFloat`, you may need to set +proper color to `gitmessengerPopupNormal`. + +Note: `gitmessengerPopupNormal` is only available on Neovim since `winhighlight` option is used. +On Vim, simply `Normal` highlight group is used for normal color. + +### Configuration for Popup Window + +Filetype `gitmessengerpopup` is set in the popup window. Please hook `FileType` event to do some +local setup within a popup window. + +For example: + +```vim +function! s:setup_git_messenger_popup() abort + " Your favorite configuration here + + " For example, set go back/forward history to / + nmap o + nmap O +endfunction +autocmd FileType gitmessengerpopup call setup_git_messenger_popup() +``` + +Recent Neovim supports adding border lines to floating windows. `git_messenger_floating_win_opts` +is available to set the options. And `g:git_messenger_popup_content_margins` can remove margins +within popup content if you feel margins are unnecessary. + +```vim +let g:git_messenger_floating_win_opts = { 'border': 'single' } +let g:git_messenger_popup_content_margins = v:false +``` + +popup with border + +### Health Check + +This plugin supports a health checker on Neovim. When you see some error, please run `:checkhealth` +to check your environment is ready for use of this plugin. + +On Vim, please install [vim-healthcheck](https://github.com/rhysd/vim-healthcheck) and run +`:CheckHealth`. It's a plugin to run `:checkhealth` on Vim. + +### Known Issues + +- On Windows, `git` command installed via MSYS does not work. Please use [Git for Windows][git-win] + for now. This issue is tracked at [#57](https://github.com/rhysd/git-messenger.vim/issues/57). + + +## License + +Distributed under [the MIT License](LICENSE) + +[repo]: https://github.com/rhysd/git-messenger.vim +[ci-badge]: https://github.com/rhysd/git-messenger.vim/workflows/CI/badge.svg?branch=master&event=push +[ci]: https://github.com/rhysd/git-messenger.vim/actions?query=workflow%3ACI+branch%3Amaster +[codecov]: https://codecov.io/gh/rhysd/git-messenger.vim +[codecov-badge]: https://codecov.io/gh/rhysd/git-messenger.vim/branch/master/graph/badge.svg +[doc]: ./doc/git-messenger.txt +[nvim-install]: https://github.com/neovim/neovim/wiki/Installing-Neovim +[git-win]: https://gitforwindows.org/ diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger.vim b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger.vim new file mode 100644 index 00000000..83c920cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger.vim @@ -0,0 +1,161 @@ +" Global variables +let g:git_messenger_close_on_cursor_moved = get(g:, 'git_messenger_close_on_cursor_moved', v:true) +let g:git_messenger_git_command = get(g:, 'git_messenger_git_command', 'git') +let g:git_messenger_into_popup_after_show = get(g:, 'git_messenger_into_popup_after_show', v:true) +let g:git_messenger_always_into_popup = get(g:, 'git_messenger_always_into_popup', v:false) +let g:git_messenger_preview_mods = get(g:, 'git_messenger_preview_mods', '') +let g:git_messenger_extra_blame_args = get(g:, 'git_messenger_extra_blame_args', '') +let g:git_messenger_include_diff = get(g:, 'git_messenger_include_diff', 'none') +let g:git_messenger_max_popup_height = get(g:, 'git_messenger_max_popup_height', v:null) +let g:git_messenger_max_popup_width = get(g:, 'git_messenger_max_popup_width', v:null) +let g:git_messenger_date_format = get(g:, 'git_messenger_date_format', '%c') +let g:git_messenger_conceal_word_diff_marker = get(g:, 'git_messenger_conceal_word_diff_marker', 1) +let g:git_messenger_floating_win_opts = get(g:, 'git_messenger_floating_win_opts', {}) +let g:git_messenger_popup_content_margins = get(g:, 'git_messenger_popup_content_margins', v:true) + +" All popup instances keyed by opener's bufnr to manage lifetime of popups +let s:all_popups = {} + +function! s:on_cursor_moved() abort + let bufnr = bufnr('%') + if !has_key(s:all_popups, bufnr) + autocmd! plugin-git-messenger-close * + return + endif + if s:all_popups[bufnr].cursor_moved() + autocmd! plugin-git-messenger-close * + call gitmessenger#close_popup(bufnr) + endif +endfunction + +function! s:on_buf_enter(bufnr) abort + let popup = s:popup_for(a:bufnr) + + if popup is v:null + autocmd! plugin-git-messenger-buf-enter + return + endif + + let b = bufnr('%') + " When entering/exiting popup window, do nothing + if popup.bufnr == b + return + endif + + " This triggers s:on_close() + call popup.close() + + if empty(s:all_popups) + autocmd! plugin-git-messenger-buf-enter + endif +endfunction + +function! s:on_open(blame) dict abort + if !has_key(a:blame.popup, 'bufnr') + " For some reason, popup was already closed + unlet! s:all_popups[a:blame.popup.opener_bufnr] + return + endif + + let opener_bufnr = a:blame.popup.opener_bufnr + let s:all_popups[opener_bufnr] = a:blame.popup + + if g:git_messenger_close_on_cursor_moved + augroup plugin-git-messenger-close + autocmd CursorMoved,CursorMovedI,InsertEnter call on_cursor_moved() + augroup END + endif + + augroup plugin-git-messenger-buf-enter + execute 'autocmd BufEnter,WinEnter * call on_buf_enter(' . opener_bufnr . ')' + augroup END +endfunction + +function! s:on_close(popup) dict abort + unlet! s:all_popups[a:popup.opener_bufnr] +endfunction + +function! s:on_error(errmsg) abort + echohl ErrorMsg + " Avoid ^@ + for line in split(a:errmsg, '\r\=\n') + echomsg line + endfor + echohl None +endfunction + +" file: string +" line: number +" bufnr: number +" opts?: {} +function! gitmessenger#new(file, line, bufnr, ...) abort + " When cursor is in popup window, close the window + if gitmessenger#popup#close_current_popup() + return + endif + + " Just after opening a popup window, move cursor into the window + if g:git_messenger_into_popup_after_show && has_key(s:all_popups, a:bufnr) + let p = s:all_popups[a:bufnr] + if has_key(p, 'bufnr') + call p.into() + return + endif + endif + + let opts = get(a:, 1, {}) + let opts.pos = getpos('.') + " Close previous popup + if has_key(s:all_popups, a:bufnr) + call s:all_popups[a:bufnr].close() + endif + + let blame = gitmessenger#blame#new(a:file, a:line, { + \ 'did_open': funcref('s:on_open', [], opts), + \ 'did_close': funcref('s:on_close', [], opts), + \ 'on_error': funcref('s:on_error'), + \ 'enter_popup': g:git_messenger_always_into_popup, + \ }) + if blame isnot v:null + call blame.start() + endif +endfunction + +function! s:popup_for(bufnr) abort + if !has_key(s:all_popups, a:bufnr) + return v:null + endif + + let popup = s:all_popups[a:bufnr] + if !has_key(popup, 'bufnr') + " Here should be unreachable + unlet! s:all_popups[a:bufnr] + return v:null + endif + + return popup +endfunction + +function! gitmessenger#close_popup(bufnr) abort + if gitmessenger#popup#close_current_popup() + return + endif + let p = s:popup_for(a:bufnr) + if p isnot v:null + call p.close() + endif +endfunction + +function! gitmessenger#scroll(bufnr, map) abort + let p = s:popup_for(a:bufnr) + if p isnot v:null + call p.scroll(a:map) + endif +endfunction + +function! gitmessenger#into_popup(bufnr) abort + let p = s:popup_for(a:bufnr) + if p isnot v:null + call p.into() + endif +endfunction diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/blame.vim b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/blame.vim new file mode 100644 index 00000000..d42e7bcc --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/blame.vim @@ -0,0 +1,502 @@ +let s:blame = {} + +function! s:git_cmd_failure(git) abort + return printf( + \ 'git-messenger: %s: `%s %s` exited with non-zero status %d', + \ join(a:git.stderr, ' '), + \ a:git.cmd, + \ join(a:git.args, ' '), + \ a:git.exit_status + \ ) +endfunction + +function! s:blame__error(msg) dict abort + if has_key(self.opts, 'on_error') + call self.opts.on_error(a:msg) + else + throw a:msg + endif +endfunction +let s:blame.error = funcref('s:blame__error') + +function! s:blame__render() dict abort + let self.popup.contents = self.state.contents + if self.state.prev_diff !=# self.state.diff + call self.popup.set_buf_var('__gitmessenger_diff', self.state.diff) + let prev_is_word = self.state.prev_diff =~# '\.word$' + let is_word = self.state.diff =~# '\.word$' + if self.state.diff !=# 'none' && prev_is_word != is_word + call self.popup.set_buf_var('&syntax', 'gitmessengerpopup') + endif + endif + call self.popup.update() +endfunction +let s:blame.render = funcref('s:blame__render') + +function! s:blame__back() dict abort + if self.state.back() + call self.render() + return + endif + + if self.prev_commit ==# '' || self.oldest_commit =~# '^0\+$' + echo 'git-messenger: No older commit found' + return + endif + + " Reset current state + call self.state.set_diff('none') + + let args = ['--no-pager', 'blame', self.prev_commit, '-L', self.line . ',+1', '--porcelain'] + split(g:git_messenger_extra_blame_args, ' ') + ['--', self.blame_file] + call self.spawn_git(args, 's:blame__after_blame') +endfunction +let s:blame.back = funcref('s:blame__back') + +function! s:blame__forward() dict abort + if self.state.forward() + call self.render() + elseif self.state.commit !=# '' + echo 'git-messenger: ' . self.state.commit . ' is the latest commit' + else + echo 'git-messenger: The latest commit' + endif +endfunction +let s:blame.forward = funcref('s:blame__forward') + +function! s:blame__open_popup() dict abort + if has_key(self, 'popup') && has_key(self.popup, 'bufnr') + " Already popup is open. It means that now older commit is showing up. + " Save the contents to history and show the contents in current + " popup. + call self.state.push() + call self.state.save() + call self.render() + return + endif + + let opts = { + \ 'filetype': 'gitmessengerpopup', + \ 'mappings': { + \ 'q': [{-> execute('close', '')}, 'Close popup window'], + \ 'o': [funcref(self.back, [], self), 'Back to older commit'], + \ 'O': [funcref(self.forward, [], self), 'Forward to newer commit'], + \ 'd': [funcref(self.reveal_diff, [v:false, v:false], self), "Toggle current file's diffs"], + \ 'D': [funcref(self.reveal_diff, [v:true, v:false], self), 'Toggle all diffs'], + \ 'r': [funcref(self.reveal_diff, [v:false, v:true], self), "Toggle current file's word diffs"], + \ 'R': [funcref(self.reveal_diff, [v:true, v:true], self), 'Toggle all word diffs'], + \ }, + \ } + if has_key(self.opts, 'did_close') + let opts.did_close = self.opts.did_close + endif + if has_key(self.opts, 'enter_popup') + let opts.enter = self.opts.enter_popup + endif + + call self.state.push() + let self.popup = gitmessenger#popup#new(self.state.contents, opts) + call self.popup.open() + + if has_key(self.opts, 'did_open') + call self.opts.did_open(self) + endif +endfunction +let s:blame.open_popup = funcref('s:blame__open_popup') + +function! s:blame__append_lines(lines) dict abort + if !g:git_messenger_popup_content_margins + let self.state.contents += [''] + endif + + let lines = a:lines + if lines[-1] ==# '' + " Strip last newline + let lines = lines[:-2] + endif + + let skip_first_nl = v:true + for line in lines + if skip_first_nl && line ==# '' + continue + else + let skip_first_nl = v:false + endif + + if g:git_messenger_popup_content_margins + if line ==# '' + let self.state.contents += [''] + else + let self.state.contents += [' ' . line] + endif + else + let self.state.contents += [line] + endif + endfor + + if g:git_messenger_popup_content_margins && self.state.contents[-1] !~# '^\s*$' + let self.state.contents += [''] + endif +endfunction +let s:blame.append_lines = funcref('s:blame__append_lines') + +function! s:blame__after_diff(next_diff, git) dict abort + let self.failed = a:git.exit_status != 0 + + if self.failed + call self.error(s:git_cmd_failure(a:git)) + return + endif + + let popup_open = has_key(self, 'popup') + + if a:git.stdout == [] || a:git.stdout == [''] || + \ (popup_open && !has_key(self.popup, 'bufnr')) + return + endif + + " When getting diff with `git show --pretty=format:%b`, it may contain + " commit body. By removing line until 'diff --git ...' line, the body is + " removed (#35) + while a:git.stdout !=# [] && stridx(a:git.stdout[0], 'diff --git ') !=# 0 + let a:git.stdout = a:git.stdout[1:] + endwhile + + call self.append_lines(a:git.stdout) + call self.state.set_diff(a:next_diff) + + if popup_open + call self.render() + else + " Note: When g:git_messenger_include_diff is not 'none' and popup is + " being opened for line which is not committed yet. + " In the case, commit hash is 0000000000000000 and `git log` is not + " available. So `git diff` is used instead and this callback is + " called. + call self.open_popup() + endif +endfunction + +function! s:blame__reveal_diff(include_all, word_diff) dict abort + if a:include_all + let next_diff = 'all' + else + let next_diff = 'current' + endif + if a:word_diff + let next_diff .= '.word' + endif + + if self.state.diff ==# next_diff + " Toggle diff + let next_diff = 'none' + endif + + " Remove diff hunks from popup + let saved = getpos('.') + try + keepjumps execute 1 + let diff_pattern = g:git_messenger_popup_content_margins ? '^ diff --git ' : '^diff --git ' + let diff_offset = g:git_messenger_popup_content_margins ? 2 : 3 + let diff_start = search(diff_pattern, 'ncW') + if diff_start > 1 + let self.state.contents = self.state.contents[ : diff_start-diff_offset] + endif + finally + keepjumps call setpos('.', saved) + endtry + + if next_diff ==# 'none' + let self.state.diff = next_diff + call self.render() + return + endif + + let hash = self.state.commit + if hash ==# '' + call self.error('Not a valid commit hash: ' . hash) + return + endif + if hash !~# '^0\+$' + " `git diff hash^..hash` is not available since hash^ is invalid when + " it is an initial commit. + let args = ['--no-pager', 'show', '--no-color', '--pretty=format:%b', hash] + else + " When the line is not committed yet, show diff against HEAD (#26) + let args = ['--no-pager', 'diff', '--no-color', 'HEAD'] + endif + + if a:word_diff + let args += ['--word-diff=plain'] + endif + + if !a:include_all + let args += ['--', self.state.diff_file_to] + let prev = self.state.diff_file_from + if prev !=# '' && prev != self.state.diff_file_to + " Note: When file was renamed, both file name before rename and file + " name after rename are necessary to show correct diff. + " If only file name after rename is specified, it shows diff as if + " the file was added at the commit not considering rename. + let args += [prev] + endif + endif + call self.spawn_git(args, funcref('s:blame__after_diff', [next_diff], self)) +endfunction +let s:blame.reveal_diff = funcref('s:blame__reveal_diff') + +function! s:blame__after_log(git) dict abort + let self.failed = a:git.exit_status != 0 + + if self.failed + call self.error(s:git_cmd_failure(a:git)) + return + endif + + if a:git.stdout != [] && a:git.stdout != [''] + call self.append_lines(a:git.stdout) + endif + + call self.open_popup() +endfunction + +function! s:blame__after_blame(git) dict abort + let self.failed = a:git.exit_status != 0 + + if self.failed + if a:git.stderr[0] =~# 'has only \d\+ lines\=' + echo 'git-messenger: ' . get(self, 'oldest_commit', 'It') . ' is the oldest commit' + return + endif + call self.error(s:git_cmd_failure(a:git)) + return + endif + + " Parse `blame --porcelain` output + " Note: Output less than 11 lines are invalid. At least followings should + " be included: + " header, author, author-email, author-time, author-tz, committer-email, + " committer-time, committer-tz, summary, filename + let stdout = a:git.stdout + if len(stdout) < 11 + " Note: '\n' is not "\n", it's intentional + call self.error('Unexpected `git blame` output: ' . join(stdout, '\n')) + return + endif + + " Blame header + " {hash} {line number of original} {line number of final} {line offset in lines group} + " + " Please see 'THE PORCELAIN FORMAT' section of `man git-blame` for more + " details + let hash = matchstr(stdout[0], '^[[:xdigit:]]\+') + if has_key(self, 'oldest_commit') && self.oldest_commit ==# hash + echo 'git-messenger: ' . hash . ' is the oldest commit' + return + endif + let not_committed_yet = hash =~# '^0\+$' + + let author = matchstr(stdout[1], '^author \zs.\+') + let author_email = matchstr(stdout[2], '^author-mail \zs\S\+') + let committer = matchstr(stdout[5], '^committer \zs.\+') + let headers = [ + \ ['History', '#' . self.state.history_no()], + \ ['Commit', hash], + \ ['Author', author . ' ' . author_email], + \ ] + + if author !=# committer + let committer_email = matchstr(stdout[6], '^committer-mail \zs\S\+') + let headers += [['Committer', committer . ' ' . committer_email]] + endif + + if exists('*strftime') + let author_time = matchstr(stdout[3], '^author-time \zs\d\+') + let committer_time = matchstr(stdout[7], '^committer-time \zs\d\+') + if author_time ==# committer_time + let headers += [['Date', strftime(g:git_messenger_date_format, str2nr(author_time))]] + else + let headers += [['Author Date', strftime(g:git_messenger_date_format, str2nr(author_time))]] + let headers += [['Committer Date', strftime(g:git_messenger_date_format, str2nr(committer_time))]] + endif + endif + + let header_width = 0 + for [key, _] in headers + let len = len(key) + if len > header_width + let header_width = len + endif + endfor + + let self.state.contents = [] + if g:git_messenger_popup_content_margins + let self.state.contents += [''] + endif + + let margin = g:git_messenger_popup_content_margins ? ' ' : '' + for [key, value] in headers + let pad = repeat(' ', header_width - len(key)) + let line = printf('%s%s: %s%s', margin, key, pad, value) + let self.state.contents += [line] + endfor + + if not_committed_yet + let summary = 'This line is not committed yet' + else + let summary = matchstr(stdout[9], '^summary \zs.*') + endif + let self.state.contents += ['', margin . summary] + if g:git_messenger_popup_content_margins + let self.state.contents += [''] + endif + + " Reset the state + let self.prev_commit = '' + let self.blame_file = '' + " Diff target file is fallback to blame target file + let self.state.diff_file_to = self.blame_file + + " Parse 'previous', 'boundary' and 'filename' + for line in stdout[10:] + " At final of output, the current line prefixed with tab is put + if line[0] ==# "\t" + break + endif + + " previous {hash} {next blame file path} + " + " where {next blame file path} is a relative path from root directory of + " the repository. + let m = matchlist(line, '^previous \([[:xdigit:]]\+\) \(.\+\)$') + if m != [] + let self.prev_commit = m[1] + let self.blame_file = m[2] + continue + endif + + " filename {file path from root dir} + " + " where {file path} is a target file of the current commit. + " The file name may be different from current editing file because + " it might be renamed. + let filename = matchstr(line, '^filename \zs.\+$') + if filename !=# '' + let self.state.diff_file_to = filename + continue + endif + + " boundary + " Boudary commit. It means current commit is the oldest. + " Nothing to do + endfor + + " diff_file_from is the same as blame_file at this moment, but stored in + " another variable since it should be stored in history. + let self.state.diff_file_from = self.blame_file + let self.oldest_commit = hash + let self.state.commit = hash + + " Check hash is 0000000000000000000000 it means that the line is not committed yet + if hash =~# '^0\+$' + if g:git_messenger_include_diff ==? 'none' + call self.open_popup() + return + endif + + " Note: To show diffs which are not committed yet, `git log` is not + " available. Use `git diff` instead. + let next_diff = 'all' + let args = ['--no-pager', 'diff', 'HEAD'] + if g:git_messenger_include_diff ==? 'current' + let next_diff = 'current' + let args += [self.blame_file] + endif + call self.spawn_git(args, funcref('s:blame__after_diff', [next_diff], self)) + return + endif + + let args = ['--no-pager', 'log', '--no-color', '-n', '1', '--pretty=format:%b'] + if g:git_messenger_include_diff !=? 'none' + if g:git_messenger_include_diff ==? 'current' + call self.state.set_diff('current') + else + call self.state.set_diff('all') + endif + let args += ['-p', '-m'] + endif + let args += [hash] + + if g:git_messenger_include_diff ==? 'current' + let args += ['--', self.state.diff_file_to] + let prev = self.state.diff_file_from + if prev !=# '' && prev != self.state.diff_file_to + " Note: When file was renamed, both file name before rename and file + " name after rename are necessary to show correct diff. + " If only file name after rename is specified, it shows diff as if + " the file was added at the commit not considering rename. + let args += [prev] + endif + endif + + call self.spawn_git(args, 's:blame__after_log') +endfunction + +function! s:blame__spawn_git(args, callback) dict abort + let git = gitmessenger#git#new(g:git_messenger_git_command, self.git_root) + let CB = a:callback + if type(CB) == v:t_string + let CB = funcref(CB, [], self) + endif + try + call git.spawn(a:args, CB) + catch /^git-messenger: / + call self.error(v:exception) + endtry +endfunction +let s:blame.spawn_git = funcref('s:blame__spawn_git') + +function! s:blame__start() dict abort + call self.spawn_git( + \ ['--no-pager', 'blame', self.blame_file, '-L', self.line . ',+1', '--porcelain'] + split(g:git_messenger_extra_blame_args, ' '), + \ 's:blame__after_blame') +endfunction +let s:blame.start = funcref('s:blame__start') + +" interface Blame { +" state: BlameHistory; +" line: number; +" git_root: string; +" blame_file: string; +" prev_commit?: string; +" oldest_commit?: string; +" opts: { +" did_open: (b: Blame) => void; +" did_close: (p: Popup) => void; +" on_error: (errmsg: string) => void; +" enter_popup: boolean; +" }; +" } +" +" blame_file: +" File path given to `git blame`. This can be relative to root of repo. +" Note: This does not need to be put in BlameHistory state because it is +" used by only `git blame`. +function! gitmessenger#blame#new(file, line, opts) abort + let file = resolve(a:file) + let b = deepcopy(s:blame) + let b.state = gitmessenger#history#new(file) + let b.line = a:line + let b.blame_file = file + let b.opts = a:opts + + let dir = fnamemodify(file, ':p:h') + let b.git_root = gitmessenger#git#root_dir(dir) + + " Validations + if b.git_root ==# '' + call b.error("git-messenger: Directory '" . dir . "' is not inside a Git repository") + return v:null + endif + + return b +endfunction diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/git.vim b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/git.vim new file mode 100644 index 00000000..b79ad9fe --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/git.vim @@ -0,0 +1,174 @@ +let s:SEP = has('win32') ? '\' : '/' + +function! s:find_dotgit(from) abort + let dir = finddir('.git', a:from . ';') + let file = findfile('.git', a:from . ';') + + if dir ==# '' && file ==# '' + return '' + endif + + " When .git directory is below the current working directory, finddir() + " returns a relative path. So ensuring an absolute path here. + let dir = dir ==# '' ? '' : fnamemodify(dir, ':p') + + " When .git exists in current directory, findfile() returns relative path + " '.git' though finddir() returns an absolute path '/path/to/.git' (#49). + " Since path length will be compared, they must be both abusolute path. + let file = file ==# '' ? '' : fnamemodify(file, ':p') + + " Choose larger (deeper) path (#48). When worktree directory is put in its + " main repository, the .git directory which is near to `from` should be + " chosen. + " When `dir` or `file` is empty, the other is chosen so we don't need to + " care about empty string here. + let dotgit = len(dir) > len(file) ? dir : file + + " Inside .git directory is outside repository + " This check must be done before chopping last path separator otherwise it + " matches to directory like /path/to/.github/ (#70) + if stridx(a:from, dotgit) == 0 + return '' + endif + + if dotgit[-1:] ==# s:SEP + " [:-2] chops last path separator (/path/to/.git/ => /path/to/.git) + let dotgit = dotgit[:-2] + endif + + return dotgit +endfunction + +" Params: +" path: string +" base path to find .git in ancestor directories +" Returns: +" string +" empty string means root directory was not found +function! gitmessenger#git#root_dir(from) abort + let from = fnameescape(fnamemodify(a:from, ':p')) + if from[-1:] ==# s:SEP + " [:-2] chops last path separator + let from = from[:-2] + endif + let dotgit = s:find_dotgit(from) + if dotgit ==# '' + return '' + endif + + " /path/to/.git => /path/to + return fnamemodify(dotgit, ':h') +endfunction + +let s:git = {} + +if has('nvim') + function! s:on_output_nvim(job, data, event) dict abort + if a:data == [''] + return + endif + " Output from Git might contain \r for example when the commit + " author's text editor uses \r\n for newlines. But Neovim reads output + " from the process line by line based on \n. The trailing \r remains + " when \r\n is used for newlines. This removes the trailing \r (#75). + call map(a:data, 'v:val[len(v:val)-1] ==# "\r" ? v:val[:-2] : v:val') + let self[a:event][-1] .= a:data[0] + call extend(self[a:event], a:data[1:]) + endfunction + + function! s:on_exit_nvim(job, code, event) dict abort + let self.exit_status = a:code + call self.on_exit(self) + endfunction +else + function! s:git__finalize_vim(ch) dict abort + if has_key(self, 'finalized') && self.finalized + return + endif + + " Note: + " Workaround for Vim's exit_cb behavior. When the callback is called, + " sometimes channel for stdout and/or stderr is not closed yet. So + " their status may be 'open'. As workaround for the behavior, we do + " polling to check the channel statuses with 1 msec interval until the + " statuses are set to 'close'. (#16) + let out_opt = {'part': 'out'} + let err_opt = {'part': 'err'} + while 1 + let out_status = ch_status(a:ch, out_opt) + let err_status = ch_status(a:ch, err_opt) + if out_status !=# 'open' && out_status !=# 'buffered' && + \ err_status !=# 'open' && err_status !=# 'buffered' + let self.finalized = v:true + call self.on_exit(self) + return + endif + sleep 1m + endwhile + endfunction + let s:git.finalize_vim = funcref('s:git__finalize_vim') + + function! s:on_output_vim(event, ch, msg) dict abort + call extend(self[a:event], split(a:msg, '\r\=\n', 1)) + endfunction + + function! s:on_exit_vim(ch, code) dict abort + let self.exit_status = a:code + call self.finalize_vim(a:ch) + endfunction +endif + +" Params: +" args: string[] +" on_exit: (git: Git) => void +" Returns: +" Job ID of the spawned process +function! s:git__spawn(args, on_exit) dict abort + let cmdline = [self.cmd, '-C', self.dir] + a:args + if has('nvim') + let self.stdout = [''] + let self.stderr = [''] + let job_id = jobstart(cmdline, { + \ 'cwd': self.dir, + \ 'on_stdout' : funcref('s:on_output_nvim', [], self), + \ 'on_stderr' : funcref('s:on_output_nvim', [], self), + \ 'on_exit' : funcref('s:on_exit_nvim', [], self), + \ }) + if job_id == 0 + throw 'git-messenger: Invalid arguments: ' . string(a:args) + elseif job_id == -1 + throw 'git-messenger: Command does not exist: ' . self.cmd + endif + else + let self.stdout = [] + let self.stderr = [] + let job_id = job_start(cmdline, { + \ 'cwd': self.dir, + \ 'out_cb' : funcref('s:on_output_vim', ['stdout'], self), + \ 'err_cb' : funcref('s:on_output_vim', ['stderr'], self), + \ 'exit_cb' : funcref('s:on_exit_vim', [], self), + \ }) + endif + let self.job_id = job_id + let self.on_exit = a:on_exit + let self.args = a:args + return job_id +endfunction +let s:git.spawn = funcref('s:git__spawn') + +" Creates new Git instance. Git instance represents one-shot Git command +" asynchronous execution. +" +" Params: +" cmd: string +" 'git' command to run Git +" dir: string +" Directory path to run Git +" Returns: +" Git object +function! gitmessenger#git#new(cmd, dir) abort + let g = deepcopy(s:git) + let g.cmd = a:cmd + let g.dir = a:dir + return g +endfunction diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/history.vim b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/history.vim new file mode 100644 index 00000000..dc570278 --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/history.vim @@ -0,0 +1,132 @@ +" Note: Index 0 means the latest entry of history + +" interface BlameState { +" commit: string; +" contents: string[]; +" diff_file_to: string; +" diff_file_from: string; +" diff: 'none' | 'all' | 'current'; +" } +" +" interface BlameHistory extends BlameState { +" _index: number; +" _history: BlameState[]; +" } +" +" History of chain of `git blame` with contents. +" +" contents: +" Lines of contents of popup +" diff_file_to: +" File path for diff. It represents the file path after the commit. +" When the file was renamed while the commit, it is different from 'diff_file_from' +" diff_file_from: +" File path for diff. It represents the file path before the commit. +" When the file was renamed while the commit, it is different from 'diff_file_to' +" diff: +" Diff type. Please see document for g:git_messenger_include_diff +" commit: +" Commit hash of the commit +" _index: +" Index of history which indicates current state. 0 means the latest history +" _history: +" History of chain of blame entries. Latter is older. +let s:history = { '_index': 0, '_history': [] } + +" Create new empty history entry as the latest +function! s:history__push() dict abort + " Note: copy() is necessary because the contents may be updated later + " for diff + " Note: 'commit' is a special key which will be never changed. This field + " will be used for checking invariant state on saving the state + let self._history += [{ 'commit': self.commit }] + let self._index = len(self._history) - 1 +endfunction +let s:history.push = funcref('s:history__push') + +" Save current state to current history entry +function! s:history__save() dict abort + if self._index > len(self._history) + throw printf('FATAL: Invariant error on saving history. Index %d is out of range. Length of history is %d', self._index, len(self._history)) + endif + + let e = self._history[self._index] + if self.commit !=# e.commit + throw printf('FATAL: Invariant error on saving history. Current commit hash %s is different from commit hash in history %s', self.commit, e.commit) + endif + + let e.diff = self.diff + let e.contents = copy(self.contents) + let e.diff_file_to = self.diff_file_to + let e.commit = self.commit + let e.diff_file_from = self.diff_file_from +endfunction +let s:history.save = funcref('s:history__save') + +" Load specific history entry as current state +function! s:history__load(index) dict abort + let e = self._history[a:index] + " Note: copy() is necessary because the contents may be updated later + " for diff. Without copy(), it modifies array in self.history directly + " but that's not intended. + let self.contents = copy(e.contents) + call self.set_diff(e.diff) + let self.commit = e.commit + let self.diff_file_to = e.diff_file_to + let self.diff_file_from = e.diff_file_from + let self._index = a:index +endfunction +let s:history._load = funcref('s:history__load') + +function! s:history__history_no() dict abort + return len(self._history) +endfunction +let s:history.history_no = funcref('s:history__history_no') + +function! s:history__set_diff(diff) dict abort + let self.prev_diff = self.diff + let self.diff = a:diff +endfunction +let s:history.set_diff = funcref('s:history__set_diff') + +" Go back to older. Load older history entry to current history. +" Returns boolean which is true when older entry was found. +function! s:history__back() dict abort + let next_index = self._index + 1 + + call self.save() + + if len(self._history) <= next_index + return v:false + endif + + call self._load(next_index) + return v:true +endfunction +let s:history.back = funcref('s:history__back') + +" Go forward to newer. Load newer history entry to current state. +" Returns boolean which is true when newer entry was found. +function! s:history__forward() dict abort + " Note: Index 0 is the latest entry + let next_index = self._index - 1 + if next_index < 0 + return v:false + endif + + call self.save() + call self._load(next_index) + return v:true +endfunction +let s:history.forward = funcref('s:history__forward') + +function! gitmessenger#history#new(filepath) abort + let h = deepcopy(s:history) + let h.contents = [] + let h.diff_file_to = a:filepath + let h.diff_file_from = a:filepath + let h.diff = 'none' + let h.prev_diff = '' + let h.commit = '' + return h +endfunction diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/popup.vim b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/popup.vim new file mode 100644 index 00000000..ed0d8ed6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/gitmessenger/popup.vim @@ -0,0 +1,339 @@ +let s:popup = {} +let s:floating_window_available = has('nvim') && exists('*nvim_win_set_config') + +function! s:get_global_pos() abort + let pos = win_screenpos('.') + return [pos[0] + winline() - 1, pos[1] + wincol() - 1] +endfunction + +function! s:popup__close() dict abort + if !has_key(self, 'bufnr') + " Already closed + return + endif + + let winnr = self.get_winnr() + if winnr > 0 + " Without this 'noautocmd', the BufWipeout event will be triggered and + " this function will be called again. + noautocmd execute winnr . 'wincmd c' + endif + + unlet self.bufnr + unlet self.win_id + + if has_key(self.opts, 'did_close') + call self.opts.did_close(self) + endif +endfunction +let s:popup.close = funcref('s:popup__close') + +function! s:popup__get_winnr() dict abort + if !has_key(self, 'bufnr') + return 0 + endif + + " Note: bufwinnr() is not available here because there may be multiple + " windows which open the buffer. This situation happens when enter v + " in popup window. It opens a new normal window with the popup's buffer. + return win_id2win(self.win_id) +endfunction +let s:popup.get_winnr = funcref('s:popup__get_winnr') + +function! s:popup__set_buf_var(name, value) dict abort + if has_key(self, 'bufnr') + call setbufvar(self.bufnr, a:name, a:value) + endif +endfunction +let s:popup.set_buf_var = funcref('s:popup__set_buf_var') + +function! s:popup__scroll(map) dict abort + let winnr = self.get_winnr() + if winnr == 0 + return + endif + execute winnr . 'wincmd w' + sandbox let input = eval('"\<'.a:map.'>"') + execute 'normal!' input + wincmd p +endfunction +let s:popup.scroll = funcref('s:popup__scroll') + +function! s:popup__into() dict abort + let winnr = self.get_winnr() + if winnr == 0 + return + endif + execute winnr . 'wincmd w' +endfunction +let s:popup.into = funcref('s:popup__into') + +function! s:popup__window_size() dict abort + let margin = g:git_messenger_popup_content_margins ? 1 : 0 + let has_max_width = type(g:git_messenger_max_popup_width) == v:t_number + if has_max_width + " ` - 1` for considering right margin + let max_width = g:git_messenger_max_popup_width - margin + endif + + let width = 0 + let height = 0 + for line in self.contents + let lw = strdisplaywidth(line) + if lw > width + if has_max_width && lw > max_width + let height += lw / max_width + 1 + let width = max_width + continue + endif + let width = lw + endif + let height += 1 + endfor + let width += margin " right margin + + if type(g:git_messenger_max_popup_height) == v:t_number && height > g:git_messenger_max_popup_height + let height = g:git_messenger_max_popup_height + endif + + return [width, height] +endfunction +let s:popup.window_size = funcref('s:popup__window_size') + +function! s:popup__floating_win_opts(width, height) dict abort + let border = has_key(g:git_messenger_floating_win_opts, 'border') + \ && index( + \ ['single', 'double', 'rounded', 'solid'], g:git_messenger_floating_win_opts['border'] + \ ) != -1 ? 2 : 0 + + " &lines - 1 because it is not allowed to overlay a floating window on a status line. + " Bottom line of a floating window must be less than line of command line. (#80) + if self.opened_at[0] + a:height + border <= &lines - 1 + let vert = 'N' + let row = self.opened_at[0] + else + let vert = 'S' + let row = self.opened_at[0] - 1 - border + endif + + if self.opened_at[1] + a:width + border <= &columns + let hor = 'W' + let col = self.opened_at[1] - 1 + else + let hor = 'E' + let col = self.opened_at[1] - border + endif + + return extend({ + \ 'relative': 'editor', + \ 'anchor': vert . hor, + \ 'row': row, + \ 'col': col, + \ 'width': a:width, + \ 'height': a:height, + \ 'style': 'minimal', + \ }, + \ g:git_messenger_floating_win_opts) +endfunction +let s:popup.floating_win_opts = funcref('s:popup__floating_win_opts') + +function! s:popup__get_opener_winnr() dict abort + let winnr = win_id2win(self.opener_winid) + if winnr != 0 + return winnr + endif + let winnr = bufwinnr(self.opener_bufnr) + if winnr > 0 + return winnr + endif + return 0 +endfunction +let s:popup.get_opener_winnr = funcref('s:popup__get_opener_winnr') + +function! s:popup__open() dict abort + let self.opened_at = s:get_global_pos() + let self.opener_bufnr = bufnr('%') + let self.opener_winid = win_getid() + let self.type = s:floating_window_available ? 'floating' : 'preview' + + let [width, height] = self.window_size() + + " Open window + if self.type ==# 'floating' + let opts = self.floating_win_opts(width, height) + let win_id = nvim_open_win(self.opener_bufnr, v:true, opts) + else + let curr_pos = getpos('.') + let mods = 'noswapfile' + if g:git_messenger_preview_mods !=# '' + let mods .= ' ' . g:git_messenger_preview_mods + endif + + " :pedit! is not available since it refreshes the file buffer (#39) + execute mods 'new' + set previewwindow + + call setpos('.', curr_pos) + wincmd P + execute height . 'wincmd _' + let win_id = win_getid() + endif + + " Setup content + enew! + let popup_bufnr = bufnr('%') + " Note: Set conceallevel for hiding word diff markers + setlocal + \ buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nonumber + \ nocursorline wrap nonumber norelativenumber signcolumn=no nofoldenable + \ nospell nolist nomodeline conceallevel=2 + call setline(1, self.contents) + setlocal nomodified nomodifiable + + " Setup highlights + if has('nvim') + setlocal winhighlight=Normal:gitmessengerPopupNormal + endif + + if has_key(self.opts, 'mappings') + for m in keys(self.opts.mappings) + execute printf('nnoremap %s :call b:__gitmessenger_popup.opts.mappings["%s"][0]()', m, m) + endfor + nnoremap ? :call b:__gitmessenger_popup.echo_help() + endif + + if has_key(self.opts, 'filetype') + let &l:filetype = self.opts.filetype + endif + + " Ensure to close popup + let b:__gitmessenger_popup = self + execute 'autocmd BufWipeout,BufLeave call getbufvar(' . popup_bufnr . ', "__gitmessenger_popup").close()' + + if has_key(self.opts, 'enter') && !self.opts.enter + noautocmd wincmd p + if self.type !=# 'floating' + " Opening a preview window may move global position of the cursor. + " `opened_at` is used for checking if the popup window should be + " closed on `CursorMoved` event. If the position is not updated + " here, the event wrongly will refer the position before opening + " the preview window. + let self.opened_at = s:get_global_pos() + endif + endif + + let self.bufnr = popup_bufnr + let self.win_id = win_id +endfunction +let s:popup.open = funcref('s:popup__open') + +function! s:popup__update() dict abort + " Note: `:noautocmd` to prevent BufLeave autocmd event (#13) + " It should be ok because the cursor position is finally back to the first + " position. + + let prev_winnr = winnr() + + let popup_winnr = self.get_winnr() + if popup_winnr == 0 + return + endif + let opener_winnr = self.get_opener_winnr() + if opener_winnr == 0 + return + endif + + if opener_winnr != prev_winnr + noautocmd execute opener_winnr . 'wincmd w' + endif + + try + let [width, height] = self.window_size() + + " Window must be configured in opener buffer since the window position + " is relative to cursor + if self.type ==# 'floating' + let id = win_getid(popup_winnr) + if id == 0 + return + endif + let opts = self.floating_win_opts(width, height) + call nvim_win_set_config(id, opts) + endif + + noautocmd execute popup_winnr . 'wincmd w' + + if self.type ==# 'preview' + execute height . 'wincmd _' + endif + + setlocal modifiable + silent %delete _ + call setline(1, self.contents) + setlocal nomodified nomodifiable + finally + if winnr() != prev_winnr + noautocmd execute prev_winnr . 'wincmd w' + endif + endtry +endfunction +let s:popup.update = funcref('s:popup__update') + +" Returns if the cursor moved since this popup window had opened +function! s:popup__cursor_moved() dict abort + return s:get_global_pos() != self.opened_at +endfunction +let s:popup.cursor_moved = funcref('s:popup__cursor_moved') + +function! s:popup__echo_help() dict abort + if has_key(self.opts, 'mappings') + let maps = keys(self.opts.mappings) + call sort(maps, 'i') + let maps += ['?'] + + for map in maps + if map ==# '?' + let desc = 'Show this help' + else + let desc = self.opts.mappings[map][1] + endif + echohl Identifier | echo ' ' . map + echohl Comment | echon ' : ' + echohl None | echon desc + endfor + endif +endfunction +let s:popup.echo_help = funcref('s:popup__echo_help') + +" contents: string[] // lines of contents +" opts: { +" floating?: boolean; +" bufnr?: number; +" cursor?: [number, number]; // (line, col) +" filetype?: string; +" did_close?: (pupup: Popup) => void; +" mappings?: { +" [keyseq: string]: [() => void, string]; +" }; +" enter?: boolean +" } +function! gitmessenger#popup#new(contents, opts) abort + let p = deepcopy(s:popup) + let opts = { 'floating': v:true } + call extend(opts, a:opts) + let p.opts = opts + let p.contents = a:contents + return p +endfunction + + +" When current window is popup, close the window. +" Returns true when popup window was closed +function! gitmessenger#popup#close_current_popup() abort + if !exists('b:__gitmessenger_popup') + return 0 + endif + call b:__gitmessenger_popup.close() + " TODO?: Back to opened_at pos by setpos() + return 1 +endfunction diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/health/gitmessenger.vim b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/health/gitmessenger.vim new file mode 100644 index 00000000..bbc9b77e --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/autoload/health/gitmessenger.vim @@ -0,0 +1,80 @@ +function! s:check_job() abort + if !has('nvim') && !has('job') + call health#report_error('Not supported since +job feature is not enabled') + else + call health#report_ok('+job is available to execute Git command') + endif +endfunction + +function! s:check_floating_window() abort + if !has('nvim') + return + endif + + if !exists('*nvim_win_set_config') + call health#report_warn( + \ 'Neovim 0.3.0 or earlier does not support floating window feature. Preview window is used instead', + \ 'Please install Neovim 0.4.0 or later') + return + endif + + " XXX: Temporary + try + noautocmd let win_id = nvim_open_win(bufnr('%'), v:false, { + \ 'relative': 'editor', + \ 'row': 0, + \ 'col': 0, + \ 'width': 2, + \ 'height': 2, + \ }) + noautocmd call nvim_win_close(win_id, v:true) + catch /^Vim\%((\a\+)\)\=:E118/ + call health#report_error( + \ 'Your Neovim is too old', + \ [ + \ 'Please update Neovim to 0.4.0 or later', + \ 'If the version does not fix the error, please make an issue at https://github.com/rhysd/git-messenger.vim', + \ ]) + return + endtry + + call health#report_ok('Floating window is available for popup window') +endfunction + +function! s:check_git_binary() abort + let cmd = get(g:, 'git_messenger_git_command', 'git') + if !executable(cmd) + call health#report_error('`' . cmd . '` command is not found. Please set proper command to g:git_messenger_git_command') + return + endif + + let output = substitute(system(cmd . ' -C . --version'), '\r\=\n', '', 'g') + if v:shell_error + call health#report_error('Git command `' . cmd . '` is broken (v1.8.5 or later is required): ' . output) + return + endif + + call health#report_ok('Git command `' . cmd . '` is available: ' . output) +endfunction + +function! s:check_vim_version() abort + if has('nvim') + return + endif + + if v:version < 800 + call health#report_error( + \ 'Your Vim version is too old: ' . v:version, + \ 'Please install Vim 8.0 or later') + return + endif + + call health#report_ok('Vim version is fine: ' . v:version) +endfunction + +function! health#gitmessenger#check() abort + call s:check_job() + call s:check_git_binary() + call s:check_floating_window() + call s:check_vim_version() +endfunction diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/doc/git-messenger.txt b/config/neovim/store/lazy-plugins/git-messenger.vim/doc/git-messenger.txt new file mode 100644 index 00000000..6ce1632c --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/doc/git-messenger.txt @@ -0,0 +1,424 @@ +*git-messenger.txt* Reveal the hidden message by git under the cursor quickly + +Author : rhysd + +CONTENTS *git-messenger-contents* + +Introduction |git-messenger-introduction| +Screenshot |git-messenger-screenshot| +Install |git-messenger-install| +Commands |git-messenger-commands| +Mappings |git-messenger-mappings| +Variables |git-messenger-variables| +Highlights |git-messenger-highlights| +Popup Window Config |git-messenger-popup-window-config| +Health Check |git-messenger-health-check| +Known Issues |git-messenger-known-issues| +Repository |git-messenger-repository-page| +License |git-messenger-license| + + +============================================================================== +INTRODUCTION *git-messenger-introduction* + +*git-messenger.vim* is a Vim/Neovim plugin to reveal the hidden message under +the cursor quickly. It shows the last commit message under the cursor in popup +window. + +When you modifying unfamiliar codes, you would sometimes wonder 'why was this +line added?' or 'why this value was chosen?' in the source code. The answer +sometimes lays in a commit message, especially in message of the last commit +which modifies the line. + +This plugin shows the message of the last commit in a 'popup window'. The +popup window is implemented in + +- Floating window on Neovim 0.4 or later +- Preview window on Vim 8+ or Neovim 0.3 or earlier + +The floating window is definitely recommended since it can shows the +information near the cursor. + +If you see some error message, please try |git-messenger-health-check|. + + +============================================================================== +SCREENSHOT *git-messenger-screenshot* + +1. Show popup window with Neovim v0.4.0-dev + +https://github.com/rhysd/ss/blob/master/git-messenger.vim/demo.gif + +2. Exploring older commits + +https://github.com/rhysd/ss/blob/master/git-messenger.vim/history.gif + +3. Exploring diff of the commit + +https://github.com/rhysd/ss/blob/master/git-messenger.vim/diff.gif + +You may be also interested in |g:git_messenger_include_diff| + + +============================================================================== +INSTALL *git-messenger-install* + +Please ensure the following requirement before installing this plugin. + +- Git v1.8.5 (for -C option of "git" command) + +If you use any package manager, please follow its instruction. + +With vim-plug: +> + Plug 'rhysd/git-messenger.vim' +< +With dein.vim: +> + call dein#add('rhysd/git-messenger.vim', { + \ 'lazy' : 1, + \ 'on_cmd' : 'GitMessenger', + \ 'on_map' : '(git-messenger-', + \ }) +< +With minpac: +> + call minpac#add('rhysd/git-messenger.vim') +< +If you're using Vim's builtin packager, please follow instruction at |pack-add|. + +vim-plug: https://github.com/junegunn/vim-plug +dein.vim: https://github.com/Shougo/dein.vim +minpac: https://github.com/k-takata/minpac + +To enable a floating window support, you need to install Neovim 0.4 or later. +Please follow the official instruction. + +https://github.com/neovim/neovim/wiki/Installing-Neovim + + +============================================================================== +COMMANDS *git-messenger-commands* + +*:GitMessenger* + + It opens a popup window with the last commit message which modified the line + at cursor. The popup window shows following contents: + + - History: Number of history. In popup window, h/l navigates to previous or + next commit. + - Commit: The commit hash + - Author: Author name and mail address of the commit + - Committer: Committer name and mail address of the commit when + committer is different from author + - Date: Author date of the commit in system format + - When a committer date and an author date are different, both + 'Author Date' and 'Committer Date' are shown + - Summary: First line after a date header line is a summary of commit + - Body: After summary, commit body is put (if the commit has body) + + The popup window will be automatically closed when you move the cursor so + you don't need to close it manually. + + Running command again after the popup window shows up moves the cursor into + the window. This behavior is useful when the commit message is too long and + window cannot show the whole content. By moving the cursor into the popup + window, you can see the rest of contents by scrolling it. This behavior can + be suppressed with |g:git_messenger_into_popup_after_show|. + + Following mappings are defined within popup window. + + | Mapping | Description | + | ------- | ------------------------------------------------------------ | + | q | Close the popup window | + | o | "o"lder. Back to older commit at the line | + | O | Opposite to older. Forward to newer commit at the line | + | d | Toggle unified diff hunks only in current file of the commit | + | D | Toggle all unified diff hunks of the commit | + | r | Toggle word diff hunks only in current file of the commit | + | R | Toggle all word diff hunks of the commit | + | ? | Show mappings help | + +*:GitMessengerClose* + + Though a popup window is automatically closed by default, it closes the + popup window explicitly. It is useful when you set + |g:git_messenger_close_on_cursor_moved| to |v:false|. + + +============================================================================== +MAPPINGS *git-messenger-mappings* + +Some mappings are available to operate a popup window. They can be +mapped to your favorite key sequences. For example: + +*(git-messenger)* + + The same as running |:GitMessenger| command. + +*(git-messenger-close)* + + The same as running |:GitMessengerClose| command. + +*(git-messenger-into-popup)* + + Moves the cursor into the popup window. It's useful when you want to scroll + the content and close the window. + +*(git-messenger-scroll-down-1)* + + Scroll down the popup window by 1 line directly. + +*(git-messenger-scroll-up-1)* + + Scroll up the popup window by 1 line directly. + +*(git-messenger-scroll-down-page)* + + Scroll down the popup window by 1 page directly. + +*(git-messenger-scroll-up-page)* + + Scroll up the popup window by 1 page directly. + +*(git-messenger-scroll-down-half)* + + Scroll down the popup window by half page directly. + +*(git-messenger-scroll-up-half)* + + Scroll up the popup window by half page directly. + + +============================================================================== +VARIABLES *git-messenger-variables* + +Some global variables are available to configure the behavior of this plugin. + + +*g:git_messenger_close_on_cursor_moved* (Default: |v:true|) + + When this value is set to |v:false|, a popup window is no longer closed + automatically when moving a cursor after the window is shown up. + +*g:git_messenger_include_diff* (Default: "none") + + When this value is set to |v:true|, a popup window includes diff hunks of the + commit by default. + Note: Typing "d" or "r" in popup window reveals diff hunks even if this + value is set to |v:false|. + + One of "none", "current", "all". + + When this value is NOT set to "none", a popup window includes diff hunks of + the commit at showing up. "current" includes diff hunks of only current file + in the commit. "all" includes all diff hunks in the commit. + + Note: Typing d/D or r/R mappings in popup window reveal diff hunks even if + this value is set to "none". + +*g:git_messenger_git_command* (Default: "git") + + "git" command to retrieve commit messages. If your "git" executable is not + in "$PATH" directories, please specify the path to the executable. + +*g:git_messenger_no_default_mappings* (Default: |v:false|) + + When this value is set to |v:true|, it does not define any key mappings. + mappings are still defined since they don't make any conflicts with + existing mappings. + +*g:git_messenger_into_popup_after_show* (Default: |v:true|) + + When this value is set to |v:false|, run |:GitMessenger| or + |(git-messenger)| again after showing a popup does not move the cursor + in the window. + +*g:git_messenger_always_into_popup* (Default: |v:false|) + + When this value is set to |v:true|, the cursor goes into a popup window when + running |:GitMessenger| or |(git-messenger)|. + +*g:git_messenger_extra_blame_args* (Default: "") + + When this variable is set the contents will be appended to the git blame + command. Use it to add options (like -w). + +*g:git_messenger_preview_mods* (Default: "") + +Command modifiers for opening preview window. The value will be passed as +prefix of |:pedit| command. +For example, setting "botright" to the variable opens a preview window at +bottom of the current window. Please see || for more details. + +NOTE: This variable is effective only when opening preview window (on Neovim + (0.3.0 or earlier) or Vim). + +*g:git_messenger_max_popup_height* (Default: |v:null|) + +Max lines of popup window in an integer value. Setting |v:null| means no +limit. + +*g:git_messenger_max_popup_width* (Default: |v:null|) + +Max characters of popup window in an integer value. Setting |v:null| means no +limit. + +*g:git_messenger_date_format* (Default: "%c") + +String value to format dates in popup window. Please see |strftime()| to know +the details of the format. +> + " Example: '2019 May 26 03:27:43' + let g:git_messenger_date_format = "%Y %b %d %X" +< +*g:git_messenger_conceal_word_diff_marker* (Default: |v:true|) + +When this value is set to |v:true|, markers for word diffs like "[-", "-]", +"{+", "+}" are concealed. Set |v:false| when you don't want to hide them. + +Note: Word diff is enabled by typing "r" in a popup window. + +*g:git_messenger_floating_win_opts* (Default: {}) + +Options passed to |nvim_open_win| on opening a popup window. This is useful +when you want to override some window options. +The following example will add single border line to the window. +> + let g:git_messenger_floating_win_opts = { 'border': 'single' } +< +*g:git_messenger_popup_content_margins* (Default: |v:true|) + +Setting |v:true| means adding margins in popup window. Blank lines at the top +and bottom of popup content are inserted. And every line is indented with one +whitespace character. + +Setting |v:false| to this variable removes all the margins. Removing margins +might be useful when you enable borders of popup window with +|g:git_messenger_floating_win_opts|. + +============================================================================== +HIGHLIGHTS *git-messenger-highlights* + +|git-messenger.vim| uses color definitions from your colorscheme for +highlighting stuffs in popup window by default. This is done by linking +highlight groups on "gitmessengerpopup" filetype. +Highlights for diff are common with normal |diff| filetype syntax highlighting. + +If the groups don't fit, please rearrange the highlight with |:hi-link|. +For example: +> + " Normal color in popup window with 'CursorLine' + hi link gitmessengerPopupNormal CursorLine + + " Header such as 'Commit:', 'Author:' with 'Statement' highlight group + hi link gitmessengerHeader Statement + + " Commit hash at 'Commit:' header with 'Special' highlight group + hi link gitmessengerHash Special + + " History number at 'History:' header with 'Title' highlight group + hi link gitmessengerHistory Title + + " Email address at 'Author:' header with 'Comment' highlight group + hi link gitmessengerEmail Comment +< +For another example, if you want to define colors directly, defining the +colors with |:hi| works fine as follows. +> + hi gitmessengerPopupNormal term=None guifg=#eeeeee guibg=#333333 ctermfg=255 ctermbg=234 + hi gitmessengerHeader term=None guifg=#88b8f6 ctermfg=111 + hi gitmessengerHash term=None guifg=#f0eaaa ctermfg=229 + hi gitmessengerHistory term=None guifg=#fd8489 ctermfg=210 + hi gitmessengerEmail term=None guifg=#999999 ctermfg=245 +< +Note: If your colorscheme does not allocate proper color for |hl-NormalFloat|, +you may need to set proper color to "gitmessengerPopupNormal". For example, if +your colorscheme use the same color for both normal background color and popup +background color, linking "gitmessengerPopupNormal" to different background +color like |hl-CursorLine| would be better. + +Note: "gitmessengerPopupNormal" is only available on Neovim since +'winhighlight' option is used. On Vim, simply |hl-Normal| is used for normal +color. + + +============================================================================== +CONFIGURATION IN POPUP WINDOW *git-messenger-popup-window-config* + +Filetype *gitmessengerpopup* is set in the popup window. Please hook |FileType| +event to do some local setup within a popup window. + +For example: +> + function! s:setup_gitmessengerpopup() abort + " For example, set go back/forward history to / + nmap o + nmap O + endfunction + autocmd FileType gitmessengerpopup call setup_gitmessengerpopup() +< + +============================================================================== +HEALTH CHECK *git-messenger-health-check* + +|git-messenger.vim| supports a health checker on Neovim. When you see some +error, please run `:checkhealth` to check your environment is ready for use +of this plugin. +> + :checkhealth +< +On Vim, please install vim-healthcheck and run `:CheckHealth`. It's a plugin to +run `:checkhealth` on Vim. +> + :CheckHealth +< +https://github.com/rhysd/vim-healthcheck + + +============================================================================== +KNOWN ISSUES *git-messenger-known-issues* + +1. +On Windows, `git` command installed via MSYS does not work. Please use +Git for Windows for now. This issue is tracked at +https://github.com/rhysd/git-messenger.vim/issues/57 + + +============================================================================== +REPOSITORY *git-messenger-repository-page* + +|git-messenger.vim| is developed at GitHub. Please make a new issue or PR to +join development. + +https://github.com/rhysd/git-messenger.vim + + +============================================================================== +LICENSE *git-messenger-license* + +|git-messenger.vim| is distributed under The MIT license. + + Copyright (c) 2019 rhysd + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +============================================================================== +vim:tw=78:ts=8:ft=help:norl:et:fen:fdl=0: diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/plugin/gitmessenger.vim b/config/neovim/store/lazy-plugins/git-messenger.vim/plugin/gitmessenger.vim new file mode 100644 index 00000000..3cbf6688 --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/plugin/gitmessenger.vim @@ -0,0 +1,24 @@ +if exists('g:loaded_git_messenger') + finish +endif +let g:loaded_git_messenger = 1 + +" Global variables used in plugin/gitmessenger.vim +let g:git_messenger_no_default_mappings = get(g:, 'git_messenger_no_default_mappings', v:false) + +command! -nargs=0 -bar GitMessenger call gitmessenger#new(expand('%:p'), line('.'), bufnr('%')) +command! -nargs=0 -bar GitMessengerClose call gitmessenger#close_popup(bufnr('%')) + +nnoremap (git-messenger) :GitMessenger +nnoremap (git-messenger-close) :call gitmessenger#close_popup(bufnr('%')) +nnoremap (git-messenger-into-popup) :call gitmessenger#into_popup(bufnr('%')) +nnoremap (git-messenger-scroll-down-1) :call gitmessenger#scroll(bufnr('%'), 'C-e') +nnoremap (git-messenger-scroll-up-1) :call gitmessenger#scroll(bufnr('%'), 'C-y') +nnoremap (git-messenger-scroll-down-page) :call gitmessenger#scroll(bufnr('%'), 'C-f') +nnoremap (git-messenger-scroll-up-page) :call gitmessenger#scroll(bufnr('%'), 'C-b') +nnoremap (git-messenger-scroll-down-half) :call gitmessenger#scroll(bufnr('%'), 'C-d') +nnoremap (git-messenger-scroll-up-half) :call gitmessenger#scroll(bufnr('%'), 'C-u') + +if !g:git_messenger_no_default_mappings + nmap gm (git-messenger) +endif diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/syntax/gitmessengerpopup.vim b/config/neovim/store/lazy-plugins/git-messenger.vim/syntax/gitmessengerpopup.vim new file mode 100644 index 00000000..10f8c0fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/syntax/gitmessengerpopup.vim @@ -0,0 +1,50 @@ +if exists('b:current_syntax') + finish +endif + +syn match gitmessengerHeader '^ \=\%(History\|Commit\|\%(Author \|Committer \)\=Date\|Author\|Committer\):' display +syn match gitmessengerHash '\%(^ \=Commit: \+\)\@<=[[:xdigit:]]\+' display +syn match gitmessengerHistory '\%(^ \=History: \+\)\@<=#\d\+' display +syn match gitmessengerEmail '\%(^ \=\%(Author\|Committer\): \+.*\)\@<=<.\+>' display + +" Diff included in popup +" There are two types of diff format; 'none' 'current', 'all', 'current.word', 'all.word'. +" 'current.word' and 'all.word' are for word diff. And 'current' and 'all' are " for unified diff. +" Define different highlights for unified diffs and word diffs. +" b:__gitmessenger_diff is set by Blame.render() in blame.vim. +if get(b:, '__gitmessenger_diff', '') =~# '\.word$' + if has('conceal') && get(g:, 'git_messenger_conceal_word_diff_marker', v:true) + syn region diffWordsRemoved matchgroup=Conceal start=/\[-/ end=/-]/ concealends oneline + syn region diffWordsAdded matchgroup=Conceal start=/{+/ end=/+}/ concealends oneline + else + syn region diffWordsRemoved start=/\[-/ end=/-]/ oneline + syn region diffWordsAdded start=/{+/ end=/+}/ oneline + endif +else + syn match diffRemoved "^ \=-.*" display + syn match diffAdded "^ \=+.*" display +endif + +syn match diffFile "^ \=diff --git .*" display +syn match diffOldFile "^ \=--- a\>.*" display +syn match diffNewFile "^ \=+++ b\>.*" display +syn match diffIndexLine "^ \=index \x\{7,}\.\.\x\{7,}.*" display +syn match diffLine "^ \=@@ .*" display + +hi def link gitmessengerHeader Identifier +hi def link gitmessengerHash Comment +hi def link gitmessengerHistory Constant +hi def link gitmessengerEmail gitmessengerPopupNormal +hi def link gitmessengerPopupNormal NormalFloat + +hi def link diffOldFile diffFile +hi def link diffNewFile diffFile +hi def link diffIndexLine PreProc +hi def link diffFile Type +hi def link diffRemoved Special +hi def link diffAdded Identifier +hi def link diffWordsRemoved diffRemoved +hi def link diffWordsAdded diffAdded +hi def link diffLine Statement + +let b:current_syntax = 'gitmessengerpopup' diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/test/.themisrc b/config/neovim/store/lazy-plugins/git-messenger.vim/test/.themisrc new file mode 100644 index 00000000..66402513 --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/test/.themisrc @@ -0,0 +1,70 @@ +call themis#option('exclude', 'test/README.md') +call themis#option('exclude', 'test/Guardfile') + +if $THEMIS_PROFILE !=# '' + execute 'profile' 'start' $THEMIS_PROFILE + profile! file ./autoload/* + profile! file ./plugin/* + profile! file ./syntax/* +endif + +let s:helper = themis#helper('assert') +call themis#helper('command').with(s:helper) + +function! GetPopupNoWait() abort + for b in range(1, bufnr('$')) + let p = getbufvar(b, '__gitmessenger_popup', v:null) + if p isnot v:null + return p + endif + endfor + return v:null +endfunction +function! GetPopup() abort + return CallWithTimeout(1, funcref('GetPopupNoWait')) +endfunction + +function! CallWithTimeout(timeout, func) abort + let total = 0 + while v:true + let v = a:func() + if v isnot v:null + return v + endif + sleep 100m + let total += 0.1 + if total >= a:timeout + return v:null + endif + endwhile +endfunction + +function! WaitUntil(func, ...) abort + let timeout = get(a:, 1, 1) " 1sec by default + let total = 0 + while !a:func() + sleep 100m + let total += 0.1 + if total >= timeout + " Note: v:true/v:false are not supported by themis.vim + " https://github.com/thinca/vim-themis/pull/56 + return 0 + endif + endwhile + return 1 +endfunction + +function! WaitWhile(func, ...) abort + return call('WaitUntil', [{-> !a:func()}] + a:000) +endfunction + +function! Git(dir, ...) abort + let cmd = join(['git', '-C', shellescape(a:dir)] + a:000, ' ') + let out = system(cmd) + if v:shell_error + throw '`' . cmd . '` failed: ' . out + endif + return out +endfunction + +let g:ON_TRAVIS = $TRAVIS !=# '' diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/test/Guardfile b/config/neovim/store/lazy-plugins/git-messenger.vim/test/Guardfile new file mode 100644 index 00000000..d08d30c3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/test/Guardfile @@ -0,0 +1,21 @@ +def run(target) + target = target.to_s + success = if target.endwith? 'nvim.vimspec' + system({ 'THEMIS_VIM' => 'nvim' }, '../vim-themis/bin/themis', target) + else + system('../vim-themis/bin/themis', target) + end + puts(success ? 'OK' : 'NG') +end + +guard :shell do + watch /^.+\.vim$/ do |m| + puts "Changed #{m[0]}. Run all tests." + run './test/' + end + + watch /^.+\.vimspec$/ do |m| + puts "Changed #{m[0]}" + run m[0] + end +end diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/test/README.md b/config/neovim/store/lazy-plugins/git-messenger.vim/test/README.md new file mode 100644 index 00000000..43df82ab --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/test/README.md @@ -0,0 +1,49 @@ +### How to Run tests + +Setup: + +``` +$ cd /path/to/git-messenger.vim +$ git clone https://github.com/thinca/vim-themis.git +``` + +Run tests on `nvim`: + +``` +$ THEMIS_VIM=nvim ./vim-themis/bin/themis test/all.vimspec +``` + +Run tests on `vim`: + +``` +$ ./vim-themis/bin/themis test/all.vimspec +``` + +### How to run guard + +Install [guard][] and [guard-shell][] as prerequisites. + +``` +$ guard -G test/Guardfile +``` + +It watches your file changes and runs tests automatically. + +### How to take coverage + +Set `$THEMIS_PROFILE` to take profiler log. + +``` +$ THEMIS_PROFILE=profile.txt ./vim-themis/bin/themis test/all.vimspec +``` + +It generates `profile.txt`. And run [covimerage][] to make a coverage file for `coverage` command. + +``` +$ covimerage write_coverage profile.txt +$ coverage report +``` + +[guard]: https://github.com/guard/guard +[guard-shell]: https://github.com/guard/guard-shell +[covimerage]: https://github.com/Vimjas/covimerage diff --git a/config/neovim/store/lazy-plugins/git-messenger.vim/test/all.vimspec b/config/neovim/store/lazy-plugins/git-messenger.vim/test/all.vimspec new file mode 100644 index 00000000..426e9c2c --- /dev/null +++ b/config/neovim/store/lazy-plugins/git-messenger.vim/test/all.vimspec @@ -0,0 +1,1486 @@ +Describe git-messenger.vim + Before all + syntax enable + " Ensure to load autoload/gitmessenger.vim + call gitmessenger#close_popup(-1) + End + + Before each + view! README.md + normal! gg0 + + " Move to introduction paragraph + normal! }2j27l + End + + After each + pclose! + let w = 2 + while w <= winnr('$') + silent! execute w . 'close!' + let w += 1 + endwhile + silent! execute 'bwipeout!' join(range(1, bufnr('$')), ' ') + End + + Describe :GitMessenger + It opens popup with floating window + GitMessenger + + let p = GetPopup() + Assert IsNotNone(p) + Assert KeyExists(p, 'bufnr') + + let lines = getbufline(p.bufnr, 1, '$') + let msg = string(lines) + Assert True(len(lines) >= 6, msg) + + let history = lines[1] + Assert Match(history, '^ History: \+#0$', msg) + + let commit = lines[2] + Assert Match(commit, '^ Commit: \+[[:xdigit:]]\{7,}$', msg) + + let author = lines[3] + Assert Match(author, '^ Author: \+\S\+ <[^>]\+>$', msg) + + let date = lines[4] + Assert Match(date, '^ Date: \+.\+$', msg) + + let summary = lines[6] + Assert NotEmpty(summary, msg) + + let hash = matchstr(commit, '^ Commit: \+\zs[[:xdigit:]]\{7,}$') + let out = system('git show -s ' . hash) + Assert Falsy(v:shell_error, out) + Assert True(stridx(out, summary) >= 0, string(out) . ' should contain ' . string(summary)) + End + + It moves cursor into popup and close it within the popup + let opener_id = win_getid() + + GitMessenger + " Wait for popup + Assert IsNotNone(GetPopup()) + + Assert Equals(win_getid(), opener_id) + + " Move cursor into popup + GitMessenger + + let popup_id = win_getid() + Assert NotEquals(popup_id, opener_id) + Assert Exists('b:__gitmessenger_popup') + + " Close popup + GitMessenger + + Assert Equals(win_getid(), opener_id) + Assert IsNone(GetPopupNoWait()) + End + + It shows an error message when commit is not found + " Open empty buffer so it's not managed by Git + enew! + + GitMessenger + Assert IsNone(GetPopup()) + + let mes = execute('message') + Assert Match(mes, '\ getline(2) =~# '^ History: \+#1$'}) + Assert True(found, 'Expected #1 but got line: ' . string(getline(2))) + + normal o + + let found = WaitUntil({-> getline(2) =~# '^ History: \+#2$'}) + Assert True(found, 'Expected #2 but got line: ' . string(getline(2))) + End + + It shows newer commit on O + GitMessenger + + let p = GetPopup() + Assert IsNotNone(p) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + Assert Match(getline(2), '^ History: \+#0$') + + normal o + call WaitUntil({-> getline(2) =~# '^ History: \+#1$'}) + + normal o + call WaitUntil({-> getline(2) =~# '^ History: \+#2$'}) + + normal O + + let found = WaitUntil({-> getline(2) =~# '^ History: \+#1$'}) + Assert True(found, 'Got line: ' . string(getline(2))) + + normal O + + let found = WaitUntil({-> getline(2) =~# '^ History: \+#0$'}) + Assert True(found, 'Got line: ' . string(getline(2))) + + " Check older again + normal o + + let found = WaitUntil({-> getline(2) =~# '^ History: \+#1$'}) + Assert True(found, 'Got line: ' . string(getline(2))) + End + + It does not cause #23 again + " 1. Open the same buffer with multiple window + " 2. Move cursror to the second window which opens the same + " buffer + vsplit + wincmd p + + let prev_win_id = win_getid() + + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal q + + Assert IsNone(GetPopupNoWait()) + + Assert Equal(win_getid(), prev_win_id) + End + + Describe #63 + Before all + augroup gitmessenger-test-issue-63 + autocmd! + autocmd FileType gitmessengerpopup nunmap d + augroup END + End + + After all + autocmd! gitmessenger-test-issue-63 + End + + It is not reproducible + GitMessenger + + let p = GetPopup() + Assert IsNotNone(p) + + " Enter popup + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + " 'd' mapping should be unmapped by hook at gitmessengerpopup + " filetype event + let output = execute('nmap d') + " Example of `output` content: + " n d *@:call b:__gitmessenger_popup.opts.mappings["d"][0]() + Assert NotMatch(output, 'gitmessenger', string(output)) + End + End + + Describe diff support + " LICENSE and README.md was added at the same commit. + " So 'current' diffs in LICENSE only shows changes of LICENSE. + " And 'all' diffs in LICENSE shows changes of both LICENSE and README.md + Before all + let LicenseDiffExists = {-> index(getline(1, '$'), ' diff --git a/LICENSE b/LICENSE') !=# -1} + let ReadmeDiffExists = {-> index(getline(1, '$'), ' diff --git a/README.md b/README.md') !=# -1} + End + + Before each + view! LICENSE + normal! gg0 + End + + It shows diff hunks of current file on d + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + " Check diff is not shown + let lines = getline(1, '$') + Assert False(LicenseDiffExists(), string(lines)) + + normal d + + let found = WaitUntil(LicenseDiffExists) + Assert True(found, 'Got lines: ' . string(getline(1, '$'))) + End + + It shows all diff hunks on D + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + " Check diff is not shown + let lines = getline(1, '$') + Assert False(LicenseDiffExists(), string(lines)) + + normal D + + let found = WaitUntil(LicenseDiffExists) + Assert True(found, 'Got lines: ' . string(getline(1, '$'))) + End + + It switches diff from 'current' to 'all' by d -> D + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal d + + let found = WaitUntil(LicenseDiffExists) + let lines = 'Got lines: ' . string(getline(1, '$')) + + Assert True(found, lines) + Assert False(ReadmeDiffExists(), lines) + + normal D + + let found = WaitUntil(ReadmeDiffExists) + let lines = 'Got lines: ' . string(getline(1, '$')) + Assert True(found, lines) + Assert True(LicenseDiffExists(), lines) + End + + It switches diff from 'all' to 'current' by D -> d + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal D + + let found = WaitUntil(LicenseDiffExists) + let lines = 'Got lines: ' . string(getline(1, '$')) + Assert True(found, lines) + Assert True(ReadmeDiffExists(), lines) + + normal d + + let removed = WaitWhile(ReadmeDiffExists) + let lines = 'Got lines: ' . string(getline(1, '$')) + Assert True(removed, lines) + Assert True(LicenseDiffExists(), lines) + End + + It toggles file diff on d -> d + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + let expected = v:true + for i in range(3) + normal d + let found = WaitUntil(LicenseDiffExists) + let lines = 'Got lines (' . i . '): ' . string(getline(1, '$')) + Assert Equal(found, expected, lines) + Assert False(ReadmeDiffExists(), lines) + " Flip expectation because d toggles file diff + let expected = !expected + endfor + End + + It toggles commit diff on D -> D + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + let expected = v:true + for i in range(3) + normal D + let found = WaitUntil(ReadmeDiffExists) + let lines = 'Got lines (' . i . '): ' . string(getline(1, '$')) + Assert Equal(found, expected, lines) + Assert Equal(LicenseDiffExists(), expected, lines) + " Flip expectation because D toggles commit diff + let expected = !expected + endfor + End + + It switches diff between 'all' and 'current' repeatedly + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + let idx = 0 + for m in repeat(['D', 'd'], 3) + execute 'normal' m + if m ==# 'd' + let ok = WaitWhile(ReadmeDiffExists) + else + let ok = WaitUntil(ReadmeDiffExists) + endif + let lines = printf('Got lines #%d (%s): %s', idx, m, string(getline(1, '$'))) + Assert True(ok, lines) + Assert True(LicenseDiffExists(), lines) + let idx += 1 + endfor + End + + Describe g:git_messenger_include_diff + Before each + let saved_include_diff = g:git_messenger_include_diff + End + + After each + let g:git_messenger_include_diff = saved_include_diff + End + + It shows 'all' diff after 'current' diff is shown by g:git_messenger_include_diff + let g:git_messenger_include_diff = 'current' + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + let lines = 'Got lines: ' . string(getline(1, '$')) + Assert True(LicenseDiffExists(), lines) + Assert False(ReadmeDiffExists(), lines) + + normal D + + let found = WaitUntil(ReadmeDiffExists) + let lines = 'Got lines: ' . string(getline(1, '$')) + Assert True(found, lines) + Assert True(LicenseDiffExists(), lines) + End + + It shows 'current' diff after 'all' diff is shown by g:git_messenger_include_diff + let g:git_messenger_include_diff = 'all' + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + let lines = 'Got lines: ' . string(getline(1, '$')) + Assert True(LicenseDiffExists(), lines) + Assert True(ReadmeDiffExists(), lines) + + normal d + + let removed = WaitWhile(ReadmeDiffExists) + let lines = 'Got lines: ' . string(getline(1, '$')) + Assert True(removed, lines) + Assert True(LicenseDiffExists(), lines) + End + End + + Describe Issue #29 + Before all + let root_dir = './issue-29' + let file = 'foo.txt' + let path = root_dir . '/' . file + + call mkdir(root_dir) + call Git(root_dir, 'init', '.') + call writefile(['aaa'], path) + call Git(root_dir, 'add', file) + call Git(root_dir, 'commit', '-m', 'init') + End + + After all + if delete(root_dir, 'rf') != 0 + throw 'delete() failed' + endif + End + + It is not reproduced again + execute 'view!' path + normal! gg + + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal d + + let found = WaitUntil({-> index(getline(1, '$'), ' diff --git a/foo.txt b/foo.txt') !=# -1}) + Assert True(found, 'Lines: ' . string(getline(1, '$')) . ' Messages: ' . execute('message')) + End + End + + It reveals word diffs with r and dismisses it with r again + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + " Check diff is not shown + Assert False(LicenseDiffExists(), string(getline(1, '$'))) + + normal r + + let found = WaitUntil(LicenseDiffExists) + let lines = getline(1, '$') + let msg = 'Got lines: ' . string(lines) + Assert True(found, msg) + Assert False(ReadmeDiffExists(), lines) + + let add_lines = filter(copy(lines), 'v:val =~# "{+.\\++}"') + Assert NotEmpty(add_lines, msg) + + normal r + + let removed = WaitWhile(LicenseDiffExists) + Assert True(removed, 'Got lines: ' . string(getline(1, '$'))) + End + + It reveals all word diffs with R and dismisses it with R again + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + " Check diff is not shown + Assert False(LicenseDiffExists(), string(getline(1, '$'))) + + normal R + + let found = WaitUntil(LicenseDiffExists) + let lines = getline(1, '$') + let msg = 'Got lines: ' . string(lines) + Assert True(found, msg) + Assert True(ReadmeDiffExists(), msg) + + let add_lines = filter(copy(lines), 'v:val =~# "{+.\\++}"') + Assert NotEmpty(add_lines, msg) + + normal R + + let removed = WaitWhile(LicenseDiffExists) + Assert True(removed, 'Got lines: ' . string(getline(1, '$'))) + End + + It switches between normal diffs and word diffs + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + " Check diff is not shown + Assert False(LicenseDiffExists(), string(getline(1, '$'))) + + let WordDiffMarkerExists = {-> !empty(filter(getline(1, '$'), 'v:val =~# "{+.\\++}"'))} + + " none -> word diff + normal r + + let found = WaitUntil(LicenseDiffExists) + let msg = 'Got lines: ' . string(getline(1, '$')) + Assert True(found, msg) + Assert True(LicenseDiffExists(), msg) + + Assert WordDiffMarkerExists() + + " word diff -> diff + normal d + + let marker_removed = WaitWhile(WordDiffMarkerExists) + let msg = 'Got lines: ' . string(getline(1, '$')) + Assert True(marker_removed, msg) + Assert True(LicenseDiffExists(), msg) + + " diff -> word diff + normal r + + let found = WaitUntil(WordDiffMarkerExists) + let msg = 'Got lines: ' . string(getline(1, '$')) + Assert True(found, msg) + End + + Describe g:git_messenger_popup_content_margins + Before + let content_margins_saved = g:git_messenger_popup_content_margins + End + + After + let g:git_messenger_popup_content_margins = content_margins_saved + End + + It removes margins on toggling diff when v:false is set (#85) + let g:git_messenger_popup_content_margins = v:false + + let LicenseDiffNoMarginsExists = {-> index(getline(1, '$'), 'diff --git a/LICENSE b/LICENSE') !=# -1} + let ReadmeDiffNoMarginsExists = {-> index(getline(1, '$'), 'diff --git a/README.md b/README.md') !=# -1} + + GitMessenger + + let p = GetPopup() + Assert IsNotNone(p) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + let num_lines = len(getbufline(p.bufnr, 1, '$')) + let expected = v:true + for i in range(3) + normal d + let found = WaitUntil(LicenseDiffNoMarginsExists) + let lines = 'Got lines (' . i . '): ' . string(getline(1, '$')) + Assert Equal(found, expected, lines) + Assert False(ReadmeDiffNoMarginsExists(), lines) + " Repeated toggling should not grow the buffer size + if !expected + Assert Equal(len(getline(1, '$')), num_lines, lines) + endif + " Flip expectation because d toggles file diff + let expected = !expected + endfor + End + End + End + + It preserves diff hunks after going back to older commit + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal d + + let found = WaitUntil({-> index(getline(1, '$'), ' diff --git a/README.md b/README.md') !=# -1}) + Assert True(found, 'Got lines: ' . string(getline(1, '$'))) + + for i in range(3) + normal o + Assert WaitUntil({-> getline(2) =~# '^ History: \+#1$'}) + + let lines = getline(1, '$') + let idx = index(lines, ' diff --git a/README.md b/README.md') + Assert Equals(idx, -1, string(lines)) + + normal O + Assert WaitUntil({-> getline(2) =~# '^ History: \+#0$'}) + + " Check diff is restored + let lines = getline(1, '$') + let idx = index(lines, ' diff --git a/README.md b/README.md') + Assert NotEqual(idx, -1, string(lines)) + endfor + End + + It preserves previous diff content after changing content repeatedly + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + let expected = v:false + for i in range(3) + if i > 0 + " Toggle diff to update content + normal d + " Flip expectation since diff was toggled + let expected = !expected + endif + + let found = WaitUntil({-> index(getline(1, '$'), ' diff --git a/README.md b/README.md') !=# -1}) + let msg = 'Got lines: (' . i . ') ' . string(getline(1, '$')) + Assert Equal(found, expected, msg) + + normal o + Assert WaitUntil({-> getline(2) =~# '^ History: \+#1$'}) + + normal O + Assert WaitUntil({-> getline(2) =~# '^ History: \+#0$'}) + + let found = WaitUntil({-> index(getline(1, '$'), ' diff --git a/README.md b/README.md') !=# -1}) + let msg = 'Got lines: (' . i . ') ' . string(getline(1, '$')) + Assert Equal(found, expected, msg) + endfor + End + + It preserves word diff hunks after going back to older commit + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal r + + let WordDiffMarkerExists = {-> !empty(filter(getline(1, '$'), 'v:val =~# "{+.\\++}"'))} + let found = WaitUntil(WordDiffMarkerExists) + Assert True(found, 'Got lines: ' . string(getline(1, '$'))) + + for i in range(3) + normal o + Assert WaitUntil({-> getline(2) =~# '^ History: \+#1$'}) + Assert False(WordDiffMarkerExists(), string(getline(1, '$'))) + + normal O + Assert WaitUntil({-> getline(2) =~# '^ History: \+#0$'}) + + " Check diff is restored + Assert True(WordDiffMarkerExists(), string(getline(1, '$'))) + endfor + End + + It highlights popup content with gitmessengerpopup filetype + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + Assert Equal(&filetype, 'gitmessengerpopup') + + " Highlight name at 'H' of 'History:' header + let [line, col] = [2, 2] + let name = synIDattr(synID(line, col, 1), 'name') + Assert Equal(name, 'gitmessengerHeader') + End + + It shows author date and committer date separately when they are different + " Move to Screenshot title. At point of f1e133c, author date and committer date are + " different for this line's commit + call search('## Screenshot') + + GitMessenger + + let p = GetPopup() + Assert IsNotNone(p) + Assert KeyExists(p, 'bufnr') + + let lines = getbufline(p.bufnr, 1, '$') + let msg = string(lines) + + Assert Match(lines[4], '^ Author Date: \+\S\+', msg) + Assert Match(lines[5], '^ Committer Date: \+\S\+', msg) + End + + Describe lines not committed yet + Before all + let root_dir = './not-committed-yet-test' + call mkdir(root_dir) + call Git('.', 'init', root_dir) + + let file_before_add = 'before_add.txt' + let file_before_commit = 'before_commit.txt' + let before_add = root_dir . '/' . file_before_add + let before_commit = root_dir . '/' . file_before_commit + + call writefile(['aaa'], before_add) + call Git(root_dir, 'add', file_before_add) + call writefile(['aaa'], before_commit) + call Git(root_dir, 'add', file_before_commit) + + call Git(root_dir, "commit -m init") + call writefile(['bbb', 'ccc'], before_add, 'a') + call writefile(['bbb', 'ccc'], before_commit, 'a') + call Git(root_dir, 'add', file_before_commit) + + let DiffExists = {ls, f -> index(ls, printf(' diff --git a/%s b/%s', f, f)) >= 0} + End + + After all + if delete(root_dir, 'rf') != 0 + throw 'delete() failed' + endif + End + + It shows 'Not committed yet' popup + for file in [before_add, before_commit] + execute 'view!' file + normal! G + + GitMessenger + let p = GetPopup() + Assert IsNotNone(p) + + let lines = getbufline(p.bufnr, 1, '$') + let msg = file . ': ' . string(lines) + + Assert True(len(lines) >= 6, msg) + + let commit = lines[2] + Assert Match(commit, '^ Commit: \+0\{7,}$', msg) + + let summary = lines[6] + Assert Equals(summary, ' This line is not committed yet', msg) + endfor + End + + It shows current diffs which are not committed on 'd' + for path in [before_add, before_commit] + execute 'view!' path + normal! G + + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + + let lines = getline(1, '$') + let msg = path . ': ' . string(lines) + + Assert False(DiffExists(lines, file_before_add), msg) + Assert False(DiffExists(lines, file_before_commit), msg) + + let len_lines = len(lines) + normal d + + Assert WaitUntil({-> len(getline(1, '$')) > len_lines}) + + let lines = getline(1, '$') + let msg = path . ': ' . string(lines) + + if path == before_add + let file = file_before_add + let other_file = file_before_commit + else + let file = file_before_commit + let other_file = file_before_add + endif + + Assert True(DiffExists(lines, file), msg) + Assert False(DiffExists(lines, other_file), msg) + endfor + End + + It shows all diffs which are not committed on 'D' + for path in [before_add, before_commit] + execute 'view!' path + normal! G + + GitMessenger + Assert IsNotNone(GetPopup()) + GitMessenger + + let lines = getline(1, '$') + let msg = path . ': ' . string(lines) + + Assert False(DiffExists(lines, file_before_add), msg) + Assert False(DiffExists(lines, file_before_commit), msg) + + let len_lines = len(lines) + normal D + + Assert WaitUntil({-> len(getline(1, '$')) > len_lines}) + + let lines = getline(1, '$') + let msg = path . ': ' . string(lines) + + Assert True(DiffExists(lines, file_before_add), msg) + Assert True(DiffExists(lines, file_before_commit), msg) + endfor + End + + " Edge cases + Describe g:git_messenger_include_diff + Before all + let saved = g:git_messenger_include_diff + End + + After all + let g:git_messenger_include_diff = saved + End + + It shows diffs not committed yet of 'current' file on popup open + let g:git_messenger_include_diff = 'current' + execute 'view!' before_add + normal! G + + GitMessenger + let p = GetPopup() + Assert IsNotNone(p) + + let lines = getbufline(p.bufnr, 1, '$') + let msg = string(lines) + + Assert True(DiffExists(lines, file_before_add), msg) + Assert False(DiffExists(lines, file_before_commit), msg) + End + + It shows 'all' diffs not committed yet on popup open + let g:git_messenger_include_diff = 'all' + execute 'view!' before_commit + normal! G + + GitMessenger + let p = GetPopup() + Assert IsNotNone(p) + + let lines = getbufline(p.bufnr, 1, '$') + let msg = string(lines) + + Assert True(DiffExists(lines, file_before_add), msg) + Assert True(DiffExists(lines, file_before_commit), msg) + End + End + End + End + + Describe g:git_messenger_include_diff + Before all + let saved = g:git_messenger_include_diff + End + + After all + let g:git_messenger_include_diff = saved + End + + It shows diff hunks of current file in opened popup + let g:git_messenger_include_diff = 'current' + GitMessenger + + let p = GetPopup() + Assert IsNotNone(p) + + let lines = getbufline(p.bufnr, 1, '$') + let idx = index(lines, ' diff --git a/README.md b/README.md') + + Assert NotEqual(idx, -1, string(lines)) + End + + It shows diff hunks of current file for older/newer commits + let g:git_messenger_include_diff = 'current' + + GitMessenger + + let p = GetPopup() + Assert IsNotNone(p) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal o + call WaitUntil({-> getline(2) =~# '^ History: \+#1$'}) + + let lines = getbufline(p.bufnr, 1, '$') + let idx = index(lines, ' diff --git a/README.md b/README.md') + Assert NotEqual(idx, -1, string(lines)) + + normal O + call WaitUntil({-> getline(2) =~# '^ History: \+#0$'}) + + let lines = getbufline(p.bufnr, 1, '$') + let idx = index(lines, ' diff --git a/README.md b/README.md') + Assert NotEqual(idx, -1, string(lines)) + End + + It shows all diff hunks in opened popup + let g:git_messenger_include_diff = 'all' + GitMessenger + + let p = GetPopup() + Assert IsNotNone(p) + + let lines = getbufline(p.bufnr, 1, '$') + let idx = index(lines, ' diff --git a/README.md b/README.md') + + Assert NotEqual(idx, -1, string(lines)) + End + + It shows all diff hunks for older/newer commits + let g:git_messenger_include_diff = 'all' + + GitMessenger + + let p = GetPopup() + Assert IsNotNone(p) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal o + call WaitUntil({-> getline(2) =~# '^ History: \+#1$'}) + + let lines = getbufline(p.bufnr, 1, '$') + let idx = index(lines, ' diff --git a/README.md b/README.md') + Assert NotEqual(idx, -1, string(lines)) + + normal O + call WaitUntil({-> getline(2) =~# '^ History: \+#0$'}) + + let lines = getbufline(p.bufnr, 1, '$') + let idx = index(lines, ' diff --git a/README.md b/README.md') + Assert NotEqual(idx, -1, string(lines)) + End + End + + Describe (git-messenger) + It is mapped to gm by default + let m = mapcheck('gm') + Assert Equal(m, '(git-messenger)') + End + + It invokes :GitMessenger + execute 'normal' "\(git-messenger)" + + let p = GetPopup() + Assert IsNotNone(p) + Assert KeyExists(p, 'bufnr') + End + End + + Describe g:git_messenger_max_popup_height + Before all + let saved = g:git_messenger_max_popup_height + End + + After all + let g:git_messenger_max_popup_height = saved + End + + It limits height of popup window + let g:git_messenger_max_popup_height = 4 + + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + Assert Equal(winheight(0), 4) + End + + It does not affect height when content has less height + let g:git_messenger_max_popup_height = 20 + + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + let h = winheight(0) + Assert True(h < 20, h) + End + End + + Describe g:git_messenger_max_popup_width + Before all + let saved = g:git_messenger_max_popup_width + End + + After all + let g:git_messenger_max_popup_width = saved + End + + It limits width of popup window + if !has('nvim-0.4') + Skip width of preview window cannot be changed + endif + + let g:git_messenger_max_popup_width = 4 + + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + Assert Equal(winwidth(0), 4) + End + + It does not affect width when content has less width + if !has('nvim-0.4') + Skip width of preview window cannot be changed + endif + + " Use first line of LICENSE file because commit message of the + " line is smaller. In this test case, it is important that width + " of content is small. + view! LICENSE + normal! gg0 + + let g:git_messenger_max_popup_width = winwidth(0) + + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + let w = winwidth(0) + Assert True(w < g:git_messenger_max_popup_width, w . ' v.s. ' . g:git_messenger_max_popup_width) + End + End + + Describe Issue #25 + Before all + let root_dir = './issue-25' + call mkdir(root_dir) + call Git('.', 'init', root_dir) + + let filename = 'foo.txt' + let filepath = root_dir . '/' . filename + let renamename = 'renamed.txt' + let renamepath = root_dir . '/' . renamename + + let lines = [ + \ 'aaa', + \ 'bbb', + \ 'ccc', + \ 'ddd', + \ 'eee', + \ 'fff', + \ 'ggg', + \ 'hhh', + \ 'iii', + \ 'jjj', + \ 'hhh', + \ ] + call writefile(lines, filepath) + call Git(root_dir, 'add', filename) + call Git(root_dir, 'commit -m init') + + let lines[0] = 'xxx' + let lines += ['kkk'] + + call Git(root_dir, 'mv', filename, renamename) + call writefile(lines, renamepath) + call Git(root_dir, 'add', renamename) + + call Git(root_dir, 'commit -m rename') + + End + + After all + if delete(root_dir, 'rf') != 0 + throw 'delete() failed' + endif + End + + Before each + execute 'view!' renamepath + End + + It should no longer be reproduced on exploring older commits + normal! gg0 + + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + Assert Match(getline(2), '^ History: \+#0$') + + normal o + + let found = WaitUntil({-> getline(2) =~# '^ History: \+#1$'}) + Assert True(found, 'Got line: ' . string(getline(2) . ' with messeage:' . execute('message'))) + End + + It should no longer be reproduced on revealing diff + " Test case 1: Before rename (foo.txt) + 2 + normal! 0 + + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal d + + let found = WaitUntil({-> index(getline(1, '$'), ' diff --git a/foo.txt b/foo.txt') !=# -1}) + let lines = getline(1, '$') + Assert True(found, 'Popup lines: ' . string(lines) . ' with message:' . execute('message')) + Assert True(index(lines, ' +aaa') > 0, string(lines)) + Assert True(index(lines, ' +bbb') > 0, string(lines)) + + quit + + " Test case 2: While renaming + normal! gg0 + + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal d + + let found = WaitUntil({-> index(getline(1, '$'), ' diff --git a/foo.txt b/renamed.txt') !=# -1}) + let lines = getline(1, '$') + Assert True(found, 'Popup lines: ' . string(lines) . ' with message:' . execute('message')) + " Should show diff of files even while renaming + Assert True(index(lines, ' -aaa') > 0, string(lines)) + Assert True(index(lines, ' +xxx') > 0, string(lines)) + Assert True(index(lines, ' bbb') > 0, string(lines)) + End + + It should no longer be reproduced at initial diff + normal! gg0 + + let saved_include_diff = g:git_messenger_include_diff + let g:git_messenger_include_diff = 'current' + try + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + let lines = getline(1, '$') + let msg = string(lines) + Assert True(index(lines, ' diff --git a/foo.txt b/renamed.txt') > 0, msg) + Assert True(index(lines, ' -aaa') > 0, msg) + Assert True(index(lines, ' +xxx') > 0, msg) + Assert True(index(lines, ' bbb') > 0, msg) + finally + let g:git_messenger_include_diff = saved_include_diff + endtry + End + + It should show diff of commit renaming the file after backing with O + " Note: This test case is follow up of 4caef1d + + normal! gg0 + + GitMessenger + Assert IsNotNone(GetPopup()) + + GitMessenger + Assert Exists('b:__gitmessenger_popup') + + normal o + + let found = WaitUntil({-> getline(2) =~# '^ History: \+#1$'}) + Assert True(found, 'Got line: ' . string(getline(2) . ' with messeage: ' . execute('message'))) + + normal O + + let found = WaitUntil({-> getline(2) =~# '^ History: \+#0$'}) + Assert True(found, 'Got line: ' . string(getline(2) . ' with messeage: ' . execute('message'))) + + normal d + + let found = WaitUntil({-> index(getline(1, '$'), ' diff --git a/foo.txt b/renamed.txt') !=# -1}) + let lines = getline(1, '$') + let msg = string(lines) + + Assert True(found, msg) + Assert True(index(lines, ' -aaa') > 0, msg) + Assert True(index(lines, ' +xxx') > 0, msg) + Assert True(index(lines, ' bbb') > 0, msg) + End + End + + Describe gitmessenger#git#root_dir() + Before all + let root_dir = './my git root with space' + let file = 'file.txt' + let path = root_dir . '/' . file + + call mkdir(root_dir) + call writefile(['123'], path) + + call Git(root_dir, 'init', '.') + call Git(root_dir, 'add', file) + call Git(root_dir, 'commit', '-m', 'init') + End + + After all + if delete(root_dir, 'rf') != 0 + throw 'delete() failed' + endif + End + + It returns correct root when pathname contains space + " call fnamemodify here the same way + " gitmessenger#blame#new() does to get our absolute path + let file = './my git root with space/file.txt' + let expected_root_dir = fnamemodify(file, ':p:h') + let actual_root_dir = gitmessenger#git#root_dir(file) + + " Failure will return the repo root instead of our created + " ad-hoc root directory + Assert Equal(actual_root_dir, expected_root_dir) + End + End + + Describe g:git_messenger_date_format + Before all + let saved_date_format = g:git_messenger_date_format + End + + After all + let g:git_messenger_date_format = saved_date_format + End + + It changes date format + " e.g. '2020' + let g:git_messenger_date_format = '%Y' + + GitMessenger + + let p = GetPopup() + let date = getbufline(p.bufnr, 1, '$')[4] + Assert Match(date, '^ Date: \+\d\{4\}$', date) + End + End + + Describe g:git_messenger_popup_content_margins + Before all + let saved_popup_content_margins = g:git_messenger_popup_content_margins + End + + After all + let g:git_messenger_popup_content_margins = saved_popup_content_margins + End + + It adds margins when v:true is set + let g:git_messenger_popup_content_margins = v:true + + GitMessenger + + let p = GetPopup() + let lines = getbufline(p.bufnr, 1, '$') + Assert True(len(lines) > 3) + + " Check margins exist + Assert Equal(lines[0], '') + Assert Equal(lines[len(lines)-1], '') + for line in lines[1:-2] + if line !=# '' + Assert Match(line, '^ ') + endif + endfor + + " With diffs + GitMessenger + normal d + + " Check margins exist + Assert Equal(lines[0], '') + Assert Equal(lines[len(lines)-1], '') + for line in lines[1:-2] + if line !=# '' + Assert Match(line, '^ ') + endif + endfor + End + + It removes margins when v:false is set + let g:git_messenger_popup_content_margins = v:false + + GitMessenger + + let p = GetPopup() + let lines = getbufline(p.bufnr, 1, '$') + Assert True(len(lines) > 3) + + " Check margins don't exist + Assert NotEqual(lines[0], '') + Assert NotEqual(lines[len(lines)-1], '') + for line in lines[1:-2] + if line !=# '' + Assert NotMatch(line, '^ ') + endif + endfor + + " With diffs + GitMessenger + normal d + + " Check margins don't exist + Assert NotEqual(lines[0], '') + Assert NotEqual(lines[len(lines)-1], '') + for line in lines[1:-2] + if line !=# '' + Assert NotMatch(line, '^ ') + endif + endfor + End + End + + Describe g:git_messenger_floating_win_opts + Before all + let saved_floating_win_opts = g:git_messenger_floating_win_opts + End + + After all + let g:git_messenger_floating_win_opts = saved_floating_win_opts + End + + It overrides floating window options + if !has('nvim') + Skip g:git_messenger_floating_win_opts is effective only on Neovim + endif + + let g:git_messenger_floating_win_opts = {'height': 10000} + GitMessenger + let p = GetPopup() + let c = nvim_win_get_config(p.win_id) + Assert Equal(c.height, 10000) + End + End + + Describe gitmessenger#git#new + It runs Git command at specified directory using -C + let git = gitmessenger#git#new('echo', '.') + let state = {} + let OnExit = {g -> extend(state, {'exit': g.exit_status})} + + call git.spawn([], OnExit) + call WaitUntil({-> has_key(state, 'exit')}) + + Assert True(len(git.stdout) > 0, string(git.stdout)) + let stdout = git.stdout[0] + Assert True(stridx(stdout, '-C .') >= 0, string(stdout)) + End + + It trims trailing \r (#75) + let git = gitmessenger#git#new('echo', '.') + let state = {} + let OnExit = {g -> extend(state, {'exit': g.exit_status})} + + if has('win32') + " 'echo' command on Windows use \r\n for newlines. So `echo "foo\r"` outputs + " "foo\r\r\n" at terminal + let input = "this\nis\ntest" + else + let input = "this\r\nis\r\ntest" + endif + + call git.spawn([input], OnExit) + call WaitUntil({-> has_key(state, 'exit')}) + + Assert Equals(state.exit, 0) + Assert True(len(git.stdout) > 0, string(git.stdout)) + + let stdout = substitute(substitute(string(git.stdout), '\r', '\\r', 'g'), '\n', '\\n', 'g') + + for n in range(len(git.stdout)) + let l = git.stdout[n] + Assert NotMatch(l, '\r$', stdout . ' at ' . n) + endfor + End + End +End + +" vim: set ft=vim: diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/doc/gitsigns.txt b/config/neovim/store/lazy-plugins/gitsigns.nvim/doc/gitsigns.txt new file mode 100644 index 00000000..4393273a --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/doc/gitsigns.txt @@ -0,0 +1,1206 @@ +*gitsigns.txt* Gitsigns +*gitsigns.nvim* + +Author: Lewis Russell +Version: v0.8.1 +Homepage: +License: MIT license + +============================================================================== +INTRODUCTION *gitsigns* + +Gitsigns is a plugin for Neovim that provides integration with Git via a +feature set which includes (but not limited to): + • Provides signs in the |signcolumn| to show changed/added/removed lines. + • Mappings to operate on hunks to stage, undo or reset against Git's index. + +Gitsigns is implemented entirely in Lua which is built into Neovim and +requires no external dependencies other than git. This is unlike other plugins +that require python, node, etc, which need to communicate with Neovim using +|RPC|. By default, Gitsigns also uses Neovim's built-in diff library +(`vim.diff`) unlike other similar plugins that need to run `git-diff` as an +external process which is less efficient, has tighter bottlenecks and requires +file IO. + +============================================================================== +USAGE *gitsigns-usage* + +For basic setup with all batteries included: +>lua + require('gitsigns').setup() +< + +Configuration can be passed to the setup function. Here is an example with most +of the default settings: +>lua + require('gitsigns').setup { + signs = { + add = { text = '┃' }, + change = { text = '┃' }, + delete = { text = '_' }, + topdelete = { text = '‾' }, + changedelete = { text = '~' }, + untracked = { text = '┆' }, + }, + signcolumn = true, -- Toggle with `:Gitsigns toggle_signs` + numhl = false, -- Toggle with `:Gitsigns toggle_numhl` + linehl = false, -- Toggle with `:Gitsigns toggle_linehl` + word_diff = false, -- Toggle with `:Gitsigns toggle_word_diff` + watch_gitdir = { + follow_files = true + }, + auto_attach = true, + attach_to_untracked = false, + current_line_blame = false, -- Toggle with `:Gitsigns toggle_current_line_blame` + current_line_blame_opts = { + virt_text = true, + virt_text_pos = 'eol', -- 'eol' | 'overlay' | 'right_align' + delay = 1000, + ignore_whitespace = false, + virt_text_priority = 100, + }, + current_line_blame_formatter = ', - ', + current_line_blame_formatter_opts = { + relative_time = false, + }, + sign_priority = 6, + update_debounce = 100, + status_formatter = nil, -- Use default + max_file_length = 40000, -- Disable if file is longer than this (in lines) + preview_config = { + -- Options passed to nvim_open_win + border = 'single', + style = 'minimal', + relative = 'cursor', + row = 0, + col = 1 + }, + } +< + +============================================================================== +MAPPINGS *gitsigns-mappings* + +Custom mappings can be defined in the `on_attach` callback in the config table +passed to |gitsigns-setup()|. See |gitsigns-config-on_attach|. + +Most actions can be repeated with `.` if you have |vim-repeat| installed. + +============================================================================== +FUNCTIONS *gitsigns-functions* + +Note functions with the {async} attribute are run asynchronously and accept +an optional {callback} argument. + + +setup({cfg}, {callback?}) *gitsigns.setup()* + Setup and start Gitsigns. + + Attributes: ~ + {async} + + Parameters: ~ + {cfg} (table|nil): Configuration for Gitsigns. + See |gitsigns-usage| for more details. + +attach({bufnr}, {ctx}, {callback?}) *gitsigns.attach()* + Attach Gitsigns to the buffer. + + Attributes: ~ + {async} + + Parameters: ~ + {bufnr} (integer): Buffer number + {ctx} (table|nil): + Git context data that may optionally be used to attach to any + buffer that represents a real git object. + • {file}: (string) + Path to the file represented by the buffer, relative to the + top-level. + • {toplevel}: (string?) + Path to the top-level of the parent git repository. + • {gitdir}: (string?) + Path to the git directory of the parent git repository + (typically the ".git/" directory). + • {commit}: (string?) + The git revision that the file belongs to. + • {base}: (string?) + The git revision that the file should be compared to. + +detach({bufnr}) *gitsigns.detach()* + Detach Gitsigns from the buffer {bufnr}. If {bufnr} is not + provided then the current buffer is used. + + Parameters: ~ + {bufnr} (integer): Buffer number + +detach_all() *gitsigns.detach_all()* + Detach Gitsigns from all buffers it is attached to. + +refresh() *gitsigns.refresh()* + Refresh all buffers. + + Attributes: ~ + {async} + +get_actions() *gitsigns.get_actions()* + Get all the available line specific actions for the current + buffer at the cursor position. + + + Returns: ~ + (table|nil): Dictionary of action name to function which when called + performs action. + +setloclist({nr}, {target}) *gitsigns.setloclist()* + Populate the location list with hunks. Automatically opens the + location list window. + + Alias for: `setqflist({target}, { use_location_list = true, nr = {nr} }` + + Attributes: ~ + {async} + + Parameters: ~ + {nr?} (integer): Window number or the |window-ID|. + `0` for the current window (default). + {target} (integer|string): See |gitsigns.setqflist()|. + +setqflist({target}, {opts}, {callback?}) *gitsigns.setqflist()* + Populate the quickfix list with hunks. Automatically opens the + quickfix window. + + Attributes: ~ + {async} + + Parameters: ~ + {target} (integer|string): + Specifies which files hunks are collected from. + Possible values. + • [integer]: The buffer with the matching buffer + number. `0` for current buffer (default). + • `"attached"`: All attached buffers. + • `"all"`: All modified files for each git + directory of all attached buffers in addition + to the current working directory. + {opts} (table|nil): Additional options: + • {use_location_list}: (boolean) + Populate the location list instead of the + quickfix list. Default to `false`. + • {nr}: (integer) + Window number or ID when using location list. + Expand folds when navigating to a hunk which is + inside a fold. Defaults to `0`. + • {open}: (boolean) + Open the quickfix/location list viewer. + Defaults to `true`. + +show({revision}) *gitsigns.show()* + Show revision {base} of the current file, if it is given, or + with the currently set base (index by default). + + If {base} is the index, then the opened buffer is editable and + any written changes will update the index accordingly. + + Examples: >vim + " View the index version of the file + :Gitsigns show + + " View revision of file in the last commit + :Gitsigns show ~1 +< + + For a more complete list of ways to specify bases, see + |gitsigns-revision|. + + Attributes: ~ + {async} + +diffthis({base}, {opts}) *gitsigns.diffthis()* + Perform a |vimdiff| on the given file with {base} if it is + given, or with the currently set base (index by default). + + If {base} is the index, then the opened buffer is editable and + any written changes will update the index accordingly. + + Examples: >vim + " Diff against the index + :Gitsigns diffthis + + " Diff against the last commit + :Gitsigns diffthis ~1 +< + + For a more complete list of ways to specify bases, see + |gitsigns-revision|. + + Attributes: ~ + {async} + + Parameters: ~ + {base} (string|nil): Revision to diff against. Defaults to index. + {opts} (table|nil): Additional options: + • {vertical}: {boolean}. Split window vertically. Defaults to + config.diff_opts.vertical. If running via command line, then + this is taken from the command modifiers. + • {split}: {string}. One of: 'aboveleft', 'belowright', + 'botright', 'rightbelow', 'leftabove', 'topleft'. Defaults to + 'aboveleft'. If running via command line, then this is taken + from the command modifiers. + +reset_base({global}) *gitsigns.reset_base()* + Reset the base revision to diff against back to the + index. + + Alias for `change_base(nil, {global})` . + +change_base({base}, {global}, {callback?}) *gitsigns.change_base()* + Change the base revision to diff against. If {base} is not + given, then the original base is used. If {global} is given + and true, then change the base revision of all buffers, + including any new buffers. + + Attributes: ~ + {async} + + Examples: >vim + " Change base to 1 commit behind head + :lua require('gitsigns').change_base('HEAD~1') + + " Also works using the Gitsigns command + :Gitsigns change_base HEAD~1 + + " Other variations + :Gitsigns change_base ~1 + :Gitsigns change_base ~ + :Gitsigns change_base ^ + + " Commits work too + :Gitsigns change_base 92eb3dd + + " Revert to original base + :Gitsigns change_base +< + + For a more complete list of ways to specify bases, see + |gitsigns-revision|. + + Parameters: ~ + {base} (string|nil): The object/revision to diff against. + {global} (boolean|nil): Change the base of all buffers. + +blame_line({opts}, {callback?}) *gitsigns.blame_line()* + Run git blame on the current line and show the results in a + floating window. If already open, calling this will cause the + window to get focus. + + Attributes: ~ + {async} + + Parameters: ~ + {opts} (table|nil): Additional options: + • {full}: (boolean) + Display full commit message with hunk. + • {ignore_whitespace}: (boolean) + Ignore whitespace when running blame. + • {rev}: (string) + Revision to blame against. + • {extra_opts}: (string[]) + Extra options passed to `git-blame`. + +get_hunks({bufnr}) *gitsigns.get_hunks()* + Get hunk array for specified buffer. + + Parameters: ~ + {bufnr} (integer): Buffer number, if not provided (or 0) + will use current buffer. + + Returns: ~ + (table|nil): Array of hunk objects. + Each hunk object has keys: + • `"type"`: String with possible values: "add", "change", + "delete" + • `"head"`: Header that appears in the unified diff + output. + • `"lines"`: Line contents of the hunks prefixed with + either `"-"` or `"+"`. + • `"removed"`: Sub-table with fields: + • `"start"`: Line number (1-based) + • `"count"`: Line count + • `"added"`: Sub-table with fields: + • `"start"`: Line number (1-based) + • `"count"`: Line count + +select_hunk() *gitsigns.select_hunk()* + Select the hunk under the cursor. + +preview_hunk_inline() *gitsigns.preview_hunk_inline()* + Preview the hunk at the cursor position inline in the buffer. + +preview_hunk() *gitsigns.preview_hunk()* + Preview the hunk at the cursor position in a floating + window. If the preview is already open, calling this + will cause the window to get focus. + +prev_hunk({opts}, {callback?}) *gitsigns.prev_hunk()* + DEPRECATED: use |gitsigns.nav_hunk()| + + Jump to the previous hunk in the current buffer. If a hunk preview + (popup or inline) was previously opened, it will be re-opened + at the previous hunk. + + Attributes: ~ + {async} + + Parameters: ~ + See |gitsigns.nav_hunk()|. + +next_hunk({opts}, {callback?}) *gitsigns.next_hunk()* + DEPRECATED: use |gitsigns.nav_hunk()| + + Jump to the next hunk in the current buffer. If a hunk preview + (popup or inline) was previously opened, it will be re-opened + at the next hunk. + + Attributes: ~ + {async} + + Parameters: ~ + See |gitsigns.nav_hunk()|. + +nav_hunk({direction}, {opts}, {callback?}) *gitsigns.nav_hunk()* + Jump to hunk in the current buffer. If a hunk preview + (popup or inline) was previously opened, it will be re-opened + at the next hunk. + + Attributes: ~ + {async} + + Parameters: ~ + {direction} ('first'|'last'|'next'|'prev'): + {opts} (table|nil): Configuration table. Keys: + • {wrap}: (boolean) + Whether to loop around file or not. Defaults + to the value 'wrapscan' + • {navigation_message}: (boolean) + Whether to show navigation messages or not. + Looks at 'shortmess' for default behaviour. + • {foldopen}: (boolean) + Expand folds when navigating to a hunk which is + inside a fold. Defaults to `true` if 'foldopen' + contains `search`. + • {preview}: (boolean) + Automatically open preview_hunk() upon navigating + to a hunk. + • {greedy}: (boolean) + Only navigate between non-contiguous hunks. Only useful if + 'diff_opts' contains `linematch`. Defaults to `true`. + • {count}: (integer) + Number of times to advance. Defaults to |v:count1|. + +reset_buffer_index() *gitsigns.reset_buffer_index()* + Unstage all hunks for current buffer in the index. Note: + Unlike |gitsigns.undo_stage_hunk()| this doesn't simply undo + stages, this runs an `git reset` on current buffers file. + + Attributes: ~ + {async} + +stage_buffer() *gitsigns.stage_buffer()* + Stage all hunks in current buffer. + + Attributes: ~ + {async} + +undo_stage_hunk() *gitsigns.undo_stage_hunk()* + Undo the last call of stage_hunk(). + + Note: only the calls to stage_hunk() performed in the current + session can be undone. + + Attributes: ~ + {async} + +reset_buffer() *gitsigns.reset_buffer()* + Reset the lines of all hunks in the buffer. + +reset_hunk({range}, {opts}, {callback?}) *gitsigns.reset_hunk()* + Reset the lines of the hunk at the cursor position, or all + lines in the given range. If {range} is provided, all lines in + the given range are reset. This supports partial-hunks, + meaning if a range only includes a portion of a particular + hunk, only the lines within the range will be reset. + + Parameters: ~ + {range} (table|nil): List-like table of two integers making + up the line range from which you want to reset the hunks. + If running via command line, then this is taken from the + command modifiers. + {opts} (table|nil): Additional options: + • {greedy}: (boolean) + Stage all contiguous hunks. Only useful if 'diff_opts' + contains `linematch`. Defaults to `true`. + +stage_hunk({range}, {opts}, {callback?}) *gitsigns.stage_hunk()* + Stage the hunk at the cursor position, or all lines in the + given range. If {range} is provided, all lines in the given + range are staged. This supports partial-hunks, meaning if a + range only includes a portion of a particular hunk, only the + lines within the range will be staged. + + Attributes: ~ + {async} + + Parameters: ~ + {range} (table|nil): List-like table of two integers making + up the line range from which you want to stage the hunks. + If running via command line, then this is taken from the + command modifiers. + {opts} (table|nil): Additional options: + • {greedy}: (boolean) + Stage all contiguous hunks. Only useful if 'diff_opts' + contains `linematch`. Defaults to `true`. + +toggle_deleted({value}) *gitsigns.toggle_deleted()* + Toggle |gitsigns-config-show_deleted| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + Returns: ~ + (boolean): Current value of |gitsigns-config-show_deleted| + +toggle_current_line_blame({value}) *gitsigns.toggle_current_line_blame()* + Toggle |gitsigns-config-current_line_blame| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + Returns: ~ + (boolean): Current value of |gitsigns-config-current_line_blame| + +toggle_word_diff({value}) *gitsigns.toggle_word_diff()* + Toggle |gitsigns-config-word_diff| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + Returns: ~ + (boolean): Current value of |gitsigns-config-word_diff| + +toggle_linehl({value}) *gitsigns.toggle_linehl()* + Toggle |gitsigns-config-linehl| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + Returns: ~ + (boolean): Current value of |gitsigns-config-linehl| + +toggle_numhl({value}) *gitsigns.toggle_numhl()* + Toggle |gitsigns-config-numhl| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + + Returns: ~ + (boolean): Current value of |gitsigns-config-numhl| + +toggle_signs({value}) *gitsigns.toggle_signs()* + Toggle |gitsigns-config-signbooleancolumn| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + Returns: ~ + (boolean): Current value of |gitsigns-config-signcolumn| + + +============================================================================== +CONFIGURATION *gitsigns-config* + +This section describes the configuration fields which can be passed to +|gitsigns.setup()|. Note fields of type `table` may be marked with extended +meaning the field is merged with the default, with the user value given higher +precedence. This allows only specific sub-fields to be configured without +having to redefine the whole field. + +signs *gitsigns-config-signs* + Type: `table[extended]` + Default: > + { + add = { text = '┃' }, + change = { text = '┃' }, + delete = { text = '▁' }, + topdelete = { text = '▔' }, + changedelete = { text = '~' }, + untracked = { text = '┆' }, + } +< + Configuration for signs: + • `text` specifies the character to use for the sign. + • `show_count` to enable showing count of hunk, e.g. number of deleted + lines. + + The highlights `GitSigns[kind][type]` is used for each kind of sign. E.g. + 'add' signs uses the highlights: + • `GitSignsAdd` (for normal text signs) + • `GitSignsAddNr` (for signs when `config.numhl == true`) + • `GitSignsAddLn `(for signs when `config.linehl == true`) + + See |gitsigns-highlight-groups|. + +worktrees *gitsigns-config-worktrees* + Type: `table`, Default: `nil` + + Detached working trees. + + Array of tables with the keys `gitdir` and `toplevel`. + + If normal attaching fails, then each entry in the table is attempted + with the work tree details set. + + Example: >lua + worktrees = { + { + toplevel = vim.env.HOME, + gitdir = vim.env.HOME .. '/projects/dotfiles/.git' + } + } + +on_attach *gitsigns-config-on_attach* + Type: `function`, Default: `nil` + + Callback called when attaching to a buffer. Mainly used to setup keymaps. + The buffer number is passed as the first argument. + + This callback can return `false` to prevent attaching to the buffer. + + Example: >lua + on_attach = function(bufnr) + if vim.api.nvim_buf_get_name(bufnr):match() then + -- Don't attach to specific buffers whose name matches a pattern + return false + end + + -- Setup keymaps + vim.api.nvim_buf_set_keymap(bufnr, 'n', 'hs', 'lua require"gitsigns".stage_hunk()', {}) + ... -- More keymaps + end +< + +watch_gitdir *gitsigns-config-watch_gitdir* + Type: `table[extended]` + Default: > + `{ + enable = true, + follow_files = true + }` +< + When opening a file, a libuv watcher is placed on the respective + `.git` directory to detect when changes happen to use as a trigger to + update signs. + + Fields: ~ + • `enable`: + Whether the watcher is enabled. + + • `follow_files`: + If a file is moved with `git mv`, switch the buffer to the new location. + +sign_priority *gitsigns-config-sign_priority* + Type: `number`, Default: `6` + + Priority to use for signs. + +signcolumn *gitsigns-config-signcolumn* + Type: `boolean`, Default: `true` + + Enable/disable symbols in the sign column. + + When enabled the highlights defined in `signs.*.hl` and symbols defined + in `signs.*.text` are used. + +numhl *gitsigns-config-numhl* + Type: `boolean`, Default: `false` + + Enable/disable line number highlights. + + When enabled the highlights defined in `signs.*.numhl` are used. If + the highlight group does not exist, then it is automatically defined + and linked to the corresponding highlight group in `signs.*.hl`. + +linehl *gitsigns-config-linehl* + Type: `boolean`, Default: `false` + + Enable/disable line highlights. + + When enabled the highlights defined in `signs.*.linehl` are used. If + the highlight group does not exist, then it is automatically defined + and linked to the corresponding highlight group in `signs.*.hl`. + +show_deleted *gitsigns-config-show_deleted* + Type: `boolean`, Default: `false` + + Show the old version of hunks inline in the buffer (via virtual lines). + + Note: Virtual lines currently use the highlight `GitSignsDeleteVirtLn`. + +diff_opts *gitsigns-config-diff_opts* + Type: `table[extended]`, Default: derived from 'diffopt' + + Diff options. If the default value is used, then changes to 'diffopt' are + automatically applied. + + Fields: ~ + • algorithm: string + Diff algorithm to use. Values: + • "myers" the default algorithm + • "minimal" spend extra time to generate the + smallest possible diff + • "patience" patience diff algorithm + • "histogram" histogram diff algorithm + • internal: boolean + Use Neovim's built in xdiff library for running diffs. + • indent_heuristic: boolean + Use the indent heuristic for the internal + diff library. + • vertical: boolean + Start diff mode with vertical splits. + • linematch: integer + Enable second-stage diff on hunks to align lines. + Requires `internal=true`. + • ignore_blank_lines: boolean + Ignore changes where lines are blank. + • ignore_whitespace_change: boolean + Ignore changes in amount of white space. + It should ignore adding trailing white space, + but not leading white space. + • ignore_whitespace: boolean + Ignore all white space changes. + • ignore_whitespace_change_at_eol: boolean + Ignore white space changes at end of line. + +base *gitsigns-config-base* + Type: `string`, Default: index + + The object/revision to diff against. + See |gitsigns-revision|. + +count_chars *gitsigns-config-count_chars* + Type: `table` + Default: > + `{ "1", "2", "3", "4", "5", "6", "7", "8", "9", + ["+"] = ">" + }` +< + The count characters used when `signs.*.show_count` is enabled. The + `+` entry is used as a fallback. With the default, any count outside + of 1-9 uses the `>` character in the sign. + + Possible use cases for this field: + • to specify unicode characters for the counts instead of 1-9. + • to define characters to be used for counts greater than 9. + +status_formatter *gitsigns-config-status_formatter* + Type: `function` + Default: > + function(status) + local added, changed, removed = status.added, status.changed, status.removed + local status_txt = {} + if added and added > 0 then table.insert(status_txt, '+'..added ) end + if changed and changed > 0 then table.insert(status_txt, '~'..changed) end + if removed and removed > 0 then table.insert(status_txt, '-'..removed) end + return table.concat(status_txt, ' ') + end +< + Function used to format `b:gitsigns_status`. + +max_file_length *gitsigns-config-max_file_length* + Type: `number`, Default: `40000` + + Max file length (in lines) to attach to. + +preview_config *gitsigns-config-preview_config* + Type: `table[extended]` + Default: > + `{ + border = "single", + col = 1, + relative = "cursor", + row = 0, + style = "minimal" + }` +< + Option overrides for the Gitsigns preview window. Table is passed directly + to `nvim_open_win`. + +auto_attach *gitsigns-config-auto_attach* + Type: `boolean`, Default: `true` + + Automatically attach to files. + +attach_to_untracked *gitsigns-config-attach_to_untracked* + Type: `boolean`, Default: `false` + + Attach to untracked files. + +update_debounce *gitsigns-config-update_debounce* + Type: `number`, Default: `100` + + Debounce time for updates (in milliseconds). + +current_line_blame *gitsigns-config-current_line_blame* + Type: `boolean`, Default: `false` + + Adds an unobtrusive and customisable blame annotation at the end of + the current line. + + The highlight group used for the text is `GitSignsCurrentLineBlame`. + +current_line_blame_opts *gitsigns-config-current_line_blame_opts* + Type: `table[extended]` + Default: > + `{ + delay = 1000, + virt_text = true, + virt_text_pos = "eol", + virt_text_priority = 100 + }` +< + Options for the current line blame annotation. + + Fields: ~ + • virt_text: boolean + Whether to show a virtual text blame annotation. + • virt_text_pos: string + Blame annotation position. Available values: + `eol` Right after eol character. + `overlay` Display over the specified column, without + shifting the underlying text. + `right_align` Display right aligned in the window. + • delay: integer + Sets the delay (in milliseconds) before blame virtual text is + displayed. + • ignore_whitespace: boolean + Ignore whitespace when running blame. + • virt_text_priority: integer + Priority of virtual text. + • extra_opts: string[] + Extra options passed to `git-blame`. + +current_line_blame_formatter_opts + *gitsigns-config-current_line_blame_formatter_opts* + DEPRECATED + + Type: `table[extended]` + Default: > + `{ + relative_time = false + }` +< + Options for the current line blame annotation formatter. + + Fields: ~ + • relative_time: boolean + +current_line_blame_formatter *gitsigns-config-current_line_blame_formatter* + Type: `string|function`, Default: `" , - "` + + String or function used to format the virtual text of + |gitsigns-config-current_line_blame|. + + When a string, accepts the following format specifiers: + + • `` + • `` + • `` + • `` + • `` + • `` or `` + • `` + • `` + • `` + • `` or `` + • `` + • `` + • `` + • `` + + For `` and ``, `FORMAT` can + be any valid date format that is accepted by `os.date()` with the + addition of `%R` (defaults to `%Y-%m-%d`): + + • `%a` abbreviated weekday name (e.g., Wed) + • `%A` full weekday name (e.g., Wednesday) + • `%b` abbreviated month name (e.g., Sep) + • `%B` full month name (e.g., September) + • `%c` date and time (e.g., 09/16/98 23:48:10) + • `%d` day of the month (16) [01-31] + • `%H` hour, using a 24-hour clock (23) [00-23] + • `%I` hour, using a 12-hour clock (11) [01-12] + • `%M` minute (48) [00-59] + • `%m` month (09) [01-12] + • `%p` either "am" or "pm" (pm) + • `%S` second (10) [00-61] + • `%w` weekday (3) [0-6 = Sunday-Saturday] + • `%x` date (e.g., 09/16/98) + • `%X` time (e.g., 23:48:10) + • `%Y` full year (1998) + • `%y` two-digit year (98) [00-99] + • `%%` the character `%´ + • `%R` relative (e.g., 4 months ago) + + When a function: + Parameters: ~ + {name} Git user name returned from `git config user.name` . + {blame_info} Table with the following keys: + • `abbrev_sha`: string + • `orig_lnum`: integer + • `final_lnum`: integer + • `author`: string + • `author_mail`: string + • `author_time`: integer + • `author_tz`: string + • `committer`: string + • `committer_mail`: string + • `committer_time`: integer + • `committer_tz`: string + • `summary`: string + • `previous`: string + • `filename`: string + • `boundary`: true? + + Note that the keys map onto the output of: + `git blame --line-porcelain` + + {opts} Passed directly from + |gitsigns-config-current_line_blame_formatter_opts|. + + Return: ~ + The result of this function is passed directly to the `opts.virt_text` + field of |nvim_buf_set_extmark| and thus must be a list of + [text, highlight] tuples. + +current_line_blame_formatter_nc + *gitsigns-config-current_line_blame_formatter_nc* + Type: `string|function`, Default: `" "` + + String or function used to format the virtual text of + |gitsigns-config-current_line_blame| for lines that aren't committed. + + See |gitsigns-config-current_line_blame_formatter| for more information. + +trouble *gitsigns-config-trouble* + Type: `boolean`, Default: true if installed + + When using setqflist() or setloclist(), open Trouble instead of the + quickfix/location list window. + +yadm *gitsigns-config-yadm* + DEPRECATED + Please use |gitsigns-config-on_attach_pre| instead + + Type: `table` + Default: > + `{ + enable = false + }` +< + yadm configuration. + +word_diff *gitsigns-config-word_diff* + Type: `boolean`, Default: `false` + + Highlight intra-line word differences in the buffer. + Requires `config.diff_opts.internal = true` . + + Uses the highlights: + • For word diff in previews: + • `GitSignsAddInline` + • `GitSignsChangeInline` + • `GitSignsDeleteInline` + • For word diff in buffer: + • `GitSignsAddLnInline` + • `GitSignsChangeLnInline` + • `GitSignsDeleteLnInline` + • For word diff in virtual lines (e.g. show_deleted): + • `GitSignsAddVirtLnInline` + • `GitSignsChangeVirtLnInline` + • `GitSignsDeleteVirtLnInline` + +debug_mode *gitsigns-config-debug_mode* + Type: `boolean`, Default: `false` + + Enables debug logging and makes the following functions + available: `dump_cache`, `debug_messages`, `clear_debug`. + + +============================================================================== +HIGHLIGHT GROUPS *gitsigns-highlight-groups* + +These are the highlights groups used by Gitsigns. + +Note if a highlight is not defined, it will be automatically derived by +searching for other defined highlights in order. + + *hl-GitSignsAdd* +GitSignsAdd + Used for the text of 'add' signs. + + Fallbacks: `GitGutterAdd`, `SignifySignAdd`, `DiffAddedGutter`, `Added`, `DiffAdd` + *hl-GitSignsChange* +GitSignsChange + Used for the text of 'change' signs. + + Fallbacks: `GitGutterChange`, `SignifySignChange`, `DiffModifiedGutter`, `Changed`, `DiffChange` + *hl-GitSignsDelete* +GitSignsDelete + Used for the text of 'delete' signs. + + Fallbacks: `GitGutterDelete`, `SignifySignDelete`, `DiffRemovedGutter`, `Removed`, `DiffDelete` + *hl-GitSignsChangedelete* +GitSignsChangedelete + Used for the text of 'changedelete' signs. + + Fallbacks: `GitSignsChange` + *hl-GitSignsTopdelete* +GitSignsTopdelete + Used for the text of 'topdelete' signs. + + Fallbacks: `GitSignsDelete` + *hl-GitSignsUntracked* +GitSignsUntracked + Used for the text of 'untracked' signs. + + Fallbacks: `GitSignsAdd` + *hl-GitSignsAddNr* +GitSignsAddNr + Used for number column (when `config.numhl == true`) of 'add' signs. + + Fallbacks: `GitGutterAddLineNr`, `GitSignsAdd` + *hl-GitSignsChangeNr* +GitSignsChangeNr + Used for number column (when `config.numhl == true`) of 'change' signs. + + Fallbacks: `GitGutterChangeLineNr`, `GitSignsChange` + *hl-GitSignsDeleteNr* +GitSignsDeleteNr + Used for number column (when `config.numhl == true`) of 'delete' signs. + + Fallbacks: `GitGutterDeleteLineNr`, `GitSignsDelete` + *hl-GitSignsChangedeleteNr* +GitSignsChangedeleteNr + Used for number column (when `config.numhl == true`) of 'changedelete' signs. + + Fallbacks: `GitSignsChangeNr` + *hl-GitSignsTopdeleteNr* +GitSignsTopdeleteNr + Used for number column (when `config.numhl == true`) of 'topdelete' signs. + + Fallbacks: `GitSignsDeleteNr` + *hl-GitSignsUntrackedNr* +GitSignsUntrackedNr + Used for number column (when `config.numhl == true`) of 'untracked' signs. + + Fallbacks: `GitSignsAddNr` + *hl-GitSignsAddLn* +GitSignsAddLn + Used for buffer line (when `config.linehl == true`) of 'add' signs. + + Fallbacks: `GitGutterAddLine`, `SignifyLineAdd`, `DiffAdd` + *hl-GitSignsChangeLn* +GitSignsChangeLn + Used for buffer line (when `config.linehl == true`) of 'change' signs. + + Fallbacks: `GitGutterChangeLine`, `SignifyLineChange`, `DiffChange` + *hl-GitSignsChangedeleteLn* +GitSignsChangedeleteLn + Used for buffer line (when `config.linehl == true`) of 'changedelete' signs. + + Fallbacks: `GitSignsChangeLn` + *hl-GitSignsUntrackedLn* +GitSignsUntrackedLn + Used for buffer line (when `config.linehl == true`) of 'untracked' signs. + + Fallbacks: `GitSignsAddLn` + *hl-GitSignsAddPreview* +GitSignsAddPreview + Used for added lines in previews. + + Fallbacks: `GitGutterAddLine`, `SignifyLineAdd`, `DiffAdd` + *hl-GitSignsDeletePreview* +GitSignsDeletePreview + Used for deleted lines in previews. + + Fallbacks: `GitGutterDeleteLine`, `SignifyLineDelete`, `DiffDelete` + *hl-GitSignsCurrentLineBlame* +GitSignsCurrentLineBlame + Used for current line blame. + + Fallbacks: `NonText` + *hl-GitSignsAddInline* +GitSignsAddInline + Used for added word diff regions in inline previews. + + Fallbacks: `TermCursor` + *hl-GitSignsDeleteInline* +GitSignsDeleteInline + Used for deleted word diff regions in inline previews. + + Fallbacks: `TermCursor` + *hl-GitSignsChangeInline* +GitSignsChangeInline + Used for changed word diff regions in inline previews. + + Fallbacks: `TermCursor` + *hl-GitSignsAddLnInline* +GitSignsAddLnInline + Used for added word diff regions when `config.word_diff == true`. + + Fallbacks: `GitSignsAddInline` + *hl-GitSignsChangeLnInline* +GitSignsChangeLnInline + Used for changed word diff regions when `config.word_diff == true`. + + Fallbacks: `GitSignsChangeInline` + *hl-GitSignsDeleteLnInline* +GitSignsDeleteLnInline + Used for deleted word diff regions when `config.word_diff == true`. + + Fallbacks: `GitSignsDeleteInline` + *hl-GitSignsDeleteVirtLn* +GitSignsDeleteVirtLn + Used for deleted lines shown by inline `preview_hunk_inline()` or `show_deleted()`. + + Fallbacks: `GitGutterDeleteLine`, `SignifyLineDelete`, `DiffDelete` + *hl-GitSignsDeleteVirtLnInLine* +GitSignsDeleteVirtLnInLine + Used for word diff regions in lines shown by inline `preview_hunk_inline()` or `show_deleted()`. + + Fallbacks: `GitSignsDeleteLnInline` + *hl-GitSignsVirtLnum* +GitSignsVirtLnum + Used for line numbers in inline hunks previews. + + Fallbacks: `GitSignsDeleteVirtLn` + +============================================================================== +COMMAND *gitsigns-command* + + *:Gitsigns* +:Gitsigns {subcmd} {args} Run a Gitsigns command. {subcmd} can be any + function documented in |gitsigns-functions|. + Each argument in {args} will be attempted to be + parsed as a Lua value using `loadstring`, however + if this fails the argument will remain as the + string given by ||. + + Note this command is equivalent to: >vim + :lua require('gitsigns').{subcmd}({args}) +< + Examples: >vim + :Gitsigns diffthis HEAD~1 + :Gitsigns blame_line + :Gitsigns toggle_signs + :Gitsigns toggle_current_line_blame + :Gitsigns change_base ~ + :Gitsigns reset_buffer + :Gitsigns change_base nil true +< + +============================================================================== +SPECIFYING OBJECTS *gitsigns-object* *gitsigns-revision* + +Gitsigns objects are Git revisions as defined in the "SPECIFYING REVISIONS" +section in the gitrevisions(7) man page. For commands that accept an optional +base, the default is the file in the index. Examples: + +Additionally, Gitsigns also accepts the value `FILE` to specify the working +version of a file. + +Object Meaning ~ +@ Version of file in the commit referenced by @ aka HEAD +main Version of file in the commit referenced by main +main^ Version of file in the parent of the commit referenced by main +main~ " +main~1 " +main...other Version of file in the merge base of main and other +@^ Version of file in the parent of HEAD +@~2 Version of file in the grandparent of HEAD +92eb3dd Version of file in the commit 92eb3dd +:1 The file's common ancestor during a conflict +:2 The alternate file in the target branch during a conflict + +============================================================================== +STATUSLINE *gitsigns-statusline* + + *b:gitsigns_status* *b:gitsigns_status_dict* +The buffer variables `b:gitsigns_status` and `b:gitsigns_status_dict` are +provided. `b:gitsigns_status` is formatted using `config.status_formatter` +. `b:gitsigns_status_dict` is a dictionary with the keys: + + • `added` - Number of added lines. + • `changed` - Number of changed lines. + • `removed` - Number of removed lines. + • `head` - Name of current HEAD (branch or short commit hash). + • `root` - Top level directory of the working tree. + • `gitdir` - .git directory. + +Example: +>vim + set statusline+=%{get(b:,'gitsigns_status','')} +< + *b:gitsigns_head* *g:gitsigns_head* +Use `g:gitsigns_head` and `b:gitsigns_head` to return the name of the current +HEAD (usually branch name). If the current HEAD is detached then this will be +a short commit hash. `g:gitsigns_head` returns the current HEAD for the +current working directory, whereas `b:gitsigns_head` returns the current HEAD +for each buffer. + + *b:gitsigns_blame_line* *b:gitsigns_blame_line_dict* +Provided if |gitsigns-config-current_line_blame| is enabled. +`b:gitsigns_blame_line` if formatted using +`config.current_line_blame_formatter`. `b:gitsigns_blame_line_dict` is a +dictionary containing of the blame object for the current line. For complete +list of keys, see the {blame_info} argument from +|gitsigns-config-current_line_blame_formatter|. + +============================================================================== +TEXT OBJECTS *gitsigns-textobject* + +Since text objects are defined via keymaps, these are exposed and configurable +via the config, see |gitsigns-config-keymaps|. The lua implementation is +exposed through |gitsigns.select_hunk()|. + +============================================================================== +EVENTS *gitsigns-events* + +|User| |autocommands| provided to allow extending behaviors. + +Example: >lua + vim.api.nvim_create_autocmd('User', { + pattern = 'GitSignsUpdate', + callback = function(args) + print(os.time(), ' Gitsigns made an update on ', args.data.buffer) + end + }) +< + *User_GitSignsUpdate* +GitSignsUpdate After Gitsigns updates its knowledge about hunks. + Provides `bufnr` in the autocmd user data. + + *User_GitSignsChanged* +GitSignsChanged After any event in which Gitsigns can potentially change + the repository. Provides `file` in the autocmd user data. + +------------------------------------------------------------------------------ +vim:tw=78:ts=8:ft=help:norl: + diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/doc/tags b/config/neovim/store/lazy-plugins/gitsigns.nvim/doc/tags new file mode 100644 index 00000000..94e17ea6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/doc/tags @@ -0,0 +1,110 @@ +:Gitsigns gitsigns.txt /*:Gitsigns* +User_GitSignsChanged gitsigns.txt /*User_GitSignsChanged* +User_GitSignsUpdate gitsigns.txt /*User_GitSignsUpdate* +b:gitsigns_blame_line gitsigns.txt /*b:gitsigns_blame_line* +b:gitsigns_blame_line_dict gitsigns.txt /*b:gitsigns_blame_line_dict* +b:gitsigns_head gitsigns.txt /*b:gitsigns_head* +b:gitsigns_status gitsigns.txt /*b:gitsigns_status* +b:gitsigns_status_dict gitsigns.txt /*b:gitsigns_status_dict* +g:gitsigns_head gitsigns.txt /*g:gitsigns_head* +gitsigns gitsigns.txt /*gitsigns* +gitsigns-command gitsigns.txt /*gitsigns-command* +gitsigns-config gitsigns.txt /*gitsigns-config* +gitsigns-config-attach_to_untracked gitsigns.txt /*gitsigns-config-attach_to_untracked* +gitsigns-config-auto_attach gitsigns.txt /*gitsigns-config-auto_attach* +gitsigns-config-base gitsigns.txt /*gitsigns-config-base* +gitsigns-config-count_chars gitsigns.txt /*gitsigns-config-count_chars* +gitsigns-config-current_line_blame gitsigns.txt /*gitsigns-config-current_line_blame* +gitsigns-config-current_line_blame_formatter gitsigns.txt /*gitsigns-config-current_line_blame_formatter* +gitsigns-config-current_line_blame_formatter_nc gitsigns.txt /*gitsigns-config-current_line_blame_formatter_nc* +gitsigns-config-current_line_blame_formatter_opts gitsigns.txt /*gitsigns-config-current_line_blame_formatter_opts* +gitsigns-config-current_line_blame_opts gitsigns.txt /*gitsigns-config-current_line_blame_opts* +gitsigns-config-debug_mode gitsigns.txt /*gitsigns-config-debug_mode* +gitsigns-config-diff_opts gitsigns.txt /*gitsigns-config-diff_opts* +gitsigns-config-linehl gitsigns.txt /*gitsigns-config-linehl* +gitsigns-config-max_file_length gitsigns.txt /*gitsigns-config-max_file_length* +gitsigns-config-numhl gitsigns.txt /*gitsigns-config-numhl* +gitsigns-config-on_attach gitsigns.txt /*gitsigns-config-on_attach* +gitsigns-config-preview_config gitsigns.txt /*gitsigns-config-preview_config* +gitsigns-config-show_deleted gitsigns.txt /*gitsigns-config-show_deleted* +gitsigns-config-sign_priority gitsigns.txt /*gitsigns-config-sign_priority* +gitsigns-config-signcolumn gitsigns.txt /*gitsigns-config-signcolumn* +gitsigns-config-signs gitsigns.txt /*gitsigns-config-signs* +gitsigns-config-status_formatter gitsigns.txt /*gitsigns-config-status_formatter* +gitsigns-config-trouble gitsigns.txt /*gitsigns-config-trouble* +gitsigns-config-update_debounce gitsigns.txt /*gitsigns-config-update_debounce* +gitsigns-config-watch_gitdir gitsigns.txt /*gitsigns-config-watch_gitdir* +gitsigns-config-word_diff gitsigns.txt /*gitsigns-config-word_diff* +gitsigns-config-worktrees gitsigns.txt /*gitsigns-config-worktrees* +gitsigns-config-yadm gitsigns.txt /*gitsigns-config-yadm* +gitsigns-events gitsigns.txt /*gitsigns-events* +gitsigns-functions gitsigns.txt /*gitsigns-functions* +gitsigns-highlight-groups gitsigns.txt /*gitsigns-highlight-groups* +gitsigns-mappings gitsigns.txt /*gitsigns-mappings* +gitsigns-object gitsigns.txt /*gitsigns-object* +gitsigns-revision gitsigns.txt /*gitsigns-revision* +gitsigns-statusline gitsigns.txt /*gitsigns-statusline* +gitsigns-textobject gitsigns.txt /*gitsigns-textobject* +gitsigns-usage gitsigns.txt /*gitsigns-usage* +gitsigns.attach() gitsigns.txt /*gitsigns.attach()* +gitsigns.blame_line() gitsigns.txt /*gitsigns.blame_line()* +gitsigns.change_base() gitsigns.txt /*gitsigns.change_base()* +gitsigns.detach() gitsigns.txt /*gitsigns.detach()* +gitsigns.detach_all() gitsigns.txt /*gitsigns.detach_all()* +gitsigns.diffthis() gitsigns.txt /*gitsigns.diffthis()* +gitsigns.get_actions() gitsigns.txt /*gitsigns.get_actions()* +gitsigns.get_hunks() gitsigns.txt /*gitsigns.get_hunks()* +gitsigns.nav_hunk() gitsigns.txt /*gitsigns.nav_hunk()* +gitsigns.next_hunk() gitsigns.txt /*gitsigns.next_hunk()* +gitsigns.nvim gitsigns.txt /*gitsigns.nvim* +gitsigns.prev_hunk() gitsigns.txt /*gitsigns.prev_hunk()* +gitsigns.preview_hunk() gitsigns.txt /*gitsigns.preview_hunk()* +gitsigns.preview_hunk_inline() gitsigns.txt /*gitsigns.preview_hunk_inline()* +gitsigns.refresh() gitsigns.txt /*gitsigns.refresh()* +gitsigns.reset_base() gitsigns.txt /*gitsigns.reset_base()* +gitsigns.reset_buffer() gitsigns.txt /*gitsigns.reset_buffer()* +gitsigns.reset_buffer_index() gitsigns.txt /*gitsigns.reset_buffer_index()* +gitsigns.reset_hunk() gitsigns.txt /*gitsigns.reset_hunk()* +gitsigns.select_hunk() gitsigns.txt /*gitsigns.select_hunk()* +gitsigns.setloclist() gitsigns.txt /*gitsigns.setloclist()* +gitsigns.setqflist() gitsigns.txt /*gitsigns.setqflist()* +gitsigns.setup() gitsigns.txt /*gitsigns.setup()* +gitsigns.show() gitsigns.txt /*gitsigns.show()* +gitsigns.stage_buffer() gitsigns.txt /*gitsigns.stage_buffer()* +gitsigns.stage_hunk() gitsigns.txt /*gitsigns.stage_hunk()* +gitsigns.toggle_current_line_blame() gitsigns.txt /*gitsigns.toggle_current_line_blame()* +gitsigns.toggle_deleted() gitsigns.txt /*gitsigns.toggle_deleted()* +gitsigns.toggle_linehl() gitsigns.txt /*gitsigns.toggle_linehl()* +gitsigns.toggle_numhl() gitsigns.txt /*gitsigns.toggle_numhl()* +gitsigns.toggle_signs() gitsigns.txt /*gitsigns.toggle_signs()* +gitsigns.toggle_word_diff() gitsigns.txt /*gitsigns.toggle_word_diff()* +gitsigns.txt gitsigns.txt /*gitsigns.txt* +gitsigns.undo_stage_hunk() gitsigns.txt /*gitsigns.undo_stage_hunk()* +hl-GitSignsAdd gitsigns.txt /*hl-GitSignsAdd* +hl-GitSignsAddInline gitsigns.txt /*hl-GitSignsAddInline* +hl-GitSignsAddLn gitsigns.txt /*hl-GitSignsAddLn* +hl-GitSignsAddLnInline gitsigns.txt /*hl-GitSignsAddLnInline* +hl-GitSignsAddNr gitsigns.txt /*hl-GitSignsAddNr* +hl-GitSignsAddPreview gitsigns.txt /*hl-GitSignsAddPreview* +hl-GitSignsChange gitsigns.txt /*hl-GitSignsChange* +hl-GitSignsChangeInline gitsigns.txt /*hl-GitSignsChangeInline* +hl-GitSignsChangeLn gitsigns.txt /*hl-GitSignsChangeLn* +hl-GitSignsChangeLnInline gitsigns.txt /*hl-GitSignsChangeLnInline* +hl-GitSignsChangeNr gitsigns.txt /*hl-GitSignsChangeNr* +hl-GitSignsChangedelete gitsigns.txt /*hl-GitSignsChangedelete* +hl-GitSignsChangedeleteLn gitsigns.txt /*hl-GitSignsChangedeleteLn* +hl-GitSignsChangedeleteNr gitsigns.txt /*hl-GitSignsChangedeleteNr* +hl-GitSignsCurrentLineBlame gitsigns.txt /*hl-GitSignsCurrentLineBlame* +hl-GitSignsDelete gitsigns.txt /*hl-GitSignsDelete* +hl-GitSignsDeleteInline gitsigns.txt /*hl-GitSignsDeleteInline* +hl-GitSignsDeleteLnInline gitsigns.txt /*hl-GitSignsDeleteLnInline* +hl-GitSignsDeleteNr gitsigns.txt /*hl-GitSignsDeleteNr* +hl-GitSignsDeletePreview gitsigns.txt /*hl-GitSignsDeletePreview* +hl-GitSignsDeleteVirtLn gitsigns.txt /*hl-GitSignsDeleteVirtLn* +hl-GitSignsDeleteVirtLnInLine gitsigns.txt /*hl-GitSignsDeleteVirtLnInLine* +hl-GitSignsTopdelete gitsigns.txt /*hl-GitSignsTopdelete* +hl-GitSignsTopdeleteNr gitsigns.txt /*hl-GitSignsTopdeleteNr* +hl-GitSignsUntracked gitsigns.txt /*hl-GitSignsUntracked* +hl-GitSignsUntrackedLn gitsigns.txt /*hl-GitSignsUntrackedLn* +hl-GitSignsUntrackedNr gitsigns.txt /*hl-GitSignsUntrackedNr* +hl-GitSignsVirtLnum gitsigns.txt /*hl-GitSignsVirtLnum* diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/gitsigns.nvim/scm-1/doc/gitsigns.txt b/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/gitsigns.nvim/scm-1/doc/gitsigns.txt new file mode 100644 index 00000000..4393273a --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/gitsigns.nvim/scm-1/doc/gitsigns.txt @@ -0,0 +1,1206 @@ +*gitsigns.txt* Gitsigns +*gitsigns.nvim* + +Author: Lewis Russell +Version: v0.8.1 +Homepage: +License: MIT license + +============================================================================== +INTRODUCTION *gitsigns* + +Gitsigns is a plugin for Neovim that provides integration with Git via a +feature set which includes (but not limited to): + • Provides signs in the |signcolumn| to show changed/added/removed lines. + • Mappings to operate on hunks to stage, undo or reset against Git's index. + +Gitsigns is implemented entirely in Lua which is built into Neovim and +requires no external dependencies other than git. This is unlike other plugins +that require python, node, etc, which need to communicate with Neovim using +|RPC|. By default, Gitsigns also uses Neovim's built-in diff library +(`vim.diff`) unlike other similar plugins that need to run `git-diff` as an +external process which is less efficient, has tighter bottlenecks and requires +file IO. + +============================================================================== +USAGE *gitsigns-usage* + +For basic setup with all batteries included: +>lua + require('gitsigns').setup() +< + +Configuration can be passed to the setup function. Here is an example with most +of the default settings: +>lua + require('gitsigns').setup { + signs = { + add = { text = '┃' }, + change = { text = '┃' }, + delete = { text = '_' }, + topdelete = { text = '‾' }, + changedelete = { text = '~' }, + untracked = { text = '┆' }, + }, + signcolumn = true, -- Toggle with `:Gitsigns toggle_signs` + numhl = false, -- Toggle with `:Gitsigns toggle_numhl` + linehl = false, -- Toggle with `:Gitsigns toggle_linehl` + word_diff = false, -- Toggle with `:Gitsigns toggle_word_diff` + watch_gitdir = { + follow_files = true + }, + auto_attach = true, + attach_to_untracked = false, + current_line_blame = false, -- Toggle with `:Gitsigns toggle_current_line_blame` + current_line_blame_opts = { + virt_text = true, + virt_text_pos = 'eol', -- 'eol' | 'overlay' | 'right_align' + delay = 1000, + ignore_whitespace = false, + virt_text_priority = 100, + }, + current_line_blame_formatter = ', - ', + current_line_blame_formatter_opts = { + relative_time = false, + }, + sign_priority = 6, + update_debounce = 100, + status_formatter = nil, -- Use default + max_file_length = 40000, -- Disable if file is longer than this (in lines) + preview_config = { + -- Options passed to nvim_open_win + border = 'single', + style = 'minimal', + relative = 'cursor', + row = 0, + col = 1 + }, + } +< + +============================================================================== +MAPPINGS *gitsigns-mappings* + +Custom mappings can be defined in the `on_attach` callback in the config table +passed to |gitsigns-setup()|. See |gitsigns-config-on_attach|. + +Most actions can be repeated with `.` if you have |vim-repeat| installed. + +============================================================================== +FUNCTIONS *gitsigns-functions* + +Note functions with the {async} attribute are run asynchronously and accept +an optional {callback} argument. + + +setup({cfg}, {callback?}) *gitsigns.setup()* + Setup and start Gitsigns. + + Attributes: ~ + {async} + + Parameters: ~ + {cfg} (table|nil): Configuration for Gitsigns. + See |gitsigns-usage| for more details. + +attach({bufnr}, {ctx}, {callback?}) *gitsigns.attach()* + Attach Gitsigns to the buffer. + + Attributes: ~ + {async} + + Parameters: ~ + {bufnr} (integer): Buffer number + {ctx} (table|nil): + Git context data that may optionally be used to attach to any + buffer that represents a real git object. + • {file}: (string) + Path to the file represented by the buffer, relative to the + top-level. + • {toplevel}: (string?) + Path to the top-level of the parent git repository. + • {gitdir}: (string?) + Path to the git directory of the parent git repository + (typically the ".git/" directory). + • {commit}: (string?) + The git revision that the file belongs to. + • {base}: (string?) + The git revision that the file should be compared to. + +detach({bufnr}) *gitsigns.detach()* + Detach Gitsigns from the buffer {bufnr}. If {bufnr} is not + provided then the current buffer is used. + + Parameters: ~ + {bufnr} (integer): Buffer number + +detach_all() *gitsigns.detach_all()* + Detach Gitsigns from all buffers it is attached to. + +refresh() *gitsigns.refresh()* + Refresh all buffers. + + Attributes: ~ + {async} + +get_actions() *gitsigns.get_actions()* + Get all the available line specific actions for the current + buffer at the cursor position. + + + Returns: ~ + (table|nil): Dictionary of action name to function which when called + performs action. + +setloclist({nr}, {target}) *gitsigns.setloclist()* + Populate the location list with hunks. Automatically opens the + location list window. + + Alias for: `setqflist({target}, { use_location_list = true, nr = {nr} }` + + Attributes: ~ + {async} + + Parameters: ~ + {nr?} (integer): Window number or the |window-ID|. + `0` for the current window (default). + {target} (integer|string): See |gitsigns.setqflist()|. + +setqflist({target}, {opts}, {callback?}) *gitsigns.setqflist()* + Populate the quickfix list with hunks. Automatically opens the + quickfix window. + + Attributes: ~ + {async} + + Parameters: ~ + {target} (integer|string): + Specifies which files hunks are collected from. + Possible values. + • [integer]: The buffer with the matching buffer + number. `0` for current buffer (default). + • `"attached"`: All attached buffers. + • `"all"`: All modified files for each git + directory of all attached buffers in addition + to the current working directory. + {opts} (table|nil): Additional options: + • {use_location_list}: (boolean) + Populate the location list instead of the + quickfix list. Default to `false`. + • {nr}: (integer) + Window number or ID when using location list. + Expand folds when navigating to a hunk which is + inside a fold. Defaults to `0`. + • {open}: (boolean) + Open the quickfix/location list viewer. + Defaults to `true`. + +show({revision}) *gitsigns.show()* + Show revision {base} of the current file, if it is given, or + with the currently set base (index by default). + + If {base} is the index, then the opened buffer is editable and + any written changes will update the index accordingly. + + Examples: >vim + " View the index version of the file + :Gitsigns show + + " View revision of file in the last commit + :Gitsigns show ~1 +< + + For a more complete list of ways to specify bases, see + |gitsigns-revision|. + + Attributes: ~ + {async} + +diffthis({base}, {opts}) *gitsigns.diffthis()* + Perform a |vimdiff| on the given file with {base} if it is + given, or with the currently set base (index by default). + + If {base} is the index, then the opened buffer is editable and + any written changes will update the index accordingly. + + Examples: >vim + " Diff against the index + :Gitsigns diffthis + + " Diff against the last commit + :Gitsigns diffthis ~1 +< + + For a more complete list of ways to specify bases, see + |gitsigns-revision|. + + Attributes: ~ + {async} + + Parameters: ~ + {base} (string|nil): Revision to diff against. Defaults to index. + {opts} (table|nil): Additional options: + • {vertical}: {boolean}. Split window vertically. Defaults to + config.diff_opts.vertical. If running via command line, then + this is taken from the command modifiers. + • {split}: {string}. One of: 'aboveleft', 'belowright', + 'botright', 'rightbelow', 'leftabove', 'topleft'. Defaults to + 'aboveleft'. If running via command line, then this is taken + from the command modifiers. + +reset_base({global}) *gitsigns.reset_base()* + Reset the base revision to diff against back to the + index. + + Alias for `change_base(nil, {global})` . + +change_base({base}, {global}, {callback?}) *gitsigns.change_base()* + Change the base revision to diff against. If {base} is not + given, then the original base is used. If {global} is given + and true, then change the base revision of all buffers, + including any new buffers. + + Attributes: ~ + {async} + + Examples: >vim + " Change base to 1 commit behind head + :lua require('gitsigns').change_base('HEAD~1') + + " Also works using the Gitsigns command + :Gitsigns change_base HEAD~1 + + " Other variations + :Gitsigns change_base ~1 + :Gitsigns change_base ~ + :Gitsigns change_base ^ + + " Commits work too + :Gitsigns change_base 92eb3dd + + " Revert to original base + :Gitsigns change_base +< + + For a more complete list of ways to specify bases, see + |gitsigns-revision|. + + Parameters: ~ + {base} (string|nil): The object/revision to diff against. + {global} (boolean|nil): Change the base of all buffers. + +blame_line({opts}, {callback?}) *gitsigns.blame_line()* + Run git blame on the current line and show the results in a + floating window. If already open, calling this will cause the + window to get focus. + + Attributes: ~ + {async} + + Parameters: ~ + {opts} (table|nil): Additional options: + • {full}: (boolean) + Display full commit message with hunk. + • {ignore_whitespace}: (boolean) + Ignore whitespace when running blame. + • {rev}: (string) + Revision to blame against. + • {extra_opts}: (string[]) + Extra options passed to `git-blame`. + +get_hunks({bufnr}) *gitsigns.get_hunks()* + Get hunk array for specified buffer. + + Parameters: ~ + {bufnr} (integer): Buffer number, if not provided (or 0) + will use current buffer. + + Returns: ~ + (table|nil): Array of hunk objects. + Each hunk object has keys: + • `"type"`: String with possible values: "add", "change", + "delete" + • `"head"`: Header that appears in the unified diff + output. + • `"lines"`: Line contents of the hunks prefixed with + either `"-"` or `"+"`. + • `"removed"`: Sub-table with fields: + • `"start"`: Line number (1-based) + • `"count"`: Line count + • `"added"`: Sub-table with fields: + • `"start"`: Line number (1-based) + • `"count"`: Line count + +select_hunk() *gitsigns.select_hunk()* + Select the hunk under the cursor. + +preview_hunk_inline() *gitsigns.preview_hunk_inline()* + Preview the hunk at the cursor position inline in the buffer. + +preview_hunk() *gitsigns.preview_hunk()* + Preview the hunk at the cursor position in a floating + window. If the preview is already open, calling this + will cause the window to get focus. + +prev_hunk({opts}, {callback?}) *gitsigns.prev_hunk()* + DEPRECATED: use |gitsigns.nav_hunk()| + + Jump to the previous hunk in the current buffer. If a hunk preview + (popup or inline) was previously opened, it will be re-opened + at the previous hunk. + + Attributes: ~ + {async} + + Parameters: ~ + See |gitsigns.nav_hunk()|. + +next_hunk({opts}, {callback?}) *gitsigns.next_hunk()* + DEPRECATED: use |gitsigns.nav_hunk()| + + Jump to the next hunk in the current buffer. If a hunk preview + (popup or inline) was previously opened, it will be re-opened + at the next hunk. + + Attributes: ~ + {async} + + Parameters: ~ + See |gitsigns.nav_hunk()|. + +nav_hunk({direction}, {opts}, {callback?}) *gitsigns.nav_hunk()* + Jump to hunk in the current buffer. If a hunk preview + (popup or inline) was previously opened, it will be re-opened + at the next hunk. + + Attributes: ~ + {async} + + Parameters: ~ + {direction} ('first'|'last'|'next'|'prev'): + {opts} (table|nil): Configuration table. Keys: + • {wrap}: (boolean) + Whether to loop around file or not. Defaults + to the value 'wrapscan' + • {navigation_message}: (boolean) + Whether to show navigation messages or not. + Looks at 'shortmess' for default behaviour. + • {foldopen}: (boolean) + Expand folds when navigating to a hunk which is + inside a fold. Defaults to `true` if 'foldopen' + contains `search`. + • {preview}: (boolean) + Automatically open preview_hunk() upon navigating + to a hunk. + • {greedy}: (boolean) + Only navigate between non-contiguous hunks. Only useful if + 'diff_opts' contains `linematch`. Defaults to `true`. + • {count}: (integer) + Number of times to advance. Defaults to |v:count1|. + +reset_buffer_index() *gitsigns.reset_buffer_index()* + Unstage all hunks for current buffer in the index. Note: + Unlike |gitsigns.undo_stage_hunk()| this doesn't simply undo + stages, this runs an `git reset` on current buffers file. + + Attributes: ~ + {async} + +stage_buffer() *gitsigns.stage_buffer()* + Stage all hunks in current buffer. + + Attributes: ~ + {async} + +undo_stage_hunk() *gitsigns.undo_stage_hunk()* + Undo the last call of stage_hunk(). + + Note: only the calls to stage_hunk() performed in the current + session can be undone. + + Attributes: ~ + {async} + +reset_buffer() *gitsigns.reset_buffer()* + Reset the lines of all hunks in the buffer. + +reset_hunk({range}, {opts}, {callback?}) *gitsigns.reset_hunk()* + Reset the lines of the hunk at the cursor position, or all + lines in the given range. If {range} is provided, all lines in + the given range are reset. This supports partial-hunks, + meaning if a range only includes a portion of a particular + hunk, only the lines within the range will be reset. + + Parameters: ~ + {range} (table|nil): List-like table of two integers making + up the line range from which you want to reset the hunks. + If running via command line, then this is taken from the + command modifiers. + {opts} (table|nil): Additional options: + • {greedy}: (boolean) + Stage all contiguous hunks. Only useful if 'diff_opts' + contains `linematch`. Defaults to `true`. + +stage_hunk({range}, {opts}, {callback?}) *gitsigns.stage_hunk()* + Stage the hunk at the cursor position, or all lines in the + given range. If {range} is provided, all lines in the given + range are staged. This supports partial-hunks, meaning if a + range only includes a portion of a particular hunk, only the + lines within the range will be staged. + + Attributes: ~ + {async} + + Parameters: ~ + {range} (table|nil): List-like table of two integers making + up the line range from which you want to stage the hunks. + If running via command line, then this is taken from the + command modifiers. + {opts} (table|nil): Additional options: + • {greedy}: (boolean) + Stage all contiguous hunks. Only useful if 'diff_opts' + contains `linematch`. Defaults to `true`. + +toggle_deleted({value}) *gitsigns.toggle_deleted()* + Toggle |gitsigns-config-show_deleted| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + Returns: ~ + (boolean): Current value of |gitsigns-config-show_deleted| + +toggle_current_line_blame({value}) *gitsigns.toggle_current_line_blame()* + Toggle |gitsigns-config-current_line_blame| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + Returns: ~ + (boolean): Current value of |gitsigns-config-current_line_blame| + +toggle_word_diff({value}) *gitsigns.toggle_word_diff()* + Toggle |gitsigns-config-word_diff| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + Returns: ~ + (boolean): Current value of |gitsigns-config-word_diff| + +toggle_linehl({value}) *gitsigns.toggle_linehl()* + Toggle |gitsigns-config-linehl| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + Returns: ~ + (boolean): Current value of |gitsigns-config-linehl| + +toggle_numhl({value}) *gitsigns.toggle_numhl()* + Toggle |gitsigns-config-numhl| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + + Returns: ~ + (boolean): Current value of |gitsigns-config-numhl| + +toggle_signs({value}) *gitsigns.toggle_signs()* + Toggle |gitsigns-config-signbooleancolumn| + + Parameters: ~ + {value} (boolean|nil): Value to set toggle. If `nil` + the toggle value is inverted. + + Returns: ~ + (boolean): Current value of |gitsigns-config-signcolumn| + + +============================================================================== +CONFIGURATION *gitsigns-config* + +This section describes the configuration fields which can be passed to +|gitsigns.setup()|. Note fields of type `table` may be marked with extended +meaning the field is merged with the default, with the user value given higher +precedence. This allows only specific sub-fields to be configured without +having to redefine the whole field. + +signs *gitsigns-config-signs* + Type: `table[extended]` + Default: > + { + add = { text = '┃' }, + change = { text = '┃' }, + delete = { text = '▁' }, + topdelete = { text = '▔' }, + changedelete = { text = '~' }, + untracked = { text = '┆' }, + } +< + Configuration for signs: + • `text` specifies the character to use for the sign. + • `show_count` to enable showing count of hunk, e.g. number of deleted + lines. + + The highlights `GitSigns[kind][type]` is used for each kind of sign. E.g. + 'add' signs uses the highlights: + • `GitSignsAdd` (for normal text signs) + • `GitSignsAddNr` (for signs when `config.numhl == true`) + • `GitSignsAddLn `(for signs when `config.linehl == true`) + + See |gitsigns-highlight-groups|. + +worktrees *gitsigns-config-worktrees* + Type: `table`, Default: `nil` + + Detached working trees. + + Array of tables with the keys `gitdir` and `toplevel`. + + If normal attaching fails, then each entry in the table is attempted + with the work tree details set. + + Example: >lua + worktrees = { + { + toplevel = vim.env.HOME, + gitdir = vim.env.HOME .. '/projects/dotfiles/.git' + } + } + +on_attach *gitsigns-config-on_attach* + Type: `function`, Default: `nil` + + Callback called when attaching to a buffer. Mainly used to setup keymaps. + The buffer number is passed as the first argument. + + This callback can return `false` to prevent attaching to the buffer. + + Example: >lua + on_attach = function(bufnr) + if vim.api.nvim_buf_get_name(bufnr):match() then + -- Don't attach to specific buffers whose name matches a pattern + return false + end + + -- Setup keymaps + vim.api.nvim_buf_set_keymap(bufnr, 'n', 'hs', 'lua require"gitsigns".stage_hunk()', {}) + ... -- More keymaps + end +< + +watch_gitdir *gitsigns-config-watch_gitdir* + Type: `table[extended]` + Default: > + `{ + enable = true, + follow_files = true + }` +< + When opening a file, a libuv watcher is placed on the respective + `.git` directory to detect when changes happen to use as a trigger to + update signs. + + Fields: ~ + • `enable`: + Whether the watcher is enabled. + + • `follow_files`: + If a file is moved with `git mv`, switch the buffer to the new location. + +sign_priority *gitsigns-config-sign_priority* + Type: `number`, Default: `6` + + Priority to use for signs. + +signcolumn *gitsigns-config-signcolumn* + Type: `boolean`, Default: `true` + + Enable/disable symbols in the sign column. + + When enabled the highlights defined in `signs.*.hl` and symbols defined + in `signs.*.text` are used. + +numhl *gitsigns-config-numhl* + Type: `boolean`, Default: `false` + + Enable/disable line number highlights. + + When enabled the highlights defined in `signs.*.numhl` are used. If + the highlight group does not exist, then it is automatically defined + and linked to the corresponding highlight group in `signs.*.hl`. + +linehl *gitsigns-config-linehl* + Type: `boolean`, Default: `false` + + Enable/disable line highlights. + + When enabled the highlights defined in `signs.*.linehl` are used. If + the highlight group does not exist, then it is automatically defined + and linked to the corresponding highlight group in `signs.*.hl`. + +show_deleted *gitsigns-config-show_deleted* + Type: `boolean`, Default: `false` + + Show the old version of hunks inline in the buffer (via virtual lines). + + Note: Virtual lines currently use the highlight `GitSignsDeleteVirtLn`. + +diff_opts *gitsigns-config-diff_opts* + Type: `table[extended]`, Default: derived from 'diffopt' + + Diff options. If the default value is used, then changes to 'diffopt' are + automatically applied. + + Fields: ~ + • algorithm: string + Diff algorithm to use. Values: + • "myers" the default algorithm + • "minimal" spend extra time to generate the + smallest possible diff + • "patience" patience diff algorithm + • "histogram" histogram diff algorithm + • internal: boolean + Use Neovim's built in xdiff library for running diffs. + • indent_heuristic: boolean + Use the indent heuristic for the internal + diff library. + • vertical: boolean + Start diff mode with vertical splits. + • linematch: integer + Enable second-stage diff on hunks to align lines. + Requires `internal=true`. + • ignore_blank_lines: boolean + Ignore changes where lines are blank. + • ignore_whitespace_change: boolean + Ignore changes in amount of white space. + It should ignore adding trailing white space, + but not leading white space. + • ignore_whitespace: boolean + Ignore all white space changes. + • ignore_whitespace_change_at_eol: boolean + Ignore white space changes at end of line. + +base *gitsigns-config-base* + Type: `string`, Default: index + + The object/revision to diff against. + See |gitsigns-revision|. + +count_chars *gitsigns-config-count_chars* + Type: `table` + Default: > + `{ "1", "2", "3", "4", "5", "6", "7", "8", "9", + ["+"] = ">" + }` +< + The count characters used when `signs.*.show_count` is enabled. The + `+` entry is used as a fallback. With the default, any count outside + of 1-9 uses the `>` character in the sign. + + Possible use cases for this field: + • to specify unicode characters for the counts instead of 1-9. + • to define characters to be used for counts greater than 9. + +status_formatter *gitsigns-config-status_formatter* + Type: `function` + Default: > + function(status) + local added, changed, removed = status.added, status.changed, status.removed + local status_txt = {} + if added and added > 0 then table.insert(status_txt, '+'..added ) end + if changed and changed > 0 then table.insert(status_txt, '~'..changed) end + if removed and removed > 0 then table.insert(status_txt, '-'..removed) end + return table.concat(status_txt, ' ') + end +< + Function used to format `b:gitsigns_status`. + +max_file_length *gitsigns-config-max_file_length* + Type: `number`, Default: `40000` + + Max file length (in lines) to attach to. + +preview_config *gitsigns-config-preview_config* + Type: `table[extended]` + Default: > + `{ + border = "single", + col = 1, + relative = "cursor", + row = 0, + style = "minimal" + }` +< + Option overrides for the Gitsigns preview window. Table is passed directly + to `nvim_open_win`. + +auto_attach *gitsigns-config-auto_attach* + Type: `boolean`, Default: `true` + + Automatically attach to files. + +attach_to_untracked *gitsigns-config-attach_to_untracked* + Type: `boolean`, Default: `false` + + Attach to untracked files. + +update_debounce *gitsigns-config-update_debounce* + Type: `number`, Default: `100` + + Debounce time for updates (in milliseconds). + +current_line_blame *gitsigns-config-current_line_blame* + Type: `boolean`, Default: `false` + + Adds an unobtrusive and customisable blame annotation at the end of + the current line. + + The highlight group used for the text is `GitSignsCurrentLineBlame`. + +current_line_blame_opts *gitsigns-config-current_line_blame_opts* + Type: `table[extended]` + Default: > + `{ + delay = 1000, + virt_text = true, + virt_text_pos = "eol", + virt_text_priority = 100 + }` +< + Options for the current line blame annotation. + + Fields: ~ + • virt_text: boolean + Whether to show a virtual text blame annotation. + • virt_text_pos: string + Blame annotation position. Available values: + `eol` Right after eol character. + `overlay` Display over the specified column, without + shifting the underlying text. + `right_align` Display right aligned in the window. + • delay: integer + Sets the delay (in milliseconds) before blame virtual text is + displayed. + • ignore_whitespace: boolean + Ignore whitespace when running blame. + • virt_text_priority: integer + Priority of virtual text. + • extra_opts: string[] + Extra options passed to `git-blame`. + +current_line_blame_formatter_opts + *gitsigns-config-current_line_blame_formatter_opts* + DEPRECATED + + Type: `table[extended]` + Default: > + `{ + relative_time = false + }` +< + Options for the current line blame annotation formatter. + + Fields: ~ + • relative_time: boolean + +current_line_blame_formatter *gitsigns-config-current_line_blame_formatter* + Type: `string|function`, Default: `" , - "` + + String or function used to format the virtual text of + |gitsigns-config-current_line_blame|. + + When a string, accepts the following format specifiers: + + • `` + • `` + • `` + • `` + • `` + • `` or `` + • `` + • `` + • `` + • `` or `` + • `` + • `` + • `` + • `` + + For `` and ``, `FORMAT` can + be any valid date format that is accepted by `os.date()` with the + addition of `%R` (defaults to `%Y-%m-%d`): + + • `%a` abbreviated weekday name (e.g., Wed) + • `%A` full weekday name (e.g., Wednesday) + • `%b` abbreviated month name (e.g., Sep) + • `%B` full month name (e.g., September) + • `%c` date and time (e.g., 09/16/98 23:48:10) + • `%d` day of the month (16) [01-31] + • `%H` hour, using a 24-hour clock (23) [00-23] + • `%I` hour, using a 12-hour clock (11) [01-12] + • `%M` minute (48) [00-59] + • `%m` month (09) [01-12] + • `%p` either "am" or "pm" (pm) + • `%S` second (10) [00-61] + • `%w` weekday (3) [0-6 = Sunday-Saturday] + • `%x` date (e.g., 09/16/98) + • `%X` time (e.g., 23:48:10) + • `%Y` full year (1998) + • `%y` two-digit year (98) [00-99] + • `%%` the character `%´ + • `%R` relative (e.g., 4 months ago) + + When a function: + Parameters: ~ + {name} Git user name returned from `git config user.name` . + {blame_info} Table with the following keys: + • `abbrev_sha`: string + • `orig_lnum`: integer + • `final_lnum`: integer + • `author`: string + • `author_mail`: string + • `author_time`: integer + • `author_tz`: string + • `committer`: string + • `committer_mail`: string + • `committer_time`: integer + • `committer_tz`: string + • `summary`: string + • `previous`: string + • `filename`: string + • `boundary`: true? + + Note that the keys map onto the output of: + `git blame --line-porcelain` + + {opts} Passed directly from + |gitsigns-config-current_line_blame_formatter_opts|. + + Return: ~ + The result of this function is passed directly to the `opts.virt_text` + field of |nvim_buf_set_extmark| and thus must be a list of + [text, highlight] tuples. + +current_line_blame_formatter_nc + *gitsigns-config-current_line_blame_formatter_nc* + Type: `string|function`, Default: `" "` + + String or function used to format the virtual text of + |gitsigns-config-current_line_blame| for lines that aren't committed. + + See |gitsigns-config-current_line_blame_formatter| for more information. + +trouble *gitsigns-config-trouble* + Type: `boolean`, Default: true if installed + + When using setqflist() or setloclist(), open Trouble instead of the + quickfix/location list window. + +yadm *gitsigns-config-yadm* + DEPRECATED + Please use |gitsigns-config-on_attach_pre| instead + + Type: `table` + Default: > + `{ + enable = false + }` +< + yadm configuration. + +word_diff *gitsigns-config-word_diff* + Type: `boolean`, Default: `false` + + Highlight intra-line word differences in the buffer. + Requires `config.diff_opts.internal = true` . + + Uses the highlights: + • For word diff in previews: + • `GitSignsAddInline` + • `GitSignsChangeInline` + • `GitSignsDeleteInline` + • For word diff in buffer: + • `GitSignsAddLnInline` + • `GitSignsChangeLnInline` + • `GitSignsDeleteLnInline` + • For word diff in virtual lines (e.g. show_deleted): + • `GitSignsAddVirtLnInline` + • `GitSignsChangeVirtLnInline` + • `GitSignsDeleteVirtLnInline` + +debug_mode *gitsigns-config-debug_mode* + Type: `boolean`, Default: `false` + + Enables debug logging and makes the following functions + available: `dump_cache`, `debug_messages`, `clear_debug`. + + +============================================================================== +HIGHLIGHT GROUPS *gitsigns-highlight-groups* + +These are the highlights groups used by Gitsigns. + +Note if a highlight is not defined, it will be automatically derived by +searching for other defined highlights in order. + + *hl-GitSignsAdd* +GitSignsAdd + Used for the text of 'add' signs. + + Fallbacks: `GitGutterAdd`, `SignifySignAdd`, `DiffAddedGutter`, `Added`, `DiffAdd` + *hl-GitSignsChange* +GitSignsChange + Used for the text of 'change' signs. + + Fallbacks: `GitGutterChange`, `SignifySignChange`, `DiffModifiedGutter`, `Changed`, `DiffChange` + *hl-GitSignsDelete* +GitSignsDelete + Used for the text of 'delete' signs. + + Fallbacks: `GitGutterDelete`, `SignifySignDelete`, `DiffRemovedGutter`, `Removed`, `DiffDelete` + *hl-GitSignsChangedelete* +GitSignsChangedelete + Used for the text of 'changedelete' signs. + + Fallbacks: `GitSignsChange` + *hl-GitSignsTopdelete* +GitSignsTopdelete + Used for the text of 'topdelete' signs. + + Fallbacks: `GitSignsDelete` + *hl-GitSignsUntracked* +GitSignsUntracked + Used for the text of 'untracked' signs. + + Fallbacks: `GitSignsAdd` + *hl-GitSignsAddNr* +GitSignsAddNr + Used for number column (when `config.numhl == true`) of 'add' signs. + + Fallbacks: `GitGutterAddLineNr`, `GitSignsAdd` + *hl-GitSignsChangeNr* +GitSignsChangeNr + Used for number column (when `config.numhl == true`) of 'change' signs. + + Fallbacks: `GitGutterChangeLineNr`, `GitSignsChange` + *hl-GitSignsDeleteNr* +GitSignsDeleteNr + Used for number column (when `config.numhl == true`) of 'delete' signs. + + Fallbacks: `GitGutterDeleteLineNr`, `GitSignsDelete` + *hl-GitSignsChangedeleteNr* +GitSignsChangedeleteNr + Used for number column (when `config.numhl == true`) of 'changedelete' signs. + + Fallbacks: `GitSignsChangeNr` + *hl-GitSignsTopdeleteNr* +GitSignsTopdeleteNr + Used for number column (when `config.numhl == true`) of 'topdelete' signs. + + Fallbacks: `GitSignsDeleteNr` + *hl-GitSignsUntrackedNr* +GitSignsUntrackedNr + Used for number column (when `config.numhl == true`) of 'untracked' signs. + + Fallbacks: `GitSignsAddNr` + *hl-GitSignsAddLn* +GitSignsAddLn + Used for buffer line (when `config.linehl == true`) of 'add' signs. + + Fallbacks: `GitGutterAddLine`, `SignifyLineAdd`, `DiffAdd` + *hl-GitSignsChangeLn* +GitSignsChangeLn + Used for buffer line (when `config.linehl == true`) of 'change' signs. + + Fallbacks: `GitGutterChangeLine`, `SignifyLineChange`, `DiffChange` + *hl-GitSignsChangedeleteLn* +GitSignsChangedeleteLn + Used for buffer line (when `config.linehl == true`) of 'changedelete' signs. + + Fallbacks: `GitSignsChangeLn` + *hl-GitSignsUntrackedLn* +GitSignsUntrackedLn + Used for buffer line (when `config.linehl == true`) of 'untracked' signs. + + Fallbacks: `GitSignsAddLn` + *hl-GitSignsAddPreview* +GitSignsAddPreview + Used for added lines in previews. + + Fallbacks: `GitGutterAddLine`, `SignifyLineAdd`, `DiffAdd` + *hl-GitSignsDeletePreview* +GitSignsDeletePreview + Used for deleted lines in previews. + + Fallbacks: `GitGutterDeleteLine`, `SignifyLineDelete`, `DiffDelete` + *hl-GitSignsCurrentLineBlame* +GitSignsCurrentLineBlame + Used for current line blame. + + Fallbacks: `NonText` + *hl-GitSignsAddInline* +GitSignsAddInline + Used for added word diff regions in inline previews. + + Fallbacks: `TermCursor` + *hl-GitSignsDeleteInline* +GitSignsDeleteInline + Used for deleted word diff regions in inline previews. + + Fallbacks: `TermCursor` + *hl-GitSignsChangeInline* +GitSignsChangeInline + Used for changed word diff regions in inline previews. + + Fallbacks: `TermCursor` + *hl-GitSignsAddLnInline* +GitSignsAddLnInline + Used for added word diff regions when `config.word_diff == true`. + + Fallbacks: `GitSignsAddInline` + *hl-GitSignsChangeLnInline* +GitSignsChangeLnInline + Used for changed word diff regions when `config.word_diff == true`. + + Fallbacks: `GitSignsChangeInline` + *hl-GitSignsDeleteLnInline* +GitSignsDeleteLnInline + Used for deleted word diff regions when `config.word_diff == true`. + + Fallbacks: `GitSignsDeleteInline` + *hl-GitSignsDeleteVirtLn* +GitSignsDeleteVirtLn + Used for deleted lines shown by inline `preview_hunk_inline()` or `show_deleted()`. + + Fallbacks: `GitGutterDeleteLine`, `SignifyLineDelete`, `DiffDelete` + *hl-GitSignsDeleteVirtLnInLine* +GitSignsDeleteVirtLnInLine + Used for word diff regions in lines shown by inline `preview_hunk_inline()` or `show_deleted()`. + + Fallbacks: `GitSignsDeleteLnInline` + *hl-GitSignsVirtLnum* +GitSignsVirtLnum + Used for line numbers in inline hunks previews. + + Fallbacks: `GitSignsDeleteVirtLn` + +============================================================================== +COMMAND *gitsigns-command* + + *:Gitsigns* +:Gitsigns {subcmd} {args} Run a Gitsigns command. {subcmd} can be any + function documented in |gitsigns-functions|. + Each argument in {args} will be attempted to be + parsed as a Lua value using `loadstring`, however + if this fails the argument will remain as the + string given by ||. + + Note this command is equivalent to: >vim + :lua require('gitsigns').{subcmd}({args}) +< + Examples: >vim + :Gitsigns diffthis HEAD~1 + :Gitsigns blame_line + :Gitsigns toggle_signs + :Gitsigns toggle_current_line_blame + :Gitsigns change_base ~ + :Gitsigns reset_buffer + :Gitsigns change_base nil true +< + +============================================================================== +SPECIFYING OBJECTS *gitsigns-object* *gitsigns-revision* + +Gitsigns objects are Git revisions as defined in the "SPECIFYING REVISIONS" +section in the gitrevisions(7) man page. For commands that accept an optional +base, the default is the file in the index. Examples: + +Additionally, Gitsigns also accepts the value `FILE` to specify the working +version of a file. + +Object Meaning ~ +@ Version of file in the commit referenced by @ aka HEAD +main Version of file in the commit referenced by main +main^ Version of file in the parent of the commit referenced by main +main~ " +main~1 " +main...other Version of file in the merge base of main and other +@^ Version of file in the parent of HEAD +@~2 Version of file in the grandparent of HEAD +92eb3dd Version of file in the commit 92eb3dd +:1 The file's common ancestor during a conflict +:2 The alternate file in the target branch during a conflict + +============================================================================== +STATUSLINE *gitsigns-statusline* + + *b:gitsigns_status* *b:gitsigns_status_dict* +The buffer variables `b:gitsigns_status` and `b:gitsigns_status_dict` are +provided. `b:gitsigns_status` is formatted using `config.status_formatter` +. `b:gitsigns_status_dict` is a dictionary with the keys: + + • `added` - Number of added lines. + • `changed` - Number of changed lines. + • `removed` - Number of removed lines. + • `head` - Name of current HEAD (branch or short commit hash). + • `root` - Top level directory of the working tree. + • `gitdir` - .git directory. + +Example: +>vim + set statusline+=%{get(b:,'gitsigns_status','')} +< + *b:gitsigns_head* *g:gitsigns_head* +Use `g:gitsigns_head` and `b:gitsigns_head` to return the name of the current +HEAD (usually branch name). If the current HEAD is detached then this will be +a short commit hash. `g:gitsigns_head` returns the current HEAD for the +current working directory, whereas `b:gitsigns_head` returns the current HEAD +for each buffer. + + *b:gitsigns_blame_line* *b:gitsigns_blame_line_dict* +Provided if |gitsigns-config-current_line_blame| is enabled. +`b:gitsigns_blame_line` if formatted using +`config.current_line_blame_formatter`. `b:gitsigns_blame_line_dict` is a +dictionary containing of the blame object for the current line. For complete +list of keys, see the {blame_info} argument from +|gitsigns-config-current_line_blame_formatter|. + +============================================================================== +TEXT OBJECTS *gitsigns-textobject* + +Since text objects are defined via keymaps, these are exposed and configurable +via the config, see |gitsigns-config-keymaps|. The lua implementation is +exposed through |gitsigns.select_hunk()|. + +============================================================================== +EVENTS *gitsigns-events* + +|User| |autocommands| provided to allow extending behaviors. + +Example: >lua + vim.api.nvim_create_autocmd('User', { + pattern = 'GitSignsUpdate', + callback = function(args) + print(os.time(), ' Gitsigns made an update on ', args.data.buffer) + end + }) +< + *User_GitSignsUpdate* +GitSignsUpdate After Gitsigns updates its knowledge about hunks. + Provides `bufnr` in the autocmd user data. + + *User_GitSignsChanged* +GitSignsChanged After any event in which Gitsigns can potentially change + the repository. Provides `file` in the autocmd user data. + +------------------------------------------------------------------------------ +vim:tw=78:ts=8:ft=help:norl: + diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/gitsigns.nvim/scm-1/gitsigns.nvim-scm-1.rockspec b/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/gitsigns.nvim/scm-1/gitsigns.nvim-scm-1.rockspec new file mode 100644 index 00000000..773609e3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/gitsigns.nvim/scm-1/gitsigns.nvim-scm-1.rockspec @@ -0,0 +1,37 @@ +local _MODREV, _SPECREV = 'scm', '-1' + +rockspec_format = "3.0" +package = 'gitsigns.nvim' +version = _MODREV .. _SPECREV + +description = { + summary = 'Git signs written in pure lua', + detailed = [[ + Super fast git decorations implemented purely in Lua. + ]], + homepage = 'http://github.com/lewis6991/gitsigns.nvim', + license = 'MIT/X11', + labels = { 'neovim' } +} + +dependencies = { + 'lua == 5.1', +} + +source = { + url = 'http://github.com/lewis6991/gitsigns.nvim/archive/v' .. _MODREV .. '.zip', + dir = 'gitsigns.nvim-' .. _MODREV, +} + +if _MODREV == 'scm' then + source = { + url = 'git://github.com/lewis6991/gitsigns.nvim', + } +end + +build = { + type = 'builtin', + copy_directories = { + 'doc' + } +} diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/gitsigns.nvim/scm-1/rock_manifest b/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/gitsigns.nvim/scm-1/rock_manifest new file mode 100644 index 00000000..694461c3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/gitsigns.nvim/scm-1/rock_manifest @@ -0,0 +1,49 @@ +rock_manifest = { + doc = { + ["gitsigns.txt"] = "b9579713a56199f1374b4e1b12578f42" + }, + ["gitsigns.nvim-scm-1.rockspec"] = "a9b165d604ce401cfeea760a9366418d", + lua = { + gitsigns = { + ["actions.lua"] = "e1d7610fc1d5fc73cb891b286df0535e", + ["async.lua"] = "15f310cd469f5bfa46bb1e49e5a1d61c", + ["attach.lua"] = "097403415eba4dc5da07fdb948647841", + ["cache.lua"] = "909e8cdc787ca5e6d3ed62782dd5453b", + cli = { + ["argparse.lua"] = "fb6c9ffda01b2090f3c252ecaf00f68a" + }, + ["cli.lua"] = "f00c3494d6be1b07c352c51132150586", + ["config.lua"] = "fa4a0f95747586aab60c9b848a72d9cf", + ["current_line_blame.lua"] = "bf5426f4569e207646c39f9d47a083af", + ["debounce.lua"] = "e0c1145a3dc341f46b36b43d814c1202", + debug = { + ["log.lua"] = "897a3bf45d0996b9517fa8c0a2ba1dac" + }, + ["debug.lua"] = "ef5c8e3c1c9da306ed7b2eb33e657236", + ["diff.lua"] = "0c462ae71c77899e81da151dcfdf77eb", + ["diff_ext.lua"] = "04005195067132403fc336422e05c7d3", + ["diff_int.lua"] = "df447e56f11906998e81d5b94499e013", + ["diffthis.lua"] = "eb4ff5d430d2c4081f1fa9651d2768f7", + git = { + ["version.lua"] = "ab78b8d8a84dd92655e4847223362923" + }, + ["git.lua"] = "082cec98968b0eca1f97f54cbd4ad880", + ["highlight.lua"] = "1d197d8f0f6f69a6455ac220a6890d80", + ["hunks.lua"] = "48fc2d8a9c89815e3c0521b1bb0788e9", + ["manager.lua"] = "af94331f013ed04d175e3c2b2d21c42b", + ["message.lua"] = "523ee4df9da4a7fa9b5ab2100eb27ac4", + ["popup.lua"] = "cbc11678e30094bff8ed54f0357e26f9", + ["repeat.lua"] = "798544de97c774bc8e10eecfd6479585", + ["signs.lua"] = "4c6533b4a406b049e68f49aee59417f6", + ["status.lua"] = "a229a7213a3fbecfcf2d98aaf587fcab", + system = { + ["compat.lua"] = "05d2b6d08602fba46be9b540288091e7" + }, + ["system.lua"] = "d73fac4f6e734cc8ce9f13d6be027503", + ["test.lua"] = "082f7c7a556bf27352a378d047b8e79b", + ["util.lua"] = "e4c4d677e3ad296adee833318a5c5845", + ["watcher.lua"] = "ffcf36424ae17548bdc629cc2de3fcaa" + }, + ["gitsigns.lua"] = "f423758eba3dfcca59f5a0710b58463f" + } +} diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/manifest b/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/manifest new file mode 100644 index 00000000..a65ff179 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1-unstable-2024-05-23-rocks/manifest @@ -0,0 +1,157 @@ +commands = {} +dependencies = { + ["gitsigns.nvim"] = { + ["scm-1"] = { + { + constraints = { + { + op = "==", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + } + } + } +} +modules = { + gitsigns = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.actions"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.async"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.attach"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.cache"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.cli"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.cli.argparse"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.config"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.current_line_blame"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.debounce"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.debug"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.debug.log"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.diff"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.diff_ext"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.diff_int"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.diffthis"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.git"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.git.version"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.highlight"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.hunks"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.manager"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.message"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.popup"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.repeat"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.signs"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.status"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.system"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.system.compat"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.test"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.util"] = { + "gitsigns.nvim/scm-1" + }, + ["gitsigns.watcher"] = { + "gitsigns.nvim/scm-1" + } +} +repository = { + ["gitsigns.nvim"] = { + ["scm-1"] = { + { + arch = "installed", + commands = {}, + dependencies = {}, + modules = { + gitsigns = "gitsigns.lua", + ["gitsigns.actions"] = "gitsigns/actions.lua", + ["gitsigns.async"] = "gitsigns/async.lua", + ["gitsigns.attach"] = "gitsigns/attach.lua", + ["gitsigns.cache"] = "gitsigns/cache.lua", + ["gitsigns.cli"] = "gitsigns/cli.lua", + ["gitsigns.cli.argparse"] = "gitsigns/cli/argparse.lua", + ["gitsigns.config"] = "gitsigns/config.lua", + ["gitsigns.current_line_blame"] = "gitsigns/current_line_blame.lua", + ["gitsigns.debounce"] = "gitsigns/debounce.lua", + ["gitsigns.debug"] = "gitsigns/debug.lua", + ["gitsigns.debug.log"] = "gitsigns/debug/log.lua", + ["gitsigns.diff"] = "gitsigns/diff.lua", + ["gitsigns.diff_ext"] = "gitsigns/diff_ext.lua", + ["gitsigns.diff_int"] = "gitsigns/diff_int.lua", + ["gitsigns.diffthis"] = "gitsigns/diffthis.lua", + ["gitsigns.git"] = "gitsigns/git.lua", + ["gitsigns.git.version"] = "gitsigns/git/version.lua", + ["gitsigns.highlight"] = "gitsigns/highlight.lua", + ["gitsigns.hunks"] = "gitsigns/hunks.lua", + ["gitsigns.manager"] = "gitsigns/manager.lua", + ["gitsigns.message"] = "gitsigns/message.lua", + ["gitsigns.popup"] = "gitsigns/popup.lua", + ["gitsigns.repeat"] = "gitsigns/repeat.lua", + ["gitsigns.signs"] = "gitsigns/signs.lua", + ["gitsigns.status"] = "gitsigns/status.lua", + ["gitsigns.system"] = "gitsigns/system.lua", + ["gitsigns.system.compat"] = "gitsigns/system/compat.lua", + ["gitsigns.test"] = "gitsigns/test.lua", + ["gitsigns.util"] = "gitsigns/util.lua", + ["gitsigns.watcher"] = "gitsigns/watcher.lua" + } + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1.rockspec b/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1.rockspec new file mode 100644 index 00000000..773609e3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/gitsigns.nvim-scm-1.rockspec @@ -0,0 +1,37 @@ +local _MODREV, _SPECREV = 'scm', '-1' + +rockspec_format = "3.0" +package = 'gitsigns.nvim' +version = _MODREV .. _SPECREV + +description = { + summary = 'Git signs written in pure lua', + detailed = [[ + Super fast git decorations implemented purely in Lua. + ]], + homepage = 'http://github.com/lewis6991/gitsigns.nvim', + license = 'MIT/X11', + labels = { 'neovim' } +} + +dependencies = { + 'lua == 5.1', +} + +source = { + url = 'http://github.com/lewis6991/gitsigns.nvim/archive/v' .. _MODREV .. '.zip', + dir = 'gitsigns.nvim-' .. _MODREV, +} + +if _MODREV == 'scm' then + source = { + url = 'git://github.com/lewis6991/gitsigns.nvim', + } +end + +build = { + type = 'builtin', + copy_directories = { + 'doc' + } +} diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns.lua new file mode 100644 index 00000000..9823593f --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns.lua @@ -0,0 +1,233 @@ +local async = require('gitsigns.async') +local log = require('gitsigns.debug.log') + +local gs_config = require('gitsigns.config') +local config = gs_config.config + +local api = vim.api +local uv = vim.uv or vim.loop + +local M = {} + +local cwd_watcher ---@type uv.uv_fs_event_t? + +--- @async +local function update_cwd_head() + if not uv.cwd() then + return + end + local paths = vim.fs.find('.git', { + limit = 1, + upward = true, + type = 'directory', + }) + + if #paths == 0 then + return + end + + if cwd_watcher then + cwd_watcher:stop() + else + cwd_watcher = assert(uv.new_fs_event()) + end + + local cwd = assert(uv.cwd()) + --- @type string, string + local gitdir, head + + local gs_cache = require('gitsigns.cache') + + -- Look in the cache first + for _, bcache in pairs(gs_cache.cache) do + local repo = bcache.git_obj.repo + if repo.toplevel == cwd then + head = repo.abbrev_head + gitdir = repo.gitdir + break + end + end + + local git = require('gitsigns.git') + + if not head or not gitdir then + local info = git.get_repo_info(cwd) + gitdir = info.gitdir + head = info.abbrev_head + end + + async.scheduler() + + api.nvim_exec_autocmds('User', { + pattern = 'GitSignsUpdate', + modeline = false, + }) + + vim.g.gitsigns_head = head + + if not gitdir then + return + end + + local towatch = gitdir .. '/HEAD' + + if cwd_watcher:getpath() == towatch then + -- Already watching + return + end + + local debounce_trailing = require('gitsigns.debounce').debounce_trailing + + local update_head = debounce_trailing( + 100, + async.create(function() + local new_head = git.get_repo_info(cwd).abbrev_head + async.scheduler() + vim.g.gitsigns_head = new_head + end) + ) + + -- Watch .git/HEAD to detect branch changes + cwd_watcher:start( + towatch, + {}, + async.create(function(err) + local __FUNC__ = 'cwd_watcher_cb' + if err then + log.dprintf('Git dir update error: %s', err) + return + end + log.dprint('Git cwd dir update') + + update_head() + end) + ) +end + +local function setup_cli() + api.nvim_create_user_command('Gitsigns', function(params) + require('gitsigns.cli').run(params) + end, { + force = true, + nargs = '*', + range = true, + complete = function(arglead, line) + return require('gitsigns.cli').complete(arglead, line) + end, + }) +end + +local function setup_debug() + log.debug_mode = config.debug_mode + log.verbose = config._verbose +end + +--- @async +local function setup_attach() + if not config.auto_attach then + return + end + + async.scheduler() + + local attach_autocmd_disabled = false + + api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'BufWritePost' }, { + group = 'gitsigns', + callback = function(args) + local bufnr = args.buf --[[@as integer]] + if attach_autocmd_disabled then + local __FUNC__ = 'attach_autocmd' + log.dprint('Attaching is disabled') + return + end + require('gitsigns.attach').attach(bufnr, nil, args.event) + end, + }) + + --- vimpgrep creates and deletes lots of buffers so attaching to each one will + --- waste lots of resource and even slow down vimgrep. + api.nvim_create_autocmd({ 'QuickFixCmdPre', 'QuickFixCmdPost' }, { + group = 'gitsigns', + pattern = '*vimgrep*', + callback = function(args) + attach_autocmd_disabled = args.event == 'QuickFixCmdPre' + end, + }) + + -- Attach to all open buffers + for _, buf in ipairs(api.nvim_list_bufs()) do + if api.nvim_buf_is_loaded(buf) and api.nvim_buf_get_name(buf) ~= '' then + -- Make sure to run each attach in its on async context in case one of the + -- attaches is aborted. + require('gitsigns.attach').attach(buf, nil, 'setup') + end + end +end + +--- @async +local function setup_cwd_head() + async.scheduler() + update_cwd_head() + + local debounce = require('gitsigns.debounce').debounce_trailing + local update_cwd_head_debounced = debounce(100, async.create(update_cwd_head)) + + -- Need to debounce in case some plugin changes the cwd too often + -- (like vim-grepper) + api.nvim_create_autocmd('DirChanged', { + group = 'gitsigns', + callback = function() + update_cwd_head_debounced() + end, + }) +end + +--- Setup and start Gitsigns. +--- +--- Attributes: ~ +--- {async} +--- +--- @param cfg table|nil Configuration for Gitsigns. +--- See |gitsigns-usage| for more details. +M.setup = async.create(1, function(cfg) + gs_config.build(cfg) + + if vim.fn.executable('git') == 0 then + print('gitsigns: git not in path. Aborting setup') + return + end + + api.nvim_create_augroup('gitsigns', {}) + + if vim.fn.has('nvim-0.9') == 0 then + require('gitsigns.git.version').check() + end + + setup_debug() + setup_cli() + require('gitsigns.highlight').setup() + setup_attach() + setup_cwd_head() +end) + +return setmetatable(M, { + __index = function(_, f) + local attach = require('gitsigns.attach') + if attach[f] then + return attach[f] + end + + local actions = require('gitsigns.actions') + if actions[f] then + return actions[f] + end + + if config.debug_mode then + local debug = require('gitsigns.debug') + if debug[f] then + return debug[f] + end + end + end, +}) diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/actions.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/actions.lua new file mode 100644 index 00000000..d9d2561b --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/actions.lua @@ -0,0 +1,1430 @@ +local async = require('gitsigns.async') +local config = require('gitsigns.config').config +local mk_repeatable = require('gitsigns.repeat').mk_repeatable +local popup = require('gitsigns.popup') +local util = require('gitsigns.util') +local manager = require('gitsigns.manager') +local git = require('gitsigns.git') +local run_diff = require('gitsigns.diff') + +local gs_cache = require('gitsigns.cache') +local cache = gs_cache.cache + +local Hunks = require('gitsigns.hunks') + +local api = vim.api +local current_buf = api.nvim_get_current_buf + +local M = {} + +--- @class Gitsigns.CmdParams.Smods +--- @field vertical boolean +--- @field split 'aboveleft'|'belowright'|'topleft'|'botright' + +--- @class Gitsigns.CmdArgs +--- @field vertical? boolean +--- @field split? boolean +--- @field global? boolean + +--- @class Gitsigns.CmdParams +--- @field range integer +--- @field line1 integer +--- @field line2 integer +--- @field count integer +--- @field smods Gitsigns.CmdParams.Smods + +--- Variations of functions from M which are used for the Gitsigns command +--- @type table +local C = {} + +local CP = {} + +local ns_inline = api.nvim_create_namespace('gitsigns_preview_inline') + +--- @param arglead string +--- @return string[] +local function complete_heads(arglead) + --- @type string[] + local all = + vim.fn.systemlist({ 'git', 'rev-parse', '--symbolic', '--branches', '--tags', '--remotes' }) + return vim.tbl_filter( + --- @param x string + --- @return boolean + function(x) + return vim.startswith(x, arglead) + end, + all + ) +end + +--- Toggle |gitsigns-config-signbooleancolumn| +--- +--- @param value boolean|nil Value to set toggle. If `nil` +--- the toggle value is inverted. +--- @return boolean : Current value of |gitsigns-config-signcolumn| +M.toggle_signs = function(value) + if value ~= nil then + config.signcolumn = value + else + config.signcolumn = not config.signcolumn + end + M.refresh() + return config.signcolumn +end + +--- Toggle |gitsigns-config-numhl| +--- +--- @param value boolean|nil Value to set toggle. If `nil` +--- the toggle value is inverted. +--- +--- @return boolean : Current value of |gitsigns-config-numhl| +M.toggle_numhl = function(value) + if value ~= nil then + config.numhl = value + else + config.numhl = not config.numhl + end + M.refresh() + return config.numhl +end + +--- Toggle |gitsigns-config-linehl| +--- +--- @param value boolean|nil Value to set toggle. If `nil` +--- the toggle value is inverted. +--- @return boolean : Current value of |gitsigns-config-linehl| +M.toggle_linehl = function(value) + if value ~= nil then + config.linehl = value + else + config.linehl = not config.linehl + end + M.refresh() + return config.linehl +end + +--- Toggle |gitsigns-config-word_diff| +--- +--- @param value boolean|nil Value to set toggle. If `nil` +--- the toggle value is inverted. +--- @return boolean : Current value of |gitsigns-config-word_diff| +M.toggle_word_diff = function(value) + if value ~= nil then + config.word_diff = value + else + config.word_diff = not config.word_diff + end + -- Don't use refresh() to avoid flicker + util.redraw({ buf = 0, range = { vim.fn.line('w0') - 1, vim.fn.line('w$') } }) + return config.word_diff +end + +--- Toggle |gitsigns-config-current_line_blame| +--- +--- @param value boolean|nil Value to set toggle. If `nil` +--- the toggle value is inverted. +--- @return boolean : Current value of |gitsigns-config-current_line_blame| +M.toggle_current_line_blame = function(value) + if value ~= nil then + config.current_line_blame = value + else + config.current_line_blame = not config.current_line_blame + end + M.refresh() + return config.current_line_blame +end + +--- Toggle |gitsigns-config-show_deleted| +--- +--- @param value boolean|nil Value to set toggle. If `nil` +--- the toggle value is inverted. +--- @return boolean : Current value of |gitsigns-config-show_deleted| +M.toggle_deleted = function(value) + if value ~= nil then + config.show_deleted = value + else + config.show_deleted = not config.show_deleted + end + M.refresh() + return config.show_deleted +end + +--- @param bufnr? integer +--- @param hunks? Gitsigns.Hunk.Hunk[]? +--- @return Gitsigns.Hunk.Hunk? hunk +--- @return integer? index +local function get_cursor_hunk(bufnr, hunks) + bufnr = bufnr or current_buf() + + if not hunks then + if not cache[bufnr] then + return + end + hunks = {} + vim.list_extend(hunks, cache[bufnr].hunks or {}) + vim.list_extend(hunks, cache[bufnr].hunks_staged or {}) + end + + local lnum = api.nvim_win_get_cursor(0)[1] + return Hunks.find_hunk(lnum, hunks) +end + +--- @param bufnr integer +local function update(bufnr) + manager.update(bufnr) + if not manager.schedule(bufnr) then + return + end + if vim.wo.diff then + require('gitsigns.diffthis').update(bufnr) + end +end + +local function get_range(params) + local range --- @type {[1]: integer, [2]: integer}? + if params.range > 0 then + range = { params.line1, params.line2 } + end + return range +end + +--- @async +--- @param bufnr integer +--- @param bcache Gitsigns.CacheEntry +--- @param greedy? boolean +--- @param staged? boolean +--- @return Gitsigns.Hunk.Hunk[]? hunks +local function get_hunks(bufnr, bcache, greedy, staged) + if greedy and config.diff_opts.linematch then + -- Re-run the diff without linematch + local buftext = util.buf_lines(bufnr) + local text --- @type string[]? + if staged then + text = bcache.compare_text_head + else + text = bcache.compare_text + end + if not text then + return + end + local hunks = run_diff(text, buftext, false) + if not manager.schedule(bufnr) then + return + end + return hunks + end + + if staged then + return bcache.hunks_staged + end + + return bcache.hunks +end + +--- @param bufnr integer +--- @param range? {[1]: integer, [2]: integer} +--- @param greedy? boolean +--- @param staged? boolean +--- @return Gitsigns.Hunk.Hunk? +local function get_hunk(bufnr, range, greedy, staged) + local bcache = cache[bufnr] + if not bcache then + return + end + local hunks = get_hunks(bufnr, bcache, greedy, staged) + + if not range then + local hunk = get_cursor_hunk(bufnr, hunks) + return hunk + end + + table.sort(range) + local top, bot = range[1], range[2] + local hunk = Hunks.create_partial_hunk(hunks or {}, top, bot) + hunk.added.lines = api.nvim_buf_get_lines(bufnr, top - 1, bot, false) + hunk.removed.lines = vim.list_slice( + bcache.compare_text, + hunk.removed.start, + hunk.removed.start + hunk.removed.count - 1 + ) + return hunk +end + +--- Stage the hunk at the cursor position, or all lines in the +--- given range. If {range} is provided, all lines in the given +--- range are staged. This supports partial-hunks, meaning if a +--- range only includes a portion of a particular hunk, only the +--- lines within the range will be staged. +--- +--- Attributes: ~ +--- {async} +--- +--- @param range table|nil List-like table of two integers making +--- up the line range from which you want to stage the hunks. +--- If running via command line, then this is taken from the +--- command modifiers. +--- @param opts table|nil Additional options: +--- • {greedy}: (boolean) +--- Stage all contiguous hunks. Only useful if 'diff_opts' +--- contains `linematch`. Defaults to `true`. +M.stage_hunk = mk_repeatable(async.create(2, function(range, opts) + --- @cast range {[1]: integer, [2]: integer}? + + opts = opts or {} + local bufnr = current_buf() + local bcache = cache[bufnr] + if not bcache then + return + end + + if not util.path_exists(bcache.file) then + print('Error: Cannot stage lines. Please add the file to the working tree.') + return + end + + local hunk = get_hunk(bufnr, range, opts.greedy ~= false, false) + + local invert = false + if not hunk then + invert = true + hunk = get_hunk(bufnr, range, opts.greedy ~= false, true) + end + + if not hunk then + api.nvim_echo({ { 'No hunk to stage', 'WarningMsg' } }, false, {}) + return + end + + bcache.git_obj:stage_hunks({ hunk }, invert) + + table.insert(bcache.staged_diffs, hunk) + + bcache:invalidate(true) + update(bufnr) +end)) + +C.stage_hunk = function(_, params) + M.stage_hunk(get_range(params)) +end + +--- @param bufnr integer +--- @param hunk Gitsigns.Hunk.Hunk +local function reset_hunk(bufnr, hunk) + local lstart, lend --- @type integer, integer + if hunk.type == 'delete' then + lstart = hunk.added.start + lend = hunk.added.start + else + lstart = hunk.added.start - 1 + lend = hunk.added.start - 1 + hunk.added.count + end + + if hunk.removed.no_nl_at_eof ~= hunk.added.no_nl_at_eof then + local no_eol = hunk.added.no_nl_at_eof or false + vim.bo[bufnr].endofline = no_eol + vim.bo[bufnr].fixendofline = no_eol + end + + util.set_lines(bufnr, lstart, lend, hunk.removed.lines) +end + +--- Reset the lines of the hunk at the cursor position, or all +--- lines in the given range. If {range} is provided, all lines in +--- the given range are reset. This supports partial-hunks, +--- meaning if a range only includes a portion of a particular +--- hunk, only the lines within the range will be reset. +--- +--- @param range table|nil List-like table of two integers making +--- up the line range from which you want to reset the hunks. +--- If running via command line, then this is taken from the +--- command modifiers. +--- @param opts table|nil Additional options: +--- • {greedy}: (boolean) +--- Stage all contiguous hunks. Only useful if 'diff_opts' +--- contains `linematch`. Defaults to `true`. +M.reset_hunk = mk_repeatable(async.create(2, function(range, opts) + --- @cast range {[1]: integer, [2]: integer}? + + opts = opts or {} + local bufnr = current_buf() + local bcache = cache[bufnr] + if not bcache then + return + end + + local hunk = get_hunk(bufnr, range, opts.greedy ~= false, false) + + if not hunk then + api.nvim_echo({ { 'No hunk to reset', 'WarningMsg' } }, false, {}) + return + end + + reset_hunk(bufnr, hunk) +end)) + +C.reset_hunk = function(_, params) + M.reset_hunk(get_range(params)) +end + +--- Reset the lines of all hunks in the buffer. +M.reset_buffer = function() + local bufnr = current_buf() + local bcache = cache[bufnr] + if not bcache then + return + end + + local hunks = bcache.hunks + if not hunks or #hunks == 0 then + api.nvim_echo({ { 'No unstaged changes in the buffer to reset', 'WarningMsg' } }, false, {}) + return + end + + for i = #hunks, 1, -1 do + reset_hunk(bufnr, hunks[i]) + end +end + +--- Undo the last call of stage_hunk(). +--- +--- Note: only the calls to stage_hunk() performed in the current +--- session can be undone. +--- +--- Attributes: ~ +--- {async} +M.undo_stage_hunk = async.create(function() + local bufnr = current_buf() + local bcache = cache[bufnr] + if not bcache then + return + end + + local hunk = table.remove(bcache.staged_diffs) + if not hunk then + print('No hunks to undo') + return + end + + bcache.git_obj:stage_hunks({ hunk }, true) + bcache:invalidate(true) + update(bufnr) +end) + +--- Stage all hunks in current buffer. +--- +--- Attributes: ~ +--- {async} +M.stage_buffer = async.create(function() + local bufnr = current_buf() + + local bcache = cache[bufnr] + if not bcache then + return + end + + -- Only process files with existing hunks + local hunks = bcache.hunks + if not hunks or #hunks == 0 then + print('No unstaged changes in file to stage') + return + end + + if not util.path_exists(bcache.git_obj.file) then + print('Error: Cannot stage file. Please add it to the working tree.') + return + end + + bcache.git_obj:stage_hunks(hunks) + + for _, hunk in ipairs(hunks) do + table.insert(bcache.staged_diffs, hunk) + end + + bcache:invalidate(true) + update(bufnr) +end) + +--- Unstage all hunks for current buffer in the index. Note: +--- Unlike |gitsigns.undo_stage_hunk()| this doesn't simply undo +--- stages, this runs an `git reset` on current buffers file. +--- +--- Attributes: ~ +--- {async} +M.reset_buffer_index = async.create(function() + local bufnr = current_buf() + local bcache = cache[bufnr] + if not bcache then + return + end + + -- `bcache.staged_diffs` won't contain staged changes outside of current + -- neovim session so signs added from this unstage won't be complete They will + -- however be fixed by gitdir watcher and properly updated We should implement + -- some sort of initial population from git diff, after that this function can + -- be improved to check if any staged hunks exists and it can undo changes + -- using git apply line by line instead of resetting whole file + bcache.staged_diffs = {} + + bcache.git_obj:unstage_file() + + bcache:invalidate(true) + update(bufnr) +end) + +--- @class Gitsigns.NavOpts +--- @field wrap boolean +--- @field foldopen boolean +--- @field navigation_message boolean +--- @field greedy boolean +--- @field preview boolean +--- @field count integer + +--- @param x string +--- @param word string +--- @return boolean +local function findword(x, word) + return string.find(x, '%f[%w_]' .. word .. '%f[^%w_]') ~= nil +end + +--- @param opts? Gitsigns.NavOpts +--- @return Gitsigns.NavOpts +local function process_nav_opts(opts) + opts = opts or {} + + -- show navigation message + if opts.navigation_message == nil then + opts.navigation_message = vim.o.shortmess:find('S') == nil + end + + -- wrap around + if opts.wrap == nil then + opts.wrap = vim.o.wrapscan + end + + if opts.foldopen == nil then + opts.foldopen = findword(vim.o.foldopen, 'search') + end + + if opts.greedy == nil then + opts.greedy = true + end + + if opts.count == nil then + opts.count = vim.v.count1 + end + + return opts +end + +-- Defer function to the next main event +--- @param fn function +local function defer(fn) + if vim.in_fast_event() then + vim.schedule(fn) + else + vim.defer_fn(fn, 1) + end +end + +--- @param bufnr integer +--- @return boolean +local function has_preview_inline(bufnr) + return #api.nvim_buf_get_extmarks(bufnr, ns_inline, 0, -1, { limit = 1 }) > 0 +end + +--- @async +--- @param direction 'first'|'last'|'next'|'prev' +--- @param opts? Gitsigns.NavOpts +local function nav_hunk(direction, opts) + opts = process_nav_opts(opts) + local bufnr = current_buf() + local bcache = cache[bufnr] + if not bcache then + return + end + + local hunks = get_hunks(bufnr, bcache, opts.greedy, false) or {} + local hunks_head = get_hunks(bufnr, bcache, opts.greedy, true) or {} + vim.list_extend(hunks, Hunks.filter_common(hunks_head, hunks) or {}) + + if not hunks or vim.tbl_isempty(hunks) then + if opts.navigation_message then + api.nvim_echo({ { 'No hunks', 'WarningMsg' } }, false, {}) + end + return + end + + local line = api.nvim_win_get_cursor(0)[1] + local index --- @type integer? + + local forwards = direction == 'next' or direction == 'last' + + for _ = 1, opts.count do + index = Hunks.find_nearest_hunk(line, hunks, direction, opts.wrap) + + if not index then + if opts.navigation_message then + api.nvim_echo({ { 'No more hunks', 'WarningMsg' } }, false, {}) + end + local _, col = vim.fn.getline(line):find('^%s*') + api.nvim_win_set_cursor(0, { line, col }) + return + end + + line = forwards and hunks[index].added.start or hunks[index].vend + end + + -- Handle topdelete + line = math.max(line, 1) + + vim.cmd([[ normal! m' ]]) -- add current cursor position to the jump list + + local _, col = vim.fn.getline(line):find('^%s*') + api.nvim_win_set_cursor(0, { line, col }) + + if opts.foldopen then + vim.cmd('silent! foldopen!') + end + + if opts.preview or popup.is_open('hunk') ~= nil then + -- Use defer so the cursor change can settle, otherwise the popup might + -- appear in the old position + defer(function() + -- Close the popup in case one is open which will cause it to focus the + -- popup + popup.close('hunk') + M.preview_hunk() + end) + elseif has_preview_inline(bufnr) then + defer(M.preview_hunk_inline) + end + + if index and opts.navigation_message then + api.nvim_echo({ { string.format('Hunk %d of %d', index, #hunks), 'None' } }, false, {}) + end +end + +--- Jump to hunk in the current buffer. If a hunk preview +--- (popup or inline) was previously opened, it will be re-opened +--- at the next hunk. +--- +--- Attributes: ~ +--- {async} +--- +--- @param direction 'first'|'last'|'next'|'prev' +--- @param opts table|nil Configuration table. Keys: +--- • {wrap}: (boolean) +--- Whether to loop around file or not. Defaults +--- to the value 'wrapscan' +--- • {navigation_message}: (boolean) +--- Whether to show navigation messages or not. +--- Looks at 'shortmess' for default behaviour. +--- • {foldopen}: (boolean) +--- Expand folds when navigating to a hunk which is +--- inside a fold. Defaults to `true` if 'foldopen' +--- contains `search`. +--- • {preview}: (boolean) +--- Automatically open preview_hunk() upon navigating +--- to a hunk. +--- • {greedy}: (boolean) +--- Only navigate between non-contiguous hunks. Only useful if +--- 'diff_opts' contains `linematch`. Defaults to `true`. +--- • {count}: (integer) +--- Number of times to advance. Defaults to |v:count1|. +M.nav_hunk = async.create(2, function(direction, opts) + nav_hunk(direction, opts) +end) + +C.nav_hunk = function(args, _) + M.nav_hunk(args[1], args) +end + +--- @deprecated use |gitsigns.nav_hunk()| +--- Jump to the next hunk in the current buffer. If a hunk preview +--- (popup or inline) was previously opened, it will be re-opened +--- at the next hunk. +--- +--- Attributes: ~ +--- {async} +--- +--- Parameters: ~ +--- See |gitsigns.nav_hunk()|. +M.next_hunk = async.create(1, function(opts) + nav_hunk('next', opts) +end) + +C.next_hunk = function(args, _) + M.nav_hunk('next', args) +end + +--- @deprecated use |gitsigns.nav_hunk()| +--- Jump to the previous hunk in the current buffer. If a hunk preview +--- (popup or inline) was previously opened, it will be re-opened +--- at the previous hunk. +--- +--- Attributes: ~ +--- {async} +--- +--- Parameters: ~ +--- See |gitsigns.nav_hunk()|. +M.prev_hunk = async.create(1, function(opts) + nav_hunk('prev', opts) +end) + +C.prev_hunk = function(args, _) + M.nav_hunk('prev', args) +end + +--- @param fmt Gitsigns.LineSpec +--- @param info table +--- @return Gitsigns.LineSpec +local function lines_format(fmt, info) + local ret = vim.deepcopy(fmt) + + for _, line in ipairs(ret) do + for _, s in ipairs(line) do + s[1] = util.expand_format(s[1], info) + end + end + + return ret +end + +--- @param hunk Gitsigns.Hunk.Hunk +--- @param fileformat string +--- @return Gitsigns.LineSpec +local function linespec_for_hunk(hunk, fileformat) + local hls = {} --- @type Gitsigns.LineSpec + + local removed, added = hunk.removed.lines, hunk.added.lines + + for _, spec in ipairs({ + { sym = '-', lines = removed, hl = 'GitSignsDeletePreview' }, + { sym = '+', lines = added, hl = 'GitSignsAddPreview' }, + }) do + for _, l in ipairs(spec.lines) do + if fileformat == 'dos' then + l = l:gsub('\r$', '') --[[@as string]] + end + hls[#hls + 1] = { + { + spec.sym .. l, + { + { + hl_group = spec.hl, + end_row = 1, -- Highlight whole line + }, + }, + }, + } + end + end + + if config.diff_opts.internal then + local removed_regions, added_regions = + require('gitsigns.diff_int').run_word_diff(removed, added) + + for _, region in ipairs(removed_regions) do + local i = region[1] + table.insert(hls[i][1][2], { + hl_group = 'GitSignsDeleteInline', + start_col = region[3], + end_col = region[4], + }) + end + + for _, region in ipairs(added_regions) do + local i = hunk.removed.count + region[1] + table.insert(hls[i][1][2], { + hl_group = 'GitSignsAddInline', + start_col = region[3], + end_col = region[4], + }) + end + end + + return hls +end + +local function noautocmd(f) + return function() + local ei = vim.o.eventignore + vim.o.eventignore = 'all' + f() + vim.o.eventignore = ei + end +end + +--- Preview the hunk at the cursor position in a floating +--- window. If the preview is already open, calling this +--- will cause the window to get focus. +M.preview_hunk = noautocmd(function() + -- Wrap in noautocmd so vim-repeat continues to work + + if popup.focus_open('hunk') then + return + end + + local bufnr = current_buf() + + local hunk, index = get_cursor_hunk(bufnr) + + if not hunk then + return + end + + local preview_linespec = { + { { 'Hunk of ', 'Title' } }, + unpack(linespec_for_hunk(hunk, vim.bo[bufnr].fileformat)), + } + + local lines_spec = lines_format(preview_linespec, { + hunk_no = index, + num_hunks = #cache[bufnr].hunks, + }) + + popup.create(lines_spec, config.preview_config, 'hunk') +end) + +local function clear_preview_inline(bufnr) + api.nvim_buf_clear_namespace(bufnr, ns_inline, 0, -1) +end + +--- @param keys string +local function feedkeys(keys) + local cy = api.nvim_replace_termcodes(keys, true, false, true) + api.nvim_feedkeys(cy, 'n', false) +end + +--- @param bufnr integer +--- @param greedy? boolean +--- @return Gitsigns.Hunk.Hunk? hunk +--- @return boolean? staged +local function get_hunk_with_staged(bufnr, greedy) + local hunk = get_hunk(bufnr, nil, greedy, false) + if hunk then + return hunk, false + end + + hunk = get_hunk(bufnr, nil, greedy, true) + if hunk then + return hunk, true + end +end + +--- Preview the hunk at the cursor position inline in the buffer. +M.preview_hunk_inline = async.create(function() + local bufnr = current_buf() + + local hunk, staged = get_hunk_with_staged(bufnr, true) + + if not hunk then + return + end + + clear_preview_inline(bufnr) + + local winid --- @type integer + manager.show_added(bufnr, ns_inline, hunk) + if config._inline2 then + if hunk.removed.count > 0 then + winid = manager.show_deleted_in_float(bufnr, ns_inline, hunk, staged) + end + else + manager.show_deleted(bufnr, ns_inline, hunk) + end + + api.nvim_create_autocmd({ 'CursorMoved', 'InsertEnter' }, { + buffer = bufnr, + desc = 'Clear gitsigns inline preview', + callback = function() + if winid then + pcall(api.nvim_win_close, winid, true) + end + clear_preview_inline(bufnr) + end, + once = true, + }) + + -- Virtual lines will be hidden if they are placed on the top row, so + -- automatically scroll the viewport. + if hunk.added.start <= 1 then + feedkeys(hunk.removed.count .. '') + end +end) + +--- Select the hunk under the cursor. +M.select_hunk = function() + local hunk = get_cursor_hunk() + if not hunk then + return + end + + vim.cmd('normal! ' .. hunk.added.start .. 'GV' .. hunk.vend .. 'G') +end + +--- Get hunk array for specified buffer. +--- +--- @param bufnr integer Buffer number, if not provided (or 0) +--- will use current buffer. +--- @return table|nil : Array of hunk objects. +--- Each hunk object has keys: +--- • `"type"`: String with possible values: "add", "change", +--- "delete" +--- • `"head"`: Header that appears in the unified diff +--- output. +--- • `"lines"`: Line contents of the hunks prefixed with +--- either `"-"` or `"+"`. +--- • `"removed"`: Sub-table with fields: +--- • `"start"`: Line number (1-based) +--- • `"count"`: Line count +--- • `"added"`: Sub-table with fields: +--- • `"start"`: Line number (1-based) +--- • `"count"`: Line count +M.get_hunks = function(bufnr) + bufnr = bufnr or current_buf() + if not cache[bufnr] then + return + end + local ret = {} --- @type Gitsigns.Hunk.Hunk_Public[] + -- TODO(lewis6991): allow this to accept a greedy option + for _, h in ipairs(cache[bufnr].hunks or {}) do + ret[#ret + 1] = { + head = h.head, + lines = Hunks.patch_lines(h, vim.bo[bufnr].fileformat), + type = h.type, + added = h.added, + removed = h.removed, + } + end + return ret +end + +--- @param repo Gitsigns.Repo +--- @param info Gitsigns.BlameInfoPublic +--- @return Gitsigns.Hunk.Hunk?, integer?, integer +local function get_blame_hunk(repo, info) + local a = {} + -- If no previous so sha of blame added the file + if info.previous_sha and info.previous_filename then + a = repo:get_show_text(info.previous_sha .. ':' .. info.previous_filename) + end + local b = repo:get_show_text(info.sha .. ':' .. info.filename) + local hunks = run_diff(a, b, false) + local hunk, i = Hunks.find_hunk(info.orig_lnum, hunks) + return hunk, i, #hunks +end + +--- @param is_committed boolean +--- @param full boolean +--- @return {[1]: string, [2]: string}[][] +local function create_blame_fmt(is_committed, full) + if not is_committed then + return { + { { '', 'Label' } }, + } + end + + return { + { + { ' ', 'Directory' }, + { ' ', 'MoreMsg' }, + { '()', 'Label' }, + { ':', 'NormalFloat' }, + }, + { { full and '' or '', 'NormalFloat' } }, + } +end + +--- Run git blame on the current line and show the results in a +--- floating window. If already open, calling this will cause the +--- window to get focus. +--- +--- Attributes: ~ +--- {async} +--- +--- @param opts table|nil Additional options: +--- • {full}: (boolean) +--- Display full commit message with hunk. +--- • {ignore_whitespace}: (boolean) +--- Ignore whitespace when running blame. +--- • {rev}: (string) +--- Revision to blame against. +--- • {extra_opts}: (string[]) +--- Extra options passed to `git-blame`. +M.blame_line = async.create(1, function(opts) + if popup.focus_open('blame') then + return + end + + --- @type Gitsigns.LineBlameOpts + opts = opts or {} + + local bufnr = current_buf() + local bcache = cache[bufnr] + if not bcache then + return + end + + local loading = vim.defer_fn(function() + popup.create({ { { 'Loading...', 'Title' } } }, config.preview_config) + end, 1000) + + if not manager.schedule(bufnr) then + return + end + + local fileformat = vim.bo[bufnr].fileformat + local lnum = api.nvim_win_get_cursor(0)[1] + local result = bcache:get_blame(lnum, opts) + pcall(function() + loading:close() + end) + + if not manager.schedule(bufnr) then + return + end + + assert(result) + + result = util.convert_blame_info(result) + + local is_committed = result.sha and tonumber('0x' .. result.sha) ~= 0 + + local blame_linespec = create_blame_fmt(is_committed, opts.full) + + if is_committed and opts.full then + local body = bcache.git_obj:command( + { 'show', '-s', '--format=%B', result.sha }, + { text = true } + ) + local hunk, hunk_no, num_hunks = get_blame_hunk(bcache.git_obj.repo, result) + assert(hunk and hunk_no and num_hunks) + + result.hunk_no = hunk_no + result.body = body + result.num_hunks = num_hunks + result.hunk_head = hunk.head + + vim.list_extend(blame_linespec, { + { { 'Hunk of ', 'Title' }, { ' ', 'LineNr' } }, + unpack(linespec_for_hunk(hunk, fileformat)), + }) + end + + if not manager.schedule(bufnr) then + return + end + + popup.create(lines_format(blame_linespec, result), config.preview_config, 'blame') +end) + +C.blame_line = function(args, _) + M.blame_line(args) +end + +--- @param bcache Gitsigns.CacheEntry +--- @param base string? +local function update_buf_base(bcache, base) + bcache.file_mode = base == 'FILE' + if not bcache.file_mode then + bcache.git_obj:update_revision(base) + end + bcache:invalidate(true) + update(bcache.bufnr) +end + +--- Change the base revision to diff against. If {base} is not +--- given, then the original base is used. If {global} is given +--- and true, then change the base revision of all buffers, +--- including any new buffers. +--- +--- Attributes: ~ +--- {async} +--- +--- Examples: >vim +--- " Change base to 1 commit behind head +--- :lua require('gitsigns').change_base('HEAD~1') +--- +--- " Also works using the Gitsigns command +--- :Gitsigns change_base HEAD~1 +--- +--- " Other variations +--- :Gitsigns change_base ~1 +--- :Gitsigns change_base ~ +--- :Gitsigns change_base ^ +--- +--- " Commits work too +--- :Gitsigns change_base 92eb3dd +--- +--- " Revert to original base +--- :Gitsigns change_base +--- < +--- +--- For a more complete list of ways to specify bases, see +--- |gitsigns-revision|. +--- +--- @param base string|nil The object/revision to diff against. +--- @param global boolean|nil Change the base of all buffers. +M.change_base = async.create(2, function(base, global) + base = util.norm_base(base) + + if global then + config.base = base + + for _, bcache in pairs(cache) do + update_buf_base(bcache, base) + end + else + local bufnr = current_buf() + local bcache = cache[bufnr] + if not bcache then + return + end + + update_buf_base(bcache, base) + end +end) + +C.change_base = function(args, _) + M.change_base(args[1], (args[2] or args.global)) +end + +CP.change_base = complete_heads + +--- Reset the base revision to diff against back to the +--- index. +--- +--- Alias for `change_base(nil, {global})` . +M.reset_base = function(global) + M.change_base(nil, global) +end + +C.reset_base = function(args, _) + M.change_base(nil, (args[1] or args.global)) +end + +--- Perform a |vimdiff| on the given file with {base} if it is +--- given, or with the currently set base (index by default). +--- +--- If {base} is the index, then the opened buffer is editable and +--- any written changes will update the index accordingly. +--- +--- Examples: >vim +--- " Diff against the index +--- :Gitsigns diffthis +--- +--- " Diff against the last commit +--- :Gitsigns diffthis ~1 +--- < +--- +--- For a more complete list of ways to specify bases, see +--- |gitsigns-revision|. +--- +--- Attributes: ~ +--- {async} +--- +--- @param base string|nil Revision to diff against. Defaults to index. +--- @param opts table|nil Additional options: +--- • {vertical}: {boolean}. Split window vertically. Defaults to +--- config.diff_opts.vertical. If running via command line, then +--- this is taken from the command modifiers. +--- • {split}: {string}. One of: 'aboveleft', 'belowright', +--- 'botright', 'rightbelow', 'leftabove', 'topleft'. Defaults to +--- 'aboveleft'. If running via command line, then this is taken +--- from the command modifiers. +M.diffthis = function(base, opts) + -- TODO(lewis6991): can't pass numbers as strings from the command line + if base ~= nil then + base = tostring(base) + end + opts = opts or {} + if not opts.vertical then + opts.vertical = config.diff_opts.vertical + end + require('gitsigns.diffthis').diffthis(base, opts) +end + +C.diffthis = function(args, params) + -- TODO(lewis6991): validate these + local opts = { + vertical = args.vertical, + split = args.split, + } + + if params.smods then + if params.smods.split ~= '' and opts.split == nil then + opts.split = params.smods.split + end + if opts.vertical == nil then + opts.vertical = params.smods.vertical + end + end + + M.diffthis(args[1], opts) +end + +CP.diffthis = complete_heads + +-- C.test = function(pos_args: {any}, named_args: {string:any}, params: api.UserCmdParams) +-- print('POS ARGS:', vim.inspect(pos_args)) +-- print('NAMED ARGS:', vim.inspect(named_args)) +-- print('PARAMS:', vim.inspect(params)) +-- end + +--- Show revision {base} of the current file, if it is given, or +--- with the currently set base (index by default). +--- +--- If {base} is the index, then the opened buffer is editable and +--- any written changes will update the index accordingly. +--- +--- Examples: >vim +--- " View the index version of the file +--- :Gitsigns show +--- +--- " View revision of file in the last commit +--- :Gitsigns show ~1 +--- < +--- +--- For a more complete list of ways to specify bases, see +--- |gitsigns-revision|. +--- +--- Attributes: ~ +--- {async} +M.show = function(revision) + local bufnr = api.nvim_get_current_buf() + if not cache[bufnr] then + print('Error: Buffer is not attached.') + return + end + local diffthis = require('gitsigns.diffthis') + diffthis.show(bufnr, revision) +end + +CP.show = complete_heads + +--- @param buf_or_filename string|integer +--- @param hunks Gitsigns.Hunk.Hunk[] +--- @param qflist table[]? +local function hunks_to_qflist(buf_or_filename, hunks, qflist) + for i, hunk in ipairs(hunks) do + qflist[#qflist + 1] = { + bufnr = type(buf_or_filename) == 'number' and buf_or_filename or nil, + filename = type(buf_or_filename) == 'string' and buf_or_filename or nil, + lnum = hunk.added.start, + text = string.format('Lines %d-%d (%d/%d)', hunk.added.start, hunk.vend, i, #hunks), + } + end +end + +--- @param target 'all'|'attached'|integer|nil +--- @return table[]? +local function buildqflist(target) + target = target or current_buf() + if target == 0 then + target = current_buf() + end + local qflist = {} --- @type table[] + + if type(target) == 'number' then + local bufnr = target + if not cache[bufnr] then + return + end + hunks_to_qflist(bufnr, cache[bufnr].hunks, qflist) + elseif target == 'attached' then + for bufnr, bcache in pairs(cache) do + hunks_to_qflist(bufnr, bcache.hunks, qflist) + end + elseif target == 'all' then + local repos = {} --- @type table + for _, bcache in pairs(cache) do + local repo = bcache.git_obj.repo + if not repos[repo.gitdir] then + repos[repo.gitdir] = repo + end + end + + local repo = git.Repo.new(assert(vim.loop.cwd())) + if not repos[repo.gitdir] then + repos[repo.gitdir] = repo + end + + for _, r in pairs(repos) do + for _, f in ipairs(r:files_changed()) do + local f_abs = r.toplevel .. '/' .. f + local stat = vim.loop.fs_stat(f_abs) + if stat and stat.type == 'file' then + local a = r:get_show_text(':0:' .. f) + async.scheduler() + local hunks = run_diff(a, util.file_lines(f_abs)) + hunks_to_qflist(f_abs, hunks, qflist) + end + end + end + end + return qflist +end + +--- Populate the quickfix list with hunks. Automatically opens the +--- quickfix window. +--- +--- Attributes: ~ +--- {async} +--- +--- @param target integer|string +--- Specifies which files hunks are collected from. +--- Possible values. +--- • [integer]: The buffer with the matching buffer +--- number. `0` for current buffer (default). +--- • `"attached"`: All attached buffers. +--- • `"all"`: All modified files for each git +--- directory of all attached buffers in addition +--- to the current working directory. +--- @param opts table|nil Additional options: +--- • {use_location_list}: (boolean) +--- Populate the location list instead of the +--- quickfix list. Default to `false`. +--- • {nr}: (integer) +--- Window number or ID when using location list. +--- Expand folds when navigating to a hunk which is +--- inside a fold. Defaults to `0`. +--- • {open}: (boolean) +--- Open the quickfix/location list viewer. +--- Defaults to `true`. +M.setqflist = async.create(2, function(target, opts) + opts = opts or {} + if opts.open == nil then + opts.open = true + end + local qfopts = { + items = buildqflist(target), + title = 'Hunks', + } + async.scheduler() + if opts.use_location_list then + local nr = opts.nr or 0 + vim.fn.setloclist(nr, {}, ' ', qfopts) + if opts.open then + if config.trouble then + require('trouble').open('loclist') + else + vim.cmd.lopen() + end + end + else + vim.fn.setqflist({}, ' ', qfopts) + if opts.open then + if config.trouble then + require('trouble').open('quickfix') + else + vim.cmd.copen() + end + end + end +end) + +C.setqflist = function(args, _) + local target = tonumber(args[1]) or args[1] + M.setqflist(target, args) +end + +--- Populate the location list with hunks. Automatically opens the +--- location list window. +--- +--- Alias for: `setqflist({target}, { use_location_list = true, nr = {nr} }` +--- +--- Attributes: ~ +--- {async} +--- +--- @param nr? integer Window number or the |window-ID|. +--- `0` for the current window (default). +--- @param target integer|string See |gitsigns.setqflist()|. +M.setloclist = function(nr, target) + M.setqflist(target, { + nr = nr, + use_location_list = true, + }) +end + +C.setloclist = function(args, _) + local target = tonumber(args[2]) or args[2] + M.setloclist(tonumber(args[1]), target) +end + +--- Get all the available line specific actions for the current +--- buffer at the cursor position. +--- +--- @return table|nil : Dictionary of action name to function which when called +--- performs action. +M.get_actions = function() + local bufnr = current_buf() + local bcache = cache[bufnr] + if not bcache then + return + end + local hunk = get_cursor_hunk() + + --- @type string[] + local actions_l = {} + + if hunk then + vim.list_extend(actions_l, { + 'stage_hunk', + 'reset_hunk', + 'preview_hunk', + 'select_hunk', + }) + else + actions_l[#actions_l + 1] = 'blame_line' + end + + if not vim.tbl_isempty(bcache.staged_diffs) then + actions_l[#actions_l + 1] = 'undo_stage_hunk' + end + + local actions = {} --- @type table + for _, a in ipairs(actions_l) do + actions[a] = M[a] --[[@as function]] + end + + return actions +end + +for name, f in + pairs(M --[[@as table]]) +do + if vim.startswith(name, 'toggle') then + C[name] = function(args) + f(args[1]) + end + end +end + +--- Refresh all buffers. +--- +--- Attributes: ~ +--- {async} +M.refresh = async.create(function() + manager.reset_signs() + require('gitsigns.highlight').setup_highlights() + require('gitsigns.current_line_blame').setup() + for k, v in pairs(cache) do + v:invalidate(true) + manager.update(k) + end +end) + +--- @param name string +--- @return fun(args: table, params: Gitsigns.CmdParams) +function M._get_cmd_func(name) + return C[name] +end + +--- @param name string +--- @return fun(arglead: string): string[] +function M._get_cmp_func(name) + return CP[name] +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/async.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/async.lua new file mode 100644 index 00000000..76fc6ce2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/async.lua @@ -0,0 +1,196 @@ +local M = {} + +--- @class Gitsigns.Async_T +--- @field _current Gitsigns.Async_T +local Async_T = {} + +-- Handle for an object currently running on the event loop. +-- The coroutine is paused while this is active. +-- Must provide methods cancel() and is_cancelled() +-- +-- Handle gets updated on each call to a wrapped functions, so provide access +-- to it via a proxy + +-- Coroutine.running() was changed between Lua 5.1 and 5.2: +-- - 5.1: Returns the running coroutine, or nil when called by the main thread. +-- - 5.2: Returns the running coroutine plus a boolean, true when the running +-- coroutine is the main one. +-- +-- For LuaJIT, 5.2 behaviour is enabled with LUAJIT_ENABLE_LUA52COMPAT +-- +-- We need to handle both. + +--- Store all the async threads in a weak table so we don't prevent them from +--- being garbage collected +--- @type table +local handles = setmetatable({}, { __mode = 'k' }) + +--- Returns whether the current execution context is async. +function M.running() + local current = coroutine.running() + if current and handles[current] then + return true + end + return false +end + +local function is_Async_T(handle) + if + handle + and type(handle) == 'table' + and vim.is_callable(handle.cancel) + and vim.is_callable(handle.is_cancelled) + then + return true + end +end + +--- Analogous to uv.close +--- @param cb function +function Async_T:cancel(cb) + -- Cancel anything running on the event loop + if self._current and not self._current:is_cancelled() then + self._current:cancel(cb) + end +end + +--- @param co thread +--- @return Gitsigns.Async_T +function Async_T.new(co) + local handle = setmetatable({}, { __index = Async_T }) + handles[co] = handle + return handle +end + +--- Analogous to uv.is_closing +--- @return boolean +function Async_T:is_cancelled() + return self._current and self._current:is_cancelled() +end + +--- @param func function +--- @param callback? fun(...: any) +--- @param ... any +--- @return Gitsigns.Async_T +local function run(func, callback, ...) + local co = coroutine.create(func) + local handle = Async_T.new(co) + + local function step(...) + local ret = { coroutine.resume(co, ...) } + local stat = ret[1] + + if not stat then + local err = ret[2] --[[@as string]] + error( + string.format('The coroutine failed with this message: %s\n%s', err, debug.traceback(co)) + ) + end + + if coroutine.status(co) == 'dead' then + if callback then + callback(unpack(ret, 2, table.maxn(ret))) + end + return + end + + --- @type integer, fun(...: any): any + local nargs, fn = ret[2], ret[3] + + assert(type(fn) == 'function', 'type error :: expected func') + + local args = { select(4, unpack(ret)) } + args[nargs] = step + + local r = fn(unpack(args, 1, nargs)) + if is_Async_T(r) then + --- @cast r Gitsigns.Async_T + handle._current = r + end + end + + step(...) + return handle +end + +--- @param argc integer +--- @param func function +--- @param ... any +--- @return any ... +function M.wait(argc, func, ...) + -- Always run the wrapped functions in xpcall and re-raise the error in the + -- coroutine. This makes pcall work as normal. + local function pfunc(...) + local args = { ... } --- @type any[] + local cb = args[argc] + args[argc] = function(...) + cb(true, ...) + end + xpcall(func, function(err) + cb(false, err, debug.traceback()) + end, unpack(args, 1, argc)) + end + + local ret = { coroutine.yield(argc, pfunc, ...) } + + local ok = ret[1] + if not ok then + --- @type string, string + local err, traceback = ret[2], ret[3] + error(string.format('Wrapped function failed: %s\n%s', err, traceback)) + end + + return unpack(ret, 2, table.maxn(ret)) +end + +--- Creates an async function with a callback style function. +--- @param argc number The number of arguments of func. Must be included. +--- @param func function A callback style function to be converted. The last argument must be the callback. +--- @return function: Returns an async function +function M.wrap(argc, func) + assert(type(func) == 'function') + assert(type(argc) == 'number') + return function(...) + return M.wait(argc, func, ...) + end +end + +--- create([argc, ] func) +--- +--- Use this to create a function which executes in an async context but +--- called from a non-async context. Inherently this cannot return anything +--- since it is non-blocking +--- +--- If argc is not provided, then the created async function cannot be continued +--- +--- @generic F: function +--- @param argc_or_func F|integer +--- @param func? F +--- @return F +function M.create(argc_or_func, func) + local argc --- @type integer + if type(argc_or_func) == 'function' then + assert(not func) + func = argc_or_func + elseif type(argc_or_func) == 'number' then + assert(type(func) == 'function') + argc = argc_or_func + end + + --- @cast func function + + return function(...) + local callback = argc and select(argc + 1, ...) or nil + return run(func, callback, unpack({ ... }, 1, argc)) + end +end + +--- An async function that when called will yield to the Neovim scheduler to be +--- able to call the API. +M.scheduler = M.wrap(1, vim.schedule) + +function M.run(func, ...) + return run(func, nil, ...) +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/attach.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/attach.lua new file mode 100644 index 00000000..fc3c2e51 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/attach.lua @@ -0,0 +1,423 @@ +local async = require('gitsigns.async') +local git = require('gitsigns.git') + +local manager = require('gitsigns.manager') + +local log = require('gitsigns.debug.log') +local dprintf = log.dprintf +local dprint = log.dprint + +local gs_cache = require('gitsigns.cache') +local cache = gs_cache.cache +local Status = require('gitsigns.status') + +local config = require('gitsigns.config').config + +local util = require('gitsigns.util') + +local throttle_by_id = require('gitsigns.debounce').throttle_by_id + +local api = vim.api +local uv = vim.loop + +local M = {} + +--- @param name string +--- @return string? buffer +--- @return string? commit +local function parse_fugitive_uri(name) + if vim.fn.exists('*FugitiveReal') == 0 then + dprint('Fugitive not installed') + return + end + + ---@type string + local path = vim.fn.FugitiveReal(name) + ---@type string? + local commit = vim.fn.FugitiveParse(name)[1]:match('([^:]+):.*') + if commit == '0' then + -- '0' means the index so clear commit so we attach normally + commit = nil + end + return path, commit +end + +--- @param name string +--- @return string buffer +--- @return string? commit +local function parse_gitsigns_uri(name) + -- TODO(lewis6991): Support submodules + --- @type any, any, string?, string?, string + local _, _, root_path, commit, rel_path = name:find([[^gitsigns://(.*)/%.git/(.*):(.*)]]) + commit = util.norm_base(commit) + if root_path then + name = root_path .. '/' .. rel_path + end + return name, commit +end + +--- @param bufnr integer +--- @return string buffer +--- @return string? commit +--- @return boolean? force_attach +local function get_buf_path(bufnr) + local file = uv.fs_realpath(api.nvim_buf_get_name(bufnr)) + or api.nvim_buf_call(bufnr, function() + return vim.fn.expand('%:p') + end) + + if vim.startswith(file, 'fugitive://') then + local path, commit = parse_fugitive_uri(file) + dprintf("Fugitive buffer for file '%s' from path '%s'", path, file) + if path then + local realpath = uv.fs_realpath(path) + if realpath then + return realpath, commit, true + end + end + end + + if vim.startswith(file, 'gitsigns://') then + local path, commit = parse_gitsigns_uri(file) + dprintf("Gitsigns buffer for file '%s' from path '%s' on commit '%s'", path, file, commit) + local realpath = uv.fs_realpath(path) + if realpath then + return realpath, commit, true + end + end + + return file +end + +local function on_lines(_, bufnr, _, first, last_orig, last_new, byte_count) + if first == last_orig and last_orig == last_new and byte_count == 0 then + -- on_lines can be called twice for undo events; ignore the second + -- call which indicates no changes. + return + end + return manager.on_lines(bufnr, first, last_orig, last_new) +end + +--- @param _ 'reload' +--- @param bufnr integer +local function on_reload(_, bufnr) + local __FUNC__ = 'on_reload' + cache[bufnr]:invalidate() + dprint('Reload') + manager.update_debounced(bufnr) +end + +--- @param _ 'detach' +--- @param bufnr integer +local function on_detach(_, bufnr) + api.nvim_clear_autocmds({ group = 'gitsigns', buffer = bufnr }) + M.detach(bufnr, true) +end + +--- @param bufnr integer +--- @return string? +--- @return string? +local function on_attach_pre(bufnr) + --- @type string?, string? + local gitdir, toplevel + if config._on_attach_pre then + --- @type {gitdir: string?, toplevel: string?} + local res = async.wait(2, config._on_attach_pre, bufnr) + dprintf('ran on_attach_pre with result %s', vim.inspect(res)) + if type(res) == 'table' then + if type(res.gitdir) == 'string' then + gitdir = res.gitdir + end + if type(res.toplevel) == 'string' then + toplevel = res.toplevel + end + end + end + return gitdir, toplevel +end + +--- @param _bufnr integer +--- @param file string +--- @param encoding string +--- @return Gitsigns.GitObj? +local function try_worktrees(_bufnr, file, encoding) + if not config.worktrees then + return + end + + for _, wt in ipairs(config.worktrees) do + local git_obj = git.Obj.new(file, encoding, wt.gitdir, wt.toplevel) + if git_obj and git_obj.object_name then + dprintf('Using worktree %s', vim.inspect(wt)) + return git_obj + end + end +end + +local setup = util.once(function() + manager.setup() + + api.nvim_create_autocmd('OptionSet', { + group = 'gitsigns', + pattern = { 'fileformat', 'bomb', 'eol' }, + callback = function() + local buf = vim.api.nvim_get_current_buf() + local bcache = cache[buf] + if not bcache then + return + end + bcache:invalidate(true) + manager.update(buf) + end, + }) + + require('gitsigns.current_line_blame').setup() + + api.nvim_create_autocmd('VimLeavePre', { + group = 'gitsigns', + callback = M.detach_all, + }) +end) + +--- @class Gitsigns.GitContext +--- @field file string +--- @field toplevel? string +--- @field gitdir? string +--- @field base? string + +--- @param bufnr integer +--- @return Gitsigns.GitContext? ctx +--- @return string? err +local function get_buf_context(bufnr) + if api.nvim_buf_line_count(bufnr) > config.max_file_length then + return nil, 'Exceeds max_file_length' + end + + local file, commit, force_attach = get_buf_path(bufnr) + + if vim.bo[bufnr].buftype ~= '' and not force_attach then + return nil, 'Non-normal buffer' + end + + local file_dir = util.dirname(file) + + if not file_dir or not util.path_exists(file_dir) then + return nil, 'Not a path' + end + + local gitdir, toplevel = on_attach_pre(bufnr) + + return { + file = file, + gitdir = gitdir, + toplevel = toplevel, + -- Commit buffers have there base set back one revision with '^' + -- Stage buffers always compare against the common ancestor (':1') + -- :0: index + -- :1: common ancestor + -- :2: target commit (HEAD) + -- :3: commit which is being merged + base = commit and (commit:match('^:[1-3]') and ':1' or commit .. '^') or nil, + } +end + +--- Ensure attaches cannot be interleaved for the same buffer. +--- Since attaches are asynchronous we need to make sure an attach isn't +--- performed whilst another one is in progress. +--- @param cbuf integer +--- @param ctx? Gitsigns.GitContext +--- @param aucmd? string +local attach_throttled = throttle_by_id(function(cbuf, ctx, aucmd) + local __FUNC__ = 'attach' + local passed_ctx = ctx ~= nil + + setup() + + if cache[cbuf] then + dprint('Already attached') + return + end + + if aucmd then + dprintf('Attaching (trigger=%s)', aucmd) + else + dprint('Attaching') + end + + if not api.nvim_buf_is_loaded(cbuf) then + dprint('Non-loaded buffer') + return + end + + if not ctx then + local err + ctx, err = get_buf_context(cbuf) + if err then + dprint(err) + return + end + assert(ctx) + end + + local encoding = vim.bo[cbuf].fileencoding + if encoding == '' then + encoding = 'utf-8' + end + + local file = ctx.file + if not vim.startswith(file, '/') and ctx.toplevel then + file = ctx.toplevel .. util.path_sep .. file + end + + local revision = ctx.base or config.base + local git_obj = git.Obj.new(file, revision, encoding, ctx.gitdir, ctx.toplevel) + + if not git_obj and not passed_ctx then + git_obj = try_worktrees(cbuf, file, encoding) + async.scheduler() + if not api.nvim_buf_is_valid(cbuf) then + return + end + end + + if not git_obj then + dprint('Empty git obj') + return + end + + async.scheduler() + if not api.nvim_buf_is_valid(cbuf) then + return + end + + Status:update(cbuf, { + head = git_obj.repo.abbrev_head, + root = git_obj.repo.toplevel, + gitdir = git_obj.repo.gitdir, + }) + + if not passed_ctx and (not util.path_exists(file) or uv.fs_stat(file).type == 'directory') then + dprint('Not a file') + return + end + + if not git_obj.relpath then + dprint('Cannot resolve file in repo') + return + end + + if not config.attach_to_untracked and git_obj.object_name == nil then + dprint('File is untracked') + return + end + + -- On windows os.tmpname() crashes in callback threads so initialise this + -- variable on the main thread. + async.scheduler() + if not api.nvim_buf_is_valid(cbuf) then + return + end + + if config.on_attach and config.on_attach(cbuf) == false then + dprint('User on_attach() returned false') + return + end + + cache[cbuf] = gs_cache.new({ + bufnr = cbuf, + file = file, + git_obj = git_obj, + }) + + if config.watch_gitdir.enable then + local watcher = require('gitsigns.watcher') + cache[cbuf].gitdir_watcher = watcher.watch_gitdir(cbuf, git_obj.repo.gitdir) + end + + if not api.nvim_buf_is_loaded(cbuf) then + dprint('Un-loaded buffer') + return + end + + -- Make sure to attach before the first update (which is async) so we pick up + -- changes from BufReadCmd. + api.nvim_buf_attach(cbuf, false, { + on_lines = on_lines, + on_reload = on_reload, + on_detach = on_detach, + }) + + api.nvim_create_autocmd('BufWrite', { + group = 'gitsigns', + buffer = cbuf, + callback = function() + manager.update_debounced(cbuf) + end, + }) + + -- Initial update + manager.update(cbuf) +end) + +--- Detach Gitsigns from all buffers it is attached to. +function M.detach_all() + for k, _ in pairs(cache) do + M.detach(k) + end +end + +--- Detach Gitsigns from the buffer {bufnr}. If {bufnr} is not +--- provided then the current buffer is used. +--- +--- @param bufnr integer Buffer number +--- @param _keep_signs? boolean +function M.detach(bufnr, _keep_signs) + -- When this is called interactively (with no arguments) we want to remove all + -- the signs, however if called via a detach event (due to nvim_buf_attach) + -- then we don't want to clear the signs in case the buffer is just being + -- updated due to the file externally changing. When this happens a detach and + -- attach event happen in sequence and so we keep the old signs to stop the + -- sign column width moving about between updates. + bufnr = bufnr or api.nvim_get_current_buf() + dprint('Detached') + local bcache = cache[bufnr] + if not bcache then + dprint('Cache was nil') + return + end + + manager.detach(bufnr, _keep_signs) + + -- Clear status variables + Status:clear(bufnr) + + gs_cache.destroy(bufnr) +end + +--- Attach Gitsigns to the buffer. +--- +--- Attributes: ~ +--- {async} +--- +--- @param bufnr integer Buffer number +--- @param ctx Gitsigns.GitContext|nil +--- Git context data that may optionally be used to attach to any +--- buffer that represents a real git object. +--- • {file}: (string) +--- Path to the file represented by the buffer, relative to the +--- top-level. +--- • {toplevel}: (string?) +--- Path to the top-level of the parent git repository. +--- • {gitdir}: (string?) +--- Path to the git directory of the parent git repository +--- (typically the ".git/" directory). +--- • {commit}: (string?) +--- The git revision that the file belongs to. +--- • {base}: (string?) +--- The git revision that the file should be compared to. +--- @param _trigger? string +M.attach = async.create(3, function(bufnr, ctx, _trigger) + attach_throttled(bufnr or api.nvim_get_current_buf(), ctx, _trigger) +end) + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/cache.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/cache.lua new file mode 100644 index 00000000..66be662c --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/cache.lua @@ -0,0 +1,151 @@ +local async = require('gitsigns.async') +local util = require('gitsigns.util') + +local M = { + CacheEntry = {}, +} + +--- @class (exact) Gitsigns.CacheEntry +--- @field bufnr integer +--- @field file string +--- @field compare_text? string[] +--- @field hunks? Gitsigns.Hunk.Hunk[] +--- @field force_next_update? boolean +--- @field file_mode? boolean +--- +--- @field compare_text_head? string[] +--- @field hunks_staged? Gitsigns.Hunk.Hunk[] +--- +--- @field staged_diffs? Gitsigns.Hunk.Hunk[] +--- @field gitdir_watcher? uv.uv_fs_event_t +--- @field git_obj Gitsigns.GitObj +--- @field blame? table +local CacheEntry = M.CacheEntry + +function CacheEntry:get_rev_bufname(rev) + rev = rev or self.git_obj.revision or ':0' + return string.format('gitsigns://%s/%s:%s', self.git_obj.repo.gitdir, rev, self.git_obj.relpath) +end + +--- Invalidate any state dependent on the buffer content. +--- If 'all' is passed, then invalidate everything. +--- @param all? boolean +function CacheEntry:invalidate(all) + self.hunks = nil + self.hunks_staged = nil + self.blame = nil + if all then + -- The below doesn't need to be invalidated + -- if the buffer changes + self.compare_text = nil + self.compare_text_head = nil + end +end + +--- @param o Gitsigns.CacheEntry +--- @return Gitsigns.CacheEntry +function M.new(o) + o.staged_diffs = o.staged_diffs or {} + return setmetatable(o, { __index = CacheEntry }) +end + +local sleep = async.wrap(2, function(duration, cb) + vim.defer_fn(cb, duration) +end) + +--- @private +function CacheEntry:wait_for_hunks() + local loop_protect = 0 + while not self.hunks and loop_protect < 10 do + loop_protect = loop_protect + 1 + sleep(100) + end +end + +-- If a file contains has up to this amount of lines, then +-- always blame the whole file, otherwise only blame one line +-- at a time. +local BLAME_THRESHOLD_LEN = 1000000 + +--- @private +--- @param lnum integer +--- @param opts Gitsigns.BlameOpts +--- @return table? +function CacheEntry:run_blame(lnum, opts) + local bufnr = self.bufnr + local blame_cache --- @type table? + repeat + local buftext = util.buf_lines(bufnr) + local tick = vim.b[bufnr].changedtick + local lnum0 = #buftext > BLAME_THRESHOLD_LEN and lnum or nil + -- TODO(lewis6991): Cancel blame on changedtick + blame_cache = self.git_obj:run_blame(buftext, lnum0, opts) + async.scheduler() + if not vim.api.nvim_buf_is_valid(bufnr) then + return + end + until vim.b[bufnr].changedtick == tick + return blame_cache +end + +--- @param file string +--- @param lnum integer +--- @return Gitsigns.BlameInfo +local function get_blame_nc(file, lnum) + local Git = require('gitsigns.git') + + return { + orig_lnum = 0, + final_lnum = lnum, + commit = Git.not_commited(file), + filename = file, + } +end + +--- @param lnum integer +--- @param opts Gitsigns.BlameOpts +--- @return Gitsigns.BlameInfo? +function CacheEntry:get_blame(lnum, opts) + if opts.rev then + local buftext = util.buf_lines(self.bufnr) + return self.git_obj:run_blame(buftext, lnum, opts)[lnum] + end + + local blame_cache = self.blame + + if not blame_cache or not blame_cache[lnum] then + self:wait_for_hunks() + local Hunks = require('gitsigns.hunks') + if Hunks.find_hunk(lnum, self.hunks) then + --- Bypass running blame (which can be expensive) if we know lnum is in a hunk + blame_cache = blame_cache or {} + blame_cache[lnum] = get_blame_nc(self.git_obj.relpath, lnum) + else + -- Refresh cache + blame_cache = self:run_blame(lnum, opts) + end + self.blame = blame_cache + end + + if blame_cache then + return blame_cache[lnum] + end +end + +function CacheEntry:destroy() + local w = self.gitdir_watcher + if w and not w:is_closing() then + w:close() + end +end + +---@type table +M.cache = {} + +--- @param bufnr integer +function M.destroy(bufnr) + M.cache[bufnr]:destroy() + M.cache[bufnr] = nil +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/cli.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/cli.lua new file mode 100644 index 00000000..8638493e --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/cli.lua @@ -0,0 +1,108 @@ +local async = require('gitsigns.async') + +local log = require('gitsigns.debug.log') +local dprintf = log.dprintf +local message = require('gitsigns.message') + +local parse_args = require('gitsigns.cli.argparse').parse_args + +local actions = require('gitsigns.actions') +local attach = require('gitsigns.attach') +local gs_debug = require('gitsigns.debug') + +--- @type table,boolean> +local sources = { + [actions] = true, + [attach] = false, + [gs_debug] = false, +} + +-- try to parse each argument as a lua boolean, nil or number, if fails then +-- keep argument as a string: +-- +-- 'false' -> false +-- 'nil' -> nil +-- '100' -> 100 +-- 'HEAD~300' -> 'HEAD~300' +local function parse_to_lua(a) + if tonumber(a) then + return tonumber(a) + elseif a == 'false' or a == 'true' then + return a == 'true' + elseif a == 'nil' then + return nil + end + return a +end + +local M = {} + +function M.complete(arglead, line) + local words = vim.split(line, '%s+') + local n = #words + + local matches = {} + if n == 2 then + for m, _ in pairs(sources) do + for func, _ in pairs(m) do + if not func:match('^[a-z]') then + -- exclude + elseif vim.startswith(func, arglead) then + table.insert(matches, func) + end + end + end + elseif n > 2 then + -- Subcommand completion + local cmp_func = actions._get_cmp_func(words[2]) + if cmp_func then + return cmp_func(arglead) + end + end + return matches +end + +M.run = async.create(1, function(params) + local __FUNC__ = 'cli.run' + local pos_args_raw, named_args_raw = parse_args(params.args) + + local func = pos_args_raw[1] + + if not func then + func = async.wait(3, vim.ui.select, M.complete('', 'Gitsigns '), {}) --[[@as string]] + if not func then + return + end + end + + local pos_args = vim.tbl_map(parse_to_lua, vim.list_slice(pos_args_raw, 2)) + local named_args = vim.tbl_map(parse_to_lua, named_args_raw) + local args = vim.tbl_extend('error', pos_args, named_args) + + dprintf( + "Running action '%s' with arguments %s", + func, + vim.inspect(args, { newline = ' ', indent = '' }) + ) + + local cmd_func = actions._get_cmd_func(func) + if cmd_func then + -- Action has a specialised mapping function from command form to lua + -- function + cmd_func(args, params) + return + end + + for m, has_named in pairs(sources) do + local f = m[func] + if type(f) == 'function' then + -- Note functions here do not have named arguments + f(unpack(pos_args), has_named and named_args or nil) + return + end + end + + message.error('%s is not a valid function or action', func) +end) + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/cli/argparse.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/cli/argparse.lua new file mode 100644 index 00000000..ad52e720 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/cli/argparse.lua @@ -0,0 +1,108 @@ +local M = {} + +local function is_char(x) + return x:match('[^=\'"%s]') ~= nil +end + +-- Return positional arguments and named arguments +--- @param x string +--- @return string[], table +function M.parse_args(x) + --- @type string[], table + local pos_args, named_args = {}, {} + + local state = 'in_arg' + local cur_arg = '' + local cur_val = '' + local cur_quote = '' + + local function peek(idx) + return x:sub(idx + 1, idx + 1) + end + + local i = 1 + while i <= #x do + local ch = x:sub(i, i) + -- dprintf('L(%d)(%s): cur_arg="%s" ch="%s"', i, state, cur_arg, ch) + + if state == 'in_arg' then + if is_char(ch) then + if ch == '-' and peek(i) == '-' then + state = 'in_flag' + cur_arg = '' + i = i + 1 + else + cur_arg = cur_arg .. ch + end + elseif ch:match('%s') then + pos_args[#pos_args + 1] = cur_arg + state = 'in_ws' + elseif ch == '=' then + cur_val = '' + local next_ch = peek(i) + if next_ch == "'" or next_ch == '"' then + cur_quote = next_ch + i = i + 1 + state = 'in_quote' + else + state = 'in_value' + end + end + elseif state == 'in_flag' then + if ch:match('%s') then + named_args[cur_arg] = true + state = 'in_ws' + else + cur_arg = cur_arg .. ch + end + elseif state == 'in_ws' then + if is_char(ch) then + if ch == '-' and peek(i) == '-' then + state = 'in_flag' + cur_arg = '' + i = i + 1 + else + state = 'in_arg' + cur_arg = ch + end + end + elseif state == 'in_value' then + if is_char(ch) then + cur_val = cur_val .. ch + elseif ch:match('%s') then + named_args[cur_arg] = cur_val + cur_arg = '' + state = 'in_ws' + end + elseif state == 'in_quote' then + local next_ch = peek(i) + if ch == '\\' and next_ch == cur_quote then + cur_val = cur_val .. next_ch + i = i + 1 + elseif ch == cur_quote then + named_args[cur_arg] = cur_val + state = 'in_ws' + if next_ch ~= '' and not next_ch:match('%s') then + error('malformed argument: ' .. next_ch) + end + else + cur_val = cur_val .. ch + end + end + i = i + 1 + end + + if #cur_arg > 0 then + if state == 'in_arg' then + pos_args[#pos_args + 1] = cur_arg + elseif state == 'in_flag' then + named_args[cur_arg] = true + elseif state == 'in_value' then + named_args[cur_arg] = cur_val + end + end + + return pos_args, named_args +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/config.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/config.lua new file mode 100644 index 00000000..1897771e --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/config.lua @@ -0,0 +1,952 @@ +--- @class (exact) Gitsigns.SchemaElem.Deprecated +--- +--- Used for renaming fields. +--- @field new_field? string +--- +--- Documentation for deprecation. Will be added to the help file and used in +--- the notification if `hard = true`. +--- @field message? string +--- +--- Emit a message via vim.notify +--- @field hard? boolean + +--- @class (exact) Gitsigns.SchemaElem +--- @field type string|string[] +--- @field refresh? fun(cb: fun()) Function to refresh the config value +--- @field deep_extend? boolean +--- @field default any +--- @field deprecated? boolean|Gitsigns.SchemaElem.Deprecated +--- @field default_help? string +--- @field description string + +--- @class (exact) Gitsigns.DiffOpts +--- @field algorithm string +--- @field internal boolean +--- @field indent_heuristic boolean +--- @field vertical boolean +--- @field linematch? integer +--- @field ignore_whitespace_change? true +--- @field ignore_whitespace? true +--- @field ignore_whitespace_change_at_eol? true +--- @field ignore_blank_lines? true + +--- @class (exact) Gitsigns.SignConfig +--- @field show_count boolean +--- @field hl string +--- @field text string +--- @field numhl string +--- @field linehl string + +--- @alias Gitsigns.SignType +--- | 'add' +--- | 'change' +--- | 'delete' +--- | 'topdelete' +--- | 'changedelete' +--- | 'untracked' + +--- @class (exact) Gitsigns.CurrentLineBlameFmtOpts +--- @field relative_time boolean + +--- @alias Gitsigns.CurrentLineBlameFmtFun fun(user: string, info: table, opts: Gitsigns.CurrentLineBlameFmtOpts): {[1]:string,[2]:string}[] + +--- @class (exact) Gitsigns.CurrentLineBlameOpts : Gitsigns.BlameOpts +--- @field virt_text? boolean +--- @field virt_text_pos? 'eol'|'overlay'|'right_align' +--- @field delay? integer +--- @field virt_text_priority? integer + +--- @class (exact) Gitsigns.BlameOpts +--- @field ignore_whitespace? boolean +--- @field rev? string +--- @field extra_opts? string[] + +--- @class (exact) Gitsigns.LineBlameOpts : Gitsigns.BlameOpts +--- @field full? boolean + +--- @class (exact) Gitsigns.Config +--- @field debug_mode boolean +--- @field diff_opts Gitsigns.DiffOpts +--- @field base? string +--- @field signs table +--- @field _signs_staged table +--- @field _signs_staged_enable boolean +--- @field count_chars table +--- @field signcolumn boolean +--- @field numhl boolean +--- @field linehl boolean +--- @field show_deleted boolean +--- @field sign_priority integer +--- @field _on_attach_pre fun(bufnr: integer, callback: fun(_: table)) +--- @field on_attach fun(bufnr: integer) +--- @field watch_gitdir { enable: boolean, follow_files: boolean } +--- @field max_file_length integer +--- @field update_debounce integer +--- @field status_formatter fun(_: table): string +--- @field current_line_blame boolean +--- @field current_line_blame_formatter_opts { relative_time: boolean } +--- @field current_line_blame_formatter string|Gitsigns.CurrentLineBlameFmtFun +--- @field current_line_blame_formatter_nc string|Gitsigns.CurrentLineBlameFmtFun +--- @field current_line_blame_opts Gitsigns.CurrentLineBlameOpts +--- @field preview_config table +--- @field auto_attach boolean +--- @field attach_to_untracked boolean +--- @field yadm { enable: boolean } +--- @field worktrees {toplevel: string, gitdir: string}[] +--- @field word_diff boolean +--- @field trouble boolean +--- -- Undocumented +--- @field _refresh_staged_on_update boolean +--- @field _threaded_diff boolean +--- @field _inline2 boolean +--- @field _git_version string +--- @field _verbose boolean +--- @field _test_mode boolean + +local M = { + Config = { + DiffOpts = {}, + SignConfig = {}, + watch_gitdir = {}, + current_line_blame_formatter_opts = {}, + current_line_blame_opts = {}, + yadm = {}, + Worktree = {}, + }, +} + +--- @param v Gitsigns.SchemaElem +--- @return any +local function resolve_default(v) + if type(v.default) == 'function' and v.type ~= 'function' then + return v.default() + else + return v.default + end +end + +--- @return Gitsigns.DiffOpts +local function parse_diffopt() + --- @type Gitsigns.DiffOpts + local r = { + algorithm = 'myers', + internal = false, + indent_heuristic = false, + vertical = true, + } + + local optmap = { + ['indent-heuristic'] = 'indent_heuristic', + internal = 'internal', + iwhite = 'ignore_whitespace_change', + iblank = 'ignore_blank_lines', + iwhiteeol = 'ignore_whitespace_change_at_eol', + iwhiteall = 'ignore_whitespace', + } + + local diffopt = vim.opt.diffopt:get() --[[@as string[] ]] + for _, o in ipairs(diffopt) do + if optmap[o] then + r[optmap[o]] = true + elseif o == 'horizontal' then + r.vertical = false + elseif vim.startswith(o, 'algorithm:') then + r.algorithm = string.sub(o, ('algorithm:'):len() + 1) + elseif vim.startswith(o, 'linematch:') then + r.linematch = tonumber(string.sub(o, ('linematch:'):len() + 1)) + end + end + + return r +end + +--- @type Gitsigns.Config +M.config = setmetatable({}, { + __index = function(t, k) + if rawget(t, k) == nil then + local field = M.schema[k] + if not field then + return + end + + rawset(t, k, resolve_default(field)) + + if field.refresh then + field.refresh(function() + rawset(t, k, resolve_default(field)) + end) + end + end + + return rawget(t, k) + end, +}) + +--- @type table +M.schema = { + signs = { + type = 'table', + deep_extend = true, + default = { + add = { hl = 'GitSignsAdd', text = '┃', numhl = 'GitSignsAddNr', linehl = 'GitSignsAddLn' }, + change = { + hl = 'GitSignsChange', + text = '┃', + numhl = 'GitSignsChangeNr', + linehl = 'GitSignsChangeLn', + }, + delete = { + hl = 'GitSignsDelete', + text = '▁', + numhl = 'GitSignsDeleteNr', + linehl = 'GitSignsDeleteLn', + }, + topdelete = { + hl = 'GitSignsTopdelete', + text = '▔', + numhl = 'GitSignsTopdeleteNr', + linehl = 'GitSignsTopdeleteLn', + }, + changedelete = { + hl = 'GitSignsChangedelete', + text = '~', + numhl = 'GitSignsChangedeleteNr', + linehl = 'GitSignsChangedeleteLn', + }, + untracked = { + hl = 'GitSignsUntracked', + text = '┆', + numhl = 'GitSignsUntrackedNr', + linehl = 'GitSignsUntrackedLn', + }, + }, + default_help = [[{ + add = { text = '┃' }, + change = { text = '┃' }, + delete = { text = '▁' }, + topdelete = { text = '▔' }, + changedelete = { text = '~' }, + untracked = { text = '┆' }, + }]], + description = [[ + Configuration for signs: + • `text` specifies the character to use for the sign. + • `show_count` to enable showing count of hunk, e.g. number of deleted + lines. + + The highlights `GitSigns[kind][type]` is used for each kind of sign. E.g. + 'add' signs uses the highlights: + • `GitSignsAdd` (for normal text signs) + • `GitSignsAddNr` (for signs when `config.numhl == true`) + • `GitSignsAddLn `(for signs when `config.linehl == true`) + + See |gitsigns-highlight-groups|. + ]], + }, + + _signs_staged = { + type = 'table', + deep_extend = true, + default = { + add = { + hl = 'GitSignsStagedAdd', + text = '┃', + numhl = 'GitSignsStagedAddNr', + linehl = 'GitSignsStagedAddLn', + }, + change = { + hl = 'GitSignsStagedChange', + text = '┃', + numhl = 'GitSignsStagedChangeNr', + linehl = 'GitSignsStagedChangeLn', + }, + delete = { + hl = 'GitSignsStagedDelete', + text = '▁', + numhl = 'GitSignsStagedDeleteNr', + linehl = 'GitSignsStagedDeleteLn', + }, + topdelete = { + hl = 'GitSignsStagedTopdelete', + text = '▔', + numhl = 'GitSignsStagedTopdeleteNr', + linehl = 'GitSignsStagedTopdeleteLn', + }, + changedelete = { + hl = 'GitSignsStagedChangedelete', + text = '~', + numhl = 'GitSignsStagedChangedeleteNr', + linehl = 'GitSignsStagedChangedeleteLn', + }, + }, + default_help = [[{ + add = { text = '┃' }, + change = { text = '┃' }, + delete = { text = '▁' }, + topdelete = { text = '▔' }, + changedelete = { text = '~' }, + }]], + description = [[ + Configuration for signs of staged hunks. + + See |gitsigns-config-signs|. + ]], + }, + + _signs_staged_enable = { + type = 'boolean', + default = false, + description = [[ + Show signs for staged hunks. + + When enabled the signs defined in |git-config-signs_staged|` are used. + ]], + }, + + worktrees = { + type = 'table', + default = nil, + description = [[ + Detached working trees. + + Array of tables with the keys `gitdir` and `toplevel`. + + If normal attaching fails, then each entry in the table is attempted + with the work tree details set. + + Example: >lua + worktrees = { + { + toplevel = vim.env.HOME, + gitdir = vim.env.HOME .. '/projects/dotfiles/.git' + } + } + ]], + }, + + _on_attach_pre = { + type = 'function', + default = nil, + description = [[ + Asynchronous hook called before attaching to a buffer. Mainly used to + configure detached worktrees. + + This callback must call its callback argument. The callback argument can + accept an optional table argument with the keys: 'gitdir' and 'toplevel'. + + Example: >lua + on_attach_pre = function(bufnr, callback) + ... + callback { + gitdir = ..., + toplevel = ... + } + end + < + ]], + }, + + on_attach = { + type = 'function', + default = nil, + description = [[ + Callback called when attaching to a buffer. Mainly used to setup keymaps. + The buffer number is passed as the first argument. + + This callback can return `false` to prevent attaching to the buffer. + + Example: >lua + on_attach = function(bufnr) + if vim.api.nvim_buf_get_name(bufnr):match() then + -- Don't attach to specific buffers whose name matches a pattern + return false + end + + -- Setup keymaps + vim.api.nvim_buf_set_keymap(bufnr, 'n', 'hs', 'lua require"gitsigns".stage_hunk()', {}) + ... -- More keymaps + end +< + ]], + }, + + watch_gitdir = { + type = 'table', + deep_extend = true, + default = { + enable = true, + follow_files = true, + }, + description = [[ + When opening a file, a libuv watcher is placed on the respective + `.git` directory to detect when changes happen to use as a trigger to + update signs. + + Fields: ~ + • `enable`: + Whether the watcher is enabled. + + • `follow_files`: + If a file is moved with `git mv`, switch the buffer to the new location. + ]], + }, + + sign_priority = { + type = 'number', + default = 6, + description = [[ + Priority to use for signs. + ]], + }, + + signcolumn = { + type = 'boolean', + default = true, + description = [[ + Enable/disable symbols in the sign column. + + When enabled the highlights defined in `signs.*.hl` and symbols defined + in `signs.*.text` are used. + ]], + }, + + numhl = { + type = 'boolean', + default = false, + description = [[ + Enable/disable line number highlights. + + When enabled the highlights defined in `signs.*.numhl` are used. If + the highlight group does not exist, then it is automatically defined + and linked to the corresponding highlight group in `signs.*.hl`. + ]], + }, + + linehl = { + type = 'boolean', + default = false, + description = [[ + Enable/disable line highlights. + + When enabled the highlights defined in `signs.*.linehl` are used. If + the highlight group does not exist, then it is automatically defined + and linked to the corresponding highlight group in `signs.*.hl`. + ]], + }, + + show_deleted = { + type = 'boolean', + default = false, + description = [[ + Show the old version of hunks inline in the buffer (via virtual lines). + + Note: Virtual lines currently use the highlight `GitSignsDeleteVirtLn`. + ]], + }, + + diff_opts = { + type = 'table', + deep_extend = true, + refresh = function(callback) + vim.api.nvim_create_autocmd('OptionSet', { + group = vim.api.nvim_create_augroup('gitsigns.config.diff_opts', {}), + pattern = 'diffopt', + callback = callback, + }) + end, + default = parse_diffopt, + default_help = "derived from 'diffopt'", + description = [[ + Diff options. If the default value is used, then changes to 'diffopt' are + automatically applied. + + Fields: ~ + • algorithm: string + Diff algorithm to use. Values: + • "myers" the default algorithm + • "minimal" spend extra time to generate the + smallest possible diff + • "patience" patience diff algorithm + • "histogram" histogram diff algorithm + • internal: boolean + Use Neovim's built in xdiff library for running diffs. + • indent_heuristic: boolean + Use the indent heuristic for the internal + diff library. + • vertical: boolean + Start diff mode with vertical splits. + • linematch: integer + Enable second-stage diff on hunks to align lines. + Requires `internal=true`. + • ignore_blank_lines: boolean + Ignore changes where lines are blank. + • ignore_whitespace_change: boolean + Ignore changes in amount of white space. + It should ignore adding trailing white space, + but not leading white space. + • ignore_whitespace: boolean + Ignore all white space changes. + • ignore_whitespace_change_at_eol: boolean + Ignore white space changes at end of line. + ]], + }, + + base = { + type = 'string', + default = nil, + default_help = 'index', + description = [[ + The object/revision to diff against. + See |gitsigns-revision|. + ]], + }, + + count_chars = { + type = 'table', + default = { + [1] = '1', -- '₁', + [2] = '2', -- '₂', + [3] = '3', -- '₃', + [4] = '4', -- '₄', + [5] = '5', -- '₅', + [6] = '6', -- '₆', + [7] = '7', -- '₇', + [8] = '8', -- '₈', + [9] = '9', -- '₉', + ['+'] = '>', -- '₊', + }, + description = [[ + The count characters used when `signs.*.show_count` is enabled. The + `+` entry is used as a fallback. With the default, any count outside + of 1-9 uses the `>` character in the sign. + + Possible use cases for this field: + • to specify unicode characters for the counts instead of 1-9. + • to define characters to be used for counts greater than 9. + ]], + }, + + status_formatter = { + type = 'function', + --- @param status Gitsigns.StatusObj + --- @return string + default = function(status) + local added, changed, removed = status.added, status.changed, status.removed + local status_txt = {} + if added and added > 0 then + table.insert(status_txt, '+' .. added) + end + if changed and changed > 0 then + table.insert(status_txt, '~' .. changed) + end + if removed and removed > 0 then + table.insert(status_txt, '-' .. removed) + end + return table.concat(status_txt, ' ') + end, + default_help = [[function(status) + local added, changed, removed = status.added, status.changed, status.removed + local status_txt = {} + if added and added > 0 then table.insert(status_txt, '+'..added ) end + if changed and changed > 0 then table.insert(status_txt, '~'..changed) end + if removed and removed > 0 then table.insert(status_txt, '-'..removed) end + return table.concat(status_txt, ' ') + end]], + description = [[ + Function used to format `b:gitsigns_status`. + ]], + }, + + max_file_length = { + type = 'number', + default = 40000, + description = [[ + Max file length (in lines) to attach to. + ]], + }, + + preview_config = { + type = 'table', + deep_extend = true, + default = { + border = 'single', + style = 'minimal', + relative = 'cursor', + row = 0, + col = 1, + }, + description = [[ + Option overrides for the Gitsigns preview window. Table is passed directly + to `nvim_open_win`. + ]], + }, + + auto_attach = { + type = 'boolean', + default = true, + description = [[ + Automatically attach to files. + ]], + }, + + attach_to_untracked = { + type = 'boolean', + default = false, + description = [[ + Attach to untracked files. + ]], + }, + + update_debounce = { + type = 'number', + default = 100, + description = [[ + Debounce time for updates (in milliseconds). + ]], + }, + + current_line_blame = { + type = 'boolean', + default = false, + description = [[ + Adds an unobtrusive and customisable blame annotation at the end of + the current line. + + The highlight group used for the text is `GitSignsCurrentLineBlame`. + ]], + }, + + current_line_blame_opts = { + type = 'table', + deep_extend = true, + default = { + virt_text = true, + virt_text_pos = 'eol', + virt_text_priority = 100, + delay = 1000, + }, + description = [[ + Options for the current line blame annotation. + + Fields: ~ + • virt_text: boolean + Whether to show a virtual text blame annotation. + • virt_text_pos: string + Blame annotation position. Available values: + `eol` Right after eol character. + `overlay` Display over the specified column, without + shifting the underlying text. + `right_align` Display right aligned in the window. + • delay: integer + Sets the delay (in milliseconds) before blame virtual text is + displayed. + • ignore_whitespace: boolean + Ignore whitespace when running blame. + • virt_text_priority: integer + Priority of virtual text. + • extra_opts: string[] + Extra options passed to `git-blame`. + ]], + }, + + current_line_blame_formatter_opts = { + type = 'table', + deep_extend = true, + deprecated = true, + default = { + relative_time = false, + }, + description = [[ + Options for the current line blame annotation formatter. + + Fields: ~ + • relative_time: boolean + ]], + }, + + current_line_blame_formatter = { + type = { 'string', 'function' }, + default = ' , - ', + description = [[ + String or function used to format the virtual text of + |gitsigns-config-current_line_blame|. + + When a string, accepts the following format specifiers: + + • `` + • `` + • `` + • `` + • `` + • `` or `` + • `` + • `` + • `` + • `` or `` + • `` + • `` + • `` + • `` + + For `` and ``, `FORMAT` can + be any valid date format that is accepted by `os.date()` with the + addition of `%R` (defaults to `%Y-%m-%d`): + + • `%a` abbreviated weekday name (e.g., Wed) + • `%A` full weekday name (e.g., Wednesday) + • `%b` abbreviated month name (e.g., Sep) + • `%B` full month name (e.g., September) + • `%c` date and time (e.g., 09/16/98 23:48:10) + • `%d` day of the month (16) [01-31] + • `%H` hour, using a 24-hour clock (23) [00-23] + • `%I` hour, using a 12-hour clock (11) [01-12] + • `%M` minute (48) [00-59] + • `%m` month (09) [01-12] + • `%p` either "am" or "pm" (pm) + • `%S` second (10) [00-61] + • `%w` weekday (3) [0-6 = Sunday-Saturday] + • `%x` date (e.g., 09/16/98) + • `%X` time (e.g., 23:48:10) + • `%Y` full year (1998) + • `%y` two-digit year (98) [00-99] + • `%%` the character `%´ + • `%R` relative (e.g., 4 months ago) + + When a function: + Parameters: ~ + {name} Git user name returned from `git config user.name` . + {blame_info} Table with the following keys: + • `abbrev_sha`: string + • `orig_lnum`: integer + • `final_lnum`: integer + • `author`: string + • `author_mail`: string + • `author_time`: integer + • `author_tz`: string + • `committer`: string + • `committer_mail`: string + • `committer_time`: integer + • `committer_tz`: string + • `summary`: string + • `previous`: string + • `filename`: string + • `boundary`: true? + + Note that the keys map onto the output of: + `git blame --line-porcelain` + + {opts} Passed directly from + |gitsigns-config-current_line_blame_formatter_opts|. + + Return: ~ + The result of this function is passed directly to the `opts.virt_text` + field of |nvim_buf_set_extmark| and thus must be a list of + [text, highlight] tuples. + ]], + }, + + current_line_blame_formatter_nc = { + type = { 'string', 'function' }, + default = ' ', + description = [[ + String or function used to format the virtual text of + |gitsigns-config-current_line_blame| for lines that aren't committed. + + See |gitsigns-config-current_line_blame_formatter| for more information. + ]], + }, + + trouble = { + type = 'boolean', + default = function() + local has_trouble = pcall(require, 'trouble') + return has_trouble + end, + default_help = 'true if installed', + description = [[ + When using setqflist() or setloclist(), open Trouble instead of the + quickfix/location list window. + ]], + }, + + yadm = { + type = 'table', + deprecated = { + message = 'Please use |gitsigns-config-on_attach_pre| instead', + }, + default = { enable = false }, + description = [[ + yadm configuration. + ]], + }, + + _git_version = { + type = 'string', + default = 'auto', + description = [[ + Version of git available. Set to 'auto' to automatically detect. + ]], + }, + + _verbose = { + type = 'boolean', + default = false, + description = [[ + More verbose debug message. Requires debug_mode=true. + ]], + }, + + _test_mode = { + description = 'Enable test mode', + type = 'boolean', + default = false, + }, + + word_diff = { + type = 'boolean', + default = false, + description = [[ + Highlight intra-line word differences in the buffer. + Requires `config.diff_opts.internal = true` . + + Uses the highlights: + • For word diff in previews: + • `GitSignsAddInline` + • `GitSignsChangeInline` + • `GitSignsDeleteInline` + • For word diff in buffer: + • `GitSignsAddLnInline` + • `GitSignsChangeLnInline` + • `GitSignsDeleteLnInline` + • For word diff in virtual lines (e.g. show_deleted): + • `GitSignsAddVirtLnInline` + • `GitSignsChangeVirtLnInline` + • `GitSignsDeleteVirtLnInline` + ]], + }, + + _refresh_staged_on_update = { + type = 'boolean', + default = false, + description = [[ + Always refresh the staged file on each update. Disabling this will cause + the staged file to only be refreshed when an update to the index is + detected. + ]], + }, + + _threaded_diff = { + type = 'boolean', + default = true, + description = [[ + Run diffs on a separate thread + ]], + }, + + _inline2 = { + type = 'boolean', + default = true, + description = [[ + Enable enhanced version of preview_hunk_inline() + ]], + }, + + debug_mode = { + type = 'boolean', + default = false, + description = [[ + Enables debug logging and makes the following functions + available: `dump_cache`, `debug_messages`, `clear_debug`. + ]], + }, +} + +local function warn(s, ...) + vim.notify(s:format(...), vim.log.levels.WARN, { title = 'gitsigns' }) +end + +--- @param config Gitsigns.Config +local function validate_config(config) + --- @diagnostic disable-next-line:no-unknown + for k, v in pairs(config) do + local kschema = M.schema[k] + if kschema == nil then + warn("gitsigns: Ignoring invalid configuration field '%s'", k) + elseif kschema.type then + if type(kschema.type) == 'string' then + vim.validate({ + [k] = { v, kschema.type }, + }) + end + end + end +end + +--- @param cfg table +local function handle_deprecated(cfg) + for k, v in pairs(M.schema) do + local dep = v.deprecated + if dep and cfg[k] ~= nil then + if type(dep) == 'table' then + if dep.new_field then + local opts_key, field = dep.new_field:match('(.*)%.(.*)') + if opts_key and field then + -- Field moved to an options table + local opts = (cfg[opts_key] or {}) --[[@as table]] + opts[field] = cfg[k] + cfg[opts_key] = opts + else + -- Field renamed + cfg[dep.new_field] = cfg[k] + end + end + + if dep.hard then + if dep.message then + warn(dep.message) + elseif dep.new_field then + warn('%s is now deprecated, please use %s', k, dep.new_field) + else + warn('%s is now deprecated; ignoring', k) + end + end + end + end + end +end + +--- @param k string +--- @param v Gitsigns.SchemaElem +--- @param user_val any +local function build_field(k, v, user_val) + local config = M.config --[[@as table]] + + if v.deep_extend then + local d = resolve_default(v) + config[k] = vim.tbl_deep_extend('force', d, user_val) + else + config[k] = user_val + end +end + +--- @param user_config Gitsigns.Config|nil +function M.build(user_config) + user_config = user_config or {} + + handle_deprecated(user_config) + + validate_config(user_config) + + for k, v in pairs(M.schema) do + if user_config[k] ~= nil then + build_field(k, v, user_config[k]) + if v.refresh then + v.refresh(function() + build_field(k, v, user_config[k]) + end) + end + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/current_line_blame.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/current_line_blame.lua new file mode 100644 index 00000000..383a28fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/current_line_blame.lua @@ -0,0 +1,242 @@ +local async = require('gitsigns.async') +local cache = require('gitsigns.cache').cache +local config = require('gitsigns.config').config +local util = require('gitsigns.util') + +local api = vim.api + +local debounce = require('gitsigns.debounce') + +local namespace = api.nvim_create_namespace('gitsigns_blame') + +local M = {} + +--- @param bufnr integer +local function reset(bufnr) + if not api.nvim_buf_is_valid(bufnr) then + return + end + api.nvim_buf_del_extmark(bufnr, namespace, 1) + vim.b[bufnr].gitsigns_blame_line_dict = nil +end + +--- @param fmt string +--- @param name string +--- @param info Gitsigns.BlameInfoPublic +--- @return string +local function expand_blame_format(fmt, name, info) + if info.author == name then + info.author = 'You' + end + return util.expand_format(fmt, info, config.current_line_blame_formatter_opts.relative_time) +end + +--- @param virt_text {[1]: string, [2]: string}[] +--- @return string +local function flatten_virt_text(virt_text) + local res = {} ---@type string[] + for _, part in ipairs(virt_text) do + res[#res + 1] = part[1] + end + return table.concat(res) +end + +--- @param winid integer +--- @return integer +local function win_width() + local winid = api.nvim_get_current_win() + local wininfo = vim.fn.getwininfo(winid)[1] + local textoff = wininfo and wininfo.textoff or 0 + return api.nvim_win_get_width(winid) - textoff +end + +--- @param bufnr integer +--- @param lnum integer +--- @return integer +local function line_len(bufnr, lnum) + local line = api.nvim_buf_get_lines(bufnr, lnum - 1, lnum, true)[1] + return api.nvim_strwidth(line) +end + +--- @param fmt string +--- @return Gitsigns.CurrentLineBlameFmtFun +local function default_formatter(fmt) + return function(username, blame_info, _opts) + return { + { + expand_blame_format(fmt, username, blame_info), + 'GitSignsCurrentLineBlame', + }, + } + end +end + +---@param bufnr integer +---@param blame_info Gitsigns.BlameInfoPublic +---@return {[1]: string, [2]:string}[] +local function get_blame_virt_text(bufnr, blame_info) + local git_obj = assert(cache[bufnr]).git_obj + + local clb_formatter = blame_info.author == 'Not Committed Yet' + and config.current_line_blame_formatter_nc + or config.current_line_blame_formatter + + if type(clb_formatter) == 'string' then + clb_formatter = default_formatter(clb_formatter) + end + + return clb_formatter(git_obj.repo.username, blame_info, config.current_line_blame_formatter_opts) +end + +--- @param bufnr integer +--- @param lnum integer +--- @param blame_info Gitsigns.BlameInfo +--- @param opts Gitsigns.CurrentLineBlameOpts +local function handle_blame_info(bufnr, lnum, blame_info, opts) + blame_info = util.convert_blame_info(blame_info) + + local virt_text = get_blame_virt_text(bufnr, blame_info) + local virt_text_str = flatten_virt_text(virt_text) + + vim.b[bufnr].gitsigns_blame_line_dict = blame_info + vim.b[bufnr].gitsigns_blame_line = virt_text_str + + if opts.virt_text then + local virt_text_pos = opts.virt_text_pos + if virt_text_pos == 'right_align' then + if api.nvim_strwidth(virt_text_str) > (win_width() - line_len(bufnr, lnum)) then + virt_text_pos = 'eol' + end + end + api.nvim_buf_set_extmark(bufnr, namespace, lnum - 1, 0, { + id = 1, + virt_text = virt_text, + virt_text_pos = virt_text_pos, + priority = opts.virt_text_priority, + hl_mode = 'combine', + }) + end +end + +--- @param winid integer +--- @return integer lnum +local function get_lnum(winid) + return api.nvim_win_get_cursor(winid)[1] +end + +--- @param winid integer +--- @param lnum integer +--- @return boolean +local function foldclosed(winid, lnum) + ---@return boolean + return api.nvim_win_call(winid, function() + return vim.fn.foldclosed(lnum) ~= -1 + end) +end + +---@return boolean +local function insert_mode() + return api.nvim_get_mode().mode == 'i' +end + +--- Update function, must be called in async context +--- @param bufnr integer +local function update0(bufnr) + async.scheduler() + if not api.nvim_buf_is_valid(bufnr) then + return + end + + if insert_mode() then + return + end + + local winid = api.nvim_get_current_win() + + if bufnr ~= api.nvim_win_get_buf(winid) then + return + end + + local lnum = get_lnum(winid) + + -- Can't show extmarks on folded lines so skip + if foldclosed(winid, lnum) then + return + end + + local bcache = cache[bufnr] + if not bcache or not bcache.git_obj.object_name then + return + end + + local opts = config.current_line_blame_opts + + local blame_info = bcache:get_blame(lnum, opts) + + if not api.nvim_win_is_valid(winid) or bufnr ~= api.nvim_win_get_buf(winid) then + return + end + + if not blame_info then + return + end + + if lnum ~= get_lnum(winid) then + -- Cursor has moved during events; abort and tr-trigger another update + update0(bufnr) + return + end + + handle_blame_info(bufnr, lnum, blame_info, opts) +end + +local update = async.create(1, debounce.throttle_by_id(update0)) + +--- @type fun(bufnr: integer) +local update_debounced + +function M.setup() + local group = api.nvim_create_augroup('gitsigns_blame', {}) + + local opts = config.current_line_blame_opts + update_debounced = debounce.debounce_trailing(opts.delay, update) + + for k, _ in pairs(cache) do + reset(k) + end + + if config.current_line_blame then + local events = { 'FocusGained', 'BufEnter', 'CursorMoved', 'CursorMovedI' } + if vim.fn.exists('#WinResized') == 1 then + -- For nvim 0.9+ + events[#events + 1] = 'WinResized' + end + + api.nvim_create_autocmd(events, { + group = group, + callback = function(args) + reset(args.buf) + update_debounced(args.buf) + end, + }) + + api.nvim_create_autocmd({ 'InsertEnter', 'FocusLost', 'BufLeave' }, { + group = group, + callback = function(args) + reset(args.buf) + end, + }) + + api.nvim_create_autocmd('OptionSet', { + group = group, + pattern = { 'fileformat', 'bomb', 'eol' }, + callback = function(args) + reset(args.buf) + end, + }) + + update_debounced(api.nvim_get_current_buf()) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/debounce.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/debounce.lua new file mode 100644 index 00000000..f0d5c3cc --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/debounce.lua @@ -0,0 +1,74 @@ +local uv = vim.loop + +local M = {} + +--- Debounces a function on the trailing edge. +--- +--- @generic F: function +--- @param ms number Timeout in ms +--- @param fn F Function to debounce +--- @param hash? integer|fun(...): any Function that determines id from arguments to fn +--- @return F Debounced function. +function M.debounce_trailing(ms, fn, hash) + local running = {} --- @type table + if type(hash) == 'number' then + local hash_i = hash + hash = function(...) + return select(hash_i, ...) + end + end + return function(...) + local id = hash and hash(...) or true + if running[id] == nil then + running[id] = assert(uv.new_timer()) + end + local timer = running[id] + local argv = { ... } + timer:start(ms, 0, function() + timer:stop() + running[id] = nil + fn(unpack(argv, 1, table.maxn(argv))) + end) + end +end + +--- Throttles a function using the first argument as an ID +--- +--- If function is already running then the function will be scheduled to run +--- again once the running call has finished. +--- +--- fn#1 _/‾\__/‾\_/‾\_____________________________ +--- throttled#1 _/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\/‾‾‾‾‾‾‾‾‾‾\____________ +-- +--- fn#2 ______/‾\___________/‾\___________________ +--- throttled#2 ______/‾‾‾‾‾‾‾‾‾‾\__/‾‾‾‾‾‾‾‾‾‾\__________ +--- +--- +--- @generic F: function +--- @param fn F Function to throttle +--- @param schedule? boolean +--- @return F throttled function. +function M.throttle_by_id(fn, schedule) + local scheduled = {} --- @type table + local running = {} --- @type table + return function(id, ...) + if scheduled[id] then + -- If fn is already scheduled, then drop + return + end + if not running[id] or schedule then + scheduled[id] = true + end + if running[id] then + return + end + while scheduled[id] do + scheduled[id] = nil + running[id] = true + fn(id, ...) + running[id] = nil + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/debug.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/debug.lua new file mode 100644 index 00000000..949e0120 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/debug.lua @@ -0,0 +1,62 @@ +local log = require('gitsigns.debug.log') + +local M = {} + +--- @param raw_item any +--- @param path string[] +--- @return any +local function process(raw_item, path) + --- @diagnostic disable-next-line:undefined-field + if path[#path] == vim.inspect.METATABLE then + return + elseif type(raw_item) == 'function' then + return + elseif type(raw_item) ~= 'table' then + return raw_item + end + --- @cast raw_item table + + local key = path[#path] + if + vim.tbl_contains({ + 'compare_text', + 'compare_text_head', + 'hunks', + 'hunks_staged', + 'staged_diffs', + }, key) + then + return { '...', length = #vim.tbl_keys(raw_item), head = raw_item[next(raw_item)] } + elseif key == 'blame' then + return { '...', length = #vim.tbl_keys(raw_item) } + end + + return raw_item +end + +--- @return any +function M.dump_cache() + -- TODO(lewis6991): hack: use package.loaded to avoid circular deps + local cache = (require('gitsigns.cache')).cache + --- @type string + local text = vim.inspect(cache, { process = process }) + vim.api.nvim_echo({ { text } }, false, {}) +end + +--- @param noecho boolean +--- @return string[]? +function M.debug_messages(noecho) + if noecho then + return log.messages + else + for _, m in ipairs(log.messages) do + vim.api.nvim_echo({ { m } }, false, {}) + end + end +end + +function M.clear_debug() + log.messages = {} +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/debug/log.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/debug/log.lua new file mode 100644 index 00000000..cce8e4ff --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/debug/log.lua @@ -0,0 +1,144 @@ +local M = { + debug_mode = false, + verbose = false, + messages = {}, --- @type string[] +} + +--- @param name string +--- @param lvl integer +--- @return any +local function getvarvalue(name, lvl) + lvl = lvl + 1 + local value --- @type any? + local found --- @type boolean? + + -- try local variables + local i = 1 + while true do + local n, v = debug.getlocal(lvl, i) + if not n then + break + end + if n == name then + value = v + found = true + end + i = i + 1 + end + if found then + return value + end + + -- try upvalues + local func = debug.getinfo(lvl).func + i = 1 + while true do + local n, v = debug.getupvalue(func, i) + if not n then + break + end + if n == name then + return v + end + i = i + 1 + end + + -- not found; get global + --- @diagnostic disable-next-line:deprecated + return getfenv(func)[name] +end + +--- @param lvl integer +--- @return {name:string, bufnr: integer} +local function get_context(lvl) + lvl = lvl + 1 + local ret = {} --- @type {name:string, bufnr: integer} + ret.name = getvarvalue('__FUNC__', lvl) + if not ret.name then + local name0 = debug.getinfo(lvl, 'n').name or '' + ret.name = name0:gsub('(.*)%d+$', '%1') + end + ret.bufnr = getvarvalue('bufnr', lvl) + or getvarvalue('_bufnr', lvl) + or getvarvalue('cbuf', lvl) + or getvarvalue('buf', lvl) + + return ret +end + +-- If called in a callback then make sure the callback defines a __FUNC__ +-- variable which can be used to identify the name of the function. +--- @param obj any +--- @param lvl integer +local function cprint(obj, lvl) + lvl = lvl + 1 + local msg = type(obj) == 'string' and obj or vim.inspect(obj) + local ctx = get_context(lvl) + local msg2 --- @type string + if ctx.bufnr then + msg2 = string.format('%s(%s): %s', ctx.name, ctx.bufnr, msg) + else + msg2 = string.format('%s: %s', ctx.name, msg) + end + table.insert(M.messages, msg2) +end + +function M.dprint(obj) + if not M.debug_mode then + return + end + cprint(obj, 2) +end + +function M.dprintf(obj, ...) + if not M.debug_mode then + return + end + cprint(obj:format(...), 2) +end + +function M.vprint(obj) + if not (M.debug_mode and M.verbose) then + return + end + cprint(obj, 2) +end + +function M.vprintf(obj, ...) + if not (M.debug_mode and M.verbose) then + return + end + cprint(obj:format(...), 2) +end + +local function eprint(msg, level) + local info = debug.getinfo(level + 2, 'Sl') + if info then + msg = string.format('(ERROR) %s(%d): %s', info.short_src, info.currentline, msg) + end + M.messages[#M.messages + 1] = debug.traceback(msg) + if M.debug_mode then + error(msg, 3) + end +end + +function M.eprint(msg) + eprint(msg, 1) +end + +function M.eprintf(fmt, ...) + eprint(fmt:format(...), 1) +end + +--- @param cond boolean +--- @param msg string +--- @return boolean +function M.assert(cond, msg) + if not cond then + eprint(msg, 1) + end + + return not cond +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diff.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diff.lua new file mode 100644 index 00000000..eb823de2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diff.lua @@ -0,0 +1,32 @@ +local config = require('gitsigns.config').config + +--- @alias Gitsigns.Difffn fun(fa: string[], fb: string[], linematch?: integer): Gitsigns.Hunk.Hunk[] + +--- @param a string[] +--- @param b string[] +--- @param linematch? boolean +--- @return Gitsigns.Hunk.Hunk[] hunks +return function(a, b, linematch) + -- -- Short circuit optimization + -- if not a or #a == 0 then + -- local Hunks = require('gitsigns.hunks') + -- local hunk = Hunks.create_hunk(0, 0, 1, #b) + -- hunk.added.lines = b + -- return { hunk } + -- end + + local diff_opts = config.diff_opts + local f --- @type Gitsigns.Difffn + if diff_opts.internal then + f = require('gitsigns.diff_int').run_diff + else + f = require('gitsigns.diff_ext').run_diff + end + + local linematch0 --- @type integer? + if linematch ~= false then + linematch0 = diff_opts.linematch + end + + return f(a, b, linematch0) +end diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diff_ext.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diff_ext.lua new file mode 100644 index 00000000..794a0683 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diff_ext.lua @@ -0,0 +1,80 @@ +local config = require('gitsigns.config').config +local git_diff = require('gitsigns.git').diff + +local gs_hunks = require('gitsigns.hunks') +local util = require('gitsigns.util') +local scheduler = require('gitsigns.async').scheduler + +local M = {} +-- Async function + +--- @param path string +--- @param text string[] +local function write_to_file(path, text) + local f, err = io.open(path, 'wb') + if f == nil then + error(err) + end + for _, l in ipairs(text) do + f:write(l) + f:write('\n') + end + f:close() +end + +--- @async +--- @param text_cmp string[] +--- @param text_buf string[] +--- @return Gitsigns.Hunk.Hunk[] +function M.run_diff(text_cmp, text_buf) + local results = {} --- @type Gitsigns.Hunk.Hunk[] + + -- tmpname must not be called in a callback + if vim.in_fast_event() then + scheduler() + end + + local file_buf = util.tmpname() + local file_cmp = util.tmpname() + + write_to_file(file_buf, text_buf) + write_to_file(file_cmp, text_cmp) + + -- Taken from gitgutter, diff.vim: + -- + -- If a file has CRLF line endings and git's core.autocrlf is true, the file + -- in git's object store will have LF line endings. Writing it out via + -- git-show will produce a file with LF line endings. + -- + -- If this last file is one of the files passed to git-diff, git-diff will + -- convert its line endings to CRLF before diffing -- which is what we want + -- but also by default outputs a warning on stderr. + -- + -- warning: LF will be replace by CRLF in . + -- The file will have its original line endings in your working directory. + -- + -- We can safely ignore the warning, we turn it off by passing the '-c + -- "core.safecrlf=false"' argument to git-diff. + + local opts = config.diff_opts + local out = git_diff(file_cmp, file_buf, opts.indent_heuristic, opts.algorithm) + + for _, line in ipairs(out) do + if vim.startswith(line, '@@') then + results[#results + 1] = gs_hunks.parse_diff_line(line) + elseif #results > 0 then + local r = results[#results] + if line:sub(1, 1) == '-' then + r.removed.lines[#r.removed.lines + 1] = line:sub(2) + elseif line:sub(1, 1) == '+' then + r.added.lines[#r.added.lines + 1] = line:sub(2) + end + end + end + + os.remove(file_buf) + os.remove(file_cmp) + return results +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diff_int.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diff_int.lua new file mode 100644 index 00000000..658cc0b8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diff_int.lua @@ -0,0 +1,191 @@ +local create_hunk = require('gitsigns.hunks').create_hunk +local config = require('gitsigns.config').config +local async = require('gitsigns.async') + +local M = {} + +--- @alias Gitsigns.Region {[1]:integer, [2]:string, [3]:integer, [4]:integer} + +--- @alias Gitsigns.RawHunk {[1]:integer, [2]:integer, [3]:integer, [4]:integer} +--- @alias Gitsigns.RawDifffn fun(a: string, b: string, linematch?: integer): Gitsigns.RawHunk[] + +--- @type Gitsigns.RawDifffn +local run_diff_xdl = function(a, b, linematch) + local opts = config.diff_opts + return vim.diff(a, b, { + result_type = 'indices', + algorithm = opts.algorithm, + indent_heuristic = opts.indent_heuristic, + ignore_whitespace = opts.ignore_whitespace, + ignore_whitespace_change = opts.ignore_whitespace_change, + ignore_whitespace_change_at_eol = opts.ignore_whitespace_change_at_eol, + ignore_blank_lines = opts.ignore_blank_lines, + linematch = linematch, + }) --[[@as Gitsigns.RawHunk[] ]] +end + +--- @type Gitsigns.RawDifffn +local run_diff_xdl_async = async.wrap( + 4, + --- @param a string + --- @param b string + --- @param linematch? integer + --- @param callback fun(hunks: Gitsigns.RawHunk[]) + function(a, b, linematch, callback) + local opts = config.diff_opts + local function toflag(f, pos) + return f and bit.lshift(1, pos) or 0 + end + + local flags = toflag(opts.indent_heuristic, 0) + + toflag(opts.ignore_whitespace, 1) + + toflag(opts.ignore_whitespace_change, 2) + + toflag(opts.ignore_whitespace_change_at_eol, 3) + + toflag(opts.ignore_blank_lines, 4) + + vim.loop + .new_work( + --- @param a0 string + --- @param b0 string + --- @param algorithm string + --- @param flags0 integer + --- @param linematch0 integer + --- @return string + function(a0, b0, algorithm, flags0, linematch0) + local function flagval(pos) + return bit.band(flags0, bit.lshift(1, pos)) ~= 0 + end + + --- @diagnostic disable-next-line:redundant-return-value + return vim.mpack.encode(vim.diff(a0, b0, { + result_type = 'indices', + algorithm = algorithm, + linematch = linematch0, + indent_heuristic = flagval(0), + ignore_whitespace = flagval(1), + ignore_whitespace_change = flagval(2), + ignore_whitespace_change_at_eol = flagval(3), + ignore_blank_lines = flagval(4), + })) + end, + --- @param r string + function(r) + callback(vim.mpack.decode(r) --[[@as Gitsigns.RawHunk[] ]]) + end + ) + :queue(a, b, opts.algorithm, flags, linematch) + end +) + +--- @param fa string[] +--- @param fb string[] +--- @param linematch? integer +--- @return Gitsigns.Hunk.Hunk[] +function M.run_diff(fa, fb, linematch) + local run_diff0 --- @type Gitsigns.RawDifffn + if config._threaded_diff and vim.is_thread then + run_diff0 = run_diff_xdl_async + else + run_diff0 = run_diff_xdl + end + + local a = table.concat(fa, '\n') + local b = table.concat(fb, '\n') + + local results = run_diff0(a, b, linematch) + + local hunks = {} --- @type Gitsigns.Hunk.Hunk[] + for _, r in ipairs(results) do + local rs, rc, as, ac = r[1], r[2], r[3], r[4] + local hunk = create_hunk(rs, rc, as, ac) + if rc > 0 then + for i = rs, rs + rc - 1 do + hunk.removed.lines[#hunk.removed.lines + 1] = fa[i] or '' + end + if rs + rc >= #fa and fa[#fa] ~= '' then + hunk.removed.no_nl_at_eof = true + end + end + if ac > 0 then + for i = as, as + ac - 1 do + hunk.added.lines[#hunk.added.lines + 1] = fb[i] or '' + end + if as + ac >= #fb and fb[#fb] ~= '' then + hunk.added.no_nl_at_eof = true + end + end + hunks[#hunks + 1] = hunk + end + + return hunks +end + +local gaps_between_regions = 5 + +--- @param hunks Gitsigns.Hunk.Hunk[] +--- @return Gitsigns.Hunk.Hunk[] +local function denoise_hunks(hunks) + -- Denoise the hunks + local ret = { hunks[1] } --- @type Gitsigns.Hunk.Hunk[] + for j = 2, #hunks do + local h, n = ret[#ret], hunks[j] + if not h or not n then + break + end + if n.added.start - h.added.start - h.added.count < gaps_between_regions then + h.added.count = n.added.start + n.added.count - h.added.start + h.removed.count = n.removed.start + n.removed.count - h.removed.start + + if h.added.count > 0 or h.removed.count > 0 then + h.type = 'change' + end + else + ret[#ret + 1] = n + end + end + return ret +end + +--- @param removed string[] +--- @param added string[] +--- @return Gitsigns.Region[] removed +--- @return Gitsigns.Region[] added +function M.run_word_diff(removed, added) + local adds = {} --- @type Gitsigns.Region[] + local rems = {} --- @type Gitsigns.Region[] + + if #removed ~= #added then + return rems, adds + end + + for i = 1, #removed do + -- pair lines by position + local a = table.concat(vim.split(removed[i], ''), '\n') + local b = table.concat(vim.split(added[i], ''), '\n') + + local hunks = {} --- @type Gitsigns.Hunk.Hunk[] + for _, r in ipairs(run_diff_xdl(a, b)) do + local rs, rc, as, ac = r[1], r[2], r[3], r[4] + + -- Balance of the unknown offset done in hunk_func + if rc == 0 then + rs = rs + 1 + end + if ac == 0 then + as = as + 1 + end + + hunks[#hunks + 1] = create_hunk(rs, rc, as, ac) + end + + hunks = denoise_hunks(hunks) + + for _, h in ipairs(hunks) do + adds[#adds + 1] = { i, h.type, h.added.start, h.added.start + h.added.count } + rems[#rems + 1] = { i, h.type, h.removed.start, h.removed.start + h.removed.count } + end + end + return rems, adds +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diffthis.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diffthis.lua new file mode 100644 index 00000000..55e70bc8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/diffthis.lua @@ -0,0 +1,234 @@ +local api = vim.api + +local async = require('gitsigns.async') +local cache = require('gitsigns.cache').cache +local util = require('gitsigns.util') +local manager = require('gitsigns.manager') +local message = require('gitsigns.message') +local Status = require('gitsigns.status') + +local throttle_by_id = require('gitsigns.debounce').throttle_by_id + +local M = {} + +--- @async +--- @param bufnr integer +--- @param dbufnr integer +--- @param base string? +local function bufread(bufnr, dbufnr, base) + local bcache = cache[bufnr] + base = util.norm_base(base) + local text --- @type string[] + if base == bcache.git_obj.revision then + text = assert(bcache.compare_text) + else + local err + text, err = bcache.git_obj:get_show_text(base) + if err then + error(err, 2) + end + async.scheduler() + if not api.nvim_buf_is_valid(bufnr) then + return + end + end + + vim.bo[dbufnr].fileformat = vim.bo[bufnr].fileformat + vim.bo[dbufnr].filetype = vim.bo[bufnr].filetype + vim.bo[dbufnr].bufhidden = 'wipe' + + local modifiable = vim.bo[dbufnr].modifiable + vim.bo[dbufnr].modifiable = true + Status:update(dbufnr, { head = base }) + + util.set_lines(dbufnr, 0, -1, text) + + vim.bo[dbufnr].modifiable = modifiable + vim.bo[dbufnr].modified = false + require('gitsigns.attach').attach(dbufnr, nil, 'BufReadCmd') +end + +--- @param bufnr integer +--- @param dbufnr integer +--- @param base string? +local bufwrite = async.create(3, function(bufnr, dbufnr, base) + local bcache = cache[bufnr] + local buftext = util.buf_lines(dbufnr) + base = util.norm_base(base) + bcache.git_obj:stage_lines(buftext) + async.scheduler() + if not api.nvim_buf_is_valid(bufnr) then + return + end + vim.bo[dbufnr].modified = false + -- If diff buffer base matches the git_obj revision then also update the + -- signs. + if base == bcache.git_obj.revision then + bcache.compare_text = buftext + manager.update(bufnr) + end +end) + +--- @async +--- Create a gitsigns buffer for a certain revision of a file +--- @param bufnr integer +--- @param base string? +--- @return string? buf Buffer name +local function create_show_buf(bufnr, base) + local bcache = assert(cache[bufnr]) + base = util.norm_base(base) + + local bufname = bcache:get_rev_bufname(base) + + if util.bufexists(bufname) then + return bufname + end + + local dbuf = api.nvim_create_buf(false, true) + api.nvim_buf_set_name(dbuf, bufname) + + local ok, err = pcall(bufread, bufnr, dbuf, base) + if not ok then + message.error(err --[[@as string]]) + async.scheduler() + api.nvim_buf_delete(dbuf, { force = true }) + return + end + + -- allow editing the index revision + if not bcache.git_obj.revision then + vim.bo[dbuf].buftype = 'acwrite' + + api.nvim_create_autocmd('BufReadCmd', { + group = 'gitsigns', + buffer = dbuf, + callback = function() + async.run(bufread, bufnr, dbuf, base) + end, + }) + + api.nvim_create_autocmd('BufWriteCmd', { + group = 'gitsigns', + buffer = dbuf, + callback = function() + bufwrite(bufnr, dbuf, base) + end, + }) + else + vim.bo[dbuf].buftype = 'nowrite' + vim.bo[dbuf].modifiable = false + end + + return bufname +end + +--- @class Gitsigns.DiffthisOpts +--- @field vertical boolean +--- @field split string + +--- @async +--- @param base string? +--- @param opts? Gitsigns.DiffthisOpts +local function diffthis_rev(base, opts) + local bufnr = api.nvim_get_current_buf() + + local bufname = create_show_buf(bufnr, base) + if not bufname then + return + end + + opts = opts or {} + + vim.cmd(table.concat({ + 'keepalt', + opts.split or 'aboveleft', + opts.vertical and 'vertical' or '', + 'diffsplit', + bufname, + }, ' ')) +end + +--- @param base string? +--- @param opts Gitsigns.DiffthisOpts +M.diffthis = async.create(2, function(base, opts) + if vim.wo.diff then + return + end + + local bufnr = api.nvim_get_current_buf() + local bcache = cache[bufnr] + if not bcache then + return + end + + local cwin = api.nvim_get_current_win() + if not base and bcache.git_obj.has_conflicts then + diffthis_rev(':2', opts) + api.nvim_set_current_win(cwin) + opts.split = 'belowright' + diffthis_rev(':3', opts) + else + diffthis_rev(base, opts) + end + api.nvim_set_current_win(cwin) +end) + +--- @param bufnr integer +--- @param base string +M.show = async.create(2, function(bufnr, base) + local bufname = create_show_buf(bufnr, base) + if not bufname then + return + end + + vim.cmd.edit(bufname) +end) + +--- @param bufnr integer +--- @return boolean +local function should_reload(bufnr) + if not vim.bo[bufnr].modified then + return true + end + local response --- @type string? + while not vim.tbl_contains({ 'O', 'L' }, response) do + response = async.wait(2, vim.ui.input, { + prompt = 'Warning: The git index has changed and the buffer was changed as well. [O]K, (L)oad File:', + }) + end + return response == 'L' +end + +-- This function needs to be throttled as there is a call to vim.ui.input +--- @param bufnr integer +M.update = throttle_by_id(async.create(1, function(bufnr) + if not vim.wo.diff then + return + end + + local bcache = cache[bufnr] + + -- Note this will be the bufname for the currently set base + -- which are the only ones we want to update + local bufname = bcache:get_rev_bufname() + + for _, w in ipairs(api.nvim_list_wins()) do + if api.nvim_win_is_valid(w) then + local b = api.nvim_win_get_buf(w) + local bname = api.nvim_buf_get_name(b) + local is_fugitive_diff_window = vim.startswith(bname, 'fugitive://') + and vim.fn.exists('*FugitiveParse') + and vim.fn.FugitiveParse(bname)[1] ~= ':' + if bname == bufname or is_fugitive_diff_window then + if should_reload(b) then + api.nvim_buf_call(b, function() + vim.cmd.doautocmd('BufReadCmd') + vim.cmd.diffthis() + end) + end + end + end + end +end)) + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/git.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/git.lua new file mode 100644 index 00000000..d506132e --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/git.lua @@ -0,0 +1,905 @@ +local async = require('gitsigns.async') +local scheduler = require('gitsigns.async').scheduler + +local log = require('gitsigns.debug.log') +local util = require('gitsigns.util') +local system = require('gitsigns.system').system + +local gs_config = require('gitsigns.config') +local config = gs_config.config + +local uv = vim.uv or vim.loop + +local dprint = log.dprint +local dprintf = log.dprintf +local error_once = require('gitsigns.message').error_once + +local check_version = require('gitsigns.git.version').check + +local M = {} + +--- @type fun(cmd: string[], opts?: vim.SystemOpts): vim.SystemCompleted +local asystem = async.wrap(3, system) + +--- @param file string +--- @return boolean +local function in_git_dir(file) + for _, p in ipairs(vim.split(file, util.path_sep)) do + if p == '.git' then + return true + end + end + return false +end + +--- @class Gitsigns.GitObj +--- @field file string +--- @field encoding string +--- @field i_crlf boolean Object has crlf +--- @field w_crlf boolean Working copy has crlf +--- @field mode_bits string +--- @field revision? string Revision the object is tracking against. Nil for index +--- @field object_name string The fixed object name to use. +--- @field relpath string +--- @field orig_relpath? string Use for tracking moved files +--- @field repo Gitsigns.Repo +--- @field has_conflicts? boolean +local Obj = {} + +M.Obj = Obj + +--- @class Gitsigns.RepoInfo +--- @field gitdir string +--- @field toplevel string +--- @field detached boolean +--- @field abbrev_head string + +--- @class Gitsigns.Repo : Gitsigns.RepoInfo +--- @field username string +local Repo = {} +M.Repo = Repo + +--- @class Gitsigns.Git.JobSpec : vim.SystemOpts +--- @field command? string +--- @field ignore_error? boolean + +--- @async +--- @param args string[] +--- @param spec? Gitsigns.Git.JobSpec +--- @return string[] stdout, string? stderr +local function git_command(args, spec) + spec = spec or {} + + local cmd = { + spec.command or 'git', + '--no-pager', + '--no-optional-locks', + '--literal-pathspecs', + '-c', + 'gc.auto=0', -- Disable auto-packing which emits messages to stderr + unpack(args), + } + + if spec.text == nil then + spec.text = true + end + + -- Fix #895. Only needed for Nvim 0.9 and older + spec.clear_env = true + + --- @type vim.SystemCompleted + local obj = asystem(cmd, spec) + local stdout = obj.stdout + local stderr = obj.stderr + + if not spec.ignore_error and obj.code > 0 then + local cmd_str = table.concat(cmd, ' ') + log.eprintf("Received exit code %d when running command\n'%s':\n%s", obj.code, cmd_str, stderr) + end + + local stdout_lines = vim.split(stdout or '', '\n') + + if spec.text then + -- If stdout ends with a newline, then remove the final empty string after + -- the split + if stdout_lines[#stdout_lines] == '' then + stdout_lines[#stdout_lines] = nil + end + end + + if log.verbose then + log.vprintf('%d lines:', #stdout_lines) + for i = 1, math.min(10, #stdout_lines) do + log.vprintf('\t%s', stdout_lines[i]) + end + end + + if stderr == '' then + stderr = nil + end + + return stdout_lines, stderr +end + +--- @async +--- @param file_cmp string +--- @param file_buf string +--- @param indent_heuristic? boolean +--- @param diff_algo string +--- @return string[] stdout, string? stderr +function M.diff(file_cmp, file_buf, indent_heuristic, diff_algo) + return git_command({ + '-c', + 'core.safecrlf=false', + 'diff', + '--color=never', + '--' .. (indent_heuristic and '' or 'no-') .. 'indent-heuristic', + '--diff-algorithm=' .. diff_algo, + '--patch-with-raw', + '--unified=0', + file_cmp, + file_buf, + }, { + -- git-diff implies --exit-code + ignore_error = true, + }) +end + +--- @async +--- @param gitdir string +--- @param head_str string +--- @param cwd string +--- @param cmd? string +--- @return string +local function process_abbrev_head(gitdir, head_str, cwd, cmd) + if not gitdir then + return head_str + end + if head_str == 'HEAD' then + local short_sha = git_command({ 'rev-parse', '--short', 'HEAD' }, { + command = cmd, + ignore_error = true, + cwd = cwd, + })[1] or '' + if log.debug_mode and short_sha ~= '' then + short_sha = 'HEAD' + end + if + util.path_exists(gitdir .. '/rebase-merge') + or util.path_exists(gitdir .. '/rebase-apply') + then + return short_sha .. '(rebasing)' + end + return short_sha + end + return head_str +end + +local has_cygpath = jit and jit.os == 'Windows' and vim.fn.executable('cygpath') == 1 + +local cygpath_convert ---@type fun(path: string): string + +if has_cygpath then + cygpath_convert = function(path) + --- @type vim.SystemCompleted + local obj = asystem({ 'cygpath', '-aw', path }) + return obj.stdout + end +end + +--- @param path string +--- @return string +local function normalize_path(path) + if path and has_cygpath and not uv.fs_stat(path) then + -- If on windows and path isn't recognizable as a file, try passing it + -- through cygpath + path = cygpath_convert(path) + end + return path +end + +--- @async +--- @param cwd string +--- @param cmd? string +--- @param gitdir? string +--- @param toplevel? string +--- @return Gitsigns.RepoInfo +function M.get_repo_info(cwd, cmd, gitdir, toplevel) + -- Does git rev-parse have --absolute-git-dir, added in 2.13: + -- https://public-inbox.org/git/20170203024829.8071-16-szeder.dev@gmail.com/ + local has_abs_gd = check_version({ 2, 13 }) + local git_dir_opt = has_abs_gd and '--absolute-git-dir' or '--git-dir' + + -- Wait for internal scheduler to settle before running command (#215) + scheduler() + + local args = {} + + if gitdir then + vim.list_extend(args, { '--git-dir', gitdir }) + end + + if toplevel then + vim.list_extend(args, { '--work-tree', toplevel }) + end + + vim.list_extend(args, { + 'rev-parse', + '--show-toplevel', + git_dir_opt, + '--abbrev-ref', + 'HEAD', + }) + + local results = git_command(args, { + command = cmd, + ignore_error = true, + cwd = toplevel or cwd, + }) + + local toplevel_r = normalize_path(results[1]) + local gitdir_r = normalize_path(results[2]) + + if gitdir_r and not has_abs_gd then + gitdir_r = assert(uv.fs_realpath(gitdir_r)) + end + + return { + toplevel = toplevel_r, + gitdir = gitdir_r, + abbrev_head = process_abbrev_head(gitdir_r, results[3], cwd, cmd), + detached = toplevel_r and gitdir_r ~= toplevel_r .. '/.git', + } +end + +-------------------------------------------------------------------------------- +-- Git repo object methods +-------------------------------------------------------------------------------- + +--- Run git command the with the objects gitdir and toplevel +--- @async +--- @param args string[] +--- @param spec? Gitsigns.Git.JobSpec +--- @return string[] stdout, string? stderr +function Repo:command(args, spec) + spec = spec or {} + spec.cwd = self.toplevel + + local args1 = { + '--git-dir', + self.gitdir, + } + + if self.detached then + vim.list_extend(args1, { '--work-tree', self.toplevel }) + end + + vim.list_extend(args1, args) + + return git_command(args1, spec) +end + +--- @return string[] +function Repo:files_changed() + --- @type string[] + local results = self:command({ 'status', '--porcelain', '--ignore-submodules' }) + + local ret = {} --- @type string[] + for _, line in ipairs(results) do + if line:sub(1, 2):match('^.M') then + ret[#ret + 1] = line:sub(4, -1) + end + end + return ret +end + +--- @param encoding string +--- @return boolean +local function iconv_supported(encoding) + -- TODO(lewis6991): needs https://github.com/neovim/neovim/pull/21924 + if vim.startswith(encoding, 'utf-16') then + return false + elseif vim.startswith(encoding, 'utf-32') then + return false + end + return true +end + +--- Get version of file in the index, return array lines +--- @param object string +--- @param encoding? string +--- @return string[] stdout, string? stderr +function Repo:get_show_text(object, encoding) + local stdout, stderr = self:command({ 'show', object }, { text = false, ignore_error = true }) + + if encoding and encoding ~= 'utf-8' and iconv_supported(encoding) then + for i, l in ipairs(stdout) do + --- @diagnostic disable-next-line:param-type-mismatch + stdout[i] = vim.iconv(l, encoding, 'utf-8') + end + end + + return stdout, stderr +end + +--- @async +function Repo:update_abbrev_head() + self.abbrev_head = M.get_repo_info(self.toplevel).abbrev_head +end + +--- @private +--- @param dir string +--- @param gitdir? string +--- @param toplevel? string +function Repo:try_yadm(dir, gitdir, toplevel) + if not config.yadm.enable or self.gitdir then + return + end + + local home = os.getenv('HOME') + + if not home or not vim.startswith(dir, home) then + return + end + + if #git_command({ 'ls-files', dir }, { command = 'yadm' }) == 0 then + return + end + + M.get_repo_info(dir, 'yadm', gitdir, toplevel) + local yadm_info = M.get_repo_info(dir, 'yadm', gitdir, toplevel) + for k, v in + pairs(yadm_info --[[@as table]]) + do + ---@diagnostic disable-next-line:no-unknown + self[k] = v + end +end + +--- @async +--- @param dir string +--- @param gitdir? string +--- @param toplevel? string +--- @return Gitsigns.Repo +function Repo.new(dir, gitdir, toplevel) + local self = setmetatable({}, { __index = Repo }) + + self.username = git_command({ 'config', 'user.name' }, { ignore_error = true })[1] + local info = M.get_repo_info(dir, nil, gitdir, toplevel) + for k, v in + pairs(info --[[@as table]]) + do + ---@diagnostic disable-next-line:no-unknown + self[k] = v + end + + self:try_yadm(dir, gitdir, toplevel) + + return self +end + +-------------------------------------------------------------------------------- +-- Git object methods +-------------------------------------------------------------------------------- + +--- Run git command the with the objects gitdir and toplevel +--- @param args string[] +--- @param spec? Gitsigns.Git.JobSpec +--- @return string[] stdout, string? stderr +function Obj:command(args, spec) + return self.repo:command(args, spec) +end + +--- @param revision? string +function Obj:update_revision(revision) + revision = util.norm_base(revision) + self.revision = revision + self:update() +end + +--- @param update_relpath? boolean +--- @param silent? boolean +--- @return boolean +function Obj:update(update_relpath, silent) + local old_object_name = self.object_name + local props = self:file_info(self.file, silent) + + if update_relpath then + self.relpath = props.relpath + end + self.object_name = props.object_name + self.mode_bits = props.mode_bits + self.has_conflicts = props.has_conflicts + self.i_crlf = props.i_crlf + self.w_crlf = props.w_crlf + + return old_object_name ~= self.object_name +end + +--- @class (exact) Gitsigns.FileInfo +--- @field relpath string +--- @field i_crlf? boolean +--- @field w_crlf? boolean +--- @field mode_bits? string +--- @field object_name? string +--- @field has_conflicts? true + +--- @param file? string +--- @param silent? boolean +--- @return Gitsigns.FileInfo +function Obj:file_info(file, silent) + if self.revision and not vim.startswith(self.revision, ':') then + return self:file_info_tree(file, silent) + else + return self:file_info_index(file, silent) + end +end + +--- @private +--- @param file? string +--- @param silent? boolean +--- @return Gitsigns.FileInfo +function Obj:file_info_index(file, silent) + local has_eol = check_version({ 2, 9 }) + + local cmd = { + '-c', + 'core.quotepath=off', + 'ls-files', + '--stage', + '--others', + '--exclude-standard', + } + + if has_eol then + cmd[#cmd + 1] = '--eol' + end + + cmd[#cmd + 1] = file or self.file + + local results, stderr = self:command(cmd, { ignore_error = true }) + + if stderr and not silent then + -- ignore_error for the cases when we run: + -- git ls-files --others exists/nonexist + if not stderr:match('^warning: could not open directory .*: No such file or directory') then + log.eprint(stderr) + end + end + + local relpath_idx = has_eol and 2 or 1 + + local result = {} + for _, line in ipairs(results) do + local parts = vim.split(line, '\t') + if #parts > relpath_idx then -- tracked file + local attrs = vim.split(parts[1], '%s+') + local stage = tonumber(attrs[3]) + if stage <= 1 then + result.mode_bits = attrs[1] + result.object_name = attrs[2] + else + result.has_conflicts = true + end + + if has_eol then + result.relpath = parts[3] + local eol = vim.split(parts[2], '%s+') + result.i_crlf = eol[1] == 'i/crlf' + result.w_crlf = eol[2] == 'w/crlf' + else + result.relpath = parts[2] + end + else -- untracked file + result.relpath = parts[relpath_idx] + end + end + + return result +end + +--- @private +--- @param file? string +--- @param silent? boolean +--- @return Gitsigns.FileInfo +function Obj:file_info_tree(file, silent) + local results, stderr = self:command({ + '-c', + 'core.quotepath=off', + 'ls-tree', + self.revision, + file or self.file, + }, { ignore_error = true }) + + if stderr then + if not silent then + log.eprint(stderr) + end + return {} + end + + local info, relpath = unpack(vim.split(results[1], '\t')) + local mode_bits, objtype, object_name = unpack(vim.split(info, '%s+')) + assert(objtype == 'blob') + + return { + mode_bits = mode_bits, + object_name = object_name, + relpath = relpath, + } +end + +--- @param revision? string +--- @return string[] stdout, string? stderr +function Obj:get_show_text(revision) + if revision and not self.relpath then + dprint('no relpath') + return {} + end + + local object = revision and (revision .. ':' .. self.relpath) or self.object_name + + if not object then + dprint('no revision or object_name') + return { '' } + end + + local stdout, stderr = self.repo:get_show_text(object, self.encoding) + + if not self.i_crlf and self.w_crlf then + -- Add cr + -- Do not add cr to the newline at the end of file + for i = 1, #stdout - 1 do + stdout[i] = stdout[i] .. '\r' + end + end + + return stdout, stderr +end + +local function autocmd_changed(file) + vim.schedule(function() + vim.api.nvim_exec_autocmds('User', { + pattern = 'GitSignsChanged', + modeline = false, + data = { file = file }, + }) + end) +end + +function Obj:unstage_file() + self:command({ 'reset', self.file }) + autocmd_changed(self.file) +end + +--- @class Gitsigns.CommitInfo +--- @field author string +--- @field author_mail string +--- @field author_time integer +--- @field author_tz string +--- @field committer string +--- @field committer_mail string +--- @field committer_time integer +--- @field committer_tz string +--- @field summary string +--- @field sha string +--- @field abbrev_sha string +--- @field boundary? true + +--- @class Gitsigns.BlameInfoPublic: Gitsigns.BlameInfo, Gitsigns.CommitInfo +--- @field body? string[] +--- @field hunk_no? integer +--- @field num_hunks? integer +--- @field hunk? string[] +--- @field hunk_head? string + +--- @class Gitsigns.BlameInfo +--- @field orig_lnum integer +--- @field final_lnum integer +--- @field commit Gitsigns.CommitInfo +--- @field filename string +--- @field previous_filename? string +--- @field previous_sha? string + +local NOT_COMMITTED = { + author = 'Not Committed Yet', + author_mail = '', + committer = 'Not Committed Yet', + committer_mail = '', +} + +--- @param file string +--- @return Gitsigns.CommitInfo +function M.not_commited(file) + local time = os.time() + return { + sha = string.rep('0', 40), + abbrev_sha = string.rep('0', 8), + author = 'Not Committed Yet', + author_mail = '', + author_tz = '+0000', + author_time = time, + committer = 'Not Committed Yet', + committer_time = time, + committer_mail = '', + committer_tz = '+0000', + summary = 'Version of ' .. file, + } +end + +---@param x any +---@return integer +local function asinteger(x) + return assert(tonumber(x)) +end + +--- @param lines string[] +--- @param lnum? integer +--- @param opts? Gitsigns.BlameOpts +--- @return table? +function Obj:run_blame(lines, lnum, opts) + local ret = {} --- @type table + + if not self.object_name or self.repo.abbrev_head == '' then + -- As we support attaching to untracked files we need to return something if + -- the file isn't isn't tracked in git. + -- If abbrev_head is empty, then assume the repo has no commits + local commit = M.not_commited(self.file) + for i in ipairs(lines) do + ret[i] = { + orig_lnum = 0, + final_lnum = i, + commit = commit, + filename = self.file, + } + end + return ret + end + + local args = { 'blame', '--contents', '-', '--incremental' } + + opts = opts or {} + + if opts.ignore_whitespace then + args[#args + 1] = '-w' + end + + if lnum then + vim.list_extend(args, { '-L', lnum .. ',+1' }) + end + + if opts.extra_opts then + vim.list_extend(args, opts.extra_opts) + end + + local ignore_file = self.repo.toplevel .. '/.git-blame-ignore-revs' + if uv.fs_stat(ignore_file) then + vim.list_extend(args, { '--ignore-revs-file', ignore_file }) + end + + args[#args + 1] = opts.rev + args[#args + 1] = '--' + args[#args + 1] = self.file + + local results, stderr = self:command(args, { stdin = lines, ignore_error = true }) + if stderr then + error_once('Error running git-blame: ' .. stderr) + return + end + + if #results == 0 then + return + end + + local commits = {} --- @type table + local i = 1 + + while i <= #results do + --- @param pat? string + --- @return string + local function get(pat) + local l = assert(results[i]) + i = i + 1 + if pat then + return l:match(pat) + end + return l + end + + local function peek(pat) + local l = results[i] + if l and pat then + return l:match(pat) + end + return l + end + + local sha, orig_lnum_str, final_lnum_str, size_str = get('(%x+) (%d+) (%d+) (%d+)') + local orig_lnum = asinteger(orig_lnum_str) + local final_lnum = asinteger(final_lnum_str) + local size = asinteger(size_str) + + if peek():match('^author ') then + --- @type table + local commit = { + sha = sha, + abbrev_sha = sha:sub(1, 8), + } + + -- filename terminates the entry + while peek() and not (peek():match('^filename ') or peek():match('^previous ')) do + local l = get() + local key, value = l:match('^([^%s]+) (.*)') + if key then + if vim.endswith(key, '_time') then + value = tonumber(value) + end + key = key:gsub('%-', '_') --- @type string + commit[key] = value + else + commit[l] = true + if l ~= 'boundary' then + dprintf("Unknown tag: '%s'", l) + end + end + end + + -- New in git 2.41: + -- The output given by "git blame" that attributes a line to contents + -- taken from the file specified by the "--contents" option shows it + -- differently from a line attributed to the working tree file. + if + commit.author_mail == '' + or commit.author_mail == 'External file (--contents)' + then + commit = vim.tbl_extend('force', commit, NOT_COMMITTED) + end + commits[sha] = commit + end + + local previous_sha, previous_filename = peek():match('^previous (%x+) (.*)') + if previous_sha then + get() + end + + local filename = assert(get():match('^filename (.*)')) + + for j = 0, size - 1 do + ret[final_lnum + j] = { + final_lnum = final_lnum + j, + orig_lnum = orig_lnum + j, + commit = commits[sha], + filename = filename, + previous_filename = previous_filename, + previous_sha = previous_sha, + } + end + end + + return ret +end + +--- @param obj Gitsigns.GitObj +local function ensure_file_in_index(obj) + if obj.object_name and not obj.has_conflicts then + return + end + + if not obj.object_name then + -- If there is no object_name then it is not yet in the index so add it + obj:command({ 'add', '--intent-to-add', obj.file }) + else + -- Update the index with the common ancestor (stage 1) which is what bcache + -- stores + local info = string.format('%s,%s,%s', obj.mode_bits, obj.object_name, obj.relpath) + obj:command({ 'update-index', '--add', '--cacheinfo', info }) + end + + obj:update() +end + +--- Stage 'lines' as the entire contents of the file +--- @param lines string[] +function Obj:stage_lines(lines) + local stdout = self:command({ + 'hash-object', + '-w', + '--path', + self.relpath, + '--stdin', + }, { stdin = lines }) + + local new_object = stdout[1] + + self:command({ + 'update-index', + '--cacheinfo', + string.format('%s,%s,%s', self.mode_bits, new_object, self.relpath), + }) + + autocmd_changed(self.file) +end + +--- @param hunks Gitsigns.Hunk.Hunk[] +--- @param invert? boolean +function Obj:stage_hunks(hunks, invert) + ensure_file_in_index(self) + + local gs_hunks = require('gitsigns.hunks') + + local patch = gs_hunks.create_patch(self.relpath, hunks, self.mode_bits, invert) + + if not self.i_crlf and self.w_crlf then + -- Remove cr + for i = 1, #patch do + patch[i] = patch[i]:gsub('\r$', '') + end + end + + self:command({ + 'apply', + '--whitespace=nowarn', + '--cached', + '--unidiff-zero', + '-', + }, { + stdin = patch, + }) + + autocmd_changed(self.file) +end + +--- @return string? +function Obj:has_moved() + local out = self:command({ 'diff', '--name-status', '-C', '--cached' }) + local orig_relpath = self.orig_relpath or self.relpath + for _, l in ipairs(out) do + local parts = vim.split(l, '%s+') + if #parts == 3 then + local orig, new = parts[2], parts[3] + if orig_relpath == orig then + self.orig_relpath = orig_relpath + self.relpath = new + self.file = self.repo.toplevel .. '/' .. new + return new + end + end + end +end + +--- @param file string +--- @param revision string? +--- @param encoding string +--- @param gitdir string? +--- @param toplevel string? +--- @return Gitsigns.GitObj? +function Obj.new(file, revision, encoding, gitdir, toplevel) + if in_git_dir(file) then + dprint('In git dir') + return nil + end + local self = setmetatable({}, { __index = Obj }) + + if not vim.startswith(file, '/') and toplevel then + file = toplevel .. util.path_sep .. file + end + + self.file = file + self.revision = util.norm_base(revision) + self.encoding = encoding + self.repo = Repo.new(util.dirname(file), gitdir, toplevel) + + if not self.repo.gitdir then + dprint('Not in git repo') + return nil + end + + -- When passing gitdir and toplevel, suppress stderr when resolving the file + local silent = gitdir ~= nil and toplevel ~= nil + + self:update(true, silent) + + return self +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/git/version.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/git/version.lua new file mode 100644 index 00000000..fedcc954 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/git/version.lua @@ -0,0 +1,101 @@ +local async = require('gitsigns.async') +local gs_config = require('gitsigns.config') + +local log = require('gitsigns.debug.log') +local err = require('gitsigns.message').error +local system = require('gitsigns.system').system + +local M = {} + +--- @type fun(cmd: string[], opts?: vim.SystemOpts): vim.SystemCompleted +local asystem = async.wrap(3, system) + +--- @class (exact) Gitsigns.Version +--- @field major integer +--- @field minor integer +--- @field patch integer + +--- @param version string +--- @return Gitsigns.Version +local function parse_version(version) + assert(version:match('%d+%.%d+%.%w+'), 'Invalid git version: ' .. version) + local ret = {} + local parts = vim.split(version, '%.') + ret.major = assert(tonumber(parts[1])) + ret.minor = assert(tonumber(parts[2])) + + if parts[3] == 'GIT' then + ret.patch = 0 + else + local patch_ver = vim.split(parts[3], '-') + ret.patch = assert(tonumber(patch_ver[1])) + end + + return ret +end + +local function set_version() + local version = gs_config.config._git_version + if version ~= 'auto' then + local ok, ret = pcall(parse_version, version) + if ok then + M.version = ret + else + err(ret --[[@as string]]) + end + return + end + + --- @type vim.SystemCompleted + local obj = asystem({ 'git', '--version' }) + async.scheduler() + + local line = vim.split(obj.stdout or '', '\n')[1] + if not line then + err("Unable to detect git version as 'git --version' failed to return anything") + log.eprint(obj.stderr) + return + end + + -- Sometime 'git --version' returns an empty string (#948) + if log.assert(type(line) == 'string', 'Unexpected output: ' .. line) then + return + end + + if log.assert(vim.startswith(line, 'git version'), 'Unexpected output: ' .. line) then + return + end + + local parts = vim.split(line, '%s+') + M.version = parse_version(parts[3]) +end + +--- Usage: check_version{2,3} +--- @param version {[1]: integer, [2]:integer, [3]:integer}? +--- @return boolean +function M.check(version) + if not M.version then + set_version() + end + + if not M.version then + return false + end + + if not version then + return false + end + + if M.version.major < version[1] then + return false + end + if version[2] and M.version.minor < version[2] then + return false + end + if version[3] and M.version.patch < version[3] then + return false + end + return true +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/highlight.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/highlight.lua new file mode 100644 index 00000000..e4bf8cd1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/highlight.lua @@ -0,0 +1,342 @@ +local api = vim.api + +--- @class Gitsigns.Hldef +--- @field [integer] string +--- @field desc string +--- @field hidden boolean +--- @field fg_factor number + +local nvim10 = vim.fn.has('nvim-0.10') > 0 + +local M = {} + +--- Use array of dict so we can iterate deterministically +--- Export for docgen +--- @type table[] +M.hls = { + { + GitSignsAdd = { + 'GitGutterAdd', + 'SignifySignAdd', + 'DiffAddedGutter', + nvim10 and 'Added' or 'diffAdded', + 'DiffAdd', + desc = "Used for the text of 'add' signs.", + }, + }, + + { + GitSignsChange = { + 'GitGutterChange', + 'SignifySignChange', + 'DiffModifiedGutter', + nvim10 and 'Changed' or 'diffChanged', + 'DiffChange', + desc = "Used for the text of 'change' signs.", + }, + }, + + { + GitSignsDelete = { + 'GitGutterDelete', + 'SignifySignDelete', + 'DiffRemovedGutter', + nvim10 and 'Removed' or 'diffRemoved', + 'DiffDelete', + desc = "Used for the text of 'delete' signs.", + }, + }, + + { + GitSignsChangedelete = { + 'GitSignsChange', + desc = "Used for the text of 'changedelete' signs.", + }, + }, + + { GitSignsTopdelete = { 'GitSignsDelete', desc = "Used for the text of 'topdelete' signs." } }, + + { GitSignsUntracked = { 'GitSignsAdd', desc = "Used for the text of 'untracked' signs." } }, + + { + GitSignsAddNr = { + 'GitGutterAddLineNr', + 'GitSignsAdd', + desc = "Used for number column (when `config.numhl == true`) of 'add' signs.", + }, + }, + + { + GitSignsChangeNr = { + 'GitGutterChangeLineNr', + 'GitSignsChange', + desc = "Used for number column (when `config.numhl == true`) of 'change' signs.", + }, + }, + + { + GitSignsDeleteNr = { + 'GitGutterDeleteLineNr', + 'GitSignsDelete', + desc = "Used for number column (when `config.numhl == true`) of 'delete' signs.", + }, + }, + + { + GitSignsChangedeleteNr = { + 'GitSignsChangeNr', + desc = "Used for number column (when `config.numhl == true`) of 'changedelete' signs.", + }, + }, + + { + GitSignsTopdeleteNr = { + 'GitSignsDeleteNr', + desc = "Used for number column (when `config.numhl == true`) of 'topdelete' signs.", + }, + }, + + { + GitSignsUntrackedNr = { + 'GitSignsAddNr', + desc = "Used for number column (when `config.numhl == true`) of 'untracked' signs.", + }, + }, + + { + GitSignsAddLn = { + 'GitGutterAddLine', + 'SignifyLineAdd', + 'DiffAdd', + desc = "Used for buffer line (when `config.linehl == true`) of 'add' signs.", + }, + }, + + { + GitSignsChangeLn = { + 'GitGutterChangeLine', + 'SignifyLineChange', + 'DiffChange', + desc = "Used for buffer line (when `config.linehl == true`) of 'change' signs.", + }, + }, + + { + GitSignsChangedeleteLn = { + 'GitSignsChangeLn', + desc = "Used for buffer line (when `config.linehl == true`) of 'changedelete' signs.", + }, + }, + + { + GitSignsUntrackedLn = { + 'GitSignsAddLn', + desc = "Used for buffer line (when `config.linehl == true`) of 'untracked' signs.", + }, + }, + + -- Don't set GitSignsDeleteLn by default + -- {GitSignsDeleteLn = {}}, + + { GitSignsStagedAdd = { 'GitSignsAdd', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedChange = { 'GitSignsChange', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedDelete = { 'GitSignsDelete', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedChangedelete = { 'GitSignsChangedelete', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedTopdelete = { 'GitSignsTopdelete', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedAddNr = { 'GitSignsAddNr', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedChangeNr = { 'GitSignsChangeNr', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedDeleteNr = { 'GitSignsDeleteNr', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedChangedeleteNr = { 'GitSignsChangedeleteNr', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedTopdeleteNr = { 'GitSignsTopdeleteNr', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedAddLn = { 'GitSignsAddLn', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedChangeLn = { 'GitSignsChangeLn', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedDeleteLn = { 'GitSignsDeleteLn', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedChangedeleteLn = { 'GitSignsChangedeleteLn', fg_factor = 0.5, hidden = true } }, + { GitSignsStagedTopdeleteLn = { 'GitSignsTopdeleteLn', fg_factor = 0.5, hidden = true } }, + + { + GitSignsAddPreview = { + 'GitGutterAddLine', + 'SignifyLineAdd', + 'DiffAdd', + desc = 'Used for added lines in previews.', + }, + }, + + { + GitSignsDeletePreview = { + 'GitGutterDeleteLine', + 'SignifyLineDelete', + 'DiffDelete', + desc = 'Used for deleted lines in previews.', + }, + }, + + { GitSignsCurrentLineBlame = { 'NonText', desc = 'Used for current line blame.' } }, + + { + GitSignsAddInline = { + 'TermCursor', + desc = 'Used for added word diff regions in inline previews.', + }, + }, + + { + GitSignsDeleteInline = { + 'TermCursor', + desc = 'Used for deleted word diff regions in inline previews.', + }, + }, + + { + GitSignsChangeInline = { + 'TermCursor', + desc = 'Used for changed word diff regions in inline previews.', + }, + }, + + { + GitSignsAddLnInline = { + 'GitSignsAddInline', + desc = 'Used for added word diff regions when `config.word_diff == true`.', + }, + }, + + { + GitSignsChangeLnInline = { + 'GitSignsChangeInline', + desc = 'Used for changed word diff regions when `config.word_diff == true`.', + }, + }, + + { + GitSignsDeleteLnInline = { + 'GitSignsDeleteInline', + desc = 'Used for deleted word diff regions when `config.word_diff == true`.', + }, + }, + + -- Currently unused + -- {GitSignsAddLnVirtLn = {'GitSignsAddLn'}}, + -- {GitSignsChangeVirtLn = {'GitSignsChangeLn'}}, + -- {GitSignsAddLnVirtLnInLine = {'GitSignsAddLnInline', }}, + -- {GitSignsChangeVirtLnInLine = {'GitSignsChangeLnInline', }}, + + { + GitSignsDeleteVirtLn = { + 'GitGutterDeleteLine', + 'SignifyLineDelete', + 'DiffDelete', + desc = 'Used for deleted lines shown by inline `preview_hunk_inline()` or `show_deleted()`.', + }, + }, + + { + GitSignsDeleteVirtLnInLine = { + 'GitSignsDeleteLnInline', + desc = 'Used for word diff regions in lines shown by inline `preview_hunk_inline()` or `show_deleted()`.', + }, + }, + + { + GitSignsVirtLnum = { + 'GitSignsDeleteVirtLn', + desc = 'Used for line numbers in inline hunks previews.', + }, + }, +} + +---@param name string +---@return table +local function get_hl(name) + --- @diagnostic disable-next-line:deprecated + return api.nvim_get_hl_by_name(name, true) +end + +--- @param hl_name string +--- @return boolean +local function is_hl_set(hl_name) + -- TODO: this only works with `set termguicolors` + local exists, hl = pcall(get_hl, hl_name) + if not exists then + return false + end + local color = hl.foreground or hl.background or hl.reverse + return color ~= nil +end + +--- @param x? number +--- @param factor number +--- @return number? +local function cmul(x, factor) + if not x or factor == 1 then + return x + end + + local r = math.floor(x / 2 ^ 16) + local x1 = x - (r * 2 ^ 16) + local g = math.floor(x1 / 2 ^ 8) + local b = math.floor(x1 - (g * 2 ^ 8)) + return math.floor( + math.floor(r * factor) * 2 ^ 16 + math.floor(g * factor) * 2 ^ 8 + math.floor(b * factor) + ) +end + +local function dprintf(fmt, ...) + require('gitsigns.debug.log').dprintf(fmt, ...) +end + +--- @param hl string +--- @param hldef Gitsigns.Hldef +local function derive(hl, hldef) + for _, d in ipairs(hldef) do + if is_hl_set(d) then + dprintf('Deriving %s from %s', hl, d) + if hldef.fg_factor then + hldef.fg_factor = hldef.fg_factor or 1 + local dh = get_hl(d) + api.nvim_set_hl(0, hl, { + default = true, + fg = cmul(dh.foreground, hldef.fg_factor), + bg = dh.background, + }) + else + api.nvim_set_hl(0, hl, { default = true, link = d }) + end + return + end + end + if hldef[1] and not hldef.fg_factor then + -- No fallback found which is set. Just link to the first fallback + -- if there are no modifiers + dprintf('Deriving %s from %s', hl, hldef[1]) + api.nvim_set_hl(0, hl, { default = true, link = hldef[1] }) + else + dprintf('Could not derive %s', hl) + end +end + +--- Setup a GitSign* highlight by deriving it from other potentially present +--- highlights. +function M.setup_highlights() + for _, hlg in ipairs(M.hls) do + for hl, hldef in pairs(hlg) do + if is_hl_set(hl) then + -- Already defined + dprintf('Highlight %s is already defined', hl) + else + derive(hl, hldef) + end + end + end +end + +function M.setup() + M.setup_highlights() + api.nvim_create_autocmd('ColorScheme', { + group = 'gitsigns', + callback = M.setup_highlights, + }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/hunks.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/hunks.lua new file mode 100644 index 00000000..a9fc7d25 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/hunks.lua @@ -0,0 +1,482 @@ +local util = require('gitsigns.util') + +local min, max = math.min, math.max + +--- @alias Gitsigns.Hunk.Type +--- | "add" +--- | "change" +--- | "delete" + +--- @class (exact) Gitsigns.Hunk.Node +--- @field start integer +--- @field count integer +--- @field lines string[] +--- @field no_nl_at_eof? true + +--- @class (exact) Gitsigns.Hunk.Hunk +--- @field type Gitsigns.Hunk.Type +--- @field head string +--- @field added Gitsigns.Hunk.Node +--- @field removed Gitsigns.Hunk.Node +--- @field vend integer + +--- @class (exact) Gitsigns.Hunk.Hunk_Public +--- @field type Gitsigns.Hunk.Type +--- @field head string +--- @field lines string[] +--- @field added Gitsigns.Hunk.Node +--- @field removed Gitsigns.Hunk.Node + +local M = {} + +--- @param old_start integer +--- @param old_count integer +--- @param new_start integer +--- @param new_count integer +--- @return Gitsigns.Hunk.Hunk +function M.create_hunk(old_start, old_count, new_start, new_count) + return { + removed = { start = old_start, count = old_count, lines = {} }, + added = { start = new_start, count = new_count, lines = {} }, + head = ('@@ -%d%s +%d%s @@'):format( + old_start, + old_count > 0 and ',' .. old_count or '', + new_start, + new_count > 0 and ',' .. new_count or '' + ), + + vend = new_start + math.max(new_count - 1, 0), + type = new_count == 0 and 'delete' or old_count == 0 and 'add' or 'change', + } +end + +--- @param hunks Gitsigns.Hunk.Hunk[] +--- @param top integer +--- @param bot integer +--- @return Gitsigns.Hunk.Hunk +function M.create_partial_hunk(hunks, top, bot) + local pretop, precount = top, bot - top + 1 + for _, h in ipairs(hunks) do + local added_in_hunk = h.added.count - h.removed.count + + local added_in_range = 0 + if h.added.start >= top and h.vend <= bot then + -- Range contains hunk + added_in_range = added_in_hunk + else + local added_above_bot = max(0, bot + 1 - (h.added.start + h.removed.count)) + local added_above_top = max(0, top - (h.added.start + h.removed.count)) + + if h.added.start >= top and h.added.start <= bot then + -- Range top intersects hunk + added_in_range = added_above_bot + elseif h.vend >= top and h.vend <= bot then + -- Range bottom intersects hunk + added_in_range = added_in_hunk - added_above_top + pretop = pretop - added_above_top + elseif h.added.start <= top and h.vend >= bot then + -- Range within hunk + added_in_range = added_above_bot - added_above_top + pretop = pretop - added_above_top + end + + if top > h.vend then + pretop = pretop - added_in_hunk + end + end + + precount = precount - added_in_range + end + + if precount == 0 then + pretop = pretop - 1 + end + + return M.create_hunk(pretop, precount, top, bot - top + 1) +end + +--- @param hunk Gitsigns.Hunk.Hunk +--- @param fileformat string +--- @return string[] +function M.patch_lines(hunk, fileformat) + local lines = {} --- @type string[] + for _, l in ipairs(hunk.removed.lines) do + lines[#lines + 1] = '-' .. l + end + for _, l in ipairs(hunk.added.lines) do + lines[#lines + 1] = '+' .. l + end + + if fileformat == 'dos' then + lines = util.strip_cr(lines) + end + return lines +end + +local function tointeger(x) + return tonumber(x) --[[@as integer]] +end + +--- @param line string +--- @return Gitsigns.Hunk.Hunk +function M.parse_diff_line(line) + local diffkey = vim.trim(vim.split(line, '@@', { plain = true })[2]) + + -- diffKey: "-xx,n +yy" + -- pre: {xx, n}, now: {yy} + --- @type string[], string[] + local pre, now = unpack(vim.tbl_map( + --- @param s string + --- @return string[] + function(s) + return vim.split(string.sub(s, 2), ',') + end, + vim.split(diffkey, ' ') + )) + + local hunk = M.create_hunk( + tointeger(pre[1]), + (tointeger(pre[2]) or 1), + tointeger(now[1]), + (tointeger(now[2]) or 1) + ) + + hunk.head = line + + return hunk +end + +--- @param hunk Gitsigns.Hunk.Hunk +--- @return integer +local function change_end(hunk) + if hunk.added.count == 0 then + -- delete + return hunk.added.start + elseif hunk.removed.count == 0 then + -- add + return hunk.added.start + hunk.added.count - 1 + else + -- change + return hunk.added.start + min(hunk.added.count, hunk.removed.count) - 1 + end +end + +--- Calculate signs needed to be applied from a hunk for a specified line range. +--- @param hunk Gitsigns.Hunk.Hunk +--- @param next Gitsigns.Hunk.Hunk? +--- @param min_lnum integer +--- @param max_lnum integer +--- @param untracked boolean +--- @return Gitsigns.Sign[] +function M.calc_signs(hunk, next, min_lnum, max_lnum, untracked) + assert( + not untracked or hunk.type == 'add', + string.format('Invalid hunk with untracked=%s hunk="%s"', untracked, hunk.head) + ) + min_lnum = min_lnum or 1 + max_lnum = max_lnum or math.huge + local start, added, removed = hunk.added.start, hunk.added.count, hunk.removed.count + + if hunk.type == 'delete' and start == 0 then + if min_lnum <= 1 then + -- topdelete signs get placed one row lower + return { { type = 'topdelete', count = removed, lnum = 1 } } + else + return {} + end + end + + --- @type Gitsigns.Sign[] + local signs = {} + + local cend = change_end(hunk) + + -- if this is a change hunk, mark changedelete if lines were removed or if the + -- next hunk removes on this hunks last line + local changedelete = false + if hunk.type == 'change' then + changedelete = removed > added + if next ~= nil and next.type == 'delete' then + changedelete = changedelete or hunk.added.start + hunk.added.count - 1 == next.added.start + end + end + + for lnum = max(start, min_lnum), min(cend, max_lnum) do + signs[#signs + 1] = { + type = (changedelete and lnum == cend) and 'changedelete' + or untracked and 'untracked' + or hunk.type, + count = lnum == start and (hunk.type == 'add' and added or removed) or nil, + lnum = lnum, + } + end + + if hunk.type == 'change' and added > removed and hunk.vend >= min_lnum and cend <= max_lnum then + for lnum = max(cend, min_lnum), min(hunk.vend, max_lnum) do + signs[#signs + 1] = { + type = 'add', + count = lnum == hunk.vend and (added - removed) or nil, + lnum = lnum, + } + end + end + + return signs +end + +--- @param relpath string +--- @param hunks Gitsigns.Hunk.Hunk[] +--- @param mode_bits string +--- @param invert? boolean +--- @return string[] +function M.create_patch(relpath, hunks, mode_bits, invert) + invert = invert or false + + local results = { + string.format('diff --git a/%s b/%s', relpath, relpath), + 'index 000000..000000 ' .. mode_bits, + '--- a/' .. relpath, + '+++ b/' .. relpath, + } + + local offset = 0 + + for _, process_hunk in ipairs(hunks) do + local start, pre_count, now_count = + process_hunk.removed.start, process_hunk.removed.count, process_hunk.added.count + + if process_hunk.type == 'add' then + start = start + 1 + end + + local pre_lines = process_hunk.removed.lines + local now_lines = process_hunk.added.lines + + if invert then + pre_count, now_count = now_count, pre_count --- @type integer, integer + pre_lines, now_lines = now_lines, pre_lines --- @type string[], string[] + end + + table.insert( + results, + string.format('@@ -%s,%s +%s,%s @@', start, pre_count, start + offset, now_count) + ) + for _, l in ipairs(pre_lines) do + results[#results + 1] = '-' .. l + end + + if process_hunk.removed.no_nl_at_eof then + results[#results + 1] = '\\ No newline at end of file' + end + + for _, l in ipairs(now_lines) do + results[#results + 1] = '+' .. l + end + + if process_hunk.added.no_nl_at_eof then + results[#results + 1] = '\\ No newline at end of file' + end + + process_hunk.removed.start = start + offset + offset = offset + (now_count - pre_count) + end + + return results +end + +--- @param hunks Gitsigns.Hunk.Hunk[] +--- @return Gitsigns.StatusObj +function M.get_summary(hunks) + --- @type Gitsigns.StatusObj + local status = { added = 0, changed = 0, removed = 0 } + + for _, hunk in ipairs(hunks or {}) do + if hunk.type == 'add' then + status.added = status.added + hunk.added.count + elseif hunk.type == 'delete' then + status.removed = status.removed + hunk.removed.count + elseif hunk.type == 'change' then + local add, remove = hunk.added.count, hunk.removed.count + local delta = min(add, remove) + status.changed = status.changed + delta + status.added = status.added + add - delta + status.removed = status.removed + remove - delta + end + end + + return status +end + +--- @param lnum integer +--- @param hunks Gitsigns.Hunk.Hunk[] +--- @return Gitsigns.Hunk.Hunk?, integer? +function M.find_hunk(lnum, hunks) + for i, hunk in ipairs(hunks or {}) do + if lnum == 1 and hunk.added.start == 0 and hunk.vend == 0 then + return hunk, i + end + + if hunk.added.start <= lnum and hunk.vend >= lnum then + return hunk, i + end + end +end + +--- @param lnum integer +--- @param hunks Gitsigns.Hunk.Hunk[] +--- @param direction 'first'|'last'|'next'|'prev' +--- @param wrap boolean +--- @return integer? +function M.find_nearest_hunk(lnum, hunks, direction, wrap) + if direction == 'first' then + return 1 + elseif direction == 'last' then + return #hunks + elseif direction == 'next' then + if hunks[1].added.start > lnum then + return 1 + end + for i = #hunks, 1, -1 do + if hunks[i].added.start <= lnum then + if i + 1 <= #hunks and hunks[i + 1].added.start > lnum then + return i + 1 + elseif wrap then + return 1 + end + end + end + elseif direction == 'prev' then + if math.max(hunks[#hunks].vend) < lnum then + return #hunks + end + for i = 1, #hunks do + if lnum <= math.max(hunks[i].vend, 1) then + if i > 1 and math.max(hunks[i - 1].vend, 1) < lnum then + return i - 1 + elseif wrap then + return #hunks + end + end + end + end +end + +--- @param a Gitsigns.Hunk.Hunk[]? +--- @param b Gitsigns.Hunk.Hunk[]? +--- @return boolean +function M.compare_heads(a, b) + if (a == nil) ~= (b == nil) then + return true + elseif a and #a ~= #b then + return true + end + for i, ah in ipairs(a or {}) do + --- @diagnostic disable-next-line:need-check-nil + if b[i].head ~= ah.head then + return true + end + end + return false +end + +--- @param a Gitsigns.Hunk.Hunk +--- @param b Gitsigns.Hunk.Hunk +--- @return boolean +local function compare_new(a, b) + if a.added.start ~= b.added.start then + return false + end + + if a.added.count ~= b.added.count then + return false + end + + for i = 1, a.added.count do + if a.added.lines[i] ~= b.added.lines[i] then + return false + end + end + + return true +end + +--- Return hunks in a using b's hunks as a filter. Only compare the 'new' section +--- of the hunk. +--- +--- Eg. Given: +--- +--- a = { +--- 1 = '@@ -24 +25,1 @@', +--- 2 = '@@ -32 +34,1 @@', +--- 3 = '@@ -37 +40,1 @@' +--- } +--- +--- b = { +--- 1 = '@@ -26 +25,1 @@' +--- } +--- +--- Since a[1] and b[1] introduce the same changes to the buffer (both have +--- +25,1), we exclude this hunk in the output so we return: +--- +--- { +--- 1 = '@@ -32 +34,1 @@', +--- 2 = '@@ -37 +40,1 @@' +--- } +--- +--- @param a Gitsigns.Hunk.Hunk[] +--- @param b Gitsigns.Hunk.Hunk[] +--- @return Gitsigns.Hunk.Hunk[]? +function M.filter_common(a, b) + if not a and not b then + return + end + + a, b = a or {}, b or {} + local max_iter = math.max(#a, #b) + + local a_i = 1 + local b_i = 1 + + --- @type Gitsigns.Hunk.Hunk[] + local ret = {} + + for _ = 1, max_iter do + local a_h, b_h = a[a_i], b[b_i] + + if not a_h then + -- Reached the end of a + break + end + + if not b_h then + -- Reached the end of b, add remainder of a + for i = a_i, #a do + ret[#ret + 1] = a[i] + end + break + end + + if a_h.added.start > b_h.added.start then + -- a pointer is ahead of b; increment b pointer + b_i = b_i + 1 + elseif a_h.added.start < b_h.added.start then + -- b pointer is ahead of a; add a_h to ret and increment a pointer + ret[#ret + 1] = a_h + a_i = a_i + 1 + else -- a_h.start == b_h.start + -- a_h and b_h start on the same line, if hunks have the same changes then + -- skip (filtered) otherwise add a_h to ret. Increment both hunk + -- pointers + -- TODO(lewis6991): Be smarter; if bh intercepts then break down ah. + if not compare_new(a_h, b_h) then + ret[#ret + 1] = a_h + end + a_i = a_i + 1 + b_i = b_i + 1 + end + end + + return ret +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/manager.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/manager.lua new file mode 100644 index 00000000..924ab30b --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/manager.lua @@ -0,0 +1,591 @@ +local async = require('gitsigns.async') + +local gs_cache = require('gitsigns.cache') +local cache = gs_cache.cache + +local Signs = require('gitsigns.signs') +local Status = require('gitsigns.status') + +local debounce_trailing = require('gitsigns.debounce').debounce_trailing +local throttle_by_id = require('gitsigns.debounce').throttle_by_id + +local log = require('gitsigns.debug.log') +local dprint = log.dprint + +local util = require('gitsigns.util') +local run_diff = require('gitsigns.diff') + +local gs_hunks = require('gitsigns.hunks') + +local config = require('gitsigns.config').config + +local api = vim.api + +local signs_normal --- @type Gitsigns.Signs +local signs_staged --- @type Gitsigns.Signs + +local M = {} + +--- @param bufnr integer +--- @param signs Gitsigns.Signs +--- @param hunks Gitsigns.Hunk.Hunk[] +--- @param top integer +--- @param bot integer +--- @param clear boolean +--- @param untracked boolean +local function apply_win_signs0(bufnr, signs, hunks, top, bot, clear, untracked) + if clear then + signs:remove(bufnr) -- Remove all signs + end + + for i, hunk in ipairs(hunks or {}) do + --- @type Gitsigns.Hunk.Hunk? + local next = hunks[i + 1] + + -- To stop the sign column width changing too much, if there are signs to be + -- added but none of them are visible in the window, then make sure to add at + -- least one sign. Only do this on the first call after an update when we all + -- the signs have been cleared. + if clear and i == 1 then + signs:add( + bufnr, + gs_hunks.calc_signs(hunk, next, hunk.added.start, hunk.added.start, untracked) + ) + end + + if top <= hunk.vend and bot >= hunk.added.start then + signs:add(bufnr, gs_hunks.calc_signs(hunk, next, top, bot, untracked)) + end + if hunk.added.start > bot then + break + end + end +end + +--- @param bufnr integer +--- @param top integer +--- @param bot integer +--- @param clear boolean +local function apply_win_signs(bufnr, top, bot, clear) + local bcache = cache[bufnr] + if not bcache then + return + end + + local untracked = bcache.git_obj.object_name == nil + apply_win_signs0(bufnr, signs_normal, bcache.hunks, top, bot, clear, untracked) + if signs_staged then + apply_win_signs0(bufnr, signs_staged, bcache.hunks_staged, top, bot, clear, false) + end +end + +--- @param blame table? +--- @param first integer +--- @param last_orig integer +--- @param last_new integer +local function on_lines_blame(blame, first, last_orig, last_new) + if not blame then + return + end + + if last_new ~= last_orig then + if last_new < last_orig then + util.list_remove(blame, last_new, last_orig) + else + util.list_insert(blame, last_orig, last_new) + end + end + + for i = math.min(first + 1, last_new), math.max(first + 1, last_new) do + blame[i] = nil + end +end + +--- @param buf integer +--- @param first integer +--- @param last_orig integer +--- @param last_new integer +--- @return true? +function M.on_lines(buf, first, last_orig, last_new) + local bcache = cache[buf] + if not bcache then + dprint('Cache for buffer was nil. Detaching') + return true + end + + on_lines_blame(bcache.blame, first, last_orig, last_new) + + signs_normal:on_lines(buf, first, last_orig, last_new) + if signs_staged then + signs_staged:on_lines(buf, first, last_orig, last_new) + end + + -- Signs in changed regions get invalidated so we need to force a redraw if + -- any signs get removed. + if bcache.hunks and signs_normal:contains(buf, first, last_new) then + -- Force a sign redraw on the next update (fixes #521) + bcache.force_next_update = true + end + + if signs_staged then + if bcache.hunks_staged and signs_staged:contains(buf, first, last_new) then + -- Force a sign redraw on the next update (fixes #521) + bcache.force_next_update = true + end + end + + M.update_debounced(buf) +end + +local ns = api.nvim_create_namespace('gitsigns') + +--- @param bufnr integer +--- @param row integer +local function apply_word_diff(bufnr, row) + -- Don't run on folded lines + if vim.fn.foldclosed(row + 1) ~= -1 then + return + end + + local bcache = cache[bufnr] + + if not bcache or not bcache.hunks then + return + end + + local line = api.nvim_buf_get_lines(bufnr, row, row + 1, false)[1] + if not line then + -- Invalid line + return + end + + local lnum = row + 1 + + local hunk = gs_hunks.find_hunk(lnum, bcache.hunks) + if not hunk then + -- No hunk at line + return + end + + if hunk.added.count ~= hunk.removed.count then + -- Only word diff if added count == removed + return + end + + local pos = lnum - hunk.added.start + 1 + + local added_line = hunk.added.lines[pos] + local removed_line = hunk.removed.lines[pos] + + local _, added_regions = require('gitsigns.diff_int').run_word_diff( + { removed_line }, + { added_line } + ) + + local cols = #line + + for _, region in ipairs(added_regions) do + local rtype, scol, ecol = region[2], region[3] - 1, region[4] - 1 + if ecol == scol then + -- Make sure region is at least 1 column wide so deletes can be shown + ecol = scol + 1 + end + + local hl_group = rtype == 'add' and 'GitSignsAddLnInline' + or rtype == 'change' and 'GitSignsChangeLnInline' + or 'GitSignsDeleteLnInline' + + local opts = { + ephemeral = true, + priority = 1000, + } + + if ecol > cols and ecol == scol + 1 then + -- delete on last column, use virtual text instead + opts.virt_text = { { ' ', hl_group } } + opts.virt_text_pos = 'overlay' + else + opts.end_col = ecol + opts.hl_group = hl_group + end + + api.nvim_buf_set_extmark(bufnr, ns, row, scol, opts) + util.redraw({ buf = bufnr, range = { row, row + 1 } }) + end +end + +local ns_rm = api.nvim_create_namespace('gitsigns_removed') + +local VIRT_LINE_LEN = 300 + +--- @param bufnr integer +local function clear_deleted(bufnr) + local marks = api.nvim_buf_get_extmarks(bufnr, ns_rm, 0, -1, {}) + for _, mark in ipairs(marks) do + api.nvim_buf_del_extmark(bufnr, ns_rm, mark[1]) + end +end + +--- @param bufnr integer +--- @param nsd integer +--- @param hunk Gitsigns.Hunk.Hunk +function M.show_deleted(bufnr, nsd, hunk) + local virt_lines = {} --- @type {[1]: string, [2]: string}[][] + + for i, line in ipairs(hunk.removed.lines) do + local vline = {} --- @type {[1]: string, [2]: string}[] + local last_ecol = 1 + + if config.word_diff then + local regions = require('gitsigns.diff_int').run_word_diff( + { hunk.removed.lines[i] }, + { hunk.added.lines[i] } + ) + + for _, region in ipairs(regions) do + local rline, scol, ecol = region[1], region[3], region[4] + if rline > 1 then + break + end + vline[#vline + 1] = { line:sub(last_ecol, scol - 1), 'GitSignsDeleteVirtLn' } + vline[#vline + 1] = { line:sub(scol, ecol - 1), 'GitSignsDeleteVirtLnInline' } + last_ecol = ecol + end + end + + if #line > 0 then + vline[#vline + 1] = { line:sub(last_ecol, -1), 'GitSignsDeleteVirtLn' } + end + + -- Add extra padding so the entire line is highlighted + local padding = string.rep(' ', VIRT_LINE_LEN - #line) + vline[#vline + 1] = { padding, 'GitSignsDeleteVirtLn' } + + virt_lines[i] = vline + end + + local topdelete = hunk.added.start == 0 and hunk.type == 'delete' + + local row = topdelete and 0 or hunk.added.start - 1 + api.nvim_buf_set_extmark(bufnr, nsd, row, -1, { + virt_lines = virt_lines, + -- TODO(lewis6991): Note virt_lines_above doesn't work on row 0 neovim/neovim#16166 + virt_lines_above = hunk.type ~= 'delete' or topdelete, + }) +end + +--- @param win integer +--- @param lnum integer +--- @param width integer +--- @return string str +--- @return {group:string, start:integer}[]? highlights +local function build_lno_str(win, lnum, width) + local has_col, statuscol = + pcall(api.nvim_get_option_value, 'statuscolumn', { win = win, scope = 'local' }) + if has_col and statuscol and statuscol ~= '' then + local ok, data = pcall(api.nvim_eval_statusline, statuscol, { + winid = win, + use_statuscol_lnum = lnum, + highlights = true, + }) + if ok then + return data.str, data.highlights + end + end + return string.format('%' .. width .. 'd', lnum) +end + +--- @param bufnr integer +--- @param nsd integer +--- @param hunk Gitsigns.Hunk.Hunk +--- @param staged boolean? +--- @return integer winid +function M.show_deleted_in_float(bufnr, nsd, hunk, staged) + local cwin = api.nvim_get_current_win() + local virt_lines = {} --- @type {[1]: string, [2]: string}[][] + local textoff = vim.fn.getwininfo(cwin)[1].textoff --[[@as integer]] + for i = 1, hunk.removed.count do + local sc = build_lno_str(cwin, hunk.removed.start + i, textoff - 1) + virt_lines[i] = { { sc, 'LineNr' } } + end + + local topdelete = hunk.added.start == 0 and hunk.type == 'delete' + local virt_lines_above = hunk.type ~= 'delete' or topdelete + + local row = topdelete and 0 or hunk.added.start - 1 + api.nvim_buf_set_extmark(bufnr, nsd, row, -1, { + virt_lines = virt_lines, + -- TODO(lewis6991): Note virt_lines_above doesn't work on row 0 neovim/neovim#16166 + virt_lines_above = virt_lines_above, + virt_lines_leftcol = true, + }) + + local bcache = cache[bufnr] + local pbufnr = api.nvim_create_buf(false, true) + local text = staged and bcache.compare_text_head or bcache.compare_text + api.nvim_buf_set_lines(pbufnr, 0, -1, false, assert(text)) + + local width = api.nvim_win_get_width(0) + + local bufpos_offset = virt_lines_above and not topdelete and 1 or 0 + + local pwinid = api.nvim_open_win(pbufnr, false, { + relative = 'win', + win = cwin, + width = width - textoff, + height = hunk.removed.count, + anchor = 'SW', + bufpos = { hunk.added.start - bufpos_offset, 0 }, + style = 'minimal', + }) + + vim.bo[pbufnr].filetype = vim.bo[bufnr].filetype + vim.bo[pbufnr].bufhidden = 'wipe' + vim.wo[pwinid].scrolloff = 0 + + api.nvim_win_call(pwinid, function() + -- Expand folds + vim.cmd('normal ' .. 'zR') + + -- Navigate to hunk + vim.cmd('normal ' .. tostring(hunk.removed.start) .. 'gg') + vim.cmd('normal ' .. vim.api.nvim_replace_termcodes('z', true, false, true)) + end) + + local last_lnum = api.nvim_buf_line_count(bufnr) + + -- Apply highlights + + for i = hunk.removed.start, hunk.removed.start + hunk.removed.count do + api.nvim_buf_set_extmark(pbufnr, nsd, i - 1, 0, { + hl_group = 'GitSignsDeleteVirtLn', + hl_eol = true, + end_row = i, + strict = i == last_lnum, + priority = 1000, + }) + end + + local removed_regions = + require('gitsigns.diff_int').run_word_diff(hunk.removed.lines, hunk.added.lines) + + for _, region in ipairs(removed_regions) do + local start_row = (hunk.removed.start - 1) + (region[1] - 1) + local start_col = region[3] - 1 + local end_col = region[4] - 1 + api.nvim_buf_set_extmark(pbufnr, nsd, start_row, start_col, { + hl_group = 'GitSignsDeleteVirtLnInline', + end_col = end_col, + end_row = start_row, + priority = 1001, + }) + end + + return pwinid +end + +--- @param bufnr integer +--- @param nsw integer +--- @param hunk Gitsigns.Hunk.Hunk +function M.show_added(bufnr, nsw, hunk) + local start_row = hunk.added.start - 1 + + for offset = 0, hunk.added.count - 1 do + local row = start_row + offset + api.nvim_buf_set_extmark(bufnr, nsw, row, 0, { + end_row = row + 1, + hl_group = 'GitSignsAddPreview', + hl_eol = true, + priority = 1000, + }) + end + + local _, added_regions = + require('gitsigns.diff_int').run_word_diff(hunk.removed.lines, hunk.added.lines) + + for _, region in ipairs(added_regions) do + local offset, rtype, scol, ecol = region[1] - 1, region[2], region[3] - 1, region[4] - 1 + + -- Special case to handle cr at eol in buffer but not in show text + local cr_at_eol_change = rtype == 'change' and vim.endswith(hunk.added.lines[offset + 1], '\r') + + api.nvim_buf_set_extmark(bufnr, nsw, start_row + offset, scol, { + end_col = ecol, + strict = not cr_at_eol_change, + hl_group = rtype == 'add' and 'GitSignsAddInline' + or rtype == 'change' and 'GitSignsChangeInline' + or 'GitSignsDeleteInline', + priority = 1001, + }) + end +end + +--- @param bufnr integer +local function update_show_deleted(bufnr) + local bcache = cache[bufnr] + + clear_deleted(bufnr) + if config.show_deleted then + for _, hunk in ipairs(bcache.hunks or {}) do + M.show_deleted(bufnr, ns_rm, hunk) + end + end +end + +--- @async +--- @nodiscard +--- @param bufnr integer +--- @param check_compare_text? boolean +--- @return boolean +function M.schedule(bufnr, check_compare_text) + async.scheduler() + if not api.nvim_buf_is_valid(bufnr) then + dprint('Buffer not valid, aborting') + return false + end + if not cache[bufnr] then + dprint('Has detached, aborting') + return false + end + if check_compare_text and not cache[bufnr].compare_text then + dprint('compare_text was invalid, aborting') + return false + end + return true +end + +--- Ensure updates cannot be interleaved. +--- Since updates are asynchronous we need to make sure an update isn't performed +--- whilst another one is in progress. If this happens then schedule another +--- update after the current one has completed. +--- @param bufnr integer +M.update = throttle_by_id(function(bufnr) + if not M.schedule(bufnr) then + return + end + local bcache = cache[bufnr] + local old_hunks, old_hunks_staged = bcache.hunks, bcache.hunks_staged + bcache.hunks, bcache.hunks_staged = nil, nil + + local git_obj = bcache.git_obj + local file_mode = bcache.file_mode + + if not bcache.compare_text or config._refresh_staged_on_update or file_mode then + if file_mode then + bcache.compare_text = util.file_lines(git_obj.file) + else + bcache.compare_text = git_obj:get_show_text() + end + if not M.schedule(bufnr, true) then + return + end + end + + local buftext = util.buf_lines(bufnr) + + bcache.hunks = run_diff(bcache.compare_text, buftext) + if not M.schedule(bufnr) then + return + end + + if config._signs_staged_enable and not file_mode and not git_obj.revision then + if not bcache.compare_text_head or config._refresh_staged_on_update then + bcache.compare_text_head = git_obj:get_show_text('HEAD') + if not M.schedule(bufnr, true) then + return + end + end + local hunks_head = run_diff(bcache.compare_text_head, buftext) + if not M.schedule(bufnr) then + return + end + bcache.hunks_staged = gs_hunks.filter_common(hunks_head, bcache.hunks) + end + + -- Note the decoration provider may have invalidated bcache.hunks at this + -- point + if + bcache.force_next_update + or gs_hunks.compare_heads(bcache.hunks, old_hunks) + or gs_hunks.compare_heads(bcache.hunks_staged, old_hunks_staged) + then + -- Apply signs to the window. Other signs will be added by the decoration + -- provider as they are drawn. + apply_win_signs(bufnr, vim.fn.line('w0'), vim.fn.line('w$'), true) + + update_show_deleted(bufnr) + bcache.force_next_update = false + + local summary = gs_hunks.get_summary(bcache.hunks) + summary.head = git_obj.repo.abbrev_head + Status:update(bufnr, summary) + end +end, true) + +--- @param bufnr integer +--- @param keep_signs? boolean +function M.detach(bufnr, keep_signs) + if not keep_signs then + -- Remove all signs + signs_normal:remove(bufnr) + if signs_staged then + signs_staged:remove(bufnr) + end + end +end + +function M.reset_signs() + -- Remove all signs + if signs_normal then + signs_normal:reset() + end + if signs_staged then + signs_staged:reset() + end +end + +--- @param _cb 'win' +--- @param _winid integer +--- @param bufnr integer +--- @param topline integer +--- @param botline_guess integer +--- @return false? +local function on_win(_cb, _winid, bufnr, topline, botline_guess) + local bcache = cache[bufnr] + if not bcache or not bcache.hunks then + return false + end + local botline = math.min(botline_guess, api.nvim_buf_line_count(bufnr)) + + apply_win_signs(bufnr, topline + 1, botline + 1, false) + + if not (config.word_diff and config.diff_opts.internal) then + return false + end +end + +--- @param _cb 'line' +--- @param _winid integer +--- @param bufnr integer +--- @param row integer +local function on_line(_cb, _winid, bufnr, row) + apply_word_diff(bufnr, row) +end + +function M.setup() + -- Calling this before any await calls will stop nvim's intro messages being + -- displayed + api.nvim_set_decoration_provider(ns, { + on_win = on_win, + on_line = on_line, + }) + + signs_normal = Signs.new(config.signs) + if config._signs_staged_enable then + signs_staged = Signs.new(config._signs_staged, 'staged') + end + + M.update_debounced = debounce_trailing(config.update_debounce, async.create(1, M.update)) +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/message.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/message.lua new file mode 100644 index 00000000..6bd8c4f8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/message.lua @@ -0,0 +1,20 @@ +local levels = vim.log.levels + +local M = {} + +--- @type fun(fmt: string, ...: string) +M.warn = vim.schedule_wrap(function(fmt, ...) + vim.notify(fmt:format(...), levels.WARN, { title = 'gitsigns' }) +end) + +--- @type fun(fmt: string, ...: string) +M.error = vim.schedule_wrap(function(fmt, ...) + vim.notify(fmt:format(...), vim.log.levels.ERROR, { title = 'gitsigns' }) +end) + +--- @type fun(fmt: string, ...: string) +M.error_once = vim.schedule_wrap(function(fmt, ...) + vim.notify_once(fmt:format(...), vim.log.levels.ERROR, { title = 'gitsigns' }) +end) + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/popup.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/popup.lua new file mode 100644 index 00000000..ea47eb79 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/popup.lua @@ -0,0 +1,282 @@ +local M = {} + +local api = vim.api + +--- @param bufnr integer +--- @param lines string[] +--- @return integer +local function bufnr_calc_width(bufnr, lines) + return api.nvim_buf_call(bufnr, function() + local width = 0 + for _, l in ipairs(lines) do + if vim.fn.type(l) == vim.v.t_string then + local len = vim.fn.strdisplaywidth(l) + if len > width then + width = len + end + end + end + return width + 1 -- Add 1 for some miinor padding + end) +end + +-- Expand height until all lines are visible to account for wrapped lines. +--- @param winid integer +--- @param nlines integer +--- @param border string +local function expand_height(winid, nlines, border) + local newheight = 0 + local maxheight = vim.o.lines - vim.o.cmdheight - (border ~= '' and 2 or 0) + for _ = 0, 50 do + local winheight = api.nvim_win_get_height(winid) + if newheight > winheight then + -- Window must be max height + break + end + --- @type integer + local wd = api.nvim_win_call(winid, function() + return vim.fn.line('w$') + end) + if wd >= nlines then + break + end + newheight = winheight + nlines - wd + if newheight > maxheight then + api.nvim_win_set_height(winid, maxheight) + break + end + api.nvim_win_set_height(winid, newheight) + end +end + +--- @class (exact) Gitsigns.HlMark +--- @field hl_group string +--- @field start_row? integer +--- @field start_col? integer +--- @field end_row? integer +--- @field end_col? integer + +--- Each element represents a multi-line segment +--- @alias Gitsigns.LineSpec { [1]: string, [2]: Gitsigns.HlMark[]}[][] + +--- @param hlmarks Gitsigns.HlMark[] +--- @param row_offset integer +local function offset_hlmarks(hlmarks, row_offset) + for _, h in ipairs(hlmarks) do + h.start_row = (h.start_row or 0) + row_offset + if h.end_row then + h.end_row = h.end_row + row_offset + end + end +end + +--- Partition the text and Gitsigns.HlMarks from a Gitsigns.LineSpec +--- @param fmt Gitsigns.LineSpec +--- @return string[] +--- @return Gitsigns.HlMark[] +local function partition_linesspec(fmt) + local lines = {} --- @type string[] + local ret = {} --- @type Gitsigns.HlMark[] + + local row = 0 + for _, section in ipairs(fmt) do + local section_text = {} --- @type string[] + local col = 0 + for _, part in ipairs(section) do + local text, hls = part[1], part[2] + + section_text[#section_text + 1] = text + + local _, no_lines = text:gsub('\n', '') + local end_row = row + no_lines --- @type integer + local end_col = no_lines > 0 and 0 or col + #text --- @type integer + + if type(hls) == 'string' then + ret[#ret + 1] = { + hl_group = hls, + start_row = row, + end_row = end_row, + start_col = col, + end_col = end_col, + } + else -- hl is Gitsigns.HlMark[] + offset_hlmarks(hls, row) + vim.list_extend(ret, hls) + end + + row = end_row + col = end_col + end + + local section_lines = vim.split(table.concat(section_text), '\n', { plain = true }) + vim.list_extend(lines, section_lines) + + row = row + 1 + end + + return lines, ret +end + +--- @param id string|true +local function close_all_but(id) + for _, winid in ipairs(api.nvim_list_wins()) do + if vim.w[winid].gitsigns_preview ~= nil and vim.w[winid].gitsigns_preview ~= id then + pcall(api.nvim_win_close, winid, true) + end + end +end + +--- @param id string +function M.close(id) + for _, winid in ipairs(api.nvim_list_wins()) do + if vim.w[winid].gitsigns_preview == id then + pcall(api.nvim_win_close, winid, true) + end + end +end + +local ns = api.nvim_create_namespace('gitsigns_popup') + +--- @param lines string[] +--- @param highlights Gitsigns.HlMark[] +--- @return integer bufnr +local function create_buf(lines, highlights) + local ts = vim.bo.tabstop + local bufnr = api.nvim_create_buf(false, true) + assert(bufnr, 'Failed to create buffer') + + -- In case nvim was opened with '-M' + vim.bo[bufnr].modifiable = true + api.nvim_buf_set_lines(bufnr, 0, -1, true, lines) + vim.bo[bufnr].modifiable = false + + -- Set tabstop before calculating the buffer width so that the correct width + -- is calculated + vim.bo[bufnr].tabstop = ts + + for _, hl in ipairs(highlights) do + local ok, err = pcall(api.nvim_buf_set_extmark, bufnr, ns, hl.start_row, hl.start_col or 0, { + hl_group = hl.hl_group, + end_row = hl.end_row, + end_col = hl.end_col, + hl_eol = true, + }) + if not ok then + error(vim.inspect(hl) .. '\n' .. err) + end + end + + return bufnr +end + +--- @param bufnr integer +--- @param opts table +--- @param id? string|true +--- @return integer winid +local function create_win(bufnr, opts, id) + id = id or true + + -- Close any popups not matching id + close_all_but(id) + + local lines = api.nvim_buf_get_lines(bufnr, 0, -1, true) + + local opts1 = vim.deepcopy(opts or {}) + opts1.height = opts1.height or #lines -- Guess, adjust later + opts1.width = opts1.width or bufnr_calc_width(bufnr, lines) + + local winid = api.nvim_open_win(bufnr, false, opts1) + + vim.w[winid].gitsigns_preview = id + + if not opts.height then + expand_height(winid, #lines, opts.border) + end + + if opts1.style == 'minimal' then + -- If 'signcolumn' = auto:1-2, then a empty signcolumn will appear and cause + -- line wrapping. + vim.wo[winid].signcolumn = 'no' + end + + -- Close the popup when navigating to any window which is not the preview + -- itself. + local group = 'gitsigns_popup' + local group_id = api.nvim_create_augroup(group, {}) + local old_cursor = api.nvim_win_get_cursor(0) + + vim.keymap.set('n', 'q', 'quit!', { silent = true, buffer = bufnr }) + + api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, { + group = group_id, + callback = function() + local cursor = api.nvim_win_get_cursor(0) + -- Did the cursor REALLY change (neovim/neovim#12923) + if + (old_cursor[1] ~= cursor[1] or old_cursor[2] ~= cursor[2]) + and api.nvim_get_current_win() ~= winid + then + -- Clear the augroup + api.nvim_create_augroup(group, {}) + pcall(api.nvim_win_close, winid, true) + return + end + old_cursor = cursor + end, + }) + + api.nvim_create_autocmd('WinClosed', { + pattern = tostring(winid), + group = group_id, + callback = function() + -- Clear the augroup + api.nvim_create_augroup(group, {}) + end, + }) + + -- update window position to follow the cursor when scrolling + api.nvim_create_autocmd('WinScrolled', { + buffer = api.nvim_get_current_buf(), + group = group_id, + callback = function() + if api.nvim_win_is_valid(winid) then + api.nvim_win_set_config(winid, opts1) + end + end, + }) + + return winid +end + +--- @param lines_spec {[1]: string, [2]: string|Gitsigns.HlMark[]}[][] +--- @param opts table +--- @param id? string +--- @return integer winid, integer bufnr +function M.create(lines_spec, opts, id) + local lines, highlights = partition_linesspec(lines_spec) + local bufnr = create_buf(lines, highlights) + local winid = create_win(bufnr, opts, id) + return winid, bufnr +end + +--- @param id string +--- @return integer? winid +function M.is_open(id) + for _, winid in ipairs(api.nvim_list_wins()) do + if vim.w[winid].gitsigns_preview == id then + return winid + end + end +end + +--- @param id string +--- @return integer? winid +function M.focus_open(id) + local winid = M.is_open(id) + if winid then + api.nvim_set_current_win(winid) + end + return winid +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/repeat.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/repeat.lua new file mode 100644 index 00000000..29b72d9d --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/repeat.lua @@ -0,0 +1,26 @@ +local M = {} + +function M.mk_repeatable(fn) + return function(...) + local args = { ... } + local nargs = select('#', ...) + vim.go.operatorfunc = "v:lua.require'gitsigns.repeat'.repeat_action" + + M.repeat_action = function() + fn(unpack(args, 1, nargs)) + if vim.fn.exists('*repeat#set') == 1 then + local action = vim.api.nvim_replace_termcodes( + string.format('call %s()', vim.go.operatorfunc), + true, + true, + true + ) + vim.fn['repeat#set'](action, -1) + end + end + + vim.cmd('normal! g@l') + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/signs.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/signs.lua new file mode 100644 index 00000000..d0f69e8b --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/signs.lua @@ -0,0 +1,135 @@ +local api = vim.api + +local config = require('gitsigns.config').config + +--- @class Gitsigns.Sign +--- @field type Gitsigns.SignType +--- @field count? integer +--- @field lnum integer + +--- @class Gitsigns.Signs +--- @field hls table +--- @field name string +--- @field group string +--- @field config table +--- @field ns integer +local M = {} + +--- @param buf integer +--- @param last_orig? integer +--- @param last_new? integer +function M:on_lines(buf, _, last_orig, last_new) + -- Remove extmarks on line deletions to mimic + -- the behaviour of vim signs. + if last_orig > last_new then + self:remove(buf, last_new + 1, last_orig) + end +end + +--- @param bufnr integer +--- @param start_lnum? integer +--- @param end_lnum? integer +function M:remove(bufnr, start_lnum, end_lnum) + if start_lnum then + api.nvim_buf_clear_namespace(bufnr, self.ns, start_lnum - 1, end_lnum or start_lnum) + else + api.nvim_buf_clear_namespace(bufnr, self.ns, 0, -1) + end +end + +---@param bufnr integer +---@param signs Gitsigns.Sign[] +function M:add(bufnr, signs) + if not config.signcolumn and not config.numhl and not config.linehl then + -- Don't place signs if it won't show anything + return + end + + for _, s in ipairs(signs) do + if not self:contains(bufnr, s.lnum) then + local cs = self.config[s.type] + local text = cs.text + if config.signcolumn and cs.show_count and s.count then + local count = s.count + local cc = config.count_chars + local count_char = cc[count] or cc['+'] or '' + text = cs.text .. count_char + end + + local hls = self.hls[s.type] + + local ok, err = pcall(api.nvim_buf_set_extmark, bufnr, self.ns, s.lnum - 1, -1, { + id = s.lnum, + sign_text = config.signcolumn and text or '', + priority = config.sign_priority, + sign_hl_group = hls.hl, + number_hl_group = config.numhl and hls.numhl or nil, + line_hl_group = config.linehl and hls.linehl or nil, + }) + + if not ok and config.debug_mode then + vim.schedule(function() + error(table.concat({ + string.format('Error placing extmark on line %d', s.lnum), + err, + }, '\n')) + end) + end + end + end +end + +---@param bufnr integer +---@param start integer +---@param last? integer +---@return boolean +function M:contains(bufnr, start, last) + local marks = api.nvim_buf_get_extmarks( + bufnr, + self.ns, + { start - 1, 0 }, + { last or start, 0 }, + { limit = 1 } + ) + return #marks > 0 +end + +function M:reset() + for _, buf in ipairs(api.nvim_list_bufs()) do + self:remove(buf) + end +end + +-- local function capitalise_word(x: string): string +-- return x:sub(1, 1):upper()..x:sub(2) +-- end + +function M.new(cfg, name) + local __FUNC__ = 'signs.init' + + -- Add when config.signs.*.[hl,numhl,linehl] are removed + -- for _, t in ipairs { + -- 'add', + -- 'change', + -- 'delete', + -- 'topdelete', + -- 'changedelete', + -- 'untracked', + -- } do + -- local hl = string.format('GitSigns%s%s', name, capitalise_word(t)) + -- obj.hls[t] = { + -- hl = hl, + -- numhl = hl..'Nr', + -- linehl = hl..'Ln', + -- } + -- end + + local self = setmetatable({}, { __index = M }) + self.config = cfg + self.hls = name == 'staged' and config._signs_staged or config.signs + self.group = 'gitsigns_signs_' .. (name or '') + self.ns = api.nvim_create_namespace(self.group) + return self +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/status.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/status.lua new file mode 100644 index 00000000..062f6baa --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/status.lua @@ -0,0 +1,52 @@ +local api = vim.api + +--- @class (exact) Gitsigns.StatusObj +--- @field added? integer +--- @field removed? integer +--- @field changed? integer +--- @field head? string +--- @field root? string +--- @field gitdir? string + +local M = {} + +--- @param bufnr integer +local function autocmd_update(bufnr) + api.nvim_exec_autocmds('User', { + pattern = 'GitSignsUpdate', + modeline = false, + data = { buffer = bufnr }, + }) +end + +--- @param bufnr integer +--- @param status Gitsigns.StatusObj +function M:update(bufnr, status) + if not api.nvim_buf_is_loaded(bufnr) then + return + end + local bstatus = vim.b[bufnr].gitsigns_status_dict + if bstatus then + status = vim.tbl_extend('force', bstatus, status) + end + vim.b[bufnr].gitsigns_head = status.head or '' + vim.b[bufnr].gitsigns_status_dict = status + + local config = require('gitsigns.config').config + + vim.b[bufnr].gitsigns_status = config.status_formatter(status) + + autocmd_update(bufnr) +end + +function M:clear(bufnr) + if not api.nvim_buf_is_loaded(bufnr) then + return + end + vim.b[bufnr].gitsigns_head = nil + vim.b[bufnr].gitsigns_status_dict = nil + vim.b[bufnr].gitsigns_status = nil + autocmd_update(bufnr) +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/system.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/system.lua new file mode 100644 index 00000000..e7f47ada --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/system.lua @@ -0,0 +1,19 @@ +local log = require('gitsigns.debug.log') + +local M = {} + +local system = vim.system or require('gitsigns.system.compat') + +--- @param cmd string[] +--- @param opts vim.SystemOpts +--- @param on_exit fun(obj: vim.SystemCompleted) +--- @return vim.SystemObj +function M.system(cmd, opts, on_exit) + local __FUNC__ = 'run_job' + if log.debug_mode then + log.dprint(table.concat(cmd, ' ')) + end + return system(cmd, opts, on_exit) +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/system/compat.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/system/compat.lua new file mode 100644 index 00000000..ce716680 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/system/compat.lua @@ -0,0 +1,338 @@ +local uv = vim.loop + +--- @param handle uv.uv_handle_t? +local function close_handle(handle) + if handle and not handle:is_closing() then + handle:close() + end +end + +--- @type vim.SystemSig +local SIG = { + HUP = 1, -- Hangup + INT = 2, -- Interrupt from keyboard + KILL = 9, -- Kill signal + TERM = 15, -- Termination signal + -- STOP = 17,19,23 -- Stop the process +} + +--- @param state vim.SystemState +local function close_handles(state) + close_handle(state.handle) + close_handle(state.stdin) + close_handle(state.stdout) + close_handle(state.stderr) + close_handle(state.timer) +end + +--- @class Gitsigns.SystemObj : vim.SystemObj +--- @field private _state vim.SystemState +local SystemObj = {} + +--- @param state vim.SystemState +--- @return vim.SystemObj +local function new_systemobj(state) + return setmetatable({ + pid = state.pid, + _state = state, + }, { __index = SystemObj }) +end + +--- @param signal integer|string +function SystemObj:kill(signal) + self._state.handle:kill(signal) +end + +--- @package +--- @param signal? vim.SystemSig +function SystemObj:_timeout(signal) + self._state.done = 'timeout' + self:kill(signal or SIG.TERM) +end + +local MAX_TIMEOUT = 2 ^ 31 + +--- @param timeout? integer +--- @return vim.SystemCompleted +function SystemObj:wait(timeout) + local state = self._state + + local done = vim.wait(timeout or state.timeout or MAX_TIMEOUT, function() + return state.result ~= nil + end, nil, true) + + if not done then + -- Send sigkill since this cannot be caught + self:_timeout(SIG.KILL) + vim.wait(timeout or state.timeout or MAX_TIMEOUT, function() + return state.result ~= nil + end, nil, true) + end + + return state.result +end + +--- @param data string[]|string|nil +function SystemObj:write(data) + local stdin = self._state.stdin + + if not stdin then + error('stdin has not been opened on this object') + end + + if type(data) == 'table' then + for _, v in ipairs(data) do + stdin:write(v) + stdin:write('\n') + end + elseif type(data) == 'string' then + stdin:write(data) + elseif data == nil then + -- Shutdown the write side of the duplex stream and then close the pipe. + -- Note shutdown will wait for all the pending write requests to complete + -- TODO(lewis6991): apparently shutdown doesn't behave this way. + -- (https://github.com/neovim/neovim/pull/17620#discussion_r820775616) + stdin:write('', function() + stdin:shutdown(function() + if stdin then + stdin:close() + end + end) + end) + end +end + +--- @return boolean +function SystemObj:is_closing() + local handle = self._state.handle + return handle == nil or handle:is_closing() or false +end + +--- @param output fun(err:string?, data: string?)|false +--- @return uv.uv_stream_t? +--- @return fun(err:string?, data: string?)? Handler +local function setup_output(output) + if output == nil then + return assert(uv.new_pipe(false)), nil + end + + if type(output) == 'function' then + return assert(uv.new_pipe(false)), output + end + + assert(output == false) + return nil, nil +end + +--- @param input string|string[]|true|nil +--- @return uv.uv_stream_t? +--- @return string|string[]? +local function setup_input(input) + if not input then + return + end + + local towrite --- @type string|string[]? + if type(input) == 'string' or type(input) == 'table' then + towrite = input + end + + return assert(uv.new_pipe(false)), towrite +end + +--- @return table +local function base_env() + local env = vim.fn.environ() --- @type table + env['NVIM'] = vim.v.servername + env['NVIM_LISTEN_ADDRESS'] = nil + return env +end + +--- uv.spawn will completely overwrite the environment +--- when we just want to modify the existing one, so +--- make sure to prepopulate it with the current env. +--- @param env? table +--- @param clear_env? boolean +--- @return string[]? +local function setup_env(env, clear_env) + if clear_env then + return env + end + + --- @type table + env = vim.tbl_extend('force', base_env(), env or {}) + + local renv = {} --- @type string[] + for k, v in pairs(env) do + renv[#renv + 1] = string.format('%s=%s', k, tostring(v)) + end + + return renv +end + +--- @param stream uv.uv_stream_t +--- @param text? boolean +--- @param bucket string[] +--- @return fun(err: string?, data: string?) +local function default_handler(stream, text, bucket) + return function(err, data) + if err then + error(err) + end + if data ~= nil then + if text then + bucket[#bucket + 1] = data:gsub('\r\n', '\n') + else + bucket[#bucket + 1] = data + end + else + stream:read_stop() + stream:close() + end + end +end + +--- @param cmd string +--- @param opts uv.spawn.options +--- @param on_exit fun(code: integer, signal: integer) +--- @param on_error fun() +--- @return uv.uv_process_t, integer +local function spawn(cmd, opts, on_exit, on_error) + local handle, pid_or_err = uv.spawn(cmd, opts, on_exit) + if not handle then + on_error() + error(pid_or_err) + end + return handle, pid_or_err --[[@as integer]] +end + +--- @param timeout integer +--- @param cb fun() +--- @return uv.uv_timer_t +local function timer_oneshot(timeout, cb) + local timer = assert(uv.new_timer()) + timer:start(timeout, 0, function() + timer:stop() + timer:close() + cb() + end) + return timer +end + +--- @param state vim.SystemState +--- @param code integer +--- @param signal integer +--- @param on_exit fun(result: vim.SystemCompleted)? +local function _on_exit(state, code, signal, on_exit) + close_handles(state) + + local check = assert(uv.new_check()) + check:start(function() + for _, pipe in pairs({ state.stdin, state.stdout, state.stderr }) do + if not pipe:is_closing() then + return + end + end + check:stop() + check:close() + + if state.done == nil then + state.done = true + end + + if (code == 0 or code == 1) and state.done == 'timeout' then + -- Unix: code == 0 + -- Windows: code == 1 + code = 124 + end + + local stdout_data = state.stdout_data + local stderr_data = state.stderr_data + + state.result = { + code = code, + signal = signal, + stdout = stdout_data and table.concat(stdout_data) or nil, + stderr = stderr_data and table.concat(stderr_data) or nil, + } + + if on_exit then + on_exit(state.result) + end + end) +end + +--- Run a system command +--- +--- @param cmd string[] +--- @param opts? vim.SystemOpts +--- @param on_exit? fun(out: vim.SystemCompleted) +--- @return vim.SystemObj +local function system(cmd, opts, on_exit) + local __FUNC__ = 'run_job' + vim.validate({ + cmd = { cmd, 'table' }, + opts = { opts, 'table', true }, + on_exit = { on_exit, 'function', true }, + }) + + opts = opts or {} + + local stdout, stdout_handler = setup_output(opts.stdout) + local stderr, stderr_handler = setup_output(opts.stderr) + local stdin, towrite = setup_input(opts.stdin) + + --- @type vim.SystemState + local state = { + done = false, + cmd = cmd, + timeout = opts.timeout, + stdin = stdin, + stdout = stdout, + stderr = stderr, + } + + --- @diagnostic disable-next-line:missing-fields + state.handle, state.pid = spawn(cmd[1], { + args = vim.list_slice(cmd, 2), + stdio = { stdin, stdout, stderr }, + cwd = opts.cwd, + --- @diagnostic disable-next-line:assign-type-mismatch + env = setup_env(opts.env, opts.clear_env), + detached = opts.detach, + hide = true, + }, function(code, signal) + _on_exit(state, code, signal, on_exit) + end, function() + close_handles(state) + end) + + if stdout then + state.stdout_data = {} + stdout:read_start(stdout_handler or default_handler(stdout, opts.text, state.stdout_data)) + end + + if stderr then + state.stderr_data = {} + stderr:read_start(stderr_handler or default_handler(stderr, opts.text, state.stderr_data)) + end + + local obj = new_systemobj(state) + + if towrite then + obj:write(towrite) + obj:write(nil) -- close the stream + end + + if opts.timeout then + state.timer = timer_oneshot(opts.timeout, function() + if state.handle and state.handle:is_active() then + obj:_timeout() + end + end) + end + + return obj +end + +return system diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/test.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/test.lua new file mode 100644 index 00000000..be60d9be --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/test.lua @@ -0,0 +1,34 @@ +local M = {} + +local function eq(act, exp) + assert(act == exp, string.format('%s != %s', act, exp)) +end + +M._tests = {} + +M._tests.expand_format = function() + local util = require('gitsigns.util') + assert('hello % world % 2021' == util.expand_format(' % % ', { + var1 = 'hello', + var2 = 'world', + var_time = 1616838297, + })) +end + +M._tests.test_args = function() + local parse_args = require('gitsigns.cli.argparse').parse_args + + local pos_args, named_args = parse_args('hello there key=value, key1="a b c"') + + eq(pos_args[1], 'hello') + eq(pos_args[2], 'there') + eq(named_args.key, 'value,') + eq(named_args.key1, 'a b c') + + pos_args, named_args = parse_args('base=HEAD~1 posarg') + + eq(named_args.base, 'HEAD~1') + eq(pos_args[1], 'posarg') +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/util.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/util.lua new file mode 100644 index 00000000..613c6679 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/util.lua @@ -0,0 +1,369 @@ +local M = {} + +function M.path_exists(path) + return vim.loop.fs_stat(path) and true or false +end + +local jit_os --- @type string + +if jit then + jit_os = jit.os:lower() +end + +local is_unix = false +if jit_os then + is_unix = jit_os == 'linux' or jit_os == 'osx' or jit_os == 'bsd' +else + local binfmt = package.cpath:match('%p[\\|/]?%p(%a+)') + is_unix = binfmt ~= 'dll' +end + +--- @param file string +--- @return string +function M.dirname(file) + return file:match(string.format('^(.+)%s[^%s]+', M.path_sep, M.path_sep)) +end + +--- @param path string +--- @return string[] +function M.file_lines(path) + local file = assert(io.open(path, 'rb')) + local contents = file:read('*a') + file:close() + return vim.split(contents, '\n') +end + +M.path_sep = package.config:sub(1, 1) + +--- @param ... integer +--- @return string +local function make_bom(...) + local r = {} + ---@diagnostic disable-next-line:no-unknown + for i, a in ipairs({ ... }) do + ---@diagnostic disable-next-line:no-unknown + r[i] = string.char(a) + end + return table.concat(r) +end + +local BOM_TABLE = { + ['utf-8'] = make_bom(0xef, 0xbb, 0xbf), + ['utf-16le'] = make_bom(0xff, 0xfe), + ['utf-16'] = make_bom(0xfe, 0xff), + ['utf-16be'] = make_bom(0xfe, 0xff), + ['utf-32le'] = make_bom(0xff, 0xfe, 0x00, 0x00), + ['utf-32'] = make_bom(0xff, 0xfe, 0x00, 0x00), + ['utf-32be'] = make_bom(0x00, 0x00, 0xfe, 0xff), + ['utf-7'] = make_bom(0x2b, 0x2f, 0x76), + ['utf-1'] = make_bom(0xf7, 0x54, 0x4c), +} + +---@param x string +---@param encoding string +---@return string +local function add_bom(x, encoding) + local bom = BOM_TABLE[encoding] + if bom then + return bom .. x + end + return x +end + +--- @param bufnr integer +--- @return string[] +function M.buf_lines(bufnr) + -- nvim_buf_get_lines strips carriage returns if fileformat==dos + local buftext = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + + local dos = vim.bo[bufnr].fileformat == 'dos' + + if dos then + for i = 1, #buftext - 1 do + buftext[i] = buftext[i] .. '\r' + end + end + + if vim.bo[bufnr].endofline then + -- Add CR to the last line + if dos then + buftext[#buftext] = buftext[#buftext] .. '\r' + end + buftext[#buftext + 1] = '' + end + + if vim.bo[bufnr].bomb then + buftext[1] = add_bom(buftext[1], vim.bo[bufnr].fileencoding) + end + + return buftext +end + +--- @param buf integer +local function delete_alt(buf) + local alt = vim.api.nvim_buf_call(buf, function() + return vim.fn.bufnr('#') + end) + if alt ~= buf and alt ~= -1 then + pcall(vim.api.nvim_buf_delete, alt, { force = true }) + end +end + +--- @param bufnr integer +--- @param name string +function M.buf_rename(bufnr, name) + vim.api.nvim_buf_set_name(bufnr, name) + delete_alt(bufnr) +end + +--- @param bufnr integer +--- @param start_row integer +--- @param end_row integer +--- @param lines string[] +function M.set_lines(bufnr, start_row, end_row, lines) + if vim.bo[bufnr].fileformat == 'dos' then + lines = M.strip_cr(lines) + end + if start_row == 0 and end_row == -1 and lines[#lines] == '' then + lines = vim.deepcopy(lines) + lines[#lines] = nil + end + vim.api.nvim_buf_set_lines(bufnr, start_row, end_row, false, lines) +end + +--- @return string +function M.tmpname() + if is_unix then + return os.tmpname() + end + return vim.fn.tempname() +end + +--- @param time number +--- @param divisor integer +--- @param time_word string +--- @return string +local function to_relative_string(time, divisor, time_word) + local num = math.floor(time / divisor) + if num > 1 then + time_word = time_word .. 's' + end + + return num .. ' ' .. time_word .. ' ago' +end + +--- @param timestamp number +--- @return string +function M.get_relative_time(timestamp) + local current_timestamp = os.time() + local elapsed = current_timestamp - timestamp + + if elapsed == 0 then + return 'a while ago' + end + + local minute_seconds = 60 + local hour_seconds = minute_seconds * 60 + local day_seconds = hour_seconds * 24 + local month_seconds = day_seconds * 30 + local year_seconds = month_seconds * 12 + + if elapsed < minute_seconds then + return to_relative_string(elapsed, 1, 'second') + elseif elapsed < hour_seconds then + return to_relative_string(elapsed, minute_seconds, 'minute') + elseif elapsed < day_seconds then + return to_relative_string(elapsed, hour_seconds, 'hour') + elseif elapsed < month_seconds then + return to_relative_string(elapsed, day_seconds, 'day') + elseif elapsed < year_seconds then + return to_relative_string(elapsed, month_seconds, 'month') + else + return to_relative_string(elapsed, year_seconds, 'year') + end +end + +--- @param opts vim.api.keyset.redraw +function M.redraw(opts) + if vim.fn.has('nvim-0.10') == 1 then + vim.api.nvim__redraw(opts) + else + vim.api.nvim__buf_redraw_range(opts.buf, opts.range[1], opts.range[2]) + end +end + +--- @param xs string[] +--- @return boolean +local function is_dos(xs) + -- Do not check CR at EOF + for i = 1, #xs - 1 do + if xs[i]:sub(-1) ~= '\r' then + return false + end + end + return true +end + +--- Strip '\r' from the EOL of each line only if all lines end with '\r' +--- @param xs0 string[] +--- @return string[] +function M.strip_cr(xs0) + if not is_dos(xs0) then + -- don't strip, return early + return xs0 + end + + -- all lines end with '\r', need to strip + local xs = vim.deepcopy(xs0) + for i = 1, #xs do + xs[i] = xs[i]:sub(1, -2) + end + return xs +end + +--- @param base? string +--- @return string? +function M.norm_base(base) + if base == ':0' then + return + end + if base and base:sub(1, 1):match('[~\\^]') then + base = 'HEAD' .. base + end + return base +end + +function M.emptytable() + return setmetatable({}, { + ---@param t table + ---@param k any + ---@return any + __index = function(t, k) + t[k] = {} + return t[k] + end, + }) +end + +local function expand_date(fmt, time) + if fmt == '%R' then + return M.get_relative_time(time) + end + return os.date(fmt, time) +end + +---@param fmt string +---@param info table +---@param reltime? boolean Use relative time as the default date format +---@return string +function M.expand_format(fmt, info, reltime) + local ret = {} --- @type string[] + + for _ = 1, 20 do -- loop protection + -- Capture or + local scol, ecol, match, key, time_fmt = fmt:find('(<([^:>]+):?([^>]*)>)') + if not match then + break + end + --- @cast key string + + ret[#ret + 1], fmt = fmt:sub(1, scol - 1), fmt:sub(ecol + 1) + + local v = info[key] + + if v then + if type(v) == 'table' then + v = table.concat(v, '\n') + end + if vim.endswith(key, '_time') then + if time_fmt == '' then + time_fmt = reltime and '%R' or '%Y-%m-%d' + end + v = expand_date(time_fmt, v) + end + match = tostring(v) + end + ret[#ret + 1] = match + end + + ret[#ret + 1] = fmt + return table.concat(ret, '') +end + +--- @param buf string +--- @return boolean +function M.bufexists(buf) + --- @diagnostic disable-next-line:param-type-mismatch + return vim.fn.bufexists(buf) == 1 +end + +--- @param x Gitsigns.BlameInfo +--- @return Gitsigns.BlameInfoPublic +function M.convert_blame_info(x) + --- @type Gitsigns.BlameInfoPublic + local ret = vim.tbl_extend('error', x, x.commit) + ret.commit = nil + return ret +end + +--- Efficiently remove items from middle of a list a list. +--- +--- Calling table.remove() in a loop will re-index the tail of the table on +--- every iteration, instead this function will re-index the table exactly +--- once. +--- +--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524 +--- +---@param t any[] +---@param first integer +---@param last integer +function M.list_remove(t, first, last) + local n = #t + for i = 0, n - first do + t[first + i] = t[last + 1 + i] + t[last + 1 + i] = nil + end +end + +--- Efficiently insert items into the middle of a list. +--- +--- Calling table.insert() in a loop will re-index the tail of the table on +--- every iteration, instead this function will re-index the table exactly +--- once. +--- +--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524 +--- +---@param t any[] +---@param first integer +---@param last integer +---@param v any +function M.list_insert(t, first, last, v) + local n = #t + + -- Shift table forward + for i = n - first, 0, -1 do + t[last + 1 + i] = t[first + i] + end + + -- Fill in new values + for i = first, last do + t[i] = v + end +end + +--- Run a function once and ignore subsequent calls +--- @generic F: function +--- @param fn F +--- @return F +function M.once(fn) + local called = false + return function(...) + if called then + return + end + called = true + return fn(...) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/watcher.lua b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/watcher.lua new file mode 100644 index 00000000..65218aa9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/lua/gitsigns/watcher.lua @@ -0,0 +1,150 @@ +local api = vim.api +local uv = vim.loop + +local Status = require('gitsigns.status') +local async = require('gitsigns.async') +local log = require('gitsigns.debug.log') +local util = require('gitsigns.util') + +local cache = require('gitsigns.cache').cache +local config = require('gitsigns.config').config +local debounce_trailing = require('gitsigns.debounce').debounce_trailing +local manager = require('gitsigns.manager') + +local dprint = log.dprint +local dprintf = log.dprintf + +--- @param bufnr integer +--- @param old_relpath string +local function handle_moved(bufnr, old_relpath) + local bcache = cache[bufnr] + local git_obj = bcache.git_obj + + local new_name = git_obj:has_moved() + if new_name then + dprintf('File moved to %s', new_name) + git_obj.relpath = new_name + if not git_obj.orig_relpath then + git_obj.orig_relpath = old_relpath + end + elseif git_obj.orig_relpath then + local orig_file = git_obj.repo.toplevel .. util.path_sep .. git_obj.orig_relpath + if not git_obj:file_info(orig_file).relpath then + return + end + --- File was moved in the index, but then reset + dprintf('Moved file reset') + git_obj.relpath = git_obj.orig_relpath + git_obj.orig_relpath = nil + else + -- File removed from index, do nothing + return + end + + git_obj.file = git_obj.repo.toplevel .. util.path_sep .. git_obj.relpath + bcache.file = git_obj.file + git_obj:update() + if not manager.schedule(bufnr) then + return + end + + local bufexists = util.bufexists(bcache.file) + local old_name = api.nvim_buf_get_name(bufnr) + + if not bufexists then + util.buf_rename(bufnr, bcache.file) + end + + local msg = bufexists and 'Cannot rename' or 'Renamed' + dprintf('%s buffer %d from %s to %s', msg, bufnr, old_name, bcache.file) +end + +--- @param bufnr integer +local watcher_handler = async.create(1, function(bufnr) + local __FUNC__ = 'watcher_handler' + + -- Avoid cache hit for detached buffer + -- ref: https://github.com/lewis6991/gitsigns.nvim/issues/956 + if not manager.schedule(bufnr) then + return + end + + local git_obj = cache[bufnr].git_obj + + git_obj.repo:update_abbrev_head() + + if not manager.schedule(bufnr) then + return + end + + Status:update(bufnr, { head = git_obj.repo.abbrev_head }) + + local was_tracked = git_obj.object_name ~= nil + local old_relpath = git_obj.relpath + + git_obj:update() + if not manager.schedule(bufnr) then + return + end + + if config.watch_gitdir.follow_files and was_tracked and not git_obj.object_name then + -- File was tracked but is no longer tracked. Must of been removed or + -- moved. Check if it was moved and switch to it. + handle_moved(bufnr, old_relpath) + if not manager.schedule(bufnr) then + return + end + end + + cache[bufnr]:invalidate(true) + + require('gitsigns.manager').update(bufnr) +end) + +local watcher_handler_debounced = debounce_trailing(200, watcher_handler, 1) + +--- vim.inspect but on one line +--- @param x any +--- @return string +local function inspect(x) + return vim.inspect(x, { indent = '', newline = ' ' }) +end + +local M = {} + +local WATCH_IGNORE = { + ORIG_HEAD = true, + FETCH_HEAD = true, +} + +--- @param bufnr integer +--- @param gitdir string +--- @return uv.uv_fs_event_t +function M.watch_gitdir(bufnr, gitdir) + dprintf('Watching git dir') + local w = assert(uv.new_fs_event()) + w:start(gitdir, {}, function(err, filename, events) + local __FUNC__ = 'watcher_cb' + if err then + dprintf('Git dir update error: %s', err) + return + end + + local info = string.format("Git dir update: '%s' %s", filename, inspect(events)) + + -- The luv docs say filename is passed as a string but it has been observed + -- to sometimes be nil. + -- https://github.com/lewis6991/gitsigns.nvim/issues/848 + if filename == nil or WATCH_IGNORE[filename] or vim.endswith(filename, '.lock') then + dprintf('%s (ignoring)', info) + return + end + + dprint(info) + + watcher_handler_debounced(bufnr) + end) + return w +end + +return M diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/nix-support/propagated-build-inputs b/config/neovim/store/lazy-plugins/gitsigns.nvim/nix-support/propagated-build-inputs new file mode 100644 index 00000000..b3b8c91f --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/nix-support/propagated-build-inputs @@ -0,0 +1 @@ +/nix/store/mqbhz05llkddfb5wni0m48kw22ixxps4-lua-5.1.5 \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/gitsigns.nvim/rock_manifest b/config/neovim/store/lazy-plugins/gitsigns.nvim/rock_manifest new file mode 100644 index 00000000..694461c3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/gitsigns.nvim/rock_manifest @@ -0,0 +1,49 @@ +rock_manifest = { + doc = { + ["gitsigns.txt"] = "b9579713a56199f1374b4e1b12578f42" + }, + ["gitsigns.nvim-scm-1.rockspec"] = "a9b165d604ce401cfeea760a9366418d", + lua = { + gitsigns = { + ["actions.lua"] = "e1d7610fc1d5fc73cb891b286df0535e", + ["async.lua"] = "15f310cd469f5bfa46bb1e49e5a1d61c", + ["attach.lua"] = "097403415eba4dc5da07fdb948647841", + ["cache.lua"] = "909e8cdc787ca5e6d3ed62782dd5453b", + cli = { + ["argparse.lua"] = "fb6c9ffda01b2090f3c252ecaf00f68a" + }, + ["cli.lua"] = "f00c3494d6be1b07c352c51132150586", + ["config.lua"] = "fa4a0f95747586aab60c9b848a72d9cf", + ["current_line_blame.lua"] = "bf5426f4569e207646c39f9d47a083af", + ["debounce.lua"] = "e0c1145a3dc341f46b36b43d814c1202", + debug = { + ["log.lua"] = "897a3bf45d0996b9517fa8c0a2ba1dac" + }, + ["debug.lua"] = "ef5c8e3c1c9da306ed7b2eb33e657236", + ["diff.lua"] = "0c462ae71c77899e81da151dcfdf77eb", + ["diff_ext.lua"] = "04005195067132403fc336422e05c7d3", + ["diff_int.lua"] = "df447e56f11906998e81d5b94499e013", + ["diffthis.lua"] = "eb4ff5d430d2c4081f1fa9651d2768f7", + git = { + ["version.lua"] = "ab78b8d8a84dd92655e4847223362923" + }, + ["git.lua"] = "082cec98968b0eca1f97f54cbd4ad880", + ["highlight.lua"] = "1d197d8f0f6f69a6455ac220a6890d80", + ["hunks.lua"] = "48fc2d8a9c89815e3c0521b1bb0788e9", + ["manager.lua"] = "af94331f013ed04d175e3c2b2d21c42b", + ["message.lua"] = "523ee4df9da4a7fa9b5ab2100eb27ac4", + ["popup.lua"] = "cbc11678e30094bff8ed54f0357e26f9", + ["repeat.lua"] = "798544de97c774bc8e10eecfd6479585", + ["signs.lua"] = "4c6533b4a406b049e68f49aee59417f6", + ["status.lua"] = "a229a7213a3fbecfcf2d98aaf587fcab", + system = { + ["compat.lua"] = "05d2b6d08602fba46be9b540288091e7" + }, + ["system.lua"] = "d73fac4f6e734cc8ce9f13d6be027503", + ["test.lua"] = "082f7c7a556bf27352a378d047b8e79b", + ["util.lua"] = "e4c4d677e3ad296adee833318a5c5845", + ["watcher.lua"] = "ffcf36424ae17548bdc629cc2de3fcaa" + }, + ["gitsigns.lua"] = "f423758eba3dfcca59f5a0710b58463f" + } +} diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/doc/haskell-tools.txt b/config/neovim/store/lazy-plugins/haskell-tools.nvim/doc/haskell-tools.txt new file mode 100644 index 00000000..d0334387 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/doc/haskell-tools.txt @@ -0,0 +1,519 @@ +============================================================================== +Table of Contents *haskell-tools.contents* + +Introduction ··························································· |intro| +The haskell-tools module ······································· |haskell-tools| +plugin configuration ···································· |haskell-tools.config| +haskell-language-server LSP client tools ··················· |haskell-tools.lsp| +haskell-tools nvim-dap setup ······························· |haskell-tools.dap| +haskell-tools Hoogle search ····························· |haskell-tools.hoogle| +haskell-tools GHCi REPL module ···························· |haskell-tools.repl| +haskell-tools Project module ··························· |haskell-tools.project| +haskell-tools fast-tags module ···························· |haskell-tools.tags| +haskell-tools Logging ······································ |haskell-tools.log| + +============================================================================== +Introduction *intro* + +This plugin automatically configures the `haskell-language-server` builtin LSP client +and integrates with other haskell tools. + +WARNING: +Do not call the `lspconfig.hls` setup or set up the lsp manually, +as doing so may cause conflicts. + +NOTE: This plugin is a filetype plugin. +There is no need to call a `setup` function. + +============================================================================== +The haskell-tools module *haskell-tools* + +Entry-point into this plugin's public API. + +HaskellTools *HaskellTools* + + +============================================================================== +plugin configuration *haskell-tools.config* + +To configure haskell-tools.nvim, set the variable `vim.g.haskell_tools`, +which is a `HTOpts` table, in your neovim configuration. + +Example: +> +---@type HTOpts +vim.g.haskell_tools = { + ---@type ToolsOpts + tools = { + -- ... + }, + ---@type HaskellLspClientOpts + hls = { + on_attach = function(client, bufnr) + -- Set keybindings, etc. here. + end, + -- ... + }, + ---@type HTDapOpts + dap = { + -- ... + }, + } +< + +Note: `vim.g.haskell_tools` can also be a function that returns a 'HTOpts' table. + + +HTOpts *HTOpts* + + Fields: ~ + {tools?} (ToolsOpts) haskell-tools module options. + {hls?} (HaskellLspClientOpts) haskell-language-server client options. + {dap?} (HTDapOpts) debug adapter config for nvim-dap. + + +ToolsOpts *ToolsOpts* + + Fields: ~ + {codeLens?} (CodeLensOpts) LSP codeLens options. + {hoogle?} (HoogleOpts) Hoogle type signature search options. + {hover?} (HoverOpts) LSP hover options. + {definition?} (DefinitionOpts) LSP go-to-definition options. + {repl?} (ReplOpts) GHCi repl options. + {tags?} (FastTagsOpts) fast-tags module options. + {log?} (HTLogOpts) haskell-tools logger options. + + +CodeLensOpts *CodeLensOpts* + + Fields: ~ + {autoRefresh?} (fun():boolean|boolean) (default: `true`) Whether to auto-refresh code-lenses. + + +HoogleOpts *HoogleOpts* + + Fields: ~ + {mode?} (HoogleMode) Use a telescope with a local hoogle installation or a web backend, or use the browser for hoogle signature search? + + +HoogleMode *HoogleMode* + + Type: ~ + "auto"|"telescope-local"|"telescope-web"|"browser" + + +HoverOpts *HoverOpts* + + Fields: ~ + {enable?} (fun():boolean|boolean) (default: `true`) Whether to enable haskell-tools hover. + {border?} (string[][]) The hover window's border. Set to `nil` to disable. + {stylize_markdown?} (boolean) (default: `false`) The builtin LSP client's default behaviour is to stylize markdown. Setting this option to false sets the file type to markdown and enables treesitter syntax highligting for Haskell snippets if nvim-treesitter is installed. + {auto_focus?} (boolean) (default: `false`) Whether to automatically switch to the hover window. + + +DefinitionOpts *DefinitionOpts* + + Fields: ~ + {hoogle_signature_fallback?} (fun():boolean|boolean) (default: `false`) Configure `vim.lsp.definition` to fall back to hoogle search (does not affect `vim.lsp.tagfunc`). + + +ReplOpts *ReplOpts* + + Fields: ~ + {handler?} (fun():ReplHandler|ReplHandler) `'builtin'`: Use the simple builtin repl. `'toggleterm'`: Use akinsho/toggleterm.nvim. + {prefer?} (fun():repl_backend|repl_backend) Prefer cabal or stack when both stack and cabal project files are present? + {builtin?} (BuiltinReplOpts) Configuration for the builtin repl. + {auto_focus?} (boolean) Whether to auto-focus the repl on toggle or send. If unset, the handler decides. + + +ReplHandler *ReplHandler* + + Type: ~ + "builtin"|"toggleterm" + + +repl_backend *repl_backend* + + Type: ~ + "cabal"|"stack" + + +BuiltinReplOpts *BuiltinReplOpts* + + Fields: ~ + {create_repl_window?} (fun(view:ReplView):fun(mk_repl_cmd:mk_repl_cmd_fun)) + + +ReplView *ReplView* + + Fields: ~ + {create_repl_split?} (fun(opts:ReplViewOpts):mk_repl_cmd_fun) Create the REPL in a horizontally split window. + {create_repl_vsplit?} (fun(opts:ReplViewOpts):mk_repl_cmd_fun) Create the REPL in a vertically split window. + {create_repl_tabnew?} (fun(opts:ReplViewOpts):mk_repl_cmd_fun) Create the REPL in a new tab. + {create_repl_cur_win?} (fun(opts:ReplViewOpts):mk_repl_cmd_fun) Create the REPL in the current window. + + +ReplViewOpts *ReplViewOpts* + + Fields: ~ + {delete_buffer_on_exit?} (boolean) Whether to delete the buffer when the Repl quits. + {size?} (fun():number|number) The size of the window or a function that determines it. + + +mk_repl_cmd_fun *mk_repl_cmd_fun* + + Type: ~ + fun():string[]|nil + + +FastTagsOpts *FastTagsOpts* + + Fields: ~ + {enable?} (boolean|fun():boolean) + + +HTLogOpts *HTLogOpts* + + Fields: ~ + {level?} (number|string) The log level. + + See: ~ + |vim.log.levels| + + +HaskellLspClientOpts *HaskellLspClientOpts* + + Fields: ~ + {auto_attach?} (fun():boolean|boolean) Whether to automatically attach the LSP client. Defaults to `true` if the haskell-language-server executable is found. + {debug?} (boolean) Whether to enable haskell-language-server debug logging. + {on_attach?} (fun(client:number,bufnr:number,ht:HaskellTools)) Callback that is invoked when the client attaches to a buffer. + {cmd?} (fun():string[]|string[]) The command to start haskell-language-server with. + {capabilities?} (lsp.ClientCapabilities) LSP client capabilities. + {settings?} (fun(project_root:string|nil):table|table) The haskell-language-server settings or a function that creates them. To view the default settings, run `haskell-language-server generate-default-config`. + {default_settings?} (table) The default haskell-language-server settings that will be used if no settings are specified or detected. + {logfile?} (string) The path to the haskell-language-server log file. + + + To print all options that are available for your haskell-language-server version, run `haskell-language-server-wrapper generate-default-config` +See: https://haskell-language-server.readthedocs.io/en/latest/configuration.html. + +HTDapOpts *HTDapOpts* + + Fields: ~ + {cmd?} (string[]) The command to start the debug adapter server with. + {logFile?} (string) Log file path for detected configurations. + {logLevel?} (HaskellDebugAdapterLogLevel) The log level for detected configurations. + {auto_discover?} (boolean|AddDapConfigOpts) Set to `false` to disable auto-discovery of launch configurations. `true` uses the default configurations options`. + + +HaskellDebugAdapterLogLevel *HaskellDebugAdapterLogLevel* + + Type: ~ + "Debug"|"Info"|"Warning"|"Error" + + +============================================================================== +haskell-language-server LSP client tools *haskell-tools.lsp* + + The following commands are available: + + * `:HlsStart` - Start the LSP client. + * `:HlsStop` - Stop the LSP client. + * `:HlsRestart` - Restart the LSP client. + * `:HlsEvalAll` - Evaluate all code snippets in comments. + +LoadHlsSettingsOpts *LoadHlsSettingsOpts* + + Fields: ~ + {settings_file_pattern} (string|nil) File name or pattern to search for. Defaults to 'hls.json' + + +HlsTools *HlsTools* + + + *HlsTools.load_hls_settings* +HlsTools.load_hls_settings({project_root}, {opts}) + Search the project root for a haskell-language-server settings JSON file and load it to a Lua table. + Falls back to the `hls.default_settings` if no file is found or file cannot be read or decoded. + + Parameters: ~ + {project_root} (string|nil) The project root + {opts} (LoadHlsSettingsOpts|nil) + + Returns: ~ + (table) hls_settings + + See: ~ + |https://haskell-language-server.readthedocs.io/en/latest/configuration.html| + + +HlsTools.start({bufnr}) *HlsTools.start* + Start or attach the LSP client. + Fails silently if the buffer's filetype is not one of the filetypes specified in the config. + + Parameters: ~ + {bufnr} (number|nil) The buffer number (optional), defaults to the current buffer + + Returns: ~ + (number|nil) The LSP client ID + + +HlsTools.stop({bufnr}) *HlsTools.stop* + Stop the LSP client. + + Parameters: ~ + {bufnr} (number|nil) The buffer number (optional), defaults to the current buffer + + Returns: ~ + (table[]) A list of clients that will be stopped + + +HlsTools.restart({bufnr}) *HlsTools.restart* + Restart the LSP client. + Fails silently if the buffer's filetype is not one of the filetypes specified in the config. + + Parameters: ~ + {bufnr} (number|nil) The buffer number (optional), defaults to the current buffer + + Returns: ~ + (number|nil) The LSP client ID after restart + + +HlsTools.buf_eval_all({bufnr}) *HlsTools.buf_eval_all* + Evaluate all code snippets in comments. + + Parameters: ~ + {bufnr} (number|nil) Defaults to the current buffer. + + Returns: ~ + (nil) + + +============================================================================== +haskell-tools nvim-dap setup *haskell-tools.dap* + +HsDapLaunchConfiguration *HsDapLaunchConfiguration* + + +HsDapTools *HsDapTools* + + +AddDapConfigOpts *AddDapConfigOpts* + + + *HsDapTools.discover_configurations* +HsDapTools.discover_configurations({bufnr}, {opts}) + Discover nvim-dap launch configurations for haskell-debug-adapter. + + Parameters: ~ + {bufnr} (number|nil) The buffer number + {opts} (AddDapConfigOpts|nil) + + Returns: ~ + (nil) + + +============================================================================== +haskell-tools Hoogle search *haskell-tools.hoogle* + +HoogleTools *HoogleTools* + + +HoogleTools.hoogle_signature({options}) *HoogleTools.hoogle_signature* + + Parameters: ~ + {options} (table|nil) Includes the `search_term` and options to pass to the telescope picker (if available) + + Returns: ~ + (nil) + + +============================================================================== +haskell-tools GHCi REPL module *haskell-tools.repl* + + The following commands are available: + + * `:HtReplToggle` - Toggle a GHCi repl. + * `:HtReplQuit` - Quit the current repl. + * `:HtReplLoad` - Load a Haskell file into the repl. + * `:HtReplReload` - Reload the current repl. + +HsReplTools *HsReplTools* + + +HsReplTools.buf_mk_repl_cmd() *HsReplTools.buf_mk_repl_cmd* + Create the command to create a repl for the current buffer. + + Returns: ~ + (table|nil) command + + +HsReplTools.operator() *HsReplTools.operator* + + See: ~ + |operatorfunc| + + +HsReplTools.paste({reg}) *HsReplTools.paste* + Paste from register `reg` to the REPL + + Parameters: ~ + {reg} (string|nil) register (defaults to '"') + + +HsReplTools.paste_type({reg}) *HsReplTools.paste_type* + Query the REPL for the type of register `reg` + + Parameters: ~ + {reg} (string|nil) register (defaults to '"') + + +HsReplTools.cword_type() *HsReplTools.cword_type* + Query the REPL for the type of word under the cursor + + +HsReplTools.paste_info({reg}) *HsReplTools.paste_info* + Query the REPL for info on register `reg` + + Parameters: ~ + {reg} (string|nil) register (defaults to '"') + + +HsReplTools.cword_info() *HsReplTools.cword_info* + Query the REPL for the type of word under the cursor + + +HsReplTools.load_file({filepath}) *HsReplTools.load_file* + Load a file into the REPL + + Parameters: ~ + {filepath} (string) The absolute file path + + +HsReplTools.reload() *HsReplTools.reload* + Reload the repl + + +============================================================================== +haskell-tools Project module *haskell-tools.project* + + The following commands are available: + + * `:HsProjectFile` - Open the project file for the current buffer (cabal.project or stack.yaml). + * `:HsPackageYaml` - Open the package.yaml file for the current buffer. + * `:HsPackageCabal` - Open the *.cabal file for the current buffer. + +HsProjectTools *HsProjectTools* + + +HsProjectTools.root_dir({project_file}) *HsProjectTools.root_dir* + Get the project's root directory + + Parameters: ~ + {project_file} (string) The path to a project file + + Returns: ~ + (string|nil) + + +HsProjectTools.open_package_yaml() *HsProjectTools.open_package_yaml* + Open the package.yaml of the package containing the current buffer. + + Returns: ~ + (nil) + + +HsProjectTools.open_package_cabal() *HsProjectTools.open_package_cabal* + Open the *.cabal file of the package containing the current buffer. + + Returns: ~ + (nil) + + +HsProjectTools.open_project_file() *HsProjectTools.open_project_file* + Open the current buffer's project file (cabal.project or stack.yaml). + + Returns: ~ + (nil) + + +============================================================================== +haskell-tools fast-tags module *haskell-tools.tags* + +GenerateProjectTagsOpts *GenerateProjectTagsOpts* + + Fields: ~ + {refresh} (boolean) Whether to refresh the tags if they have already been generated + + +FastTagsTools *FastTagsTools* + for the project (default: true) + + + *FastTagsTools.generate_project_tags* +FastTagsTools.generate_project_tags({path}, {opts}) + Generates tags for the current project + + Parameters: ~ + {path} (string|nil) File path + {opts} (GenerateProjectTagsOpts|nil) Options + + + *FastTagsTools.generate_package_tags* +FastTagsTools.generate_package_tags({path}) + Generate tags for the package containing `path` + + Parameters: ~ + {path} (string|nil) File path + + +============================================================================== +haskell-tools Logging *haskell-tools.log* + + The following commands are available: + + * `:HtLog` - Open the haskell-tools.nvim log file. + * `:HlsLog` - Open the haskell-language-server log file. + * `:HtSetLogLevel` - Set the haskell-tools.nvim and LSP client log level. + +HaskellToolsLog *HaskellToolsLog* + + +HaskellToolsLog.get_hls_logfile() *HaskellToolsLog.get_hls_logfile* + Get the haskell-language-server log file + + Returns: ~ + (string) filepath + + +HaskellToolsLog.get_logfile() *HaskellToolsLog.get_logfile* + Get the haskell-tools.nvim log file path. + + Returns: ~ + (string) filepath + + +HaskellToolsLog.nvim_open_hls_logfile() *HaskellToolsLog.nvim_open_hls_logfile* + Open the haskell-language-server log file + + Returns: ~ + (nil) + + +HaskellToolsLog.nvim_open_logfile() *HaskellToolsLog.nvim_open_logfile* + Open the haskell-tools.nvim log file. + + Returns: ~ + (nil) + + +HaskellToolsLog.set_level() *HaskellToolsLog.set_level* + + Returns: ~ + (nil) + + See: ~ + |vim.log.levels| + + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/doc/tags b/config/neovim/store/lazy-plugins/haskell-tools.nvim/doc/tags new file mode 100644 index 00000000..57884cf9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/doc/tags @@ -0,0 +1,69 @@ +AddDapConfigOpts haskell-tools.txt /*AddDapConfigOpts* +BuiltinReplOpts haskell-tools.txt /*BuiltinReplOpts* +CodeLensOpts haskell-tools.txt /*CodeLensOpts* +DefinitionOpts haskell-tools.txt /*DefinitionOpts* +FastTagsOpts haskell-tools.txt /*FastTagsOpts* +FastTagsTools haskell-tools.txt /*FastTagsTools* +FastTagsTools.generate_package_tags haskell-tools.txt /*FastTagsTools.generate_package_tags* +FastTagsTools.generate_project_tags haskell-tools.txt /*FastTagsTools.generate_project_tags* +GenerateProjectTagsOpts haskell-tools.txt /*GenerateProjectTagsOpts* +HTDapOpts haskell-tools.txt /*HTDapOpts* +HTLogOpts haskell-tools.txt /*HTLogOpts* +HTOpts haskell-tools.txt /*HTOpts* +HaskellDebugAdapterLogLevel haskell-tools.txt /*HaskellDebugAdapterLogLevel* +HaskellLspClientOpts haskell-tools.txt /*HaskellLspClientOpts* +HaskellTools haskell-tools.txt /*HaskellTools* +HaskellToolsLog haskell-tools.txt /*HaskellToolsLog* +HaskellToolsLog.get_hls_logfile haskell-tools.txt /*HaskellToolsLog.get_hls_logfile* +HaskellToolsLog.get_logfile haskell-tools.txt /*HaskellToolsLog.get_logfile* +HaskellToolsLog.nvim_open_hls_logfile haskell-tools.txt /*HaskellToolsLog.nvim_open_hls_logfile* +HaskellToolsLog.nvim_open_logfile haskell-tools.txt /*HaskellToolsLog.nvim_open_logfile* +HaskellToolsLog.set_level haskell-tools.txt /*HaskellToolsLog.set_level* +HlsTools haskell-tools.txt /*HlsTools* +HlsTools.buf_eval_all haskell-tools.txt /*HlsTools.buf_eval_all* +HlsTools.load_hls_settings haskell-tools.txt /*HlsTools.load_hls_settings* +HlsTools.restart haskell-tools.txt /*HlsTools.restart* +HlsTools.start haskell-tools.txt /*HlsTools.start* +HlsTools.stop haskell-tools.txt /*HlsTools.stop* +HoogleMode haskell-tools.txt /*HoogleMode* +HoogleOpts haskell-tools.txt /*HoogleOpts* +HoogleTools haskell-tools.txt /*HoogleTools* +HoogleTools.hoogle_signature haskell-tools.txt /*HoogleTools.hoogle_signature* +HoverOpts haskell-tools.txt /*HoverOpts* +HsDapLaunchConfiguration haskell-tools.txt /*HsDapLaunchConfiguration* +HsDapTools haskell-tools.txt /*HsDapTools* +HsDapTools.discover_configurations haskell-tools.txt /*HsDapTools.discover_configurations* +HsProjectTools haskell-tools.txt /*HsProjectTools* +HsProjectTools.open_package_cabal haskell-tools.txt /*HsProjectTools.open_package_cabal* +HsProjectTools.open_package_yaml haskell-tools.txt /*HsProjectTools.open_package_yaml* +HsProjectTools.open_project_file haskell-tools.txt /*HsProjectTools.open_project_file* +HsProjectTools.root_dir haskell-tools.txt /*HsProjectTools.root_dir* +HsReplTools haskell-tools.txt /*HsReplTools* +HsReplTools.buf_mk_repl_cmd haskell-tools.txt /*HsReplTools.buf_mk_repl_cmd* +HsReplTools.cword_info haskell-tools.txt /*HsReplTools.cword_info* +HsReplTools.cword_type haskell-tools.txt /*HsReplTools.cword_type* +HsReplTools.load_file haskell-tools.txt /*HsReplTools.load_file* +HsReplTools.operator haskell-tools.txt /*HsReplTools.operator* +HsReplTools.paste haskell-tools.txt /*HsReplTools.paste* +HsReplTools.paste_info haskell-tools.txt /*HsReplTools.paste_info* +HsReplTools.paste_type haskell-tools.txt /*HsReplTools.paste_type* +HsReplTools.reload haskell-tools.txt /*HsReplTools.reload* +LoadHlsSettingsOpts haskell-tools.txt /*LoadHlsSettingsOpts* +ReplHandler haskell-tools.txt /*ReplHandler* +ReplOpts haskell-tools.txt /*ReplOpts* +ReplView haskell-tools.txt /*ReplView* +ReplViewOpts haskell-tools.txt /*ReplViewOpts* +ToolsOpts haskell-tools.txt /*ToolsOpts* +haskell-tools haskell-tools.txt /*haskell-tools* +haskell-tools.config haskell-tools.txt /*haskell-tools.config* +haskell-tools.contents haskell-tools.txt /*haskell-tools.contents* +haskell-tools.dap haskell-tools.txt /*haskell-tools.dap* +haskell-tools.hoogle haskell-tools.txt /*haskell-tools.hoogle* +haskell-tools.log haskell-tools.txt /*haskell-tools.log* +haskell-tools.lsp haskell-tools.txt /*haskell-tools.lsp* +haskell-tools.project haskell-tools.txt /*haskell-tools.project* +haskell-tools.repl haskell-tools.txt /*haskell-tools.repl* +haskell-tools.tags haskell-tools.txt /*haskell-tools.tags* +intro haskell-tools.txt /*intro* +mk_repl_cmd_fun haskell-tools.txt /*mk_repl_cmd_fun* +repl_backend haskell-tools.txt /*repl_backend* diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/cabal.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/cabal.lua new file mode 100644 index 00000000..262f37c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/cabal.lua @@ -0,0 +1 @@ +require('haskell-tools.internal').ftplugin() diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/cabalproject.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/cabalproject.lua new file mode 100644 index 00000000..262f37c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/cabalproject.lua @@ -0,0 +1 @@ +require('haskell-tools.internal').ftplugin() diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/haskell.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/haskell.lua new file mode 100644 index 00000000..262f37c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/haskell.lua @@ -0,0 +1 @@ +require('haskell-tools.internal').ftplugin() diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/lhaskell.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/lhaskell.lua new file mode 100644 index 00000000..262f37c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/ftplugin/lhaskell.lua @@ -0,0 +1 @@ +require('haskell-tools.internal').ftplugin() diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/doc/haskell-tools.txt b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/doc/haskell-tools.txt new file mode 100644 index 00000000..d0334387 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/doc/haskell-tools.txt @@ -0,0 +1,519 @@ +============================================================================== +Table of Contents *haskell-tools.contents* + +Introduction ··························································· |intro| +The haskell-tools module ······································· |haskell-tools| +plugin configuration ···································· |haskell-tools.config| +haskell-language-server LSP client tools ··················· |haskell-tools.lsp| +haskell-tools nvim-dap setup ······························· |haskell-tools.dap| +haskell-tools Hoogle search ····························· |haskell-tools.hoogle| +haskell-tools GHCi REPL module ···························· |haskell-tools.repl| +haskell-tools Project module ··························· |haskell-tools.project| +haskell-tools fast-tags module ···························· |haskell-tools.tags| +haskell-tools Logging ······································ |haskell-tools.log| + +============================================================================== +Introduction *intro* + +This plugin automatically configures the `haskell-language-server` builtin LSP client +and integrates with other haskell tools. + +WARNING: +Do not call the `lspconfig.hls` setup or set up the lsp manually, +as doing so may cause conflicts. + +NOTE: This plugin is a filetype plugin. +There is no need to call a `setup` function. + +============================================================================== +The haskell-tools module *haskell-tools* + +Entry-point into this plugin's public API. + +HaskellTools *HaskellTools* + + +============================================================================== +plugin configuration *haskell-tools.config* + +To configure haskell-tools.nvim, set the variable `vim.g.haskell_tools`, +which is a `HTOpts` table, in your neovim configuration. + +Example: +> +---@type HTOpts +vim.g.haskell_tools = { + ---@type ToolsOpts + tools = { + -- ... + }, + ---@type HaskellLspClientOpts + hls = { + on_attach = function(client, bufnr) + -- Set keybindings, etc. here. + end, + -- ... + }, + ---@type HTDapOpts + dap = { + -- ... + }, + } +< + +Note: `vim.g.haskell_tools` can also be a function that returns a 'HTOpts' table. + + +HTOpts *HTOpts* + + Fields: ~ + {tools?} (ToolsOpts) haskell-tools module options. + {hls?} (HaskellLspClientOpts) haskell-language-server client options. + {dap?} (HTDapOpts) debug adapter config for nvim-dap. + + +ToolsOpts *ToolsOpts* + + Fields: ~ + {codeLens?} (CodeLensOpts) LSP codeLens options. + {hoogle?} (HoogleOpts) Hoogle type signature search options. + {hover?} (HoverOpts) LSP hover options. + {definition?} (DefinitionOpts) LSP go-to-definition options. + {repl?} (ReplOpts) GHCi repl options. + {tags?} (FastTagsOpts) fast-tags module options. + {log?} (HTLogOpts) haskell-tools logger options. + + +CodeLensOpts *CodeLensOpts* + + Fields: ~ + {autoRefresh?} (fun():boolean|boolean) (default: `true`) Whether to auto-refresh code-lenses. + + +HoogleOpts *HoogleOpts* + + Fields: ~ + {mode?} (HoogleMode) Use a telescope with a local hoogle installation or a web backend, or use the browser for hoogle signature search? + + +HoogleMode *HoogleMode* + + Type: ~ + "auto"|"telescope-local"|"telescope-web"|"browser" + + +HoverOpts *HoverOpts* + + Fields: ~ + {enable?} (fun():boolean|boolean) (default: `true`) Whether to enable haskell-tools hover. + {border?} (string[][]) The hover window's border. Set to `nil` to disable. + {stylize_markdown?} (boolean) (default: `false`) The builtin LSP client's default behaviour is to stylize markdown. Setting this option to false sets the file type to markdown and enables treesitter syntax highligting for Haskell snippets if nvim-treesitter is installed. + {auto_focus?} (boolean) (default: `false`) Whether to automatically switch to the hover window. + + +DefinitionOpts *DefinitionOpts* + + Fields: ~ + {hoogle_signature_fallback?} (fun():boolean|boolean) (default: `false`) Configure `vim.lsp.definition` to fall back to hoogle search (does not affect `vim.lsp.tagfunc`). + + +ReplOpts *ReplOpts* + + Fields: ~ + {handler?} (fun():ReplHandler|ReplHandler) `'builtin'`: Use the simple builtin repl. `'toggleterm'`: Use akinsho/toggleterm.nvim. + {prefer?} (fun():repl_backend|repl_backend) Prefer cabal or stack when both stack and cabal project files are present? + {builtin?} (BuiltinReplOpts) Configuration for the builtin repl. + {auto_focus?} (boolean) Whether to auto-focus the repl on toggle or send. If unset, the handler decides. + + +ReplHandler *ReplHandler* + + Type: ~ + "builtin"|"toggleterm" + + +repl_backend *repl_backend* + + Type: ~ + "cabal"|"stack" + + +BuiltinReplOpts *BuiltinReplOpts* + + Fields: ~ + {create_repl_window?} (fun(view:ReplView):fun(mk_repl_cmd:mk_repl_cmd_fun)) + + +ReplView *ReplView* + + Fields: ~ + {create_repl_split?} (fun(opts:ReplViewOpts):mk_repl_cmd_fun) Create the REPL in a horizontally split window. + {create_repl_vsplit?} (fun(opts:ReplViewOpts):mk_repl_cmd_fun) Create the REPL in a vertically split window. + {create_repl_tabnew?} (fun(opts:ReplViewOpts):mk_repl_cmd_fun) Create the REPL in a new tab. + {create_repl_cur_win?} (fun(opts:ReplViewOpts):mk_repl_cmd_fun) Create the REPL in the current window. + + +ReplViewOpts *ReplViewOpts* + + Fields: ~ + {delete_buffer_on_exit?} (boolean) Whether to delete the buffer when the Repl quits. + {size?} (fun():number|number) The size of the window or a function that determines it. + + +mk_repl_cmd_fun *mk_repl_cmd_fun* + + Type: ~ + fun():string[]|nil + + +FastTagsOpts *FastTagsOpts* + + Fields: ~ + {enable?} (boolean|fun():boolean) + + +HTLogOpts *HTLogOpts* + + Fields: ~ + {level?} (number|string) The log level. + + See: ~ + |vim.log.levels| + + +HaskellLspClientOpts *HaskellLspClientOpts* + + Fields: ~ + {auto_attach?} (fun():boolean|boolean) Whether to automatically attach the LSP client. Defaults to `true` if the haskell-language-server executable is found. + {debug?} (boolean) Whether to enable haskell-language-server debug logging. + {on_attach?} (fun(client:number,bufnr:number,ht:HaskellTools)) Callback that is invoked when the client attaches to a buffer. + {cmd?} (fun():string[]|string[]) The command to start haskell-language-server with. + {capabilities?} (lsp.ClientCapabilities) LSP client capabilities. + {settings?} (fun(project_root:string|nil):table|table) The haskell-language-server settings or a function that creates them. To view the default settings, run `haskell-language-server generate-default-config`. + {default_settings?} (table) The default haskell-language-server settings that will be used if no settings are specified or detected. + {logfile?} (string) The path to the haskell-language-server log file. + + + To print all options that are available for your haskell-language-server version, run `haskell-language-server-wrapper generate-default-config` +See: https://haskell-language-server.readthedocs.io/en/latest/configuration.html. + +HTDapOpts *HTDapOpts* + + Fields: ~ + {cmd?} (string[]) The command to start the debug adapter server with. + {logFile?} (string) Log file path for detected configurations. + {logLevel?} (HaskellDebugAdapterLogLevel) The log level for detected configurations. + {auto_discover?} (boolean|AddDapConfigOpts) Set to `false` to disable auto-discovery of launch configurations. `true` uses the default configurations options`. + + +HaskellDebugAdapterLogLevel *HaskellDebugAdapterLogLevel* + + Type: ~ + "Debug"|"Info"|"Warning"|"Error" + + +============================================================================== +haskell-language-server LSP client tools *haskell-tools.lsp* + + The following commands are available: + + * `:HlsStart` - Start the LSP client. + * `:HlsStop` - Stop the LSP client. + * `:HlsRestart` - Restart the LSP client. + * `:HlsEvalAll` - Evaluate all code snippets in comments. + +LoadHlsSettingsOpts *LoadHlsSettingsOpts* + + Fields: ~ + {settings_file_pattern} (string|nil) File name or pattern to search for. Defaults to 'hls.json' + + +HlsTools *HlsTools* + + + *HlsTools.load_hls_settings* +HlsTools.load_hls_settings({project_root}, {opts}) + Search the project root for a haskell-language-server settings JSON file and load it to a Lua table. + Falls back to the `hls.default_settings` if no file is found or file cannot be read or decoded. + + Parameters: ~ + {project_root} (string|nil) The project root + {opts} (LoadHlsSettingsOpts|nil) + + Returns: ~ + (table) hls_settings + + See: ~ + |https://haskell-language-server.readthedocs.io/en/latest/configuration.html| + + +HlsTools.start({bufnr}) *HlsTools.start* + Start or attach the LSP client. + Fails silently if the buffer's filetype is not one of the filetypes specified in the config. + + Parameters: ~ + {bufnr} (number|nil) The buffer number (optional), defaults to the current buffer + + Returns: ~ + (number|nil) The LSP client ID + + +HlsTools.stop({bufnr}) *HlsTools.stop* + Stop the LSP client. + + Parameters: ~ + {bufnr} (number|nil) The buffer number (optional), defaults to the current buffer + + Returns: ~ + (table[]) A list of clients that will be stopped + + +HlsTools.restart({bufnr}) *HlsTools.restart* + Restart the LSP client. + Fails silently if the buffer's filetype is not one of the filetypes specified in the config. + + Parameters: ~ + {bufnr} (number|nil) The buffer number (optional), defaults to the current buffer + + Returns: ~ + (number|nil) The LSP client ID after restart + + +HlsTools.buf_eval_all({bufnr}) *HlsTools.buf_eval_all* + Evaluate all code snippets in comments. + + Parameters: ~ + {bufnr} (number|nil) Defaults to the current buffer. + + Returns: ~ + (nil) + + +============================================================================== +haskell-tools nvim-dap setup *haskell-tools.dap* + +HsDapLaunchConfiguration *HsDapLaunchConfiguration* + + +HsDapTools *HsDapTools* + + +AddDapConfigOpts *AddDapConfigOpts* + + + *HsDapTools.discover_configurations* +HsDapTools.discover_configurations({bufnr}, {opts}) + Discover nvim-dap launch configurations for haskell-debug-adapter. + + Parameters: ~ + {bufnr} (number|nil) The buffer number + {opts} (AddDapConfigOpts|nil) + + Returns: ~ + (nil) + + +============================================================================== +haskell-tools Hoogle search *haskell-tools.hoogle* + +HoogleTools *HoogleTools* + + +HoogleTools.hoogle_signature({options}) *HoogleTools.hoogle_signature* + + Parameters: ~ + {options} (table|nil) Includes the `search_term` and options to pass to the telescope picker (if available) + + Returns: ~ + (nil) + + +============================================================================== +haskell-tools GHCi REPL module *haskell-tools.repl* + + The following commands are available: + + * `:HtReplToggle` - Toggle a GHCi repl. + * `:HtReplQuit` - Quit the current repl. + * `:HtReplLoad` - Load a Haskell file into the repl. + * `:HtReplReload` - Reload the current repl. + +HsReplTools *HsReplTools* + + +HsReplTools.buf_mk_repl_cmd() *HsReplTools.buf_mk_repl_cmd* + Create the command to create a repl for the current buffer. + + Returns: ~ + (table|nil) command + + +HsReplTools.operator() *HsReplTools.operator* + + See: ~ + |operatorfunc| + + +HsReplTools.paste({reg}) *HsReplTools.paste* + Paste from register `reg` to the REPL + + Parameters: ~ + {reg} (string|nil) register (defaults to '"') + + +HsReplTools.paste_type({reg}) *HsReplTools.paste_type* + Query the REPL for the type of register `reg` + + Parameters: ~ + {reg} (string|nil) register (defaults to '"') + + +HsReplTools.cword_type() *HsReplTools.cword_type* + Query the REPL for the type of word under the cursor + + +HsReplTools.paste_info({reg}) *HsReplTools.paste_info* + Query the REPL for info on register `reg` + + Parameters: ~ + {reg} (string|nil) register (defaults to '"') + + +HsReplTools.cword_info() *HsReplTools.cword_info* + Query the REPL for the type of word under the cursor + + +HsReplTools.load_file({filepath}) *HsReplTools.load_file* + Load a file into the REPL + + Parameters: ~ + {filepath} (string) The absolute file path + + +HsReplTools.reload() *HsReplTools.reload* + Reload the repl + + +============================================================================== +haskell-tools Project module *haskell-tools.project* + + The following commands are available: + + * `:HsProjectFile` - Open the project file for the current buffer (cabal.project or stack.yaml). + * `:HsPackageYaml` - Open the package.yaml file for the current buffer. + * `:HsPackageCabal` - Open the *.cabal file for the current buffer. + +HsProjectTools *HsProjectTools* + + +HsProjectTools.root_dir({project_file}) *HsProjectTools.root_dir* + Get the project's root directory + + Parameters: ~ + {project_file} (string) The path to a project file + + Returns: ~ + (string|nil) + + +HsProjectTools.open_package_yaml() *HsProjectTools.open_package_yaml* + Open the package.yaml of the package containing the current buffer. + + Returns: ~ + (nil) + + +HsProjectTools.open_package_cabal() *HsProjectTools.open_package_cabal* + Open the *.cabal file of the package containing the current buffer. + + Returns: ~ + (nil) + + +HsProjectTools.open_project_file() *HsProjectTools.open_project_file* + Open the current buffer's project file (cabal.project or stack.yaml). + + Returns: ~ + (nil) + + +============================================================================== +haskell-tools fast-tags module *haskell-tools.tags* + +GenerateProjectTagsOpts *GenerateProjectTagsOpts* + + Fields: ~ + {refresh} (boolean) Whether to refresh the tags if they have already been generated + + +FastTagsTools *FastTagsTools* + for the project (default: true) + + + *FastTagsTools.generate_project_tags* +FastTagsTools.generate_project_tags({path}, {opts}) + Generates tags for the current project + + Parameters: ~ + {path} (string|nil) File path + {opts} (GenerateProjectTagsOpts|nil) Options + + + *FastTagsTools.generate_package_tags* +FastTagsTools.generate_package_tags({path}) + Generate tags for the package containing `path` + + Parameters: ~ + {path} (string|nil) File path + + +============================================================================== +haskell-tools Logging *haskell-tools.log* + + The following commands are available: + + * `:HtLog` - Open the haskell-tools.nvim log file. + * `:HlsLog` - Open the haskell-language-server log file. + * `:HtSetLogLevel` - Set the haskell-tools.nvim and LSP client log level. + +HaskellToolsLog *HaskellToolsLog* + + +HaskellToolsLog.get_hls_logfile() *HaskellToolsLog.get_hls_logfile* + Get the haskell-language-server log file + + Returns: ~ + (string) filepath + + +HaskellToolsLog.get_logfile() *HaskellToolsLog.get_logfile* + Get the haskell-tools.nvim log file path. + + Returns: ~ + (string) filepath + + +HaskellToolsLog.nvim_open_hls_logfile() *HaskellToolsLog.nvim_open_hls_logfile* + Open the haskell-language-server log file + + Returns: ~ + (nil) + + +HaskellToolsLog.nvim_open_logfile() *HaskellToolsLog.nvim_open_logfile* + Open the haskell-tools.nvim log file. + + Returns: ~ + (nil) + + +HaskellToolsLog.set_level() *HaskellToolsLog.set_level* + + Returns: ~ + (nil) + + See: ~ + |vim.log.levels| + + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/cabal.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/cabal.lua new file mode 100644 index 00000000..262f37c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/cabal.lua @@ -0,0 +1 @@ +require('haskell-tools.internal').ftplugin() diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/cabalproject.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/cabalproject.lua new file mode 100644 index 00000000..262f37c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/cabalproject.lua @@ -0,0 +1 @@ +require('haskell-tools.internal').ftplugin() diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/haskell.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/haskell.lua new file mode 100644 index 00000000..262f37c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/haskell.lua @@ -0,0 +1 @@ +require('haskell-tools.internal').ftplugin() diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/lhaskell.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/lhaskell.lua new file mode 100644 index 00000000..262f37c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/ftplugin/lhaskell.lua @@ -0,0 +1 @@ +require('haskell-tools.internal').ftplugin() diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/haskell-tools.nvim-3.1.8-1.rockspec b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/haskell-tools.nvim-3.1.8-1.rockspec new file mode 100644 index 00000000..f3383c0a --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/haskell-tools.nvim-3.1.8-1.rockspec @@ -0,0 +1,34 @@ +local git_ref = '3.1.8' +local modrev = '3.1.8' +local specrev = '1' + +local repo_url = 'https://github.com/mrcjkb/haskell-tools.nvim' + +rockspec_format = '3.0' +package = 'haskell-tools.nvim' +version = modrev ..'-'.. specrev + +description = { + summary = 'Supercharge your Haskell experience in neovim!', + detailed = [[ +This plugin automatically configures the haskell-language-server builtin LSP client +and integrates with other Haskell tools. See the README's #features section +for more info.]], + labels = { 'dap', 'debug-adapter-protocol', 'fast-tags', 'haskell', 'hoogle', 'language-server', 'language-server-protocol', 'lsp', 'lsp-client', 'lua', 'neovim', 'neovim-plugin', 'nvim', 'plugin', 'repl', 'tagfunc', 'telescope', 'vim' } , + homepage = 'https://github.com/mrcjkb/haskell-tools.nvim', + license = 'GPL-2.0' +} + +dependencies = { 'lua >= 5.1' } + +test_dependencies = { } + +source = { + url = repo_url .. '/archive/' .. git_ref .. '.zip', + dir = 'haskell-tools.nvim-' .. '3.1.8', +} + +build = { + type = 'builtin', + copy_directories = { 'doc', 'ftplugin' } , +} diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/rock_manifest b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/rock_manifest new file mode 100644 index 00000000..698d244b --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/haskell-tools.nvim/3.1.8-1/rock_manifest @@ -0,0 +1,73 @@ +rock_manifest = { + doc = { + ["haskell-tools.txt"] = "dea283444425fa165997d28c1ac610c8" + }, + ftplugin = { + ["cabal.lua"] = "4f72e96d3601fed58fd4a24e9633172d", + ["cabalproject.lua"] = "4f72e96d3601fed58fd4a24e9633172d", + ["haskell.lua"] = "4f72e96d3601fed58fd4a24e9633172d", + ["lhaskell.lua"] = "4f72e96d3601fed58fd4a24e9633172d" + }, + ["haskell-tools.nvim-3.1.8-1.rockspec"] = "653d0f8f3d848afa2ac2e8d64e470886", + lua = { + ["haskell-tools"] = { + ["compat.lua"] = "1457323a47d791e5ff2393871a112357", + config = { + ["check.lua"] = "4c6c96c3b51efe53b54c8bc4f14d93d6", + ["init.lua"] = "e1f6bc006747eb79f131b527acb83b1f", + ["internal.lua"] = "373f754423a5786afbfcdea048d74bc7" + }, + dap = { + ["init.lua"] = "007e49a6c0a33047d9dc3cc0c76474e7", + ["internal.lua"] = "f30905af3fdff69d13c20682767c44a2" + }, + ["deps.lua"] = "ff7e0f06f003b497c958805a391c1eaf", + ["health.lua"] = "941c948e1667a8a532f2b93f59d0347b", + hoogle = { + ["helpers.lua"] = "e5428112e897aa06b0a76d186679b110", + ["init.lua"] = "5385afa48b78262001e50154b6f02ec3", + ["local.lua"] = "eca412a13b7a33cba0b2935c4024d864", + ["web.lua"] = "4ebe05b9a484b20e88ff51af76f6a88e" + }, + ["init.lua"] = "81946a46a32bdc3066333f6cac348712", + ["internal.lua"] = "f149d8830ae8f0516393ffb31b573765", + log = { + ["init.lua"] = "c8b080a5a9701cb8ae3c526acd868964", + ["internal.lua"] = "ed9e8200543b60724d27d5a991191a3e" + }, + lsp = { + ["definition.lua"] = "04348f8215b227a5da688b92e1cfe23b", + ["eval.lua"] = "69614508eab450ad811c9cbdd46130a9", + ["helpers.lua"] = "010ab78c87db24c8142c29ac35c218e8", + ["hover.lua"] = "9e03896e1877b06c98f11ce1b643343c", + ["init.lua"] = "44c4d7be0311e2d746e00308710345e9" + }, + ["os.lua"] = "110cd7ec57e6587360092f8f78025783", + ["parser.lua"] = "402dc6ebad5cffbe6700e774ad64724a", + project = { + ["cabal.lua"] = "ef4d57f352f6f15cb21bc3dc72e37872", + ["helpers.lua"] = "c167cfdaca3d44feb95eb921470dbaf0", + ["init.lua"] = "eebdb000f25db6280299ccc89ee64fc6", + ["stack.lua"] = "71c00101f8004f9c905a9ddb33dc6889" + }, + repl = { + ["builtin.lua"] = "c1b6210d95f533ec06bb3857f8d1dc7e", + ["init.lua"] = "3529ee48e2a8e39f962fd6d351ff6ee3", + ["toggleterm.lua"] = "f62892e49e2f5b25394f0c254a9cebb6" + }, + ["strings.lua"] = "6429d6757a6c08856f425b72d7d8362c", + ["tags.lua"] = "b82dd1a468edfc13323b2f290f73dc24", + types = { + ["internal.lua"] = "a078237ae853c7f19c396a7d5001fa28" + } + }, + telescope = { + _extensions = { + ht = { + ["extension.lua"] = "040c10b48645d3230bba5952281ea858" + }, + ["ht.lua"] = "ebd1a0ec34353b6b42b5e6012ff69dd9" + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/manifest b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/manifest new file mode 100644 index 00000000..846cf9c4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1-unstable-2024-05-20-rocks/manifest @@ -0,0 +1,173 @@ +commands = {} +dependencies = { + ["haskell-tools.nvim"] = { + ["3.1.8-1"] = { + { + constraints = { + { + op = ">=", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + } + } + } +} +modules = { + ["haskell-tools.compat"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.config.check"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.config.init"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.config.internal"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.dap.init"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.dap.internal"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.deps"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.health"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.hoogle.helpers"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.hoogle.init"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.hoogle.local"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.hoogle.web"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.init"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.internal"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.log.init"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.log.internal"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.lsp.definition"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.lsp.eval"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.lsp.helpers"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.lsp.hover"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.lsp.init"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.os"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.parser"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.project.cabal"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.project.helpers"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.project.init"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.project.stack"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.repl.builtin"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.repl.init"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.repl.toggleterm"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.strings"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.tags"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["haskell-tools.types.internal"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["telescope._extensions.ht"] = { + "haskell-tools.nvim/3.1.8-1" + }, + ["telescope._extensions.ht.extension"] = { + "haskell-tools.nvim/3.1.8-1" + } +} +repository = { + ["haskell-tools.nvim"] = { + ["3.1.8-1"] = { + { + arch = "installed", + commands = {}, + dependencies = {}, + modules = { + ["haskell-tools.compat"] = "haskell-tools/compat.lua", + ["haskell-tools.config.check"] = "haskell-tools/config/check.lua", + ["haskell-tools.config.init"] = "haskell-tools/config/init.lua", + ["haskell-tools.config.internal"] = "haskell-tools/config/internal.lua", + ["haskell-tools.dap.init"] = "haskell-tools/dap/init.lua", + ["haskell-tools.dap.internal"] = "haskell-tools/dap/internal.lua", + ["haskell-tools.deps"] = "haskell-tools/deps.lua", + ["haskell-tools.health"] = "haskell-tools/health.lua", + ["haskell-tools.hoogle.helpers"] = "haskell-tools/hoogle/helpers.lua", + ["haskell-tools.hoogle.init"] = "haskell-tools/hoogle/init.lua", + ["haskell-tools.hoogle.local"] = "haskell-tools/hoogle/local.lua", + ["haskell-tools.hoogle.web"] = "haskell-tools/hoogle/web.lua", + ["haskell-tools.init"] = "haskell-tools/init.lua", + ["haskell-tools.internal"] = "haskell-tools/internal.lua", + ["haskell-tools.log.init"] = "haskell-tools/log/init.lua", + ["haskell-tools.log.internal"] = "haskell-tools/log/internal.lua", + ["haskell-tools.lsp.definition"] = "haskell-tools/lsp/definition.lua", + ["haskell-tools.lsp.eval"] = "haskell-tools/lsp/eval.lua", + ["haskell-tools.lsp.helpers"] = "haskell-tools/lsp/helpers.lua", + ["haskell-tools.lsp.hover"] = "haskell-tools/lsp/hover.lua", + ["haskell-tools.lsp.init"] = "haskell-tools/lsp/init.lua", + ["haskell-tools.os"] = "haskell-tools/os.lua", + ["haskell-tools.parser"] = "haskell-tools/parser.lua", + ["haskell-tools.project.cabal"] = "haskell-tools/project/cabal.lua", + ["haskell-tools.project.helpers"] = "haskell-tools/project/helpers.lua", + ["haskell-tools.project.init"] = "haskell-tools/project/init.lua", + ["haskell-tools.project.stack"] = "haskell-tools/project/stack.lua", + ["haskell-tools.repl.builtin"] = "haskell-tools/repl/builtin.lua", + ["haskell-tools.repl.init"] = "haskell-tools/repl/init.lua", + ["haskell-tools.repl.toggleterm"] = "haskell-tools/repl/toggleterm.lua", + ["haskell-tools.strings"] = "haskell-tools/strings.lua", + ["haskell-tools.tags"] = "haskell-tools/tags.lua", + ["haskell-tools.types.internal"] = "haskell-tools/types/internal.lua", + ["telescope._extensions.ht"] = "telescope/_extensions/ht.lua", + ["telescope._extensions.ht.extension"] = "telescope/_extensions/ht/extension.lua" + } + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1.rockspec b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1.rockspec new file mode 100644 index 00000000..f3383c0a --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/haskell-tools.nvim-3.1.8-1.rockspec @@ -0,0 +1,34 @@ +local git_ref = '3.1.8' +local modrev = '3.1.8' +local specrev = '1' + +local repo_url = 'https://github.com/mrcjkb/haskell-tools.nvim' + +rockspec_format = '3.0' +package = 'haskell-tools.nvim' +version = modrev ..'-'.. specrev + +description = { + summary = 'Supercharge your Haskell experience in neovim!', + detailed = [[ +This plugin automatically configures the haskell-language-server builtin LSP client +and integrates with other Haskell tools. See the README's #features section +for more info.]], + labels = { 'dap', 'debug-adapter-protocol', 'fast-tags', 'haskell', 'hoogle', 'language-server', 'language-server-protocol', 'lsp', 'lsp-client', 'lua', 'neovim', 'neovim-plugin', 'nvim', 'plugin', 'repl', 'tagfunc', 'telescope', 'vim' } , + homepage = 'https://github.com/mrcjkb/haskell-tools.nvim', + license = 'GPL-2.0' +} + +dependencies = { 'lua >= 5.1' } + +test_dependencies = { } + +source = { + url = repo_url .. '/archive/' .. git_ref .. '.zip', + dir = 'haskell-tools.nvim-' .. '3.1.8', +} + +build = { + type = 'builtin', + copy_directories = { 'doc', 'ftplugin' } , +} diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/compat.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/compat.lua new file mode 100644 index 00000000..9fac7e67 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/compat.lua @@ -0,0 +1,49 @@ +---@diagnostic disable: deprecated, duplicate-doc-field, duplicate-doc-alias +---@mod haskell-tools.compat Functions for backward compatibility with older Neovim versions +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +---@brief ]] + +local compat = {} + +compat.joinpath = vim.fs.joinpath or function(...) + return (table.concat({ ... }, '/'):gsub('//+', '/')) +end + +compat.get_clients = vim.lsp.get_clients or vim.lsp.get_active_clients + +compat.uv = vim.uv or vim.loop + +--- @class vim.SystemCompleted +--- @field code integer +--- @field signal integer +--- @field stdout? string +--- @field stderr? string + +--- @alias lsp.Client vim.lsp.Client + +compat.system = vim.system + -- wrapper around vim.fn.system to give it a similar API to vim.system + or function(cmd, _, on_exit) + local output = vim.fn.system(cmd) + local ok = vim.v.shell_error + ---@type vim.SystemCompleted + local systemObj = { + signal = 0, + stdout = ok and (output or '') or nil, + stderr = not ok and (output or '') or nil, + code = vim.v.shell_error, + } + on_exit(systemObj) + return systemObj + end + +---@type fun(tbl:table):table +compat.tbl_flatten = vim.iter and function(tbl) + return vim.iter(tbl):flatten(math.huge):totable() +end or vim.tbl_flatten + +return compat diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/config/check.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/config/check.lua new file mode 100644 index 00000000..9918bbe4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/config/check.lua @@ -0,0 +1,202 @@ +---@mod haskell-tools.config.check haskell-tools configuration check + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +---@brief ]] + +---@class HtConfigCheck +local HtConfigCheck = {} + +---@param path string +---@param msg string|nil +---@return string +local function mk_error_msg(path, msg) + return msg and path .. '.' .. msg or path +end + +---@param path string The config path +---@param tbl table The table to validate +---@see vim.validate +---@return boolean is_valid +---@return string|nil error_message +local function validate(path, tbl) + local prefix = 'Invalid config: ' + local ok, err = pcall(vim.validate, tbl) + return ok or false, prefix .. mk_error_msg(path, err) +end + +---Validates the config. +---@param cfg HTConfig +---@return boolean is_valid +---@return string|nil error_message +function HtConfigCheck.validate(cfg) + local ok, err + local hls = cfg.hls + ok, err = validate('hls', { + auto_attach = { hls.auto_attach, { 'boolean', 'function' } }, + capabilities = { hls.capabilities, 'table' }, + cmd = { hls.cmd, { 'table', 'function' } }, + debug = { hls.debug, 'boolean' }, + default_settings = { hls.default_settings, 'table' }, + on_attach = { hls.on_attach, 'function' }, + settings = { hls.settings, { 'function', 'table' } }, + }) + if not ok then + return false, err + end + local tools = cfg.tools + local codeLens = tools.codeLens + ok, err = validate('tools.codeLens', { + autoRefresh = { codeLens.autoRefresh, { 'boolean', 'function' } }, + }) + if not ok then + return false, err + end + local definition = tools.definition + ok, err = validate('tools.definition', { + hoogle_signature_fallback = { definition.hoogle_signature_fallback, { 'boolean', 'function' } }, + }) + if not ok then + return false, err + end + local hoogle = tools.hoogle + local valid_modes = { 'auto', 'telescope-local', 'telescope-web', 'browser' } + ok, err = validate('tools.hoogle', { + mode = { + hoogle.mode, + function(mode) + return vim.tbl_contains(valid_modes, mode) + end, + 'one of ' .. vim.inspect(valid_modes), + }, + }) + if not ok then + return false, err + end + local hover = tools.hover + ok, err = validate('tools.hover', { + auto_focus = { hover.auto_focus, 'boolean', true }, + border = { hover.border, 'table', true }, + enable = { hover.enable, { 'boolean', 'function' } }, + stylize_markdown = { hover.stylize_markdown, 'boolean' }, + }) + if not ok then + return false, err + end + local log = tools.log + ok, err = validate('tools.log', { + log = { log.level, { 'number', 'string' } }, + }) + if not ok then + return false, err + end + local repl = tools.repl + local valid_handlers = { 'builtin', 'toggleterm' } + local valid_backends = { 'cabal', 'stack' } + ok, err = validate('tools.repl', { + auto_focus = { repl.auto_focus, 'boolean', true }, + builtin = { repl.builtin, 'table' }, + handler = { + repl.handler, + function(handler) + return type(handler) == 'function' or vim.tbl_contains(valid_handlers, handler) + end, + 'one of ' .. vim.inspect(valid_handlers), + }, + prefer = { + repl.prefer, + function(backend) + return type(backend) == 'function' or vim.tbl_contains(valid_backends, backend) + end, + 'one of ' .. vim.inspect(valid_backends), + }, + }) + if not ok then + return false, err + end + local builtin = repl.builtin + ok, err = validate('tools.repl.builtin', { + create_repl_window = { builtin.create_repl_window, 'function' }, + }) + if not ok then + return false, err + end + local tags = tools.tags + ok, err = validate('tools.tags', { + enable = { tags.enable, { 'boolean', 'function' } }, + package_events = { tags.package_events, 'table' }, + }) + if not ok then + return false, err + end + local dap = cfg.dap + local valid_dap_log_levels = { 'Debug', 'Info', 'Warning', 'Error' } + ok, err = validate('dap', { + auto_discover = { dap.auto_discover, { 'boolean', 'table' } }, + cmd = { dap.cmd, { 'function', 'table' } }, + logFile = { dap.logFile, 'string' }, + logLevel = { + dap.logLevel, + function(level) + return type(level) == 'string' and vim.tbl_contains(valid_dap_log_levels, level) + end, + 'one of ' .. vim.inspect(valid_backends), + }, + }) + if not ok then + return false, err + end + local auto_discover = dap.auto_discover + if type(auto_discover) == 'table' then + ---@cast auto_discover AddDapConfigOpts + ok, err = validate('dap.auto_discover', { + autodetect = { auto_discover.autodetect, 'boolean' }, + settings_file_pattern = { auto_discover.settings_file_pattern, 'string' }, + }) + if not ok then + return false, err + end + end + return true +end + +---Recursively check a table for unrecognized keys, +---using a default table as a reference +---@param tbl table +---@param default_tbl table +---@param ignored_keys string[] +---@return string[] +function HtConfigCheck.get_unrecognized_keys(tbl, default_tbl, ignored_keys) + local unrecognized_keys = {} + for k, _ in pairs(tbl) do + unrecognized_keys[k] = true + end + for k, _ in pairs(default_tbl) do + unrecognized_keys[k] = false + end + local ret = {} + for k, _ in pairs(unrecognized_keys) do + if unrecognized_keys[k] then + ret[k] = k + end + if type(default_tbl[k]) == 'table' and tbl[k] then + for _, subk in pairs(HtConfigCheck.get_unrecognized_keys(tbl[k], default_tbl[k], {})) do + local key = k .. '.' .. subk + ret[key] = key + end + end + end + for k, _ in pairs(ret) do + for _, ignore in pairs(ignored_keys) do + if vim.startswith(k, ignore) then + ret[k] = nil + end + end + end + return vim.tbl_keys(ret) +end + +return HtConfigCheck diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/config/init.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/config/init.lua new file mode 100644 index 00000000..8e1df78b --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/config/init.lua @@ -0,0 +1,123 @@ +---@mod haskell-tools.config plugin configuration +--- +---@brief [[ +---To configure haskell-tools.nvim, set the variable `vim.g.haskell_tools`, +---which is a `HTOpts` table, in your neovim configuration. +--- +---Example: +---> +------@type HTOpts +---vim.g.haskell_tools = { +--- ---@type ToolsOpts +--- tools = { +--- -- ... +--- }, +--- ---@type HaskellLspClientOpts +--- hls = { +--- on_attach = function(client, bufnr) +--- -- Set keybindings, etc. here. +--- end, +--- -- ... +--- }, +--- ---@type HTDapOpts +--- dap = { +--- -- ... +--- }, +--- } +---< +--- +---Note: `vim.g.haskell_tools` can also be a function that returns a 'HTOpts' table. +--- +---@brief ]] + +local config = {} + +---@type (fun():HTOpts) | HTOpts | nil +vim.g.haskell_tools = vim.g.haskell_tools + +---@class HTOpts +---@field tools? ToolsOpts haskell-tools module options. +---@field hls? HaskellLspClientOpts haskell-language-server client options. +---@field dap? HTDapOpts debug adapter config for nvim-dap. +---@class ToolsOpts +---@field codeLens? CodeLensOpts LSP codeLens options. +---@field hoogle? HoogleOpts Hoogle type signature search options. +---@field hover? HoverOpts LSP hover options. +---@field definition? DefinitionOpts LSP go-to-definition options. +---@field repl? ReplOpts GHCi repl options. +---@field tags? FastTagsOpts fast-tags module options. +---@field log? HTLogOpts haskell-tools logger options. + +---@class CodeLensOpts +---@field autoRefresh? (fun():boolean) | boolean (default: `true`) Whether to auto-refresh code-lenses. + +---@class HoogleOpts +---@field mode? HoogleMode Use a telescope with a local hoogle installation or a web backend, or use the browser for hoogle signature search? + +---@alias HoogleMode 'auto' | 'telescope-local' | 'telescope-web' | 'browser' + +---@class HoverOpts +---@field enable? (fun():boolean) | boolean (default: `true`) Whether to enable haskell-tools hover. +---@field border? string[][] The hover window's border. Set to `nil` to disable. +---@field stylize_markdown? boolean (default: `false`) The builtin LSP client's default behaviour is to stylize markdown. Setting this option to false sets the file type to markdown and enables treesitter syntax highligting for Haskell snippets if nvim-treesitter is installed. +---@field auto_focus? boolean (default: `false`) Whether to automatically switch to the hover window. + +---@class DefinitionOpts +---@field hoogle_signature_fallback? (fun():boolean) | boolean (default: `false`) Configure `vim.lsp.definition` to fall back to hoogle search (does not affect `vim.lsp.tagfunc`). + +---@class ReplOpts +---@field handler? (fun():ReplHandler) | ReplHandler `'builtin'`: Use the simple builtin repl. `'toggleterm'`: Use akinsho/toggleterm.nvim. +---@field prefer? (fun():repl_backend) | repl_backend Prefer cabal or stack when both stack and cabal project files are present? +---@field builtin? BuiltinReplOpts Configuration for the builtin repl. +---@field auto_focus? boolean Whether to auto-focus the repl on toggle or send. If unset, the handler decides. + +---@alias ReplHandler 'builtin' | 'toggleterm' +---@alias repl_backend 'cabal' | 'stack' + +---@class BuiltinReplOpts +---@field create_repl_window? (fun(view:ReplView):fun(mk_repl_cmd:mk_repl_cmd_fun)) How to create the repl window. Should return a function that calls one of the `ReplView`'s functions. + +---@class ReplView +---@field create_repl_split? fun(opts:ReplViewOpts):mk_repl_cmd_fun Create the REPL in a horizontally split window. +---@field create_repl_vsplit? fun(opts:ReplViewOpts):mk_repl_cmd_fun Create the REPL in a vertically split window. +---@field create_repl_tabnew? fun(opts:ReplViewOpts):mk_repl_cmd_fun Create the REPL in a new tab. +---@field create_repl_cur_win? fun(opts:ReplViewOpts):mk_repl_cmd_fun Create the REPL in the current window. + +---@class ReplViewOpts +---@field delete_buffer_on_exit? boolean Whether to delete the buffer when the Repl quits. +---@field size? (fun():number) | number The size of the window or a function that determines it. + +---@alias mk_repl_cmd_fun fun():(string[]|nil) + +---@class FastTagsOpts +---@field enable? boolean | (fun():boolean) Enabled by default if the `fast-tags` executable is found. +---@field package_events? string[] `autocmd` Events to trigger package tag generation. + +---@class HTLogOpts +---@field level? number | string The log level. +---@see vim.log.levels + +---@class HaskellLspClientOpts +---@field auto_attach? (fun():boolean) | boolean Whether to automatically attach the LSP client. Defaults to `true` if the haskell-language-server executable is found. +---@field debug? boolean Whether to enable haskell-language-server debug logging. +---@field on_attach? fun(client:number,bufnr:number,ht:HaskellTools) Callback that is invoked when the client attaches to a buffer. +---@field cmd? (fun():string[]) | string[] The command to start haskell-language-server with. +---@field capabilities? lsp.ClientCapabilities LSP client capabilities. +---@field settings? (fun(project_root:string|nil):table) | table The haskell-language-server settings or a function that creates them. To view the default settings, run `haskell-language-server generate-default-config`. +---@field default_settings? table The default haskell-language-server settings that will be used if no settings are specified or detected. +---@field logfile? string The path to the haskell-language-server log file. + +---@brief [[ +--- To print all options that are available for your haskell-language-server version, run `haskell-language-server-wrapper generate-default-config` +---See: https://haskell-language-server.readthedocs.io/en/latest/configuration.html. +---@brief ]] + +---@class HTDapOpts +---@field cmd? string[] The command to start the debug adapter server with. +---@field logFile? string Log file path for detected configurations. +---@field logLevel? HaskellDebugAdapterLogLevel The log level for detected configurations. +---@field auto_discover? boolean | AddDapConfigOpts Set to `false` to disable auto-discovery of launch configurations. `true` uses the default configurations options`. + +---@alias HaskellDebugAdapterLogLevel 'Debug' | 'Info' | 'Warning' | 'Error' + +return config diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/config/internal.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/config/internal.lua new file mode 100644 index 00000000..f04a5013 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/config/internal.lua @@ -0,0 +1,295 @@ +---@mod haskell-tools.config.internal + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- The internal configuration. +--- Merges the default config with `vim.g.haskell_tools`. +---@brief ]] + +local deps = require('haskell-tools.deps') +local compat = require('haskell-tools.compat') + +---@type HTConfig +local HTConfig = {} + +local ht_capabilities = vim.lsp.protocol.make_client_capabilities() +local cmp_capabilities = deps.if_available('cmp_nvim_lsp', function(cmp_nvim_lsp) + return cmp_nvim_lsp.default_capabilities() +end, {}) +local selection_range_capabilities = deps.if_available('lsp-selection-range', function(lsp_selection_range) + return lsp_selection_range.update_capabilities {} +end, {}) +local folding_range_capabilities = deps.if_available('ufo', function(_) + return { + textDocument = { + foldingRange = { + dynamicRegistration = false, + lineFoldingOnly = true, + }, + }, + } +end, {}) +local capabilities = vim.tbl_deep_extend( + 'keep', + ht_capabilities, + cmp_capabilities, + selection_range_capabilities, + folding_range_capabilities +) + +---@class HTConfig haskell-tools.nvim plugin configuration. +local HTDefaultConfig = { + + ---@class ToolsConfig haskell-tools module config. + tools = { + ---@class CodeLensConfig LSP codeLens options. + codeLens = { + ---@type boolean | (fun():boolean) (default: `true`) Whether to auto-refresh code-lenses. + autoRefresh = true, + }, + ---@class HoogleConfig hoogle type signature search config. + hoogle = { + ---@type HoogleMode Use a telescope with a local hoogle installation or a web backend, or use the browser for hoogle signature search? + mode = 'auto', + }, + ---@class HoverConfig Enhanced LSP hover options. + hover = { + ---@type boolean | (fun():boolean) (default: `true`) Whether to enable haskell-tools hover. + enable = true, + ---@type string[][] | nil The hover window's border. Set to `nil` to disable. + border = { + { '╭', 'FloatBorder' }, + { '─', 'FloatBorder' }, + { '╮', 'FloatBorder' }, + { '│', 'FloatBorder' }, + { '╯', 'FloatBorder' }, + { '─', 'FloatBorder' }, + { '╰', 'FloatBorder' }, + { '│', 'FloatBorder' }, + }, + ---@type boolean (default: `false`) The builtin LSP client's default behaviour is to stylize markdown. Setting this option to false sets the file type to markdown and enables treesitter syntax highligting for Haskell snippets if nvim-treesitter is installed. + stylize_markdown = false, + ---@type boolean (default: `false`) Whether to automatically switch to the hover window. + auto_focus = false, + }, + ---@class DefinitionConfig Enhanced LSP go-to-definition options. + definition = { + ---@type boolean | (fun():boolean) (default: `false`) Configure `vim.lsp.definition` to fall back to hoogle search (does not affect `vim.lsp.tagfunc`). + hoogle_signature_fallback = false, + }, + ---@class ReplConfig GHCi repl options. + repl = { + ---@type ReplHandler | (fun():ReplHandler) `'builtin'`: Use the simple builtin repl. `'toggleterm'`: Use akinsho/toggleterm.nvim. + handler = 'builtin', + ---@type repl_backend | (fun():repl_backend) Prefer cabal or stack when both stack and cabal project files are present? + prefer = function() + return vim.fn.executable('stack') == 1 and 'stack' or 'cabal' + end, + ---@class BuiltinReplConfig Configuration for the builtin repl + builtin = { + ---@type fun(view:ReplView):fun(mk_repl_cmd:mk_repl_cmd_fun) How to create the repl window. Should return a function that calls one of the `ReplView`'s functions. + create_repl_window = function(view) + return view.create_repl_split { size = vim.o.lines / 3 } + end, + }, + ---@type boolean | nil Whether to auto-focus the repl on toggle or send. If unset, the handler decides. + auto_focus = nil, + }, + ---@class FastTagsConfig fast-tags module options. + tags = { + ---@type boolean | (fun():boolean) Enabled by default if the `fast-tags` executable is found. + enable = function() + return vim.fn.executable('fast-tags') == 1 + end, + ---@type string[] `autocmd` Events to trigger package tag generation. + package_events = { 'BufWritePost' }, + }, + ---@class HTLogConfig haskell-tools logger options. + log = { + ---@diagnostic disable-next-line: param-type-mismatch + logfile = compat.joinpath(vim.fn.stdpath('log'), 'haskell-tools.log'), + ---@type number | string The log level. + ---@see vim.log.levels + level = vim.log.levels.WARN, + }, + }, + ---@class HaskellLspClientConfig haskell-language-server client options. + hls = { + ---@type boolean | (fun():boolean) Whether to automatically attach the LSP client. Defaults to `true` if the haskell-language-server executable is found. + auto_attach = function() + local Types = require('haskell-tools.types.internal') + local cmd = Types.evaluate(HTConfig.hls.cmd) + ---@cast cmd string[] + local hls_bin = cmd[1] + return vim.fn.executable(hls_bin) == 1 + end, + ---@type boolean Whether to enable haskell-language-server debug logging. + debug = false, + ---@type (fun(client:number,bufnr:number,ht:HaskellTools)) Callback that is invoked when the client attaches to a buffer. + ---@see vim.lsp.start + on_attach = function(_, _, _) end, + ---@type string[] | (fun():string[]) The command to start haskell-language-server with. + ---@see vim.lsp.start + cmd = function() + -- Some distributions don't prorvide a hls wrapper. + -- So we check if it exists and fall back to hls if it doesn't + local hls_bin = 'haskell-language-server' + local hls_wrapper_bin = hls_bin .. '-wrapper' + local bin = vim.fn.executable(hls_wrapper_bin) == 1 and hls_wrapper_bin or hls_bin + local cmd = { bin, '--lsp', '--logfile', HTConfig.hls.logfile } + if HTConfig.hls.debug then + table.insert(cmd, '--debug') + end + return cmd + end, + ---@type lsp.ClientCapabilities | nil LSP client capabilities. + ---@see vim.lsp.protocol.make_client_capabilities + ---@see vim.lsp.start + capabilities = capabilities, + ---@type table | (fun(project_root:string|nil):table) | nil The haskell-language-server settings or a function that creates them. To view the default settings, run `haskell-language-server generate-default-config`. + settings = function(project_root) + local ht = require('haskell-tools') + return ht.lsp.load_hls_settings(project_root) + end, + ---@type table The default haskell-language-server settings that will be used if no settings are specified or detected. + default_settings = { + haskell = { + -- The formatting providers. + formattingProvider = 'fourmolu', + -- Maximum number of completions sent to the LSP client. + maxCompletions = 40, + -- Whether to typecheck the entire project on initial load. + -- Could drive to bad performance in large projects, if set to true. + checkProject = true, + -- When to typecheck reverse dependencies of a file; + -- one of NeverCheck, CheckOnSave (means dependent/parent modules will only be checked when you save), + -- or AlwaysCheck (means re-typechecking them on every change). + checkParents = 'CheckOnSave', + plugin = { + alternateNumberFormat = { globalOn = true }, + callHierarchy = { globalOn = true }, + changeTypeSignature = { globalOn = true }, + class = { + codeActionsOn = true, + codeLensOn = true, + }, + eval = { + globalOn = true, + config = { + diff = true, + exception = true, + }, + }, + excplicitFixity = { globalOn = true }, + gadt = { globalOn = true }, + ['ghcide-code-actions-bindings'] = { globalOn = true }, + ['ghcide-code-actions-fill-holes'] = { globalOn = true }, + ['ghcide-code-actions-imports-exports'] = { globalOn = true }, + ['ghcide-code-actions-type-signatures'] = { globalOn = true }, + ['ghcide-completions'] = { + globalOn = true, + config = { + autoExtendOn = true, + snippetsOn = true, + }, + }, + ['ghcide-hover-and-symbols'] = { + hoverOn = true, + symbolsOn = true, + }, + ['ghcide-type-lenses'] = { + globalOn = true, + config = { + mode = 'always', + }, + }, + haddockComments = { globalOn = true }, + hlint = { + codeActionsOn = true, + diagnosticsOn = true, + }, + importLens = { + globalOn = true, + codeActionsOn = true, + codeLensOn = true, + }, + moduleName = { globalOn = true }, + pragmas = { + codeActionsOn = true, + completionOn = true, + }, + qualifyImportedNames = { globalOn = true }, + refineImports = { + codeActionsOn = true, + codeLensOn = true, + }, + rename = { + globalOn = true, + config = { crossModule = true }, + }, + retrie = { globalOn = true }, + splice = { globalOn = true }, + tactics = { + codeActionsOn = true, + codeLensOn = true, + config = { + auto_gas = 4, + hole_severity = nil, + max_use_ctor_actions = 5, + proofstate_styling = true, + timeout_duration = 2, + }, + hoverOn = true, + }, + }, + }, + }, + ---@type string The path to the haskell-language-server log file. + logfile = vim.fn.tempname() .. '-haskell-language-server.log', + }, + ---@class HTDapConfig debug adapter config for nvim-dap. + dap = { + ---@type string[] | (fun():string[]) The command to start the debug adapter server with. + cmd = { 'haskell-debug-adapter' }, + ---@type string Log file path for detected configurations. + logFile = vim.fn.stdpath('data') .. '/haskell-dap.log', + ---@type HaskellDebugAdapterLogLevel The log level for detected configurations. + logLevel = 'Warning', + ---@type boolean | AddDapConfigOpts Set to `false` to disable auto-discovery of launch configurations. `true` uses the default configurations options`. + auto_discover = true, + }, + debug_info = { + ---@type boolean + was_g_haskell_tools_sourced = vim.g.haskell_tools ~= nil, + }, +} + +local haskell_tools = vim.g.haskell_tools or {} +---@type HTOpts +local opts = type(haskell_tools) == 'function' and haskell_tools() or haskell_tools + +---@type HTConfig +HTConfig = vim.tbl_deep_extend('force', {}, HTDefaultConfig, opts) +local check = require('haskell-tools.config.check') +local ok, err = check.validate(HTConfig) +if not ok then + vim.notify('haskell-tools: ' .. err, vim.log.levels.ERROR) +end + +local dont_warn = { + 'tools.repl.auto_focus', + 'hls.capabilities', + 'hls.settings', + 'hls.default_settings', +} +local unrecognized_keys = check.get_unrecognized_keys(opts, HTDefaultConfig, dont_warn) +if #unrecognized_keys > 0 then + vim.notify('unrecognized configs in vim.g.haskell_tools: ' .. vim.inspect(unrecognized_keys), vim.log.levels.WARN) +end + +HTConfig.debug_info.unrecognized_keys = unrecognized_keys + +return HTConfig diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/dap/init.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/dap/init.lua new file mode 100644 index 00000000..0e714b67 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/dap/init.lua @@ -0,0 +1,156 @@ +---@mod haskell-tools.dap haskell-tools nvim-dap setup + +local deps = require('haskell-tools.deps') +local Types = require('haskell-tools.types.internal') +local compat = require('haskell-tools.compat') + +---@param root_dir string +local function get_ghci_dap_cmd(root_dir) + local HtProjectHelpers = require('haskell-tools.project.helpers') + if HtProjectHelpers.is_cabal_project(root_dir) then + return 'cabal exec -- ghci-dap --interactive -i ${workspaceFolder}' + else + return 'stack ghci --test --no-load --no-build --main-is TARGET --ghci-options -fprint-evld-with-show' + end +end + +---@param root_dir string +---@param opts AddDapConfigOpts +---@return HsDapLaunchConfiguration[] +local function find_json_configurations(root_dir, opts) + ---@type HsDapLaunchConfiguration[] + local configurations = {} + local log = require('haskell-tools.log.internal') + local results = vim.fn.glob(compat.joinpath(root_dir, opts.settings_file_pattern), true, true) + if #results == 0 then + log.info(opts.settings_file_pattern .. ' not found in project root ' .. root_dir) + else + for _, launch_json in pairs(results) do + local OS = require('haskell-tools.os') + local content = OS.read_file(launch_json) + local success, settings = pcall(vim.json.decode, content) + if not success then + local msg = 'Could not decode ' .. launch_json .. '.' + log.warn { msg, error } + elseif settings and settings.configurations and type(settings.configurations) == 'table' then + configurations = vim.list_extend(configurations, settings.configurations) + end + end + end + return configurations +end + +---@param root_dir string +---@return HsDapLaunchConfiguration[] +local function detect_launch_configurations(root_dir) + local launch_configurations = {} + local HTConfig = require('haskell-tools.config.internal') + local dap_opts = HTConfig.dap + ---@param entry_point HsEntryPoint + ---@return HsDapLaunchConfiguration + local function mk_launch_configuration(entry_point) + ---@class HsDapLaunchConfiguration + local HsDapLaunchConfiguration = { + type = 'ghc', + request = 'launch', + name = entry_point.package_name .. ':' .. entry_point.exe_name, + workspace = '${workspaceFolder}', + startup = compat.joinpath(entry_point.package_dir, entry_point.source_dir, entry_point.main), + startupFunc = '', -- defaults to 'main' if not set + startupArgs = '', + stopOnEntry = false, + mainArgs = '', + logFile = dap_opts.logFile, + logLevel = dap_opts.logLevel, + ghciEnv = vim.empty_dict(), + ghciPrompt = 'λ: ', + ghciInitialPrompt = 'ghci> ', + ghciCmd = get_ghci_dap_cmd(root_dir), + forceInspect = false, + } + return HsDapLaunchConfiguration + end + local HtProjectHelpers = require('haskell-tools.project.helpers') + for _, entry_point in pairs(HtProjectHelpers.parse_project_entrypoints(root_dir)) do + table.insert(launch_configurations, mk_launch_configuration(entry_point)) + end + return launch_configurations +end + +---@type table +local _configuration_cache = {} + +if not deps.has('dap') then + ---@type HsDapTools + local NullHsDapTools = { + discover_configurations = function(_) end, + } + return NullHsDapTools +end + +local dap = require('dap') + +---@class HsDapTools +local HsDapTools = {} + +---@class AddDapConfigOpts +local DefaultAutoDapConfigOpts = { + ---@type boolean Whether to automatically detect launch configurations for the project. + autodetect = true, + ---@type string File name or pattern to search for. Defaults to 'launch.json'. + settings_file_pattern = 'launch.json', +} + +---Discover nvim-dap launch configurations for haskell-debug-adapter. +---@param bufnr number|nil The buffer number +---@param opts AddDapConfigOpts|nil +---@return nil +HsDapTools.discover_configurations = function(bufnr, opts) + local HTConfig = require('haskell-tools.config.internal') + local HTDapConfig = HTConfig.dap + local log = require('haskell-tools.log.internal') + local dap_cmd = Types.evaluate(HTDapConfig.cmd) or {} + if #dap_cmd == 0 or vim.fn.executable(dap_cmd[1]) ~= 1 then + log.debug { 'DAP server executable not found.', dap_cmd } + return + end + ---@cast dap_cmd string[] + dap.adapters.ghc = { + type = 'executable', + command = table.concat(dap_cmd, ' '), + } + bufnr = bufnr or 0 -- Default to current buffer + opts = vim.tbl_deep_extend('force', {}, DefaultAutoDapConfigOpts, opts or {}) + local filename = vim.api.nvim_buf_get_name(bufnr) + local HtProjectHelpers = require('haskell-tools.project.helpers') + local project_root = HtProjectHelpers.match_project_root(filename) + if not project_root then + log.warn('dap: Unable to detect project root for file ' .. filename) + return + end + if _configuration_cache[project_root] then + log.debug('dap: Found cached configuration. Skipping.') + return + end + local discovered_configurations = {} + local json_configurations = find_json_configurations(project_root, opts) + vim.list_extend(discovered_configurations, json_configurations) + if opts.autodetect then + local detected_configurations = detect_launch_configurations(project_root) + vim.list_extend(discovered_configurations, detected_configurations) + end + _configuration_cache[project_root] = discovered_configurations + ---@type HsDapLaunchConfiguration[] + local dap_configurations = dap.configurations.haskell or {} + for _, cfg in ipairs(discovered_configurations) do + for i, existing_config in pairs(dap_configurations) do + if cfg.name == existing_config.name and cfg.startup == existing_config.startup then + table.remove(dap_configurations, i) + end + end + table.insert(dap_configurations, cfg) + end + dap.configurations.haskell = dap_configurations +end + +return HsDapTools diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/dap/internal.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/dap/internal.lua new file mode 100644 index 00000000..2562ce63 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/dap/internal.lua @@ -0,0 +1,33 @@ +local compat = require('haskell-tools.compat') + +---@class DapInternal +local Dap = {} + +---@param package_name string +---@param exe_name string +---@param package_dir string +---@param mains string[] +---@param source_dirs string[] +---@return HsEntryPoint[] entry_points +Dap.mk_entry_points = function(package_name, exe_name, package_dir, mains, source_dirs) + ---@type HsEntryPoint[] + local entry_points = {} + for _, source_dir in pairs(source_dirs) do + for _, main in pairs(mains) do + local filename = compat.joinpath(package_dir, source_dir, main) + if vim.fn.filereadable(filename) == 1 then + local entry_point = { + package_name = package_name, + exe_name = exe_name, + main = main, + source_dir = source_dir, + package_dir = package_dir, + } + table.insert(entry_points, entry_point) + end + end + end + return entry_points +end + +return Dap diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/deps.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/deps.lua new file mode 100644 index 00000000..72460d3f --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/deps.lua @@ -0,0 +1,80 @@ +---@mod haskell-tools.deps + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +---@brief ]] + +---@class Deps +local Deps = {} + +---@param modname string The name of the module +---@param on_available any|nil Callback. Can be a function that takes the module name as an argument or a value. +---@param on_not_available any|nil Callback to execute if the module is not available. Can be a function or a value. +---@return any result Return value of on_available or on_not_available +function Deps.if_available(modname, on_available, on_not_available) + local has_mod, mod = pcall(require, modname) + if has_mod and type(on_available) == 'function' then + return on_available(mod) + elseif has_mod then + return on_available + end + if not on_not_available then + return nil + end + if type(on_not_available) == 'function' then + return on_not_available() + end + return on_not_available +end + +---Require a module or fail +---@param modname string +---@param plugin_name string +---@return unknown +---@require +function Deps.require_or_err(modname, plugin_name) + return Deps.if_available(modname, function(mod) + return mod + end, function() + error('haskell-tools: This plugin requires the ' .. plugin_name .. ' plugin.') + end) +end + +---@param modname string The name of the module +---@return boolean +function Deps.has(modname) + return Deps.if_available(modname, true, false) +end + +---@return boolean +function Deps.has_telescope() + return Deps.has('telescope') +end + +---@return unknown +---@require +function Deps.require_telescope(modname) + return Deps.require_or_err(modname, 'nvim-telescope/telescope.nvim') +end + +---@return unknown +---@require +function Deps.require_toggleterm(modname) + return Deps.require_or_err(modname, 'akinsho/toggleterm') +end + +---@return boolean +function Deps.has_toggleterm() + return Deps.has('toggleterm') +end + +---@return unknown +---@require +function Deps.require_iron(modname) + return Deps.require_or_err(modname, 'hkupty/iron.nvim') +end + +return Deps diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/health.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/health.lua new file mode 100644 index 00000000..4ee5888b --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/health.lua @@ -0,0 +1,262 @@ +---@mod haskell-tools.health Health checks + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +---@brief ]] + +local health = {} + +local Types = require('haskell-tools.types.internal') +local deps = require('haskell-tools.deps') +local HTConfig = require('haskell-tools.config.internal') +local h = vim.health or require('health') +---@diagnostic disable-next-line: deprecated +local start = h.start or h.report_start +---@diagnostic disable-next-line: deprecated +local ok = h.ok or h.report_ok +---@diagnostic disable-next-line: deprecated +local error = h.error or h.report_error +---@diagnostic disable-next-line: deprecated +local warn = h.warn or h.report_warn + +---@class LuaDependency +---@field module string The name of a module +---@field optional fun():boolean Function that returns whether the dependency is optional +---@field url string URL (markdown) +---@field info string Additional information + +---@type LuaDependency[] +local lua_dependencies = { + { + module = 'telescope', + optional = function() + if not HTConfig then + return true + end + local hoogle_mode = HTConfig.tools.hoogle.mode + return hoogle_mode:match('telescope') == nil + end, + url = '[nvim-telescope/telescope.nvim](https://github.com/nvim-telescope/telescope.nvim)', + info = 'Required for hoogle search modes "telescope-local" and "telescope-web"', + }, +} + +---@class ExternalDependency +---@field name string Name of the dependency +---@field get_binaries fun():string[]Function that returns the binaries to check for +---@field optional fun():boolean Function that returns whether the dependency is optional +---@field url string URL (markdown) +---@field info string Additional information +---@field extra_checks function|nil Optional extra checks to perform if the dependency is installed + +---@type ExternalDependency[] +local external_dependencies = { + { + name = 'haskell-language-server', + get_binaries = function() + local default = { 'haskell-language-server-wrapper', 'haskell-language-server' } + if not HTConfig then + return default + end + local cmd = Types.evaluate(HTConfig.hls.cmd) + if not cmd or #cmd == 0 then + return default + end + return { cmd[1] } + end, + optional = function() + return true + end, + url = '[haskell-language-server](https://haskell-language-server.readthedocs.io)', + info = 'Required by the LSP client.', + }, + { + name = 'hoogle', + get_binaries = function() + return { 'hoogle' } + end, + optional = function() + if not HTConfig then + return true + end + local hoogle_mode = HTConfig.tools.hoogle.mode + return hoogle_mode ~= 'telescope-local' + end, + url = '[ndmitchell/hoogle](https://github.com/ndmitchell/hoogle)', + info = [[ + Recommended for better Hoogle search performance. + Without a local installation, the web API will be used by default. + Required if the hoogle mode is set to "telescope-local". + ]], + extra_checks = function() + local handle, errmsg = io.popen('hoogle base') + if handle then + handle:close() + end + if errmsg then + local hoogle_mode = HTConfig.tools.hoogle.mode + if hoogle_mode and hoogle_mode == 'auto' or hoogle_mode == 'telescope-local' then + error('hoogle: ' .. errmsg) + else + warn('hoogle: ' .. errmsg) + end + end + end, + }, + { + name = 'fast-tags', + get_binaries = function() + return { 'fast-tags' } + end, + optional = function() + return true + end, + url = '[fast-tags](https://hackage.haskell.org/package/fast-tags)', + info = 'Optional, for generating tags as a `tagfunc` fallback.', + }, + { + name = 'curl', + get_binaries = function() + return { 'curl' } + end, + optional = function() + local hoogle_mode = HTConfig.tools.hoogle.mode + return not hoogle_mode or hoogle_mode ~= 'telescope-web' + end, + url = '[curl](https://curl.se/)', + info = 'Required for "telescope-web" hoogle seach mode.', + }, + { + name = 'haskell-debug-adapter', + get_binaries = function() + return { 'haskell-debug-adapter' } + end, + optional = function() + return true + end, + url = '[haskell-debug-adapter](https://github.com/phoityne/haskell-debug-adapter)', + info = 'Optional, for `dap` support.', + }, + { + name = 'ghci-dap', + get_binaries = function() + return { 'ghci-dap' } + end, + optional = function() + return true + end, + url = '[ghci-dap](https://github.com/phoityne/ghci-dap)', + info = 'Optional, for `dap` support.', + }, +} + +---@param dep LuaDependency +local function check_lua_dependency(dep) + if deps.has(dep.module) then + ok(dep.url .. ' installed.') + return + end + if dep.optional() then + warn(('%s not installed. %s %s'):format(dep.module, dep.info, dep.url)) + else + error(('Lua dependency %s not found: %s'):format(dep.module, dep.url)) + end +end + +---@param dep ExternalDependency +---@return boolean is_installed +---@return string|nil version +local check_installed = function(dep) + local binaries = dep.get_binaries() + for _, binary in ipairs(binaries) do + if vim.fn.executable(binary) == 1 then + local handle = io.popen(binary .. ' --version') + if handle then + local binary_version, error_msg = handle:read('*a') + handle:close() + if error_msg then + return true + end + return true, binary_version + end + return true + end + end + return false +end + +---@param dep ExternalDependency +local function check_external_dependency(dep) + local installed, mb_version = check_installed(dep) + if installed then + local mb_version_newline_idx = mb_version and mb_version:find('\n') + local mb_version_len = mb_version and (mb_version_newline_idx and mb_version_newline_idx - 1 or mb_version:len()) + local version = mb_version and mb_version:sub(0, mb_version_len) or '(unknown version)' + ok(('%s: found %s'):format(dep.name, version)) + if dep.extra_checks then + dep.extra_checks() + end + return + end + if dep.optional() then + warn(([[ + %s: not found. + Install %s for extended capabilities. + %s + ]]):format(dep.name, dep.url, dep.info)) + else + error(([[ + %s: not found. + haskell-tools.nvim requires %s. + %s + ]]):format(dep.name, dep.url, dep.info)) + end +end + +local function check_config() + start('Checking config') + if vim.g.haskell_tools and not HTConfig.debug_info.was_g_haskell_tools_sourced then + error('vim.g.haskell_tools is set, but was not sourced before haskell-tools.nvim was initialized.') + return + end + local valid, err = require('haskell-tools.config.check').validate(HTConfig) + if valid then + ok('No errors found in config.') + else + error(err or '' .. vim.g.haskell_tools and '' or ' This looks like a plugin bug!') + end + local unrecognized_keys = HTConfig.debug_info.unrecognized_keys + if #unrecognized_keys > 0 then + warn('unrecognized configs in vim.g.haskell_tools: ' .. vim.inspect(unrecognized_keys)) + end +end + +local function check_for_conflicts() + start('Checking for conflicting plugins') + for _, autocmd in ipairs(vim.api.nvim_get_autocmds { event = 'FileType', pattern = 'haskell' }) do + if autocmd.group_name and autocmd.group_name == 'lspconfig' and autocmd.desc and autocmd.desc:match(' hls ') then + error('lspconfig.hls has been setup. This will likely lead to conflicts with the haskell-tools LSP client.') + return + end + end + ok('No conflicting plugins detected.') +end + +function health.check() + start('Checking for Lua dependencies') + for _, dep in ipairs(lua_dependencies) do + check_lua_dependency(dep) + end + + start('Checking external dependencies') + for _, dep in ipairs(external_dependencies) do + check_external_dependency(dep) + end + check_config() + check_for_conflicts() +end + +return health diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/helpers.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/helpers.lua new file mode 100644 index 00000000..2f3bafe2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/helpers.lua @@ -0,0 +1,147 @@ +---@mod haskell-tools.hoogle.helpers + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- This module provides hoogle search capabilities for telescope.nvim, +--- The telescope search is mostly inspired by telescope_hoogle by Luc Tielen, +--- but has been redesigned for searching for individual terms. +--- https://github.com/luc-tielen/telescope_hoogle +---@brief ]] + +local deps = require('haskell-tools.deps') +local OS = require('haskell-tools.os') +local actions = deps.require_telescope('telescope.actions') +local actions_state = deps.require_telescope('telescope.actions.state') +local entry_display = deps.require_telescope('telescope.pickers.entry_display') + +---@class HoogleHelpers +local HoogleHelpers = {} + +---@param buf number the telescope buffebuffer numberr +---@param map fun(mode:string,keys:string,action:function) callback for creating telescope keymaps +---@return boolean +function HoogleHelpers.hoogle_attach_mappings(buf, map) + actions.select_default:replace(function() + -- Copy type signature to clipboard + local entry = actions_state.get_selected_entry() + local reg = vim.o.clipboard == 'unnamedplus' and '+' or '"' + vim.fn.setreg(reg, entry.type_sig) + actions.close(buf) + end) + map('i', '', function() + -- Open in browser + local entry = actions_state.get_selected_entry() + OS.open_browser(entry.url) + end) + map('i', '', function() + -- Replace word under cursor + local entry = actions_state.get_selected_entry() + local func_name = entry.type_sig:match('([^%s]*)%s::') + if not func_name then + vim.notify('Hoogle (replace): Not a function.', vim.log.levels.WARN) + return + end + actions.close(buf) + vim.api.nvim_input('ciw' .. func_name .. '') + end) + return true +end + +---Format an html string to be displayed by Neovim +---@param html string +---@return string nvim_str +local function format_html(html) + return html and html:gsub('<', '<'):gsub('>', '>'):gsub('&', '&') or '' +end + +---@class TelescopeHoogleEntry +---@field value string +---@field valid boolean +---@field type_sig string The entry's type signature +---@field module_name string The name of the module that contains the entry +---@field url string|nil The entry's Hackage URL +---@field docs string|nil The Hoogle entry's documentation +---@field display fun(TelescopeHoogleEntry):TelescopeDisplay +---@field ordinal string +---@field preview_command fun(TelescopeHoogleEntry, number):nil + +---Show a preview in the Telescope previewer +---@param entry TelescopeHoogleEntry +---@param buf number the Telescope preview buffer +local function show_preview(entry, buf) + local docs = format_html(entry.docs) + local lines = vim.split(docs, '\n') + vim.api.nvim_buf_set_lines(buf, 0, -1, true, lines) + + vim.api.nvim_buf_call(buf, function() + local win = vim.fn.win_findbuf(buf)[1] + vim.wo[win].conceallevel = 2 + vim.wo[win].wrap = true + vim.wo[win].linebreak = true + vim.bo[buf].textwidth = 80 + end) +end + +---@class TelescopeDisplay + +---@param entry TelescopeHoogleEntry +---@return TelescopeDisplay +local function make_display(entry) + local module = entry.module_name + + local displayer = entry_display.create { + separator = '', + items = { + { width = module and #module + 1 or 0 }, + { remaining = true }, + }, + } + return displayer { { module, 'Structure' }, { entry.type_sig, 'Type' } } +end + +---@param hoogle_item string +---@return string type_signature +local function get_type_sig(hoogle_item) + local name = hoogle_item:match('(.*)') + local sig = hoogle_item:match(':: (.*)') + if name and sig then + local name_with_type = (name .. ' :: ' .. format_html(sig)):gsub('%s+', ' ') -- trim duplicate whitespace + return name_with_type + end + return hoogle_item +end + +---@class HoogleData +---@field module HoogleModule|nil +---@field item string|nil +---@field url string|nil +---@field docs string|nil + +---@class HoogleModule +---@field name string + +---@param data HoogleData +---@return TelescopeHoogleEntry|nil +function HoogleHelpers.mk_hoogle_entry(data) + local module_name = (data.module or {}).name + local type_sig = data.item and get_type_sig(data.item) or '' + if not module_name or not type_sig then + return nil + end + return { + value = data, + valid = true, + type_sig = type_sig, + module_name = module_name, + url = data.url, + docs = data.docs, + display = make_display, + ordinal = data.item .. data.url, + preview_command = show_preview, + } +end + +return HoogleHelpers diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/init.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/init.lua new file mode 100644 index 00000000..c04daa31 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/init.lua @@ -0,0 +1,114 @@ +---@mod haskell-tools.hoogle haskell-tools Hoogle search + +local log = require('haskell-tools.log.internal') +local lsp_util = vim.lsp.util + +---@type fun(sig_or_func_name:string, options:table|nil):nil +local handler + +---@param options table +---@return fun(err: lsp.ResponseError|nil, result: any, context: lsp.HandlerContext, config: table|nil) +local function mk_lsp_hoogle_signature_handler(options) + return function(_, result, _, _) + if not (result and result.contents) then + vim.notify('hoogle: No information available') + return + end + local func_name = vim.fn.expand('') + ---@cast func_name string + local HtParser = require('haskell-tools.parser') + local signature_or_func_name = HtParser.try_get_signatures_from_markdown(func_name, result.contents.value) + or func_name + log.debug { 'Hoogle LSP signature search', signature_or_func_name } + if signature_or_func_name ~= '' then + handler(signature_or_func_name, options) + end + end +end + +---@param options table +local function lsp_hoogle_signature(options) + local params = lsp_util.make_position_params() + return vim.lsp.buf_request(0, 'textDocument/hover', params, mk_lsp_hoogle_signature_handler(options)) +end + +local HTConfig = require('haskell-tools.config.internal') +local opts = HTConfig.tools.hoogle +local hoogle_web = require('haskell-tools.hoogle.web') +local hoogle_local = require('haskell-tools.hoogle.local') + +---@return nil +local function set_web_handler() + handler = hoogle_web.telescope_search + log.debug('handler = telescope-web') +end + +---@return nil +local function set_local_handler() + handler = hoogle_local.telescope_search + log.debug('handler = telescope-local') +end + +---@return nil +local function set_browser_handler() + handler = hoogle_web.browser_search + log.debug('handler = browser') +end + +if opts.mode == 'telescope-web' then + set_web_handler() +elseif opts.mode == 'telescope-local' then + if not hoogle_local.has_hoogle() then + local msg = 'handler set to "telescope-local" but no hoogle executable found.' + log.warn(msg) + vim.notify('haskell-tools.hoogle: ' .. msg, vim.log.levels.WARN) + set_web_handler() + return + end + local deps = require('haskell-tools.deps') + if not deps.has_telescope() then + local msg = 'handler set to "telescope-local" but telescope.nvim is not installed.' + log.warn(msg) + vim.notify('haskell-tools.hoogle: ' .. msg, vim.log.levels.WARN) + set_web_handler() + return + end + set_local_handler() +elseif opts.mode == 'browser' then + set_browser_handler() +elseif opts.mode == 'auto' then + local deps = require('haskell-tools.deps') + if not deps.has_telescope() then + set_browser_handler() + elseif hoogle_local.has_hoogle() then + set_local_handler() + else + set_web_handler() + end +end + +---@class HoogleTools +local HoogleTools = {} + +---@param options table|nil Includes the `search_term` and options to pass to the telescope picker (if available) +---@return nil +HoogleTools.hoogle_signature = function(options) + options = options or {} + log.debug { 'Hoogle signature search options', options } + if options.search_term then + handler(options.search_term) + return + end + local LspHelpers = require('haskell-tools.lsp.helpers') + local clients = LspHelpers.get_clients { bufnr = vim.api.nvim_get_current_buf() } + if #clients > 0 then + lsp_hoogle_signature(options) + else + log.debug('Hoogle signature search: No clients attached. Falling back to .') + local cword = vim.fn.expand('') + ---@cast cword string + handler(cword, options) + end +end + +return HoogleTools diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/local.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/local.lua new file mode 100644 index 00000000..0ce363a5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/local.lua @@ -0,0 +1,102 @@ +---@mod haskell-tools.hoogle.local + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +---@brief ]] + +local log = require('haskell-tools.log.internal') +local deps = require('haskell-tools.deps') +local compat = require('haskell-tools.compat') + +---@class LocalHoogleHandler +local HoogleLocal = {} + +---@return boolean has_hoogle `true` if the `hoogle` executable exists +function HoogleLocal.has_hoogle() + return vim.fn.executable('hoogle') == 1 +end + +if not HoogleLocal.has_hoogle() then + return HoogleLocal +end + +if not deps.has_telescope() then + return HoogleLocal +end + +---@class LocalHoogleOpts +---@field entry_maker function|nil telescope entry maker +---@field count number|nil number of results to display + +---Construct the hoogle cli arguments +---@param search_term string The Hoogle search term +---@param opts LocalHoogleOpts +---@return string[] hoogle_args +local function mk_hoogle_args(search_term, opts) + local count = opts.count or 50 + local args = compat.tbl_flatten { '--json', '--count=' .. count, search_term } + log.debug { 'Hoogle local args', args } + return args +end + +local pickers = deps.require_telescope('telescope.pickers') +local finders = deps.require_telescope('telescope.finders') +local previewers = deps.require_telescope('telescope.previewers') +local HoogleHelpers = require('haskell-tools.hoogle.helpers') + +---@param search_term string The Hoogle search term +---@param opts LocalHoogleOpts|nil +---@return nil +function HoogleLocal.telescope_search(search_term, opts) + opts = opts or {} + opts.entry_maker = opts.entry_maker or HoogleHelpers.mk_hoogle_entry + local config = deps.require_telescope('telescope.config').values + if not config then + local msg = 'telescope.nvim has not been setup.' + log.error(msg) + vim.notify_once('haskell-tools.hoogle: ' .. msg, vim.log.levels.ERROR) + return + end + local cmd = vim.list_extend({ 'hoogle' }, mk_hoogle_args(search_term, opts)) + compat.system( + cmd, + nil, + vim.schedule_wrap(function(result) + ---@cast result vim.SystemCompleted + local output = result.stdout + if result.code ~= 0 or output == nil then + local err_msg = 'haskell-tools: hoogle search failed. Exit code: ' .. result.code + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return + end + local success, results = pcall(vim.json.decode, output) + if not success then + log.error { 'Hoogle: Could not process result.', output } + vim.notify('Hoogle: Could not process result - ' .. vim.inspect(output), vim.log.levels.ERROR) + return + end + if #results < 1 or output == 'No results found' then + vim.notify('Hoogle: No results found.', vim.log.levels.INFO) + return + end + pickers + .new(opts, { + prompt_title = 'Hoogle: ' .. search_term, + sorter = config.generic_sorter(opts), + finder = finders.new_table { + results = results, + entry_maker = HoogleHelpers.mk_hoogle_entry, + }, + previewer = previewers.display_content.new(opts), + attach_mappings = HoogleHelpers.hoogle_attach_mappings, + }) + :find() + end) + ) +end + +return HoogleLocal diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/web.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/web.lua new file mode 100644 index 00000000..2eb0a487 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/hoogle/web.lua @@ -0,0 +1,129 @@ +---@mod haskell-tools.hoogle.web + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +---@brief ]] + +local log = require('haskell-tools.log.internal') +local deps = require('haskell-tools.deps') +local OS = require('haskell-tools.os') +local compat = require('haskell-tools.compat') + +---@class WebHoogleHandler +local WebHoogleHandler = {} + +---@param c string A single character +---@return string The hex representation +local char_to_hex = function(c) + return string.format('%%%02X', c:byte()) +end + +---Encode a URL so it can be opened in a browser +---@param url string +---@return string encoded_url +local function urlencode(url) + url = url:gsub('\n', '\r\n') + url = url:gsub('([^%w ])', char_to_hex) + url = url:gsub(' ', '+') + return url +end + +---@class TelescopeHoogleWebOpts +---@field hoogle HoogleWebSearchOpts|nil + +---@class HoogleWebSearchOpts +---@field scope string|nil The scope of the search +---@field json boolean|nil Whather to request JSON enocded results + +---Build a Hoogle request URL +---@param search_term string +---@param opts TelescopeHoogleWebOpts +local function mk_hoogle_request(search_term, opts) + local hoogle_opts = opts.hoogle or {} + local scope_param = hoogle_opts.scope and '&scope=' .. hoogle_opts.scope or '' + local hoogle_request = 'https://hoogle.haskell.org/?hoogle=' + .. urlencode(search_term) + .. scope_param + .. (hoogle_opts.json and '&mode=json' or '') + log.debug { 'Hoogle web request', hoogle_request } + return hoogle_request +end + +if deps.has_telescope() then + local pickers = deps.require_telescope('telescope.pickers') + local finders = deps.require_telescope('telescope.finders') + local previewers = deps.require_telescope('telescope.previewers') + local HoogleHelpers = require('haskell-tools.hoogle.helpers') + + ---@param search_term string + ---@param opts TelescopeHoogleWebOpts|nil + ---@return nil + function WebHoogleHandler.telescope_search(search_term, opts) + local config = deps.require_telescope('telescope.config').values + if not config then + local msg = 'telescope.nvim has not been setup. Falling back to browser search.' + log.warn(msg) + vim.notify_once('haskell-tools.hoogle: ' .. msg, vim.log.levels.WARN) + WebHoogleHandler.browser_search(search_term, opts) + return + end + if vim.fn.executable('curl') == 0 then + log.error('curl executable not found.') + vim.notify("haskell-tools.hoogle-web: 'curl' executable not found! Aborting.", vim.log.levels.ERROR) + return + end + opts = opts or {} + opts.hoogle = opts.hoogle or {} + opts.hoogle.json = true + local url = mk_hoogle_request(search_term, opts) + local curl_command = { 'curl', '--silent', url, '-H', 'Accept: application/json' } + log.debug(curl_command) + compat.system(curl_command, nil, function(result) + ---@cast result vim.SystemCompleted + log.debug { 'Hoogle web response', result } + local response = result.stdout + if result.code ~= 0 or response == nil then + vim.notify('hoogle web: ' .. (result.stderr or 'error calling curl'), vim.log.levels.ERROR) + return + end + local ok, results = pcall(vim.json.decode, response) + vim.schedule(function() + if not ok then + log.error { 'Hoogle web response (invalid JSON)', curl_command, 'result: ' .. result } + vim.notify( + "haskell-tools.hoogle: Received invalid JSON from curl. Likely due to a failed request. See ':HtLog' for details'", + vim.log.levels.ERROR + ) + return + end + pickers + .new(opts, { + prompt_title = 'Hoogle: ' .. search_term, + finder = finders.new_table { + results = results, + entry_maker = HoogleHelpers.mk_hoogle_entry, + }, + sorter = config.generic_sorter(opts), + previewer = previewers.display_content.new(opts), + attach_mappings = HoogleHelpers.hoogle_attach_mappings, + }) + :find() + end) + end) + end +end + +---@param search_term string +---@param opts TelescopeHoogleWebOpts|nil +---@return nil +function WebHoogleHandler.browser_search(search_term, opts) + opts = vim.tbl_deep_extend('keep', opts or {}, { + hoogle = { json = false }, + }) + OS.open_browser(mk_hoogle_request(search_term, opts)) +end + +return WebHoogleHandler diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/init.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/init.lua new file mode 100644 index 00000000..0ddbc23e --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/init.lua @@ -0,0 +1,58 @@ +---@toc haskell-tools.contents + +---@mod intro Introduction +---@brief [[ +---This plugin automatically configures the `haskell-language-server` builtin LSP client +---and integrates with other haskell tools. +---@brief ]] +--- +---@brief [[ +---WARNING: +---Do not call the `lspconfig.hls` setup or set up the lsp manually, +---as doing so may cause conflicts. +---@brief ]] +--- +---@brief [[ +---NOTE: This plugin is a filetype plugin. +---There is no need to call a `setup` function. +---@brief ]] + +---@mod haskell-tools The haskell-tools module + +---@brief [[ +---Entry-point into this plugin's public API. + +---@brief ]] + +---@param module string +---@return table +local function lazy_require(module) + return setmetatable({}, { + __index = function(_, key) + return require(module)[key] + end, + __newindex = function(_, key, value) + require(module)[key] = value + end, + }) +end + +---@class HaskellTools +local HaskellTools = { + ---@type HlsTools + lsp = lazy_require('haskell-tools.lsp'), + ---@type HoogleTools + hoogle = lazy_require('haskell-tools.hoogle'), + ---@type HsReplTools + repl = lazy_require('haskell-tools.repl'), + ---@type HsProjectTools + project = lazy_require('haskell-tools.project'), + ---@type FastTagsTools + tags = lazy_require('haskell-tools.tags'), + ---@type HsDapTools + dap = lazy_require('haskell-tools.dap'), + ---@type HaskellToolsLog + log = lazy_require('haskell-tools.log'), +} + +return HaskellTools diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/internal.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/internal.lua new file mode 100644 index 00000000..51ac7d15 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/internal.lua @@ -0,0 +1,79 @@ +---@mod haskell-tools.internal + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- The internal API for use by this plugin's ftplugins +---@brief ]] + +local HTConfig = require('haskell-tools.config.internal') + +---@class InternalApi +local InternalApi = {} + +---@return boolean tf Is LSP supported for the current buffer? +local function buf_is_lsp_supported() + local bufnr = vim.api.nvim_get_current_buf() + local HtProjectHelpers = require('haskell-tools.project.helpers') + if not HtProjectHelpers.is_cabal_file(bufnr) then + return true + end + local LspHelpers = require('haskell-tools.lsp.helpers') + return LspHelpers.is_hls_version_with_cabal_support() +end + +---Starts or attaches an LSP client to the current buffer and sets up the plugin if necessary. +--- +---@see haskell-tools.config for configuration options. +---@see ftplugin +---@see base-directories +---@usage [[ +----- In your neovim configuration, set: +---vim.g.haskell_tools = { +--- tools = { +--- -- ... +--- }, +--- hls = { +--- on_attach = function(client, bufnr) +--- -- Set keybindings, etc. here. +--- end, +--- -- ... +--- }, +--- } +----- In `~/.config/nvim/ftplugin/after/.lua`, call +---local ht = require('haskell-tools') +---ht.start_or_attach() +---@usage ]] +local function start_or_attach() + local Types = require('haskell-tools.types.internal') + local HaskellTools = require('haskell-tools') + if Types.evaluate(HTConfig.hls.auto_attach) and buf_is_lsp_supported() then + HaskellTools.lsp.start() + end + if Types.evaluate(HTConfig.tools.tags.enable) then + HaskellTools.tags.generate_project_tags(nil, { refresh = false }) + end +end + +---Auto-discover nvim-dap launch configurations (if auto-discovery is enabled) +local function dap_discover() + local auto_discover = HTConfig.dap.auto_discover + if not auto_discover then + return + elseif type(auto_discover) == 'boolean' then + return require('haskell-tools').dap.discover_configurations() + end + ---@cast auto_discover AddDapConfigOpts + local bufnr = vim.api.nvim_get_current_buf() + require('haskell-tools').dap.discover_configurations(bufnr, auto_discover) +end + +---ftplugin implementation +function InternalApi.ftplugin() + start_or_attach() + dap_discover() +end + +return InternalApi diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/log/init.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/log/init.lua new file mode 100644 index 00000000..994f9c77 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/log/init.lua @@ -0,0 +1,76 @@ +---@mod haskell-tools.log haskell-tools Logging +--- +---@brief [[ +--- The following commands are available: +--- +--- * `:HtLog` - Open the haskell-tools.nvim log file. +--- * `:HlsLog` - Open the haskell-language-server log file. +--- * `:HtSetLogLevel` - Set the haskell-tools.nvim and LSP client log level. +---@brief ]] + +---@class HaskellToolsLog +local HaskellToolsLog = {} + +---Get the haskell-language-server log file +---@return string filepath +function HaskellToolsLog.get_hls_logfile() + return require('haskell-tools.log.internal').get_hls_logfile() +end + +---Get the haskell-tools.nvim log file path. +---@return string filepath +function HaskellToolsLog.get_logfile() + return require('haskell-tools.log.internal').get_logfile() +end + +---Open the haskell-language-server log file +---@return nil +function HaskellToolsLog.nvim_open_hls_logfile() + return require('haskell-tools.log.internal').nvim_open_hls_logfile() +end + +---Open the haskell-tools.nvim log file. +---@return nil +function HaskellToolsLog.nvim_open_logfile() + return require('haskell-tools.log.internal').nvim_open_logfile() +end + +---Set the haskell-tools.nvim and LSP client log level +---@param level (string|integer) The log level +---@return nil +---@see vim.log.levels +function HaskellToolsLog.set_level(level) + return require('haskell-tools.log.internal').set_level(level) +end + +local commands = { + { + 'HtLog', + function() + HaskellToolsLog.nvim_open_logfile() + end, + {}, + }, + { + 'HlsLog', + function() + HaskellToolsLog.nvim_open_hls_logfile() + end, + { nargs = 1 }, + }, + { + 'HtSetLogLevel', + function(tbl) + local level = vim.fn.expand(tbl.args) + ---@cast level string + HaskellToolsLog.set_level(tonumber(level) or level) + end, + { nargs = 1 }, + }, +} + +for _, command in ipairs(commands) do + vim.api.nvim_create_user_command(unpack(command)) +end + +return HaskellToolsLog diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/log/internal.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/log/internal.lua new file mode 100644 index 00000000..11abf68f --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/log/internal.lua @@ -0,0 +1,148 @@ +---@mod haskell-tools.log.internal haskell-tools Logging (internal) + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- The internal API for use by this plugin's ftplugins +---@brief ]] + +local compat = require('haskell-tools.compat') + +---@class HaskellToolsLogInternal +local HaskellToolsLogInternal = { + -- NOTE: These functions are initialised as empty for type checking purposes + -- and implemented later. + trace = function(_) end, + debug = function(_) end, + info = function(_) end, + warn = function(_) end, + error = function(_) end, +} + +local LARGE = 1e9 + +local log_date_format = '%F %H:%M:%S' + +local function format_log(arg) + return vim.inspect(arg, { newline = '' }) +end + +local HTConfig = require('haskell-tools.config.internal') + +local logfilename = HTConfig.tools.log.logfile + +---Get the haskell-tools.nvim log file path. +---@return string filepath +function HaskellToolsLogInternal.get_logfile() + return logfilename +end + +---Open the haskell-tools.nvim log file. +function HaskellToolsLogInternal.nvim_open_logfile() + vim.cmd('e ' .. HaskellToolsLogInternal.get_logfile()) +end + +local logfile, openerr +--- @private +--- Opens log file. Returns true if file is open, false on error +--- @return boolean +local function open_logfile() + -- Try to open file only once + if logfile then + return true + end + if openerr then + return false + end + + logfile, openerr = io.open(logfilename, 'a+') + if not logfile then + local err_msg = string.format('Failed to open haskell-tools.nvim log file: %s', openerr) + vim.notify(err_msg, vim.log.levels.ERROR) + return false + end + + local log_info = compat.uv.fs_stat(logfilename) + if log_info and log_info.size > LARGE then + local warn_msg = + string.format('haskell-tools.nvim log is large (%d MB): %s', log_info.size / (1000 * 1000), logfilename) + vim.notify(warn_msg, vim.log.levels.WARN) + end + + -- Start message for logging + logfile:write(string.format('[START][%s] haskell-tools.nvim logging initiated\n', os.date(log_date_format))) + return true +end + +local opts = HTConfig.tools.log + +local hls_log = HTConfig.hls.logfile + +--- Get the haskell-language-server log file +function HaskellToolsLogInternal.get_hls_logfile() + return hls_log +end + +-- Open the haskell-language-server log file +function HaskellToolsLogInternal.nvim_open_hls_logfile() + vim.cmd('e ' .. HaskellToolsLogInternal.get_hls_logfile()) +end + +local log_levels = vim.deepcopy(vim.log.levels) +for levelstr, levelnr in pairs(log_levels) do + log_levels[levelnr] = levelstr +end + +--- Set the log level +--- @param level (string|integer) The log level +--- @see vim.log.levels +function HaskellToolsLogInternal.set_level(level) + if type(level) == 'string' then + HaskellToolsLogInternal.level = + assert(log_levels[string.upper(level)], string.format('haskell-tools: Invalid log level: %q', level)) + else + assert(log_levels[level], string.format('haskell-tools: Invalid log level: %d', level)) + HaskellToolsLogInternal.level = level + end + vim.lsp.set_log_level(HaskellToolsLogInternal.level) +end + +HaskellToolsLogInternal.set_level(opts.level) + +for level, levelnr in pairs(vim.log.levels) do + HaskellToolsLogInternal[level:lower()] = function(...) + if HaskellToolsLogInternal.level == vim.log.levels.OFF or not open_logfile() then + return false + end + local argc = select('#', ...) + if levelnr < HaskellToolsLogInternal.level then + return false + end + if argc == 0 then + return true + end + local info = debug.getinfo(2, 'Sl') + local fileinfo = string.format('%s:%s', info.short_src, info.currentline) + local parts = { + table.concat({ level, '|', os.date(log_date_format), '|', fileinfo, '|' }, ' '), + } + for i = 1, argc do + local arg = select(i, ...) + if arg == nil then + table.insert(parts, '') + elseif type(arg) == 'string' then + table.insert(parts, arg) + else + table.insert(parts, format_log(arg)) + end + end + logfile:write(table.concat(parts, ' '), '\n') + logfile:flush() + end +end + +HaskellToolsLogInternal.debug { 'Config', HTConfig } + +return HaskellToolsLogInternal diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/definition.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/definition.lua new file mode 100644 index 00000000..6d9fc37e --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/definition.lua @@ -0,0 +1,29 @@ +---@mod haskell-tools.lsp.definition LSP textDocument/definition override + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +---@brief ]] + +local log = require('haskell-tools.log.internal') + +local lsp_definition = {} + +---@param opts table|nil +---@return nil +function lsp_definition.mk_hoogle_fallback_definition_handler(opts) + return function(_, result, ...) + local ht = require('haskell-tools') + if #result > 0 then + local default_handler = vim.lsp.handlers['textDocument/definition'] + return default_handler(_, result, ...) + end + log.debug('Definition not found. Falling back to Hoogle search.') + vim.notify('Definition not found. Falling back to Hoogle search...', vim.log.levels.WARN) + ht.hoogle.hoogle_signature(opts or {}) + end +end + +return lsp_definition diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/eval.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/eval.lua new file mode 100644 index 00000000..b42ea163 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/eval.lua @@ -0,0 +1,57 @@ +---@mod haskell-tools.lsp.eval LSP code snippet evaluation +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- General utility functions that may need to be moded somewhere else +---@brief ]] +local eval = {} + +local LspHelpers = require('haskell-tools.lsp.helpers') + +---@param bufnr number The buffer number +---@return table[] The `evalCommand` lenses, in reverse order +local function get_eval_command_lenses(bufnr, exclude_lines) + exclude_lines = exclude_lines or {} + local eval_cmd_lenses = {} + for _, lens in pairs(vim.lsp.codelens.get(bufnr)) do + if lens.command.command:match('evalCommand') and not vim.tbl_contains(exclude_lines, lens.range.start.line) then + table.insert(eval_cmd_lenses, 1, lens) + end + end + return eval_cmd_lenses +end + +---@param client table LSP client +---@param lens table +---@param bufnr number +---@param exclude_lines number[]|nil -- (optional) `codeLens.range.start.line`s to exclude +---@return nil +local function go(client, bufnr, lens, exclude_lines) + local command = lens.command + client.request_sync('workspace/executeCommand', command, 1000, bufnr) + exclude_lines[#exclude_lines + 1] = lens.range.start.line + local new_lenses = get_eval_command_lenses(bufnr, exclude_lines) + if #new_lenses > 0 then + go(client, bufnr, new_lenses[1], exclude_lines) + end +end + +---@param bufnr number|nil The buffer number +---@return nil +function eval.all(bufnr) + bufnr = bufnr or vim.api.nvim_win_get_buf(0) + local clients = LspHelpers.get_active_ht_clients(bufnr) + if not clients or #clients == 0 then + return + end + local client = clients[1] + local lenses = get_eval_command_lenses(bufnr) + if #lenses > 0 then + go(client, bufnr, lenses[1], {}) + vim.lsp.codelens.refresh() + end +end + +return eval diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/helpers.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/helpers.lua new file mode 100644 index 00000000..822da02d --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/helpers.lua @@ -0,0 +1,98 @@ +---@mod haskell-tools.lsp.helpers + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- LSP helper functions +---@brief ]] + +local Types = require('haskell-tools.types.internal') + +---@class LspHelpers +local LspHelpers = {} + +local compat = require('haskell-tools.compat') +LspHelpers.get_clients = compat.get_clients + +LspHelpers.haskell_client_name = 'haskell-tools.nvim' +LspHelpers.cabal_client_name = 'haskell-tools.nvim (cabal)' + +---@param bufnr number the buffer to get clients for +---@return lsp.Client[] haskell_clients +---@see util.get_clients +function LspHelpers.get_active_haskell_clients(bufnr) + return LspHelpers.get_clients { bufnr = bufnr, name = LspHelpers.haskell_client_name } +end + +---@param bufnr number the buffer to get clients for +---@return lsp.Client[] cabal_clinets +---@see util.get_clients +function LspHelpers.get_active_cabal_clients(bufnr) + return LspHelpers.get_clients { bufnr = bufnr, name = LspHelpers.cabal_client_name } +end + +---@param bufnr number the buffer to get clients for +---@return lsp.Client[] ht_clients The haskell + cabal clients +---@see util.get_clients +---@see util.get_active_haskell_clients +---@see util.get_active_cabal_clients +function LspHelpers.get_active_ht_clients(bufnr) + local clients = {} + vim.list_extend(clients, LspHelpers.get_active_haskell_clients(bufnr)) + vim.list_extend(clients, LspHelpers.get_active_cabal_clients(bufnr)) + return clients +end + +---@return string[] cmd The command to invoke haskell-language-server +LspHelpers.get_hls_cmd = function() + local HTConfig = require('haskell-tools.config.internal') + local cmd = Types.evaluate(HTConfig.hls.cmd) + ---@cast cmd string[] + assert(type(cmd) == 'table', 'haskell-tools: hls.cmd should evaluate to a string[]') + assert(#cmd > 1, 'haskell-tools: hls.cmd evaluates to an empty list.') + return cmd +end + +---Returns nil if the hls version cannot be determined. +---@return number[]|nil hls_version The haskell-language-server version +local function get_hls_version() + local hls_bin = LspHelpers.get_hls_cmd()[1] + if vim.fn.executable(hls_bin) ~= 1 then + return nil + end + local handle = io.popen(hls_bin .. ' --version') + if not handle then + return nil + end + local output, error_msg = handle:read('*a') + handle:close() + if error_msg then + return nil + end + local version_str = output:match('version:%s([^%s]*)%s.*') + if not version_str then + return nil + end + local function parse_version() + local version = {} + for str in string.gmatch(version_str, '([^%.]+)') do + table.insert(version, tonumber(str)) + end + return #version > 1 and version + end + local ok, version = pcall(parse_version) + return ok and version or nil +end + +---@return boolean +LspHelpers.is_hls_version_with_cabal_support = function() + local version = get_hls_version() + -- XXX: If the version cannot be parsed, we assume it supports + --- cabal (in case there is a newer version that we cannot + --- parse the version from). + return version == nil or version[1] > 1 or version[2] >= 9 +end + +return LspHelpers diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/hover.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/hover.lua new file mode 100644 index 00000000..a55c5ac7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/hover.lua @@ -0,0 +1,312 @@ +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- Inspired by rust-tools.nvim's hover_actions +---@brief ]] + +local log = require('haskell-tools.log.internal') +local lsp_util = vim.lsp.util +local HtParser = require('haskell-tools.parser') +local OS = require('haskell-tools.os') +local HtProjectHelpers = require('haskell-tools.project.helpers') + +local hover = {} + +---@class HtHoverState +---@field winnr number|nil The hover window number +---@field commands (fun():nil)[] List of hover actions + +---@type HtHoverState +local _state = { + winnr = nil, + commands = {}, +} + +---@return nil +local function close_hover() + local winnr = _state.winnr + if winnr ~= nil and vim.api.nvim_win_is_valid(winnr) then + vim.api.nvim_win_close(winnr, true) + _state.winnr = nil + _state.commands = {} + end +end + +---Execute the command at the cursor position +---@retrun nil +local function run_command() + local winnr = vim.api.nvim_get_current_win() + local line = vim.api.nvim_win_get_cursor(winnr)[1] + + if line > #_state.commands then + return + end + local action = _state.commands[line] + close_hover() + action() +end + +---@param x string hex +---@return string char +local function hex_to_char(x) + return string.char(tonumber(x, 16)) +end + +---Formats a location in a Haskell file, shortening it to a relative path if possible. +---@param location string The location provided by LSP hover +---@param current_file string The current file path or an empty string +---@return string formatted_location or the original location if the file is not a Haskell file +local function format_location(location, current_file) + -- remove * + -- replace quotes with markdown backticks + -- decode url-encoded characters + local formatted_location = ('%s') + :format(location) + :gsub('%*', '') -- remove * + :gsub('‘', '`') -- markdown backticks + :gsub('’', '`') + :gsub('%%(%x%x)', hex_to_char) -- decode url-encoded characters + local file_location = formatted_location:match('(.*).hs:') + if not file_location then + return formatted_location + end + local is_current_buf = formatted_location:find(current_file, 1, true) == 1 + if is_current_buf then + return formatted_location:sub(#current_file + 2) + end + local path = file_location .. '.hs' + local package_path = HtProjectHelpers.match_package_root(path) + if package_path then + return formatted_location:sub(#package_path + 2) -- trim package path + first '/' + end + local project_path = HtProjectHelpers.match_project_root(path) + if project_path then + formatted_location = formatted_location:sub(#project_path + 2):gsub('/', ':', 1) -- trim project path + first '/' + end + return formatted_location +end + +---@param result table LSP result +---@return string location string +local function mk_location(result) + local range_start = result.range and result.range.start or {} + local line = range_start.line + local character = range_start.character + local uri = result.uri and result.uri:gsub('file://', '') + return line and character and uri and uri .. ':' .. tostring(line + 1) .. ':' .. tostring(character + 1) or '' +end + +---Is the result's start location the same as the params location? +---@param params table LSP location params +---@param result table LSP result +---@return boolean +local function is_same_position(params, result) + local range_start = result.range and result.range.start or {} + return params.textDocument.uri == result.uri + and params.position.line == range_start.line + and params.position.character == range_start.character +end + +---LSP handler for textDocument/hover +---@param result table +---@param ctx table +---@param config table|nil +---@return number|nil bufnr +---@return number|nil winnr +function hover.on_hover(_, result, ctx, config) + local ht = require('haskell-tools') + config = config or {} + config.focus_id = ctx.method + if vim.api.nvim_get_current_buf() ~= ctx.bufnr then + -- Ignore result since buffer changed. + return + end + if not (result and result.contents) then + vim.notify('No information available') + return + end + local markdown_lines = lsp_util.convert_input_to_markdown_lines(result.contents) + if vim.tbl_isempty(markdown_lines) then + log.debug('No hover information available.') + vim.notify('No information available') + return + end + local to_remove = {} + local actions = {} + _state.commands = {} + local func_name = vim.fn.expand('') + ---@cast func_name string + local _, signatures = HtParser.try_get_signatures_from_markdown(func_name, result.contents.value) + for _, signature in pairs(signatures) do + table.insert(actions, 1, string.format('%d. Hoogle search: `%s`', #actions + 1, signature)) + table.insert(_state.commands, function() + log.debug { 'Hover: Hoogle search for signature', signature } + ht.hoogle.hoogle_signature { search_term = signature } + end) + end + local cword = vim.fn.expand('') + table.insert(actions, 1, string.format('%d. Hoogle search: `%s`', #actions + 1, cword)) + table.insert(_state.commands, function() + log.debug { 'Hover: Hoogle search for cword', cword } + ht.hoogle.hoogle_signature { search_term = cword } + end) + local params = ctx.params + local found_location = false + local found_type_definition = false + local found_documentation = false + local found_source = false + for i, value in ipairs(markdown_lines) do + if vim.startswith(value, '[Documentation]') and not found_documentation then + found_documentation = true + table.insert(to_remove, 1, i) + table.insert(actions, 1, string.format('%d. Open documentation in browser', #actions + 1)) + local uri = string.match(value, '%[Documentation%]%((.+)%)') + table.insert(_state.commands, function() + log.debug { 'Hover: Open documentation in browser', uri } + OS.open_browser(uri) + end) + elseif vim.startswith(value, '[Source]') and not found_source then + found_source = true + table.insert(to_remove, 1, i) + table.insert(actions, 1, string.format('%d. View source in browser', #actions + 1)) + local uri = string.match(value, '%[Source%]%((.+)%)') + table.insert(_state.commands, function() + log.debug { 'Hover: View source in browser', uri } + OS.open_browser(uri) + end) + end + local location = string.match(value, '*Defined [ia][nt] (.+)') + local current_file = params.textDocument.uri:gsub('file://', '') + local results, err, definition_results + if location == nil or found_location then + goto SkipDefinition + end + found_location = true + table.insert(to_remove, 1, i) + results, err = vim.lsp.buf_request_sync(0, 'textDocument/definition', params, 1000) + if err or results == nil or #results == 0 then + goto SkipDefinition + end + definition_results = results[1] and results[1].result or {} + if #definition_results > 0 then + local location_suffix = ('%s'):format(format_location(location, current_file)) + local definition_result = definition_results[1] + if not is_same_position(params, definition_result) then + log.debug { 'Hover: definition location', location_suffix } + table.insert(actions, 1, string.format('%d. Go to definition at ' .. location_suffix, #actions + 1)) + table.insert(_state.commands, function() + -- We don't call vim.lsp.buf.definition() because the location params may have changed + local definition_ctx = vim.tbl_extend('force', ctx, { + method = 'textDocument/definition', + }) + log.debug { 'Hover: Go to definition', definition_result } + ---Neovim 0.9 has a bug in the lua doc + ---@diagnostic disable-next-line: param-type-mismatch + vim.lsp.handlers['textDocument/definition'](nil, definition_result, definition_ctx) + end) + end + else -- Display Hoogle search instead + local pkg = location:match('‘(.+)’') + local search_term = pkg and pkg .. '.' .. cword or cword + table.insert(actions, 1, string.format('%d. Hoogle search: `%s`', #actions + 1, search_term)) + table.insert(_state.commands, function() + log.debug { 'Hover: Hoogle search for definition', search_term } + ht.hoogle.hoogle_signature { search_term = search_term } + end) + end + table.insert(actions, 1, string.format('%d. Find references', #actions + 1)) + table.insert(_state.commands, function() + local reference_params = vim.tbl_deep_extend('force', params, { context = { includeDeclaration = true } }) + log.debug { 'Hover: Find references', reference_params } + -- We don't call vim.lsp.buf.references() because the location params may have changed + ---@diagnostic disable-next-line: missing-parameter + vim.lsp.buf_request(0, 'textDocument/references', reference_params) + end) + ::SkipDefinition:: + if found_type_definition then + goto SkipTypeDefinition + end + results, err = vim.lsp.buf_request_sync(0, 'textDocument/typeDefinition', params, 1000) + if err or results == nil or #results == 0 then -- Can go to type definition + goto SkipTypeDefinition + end + found_type_definition = true + local type_definition_results = results[1] and results[1].result or {} + if #type_definition_results == 0 then + goto SkipTypeDefinition + end + local type_definition_result = type_definition_results[1] + local type_def_suffix = format_location(mk_location(type_definition_result), current_file) + if is_same_position(params, result) then + goto SkipTypeDefinition + end + log.debug { 'Hover: type definition location', type_def_suffix } + table.insert(actions, 1, string.format('%d. Go to type definition at ' .. type_def_suffix, #actions + 1)) + table.insert(_state.commands, function() + -- We don't call vim.lsp.buf.typeDefinition() because the location params may have changed + local type_definition_ctx = vim.tbl_extend('force', ctx, { + method = 'textDocument/typeDefinition', + }) + log.debug { 'Hover: Go to type definition', type_definition_result } + ---Neovim 0.9 has a bug in the lua doc + ---@diagnostic disable-next-line: param-type-mismatch + vim.lsp.handlers['textDocument/typeDefinition'](nil, type_definition_result, type_definition_ctx) + end) + ::SkipTypeDefinition:: + end + for _, pos in ipairs(to_remove) do + table.remove(markdown_lines, pos) + end + for _, action in ipairs(actions) do + table.insert(markdown_lines, 1, action) + end + if #actions > 0 then + table.insert(markdown_lines, #actions + 1, '') + table.insert(markdown_lines, #actions + 1, '') + end + local HTConfig = require('haskell-tools.config.internal') + local opts = HTConfig.tools.hover + config = vim.tbl_extend('keep', { + border = opts.border, + stylize_markdown = opts.stylize_markdown, + focusable = true, + focus_id = 'haskell-tools-hover', + close_events = { 'CursorMoved', 'BufHidden', 'InsertCharPre' }, + }, config or {}) + local bufnr, winnr = lsp_util.open_floating_preview(markdown_lines, 'markdown', config) + if opts.stylize_markdown == false then + vim.bo[bufnr].filetype = 'markdown' + end + if opts.auto_focus == true then + vim.api.nvim_set_current_win(winnr) + end + + if _state.winnr ~= nil then + return bufnr, winnr + end + + _state.winnr = winnr + vim.keymap.set('n', '', close_hover, { buffer = bufnr, noremap = true, silent = true }) + vim.api.nvim_buf_attach(bufnr, false, { + on_detach = function() + _state.winnr = nil + end, + }) + + if #_state.commands == 0 then + return bufnr, winnr + end + + vim.api.nvim_set_option_value('cursorline', true, { win = winnr }) + + -- run the command under the cursor + vim.keymap.set('n', '', function() + run_command() + end, { buffer = bufnr, noremap = true, silent = true }) + + return bufnr, winnr +end + +return hover diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/init.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/init.lua new file mode 100644 index 00000000..16461a4c --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/lsp/init.lua @@ -0,0 +1,265 @@ +---@mod haskell-tools.lsp haskell-language-server LSP client tools + +local HTConfig = require('haskell-tools.config.internal') +local log = require('haskell-tools.log.internal') +local Types = require('haskell-tools.types.internal') +local compat = require('haskell-tools.compat') + +---@brief [[ +--- The following commands are available: +--- +--- * `:HlsStart` - Start the LSP client. +--- * `:HlsStop` - Stop the LSP client. +--- * `:HlsRestart` - Restart the LSP client. +--- * `:HlsEvalAll` - Evaluate all code snippets in comments. +---@brief ]] + +---To minimise the risk of this occurring, we attempt to shut down hls cleanly before exiting neovim. +---@param client lsp.Client The LSP client +---@param bufnr number The buffer number +---@return nil +local function ensure_clean_exit_on_quit(client, bufnr) + vim.api.nvim_create_autocmd('VimLeavePre', { + group = vim.api.nvim_create_augroup('haskell-tools-hls-clean-exit-' .. tostring(client.id), { clear = true }), + callback = function() + log.debug('Stopping LSP client...') + vim.lsp.stop_client(client, false) + end, + buffer = bufnr, + }) +end + +---A workaround for #48: +---Some plugins that add LSP client capabilities which are not built-in to neovim +---(like nvim-ufo and nvim-lsp-selection-range) cause error messages, because +---haskell-language-server falsly advertises those server_capabilities for cabal files. +---@param client lsp.Client +---@return nil +local function fix_cabal_client(client) + local LspHelpers = require('haskell-tools.lsp.helpers') + if client.name == LspHelpers.cabal_client_name and client.server_capabilities then + ---@diagnostic disable-next-line: inject-field + client.server_capabilities = vim.tbl_extend('force', client.server_capabilities, { + foldingRangeProvider = false, + selectionRangeProvider = false, + documentHighlightProvider = false, + }) + end +end + +---@class LoadHlsSettingsOpts +---@field settings_file_pattern string|nil File name or pattern to search for. Defaults to 'hls.json' + +log.debug('Setting up the LSP client...') +local hls_opts = HTConfig.hls +local handlers = {} + +local tools_opts = HTConfig.tools +local definition_opts = tools_opts.definition or {} + +if Types.evaluate(definition_opts.hoogle_signature_fallback) then + local lsp_definition = require('haskell-tools.lsp.definition') + log.debug('Wrapping vim.lsp.buf.definition with Hoogle signature fallback.') + handlers['textDocument/definition'] = lsp_definition.mk_hoogle_fallback_definition_handler(definition_opts) +end +local hover_opts = tools_opts.hover +if Types.evaluate(hover_opts.enable) then + local hover = require('haskell-tools.lsp.hover') + handlers['textDocument/hover'] = hover.on_hover +end + +---@class HlsTools +local HlsTools = {} +---Search the project root for a haskell-language-server settings JSON file and load it to a Lua table. +---Falls back to the `hls.default_settings` if no file is found or file cannot be read or decoded. +---@param project_root string|nil The project root +---@param opts LoadHlsSettingsOpts|nil +---@return table hls_settings +---@see https://haskell-language-server.readthedocs.io/en/latest/configuration.html +HlsTools.load_hls_settings = function(project_root, opts) + local default_settings = HTConfig.hls.default_settings + if not project_root then + return default_settings + end + local default_opts = { settings_file_pattern = 'hls.json' } + opts = vim.tbl_deep_extend('force', {}, default_opts, opts or {}) + local results = vim.fn.glob(compat.joinpath(project_root, opts.settings_file_pattern), true, true) + if #results == 0 then + log.info(opts.settings_file_pattern .. ' not found in project root ' .. project_root) + return default_settings + end + local settings_json = results[1] + local OS = require('haskell-tools.os') + local content = OS.read_file(settings_json) + local success, settings = pcall(vim.json.decode, content) + if not success then + local msg = 'Could not decode ' .. settings_json .. '. Falling back to default settings.' + log.warn { msg, error } + vim.notify('haskell-tools.lsp: ' .. msg, vim.log.levels.WARN) + return default_settings + end + log.debug { 'hls settings', settings } + return settings or default_settings +end + +---Start or attach the LSP client. +---Fails silently if the buffer's filetype is not one of the filetypes specified in the config. +---@param bufnr number|nil The buffer number (optional), defaults to the current buffer +---@return number|nil client_id The LSP client ID +HlsTools.start = function(bufnr) + local ht = require('haskell-tools') + bufnr = bufnr or vim.api.nvim_get_current_buf() + local file = vim.api.nvim_buf_get_name(bufnr) + if not file or #file == 0 then + local msg = 'Could not determine the name of buffer ' .. bufnr .. '.' + log.debug('lsp.start: ' .. msg) + return + end + local HtProjectHelpers = require('haskell-tools.project.helpers') + local is_cabal = HtProjectHelpers.is_cabal_file(bufnr) + local project_root = ht.project.root_dir(file) + local hls_settings = type(hls_opts.settings) == 'function' and hls_opts.settings(project_root) or hls_opts.settings + local LspHelpers = require('haskell-tools.lsp.helpers') + local cmd = LspHelpers.get_hls_cmd() + local hls_bin = cmd[1] + if vim.fn.executable(hls_bin) == 0 then + log.warn('Executable ' .. hls_bin .. ' not found.') + end + + local lsp_start_opts = { + name = is_cabal and LspHelpers.cabal_client_name or LspHelpers.haskell_client_name, + cmd = Types.evaluate(cmd), + root_dir = project_root, + filetypes = is_cabal and { 'cabal', 'cabalproject' } or { 'haskell', 'lhaskell' }, + capabilities = hls_opts.capabilities, + handlers = handlers, + settings = hls_settings, + on_attach = function(client_id, buf) + log.debug('LSP attach') + local ok, err = pcall(hls_opts.on_attach, client_id, buf, ht) + if not ok then + ---@cast err string + log.error { 'on_attach failed', err } + vim.notify('haskell-tools.lsp: Error in hls.on_attach: ' .. err) + end + local function buf_refresh_codeLens() + vim.schedule(function() + for _, client in pairs(LspHelpers.get_active_ht_clients(bufnr)) do + if client.server_capabilities.codeLensProvider then + vim.lsp.codelens.refresh() + return + end + end + end) + end + local code_lens_opts = tools_opts.codeLens or {} + if Types.evaluate(code_lens_opts.autoRefresh) then + vim.api.nvim_create_autocmd({ 'InsertLeave', 'BufWritePost', 'TextChanged' }, { + group = vim.api.nvim_create_augroup('haskell-tools-code-lens', {}), + callback = buf_refresh_codeLens, + buffer = buf, + }) + buf_refresh_codeLens() + end + end, + on_init = function(client, _) + ensure_clean_exit_on_quit(client, bufnr) + fix_cabal_client(client) + end, + } + log.debug('LSP start options: lsp_start_opts') + local client_id = vim.lsp.start(lsp_start_opts) + return client_id +end + +---Stop the LSP client. +---@param bufnr number|nil The buffer number (optional), defaults to the current buffer +---@return table[] clients A list of clients that will be stopped +HlsTools.stop = function(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + local LspHelpers = require('haskell-tools.lsp.helpers') + local clients = LspHelpers.get_active_ht_clients(bufnr) + vim.lsp.stop_client(clients) + return clients +end + +---Restart the LSP client. +---Fails silently if the buffer's filetype is not one of the filetypes specified in the config. +---@param bufnr number|nil The buffer number (optional), defaults to the current buffer +---@return number|nil client_id The LSP client ID after restart +HlsTools.restart = function(bufnr) + local lsp = require('haskell-tools').lsp + bufnr = bufnr or vim.api.nvim_get_current_buf() + local clients = lsp.stop(bufnr) + local timer, err_name, err_msg = compat.uv.new_timer() + if not timer then + log.error { 'Could not create timer', err_name, err_msg } + return + end + local attempts_to_live = 50 + local stopped_client_count = 0 + timer:start(200, 100, function() + for _, client in ipairs(clients) do + if client:is_stopped() then + stopped_client_count = stopped_client_count + 1 + vim.schedule(function() + lsp.start(bufnr) + end) + end + end + if stopped_client_count >= #clients then + timer:stop() + attempts_to_live = 0 + elseif attempts_to_live <= 0 then + vim.notify('haslell-tools.lsp: Could not restart all LSP clients.', vim.log.levels.ERROR) + timer:stop() + attempts_to_live = 0 + end + attempts_to_live = attempts_to_live - 1 + end) +end + +---Evaluate all code snippets in comments. +---@param bufnr number|nil Defaults to the current buffer. +---@return nil +HlsTools.buf_eval_all = function(bufnr) + local eval = require('haskell-tools.lsp.eval') + return eval.all(bufnr) +end + +local commands = { + { + 'HlsStart', + function() + HlsTools.start() + end, + {}, + }, + { + 'HlsStop', + function() + HlsTools.stop() + end, + {}, + }, + { + 'HlsRestart', + function() + HlsTools.restart() + end, + {}, + }, + { + 'HlsEvalAll', + function() + HlsTools.buf_eval_all() + end, + {}, + }, +} + +for _, command in ipairs(commands) do + vim.api.nvim_create_user_command(unpack(command)) +end + +return HlsTools diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/os.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/os.lua new file mode 100644 index 00000000..644cc855 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/os.lua @@ -0,0 +1,80 @@ +---@mod haskell-tools.os + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- Functions for interacting with the operating system +---@brief ]] + +local compat = require('haskell-tools.compat') +local log = require('haskell-tools.log.internal') +local uv = compat.uv + +---@class OS +local OS = {} + +---@param url string +---@return nil +OS.open_browser = function(url) + local browser_cmd + if vim.fn.has('unix') == 1 then + if vim.fn.executable('sensible-browser') == 1 then + browser_cmd = 'sensible-browser' + else + browser_cmd = 'xdg-open' + end + end + if vim.fn.has('mac') == 1 then + browser_cmd = 'open' + end + if browser_cmd and vim.fn.executable(browser_cmd) == 1 then + local cmd = { browser_cmd, url } + log.debug { 'Opening browser', cmd } + compat.system(cmd, nil, function(sc) + ---@cast sc vim.SystemCompleted + if sc.code ~= 0 then + log.error { 'Error opening browser', sc.code, sc.stderr } + end + end) + return + end + local msg = 'No executable found to open the browser.' + log.error(msg) + vim.notify('haskell-tools.hoogle: ' .. msg, vim.log.levels.ERROR) +end + +---Read the content of a file +---@param filename string +---@return string|nil content +OS.read_file = function(filename) + local content + local f = io.open(filename, 'r') + if f then + content = f:read('*a') + f:close() + end + return content +end + +---Asynchronously the content of a file +---@param filename string +---@return string|nil content +---@async +OS.read_file_async = function(filename) + local file_fd = uv.fs_open(filename, 'r', 438) + if not file_fd then + return nil + end + local stat = uv.fs_fstat(file_fd) + if not stat then + return nil + end + local data = uv.fs_read(file_fd, stat.size, 0) + uv.fs_close(file_fd) + ---@cast data string? + return data +end + +return OS diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/parser.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/parser.lua new file mode 100644 index 00000000..6f7318c5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/parser.lua @@ -0,0 +1,52 @@ +---@mod haskell-tools.parser + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- Parsing functions +---@brief ]] + +---@class HtParser +local HtParser = {} + +--- Pretty-print a type signature +--- @param sig string|nil The raw signature +--- @return string|nil pp_sig The pretty-printed signature +local function pp_signature(sig) + local pp_sig = sig + and sig + :gsub('\n', ' ') -- join lines + :gsub('forall .*%.%s', '') -- hoogle cannot search for `forall a.` + :gsub('^%s*(.-)%s*$', '%1') -- trim + return pp_sig +end + +--- Get the type signature of the word under the cursor from markdown +--- @param func_name string the name of the function +--- @param docs string Markdown docs +--- @return string|nil function_signature Type signature, or the word under the cursor if none was found +--- @return string[] signatures Other type signatures returned by hls +HtParser.try_get_signatures_from_markdown = function(func_name, docs) + local all_sigs = {} + ---@type string|nil + local raw_func_sig = docs:match('```haskell\n' .. func_name .. '%s::%s([^```]*)') + for code_block in docs:gmatch('```haskell\n([^```]*)\n```') do + ---@type string|nil + local sig = code_block:match('::%s([^```]*)') + local pp_sig = sig and pp_signature(sig) + if sig and not vim.tbl_contains(all_sigs, pp_sig) then + table.insert(all_sigs, pp_sig) + end + end + return raw_func_sig and pp_signature(raw_func_sig), all_sigs +end + +---@param str string +---@return integer indent +HtParser.get_indent = function(str) + return #(str:match('^(%s+)%S') or '') +end + +return HtParser diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/cabal.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/cabal.lua new file mode 100644 index 00000000..af801880 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/cabal.lua @@ -0,0 +1,124 @@ +---@mod haskell-tools.cabal + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- Helper functions related to cabal projects +---@brief ]] + +local Strings = require('haskell-tools.strings') +local HtParser = require('haskell-tools.parser') +local Dap = require('haskell-tools.dap.internal') +local OS = require('haskell-tools.os') +local compat = require('haskell-tools.compat') + +---@class CabalProjectHelper +local CabalProjectHelper = {} + +---@class CabalEntryPointParserData +---@field idx integer +---@field lines string[] +---@field line string +---@field package_dir string + +---@class CabalEntryPointParserState +---@field package_name string +---@field entry_points HsEntryPoint[] +---@field mains string[] +---@field source_dirs string[] +---@field src_dir_indent_pattern string +---@field exe_name string | nil + +---@param data CabalEntryPointParserData +---@param state CabalEntryPointParserState +local function get_entrypoint_from_line(data, state) + local package_dir = data.package_dir + local idx = data.idx + local lines = data.lines + local line = data.line + state.package_name = state.package_name or line:match('^name:%s*(.+)') + local no_indent = HtParser.get_indent(line) == 0 + if no_indent or idx == #lines then + vim.list_extend( + state.entry_points, + Dap.mk_entry_points(state.package_name, state.exe_name, package_dir, state.mains, state.source_dirs) + ) + state.mains = {} + state.source_dirs = {} + state.exe_name = nil + end + state.exe_name = state.exe_name or line:match('^%S+%s+(.+)') or state.package_name + -- main detection + local main = line:match('main%-is:%s+(.+)%.hs') + if not main and lines[idx + 1] and line:match('main%-is:') then + main = (lines[idx + 1]):match('%s+(.+)%.hs') + end + if main then + table.insert(state.mains, main .. '.hs') + end + -- source directory detection + local is_src_dir_end = state.src_dir_indent_pattern and (line == '' or line:match(state.src_dir_indent_pattern)) + if is_src_dir_end then + state.src_dir_indent_pattern = nil + end + if state.src_dir_indent_pattern then + local source_dir = line:match(',%s*(.*)') or line:match('%s+(.*)') + if source_dir then + table.insert(state.source_dirs, source_dir) + end + else + local source_dirs_indent = line:match('(%s*)hs%-source%-dirs:') + if source_dirs_indent then + state.src_dir_indent_pattern = '^' .. ('%s'):rep(#source_dirs_indent) .. '%S+' + end + end +end + +---Parse the DAP entry points from a *.cabal file +---@param package_file string Path to the *.cabal file +---@return HsEntryPoint[] entry_points +---@async +local function parse_package_entrypoints(package_file) + local state = { + entry_points = {}, + mains = {}, + source_dirs = {}, + } + local package_dir = vim.fn.fnamemodify(package_file, ':h') or package_file + local entry_points = {} + local content = OS.read_file_async(package_file) + if not content then + return entry_points + end + local lines = vim.split(content, '\n') or {} + for idx, line in ipairs(lines) do + local is_comment = vim.startswith(Strings.trim(line), '--') + if not is_comment then + ---@type CabalEntryPointParserData + local data = { + package_dir = package_dir, + line = line, + lines = lines, + idx = idx, + } + get_entrypoint_from_line(data, state) + end + end + return state.entry_points +end + +---Parse the DAP entry points from a *.cabal file +---@param package_path string Path to a package directory +---@return HsEntryPoint[] entry_points +---@async +function CabalProjectHelper.parse_package_entrypoints(package_path) + local entry_points = {} + for _, package_file in pairs(vim.fn.glob(compat.joinpath(package_path, '*.cabal'), true, true)) do + vim.list_extend(entry_points, parse_package_entrypoints(package_file)) + end + return entry_points +end + +return CabalProjectHelper diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/helpers.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/helpers.lua new file mode 100644 index 00000000..e2f75b53 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/helpers.lua @@ -0,0 +1,264 @@ +---@mod haskell-tools.project + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- Utility functions for analysing a project. +---@brief ]] + +local log = require('haskell-tools.log.internal') +local Strings = require('haskell-tools.strings') +local OS = require('haskell-tools.os') +local cabal = require('haskell-tools.project.cabal') +local stack = require('haskell-tools.project.stack') +local compat = require('haskell-tools.compat') + +---@class HtProjectHelpers +local HtProjectHelpers = {} + +---@param path string +---@return string stripped_path For zipfile: or tarfile: virtual paths, returns the path to the archive. Other paths are returned unaltered. +--- Taken from nvim-lspconfig +local function strip_archive_subpath(path) + -- Matches regex from zip.vim / tar.vim + path = vim.fn.substitute(path, 'zipfile://\\(.\\{-}\\)::[^\\\\].*$', '\\1', '') or path + path = vim.fn.substitute(path, 'tarfile:\\(.\\{-}\\)::.*$', '\\1', '') or path + return path +end + +---@param path string the file path to search in +---@param ... string Search patterns (can be globs) +---@return string|nil The first file that matches the globs +local function find_file(path, ...) + for _, search_term in ipairs(compat.tbl_flatten { ... }) do + local results = vim.fn.glob(compat.joinpath(path, search_term), true, true) + if #results > 0 then + return results[1] + end + end +end + +---Iterate the path until we find the rootdir. +---@param startpath string The start path +---@return fun(_:any,path:string):(string?,string?) +---@return string startpath +---@return string startpath +local function iterate_parents(startpath) + ---@param _ any Ignored + ---@param path string file path + ---@return string|nil path + ---@return string|nil startpath + local function it(_, path) + local next = vim.fn.fnamemodify(path, ':h') + if not next or vim.fn.isdirectory(next) == 0 or next == path or next == '/nix/store' then + return + end + if compat.uv.fs_realpath(next) then + return next, startpath + end + end + return it, startpath, startpath +end + +---@param startpath string The start path to search upward from +---@param matcher fun(path:string):string|nil +---@return string|nil +local function search_ancestors(startpath, matcher) + if matcher(startpath) then + return startpath + end + local max_iterations = 100 + for path in iterate_parents(startpath) do + max_iterations = max_iterations - 1 + if max_iterations == 0 then + return + end + if not path then + return + end + if matcher(path) then + return path + end + end +end + +---@param ... string Globs to match in the root directory +---@return fun(path:string):(string|nil) +local function root_pattern(...) + local args = compat.tbl_flatten { ... } + local function matcher(path) + return find_file(path, unpack(args)) + end + return function(path) + local startpath = strip_archive_subpath(path) + return search_ancestors(startpath, matcher) + end +end + +---@param path string +---@return string escaped_path +local function escape_glob_wildcards(path) + local escaped_path = path:gsub('([%[%]%?%*])', '\\%1') + return escaped_path +end + +---Get the root of a cabal multi-package project for a path +HtProjectHelpers.match_cabal_multi_project_root = root_pattern('cabal.project') + +---Get the root of a cabal package for a path +HtProjectHelpers.match_cabal_package_root = root_pattern('*.cabal') + +---Get the root of the cabal project for a path +---@param path string File path +HtProjectHelpers.match_cabal_project_root = function(path) + return HtProjectHelpers.match_cabal_multi_project_root(path) or HtProjectHelpers.match_cabal_package_root(path) +end + +---Get the root of the stack project for a path +HtProjectHelpers.match_stack_project_root = root_pattern('stack.yaml') + +---Get the root of the project for a path +HtProjectHelpers.match_project_root = root_pattern('cabal.project', 'stack.yaml') + +---Get the root of the package for a path +HtProjectHelpers.match_package_root = root_pattern('*.cabal', 'package.yaml') + +---Get the directory containing a haskell-language-server hie.yaml +HtProjectHelpers.match_hie_yaml = root_pattern('hie.yaml') + +---Get the package.yaml for a given path +---@param path string +---@return string|nil package_yaml_path +function HtProjectHelpers.get_package_yaml(path) + local match = root_pattern('package.yaml') + local dir = match(path) + return dir and dir .. '/package.yaml' +end + +---Get the *.cabal for a given path +---@param path string +---@return string|nil cabal_file_path +function HtProjectHelpers.get_package_cabal(path) + local match = root_pattern('*.cabal') + local dir = match(path) + if not dir then + return nil + end + dir = escape_glob_wildcards(dir) + for _, pattern in ipairs(vim.fn.glob(compat.joinpath(dir, '*.cabal'), true, true)) do + if pattern then + return pattern + end + end +end + +---Is `path` part of a cabal project? +---@param path string +---@return boolean is_cabal_project +function HtProjectHelpers.is_cabal_project(path) + local get_root = root_pattern('*.cabal', 'cabal.project') + if get_root(path) ~= nil then + log.debug('Detected cabal project.') + return true + end + return false +end + +---Is `path` part of a stack project? +---@param path string +---@return boolean is_stack_project +function HtProjectHelpers.is_stack_project(path) + if HtProjectHelpers.match_stack_project_root(path) ~= nil then + log.debug('Detected stack project.') + return true + end + return false +end + +---Get the package name for a given path +---@param path string +---@return string|nil package_name +function HtProjectHelpers.get_package_name(path) + local package_path = HtProjectHelpers.match_package_root(path) + return package_path and vim.fn.fnamemodify(package_path, ':t') +end + +---Parse the package paths (absolute) from a project file +---@param project_file string project file (cabal.project or stack.yaml) +---@return string[] package_paths +---@async +function HtProjectHelpers.parse_package_paths(project_file) + local package_paths = {} + local content = OS.read_file_async(project_file) + if not content then + return package_paths + end + local project_dir = vim.fn.fnamemodify(project_file, ':h') + local lines = vim.split(content, '\n') or {} + local packages_start = false + for _, line in ipairs(lines) do + if packages_start then + local is_indented = line:match('^%s') ~= nil + local is_yaml_list_elem = line:match('^%-') ~= nil + if not (is_indented or is_yaml_list_elem) then + return package_paths + end + end + if packages_start then + local trimmed = Strings.trim(line) + local pkg_rel_path = trimmed:match('/(.+)') + local pkg_path = compat.joinpath(project_dir, pkg_rel_path) + if vim.fn.isdirectory(pkg_path) == 1 then + package_paths[#package_paths + 1] = pkg_path + end + end + if line:match('packages:') then + packages_start = true + end + end + return package_paths +end + +---Parse the DAP entry points from a *.cabal file +---@param package_path string Path to a package directory +---@return HsEntryPoint[] entry_points +---@async +function HtProjectHelpers.parse_package_entrypoints(package_path) + if HtProjectHelpers.is_cabal_project(package_path) then + return cabal.parse_package_entrypoints(package_path) + end + return stack.parse_package_entrypoints(package_path) +end + +---@param project_root string Project root directory +---@return HsEntryPoint[] +---@async +function HtProjectHelpers.parse_project_entrypoints(project_root) + local entry_points = {} + local project_file = compat.joinpath(project_root, 'cabal.project') + if vim.fn.filereadable(project_file) == 1 then + for _, package_path in pairs(HtProjectHelpers.parse_package_paths(project_file)) do + vim.list_extend(entry_points, cabal.parse_package_entrypoints(package_path)) + end + return entry_points + end + project_file = compat.joinpath(project_root, 'stack.yaml') + if vim.fn.filereadable(project_file) == 1 then + for _, package_path in pairs(HtProjectHelpers.parse_package_paths(project_file)) do + vim.list_extend(entry_points, stack.parse_package_entrypoints(package_path)) + end + return entry_points + end + return cabal.parse_package_entrypoints(project_root) +end + +---@param bufnr number The buffer number +---@return boolean is_cabal_file +HtProjectHelpers.is_cabal_file = function(bufnr) + local filetype = vim.bo[bufnr].filetype + return filetype == 'cabal' or filetype == 'cabalproject' +end + +return HtProjectHelpers diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/init.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/init.lua new file mode 100644 index 00000000..bbcd3f01 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/init.lua @@ -0,0 +1,174 @@ +---@mod haskell-tools.project haskell-tools Project module + +local log = require('haskell-tools.log.internal') +local deps = require('haskell-tools.deps') + +---@brief [[ +--- The following commands are available: +--- +--- * `:HsProjectFile` - Open the project file for the current buffer (cabal.project or stack.yaml). +--- * `:HsPackageYaml` - Open the package.yaml file for the current buffer. +--- * `:HsPackageCabal` - Open the *.cabal file for the current buffer. +---@brief ]] + +---@param callback fun(opts:table):nil +---@param opts table +local function telescope_package_search(callback, opts) + local file = vim.api.nvim_buf_get_name(0) + if vim.fn.filewritable(file) == 0 then + local err_msg = 'Telescope package search: File not found: ' .. file + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return + end + local HtProjectHelpers = require('haskell-tools.project.helpers') + local package_root = HtProjectHelpers.match_package_root(file) + if not package_root then + local err_msg = 'Telescope package search: No package root found for file ' .. file + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return + end + opts = vim.tbl_deep_extend('keep', { + cwd = package_root, + prompt_title = (opts.prompt_title_prefix or 'Package') .. ': ' .. vim.fn.fnamemodify(package_root, ':t'), + }, opts or {}) + callback(opts) +end + +log.debug('Setting up project tools...') + +--- Live grep the current package with telescope. +--- available if nvim-telescope/telescope.nvim is installed. +---@param opts table|nil telescope options +local function telescope_package_grep(opts) + local t = require('telescope.builtin') + opts = vim.tbl_deep_extend('keep', { prompt_title_prefix = 'package live grep' }, opts or {}) + telescope_package_search(t.live_grep, opts) +end + +--- Find file in the current package with telescope +--- available if nvim-telescope/telescope.nvim is installed. +---@param opts table|nil telescope options +local function telescope_package_files(opts) + local t = require('telescope.builtin') + opts = vim.tbl_deep_extend('keep', { prompt_title_prefix = 'package file search' }, opts or {}) + telescope_package_search(t.find_files, opts) +end + +---@class HsProjectTools +local HsProjectTools = {} + +---Get the project's root directory +---@param project_file string The path to a project file +---@return string|nil +HsProjectTools.root_dir = function(project_file) + local HtProjectHelpers = require('haskell-tools.project.helpers') + return HtProjectHelpers.match_cabal_project_root(project_file) + or HtProjectHelpers.match_stack_project_root(project_file) + or HtProjectHelpers.match_package_root(project_file) + or HtProjectHelpers.match_hie_yaml(project_file) +end + +---Open the package.yaml of the package containing the current buffer. +---@return nil +HsProjectTools.open_package_yaml = function() + local HtProjectHelpers = require('haskell-tools.project.helpers') + vim.schedule(function() + local file = vim.api.nvim_buf_get_name(0) + local result = HtProjectHelpers.get_package_yaml(file) + if not result then + local context = '' + if HtProjectHelpers.is_cabal_project(file) then + context = ' cabal project file' + end + local err_msg = 'HsPackageYaml: Cannot find package.yaml file for' .. context .. ': ' .. file + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return + end + vim.cmd('e ' .. result) + end) +end + +---Open the *.cabal file of the package containing the current buffer. +---@return nil +HsProjectTools.open_package_cabal = function() + vim.schedule(function() + local HtProjectHelpers = require('haskell-tools.project.helpers') + local file = vim.api.nvim_buf_get_name(0) + if vim.fn.filewritable(file) ~= 0 and not HtProjectHelpers.is_cabal_project(file) then + vim.notify('HsPackageCabal: Not a cabal project?', vim.log.levels.ERROR) + return + end + local result = HtProjectHelpers.get_package_cabal(file) + if not result then + local err_msg = 'HsPackageCabal: Cannot find *.cabal file for: ' .. file + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return + end + vim.cmd('e ' .. result) + end) +end + +---Open the current buffer's project file (cabal.project or stack.yaml). +---@return nil +HsProjectTools.open_project_file = function() + vim.schedule(function() + local HtProjectHelpers = require('haskell-tools.project.helpers') + local file = vim.api.nvim_buf_get_name(0) + local stack_project_root = HtProjectHelpers.match_stack_project_root(file) + if stack_project_root then + vim.cmd('e ' .. stack_project_root .. '/stack.yaml') + return + end + local cabal_project_root = HtProjectHelpers.match_cabal_multi_project_root(file) + if cabal_project_root then + vim.cmd('e ' .. cabal_project_root .. '/cabal.project') + return + end + local package_cabal = HtProjectHelpers.get_package_cabal(file) + if package_cabal then + vim.cmd('e ' .. package_cabal) + end + local err_msg = 'HsProjectFile: Cannot find project file from: ' .. file + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + end) +end + +HsProjectTools.telescope_package_grep = deps.has('telescope.builtin') and telescope_package_grep or nil + +HsProjectTools.telescope_package_files = deps.has('telescope.builtin') and telescope_package_files or nil + +local commands = { + { + 'HsPackageYaml', + function() + HsProjectTools.open_package_yaml() + end, + {}, + }, + { + 'HsPackageCabal', + function() + HsProjectTools.open_package_cabal() + end, + {}, + }, + { + 'HsProjectFile', + function() + HsProjectTools.open_project_file() + end, + {}, + }, +} + +--- Available if nvim-telescope/telescope.nvim is installed. +for _, command in ipairs(commands) do + vim.api.nvim_create_user_command(unpack(command)) +end + +return HsProjectTools diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/stack.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/stack.lua new file mode 100644 index 00000000..87f76b74 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/project/stack.lua @@ -0,0 +1,159 @@ +---@mod haskell-tools.stack + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- Helper functions related to stack projects +---@brief ]] + +local Strings = require('haskell-tools.strings') +local HtParser = require('haskell-tools.parser') +local Dap = require('haskell-tools.dap.internal') +local OS = require('haskell-tools.os') +local compat = require('haskell-tools.compat') + +---@class StackProjectHelper +local StackProjectHelper = {} + +---@param str string +---@return boolean is_yaml_comment +local function is_yaml_comment(str) + return vim.startswith(Strings.trim(str), '#') +end + +---@class StackEntryPointParserData +---@field idx integer +---@field lines string[] +---@field line string +---@field package_dir string +---@field next_line string|nil + +---@class StackEntryPointParserState +---@field package_name string +---@field entry_points HsEntryPoint[] +---@field mains string[] +---@field source_dirs string[] +---@field parsing_exe_list boolean +---@field parsing_exe boolean +---@field parsing_source_dirs boolean +---@field exe_indent integer | nil +---@field exe_name string | nil + +---@param data StackEntryPointParserData +---@param state StackEntryPointParserState +local function parse_exe_list_line(data, state) + local package_dir = data.package_dir + local idx = data.idx + local lines = data.lines + local line = data.line + local next_line = lines[idx + 1] + local indent = HtParser.get_indent(line) + state.exe_indent = state.exe_indent or indent + state.exe_name = indent == state.exe_indent and line:match('%s*(.+):') or state.exe_name + if state.parsing_exe then + local main = line:match('main:%s+(.+)%.hs') + if not main and line:match('main:') and next_line then + main = next_line:match('%s+(.+)%.hs') + end + if main then + table.insert(state.mains, main .. '.hs') + end + local source_dir = line:match('source%-dirs:%s+(.+)') + if source_dir then + -- Single source directory + state.parsing_source_dirs = false + end + if state.parsing_source_dirs then + source_dir = line:match('%s+%-%s*(.*)') or line:match('%s+(.*)') + end + if source_dir then + table.insert(state.source_dirs, source_dir) + end + local is_source_dir_list = not source_dir and line:match('source%-dirs:') ~= nil + state.parsing_source_dirs = is_source_dir_list + or ( + state.parsing_source_dirs + and next_line + and (next_line:match('^%s+%-') or HtParser.get_indent(next_line) > indent) + ) + end + if state.parsing_exe and (not next_line or HtParser.get_indent(next_line) == 0 or indent <= state.exe_indent) then + vim.list_extend( + state.entry_points, + Dap.mk_entry_points(state.package_name, state.exe_name, package_dir, state.mains, state.source_dirs) + ) + state.mains = {} + state.source_dirs = {} + state.parsing_exe = false + else + state.parsing_exe = indent >= state.exe_indent + end +end + +---@param data StackEntryPointParserData +---@param state StackEntryPointParserState +local function get_entrypoint_from_line(data, state) + local line = data.line + state.package_name = state.package_name or line:match('^name:%s*(.+)') + local indent = HtParser.get_indent(line) + if indent == 0 then + state.parsing_exe_list = false + state.exe_indent = nil + end + if state.parsing_exe_list then + parse_exe_list_line(data, state) + end +end + +---Parse the DAP entry points from a *.cabal file +---@param package_file string Path to the *.cabal file +---@return HsEntryPoint[] entry_points +---@async +local function parse_package_entrypoints(package_file) + local state = { + entry_points = {}, + mains = {}, + source_dirs = {}, + parsing_exe_list = false, + parsing_exe = false, + parsing_source_dirs = false, + } + local package_dir = vim.fn.fnamemodify(package_file, ':h') or package_file + local content = OS.read_file_async(package_file) + if not content then + return state.entry_points + end + local lines = vim.split(content, '\n') or {} + for idx, line in ipairs(lines) do + if not is_yaml_comment(line) then + ---@type StackEntryPointParserData + local data = { + package_dir = package_dir, + line = line, + lines = lines, + idx = idx, + } + get_entrypoint_from_line(data, state) + end + if line:match('^executables:') or line:match('^tests:') then + state.parsing_exe_list = true + end + end + return state.entry_points +end + +---Parse the DAP entry points from a package.yaml file +---@param package_path string Path to a package directory +---@return HsEntryPoint[] entry_points +---@async +function StackProjectHelper.parse_package_entrypoints(package_path) + local entry_points = {} + for _, package_file in pairs(vim.fn.glob(compat.joinpath(package_path, 'package.yaml'), true, true)) do + vim.list_extend(entry_points, parse_package_entrypoints(package_file)) + end + return entry_points +end + +return StackProjectHelper diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/repl/builtin.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/repl/builtin.lua new file mode 100644 index 00000000..07bc93ee --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/repl/builtin.lua @@ -0,0 +1,279 @@ +---@mod haskell-tools.repl.builtin + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +---Utility functions for the ghci repl module. +---@brief ]] + +local log = require('haskell-tools.log.internal') + +---@class BuiltinRepl +---@field bufnr number +---@field job_id number +---@field cmd string[] + +---@type BuiltinRepl | nil +local BuiltinRepl = nil + +local function is_repl_loaded() + return BuiltinRepl ~= nil and vim.api.nvim_buf_is_loaded(BuiltinRepl.bufnr) +end + +---@param callback fun(repl:BuiltinRepl) +---@return nil +local function when_repl_loaded(callback) + if is_repl_loaded() then + local repl = BuiltinRepl + ---@cast repl BuiltinRepl + callback(repl) + end +end + +--- @param cmd string[]? +local function is_new_cmd(cmd) + return BuiltinRepl ~= nil and table.concat(BuiltinRepl.cmd) ~= table.concat(cmd or {}) +end + +---Creates a repl on buffer with id `bufnr`. +---@param bufnr number Buffer to be used. +---@param cmd string[] command to start the repl +---@param opts ReplViewOpts? +---@return nil +local function buf_create_repl(bufnr, cmd, opts) + vim.api.nvim_win_set_buf(0, bufnr) + opts = vim.tbl_extend('force', vim.empty_dict(), opts or {}) + local function delete_repl_buf() + local winid = vim.fn.bufwinid(bufnr) + if winid ~= nil then + vim.api.nvim_win_close(winid, true) + end + vim.api.nvim_buf_delete(bufnr, { force = true }) + end + if opts.delete_buffer_on_exit then + opts.on_exit = function(_, exit_code, _) + log.debug('repl.builtin: exit') + if exit_code ~= 0 then + local msg = 'repl.builtin: non-zero exit code: ' .. exit_code + log.warn(msg) + vim.notify(msg, vim.log.levels.WARN) + end + delete_repl_buf() + end + local repl_log = function(logger) + return function(_, data, name) + logger { 'repl.builtin', data, name } + end + end + opts.on_stdout = repl_log(log.debug) + opts.on_stderr = repl_log(log.warn) + opts.on_stdin = repl_log(log.debug) + end + log.debug { 'repl.builtin: Opening terminal', cmd, opts } + local job_id = vim.fn.termopen(cmd, opts) + if not job_id then + log.error('repl.builtin: Failed to open a terminal') + vim.notify('haskell-tools: Could not start the repl.', vim.log.levels.ERROR) + delete_repl_buf() + return + end + BuiltinRepl = { + bufnr = bufnr, + job_id = job_id, + cmd = cmd, + } + log.debug { 'repl.builtin: Created repl.', BuiltinRepl } +end + +---Create a split +---@param size function|number|nil +local function create_split(size) + size = size and (type(size) == 'function' and size() or size) or vim.o.lines / 3 + local args = vim.empty_dict() or {} + table.insert(args, size) + table.insert(args, 'split') + vim.cmd(table.concat(args, ' ')) +end + +---Create a vertical split +---@param size function|number? +local function create_vsplit(size) + size = size and (type(size) == 'function' and size() or size) or vim.o.columns / 2 + local args = vim.empty_dict() or {} + table.insert(args, size) + table.insert(args, 'vsplit') + vim.cmd(table.concat(args, ' ')) +end + +---Create a new tab +---@param _ any +local function create_tab(_) + vim.cmd('tabnew') +end + +---@param mk_repl_cmd fun(string):(string[]?) +---@param options ReplConfig +---@return ReplHandlerImpl handler +return function(mk_repl_cmd, options) + ---@class ReplHandlerImpl + local ReplHandlerImpl = {} + + ---Create a new repl (or toggle its visibility) + ---@param create_win function|number Function for creating the window or an existing window number + ---@param mk_cmd fun():string[] Function for creating the repl command + ---@param opts ReplViewOpts? + ---@return nil + local function create_or_toggle(create_win, mk_cmd, opts) + local cmd = mk_cmd() + if cmd == nil then + local err_msg = 'haskell-tools.repl.builtin: Could not create a repl command.' + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return + end + if is_new_cmd(cmd) then + log.debug { 'repl.builtin: New command', cmd } + ReplHandlerImpl.quit() + end + if is_repl_loaded() then + local repl = BuiltinRepl + ---@cast repl BuiltinRepl + log.debug('repl.builtin: is loaded') + local winid = vim.fn.bufwinid(repl.bufnr) + if winid ~= -1 then + log.debug('repl.builtin: Hiding window ' .. winid) + vim.api.nvim_win_hide(winid) + else + create_win() + vim.api.nvim_set_current_buf(repl.bufnr) + winid = vim.fn.bufwinid(repl.bufnr) + if winid ~= nil then + log.debug('repl.builtin: Created window ' .. winid) + vim.api.nvim_set_current_win(winid) + end + end + return + end + log.debug('repl.builtin: is not loaded') + opts = opts or vim.empty_dict() + local bufnr = vim.api.nvim_create_buf(true, true) + create_win() + vim.api.nvim_set_current_buf(bufnr) + local winid = vim.fn.bufwinid(bufnr) + if winid ~= nil then + log.debug('repl.builtin: Created window ' .. winid) + vim.api.nvim_set_current_win(winid) + end + buf_create_repl(bufnr, cmd, opts) + end + + ---@type ReplView + local ReplView = { + ---Create a new repl in a horizontal split + ---@param opts ReplViewOpts? + ---@return fun(mk_cmd_fun) create_repl + create_repl_split = function(opts) + return function(mk_cmd) + create_or_toggle(create_split, mk_cmd, opts) + end + end, + + ---Create a new repl in a vertical split + ---@param opts ReplViewOpts? + ---@return fun(function) create_repl + create_repl_vsplit = function(opts) + return function(mk_cmd) + create_or_toggle(create_vsplit, mk_cmd, opts) + end + end, + + ---Create a new repl in a new tab + ---@param opts ReplViewOpts? + ---@return fun(function) create_repl + create_repl_tabnew = function(opts) + return function(mk_cmd) + create_or_toggle(create_tab, mk_cmd, opts) + end + end, + + ---Create a new repl in the current window + ---@param opts ReplViewOpts? + ---@return fun(function) create_repl + create_repl_cur_win = function(opts) + return function(mk_cmd) + create_or_toggle(function(_) end, mk_cmd, opts) + end + end, + } + + log.debug { 'repl.builtin setup', options } + ---@private + ReplHandlerImpl.go_back = options.auto_focus ~= true + + ---@param filepath string path of the file to load into the repl + ---@param _ table? + function ReplHandlerImpl.toggle(filepath, _) + local cur_win = vim.api.nvim_get_current_win() + if filepath and not vim.endswith(filepath, '.hs') then + local err_msg = 'haskell-tools.repl.builtin: Not a Haskell file: ' .. filepath + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return + end + + ---@return string[]? + local function mk_repl_cmd_wrapped() + return mk_repl_cmd(filepath) + end + + local create_or_toggle_callback = options.builtin.create_repl_window(ReplView) + create_or_toggle_callback(mk_repl_cmd_wrapped) + if cur_win ~= -1 and ReplHandlerImpl.go_back then + vim.api.nvim_set_current_win(cur_win) + else + vim.cmd('startinsert') + end + end + + ---Quit the repl + ---@return nil + function ReplHandlerImpl.quit() + when_repl_loaded(function(repl) + log.debug('repl.builtin: sending quit to repl.') + local success, result = pcall(ReplHandlerImpl.send_cmd, ':q') + if not success then + log.warn { 'repl.builtin: Could not send quit command', result } + end + local winid = vim.fn.bufwinid(repl.bufnr) + if winid ~= -1 then + vim.api.nvim_win_close(winid, true) + end + vim.api.nvim_buf_delete(repl.bufnr, { force = true }) + end) + end + + ---Send a command to the repl, followed by + ---@param txt string The text to send + ---@return nil + function ReplHandlerImpl.send_cmd(txt) + when_repl_loaded(function(repl) + local cr = '\13' + local repl_winid = vim.fn.bufwinid(repl.bufnr) + local function repl_set_cursor() + if repl_winid ~= -1 then + vim.api.nvim_win_set_cursor(repl_winid, { vim.api.nvim_buf_line_count(repl.bufnr), 0 }) + end + end + repl_set_cursor() + vim.api.nvim_chan_send(repl.job_id, txt .. cr) + repl_set_cursor() + if not ReplHandlerImpl.go_back and repl_winid ~= nil then + vim.api.nvim_set_current_win(repl_winid) + vim.cmd('startinsert') + end + end) + end + return ReplHandlerImpl +end diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/repl/init.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/repl/init.lua new file mode 100644 index 00000000..c166fe96 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/repl/init.lua @@ -0,0 +1,291 @@ +---@mod haskell-tools.repl haskell-tools GHCi REPL module + +---@bruief [[ +---Tools for interaction with a GHCi REPL +---@bruief ]] + +---@brief [[ +--- The following commands are available: +--- +--- * `:HtReplToggle` - Toggle a GHCi repl. +--- * `:HtReplQuit` - Quit the current repl. +--- * `:HtReplLoad` - Load a Haskell file into the repl. +--- * `:HtReplReload` - Reload the current repl. +---@brief ]] + +local log = require('haskell-tools.log.internal') +local Types = require('haskell-tools.types.internal') +local compat = require('haskell-tools.compat') + +---Extend a repl command for `file`. +---If `file` is `nil`, create a repl the nearest package. +---@param cmd string[] The command to extend +---@param file string|nil An optional project file +---@return string[]|nil +local function extend_repl_cmd(cmd, file) + local HtProjectHelpers = require('haskell-tools.project.helpers') + if file == nil then + file = vim.api.nvim_buf_get_name(0) + log.debug('extend_repl_cmd: No file specified. Using current buffer: ' .. file) + local project_root = HtProjectHelpers.match_project_root(file) + local subpackage = project_root and HtProjectHelpers.get_package_name(file) + if subpackage then + table.insert(cmd, subpackage) + log.debug { 'extend_repl_cmd: Extended cmd with package.', cmd } + return cmd + else + log.debug { 'extend_repl_cmd: No subpackage or no package found.', cmd } + return cmd + end + end + log.debug('extend_repl_cmd: File: ' .. file) + local project_root = HtProjectHelpers.match_project_root(file) + local subpackage = project_root and HtProjectHelpers.get_package_name(file) + if not subpackage then + log.debug { 'extend_repl_cmd: No package found.', cmd } + return cmd + end + if vim.endswith(file, '.hs') then + table.insert(cmd, file) + else + log.debug('extend_repl_cmd: Not a Haskell file.') + table.insert(cmd, subpackage) + end + log.debug { 'extend_repl_cmd', cmd } + return cmd +end + +---Create a cabal repl command for `file`. +---If `file` is `nil`, create a repl the nearest package. +---@param file string|nil +---@return string[]|nil command +local function mk_cabal_repl_cmd(file) + return extend_repl_cmd({ 'cabal', 'repl', '--ghc-option', '-Wwarn' }, file) +end + +---Create a stack repl command for `file`. +---If `file` is `nil`, create a repl the nearest package. +---@param file string|nil +---@return string[]|nil command +local function mk_stack_repl_cmd(file) + return extend_repl_cmd({ 'stack', 'ghci' }, file) +end + +---Create the command to create a repl for a file. +---If `file` is `nil`, create a repl for the nearest package. +---@param file string|nil +---@return table|nil command +local function mk_repl_cmd(file) + local chk_path = file + if not chk_path then + chk_path = vim.api.nvim_buf_get_name(0) + if vim.fn.filewritable(chk_path) == 0 then + local err_msg = 'haskell-tools.repl: File not found. Has it been saved? ' .. chk_path + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return nil + end + end + local HTConfig = require('haskell-tools.config.internal') + local opts = HTConfig.tools.repl + local HtProjectHelpers = require('haskell-tools.project.helpers') + if Types.evaluate(opts.prefer) == 'stack' and HtProjectHelpers.is_stack_project(chk_path) then + return mk_stack_repl_cmd(file) + end + if HtProjectHelpers.is_cabal_project(chk_path) then + return mk_cabal_repl_cmd(file) + end + if HtProjectHelpers.is_stack_project(chk_path) then + return mk_stack_repl_cmd(file) + end + if vim.fn.executable('ghci') == 1 then + local cmd = compat.tbl_flatten { 'ghci', file and { file } or {} } + log.debug { 'mk_repl_cmd', cmd } + return cmd + end + local err_msg = 'haskell-tools.repl: No ghci executable found.' + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return nil +end + +local HTConfig = require('haskell-tools.config.internal') +local opts = HTConfig.tools.repl +---@type ReplHandlerImpl +local handler + +local handler_type = Types.evaluate(opts.handler) +---@cast handler_type ReplHandler +if handler_type == 'toggleterm' then + log.info('handler = toggleterm') + handler = require('haskell-tools.repl.toggleterm')(mk_repl_cmd, opts) +else + if handler_type ~= 'builtin' then + log.warn('Invalid repl handler type. Falling back to builtin') + vim.notify_once( + 'haskell-tools.repl: the handler "' .. handler_type .. '" is invalid. Defaulting to "builtin".', + vim.log.levels.WARN + ) + else + log.info('handler = builtin') + end + handler = require('haskell-tools.repl.builtin')(mk_repl_cmd, opts) +end + +local function handle_reg(cmd, reg) + local data = vim.fn.getreg(reg or '"') + handler.send_cmd(cmd .. ' ' .. data) +end + +local function handle_cword(cmd) + local cword = vim.fn.expand('') + handler.send_cmd(cmd .. ' ' .. cword) +end + +---@param lines string[] +local function repl_send_lines(lines) + if #lines > 1 then + handler.send_cmd(':{') + for _, line in ipairs(lines) do + handler.send_cmd(line) + end + handler.send_cmd(':}') + else + handler.send_cmd(lines[1]) + end +end + +---@class HsReplTools +local HsReplTools = {} + +HsReplTools.mk_repl_cmd = mk_repl_cmd + +---Create the command to create a repl for the current buffer. +---@return table|nil command +HsReplTools.buf_mk_repl_cmd = function() + local file = vim.api.nvim_buf_get_name(0) + return mk_repl_cmd(file) +end + +---Toggle a GHCi REPL +HsReplTools.toggle = handler.toggle + +---Quit the REPL +HsReplTools.quit = handler.quit + +---Can be used to send text objects to the repl. +---@usage [[ +---vim.keymap.set('n', 'ghc', ht.repl.operator, {noremap = true}) +---@usage ]] +---@see operatorfunc +HsReplTools.operator = function() + local old_operator_func = vim.go.operatorfunc + _G.op_func_send_to_repl = function() + local start = vim.api.nvim_buf_get_mark(0, '[') + local finish = vim.api.nvim_buf_get_mark(0, ']') + local text = vim.api.nvim_buf_get_text(0, start[1] - 1, start[2], finish[1], finish[2] + 1, {}) + repl_send_lines(text) + vim.go.operatorfunc = old_operator_func + _G.op_func_formatting = nil + end + vim.go.operatorfunc = 'v:lua.op_func_send_to_repl' + vim.api.nvim_feedkeys('g@', 'n', false) +end + +---Paste from register `reg` to the REPL +---@param reg string|nil register (defaults to '"') +HsReplTools.paste = function(reg) + local data = vim.fn.getreg(reg or '"') + ---@cast data string + if vim.endswith(data, '\n') then + data = data:sub(1, #data - 1) + end + local lines = vim.split(data, '\n') + if #lines <= 1 then + lines = { data } + end + repl_send_lines(lines) +end + +---Query the REPL for the type of register `reg` +---@param reg string|nil register (defaults to '"') +HsReplTools.paste_type = function(reg) + handle_reg(':t', reg) +end + +---Query the REPL for the type of word under the cursor +HsReplTools.cword_type = function() + handle_cword(':t') +end + +---Query the REPL for info on register `reg` +---@param reg string|nil register (defaults to '"') +HsReplTools.paste_info = function(reg) + handle_reg(':i', reg) +end + +---Query the REPL for the type of word under the cursor +HsReplTools.cword_info = function() + handle_cword(':i') +end + +---Load a file into the REPL +---@param filepath string The absolute file path +HsReplTools.load_file = function(filepath) + if vim.fn.filereadable(filepath) == 0 then + local err_msg = 'File: ' .. filepath .. ' does not exist or is not readable.' + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + end + handler.send_cmd(':l ' .. filepath) +end + +---Reload the repl +HsReplTools.reload = function() + handler.send_cmd(':r') +end + +vim.keymap.set('n', 'ghc', HsReplTools.operator, { noremap = true }) + +local commands = { + { + 'HtReplToggle', + ---@param tbl table + function(tbl) + local filepath = tbl.args ~= '' and vim.fn.expand(tbl.args) + ---@cast filepath string + HsReplTools.toggle(filepath) + end, + { nargs = '?' }, + }, + { + 'HtReplLoad', + ---@param tbl table + function(tbl) + local filepath = vim.fn.expand(tbl.args) + ---@cast filepath string + HsReplTools.load_file(filepath) + end, + { nargs = 1 }, + }, + { + 'HtReplQuit', + function() + HsReplTools.quit() + end, + {}, + }, + { + 'HtReplReload', + function() + HsReplTools.reload() + end, + {}, + }, +} + +for _, command in ipairs(commands) do + vim.api.nvim_create_user_command(unpack(command)) +end + +return HsReplTools diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/repl/toggleterm.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/repl/toggleterm.lua new file mode 100644 index 00000000..980f80c8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/repl/toggleterm.lua @@ -0,0 +1,138 @@ +---@mod haskell-tools.repl.toggleterm + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +---Wraps the toggleterm.nvim API to provide a GHIi repl. + +---@brief ]] + +local log = require('haskell-tools.log.internal') +local deps = require('haskell-tools.deps') + +local last_cmd = '' + +---@param cmd string? +local function is_new_cmd(cmd) + return last_cmd ~= (cmd or '') +end + +--- Quote a string +--- @param str string +--- @return string quoted_string +local function quote(str) + return '"' .. str .. '"' +end + +---@param mk_repl_cmd fun(string?):string[]? Function for building the repl that takes an optional file path +---@param opts ReplConfig +---@return ReplHandlerImpl +return function(mk_repl_cmd, opts) + local ReplHandlerImpl = { + ---@private + ---@type Terminal | nil + terminal = nil, + } + opts = opts or vim.empty_dict() + if opts.auto_focus == nil then + ---@private + ReplHandlerImpl.go_back = true + else + ---@private + ReplHandlerImpl.go_back = not opts.auto_focus + end + log.debug('repl.toggleterm setup') + ---@type Terminal + local Terminal = deps.require_toggleterm('toggleterm.terminal').Terminal + + ---@param cmd string The command to execute in the terminal + ---@return Terminal + local function mk_new_terminal(cmd) + local terminal_opts = { + cmd = cmd, + hidden = true, + close_on_exit = false, + on_stdout = function(_, job, data, name) + log.debug { 'Job ' .. job .. ' - stdout', data, name } + end, + on_stderr = function(_, job, data, name) + log.warn { 'Job ' .. job .. ' - stderr', data, name } + end, + on_exit = function(_, job, exit_code, name) + log.debug { 'Job ' .. job .. ' - exit code ' .. exit_code, name } + end, + } + log.debug { 'Creating new terminal', terminal_opts } + return Terminal:new(terminal_opts) + end + + --- @param filepath string? Path of the file to load into the repl + function ReplHandlerImpl.toggle(filepath, _) + opts = opts or vim.empty_dict() + if filepath and not vim.endswith(filepath, '.hs') then + local err_msg = 'haskell-tools.repl.toggleterm: Not a Haskell file: ' .. filepath + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return + end + local cmd = mk_repl_cmd(filepath and quote(filepath)) or {} + if #cmd == 0 then + local err_msg = 'haskell-tools.repl.toggleterm: Could not create a repl command.' + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return + end + local cmd_str = table.concat(cmd, ' ') + if is_new_cmd(cmd_str) then + log.debug { 'repl.toggleterm: New command', cmd_str } + ReplHandlerImpl.quit() + end + local cur_win = vim.api.nvim_get_current_win() + local is_normal_mode = vim.api.nvim_get_mode().mode == 'n' + ReplHandlerImpl.terminal = ReplHandlerImpl.terminal or mk_new_terminal(cmd_str) + local function toggle() + ReplHandlerImpl.terminal:toggle() + end + local success, result = pcall(toggle) + if not success then + log.error { 'repl.toggleterm: toggle failed', result } + end + if cur_win ~= -1 and ReplHandlerImpl.go_back then + vim.api.nvim_set_current_win(cur_win) + if is_normal_mode then + vim.cmd('stopinsert') + end + end + last_cmd = cmd_str + end + + ---Quit the repl + ---@retrun nil + function ReplHandlerImpl.quit() + if ReplHandlerImpl.terminal ~= nil then + log.debug('repl.toggleterm: sending quit to repl.') + local success, result = pcall(ReplHandlerImpl.send_cmd, ':q') + if not success then + log.warn { 'repl.toggleterm: Could not send quit command', result } + end + ReplHandlerImpl.terminal:close() + ReplHandlerImpl.terminal = nil + end + end + + ---Send a command to the repl, followed by + ---@param txt string the command text to send + ---@return nil + function ReplHandlerImpl.send_cmd(txt) + opts = opts or vim.empty_dict() + vim.tbl_extend('force', { + go_back = false, + }, opts) + if ReplHandlerImpl.terminal ~= nil then + ReplHandlerImpl.terminal:send(txt, ReplHandlerImpl.go_back) + end + end + return ReplHandlerImpl +end diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/strings.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/strings.lua new file mode 100644 index 00000000..90c8dff4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/strings.lua @@ -0,0 +1,21 @@ +---@mod haskell-tools.strings + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- Helper functions for working with strings +---@brief ]] + +---@class StringsUtil +local Strings = {} + +---Trim leading and trailing whitespace. +---@param str string +---@return string trimmed +Strings.trim = function(str) + return (str:match('^%s*(.*)') or str):gsub('%s*$', '') +end + +return Strings diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/tags.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/tags.lua new file mode 100644 index 00000000..332c5f77 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/tags.lua @@ -0,0 +1,107 @@ +---@mod haskell-tools.tags haskell-tools fast-tags module + +local HTConfig = require('haskell-tools.config.internal') +local Types = require('haskell-tools.types.internal') +local log = require('haskell-tools.log.internal') +local compat = require('haskell-tools.compat') + +local _state = { + fast_tags_generating = false, + projects = {}, +} + +log.debug('Setting up fast-tags tools') +local config = HTConfig.tools.tags + +---@class GenerateProjectTagsOpts +---@field refresh boolean Whether to refresh the tags if they have already been generated +--- for the project (default: true) + +---@class FastTagsTools +local FastTagsTools = {} + +---Generates tags for the current project +---@param path string|nil File path +---@param opts GenerateProjectTagsOpts|nil Options +FastTagsTools.generate_project_tags = function(path, opts) + path = path or vim.api.nvim_buf_get_name(0) + opts = vim.tbl_extend('force', { refresh = true }, opts or {}) + local HtProjectHelpers = require('haskell-tools.project.helpers') + local project_root = HtProjectHelpers.match_project_root(path) + if not project_root then + log.warn('generate_project_tags: No project root found.') + return + end + if opts.refresh == false and _state.projects[project_root] then + log.debug('Project tags already generated. Skipping.') + return + end + _state.projects[project_root] = true + _state.fast_tags_generating = true + if project_root then + log.debug('Generating project tags for' .. project_root) + compat.system({ 'fast-tags', '-R', project_root }, nil, function(sc) + if sc.code ~= 0 then + log.error { 'Error running fast-tags on project root', sc.code, sc.stderr } + end + ---@cast sc vim.SystemCompleted + _state.fast_tags_generating = false + end) + end +end + +---Generate tags for the package containing `path` +---@param path string|nil File path +FastTagsTools.generate_package_tags = function(path) + path = path or vim.api.nvim_buf_get_name(0) + _state.fast_tags_generating = true + local HtProjectHelpers = require('haskell-tools.project.helpers') + local rel_package_root = HtProjectHelpers.match_package_root(path) + if not rel_package_root then + log.warn('generate_package_tags: No rel_package root found.') + return + end + local package_root = vim.fn.getcwd() .. '/' .. rel_package_root + local project_root = HtProjectHelpers.match_project_root(path) or vim.fn.getcwd() + if not package_root then + log.warn('generate_package_tags: No package root found.') + return + end + if not project_root then + log.warn('generate_package_tags: No project root found.') + return + end + compat.system({ 'fast-tags', '-R', package_root, project_root }, nil, function(sc) + ---@cast sc vim.SystemCompleted + if sc.code ~= 0 then + log.error { 'Error running fast-tags on package', sc.code, sc.stderr } + end + _state.fast_tags_generating = false + end) +end + +if not Types.evaluate(config.enable) then + return +end + +if vim.fn.executable('fast-tags') ~= 1 then + local err_msg = 'haskell-tools: fast-tags fallback configured, but fast-tags executable not found' + log.error(err_msg) + vim.notify(err_msg, vim.log.levels.ERROR) + return +end +local package_events = config.package_events +if #package_events > 0 then + vim.api.nvim_create_autocmd(package_events, { + group = vim.api.nvim_create_augroup('haskell-tools-generate-package-tags', {}), + pattern = { 'haskell', '*.hs' }, + callback = function(meta) + if _state.fast_tags_generating then + return + end + FastTagsTools.generate_package_tags(meta.file) + end, + }) +end + +return FastTagsTools diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/types/internal.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/types/internal.lua new file mode 100644 index 00000000..86265149 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/haskell-tools/types/internal.lua @@ -0,0 +1,32 @@ +---@mod haskell-tools.internal-types + +---@brief [[ + +---WARNING: This is not part of the public API. +---Breaking changes to this module will not be reflected in the semantic versioning of this plugin. + +--- Type definitions +---@brief ]] + +---@class HsEntryPoint +---@field package_dir string +---@field package_name string +---@field exe_name string +---@field main string +---@field source_dir string + +local Types = {} + +---Evaluate a value that may be a function +---or an evaluated value +---@generic T +---@param value (fun():T)|T +---@return T +Types.evaluate = function(value) + if type(value) == 'function' then + return value() + end + return value +end + +return Types diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/telescope/_extensions/ht.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/telescope/_extensions/ht.lua new file mode 100644 index 00000000..0d604652 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/telescope/_extensions/ht.lua @@ -0,0 +1,36 @@ +---@mod haskell-tools.telescope-extension haskell-tools Telescope extension +---@brief [[ +--- If `telescope.nvim` is installed, `haskell-tools` will register the `ht` extenstion +--- with the following commands: +--- +--- * `:Telescope ht package_files` - Search for files within the current (sub)package. +--- * `:Telescope ht package_hsfiles` - Search for Haskell files within the current (sub)package. +--- * `:Telescope ht package_grep` - Live grep within the current (sub)package. +--- * `:Telescope ht package_hsgrep` - Live grep Haskell files within the current (sub)package. +--- * `:Telescope ht hoogle_signature` - Run a Hoogle search for the type signature under the cursor. +--- +--- To load the extension, call +--- +--- > +--- require('telescope').load_extension('ht') +--- < +--- +--- In Lua, you can access the extension with +--- +--- > +--- local telescope = require('telescope') +--- telescope.extensions.ht.package_files() +--- telescope.extensions.ht.package_hsfiles() +--- telescope.extensions.ht.package_grep() +--- telescope.extensions.ht.package_hsgrep() +--- telescope.extensions.ht.hoogle_signature() +--- < +--- +---@brief ]] + +local deps = require('haskell-tools.deps') + +return deps.if_available('telescope', function(telescope) + local ht_extension = require('telescope._extensions.ht.extension') + return telescope.register_extension(ht_extension) +end) diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/telescope/_extensions/ht/extension.lua b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/telescope/_extensions/ht/extension.lua new file mode 100644 index 00000000..10e87453 --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/lua/telescope/_extensions/ht/extension.lua @@ -0,0 +1,39 @@ +local function n_assert(mod) + if mod then + return mod + end + local function notify_err(_) + vim.notify('haskell-tools.nvim has not been set up yet.', vim.log.levels.ERROR) + end + return { + telescope_package_grep = notify_err, + telescope_package_files = notify_err, + hoogle_signature = notify_err, + } +end + +return { + exports = { + -- Live grep within the current (sub)package + package_grep = function(opts) + n_assert(require('haskell-tools').project).telescope_package_grep(opts) + end, + -- Live grep within Haskell files in the current (sub)package + package_hsgrep = function(opts) + opts = vim.tbl_deep_extend('keep', { type_filter = 'haskell' }, opts or {}) + n_assert(require('haskell-tools').project).telescope_package_grep(opts) + end, + -- Find files within the current (sub)package + package_files = function(opts) + n_assert(require('haskell-tools').project).telescope_package_files(opts) + end, + -- Find Haskell files within the current (sub)package + package_hsfiles = function(opts) + opts = vim.tbl_deep_extend('keep', { type_filter = 'haskell' }, opts or {}) + n_assert(require('haskell-tools').project).telescope_package_grep(opts) + end, + hoogle_signature = function(opts) + n_assert(require('haskell-tools').hoogle).hoogle_signature(opts) + end, + }, +} diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/nix-support/propagated-build-inputs b/config/neovim/store/lazy-plugins/haskell-tools.nvim/nix-support/propagated-build-inputs new file mode 100644 index 00000000..b3b8c91f --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/nix-support/propagated-build-inputs @@ -0,0 +1 @@ +/nix/store/mqbhz05llkddfb5wni0m48kw22ixxps4-lua-5.1.5 \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/haskell-tools.nvim/rock_manifest b/config/neovim/store/lazy-plugins/haskell-tools.nvim/rock_manifest new file mode 100644 index 00000000..698d244b --- /dev/null +++ b/config/neovim/store/lazy-plugins/haskell-tools.nvim/rock_manifest @@ -0,0 +1,73 @@ +rock_manifest = { + doc = { + ["haskell-tools.txt"] = "dea283444425fa165997d28c1ac610c8" + }, + ftplugin = { + ["cabal.lua"] = "4f72e96d3601fed58fd4a24e9633172d", + ["cabalproject.lua"] = "4f72e96d3601fed58fd4a24e9633172d", + ["haskell.lua"] = "4f72e96d3601fed58fd4a24e9633172d", + ["lhaskell.lua"] = "4f72e96d3601fed58fd4a24e9633172d" + }, + ["haskell-tools.nvim-3.1.8-1.rockspec"] = "653d0f8f3d848afa2ac2e8d64e470886", + lua = { + ["haskell-tools"] = { + ["compat.lua"] = "1457323a47d791e5ff2393871a112357", + config = { + ["check.lua"] = "4c6c96c3b51efe53b54c8bc4f14d93d6", + ["init.lua"] = "e1f6bc006747eb79f131b527acb83b1f", + ["internal.lua"] = "373f754423a5786afbfcdea048d74bc7" + }, + dap = { + ["init.lua"] = "007e49a6c0a33047d9dc3cc0c76474e7", + ["internal.lua"] = "f30905af3fdff69d13c20682767c44a2" + }, + ["deps.lua"] = "ff7e0f06f003b497c958805a391c1eaf", + ["health.lua"] = "941c948e1667a8a532f2b93f59d0347b", + hoogle = { + ["helpers.lua"] = "e5428112e897aa06b0a76d186679b110", + ["init.lua"] = "5385afa48b78262001e50154b6f02ec3", + ["local.lua"] = "eca412a13b7a33cba0b2935c4024d864", + ["web.lua"] = "4ebe05b9a484b20e88ff51af76f6a88e" + }, + ["init.lua"] = "81946a46a32bdc3066333f6cac348712", + ["internal.lua"] = "f149d8830ae8f0516393ffb31b573765", + log = { + ["init.lua"] = "c8b080a5a9701cb8ae3c526acd868964", + ["internal.lua"] = "ed9e8200543b60724d27d5a991191a3e" + }, + lsp = { + ["definition.lua"] = "04348f8215b227a5da688b92e1cfe23b", + ["eval.lua"] = "69614508eab450ad811c9cbdd46130a9", + ["helpers.lua"] = "010ab78c87db24c8142c29ac35c218e8", + ["hover.lua"] = "9e03896e1877b06c98f11ce1b643343c", + ["init.lua"] = "44c4d7be0311e2d746e00308710345e9" + }, + ["os.lua"] = "110cd7ec57e6587360092f8f78025783", + ["parser.lua"] = "402dc6ebad5cffbe6700e774ad64724a", + project = { + ["cabal.lua"] = "ef4d57f352f6f15cb21bc3dc72e37872", + ["helpers.lua"] = "c167cfdaca3d44feb95eb921470dbaf0", + ["init.lua"] = "eebdb000f25db6280299ccc89ee64fc6", + ["stack.lua"] = "71c00101f8004f9c905a9ddb33dc6889" + }, + repl = { + ["builtin.lua"] = "c1b6210d95f533ec06bb3857f8d1dc7e", + ["init.lua"] = "3529ee48e2a8e39f962fd6d351ff6ee3", + ["toggleterm.lua"] = "f62892e49e2f5b25394f0c254a9cebb6" + }, + ["strings.lua"] = "6429d6757a6c08856f425b72d7d8362c", + ["tags.lua"] = "b82dd1a468edfc13323b2f290f73dc24", + types = { + ["internal.lua"] = "a078237ae853c7f19c396a7d5001fa28" + } + }, + telescope = { + _extensions = { + ht = { + ["extension.lua"] = "040c10b48645d3230bba5952281ea858" + }, + ["ht.lua"] = "ebd1a0ec34353b6b42b5e6012ff69dd9" + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/intellitab.nvim/.gitignore b/config/neovim/store/lazy-plugins/intellitab.nvim/.gitignore new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/config/neovim/store/lazy-plugins/intellitab.nvim/.gitignore @@ -0,0 +1 @@ + diff --git a/config/neovim/store/lazy-plugins/intellitab.nvim/LICENSE b/config/neovim/store/lazy-plugins/intellitab.nvim/LICENSE new file mode 100644 index 00000000..becab348 --- /dev/null +++ b/config/neovim/store/lazy-plugins/intellitab.nvim/LICENSE @@ -0,0 +1,19 @@ +Copyright 2021 Pedro Alves + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/intellitab.nvim/README.md b/config/neovim/store/lazy-plugins/intellitab.nvim/README.md new file mode 100644 index 00000000..bbe733b3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/intellitab.nvim/README.md @@ -0,0 +1,48 @@ +# IntelliTab +IntelliTab is a small neovim plugin that fixes an annoying gripe I've had with +vim's tab completion implementation for ages: pressing tab on a blank line only +indents by a single space, instead of automatically indenting to your specified +location. + +With IntelliTab, pressing Tab works like it does on editors such as VSCode, by +indenting to the same place `smartindent` would have indented it to if it were +a new line. + +## Installation +Install using your favourite package manager: + +```vim +Plug 'pta2002/intellitab.nvim' +``` + +Now, just rebind ``: + +```vim +inoremap lua require("intellitab").indent() +``` + +That's it! + +### If you are using CoC +CoC wants to have its own binding for Tab, which means it won't be compatible with intellitab by default. A solution to this is to bind Tab to this instead: + +```vim +inoremap + \ pumvisible() ? "\" : + \ check_back_space() ? lua require("intellitab").indent() : + \ coc#refresh() +inoremap pumvisible() ? "\" : "\" +``` + +### If you are using nvim-cmp +On the `nvim-cmp` setup, add this binding for tab: + +```lua +[''] = function(fallback) + if cmp.visible() then + cmp.select_next_item() -- or whatever else you want nvim-cmp to do when you press tab + else + require("intellitab").indent() + end +end +``` diff --git a/config/neovim/store/lazy-plugins/intellitab.nvim/lua/intellitab/init.lua b/config/neovim/store/lazy-plugins/intellitab.nvim/lua/intellitab/init.lua new file mode 100644 index 00000000..b27a09e8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/intellitab.nvim/lua/intellitab/init.lua @@ -0,0 +1,68 @@ +local v = vim.api + +local treesitter = require("nvim-treesitter") + +local function get_line_indent(line, sw) + local indent = 0; + + for c in line:gmatch("%s") do + if c == "\t" then + indent = indent + sw + else + indent = indent + 1 + end + end + + return indent +end + +local function indent() + local cursor = v.nvim_win_get_cursor(0) + local line = v.nvim_buf_get_lines(0, cursor[1] - 1, cursor[1], false)[1] + + local indentexpr = v.nvim_buf_get_option(0, "indentexpr") + local expand = v.nvim_buf_get_option(0, "expandtab") + local shiftwidth = v.nvim_eval("shiftwidth()") + local tab_char = v.nvim_replace_termcodes("", true, true, true) + local indent_goal + + if treesitter ~= nil and treesitter.get_indent ~= nil then + indent_goal = treesitter.get_indent(line) + end + + if indent_goal == nil or indent_goal < 0 then + if indentexpr ~= "" then + indent_goal = v.nvim_eval(indentexpr) + elseif v.nvim_buf_get_option(0, "cindent") then + indent_goal = v.nvim_call_function("cindent", {cursor[1]}) + else + indent_goal = v.nvim_call_function("indent", {cursor[1]}) + end + end + + if indent_goal == -1 and cursor[1] ~= 1 then + local prev_line = v.nvim_buf_get_lines(0, cursor[1] - 2, cursor[1] - 1, false)[1] + indent_goal = get_line_indent(prev_line, shiftwidth) + end + + -- Reset the cursor, since the functions are free to move it + v.nvim_win_set_cursor(0, cursor) + + if cursor[2] == 0 and line == "" and indent_goal ~= nil and indent_goal > 0 then + local i = 0 + while i < indent_goal do + if expand then + v.nvim_feedkeys(" ", 'n', true) + i = i + 1 + else + v.nvim_feedkeys(tab_char, 'n', true) + i = i + shiftwidth + end + end + print(i, indent_goal) + else + v.nvim_feedkeys(tab_char, 'n', true) + end +end + +return {indent = indent} diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/.github/FUNDING.yml b/config/neovim/store/lazy-plugins/lazygit.nvim/.github/FUNDING.yml new file mode 100644 index 00000000..b60b8c7b --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [kdheepak] +custom: ['https://donate.stripe.com/8wM9E7bBO9ZsbGUdQR'] diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/.github/ISSUE_TEMPLATE/bug_report.md b/config/neovim/store/lazy-plugins/lazygit.nvim/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..fc1a92ae --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Description** + + + +**To Reproduce** + +Here are the steps to reproduce the behavior using [MINRC](../blob/master/tests/MINRC): + + + +**Expected behavior** + +What is expected to happen: + +**Screenshots** + + + +**Desktop (please complete the following information):** + + - nvim --version: diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/.github/ISSUE_TEMPLATE/feature_request.md b/config/neovim/store/lazy-plugins/lazygit.nvim/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..f25a78ba --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** + + + +**Describe the solution you'd like** + + + +**Additional context** + + diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/LICENSE b/config/neovim/store/lazy-plugins/lazygit.nvim/LICENSE new file mode 100644 index 00000000..15af0276 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2020 Dheepak Krishnamurthy + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/README.md b/config/neovim/store/lazy-plugins/lazygit.nvim/README.md new file mode 100644 index 00000000..bd774c1a --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/README.md @@ -0,0 +1,274 @@ +# lazygit.nvim + +Plugin for calling [lazygit](https://github.com/jesseduffield/lazygit) from within neovim. + +![](https://user-images.githubusercontent.com/1813121/87866391-79fcfe00-c93e-11ea-94a9-204947de1b39.gif) + +See [akinsho/nvim-toggleterm](https://github.com/akinsho/nvim-toggleterm.lua#custom-terminals) or [voldikss/vim-floaterm](https://github.com/voldikss/vim-floaterm) as an alternative to this package. + +### Install + +Install using [`vim-plug`](https://github.com/junegunn/vim-plug): + +```vim +" nvim v0.7.2 +Plug 'kdheepak/lazygit.nvim' +``` + +Install using [`packer.nvim`](https://github.com/wbthomason/packer.nvim): + +```lua +-- nvim v0.7.2 +use({ + "kdheepak/lazygit.nvim", + -- optional for floating window border decoration + requires = { + "nvim-lua/plenary.nvim", + }, +}) +``` + +Install using [`lazy.nvim`](https://github.com/folke/lazy.nvim): + +```lua +-- nvim v0.8.0 +return { + "kdheepak/lazygit.nvim", + cmd = { + "LazyGit", + "LazyGitConfig", + "LazyGitCurrentFile", + "LazyGitFilter", + "LazyGitFilterCurrentFile", + }, + -- optional for floating window border decoration + dependencies = { + "nvim-lua/plenary.nvim", + }, + -- setting the keybinding for LazyGit with 'keys' is recommended in + -- order to load the plugin when the command is run for the first time + keys = { + { "lg", "LazyGit", desc = "LazyGit" } + } +} +``` + +Feel free to use any plugin manager. +Just remember that if you are not using the latest neovim release, you will need to use [the `nvim-v0.4.3` branch](https://github.com/kdheepak/lazygit.vim/tree/nvim-v0.4.3). +Integration with `nvr` works better on the `main` branch. + +You can check what version of `neovim` you have: + +```bash +nvim --version +``` + +### Usage + +The following are configuration options and their defaults. + +```vim +let g:lazygit_floating_window_winblend = 0 " transparency of floating window +let g:lazygit_floating_window_scaling_factor = 0.9 " scaling factor for floating window +let g:lazygit_floating_window_border_chars = ['╭','─', '╮', '│', '╯','─', '╰', '│'] " customize lazygit popup window border characters +let g:lazygit_floating_window_use_plenary = 0 " use plenary.nvim to manage floating window if available +let g:lazygit_use_neovim_remote = 1 " fallback to 0 if neovim-remote is not installed + +let g:lazygit_use_custom_config_file_path = 0 " config file path is evaluated if this value is 1 +let g:lazygit_config_file_path = '' " custom config file path +" OR +let g:lazygit_config_file_path = [] " list of custom config file paths +``` + +```lua +vim.g.lazygit_floating_window_winblend = 0 -- transparency of floating window +vim.g.lazygit_floating_window_scaling_factor = 0.9 -- scaling factor for floating window +vim.g.lazygit_floating_window_border_chars = {'╭','─', '╮', '│', '╯','─', '╰', '│'} -- customize lazygit popup window border characters +vim.g.lazygit_floating_window_use_plenary = 0 -- use plenary.nvim to manage floating window if available +vim.g.lazygit_use_neovim_remote = 1 -- fallback to 0 if neovim-remote is not installed + +vim.g.lazygit_use_custom_config_file_path = 0 -- config file path is evaluated if this value is 1 +vim.g.lazygit_config_file_path = '' -- custom config file path +-- OR +vim.g.lazygit_config_file_path = {} -- table of custom config file paths +``` + +Call `:LazyGit` to start a floating window with `lazygit` in the current working directory. +And set up a mapping to call `:LazyGit`: + +```vim +" setup mapping to call :LazyGit +nnoremap gg :LazyGit +``` + +Call `:LazyGitCurrentFile` to start a floating window with `lazygit` in the project root of the current file. + +Open the configuration file for `lazygit` directly from vim. + +```vim +:LazyGitConfig +``` + +If the file does not exist it'll load the defaults for you. + +![](https://user-images.githubusercontent.com/1813121/78830902-46721580-79d8-11ea-8809-291b346b6c42.gif) + +Open project commits with `lazygit` directly from vim in floating window. + +```vim +:LazyGitFilter +``` + +Open buffer commits with `lazygit` directly from vim in floating window. + +```vim +:LazyGitFilterCurrentFile +``` + +**Using neovim-remote** + +If you have [neovim-remote](https://github.com/mhinz/neovim-remote) and have configured to use it in neovim, it'll launch the commit editor inside your neovim instance when you use `C` inside `lazygit`. + +1. `pip install neovim-remote` + +2. Add the following to your `~/.bashrc`: + +```bash +if [ -n "$NVIM_LISTEN_ADDRESS" ]; then + alias nvim=nvr -cc split --remote-wait +'set bufhidden=wipe' +fi +``` + +3. Set `EDITOR` environment variable in `~/.bashrc`: + +```bash +if [ -n "$NVIM_LISTEN_ADDRESS" ]; then + export VISUAL="nvr -cc split --remote-wait +'set bufhidden=wipe'" + export EDITOR="nvr -cc split --remote-wait +'set bufhidden=wipe'" +else + export VISUAL="nvim" + export EDITOR="nvim" +fi +``` + +4. Add the following to `~/.vimrc`: + +```vim +if has('nvim') && executable('nvr') + let $GIT_EDITOR = "nvr -cc split --remote-wait +'set bufhidden=wipe'" +endif +``` + +If you have `neovim-remote` and don't want `lazygit.nvim` to use it, you can disable it using the following configuration option: + +```vim +let g:lazygit_use_neovim_remote = 0 +``` + +**Using nvim --listen and nvim --server to edit files in same process** + +You can use vanilla nvim server to edit files in the same nvim instance when you use `e` inside `lazygit`. + +1. You have to start nvim with the `--listen` parameter. An easy way to ensure this is to use an alias: +```bash +# ~/.bashrc +alias vim='nvim --listen /tmp/nvim-server.pipe' +``` + +2. You have to modify lazygit to attempt connecting to existing nvim instance on edit: +```yml +# ~/.config/jesseduffield/lazygit/config.yml +os: + editCommand: 'nvim' + editCommandTemplate: '{{editor}} --server /tmp/nvim-server.pipe --remote-tab "$(pwd)/{{filename}}"' +``` + +### Telescope Plugin + +The Telescope plugin is used to track all git repository visited in one nvim session. + +![lazygittelplugin](https://user-images.githubusercontent.com/10464534/156933468-c89abee4-6afb-457c-8b02-55b67913aef2.png) +(background image is not included :smirk:) + +**Why a telescope Plugin** ? + +Assuming you have one or more submodule(s) in your project and you want to commit changes in both the submodule(s) +and the main repo. +Though switching between submodules and main repo is not straight forward. +A solution at first could be: + +1. open a file inside the submodule +2. open lazygit +3. do commit +4. then open a file in the main repo +5. open lazygit +6. do commit + +That is really annoying. +Instead, you can open it with telescope. + +**How to use** + +Install using [`packer.nvim`](https://github.com/wbthomason/packer.nvim): + +```lua +-- nvim v0.7.2 +use({ + "kdheepak/lazygit.nvim", + requires = { + "nvim-telescope/telescope.nvim", + "nvim-lua/plenary.nvim", + }, + config = function() + require("telescope").load_extension("lazygit") + end, +}) +``` + +Install using [`lazy.nvim`](https://github.com/folke/lazy.nvim): + +```lua +-- nvim v0.8.0 +require("lazy").setup({ + { + "kdheepak/lazygit.nvim", + dependencies = { + "nvim-telescope/telescope.nvim", + "nvim-lua/plenary.nvim" + }, + config = function() + require("telescope").load_extension("lazygit") + end, + }, +}) +``` + +Lazy loading `lazygit.nvim` for telescope functionality is not supported. Open an issue if you wish to have this feature. + +If you are not using Packer, to load the telescope extension, you have to add this line to your configuration: + +```lua +require('telescope').load_extension('lazygit') +``` + +By default the paths of each repo is stored only when lazygit is triggered. +Though, this may not be convenient, so it possible to do something like this: + +```vim +autocmd BufEnter * :lua require('lazygit.utils').project_root_dir() +``` + +That makes sure that any opened buffer which is contained in a git repo will be tracked. + +Once you have loaded the extension, you can invoke the plugin using: + +```lua +lua require("telescope").extensions.lazygit.lazygit() +``` + +### Highlighting groups + +| Highlight Group | Default Group | Description | +| ------------------| --------------| -----------------------------------------| +| **LazyGitFloat** | **_Normal_** | Float terminal foreground and background | +| **LazyGitBorder** | **_Normal_** | Float terminal border | diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/ftplugin/gitcommit.vim b/config/neovim/store/lazy-plugins/lazygit.nvim/ftplugin/gitcommit.vim new file mode 100644 index 00000000..4fc1f6a8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/ftplugin/gitcommit.vim @@ -0,0 +1,7 @@ +if exists("g:lazygit_opened") && g:lazygit_opened && g:lazygit_use_neovim_remote && executable("nvr") + augroup lazygit_neovim_remote + autocmd! + autocmd BufUnload :lua local root = require('lazygit').project_root_dir(); vim.schedule(function() require('lazygit').lazygit(root) end) + autocmd BufUnload :let g:lazygit_opened=0 + augroup END +end diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/lua/lazygit.lua b/config/neovim/store/lazy-plugins/lazygit.nvim/lua/lazygit.lua new file mode 100644 index 00000000..a5662b36 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/lua/lazygit.lua @@ -0,0 +1,170 @@ +local open_floating_window = require("lazygit.window").open_floating_window +local project_root_dir = require("lazygit.utils").project_root_dir +local get_root = require("lazygit.utils").get_root +local is_lazygit_available = require("lazygit.utils").is_lazygit_available +local is_symlink = require("lazygit.utils").is_symlink +local open_or_create_config = require("lazygit.utils").open_or_create_config + +local fn = vim.fn + +LAZYGIT_BUFFER = nil +LAZYGIT_LOADED = false +vim.g.lazygit_opened = 0 +local prev_win = -1 +local win = -1 +local buffer = -1 + +--- on_exit callback function to delete the open buffer when lazygit exits in a neovim terminal +local function on_exit(job_id, code, event) + if code ~= 0 then + return + end + + LAZYGIT_BUFFER = nil + LAZYGIT_LOADED = false + vim.g.lazygit_opened = 0 + vim.cmd("silent! :checktime") + + if vim.api.nvim_win_is_valid(prev_win) then + vim.api.nvim_win_close(win, true) + vim.api.nvim_set_current_win(prev_win) + prev_win = -1 + if vim.api.nvim_buf_is_valid(buffer) and vim.api.nvim_buf_is_loaded(buffer) then + vim.api.nvim_buf_delete(buffer, { force = true }) + end + buffer = -1 + win = -1 + end +end + +--- Call lazygit +local function exec_lazygit_command(cmd) + if LAZYGIT_LOADED == false then + -- ensure that the buffer is closed on exit + vim.g.lazygit_opened = 1 + vim.fn.termopen(cmd, { on_exit = on_exit }) + end + vim.cmd("startinsert") +end + +local function lazygitdefaultconfigpath() + -- lazygit -cd gives only the config dir, not the config file, so concat config.yml + return fn.substitute(fn.system("lazygit -cd"), "\n", "", "") .. "/config.yml" +end + +local function lazygitgetconfigpath() + local default_config_path = lazygitdefaultconfigpath() + -- if vim.g.lazygit_config_file_path is a table, check if all config files exist + if vim.g.lazygit_config_file_path then + if type(vim.g.lazygit_config_file_path) == "table" then + for _, config_file in ipairs(vim.g.lazygit_config_file_path) do + if fn.empty(fn.glob(config_file)) == 1 then + print("lazygit: custom config file path: '" .. config_file .. "' could not be found. Returning default config") + return default_config_path + end + end + return vim.g.lazygit_config_file_path + elseif fn.empty(fn.glob(vim.g.lazygit_config_file_path)) == 0 then + return vim.g.lazygit_config_file_path + else + print("lazygit: custom config file path: '" .. vim.g.lazygit_config_file_path .. "' could not be found. Returning default config") + return default_config_path + end + else + print("lazygit: custom config file path is not set, option: 'lazygit_config_file_path' is missing") + -- any issue with the config file we fallback to the default config file path + return default_config_path + end +end + +--- :LazyGit entry point +local function lazygit(path) + if is_lazygit_available() ~= true then + print("Please install lazygit. Check documentation for more information") + return + end + + prev_win = vim.api.nvim_get_current_win() + + win, buffer = open_floating_window() + + local cmd = "lazygit" + + -- set path to the root path + _ = project_root_dir() + + if vim.g.lazygit_use_custom_config_file_path == 1 then + local config_path = lazygitgetconfigpath() + if type(config_path) == "table" then + config_path = table.concat(config_path, ",") + end + cmd = cmd .. " -ucf '" .. config_path .. "'" -- quote config_path to avoid whitespace errors + end + + if path == nil then + if is_symlink() then + path = project_root_dir() + end + else + if fn.isdirectory(path) then + cmd = cmd .. " -p " .. path + end + end + + exec_lazygit_command(cmd) +end + +--- :LazyGitCurrentFile entry point +local function lazygitcurrentfile() + local current_dir = vim.fn.expand("%:p:h") + local git_root = get_root(current_dir) + lazygit(git_root) +end + +--- :LazyGitFilter entry point +local function lazygitfilter(path) + if is_lazygit_available() ~= true then + print("Please install lazygit. Check documentation for more information") + return + end + if path == nil then + path = project_root_dir() + end + prev_win = vim.api.nvim_get_current_win() + win, buffer = open_floating_window() + local cmd = "lazygit " .. "-f " .. path + exec_lazygit_command(cmd) +end + +--- :LazyGitFilterCurrentFile entry point +local function lazygitfiltercurrentfile() + local current_file = vim.fn.expand("%") + lazygitfilter(current_file) +end + +--- :LazyGitConfig entry point +local function lazygitconfig() + local config_file = lazygitgetconfigpath() + + if type(config_file) == "table" then + vim.ui.select( + config_file, + { prompt = "select config file to edit" }, + function (path) + open_or_create_config(path) + end + ) + else + open_or_create_config(config_file) + end + +end + +return { + lazygit = lazygit, + lazygitcurrentfile = lazygitcurrentfile, + lazygitfilter = lazygitfilter, + lazygitfiltercurrentfile = lazygitfiltercurrentfile, + lazygitconfig = lazygitconfig, + project_root_dir = project_root_dir, +} diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/lua/lazygit/utils.lua b/config/neovim/store/lazy-plugins/lazygit.nvim/lua/lazygit/utils.lua new file mode 100644 index 00000000..08b34153 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/lua/lazygit/utils.lua @@ -0,0 +1,127 @@ +local fn = vim.fn + +-- store all git repositories visited in this session +local lazygit_visited_git_repos = {} + +-- TODO:check if the repo isa git repo +local function append_git_repo_path(repo_path) + if repo_path == nil or not fn.isdirectory(repo_path) then + return + end + + for _, path in ipairs(lazygit_visited_git_repos) do + if path == repo_path then + return + end + end + + table.insert(lazygit_visited_git_repos, tostring(repo_path)) +end + + +--- Strip leading and lagging whitespace +local function trim(str) + return str:gsub('^%s+', ''):gsub('%s+$', '') +end + + +local function get_root(cwd) + local status, job = pcall(require, 'plenary.job') + if not status then + return fn.system('git rev-parse --show-toplevel') + end + + local gitroot_job = job:new({ + 'git', + 'rev-parse', + '--show-toplevel', + cwd=cwd + }) + + local path, code = gitroot_job:sync() + if (code ~= 0) then + return nil + end + + return table.concat(path, "") +end + +--- Get project_root_dir for git repository +local function project_root_dir() + -- always use bash on Unix based systems. + local oldshell = vim.o.shell + if vim.fn.has('win32') == 0 then + vim.o.shell = 'bash' + end + + local cwd = vim.loop.cwd() + local root = get_root(cwd) + if root == nil then + vim.o.shell = oldshell + return nil + end + + local cmd = string.format('cd "%s" && git rev-parse --show-toplevel', fn.fnamemodify(fn.resolve(fn.expand('%:p')), ':h'), root) + -- try symlinked file location instead + local gitdir = fn.system(cmd) + local isgitdir = fn.matchstr(gitdir, '^fatal:.*') == '' + + if isgitdir then + vim.o.shell = oldshell + append_git_repo_path(gitdir) + return trim(gitdir) + end + + -- revert to old shell + vim.o.shell = oldshell + + local repo_path = fn.getcwd(0, 0) + append_git_repo_path(repo_path) + + -- just return current working directory + return repo_path +end + +--- Check if lazygit is available +local function is_lazygit_available() + return fn.executable('lazygit') == 1 +end + +local function is_symlink() + local resolved = fn.resolve(fn.expand('%:p')) + return resolved ~= fn.expand('%:p') +end + +local function open_or_create_config(path) + if fn.empty(fn.glob(path)) == 1 then + -- file does not exist + -- check if user wants to create it + local answer = fn.confirm( + "File " + .. path + .. " does not exist.\nDo you want to create the file and populate it with the default configuration?", + "&Yes\n&No" + ) + if answer == 2 then + return nil + end + if fn.isdirectory(fn.fnamemodify(path, ":h")) == false then + -- directory does not exist + fn.mkdir(fn.fnamemodify(path, ":h"), "p") + end + vim.cmd("edit " .. path) + vim.cmd([[execute "silent! 0read !lazygit -c"]]) + vim.cmd([[execute "normal 1G"]]) + else + vim.cmd("edit " .. path) + end +end + +return { + get_root = get_root, + project_root_dir = project_root_dir, + lazygit_visited_git_repos = lazygit_visited_git_repos, + is_lazygit_available = is_lazygit_available, + is_symlink = is_symlink, + open_or_create_config = open_or_create_config, +} diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/lua/lazygit/window.lua b/config/neovim/store/lazy-plugins/lazygit.nvim/lua/lazygit/window.lua new file mode 100644 index 00000000..86c02c16 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/lua/lazygit/window.lua @@ -0,0 +1,89 @@ +local api = vim.api + +--- open floating window with nice borders +local function open_floating_window() + local floating_window_scaling_factor = vim.g.lazygit_floating_window_scaling_factor + + -- Why is this required? + -- vim.g.lazygit_floating_window_scaling_factor returns different types if the value is an integer or float + if type(floating_window_scaling_factor) == 'table' then + floating_window_scaling_factor = floating_window_scaling_factor[false] + end + + local status, plenary = pcall(require, 'plenary.window.float') + if status and vim.g.lazygit_floating_window_use_plenary and vim.g.lazygit_floating_window_use_plenary ~= 0 then + local ret = plenary.percentage_range_window(floating_window_scaling_factor, floating_window_scaling_factor, {winblend=vim.g.lazygit_floating_window_winblend}) + return ret.win_id, ret.bufnr + end + + local height = math.ceil(vim.o.lines * floating_window_scaling_factor) - 1 + local width = math.ceil(vim.o.columns * floating_window_scaling_factor) + + local row = math.ceil(vim.o.lines - height) / 2 + local col = math.ceil(vim.o.columns - width) / 2 + + local border_opts = { + style = 'minimal', + relative = 'editor', + row = row - 1, + col = col - 1, + width = width + 2, + height = height + 2, + } + + local opts = { style = 'minimal', relative = 'editor', row = row, col = col, width = width, height = height } + + local topleft, top, topright, right, botright, bot, botleft, left + local window_chars = vim.g.lazygit_floating_window_border_chars + if type(window_chars) == 'table' and #window_chars == 8 then + topleft, top, topright, right, botright, bot, botleft, left = unpack(window_chars) + else + topleft, top, topright, right, botright, bot, botleft, left = '╭','─', '╮', '│', '╯','─', '╰', '│' + end + + local border_lines = { topleft .. string.rep(top, width) .. topright } + local middle_line = left .. string.rep(' ', width) .. right + for _ = 1, height do + table.insert(border_lines, middle_line) + end + table.insert(border_lines, botleft .. string.rep(bot, width) .. botright) + + -- create a unlisted scratch buffer for the border + local border_buffer = api.nvim_create_buf(false, true) + + -- set border_lines in the border buffer from start 0 to end -1 and strict_indexing false + api.nvim_buf_set_lines(border_buffer, 0, -1, true, border_lines) + -- create border window + local border_window = api.nvim_open_win(border_buffer, true, border_opts) + vim.api.nvim_set_hl(0, "LazyGitBorder", { link = "Normal", default = true }) + vim.cmd('set winhl=NormalFloat:LazyGitBorder') + + -- create a unlisted scratch buffer + if LAZYGIT_BUFFER == nil or vim.fn.bufwinnr(LAZYGIT_BUFFER) == -1 then + LAZYGIT_BUFFER = api.nvim_create_buf(false, true) + else + LAZYGIT_LOADED = true + end + -- create file window, enter the window, and use the options defined in opts + local win = api.nvim_open_win(LAZYGIT_BUFFER, true, opts) + + vim.bo[LAZYGIT_BUFFER].filetype = 'lazygit' + + vim.cmd('setlocal bufhidden=hide') + vim.cmd('setlocal nocursorcolumn') + vim.api.nvim_set_hl(0, "LazyGitFloat", { link = "Normal", default = true }) + vim.cmd('setlocal winhl=NormalFloat:LazyGitFloat') + vim.cmd('set winblend=' .. vim.g.lazygit_floating_window_winblend) + + -- use autocommand to ensure that the border_buffer closes at the same time as the main buffer + local cmd = [[autocmd WinLeave silent! execute 'hide']] + vim.cmd(cmd) + cmd = [[autocmd WinLeave silent! execute 'silent bdelete! %s']] + vim.cmd(cmd:format(border_buffer)) + + return win, LAZYGIT_BUFFER +end + +return { + open_floating_window = open_floating_window, +} diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/lua/telescope/_extensions/lazygit.lua b/config/neovim/store/lazy-plugins/lazygit.nvim/lua/telescope/_extensions/lazygit.lua new file mode 100644 index 00000000..e0f3d679 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/lua/telescope/_extensions/lazygit.lua @@ -0,0 +1,93 @@ +local pickers = require("telescope.pickers") +local finders = require("telescope.finders") +local actions = require("telescope.actions") +local action_state = require("telescope.actions.state") +local conf = require("telescope.config").values +local lazygit_utils = require("lazygit.utils") + + +local function open_lazygit(prompt_buf) + local entry = action_state.get_selected_entry() + vim.fn.execute('cd ' .. entry.value) + + local cmd = [[lua require"lazygit".lazygit(nil)]] + vim.api.nvim_command(cmd) + + vim.cmd('stopinsert') + vim.cmd([[execute "normal i"]]) + vim.fn.feedkeys('j') + vim.api.nvim_buf_set_keymap(0, 't', '', '', {noremap = true, silent = true}) +end + + +local lazygit_repos = function(opts) + local displayer = require("telescope.pickers.entry_display").create { + separator = "", + -- TODO: make use of telescope geometry + items = { + {width = 4}, + {width = 55}, + {remaining = true}, + }, + } + + local repos = {} + for _, v in pairs(lazygit_utils.lazygit_visited_git_repos) do + if v == nil then + goto skip + end + + local index = #repos + 1 + local entry = + { + idx = index, + value = v:gsub("%s", ""), + -- retrieve git repo name + repo_name= v:gsub("%s", ""):match("^.+/(.+)$"), + } + + table.insert(repos, index, entry) + + ::skip:: + end + + pickers.new(opts or {}, { + prompt_title = "lazygit repos", + finder = finders.new_table { + results = repos, + entry_maker = function(entry) + local make_display = function() + return displayer + { + {entry.idx}, + {entry.repo_name}, + } + end + + return { + value = entry.value, + ordinal = string.format("%s %s", entry.idx, entry.repo_name), + display = make_display, + } + end, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_buf, _) + actions.select_default:replace(function () + -- for what ever reason any attempt to open an external window (such as lazygit) + -- shall be done after closing the buffer manually + actions.close(prompt_buf) + + open_lazygit() + end + ) + return true + end + }):find() +end + +return require("telescope").register_extension({ + exports = { + lazygit = lazygit_repos, + } +}) diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/plugin/lazygit.vim b/config/neovim/store/lazy-plugins/lazygit.nvim/plugin/lazygit.vim new file mode 100644 index 00000000..c5286e54 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/plugin/lazygit.vim @@ -0,0 +1,60 @@ +scriptencoding utf-8 + +if exists('g:loaded_lazygit_vim') | finish | endif + +let s:save_cpo = &cpoptions +set cpoptions&vim + +"""""""""""""""""""""""""""""""""""""""""""""""""""""" + +if !exists('g:lazygit_floating_window_winblend') + let g:lazygit_floating_window_winblend = 0 +endif + +if !exists('g:lazygit_floating_window_scaling_factor') + let g:lazygit_floating_window_scaling_factor = 0.9 +endif + +if !exists('g:lazygit_use_neovim_remote') + let g:lazygit_use_neovim_remote = executable('nvr') ? 1 : 0 +endif + +if exists('g:lazygit_floating_window_corner_chars') + echohl WarningMsg + echomsg "`g:lazygit_floating_window_corner_chars` is deprecated. Please use `g:lazygit_floating_window_border_chars` instead." + echohl None + if !exists('g:lazygit_floating_window_border_chars') + let g:lazygit_floating_window_border_chars = g:lazygit_floating_window_corner_chars + endif +endif + +if !exists('g:lazygit_floating_window_border_chars') + let g:lazygit_floating_window_border_chars = ['╭','─', '╮', '│', '╯','─', '╰', '│'] +endif + +" if lazygit_use_custom_config_file_path is set to 1 the +" lazygit_config_file_path option will be evaluated +if !exists('g:lazygit_use_custom_config_file_path') + let g:lazygit_use_custom_config_file_path = 0 +endif +" path to custom config file +if !exists('g:lazygit_config_file_path') + let g:lazygit_config_file_path = '' +endif + +command! LazyGit lua require'lazygit'.lazygit() + +command! LazyGitCurrentFile lua require'lazygit'.lazygitcurrentfile() + +command! LazyGitFilter lua require'lazygit'.lazygitfilter() + +command! LazyGitFilterCurrentFile lua require'lazygit'.lazygitfiltercurrentfile() + +command! LazyGitConfig lua require'lazygit'.lazygitconfig() + +"""""""""""""""""""""""""""""""""""""""""""""""""""""" + +let &cpoptions = s:save_cpo +unlet s:save_cpo + +let g:loaded_lazygit_vim = 1 diff --git a/config/neovim/store/lazy-plugins/lazygit.nvim/tests/MINRC b/config/neovim/store/lazy-plugins/lazygit.nvim/tests/MINRC new file mode 100644 index 00000000..553843cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/lazygit.nvim/tests/MINRC @@ -0,0 +1,22 @@ +" If after installing lazygit.vim, you are having trouble getting it to work, try the following minimal vimrc file. + +set nocompatible " be iMproved, required +filetype off " required + +if empty(glob('~/.local/share/nvim/site/autoload/plug.vim')) + silent !curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs + \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim + autocmd VimEnter * PlugInstall --sync | source $MYVIMRC +endif + +call plug#begin('~/.local/share/nvim/plugged') + +Plug '~/gitrepos/lazygit.nvim' + +" Initialize plugin system +call plug#end() + +let mapleader = " " +nnoremap lg :LazyGit + +" Save the above to a file `MINRC`, and run `nvim -u MINRC` and type `lg` in normal mode. diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/.github/ISSUE_TEMPLATE/bug_report.md b/config/neovim/store/lazy-plugins/lualine.nvim/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..d52c2123 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,70 @@ +--- +name: Bug Report +about: Report a problem in lualine +title: 'Bug: ' +labels: bug + +--- + + + +### Self Checks + + +- [ ] I'm using the latest lualine. +- [ ] I didn't find the issue in existing issues or PRs. + +### How to reproduce the problem + + +### Expected behaviour + + +### Actual behaviour + + +### Minimal config to reproduce the issue + + + + +### Additional information + diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/.github/ISSUE_TEMPLATE/feature_request.md b/config/neovim/store/lazy-plugins/lualine.nvim/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..71e2f3f4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature Request +about: Request a feature for lualine +title: "Feat: " +labels: new feature +--- + + + +### Requested feature + + + +### Motivation + + diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/.github/generated-files-bot.yml b/config/neovim/store/lazy-plugins/lualine.nvim/.github/generated-files-bot.yml new file mode 100644 index 00000000..0ada608e --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/.github/generated-files-bot.yml @@ -0,0 +1,6 @@ +generatedFiles: + - path: "doc/lualine.txt" + message: "`lualine.txt` is generated from README.md. Make changes there instead." +ignoreAuthors: + - 'github-actions[bot]' + - 'shadmansaleh' diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/.github/stale.yml b/config/neovim/store/lazy-plugins/lualine.nvim/.github/stale.yml new file mode 100644 index 00000000..942c0b41 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/.github/stale.yml @@ -0,0 +1,22 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - upstream + - help wanted + - wip + - good first issue + - discuss + - pin +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/.github/workflows/autogen.yml b/config/neovim/store/lazy-plugins/lualine.nvim/.github/workflows/autogen.yml new file mode 100644 index 00000000..bde7d91b --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/.github/workflows/autogen.yml @@ -0,0 +1,44 @@ +name: autogen +on: + push: + branches: [master] + paths: + - "lua/**.lua" + - "examples/**.lua" + - ".stylua.toml" + - "README.md" + +# Cancel any in-progress CI runs for a PR if it is updated +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +jobs: + autogen: + name: "(vimdoc|formating)" + runs-on: ubuntu-20.04 + timeout-minutes: 10 + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v2 + - name: Generate docs with panvimdoc + uses: kdheepak/panvimdoc@v2.7.1 + with: + vimdoc: lualine + description: fast and easy to configure statusline plugin for neovim + - name: Apply stylua + uses: JohnnyMorganz/stylua-action@1.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --config-path=.stylua.toml lua/ examples/ + version: 0.14.1 + - name: Push changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_user_name: github-actions[bot] + commit_user_email: 41898282+github-actions[bot]@users.noreply.github.com + commit_message: "chore: autogen (vimdocs+formating)" + branch: ${{ github.head_ref }} + file_pattern: lua/ examples/ doc/lualine.txt diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/lualine.nvim/.github/workflows/ci.yml new file mode 100644 index 00000000..2b9d52ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/.github/workflows/ci.yml @@ -0,0 +1,68 @@ +name: Tests + +on: + push: + branches: [master] + pull_request: + branches: [master] + +# Cancel any in-progress CI runs for a PR if it is updated +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + + +jobs: + tests: + name: ${{ matrix.os }} ${{ matrix.flavor }} + strategy: + fail-fast: false + matrix: + include: + - flavor: nvim-0.7 + runner: ubuntu-20.04 + os: linux + nvim_version: v0.7.0 + - flavor: nvim-0.8 + runner: ubuntu-20.04 + os: linux + nvim_version: v0.8.0 + - flavor: nvim-0.9 + runner: ubuntu-20.04 + os: linux + nvim_version: v0.9.0 + - flavor: nvim-nightly + runner: ubuntu-20.04 + os: linux + nvim_version: nightly + runs-on: ${{ matrix.runner }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - name: Setup Test Environment + run: | + mkdir -p ./tmp_home/nvim/pack/vendor/start + git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ./tmp_home/nvim/pack/vendor/start/plenary.nvim + git clone --depth 1 https://github.com/nvim-tree/nvim-web-devicons ./tmp_home/nvim/pack/vendor/start/nvim-web-devicons + ln -s $(pwd) ./tmp_home/nvim/pack/vendor/start + - name: Setup neovim ${{matrix.nvim_version}} + uses: rhysd/action-setup-vim@v1 + with: + neovim: true + version: ${{matrix.nvim_version}} + - name: Run tests + run: | + make test + lint: + runs-on: ubuntu-20.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - name: Setup linters + run: | + sudo apt-get update + sudo apt-get install luarocks + sudo luarocks install luacheck + - name: Run luacheck + run: | + make lint diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/.gitignore b/config/neovim/store/lazy-plugins/lualine.nvim/.gitignore new file mode 100644 index 00000000..4825b3db --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/.gitignore @@ -0,0 +1,58 @@ +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# ctags +tags + +# helptags +/doc/tags + +# panvimdoc +/panvimdoc/ + +# luacov +/luacov.report +/luacov.report.index +/luacov.stats + +# tests +/tmp_home/ +.luarc.json diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/.luacheckrc b/config/neovim/store/lazy-plugins/lualine.nvim/.luacheckrc new file mode 100644 index 00000000..7ce53af9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/.luacheckrc @@ -0,0 +1,15 @@ +globals = { + "vim", + "assert" +} + +-- Don't report unused self arguments of methods. +self = false + +-- Rerun tests only if their modification time changed. +cache = true + +ignore = { + "631", -- max_line_length + "212/_.*", -- unused argument, for vars with "_" prefix +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/.luacov b/config/neovim/store/lazy-plugins/lualine.nvim/.luacov new file mode 100644 index 00000000..b07e6fb5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/.luacov @@ -0,0 +1,20 @@ +return { + include = { + "lua/lualine$", + "lua/lualine_require$", + "lua/lualine%/.+$", + }, + exclude = { + "lua/lualine/themes%/.+$", + "lua/lualine/extensions%/.+$", + }, + modules = { + ["lualine"] = "lua/lualine.lua", + ["lualine_require"] = "lua/lualine_require.lua", + ["lualine.*"] = "lua/", + }, + statsfile = 'luacov.stats', + reportfile = 'luacov.report', + -- includeuntestedfiles = true, +} +-- vim:ft=lua diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/.stylua.toml b/config/neovim/store/lazy-plugins/lualine.nvim/.stylua.toml new file mode 100644 index 00000000..420d228c --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/.stylua.toml @@ -0,0 +1,5 @@ +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferSingle" +call_parentheses = "NoSingleTable" +collapse_simple_statement = "Never" diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/CONTRIBUTING.md b/config/neovim/store/lazy-plugins/lualine.nvim/CONTRIBUTING.md new file mode 100644 index 00000000..cdf00c2c --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# Contributing to lualine.nvim + +Thanks for considering to contribute. + +### Getting started + +If you're not sure where to help? You can try these: + +- You can look at the currently open [issues](https://github.com/nvim-lualine/lualine.nvim/issues) + to see if some bug needs fixing or for cool feature ideas.
+ You should also look at currently open PRs ([Pull requests](https://github.com/nvim-lualine/lualine.nvim/pulls)) to see if some abandoned PR interests you.
+ *We could really use some help with tests & docs they are currently lacking :)* +- You can add an exciting new component, extension or theme. + Note: Currently we aren't adding regular colorscheme based themes. + We think they make more sense with colorschemes as they tend not to get + updated once added here. But if you have some unique themes idea like [auto](https://github.com/nvim-lualine/lualine.nvim/blob/master/THEMES.md#auto) or [pywal](https://github.com/nvim-lualine/lualine.nvim/blob/master/THEMES.md#pywal) feel free to open an PR or issue. +- Feel free to open issues or unfinished PRs for help. + I'd actually recommend you to open an issue first for bigger PRs to discuss + the feature with a maintainer beforehand. That way you can know if the + feature is likely to be accepted or not before you get started. + You'll get recommendation and help with implementation specially if you show + willingness to implement it yourself. +- Do add tests and docs for your changes. + +Good luck! + +### Developer tools + +*Let's introduce you to the tools we use.* + +- Your PR needs to pass tests & linter. We lint our codebase with [luacheck](https://github.com/mpeterv/luacheck) + and run tests with [plenary-test][plenary.nvim] these will be run on CI. If you want you can run tests & linter + locally with `make test` & `make lint` respectively. Or `make check` to run both linter & tests. For running + tests you'll have to make sure lualine.nvim, [plenary.nvim][plenary.nvim] and + [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) are in same directory. +- Lua codebase gets formatted with [stylua](https://github.com/JohnnyMorganz/StyLua) in CI. + So you can ignore formatting. But if you want to submit formatted + PR you can run formatter locally with `make format`. +- VimDocs are auto generated with [panvimdoc](https://github.com/kdheepak/panvimdoc) from README.md. + So don't make changes to doc/lualine.txt . Instead add your docs to README or Wiki. + The docgen in ran by CI too. If you want to run it locally you can do so + with `make docgen`. Note: you'll need to have [pandoc](https://github.com/jgm/pandoc) installed. +- `make precommit_check` can come quite handy it'll run all the above mentioned tools +- You can check our test coverage with `make testcov`. + You'll need to have [luacov](https://github.com/keplerproject/luacov) + & [luacov-console](https://github.com/spacewander/luacov-console) installed for that. + If you want luacov's detailed report files, run the command with the `NOCLEAN` env set. + For example `NOCLEAN=1 make testcov` + +[plenary.nvim]: https://github.com/nvim-lua/plenary.nvim diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/LICENSE b/config/neovim/store/lazy-plugins/lualine.nvim/LICENSE new file mode 100644 index 00000000..0a2d4630 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 hoob3rt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/Makefile b/config/neovim/store/lazy-plugins/lualine.nvim/Makefile new file mode 100644 index 00000000..131d0228 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/Makefile @@ -0,0 +1,38 @@ +.DEFAULT_GOAL = check + +lint: + @luacheck lua/lualine/ + @luacheck tests/ + @luacheck examples/ + +format: + @stylua --config-path=.stylua.toml lua/ examples/ + +test: + @mkdir -p tmp_home + @export XDG_DATA_HOME='./tmp_home' && \ + export XDG_CONFIG_HOME='./tmp_home' && \ + bash ./scripts/test_runner.sh + @rm -rf tmp_home + +# Install luacov & luacov-console from luarocks +testcov: + @mkdir -p ./tmp_home/data/nvim + @mkdir -p ./tmp_home/config/nvim + @export XDG_DATA_HOME=$(realpath './tmp_home/data') && \ + export XDG_CONFIG_HOME=$(realpath './tmp_home/config') && \ + export TEST_COV=true && \ + bash ./scripts/test_runner.sh + @luacov-console lua/ + @luacov-console -s +ifeq ($(NOCLEAN), ) + @rm luacov.* +endif + @rm -rf tmp_home + +docgen: + @sh ./scripts/docgen.sh + +precommit_check: docgen format test lint + +check: lint test diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/README.md b/config/neovim/store/lazy-plugins/lualine.nvim/README.md new file mode 100644 index 00000000..eeb8d42c --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/README.md @@ -0,0 +1,1020 @@ +# lualine.nvim + + + +![code size](https://img.shields.io/github/languages/code-size/nvim-lualine/lualine.nvim?style=flat-square) +![license](https://img.shields.io/github/license/nvim-lualine/lualine.nvim?style=flat-square) + + + +A blazing fast and easy to configure Neovim statusline written in Lua. + +`lualine.nvim` requires Neovim >= 0.7. + +For previous versions of neovim please use compatability tags for example +compat-nvim-0.5 + +## Contributing + +Feel free to create an issue/PR if you want to see anything else implemented. +If you have some question or need help with configuration, start a [discussion](https://github.com/nvim-lualine/lualine.nvim/discussions). + +Please read [CONTRIBUTING.md](./CONTRIBUTING.md) before opening a PR. +You can also help with documentation in the [wiki](https://github.com/nvim-lualine/lualine.nvim/wiki). + + + +## Screenshots + +Here is a preview of what lualine can look like. + +

+ + + + + +

+ +Screenshots of all available themes are listed in [THEMES.md](./THEMES.md) + +For those who want to break the norms, you can create custom looks for lualine. + +**Example** : + +- [evil_lualine](examples/evil_lualine.lua) + +- [slanted-gaps](examples/slanted-gaps.lua) + +- [bubbles](examples/bubbles.lua) + + + + +## Performance compared to other plugins + +Unlike other statusline plugins, lualine loads only the components you specify, and nothing else. + +Startup time performance measured with an amazing plugin [dstein64/vim-startuptime](https://github.com/dstein64/vim-startuptime) + +Times are measured with a clean `init.vim` with only `vim-startuptime`, +`vim-plug` and given statusline plugin installed. +In control just `vim-startuptime` and`vim-plug` is installed. +And measured time is complete startuptime of vim not time spent +on specific plugin. These numbers are the average of 20 runs. + +| control | lualine | lightline | airline | +| :-----: | :-----: | :-------: | :-----: | +| 17.2 ms | 24.8 ms | 25.5 ms | 79.9 ms | + +Last Updated On: 18-04-2022 + +## Installation + +### [vim-plug](https://github.com/junegunn/vim-plug) + +```vim +Plug 'nvim-lualine/lualine.nvim' +" If you want to have icons in your statusline choose one of these +Plug 'nvim-tree/nvim-web-devicons' +``` + +### [packer.nvim](https://github.com/wbthomason/packer.nvim) + +```lua +use { + 'nvim-lualine/lualine.nvim', + requires = { 'nvim-tree/nvim-web-devicons', opt = true } +} +``` + +### [lazy.nvim](https://github.com/folke/lazy.nvim) + +```lua +{ + 'nvim-lualine/lualine.nvim', + dependencies = { 'nvim-tree/nvim-web-devicons' } +} +``` + +You'll also need to have a patched font if you want icons. + +## Usage and customization + +Lualine has sections as shown below. + +```text ++-------------------------------------------------+ +| A | B | C X | Y | Z | ++-------------------------------------------------+ +``` + +Each sections holds its components e.g. Vim's current mode. + +### Configuring lualine in init.vim + +All the examples below are in lua. You can use the same examples +in `.vim` files by wrapping them in lua heredoc like this: + +```vim +lua << END +require('lualine').setup() +END +``` + +For more information, check out `:help lua-heredoc`. + +#### Default configuration + +```lua +require('lualine').setup { + options = { + icons_enabled = true, + theme = 'auto', + component_separators = { left = '', right = ''}, + section_separators = { left = '', right = ''}, + disabled_filetypes = { + statusline = {}, + winbar = {}, + }, + ignore_focus = {}, + always_divide_middle = true, + globalstatus = false, + refresh = { + statusline = 1000, + tabline = 1000, + winbar = 1000, + } + }, + sections = { + lualine_a = {'mode'}, + lualine_b = {'branch', 'diff', 'diagnostics'}, + lualine_c = {'filename'}, + lualine_x = {'encoding', 'fileformat', 'filetype'}, + lualine_y = {'progress'}, + lualine_z = {'location'} + }, + inactive_sections = { + lualine_a = {}, + lualine_b = {}, + lualine_c = {'filename'}, + lualine_x = {'location'}, + lualine_y = {}, + lualine_z = {} + }, + tabline = {}, + winbar = {}, + inactive_winbar = {}, + extensions = {} +} +``` + +If you want to get your current lualine config, you can +do so with: + +```lua +require('lualine').get_config() + +``` + +--- + +### Starting lualine + +```lua +require('lualine').setup() +``` + +--- + +### Setting a theme + +```lua +options = { theme = 'gruvbox' } +``` + +All available themes are listed in [THEMES.md](./THEMES.md). + +Please create a PR if you managed to port a popular theme before us, [here is how to do it](./CONTRIBUTING.md). + +#### Customizing themes + +```lua +local custom_gruvbox = require'lualine.themes.gruvbox' + +-- Change the background of lualine_c section for normal mode +custom_gruvbox.normal.c.bg = '#112233' + +require('lualine').setup { + options = { theme = custom_gruvbox }, + ... +} +``` + +Theme structure is available [here](https://github.com/nvim-lualine/lualine.nvim/wiki/Writing-a-theme). + +--- + +### Separators + +lualine defines two kinds of separators: + +- `section_separators` - separators between sections +- `component_separators` - separators between the different components in sections + +**Note**: if viewing this README in a browser, chances are the characters below will not be visible. + +```lua +options = { + section_separators = { left = '', right = '' }, + component_separators = { left = '', right = '' } +} +``` + +Here, left refers to the left-most sections (a, b, c), and right refers +to the right-most sections (x, y, z). + +#### Disabling separators + +```lua +options = { section_separators = '', component_separators = '' } +``` + +--- + +### Changing components in lualine sections + +```lua +sections = {lualine_a = {'mode'}} +``` + +#### Available components + +- `branch` (git branch) +- `buffers` (shows currently available buffers) +- `diagnostics` (diagnostics count from your preferred source) +- `diff` (git diff status) +- `encoding` (file encoding) +- `fileformat` (file format) +- `filename` +- `filesize` +- `filetype` +- `hostname` +- `location` (location in file in line:column format) +- `mode` (vim mode) +- `progress` (%progress in file) +- `searchcount` (number of search matches when hlsearch is active) +- `selectioncount` (number of selected characters or lines) +- `tabs` (shows currently available tabs) +- `windows` (shows currently available windows) + +#### Custom components + +##### Lua functions as lualine component + +```lua +local function hello() + return [[hello world]] +end +sections = { lualine_a = { hello } } +``` + +##### Vim functions as lualine component + +```lua +sections = { lualine_a = {'FugitiveHead'} } +``` + +##### Vim's statusline items as lualine component + +```lua +sections = { lualine_c = {'%=', '%t%m', '%3p'} } +``` + +##### Vim variables as lualine component + +Variables from `g:`, `v:`, `t:`, `w:`, `b:`, `o:`, `to:`, `wo:`, `bo:` scopes can be used. + +See `:h lua-vim-variables` and `:h lua-vim-options` if you are not sure what to use. + +```lua +sections = { lualine_a = { 'g:coc_status', 'bo:filetype' } } +``` + +##### Lua expressions as lualine component + +You can use any valid lua expression as a component including: + +- oneliners +- global variables +- require statements + +```lua +sections = { lualine_c = { "os.date('%a')", 'data', "require'lsp-status'.status()" } } +``` + +`data` is a global variable in this example. + +--- + +### Component options + +Component options can change the way a component behave. +There are two kinds of options: + +- global options affecting all components +- local options affecting specific + +Global options can be used as local options (can be applied to specific components) +but you cannot use local options as global. +Global option used locally overwrites the global, for example: + +```lua + require('lualine').setup { + options = { fmt = string.lower }, + sections = { lualine_a = { + { 'mode', fmt = function(str) return str:sub(1,1) end } }, + lualine_b = {'branch'} } + } +``` + +`mode` will be formatted with the passed function so only first char will be +shown . On the other hand branch will be formatted with global formatter +`string.lower` so it will be showed in lower case. + +#### Available options + +#### Global options + +These are `options` that are used in options table. +They set behavior of lualine. + +Values set here are treated as default for other options +that work in component level. + +For example even though `icons_enabled` is a general component option. +You can set `icons_enabled` to `false` and icons will be disabled on all +component. You can still overwrite defaults set in option table by specifying +the option value in component. + +```lua +options = { + theme = 'auto', -- lualine theme + component_separators = { left = '', right = '' }, + section_separators = { left = '', right = '' }, + disabled_filetypes = { -- Filetypes to disable lualine for. + statusline = {}, -- only ignores the ft for statusline. + winbar = {}, -- only ignores the ft for winbar. + }, + + ignore_focus = {}, -- If current filetype is in this list it'll + -- always be drawn as inactive statusline + -- and the last window will be drawn as active statusline. + -- for example if you don't want statusline of + -- your file tree / sidebar window to have active + -- statusline you can add their filetypes here. + + always_divide_middle = true, -- When set to true, left sections i.e. 'a','b' and 'c' + -- can't take over the entire statusline even + -- if neither of 'x', 'y' or 'z' are present. + + globalstatus = false, -- enable global statusline (have a single statusline + -- at bottom of neovim instead of one for every window). + -- This feature is only available in neovim 0.7 and higher. + + refresh = { -- sets how often lualine should refresh it's contents (in ms) + statusline = 1000, -- The refresh option sets minimum time that lualine tries + tabline = 1000, -- to maintain between refresh. It's not guarantied if situation + winbar = 1000 -- arises that lualine needs to refresh itself before this time + -- it'll do it. + + -- Also you can force lualine's refresh by calling refresh function + -- like require('lualine').refresh() + } +} +``` + +#### General component options + +These are options that control behavior at component level +and are available for all components. + +```lua +sections = { + lualine_a = { + { + 'mode', + icons_enabled = true, -- Enables the display of icons alongside the component. + -- Defines the icon to be displayed in front of the component. + -- Can be string|table + -- As table it must contain the icon as first entry and can use + -- color option to custom color the icon. Example: + -- {'branch', icon = ''} / {'branch', icon = {'', color={fg='green'}}} + + -- icon position can also be set to the right side from table. Example: + -- {'branch', icon = {'', align='right', color={fg='green'}}} + icon = nil, + + separator = nil, -- Determines what separator to use for the component. + -- Note: + -- When a string is provided it's treated as component_separator. + -- When a table is provided it's treated as section_separator. + -- Passing an empty string disables the separator. + -- + -- These options can be used to set colored separators + -- around a component. + -- + -- The options need to be set as such: + -- separator = { left = '', right = ''} + -- + -- Where left will be placed on left side of component, + -- and right will be placed on its right. + -- + + cond = nil, -- Condition function, the component is loaded when the function returns `true`. + + draw_empty = false, -- Whether to draw component even if it's empty. + -- Might be useful if you want just the separator. + + -- Defines a custom color for the component: + -- + -- 'highlight_group_name' | { fg = '#rrggbb'|cterm_value(0-255)|'color_name(red)', bg= '#rrggbb', gui='style' } | function + -- Note: + -- '|' is synonymous with 'or', meaning a different acceptable format for that placeholder. + -- color function has to return one of other color types ('highlight_group_name' | { fg = '#rrggbb'|cterm_value(0-255)|'color_name(red)', bg= '#rrggbb', gui='style' }) + -- color functions can be used to have different colors based on state as shown below. + -- + -- Examples: + -- color = { fg = '#ffaa88', bg = 'grey', gui='italic,bold' }, + -- color = { fg = 204 } -- When fg/bg are omitted, they default to the your theme's fg/bg. + -- color = 'WarningMsg' -- Highlight groups can also be used. + -- color = function(section) + -- return { fg = vim.bo.modified and '#aa3355' or '#33aa88' } + -- end, + color = nil, -- The default is your theme's color for that section and mode. + + -- Specify what type a component is, if omitted, lualine will guess it for you. + -- + -- Available types are: + -- [format: type_name(example)], mod(branch/filename), + -- stl(%f/%m), var(g:coc_status/bo:modifiable), + -- lua_expr(lua expressions), vim_fun(viml function name) + -- + -- Note: + -- lua_expr is short for lua-expression and vim_fun is short for vim-function. + type = nil, + + padding = 1, -- Adds padding to the left and right of components. + -- Padding can be specified to left or right independently, e.g.: + -- padding = { left = left_padding, right = right_padding } + + fmt = nil, -- Format function, formats the component's output. + -- This function receives two arguments: + -- - string that is going to be displayed and + -- that can be changed, enhanced and etc. + -- - context object with information you might + -- need. E.g. tabnr if used with tabs. + on_click = nil, -- takes a function that is called when component is clicked with mouse. + -- the function receives several arguments + -- - number of clicks in case of multiple clicks + -- - mouse button used (l(left)/r(right)/m(middle)/...) + -- - modifiers pressed (s(shift)/c(ctrl)/a(alt)/m(meta)...) + } + } +} +``` + +#### Component specific options + +These are options that are available on specific components. +For example, you have option on `diagnostics` component to +specify what your diagnostic sources will be. + +#### buffers component options + +```lua +sections = { + lualine_a = { + { + 'buffers', + show_filename_only = true, -- Shows shortened relative path when set to false. + hide_filename_extension = false, -- Hide filename extension when set to true. + show_modified_status = true, -- Shows indicator when the buffer is modified. + + mode = 0, -- 0: Shows buffer name + -- 1: Shows buffer index + -- 2: Shows buffer name + buffer index + -- 3: Shows buffer number + -- 4: Shows buffer name + buffer number + + max_length = vim.o.columns * 2 / 3, -- Maximum width of buffers component, + -- it can also be a function that returns + -- the value of `max_length` dynamically. + filetype_names = { + TelescopePrompt = 'Telescope', + dashboard = 'Dashboard', + packer = 'Packer', + fzf = 'FZF', + alpha = 'Alpha' + }, -- Shows specific buffer name for that filetype ( { `filetype` = `buffer_name`, ... } ) + + -- Automatically updates active buffer color to match color of other components (will be overidden if buffers_color is set) + use_mode_colors = false, + + buffers_color = { + -- Same values as the general color option can be used here. + active = 'lualine_{section}_normal', -- Color for active buffer. + inactive = 'lualine_{section}_inactive', -- Color for inactive buffer. + }, + + symbols = { + modified = ' ●', -- Text to show when the buffer is modified + alternate_file = '#', -- Text to show to identify the alternate file + directory = '', -- Text to show when the buffer is a directory + }, + } + } +} +``` + +#### datetime component options + +```lua +sections = { + lualine_a = { + { + 'datetime', + -- options: default, us, uk, iso, or your own format string ("%H:%M", etc..) + style = 'default' + } + } +} +``` + +#### diagnostics component options + +```lua +sections = { + lualine_a = { + { + 'diagnostics', + + -- Table of diagnostic sources, available sources are: + -- 'nvim_lsp', 'nvim_diagnostic', 'nvim_workspace_diagnostic', 'coc', 'ale', 'vim_lsp'. + -- or a function that returns a table as such: + -- { error=error_cnt, warn=warn_cnt, info=info_cnt, hint=hint_cnt } + sources = { 'nvim_diagnostic', 'coc' }, + + -- Displays diagnostics for the defined severity types + sections = { 'error', 'warn', 'info', 'hint' }, + + diagnostics_color = { + -- Same values as the general color option can be used here. + error = 'DiagnosticError', -- Changes diagnostics' error color. + warn = 'DiagnosticWarn', -- Changes diagnostics' warn color. + info = 'DiagnosticInfo', -- Changes diagnostics' info color. + hint = 'DiagnosticHint', -- Changes diagnostics' hint color. + }, + symbols = {error = 'E', warn = 'W', info = 'I', hint = 'H'}, + colored = true, -- Displays diagnostics status in color if set to true. + update_in_insert = false, -- Update diagnostics in insert mode. + always_visible = false, -- Show diagnostics even if there are none. + } + } +} +``` + +#### diff component options + +```lua +sections = { + lualine_a = { + { + 'diff', + colored = true, -- Displays a colored diff status if set to true + diff_color = { + -- Same color values as the general color option can be used here. + added = 'LuaLineDiffAdd', -- Changes the diff's added color + modified = 'LuaLineDiffChange', -- Changes the diff's modified color + removed = 'LuaLineDiffDelete', -- Changes the diff's removed color you + }, + symbols = {added = '+', modified = '~', removed = '-'}, -- Changes the symbols used by the diff. + source = nil, -- A function that works as a data source for diff. + -- It must return a table as such: + -- { added = add_count, modified = modified_count, removed = removed_count } + -- or nil on failure. count <= 0 won't be displayed. + } + } +} +``` + +#### fileformat component options + +```lua +sections = { + lualine_a = { + { + 'fileformat', + symbols = { + unix = '', -- e712 + dos = '', -- e70f + mac = '', -- e711 + } + } + } +} +``` + +#### filename component options + +```lua +sections = { + lualine_a = { + { + 'filename', + file_status = true, -- Displays file status (readonly status, modified status) + newfile_status = false, -- Display new file status (new file means no write after created) + path = 0, -- 0: Just the filename + -- 1: Relative path + -- 2: Absolute path + -- 3: Absolute path, with tilde as the home directory + -- 4: Filename and parent dir, with tilde as the home directory + + shorting_target = 40, -- Shortens path to leave 40 spaces in the window + -- for other components. (terrible name, any suggestions?) + symbols = { + modified = '[+]', -- Text to show when the file is modified. + readonly = '[-]', -- Text to show when the file is non-modifiable or readonly. + unnamed = '[No Name]', -- Text to show for unnamed buffers. + newfile = '[New]', -- Text to show for newly created file before first write + } + } + } +} +``` + +#### filetype component options + +```lua +sections = { + lualine_a = { + { + 'filetype', + colored = true, -- Displays filetype icon in color if set to true + icon_only = false, -- Display only an icon for filetype + icon = { align = 'right' }, -- Display filetype icon on the right hand side + -- icon = {'X', align='right'} + -- Icon string ^ in table is ignored in filetype component + } + } +} +``` + +#### searchcount component options + +```lua +sections = { + lualine_a = { + { + 'searchcount', + maxcount = 999, + timeout = 500, + } + } +} +``` + +#### tabs component options + +```lua +sections = { + lualine_a = { + { + 'tabs', + tab_max_length = 40, -- Maximum width of each tab. The content will be shorten dynamically (example: apple/orange -> a/orange) + max_length = vim.o.columns / 3, -- Maximum width of tabs component. + -- Note: + -- It can also be a function that returns + -- the value of `max_length` dynamically. + mode = 0, -- 0: Shows tab_nr + -- 1: Shows tab_name + -- 2: Shows tab_nr + tab_name + + path = 0, -- 0: just shows the filename + -- 1: shows the relative path and shorten $HOME to ~ + -- 2: shows the full path + -- 3: shows the full path and shorten $HOME to ~ + + -- Automatically updates active tab color to match color of other components (will be overidden if buffers_color is set) + use_mode_colors = false, + + tabs_color = { + -- Same values as the general color option can be used here. + active = 'lualine_{section}_normal', -- Color for active tab. + inactive = 'lualine_{section}_inactive', -- Color for inactive tab. + }, + + show_modified_status = true, -- Shows a symbol next to the tab name if the file has been modified. + symbols = { + modified = '[+]', -- Text to show when the file is modified. + }, + + fmt = function(name, context) + -- Show + if buffer is modified in tab + local buflist = vim.fn.tabpagebuflist(context.tabnr) + local winnr = vim.fn.tabpagewinnr(context.tabnr) + local bufnr = buflist[winnr] + local mod = vim.fn.getbufvar(bufnr, '&mod') + + return name .. (mod == 1 and ' +' or '') + end + } + } +} +``` + +#### windows component options + +```lua +sections = { + lualine_a = { + { + 'windows', + show_filename_only = true, -- Shows shortened relative path when set to false. + show_modified_status = true, -- Shows indicator when the window is modified. + + mode = 0, -- 0: Shows window name + -- 1: Shows window index + -- 2: Shows window name + window index + + max_length = vim.o.columns * 2 / 3, -- Maximum width of windows component, + -- it can also be a function that returns + -- the value of `max_length` dynamically. + filetype_names = { + TelescopePrompt = 'Telescope', + dashboard = 'Dashboard', + packer = 'Packer', + fzf = 'FZF', + alpha = 'Alpha' + }, -- Shows specific window name for that filetype ( { `filetype` = `window_name`, ... } ) + + disabled_buftypes = { 'quickfix', 'prompt' }, -- Hide a window if its buffer's type is disabled + + -- Automatically updates active window color to match color of other components (will be overidden if buffers_color is set) + use_mode_colors = false, + + windows_color = { + -- Same values as the general color option can be used here. + active = 'lualine_{section}_normal', -- Color for active window. + inactive = 'lualine_{section}_inactive', -- Color for inactive window. + }, + } + } +} +``` + +--- + +### Tabline + +You can use lualine to display components in tabline. +The configuration for tabline sections is exactly the same as that of the statusline. + +```lua +tabline = { + lualine_a = {}, + lualine_b = {'branch'}, + lualine_c = {'filename'}, + lualine_x = {}, + lualine_y = {}, + lualine_z = {} +} +``` + +This will show the branch and filename components on top of neovim inside tabline. + +lualine also provides 2 components, buffers and tabs, that you can use to get a more traditional tabline/bufferline. + +```lua +tabline = { + lualine_a = {'buffers'}, + lualine_b = {'branch'}, + lualine_c = {'filename'}, + lualine_x = {}, + lualine_y = {}, + lualine_z = {'tabs'} +} +``` + +### Winbar + +From neovim-0.8 you can customize your winbar with lualine. +Winbar configuration is similar to statusline. + +```lua +winbar = { + lualine_a = {}, + lualine_b = {}, + lualine_c = {'filename'}, + lualine_x = {}, + lualine_y = {}, + lualine_z = {} +} + +inactive_winbar = { + lualine_a = {}, + lualine_b = {}, + lualine_c = {'filename'}, + lualine_x = {}, + lualine_y = {}, + lualine_z = {} +} +``` + +Just like statusline you can separately specify winbar for active and inactive +windows. Any lualine component can be placed in winbar. All kinds of custom +components supported in statusline are also supported for winbar too. In general +You can treat winbar as another lualine statusline that just appears on top +of windows instead of at bottom. + +#### Buffers + +Shows currently open buffers. Like bufferline . See +[buffers options](#buffers-component-options) +for all builtin behaviors of buffers component. +You can use `:LualineBuffersJump` to jump to buffer based on index +of buffer in buffers component. +Jumping to non-existent buffer indices generates an error. To avoid these errors +`LualineBuffersJump` provides `` support, meaning that you can call +`:LualineBufferJump!` to ignore these errors. + +```vim + :LualineBuffersJump 2 " Jumps to 2nd buffer in buffers component. + :LualineBuffersJump $ " Jumps to last buffer in buffers component. + :LualineBuffersJump! 3 " Attempts to jump to 3rd buffer, if it exists. +``` + +#### Tabs + +Shows currently open tab. Like usual tabline. See +[tabs options](#tabs-component-options) +for all builtin behaviors of tabs component. +You can also use `:LualineRenameTab` to set a name for a tabpage. +For example: + +```vim +:LualineRenameTab Project_K +``` + +It's useful when you're using rendering mode 2/3 in tabs. +To unname a tabpage run `:LualineRenameTab` without argument. + +#### Tabline as statusline + +You can also completely move your statusline to a tabline by configuring +`lualine.tabline` and disabling `lualine.sections` and `lualine.inactive_sections`: + +```lua +tabline = { +...... + }, +sections = {}, +inactive_sections = {}, +``` + +If you want a more sophisticated tabline you can use other +tabline plugins with lualine too, for example: + +- [nvim-bufferline](https://github.com/akinsho/nvim-bufferline.lua) +- [tabline.nvim](https://github.com/kdheepak/tabline.nvim) + +tabline.nvim even uses lualine's theme by default 🙌 +You can find a bigger list [here](https://github.com/rockerBOO/awesome-neovim#tabline). + +--- + +### Extensions + +lualine extensions change statusline appearance for a window/buffer with +specified filetypes. + +By default no extensions are loaded to improve performance. +You can load extensions with: + +```lua +extensions = {'quickfix'} +``` + +#### Available extensions + +- aerial +- chadtree +- ctrlspace +- fern +- fugitive +- fzf +- lazy +- man +- mason +- mundo +- neo-tree +- nerdtree +- nvim-dap-ui +- nvim-tree +- oil +- overseer +- quickfix +- symbols-outline +- toggleterm +- trouble + +#### Custom extensions + +You can define your own extensions. If you believe an extension may be useful to others, then please submit a PR. + +```lua +local my_extension = { sections = { lualine_a = {'mode'} }, filetypes = {'lua'} } +require('lualine').setup { extensions = { my_extension } } +``` + +--- + +### Refreshing lualine + +By default lualine refreshes itself based on timer and some events. You can set +the interval of the timer with refresh option. However you can also force +lualine to refresh at any time by calling `lualine.refresh` function. + +```lua +require('lualine').refresh({ + scope = 'tabpage', -- scope of refresh all/tabpage/window + place = { 'statusline', 'winbar', 'tabline' }, -- lualine segment ro refresh. +}) +``` + +The arguments shown here are default values. So not passing any of them will be +treated as if a default value was passed. + +So you can simply do + +```lua +require('lualine').refresh() +``` + +Avoid calling `lualine.refresh` inside components. Since components are evaluated +during refresh, calling refresh while refreshing can have undesirable effects. + +### Disabling lualine + +You can disable lualine for specific filetypes: + +```lua +options = { disabled_filetypes = {'lua'} } +``` + +You can also disable lualine completely. +Note that you need to call this after the setup + +```lua + require('lualine').hide({ + place = {'statusline', 'tabline', 'winbar'}, -- The segment this change applies to. + unhide = false, -- whether to re-enable lualine again/ + }) +``` + +The arguments show for hide above are default values. +Which means even if the hide function is called without +arguments it'll work as if these were passed. + +So in short to disable lualine completely you can do + +```lua +require('lualine').hide() +``` + +To enable it again you can do + +```lua +require('lualine').hide({unhide=true}) +``` + + + +### Contributors + +Thanks to these wonderful people, we enjoy this awesome plugin. + + + + + + + +### Wiki + +Check out the [wiki](https://github.com/nvim-lualine/lualine.nvim/wiki) for more info. + +You can find some useful [configuration snippets](https://github.com/nvim-lualine/lualine.nvim/wiki/Component-snippets) here. You can also share your awesome snippets with others. + +If you want to extend lualine with plugins or want to know +which ones already do, [wiki/plugins](https://github.com/nvim-lualine/lualine.nvim/wiki/Plugins) is for you. diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/THEMES.md b/config/neovim/store/lazy-plugins/lualine.nvim/THEMES.md new file mode 100644 index 00000000..379b6200 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/THEMES.md @@ -0,0 +1,361 @@ +# Available themes + +All available themes are only best effort ports by myself/ other users. If you find a theme to be weird/ wrong please open an issue/ pr. + +### auto + +auto is a special theme. It will automatically load theme for your colorscheme. +If there's no theme available for your colorscheme then +it'll try it's best to generate one. + +### 16color + +

+ + + + +

+ +### ayu_dark + +

+ + + + +

+ +### ayu_light + +

+ + + + +

+ +### ayu_mirage + +

+ + + + +

+ +### ayu + +It's a combination of ayu_light, ayu_dark & ayu_mirage. If `g:ayucolor` exists, +it loads one of these based on your `g:ayucolor` option. Otherwise, it will +load ayu_light when background=light and ayu_dark when background=dark +But if `g:ayuprefermirage` exists, it will load ayu_mirage instead when +`background=dark`. + +### base16 + +This theme will automatically use colors defined by your colorscheme using [tinted-theming/base16-vim](https://github.com/tinted-theming/base16-vim) or [RRethy/nvim-base16](https://github.com/RRethy/nvim-base16)] plugin. +The following example is using the `tomorrow-night` colorscheme: + +

+ + + + +

+ +### codedark + +

+ + + + +

+ +### dracula + +

+ + + + +

+ +### everforest + +

+ + + + +

+ +### gruvbox_dark + +

+ + + + +

+ +### gruvbox_light + +

+ + + + +

+ +### gruvbox + +It's a combination of gruvbox_light and gruvbox_dark. It loads either of +them based you your `background` option. + +### gruvbox-material + +

+ + + + +

+ +### horizon + +

+ + + + +

+ +### iceberg_dark + +

+ + + + +

+ +### iceberg_light + +

+ + + + +

+ +### iceberg + +It's a combination of iceberg_light and iceberg_dark. It loads either of +them based you your `background` option. + +### jellybeans + +

+ + + + +

+ +### material + +

+ + + + +

+ +### modus-vivendi + +

+ + + + +

+ +### molokai + +

+ + + + +

+ +### moonfly + +

+ + + + +

+ +### nightfly + +

+ + + + +

+ +### nord + +

+ + + + +

+ +### OceanicNext + +

+ + + + +

+ +### onedark + +

+ + + + +

+ +### onelight + +

+ + + + +

+ +### palenight + +

+ + + + +

+ +### papercolor_dark + +

+ + + + +

+ +### papercolor_light + +

+ + + + +

+ +### PaperColor + +It's a combination of papercolor_light and papercolor_dark. It loads either of +them based you your `background` option. + +### powerline + +

+ + + + +

+ +### powerline_dark + +

+ + + + +

+ +### pywal + +pywal is another special theme. It will load the colors from your current [pywal](https://github.com/dylanaraps/pywal) cache, specifically `~/.cache/wal/colors.sh` and generate a theme. + +#### `wal --theme ashes` + + + +#### `wal --theme -l github` + + + +#### `wal --theme vscode` + + + +#### `wal --theme zenburn` + + + +### seoul256 + +

+ + + + +

+ +### solarized_dark + +

+ + + + +

+ +### solarized_light + +

+ + + + +

+ +### Tomorrow + +

+ + + + +

+ +### wombat + +

+ + + + +

diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/doc/lualine.txt b/config/neovim/store/lazy-plugins/lualine.nvim/doc/lualine.txt new file mode 100644 index 00000000..05d45660 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/doc/lualine.txt @@ -0,0 +1,1082 @@ +*lualine.txt* fast and easy to configure statusline plugin for neovim + +============================================================================== +Table of Contents *lualine-table-of-contents* + +1. lualine.nvim |lualine-lualine.nvim| + - Contributing |lualine-contributing| + - Performance compared to other plugins|lualine-performance-compared-to-other-plugins| + - Installation |lualine-installation| + - Usage and customization |lualine-usage-and-customization| + +============================================================================== +1. lualine.nvim *lualine-lualine.nvim* + +A blazing fast and easy to configure Neovim statusline written in Lua. + +`lualine.nvim` requires Neovim >= 0.7. + +For previous versions of neovim please use compatability tags for example +compat-nvim-0.5 + +CONTRIBUTING *lualine-contributing* + +Feel free to create an issue/PR if you want to see anything else implemented. +If you have some question or need help with configuration, start a discussion +. + +Please read CONTRIBUTING.md <./CONTRIBUTING.md> before opening a PR. You can +also help with documentation in the wiki +. + +PERFORMANCE COMPARED TO OTHER PLUGINS*lualine-performance-compared-to-other-plugins* + +Unlike other statusline plugins, lualine loads only the components you specify, +and nothing else. + +Startup time performance measured with an amazing plugin +dstein64/vim-startuptime + +Times are measured with a clean `init.vim` with only `vim-startuptime`, +`vim-plug` and given statusline plugin installed. In control just +`vim-startuptime` and`vim-plug` is installed. And measured time is complete +startuptime of vim not time spent on specific plugin. These numbers are the +average of 20 runs. + +│control│lualine│lightline│airline│ +│17.2 ms│24.8 ms│ 25.5 ms │79.9 ms│ + + +Last Updated On: 18-04-2022 + +INSTALLATION *lualine-installation* + +VIM-PLUG ~ + +> + Plug 'nvim-lualine/lualine.nvim' + " If you want to have icons in your statusline choose one of these + Plug 'nvim-tree/nvim-web-devicons' +< + + +PACKER.NVIM ~ + +> + use { + 'nvim-lualine/lualine.nvim', + requires = { 'nvim-tree/nvim-web-devicons', opt = true } + } +< + + +LAZY.NVIM ~ + +> + { + 'nvim-lualine/lualine.nvim', + dependencies = { 'nvim-tree/nvim-web-devicons' } + } +< + + +You’ll also need to have a patched font if you want icons. + +USAGE AND CUSTOMIZATION *lualine-usage-and-customization* + +Lualine has sections as shown below. + +> + +-------------------------------------------------+ + | A | B | C X | Y | Z | + +-------------------------------------------------+ +< + + +Each sections holds its components e.g. Vim’s current mode. + +CONFIGURING LUALINE IN INIT.VIM ~ + +All the examples below are in lua. You can use the same examples in `.vim` +files by wrapping them in lua heredoc like this: + +> + lua << END + require('lualine').setup() + END +< + + +For more information, check out `:help lua-heredoc`. + + *lualine-Default-configuration* + +> + require('lualine').setup { + options = { + icons_enabled = true, + theme = 'auto', + component_separators = { left = '', right = ''}, + section_separators = { left = '', right = ''}, + disabled_filetypes = { + statusline = {}, + winbar = {}, + }, + ignore_focus = {}, + always_divide_middle = true, + globalstatus = false, + refresh = { + statusline = 1000, + tabline = 1000, + winbar = 1000, + } + }, + sections = { + lualine_a = {'mode'}, + lualine_b = {'branch', 'diff', 'diagnostics'}, + lualine_c = {'filename'}, + lualine_x = {'encoding', 'fileformat', 'filetype'}, + lualine_y = {'progress'}, + lualine_z = {'location'} + }, + inactive_sections = { + lualine_a = {}, + lualine_b = {}, + lualine_c = {'filename'}, + lualine_x = {'location'}, + lualine_y = {}, + lualine_z = {} + }, + tabline = {}, + winbar = {}, + inactive_winbar = {}, + extensions = {} + } +< + + +Default configuration If you want to get your current lualine + config, you can do so with: + + +> + require('lualine').get_config() +< + + +------------------------------------------------------------------------------ + +STARTING LUALINE ~ + +> + require('lualine').setup() +< + + +------------------------------------------------------------------------------ + +SETTING A THEME ~ + +> + options = { theme = 'gruvbox' } +< + + +All available themes are listed in THEMES.md <./THEMES.md>. + +Please create a PR if you managed to port a popular theme before us, here is +how to do it <./CONTRIBUTING.md>. + + *lualine-Customizing-themes* + +> + local custom_gruvbox = require'lualine.themes.gruvbox' + + -- Change the background of lualine_c section for normal mode + custom_gruvbox.normal.c.bg = '#112233' + + require('lualine').setup { + options = { theme = custom_gruvbox }, + ... + } +< + + +Customizing themes Theme structure is available here + . + + +------------------------------------------------------------------------------ + +SEPARATORS ~ + +lualine defines two kinds of separators: + + +- `section_separators` - separators between sections +- `component_separators` - separators between the different components in sections + + +**Note**: if viewing this README in a browser, chances are the characters below +will not be visible. + +> + options = { + section_separators = { left = '', right = '' }, + component_separators = { left = '', right = '' } + } +< + + +Here, left refers to the left-most sections (a, b, c), and right refers to the +right-most sections (x, y, z). + + *lualine-Disabling-separators* + +> + options = { section_separators = '', component_separators = '' } +< + + +------------------------------------------------------------------------------ + +CHANGING COMPONENTS IN LUALINE SECTIONS ~ + +> + sections = {lualine_a = {'mode'}} +< + + + *lualine-Available-components* + + +- `branch` (git branch) +- `buffers` (shows currently available buffers) +- `diagnostics` (diagnostics count from your preferred source) +- `diff` (git diff status) +- `encoding` (file encoding) +- `fileformat` (file format) +- `filename` +- `filesize` +- `filetype` +- `hostname` +- `location` (location in file in line:column format) +- `mode` (vim mode) +- `progress` (%progress in file) +- `searchcount` (number of search matches when hlsearch is active) +- `selectioncount` (number of selected characters or lines) +- `tabs` (shows currently available tabs) +- `windows` (shows currently available windows) + + + *lualine-Custom-components* + +LUA FUNCTIONS AS LUALINE COMPONENT + +> + local function hello() + return [[hello world]] + end + sections = { lualine_a = { hello } } +< + + +VIM FUNCTIONS AS LUALINE COMPONENT + +> + sections = { lualine_a = {'FugitiveHead'} } +< + + +VIM’S STATUSLINE ITEMS AS LUALINE COMPONENT + +> + sections = { lualine_c = {'%=', '%t%m', '%3p'} } +< + + +VIM VARIABLES AS LUALINE COMPONENT + +Variables from `g:`, `v:`, `t:`, `w:`, `b:`, `o:`, `to:`, `wo:`, `bo:` scopes +can be used. + +See `:h lua-vim-variables` and `:h lua-vim-options` if you are not sure what to +use. + +> + sections = { lualine_a = { 'g:coc_status', 'bo:filetype' } } +< + + +LUA EXPRESSIONS AS LUALINE COMPONENT + +You can use any valid lua expression as a component including: + + +- oneliners +- global variables +- require statements + + +> + sections = { lualine_c = { "os.date('%a')", 'data', "require'lsp-status'.status()" } } +< + + +`data` is a global variable in this example. + +------------------------------------------------------------------------------ + +COMPONENT OPTIONS ~ + +Component options can change the way a component behave. There are two kinds of +options: + + +- global options affecting all components +- local options affecting specific + + +Global options can be used as local options (can be applied to specific +components) but you cannot use local options as global. Global option used +locally overwrites the global, for example: + +> + require('lualine').setup { + options = { fmt = string.lower }, + sections = { lualine_a = { + { 'mode', fmt = function(str) return str:sub(1,1) end } }, + lualine_b = {'branch'} } + } +< + + +`mode` will be formatted with the passed function so only first char will be +shown. On the other hand branch will be formatted with global formatter +`string.lower` so it will be showed in lower case. + + *lualine-Available-options* + + *lualine-Global-options* + +Global options These are `options` that are used in + options table. They set behavior of + lualine. + + +Values set here are treated as default for other options that work in component +level. + +For example even though `icons_enabled` is a general component option. You can +set `icons_enabled` to `false` and icons will be disabled on all component. You +can still overwrite defaults set in option table by specifying the option value +in component. + +> + options = { + theme = 'auto', -- lualine theme + component_separators = { left = '', right = '' }, + section_separators = { left = '', right = '' }, + disabled_filetypes = { -- Filetypes to disable lualine for. + statusline = {}, -- only ignores the ft for statusline. + winbar = {}, -- only ignores the ft for winbar. + }, + + ignore_focus = {}, -- If current filetype is in this list it'll + -- always be drawn as inactive statusline + -- and the last window will be drawn as active statusline. + -- for example if you don't want statusline of + -- your file tree / sidebar window to have active + -- statusline you can add their filetypes here. + + always_divide_middle = true, -- When set to true, left sections i.e. 'a','b' and 'c' + -- can't take over the entire statusline even + -- if neither of 'x', 'y' or 'z' are present. + + globalstatus = false, -- enable global statusline (have a single statusline + -- at bottom of neovim instead of one for every window). + -- This feature is only available in neovim 0.7 and higher. + + refresh = { -- sets how often lualine should refresh it's contents (in ms) + statusline = 1000, -- The refresh option sets minimum time that lualine tries + tabline = 1000, -- to maintain between refresh. It's not guarantied if situation + winbar = 1000 -- arises that lualine needs to refresh itself before this time + -- it'll do it. + + -- Also you can force lualine's refresh by calling refresh function + -- like require('lualine').refresh() + } + } +< + + + *lualine-General-component-options* + +General component options These are options that control behavior + at component level and are available for + all components. + + +> + sections = { + lualine_a = { + { + 'mode', + icons_enabled = true, -- Enables the display of icons alongside the component. + -- Defines the icon to be displayed in front of the component. + -- Can be string|table + -- As table it must contain the icon as first entry and can use + -- color option to custom color the icon. Example: + -- {'branch', icon = ''} / {'branch', icon = {'', color={fg='green'}}} + + -- icon position can also be set to the right side from table. Example: + -- {'branch', icon = {'', align='right', color={fg='green'}}} + icon = nil, + + separator = nil, -- Determines what separator to use for the component. + -- Note: + -- When a string is provided it's treated as component_separator. + -- When a table is provided it's treated as section_separator. + -- Passing an empty string disables the separator. + -- + -- These options can be used to set colored separators + -- around a component. + -- + -- The options need to be set as such: + -- separator = { left = '', right = ''} + -- + -- Where left will be placed on left side of component, + -- and right will be placed on its right. + -- + + cond = nil, -- Condition function, the component is loaded when the function returns `true`. + + draw_empty = false, -- Whether to draw component even if it's empty. + -- Might be useful if you want just the separator. + + -- Defines a custom color for the component: + -- + -- 'highlight_group_name' | { fg = '#rrggbb'|cterm_value(0-255)|'color_name(red)', bg= '#rrggbb', gui='style' } | function + -- Note: + -- '|' is synonymous with 'or', meaning a different acceptable format for that placeholder. + -- color function has to return one of other color types ('highlight_group_name' | { fg = '#rrggbb'|cterm_value(0-255)|'color_name(red)', bg= '#rrggbb', gui='style' }) + -- color functions can be used to have different colors based on state as shown below. + -- + -- Examples: + -- color = { fg = '#ffaa88', bg = 'grey', gui='italic,bold' }, + -- color = { fg = 204 } -- When fg/bg are omitted, they default to the your theme's fg/bg. + -- color = 'WarningMsg' -- Highlight groups can also be used. + -- color = function(section) + -- return { fg = vim.bo.modified and '#aa3355' or '#33aa88' } + -- end, + color = nil, -- The default is your theme's color for that section and mode. + + -- Specify what type a component is, if omitted, lualine will guess it for you. + -- + -- Available types are: + -- [format: type_name(example)], mod(branch/filename), + -- stl(%f/%m), var(g:coc_status/bo:modifiable), + -- lua_expr(lua expressions), vim_fun(viml function name) + -- + -- Note: + -- lua_expr is short for lua-expression and vim_fun is short for vim-function. + type = nil, + + padding = 1, -- Adds padding to the left and right of components. + -- Padding can be specified to left or right independently, e.g.: + -- padding = { left = left_padding, right = right_padding } + + fmt = nil, -- Format function, formats the component's output. + -- This function receives two arguments: + -- - string that is going to be displayed and + -- that can be changed, enhanced and etc. + -- - context object with information you might + -- need. E.g. tabnr if used with tabs. + on_click = nil, -- takes a function that is called when component is clicked with mouse. + -- the function receives several arguments + -- - number of clicks in case of multiple clicks + -- - mouse button used (l(left)/r(right)/m(middle)/...) + -- - modifiers pressed (s(shift)/c(ctrl)/a(alt)/m(meta)...) + } + } + } +< + + + *lualine-Component-specific-options* + +Component specific options These are options that are available on + specific components. For example, you + have option on `diagnostics` component + to specify what your diagnostic sources + will be. + + + *lualine-buffers-component-options* + +> + sections = { + lualine_a = { + { + 'buffers', + show_filename_only = true, -- Shows shortened relative path when set to false. + hide_filename_extension = false, -- Hide filename extension when set to true. + show_modified_status = true, -- Shows indicator when the buffer is modified. + + mode = 0, -- 0: Shows buffer name + -- 1: Shows buffer index + -- 2: Shows buffer name + buffer index + -- 3: Shows buffer number + -- 4: Shows buffer name + buffer number + + max_length = vim.o.columns * 2 / 3, -- Maximum width of buffers component, + -- it can also be a function that returns + -- the value of `max_length` dynamically. + filetype_names = { + TelescopePrompt = 'Telescope', + dashboard = 'Dashboard', + packer = 'Packer', + fzf = 'FZF', + alpha = 'Alpha' + }, -- Shows specific buffer name for that filetype ( { `filetype` = `buffer_name`, ... } ) + + -- Automatically updates active buffer color to match color of other components (will be overidden if buffers_color is set) + use_mode_colors = false, + + buffers_color = { + -- Same values as the general color option can be used here. + active = 'lualine_{section}_normal', -- Color for active buffer. + inactive = 'lualine_{section}_inactive', -- Color for inactive buffer. + }, + + symbols = { + modified = ' ●', -- Text to show when the buffer is modified + alternate_file = '#', -- Text to show to identify the alternate file + directory = '', -- Text to show when the buffer is a directory + }, + } + } + } +< + + + *lualine-datetime-component-options* + +> + sections = { + lualine_a = { + { + 'datetime', + -- options: default, us, uk, iso, or your own format string ("%H:%M", etc..) + style = 'default' + } + } + } +< + + + *lualine-diagnostics-component-options* + +> + sections = { + lualine_a = { + { + 'diagnostics', + + -- Table of diagnostic sources, available sources are: + -- 'nvim_lsp', 'nvim_diagnostic', 'nvim_workspace_diagnostic', 'coc', 'ale', 'vim_lsp'. + -- or a function that returns a table as such: + -- { error=error_cnt, warn=warn_cnt, info=info_cnt, hint=hint_cnt } + sources = { 'nvim_diagnostic', 'coc' }, + + -- Displays diagnostics for the defined severity types + sections = { 'error', 'warn', 'info', 'hint' }, + + diagnostics_color = { + -- Same values as the general color option can be used here. + error = 'DiagnosticError', -- Changes diagnostics' error color. + warn = 'DiagnosticWarn', -- Changes diagnostics' warn color. + info = 'DiagnosticInfo', -- Changes diagnostics' info color. + hint = 'DiagnosticHint', -- Changes diagnostics' hint color. + }, + symbols = {error = 'E', warn = 'W', info = 'I', hint = 'H'}, + colored = true, -- Displays diagnostics status in color if set to true. + update_in_insert = false, -- Update diagnostics in insert mode. + always_visible = false, -- Show diagnostics even if there are none. + } + } + } +< + + + *lualine-diff-component-options* + +> + sections = { + lualine_a = { + { + 'diff', + colored = true, -- Displays a colored diff status if set to true + diff_color = { + -- Same color values as the general color option can be used here. + added = 'LuaLineDiffAdd', -- Changes the diff's added color + modified = 'LuaLineDiffChange', -- Changes the diff's modified color + removed = 'LuaLineDiffDelete', -- Changes the diff's removed color you + }, + symbols = {added = '+', modified = '~', removed = '-'}, -- Changes the symbols used by the diff. + source = nil, -- A function that works as a data source for diff. + -- It must return a table as such: + -- { added = add_count, modified = modified_count, removed = removed_count } + -- or nil on failure. count <= 0 won't be displayed. + } + } + } +< + + + *lualine-fileformat-component-options* + +> + sections = { + lualine_a = { + { + 'fileformat', + symbols = { + unix = '', -- e712 + dos = '', -- e70f + mac = '', -- e711 + } + } + } + } +< + + + *lualine-filename-component-options* + +> + sections = { + lualine_a = { + { + 'filename', + file_status = true, -- Displays file status (readonly status, modified status) + newfile_status = false, -- Display new file status (new file means no write after created) + path = 0, -- 0: Just the filename + -- 1: Relative path + -- 2: Absolute path + -- 3: Absolute path, with tilde as the home directory + -- 4: Filename and parent dir, with tilde as the home directory + + shorting_target = 40, -- Shortens path to leave 40 spaces in the window + -- for other components. (terrible name, any suggestions?) + symbols = { + modified = '[+]', -- Text to show when the file is modified. + readonly = '[-]', -- Text to show when the file is non-modifiable or readonly. + unnamed = '[No Name]', -- Text to show for unnamed buffers. + newfile = '[New]', -- Text to show for newly created file before first write + } + } + } + } +< + + + *lualine-filetype-component-options* + +> + sections = { + lualine_a = { + { + 'filetype', + colored = true, -- Displays filetype icon in color if set to true + icon_only = false, -- Display only an icon for filetype + icon = { align = 'right' }, -- Display filetype icon on the right hand side + -- icon = {'X', align='right'} + -- Icon string ^ in table is ignored in filetype component + } + } + } +< + + + *lualine-searchcount-component-options* + +> + sections = { + lualine_a = { + { + 'searchcount', + maxcount = 999, + timeout = 500, + } + } + } +< + + + *lualine-tabs-component-options* + +> + sections = { + lualine_a = { + { + 'tabs', + tab_max_length = 40, -- Maximum width of each tab. The content will be shorten dynamically (example: apple/orange -> a/orange) + max_length = vim.o.columns / 3, -- Maximum width of tabs component. + -- Note: + -- It can also be a function that returns + -- the value of `max_length` dynamically. + mode = 0, -- 0: Shows tab_nr + -- 1: Shows tab_name + -- 2: Shows tab_nr + tab_name + + path = 0, -- 0: just shows the filename + -- 1: shows the relative path and shorten $HOME to ~ + -- 2: shows the full path + -- 3: shows the full path and shorten $HOME to ~ + + -- Automatically updates active tab color to match color of other components (will be overidden if buffers_color is set) + use_mode_colors = false, + + tabs_color = { + -- Same values as the general color option can be used here. + active = 'lualine_{section}_normal', -- Color for active tab. + inactive = 'lualine_{section}_inactive', -- Color for inactive tab. + }, + + show_modified_status = true, -- Shows a symbol next to the tab name if the file has been modified. + symbols = { + modified = '[+]', -- Text to show when the file is modified. + }, + + fmt = function(name, context) + -- Show + if buffer is modified in tab + local buflist = vim.fn.tabpagebuflist(context.tabnr) + local winnr = vim.fn.tabpagewinnr(context.tabnr) + local bufnr = buflist[winnr] + local mod = vim.fn.getbufvar(bufnr, '&mod') + + return name .. (mod == 1 and ' +' or '') + end + } + } + } +< + + + *lualine-windows-component-options* + +> + sections = { + lualine_a = { + { + 'windows', + show_filename_only = true, -- Shows shortened relative path when set to false. + show_modified_status = true, -- Shows indicator when the window is modified. + + mode = 0, -- 0: Shows window name + -- 1: Shows window index + -- 2: Shows window name + window index + + max_length = vim.o.columns * 2 / 3, -- Maximum width of windows component, + -- it can also be a function that returns + -- the value of `max_length` dynamically. + filetype_names = { + TelescopePrompt = 'Telescope', + dashboard = 'Dashboard', + packer = 'Packer', + fzf = 'FZF', + alpha = 'Alpha' + }, -- Shows specific window name for that filetype ( { `filetype` = `window_name`, ... } ) + + disabled_buftypes = { 'quickfix', 'prompt' }, -- Hide a window if its buffer's type is disabled + + -- Automatically updates active window color to match color of other components (will be overidden if buffers_color is set) + use_mode_colors = false, + + windows_color = { + -- Same values as the general color option can be used here. + active = 'lualine_{section}_normal', -- Color for active window. + inactive = 'lualine_{section}_inactive', -- Color for inactive window. + }, + } + } + } +< + + +------------------------------------------------------------------------------ + +TABLINE ~ + +You can use lualine to display components in tabline. The configuration for +tabline sections is exactly the same as that of the statusline. + +> + tabline = { + lualine_a = {}, + lualine_b = {'branch'}, + lualine_c = {'filename'}, + lualine_x = {}, + lualine_y = {}, + lualine_z = {} + } +< + + +This will show the branch and filename components on top of neovim inside +tabline. + +lualine also provides 2 components, buffers and tabs, that you can use to get a +more traditional tabline/bufferline. + +> + tabline = { + lualine_a = {'buffers'}, + lualine_b = {'branch'}, + lualine_c = {'filename'}, + lualine_x = {}, + lualine_y = {}, + lualine_z = {'tabs'} + } +< + + +WINBAR ~ + +From neovim-0.8 you can customize your winbar with lualine. Winbar +configuration is similar to statusline. + +> + winbar = { + lualine_a = {}, + lualine_b = {}, + lualine_c = {'filename'}, + lualine_x = {}, + lualine_y = {}, + lualine_z = {} + } + + inactive_winbar = { + lualine_a = {}, + lualine_b = {}, + lualine_c = {'filename'}, + lualine_x = {}, + lualine_y = {}, + lualine_z = {} + } +< + + +Just like statusline you can separately specify winbar for active and inactive +windows. Any lualine component can be placed in winbar. All kinds of custom +components supported in statusline are also supported for winbar too. In +general You can treat winbar as another lualine statusline that just appears on +top of windows instead of at bottom. + + *lualine-Buffers* + +Buffers Shows currently open buffers. Like + bufferline. See + |lualine-buffers-options| for all + builtin behaviors of buffers component. + You can use `:LualineBuffersJump` to + jump to buffer based on index of buffer + in buffers component. Jumping to + non-existent buffer indices generates an + error. To avoid these errors + `LualineBuffersJump` provides `` + support, meaning that you can call + `:LualineBufferJump!` to ignore these + errors. + + +> + :LualineBuffersJump 2 " Jumps to 2nd buffer in buffers component. + :LualineBuffersJump $ " Jumps to last buffer in buffers component. + :LualineBuffersJump! 3 " Attempts to jump to 3rd buffer, if it exists. +< + + + *lualine-Tabs* + +Tabs Shows currently open tab. Like usual + tabline. See |lualine-tabs-options| for + all builtin behaviors of tabs component. + You can also use `:LualineRenameTab` to + set a name for a tabpage. For example: + + +> + :LualineRenameTab Project_K +< + + +It’s useful when you’re using rendering mode 2/3 in tabs. To unname a +tabpage run `:LualineRenameTab` without argument. + + *lualine-Tabline-as-statusline* + +Tabline as statusline You can also completely move your + statusline to a tabline by configuring + `lualine.tabline` and disabling + `lualine.sections` and + `lualine.inactive_sections`: + + +> + tabline = { + ...... + }, + sections = {}, + inactive_sections = {}, +< + + +If you want a more sophisticated tabline you can use other tabline plugins with +lualine too, for example: + + +- nvim-bufferline +- tabline.nvim + + +tabline.nvim even uses lualine’s theme by default 🙌 You can find a bigger +list here . + +------------------------------------------------------------------------------ + +EXTENSIONS ~ + +lualine extensions change statusline appearance for a window/buffer with +specified filetypes. + +By default no extensions are loaded to improve performance. You can load +extensions with: + +> + extensions = {'quickfix'} +< + + + *lualine-Available-extensions* + + +- aerial +- chadtree +- ctrlspace +- fern +- fugitive +- fzf +- lazy +- man +- mason +- mundo +- neo-tree +- nerdtree +- nvim-dap-ui +- nvim-tree +- oil +- overseer +- quickfix +- symbols-outline +- toggleterm +- trouble + + + *lualine-Custom-extensions* + +Custom extensions You can define your own extensions. If + you believe an extension may be useful + to others, then please submit a PR. + + +> + local my_extension = { sections = { lualine_a = {'mode'} }, filetypes = {'lua'} } + require('lualine').setup { extensions = { my_extension } } +< + + +------------------------------------------------------------------------------ + +REFRESHING LUALINE ~ + +By default lualine refreshes itself based on timer and some events. You can set +the interval of the timer with refresh option. However you can also force +lualine to refresh at any time by calling `lualine.refresh` function. + +> + require('lualine').refresh({ + scope = 'tabpage', -- scope of refresh all/tabpage/window + place = { 'statusline', 'winbar', 'tabline' }, -- lualine segment ro refresh. + }) +< + + +The arguments shown here are default values. So not passing any of them will be +treated as if a default value was passed. + +So you can simply do + +> + require('lualine').refresh() +< + + +Avoid calling `lualine.refresh` inside components. Since components are +evaluated during refresh, calling refresh while refreshing can have undesirable +effects. + +DISABLING LUALINE ~ + +You can disable lualine for specific filetypes: + +> + options = { disabled_filetypes = {'lua'} } +< + + +You can also disable lualine completely. Note that you need to call this after +the setup + +> + require('lualine').hide({ + place = {'statusline', 'tabline', 'winbar'}, -- The segment this change applies to. + unhide = false, -- whether to re-enable lualine again/ + }) +< + + +The arguments show for hide above are default values. Which means even if the +hide function is called without arguments it’ll work as if these were passed. + +So in short to disable lualine completely you can do + +> + require('lualine').hide() +< + + +To enable it again you can do + +> + require('lualine').hide({unhide=true}) +< + + +WIKI ~ + +Check out the wiki for more +info. + +You can find some useful configuration snippets + here. +You can also share your awesome snippets with others. + +If you want to extend lualine with plugins or want to know which ones already +do, wiki/plugins is +for you. + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/examples/bubbles.lua b/config/neovim/store/lazy-plugins/lualine.nvim/examples/bubbles.lua new file mode 100644 index 00000000..88edf574 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/examples/bubbles.lua @@ -0,0 +1,62 @@ +-- Bubbles config for lualine +-- Author: lokesh-krishna +-- MIT license, see LICENSE for more details. + +-- stylua: ignore +local colors = { + blue = '#80a0ff', + cyan = '#79dac8', + black = '#080808', + white = '#c6c6c6', + red = '#ff5189', + violet = '#d183e8', + grey = '#303030', +} + +local bubbles_theme = { + normal = { + a = { fg = colors.black, bg = colors.violet }, + b = { fg = colors.white, bg = colors.grey }, + c = { fg = colors.white }, + }, + + insert = { a = { fg = colors.black, bg = colors.blue } }, + visual = { a = { fg = colors.black, bg = colors.cyan } }, + replace = { a = { fg = colors.black, bg = colors.red } }, + + inactive = { + a = { fg = colors.white, bg = colors.black }, + b = { fg = colors.white, bg = colors.black }, + c = { fg = colors.white }, + }, +} + +require('lualine').setup { + options = { + theme = bubbles_theme, + component_separators = '', + section_separators = { left = '', right = '' }, + }, + sections = { + lualine_a = { { 'mode', separator = { left = '' }, right_padding = 2 } }, + lualine_b = { 'filename', 'branch' }, + lualine_c = { + '%=', --[[ add your center compoentnts here in place of this comment ]] + }, + lualine_x = {}, + lualine_y = { 'filetype', 'progress' }, + lualine_z = { + { 'location', separator = { right = '' }, left_padding = 2 }, + }, + }, + inactive_sections = { + lualine_a = { 'filename' }, + lualine_b = {}, + lualine_c = {}, + lualine_x = {}, + lualine_y = {}, + lualine_z = { 'location' }, + }, + tabline = {}, + extensions = {}, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/examples/evil_lualine.lua b/config/neovim/store/lazy-plugins/lualine.nvim/examples/evil_lualine.lua new file mode 100644 index 00000000..13869203 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/examples/evil_lualine.lua @@ -0,0 +1,221 @@ +-- Eviline config for lualine +-- Author: shadmansaleh +-- Credit: glepnir +local lualine = require('lualine') + +-- Color table for highlights +-- stylua: ignore +local colors = { + bg = '#202328', + fg = '#bbc2cf', + yellow = '#ECBE7B', + cyan = '#008080', + darkblue = '#081633', + green = '#98be65', + orange = '#FF8800', + violet = '#a9a1e1', + magenta = '#c678dd', + blue = '#51afef', + red = '#ec5f67', +} + +local conditions = { + buffer_not_empty = function() + return vim.fn.empty(vim.fn.expand('%:t')) ~= 1 + end, + hide_in_width = function() + return vim.fn.winwidth(0) > 80 + end, + check_git_workspace = function() + local filepath = vim.fn.expand('%:p:h') + local gitdir = vim.fn.finddir('.git', filepath .. ';') + return gitdir and #gitdir > 0 and #gitdir < #filepath + end, +} + +-- Config +local config = { + options = { + -- Disable sections and component separators + component_separators = '', + section_separators = '', + theme = { + -- We are going to use lualine_c an lualine_x as left and + -- right section. Both are highlighted by c theme . So we + -- are just setting default looks o statusline + normal = { c = { fg = colors.fg, bg = colors.bg } }, + inactive = { c = { fg = colors.fg, bg = colors.bg } }, + }, + }, + sections = { + -- these are to remove the defaults + lualine_a = {}, + lualine_b = {}, + lualine_y = {}, + lualine_z = {}, + -- These will be filled later + lualine_c = {}, + lualine_x = {}, + }, + inactive_sections = { + -- these are to remove the defaults + lualine_a = {}, + lualine_b = {}, + lualine_y = {}, + lualine_z = {}, + lualine_c = {}, + lualine_x = {}, + }, +} + +-- Inserts a component in lualine_c at left section +local function ins_left(component) + table.insert(config.sections.lualine_c, component) +end + +-- Inserts a component in lualine_x at right section +local function ins_right(component) + table.insert(config.sections.lualine_x, component) +end + +ins_left { + function() + return '▊' + end, + color = { fg = colors.blue }, -- Sets highlighting of component + padding = { left = 0, right = 1 }, -- We don't need space before this +} + +ins_left { + -- mode component + function() + return '' + end, + color = function() + -- auto change color according to neovims mode + local mode_color = { + n = colors.red, + i = colors.green, + v = colors.blue, + [''] = colors.blue, + V = colors.blue, + c = colors.magenta, + no = colors.red, + s = colors.orange, + S = colors.orange, + [''] = colors.orange, + ic = colors.yellow, + R = colors.violet, + Rv = colors.violet, + cv = colors.red, + ce = colors.red, + r = colors.cyan, + rm = colors.cyan, + ['r?'] = colors.cyan, + ['!'] = colors.red, + t = colors.red, + } + return { fg = mode_color[vim.fn.mode()] } + end, + padding = { right = 1 }, +} + +ins_left { + -- filesize component + 'filesize', + cond = conditions.buffer_not_empty, +} + +ins_left { + 'filename', + cond = conditions.buffer_not_empty, + color = { fg = colors.magenta, gui = 'bold' }, +} + +ins_left { 'location' } + +ins_left { 'progress', color = { fg = colors.fg, gui = 'bold' } } + +ins_left { + 'diagnostics', + sources = { 'nvim_diagnostic' }, + symbols = { error = ' ', warn = ' ', info = ' ' }, + diagnostics_color = { + color_error = { fg = colors.red }, + color_warn = { fg = colors.yellow }, + color_info = { fg = colors.cyan }, + }, +} + +-- Insert mid section. You can make any number of sections in neovim :) +-- for lualine it's any number greater then 2 +ins_left { + function() + return '%=' + end, +} + +ins_left { + -- Lsp server name . + function() + local msg = 'No Active Lsp' + local buf_ft = vim.api.nvim_buf_get_option(0, 'filetype') + local clients = vim.lsp.get_active_clients() + if next(clients) == nil then + return msg + end + for _, client in ipairs(clients) do + local filetypes = client.config.filetypes + if filetypes and vim.fn.index(filetypes, buf_ft) ~= -1 then + return client.name + end + end + return msg + end, + icon = ' LSP:', + color = { fg = '#ffffff', gui = 'bold' }, +} + +-- Add components to right sections +ins_right { + 'o:encoding', -- option component same as &encoding in viml + fmt = string.upper, -- I'm not sure why it's upper case either ;) + cond = conditions.hide_in_width, + color = { fg = colors.green, gui = 'bold' }, +} + +ins_right { + 'fileformat', + fmt = string.upper, + icons_enabled = false, -- I think icons are cool but Eviline doesn't have them. sigh + color = { fg = colors.green, gui = 'bold' }, +} + +ins_right { + 'branch', + icon = '', + color = { fg = colors.violet, gui = 'bold' }, +} + +ins_right { + 'diff', + -- Is it me or the symbol for modified us really weird + symbols = { added = ' ', modified = '󰝤 ', removed = ' ' }, + diff_color = { + added = { fg = colors.green }, + modified = { fg = colors.orange }, + removed = { fg = colors.red }, + }, + cond = conditions.hide_in_width, +} + +ins_right { + function() + return '▊' + end, + color = { fg = colors.blue }, + padding = { left = 1 }, +} + +-- Now don't forget to initialize lualine +lualine.setup(config) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/examples/slanted-gaps.lua b/config/neovim/store/lazy-plugins/lualine.nvim/examples/slanted-gaps.lua new file mode 100644 index 00000000..ba1fb21d --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/examples/slanted-gaps.lua @@ -0,0 +1,124 @@ +local colors = { + red = '#ca1243', + grey = '#a0a1a7', + black = '#383a42', + white = '#f3f3f3', + light_green = '#83a598', + orange = '#fe8019', + green = '#8ec07c', +} + +local theme = { + normal = { + a = { fg = colors.white, bg = colors.black }, + b = { fg = colors.white, bg = colors.grey }, + c = { fg = colors.black, bg = colors.white }, + z = { fg = colors.white, bg = colors.black }, + }, + insert = { a = { fg = colors.black, bg = colors.light_green } }, + visual = { a = { fg = colors.black, bg = colors.orange } }, + replace = { a = { fg = colors.black, bg = colors.green } }, +} + +local empty = require('lualine.component'):extend() +function empty:draw(default_highlight) + self.status = '' + self.applied_separator = '' + self:apply_highlights(default_highlight) + self:apply_section_separators() + return self.status +end + +-- Put proper separators and gaps between components in sections +local function process_sections(sections) + for name, section in pairs(sections) do + local left = name:sub(9, 10) < 'x' + for pos = 1, name ~= 'lualine_z' and #section or #section - 1 do + table.insert(section, pos * 2, { empty, color = { fg = colors.white, bg = colors.white } }) + end + for id, comp in ipairs(section) do + if type(comp) ~= 'table' then + comp = { comp } + section[id] = comp + end + comp.separator = left and { right = '' } or { left = '' } + end + end + return sections +end + +local function search_result() + if vim.v.hlsearch == 0 then + return '' + end + local last_search = vim.fn.getreg('/') + if not last_search or last_search == '' then + return '' + end + local searchcount = vim.fn.searchcount { maxcount = 9999 } + return last_search .. '(' .. searchcount.current .. '/' .. searchcount.total .. ')' +end + +local function modified() + if vim.bo.modified then + return '+' + elseif vim.bo.modifiable == false or vim.bo.readonly == true then + return '-' + end + return '' +end + +require('lualine').setup { + options = { + theme = theme, + component_separators = '', + section_separators = { left = '', right = '' }, + }, + sections = process_sections { + lualine_a = { 'mode' }, + lualine_b = { + 'branch', + 'diff', + { + 'diagnostics', + source = { 'nvim' }, + sections = { 'error' }, + diagnostics_color = { error = { bg = colors.red, fg = colors.white } }, + }, + { + 'diagnostics', + source = { 'nvim' }, + sections = { 'warn' }, + diagnostics_color = { warn = { bg = colors.orange, fg = colors.white } }, + }, + { 'filename', file_status = false, path = 1 }, + { modified, color = { bg = colors.red } }, + { + '%w', + cond = function() + return vim.wo.previewwindow + end, + }, + { + '%r', + cond = function() + return vim.bo.readonly + end, + }, + { + '%q', + cond = function() + return vim.bo.buftype == 'quickfix' + end, + }, + }, + lualine_c = {}, + lualine_x = {}, + lualine_y = { search_result, 'filetype' }, + lualine_z = { '%l:%c', '%p%%/%L' }, + }, + inactive_sections = { + lualine_c = { '%f %y %m' }, + lualine_x = {}, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine.lua new file mode 100644 index 00000000..60930492 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine.lua @@ -0,0 +1,656 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local M = {} + +local lualine_require = require('lualine_require') +local modules = lualine_require.lazy_require { + highlight = 'lualine.highlight', + loader = 'lualine.utils.loader', + utils_section = 'lualine.utils.section', + utils = 'lualine.utils.utils', + utils_notices = 'lualine.utils.notices', + config_module = 'lualine.config', + nvim_opts = 'lualine.utils.nvim_opts', +} +local config -- Stores currently applied config +local timers = { + stl_timer = vim.loop.new_timer(), + tal_timer = vim.loop.new_timer(), + wb_timer = vim.loop.new_timer(), + halt_stl_refresh = false, -- mutex ? + halt_tal_refresh = false, + halt_wb_refresh = false, +} + +local last_focus = {} +local refresh_real_curwin + +-- The events on which lualine redraws itself +local default_refresh_events = + 'WinEnter,BufEnter,BufWritePost,SessionLoadPost,FileChangedShellPost,VimResized,Filetype,CursorMoved,CursorMovedI,ModeChanged' +-- Helper for apply_transitional_separators() +--- finds first applied highlight group after str_checked in status +---@param status string : unprocessed statusline string +---@param str_checked number : position of how far status has been checked +---@return string|nil the hl group name or nil +local function find_next_hl(status, str_checked) + -- Gets the next valid hl group from str_checked + local hl_pos_start, hl_pos_end = status:find('%%#.-#', str_checked) + while true do + if not hl_pos_start then + return nil + end + -- When there are more that one hl group next to one another like + -- %#HL1#%#HL2#%#HL3# we need to return HL3. This makes that happen. + local next_start, next_end = status:find('^%%#.-#', hl_pos_end + 1) + if next_start == nil then + break + end + hl_pos_start, hl_pos_end = next_start, next_end + end + return status:sub(hl_pos_start + 2, hl_pos_end - 1) +end + +-- Helper for apply_transitional_separators() +--- applies transitional separator highlight + transitional separator +---@param status string : unprocessed statusline string +---@param str_checked number : position of how far status has been checked +---@param last_hl string : last applied hl group name before str_checked +---@param reverse boolean : reverse the hl group ( true for right separators ) +---@return string|nil concatenate separator highlight and transitional separator +local function fill_section_separator(status, is_focused, str_checked, last_hl, sep, reverse) + -- Inserts transitional separator along with transitional highlight + local next_hl = find_next_hl(status, str_checked) + if last_hl == nil then + last_hl = modules.highlight.get_stl_default_hl(is_focused) + end + if next_hl == nil then + next_hl = modules.highlight.get_stl_default_hl(is_focused) + end + if #next_hl == 0 or #last_hl == 0 then + return + end + local transitional_highlight = reverse -- lua ternary assignment x ? y : z + and modules.highlight.get_transitional_highlights(last_hl, next_hl) + or modules.highlight.get_transitional_highlights(next_hl, last_hl) + if transitional_highlight then + return transitional_highlight .. sep + end +end + +--- processes statusline string +--- replaces %z/Z{sep} with proper left/right separator highlight + sep +---@param status string : unprocessed statusline string +---@return string : processed statusline string +local function apply_transitional_separators(status, is_focused) + local status_applied = {} -- Collects all the pieces for concatenation + local last_hl -- Stores last highlight group that we found + local last_hl_reseted = false -- Whether last_hl is nil after reset + -- it after %= + local copied_pos = 1 -- Tracks how much we've copied over to status_applied + local str_checked = 1 -- Tracks where the searcher head is at + + -- Process entire status replace the %z{sep} & %Z{sep} placeholders + -- with proper transitional separator. + while str_checked ~= nil do + str_checked = status:find('%%', str_checked) + if str_checked == nil then + break + end + table.insert(status_applied, status:sub(copied_pos, str_checked - 1)) + -- -1 so we don't copy '%' + copied_pos = str_checked + local next_char = modules.utils.charAt(status, str_checked + 1) + if next_char == '#' then + -- %#hl_name# highlights + last_hl = status:match('^%%#(.-)#', str_checked) + str_checked = str_checked + #last_hl + 3 + elseif next_char == 'z' then + -- %z{sep} is marker for left separator and + local sep = status:match('^%%z{(.-)}', str_checked) + str_checked = str_checked + #sep + 4 -- 4 = len(%{}) + if not (last_hl == nil and last_hl_reseted) then + local trans_sep = fill_section_separator(status, is_focused, str_checked, last_hl, sep, false) + if trans_sep then + table.insert(status_applied, trans_sep) + end + end + if last_hl_reseted then + last_hl_reseted = false + end + copied_pos = str_checked + elseif next_char == 'Z' then + -- %Z{sep} is marker for right separator and + local sep = status:match('^%%Z{(.-)}', str_checked) + str_checked = str_checked + #sep + 4 -- 4 = len(%{}) + if status:find('^%%z', str_checked) or status:find('^%%<%%Z', str_checked) then + -- When transitional right_sep and left_sep are right next to each other + -- and in this exact order skip the left sep as we can't draw both. + str_checked = status:find('}', str_checked) + 1 + end + local trans_sep = fill_section_separator(status, is_focused, str_checked, last_hl, sep, true) + if trans_sep then + table.insert(status_applied, trans_sep) + end + copied_pos = str_checked + elseif next_char == '%' then + str_checked = str_checked + 2 -- Skip the following % too + elseif next_char == '=' and last_hl and (last_hl:find('^lualine_a') or last_hl:find('^lualine_b')) then + -- TODO: Fix this properly + -- This check for lualine_a and lualine_b is dumb. It doesn't guarantee + -- c or x section isn't present. Worst case scenario after this patch + -- we have another visual bug that occurs less frequently. + -- Annoying Edge Cases + last_hl = nil + last_hl_reseted = true + str_checked = str_checked + 1 -- Skip the following % too + else + str_checked = str_checked + 1 -- Push it forward to avoid inf loop + end + end + table.insert(status_applied, status:sub(copied_pos)) -- Final chunk + return table.concat(status_applied) +end + +--- creates the statusline string +---@param sections table : section config where components are replaced with +--- component objects +---@param is_focused boolean : whether being evaluated for focused window or not +---@return string statusline string +local statusline = modules.utils.retry_call_wrap(function(sections, is_focused, is_winbar) + -- The sequence sections should maintain [SECTION_SEQUENCE] + local section_sequence = { 'a', 'b', 'c', 'x', 'y', 'z' } + local status = {} + local applied_midsection_divider = false + local applied_trunc = false + for _, section_name in ipairs(section_sequence) do + if sections['lualine_' .. section_name] then + -- insert highlight+components of this section to status_builder + local section_data = + modules.utils_section.draw_section(sections['lualine_' .. section_name], section_name, is_focused) + if #section_data > 0 then + if not applied_midsection_divider and section_name > 'c' then + applied_midsection_divider = true + section_data = modules.highlight.format_highlight('c', is_focused) .. '%=' .. section_data + end + if not applied_trunc and section_name > 'b' then + applied_trunc = true + section_data = '%<' .. section_data + end + table.insert(status, section_data) + end + end + end + if applied_midsection_divider == false and config.options.always_divide_middle ~= false and not is_winbar then + -- When non of section x,y,z is present + table.insert(status, modules.highlight.format_highlight('c', is_focused) .. '%=') + end + return apply_transitional_separators(table.concat(status), is_focused) +end) + +--- check if any extension matches the filetype and return proper sections +---@param current_ft string : filetype name of current file +---@param is_focused boolean : whether being evaluated for focused window or not +---@return table|nil : (section_table) section config where components are replaced with +--- component objects +-- TODO: change this so it uses a hash table instead of iteration over list +-- to improve redraws. Add buftype / bufname for extensions +-- or some kind of cond ? +local function get_extension_sections(current_ft, is_focused, sec_name) + for _, extension in ipairs(config.extensions) do + if vim.tbl_contains(extension.filetypes, current_ft) then + if is_focused then + return extension[sec_name] + else + return extension['inactive_' .. sec_name] or extension[sec_name] + end + end + end + return nil +end + +---@return string statusline string for tabline +local function tabline() + return statusline(config.tabline, 3) +end + +local function notify_theme_error(theme_name) + local message_template = theme_name ~= 'auto' + and [[ +### options.theme +Theme `%s` not found, falling back to `auto`. Check if spelling is right. +]] + or [[ +### options.theme +Theme `%s` failed, falling back to `gruvbox`. +This shouldn't happen. +Please report the issue at https://github.com/nvim-lualine/lualine.nvim/issues . +Also provide what colorscheme you're using. +]] + modules.utils_notices.add_notice(string.format(message_template, theme_name)) +end + +--- Sets up theme by defining hl groups and setting theme cache in 'highlight.lua'. +--- Uses 'options.theme' variable to apply the theme: +--- - If the value is a string, it'll load a theme of that name. +--- - If it's a table, it's directly used as the theme. +--- If loading the theme fails, this falls back to 'auto' theme. +--- If the 'auto' theme also fails, this falls back to 'gruvbox' theme. +--- Also sets up auto command to reload lualine on ColorScheme or background changes. +local function setup_theme() + local function get_theme_from_config() + local theme_name = config.options.theme + if type(theme_name) == 'string' then + local ok, theme = pcall(modules.loader.load_theme, theme_name) + if ok and theme then + return theme + end + elseif type(theme_name) == 'table' then + -- use the provided theme as-is + return config.options.theme + elseif type(theme_name) == 'function' then + -- call function and use returned (dynamic) theme as-is + return config.options.theme() + end + if theme_name ~= 'auto' then + notify_theme_error(theme_name) + local ok, theme = pcall(modules.loader.load_theme, 'auto') + if ok and theme then + return theme + end + end + notify_theme_error('auto') + return modules.loader.load_theme('gruvbox') + end + local theme = get_theme_from_config() + modules.highlight.create_highlight_groups(theme) + vim.cmd([[autocmd lualine ColorScheme * lua require'lualine'.setup() + autocmd lualine OptionSet background lua require'lualine'.setup()]]) +end + +---@alias StatusDispatchSecs +---| 'sections' +---| 'winbar' +--- generates lualine.statusline & lualine.winbar function +--- creates a closer that can draw sections of sec_name. +---@param sec_name StatusDispatchSecs +---@return function(focused:bool):string +local function status_dispatch(sec_name) + return function(focused) + local retval + local current_ft = refresh_real_curwin + and vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(refresh_real_curwin), 'filetype') + or vim.bo.filetype + local is_focused = focused ~= nil and focused or modules.utils.is_focused() + if + vim.tbl_contains( + config.options.disabled_filetypes[(sec_name == 'sections' and 'statusline' or sec_name)], + current_ft + ) + then + -- disable on specific filetypes + return nil + end + local extension_sections = get_extension_sections(current_ft, is_focused, sec_name) + if extension_sections ~= nil then + retval = statusline(extension_sections, is_focused, sec_name == 'winbar') + else + retval = statusline(config[(is_focused and '' or 'inactive_') .. sec_name], is_focused, sec_name == 'winbar') + end + return retval + end +end + +---@alias LualineRefreshOptsKind +---| 'all' +---| 'tabpage' +---| 'window' +---@alias LualineRefreshOptsPlace +---| 'statusline' +---| 'tabline' +---| 'winbar' +---@class LualineRefreshOpts +---@field scope LualineRefreshOptsKind +---@field place LualineRefreshOptsPlace[] +---@field trigger 'autocmd'|'autocmd_redired'|'timer'|'unknown' +--- Refresh contents of lualine +---@param opts LualineRefreshOpts +local function refresh(opts) + if opts == nil then + opts = {} + end + opts = vim.tbl_extend('keep', opts, { + scope = 'tabpage', + place = { 'statusline', 'winbar', 'tabline' }, + trigger = 'unknown', + }) + + -- updating statusline in autocommands context seems to trigger 100 different bugs + -- lets just defer it to a timer context and update there + -- Since updating stl in command mode doesn't take effect + -- refresh ModeChanged command in autocmd context as exception. + -- workaround for + -- https://github.com/neovim/neovim/issues/15300 + -- https://github.com/neovim/neovim/issues/19464 + -- https://github.com/nvim-lualine/lualine.nvim/issues/753 + -- https://github.com/nvim-lualine/lualine.nvim/issues/751 + -- https://github.com/nvim-lualine/lualine.nvim/issues/755 + -- https://github.com/neovim/neovim/issues/19472 + -- https://github.com/nvim-lualine/lualine.nvim/issues/791 + if + opts.trigger == 'autocmd' + and vim.v.event.new_mode ~= 'c' + -- scheduling in op-pending mode seems to call the callback forever. + -- so this is restricted in op-pending mode. + -- https://github.com/neovim/neovim/issues/22263 + -- https://github.com/nvim-lualine/lualine.nvim/issues/967 + -- note this breaks mode component while switching to op-pending mode + and not vim.tbl_contains({ 'no', 'nov', 'noV' }, vim.v.event.new_mode) + and not vim.tbl_contains({ 'no', 'nov', 'noV' }, vim.v.event.old_mode) + then + opts.trigger = 'autocmd_redired' + vim.schedule(function() + M.refresh(opts) + end) + return + end + + local wins = {} + local old_actual_curwin = vim.g.actual_curwin + + -- ignore focus on filetypes listed in options.ignore_focus + local curwin = vim.api.nvim_get_current_win() + local curtab = vim.api.nvim_get_current_tabpage() + if last_focus[curtab] == nil or not vim.api.nvim_win_is_valid(last_focus[curtab]) then + if + not vim.tbl_contains( + config.options.ignore_focus, + vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(curwin), 'filetype') + ) + then + last_focus[curtab] = curwin + else + local tab_wins = vim.api.nvim_tabpage_list_wins(curtab) + if #tab_wins == 1 then + last_focus[curtab] = curwin + else + local focusable_win = curwin + for _, win in ipairs(tab_wins) do + if + not vim.tbl_contains( + config.options.ignore_focus, + vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(win), 'filetype') + ) + then + focusable_win = win + break + end + end + last_focus[curtab] = focusable_win + end + end + else + if + not vim.tbl_contains( + config.options.ignore_focus, + vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(curwin), 'filetype') + ) + then + last_focus[curtab] = curwin + end + end + vim.g.actual_curwin = last_focus[curtab] + + -- gather which windows needs update + if opts.scope == 'all' then + if vim.tbl_contains(opts.place, 'statusline') or vim.tbl_contains(opts.place, 'winbar') then + wins = vim.tbl_filter(function(win) + return vim.fn.win_gettype(win) ~= 'popup' + end, vim.api.nvim_list_wins()) + end + elseif opts.scope == 'tabpage' then + if vim.tbl_contains(opts.place, 'statusline') or vim.tbl_contains(opts.place, 'winbar') then + wins = vim.tbl_filter(function(win) + return vim.fn.win_gettype(win) ~= 'popup' + end, vim.api.nvim_tabpage_list_wins(0)) + end + elseif opts.scope == 'window' then + wins = { curwin } + end + + -- update them + if not timers.halt_stl_refresh and vim.tbl_contains(opts.place, 'statusline') then + for _, win in ipairs(wins) do + refresh_real_curwin = config.options.globalstatus and last_focus[curtab] or win + local set_win = config.options.globalstatus + and vim.fn.win_gettype(refresh_real_curwin) == 'popup' + and refresh_real_curwin + or win + local stl_cur = vim.api.nvim_win_call(refresh_real_curwin, M.statusline) + local stl_last = modules.nvim_opts.get_cache('statusline', { window = set_win }) + if stl_cur or stl_last then + modules.nvim_opts.set('statusline', stl_cur, { window = set_win }) + end + end + end + if not timers.halt_wb_refresh and vim.tbl_contains(opts.place, 'winbar') then + for _, win in ipairs(wins) do + refresh_real_curwin = win + if vim.api.nvim_win_get_height(win) > 1 then + local wbr_cur = vim.api.nvim_win_call(refresh_real_curwin, M.winbar) + local wbr_last = modules.nvim_opts.get_cache('winbar', { window = win }) + if wbr_cur or wbr_last then + modules.nvim_opts.set('winbar', wbr_cur, { window = win }) + end + end + end + end + if not timers.halt_tal_refresh and vim.tbl_contains(opts.place, 'tabline') then + refresh_real_curwin = curwin + local tbl_cur = vim.api.nvim_win_call(curwin, tabline) + local tbl_last = modules.nvim_opts.get_cache('tabline', { global = true }) + if tbl_cur or tbl_last then + modules.nvim_opts.set('tabline', tbl_cur, { global = true }) + end + end + + vim.g.actual_curwin = old_actual_curwin + refresh_real_curwin = nil +end + +--- Sets &tabline option to lualine +---@param hide boolean|nil if should hide tabline +local function set_tabline(hide) + vim.loop.timer_stop(timers.tal_timer) + timers.halt_tal_refresh = true + vim.cmd([[augroup lualine_tal_refresh | exe "autocmd!" | augroup END]]) + if not hide and next(config.tabline) ~= nil then + vim.loop.timer_start( + timers.tal_timer, + 0, + config.options.refresh.tabline, + modules.utils.timer_call(timers.tal_timer, 'lualine_tal_refresh', function() + refresh { kind = 'tabpage', place = { 'tabline' }, trigger = 'timer' } + end, 3, 'lualine: Failed to refresh tabline') + ) + modules.utils.define_autocmd( + default_refresh_events, + '*', + "call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['tabline'], 'trigger': 'autocmd'})", + 'lualine_tal_refresh' + ) + modules.nvim_opts.set('showtabline', 2, { global = true }) + timers.halt_tal_refresh = false + else + modules.nvim_opts.restore('tabline', { global = true }) + modules.nvim_opts.restore('showtabline', { global = true }) + end +end + +--- Sets &statusline option to lualine +--- adds auto command to redraw lualine on VimResized event +---@param hide boolean|nil if should hide statusline +local function set_statusline(hide) + vim.loop.timer_stop(timers.stl_timer) + timers.halt_stl_refresh = true + vim.cmd([[augroup lualine_stl_refresh | exe "autocmd!" | augroup END]]) + if not hide and (next(config.sections) ~= nil or next(config.inactive_sections) ~= nil) then + if vim.go.statusline == '' then + modules.nvim_opts.set('statusline', '%#Normal#', { global = true }) + end + if config.options.globalstatus then + modules.nvim_opts.set('laststatus', 3, { global = true }) + vim.loop.timer_start( + timers.stl_timer, + 0, + config.options.refresh.statusline, + modules.utils.timer_call(timers.stl_timer, 'lualine_stl_refresh', function() + refresh { kind = 'window', place = { 'statusline' }, trigger = 'timer' } + end, 3, 'lualine: Failed to refresh statusline') + ) + modules.utils.define_autocmd( + default_refresh_events, + '*', + "call v:lua.require'lualine'.refresh({'kind': 'window', 'place': ['statusline'], 'trigger': 'autocmd'})", + 'lualine_stl_refresh' + ) + else + modules.nvim_opts.set('laststatus', 2, { global = true }) + vim.loop.timer_start( + timers.stl_timer, + 0, + config.options.refresh.statusline, + modules.utils.timer_call(timers.stl_timer, 'lualine_stl_refresh', function() + refresh { kind = 'tabpage', place = { 'statusline' }, trigger = 'timer' } + end, 3, 'lualine: Failed to refresh statusline') + ) + modules.utils.define_autocmd( + default_refresh_events, + '*', + "call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['statusline'], 'trigger': 'autocmd'})", + 'lualine_stl_refresh' + ) + end + timers.halt_stl_refresh = false + else + modules.nvim_opts.restore('statusline', { global = true }) + for _, win in ipairs(vim.api.nvim_list_wins()) do + modules.nvim_opts.restore('statusline', { window = win }) + end + modules.nvim_opts.restore('laststatus', { global = true }) + end +end + +--- Sets &winbar option to lualine +---@param hide boolean|nil if should unset winbar +local function set_winbar(hide) + vim.loop.timer_stop(timers.wb_timer) + timers.halt_wb_refresh = true + vim.cmd([[augroup lualine_wb_refresh | exe "autocmd!" | augroup END]]) + if not hide and (next(config.winbar) ~= nil or next(config.inactive_winbar) ~= nil) then + vim.loop.timer_start( + timers.wb_timer, + 0, + config.options.refresh.winbar, + modules.utils.timer_call(timers.wb_timer, 'lualine_wb_refresh', function() + refresh { kind = 'tabpage', place = { 'winbar' }, trigger = 'timer' } + end, 3, 'lualine: Failed to refresh winbar') + ) + modules.utils.define_autocmd( + default_refresh_events, + '*', + "call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['winbar'], 'trigger': 'autocmd'})", + 'lualine_wb_refresh' + ) + timers.halt_wb_refresh = false + elseif vim.fn.has('nvim-0.8') == 1 then + modules.nvim_opts.restore('winbar', { global = true }) + for _, win in ipairs(vim.api.nvim_list_wins()) do + modules.nvim_opts.restore('winbar', { window = win }) + end + end +end + +---@alias LualineHideOptsPlace +---| 'statusline' +---| 'tabline' +---| 'winbar' +---@class LualineHideOpts +---@field place LualineHideOptsPlace[] +---@field unhide boolean +---@param opts LualineHideOpts +local function hide(opts) + if opts == nil then + opts = {} + end + opts = vim.tbl_extend('keep', opts, { + place = { 'statusline', 'tabline', 'winbar' }, + unhide = false, + }) + local hide_fn = { + statusline = set_statusline, + tabline = set_tabline, + winbar = set_winbar, + } + for _, place in ipairs(opts.place) do + if hide_fn[place] then + hide_fn[place](not opts.unhide) + end + end +end + +--- Check neovim compatibilitu +local function verify_nvim_version() + if vim.fn.has('nvim-0.7') == 1 then + return true + end + modules.utils_notices.add_notice([[ +### Incompatible Neovim version +Lualine supports neovim 0.7 and up. It seems you're using a older version. +Please update to newer version. Or if you have atleast neovim 0.5 you +can use older compatible versions of lualine using compat tags like +`compat-nvim-0.5`, `compat-nvim-0.6`. +]]) + return false +end + +-- lualine.setup function +--- sets new user config +--- This function doesn't load components/theme etc... They are done before +--- first statusline redraw and after new config. This is more efficient when +--- lualine config is done in several setup calls as chunks. This way +--- we don't initialize components just to throw them away. Instead they are +--- initialized when we know we will use them. +--- sets &last_status to 2 +---@param user_config table table +local function setup(user_config) + if package.loaded['lualine.utils.notices'] then + -- When notices module is not loaded there are no notices to clear. + modules.utils_notices.clear_notices() + end + if verify_nvim_version() then + config = modules.config_module.apply_configuration(user_config) + vim.cmd([[augroup lualine | exe "autocmd!" | augroup END]]) + setup_theme() + -- load components & extensions + modules.loader.load_all(config) + set_statusline() + set_tabline() + set_winbar() + end + if package.loaded['lualine.utils.notices'] then + modules.utils_notices.notice_message_startup() + end +end + +M = { + setup = setup, + statusline = status_dispatch('sections'), + tabline = tabline, + get_config = modules.config_module.get_config, + refresh = refresh, + winbar = status_dispatch('winbar'), + hide = hide, +} + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/component.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/component.lua new file mode 100644 index 00000000..a42f589b --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/component.lua @@ -0,0 +1,291 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local lualine_require = require('lualine_require') +local require = lualine_require.require +local M = require('lualine.utils.class'):extend() +local modules = lualine_require.lazy_require { + highlight = 'lualine.highlight', + utils_notices = 'lualine.utils.notices', + fn_store = 'lualine.utils.fn_store', +} + +-- Used to provide a unique id for each component +local component_no = 1 +function M._reset_components() + component_no = 1 +end + +-- variable to store component output for manipulation +M.status = '' + +function M:__tostring() + local str = 'Component: ' .. self.options.component_name + if self.debug then + str = str .. '\n---------------------\n' .. vim.inspect(self) + end + return str +end + +M.__is_lualine_component = true + +---initialize new component +---@param options table options for component +function M:init(options) + self.options = options or {} + component_no = component_no + 1 + if not self.options.component_name then + self.options.component_name = tostring(component_no) + end + self.component_no = component_no + self:set_separator() + self:create_option_highlights() + self:set_on_click() +end + +---sets the default separator for component based on whether the component +---is in left sections or right sections when separator option is omitted. +function M:set_separator() + if self.options.separator == nil then + if self.options.component_separators then + if self.options.self.section < 'x' then + self.options.separator = self.options.component_separators.left + else + self.options.separator = self.options.component_separators.right + end + end + end +end + +---creates hl group from color option +function M:create_option_highlights() + -- set custom highlights + if self.options.color then + self.options.color_highlight = self:create_hl(self.options.color) + end + -- setup icon highlight + if type(self.options.icon) == 'table' and self.options.icon.color then + self.options.icon_color_highlight = self:create_hl(self.options.icon.color) + end +end + +---Setup on click function so they can be added during drawing. +function M:set_on_click() + if self.options.on_click ~= nil then + if vim.fn.has('nvim-0.8') == 0 then + modules.utils_notices.add_notice( + '### Options.on_click\nSorry `on_click` can only be used in neovim 0.8 or higher.\n' + ) + self.options.on_click = nil + return + end + self.on_click_id = modules.fn_store.register_fn(self.component_no, self.options.on_click) + end +end + +---adds spaces to left and right of a component +function M:apply_padding() + local padding = self.options.padding + local l_padding, r_padding + if padding == nil then + padding = 1 + end + if type(padding) == 'number' then + l_padding, r_padding = padding, padding + elseif type(padding) == 'table' then + l_padding, r_padding = padding.left, padding.right + end + if l_padding then + if self.status:find('%%#.*#') == 1 then + -- When component has changed the highlight at beginning + -- we will add the padding after the highlight + local pre_highlight = vim.fn.matchlist(self.status, [[\(%#.\{-\}#\)]])[2] + self.status = pre_highlight .. string.rep(' ', l_padding) .. self.status:sub(#pre_highlight + 1, #self.status) + else + self.status = string.rep(' ', l_padding) .. self.status + end + end + if r_padding then + self.status = self.status .. string.rep(' ', r_padding) + end +end + +---applies custom highlights for component +function M:apply_highlights(default_highlight) + if self.options.color_highlight then + local hl_fmt + hl_fmt, M.color_fn_cache = self:format_hl(self.options.color_highlight) + self.status = hl_fmt .. self.status + end + if type(self.options.separator) ~= 'table' and self.status:find('%%#') then + -- Apply default highlight only when we aren't applying trans sep and + -- the component has changed it's hl. Since we won't be applying + -- regular sep in those cases so ending with default hl isn't necessary + self.status = self.status .. default_highlight + -- Also put it in applied sep so when sep get striped so does the hl + self.applied_separator = default_highlight + end + -- Prepend default hl when the component doesn't start with hl otherwise + -- color in previous component can cause side effect + if not self.status:find('^%%#') then + self.status = default_highlight .. self.status + end +end + +---apply icon to component (appends/prepends component with icon) +function M:apply_icon() + local icon = self.options.icon + if self.options.icons_enabled and icon then + if type(icon) == 'table' then + icon = icon[1] + end + if + self.options.icon_color_highlight + and type(self.options.icon) == 'table' + and self.options.icon.align == 'right' + then + self.status = table.concat { + self.status, + ' ', + self:format_hl(self.options.icon_color_highlight), + icon, + self:get_default_hl(), + } + elseif self.options.icon_color_highlight then + self.status = table.concat { + self:format_hl(self.options.icon_color_highlight), + icon, + self:get_default_hl(), + ' ', + self.status, + } + elseif type(self.options.icon) == 'table' and self.options.icon.align == 'right' then + self.status = table.concat({ self.status, icon }, ' ') + else + self.status = table.concat({ icon, self.status }, ' ') + end + end +end + +---apply separator at end of component only when +---custom highlights haven't affected background +function M:apply_separator() + local separator = self.options.separator + if type(separator) == 'table' then + if self.options.separator[2] == '' then + if self.options.self.section < 'x' then + separator = self.options.component_separators.left + else + separator = self.options.component_separators.right + end + else + return + end + end + if separator and #separator > 0 then + self.status = self.status .. separator + self.applied_separator = self.applied_separator .. separator + end +end + +---apply transitional separator for the component +function M:apply_section_separators() + if type(self.options.separator) ~= 'table' then + return + end + if self.options.separator.left ~= nil and self.options.separator.left ~= '' then + self.status = string.format('%%z{%s}%s', self.options.separator.left, self.status) + self.strip_previous_separator = true + end + if self.options.separator.right ~= nil and self.options.separator.right ~= '' then + self.status = string.format('%s%%Z{%s}', self.status, self.options.separator.right) + end +end + +---Add on click function description to already drawn item +function M:apply_on_click() + if self.on_click_id then + self.status = self:format_fn(self.on_click_id, self.status) + end +end + +---remove separator from tail of this component. +---called by lualine.utils.sections.draw_section to manage unnecessary separators +function M:strip_separator() + if not self.applied_separator then + self.applied_separator = '' + end + self.status = self.status:sub(1, (#self.status - #self.applied_separator)) + self.applied_separator = nil + return self.status +end + +function M:get_default_hl() + if self.options.color_highlight then + return self:format_hl(self.options.color_highlight) + elseif self.default_hl then + return self.default_hl + else + return modules.highlight.format_highlight(self.options.self.section) + end +end + +---create a lualine highlight for color +---@param color table|string|function defined color for hl +---@param hint string|nil hint for hl name +---@return table an identifier to later retrieve the hl for application +function M:create_hl(color, hint) + hint = hint and self.options.component_name .. '_' .. hint or self.options.component_name + return modules.highlight.create_component_highlight_group(color, hint, self.options, false) +end + +---Get stl formatted hl group for hl_token +---@param hl_token table identifier received from create_hl or create_component_highlight_group +---@return string stl formatted hl group for hl_token +function M:format_hl(hl_token) + return modules.highlight.component_format_highlight(hl_token) +end + +---Wrap str with click format for function of id +---@param id number +---@param str string +---@return string +function M:format_fn(id, str) + return string.format("%%%d@v:lua.require'lualine.utils.fn_store'.call_fn@%s%%T", id, str) +end + +-- luacheck: push no unused args +---actual function that updates a component. Must be overwritten with component functionality +function M:update_status(is_focused) end +-- luacheck: pop + +---driver code of the class +---@param default_highlight string default hl group of section where component resides +---@param is_focused boolean|number whether drawing for active or inactive statusline. +---@return string stl formatted rendering string for component +function M:draw(default_highlight, is_focused) + self.status = '' + self.applied_separator = '' + + if self.options.cond ~= nil and self.options.cond() ~= true then + return self.status + end + self.default_hl = default_highlight + local status = self:update_status(is_focused) + if self.options.fmt then + status = self.options.fmt(status or '', self) + end + if type(status) == 'string' and (#status > 0 or self.options.draw_empty) then + self.status = status + if #status > 0 then + self:apply_icon() + self:apply_padding() + end + self:apply_on_click() + self:apply_highlights(default_highlight) + self:apply_section_separators() + self:apply_separator() + end + return self.status +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/branch/git_branch.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/branch/git_branch.lua new file mode 100644 index 00000000..dcc9775f --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/branch/git_branch.lua @@ -0,0 +1,149 @@ +local M = {} + +local require = require('lualine_require').require +local utils = require('lualine.utils.utils') + +-- vars +local current_git_branch = '' +local current_git_dir = '' +local branch_cache = {} -- stores last known branch for a buffer +local active_bufnr = '0' +-- os specific path separator +local sep = package.config:sub(1, 1) +-- event watcher to watch head file +-- Use file watch for non Windows and poll for Windows. +-- Windows doesn't like file watch for some reason. +local file_changed = sep ~= '\\' and vim.loop.new_fs_event() or vim.loop.new_fs_poll() +local git_dir_cache = {} -- Stores git paths that we already know of + +---sets git_branch variable to branch name or commit hash if not on branch +---@param head_file string full path of .git/HEAD file +local function get_git_head(head_file) + local f_head = io.open(head_file) + if f_head then + local HEAD = f_head:read() + f_head:close() + local branch = HEAD:match('ref: refs/heads/(.+)$') + if branch then + current_git_branch = branch + else + current_git_branch = HEAD:sub(1, 6) + end + end + return nil +end + +---updates the current value of git_branch and sets up file watch on HEAD file +local function update_branch() + active_bufnr = tostring(vim.api.nvim_get_current_buf()) + file_changed:stop() + local git_dir = current_git_dir + if git_dir and #git_dir > 0 then + local head_file = git_dir .. sep .. 'HEAD' + get_git_head(head_file) + file_changed:start( + head_file, + sep ~= '\\' and {} or 1000, + vim.schedule_wrap(function() + -- reset file-watch + update_branch() + end) + ) + else + -- set to '' when git dir was not found + current_git_branch = '' + end + branch_cache[vim.api.nvim_get_current_buf()] = current_git_branch +end + +---updates the current value of current_git_branch and sets up file watch on HEAD file if value changed +local function update_current_git_dir(git_dir) + if current_git_dir ~= git_dir then + current_git_dir = git_dir + update_branch() + end +end + +---returns full path to git directory for dir_path or current directory +---@param dir_path string|nil +---@return string|nil +function M.find_git_dir(dir_path) + local git_dir = vim.env.GIT_DIR + if git_dir then + update_current_git_dir(git_dir) + return git_dir + end + + -- get file dir so we can search from that dir + local file_dir = dir_path or vim.fn.expand('%:p:h') + + -- extract correct file dir from terminals + if file_dir and file_dir:match('term://.*') then + file_dir = vim.fn.expand(file_dir:gsub('term://(.+)//.+', '%1')) + end + + local root_dir = file_dir + -- Search upward for .git file or folder + while root_dir do + if git_dir_cache[root_dir] then + git_dir = git_dir_cache[root_dir] + break + end + local git_path = root_dir .. sep .. '.git' + local git_file_stat = vim.loop.fs_stat(git_path) + if git_file_stat then + if git_file_stat.type == 'directory' then + git_dir = git_path + elseif git_file_stat.type == 'file' then + -- separate git-dir or submodule is used + local file = io.open(git_path) + if file then + git_dir = file:read() + git_dir = git_dir and git_dir:match('gitdir: (.+)$') + file:close() + end + -- submodule / relative file path + if git_dir and git_dir:sub(1, 1) ~= sep and not git_dir:match('^%a:.*$') then + git_dir = git_path:match('(.*).git') .. git_dir + end + end + if git_dir then + local head_file_stat = vim.loop.fs_stat(git_dir .. sep .. 'HEAD') + if head_file_stat and head_file_stat.type == 'file' then + break + else + git_dir = nil + end + end + end + root_dir = root_dir:match('(.*)' .. sep .. '.-') + end + + git_dir_cache[file_dir] = git_dir + if dir_path == nil then + update_current_git_dir(git_dir) + end + return git_dir +end + +---initializes git_branch module +function M.init() + -- run watch head on load so branch is present when component is loaded + M.find_git_dir() + -- update branch state of BufEnter as different Buffer may be on different repos + utils.define_autocmd('BufEnter', "lua require'lualine.components.branch.git_branch'.find_git_dir()") +end +function M.get_branch(bufnr) + if vim.g.actual_curbuf ~= nil and active_bufnr ~= vim.g.actual_curbuf then + -- Workaround for https://github.com/nvim-lualine/lualine.nvim/issues/286 + -- See upstream issue https://github.com/neovim/neovim/issues/15300 + -- Diff is out of sync re sync it. + M.find_git_dir() + end + if bufnr then + return branch_cache[bufnr] or '' + end + return current_git_branch +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/branch/init.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/branch/init.lua new file mode 100644 index 00000000..7e75544d --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/branch/init.lua @@ -0,0 +1,25 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local M = require('lualine.component'):extend() +local modules = require('lualine_require').lazy_require { + git_branch = 'lualine.components.branch.git_branch', + highlight = 'lualine.highlight', + utils = 'lualine.utils.utils', +} + +-- Initializer +M.init = function(self, options) + M.super.init(self, options) + if not self.options.icon then + self.options.icon = '' -- e0a0 + end + modules.git_branch.init() +end + +M.update_status = function(_, is_focused) + local buf = (not is_focused and vim.api.nvim_get_current_buf()) + local branch = modules.git_branch.get_branch(buf) + return modules.utils.stl_escape(branch) +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/buffers/buffer.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/buffers/buffer.lua new file mode 100644 index 00000000..c58c320d --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/buffers/buffer.lua @@ -0,0 +1,192 @@ +local Buffer = require('lualine.utils.class'):extend() + +local modules = require('lualine_require').lazy_require { + highlight = 'lualine.highlight', + utils = 'lualine.utils.utils', +} + +---initialize a new buffer from opts +---@param opts table +function Buffer:init(opts) + assert(opts.bufnr, 'Cannot create Buffer without bufnr') + self.bufnr = opts.bufnr + self.buf_index = opts.buf_index + self.options = opts.options + self.highlights = opts.highlights + self:get_props() +end + +function Buffer:is_current() + return vim.api.nvim_get_current_buf() == self.bufnr +end + +function Buffer:is_alternate() + return vim.fn.bufnr('#') == self.bufnr and not self:is_current() +end + +---setup icons, modified status for buffer +function Buffer:get_props() + self.file = modules.utils.stl_escape(vim.api.nvim_buf_get_name(self.bufnr)) + self.buftype = vim.api.nvim_buf_get_option(self.bufnr, 'buftype') + self.filetype = vim.api.nvim_buf_get_option(self.bufnr, 'filetype') + local modified = self.options.show_modified_status and vim.api.nvim_buf_get_option(self.bufnr, 'modified') + self.modified_icon = modified and self.options.symbols.modified or '' + self.alternate_file_icon = self:is_alternate() and self.options.symbols.alternate_file or '' + self.icon = '' + if self.options.icons_enabled then + local dev + local status, _ = pcall(require, 'nvim-web-devicons') + if not status then + dev, _ = '', '' + elseif self.filetype == 'TelescopePrompt' then + dev, _ = require('nvim-web-devicons').get_icon('telescope') + elseif self.filetype == 'fugitive' then + dev, _ = require('nvim-web-devicons').get_icon('git') + elseif self.filetype == 'vimwiki' then + dev, _ = require('nvim-web-devicons').get_icon('markdown') + elseif self.buftype == 'terminal' then + dev, _ = require('nvim-web-devicons').get_icon('zsh') + elseif vim.fn.isdirectory(self.file) == 1 then + dev, _ = self.options.symbols.directory, nil + else + dev, _ = require('nvim-web-devicons').get_icon(self.file, vim.fn.expand('#' .. self.bufnr .. ':e')) + end + if dev then + self.icon = dev .. ' ' + end + end +end + +---returns line configured for handling mouse click +---@param name string +---@return string +function Buffer:configure_mouse_click(name) + return string.format('%%%s@LualineSwitchBuffer@%s%%T', self.bufnr, name) +end + +---returns rendered buffer +---@return string +function Buffer:render() + local name = self:name() + if self.options.fmt then + name = self.options.fmt(name or '', self) + end + + if self.ellipse then -- show ellipsis + name = '...' + else + name = self:apply_mode(name) + end + name = Buffer.apply_padding(name, self.options.padding) + self.len = vim.fn.strchars(name) + + -- setup for mouse clicks + local line = self:configure_mouse_click(name) + -- apply highlight + line = modules.highlight.component_format_highlight(self.highlights[(self.current and 'active' or 'inactive')]) + .. line + + -- apply separators + if self.options.self.section < 'x' and not self.first then + local sep_before = self:separator_before() + line = sep_before .. line + self.len = self.len + vim.fn.strchars(sep_before) + elseif self.options.self.section >= 'x' and not self.last then + local sep_after = self:separator_after() + line = line .. sep_after + self.len = self.len + vim.fn.strchars(sep_after) + end + return line +end + +---apply separator before current buffer +---@return string +function Buffer:separator_before() + if self.current or self.aftercurrent then + return '%Z{' .. self.options.section_separators.left .. '}' + else + return self.options.component_separators.left + end +end + +---apply separator after current buffer +---@return string +function Buffer:separator_after() + if self.current or self.beforecurrent then + return '%z{' .. self.options.section_separators.right .. '}' + else + return self.options.component_separators.right + end +end + +---returns name of current buffer after filtering special buffers +---@return string +function Buffer:name() + if self.options.filetype_names[self.filetype] then + return self.options.filetype_names[self.filetype] + elseif self.buftype == 'help' then + return 'help:' .. vim.fn.fnamemodify(self.file, ':t:r') + elseif self.buftype == 'terminal' then + local match = string.match(vim.split(self.file, ' ')[1], 'term:.*:(%a+)') + return match ~= nil and match or vim.fn.fnamemodify(vim.env.SHELL, ':t') + elseif self.buftype == 'quickfix' then + local is_loclist = 0 ~= vim.fn.getloclist(0, { filewinid = 1 }).filewinid + return is_loclist and 'Location list' or 'Quickfix List' + elseif vim.fn.isdirectory(self.file) == 1 then + return vim.fn.fnamemodify(self.file, ':p:.') + elseif self.file == '' then + return '[No Name]' + end + + local name + if self.options.show_filename_only then + name = vim.fn.fnamemodify(self.file, ':t') + else + name = vim.fn.pathshorten(vim.fn.fnamemodify(self.file, ':p:.')) + end + if self.options.hide_filename_extension then + name = vim.fn.fnamemodify(name, ':r') + end + return name +end + +---adds spaces to left and right +function Buffer.apply_padding(str, padding) + local l_padding, r_padding = 1, 1 + if type(padding) == 'number' then + l_padding, r_padding = padding, padding + elseif type(padding) == 'table' then + l_padding, r_padding = padding.left or 0, padding.right or 0 + end + return string.rep(' ', l_padding) .. str .. string.rep(' ', r_padding) +end + +function Buffer:apply_mode(name) + if self.options.mode == 0 then + return string.format('%s%s%s%s', self.alternate_file_icon, self.icon, name, self.modified_icon) + end + + if self.options.mode == 1 then + return string.format('%s%s %s%s', self.alternate_file_icon, self.buf_index or '', self.icon, self.modified_icon) + end + + if self.options.mode == 2 then + return string.format( + '%s%s %s%s%s', + self.alternate_file_icon, + self.buf_index or '', + self.icon, + name, + self.modified_icon + ) + end + + if self.options.mode == 3 then + return string.format('%s%s %s%s', self.alternate_file_icon, self.bufnr or '', self.icon, self.modified_icon) + end + + -- if self.options.mode == 4 then + return string.format('%s%s %s%s%s', self.alternate_file_icon, self.bufnr or '', self.icon, name, self.modified_icon) +end + +return Buffer diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/buffers/init.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/buffers/init.lua new file mode 100644 index 00000000..af456610 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/buffers/init.lua @@ -0,0 +1,245 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local require = require('lualine_require').require +local Buffer = require('lualine.components.buffers.buffer') +local M = require('lualine.component'):extend() +local highlight = require('lualine.highlight') + +local default_options = { + show_filename_only = true, + hide_filename_extension = false, + show_modified_status = true, + mode = 0, + max_length = 0, + filetype_names = { + TelescopePrompt = 'Telescope', + dashboard = 'Dashboard', + packer = 'Packer', + fzf = 'FZF', + alpha = 'Alpha', + }, + use_mode_colors = false, + buffers_color = { + active = nil, + inactive = nil, + }, + symbols = { + modified = ' ●', + alternate_file = '#', + directory = '', + }, +} + +-- This function is duplicated in tabs +---returns the proper hl for buffer in section. Used for setting default highlights +---@param section string name of section buffers component is in +---@param is_active boolean +---@return string hl name +local function get_hl(section, is_active) + local suffix = is_active and highlight.get_mode_suffix() or '_inactive' + local section_redirects = { + lualine_x = 'lualine_c', + lualine_y = 'lualine_b', + lualine_z = 'lualine_a', + } + if section_redirects[section] then + section = highlight.highlight_exists(section .. suffix) and section or section_redirects[section] + end + return section .. suffix +end + +function M:init(options) + M.super.init(self, options) + -- if use_mode_colors is set, use a function so that the colors update + local default_active = options.use_mode_colors + and function() + return get_hl('lualine_' .. options.self.section, true) + end + or get_hl('lualine_' .. options.self.section, true) + default_options.buffers_color = { + active = default_active, + inactive = get_hl('lualine_' .. options.self.section, false), + } + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) + if self.options.component_name == 'buffers' then + self.highlights = { + active = self:create_hl(self.options.buffers_color.active, 'active'), + inactive = self:create_hl(self.options.buffers_color.inactive, 'inactive'), + } + end +end + +function M:new_buffer(bufnr, buf_index) + bufnr = bufnr or vim.api.nvim_get_current_buf() + buf_index = buf_index or '' + return Buffer:new { + bufnr = bufnr, + buf_index = buf_index, + options = self.options, + highlights = self.highlights, + } +end + +function M:buffers() + local buffers = {} + M.bufpos2nr = {} + for b = 1, vim.fn.bufnr('$') do + if vim.fn.buflisted(b) ~= 0 and vim.api.nvim_buf_get_option(b, 'buftype') ~= 'quickfix' then + buffers[#buffers + 1] = self:new_buffer(b, #buffers + 1) + M.bufpos2nr[#buffers] = b + end + end + + return buffers +end + +function M:update_status() + local data = {} + local buffers = self:buffers() + local current = -2 + -- mark the first, last, current, before current, after current buffers + -- for rendering + if buffers[1] then + buffers[1].first = true + end + if buffers[#buffers] then + buffers[#buffers].last = true + end + for i, buffer in ipairs(buffers) do + if buffer:is_current() then + buffer.current = true + current = i + end + end + if buffers[current - 1] then + buffers[current - 1].beforecurrent = true + end + if buffers[current + 1] then + buffers[current + 1].aftercurrent = true + end + + local max_length = self.options.max_length + if type(max_length) == 'function' then + max_length = max_length(self) + end + + if max_length == 0 then + max_length = math.floor(2 * vim.o.columns / 3) + end + local total_length + for i, buffer in pairs(buffers) do + if buffer.current then + current = i + end + end + -- start drawing from current buffer and draw left and right of it until + -- all buffers are drawn or max_length has been reached. + if current == -2 then + local b = self:new_buffer() + b.current = true + if self.options.self.section < 'x' then + b.last = true + if #buffers > 0 then + buffers[#buffers].last = nil + end + buffers[#buffers + 1] = b + current = #buffers + else + b.first = true + if #buffers > 0 then + buffers[1].first = nil + end + table.insert(buffers, 1, b) + current = 1 + end + end + local current_buffer = buffers[current] + data[#data + 1] = current_buffer:render() + total_length = current_buffer.len + local i = 0 + local before, after + while true do + i = i + 1 + before = buffers[current - i] + after = buffers[current + i] + local rendered_before, rendered_after + if before == nil and after == nil then + break + end + -- draw left most undrawn buffer if fits in max_length + if before then + rendered_before = before:render() + total_length = total_length + before.len + if total_length > max_length then + break + end + table.insert(data, 1, rendered_before) + end + -- draw right most undrawn buffer if fits in max_length + if after then + rendered_after = after:render() + total_length = total_length + after.len + if total_length > max_length then + break + end + data[#data + 1] = rendered_after + end + end + -- draw ellipsis (...) on relevant sides if all buffers don't fit in max_length + if total_length > max_length then + if before ~= nil then + before.ellipse = true + before.first = true + table.insert(data, 1, before:render()) + end + if after ~= nil then + after.ellipse = true + after.last = true + data[#data + 1] = after:render() + end + end + + return table.concat(data) +end + +function M:draw() + self.status = '' + self.applied_separator = '' + + if self.options.cond ~= nil and self.options.cond() ~= true then + return self.status + end + local status = self:update_status() + if type(status) == 'string' and #status > 0 then + self.status = status + self:apply_section_separators() + self:apply_separator() + end + return self.status +end + +function M.buffer_jump(buf_pos, bang) + if buf_pos == '$' then + buf_pos = #M.bufpos2nr + else + buf_pos = tonumber(buf_pos) + end + if buf_pos < 1 or buf_pos > #M.bufpos2nr then + if bang ~= '!' then + error('Error: Unable to jump buffer position out of range') + else + return + end + end + vim.api.nvim_set_current_buf(M.bufpos2nr[buf_pos]) +end + +vim.cmd([[ + function! LualineSwitchBuffer(bufnr, mouseclicks, mousebutton, modifiers) + execute ":buffer " . a:bufnr + endfunction + + command! -nargs=1 -bang LualineBuffersJump call v:lua.require'lualine.components.buffers'.buffer_jump(, "") +]]) + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/datetime.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/datetime.lua new file mode 100644 index 00000000..d1c702e5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/datetime.lua @@ -0,0 +1,31 @@ +-- Copyright (c) 2023 Willothy +-- MIT license, see LICENSE for more details. +local lualine_require = require('lualine_require') +local M = lualine_require.require('lualine.component'):extend() + +local default_options = { + -- default, us, uk, iso, or format (ex. "%d/%m/%Y ...") + style = 'default', +} + +function M:init(options) + M.super.init(self, options) + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) +end + +function M:update_status() + local fmt = self.options.style + if self.options.style == 'default' then + fmt = '%A, %B %d | %H:%M' + elseif self.options.style == 'us' then + fmt = '%m/%d/%Y' + elseif self.options.style == 'uk' then + fmt = '%d/%m/%Y' + elseif self.options.style == 'iso' then + fmt = '%Y-%m-%d' + end + + return os.date(fmt) +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diagnostics/config.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diagnostics/config.lua new file mode 100644 index 00000000..094f88ef --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diagnostics/config.lua @@ -0,0 +1,59 @@ +local require = require('lualine_require').require +local utils = require('lualine.utils.utils') +local M = {} + +-- default symbols for diagnostics component +M.symbols = { + icons = { + error = '󰅚 ', -- x000f015a + warn = '󰀪 ', -- x000f002a + info = '󰋽 ', -- x000f02fd + hint = '󰌶 ', -- x000f0336 + }, + no_icons = { error = 'E:', warn = 'W:', info = 'I:', hint = 'H:' }, +} + +-- default options for diagnostics component +M.options = { + colored = true, + update_in_insert = false, + always_visible = false, + sources = { 'nvim_diagnostic', 'coc' }, + sections = { 'error', 'warn', 'info', 'hint' }, +} + +function M.apply_default_colors(opts) + local default_diagnostics_color = { + error = { + fg = utils.extract_color_from_hllist( + { 'fg', 'sp' }, + { 'DiagnosticError', 'LspDiagnosticsDefaultError', 'DiffDelete' }, + '#e32636' + ), + }, + warn = { + fg = utils.extract_color_from_hllist( + { 'fg', 'sp' }, + { 'DiagnosticWarn', 'LspDiagnosticsDefaultWarning', 'DiffText' }, + '#ffa500' + ), + }, + info = { + fg = utils.extract_color_from_hllist( + { 'fg', 'sp' }, + { 'DiagnosticInfo', 'LspDiagnosticsDefaultInformation', 'Normal' }, + '#ffffff' + ), + }, + hint = { + fg = utils.extract_color_from_hllist( + { 'fg', 'sp' }, + { 'DiagnosticHint', 'LspDiagnosticsDefaultHint', 'DiffChange' }, + '#273faf' + ), + }, + } + opts.diagnostics_color = vim.tbl_deep_extend('keep', opts.diagnostics_color or {}, default_diagnostics_color) +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diagnostics/init.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diagnostics/init.lua new file mode 100644 index 00000000..5d8c9f41 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diagnostics/init.lua @@ -0,0 +1,111 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local lualine_require = require('lualine_require') +local modules = lualine_require.lazy_require { + default_config = 'lualine.components.diagnostics.config', + sources = 'lualine.components.diagnostics.sources', + highlight = 'lualine.highlight', + utils = 'lualine.utils.utils', + utils_notices = 'lualine.utils.notices', +} + +local M = lualine_require.require('lualine.component'):extend() + +M.diagnostics_sources = modules.sources.sources +M.get_diagnostics = modules.sources.get_diagnostics + +-- Initializer +function M:init(options) + -- Run super() + M.super.init(self, options) + -- Apply default options + modules.default_config.apply_default_colors(self.options) + self.options = vim.tbl_deep_extend('keep', self.options or {}, modules.default_config.options) + -- Apply default symbols + self.symbols = vim.tbl_extend( + 'keep', + self.options.symbols or {}, + self.options.icons_enabled ~= false and modules.default_config.symbols.icons + or modules.default_config.symbols.no_icons + ) + -- Initialize highlight groups + if self.options.colored then + self.highlight_groups = { + error = self:create_hl(self.options.diagnostics_color.error, 'error'), + warn = self:create_hl(self.options.diagnostics_color.warn, 'warn'), + info = self:create_hl(self.options.diagnostics_color.info, 'info'), + hint = self:create_hl(self.options.diagnostics_color.hint, 'hint'), + } + end + + -- Initialize variable to store last update so we can use it in insert + -- mode for no update_in_insert + self.last_diagnostics_count = {} + + -- Error out no source + if #self.options.sources < 1 then + modules.utils_notices.add_notice( + '### diagnostics.sources\n\nno sources for diagnostics configured.\nPlease specify which diagnostics source you want lualine to use with `sources` option.\n' + ) + end +end + +function M:update_status() + local bufnr = vim.api.nvim_get_current_buf() + local diagnostics_count + local result = {} + if self.options.update_in_insert or vim.api.nvim_get_mode().mode:sub(1, 1) ~= 'i' then + local error_count, warning_count, info_count, hint_count = 0, 0, 0, 0 + local diagnostic_data = modules.sources.get_diagnostics(self.options.sources) + -- sum all the counts + for _, data in pairs(diagnostic_data) do + error_count = error_count + data.error + warning_count = warning_count + data.warn + info_count = info_count + data.info + hint_count = hint_count + data.hint + end + diagnostics_count = { + error = error_count, + warn = warning_count, + info = info_count, + hint = hint_count, + } + -- Save count for insert mode + self.last_diagnostics_count[bufnr] = diagnostics_count + else -- Use cached count in insert mode with update_in_insert disabled + diagnostics_count = self.last_diagnostics_count[bufnr] or { error = 0, warn = 0, info = 0, hint = 0 } + end + + local always_visible = false + if type(self.options.always_visible) == 'boolean' then + always_visible = self.options.always_visible + elseif type(self.options.always_visible) == 'function' then + always_visible = self.options.always_visible() + end + + -- format the counts with symbols and highlights + if self.options.colored then + local colors, bgs = {}, {} + for name, hl in pairs(self.highlight_groups) do + colors[name] = self:format_hl(hl) + bgs[name] = modules.utils.extract_highlight_colors(colors[name]:match('%%#(.-)#'), 'bg') + end + local previous_section, padding + for _, section in ipairs(self.options.sections) do + if diagnostics_count[section] ~= nil and (always_visible or diagnostics_count[section] > 0) then + padding = previous_section and (bgs[previous_section] ~= bgs[section]) and ' ' or '' + previous_section = section + table.insert(result, colors[section] .. padding .. self.symbols[section] .. diagnostics_count[section]) + end + end + else + for _, section in ipairs(self.options.sections) do + if diagnostics_count[section] ~= nil and (always_visible or diagnostics_count[section] > 0) then + table.insert(result, self.symbols[section] .. diagnostics_count[section]) + end + end + end + return table.concat(result, ' ') +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diagnostics/sources.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diagnostics/sources.lua new file mode 100644 index 00000000..c0ef78c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diagnostics/sources.lua @@ -0,0 +1,100 @@ +local M = {} + +---functions that how how to retrieve diagnostics from specific source. +---returns error_count:number, warning_count:number, +--- info_count:number, hint_count:number +M.sources = { + nvim_lsp = function() + local error_count, warning_count, info_count, hint_count + local diagnostics = vim.diagnostic.get(0) + local count = { 0, 0, 0, 0 } + for _, diagnostic in ipairs(diagnostics) do + if vim.startswith(vim.diagnostic.get_namespace(diagnostic.namespace).name, 'vim.lsp') then + count[diagnostic.severity] = count[diagnostic.severity] + 1 + end + end + error_count = count[vim.diagnostic.severity.ERROR] + warning_count = count[vim.diagnostic.severity.WARN] + info_count = count[vim.diagnostic.severity.INFO] + hint_count = count[vim.diagnostic.severity.HINT] + return error_count, warning_count, info_count, hint_count + end, + nvim_workspace_diagnostic = function() + local diag_severity = vim.diagnostic.severity + + local function workspace_diag(severity) + local count = vim.diagnostic.get(nil, { severity = severity }) + return vim.tbl_count(count) + end + + return workspace_diag(diag_severity.ERROR), + workspace_diag(diag_severity.WARN), + workspace_diag(diag_severity.INFO), + workspace_diag(diag_severity.HINT) + end, + nvim_diagnostic = function() + local diagnostics = vim.diagnostic.get(0) + local count = { 0, 0, 0, 0 } + for _, diagnostic in ipairs(diagnostics) do + count[diagnostic.severity] = count[diagnostic.severity] + 1 + end + return count[vim.diagnostic.severity.ERROR], + count[vim.diagnostic.severity.WARN], + count[vim.diagnostic.severity.INFO], + count[vim.diagnostic.severity.HINT] + end, + coc = function() + local data = vim.b.coc_diagnostic_info + if data then + return data.error, data.warning, data.information, data.hint + else + return 0, 0, 0, 0 + end + end, + ale = function() + local ok, data = pcall(vim.fn['ale#statusline#Count'], vim.api.nvim_get_current_buf()) + if ok then + return data.error + data.style_error, data.warning + data.style_warning, data.info, 0 + else + return 0, 0, 0, 0 + end + end, + vim_lsp = function() + local ok, data = pcall(vim.fn['lsp#get_buffer_diagnostics_counts']) + if ok then + return data.error, data.warning, data.information, data.hint + else + return 0, 0, 0, 0 + end + end, +} + +---returns list of diagnostics count from all sources +---@param sources table list of sources +---@return table {{error_count, warning_count, info_count, hint_count}} +M.get_diagnostics = function(sources) + local result = {} + for index, source in ipairs(sources) do + if type(source) == 'string' then + local error_count, warning_count, info_count, hint_count = M.sources[source]() + result[index] = { + error = error_count, + warn = warning_count, + info = info_count, + hint = hint_count, + } + elseif type(source) == 'function' then + local source_result = source() + source_result = type(source_result) == 'table' and source_result or {} + result[index] = { + error = source_result.error or 0, + warn = source_result.warn or 0, + info = source_result.info or 0, + hint = source_result.hint or 0, + } + end + end + return result +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diff/git_diff.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diff/git_diff.lua new file mode 100644 index 00000000..a460f8f1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diff/git_diff.lua @@ -0,0 +1,138 @@ +local lualine_require = require('lualine_require') +local modules = lualine_require.lazy_require { + utils = 'lualine.utils.utils', + Job = 'lualine.utils.job', +} + +local M = {} + +-- Vars +-- variable to store git diff stats +local git_diff = nil +-- accumulates output from diff process +local diff_output_cache = {} +-- variable to store git_diff job +local diff_job = nil + +local active_bufnr = '0' +local diff_cache = {} -- Stores last known value of diff of a buffer + +---initialize the module +---param opts table +function M.init(opts) + if type(opts.source) == 'function' then + M.src = opts.source + else + modules.utils.define_autocmd('BufEnter', "lua require'lualine.components.diff.git_diff'.update_diff_args()") + modules.utils.define_autocmd('BufWritePost', "lua require'lualine.components.diff.git_diff'.update_git_diff()") + M.update_diff_args() + end +end + +---Api to get git sign count +---scheme : +---{ +--- added = added_count, +--- modified = modified_count, +--- removed = removed_count, +---} +---error_code = { added = -1, modified = -1, removed = -1 } +---@param bufnr number|nil +function M.get_sign_count(bufnr) + if bufnr then + return diff_cache[bufnr] + end + if M.src then + git_diff = M.src() + diff_cache[vim.api.nvim_get_current_buf()] = git_diff + elseif vim.g.actual_curbuf ~= nil and active_bufnr ~= vim.g.actual_curbuf then + -- Workaround for https://github.com/nvim-lualine/lualine.nvim/issues/286 + -- See upstream issue https://github.com/neovim/neovim/issues/15300 + -- Diff is out of sync re sync it. + M.update_diff_args() + end + return git_diff +end + +---process diff data and update git_diff{ added, removed, modified } +---@param data string output on stdout od git diff job +local function process_diff(data) + -- Adapted from https://github.com/wbthomason/nvim-vcs.lua + local added, removed, modified = 0, 0, 0 + for _, line in ipairs(data) do + if string.find(line, [[^@@ ]]) then + local tokens = vim.fn.matchlist(line, [[^@@ -\v(\d+),?(\d*) \+(\d+),?(\d*)]]) + local line_stats = { + mod_count = tokens[3] == nil and 0 or tokens[3] == '' and 1 or tonumber(tokens[3]), + new_count = tokens[5] == nil and 0 or tokens[5] == '' and 1 or tonumber(tokens[5]), + } + + if line_stats.mod_count == 0 and line_stats.new_count > 0 then + added = added + line_stats.new_count + elseif line_stats.mod_count > 0 and line_stats.new_count == 0 then + removed = removed + line_stats.mod_count + else + local min = math.min(line_stats.mod_count, line_stats.new_count) + modified = modified + min + added = added + line_stats.new_count - min + removed = removed + line_stats.mod_count - min + end + end + end + git_diff = { added = added, modified = modified, removed = removed } +end + +---updates the job args +function M.update_diff_args() + -- Don't show git diff when current buffer doesn't have a filename + active_bufnr = tostring(vim.api.nvim_get_current_buf()) + if #vim.fn.expand('%') == 0 then + M.diff_args = nil + git_diff = nil + return + end + M.diff_args = { + cmd = string.format( + [[git -C %s --no-pager diff --no-color --no-ext-diff -U0 -- %s]], + vim.fn.expand('%:h'), + vim.fn.expand('%:t') + ), + on_stdout = function(_, data) + if next(data) then + diff_output_cache = vim.list_extend(diff_output_cache, data) + end + end, + on_stderr = function(_, data) + data = table.concat(data, '\n') + if #data > 0 then + git_diff = nil + diff_output_cache = {} + end + end, + on_exit = function() + if #diff_output_cache > 0 then + process_diff(diff_output_cache) + else + git_diff = { added = 0, modified = 0, removed = 0 } + end + diff_cache[vim.api.nvim_get_current_buf()] = git_diff + end, + } + M.update_git_diff() +end + +---update git_diff variable +function M.update_git_diff() + if M.diff_args then + diff_output_cache = {} + if diff_job then + diff_job:stop() + end + diff_job = modules.Job(M.diff_args) + if diff_job then + diff_job:start() + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diff/init.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diff/init.lua new file mode 100644 index 00000000..9201b2bb --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/diff/init.lua @@ -0,0 +1,93 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local lualine_require = require('lualine_require') +local modules = lualine_require.lazy_require { + git_diff = 'lualine.components.diff.git_diff', + utils = 'lualine.utils.utils', + utils_notices = 'lualine.utils.notices', + highlight = 'lualine.highlight', +} +local M = lualine_require.require('lualine.component'):extend() + +local default_options = { + colored = true, + symbols = { added = '+', modified = '~', removed = '-' }, +} + +local function apply_default_colors(opts) + local default_diff_color = { + added = { + fg = modules.utils.extract_color_from_hllist( + 'fg', + { 'LuaLineDiffAdd', 'GitSignsAdd', 'GitGutterAdd', 'DiffAdded', 'DiffAdd' }, + '#90ee90' + ), + }, + modified = { + fg = modules.utils.extract_color_from_hllist( + 'fg', + { 'LuaLineDiffChange', 'GitSignsChange', 'GitGutterChange', 'DiffChanged', 'DiffChange' }, + '#f0e130' + ), + }, + removed = { + fg = modules.utils.extract_color_from_hllist( + 'fg', + { 'LuaLineDiffDelete', 'GitSignsDelete', 'GitGutterDelete', 'DiffRemoved', 'DiffDelete' }, + '#ff0038' + ), + }, + } + opts.diff_color = vim.tbl_deep_extend('keep', opts.diff_color or {}, default_diff_color) +end + +-- Initializer +function M:init(options) + M.super.init(self, options) + apply_default_colors(self.options) + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) + -- create highlights and save highlight_name in highlights table + if self.options.colored then + self.highlights = { + added = self:create_hl(self.options.diff_color.added, 'added'), + modified = self:create_hl(self.options.diff_color.modified, 'modified'), + removed = self:create_hl(self.options.diff_color.removed, 'removed'), + } + end + modules.git_diff.init(self.options) +end + +-- Function that runs every time statusline is updated +function M:update_status(is_focused) + local git_diff = modules.git_diff.get_sign_count((not is_focused and vim.api.nvim_get_current_buf())) + if git_diff == nil then + return '' + end + + local colors = {} + if self.options.colored then + -- load the highlights and store them in colors table + for name, highlight_name in pairs(self.highlights) do + colors[name] = self:format_hl(highlight_name) + end + end + + local result = {} + -- loop though data and load available sections in result table + for _, name in ipairs { 'added', 'modified', 'removed' } do + if git_diff[name] and git_diff[name] > 0 then + if self.options.colored then + table.insert(result, colors[name] .. self.options.symbols[name] .. git_diff[name]) + else + table.insert(result, self.options.symbols[name] .. git_diff[name]) + end + end + end + if #result > 0 then + return table.concat(result, ' ') + else + return '' + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/encoding.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/encoding.lua new file mode 100644 index 00000000..80a5ece7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/encoding.lua @@ -0,0 +1,7 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local function encoding() + return vim.opt.fileencoding:get() +end + +return encoding diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/fileformat.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/fileformat.lua new file mode 100644 index 00000000..49e3b3e8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/fileformat.lua @@ -0,0 +1,30 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local M = require('lualine.component'):extend() + +-- stylua: ignore +local symbols = { + unix = '', -- e712 + dos = '', -- e70f + mac = '', -- e711 +} + +-- Initializer +function M:init(options) + -- Run super() + M.super.init(self, options) + -- Apply default symbols + self.symbols = vim.tbl_extend('keep', self.options.symbols or {}, symbols) +end + +-- Function that runs every time statusline is updated +function M:update_status() + local format = vim.bo.fileformat + if self.options.icons_enabled then + return self.symbols[format] or format + else + return format + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/filename.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/filename.lua new file mode 100644 index 00000000..22fd964a --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/filename.lua @@ -0,0 +1,119 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local M = require('lualine.component'):extend() + +local modules = require('lualine_require').lazy_require { + utils = 'lualine.utils.utils', +} + +local default_options = { + symbols = { + modified = '[+]', + readonly = '[-]', + unnamed = '[No Name]', + newfile = '[New]', + }, + file_status = true, + newfile_status = false, + path = 0, + shorting_target = 40, +} + +local function is_new_file() + local filename = vim.fn.expand('%') + return filename ~= '' and vim.bo.buftype == '' and vim.fn.filereadable(filename) == 0 +end + +---shortens path by turning apple/orange -> a/orange +---@param path string +---@param sep string path separator +---@param max_len integer maximum length of the full filename string +---@return string +local function shorten_path(path, sep, max_len) + local len = #path + if len <= max_len then + return path + end + + local segments = vim.split(path, sep) + for idx = 1, #segments - 1 do + if len <= max_len then + break + end + + local segment = segments[idx] + local shortened = segment:sub(1, vim.startswith(segment, '.') and 2 or 1) + segments[idx] = shortened + len = len - (#segment - #shortened) + end + + return table.concat(segments, sep) +end + +local function filename_and_parent(path, sep) + local segments = vim.split(path, sep) + if #segments == 0 then + return path + elseif #segments == 1 then + return segments[#segments] + else + return table.concat({ segments[#segments - 1], segments[#segments] }, sep) + end +end + +M.init = function(self, options) + M.super.init(self, options) + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) +end + +M.update_status = function(self) + local path_separator = package.config:sub(1, 1) + local data + if self.options.path == 1 then + -- relative path + data = vim.fn.expand('%:~:.') + elseif self.options.path == 2 then + -- absolute path + data = vim.fn.expand('%:p') + elseif self.options.path == 3 then + -- absolute path, with tilde + data = vim.fn.expand('%:p:~') + elseif self.options.path == 4 then + -- filename and immediate parent + data = filename_and_parent(vim.fn.expand('%:p:~'), path_separator) + else + -- just filename + data = vim.fn.expand('%:t') + end + + if data == '' then + data = self.options.symbols.unnamed + end + + if self.options.shorting_target ~= 0 then + local windwidth = self.options.globalstatus and vim.go.columns or vim.fn.winwidth(0) + local estimated_space_available = windwidth - self.options.shorting_target + + data = shorten_path(data, path_separator, estimated_space_available) + end + + data = modules.utils.stl_escape(data) + + local symbols = {} + if self.options.file_status then + if vim.bo.modified then + table.insert(symbols, self.options.symbols.modified) + end + if vim.bo.modifiable == false or vim.bo.readonly == true then + table.insert(symbols, self.options.symbols.readonly) + end + end + + if self.options.newfile_status and is_new_file() then + table.insert(symbols, self.options.symbols.newfile) + end + + return data .. (#symbols > 0 and ' ' .. table.concat(symbols, '') or '') +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/filesize.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/filesize.lua new file mode 100644 index 00000000..77f482ca --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/filesize.lua @@ -0,0 +1,25 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local function filesize() + local file = vim.fn.expand('%:p') + if file == nil or #file == 0 then + return '' + end + local size = vim.fn.getfsize(file) + if size <= 0 then + return '' + end + + local suffixes = { 'b', 'k', 'm', 'g' } + + local i = 1 + while size > 1024 and i < #suffixes do + size = size / 1024 + i = i + 1 + end + + local format = i == 1 and '%d%s' or '%.1f%s' + return string.format(format, size, suffixes[i]) +end + +return filesize diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/filetype.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/filetype.lua new file mode 100644 index 00000000..748359fe --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/filetype.lua @@ -0,0 +1,82 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local lualine_require = require('lualine_require') +local modules = lualine_require.lazy_require { + highlight = 'lualine.highlight', + utils = 'lualine.utils.utils', +} +local M = lualine_require.require('lualine.component'):extend() + +local default_options = { + colored = true, + icon_only = false, +} + +function M:init(options) + M.super.init(self, options) + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) + self.icon_hl_cache = {} +end + +function M.update_status() + local ft = vim.bo.filetype or '' + return modules.utils.stl_escape(ft) +end + +function M:apply_icon() + if not self.options.icons_enabled then + return + end + + local icon, icon_highlight_group + local ok, devicons = pcall(require, 'nvim-web-devicons') + if ok then + icon, icon_highlight_group = devicons.get_icon(vim.fn.expand('%:t')) + if icon == nil then + icon, icon_highlight_group = devicons.get_icon_by_filetype(vim.bo.filetype) + end + + if icon == nil and icon_highlight_group == nil then + icon = '' + icon_highlight_group = 'DevIconDefault' + end + if icon then + icon = icon .. ' ' + end + if self.options.colored then + local highlight_color = modules.utils.extract_highlight_colors(icon_highlight_group, 'fg') + if highlight_color then + local default_highlight = self:get_default_hl() + local icon_highlight = self.icon_hl_cache[highlight_color] + if not icon_highlight or not modules.highlight.highlight_exists(icon_highlight.name .. '_normal') then + icon_highlight = self:create_hl({ fg = highlight_color }, icon_highlight_group) + self.icon_hl_cache[highlight_color] = icon_highlight + end + + icon = self:format_hl(icon_highlight) .. icon .. default_highlight + end + end + else + ok = vim.fn.exists('*WebDevIconsGetFileTypeSymbol') + if ok ~= 0 then + icon = vim.fn.WebDevIconsGetFileTypeSymbol() + if icon then + icon = icon .. ' ' + end + end + end + + if not icon then + return + end + + if self.options.icon_only then + self.status = icon + elseif type(self.options.icon) == 'table' and self.options.icon.align == 'right' then + self.status = self.status .. ' ' .. icon + else + self.status = icon .. self.status + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/hostname.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/hostname.lua new file mode 100644 index 00000000..c43121ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/hostname.lua @@ -0,0 +1,11 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local modules = require('lualine_require').lazy_require { + utils = 'lualine.utils.utils', +} + +local function hostname() + return modules.utils.stl_escape(vim.loop.os_gethostname()) +end + +return hostname diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/location.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/location.lua new file mode 100644 index 00000000..1b276acc --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/location.lua @@ -0,0 +1,9 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local function location() + local line = vim.fn.line('.') + local col = vim.fn.virtcol('.') + return string.format('%3d:%-2d', line, col) +end + +return location diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/mode.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/mode.lua new file mode 100644 index 00000000..721d6ca1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/mode.lua @@ -0,0 +1,4 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local get_mode = require('lualine.utils.mode').get_mode +return get_mode diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/progress.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/progress.lua new file mode 100644 index 00000000..c4891fee --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/progress.lua @@ -0,0 +1,15 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local function progress() + local cur = vim.fn.line('.') + local total = vim.fn.line('$') + if cur == 1 then + return 'Top' + elseif cur == total then + return 'Bot' + else + return string.format('%2d%%%%', math.floor(cur / total * 100)) + end +end + +return progress diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/searchcount.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/searchcount.lua new file mode 100644 index 00000000..a70ff838 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/searchcount.lua @@ -0,0 +1,31 @@ +local M = require('lualine.component'):extend() + +local default_options = { + maxcount = 999, + timeout = 500, +} + +-- Initializer +function M:init(options) + -- Run super() + M.super.init(self, options) + -- Apply default options + self.options = vim.tbl_extend('keep', self.options or {}, default_options) +end + +-- Function that runs every time statusline is updated +function M:update_status() + if vim.v.hlsearch == 0 then + return '' + end + + local ok, result = pcall(vim.fn.searchcount, { maxcount = self.options.maxcount, timeout = self.options.timeout }) + if not ok or next(result) == nil then + return '' + end + + local denominator = math.min(result.total, result.maxcount) + return string.format('[%d/%d]', result.current, denominator) +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/selectioncount.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/selectioncount.lua new file mode 100644 index 00000000..f08bde0c --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/selectioncount.lua @@ -0,0 +1,16 @@ +local function selectioncount() + local mode = vim.fn.mode(true) + local line_start, col_start = vim.fn.line('v'), vim.fn.col('v') + local line_end, col_end = vim.fn.line('.'), vim.fn.col('.') + if mode:match('') then + return string.format('%dx%d', math.abs(line_start - line_end) + 1, math.abs(col_start - col_end) + 1) + elseif mode:match('V') or line_start ~= line_end then + return math.abs(line_start - line_end) + 1 + elseif mode:match('v') then + return math.abs(col_start - col_end) + 1 + else + return '' + end +end + +return selectioncount diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/special/eval_func_component.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/special/eval_func_component.lua new file mode 100644 index 00000000..804a8c7a --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/special/eval_func_component.lua @@ -0,0 +1,48 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local M = require('lualine.component'):extend() + +function M:update_status() + local component = self.options[1] + local ok, status + if self.options.type == nil then + ok, status = pcall(M.lua_eval, component) + if not ok then + status = M.vim_function(component) + end + else + if self.options.type == 'lua_expr' then + ok, status = pcall(M.lua_eval, component) + if not ok then + status = nil + end + elseif self.options.type == 'vim_fun' then + status = M.vim_function(component) + end + end + return status +end + +---evaluate the lua code and return it's result as string +---@param code string +---@return string +function M.lua_eval(code) + local result = loadstring('return ' .. code)() + assert(result, 'String expected got nil') + return tostring(result) +end + +---call vim function (name) and return it's result as string +---@param name string +---@return string +function M.vim_function(name) + -- vim function component + local ok, return_val = pcall(vim.api.nvim_call_function, name, {}) + if not ok then + return '' + end -- function call failed + ok, return_val = pcall(tostring, return_val) + return ok and return_val or '' +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/special/function_component.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/special/function_component.lua new file mode 100644 index 00000000..4856dad6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/special/function_component.lua @@ -0,0 +1,21 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local M = require('lualine.component'):extend() + +M.update_status = function(self, is_focused) + -- 1st element in options table is the function provided by config + local ok, retval + ok, retval = pcall(self.options[1], self, is_focused) + if not ok then + return '' + end + if type(retval) ~= 'string' then + ok, retval = pcall(tostring, retval) + if not ok then + return '' + end + end + return retval +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/special/vim_var_component.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/special/vim_var_component.lua new file mode 100644 index 00000000..e3924d4a --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/special/vim_var_component.lua @@ -0,0 +1,37 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local require = require('lualine_require').require +local M = require('lualine.component'):extend() +local utils = require('lualine.utils.utils') + +function M:update_status() + local component = self.options[1] + -- vim variable component + -- accepts g:, v:, t:, w:, b:, o, go:, vo:, to:, wo:, bo: + -- filters g portion from g:var + local scope = component:match('[gvtwb]?o?') + -- filters var portion from g:var + local var_name = component:sub(#scope + 2, #component) + -- Displays nothing when variable aren't present + if not (scope and var_name) then + return '' + end + -- Support accessing keys within dictionary + -- https://github.com/nvim-lualine/lualine.nvim/issues/25#issuecomment-907374548 + local name_chunks = vim.split(var_name, '%.') + local return_val = vim[scope][name_chunks[1]] + for i = 2, #name_chunks do + if return_val == nil then + break + end + return_val = return_val[name_chunks[i]] + end + if return_val == nil then + return '' + end + local ok + ok, return_val = pcall(tostring, return_val) + return ok and utils.stl_escape(return_val) or '' +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/tabs/init.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/tabs/init.lua new file mode 100644 index 00000000..5d1966e6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/tabs/init.lua @@ -0,0 +1,193 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local require = require('lualine_require').require +local Tab = require('lualine.components.tabs.tab') +local M = require('lualine.component'):extend() +local highlight = require('lualine.highlight') + +local default_options = { + max_length = 0, + tab_max_length = 40, + mode = 0, + use_mode_colors = false, + path = 0, + tabs_color = { + active = nil, + inactive = nil, + }, + show_modified_status = true, + symbols = { + modified = '[+]', + }, +} + +-- This function is duplicated in buffers +---returns the proper hl for tab in section. Used for setting default highlights +---@param section string name of section tabs component is in +---@param is_active boolean +---@return string hl name +local function get_hl(section, is_active) + local suffix = is_active and highlight.get_mode_suffix() or '_inactive' + local section_redirects = { + lualine_x = 'lualine_c', + lualine_y = 'lualine_b', + lualine_z = 'lualine_a', + } + if section_redirects[section] then + section = highlight.highlight_exists(section .. suffix) and section or section_redirects[section] + end + return section .. suffix +end + +function M:init(options) + M.super.init(self, options) + -- if use_mode_colors is set, use a function so that the colors update + local default_active = options.use_mode_colors + and function() + return get_hl('lualine_' .. options.self.section, true) + end + or get_hl('lualine_' .. options.self.section, true) + default_options.tabs_color = { + active = default_active, + inactive = get_hl('lualine_' .. options.self.section, false), + } + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) + -- stylua: ignore + self.highlights = { + active = self:create_hl(self.options.tabs_color.active, 'active'), + inactive = self:create_hl(self.options.tabs_color.inactive, 'inactive'), + } +end + +function M:update_status() + local data = {} + local tabs = {} + for nr, id in ipairs(vim.api.nvim_list_tabpages()) do + tabs[#tabs + 1] = Tab { tabId = id, tabnr = nr, options = self.options, highlights = self.highlights } + end + -- mark the first, last, current, before current, after current tabpages + -- for rendering + local current = vim.fn.tabpagenr() + tabs[1].first = true + tabs[#tabs].last = true + if tabs[current] then + tabs[current].current = true + end + if tabs[current - 1] then + tabs[current - 1].beforecurrent = true + end + if tabs[current + 1] then + tabs[current + 1].aftercurrent = true + end + + local max_length = self.options.max_length + if type(max_length) == 'function' then + max_length = max_length(self) + end + + if max_length == 0 then + max_length = math.floor(vim.o.columns / 3) + end + local total_length + for i, tab in pairs(tabs) do + if tab.current then + current = i + end + end + local current_tab = tabs[current] + -- start drawing from current tab and draw left and right of it until + -- all tabpages are drawn or max_length has been reached. + if current_tab == nil then -- maybe redundant code + local t = Tab { + tabId = vim.api.nvim_get_current_tabpage(), + tabnr = vim.fn.tabpagenr(), + options = self.options, + highlights = self.highlights, + } + t.current = true + t.last = true + data[#data + 1] = t:render() + else + data[#data + 1] = current_tab:render() + total_length = current_tab.len + local i = 0 + local before, after + while true do + i = i + 1 + before = tabs[current - i] + after = tabs[current + i] + local rendered_before, rendered_after + if before == nil and after == nil then + break + end + -- draw left most undrawn tab if fits in max_length + if before then + rendered_before = before:render() + total_length = total_length + before.len + if total_length > max_length then + break + end + table.insert(data, 1, rendered_before) + end + -- draw right most undrawn tab if fits in max_length + if after then + rendered_after = after:render() + total_length = total_length + after.len + if total_length > max_length then + break + end + data[#data + 1] = rendered_after + end + end + -- draw ellipsis (...) on relevant sides if all tabs don't fit in max_length + if total_length > max_length then + if before ~= nil then + before.ellipse = true + before.first = true + table.insert(data, 1, before:render()) + end + if after ~= nil then + after.ellipse = true + after.last = true + data[#data + 1] = after:render() + end + end + end + + return table.concat(data) +end + +function M:draw() + self.status = '' + self.applied_separator = '' + + if self.options.cond ~= nil and self.options.cond() ~= true then + return self.status + end + local status = self:update_status() + if type(status) == 'string' and #status > 0 then + self.status = status + self:apply_section_separators() + self:apply_separator() + end + return self.status +end + +vim.cmd([[ + function! LualineSwitchTab(tabnr, mouseclicks, mousebutton, modifiers) + execute a:tabnr . "tabnext" + endfunction + + function! LualineRenameTab(...) + if a:0 == 1 + let t:tabname = a:1 + else + unlet t:tabname + end + redrawtabline + endfunction + + command! -nargs=? LualineRenameTab call LualineRenameTab("") +]]) + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/tabs/tab.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/tabs/tab.lua new file mode 100644 index 00000000..56c4788a --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/tabs/tab.lua @@ -0,0 +1,178 @@ +local Tab = require('lualine.utils.class'):extend() + +local modules = require('lualine_require').lazy_require { + highlight = 'lualine.highlight', + utils = 'lualine.utils.utils', +} + +---initialize a new tab from opts +---@param opts table +function Tab:init(opts) + assert(opts.tabnr, 'Cannot create Tab without tabnr') + self.tabnr = opts.tabnr + self.tabId = opts.tabId + self.options = opts.options + self.highlights = opts.highlights + self.modified_icon = '' + self:get_props() +end + +function Tab:get_props() + local buflist = vim.fn.tabpagebuflist(self.tabnr) + local winnr = vim.fn.tabpagewinnr(self.tabnr) + local bufnr = buflist[winnr] + self.file = modules.utils.stl_escape(vim.api.nvim_buf_get_name(bufnr)) + self.filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype') + self.buftype = vim.api.nvim_buf_get_option(bufnr, 'buftype') + + if self.options.show_modified_status then + for _, b in ipairs(buflist) do + if vim.api.nvim_buf_get_option(b, 'modified') then + self.modified_icon = self.options.symbols.modified or '' + break + end + end + end +end + +---returns name for tab. Tabs name is the name of buffer in last active window +--- of the tab. +---@return string +function Tab:label() + local ok, custom_tabname = pcall(vim.api.nvim_tabpage_get_var, self.tabId, 'tabname') + if not ok then + custom_tabname = nil + end + if custom_tabname and custom_tabname ~= '' then + return modules.utils.stl_escape(custom_tabname) + end + if self.filetype == 'fugitive' then + return 'fugitive: ' .. vim.fn.fnamemodify(self.file, ':h:h:t') + elseif self.buftype == 'help' then + return 'help:' .. vim.fn.fnamemodify(self.file, ':t:r') + elseif self.buftype == 'terminal' then + local match = string.match(vim.split(self.file, ' ')[1], 'term:.*:(%a+)') + return match ~= nil and match or vim.fn.fnamemodify(vim.env.SHELL, ':t') + elseif self.file == '' then + return '[No Name]' + end + if self.options.path == 1 then + return vim.fn.fnamemodify(self.file, ':~:.') + elseif self.options.path == 2 then + return vim.fn.fnamemodify(self.file, ':p') + elseif self.options.path == 3 then + return vim.fn.fnamemodify(self.file, ':p:~') + else + return vim.fn.fnamemodify(self.file, ':t') + end +end + +---shortens path by turning apple/orange -> a/orange +---@param path string +---@param sep string path separator +---@param max_len integer maximum length of the full filename string +---@return string +local function shorten_path(path, sep, max_len) + local len = #path + if len <= max_len then + return path + end + + local segments = vim.split(path, sep) + for idx = 1, #segments - 1 do + if len <= max_len then + break + end + + local segment = segments[idx] + local shortened = segment:sub(1, vim.startswith(segment, '.') and 2 or 1) + segments[idx] = shortened + len = len - (#segment - #shortened) + end + + return table.concat(segments, sep) +end + +---returns rendered tab +---@return string +function Tab:render() + local name = self:label() + if self.options.tab_max_length ~= 0 then + local path_separator = package.config:sub(1, 1) + name = shorten_path(name, path_separator, self.options.tab_max_length) + end + if self.options.fmt then + name = self.options.fmt(name or '', self) + end + if self.ellipse then -- show ellipsis + name = '...' + else + -- different formats for different modes + if self.options.mode == 0 then + name = tostring(self.tabnr) + if self.modified_icon ~= '' then + name = string.format('%s%s', name, self.modified_icon) + end + elseif self.options.mode == 1 then + if self.modified_icon ~= '' then + name = string.format('%s %s', self.modified_icon, name) + end + else + name = string.format('%s%s %s', tostring(self.tabnr), self.modified_icon, name) + end + end + + name = Tab.apply_padding(name, self.options.padding) + self.len = vim.fn.strchars(name) + + -- setup for mouse clicks + local line = string.format('%%%s@LualineSwitchTab@%s%%T', self.tabnr, name) + -- apply highlight + line = modules.highlight.component_format_highlight(self.highlights[(self.current and 'active' or 'inactive')]) + .. line + + -- apply separators + if self.options.self.section < 'x' and not self.first then + local sep_before = self:separator_before() + line = sep_before .. line + self.len = self.len + vim.fn.strchars(sep_before) + elseif self.options.self.section >= 'x' and not self.last then + local sep_after = self:separator_after() + line = line .. sep_after + self.len = self.len + vim.fn.strchars(sep_after) + end + return line +end + +---apply separator before current tab +---@return string +function Tab:separator_before() + if self.current or self.aftercurrent then + return '%Z{' .. self.options.section_separators.left .. '}' + else + return self.options.component_separators.left + end +end + +---apply separator after current tab +---@return string +function Tab:separator_after() + if self.current or self.beforecurrent then + return '%z{' .. self.options.section_separators.right .. '}' + else + return self.options.component_separators.right + end +end + +---adds spaces to left and right +function Tab.apply_padding(str, padding) + local l_padding, r_padding = 1, 1 + if type(padding) == 'number' then + l_padding, r_padding = padding, padding + elseif type(padding) == 'table' then + l_padding, r_padding = padding.left or 0, padding.right or 0 + end + return string.rep(' ', l_padding) .. str .. string.rep(' ', r_padding) +end + +return Tab diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/windows/init.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/windows/init.lua new file mode 100644 index 00000000..ee00b007 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/windows/init.lua @@ -0,0 +1,64 @@ +local Window = require('lualine.components.windows.window') +local M = require('lualine.components.buffers'):extend() + +local default_options = { + disabled_filetypes = {}, + disabled_buftypes = { 'quickfix', 'prompt' }, +} + +function M:init(options) + options.buffers_color = nil -- buffers_color isn't windows option. + M.super.init(self, options) + + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) + self.options.windows_color = vim.tbl_deep_extend('keep', self.options.windows_color or {}, self.options.buffers_color) + self.options.buffers_color = nil -- this is the default value of colors generated by parent buffers component. + + self.highlights = { + active = self:create_hl(self.options.windows_color.active, 'active'), + inactive = self:create_hl(self.options.windows_color.inactive, 'inactive'), + } +end + +function M:new_buffer(winnr) + winnr = winnr or vim.api.nvim_get_current_win() + + return Window:new { + winnr = winnr, + options = self.options, + highlights = self.highlights, + } +end + +--- Override to only return buffers shown in the windows of the current tab +function M:buffers() + local tabnr = vim.api.nvim_get_current_tabpage() + local buffers = {} + + for _, winnr in ipairs(vim.api.nvim_tabpage_list_wins(tabnr)) do + if not self:should_hide(winnr) then + buffers[#buffers + 1] = self:new_buffer(winnr) + end + end + + return buffers +end + +function M:should_hide(winnr) + local bufnr = vim.api.nvim_win_get_buf(winnr) + local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype') + local buftype = vim.api.nvim_buf_get_option(bufnr, 'buftype') + local is_filetype_disabled = vim.tbl_contains(self.options.disabled_filetypes, filetype) + local is_buftype_disabled = vim.tbl_contains(self.options.disabled_buftypes, buftype) + local is_floating = '' ~= vim.api.nvim_win_get_config(winnr).relative + + return is_floating or is_buftype_disabled or is_filetype_disabled +end + +vim.cmd([[ + function! LualineSwitchWindow(win_number, mouseclicks, mousebutton, modifiers) + execute a:win_number . 'wincmd w' + endfunction +]]) + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/windows/window.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/windows/window.lua new file mode 100644 index 00000000..8c7e0a12 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/components/windows/window.lua @@ -0,0 +1,35 @@ +local Window = require('lualine.components.buffers.buffer'):extend() + +---initialize a new buffer from opts +---@param opts table +function Window:init(opts) + assert(opts.winnr, 'Cannot create Window without winnr') + opts.bufnr = vim.api.nvim_win_get_buf(opts.winnr) + + Window.super.init(self, opts) + + self.winnr = opts.winnr + self.win_number = vim.api.nvim_win_get_number(self.winnr) +end + +function Window:is_current() + return vim.api.nvim_get_current_win() == self.winnr +end + +function Window:apply_mode(name) + if self.options.mode == 0 then + return string.format('%s%s%s', self.icon, name, self.modified_icon) + end + + if self.options.mode == 1 then + return string.format('%s %s%s', self.win_number, self.icon, self.modified_icon) + end + + return string.format('%s %s%s%s', self.win_number, self.icon, name, self.modified_icon) +end + +function Window:configure_mouse_click(name) + return string.format('%%%s@LualineSwitchWindow@%s%%T', self.win_number, name) +end + +return Window diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/config.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/config.lua new file mode 100644 index 00000000..cc92099d --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/config.lua @@ -0,0 +1,135 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local require = require('lualine_require').require +local utils = require('lualine.utils.utils') +local modules = require('lualine_require').lazy_require { + utils_notices = 'lualine.utils.notices', +} + +local config = { + options = { + icons_enabled = true, + theme = 'auto', + component_separators = { left = '', right = '' }, + section_separators = { left = '', right = '' }, + disabled_filetypes = { + statusline = {}, + winbar = {}, + }, + ignore_focus = {}, + always_divide_middle = true, + globalstatus = vim.go.laststatus == 3, + refresh = { + statusline = 1000, + tabline = 1000, + winbar = 1000, + }, + }, + sections = { + lualine_a = { 'mode' }, + lualine_b = { 'branch', 'diff', 'diagnostics' }, + lualine_c = { 'filename' }, + lualine_x = { 'encoding', 'fileformat', 'filetype' }, + lualine_y = { 'progress' }, + lualine_z = { 'location' }, + }, + inactive_sections = { + lualine_a = {}, + lualine_b = {}, + lualine_c = { 'filename' }, + lualine_x = { 'location' }, + lualine_y = {}, + lualine_z = {}, + }, + tabline = {}, + winbar = {}, + inactive_winbar = {}, + extensions = {}, +} + +--- change separator format 'x' to {left='x', right='x'} +---@param separators string|table +---@return table +local function fix_separators(separators) + if separators ~= nil then + if type(separators) == 'string' then + return { left = separators, right = separators } + end + end + return separators +end + +---copy raw disabled_filetypes to inner statusline & winbar tables. +---@param disabled_filetypes table +---@return table +local function fix_disabled_filetypes(disabled_filetypes) + if disabled_filetypes == nil then + return + end + if disabled_filetypes.statusline == nil then + disabled_filetypes.statusline = {} + end + if disabled_filetypes.winbar == nil then + disabled_filetypes.winbar = {} + end + for k, disabled_ft in ipairs(disabled_filetypes) do + table.insert(disabled_filetypes.statusline, disabled_ft) + table.insert(disabled_filetypes.winbar, disabled_ft) + disabled_filetypes[k] = nil + end + return disabled_filetypes +end +---extends config based on config_table +---@param config_table table +---@return table copy of config +local function apply_configuration(config_table) + if not config_table then + return utils.deepcopy(config) + end + local function parse_sections(section_group_name) + if config_table[section_group_name] == nil then + return + end + if not next(config_table[section_group_name]) then + config[section_group_name] = {} + return + end + for section_name, section in pairs(config_table[section_group_name]) do + if section_name == 'refresh' then + config[section_group_name][section_name] = + vim.tbl_deep_extend('force', config[section_group_name][section_name], utils.deepcopy(section)) + else + config[section_group_name][section_name] = utils.deepcopy(section) + end + end + end + if vim.fn.has('nvim-0.8') == 0 and (next(config_table.winbar or {}) or next(config_table.inactive_winbar or {})) then + modules.utils_notices.add_notice('### winbar\nSorry `winbar can only be used in neovim 0.8 or higher.\n') + config_table.winbar = {} + config_table.inactive_winbar = {} + end + parse_sections('options') + parse_sections('sections') + parse_sections('inactive_sections') + parse_sections('tabline') + parse_sections('winbar') + parse_sections('inactive_winbar') + if config_table.extensions then + config.extensions = utils.deepcopy(config_table.extensions) + end + config.options.section_separators = fix_separators(config.options.section_separators) + config.options.component_separators = fix_separators(config.options.component_separators) + config.options.disabled_filetypes = fix_disabled_filetypes(config.options.disabled_filetypes) + return utils.deepcopy(config) +end + +--- returns current active config +---@return table a copy of config +local function get_current_config() + return utils.deepcopy(config) +end + +return { + get_config = get_current_config, + apply_configuration = apply_configuration, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/aerial.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/aerial.lua new file mode 100644 index 00000000..474a5a81 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/aerial.lua @@ -0,0 +1,9 @@ +-- MIT license, see LICENSE for more details. +-- Extension for aerial.nvim +local M = {} + +M.sections = { lualine_a = { 'filetype' } } + +M.filetypes = { 'aerial' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/chadtree.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/chadtree.lua new file mode 100644 index 00000000..26a54633 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/chadtree.lua @@ -0,0 +1,11 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local nerdtree = require('lualine.extensions.nerdtree') + +local M = {} + +M.sections = vim.deepcopy(nerdtree.sections) + +M.filetypes = { 'CHADTree' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/ctrlspace.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/ctrlspace.lua new file mode 100644 index 00000000..bc3f7647 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/ctrlspace.lua @@ -0,0 +1,20 @@ +local M = {} + +M.sections = { + lualine_a = { + function() + return vim.fn['ctrlspace#context#Configuration']().Symbols.CS + end, + }, + lualine_b = { 'ctrlspace#api#StatuslineModeSegment' }, + lualine_y = { 'ctrlspace#api#StatuslineTabSegment' }, + lualine_z = { + function() + return 'CtrlSpace' + end, + }, +} + +M.filetypes = { 'ctrlspace' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/fern.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/fern.lua new file mode 100644 index 00000000..70216864 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/fern.lua @@ -0,0 +1,11 @@ +-- MIT license, see LICENSE for more details. +-- Extension for fern file explorer. +local nerdtree = require('lualine.extensions.nerdtree') + +local M = {} + +M.sections = vim.deepcopy(nerdtree.sections) + +M.filetypes = { 'fern' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/fugitive.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/fugitive.lua new file mode 100644 index 00000000..6e75e401 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/fugitive.lua @@ -0,0 +1,17 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local M = {} + +local function fugitive_branch() + local icon = '' -- e0a0 + return icon .. ' ' .. vim.fn.FugitiveHead() +end + +M.sections = { + lualine_a = { fugitive_branch }, + lualine_z = { 'location' }, +} + +M.filetypes = { 'fugitive' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/fzf.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/fzf.lua new file mode 100644 index 00000000..956a0e91 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/fzf.lua @@ -0,0 +1,45 @@ +--[[ +lualine extension for fzf filetypes: +works with both https://github.com/junegunn/fzf.vim and https://github.com/ibhagwan/fzf-lua + +-- fzf-lua must be set-up in split mode +]] + +local function has_fzf() + return pcall(require, 'fzf-lua') +end + +local function fzf_picker() + if not has_fzf() then + return '' + end + + local info_string = vim.inspect(require('fzf-lua').get_info()['fnc']) + return info_string:gsub('"', '') +end + +local function fzf_element() + if not has_fzf() then + return '' + end + + local fzf = require('fzf-lua') + local selected = fzf.get_info().selected + return fzf.path.entry_to_file(selected).path +end + +local function fzf_statusline() + return 'FZF' +end + +local M = {} + +M.sections = { + lualine_a = { fzf_statusline }, + lualine_y = { fzf_element }, + lualine_z = { fzf_picker }, +} + +M.filetypes = { 'fzf' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/lazy.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/lazy.lua new file mode 100644 index 00000000..6051bb3f --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/lazy.lua @@ -0,0 +1,31 @@ +-- lualine extension for lazy.nvim + +local ok, lazy = pcall(require, 'lazy') +if not ok then + return '' +end + +local M = {} + +M.sections = { + lualine_a = { + function() + return 'lazy 💤' + end, + }, + lualine_b = { + function() + return 'loaded: ' .. lazy.stats().loaded .. '/' .. lazy.stats().count + end, + }, + lualine_c = { + { + require('lazy.status').updates, + cond = require('lazy.status').has_updates, + }, + }, +} + +M.filetypes = { 'lazy' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/man.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/man.lua new file mode 100644 index 00000000..ba976dee --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/man.lua @@ -0,0 +1,16 @@ +local M = {} + +M.sections = { + lualine_a = { + function() + return 'MAN' + end, + }, + lualine_b = { { 'filename', file_status = false } }, + lualine_y = { 'progress' }, + lualine_z = { 'location' }, +} + +M.filetypes = { 'man' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/mason.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/mason.lua new file mode 100644 index 00000000..5432fe66 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/mason.lua @@ -0,0 +1,29 @@ +-- lualine extension for mason.nvim + +local mason_registry +local function maybe_set_registry() + local ok, registry = pcall(require, 'mason-registry') + if ok then + mason_registry = registry + end +end + +local M = {} + +M.sections = { + lualine_a = { + function() + return 'Mason' + end, + }, + lualine_b = { + function() + maybe_set_registry() + return 'Installed: ' .. #mason_registry.get_installed_packages() .. '/' .. #mason_registry.get_all_package_specs() + end, + }, +} + +M.filetypes = { 'mason' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/mundo.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/mundo.lua new file mode 100644 index 00000000..5ecf96f0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/mundo.lua @@ -0,0 +1,19 @@ +local M = {} + +M.sections = { + lualine_a = { + function() + local ft = vim.opt_local.filetype:get() + return (ft == 'Mundo') and 'Change tree' or (ft == 'MundoDiff') and 'Change diff' + end, + }, + lualine_y = { 'progress' }, + lualine_z = { 'location' }, +} + +M.filetypes = { + 'Mundo', + 'MundoDiff', +} + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/neo-tree.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/neo-tree.lua new file mode 100644 index 00000000..9149766f --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/neo-tree.lua @@ -0,0 +1,11 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local nerdtree = require('lualine.extensions.nerdtree') + +local M = {} + +M.sections = vim.deepcopy(nerdtree.sections) + +M.filetypes = { 'neo-tree' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/nerdtree.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/nerdtree.lua new file mode 100644 index 00000000..27b117b5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/nerdtree.lua @@ -0,0 +1,15 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local function get_short_cwd() + return vim.fn.fnamemodify(vim.fn.getcwd(), ':~') +end + +local M = {} + +M.sections = { + lualine_a = { get_short_cwd }, +} + +M.filetypes = { 'nerdtree' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/nvim-dap-ui.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/nvim-dap-ui.lua new file mode 100644 index 00000000..42d5265c --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/nvim-dap-ui.lua @@ -0,0 +1,18 @@ +-- MIT license, see LICENSE for more details. +-- Extension for nvim-dap-ui +local M = {} + +M.sections = { + lualine_a = { { 'filename', file_status = false } }, +} + +M.filetypes = { + 'dap-repl', + 'dapui_console', + 'dapui_watches', + 'dapui_stacks', + 'dapui_breakpoints', + 'dapui_scopes', +} + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/nvim-tree.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/nvim-tree.lua new file mode 100644 index 00000000..7c232104 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/nvim-tree.lua @@ -0,0 +1,11 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local nerdtree = require('lualine.extensions.nerdtree') + +local M = {} + +M.sections = vim.deepcopy(nerdtree.sections) + +M.filetypes = { 'NvimTree' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/oil.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/oil.lua new file mode 100644 index 00000000..afcf3e12 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/oil.lua @@ -0,0 +1,20 @@ +-- Extension for oil.nvim + +local M = {} + +M.sections = { + lualine_a = { + function() + local ok, oil = pcall(require, 'oil') + if ok then + return vim.fn.fnamemodify(oil.get_current_dir(), ':~') + else + return '' + end + end, + }, +} + +M.filetypes = { 'oil' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/overseer.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/overseer.lua new file mode 100644 index 00000000..e49a4c46 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/overseer.lua @@ -0,0 +1,16 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. + +local M = {} + +M.sections = { + lualine_a = { + function() + return 'OverseerList' + end, + }, +} + +M.filetypes = { 'OverseerList' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/quickfix.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/quickfix.lua new file mode 100644 index 00000000..05cced54 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/quickfix.lua @@ -0,0 +1,34 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +-- +local function is_loclist() + return vim.fn.getloclist(0, { filewinid = 1 }).filewinid ~= 0 +end + +local function label() + return is_loclist() and 'Location List' or 'Quickfix List' +end + +local function title() + if is_loclist() then + return vim.fn.getloclist(0, { title = 0 }).title + end + return vim.fn.getqflist({ title = 0 }).title +end + +local M = {} + +function M.init() + -- Make sure ft wf doesn't create a custom statusline + vim.g.qf_disable_statusline = true +end + +M.sections = { + lualine_a = { label }, + lualine_b = { title }, + lualine_z = { 'location' }, +} + +M.filetypes = { 'qf' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/symbols-outline.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/symbols-outline.lua new file mode 100644 index 00000000..78ca9bcd --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/symbols-outline.lua @@ -0,0 +1,9 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local M = {} + +M.sections = { lualine_a = { 'filetype' } } + +M.filetypes = { 'Outline' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/toggleterm.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/toggleterm.lua new file mode 100644 index 00000000..a9be0017 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/toggleterm.lua @@ -0,0 +1,16 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. + +local function toggleterm_statusline() + return 'ToggleTerm #' .. vim.b.toggle_number +end + +local M = {} + +M.sections = { + lualine_a = { toggleterm_statusline }, +} + +M.filetypes = { 'toggleterm' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/trouble.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/trouble.lua new file mode 100644 index 00000000..1f79ecfe --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/extensions/trouble.lua @@ -0,0 +1,22 @@ +local M = {} + +local function get_trouble_mode() + local opts = require('trouble.config').options + + local words = vim.split(opts.mode, '[%W]') + for i, word in ipairs(words) do + words[i] = word:sub(1, 1):upper() .. word:sub(2) + end + + return table.concat(words, ' ') +end + +M.sections = { + lualine_a = { + get_trouble_mode, + }, +} + +M.filetypes = { 'Trouble' } + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/highlight.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/highlight.lua new file mode 100644 index 00000000..62343031 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/highlight.lua @@ -0,0 +1,494 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local M = {} +local lualine_require = require('lualine_require') +local require = lualine_require.require +local modules = lualine_require.lazy_require { + utils = 'lualine.utils.utils', + color_utils = 'lualine.utils.color_utils', +} + +local section_highlight_map = { x = 'c', y = 'b', z = 'a' } +local active_theme = nil +local theme_hls = {} +local create_cterm_colors = false + +-- table to store the highlight names created by lualine +local loaded_highlights = {} + +-- table to map mode to highlight suffixes +local mode_to_highlight = { + ['VISUAL'] = '_visual', + ['V-BLOCK'] = '_visual', + ['V-LINE'] = '_visual', + ['SELECT'] = '_visual', + ['S-LINE'] = '_visual', + ['S-BLOCK'] = '_visual', + ['REPLACE'] = '_replace', + ['V-REPLACE'] = '_replace', + ['INSERT'] = '_insert', + ['COMMAND'] = '_command', + ['EX'] = '_command', + ['MORE'] = '_command', + ['CONFIRM'] = '_command', + ['TERMINAL'] = '_terminal', +} + +--- Get highlight suffix for current mode, or inactive if not focused +---@return string mode_suffix +function M.get_mode_suffix() + local mode = require('lualine.utils.mode').get_mode() + return mode_to_highlight[mode] or '_normal' +end + +--- determine if an highlight exist and isn't cleared +---@param highlight_name string +---@return boolean whether hl_group was defined with highlight_name +function M.highlight_exists(highlight_name) + return loaded_highlights[highlight_name] or false +end + +--- clears loaded_highlights table and highlights +local function clear_highlights() + for highlight_name, _ in pairs(loaded_highlights) do + vim.cmd('highlight clear ' .. highlight_name) + end + loaded_highlights = {} +end + +---converts cterm, color_name type colors to #rrggbb format +---@param color string|number +---@return string +local function sanitize_color(color) + if color == nil or color == '' or (type(color) == 'string' and color:lower() == 'none') then + return 'None' + end + if type(color) == 'string' then + if color:sub(1, 1) == '#' then + return color + end -- RGB value + return modules.color_utils.color_name2rgb(color) + elseif type(color) == 'number' then + if color > 255 then + error("What's this it can't be higher then 255 and you've given " .. color) + end + return modules.color_utils.cterm2rgb(color) + end +end + +---converts color_name type colors to cterm format and let cterm color pass through +---@param color string|number +---@return string +local function sanitize_color_for_cterm(color) + if type(color) == 'number' then + if color > 255 then + error("What's this it can't be higher then 255 and you've given " .. color) + end + return color + end + return modules.color_utils.rgb2cterm(sanitize_color(color)) +end + +function M.get_lualine_hl(name) + local hl = loaded_highlights[name] + if hl and not hl.empty then + if hl.link then + return modules.utils.extract_highlight_colors(hl.link) + end + local hl_def = { + fg = hl.fg ~= 'None' and vim.deepcopy(hl.fg) or nil, + bg = hl.bg ~= 'None' and vim.deepcopy(hl.bg) or nil, + sp = hl.sp ~= 'None' and vim.deepcopy(hl.sp) or nil, + } + if hl.gui then + for _, flag in ipairs(vim.split(hl.gui, ',')) do + if flag ~= 'None' then + hl_def[flag] = true + end + end + end + + return hl_def + end +end + +--- Define a hl_group +---@param name string +---@param foreground string|number: color +---@param background string|number: color +---@param gui table cterm/gui options like bold/italic etc. +---@param link string hl_group name to link new hl to +function M.highlight(name, foreground, background, gui, link) + local command = { 'highlight!' } + if link and #link > 0 then + if loaded_highlights[name] and loaded_highlights[name].link == link then + return + end + vim.list_extend(command, { 'link', name, link }) + else + local foreground_rgb = sanitize_color(foreground) + local background_rgb = sanitize_color(background) + gui = (gui ~= nil and gui ~= '') and gui or 'None' + if + loaded_highlights[name] + and loaded_highlights[name].fg == foreground_rgb + and loaded_highlights[name].bg == background_rgb + and loaded_highlights[name].gui == gui + then + return -- color is already defined why are we doing this anyway ? + end + table.insert(command, name) + table.insert(command, 'guifg=' .. foreground_rgb) + table.insert(command, 'guibg=' .. background_rgb) + table.insert(command, 'gui=' .. gui) + if create_cterm_colors then + -- Not setting color from xxxground_rgb to let possible user 256 number through + table.insert(command, 'ctermfg=' .. sanitize_color_for_cterm(foreground)) + table.insert(command, 'ctermbg=' .. sanitize_color_for_cterm(background)) + table.insert(command, 'cterm=' .. gui) + end + end + vim.cmd(table.concat(command, ' ')) + + -- update attached hl groups + local old_hl_def = loaded_highlights[name] + if old_hl_def and next(old_hl_def.attached) then + -- Update attached hl groups as they announced to depend on hl_group 'name' + -- 'hl' being in 'name's attached table means 'hl' + -- depends of 'name'. + -- 'hl' key in attached table will contain a table that + -- defines the relation between 'hl' & 'name'. + -- name.attached.hl = { bg = 'fg' } means + -- hl's fg is same as 'names' bg . So 'hl's fg should + -- be updated when ever 'name' changes it's 'bg' + local bg_changed = old_hl_def.bg ~= background + local fg_changed = old_hl_def.bg ~= foreground + local gui_changed = old_hl_def.gui ~= gui + for attach_name, attach_desc in pairs(old_hl_def.attached) do + if bg_changed and attach_desc.bg and loaded_highlights[attach_name] then + M.highlight( + attach_name, + attach_desc.bg == 'fg' and background or loaded_highlights[attach_name].fg, + attach_desc.bg == 'bg' and background or loaded_highlights[attach_name].bg, + loaded_highlights[attach_name].gui, + loaded_highlights[attach_name].link + ) + end + if fg_changed and attach_desc.fg and loaded_highlights[attach_name] then + M.highlight( + attach_name, + attach_desc.fg == 'fg' and foreground or loaded_highlights[attach_name].fg, + attach_desc.fg == 'bg' and foreground or loaded_highlights[attach_name].bg, + loaded_highlights[attach_name].gui, + loaded_highlights[attach_name].link + ) + end + if gui_changed and attach_desc.gui and loaded_highlights[attach_name] then + M.highlight( + attach_name, + loaded_highlights[attach_name].fg, + loaded_highlights[attach_name].bg, + gui, + loaded_highlights[attach_name].link + ) + end + end + end + -- store current hl state + loaded_highlights[name] = { + fg = foreground, + bg = background, + gui = gui, + link = link, + attached = old_hl_def and old_hl_def.attached or {}, + } +end + +---Attach a hl to another, so the attached auto updates on change to hl that it's attached too. +---@param provider string the hl receiver is getting attached to +---@param receiver string the hl that will be auto updated upon change to provider +---@param provider_el_type string (fg/bg) what element receiver relates to of provider +---@param receiver_el_type string (fg/bg) what element provider relates to of receiver +local function attach_hl(provider, receiver, provider_el_type, receiver_el_type) + if loaded_highlights[provider] == nil then + loaded_highlights[provider] = { empty = true, attached = {} } + end + loaded_highlights[provider].attached[receiver] = { [provider_el_type] = receiver_el_type } +end +---define hl_groups for a theme +---@param theme table +function M.create_highlight_groups(theme) + clear_highlights() + active_theme = theme + theme_hls = {} + local psudo_options = { self = { section = 'a' } } + create_cterm_colors = not vim.go.termguicolors + for mode, sections in pairs(theme) do + theme_hls[mode] = {} + for section, color in pairs(sections) do + local hl_tag = mode + psudo_options.self.section = section + theme_hls[mode][section] = M.create_component_highlight_group(color, hl_tag, psudo_options, true) + end + end +end + +---@description: adds '_mode' at end of highlight_group +---@param highlight_group string name of highlight group +---@return string highlight group name with mode +local function append_mode(highlight_group, is_focused) + if is_focused == nil then + is_focused = modules.utils.is_focused() + end + if is_focused == false then + return highlight_group .. '_inactive' + end + return highlight_group .. M.get_mode_suffix() +end + +-- Helper function for create component highlight +---Handles fall back of colors when creating highlight group +---@param hl_name string name of highlight that we are setting default values for +---@param mode string mode which default component color should be given. +---@param section string the lualine section component is in. +---@param color table color passed for creating component highlight +---@param options table Options table of component this is first fall back +local function get_default_component_color(hl_name, mode, section, color, options) + local default_theme_color + if active_theme[mode] and active_theme[mode][section] then + default_theme_color = active_theme[mode][section] + elseif section >= 'c' and active_theme[mode] and active_theme[mode][section_highlight_map[section]] then + default_theme_color = active_theme[mode][section_highlight_map[section]] + elseif section >= 'c' and active_theme.normal[section_highlight_map[section]] then + default_theme_color = active_theme.normal[section_highlight_map[section]] + else + default_theme_color = active_theme.normal[section] + end + + local ret = { fg = color.fg, bg = color.bg, gui = color.gui } + if ret.fg and ret.bg then + return ret + end + + local function apply_default(def_color, def_name) + if type(def_color) == 'function' and loaded_highlights[def_name] and not loaded_highlights[def_name].empty then + if loaded_highlights[def_name].link then + def_color = loaded_highlights[def_name].link + else + def_color = loaded_highlights[def_name] + end + end + if type(def_color) == 'function' then + def_color = def_color { section = section } + end + if type(def_color) == 'string' then + def_color = modules.utils.extract_highlight_colors(def_color) + end + if type(def_color) == 'table' then + if not ret.fg then + ret.fg = def_color.fg + attach_hl(def_name, hl_name, 'fg', 'fg') + end + if not ret.bg then + ret.bg = def_color.bg + attach_hl(def_name, hl_name, 'bg', 'bg') + end + end + end + + if + options.color + and options.color_highlight + and options.color_highlight.name + and options.color_highlight.name .. '_' .. mode ~= hl_name + then + apply_default(options.color, options.color_highlight.name .. '_' .. mode) + end + + if not ret.fg or not ret.bg then + apply_default(default_theme_color, string.format('lualine_%s_%s', section, mode)) + end + ret.fg = sanitize_color(ret.fg) + ret.bg = sanitize_color(ret.bg) + return ret +end + +---Create highlight group with fg bg and gui from theme +---@param color table has to be { fg = "#rrggbb", bg="#rrggbb" gui = "effect" } +--- all the color elements are optional if fg or bg is not given options +--- must be provided So fg and bg can default the themes colors +---@param highlight_tag string is unique tag for highlight group +---returns the name of highlight group +---@param options table is parameter of component.init() function +---@return table that can be used by component_format_highlight +--- to retrieve highlight group +function M.create_component_highlight_group(color, highlight_tag, options, apply_no_default) + local section = options.self.section + local tag_id = 0 + while + M.highlight_exists(table.concat({ 'lualine', section, highlight_tag }, '_')) + or (section and M.highlight_exists(table.concat({ 'lualine', section, highlight_tag, 'normal' }, '_'))) + do + highlight_tag = highlight_tag .. '_' .. tostring(tag_id) + tag_id = tag_id + 1 + end + + if type(color) == 'string' then + local highlight_group_name = table.concat({ 'lualine', section, highlight_tag }, '_') + M.highlight(highlight_group_name, nil, nil, nil, color) -- l8nk to group + return { + name = highlight_group_name, + fn = nil, + no_mode = true, + link = true, + section = section, + options = options, + no_default = apply_no_default, + } + end + + if type(color) ~= 'function' and (apply_no_default or (color.bg and color.fg)) then + -- When bg and fg are both present we don't need to set highlights for + -- each mode as they will surely look the same. So we can work without options + local highlight_group_name = table.concat({ 'lualine', section, highlight_tag }, '_') + M.highlight(highlight_group_name, color.fg, color.bg, color.gui, nil) + return { + name = highlight_group_name, + fn = nil, + no_mode = true, + section = section, + options = options, + no_default = apply_no_default, + } + end + + local modes = { + 'normal', + 'insert', + 'visual', + 'replace', + 'command', + 'terminal', + 'inactive', + } + for _, mode in ipairs(modes) do + local hl_name = table.concat({ 'lualine', section, highlight_tag, mode }, '_') + local cl = color + if type(color) == 'function' then + cl = color { section = section } or {} + end + if type(cl) == 'string' then + cl = { link = cl } + else + cl = get_default_component_color(hl_name, mode, section, cl, options) + end + M.highlight(hl_name, cl.fg, cl.bg, cl.gui, cl.link) + end + return { + name = table.concat({ 'lualine', section, highlight_tag }, '_'), + fn = type(color) == 'function' and color, + no_mode = false, + link = false, + section = section, + options = options, + no_default = apply_no_default, + } +end + +---@description: retrieve highlight_groups for components +---@param highlight table return value of create_component_highlight_group +--- return value of create_component_highlight_group is to be passed in +--- this parameter to receive highlight that was created +---@return string formatted highlight group name +function M.component_format_highlight(highlight, is_focused) + if not highlight.fn then + local highlight_group = highlight.name + if highlight.no_mode then + return '%#' .. highlight_group .. '#' + end + highlight_group = append_mode(highlight_group, is_focused) + return '%#' .. highlight_group .. '#' + else + local color = highlight.fn { section = highlight.section } or {} + local hl_name = highlight.name + if type(color) == 'string' then + M.highlight(hl_name .. M.get_mode_suffix(), nil, nil, nil, color) + return '%#' .. hl_name .. M.get_mode_suffix() .. '#' + elseif type(color) == 'table' then + if not highlight.no_default and not (color.fg and color.bg) then + hl_name = append_mode(highlight.name, is_focused) + color = + get_default_component_color(hl_name, M.get_mode_suffix():sub(2), highlight.section, color, highlight.options) + end + M.highlight(hl_name, color.fg, color.bg, color.gui, color.link) + return '%#' .. hl_name .. '#', color + end + end +end + +---@description: retrieve highlight_groups for section +---@param section string highlight group name without mode +--- return value of create_component_highlight_group is to be passed in +--- this parameter to receive highlight that was created +---@param is_focused boolean +---@return string formatted highlight group name +function M.format_highlight(section, is_focused) + local mode = append_mode('', is_focused):sub(2) + local ret = '' + + if theme_hls[mode] and theme_hls[mode][section] then + ret = M.component_format_highlight(theme_hls[mode][section], is_focused) + elseif theme_hls[mode] and section > 'c' and theme_hls[mode][section_highlight_map[section]] then + ret = M.component_format_highlight(theme_hls[mode][section_highlight_map[section]], is_focused) + elseif theme_hls['normal'] and theme_hls['normal'][section] then + ret = M.component_format_highlight(theme_hls['normal'][section], is_focused) + elseif theme_hls['normal'] and section > 'c' and theme_hls['normal'][section_highlight_map[section]] then + ret = M.component_format_highlight(theme_hls['normal'][section_highlight_map[section]], is_focused) + end + + return ret +end + +---@description : Provides transitional highlights for section separators. +---@param left_hl string this highlights bg is used for fg of transitional hl +---@param right_hl string this highlights bg is used for bg of transitional hl +--- '▶️' and '◀️' ' needs reverse colors so the caller should swap left and right +---@return string formatted highlight group name +function M.get_transitional_highlights(left_hl, right_hl) + -- When both left and right highlights are same or one is absent + -- nothing to transition to. + if left_hl == nil or right_hl == nil or left_hl == right_hl then + return nil + end + + -- construct the name of highlight group + local highlight_name = table.concat({ 'lualine_transitional', left_hl, 'to', right_hl }, '_') + if not M.highlight_exists(highlight_name) then + -- Create the highlight_group if needed + -- Get colors from highlights + local fg = modules.utils.extract_highlight_colors(left_hl, 'bg') + local bg = modules.utils.extract_highlight_colors(right_hl, 'bg') + if not fg and not bg then + return nil -- Color retrieval failed + end + if bg == fg then + return nil -- Separator won't be visible anyway + end + M.highlight(highlight_name, fg, bg, nil, nil) + attach_hl(left_hl, highlight_name, 'bg', 'fg') + attach_hl(right_hl, highlight_name, 'bg', 'bg') + end + return '%#' .. highlight_name .. '#' +end + +function M.get_stl_default_hl(focused) + if focused == 3 then + return 'TabLineFill' + elseif not focused then + return 'StatusLineNC' + else + return 'StatusLine' + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/16color.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/16color.lua new file mode 100644 index 00000000..fe093a37 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/16color.lua @@ -0,0 +1,38 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit itchyny, jackno (lightline) +-- stylua: ignore +local colors = { + black = '#000000', + maroon = '#800000', + green = '#008000', + olive = '#808000', + navy = '#000080', + purple = '#800080', + teal = '#008080', + silver = '#c0c0c0', + gray = '#808080', + red = '#ff0000', + lime = '#00ff00', + yellow = '#ffff00', + blue = '#0000ff', + fuchsia = '#ff00ff', + aqua = '#00ffff', + white = '#ffffff', +} + +return { + normal = { + a = { fg = colors.white, bg = colors.blue, gui = 'bold' }, + b = { fg = colors.white, bg = colors.gray }, + c = { fg = colors.silver, bg = colors.black }, + }, + insert = { a = { fg = colors.white, bg = colors.green, gui = 'bold' } }, + visual = { a = { fg = colors.white, bg = colors.purple, gui = 'bold' } }, + replace = { a = { fg = colors.white, bg = colors.red, gui = 'bold' } }, + inactive = { + a = { fg = colors.silver, bg = colors.gray, gui = 'bold' }, + b = { fg = colors.gray, bg = colors.black }, + c = { fg = colors.silver, bg = colors.black }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/OceanicNext.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/OceanicNext.lua new file mode 100644 index 00000000..9169139d --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/OceanicNext.lua @@ -0,0 +1,43 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Generated by lightline to lualine theme converter +-- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 +-- stylua: ignore +local colors = { + color0 = '#ffffff', + color1 = '#99c794', + color2 = '#65737e', + color3 = '#343d46', + color4 = '#6699cc', + color5 = '#d8dee9', + color6 = '#f99157', + color7 = '#ec5f67', +} + +return { + insert = { + a = { fg = colors.color0, bg = colors.color1, gui = 'bold' }, + b = { fg = colors.color0, bg = colors.color2 }, + c = { fg = colors.color0, bg = colors.color3 }, + }, + normal = { + a = { fg = colors.color0, bg = colors.color4, gui = 'bold' }, + b = { fg = colors.color0, bg = colors.color2 }, + c = { fg = colors.color0, bg = colors.color3 }, + }, + inactive = { + a = { fg = colors.color5, bg = colors.color2, gui = 'bold' }, + b = { fg = colors.color5, bg = colors.color3 }, + c = { fg = colors.color2, bg = colors.color3 }, + }, + visual = { + a = { fg = colors.color0, bg = colors.color6, gui = 'bold' }, + b = { fg = colors.color0, bg = colors.color2 }, + c = { fg = colors.color0, bg = colors.color3 }, + }, + replace = { + a = { fg = colors.color0, bg = colors.color7, gui = 'bold' }, + b = { fg = colors.color0, bg = colors.color2 }, + c = { fg = colors.color0, bg = colors.color3 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/PaperColor.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/PaperColor.lua new file mode 100644 index 00000000..ea3c9bb7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/PaperColor.lua @@ -0,0 +1,6 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: itchyny(lightline) +local background = vim.opt.background:get() + +return require('lualine.themes.papercolor_' .. background) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/Tomorrow.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/Tomorrow.lua new file mode 100644 index 00000000..48031b03 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/Tomorrow.lua @@ -0,0 +1,43 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Generated by lightline to lualine theme converter +-- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 +-- stylua: ignore +local colors = { + color14 = '#718c00', + color0 = '#666666', + color1 = '#c8c8c8', + color2 = '#808080', + color3 = '#fafafa', + color4 = '#4271ae', + color5 = '#4d4d4c', + color6 = '#b4b4b4', + color7 = '#555555', + color8 = '#8959a8', + color11 = '#f5871f', +} + +return { + inactive = { + a = { fg = colors.color0, bg = colors.color1, gui = 'bold' }, + b = { fg = colors.color2, bg = colors.color3 }, + c = { fg = colors.color0, bg = colors.color1 }, + }, + normal = { + a = { fg = colors.color1, bg = colors.color4, gui = 'bold' }, + b = { fg = colors.color5, bg = colors.color6 }, + c = { fg = colors.color7, bg = colors.color1 }, + }, + visual = { + a = { fg = colors.color1, bg = colors.color8, gui = 'bold' }, + b = { fg = colors.color5, bg = colors.color6 }, + }, + replace = { + a = { fg = colors.color1, bg = colors.color11, gui = 'bold' }, + b = { fg = colors.color5, bg = colors.color6 }, + }, + insert = { + a = { fg = colors.color1, bg = colors.color14, gui = 'bold' }, + b = { fg = colors.color5, bg = colors.color6 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/auto.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/auto.lua new file mode 100644 index 00000000..fe106bdb --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/auto.lua @@ -0,0 +1,172 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local utils = require('lualine.utils.utils') +local loader = require('lualine.utils.loader') + +local color_name = vim.g.colors_name +if color_name then + -- All base16 colorschemes share the same theme + if 'base16' == color_name:sub(1, 6) then + color_name = 'base16' + end + + -- Check if there's a theme for current colorscheme + -- If there is load that instead of generating a new one + local ok, theme = pcall(loader.load_theme, color_name) + if ok and theme then + return theme + end +end + +--------------- +-- Constants -- +--------------- +-- fg and bg must have this much contrast range 0 < contrast_threshold < 0.5 +local contrast_threshold = 0.3 +-- how much brightness is changed in percentage for light and dark themes +local brightness_modifier_parameter = 10 + +-- Turns #rrggbb -> { red, green, blue } +local function rgb_str2num(rgb_color_str) + if rgb_color_str:find('#') == 1 then + rgb_color_str = rgb_color_str:sub(2, #rgb_color_str) + end + local red = tonumber(rgb_color_str:sub(1, 2), 16) + local green = tonumber(rgb_color_str:sub(3, 4), 16) + local blue = tonumber(rgb_color_str:sub(5, 6), 16) + return { red = red, green = green, blue = blue } +end + +-- Turns { red, green, blue } -> #rrggbb +local function rgb_num2str(rgb_color_num) + local rgb_color_str = string.format('#%02x%02x%02x', rgb_color_num.red, rgb_color_num.green, rgb_color_num.blue) + return rgb_color_str +end + +-- Returns brightness level of color in range 0 to 1 +-- arbitrary value it's basically an weighted average +local function get_color_brightness(rgb_color) + local color = rgb_str2num(rgb_color) + local brightness = (color.red * 2 + color.green * 3 + color.blue) / 6 + return brightness / 256 +end + +-- returns average of colors in range 0 to 1 +-- used to determine contrast level +local function get_color_avg(rgb_color) + local color = rgb_str2num(rgb_color) + return (color.red + color.green + color.blue) / 3 / 256 +end + +-- Clamps the val between left and right +local function clamp(val, left, right) + if val > right then + return right + end + if val < left then + return left + end + return val +end + +-- Changes brightness of rgb_color by percentage +local function brightness_modifier(rgb_color, percentage) + local color = rgb_str2num(rgb_color) + color.red = clamp(color.red + (color.red * percentage / 100), 0, 255) + color.green = clamp(color.green + (color.green * percentage / 100), 0, 255) + color.blue = clamp(color.blue + (color.blue * percentage / 100), 0, 255) + return rgb_num2str(color) +end + +-- Changes contrast of rgb_color by amount +local function contrast_modifier(rgb_color, amount) + local color = rgb_str2num(rgb_color) + color.red = clamp(color.red + amount, 0, 255) + color.green = clamp(color.green + amount, 0, 255) + color.blue = clamp(color.blue + amount, 0, 255) + return rgb_num2str(color) +end + +-- Changes brightness of foreground color to achieve contrast +-- without changing the color +local function apply_contrast(highlight) + local highlight_bg_avg = get_color_avg(highlight.bg) + local contrast_threshold_config = clamp(contrast_threshold, 0, 0.5) + local contrast_change_step = 5 + if highlight_bg_avg > 0.5 then + contrast_change_step = -contrast_change_step + end + + -- Don't waste too much time here max 25 iteration should be more than enough + local iteration_count = 1 + while math.abs(get_color_avg(highlight.fg) - highlight_bg_avg) < contrast_threshold_config and iteration_count < 25 do + highlight.fg = contrast_modifier(highlight.fg, contrast_change_step) + iteration_count = iteration_count + 1 + end +end + +-- Get the colors to create theme +-- stylua: ignore +local colors = { + normal = utils.extract_color_from_hllist('bg', { 'PmenuSel', 'PmenuThumb', 'TabLineSel' }, '#000000'), + insert = utils.extract_color_from_hllist('fg', { 'String', 'MoreMsg' }, '#000000'), + replace = utils.extract_color_from_hllist('fg', { 'Number', 'Type' }, '#000000'), + visual = utils.extract_color_from_hllist('fg', { 'Special', 'Boolean', 'Constant' }, '#000000'), + command = utils.extract_color_from_hllist('fg', { 'Identifier' }, '#000000'), + back1 = utils.extract_color_from_hllist('bg', { 'Normal', 'StatusLineNC' }, '#000000'), + fore = utils.extract_color_from_hllist('fg', { 'Normal', 'StatusLine' }, '#000000'), + back2 = utils.extract_color_from_hllist('bg', { 'StatusLine' }, '#000000'), +} + +-- Change brightness of colors +-- Darken if light theme (or) Lighten if dark theme +local normal_color = utils.extract_highlight_colors('Normal', 'bg') +if normal_color ~= nil then + if get_color_brightness(normal_color) > 0.5 then + brightness_modifier_parameter = -brightness_modifier_parameter + end + for name, color in pairs(colors) do + colors[name] = brightness_modifier(color, brightness_modifier_parameter) + end +end + +-- Basic theme definition +local M = { + normal = { + a = { bg = colors.normal, fg = colors.back1, gui = 'bold' }, + b = { bg = colors.back1, fg = colors.normal }, + c = { bg = colors.back2, fg = colors.fore }, + }, + insert = { + a = { bg = colors.insert, fg = colors.back1, gui = 'bold' }, + b = { bg = colors.back1, fg = colors.insert }, + c = { bg = colors.back2, fg = colors.fore }, + }, + replace = { + a = { bg = colors.replace, fg = colors.back1, gui = 'bold' }, + b = { bg = colors.back1, fg = colors.replace }, + c = { bg = colors.back2, fg = colors.fore }, + }, + visual = { + a = { bg = colors.visual, fg = colors.back1, gui = 'bold' }, + b = { bg = colors.back1, fg = colors.visual }, + c = { bg = colors.back2, fg = colors.fore }, + }, + command = { + a = { bg = colors.command, fg = colors.back1, gui = 'bold' }, + b = { bg = colors.back1, fg = colors.command }, + c = { bg = colors.back2, fg = colors.fore }, + }, +} + +M.terminal = M.command +M.inactive = M.normal + +-- Apply proper contrast so text is readable +for _, section in pairs(M) do + for _, highlight in pairs(section) do + apply_contrast(highlight) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu.lua new file mode 100644 index 00000000..b7843064 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu.lua @@ -0,0 +1,8 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: itchyny(lightline) +-- License: MIT License +local background = vim.opt.background:get() +local style = vim.g.ayucolor or ((background == 'dark') and vim.g.ayuprefermirage and 'mirage' or background) + +return require('lualine.themes.ayu_' .. style) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu_dark.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu_dark.lua new file mode 100644 index 00000000..7dc8f75a --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu_dark.lua @@ -0,0 +1,40 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Generated by lightline to lualine theme converter +-- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 +-- stylua: ignore +local colors = { + color2 = '#0f1419', + color3 = '#ffee99', + color4 = '#e6e1cf', + color5 = '#14191f', + color13 = '#b8cc52', + color10 = '#36a3d9', + color8 = '#f07178', + color9 = '#3e4b59', +} + +return { + visual = { + a = { fg = colors.color2, bg = colors.color3, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + replace = { + a = { fg = colors.color2, bg = colors.color8, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + inactive = { + c = { fg = colors.color4, bg = colors.color2 }, + a = { fg = colors.color4, bg = colors.color5, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + normal = { + c = { fg = colors.color9, bg = colors.color2 }, + a = { fg = colors.color2, bg = colors.color10, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + insert = { + a = { fg = colors.color2, bg = colors.color13, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu_light.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu_light.lua new file mode 100644 index 00000000..0c0d1b6a --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu_light.lua @@ -0,0 +1,40 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Generated by lightline to lualine theme converter +-- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 +-- stylua: ignore +local colors = { + color2 = '#f3f3f3', + color3 = '#A37ACC', + color4 = '#5C6773', + color5 = '#d3d3d3', + color13 = '#86B300', + color10 = '#59c2ff', + color8 = '#f07178', + color9 = '#828C99', +} + +return { + visual = { + a = { fg = colors.color2, bg = colors.color3, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + replace = { + a = { fg = colors.color2, bg = colors.color8, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + inactive = { + c = { fg = colors.color4, bg = colors.color2 }, + a = { fg = colors.color4, bg = colors.color5, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + normal = { + c = { fg = colors.color9, bg = colors.color2 }, + a = { fg = colors.color2, bg = colors.color10, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + insert = { + a = { fg = colors.color2, bg = colors.color13, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu_mirage.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu_mirage.lua new file mode 100644 index 00000000..5bc9f38b --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/ayu_mirage.lua @@ -0,0 +1,40 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Generated by lightline to lualine theme converter +-- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 +-- stylua: ignore +local colors = { + color2 = '#242b38', + color3 = '#d4bfff', + color4 = '#d9d7ce', + color5 = '#272d38', + color13 = '#bbe67e', + color10 = '#59c2ff', + color8 = '#f07178', + color9 = '#607080', +} + +return { + visual = { + a = { fg = colors.color2, bg = colors.color3, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + replace = { + a = { fg = colors.color2, bg = colors.color8, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + inactive = { + c = { fg = colors.color4, bg = colors.color2 }, + a = { fg = colors.color4, bg = colors.color5, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + normal = { + c = { fg = colors.color9, bg = colors.color2 }, + a = { fg = colors.color2, bg = colors.color10, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + insert = { + a = { fg = colors.color2, bg = colors.color13, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/base16.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/base16.lua new file mode 100644 index 00000000..daf6a6bf --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/base16.lua @@ -0,0 +1,112 @@ +local modules = require('lualine_require').lazy_require { notices = 'lualine.utils.notices' } + +local function add_notice(notice) + modules.notices.add_notice('theme(base16): ' .. notice) +end + +local function setup(colors) + local theme = { + normal = { + a = { fg = colors.bg, bg = colors.normal }, + b = { fg = colors.light_fg, bg = colors.alt_bg }, + c = { fg = colors.fg, bg = colors.bg }, + }, + replace = { + a = { fg = colors.bg, bg = colors.replace }, + b = { fg = colors.light_fg, bg = colors.alt_bg }, + }, + insert = { + a = { fg = colors.bg, bg = colors.insert }, + b = { fg = colors.light_fg, bg = colors.alt_bg }, + }, + visual = { + a = { fg = colors.bg, bg = colors.visual }, + b = { fg = colors.light_fg, bg = colors.alt_bg }, + }, + inactive = { + a = { fg = colors.dark_fg, bg = colors.bg }, + b = { fg = colors.dark_fg, bg = colors.bg }, + c = { fg = colors.dark_fg, bg = colors.bg }, + }, + } + + theme.command = theme.normal + theme.terminal = theme.insert + + return theme +end + +local function setup_default() + return setup { + bg = '#282a2e', + alt_bg = '#373b41', + dark_fg = '#969896', + fg = '#b4b7b4', + light_fg = '#c5c8c6', + normal = '#81a2be', + insert = '#b5bd68', + visual = '#b294bb', + replace = '#de935f', + } +end + +local function setup_base16_nvim() + -- Continue to load nvim-base16 + local loaded, base16 = pcall(require, 'base16-colorscheme') + + if not loaded then + add_notice( + 'nvim-base16 is not currently present in your runtimepath, make sure it is properly installed,' + .. ' fallback to default colors.' + ) + + return nil + end + + if not base16.colors and not vim.env.BASE16_THEME then + add_notice( + 'nvim-base16 is not loaded yet, you should update your configuration to load it before lualine' + .. ' so that the colors from your colorscheme can be used, fallback to "tomorrow-night" theme.' + ) + elseif not base16.colors and not base16.colorschemes[vim.env.BASE16_THEME] then + add_notice( + 'The colorscheme "%s" defined by the environment variable "BASE16_THEME" is not handled by' + .. ' nvim-base16, fallback to "tomorrow-night" theme.' + ) + end + + local colors = base16.colors or base16.colorschemes[vim.env.BASE16_THEME or 'tomorrow-night'] + + return setup { + bg = colors.base01, + alt_bg = colors.base02, + dark_fg = colors.base03, + fg = colors.base04, + light_fg = colors.base05, + normal = colors.base0D, + insert = colors.base0B, + visual = colors.base0E, + replace = colors.base09, + } +end + +local function setup_base16_vim() + -- Check if tinted-theming/base16-vim is already loaded + if vim.g.base16_gui00 and vim.g.base16_gui0F then + return setup { + bg = vim.g.base16_gui01, + alt_bg = vim.g.base16_gui02, + dark_fg = vim.g.base16_gui03, + fg = vim.g.base16_gui04, + light_fg = vim.g.base16_gui05, + normal = vim.g.base16_gui0D, + insert = vim.g.base16_gui0B, + visual = vim.g.base16_gui0E, + replace = vim.g.base16_gui09, + } + end + + return nil +end + +return setup_base16_vim() or setup_base16_nvim() or setup_default() diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/codedark.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/codedark.lua new file mode 100644 index 00000000..cc5ffdae --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/codedark.lua @@ -0,0 +1,38 @@ +-- Copyright (c) 2020-2021 Shatur95 +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + gray = '#3C3C3C', + lightred = '#D16969', + blue = '#569CD6', + pink = '#C586C0', + black = '#262626', + white = '#D4D4D4', + green = '#608B4E', +} + +return { + normal = { + b = { fg = colors.green, bg = colors.black }, + a = { fg = colors.black, bg = colors.green, gui = 'bold' }, + c = { fg = colors.white, bg = colors.black }, + }, + visual = { + b = { fg = colors.pink, bg = colors.black }, + a = { fg = colors.black, bg = colors.pink, gui = 'bold' }, + }, + inactive = { + b = { fg = colors.black, bg = colors.blue }, + a = { fg = colors.white, bg = colors.gray, gui = 'bold' }, + }, + replace = { + b = { fg = colors.lightred, bg = colors.black }, + a = { fg = colors.black, bg = colors.lightred, gui = 'bold' }, + c = { fg = colors.white, bg = colors.black }, + }, + insert = { + b = { fg = colors.blue, bg = colors.black }, + a = { fg = colors.black, bg = colors.blue, gui = 'bold' }, + c = { fg = colors.white, bg = colors.black }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/dracula.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/dracula.lua new file mode 100644 index 00000000..6387201f --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/dracula.lua @@ -0,0 +1,48 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit itchyny, jackno (lightline) +-- stylua: ignore +local colors = { + gray = '#44475a', + lightgray = '#5f6a8e', + orange = '#ffb86c', + purple = '#bd93f9', + red = '#ff5555', + yellow = '#f1fa8c', + green = '#50fa7b', + white = '#f8f8f2', + black = '#282a36', +} + +return { + normal = { + a = { bg = colors.purple, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.gray, fg = colors.white }, + }, + insert = { + a = { bg = colors.green, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.gray, fg = colors.white }, + }, + visual = { + a = { bg = colors.yellow, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.gray, fg = colors.white }, + }, + replace = { + a = { bg = colors.red, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.gray, fg = colors.white }, + }, + command = { + a = { bg = colors.orange, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.gray, fg = colors.white }, + }, + inactive = { + a = { bg = colors.gray, fg = colors.white, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.gray, fg = colors.white }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/everforest.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/everforest.lua new file mode 100644 index 00000000..d71b8a94 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/everforest.lua @@ -0,0 +1,53 @@ +-- Copyright (c) 2020-2021 gnuyent +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + bg0 = '#323d43', + bg1 = '#3c474d', + bg3 = '#505a60', + fg = '#d8caac', + aqua = '#87c095', + green = '#a7c080', + orange = '#e39b7b', + purple = '#d39bb6', + red = '#e68183', + grey1 = '#868d80', +} + +return { + normal = { + a = { bg = colors.green, fg = colors.bg0, gui = 'bold' }, + b = { bg = colors.bg3, fg = colors.fg }, + c = { bg = colors.bg1, fg = colors.fg }, + }, + insert = { + a = { bg = colors.fg, fg = colors.bg0, gui = 'bold' }, + b = { bg = colors.bg3, fg = colors.fg }, + c = { bg = colors.bg1, fg = colors.fg }, + }, + visual = { + a = { bg = colors.red, fg = colors.bg0, gui = 'bold' }, + b = { bg = colors.bg3, fg = colors.fg }, + c = { bg = colors.bg1, fg = colors.fg }, + }, + replace = { + a = { bg = colors.orange, fg = colors.bg0, gui = 'bold' }, + b = { bg = colors.bg3, fg = colors.fg }, + c = { bg = colors.bg1, fg = colors.fg }, + }, + command = { + a = { bg = colors.aqua, fg = colors.bg0, gui = 'bold' }, + b = { bg = colors.bg3, fg = colors.fg }, + c = { bg = colors.bg1, fg = colors.fg }, + }, + terminal = { + a = { bg = colors.purple, fg = colors.bg0, gui = 'bold' }, + b = { bg = colors.bg3, fg = colors.fg }, + c = { bg = colors.bg1, fg = colors.fg }, + }, + inactive = { + a = { bg = colors.bg1, fg = colors.grey1, gui = 'bold' }, + b = { bg = colors.bg1, fg = colors.grey1 }, + c = { bg = colors.bg1, fg = colors.grey1 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox-material.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox-material.lua new file mode 100644 index 00000000..5e2aeb78 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox-material.lua @@ -0,0 +1,29 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + fg1 = '#282828', + color2 = '#504945', + fg2 = '#ddc7a1', + color3 = '#32302f', + color4 = '#a89984', + color5 = '#7daea3', + color6 = '#a9b665', + color7 = '#d8a657', + color8 = '#d3869b', + color9 = '#ea6962', +} + +return { + normal = { + a = { fg = colors.fg1, bg = colors.color4, gui = 'bold' }, + b = { fg = colors.fg2, bg = colors.color2 }, + c = { fg = colors.fg2, bg = colors.color3 }, + }, + command = { a = { fg = colors.fg1, bg = colors.color5, gui = 'bold' } }, + inactive = { a = { fg = colors.fg2, bg = colors.color2 } }, + insert = { a = { fg = colors.fg1, bg = colors.color6, gui = 'bold' } }, + replace = { a = { fg = colors.fg1, bg = colors.color7, gui = 'bold' } }, + terminal = { a = { fg = colors.fg1, bg = colors.color8, gui = 'bold' } }, + visual = { a = { fg = colors.fg1, bg = colors.color9, gui = 'bold' } }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox.lua new file mode 100644 index 00000000..c284b0fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox.lua @@ -0,0 +1,7 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: itchyny(lightline) +-- License: MIT License +local background = vim.opt.background:get() + +return require('lualine.themes.gruvbox_' .. background) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox_dark.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox_dark.lua new file mode 100644 index 00000000..00bd62d1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox_dark.lua @@ -0,0 +1,48 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + black = '#282828', + white = '#ebdbb2', + red = '#fb4934', + green = '#b8bb26', + blue = '#83a598', + yellow = '#fe8019', + gray = '#a89984', + darkgray = '#3c3836', + lightgray = '#504945', + inactivegray = '#7c6f64', +} + +return { + normal = { + a = { bg = colors.gray, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.darkgray, fg = colors.gray }, + }, + insert = { + a = { bg = colors.blue, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.lightgray, fg = colors.white }, + }, + visual = { + a = { bg = colors.yellow, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.inactivegray, fg = colors.black }, + }, + replace = { + a = { bg = colors.red, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.black, fg = colors.white }, + }, + command = { + a = { bg = colors.green, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.inactivegray, fg = colors.black }, + }, + inactive = { + a = { bg = colors.darkgray, fg = colors.gray, gui = 'bold' }, + b = { bg = colors.darkgray, fg = colors.gray }, + c = { bg = colors.darkgray, fg = colors.gray }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox_light.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox_light.lua new file mode 100644 index 00000000..75adb3a7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/gruvbox_light.lua @@ -0,0 +1,47 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + black = '#3c3836', + white = '#f9f5d7', + orange = '#af3a03', + green = '#427b58', + blue = '#076678', + gray = '#d5c4a1', + darkgray = '#7c6f64', + lightgray = '#ebdbb2', + inactivegray = '#a89984' +} + +return { + normal = { + a = { bg = colors.darkgray, fg = colors.white, gui = 'bold' }, + b = { bg = colors.gray, fg = colors.darkgray }, + c = { bg = colors.lightgray, fg = colors.darkgray }, + }, + insert = { + a = { bg = colors.blue, fg = colors.white, gui = 'bold' }, + b = { bg = colors.gray, fg = colors.darkgray }, + c = { bg = colors.gray, fg = colors.black }, + }, + visual = { + a = { bg = colors.orange, fg = colors.white, gui = 'bold' }, + b = { bg = colors.gray, fg = colors.darkgray }, + c = { bg = colors.darkgray, fg = colors.white }, + }, + replace = { + a = { bg = colors.green, fg = colors.white, gui = 'bold' }, + b = { bg = colors.gray, fg = colors.darkgray }, + c = { bg = colors.gray, fg = colors.black }, + }, + command = { + a = { bg = colors.darkgray, fg = colors.white, gui = 'bold' }, + b = { bg = colors.gray, fg = colors.darkgray }, + c = { bg = colors.lightgray, fg = colors.darkgray }, + }, + inactive = { + a = { bg = colors.lightgray, fg = colors.inactivegray }, + b = { bg = colors.lightgray, fg = colors.inactivegray }, + c = { bg = colors.lightgray, fg = colors.inactivegray }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/horizon.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/horizon.lua new file mode 100644 index 00000000..020d619c --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/horizon.lua @@ -0,0 +1,48 @@ +-- Copyright (c) 2021 Jnhtr +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + black = '#1c1e26', + white = '#6C6F93', + red = '#F43E5C', + green = '#09F7A0', + blue = '#25B2BC', + yellow = '#F09383', + gray = '#E95678', + darkgray = '#1A1C23', + lightgray = '#2E303E', + inactivegray = '#1C1E26', +} + +return { + normal = { + a = { bg = colors.gray, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.darkgray, fg = colors.white }, + }, + insert = { + a = { bg = colors.blue, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.darkgray, fg = colors.white }, + }, + visual = { + a = { bg = colors.yellow, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.darkgray, fg = colors.white }, + }, + replace = { + a = { bg = colors.red, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.darkgray, fg = colors.white }, + }, + command = { + a = { bg = colors.green, fg = colors.black, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.white }, + c = { bg = colors.darkgray, fg = colors.white }, + }, + inactive = { + a = { bg = colors.inactivegray, fg = colors.lightgray, gui = 'bold' }, + b = { bg = colors.inactivegray, fg = colors.lightgray }, + c = { bg = colors.inactivegray, fg = colors.lightgray }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/iceberg.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/iceberg.lua new file mode 100644 index 00000000..ca241333 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/iceberg.lua @@ -0,0 +1,7 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: itchyny(lightline) +-- License: MIT License +local background = vim.opt.background:get() + +return require('lualine.themes.iceberg_' .. background) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/iceberg_dark.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/iceberg_dark.lua new file mode 100644 index 00000000..a24b5429 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/iceberg_dark.lua @@ -0,0 +1,42 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Generated by lightline to lualine theme converter +-- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 +-- stylua: ignore +local colors = { + color2 = '#161821', + color3 = '#b4be82', + color4 = '#c6c8d1', + color5 = '#2e313f', + color8 = '#e2a478', + color9 = '#3e445e', + color10 = '#0f1117', + color11 = '#17171b', + color12 = '#818596', + color15 = '#84a0c6', +} + +return { + visual = { + a = { fg = colors.color2, bg = colors.color3, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + replace = { + a = { fg = colors.color2, bg = colors.color8, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + inactive = { + a = { fg = colors.color9, bg = colors.color10, gui = 'bold' }, + b = { fg = colors.color9, bg = colors.color10 }, + c = { fg = colors.color9, bg = colors.color10 }, + }, + normal = { + a = { fg = colors.color11, bg = colors.color12, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + c = { fg = colors.color4, bg = colors.color10 }, + }, + insert = { + a = { fg = colors.color2, bg = colors.color15, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/iceberg_light.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/iceberg_light.lua new file mode 100644 index 00000000..3d9ada93 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/iceberg_light.lua @@ -0,0 +1,40 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Generated by lightline to lualine theme converter +-- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 +-- stylua: ignore +local colors = { + color5 = '#668e3d', + color8 = '#757ca3', + color9 = '#8b98b6', + color10 = '#cad0de', + color11 = '#2d539e', + color0 = '#e8e9ec', + color1 = '#9fa6c0', + color2 = '#c57339', +} + +return { + replace = { + b = { fg = colors.color0, bg = colors.color1 }, + a = { fg = colors.color0, bg = colors.color2, gui = 'bold' }, + }, + visual = { + b = { fg = colors.color0, bg = colors.color1 }, + a = { fg = colors.color0, bg = colors.color5, gui = 'bold' }, + }, + normal = { + b = { fg = colors.color0, bg = colors.color1 }, + a = { fg = colors.color0, bg = colors.color8, gui = 'bold' }, + c = { fg = colors.color9, bg = colors.color10 }, + }, + inactive = { + b = { fg = colors.color9, bg = colors.color10 }, + a = { fg = colors.color9, bg = colors.color10, gui = 'bold' }, + c = { fg = colors.color9, bg = colors.color10 }, + }, + insert = { + b = { fg = colors.color0, bg = colors.color1 }, + a = { fg = colors.color0, bg = colors.color11, gui = 'bold' }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/jellybeans.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/jellybeans.lua new file mode 100644 index 00000000..d4215614 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/jellybeans.lua @@ -0,0 +1,41 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Generated by lightline to lualine theme converter +-- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 +-- stylua: ignore +local colors = { + color2 = '#30302c', + color3 = '#f0a0c0', + color4 = '#e8e8d3', + color5 = '#4e4e43', + color8 = '#cf6a4c', + color9 = '#666656', + color10 = '#808070', + color11 = '#8197bf', + color14 = '#99ad6a', +} + +return { + visual = { + a = { fg = colors.color2, bg = colors.color3, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + replace = { + a = { fg = colors.color2, bg = colors.color8, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + inactive = { + c = { fg = colors.color9, bg = colors.color2 }, + a = { fg = colors.color10, bg = colors.color2, gui = 'bold' }, + b = { fg = colors.color9, bg = colors.color2 }, + }, + normal = { + c = { fg = colors.color10, bg = colors.color2 }, + a = { fg = colors.color2, bg = colors.color11, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, + insert = { + a = { fg = colors.color2, bg = colors.color14, gui = 'bold' }, + b = { fg = colors.color4, bg = colors.color5 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/material.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/material.lua new file mode 100644 index 00000000..2d2bcb19 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/material.lua @@ -0,0 +1,42 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: Lokesh Krishna(lightline) +-- stylua: ignore +local colors = { + fg = '#eeffff', + bg = '#263238', + blue = '#82aaff', + green = '#c3e88d', + purple = '#c792ea', + red1 = '#f07178', + red2 = '#ff5370', + yellow = '#ffcb6b', + gray1 = '#314549', + gray2 = '#2E3C43', + gray3 = '#515559', +} + +return { + normal = { + a = { fg = colors.bg, bg = colors.blue, gui = 'bold' }, + b = { fg = colors.fg, bg = colors.gray3 }, + c = { fg = colors.fg, bg = colors.gray2 }, + }, + insert = { + a = { fg = colors.bg, bg = colors.green, gui = 'bold' }, + b = { fg = colors.fg, bg = colors.gray3 }, + }, + visual = { + a = { fg = colors.bg, bg = colors.purple, gui = 'bold' }, + b = { fg = colors.fg, bg = colors.gray3 }, + }, + replace = { + a = { fg = colors.bg, bg = colors.red1, gui = 'bold' }, + b = { fg = colors.fg, bg = colors.gray3 }, + }, + inactive = { + a = { fg = colors.fg, bg = colors.bg, gui = 'bold' }, + b = { fg = colors.fg, bg = colors.bg }, + c = { fg = colors.fg, bg = colors.gray2 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/modus-vivendi.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/modus-vivendi.lua new file mode 100644 index 00000000..3ad42af8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/modus-vivendi.lua @@ -0,0 +1,48 @@ +-- Copyright (c) 2020-2021 ronniedroid +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + black = '#000000', + white = '#eeeeee', + red = '#ffa0a0', + green = '#88cf88', + blue = '#92baff', + magenta = '#feacd0', + cyan = '#a0bfdf', + gray = '#2f2f2f', + darkgray = '#202020', + lightgray = '#434343' +} + +return { + normal = { + a = { bg = colors.blue, fg = colors.lightgray, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.blue }, + c = { bg = colors.gray, fg = colors.white }, + }, + insert = { + a = { bg = colors.cyan, fg = colors.lightgray, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.cyan }, + c = { bg = colors.gray, fg = colors.white }, + }, + visual = { + a = { bg = colors.magenta, fg = colors.lightgray, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.magenta }, + c = { bg = colors.gray, fg = colors.white }, + }, + replace = { + a = { bg = colors.red, fg = colors.lightgray, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.red }, + c = { bg = colors.gray, fg = colors.white }, + }, + command = { + a = { bg = colors.green, fg = colors.lightgray, gui = 'bold' }, + b = { bg = colors.lightgray, fg = colors.green }, + c = { bg = colors.gray, fg = colors.white }, + }, + inactive = { + a = { bg = colors.darkgray, fg = colors.lightgray, gui = 'bold' }, + b = { bg = colors.darkgray, fg = colors.lightgray }, + c = { bg = colors.darkgray, fg = colors.lightgray }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/molokai.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/molokai.lua new file mode 100644 index 00000000..c7909dee --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/molokai.lua @@ -0,0 +1,31 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: challsted(lightline) +-- stylua: ignore +local colors = { + black = '#232526', + gray = '#808080', + white = '#f8f8f2', + cyan = '#66d9ef', + green = '#a6e22e', + orange = '#ef5939', + pink = '#f92672', + red = '#ff0000', + yellow = '#e6db74', +} + +return { + normal = { + a = { fg = colors.black, bg = colors.cyan, gui = 'bold' }, + b = { fg = colors.black, bg = colors.pink }, + c = { fg = colors.orange, bg = colors.black }, + }, + insert = { a = { fg = colors.black, bg = colors.green, gui = 'bold' } }, + visual = { a = { fg = colors.black, bg = colors.yellow, gui = 'bold' } }, + replace = { a = { fg = colors.black, bg = colors.red, gui = 'bold' } }, + inactive = { + a = { fg = colors.pink, bg = colors.black, gui = 'bold' }, + b = { fg = colors.white, bg = colors.pink }, + c = { fg = colors.gray, bg = colors.black }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/moonfly.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/moonfly.lua new file mode 100644 index 00000000..cc832772 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/moonfly.lua @@ -0,0 +1,40 @@ +-- moonfly color scheme for lualine +-- +-- URL: github.com/bluz71/vim-moonfly-colors +-- License: MIT (https://opensource.org/licenses/MIT) + +-- stylua: ignore +local colors = { + color3 = '#303030', + color6 = '#9e9e9e', + color7 = '#80a0ff', + color8 = '#ae81ff', + color0 = '#1c1c1c', + color1 = '#ff5189', + color2 = '#c6c6c6', +} + +return { + replace = { + a = { fg = colors.color0, bg = colors.color1, gui = 'bold' }, + b = { fg = colors.color2, bg = colors.color3 }, + }, + inactive = { + a = { fg = colors.color6, bg = colors.color3, gui = 'bold' }, + b = { fg = colors.color6, bg = colors.color3 }, + c = { fg = colors.color6, bg = colors.color3 }, + }, + normal = { + a = { fg = colors.color0, bg = colors.color7, gui = 'bold' }, + b = { fg = colors.color2, bg = colors.color3 }, + c = { fg = colors.color2, bg = colors.color3 }, + }, + visual = { + a = { fg = colors.color0, bg = colors.color8, gui = 'bold' }, + b = { fg = colors.color2, bg = colors.color3 }, + }, + insert = { + a = { fg = colors.color0, bg = colors.color2, gui = 'bold' }, + b = { fg = colors.color2, bg = colors.color3 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/nightfly.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/nightfly.lua new file mode 100644 index 00000000..3df5b184 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/nightfly.lua @@ -0,0 +1,37 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + color3 = '#2c3043', + color6 = '#a1aab8', + color7 = '#82aaff', + color8 = '#ae81ff', + color0 = '#092236', + color1 = '#ff5874', + color2 = '#c3ccdc', +} + +return { + replace = { + a = { fg = colors.color0, bg = colors.color1, gui = 'bold' }, + b = { fg = colors.color2, bg = colors.color3 }, + }, + inactive = { + a = { fg = colors.color6, bg = colors.color3, gui = 'bold' }, + b = { fg = colors.color6, bg = colors.color3 }, + c = { fg = colors.color6, bg = colors.color3 }, + }, + normal = { + a = { fg = colors.color0, bg = colors.color7, gui = 'bold' }, + b = { fg = colors.color2, bg = colors.color3 }, + c = { fg = colors.color2, bg = colors.color3 }, + }, + visual = { + a = { fg = colors.color0, bg = colors.color8, gui = 'bold' }, + b = { fg = colors.color2, bg = colors.color3 }, + }, + insert = { + a = { fg = colors.color0, bg = colors.color2, gui = 'bold' }, + b = { fg = colors.color2, bg = colors.color3 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/nord.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/nord.lua new file mode 100644 index 00000000..2b3b4c26 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/nord.lua @@ -0,0 +1,28 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + nord1 = '#3B4252', + nord3 = '#4C566A', + nord5 = '#E5E9F0', + nord6 = '#ECEFF4', + nord7 = '#8FBCBB', + nord8 = '#88C0D0', + nord13 = '#EBCB8B', +} + +return { + normal = { + a = { fg = colors.nord1, bg = colors.nord8, gui = 'bold' }, + b = { fg = colors.nord5, bg = colors.nord1 }, + c = { fg = colors.nord5, bg = colors.nord3 }, + }, + insert = { a = { fg = colors.nord1, bg = colors.nord6, gui = 'bold' } }, + visual = { a = { fg = colors.nord1, bg = colors.nord7, gui = 'bold' } }, + replace = { a = { fg = colors.nord1, bg = colors.nord13, gui = 'bold' } }, + inactive = { + a = { fg = colors.nord1, bg = colors.nord8, gui = 'bold' }, + b = { fg = colors.nord5, bg = colors.nord1 }, + c = { fg = colors.nord5, bg = colors.nord1 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/onedark.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/onedark.lua new file mode 100644 index 00000000..d252eb22 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/onedark.lua @@ -0,0 +1,36 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: Zoltan Dalmadi(lightline) +-- stylua: ignore +local colors = { + blue = '#61afef', + green = '#98c379', + purple = '#c678dd', + cyan = '#56b6c2', + red1 = '#e06c75', + red2 = '#be5046', + yellow = '#e5c07b', + fg = '#abb2bf', + bg = '#282c34', + gray1 = '#828997', + gray2 = '#2c323c', + gray3 = '#3e4452', +} + +return { + normal = { + a = { fg = colors.bg, bg = colors.green, gui = 'bold' }, + b = { fg = colors.fg, bg = colors.gray3 }, + c = { fg = colors.fg, bg = colors.gray2 }, + }, + command = { a = { fg = colors.bg, bg = colors.yellow, gui = 'bold' } }, + insert = { a = { fg = colors.bg, bg = colors.blue, gui = 'bold' } }, + visual = { a = { fg = colors.bg, bg = colors.purple, gui = 'bold' } }, + terminal = { a = { fg = colors.bg, bg = colors.cyan, gui = 'bold' } }, + replace = { a = { fg = colors.bg, bg = colors.red1, gui = 'bold' } }, + inactive = { + a = { fg = colors.gray1, bg = colors.bg, gui = 'bold' }, + b = { fg = colors.gray1, bg = colors.bg }, + c = { fg = colors.gray1, bg = colors.gray2 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/onelight.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/onelight.lua new file mode 100644 index 00000000..03b6868e --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/onelight.lua @@ -0,0 +1,34 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: Zoltan Dalmadi(lightline) +-- stylua: ignore +local colors = { + blue = '#4078f2', + green = '#50a14f', + purple = '#a626a4', + red1 = '#e45649', + red2 = '#ca1243', + yellow = '#c18401', + fg = '#494b53', + bg = '#fafafa', + gray1 = '#696c77', + gray2 = '#f0f0f0', + gray3 = '#d0d0d0', +} + +return { + normal = { + a = { fg = colors.bg, bg = colors.green, gui = 'bold' }, + b = { fg = colors.fg, bg = colors.gray3 }, + c = { fg = colors.fg, bg = colors.gray2 }, + }, + command = { a = { fg = colors.bg, bg = colors.yellow, gui = 'bold' } }, + insert = { a = { fg = colors.bg, bg = colors.blue, gui = 'bold' } }, + visual = { a = { fg = colors.bg, bg = colors.purple, gui = 'bold' } }, + replace = { a = { fg = colors.bg, bg = colors.red1, gui = 'bold' } }, + inactive = { + a = { fg = colors.bg, bg = colors.gray3, gui = 'bold' }, + b = { fg = colors.bg, bg = colors.gray3 }, + c = { fg = colors.gray3, bg = colors.gray2 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/palenight.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/palenight.lua new file mode 100644 index 00000000..5e66dab7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/palenight.lua @@ -0,0 +1,47 @@ +-- Copyright (c) 2020-2021 IGI-111 +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + vertsplit = '#181A1F', + special_grey = '#3B4048', + menu_grey = '#3E4452', + cursor_grey = '#2C323C', + gutter_fg_grey = '#4B5263', + blue = '#82b1ff', + dark_red = '#BE5046', + white = '#bfc7d5', + green = '#C3E88D', + purple = '#c792ea', + yellow = '#ffcb6b', + light_red = '#ff869a', + red = '#ff5370', + dark_yellow = '#F78C6C', + cyan = '#89DDFF', + comment_grey = '#697098', + black = '#292D3E', +} + +return { + normal = { + a = { fg = colors.black, bg = colors.purple, gui = 'bold' }, + b = { fg = colors.purple, bg = colors.menu_grey }, + c = { fg = colors.comment_grey, bg = colors.black }, + }, + insert = { + a = { fg = colors.black, bg = colors.blue, gui = 'bold' }, + b = { fg = colors.blue, bg = colors.menu_grey }, + }, + visual = { + a = { fg = colors.black, bg = colors.cyan, gui = 'bold' }, + b = { fg = colors.cyan, bg = colors.menu_grey }, + }, + replace = { + a = { fg = colors.black, bg = colors.green, gui = 'bold' }, + b = { fg = colors.green, bg = colors.menu_grey }, + }, + inactive = { + a = { fg = colors.black, bg = colors.menu_grey, gui = 'bold' }, + b = { fg = colors.black, bg = colors.menu_grey }, + c = { fg = colors.black, bg = colors.menu_grey }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/papercolor_dark.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/papercolor_dark.lua new file mode 100644 index 00000000..6fba11bc --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/papercolor_dark.lua @@ -0,0 +1,42 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: TKNGUE(lightline) +-- stylua: ignore +local colors = { + red = '#df0000', + green = '#008700', + blue = '#00afaf', + pink = '#afdf00', + olive = '#dfaf5f', + navy = '#df875f', + orange = '#d75f00', + purple = '#8959a8', + aqua = '#3e999f', + foreground = '#d0d0d0', + background = '#444444', + window = '#efefef', + status = '#c6c6c6', + error = '#5f0000', + statusline_active_fg = '#1c1c1c', + statusline_active_bg = '#5f8787', + statusline_inactive_fg = '#c6c6c6', + statusline_inactive_bg = '#444444', + visual_fg = '#000000', + visual_bg = '#8787af', +} + +return { + normal = { + a = { fg = colors.foreground, bg = colors.background, gui = 'bold' }, + b = { fg = colors.statusline_active_fg, bg = colors.status }, + c = { fg = colors.statusline_active_fg, bg = colors.statusline_active_bg }, + }, + insert = { a = { fg = colors.background, bg = colors.blue, gui = 'bold' } }, + visual = { a = { fg = colors.visual_fg, bg = colors.visual_bg, gui = 'bold' } }, + replace = { a = { fg = colors.background, bg = colors.pink, gui = 'bold' } }, + inactive = { + a = { fg = colors.foreground, bg = colors.background, gui = 'bold' }, + b = { fg = colors.foreground, bg = colors.background }, + c = { fg = colors.foreground, bg = colors.background }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/papercolor_light.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/papercolor_light.lua new file mode 100644 index 00000000..336bb887 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/papercolor_light.lua @@ -0,0 +1,40 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: TKNGUE(lightline) +-- stylua: ignore +local colors = { + red = '#df0000', + green = '#008700', + blue = '#4271ae', + pink = '#d7005f', + olive = '#718c00', + navy = '#005f87', + orange = '#d75f00', + purple = '#8959a8', + aqua = '#3e999f', + foreground = '#4d4d4c', + background = '#F5F5F5', + window = '#efefef', + status = '#3e999f', + error = '#ffafdf', + statusline_active_fg = '#efefef', + statusline_active_bg = '#005f87', + statusline_inactive_fg = '#4d4d4c', + statusline_inactive_bg = '#dadada', +} + +return { + normal = { + a = { fg = colors.foreground, bg = colors.background, gui = 'bold' }, + b = { fg = colors.statusline_active_fg, bg = colors.status }, + c = { fg = colors.statusline_active_fg, bg = colors.statusline_active_bg }, + }, + insert = { a = { fg = colors.blue, bg = colors.background, gui = 'bold' } }, + visual = { a = { fg = colors.background, bg = colors.orange, gui = 'bold' } }, + replace = { a = { fg = colors.background, bg = colors.pink, gui = 'bold' } }, + inactive = { + a = { fg = colors.foreground, bg = colors.background, gui = 'bold' }, + b = { fg = colors.foreground, bg = colors.background }, + c = { fg = colors.foreground, bg = colors.background }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/powerline.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/powerline.lua new file mode 100644 index 00000000..5bb354aa --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/powerline.lua @@ -0,0 +1,44 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local Colors = { + white = '#ffffff', + darkestgreen = '#005f00', + brightgreen = '#afdf00', + darkestcyan = '#005f5f', + mediumcyan = '#87dfff', + darkestblue = '#005f87', + darkred = '#870000', + brightred = '#df0000', + brightorange = '#ff8700', + gray1 = '#262626', + gray2 = '#303030', + gray4 = '#585858', + gray5 = '#606060', + gray7 = '#9e9e9e', + gray10 = '#f0f0f0', +} + +local M = { + normal = { + a = { fg = Colors.darkestgreen, bg = Colors.brightgreen, gui = 'bold' }, + b = { fg = Colors.gray10, bg = Colors.gray5 }, + c = { fg = Colors.gray7, bg = Colors.gray2 }, + }, + insert = { + a = { fg = Colors.darkestcyan, bg = Colors.white, gui = 'bold' }, + b = { fg = Colors.darkestcyan, bg = Colors.mediumcyan }, + c = { fg = Colors.mediumcyan, bg = Colors.darkestblue }, + }, + visual = { a = { fg = Colors.darkred, bg = Colors.brightorange, gui = 'bold' } }, + replace = { a = { fg = Colors.white, bg = Colors.brightred, gui = 'bold' } }, + inactive = { + a = { fg = Colors.gray1, bg = Colors.gray5, gui = 'bold' }, + b = { fg = Colors.gray1, bg = Colors.gray5 }, + c = { bg = Colors.gray1, fg = Colors.gray5 }, + }, +} + +M.terminal = M.insert + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/powerline_dark.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/powerline_dark.lua new file mode 100644 index 00000000..e992212f --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/powerline_dark.lua @@ -0,0 +1,60 @@ +-- Copyright (c) 2021 Ashish Panigrahi +-- MIT license, see LICENSE for more details. +-- stylua: ignore +local colors = { + black = '#202020', + neon = '#DFFF00', + white = '#FFFFFF', + green = '#00D700', + purple = '#5F005F', + blue = '#00DFFF', + darkblue = '#00005F', + navyblue = '#000080', + brightgreen = '#9CFFD3', + gray = '#444444', + darkgray = '#3c3836', + lightgray = '#504945', + inactivegray = '#7c6f64', + orange = '#FFAF00', + red = '#5F0000', + brightorange = '#C08A20', + brightred = '#AF0000', + cyan = '#00DFFF', +} + +local M = { + normal = { + a = { bg = colors.neon, fg = colors.black, gui = 'bold' }, + b = { bg = colors.gray, fg = colors.white }, + c = { bg = colors.black, fg = colors.brightgreen }, + }, + insert = { + a = { bg = colors.blue, fg = colors.darkblue, gui = 'bold' }, + b = { bg = colors.navyblue, fg = colors.white }, + c = { bg = colors.purple, fg = colors.white }, + }, + visual = { + a = { bg = colors.orange, fg = colors.black, gui = 'bold' }, + b = { bg = colors.darkgray, fg = colors.white }, + c = { bg = colors.red, fg = colors.white }, + }, + replace = { + a = { bg = colors.brightred, fg = colors.white, gui = 'bold' }, + b = { bg = colors.cyan, fg = colors.darkblue }, + c = { bg = colors.navyblue, fg = colors.white }, + }, + command = { + a = { bg = colors.green, fg = colors.black, gui = 'bold' }, + b = { bg = colors.darkgray, fg = colors.white }, + c = { bg = colors.black, fg = colors.brightgreen }, + }, + inactive = { + a = { bg = colors.darkgray, fg = colors.gray, gui = 'bold' }, + b = { bg = colors.darkgray, fg = colors.gray }, + c = { bg = colors.darkgray, fg = colors.gray }, + }, +} + +M.terminal = M.insert + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/pywal.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/pywal.lua new file mode 100644 index 00000000..fad51714 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/pywal.lua @@ -0,0 +1,50 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +local lualine_require = require('lualine_require') +local modules = lualine_require.lazy_require { + utils_notices = 'lualine.utils.notices', +} +local sep = package.config:sub(1, 1) +local wal_colors_path = table.concat({ os.getenv('HOME'), '.cache', 'wal', 'colors.sh' }, sep) +local wal_colors_file = io.open(wal_colors_path, 'r') + +if wal_colors_file == nil then + modules.utils_notices.add_notice('lualine.nvim: ' .. wal_colors_path .. ' not found') + error('') +end + +local ok, wal_colors_text = pcall(wal_colors_file.read, wal_colors_file, '*a') +wal_colors_file:close() + +if not ok then + modules.utils_notices.add_notice('lualine.nvim: ' .. wal_colors_path .. ' could not be read: ' .. wal_colors_text) + error('') +end + +local colors = {} + +for line in vim.gsplit(wal_colors_text, '\n') do + if line:match("^[a-z0-9]+='#[a-fA-F0-9]+'$") ~= nil then + local i = line:find('=') + local key = line:sub(0, i - 1) + local value = line:sub(i + 2, #line - 1) + colors[key] = value + end +end + +return { + normal = { + a = { fg = colors.background, bg = colors.color4, gui = 'bold' }, + b = { fg = colors.foreground, bg = colors.color8 }, + c = { fg = colors.foreground, bg = colors.background }, + }, + insert = { a = { fg = colors.background, bg = colors.color2, gui = 'bold' } }, + visual = { a = { fg = colors.background, bg = colors.color3, gui = 'bold' } }, + replace = { a = { fg = colors.background, bg = colors.color1, gui = 'bold' } }, + inactive = { + a = { fg = colors.foreground, bg = colors.background, gui = 'bold' }, + b = { fg = colors.foreground, bg = colors.background }, + c = { fg = colors.foreground, bg = colors.background }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/seoul256.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/seoul256.lua new file mode 100644 index 00000000..3748e365 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/seoul256.lua @@ -0,0 +1,41 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Generated by lightline to lualine theme converter +-- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 +-- stylua: ignore +local colors = { + color5 = '#d7afaf', + color6 = '#666656', + color7 = '#808070', + color10 = '#87af87', + color13 = '#df5f87', + color14 = '#87afaf', + color0 = '#e8e8d3', + color1 = '#4e4e43', + color4 = '#30302c', +} + +return { + visual = { + b = { fg = colors.color0, bg = colors.color1 }, + a = { fg = colors.color4, bg = colors.color5, gui = 'bold' }, + }, + inactive = { + b = { fg = colors.color6, bg = colors.color4 }, + c = { fg = colors.color6, bg = colors.color4 }, + a = { fg = colors.color7, bg = colors.color4, gui = 'bold' }, + }, + insert = { + b = { fg = colors.color0, bg = colors.color1 }, + a = { fg = colors.color4, bg = colors.color10, gui = 'bold' }, + }, + replace = { + b = { fg = colors.color0, bg = colors.color1 }, + a = { fg = colors.color4, bg = colors.color13, gui = 'bold' }, + }, + normal = { + b = { fg = colors.color0, bg = colors.color1 }, + c = { fg = colors.color7, bg = colors.color4 }, + a = { fg = colors.color4, bg = colors.color14, gui = 'bold' }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/solarized.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/solarized.lua new file mode 100644 index 00000000..2e72cf94 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/solarized.lua @@ -0,0 +1,7 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: itchyny(lightline) +-- License: MIT License +local background = vim.opt.background:get() + +return require('lualine.themes.solarized_' .. background) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/solarized_dark.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/solarized_dark.lua new file mode 100644 index 00000000..888790af --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/solarized_dark.lua @@ -0,0 +1,38 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: itchyny(lightline) +-- stylua: ignore +local colors = { + base03 = '#002b36', + base02 = '#073642', + base01 = '#586e75', + base00 = '#657b83', + base0 = '#839496', + base1 = '#93a1a1', + base2 = '#eee8d5', + base3 = '#fdf6e3', + yellow = '#b58900', + orange = '#cb4b16', + red = '#dc322f', + magenta = '#d33682', + violet = '#6c71c4', + blue = '#268bd2', + cyan = '#2aa198', + green = '#859900', +} + +return { + normal = { + a = { fg = colors.base03, bg = colors.blue, gui = 'bold' }, + b = { fg = colors.base03, bg = colors.base1 }, + c = { fg = colors.base1, bg = colors.base02 }, + }, + insert = { a = { fg = colors.base03, bg = colors.green, gui = 'bold' } }, + visual = { a = { fg = colors.base03, bg = colors.magenta, gui = 'bold' } }, + replace = { a = { fg = colors.base03, bg = colors.red, gui = 'bold' } }, + inactive = { + a = { fg = colors.base0, bg = colors.base02, gui = 'bold' }, + b = { fg = colors.base03, bg = colors.base00 }, + c = { fg = colors.base01, bg = colors.base02 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/solarized_light.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/solarized_light.lua new file mode 100644 index 00000000..e686891a --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/solarized_light.lua @@ -0,0 +1,38 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: itchyny(lightline) +-- stylua: ignore +local colors = { + base3 = '#002b36', + base2 = '#073642', + base1 = '#586e75', + base0 = '#657b83', + base00 = '#839496', + base01 = '#93a1a1', + base02 = '#eee8d5', + base03 = '#fdf6e3', + yellow = '#b58900', + orange = '#cb4b16', + red = '#dc322f', + magenta = '#d33682', + violet = '#6c71c4', + blue = '#268bd2', + cyan = '#2aa198', + green = '#859900', +} + +return { + normal = { + a = { fg = colors.base03, bg = colors.blue, gui = 'bold' }, + b = { fg = colors.base03, bg = colors.base1 }, + c = { fg = colors.base1, bg = colors.base02 }, + }, + insert = { a = { fg = colors.base03, bg = colors.green, gui = 'bold' } }, + visual = { a = { fg = colors.base03, bg = colors.magenta, gui = 'bold' } }, + replace = { a = { fg = colors.base03, bg = colors.red, gui = 'bold' } }, + inactive = { + a = { fg = colors.base0, bg = colors.base02, gui = 'bold' }, + b = { fg = colors.base03, bg = colors.base00 }, + c = { fg = colors.base01, bg = colors.base02 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/wombat.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/wombat.lua new file mode 100644 index 00000000..36451b84 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/themes/wombat.lua @@ -0,0 +1,38 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +-- Credit: itchyny(lightline) +-- stylua: ignore +local colors = { + base03 = '#242424', + base023 = '#353535', + base02 = '#444444', + base01 = '#585858', + base00 = '#666666', + base0 = '#808080', + base1 = '#969696', + base2 = '#a8a8a8', + base3 = '#d0d0d0', + yellow = '#cae682', + orange = '#e5786d', + red = '#e5786d', + magenta = '#f2c68a', + blue = '#8ac6f2', + cyan = '#8ac6f2', + green = '#95e454', +} + +return { + normal = { + a = { fg = colors.base02, bg = colors.blue, gui = 'bold' }, + b = { fg = colors.base02, bg = colors.base0 }, + c = { fg = colors.base2, bg = colors.base02 }, + }, + insert = { a = { fg = colors.base02, bg = colors.green, gui = 'bold' } }, + visual = { a = { fg = colors.base02, bg = colors.magenta, gui = 'bold' } }, + replace = { a = { fg = colors.base023, bg = colors.red, gui = 'bold' } }, + inactive = { + a = { fg = colors.base1, bg = colors.base02, gui = 'bold' }, + b = { fg = colors.base023, bg = colors.base01 }, + c = { fg = colors.base1, bg = colors.base023 }, + }, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/class.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/class.lua new file mode 100644 index 00000000..3531ad46 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/class.lua @@ -0,0 +1,43 @@ +-- Adapted from https://github.com/rxi/classic/blob/master/classic.lua +local Object = {} + +Object.__index = Object + +-- luacheck: push no unused args +---Initializer +function Object:init(...) end +-- luacheck: pop + +---Extend base class to create a child class +function Object:extend() + local cls = {} + for k, v in pairs(self) do + if k:find('__') == 1 then + cls[k] = v + end + end + cls.__index = cls + cls.super = self + setmetatable(cls, self) + return cls +end + +-- luacheck: push no unused args +function Object:__tostring() + return 'Object' +end +-- luacheck: pop + +---Creates a new object +function Object:new(...) + local obj = setmetatable({}, self) + obj:init(...) + return obj +end + +---Creates a new object +function Object:__call(...) + return self:new(...) +end + +return Object diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/color_utils.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/color_utils.lua new file mode 100644 index 00000000..90b5f65d --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/color_utils.lua @@ -0,0 +1,331 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local M = {} + +-- stylua: ignore start +-- color conversion +local color_table = { + -- lookup table for cterm colors + -- format {'color_code', {r,g,b}} + + -- Primary 3-bit (8 colors). Unique representation! + {'00', { 0, 0, 0 }}, + {'01', { 128, 0, 0 }}, + {'02', { 0, 128, 0 }}, + {'03', { 128, 128, 0 }}, + {'04', { 0, 0, 128 }}, + {'05', { 128, 0, 128 }}, + {'06', { 0, 128, 128 }}, + {'07', { 192, 192, 192 }}, + + -- equivalent "bright" versions of original 8 colors. + {'08', { 128, 128, 128 }}, + {'09', { 255, 0, 0 }}, + {'10', { 0, 255, 0 }}, + {'11', { 255, 255, 0 }}, + {'12', { 0, 0, 255 }}, + {'13', { 255, 0, 255 }}, + {'14', { 0, 255, 255 }}, + {'15', { 255, 255, 255 }}, + + -- Strictly ascending. + {'16', { 0, 0, 0 }}, + {'17', { 0, 0, 95 }}, + {'18', { 0, 0, 135 }}, + {'19', { 0, 0, 175 }}, + {'20', { 0, 0, 215 }}, + {'21', { 0, 0, 255 }}, + {'22', { 0, 95, 0 }}, + {'23', { 0, 95, 95 }}, + {'24', { 0, 95, 135 }}, + {'25', { 0, 95, 175 }}, + {'26', { 0, 95, 215 }}, + {'27', { 0, 95, 255 }}, + {'28', { 0, 135, 0 }}, + {'29', { 0, 135, 95 }}, + {'30', { 0, 135, 135 }}, + {'31', { 0, 135, 175 }}, + {'32', { 0, 135, 215 }}, + {'33', { 0, 135, 255 }}, + {'34', { 0, 175, 0 }}, + {'35', { 0, 175, 95 }}, + {'36', { 0, 175, 135 }}, + {'37', { 0, 175, 175 }}, + {'38', { 0, 175, 215 }}, + {'39', { 0, 175, 255 }}, + {'40', { 0, 215, 0 }}, + {'41', { 0, 215, 95 }}, + {'42', { 0, 215, 135 }}, + {'43', { 0, 215, 175 }}, + {'44', { 0, 215, 215 }}, + {'45', { 0, 215, 255 }}, + {'46', { 0, 255, 0 }}, + {'47', { 0, 255, 95 }}, + {'48', { 0, 255, 135 }}, + {'49', { 0, 255, 175 }}, + {'50', { 0, 255, 215 }}, + {'51', { 0, 255, 255 }}, + {'52', { 95, 0, 0 }}, + {'53', { 95, 0, 95 }}, + {'54', { 95, 0, 135 }}, + {'55', { 95, 0, 175 }}, + {'56', { 95, 0, 215 }}, + {'57', { 95, 0, 255 }}, + {'58', { 95, 95, 0 }}, + {'59', { 95, 95, 95 }}, + {'60', { 95, 95, 135 }}, + {'61', { 95, 95, 175 }}, + {'62', { 95, 95, 215 }}, + {'63', { 95, 95, 255 }}, + {'64', { 95, 135, 0 }}, + {'65', { 95, 135, 95 }}, + {'66', { 95, 135, 135 }}, + {'67', { 95, 135, 175 }}, + {'68', { 95, 135, 215 }}, + {'69', { 95, 135, 255 }}, + {'70', { 95, 175, 0 }}, + {'71', { 95, 175, 95 }}, + {'72', { 95, 175, 135 }}, + {'73', { 95, 175, 175 }}, + {'74', { 95, 175, 215 }}, + {'75', { 95, 175, 255 }}, + {'76', { 95, 215, 0 }}, + {'77', { 95, 215, 95 }}, + {'78', { 95, 215, 135 }}, + {'79', { 95, 215, 175 }}, + {'80', { 95, 215, 215 }}, + {'81', { 95, 215, 255 }}, + {'82', { 95, 255, 0 }}, + {'83', { 95, 255, 95 }}, + {'84', { 95, 255, 135 }}, + {'85', { 95, 255, 175 }}, + {'86', { 95, 255, 215 }}, + {'87', { 95, 255, 255 }}, + {'88', { 135, 0, 0 }}, + {'89', { 135, 0, 95 }}, + {'90', { 135, 0, 135 }}, + {'91', { 135, 0, 175 }}, + {'92', { 135, 0, 215 }}, + {'93', { 135, 0, 255 }}, + {'94', { 135, 95, 0 }}, + {'95', { 135, 95, 95 }}, + {'96', { 135, 95, 135 }}, + {'97', { 135, 95, 175 }}, + {'98', { 135, 95, 215 }}, + {'99', { 135, 95, 255 }}, + {'100', { 135, 135, 0 }}, + {'101', { 135, 135, 95 }}, + {'102', { 135, 135, 135 }}, + {'103', { 135, 135, 175 }}, + {'104', { 135, 135, 215 }}, + {'105', { 135, 135, 255 }}, + {'106', { 135, 175, 0 }}, + {'107', { 135, 175, 95 }}, + {'108', { 135, 175, 135 }}, + {'109', { 135, 175, 175 }}, + {'110', { 135, 175, 215 }}, + {'111', { 135, 175, 255 }}, + {'112', { 135, 215, 0 }}, + {'113', { 135, 215, 95 }}, + {'114', { 135, 215, 135 }}, + {'115', { 135, 215, 175 }}, + {'116', { 135, 215, 215 }}, + {'117', { 135, 215, 255 }}, + {'118', { 135, 255, 0 }}, + {'119', { 135, 255, 95 }}, + {'120', { 135, 255, 135 }}, + {'121', { 135, 255, 175 }}, + {'122', { 135, 255, 215 }}, + {'123', { 135, 255, 255 }}, + {'124', { 175, 0, 0 }}, + {'125', { 175, 0, 95 }}, + {'126', { 175, 0, 135 }}, + {'127', { 175, 0, 175 }}, + {'128', { 175, 0, 215 }}, + {'129', { 175, 0, 255 }}, + {'130', { 175, 95, 0 }}, + {'131', { 175, 95, 95 }}, + {'132', { 175, 95, 135 }}, + {'133', { 175, 95, 175 }}, + {'134', { 175, 95, 215 }}, + {'135', { 175, 95, 255 }}, + {'136', { 175, 135, 0 }}, + {'137', { 175, 135, 95 }}, + {'138', { 175, 135, 135 }}, + {'139', { 175, 135, 175 }}, + {'140', { 175, 135, 215 }}, + {'141', { 175, 135, 255 }}, + {'142', { 175, 175, 0 }}, + {'143', { 175, 175, 95 }}, + {'144', { 175, 175, 135 }}, + {'145', { 175, 175, 175 }}, + {'146', { 175, 175, 215 }}, + {'147', { 175, 175, 255 }}, + {'148', { 175, 215, 0 }}, + {'149', { 175, 215, 95 }}, + {'150', { 175, 215, 135 }}, + {'151', { 175, 215, 175 }}, + {'152', { 175, 215, 215 }}, + {'153', { 175, 215, 255 }}, + {'154', { 175, 255, 0 }}, + {'155', { 175, 255, 95 }}, + {'156', { 175, 255, 135 }}, + {'157', { 175, 255, 175 }}, + {'158', { 175, 255, 215 }}, + {'159', { 175, 255, 255 }}, + {'160', { 215, 0, 0 }}, + {'161', { 215, 0, 95 }}, + {'162', { 215, 0, 135 }}, + {'163', { 215, 0, 175 }}, + {'164', { 215, 0, 215 }}, + {'165', { 215, 0, 255 }}, + {'166', { 215, 95, 0 }}, + {'167', { 215, 95, 95 }}, + {'168', { 215, 95, 135 }}, + {'169', { 215, 95, 175 }}, + {'170', { 215, 95, 215 }}, + {'171', { 215, 95, 255 }}, + {'172', { 215, 135, 0 }}, + {'173', { 215, 135, 95 }}, + {'174', { 215, 135, 135 }}, + {'175', { 215, 135, 175 }}, + {'176', { 215, 135, 215 }}, + {'177', { 215, 135, 255 }}, + {'178', { 215, 175, 0 }}, + {'179', { 215, 175, 95 }}, + {'180', { 215, 175, 135 }}, + {'181', { 215, 175, 175 }}, + {'182', { 215, 175, 215 }}, + {'183', { 215, 175, 255 }}, + {'184', { 215, 215, 0 }}, + {'185', { 215, 215, 95 }}, + {'186', { 215, 215, 135 }}, + {'187', { 215, 215, 175 }}, + {'188', { 215, 215, 215 }}, + {'189', { 215, 215, 255 }}, + {'190', { 215, 255, 0 }}, + {'191', { 215, 255, 95 }}, + {'192', { 215, 255, 135 }}, + {'193', { 215, 255, 175 }}, + {'194', { 215, 255, 215 }}, + {'195', { 215, 255, 255 }}, + {'196', { 255, 0, 0 }}, + {'197', { 255, 0, 95 }}, + {'198', { 255, 0, 135 }}, + {'199', { 255, 0, 175 }}, + {'200', { 255, 0, 215 }}, + {'201', { 255, 0, 255 }}, + {'202', { 255, 95, 0 }}, + {'203', { 255, 95, 95 }}, + {'204', { 255, 95, 135 }}, + {'205', { 255, 95, 175 }}, + {'206', { 255, 95, 215 }}, + {'207', { 255, 95, 255 }}, + {'208', { 255, 135, 0 }}, + {'209', { 255, 135, 95 }}, + {'210', { 255, 135, 135 }}, + {'211', { 255, 135, 175 }}, + {'212', { 255, 135, 215 }}, + {'213', { 255, 135, 255 }}, + {'214', { 255, 175, 0 }}, + {'215', { 255, 175, 95 }}, + {'216', { 255, 175, 135 }}, + {'217', { 255, 175, 175 }}, + {'218', { 255, 175, 215 }}, + {'219', { 255, 175, 255 }}, + {'220', { 255, 215, 0 }}, + {'221', { 255, 215, 95 }}, + {'222', { 255, 215, 135 }}, + {'223', { 255, 215, 175 }}, + {'224', { 255, 215, 215 }}, + {'225', { 255, 215, 255 }}, + {'226', { 255, 255, 0 }}, + {'227', { 255, 255, 95 }}, + {'228', { 255, 255, 135 }}, + {'229', { 255, 255, 175 }}, + {'230', { 255, 255, 215 }}, + {'231', { 255, 255, 255 }}, + + -- Gray-scale range. + {'232', { 8, 8, 8 }}, + {'233', { 18, 18, 18 }}, + {'234', { 28, 28, 28 }}, + {'235', { 38, 38, 38 }}, + {'236', { 48, 48, 48 }}, + {'237', { 58, 58, 58 }}, + {'238', { 68, 68, 68 }}, + {'239', { 78, 78, 78 }}, + {'240', { 88, 88, 88 }}, + {'241', { 98, 98, 98 }}, + {'242', { 108, 108, 108 }}, + {'243', { 118, 118, 118 }}, + {'244', { 128, 128, 128 }}, + {'245', { 138, 138, 138 }}, + {'246', { 148, 148, 148 }}, + {'247', { 158, 158, 158 }}, + {'248', { 168, 168, 168 }}, + {'249', { 178, 178, 178 }}, + {'250', { 188, 188, 188 }}, + {'251', { 198, 198, 198 }}, + {'252', { 208, 208, 208 }}, + {'253', { 218, 218, 218 }}, + {'254', { 228, 228, 228 }}, + {'255', { 238, 238, 238 }}, +} +-- stylua: ignore end + +---converts #rrggbb formatted color to cterm ('0'-'255') color +---@param hex_color string +---@return string +function M.rgb2cterm(hex_color) + if hex_color == 'None' then + return 'None' + end + local function get_color_distance(color1, color2) + -- returns how much color2 deviates from color1 + local dr = math.abs(color1[1] - color2[1]) / (color1[1] + 1) * 100 + local dg = math.abs(color1[2] - color2[2]) / (color1[2] + 1) * 100 + local db = math.abs(color1[3] - color2[3]) / (color1[3] + 1) * 100 + return (dr + dg + db) + end + + local r = tonumber(hex_color:sub(2, 3), 16) + local g = tonumber(hex_color:sub(4, 5), 16) + local b = tonumber(hex_color:sub(6, 7), 16) + + -- check which cterm color is closest to hex colors in terms of rgb values + local closest_cterm_color = 0 + local min_distance = 10000 + for _, color in ipairs(color_table) do + local current_distance = get_color_distance(color[2], { r, g, b }) + if current_distance < min_distance then + min_distance = current_distance + closest_cterm_color = color[1] + end + end + return closest_cterm_color +end + +---converts color name (only ones supported by neovim) formatted colors to #rrggbb +---@param name string like red,green,grey +---@return string +function M.color_name2rgb(name) + local color_val = vim.api.nvim_get_color_by_name(name) + if color_val == -1 then + return '#' .. name -- Assuming it's 'rrggbb' without # not rad instead of red + end + return string.format('#%06x', color_val) +end + +---converts cterm(0-255) to #rrggbb +---@param color number +---@return string +function M.cterm2rgb(color) + local color_data = color_table[color + 1] + if color_data ~= nil then + color_data = color_data[2] + return string.format('#%02x%02x%02x', color_data[1], color_data[2], color_data[3]) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/fn_store.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/fn_store.lua new file mode 100644 index 00000000..a3b59ae9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/fn_store.lua @@ -0,0 +1,38 @@ +local M = {} +local fns = {} + +---Store fn with id in fns so it can be later called +---@param id integer component id (for now we are only doing one fn per component) +---@param fn function the actual function to store. +---@return number same id that was provided. +function M.register_fn(id, fn) + vim.validate { + id = { id, 'n' }, + fn = { fn, 'f' }, + } + fns[id] = fn + return id +end + +---Get the function with id +---@param id number id of the fn to retrieve +---@return function +function M.get_fn(id) + vim.validate { id = { id, 'n' } } + return fns[id] or function() end +end + +---Call the function of id with args +---@param id number +---@param ... any +---@return any +function M.call_fn(id, ...) + return M.get_fn(id)(...) +end + +---Clear the fns table +function M.clear_fns() + fns = {} +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/job.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/job.lua new file mode 100644 index 00000000..eadcbfed --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/job.lua @@ -0,0 +1,54 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +--- wrapper around job api +--- creates a job handler when called +local Job = setmetatable({ + --- start the job + start = function(self) + self.job_id = vim.fn.jobstart(self.args.cmd, self.args) + return self.job_id > 0 + end, + --- stop the job. Also immediately disables io from the job. + stop = function(self) + if self.killed then + return + end + if self.job_id and self.job_id > 0 then + vim.fn.jobstop(self.job_id) + end + self.job_id = 0 + self.killed = true + end, + -- Wraps callbacks so they are only called when job is alive + -- This avoids race conditions + wrap_cb_alive = function(self, name) + local original_cb = self.args[name] + if original_cb then + self.args[name] = function(...) + if not self.killed then + return original_cb(...) + end + end + end + end, +}, { + ---create new job handler + ---@param self table base job table + ---@param args table same args as jobstart except cmd is also passed in part of it + ---@return table new job handler + __call = function(self, args) + args = vim.deepcopy(args or {}) + if type(args.cmd) == 'string' then + args.cmd = vim.split(args.cmd, ' ') + end + self.__index = self + local job = setmetatable({ args = args }, self) + job:wrap_cb_alive('on_stdout') + job:wrap_cb_alive('on_stderr') + job:wrap_cb_alive('on_stdin') + return job + end, +}) + +return Job diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/loader.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/loader.lua new file mode 100644 index 00000000..e0c0385e --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/loader.lua @@ -0,0 +1,257 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. + +local lualine_require = require('lualine_require') +local require = lualine_require.require +local modules = lualine_require.lazy_require { + utils = 'lualine.utils.utils', + notice = 'lualine.utils.notices', + fn_store = 'lualine.utils.fn_store', +} +local is_valid_filename = lualine_require.is_valid_filename +local sep = lualine_require.sep + +--- function that loads specific type of component +local component_types = { + -- loads custom component + custom = function(component) + return component[1](component) + end, + --- loads lua functions as component + lua_fun = function(component) + return require('lualine.components.special.function_component')(component) + end, + --- loads lua modules as components (ones in /lua/lualine/components/) + mod = function(component) + local ok, loaded_component = pcall(require, 'lualine.components.' .. component[1]) + if ok then + component.component_name = component[1] + if type(loaded_component) == 'table' then + loaded_component = loaded_component(component) + elseif type(loaded_component) == 'function' then + component[1] = loaded_component + loaded_component = require('lualine.components.special.function_component')(component) + end + return loaded_component + end + end, + --- loads builtin statusline patterns as component + stl = function(component) + local stl_expr = component[1] -- Vim's %p %l statusline elements + component[1] = function() + return stl_expr + end + return require('lualine.components.special.function_component')(component) + end, + --- loads variables & options (g:,go:,b:,bo:...) as components + var = function(component) + return require('lualine.components.special.vim_var_component')(component) + end, + --- loads vim functions and lua expressions as components + ['_'] = function(component) + return require('lualine.components.special.eval_func_component')(component) + end, +} + +---load a component from component config +---@param component table component + component options +---@return table the loaded & initialized component +local function component_loader(component) + if type(component[1]) == 'function' then + return component_types.lua_fun(component) + elseif type(component[1]) == 'string' then + -- load the component + if component.type ~= nil then + if component_types[component.type] and component.type ~= 'lua_fun' then + return component_types[component.type](component) + elseif component.type == 'vim_fun' or component.type == 'lua_expr' then + return component_types['_'](component) + else + modules.notice.add_notice(string.format( + [[ +### component.type + +component type '%s' isn't recognised. Check if spelling is correct.]], + component.type + )) + end + end + local loaded_component = component_types.mod(component) + if loaded_component then + return loaded_component + elseif string.char(component[1]:byte(1)) == '%' then + return component_types.stl(component) + elseif component[1]:find('[gvtwb]?o?:') == 1 then + return component_types.var(component) + else + return component_types['_'](component) + end + elseif type(component[1]) == 'table' then + return component_types.custom(component) + end +end + +--- Shows notice about invalid types passed as component +--- @param index number the index of component in section table +--- @param component table containing component elements +--- return bool whether check passed or not +local function is_valid_component_type(index, component) + if type(component_types) == 'table' and type(index) == 'number' then + return true + end + modules.notice.add_notice(string.format( + [[ +### Unrecognized component +Only functions, strings and tables can be used as component. +You seem to have a `%s` as component indexed as `%s`. +Something like: +```lua + %s = %s, +``` + +This commonly occurs when you forget to pass table with option for component. +When a component has option that component needs to be a table which holds +the component as first element and the options as key value pairs. +For example: +```lua +lualine_c = { + {'diagnostics', + sources = {'nvim'}, + } +} +``` +Notice the inner extra {} surrounding the component and it's options. +Make sure your config follows this. +]], + type(component), + index, + index, + vim.inspect(component) + )) + return false +end + +---loads all the section from a config +---@param sections table list of sections +---@param options table global options table +local function load_sections(sections, options) + for section_name, section in pairs(sections) do + for index, component in pairs(section) do + if (type(component) == 'string' or type(component) == 'function') or modules.utils.is_component(component) then + component = { component } + end + if is_valid_component_type(index, component) then + component.self = {} + component.self.section = section_name:match('lualine_(.*)') + -- apply default args + component = vim.tbl_extend('keep', component, options) + section[index] = component_loader(component) + end + end + end +end + +---loads all the configs (active, inactive, tabline) +---@param config table user config +local function load_components(config) + local sec_names = { 'sections', 'inactive_sections', 'tabline', 'winbar', 'inactive_winbar' } + for _, section in ipairs(sec_names) do + load_sections(config[section], config.options) + end +end + +---loads all the extensions +---@param config table user config +local function load_extensions(config) + local loaded_extensions = {} + local sec_names = { 'sections', 'inactive_sections', 'winbar', 'inactive_winbar' } + for _, extension in pairs(config.extensions) do + if type(extension) == 'string' then + local ok + ok, extension = pcall(require, 'lualine.extensions.' .. extension) + if not ok then + modules.notice.add_notice(string.format( + [[ +### Extensions +Extension named `%s` was not found . Check if spelling is correct. +]], + extension + )) + end + end + if type(extension) == 'table' then + local local_extension = modules.utils.deepcopy(extension) + for _, section in ipairs(sec_names) do + if local_extension[section] then + load_sections(local_extension[section], config.options) + end + end + if type(local_extension.init) == 'function' then + local_extension.init() + end + table.insert(loaded_extensions, local_extension) + end + end + config.extensions = loaded_extensions +end + +---loads sections and extensions or entire user config +---@param config table user config +local function load_all(config) + require('lualine.component')._reset_components() + modules.fn_store.clear_fns() + require('lualine.utils.nvim_opts').reset_cache() + load_components(config) + load_extensions(config) +end + +---loads a theme from lua module +---prioritizes external themes (from user config or other plugins) over the bundled ones +---@param theme_name string +---@return table theme definition from module +local function load_theme(theme_name) + assert(is_valid_filename(theme_name), 'Invalid filename') + local retval + local path = table.concat { 'lua/lualine/themes/', theme_name, '.lua' } + local files = vim.api.nvim_get_runtime_file(path, true) + if #files <= 0 then + path = table.concat { 'lua/lualine/themes/', theme_name, '/init.lua' } + files = vim.api.nvim_get_runtime_file(path, true) + end + local n_files = #files + if n_files == 0 then + -- No match found + error(path .. ' Not found') + elseif n_files == 1 then + -- when only one is found run that and return it's return value + retval = dofile(files[1]) + else + -- put entries from user config path in front + local user_config_path = vim.fn.stdpath('config') + table.sort(files, function(a, b) + local pattern = table.concat { user_config_path, sep } + return string.match(a, pattern) or not string.match(b, pattern) + end) + -- More then 1 found . Use the first one that isn't in lualines repo + local lualine_repo_pattern = table.concat({ 'lualine.nvim', 'lua', 'lualine' }, sep) + local file_found = false + for _, file in ipairs(files) do + if not file:find(lualine_repo_pattern) then + retval = dofile(file) + file_found = true + break + end + end + if not file_found then + -- This shouldn't happen but somehow we have multiple files but they + -- appear to be in lualines repo . Just run the first one + retval = dofile(files[1]) + end + end + return retval +end + +return { + load_all = load_all, + load_theme = load_theme, +} diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/mode.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/mode.lua new file mode 100644 index 00000000..2fdd5bfc --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/mode.lua @@ -0,0 +1,54 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local Mode = {} + +-- stylua: ignore +Mode.map = { + ['n'] = 'NORMAL', + ['no'] = 'O-PENDING', + ['nov'] = 'O-PENDING', + ['noV'] = 'O-PENDING', + ['no\22'] = 'O-PENDING', + ['niI'] = 'NORMAL', + ['niR'] = 'NORMAL', + ['niV'] = 'NORMAL', + ['nt'] = 'NORMAL', + ['ntT'] = 'NORMAL', + ['v'] = 'VISUAL', + ['vs'] = 'VISUAL', + ['V'] = 'V-LINE', + ['Vs'] = 'V-LINE', + ['\22'] = 'V-BLOCK', + ['\22s'] = 'V-BLOCK', + ['s'] = 'SELECT', + ['S'] = 'S-LINE', + ['\19'] = 'S-BLOCK', + ['i'] = 'INSERT', + ['ic'] = 'INSERT', + ['ix'] = 'INSERT', + ['R'] = 'REPLACE', + ['Rc'] = 'REPLACE', + ['Rx'] = 'REPLACE', + ['Rv'] = 'V-REPLACE', + ['Rvc'] = 'V-REPLACE', + ['Rvx'] = 'V-REPLACE', + ['c'] = 'COMMAND', + ['cv'] = 'EX', + ['ce'] = 'EX', + ['r'] = 'REPLACE', + ['rm'] = 'MORE', + ['r?'] = 'CONFIRM', + ['!'] = 'SHELL', + ['t'] = 'TERMINAL', +} + +---@return string current mode name +function Mode.get_mode() + local mode_code = vim.api.nvim_get_mode().mode + if Mode.map[mode_code] == nil then + return mode_code + end + return Mode.map[mode_code] +end + +return Mode diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/notices.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/notices.lua new file mode 100644 index 00000000..b0aa9c44 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/notices.lua @@ -0,0 +1,91 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +-- To provide notices for user +local M = {} +local notices = {} +local persistent_notices = {} + +---append new notice +---@param notice string|table table is a list of strings +function M.add_notice(notice) + if type(notice) == 'string' then + notice = vim.split(notice, '\n') + end + if notice[#notice] ~= '' then + notice[#notice + 1] = '' + end + table.insert(notices, notice) +end + +---appends persistent notice. These don't get cleared on setup +---@param notice string|table table is a list of strings +function M.add_persistent_notice(notice) + if type(notice) == 'string' then + notice = vim.split(notice, '\n') + end + if not vim.tbl_contains(persistent_notices, notice) then + table.insert(persistent_notices, notice) + end +end + +---show setup :LuaLineNotices and show notification about error when there +---are notices available +local notify_done = false +function M.notice_message_startup() + notify_done = false + vim.defer_fn(function() + if notify_done then + return + end + if #notices > 0 or #persistent_notices > 0 then + vim.cmd('command! -nargs=0 LualineNotices lua require"lualine.utils.notices".show_notices()') + vim.notify( + 'lualine: There are some issues with your config. Run :LualineNotices for details', + vim.log.levels.WARN, + {} + ) + end + notify_done = true + end, 2000) +end + +---create notice view +function M.show_notices() + vim.cmd('silent! keepalt split') + + local winid = vim.api.nvim_get_current_win() + local bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_win_set_buf(winid, bufnr) + + vim.wo[winid].winfixheight = true + vim.wo[winid].winfixwidth = true + vim.wo[winid].number = false + vim.wo[winid].foldcolumn = '0' + vim.wo[winid].relativenumber = false + vim.wo[winid].signcolumn = 'no' + vim.bo[bufnr].filetype = 'markdown' + + vim.api.nvim_buf_set_keymap(bufnr, 'n', 'q', 'bd', { noremap = true, silent = true }) + + local ok, _ = pcall(vim.api.nvim_buf_set_name, 0, 'Lualine Notices') + if not ok then + vim.notify('Lualine Notices is already open in another buffer', vim.log.levels.ERROR, {}) + vim.cmd('normal q') + return + end + local notice = vim.tbl_flatten(persistent_notices) + notice = vim.list_extend(notice, vim.tbl_flatten(notices)) + vim.fn.appendbufline(bufnr, 0, notice) + + vim.fn.deletebufline(bufnr, #notice, vim.fn.line('$')) + vim.api.nvim_win_set_cursor(winid, { 1, 0 }) + vim.bo[bufnr].modified = false + vim.bo[bufnr].modifiable = false +end + +function M.clear_notices() + notices = {} +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/nvim_opts.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/nvim_opts.lua new file mode 100644 index 00000000..634c18c9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/nvim_opts.lua @@ -0,0 +1,153 @@ +local M = {} + +-- keeps backup of options that we cahge so we can restore it. +-- format: +-- options { +-- global = <1> { +-- name = {prev, set} +-- }, +-- buffer = { +-- buf1 = <1>, +-- buf2 = <1> +-- }, +-- window = { +-- win1 = <1>, +-- win2 = <1> +-- } +-- } +---@class LualineNvimOptCacheOptStore +---@field prev any +---@field set any +---@alias LualineNvimOptCacheOpt table +---@class LualineNvimOptCache +---@field global LualineNvimOptCacheOptStore[] +---@field buffer table +---@field window table +---@type LualineNvimOptCache +local options = { global = {}, buffer = {}, window = {} } + +-- helper function for M.set +local function set_opt(name, val, getter_fn, setter_fn, cache_tbl) + -- before nvim 0.7 nvim_win_get_option... didn't return default value when + -- the option wasn't set instead threw error. + -- So we need pcall (probably just for test) + local ok, cur = pcall(getter_fn, name) + if not ok then + cur = nil + end + if cur == val then + return + end + if cache_tbl[name] == nil then + cache_tbl[name] = {} + end + if cache_tbl[name].set ~= cur then + if type(cur) ~= 'string' or not cur:find('lualine') then + cache_tbl[name].prev = cur + end + end + cache_tbl[name].set = val + setter_fn(name, val) +end + +-- set a option value +---@param name string +---@param val any +---@param opts table|nil when table can be {global=true | buffer = bufnr | window = winnr} +--- when nil it's treated as {global = true} +function M.set(name, val, opts) + if opts == nil or opts.global then + set_opt(name, val, vim.api.nvim_get_option, vim.api.nvim_set_option, options.global) + elseif opts.buffer then + if options.buffer[opts.buffer] == nil then + options.buffer[opts.buffer] = {} + end + set_opt(name, val, function(nm) + return vim.api.nvim_buf_get_option(opts.buffer, nm) + end, function(nm, vl) + vim.api.nvim_buf_set_option(opts.buffer, nm, vl) + end, options.buffer[opts.buffer]) + elseif opts.window then + if options.window[opts.window] == nil then + options.window[opts.window] = {} + end + set_opt(name, val, function(nm) + return vim.api.nvim_win_get_option(opts.window, nm) + end, function(nm, vl) + vim.api.nvim_win_set_option(opts.window, nm, vl) + end, options.window[opts.window]) + end +end + +-- resoters old value of option name +---@param name string +---@param opts table|nil same as M.set +function M.restore(name, opts) + if opts == nil or opts.global then + if options.global[name] ~= nil and options.global[name].prev ~= nil then + local restore_to = options.global[name].prev + if type(restore_to) == 'string' and restore_to:find('lualine') then + restore_to = '' + end + vim.api.nvim_set_option(name, restore_to) + end + elseif opts.buffer then + if + options.buffer[opts.buffer] ~= nil + and options.buffer[opts.buffer][name] ~= nil + and options.buffer[opts.buffer][name].prev ~= nil + then + local restore_to = options.buffer[opts.buffer][name].prev + if type(restore_to) == 'string' and restore_to:find('lualine') then + restore_to = '' + end + vim.api.nvim_buf_set_option(opts.buffer, name, restore_to) + end + elseif opts.window then + if + options.window[opts.window] ~= nil + and options.window[opts.window][name] ~= nil + and options.window[opts.window][name].prev ~= nil + then + local restore_to = options.window[opts.window][name].prev + if type(restore_to) == 'string' and restore_to:find('lualine') then + restore_to = '' + end + vim.api.nvim_win_set_option(opts.window, name, restore_to) + end + end +end + +-- returns cache for the option name +---@param name string +---@param opts table|nil same as M.set +function M.get_cache(name, opts) + if opts == nil or opts.global then + if options.global[name] ~= nil and options.global[name].prev ~= nil then + return options.global[name].prev + end + elseif opts.buffer then + if + options.buffer[opts.buffer] ~= nil + and options.buffer[opts.buffer][name] ~= nil + and options.buffer[opts.buffer][name].prev ~= nil + then + return options.buffer[opts.buffer][name].prev + end + elseif opts.window then + if + options.window[opts.window] ~= nil + and options.window[opts.window][name] ~= nil + and options.window[opts.window][name].prev ~= nil + then + return options.window[opts.window][name].prev + end + end +end + +-- resets cache for options +function M.reset_cache() + options = { global = {}, buffer = {}, window = {} } +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/section.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/section.lua new file mode 100644 index 00000000..57a309b0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/section.lua @@ -0,0 +1,124 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +local M = {} +local require = require('lualine_require').require +local utils = require('lualine.utils.utils') +local highlight = require('lualine.highlight') + +---runs draw function on components in section +---handles separator edge cases :/ +---also handles default transitional separators at section boundaries +---(why? I don't know) +---@param section table list of components +---@param section_name string used for getting proper hl +---@param is_focused boolean +---@return string formatted string for a section +--TODO Clean this up this does lots of messy stuff. +function M.draw_section(section, section_name, is_focused) + local highlight_name = highlight.format_highlight(section_name, is_focused) + + local status = {} + for _, component in pairs(section) do + -- load components into status table + if type(component) ~= 'table' or (type(component) == 'table' and not component.component_no) then + return '' -- unknown element in section. section possibly not yet loaded + end + table.insert(status, component:draw(highlight_name, is_focused)) + end + + local section_color = utils.extract_highlight_colors(string.match(highlight_name, '%%#(.*)#')) + + -- Flags required for knowing when to remove component separator + local strip_next_component = false + local last_component_found = false + local first_component_no = #section + + -- Check through components to see when component separator need to be removed + for component_no = #section, 1, -1 do + if #status[component_no] > 0 then + first_component_no = component_no + end + -- Remove component separator with highlight for last component + if not last_component_found and #status[component_no] > 0 then + last_component_found = true + status[component_no] = section[component_no]:strip_separator() + if section_name < 'c' then + if + type(section[first_component_no].options.separator) ~= 'table' + and (section[1].options.section_separators.left ~= nil and section[1].options.section_separators.left ~= '') + then + status[component_no] = + string.format('%s%%Z{%s}', status[component_no], section[1].options.section_separators.left) + end + end + end + -- Remove component separator when color option is used in next component + if strip_next_component then + strip_next_component = false + status[component_no] = section[component_no]:strip_separator() + end + -- Remove component separator when color option is used to color background + if + ( + type(section[component_no].options.color) == 'table' + and section[component_no].options.color.bg + and section[component_no].options.color.bg ~= section_color.bg + ) + or type(section[component_no].options.color) == 'string' + or ( + type(section[component_no].options.color) == 'function' + and section[component_no].color_fn_cache + and section[component_no].color_fn_cache.bg + and section[component_no].color_fn_cache.bg ~= section_color.bg + ) + then + strip_next_component = true + status[component_no] = section[component_no]:strip_separator() + end + + if section[component_no].strip_previous_separator == true then + strip_next_component = true + end + end + + local left_separator_string = '' + if + section_name > 'x' + and section[first_component_no] + and type(section[first_component_no].options.separator) ~= 'table' + and (section[1].options.section_separators.right ~= nil and section[1].options.section_separators.right ~= '') + then + left_separator_string = string.format( + '%%z{%s}', + section[first_component_no].options.ls_separator or section[1].options.section_separators.right + ) + end + + -- Remove empty strings from status + status = utils.list_shrink(status) + local status_str = table.concat(status) + + if #status_str == 0 then + return '' + end + + local needs_hl + + local find_start_trans_sep_start, find_start_trans_sep_end = status_str:find('^%%z{.-}') + if find_start_trans_sep_start then + -- the section doesn't need to be prepended with default hl when sections + -- first component has transitional sep + needs_hl = status_str:find('^%%#', find_start_trans_sep_end + 1) + else + needs_hl = status_str:find('^%%#') + end + + if needs_hl then + -- Don't prepend with old highlight when the component changes it immediately + return left_separator_string .. status_str + else + return left_separator_string .. highlight_name .. status_str + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/utils.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/utils.lua new file mode 100644 index 00000000..143d0410 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine/utils/utils.lua @@ -0,0 +1,224 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local M = {} + +-- Note for now only works for termguicolors scope can be bg or fg or any other +-- attr parameter like bold/italic/reverse +---@param color_group string hl_group name +---@param scope string bg | fg | sp +---@return table|string returns #rrggbb formatted color when scope is specified +---- or complete color table when scope isn't specified +function M.extract_highlight_colors(color_group, scope) + local color = require('lualine.highlight').get_lualine_hl(color_group) + if not color then + if vim.fn.hlexists(color_group) == 0 then + return nil + end + color = vim.api.nvim_get_hl_by_name(color_group, true) + if color.background ~= nil then + color.bg = string.format('#%06x', color.background) + color.background = nil + end + if color.foreground ~= nil then + color.fg = string.format('#%06x', color.foreground) + color.foreground = nil + end + if color.special ~= nil then + color.sp = string.format('#%06x', color.special) + color.special = nil + end + end + if scope then + return color[scope] + end + return color +end + +--- retrieves color value from highlight group name in syntax_list +--- first present highlight is returned +---@param scope string|table +---@param syntaxlist table +---@param default string +---@return string|nil +function M.extract_color_from_hllist(scope, syntaxlist, default) + scope = type(scope) == 'string' and { scope } or scope + for _, highlight_name in ipairs(syntaxlist) do + if vim.fn.hlexists(highlight_name) ~= 0 then + local color = M.extract_highlight_colors(highlight_name) + for _, sc in ipairs(scope) do + if color.reverse then + if sc == 'bg' then + sc = 'fg' + else + sc = 'bg' + end + end + if color[sc] then + return color[sc] + end + end + end + end + return default +end + +---remove empty strings from list +---@param list table +---@return table +function M.list_shrink(list) + local new_list = {} + for i = 1, #list do + if list[i] and #list[i] > 0 then + table.insert(new_list, list[i]) + end + end + return new_list +end + +--- Check if a auto command is already defined +---@param event string +---@param pattern string +---@param command_str string +---@return boolean whether autocmd is already defined +local function autocmd_is_defined(event, pattern, command_str) + return vim.api.nvim_exec(string.format('au lualine %s %s', event, pattern), true):find(command_str) ~= nil +end + +--- Define a auto command if it's not already defined +---@param event string event name +---@param pattern string event pattern +---@param cmd string command to run on event +---@param group string group name defaults to lualine +function M.define_autocmd(event, pattern, cmd, group) + if not cmd then + cmd = pattern + pattern = '*' + end + if not autocmd_is_defined(event, pattern, cmd) then + vim.cmd(string.format('autocmd %s %s %s %s', group or 'lualine', event, pattern, cmd)) + end +end + +-- Check if statusline is on focused window or not +function M.is_focused() + return tonumber(vim.g.actual_curwin) == vim.api.nvim_get_current_win() +end + +--- Check what's the character at pos +---@param str string +---@param pos number +---@return string character at position pos in string str +function M.charAt(str, pos) + return string.char(str:byte(pos)) +end + +-- deepcopy adapted from penlight +-- https://github.com/lunarmodules/Penlight/blob/0653cdb05591454a9804a7fee8c873b8f06b0b8f/lua/pl/tablex.lua#L98-L122 +local function cycle_aware_copy(t, cache) + if type(t) ~= 'table' then + return t + end + if cache[t] then + return cache[t] + end + local res = {} + cache[t] = res + local mt = getmetatable(t) + for k, v in pairs(t) do + k = cycle_aware_copy(k, cache) + v = cycle_aware_copy(v, cache) + res[k] = v + end + setmetatable(res, mt) + return res +end + +--- make a deep copy of a table, recursively copying all the keys and fields. +-- This supports cycles in tables; cycles will be reproduced in the copy. +-- This will also set the copied table's metatable to that of the original. +-- @within Copying +-- @tab t A table +-- @return new table +function M.deepcopy(t) + return cycle_aware_copy(t, {}) +end + +--- Check if comp is a lualine component +--- @param comp any +--- @return boolean +function M.is_component(comp) + if type(comp) ~= 'table' then + return false + end + local mt = getmetatable(comp) + return mt and mt.__is_lualine_component == true +end + +--- Call function with args and return it's result. +--- If error occurs during fn retry times times. +---@param fn function Function to call. +---@param args table List of arguments used for calling function. +---@param times number Number of times to retry on error. +---@return any Result of fn. +function M.retry_call(fn, args, times) + times = times or 3 + for _ = 0, times - 1 do + local result = { pcall(fn, unpack(args)) } + if result[1] == true then + return unpack(result, 2) + end + end + return fn(unpack(args)) +end + +--- Wrap a function in retry_call +---@param fn function Function to call. +---@param times number Number of times to retry on error. +---@return function retry call wrapped function +function M.retry_call_wrap(fn, times) + return function(...) + return M.retry_call(fn, { ... }, times) + end +end + +---Escape % in str so it doesn't get picked as stl item. +---@param str string +---@return string +function M.stl_escape(str) + if type(str) ~= 'string' then + return str + end + return str:gsub('%%', '%%%%') +end + +---A safe call inside a timer +---@param timer userdata +---@param augroup string|nil autocmd group to reset too on error. +---@param fn function +---@param max_err integer +---@param err_msg string +---@return function a wrapped fn that can be called inside a timer and that +---stops the timer after max_err errors in calling fn +function M.timer_call(timer, augroup, fn, max_err, err_msg) + local err_cnt, ret = 0, nil + max_err = max_err or 3 + return vim.schedule_wrap(function(...) + if err_cnt > max_err then + vim.loop.timer_stop(timer) + if augroup then + vim.cmd(string.format([[augroup %s | exe "autocmd!" | augroup END]], augroup)) + end + error(err_msg .. ':\n' .. tostring(ret)) + end + local ok + ok, ret = pcall(fn, ...) + if ok then + err_cnt = 0 + else + err_cnt = err_cnt + 1 + end + return ret + end) +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine_require.lua b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine_require.lua new file mode 100644 index 00000000..968e9af1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/lua/lualine_require.lua @@ -0,0 +1,98 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local M = {} + +M.sep = package.config:sub(1, 1) + +-- Figures out full path of lualine installation +local source = debug.getinfo(1, 'S').source +if source:sub(1, 1) == '@' then + local base_start = source:find(table.concat({ 'lualine.nvim', 'lua', 'lualine_require.lua' }, M.sep)) + if base_start then + source = source:sub(2, base_start + 12 + 1 + 3) -- #lualine.nvim = 12 , #lua = 3. + if source then + M.plugin_dir = source + end + end +end + +--- checks if name is valid +---@param name string +---@return boolean +function M.is_valid_filename(name) + local invalid_chars = '[^a-zA-Z0-9_. -]' + return name:find(invalid_chars) == nil +end + +---require module module +---@param module string mogule arraived +---@return any the required module +function M.require(module) + if package.loaded[module] then + return package.loaded[module] + end + local pattern_dir = module:gsub('%.', M.sep) + local pattern_path = pattern_dir .. '.lua' + if M.plugin_dir then + local path = M.plugin_dir .. pattern_path + assert(M.is_valid_filename(module), 'Invalid filename') + local file_stat, dir_stat + file_stat = vim.loop.fs_stat(path) + if not file_stat then + path = M.plugin_dir .. pattern_dir + dir_stat = vim.loop.fs_stat(path) + if dir_stat and dir_stat.type == 'directory' then + path = path .. M.sep .. 'init.lua' + file_stat = vim.loop.fs_stat(path) + end + end + if file_stat and file_stat.type == 'file' then + local mod_result = dofile(path) + package.loaded[module] = mod_result + return mod_result + end + end + + pattern_path = table.concat { 'lua/', module:gsub('%.', '/'), '.lua' } + local paths = vim.api.nvim_get_runtime_file(pattern_path, false) + if #paths <= 0 then + pattern_path = table.concat { 'lua/', module:gsub('%.', '/'), '/init.lua' } + paths = vim.api.nvim_get_runtime_file(pattern_path, false) + end + if #paths > 0 then + -- put entries from user config path in front + local user_config_path = vim.fn.stdpath('config') + table.sort(paths, function(a, b) + return vim.startswith(a, user_config_path) or not vim.startswith(b, user_config_path) + end) + local mod_result = dofile(paths[1]) + package.loaded[module] = mod_result + return mod_result + end + + return require(module) +end + +---requires modules when they are used +---@param modules table k-v table where v is module path and k is name that will +--- be indexed +---@return table metatable where when a key is indexed it gets required and cached +function M.lazy_require(modules) + return setmetatable({}, { + __index = function(self, key) + local loaded = rawget(self, key) + if loaded ~= nil then + return loaded + end + local module_location = modules[key] + if module_location == nil then + return nil + end + local module = M.require(module_location) + rawset(self, key, module) + return module + end, + }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/scripts/docgen.sh b/config/neovim/store/lazy-plugins/lualine.nvim/scripts/docgen.sh new file mode 100644 index 00000000..fbc139b1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/scripts/docgen.sh @@ -0,0 +1,41 @@ +# Copyright (c) 2020-2021 shadmansaleh +# MIT license, see LICENSE for more details. + +PANVIMDOC_TAG_VERSION="v2.7.1" # panvimdoc version + +# panvimdocs metadata +PANVIMDOC_VIMDOC="lualine" +PANVIMDOC_DESCRIPTION="fast and easy to configure statusline plugin for neovim" +PANVIMDOC_PANDOC="README.md" +PANVIMDOC_VERSION="NVIM v0.5.0" +PANVIMDOC_TOC=true +PANDOC_OUTPUT="doc/lualine.txt" + +PANVIMDOC_INSTALLED=false # Whether panvimdoc was installed by this script + +if [ ! -d "panvimdoc/" ];then + # Grab panvimdoc if not present + PANVIMDOC_INSTALLED=true + echo "Installing panvimdoc" + git clone --depth 1\ + --branch "${PANVIMDOC_TAG_VERSION}"\ + "https://github.com/kdheepak/panvimdoc" "panvimdoc" +fi + +echo "Generating docs" +pandoc --metadata=project:"${PANVIMDOC_VIMDOC}"\ + --metadata=toc:${PANVIMDOC_TOC}\ + --metadata=vimversion:"${PANVIMDOC_VERSION}"\ + --metadata=description:"${PANVIMDOC_DESCRIPTION}"\ + --lua-filter ./panvimdoc/scripts/skip-blocks.lua\ + --lua-filter ./panvimdoc/scripts/include-files.lua\ + -t ./panvimdoc/scripts/panvimdoc.lua\ + -o "${PANDOC_OUTPUT}"\ + "${PANVIMDOC_PANDOC}" + +if $PANVIMDOC_INSTALLED ;then + # Remove panvimdoc if it was installed by this script + echo "Removing panvimdoc" + rm -rf panvimdoc +fi + diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/scripts/nvim_isolated_conf.sh b/config/neovim/store/lazy-plugins/lualine.nvim/scripts/nvim_isolated_conf.sh new file mode 100644 index 00000000..762bfd8b --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/scripts/nvim_isolated_conf.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +# Copyright (c) 2020-2021 shadmansaleh +# MIT license, see LICENSE for more details. + +USAGE="Usage nvim_isolated_conf.sh [OPTIONS] Directory +A tool to easily test isolated neovim config + +Options: + -c Create a minimal config tree at Directory + -e Edit init.vim of config in Directory + -h Show this message + -l Load neovim with config from Directory +" + +INIT_TEMPLATE="call plug#begin(\"%s/.local/share/nvim/plugged\") +\" Your plugins go here like +Plug 'nvim-lualine/lualine.nvim' + + +call plug#end() + +\" Your Viml part of config goes here +\" colorscheme onedark + + +lua << END +-- Your lua part of config goes here +require'lualine'.setup { + +} + + +END + +\" Instructions: +\" ------------------------------------------------------------- +\" Load this config with nvim_isolated_conf.sh -l %s +\" Remember to run :PlugInstall after changing plugin section +\" Also delete the comments before putting this file on issue +\" That will reduce noise +\" You can delete %s once you're done" + +while getopts "c:e:hl:" arg; do + case $arg in + h) Help=true;; + c) CreateDirInput=$OPTARG;; + l) LoadDirInput=$OPTARG;; + e) EditDirInput=$OPTARG;; + esac +done +shift $((OPTIND -1)) + + +if ! [ -z $LoadDirInput ];then + LoadDir=$(realpath $LoadDirInput) + if [ -d $LoadDir ];then + export NVIM_CONFIG_HOME=$LoadDir + export XDG_CONFIG_HOME=$NVIM_CONFIG_HOME/.config + export XDG_DATA_HOME=$NVIM_CONFIG_HOME/.local/share + export XDG_CACHE_HOME=$NVIM_CONFIG_HOME/.cache + export XDG_STATE_HOME=$NVIM_CONFIG_HOME/.local/state + nvim $@ + else + echo "Sorry can't load neovim config. ${LoadDir} doesn't exist" + fi +elif ! [ -z $CreateDirInput ];then + CreateDir=$(realpath $CreateDirInput) + echo "Creating directories" + mkdir -p ${CreateDir}/.local/share/nvim/site/autoload + mkdir -p ${CreateDir}/.config/nvim + echo "Installing VimPlug" + wget -q "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim" -O ${CreateDir}/.local/share/nvim/site/autoload/plug.vim + echo "Writing minimal init" + printf "${INIT_TEMPLATE}" ${CreateDir} ${CreateDir} ${CreateDir} > ${CreateDir}/.config/nvim/init.vim + echo "" + echo "You can edit the ${CreateDirInput}/.config/nvim/init.vim to put your config" + echo "You can load this config with nvim_isolated_conf.sh -l ${CreateDirInput}" + echo "You can open config (init.vim) to edit with nvim_isolated_conf.sh -e ${CreateDirInput}" +elif ! [ -z $EditDirInput ];then + if [ -d $EditDirInput ];then + if ! [ -z $EDITOR ];then + $EDITOR $EditDirInput/.config/nvim/init.vim + else + nvim $EditDirInput/.config/nvim/init.vim + fi + else + echo "Sorry can't load neovim config. ${LoadDir} doesn't exist" + fi +else + printf "$USAGE" +fi diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/scripts/test_runner.sh b/config/neovim/store/lazy-plugins/lualine.nvim/scripts/test_runner.sh new file mode 100644 index 00000000..20ab71d4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/scripts/test_runner.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e + +REPO_DIR=$(git rev-parse --show-toplevel) + +nvim_t() { + nvim -u "$REPO_DIR/tests/minimal_init.lua" -c "set runtimepath+=$REPO_DIR" "$@" +} + +if [ -n "$1" ]; then + nvim_t --headless -c "lua require('plenary.busted').run('$1')" +else + nvim_t --headless -c "lua require'plenary.test_harness'.test_directory( 'tests/', { minimal_init = './tests/minimal_init.lua' })" -c "qa!" +fi diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/tests/helpers.lua b/config/neovim/store/lazy-plugins/lualine.nvim/tests/helpers.lua new file mode 100644 index 00000000..204cc2f7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/tests/helpers.lua @@ -0,0 +1,99 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +assert = require('luassert') +local eq = assert.are.same + +local M = {} + +M.meths = setmetatable({}, { + __index = function(_, key) + return vim.api['nvim_' .. key] + end, +}) + +function M.init_component(component, opts) + if component == nil then + component = 'special.function_component' + else + opts.component_name = component + end + local comp = require('lualine.components.' .. component) + if type(comp) == 'table' then + comp = comp(opts) + elseif type(comp) == 'function' then + opts[1] = comp + comp = require('lualine.components.special.function_component')(opts) + end + return comp +end + +-- Checks output of a component +M.assert_component = function(component, opts, result, is_active) + local comp = M.init_component(component, opts) + -- for testing global options + eq(result, comp:draw(opts.hl, is_active or true)) +end + +function M.assert_component_instance(comp, result) + eq(result, comp:draw(comp.options.hl)) +end +-- sets defaults for component options +M.build_component_opts = function(opts) + if not opts then + opts = {} + end + if opts[1] == nil then + opts[1] = function() + return 'test' + end + end + if not opts.self then + opts.self = { section = 'c' } + end + if not opts.theme then + opts.theme = 'gruvbox' + end + if not opts.hl then + opts.hl = '' + end + if opts.icons_enabled == nil then + opts.icons_enabled = true + end + if not opts.component_separators then + opts.component_separators = { left = '', right = '' } + end + if not opts.section_separators then + opts.section_separators = { left = '', right = '' } + end + return opts +end + +M.P = function(t) + print(vim.inspect(t)) +end + +function M.dedent(str, leave_indent) + -- find minimum common indent across lines + local indent = nil + for line in str:gmatch('[^\n]+') do + local line_indent = line:match('^%s+') or '' + if indent == nil or #line_indent < #indent then + indent = line_indent + end + end + if indent == nil or #indent == 0 then + -- no minimum common indent + return str + end + local left_indent = (' '):rep(leave_indent or 0) + -- create a pattern for the indent + indent = indent:gsub('%s', '[ \t]') + -- strip it from the first line + str = str:gsub('^' .. indent, left_indent) + -- strip it from the remaining lines + str = str:gsub('[\n]' .. indent, '\n' .. left_indent) + return str +end + +return M diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/tests/minimal_init.lua b/config/neovim/store/lazy-plugins/lualine.nvim/tests/minimal_init.lua new file mode 100644 index 00000000..109c2e7c --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/tests/minimal_init.lua @@ -0,0 +1,14 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +if os.getenv('TEST_COV') then + require('luacov') +end +-- load lualine and plenary +vim.cmd([[ + set noswapfile + set rtp+=. + set rtp+=../plenary.nvim + set rtp+=../nvim-web-devicons/ + runtime plugin/plenary.vim +]]) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/component_spec.lua b/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/component_spec.lua new file mode 100644 index 00000000..7be377bd --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/component_spec.lua @@ -0,0 +1,843 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +local helpers = require('tests.helpers') + +local eq = assert.are.same +local neq = assert.are_not.same +local assert_component = helpers.assert_component +local build_component_opts = helpers.build_component_opts +local stub = require('luassert.stub') + +describe('Component:', function() + it('can select separators', function() + local opts = build_component_opts() + local comp = require('lualine.components.special.function_component')(opts) + -- correct for lualine_c + eq('', comp.options.separator) + local opts2 = build_component_opts { self = { section = 'y' } } + local comp2 = require('lualine.components.special.function_component')(opts2) + -- correct for lualine_u + eq('', comp2.options.separator) + end) + + it('can provide unique identifier', function() + local opts1 = build_component_opts() + local comp1 = require('lualine.components.special.function_component')(opts1) + local opts2 = build_component_opts() + local comp2 = require('lualine.components.special.function_component')(opts2) + neq(comp1.component_no, comp2.component_no) + end) + + it('create option highlights', function() + local color = { fg = '#224532', bg = '#892345' } + local opts1 = build_component_opts { color = color } + local hl = require('lualine.highlight') + stub(hl, 'create_component_highlight_group') + hl.create_component_highlight_group.returns('MyCompHl') + local comp1 = require('lualine.components.special.function_component')(opts1) + eq('MyCompHl', comp1.options.color_highlight) + -- color highlight wan't in options when create_comp_hl was + -- called so remove it before assert + comp1.options.color_highlight = nil + assert.stub(hl.create_component_highlight_group).was_called_with( + color, + comp1.options.component_name, + comp1.options, + false + ) + hl.create_component_highlight_group:revert() + color = 'MyHl' + local opts2 = build_component_opts { color = color } + stub(hl, 'create_component_highlight_group') + hl.create_component_highlight_group.returns('MyCompLinkedHl') + local comp2 = require('lualine.components.special.function_component')(opts2) + eq('MyCompLinkedHl', comp2.options.color_highlight) + -- color highlight wan't in options when create_comp_hl was + -- called so remove it before assert + comp2.options.color_highlight = nil + assert.stub(hl.create_component_highlight_group).was_called_with( + color, + comp2.options.component_name, + comp2.options, + false + ) + hl.create_component_highlight_group:revert() + end) + + it('can draw', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + assert_component(nil, opts, 'test') + end) + + it('can apply separators', function() + local opts = build_component_opts { padding = 0 } + assert_component(nil, opts, 'test') + end) + + it('can apply default highlight', function() + local opts = build_component_opts { padding = 0, hl = '%#My_highlight#' } + assert_component(nil, opts, '%#My_highlight#test') + opts = build_component_opts { + function() + return '%#Custom_hl#test' + end, + padding = 0, + hl = '%#My_highlight#', + } + assert_component(nil, opts, '%#Custom_hl#test%#My_highlight#') + opts = build_component_opts { + function() + return 'in middle%#Custom_hl#test' + end, + padding = 0, + hl = '%#My_highlight#', + } + assert_component(nil, opts, '%#My_highlight#in middle%#Custom_hl#test%#My_highlight#') + end) + + describe('Global options:', function() + it('left_padding', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = { left = 5 }, + } + assert_component(nil, opts, ' test') + end) + + it('right_padding', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = { right = 5 }, + } + assert_component(nil, opts, 'test ') + end) + + it('padding', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 5, + } + assert_component(nil, opts, ' test ') + end) + + it('icon', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + icon = '0', + } + assert_component(nil, opts, '0 test') + end) + + it('icons_enabled', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + icons_enabled = true, + icon = '0', + } + assert_component(nil, opts, '0 test') + local opts2 = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + icons_enabled = false, + icon = '0', + } + assert_component(nil, opts2, 'test') + end) + + it('separator', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + separator = '|', + } + assert_component(nil, opts, 'test|') + end) + + it('fmt', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + fmt = function(data) + return data:sub(1, 1):upper() .. data:sub(2, #data) + end, + } + assert_component(nil, opts, 'Test') + end) + + it('cond', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + cond = function() + return true + end, + } + assert_component(nil, opts, 'test') + local opts2 = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + cond = function() + return false + end, + } + assert_component(nil, opts2, '') + end) + + it('color', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + color = 'MyHl', + } + local comp = require('lualine.components.special.function_component')(opts) + local custom_link_hl_name = 'lualine_c_' .. comp.options.component_name + eq('%#' .. custom_link_hl_name .. '#test', comp:draw(opts.hl)) + local opts2 = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + color = { bg = '#230055', fg = '#223344' }, + } + local hl = require('lualine.highlight') + stub(hl, 'component_format_highlight') + hl.component_format_highlight.returns('%#MyCompHl#') + local comp2 = require('lualine.components.special.function_component')(opts2) + assert_component(nil, opts2, '%#MyCompHl#test') + assert.stub(hl.component_format_highlight).was_called_with(comp2.options.color_highlight) + hl.component_format_highlight:revert() + end) + + it('draw_empty', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + separator = '>', + fmt = function() + return '' + end, + draw_empty = true, + } + assert_component(nil, opts, '>') + end) + end) +end) + +describe('Encoding component', function() + it('works', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + local tmp_path = 'tmp.txt' + local tmp_fp = io.open(tmp_path, 'w') + tmp_fp:write('test file') + tmp_fp:close() + vim.cmd('e ' .. tmp_path) + assert_component('encoding', opts, 'utf-8') + vim.cmd('bd!') + os.remove(tmp_path) + end) +end) + +describe('Fileformat component', function() + it('works with icons', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + local fmt = vim.bo.fileformat + vim.bo.fileformat = 'unix' + assert_component('fileformat', opts, '') + vim.bo.fileformat = 'dos' + assert_component('fileformat', opts, '') + vim.bo.fileformat = 'mac' + assert_component('fileformat', opts, '') + vim.bo.fileformat = fmt + end) + it('works without icons', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + icons_enabled = false, + } + assert_component('fileformat', opts, vim.bo.fileformat) + end) +end) + +describe('Filetype component', function() + local filetype + + before_each(function() + filetype = vim.bo.filetype + vim.bo.filetype = 'lua' + end) + + after_each(function() + vim.bo.filetype = filetype + end) + + it('does not add icon when library unavailable', function() + local old_require = _G.require + function _G.require(...) + if select(1, ...) == 'nvim-web-devicons' then + error('Test case not suppose to have web-dev-icon 👀') + end + return old_require(...) + end + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + assert_component('filetype', opts, 'lua') + _G.require = old_require + end) + + it('colors nvim-web-devicons icons', function() + vim.g.actual_curwin = tostring(vim.api.nvim_get_current_win()) + stub(vim.fn, 'expand') + vim.fn.expand.on_call_with('%:t').returns('test.lua') + + local hl = require('lualine.highlight') + stub(hl, 'create_component_highlight_group') + hl.create_component_highlight_group.returns { name = 'MyCompHl', no_mode = false, section = 'a' } + stub(hl, 'format_highlight') + hl.format_highlight.returns('%#lualine_c_normal#') + + local utils = require('lualine.utils.utils') + stub(utils, 'extract_highlight_colors') + utils.extract_highlight_colors.returns('#000') + + local devicons = require('nvim-web-devicons') + stub(devicons, 'get_icon') + devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group') + + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + hl = '%#lualine_c_normal#', + padding = 0, + colored = true, + icon_only = false, + } + assert_component('filetype', opts, '%#MyCompHl_normal#* %#lualine_c_normal#lua%#lualine_c_normal#') + assert.stub(devicons.get_icon).was_called_with('test.lua') + assert.stub(utils.extract_highlight_colors).was_called_with('test_highlight_group', 'fg') + assert.stub(hl.create_component_highlight_group).was_called_with( + { fg = '#000' }, + 'filetype_test_highlight_group', + opts, + false + ) + assert.stub(vim.fn.expand).was_called_with('%:t') + + devicons.get_icon:revert() + utils.extract_highlight_colors:revert() + hl.create_component_highlight_group:revert() + hl.format_highlight:revert() + vim.fn.expand:revert() + vim.g.actual_curwin = nil + end) + + it("doesn't color when colored is false", function() + stub(vim.fn, 'expand') + vim.fn.expand.on_call_with('%:t').returns('test.lua') + + local hl = require('lualine.highlight') + stub(hl, 'create_component_highlight_group') + + local utils = require('lualine.utils.utils') + stub(utils, 'extract_highlight_colors') + + local devicons = require('nvim-web-devicons') + stub(devicons, 'get_icon') + devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group') + + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + colored = false, + } + assert_component('filetype', opts, '* lua') + assert.stub(devicons.get_icon).was_called_with('test.lua') + assert.stub(utils.extract_highlight_colors).was_not_called() + assert.stub(hl.create_component_highlight_group).was_not_called() + assert.stub(vim.fn.expand).was_called_with('%:t') + + devicons.get_icon:revert() + utils.extract_highlight_colors:revert() + hl.create_component_highlight_group:revert() + vim.fn.expand:revert() + end) + + it('displays only icon when icon_only is true', function() + stub(vim.fn, 'expand') + vim.fn.expand.on_call_with('%:t').returns('test.lua') + + local hl = require('lualine.highlight') + stub(hl, 'create_component_highlight_group') + + local utils = require('lualine.utils.utils') + stub(utils, 'extract_highlight_colors') + + local devicons = require('nvim-web-devicons') + stub(devicons, 'get_icon') + devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group') + + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + colored = false, + icon_only = true, + } + assert_component('filetype', opts, '* ') + assert.stub(devicons.get_icon).was_called_with('test.lua') + assert.stub(utils.extract_highlight_colors).was_not_called() + assert.stub(hl.create_component_highlight_group).was_not_called() + assert.stub(vim.fn.expand).was_called_with('%:t') + + devicons.get_icon:revert() + utils.extract_highlight_colors:revert() + hl.create_component_highlight_group:revert() + vim.fn.expand:revert() + end) + + it('displays right aligned icon when icon.align is "right"', function() + stub(vim.fn, 'expand') + vim.fn.expand.on_call_with('%:t').returns('test.lua') + + local hl = require('lualine.highlight') + stub(hl, 'create_component_highlight_group') + + local utils = require('lualine.utils.utils') + stub(utils, 'extract_highlight_colors') + + local devicons = require('nvim-web-devicons') + stub(devicons, 'get_icon') + devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group') + + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + colored = false, + icon_only = false, + icon = { align = 'right' } + } + assert_component('filetype', opts, 'lua * ') + assert.stub(devicons.get_icon).was_called_with('test.lua') + assert.stub(utils.extract_highlight_colors).was_not_called() + assert.stub(hl.create_component_highlight_group).was_not_called() + assert.stub(vim.fn.expand).was_called_with('%:t') + + devicons.get_icon:revert() + utils.extract_highlight_colors:revert() + hl.create_component_highlight_group:revert() + vim.fn.expand:revert() + end) + + it('uses filetype lookup when file has no extension', function() + stub(vim.fn, 'expand') + vim.fn.expand.on_call_with('%:t').returns('test') + + local hl = require('lualine.highlight') + stub(hl, 'create_component_highlight_group') + + local utils = require('lualine.utils.utils') + stub(utils, 'extract_highlight_colors') + + local devicons = require('nvim-web-devicons') + stub(devicons, 'get_icon') + devicons.get_icon.on_call_with('test').returns(nil) + stub(devicons, 'get_icon_by_filetype') + devicons.get_icon_by_filetype.on_call_with('lua').returns('*', 'test_highlight_group') + + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + colored = false, + icon_only = false, + } + assert_component('filetype', opts, '* lua') + assert.stub(devicons.get_icon).was_called_with('test') + assert.stub(devicons.get_icon_by_filetype).was_called_with('lua') + assert.stub(utils.extract_highlight_colors).was_not_called() + assert.stub(hl.create_component_highlight_group).was_not_called() + assert.stub(vim.fn.expand).was_called_with('%:t') + + devicons.get_icon_by_filetype:revert() + devicons.get_icon:revert() + utils.extract_highlight_colors:revert() + hl.create_component_highlight_group:revert() + vim.fn.expand:revert() + end) +end) + +describe('Hostname component', function() + it('works', function() + stub(vim.loop, 'os_gethostname') + vim.loop.os_gethostname.returns('localhost') + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + assert_component('hostname', opts, 'localhost') + vim.loop.os_gethostname:revert() + end) +end) + +describe('Location component', function() + it('works', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + assert_component('location', opts, ' 1:1 ') + vim.cmd('normal! 9o') + assert_component('location', opts, ' 10:1 ') + vim.api.nvim_win_set_cursor(0, {5, 0}) + assert_component('location', opts, ' 5:1 ') + -- test column number + vim.cmd('normal! oTest') + assert_component('location', opts, ' 6:4 ') + -- test column number in line containing cyrillic symbols + vim.cmd('normal! oТест') + assert_component('location', opts, ' 7:4 ') + vim.cmd('bdelete!') + end) +end) + +describe('Progress component', function() + it('works', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + assert_component('progress', opts, 'Top') + vim.cmd('normal! 9o') + assert_component('progress', opts, 'Bot') + vim.api.nvim_win_set_cursor(0, {5, 0}) + assert_component('progress', opts, '50%%') + vim.cmd('bdelete!') + end) +end) + +describe('Mode component', function() + it('works', function() + stub(vim.api, 'nvim_get_mode') + vim.api.nvim_get_mode.returns { mode = 'n', blocking = false } + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + assert_component('mode', opts, 'NORMAL') + vim.api.nvim_get_mode:revert() + end) +end) + +describe('FileSize component', function() + it('works', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + local fname = 'test-file.txt' + local f = io.open(fname, 'w') + f:write(string.rep('........................................\n', 200)) + f:close() + vim.cmd(':edit ' .. fname) + assert_component('filesize', opts, '8.0k') + vim.cmd(':bdelete!') + os.remove(fname) + end) +end) + +describe('Filename component', function() + it('works', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + file_status = false, + path = 0, + } + vim.cmd(':e test-file.txt') + assert_component('filename', opts, 'test-file.txt') + vim.cmd(':bdelete!') + end) + + it('can show file_status', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + file_status = true, + path = 0, + } + vim.cmd(':e test-file.txt') + vim.bo.modified = false + assert_component('filename', opts, 'test-file.txt') + vim.bo.modified = true + assert_component('filename', opts, 'test-file.txt [+]') + vim.bo.ro = true + assert_component('filename', opts, 'test-file.txt [+][-]') + vim.bo.modified = false + assert_component('filename', opts, 'test-file.txt [-]') + vim.cmd(':bdelete!') + end) + + it('can show new_file_status', function () + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + newfile_status = true, + path = 0, + } + vim.cmd(':e new-file.txt') + assert_component('filename', opts, 'new-file.txt [New]') + vim.bo.modified = true + assert_component('filename', opts, 'new-file.txt [+][New]') + vim.cmd(':bdelete!') + end) + + it('can show relative path', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + file_status = false, + path = 1, + } + vim.cmd(':e test-file.txt') + assert_component('filename', opts, vim.fn.expand('%:~:.')) + vim.cmd(':bdelete!') + end) + + it('can show full path', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + file_status = false, + path = 2, + shorting_target = 0, + } + vim.cmd(':e test-file.txt') + assert_component('filename', opts, vim.fn.expand('%:p')) + vim.cmd(':bdelete!') + end) + + it('shortens path', function() + stub(vim.fn, 'expand') + vim.fn.expand.on_call_with('%:p').returns('/home/foobar/test/test.lua') + stub(vim.fn, 'winwidth') + vim.fn.winwidth.on_call_with(0).returns(100) + + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + file_status = false, + path = 2, + shorting_target = 90, + } + vim.cmd(':e test-file.txt') + assert_component('filename', opts, '/h/f/t/test.lua') + + vim.cmd(':bdelete!') + vim.fn.winwidth:revert() + vim.fn.expand:revert() + end) + + it('shortens path with tilde', function() + stub(vim.fn, 'expand') + vim.fn.expand.on_call_with('%:p:~').returns('~/test/test.lua') + stub(vim.fn, 'winwidth') + vim.fn.winwidth.on_call_with(0).returns(100) + + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + file_status = false, + path = 3, + shorting_target = 90, + } + vim.cmd(':e test-file.txt') + assert_component('filename', opts, '~/t/test.lua') + + vim.cmd(':bdelete!') + vim.fn.winwidth:revert() + vim.fn.expand:revert() + end) + + it('shortens path with hidden directory', function() + stub(vim.fn, 'expand') + vim.fn.expand.on_call_with('%:p').returns('/home/foobar/.test/test.lua') + stub(vim.fn, 'winwidth') + vim.fn.winwidth.on_call_with(0).returns(100) + + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + file_status = false, + path = 2, + shorting_target = 90, + } + vim.cmd(':e test-file.txt') + assert_component('filename', opts, '/h/f/.t/test.lua') + + vim.cmd(':bdelete!') + vim.fn.winwidth:revert() + vim.fn.expand:revert() + end) + + it('shortens path with %', function() + stub(vim.fn, 'expand') + vim.fn.expand.on_call_with('%:p').returns('%dir1/%dir2/%actual_%file') + stub(vim.fn, 'winwidth') + vim.fn.winwidth.on_call_with(0).returns(100) + + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + file_status = false, + path = 2, + shorting_target = 90, + } + vim.cmd(':e test-file.txt') + assert_component('filename', opts, '%%/%%/%%actual_%%file') + + vim.cmd(':bdelete!') + vim.fn.winwidth:revert() + vim.fn.expand:revert() + end) +end) + +describe('vim option & variable component', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + + local function assert_vim_var_component(name, options, result) + options[1] = name + assert_component('special.vim_var_component', options, result) + opts[1] = nil + end + it('works with variable', function() + assert_vim_var_component('g:gvar', opts, '') + vim.g.gvar = 'var1' + assert_vim_var_component('g:gvar', opts, 'var1') + vim.g.gvar = 'var2' + assert_vim_var_component('g:gvar', opts, 'var2') + vim.b.gvar = 'bvar1' + assert_vim_var_component('b:gvar', opts, 'bvar1') + vim.w.gvar = 'wvar1' + assert_vim_var_component('w:gvar', opts, 'wvar1') + end) + it('can index dictionaries', function() + vim.g.gvar = { a = { b = 'var-value' } } + assert_vim_var_component('g:gvar.a.b', opts, 'var-value') + end) + it('works with options', function() + local old_number = vim.wo.number + vim.wo.number = false + assert_vim_var_component('wo:number', opts, 'false') + vim.wo.number = old_number + local old_tw = vim.go.tw + vim.go.tw = 80 + assert_vim_var_component('go:tw', opts, '80') + vim.go.tw = old_tw + end) +end) + +describe('Vim option & variable component', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + + local function assert_vim_var_component(name, options, result) + options[1] = name + assert_component('special.eval_func_component', options, result) + opts[1] = nil + end + + it('works with vim function', function() + vim.cmd([[ + func! TestFunction() abort + return "TestVimFunction" + endf + ]]) + assert_vim_var_component('TestFunction', opts, 'TestVimFunction') + vim.cmd('delfunction TestFunction') + end) + + it('works with lua expression', function() + _G.TestFunction = function() + return 'TestLuaFunction' + end + assert_vim_var_component('TestFunction()', opts, 'TestLuaFunction') + _G.TestFunction = nil + end) +end) + +describe('Branch component', function() + -- these tests are broken in wsl will look at them later + if vim.fn.has('wsl') == 1 then + return + end + local tmpdir + local file + local git = function(...) + return vim.fn.system( + "git -c user.name='asdf' -c user.email='asdf@jlk.org' -C " .. tmpdir .. ' ' .. string.format(...) + ) + end + local assert_comp_ins = helpers.assert_component_instance + + before_each(function() + tmpdir = os.tmpname() + os.remove(tmpdir) + file = tmpdir .. '/test.txt' + vim.fn.mkdir(tmpdir, 'p') + git('init -b test_branch') + vim.cmd([[aug lualine + au! + aug END + ]]) + end) + + after_each(function() + os.remove(tmpdir) + end) + + it('works with regular branches', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + padding = 0, + } + local branch_comp = helpers.init_component('branch', opts) + vim.cmd('e ' .. file) + assert_comp_ins(branch_comp, ' test_branch') + git('checkout -b test_branch2') + vim.cmd('e k') + vim.cmd('bd') + vim.cmd('e ' .. file) + opts.icons_enabled = false + assert_comp_ins(branch_comp, 'test_branch2') + end) + + it('works in detached head mode', function() + local opts = build_component_opts { + component_separators = { left = '', right = '' }, + icons_enabled = false, + padding = 0, + } + git('checkout -b test_branch2') + git('commit --allow-empty -m "test commit1"') + git('commit --allow-empty -m "test commit2"') + git('commit --allow-empty -m "test commit3"') + git('checkout HEAD~1') + vim.cmd('e ' .. file) + local rev = git('rev-parse --short=6 HEAD'):sub(1, 6) + assert_component('branch', opts, rev) + end) +end) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/config_spec.lua b/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/config_spec.lua new file mode 100644 index 00000000..ba7532ae --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/config_spec.lua @@ -0,0 +1,185 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +local eq = assert.are.same + +describe('config parsing', function() + local config_module = require('lualine.config') + + describe('options', function() + describe('icons_enabled', function() + it('default', function() + local config = config_module.apply_configuration {} + eq(config.options.icons_enabled, true) + end) + it('custom', function() + local config = { options = { icons_enabled = false } } + config = config_module.apply_configuration(config) + eq(config.options.icons_enabled, false) + end) + end) + + describe('theme', function() + it('default', function() + local config = config_module.apply_configuration {} + eq(config.options.theme, 'auto') + end) + it('custom', function() + local config = { options = { theme = 'nord' } } + config = config_module.apply_configuration(config) + eq(config.options.theme, 'nord') + config = { options = { theme = {} } } + config = config_module.apply_configuration(config) + eq(config.options.theme, {}) + end) + end) + + describe('separators', function() + it('default', function() + local config = config_module.apply_configuration {} + eq(config.options.component_separators, { left = '', right = '' }) + eq(config.options.section_separators, { left = '', right = '' }) + end) + it('double separators', function() + local config = { + options = { + component_separators = { left = 'a', right = 'b' }, + section_separators = { left = 'c', right = 'd' }, + }, + } + config = config_module.apply_configuration(config) + eq(config.options.component_separators, { left = 'a', right = 'b' }) + eq(config.options.section_separators, { left = 'c', right = 'd' }) + end) + + describe('single separator', function() + it('string', function() + local config = { + options = { component_separators = 'a', section_separators = 'b' }, + } + config = config_module.apply_configuration(config) + eq(config.options.component_separators, { left = 'a', right = 'a' }) + eq(config.options.section_separators, { left = 'b', right = 'b' }) + end) + it('table', function() + local config = { + options = { + component_separators = { left = 'a', right = 'b' }, + section_separators = { left = 'b', right = 'a' }, + }, + } + config = config_module.apply_configuration(config) + eq(config.options.component_separators, { left = 'a', right = 'b' }) + eq(config.options.section_separators, { left = 'b', right = 'a' }) + end) + end) + it('no separators', function() + local config = { + options = { component_separators = {}, section_separators = {} }, + } + config = config_module.apply_configuration(config) + eq(config.options.component_separators, {}) + eq(config.options.section_separators, {}) + end) + end) + + describe('disabled filetypes', function() + it('default', function() + local config = config_module.apply_configuration {} + eq(config.options.disabled_filetypes, {statusline={}, winbar={}}) + end) + it('custom', function() + local config = { options = { disabled_filetypes = { 'lua' } } } + config = config_module.apply_configuration(config) + eq(config.options.disabled_filetypes, {statusline={'lua'}, winbar={'lua'}}) + end) + end) + + describe('non default global option', function() + it('default', function() + local config = { options = {} } + config = config_module.apply_configuration(config) + eq(config.options.non_default_global_option, nil) + end) + it('custom', function() + local config = { options = { non_default_global_option = 1 } } + config = config_module.apply_configuration(config) + eq(config.options.non_default_global_option, 1) + end) + end) + end) + + describe('sections', function() + it('default', function() + local config = {} + config = config_module.apply_configuration(config) + local lualine_default_sections = { + lualine_a = { 'mode' }, + lualine_b = { 'branch', 'diff', 'diagnostics' }, + lualine_c = { 'filename' }, + lualine_x = { 'encoding', 'fileformat', 'filetype' }, + lualine_y = { 'progress' }, + lualine_z = { 'location' }, + } + eq(config.sections, lualine_default_sections) + end) + it('custom', function() + local custom_sections = { + lualine_a = { { 'mode', lower = true } }, + lualine_b = { 'branch', { 'branch', lower = true } }, + lualine_c = nil, + lualine_x = {}, + } + local expected_sections = { + lualine_a = { { 'mode', lower = true } }, + lualine_b = { 'branch', { 'branch', lower = true } }, + lualine_c = { 'filename' }, + lualine_x = {}, + lualine_y = { 'progress' }, + lualine_z = { 'location' }, + } + local config = { sections = custom_sections } + config = config_module.apply_configuration(config) + eq(config.sections, expected_sections) + end) + end) + + describe('inactive_sections', function() end) + + describe('tabline', function() + it('default', function() + local config = {} + config = config_module.apply_configuration(config) + eq(config.tabline, {}) + end) + it('custom', function() + local custom_sections = { + lualine_a = { { 'mode', lower = true } }, + lualine_b = { 'branch', { 'branch', lower = true } }, + lualine_c = nil, + lualine_x = {}, + } + local expected_sections = { + lualine_a = { { 'mode', lower = true } }, + lualine_b = { 'branch', { 'branch', lower = true } }, + lualine_x = {}, + } + local config = { tabline = custom_sections } + config = config_module.apply_configuration(config) + eq(config.tabline, expected_sections) + end) + end) + + describe('extensions', function() + it('default', function() + local config = { options = {} } + config = config_module.apply_configuration(config) + eq(config.extensions, {}) + end) + it('custom', function() + local config = { extensions = { 'fugitive' } } + config = config_module.apply_configuration(config) + eq(config.extensions, { 'fugitive' }) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/lualine_spec.lua b/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/lualine_spec.lua new file mode 100644 index 00000000..eb46a3ce --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/lualine_spec.lua @@ -0,0 +1,1027 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +local eq = assert.are.same +local statusline = require('tests.statusline').new(120, 'active') +local inactive_statusline = statusline.new(120, 'inactive') +local tabline = statusline.new(120, 'tabline') + +describe('Lualine', function() + local config + before_each(function() + config = { + options = { + icons_enabled = true, + theme = 'gruvbox', + component_separators = { left = '', right = '' }, + section_separators = { left = '', right = '' }, + disabled_filetypes = { + statusline = {}, + winbar = {}, + }, + ignore_focus = {}, + always_divide_middle = true, + globalstatus = false, + refresh = { + statusline = 1000, + tabline = 1000, + winbar = 1000, + }, + }, + sections = { + lualine_a = { 'mode' }, + -- We can't test branch component inside lualines repo. + -- As branch name will differ in pr/CI. We could setup a dummy repo + -- but plenary doesn't yet support setup() & teardown() so replacing + -- branch with a dummy component. + lualine_b = { + { + function() + return 'master' + end, + icon = '', + }, + 'diagnostics', + }, + lualine_c = { 'filename' }, + lualine_x = { 'encoding', 'fileformat', 'filetype' }, + lualine_y = { 'progress' }, + lualine_z = { 'location' }, + }, + inactive_sections = { + lualine_a = {}, + lualine_b = {}, + lualine_c = { 'filename' }, + lualine_x = { 'location' }, + lualine_y = {}, + lualine_z = {}, + }, + tabline = {}, + winbar = {}, + inactive_winbar = {}, + extensions = {}, + } + + vim.opt.swapfile = false + vim.cmd('bufdo bdelete') + pcall(vim.cmd, 'tabdo tabclose') + require('lualine').setup(config) + end) + + it('shows active statusline', function() + statusline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984" } + 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2" } + 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: NORMAL } + {2:} + {3:  master } + {4:} + {5: [No Name] } + {5: } + {5:  } + {4:} + {3: Top } + {2:} + {1: 1:1 }| + ]===]) + end) + + it('shows inactive statusline', function() + inactive_statusline:expect([===[ + highlights = { + 1: lualine_c_inactive = { bg = "#3c3836", fg = "#a89984" } + } + |{1: [No Name] } + {1: } + {1: 1:1 }| + ]===]) + end) + + it('get_config can retrieve config', function() + eq(config, require('lualine').get_config()) + end) + + it('can live update config', function() + local conf = require('lualine').get_config() + conf.sections.lualine_a = {} + require('lualine').setup(conf) + statusline:expect([===[ + highlights = { + 1: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2" } + 2: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 4: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984" } + 5: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + } + |{1:  master } + {2:} + {3: [No Name] } + {3: } + {3:  } + {2:} + {1: Top } + {4:} + {5: 1:1 }| + ]===]) + end) + + it('Can work without section separators', function() + config.options.section_separators = '' + require('lualine').setup(config) + statusline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: NORMAL } + {2:  master } + {3: [No Name] } + {3: } + {3:  } + {2: Top } + {1: 1:1 }| + ]===]) + end) + + it('Can work without component_separators', function() + table.insert(config.sections.lualine_a, function() + return 'test_comp1' + end) + table.insert(config.sections.lualine_z, function() + return 'test_comp2' + end) + + require('lualine').setup(config) + statusline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984" } + 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2" } + 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: NORMAL } + {1: test_comp1 } + {2:} + {3:  master } + {4:} + {5: [No Name] } + {5: } + {5:  } + {4:} + {3: Top } + {2:} + {1: 1:1 } + {1: test_comp2 }| + ]===]) + + config.options.component_separators = '' + require('lualine').setup(config) + statusline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984" } + 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2" } + 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: NORMAL } + {1: test_comp1 } + {2:} + {3:  master } + {4:} + {5: [No Name] } + {5: } + {5:  } + {4:} + {3: Top } + {2:} + {1: 1:1 } + {1: test_comp2 }| + ]===]) + end) + + it('mid divider can be disabled on special case', function() + config.options.always_divide_middle = false + config.sections.lualine_x = {} + config.sections.lualine_y = {} + config.sections.lualine_z = {} + require('lualine').setup(config) + statusline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984" } + 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2" } + 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: NORMAL } + {2:} + {3:  master } + {4:} + {5: [No Name] }| + ]===]) + end) + + it('works with icons disabled', function() + config.options.icons_enabled = false + config.options.section_separators = '' + require('lualine').setup(config) + statusline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: NORMAL } + {2: master } + {3: [No Name] } + {3: } + {3: unix } + {2: Top } + {1: 1:1 }| + ]===]) + end) + + it('can be desabled for specific filetypes', function() + config.options.disabled_filetypes = { 'test_ft' } + require('lualine').setup(config) + local old_ft = vim.bo.ft + vim.bo.ft = 'test_ft' + statusline:expect(nil) + vim.bo.ft = old_ft + end) + + it('can apply custom extensions', function() + table.insert(config.extensions, { + filetypes = { 'test_ft' }, + sections = { + lualine_a = { + function() + return 'custom_extension_component' + end, + }, + }, + }) + local old_ft = vim.bo.ft + vim.bo.ft = 'test_ft' + require('lualine').setup(config) + statusline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: custom_extension_component } + {2:} + {3: }| + ]===]) + vim.bo.ft = old_ft + end) + + it('same extension can be applied to multiple filetypes', function() + table.insert(config.extensions, { + filetypes = { 'test_ft1', 'test_ft2' }, + sections = { + lualine_a = { + function() + return 'custom_extension_component' + end, + }, + }, + }) + local old_ft = vim.bo.ft + vim.bo.ft = 'test_ft1' + require('lualine').setup(config) + statusline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: custom_extension_component } + {2:} + {3: }| + ]===]) + vim.bo.ft = old_ft + statusline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984" } + 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2" } + 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: NORMAL } + {2:} + {3:  master } + {4:} + {5: [No Name] } + {5: } + {5:  } + {4:} + {3: Top } + {2:} + {1: 1:1 }| + ]===]) + vim.bo.ft = 'test_ft2' + statusline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: custom_extension_component } + {2:} + {3: }| + ]===]) + vim.bo.ft = old_ft + end) + + -- TODO: figure put why some of the tablines tests fail in CI + describe('tabline', function() + local tab_conf = vim.deepcopy(config) + tab_conf.tabline = { + lualine_a = { + function() + return 'tabline_component' + end, + }, + lualine_b = {}, + lualine_c = {}, + lualine_x = {}, + lualine_y = {}, + lualine_z = {}, + } + + it('can use tabline', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { + function() + return 'tabline_component' + end, + } + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: tabline_component } + {2:} + {3: }| + ]===]) + end) + + it('can use tabline as statusline', function() + local conf = vim.deepcopy(config) + conf.tabline = conf.sections + conf.sections = {} + conf.inactive_sections = {} + require('lualine').setup(conf) + require('lualine').statusline() + eq('%#Normal#', vim.go.statusline) + + tabline:expect([===[ + highlights = { + 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984" } + 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2" } + 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: NORMAL } + {2:} + {3:  master } + {4:} + {5: [No Name] } + {5: } + {5:  } + {4:} + {3: Top } + {2:} + {1: 1:1 }| + ]===]) + end) + describe('tabs component', function() + it('works', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'tabs', max_length = 1e3 } } + vim.cmd('tabnew') + vim.cmd('tabnew') + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 } + {1: 2 } + {2:} + {3: 3 } + {4:} + {5: }| + ]===]) + + vim.cmd('tabprev') + tabline:expect([===[ + highlights = { + 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_tabs_active_to_lualine_a_tabs_inactive = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 } + {2:} + {3: 2 } + {4:} + {1: 3 } + {5: }| + ]===]) + + vim.cmd('tabprev') + tabline:expect([===[ + highlights = { + 1: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_tabs_active_to_lualine_a_tabs_inactive = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 } + {2:} + {3: 2 } + {3: 3 } + {4: }| + ]===]) + end) + + it('mode option can change layout', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'tabs', max_length = 1e3, mode = 0 } } + vim.cmd('tabe ' .. 'a.txt') + vim.cmd('tabe ' .. 'b.txt') + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 } + {1: 2 } + {2:} + {3: 3 } + {4:} + {5: }| + ]===]) + + tabline:expect([===[ + highlights = { + 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 } + {1: 2 } + {2:} + {3: 3 } + {4:} + {5: }| + ]===]) + + conf.tabline.lualine_a = { { 'tabs', max_length = 1e3, mode = 1 } } + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: [No Name] } + {1: a.txt } + {2:} + {3: b.txt } + {4:} + {5: }| + ]===]) + + conf.tabline.lualine_a = { { 'tabs', max_length = 1e3, mode = 2 } } + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 [No Name] } + {1: 2 a.txt } + {2:} + {3: 3 b.txt } + {4:} + {5: }| + ]===]) + end) + end) + + describe('buffers component', function() + it('works', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, icons_enabled = false } } + vim.cmd('tabe ' .. 'a.txt') + vim.cmd('tabe ' .. 'b.txt') + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: #a.txt } + {2:} + {3: b.txt } + {4:} + {1: [No Name] } + {MATCH:{5:%s+}|} + ]===]) + + vim.cmd('tabprev') + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: a.txt } + {2:} + {3: b.txt } + {3: #[No Name] } + {MATCH:{4:%s+}|} + ]===]) + + vim.cmd('tabprev') + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: a.txt } + {1: #b.txt } + {2:} + {3: [No Name] } + {4:} + {MATCH:{5:%s+}|} + ]===]) + end) + + it('mode option can change layout', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'tabs', max_length = 1e3, mode = 0, icons_enabled = false } } + vim.cmd('tabe ' .. 'a.txt') + vim.cmd('tabe ' .. 'b.txt') + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + {MATCH:|{1: %d+ }} + {MATCH:{1: %d+ }} + {2:} + {MATCH:{3: %d+ }} + {4:} + {MATCH:{5:%s+}|} + ]===]) + + conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, mode = 1, icons_enabled = false } } + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + {MATCH:|{1: #%d+ }} + {2:} + {MATCH:{3: %d+ }} + {4:} + {MATCH:{1: %d+ }} + {MATCH:{5:%s+}|} + ]===]) + + conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, mode = 2, icons_enabled = false } } + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + {MATCH:|{1: #%d+ a.txt }} + {2:} + {MATCH:{3: %d+ b.txt }} + {4:} + {MATCH:{1: %d+ %[No Name%] }} + {MATCH:{5:%s+}|} + ]===]) + end) + + it('can show modified status', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, show_modified_status = true, icons_enabled = false } } + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: [No Name] } + {2:} + {3: }| + ]===]) + + vim.bo.modified = true + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: [No Name] ● } + {2:} + {3: }| + ]===]) + vim.bo.modified = false + end) + + it('can show relative path', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, show_filename_only = false, icons_enabled = false } } + require('lualine').setup(conf) + require('lualine').statusline() + local path = 'aaaaaa/bbbbb/cccccc/ddddd/eeeee/ffff/gggg' + vim.fn.mkdir(path, 'p') + vim.cmd('e ' .. path .. '/asdf.txt') + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: a/b/c/d/e/f/g/asdf.txt } + {2:} + {3: }| + ]===]) + vim.fn.delete(path:match('(%w+)/.*'), 'rf') + end) + + it('can show ellipsis when max_width is crossed', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'buffers', max_length = 1 } } + vim.cmd('tabe a.txt') + vim.cmd('tabe b.txt') + vim.cmd('tabprev') + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 󰈙 a.txt } + {2:} + {3: ... } + {4: }| + ]===]) + end) + + it('can show filetype icons', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, show_filename_only = false } } + require('lualine').setup(conf) + require('lualine').statusline() + vim.cmd('e t.lua') + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1:  t.lua } + {2:} + {3: }| + ]===]) + end) + + it('can show buffer numbers instead of indices (without file names)', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'buffers', mode = 3, max_length = 1e3, icons_enabled = false } } + require('lualine').setup(conf) + require('lualine').statusline() + vim.cmd('e a.txt') + vim.cmd('silent! bd #') -- NeoVim 0.5 does not create an unnamed buffer. This ensures consistent results between NeoVim versions. + vim.cmd('e b.txt') + local bufnr_a = vim.fn.bufnr('a.txt') + local bufnr_b = vim.fn.bufnr('b.txt') + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: #]===] .. bufnr_a .. [===[ } + {2:} + {3: ]===] .. bufnr_b .. [===[ } + {4:} + {MATCH:{5:%s+}|} + ]===]) + end) + + it('can show buffer numbers instead of indices (with file names)', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'buffers', mode = 4, max_length = 1e3, icons_enabled = false } } + vim.cmd('e a.txt') + vim.cmd('silent! bd #') -- NeoVim 0.5 does not create an unnamed buffer. This ensures consistent results between NeoVim versions. + vim.cmd('e b.txt') + local bufnr_a = vim.fn.bufnr('a.txt') + local bufnr_b = vim.fn.bufnr('b.txt') + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: #]===] .. bufnr_a .. [===[ a.txt } + {2:} + {3: ]===] .. bufnr_b .. [===[ b.txt } + {4:} + {MATCH:{5:%s+}|} + ]===]) + end) + + it('displays alternate buffer correctly when switching buffers', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'buffers', mode = 3, max_length = 1e3, icons_enabled = false } } + require('lualine').setup(conf) + require('lualine').statusline() + vim.cmd('e a.txt') + vim.cmd('silent! bd #') -- NeoVim 0.5 does not create an unnamed buffer. This ensures consistent results between NeoVim versions. + vim.cmd('e b.txt') + local bufnr_a = vim.fn.bufnr('a.txt') + local bufnr_b = vim.fn.bufnr('b.txt') + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: #]===] .. bufnr_a .. [===[ } + {2:} + {3: ]===] .. bufnr_b .. [===[ } + {4:} + {MATCH:{5:%s+}|} + ]===]) + vim.cmd('e a.txt') + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: ]===] .. bufnr_a .. [===[ } + {2:} + {3: #]===] .. bufnr_b .. [===[ } + {MATCH:{4:%s+}|} + ]===]) + vim.cmd('bprev') + tabline:expect([===[ + highlights = { + 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" } + 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" } + 4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: #]===] .. bufnr_a .. [===[ } + {2:} + {3: ]===] .. bufnr_b .. [===[ } + {4:} + {MATCH:{5:%s+}|} + ]===]) + end) + end) + + describe('windows component', function() + it('works', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'windows', max_length = 1e3, mode = 2, icons_enabled = false } } + vim.cmd('e ' .. 'a.txt') + vim.cmd('tabe ' .. 'b.txt') + vim.cmd('vsplit ' .. 'c.txt') + vim.cmd('tabe ' .. 'd.txt') + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_windows_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_windows_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 d.txt } + {2:} + {3: }| + ]===]) + + vim.cmd('tabprev') + tabline:expect([===[ + highlights = { + 1: lualine_a_windows_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_windows_active_to_lualine_a_windows_inactive = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_a_windows_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 c.txt } + {2:} + {3: 2 b.txt } + {4: }| + ]===]) + + vim.cmd('tabprev') + tabline:expect([===[ + highlights = { + 1: lualine_a_windows_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_windows_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 a.txt } + {2:} + {3: }| + ]===]) + end) + end) + end) + + describe('diagnostics', function() + local diagnostics_conf = vim.deepcopy(config) + diagnostics_conf.sections = { + lualine_a = { + { + 'diagnostics', + symbols = { error = 'E:', warn = 'W:', info = 'I:', hint = 'H:' }, + diagnostics_color = { + error = { bg = '#a89984', fg = '#ff0000' }, + warn = { bg = '#a89984', fg = '#ffa500' }, + info = { bg = '#a89984', fg = '#add8e6' }, + hint = { bg = '#a89984', fg = '#d3d3d3' }, + }, + sources = { + function() + return {} + end, + }, + }, + }, + lualine_b = {}, + lualine_c = {}, + lualine_x = {}, + lualine_y = {}, + lualine_z = {}, + } + + it('does not show without diagnostics', function() + local conf = vim.deepcopy(diagnostics_conf) + require('lualine').setup(conf) + statusline:expect([===[ + highlights = { + 1: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: }| + ]===]) + end) + + it('shows only positive diagnostics', function() + local conf = vim.deepcopy(diagnostics_conf) + conf.sections.lualine_a[1].sources[1] = function() + return { error = 0, warn = 0, info = 1, hint = 0 } + end + require('lualine').setup(conf) + statusline:expect([===[ + highlights = { + 1: lualine_a_diagnostics_info = { bg = "#a89984", fg = "#add8e6" } + 2: lualine_transitional_lualine_a_diagnostics_info_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: I:1 } + {2:} + {3: }| + ]===]) + end) + + it('shows all diagnostics with same background', function() + local conf = vim.deepcopy(diagnostics_conf) + conf.sections.lualine_a[1].sources[1] = function() + return { error = 1, warn = 2, info = 3, hint = 4 } + end + require('lualine').setup(conf) + statusline:expect([===[ + highlights = { + 1: lualine_a_diagnostics_error = { bg = "#a89984", fg = "#ff0000" } + 2: lualine_a_diagnostics_warn = { bg = "#a89984", fg = "#ffa500" } + 3: lualine_a_diagnostics_info = { bg = "#a89984", fg = "#add8e6" } + 4: lualine_a_diagnostics_hint = { bg = "#a89984", fg = "#d3d3d3" } + 5: lualine_transitional_lualine_a_diagnostics_hint_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 6: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: E:1 } + {2:W:2 } + {3:I:3 } + {4:H:4 } + {5:} + {6: }| + ]===]) + end) + + it('shows all diagnostics with padding when background changes', function() + local conf = vim.deepcopy(diagnostics_conf) + conf.sections.lualine_a[1].sources[1] = function() + return { error = 1, warn = 2, info = 3, hint = 4 } + end + conf.sections.lualine_a[1].diagnostics_color = { + error = { bg = '#ff0000', fg = '#a89984' }, + warn = { bg = '#ffa500', fg = '#a89984' }, + info = { bg = '#add8e6', fg = '#a89984' }, + hint = { bg = '#add8e6', fg = '#a89984' }, + } + require('lualine').setup(conf) + statusline:expect([===[ + highlights = { + 1: lualine_a_diagnostics_error = { bg = "#ff0000", fg = "#a89984" } + 2: lualine_a_diagnostics_warn = { bg = "#ffa500", fg = "#a89984" } + 3: lualine_a_diagnostics_info = { bg = "#add8e6", fg = "#a89984" } + 4: lualine_a_diagnostics_hint = { bg = "#add8e6", fg = "#a89984" } + 5: lualine_transitional_lualine_a_diagnostics_hint_to_lualine_c_normal = { bg = "#3c3836", fg = "#add8e6" } + 6: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: E:1 } + {2: W:2 } + {3: I:3 } + {4:H:4 } + {5:} + {6: }| + ]===]) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/utils_spec.lua b/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/utils_spec.lua new file mode 100644 index 00000000..30654b74 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/tests/spec/utils_spec.lua @@ -0,0 +1,160 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +local helpers = require('tests.helpers') + +local eq = assert.are.same +local build_component_opts = helpers.build_component_opts +local stub = require('luassert.stub') + +describe('Utils', function() + local utils = require('lualine.utils.utils') + + it('can retrieve highlight groups', function() + local hl2 = { fg = '#aabbcc', bg = '#889977', sp = '#997788', reverse = true, undercurl = true } + -- handles non existing hl groups + eq(utils.extract_highlight_colors('hl2'), nil) + -- create highlight + vim.cmd(string.format('hi hl2 guifg=%s guibg=%s guisp=%s gui=reverse,undercurl', hl2.fg, hl2.bg, hl2.sp)) + -- Can retrieve entire highlight table + eq(utils.extract_highlight_colors('hl2'), hl2) + -- Can retrieve specific parts of highlight + eq(utils.extract_highlight_colors('hl2', 'fg'), hl2.fg) + -- clear hl2 + vim.cmd('hi clear hl2') + end) + + it('can extract individual highlight color', function() + local fg_clr = '#aabbcc' + local bg_clr = '#889977' + local sp_clr = '#997788' + local def_clr = '#ff0000' + local hl_std = { fg = fg_clr, bg = bg_clr } + local hl_rvs = { fg = fg_clr, bg = bg_clr, reverse = true } + local hl_ul = { sp = sp_clr, undercurl = true } + local hl_ul_rvs = { fg = fg_clr, bg = bg_clr, sp = sp_clr, reverse = true, undercurl = true } + -- create highlights + vim.cmd(string.format('hi hl_std guifg=%s guibg=%s', hl_std.fg, hl_std.bg)) + vim.cmd(string.format('hi hl_rvs guifg=%s guibg=%s gui=reverse', hl_rvs.fg, hl_rvs.bg)) + vim.cmd(string.format('hi hl_ul guisp=%s gui=undercurl', hl_ul.sp)) + vim.cmd(string.format('hi hl_ul_rvs guifg=%s guibg=%s guisp=%s gui=reverse,undercurl', hl_ul_rvs.fg, hl_ul_rvs.bg, hl_ul_rvs.sp)) + -- Can extract color from primary highlight group + eq(utils.extract_color_from_hllist('fg', {'hl_std','hl_ul'}, def_clr), fg_clr) + -- Can extract color from fallback highlight group + eq(utils.extract_color_from_hllist('fg', {'hl_noexist','hl_std'}, def_clr), fg_clr) + -- Can fall back to default color on nonexistent color + eq(utils.extract_color_from_hllist('fg', {'hl_ul'}, def_clr), def_clr) + -- Can fall back to default color on nonexistent highlight group + eq(utils.extract_color_from_hllist('fg', {'hl_noexist'}, def_clr), def_clr) + -- Can extract fallback color + eq(utils.extract_color_from_hllist({'fg','sp'}, {'hl_ul'}, def_clr), sp_clr) + -- Can extract reverse color + eq(utils.extract_color_from_hllist('fg', {'hl_rvs'}, def_clr), bg_clr) + -- Can extract fallback reverse color + eq(utils.extract_color_from_hllist({'sp','fg'}, {'hl_rvs'}, def_clr), bg_clr) + -- clear highlights + vim.cmd('hi clear hl_std') + vim.cmd('hi clear hl_rvs') + vim.cmd('hi clear hl_ul') + vim.cmd('hi clear hl_ul_rvs') + end) + + it('can shrink list with holes', function() + local list_with_holes = { + '2', + '4', + '6', + nil, + '43', + nil, + '2', + '', + 'a', + '', + 'b', + ' ', + } + local list_without_holes = { '2', '4', '6', '43', '2', 'a', 'b', ' ' } + eq(utils.list_shrink(list_with_holes), list_without_holes) + end) +end) + +describe('Cterm generator', function() + local cterm = require('lualine.utils.color_utils') + + it('can convert rgb to cterm', function() + local colors = { ['#112233'] = 235, ['#7928ae'] = 97, ['#017bdc'] = 68 } + for rgb, ct in pairs(colors) do + eq(cterm.rgb2cterm(rgb), tostring(ct)) + end + end) +end) + +describe('Section generator', function() + local hl = require('lualine.highlight') + stub(hl, 'format_highlight') + hl.format_highlight.returns('%#lualine_c_normal#') + + local sec = require('lualine.utils.section') + it('can draw', function() + local opts = build_component_opts { section_separators = { left = '', right = '' } } + local section = { + require('lualine.components.special.function_component')(opts), + require('lualine.components.special.function_component')(opts), + } + eq('%#lualine_c_normal# test %#lualine_c_normal# test ', sec.draw_section(section, 'c', true)) + + hl.format_highlight:revert() + end) + + it('can remove separators from component with custom colors', function() + stub(hl, 'format_highlight') + stub(hl, 'get_lualine_hl') + hl.format_highlight.returns('%#lualine_MySection_normal#') + hl.get_lualine_hl.returns { fg = '#000000', bg = '#ffffff' } + + vim.g.actual_curwin = tostring(vim.api.nvim_get_current_win()) + local opts = build_component_opts { section_separators = { left = '', right = '' } } + local opts_colored = build_component_opts { color = 'MyColor' } + local opts_colored2 = build_component_opts { + color = { bg = '#223344' }, + section_separators = { left = '', right = '' }, + } + local opts_colored3 = build_component_opts { + color = { fg = '#223344' }, + section_separators = { left = '', right = '' }, + } + require('lualine.highlight').create_highlight_groups(require('lualine.themes.gruvbox')) + local section = { + require('lualine.components.special.function_component')(opts), + require('lualine.components.special.function_component')(opts_colored), + require('lualine.components.special.function_component')(opts), + } + local highlight_name2 = 'lualine_c_' .. section[2].options.component_name + -- Removes separator on string color + eq( + '%#lualine_MySection_normal# test %#' .. highlight_name2 .. '#' .. ' test %#lualine_MySection_normal# test ', + sec.draw_section(section, 'MySection') + ) + section[2] = require('lualine.components.special.function_component')(opts_colored2) + local highlight_name = '%#lualine_c_' .. section[2].options.component_name .. '_normal#' + -- Removes separator on color with bg + eq( + '%#lualine_MySection_normal# test ' .. highlight_name .. ' test %#lualine_MySection_normal# test ', + sec.draw_section(section, 'MySection') + ) + section[2] = require('lualine.components.special.function_component')(opts_colored3) + highlight_name2 = '%#lualine_c_' .. section[2].options.component_name .. '_normal#' + -- Doesn't remove separator on color without bg + eq( + '%#lualine_MySection_normal# test ' + .. highlight_name2 + .. ' test %#lualine_MySection_normal#%#lualine_MySection_normal# test ', + sec.draw_section(section, 'MySection') + ) + vim.g.actual_curwin = nil + + hl.format_highlight:revert() + hl.get_lualine_hl:revert() + end) +end) diff --git a/config/neovim/store/lazy-plugins/lualine.nvim/tests/statusline.lua b/config/neovim/store/lazy-plugins/lualine.nvim/tests/statusline.lua new file mode 100644 index 00000000..00e18559 --- /dev/null +++ b/config/neovim/store/lazy-plugins/lualine.nvim/tests/statusline.lua @@ -0,0 +1,229 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +--- ## Testing module for lualines statusline +--- +--- ###Uses: +--- +--- Create a new instance with status width 120 & for active statusline +--- like following. +--- +--- ``lua +--- local statusline = require('tests.statusline').new(120, 'active') +--- ``` +--- +--- To create a new instance with status width 80 & for inactive statusline use following. +--- +--- ``lua +--- local statusline = require('tests.statusline').new(120, 'inactive') +--- ``` +--- +--- Now setup the state you want to test. +--- To test you'll call `expect` method on statusline for example. +--- +--- To create a new instance with status width 80 & tabline +--- +--- ``lua +--- local statusline = require('tests.statusline').new(120, 'tabline') +--- ``` +--- +--- Now setup the state you want to test. +--- To test you'll call `expect` method on statusline for example. +--- +--- ``lua +--- statusline:expect([===[ +--- highlights = { +--- 1: lualine_c_inactive = { bg = "#3c3836", fg = "#a89984" } +--- } +--- |{1: [No Name] } +--- {1: } +--- {1: 0:1 }| +--- +---]===]) +--- ``` +--- +--- For more flexibility you can match a pattern in expect block. +--- ``lua +--- statusline:expect([===[ +--- highlights = { +--- 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } +--- 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836" } +--- 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" } +--- 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } +--- 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } +--- } +--- {MATCH:|{1: %d+ }} +--- {MATCH:{1: %d+ }} +--- {2:} +--- {MATCH:{3: %d+ }} +--- {4:} +--- {MATCH:{5:%s+}|} +--- +---]===]) +--- ``` +--- +--- An easy way to create an expect block is to call `snapshot` method +--- on statusline where you'll call expect and run the test. It will print +--- an expect block based on the state of statusline. You can copy it and +--- replace the snapshot call with the expect call. +--- +--- ``lua +--- statusline:snapshot() +--- ``` + +local helpers = require('tests.helpers') +local stub = require('luassert.stub') + +local M = {} + +local function eval_stl(stl_expr, width, eval_type) + local stl_buf, hl_list, stl_eval_res + stl_eval_res = vim.api.nvim_eval_statusline( + stl_expr, + { maxwidth = width, highlights = true, fillchar = ' ', use_tabline = (eval_type == 'tabline') } + ) + stl_buf, hl_list = stl_eval_res.str, stl_eval_res.highlights + + local hl_map = {} + + local buf = { 'highlights = {' } + local hl_id = 1 + for _, hl in ipairs(hl_list) do + local hl_name = hl.group + if not hl_map[hl_name] then + hl_map[hl_name] = require('lualine.utils.utils').extract_highlight_colors(hl_name) or {} + table.insert( + buf, + string.format(' %4d: %s = %s', hl_id, hl_name, vim.inspect(hl_map[hl_name], { newline = ' ', indent = '' })) + ) + hl_map[hl_name].id = hl_id + hl_id = hl_id + 1 + end + end + table.insert(buf, '}') + + local stl = {} + for i = 1, #hl_list do + local start, finish = hl_list[i].start, hl_list[i + 1] and hl_list[i + 1].start or #stl_buf + if start ~= finish then + table.insert( + stl, + string.format('{%d:%s}', hl_map[hl_list[i].group].id, vim.fn.strpart(stl_buf, start, finish - start)) + ) + end + end + table.insert(buf, '|' .. table.concat(stl, '\n') .. '|') + table.insert(buf, '') + return table.concat(buf, '\n') +end + +function M:expect_expr(expect, expr) + if expr == nil then + -- test if both are nil when running expect against nil + assert.are.same(expect, nil) + return + end + expect = helpers.dedent(expect) + local actual = eval_stl(expr, self.width, self.type) + local matched = true + local errmsg = {} + if expect ~= actual then + expect = vim.split(expect, '\n') + actual = vim.split(actual, '\n') + if expect[#expect] == '' then + expect[#expect] = nil + end + if actual[#actual] == '' then + actual[#actual] = nil + end + for i = 1, math.max(#expect, #actual) do + if expect[i] and actual[i] then + local match_pat = expect[i]:match('{MATCH:(.*)}') + if expect[i] == actual[i] or (match_pat and actual[i]:match(match_pat)) then + expect[i] = string.rep(' ', 2) .. expect[i] + actual[i] = string.rep(' ', 2) .. actual[i] + goto loop_end + end + end + matched = false + if expect[i] then + expect[i] = '*' .. string.rep(' ', 1) .. expect[i] + end + if actual[i] then + actual[i] = '*' .. string.rep(' ', 1) .. actual[i] + end + ::loop_end:: + end + end + if not matched then + table.insert(errmsg, 'Unexpected statusline') + table.insert(errmsg, 'Expected:') + table.insert(errmsg, table.concat(expect, '\n') .. '\n') + table.insert(errmsg, 'Actual:') + table.insert(errmsg, table.concat(actual, '\n')) + end + assert(matched, table.concat(errmsg, '\n')) +end + +function M:snapshot_expr(expr) + local type_map = { + active = 'statusline', + inactive = 'inactive_statusline', + tabline = 'tabline', + } + print((type_map[self.type] or 'statusline') .. ':expect([===[') + print(eval_stl(expr, self.width, self.type) .. ']===])') +end + +function M:snapshot() + local utils = require('lualine.utils.utils') + stub(utils, 'is_focused') + utils.is_focused.returns(self.type ~= 'inactive') + local expr + if self.type == 'inactive' then + expr = require('lualine').statusline(false) + elseif self.type == 'tabline' then + expr = require('lualine').tabline() + else + expr = require('lualine').statusline(true) + end + self:snapshot_expr(expr) + utils.is_focused:revert() +end + +function M:expect(result) + local utils = require('lualine.utils.utils') + stub(utils, 'is_focused') + utils.is_focused.returns(self.type ~= 'inactive') + local expr + if self.type == 'inactive' then + expr = require('lualine').statusline(false) + elseif self.type == 'tabline' then + expr = require('lualine').tabline() + else + expr = require('lualine').statusline(true) + end + self:expect_expr(result, expr) + utils.is_focused:revert() +end + +function M.new(_, width, eval_type) + if type(_) ~= 'table' then + eval_type = width + width = _ + end + local self = {} + self.width = width or 120 + self.type = eval_type + if self.type == nil then + self.type = 'active' + end + return setmetatable(self, { + __index = M, + __call = function(_, ...) + M.new(...) + end, + }) +end + +return M.new() diff --git a/config/neovim/store/lazy-plugins/luasnip/doc/luasnip.txt b/config/neovim/store/lazy-plugins/luasnip/doc/luasnip.txt new file mode 100644 index 00000000..e0ce8a2a --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/doc/luasnip.txt @@ -0,0 +1,3709 @@ +*luasnip.txt* For NVIM v0.8.0 Last change: 2024 May 24 + +============================================================================== +Table of Contents *luasnip-table-of-contents* + +1. Basics |luasnip-basics| + - Jump-Index |luasnip-basics-jump-index| + - Adding Snippets |luasnip-basics-adding-snippets| + - Snippet Insertion |luasnip-basics-snippet-insertion| +2. Node |luasnip-node| + - Api |luasnip-node-api| +3. Snippets |luasnip-snippets| + - Data |luasnip-snippets-data| +4. TextNode |luasnip-textnode| +5. InsertNode |luasnip-insertnode| +6. FunctionNode |luasnip-functionnode| +7. Node Reference |luasnip-node-reference| +8. ChoiceNode |luasnip-choicenode| +9. SnippetNode |luasnip-snippetnode| +10. IndentSnippetNode |luasnip-indentsnippetnode| +11. DynamicNode |luasnip-dynamicnode| +12. RestoreNode |luasnip-restorenode| +13. Key Indexer |luasnip-key-indexer| +14. Absolute Indexer |luasnip-absolute-indexer| +15. MultiSnippet |luasnip-multisnippet| +16. Extras |luasnip-extras| + - Lambda |luasnip-extras-lambda| + - Match |luasnip-extras-match| + - Repeat |luasnip-extras-repeat| + - Partial |luasnip-extras-partial| + - Nonempty |luasnip-extras-nonempty| + - Dynamic Lambda |luasnip-extras-dynamic-lambda| + - FMT |luasnip-extras-fmt| + - Conditions |luasnip-extras-conditions| + - On The Fly-Snippets |luasnip-extras-on-the-fly-snippets| + - select_choice |luasnip-extras-select_choice| + - Filetype-Functions |luasnip-extras-filetype-functions| + - Postfix-Snippet |luasnip-extras-postfix-snippet| + - Treesitter-Postfix-Snippet |luasnip-extras-treesitter-postfix-snippet| + - Snippet List |luasnip-extras-snippet-list| + - Snippet Location |luasnip-extras-snippet-location| +17. Extend Decorator |luasnip-extend-decorator| +18. LSP-Snippets |luasnip-lsp-snippets| + - Snipmate Parser |luasnip-lsp-snippets-snipmate-parser| + - Transformations |luasnip-lsp-snippets-transformations| +19. Variables |luasnip-variables| + - Environment Namespaces |luasnip-variables-environment-namespaces| + - LSP-Variables |luasnip-variables-lsp-variables| +20. Loaders |luasnip-loaders| + - Snippet-specific filetypes |luasnip-loaders-snippet-specific-filetypes| + - VS-Code |luasnip-loaders-vs-code| + - SNIPMATE |luasnip-loaders-snipmate| + - Lua |luasnip-loaders-lua| + - edit_snippets |luasnip-loaders-edit_snippets| +21. SnippetProxy |luasnip-snippetproxy| +22. ext_opts |luasnip-ext_opts| +23. Docstrings |luasnip-docstrings| +24. Docstring-Cache |luasnip-docstring-cache| +25. Events |luasnip-events| +26. Cleanup |luasnip-cleanup| +27. Logging |luasnip-logging| +28. Source |luasnip-source| +29. Config-Options |luasnip-config-options| +30. Troubleshooting |luasnip-troubleshooting| + - Adding Snippets |luasnip-troubleshooting-adding-snippets| +31. API |luasnip-api| +> + __ ____ + /\ \ /\ _`\ __ + \ \ \ __ __ __ \ \,\L\_\ ___ /\_\ _____ + \ \ \ __/\ \/\ \ /'__`\\/_\__ \ /' _ `\/\ \/\ '__`\ + \ \ \L\ \ \ \_\ \/\ \L\.\_/\ \L\ \/\ \/\ \ \ \ \ \L\ \ + \ \____/\ \____/\ \__/.\_\ `\____\ \_\ \_\ \_\ \ ,__/ + \/___/ \/___/ \/__/\/_/\/_____/\/_/\/_/\/_/\ \ \/ + \ \_\ + \/_/ +< + +LuaSnip is a snippet engine written entirely in Lua. It has some great features +like inserting text (`luasnip-function-node`) or nodes (`luasnip-dynamic-node`) +based on user input, parsing LSP syntax and switching nodes +(`luasnip-choice-node`). For basic setup like mappings and installing, check +the README. + +All code snippets in this help assume the following: + +>lua + local ls = require("luasnip") + local s = ls.snippet + local sn = ls.snippet_node + local isn = ls.indent_snippet_node + local t = ls.text_node + local i = ls.insert_node + local f = ls.function_node + local c = ls.choice_node + local d = ls.dynamic_node + local r = ls.restore_node + local events = require("luasnip.util.events") + local ai = require("luasnip.nodes.absolute_indexer") + local extras = require("luasnip.extras") + local l = extras.lambda + local rep = extras.rep + local p = extras.partial + local m = extras.match + local n = extras.nonempty + local dl = extras.dynamic_lambda + local fmt = require("luasnip.extras.fmt").fmt + local fmta = require("luasnip.extras.fmt").fmta + local conds = require("luasnip.extras.expand_conditions") + local postfix = require("luasnip.extras.postfix").postfix + local types = require("luasnip.util.types") + local parse = require("luasnip.util.parser").parse_snippet + local ms = ls.multi_snippet + local k = require("luasnip.nodes.key_indexer").new_key +< + +As noted in the |luasnip-loaders-lua|-section: + + + By default, the names from `luasnip.config.snip_env` + + will be used, but it’s possible to customize them by setting `snip_env` in + `setup`. +Furthermore, note that while this document assumes you have defined `ls` to be +`require("luasnip")`, it is **not** provided in the default set of variables. + + +============================================================================== +1. Basics *luasnip-basics* + +In LuaSnip, snippets are made up of `nodes`. These can contain either + +- static text (`textNode`) +- text that can be edited (`insertNode`) +- text that can be generated from the contents of other nodes (`functionNode`) +- other nodes + - `choiceNode`: allows choosing between two nodes (which might contain more + nodes) + - `restoreNode`: store and restore input to nodes +- or nodes that can be generated based on input (`dynamicNode`). + +Snippets are always created using the `s(trigger:string, +nodes:table)`-function. It is explained in more detail in |luasnip-snippets|, +but the gist is that it creates a snippet that contains the nodes specified in +`nodes`, which will be inserted into a buffer if the text before the cursor +matches `trigger` when `ls.expand` is called. + + +JUMP-INDEX *luasnip-basics-jump-index* + +Nodes that can be jumped to (`insertNode`, `choiceNode`, `dynamicNode`, +`restoreNode`, `snippetNode`) all require a "jump-index" so luasnip knows the +order in which these nodes are supposed to be visited ("jumped to"). + +>lua + s("trig", { + i(1), t"text", i(2), t"text again", i(3) + }) +< + +These indices don’t "run" through the entire snippet, like they do in +textmate-snippets (`"$1 ${2: $3 $4}"`), they restart at 1 in each nested +snippetNode: + +>lua + s("trig", { + i(1), t" ", sn(2, { + t" ", i(1), t" ", i(2) + }) + }) +< + +(roughly equivalent to the given textmate-snippet). + + +ADDING SNIPPETS *luasnip-basics-adding-snippets* + +The snippets for a given filetype have to be added to luasnip via +`ls.add_snippets(filetype, snippets)`. Snippets that should be accessible +globally (in all filetypes) have to be added to the special filetype `all`. + +>lua + ls.add_snippets("all", { + s("ternary", { + -- equivalent to "${1:cond} ? ${2:then} : ${3:else}" + i(1, "cond"), t(" ? "), i(2, "then"), t(" : "), i(3, "else") + }) + }) +< + +It is possible to make snippets from one filetype available to another using +`ls.filetype_extend`, more info on that in the section |luasnip-api|. + + +SNIPPET INSERTION *luasnip-basics-snippet-insertion* + +When a new snippet is expanded, it can be connected with the snippets that have +already been expanded in the buffer in various ways. First of all, Luasnip +distinguishes between root-snippets and child-snippets. The latter are nested +inside other snippets, so when jumping through a snippet, one may also traverse +the child-snippets expanded inside it, more or less as if the child just +contains more nodes of the parent. Root-snippets are of course characterised by +not being child-snippets. When expanding a new snippet, it becomes a child of +the snippet whose region it is expanded inside, and a root if it is not inside +any snippet’s region. If it is inside another snippet, the specific node it +is inside is determined, and the snippet then nested inside that node. + +- If that node is interactive (for example, an `insertNode`), the new snippet + will be traversed when the node is visited, as long as the + configuration-option `link_children` is enabled. If it is not enabled, it is + possible to jump from the snippet to the node, but not the other way around. +- If that node is not interactive, the snippet will be linked to the currently + active node, also such that it will not be jumped to again once it is left. + This is to prevent jumping large distances across the buffer as much as + possible. There may still be one large jump from the snippet back to the + current node it is nested inside, but that seems hard to avoid. + Thus, one should design snippets such that the regions where other snippets + may be expanded are inside `insertNodes`. + +If the snippet is not a child, but a root, it can be linked up with the roots +immediately adjacent to it by enabling `link_roots` in `setup`. Since by +default only one root is remembered, one should also set `keep_roots` if +`link_roots` is enabled. The two are separate options, since roots that are not +linked can still be reached by `ls.activate_node()`. This setup (remember +roots, but don’t jump to them) is useful for a super-tab like mapping +(`` and jump on the same key), where one would like to still enter +previous roots. Since there would almost always be more jumps if the roots are +linked, regular `` would not work almost all the time, and thus +`link_roots` has to stay disabled. + + +============================================================================== +2. Node *luasnip-node* + +Every node accepts, as its last parameter, an optional table of arguments. +There are some common ones (which are listed here), and some that only apply to +some nodes (`user_args` for function/dynamicNode). These `opts` are only +mentioned if they accept options that are not common to all nodes. + +Common opts: + +- `node_ext_opts` and `merge_node_ext_opts`: Control `ext_opts` (most likely + highlighting) of the node. Described in detail in |luasnip-ext_opts| +- `key`: The node can be reffered to by this key. Useful for either |luasnip-key-indexer| or for finding the node at runtime (See + |luasnip-snippets-api|), for example inside a `dynamicNode`. The keys + do not have to be unique across the entire lifetime of the snippet, but at any + point in time, the snippet may contain each key only once. This means it is + fine to return a keyed node from a `dynamicNode`, because even if it will be + generated multiple times, those will not be valid at the same time. +- `node_callbacks`: Define event-callbacks for this node (see + |luasnip-events|). + Accepts a table that maps an event, e.g. `events.enter` to the callback + (essentially the same as `callbacks` passed to `s`, only that there is no + first mapping from jump-index to the table of callbacks). + + +API *luasnip-node-api* + +- `get_jump_index()`: this method returns the jump-index of a node. If a node + doesn’t have a jump-index, this method returns `nil` instead. +- `get_buf_position(opts) -> {from_position, to_position}`: + Determines the range of the buffer occupied by this node. `from`- and + `to_position` are `row,column`-tuples, `0,0`-indexed (first line is 0, first + column is 0) and end-inclusive (see |api-indexing|, this is extmarks + indexing). + - `opts`: `table|nil`, options, valid keys are: + - `raw`: `bool`, default `true`. This can be used to switch between + byte-columns (`raw=true`) and visual columns (`raw=false`). This makes a + difference if the line contains characters represented by multiple bytes + in UTF, for example `ÿ`. + + +============================================================================== +3. Snippets *luasnip-snippets* + +The most direct way to define snippets is `s`: + +>lua + s({trig="trigger"}, {}) +< + +(This snippet is useless beyond serving as a minimal example) + +`s(context, nodes, opts) -> snippet` + +- `context`: Either table or a string. Passing a string is equivalent to passing + >lua + { + trig = context + } + < + The following keys are valid: + - `trig`: string, the trigger of the snippet. If the text in front of (to the + left of) the cursor when `ls.expand()` is called matches it, the snippet will + be expanded. By default, "matches" means the text in front of the cursor + matches the trigger exactly, this behaviour can be modified through + `trigEngine` + - `name`: string, can be used by e.g. `nvim-compe` to identify the snippet. + - `desc` (or `dscr`): string, description of the snippet, -separated or table for + multiple lines. + - `wordTrig`: boolean, if true, the snippet is only expanded if the word + (`[%w_]+`) before the cursor matches the trigger entirely. True by default. + - `regTrig`: boolean, whether the trigger should be interpreted as a lua pattern. + False by default. Consider setting `trigEngine` to `"pattern"` instead, it is + more expressive, and in line with other settings. + - `trigEngine`: (function|string), determines how `trig` is interpreted, and what + it means for it to "match" the text in front of the cursor. This behaviour can + be completely customized by passing a function, but the predefined ones, which + are accessible by passing their identifier, should suffice in most cases: + - `"plain"`: the default-behaviour, the trigger has to match the text before + the cursor exactly. + - `"pattern"`: the trigger is interpreted as a lua-pattern, and is a match if + `trig .. "$"` matches the line up to the cursor. Capture-groups will be + accessible as `snippet.captures`. + - `"ecma"`: the trigger is interpreted as an ECMAscript-regex, and is a + match if `trig .. "$"` matches the line up to the cursor. Capture-groups + will be accessible as `snippet.captures`. + This `trigEngine` requires `jsregexp` (see + |luasnip-lsp-snippets-transformations|) to be installed, if it + is not, this engine will behave like `"plain"`. + - `"vim"`: the trigger is interpreted as a vim-regex, and is a match if + `trig .. "$"` matches the line up to the cursor. As with the other + regex/pattern-engines, captures will be available as `snippet.captures`, + but there is one caveat: the matching is done using `matchlist`, so for + now empty-string submatches will be interpreted as unmatched, and the + corresponding `snippet.capture[i]` will be `nil` (this will most likely + change, don’t rely on this behavior). + Besides these predefined engines, it is also possible to create new ones: + Instead of a string, pass a function which satisfies `trigEngine(trigger, opts) + -> (matcher(line_to_cursor, trigger) -> whole_match, captures)` (ie. the + function receives `trig` and `trigEngineOpts` can, for example, precompile a + regex, and then returns a function responsible for determining whether the + current cursor-position (represented by the line up to the cursor) matches the + trigger (it is passed again here so engines which don’t do any + trigger-specific work (like compilation) can just return a static `matcher`), + and what the capture-groups are). The `lua`-engine, for example, can be + implemented like this: + >lua + local function matcher(line_to_cursor, trigger) + -- look for match which ends at the cursor. + -- put all results into a list, there might be many capture-groups. + local find_res = { line_to_cursor:find(trigger .. "$") } + + if #find_res > 0 then + -- if there is a match, determine matching string, and the + -- capture-groups. + local captures = {} + -- find_res[1] is `from`, find_res[2] is `to` (which we already know + -- anyway). + local from = find_res[1] + local match = line_to_cursor:sub(from, #line_to_cursor) + -- collect capture-groups. + for i = 3, #find_res do + captures[i - 2] = find_res[i] + end + return match, captures + else + return nil + end + end + + local function engine(trigger) + -- don't do any special work here, can't precompile lua-pattern. + return matcher + end + < + The predefined engines are defined in `trig_engines.lua` + , + read it for more examples. + - `trigEngineOpts`: `table`, options for the used trigEngine. The + valid options are: + - `max_len`: number, upper bound on the length of the trigger. + If this is set, the `line_to_cursor` will be truncated (from the cursor of + course) to `max_len` characters before performing the match. + This is implemented because feeding long `line_to_cursor` into eg. the + pattern-trigEngine will hurt performance quite a bit (see issue + Luasnip#1103). + This option is implemented for all `trigEngines`. + - `docstring`: string, textual representation of the snippet, specified like + `desc`. Overrides docstrings loaded from json. + - `docTrig`: string, used as `line_to_cursor` during docstring-generation. This + might be relevant if the snippet relies on specific values in the + capture-groups (for example, numbers, which won’t work with the default + `$CAPTURESN` used during docstring-generation) + - `hidden`: boolean, hint for completion-engines. If set, the snippet should not + show up when querying snippets. + - `priority`: positive number, Priority of the snippet, 1000 by default. Snippets + with high priority will be matched to a trigger before those with a lower one. + The priority for multiple snippets can also be set in `add_snippets`. + - `snippetType`: string, should be either `snippet` or `autosnippet` (ATTENTION: + singular form is used), decides whether this snippet has to be triggered by + `ls.expand()` or whether is triggered automatically (don’t forget to set + `ls.config.setup({ enable_autosnippets = true })` if you want to use this + feature). If unset it depends on how the snippet is added of which type the + snippet will be. + - `resolveExpandParams`: `fn(snippet, line_to_cursor, matched_trigger, captures) + -> table|nil`, where + - `snippet`: `Snippet`, the expanding snippet object + - `line_to_cursor`: `string`, the line up to the cursor. + - `matched_trigger`: `string`, the fully matched trigger (can be retrieved + from `line_to_cursor`, but we already have that info here :D) + - `captures`: `captures` as returned by `trigEngine`. + This function will be evaluated in `Snippet:matches()` to decide whether the + snippet can be expanded or not. Returns a table if the snippet can be expanded, + `nil` if can not. The returned table can contain any of these fields: + - `trigger`: `string`, the fully matched trigger. + - `captures`: `table`, this list could update the capture-groups from + parameter in snippet expansion. + Both `trigger` and `captures` can override the values returned via + `trigEngine`. + - `clear_region`: `{ "from": {, }, "to": {, } }`, + both (0, 0)-indexed, the region where text has to be cleared before + inserting the snippet. + - `env_override`: `map string->(string[]|string)`, override or extend + the snippet’s environment (`snip.env`) + If any of these is `nil`, the default is used (`trigger` and `captures` as + returned by `trigEngine`, `clear_region` such that exactly the trigger is + deleted, no overridden environment-variables). + A good example for the usage of `resolveExpandParams` can be found in the + implementation of `postfix` + . + - `condition`: `fn(line_to_cursor, matched_trigger, captures) -> bool`, where + - `line_to_cursor`: `string`, the line up to the cursor. + - `matched_trigger`: `string`, the fully matched trigger (can be retrieved + from `line_to_cursor`, but we already have that info here :D) + - `captures`: if the trigger is pattern, this list contains the + capture-groups. Again, could be computed from `line_to_cursor`, but we + already did so. + - `show_condition`: `f(line_to_cursor) -> bool`. + - `line_to_cursor`: `string`, the line up to the cursor. + This function is (should be) evaluated by completion engines, indicating + whether the snippet should be included in current completion candidates. + Defaults to a function returning `true`. This is different from `condition` + because `condition` is evaluated by LuaSnip on snippet expansion (and thus has + access to the matched trigger and captures), while `show_condition` is (should + be) evaluated by the completion engines when scanning for available snippet + candidates. + - `filetype`: `string`, the filetype of the snippet. This overrides the filetype + the snippet is added (via `add_snippet`) as. +- `nodes`: A single node or a list of nodes. The nodes that make up the snippet. +- `opts`: A table with the following valid keys: + - `callbacks`: Contains functions that are called upon entering/leaving a node of + this snippet. For example: to print text upon entering the _second_ node of a + snippet, `callbacks` should be set as follows: + >lua + { + -- position of the node, not the jump-index!! + -- s("trig", {t"first node", t"second node", i(1, "third node")}). + [2] = { + [events.enter] = function(node, _event_args) print("2!") end + } + } + < + To register a callback for the snippets’ own events, the key `[-1]` may be + used. More info on events in |luasnip-events| + - `child_ext_opts`, `merge_child_ext_opts`: Control `ext_opts` applied to the + children of this snippet. More info on those in the |luasnip-ext_opts|-section. + +The `opts`-table, as described here, can also be passed to e.g. `snippetNode` +and `indentSnippetNode`. It is also possible to set `condition` and +`show_condition` (described in the documentation of the `context`-table) from +`opts`. They should, however, not be set from both. + + +DATA *luasnip-snippets-data* + +Snippets contain some interesting tables during runtime: + +- `snippet.env`: Contains variables used in the LSP-protocol, for example + `TM_CURRENT_LINE` or `TM_FILENAME`. It’s possible to add customized variables + here too, check |luasnip-variables-environment-namespaces| +- `snippet.captures`: If the snippet was triggered by a pattern (`regTrig`), and + the pattern contained capture-groups, they can be retrieved here. +- `snippet.trigger`: The string that triggered this snippet. Again, only + interesting if the snippet was triggered through `regTrig`, for getting the + full match. + +These variables/tables primarily come in handy in `dynamic/functionNodes`, +where the snippet can be accessed through the immediate parent +(`parent.snippet`), which is passed to the function. (in most cases `parent == +parent.snippet`, but the `parent` of the dynamicNode is not always the +surrounding snippet, it could be a `snippetNode`). + +## Api + +- `invalidate()`: call this method to effectively remove the snippet. The + snippet will no longer be able to expand via `expand` or `expand_auto`. It + will also be hidden from lists (at least if the plugin creating the list + respects the `hidden`-key), but it might be necessary to call + `ls.refresh_notify(ft)` after invalidating snippets. +- `get_keyed_node(key)`: Returns the currently visible node associated with + `key`. + + +============================================================================== +4. TextNode *luasnip-textnode* + +The most simple kind of node; just text. + +>lua + s("trigger", { t("Wow! Text!") }) +< + +This snippet expands to + +> + Wow! Text!⎵ +< + +where ⎵ is the cursor. + +Multiline strings can be defined by passing a table of lines rather than a +string: + +>lua + s("trigger", { + t({"Wow! Text!", "And another line."}) + }) +< + +`t(text, node_opts)`: + +- `text`: `string` or `string[]` +- `node_opts`: `table`, see |luasnip-node| + + +============================================================================== +5. InsertNode *luasnip-insertnode* + +These Nodes contain editable text and can be jumped to- and from (e.g. +traditional placeholders and tabstops, like `$1` in textmate-snippets). + +The functionality is best demonstrated with an example: + +>lua + s("trigger", { + t({"After expanding, the cursor is here ->"}), i(1), + t({"", "After jumping forward once, cursor is here ->"}), i(2), + t({"", "After jumping once more, the snippet is exited there ->"}), i(0), + }) +< + +The Insert Nodes are visited in order `1,2,3,..,n,0`. (The jump-index 0 also +_has_ to belong to an `insertNode`!) So the order of InsertNode-jumps is as +follows: + +1. After expansion, the cursor is at InsertNode 1, +2. after jumping forward once at InsertNode 2, +3. and after jumping forward again at InsertNode 0. + +If no 0-th InsertNode is found in a snippet, one is automatically inserted +after all other nodes. + +The jump-order doesn’t have to follow the "textual" order of the nodes: + +>lua + s("trigger", { + t({"After jumping forward once, cursor is here ->"}), i(2), + t({"", "After expanding, the cursor is here ->"}), i(1), + t({"", "After jumping once more, the snippet is exited there ->"}), i(0), + }) +< + +The above snippet will behave as follows: + +1. After expansion, we will be at InsertNode 1. +2. After jumping forward, we will be at InsertNode 2. +3. After jumping forward again, we will be at InsertNode 0. + +An **important** (because here Luasnip differs from other snippet engines) +detail is that the jump-indices restart at 1 in nested snippets: + +>lua + s("trigger", { + i(1, "First jump"), + t(" :: "), + sn(2, { + i(1, "Second jump"), + t" : ", + i(2, "Third jump") + }) + }) +< + +as opposed to e.g. the textmate syntax, where tabstops are snippet-global: + +>snippet + ${1:First jump} :: ${2: ${3:Third jump} : ${4:Fourth jump}} +< + +(this is not exactly the same snippet of course, but as close as possible) (the +restart-rule only applies when defining snippets in lua, the above +textmate-snippet will expand correctly when parsed). + +`i(jump_index, text, node_opts)` + +- `jump_index`: `number`, this determines when this node will be jumped to (see + |luasnip-basics-jump-index|). +- `text`: `string|string[]`, a single string for just one line, a list with >1 + entries for multiple lines. + This text will be SELECTed when the `insertNode` is jumped into. +- `node_opts`: `table`, described in |luasnip-node| + +If the `jump_index` is `0`, replacing its’ `text` will leave it outside the +`insertNode` (for reasons, check out Luasnip#110). + + +============================================================================== +6. FunctionNode *luasnip-functionnode* + +Function Nodes insert text based on the content of other nodes using a +user-defined function: + +>lua + local function fn( + args, -- text from i(2) in this example i.e. { { "456" } } + parent, -- parent snippet or parent node + user_args -- user_args from opts.user_args + ) + return '[' .. args[1][1] .. user_args .. ']' + end + + s("trig", { + i(1), t '<-i(1) ', + f(fn, -- callback (args, parent, user_args) -> string + {2}, -- node indice(s) whose text is passed to fn, i.e. i(2) + { user_args = { "user_args_value" }} -- opts + ), + t ' i(2)->', i(2), t '<-i(2) i(0)->', i(0) + }) +< + +`f(fn, argnode_references, node_opts)`: - `fn`: `function(argnode_text, parent, +user_args1,...,user_argsn) -> text` - `argnode_text`: `string[][]`, the text +currently contained in the argnodes (e.g. `{{line1}, {line1, line2}}`). The +snippet indent will be removed from all lines following the first. + +- `parent`: The immediate parent of the `functionNode`. It is included here as it + allows easy access to some information that could be useful in functionNodes + (see |luasnip-snippets-data| for some examples). Many snippets access the + surrounding snippet just as `parent`, but if the `functionNode` is nested + within a `snippetNode`, the immediate parent is a `snippetNode`, not the + surrounding snippet (only the surrounding snippet contains data like `env` or + `captures`). +- `user_args`: The `user_args` passed in `opts`. Note that there may be multiple + user_args (e.g. `user_args1, ..., user_argsn`). + +`fn` shall return a string, which will be inserted as is, or a table of strings +for multiline strings, where all lines following the first will be prefixed +with the snippets’ indentation. + +- `argnode_references`: `node_reference[]|node_refernce|nil`. Either no, a + single, or multiple |luasnip-node-reference|s. Changing any of these will + trigger a re-evaluation of `fn`, and insertion of the updated text. If no node + reference is passed, the `functionNode` is evaluated once upon expansion. +- `node_opts`: `table`, see |luasnip-node|. One additional key is supported: + - `user_args`: `any[]`, these will be passed to `fn` as `user_arg1`-`user_argn`. + These make it easier to reuse similar functions, for example a functionNode + that wraps some text in different delimiters (`()`, `[]`, …). + >lua + local function reused_func(_,_, user_arg1) + return user_arg1 + end + + s("trig", { + f(reused_func, {}, { + user_args = {"text"} + }), + f(reused_func, {}, { + user_args = {"different text"} + }), + }) + < + +**Examples**: + +- Use captures from the regex trigger using a functionNode: + >lua + s({trig = "b(%d)", regTrig = true}, + f(function(args, snip) return + "Captured Text: " .. snip.captures[1] .. "." end, {}) + ) + < +- `argnodes_text` during function evaluation: + >lua + s("trig", { + i(1, "text_of_first"), + i(2, {"first_line_of_second", "second_line_of_second"}), + f(function(args, snip) + --here + -- order is 2,1, not 1,2!! + end, {2, 1} )}) + < + At `--here`, `args` would look as follows (provided no text was changed after + expansion): + >lua + args = { + {"first_line_of_second", "second_line_of_second"}, + {"text_of_first"} + } + < +- |luasnip-absolute-indexer|: + >lua + s("trig", { + i(1, "text_of_first"), + i(2, {"first_line_of_second", "second_line_of_second"}), + f(function(args, snip) + -- just concat first lines of both. + return args[1][1] .. args[2][1] + end, {ai[2], ai[1]} )}) + < + +If the function only performs simple operations on text, consider using the +`lambda` from `luasnip.extras` (See |luasnip-extras-lambda|) + + +============================================================================== +7. Node Reference *luasnip-node-reference* + +Node references are used to refer to other nodes in various parts of +luasnip’s API. For example, argnodes in functionNode, dynamicNode or lambda +are node references. These references can be either of: + +- `number`: the jump-index of the node. + This will be resolved relative to the parent of the node this is passed to. + (So, only nodes with the same parent can be referenced. This is very easy to + grasp, but also limiting) +- `key_indexer`: the key of the node, if it is present. This will come in + handy if the node that is being referred to is not in the same + snippet/snippetNode as the one the node reference is passed to. + Also, it is the proper way to refer to a non-interactive node (a + functionNode, for example) +- `absolute_indexer`: the absolute position of the node. Just like + `key_indexer`, it allows addressing non-sibling nodes, but is a bit more + awkward to handle since a path from root to node has to be determined, + whereas `key_indexer` just needs the key to match. + Due to this, `key_indexer` should be generally preferred. + (More information in |luasnip-absolute-indexer|). +- `node`: just the node. Usage of this is discouraged since it can lead to + subtle errors (for example, if the node passed here is captured in a closure + and therefore not copied with the remaining tables in the snippet; there’s a + big comment about just this in commit 8bfbd61). + + +============================================================================== +8. ChoiceNode *luasnip-choicenode* + +ChoiceNodes allow choosing between multiple nodes. + +>lua + s("trig", c(1, { + t("Ugh boring, a text node"), + i(nil, "At least I can edit something now..."), + f(function(args) return "Still only counts as text!!" end, {}) + })) +< + +`c(jump_index, choices, node_opts)` + +- `jump_index`: `number`, since choiceNodes can be jumped to, they need a + jump-index (Info in |luasnip-basics-jump-index|). +- `choices`: `node[]|node`, the choices. The first will be initialliy active. + A list of nodes will be turned into a `snippetNode`. +- `node_opts`: `table`. `choiceNode` supports the keys common to all nodes + described in |luasnip-node|, and one additional key: + - `restore_cursor`: `false` by default. If it is set, and the node that was being + edited also appears in the switched to choice (can be the case if a + `restoreNode` is present in both choice) the cursor is restored relative to + that node. The default is `false` as enabling might lead to decreased + performance. It’s possible to override the default by wrapping the + `choiceNode` constructor in another function that sets `opts.restore_cursor` to + `true` and then using that to construct `choiceNode`s: + >lua + local function restore_cursor_choice(pos, choices, opts) + if opts then + opts.restore_cursor = true + else + opts = {restore_cursor = true} + end + return c(pos, choices, opts) + end + < + +Jumpable nodes that normally expect an index as their first parameter don’t +need one inside a choiceNode; their jump-index is the same as the +choiceNodes’. + +As it is only possible (for now) to change choices from within the choiceNode, +make sure that all of the choices have some place for the cursor to stop at! + +This means that in `sn(nil, {...nodes...})` `nodes` has to contain e.g. an +`i(1)`, otherwise luasnip will just "jump through" the nodes, making it +impossible to change the choice. + +>lua + c(1, { + t"some text", -- textNodes are just stopped at. + i(nil, "some text"), -- likewise. + sn(nil, {t"some text"}) -- this will not work! + sn(nil, {i(1), t"some text"}) -- this will. + }) +< + +The active choice for a choiceNode can be changed by either calling one of +`ls.change_choice(1)` (forwards) or `ls.change_choice(-1)` (backwards), or by +calling `ls.set_choice(choice_indx)`. + +One way to easily interact with choiceNodes is binding `change_choice(1/-1)` to +keys: + +>lua + -- set keybinds for both INSERT and VISUAL. + vim.api.nvim_set_keymap("i", "", "luasnip-next-choice", {}) + vim.api.nvim_set_keymap("s", "", "luasnip-next-choice", {}) + vim.api.nvim_set_keymap("i", "", "luasnip-prev-choice", {}) + vim.api.nvim_set_keymap("s", "", "luasnip-prev-choice", {}) +< + +Apart from this, there is also a picker (see |luasnip-select_choice| where no +cycling is necessary and any choice can be selected right away, via +`vim.ui.select`. + + +============================================================================== +9. SnippetNode *luasnip-snippetnode* + +SnippetNodes directly insert their contents into the surrounding snippet. This +is useful for `choiceNode`s, which only accept one child, or `dynamicNode`s, +where nodes are created at runtime and inserted as a `snippetNode`. + +Their syntax is similar to `s`, however, where snippets require a table +specifying when to expand, `snippetNode`s, similar to `insertNode`s, expect a +jump-index. + +>lua + s("trig", sn(1, { + t("basically just text "), + i(1, "And an insertNode.") + })) +< + +`sn(jump_index, nodes, node_opts)` + +- `jump_index`: `number`, the usual |luasnip-jump-index|. +- `nodes`: `node[]|node`, just like for `s`. + Note that `snippetNode`s don’t accept an `i(0)`, so the jump-indices of the nodes + inside them have to be in `1,2,...,n`. +- `node_opts`: `table`: again, the keys common to all nodes (documented in + |luasnip-node|) are supported, but also + - `callbacks`, + - `child_ext_opts` and + - `merge_child_ext_opts`, + which are further explained in |luasnip-snippets|. + + +============================================================================== +10. IndentSnippetNode *luasnip-indentsnippetnode* + +By default, all nodes are indented at least as deep as the trigger. With these +nodes it’s possible to override that behaviour: + +>lua + s("isn", { + isn(1, { + t({"This is indented as deep as the trigger", + "and this is at the beginning of the next line"}) + }, "") + }) +< + +(Note the empty string passed to isn). + +Indent is only applied after linebreaks, so it’s not possible to remove +indent on the line where the snippet was triggered using `ISN` (That is +possible via regex triggers where the entire line before the trigger is +matched). + +Another nice use case for `ISN` is inserting text, e.g. `//` or some other +comment string before the nodes of the snippet: + +>lua + s("isn2", { + isn(1, t({"//This is", "A multiline", "comment"}), "$PARENT_INDENT//") + }) +< + +Here the `//` before `This is` is important, once again, because indent is only +applied after linebreaks. + +To enable such usage, `$PARENT_INDENT` in the indentstring is replaced by the +parent’s indent. + +`isn(jump_index, nodes, indentstring, node_opts)` + +All of these parameters except `indentstring` are exactly the same as in +|luasnip-snippetnode|. + +- `indentstring`: `string`, will be used to indent the nodes inside this + `snippetNode`. + All occurences of `"$PARENT_INDENT"` are replaced with the actual indent of + the parent. + + +============================================================================== +11. DynamicNode *luasnip-dynamicnode* + +Very similar to functionNode, but returns a snippetNode instead of just text, +which makes them very powerful as parts of the snippet can be changed based on +user input. + +`d(jump_index, function, node-references, opts)`: + +- `jump_index`: `number`, just like all jumpable nodes, its’ position in the + jump-list (|luasnip-basics-jump-index|). +- `function`: `fn(args, parent, old_state, user_args) -> snippetNode` This + function is called when the argnodes’ text changes. It should generate and + return (wrapped inside a `snippetNode`) nodes, which will be inserted at the + dynamicNode’s place. `args`, `parent` and `user_args` are also explained in + |luasnip-functionnode| + - `args`: `table of text` (`{{"node1line1", "node1line2"}, {"node2line1"}}`) + from nodes the `dynamicNode` depends on. + - `parent`: the immediate parent of the `dynamicNode`. + - `old_state`: a user-defined table. This table may contain anything; its + intended usage is to preserve information from the previously generated + `snippetNode`. If the `dynamicNode` depends on other nodes, it may be + reconstructed, which means all user input (text inserted in `insertNodes`, + changed choices) to the previous `dynamicNode` is lost. + The `old_state` table must be stored in `snippetNode` returned by + the function (`snippetNode.old_state`). + The second example below illustrates the usage of `old_state`. + - `user_args`: passed through from `dynamicNode`-opts; may have more than one + argument. +- `node_references`: `node_reference[]|node_references|nil`, + |luasnip-node-references| to the nodes the dynamicNode depends on: if any of + these trigger an update (for example, if the text inside them changes), the + `dynamicNode`s’ function will be executed, and the result inserted at the + `dynamicNode`s place. (`dynamicNode` behaves exactly the same as `functionNode` + in this regard). +- `opts`: In addition to the common |luasnip-node|-keys, there is, again, + - `user_args`, which is described in |luasnip-functionnode|. + +**Examples**: + +This `dynamicNode` inserts an `insertNode` which copies the text inside the +first `insertNode`. + +>lua + s("trig", { + t"text: ", i(1), t{"", "copy: "}, + d(2, function(args) + -- the returned snippetNode doesn't need a position; it's inserted + -- "inside" the dynamicNode. + return sn(nil, { + -- jump-indices are local to each snippetNode, so restart at 1. + i(1, args[1]) + }) + end, + {1}) + }) +< + +This snippet makes use of `old_state` to count the number of updates. + +To store/restore values generated by the `dynamicNode` or entered into +`insert/choiceNode`, consider using the shortly-introduced `restoreNode` +instead of `old_state`. + +>lua + local function count(_, _, old_state) + old_state = old_state or { + updates = 0 + } + + old_state.updates = old_state.updates + 1 + + local snip = sn(nil, { + t(tostring(old_state.updates)) + }) + + snip.old_state = old_state + return snip + end + + ls.add_snippets("all", + s("trig", { + i(1, "change to update"), + d(2, count, {1}) + }) + ) +< + +As with `functionNode`, `user_args` can be used to reuse similar `dynamicNode`- +functions. + + +============================================================================== +12. RestoreNode *luasnip-restorenode* + +This node can store and restore a snippetNode as is. This includes changed +choices and changed text. Its’ usage is best demonstrated by an example: + +>lua + s("paren_change", { + c(1, { + sn(nil, { t("("), r(1, "user_text"), t(")") }), + sn(nil, { t("["), r(1, "user_text"), t("]") }), + sn(nil, { t("{"), r(1, "user_text"), t("}") }), + }), + }, { + stored = { + -- key passed to restoreNodes. + ["user_text"] = i(1, "default_text") + } + }) +< + +Here the text entered into `user_text` is preserved upon changing choice. + +`r(jump_index, key, nodes, node_opts)`: + +- `jump_index`, when to jump to this node. +- `key`, `string`: `restoreNode`s with the same key share their content. +- `nodes`, `node[]|node`: the content of the `restoreNode`. + Can either be a single node, or a table of nodes (both of which will be + wrapped inside a `snippetNode`, except if the single node already is a + `snippetNode`). + The content for a given key may be defined multiple times, but if the + contents differ, it’s undefined which will actually be used. + If a key’s content is defined in a `dynamicNode`, it will not be initially + used for `restoreNodes` outside that `dynamicNode`. A way around this + limitation is defining the content in the `restoreNode` outside the + `dynamicNode`. + +The content for a key may also be defined in the `opts`-parameter of the +snippet-constructor, as seen in the example above. The `stored`-table accepts +the same values as the `nodes`-parameter passed to `r`. If no content is +defined for a key, it defaults to the empty `insertNode`. + +An important-to-know limitation of `restoreNode` is that, for a given key, only +one may be visible at a time. See this issue + for details. + +The `restoreNode` is especially useful for storing input across updates of a +`dynamicNode`. Consider this: + +>lua + local function simple_restore(args, _) + return sn(nil, {i(1, args[1]), i(2, "user_text")}) + end + + s("rest", { + i(1, "preset"), t{"",""}, + d(2, simple_restore, 1) + }) +< + +Every time the `i(1)` in the outer snippet is changed, the text inside the +`dynamicNode` is reset to `"user_text"`. This can be prevented by using a +`restoreNode`: + +>lua + local function simple_restore(args, _) + return sn(nil, {i(1, args[1]), r(2, "dyn", i(nil, "user_text"))}) + end + + s("rest", { + i(1, "preset"), t{"",""}, + d(2, simple_restore, 1) + }) +< + +Now the entered text is stored. + +`restoreNode`s indent is not influenced by `indentSnippetNodes` right now. If +that really bothers you feel free to open an issue. + + +============================================================================== +13. Key Indexer *luasnip-key-indexer* + +A very flexible way of referencing nodes (|luasnip-node-reference|). While the +straightforward way of addressing nodes via their |luasnip-jump-index| suffices +in most cases, a `dynamic/functionNode` can only depend on nodes in the same +snippet(Node), its siblings (since the index is interpreted as relative to +their parent). Accessing a node with a different parent is thus not possible. +Secondly, and less relevant, only nodes that actually have a jump-index can be +referred to (a `functionNode`, for example, cannot be depended on). Both of +these restrictions are lifted with `key_indexer`: It allows addressing nodes by +their key, which can be set when the node is constructed, and is wholly +independent of the nodes’ position in the snippet, thus enabling descriptive +labeling. + +The following snippets demonstrate the issue and the solution by using +`key_indexer`: + +First, the addressed problem of referring to nodes outside the `functionNode`s +parent: + +>lua + s("trig", { + i(1), c(2, { + sn(nil, { + t"cannot access the argnode :(", + f(function(args) + return args[1] + end, {???}) -- can't refer to i(1), since it isn't a sibling of `f`. + }), + t"sample_text" + }) + }) +< + +And the solution: first give the node we want to refer to a key, and then pass +the same to the `functionNode`. + +>lua + s("trig", { + i(1, "", {key = "i1-key"}), c(2, { + sn(nil, { i(1), + t"can access the argnode :)", + f(function(args) + return args[1] + end, k("i1-key") ) + }), + t"sample_text" + }) + }) +< + + +============================================================================== +14. Absolute Indexer *luasnip-absolute-indexer* + +`absolute_indexer` allows accessing nodes by their unique jump-index path from +the snippet-root. This makes it almost as powerful as |luasnip-key-indexer|, +but again removes the possibility of referring to non-jumpable nodes and makes +it all a bit more error-prone since the jump-index paths are hard to follow, +and (unfortunately) have to be a bit verbose (see the long example of +`absolute_indexer`-positions below). Consider just using |luasnip-key-indexer| +instead. + +(The solution-snippet from |luasnip-key-indexer|, but using `ai` instead.) + +>lua + s("trig", { + i(1), c(2, { + sn(nil, { i(1), + t"can access the argnode :)", + f(function(args) + return args[1] + end, ai(1) ) + }), + t"sample_text" + }) + }) +< + +There are some quirks in addressing nodes: + +>lua + s("trig", { + i(2), -- ai[2]: indices based on jump-index, not position. + sn(1, { -- ai[1] + i(1), -- ai[1][1] + t"lel", -- not addressable. + i(2) -- ai[1][2] + }), + c(3, { -- ai[3] + i(nil), -- ai[3][1] + t"lel", -- ai[3][2]: choices are always addressable. + }), + d(4, function() -- ai[4] + return sn(nil, { -- ai[4][0] + i(1), -- ai[4][0][1] + }) + end, {}), + r(5, "restore_key", -- ai[5] + i(1) -- ai[5][0][1]: restoreNodes always store snippetNodes. + ), + r(6, "restore_key_2", -- ai[6] + sn(nil, { -- ai[6][0] + i(1) -- ai[6][0][1] + }) + ) + }) +< + +Note specifically that the index of a dynamicNode differs from that of the +generated snippetNode, and that restoreNodes (internally) always store a +snippetNode, so even if the restoreNode only contains one node, that node has +to be accessed as `ai[restoreNodeIndx][0][1]`. + +`absolute_indexer`s’ can be constructed in different ways: + +- `ai[1][2][3]` +- `ai(1, 2, 3)` +- `ai{1, 2, 3}` + +are all the same node. + + +============================================================================== +15. MultiSnippet *luasnip-multisnippet* + +There are situations where it might be comfortable to access a snippet in +different ways. For example, one might want to enable auto-triggering in +regions where the snippets usage is common, while leaving it manual-only in +others. This is where `ms` should be used: A single snippet can be associated +with multiple `context`s (the `context`-table determines the conditions under +which a snippet may be triggered). This has the advantage (compared with just +registering copies) that all `context`s are backed by a single snippet, and not +multiple, and it’s (at least should be :D) more comfortable to use. + +`ms(contexts, nodes, opts) -> addable`: + +- `contexts`: table containing list of `contexts`, and some keywords. + `context` are described in |luasnip-snippets|, here they may also be tables + or strings. + So far, there is only one valid keyword: + - `common`: Accepts yet another context. + The options in `common` are applied to (but don’t override) the other + contexts specified in `contexts`. +- `nodes`: List of nodes, exactly like in |luasnip-snippets|. +- `opts`: Table, options for this function: + - `common_opts`: The snippet-options (see also |luasnip-snippets|) applied + to the snippet generated from `nodes`. + +The returned object is an `addable`, something which can be passed to +`add_snippets`, or returned from the lua-loader. + +**Examples**: + +>lua + ls.add_snippets("all", { + ms({"a", "b"}, {t"a or b"}) + }) +< + +>lua + ls.add_snippets("all", { + ms({ + common = {snippetType = "autosnippet"}, + "a", + "b" + }, { + t"a or b (but autotriggered!!)" + }) + }) +< + +>lua + ls.add_snippets("all", { + ms({ + common = {snippetType = "autosnippet"}, + {trig = "a", snippetType = "snippet"}, + "b", + {trig = "c", condition = function(line_to_cursor) + return line_to_cursor == "" + end} + }, { + t"a or b (but autotriggered!!)" + }) + }) +< + + +============================================================================== +16. Extras *luasnip-extras* + + +LAMBDA *luasnip-extras-lambda* + +A shortcut for `functionNode`s that only do very basic string manipulation. + +`l(lambda, argnodes)`: + +- `lambda`: An object created by applying string-operations to `l._n`, objects + representing the `n`th argnode. + For example: + - `l._1:gsub("a", "e")` replaces all occurences of "a" in the text of the + first argnode with "e", or + - `l._1 .. l._2` concatenates text of the first and second argnode. + If an argnode contains multiple lines of text, they are concatenated with + `"\n"` prior to any operation. +- `argnodes`, a |luasnip-node-reference|, just like in function- and + dynamicNode. + +There are many examples for `lambda` in `Examples/snippets.lua` + + +MATCH *luasnip-extras-match* + +`match` can insert text based on a predicate (again, a shorthand for +`functionNode`). + +`match(argnodes, condition, then, else)`: + +- `argnode`: A single |luasnip-node-reference|. May not be nil, or + a table. +- `condition` may be either of + - `string`: interpreted as a lua pattern. Matched on the `\n`-joined (in case + it’s multiline) text of the first argnode (`args[1]:match(condition)`). + - `function`: `fn(args, snip) -> bool`: takes the same parameters as the + `functionNode`-function, any value other than nil or false is interpreted + as a match. + - `lambda`: `l._n` is the `\n`-joined text of the nth argnode. + Useful if string manipulations have to be performed before the string is matched. + Should end with `match`, but any other truthy result will be interpreted + as matching. +- `then` is inserted if the condition matches, +- `else` if it does not. + +Both `then` and `else` can be either text, lambda or function (with the same +parameters as specified above). `then`’s default-value depends on the +`condition`: + +- `pattern`: Simply the return value from the `match`, e.g. the entire match, + or, if there were capture groups, the first capture group. +- `function`: the return value of the function if it is either a string, or a + table (if there is no `then`, the function cannot return a table containing + something other than strings). +- `lambda`: Simply the first value returned by the lambda. + +Examples: + +- `match(n, "^ABC$", "A")`. +- `match(n, lambda._1:match(lambda._1:reverse()), "PALINDROME")` + >lua + s("trig", { + i(1), t":", + i(2), t"::", + m({1, 2}, l._1:match("^"..l._2.."$"), l._1:gsub("a", "e")) + }) + < +- >lua + s("extras1", { + i(1), t { "", "" }, m(1, "^ABC$", "A") + }) + < + Inserts "A" if the node with jump-index `n` matches "ABC" exactly, nothing + otherwise. +- >lua + s("extras2", { + i(1, "INPUT"), t { "", "" }, m(1, l._1:match(l._1:reverse()), "PALINDROME") + }) + < + Inserts `"PALINDROME"` if i(1) contains a palindrome. +- >lua + s("extras3", { + i(1), t { "", "" }, i(2), t { "", "" }, + m({ 1, 2 }, l._1:match("^" .. l._2 .. "$"), l._1:gsub("a", "e")) + }) + < + This inserts the text of the node with jump-index 1, with all occurences of `a` + replaced with `e`, if the second insertNode matches the first exactly. + + +REPEAT *luasnip-extras-repeat* + +Inserts the text of the passed node. + +`rep(node_reference)` - `node_reference`, a single |luasnip-node-reference|. + +>lua + s("extras4", { i(1), t { "", "" }, extras.rep(1) }) +< + + +PARTIAL *luasnip-extras-partial* + +Evaluates a function on expand and inserts its value. + +`partial(fn, params...)` - `fn`: any function - `params`: varargs, any, will be +passed to `fn`. + +For example `partial(os.date, "%Y")` inserts the current year on expansion. + +>lua + s("extras5", { extras.partial(os.date, "%Y") }) +< + + +NONEMPTY *luasnip-extras-nonempty* + +Inserts text if the referenced node doesn’t contain any text. + +`nonempty(node_reference, not_empty, empty)`: + +- `node_reference`, a single |luasnip-node-reference|. +- `not_empty`, `string`: inserted if the node is not empty. +- `empty`, `string`: inserted if the node is empty. + +>lua + s("extras6", { i(1, ""), t { "", "" }, extras.nonempty(1, "not empty!", "empty!") }) +< + + +DYNAMIC LAMBDA *luasnip-extras-dynamic-lambda* + +Pretty much the same as lambda, but it inserts the resulting text as an +insertNode, and, as such, it can be quickly overridden. + +`dynamic_lambda(jump_indx, lambda, node_references)` - `jump_indx`, as usual, +the jump-indx. + +The remaining arguments carry over from lambda. + +>lua + s("extras7", { i(1), t { "", "" }, extras.dynamic_lambda(2, l._1 .. l._1, 1) }) +< + + +FMT *luasnip-extras-fmt* + +Authoring snippets can be quite clunky, especially since every second node is +probably a `textNode`, inserting a small number of characters between two more +complicated nodes. + +`fmt` can be used to define snippets in a much more readable way. This is +achieved by borrowing (as the name implies) from `format`-functionality (our +syntax is very similar to python’s +). + +`fmt` accepts a string and a table of nodes. Each occurrence of a delimiter +pair in the string is replaced by one node from the table, while text outside +the delimiters is turned into textNodes. + +Simple example: + +>lua + ls.add_snippets("all", { + -- important! fmt does not return a snippet, it returns a table of nodes. + s("example1", fmt("just an {iNode1}", { + iNode1 = i(1, "example") + })), + s("example2", fmt([[ + if {} then + {} + end + ]], { + -- i(1) is at nodes[1], i(2) at nodes[2]. + i(1, "not now"), i(2, "when") + })), + s("example3", fmt([[ + if <> then + <> + end + ]], { + -- i(1) is at nodes[1], i(2) at nodes[2]. + i(1, "not now"), i(2, "when") + }, { + delimiters = "<>" + })), + s("example4", fmt([[ + repeat {a} with the same key {a} + ]], { + a = i(1, "this will be repeat") + }, { + repeat_duplicates = true + })) + }) +< + +One important detail here is that the position of the delimiters does not, in +any way, correspond to the jump-index of the nodes! + +`fmt(format:string, nodes:table of nodes, opts:table|nil) -> table of nodes` + +- `format`: a string. Occurences of `{}` ( `{}` are customizable; more + on that later) are replaced with `content[]` (which should be a + node), while surrounding text becomes `textNode`s. + To escape a delimiter, repeat it (`"{{"`). + If no key is given (`{}`) are numbered automatically: + `"{} ? {} : {}"` becomes `"{1} ? {2} : {3}"`, while + `"{} ? {3} : {}"` becomes `"{1} ? {3} : {4}"` (the count restarts at each + numbered placeholder). + If a key appears more than once in `format`, the node in + `content[]` is inserted for the first, and copies of it for + subsequent occurences. +- `nodes`: just a table of nodes. +- `opts`: optional arguments: + - `delimiters`: string, two characters. Change `{}` to some other pair, e.g. + `"<>"`. + - `strict`: Warn about unused nodes (default true). + - `trim_empty`: remove empty (`"%s*"`) first and last line in `format`. Useful + when passing multiline strings via `[[]]` (default true). + - `dedent`: remove indent common to all lines in `format`. Again, makes + passing multiline-strings a bit nicer (default true). + - `repeat_duplicates`: repeat nodes when a key is reused instead of copying + the node if it has a jump-index, refer to |luasnip-basics-jump-index| to + know which nodes have a jump-index (default false). + +There is also `require("luasnip.extras.fmt").fmta`. This only differs from +`fmt` by using angle brackets (`<>`) as the default delimiter. + + +CONDITIONS *luasnip-extras-conditions* + +This module (`luasnip.extras.condition`) contains functions that can be passed +to a snippet’s `condition` or `show_condition`. These are grouped accordingly +into `luasnip.extras.conditions.expand` and `luasnip.extras.conditions.show`: + +**expand**: + +- `line_begin`: only expand if the cursor is at the beginning of the line. + +**show**: + +- `line_end`: only expand at the end of the line. +- `has_selected_text`: only expand if there’s selected text stored after pressing + `store_selection_keys`. + +Additionally, `expand` contains all conditions provided by `show`. + + +CONDITION OBJECTS ~ + +`luasnip.extras.conditions` also contains condition objects. These can, just +like functions, be passed to `condition` or `show_condition`, but can also be +combined with each other into logical expressions: + +- `-c1 -> not c1` +- `c1 * c2 -> c1 and c2` +- `c1 + c2 -> c1 or c2` +- `c1 - c2 -> c1 and not c2`: This is similar to set differences: + `A \ B = {a in A | a not in B}`. This makes `-(a + b) = -a - b` an identity + representing de Morgan’s law: `not (a or b) = not a and not b`. However, + since boolean algebra lacks an additive inverse, `a + (-b) = a - b` does not + hold. Thus, this is NOT the same as `c1 + (-c2)`. +- `c1 ^ c2 -> c1 xor(!=) c2` +- `c1 % c2 -> c1 xnor(==) c2`: This decision may seem weird, considering how + there is an overload for the `==`-operator. Unfortunately, it’s not possible + to use this for our purposes (some info + here ), + so we decided to make use of a more obscure symbol (which will hopefully avoid + false assumptions about its meaning). + +This makes logical combinations of conditions very readable. Compare + +>lua + condition = conditions.expand.line_end + conditions.expand.line_begin +< + +with the more verbose + +>lua + condition = function(...) return conditions.expand.line_end(...) or conditions.expand.line_begin(...) end +< + +The conditions provided in `show` and `expand` are already condition objects. +To create new ones, use +`require("luasnip.extras.conditions").make_condition(condition_fn)` + + +ON THE FLY-SNIPPETS *luasnip-extras-on-the-fly-snippets* + +Sometimes it’s desirable to create snippets tailored for exactly the current +situation. For example inserting repetitive, but just slightly different +invocations of some function, or supplying data in some schema. + +On-the-fly snippets enable exactly this use case: they can be quickly created +and expanded with as little disruption as possible. + +Since they should mainly fast to write and don’t necessarily need all bells +and whistles, they don’t make use of lsp/textmate-syntax, but a more +simplistic one: + +- `$anytext` denotes a placeholder (`insertNode`) with text "anytext". The text + also serves as a unique key: if there are multiple placeholders with the same + key, only the first will be editable, the others will just mirror it. +- … That’s it. `$` can be escaped by preceding it with a second `$`, all other + symbols will be interpreted literally. + +There is currently only one way to expand on-the-fly snippets: +`require('luasnip.extras.otf').on_the_fly("")` will interpret +whatever text is in the register `` as a snippet, and expand it +immediately. The idea behind this mechanism is that it enables a very immediate +way of supplying and retrieving (expanding) the snippet: write the snippet-body +into the buffer, cut/yank it into some register, and call +`on_the_fly("")` to expand the snippet. + +Here’s one set of example keybindings: + +>vim + " in the first call: passing the register is optional since `on_the_fly` + " defaults to the unnamed register, which will always contain the previously cut + " text. + vnoremap "eclua require('luasnip.extras.otf').on_the_fly("e") + inoremap lua require('luasnip.extras.otf').on_the_fly("e") +< + +Obviously, `` is arbritary and can be changed to any other key combo. +Another interesting application is allowing multiple on-the-fly snippets at the +same time by retrieving snippets from multiple registers: + +>vim + " For register a + vnoremap a "aclua require('luasnip.extras.otf').on_the_fly() + inoremap a lua require('luasnip.extras.otf').on_the_fly("a") + + + " For register b + vnoremap a "bc:lua require('luasnip.extras.otf').on_the_fly() + inoremap b lua require('luasnip.extras.otf').on_the_fly("b") +< + + +SELECT_CHOICE *luasnip-extras-select_choice* + +It’s possible to leverage `vim.ui.select` for selecting a choice directly, +without cycling through the available choices. All that is needed for this is +calling `require("luasnip.extras.select_choice")`, most likely via some +keybind, e.g. + +>vim + inoremap lua require("luasnip.extras.select_choice")() +< + +while inside a choiceNode. The `opts.kind` hint for `vim.ui.select` will be set +to `luasnip`. + + +FILETYPE-FUNCTIONS *luasnip-extras-filetype-functions* + +Contains some utility functions that can be passed to the `ft_func` or +`load_ft_func`-settings. + +- `from_filetype`: the default for `ft_func`. Simply returns the filetype(s) of + the buffer. +- `from_cursor_pos`: uses treesitter to determine the filetype at the cursor. + With that, it’s possible to expand snippets in injected regions, as long as + the treesitter parser supports them. If this is used in conjuction with + `lazy_load`, extra care must be taken that all the filetypes that can be + expanded in a given buffer are also returned by `load_ft_func` (otherwise their + snippets may not be loaded). This can easily be achieved with `extend_load_ft`. +- `extend_load_ft`: `fn(extend_ft:map) -> fn` A simple solution to the problem + described above is loading more filetypes than just that of the target buffer + when `lazy_load`ing. This can be done ergonomically via `extend_load_ft`: + calling it with a table where the keys are filetypes, and the values are the + filetypes that should be loaded additionaly returns a function that can be + passed to `load_ft_func` and takes care of extending the filetypes properly. + >lua + ls.setup({ + load_ft_func = + -- Also load both lua and json when a markdown-file is opened, + -- javascript for html. + -- Other filetypes just load themselves. + require("luasnip.extras.filetype_functions").extend_load_ft({ + markdown = {"lua", "json"}, + html = {"javascript"} + }) + }) + < + + +POSTFIX-SNIPPET *luasnip-extras-postfix-snippet* + +Postfix snippets, famously used in rust analyzer + and various IDEs, are a type of snippet +which alters text before the snippet’s trigger. While these can be +implemented using regTrig snippets, this helper makes the process easier in +most cases. + +The simplest example, which surrounds the text preceeding the `.br` with +brackets `[]`, looks like: + +>lua + postfix(".br", { + f(function(_, parent) + return "[" .. parent.snippet.env.POSTFIX_MATCH .. "]" + end, {}), + }) +< + +and is triggered with `xxx.br` and expands to `[xxx]`. + +Note the `parent.snippet.env.POSTFIX_MATCH` in the function node. This is +additional field generated by the postfix snippet. This field is generated by +extracting the text matched (using a configurable matching string, see below) +from before the trigger. In the case above, the field would equal `"xxx"`. This +is also usable within dynamic nodes. + +This field can also be used within lambdas and dynamic nodes. + +>lua + postfix(".br", { + l("[" .. l.POSTFIX_MATCH .. "]"), + }) +< + +>lua + postfix(".brd", { + d(1, function (_, parent) + return sn(nil, {t("[" .. parent.env.POSTFIX_MATCH .. "]")}) + end) + }) +< + +The arguments to `postfix` are identical to the arguments to `s` but with a few +extra options. + +The first argument can be either a string or a table. If it is a string, that +string will act as the trigger, and if it is a table it has the same valid keys +as the table in the same position for `s` except: + +- `wordTrig`: This key will be ignored if passed in, as it must always be + false for postfix snippets. +- `match_pattern`: The pattern that the line before the trigger is matched + against. The default match pattern is `"[%w%.%_%-]+$"`. Note the `$`. This + matches since only the line _up until_ the beginning of the trigger is + matched against the pattern, which makes the character immediately + preceeding the trigger match as the end of the string. + +Some other match strings, including the default, are available from the postfix +module. `require("luasnip.extras.postfix).matches`: + +- `default`: `[%w%.%_%-%"%']+$` +- `line`: `^.+$` + +The second argument is identical to the second argument for `s`, that is, a +table of nodes. + +The optional third argument is the same as the third (`opts`) argument to the +`s` function, but with one difference: + +The postfix snippet works using a callback on the pre_expand event of the +snippet. If you pass a callback on the pre_expand event (structure example +below) it will get run after the builtin callback. + +>lua + { + callbacks = { + [-1] = { + [events.pre_expand] = function(snippet, event_args) + -- function body to match before the dot + -- goes here + end + } + } + } +< + + +TREESITTER-POSTFIX-SNIPPET *luasnip-extras-treesitter-postfix-snippet* + +Instead of triggering a postfix-snippet when some pattern matches in front of +the trigger, it might be useful to match if some specific treesitter-nodes +surround/are in front of the trigger. While this functionality can also be +implemented by a cusutom `resolveExpandParams`, this helper simplifies the +common cases. + +This matching of treesitter-nodes can be done either + +- by providing a query and the name of the capture that should be in front of + the trigger (in most cases, the complete match, but requiring specific nodes + before/after the matched node may be useful as well), or +- by providing a function that manually walks the node-tree, and returns the + node in front of the trigger on success (for increased flexibility). + +A simple example, which surrounds the previous node’s text preceeding the +`.mv` with `std::move()` in cpp files, looks like: + +>lua + local treesitter_postfix = require("luasnip.extras.treesitter_postfix").treesitter_postfix + + treesitter_postfix({ + trig = ".mv", + matchTSNode = { + query = [[ + [ + (call_expression) + (identifier) + (template_function) + (subscript_expression) + (field_expression) + (user_defined_literal) + ] @prefix + ]] + query_lang = "cpp" + }, + },{ + f(function(_, parent) + local node_content = table.concat(parent.snippet.env.LS_TSMATCH, '\n') + local replaced_content = ("std::move(%s)"):format(node_content) + return vim.split(ret_str, "\n", { trimempty = false }) + end) + }) +< + +`LS_TSMATCH` is the treesitter-postfix equivalent to `POSTFIX_MATCH`, and is +populated with the match (in this case the text of a treesitter-node) in front +of the trigger. + +The arguments to `treesitter_postfix` are identical to the arguments to `s` but +with a few extra options. + +The first argument has to be a table, which defines at least `trig` and +`matchTSNode`. All keys from the regular `s` may be set here (except for +`wordTrig`, which will be ignored), and additionally the following: + +- `reparseBuffer`, `string?`: Sometimes the trigger may interfere with + treesitter recognizing queries correctly. With this option, the trigger may + either be removed from the live-buffer (`"live"`), from a copy of the buffer + (`"copy"`), or not at all (`nil`). +- `matchTSNode`: How to determine whether there is a matching node in front of + the cursor. There are two options: + - `fun(parser: LuaSnip.extra.TSParser, pos: { [1]: number, [2]: number }): LuaSnip.extra.NamedTSMatch?, TSNode? Manually determine whether there is a matching node that ends just before`pos`(the beginning of the trigger). Return`nil,nil`if there is no match, otherwise first return a table mapping names to nodes (the text, position and type of these will be provided via`snip.env`), and second the node that is the matched node. + - `LuaSnip.extra.MatchTSNodeOpts`, which represents a query and provides all + captures of the matched pattern in `NamedTSMatch`. It contains the following + options: + - `query`, `string`: The query, in textual form. + - `query_name`, `string`: The name of the runtime-query to be used (passed + to `query.get()`), defaults to `"luasnip"` (so one could create a + file which only contains queries used by luasnip, like + `$CONFDIR/queries//luasnip.scm`, which might make sense to define + general concepts independent of a single snippet). + `query` and `query_name` are mutually exclusive, only one of both shall be + defined. + - `query_lang`, `string`: The language of the query. This is the only + required parameter to this function, since there’s no sufficiently + straightforward way to determine the language of the query for us. + Consider using `extend_override` to define a `ts_postfix`-function that + automatically fills in the language for the filetype of the snippet-file. + - `match_captures`, `string|string[]`: The capture(s) to use for determining + the actual prefix (so the node that should be immediately in front of the + trigger). This defaults to just `"prefix"`. + - `select`, `string?|fun(): LuaSnip.extra.MatchSelector`: Since there may be + multiple matching captures in front of the cursor, there has to be some + way to select the node that will actually be used. + If this is a string, it has to be one of "any", "shortest", or "longest", + which mean that any, the shortest, or the longest match is used. + If it is a function, it must return a table with two fields, `record` and + `retrieve`. `record` is called with a TSMatch and a potential node for the + ts-match, and may return `true` to abort the selection-procedure. + `retrieve` must return either a TSMatch-TSNode-tuple (which is used as the + match) or `nil`, to signify that there is no match. + `lua/luasnip/extras/_treesitter.lua` contains the table + `builtin_tsnode_selectors`, which contains the implementations for + any/shortest/longest, which can be used as examples for more complicated + custom-selectors. + +The text of the matched node can be accessed as `snip.env.LS_TSMATCH`. The text +of the nodes returned as `NamedTSMatch` can be accessed as +`snip.env.LS_TSCAPTURE_`, and their range and type as +`snip.env.LS_TSDATA..range/type` (where range is a tuple +of row-col-tuples, both 0-indexed). + +For a query like + +>scm + (function_declaration + name: (identifier) @fname + parameters: (parameters) @params + body: (block) @body + ) @prefix +< + +matched against + +>lua + function add(a, b) + return a + b + end +< + +`snip.env` would contain: + +- `LS_TSMATCH`: `{ "function add(a, b)", "\treturn a + b", "end" }` +- `LS_TSDATA`: + >lua + { + body = { + range = { { 1, 1 }, { 1, 13 } }, + type = "block" + }, + fname = { + range = { { 0, 9 }, { 0, 12 } }, + type = "identifier" + }, + params = { + range = { { 0, 12 }, { 0, 18 } }, + type = "parameters" + }, + prefix = { + range = { { 0, 0 }, { 2, 3 } }, + type = "function_declaration" + } + } + < +- `LS_TSCAPTURE_FNAME`: `{ "add" }` +- `LS_TSCAPTURE_PARAMS`: `{ "(a, b)" }` +- `LS_TSCAPTURE_BODY`: `{ "return a + b" }` +- `LS_TSCAPTURE_PREFIX`: `{ "function add(a, b)", "\treturn a + b", "end" }` + +(note that all variables containing text of nodes are string-arrays, one entry +for each line) + +There is one important caveat when accessing `LS_TSDATA` in +function/dynamicNodes: It won’t contain the values as specified here while +generating docstrings (in fact, it won’t even be a table). Since docstrings +have to be generated without any runtime-information, we just have to provide +dummy-data in `env`, which will be some kind of string related to the name of +the env-variable. Since the structure of `LS_TSDATA` obviously does not fit +that model, we can’t really handle it in a nice way (at least yet). So, for +now, best include a check like `local static_evaluation = type(env.LS_TSDATA) +== "string"`, and behave accordingly if `static_evaluation` is true (for +example, return some value tailored for displaying it in a docstring). + +One more example, which actually uses a few captures: + +>lua + ts_post({ + matchTSNode = { + query = [[ + (function_declaration + name: (identifier) @fname + parameters: (parameters) @params + body: (block) @body + ) @prefix + ]], + query_lang = "lua", + }, + trig = ".var" + }, fmt([[ + local {} = function{} + {} + end + ]], { + l(l.LS_TSCAPTURE_FNAME), + l(l.LS_TSCAPTURE_PARAMS), + l(l.LS_TSCAPTURE_BODY), + })) +< + +The module `luasnip.extras.treesitter_postfix` contains a few functions that +may be useful for creating more efficient ts-postfix-snippets. Nested in +`builtin.tsnode_matcher` are: + +- `fun find_topmost_types(types: string[]): MatchTSNodeFunc`: Generates + a `LuaSnip.extra.MatchTSNodeFunc` which returns the last parent whose type + is in `types`. +- `fun find_first_types(types: string[]): MatchTSNodeFunc`: Similar to + `find_topmost_types`, only this one matches the first parent whose type is in + types. +- `find_nth_parent(n: number): MatchTSNodeFunc`: Simply matches the `n`-th + parent of the innermost node infront of the trigger. + +With `find_topmost_types`, the first example can be implemented more +efficiently (without needing a whole query): + +>lua + local postfix_builtin = require("luasnip.extras.treesitter_postfix").builtin + + ls.add_snippets("all", { + ts_post({ + matchTSNode = postfix_builtin.tsnode_matcher.find_topmost_types({ + "call_expression", + "identifier", + "template_function", + "subscript_expression", + "field_expression", + "user_defined_literal" + }), + trig = ".mv" + }, { + l(l_str.format("std::move(%s)", l.LS_TSMATCH)) + }) + }, {key = "asdf"}) +< + + +SNIPPET LIST *luasnip-extras-snippet-list* + +>lua + local sl = require("luasnip.extras.snippet_list") +< + +Makes an `open` function available to use to open currently available snippets +in a different buffer/window/tab. + +`sl.open(opts:table|nil)` + +- `opts`: optional arguments: + - `snip_info`: `snip_info(snippet) -> table representation of snippet` + - `printer`: `printer(snippets:table) -> any` + - `display`: `display(snippets:any)` + +Benefits include: syntax highlighting, searching, and customizability. + +Simple Example: + +>lua + sl.open() +< + +Customization Examples: + +>lua + -- making our own snip_info + local function snip_info(snippet) + return { name = snippet.name } + end + + -- using it + sl.open({snip_info = snip_info}) +< + +>lua + -- making our own printer + local function printer(snippets) + local res = "" + + for ft, snips in pairs(snippets) do + res = res .. ft .. "\n" + for _, snip in pairs(snips) do + res = res .. " " .. "Name: " .. snip.name .. "\n" + res = res .. " " .. "Desc: " .. snip.description[1] .. "\n" + res = res .. " " .. "Trigger: " .. snip.trigger .. "\n" + res = res .. " ----" .. "\n" + end + end + + return res + end + + + -- using it + sl.open({printer = printer}) +< + +>lua + -- making our own display + local function display(printer_result) + -- right vertical split + vim.cmd("botright vnew") + + -- get buf and win handle + local buf = vim.api.nvim_get_current_buf() + local win = vim.api.nvim_get_current_win() + + -- setting window and buffer options + vim.api.nvim_win_set_option(win, "foldmethod", "manual") + vim.api.nvim_buf_set_option(buf, "filetype", "javascript") + + vim.api.nvim_buf_set_option(buf, "buftype", "nofile") + vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe") + vim.api.nvim_buf_set_option(buf, "buflisted", false) + + vim.api.nvim_buf_set_name(buf, "Custom Display buf " .. buf) + + -- dump snippets + local replacement = vim.split(printer_result) + vim.api.nvim_buf_set_lines(buf, 0, 0, false, replacement) + end + + -- using it + sl.open({display = display}) +< + +There is a **caveat** with implementing your own printer and/or display +function. The **default** behavior for the printer function is to return a +string representation of the snippets. The display function uses the results +from the printer function, therefore by **default** the display function is +expecting that result to be a string. + +However, this doesn’t have to be the case. For example, you can implement +your own printer function that returns a table representation of the snippets +**but** you would have to then implement your own display function or some +other function in order to return the result as a string. + +An `options` table, which has some core functionality that can be used to +customize 'common' settings, is provided. + +- `sl.options`: options table: + - `display`: `display(opts:table|nil) -> function(printer_result:string)` + +You can see from the example above that making a custom display is a fairly +involved process. What if you just wanted to change a buffer option like the +name or just the filetype? This is where `sl.options.display` comes in. It +allows you to customize buffer and window options while keeping the default +behavior. + +`sl.options.display(opts:table|nil) -> function(printer_result:string)` + +- `opts`: optional arguments: + - `win_opts`: `table which has a {window_option = value} form` + - `buf_opts`: `table which has a {buffer_option = value} form` + - `get_name`: `get_name(buf) -> string` + +Let’s recreate the custom display example above: + +>lua + -- keeping the default display behavior but modifying window/buffer + local modified_default_display = sl.options.display({ + buf_opts = {filetype = "javascript"}, + win_opts = {foldmethod = "manual"}, + get_name = function(buf) return "Custom Display buf " .. buf end + }) + + -- using it + sl.open({display = modified_default_display}) +< + + +SNIPPET LOCATION *luasnip-extras-snippet-location* + +This module can consume a snippets |luasnip-source|, more specifically, jump to +the location referred by it. This is primarily implemented for snippet which +got their source from one of the loaders, but might also work for snippets +where the source was set manually. + +`require("luasnip.extras.snip_location")`: + +- `snip_location.jump_to_snippet(snip, opts)` + Jump to the definition of `snip`. + - `snip`: a snippet with attached source-data. + - `opts`: `nil|table`, optional arguments, valid keys are: + - `hl_duration_ms`: `number`, duration for which the definition should be highlighted, + in milliseconds. 0 disables the highlight. + - `edit_fn`: `function(file)`, this function will be called with the file + the snippet is located in, and is responsible for jumping to it. + We assume that after it has returned, the current buffer contains `file`. +- `snip_location.jump_to_active_snippet(opts)` + Jump to definition of active snippet. + - `opts`: `nil|table`, accepts the same keys as the `opts`-parameter of + `jump_to_snippet`. + + +============================================================================== +17. Extend Decorator *luasnip-extend-decorator* + +Most of luasnip’s functions have some arguments to control their behaviour. +Examples include `s`, where `wordTrig`, `regTrig`, … can be set in the first +argument to the function, or `fmt`, where the delimiter can be set in the third +argument. This is all good and well, but if these functions are often used with +non-default settings, it can become cumbersome to always explicitly set them. + +This is where the `extend_decorator` comes in: it can be used to create +decorated functions which always extend the arguments passed directly with +other previously defined ones. + +An example: + +>lua + local fmt = require("luasnip.extras.fmt").fmt + + fmt("{}", {i(1)}) -- -> list of nodes, containing just the i(1). + + -- when authoring snippets for some filetype where `{` and `}` are common, they + -- would always have to be escaped in the format-string. It might be preferable + -- to use other delimiters, like `<` and `>`. + + fmt("<>", {i(1)}, {delimiters = "<>"}) -- -> same as above. + + -- but it's quite annoying to always pass the `{delimiters = "<>"}`. + + -- with extend_decorator: + local fmt_angle = ls.extend_decorator.apply(fmt, {delimiters = "<>"}) + fmt_angle("<>", {i(1)}) -- -> same as above. + + -- the same also works with other functions provided by luasnip, for example all + -- node/snippet-constructors and `parse_snippet`. +< + +`extend_decorator.apply(fn, ...)` requires that `fn` is previously registered +via `extend_decorator.register`. (This is not limited to LuaSnip’s functions; +although, for usage outside of LuaSnip, best copy the source file: +`/lua/luasnip/util/extend_decorator.lua`). + +`register(fn, ...)`: + +- `fn`: the function. +- `...`: any number of tables. Each specifies how to extend an argument of `fn`. + The tables accept: + - arg_indx, `number` (required): the position of the parameter to override. + - extend, `fn(arg, extend_value) -> effective_arg` (optional): this function + is used to extend the args passed to the decorated function. + It defaults to a function which just extends the the arg-table with the + extend table (accepts `nil`). + This extend behaviour is adaptable to accomodate `s`, where the first + argument may be string or table. + +`apply(fn, ...) -> decorated_fn`: + +- `fn`: the function to decorate. +- `...`: The values to extend with. These should match the descriptions passed + in `register` (the argument first passed to `register` will be extended with + the first value passed here). + +One more example for registering a new function: + +>lua + local function somefn(arg1, arg2, opts1, opts2) + -- not important + end + + -- note the reversed arg_indx!! + extend_decorator.register(somefn, {arg_indx=4}, {arg_indx=3}) + local extended = extend_decorator.apply(somefn, + {key = "opts2 is extended with this"}, + {key = "and opts1 with this"}) + extended(...) +< + + +============================================================================== +18. LSP-Snippets *luasnip-lsp-snippets* + +LuaSnip is capable of parsing LSP-style snippets using +`ls.parser.parse_snippet(context, snippet_string, opts)`: + +>lua + ls.parser.parse_snippet({trig = "lsp"}, "$1 is ${2|hard,easy,challenging|}") +< + +`context` can be: - `string|table`: treated like the first argument to `ls.s`, +`parse_snippet` returns a snippet. - `number`: `parse_snippet` returns a +snippetNode, with the position `context`. - `nil`: `parse_snippet` returns a +flat table of nodes. This can be used like `fmt`. + +Nested placeholders(`"${1:this is ${2:nested}}"`) will be turned into +choiceNodes with: - the given snippet(`"this is ${1:nested}"`) and - an empty +insertNode + +This behaviour can be modified by changing `parser_nested_assembler` in +`ls.setup()`. + +LuaSnip will also modify some snippets that it is incapable of representing +accurately: + +- if the `$0` is a placeholder with something other than just text inside +- if the `$0` is a choice +- if the `$0` is not an immediate child of the snippet (it could be inside a + placeholder: `"${1: $0 }"`) + +To remedy those incompatibilities, the invalid `$0` will be replaced with a +tabstop/placeholder/choice which will be visited just before the new `$0`. This +new `$0` will be inserted at the (textually) earliest valid position behind the +invalid `$0`. + +`opts` can contain the following keys: - `trim_empty`: boolean, remove empty +lines from the snippet. Default true. - `dedent`: boolean, remove common indent +from the snippet’s lines. Default true. + +Both `trim_empty` and `dedent` will be disabled for snippets parsed via +`ls.lsp_expand`: it might prevent correct expansion of snippets sent by lsp. + + +SNIPMATE PARSER *luasnip-lsp-snippets-snipmate-parser* + +It is furthermore possible to parse SnipMate snippets (this includes support +for vimscript-evaluation!!) + +SnipMate snippets need to be parsed with a different function, +`ls.parser.parse_snipmate`: + +>lua + ls.parser.parse_snipmate("year", "The year is `strftime('%Y')`") +< + +`parse_snipmate` accepts the same arguments as `parse_snippet`, only the +snippet body is parsed differently. + + +TRANSFORMATIONS *luasnip-lsp-snippets-transformations* + +To apply Variable/Placeholder-transformations +, +luasnip needs to apply ECMAScript regexes. This is implemented by relying on +`jsregexp` . + +The easiest (but potentially error-prone) way to install it is by calling `make +install_jsregexp` in the repo root. + +This process can be automated by `packer.nvim`: + +>lua + use { "L3MON4D3/LuaSnip", run = "make install_jsregexp" } +< + +If this fails, first open an issue :P, and then try installing the +`jsregexp`-luarock. This is also possible via `packer.nvim`, although actual +usage may require a small workaround, see here + or here +. + +Alternatively, `jsregexp` can be cloned locally, `make`d, and the resulting +`jsregexp.so` placed in some place where nvim can find it (probably +`~/.config/nvim/lua/`). + +If `jsregexp` is not available, transformations are replaced by a simple copy. + + +============================================================================== +19. Variables *luasnip-variables* + +All `TM_something`-variables are supported with two additions: `LS_SELECT_RAW` +and `LS_SELECT_DEDENT`. These were introduced because `TM_SELECTED_TEXT` is +designed to be compatible with VSCode’s behavior, which can be +counterintuitive when the snippet can be expanded at places other than the +point where selection started (or when doing transformations on selected text). +Besides those we also provide `LS_TRIGGER` which contains the trigger of the +snippet, and `LS_CAPTURE_n` (where n is a positive integer) that contains the +n-th capture when using a regex with capture groups as `trig` in the snippet +definition. + +All variables can be used outside of lsp-parsed snippets as their values are +stored in a snippets’ `snip.env`-table: + +>lua + s("selected_text", f(function(args, snip) + local res, env = {}, snip.env + table.insert(res, "Selected Text (current line is " .. env.TM_LINE_NUMBER .. "):") + for _, ele in ipairs(env.LS_SELECT_RAW) do table.insert(res, ele) end + return res + end, {})) +< + +To use any `*SELECT*` variable, the `store_selection_keys` must be set via +`require("luasnip").config.setup({store_selection_keys=""})`. In this +case, hitting `` while in visual mode will populate the `*SELECT*`-vars +for the next snippet and then clear them. + + +ENVIRONMENT NAMESPACES *luasnip-variables-environment-namespaces* + +You can also add your own variables by using the `ls.env_namespace(name, opts)` +where: + +- `name`: `string` the names the namespace, can’t contain the character “_” +- `opts` is a table containing (in every case `EnvVal` is the same as `string|list[string]`: + - `vars`: `(fn(name:string)->EnvVal) | map[string, EnvVal]` + Is a function that receives a string and returns a value for the var with that name + or a table from var name to a value + (in this case, if the value is a function it will be executed lazily once per snippet expansion). + - `init`: `fn(info: table)->map[string, EnvVal]` Returns + a table of variables that will set to the environment of the snippet on expansion, + use this for vars that have to be calculated in that moment or that depend on each other. + The `info` table argument contains `pos` (0-based position of the cursor on expansion), + the `trigger` of the snippet and the `captures` list. + - `eager`: `list[string]` names of variables that will be taken from `vars` and appended eagerly (like those in init) + - `multiline_vars`: `(fn(name:string)->bool)|map[sting, bool]|bool|string[]` Says if certain vars are a table or just a string, + can be a function that get’s the name of the var and returns true if the var is a key, + a list of vars that are tables or a boolean for the full namespace, it’s false by default. Refer to + issue#510 for more information. + +The four fields of `opts` are optional but you need to provide either `init` or +`vars`, and `eager` can’t be without `vars`. Also, you can’t use namespaces +that override default vars. + +A simple example to make it more clear: + +>lua + local function random_lang() + return ({"LUA", "VIML", "VIML9"})[math.floor(math.random()/2 + 1.5)] + end + + ls.env_namespace("MY", {vars={ NAME="LuaSnip", LANG=random_lang }}) + + -- then you can use $MY_NAME and $MY_LANG in your snippets + + ls.env_namespace("SYS", {vars=os.getenv, eager={"HOME"}}) + + -- then you can use $SYS_HOME which was eagerly initialized but also $SYS_USER (or any other system environment var) in your snippets + + lsp.env_namespace("POS", {init=function(info) return {VAL=vim.inspect(info.pos)} end}) + + -- then you can use $POS_VAL in your snippets + + s("custom_env", d(1, function(args, parent) + local env = parent.snippet.env + return sn(nil, t { + "NAME: " .. env.MY_NAME, + "LANG: " .. env.MY_LANG, + "HOME: " .. env.SYS_HOME, + "USER: " .. env.SYS_USER, + "VAL: " .. env.POS_VAL + }) + end, {})) +< + + +LSP-VARIABLES *luasnip-variables-lsp-variables* + +All variables, even ones added via `env_namespace`, can be accessed in LSP +snippets as `$VAR_NAME`. + +The lsp-spec states: + +------------------------------------------------------------------------------ +With `$name` or `${name:default}` you can insert the value of a variable. When +a variable isn’t set, its default or the empty string is inserted. When a +variable is unknown (that is, its name isn’t defined) the name of the +variable is inserted and it is transformed into a placeholder. + +------------------------------------------------------------------------------ +The above necessiates a differentiation between `unknown` and `unset` +variables: + +For LuaSnip, a variable `VARNAME` is `unknown` when `env.VARNAME` returns `nil` +and `unset` if it returns an empty string. + +Consider this when adding env-variables which might be used in LSP snippets. + + +============================================================================== +20. Loaders *luasnip-loaders* + +Luasnip is capable of loading snippets from different formats, including both +the well-established VSCode and SnipMate format, as well as plain Lua files for +snippets written in Lua. + +All loaders (except the vscode-standalone-loader) share a similar interface: +`require("luasnip.loaders.from_{vscode,snipmate,lua}").{lazy_,}load(opts:table|nil)` + +where `opts` can contain the following keys: + +- `paths`: List of paths to load. Can be a table, or a single + comma-separated string. + The paths may begin with `~/` or `./` to indicate that the path is + relative to your `$HOME` or to the directory where your `$MYVIMRC` resides + (useful to add your snippets). + If not set, `runtimepath` is searched for + directories that contain snippets. This procedure differs slightly for + each loader: + - `lua`: the snippet-library has to be in a directory named + `"luasnippets"`. + - `snipmate`: similar to lua, but the directory has to be `"snippets"`. + - `vscode`: any directory in `runtimepath` that contains a + `package.json` contributing snippets. +- `lazy_paths`: behaves essentially like `paths`, with two exceptions: if it is + `nil`, it does not default to `runtimepath`, and the paths listed here do not + need to exist, and will be loaded on creation. + LuaSnip will do its best to determine the path that this should resolve to, + but since the resolving we do is not very sophisticated it may produce + incorrect paths. Definitely check the log if snippets are not loaded as + expected. +- `exclude`: List of languages to exclude, empty by default. +- `include`: List of languages to include, includes everything by default. +- `{override,default}_priority`: These keys are passed straight to the + `add_snippets`-calls (documented in |luasnip-api|) and can therefore change the + priority of snippets loaded from some colletion (or, in combination with + `{in,ex}clude`, only some of its snippets). +- `fs_event_providers`: `table?`, specifies which mechanisms + should be used to watch files for updates/creation. + If `autocmd` is set to `true`, a `BufWritePost`-hook watches files of this + collection, if `libuv` is set, the file-watcher-api exposed by libuv is used + to watch for updates. + Use `libuv` if you want snippets to update from other neovim-instances, and + `autocmd` if the collection resides on a filesystem where the libuv-watchers + may not work correctly. Or, of course, just enable both :D + By default, only `autocmd` is enabled. + +While `load` will immediately load the snippets, `lazy_load` will defer loading +until the snippets are actually needed (whenever a new buffer is created, or +the filetype is changed luasnip actually loads `lazy_load`ed snippets for the +filetypes associated with this buffer. This association can be changed by +customizing `load_ft_func` in `setup`: the option takes a function that, passed +a `bufnr`, returns the filetypes that should be loaded (`fn(bufnr) -> filetypes +(string[])`)). + +All of the loaders support reloading, so simply editing any file contributing +snippets will reload its snippets (according to `fs_event_providers` in the +instance where the file was edited, or in other instances as well). + +As an alternative (or addition) to automatic reloading, luasnip can also +process manual updates to files: Call +`require("luasnip.loaders").reload_file(path)` to reload the file at `path`. +This may be useful when the collection is controlled by some other plugin, or +when enabling the other reload-mechanisms is for some reason undesirable +(performance? minimalism?). + +For easy editing of these files, LuaSnip provides a `vim.ui.select`-based +dialog (|luasnip-loaders-edit_snippets|) where first the filetype, and then the +file can be selected. + + +SNIPPET-SPECIFIC FILETYPES *luasnip-loaders-snippet-specific-filetypes* + +Some loaders (vscode,lua) support giving snippets generated in some file their +own filetype (vscode via `scope`, lua via the underlying `filetype`-option for +snippets). These snippet-specific filetypes are not considered when determining +which files to `lazy_load` for some filetype, this is exclusively determined by +the `language` associated with a file in vscodes’ `package.json`, and the +file/directory-name in lua. + +- This can be resolved relatively easily in vscode, where the `language` + advertised in `package.json` can just be a superset of the `scope`s in the file. +- Another simplistic solution is to set the language to `all` (in lua, it might + make sense to create a directory `luasnippets/all/*.lua` to group these files + together). +- Another approach is to modify `load_ft_func` to load a custom filetype if the + snippets should be activated, and store the snippets in a file for that + filetype. This can be used to group snippets by e.g. framework, and load them + once a file belonging to such a framework is edited. + +**Example**: `react.lua` + +>lua + return { + s({filetype = "css", trig = ...}, ...), + s({filetype = "html", trig = ...}, ...), + s({filetype = "js", trig = ...}, ...), + } +< + +`luasnip_config.lua` + +>lua + load_ft_func = function(bufnr) + if "" then + -- will load `react.lua` for this buffer + return {"react"} + else + return require("luasnip.extras.filetype_functions").from_filetype_load + end + end +< + +See the |luasnip-troubleshooting-adding-snippets-loaders| section if one is +having issues adding snippets via loaders. + + +VS-CODE *luasnip-loaders-vs-code* + +As a reference on the structure of these snippet libraries, see +`friendly-snippets` . + +We support a small extension: snippets can contain LuaSnip-specific options in +the `luasnip`-table: + +>json + "example1": { + "prefix": "options", + "body": [ + "whoa! :O" + ], + "luasnip": { + "priority": 2000, + "autotrigger": true, + "wordTrig": false + } + } +< + +Files with the extension `jsonc` will be parsed as `jsonc`, json with comments +, while +`*.json` are parsed with a regular `json` parser, where comments are +disallowed. (the json-parser is a bit faster, so don’t default to `jsonc` if +it’s not necessary). + +**Example**: + +`~/.config/nvim/my_snippets/package.json`: + +>json + { + "name": "example-snippets", + "contributes": { + "snippets": [ + { + "language": [ + "all" + ], + "path": "./snippets/all.json" + }, + { + "language": [ + "lua" + ], + "path": "./lua.json" + } + ] + } + } +< + +`~/.config/nvim/my_snippets/snippets/all.json`: + +>json + { + "snip1": { + "prefix": "all1", + "body": [ + "expands? jumps? $1 $2 !" + ] + }, + "snip2": { + "prefix": "all2", + "body": [ + "multi $1", + "line $2", + "snippet$0" + ] + } + } +< + +`~/.config/nvim/my_snippets/lua.json`: + +>json + { + "snip1": { + "prefix": "lua", + "body": [ + "lualualua" + ] + } + } +< + +This collection can be loaded with any of + +>lua + -- don't pass any arguments, luasnip will find the collection because it is + -- (probably) in rtp. + require("luasnip.loaders.from_vscode").lazy_load() + -- specify the full path... + require("luasnip.loaders.from_vscode").lazy_load({paths = "~/.config/nvim/my_snippets"}) + -- or relative to the directory of $MYVIMRC + require("luasnip.loaders.from_vscode").load({paths = "./my_snippets"}) +< + + +STANDALONE ~ + +Beside snippet-libraries provided by packages, vscode also supports another +format which can be used for project-local snippets, or user-defined snippets, +`.code-snippets`. + +The layout of these files is almost identical to that of the package-provided +snippets, but there is one additional field supported in the +snippet-definitions, `scope`, with which the filetype of the snippet can be +set. If `scope` is not set, the snippet will be added to the global filetype +(`all`). + +`require("luasnip.loaders.from_vscode").load_standalone(opts)` + +- `opts`: `table`, can contain the following keys: + - `path`: `string`, Path to the `*.code-snippets`-file that should be loaded. + Just like the paths in `load`, this one can begin with a `"~/"` to be + relative to `$HOME`, and a `"./"` to be relative to the + neovim-config-directory. + - `{override,default}_priority`: These keys are passed straight to the + `add_snippets`-calls (documented in |luasnip-api|) and can be used to change + the priority of the loaded snippets. + - `lazy`: `boolean`, if it is set, the file does not have to exist when + `load_standalone` is called, and it will be loaded on creation. + `false` by default. + +**Example**: `a.code-snippets`: + +>jsonc + { + // a comment, since `.code-snippets` may contain jsonc. + "c/cpp-snippet": { + "prefix": [ + "trigger1", + "trigger2" + ], + "body": [ + "this is $1", + "my snippet $2" + ], + "description": "A description of the snippet.", + "scope": "c,cpp" + }, + "python-snippet": { + "prefix": "trig", + "body": [ + "this is $1", + "a different snippet $2" + ], + "description": "Another snippet-description.", + "scope": "python" + }, + "global snippet": { + "prefix": "trigg", + "body": [ + "this is $1", + "the last snippet $2" + ], + "description": "One last snippet-description.", + } + } +< + +This file can be loaded by calling + +>lua + require("luasnip.loaders.from_vscode").load_standalone({path = "a.code-snippets"}) +< + + +SNIPMATE *luasnip-loaders-snipmate* + +Luasnip does not support the full snipmate format: Only `./{ft}.snippets` and +`./{ft}/*.snippets` will be loaded. See honza/vim-snippets + for lots of examples. + +Like VSCode, the SnipMate format is also extended to make use of some of +LuaSnip’s more advanced capabilities: + +>snippets + priority 2000 + autosnippet options + whoa :O +< + +**Example**: + +`~/.config/nvim/snippets/c.snippets`: + +>snippets + # this is a comment + snippet c c-snippet + c! +< + +`~/.config/nvim/snippets/cpp.snippets`: + +>snippets + extends c + + snippet cpp cpp-snippet + cpp! +< + +This can, again, be loaded with any of + +>lua + require("luasnip.loaders.from_snipmate").load() + -- specify the full path... + require("luasnip.loaders.from_snipmate").lazy_load({paths = "~/.config/nvim/snippets"}) + -- or relative to the directory of $MYVIMRC + require("luasnip.loaders.from_snipmate").lazy_load({paths = "./snippets"}) +< + +Stuff to watch out for: + +- Using both `extends ` in `.snippets` and + `ls.filetype_extend("", {""})` leads to duplicate snippets. +- `${VISUAL}` will be replaced by `$TM_SELECTED_TEXT` to make the snippets + compatible with LuaSnip +- We do not implement eval using ` (backtick). This may be implemented in the + future. + + +LUA *luasnip-loaders-lua* + +Instead of adding all snippets via `add_snippets`, it’s possible to store +them in separate files and load all of those. The file-structure here is +exactly the supported snipmate-structure, e.g. `.lua` or `/*.lua` to +add snippets for the filetype ``. + +There are two ways to add snippets: + +- the files may return two lists of snippets, the snippets in the first are all + added as regular snippets, while the snippets in the second will be added as + autosnippets (both are the defaults, if a snippet defines a different + `snippetType`, that will have preference) +- snippets can also be appended to the global (only for these files - they are + not visible anywhere else) tables `ls_file_snippets` and + `ls_file_autosnippets`. This can be combined with a custom `snip_env` to define + and add snippets with one function call: + >lua + ls.setup({ + snip_env = { + s = function(...) + local snip = ls.s(...) + -- we can't just access the global `ls_file_snippets`, since it will be + -- resolved in the environment of the scope in which it was defined. + table.insert(getfenv(2).ls_file_snippets, snip) + end, + parse = function(...) + local snip = ls.parser.parse_snippet(...) + table.insert(getfenv(2).ls_file_snippets, snip) + end, + -- remaining definitions. + ... + }, + ... + }) + < + This is more flexible than the previous approach since the snippets don’t + have to be collected; they just have to be defined using the above `s` and + `parse`. + +As defining all of the snippet constructors (`s`, `c`, `t`, …) in every file +is rather cumbersome, LuaSnip will bring some globals into scope for executing +these files. By default, the names from `luasnip.config.snip_env` + +will be used, but it’s possible to customize them by setting `snip_env` in +`setup`. + +**Example**: + +`~/snippets/all.lua`: + +>lua + return { + s("trig", t("loaded!!")) + } +< + +`~/snippets/c.lua`: + +>lua + return { + s("ctrig", t("also loaded!!")) + }, { + s("autotrig", t("autotriggered, if enabled")) + } +< + +Load via + +>lua + require("luasnip.loaders.from_lua").load({paths = "~/snippets"}) +< + + +RELOADING WHEN EDITING REQUIRE’D FILES ~ + +While the lua-snippet-files will be reloaded on edit, this does not +automatically happen if a file the snippet-file depends on (eg. via `require`) +is changed. Since this still may still be desirable, there are two functions +exposed when a file is loaded by the lua-loader: `ls_tracked_dofile` and +`ls_tracked_dopackage`. They perform like `dofile` and (almost like) `require`, +but both register the loaded file internally as a dependency of the +snippet-file, so it can be reloaded when the loaded file is edited. As stated, +`ls_tracked_dofile` behaves exactly like `dofile`, but does the dependency-work +as well. `ls_tracked_dopackage` mimics `require` in that it does not take a +path, but a module-name like `"luasnip.loaders.from_lua"`, and then searches +the `runtimepath/lua`-directories, and path and cpath for the module. Unlike +`require`, the file will not be cached, since that would complicate the +reload-on-edit-behaviour. + + +EDIT_SNIPPETS *luasnip-loaders-edit_snippets* + +To easily edit snippets for the current session, the files loaded by any loader +can be quickly edited via +`require("luasnip.loaders").edit_snippet_files(opts:table|nil)` + +When called, it will open a `vim.ui.select`-dialog to select first a filetype, +and then (if there are multiple) the associated file to edit. + +`opts` contains four settings: + +- `ft_filter`: `fn(filetype:string) -> bool` Optionally filter initially listed + filetypes. `true` -> filetype will be listed, `false` -> not listed. Accepts + all filetypes by default. +- `format`: `fn(file:string, source_name:string) -> string|nil` `file` is simply + the path to the file, `source_name` is one of `"lua"`, `"snipmate"` or + `"vscode"`. If a string is returned, it is used as the title of the item, `nil` + on the other hand will filter out this item. The default simply replaces some + long strings (packer-path and config-path) in `file` with shorter, symbolic + names (`"$PLUGINS"`, `"$CONFIG"`), but this can be extended to + - filter files from some specific source/path + - more aggressively shorten paths using symbolic names, e.g. + `"$FRIENDLY_SNIPPETS"`. Example: hide the `*.lua` snippet files, and shorten + the path with `$LuaSnip`: + >lua + require "luasnip.loaders" .edit_snippet_files { + format = function(file, source_name) + if source_name == "lua" then return nil + else return file:gsub("/root/.config/nvim/luasnippets", "$LuaSnip") + end + end + } + < +- `edit`: `fn(file:string)` This function is supposed to open the file for + editing. The default is a simple `vim.cmd("edit " .. file)` (replace the + current buffer), but one could open the file in a split, a tab, or a floating + window, for example. +- `extend`: `fn(ft:string, ft_paths:string[]) -> (string,string)[]` This function + can be used to create additional choices for the file-selection. + - `ft`: The filetype snippet-files are queried for. + - `ft_paths`: list of paths to the known snippet files. + The function should return a list of `(string,string)`-tuples. The first of + each pair is the label that will appear in the selection-prompt, and the second + is the path that will be passed to the `edit()` function if that item was + selected. + This can be used to create a new snippet file for the current filetype: + >lua + require("luasnip.loaders").edit_snippet_files { + extend = function(ft, paths) + if #paths == 0 then + return { + { "$CONFIG/" .. ft .. ".snippets", + string.format("%s/%s.snippets", , ft) } + } + end + + return {} + end + } + < + +One comfortable way to call this function is registering it as a command: + +>vim + command! LuaSnipEdit :lua require("luasnip.loaders").edit_snippet_files() +< + + +============================================================================== +21. SnippetProxy *luasnip-snippetproxy* + +`SnippetProxy` is used internally to alleviate the upfront cost of loading +snippets from e.g. a SnipMate library or a VSCode package. This is achieved by +only parsing the snippet on expansion, not immediately after reading it from +some file. `SnippetProxy` may also be used from Lua directly to get the same +benefits: + +This will parse the snippet on startup: + +>lua + ls.parser.parse_snippet("trig", "a snippet $1!") +< + +while this will parse the snippet upon expansion: + +>lua + local sp = require("luasnip.nodes.snippetProxy") + sp("trig", "a snippet $1") +< + +`sp(context, body, opts) -> snippetProxy` + +- `context`: exactly the same as the first argument passed to `ls.s`. +- `body`: the snippet body. +- `opts`: accepts the same `opts` as `ls.s`, with some additions: + - `parse_fn`: the function for parsing the snippet. Defaults to + `ls.parser.parse_snippet` (the parser for LSP snippets), an alternative is + the parser for SnipMate snippets (`ls.parser.parse_snipmate`). + + +============================================================================== +22. ext_opts *luasnip-ext_opts* + +`ext_opts` can be used to set the `opts` (see `nvim_buf_set_extmark`) of the +extmarks used for marking node positions, either globally, per snippet or per +node. This means that they allow highlighting the text inside of nodes, or +adding virtual text to the line the node begins on. + +This is an example for the `node_ext_opts` used to set `ext_opts` of single +nodes: + +>lua + local ext_opts = { + -- these ext_opts are applied when the node is active (e.g. it has been + -- jumped into, and not out yet). + active = + -- this is the table actually passed to `nvim_buf_set_extmark`. + { + -- highlight the text inside the node red. + hl_group = "GruvboxRed" + }, + -- these ext_opts are applied when the node is not active, but + -- the snippet still is. + passive = { + -- add virtual text on the line of the node, behind all text. + virt_text = {{"virtual text!!", "GruvboxBlue"}} + }, + -- visited or unvisited are applied when a node was/was not jumped into. + visited = { + hl_group = "GruvboxBlue" + }, + unvisited = { + hl_group = "GruvboxGreen" + }, + -- and these are applied when both the node and the snippet are inactive. + snippet_passive = {} + } + + s("trig", { + i(1, "text1", { + node_ext_opts = ext_opts + }), + i(2, "text2", { + node_ext_opts = ext_opts + }) + }) +< + +In the above example, the text inside the insertNodes is higlighted in green if +they were not yet visited, in blue once they were, and red while they are. The +virtual text "virtual text!!" is visible as long as the snippet is active. + +To make defining `ext_opts` less verbose, more specific states inherit from +less specific ones: + +- `passive` inherits from `snippet_passive` +- `visited` and `unvisited` from `passive` +- `active` from `visited` + +To disable a key from a less specific state, it has to be explicitly set to its +default, e.g. to disable highlighting inherited from `passive` when the node +is `active`, `hl_group` should be set to `None`. + +------------------------------------------------------------------------------ +As stated earlier, these `ext_opts` can also be applied globally or for an +entire snippet. For this, it’s necessary to specify which kind of node a +given set of `ext_opts` should be applied to: + +>lua + local types = require("luasnip.util.types") + + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = {...}, + visited = {...}, + passive = {...}, + snippet_passive = {...} + }, + [types.choiceNode] = { + active = {...}, + unvisited = {...} + }, + [types.snippet] = { + passive = {...} + } + } + }) +< + +The above applies the given `ext_opts` to all nodes of these types, in all +snippets. + +>lua + local types = require("luasnip.util.types") + + s("trig", { i(1, "text1"), i(2, "text2") }, { + child_ext_opts = { + [types.insertNode] = { + passive = { + hl_group = "GruvboxAqua" + } + } + } + }) +< + +However, the `ext_opts` here are only applied to the `insertNodes` inside this +snippet. + +------------------------------------------------------------------------------ +By default, the `ext_opts` actually used for a node are created by extending +the `node_ext_opts` with the `effective_child_ext_opts[node.type]` of the +parent, which are in turn the parent’s `child_ext_opts` extended with the +global `ext_opts` (those set `ls.setup`). + +It’s possible to prevent both of these merges by passing +`merge_node/child_ext_opts=false` to the snippet/node-opts: + +>lua + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = {...} + } + } + }) + + s("trig", { + i(1, "text1", { + node_ext_opts = { + active = {...} + }, + merge_node_ext_opts = false + }), + i(2, "text2") + }, { + child_ext_opts = { + [types.insertNode] = { + passive = {...} + } + }, + merge_child_ext_opts = false + }) +< + +------------------------------------------------------------------------------ +The `hl_group` of the global `ext_opts` can also be set via standard highlight +groups: + +>lua + vim.cmd("hi link LuasnipInsertNodePassive GruvboxRed") + vim.cmd("hi link LuasnipSnippetPassive GruvboxBlue") + + -- needs to be called for resolving the effective ext_opts. + ls.setup({}) +< + +The names for the used highlight groups are +`"Luasnip{Passive,Active,SnippetPassive}"`, where `` can be any +kind of node in PascalCase (or "Snippet"). + +------------------------------------------------------------------------------ +One problem that might arise when nested nodes are highlighted is that the +highlight of inner nodes should be visible, e.g. above that of nodes they are +nested inside. + +This can be controlled using the `priority`-key in `ext_opts`. In +`nvim_buf_set_extmark`, that value is an absolute value, but here it is +relative to some base-priority, which is increased for each nesting level of +snippet(Nodes)s. + +Both the initial base-priority and its’ increase and can be controlled using +`ext_base_prio` and `ext_prio_increase`: + +>lua + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = { + hl_group = "GruvboxBlue", + -- the priorities should be \in [0, ext_prio_increase). + priority = 1 + } + }, + [types.choiceNode] = { + active = { + hl_group = "GruvboxRed" + -- priority defaults to 0 + } + } + } + ext_base_prio = 200, + ext_prio_increase = 2 + }) +< + +Here the highlight of an insertNode nested directly inside a choiceNode is +always visible on top of it. + + +============================================================================== +23. Docstrings *luasnip-docstrings* + +Snippet docstrings can be queried using `snippet:get_docstring()`. The function +evaluates the snippet as if it was expanded regularly, which can be problematic +if e.g. a dynamicNode in the snippet relies on inputs other than the argument +nodes. `snip.env` and `snip.captures` are populated with the names of the +queried variable and the index of the capture respectively +(`snip.env.TM_SELECTED_TEXT` -> `'$TM_SELECTED_TEXT'`, `snip.captures[1]` -> +`'$CAPTURES1'`). Although this leads to more expressive docstrings, it can +cause errors in functions that e.g. rely on a capture being a number: + +>lua + s({trig = "(%d)", regTrig = true}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +This snippet works fine because `snippet.captures[1]` is always a number. +During docstring generation, however, `snippet.captures[1]` is `'$CAPTURES1'`, +which will cause an error in the functionNode. Issues with `snippet.captures` +can be prevented by specifying `docTrig` during snippet-definition: + +>lua + s({trig = "(%d)", regTrig = true, docTrig = "3"}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +`snippet.captures` and `snippet.trigger` will be populated as if actually +triggered with `3`. + +Other issues will have to be handled manually by checking the contents of e.g. +`snip.env` or predefining the docstring for the snippet: + +>lua + s({trig = "(%d)", regTrig = true, docstring = "repeatmerepeatmerepeatme"}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +Refer to #515 for a better +example to understand `docTrig` and `docstring`. + + +============================================================================== +24. Docstring-Cache *luasnip-docstring-cache* + +Although generation of docstrings is pretty fast, it’s preferable to not redo +it as long as the snippets haven’t changed. Using +`ls.store_snippet_docstrings(snippets)` and its counterpart +`ls.load_snippet_docstrings(snippets)`, they may be serialized from or +deserialized into the snippets. Both functions accept a table structsured like +this: `{ft1={snippets}, ft2={snippets}}`. Such a table containing all snippets +can be obtained via `ls.get_snippets()`. `load` should be called before any of +the `loader`-functions as snippets loaded from VSCode style packages already +have their `docstring` set (`docstrings` wouldn’t be overwritten, but +there’d be unnecessary calls). + +The cache is located at `stdpath("cache")/luasnip/docstrings.json` (probably +`~/.cache/nvim/luasnip/docstrings.json`). + + +============================================================================== +25. Events *luasnip-events* + +Events can be used to react to some action inside snippets. These callbacks can +be defined per snippet (`callbacks`-key in snippet constructor), per-node by +passing them as `node_callbacks` in `node_opts`, or globally (autocommand). + +`callbacks`: `fn(node[, event_args]) -> event_res` All callbacks receive the +`node` associated with the event and event-specific optional arguments, +`event_args`. `event_res` is only used in one event, `pre_expand`, where some +properties of the snippet can be changed. If multiple callbacks return +`event_res`, we only guarantee that one of them will be effective, not all of +them. + +`autocommand`: Luasnip uses `User`-events. Autocommands for these can be +registered using + +>vim + au User SomeUserEvent echom "SomeUserEvent was triggered" +< + +or + +>lua + vim.api.nvim_create_autocommand("User", { + patter = "SomeUserEvent", + command = "echom SomeUserEvent was triggered" + }) +< + +The node and `event_args` can be accessed through `require("luasnip").session`: + +- `node`: `session.event_node` +- `event_args`: `session.event_args` + +**Events**: + +- `enter/leave`: Called when a node is entered/left (for example when jumping + around in a snippet). + `User-event`: `"Luasnip{Enter,Leave}"`, with `` in + PascalCase, e.g. `InsertNode` or `DynamicNode`. + `event_args`: none +- `change_choice`: When the active choice in a choiceNode is changed. + `User-event`: `"LuasnipChangeChoice"` + `event_args`: none +- `pre_expand`: Called before a snippet is expanded. Modifying text is allowed, + the expand-position will be adjusted so the snippet expands at the same + position relative to existing text. + `User-event`: `"LuasnipPreExpand"` + `event_args`: + - `expand_pos`: `{, }`, position at which the snippet will be + expanded. `` and `` are both 0-indexed. + `event_res`: + - `env_override`: `map string->(string[]|string)`, override or extend the + snippet’s environment (`snip.env`). + +A pretty useless, beyond serving as an example here, application of these would +be printing e.g. the node’s text after entering: + +>lua + vim.api.nvim_create_autocmd("User", { + pattern = "LuasnipInsertNodeEnter", + callback = function() + local node = require("luasnip").session.event_node + print(table.concat(node:get_text(), "\n")) + end + }) +< + +or some information about expansions: + +>lua + vim.api.nvim_create_autocmd("User", { + pattern = "LuasnipPreExpand", + callback = function() + -- get event-parameters from `session`. + local snippet = require("luasnip").session.event_node + local expand_position = + require("luasnip").session.event_args.expand_pos + + print(string.format("expanding snippet %s at %s:%s", + table.concat(snippet:get_docstring(), "\n"), + expand_position[1], + expand_position[2] + )) + end + }) +< + + +============================================================================== +26. Cleanup *luasnip-cleanup* + +The function ls.cleanup() triggers the `LuasnipCleanup` user event, that you +can listen to do some kind of cleaning in your own snippets; by default it will +empty the snippets table and the caches of the lazy_load. + + +============================================================================== +27. Logging *luasnip-logging* + +Luasnip uses logging to report unexpected program states, and information on +what’s going on in general. If something does not work as expected, taking a +look at the log (and potentially increasing the loglevel) might give some good +hints towards what is going wrong. + +The log is stored in `/luasnip.log` +(`/luasnip.log` for Neovim versions where +`stdpath("log")` does not exist), and can be opened by calling `ls.log.open()`. +The loglevel (granularity of reported events) can be adjusted by calling +`ls.log.set_loglevel("error"|"warn"|"info"|"debug")`. `"debug"` has the highest +granularity, `"error"` the lowest, the default is `"warn"`. + +Once this log grows too large (10MiB, currently not adjustable), it will be +renamed to `luasnip.log.old`, and a new, empty log created in its place. If +there already exists a `luasnip.log.old`, it will be deleted. + +`ls.log.ping()` can be used to verify the log is working correctly: it will +print a short message to the log. + + +============================================================================== +28. Source *luasnip-source* + +It is possible to attach, to a snippet, information about its source. This can +be done either by the various loaders (if it is enabled in `ls.setup` +(|luasnip-config-options|, `loaders_store_source`)), or manually. The attached +data can be used by |luasnip-extras-snippet-location| to jump to the definition +of a snippet. + +It is also possible to get/set the source of a snippet via API: + +`ls.snippet_source`: + +- `get(snippet) -> source_data`: + Retrieve the source-data of `snippet`. `source_data` always contains the key + `file`, the file in which the snippet was defined, and may additionally + contain `line` or `line_end`, the first and last line of the definition. +- `set(snippet, source)`: + Set the source of a snippet. + - `snippet`: a snippet which was added via `ls.add_snippets`. + - `source`: a `source`-object, obtained from either `from_debuginfo` or + `from_location`. +- `from_location(file, opts) -> source`: + - `file`: `string`, The path to the file in which the snippet is defined. + - `opts`: `table|nil`, optional parameters for the source. + - `line`: `number`, the first line of the definition. 1-indexed. + - `line_end`: `number`, the final line of the definition. 1-indexed. +- `from_debuginfo(debuginfo) -> source`: + Generates source from the table returned by `debug.getinfo` (from now on + referred to as `debuginfo`). `debuginfo` has to be of a frame of a function + which is backed by a file, and has to contain this information, ie. has to be + generated by `debug.get_info(*, "Sl")` (at least `"Sl"`, it may also contain + more info). + + +============================================================================== +29. Config-Options *luasnip-config-options* + +These are the settings you can provide to `luasnip.setup()`: + +- `keep_roots`: Whether snippet-roots should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `link_roots`: Whether snippet-roots should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `exit_roots`: Whether snippet-roots should exit at reaching at their last node, + `$0`. This setting is only valid for root snippets, not child snippets. This + setting may avoid unexpected behavior by disallowing to jump earlier (finished) + snippets. Check |luasnip-basics-snippet-insertion| for more information on + snippet-roots. +- `link_children`: Whether children should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `history` (deprecated): if not nil, `keep_roots`, `link_roots`, and + `link_children` will be set to the value of `history`, and `exit_roots` will + set to inverse value of `history`. This is just to ensure + backwards-compatibility. +- `update_events`: Choose which events trigger an update of the active nodes’ + dependents. Default is just `'InsertLeave'`, `'TextChanged,TextChangedI'` would + update on every change. These, like all other `*_events` are passed to + `nvim_create_autocmd` as `events`, so they can be wrapped in a table, like + >lua + ls.setup({ + update_events = {"TextChanged", "TextChangedI"} + }) + < +- `region_check_events`: Events on which to leave the current snippet-root if the + cursor is outside its’ 'region'. Disabled by default, `'CursorMoved'`, + `'CursorHold'` or `'InsertEnter'` seem reasonable. +- `delete_check_events`: When to check if the current snippet was deleted, and if + so, remove it from the history. Off by default, `'TextChanged'` (perhaps + `'InsertLeave'`, to react to changes done in Insert mode) should work just fine + (alternatively, this can also be mapped using `luasnip-delete-check`). +- `store_selection_keys`: Mapping for populating `TM_SELECTED_TEXT` and related + variables (not set by default). If you want to set this mapping yourself, map + `ls.select_keys` (not a function, actually a string/key-combination) as a rhs. +- `enable_autosnippets`: Autosnippets are disabled by default to minimize + performance penalty if unused. Set to `true` to enable. +- `ext_opts`: Additional options passed to extmarks. Can be used to add + passive/active highlight on a per-node-basis (more info in DOC.md) +- `parser_nested_assembler`: Override the default behaviour of inserting a + `choiceNode` containing the nested snippet and an empty `insertNode` for nested + placeholders (`"${1: ${2: this is nested}}"`). For an example (behaviour more + similar to vscode), check here + +- `ft_func`: Source of possible filetypes for snippets. Defaults to a function, + which returns `vim.split(vim.bo.filetype, ".", true)`, but check + filetype_functions or the + |luasnip-extras-filetype-functions|-section for more options. +- `load_ft_func`: Function to determine which filetypes belong to a given buffer + (used for `lazy_loading`). `fn(bufnr) -> filetypes (string[])`. Again, there + are some examples in filetype_functions + . +- `snip_env`: The best way to author snippets in lua involves the lua-loader (see + |luasnip-loaders-lua|). Unfortunately, this requires that snippets are defined + in separate files, which means that common definitions like `s`, `i`, `sn`, + `t`, `fmt`, … have to be repeated in each of them, and that adding more + customized functions to ease writing snippets also requires some setup. + `snip_env` can be used to insert variables into exactly the places where + lua-snippets are defined (for now only the file loaded by the lua-loader). + Setting `snip_env` to `{ some_global = "a value" }` will add (amongst the + defaults stated at the beginning of this documentation) the global variable + `some_global` while evaluating these files. There are special keys which, when + set in `snip_env` change the behaviour of this option, and are not passed + through to the lua-files: + - `__snip_env_behaviour`, string: either `"set"` or `"extend"` (default + `"extend"`) + If this is `"extend"`, the variables defined in `snip_env` will complement (and + override) the defaults. If this is not desired, `"set"` will not include the + defaults, but only the variables set here. + One side-effect of this is that analysis-tools (most likely + `lua-language-server`) for lua will generate diagnostics for the usage of + undefined symbols. If you mind the (probably) large number of generated + warnings, consider adding the undefined globals to the globals recognized by + `lua-language-server` or add `---@diagnostic disable: undefined-global` + somewhere in the affected files. +- `loaders_store_source`, boolean, whether loaders should store the source of the + loaded snippets. Enabling this means that the definition of any snippet can be + jumped to via |luasnip-extras-snippet-location|, but also entails slightly + increased memory consumption (and load-time, but it’s not really noticeable). + + +============================================================================== +30. Troubleshooting *luasnip-troubleshooting* + + +ADDING SNIPPETS *luasnip-troubleshooting-adding-snippets* + +### Loaders + +- **Filetypes**. LuaSnip uses `all` as the global filetype. As most snippet + collections don’t explicitly target LuaSnip, they may not provide global + snippets for this filetype, but another, like `_` (`honza/vim-snippets`). In + these cases, it’s necessary to extend LuaSnip’s global filetype with the + collection’s global filetype: + >lua + ls.filetype_extend("all", { "_" }) + < + In general, if some snippets don’t show up when loading a collection, a good + first step is checking the filetype LuaSnip is actually looking into (print + them for the current buffer via `:lua + print(vim.inspect(require("luasnip").get_snippet_filetypes()))`), against the + one the missing snippet is provided for (in the collection). If there is indeed + a mismatch, `filetype_extend` can be used to also search the collection’s + filetype: + >lua + ls.filetype_extend("", { "" }) + < +- **Non-default ft_func loading**. As we only load `lazy_load`ed snippets on some + events, `lazy_load` will probably not play nice when a non-default `ft_func` is + used: if it depends on e.g. the cursor position, only the filetypes for the + cursor position when the `lazy_load` events are triggered will be loaded. Check + |luasnip-extras-filetype-function|’s `extend_load_ft` for a solution. + + +GENERAL ~ + +- **Snippets sharing triggers**. If multiple snippets could be triggered at the + current buffer-position, the snippet that was defined first in one’s + configuration will be expanded first. As a small, real-world LaTeX math + example, given the following two snippets with triggers `.ov` and `ov`: + >lua + postfix( -- Insert over-line command to text via post-fix + { trig = ".ov", snippetType = "autosnippet" }, + { + f(function(_, parent) + return "\\overline{" .. parent.snippet.env.POSTFIX_MATCH .. "}" + end, {}), + } + ), + s( -- Insert over-line command + { trig = "ov", snippetType="autosnippet" }, + fmt( + [[\overline{<>}]], + { i(1) }, + { delimiters = "<>" } + ) + ), + < + If one types `x` followed by `.ov`, the postfix snippet expands producing + `\overline{x}`. However, if the `postfix` snippet above is defined _after_ the + normal snippet `s`, then the same key press sequence produces `x.\overline{}`. + This behaviour can be overridden by explicitly providing a priority to such + snippets. For example, in the above code, if the `postfix` snippet was defined + after the normal snippet `s`, then adding `priority=1001` to the `postfix` + snippet will cause it to expand as if it were defined before the normal snippet + `s`. Snippet `priority` is discussed in the Snippets section + of the + documentation. + + +============================================================================== +31. API *luasnip-api* + +`require("luasnip")`: + +- `add_snippets(ft:string or nil, snippets:list or table, opts:table or nil)`: + Makes `snippets` (list of snippets) available in `ft`. If `ft` is `nil`, + `snippets` should be a table containing lists of snippets, the keys are + corresponding filetypes. `opts` may contain the following keys: + - `type`: type of `snippets`, `"snippets"` or `"autosnippets"` (ATTENTION: + plural form used here). This serves as default value for the `snippetType` + key of each snippet added by this call see |luasnip-snippets|. + - `key`: Key that identifies snippets added via this call. + If `add_snippets` is called with a key that was already used, the snippets + from that previous call will be removed. + This can be used to reload snippets: pass an unique key to each + `add_snippets` and just redo the `add_snippets`-call when the snippets have + changed. + - `override_priority`: set priority for all snippets. + - `default_priority`: set priority only for snippets without snippet priority. +- `clean_invalidated(opts: table or nil) -> bool`: clean invalidated snippets + from internal snippet storage. Invalidated snippets are still stored; it might + be useful to actually remove them as they still have to be iterated during + expansion. + `opts` may contain: + - `inv_limit`: how many invalidated snippets are allowed. If the number of + invalid snippets doesn’t exceed this threshold, they are not yet cleaned up. + A small number of invalidated snippets (<100) probably doesn’t affect runtime + at all, whereas recreating the internal snippet storage might. +- `get_id_snippet(id)`: returns snippet corresponding to id. +- `in_snippet()`: returns true if the cursor is inside the current snippet. +- `jumpable(direction)`: returns true if the current node has a next(`direction` + = 1) or previous(`direction` = -1), e.g. whether it’s possible to jump + forward or backward to another node. +- `jump(direction)`: returns true if the jump was successful. +- `expandable()`: true if a snippet can be expanded at the current cursor + position. +- `expand(opts)`: expands the snippet at(before) the cursor. `opts` may contain: + - `jump_into_func` passed through to `ls.snip_expand`, check its’ doc for a + description. +- `expand_or_jumpable()`: returns `expandable() or jumpable(1)` (exists only + because commonly, one key is used to both jump forward and expand). +- `expand_or_locally_jumpable()`: same as `expand_or_jumpable()` except jumpable + is ignored if the cursor is not inside the current snippet. +- `locally_jumpable(direction)`: same as `jumpable()` except it is ignored if the + cursor is not inside the current snippet. +- `expand_or_jump()`: returns true if jump/expand was succesful. +- `expand_auto()`: expands the autosnippets before the cursor (not necessary to + call manually, will be called via autocmd if `enable_autosnippets` is set in + the config). +- `snip_expand(snip, opts)`: expand `snip` at the current cursor position. `opts` + may contain the following keys: + - `clear_region`: A region of text to clear after expanding (but before jumping + into) snip. It has to be at this point (and therefore passed to this function) + as clearing before expansion will populate `TM_CURRENT_LINE` and + `TM_CURRENT_WORD` with wrong values (they would miss the snippet trigger) and + clearing after expansion may move the text currently under the cursor and have + it end up not at the `i(1)`, but a `#trigger` chars to its right. The actual + values used for clearing are `from` and `to`, both (0,0)-indexed + byte-positions. If the variables don’t have to be populated with the correct + values, it’s safe to remove the text manually. + - `expand_params`: table, override `trigger`, `captures` or environment of the + snippet. This is useful for manually expanding snippets where the trigger + passed via `trig` is not the text triggering the snippet, or those which expect + `captures` (basically, snippets with a non-plaintext `trigEngine`). + One example: ```lua snip_expand(snip, { trigger = "override_trigger", captures + = {"first capture", "second capture"}, env_override = { this_key = "some + value", other_key = {"multiple", "lines"}, TM_FILENAME = + "some_other_filename.lua" } }) + - `pos`: position (`{line, col}`), (0,0)-indexed (in bytes, as returned by + `nvim_win_get_cursor()`), where the snippet should be expanded. The snippet + will be put between `(line,col-1)` and `(line,col)`. The snippet will be + expanded at the current cursor if pos is nil. + - `jump_into_func`: fn(snippet) -> node: Callback responsible for jumping into + the snippet. The returned node is set as the new active node, ie. it is the + origin of the next jump. The default is basically this: `lua function(snip) -- + jump_into set the placeholder of the snippet, 1 -- to jump forwards. return + snip:jump_into(1)` while this can be used to only insert the snippet: `lua + function(snip) return snip.insert_nodes[0] end` + - `indent`: bool?, defaults to `true`. Whether LuaSnip will try to add additional + indents to fit current indent level in snippet expanding. This option is useful + when some LSP server already take indents into consideration. In such cases, + LuaSnip should not try to add additional indents. If you are using `nvim-cmp`, + sample config: + >lua + require("cmp").setup { + snippet = { + expand = function(args) + local indent_nodes = true + if vim.api.nvim_get_option_value("filetype", { buf = 0 }) == "dart" then + indent_nodes = false + end + require("luasnip").lsp_expand(args.body, { + indent = indent_nodes, + }) + end, + }, + } + < + `opts` and any of its parameters may be nil. +- `get_active_snip()`: returns the currently active snippet (not node!). +- `choice_active()`: true if inside a choiceNode. +- `change_choice(direction)`: changes the choice in the innermost currently + active choiceNode forward (`direction` = 1) or backward (`direction` = -1). +- `unlink_current()`: removes the current snippet from the jumplist (useful if + luasnip fails to automatically detect e.g. deletion of a snippet) and sets the + current node behind the snippet, or, if not possible, before it. +- `lsp_expand(snip_string, opts)`: expands the LSP snippet defined via + `snip_string` at the cursor. `opts` can have the same options as `opts` in + `snip_expand`. +- `active_update_dependents()`: update all function/dynamicNodes that have the + current node as an argnode (will actually only update them if the text in any + of the argnodes changed). +- `available(snip_info)`: returns a table of all snippets defined for the current + filetypes(s) (`{ft1={snip1, snip2}, ft2={snip3, snip4}}`). The structure of the + snippet is defined by `snip_info` which is a function (`snip_info(snip)`) that + takes in a snippet (`snip`), finds the desired information on it, and returns + it. `snip_info` is an optional argument as a default has already been defined. + You can use it for more granular control over the table of snippets that is + returned. +- `exit_out_of_region(node)`: checks whether the cursor is still within the range + of the root-snippet `node` belongs to. If yes, no change occurs; if no, the + root-snippet is exited and its `$0` will be the new active node. If a jump + causes an error (happens mostly because the text of a snippet was deleted), the + snippet is removed from the jumplist and the current node set to the + end/beginning of the next/previous snippet. +- `store_snippet_docstrings(snippet_table)`: Stores the docstrings of all + snippets in `snippet_table` to a file + (`stdpath("cache")/luasnip/docstrings.json`). Calling + `store_snippet_docstrings(snippet_table)` after adding/modifying snippets and + `load_snippet_docstrings(snippet_table)` on startup after all snippets have + been added to `snippet_table` is a way to avoide regenerating the (unchanged) + docstrings on each startup. (Depending on when the docstrings are required and + how luasnip is loaded, it may be more sensible to let them load lazily, + e.g. just before they are required). `snippet_table` should be laid out just + like `luasnip.snippets` (it will most likely always _be_ `luasnip.snippets`). +- `load_snippet_docstrings(snippet_table)`: Load docstrings for all snippets in + `snippet_table` from `stdpath("cache")/luasnip/docstrings.json`. The docstrings + are stored and restored via trigger, meaning if two snippets for one filetype + have the same (very unlikely to happen in actual usage), bugs could occur. + `snippet_table` should be laid out as described in `store_snippet_docstrings`. +- `unlink_current_if_deleted()`: Checks if the current snippet was deleted; if + so, it is removed from the jumplist. This is not 100% reliable as LuaSnip only + sees the extmarks and their beginning/end may not be on the same position, even + if all the text between them was deleted. +- `filetype_extend(filetype:string, extend_filetypes:table of string)`: Tells + luasnip that for a buffer with `ft=filetype`, snippets from `extend_filetypes` + should be searched as well. `extend_filetypes` is a lua-array (`{ft1, ft2, + ft3}`). `luasnip.filetype_extend("lua", {"c", "cpp"})` would search and expand + c and cpp snippets for lua files. +- `filetype_set(filetype:string, replace_filetypes:table of string)`: Similar to + `filetype_extend`, but where _append_ appended filetypes, _set_ sets them: + `filetype_set("lua", {"c"})` causes only c snippets to be expanded in lua + files; lua snippets aren’t even searched. +- `cleanup()`: clears all snippets. Not useful for regular usage, only when + authoring and testing snippets. +- `refresh_notify(ft:string)`: Triggers an autocmd that other plugins can hook + into to perform various cleanup for the refreshed filetype. Useful for + signaling that new snippets were added for the filetype `ft`. +- `set_choice(indx:number)`: Changes to the `indx`th choice. If no `choiceNode` + is active, an error is thrown. If the active `choiceNode` doesn’t have an + `indx`th choice, an error is thrown. +- `get_current_choices() -> string[]`: Returns a list of multiline-strings + (themselves lists, even if they have only one line), the `i`th string + corresponding to the `i`th choice of the currently active `choiceNode`. If no + `choiceNode` is active, an error is thrown. +- `setup_snip_env()`: Adds the variables defined (during `setup`) in `snip_env` + to the callers environment. +- `get_snip_env()`: Returns `snip_env`. +- `jump_destination(direction)`: Returns the node the next jump in `direction` + (either -1 or 1, for backwards, forwards respectively) leads to, or `nil` if + the destination could not be determined (most likely because there is no node + that can be jumped to in the given direction, or there is no active node). +- `activate_node(opts)`: Activate a node in any snippet. `opts` contains the + following options: + - `pos`, `{[1]: row, [2]: byte-column}?`: The position at which a node should + be activated. Defaults to the position of the cursor. + - `strict`, `bool?`: If set, throw an error if the node under the cursor can’t + be jumped into. If not set, fall back to any node of the snippet and enter + that instead. + - `select`, `bool?`: Whether the text inside the node should be selected. + Defaults to true. + +Not covered in this section are the various node-constructors exposed by the +module, their usage is shown either previously in this file or in +`Examples/snippets.lua` (in the repo). + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/luasnip/doc/tags b/config/neovim/store/lazy-plugins/luasnip/doc/tags new file mode 100644 index 00000000..3c6f662a --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/doc/tags @@ -0,0 +1,63 @@ +luasnip-absolute-indexer luasnip.txt /*luasnip-absolute-indexer* +luasnip-api luasnip.txt /*luasnip-api* +luasnip-basics luasnip.txt /*luasnip-basics* +luasnip-basics-adding-snippets luasnip.txt /*luasnip-basics-adding-snippets* +luasnip-basics-jump-index luasnip.txt /*luasnip-basics-jump-index* +luasnip-basics-snippet-insertion luasnip.txt /*luasnip-basics-snippet-insertion* +luasnip-choicenode luasnip.txt /*luasnip-choicenode* +luasnip-cleanup luasnip.txt /*luasnip-cleanup* +luasnip-config-options luasnip.txt /*luasnip-config-options* +luasnip-docstring-cache luasnip.txt /*luasnip-docstring-cache* +luasnip-docstrings luasnip.txt /*luasnip-docstrings* +luasnip-dynamicnode luasnip.txt /*luasnip-dynamicnode* +luasnip-events luasnip.txt /*luasnip-events* +luasnip-ext_opts luasnip.txt /*luasnip-ext_opts* +luasnip-extend-decorator luasnip.txt /*luasnip-extend-decorator* +luasnip-extras luasnip.txt /*luasnip-extras* +luasnip-extras-conditions luasnip.txt /*luasnip-extras-conditions* +luasnip-extras-dynamic-lambda luasnip.txt /*luasnip-extras-dynamic-lambda* +luasnip-extras-filetype-functions luasnip.txt /*luasnip-extras-filetype-functions* +luasnip-extras-fmt luasnip.txt /*luasnip-extras-fmt* +luasnip-extras-lambda luasnip.txt /*luasnip-extras-lambda* +luasnip-extras-match luasnip.txt /*luasnip-extras-match* +luasnip-extras-nonempty luasnip.txt /*luasnip-extras-nonempty* +luasnip-extras-on-the-fly-snippets luasnip.txt /*luasnip-extras-on-the-fly-snippets* +luasnip-extras-partial luasnip.txt /*luasnip-extras-partial* +luasnip-extras-postfix-snippet luasnip.txt /*luasnip-extras-postfix-snippet* +luasnip-extras-repeat luasnip.txt /*luasnip-extras-repeat* +luasnip-extras-select_choice luasnip.txt /*luasnip-extras-select_choice* +luasnip-extras-snippet-list luasnip.txt /*luasnip-extras-snippet-list* +luasnip-extras-snippet-location luasnip.txt /*luasnip-extras-snippet-location* +luasnip-extras-treesitter-postfix-snippet luasnip.txt /*luasnip-extras-treesitter-postfix-snippet* +luasnip-functionnode luasnip.txt /*luasnip-functionnode* +luasnip-indentsnippetnode luasnip.txt /*luasnip-indentsnippetnode* +luasnip-insertnode luasnip.txt /*luasnip-insertnode* +luasnip-key-indexer luasnip.txt /*luasnip-key-indexer* +luasnip-loaders luasnip.txt /*luasnip-loaders* +luasnip-loaders-edit_snippets luasnip.txt /*luasnip-loaders-edit_snippets* +luasnip-loaders-lua luasnip.txt /*luasnip-loaders-lua* +luasnip-loaders-snipmate luasnip.txt /*luasnip-loaders-snipmate* +luasnip-loaders-snippet-specific-filetypes luasnip.txt /*luasnip-loaders-snippet-specific-filetypes* +luasnip-loaders-vs-code luasnip.txt /*luasnip-loaders-vs-code* +luasnip-logging luasnip.txt /*luasnip-logging* +luasnip-lsp-snippets luasnip.txt /*luasnip-lsp-snippets* +luasnip-lsp-snippets-snipmate-parser luasnip.txt /*luasnip-lsp-snippets-snipmate-parser* +luasnip-lsp-snippets-transformations luasnip.txt /*luasnip-lsp-snippets-transformations* +luasnip-multisnippet luasnip.txt /*luasnip-multisnippet* +luasnip-node luasnip.txt /*luasnip-node* +luasnip-node-api luasnip.txt /*luasnip-node-api* +luasnip-node-reference luasnip.txt /*luasnip-node-reference* +luasnip-restorenode luasnip.txt /*luasnip-restorenode* +luasnip-snippetnode luasnip.txt /*luasnip-snippetnode* +luasnip-snippetproxy luasnip.txt /*luasnip-snippetproxy* +luasnip-snippets luasnip.txt /*luasnip-snippets* +luasnip-snippets-data luasnip.txt /*luasnip-snippets-data* +luasnip-source luasnip.txt /*luasnip-source* +luasnip-table-of-contents luasnip.txt /*luasnip-table-of-contents* +luasnip-textnode luasnip.txt /*luasnip-textnode* +luasnip-troubleshooting luasnip.txt /*luasnip-troubleshooting* +luasnip-troubleshooting-adding-snippets luasnip.txt /*luasnip-troubleshooting-adding-snippets* +luasnip-variables luasnip.txt /*luasnip-variables* +luasnip-variables-environment-namespaces luasnip.txt /*luasnip-variables-environment-namespaces* +luasnip-variables-lsp-variables luasnip.txt /*luasnip-variables-lsp-variables* +luasnip.txt luasnip.txt /*luasnip.txt* diff --git a/config/neovim/store/lazy-plugins/luasnip/ftplugin/snippets.vim b/config/neovim/store/lazy-plugins/luasnip/ftplugin/snippets.vim new file mode 100644 index 00000000..b13cc8af --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/ftplugin/snippets.vim @@ -0,0 +1,14 @@ +" Vim filetype plugin for SnipMate snippets (.snippets files) + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setl et< sts< cms< fdm< fde<" + +" Use hard tabs +setlocal noexpandtab softtabstop=0 + +setlocal commentstring=#\ %s +setlocal nospell diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/_types.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/_types.lua new file mode 100644 index 00000000..ec839c87 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/_types.lua @@ -0,0 +1,8 @@ +---@alias LuaSnip.Cursor {[1]: number, [2]: number} + +---@class LuaSnip.MatchRegion 0-based region +---@field row integer 0-based row +---@field col_range { [1]: integer, [2]: integer } 0-based column range, from-in, to-exclusive + +---@alias LuaSnip.Addable table +---Anything that can be passed to ls.add_snippets(). diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/config.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/config.lua new file mode 100644 index 00000000..aaaaa9c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/config.lua @@ -0,0 +1,143 @@ +local ext_util = require("luasnip.util.ext_opts") +local session = require("luasnip.session") +local conf_defaults = require("luasnip.default_config") + +local function set_snip_env(target_conf_defaults, user_config) + if not user_config.snip_env then + -- target_conf already contains defaults + return + end + + -- either "set" or "extend", make sure it does not appear in the final snip_env. + local snip_env_behaviour = user_config.snip_env.__snip_env_behaviour ~= nil + and user_config.snip_env.__snip_env_behaviour + or "extend" + assert( + snip_env_behaviour == "set" or snip_env_behaviour == "extend", + "Unknown __snip_env_behaviour, `" .. snip_env_behaviour .. "`" + ) + user_config.snip_env.__snip_env_behaviour = nil + + if snip_env_behaviour == "set" then + target_conf_defaults.snip_env = user_config.snip_env + else + -- cannot use vim.tbl_extend, since we'd need to transfer the metatable. + for k, v in pairs(user_config.snip_env) do + target_conf_defaults.snip_env[k] = v + end + end + + -- set to nil, to mark that it's handled. + user_config.snip_env = nil +end + +-- declare here to use in set_config. +local c + +c = { + set_config = function(user_config) + user_config = user_config or {} + local conf = vim.deepcopy(conf_defaults) + + -- remove unused highlights from default-ext_opts. + ext_util.clear_invalid(conf.ext_opts) + conf.ext_opts = ext_util.child_complete(conf.ext_opts) + user_config.ext_opts = + ext_util.child_complete(user_config.ext_opts or {}) + ext_util.child_extend(user_config.ext_opts, conf.ext_opts) + + -- use value from update_events, then updateevents. + -- also nil updateevents, don't spill it into the main config. + user_config.update_events = user_config.update_events + or user_config.updateevents + user_config.updateevents = nil + + set_snip_env(conf, user_config) + + -- handle legacy-key history. + if user_config.history ~= nil then + conf.keep_roots = user_config.history + conf.link_roots = user_config.history + conf.exit_roots = not user_config.history + conf.link_children = user_config.history + + -- unset key to prevent handling twice. + conf.history = nil + end + + for k, v in pairs(user_config) do + conf[k] = v + end + + session.config = conf + c._setup() + end, + + _setup = function() + local augroup = vim.api.nvim_create_augroup("luasnip", {}) + + -- events: string[], or string. if string[], each string is one + -- event-name, if string, either one event-name, or multiple delimited by `,`. + local function ls_autocmd(events, callback) + if type(events) == "string" then + -- split on ',' for backwards compatibility. + -- remove spaces from string. + events = vim.split(events:gsub(" ", ""), ",") + end + vim.api.nvim_create_autocmd(events, { + callback = callback, + group = augroup, + }) + end + if session.config.delete_check_events ~= nil then + ls_autocmd( + session.config.delete_check_events, + require("luasnip").unlink_current_if_deleted + ) + end + ls_autocmd( + session.config.update_events, + require("luasnip").active_update_dependents + ) + if session.config.region_check_events ~= nil then + ls_autocmd(session.config.region_check_events, function() + require("luasnip").exit_out_of_region( + require("luasnip").session.current_nodes[vim.api.nvim_get_current_buf()] + ) + end) + end + -- Remove buffers' nodes on deletion+wipeout. + ls_autocmd({ "BufDelete", "BufWipeout" }, function(event) + local current_nodes = require("luasnip").session.current_nodes + if current_nodes then + current_nodes[event.buf] = nil + end + end) + if session.config.enable_autosnippets then + ls_autocmd("InsertCharPre", function() + Luasnip_just_inserted = true + end) + ls_autocmd({ "TextChangedI", "TextChangedP" }, function() + if Luasnip_just_inserted then + require("luasnip").expand_auto() + Luasnip_just_inserted = nil + end + end) + end + + if session.config.store_selection_keys then + vim.cmd( + string.format( + [[xnoremap %s %s]], + session.config.store_selection_keys, + require("luasnip.util.select").select_keys + ) + ) + end + end, +} + +-- Keep these two for backward compativility +c.setup = c.set_config + +return c diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/default_config.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/default_config.lua new file mode 100644 index 00000000..68ed80c4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/default_config.lua @@ -0,0 +1,213 @@ +local types = require("luasnip.util.types") +local lazy_table = require("luasnip.util.lazy_table") + +local ft_functions = require("luasnip.extras.filetype_functions") + +-- Inserts a insert(1) before all other nodes, decreases node.pos's as indexing is "wrong". +local function modify_nodes(snip) + for i = #snip.nodes, 1, -1 do + snip.nodes[i + 1] = snip.nodes[i] + local node = snip.nodes[i + 1] + if node.pos then + node.pos = node.pos + 1 + end + end + + local iNode = require("luasnip.nodes.insertNode") + snip.nodes[1] = iNode.I(1) +end + +local lazy_snip_env = { + s = function() + return require("luasnip.nodes.snippet").S + end, + sn = function() + return require("luasnip.nodes.snippet").SN + end, + isn = function() + return require("luasnip.nodes.snippet").ISN + end, + t = function() + return require("luasnip.nodes.textNode").T + end, + i = function() + return require("luasnip.nodes.insertNode").I + end, + f = function() + return require("luasnip.nodes.functionNode").F + end, + c = function() + return require("luasnip.nodes.choiceNode").C + end, + d = function() + return require("luasnip.nodes.dynamicNode").D + end, + r = function() + return require("luasnip.nodes.restoreNode").R + end, + events = function() + return require("luasnip.util.events") + end, + k = function() + return require("luasnip.nodes.key_indexer").new_key + end, + ai = function() + return require("luasnip.nodes.absolute_indexer") + end, + extras = function() + return require("luasnip.extras") + end, + l = function() + return require("luasnip.extras").lambda + end, + rep = function() + return require("luasnip.extras").rep + end, + p = function() + return require("luasnip.extras").partial + end, + m = function() + return require("luasnip.extras").match + end, + n = function() + return require("luasnip.extras").nonempty + end, + dl = function() + return require("luasnip.extras").dynamic_lambda + end, + fmt = function() + return require("luasnip.extras.fmt").fmt + end, + fmta = function() + return require("luasnip.extras.fmt").fmta + end, + conds = function() + return require("luasnip.extras.expand_conditions") + end, + postfix = function() + return require("luasnip.extras.postfix").postfix + end, + types = function() + return require("luasnip.util.types") + end, + parse = function() + return require("luasnip.util.parser").parse_snippet + end, + ms = function() + return require("luasnip.nodes.multiSnippet").new_multisnippet + end, +} + +-- stylua: ignore +return { + -- corresponds to legacy "history=false". + keep_roots = false, + link_roots = false, + exit_roots = true, + link_children = false, + + update_events = "InsertLeave", + -- see :h User, event should never be triggered(except if it is `doautocmd`'d) + region_check_events = nil, + delete_check_events = nil, + store_selection_keys = nil, -- Supossed to be the same as the expand shortcut + ext_opts = { + [types.textNode] = { + active = { hl_group = "LuasnipTextNodeActive" }, + passive = { hl_group = "LuasnipTextNodePassive" }, + visited = { hl_group = "LuasnipTextNodeVisited" }, + unvisited = { hl_group = "LuasnipTextNodeUnvisited" }, + snippet_passive = { hl_group = "LuasnipTextNodeSnippetPassive" }, + }, + [types.insertNode] = { + active = { hl_group = "LuasnipInsertNodeActive" }, + passive = { hl_group = "LuasnipInsertNodePassive" }, + visited = { hl_group = "LuasnipInsertNodeVisited" }, + unvisited = { hl_group = "LuasnipInsertNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipInsertNodeSnippetPassive", + }, + }, + [types.exitNode] = { + active = { hl_group = "LuasnipExitNodeActive" }, + passive = { hl_group = "LuasnipExitNodePassive" }, + visited = { hl_group = "LuasnipExitNodeVisited" }, + unvisited = { hl_group = "LuasnipExitNodeUnvisited" }, + snippet_passive = { hl_group = "LuasnipExitNodeSnippetPassive" }, + }, + [types.functionNode] = { + active = { hl_group = "LuasnipFunctionNodeActive" }, + passive = { hl_group = "LuasnipFunctionNodePassive" }, + visited = { hl_group = "LuasnipFunctionNodeVisited" }, + unvisited = { hl_group = "LuasnipFunctionNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipFunctionNodeSnippetPassive", + }, + }, + [types.snippetNode] = { + active = { hl_group = "LuasnipSnippetNodeActive" }, + passive = { hl_group = "LuasnipSnippetNodePassive" }, + visited = { hl_group = "LuasnipSnippetNodeVisited" }, + unvisited = { hl_group = "LuasnipSnippetNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipSnippetNodeSnippetPassive", + }, + }, + [types.choiceNode] = { + active = { hl_group = "LuasnipChoiceNodeActive" }, + passive = { hl_group = "LuasnipChoiceNodePassive" }, + visited = { hl_group = "LuasnipChoiceNodeVisited" }, + unvisited = { hl_group = "LuasnipChoiceNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipChoiceNodeSnippetPassive", + }, + }, + [types.dynamicNode] = { + active = { hl_group = "LuasnipDynamicNodeActive" }, + passive = { hl_group = "LuasnipDynamicNodePassive" }, + visited = { hl_group = "LuasnipDynamicNodeVisited" }, + unvisited = { hl_group = "LuasnipDynamicNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipDynamicNodeSnippetPassive", + }, + }, + [types.snippet] = { + active = { hl_group = "LuasnipSnippetActive" }, + passive = { hl_group = "LuasnipSnippetPassive" }, + -- not used! + visited = { hl_group = "LuasnipSnippetVisited" }, + unvisited = { hl_group = "LuasnipSnippetUnvisited" }, + snippet_passive = { hl_group = "LuasnipSnippetSnippetPassive" }, + }, + [types.restoreNode] = { + active = { hl_group = "LuasnipRestoreNodeActive" }, + passive = { hl_group = "LuasnipRestoreNodePassive" }, + visited = { hl_group = "LuasnipRestoreNodeVisited" }, + unvisited = { hl_group = "LuasnipRestoreNodeUnvisited" }, + snippet_passive = { + hl_group = "LuasnipRestoreNodeSnippetPassive", + }, + }, + }, + ext_base_prio = 200, + ext_prio_increase = 9, + enable_autosnippets = false, + parser_nested_assembler = function(pos, snip) + -- only require here, to prevent some upfront load-cost. + local iNode = require("luasnip.nodes.insertNode") + local cNode = require("luasnip.nodes.choiceNode") + + modify_nodes(snip) + snip:init_nodes() + snip.pos = nil + + return cNode.C(pos, { snip, iNode.I(nil, { "" }) }) + end, + -- Function expected to return a list of filetypes (or empty list) + ft_func = ft_functions.from_filetype, + -- fn(bufnr) -> string[] (filetypes). + load_ft_func = ft_functions.from_filetype_load, + -- globals injected into luasnippet-files. + snip_env = lazy_table({}, lazy_snip_env), + loaders_store_source = false, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_extra_types.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_extra_types.lua new file mode 100644 index 00000000..d8d7305c --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_extra_types.lua @@ -0,0 +1,26 @@ +---@class LuaSnip.extra.MatchTSNodeOpts Passed in by the user, describes how to +---select a node from the tree via a query and captures. +---@field query? string A query, as text +---@field query_name? string The name of a query (passed to `vim.treesitter.query.get`). +---@field query_lang string The language of the query. +---@field select? LuaSnip.extra.BuiltinMatchSelector|LuaSnip.extra.MatchSelector +---@field match_captures? string|string[] + +---@class LuaSnip.extra.MatchedTSNodeInfo +---@field capture_name string +---@field node TSNode + +---@alias LuaSnip.extra.BuiltinMatchSelector +---| '"any"' # The default selector, selects the first match but not return all captures +---| '"shortest"' # Selects the shortest match, return all captures too +---| '"longest"' # Selects the longest match, return all captures too + +---Call record repeatedly to record all matches/nodes, retrieve once there are no more matches +---@class LuaSnip.extra.MatchSelector +---@field record fun(ts_match: TSMatch?, node: TSNode): boolean return true if recording can be aborted +--- (because the best match has been found) +---@field retrieve fun(): TSMatch?,TSNode? return the best match, as determined by this selector. + +---@alias LuaSnip.extra.MatchTSNodeFunc fun(parser: LuaSnip.extra.TSParser, cursor: LuaSnip.Cursor): LuaSnip.extra.NamedTSMatch?,TSNode? + +---@alias LuaSnip.extra.NamedTSMatch table diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_lambda.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_lambda.lua new file mode 100644 index 00000000..cea4490d --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_lambda.lua @@ -0,0 +1,377 @@ +-- Mostly borrowed from https://github.com/lunarmodules/Penlight/ with just some changes to use +-- neovim internal functions and reformat +-- Copyright (C) 2009-2016 Steve Donovan, David Manura. + +local concat, append = table.concat, table.insert +local map = vim.tbl_map + +local _DEBUG = rawget(_G, "_DEBUG") + +local function assert_arg(n, val, tp, verify, msg, lev) + if type(val) ~= tp then + error( + ("argument %d expected a '%s', got a '%s'"):format(n, tp, type(val)), + lev or 2 + ) + end + if verify and not verify(val) then + error(("argument %d: '%s' %s"):format(n, val, msg), lev or 2) + end +end + +local lambda = {} + +-- metatable for Placeholder Expressions (PE) +local _PEMT = {} + +local function P(t) + setmetatable(t, _PEMT) + return t +end + +lambda.PE = P + +local function isPE(obj) + return getmetatable(obj) == _PEMT +end + +lambda.isPE = isPE + +-- construct a placeholder variable (e.g _1 and _2) +local function PH(idx) + return P({ op = "X", repr = "args[" .. idx .. "]", index = idx }) +end + +-- construct a constant placeholder variable (e.g _C1 and _C2) +local function CPH(idx) + return P({ op = "X", repr = "_C" .. idx, index = idx }) +end + +lambda._1, lambda._2, lambda._3, lambda._4, lambda._5 = + PH(1), PH(2), PH(3), PH(4), PH(5) +lambda._0 = P({ op = "X", repr = "...", index = 0 }) + +function lambda.Var(name) + local ls = vim.split(name, "[%s,]+") + local res = {} + for i = 1, #ls do + append(res, P({ op = "X", repr = ls[i], index = 0 })) + end + return unpack(res) +end + +function lambda._(value) + return P({ op = "X", repr = value, index = "wrap" }) +end + +-- unknown keys are some named variable. +setmetatable(lambda, { + __index = function(_, key) + -- \\n to be correctly interpreted in `load()`. + return P({ + op = "X", + repr = "args." .. key, + index = 0, + }) + end, +}) + +local repr + +lambda.Nil = lambda.Var("nil") + +function _PEMT.__index(obj, key) + return P({ op = "[]", obj, key }) +end + +function _PEMT.__call(fun, ...) + return P({ op = "()", fun, ... }) +end + +function _PEMT.__tostring(e) + return repr(e) +end + +function _PEMT.__unm(arg) + return P({ op = "unm", arg }) +end + +function lambda.Not(arg) + return P({ op = "not", arg }) +end + +function lambda.Len(arg) + return P({ op = "#", arg }) +end + +local function binreg(context, t) + for name, op in pairs(t) do + rawset(context, name, function(x, y) + return P({ op = op, x, y }) + end) + end +end + +local function import_name(name, fun, context) + rawset(context, name, function(...) + return P({ op = "()", fun, ... }) + end) +end + +local imported_functions = {} + +local function is_global_table(n) + return type(_G[n]) == "table" +end + +--- wrap a table of functions. This makes them available for use in +-- placeholder expressions. +-- @string tname a table name +-- @tab context context to put results, defaults to environment of caller +function lambda.import(tname, context) + assert_arg( + 1, + tname, + "string", + is_global_table, + "arg# 1: not a name of a global table" + ) + local t = _G[tname] + context = context or _G + for name, fun in pairs(t) do + import_name(name, fun, context) + imported_functions[fun] = name + end +end + +--- register a function for use in placeholder expressions. +-- @lambda fun a function +-- @string[opt] name an optional name +-- @return a placeholder functiond +function lambda.register(fun, name) + assert_arg(1, fun, "function") + if name then + assert_arg(2, name, "string") + imported_functions[fun] = name + end + return function(...) + return P({ op = "()", fun, ... }) + end +end + +function lambda.lookup_imported_name(fun) + return imported_functions[fun] +end + +local function _arg(...) + return ... +end + +function lambda.Args(...) + return P({ op = "()", _arg, ... }) +end + +-- binary operators with their precedences (see Lua manual) +-- precedences might be incremented by one before use depending on +-- left- or right-associativity, space them out +local binary_operators = { + ["or"] = 0, + ["and"] = 2, + ["=="] = 4, + ["~="] = 4, + ["<"] = 4, + [">"] = 4, + ["<="] = 4, + [">="] = 4, + [".."] = 6, + ["+"] = 8, + ["-"] = 8, + ["*"] = 10, + ["/"] = 10, + ["%"] = 10, + ["^"] = 14, +} + +-- unary operators with their precedences +local unary_operators = { + ["not"] = 12, + ["#"] = 12, + ["unm"] = 12, +} + +-- comparisons (as prefix functions) +binreg(lambda, { + And = "and", + Or = "or", + Eq = "==", + Lt = "<", + Gt = ">", + Le = "<=", + Ge = ">=", +}) + +-- standard binary operators (as metamethods) +binreg(_PEMT, { + __add = "+", + __sub = "-", + __mul = "*", + __div = "/", + __mod = "%", + __pow = "^", + __concat = "..", +}) + +binreg(_PEMT, { __eq = "==" }) + +--- all elements of a table except the first. +-- @tab ls a list-like table. +function lambda.tail(ls) + assert_arg(1, ls, "table") + local res = {} + for i = 2, #ls do + append(res, ls[i]) + end + return res +end + +--- create a string representation of a placeholder expression. +-- @param e a placeholder expression +-- @param lastpred not used +function repr(e, lastpred) + local tail = lambda.tail + if isPE(e) then + local pred = binary_operators[e.op] or unary_operators[e.op] + if pred then + -- binary or unary operator + local s + if binary_operators[e.op] then + local left_pred = pred + local right_pred = pred + if e.op == ".." or e.op == "^" then + left_pred = left_pred + 1 + else + right_pred = right_pred + 1 + end + local left_arg = repr(e[1], left_pred) + local right_arg = repr(e[2], right_pred) + s = left_arg .. " " .. e.op .. " " .. right_arg + else + local op = e.op == "unm" and "-" or e.op + s = op .. " " .. repr(e[1], pred) + end + if lastpred and lastpred > pred then + s = "(" .. s .. ")" + end + return s + else -- either postfix, or a placeholder + local ls = map(repr, e) + if e.op == "[]" then + return ls[1] .. "[" .. ls[2] .. "]" + elseif e.op == "()" then + local fn + if ls[1] ~= nil then -- was _args, undeclared! + fn = ls[1] + else + fn = "" + end + return fn .. "(" .. concat(tail(ls), ",") .. ")" + else + return e.repr + end + end + elseif type(e) == "string" then + return '"' .. e .. '"' + elseif type(e) == "function" then + local name = lambda.lookup_imported_name(e) + if name then + return name + else + return tostring(e) + end + else + return tostring(e) --should not really get here! + end +end +lambda.repr = repr + +-- collect all the non-PE values in this PE into vlist, and replace each occurence +-- with a constant PH (_C1, etc). Return the maximum placeholder index found. +local collect_values +function collect_values(e, vlist) + if isPE(e) then + if e.op ~= "X" then + local m = 0 + for i = 1, #e do + local subx = e[i] + local pe = isPE(subx) + if pe then + if subx.op == "X" and subx.index == "wrap" then + subx = subx.repr + pe = false + else + m = math.max(m, collect_values(subx, vlist)) + end + end + if not pe then + append(vlist, subx) + e[i] = CPH(#vlist) + end + end + return m + else -- was a placeholder, it has an index... + return e.index + end + else -- plain value has no placeholder dependence + return 0 + end +end +lambda.collect_values = collect_values + +--- instantiate a PE into an actual function. First we find the largest placeholder used, +-- e.g. _2; from this a list of the formal parameters can be build. Then we collect and replace +-- any non-PE values from the PE, and build up a constant binding list. +-- Finally, the expression can be compiled, and e.__PE_function is set. +-- @param e a placeholder expression +-- @return a function +function lambda.instantiate(e) + local consts, values = {}, {} + local rep, err, fun + local n = lambda.collect_values(e, values) + for i = 1, #values do + append(consts, "_C" .. i) + if _DEBUG then + print(i, values[i]) + end + end + + consts = concat(consts, ",") + rep = repr(e) + local fstr = ("return function(%s) return function(args) return %s end end"):format( + consts, + rep + ) + if _DEBUG then + print(fstr) + end + fun, err = load(fstr, "fun") + if not fun then + return nil, err + end + fun = fun() -- get wrapper + fun = fun(unpack(values)) -- call wrapper (values could be empty) + e.__PE_function = fun + return fun +end + +--- instantiate a PE unless it has already been done. +-- @param e a placeholder expression +-- @return the function +function lambda.I(e) + if rawget(e, "__PE_function") then + return e.__PE_function + else + return lambda.instantiate(e) + end +end + +return lambda diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_parser_combinator.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_parser_combinator.lua new file mode 100644 index 00000000..b761b3c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_parser_combinator.lua @@ -0,0 +1,94 @@ +-- Minimal parser combinator, +-- only for internal use so not exposed elsewhere nor documented in the oficial doc +-- +local M = {} + +-- Consumes strings matching a pattern, generates the matched string +function M.pattern(pat) + return function(text, pos) + local s, e = text:find(pat, pos) + + if s then + local v = text:sub(s, e) + return true, v, pos + #v + else + return false, nil, pos + end + end +end + +-- Matches whatever `p matches and generates whatever p generates after +-- transforming it with `f +function M.map(p, f) + return function(text, pos) + local succ, val, new_pos = p(text, pos) + if succ then + return true, f(val), new_pos + end + return false, nil, pos + end +end + +-- Matches and generates the same as the first of it's children that matches something +function M.any(...) + local parsers = { ... } + return function(text, pos) + for _, p in ipairs(parsers) do + local succ, val, new_pos = p(text, pos) + if succ then + return true, val, new_pos + end + end + return false, nil, pos + end +end + +-- Matches all what its children do in sequence, generates a table of its children generations +function M.seq(...) + local parsers = { ... } + return function(text, pos) + local original_pos = pos + local values = {} + for _, p in ipairs(parsers) do + local succ, val, new_pos = p(text, pos) + pos = new_pos + if not succ then + return false, nil, original_pos + end + table.insert(values, val) + end + return true, values, pos + end +end + +-- Matches cero or more times what it child do in sequence, generates a table with those generations +function M.star(p) + return function(text, pos) + local len = #text + local values = {} + + while pos <= len do + local succ, val, new_pos = p(text, pos) + if succ then + pos = new_pos + table.insert(values, val) + else + break + end + end + return #values > 0, values, pos + end +end + +-- Consumes a literal string, does not generates +function M.literal(t) + return function(text, pos) + if text:sub(pos, pos + #t - 1) == t then + return true, nil, pos + #t + else + return false, text:sub(pos, pos + #t), pos + #t + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_treesitter.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_treesitter.lua new file mode 100644 index 00000000..69aef027 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/_treesitter.lua @@ -0,0 +1,433 @@ +local util = require("luasnip.util.util") +local tbl = require("luasnip.util.table") + +local function get_lang(bufnr) + local ft = vim.api.nvim_buf_get_option(bufnr, "ft") + local lang = vim.treesitter.language.get_lang(ft) or ft + return lang +end + +-- Inspect node +---@param node TSNode? +---@return string +local function inspect_node(node) + if node == nil then + return "nil" + end + + local start_row, start_col, end_row, end_col = + vim.treesitter.get_node_range(node) + + return ("%s [%d, %d] [%d, %d]"):format( + node:type(), + start_row, + start_col, + end_row, + end_col + ) +end + +---@param bufnr number +---@param region LuaSnip.MatchRegion +---@return LanguageTree, string +local function reparse_buffer_after_removing_match(bufnr, region) + local lang = get_lang(bufnr) + + -- have to get entire buffer, a pattern-match may include lines behind the trigger. + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + -- region is 0-indexed, lines and strings 1-indexed. + local region_line = lines[region.row + 1] + -- sub includes end, want to exclude it. + local left_part = region_line:sub(1, region.col_range[1] + 1 - 1) + local right_part = region_line:sub(region.col_range[2] + 1) + + lines[region.row + 1] = left_part .. right_part + + local source = table.concat(lines, "\n") + + ---@type LanguageTree + local parser = vim.treesitter.get_string_parser(source, lang, nil) + parser:parse() + return parser, source +end + +---@class LuaSnip.extra.FixBufferContext +---@field ori_bufnr number +---@field ori_text string +---@field region LuaSnip.MatchRegion +local FixBufferContext = {} + +---@param ori_bufnr number +---@param region LuaSnip.MatchRegion +---@return LuaSnip.extra.FixBufferContext +function FixBufferContext.new(ori_bufnr, region, region_content) + local o = { + ori_bufnr = ori_bufnr, + ori_text = region_content, + region = region, + } + setmetatable(o, { + __index = FixBufferContext, + }) + + return o +end + +function FixBufferContext:enter() + vim.api.nvim_buf_set_text( + self.ori_bufnr, + self.region.row, + self.region.col_range[1], + self.region.row, + self.region.col_range[2], + { "" } + ) + + local parser, source = + vim.treesitter.get_parser(self.ori_bufnr), self.ori_bufnr + parser:parse() + + return parser, source +end + +function FixBufferContext:leave() + vim.api.nvim_buf_set_text( + self.ori_bufnr, + self.region.row, + self.region.col_range[1], + self.region.row, + self.region.col_range[1], + { self.ori_text } + ) + + -- The cursor does not necessarily move with the insertion, and has to be + -- restored manually. + -- when making this work for expansion away from cursor, store cursor-pos + -- in self. + vim.api.nvim_win_set_cursor( + 0, + { self.region.row + 1, self.region.col_range[2] } + ) + + local parser, source = + vim.treesitter.get_parser(self.ori_bufnr), self.ori_bufnr + parser:parse() + return parser, source +end + +-- iterate over all +local function captures_iter(captures) + -- turn string/string[] into map: string -> bool, for querying whether some + -- string is present in captures. + local capture_map = tbl.list_to_set(captures) + + -- receives the query and the iterator over all its matches. + return function(query, match_iter) + local current_match + local current_capture_id + local iter + local pattern + + iter = function() + -- if there is no current match to continue, + if not current_match then + pattern, current_match, _ = match_iter() + + -- occurs once there are no more matches. + if not pattern then + return nil + end + end + while true do + local node + current_capture_id, node = + next(current_match, current_capture_id) + if not current_capture_id then + break + end + + local capture_name = query.captures[current_capture_id] + + if capture_map[capture_name] then + return current_match, node + end + end + + -- iterated over all captures of the current match, reset it to + -- retrieve the next match in the recursion. + current_match = nil + + -- tail-call-optimization! :fingers_crossed: + return iter() + end + + return iter + end +end + +local builtin_tsnode_selectors = { + any = function() + local best_node + local best_node_match + return { + record = function(match, node) + best_node = node + best_node_match = match + -- abort immediately, we just want any match. + return true + end, + retrieve = function() + return best_node_match, best_node + end, + } + end, + shortest = function() + local best_node + local best_node_match + + -- end is already equal, only have to compare start. + local best_node_start + return { + record = function(match, node) + local start_row, start_col, _, _ = + vim.treesitter.get_node_range(node) + if + (best_node == nil) + or (start_row > best_node_start[1]) + or ( + start_row == best_node_start[1] + and start_col > best_node_start[2] + ) + then + best_node = node + best_node_match = match + best_node_start = { start_row, start_col } + end + -- don't abort, have to see all potential nodes to find shortest match. + return false + end, + retrieve = function() + return best_node_match, best_node + end, + } + end, + longest = function() + local best_node + local best_node_match + + -- end is already equal, only have to compare start. + local best_node_start + return { + record = function(match, node) + local start_row, start_col, _, _ = + vim.treesitter.get_node_range(node) + if + (best_node == nil) + or (start_row < best_node_start[1]) + or ( + start_row == best_node_start[1] + and start_col < best_node_start[2] + ) + then + best_node = node + best_node_match = match + best_node_start = { start_row, start_col } + end + -- don't abort, have to see all potential nodes to find longest match. + return false + end, + retrieve = function() + return best_node_match, best_node + end, + } + end, +} + +---@class LuaSnip.extra.TSParser +---@field parser LanguageTree +---@field source string|number +local TSParser = {} + +---@param bufnr number? +---@param parser LanguageTree +---@param source string|number +---@return LuaSnip.extra.TSParser? +function TSParser.new(bufnr, parser, source) + bufnr = bufnr or vim.api.nvim_get_current_buf() + + local o = { + parser = parser, + source = source, + } + + setmetatable(o, { + __index = TSParser, + ---@param self LuaSnip.extra.TSParser + ---@return string + __tostring = function(self) + return ("trees: %d, source: %s"):format( + #self.parser:trees(), + type(self.source) == "number" and tostring(self.source) + or "[COPIED]" + ) + end, + }) + return o +end + +---@param pos { [1]: number, [2]: number }? +---@return TSNode? +function TSParser:get_node_at_pos(pos) + pos = vim.F.if_nil(pos, util.get_cursor_0ind()) + local row, col = pos[1], pos[2] + assert( + row >= 0 and col >= 0, + "Invalid position: row and col must be non-negative" + ) + local range = { row, col, row, col } + return self.parser:named_node_for_range( + range, + { ignore_injections = false } + ) +end + +---Get the root for the smallest tree containing `pos`. +---@param pos { [1]: number, [2]: number } +---@return TSNode? +function TSParser:root_at(pos) + local tree = self.parser:tree_for_range( + { pos[1], pos[2], pos[1], pos[2] }, + { ignore_injections = false } + ) + if not tree then + return nil + end + + return tree:root() +end + +---@param match_opts LuaSnip.extra.EffectiveMatchTSNodeOpts +---@param pos { [1]: number, [2]: number } +---@return LuaSnip.extra.NamedTSMatch?, TSNode? +function TSParser:match_at(match_opts, pos) + -- Since we want to find a match to the left of pos, and if we accept there + -- has to be at least one character (I assume), we should probably not look + -- for the tree containing `pos`, since that might be the wrong one (if + -- injected languages are in play). + local root = self:root_at({ pos[1], pos[2] - 1 }) + if root == nil then + return nil, nil + end + local root_from_line, _, root_to_line, _ = root:range() + + local query = match_opts.query + local selector = match_opts.selector() + local next_ts_match = + -- end-line is excluded by iter_matches, if the column of root_to + -- greater than 0, we would erroneously ignore a line that could + -- contain our match. + query:iter_matches(root, self.source, root_from_line, root_to_line + 1) + + for match, node in match_opts.generator(query, next_ts_match) do + -- false: don't include bytes. + local _, _, end_row, end_col = node:range(false) + if end_row == pos[1] and end_col == pos[2] then + if selector.record(match, node) then + -- should abort iteration + break + end + end + end + + local best_match, node = selector.retrieve() + if not best_match then + return nil, nil + end + + -- map captures via capture-name, not id. + local named_captures_match = {} + for id, capture_node in pairs(best_match) do + named_captures_match[query.captures[id]] = capture_node + end + return named_captures_match, node +end + +---@param node TSNode +---@return string +function TSParser:get_node_text(node) + -- not sure what happens if this is multiline. + return vim.treesitter.get_node_text(node, self.source) +end + +---@param root TSNode +---@param n number +---@param matcher fun(node:TSNode):boolean|nil +---@return TSNode? +local function find_nth_parent(root, n, matcher) + local parent = root + matcher = matcher or function() + return true + end + local i = 0 + while i < n do + if not parent or not matcher(parent) then + return nil + end + parent = parent:parent() + i = i + 1 + end + if not parent or not matcher(parent) then + return nil + end + return parent +end + +---@param root TSNode +---@param matcher fun(node:TSNode):boolean|nil +local function find_topmost_parent(root, matcher) + ---@param node TSNode? + ---@return TSNode? + local function _impl(node) + if node == nil then + return nil + end + local current = nil + if matcher == nil or matcher(node) then + current = node + end + return vim.F.if_nil(_impl(node:parent()), current) + end + + return _impl(root) +end + +---@param root TSNode +---@param matcher fun(node:TSNode):boolean|nil +local function find_first_parent(root, matcher) + ---@param node TSNode? + ---@return TSNode? + local function _impl(node) + if node == nil then + return nil + end + if matcher == nil or matcher(node) then + return node + end + return _impl(node:parent()) + end + + return _impl(root) +end + +return { + get_lang = get_lang, + reparse_buffer_after_removing_match = reparse_buffer_after_removing_match, + TSParser = TSParser, + FixBufferContext = FixBufferContext, + find_topmost_parent = find_topmost_parent, + find_first_parent = find_first_parent, + find_nth_parent = find_nth_parent, + inspect_node = inspect_node, + captures_iter = captures_iter, + builtin_tsnode_selectors = builtin_tsnode_selectors, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/conditions/expand.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/conditions/expand.lua new file mode 100644 index 00000000..e97d85fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/conditions/expand.lua @@ -0,0 +1,14 @@ +local cond_obj = require("luasnip.extras.conditions") + +-- use the functions from show as basis and extend/overwrite functions specific for expand here +local M = vim.deepcopy(require("luasnip.extras.conditions.show")) +----------------------- +-- PRESET CONDITIONS -- +----------------------- +local function line_begin(line_to_cursor, matched_trigger) + -- +1 because `string.sub("abcd", 1, -2)` -> abc + return line_to_cursor:sub(1, -(#matched_trigger + 1)):match("^%s*$") +end +M.line_begin = cond_obj.make_condition(line_begin) + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/conditions/init.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/conditions/init.lua new file mode 100644 index 00000000..db9ff39f --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/conditions/init.lua @@ -0,0 +1,55 @@ +local M = {} + +----------------------- +-- CONDITION OBJECTS -- +----------------------- +local condition_mt = { + -- logic operators + -- not '-' + __unm = function(o1) + return M.make_condition(function(...) + return not o1(...) + end) + end, + -- or '+' + __add = function(o1, o2) + return M.make_condition(function(...) + return o1(...) or o2(...) + end) + end, + __sub = function(o1, o2) + return M.make_condition(function(...) + return o1(...) and not o2(...) + end) + end, + -- and '*' + __mul = function(o1, o2) + return M.make_condition(function(...) + return o1(...) and o2(...) + end) + end, + -- xor '^' + __pow = function(o1, o2) + return M.make_condition(function(...) + return o1(...) ~= o2(...) + end) + end, + -- xnor '%' + -- might be counter intuitive, but as we can't use '==' (must return bool) + -- it's best to use something weird (doesn't have to be used) + __mod = function(o1, o2) + return function(...) + return o1(...) == o2(...) + end + end, + -- use table like a function by overloading __call + __call = function(tab, line_to_cursor, matched_trigger, captures) + return tab.func(line_to_cursor, matched_trigger, captures) + end, +} + +function M.make_condition(func) + return setmetatable({ func = func }, condition_mt) +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/conditions/show.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/conditions/show.lua new file mode 100644 index 00000000..eb11cf4e --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/conditions/show.lua @@ -0,0 +1,21 @@ +local cond_obj = require("luasnip.extras.conditions") + +local M = {} +----------------------- +-- PRESET CONDITIONS -- +----------------------- +local function line_end(line_to_cursor) + local line = vim.api.nvim_get_current_line() + -- looks pretty inefficient, but as lue interns strings, this is just a + -- comparision of pointers (which probably is faster than calculate the + -- length and then checking) + return line_to_cursor == line +end +M.line_end = cond_obj.make_condition(line_end) + +local function has_selected_text() + return vim.b.LUASNIP_TM_SELECT ~= nil +end +M.has_selected_text = cond_obj.make_condition(has_selected_text) + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/expand_conditions.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/expand_conditions.lua new file mode 100644 index 00000000..69c662fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/expand_conditions.lua @@ -0,0 +1 @@ +return require("luasnip.extras.conditions.expand") diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/filetype_functions.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/filetype_functions.lua new file mode 100644 index 00000000..e7011f0c --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/filetype_functions.lua @@ -0,0 +1,89 @@ +local function fts_from_ts_lang(lang) + local fts = {} + -- In case of someone using nvim <= 0.9 + if vim.treesitter.language and vim.treesitter.language.get_filetypes then + fts = vim.treesitter.language.get_filetypes(lang) + end + -- Keep lang as part of the result, for backward compatibility. + -- If lang is already part of fts, one entry will be removed by deduplicate + -- in get_snippet_filetypes(). + table.insert(fts, lang) + return fts +end + +local function from_cursor_pos() + -- get_parser errors if parser not present (no grammar for language). + local has_parser, parser = pcall(vim.treesitter.get_parser) + + if has_parser then + local cursor = require("luasnip.util.util").get_cursor_0ind() + -- assumption: languagetree uses 0-indexed byte-ranges. + local lang = parser + :language_for_range({ + cursor[1], + cursor[2], + cursor[1], + cursor[2], + }) + :lang() + return fts_from_ts_lang(lang) + else + return {} + end +end + +local function from_filetype() + return vim.split(vim.bo.filetype, ".", { plain = true, trimemtpy = false }) +end + +-- NOTE: Beware that the resulting filetypes may differ from the ones in `vim.bo.filetype`. (for +-- example the filetype for LaTeX is 'latex' and not 'tex' as in `vim.bo.filetype`) -- +local function from_pos_or_filetype() + local from_cursor = from_cursor_pos() + if not vim.tbl_isempty(from_cursor) then + return from_cursor + else + return from_filetype() + end +end + +local function from_filetype_load(bufnr) + return vim.split(vim.api.nvim_buf_get_option(bufnr, "filetype"), ".", true) +end + +local function extend_load_ft(extend_fts) + setmetatable(extend_fts, { + -- if the filetype is not extended, only it itself should be loaded. + -- preventing ifs via __index. + __index = function(t, ft) + local val = { ft } + rawset(t, ft, val) + return val + end, + }) + + for ft, _ in pairs(extend_fts) do + -- append the regular filetype to the extend-filetypes. + table.insert(extend_fts[ft], ft) + end + + return function(bufnr) + local fts = + vim.split(vim.api.nvim_buf_get_option(bufnr, "filetype"), ".", true) + local res = {} + + for _, ft in ipairs(fts) do + vim.list_extend(res, extend_fts[ft]) + end + + return res + end +end + +return { + from_filetype = from_filetype, + from_cursor_pos = from_cursor_pos, + from_pos_or_filetype = from_pos_or_filetype, + from_filetype_load = from_filetype_load, + extend_load_ft = extend_load_ft, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/fmt.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/fmt.lua new file mode 100644 index 00000000..4d8e3152 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/fmt.lua @@ -0,0 +1,231 @@ +local text_node = require("luasnip.nodes.textNode").T +local wrap_nodes = require("luasnip.util.util").wrap_nodes +local extend_decorator = require("luasnip.util.extend_decorator") +local Str = require("luasnip.util.str") +local rp = require("luasnip.extras").rep + +-- https://gist.github.com/tylerneylon/81333721109155b2d244 +local function copy3(obj, seen) + -- Handle non-tables and previously-seen tables. + if type(obj) ~= "table" then + return obj + end + if seen and seen[obj] then + return seen[obj] + end + + -- New table; mark it as seen an copy recursively. + local s = seen or {} + local res = {} + s[obj] = res + for k, v in next, obj do + res[copy3(k, s)] = copy3(v, s) + end + return setmetatable(res, getmetatable(obj)) +end + +-- Interpolate elements from `args` into format string with placeholders. +-- +-- The placeholder syntax for selecting from `args` is similar to fmtlib and +-- Python's .format(), with some notable differences: +-- * no format options (like `{:.2f}`) +-- * 1-based indexing +-- * numbered/auto-numbered placeholders can be mixed; numbered ones set the +-- current index to new value, so following auto-numbered placeholders start +-- counting from the new value (e.g. `{} {3} {}` is `{1} {3} {4}`) +-- +-- Arguments: +-- fmt: string with placeholders +-- args: table with list-like and/or map-like keys +-- opts: +-- delimiters: string, 2 distinct characters (left, right), default "{}" +-- strict: boolean, set to false to allow for unused `args`, default true +-- repeat_duplicates: boolean, repeat nodes which have jump_index instead of copying them, default false +-- Returns: a list of strings and elements of `args` inserted into placeholders +local function interpolate(fmt, args, opts) + local defaults = { + delimiters = "{}", + strict = true, + repeat_duplicates = false, + } + opts = vim.tbl_extend("force", defaults, opts or {}) + + -- sanitize delimiters + assert( + #opts.delimiters == 2, + 'Currently only single-char delimiters are supported, e.g. delimiters="{}" (left, right)' + ) + assert( + opts.delimiters:sub(1, 1) ~= opts.delimiters:sub(2, 2), + "Delimiters must be two _different_ characters" + ) + local delimiters = { + left = opts.delimiters:sub(1, 1), + right = opts.delimiters:sub(2, 2), + esc_left = vim.pesc(opts.delimiters:sub(1, 1)), + esc_right = vim.pesc(opts.delimiters:sub(2, 2)), + } + + -- manage insertion of text/args + local elements = {} + local last_index = 0 + local used_keys = {} + + local add_text = function(text) + if #text > 0 then + table.insert(elements, text) + end + end + local add_arg = function(placeholder) + local num = tonumber(placeholder) + local key + if num then -- numbered placeholder + last_index = num + key = last_index + elseif placeholder == "" then -- auto-numbered placeholder + key = last_index + 1 + last_index = key + else -- named placeholder + key = placeholder + end + assert( + args[key], + string.format( + "Missing key `%s` in format arguments: `%s`", + key, + fmt + ) + ) + -- if the node was already used, insert a copy of it. + -- The nodes are modified in-place as part of constructing the snippet, + -- modifying one node twice will lead to UB. + if used_keys[key] then + local jump_index = args[key]:get_jump_index() -- For nodes that don't have a jump index, copy it instead + if not opts.repeat_duplicates or jump_index == nil then + table.insert(elements, copy3(args[key])) + else + table.insert(elements, rp(jump_index)) + end + else + table.insert(elements, args[key]) + used_keys[key] = true + end + end + + -- iterate keeping a range from previous match, e.g. (not in_placeholder vs in_placeholder) + -- "Sample {2} string {3}." OR "Sample {2} string {3}." + -- left^--------^right OR left^-^right + local pattern = + string.format("[%s%s]", delimiters.esc_left, delimiters.esc_right) + local in_placeholder = false + local left = 0 + + while true do + local right = fmt:find(pattern, left + 1) + -- if not found, add the remaining part of string and finish + if right == nil then + assert( + not in_placeholder, + string.format('Missing closing delimiter: "%s"', fmt:sub(left)) + ) + add_text(fmt:sub(left + 1)) + break + end + -- check if the delimiters are escaped + local delim = fmt:sub(right, right) + local next_char = fmt:sub(right + 1, right + 1) + if not in_placeholder and delim == next_char then + -- add the previous part of the string with a single delimiter + add_text(fmt:sub(left + 1, right)) + -- and jump over the second one + left = right + 1 + -- "continue" + else -- non-escaped delimiter + assert( + delim + == (in_placeholder and delimiters.right or delimiters.left), + string.format( + 'Found unescaped %s %s placeholder; format[%d:%d]="%s"', + delim, + in_placeholder and "inside" or "outside", + left, + right, + fmt:sub(left, right) + ) + ) + -- add arg/text depending on current state + local add = in_placeholder and add_arg or add_text + add(fmt:sub(left + 1, right - 1)) + -- update state + left = right + in_placeholder = delim == delimiters.left + end + end + + -- sanity check: all arguments were used + if opts.strict then + for key, _ in pairs(args) do + assert( + used_keys[key], + string.format("Unused argument: args[%s]", key) + ) + end + end + + return elements +end + +-- Use a format string with placeholders to interpolate nodes. +-- +-- See `interpolate` documentation for details on the format. +-- +-- Arguments: +-- str: format string +-- nodes: snippet node or list of nodes +-- opts: optional table +-- trim_empty: boolean, remove whitespace-only first/last lines, default true +-- dedent: boolean, remove all common indent in `str`, default true +-- ... the rest is passed to `interpolate` +-- Returns: list of snippet nodes +local function format_nodes(str, nodes, opts) + local defaults = { + trim_empty = true, + dedent = true, + } + opts = vim.tbl_extend("force", defaults, opts or {}) + + -- allow to pass a single node + nodes = wrap_nodes(nodes) + + -- optimization: avoid splitting multiple times + local lines = nil + + lines = vim.split(str, "\n", true) + Str.process_multiline(lines, opts) + str = table.concat(lines, "\n") + + -- pop format_nodes's opts + for key, _ in ipairs(defaults) do + opts[key] = nil + end + + local parts = interpolate(str, nodes, opts) + return vim.tbl_map(function(part) + -- wrap strings in text nodes + if type(part) == "string" then + return text_node(vim.split(part, "\n", true)) + else + return part + end + end, parts) +end + +extend_decorator.register(format_nodes, { arg_indx = 3 }) + +return { + interpolate = interpolate, + format_nodes = format_nodes, + -- alias + fmt = format_nodes, + fmta = extend_decorator.apply(format_nodes, { delimiters = "<>" }), +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/init.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/init.lua new file mode 100644 index 00000000..c4be557d --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/init.lua @@ -0,0 +1,164 @@ +local F = require("luasnip.nodes.functionNode").F +local SN = require("luasnip.nodes.snippet").SN +local D = require("luasnip.nodes.dynamicNode").D +local I = require("luasnip.nodes.insertNode").I + +local lambda = {} + +local function _concat(lines) + return table.concat(lines, "\n") +end + +local function make_lambda_args(node_args, imm_parent) + local snip = imm_parent.snippet + -- turn args' table-multilines into \n-multilines (needs to be possible + -- to process args with luas' string-functions). + local args = vim.tbl_map(_concat, node_args) + + setmetatable(args, { + __index = function(table, key) + local val + -- key may be capture or env-variable. + local num = key:match("CAPTURE(%d+)") + if num then + val = snip.captures[tonumber(num)] + else + -- env may be string or table. + if type(snip.env[key]) == "table" then + -- table- to \n-multiline. + val = _concat(snip.env[key]) + else + val = snip.env[key] + end + end + rawset(table, key, val) + return val + end, + }) + return args +end + +local function expr_to_fn(expr) + local _lambda = require("luasnip.extras._lambda") + + local fn_code = _lambda.instantiate(expr) + local function fn(args, snip) + -- to be sure, lambda may end with a `match` returning nil. + local out = fn_code(make_lambda_args(args, snip)) or "" + return vim.split(out, "\n") + end + return fn +end + +local LM = {} +function LM:__index(key) + return require("luasnip.extras._lambda")[key] +end +function LM:__call(expr, input_ids) + return F(expr_to_fn(expr), input_ids) +end + +setmetatable(lambda, LM) + +local function to_function(val, use_re) + if type(val) == "function" then + return val + end + if type(val) == "string" and not use_re then + return function() + return val + end + end + if type(val) == "string" and use_re then + return function(args) + return _concat(args[1]):match(val) + end + end + if lambda.isPE(val) then + local lmb = lambda.instantiate(val) + return function(args, snip) + return lmb(make_lambda_args(args, snip)) + end + end + assert(false, "Can't convert argument to function") +end + +local function match(index, _match, _then, _else) + assert(_match, "You have to pass at least 2 arguments") + + _match = to_function(_match, true) + _then = to_function(_then or function(args, snip) + local match_return = _match(args, snip) + return ( + ( + type(match_return) == "string" + -- _assume_ table of string. + or type(match_return) == "table" + ) and match_return + ) or "" + end) + _else = to_function(_else or "") + + local function func(args, snip) + local out = nil + if _match(args, snip) then + out = _then(args, snip) + else + out = _else(args, snip) + end + -- \n is used as a line-separator for simple strings. + return type(out) == "string" and vim.split(out, "\n") or out + end + + return F(func, index) +end + +return { + lambda = lambda, + match = match, + -- repeat a node. + rep = function(node_indx) + return F(function(args) + return args[1] + end, node_indx) + end, + -- Insert the output of a function. + partial = function(func, ...) + return F(function(_, _, ...) + return func(...) + end, {}, { user_args = { ... } }) + end, + nonempty = function(indx, text_if, text_if_not) + assert( + type(indx) == "number", + "this only checks one node for emptiness!" + ) + assert( + text_if, + "At least the text for nonemptiness has to be supplied." + ) + + return F(function(args) + return (args[1][1] ~= "" or #args[1] > 1) and text_if + or (text_if_not or "") + end, { + indx, + }) + end, + dynamic_lambda = function(pos, lambd, args_indcs) + local insert_preset_text_func = lambda.instantiate(lambd) + return D(pos, function(args, imm_parent) + -- to be sure, lambda may end with a `match` returning nil. + local out = insert_preset_text_func( + make_lambda_args(args, imm_parent) + ) or "" + return SN(pos, { + I(1, vim.split(out, "\n")), + }) + end, args_indcs) + end, + + --alias + l = lambda, + m = match, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/otf.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/otf.lua new file mode 100644 index 00000000..514ddb05 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/otf.lua @@ -0,0 +1,89 @@ +local ls = require("luasnip") +local cp = require("luasnip.util.functions").copy +local p = require("luasnip.extras._parser_combinator") +local dedent = require("luasnip.util.str").dedent +local M = {} + +local T = { EOL = "EOL", TXT = "TXT", INP = "INP" } + +local chunk = p.any( + p.map(p.literal("$$"), function() + return { T.TXT, "$" } + end), + p.map(p.literal("\n"), function() + return { T.EOL } + end), + p.map(p.seq(p.literal("$"), p.pattern("%w*")), function(c) + return { T.INP, c[1] } + end), + p.map(p.pattern("[^\n$]*"), function(c) + return { T.TXT, c } + end) +) + +M._snippet_chunks = p.star(chunk) + +function M._txt_to_snip(txt) + local t = ls.t + local s = ls.s + local i = ls.i + local f = ls.f + txt = dedent(txt) + + -- The parser does not handle empty strings + if txt == "" then + return s("", t({ "" })) + end + + local _, chunks, _ = M._snippet_chunks(txt, 1) + + local current_text_arg = { "" } + local nodes = {} + local know_inputs = {} + local last_input_pos = 0 + + for _, part in ipairs(chunks) do + if part[1] == T.TXT then + current_text_arg[#current_text_arg] = current_text_arg[#current_text_arg] + .. part[2] + elseif #current_text_arg > 1 or current_text_arg[1] ~= "" then + table.insert(nodes, t(current_text_arg)) + current_text_arg = { "" } + end + + if part[1] == T.EOL then + table.insert(current_text_arg, "") + elseif part[1] == T.INP then + local inp_pos = know_inputs[part[2]] + if inp_pos then + table.insert(nodes, f(cp, { inp_pos })) + else + last_input_pos = last_input_pos + 1 + know_inputs[part[2]] = last_input_pos + table.insert(nodes, i(last_input_pos, part[2])) + end + end + end + if #current_text_arg > 1 or current_text_arg[1] ~= "" then + table.insert(nodes, t(current_text_arg)) + end + return s("", nodes) +end + +local last_snip = nil +local last_reg = nil + +-- Create snippets On The Fly +-- It's advaisable not to use the default register as luasnip will probably +-- override it +function M.on_the_fly(regname) + regname = regname or "" + local reg = table.concat(vim.fn.getreg(regname, 1, true), "\n") -- Avoid eol in the last line + if last_reg ~= reg then + last_reg = reg + last_snip = M._txt_to_snip(reg) + end + ls.snip_expand(last_snip) +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/postfix.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/postfix.lua new file mode 100644 index 00000000..c951b5a8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/postfix.lua @@ -0,0 +1,75 @@ +local snip = require("luasnip.nodes.snippet").S +local events = require("luasnip.util.events") +local extend_decorator = require("luasnip.util.extend_decorator") +local node_util = require("luasnip.nodes.util") +local util = require("luasnip.util.util") + +local matches = { + default = [[[%w%.%_%-%"%']+$]], + line = "^.+$", +} + +local function wrap_resolve_expand_params(match_pattern, user_resolve) + return function(snippet, line_to_cursor, match, captures) + if line_to_cursor:sub(1, -1 - #match):match(match_pattern) == nil then + return nil + end + + local pos = util.get_cursor_0ind() + local line_to_cursor_except_match = + line_to_cursor:sub(1, #line_to_cursor - #match) + local postfix_match = line_to_cursor_except_match:match(match_pattern) + or "" + local res = { + clear_region = { + from = { pos[1], pos[2] - #postfix_match - #match }, + to = pos, + }, + env_override = { + POSTFIX_MATCH = postfix_match, + }, + } + + if user_resolve then + local user_res = + user_resolve(snippet, line_to_cursor, match, captures) + if user_res then + res = vim.tbl_deep_extend("force", res, user_res, { + env_override = {}, + }) + else + return nil + end + end + return res + end +end + +local function postfix(context, nodes, opts) + opts = opts or {} + local user_callback = vim.tbl_get(opts, "callbacks", -1, events.pre_expand) + vim.validate({ + context = { context, { "string", "table" } }, + nodes = { nodes, "table" }, + opts = { opts, "table" }, + user_callback = { user_callback, { "nil", "function" } }, + }) + + context = node_util.wrap_context(context) + context.wordTrig = false + local match_pattern = context.match_pattern or matches.default + context.resolveExpandParams = + wrap_resolve_expand_params(match_pattern, context.resolveExpandParams) + + return snip(context, nodes, opts) +end +extend_decorator.register( + postfix, + { arg_indx = 1, extend = node_util.snippet_extend_context }, + { arg_indx = 3 } +) + +return { + postfix = postfix, + matches = matches, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/select_choice.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/select_choice.lua new file mode 100644 index 00000000..adca3a2a --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/select_choice.lua @@ -0,0 +1,25 @@ +local session = require("luasnip.session") +local ls = require("luasnip") + +local function set_choice_callback(_, indx) + if not indx then + return + end + -- feed+immediately execute i to enter INSERT after vim.ui.input closes. + vim.api.nvim_feedkeys("i", "x", false) + ls.set_choice(indx) +end + +local function select_choice() + assert( + session.active_choice_nodes[vim.api.nvim_get_current_buf()], + "No active choiceNode" + ) + vim.ui.select( + ls.get_current_choices(), + { kind = "luasnip" }, + set_choice_callback + ) +end + +return select_choice diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/snip_location.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/snip_location.lua new file mode 100644 index 00000000..cb4618ad --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/snip_location.lua @@ -0,0 +1,190 @@ +local Source = require("luasnip.session.snippet_collection.source") +local util = require("luasnip.util.util") + +-- stylua: ignore +local tsquery_parse = + (vim.treesitter.query and vim.treesitter.query.parse) + and vim.treesitter.query.parse + or vim.treesitter.parse_query + +local M = {} + +-- return: 4-tuple, {start_line, start_col, end_line, end_col}, range of +-- function-call. +local function lua_find_function_call_node_at(bufnr, line) + local has_parser, parser = pcall(vim.treesitter.get_parser, bufnr, "lua") + if not has_parser then + error("Error while getting parser: " .. parser) + end + + local root = parser:parse()[1]:root() + local query = tsquery_parse("lua", [[(function_call) @f_call]]) + for _, node, _ in query:iter_captures(root, bufnr, line, line + 300) do + if node:range() == line then + return { node:range() } + end + end + error( + "Query for `(function_call)` starting at line %s did not yield a result." + ) +end + +local function range_highlight(line_start, line_end, hl_duration_ms) + -- make sure line_end is also visible. + vim.api.nvim_win_set_cursor(0, { line_end, 0 }) + vim.api.nvim_win_set_cursor(0, { line_start, 0 }) + + if hl_duration_ms > 0 then + local hl_buf = vim.api.nvim_get_current_buf() + + -- highlight snippet for 1000ms + local id = vim.api.nvim_buf_set_extmark( + hl_buf, + ls.session.ns_id, + line_start - 1, + 0, + { + -- one line below, at col 0 => entire last line is highlighted. + end_row = line_end - 1 + 1, + hl_group = "Visual", + } + ) + vim.defer_fn(function() + vim.api.nvim_buf_del_extmark(hl_buf, ls.session.ns_id, id) + end, hl_duration_ms) + end +end + +local function json_find_snippet_definition(bufnr, filetype, snippet_name) + local parser_ok, parser = pcall(vim.treesitter.get_parser, bufnr, filetype) + if not parser_ok then + error("Error while getting parser: " .. parser) + end + + local root = parser:parse()[1]:root() + -- don't want to pass through whether this file is json or jsonc, just use + -- parser-language. + local query = tsquery_parse( + parser:lang(), + ([[ + (pair + key: (string (string_content) @key (#eq? @key "%s")) + ) @snippet + ]]):format(snippet_name) + ) + for id, node, _ in query:iter_captures(root, bufnr) do + if + query.captures[id] == "snippet" + and node:parent():parent() == root + then + -- return first match. + return { node:range() } + end + end + + error( + ("Treesitter did not find the definition for snippet `%s`"):format( + snippet_name + ) + ) +end + +local function win_edit(file) + vim.api.nvim_command(":e " .. file) +end + +function M.jump_to_snippet(snip, opts) + opts = opts or {} + local hl_duration_ms = opts.hl_duration_ms or 1500 + local edit_fn = opts.edit_fn or win_edit + + local source = Source.get(snip) + if not source then + print("Snippet does not have a source.") + return + end + + edit_fn(source.file) + -- assumption: after this, file is the current buffer. + + if source.line and source.line_end then + -- happy path: we know both begin and end of snippet-definition. + range_highlight(source.line, source.line_end, hl_duration_ms) + return + end + + local fcall_range + local ft = util.ternary( + vim.bo[0].filetype ~= "", + vim.bo[0].filetype, + vim.api.nvim_buf_get_name(0):match("%.([^%.]+)$") + ) + if ft == "lua" then + if source.line then + -- in lua-file, can get region of definition via treesitter. + -- 0: current buffer. + local ok + ok, fcall_range = + pcall(lua_find_function_call_node_at, 0, source.line - 1) + if not ok then + print( + "Could not determine range for snippet-definition: " + .. fcall_range + ) + vim.api.nvim_win_set_cursor(0, { source.line, 0 }) + return + end + else + print("Can't jump to snippet: source does not provide line.") + return + end + -- matches *.json or *.jsonc. + elseif ft == "json" or ft == "jsonc" then + local ok + ok, fcall_range = pcall(json_find_snippet_definition, 0, ft, snip.name) + if not ok then + print( + "Could not determine range of snippet-definition: " + .. fcall_range + ) + return + end + else + print( + ("Don't know how to highlight snippet-definitions in current buffer `%s`.%s"):format( + vim.api.nvim_buf_get_name(0), + source.line ~= nil and " Jumping to `source.line`" or "" + ) + ) + + if source.line ~= nil then + vim.api.nvim_win_set_cursor(0, { source.line, 0 }) + end + return + end + assert(fcall_range ~= nil, "fcall_range is not nil") + + -- 1 is line_from, 3 is line_end. + -- +1 since range is row-0-indexed. + range_highlight(fcall_range[1] + 1, fcall_range[3] + 1, hl_duration_ms) + + local new_source = Source.from_location( + source.file, + { line = fcall_range[1] + 1, line_end = fcall_range[3] + 1 } + ) + Source.set(snip, new_source) +end + +function M.jump_to_active_snippet(opts) + local active_node = + require("luasnip.session").current_nodes[vim.api.nvim_get_current_buf()] + if not active_node then + print("No active snippet.") + return + end + + local snip = active_node.parent.snippet + M.jump_to_snippet(snip, opts) +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/snippet_list.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/snippet_list.lua new file mode 100644 index 00000000..818714bc --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/snippet_list.lua @@ -0,0 +1,96 @@ +local available = require("luasnip").available + +local function snip_info(snippet) + return { + name = snippet.name, + trigger = snippet.trigger, + description = snippet.description, + wordTrig = snippet.wordTrig and true or false, + regTrig = snippet.regTrig and true or false, + docstring = snippet:get_docstring(), + } +end + +local function get_name(buf) + return "LuaSnip://Snippets" +end + +local win_opts = { foldmethod = "indent" } +local buf_opts = { filetype = "lua" } + +local function set_win_opts(win, opts) + for opt, val in pairs(opts) do + vim.api.nvim_win_set_option(win, opt, val) + end +end + +local function set_buf_opts(buf, opts) + for opt, val in pairs(opts) do + vim.api.nvim_buf_set_option(buf, opt, val) + end +end + +local function make_scratch_buf(buf) + local opts = { + buftype = "nofile", + bufhidden = "wipe", + buflisted = false, + swapfile = false, + modified = false, + modeline = false, + } + + set_buf_opts(buf, opts) +end + +local function display_split(opts) + opts = opts or {} + opts.win_opts = opts.win_opts or win_opts + opts.buf_opts = opts.buf_opts or buf_opts + opts.get_name = opts.get_name or get_name + + return function(printer_result) + -- create and open buffer on right vertical split + vim.cmd("botright vnew") + + -- get buf and win handle + local buf = vim.api.nvim_get_current_buf() + local win = vim.api.nvim_get_current_win() + + -- make scratch buffer + vim.api.nvim_buf_set_name(buf, opts.get_name(buf)) + make_scratch_buf(buf) + + -- disable diagnostics + vim.diagnostic.disable(buf) + + -- set any extra win and buf opts + set_win_opts(win, opts.win_opts) + set_buf_opts(buf, opts.buf_opts) + + -- dump snippets + local replacement = vim.split(printer_result, "\n") + vim.api.nvim_buf_set_lines(buf, 0, 0, false, replacement) + + -- make it unmodifiable at this point + vim.api.nvim_buf_set_option(buf, "modifiable", false) + end +end + +local function open(opts) + opts = opts or {} + opts.snip_info = opts.snip_info or snip_info + opts.printer = opts.printer or vim.inspect + opts.display = opts.display or display_split() + + -- load snippets before changing windows/buffers + local snippets = available(opts.snip_info) + + -- open snippets + opts.display(opts.printer(snippets)) +end + +return { + open = open, + options = { display = display_split }, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/treesitter_postfix.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/treesitter_postfix.lua new file mode 100644 index 00000000..b313d92d --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/extras/treesitter_postfix.lua @@ -0,0 +1,324 @@ +if vim.version().major == 0 and vim.version().minor < 9 then + -- need LanguageTree:tree_for_range and don't want to go through the hassle + -- of differentiating multiple version of query.get/parse. + error("treesitter_postfix does not support neovim < 0.9") +end + +local snip = require("luasnip.nodes.snippet").S +local ts = require("luasnip.extras._treesitter") +local node_util = require("luasnip.nodes.util") +local extend_decorator = require("luasnip.util.extend_decorator") +local tbl = require("luasnip.util.table") +local util = require("luasnip.util.util") + +--- Normalize the arguments passed to treesitter_postfix into a function that +--- returns treesitter-matches to the specified query+captures. +---@param opts LuaSnip.extra.MatchTSNodeOpts +---@return LuaSnip.extra.MatchTSNodeFunc +local function generate_match_tsnode_func(opts) + local match_opts = {} + + if opts.query then + match_opts.query = + vim.treesitter.query.parse(opts.query_lang, opts.query) + else + match_opts.query = vim.treesitter.query.get( + opts.query_lang, + opts.query_name or "luasnip" + ) + end + + match_opts.generator = ts.captures_iter(opts.match_captures or "prefix") + + if type(opts.select) == "function" then + match_opts.selector = opts.select + elseif type(opts.select) == "string" then + match_opts.selector = ts.builtin_tsnode_selectors[opts.select] + assert( + match_opts.selector, + "Selector " .. opts.select .. "is not known" + ) + else + match_opts.selector = ts.builtin_tsnode_selectors.any + end + + ---@param parser LuaSnip.extra.TSParser + ---@param pos { [1]: number, [2]: number } + return function(parser, pos) + return parser:match_at( + match_opts, --[[@as LuaSnip.extra.MatchTSNodeOpts]] + pos + ) + end +end + +local function make_reparse_enter_and_leave_func( + reparse, + bufnr, + trigger_region, + trigger +) + if reparse == "live" then + local context = ts.FixBufferContext.new(bufnr, trigger_region, trigger) + return function() + return context:enter() + end, function(_) + context:leave() + end + elseif reparse == "copy" then + local parser, source = + ts.reparse_buffer_after_removing_match(bufnr, trigger_region) + return function() + return parser, source + end, function() + parser:destroy() + end + else + return function() + return vim.treesitter.get_parser(bufnr), bufnr + end, function(_) end + end +end + +---Optionally parse the buffer +---@param reparse boolean|string|nil +---@param real_resolver function +---@return fun(snippet, line_to_cursor, matched_trigger, captures):table? +local function wrap_with_reparse_context(reparse, real_resolver) + return function(snippet, line_to_cursor, matched_trigger, captures) + local bufnr = vim.api.nvim_win_get_buf(0) + local cursor = util.get_cursor_0ind() + local trigger_region = { + row = cursor[1], + col_range = { + -- includes from, excludes to. + cursor[2] - #matched_trigger, + cursor[2], + }, + } + + local enter, leave = make_reparse_enter_and_leave_func( + reparse, + bufnr, + trigger_region, + matched_trigger + ) + local parser, source = enter() + if parser == nil or source == nil then + return nil + end + + local ret = real_resolver( + snippet, + line_to_cursor, + matched_trigger, + captures, + parser, + source, + bufnr, + { cursor[1], cursor[2] - #matched_trigger } + ) + + leave() + + return ret + end +end + +---@param match_tsnode LuaSnip.extra.MatchTSNodeFunc Determines the constraints on the matched node. +local function generate_resolve_expand_param(match_tsnode, user_resolver) + ---@param snippet any + ---@param line_to_cursor string + ---@param matched_trigger string + ---@param captures any + ---@param parser LanguageTree + ---@param source number|string + ---@param bufnr number + ---@param pos { [1]: number, [2]: number } + return function( + snippet, + line_to_cursor, + matched_trigger, + captures, + parser, + source, + bufnr, + pos + ) + local ts_parser = ts.TSParser.new(bufnr, parser, source) + if ts_parser == nil then + return + end + + local row, col = unpack(pos) + + local best_match, prefix_node = match_tsnode(ts_parser, { row, col }) + + if best_match == nil or prefix_node == nil then + return nil + end + + local start_row, start_col, _, _ = prefix_node:range() + + local env = { + LS_TSMATCH = vim.split(ts_parser:get_node_text(prefix_node), "\n"), + -- filled subsequently. + LS_TSDATA = {}, + } + for capture_name, node in pairs(best_match) do + env["LS_TSCAPTURE_" .. capture_name:upper()] = + vim.split(ts_parser:get_node_text(node), "\n") + + local from_r, from_c, to_r, to_c = node:range() + env.LS_TSDATA[capture_name] = { + type = node:type(), + range = { { from_r, from_c }, { to_r, to_c } }, + } + end + + local ret = { + trigger = matched_trigger, + captures = captures, + clear_region = { + from = { + start_row, + start_col, + }, + to = { + pos[1], + pos[2] + #matched_trigger, + }, + }, + env_override = env, + } + + if user_resolver then + local user_res = user_resolver( + snippet, + line_to_cursor, + matched_trigger, + captures + ) + if user_res then + ret = vim.tbl_deep_extend( + "force", + ret, + user_res, + { env_override = {} } + ) + else + return nil + end + end + + return ret + end +end + +local function generate_simple_parent_lookup_function(lookup_fun) + ---@param types string|string[] + ---@return LuaSnip.extra.MatchTSNodeFunc + return function(types) + local type_checker = tbl.list_to_set(types) + ---@param parser LuaSnip.extra.TSParser + ---@param pos { [1]: number, [2]: number } + return function(parser, pos) + -- check node just before the position. + local root = parser:get_node_at_pos({ pos[1], pos[2] - 1 }) + + if root == nil then + return + end + + ---@param node TSNode + local check_node_exclude_pos = function(node) + local _, _, end_row, end_col = node:range(false) + return end_row == pos[1] and end_col == pos[2] + end + ---@param node TSNode + local check_node_type = function(node) + return type_checker[node:type()] + end + + local prefix_node = lookup_fun(root, function(node) + return check_node_type(node) and check_node_exclude_pos(node) + end) + if prefix_node == nil then + return nil, nil + end + return {}, prefix_node + end + end +end + +---@param n number +local function find_nth_parent(n) + ---@param parser LuaSnip.extra.TSParser + ---@param pos { [1]: number, [2]: number } + return function(parser, pos) + local inner_node = parser:get_node_at_pos({ pos[1], pos[2] - 1 }) + + if inner_node == nil then + return + end + + ---@param node TSNode + local check_node_exclude_pos = function(node) + local _, _, end_row, end_col = node:range(false) + return end_row == pos[1] and end_col == pos[2] + end + + return {}, ts.find_nth_parent(inner_node, n, check_node_exclude_pos) + end +end + +local function treesitter_postfix(context, nodes, opts) + opts = opts or {} + vim.validate({ + context = { context, { "string", "table" } }, + nodes = { nodes, "table" }, + opts = { opts, "table" }, + }) + + context = node_util.wrap_context(context) + context.wordTrig = false + + ---@type LuaSnip.extra.MatchTSNodeFunc + local match_tsnode_func + if type(context.matchTSNode) == "function" then + match_tsnode_func = context.matchTSNode + else + match_tsnode_func = generate_match_tsnode_func(context.matchTSNode) + end + + local expand_params_resolver = generate_resolve_expand_param( + match_tsnode_func, + context.resolveExpandParams + ) + + context.resolveExpandParams = + wrap_with_reparse_context(context.reparseBuffer, expand_params_resolver) + + return snip(context, nodes, opts) +end + +extend_decorator.register( + treesitter_postfix, + { arg_indx = 1, extend = node_util.snippet_extend_context }, + { arg_indx = 3 } +) + +return { + treesitter_postfix = treesitter_postfix, + builtin = { + tsnode_matcher = { + find_topmost_types = generate_simple_parent_lookup_function( + ts.find_topmost_parent + ), + find_first_types = generate_simple_parent_lookup_function( + ts.find_first_parent + ), + find_nth_parent = find_nth_parent, + }, + }, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/health.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/health.lua new file mode 100644 index 00000000..a06fea12 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/health.lua @@ -0,0 +1,14 @@ +return { + check = function() + vim.health.start("luasnip") + local jsregexp = require("luasnip.util.jsregexp") + if jsregexp then + vim.health.ok("jsregexp is installed") + else + vim.health.warn([[ + For Variable/Placeholder-transformations, luasnip requires + the jsregexp library. See `:h luasnip-lsp-snippets-transformations` for advice + ]]) + end + end, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/init.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/init.lua new file mode 100644 index 00000000..14d07443 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/init.lua @@ -0,0 +1,896 @@ +local util = require("luasnip.util.util") +local lazy_table = require("luasnip.util.lazy_table") +local types = require("luasnip.util.types") +local node_util = require("luasnip.nodes.util") + +local session = require("luasnip.session") +local snippet_collection = require("luasnip.session.snippet_collection") +local Environ = require("luasnip.util.environ") +local extend_decorator = require("luasnip.util.extend_decorator") + +local loader = require("luasnip.loaders") + +local next_expand = nil +local next_expand_params = nil +local ls +local luasnip_data_dir = vim.fn.stdpath("cache") .. "/luasnip" + +local log = require("luasnip.util.log").new("main") + +local function get_active_snip() + local node = session.current_nodes[vim.api.nvim_get_current_buf()] + if not node then + return nil + end + while node.parent do + node = node.parent + end + return node +end + +-- returns matching snippet (needs to be copied before usage!) and its expand- +-- parameters(trigger and captures). params are returned here because there's +-- no need to recalculate them. +local function match_snippet(line, type) + return snippet_collection.match_snippet( + line, + util.get_snippet_filetypes(), + type + ) +end + +-- ft: +-- * string: interpreted as filetype, return corresponding snippets. +-- * nil: return snippets for all filetypes: +-- { +-- lua = {...}, +-- cpp = {...}, +-- ... +-- } +-- opts: optional args, can contain `type`, either "snippets" or "autosnippets". +-- +-- return table, may be empty. +local function get_snippets(ft, opts) + opts = opts or {} + + return snippet_collection.get_snippets(ft, opts.type or "snippets") or {} +end + +local function default_snip_info(snip) + return { + name = snip.name, + trigger = snip.trigger, + description = snip.description, + wordTrig = snip.wordTrig and true or false, + regTrig = snip.regTrig and true or false, + } +end + +local function available(snip_info) + snip_info = snip_info or default_snip_info + + local fts = util.get_snippet_filetypes() + local res = {} + for _, ft in ipairs(fts) do + res[ft] = {} + for _, snip in ipairs(get_snippets(ft)) do + if not snip.invalidated then + table.insert(res[ft], snip_info(snip)) + end + end + for _, snip in ipairs(get_snippets(ft, { type = "autosnippets" })) do + if not snip.invalidated then + table.insert(res[ft], snip_info(snip)) + end + end + end + return res +end + +local unlink_set_adjacent_as_current +local function unlink_set_adjacent_as_current_no_log(snippet) + -- prefer setting previous/outer insertNode as current node. + local next_current = + -- either pick i0 of snippet before, or i(-1) of next snippet. + snippet.prev.prev or snippet:next_node() + snippet:remove_from_jumplist() + + if next_current then + -- if snippet was active before, we need to now set its parent to be no + -- longer inner_active. + if + snippet.parent_node == next_current and next_current.inner_active + then + snippet.parent_node:input_leave_children() + else + -- set no_move. + local ok, err = pcall(next_current.input_enter, next_current, true) + if not ok then + -- this won't try to set the previously broken snippet as + -- current, since that link is removed in + -- `remove_from_jumplist`. + unlink_set_adjacent_as_current( + next_current.parent.snippet, + "Error while setting adjacent snippet as current node: %s", + err + ) + end + end + end + + session.current_nodes[vim.api.nvim_get_current_buf()] = next_current +end +function unlink_set_adjacent_as_current(snippet, reason, ...) + log.warn("Removing snippet %s: %s", snippet.trigger, reason:format(...)) + unlink_set_adjacent_as_current_no_log(snippet) +end + +local function unlink_current() + local current = session.current_nodes[vim.api.nvim_get_current_buf()] + if not current then + print("No active Snippet") + return + end + unlink_set_adjacent_as_current_no_log(current.parent.snippet) +end + +-- return next active node. +local function safe_jump_current(dir, no_move, dry_run) + local node = session.current_nodes[vim.api.nvim_get_current_buf()] + if not node then + return nil + end + + local ok, res = pcall(node.jump_from, node, dir, no_move, dry_run) + if ok then + return res + else + local snip = node.parent.snippet + + unlink_set_adjacent_as_current( + snip, + "Removing snippet `%s` due to error %s", + snip.trigger, + res + ) + return session.current_nodes[vim.api.nvim_get_current_buf()] + end +end +local function jump(dir) + local current = session.current_nodes[vim.api.nvim_get_current_buf()] + if current then + local next_node = util.no_region_check_wrap(safe_jump_current, dir) + if next_node == nil then + session.current_nodes[vim.api.nvim_get_current_buf()] = nil + return true + end + if session.config.exit_roots then + if next_node.pos == 0 and next_node.parent.parent_node == nil then + session.current_nodes[vim.api.nvim_get_current_buf()] = nil + return true + end + end + session.current_nodes[vim.api.nvim_get_current_buf()] = next_node + return true + else + return false + end +end +local function jump_destination(dir) + -- dry run of jump (+no_move ofc.), only retrieves destination-node. + return safe_jump_current(dir, true, { active = {} }) +end + +local function jumpable(dir) + -- node is jumpable if there is a destination. + return jump_destination(dir) + ~= session.current_nodes[vim.api.nvim_get_current_buf()] +end + +local function expandable() + next_expand, next_expand_params = + match_snippet(util.get_current_line_to_cursor(), "snippets") + return next_expand ~= nil +end + +local function expand_or_jumpable() + return expandable() or jumpable(1) +end + +local function in_snippet() + -- check if the cursor on a row inside a snippet. + local node = session.current_nodes[vim.api.nvim_get_current_buf()] + if not node then + return false + end + local snippet = node.parent.snippet + local ok, snip_begin_pos, snip_end_pos = + pcall(snippet.mark.pos_begin_end, snippet.mark) + if not ok then + -- if there was an error getting the position, the snippets text was + -- most likely removed, resulting in messed up extmarks -> error. + -- remove the snippet. + unlink_set_adjacent_as_current( + snippet, + "Error while getting extmark-position: %s", + snip_begin_pos + ) + return + end + local pos = vim.api.nvim_win_get_cursor(0) + if pos[1] - 1 >= snip_begin_pos[1] and pos[1] - 1 <= snip_end_pos[1] then + return true -- cursor not on row inside snippet + end +end + +local function expand_or_locally_jumpable() + return expandable() or (in_snippet() and jumpable(1)) +end + +local function locally_jumpable(dir) + return in_snippet() and jumpable(dir) +end + +local function _jump_into_default(snippet) + return util.no_region_check_wrap(snippet.jump_into, snippet, 1) +end + +-- opts.clear_region: table, keys `from` and `to`, both (0,0)-indexed. +local function snip_expand(snippet, opts) + local snip = snippet:copy() + + opts = opts or {} + opts.expand_params = opts.expand_params or {} + -- override with current position if none given. + opts.pos = opts.pos or util.get_cursor_0ind() + opts.jump_into_func = opts.jump_into_func or _jump_into_default + opts.indent = vim.F.if_nil(opts.indent, true) + + snip.trigger = opts.expand_params.trigger or snip.trigger + snip.captures = opts.expand_params.captures or {} + + local info = + { trigger = snip.trigger, captures = snip.captures, pos = opts.pos } + local env = Environ:new(info) + Environ:override(env, opts.expand_params.env_override or {}) + + local pos_id = vim.api.nvim_buf_set_extmark( + 0, + session.ns_id, + opts.pos[1], + opts.pos[2], + -- track position between pos[2]-1 and pos[2]. + { right_gravity = false } + ) + + -- optionally clear text. Text has to be cleared befor jumping into the new + -- snippet, as the cursor-position can end up in the wrong position (to be + -- precise the text will be moved, the cursor will stay at the same + -- position, which is just as bad) if text before the cursor, on the same + -- line is cleared. + if opts.clear_region then + vim.api.nvim_buf_set_text( + 0, + opts.clear_region.from[1], + opts.clear_region.from[2], + opts.clear_region.to[1], + opts.clear_region.to[2], + { "" } + ) + end + + local snip_parent_node = snip:trigger_expand( + session.current_nodes[vim.api.nvim_get_current_buf()], + pos_id, + env, + opts.indent + ) + + -- jump_into-callback returns new active node. + session.current_nodes[vim.api.nvim_get_current_buf()] = + opts.jump_into_func(snip) + + local buf_snippet_roots = + session.snippet_roots[vim.api.nvim_get_current_buf()] + if not session.config.keep_roots and #buf_snippet_roots > 1 then + -- if history is not set, and there is more than one snippet-root, + -- remove the other one. + -- The nice thing is: since we maintain that #buf_snippet_roots == 1 + -- whenever outside of this function, we know that if we're here, it's + -- because this snippet was just inserted into buf_snippet_roots. + -- Armed with this knowledge, we can just check which of the roots is + -- this snippet, and remove the other one. + buf_snippet_roots[buf_snippet_roots[1] == snip and 2 or 1]:remove_from_jumplist() + end + + -- stores original snippet, it doesn't contain any data from expansion. + session.last_expand_snip = snippet + session.last_expand_opts = opts + + -- set last action for vim-repeat. + -- will silently fail if vim-repeat isn't available. + -- -1 to disable count. + vim.cmd([[silent! call repeat#set("\luasnip-expand-repeat", -1)]]) + + return snip +end + +---Find a snippet matching the current cursor-position. +---@param opts table: may contain: +--- - `jump_into_func`: passed through to `snip_expand`. +---@return boolean: whether a snippet was expanded. +local function expand(opts) + local expand_params + local snip + -- find snip via next_expand (set from previous expandable()) or manual matching. + if next_expand ~= nil then + snip = next_expand + expand_params = next_expand_params + + next_expand = nil + next_expand_params = nil + else + snip, expand_params = + match_snippet(util.get_current_line_to_cursor(), "snippets") + end + if snip then + local jump_into_func = opts and opts.jump_into_func + + local cursor = util.get_cursor_0ind() + + local clear_region = expand_params.clear_region + or { + from = { + cursor[1], + cursor[2] - #expand_params.trigger, + }, + to = cursor, + } + + -- override snip with expanded copy. + snip = snip_expand(snip, { + expand_params = expand_params, + -- clear trigger-text. + clear_region = clear_region, + jump_into_func = jump_into_func, + }) + + return true + end + return false +end + +local function expand_auto() + local snip, expand_params = + match_snippet(util.get_current_line_to_cursor(), "autosnippets") + if snip then + local cursor = util.get_cursor_0ind() + local clear_region = expand_params.clear_region + or { + from = { + cursor[1], + cursor[2] - #expand_params.trigger, + }, + to = cursor, + } + snip = snip_expand(snip, { + expand_params = expand_params, + -- clear trigger-text. + clear_region = clear_region, + }) + end +end + +local function expand_repeat() + -- prevent clearing text with repeated expand. + session.last_expand_opts.clear_region = nil + session.last_expand_opts.pos = nil + + snip_expand(session.last_expand_snip, session.last_expand_opts) +end + +-- return true and expand snippet if expandable, return false if not. +local function expand_or_jump() + if expand() then + return true + end + if jump(1) then + return true + end + return false +end + +local function lsp_expand(body, opts) + -- expand snippet as-is. + snip_expand( + ls.parser.parse_snippet( + "", + body, + { trim_empty = false, dedent = false } + ), + opts + ) +end + +local function choice_active() + return session.active_choice_nodes[vim.api.nvim_get_current_buf()] ~= nil +end + +-- attempts to do some action on the snippet (like change_choice, set_choice), +-- if it fails the snippet is removed and the next snippet becomes the current node. +-- ... is passed to pcall as-is. +local function safe_choice_action(snip, ...) + local ok, res = pcall(...) + if ok then + return res + else + -- not very elegant, but this way we don't have a near + -- re-implementation of unlink_current. + unlink_set_adjacent_as_current( + snip, + "Removing snippet `%s` due to error %s", + snip.trigger, + res + ) + return session.current_nodes[vim.api.nvim_get_current_buf()] + end +end +local function change_choice(val) + local active_choice = + session.active_choice_nodes[vim.api.nvim_get_current_buf()] + assert(active_choice, "No active choiceNode") + local new_active = util.no_region_check_wrap( + safe_choice_action, + active_choice.parent.snippet, + active_choice.change_choice, + active_choice, + val, + session.current_nodes[vim.api.nvim_get_current_buf()] + ) + session.current_nodes[vim.api.nvim_get_current_buf()] = new_active +end + +local function set_choice(choice_indx) + local active_choice = + session.active_choice_nodes[vim.api.nvim_get_current_buf()] + assert(active_choice, "No active choiceNode") + local choice = active_choice.choices[choice_indx] + assert(choice, "Invalid Choice") + local new_active = util.no_region_check_wrap( + safe_choice_action, + active_choice.parent.snippet, + active_choice.set_choice, + active_choice, + choice, + session.current_nodes[vim.api.nvim_get_current_buf()] + ) + session.current_nodes[vim.api.nvim_get_current_buf()] = new_active +end + +local function get_current_choices() + local active_choice = + session.active_choice_nodes[vim.api.nvim_get_current_buf()] + assert(active_choice, "No active choiceNode") + + local choice_lines = {} + + active_choice:update_static_all() + for i, choice in ipairs(active_choice.choices) do + choice_lines[i] = table.concat(choice:get_docstring(), "\n") + end + + return choice_lines +end + +local function active_update_dependents() + local active = session.current_nodes[vim.api.nvim_get_current_buf()] + -- special case for startNode, cannot focus on those (and they can't + -- have dependents) + -- don't update if a jump/change_choice is in progress. + if not session.jump_active and active and active.pos > 0 then + -- Save cursor-pos to restore later. + local cur = util.get_cursor_0ind() + local cur_mark = vim.api.nvim_buf_set_extmark( + 0, + session.ns_id, + cur[1], + cur[2], + { right_gravity = false } + ) + + local ok, err = pcall(active.update_dependents, active) + if not ok then + unlink_set_adjacent_as_current( + active.parent.snippet, + "Error while updating dependents for snippet %s due to error %s", + active.parent.snippet.trigger, + err + ) + return + end + + -- 'restore' orientation of extmarks, may have been changed by some set_text or similar. + ok, err = pcall(active.focus, active) + if not ok then + unlink_set_adjacent_as_current( + active.parent.snippet, + "Error while entering node in snippet %s: %s", + active.parent.snippet.trigger, + err + ) + + return + end + + -- Don't account for utf, nvim_win_set_cursor doesn't either. + cur = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + cur_mark, + { details = false } + ) + util.set_cursor_0ind(cur) + end +end + +local function store_snippet_docstrings(snippet_table) + -- ensure the directory exists. + -- 493 = 0755 + vim.loop.fs_mkdir(luasnip_data_dir, 493) + + -- fs_open() with w+ creates the file if nonexistent. + local docstring_cache_fd = vim.loop.fs_open( + luasnip_data_dir .. "/docstrings.json", + "w+", + -- 420 = 0644 + 420 + ) + + -- get size for fs_read() + local cache_size = vim.loop.fs_fstat(docstring_cache_fd).size + local file_could_be_read, docstrings = pcall( + util.json_decode, + -- offset 0. + vim.loop.fs_read(docstring_cache_fd, cache_size, 0) + ) + docstrings = file_could_be_read and docstrings or {} + + for ft, snippets in pairs(snippet_table) do + if not docstrings[ft] then + docstrings[ft] = {} + end + for _, snippet in ipairs(snippets) do + docstrings[ft][snippet.trigger] = snippet:get_docstring() + end + end + + vim.loop.fs_write(docstring_cache_fd, util.json_encode(docstrings)) +end + +local function load_snippet_docstrings(snippet_table) + -- ensure the directory exists. + -- 493 = 0755 + vim.loop.fs_mkdir(luasnip_data_dir, 493) + + -- fs_open() with "r" returns nil if the file doesn't exist. + local docstring_cache_fd = vim.loop.fs_open( + luasnip_data_dir .. "/docstrings.json", + "r", + -- 420 = 0644 + 420 + ) + + if not docstring_cache_fd then + error("Cached docstrings could not be read!") + return + end + -- get size for fs_read() + local cache_size = vim.loop.fs_fstat(docstring_cache_fd).size + local docstrings = util.json_decode( + -- offset 0. + vim.loop.fs_read(docstring_cache_fd, cache_size, 0) + ) + + for ft, snippets in pairs(snippet_table) do + -- skip if fieltype not in cache. + if docstrings[ft] then + for _, snippet in ipairs(snippets) do + -- only set if it hasn't been set already. + if not snippet.docstring then + snippet.docstring = docstrings[ft][snippet.trigger] + end + end + end + end +end + +local function unlink_current_if_deleted() + local node = session.current_nodes[vim.api.nvim_get_current_buf()] + if not node then + return + end + local snippet = node.parent.snippet + + -- extmarks_valid checks that + -- * textnodes that should contain text still do so, and + -- * that extmarks still fulfill all expectations (should be successive, no gaps, etc.) + if not snippet:extmarks_valid() then + unlink_set_adjacent_as_current( + snippet, + "Detected deletion of snippet `%s`, removing it", + snippet.trigger + ) + end +end + +local function exit_out_of_region(node) + -- if currently jumping via luasnip or no active node: + if session.jump_active or not node then + return + end + + local pos = util.get_cursor_0ind() + local snippet + if node.type == types.snippet then + snippet = node + else + snippet = node.parent.snippet + end + + -- find root-snippet. + while snippet.parent_node do + snippet = snippet.parent_node.parent.snippet + end + + local ok, snip_begin_pos, snip_end_pos = + pcall(snippet.mark.pos_begin_end, snippet.mark) + + if not ok then + unlink_set_adjacent_as_current( + snippet, + "Error while getting extmark-position: %s", + snip_begin_pos + ) + return + end + + -- stylua: ignore + -- leave if curser before or behind snippet + if pos[1] < snip_begin_pos[1] or + pos[1] > snip_end_pos[1] then + + -- make sure the snippet can safely be entered, since it may have to + -- be, in `refocus`. + if not snippet:extmarks_valid() then + unlink_set_adjacent_as_current(snippet, "Leaving snippet-root due to invalid extmarks.") + return + end + + local next_active = snippet.insert_nodes[0] + -- if there is a snippet nested into the $0, enter its $0 instead, + -- recursively. + -- This is to ensure that a jump forward after leaving the region of a + -- root will jump to the next root, or not result in a jump at all. + while next_active.inner_first do + -- make sure next_active is nested into completely intact + -- snippets, since that is a precondition on the to-node of + if not next_active.inner_first:extmarks_valid() then + next_active.inner_first:remove_from_jumplist() + else + -- inner_first is always the snippet, not the -1-node. + next_active = next_active.inner_first.insert_nodes[0] + end + end + + node_util.refocus(node, next_active) + session.current_nodes[vim.api.nvim_get_current_buf()] = next_active + end +end + +-- ft string, extend_ft table of strings. +local function filetype_extend(ft, extend_ft) + vim.list_extend(session.ft_redirect[ft], extend_ft) + session.ft_redirect[ft] = util.deduplicate(session.ft_redirect[ft]) +end + +-- ft string, fts table of strings. +local function filetype_set(ft, fts) + session.ft_redirect[ft] = util.deduplicate(fts) +end + +local function cleanup() + -- Use this to reload luasnip + vim.api.nvim_exec_autocmds( + "User", + { pattern = "LuasnipCleanup", modeline = false } + ) + -- clear all snippets. + snippet_collection.clear_snippets() + loader.cleanup() +end + +local function refresh_notify(ft) + snippet_collection.refresh_notify(ft) +end + +local function setup_snip_env() + local combined_table = vim.tbl_extend("force", _G, session.config.snip_env) + -- TODO: if desired, take into account _G's __index before looking into + -- snip_env's __index. + setmetatable(combined_table, getmetatable(session.config.snip_env)) + + setfenv(2, combined_table) +end +local function get_snip_env() + return session.get_snip_env() +end + +local function get_id_snippet(id) + return snippet_collection.get_id_snippet(id) +end + +local function add_snippets(ft, snippets, opts) + -- don't use yet, not available in some neovim-versions. + -- + -- vim.validate({ + -- filetype = { ft, { "string", "nil" } }, + -- snippets = { snippets, "table" }, + -- opts = { opts, { "table", "nil" } }, + -- }) + + opts = opts or {} + opts.refresh_notify = opts.refresh_notify or true + -- alternatively, "autosnippets" + opts.type = opts.type or "snippets" + + -- if ft is nil, snippets already has this format. + if ft then + snippets = { + [ft] = snippets, + } + end + + snippet_collection.add_snippets(snippets, opts) + + if opts.refresh_notify then + for ft_, _ in pairs(snippets) do + refresh_notify(ft_) + end + end +end + +local function clean_invalidated(opts) + opts = opts or {} + snippet_collection.clean_invalidated(opts) +end + +local function activate_node(opts) + opts = opts or {} + local pos = opts.pos or util.get_cursor_0ind() + local strict = vim.F.if_nil(opts.strict, false) + local select = vim.F.if_nil(opts.select, true) + + -- find tree-node the snippet should be inserted at (could be before another node). + local _, _, _, node = node_util.snippettree_find_undamaged_node(pos, { + tree_respect_rgravs = false, + tree_preference = node_util.binarysearch_preference.inside, + snippet_mode = "interactive", + }) + + if not node then + error("No Snippet at that position") + return + end + + -- only activate interactive nodes, or nodes that are immediately nested + -- inside a choiceNode. + if not node:interactive() then + if strict then + error("Refusing to activate a non-interactive node.") + return + else + -- fall back to known insertNode. + -- snippet.insert_nodes[1] may be preferable, but that is not + -- certainly an insertNode (and does not even certainly contain an + -- insertNode, think snippetNode with only textNode). + -- We could *almost* find the first activateable node by + -- dry_run-jumping into the snippet, but then we'd also need some + -- mechanism for setting the active-state of all nodes to false, + -- which we don't yet have. + -- + -- Instead, just choose -1-node, and allow jumps from there, which + -- is much simpler. + node = node.parent.snippet.prev + end + end + + node_util.refocus( + session.current_nodes[vim.api.nvim_get_current_buf()], + node + ) + if select then + -- input_enter node again, to get highlight and the like. + -- One side-effect of this is that an event will be execute twice, but I + -- feel like that is a trade-off worth doing, since it otherwise refocus + -- would have to be more complicated (or at least, restructured). + node:input_enter() + end + session.current_nodes[vim.api.nvim_get_current_buf()] = node +end + +-- make these lazy, such that we don't have to load them before it's really +-- necessary (drives up cost of initial load, otherwise). +-- stylua: ignore +local ls_lazy = { + s = function() return require("luasnip.nodes.snippet").S end, + sn = function() return require("luasnip.nodes.snippet").SN end, + t = function() return require("luasnip.nodes.textNode").T end, + f = function() return require("luasnip.nodes.functionNode").F end, + i = function() return require("luasnip.nodes.insertNode").I end, + c = function() return require("luasnip.nodes.choiceNode").C end, + d = function() return require("luasnip.nodes.dynamicNode").D end, + r = function() return require("luasnip.nodes.restoreNode").R end, + snippet = function() return require("luasnip.nodes.snippet").S end, + snippet_node = function() return require("luasnip.nodes.snippet").SN end, + parent_indexer = function() return require("luasnip.nodes.snippet").P end, + indent_snippet_node = function() return require("luasnip.nodes.snippet").ISN end, + text_node = function() return require("luasnip.nodes.textNode").T end, + function_node = function() return require("luasnip.nodes.functionNode").F end, + insert_node = function() return require("luasnip.nodes.insertNode").I end, + choice_node = function() return require("luasnip.nodes.choiceNode").C end, + dynamic_node = function() return require("luasnip.nodes.dynamicNode").D end, + restore_node = function() return require("luasnip.nodes.restoreNode").R end, + parser = function() return require("luasnip.util.parser") end, + config = function() return require("luasnip.config") end, + multi_snippet = function() return require("luasnip.nodes.multiSnippet").new_multisnippet end, + snippet_source = function() return require("luasnip.session.snippet_collection.source") end, + select_keys = function() return require("luasnip.util.select").select_keys end +} + +ls = lazy_table({ + expand_or_jumpable = expand_or_jumpable, + expand_or_locally_jumpable = expand_or_locally_jumpable, + locally_jumpable = locally_jumpable, + jumpable = jumpable, + expandable = expandable, + in_snippet = in_snippet, + expand = expand, + snip_expand = snip_expand, + expand_repeat = expand_repeat, + expand_auto = expand_auto, + expand_or_jump = expand_or_jump, + jump = jump, + get_active_snip = get_active_snip, + choice_active = choice_active, + change_choice = change_choice, + set_choice = set_choice, + get_current_choices = get_current_choices, + unlink_current = unlink_current, + lsp_expand = lsp_expand, + active_update_dependents = active_update_dependents, + available = available, + exit_out_of_region = exit_out_of_region, + load_snippet_docstrings = load_snippet_docstrings, + store_snippet_docstrings = store_snippet_docstrings, + unlink_current_if_deleted = unlink_current_if_deleted, + filetype_extend = filetype_extend, + filetype_set = filetype_set, + add_snippets = add_snippets, + get_snippets = get_snippets, + get_id_snippet = get_id_snippet, + setup_snip_env = setup_snip_env, + get_snip_env = get_snip_env, + clean_invalidated = clean_invalidated, + get_snippet_filetypes = util.get_snippet_filetypes, + jump_destination = jump_destination, + session = session, + cleanup = cleanup, + refresh_notify = refresh_notify, + env_namespace = Environ.env_namespace, + setup = require("luasnip.config").setup, + extend_decorator = extend_decorator, + log = require("luasnip.util.log"), + activate_node = activate_node, +}, ls_lazy) + +return ls diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/data.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/data.lua new file mode 100644 index 00000000..6e21d6c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/data.lua @@ -0,0 +1,27 @@ +--- This module stores all files loaded by any of the loaders, ordered by their +--- filetype, and other data. +--- This is to facilitate luasnip.loaders.edit_snippets, and to handle +--- persistency of data, which is not given if it is stored in the module-file, +--- since the module-name we use (luasnip.loaders.*) is not necessarily the one +--- used by the user (luasnip/loader/*, for example), and the returned modules +--- are different tables. + +local autotable = require("luasnip.util.auto_table").autotable + +local M = { + lua_collections = {}, + lua_ft_paths = autotable(2), + + snipmate_collections = {}, + snipmate_ft_paths = autotable(2), + -- set by loader. + snipmate_cache = nil, + + vscode_package_collections = {}, + vscode_standalone_watchers = {}, + vscode_ft_paths = autotable(2), + -- set by loader. + vscode_cache = nil, +} + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/from_lua.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/from_lua.lua new file mode 100644 index 00000000..629d5c64 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/from_lua.lua @@ -0,0 +1,466 @@ +-- loads snippets from directory structured almost like snipmate-collection: +-- - files all named .lua +-- - each returns table containing keys (optional) "snippets" and +-- "autosnippets", value for each a list of snippets. +-- +-- cache: +-- - lazy_load_paths: { +-- { +-- add_opts = {...}, +-- ft1 = {filename1, filename2}, +-- ft2 = {filename1}, +-- ... +-- }, { +-- add_opts = {...}, +-- ft1 = {filename1}, +-- ... +-- } +-- } +-- +-- each call to load generates a new entry in that list. We cannot just merge +-- all files for some ft since add_opts might be different (they might be from +-- different lazy_load-calls). + +local loader_util = require("luasnip.loaders.util") +local log = require("luasnip.util.log").new("lua-loader") +local session = require("luasnip.session") +local util = require("luasnip.util.util") +local autotable = require("luasnip.util.auto_table").autotable +local tree_watcher = require("luasnip.loaders.fs_watchers").tree +local path_watcher = require("luasnip.loaders.fs_watchers").path +local digraph = require("luasnip.util.directed_graph") +local refresh_notify = + require("luasnip.session.enqueueable_operations").refresh_notify +local clean_invalidated = + require("luasnip.session.enqueueable_operations").clean_invalidated + +local Data = require("luasnip.loaders.data") + +local M = {} + +-- ASSUMPTION: this function will only be called inside the snippet-constructor, +-- to find the location of the lua-loaded file calling it. +-- It is not exported, because it will (in its current state) only ever be used +-- in one place, and it feels a bit wrong to expose put a function into `M`. +-- Instead, it is inserted into the global environment before a luasnippet-file +-- is loaded, and removed from it immediately when this is done +local function get_loaded_file_debuginfo() + -- we can skip looking at the first four stackframes, since + -- 1 is this function + -- 2 is the snippet-constructor + -- ... (here anything is going on, could be 0 stackframes, could be many) + -- n-2 (at least 3) is the loaded file + -- n-1 (at least 4) is pcall + -- n (at least 5) is _luasnip_load_file + local current_call_depth = 4 + local debuginfo + + repeat + current_call_depth = current_call_depth + 1 + debuginfo = debug.getinfo(current_call_depth, "n") + until debuginfo.name == "_luasnip_load_file" + + -- ret is stored into a local, and not returned immediately to prevent tail + -- call optimization, which seems to invalidate the stackframe-numbers + -- determined earlier. + -- + -- current_call_depth-0 is _luasnip_load_file, + -- current_call_depth-1 is pcall, and + -- current_call_depth-2 is the lua-loaded file. + -- "Sl": get only source-file and current line. + local ret = debug.getinfo(current_call_depth - 2, "Sl") + return ret +end + +local function search_lua_rtp(modulename) + -- essentially stolen from vim.loader. + local rtp_lua_path = package.path + for _, path in ipairs(vim.api.nvim_get_runtime_file("", true)) do + rtp_lua_path = rtp_lua_path + .. (";%s/lua/?.lua;%s/lua/?/init.lua"):format(path, path) + end + + return package.searchpath(modulename, rtp_lua_path) +end + +local function _luasnip_load_file(file) + -- vim.loader.enabled does not seem to be official api, so always reset + -- if the loader is available. + -- To be sure, even pcall it, in case there are conditions under which + -- it might error. + if vim.loader then + -- pcall, not sure if this can fail in some way.. + -- Does not seem like it though + local ok, res = pcall(vim.loader.reset, file) + if not ok then + log.warn("Could not reset cache for file %s\n: %s", file, res) + end + end + + local func, error_msg = loadfile(file) + if error_msg then + log.error("Failed to load %s\n: %s", file, error_msg) + error(string.format("Failed to load %s\n: %s", file, error_msg)) + end + + -- the loaded file may add snippets to these tables, they'll be + -- combined with the snippets returned regularly. + local file_added_snippets = {} + local file_added_autosnippets = {} + + local dependent_files = {} + + -- setup snip_env in func + local func_env + local function ls_tracked_dofile(filename) + local package_func, err_msg = loadfile(filename) + if package_func then + setfenv(package_func, func_env) + table.insert(dependent_files, filename) + else + error(("File %s could not be loaded: %s"):format(filename, err_msg)) + end + + return package_func() + end + func_env = vim.tbl_extend( + "force", + -- extend the current(expected!) globals with the snip_env, and the + -- two tables. + _G, + session.get_snip_env(), + { + ls_file_snippets = file_added_snippets, + ls_file_autosnippets = file_added_autosnippets, + ls_tracked_dofile = ls_tracked_dofile, + ls_tracked_dopackage = function(package_name) + local package_file = search_lua_rtp(package_name) + if not package_file then + error( + ("Could not find package %s in rtp and package.path"):format( + package_name + ) + ) + end + return ls_tracked_dofile(package_file) + end, + } + ) + -- defaults snip-env requires metatable for resolving + -- lazily-initialized keys. If we have to combine this with an eventual + -- metatable of _G, look into unifying ls.setup_snip_env and this. + setmetatable(func_env, getmetatable(session.get_snip_env())) + setfenv(func, func_env) + + -- Since this function has to reach the snippet-constructor, and fenvs + -- aren't inherited by called functions, we have to set it in the global + -- environment. + _G.__luasnip_get_loaded_file_frame_debuginfo = util.ternary( + session.config.loaders_store_source, + get_loaded_file_debuginfo, + nil + ) + local run_ok, file_snippets, file_autosnippets = pcall(func) + -- immediately nil it. + _G.__luasnip_get_loaded_file_frame_debuginfo = nil + + if not run_ok then + log.error("Failed to execute\n: %s", file, file_snippets) + error("Failed to execute " .. file .. "\n: " .. file_snippets) + end + + -- make sure these aren't nil. + file_snippets = file_snippets or {} + file_autosnippets = file_autosnippets or {} + + vim.list_extend(file_snippets, file_added_snippets) + vim.list_extend(file_autosnippets, file_added_autosnippets) + + return file_snippets, file_autosnippets, dependent_files +end + +local function lua_package_file_filter(fname) + return fname:match("%.lua$") +end + +--- Collection watches all files that belong to a collection of snippets below +--- some root, and registers new files. +local Collection = {} +local Collection_mt = { + __index = Collection, +} + +function Collection.new( + root, + lazy, + include_ft, + exclude_ft, + add_opts, + lazy_watcher, + fs_event_providers +) + local ft_filter = loader_util.ft_filter(include_ft, exclude_ft) + local o = setmetatable({ + root = root, + file_filter = function(path, ft) + if not path:sub(1, #root) == root then + log.warn( + "Tried to filter file `%s`, which is not inside the root `%s`.", + path, + root + ) + return false + end + return lua_package_file_filter(path) and ft_filter(ft) + end, + add_opts = add_opts, + lazy = lazy, + -- store ft -> set of files that should be lazy-loaded. + lazy_files = autotable(2, { warn = false }), + -- store, for all files in this collection, their filetype. + -- No need to always recompute it, and we can use this to store which + -- files belong to the collection. + loaded_path_ft = {}, + file_dependencies = digraph.new_labeled(), + -- store fs_watchers for files the snippets-files depend on. + dependency_watchers = {}, + fs_event_providers = fs_event_providers, + }, Collection_mt) + + -- only register files up to a depth of 2. + local ok, err_or_watcher = pcall(tree_watcher, root, 2, { + -- don't handle removals for now. + new_file = function(path) + local path_ft = loader_util.collection_file_ft(o.root, path) + -- detected new file, make sure it is allowed by our filters. + if o.file_filter(path, path_ft) then + o:add_file(path, path_ft) + end + end, + change_file = function(path) + o:reload(path) + end, + }, { lazy = lazy_watcher, fs_event_providers = fs_event_providers }) + + if not ok then + error(("Could not create watcher: %s"):format(err_or_watcher)) + end + + o.watcher = err_or_watcher + + log.info("Initialized snippet-collection at `%s`", root) + + return o +end + +-- Add file with some filetype to collection. +function Collection:add_file(path, ft) + Data.lua_ft_paths[ft][path] = true + + if self.lazy then + if not session.loaded_fts[ft] then + log.info( + "Registering lazy-load-snippets for ft `%s` from file `%s`", + ft, + path + ) + + -- only register to load later. + self.lazy_files[ft][path] = true + return + else + log.info( + "Filetype `%s` is already active, loading immediately.", + ft + ) + end + end + + self:load_file(path, ft) +end +function Collection:load_file(path, ft) + log.info("Adding snippets for filetype `%s` from file `%s`", ft, path) + self.loaded_path_ft[path] = ft + + local snippets, autosnippets, dependent_files = _luasnip_load_file(path) + + -- ignored if it already exists. + self.file_dependencies:set_vertex(path) + -- make sure we don't retain any old dependencies. + self.file_dependencies:clear_edges(path) + + for _, file_dependency in ipairs(dependent_files) do + -- ignored if it already exists. + self.file_dependencies:set_vertex(file_dependency) + -- path depends on dependent_file => if dependent_file is changed, path + -- should be updated. + self.file_dependencies:set_edge(file_dependency, path, path) + + if not self.dependency_watchers[file_dependency] then + self.dependency_watchers[file_dependency] = path_watcher( + file_dependency, + { + change = function(_) + local depending_files = + self.file_dependencies:connected_component( + file_dependency, + "Forward" + ) + for _, file in ipairs(depending_files) do + -- Prevent loading one of the utility-files as a snippet-file. + -- This will not reject any snippet-file in + -- depending_files. This is because since they are in + -- depending_files, we have their dependency-information, + -- which can only be obtained by loading them, and so there + -- can't be any unloaded files in there. + if self.loaded_path_ft[file] then + self:load_file(file, self.loaded_path_ft[file]) + end + end + end, + }, + { lazy = false, fs_event_providers = self.fs_event_providers } + ) + end + end + + loader_util.add_file_snippets( + ft, + path, + snippets, + autosnippets, + self.add_opts + ) + + refresh_notify(ft) +end +function Collection:do_lazy_load(ft) + for file, _ in pairs(self.lazy_files[ft]) do + if not self.loaded_path_ft[file] then + self:load_file(file, ft) + end + end +end + +-- will only do something, if the file at `path` was loaded previously. +function Collection:reload(path) + local path_ft = self.loaded_path_ft[path] + if not path_ft then + -- file not yet loaded. + return + end + + -- will override previously-loaded snippets from this path. + self:load_file(path, path_ft) + + -- clean snippets if enough were removed. + clean_invalidated() +end + +function Collection:stop() + self.watcher:stop() + for _, watcher in pairs(self.dependency_watchers) do + watcher:stop() + end +end + +function M._load_lazy_loaded_ft(ft) + log.info("Loading lazy-load-snippets for filetype `%s`", ft) + + for _, collection in ipairs(Data.lua_collections) do + collection:do_lazy_load(ft) + end +end + +local function _load(lazy, opts) + local o = loader_util.normalize_opts(opts) + + local collection_roots = + loader_util.resolve_root_paths(o.paths, "luasnippets") + local lazy_roots = loader_util.resolve_lazy_root_paths(o.lazy_paths) + + log.info( + "Found roots `%s` for paths `%s`.", + vim.inspect(collection_roots), + vim.inspect(o.paths) + ) + if o.paths and #o.paths ~= #collection_roots then + log.warn( + "Could not resolve all collection-roots for paths `%s`: only found `%s`", + vim.inspect(o.paths), + vim.inspect(collection_roots) + ) + end + + log.info( + "Determined roots `%s` for lazy_paths `%s`.", + vim.inspect(lazy_roots), + vim.inspect(o.lazy_paths) + ) + if o.lazy_paths and #o.lazy_paths ~= #lazy_roots then + log.warn( + "Could not resolve all collection-roots for lazy_paths `%s`: only found `%s`", + vim.inspect(o.lazy_paths), + vim.inspect(lazy_roots) + ) + end + + for paths_lazy, roots in pairs({ + [true] = lazy_roots, + [false] = collection_roots, + }) do + for _, collection_root in ipairs(roots) do + local ok, coll_or_err = pcall( + Collection.new, + collection_root, + lazy, + o.include, + o.exclude, + o.add_opts, + paths_lazy, + o.fs_event_providers + ) + + if not ok then + log.error( + "Could not create collection at %s: %s", + collection_root, + coll_or_err + ) + else + table.insert(Data.lua_collections, coll_or_err) + end + end + end +end + +--- Load lua-snippet-collections immediately. +--- @param opts LuaSnip.Loaders.LoadOpts? +function M.load(opts) + _load(false, opts) +end + +--- Load lua-snippet-collections on demand. +--- @param opts LuaSnip.Loaders.LoadOpts? +function M.lazy_load(opts) + _load(true, opts) + + -- load for current buffer on startup. + for _, ft in + ipairs(loader_util.get_load_fts(vim.api.nvim_get_current_buf())) + do + M._load_lazy_loaded_ft(ft) + end +end + +function M.clean() + for _, collection in ipairs(Data.lua_collections) do + collection:stop() + end + -- bit ugly, keep in sync with defaults in data.lua. + -- Don't anticipate those changing, so fine I guess. + Data.lua_collections = {} + Data.lua_ft_paths = autotable(2) +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/from_snipmate.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/from_snipmate.lua new file mode 100644 index 00000000..f70288de --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/from_snipmate.lua @@ -0,0 +1,512 @@ +local loader_util = require("luasnip.loaders.util") +local util = require("luasnip.util.util") +local tbl_util = require("luasnip.util.table") +local Path = require("luasnip.util.path") +local autotable = require("luasnip.util.auto_table").autotable +local digraph = require("luasnip.util.directed_graph") +local tree_watcher = require("luasnip.loaders.fs_watchers").tree +local Data = require("luasnip.loaders.data") +local session = require("luasnip.session") +local snippetcache = require("luasnip.loaders.snippet_cache") +local refresh_notify = + require("luasnip.session.enqueueable_operations").refresh_notify +local clean_invalidated = + require("luasnip.session.enqueueable_operations").clean_invalidated + +local log = require("luasnip.util.log").new("snipmate-loader") + +--- Load data from any snippet-file. +--- @param filename string +--- @return LuaSnip.Loaders.SnippetFileData +local function load_snipmate(filename) + local buffer_ok, buffer = pcall(Path.read_file, filename) + if not buffer_ok then + log.error(("Could not read file %s: %s"):format(filename, buffer)) + -- return dummy-data. + return { + snippets = {}, + autosnippets = {}, + misc = {}, + } + end + + local sp = require("luasnip.nodes.snippetProxy") + local snipmate_parse_fn = require("luasnip.util.parser").parse_snipmate + local source = require("luasnip.session.snippet_collection.source") + + -- could also be separate variables, but easier to access this way. + local snippets = { + snippet = {}, + autosnippet = {}, + } + local extends = {} + + ---@type string[] + local lines = loader_util.split_lines(buffer) + local i = 1 + + local function _parse(snippet_type, snipmate_opts) + local line = lines[i] + -- "snippet" or "autosnippet" + local prefix, description = + line:match("^" .. snippet_type .. [[%s+(%S+)%s*(.*)]]) + local body = {} + + local snip_begin_line = i + i = i + 1 + ---@type number + local indent + + while i <= #lines do + line = lines[i] + if line:find("^%s+") then + if not indent then + indent = #line:match("^%s+") + end + line = line:sub(indent + 1) + line = line:gsub("${VISUAL}", "${TM_SELECTED_TEXT}") + elseif line ~= "" then + break + end + table.insert(body, line) + i = i + 1 + end + + body = table.concat(body, "\n") + local snip = sp( + { + trig = prefix, + desc = description, + wordTrig = true, + priority = snipmate_opts.priority, + }, + body, + { + parse_fn = snipmate_parse_fn, + } + ) + if session.config.loaders_store_source then + snip._source = source.from_location( + filename, + { line = snip_begin_line, line_end = i - 1 } + ) + end + table.insert(snippets[snippet_type], snip) + end + + -- options for some snippet can be specified in the lines before the + -- {auto}snippet-keyword ("priority 2000\nsnippet...."). + -- They are stored in snipmate_opts, which is cleaned whenever a snippet is + -- actually created. + local snipmate_opts = {} + while i <= #lines do + local line = lines[i] + if vim.startswith(line, "snippet") then + _parse("snippet", snipmate_opts) + snipmate_opts = {} + elseif vim.startswith(line, "autosnippet") then + _parse("autosnippet", snipmate_opts) + snipmate_opts = {} + elseif vim.startswith(line, "extends") then + vim.list_extend(extends, vim.split(vim.trim(line:sub(8)), "[,%s]+")) + i = i + 1 + elseif vim.startswith(line, "#") or line:find("^%s*$") then + -- comment and blank line + i = i + 1 + elseif vim.startswith(line, "priority") then + snipmate_opts.priority = tonumber(line:match("priority%s+(%d+)")) + i = i + 1 + else + log.error("Invalid line in %s: %s", filename, i) + error(("Invalid line in %s: %s"):format(filename, i)) + end + end + + return { + snippets = snippets.snippet, + autosnippets = snippets.autosnippet, + misc = extends, + } +end + +-- cache snippets without filetype-association for reuse. +Data.snipmate_cache = snippetcache.new(load_snipmate) + +--- Collection watches all files that belong to a collection of snippets below +--- some root, and registers new files. +local Collection = {} +local Collection_mt = { + __index = Collection, +} + +local function snipmate_package_file_filter(fname) + return fname:match("%.snippets$") +end + +function Collection.new( + root, + lazy, + include_ft, + exclude_ft, + add_opts, + lazy_watcher, + fs_event_providers +) + local ft_filter = loader_util.ft_filter(include_ft, exclude_ft) + local o = setmetatable({ + root = root, + + --- @alias LuaSnip.Loaders.Snipmate.FileCategory + --- | '"collection"' File only belongs to the collection + --- | '"load"' File should be loaded + + --- Determine whether a file should be loaded, belongs to the + --- collection, or doesn't. + --- This distinction is important because we need to know about all + --- files to correctly resolve `extend `, but only want to load + --- the filetypes allowed by in/exclude. + --- @param path string + ---@return LuaSnip.Loaders.Snipmate.FileCategory? + categorize_file = function(path) + if not path:sub(1, #root) == root then + log.warn( + "Tried to filter file `%s`, which is not inside the root `%s`.", + path, + root + ) + return nil + end + if snipmate_package_file_filter(path) then + local path_ft = loader_util.collection_file_ft(root, path) + if ft_filter(path_ft) then + return "load" + end + return "collection" + end + return nil + end, + -- sometimes we don't want the full categorize-file-data, only which + -- filetypes should be loaded. + ft_filter = ft_filter, + + add_opts = add_opts, + lazy = lazy, + -- store ft -> set of files that should be lazy-loaded. + lazy_files = autotable(2, { warn = false }), + -- store for each path the set of filetypes it has been loaded with. + loaded_path_fts = autotable(2, { warn = false }), + -- model filetype-extensions (`extends ` in `ft.snippets`). + -- Better than a flat table with t[ft] = {someft=true, somotherft=true} + -- since transitive dependencies are easier to understand/query. + -- There is an edge with source src to destination dst, if snippets for + -- filetype src also contribute to filetype dst. + -- Since we respect transitive `extends`, we can get all filetypes a + -- snippet-file for some filetype A contributes to by querying the + -- connected component of A (all filetype-vertices reachable from A). + ft_extensions = digraph.new_labeled(), + -- store all files in the collection, by their filetype. + -- This information is necessary to handle `extends` even for files + -- that are not actually loaded (due to in/exclude). + collection_files_by_ft = autotable(2, { warn = false }), + -- set if creation successful. + watcher = nil, + }, Collection_mt) + + -- only register files up to a depth of 2. + local ok, err_or_watcher = pcall(tree_watcher, root, 2, { + -- don't handle removals for now. + new_file = function(path) + ---@as LuaSnip.Loaders.Snipmate.FileCategory + local file_category = o.categorize_file(path) + + if file_category then + -- know it's at least in the collection -> can register it. + local file_ft = loader_util.collection_file_ft(o.root, path) + o:register_file(path, file_ft) + if file_category == "load" then + -- actually load if allowed by in/exclude. + o:add_file(path, file_ft) + end + end + end, + change_file = function(path) + vim.schedule_wrap(function() + o:reload(path) + end)() + end, + }, { lazy = lazy_watcher, fs_event_providers = fs_event_providers }) + + if not ok then + error(("Could not create watcher: %s"):format(err_or_watcher)) + end + + o.watcher = err_or_watcher + + log.info("Initialized snippet-collection at `%s`", root) + + return o +end + +--- Makes the file known to the collection, but does not load its snippets. +--- This is important because `extends` may require loading a file excluded by +--- `file_filter`, ie `include` and `exclude`. +--- @param path string +--- @param ft string +function Collection:register_file(path, ft) + self.collection_files_by_ft[ft][path] = true +end + +--- Register a file-filetype-association with the collection. +--- @param path string Path to a file that belongs to this collection. +--- @param add_ft string The original filetype this file should be added as. +--- Since we have to support filetype-extensions, this may +--- add the snippets of the file to several other +--- filetypes. +function Collection:add_file(path, add_ft) + -- register known file. + Data.snipmate_ft_paths[add_ft][path] = true + + if self.lazy then + if not session.loaded_fts[add_ft] then + log.info( + "Registering lazy-load-snippets for ft `%s` from file `%s`", + add_ft, + path + ) + + -- only register to load later. + self.lazy_files[add_ft][path] = true + return + else + log.info( + "Filetype `%s` is already active, loading immediately.", + add_ft + ) + end + end + + -- extended filetypes will be loaded in load_file. + self:load_file(path, add_ft, "SkipIfLoaded") +end + +--- @alias LuaSnip.Loaders.Snipmate.SkipLoad +--- | '"ForceLoad"' Always load, even if it was already loaded. +--- | '"SkipIfLoaded"' Skip the load if the file has been loaded already. + +-- loads the fts that extend load_ft as well. +-- skip_load_mode allows this code to both prevent unnecessary loads (which +-- could be caused if some file is added to the same filetype more than once), +-- while still handling reload (where the files has to be loaded again for +-- every filetype, even if it already is loaded (since it may have different +-- snippets)) +function Collection:load_file(path, ft, skip_load_mode) + if skip_load_mode == "SkipIfLoaded" and self.loaded_path_fts[path][ft] then + return + end + -- ignore load_file if ft is excluded/not included. + if not self.ft_filter(ft) then + return + end + + log.info("Adding snippets for filetype `%s` from file `%s`", ft, path) + + -- Set here to skip loads triggered for the same path-file-combination in + -- subsequent code, which would trigger and endless loop. + self.loaded_path_fts[path][ft] = true + + -- this may already be set, but setting again here ensures that a file is + -- certainly associated with each filetype it's loaded for. (for example, + -- file-ft-combinations loaded as a dependency from another file may not be + -- set already). + Data.snipmate_ft_paths[ft][path] = true + + -- snippets may already be loaded -> get them from cache. + local data = Data.snipmate_cache:fetch(path) + local snippets = data.snippets + local autosnippets = data.autosnippets + -- data.misc is user-input, clean it here. + local extended_fts = util.deduplicate(data.misc) + + -- ignored if it already exists. + self.ft_extensions:set_vertex(ft) + -- make sure we don't retain any old dependencies. + self.ft_extensions:clear_edges(path) + + for _, extended_ft in pairs(extended_fts) do + -- ignored if it already exists. + self.ft_extensions:set_vertex(extended_ft) + -- snippets for extended_ft should also be loaded if ft is loaded + -- label edge with path, so all edges from this file can be updated on + -- reload. + self.ft_extensions:set_edge(extended_ft, ft, path) + end + + loader_util.add_file_snippets( + ft, + path, + snippets, + autosnippets, + self.add_opts + ) + + -- get all filetypes this one extends (directly or transitively), and load + -- their files for ft. + local load_fts = self.ft_extensions:connected_component(ft, "Backward") + for _, extended_ft in ipairs(load_fts) do + for file, _ in pairs(self.collection_files_by_ft[extended_ft]) do + -- check which filetypes a file with filetype extended_ft has to be + -- loaded for currently!! (ie. which filetypes directly or + -- transitively extend extended_ft. + -- The reason we can't just check that statically once, or just load + -- a file for the filetypes its filetype is initially extended by is + -- that the extends-graph may change on each `load_file`, and we + -- want to respect these changes. + for _, file_ft in + ipairs( + self.ft_extensions:connected_component( + extended_ft, + "Forward" + ) + ) + do + -- skips load if the file is already loaded for the given filetype. + -- One bad side-effect of this current implementation is that + -- the edges in the graph will be reset/set multiple times, + -- until they are retained in the last load_file-call to the + -- last filetype. + self:load_file(file, file_ft, "SkipIfLoaded") + end + end + end + + refresh_notify(ft) +end + +function Collection:do_lazy_load(lazy_ft) + for file, _ in pairs(self.lazy_files[lazy_ft]) do + for _, ft in + ipairs(self.ft_extensions:connected_component(lazy_ft, "Forward")) + do + -- skips load if the file is already loaded for the given filetype. + self:load_file(file, ft, "SkipIfLoaded") + end + end +end +-- will only do something, if the file at `path` is actually in the collection. +function Collection:reload(path) + local loaded_fts = tbl_util.set_to_list(self.loaded_path_fts[path]) + for _, loaded_ft in ipairs(loaded_fts) do + -- will override previously-loaded snippets from this path. + self:load_file(path, loaded_ft, "ForceLoad") + end + + -- clean snippets if enough were removed. + clean_invalidated() +end + +function Collection:stop() + self.watcher:stop() +end + +local M = {} + +function M._load_lazy_loaded_ft(ft) + log.info("Loading lazy-load-snippets for filetype `%s`", ft) + + for _, collection in ipairs(Data.snipmate_collections) do + collection:do_lazy_load(ft) + end +end + +--- Generalized loading of collections. +--- @param lazy boolean Whether the collection should be loaded lazily. +--- @param opts LuaSnip.Loaders.LoadOpts? +local function _load(lazy, opts) + local o = loader_util.normalize_opts(opts) + + local collection_roots = loader_util.resolve_root_paths(o.paths, "snippets") + local lazy_roots = loader_util.resolve_lazy_root_paths(o.lazy_paths) + + log.info( + "Found roots `%s` for paths `%s`.", + vim.inspect(collection_roots), + vim.inspect(o.paths) + ) + if o.paths and #o.paths ~= #collection_roots then + log.warn( + "Could not resolve all collection-roots for paths `%s`: only found `%s`", + vim.inspect(o.paths), + vim.inspect(collection_roots) + ) + end + + log.info( + "Determined roots `%s` for lazy_paths `%s`.", + vim.inspect(lazy_roots), + vim.inspect(o.lazy_paths) + ) + if o.lazy_paths and #o.lazy_paths ~= #lazy_roots then + log.warn( + "Could not resolve all collection-roots for lazy_paths `%s`: only found `%s`", + vim.inspect(o.lazy_paths), + vim.inspect(lazy_roots) + ) + end + + for paths_lazy, roots in pairs({ + [true] = lazy_roots, + [false] = collection_roots, + }) do + for _, collection_root in ipairs(roots) do + local ok, coll_or_err = pcall( + Collection.new, + collection_root, + lazy, + o.include, + o.exclude, + o.add_opts, + paths_lazy, + o.fs_event_providers + ) + + if not ok then + log.error( + "Could not create collection at %s: %s", + collection_root, + coll_or_err + ) + else + table.insert(Data.snipmate_collections, coll_or_err) + end + end + end +end + +--- Load snipmate-snippet-collections immediately. +--- @param opts LuaSnip.Loaders.LoadOpts? +function M.load(opts) + _load(false, opts) +end + +--- Load snipmate-snippet-collections on demand. +--- @param opts LuaSnip.Loaders.LoadOpts? +function M.lazy_load(opts) + _load(true, opts) + -- load for current buffer on startup. + for _, ft in + ipairs(loader_util.get_load_fts(vim.api.nvim_get_current_buf())) + do + M._load_lazy_loaded_ft(ft) + end +end + +function M.clean() + for _, collection in ipairs(Data.snipmate_collections) do + collection:stop() + end + Data.snipmate_ft_paths = autotable(2) + -- don't reset cache, snippets are correctly updated on file-change anyway, + -- and there is no persistent data passed on. +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/from_vscode.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/from_vscode.lua new file mode 100644 index 00000000..f8c3f7b6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/from_vscode.lua @@ -0,0 +1,645 @@ +local util = require("luasnip.util.util") +local loader_util = require("luasnip.loaders.util") +local Path = require("luasnip.util.path") +local log = require("luasnip.util.log").new("vscode-loader") +local autotable = require("luasnip.util.auto_table").autotable +local path_watcher = require("luasnip.loaders.fs_watchers").path +local Data = require("luasnip.loaders.data") +local session = require("luasnip.session") +local refresh_notify = + require("luasnip.session.enqueueable_operations").refresh_notify +local clean_invalidated = + require("luasnip.session.enqueueable_operations").clean_invalidated + +-- create snippetProxy which does not trim lines and dedent text. +-- It's fair to use passed test as-is, if it's from json. +local parse = require("luasnip.util.parser").parse_snippet +local sp = require("luasnip.util.extend_decorator").apply( + require("luasnip.nodes.snippetProxy"), + {}, + { + parse_fn = function(ctx, body) + return parse(ctx, body, { trim_empty = false, dedent = false }) + end, + } +) + +local json_decoders = { + json = util.json_decode, + jsonc = require("luasnip.util.jsonc").decode, + ["code-snippets"] = require("luasnip.util.jsonc").decode, +} + +local function read_json(fname) + local data_ok, data = pcall(Path.read_file, fname) + if not data_ok then + log.error("Could not read file %s", fname) + return nil + end + + local fname_extension = Path.extension(fname) + if json_decoders[fname_extension] == nil then + log.error( + "`%s` was expected to have file-extension either `json`, `jsonc` or `code-snippets`, but doesn't.", + fname + ) + return nil + end + local fname_decoder = json_decoders[fname_extension] + + local status, result = pcall(fname_decoder, data) + if status then + return result + else + log.error("Could not parse file %s: %s", fname, result) + return nil + end +end + +--- Load snippets from vscode-snippet-file. +--- @param file string Path to file +---@return LuaSnip.Loaders.SnippetFileData +local function get_file_snippets(file) + local source = require("luasnip.session.snippet_collection.source") + local multisnippet = require("luasnip.nodes.multiSnippet") + + -- since most snippets we load don't have a scope-field, we just insert + -- them here by default. + local snippets = {} + + local snippet_set_data = read_json(file) + if snippet_set_data == nil then + log.error("Reading json from file `%s` failed, skipping it.", file) + + return { + snippets = {}, + autosnippets = {}, + misc = {}, + } + end + + for name, parts in pairs(snippet_set_data) do + local body = type(parts.body) == "string" and parts.body + or table.concat(parts.body, "\n") + + local ls_conf = parts.luasnip or {} + + -- we may generate multiple interfaces to the same snippet + -- (different filetype, different triggers) + + -- context common to all snippets generated here. + local common_context = { + name = name, + desc = parts.description or name, + wordTrig = ls_conf.wordTrig, + priority = ls_conf.priority, + snippetType = ls_conf.autotrigger and "autosnippet" or "snippet", + } + + -- Sometimes it's a list of prefixes instead of a single one + local prefixes = type(parts.prefix) == "table" and parts.prefix + or { parts.prefix } + + -- vscode documents `,`, but `.` also works. + -- an entry `false` in this list will cause a `ft=nil` for the snippet. + local filetypes = parts.scope and vim.split(parts.scope, "[.,]") + or { false } + + local contexts = {} + for _, prefix in ipairs(prefixes) do + for _, filetype in ipairs(filetypes) do + table.insert( + contexts, + { filetype = filetype or nil, trig = prefix } + ) + end + end + + local snip + if #contexts > 1 then + -- only construct multisnippet if it is actually necessary. + contexts.common = common_context + snip = multisnippet._raw_ms(contexts, sp(nil, body), {}) + elseif #contexts == 1 then + -- have to add options from common context to the trig/filetype-context. + snip = sp(vim.tbl_extend("keep", contexts[1], common_context), body) + end + + if snip then + if session.config.loaders_store_source then + -- only know file, not line or line_end. + snip._source = source.from_location(file) + end + + table.insert(snippets, snip) + end + end + + return { + snippets = snippets, + autosnippets = {}, + misc = {}, + } +end + +-- has to be set in separate module to allow different module-path-separators +-- in `require`. +Data.vscode_cache = + require("luasnip.loaders.snippet_cache").new(get_file_snippets) + +--- Parse package.json(c), determine all files that contribute snippets, and +--- which filetype is associated with them. +--- @param manifest string +--- @return table> +local function get_snippet_files(manifest) + -- if root doesn't contain a package.json, or it contributes no snippets, + -- return no snippets. + if not Path.exists(manifest) then + log.warn("Manifest %s does not exist", manifest) + return {} + end + + local package_data = read_json(manifest) + if not package_data then + -- since it is a `.json/jsonc`, the json not being correct should be an error. + log.error("Could not read json from `%s`", manifest) + return {} + end + + if + not package_data.contributes or not package_data.contributes.snippets + then + log.warn("Manifest %s does not contribute any snippets.", manifest) + return {} + end + + -- stores ft -> files -> true|nil, allow iterating files and their + -- filetypes while preventing duplicates. + local ft_file_set = autotable(2, { warn = false }) + + -- parent-directory of package.json(c), all files in the package.json(c) + -- are relative to it. + local package_parent = Path.parent(manifest) + + for _, snippet_entry in pairs(package_data.contributes.snippets) do + local absolute_path = Path.join(package_parent, snippet_entry.path) + + local normalized_snippet_file = Path.normalize(absolute_path) + + if not normalized_snippet_file then + -- path does not exist (yet), try and guess the correct path anyway. + normalized_snippet_file = Path.normalize_nonexisting(absolute_path) + log.warn( + "Could not find file %s advertised in %s, guessing %s as the absolute and normalized path.", + absolute_path, + manifest, + normalized_snippet_file + ) + end + + local langs = snippet_entry.language + if type(langs) ~= "table" then + langs = { langs } + end + for _, ft in ipairs(langs) do + ft_file_set[ft][normalized_snippet_file] = true + end + end + + return ft_file_set +end + +-- Responsible for watching a single json-snippet-file. +local SnippetfileWatcher = {} +local SnippetfileWatcher_mt = { __index = SnippetfileWatcher } + +function SnippetfileWatcher.new( + path, + initial_ft, + fs_event_providers, + lazy, + load_cb +) + local o = setmetatable({ + path = path, + load_cb = load_cb, + -- track which filetypes this file has been loaded for, so we can + -- reload for all of them. + loaded_fts = { [initial_ft] = true }, + }, SnippetfileWatcher_mt) + + local load_all_fts = function() + for ft, _ in pairs(o.loaded_fts) do + load_cb(path, ft) + refresh_notify(ft) + end + end + local ok, err_or_watcher = pcall(path_watcher, path, { + add = load_all_fts, + change = function() + load_all_fts() + + -- clean snippets if enough were removed. + clean_invalidated() + end, + }, { lazy = lazy, fs_event_providers = fs_event_providers }) + + if not ok then + -- has to be handled by caller, we can't really proceed if the creation + -- failed. + error( + ("Could not create path_watcher for path %s: %s"):format( + path, + err_or_watcher + ) + ) + end + + o.watcher = err_or_watcher + + return o +end + +-- called by collection. +function SnippetfileWatcher:add_ft(ft) + if self.loaded_fts[ft] then + -- already loaded. + return + end + self.loaded_fts[ft] = true + self.load_cb(self.path, ft) +end + +function SnippetfileWatcher:stop() + self.watcher:stop() +end + +--- Collection watches all files that belong to a collection of snippets below +--- some root, and registers new files. +local Collection = {} +local Collection_mt = { + __index = Collection, +} + +function Collection.new( + manifest_path, + lazy, + include_ft, + exclude_ft, + add_opts, + lazy_watcher, + fs_event_providers +) + local ft_filter = loader_util.ft_filter(include_ft, exclude_ft) + local o = setmetatable({ + lazy = lazy, + -- store ft -> set of files that should be lazy-loaded. + lazy_files = autotable(2, { warn = false }), + fs_event_providers = fs_event_providers, + + -- store path-watchers (so we don't register more than one for one + -- path), and so we can disable them. + path_watchers = {}, + -- for really loading a file. + -- this is not done in Collection:load itself, since it may have to be + -- performed as a callback on file-creation. + load_callback = function(path, ft) + local data = Data.vscode_cache:fetch(path) + -- autosnippets are included in snippets for this loader. + local snippets = data.snippets + loader_util.add_file_snippets(ft, path, snippets, {}, add_opts) + end, + -- initialized in a bit, we have to store+reset a watcher for the manifest-file. + manifest_watcher = nil, + }, Collection_mt) + + -- callback for updating the file-filetype-associations from the manifest. + local update_manifest = function() + local manifest_ft_paths = get_snippet_files(manifest_path) + for ft, path_set in pairs(manifest_ft_paths) do + if ft_filter(ft) then + for path, _ in pairs(path_set) do + o:add_file(path, ft) + end + end + end + end + + local ok, watcher_or_err = pcall(path_watcher, manifest_path, { + -- don't handle removals for now. + add = update_manifest, + change = update_manifest, + }, { lazy = lazy_watcher, fs_event_providers = fs_event_providers }) + + if not ok then + error(("Could not create watcher: %s"):format(watcher_or_err)) + end + o.manifest_watcher = watcher_or_err + + log.info("Initialized snippet-collection with manifest %s", manifest_path) + + return o +end + +-- Add file with some filetype to collection, load according to lazy_load. +function Collection:add_file(path, ft) + Data.vscode_ft_paths[ft][path] = true + + if self.lazy then + if not session.loaded_fts[ft] then + log.info( + "Registering lazy-load-snippets for ft `%s` from file `%s`", + ft, + path + ) + + -- only register to load later. + self.lazy_files[ft][path] = true + return + else + log.info( + "Filetype `%s` is already active, loading immediately.", + ft + ) + end + end + + self:load_file(path, ft) +end + +function Collection:load_file(path, ft) + log.info("Registering file %s with filetype %s for loading.", path, ft) + if not self.path_watchers[path] then + -- always register these lazily, that way an upate to the package.json + -- without the snippet-file existing will work! + -- Also make sure we use the same fs_event_providers. + local ok, watcher_or_err = pcall( + SnippetfileWatcher.new, + path, + ft, + self.fs_event_providers, + true, + self.load_callback + ) + if not ok then + log.error( + "Could not create SnippetFileWatcher for path %s: %s", + path, + watcher_or_err + ) + return + end + self.path_watchers[path] = watcher_or_err + else + -- make new filetype known to existing watcher. + self.path_watchers[path]:add_ft(ft) + end +end + +-- stop all watchers associated with this collection, to make sure no snippets +-- are added from this collection again. +function Collection:stop() + self.manifest_watcher:stop() + for _, watcher in pairs(self.path_watchers) do + watcher:stop() + end +end + +function Collection:do_lazy_load(ft) + for file, _ in pairs(self.lazy_files[ft]) do + self:load_file(file, ft) + end +end + +local M = {} + +local function get_rtp_paths() + return vim.list_extend( + -- would be very surprised if this yields duplicates :D + vim.api.nvim_get_runtime_file("package.json", true), + vim.api.nvim_get_runtime_file("package.jsonc", true) + ) +end + +--- Generate list of manifest-paths from list of directory-paths. +--- If nil, search rtp. +--- If a given directory, or the mani +--- +--- @param paths string|table? List of existing directories. If nil, search runtimepath. +---@return string[] manifest_paths +local function get_manifests(paths) + local manifest_paths = {} + -- list of paths to crawl for loading (could be a table or a comma-separated-list) + if paths then + -- Get path to package.json/package.jsonc, or continue if it does not exist. + for _, dir in ipairs(paths) do + local tentative_manifest_path = + Path.expand_keep_symlink(Path.join(dir, "package.json")) + if Path.exists(tentative_manifest_path) then + table.insert(manifest_paths, tentative_manifest_path) + else + tentative_manifest_path = + Path.expand_keep_symlink(Path.join(dir, "package.jsonc")) + if Path.exists(tentative_manifest_path) then + table.insert(manifest_paths, tentative_manifest_path) + else + log.warn( + "Could not find package.json(c) in path %s (expanded to %s).", + dir, + Path.expand(dir) + ) + end + end + end + else + manifest_paths = get_rtp_paths() + end + + return manifest_paths +end + +--- Generate list of paths to manifests that may not yet exist, from list of +--- directories (which also may not yet exist). +--- One peculiarity: This will generate two paths for each directory, since we +--- don't know if the package.json or the package.jsonc will be created. +--- This may cause a bit of overhead (not much due to snippet-cache) if both +--- are created and contribute the same snippets, but that's unlikely and/or +--- user error :P +--- @param paths string[] +---@return string[] +local function get_lazy_manifests(paths) + local lazy_manifest_paths = {} + if paths then + -- list of directories, convert to list of existing manifest-files. + if type(paths) == "string" then + paths = vim.split(paths, ",") + end + for _, dir in ipairs(paths) do + local absolute_dir = Path.expand_maybe_nonexisting(dir) + + table.insert( + lazy_manifest_paths, + Path.join(absolute_dir, "package.json") + ) + table.insert( + lazy_manifest_paths, + Path.join(absolute_dir, "package.jsonc") + ) + end + end + + return lazy_manifest_paths +end + +local function _load(lazy, opts) + local o = loader_util.normalize_opts(opts) + + local manifests = get_manifests(o.paths) + local lazy_manifests = get_lazy_manifests(o.lazy_paths) + + log.info( + "Found manifests `%s` for paths `%s`.", + vim.inspect(manifests), + vim.inspect(o.paths) + ) + if o.paths and #o.paths ~= #manifests then + log.warn( + "Could not resolve all manifests for paths `%s`: only found `%s`", + vim.inspect(o.paths), + vim.inspect(manifests) + ) + end + + log.info( + "Determined roots `%s` for lazy_paths `%s`.", + vim.inspect(lazy_manifests), + vim.inspect(o.lazy_paths) + ) + -- two lazy manifests from each lazy directory. + if o.lazy_paths and #o.lazy_paths ~= 2 * #lazy_manifests then + log.warn( + "Could not resolve all manifests for lazy_paths `%s`: only found `%s`", + vim.inspect(o.lazy_paths), + vim.inspect(lazy_manifests) + ) + end + + for is_lazy, manifest_paths in pairs({ + [true] = lazy_manifests, + [false] = manifests, + }) do + for _, manifest_path in ipairs(manifest_paths) do + local ok, coll_or_err = pcall( + Collection.new, + manifest_path, + lazy, + o.include, + o.exclude, + o.add_opts, + is_lazy, + o.fs_event_providers + ) + + if not ok then + log.error( + "Could not create collection for manifest %s: %s", + manifest_path, + coll_or_err + ) + else + table.insert(Data.vscode_package_collections, coll_or_err) + end + end + end +end + +function M._load_lazy_loaded_ft(ft) + log.info("Loading lazy-load-snippets for filetype `%s`", ft) + + for _, collection in ipairs(Data.vscode_package_collections) do + collection:do_lazy_load(ft) + end + -- no need to lazy_load standalone-snippets. +end + +function M.load(opts) + _load(false, opts) +end + +function M.lazy_load(opts) + _load(true, opts) + + -- load for current buffer on startup. + for _, ft in + ipairs(loader_util.get_load_fts(vim.api.nvim_get_current_buf())) + do + M._load_lazy_loaded_ft(ft) + end +end + +function M.load_standalone(opts) + opts = opts or {} + + local lazy = vim.F.if_nil(opts.lazy, false) + local add_opts = loader_util.make_add_opts(opts) + local fs_event_providers = + vim.F.if_nil(opts.fs_event_providers, { autocmd = true, libuv = false }) + + local path + if not lazy then + path = Path.expand(opts.path) + if not path then + log.error( + "Expanding path %s does not produce an existing path.", + opts.path + ) + return + end + else + path = Path.expand_maybe_nonexisting(opts.path) + end + + Data.vscode_ft_paths["all"][path] = true + + local ok, watcher_or_err = pcall( + SnippetfileWatcher.new, + path, + "all", + fs_event_providers, + lazy, + function() + local data = Data.vscode_cache:fetch(path) + -- autosnippets are included in snippets for this loader. + local snippets = data.snippets + loader_util.add_file_snippets("all", path, snippets, {}, add_opts) + end + ) + + if not ok then + log.error( + "Could not create SnippetFileWatcher for path %s: %s", + path, + watcher_or_err + ) + return + end + + table.insert(Data.vscode_standalone_watchers, watcher_or_err) +end + +function M.clean() + for _, collection in ipairs(Data.vscode_package_collections) do + collection:stop() + end + Data.vscode_package_collections = {} + for _, standalone_watcher in ipairs(Data.vscode_standalone_watchers) do + standalone_watcher:stop() + end + Data.vscode_standalone_watchers = {} + + Data.vscode_ft_paths = autotable(2) + -- don't reset cache, there's no reason to discard the already-loaded + -- snippets as long as they're unchanged. +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/fs_watchers.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/fs_watchers.lua new file mode 100644 index 00000000..11f90539 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/fs_watchers.lua @@ -0,0 +1,723 @@ +local Path = require("luasnip.util.path") +local uv = vim.uv or vim.loop +local util = require("luasnip.util.util") +local log_tree = require("luasnip.util.log").new("tree-watcher") +local log_path = require("luasnip.util.log").new("path-watcher") +local log = require("luasnip.util.log").new("fs-watchers") + +local M = {} + +-- used by both watchers. +local callback_mt = { + __index = function() + return util.nop + end, +} + +--- @alias LuaSnip.FSWatcher.FSEventProviders +--- | '"autocmd"' Hook into BufWritePost to receive notifications on file-changes. +--- | '"libuv"' Register uv.fs_event to receive notifications on file-changes. + +--- @alias LuaSnip.FSWatcher.Callback fun(full_path: string) + +--- @class LuaSnip.FSWatcher.TreeCallbacks +--- @field new_file LuaSnip.FSWatcher.Callback? +--- @field new_dir LuaSnip.FSWatcher.Callback? +--- @field remove_file LuaSnip.FSWatcher.Callback? +--- @field remove_dir LuaSnip.FSWatcher.Callback? +--- @field remove_root LuaSnip.FSWatcher.Callback? +--- @field change_file LuaSnip.FSWatcher.Callback? +--- @field change_dir LuaSnip.FSWatcher.Callback? +--- The callbacks are called with the full path to the file/directory that is +--- affected. +--- Callbacks that are not set will be replaced by a nop. + +--- @class LuaSnip.FSWatcher.PathCallbacks +--- @field add LuaSnip.FSWatcher.Callback? +--- @field remove LuaSnip.FSWatcher.Callback? +--- @field change LuaSnip.FSWatcher.Callback? +--- The callbacks are called with the full path to the file that path-watcher +--- is registered on. +--- Callbacks that are not set will be replaced by a nop. + +--- @class LuaSnip.FSWatcher.Options +--- @field lazy boolean? +--- If set, the watcher will be initialized even if the root/watched path does +--- not yet exist, and start notifications once it is created. +--- @field fs_event_providers table? +--- Which providers to use for receiving file-changes. + +local function get_opts(opts) + opts = opts or {} + local lazy = vim.F.if_nil(opts.lazy, false) + local fs_event_providers = + vim.F.if_nil(opts.fs_event_providers, { autocmd = true, libuv = false }) + + return lazy, fs_event_providers +end + +-- plain list, don't use map-style table since we'll only need direct access to +-- a watcher when it is stopped, which seldomly happens (at least, compared to +-- how often it is iterated in the autocmd-callback). +M.autocmd_watchers = {} + +vim.api.nvim_create_augroup("_luasnip_fs_watcher", {}) +vim.api.nvim_create_autocmd({ "BufWritePost" }, { + callback = function(args) + log.debug("Received BufWritePost for file %s.", args.file) + local realpath = Path.normalize(args.file) + if not realpath then + -- if nil, the path does not exist for some reason. + log.info( + "Registered BufWritePost with %s, but realpath does not exist. Aborting fs-watcher-notification.", + args.file + ) + return + end + log.debug( + "Received update for file %s, using realpath %s.", + args.file, + realpath + ) + + -- remove stopped watchers. + -- Does not really matter whether we do this before or after the + -- callbacks, since stopped watchers already take care to not do + -- callbacks. + -- Doing this during the callback-invocations, however, would incur + -- some more complexity since ipairs does not support removal of + -- elements during the iteration. + M.autocmd_watchers = vim.tbl_filter(function(watcher) + -- this won't catch unstarted watchers, since they can't be in this + -- list in the first place. + return not watcher.stopped + end, M.autocmd_watchers) + + for _, watcher in ipairs(M.autocmd_watchers) do + watcher:BufWritePost_callback(realpath) + end + end, + group = "_luasnip_fs_watcher", +}) + +-- similar autocmd_watchers, only this list contains watchers that should be +-- notified on a manual update (which right now is every watcher). +M.active_watchers = {} +function M.write_notify(realpath) + M.active_watchers = vim.tbl_filter(function(watcher) + -- this won't catch unstarted watchers, since they can't be in this + -- list in the first place. + return not watcher.stopped + end, M.active_watchers) + + for _, watcher in ipairs(M.active_watchers) do + watcher:BufWritePost_callback(realpath) + end +end + +--- @class LuaSnip.FSWatcher.Tree +--- @field root string +--- @field fs_event userdata +--- @field files table +--- @field dir_watchers table +--- @field removed boolean +--- @field stopped boolean +--- @field callbacks LuaSnip.FSWatcher.TreeCallbacks +--- @field depth number How deep the root should be monitored. +--- @field fs_event_providers table +--- @field root_realpath string? Set as soon as the watcher is started. +local TreeWatcher = {} +local TreeWatcher_mt = { + __index = TreeWatcher, +} + +function TreeWatcher:stop() + for _, child_watcher in ipairs(self.dir_watchers) do + child_watcher:stop() + end + self:stop_self() +end + +function TreeWatcher:stop_self() + -- don't check which fs_event_providers were actually started, for both of + -- these it should not matter if they weren't. + self.stopped = true + self.send_notifications = false + + self.fs_event:stop() + -- will be removed from active_watchers/autocmd_watchers after the next event, but already won't receive it. +end + +function TreeWatcher:fs_event_callback(err, relpath, events) + if not self.send_notifications then + -- abort if we should not send notifications anymore. + return + end + vim.schedule_wrap(function() + log_tree.debug( + "raw: self.root: %s; err: %s; relpath: %s; change: %s; rename: %s", + self.root, + err, + relpath, + events.change, + events.rename + ) + local full_path = Path.join(self.root, relpath) + local path_stat = uv.fs_stat(full_path) + + -- try to figure out what happened in the directory. + if events.rename then + if not uv.fs_stat(self.root) then + self:remove_root() + return + end + if not path_stat then + self:remove_child(relpath, full_path) + return + end + + local f_type + -- if there is a link to a directory, we are notified on changes!! + if path_stat.type == "link" then + f_type = uv.fs_stat(uv.fs_realpath(full_path)) + else + f_type = path_stat.type + end + + if f_type == "file" then + if self.files[relpath] then + -- rename and file exists => a new file was moved into its + -- place => handle as changed file. + self:change_file(relpath, full_path) + else + self:new_file(relpath, full_path) + end + return + elseif f_type == "directory" then + if self.dir_watchers[relpath] then + -- rename and directory exists => directory is overwritten + -- => stop recursively, clear, and start a new watcher. + self.dir_watchers[relpath]:stop() + self.dir_watchers[relpath] = nil + end + self:new_dir(relpath, full_path) + return + end + elseif events.change then + self:change_child(relpath, full_path) + end + end)() +end + +-- May not recognize child correctly if there are symlinks on the path from the +-- child to the directory-root. +-- Should be fine, especially since, I think, fs_event can recognize those +-- correctly, which means that this is an issue only very seldomly. +function TreeWatcher:BufWritePost_callback(realpath) + if not self.send_notifications then + return + end + + if realpath:sub(1, #self.realpath_root) ~= self.realpath_root then + -- not inside this root. + return + end + + -- `#self.realpath_root+2`: remove root and path-separator. + local root_relative_components = + Path.components(realpath:sub(#self.realpath_root + 2)) + local rel = root_relative_components[1] + if #root_relative_components == 1 then + -- wrote file. + -- either new, or changed. + if self.files[rel] then + -- use regular root for notifications! + self:change_file(rel, Path.join(self.root, rel)) + else + self:new_file(rel, Path.join(self.root, rel)) + end + else + if self.dir_watchers[rel] then + if #root_relative_components == 2 then + -- only notify if the changed file is immediately in the + -- directory we're watching! + -- I think this is the behaviour of fs_event, and logically + -- makes sense. + self:change_dir(rel, Path.join(self.root, rel)) + end + else + -- does nothing if the directory already exists. + self:new_dir(rel, Path.join(self.root, rel)) + end + end +end + +function TreeWatcher:start() + if self.depth == 0 then + -- don't watch children for 0-depth. + return + end + + if self.stopped then + -- stopping overrides and prevents starting. + return + end + + self.send_notifications = true + + if self.fs_event_providers.libuv then + -- does not work on nfs-drive, at least if it's edited from another + -- machine. + local success, err = self.fs_event:start( + self.root, + {}, + function(err, relpath, events) + self:fs_event_callback(err, relpath, events) + end + ) + + if not success then + log_tree.error( + "Could not start libuv-monitor for path %s due to error %s", + self.path, + err + ) + else + log_tree.info( + "Monitoring root-directory %s with libuv-monitor.", + self.root + ) + end + end + + -- needed by BufWritePost-callback. + self.realpath_root = Path.normalize(self.root) + + if self.fs_event_providers.autocmd then + if self.realpath_root then + -- receive notifications on BufWritePost. + table.insert(M.autocmd_watchers, self) + log_tree.info( + "Monitoring root-directory %s with autocmd-monitor.", + self.root + ) + else + log_tree.error( + "Could not resolve realpath for root %s, not enabling autocmd-monitor", + self.root + ) + end + end + + if self.realpath_root then + table.insert(M.active_watchers, self) + end + + -- do initial scan after starting the watcher. + -- Scanning first, and then starting the watcher leaves a period of time + -- where a new file may be created (after scanning, before watching), where + -- we wont know about it. + -- If I understand the uv-eventloop correctly, this function, `new`, will + -- be executed completely before a callback is called, so self.files and + -- self.dir_watchers should be populated correctly when a callback is + -- received, even if it was received before all directories/files were + -- added. + -- This difference can be observed, at least on my machine, by watching a + -- directory A, and then creating a nested directory B, and children for it + -- in one command, ie. `mkdir -p A/B/{1,2,3,4,5,6,7,8,9}`. + -- If the callback is registered after the scan, the latter directories + -- (ie. 4-9) did not show up, whereas everything did work correctly if the + -- watcher was activated before the scan. + -- (almost everything, one directory was included in the initial scan and + -- the watch-event, but that seems okay for our purposes) + local files, dirs = Path.scandir(self.root) + for _, file in ipairs(files) do + local relpath = file:sub(#self.root + 2) + self:new_file(relpath, file) + end + for _, dir in ipairs(dirs) do + local relpath = dir:sub(#self.root + 2) + self:new_dir(relpath, dir) + end +end + +-- these functions maintain our logical view of the directory, and call +-- callbacks when we detect a change. +function TreeWatcher:new_file(rel, full) + if self.files[rel] then + -- already added + return + end + + log_tree.debug("new file %s %s", rel, full) + self.files[rel] = true + self.callbacks.new_file(full) +end +function TreeWatcher:new_dir(rel, full) + if self.dir_watchers[rel] then + -- already added + return + end + + log_tree.debug("new dir %s %s", rel, full) + -- first do callback for this directory, then look into (and potentially do + -- callbacks for) children. + self.callbacks.new_dir(full) + -- directory exists => don't need to set lazy. + -- inherit fs_event_providers. + self.dir_watchers[rel] = M.tree( + full, + self.depth - 1, + self.callbacks, + { lazy = false, fs_event_providers = self.fs_event_providers } + ) +end + +function TreeWatcher:change_file(rel, full) + log_tree.debug("changed file %s %s", rel, full) + self.callbacks.change_file(full) +end +function TreeWatcher:change_dir(rel, full) + log_tree.debug("changed dir %s %s", rel, full) + self.callbacks.change_dir(full) +end +function TreeWatcher:change_child(rel, full) + if self.dir_watchers[rel] then + self:change_dir(rel, full) + elseif self.files[rel] then + self:change_file(rel, full) + end +end + +function TreeWatcher:remove_child(rel, full) + if self.dir_watchers[rel] then + log_tree.debug("removing dir %s %s", rel, full) + -- should have been stopped by the watcher for the child, or it was not + -- even started due to depth. + self.dir_watchers[rel]:remove_root() + self.dir_watchers[rel] = nil + + self.callbacks.remove_dir(full) + elseif self.files[rel] then + log_tree.debug("removing file %s %s", rel, full) + self.files[rel] = nil + + self.callbacks.remove_file(full) + end +end + +function TreeWatcher:remove_root() + if self.removed then + -- already removed + return + end + log_tree.debug("removing root %s", self.root) + self.removed = true + -- stop own, children should have handled themselves, if they are watched + -- (and we don't need to do anything for unwatched children). + self:stop_self() + + -- removing entries (set them to nil) is apparently fine when iterating via + -- pairs. + for relpath, _ in pairs(self.files) do + local child_full = Path.join(self.root, relpath) + self:remove_child(relpath, child_full) + end + for relpath, _ in pairs(self.dir_watchers) do + local child_full = Path.join(self.root, relpath) + self:remove_child(relpath, child_full) + end + + self.callbacks.remove_root(self.root) +end + +--- Set up new watcher for a tree of files and directories. +--- @param root string Absolute path to the root. +--- @param depth number The depth up to which to monitor. 1 means that the +--- immediate children will be monitored, 2 includes their +--- children, and so on. +--- @param callbacks LuaSnip.FSWatcher.TreeCallbacks The callbacks to use for this watcher. +--- @param opts LuaSnip.FSWatcher.Options Options, described in their class. +--- @return LuaSnip.FSWatcher.Tree +function M.tree(root, depth, callbacks, opts) + local lazy, fs_event_providers = get_opts(opts) + + -- do nothing on missing callback. + callbacks = setmetatable(callbacks or {}, callback_mt) + + local o = setmetatable({ + root = root, + fs_event = uv.new_fs_event(), + files = {}, + dir_watchers = {}, + -- removed: have not yet triggered the removed-callback. + removed = false, + + -- track whether the watcher was stopped at some point, and if it as, + -- don't allow it to start again. + stopped = false, + -- whether notifications should be sent. + -- Modified by start/stop, wait for start to send any => start out as + -- false. + send_notifications = false, + + callbacks = callbacks, + depth = depth, + fs_event_providers = fs_event_providers, + }, TreeWatcher_mt) + + -- if the path does not yet exist, set watcher up s.t. it will start + -- watching when the directory is created. + if not uv.fs_stat(root) and lazy then + -- root does not yet exist, need to create a watcher that notifies us + -- of its creation. + local parent_path = Path.parent(root) + if not parent_path then + error(("Could not find parent-path for %s"):format(root)) + end + + log_tree.info( + "Path %s does not exist yet, watching %s for creation.", + root, + parent_path + ) + + local parent_watcher + parent_watcher = M.tree(parent_path, 1, { + new_dir = function(full) + if full == root then + o:start() + -- directory was created, stop watching. + parent_watcher:stop_self() + end + end, + -- use same providers. + }, { lazy = true, fs_event_providers = fs_event_providers }) + else + o:start() + end + + return o +end + +--- @class LuaSnip.FSWatcher.Path +--- @field private path string +--- @field private fs_event userdata +--- @field private removed boolean +--- @field private stopped boolean +--- @field private send_notifications boolean +--- @field private callbacks LuaSnip.FSWatcher.TreeCallbacks +--- @field private fs_event_providers table +--- @field private realpath string? Set as soon as the watcher is started. +local PathWatcher = {} + +local PathWatcher_mt = { + __index = PathWatcher, +} + +function PathWatcher:change(full) + log_path.info("detected change at path %s", full) + if self.removed then + -- this is certainly unexpected. + log_path.warn( + "PathWatcher at %s detected change, but path does not exist logically. Not triggering callback.", + full + ) + else + self.callbacks.change(self.path) + end +end + +function PathWatcher:add() + if not self.removed then + -- already added + return + end + log_path.info("adding path %s", self.path) + self.removed = false + + self.callbacks.add(self.path) +end +function PathWatcher:remove() + if self.removed then + -- already removed + return + end + log_path.debug("removing path %s", self.path) + log_path.info("path %s was removed, stopping watcher.", self.path) + + self.removed = true + + self.callbacks.remove(self.path) + + -- Would have to re-register for new file to receive new notifications. + self:stop() +end + +function PathWatcher:fs_event_callback(err, relpath, events) + if not self.send_notifications then + return + end + + vim.schedule_wrap(function() + log_path.debug( + "raw: path: %s; err: %s; relpath: %s; change: %s; rename: %s", + self.path, + err, + relpath, + events.change, + events.rename + ) + + if events.rename then + if not uv.fs_stat(self.path) then + self:remove() + else + self:add() + end + elseif events.change then + self:change() + end + end)() +end + +function PathWatcher:BufWritePost_callback(realpath) + if not self.send_notifications then + return + end + + if realpath == self.realpath then + -- notify using passed path, not realpath. + self:change(self.path) + end +end + +function PathWatcher:start() + if self.stopped then + -- stop() prevents start. + return + end + self.send_notifications = true + + if self.fs_event_providers.libuv then + -- does not work on nfs-drive, at least if it's edited from another + -- machine. + local success, err = self.fs_event:start( + self.path, + {}, + function(err, relpath, events) + self:fs_event_callback(err, relpath, events) + end + ) + + if not success then + log_path.error( + "Could not start libuv-monitor for file %s due to error %s", + self.path, + err + ) + else + log_path.info("Monitoring file %s with libuv-monitor.", self.path) + end + end + + local realpath = Path.normalize(self.path) + + if self.fs_event_providers.autocmd then + if realpath then + self.realpath = realpath + + -- path exists, add file-monitor. + table.insert(M.autocmd_watchers, self) + log_path.info("Monitoring file %s with autocmd-monitor.", self.path) + else + log_path.error( + "Could not resolve realpath for file %s, not enabling BufWritePost-monitor", + self.path + ) + end + end + + if realpath then + table.insert(M.active_watchers, self) + end + + if realpath then + -- path exists, notify. + self:add() + -- no else, never added the path, never call remove. + end +end + +function PathWatcher:stop() + -- don't check which fs_event_providers were actually started, for both of + -- these it should not matter if they weren't. + self.stopped = true + self.send_notifications = false + + self.fs_event:stop() +end + +--- Set up new watcher on a single path only. +--- @param path string Absolute path to the root. +--- @param callbacks LuaSnip.FSWatcher.PathCallbacks The callbacks to use for this watcher. +--- @param opts LuaSnip.FSWatcher.Options? Options, described in their class. +--- @return LuaSnip.FSWatcher.Path +function M.path(path, callbacks, opts) + local lazy, fs_event_providers = get_opts(opts) + + -- do nothing on missing callback. + callbacks = setmetatable(callbacks or {}, callback_mt) + + --- @as LuaSnip.FSWatcher.Path + local o = setmetatable({ + path = path, + fs_event = uv.new_fs_event(), + -- Don't send an initial remove-callback if the path does not yet + -- exist. + -- Always send add first, or send nothing. + removed = true, + -- these two are just like in TreeWatcher. + stopped = false, + -- wait for `start()` to send notifications. + send_notifications = false, + callbacks = callbacks, + fs_event_providers = fs_event_providers, + }, PathWatcher_mt) + + -- if the path does not yet exist, set watcher up s.t. it will start + -- watching when the directory is created. + if not uv.fs_stat(path) and lazy then + -- root does not yet exist, need to create a watcher that notifies us + -- of its creation. + local parent_path = Path.parent(path) + if not parent_path then + error(("Could not find parent-path for %s"):format(path)) + end + + log_path.info( + "Path %s does not exist yet, watching %s for creation.", + path, + parent_path + ) + + local parent_watcher + parent_watcher = M.tree(parent_path, 1, { + -- in path_watcher, watch for new file. + new_file = function(full) + log_path.info("Path: %s %s", full, path) + if full == path then + o:start() + -- directory was created, stop watching. + parent_watcher:stop_self() + end + end, + }, { lazy = true, fs_event_providers = fs_event_providers }) + else + o:start() + end + + return o +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/init.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/init.lua new file mode 100644 index 00000000..daf6064c --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/init.lua @@ -0,0 +1,158 @@ +local util = require("luasnip.util.util") +local Path = require("luasnip.util.path") +local loader_util = require("luasnip.loaders.util") +local session = require("luasnip.session") +local loader_data = require("luasnip.loaders.data") +local fs_watchers = require("luasnip.loaders.fs_watchers") +local log = require("luasnip.util.log").new("loader") + +local M = {} + +-- used to map cache-name to name passed to format. +local clean_name = { + vscode_packages = "vscode", + vscode_standalone = "vscode-standalone", + snipmate = "snipmate", + lua = "lua", +} +local function default_format(path, _) + path = path:gsub( + vim.pesc(vim.fn.stdpath("data") .. "/site/pack/packer/start"), + "$PLUGINS" + ) + if vim.env.HOME then + path = path:gsub(vim.pesc(vim.env.HOME .. "/.config/nvim"), "$CONFIG") + end + return path +end + +local function default_edit(file) + vim.cmd("edit " .. file) +end + +--- Quickly jump to snippet-file from any source for the active filetypes. +---@param opts nil|table, options for this function: +--- - ft_filter: fn(filetype:string) -> bool +--- Optionally filter filetypes which can be picked from. `true` -> filetype +--- is listed, `false` -> not listed. +--- +--- - format: fn(path:string, source_name:string) -> string|nil +--- source_name is one of "vscode", "snipmate" or "lua". +--- May be used to format the displayed items. For example, replace the +--- excessively long packer-path with something shorter. +--- If format returns nil for some item, the item will not be displayed. +--- +--- - edit: fn(file:string): this function is called with the snippet-file as +--- the lone argument. +--- The default is a function which just calls `vim.cmd("edit " .. file)`. +function M.edit_snippet_files(opts) + opts = opts or {} + local format = opts.format or default_format + local edit = opts.edit or default_edit + local extend = opts.extend or function(_, _) + return {} + end + + local function ft_edit_picker(ft, _) + if ft then + local ft_paths = {} + local items = {} + + -- concat files from all loaders for the selected filetype ft. + for cache_name, ft_file_set in pairs({ + vscode_packages = loader_data.vscode_ft_paths[ft], + vscode_standalone = {}, + snipmate = loader_data.snipmate_ft_paths[ft], + lua = loader_data.lua_ft_paths[ft], + }) do + for path, _ in pairs(ft_file_set or {}) do + local fmt_name = format(path, clean_name[cache_name]) + if fmt_name then + table.insert(ft_paths, path) + table.insert(items, fmt_name) + end + end + end + + -- extend filetypes with user-defined function. + local extended = extend(ft, ft_paths) + assert( + type(extended) == "table", + "You must return a table in extend function" + ) + for _, pair in ipairs(extended) do + table.insert(items, pair[1]) + table.insert(ft_paths, pair[2]) + end + + -- prompt user again if there are multiple files providing this filetype. + if #ft_paths > 1 then + vim.ui.select(items, { + prompt = "Multiple files for this filetype, choose one:", + }, function(_, indx) + if indx and ft_paths[indx] then + edit(ft_paths[indx]) + end + end) + elseif ft_paths[1] then + edit(ft_paths[1]) + end + end + end + + local ft_filter = opts.ft_filter or util.yes + + local all_fts = {} + vim.list_extend(all_fts, util.get_snippet_filetypes()) + vim.list_extend( + all_fts, + loader_util.get_load_fts(vim.api.nvim_get_current_buf()) + ) + all_fts = util.deduplicate(all_fts) + + local filtered_fts = {} + for _, ft in ipairs(all_fts) do + if ft_filter(ft) then + table.insert(filtered_fts, ft) + end + end + + if #filtered_fts == 1 then + ft_edit_picker(filtered_fts[1]) + elseif #filtered_fts > 1 then + vim.ui.select(filtered_fts, { + prompt = "Select filetype:", + }, ft_edit_picker) + end +end + +function M.cleanup() + require("luasnip.loaders.from_lua").clean() + require("luasnip.loaders.from_snipmate").clean() + require("luasnip.loaders.from_vscode").clean() +end + +function M.load_lazy_loaded(bufnr) + local fts = loader_util.get_load_fts(bufnr) + + for _, ft in ipairs(fts) do + if not session.loaded_fts[ft] then + require("luasnip.loaders.from_lua")._load_lazy_loaded_ft(ft) + require("luasnip.loaders.from_snipmate")._load_lazy_loaded_ft(ft) + require("luasnip.loaders.from_vscode")._load_lazy_loaded_ft(ft) + end + session.loaded_fts[ft] = true + end +end + +function M.reload_file(path) + local realpath = Path.normalize(path) + if not realpath then + return nil, ("Could not reload file %s: does not exist."):format(path) + else + fs_watchers.write_notify(realpath) + return true + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/snippet_cache.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/snippet_cache.lua new file mode 100644 index 00000000..42a590cb --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/snippet_cache.lua @@ -0,0 +1,93 @@ +local uv = vim.uv or vim.loop +local duplicate = require("luasnip.nodes.duplicate") + +--- @class LuaSnip.Loaders.SnippetCache.Mtime +--- @field sec number +--- @field nsec number +--- Stores modified time for a file. + +--- @class LuaSnip.Loaders.SnippetCache.TimeCacheEntry +--- @field mtime LuaSnip.Loaders.SnippetCache.Mtime? +--- @field data LuaSnip.Loaders.SnippetFileData +--- mtime is nil if the file does not currently exist. Since `get_fn` may still +--- return data, there's no need to treat this differently. + +--- @class LuaSnip.Loaders.SnippetCache +--- SnippetCache stores snippets and other data loaded by files. +--- @field private get_fn fun(file: string): LuaSnip.Loaders.SnippetFileData +--- @field private cache table +local SnippetCache = {} +SnippetCache.__index = SnippetCache + +local M = {} + +--- @class LuaSnip.Loaders.SnippetFileData +--- @field snippets LuaSnip.Addable[] +--- @field autosnippets LuaSnip.Addable[] +--- @field misc table any data. + +--- Create new cache. +--- @param get_fn fun(file: string): LuaSnip.Loaders.SnippetFileData +--- @return LuaSnip.Loaders.SnippetCache +function M.new(get_fn) + return setmetatable({ + get_fn = get_fn, + cache = {}, + }, SnippetCache) +end + +--- Copy addables from data to new table. +--- @param data LuaSnip.Loaders.SnippetFileData +--- @return LuaSnip.Loaders.SnippetFileData +local function copy_filedata(data) + --- @as LuaSnip.Loaders.SnippetFileData + return { + snippets = vim.tbl_map(duplicate.duplicate_addable, data.snippets), + autosnippets = vim.tbl_map( + duplicate.duplicate_addable, + data.autosnippets + ), + misc = vim.deepcopy(data.misc), + } +end + +--- Retrieve loaded data for any file, either from the cache, or directly from +--- the file. +--- For storage-efficiency (and to elide the otherwise necessary deepcopy), the +--- snippets are duplicated, which should not leak. +--- @param fname string +--- @return LuaSnip.Loaders.SnippetFileData +function SnippetCache:fetch(fname) + local cached = self.cache[fname] + local current_stat = uv.fs_stat(fname) + + --- @as LuaSnip.Loaders.SnippetCache.Mtime + local mtime = current_stat and current_stat.mtime + + if + cached + and mtime + and mtime.sec == cached.mtime.sec + and mtime.nsec == cached.mtime.nsec + then + -- happy path: data is cached, and valid => just return cached data. + return copy_filedata(cached.data) + end + + -- data is stale (cache entry does not exist, file was written after + -- cache-creation, or the file was deleted). + -- fetch data from updated file + local res = self.get_fn(fname) + + -- store it. + self.cache[fname] = { + data = res, + mtime = mtime, + } + + -- return it. + -- Don't copy here, no need to. + return res +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/types.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/types.lua new file mode 100644 index 00000000..b1297e14 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/types.lua @@ -0,0 +1,8 @@ +--- @class LuaSnip.Loaders.LoadOpts +--- @field paths string[]?|string? Either a list of paths, or ","-delimited paths. If nil, searches rtp for snippet-collections. +--- @field lazy_paths string[]?|string? Like paths, but these will be watched, and loaded when creation is detected. +--- @field include string[]? If set, all filetypes not in include will be excluded from loading. +--- @field exclude string[]? Exclude these filetypes, even if they are set in include. +--- @field override_priority number? load all snippets with this priority. +--- @field default_priority number? snippet-priority, unless the snippet sets its own priority. +--- @field fs_event_providers table? How to monitor the filesystem diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/util.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/util.lua new file mode 100644 index 00000000..59333af6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/loaders/util.lua @@ -0,0 +1,299 @@ +local Path = require("luasnip.util.path") +local util = require("luasnip.util.util") +local session = require("luasnip.session") +local snippet_collection = require("luasnip.session.snippet_collection") +local log = require("luasnip.util.log").new("loaders") + +local function filetypelist_to_set(list) + vim.validate({ list = { list, "table", true } }) + if not list then + return list + end + local out = {} + for _, ft in ipairs(list) do + out[ft] = true + end + return out +end + +local function split_lines(filestring) + local newline_code + if vim.endswith(filestring, "\r\n") then -- dos + newline_code = "\r\n" + elseif vim.endswith(filestring, "\r") then -- mac + -- both mac and unix-files contain a trailing newline which would lead + -- to an additional empty line being read (\r, \n _terminate_ lines, they + -- don't _separate_ them) + newline_code = "\r" + filestring = filestring:sub(1, -2) + elseif vim.endswith(filestring, "\n") then -- unix + newline_code = "\n" + filestring = filestring:sub(1, -2) + else -- dos + newline_code = "\r\n" + end + return vim.split( + filestring, + newline_code, + { plain = true, trimemtpy = false } + ) +end + +local function non_nil(v) + return v ~= nil +end + +local function resolve_root_paths(paths, rtp_dirname) + if not paths then + paths = vim.api.nvim_get_runtime_file(rtp_dirname, true) + end + + paths = vim.tbl_map(Path.expand, paths) + paths = vim.tbl_filter(non_nil, paths) + paths = util.deduplicate(paths) + + return paths +end + +local function resolve_lazy_root_paths(paths) + paths = vim.tbl_map(Path.expand_maybe_nonexisting, paths) + paths = vim.tbl_filter(non_nil, paths) + paths = util.deduplicate(paths) + + return paths +end + +local function ft_filter(include, exclude) + exclude = filetypelist_to_set(exclude) + include = filetypelist_to_set(include) + + return function(lang) + if exclude and exclude[lang] then + return false + end + if include == nil or include[lang] then + return true + end + end +end + +local function _append(tbl, name, elem) + if tbl[name] == nil then + tbl[name] = {} + end + table.insert(tbl[name], elem) +end + +---Get paths of .snippets files +---@param root string @snippet directory path +---@return table @keys are file types, values are paths +local function get_ft_paths(root, extension) + local ft_path = {} + local files, dirs = Path.scandir(root) + for _, file in ipairs(files) do + local ft, ext = Path.basename(file, true) + if ext == extension then + _append(ft_path, ft, file) + end + end + for _, dir in ipairs(dirs) do + -- directory-name is ft for snippet-files. + local ft = vim.fn.fnamemodify(dir, ":t") + files, _ = Path.scandir(dir) + for _, file in ipairs(files) do + if vim.endswith(file, extension) then + -- produce normalized filenames. + local normalized_fname = Path.normalize(file) + if normalized_fname then + _append(ft_path, ft, normalized_fname) + end + end + end + end + return ft_path +end + +-- fname must be in the directory-tree below root. +-- collection_root may not end with a path-separator. +-- If both are from "realpath", and fname belongs to the collection, this +-- should be a given. +local function collection_file_ft(collection_root, fname) + local collection_components = Path.components(collection_root) + local fname_components = Path.components(fname) + + if #fname_components == #collection_components + 1 then + -- if the file is a direct child of the collection-root, get the text + -- before the last dot. + return fname_components[#collection_components + 1]:match( + "(.*)%.[^%.]*" + ) + else + -- if the file is nested deeper, the name of the directory immediately + -- below the root is the filetype. + return fname_components[#collection_components + 1] + end +end + +-- extend table like {lua = {path1}, c = {path1, path2}, ...}, new_paths has the same layout. +local function extend_ft_paths(paths, new_paths) + for ft, path in pairs(new_paths) do + if paths[ft] then + vim.list_extend(paths[ft], path) + else + paths[ft] = vim.deepcopy(path) + end + end +end + +--- Find +--- 1. all files that belong to a collection and +--- 2. the files from that +--- collection that should actually be loaded. +---@param opts table: straight from `load`/`lazy_load`. +---@param rtp_dirname string: if no path is given in opts, we look for a +--- directory named `rtp_dirname` in the runtimepath. +---@param extension string: extension of valid snippet-files for the given +--- collection (eg `.lua` or `.snippets`) +---@return table: a list of tables, each of the inner tables contains two +--- entries: +--- - collection_paths: ft->files for the entire collection and +--- - load_paths: ft->files for only the files that should be loaded. +--- All produced filenames are normalized, eg. links are resolved and +--- unnecessary . or .. removed. +local function get_load_paths_snipmate_like(opts, rtp_dirname, extension) + local collections_load_paths = {} + + for _, path in ipairs(resolve_root_paths(opts.paths, rtp_dirname)) do + local collection_ft_paths = get_ft_paths(path, extension) + + local load_paths = vim.deepcopy(collection_ft_paths) + -- remove files for excluded/non-included filetypes here. + local collection_filter = ft_filter(opts.exclude, opts.include) + for ft, _ in pairs(load_paths) do + if not collection_filter(ft) then + load_paths[ft] = nil + end + end + + table.insert(collections_load_paths, { + collection_paths = collection_ft_paths, + load_paths = load_paths, + }) + end + + return collections_load_paths +end + +--- Asks (via vim.ui.select) to edit a file that currently provides snippets +---@param ft_files table, map filetype to a number of files. +local function edit_snippet_files(ft_files) + local fts = util.get_snippet_filetypes() + vim.ui.select(fts, { + prompt = "Select filetype:", + }, function(item, _) + if item then + local ft_paths = ft_files[item] + if ft_paths then + -- prompt user again if there are multiple files providing this filetype. + if #ft_paths > 1 then + vim.ui.select(ft_paths, { + prompt = "Multiple files for this filetype, choose one:", + }, function(multi_item) + if multi_item then + vim.cmd("edit " .. multi_item) + end + end) + else + vim.cmd("edit " .. ft_paths[1]) + end + else + print("No file for this filetype.") + end + end + end) +end + +local function make_add_opts(opts) + return { + override_priority = opts.override_priority, + default_priority = opts.default_priority, + } +end + +local function get_load_fts(bufnr) + local fts = session.config.load_ft_func(bufnr) + -- also add "all", loaded by all buffers. + table.insert(fts, "all") + + return util.deduplicate(util.redirect_filetypes(fts)) +end + +local function add_file_snippets(ft, filename, snippets, autosnippets, add_opts) + snippet_collection.add_snippets( + { [ft] = snippets }, + vim.tbl_extend("keep", { + type = "snippets", + key = "__snippets__" .. ft .. "__" .. filename, + }, add_opts) + ) + snippet_collection.add_snippets( + { [ft] = autosnippets }, + vim.tbl_extend("keep", { + type = "autosnippets", + key = "__autosnippets__" .. ft .. "__" .. filename, + }, add_opts) + ) + log.info( + "Adding %s snippets and %s autosnippets from %s to ft `%s`", + #snippets, + #autosnippets, + filename, + ft + ) +end + +local function normalize_opts(opts) + opts = opts or {} + + local paths = opts.paths + if type(paths) == "string" then + paths = vim.split(paths, ",") + end + + local add_opts = make_add_opts(opts) + local include = opts.include + local exclude = opts.exclude + local lazy_paths = opts.lazy_paths or {} + if type(lazy_paths) == "string" then + lazy_paths = vim.split(lazy_paths, ",") + end + + local fs_event_providers = + vim.F.if_nil(opts.fs_event_providers, { autocmd = true, libuv = false }) + + return { + paths = paths, + lazy_paths = lazy_paths, + include = include, + exclude = exclude, + add_opts = add_opts, + fs_event_providers = fs_event_providers, + } +end + +return { + filetypelist_to_set = filetypelist_to_set, + split_lines = split_lines, + resolve_root_paths = resolve_root_paths, + resolve_lazy_root_paths = resolve_lazy_root_paths, + ft_filter = ft_filter, + get_ft_paths = get_ft_paths, + get_load_paths_snipmate_like = get_load_paths_snipmate_like, + extend_ft_paths = extend_ft_paths, + edit_snippet_files = edit_snippet_files, + make_add_opts = make_add_opts, + collection_file_ft = collection_file_ft, + get_load_fts = get_load_fts, + add_file_snippets = add_file_snippets, + normalize_opts = normalize_opts, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/absolute_indexer.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/absolute_indexer.lua new file mode 100644 index 00000000..18546bdb --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/absolute_indexer.lua @@ -0,0 +1,27 @@ +-- absolute_indexer[0][1][2][3] -> { absolute_insert_position = {0,1,2,3} } + +local function new() + return setmetatable({ + absolute_insert_position = {}, + }, { + __index = function(table, key) + table.absolute_insert_position[#table.absolute_insert_position + 1] = + key + return table + end, + }) +end + +return setmetatable({}, { + __index = function(_, key) + -- create new table and index it. + return new()[key] + end, + __call = function(_, ...) + return { + -- passing ... to a function passes only the first of the + -- variable number of args. + absolute_insert_position = type(...) == "number" and { ... } or ..., + } + end, +}) diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/choiceNode.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/choiceNode.lua new file mode 100644 index 00000000..22de611b --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/choiceNode.lua @@ -0,0 +1,427 @@ +local Node = require("luasnip.nodes.node").Node +local ChoiceNode = Node:new() +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local mark = require("luasnip.util.mark").mark +local session = require("luasnip.session") +local sNode = require("luasnip.nodes.snippet").SN +local extend_decorator = require("luasnip.util.extend_decorator") + +function ChoiceNode:init_nodes() + for i, choice in ipairs(self.choices) do + -- setup jumps + choice.next = self + choice.prev = self + + -- forward values for unknown keys from choiceNode. + choice.choice = self + local node_mt = getmetatable(choice) + setmetatable(choice, { + __index = function(node, key) + return node_mt[key] or node.choice[key] + end, + }) + + -- replace nodes' original update_dependents with function that also + -- calls this choiceNodes' update_dependents. + -- + -- cannot define as `function node:update_dependents()` as _this_ + -- choiceNode would be `self`. + -- Also rely on node.choice, as using `self` there wouldn't be caught + -- by copy and the wrong node would be updated. + choice.update_dependents = function(node) + node:_update_dependents() + node.choice:update_dependents() + end + + choice.next_choice = self.choices[i + 1] + choice.prev_choice = self.choices[i - 1] + end + self.choices[#self.choices].next_choice = self.choices[1] + self.choices[1].prev_choice = self.choices[#self.choices] + + self.active_choice = self.choices[1] +end + +local function C(pos, choices, opts) + opts = opts or {} + if opts.restore_cursor == nil then + -- disable by default, can affect performance. + opts.restore_cursor = false + end + + -- allow passing table of nodes in choices, will be turned into a + -- snippetNode. + for indx, choice in ipairs(choices) do + if not getmetatable(choice) then + -- is a normal table, not a node. + choices[indx] = sNode(nil, choice) + end + end + + local c = ChoiceNode:new({ + active = false, + pos = pos, + choices = choices, + type = types.choiceNode, + mark = nil, + dependents = {}, + -- default to true. + restore_cursor = opts.restore_cursor, + }, opts) + c:init_nodes() + return c +end +extend_decorator.register(C, { arg_indx = 3 }) + +function ChoiceNode:subsnip_init() + node_util.subsnip_init_children(self.parent, self.choices) +end + +ChoiceNode.init_positions = node_util.init_child_positions_func( + "absolute_position", + "choices", + "init_positions" +) +ChoiceNode.init_insert_positions = node_util.init_child_positions_func( + "absolute_insert_position", + "choices", + "init_insert_positions" +) + +function ChoiceNode:make_args_absolute() + -- relative indices are relative to the parent of the choiceNode, + -- temporarily remove last component of position + local last_indx = #self.absolute_insert_position + local last = self.absolute_insert_position[last_indx] + self.absolute_insert_position[#self.absolute_insert_position] = nil + + for _, choice in ipairs(self.choices) do + -- relative to choiceNode!! + choice:make_args_absolute(self.absolute_insert_position) + end + + self.absolute_insert_position[last_indx] = last +end + +function ChoiceNode:put_initial(pos) + local old_pos = vim.deepcopy(pos) + + self.active_choice:put_initial(pos) + + local mark_opts = vim.tbl_extend("keep", { + right_gravity = false, + end_right_gravity = false, + }, self.active_choice:get_passive_ext_opts()) + + self.active_choice.mark = mark(old_pos, pos, mark_opts) + self.visible = true +end + +function ChoiceNode:indent(indentstr) + for _, node in ipairs(self.choices) do + node:indent(indentstr) + end +end + +function ChoiceNode:expand_tabs(tabwidth, indentstringlen) + for _, node in ipairs(self.choices) do + node:expand_tabs(tabwidth, indentstringlen) + end +end + +function ChoiceNode:input_enter(_, dry_run) + if dry_run then + dry_run.active[self] = true + return + end + + self.mark:update_opts(self.ext_opts.active) + self:focus() + + self.prev_choice_node = + session.active_choice_nodes[vim.api.nvim_get_current_buf()] + session.active_choice_nodes[vim.api.nvim_get_current_buf()] = self + self.visited = true + self.active = true + + self:event(events.enter) +end + +function ChoiceNode:input_leave(_, dry_run) + if dry_run then + dry_run.active[self] = false + return + end + + self:event(events.leave) + + self.mark:update_opts(self:get_passive_ext_opts()) + self:update_dependents() + session.active_choice_nodes[vim.api.nvim_get_current_buf()] = + self.prev_choice_node + self.active = false +end + +function ChoiceNode:set_old_text() + self.old_text = self:get_text() + self.active_choice.old_text = self.old_text +end + +function ChoiceNode:get_static_text() + return self.choices[1]:get_static_text() +end + +function ChoiceNode:get_docstring() + return util.string_wrap( + self.choices[1]:get_docstring(), + rawget(self, "pos") + ) +end + +function ChoiceNode:jump_into(dir, no_move, dry_run) + self:init_dry_run_active(dry_run) + + if self:is_active(dry_run) then + self:input_leave(no_move, dry_run) + + if dir == 1 then + return self.next:jump_into(dir, no_move, dry_run) + else + return self.prev:jump_into(dir, no_move, dry_run) + end + else + self:input_enter(no_move, dry_run) + + return self.active_choice:jump_into(dir, no_move, dry_run) + end +end + +function ChoiceNode:update() + self.active_choice:update() +end + +function ChoiceNode:update_static_all() + for _, choice in ipairs(self.choices) do + choice:update_static() + end +end + +function ChoiceNode:update_static() + self.active_choice:update_static() +end + +function ChoiceNode:update_restore() + self.active_choice:update_restore() +end + +function ChoiceNode:setup_choice_jumps() end + +function ChoiceNode:find_node(predicate) + if self.active_choice then + if predicate(self.active_choice) then + return self.active_choice + else + return self.active_choice:find_node(predicate) + end + end + return nil +end + +-- used to uniquely identify this change-choice-action. +local change_choice_id = 0 + +function ChoiceNode:set_choice(choice, current_node) + change_choice_id = change_choice_id + 1 + -- to uniquely identify this node later (storing the pointer isn't enough + -- because this is supposed to work with restoreNodes, which are copied). + current_node.change_choice_id = change_choice_id + + local insert_pre_cc = vim.fn.mode() == "i" + -- is byte-indexed! Doesn't matter here, but important to be aware of. + local cursor_pos_pre_relative = + util.pos_sub(util.get_cursor_0ind(), current_node.mark:pos_begin_raw()) + + self.active_choice:store() + + -- tear down current choice. + -- leave all so the choice (could be a snippet) is in the correct state for the next enter. + node_util.leave_nodes_between(self.active_choice, current_node) + + self.active_choice:exit() + + -- clear text. + -- + -- active_choice has to be disabled (nilled?) to prevent reading from + -- cleared mark in set_mark_rgrav (which will be called in + -- self:set_text({""}) a few lines below). + self.active_choice = nil + self:set_text({ "" }) + + self.active_choice = choice + + self.active_choice.mark = self.mark:copy_pos_gravs( + vim.deepcopy(self.active_choice:get_passive_ext_opts()) + ) + + -- re-init positions for child-restoreNodes (they will update their + -- children in put_initial, but their own position has to be changed here). + self:init_positions(self.absolute_position) + self:init_insert_positions(self.absolute_insert_position) + + -- self is still focused, from `set_text`. + self.active_choice:put_initial(self.mark:pos_begin_raw()) + -- adjust gravity in left side of inserted node, such that it matches the + -- current gravity of self. + local _, to = self.mark:pos_begin_end_raw() + self.active_choice:subtree_set_pos_rgrav(to, -1, true) + + self.active_choice:update_restore() + self.active_choice:update_all_dependents() + self:update_dependents() + + -- Another node may have been entered in update_dependents. + self:focus() + self:event(events.change_choice) + + if self.restore_cursor then + local target_node = self:find_node(function(test_node) + return test_node.change_choice_id == change_choice_id + end) + + if target_node then + -- the node that the cursor was in when changeChoice was called exists + -- in the active choice! Enter it and all nodes between it and this choiceNode, + -- then set the cursor. + -- Pass no_move=true, we will set the cursor ourselves. + node_util.enter_nodes_between(self, target_node, true) + + if insert_pre_cc then + util.set_cursor_0ind( + util.pos_add( + target_node.mark:pos_begin_raw(), + cursor_pos_pre_relative + ) + ) + else + node_util.select_node(target_node) + end + return target_node + end + end + + return self.active_choice:jump_into(1) +end + +function ChoiceNode:change_choice(dir, current_node) + -- stylua: ignore + return self:set_choice( + dir == 1 and self.active_choice.next_choice + or self.active_choice.prev_choice, + current_node ) +end + +function ChoiceNode:copy() + local o = vim.deepcopy(self) + for i, node in ipairs(self.choices) do + if node.type == types.snippetNode or node.type == types.choiceNode then + o.choices[i] = node:copy() + else + setmetatable(o.choices[i], getmetatable(node)) + end + end + setmetatable(o, getmetatable(self)) + return o +end + +function ChoiceNode:exit() + self.visible = false + if self.active_choice then + self.active_choice:exit() + end + self.mark:clear() + if self.active then + session.active_choice_nodes[vim.api.nvim_get_current_buf()] = + self.prev_choice_node + end + self.active = false +end + +function ChoiceNode:set_ext_opts(name) + Node.set_ext_opts(self, name) + + self.active_choice:set_ext_opts(name) +end + +function ChoiceNode:store() + self.active_choice:store() +end + +function ChoiceNode:insert_to_node_absolute(position) + if #position == 0 then + return self.absolute_position + end + local front = util.pop_front(position) + return self.choices[front]:insert_to_node_absolute(position) +end + +function ChoiceNode:set_dependents() + for _, node in ipairs(self.choices) do + node:set_dependents() + end +end + +function ChoiceNode:set_argnodes(dict) + Node.set_argnodes(self, dict) + + for _, node in ipairs(self.choices) do + node:set_argnodes(dict) + end +end + +function ChoiceNode:update_all_dependents() + -- call the version that only updates this node. + self:_update_dependents() + + self.active_choice:update_all_dependents() +end + +function ChoiceNode:update_all_dependents_static() + -- call the version that only updates this node. + self:_update_dependents_static() + + self.active_choice:update_all_dependents_static() +end + +function ChoiceNode:resolve_position(position) + return self.choices[position] +end + +function ChoiceNode:static_init() + Node.static_init(self) + self.active_choice:static_init() +end + +function ChoiceNode:subtree_set_pos_rgrav(pos, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) + if self.active_choice then + self.active_choice:subtree_set_pos_rgrav(pos, direction, rgrav) + end +end + +function ChoiceNode:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) + if self.active_choice then + self.active_choice:subtree_set_rgrav(rgrav) + end +end + +function ChoiceNode:extmarks_valid() + return node_util.generic_extmarks_valid(self, self.active_choice) +end + +return { + C = C, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/duplicate.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/duplicate.lua new file mode 100644 index 00000000..eac2a31e --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/duplicate.lua @@ -0,0 +1,73 @@ +local snip_mod = require("luasnip.nodes.snippet") + +local M = {} + +local DupExpandable = {} + +-- just pass these through to _expandable. +function DupExpandable:get_docstring() + return self._expandable:get_docstring() +end +function DupExpandable:copy() + local copy = self._expandable:copy() + copy.id = self.id + + return copy +end + +-- this is modified in `self:invalidate` _and_ needs to be called on _expandable. +function DupExpandable:matches(...) + -- use snippet-module matches, self._expandable might have had its match + -- overwritten by invalidate. + -- (if there are more issues with this, consider some other mechanism for + -- invalidating) + return snip_mod.Snippet.matches(self._expandable, ...) +end + +-- invalidate has to be called on this snippet itself. +function DupExpandable:invalidate() + snip_mod.Snippet.invalidate(self) +end + +local dup_mt = { + -- index DupExpandable for own functions, and then the expandable stored in + -- self/t. + __index = function(t, k) + if DupExpandable[k] then + return DupExpandable[k] + end + + return t._expandable[k] + end, +} + +function M.duplicate_expandable(expandable) + return setmetatable({ + _expandable = expandable, + -- copy these! + -- if `expandable` is invalidated, we don't necessarily want this + -- expandable to be invalidated as well. + hidden = expandable.hidden, + invalidated = expandable.invalidated, + }, dup_mt) +end + +local DupAddable = {} + +function DupAddable:retrieve_all() + -- always return the same set of items, necessary when for invalidate via + -- key to work correctly. + return self._all +end +local DupAddable_mt = { + __index = DupAddable, +} + +function M.duplicate_addable(addable) + return setmetatable({ + addable = addable, + _all = vim.tbl_map(M.duplicate_expandable, addable:retrieve_all()), + }, DupAddable_mt) +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/dynamicNode.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/dynamicNode.lua new file mode 100644 index 00000000..0daad6f2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/dynamicNode.lua @@ -0,0 +1,439 @@ +local DynamicNode = require("luasnip.nodes.node").Node:new() +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local Node = require("luasnip.nodes.node").Node +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local FunctionNode = require("luasnip.nodes.functionNode").FunctionNode +local SnippetNode = require("luasnip.nodes.snippet").SN +local extend_decorator = require("luasnip.util.extend_decorator") + +local function D(pos, fn, args, opts) + opts = opts or {} + + return DynamicNode:new({ + pos = pos, + fn = fn, + args = node_util.wrap_args(args), + type = types.dynamicNode, + mark = nil, + user_args = opts.user_args or {}, + dependents = {}, + active = false, + }, opts) +end +extend_decorator.register(D, { arg_indx = 4 }) + +function DynamicNode:input_enter(_, dry_run) + if dry_run then + dry_run.active[self] = true + return + end + + self.visited = true + self.active = true + self.mark:update_opts(self.ext_opts.active) + + self:event(events.enter) +end + +function DynamicNode:input_leave(_, dry_run) + if dry_run then + dry_run.active[self] = false + return + end + self:event(events.leave) + + self:update_dependents() + self.active = false + self.mark:update_opts(self:get_passive_ext_opts()) +end + +function DynamicNode:get_static_text() + if self.static_snip then + return self.static_snip:get_static_text() + else + self:update_static() + if self.static_snip then + return self.static_snip:get_static_text() + else + return { "" } + end + end +end + +function DynamicNode:get_docstring() + if not self.docstring then + if self.static_snip then + self.docstring = self.static_snip:get_docstring() + else + self.docstring = { "" } + end + end + return self.docstring +end + +-- DynamicNode's don't have static text, only set as visible. +function DynamicNode:put_initial(_) + self.visible = true +end + +function DynamicNode:indent(_) end + +function DynamicNode:expand_tabs(_) end + +function DynamicNode:jump_into(dir, no_move, dry_run) + -- init dry_run-state for this node. + self:init_dry_run_active(dry_run) + + if self:is_active(dry_run) then + self:input_leave(no_move, dry_run) + + if dir == 1 then + return self.next:jump_into(dir, no_move, dry_run) + else + return self.prev:jump_into(dir, no_move, dry_run) + end + else + self:input_enter(no_move, dry_run) + + if self.snip then + return self.snip:jump_into(dir, no_move, dry_run) + else + -- this will immediately enter and leave, but IMO that's expected + -- behaviour. + self:input_leave(no_move, dry_run) + if dir == 1 then + return self.next:jump_into(dir, no_move, dry_run) + else + return self.prev:jump_into(dir, no_move, dry_run) + end + end + end +end + +function DynamicNode:update() + local args = self:get_args() + if vim.deep_equal(self.last_args, args) then + -- no update, the args still match. + return + end + + if not self.parent.snippet:extmarks_valid() then + error("Refusing to update inside a snippet with invalid extmarks") + end + + local tmp + if self.snip then + if not args then + -- a snippet exists, don't delete it. + return + end + + -- build new snippet before exiting, markers may be needed for construncting. + tmp = self.fn( + args, + self.parent, + self.snip.old_state, + unpack(self.user_args) + ) + self.snip:exit() + self.snip = nil + + -- focuses node. + self:set_text({ "" }) + else + self:focus() + if not args then + -- no snippet exists, set an empty one. + tmp = SnippetNode(nil, {}) + else + -- also enter node here. + tmp = self.fn(args, self.parent, nil, unpack(self.user_args)) + end + end + self.last_args = args + + -- act as if snip is directly inside parent. + tmp.parent = self.parent + tmp.indx = self.indx + + tmp.next = self + tmp.prev = self + + tmp.snippet = self.parent.snippet + + tmp:resolve_child_ext_opts() + tmp:resolve_node_ext_opts() + tmp:subsnip_init() + + tmp.mark = + self.mark:copy_pos_gravs(vim.deepcopy(tmp:get_passive_ext_opts())) + tmp.dynamicNode = self + tmp.update_dependents = function(node) + node:_update_dependents() + node.dynamicNode:update_dependents() + end + + tmp:init_positions(self.snip_absolute_position) + tmp:init_insert_positions(self.snip_absolute_insert_position) + + tmp:make_args_absolute() + + tmp:set_dependents() + tmp:set_argnodes(self.parent.snippet.dependents_dict) + + if vim.bo.expandtab then + tmp:expand_tabs(util.tab_width(), #self.parent.indentstr) + end + tmp:indent(self.parent.indentstr) + + -- sets own extmarks false,true + self:focus() + local from, to = self.mark:pos_begin_end_raw() + -- inserts nodes with extmarks false,false + tmp:put_initial(from) + -- adjust gravity in left side of snippet, such that it matches the current + -- gravity of self. + tmp:subtree_set_pos_rgrav(to, -1, true) + + self.snip = tmp + + -- Update, tbh no idea how that could come in handy, but should be done. + -- Both are needed, because + -- - a node could only depend on nodes outside of tmp + -- - a node outside of tmp could depend on one inside of tmp + tmp:update() + tmp:update_all_dependents() + + self:update_dependents() +end + +local update_errorstring = [[ +Error while evaluating dynamicNode@%d for snippet '%s': +%s + +:h luasnip-docstring for more info]] +function DynamicNode:update_static() + local args = self:get_static_args() + if vim.deep_equal(self.last_static_args, args) then + -- no update, the args still match. + return + end + + local tmp, ok + if self.static_snip then + if not args then + -- a snippet exists, don't delete it. + return + end + + -- build new snippet before exiting, markers may be needed for construncting. + ok, tmp = pcall( + self.fn, + args, + self.parent, + self.snip.old_state, + unpack(self.user_args) + ) + else + if not args then + -- no snippet exists, set an empty one. + tmp = SnippetNode(nil, {}) + else + -- also enter node here. + ok, tmp = + pcall(self.fn, args, self.parent, nil, unpack(self.user_args)) + end + end + if not ok then + print( + update_errorstring:format(self.indx, self.parent.snippet.name, tmp) + ) + -- set empty snippet on failure + tmp = SnippetNode(nil, {}) + end + self.last_static_args = args + + -- act as if snip is directly inside parent. + tmp.parent = self.parent + tmp.indx = self.indx + tmp.pos = rawget(self, "pos") + + tmp.next = self + tmp.prev = self + + -- doesn't matter here, but they'll have to be set. + tmp.ext_opts = self.parent.ext_opts + tmp.snippet = self.parent.snippet + + tmp.dynamicNode = self + tmp.update_dependents_static = function(node) + node:_update_dependents_static() + node.dynamicNode:update_dependents_static() + end + + tmp:resolve_child_ext_opts() + tmp:resolve_node_ext_opts() + tmp:subsnip_init() + + tmp:init_positions(self.snip_absolute_position) + tmp:init_insert_positions(self.snip_absolute_insert_position) + + tmp:make_args_absolute() + + tmp:set_dependents() + tmp:set_argnodes(self.parent.snippet.dependents_dict) + + -- do not expand tabs!! This is only necessary if the snippet is inserted + -- in a buffer, some information is lost if tabs (indent) is replaced with + -- whitespace. + -- This might make a difference when another f/dynamicNode depends on this + -- one, and the function expects expanded tabs... imo the function should + -- be adjusted to accept any whitespace. + tmp:indent(self.parent.indentstr) + + tmp:static_init() + + tmp:update_static() + -- updates dependents in tmp. + tmp:update_all_dependents_static() + + self.static_snip = tmp + -- updates own dependents. + self:update_dependents_static() +end + +function DynamicNode:exit() + self.visible = false + self.mark:clear() + -- check if snip actually exists, may not be the case if + -- the surrounding snippet was deleted just before. + if self.snip then + self.snip:exit() + end + self.stored_snip = self.snip + self.snip = nil + self.active = false +end + +function DynamicNode:set_ext_opts(name) + Node.set_ext_opts(self, name) + + -- might not have been generated (missing nodes). + if self.snip then + self.snip:set_ext_opts(name) + end +end + +function DynamicNode:store() + if self.snip then + self.snip:store() + end +end + +function DynamicNode:update_restore() + -- only restore snippet if arg-values still match. + if self.stored_snip and vim.deep_equal(self:get_args(), self.last_args) then + local tmp = self.stored_snip + + tmp.mark = + self.mark:copy_pos_gravs(vim.deepcopy(tmp:get_passive_ext_opts())) + + -- position might (will probably!!) still have changed, so update it + -- here too (as opposed to only in update). + tmp:init_positions(self.snip_absolute_position) + tmp:init_insert_positions(self.snip_absolute_insert_position) + + tmp:make_args_absolute() + + tmp:set_dependents() + tmp:set_argnodes(self.parent.snippet.dependents_dict) + + -- sets own extmarks false,true + self:focus() + -- inserts nodes with extmarks false,false + local from, to = self.mark:pos_begin_end_raw() + tmp:put_initial(from) + -- adjust gravity in left side of snippet, such that it matches the current + -- gravity of self. + tmp:subtree_set_pos_rgrav(to, -1, true) + + -- set snip before update_restore, since update_restore involves + -- calling `focus`, and that needs `snip` to be set. + -- If it is not set, tmp is not reachable via get_nodes_between. + -- (TODO: This is pretty bad, have to rethink design sometime). + self.snip = tmp + + tmp:update_restore() + else + self:update() + end +end + +function DynamicNode:find_node(predicate) + if self.snip then + if predicate(self.snip) then + return self.snip + else + return self.snip:find_node(predicate) + end + end + return nil +end + +function DynamicNode:insert_to_node_absolute(position) + if #position == 0 then + return self.absolute_position + end + return self.snip and self.snip:insert_to_node_absolute(position) +end + +function DynamicNode:init_insert_positions(position_so_far) + Node.init_insert_positions(self, position_so_far) + self.snip_absolute_insert_position = + vim.deepcopy(self.absolute_insert_position) + -- nodes of current snippet should have a 0 before. + self.snip_absolute_insert_position[#self.snip_absolute_insert_position + 1] = + 0 +end + +function DynamicNode:init_positions(position_so_far) + Node.init_positions(self, position_so_far) + self.snip_absolute_position = vim.deepcopy(self.absolute_position) + -- Reach current snippet as snip_absolute_position..0. + self.snip_absolute_position[#self.snip_absolute_position + 1] = 0 +end + +DynamicNode.make_args_absolute = FunctionNode.make_args_absolute +DynamicNode.set_dependents = FunctionNode.set_dependents + +function DynamicNode:resolve_position(position) + -- position must be 0, there are no other options. + return self.snip +end + +function DynamicNode:subtree_set_pos_rgrav(pos, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) + if self.snip then + self.snip:subtree_set_pos_rgrav(pos, direction, rgrav) + end +end + +function DynamicNode:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) + if self.snip then + self.snip:subtree_set_rgrav(rgrav) + end +end + +function DynamicNode:extmarks_valid() + if self.snip then + return node_util.generic_extmarks_valid(self, self.snip) + end + return true +end + +return { + D = D, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/functionNode.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/functionNode.lua new file mode 100644 index 00000000..cb1a77ae --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/functionNode.lua @@ -0,0 +1,155 @@ +local Node = require("luasnip.nodes.node").Node +local FunctionNode = Node:new() +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local types = require("luasnip.util.types") +local tNode = require("luasnip.nodes.textNode").textNode +local extend_decorator = require("luasnip.util.extend_decorator") +local key_indexer = require("luasnip.nodes.key_indexer") + +local function F(fn, args, opts) + opts = opts or {} + + return FunctionNode:new({ + fn = fn, + args = node_util.wrap_args(args), + type = types.functionNode, + mark = nil, + user_args = opts.user_args or {}, + }, opts) +end +extend_decorator.register(F, { arg_indx = 3 }) + +FunctionNode.input_enter = tNode.input_enter + +function FunctionNode:get_static_text() + -- static_text will already have been generated, if possible. + -- If it isn't generated, prevent errors by just setting it to empty text. + if not self.static_text then + self.static_text = { "" } + end + return self.static_text +end + +-- function-text will not stand out in any way in docstring. +FunctionNode.get_docstring = FunctionNode.get_static_text + +function FunctionNode:update() + local args = self:get_args() + -- skip this update if + -- - not all nodes are available. + -- - the args haven't changed. + if not args or vim.deep_equal(args, self.last_args) then + return + end + + if not self.parent.snippet:extmarks_valid() then + error("Refusing to update inside a snippet with invalid extmarks") + end + + self.last_args = args + local text = + util.to_string_table(self.fn(args, self.parent, unpack(self.user_args))) + if vim.bo.expandtab then + util.expand_tabs(text, util.tab_width(), #self.parent.indentstr) + end + + -- don't expand tabs in parent.indentstr, use it as-is. + self:set_text(util.indent(text, self.parent.indentstr)) + self:update_dependents() +end + +local update_errorstring = [[ +Error while evaluating functionNode@%d for snippet '%s': +%s + +:h luasnip-docstring for more info]] +function FunctionNode:update_static() + local args = self:get_static_args() + -- skip this update if + -- - not all nodes are available. + -- - the args haven't changed. + if not args or vim.deep_equal(args, self.last_args) then + return + end + -- should be okay to set last_args even if `fn` potentially fails, future + -- updates will fail aswell, if not the `fn` also doesn't always work + -- correctly in normal expansion. + self.last_args = args + local ok, static_text = + pcall(self.fn, args, self.parent, unpack(self.user_args)) + if not ok then + print( + update_errorstring:format( + self.indx, + self.parent.snippet.name, + static_text + ) + ) + static_text = { "" } + end + self.static_text = + util.indent(util.to_string_table(static_text), self.parent.indentstr) +end + +function FunctionNode:update_restore() + -- only if args still match. + if self.static_text and vim.deep_equal(self:get_args(), self.last_args) then + self:set_text(self.static_text) + else + self:update() + end +end + +-- FunctionNode's don't have static text, only set visibility. +function FunctionNode:put_initial(_) + self.visible = true +end + +function FunctionNode:indent(_) end + +function FunctionNode:expand_tabs(_) end + +function FunctionNode:make_args_absolute(position_so_far) + self.args_absolute = {} + node_util.make_args_absolute(self.args, position_so_far, self.args_absolute) +end + +function FunctionNode:set_dependents() + local dict = self.parent.snippet.dependents_dict + local append_list = + vim.list_extend({ "dependents" }, self.absolute_position) + append_list[#append_list + 1] = "dependent" + + for _, arg in ipairs(self.args_absolute) do + -- if arg is a luasnip-node, just insert it as the key. + -- important!! rawget, because indexing absolute_indexer with some key + -- appends the key. + -- Maybe this is stupid?? + if rawget(arg, "type") ~= nil then + dict:set(vim.list_extend({ arg }, append_list), self) + elseif arg.absolute_insert_position then + -- copy absolute_insert_position, list_extend mutates. + dict:set( + vim.list_extend( + vim.deepcopy(arg.absolute_insert_position), + append_list + ), + self + ) + elseif key_indexer.is_key(arg) then + dict:set(vim.list_extend({ "key", arg.key }, append_list), self) + end + end +end + +function FunctionNode:is_interactive() + -- the function node is only evaluated once if it has no argnodes -> it's + -- not interactive then. + return #self.args ~= 0 +end + +return { + F = F, + FunctionNode = FunctionNode, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/insertNode.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/insertNode.lua new file mode 100644 index 00000000..b8b456fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/insertNode.lua @@ -0,0 +1,330 @@ +local Node = require("luasnip.nodes.node") +local InsertNode = Node.Node:new() +local ExitNode = InsertNode:new() +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local extend_decorator = require("luasnip.util.extend_decorator") + +local function I(pos, static_text, opts) + static_text = util.to_string_table(static_text) + + if pos == 0 then + return ExitNode:new({ + pos = pos, + static_text = static_text, + mark = nil, + dependents = {}, + type = types.exitNode, + -- will only be needed for 0-node, -1-node isn't set with this. + ext_gravities_active = { false, false }, + }, opts) + else + return InsertNode:new({ + pos = pos, + static_text = static_text, + mark = nil, + dependents = {}, + type = types.insertNode, + inner_active = false, + }, opts) + end +end +extend_decorator.register(I, { arg_indx = 3 }) + +function ExitNode:input_enter(no_move, dry_run) + if dry_run then + return + end + + -- Don't enter node for -1-node, it isn't in the node-table. + if self.pos == 0 then + InsertNode.input_enter(self, no_move, dry_run) + else + -- -1-node: + -- set rgrav true on left side of snippet. Text inserted now pushes the + -- snippet, and is not contained in it. + local begin_pos = self.mark:pos_begin_raw() + self.parent:subtree_set_pos_rgrav(begin_pos, 1, true) + + if not no_move then + if vim.fn.mode() == "i" then + util.insert_move_on(begin_pos) + else + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes("", true, false, true), + "n", + true + ) + util.normal_move_on_insert(begin_pos) + end + end + + self:event(events.enter) + end +end + +function ExitNode:focus() + local lrgrav, rrgrav + local snippet = self.parent + -- if last of first node of the snippet, make inserted text move out of snippet. + if snippet.nodes[#snippet.nodes] == self then + lrgrav = false + rrgrav = false + elseif snippet.nodes[1] == self then + lrgrav = true + rrgrav = true + else + lrgrav = false + rrgrav = true + end + + Node.focus_node(self, lrgrav, rrgrav) +end + +function ExitNode:input_leave(no_move, dry_run) + if dry_run then + return + end + + if self.pos == 0 then + InsertNode.input_leave(self, no_move, dry_run) + else + self:event(events.leave) + end +end + +function ExitNode:_update_dependents() end +function ExitNode:update_dependents() end +function ExitNode:update_all_dependents() end + +function ExitNode:_update_dependents_static() end +function ExitNode:update_dependents_static() end +function ExitNode:update_all_dependents_static() end +function ExitNode:is_interactive() + return true +end + +function InsertNode:input_enter(no_move, dry_run) + if dry_run then + return + end + + self.visited = true + self.mark:update_opts(self.ext_opts.active) + + -- no_move only prevents moving the cursor, but the active node should + -- still be focused. + self:focus() + + if not no_move then + -- SELECT snippet text only when there is text to select (more oft than not there isnt). + local mark_begin_pos, mark_end_pos = self.mark:pos_begin_end_raw() + if not util.pos_equal(mark_begin_pos, mark_end_pos) then + util.any_select(mark_begin_pos, mark_end_pos) + else + -- if current and target mode is INSERT, there's no reason to leave it. + if vim.fn.mode() == "i" then + util.insert_move_on(mark_begin_pos) + else + -- mode might be VISUAL or something else, but always leads to normal. + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes("", true, false, true), + "n", + true + ) + util.normal_move_on_insert(mark_begin_pos) + end + end + end + + self:event(events.enter) +end + +-- only necessary for insertNodes, inner_active (unlike `active`) does not occur +-- in other nodes. +-- Since insertNodes don't have `active`, we can use the dry_run.active-field +-- for this. +function InsertNode:init_dry_run_inner_active(dry_run) + if dry_run and dry_run.active[self] == nil then + dry_run.active[self] = self.inner_active + end +end +function InsertNode:is_inner_active(dry_run) + return (not dry_run and self.inner_active) + or (dry_run and dry_run.active[self]) +end + +function InsertNode:jump_into(dir, no_move, dry_run) + self:init_dry_run_inner_active(dry_run) + + if self:is_inner_active(dry_run) then + if dir == 1 then + if self.next then + self:input_leave_children(dry_run) + self:input_leave(no_move, dry_run) + return self.next:jump_into(dir, no_move, dry_run) + else + return nil + end + else + if self.prev then + self:input_leave_children(dry_run) + self:input_leave(no_move, dry_run) + return self.prev:jump_into(dir, no_move, dry_run) + else + return nil + end + end + else + self:input_enter(no_move, dry_run) + return self + end +end + +function ExitNode:jump_from(dir, no_move, dry_run) + self:init_dry_run_inner_active(dry_run) + + local next_node = util.ternary(dir == 1, self.next, self.prev) + local next_inner_node = + util.ternary(dir == 1, self.inner_first, self.inner_last) + + if next_inner_node then + self:input_enter_children(dry_run) + return next_inner_node:jump_into(dir, no_move, dry_run) + else + if next_node then + local next_node_dry_run = { active = {} } + -- don't have to `init_dry_run_inner_active` since this node does + -- not have children active if jump_from is called. + + -- true: don't move + local target_node = + next_node:jump_into(dir, true, next_node_dry_run) + -- if there is no node that can serve as jump-target, just remain + -- here. + -- Regular insertNodes don't have to handle this, since there is + -- always an exitNode or another insertNode at their endpoints. + if not target_node then + return self + end + + self:input_leave(no_move, dry_run) + return next_node:jump_into(dir, no_move, dry_run) or self + else + return self + end + end +end + +function InsertNode:jump_from(dir, no_move, dry_run) + self:init_dry_run_inner_active(dry_run) + + local next_node = util.ternary(dir == 1, self.next, self.prev) + local next_inner_node = + util.ternary(dir == 1, self.inner_first, self.inner_last) + + if next_inner_node then + self:input_enter_children(dry_run) + return next_inner_node:jump_into(dir, no_move, dry_run) + else + if next_node then + self:input_leave(no_move, dry_run) + return next_node:jump_into(dir, no_move, dry_run) + end + end +end + +function InsertNode:input_enter_children(dry_run) + if dry_run then + dry_run.active[self] = true + else + self.inner_active = true + end +end +function InsertNode:input_leave_children(dry_run) + if dry_run then + dry_run.active[self] = false + else + self.inner_active = false + end +end + +function InsertNode:input_leave(_, dry_run) + if dry_run then + return + end + + self:event(events.leave) + + self:update_dependents() + self.mark:update_opts(self:get_passive_ext_opts()) +end + +function InsertNode:exit() + if self.inner_first then + self.inner_first:exit() + end + self.visible = false + self.inner_first = nil + self.inner_last = nil + self.inner_active = false + self.mark:clear() +end + +function InsertNode:get_docstring() + -- copy as to not in-place-modify static text. + return util.string_wrap(self.static_text, rawget(self, "pos")) +end + +function InsertNode:is_interactive() + return true +end + +function InsertNode:child_snippets() + local own_child_snippets = {} + for _, child_snippet in ipairs(self.parent.snippet.child_snippets) do + if child_snippet.parent_node == self then + table.insert(own_child_snippets, child_snippet) + end + end + return own_child_snippets +end + +function InsertNode:subtree_set_pos_rgrav(pos, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) + + local own_child_snippets = self:child_snippets() + + local child_from_indx + if direction == 1 then + child_from_indx = 1 + else + child_from_indx = #own_child_snippets + end + + node_util.nodelist_adjust_rgravs( + own_child_snippets, + child_from_indx, + pos, + direction, + rgrav, + -- don't assume that the child-snippets are all adjacent. + false + ) +end + +function InsertNode:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) + + local own_child_snippets = self:child_snippets() + + for _, child_snippet in ipairs(own_child_snippets) do + child_snippet:subtree_set_rgrav(rgrav) + end +end + +return { + I = I, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/key_indexer.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/key_indexer.lua new file mode 100644 index 00000000..04e6f7f3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/key_indexer.lua @@ -0,0 +1,12 @@ +local M = {} + +local key_mt = {} +function M.new_key(key) + return setmetatable({ key = key }, key_mt) +end + +function M.is_key(t) + return getmetatable(t) == key_mt +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/multiSnippet.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/multiSnippet.lua new file mode 100644 index 00000000..108206ea --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/multiSnippet.lua @@ -0,0 +1,117 @@ +local snip_mod = require("luasnip.nodes.snippet") +local node_util = require("luasnip.nodes.util") +local extend_decorator = require("luasnip.util.extend_decorator") + +local VirtualSnippet = {} +local VirtualSnippet_mt = { __index = VirtualSnippet } + +function VirtualSnippet:get_docstring() + return self.snippet:get_docstring() +end +function VirtualSnippet:copy() + local copy = self.snippet:copy() + copy.id = self.id + + return copy +end + +-- VirtualSnippet has all the fields for executing these methods. +VirtualSnippet.matches = snip_mod.Snippet.matches +VirtualSnippet.invalidate = snip_mod.Snippet.invalidate + +---Create new virtual snippet, ie. an object which is capable of performning +---all the functions expected from a snippet which is yet to be expanded +---(`matches`,`get_docstring`,`invalidate`,`retrieve_all`,`copy`) +---@param context context as defined for snippet-constructor. Table, not nil. +---@param snippet The snippet this virtual snippet will return on `copy`, also not nil. +---@param opts opts as defined for snippet-constructor. Has to be a table, may be empty. +local function new_virtual_snippet(context, snippet, opts) + -- init fields necessary for matches, invalidate, adding the snippet. + local o = snip_mod.init_snippet_context(context, opts) + o.snippet = snippet + + setmetatable(o, VirtualSnippet_mt) + + return o +end + +local MultiSnippet = {} +local MultiSnippet_mt = { __index = MultiSnippet } + +function MultiSnippet:retrieve_all() + return self.v_snips +end + +local function multisnippet_from_snippet_obj(contexts, snippet, snippet_opts) + assert( + type(contexts) == "table", + "multisnippet: expected contexts to be a table." + ) + local common_context = node_util.wrap_context(contexts.common) or {} + + local v_snips = {} + for _, context in ipairs(contexts) do + local complete_context = vim.tbl_extend( + "keep", + node_util.wrap_context(context), + common_context + ) + table.insert( + v_snips, + new_virtual_snippet(complete_context, snippet, snippet_opts) + ) + end + + local o = { + v_snips = v_snips, + } + + setmetatable(o, MultiSnippet_mt) + + return o +end + +local function multisnippet_from_nodes(contexts, nodes, opts) + opts = opts or {} + local common_snip_opts = opts.common_opts or {} + + -- create snippet without `context`-fields! + -- compare to `S` (aka `s`, the default snippet-constructor) in + -- `nodes/snippet.lua`. + return multisnippet_from_snippet_obj( + contexts, + snip_mod._S( + snip_mod.init_snippet_opts(common_snip_opts), + nodes, + common_snip_opts + ), + common_snip_opts + ) +end + +local function extend_multisnippet_contexts(passed_arg, extend_arg) + -- extend passed arg with contexts passed in extend-call + vim.list_extend(passed_arg, extend_arg) + + -- extend ("keep") valid keyword-arguments. + passed_arg.common = vim.tbl_deep_extend( + "keep", + node_util.wrap_context(passed_arg.common) or {}, + node_util.wrap_context(extend_arg.common) or {} + ) + + return passed_arg +end +extend_decorator.register( + multisnippet_from_nodes, + -- first arg needs special handling (extend list of contexts (index i + -- becomes i+#passed_arg, not i again)) + { arg_indx = 1, extend = extend_multisnippet_contexts }, + -- opts can just be `vim.tbl_extend`ed. + { arg_indx = 3 } +) + +return { + new_multisnippet = multisnippet_from_nodes, + _raw_ms = multisnippet_from_snippet_obj, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/node.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/node.lua new file mode 100644 index 00000000..ad41c923 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/node.lua @@ -0,0 +1,643 @@ +local session = require("luasnip.session") +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local ext_util = require("luasnip.util.ext_opts") +local events = require("luasnip.util.events") +local key_indexer = require("luasnip.nodes.key_indexer") +local types = require("luasnip.util.types") + +local Node = {} + +function Node:new(o, opts) + o = o or {} + + -- visible is true if the node is visible on-screen, during normal + -- expansion, static_visible is needed for eg. get_static_text, where + -- argnodes in inactive choices will happily provide their static text, + -- which leads to inaccurate docstrings. + o.visible = false + o.static_visible = false + o.old_text = {} + o.visited = false + -- override existing keys, might be necessary due to double-init from + -- snippetProxy, but shouldn't hurt. + o = vim.tbl_extend("force", o, node_util.init_node_opts(opts or {})) + + setmetatable(o, self) + self.__index = self + + return o +end + +function Node:get_static_text() + -- return nil if not visible. + -- This will prevent updates if not all nodes are visible during + -- docstring/static_text-generation. (One example that would otherwise fail + -- is the following snippet: + -- + -- s("trig", { + -- i(1, "cccc"), + -- t" ", + -- c(2, { + -- t"aaaa", + -- i(nil, "bbbb") + -- }), + -- f(function(args) return args[1][1]..args[2][1] end, {ai[2][2], 1} ) + -- }) + -- + -- ) + -- By also allowing visible, and not only static_visible, the docstrings + -- generated during `get_current_choices` (ie. without having the whole + -- snippet `static_init`ed) get better. + if not self.visible and not self.static_visible then + return nil + end + return self.static_text +end + +function Node:get_docstring() + -- visibility only matters for get_static_text because that's called for + -- argnodes whereas get_docstring will only be called for actually + -- visible nodes. + return self.static_text +end + +function Node:put_initial(pos) + -- access static text directly, get_static_text() won't work due to + -- static_visible not being set. + util.put(self.static_text, pos) + self.visible = true +end + +function Node:input_enter(_, _) + self.visited = true + self.mark:update_opts(self.ext_opts.active) + + self:event(events.enter) +end + +-- dry_run: if not nil, it has to be a table with the key `active` also a table. +-- dry_run.active[node] stores whether the node is "active" in the dry run (we +-- can't change the `active`-state in the actual node, so changes to the +-- active-state are stored in the `dry_run`-table, which is passed to all nodes +-- that participate in the jump) +-- The changes to `active` have to be stored. Otherwise, `dry_run` can lead to +-- endless loops in cases like: +-- ```lua +-- s({ trig = 'n' } , { i(1, "1"), sn(2, {t"asdf"}), i(3, "3") }) +-- ``` +-- +-- Here, jumping from 1 will first set active on the snippetNode, then, since +-- there are no interactive nodes inside it, and since active is set, we will +-- jump to the `i(3)`. +-- If active is not set during the dry_run, we will just keep jumping into the +-- inner textNode. +-- +-- A similar problem occurs in nested expansions (insertNode.inner_active +-- is not set). +function Node:jump_into(_, no_move, dry_run) + if not dry_run then + self:input_enter(no_move, dry_run) + end + return self +end + +function Node:jump_from(dir, no_move, dry_run) + self:input_leave(no_move, dry_run) + if dir == 1 then + if self.next then + return self.next:jump_into(dir, no_move, dry_run) + else + return nil + end + else + if self.prev then + return self.prev:jump_into(dir, no_move, dry_run) + else + return nil + end + end +end + +function Node:jumpable(dir) + if dir == 1 then + return self.next ~= nil + else + return self.prev ~= nil + end +end + +function Node:get_text() + if not self.visible then + return nil + end + local ok, text = pcall(function() + local from_pos, to_pos = self.mark:pos_begin_end_raw() + + -- end-exclusive indexing. + local lines = + vim.api.nvim_buf_get_lines(0, from_pos[1], to_pos[1] + 1, false) + + if #lines == 1 then + lines[1] = string.sub(lines[1], from_pos[2] + 1, to_pos[2]) + else + lines[1] = string.sub(lines[1], from_pos[2] + 1, #lines[1]) + + -- node-range is end-exclusive. + lines[#lines] = string.sub(lines[#lines], 1, to_pos[2]) + end + return lines + end) + -- if deleted. + return ok and text or { "" } +end + +function Node:set_old_text() + self.old_text = self:get_text() +end + +function Node:exit() + self.visible = false + self.mark:clear() +end + +function Node:get_passive_ext_opts() + if self.visited then + return self.ext_opts.visited + else + return self.ext_opts.unvisited + end +end + +function Node:input_leave(_, dry_run) + if dry_run then + return + end + self:event(events.leave) + + self.mark:update_opts(self:get_passive_ext_opts()) +end +function Node:input_leave_children() end +function Node:input_enter_children() end + +local function find_dependents(self, position_self, dict) + local nodes = {} + + -- this might also be called from a node which does not possess a position! + -- (for example, a functionNode may be depended upon via its key) + if position_self then + position_self[#position_self + 1] = "dependents" + vim.list_extend(nodes, dict:find_all(position_self, "dependent") or {}) + position_self[#position_self] = nil + end + + vim.list_extend( + nodes, + dict:find_all({ self, "dependents" }, "dependent") or {} + ) + + if self.key then + vim.list_extend( + nodes, + dict:find_all({ "key", self.key, "dependents" }, "dependent") or {} + ) + end + + return nodes +end + +function Node:_update_dependents() + local dependent_nodes = find_dependents( + self, + self.absolute_insert_position, + self.parent.snippet.dependents_dict + ) + if #dependent_nodes == 0 then + return + end + for _, node in ipairs(dependent_nodes) do + if node.visible then + node:update() + end + end +end + +-- _update_dependents is the function to update the nodes' dependents, +-- update_dependents is what will actually be called. +-- This allows overriding update_dependents in a parent-node (eg. snippetNode) +-- while still having access to the original function (for subsequent overrides). +Node.update_dependents = Node._update_dependents +-- update_all_dependents is used to update all nodes' dependents in a +-- snippet-tree. Necessary in eg. set_choice (especially since nodes may have +-- dependencies outside the tree itself, so update_all_dependents should take +-- care of those too.) +Node.update_all_dependents = Node._update_dependents + +function Node:_update_dependents_static() + local dependent_nodes = find_dependents( + self, + self.absolute_insert_position, + self.parent.snippet.dependents_dict + ) + if #dependent_nodes == 0 then + return + end + for _, node in ipairs(dependent_nodes) do + if node.static_visible then + node:update_static() + end + end +end + +Node.update_dependents_static = Node._update_dependents_static +Node.update_all_dependents_static = Node._update_dependents_static + +function Node:update() end + +function Node:update_static() end + +function Node:expand_tabs(tabwidth, indentstr) + util.expand_tabs(self.static_text, tabwidth, indentstr) +end + +function Node:indent(indentstr) + util.indent(self.static_text, indentstr) +end + +function Node:subsnip_init() end + +function Node:init_positions(position_so_far) + self.absolute_position = vim.deepcopy(position_so_far) +end + +function Node:init_insert_positions(position_so_far) + self.absolute_insert_position = vim.deepcopy(position_so_far) +end + +function Node:event(event) + local node_callback = self.node_callbacks[event] + if node_callback then + node_callback(self) + end + + -- try to get the callback from the parent. + if self.pos then + -- node needs position to get callback (nodes may not have position if + -- defined in a choiceNode, ie. c(1, { + -- i(nil, {"works!"}) + -- })) + -- works just fine. + local parent_callback = self.parent.callbacks[self.pos][event] + if parent_callback then + parent_callback(self) + end + end + + session.event_node = self + vim.api.nvim_exec_autocmds("User", { + pattern = "Luasnip" .. events.to_string(self.type, event), + modeline = false, + }) +end + +local function get_args(node, get_text_func_name) + local argnodes_text = {} + for _, arg in ipairs(node.args_absolute) do + local argnode + if key_indexer.is_key(arg) then + argnode = node.parent.snippet.dependents_dict:get({ + "key", + arg.key, + "node", + }) + else + -- since arg may be a node, it may not be initialized in the snippet + -- and therefore not have an absolute_insert_position. Check for that. + if not arg.absolute_insert_position then + -- the node is not (yet, maybe) visible. + return nil + end + local dict_key = arg.absolute_insert_position + -- will append to arg.absolute_insert_position, but it's restored + -- two lines down. + -- (dict:get shouldn't (yeah yeah, you never know, but this really + -- shouldn't) fail, so we don't worry with pcall) + table.insert(dict_key, "node") + argnode = node.parent.snippet.dependents_dict:get(dict_key) + dict_key[#dict_key] = nil + end + -- maybe the node is part of a dynamicNode and not yet generated. + if not argnode then + return nil + end + + local argnode_text = argnode[get_text_func_name](argnode) + -- can only occur with `get_text`. If one returns nil, the argnode + -- isn't visible or some other error occured. Either way, return nil + -- to signify that not all argnodes are available. + if not argnode_text then + return nil + end + table.insert(argnodes_text, argnode_text) + end + + return argnodes_text +end + +function Node:get_args() + return get_args(self, "get_text") +end +function Node:get_static_args() + return get_args(self, "get_static_text") +end + +function Node:get_jump_index() + return self.pos +end + +function Node:set_ext_opts(name) + -- differentiate, either visited or unvisited needs to be set. + if name == "passive" then + self.mark:update_opts(self:get_passive_ext_opts()) + else + self.mark:update_opts(self.ext_opts[name]) + end +end + +-- for insert,functionNode. +function Node:store() + self.static_text = self:get_text() +end + +function Node:update_restore() end + +-- find_node only needs to check children, self is checked by the parent. +function Node:find_node() + return nil +end + +Node.ext_gravities_active = { false, true } + +function Node:insert_to_node_absolute(position) + -- this node is a leaf, just return its position + return self.absolute_position +end + +function Node:set_dependents() end + +function Node:set_argnodes(dict) + if self.absolute_insert_position then + -- append+remove "node" from absolute_insert_position to quickly create + -- key for dict. + table.insert(self.absolute_insert_position, "node") + dict:set(self.absolute_insert_position, self) + self.absolute_insert_position[#self.absolute_insert_position] = nil + end + if self.key then + dict:set({ "key", self.key, "node" }, self) + end +end + +function Node:make_args_absolute() end + +function Node:resolve_position(position) + error( + string.format( + "invalid resolve_position(%d) on node at %s", + position, + vim.inspect(self.absolute_position) + ) + ) +end + +function Node:static_init() + self.static_visible = true +end + +-- resolve_*node*_ext_opts because snippet(Node)s have child_ext_opts, which +-- also have to be resolved. +-- This function generates a nodes ext_opts (those actually used in highlighting). +function Node:resolve_node_ext_opts(base_prio, parent_ext_opts) + if self.merge_node_ext_opts then + self.ext_opts = ext_util.extend( + vim.deepcopy(self.node_ext_opts), + parent_ext_opts or self.parent.effective_child_ext_opts[self.type] + ) + else + self.ext_opts = self.node_ext_opts + end + + ext_util.set_abs_prio( + self.ext_opts, + (base_prio or self.parent.ext_opts.base_prio) + + session.config.ext_prio_increase + ) +end + +function Node:is_interactive() + -- safe default. + return true +end + +-- initialize active-setting in dry_run-table for `self`. +function Node:init_dry_run_active(dry_run) + if dry_run and dry_run.active[self] == nil then + dry_run.active[self] = self.active + end +end +-- determine whether this node is currently active. +-- This is its own function (and not just a flat table-check) since we have to +-- check the data in the dry_run-table or the node, depending on `dry_run`. +function Node:is_active(dry_run) + return (not dry_run and self.active) or (dry_run and dry_run.active[self]) +end + +function Node:get_buf_position(opts) + opts = opts or {} + local raw = opts.raw ~= nil and opts.raw or true + + if raw then + return self.mark:pos_begin_end_raw() + else + return self.mark:pos_begin_end() + end +end + +-- only does something for insert- and snippetNode. +function Node:set_sibling_rgravs(_, _, _, _) end + +-- when an insertNode receives text, its mark/region should contain all the +-- text that is inserted. +-- This can be achieved by setting the left and right "right-gravity"(rgrav) of +-- the mark, which are responsible for controlling the direction an endpoint of +-- the mark is moved when text is inserted. +-- When a regular insertNode is focused/entered, we would like the left and +-- right rgrav to be false and true, respectively. Example: +-- this is an insertNodeAnd this is another insertNode +-- mark1: l r +-- mark2: l r +-- if `this is an insertNode` should be focused, we have to set the rgrav of +-- l1 false (because inserting text at the column of l1 should not shift l1 to +-- the right). Similarly, the rgrav of r1 has to be set true, text inserted at +-- its column SHOULD move it to the right. +-- Complicating this whole thing: if like above there is an adjacent +-- insertNode, its gravities have to be adjusted as well (if they are not, the +-- insertNodes regions would overlap, which is obviously confusing). So, when +-- adjusting some nodes rgravs, those of the siblings may have to be adjusted as well. +-- Another example: +-- aacc +-- mark1: l r +-- mark2: l +-- r +-- mark3: l r +-- (the insertNode for mark2 is not visible at all, l2 and r2 are in the same +-- column) +-- This example highlights that not only the immediate sibling might need +-- adjusting, but all siblings that share a mark-boundary with the node that +-- should be focused. +-- Even further complicating the matter: Snippets are trees, and failing to +-- set the rgrav of snippet adjacent to (sharing an endpoint with) the node we +-- want to focus, regardless of its position in the tree, will lead to extmarks +-- covering the wrong regions. +-- +-- More complications: focusing a node does not always mean setting the rgravs +-- such that text will end up inside the node! +-- For example, in the case of a terminating i(0) (like s("trig", {i(1, +-- "text"), t" ", i(0)})), we would like to NOT include the text entered into +-- it in the snippet. Thus, the gravities of it and all its parents have to be +-- set (in this case) false,false, if the i(0) were at the beginning of the +-- snippet (weird??) they'd have to be true,true. +-- +-- +-- Unfortunately, we cannot guarantee that two extmarks on the same position +-- also have the same gravities, for exmample if the text inside a focused node +-- is deleted, and then another unrelated node is focused, the two endpoints of +-- the previously focused node will have opposing rgravs. +-- Maybe this whole procedure could be sped up further if we can assume that +-- identical endpoints imply identical rgravs. +local function focus_node(self, lrgrav, rrgrav) + -- find nodes on path from self to root. + local nodes_path = node_util.root_path(self) + + -- direction is the direction away from this node, towards the outside of + -- the tree-representation of the snippet. + -- This is dubbed "direction" because it is the direction we will search in + -- to find nodes on one endpoint of self. + for _, direction in ipairs({ -1, 1 }) do + local self_direction_endpoint = self.mark:get_endpoint(direction) + local direction_rgrav = util.ternary(direction == -1, lrgrav, rrgrav) + local effective_direction_rgrav = direction_rgrav + + -- adjust left rgrav of all nodes on path upwards to root/snippet: + -- (i st. self and the snippet are both handled) + for i = 1, #nodes_path do + local node = nodes_path[i] + local node_direction_endpoint = node.mark:get_endpoint(direction) + + if + not util.pos_equal( + node_direction_endpoint, + self_direction_endpoint + ) + then + -- stop adjusting rgravs once self no longer is on the boundary of + -- its parents, or if the rgrav is already set correctly. + break + end + + node.mark:set_rgrav(direction, effective_direction_rgrav) + + -- Once self's snippet is reached on the root-path, we will only + -- adjust nodes self should be completely contained inside. + -- Since the rgravs, however, may be set up otherwise (for example + -- when focusing on an $0 that is the last node of the snippet), we + -- have to adjust them now. + if node.snippet == node then + effective_direction_rgrav = direction == 1 + end + + -- can't use node.parent, since that might skip nodes (in the case of + -- dynamicNode, for example, the generated snippets parent is not the + -- dynamicNode, but its parent). + -- also: don't need to check for nil, because the + local node_above = nodes_path[i + 1] + if node_above then + node_above:set_sibling_rgravs( + node, + self_direction_endpoint, + direction, + effective_direction_rgrav + ) + end + end + self:subtree_set_pos_rgrav( + self_direction_endpoint, + -direction, + direction_rgrav + ) + end +end + +function Node:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) +end + +function Node:subtree_set_pos_rgrav(_, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) +end + +function Node:focus() + focus_node(self, false, true) +end + +function Node:set_text(text) + self:focus() + + local node_from, node_to = self.mark:pos_begin_end_raw() + local ok = pcall( + vim.api.nvim_buf_set_text, + 0, + node_from[1], + node_from[2], + node_to[1], + node_to[2], + text + ) + -- we can assume that (part of) the snippet was deleted; remove it from + -- the jumplist. + if not ok then + error("[LuaSnip Failed]: " .. vim.inspect(text)) + end +end + +-- since parents validate the adjacency, nodes where we don't know anything +-- about the text inside them just have to assume they haven't been deleted :D +function Node:extmarks_valid() + return true +end + +function Node:linkable() + -- linkable if insert or exitNode. + return vim.tbl_contains( + { types.insertNode, types.exitNode }, + rawget(self, "type") + ) +end +function Node:interactive() + -- interactive if immediately inside choiceNode. + return vim.tbl_contains( + { types.insertNode, types.exitNode }, + rawget(self, "type") + ) or rawget(self, "choice") ~= nil +end +function Node:leaf() + return vim.tbl_contains( + { types.textNode, types.functionNode, types.insertNode, types.exitNode }, + rawget(self, "type") + ) +end + +return { + Node = Node, + focus_node = focus_node, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/restoreNode.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/restoreNode.lua new file mode 100644 index 00000000..48c8448a --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/restoreNode.lua @@ -0,0 +1,307 @@ +-- restoreNode is implemented similarly to dynamicNode, only that it gets the snippetNode not from some function, but from self.snip.stored[key]. + +local Node = require("luasnip.nodes.node").Node +local wrap_nodes_in_snippetNode = + require("luasnip.nodes.snippet").wrap_nodes_in_snippetNode +local RestoreNode = Node:new() +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local util = require("luasnip.util.util") +local node_util = require("luasnip.nodes.util") +local mark = require("luasnip.util.mark").mark +local extend_decorator = require("luasnip.util.extend_decorator") + +local function R(pos, key, nodes, opts) + -- don't create nested snippetNodes, unnecessary. + nodes = nodes and wrap_nodes_in_snippetNode(nodes) + + return RestoreNode:new({ + pos = pos, + key = key, + mark = nil, + snip = nodes, + type = types.restoreNode, + dependents = {}, + -- TODO: find out why it's necessary only for this node. + active = false, + }, opts) +end +extend_decorator.register(R, { arg_indx = 4 }) + +function RestoreNode:exit() + if not self.visible then + -- already exited. + return + end + + self.visible = false + self.mark:clear() + -- snip should exist if exit is called. + self.snip:store() + -- will be copied on restore, no need to copy here too. + self.parent.snippet.stored[self.key] = self.snip + self.snip:exit() + self.snip = nil + self.active = false +end + +function RestoreNode:input_enter(_, dry_run) + if dry_run then + dry_run.active[self] = true + return + end + + self.active = true + self.visited = true + self.mark:update_opts(self.ext_opts.active) + + self:event(events.enter) +end + +function RestoreNode:input_leave(_, dry_run) + if dry_run then + dry_run.active[self] = false + return + end + + self:event(events.leave) + + self:update_dependents() + self.active = false + + self.mark:update_opts(self:get_passive_ext_opts()) +end + +-- set snippetNode for this key here. +function RestoreNode:subsnip_init() + -- don't overwrite potentially stored snippetNode. + -- due to metatable, there will always be a node set, but only those set + -- by it (should) have the is_default set to true. + if self.parent.snippet.stored[self.key].is_default and self.snip then + self.parent.snippet.stored[self.key] = self.snip + end +end + +-- don't need these, will be done in put_initial and get_static/docstring. +function RestoreNode:indent(_) end + +function RestoreNode:expand_tabs(_) end + +-- will be called when before expansion but after snip.parent was initialized. +-- Get the actual snippetNode here. +function RestoreNode:put_initial(pos) + local tmp = self.parent.snippet.stored[self.key] + + -- act as if snip is directly inside parent. + tmp.parent = self.parent + tmp.indx = self.indx + + tmp.next = self + tmp.prev = self + + tmp.snippet = self.parent.snippet + + tmp.restore_node = self + tmp.update_dependents = function(node) + node:_update_dependents() + -- self is restoreNode. + node.restore_node:update_dependents() + end + + tmp:resolve_child_ext_opts() + tmp:resolve_node_ext_opts() + tmp:subsnip_init() + + tmp:init_positions(self.snip_absolute_position) + tmp:init_insert_positions(self.snip_absolute_insert_position) + + tmp:make_args_absolute() + + tmp:set_dependents() + tmp:set_argnodes(self.parent.snippet.dependents_dict) + + if vim.bo.expandtab then + tmp:expand_tabs(util.tab_width(), self.parent.indentstring) + end + + -- correctly set extmark for node. + -- does not modify ext_opts[node.type]. + local mark_opts = vim.tbl_extend("keep", { + right_gravity = false, + end_right_gravity = false, + }, tmp:get_passive_ext_opts()) + + local old_pos = vim.deepcopy(pos) + tmp:put_initial(pos) + tmp.mark = mark(old_pos, pos, mark_opts) + + -- no need to call update here, will be done by function calling this + -- function. + + self.snip = tmp + self.visible = true +end + +-- the same as DynamicNode. +function RestoreNode:jump_into(dir, no_move, dry_run) + self:init_dry_run_active(dry_run) + + if self:is_active(dry_run) then + self:input_leave(no_move, dry_run) + + if dir == 1 then + return self.next:jump_into(dir, no_move, dry_run) + else + return self.prev:jump_into(dir, no_move, dry_run) + end + else + self:input_enter(no_move, dry_run) + + return self.snip:jump_into(dir, no_move, dry_run) + end +end + +function RestoreNode:set_ext_opts(name) + Node.set_ext_opts(self, name) + + self.snip:set_ext_opts(name) +end + +function RestoreNode:update() + self.snip:update() +end + +function RestoreNode:update_static() + -- *_static-methods can use the stored snippet, since they don't require + -- the snip to actually be inside the restoreNode. + self.parent.snippet.stored[self.key]:update_static() +end + +local function snip_init(self, snip) + snip.parent = self.parent + + snip.snippet = self.parent.snippet + -- pos should be nil if the restoreNode is inside a choiceNode. + snip.pos = rawget(self, "pos") + + snip:resolve_child_ext_opts() + snip:resolve_node_ext_opts() + snip:subsnip_init() + + snip:init_positions(self.snip_absolute_position) + snip:init_insert_positions(self.snip_absolute_insert_position) + + snip:make_args_absolute() + + snip:set_dependents() + snip:set_argnodes(self.parent.snippet.dependents_dict) + + snip:static_init() +end + +function RestoreNode:static_init() + Node.static_init(self) + self.snip = self.parent.snippet.stored[self.key] + snip_init(self, self.snip) +end + +function RestoreNode:get_static_text() + -- cache static_text, no need to recalculate function. + if not self.static_text then + self.static_text = + self.parent.snippet.stored[self.key]:get_static_text() + end + return self.static_text +end + +function RestoreNode:get_docstring() + if not self.docstring then + self.docstring = self.parent.snippet.stored[self.key]:get_docstring() + end + return self.docstring +end + +function RestoreNode:store() end + +-- will be restored through other means. +function RestoreNode:update_restore() + self.snip:update_restore() +end + +function RestoreNode:find_node(predicate) + if self.snip then + if predicate(self.snip) then + return self.snip + else + return self.snip:find_node(predicate) + end + end + return nil +end + +function RestoreNode:insert_to_node_absolute(position) + if #position == 0 then + return self.absolute_position + end + -- nil if not yet available. + return self.snip and self.snip:insert_to_node_absolute(position) +end + +function RestoreNode:update_all_dependents() + self:_update_dependents() + self.snip:update_all_dependents() +end + +function RestoreNode:update_all_dependents_static() + self:_update_dependents_static() + self.parent.snippet.stored[self.key]:_update_dependents_static() +end + +function RestoreNode:init_insert_positions(position_so_far) + Node.init_insert_positions(self, position_so_far) + self.snip_absolute_insert_position = + vim.deepcopy(self.absolute_insert_position) + -- nodes of current snippet should have a 0 before. + self.snip_absolute_insert_position[#self.snip_absolute_insert_position + 1] = + 0 +end + +function RestoreNode:init_positions(position_so_far) + Node.init_positions(self, position_so_far) + self.snip_absolute_position = vim.deepcopy(self.absolute_position) + -- Reach current snippet as snip_absolute_position..0. + self.snip_absolute_position[#self.snip_absolute_position + 1] = 0 +end + +function RestoreNode:resolve_position(position) + -- position must be 0, there are no other options. + return self.snip +end + +function RestoreNode:is_interactive() + -- shouldn't be called, but revisit this once is_interactive is used in + -- places other than lsp-snippets. + return true +end + +function RestoreNode:subtree_set_pos_rgrav(pos, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) + if self.snip then + self.snip:subtree_set_pos_rgrav(pos, direction, rgrav) + end +end + +function RestoreNode:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) + if self.snip then + self.snip:subtree_set_rgrav(rgrav) + end +end + +function RestoreNode:extmarks_valid() + return node_util.generic_extmarks_valid(self, self.snip) +end + +return { + R = R, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/snippet.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/snippet.lua new file mode 100644 index 00000000..a0be2fb9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/snippet.lua @@ -0,0 +1,1572 @@ +local node_mod = require("luasnip.nodes.node") +local iNode = require("luasnip.nodes.insertNode") +local tNode = require("luasnip.nodes.textNode") +local util = require("luasnip.util.util") +local ext_util = require("luasnip.util.ext_opts") +local node_util = require("luasnip.nodes.util") +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local mark = require("luasnip.util.mark").mark +local Environ = require("luasnip.util.environ") +local session = require("luasnip.session") +local pattern_tokenizer = require("luasnip.util.pattern_tokenizer") +local dict = require("luasnip.util.dict") +local snippet_collection = require("luasnip.session.snippet_collection") +local extend_decorator = require("luasnip.util.extend_decorator") +local source = require("luasnip.session.snippet_collection.source") +local loader_util = require("luasnip.loaders.util") +local trig_engines = require("luasnip.nodes.util.trig_engines") + +local true_func = function() + return true +end + +local generate_resolve_expand_params_func = function(condition, user_resolve) + return function(self, line_to_cursor, match, captures) + if condition then + if not condition(line_to_cursor, match, captures) then + return nil + end + end + + local default_expand_params = { + trigger = match, + captures = captures, + } + + if user_resolve then + local res = user_resolve(self, line_to_cursor, match, captures) + if res == nil then + return nil + end + return vim.tbl_extend("force", default_expand_params, res) + else + return default_expand_params + end + end +end + +local callbacks_mt = { + __index = function(table, key) + rawset(table, key, {}) + return {} + end, +} + +-- declare SN here, is needed in metatable. +local SN + +local stored_mt = { + __index = function(table, key) + -- default-node is just empty text. + local val = SN(nil, { iNode.I(1) }) + val.is_default = true + rawset(table, key, val) + return val + end, +} + +local Snippet = node_mod.Node:new() + +local Parent_indexer = {} + +function Parent_indexer:new(o) + setmetatable(o, self) + self.__index = self + return o +end + +-- Returns referred node from parent (or parents' parent). +function Parent_indexer:resolve(snippet) + -- recurse if index is a parent_indexer + if getmetatable(self.indx) == Parent_indexer then + return self.indx:resolve(snippet.parent) + else + return snippet.parent.insert_nodes[self.indx] + end +end + +local function P(indx) + return Parent_indexer:new({ indx = indx }) +end + +function Snippet:init_nodes() + local insert_nodes = {} + for i, node in ipairs(self.nodes) do + node.parent = self + node.indx = i + if + node.type == types.insertNode + or node.type == types.exitNode + or node.type == types.snippetNode + or node.type == types.choiceNode + or node.type == types.dynamicNode + or node.type == types.restoreNode + then + if node.pos then + insert_nodes[node.pos] = node + end + end + + node.update_dependents = function(node) + node:_update_dependents() + node.parent:update_dependents() + end + end + + if insert_nodes[1] then + insert_nodes[1].prev = self + for i = 2, #insert_nodes do + insert_nodes[i].prev = insert_nodes[i - 1] + insert_nodes[i - 1].next = insert_nodes[i] + end + insert_nodes[#insert_nodes].next = self + + self.inner_first = insert_nodes[1] + self.inner_last = insert_nodes[#insert_nodes] + else + self.inner_first = self + self.inner_last = self + end + + self.insert_nodes = insert_nodes +end + +local function wrap_nodes_in_snippetNode(nodes) + if getmetatable(nodes) then + -- is a node, not a table. + if nodes.type ~= types.snippetNode then + -- is not a snippetNode. + + -- pos might have been nil, just set it correctly here. + nodes.pos = 1 + return SN(nil, { nodes }) + else + -- is a snippetNode, wrapping it twice is unnecessary. + return nodes + end + else + -- is a table of nodes. + return SN(nil, nodes) + end +end + +local function init_snippetNode_opts(opts) + local in_node = {} + + in_node.child_ext_opts = + ext_util.child_complete(vim.deepcopy(opts.child_ext_opts or {})) + + if opts.merge_child_ext_opts == nil then + in_node.merge_child_ext_opts = true + else + in_node.merge_child_ext_opts = opts.merge_child_ext_opts + end + + in_node.callbacks = opts.callbacks or {} + -- return empty table for non-specified callbacks. + setmetatable(in_node.callbacks, callbacks_mt) + + return in_node +end + +local function init_snippet_opts(opts) + local in_node = {} + + -- return sn(t("")) for so-far-undefined keys. + in_node.stored = setmetatable(opts.stored or {}, stored_mt) + + -- wrap non-snippetNode in snippetNode. + for key, nodes in pairs(in_node.stored) do + in_node.stored[key] = wrap_nodes_in_snippetNode(nodes) + end + + return vim.tbl_extend("error", in_node, init_snippetNode_opts(opts)) +end + +-- context, opts non-nil tables. +local function init_snippet_context(context, opts) + local effective_context = {} + + -- trig is set by user, trigger is used internally. + -- not worth a breaking change, we just make it compatible here. + effective_context.trigger = context.trig + + effective_context.name = context.name or context.trig + + -- context.{desc,dscr} could be nil, string or table. + -- (defaults to trigger) + effective_context.description = + util.to_line_table(context.desc or context.dscr or context.trig) + -- (keep dscr to avoid breaking downstream usages) + effective_context.dscr = effective_context.description + + -- might be nil, but whitelisted in snippetProxy. + effective_context.priority = context.priority + + -- might be nil, but whitelisted in snippetProxy. + -- shall be a string, allowed values: "snippet", "autosnippet" + -- stylua: ignore + assert( + context.snippetType == nil + or context.snippetType == "snippet" + or context.snippetType == "autosnippet", + "snippetType has to be either 'snippet' or 'autosnippet' (or unset)" + ) + -- switch to plural forms so that we can use this for indexing + -- stylua: ignore + effective_context.snippetType = + context.snippetType == "autosnippet" and "autosnippets" + or context.snippetType == "snippet" and "snippets" + or nil + + -- may be nil. + effective_context.filetype = context.filetype + + -- maybe do this in a better way when we have more parameters, but this is + -- fine for now: + + -- not a necessary argument. + if context.docstring ~= nil then + effective_context.docstring = util.to_line_table(context.docstring) + end + + -- can't use `cond and ... or ...` since we have truthy values. + effective_context.wordTrig = + util.ternary(context.wordTrig ~= nil, context.wordTrig, true) + effective_context.hidden = + util.ternary(context.hidden ~= nil, context.hidden, false) + + effective_context.regTrig = + util.ternary(context.regTrig ~= nil, context.regTrig, false) + + effective_context.docTrig = context.docTrig + local engine + if type(context.trigEngine) == "function" then + -- if trigEngine is function, just use that. + engine = context.trigEngine + else + -- otherwise, it is nil or string, if it is string, that is the name, + -- otherwise use "pattern" if regTrig is set, and finally fall back to + -- "plain" if it is not. + local engine_name = util.ternary( + context.trigEngine ~= nil, + context.trigEngine, + util.ternary(context.regTrig ~= nil, "pattern", "plain") + ) + engine = trig_engines[engine_name] + end + -- make sure to pass through nil-trigEngineOpts, they will be recognized and + -- we will get a default-version of that function instead of generating a + -- curried (?) version of it (which would waste space I think). + effective_context.trig_matcher = + engine(effective_context.trigger, context.trigEngineOpts) + + effective_context.resolveExpandParams = generate_resolve_expand_params_func( + context.condition or opts.condition, + context.resolveExpandParams + ) + effective_context.show_condition = context.show_condition + or opts.show_condition + or true_func + + -- init invalidated here. + -- This is because invalidated is a key that can be populated without any + -- information on the actual snippet (it can be used by snippetProxy!) and + -- it should be also available to the snippet-representations in the + -- snippet-list, and not in the expanded snippet, as doing this in + -- `init_snippet_opts` would suggest. + effective_context.invalidated = false + + return effective_context +end + +-- Create snippet without initializing opts+context. +-- this might be called from snippetProxy. +local function _S(snip, nodes, opts) + nodes = util.wrap_nodes(nodes) + -- tbl_extend creates a new table! Important with Proxy, metatable of snip + -- will be changed later. + snip = Snippet:new( + vim.tbl_extend("error", snip, { + nodes = nodes, + insert_nodes = {}, + current_insert = 0, + mark = nil, + dependents = {}, + active = false, + type = types.snippet, + -- dependents_dict is responsible for associating + -- function/dynamicNodes ("dependents") with their argnodes. + -- There are a few important requirements that have to be + -- fulfilled: + -- Allow associating present dependent with non-present argnode + -- (and vice-versa). + -- This is required, because a node outside some dynamicNode + -- could depend on a node inside it, and since the content of a + -- dynamicNode changes, it is possible that the argnode will be + -- generated. + -- As soon as that happens, it should be possible to immediately + -- find the dependents that depend on the newly-generated argnode, + -- without searching the snippet. + -- + -- The dependents_dict enables all of this by storing every node + -- which is addressable by either `absolute_indexer` or + -- `key_indexer` (or even directly, just with its own + -- table, ie. `self`) under its path. + -- * `absolute_indexer`: the path is the sequence of jump_indices + -- which leads to this node, for example {1,3,1}. + -- * `key_indexer`: the path is {"key", }. + -- * `node`: the path is {node}. + -- With each type of node-reference (absolute_indexer, key, node), + -- the node which is referenced by it, is stored under path .. + -- {"node"} (if it exists inside the current snippet!!), while the + -- dependents are stored at path .. {"dependents"}. + -- The manner in which the dependents are stored is also + -- interesting: + -- They are not stored in eg a list, since we would then have to + -- deal with explicitly invalidating them (remove them from the + -- list to prevent its growing too large). No, the dependents are + -- stored under their own absolute position (not absolute _insert_ + -- position, functionNodes don't have a jump-index, and thus can't + -- be addressed using absolute insert position), which means that + -- + -- a) once a dependent is re-generated, for example by a + -- dynamicNode, it will not take up new space, but simply overwrite + -- the old one (which is very desirable!!) + -- b) we will still store some older, unnecessary dependents + -- + -- (imo) a outweighs b, which is why this design was chosen. + -- (non-visible nodes are ignored by tracking their visibility in + -- the snippet separately, it is then queried in eg. + -- `update_dependents`) + -- + -- Related functions: + -- * `dependent:set_dependents` to insert argnode+dependent in + -- `dependents_dict`, in the according to the above description. + -- * `set_argnodes` to insert the absolute_insert_position .. + -- {"node"} into dependents_dict. + -- * `get_args` to get the text of the argnodes to some dependent + -- node. + -- * `update_dependents` can be called to find all dependents, and + -- update the visible ones. + dependents_dict = dict.new(), + + -- list of snippets expanded within the region of this snippet. + -- sorted by their buffer-position, for quick searching. + child_snippets = {}, + }), + opts + ) + + -- is propagated to all subsnippets, used to quickly find the outer snippet + snip.snippet = snip + + -- if the snippet is expanded inside another snippet (can be recognized by + -- non-nil parent_node), the node of the snippet this one is inside has to + -- update its dependents. + function snip:_update_dependents() + if self.parent_node then + self.parent_node:update_dependents() + end + end + snip.update_dependents = snip._update_dependents + + snip:init_nodes() + + if not snip.insert_nodes[0] then + -- Generate implied i(0) + local i0 = iNode.I(0) + local i0_indx = #nodes + 1 + i0.parent = snip + i0.indx = i0_indx + snip.insert_nodes[0] = i0 + snip.nodes[i0_indx] = i0 + end + + return snip +end + +local function S(context, nodes, opts) + opts = opts or {} + + local snip = init_snippet_context(node_util.wrap_context(context), opts) + snip = vim.tbl_extend("error", snip, init_snippet_opts(opts)) + + snip = _S(snip, nodes, opts) + + if __luasnip_get_loaded_file_frame_debuginfo ~= nil then + -- this snippet is being lua-loaded, and the source should be recorded. + snip._source = + source.from_debuginfo(__luasnip_get_loaded_file_frame_debuginfo()) + end + + return snip +end +extend_decorator.register( + S, + { arg_indx = 1, extend = node_util.snippet_extend_context }, + { arg_indx = 3 } +) + +function SN(pos, nodes, opts) + opts = opts or {} + + local snip = Snippet:new( + vim.tbl_extend("error", { + pos = pos, + nodes = util.wrap_nodes(nodes), + insert_nodes = {}, + current_insert = 0, + mark = nil, + dependents = {}, + active = false, + type = types.snippetNode, + }, init_snippetNode_opts(opts)), + opts + ) + snip:init_nodes() + + return snip +end +extend_decorator.register(SN, { arg_indx = 3 }) + +local function ISN(pos, nodes, indent_text, opts) + local snip = SN(pos, nodes, opts) + + local function get_indent(parent_indent) + local indentstring = "" + if vim.bo.expandtab then + -- preserve content of $PARENT_INDENT, but expand tabs before/after it + for str in vim.gsplit(indent_text, "$PARENT_INDENT", true) do + -- append expanded text and parent_indent, we'll remove the superfluous one after the loop. + indentstring = indentstring + .. util.expand_tabs( + { str }, + util.tab_width(), + #indentstring + #parent_indent + )[1] + .. parent_indent + end + indentstring = indentstring:sub(1, -#parent_indent - 1) + else + indentstring = indent_text:gsub("$PARENT_INDENT", parent_indent) + end + + return indentstring + end + + function snip:indent(parent_indent) + Snippet.indent(self, get_indent(parent_indent)) + end + + -- expand_tabs also needs to be modified: the children of the isn get the + -- indent of the isn, so we'll have to calculate it now. + -- This is done with a dummy-indentstring of the correct length. + function snip:expand_tabs(tabwidth, indentstrlen) + Snippet.expand_tabs( + self, + tabwidth, + #get_indent(string.rep(" ", indentstrlen)) + ) + end + + return snip +end +extend_decorator.register(ISN, { arg_indx = 4 }) + +function Snippet:remove_from_jumplist() + if not self.visible then + -- snippet not visible => already removed. + -- Don't remove it twice. + return + end + + -- prev is i(-1)(startNode), prev of that is the outer/previous snippet. + -- pre is $0 or insertNode. + local pre = self.prev.prev + -- similar for next, self.next is the i(0). + -- nxt is snippet. + local nxt = self.next.next + + self:exit() + + local sibling_list = self.parent_node ~= nil + and self.parent_node.parent.snippet.child_snippets + or session.snippet_roots[vim.api.nvim_get_current_buf()] + local self_indx + for i, snip in ipairs(sibling_list) do + if snip == self then + self_indx = i + end + end + table.remove(sibling_list, self_indx) + + -- previous snippet jumps to this one => redirect to jump to next one. + if pre then + if pre.inner_first == self then + if pre == nxt then + pre.inner_first = nil + else + pre.inner_first = nxt + end + elseif pre.next == self then + pre.next = nxt + end + end + if nxt then + if nxt.inner_last == self.next then + if pre == nxt then + nxt.inner_last = nil + else + nxt.inner_last = pre + end + -- careful here!! nxt.prev is its start_node, nxt.prev.prev is this + -- snippet. + elseif nxt.prev.prev == self.next then + nxt.prev.prev = pre + end + end +end + +local function insert_into_jumplist( + snippet, + start_node, + current_node, + parent_node, + sibling_snippets, + own_indx +) + local prev_snippet = sibling_snippets[own_indx - 1] + -- have not yet inserted self!! + local next_snippet = sibling_snippets[own_indx] + + -- only consider sibling-snippets with the same parent-node as + -- previous/next snippet for linking-purposes. + -- They are siblings because they are expanded in the same snippet, not + -- because they have the same parent_node. + local prev, next + if prev_snippet ~= nil and prev_snippet.parent_node == parent_node then + prev = prev_snippet + end + if next_snippet ~= nil and next_snippet.parent_node == parent_node then + next = next_snippet + end + + -- whether roots should be linked together. + local link_roots = session.config.link_roots + + -- whether children of the same snippet should be linked to their parent + -- and eachother. + local link_children = session.config.link_children + + if parent_node then + if node_util.linkable_node(parent_node) then + -- snippetNode (which has to be empty to be viable here) and + -- insertNode can both deal with inserting a snippet inside them + -- (ie. hooking it up st. it can be visited after jumping back to + -- the snippet of parent). + -- in all cases + if link_children and prev ~= nil then + -- if we have a previous snippet we can link to, just do that. + prev.next.next = snippet + start_node.prev = prev.insert_nodes[0] + else + -- only jump from parent to child if link_children is set. + if link_children then + -- prev is nil, but we can link up using the parent. + parent_node.inner_first = snippet + end + -- make sure we can jump back to the parent. + start_node.prev = parent_node + end + + -- exact same reasoning here as in prev-case above, omitting comments. + if link_children and next ~= nil then + -- jump from next snippets start_node to $0. + next.prev.prev = snippet.insert_nodes[0] + -- jump from $0 to next snippet (skip its start_node) + snippet.insert_nodes[0].next = next + else + if link_children then + parent_node.inner_last = snippet.insert_nodes[0] + end + snippet.insert_nodes[0].next = parent_node + end + else + -- naively, even if the parent is linkable, there might be snippets + -- before/after that share the same parent, so we could + -- theoretically link up with them. + -- This, however, can cause cyclic jumps, for example if the + -- previous child-snippet contains the current node: we will jump + -- from the end of the new snippet into the previous child-snippet, + -- and from its last node into the new snippet. + -- Since cycles should be avoided (very weird if the jumps just go + -- in a circle), we have no choice but to fall back to this + -- old-style linkage. + + -- Don't jump from current_node to this snippet (I feel + -- like that should be good: one can still get back to ones + -- previous history, and we don't mess up whatever jumps + -- are set up around current_node) + start_node.prev = current_node + snippet.insert_nodes[0].next = current_node + end + -- don't link different root-nodes for unlinked_roots. + elseif link_roots then + -- inserted into top-level snippet-forest, just hook up with prev, next. + -- prev and next have to be snippets or nil, in this case. + if prev ~= nil then + prev.next.next = snippet + start_node.prev = prev.insert_nodes[0] + end + if next ~= nil then + snippet.insert_nodes[0].next = next + next.prev.prev = snippet.insert_nodes[0] + end + end + + table.insert(sibling_snippets, own_indx, snippet) +end + +function Snippet:trigger_expand(current_node, pos_id, env, indent_nodes) + local pos = vim.api.nvim_buf_get_extmark_by_id(0, session.ns_id, pos_id, {}) + + -- find tree-node the snippet should be inserted at (could be before another node). + local _, sibling_snippets, own_indx, parent_node = + node_util.snippettree_find_undamaged_node(pos, { + tree_respect_rgravs = false, + tree_preference = node_util.binarysearch_preference.outside, + snippet_mode = "linkable", + }) + local n_siblings_pre = #sibling_snippets + + if current_node then + if parent_node then + if node_util.linkable_node(parent_node) then + node_util.refocus(current_node, parent_node) + parent_node:input_enter_children() + else + -- enter extmarks of parent_node, but don't enter it + -- "logically", it will not be the parent of the snippet. + parent_node:focus() + -- enter current node, it will contain the new snippet. + current_node:input_enter_children() + end + else + -- if no parent_node, completely leave. + node_util.refocus(current_node, nil) + + -- in this branch, it may happen that the snippet we leave is + -- invalid and removed from the snippet-list during `refocus`. + -- This is not catastrophic, but we have to recognize it here, and + -- update the `own_indx` among the snippet-roots (one was deleted, + -- the computed index is no longer valid since there may have been + -- a shift down over `own_indx`) + if n_siblings_pre ~= #sibling_snippets then + -- only own_indx can change, since the text in the buffer is + -- unchanged, while the number of roots is. + _, _, own_indx, _ = + node_util.snippettree_find_undamaged_node(pos, { + tree_respect_rgravs = false, + tree_preference = node_util.binarysearch_preference.outside, + snippet_mode = "linkable", + }) + end + end + + -- There may be other snippets inside of this parent_node/on this level + -- of the snippet-tree whose extmarks have to be adjusted s.t. they + -- don't contain the text that will be inserted during put_initial. + -- Node's/snippet's extmarks that are outside of this parent_node/not + -- siblings of this node will be adjusted during the refocus above, if + -- applicable. + -- + -- The following adjustments may do too much, but there's no issue + -- with that, and we're on the safe side. + + -- set rgrav false for snippets/nodes where the right boundary + -- coincides with the position we insert at now... + for i = 1, own_indx - 1 do + sibling_snippets[i]:subtree_set_pos_rgrav(pos, -1, false) + end + -- set rgrav true for snippets/nodes where the left boundary + -- coincides with the position we insert at now... + for i = own_indx, #sibling_snippets do + sibling_snippets[i]:subtree_set_pos_rgrav(pos, 1, true) + end + end + + local pre_expand_res = self:event(events.pre_expand, { expand_pos = pos }) + or {} + -- update pos, event-callback might have moved the extmark. + pos = vim.api.nvim_buf_get_extmark_by_id(0, session.ns_id, pos_id, {}) + + Environ:override(env, pre_expand_res.env_override or {}) + + if indent_nodes then + local indentstring = util.line_chars_before(pos):match("^%s*") + -- expand tabs before indenting to keep indentstring unmodified + if vim.bo.expandtab then + self:expand_tabs(util.tab_width(), #indentstring) + end + self:indent(indentstring) + end + + -- (possibly) keep user-set opts. + if self.merge_child_ext_opts then + self.effective_child_ext_opts = ext_util.child_extend( + vim.deepcopy(self.child_ext_opts), + session.config.ext_opts + ) + else + self.effective_child_ext_opts = vim.deepcopy(self.child_ext_opts) + end + + local parent_ext_base_prio + -- if inside another snippet, increase priority accordingly. + -- parent_node is only set if this snippet is expanded inside another one. + if parent_node then + parent_ext_base_prio = parent_node.parent.ext_opts.base_prio + else + parent_ext_base_prio = session.config.ext_base_prio + end + + -- own highlight comes from self.child_ext_opts.snippet. + self:resolve_node_ext_opts( + parent_ext_base_prio, + self.effective_child_ext_opts[self.type] + ) + + self.env = env + self:subsnip_init() + + self:init_positions({}) + self:init_insert_positions({}) + + self:make_args_absolute() + + self:set_dependents() + self:set_argnodes(self.dependents_dict) + + -- at this point `stored` contains the snippetNodes that will actually + -- be used, indent them once here. + for _, node in pairs(self.stored) do + node:indent(self.indentstr) + end + + local start_node = iNode.I(0) + + local old_pos = vim.deepcopy(pos) + self:put_initial(pos) + + local mark_opts = vim.tbl_extend("keep", { + right_gravity = false, + end_right_gravity = false, + }, self:get_passive_ext_opts()) + self.mark = mark(old_pos, pos, mark_opts) + + self:update() + self:update_all_dependents() + + -- Marks should stay at the beginning of the snippet, only the first mark is needed. + start_node.mark = self.nodes[1].mark + start_node.pos = -1 + -- needed for querying node-path from snippet to this node. + start_node.absolute_position = { -1 } + start_node.parent = self + + -- hook up i0 and start_node, and then the snippet itself. + -- they are outside, not inside the snippet. + -- This should clearly be the case for start_node, but also for $0 since + -- jumping to $0 should make/mark the snippet non-active (for example via + -- extmarks) + start_node.next = self + self.prev = start_node + self.insert_nodes[0].prev = self + self.next = self.insert_nodes[0] + + -- parent_node is nil if the snippet is toplevel. + self.parent_node = parent_node + + insert_into_jumplist( + self, + start_node, + current_node, + parent_node, + sibling_snippets, + own_indx + ) + + return parent_node +end + +-- returns copy of snip if it matches, nil if not. +function Snippet:matches(line_to_cursor) + local match, captures = self.trig_matcher(line_to_cursor, self.trigger) + + -- Trigger or regex didn't match. + if not match then + return nil + end + + local expand_params = + self:resolveExpandParams(line_to_cursor, match, captures) + if not expand_params then + return nil + end + + local from = #line_to_cursor - #match + 1 + + -- if wordTrig is set, the char before the trigger can't be \w or the + -- word has to start at the beginning of the line. + if + self.wordTrig + and not ( + from == 1 + or string.match( + string.sub(line_to_cursor, from - 1, from - 1), + "[%w_]" + ) + == nil + ) + then + return nil + end + + return expand_params +end + +-- https://gist.github.com/tylerneylon/81333721109155b2d244 +local function copy3(obj, seen) + -- Handle non-tables and previously-seen tables. + if type(obj) ~= "table" then + return obj + end + if seen and seen[obj] then + return seen[obj] + end + + -- New table; mark it as seen an copy recursively. + local s = seen or {} + local res = {} + s[obj] = res + for k, v in next, obj do + res[copy3(k, s)] = copy3(v, s) + end + return setmetatable(res, getmetatable(obj)) +end + +function Snippet:copy() + return copy3(self) +end + +function Snippet:del_marks() + for _, node in ipairs(self.nodes) do + vim.api.nvim_buf_del_extmark(0, session.ns_id, node.mark.id) + end +end + +function Snippet:is_interactive(info) + for _, node in ipairs(self.nodes) do + -- return true if any node depends on another node or is an insertNode. + if node:is_interactive(info) then + return true + end + end + return false +end + +function Snippet:dump() + for i, node in ipairs(self.nodes) do + print(i) + print(node.mark.opts.right_gravity, node.mark.opts.end_right_gravity) + local from, to = node.mark:pos_begin_end() + print(from[1], from[2]) + print(to[1], to[2]) + end +end + +function Snippet:put_initial(pos) + for _, node in ipairs(self.nodes) do + -- save pos to compare to later. + local old_pos = vim.deepcopy(pos) + node:put_initial(pos) + + -- correctly set extmark for node. + -- does not modify ext_opts[node.type]. + local mark_opts = vim.tbl_extend("keep", { + right_gravity = false, + end_right_gravity = false, + }, node:get_passive_ext_opts()) + node.mark = mark(old_pos, pos, mark_opts) + end + self.visible = true +end + +-- populate env,inden,captures,trigger(regex),... but don't put any text. +-- the env may be passed in opts via opts.env, if none is passed a new one is +-- generated. +function Snippet:fake_expand(opts) + if not opts then + opts = {} + end + -- set eg. env.TM_SELECTED_TEXT to $TM_SELECTED_TEXT + if opts.env then + self.env = opts.env + else + self.env = Environ.fake() + end + + self.captures = {} + setmetatable(self.captures, { + __index = function(_, key) + return "$CAPTURE" .. tostring(key) + end, + }) + if self.docTrig then + -- use docTrig as entire line up to cursor, this assumes that it + -- actually matches the trigger. + local _ + _, self.captures = self.trig_matcher(self.docTrig, self.trigger) + self.trigger = self.docTrig + else + self.trigger = "$TRIGGER" + end + self.ext_opts = vim.deepcopy(session.config.ext_opts) + + self:indent("") + + -- ext_opts don't matter here, just use convenient values. + self.effective_child_ext_opts = self.child_ext_opts + self.ext_opts = self.node_ext_opts + + self:subsnip_init() + + self:init_positions({}) + self:init_insert_positions({}) + + self:make_args_absolute() + + self:set_dependents() + self:set_argnodes(self.dependents_dict) + + self:static_init() + + -- no need for update_dependents_static, update_static alone will cause updates for all child-nodes. + self:update_static() +end + +-- to work correctly, this may require that the snippets' env,indent,captures? are +-- set. +function Snippet:get_static_text() + if self.static_text then + return self.static_text + -- copy+fake_expand the snippet here instead of in whatever code needs to know the docstring. + elseif not self.ext_opts then + -- not a snippetNode and not yet initialized + local snipcop = self:copy() + -- sets env, captures, etc. + snipcop:fake_expand() + local static_text = snipcop:get_static_text() + self.static_text = static_text + return static_text + end + + if not self.static_visible then + return nil + end + local text = { "" } + for _, node in ipairs(self.nodes) do + local node_text = node:get_static_text() + -- append first line to last line of text so far. + text[#text] = text[#text] .. node_text[1] + for i = 2, #node_text do + text[#text + 1] = node_text[i] + end + end + -- cache computed text, may be called multiple times for + -- function/dynamicNodes. + self.static_text = text + return text +end + +function Snippet:get_docstring() + if self.docstring then + return self.docstring + -- copy+fake_expand the snippet here instead of in whatever code needs to know the docstring. + elseif not self.ext_opts then + -- not a snippetNode and not yet initialized + local snipcop = self:copy() + -- sets env, captures, etc. + snipcop:fake_expand() + local docstring = snipcop:get_docstring() + self.docstring = docstring + return docstring + end + local docstring = { "" } + for _, node in ipairs(self.nodes) do + local node_text = node:get_docstring() + -- append first line to last line of text so far. + docstring[#docstring] = docstring[#docstring] .. node_text[1] + for i = 2, #node_text do + docstring[#docstring + 1] = node_text[i] + end + end + -- cache computed text, may be called multiple times for + -- function/dynamicNodes. + -- if not outer snippet, wrap it in ${}. + self.docstring = self.type == types.snippet and docstring + or util.string_wrap(docstring, rawget(self, "pos")) + return self.docstring +end + +function Snippet:update() + for _, node in ipairs(self.nodes) do + node:update() + end +end + +function Snippet:update_static() + for _, node in ipairs(self.nodes) do + node:update_static() + end +end + +function Snippet:update_restore() + for _, node in ipairs(self.nodes) do + node:update_restore() + end +end + +function Snippet:store() + for _, node in ipairs(self.nodes) do + node:store() + end +end + +function Snippet:indent(prefix) + self.indentstr = prefix + for _, node in ipairs(self.nodes) do + node:indent(prefix) + end +end + +function Snippet:expand_tabs(tabwidth, indenstringlen) + for _, node in ipairs(self.nodes) do + node:expand_tabs(tabwidth, indenstringlen) + end +end + +function Snippet:subsnip_init() + node_util.subsnip_init_children(self, self.nodes) +end + +Snippet.init_positions = node_util.init_child_positions_func( + "absolute_position", + "nodes", + "init_positions" +) +Snippet.init_insert_positions = node_util.init_child_positions_func( + "absolute_insert_position", + "insert_nodes", + "init_insert_positions" +) + +function Snippet:make_args_absolute() + for _, node in ipairs(self.nodes) do + node:make_args_absolute(self.absolute_insert_position) + end +end + +function Snippet:input_enter(_, dry_run) + if dry_run then + dry_run.active[self] = true + return + end + + self.visited = true + self.active = true + + if self.type == types.snippet then + -- set snippet-passive -> visited/unvisited for all children. + self:set_ext_opts("passive") + end + self.mark:update_opts(self.ext_opts.active) + + self:event(events.enter) +end + +function Snippet:input_leave(_, dry_run) + if dry_run then + dry_run.active[self] = false + return + end + + self:event(events.leave) + self:update_dependents() + + -- set own ext_opts to snippet-passive, there is no passive for snippets. + self.mark:update_opts(self.ext_opts.snippet_passive) + if self.type == types.snippet then + -- also override all nodes' ext_opt. + self:set_ext_opts("snippet_passive") + end + + self.active = false +end + +function Snippet:set_ext_opts(opt_name) + for _, node in ipairs(self.nodes) do + node:set_ext_opts(opt_name) + end +end + +function Snippet:jump_into(dir, no_move, dry_run) + self:init_dry_run_active(dry_run) + + -- if dry_run, ignore self.active + if self:is_active(dry_run) then + self:input_leave(no_move, dry_run) + + if dir == 1 then + return self.next:jump_into(dir, no_move, dry_run) + else + return self.prev:jump_into(dir, no_move, dry_run) + end + else + self:input_enter(no_move, dry_run) + + if dir == 1 then + return self.inner_first:jump_into(dir, no_move, dry_run) + else + return self.inner_last:jump_into(dir, no_move, dry_run) + end + end +end + +-- Snippets inherit Node:jump_from, it shouldn't occur normally, but may be +-- used in LSP-Placeholders. + +function Snippet:exit() + if self.type == types.snippet then + -- if exit is called, this will not be visited again. + -- Thus, also clean up the child-snippets, which will also not be + -- visited again, since they can only be visited through self. + for _, child in ipairs(self.child_snippets) do + child:exit() + end + end + + self.visible = false + for _, node in ipairs(self.nodes) do + node:exit() + end + self.mark:clear() + self.active = false +end + +function Snippet:text_only() + for _, node in ipairs(self.nodes) do + if node.type ~= types.textNode then + return false + end + end + return true +end + +function Snippet:event(event, event_args) + -- there are 3 sources of a callback, for a snippetNode: + -- self.callbacks[-1], self.node_callbacks, and parent.callbacks[self.pos]. + local m1_cb, cb, parent_cb + -- since we handle pre-expand callbacks here, we need to handle the + -- event_res, which may be returned by more than one callback. + -- In order to keep it simple, we just return any non-nil result. + local m1_cb_res, cb_res, parent_cb_res + + m1_cb = self.callbacks[-1][event] + if m1_cb then + m1_cb_res = m1_cb(self, event_args) + end + + cb = self.node_callbacks[event] + if cb then + cb_res = cb(self, event_args) + end + + if self.type == types.snippetNode and self.pos then + -- if snippetNode, also do callback for position in parent. + parent_cb = self.parent.callbacks[self.pos][event] + if parent_cb then + parent_cb_res = parent_cb(self) + end + end + + session.event_node = self + session.event_args = event_args + vim.api.nvim_exec_autocmds("User", { + pattern = "Luasnip" .. events.to_string(self.type, event), + modeline = false, + }) + + return vim.F.if_nil(cb_res, m1_cb_res, parent_cb_res) +end + +local function nodes_from_pattern(pattern) + local nodes = {} + local text_active = true + local iNode_indx = 1 + local tokens = pattern_tokenizer.tokenize(pattern) + for _, text in ipairs(tokens) do + if text_active then + nodes[#nodes + 1] = tNode.T(text) + else + nodes[#nodes + 1] = iNode.I(iNode_indx, text) + iNode_indx = iNode_indx + 1 + end + text_active = not text_active + end + -- This is done so the user ends up at the end of the snippet either way + -- and may use their regular expand-key to expand the snippet. + -- Autoexpanding doesn't quite work, if the snippet ends with an + -- interactive part and the user overrides whatever is put in there, the + -- jump to the i(0) may trigger an expansion, and the helper-snippet could + -- not easily be removed, as the snippet the user wants to actually use is + -- inside of it. + -- Because of that it is easier to let the user do the actual expanding, + -- but help them on the way to it (by providing an easy way to override the + -- "interactive" parts of the pattern-trigger). + -- + -- if even number of nodes, the last is an insertNode (nodes begins with + -- textNode and alternates between the two). + if #nodes % 2 == 0 then + nodes[#nodes] = iNode.I(0, tokens[#tokens]) + else + nodes[#nodes + 1] = iNode.I(0) + end + return nodes +end + +-- only call on actual snippets, snippetNodes don't have trigger. +function Snippet:get_pattern_expand_helper() + if not self.expand_helper_snippet then + local nodes = nodes_from_pattern(self.trigger) + self.expand_helper_snippet = S(self.trigger, nodes, { + callbacks = { + [0] = { + [events.enter] = function(_) + vim.schedule(function() + -- Remove this helper snippet as soon as the i(0) + -- is reached. + require("luasnip").unlink_current() + end) + end, + }, + }, + }) + end + -- will be copied in actual expand. + return self.expand_helper_snippet +end + +function Snippet:find_node(predicate) + for _, node in ipairs(self.nodes) do + if predicate(node) then + return node + else + local node_in_child = node:find_node(predicate) + if node_in_child then + return node_in_child + end + end + end + return nil +end + +function Snippet:insert_to_node_absolute(position) + if #position == 0 then + return self.absolute_position + end + local insert_indx = util.pop_front(position) + return self.insert_nodes[insert_indx]:insert_to_node_absolute(position) +end + +function Snippet:set_dependents() + for _, node in ipairs(self.nodes) do + node:set_dependents() + end +end + +function Snippet:set_argnodes(dict) + node_mod.Node.set_argnodes(self, dict) + for _, node in ipairs(self.nodes) do + node:set_argnodes(dict) + end +end + +function Snippet:update_all_dependents() + -- call the version that only updates this node. + self:_update_dependents() + -- only for insertnodes, others will not have dependents. + for _, node in ipairs(self.insert_nodes) do + node:update_all_dependents() + end +end +function Snippet:update_all_dependents_static() + -- call the version that only updates this node. + self:_update_dependents_static() + -- only for insertnodes, others will not have dependents. + for _, node in ipairs(self.insert_nodes) do + node:update_all_dependents_static() + end +end + +function Snippet:resolve_position(position) + -- only snippets have -1-node. + if position == -1 and self.type == types.snippet then + return self.prev + end + + return self.nodes[position] +end + +function Snippet:static_init() + node_mod.Node.static_init(self) + for _, node in ipairs(self.nodes) do + node:static_init() + end +end + +-- called only for snippetNodes! +function Snippet:resolve_child_ext_opts() + if self.merge_child_ext_opts then + self.effective_child_ext_opts = ext_util.child_extend( + vim.deepcopy(self.child_ext_opts), + self.parent.effective_child_ext_opts + ) + else + self.effective_child_ext_opts = vim.deepcopy(self.child_ext_opts) + end +end + +local function no_match() + return nil +end + +function Snippet:invalidate() + self.hidden = true + -- override matching-function. + self.matches = no_match + self.invalidated = true + snippet_collection.invalidated_count = snippet_collection.invalidated_count + + 1 +end + +-- used in add_snippets to get variants of snippet. +function Snippet:retrieve_all() + return { self } +end + +function Snippet:get_keyed_node(key) + -- get key-node from dependents_dict. + return self.dependents_dict:get({ "key", key, "node" }) +end + +-- adjust rgrav of nodes left (direction=-1) or right (direction=1) of node at +-- child_indx. +-- (direction is the direction into which is searched, from child_indx outward) +-- assumption: direction-endpoint of node is on child_endpoint. (caller +-- responsible) +function Snippet:set_sibling_rgravs(node, child_endpoint, direction, rgrav) + node_util.nodelist_adjust_rgravs( + self.nodes, + node.absolute_position[#node.absolute_position] + direction, + child_endpoint, + direction, + rgrav, + true + ) +end + +-- called only if the "-direction"-endpoint has to be changed, but the +-- "direction"-endpoint not. +function Snippet:subtree_set_pos_rgrav(pos, direction, rgrav) + self.mark:set_rgrav(-direction, rgrav) + + local child_from_indx + if direction == 1 then + child_from_indx = 1 + else + child_from_indx = #self.nodes + end + + node_util.nodelist_adjust_rgravs( + self.nodes, + child_from_indx, + pos, + direction, + rgrav, + true + ) +end +-- changes rgrav of all nodes and all endpoints in this snippetNode to `rgrav`. +function Snippet:subtree_set_rgrav(rgrav) + self.mark:set_rgravs(rgrav, rgrav) + for _, node in ipairs(self.nodes) do + node:subtree_set_rgrav(rgrav) + end +end + +-- for this to always return a node if pos is withing the snippet-boundaries, +-- the snippet must have valid extmarks. +-- Looks for a node that has a specific property (either linkable, or +-- interactive), which can be indicated by setting mode to either of the two +-- (as string). +function Snippet:node_at(pos, mode) + if #self.nodes == 0 then + -- special case: no children (can naturally occur with dynamicNode, + -- when its function could not be evaluated, or if a user passed an empty snippetNode). + return self + end + + -- collect nodes where pos is either in gravity-adjusted boundaries, .. + local gravity_matches = {} + -- .. or just inside the regular boundaries. + -- Both are needed, so we can fall back to matches if there is no gravity_match + -- with the desired mode ("linkable" or "interactive"), fall back to + -- extmark_matches if there is also no regular match with the desired mode, + -- and finally fall back to any match (still preferring extmark-match) if + -- there is no match with the desired mode at all. + -- Unfortunately, all this is necessary, since there are many cases where + -- we may return no linkable node, despite there apparently being one in + -- reach of the cursor. + local matches = {} + -- find_node visits all nodes in-order until the predicate returns true. + self:find_node(function(node) + if not node:leaf() then + -- not a leaf-node. + return false + end + + local node_mark = node.mark + local node_from, node_to = node_mark:pos_begin_end_raw() + -- if pos certainly beyond node, quickly continue. + -- This means a little more work for the nodes in range of pos, while + -- all nodes well before it are quickly skipped => should benefit + -- all cases where the runtime of this is noticeable, and which are not + -- unrealistic (lots of zero-width nodes). + if util.pos_cmp(pos, { node_to[1], node_to[2] + 1 }) > 0 then + return false + end + + -- generate gravity-adjusted endpoints. + local grav_adjusted_from = { node_from[1], node_from[2] } + local grav_adjusted_to = { node_to[1], node_to[2] } + if node_mark:get_rgrav(-1) then + grav_adjusted_from[2] = grav_adjusted_from[2] + 1 + end + if node_mark:get_rgrav(1) then + grav_adjusted_to[2] = grav_adjusted_to[2] + 1 + end + + local cmp_pos_to = util.pos_cmp(pos, node_to) + local cmp_pos_from = util.pos_cmp(pos, node_from) + local cmp_grav_from = util.pos_cmp(pos, grav_adjusted_from) + local cmp_grav_to = util.pos_cmp(pos, grav_adjusted_to) + + if cmp_pos_from < 0 then + -- abort once the first node is definitely beyond pos. + -- (extmark-gravity can't move column to the left). + return true + end + + -- pos between from,to <=> from <= pos < to is used when choosing which + -- extmark to insert text into, so we should adopt it here. + if cmp_grav_from >= 0 and cmp_grav_to < 0 then + table.insert(gravity_matches, node) + end + -- matches does not have to respect the extmark-conventions, just catch + -- all possible nodes. + if cmp_pos_from >= 0 and cmp_pos_to <= 0 then + table.insert(matches, node) + end + end) + + -- instead of stupid nesting ifs, and because we can't use goto since + -- non-luajit-users should also be able to run luasnip :((( + return (function() + for _, node in ipairs(gravity_matches) do + if node[mode](node) then + return node + end + end + for _, node in ipairs(matches) do + if node[mode](node) then + return node + end + end + -- no interactive node found, fall back to any match. + return gravity_matches[1] or matches[1] + end)() +end + +-- return the node the snippet jumps to, or nil if there isn't one. +function Snippet:next_node() + -- self.next is $0, .next is either the surrounding node, or the next + -- snippet in the list, .prev is the i(-1) if the self.next.next is the + -- next snippet. + + if self.parent_node and self.next.next == self.parent_node then + return self.next.next + else + return (self.next.next and self.next.next.prev) + end +end + +function Snippet:extmarks_valid() + -- assumption: extmarks are contiguous, and all can be queried via pos_begin_end_raw. + local ok, current_from, self_to = + pcall(self.mark.pos_begin_end_raw, self.mark) + if not ok then + return false + end + + -- below code does not work correctly if the snippet(Node) does not have any children. + if #self.nodes == 0 then + return true + end + + for _, node in ipairs(self.nodes) do + local ok_, node_from, node_to = + pcall(node.mark.pos_begin_end_raw, node.mark) + -- this snippet is invalid if: + -- - we can't get the position of some node + -- - the positions aren't contiguous or don't completely fill the parent, or + -- - any child of this node violates these rules. + if + not ok_ + or util.pos_cmp(current_from, node_from) ~= 0 + or not node:extmarks_valid() + then + return false + end + current_from = node_to + end + if util.pos_cmp(current_from, self_to) ~= 0 then + return false + end + + return true +end + +return { + Snippet = Snippet, + S = S, + _S = _S, + SN = SN, + P = P, + ISN = ISN, + wrap_nodes_in_snippetNode = wrap_nodes_in_snippetNode, + init_snippet_context = init_snippet_context, + init_snippet_opts = init_snippet_opts, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/snippetProxy.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/snippetProxy.lua new file mode 100644 index 00000000..03e1c878 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/snippetProxy.lua @@ -0,0 +1,125 @@ +-- the idea of this class is to lazily parse snippet (eg. only on expansion). +-- +-- This is achieved by returning a proxy that has enough information to tell +-- whether the snippet should be expanded at a given point (eg. all fields +-- necessary to perform Snippet:matches()), but doesn't actually +-- have to parse the snippet, leaving up-front cost of loading a bunch of +-- snippets at a minimum. + +local lsp_parse_fn = require("luasnip.util.parser").parse_snippet +local snip_mod = require("luasnip.nodes.snippet") +local node_util = require("luasnip.nodes.util") +local extend_decorator = require("luasnip.util.extend_decorator") + +local SnippetProxy = {} + +-- add Snippet-functions SnippetProxy can perform using the available data. +SnippetProxy.matches = snip_mod.Snippet.matches +SnippetProxy.invalidate = snip_mod.Snippet.invalidate +SnippetProxy.retrieve_all = snip_mod.Snippet.retrieve_all + +function SnippetProxy:get_docstring() + return self.docstring +end + +function SnippetProxy:instantiate(parse_fn) + -- self already contains initialized context and opts, can just be passed + -- here, no problem. + -- Bonus: if some keys are set on the snippets in the table (from the + -- outside, for whatever reason), they are also present in the expanded + -- snippet. + -- + -- _S will copy self, so we can safely mutate (set metatables). + local snippet = snip_mod._S(self, parse_fn(nil, self._snippet_string)) + -- snippet will have snippetProxies `copy`, nil it in snippet so it calls + -- snippet-copy via metatable. + snippet.copy = nil + + self._snippet = snippet + -- directly call into snippet on missing keys. + setmetatable(self, { + __index = self._snippet, + }) + + -- return snippet so it can provide a missing key. + return snippet +end + +-- some values of the snippet are nil by default, list them here so snippets +-- aren't instantiated because of them. +local license_to_nil = + { priority = true, snippetType = true, _source = true, filetype = true } + +-- context and opts are (almost) the same objects as in s(contex, nodes, opts), snippet is a string representing the snippet. +-- opts can aditionally contain the key `parse_fn`, which will be used to parse +-- the snippet. This is useful, since snipmate-snippets are parsed with a +-- function than regular lsp-snippets. +-- context can be nil, in that case the resulting object can't be inserted into +-- the snippet-tables, but may be used after expansion (i.e. returned from +-- snippet:copy) +local function new(context, snippet, opts) + opts = opts or {} + + -- default to regular lsp-parse-function. + local parse_fn = lsp_parse_fn + + if opts.parse_fn then + parse_fn = opts.parse_fn + end + -- "error": there should not be duplicate keys, don't silently overwrite/keep. + local sp = vim.tbl_extend( + "error", + {}, + context + and snip_mod.init_snippet_context( + node_util.wrap_context(context), + opts + ) + or {}, + snip_mod.init_snippet_opts(opts), + node_util.init_node_opts(opts) + ) + + sp._snippet_string = snippet + -- override docstring + sp.docstring = snippet + + setmetatable(sp, { + __index = function(t, k) + if license_to_nil[k] then + -- k might be nil, return it. + return nil + end + + if SnippetProxy[k] then + -- if it is possible to perform this operation without actually parsing the snippet, just do it. + return SnippetProxy[k] + end + local snip = SnippetProxy.instantiate(t, parse_fn) + if k == "_snippet" then + return snip + else + return snip[k] + end + end, + }) + + -- snippetProxy has to be able to return snippet on copy even after parsing, + -- when the metatable has been changed. Therefore: set copy in each instance + -- of snippetProxy. + function sp:copy() + local copy = self._snippet:copy() + copy.id = self.id + + return copy + end + + return sp +end +extend_decorator.register( + new, + { arg_indx = 1, extend = node_util.snippet_extend_context }, + { arg_indx = 3 } +) + +return new diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/textNode.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/textNode.lua new file mode 100644 index 00000000..88fad040 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/textNode.lua @@ -0,0 +1,71 @@ +local node_mod = require("luasnip.nodes.node") +local util = require("luasnip.util.util") +local types = require("luasnip.util.types") +local events = require("luasnip.util.events") +local extend_decorator = require("luasnip.util.extend_decorator") + +local TextNode = node_mod.Node:new() + +local function T(static_text, opts) + return TextNode:new({ + static_text = util.to_string_table(static_text), + mark = nil, + type = types.textNode, + }, opts) +end +extend_decorator.register(T, { arg_indx = 2 }) + +function TextNode:input_enter(no_move, dry_run) + if dry_run then + return + end + + self.mark:update_opts(self.ext_opts.active) + self.visited = true + + if not no_move then + local mark_begin_pos = self.mark:pos_begin_raw() + if vim.fn.mode() == "i" then + util.insert_move_on(mark_begin_pos) + else + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes("", true, false, true), + "n", + true + ) + util.normal_move_on_insert(mark_begin_pos) + end + end + + self:event(events.enter, no_move) +end + +function TextNode:update_all_dependents() end + +function TextNode:is_interactive() + -- a resounding false. + return false +end + +function TextNode:extmarks_valid() + local from, to = self.mark:pos_begin_end_raw() + if + util.pos_cmp(from, to) == 0 + and not ( + #self.static_text == 0 + or (#self.static_text == 1 and #self.static_text[1] == 0) + ) + then + -- assume the snippet is invalid if a textNode occupies zero space, + -- but has text which would occupy some. + -- This should allow some modifications, but as soon as a textNode is + -- deleted entirely, we sound the alarm :D + return false + end + return true +end + +return { + T = T, + textNode = TextNode, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/util.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/util.lua new file mode 100644 index 00000000..53d517b7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/util.lua @@ -0,0 +1,789 @@ +local util = require("luasnip.util.util") +local ext_util = require("luasnip.util.ext_opts") +local types = require("luasnip.util.types") +local key_indexer = require("luasnip.nodes.key_indexer") +local session = require("luasnip.session") + +local function subsnip_init_children(parent, children) + for _, child in ipairs(children) do + if child.type == types.snippetNode then + child.snippet = parent.snippet + child:resolve_child_ext_opts() + end + child:resolve_node_ext_opts() + child:subsnip_init() + end +end + +local function init_child_positions_func( + key, + node_children_key, + child_func_name +) + -- maybe via load()? + return function(node, position_so_far) + node[key] = vim.deepcopy(position_so_far) + local pos_depth = #position_so_far + 1 + + for indx, child in ipairs(node[node_children_key]) do + position_so_far[pos_depth] = indx + child[child_func_name](child, position_so_far) + end + -- undo changes to position_so_far. + position_so_far[pos_depth] = nil + end +end + +local function make_args_absolute(args, parent_insert_position, target) + for i, arg in ipairs(args) do + if type(arg) == "number" then + -- the arg is a number, should be interpreted relative to direct + -- parent. + local t = vim.deepcopy(parent_insert_position) + table.insert(t, arg) + target[i] = { absolute_insert_position = t } + else + -- insert node, absolute_indexer, or key itself, node's + -- absolute_insert_position may be nil, check for that during + -- usage. + target[i] = arg + end + end +end + +local function wrap_args(args) + -- stylua: ignore + if type(args) ~= "table" or + (type(args) == "table" and args.absolute_insert_position) or + key_indexer.is_key(args) then + -- args is one single arg, wrap it. + return { args } + else + return args + end +end + +-- includes child, does not include parent. +local function get_nodes_between(parent, child) + local nodes = {} + + -- special case for nodes without absolute_position (which is only + -- start_node). + if child.pos == -1 then + -- no nodes between, only child. + nodes[1] = child + return nodes + end + + local child_pos = child.absolute_position + + local indx = #parent.absolute_position + 1 + local prev = parent + while child_pos[indx] do + local next = prev:resolve_position(child_pos[indx]) + nodes[#nodes + 1] = next + prev = next + indx = indx + 1 + end + + return nodes +end + +-- assumes that children of child are not even active. +-- If they should also be left, do that separately. +-- Does not leave the parent. +local function leave_nodes_between(parent, child, no_move) + local nodes = get_nodes_between(parent, child) + if #nodes == 0 then + return + end + + -- reverse order, leave child first. + for i = #nodes, 2, -1 do + -- this only happens for nodes where the parent will also be left + -- entirely (because we stop at nodes[2], and handle nodes[1] + -- separately) + nodes[i]:input_leave(no_move) + nodes[i - 1]:input_leave_children() + end + nodes[1]:input_leave(no_move) +end + +local function enter_nodes_between(parent, child, no_move) + local nodes = get_nodes_between(parent, child) + if #nodes == 0 then + return + end + + for i = 1, #nodes - 1 do + -- only enter children for nodes before the last (lowest) one. + nodes[i]:input_enter(no_move) + nodes[i]:input_enter_children() + end + nodes[#nodes]:input_enter(no_move) +end + +local function select_node(node) + local node_begin, node_end = node.mark:pos_begin_end_raw() + util.any_select(node_begin, node_end) +end + +local function print_dict(dict) + print(vim.inspect(dict, { + process = function(item, path) + if path[#path] == "node" or path[#path] == "dependent" then + return "node@" .. vim.inspect(item.absolute_position) + elseif path[#path] ~= vim.inspect.METATABLE then + return item + end + end, + })) +end + +local function init_node_opts(opts) + local in_node = {} + if not opts then + opts = {} + end + + -- copy once here, the opts might be reused. + in_node.node_ext_opts = + ext_util.complete(vim.deepcopy(opts.node_ext_opts or {})) + + if opts.merge_node_ext_opts == nil then + in_node.merge_node_ext_opts = true + else + in_node.merge_node_ext_opts = opts.merge_node_ext_opts + end + + in_node.key = opts.key + + in_node.node_callbacks = opts.node_callbacks or {} + + return in_node +end + +local function snippet_extend_context(arg, extend) + if type(arg) == "string" then + arg = { trig = arg } + end + + -- both are table or nil now. + return vim.tbl_extend("keep", arg or {}, extend or {}) +end + +local function wrap_context(context) + if type(context) == "string" then + return { trig = context } + else + return context + end +end + +local function linkable_node(node) + -- node.type has to be one of insertNode, exitNode. + return vim.tbl_contains( + { types.insertNode, types.exitNode }, + rawget(node, "type") + ) +end + +-- mainly used internally, by binarysearch_pos. +-- these are the nodes that are definitely not linkable, there are nodes like +-- dynamicNode or snippetNode that might be linkable, depending on their +-- content. Could look into that to make this more complete, but that does not +-- feel appropriate (higher runtime), most cases should be served well by this +-- heuristic. +local function non_linkable_node(node) + return vim.tbl_contains( + { types.textNode, types.functionNode }, + rawget(node, "type") + ) +end +-- return whether a node is certainly (not) interactive. +-- Coincindentially, the same nodes as (non-)linkable ones, but since there is a +-- semantic difference, use separate names. +local interactive_node = linkable_node +local non_interactive_node = non_linkable_node + +local function prefer_nodes(prefer_func, reject_func) + return function(cmp_mid_to, cmp_mid_from, mid_node) + local reject_mid = reject_func(mid_node) + local prefer_mid = prefer_func(mid_node) + + -- if we can choose which node to continue in, prefer the one that + -- may be linkable/interactive. + if cmp_mid_to == 0 and reject_mid then + return true, false + elseif cmp_mid_from == 0 and reject_mid then + return false, true + elseif (cmp_mid_to == 0 or cmp_mid_from == 0) and prefer_mid then + return false, false + else + return cmp_mid_to >= 0, cmp_mid_from < 0 + end + end +end + +-- functions for resolving conflicts, if `pos` is on the boundary of two nodes. +-- Return whether to continue behind or before mid (in that order). +-- At most one of those may be true, of course. +local binarysearch_preference = { + outside = function(cmp_mid_to, cmp_mid_from, _) + return cmp_mid_to >= 0, cmp_mid_from <= 0 + end, + inside = function(cmp_mid_to, cmp_mid_from, _) + return cmp_mid_to > 0, cmp_mid_from < 0 + end, + linkable = prefer_nodes(linkable_node, non_linkable_node), + interactive = prefer_nodes(interactive_node, non_interactive_node), +} +-- `nodes` is a list of nodes ordered by their occurrence in the buffer. +-- `pos` is a row-column-tuble, byte-columns, and we return the node the LEFT +-- EDGE(/side) of `pos` is inside. +-- This convention is chosen since a snippet inserted at `pos` will move the +-- character at `pos` to the right. +-- The exact meaning of "inside" can be influenced with `respect_rgravs` and +-- `boundary_resolve_mode`: +-- * if `respect_rgravs` is true, "inside" emulates the shifting-behaviour of +-- extmarks: +-- First of all, we compare the left edge of `pos` with the left/right edges +-- of from/to, depending on rgrav. +-- If the left edge is <= left/right edge of from, and < left/right edge of +-- to, `pos` is inside the node. +-- +-- * if `respect_rgravs` is false, pos has to be fully inside a node to be +-- considered inside it. If pos is on the left endpoint, it is considered to be +-- left of the node, and likewise for the right endpoint. +-- +-- * `boundary_resolve_mode` changes how a position on the boundary of a node +-- is treated: +-- * for `"prefer_linkable/interactive"`, we assume that the nodes in `nodes` are +-- contiguous, and prefer falling into the previous/next node if `pos` is on +-- mid's boundary, and mid is not linkable/interactie. +-- This way, we are more likely to return a node that can handle a new +-- snippet/is interactive. +-- * `"prefer_outside"` makes sense when the nodes are not contiguous, and we'd +-- like to find a position between two nodes. +-- This mode makes sense for finding the snippet a new snippet should be +-- inserted in, since we'd like to prefer inserting before/after a snippet, if +-- the position is ambiguous. +-- +-- In general: +-- These options are useful for making this function more general: When +-- searching in the contiguous nodes of a snippet, we'd like this routine to +-- return any of them (obviously the one pos is inside/or on the border of, and +-- we'd like to prefer returning a node that can be linked), but in no case +-- fail. +-- However! when searching the top-level snippets with the intention of finding +-- the snippet/node a new snippet should be expanded inside, it seems better to +-- shift an existing snippet to the right/left than expand the new snippet +-- inside it (when the expand-point is on the boundary). +local function binarysearch_pos( + nodes, + pos, + respect_rgravs, + boundary_resolve_mode +) + local left = 1 + local right = #nodes + + -- actual search-routine from + -- https://github.com/Roblox/Wiki-Lua-Libraries/blob/master/StandardLibraries/BinarySearch.lua + if #nodes == 0 then + return nil, 1 + end + while true do + local mid = left + math.floor((right - left) / 2) + local mid_mark = nodes[mid].mark + local ok, mid_from, mid_to = pcall(mid_mark.pos_begin_end_raw, mid_mark) + + if not ok then + -- error while running this procedure! + -- return false (because I don't know how to do this with `error` + -- and the offending node). + -- (returning data instead of a message in `error` seems weird..) + return false, mid + end + + if respect_rgravs then + -- if rgrav is set on either endpoint, the node considers its + -- endpoint to be the right, not the left edge. + -- We only want to work with left edges but since the right edge is + -- the left edge of the next column, this is not an issue :) + -- TODO: does this fail with multibyte characters??? + if mid_mark:get_rgrav(-1) then + mid_from[2] = mid_from[2] + 1 + end + if mid_mark:get_rgrav(1) then + mid_to[2] = mid_to[2] + 1 + end + end + + local cmp_mid_to = util.pos_cmp(pos, mid_to) + local cmp_mid_from = util.pos_cmp(pos, mid_from) + + local cont_behind_mid, cont_before_mid = + boundary_resolve_mode(cmp_mid_to, cmp_mid_from, nodes[mid]) + + if cont_behind_mid then + -- make sure right-left becomes smaller. + left = mid + 1 + if left > right then + return nil, mid + 1 + end + elseif cont_before_mid then + -- continue search on left side + right = mid - 1 + if left > right then + return nil, mid + end + else + -- greater-equal than mid_from, smaller or equal to mid_to => left edge + -- of pos is inside nodes[mid] :) + return nodes[mid], mid + end + end +end + +-- a and b have to be in the same snippet, return their first (as seen from +-- them) common parent. +local function first_common_node(a, b) + local a_pos = a.absolute_position + local b_pos = b.absolute_position + + -- last as seen from root. + local i = 0 + local last_common = a.parent.snippet + -- invariant: last_common is parent of both a and b. + while (a_pos[i + 1] ~= nil) and a_pos[i + 1] == b_pos[i + 1] do + last_common = last_common:resolve_position(a_pos[i + 1]) + i = i + 1 + end + + return last_common +end + +-- roots at depth 0, children of root at depth 1, their children at 2, ... +local function snippettree_depth(snippet) + local depth = 0 + while snippet.parent_node ~= nil do + snippet = snippet.parent_node.parent.snippet + depth = depth + 1 + end + return depth +end + +-- find the first common snippet a and b have on their respective unique paths +-- to the snippet-roots. +-- if no common ancestor exists (ie. a and b are roots of their buffers' +-- forest, or just in different trees), return nil. +-- in both cases, the paths themselves are returned as well. +-- The common ancestor is included in the paths, except if there is none. +-- Instead of storing the snippets in the paths, they are represented by the +-- node which contains the next-lower snippet in the path (or `from`/`to`, if it's +-- the first node of the path) +-- This is a bit complicated, but this representation contains more information +-- (or, more easily accessible information) than storing snippets: the +-- immediate parent of the child along the path cannot be easily retrieved if +-- the snippet is stored, but the snippet can be easily retrieved if the child +-- is stored (.parent.snippet). +-- And, so far this is pretty specific to refocus, and thus modeled so there is +-- very little additional work in that method. +-- At most one of a,b may be nil. +local function first_common_snippet_ancestor_path(a, b) + local a_path = {} + local b_path = {} + + -- general idea: we find the depth of a and b, walk upward with the deeper + -- one until we find its first ancestor with the same depth as the less + -- deep snippet, and then follow both paths until they arrive at the same + -- snippet (or at the root of their respective trees). + -- if either is nil, we treat it like it's one of the roots (the code will + -- behave correctly this way, and return an empty path for the nil-node, + -- and the correct path for the non-nil one). + local a_depth = a ~= nil and snippettree_depth(a) or 0 + local b_depth = b ~= nil and snippettree_depth(b) or 0 + + -- bit subtle: both could be 0, but one could be nil. + -- deeper should not be nil! (this allows us to do the whole walk for the + -- non-nil node in the first for-loop, as opposed to needing some special + -- handling). + local deeper, deeper_path, other, other_path + if b == nil or (a ~= nil and a_depth > b_depth) then + deeper = a + other = b + deeper_path = a_path + other_path = b_path + else + -- we land here if `b ~= nil and (a == nil or a_depth >= b_depth)`, so + -- exactly what we want. + deeper = b + other = a + deeper_path = b_path + other_path = a_path + end + + for _ = 1, math.abs(a_depth - b_depth) do + table.insert(deeper_path, deeper.parent_node) + deeper = deeper.parent_node.parent.snippet + end + -- here: deeper and other are at the same depth. + -- If we walk upwards one step at a time, they will meet at the same + -- parent, or hit their respective roots. + + -- deeper can't be nil, if other is, we are done here and can return the + -- paths (and there is no shared node) + if other == nil then + return nil, a_path, b_path + end + -- beyond here, deeper and other are not nil. + + while deeper ~= other do + if deeper.parent_node == nil then + -- deeper is at depth 0 => other as well => both are roots. + return nil, a_path, b_path + end + + table.insert(deeper_path, deeper.parent_node) + table.insert(other_path, other.parent_node) + + -- walk one step towards root. + deeper = deeper.parent_node.parent.snippet + other = other.parent_node.parent.snippet + end + + -- either one will do here. + return deeper, a_path, b_path +end + +-- removes focus from `from` and upwards up to the first common ancestor +-- (node!) of `from` and `to`, and then focuses nodes between that ancestor and +-- `to`. +-- Requires that `from` is currently entered/focused, and that no snippet +-- between `to` and its root is invalid. +local function refocus(from, to) + if from == nil and to == nil then + -- absolutely nothing to do, should not happen. + return + end + -- pass nil if from/to is nil. + -- if either is nil, first_common_node is nil, and the corresponding list empty. + local first_common_snippet, from_snip_path, to_snip_path = + first_common_snippet_ancestor_path( + from and from.parent.snippet, + to and to.parent.snippet + ) + + -- we want leave/enter_path to be s.t. leaving/entering all nodes between + -- each entry and its snippet (and the snippet itself) will leave/enter all + -- nodes between the first common snippet (or the root-snippet) and + -- from/to. + -- Then, the nodes between the first common node and the respective + -- entrypoints (also nodes) into the first common snippet will have to be + -- left/entered, which is handled by final_leave_/first_enter_/common_node. + + -- from, to are not yet in the paths. + table.insert(from_snip_path, 1, from) + table.insert(to_snip_path, 1, to) + + -- determine how far to leave: if there is a common snippet, only up to the + -- first common node of from and to, otherwise leave the one snippet, and + -- enter the other completely. + local final_leave_node, first_enter_node, common_node + if first_common_snippet then + -- there is a common snippet => there is a common node => we have to + -- set final_leave_node, first_enter_node, and common_node. + final_leave_node = from_snip_path[#from_snip_path] + first_enter_node = to_snip_path[#to_snip_path] + common_node = first_common_node(first_enter_node, final_leave_node) + + -- Also remove these last nodes from the lists, their snippet is not + -- supposed to be left entirely. + from_snip_path[#from_snip_path] = nil + to_snip_path[#to_snip_path] = nil + end + + -- now do leave/enter, set no_move on all operations. + -- if one of from/to was nil, there are no leave/enter-operations done for + -- it (from/to_snip_path is {}, final_leave/first_enter_* is nil). + + -- leave_children on all from-nodes except the original from. + if #from_snip_path > 0 then + local ok1, ok2 + if from.type == types.exitNode then + ok1 = pcall(from.input_leave, from, true) + ok2 = true + else + -- we know that the first node is from. + ok1 = pcall(leave_nodes_between, from.parent.snippet, from, true) + -- leave_nodes_between does not affect snippet, so that has to be left + -- here. + -- snippet does not have input_leave_children, so only input_leave + -- needs to be called. + ok2 = pcall( + from.parent.snippet.input_leave, + from.parent.snippet, + true + ) + end + if not ok1 or not ok2 then + from.parent.snippet:remove_from_jumplist() + end + end + for i = 2, #from_snip_path do + local node = from_snip_path[i] + local ok1, ok2, ok3 + ok1 = pcall(node.input_leave_children, node) + + if node.type == types.exitNode then + ok2 = pcall(node.input_leave, node, true) + ok3 = true + else + ok2 = pcall(leave_nodes_between, node.parent.snippet, node, true) + ok3 = pcall( + node.parent.snippet.input_leave, + node.parent.snippet, + true + ) + end + + if not ok1 or not ok2 or not ok3 then + from.parent.snippet:remove_from_jumplist() + end + end + + -- this leave, and the following enters should be safe: the path to `to` + -- was verified via extmarks_valid (precondition). + if common_node and final_leave_node then + -- if the final_leave_node is from, its children are not active (which + -- stems from the requirement that from is the currently active node), + -- and so don't have to be left. + if final_leave_node ~= from then + final_leave_node:input_leave_children() + end + leave_nodes_between(common_node, final_leave_node, true) + end + + if common_node and first_enter_node then + -- In general we assume that common_node is active when we are here. + -- This may not be the case if we are currently inside the i(0) or + -- i(-1), since the snippet might be the common node and in this case, + -- it is inactive. + -- This means that, if we want to enter a non-exitNode, we have to + -- explicitly activate the snippet for all jumps to behave correctly. + -- (if we enter a i(0)/i(-1), this is not necessary, of course). + if + final_leave_node.type == types.exitNode + and first_enter_node.type ~= types.exitNode + then + common_node:input_enter(true) + end + -- symmetrically, entering an i(0)/i(-1) requires leaving the snippet. + if + final_leave_node.type ~= types.exitNode + and first_enter_node.type == types.exitNode + then + common_node:input_leave(true) + end + + enter_nodes_between(common_node, first_enter_node, true) + + -- if the `first_enter_node` is already `to` (occurs if `to` is in the + -- common snippet of to and from), we should not enter its children. + -- (we only want to `input_enter` to.) + if first_enter_node ~= to then + first_enter_node:input_enter_children() + end + end + + -- same here, input_enter_children has to be called manually for the + -- to-nodes of the path we are entering (since enter_nodes_between does not + -- call it for the child-node). + + for i = #to_snip_path, 2, -1 do + local node = to_snip_path[i] + if node.type ~= types.exitNode then + node.parent.snippet:input_enter(true) + enter_nodes_between(node.parent.snippet, node, true) + else + node:input_enter(true) + end + node:input_enter_children() + end + if #to_snip_path > 0 then + if to.type ~= types.exitNode then + to.parent.snippet:input_enter(true) + else + to.parent.snippet:input_leave(true) + end + enter_nodes_between(to.parent.snippet, to, true) + end + + -- it may be that we only leave nodes in this process (happens if to is a + -- parent of from). + -- If that is the case, we will not explicitly focus on to, and it may be + -- that focus is even lost if it was focused previously (leave may trigger + -- update, update may change focus) + -- To prevent this, just call focus here, which is pretty close to a noop + -- if to is already focused. + if to then + to:focus() + end +end + +local function generic_extmarks_valid(node, child) + -- valid if + -- - extmark-extents match. + -- - current choice is valid + local ok1, self_from, self_to = + pcall(node.mark.pos_begin_end_raw, node.mark) + local ok2, child_from, child_to = + pcall(child.mark.pos_begin_end_raw, child.mark) + + if + not ok1 + or not ok2 + or util.pos_cmp(self_from, child_from) ~= 0 + or util.pos_cmp(self_to, child_to) ~= 0 + then + return false + end + return child:extmarks_valid() +end + +-- returns: * the smallest known snippet `pos` is inside. +-- * the list of other snippets inside the snippet of this smallest +-- node +-- * the index this snippet would be at if inserted into that list +-- * the node of this snippet pos is on. +local function snippettree_find_undamaged_node(pos, opts) + local prev_parent, child_indx, found_parent + local prev_parent_children = + session.snippet_roots[vim.api.nvim_get_current_buf()] + + while true do + -- false: don't respect rgravs. + -- Prefer inserting the snippet outside an existing one. + found_parent, child_indx = binarysearch_pos( + prev_parent_children, + pos, + opts.tree_respect_rgravs, + opts.tree_preference + ) + if found_parent == false then + -- if the procedure returns false, there was an error getting the + -- position of a node (in this case, that node is a snippet). + -- The position of the offending snippet is returned in child_indx, + -- and we can remove it here. + prev_parent_children[child_indx]:remove_from_jumplist() + elseif found_parent ~= nil and not found_parent:extmarks_valid() then + -- found snippet damaged (the idea to sidestep the damaged snippet, + -- even if no error occurred _right now_, is to ensure that we can + -- input_enter all the nodes along the insertion-path correctly). + found_parent:remove_from_jumplist() + -- continue again with same parent, but one less snippet in its + -- children => shouldn't cause endless loop. + elseif found_parent == nil then + break + else + prev_parent = found_parent + -- can index prev_parent, since found_parent is not nil, and + -- assigned to prev_parent. + prev_parent_children = prev_parent.child_snippets + end + end + + local node + if prev_parent then + -- if found, find node to insert at, prefer receiving a linkable node. + node = prev_parent:node_at(pos, opts.snippet_mode) + end + + return prev_parent, prev_parent_children, child_indx, node +end + +local function root_path(node) + local path = {} + + while node do + local node_snippet = node.parent.snippet + local snippet_node_path = get_nodes_between(node_snippet, node) + -- get_nodes_between gives parent -> node, but we need + -- node -> parent => insert back to front. + for i = #snippet_node_path, 1, -1 do + table.insert(path, snippet_node_path[i]) + end + -- parent not in get_nodes_between. + table.insert(path, node_snippet) + + node = node_snippet.parent_node + end + + return path +end + +-- adjust rgravs of siblings of the node with indx child_from_indx in nodes. +local function nodelist_adjust_rgravs( + nodes, + child_from_indx, + child_endpoint, + direction, + rgrav, + nodes_adjacent +) + -- only handle siblings, not the node with child_from_indx itself. + local i = child_from_indx + local node = nodes[i] + while node do + local direction_node_endpoint = node.mark:get_endpoint(direction) + if util.pos_equal(direction_node_endpoint, child_endpoint) then + -- both endpoints of node are on top of child_endpoint (we wouldn't + -- be in the loop with `node` if the -direction-endpoint didn't + -- match), so update rgravs of the entire subtree to match rgrav + node:subtree_set_rgrav(rgrav) + else + -- either assume that they are adjacent, or check. + if + nodes_adjacent + or util.pos_equal( + node.mark:get_endpoint(-direction), + child_endpoint + ) + then + -- only the -direction-endpoint matches child_endpoint, adjust its + -- position and break the loop (don't need to look at any other + -- siblings). + node:subtree_set_pos_rgrav(child_endpoint, direction, rgrav) + end + break + end + + i = i + direction + node = nodes[i] + end +end + +return { + subsnip_init_children = subsnip_init_children, + init_child_positions_func = init_child_positions_func, + make_args_absolute = make_args_absolute, + wrap_args = wrap_args, + wrap_context = wrap_context, + get_nodes_between = get_nodes_between, + leave_nodes_between = leave_nodes_between, + enter_nodes_between = enter_nodes_between, + select_node = select_node, + print_dict = print_dict, + init_node_opts = init_node_opts, + snippet_extend_context = snippet_extend_context, + linkable_node = linkable_node, + binarysearch_pos = binarysearch_pos, + binarysearch_preference = binarysearch_preference, + refocus = refocus, + generic_extmarks_valid = generic_extmarks_valid, + snippettree_find_undamaged_node = snippettree_find_undamaged_node, + interactive_node = interactive_node, + root_path = root_path, + nodelist_adjust_rgravs = nodelist_adjust_rgravs, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/util/trig_engines.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/util/trig_engines.lua new file mode 100644 index 00000000..30da7245 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/nodes/util/trig_engines.lua @@ -0,0 +1,132 @@ +local jsregexp_compile_safe = require("luasnip.util.jsregexp") + +-- generate nil-opts-instances here, and save them. +-- This is to prevent generating 100s of the exact same function. +local default_match_pattern, default_match_plain, default_match_vim + +local function apply_common_opts(line_to_cursor, opts) + if opts and opts.max_len then + return line_to_cursor:sub(#line_to_cursor - opts.max_len + 1) + else + return line_to_cursor + end +end + +-- these functions get the line up to the cursor, the trigger, and then +-- determine whether the trigger matches the current line. +-- If the trigger does not match, the functions shall return nil, otherwise +-- the matching substring and the list of captures (empty table if there aren't +-- any). + +local function match_plain(_, opts) + if opts == nil then + return default_match_plain + end + + return function(line_to_cursor, trigger) + line_to_cursor = apply_common_opts(line_to_cursor, opts) + if + line_to_cursor:sub(#line_to_cursor - #trigger + 1, #line_to_cursor) + == trigger + then + -- no captures for plain trigger. + return trigger, {} + else + return nil + end + end +end +default_match_plain = match_plain(nil, {}) + +local function match_pattern(_, opts) + if opts == nil then + return default_match_pattern + end + + return function(line_to_cursor, trigger) + line_to_cursor = apply_common_opts(line_to_cursor, opts) + + -- look for match which ends at the cursor. + -- put all results into a list, there might be many capture-groups. + local find_res = { line_to_cursor:find(trigger .. "$") } + + if #find_res > 0 then + -- if there is a match, determine matching string, and the + -- capture-groups. + local captures = {} + -- find_res[1] is `from`, find_res[2] is `to` (which we already know + -- anyway). + local from = find_res[1] + local match = line_to_cursor:sub(from, #line_to_cursor) + -- collect capture-groups. + for i = 3, #find_res do + captures[i - 2] = find_res[i] + end + return match, captures + else + return nil + end + end +end +default_match_pattern = match_pattern(nil, {}) + +local ecma_engine +if jsregexp_compile_safe then + ecma_engine = function(trig, opts) + local trig_compiled, err_maybe = jsregexp_compile_safe(trig .. "$", "") + if not trig_compiled then + error(("Error while compiling regex: %s"):format(err_maybe)) + end + + return function(line_to_cursor, _) + line_to_cursor = apply_common_opts(line_to_cursor, opts) + + -- get first (very likely only, since we appended the "$") match. + local match = trig_compiled(line_to_cursor)[1] + if match then + -- return full match, and all groups. + return line_to_cursor:sub(match.begin_ind), match.groups + else + return nil + end + end + end +else + ecma_engine = function(x, opts) + return match_plain(x, opts) + end +end + +local function match_vim(_, opts) + if opts == nil then + return default_match_vim + end + + return function(line_to_cursor, trigger) + line_to_cursor = apply_common_opts(line_to_cursor, opts) + + local matchlist = vim.fn.matchlist(line_to_cursor, trigger .. "$") + if #matchlist > 0 then + local groups = {} + for i = 2, 10 do + -- PROBLEM: vim does not differentiate between an empty ("") + -- and a missing capture. + -- Since we need to differentiate between the two (Check `:h + -- luasnip-variables-lsp-variables`), we assume, here, that an + -- empty string is an unmatched group. + groups[i - 1] = matchlist[i] ~= "" and matchlist[i] or nil + end + return matchlist[1], groups + else + return nil + end + end +end +default_match_vim = match_vim(nil, {}) + +return { + plain = match_plain, + pattern = match_pattern, + ecma = ecma_engine, + vim = match_vim, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/enqueueable_operations.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/enqueueable_operations.lua new file mode 100644 index 00000000..7d56691c --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/enqueueable_operations.lua @@ -0,0 +1,35 @@ +local snippet_collection = require("luasnip.session.snippet_collection") + +local M = {} + +local refresh_enqueued = false +local next_refresh_fts = {} + +function M.refresh_notify(ft) + next_refresh_fts[ft] = true + + if not refresh_enqueued then + vim.schedule(function() + for enq_ft, _ in pairs(next_refresh_fts) do + snippet_collection.refresh_notify(enq_ft) + end + + next_refresh_fts = {} + refresh_enqueued = false + end) + + refresh_enqueued = true + end +end + +local clean_enqueued = false +function M.clean_invalidated() + if not clean_enqueued then + vim.schedule(function() + snippet_collection.clean_invalidated({ inv_limit = 100 }) + end) + end + clean_enqueued = true +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/init.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/init.lua new file mode 100644 index 00000000..ad1d6dfa --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/init.lua @@ -0,0 +1,52 @@ +-- used to store values like current nodes or the active node for autocommands. +local M = {} + +M.ft_redirect = {} +setmetatable(M.ft_redirect, { + __index = function(table, key) + -- no entry for this ft(key), set it to avoid calls on each expand for + -- this filetype. + local val = { key } + rawset(table, key, val) + return val + end, +}) + +M.current_nodes = {} +-- roots of snippet-trees, per-buffer. +-- snippet_roots[n] => list of snippet-roots in buffer n. +M.snippet_roots = setmetatable({}, { + -- create missing lists automatically. + __index = function(t, k) + local new_t = {} + rawset(t, k, new_t) + return new_t + end, +}) +M.ns_id = vim.api.nvim_create_namespace("Luasnip") +M.active_choice_nodes = {} + +-- only here for overview. +M.latest_load_ft = nil + +M.last_expand_snip = nil +M.last_expand_opts = nil + +-- jump_active is set while luasnip moves the cursor, prevents +-- (for example) updating dependents or deleting a snippet via +-- exit_out_of_region while jumping. +-- init with false, it will be set by (eg.) ls.jump(). +M.jump_active = false + +-- initial value, might be overwritten immediately. +-- No danger of overwriting user-config, since this has to be loaded to allow +-- overwriting. +M.config = require("luasnip.default_config") + +M.loaded_fts = {} + +function M.get_snip_env() + return M.config.snip_env +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/snippet_collection/init.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/snippet_collection/init.lua new file mode 100644 index 00000000..33f05c04 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/snippet_collection/init.lua @@ -0,0 +1,328 @@ +local source = require("luasnip.session.snippet_collection.source") +local u_table = require("luasnip.util.table") +local auto_creating_tables = + require("luasnip.util.auto_table").warn_depth_autotable +local session = require("luasnip.session") + +-- store snippets by some key. +-- also ordered by filetype, eg. +-- { +-- key = { +-- ft1 = {...}, +-- ft2 = {...} +-- } +-- } +local M = { + invalidated_count = 0, +} + +local by_key = {} + +-- stores snippets/autosnippets by priority. +local by_prio = { + snippets = { + -- stores sorted keys, eg 1=1000, 2=1010, 3=1020,..., used for + -- quick iterating. + order = {}, + }, + autosnippets = { + order = {}, + }, +} + +-- this isn't in util/util.lua due to circular dependencies. Would be cleaner +-- to include it there, but it's alright to keep here for now. +-- +-- this is linear, binary search would certainly be nicer, but for our +-- applications this should easily be enough. +local function insert_sorted_unique(t, k) + local tbl_len = #t + + local i = 1 + -- k does not yet exist in table, find first i so t[i] > k. + for _ = 1, tbl_len do + if t[i] > k then + break + end + i = i + 1 + end + + -- shift all t[j] with j > i back by one. + for j = tbl_len, i, -1 do + t[j + 1] = t[j] + end + + t[i] = k +end + +local by_prio_snippets_mt = { + __index = function(s, k) + -- make new tables as they are indexed + return auto_creating_tables(s, k, 3) + end, + __newindex = function(t, k, v) + -- update priority-order as well. + insert_sorted_unique(t.order, k) + rawset(t, k, v) + end, +} +-- metatable for the by_prio table used when by_prio.type[prio] is reset +-- create here so that it can be shared and only has to be created once +local prio_mt2 = { + __index = function(s, k) + -- make new tables as they are indexed + return auto_creating_tables(s, k, 2) + end, +} + +setmetatable(by_prio.snippets, by_prio_snippets_mt) +setmetatable(by_prio.autosnippets, by_prio_snippets_mt) + +-- iterate priorities, high to low. +local function prio_iter(type) + local order = by_prio[type].order + local i = #order + 1 + + return function() + i = i - 1 + if i > 0 then + return by_prio[type][order[i]] + end + return nil + end +end + +local by_ft = { + snippets = {}, + autosnippets = {}, +} + +local by_ft_snippets_mt = { + __index = function(s, k) + return auto_creating_tables(s, k, 2) + end, +} +setmetatable(by_ft.snippets, by_ft_snippets_mt) +setmetatable(by_ft.autosnippets, by_ft_snippets_mt) + +local by_id = setmetatable({}, { + -- make by_id-table weak (v). + -- this means it won't be necessary to explicitly nil values (snippets) in + -- this table. + __mode = "v", +}) + +-- ft: any filetype, optional. +function M.clear_snippets(ft) + if ft then + -- remove all ft-(auto)snippets for all priorities. + -- set to empty table so we won't need to rebuild/clear the order-table. + for _, prio in ipairs(by_prio.snippets.order) do + by_prio.snippets[prio][ft] = {} + end + for _, prio in ipairs(by_prio.autosnippets.order) do + by_prio.autosnippets[prio][ft] = {} + end + + by_ft.snippets[ft] = nil + by_ft.autosnippets[ft] = nil + + for key, _ in pairs(by_key) do + by_key[key][ft] = nil + end + else + -- remove all (auto)snippets for all priorities. + for _, prio in ipairs(by_prio.snippets.order) do + by_prio.snippets[prio] = {} + setmetatable(by_prio.snippets[prio], prio_mt2) + end + for _, prio in ipairs(by_prio.autosnippets.order) do + by_prio.autosnippets[prio] = {} + setmetatable(by_prio.autosnippets[prio], prio_mt2) + end + + by_key = {} + by_ft.snippets = {} + setmetatable(by_ft.snippets, by_ft_snippets_mt) + by_ft.autosnippets = {} + setmetatable(by_ft.autosnippets, by_ft_snippets_mt) + end +end + +function M.match_snippet(line, fts, type) + local expand_params + + for prio_by_ft in prio_iter(type) do + for _, ft in ipairs(fts) do + for _, snip in ipairs(prio_by_ft[ft] or {}) do + expand_params = snip:matches(line) + if expand_params then + -- return matching snippet and table with expand-parameters. + return snip, expand_params + end + end + end + end + + return nil +end + +local function without_invalidated(snippets_by_ft) + local new_snippets = {} + + for ft, ft_snippets in pairs(snippets_by_ft) do + new_snippets[ft] = {} + for _, snippet in ipairs(ft_snippets) do + if not snippet.invalidated then + table.insert(new_snippets[ft], snippet) + end + end + end + + return new_snippets +end + +function M.clean_invalidated(opts) + if opts.inv_limit then + if M.invalidated_count <= opts.inv_limit then + return + end + end + + -- remove invalidated snippets from all tables. + for _, type_snippets in pairs(by_prio) do + for key, prio_snippets in pairs(type_snippets) do + if key ~= "order" then + type_snippets[key] = without_invalidated(prio_snippets) + setmetatable(type_snippets[key], prio_mt2) + end + end + end + + for type, type_snippets in pairs(by_ft) do + by_ft[type] = without_invalidated(type_snippets) + setmetatable(by_ft[type], by_ft_snippets_mt) + end + + for key, key_snippets in pairs(by_key) do + by_key[key] = without_invalidated(key_snippets) + end + + M.invalidated_count = 0 +end + +local function invalidate_addables(addables_by_ft) + for _, addables in pairs(addables_by_ft) do + for _, addable in ipairs(addables) do + for _, expandable in ipairs(addable:retrieve_all()) do + expandable:invalidate() + end + end + end + M.clean_invalidated({ inv_limit = 100 }) +end + +local current_id = 0 +-- snippets like {ft1={}, ft2={}}, opts should be properly +-- initialized with default values. +function M.add_snippets(snippets, opts) + for ft, ft_snippets in pairs(snippets) do + for _, addable in ipairs(ft_snippets) do + for _, snip in ipairs(addable:retrieve_all()) do + local snip_prio = opts.override_priority + or (snip.priority and snip.priority) + or opts.default_priority + or 1000 + + -- if snippetType undefined by snippet, take default value from opts + local snip_type = snip.snippetType ~= nil and snip.snippetType + or opts.type + assert( + snip_type == "autosnippets" or snip_type == "snippets", + "snippetType must be either 'autosnippets' or 'snippets', was " + .. vim.inspect(snip_type) + ) + + local snip_ft = snip.filetype or ft + + snip.id = current_id + current_id = current_id + 1 + + -- do the insertion + table.insert(by_prio[snip_type][snip_prio][snip_ft], snip) + table.insert(by_ft[snip_type][snip_ft], snip) + by_id[snip.id] = snip + + -- set source if it is available. + if snip._source then + source.set(snip, snip._source) + end + end + end + end + + if opts.key then + if by_key[opts.key] then + invalidate_addables(by_key[opts.key]) + end + by_key[opts.key] = snippets + end +end + +-- specialized copy functions to not loose performance on ifs when copying +-- and to be able to specify when pairs or ipairs is used +local function copy_by_ft_type_ft(tab) + local r = {} + for k, v in ipairs(tab) do + r[k] = v + end + return r +end +local function copy_by_ft_type(tab) + local r = {} + for k, v in pairs(tab) do + r[k] = copy_by_ft_type_ft(v) + end + return r +end + +-- ft may be nil, type not. +function M.get_snippets(ft, type) + if ft then + return copy_by_ft_type_ft(by_ft[type][ft]) + else + return copy_by_ft_type(by_ft[type]) + end +end + +function M.get_id_snippet(id) + return by_id[id] +end + +local function get_all_snippet_fts() + local ft_set = {} + for ft, _ in pairs(by_ft.snippets) do + ft_set[ft] = true + end + for ft, _ in pairs(by_ft.autosnippets) do + ft_set[ft] = true + end + + return u_table.set_to_list(ft_set) +end + +-- modules that want to call refresh_notify probably also want to notify others +-- of adding those snippets => put those functions into the same module. +function M.refresh_notify(ft_or_nil) + local fts = ft_or_nil and { ft_or_nil } or get_all_snippet_fts() + + for _, ft in ipairs(fts) do + session.latest_load_ft = ft + vim.api.nvim_exec_autocmds( + "User", + { pattern = "LuasnipSnippetsAdded", modeline = false } + ) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/snippet_collection/source.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/snippet_collection/source.lua new file mode 100644 index 00000000..9f3319ef --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/session/snippet_collection/source.lua @@ -0,0 +1,38 @@ +local id_to_source = {} + +local M = {} + +function M.from_debuginfo(debuginfo) + assert(debuginfo.source, "debuginfo contains source") + assert( + debuginfo.source:match("^@"), + "debuginfo-source is a file: " .. debuginfo.source + ) + + return { + -- omit leading '@'. + file = debuginfo.source:sub(2), + line = debuginfo.currentline, + } +end + +function M.from_location(file, opts) + assert(file, "source needs file") + opts = opts or {} + + return { file = file, line = opts.line, line_end = opts.line_end } +end + +function M.set(snippet, source) + -- snippets only get their id after being added, make sure this is the + -- case. + assert(snippet.id, "snippet has an id") + + id_to_source[snippet.id] = source +end + +function M.get(snippet) + return id_to_source[snippet.id] +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/snippets.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/snippets.lua new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/_builtin_vars.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/_builtin_vars.lua new file mode 100644 index 00000000..8287182f --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/_builtin_vars.lua @@ -0,0 +1,208 @@ +local util = require("luasnip.util.util") +local select_util = require("luasnip.util.select") +local time_util = require("luasnip.util.time") +local lazy_vars = {} + +-- Variables defined in https://code.visualstudio.com/docs/editor/userdefinedsnippets#_variables + +-- Inherited from TextMate +function lazy_vars.TM_FILENAME() + return vim.fn.expand("%:t") +end + +function lazy_vars.TM_FILENAME_BASE() + return vim.fn.expand("%:t:s?\\.[^\\.]\\+$??") +end + +function lazy_vars.TM_DIRECTORY() + return vim.fn.expand("%:p:h") +end + +function lazy_vars.TM_FILEPATH() + return vim.fn.expand("%:p") +end + +-- Vscode only + +function lazy_vars.CLIPBOARD() -- The contents of your clipboard + return vim.fn.getreg('"', 1, true) +end + +local function buf_to_ws_part() + local LSP_WORSKPACE_PARTS = "LSP_WORSKPACE_PARTS" + local ok, ws_parts = pcall(vim.api.nvim_buf_get_var, 0, LSP_WORSKPACE_PARTS) + if not ok then + local file_path = vim.fn.expand("%:p") + + for _, ws in pairs(vim.lsp.buf.list_workspace_folders()) do + if file_path:find(ws, 1, true) == 1 then + ws_parts = { ws, file_path:sub(#ws + 2, -1) } + break + end + end + -- If it can't be extracted from lsp, then we use the file path + if not ok and not ws_parts then + ws_parts = { vim.fn.expand("%:p:h"), vim.fn.expand("%:p:t") } + end + vim.api.nvim_buf_set_var(0, LSP_WORSKPACE_PARTS, ws_parts) + end + return ws_parts +end + +function lazy_vars.RELATIVE_FILEPATH() -- The relative (to the opened workspace or folder) file path of the current document + return buf_to_ws_part()[2] +end + +function lazy_vars.WORKSPACE_FOLDER() -- The path of the opened workspace or folder + return buf_to_ws_part()[1] +end + +function lazy_vars.WORKSPACE_NAME() -- The name of the opened workspace or folder + local parts = vim.split(buf_to_ws_part()[1] or "", "[\\/]") + return parts[#parts] +end + +-- DateTime Related +function lazy_vars.CURRENT_YEAR() + return os.date("%Y") +end + +function lazy_vars.CURRENT_YEAR_SHORT() + return os.date("%y") +end + +function lazy_vars.CURRENT_MONTH() + return os.date("%m") +end + +function lazy_vars.CURRENT_MONTH_NAME() + return os.date("%B") +end + +function lazy_vars.CURRENT_MONTH_NAME_SHORT() + return os.date("%b") +end + +function lazy_vars.CURRENT_DATE() + return os.date("%d") +end + +function lazy_vars.CURRENT_DAY_NAME() + return os.date("%A") +end + +function lazy_vars.CURRENT_DAY_NAME_SHORT() + return os.date("%a") +end + +function lazy_vars.CURRENT_HOUR() + return os.date("%H") +end + +function lazy_vars.CURRENT_MINUTE() + return os.date("%M") +end + +function lazy_vars.CURRENT_SECOND() + return os.date("%S") +end + +function lazy_vars.CURRENT_SECONDS_UNIX() + return tostring(os.time()) +end + +function lazy_vars.CURRENT_TIMEZONE_OFFSET() + return time_util + .get_timezone_offset(os.time()) + :gsub("([+-])(%d%d)(%d%d)$", "%1%2:%3") +end + +-- For inserting random values + +math.randomseed(os.time()) + +function lazy_vars.RANDOM() + return string.format("%06d", math.random(999999)) +end + +function lazy_vars.RANDOM_HEX() + return string.format("%06x", math.random(16777216)) --16^6 +end + +function lazy_vars.UUID() + local random = math.random + local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" + local out + local function subs(c) + local v = (((c == "x") and random(0, 15)) or random(8, 11)) + return string.format("%x", v) + end + + out = template:gsub("[xy]", subs) + return out +end + +function lazy_vars.LINE_COMMENT() + return util.buffer_comment_chars()[1] +end + +function lazy_vars.BLOCK_COMMENT_START() + return util.buffer_comment_chars()[2] +end + +function lazy_vars.BLOCK_COMMENT_END() + return util.buffer_comment_chars()[3] +end + +-- These are the vars that have to be populated once the snippet starts to avoid any issue +local function eager_vars(info) + local vars = {} + local pos = info.pos + vars.TM_CURRENT_LINE = + vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false)[1] + vars.TM_CURRENT_WORD = util.word_under_cursor(pos, vars.TM_CURRENT_LINE) + vars.TM_LINE_INDEX = tostring(pos[1]) + vars.TM_LINE_NUMBER = tostring(pos[1] + 1) + vars.LS_SELECT_RAW, vars.LS_SELECT_DEDENT, vars.TM_SELECTED_TEXT = + select_util.retrieve() + -- These are for backward compatibility, for now on all builtins that are not part of TM_ go in LS_ + vars.SELECT_RAW, vars.SELECT_DEDENT = + vars.LS_SELECT_RAW, vars.LS_SELECT_DEDENT + for i, cap in ipairs(info.captures) do + vars["LS_CAPTURE_" .. i] = cap + end + vars.LS_TRIGGER = info.trigger + return vars +end + +local builtin_ns = { SELECT = true, LS = true } + +for name, _ in pairs(lazy_vars) do + local parts = vim.split(name, "_") + if #parts > 1 then + builtin_ns[parts[1]] = true + end +end + +local _is_table = { + TM_SELECTED_TEXT = true, + SELECT_RAW = true, + SELECT_DEDENT = true, +} + +return { + is_table = function(key) + -- variables generated at runtime by treesitter-postfix. + if + key:match("LS_TSCAPTURE_.*") + or key == "LS_TSPOSTFIX_MATCH" + or key == "LS_TSPOSTFIX_DATA" + then + return true + end + return _is_table[key] or false + end, + vars = lazy_vars, + init = eager_vars, + builtin_ns = builtin_ns, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/auto_table.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/auto_table.lua new file mode 100644 index 00000000..3a9858c0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/auto_table.lua @@ -0,0 +1,54 @@ +-- depth specifies how many levels under this table should be allowed to index +-- throug this metamethod +-- set depth to 0 to disable checking +-- Acknowledgment: This is (maybe more than) inspired by +-- https://lua-users.org/wiki/AutomagicTables so special thanks to +-- Thomas Wrensch and Rici Lake for sharing their ideas on this topic. +local function auto_creating_tables_warn_depth(self, key, depth) + local t = {} + assert(depth ~= 1, "don't index at that level") + setmetatable(t, { + -- creating a new function on each time (could be shared) isn't that + -- nice. Nonetheless this shouldn't be too bad, as these are only + -- created twice (auto+snippet) per ft and twice for each prio,ft + -- combination + __index = function(s, k) + return auto_creating_tables_warn_depth(s, k, depth - 1) + end, + }) + self[key] = t + return t +end + +local function auto_creating_tables(self, key, depth) + local t = {} + if depth ~= 1 then + setmetatable(t, { + __index = function(s, k) + return auto_creating_tables(s, k, depth - 1) + end, + }) + end + self[key] = t + return t +end + +local M = {} + +function M.autotable(max_depth, opts) + opts = opts or {} + local warn = vim.F.if_nil(opts.warn, false) + + local auto_table_func = warn and auto_creating_tables_warn_depth + or auto_creating_tables + + return setmetatable({}, { + __index = function(s, k) + return auto_table_func(s, k, max_depth - 1) + end, + }) +end + +M.warn_depth_autotable = auto_creating_tables_warn_depth + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/dict.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/dict.lua new file mode 100644 index 00000000..3ce60ab6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/dict.lua @@ -0,0 +1,63 @@ +local Dictionary = {} + +local function new(o) + return setmetatable(o or {}, { + __index = Dictionary, + }) +end + +function Dictionary:set(path, value) + -- Insp(path) + -- print("val: ", value) + local current_table = self + for i = 1, #path - 1 do + local crt_key = path[i] + if not current_table[crt_key] then + current_table[crt_key] = {} + end + current_table = current_table[crt_key] + end + current_table[path[#path]] = value +end + +function Dictionary:get(path) + local current_table = self + for _, v in ipairs(path) do + if not current_table[v] then + return nil + end + current_table = current_table[v] + end + -- may not be a table. + return current_table +end + +function Dictionary:find_all(path, key) + local res = {} + local to_search = { self:get(path) } + if not to_search[1] then + return nil + end + + -- weird hybrid of depth- and breadth-first search for key, collect values in res. + local search_index = 1 + local search_size = 1 + while search_size > 0 do + for k, v in pairs(to_search[search_index]) do + if k == key then + res[#res + 1] = v + else + to_search[search_index + search_size] = v + search_size = search_size + 1 + end + end + search_index = search_index + 1 + search_size = search_size - 1 + end + + return res +end + +return { + new = new, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/directed_graph.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/directed_graph.lua new file mode 100644 index 00000000..4c906328 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/directed_graph.lua @@ -0,0 +1,263 @@ +local tbl_util = require("luasnip.util.table") +local autotable = require("luasnip.util.auto_table").autotable + +local DirectedGraph = {} + +-- set __index directly in DirectedGraph, otherwise each DirectedGraph-object would have its' +-- own metatable (one more table around), which would not be bad, but +-- unnecessary nonetheless. +DirectedGraph.__index = DirectedGraph + +local Vertex = {} +Vertex.__index = Vertex + +local function new_graph() + return setmetatable({ + -- all vertices of this graph. + vertices = {}, + }, DirectedGraph) +end +local function new_vertex() + return setmetatable({ + -- vertices this vertex has an edge from/to. + -- map[vert -> bool] + incoming_edge_verts = {}, + outgoing_edge_verts = {}, + }, Vertex) +end + +---Add new vertex to the DirectedGraph +---@return table: the generated vertex, to be used in `set_edge`, for example. +function DirectedGraph:add_vertex() + local vert = new_vertex() + table.insert(self.vertices, vert) + return vert +end + +---Remove vertex and its edges from DirectedGraph. +---@param v table: the vertex. +function DirectedGraph:clear_vertex(v) + if not vim.tbl_contains(self.vertices, v) then + -- vertex does not belong to this graph. Maybe throw error/make + -- condition known? + return + end + -- remove outgoing.. + for outgoing_edge_vert, _ in pairs(v.outgoing_edge_verts) do + self:clear_edge(v, outgoing_edge_vert) + end + -- ..and incoming edges with v from the graph. + for incoming_edge_vert, _ in pairs(v.incoming_edge_verts) do + self:clear_edge(incoming_edge_vert, v) + end +end + +---Add edge from v1 to v2 +---@param v1 table: vertex in the graph. +---@param v2 table: vertex in the graph. +function DirectedGraph:set_edge(v1, v2) + if v1.outgoing_edge_verts[v2] then + -- the edge already exists. Don't return an error, for now. + return + end + -- link vertices. + v1.outgoing_edge_verts[v2] = true + v2.incoming_edge_verts[v1] = true +end + +---Remove edge from v1 to v2 +---@param v1 table: vertex in the graph. +---@param v2 table: vertex in the graph. +function DirectedGraph:clear_edge(v1, v2) + assert(v1.outgoing_edge_verts[v2], "nonexistent edge cannot be removed.") + -- unlink vertices. + v1.outgoing_edge_verts[v2] = nil + v2.incoming_edge_verts[v1] = nil +end + +---Find and return verts with indegree 0. +---@param graph table: graph. +---@return table of vertices. +local function source_verts(graph) + local indegree_0_verts = {} + for _, vert in ipairs(graph.vertices) do + if vim.tbl_count(vert.incoming_edge_verts) == 0 then + table.insert(indegree_0_verts, vert) + end + end + return indegree_0_verts +end + +---Copy graph. +---@param graph table: graph. +---@return table,table: copied graph and table for mapping copied node to +---original node(original_vert[some_vert_from_copy] -> corresponding original +---vert). +local function graph_copy(graph) + local copy = vim.deepcopy(graph) + local original_vert = {} + for i, copy_vert in ipairs(copy.vertices) do + original_vert[copy_vert] = graph.vertices[i] + end + return copy, original_vert +end + +---Generate a (it's not necessarily unique) topological sorting of this graphs +---vertices. +---https://en.wikipedia.org/wiki/Topological_sorting, this uses Kahn's Algorithm. +---@return table|nil: sorted vertices of this graph, nil if there is no +---topological sorting (eg. if the graph has a cycle). +function DirectedGraph:topological_sort() + local sorting = {} + + -- copy self so edges can be removed without affecting the real graph. + local graph, original_vert = graph_copy(self) + + -- find vertices without incoming edges. + -- invariant: at the end of each step, sources contains all vertices + -- without incoming edges. + local sources = source_verts(graph) + while #sources > 0 do + -- pop v from sources. + local v = sources[#sources] + sources[#sources] = nil + + -- v has no incoming edges, it can be next in the sorting. + -- important!! don't insert v, insert the corresponding vertex from the + -- original graph. The copied vertices are not known outside this + -- function (alternative: maybe return indices in graph.vertices?). + table.insert(sorting, original_vert[v]) + + -- find vertices which, if v is removed from graph, have no more incoming edges. + -- Those are sources after v is removed. + for outgoing_edge_vert, _ in pairs(v.outgoing_edge_verts) do + -- there is one edge, it has to be from v. + if vim.tbl_count(outgoing_edge_vert.incoming_edge_verts) == 1 then + table.insert(sources, outgoing_edge_vert) + end + end + + -- finally: remove v from graph and sources. + graph:clear_vertex(v) + end + + if #sorting ~= #self.vertices then + -- error: the sorting does not contain all vertices -> the graph has a cycle. + return nil + end + return sorting +end + +-- return all vertices reachable from this one. +function DirectedGraph:connected_component(vert, edge_direction) + local outgoing_vertices_field = edge_direction == "Backward" + and "incoming_edge_verts" + or "outgoing_edge_verts" + + local visited = {} + local to_visit = { [vert] = true } + + -- get any value in table. + local next_vert, _ = next(to_visit, nil) + while next_vert do + to_visit[next_vert] = nil + visited[next_vert] = true + + for neighbor, _ in pairs(next_vert[outgoing_vertices_field]) do + if not visited[neighbor] then + to_visit[neighbor] = true + end + end + + next_vert, _ = next(to_visit, nil) + end + + return tbl_util.set_to_list(visited) +end + +-- Very useful to have a graph where vertices are associated with some label. +-- This just proxies DirectedGraph and swaps labels and vertices in +-- parameters/return-values. +local LabeledDigraph = {} +LabeledDigraph.__index = LabeledDigraph + +local function new_labeled_graph() + return setmetatable({ + graph = new_graph(), + label_to_vert = {}, + vert_to_label = {}, + -- map label -> origin-vert -> dest-vert + label_to_verts = autotable(3, { warn = false }), + -- map edge (origin,dest) to set of labels. + verts_to_label = autotable(3, { warn = false }), + }, LabeledDigraph) +end + +function LabeledDigraph:set_vertex(label) + if self.label_to_vert[label] then + -- don't add same label again. + return + end + + local vert = self.graph:add_vertex() + self.label_to_vert[label] = vert + self.vert_to_label[vert] = label +end + +function LabeledDigraph:set_edge(lv1, lv2, edge_label) + -- ensure vertices exist. + self:set_vertex(lv1) + self:set_vertex(lv2) + + if self.verts_to_label[lv1][lv2][edge_label] then + -- edge exists, do nothing. + return + end + + -- determine before setting the lv1-lv2-edge. + local other_edge_exists = next(self.verts_to_label[lv1][lv2], nil) ~= nil + + -- store both associations; + self.verts_to_label[lv1][lv2][edge_label] = true + self.label_to_verts[edge_label][lv1][lv2] = true + + if other_edge_exists then + -- there already exists an entry for this edge, no need to add it to + -- the graph. + return + end + self.graph:set_edge(self.label_to_vert[lv1], self.label_to_vert[lv2]) +end + +function LabeledDigraph:clear_edge(lv1, lv2, ledge) + if not self.verts_to_label[lv1][lv2][ledge] then + -- edge does not exist, do nothing. + return + end + + self.verts_to_label[lv1][lv2][ledge] = nil + if next(self.verts_to_label[lv1][lv2]) == nil then + -- removed last edge between v1, v2 -> remove edge in graph. + self.graph:clear_edge(self.label_to_vert[lv1], self.label_to_vert[lv2]) + end +end +function LabeledDigraph:clear_edges(label) + for lv1, lv2 in pairs(self.label_to_verts[label]) do + self:clear_edge(lv1, lv2, label) + end + -- set to nil, not {}, so autotable can work its magic. + self.label_to_verts[label] = nil +end + +function LabeledDigraph:connected_component(lv, edge_direction) + self:set_vertex(lv) + + return vim.tbl_map(function(v) + return self.vert_to_label[v] + end, self.graph:connected_component(self.label_to_vert[lv], edge_direction)) +end + +return { + new = new_graph, + new_labeled = new_labeled_graph, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/environ.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/environ.lua new file mode 100644 index 00000000..bd73d1a2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/environ.lua @@ -0,0 +1,188 @@ +local builtin_namespace = require("luasnip.util._builtin_vars") + +local function tbl_to_lazy_env(tbl) + local function wrapper(varname) + local val_ = tbl[varname] + if type(val_) == "function" then + return val_() + end + return val_ + end + + return wrapper +end + +local namespaces = {} + +-- Namespaces allow users to define their own environmet variables +local function _resolve_namespace_var(full_varname) + local parts = vim.split(full_varname, "_") + local nmsp = namespaces[parts[1]] + + local varname + -- Is safe to fallback to the buitin-unnamed namespace as the checks in _env_namespace + -- don't allow overriding those vars + if nmsp then + varname = full_varname:sub(#parts[1] + 2) + else + nmsp = namespaces[""] + varname = full_varname + end + return nmsp, varname +end + +local Environ = {} + +function Environ.is_table(var_fullname) + local nmsp, varname = _resolve_namespace_var(var_fullname) + ---@diagnostic disable-next-line: need-check-nil + return nmsp.is_table(varname) +end + +function Environ:new(info, o) + o = o or {} + setmetatable(o, self) + vim.list_extend(info, info.pos) -- For compatibility with old user defined namespaces + + for ns_name, ns in pairs(namespaces) do + local eager_vars = {} + if ns.init then + eager_vars = ns.init(info) + end + for _, eager in ipairs(ns.eager) do + if not eager_vars[eager] then + eager_vars[eager] = ns.vars(eager) + end + end + + local prefix = "" + if ns_name ~= "" then + prefix = ns_name .. "_" + end + + for name, val in pairs(eager_vars) do + name = prefix .. name + rawset(o, name, val) + end + end + return o +end + +local builtin_ns_names = vim.inspect(vim.tbl_keys(builtin_namespace.builtin_ns)) + +local function _env_namespace(name, opts) + assert( + opts and type(opts) == "table", + ("Your opts for '%s' has to be a table"):format(name) + ) + assert( + opts.init or opts.vars, + ("Your opts for '%s' needs init or vars"):format(name) + ) + + -- namespace.eager → ns.vars + assert( + not opts.eager or opts.vars, + ("Your opts for %s can't set a `eager` field without the `vars` one"):format( + name + ) + ) + + opts.eager = opts.eager or {} + local multiline_vars = opts.multiline_vars or false + + local type_of_it = type(multiline_vars) + + assert( + type_of_it == "table" + or type_of_it == "boolean" + or type_of_it == "function", + ("Your opts for %s can't have `multiline_vars` of type %s"):format( + name, + type_of_it + ) + ) + + -- If type is function we don't have to override it + if type_of_it == "table" then + local is_table_set = {} + + for _, key in ipairs(multiline_vars) do + is_table_set[key] = true + end + + opts.is_table = function(key) + return is_table_set[key] or false + end + elseif type_of_it == "boolean" then + opts.is_table = function(_) + return multiline_vars + end + else -- is a function + opts.is_table = multiline_vars + end + + if opts.vars and type(opts.vars) == "table" then + opts.vars = tbl_to_lazy_env(opts.vars) + end + + namespaces[name] = opts +end + +_env_namespace("", builtin_namespace) + +-- The exposed api checks for the names to avoid accidental overrides +function Environ.env_namespace(name, opts) + assert( + name:match("^[a-zA-Z][a-zA-Z0-9]*$"), + ("You can't create a namespace with name '%s' it has to contain only and at least a non alpha-numeric character"):format( + name + ) + ) + assert( + not builtin_namespace.builtin_ns[name], + ("You can't create a namespace with name '%s' because is one one of %s"):format( + name, + builtin_ns_names + ) + ) + + _env_namespace(name, opts) +end + +function Environ:__index(key) + local nmsp, varname = _resolve_namespace_var(key) + ---@diagnostic disable-next-line: need-check-nil + local val = nmsp.vars(varname) + + rawset(self, key, val) + + return val +end + +function Environ:override(env, new_env) + for k, v in pairs(new_env) do + env[k] = v + end +end + +local fake_env = { + __index = function(tbl, key) + local var + if Environ.is_table(key) then + var = { "$" .. key } + else + var = "$" .. key + end + rawset(tbl, key, var) + return var + end, +} + +function Environ.fake() + local o = {} + setmetatable(o, fake_env) + return o +end + +return Environ diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/events.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/events.lua new file mode 100644 index 00000000..a7ba52d4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/events.lua @@ -0,0 +1,18 @@ +local node_names = require("luasnip.util.types").names_pascal_case + +return { + enter = 1, + leave = 2, + change_choice = 3, + pre_expand = 4, + to_string = function(node_type, event_id) + if event_id == 3 then + return "ChangeChoice" + elseif event_id == 4 then + return "PreExpand" + else + return node_names[node_type] + .. (event_id == 1 and "Enter" or "Leave") + end + end, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/ext_opts.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/ext_opts.lua new file mode 100644 index 00000000..109bd6fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/ext_opts.lua @@ -0,0 +1,159 @@ +-- eventually turn ext_opts into proper objects, mainly for +-- default-construction eg. assured `complete`. +-- +-- child_*-functions perform the same operation as theiry non-child +-- counterparts, but on a collection (eg. +-- `{[types.insertNode={...}, [types.textNode]= {...}]}`) of ext_opts. + +local types = require("luasnip.util.types") + +-- vim.tbl_extend always creates a new table, but doesn't accept nil, so we +-- always pass this empty table, which will (has to!) stay empty. +local shared_empty_table = {} + +local states = { + "active", + "passive", + "snippet_passive", + "visited", + "unvisited", +} + +-- opts: child_ext_opts, have to have hl_group set for all combinations of +-- node-type and active,passive,snippet_passive,visited,unvisited. +local function clear_invalid(opts) + --stylua: ignore start + for _, node_type in pairs(types.node_types) do + for _, state in ipairs(states) do + local state_hl_group = opts[node_type][state].hl_group + + opts[node_type][state].hl_group = + vim.fn.hlexists(state_hl_group) == 1 and state_hl_group + or nil + end + end + --stylua: ignore end +end + +local function _complete_ext_opts(ext_opts) + if not ext_opts then + ext_opts = {} + end + ext_opts.snippet_passive = ext_opts.snippet_passive or {} + ext_opts.passive = vim.tbl_extend( + "keep", + ext_opts.passive or shared_empty_table, + ext_opts.snippet_passive or shared_empty_table + ) + -- both unvisited and visited inherit from passive. + ext_opts.unvisited = vim.tbl_extend( + "keep", + ext_opts.unvisited or shared_empty_table, + ext_opts.passive or shared_empty_table + ) + ext_opts.visited = vim.tbl_extend( + "keep", + ext_opts.visited or shared_empty_table, + ext_opts.passive or shared_empty_table + ) + -- active inherits from visited. + ext_opts.active = vim.tbl_extend( + "keep", + ext_opts.active or shared_empty_table, + ext_opts.visited or shared_empty_table + ) + + for _, state in ipairs(states) do + --stylua: ignore start + if ext_opts[state].hl_group and not + ext_opts[state].priority then + ext_opts[state].priority = 0 + end + --stylua: ignore end + end + + return ext_opts +end + +-- active inherits unset values from passive, which in turn inherits from +-- snippet_passive. +-- Also make sure that all keys have a table, and are not nil! +local function child_complete(ext_opts) + for _, node_type in pairs(types.node_types) do + ext_opts[node_type] = _complete_ext_opts(ext_opts[node_type]) + end + ext_opts.base_prio = 0 + + return ext_opts +end + +local function complete(ext_opts) + _complete_ext_opts(ext_opts) + ext_opts.base_prio = 0 + + return ext_opts +end + +-- in-place adds opts of b to a, doesn't override. +-- a/b: completed ext_opts, not nil. +local function extend(opts_a, opts_b) + for _, state in ipairs(states) do + opts_a[state] = vim.tbl_extend("keep", opts_a[state], opts_b[state]) + end + + return opts_a +end + +-- in-place adds opts of b to a, doesn't override. +-- a/b: completed child_ext_opts, not nil. +local function child_extend(opts_a, opts_b) + for _, node_type in ipairs(types.node_types) do + extend(opts_a[node_type], opts_b[node_type]) + end + + return opts_a +end + +local function increase_prio(opts, inc) + -- increase only if there is a priority. + for _, state in ipairs(states) do + opts[state].priority = opts[state].priority + and (opts[state].priority + inc) + end +end + +-- ext_opts-priorities are defined relative to some base-priority. +-- As nvim_api_buf_set_extmark takes absolute values only, we have to +-- set the absolute priorities, which can vary depending on nesting-level +-- of a given snippet, during runtime, by increasing the relative priorities by +-- either the conf.base_prio or the base-prio used in the previous nesting-level. +local function set_abs_prio(opts, new_base_prio) + -- undo previous increase. + -- base_prio is initialized with 0. + local prio_offset = new_base_prio - opts.base_prio + opts.base_prio = new_base_prio + increase_prio(opts, prio_offset) + + return opts +end + +local function child_set_abs_prio(opts, new_base_prio) + -- undo previous increase. + -- base_prio is initialized with 0. + local prio_offset = new_base_prio - opts.base_prio + opts.base_prio = new_base_prio + for _, node_type in ipairs(types.node_types) do + increase_prio(opts[node_type], prio_offset) + end + return opts +end + +return { + clear_invalid = clear_invalid, + complete = complete, + child_complete = child_complete, + extend = extend, + child_extend = child_extend, + set_abs_prio = set_abs_prio, + child_set_abs_prio = child_set_abs_prio, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/extend_decorator.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/extend_decorator.lua new file mode 100644 index 00000000..f0723819 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/extend_decorator.lua @@ -0,0 +1,82 @@ +local M = {} + +-- map fn -> {arg_indx = int, extend = fn}[] +local function_properties = setmetatable({}, { __mode = "k" }) + +local function default_extend(arg, extend) + return vim.tbl_extend("keep", arg or {}, extend or {}) +end + +---Create a new decorated version of `fn`. +---@param fn The function to create a decorator for. +---@vararg The values to extend with. These should match the descriptions passed +---in `register`: +---```lua +---local function somefn(arg1, arg2, opts1, opts2) +---... +---end +---register(somefn, {arg_indx=4}, {arg_indx=3}) +---apply(somefn, +--- {key = "opts2 is extended with this"}, +--- {key = "and opts1 with this"}) +---``` +---@return function: The decorated function. +function M.apply(fn, ...) + local extend_properties = function_properties[fn] + assert( + extend_properties, + "Cannot extend this function, it was not registered! Check :h luasnip-extend_decorator for more infos." + ) + + local extend_values = { ... } + + local decorated_fn = function(...) + local direct_args = { ... } + + -- override values of direct argument. + for i, ep in ipairs(extend_properties) do + local arg_indx = ep.arg_indx + + -- still allow overriding with directly-passed keys. + direct_args[arg_indx] = + ep.extend(direct_args[arg_indx], extend_values[i]) + end + + -- important: http://www.lua.org/manual/5.3/manual.html#3.4 + -- Passing arguments after the results from `unpack` would mess all this + -- up. + return fn(unpack(direct_args)) + end + + -- we know how to extend the decorated function! + function_properties[decorated_fn] = extend_properties + + return decorated_fn +end + +---Prepare a function for usage with extend_decorator. +---To create a decorated function which extends `opts`-style tables passed to it, we need to know +--- 1. which parameter-position the opts are in and +--- 2. how to extend them. +---@param fn function: the function that should be registered. +---@vararg tables. Each describes how to extend one parameter to `fn`. +---The tables accept the following keys: +--- - arg_indx, number (required): the position of the parameter to override. +--- - extend, fn(arg, extend_value) -> effective_arg (optional): this function +--- is used to extend the args passed to the decorated function. +--- It defaults to a function which just extends the the arg-table with the +--- extend-table. +--- This extend-behaviour is adaptable to accomodate `s`, where the first +--- argument may be string or table. +function M.register(fn, ...) + local fn_eps = { ... } + + -- make sure ep.extend is set. + for _, ep in ipairs(fn_eps) do + ep.extend = ep.extend or default_extend + end + + function_properties[fn] = fn_eps +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/functions.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/functions.lua new file mode 100644 index 00000000..3fc1ad7d --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/functions.lua @@ -0,0 +1,49 @@ +local sn = require("luasnip.nodes.snippet").SN +local t = require("luasnip.nodes.textNode").T + +return { + var = function(_, _, node, text) + local v = node.parent.snippet.env[text] + if type(v) == "table" then + -- Avoid issues with empty vars + if #v > 0 then + return v + else + return { "" } + end + else + return { v } + end + end, + better_var = function(varname) + return function(_, parent) + local v = parent.snippet.env[varname] + if type(v) == "table" then + -- Avoid issues with empty vars + if #v > 0 then + return v + else + return { "" } + end + else + return { v } + end + end + end, + eval_vim_dynamic = function(vimstring) + return function() + -- 'echo'd string is returned to lua. + return sn(nil, { + t( + vim.split( + vim.api.nvim_exec("echo " .. vimstring, true), + "\n" + ) + ), + }) + end + end, + copy = function(args) + return args[1] + end, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/jsonc.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/jsonc.lua new file mode 100644 index 00000000..d72dad5a --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/jsonc.lua @@ -0,0 +1,469 @@ +-- Taken from https://github.com/actboy168/json.lua + +-- MIT License +-- +-- Copyright (c) 2020 actboy168 +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in all +-- copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. + +local type = type +local next = next +local error = error +local tonumber = tonumber +local string_char = string.char +local string_byte = string.byte +local string_find = string.find +local string_match = string.match +local string_gsub = string.gsub +local string_sub = string.sub +local string_format = string.format + +local utf8_char + +if _VERSION == "Lua 5.1" or _VERSION == "Lua 5.2" then + local math_floor = math.floor + function utf8_char(c) + if c <= 0x7f then + return string_char(c) + elseif c <= 0x7ff then + return string_char(math_floor(c / 64) + 192, c % 64 + 128) + elseif c <= 0xffff then + return string_char( + math_floor(c / 4096) + 224, + math_floor(c % 4096 / 64) + 128, + c % 64 + 128 + ) + elseif c <= 0x10ffff then + return string_char( + math_floor(c / 262144) + 240, + math_floor(c % 262144 / 4096) + 128, + math_floor(c % 4096 / 64) + 128, + c % 64 + 128 + ) + end + error(string_format("invalid UTF-8 code '%x'", c)) + end +else + utf8_char = utf8.char +end + +local encode_escape_map = { + ['"'] = '\\"', + ["\\"] = "\\\\", + ["/"] = "\\/", + ["\b"] = "\\b", + ["\f"] = "\\f", + ["\n"] = "\\n", + ["\r"] = "\\r", + ["\t"] = "\\t", +} + +local decode_escape_set = {} +local decode_escape_map = {} +for k, v in next, encode_escape_map do + decode_escape_map[v] = k + decode_escape_set[string_byte(v, 2)] = true +end + +local statusBuf +local statusPos +local statusTop +local statusAry = {} +local statusRef = {} + +local function find_line() + local line = 1 + local pos = 1 + while true do + local f, _, nl1, nl2 = string_find(statusBuf, "([\n\r])([\n\r]?)", pos) + if not f then + return line, statusPos - pos + 1 + end + local newpos = f + ((nl1 == nl2 or nl2 == "") and 1 or 2) + if newpos > statusPos then + return line, statusPos - pos + 1 + end + pos = newpos + line = line + 1 + end +end + +local function decode_error(msg) + error(string_format("ERROR: %s at line %d col %d", msg, find_line()), 2) +end + +local function get_word() + return string_match(statusBuf, "^[^ \t\r\n%]},]*", statusPos) +end + +local function skip_comment(b) + if + b ~= 47 --[[ '/' ]] + then + return + end + local c = string_byte(statusBuf, statusPos + 1) + if + c == 42 --[[ '*' ]] + then + -- block comment + local pos = string_find(statusBuf, "*/", statusPos) + if pos then + statusPos = pos + 2 + else + statusPos = #statusBuf + 1 + end + return true + elseif + c == 47 --[[ '/' ]] + then + -- line comment + local pos = string_find(statusBuf, "[\r\n]", statusPos) + if pos then + statusPos = pos + else + statusPos = #statusBuf + 1 + end + return true + end +end + +local function next_byte() + local pos = string_find(statusBuf, "[^ \t\r\n]", statusPos) + if pos then + statusPos = pos + local b = string_byte(statusBuf, pos) + if not skip_comment(b) then + return b + end + return next_byte() + end + return -1 +end + +local function decode_unicode_surrogate(s1, s2) + return utf8_char( + 0x10000 + + (tonumber(s1, 16) - 0xd800) * 0x400 + + (tonumber(s2, 16) - 0xdc00) + ) +end + +local function decode_unicode_escape(s) + return utf8_char(tonumber(s, 16)) +end + +local function decode_string() + local has_unicode_escape = false + local has_escape = false + local i = statusPos + 1 + while true do + i = string_find(statusBuf, '[%z\1-\31\\"]', i) + if not i then + decode_error("expected closing quote for string") + end + local x = string_byte(statusBuf, i) + if x < 32 then + statusPos = i + decode_error("control character in string") + end + if + x == 34 --[[ '"' ]] + then + local s = string_sub(statusBuf, statusPos + 1, i - 1) + if has_unicode_escape then + s = string_gsub( + string_gsub( + s, + "\\u([dD][89aAbB]%x%x)\\u([dD][c-fC-F]%x%x)", + decode_unicode_surrogate + ), + "\\u(%x%x%x%x)", + decode_unicode_escape + ) + end + if has_escape then + s = string_gsub(s, "\\.", decode_escape_map) + end + statusPos = i + 1 + return s + end + --assert(x == 92 --[[ "\\" ]]) + local nx = string_byte(statusBuf, i + 1) + if + nx == 117 --[[ "u" ]] + then + if not string_match(statusBuf, "^%x%x%x%x", i + 2) then + statusPos = i + decode_error("invalid unicode escape in string") + end + has_unicode_escape = true + i = i + 6 + else + if not decode_escape_set[nx] then + statusPos = i + decode_error( + "invalid escape char '" + .. (nx and string_char(nx) or "") + .. "' in string" + ) + end + has_escape = true + i = i + 2 + end + end +end + +local function decode_number() + local num, c = + string_match(statusBuf, "^([0-9]+%.?[0-9]*)([eE]?)", statusPos) + if + not num or string_byte(num, -1) == 0x2E --[[ "." ]] + then + decode_error("invalid number '" .. get_word() .. "'") + end + if c ~= "" then + num = string_match( + statusBuf, + "^([^eE]*[eE][-+]?[0-9]+)[ \t\r\n%]},/]", + statusPos + ) + if not num then + decode_error("invalid number '" .. get_word() .. "'") + end + end + statusPos = statusPos + #num + return tonumber(num) +end + +local function decode_number_zero() + local num, c = string_match(statusBuf, "^(.%.?[0-9]*)([eE]?)", statusPos) + if + not num + or string_byte(num, -1) == 0x2E --[[ "." ]] + or string_match(statusBuf, "^.[0-9]+", statusPos) + then + decode_error("invalid number '" .. get_word() .. "'") + end + if c ~= "" then + num = string_match( + statusBuf, + "^([^eE]*[eE][-+]?[0-9]+)[ \t\r\n%]},/]", + statusPos + ) + if not num then + decode_error("invalid number '" .. get_word() .. "'") + end + end + statusPos = statusPos + #num + return tonumber(num) +end + +local function decode_number_negative() + statusPos = statusPos + 1 + local c = string_byte(statusBuf, statusPos) + if c then + if c == 0x30 then + return -decode_number_zero() + elseif c > 0x30 and c < 0x3A then + return -decode_number() + end + end + decode_error("invalid number '" .. get_word() .. "'") +end + +local function decode_true() + if string_sub(statusBuf, statusPos, statusPos + 3) ~= "true" then + decode_error("invalid literal '" .. get_word() .. "'") + end + statusPos = statusPos + 4 + return true +end + +local function decode_false() + if string_sub(statusBuf, statusPos, statusPos + 4) ~= "false" then + decode_error("invalid literal '" .. get_word() .. "'") + end + statusPos = statusPos + 5 + return false +end + +local function decode_null() + if string_sub(statusBuf, statusPos, statusPos + 3) ~= "null" then + decode_error("invalid literal '" .. get_word() .. "'") + end + statusPos = statusPos + 4 + return vim.NIL +end + +local function decode_array() + statusPos = statusPos + 1 + local res = {} + local chr = next_byte() + if + chr == 93 --[[ ']' ]] + then + statusPos = statusPos + 1 + return res + end + statusTop = statusTop + 1 + statusAry[statusTop] = true + statusRef[statusTop] = res + return res +end + +local function decode_object() + statusPos = statusPos + 1 + local res = {} + local chr = next_byte() + if + chr == 125 --[[ ']' ]] + then + statusPos = statusPos + 1 + return vim.empty_dict() + end + statusTop = statusTop + 1 + statusAry[statusTop] = false + statusRef[statusTop] = res + return res +end + +local decode_uncompleted_map = { + [string_byte('"')] = decode_string, + [string_byte("0")] = decode_number_zero, + [string_byte("1")] = decode_number, + [string_byte("2")] = decode_number, + [string_byte("3")] = decode_number, + [string_byte("4")] = decode_number, + [string_byte("5")] = decode_number, + [string_byte("6")] = decode_number, + [string_byte("7")] = decode_number, + [string_byte("8")] = decode_number, + [string_byte("9")] = decode_number, + [string_byte("-")] = decode_number_negative, + [string_byte("t")] = decode_true, + [string_byte("f")] = decode_false, + [string_byte("n")] = decode_null, + [string_byte("[")] = decode_array, + [string_byte("{")] = decode_object, +} +local function unexpected_character() + decode_error( + "unexpected character '" + .. string_sub(statusBuf, statusPos, statusPos) + .. "'" + ) +end +local function unexpected_eol() + decode_error("unexpected character ''") +end + +local decode_map = {} +for i = 0, 255 do + decode_map[i] = decode_uncompleted_map[i] or unexpected_character +end +decode_map[-1] = unexpected_eol + +local function decode() + return decode_map[next_byte()]() +end + +local function decode_item() + local top = statusTop + local ref = statusRef[top] + if statusAry[top] then + ref[#ref + 1] = decode() + else + local key = decode_string() + if + next_byte() ~= 58 --[[ ':' ]] + then + decode_error("expected ':'") + end + statusPos = statusPos + 1 + ref[key] = decode() + end + if top == statusTop then + repeat + local chr = next_byte() + statusPos = statusPos + 1 + if + chr == 44 --[[ "," ]] + then + local c = next_byte() + if statusAry[statusTop] then + if + c ~= 93 --[[ "]" ]] + then + return + end + else + if + c ~= 125 --[[ "}" ]] + then + return + end + end + statusPos = statusPos + 1 + else + if statusAry[statusTop] then + if + chr ~= 93 --[[ "]" ]] + then + decode_error("expected ']' or ','") + end + else + if + chr ~= 125 --[[ "}" ]] + then + decode_error("expected '}' or ','") + end + end + end + statusTop = statusTop - 1 + until statusTop == 0 + end +end + +local M = {} + +function M.decode(str) + if type(str) ~= "string" then + error("expected argument of type string, got " .. type(str)) + end + if str == "" then + error("attempted to decode an empty string") + end + statusBuf = str + statusPos = 1 + statusTop = 0 + if next_byte() == -1 then + return vim.NIL + end + local res = decode() + while statusTop > 0 do + decode_item() + end + if string_find(statusBuf, "[^ \t\r\n]", statusPos) then + decode_error("trailing garbage") + end + return res +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/jsregexp.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/jsregexp.lua new file mode 100644 index 00000000..54be37ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/jsregexp.lua @@ -0,0 +1,41 @@ +local Path = require("luasnip.util.path") + +-- neovim-loader does not handle module-names with dots correctly, so for +-- jsregexp-0.0.6, the call to require("jsregexp.core") in jsregexp.lua errors +-- even if the library is in rtp. +-- Resolve path to jsregexp.so manually, and loadlib it in preload (and remove +-- preload after requires are done and have failed/worked). + +-- omit "@". +local this_file = debug.getinfo(1).source:sub(2) +local repo_dir = vim.fn.fnamemodify(this_file, ":h:h:h:h") +local jsregexp_core_path = Path.join(repo_dir, "deps", "luasnip-jsregexp.so") + +-- rather gracefully, if the path does not exist, or loadlib can't do its job +-- for some other reason, the preload will be set to nil, ie not be set. +-- +-- This means we don't hinder a regularly installed 0.0.6-jsregexp-library, +-- since its `require("jsregexp.core")` will be unaffected. +package.preload["jsregexp.core"] = + package.loadlib(jsregexp_core_path, "luaopen_jsregexp_core") + +-- jsregexp: first try loading the version installed by luasnip, then global ones. +local jsregexp_ok, jsregexp = pcall(require, "luasnip-jsregexp") +if not jsregexp_ok then + jsregexp_ok, jsregexp = pcall(require, "jsregexp") +end + +-- don't want to affect other requires. +package.preload["jsregexp.core"] = nil + +if not jsregexp_ok then + return false +end + +-- detect version, and return compile-function. +-- 0.0.6-compile_safe and 0.0.5-compile behave the same, ie. nil, err on error. +if jsregexp.compile_safe then + return jsregexp.compile_safe +else + return jsregexp.compile +end diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/lazy_table.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/lazy_table.lua new file mode 100644 index 00000000..6c5e4821 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/lazy_table.lua @@ -0,0 +1,13 @@ +return function(lazy_t, lazy_defs) + return setmetatable(lazy_t, { + __index = function(t, k) + local v = lazy_defs[k] + if v then + local v_resolved = v() + rawset(t, k, v_resolved) + return v_resolved + end + return nil + end, + }) +end diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/log.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/log.lua new file mode 100644 index 00000000..ad4045da --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/log.lua @@ -0,0 +1,123 @@ +local util = require("luasnip.util.util") + +-- older neovim-versions (even 0.7.2) do not have stdpath("log"). +local logpath_ok, logpath = pcall(vim.fn.stdpath, "log") +if not logpath_ok then + logpath = vim.fn.stdpath("cache") +end + +-- just to be sure this dir exists. +-- 448 = 0700 +vim.loop.fs_mkdir(logpath, 448) + +local log_location = logpath .. "/luasnip.log" +local log_old_location = logpath .. "/luasnip.log.old" + +local luasnip_log_fd = vim.loop.fs_open( + log_location, + -- only append. + "a", + -- 420 = 0644 + 420 +) + +local function log_line_append(msg) + msg = msg:gsub("\n", "\n | ") + vim.loop.fs_write(luasnip_log_fd, msg .. "\n") +end + +if not luasnip_log_fd then + -- print a warning + print( + ("LuaSnip: could not open log at %s. Not logging for this session."):format( + log_location + ) + ) + -- make log_line_append do nothing. + log_line_append = util.nop +else + -- if log_fd found, check if log should be rotated. + local logsize = vim.loop.fs_fstat(luasnip_log_fd).size + if logsize > 10 * 2 ^ 20 then + -- logsize > 10MiB: + -- move log -> old log, start new log. + vim.loop.fs_rename(log_location, log_old_location) + luasnip_log_fd = vim.loop.fs_open( + log_location, + -- only append. + "a", + -- 420 = 0644 + 420 + ) + end +end + +local M = {} + +local log = { + error = function(msg) + log_line_append("ERROR | " .. msg) + end, + warn = function(msg) + log_line_append("WARN | " .. msg) + end, + info = function(msg) + log_line_append("INFO | " .. msg) + end, + debug = function(msg) + log_line_append("DEBUG | " .. msg) + end, +} + +-- functions copied directly by deepcopy. +-- will be initialized later on, by set_loglevel. +local effective_log + +-- levels sorted by importance, descending. +local loglevels = { "error", "warn", "info", "debug" } + +-- special key none disable all logging. +function M.set_loglevel(target_level) + local target_level_indx = util.indx_of(loglevels, target_level) + if target_level == "none" then + target_level_indx = 0 + end + + assert(target_level_indx ~= nil, "invalid level!") + + -- reset effective loglevels, set those with importance higher than + -- target_level, disable (nop) those with lower. + effective_log = {} + for i = 1, target_level_indx do + effective_log[loglevels[i]] = log[loglevels[i]] + end + for i = target_level_indx + 1, #loglevels do + effective_log[loglevels[i]] = util.nop + end +end + +function M.new(module_name) + local module_log = {} + for name, _ in pairs(log) do + module_log[name] = function(msg, ...) + -- don't immediately get the referenced function, we'd like to + -- allow changing the loglevel on-the-fly. + effective_log[name](module_name .. ": " .. msg:format(...)) + end + end + return module_log +end + +function M.open() + vim.cmd(("tabnew %s"):format(log_location)) +end + +-- to verify log is working. +function M.ping() + log_line_append(("PONG | pong! (%s)"):format(os.date())) +end + +-- set default-loglevel. +M.set_loglevel("warn") + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/mark.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/mark.lua new file mode 100644 index 00000000..daf9b074 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/mark.lua @@ -0,0 +1,208 @@ +local session = require("luasnip.session") + +local Mark = {} + +function Mark:new(o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o +end + +-- opts just like in nvim_buf_set_extmark. +local function mark(pos_begin, pos_end, opts) + return Mark:new({ + id = vim.api.nvim_buf_set_extmark( + 0, + session.ns_id, + pos_begin[1], + pos_begin[2], + -- override end_* in opts. + vim.tbl_extend( + "force", + opts, + { end_line = pos_end[1], end_col = pos_end[2] } + ) + ), + -- store opts here, can't be queried using nvim_buf_get_extmark_by_id. + opts = opts, + }) +end + +local function bytecol_to_utfcol(pos) + local line = vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false) + -- line[1]: get_lines returns table. + -- use utf16-index. + local utf16_indx, _ = vim.str_utfindex(line[1] or "", pos[2]) + return { pos[1], utf16_indx } +end + +function Mark:pos_begin_end() + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + self.id, + { details = true } + ) + + return bytecol_to_utfcol({ mark_info[1], mark_info[2] }), + bytecol_to_utfcol({ mark_info[3].end_row, mark_info[3].end_col }) +end + +function Mark:pos_begin() + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + self.id, + { details = false } + ) + + return bytecol_to_utfcol({ mark_info[1], mark_info[2] }) +end + +function Mark:pos_end() + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + self.id, + { details = true } + ) + + return bytecol_to_utfcol({ mark_info[3].end_row, mark_info[3].end_col }) +end + +function Mark:pos_begin_end_raw() + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + self.id, + { details = true } + ) + return { mark_info[1], mark_info[2] }, { + mark_info[3].end_row, + mark_info[3].end_col, + } +end + +function Mark:pos_begin_raw() + local mark_info = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + self.id, + { details = false } + ) + return { mark_info[1], mark_info[2] } +end + +function Mark:copy_pos_gravs(opts) + local pos_beg, pos_end = self:pos_begin_end_raw() + opts.right_gravity = self.opts.right_gravity + opts.end_right_gravity = self.opts.end_right_gravity + return mark(pos_beg, pos_end, opts) +end + +-- opts just like in nvim_buf_set_extmark. +-- opts as first arg bcs. pos are pretty likely to stay the same. +function Mark:update(opts, pos_begin, pos_end) + -- if one is changed, the other is likely as well. + if not pos_begin then + pos_begin = old_pos_begin + if not pos_end then + pos_end = old_pos_end + end + end + -- override with new. + self.opts = vim.tbl_extend("force", self.opts, opts) + vim.api.nvim_buf_set_extmark( + 0, + session.ns_id, + pos_begin[1], + pos_begin[2], + vim.tbl_extend( + "force", + self.opts, + { id = self.id, end_line = pos_end[1], end_col = pos_end[2] } + ) + ) +end + +function Mark:set_opts(opts) + local pos_begin, pos_end = self:pos_begin_end_raw() + vim.api.nvim_buf_del_extmark(0, session.ns_id, self.id) + + self.opts = opts + -- set new extmark, current behaviour for updating seems inconsistent, + -- eg. gravs are reset, deco is kept. + self.id = vim.api.nvim_buf_set_extmark( + 0, + session.ns_id, + pos_begin[1], + pos_begin[2], + vim.tbl_extend( + "force", + opts, + { end_line = pos_end[1], end_col = pos_end[2] } + ) + ) +end + +function Mark:set_rgravs(rgrav_left, rgrav_right) + -- don't update if nothing would change. + if + self.opts.right_gravity ~= rgrav_left + or self.opts.end_right_gravity ~= rgrav_right + then + self.opts.right_gravity = rgrav_left + self.opts.end_right_gravity = rgrav_right + self:set_opts(self.opts) + end +end + +function Mark:get_rgrav(which) + if which == -1 then + return self.opts.right_gravity + else + return self.opts.end_right_gravity + end +end + +function Mark:set_rgrav(which, rgrav) + if which == -1 then + if self.opts.right_gravity == rgrav then + return + end + self.opts.right_gravity = rgrav + else + if self.opts.end_right_gravity == rgrav then + return + end + self.opts.end_right_gravity = rgrav + end + self:set_opts(self.opts) +end + +function Mark:get_endpoint(which) + -- simpler for now, look into perf here later. + local l, r = self:pos_begin_end_raw() + if which == -1 then + return l + else + return r + end +end + +-- change all opts except rgravs. +function Mark:update_opts(opts) + local opts_cp = vim.deepcopy(opts) + opts_cp.right_gravity = self.opts.right_gravity + opts_cp.end_right_gravity = self.opts.end_right_gravity + self:set_opts(opts_cp) +end + +function Mark:clear() + vim.api.nvim_buf_del_extmark(0, session.ns_id, self.id) +end + +return { + mark = mark, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/ast_parser.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/ast_parser.lua new file mode 100644 index 00000000..20f056a3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/ast_parser.lua @@ -0,0 +1,350 @@ +local ast_utils = require("luasnip.util.parser.ast_utils") +local Ast = require("luasnip.util.parser.neovim_ast") +local tNode = require("luasnip.nodes.textNode") +local iNode = require("luasnip.nodes.insertNode") +local fNode = require("luasnip.nodes.functionNode") +local cNode = require("luasnip.nodes.choiceNode") +local dNode = require("luasnip.nodes.dynamicNode") +local sNode = require("luasnip.nodes.snippet") +local functions = require("luasnip.util.functions") +local Environ = require("luasnip.util.environ") +local session = require("luasnip.session") +local util = require("luasnip.util.util") + +local M = {} + +local _split = function(s) + return vim.split(s, "\n", { plain = true }) +end + +local types = ast_utils.types + +local to_node + +local function fix_node_indices(nodes) + local used_nodes = {} + for _, node in ipairs(nodes) do + if node.pos and node.pos > 0 then + used_nodes[node.pos] = node + end + end + + for _, v, i in util.key_sorted_pairs(used_nodes) do + v.pos = i + end + return nodes +end + +local function ast2luasnip_nodes(ast_nodes) + local nodes = {} + for i, ast_node in ipairs(ast_nodes) do + nodes[i] = ast_node.parsed + end + + return fix_node_indices(nodes) +end + +local function var_func(ast) + local varname = ast.name + + local transform_func + if ast.transform then + transform_func = ast_utils.apply_transform(ast.transform) + else + transform_func = util.id + end + + return function(_, parent, _, variable_default) + local v = parent.snippet.env[varname] + local lines + if type(v) == "table" then + -- Avoid issues with empty vars + if #v > 0 then + lines = v + else + lines = { "" } + end + else + lines = { v } + end + + -- quicker than checking `lines` in some way. + if not v then + -- the variable is not defined: + -- insert the variable's name as a placeholder. + return sNode.SN(nil, { iNode.I(1, varname) }) + end + if #lines == 0 or (#lines == 1 and #lines[1] == 0) then + -- The variable is empty. + + -- default passed as user_arg, rationale described in + -- types.VARIABLE-to_node_func. + if variable_default then + return variable_default + else + -- lines might still just be {} (#lines == 0). + lines = { "" } + end + end + + -- v exists and has no default, return the (maybe modified) lines. + return sNode.SN(nil, { tNode.T(transform_func(lines)) }) + end +end + +local function copy_func(tabstop) + local transform_func + if tabstop.transform then + transform_func = ast_utils.apply_transform(tabstop.transform) + else + transform_func = util.id + end + return function(args) + return transform_func(args[1]) + end +end + +local function placeholder_func(_, parent, _, placeholder_snip) + local env = parent.snippet.env + -- is_interactive needs env to determine interactiveness. + -- env is passed through to all following is_interactive calls. + if not placeholder_snip:is_interactive(env) then + -- this placeholder only contains text or (transformed) + -- variables, so an insertNode can be generated from its + -- contents. + -- create new snippet that only contains the parsed snippetNode, so we + -- can `fake_expand` and `get_static_text()` it. + local snippet = sNode.S("", placeholder_snip) + + -- get active env from snippet. + snippet:fake_expand({ env = env }) + local iText = snippet:get_static_text() + + -- no need to un-escape iText, that was already done. + return sNode.SN(nil, iNode.I(1, iText)) + end + + return sNode.SN( + nil, + session.config.parser_nested_assembler(1, placeholder_snip) + ) +end + +---If this tabstop-node (CHOICE, TABSTOP or PLACEHOLDER) is a copy of another, +---set that up and return, otherwise return false. +---@param ast table: ast-node. +---@return boolean: whether the node is now parsed. +local function tabstop_node_copy_inst(ast) + local existing_tabstop_ast_node = ast.copies + if existing_tabstop_ast_node then + -- this tabstop is a mirror of an already-parsed tabstop/placeholder. + ast.parsed = + fNode.F(copy_func(ast), { existing_tabstop_ast_node.parsed }) + return true + end + return false +end +-- these actually create nodes from any AST. +local to_node_funcs = { + -- careful! this parses the snippet into a list of nodes, not a full snippet! + -- The table can then be passed to the regular snippet-constructors. + [types.SNIPPET] = function(ast, _) + ast.parsed = ast2luasnip_nodes(ast.children) + end, + [types.TEXT] = function(ast, _) + local text = _split(ast.esc) + ast.parsed = tNode.T(text) + end, + [types.CHOICE] = function(ast) + -- even choices may be copies. + if tabstop_node_copy_inst(ast) then + return + end + + local choices = {} + for i, choice in ipairs(ast.items) do + choices[i] = tNode.T(_split(choice)) + end + + ast.parsed = cNode.C(ast.tabstop, choices) + end, + [types.TABSTOP] = function(ast) + if tabstop_node_copy_inst(ast) then + return + end + -- tabstops don't have placeholder-text. + ast.parsed = iNode.I(ast.tabstop) + end, + [types.PLACEHOLDER] = function(ast, state) + if tabstop_node_copy_inst(ast) then + return + end + + local node + if #ast.children == 1 and ast.children[1].type == types.TEXT then + -- we cannot place a dynamicNode as $0. + -- But all valid ${0:some nodes here} contain just text inside + -- them, so this works :) + node = iNode.I(ast.tabstop, _split(ast.children[1].esc)) + else + local snip = sNode.SN(1, ast2luasnip_nodes(ast.children)) + node = dNode.D(ast.tabstop, placeholder_func, {}, { + -- pass snip here, again to preserve references to other tables. + user_args = { snip }, + }) + end + + ast.parsed = node + end, + [types.VARIABLE] = function(ast, state) + local var = ast.name + + local default + if ast.children then + default = sNode.SN(nil, ast2luasnip_nodes(ast.children)) + end + + local fn + local is_interactive_fn + if state.var_functions[var] then + fn, is_interactive_fn = unpack(state.var_functions[var]) + else + fn = var_func(ast) + -- override the regular `is_interactive` to accurately determine + -- whether the snippet produced by the dynamicNode is interactive + -- or not. This is important when a variable is wrapped inside a + -- placeholder: ${1:$TM_SELECTED_TEXT} + -- With variable-environments we cannot tell at parse-time whether + -- the dynamicNode will be just text, an insertNode or some other + -- nodes(the default), so that has to happen at runtime now. + is_interactive_fn = function(_, env) + local var_value = env[var] + + if not var_value then + -- inserts insertNode. + return true + end + + -- just wrap it for more uniformity. + if type(var_value) == "string" then + var_value = { var_value } + end + + if + (#var_value == 1 and #var_value[1] == 0) + or #var_value == 0 + then + -- var is empty, default is inserted. + -- if no default, it's not interactive (an empty string is inserted). + return default and default:is_interactive() + end + + -- variable is just inserted, not interactive. + return false + end + end + + local d = dNode.D(ast.potential_tabstop, fn, {}, { + -- TRICKY!!!! + -- Problem: if the default is passed to the dynamicNode-function via lambda-capture, the + -- copy-routine, which will run on expansion, cannot associate these + -- nodes inside the passed nodes with the ones that are inside the + -- snippet. + -- For example, if `default` contains a functionNode which relies on + -- an insertNode within the snippet, it has the insertNode as an + -- argnode stored inside it. During copy, the copied insertNode (eg + -- a pointer to it) has to be inserted at this position as well, + -- otherwise there might be bugs (the snippet thinks the argnode is + -- present, but it isn't). + -- + -- This means that these nodes may not be passed as a simple + -- lambda-capture (!!). + -- I don't really like this, it can lead to very subtle errors (not + -- in this instance, but needing to do this in general). + -- + -- TODO: think about ways to avoid this. OTOH, this is almost okay, + -- just needs to be documented a bit. + -- + -- `default` is potentially nil. + user_args = { default }, + }) + d.is_interactive = is_interactive_fn + + -- if the variable is preceded by \n, the indent is applied to + -- all lines of the variable (important for eg. TM_SELECTED_TEXT). + if ast.previous_text ~= nil and #ast.previous_text > 1 then + local last_line_indent = + ast.previous_text[#ast.previous_text]:match("^%s+$") + if last_line_indent then + -- TM_SELECTED_TEXT contains the indent of the selected + -- snippets, which leads to correct indentation if the + -- snippet is expanded at the position the text was removed + -- from. + -- This seems pretty stupid, but TM_SELECTED_TEXT is + -- desigend to be compatible with vscode. + -- Use SELECT_DEDENT insted. + -- stylua: ignore + local indentstring = var ~= "TM_SELECTED_TEXT" + and "$PARENT_INDENT" .. last_line_indent + or last_line_indent + + -- adjust current d's jump-position.. + d.pos = 1 + -- ..so it has the correct position when wrapped inside a + -- snippetNode. + d = sNode.ISN(ast.potential_tabstop, { d }, indentstring) + end + end + + ast.parsed = d + end, +} + +--- Converts any ast into luasnip-nodes. +--- Snippets return a table of nodes, those can be used like the return-value of `fmt`. +---@param ast table: AST, as generated by `require("vim.lsp._snippet").parse` +---@param state table: +--- - `var_functions`: table, string -> {dNode-fn, is_interactive_fn} +--- For now, only used when parsing snipmate-snippets. +---@return table: node corresponding to `ast`. +function to_node(ast, state) + if not Ast.is_node(ast) then + -- ast is not an ast (probably a luasnip-node), return it as-is. + return ast + end + return to_node_funcs[ast.type](ast, state) +end + +--- Converts any ast into usable nodes. +---@param ast table: AST, as generated by `require("vim.lsp._snippet").parse` +---@param state table: +--- - `var_functions`: table, string -> {dNode-fn, is_interactive_fn} +--- For now, only used when parsing snipmate-snippets. +---@return table: list of luasnip-nodes. +function M.to_luasnip_nodes(ast, state) + state = state or {} + state.var_functions = state.var_functions or {} + + ast_utils.give_vars_potential_tabstop(ast) + + -- fix disallowed $0 in snippet. + -- TODO(logging): report changes here. + ast_utils.fix_zero(ast) + + -- Variables need the text just in front of them to determine whether to + -- indent all lines of the Variable. + ast_utils.give_vars_previous_text(ast) + + local ast_nodes_topsort = ast_utils.parse_order(ast) + assert( + ast_nodes_topsort, + "cannot represent snippet: contains circular dependencies" + ) + for _, node in ipairs(ast_nodes_topsort) do + to_node(node, state) + end + + return ast.parsed +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/ast_utils.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/ast_utils.lua new file mode 100644 index 00000000..42e614ec --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/ast_utils.lua @@ -0,0 +1,461 @@ +local Ast = require("luasnip.util.parser.neovim_ast") +local types = Ast.node_type +local util = require("luasnip.util.util") +local Str = require("luasnip.util.str") +local log = require("luasnip.util.log").new("parser") +local jsregexp_compile_safe = require("luasnip.util.jsregexp") + +local directed_graph = require("luasnip.util.directed_graph") + +local M = {} + +---Walks ast pre-order, from left to right, applying predicate fn. +---The walk is aborted as soon as fn matches (eg. returns true). +---The walk does not recurse into Transform or choice, eg. it only covers nodes +---that can be jumped (in)to. +---@param ast table: the tree. +---@param fn function: the predicate. +---@return boolean: whether the predicate matched. +local function predicate_ltr_nodes(ast, fn) + if fn(ast) then + return true + end + for _, node in ipairs(ast.children or {}) do + if predicate_ltr_nodes(node, fn) then + return true + end + end + + return false +end + +-- tested in vscode: +-- in "${1|b,c|} ${1:aa}" ${1:aa} is the copy, +-- in "${1:aa}, ${1|b,c|}" ${1|b,c} is the copy => with these two the position +-- determines which is the real tabstop => they have the same priority. +-- in "$1 ${1:aa}", $1 is the copy, so it has to have a lower priority. +local function type_real_tabstop_prio(node) + local _type_real_tabstop_prio = { + [types.TABSTOP] = 1, + [types.PLACEHOLDER] = 2, + [types.CHOICE] = 2, + } + if node.transform then + return 0 + end + return _type_real_tabstop_prio[node.type] +end + +---The name of this function is horrible, but I can't come up with something +---more succinct. +---The idea here is to find which of two nodes is "smaller" in a +---"real-tabstop"-ordering relation on all the nodes of a snippet. +---REQUIREMENT!!! The nodes have to be passed in the order they appear in in +---the snippet, eg. prev_node has to appear earlier in the text (or be a parent +---of) current_node. +---@param prev_node table: the ast node earlier in the text. +---@param current_node table: the other ast node. +---@return boolean: true if prev_node is less than (according to the +---"real-tabstop"-ordering described above and in the docstring of +---`add_dependents`), false otherwise. +local function real_tabstop_order_less(prev_node, current_node) + local prio_prev = type_real_tabstop_prio(prev_node) + local prio_current = type_real_tabstop_prio(current_node) + -- if type-prio is the same, the one that appeared earlier is the real tabstop. + return prio_prev == prio_current and false or prio_prev < prio_current +end + +---Find the real (eg. the one that is not a copy) $0. +---@param ast table: ast +---@return number, number, boolean: first, the type of the node with position 0, then +--- the child of `ast` containing it and last whether the real $0 is copied. +local function real_zero_node(ast) + local real_zero = nil + local real_zero_indx = nil + local is_copied = false + + local _search_zero + _search_zero = function(node) + local had_zero = false + -- find placeholder/tabstop/choice with position 0 + if node.tabstop == 0 then + if not real_zero then + real_zero = node + had_zero = true + else + if real_tabstop_order_less(real_zero, node) then + -- node has a higher prio than the current real_zero. + real_zero = node + had_zero = true + end + -- we already encountered a zero-node, since i(0) cannot be + -- copied this has to be reported to the caller. + is_copied = true + end + end + for indx, child in ipairs(node.children or {}) do + local zn, _ = _search_zero(child) + -- due to recursion, this will be called last in the loop of the + -- outermost snippet. + -- real_zero_indx will be the position of the child of snippet, in + -- which the real $0 is located. + if zn then + real_zero_indx = indx + had_zero = true + end + end + + return had_zero + end + _search_zero(ast) + + return real_zero, real_zero_indx, is_copied +end + +local function count_tabstop(ast, tabstop_indx) + local count = 0 + + predicate_ltr_nodes(ast, function(node) + if node.tabstop == tabstop_indx then + count = count + 1 + end + -- only stop once all nodes were looked at. + return false + end) + + return count +end + +local function text_only_placeholder(placeholder) + local only_text = true + + predicate_ltr_nodes(placeholder, function(node) + if node == placeholder then + -- ignore placeholder. + return false + end + if node.type ~= types.TEXT then + only_text = false + -- we found non-text, no need to search more. + return true + end + end) + + return only_text +end + +local function max_position(ast) + local max = 0 + predicate_ltr_nodes(ast, function(node) + local new_max = node.tabstop or 0 + if new_max > max then + max = new_max + end + -- don't stop early. + return false + end) + + return max +end + +local function replace_position(ast, p1, p2) + predicate_ltr_nodes(ast, function(node) + if node.tabstop == p1 then + node.tabstop = p2 + end + -- look at all nodes. + return false + end) +end + +function M.fix_zero(ast) + local zn, ast_child_with_0_indx, is_copied = real_zero_node(ast) + -- if zn exists, is a tabstop, an immediate child of `ast`, and does not + -- have to be copied, the snippet can be accurately represented by luasnip. + -- (also if zn just does not exist, ofc). + -- + -- If the snippet can't be represented as-is, the ast needs to be modified + -- as described below. + if + not zn + or ( + zn + and not is_copied + and (zn.type == types.TABSTOP or (zn.type == types.PLACEHOLDER and text_only_placeholder( + zn + ))) + and ast.children[ast_child_with_0_indx] == zn + ) + then + return + end + + -- bad, a choice or placeholder is at position 0. + -- replace all ${0:...} with ${n+1:...} (n highest position) + -- max_position is at least 0, all's good. + local max_pos = max_position(ast) + replace_position(ast, 0, max_pos + 1) + + -- insert $0 as a direct child to snippet, just behind the original $0/the + -- node containing it. + table.insert(ast.children, ast_child_with_0_indx + 1, Ast.tabstop(0)) +end + +---This function identifies which tabstops/placeholder/choices are copies, and +---which are "real tabstops"(/choices/placeholders). The real tabstops are +---extended with a list of their dependents (tabstop.dependents), the copies +---with their real tabstop (copy.copies) +--- +---Rules for which node of any two nodes with the same tabstop-index is the +---real tabstop: +--- - if one is a tabstop and the other a placeholder/choice, the +--- placeholder/choice is the real tabstop. +--- - if they are both tabstop or both placeholder/choice, the one which +--- appears earlier in the snippet is the real tabstop. +--- (in "${1: ${1:lel}}" the outer ${1:...} appears earlier). +--- +---@param ast table: the AST. +function M.add_dependents(ast) + -- all nodes that have a tabstop. + -- map tabstop-index (number) -> node. + local tabstops = {} + + -- nodes which copy some tabstop. + -- map tabstop-index (number) -> node[] (since there could be multiple copies of that one snippet). + local copies = {} + + predicate_ltr_nodes(ast, function(node) + if not node.tabstop then + -- not a tabstop-node -> continue. + return false + end + + if not tabstops[node.tabstop] then + tabstops[node.tabstop] = node + -- continue, we want to find all dependencies. + return false + end + if not copies[node.tabstop] then + copies[node.tabstop] = {} + end + if real_tabstop_order_less(tabstops[node.tabstop], node) then + table.insert(copies[node.tabstop], tabstops[node.tabstop]) + tabstops[node.tabstop] = node + else + table.insert(copies[node.tabstop], node) + end + -- continue. + return false + end) + + -- associate real tabstop with its copies (by storing the copies in the real tabstop). + for i, real_tabstop in pairs(tabstops) do + real_tabstop.dependents = {} + for _, copy in ipairs(copies[i] or {}) do + table.insert(real_tabstop.dependents, copy) + copy.copies = real_tabstop + end + end +end + +local function apply_modifier(text, modifier) + local mod_fn = Str.vscode_string_modifiers[modifier] + if mod_fn then + return mod_fn(text) + else + -- this can't really be reached, since only correct and available + -- modifiers are parsed successfully + -- (https://github.com/L3MON4D3/LuaSnip/blob/5fbebf6409f86bc4b7b699c2c80745e1ed190c16/lua/luasnip/util/parser/neovim_parser.lua#L239-L245). + log.warn( + "Tried to apply unknown modifier `%s` while parsing snippet, recovering by applying identity instead.", + modifier + ) + return text + end +end + +local function apply_transform_format(nodes, captures) + local transformed = "" + for _, node in ipairs(nodes) do + if node.type == types.TEXT then + transformed = transformed .. node.esc + else + local capture = captures[node.capture_index] + -- capture exists if it ..exists.. and is nonempty. + if capture and #capture > 0 then + if node.if_text then + transformed = transformed .. node.if_text + elseif node.modifier then + transformed = transformed + .. apply_modifier(capture, node.modifier) + else + transformed = transformed .. capture + end + else + if node.else_text then + transformed = transformed .. node.else_text + end + end + end + end + + return transformed +end + +function M.apply_transform(transform) + if jsregexp_compile_safe then + local reg_compiled, err = + jsregexp_compile_safe(transform.pattern, transform.option) + + if reg_compiled then + -- can be passed to functionNode! + return function(lines) + -- luasnip expects+passes lines as list, but regex needs one string. + lines = table.concat(lines, "\n") + local matches = reg_compiled(lines) + + local transformed = "" + -- index one past the end of previous match. + -- This is used to append unmatched characters to `transformed`, so + -- it's initialized such that the first append is from 1. + local prev_match_end = 0 + for _, match in ipairs(matches) do + -- begin_ind and end_ind are inclusive. + transformed = transformed + .. lines:sub(prev_match_end + 1, match.begin_ind - 1) + .. apply_transform_format( + transform.format, + match.groups + ) + + -- end-inclusive + prev_match_end = match.end_ind + end + transformed = transformed + .. lines:sub(prev_match_end + 1, #lines) + + return vim.split(transformed, "\n") + end + else + log.error( + "Failed parsing regex `%s` with options `%s`: %s", + transform.pattern, + transform.option, + err + ) + -- fall through to returning identity. + end + end + + -- without jsregexp, or without a valid regex, we cannot properly transform + -- whatever is supposed to be transformed here. + -- Just return a function that returns the to-be-transformed string + -- unmodified. + return util.id +end + +---Variables need the text which is in front of them to determine whether they +---have to be indented ("asdf\n\t$TM_SELECTED_TEXT": vscode indents all lines +---of TM_SELECTED_TEXT). +--- +---The text is accessible as ast_node.previous_text, a string[]. +---@param ast table: the AST. +function M.give_vars_previous_text(ast) + local last_text = { "" } + -- important: predicate_ltr_nodes visits the node in the order they appear, + -- textually, in the snippet. + -- This is necessary to actually ensure the variables actually get the text just in front of them. + predicate_ltr_nodes(ast, function(node) + if node.children then + -- continue if this node is not a leaf. + -- Since predicate_ltr_nodes runs fn first for the placeholder, and + -- then for its' children, `last_text` would be reset wrongfully + -- (example: "asdf\n\t${1:$TM_SELECTED_TEXT}". Here the placeholder + -- is encountered before the variable -> no indentation). + -- + -- ignoring non-leaf-nodes makes it so that only the nodes which + -- actually contribute text (placeholders are "invisible" in that + -- they don't add text themselves, they do it through their + -- children) are considered. + return false + end + if node.type == types.TEXT then + last_text = vim.split(node.esc, "\n") + elseif node.type == types.VARIABLE then + node.previous_text = last_text + else + -- reset last_text when a different node is encountered. + last_text = { "" } + end + -- continue.. + return false + end) +end + +---Variables are turned into placeholders if the Variable is undefined or not set. +---Since in luasnip, variables can be added at runtime, the decision whether a +---variable is just some text, inserts its default, or its variable-name has to +---be deferred to runtime. +---So, each variable is a dynamicNode, and needs a tabstop. +---In vscode the variables are visited +--- 1) after all other tabstops/placeholders/choices and +--- 2) in the order they appear in the snippet-body. +---We mimic this behaviour. +---@param ast table: The AST. +function M.give_vars_potential_tabstop(ast) + local last_tabstop = max_position(ast) + + predicate_ltr_nodes(ast, function(node) + if node.type == types.VARIABLE then + last_tabstop = last_tabstop + 1 + node.potential_tabstop = last_tabstop + end + end) +end + +function M.parse_order(ast) + M.add_dependents(ast) + -- build Directed Graph from ast-nodes. + -- vertices are ast-nodes, edges define has-to-be-parsed-before-relations + -- (a child of some placeholder would have an edge to it, real tabstops + -- have edges to their copies). + local g = directed_graph.new() + -- map node -> vertex. + local to_vert = {} + + -- add one vertex for each node + create map node->vert. + predicate_ltr_nodes(ast, function(node) + to_vert[node] = g:add_vertex() + end) + + predicate_ltr_nodes(ast, function(node) + if node.dependents then + -- if the node has dependents, it has to be parsed before they are. + for _, dep in ipairs(node.dependents) do + g:set_edge(to_vert[node], to_vert[dep]) + end + end + if node.children then + -- if the node has children, they have to be parsed before it can + -- be parsed. + for _, child in ipairs(node.children) do + g:set_edge(to_vert[child], to_vert[node]) + end + end + end) + + local topsort = g:topological_sort() + if not topsort then + -- ast (with additional dependencies) contains circle. + return nil + end + + local to_node = util.reverse_lookup(to_vert) + return vim.tbl_map(function(vertex) + return to_node[vertex] + end, topsort) +end + +M.types = types + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/init.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/init.lua new file mode 100644 index 00000000..31320faa --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/init.lua @@ -0,0 +1,141 @@ +local sNode = require("luasnip.nodes.snippet") +local ast_parser = require("luasnip.util.parser.ast_parser") +local parse = require("luasnip.util.parser.neovim_parser").parse +local Ast = require("luasnip.util.parser.neovim_ast") +local Str = require("luasnip.util.str") +local functions = require("luasnip.util.functions") +local util = require("luasnip.util.util") +local extend_decorator = require("luasnip.util.extend_decorator") + +local M = {} + +---Parse snippet represented by `body`. +---@param context (table|string|number|nil): +--- - table|string: treated like the first argument to `ls.snippet`, +--- returns a snippet. +--- - number: Returns a snippetNode, `context` is its' jump-position. +--- - nil: Returns a flat list of luasnip-nodes, to be used however. +---@param body string: the representation of the snippet. +---@param opts table|nil: optional parameters. Valid keys: +--- - `trim_empty`: boolean, remove empty lines from the snippet. +--- - `dedent`: boolean, remove common indent from the snippet's lines. +--- - `variables`: map[string-> (fn()->string)], variables to be used only in this +--- snippet. +---@return table: the snippet, in the representation dictated by the value of +---`context`. +function M.parse_snippet(context, body, opts) + opts = opts or {} + if opts.dedent == nil then + opts.dedent = true + end + if opts.trim_empty == nil then + opts.trim_empty = true + end + + body = Str.sanitize(body) + + local lines = vim.split(body, "\n") + Str.process_multiline(lines, opts) + body = table.concat(lines, "\n") + + local ast + if body == "" then + ast = Ast.snippet({ + Ast.text(""), + }) + else + ast = parse(body) + end + + local nodes = ast_parser.to_luasnip_nodes(ast, { + var_functions = opts.variables, + }) + + if type(context) == "number" then + return sNode.SN(context, nodes) + end + if type(context) == "nil" then + return nodes + end + + if type(context) == "string" then + context = { trig = context } + end + context.docstring = body + + return sNode.S(context, nodes) +end +local function context_extend(arg, extend) + local argtype = type(arg) + if argtype == "string" then + arg = { trig = arg } + end + + if argtype == "table" then + return vim.tbl_extend("keep", arg, extend or {}) + end + + -- fall back to unchanged arg. + -- log this, probably. + return arg +end +extend_decorator.register( + M.parse_snippet, + { arg_indx = 1, extend = context_extend }, + { arg_indx = 3 } +) + +local function backticks_to_variable(body) + local var_map = {} + local variable_indx = 1 + local var_string = "" + + local processed_to = 1 + for from, to in Str.unescaped_pairs(body, "`", "`") do + local varname = "LUASNIP_SNIPMATE_VAR" .. variable_indx + var_string = var_string + -- since the first unescaped ` is at from, there is no unescaped ` + -- in body:sub(old_to, from-1). We can therefore gsub occurences of + -- \`, without worrying about potentially changing something like + -- \\` (or \\\\`) into \` (\\\`). + .. body:sub(processed_to, from - 1):gsub("\\`", "`") + -- `$varname` is unsafe, might lead to something like "my + -- snip$LUASNIP_SNIPMATE_VAR1pet", where the variable is + -- interpreted as "LUASNIP_SNIPMATE_VAR1pet". + -- This cannot happen with curly braces. + .. "${" + .. varname + .. "}" + + -- don't include backticks in vimscript. + var_map[varname] = + functions.eval_vim_dynamic(body:sub(from + 1, to - 1)) + processed_to = to + 1 + variable_indx = variable_indx + 1 + end + + -- append remaining characters. + var_string = var_string .. body:sub(processed_to, -1):gsub("\\`", "`") + + return var_map, var_string +end + +function M.parse_snipmate(context, body, opts) + local new_vars + new_vars, body = backticks_to_variable(body) + + opts = opts or {} + opts.variables = {} + for name, fn in pairs(new_vars) do + -- created dynamicNode is not interactive. + opts.variables[name] = { fn, util.no } + end + return M.parse_snippet(context, body, opts) +end +extend_decorator.register( + M.parse_snipmate, + { arg_indx = 1, extend = context_extend }, + { arg_indx = 3 } +) + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/neovim_ast.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/neovim_ast.lua new file mode 100644 index 00000000..5fcd6251 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/neovim_ast.lua @@ -0,0 +1,207 @@ +-- ripped out of neovim. + +local M = {} + +local node_type = { + SNIPPET = 0, + TABSTOP = 1, + PLACEHOLDER = 2, + VARIABLE = 3, + CHOICE = 4, + TRANSFORM = 5, + FORMAT = 6, + TEXT = 7, +} + +M.node_type = node_type + +local Node = {} +function Node:__tostring() + local insert_text = {} + if self.type == node_type.SNIPPET then + for _, c in ipairs(self.children) do + table.insert(insert_text, tostring(c)) + end + elseif self.type == node_type.CHOICE then + table.insert(insert_text, self.items[1]) + elseif self.type == node_type.PLACEHOLDER then + for _, c in ipairs(self.children or {}) do + table.insert(insert_text, tostring(c)) + end + elseif self.type == node_type.TEXT then + table.insert(insert_text, self.esc) + end + return table.concat(insert_text, "") +end + +--- @private +local function new(t) + return setmetatable(t, Node) +end + +---Determine whether {t} is an AST-node. +---@param t table +---@return boolean +local function is_node(t) + return getmetatable(t) == Node +end + +M.is_node = is_node + +---Create a new snippet. +---@param children (ast-node[]) Contents of the snippet. +---@return table |lsp-parser-snippet| +function M.snippet(children) + return new({ + type = node_type.SNIPPET, + children = children, + }) +end + +---Create a new tabstop. +---@param tabstop (number) Position of this tabstop. +---@param transform (table|nil) optional transform applied to the tabstop. +---@return table |lsp-parser-tabstop| +function M.tabstop(tabstop, transform) + return new({ + type = node_type.TABSTOP, + tabstop = tabstop, + transform = transform, + }) +end + +---Create a new placeholder. +---@param tabstop (number) Position of the placeholder. +---@param children (ast-node[]) Content of the placeholder. +---@return table |lsp-parser-placeholder| +function M.placeholder(tabstop, children) + return new({ + type = node_type.PLACEHOLDER, + tabstop = tabstop, + children = children, + }) +end + +---Create a new variable. +---@param name (string) Name. +---@param replacement (node[] | transform | nil) +--- - (node[]) Inserted when the variable is empty. +--- - (transform) Applied to the variable's value. +---@return table |lsp-parser-variable| +function M.variable(name, replacement) + local transform, children + -- transform is an ast-node, children a flat list of nodes. + if is_node(replacement) then + transform = replacement + else + children = replacement + end + + return new({ + type = node_type.VARIABLE, + name = name, + transform = transform, + children = children, + }) +end + +---Create a new choice. +---@param tabstop (number) Position of the choice. +---@param items (string[]) Choices. +---@return table |lsp-parser-choice| +function M.choice(tabstop, items) + return new({ + type = node_type.CHOICE, + tabstop = tabstop, + items = items, + }) +end + +---Create a new transform. +---@param pattern (string) Regex applied to the variable/tabstop this transform +--- is supplied to. +---@param format (table of Format|Text) Replacement for the regex. +---@param option (string|nil) Regex-options, default "". +---@return table |lsp-parser-transform| +function M.transform(pattern, format, option) + return new({ + type = node_type.TRANSFORM, + pattern = pattern, + format = format, + option = option or "", + }) +end + +---Create a new format which either inserts the capture at {capture_index}, +---applies a modifier to the capture or inserts {if_text} if the capture is +---nonempty, and {else_text} otherwise. +---@param capture_index (number) Capture this format is applied to. +---@param capture_transform (string | table | nil) +--- - (string): {capture_transform} is a modifier. +--- - (table): {capture_transform} can contain either of +--- - {if_text} (string) Inserted for nonempty +--- capture. +--- - {else_text} (string) Inserted for empty or +--- undefined capture. +---@return table |lsp-parser-format| +function M.format(capture_index, capture_transform) + local if_text, else_text, modifier + if type(capture_transform) == "table" then + if_text = capture_transform.if_text + else_text = capture_transform.else_text + elseif type(capture_transform) == "string" then + modifier = capture_transform + end + + return new({ + type = node_type.FORMAT, + capture_index = capture_index, + modifier = modifier, + if_text = if_text, + else_text = else_text, + }) +end + +---Create new text. +---@param esc (string) Escaped text. +---@param raw (string|nil, default {esc}) Unescaped text. +--- +---@return table |lsp-parser-text| +function M.text(esc, raw) + return new({ + type = node_type.TEXT, + esc = esc, + raw = raw or esc, + }) +end + +function M.merge_adjacent_text(ast) + if ast.children then + -- build new table of children. + local new_children = {} + -- last_child shall always point to the last entry in new_children. + local last_child + + for _, child in ipairs(ast.children) do + -- first, recurse into children. + -- When we do this is not important, since it does not change the TEXT-nodes, here is just comfortable. + M.merge_adjacent_text(child) + + if + child.type == node_type.TEXT + and last_child + and last_child.type == node_type.TEXT + then + last_child.raw = last_child.raw .. child.raw + last_child.esc = last_child.esc .. child.esc + else + table.insert(new_children, child) + last_child = child + end + end + + ast.children = new_children + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/neovim_parser.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/neovim_parser.lua new file mode 100644 index 00000000..5848c83f --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/parser/neovim_parser.lua @@ -0,0 +1,479 @@ +-- ripped out of neovim. + +local P = {} + +local ast = require("luasnip.util.parser.neovim_ast") + +---Take characters until the target characters (The escape sequence is '\' + char) +---@param targets string[] The character list for stop consuming text. +---@param specials string[] If the character isn't contained in targets/specials, '\' will be left. +---@private +function P.take_until(targets, specials) + targets = targets or {} + specials = specials or {} + + return function(input, pos) + local new_pos = pos + local raw = {} + local esc = {} + while new_pos <= #input do + local c = string.sub(input, new_pos, new_pos) + if c == "\\" then + table.insert(raw, "\\") + new_pos = new_pos + 1 + c = string.sub(input, new_pos, new_pos) + if + not vim.tbl_contains(targets, c) + and not vim.tbl_contains(specials, c) + then + table.insert(esc, "\\") + end + table.insert(raw, c) + table.insert(esc, c) + new_pos = new_pos + 1 + else + if vim.tbl_contains(targets, c) then + break + end + table.insert(raw, c) + table.insert(esc, c) + new_pos = new_pos + 1 + end + end + + if new_pos == pos then + return P.unmatch(pos) + end + + return { + parsed = true, + value = { + raw = table.concat(raw, ""), + esc = table.concat(esc, ""), + }, + pos = new_pos, + } + end +end + +---@private +function P.unmatch(pos) + return { + parsed = false, + value = nil, + pos = pos, + } +end + +---@private +function P.map(parser, map) + return function(input, pos) + local result = parser(input, pos) + if result.parsed then + return { + parsed = true, + value = map(result.value), + pos = result.pos, + } + end + return P.unmatch(pos) + end +end + +---@private +function P.lazy(factory) + return function(input, pos) + return factory()(input, pos) + end +end + +---@private +function P.token(token) + return function(input, pos) + local maybe_token = string.sub(input, pos, pos + #token - 1) + if token == maybe_token then + return { + parsed = true, + value = maybe_token, + pos = pos + #token, + } + end + return P.unmatch(pos) + end +end + +---@private +function P.pattern(p) + return function(input, pos) + local maybe_match = string.match(string.sub(input, pos), "^" .. p) + if maybe_match then + return { + parsed = true, + value = maybe_match, + pos = pos + #maybe_match, + } + end + return P.unmatch(pos) + end +end + +---@private +function P.many(parser) + return function(input, pos) + local values = {} + local new_pos = pos + while new_pos <= #input do + local result = parser(input, new_pos) + if not result.parsed then + break + end + table.insert(values, result.value) + new_pos = result.pos + end + if #values > 0 then + return { + parsed = true, + value = values, + pos = new_pos, + } + end + return P.unmatch(pos) + end +end + +---@private +function P.any(...) + local parsers = { ... } + return function(input, pos) + for _, parser in ipairs(parsers) do + local result = parser(input, pos) + if result.parsed then + return result + end + end + return P.unmatch(pos) + end +end + +---@private +function P.opt(parser) + return function(input, pos) + local result = parser(input, pos) + return { + parsed = true, + value = result.value, + pos = result.pos, + } + end +end + +---@private +function P.seq(...) + local parsers = { ... } + return function(input, pos) + local values = {} + local new_pos = pos + for i, parser in ipairs(parsers) do + local result = parser(input, new_pos) + if result.parsed then + values[i] = result.value + new_pos = result.pos + else + return P.unmatch(pos) + end + end + return { + parsed = true, + value = values, + pos = new_pos, + } + end +end + +---see https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar + +local S = {} +S.dollar = P.token("$") +S.open = P.token("{") +S.close = P.token("}") +S.colon = P.token(":") +S.slash = P.token("/") +S.comma = P.token(",") +S.pipe = P.token("|") +S.plus = P.token("+") +S.minus = P.token("-") +S.question = P.token("?") +S.int = P.map(P.pattern("[0-9]+"), function(value) + return tonumber(value, 10) +end) +S.var = P.pattern("[%a_][%w_]+") +S.text = function(targets, specials) + return P.map(P.take_until(targets, specials), function(value) + return ast.text(value.esc, value.raw) + end) +end +S.patterntext = function(pattern) + return P.map(P.pattern(pattern), function(value) + return ast.text(value, value) + end) +end +S.text_or_empty = function(targets, specials) + return P.map( + P.any(P.take_until(targets, specials), P.token("")), + function(value) + -- if it is empty, we have to return a valid S.text-object. + if value == "" then + return ast.text("", "") + else + return ast.text(value.esc, value.raw) + end + end + ) +end + +S.toplevel = P.lazy(function() + return P.any(S.placeholder, S.tabstop, S.variable, S.choice) +end) + +S.format = P.any( + P.map(P.seq(S.dollar, S.int), function(values) + return ast.format(values[2]) + end), + P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values) + return ast.format(values[3]) + end), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + S.slash, + P.any( + P.token("upcase"), + P.token("downcase"), + P.token("capitalize"), + P.token("camelcase"), + P.token("pascalcase") + ), + S.close + ), + function(values) + return ast.format(values[3], values[6]) + end + ), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.seq( + S.question, + P.opt(P.take_until({ ":" }, { "\\" })), + S.colon, + P.opt(P.take_until({ "}" }, { "\\" })) + ), + S.close + ), + function(values) + return ast.format(values[3], { + if_text = values[5][2] and values[5][2].esc or "", + else_text = values[5][4] and values[5][4].esc or "", + }) + end + ), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.seq(S.plus, P.opt(P.take_until({ "}" }, { "\\" }))), + S.close + ), + function(values) + return ast.format(values[3], { + if_text = values[5][2] and values[5][2].esc or "", + }) + end + ), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + S.minus, + P.opt(P.take_until({ "}" }, { "\\" })), + S.close + ), + function(values) + return ast.format(values[3], { + else_text = values[6] and values[6].esc or "", + }) + end + ), + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.opt(P.take_until({ "}" }, { "\\" })), + S.close + ), + function(values) + return ast.format(values[3], { + else_text = values[5] and values[5].esc or "", + }) + end + ) +) + +S.transform = P.map( + P.seq( + S.slash, + P.take_until({ "/" }, { "\\" }), + S.slash, + P.many( + P.any( + S.format, + S.text({ "$", "/" }, { "\\" }), + S.patterntext("[^/]") + ) + ), + S.slash, + P.opt(P.pattern("[ig]+")) + ), + function(values) + return ast.transform(values[2].raw, values[4], values[6]) + end +) + +S.tabstop = P.any( + P.map(P.seq(S.dollar, S.int), function(values) + return ast.tabstop(values[2]) + end), + P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values) + return ast.tabstop(values[3]) + end), + P.map(P.seq(S.dollar, S.open, S.int, S.transform, S.close), function(values) + return ast.tabstop(values[3], values[4]) + end) +) + +S.placeholder = P.any( + P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.colon, + P.opt( + P.many( + P.any( + S.toplevel, + S.text({ "$", "}" }, { "\\" }), + S.patterntext("[^}]") + ) + ) + ), + S.close + ), + function(values) + -- no children -> manually create empty text. + return ast.placeholder(values[3], values[5] or { ast.text("") }) + end + ) +) + +S.choice = P.map( + P.seq( + S.dollar, + S.open, + S.int, + S.pipe, + P.opt( + P.many( + P.map( + P.seq(S.text_or_empty({ ",", "|" }), S.comma), + function(values) + return values[1].esc + end + ) + ) + ), + P.map(P.any(S.text_or_empty({ ",", "|" }), S.empty), function(values) + return values.esc + end), + S.pipe, + S.close + ), + function(values) + local choices = values[5] or {} + table.insert(choices, values[6]) + return ast.choice(values[3], choices) + end +) + +S.variable = P.any( + P.map(P.seq(S.dollar, S.var), function(values) + return ast.variable(values[2]) + end), + P.map(P.seq(S.dollar, S.open, S.var, S.close), function(values) + return ast.variable(values[3]) + end), + P.map(P.seq(S.dollar, S.open, S.var, S.transform, S.close), function(values) + return ast.variable(values[3], values[4]) + end), + P.map( + P.seq( + S.dollar, + S.open, + S.var, + S.colon, + P.many( + P.any( + S.toplevel, + S.text({ "$", "}" }, { "\\" }), + S.patterntext("[^}]") + ) + ), + S.close + ), + function(values) + return ast.variable(values[3], values[5]) + end + ) +) + +S.snippet = P.map( + P.many( + P.any(S.toplevel, S.text({ "$" }, { "}", "\\" }), S.patterntext(".")) + ), + function(values) + -- Insp(values) + return ast.snippet(values) + end +) + +local M = {} + +---Build the AST for {input}. +---@param input string A snippet as defined in +--- https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar +---@return (Snippet) +function M.parse(input) + local result = S.snippet(input, 1) + if not result.parsed then + error("snippet parsing failed.") + end + + ast.merge_adjacent_text(result.value) + return result.value +end + +M.ast = ast + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/path.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/path.lua new file mode 100644 index 00000000..83141098 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/path.lua @@ -0,0 +1,266 @@ +local Path = {} + +local uv = vim.loop + +local sep = (function() + if jit then + local os = jit.os:lower() + if vim.tbl_contains({ "linux", "osx", "bsd" }, os) then + return "/" + else + return "\\" + end + end + return package.config:sub(1, 1) +end)() + +local root_pattern = (function() + return uv.os_uname().sysname:find("Windows") and "%w%:" or "%/" +end)() + +function Path.join(...) + return table.concat({ ... }, sep) +end + +function Path.exists(filepath) + return uv.fs_stat(filepath) and true or false +end + +function Path.async_read_file(path, callback) + uv.fs_open(path, "r", tonumber("0666", 8), function(err, fd) + assert(not err, err) + uv.fs_fstat(fd, function(err, stat) + assert(not err, err) + uv.fs_read(fd, stat.size, 0, function(err, buffer) + assert(not err, err) + uv.fs_close(fd, function(err) + assert(not err, err) + callback(buffer) + end) + end) + end) + end) +end + +---@param path string +---@return string buffer @content of file +function Path.read_file(path) + -- permissions: rrr + local fd = assert(uv.fs_open(path, "r", tonumber("0444", 8))) + local stat = assert(uv.fs_fstat(fd)) + -- read from offset 0. + local buf = assert(uv.fs_read(fd, stat.size, 0)) + uv.fs_close(fd) + + return buf +end + +local MYCONFIG_ROOT + +if vim.env.MYVIMRC then + MYCONFIG_ROOT = vim.fn.fnamemodify(vim.env.MYVIMRC, ":p:h") +else + MYCONFIG_ROOT = vim.fn.getcwd() +end + +-- sometimes we don't want to resolve symlinks, but handle ~/ and ./ +function Path.expand_keep_symlink(filepath) + -- omit second return-value of :gsub + local res = filepath + :gsub("^~", vim.env.HOME) + :gsub("^[.][/\\]", MYCONFIG_ROOT .. sep) + return res +end +function Path.expand(filepath) + return uv.fs_realpath(Path.expand_keep_symlink(filepath)) +end + +-- do our best at normalizing a non-existing path. +function Path.normalize_nonexisting(filepath, cwd) + cwd = cwd or vim.fn.getcwd() + + local normalized = filepath + -- replace multiple slashes by one. + :gsub(sep .. sep .. "+", sep) + -- remove trailing slash. + :gsub(sep .. "$", "") + -- remove ./ from path. + :gsub("%." .. sep, "") + + -- if not yet absolute, prepend path to current directory. + if not normalized:match("^" .. root_pattern .. "") then + normalized = Path.join(cwd, normalized) + end + + return normalized +end + +function Path.expand_nonexisting(filepath, cwd) + filepath + -- replace ~ with home-directory. + :gsub("^~", vim.env.HOME) + -- replace ./ or .\ with config-directory (likely ~/.config/nvim) + :gsub( + "^[.][/\\]", + MYCONFIG_ROOT .. sep + ) + + return Path.normalize_nonexisting(filepath, cwd) +end + +-- do our best at expanding a path that may or may not exist (ie. check if it +-- exists, if so do regular expand, and guess expanded path otherwise) +-- Not the clearest name :/ +function Path.expand_maybe_nonexisting(filepath, cwd) + local real_expanded = Path.expand(filepath) + if not real_expanded then + real_expanded = Path.expand_nonexisting(filepath, cwd) + end + return real_expanded +end + +function Path.normalize_maybe_nonexisting(filepath, cwd) + local real_normalized = Path.normalize(filepath) + if not real_normalized then + real_normalized = Path.normalize_nonexisting(filepath, cwd) + end + return real_normalized +end + +---Return files and directories in path as a list +---@param root string +---@return string[] files, string[] directories +function Path.scandir(root) + local files, dirs = {}, {} + local fs = uv.fs_scandir(root) + if fs then + local name, type = "", "" + while name do + name, type = uv.fs_scandir_next(fs) + local path = Path.join(root, name) + -- On networked filesystems, it can happen that we get + -- a name, but no type. In this case, we must query the + -- type manually via fs_stat(). See issue: + -- https://github.com/luvit/luv/issues/660 + if name and not type then + local stat = uv.fs_stat(path) + type = stat and stat.type + end + if type == "file" then + table.insert(files, path) + elseif type == "directory" then + table.insert(dirs, path) + elseif type == "link" then + local followed_path = uv.fs_realpath(path) + if followed_path then + local stat = uv.fs_stat(followed_path) + if stat.type == "file" then + table.insert(files, path) + elseif stat.type == "directory" then + table.insert(dirs, path) + end + end + end + end + end + return files, dirs +end + +---Get basename +---@param filepath string +---@param ext boolean if true, separate the file extension +---@return string, string? +---Example: +--- Path.basename("~/.config/nvim/init.lua") -> init.lua +--- Path.basename("~/.config/nvim/init.lua", true) -> init, lua +function Path.basename(filepath, ext) + local base = filepath + if base:find(sep) then + base = base:match(("%s([^%s]+)$"):format(sep, sep)) + end + if ext then + return base:match("(.*)%.(.+)") + else + return base + end +end + +function Path.extension(fname) + return fname:match("%.([^%.]+)$") +end + +function Path.components(path) + return vim.split(path, sep, { plain = true, trimempty = true }) +end + +---Get parent of a path, without trailing separator +---if path is a directory or does not have a parent, returns nil +---Example: +--- On platforms that use "\\" backslash as path separator, e.g., Windows: +--- Path.parent("C:/project_root/file.txt") -- returns "C:/project_root" +--- Path.parent([[C:\project_root\file.txt]]) -- returns [[C:\project_root]] +--- +--- -- the followings return `nil`s +--- Path.parent("C:/") +--- Path.parent([[C:\]]) +--- Path.parent([[C:\project_root\]]) +--- +--- -- WARN: although it's unlikely that we will reach the driver's root +--- -- level, Path.parent("C:\file.txt") returns "C:", and please be +--- -- cautious when passing the parent path to some vim functions because +--- -- some vim functions on Windows treat "C:" as a file instead: +--- -- vim.fn.fnamemodify("C:", ":p") -- returns $CWD .. sep .. "C:" +--- -- To get the desired result, use vim.fn.fnamemodify("C:" .. sep, ":p") +--- +--- On platforms that use "/" forward slash as path separator, e.g., linux: +--- Path.parent("/project_root/file.txt") returns "/project_root" +--- Path.parent("/file.txt") returns "" +--- +--- -- the followings return `nil`s +--- Path.parent("/") +--- Path.parent("/project_root/") +--- +--- -- backslash in a valid filename character in linux: +--- Path.parent([[/project_root/\valid\file\name.txt]]) returns "/project_root" +Path.parent = (function() + ---@alias PathSeparator "/" | "\\" + ---@param os_sep PathSeparator + ---@return fun(string): string | nil + local function generate_parent(os_sep) + if os_sep == "/" then + ---@param path string + ---@return string | nil + return function(path) + local last_component = path:match("[/]+[^/]+$") + if not last_component then + return nil + end + + return path:sub(1, #path - #last_component) + end + else + ---@param path string + ---@return string | nil + return function(path) + local last_component = path:match("[/\\]+[^/\\]+$") + if not last_component then + return nil + end + + return path:sub(1, #path - #last_component) + end + end + end + + -- for test only + if __LUASNIP_TEST_SEP_OVERRIDE then + return generate_parent + else + return generate_parent(sep) + end +end)() + +-- returns nil if the file does not exist! +Path.normalize = uv.fs_realpath + +return Path diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/pattern_tokenizer.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/pattern_tokenizer.lua new file mode 100644 index 00000000..8dfc1a44 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/pattern_tokenizer.lua @@ -0,0 +1,137 @@ +local is_class = { + a = true, + c = true, + d = true, + l = true, + p = true, + s = true, + u = true, + w = true, + x = true, + z = true, + -- and uppercase versions. + A = true, + C = true, + D = true, + L = true, + P = true, + S = true, + U = true, + W = true, + X = true, + Z = true, + -- all others false. +} + +local is_rep_mod = { + ["+"] = true, + ["*"] = true, + ["-"] = true, + ["?"] = true, +} + +local function is_escaped(text, indx) + local count = 0 + for i = indx - 1, 1, -1 do + if string.sub(text, i, i) == "%" then + count = count + 1 + else + break + end + end + return count % 2 == 1 +end + +local function charset_end_indx(string, start_indx) + -- set plain + local indx = string:find("]", start_indx, true) + -- find unescaped ']' + while indx and is_escaped(string, indx) do + indx = string:find("]", indx + 1, true) + end + return indx +end + +return { + tokenize = function(pattern) + local indx = 1 + local current_text = "" + local tokens = {} + -- assume the pattern starts with text (as opposed to eg. a character + -- class), worst-case an empty textNode is (unnecessarily) inserted at + -- the beginning. + local is_text = true + while indx <= #pattern do + local next_indx + local next_text + local next_is_text + -- for some atoms *,+,-,? are not applicable, ignore them. + local repeatable = true + local char = pattern:sub(indx, indx) + if char == "%" then + if pattern:sub(indx + 1, indx + 1) == "b" then + -- %b seems to consume exactly the next two chars literally. + next_is_text = false + next_indx = indx + 4 + repeatable = false + elseif is_class[pattern:sub(indx + 1, indx + 1)] then + next_is_text = false + next_indx = indx + 2 + else + -- not a class, just an escaped character. + next_is_text = true + next_indx = indx + 2 + -- only append escaped char, not '%'. + end + elseif char == "." then + next_is_text = false + next_indx = indx + 1 + elseif char == "[" then + next_is_text = false + -- if not found, just exit loop now, pattern is malformed. + next_indx = (charset_end_indx(pattern, indx) or #pattern) + 1 + elseif + char == "(" + or char == ")" + or (char == "^" and indx == 1) + then + -- ^ is interpreted literally if not at beginning. + -- $ will always be interpreted literally in triggers. + + -- remove ( and ) from text. + -- keep text or no-text active. + next_is_text = is_text + -- increase indx to exclude ( from tokens. + indx = indx + 1 + next_indx = indx + -- cannot repeat group. + repeatable = false + else + next_is_text = true + next_indx = indx + 1 + end + + if repeatable and is_rep_mod[pattern:sub(next_indx, next_indx)] then + next_indx = next_indx + 1 + next_is_text = false + end + + next_text = pattern:sub(indx, next_indx - 1) + + -- check if this token is still the same as the previous. + if next_is_text == is_text then + current_text = current_text .. next_text + else + tokens[#tokens + 1] = current_text + current_text = next_text + end + + indx = next_indx + is_text = next_is_text + end + + -- add last part, would normally be added at the end of the loop. + tokens[#tokens + 1] = current_text + return tokens + end, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/select.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/select.lua new file mode 100644 index 00000000..decf8fb5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/select.lua @@ -0,0 +1,141 @@ +local M = {} + +local SELECT_RAW = "LUASNIP_SELECT_RAW" +local SELECT_DEDENT = "LUASNIP_SELECT_DEDENT" +local TM_SELECT = "LUASNIP_TM_SELECT" + +function M.retrieve() + local ok, val = pcall(vim.api.nvim_buf_get_var, 0, SELECT_RAW) + if ok then + local result = { + val, + vim.api.nvim_buf_get_var(0, SELECT_DEDENT), + vim.api.nvim_buf_get_var(0, TM_SELECT), + } + + vim.api.nvim_buf_del_var(0, SELECT_RAW) + vim.api.nvim_buf_del_var(0, SELECT_DEDENT) + vim.api.nvim_buf_del_var(0, TM_SELECT) + + return unpack(result) + end + return {}, {}, {} +end + +local function get_min_indent(lines) + -- "^(%s*)%S": match only lines that actually contain text. + local min_indent = lines[1]:match("^(%s*)%S") + for i = 2, #lines do + -- %s* -> at least matches + local line_indent = lines[i]:match("^(%s*)%S") + -- ignore if not matched. + if line_indent then + -- if no line until now matched, use line_indent. + if not min_indent or #line_indent < #min_indent then + min_indent = line_indent + end + end + end + return min_indent +end + +local function store_registers(...) + local names = { ... } + local restore_data = {} + for _, name in ipairs(names) do + restore_data[name] = { + data = vim.fn.getreg(name), + type = vim.fn.getregtype(name), + } + end + return restore_data +end + +local function restore_registers(restore_data) + for name, name_restore_data in pairs(restore_data) do + vim.fn.setreg(name, name_restore_data.data, name_restore_data.type) + end +end + +-- subtle: `:lua` exits VISUAL, which means that the '< '>-marks will be set correctly! +-- Afterwards, we can just use lua, which does not change the mode. +M.select_keys = + [[:lua require("luasnip.util.select").pre_cut()gv"zslua require('luasnip.util.select').post_cut("z")]] + +local saved_registers +local lines +local start_line, start_col, end_line, end_col +local mode +function M.pre_cut() + -- store registers so we don't change any of them. + -- "" is affected since we perform a cut (s), 1-9 also (although :h + -- quote_number seems to state otherwise for cuts to specific registers..?). + saved_registers = + store_registers("", "1", "2", "3", "4", "5", "6", "7", "8", "9", "z") + + -- store data needed for de-indenting lines. + start_line = vim.fn.line("'<") - 1 + start_col = vim.fn.col("'<") + end_line = vim.fn.line("'>") - 1 + end_col = vim.fn.col("'>") + -- +1: include final line. + lines = vim.api.nvim_buf_get_lines(0, start_line, end_line + 1, true) + mode = vim.fn.visualmode() +end + +function M.post_cut(register_name) + -- remove trailing newline. + local chunks = vim.split(vim.fn.getreg(register_name):gsub("\n$", ""), "\n") + + -- make sure to restore the registers to the state they were before cutting. + restore_registers(saved_registers) + + local tm_select, select_dedent = vim.deepcopy(chunks), vim.deepcopy(chunks) + + local min_indent = get_min_indent(lines) or "" + if mode == "V" then + tm_select[1] = tm_select[1]:gsub("^%s+", "") + -- remove indent from all lines: + for i = 1, #select_dedent do + select_dedent[i] = select_dedent[i]:gsub("^" .. min_indent, "") + end + -- due to the trailing newline of the last line, and vim.split's + -- behaviour, the last line of `chunks` is always empty. + -- Keep this + elseif mode == "v" then + -- if selection starts inside indent, remove indent. + if #min_indent > start_col then + select_dedent[1] = lines[1]:gsub(min_indent, "") + end + for i = 2, #select_dedent - 1 do + select_dedent[i] = select_dedent[i]:gsub(min_indent, "") + end + + -- remove as much indent from the last line as possible. + if #min_indent > end_col then + select_dedent[#select_dedent] = "" + else + select_dedent[#select_dedent] = + select_dedent[#select_dedent]:gsub("^" .. min_indent, "") + end + else + -- in block: if indent is in block, remove the part of it that is inside + -- it for select_dedent. + if #min_indent > start_col then + local indent_in_block = min_indent:sub(start_col, #min_indent) + for i, line in ipairs(chunks) do + select_dedent[i] = line:gsub("^" .. indent_in_block, "") + end + end + end + + vim.api.nvim_buf_set_var(0, SELECT_RAW, chunks) + vim.api.nvim_buf_set_var(0, SELECT_DEDENT, select_dedent) + vim.api.nvim_buf_set_var(0, TM_SELECT, tm_select) + + lines = nil + start_line, start_col, end_line, end_col = nil, nil, nil, nil + mode = nil +end + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/str.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/str.lua new file mode 100644 index 00000000..7b8ec5f5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/str.lua @@ -0,0 +1,141 @@ +-- Some string processing utility functions +local M = {} + +---In-place dedents strings in lines. +---@param lines string[]. +local function dedent(lines) + if #lines > 0 then + local ind_size = math.huge + for i, _ in ipairs(lines) do + local i1, i2 = lines[i]:find("^%s*[^%s]") + if i1 and i2 < ind_size then + ind_size = i2 + end + end + for i, _ in ipairs(lines) do + lines[i] = lines[i]:sub(ind_size, -1) + end + end +end + +---Applies opts to lines. +---lines is modified in-place. +---@param lines string[]. +---@param options table, required, can have values: +--- - trim_empty: removes empty first and last lines. +--- - dedent: removes indent common to all lines. +function M.process_multiline(lines, options) + if options.trim_empty then + if lines[1]:match("^%s*$") then + table.remove(lines, 1) + end + if #lines > 0 and lines[#lines]:match("^%s*$") then + lines[#lines] = nil + end + end + + if options.dedent then + dedent(lines) + end +end + +function M.dedent(s) + local lst = vim.split(s, "\n") + dedent(lst) + return table.concat(lst, "\n") +end + +local function is_escaped(s, indx) + local count = 0 + for i = indx - 1, 1, -1 do + if string.sub(s, i, i) == "\\" then + count = count + 1 + else + break + end + end + return count % 2 == 1 +end + +--- return position of next (relative to `start`) unescaped occurence of +--- `target` in `s`. +---@param s string +---@param target string +---@param start number +local function find_next_unescaped(s, target, start) + while true do + local from = s:find(target, start, true) + if not from then + return nil + end + if not is_escaped(s, from) then + return from + end + start = from + 1 + end +end + +--- Creates iterator that returns all positions of substrings .* +--- in `s`, where left and right are not escaped. +--- Only complete pairs left,right are returned, an unclosed left is ignored. +---@param s string +---@param left string +---@param right string +---@return function: iterator, returns pairs from,to. +function M.unescaped_pairs(s, left, right) + local search_from = 1 + + return function() + local match_from = find_next_unescaped(s, left, search_from) + if not match_from then + return nil + end + local match_to = find_next_unescaped(s, right, match_from + 1) + if not match_to then + return nil + end + + search_from = match_to + 1 + return match_from, match_to + end +end + +function M.aupatescape(s) + if vim.fn.has("win32") or vim.fn.has("win64") then + -- windows: replace \ with / for au-pattern. + s, _ = s:gsub("\\", "/") + end + local escaped, _ = s:gsub(",", "\\,") + return vim.fn.fnameescape(escaped) +end + +function M.sanitize(str) + return str:gsub("%\r", "") +end + +-- string-operations implemented according to +-- https://github.com/microsoft/vscode/blob/71c221c532996c9976405f62bb888283c0cf6545/src/vs/editor/contrib/snippet/browser/snippetParser.ts#L372-L415 +-- such that they can be used for snippet-transformations in vscode-snippets. +local function capitalize(str) + -- uppercase first character. + return str:gsub("^.", string.upper) +end +local function pascalcase(str) + local pascalcased = "" + for match in str:gmatch("[a-zA-Z0-9]+") do + pascalcased = pascalcased .. capitalize(match) + end + return pascalcased +end +M.vscode_string_modifiers = { + upcase = string.upper, + downcase = string.lower, + capitalize = capitalize, + pascalcase = pascalcase, + camelcase = function(str) + -- same as pascalcase, but first character lowercased. + return pascalcase(str):gsub("^.", string.lower) + end, +} + +return M diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/table.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/table.lua new file mode 100644 index 00000000..31762686 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/table.lua @@ -0,0 +1,39 @@ +---Convert set of values to a list of those values. +---@generic T +---@param tbl T|T[]|table +---@return table +local function set_to_list(tbl) + local ls = {} + + for v, _ in pairs(tbl) do + table.insert(ls, v) + end + + return ls +end + +---Convert value or list of values to a table of booleans for fast lookup. +---@generic T +---@param values T|T[]|table +---@return table +local function list_to_set(values) + if values == nil then + return {} + end + + if type(values) ~= "table" then + return { [values] = true } + end + + local list = {} + for _, v in ipairs(values) do + list[v] = true + end + + return list +end + +return { + list_to_set = list_to_set, + set_to_list = set_to_list, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/time.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/time.lua new file mode 100644 index 00000000..39fc9233 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/time.lua @@ -0,0 +1,13 @@ +-- http://lua-users.org/wiki/TimeZone +local function get_timezone_offset(ts) + local utcdate = os.date("!*t", ts) + local localdate = os.date("*t", ts) + localdate.isdst = false -- this is the trick + local diff = os.difftime(os.time(localdate), os.time(utcdate)) + local h, m = math.modf(diff / 3600) + return string.format("%+.4d", 100 * h + 60 * m) +end + +return { + get_timezone_offset = get_timezone_offset, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/types.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/types.lua new file mode 100644 index 00000000..a9ded11d --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/types.lua @@ -0,0 +1,34 @@ +return { + textNode = 1, + insertNode = 2, + functionNode = 3, + snippetNode = 4, + choiceNode = 5, + dynamicNode = 6, + snippet = 7, + exitNode = 8, + restoreNode = 9, + node_types = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, + names = { + "textNode", + "insertNode", + "functionNode", + "snippetNode", + "choiceNode", + "dynamicNode", + "snippet", + "exitNode", + "restoreNode", + }, + names_pascal_case = { + "TextNode", + "InsertNode", + "FunctionNode", + "SnippetNode", + "ChoiceNode", + "DynamicNode", + "Snippet", + "ExitNode", + "RestoreNode", + }, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/util.lua b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/util.lua new file mode 100644 index 00000000..956eaf7a --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/lua/luasnip/util/util.lua @@ -0,0 +1,522 @@ +local session = require("luasnip.session") + +local function get_cursor_0ind() + local c = vim.api.nvim_win_get_cursor(0) + c[1] = c[1] - 1 + return c +end + +-- don't use utf-indexed column, win_set_cursor ignores these. +local function set_cursor_0ind(c) + c[1] = c[1] + 1 + vim.api.nvim_win_set_cursor(0, c) +end + +-- pos: (0,0)-indexed. +local function line_chars_before(pos) + -- cur-rows are 1-indexed, api-rows 0. + local line = vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false) + return string.sub(line[1], 1, pos[2]) +end + +-- returns current line with text up-to and excluding the cursor. +local function get_current_line_to_cursor() + return line_chars_before(get_cursor_0ind()) +end + +-- delete n chars before cursor, MOVES CURSOR +local function remove_n_before_cur(n) + local cur = get_cursor_0ind() + vim.api.nvim_buf_set_text(0, cur[1], cur[2] - n, cur[1], cur[2], { "" }) + cur[2] = cur[2] - n + set_cursor_0ind(cur) +end + +-- in-place modifies the table. +local function dedent(text, indentstring) + -- 2 because 1 shouldn't contain indent. + for i = 2, #text do + text[i] = text[i]:gsub("^" .. indentstring, "") + end + return text +end + +-- in-place insert indenstrig before each line. +local function indent(text, indentstring) + for i = 2, #text - 1, 1 do + -- only indent if there is actually text. + if #text[i] > 0 then + text[i] = indentstring .. text[i] + end + end + -- assuming that the last line should be indented as it is probably + -- followed by some other node, therefore isn't an empty line. + if #text > 1 then + text[#text] = indentstring .. text[#text] + end + return text +end + +--- In-place expands tabs in `text`. +--- Difficulties: +--- we cannot simply replace tabs with a given number of spaces, the tabs align +--- text at multiples of `tabwidth`. This is also the reason we need the number +--- of columns the text is already indented by (otherwise we can only start a 0). +---@param text string[], multiline string. +---@param tabwidth number, displaycolumns one tab should shift following text +--- by. +---@param parent_indent_displaycolumns number, displaycolumn this text is +--- already at. +---@return string[], `text` (only for simple nesting). +local function expand_tabs(text, tabwidth, parent_indent_displaycolumns) + for i, line in ipairs(text) do + local new_line = "" + local start_indx = 1 + while true do + local tab_indx = line:find("\t", start_indx, true) + -- if no tab found, sub till end (ie. -1). + new_line = new_line .. line:sub(start_indx, (tab_indx or 0) - 1) + if tab_indx then + -- #new_line is index of this tab in new_line. + new_line = new_line + .. string.rep( + " ", + tabwidth + - ( + (parent_indent_displaycolumns + #new_line) + % tabwidth + ) + ) + else + -- reached end of string. + break + end + start_indx = tab_indx + 1 + end + text[i] = new_line + end + return text +end + +local function tab_width() + return vim.bo.shiftwidth ~= 0 and vim.bo.shiftwidth or vim.bo.tabstop +end + +local function mark_pos_equal(m1, m2) + local p1 = vim.api.nvim_buf_get_extmark_by_id(0, session.ns_id, m1, {}) + local p2 = vim.api.nvim_buf_get_extmark_by_id(0, session.ns_id, m2, {}) + return p1[1] == p2[1] and p1[2] == p2[2] +end + +local function move_to_mark(id) + local new_cur_pos + new_cur_pos = vim.api.nvim_buf_get_extmark_by_id( + 0, + session.ns_id, + id, + { details = false } + ) + set_cursor_0ind(new_cur_pos) +end + +local function bytecol_to_utfcol(pos) + local line = vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false) + -- line[1]: get_lines returns table. + return { pos[1], vim.str_utfindex(line[1] or "", pos[2]) } +end + +local function replace_feedkeys(keys, opts) + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes(keys, true, false, true), + -- folds are opened manually now, no need to pass t. + -- n prevents langmap from interfering. + opts or "n", + true + ) +end + +-- pos: (0,0)-indexed. +local function cursor_set_keys(pos, before) + if before then + if pos[2] == 0 then + pos[1] = pos[1] - 1 + -- pos2 is set to last columnt of previous line. + -- # counts bytes, but win_set_cursor expects bytes, so all's good. + pos[2] = + #vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false)[1] + else + pos[2] = pos[2] - 1 + end + end + + return "lua vim.api.nvim_win_set_cursor(0,{" + -- +1, win_set_cursor starts at 1. + .. pos[1] + 1 + .. "," + -- -1 works for multibyte because of rounding, apparently. + .. pos[2] + .. "})" + .. ":silent! foldopen!" +end + +-- any for any mode. +-- other functions prefixed with eg. normal have to be in that mode, the +-- initial esc removes that need. +local function any_select(b, e) + -- stylua: ignore + replace_feedkeys( + -- this esc -> movement sometimes leads to a slight flicker + -- TODO: look into preventing that reliably. + -- Go into visual, then place endpoints. + -- This is to allow us to place the cursor on the \n of a line. + -- see #1158 + "" + -- open folds that contain this selection. + -- we assume that the selection is contained in at most one fold, and + -- that that fold covers b. + -- if we open the fold while visual is active, the selection will be + -- wrong, so this is necessary before we enter VISUAL. + .. cursor_set_keys(b) + -- start visual highlight and move to b again. + -- since we are now in visual, this might actually move the cursor. + .. "v" + .. cursor_set_keys(b) + -- swap to other end of selection, and move it to e. + .. "o" + .. (vim.o.selection == "exclusive" and + cursor_set_keys(e) or + -- set before + cursor_set_keys(e, true)) + .. "o_" ) +end + +local function normal_move_on_insert(new_cur_pos) + -- moving in normal and going into insert is kind of annoying, eg. when the + -- cursor is, in normal, on a tab, i will set it on the beginning of the + -- tab. There's more problems, but this is very safe. + replace_feedkeys("i" .. cursor_set_keys(new_cur_pos)) +end + +local function insert_move_on(new_cur_pos) + -- maybe feedkeys this too. + set_cursor_0ind(new_cur_pos) + vim.api.nvim_command("redraw!") +end + +local function multiline_equal(t1, t2) + for i, line in ipairs(t1) do + if line ~= t2[i] then + return false + end + end + + return #t1 == #t2 +end + +local function word_under_cursor(cur, line) + local ind_start = 1 + local ind_end = #line + + while true do + local tmp = string.find(line, "%W%w", ind_start) + if not tmp then + break + end + if tmp > cur[2] + 1 then + break + end + ind_start = tmp + 1 + end + + local tmp = string.find(line, "%w%W", cur[2] + 1) + if tmp then + ind_end = tmp + end + + return string.sub(line, ind_start, ind_end) +end + +-- Put text and update cursor(pos) where cursor is byte-indexed. +local function put(text, pos) + vim.api.nvim_buf_set_text(0, pos[1], pos[2], pos[1], pos[2], text) + -- add rows + pos[1] = pos[1] + #text - 1 + -- add columns, start at 0 if no rows were added, else at old col-value. + pos[2] = (#text > 1 and 0 or pos[2]) + #text[#text] +end + +--[[ Wraps the value in a table if it's not one, makes + the first element an empty str if the table is empty]] +local function to_string_table(value) + if not value then + return { "" } + end + if type(value) == "string" then + return { value } + end + -- at this point it's a table + if #value == 0 then + return { "" } + end + -- non empty table + return value +end + +-- Wrap node in a table if it is not one +local function wrap_nodes(nodes) + -- safe to assume, if nodes has a metatable, it is a single node, not a + -- table. + if getmetatable(nodes) and nodes.type then + return { nodes } + else + return nodes + end +end + +local function pos_equal(p1, p2) + return p1[1] == p2[1] and p1[2] == p2[2] +end + +local function string_wrap(lines, pos) + local new_lines = vim.deepcopy(lines) + if #new_lines == 1 and #new_lines[1] == 0 then + return { "$" .. (pos and tostring(pos) or "{}") } + end + new_lines[1] = "${" + .. (pos and (tostring(pos) .. ":") or "") + .. new_lines[1] + new_lines[#new_lines] = new_lines[#new_lines] .. "}" + return new_lines +end + +-- Heuristic to extract the comment style from the commentstring +local _comments_cache = {} +local function buffer_comment_chars() + local commentstring = vim.bo.commentstring + if _comments_cache[commentstring] then + return _comments_cache[commentstring] + end + local comments = { "//", "/*", "*/" } + local placeholder = "%s" + local index_placeholder = commentstring:find(vim.pesc(placeholder)) + if index_placeholder then + index_placeholder = index_placeholder - 1 + if index_placeholder + #placeholder == #commentstring then + comments[1] = vim.trim(commentstring:sub(1, -#placeholder - 1)) + else + comments[2] = vim.trim(commentstring:sub(1, index_placeholder)) + comments[3] = vim.trim( + commentstring:sub(index_placeholder + #placeholder + 1, -1) + ) + end + end + _comments_cache[commentstring] = comments + return comments +end + +local function to_line_table(table_or_string) + local tbl = to_string_table(table_or_string) + + -- split entries at \n. + local line_table = {} + for _, str in ipairs(tbl) do + local split = vim.split(str, "\n", true) + for i = 1, #split do + line_table[#line_table + 1] = split[i] + end + end + + return line_table +end + +local function find_outer_snippet(node) + while node.parent do + node = node.parent + end + return node +end + +local function redirect_filetypes(fts) + local snippet_fts = {} + + for _, ft in ipairs(fts) do + vim.list_extend(snippet_fts, session.ft_redirect[ft]) + end + + return snippet_fts +end + +local function deduplicate(list) + vim.validate({ list = { list, "table" } }) + local ret = {} + local contains = {} + for _, v in ipairs(list) do + if not contains[v] then + table.insert(ret, v) + contains[v] = true + end + end + return ret +end + +local function get_snippet_filetypes() + local config = require("luasnip.session").config + local fts = config.ft_func() + -- add all last. + table.insert(fts, "all") + + return deduplicate(redirect_filetypes(fts)) +end + +local function pos_add(p1, p2) + return { p1[1] + p2[1], p1[2] + p2[2] } +end +local function pos_sub(p1, p2) + return { p1[1] - p2[1], p1[2] - p2[2] } +end + +local function pop_front(list) + local front = list[1] + for i = 2, #list do + list[i - 1] = list[i] + end + list[#list] = nil + return front +end + +local function sorted_keys(t) + local s = {} + local i = 1 + for k, _ in pairs(t) do + s[i] = k + i = i + 1 + end + table.sort(s) + return s +end + +-- from https://www.lua.org/pil/19.3.html +local function key_sorted_pairs(t) + local sorted = sorted_keys(t) + local i = 0 + return function() + i = i + 1 + if sorted[i] == nil then + return nil + else + return sorted[i], t[sorted[i]], i + end + end +end + +local function no_region_check_wrap(fn, ...) + session.jump_active = true + -- will run on next tick, after autocommands (especially CursorMoved) for this are done. + vim.schedule(function() + session.jump_active = false + end) + return fn(...) +end + +local function id(a) + return a +end + +local function no() + return false +end + +local function yes() + return true +end + +local function reverse_lookup(t) + local rev = {} + for k, v in pairs(t) do + rev[v] = k + end + return rev +end + +local function nop() end + +local function indx_of(t, v) + for i, value in ipairs(t) do + if v == value then + return i + end + end + return nil +end + +local function ternary(cond, if_val, else_val) + if cond == true then + return if_val + else + return else_val + end +end + +-- just compare two integers. +local function cmp(i1, i2) + -- lets hope this ends up as one cmp. + if i1 < i2 then + return -1 + end + if i1 > i2 then + return 1 + end + return 0 +end + +-- compare two positions, <0 => pos1 pos1=pos2, >0 => pos1 > pos2. +local function pos_cmp(pos1, pos2) + -- if row is different it determines result, otherwise the column does. + return 2 * cmp(pos1[1], pos2[1]) + cmp(pos1[2], pos2[2]) +end + +return { + get_cursor_0ind = get_cursor_0ind, + set_cursor_0ind = set_cursor_0ind, + move_to_mark = move_to_mark, + normal_move_on_insert = normal_move_on_insert, + insert_move_on = insert_move_on, + any_select = any_select, + remove_n_before_cur = remove_n_before_cur, + get_current_line_to_cursor = get_current_line_to_cursor, + line_chars_before = line_chars_before, + mark_pos_equal = mark_pos_equal, + multiline_equal = multiline_equal, + word_under_cursor = word_under_cursor, + put = put, + to_string_table = to_string_table, + wrap_nodes = wrap_nodes, + pos_equal = pos_equal, + dedent = dedent, + indent = indent, + expand_tabs = expand_tabs, + tab_width = tab_width, + buffer_comment_chars = buffer_comment_chars, + string_wrap = string_wrap, + to_line_table = to_line_table, + find_outer_snippet = find_outer_snippet, + redirect_filetypes = redirect_filetypes, + get_snippet_filetypes = get_snippet_filetypes, + json_decode = vim.json.decode, + json_encode = vim.json.encode, + bytecol_to_utfcol = bytecol_to_utfcol, + pos_sub = pos_sub, + pos_add = pos_add, + deduplicate = deduplicate, + pop_front = pop_front, + key_sorted_pairs = key_sorted_pairs, + no_region_check_wrap = no_region_check_wrap, + id = id, + no = no, + yes = yes, + reverse_lookup = reverse_lookup, + nop = nop, + indx_of = indx_of, + ternary = ternary, + pos_cmp = pos_cmp, +} diff --git a/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/doc/luasnip.txt b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/doc/luasnip.txt new file mode 100644 index 00000000..e0ce8a2a --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/doc/luasnip.txt @@ -0,0 +1,3709 @@ +*luasnip.txt* For NVIM v0.8.0 Last change: 2024 May 24 + +============================================================================== +Table of Contents *luasnip-table-of-contents* + +1. Basics |luasnip-basics| + - Jump-Index |luasnip-basics-jump-index| + - Adding Snippets |luasnip-basics-adding-snippets| + - Snippet Insertion |luasnip-basics-snippet-insertion| +2. Node |luasnip-node| + - Api |luasnip-node-api| +3. Snippets |luasnip-snippets| + - Data |luasnip-snippets-data| +4. TextNode |luasnip-textnode| +5. InsertNode |luasnip-insertnode| +6. FunctionNode |luasnip-functionnode| +7. Node Reference |luasnip-node-reference| +8. ChoiceNode |luasnip-choicenode| +9. SnippetNode |luasnip-snippetnode| +10. IndentSnippetNode |luasnip-indentsnippetnode| +11. DynamicNode |luasnip-dynamicnode| +12. RestoreNode |luasnip-restorenode| +13. Key Indexer |luasnip-key-indexer| +14. Absolute Indexer |luasnip-absolute-indexer| +15. MultiSnippet |luasnip-multisnippet| +16. Extras |luasnip-extras| + - Lambda |luasnip-extras-lambda| + - Match |luasnip-extras-match| + - Repeat |luasnip-extras-repeat| + - Partial |luasnip-extras-partial| + - Nonempty |luasnip-extras-nonempty| + - Dynamic Lambda |luasnip-extras-dynamic-lambda| + - FMT |luasnip-extras-fmt| + - Conditions |luasnip-extras-conditions| + - On The Fly-Snippets |luasnip-extras-on-the-fly-snippets| + - select_choice |luasnip-extras-select_choice| + - Filetype-Functions |luasnip-extras-filetype-functions| + - Postfix-Snippet |luasnip-extras-postfix-snippet| + - Treesitter-Postfix-Snippet |luasnip-extras-treesitter-postfix-snippet| + - Snippet List |luasnip-extras-snippet-list| + - Snippet Location |luasnip-extras-snippet-location| +17. Extend Decorator |luasnip-extend-decorator| +18. LSP-Snippets |luasnip-lsp-snippets| + - Snipmate Parser |luasnip-lsp-snippets-snipmate-parser| + - Transformations |luasnip-lsp-snippets-transformations| +19. Variables |luasnip-variables| + - Environment Namespaces |luasnip-variables-environment-namespaces| + - LSP-Variables |luasnip-variables-lsp-variables| +20. Loaders |luasnip-loaders| + - Snippet-specific filetypes |luasnip-loaders-snippet-specific-filetypes| + - VS-Code |luasnip-loaders-vs-code| + - SNIPMATE |luasnip-loaders-snipmate| + - Lua |luasnip-loaders-lua| + - edit_snippets |luasnip-loaders-edit_snippets| +21. SnippetProxy |luasnip-snippetproxy| +22. ext_opts |luasnip-ext_opts| +23. Docstrings |luasnip-docstrings| +24. Docstring-Cache |luasnip-docstring-cache| +25. Events |luasnip-events| +26. Cleanup |luasnip-cleanup| +27. Logging |luasnip-logging| +28. Source |luasnip-source| +29. Config-Options |luasnip-config-options| +30. Troubleshooting |luasnip-troubleshooting| + - Adding Snippets |luasnip-troubleshooting-adding-snippets| +31. API |luasnip-api| +> + __ ____ + /\ \ /\ _`\ __ + \ \ \ __ __ __ \ \,\L\_\ ___ /\_\ _____ + \ \ \ __/\ \/\ \ /'__`\\/_\__ \ /' _ `\/\ \/\ '__`\ + \ \ \L\ \ \ \_\ \/\ \L\.\_/\ \L\ \/\ \/\ \ \ \ \ \L\ \ + \ \____/\ \____/\ \__/.\_\ `\____\ \_\ \_\ \_\ \ ,__/ + \/___/ \/___/ \/__/\/_/\/_____/\/_/\/_/\/_/\ \ \/ + \ \_\ + \/_/ +< + +LuaSnip is a snippet engine written entirely in Lua. It has some great features +like inserting text (`luasnip-function-node`) or nodes (`luasnip-dynamic-node`) +based on user input, parsing LSP syntax and switching nodes +(`luasnip-choice-node`). For basic setup like mappings and installing, check +the README. + +All code snippets in this help assume the following: + +>lua + local ls = require("luasnip") + local s = ls.snippet + local sn = ls.snippet_node + local isn = ls.indent_snippet_node + local t = ls.text_node + local i = ls.insert_node + local f = ls.function_node + local c = ls.choice_node + local d = ls.dynamic_node + local r = ls.restore_node + local events = require("luasnip.util.events") + local ai = require("luasnip.nodes.absolute_indexer") + local extras = require("luasnip.extras") + local l = extras.lambda + local rep = extras.rep + local p = extras.partial + local m = extras.match + local n = extras.nonempty + local dl = extras.dynamic_lambda + local fmt = require("luasnip.extras.fmt").fmt + local fmta = require("luasnip.extras.fmt").fmta + local conds = require("luasnip.extras.expand_conditions") + local postfix = require("luasnip.extras.postfix").postfix + local types = require("luasnip.util.types") + local parse = require("luasnip.util.parser").parse_snippet + local ms = ls.multi_snippet + local k = require("luasnip.nodes.key_indexer").new_key +< + +As noted in the |luasnip-loaders-lua|-section: + + + By default, the names from `luasnip.config.snip_env` + + will be used, but it’s possible to customize them by setting `snip_env` in + `setup`. +Furthermore, note that while this document assumes you have defined `ls` to be +`require("luasnip")`, it is **not** provided in the default set of variables. + + +============================================================================== +1. Basics *luasnip-basics* + +In LuaSnip, snippets are made up of `nodes`. These can contain either + +- static text (`textNode`) +- text that can be edited (`insertNode`) +- text that can be generated from the contents of other nodes (`functionNode`) +- other nodes + - `choiceNode`: allows choosing between two nodes (which might contain more + nodes) + - `restoreNode`: store and restore input to nodes +- or nodes that can be generated based on input (`dynamicNode`). + +Snippets are always created using the `s(trigger:string, +nodes:table)`-function. It is explained in more detail in |luasnip-snippets|, +but the gist is that it creates a snippet that contains the nodes specified in +`nodes`, which will be inserted into a buffer if the text before the cursor +matches `trigger` when `ls.expand` is called. + + +JUMP-INDEX *luasnip-basics-jump-index* + +Nodes that can be jumped to (`insertNode`, `choiceNode`, `dynamicNode`, +`restoreNode`, `snippetNode`) all require a "jump-index" so luasnip knows the +order in which these nodes are supposed to be visited ("jumped to"). + +>lua + s("trig", { + i(1), t"text", i(2), t"text again", i(3) + }) +< + +These indices don’t "run" through the entire snippet, like they do in +textmate-snippets (`"$1 ${2: $3 $4}"`), they restart at 1 in each nested +snippetNode: + +>lua + s("trig", { + i(1), t" ", sn(2, { + t" ", i(1), t" ", i(2) + }) + }) +< + +(roughly equivalent to the given textmate-snippet). + + +ADDING SNIPPETS *luasnip-basics-adding-snippets* + +The snippets for a given filetype have to be added to luasnip via +`ls.add_snippets(filetype, snippets)`. Snippets that should be accessible +globally (in all filetypes) have to be added to the special filetype `all`. + +>lua + ls.add_snippets("all", { + s("ternary", { + -- equivalent to "${1:cond} ? ${2:then} : ${3:else}" + i(1, "cond"), t(" ? "), i(2, "then"), t(" : "), i(3, "else") + }) + }) +< + +It is possible to make snippets from one filetype available to another using +`ls.filetype_extend`, more info on that in the section |luasnip-api|. + + +SNIPPET INSERTION *luasnip-basics-snippet-insertion* + +When a new snippet is expanded, it can be connected with the snippets that have +already been expanded in the buffer in various ways. First of all, Luasnip +distinguishes between root-snippets and child-snippets. The latter are nested +inside other snippets, so when jumping through a snippet, one may also traverse +the child-snippets expanded inside it, more or less as if the child just +contains more nodes of the parent. Root-snippets are of course characterised by +not being child-snippets. When expanding a new snippet, it becomes a child of +the snippet whose region it is expanded inside, and a root if it is not inside +any snippet’s region. If it is inside another snippet, the specific node it +is inside is determined, and the snippet then nested inside that node. + +- If that node is interactive (for example, an `insertNode`), the new snippet + will be traversed when the node is visited, as long as the + configuration-option `link_children` is enabled. If it is not enabled, it is + possible to jump from the snippet to the node, but not the other way around. +- If that node is not interactive, the snippet will be linked to the currently + active node, also such that it will not be jumped to again once it is left. + This is to prevent jumping large distances across the buffer as much as + possible. There may still be one large jump from the snippet back to the + current node it is nested inside, but that seems hard to avoid. + Thus, one should design snippets such that the regions where other snippets + may be expanded are inside `insertNodes`. + +If the snippet is not a child, but a root, it can be linked up with the roots +immediately adjacent to it by enabling `link_roots` in `setup`. Since by +default only one root is remembered, one should also set `keep_roots` if +`link_roots` is enabled. The two are separate options, since roots that are not +linked can still be reached by `ls.activate_node()`. This setup (remember +roots, but don’t jump to them) is useful for a super-tab like mapping +(`` and jump on the same key), where one would like to still enter +previous roots. Since there would almost always be more jumps if the roots are +linked, regular `` would not work almost all the time, and thus +`link_roots` has to stay disabled. + + +============================================================================== +2. Node *luasnip-node* + +Every node accepts, as its last parameter, an optional table of arguments. +There are some common ones (which are listed here), and some that only apply to +some nodes (`user_args` for function/dynamicNode). These `opts` are only +mentioned if they accept options that are not common to all nodes. + +Common opts: + +- `node_ext_opts` and `merge_node_ext_opts`: Control `ext_opts` (most likely + highlighting) of the node. Described in detail in |luasnip-ext_opts| +- `key`: The node can be reffered to by this key. Useful for either |luasnip-key-indexer| or for finding the node at runtime (See + |luasnip-snippets-api|), for example inside a `dynamicNode`. The keys + do not have to be unique across the entire lifetime of the snippet, but at any + point in time, the snippet may contain each key only once. This means it is + fine to return a keyed node from a `dynamicNode`, because even if it will be + generated multiple times, those will not be valid at the same time. +- `node_callbacks`: Define event-callbacks for this node (see + |luasnip-events|). + Accepts a table that maps an event, e.g. `events.enter` to the callback + (essentially the same as `callbacks` passed to `s`, only that there is no + first mapping from jump-index to the table of callbacks). + + +API *luasnip-node-api* + +- `get_jump_index()`: this method returns the jump-index of a node. If a node + doesn’t have a jump-index, this method returns `nil` instead. +- `get_buf_position(opts) -> {from_position, to_position}`: + Determines the range of the buffer occupied by this node. `from`- and + `to_position` are `row,column`-tuples, `0,0`-indexed (first line is 0, first + column is 0) and end-inclusive (see |api-indexing|, this is extmarks + indexing). + - `opts`: `table|nil`, options, valid keys are: + - `raw`: `bool`, default `true`. This can be used to switch between + byte-columns (`raw=true`) and visual columns (`raw=false`). This makes a + difference if the line contains characters represented by multiple bytes + in UTF, for example `ÿ`. + + +============================================================================== +3. Snippets *luasnip-snippets* + +The most direct way to define snippets is `s`: + +>lua + s({trig="trigger"}, {}) +< + +(This snippet is useless beyond serving as a minimal example) + +`s(context, nodes, opts) -> snippet` + +- `context`: Either table or a string. Passing a string is equivalent to passing + >lua + { + trig = context + } + < + The following keys are valid: + - `trig`: string, the trigger of the snippet. If the text in front of (to the + left of) the cursor when `ls.expand()` is called matches it, the snippet will + be expanded. By default, "matches" means the text in front of the cursor + matches the trigger exactly, this behaviour can be modified through + `trigEngine` + - `name`: string, can be used by e.g. `nvim-compe` to identify the snippet. + - `desc` (or `dscr`): string, description of the snippet, -separated or table for + multiple lines. + - `wordTrig`: boolean, if true, the snippet is only expanded if the word + (`[%w_]+`) before the cursor matches the trigger entirely. True by default. + - `regTrig`: boolean, whether the trigger should be interpreted as a lua pattern. + False by default. Consider setting `trigEngine` to `"pattern"` instead, it is + more expressive, and in line with other settings. + - `trigEngine`: (function|string), determines how `trig` is interpreted, and what + it means for it to "match" the text in front of the cursor. This behaviour can + be completely customized by passing a function, but the predefined ones, which + are accessible by passing their identifier, should suffice in most cases: + - `"plain"`: the default-behaviour, the trigger has to match the text before + the cursor exactly. + - `"pattern"`: the trigger is interpreted as a lua-pattern, and is a match if + `trig .. "$"` matches the line up to the cursor. Capture-groups will be + accessible as `snippet.captures`. + - `"ecma"`: the trigger is interpreted as an ECMAscript-regex, and is a + match if `trig .. "$"` matches the line up to the cursor. Capture-groups + will be accessible as `snippet.captures`. + This `trigEngine` requires `jsregexp` (see + |luasnip-lsp-snippets-transformations|) to be installed, if it + is not, this engine will behave like `"plain"`. + - `"vim"`: the trigger is interpreted as a vim-regex, and is a match if + `trig .. "$"` matches the line up to the cursor. As with the other + regex/pattern-engines, captures will be available as `snippet.captures`, + but there is one caveat: the matching is done using `matchlist`, so for + now empty-string submatches will be interpreted as unmatched, and the + corresponding `snippet.capture[i]` will be `nil` (this will most likely + change, don’t rely on this behavior). + Besides these predefined engines, it is also possible to create new ones: + Instead of a string, pass a function which satisfies `trigEngine(trigger, opts) + -> (matcher(line_to_cursor, trigger) -> whole_match, captures)` (ie. the + function receives `trig` and `trigEngineOpts` can, for example, precompile a + regex, and then returns a function responsible for determining whether the + current cursor-position (represented by the line up to the cursor) matches the + trigger (it is passed again here so engines which don’t do any + trigger-specific work (like compilation) can just return a static `matcher`), + and what the capture-groups are). The `lua`-engine, for example, can be + implemented like this: + >lua + local function matcher(line_to_cursor, trigger) + -- look for match which ends at the cursor. + -- put all results into a list, there might be many capture-groups. + local find_res = { line_to_cursor:find(trigger .. "$") } + + if #find_res > 0 then + -- if there is a match, determine matching string, and the + -- capture-groups. + local captures = {} + -- find_res[1] is `from`, find_res[2] is `to` (which we already know + -- anyway). + local from = find_res[1] + local match = line_to_cursor:sub(from, #line_to_cursor) + -- collect capture-groups. + for i = 3, #find_res do + captures[i - 2] = find_res[i] + end + return match, captures + else + return nil + end + end + + local function engine(trigger) + -- don't do any special work here, can't precompile lua-pattern. + return matcher + end + < + The predefined engines are defined in `trig_engines.lua` + , + read it for more examples. + - `trigEngineOpts`: `table`, options for the used trigEngine. The + valid options are: + - `max_len`: number, upper bound on the length of the trigger. + If this is set, the `line_to_cursor` will be truncated (from the cursor of + course) to `max_len` characters before performing the match. + This is implemented because feeding long `line_to_cursor` into eg. the + pattern-trigEngine will hurt performance quite a bit (see issue + Luasnip#1103). + This option is implemented for all `trigEngines`. + - `docstring`: string, textual representation of the snippet, specified like + `desc`. Overrides docstrings loaded from json. + - `docTrig`: string, used as `line_to_cursor` during docstring-generation. This + might be relevant if the snippet relies on specific values in the + capture-groups (for example, numbers, which won’t work with the default + `$CAPTURESN` used during docstring-generation) + - `hidden`: boolean, hint for completion-engines. If set, the snippet should not + show up when querying snippets. + - `priority`: positive number, Priority of the snippet, 1000 by default. Snippets + with high priority will be matched to a trigger before those with a lower one. + The priority for multiple snippets can also be set in `add_snippets`. + - `snippetType`: string, should be either `snippet` or `autosnippet` (ATTENTION: + singular form is used), decides whether this snippet has to be triggered by + `ls.expand()` or whether is triggered automatically (don’t forget to set + `ls.config.setup({ enable_autosnippets = true })` if you want to use this + feature). If unset it depends on how the snippet is added of which type the + snippet will be. + - `resolveExpandParams`: `fn(snippet, line_to_cursor, matched_trigger, captures) + -> table|nil`, where + - `snippet`: `Snippet`, the expanding snippet object + - `line_to_cursor`: `string`, the line up to the cursor. + - `matched_trigger`: `string`, the fully matched trigger (can be retrieved + from `line_to_cursor`, but we already have that info here :D) + - `captures`: `captures` as returned by `trigEngine`. + This function will be evaluated in `Snippet:matches()` to decide whether the + snippet can be expanded or not. Returns a table if the snippet can be expanded, + `nil` if can not. The returned table can contain any of these fields: + - `trigger`: `string`, the fully matched trigger. + - `captures`: `table`, this list could update the capture-groups from + parameter in snippet expansion. + Both `trigger` and `captures` can override the values returned via + `trigEngine`. + - `clear_region`: `{ "from": {, }, "to": {, } }`, + both (0, 0)-indexed, the region where text has to be cleared before + inserting the snippet. + - `env_override`: `map string->(string[]|string)`, override or extend + the snippet’s environment (`snip.env`) + If any of these is `nil`, the default is used (`trigger` and `captures` as + returned by `trigEngine`, `clear_region` such that exactly the trigger is + deleted, no overridden environment-variables). + A good example for the usage of `resolveExpandParams` can be found in the + implementation of `postfix` + . + - `condition`: `fn(line_to_cursor, matched_trigger, captures) -> bool`, where + - `line_to_cursor`: `string`, the line up to the cursor. + - `matched_trigger`: `string`, the fully matched trigger (can be retrieved + from `line_to_cursor`, but we already have that info here :D) + - `captures`: if the trigger is pattern, this list contains the + capture-groups. Again, could be computed from `line_to_cursor`, but we + already did so. + - `show_condition`: `f(line_to_cursor) -> bool`. + - `line_to_cursor`: `string`, the line up to the cursor. + This function is (should be) evaluated by completion engines, indicating + whether the snippet should be included in current completion candidates. + Defaults to a function returning `true`. This is different from `condition` + because `condition` is evaluated by LuaSnip on snippet expansion (and thus has + access to the matched trigger and captures), while `show_condition` is (should + be) evaluated by the completion engines when scanning for available snippet + candidates. + - `filetype`: `string`, the filetype of the snippet. This overrides the filetype + the snippet is added (via `add_snippet`) as. +- `nodes`: A single node or a list of nodes. The nodes that make up the snippet. +- `opts`: A table with the following valid keys: + - `callbacks`: Contains functions that are called upon entering/leaving a node of + this snippet. For example: to print text upon entering the _second_ node of a + snippet, `callbacks` should be set as follows: + >lua + { + -- position of the node, not the jump-index!! + -- s("trig", {t"first node", t"second node", i(1, "third node")}). + [2] = { + [events.enter] = function(node, _event_args) print("2!") end + } + } + < + To register a callback for the snippets’ own events, the key `[-1]` may be + used. More info on events in |luasnip-events| + - `child_ext_opts`, `merge_child_ext_opts`: Control `ext_opts` applied to the + children of this snippet. More info on those in the |luasnip-ext_opts|-section. + +The `opts`-table, as described here, can also be passed to e.g. `snippetNode` +and `indentSnippetNode`. It is also possible to set `condition` and +`show_condition` (described in the documentation of the `context`-table) from +`opts`. They should, however, not be set from both. + + +DATA *luasnip-snippets-data* + +Snippets contain some interesting tables during runtime: + +- `snippet.env`: Contains variables used in the LSP-protocol, for example + `TM_CURRENT_LINE` or `TM_FILENAME`. It’s possible to add customized variables + here too, check |luasnip-variables-environment-namespaces| +- `snippet.captures`: If the snippet was triggered by a pattern (`regTrig`), and + the pattern contained capture-groups, they can be retrieved here. +- `snippet.trigger`: The string that triggered this snippet. Again, only + interesting if the snippet was triggered through `regTrig`, for getting the + full match. + +These variables/tables primarily come in handy in `dynamic/functionNodes`, +where the snippet can be accessed through the immediate parent +(`parent.snippet`), which is passed to the function. (in most cases `parent == +parent.snippet`, but the `parent` of the dynamicNode is not always the +surrounding snippet, it could be a `snippetNode`). + +## Api + +- `invalidate()`: call this method to effectively remove the snippet. The + snippet will no longer be able to expand via `expand` or `expand_auto`. It + will also be hidden from lists (at least if the plugin creating the list + respects the `hidden`-key), but it might be necessary to call + `ls.refresh_notify(ft)` after invalidating snippets. +- `get_keyed_node(key)`: Returns the currently visible node associated with + `key`. + + +============================================================================== +4. TextNode *luasnip-textnode* + +The most simple kind of node; just text. + +>lua + s("trigger", { t("Wow! Text!") }) +< + +This snippet expands to + +> + Wow! Text!⎵ +< + +where ⎵ is the cursor. + +Multiline strings can be defined by passing a table of lines rather than a +string: + +>lua + s("trigger", { + t({"Wow! Text!", "And another line."}) + }) +< + +`t(text, node_opts)`: + +- `text`: `string` or `string[]` +- `node_opts`: `table`, see |luasnip-node| + + +============================================================================== +5. InsertNode *luasnip-insertnode* + +These Nodes contain editable text and can be jumped to- and from (e.g. +traditional placeholders and tabstops, like `$1` in textmate-snippets). + +The functionality is best demonstrated with an example: + +>lua + s("trigger", { + t({"After expanding, the cursor is here ->"}), i(1), + t({"", "After jumping forward once, cursor is here ->"}), i(2), + t({"", "After jumping once more, the snippet is exited there ->"}), i(0), + }) +< + +The Insert Nodes are visited in order `1,2,3,..,n,0`. (The jump-index 0 also +_has_ to belong to an `insertNode`!) So the order of InsertNode-jumps is as +follows: + +1. After expansion, the cursor is at InsertNode 1, +2. after jumping forward once at InsertNode 2, +3. and after jumping forward again at InsertNode 0. + +If no 0-th InsertNode is found in a snippet, one is automatically inserted +after all other nodes. + +The jump-order doesn’t have to follow the "textual" order of the nodes: + +>lua + s("trigger", { + t({"After jumping forward once, cursor is here ->"}), i(2), + t({"", "After expanding, the cursor is here ->"}), i(1), + t({"", "After jumping once more, the snippet is exited there ->"}), i(0), + }) +< + +The above snippet will behave as follows: + +1. After expansion, we will be at InsertNode 1. +2. After jumping forward, we will be at InsertNode 2. +3. After jumping forward again, we will be at InsertNode 0. + +An **important** (because here Luasnip differs from other snippet engines) +detail is that the jump-indices restart at 1 in nested snippets: + +>lua + s("trigger", { + i(1, "First jump"), + t(" :: "), + sn(2, { + i(1, "Second jump"), + t" : ", + i(2, "Third jump") + }) + }) +< + +as opposed to e.g. the textmate syntax, where tabstops are snippet-global: + +>snippet + ${1:First jump} :: ${2: ${3:Third jump} : ${4:Fourth jump}} +< + +(this is not exactly the same snippet of course, but as close as possible) (the +restart-rule only applies when defining snippets in lua, the above +textmate-snippet will expand correctly when parsed). + +`i(jump_index, text, node_opts)` + +- `jump_index`: `number`, this determines when this node will be jumped to (see + |luasnip-basics-jump-index|). +- `text`: `string|string[]`, a single string for just one line, a list with >1 + entries for multiple lines. + This text will be SELECTed when the `insertNode` is jumped into. +- `node_opts`: `table`, described in |luasnip-node| + +If the `jump_index` is `0`, replacing its’ `text` will leave it outside the +`insertNode` (for reasons, check out Luasnip#110). + + +============================================================================== +6. FunctionNode *luasnip-functionnode* + +Function Nodes insert text based on the content of other nodes using a +user-defined function: + +>lua + local function fn( + args, -- text from i(2) in this example i.e. { { "456" } } + parent, -- parent snippet or parent node + user_args -- user_args from opts.user_args + ) + return '[' .. args[1][1] .. user_args .. ']' + end + + s("trig", { + i(1), t '<-i(1) ', + f(fn, -- callback (args, parent, user_args) -> string + {2}, -- node indice(s) whose text is passed to fn, i.e. i(2) + { user_args = { "user_args_value" }} -- opts + ), + t ' i(2)->', i(2), t '<-i(2) i(0)->', i(0) + }) +< + +`f(fn, argnode_references, node_opts)`: - `fn`: `function(argnode_text, parent, +user_args1,...,user_argsn) -> text` - `argnode_text`: `string[][]`, the text +currently contained in the argnodes (e.g. `{{line1}, {line1, line2}}`). The +snippet indent will be removed from all lines following the first. + +- `parent`: The immediate parent of the `functionNode`. It is included here as it + allows easy access to some information that could be useful in functionNodes + (see |luasnip-snippets-data| for some examples). Many snippets access the + surrounding snippet just as `parent`, but if the `functionNode` is nested + within a `snippetNode`, the immediate parent is a `snippetNode`, not the + surrounding snippet (only the surrounding snippet contains data like `env` or + `captures`). +- `user_args`: The `user_args` passed in `opts`. Note that there may be multiple + user_args (e.g. `user_args1, ..., user_argsn`). + +`fn` shall return a string, which will be inserted as is, or a table of strings +for multiline strings, where all lines following the first will be prefixed +with the snippets’ indentation. + +- `argnode_references`: `node_reference[]|node_refernce|nil`. Either no, a + single, or multiple |luasnip-node-reference|s. Changing any of these will + trigger a re-evaluation of `fn`, and insertion of the updated text. If no node + reference is passed, the `functionNode` is evaluated once upon expansion. +- `node_opts`: `table`, see |luasnip-node|. One additional key is supported: + - `user_args`: `any[]`, these will be passed to `fn` as `user_arg1`-`user_argn`. + These make it easier to reuse similar functions, for example a functionNode + that wraps some text in different delimiters (`()`, `[]`, …). + >lua + local function reused_func(_,_, user_arg1) + return user_arg1 + end + + s("trig", { + f(reused_func, {}, { + user_args = {"text"} + }), + f(reused_func, {}, { + user_args = {"different text"} + }), + }) + < + +**Examples**: + +- Use captures from the regex trigger using a functionNode: + >lua + s({trig = "b(%d)", regTrig = true}, + f(function(args, snip) return + "Captured Text: " .. snip.captures[1] .. "." end, {}) + ) + < +- `argnodes_text` during function evaluation: + >lua + s("trig", { + i(1, "text_of_first"), + i(2, {"first_line_of_second", "second_line_of_second"}), + f(function(args, snip) + --here + -- order is 2,1, not 1,2!! + end, {2, 1} )}) + < + At `--here`, `args` would look as follows (provided no text was changed after + expansion): + >lua + args = { + {"first_line_of_second", "second_line_of_second"}, + {"text_of_first"} + } + < +- |luasnip-absolute-indexer|: + >lua + s("trig", { + i(1, "text_of_first"), + i(2, {"first_line_of_second", "second_line_of_second"}), + f(function(args, snip) + -- just concat first lines of both. + return args[1][1] .. args[2][1] + end, {ai[2], ai[1]} )}) + < + +If the function only performs simple operations on text, consider using the +`lambda` from `luasnip.extras` (See |luasnip-extras-lambda|) + + +============================================================================== +7. Node Reference *luasnip-node-reference* + +Node references are used to refer to other nodes in various parts of +luasnip’s API. For example, argnodes in functionNode, dynamicNode or lambda +are node references. These references can be either of: + +- `number`: the jump-index of the node. + This will be resolved relative to the parent of the node this is passed to. + (So, only nodes with the same parent can be referenced. This is very easy to + grasp, but also limiting) +- `key_indexer`: the key of the node, if it is present. This will come in + handy if the node that is being referred to is not in the same + snippet/snippetNode as the one the node reference is passed to. + Also, it is the proper way to refer to a non-interactive node (a + functionNode, for example) +- `absolute_indexer`: the absolute position of the node. Just like + `key_indexer`, it allows addressing non-sibling nodes, but is a bit more + awkward to handle since a path from root to node has to be determined, + whereas `key_indexer` just needs the key to match. + Due to this, `key_indexer` should be generally preferred. + (More information in |luasnip-absolute-indexer|). +- `node`: just the node. Usage of this is discouraged since it can lead to + subtle errors (for example, if the node passed here is captured in a closure + and therefore not copied with the remaining tables in the snippet; there’s a + big comment about just this in commit 8bfbd61). + + +============================================================================== +8. ChoiceNode *luasnip-choicenode* + +ChoiceNodes allow choosing between multiple nodes. + +>lua + s("trig", c(1, { + t("Ugh boring, a text node"), + i(nil, "At least I can edit something now..."), + f(function(args) return "Still only counts as text!!" end, {}) + })) +< + +`c(jump_index, choices, node_opts)` + +- `jump_index`: `number`, since choiceNodes can be jumped to, they need a + jump-index (Info in |luasnip-basics-jump-index|). +- `choices`: `node[]|node`, the choices. The first will be initialliy active. + A list of nodes will be turned into a `snippetNode`. +- `node_opts`: `table`. `choiceNode` supports the keys common to all nodes + described in |luasnip-node|, and one additional key: + - `restore_cursor`: `false` by default. If it is set, and the node that was being + edited also appears in the switched to choice (can be the case if a + `restoreNode` is present in both choice) the cursor is restored relative to + that node. The default is `false` as enabling might lead to decreased + performance. It’s possible to override the default by wrapping the + `choiceNode` constructor in another function that sets `opts.restore_cursor` to + `true` and then using that to construct `choiceNode`s: + >lua + local function restore_cursor_choice(pos, choices, opts) + if opts then + opts.restore_cursor = true + else + opts = {restore_cursor = true} + end + return c(pos, choices, opts) + end + < + +Jumpable nodes that normally expect an index as their first parameter don’t +need one inside a choiceNode; their jump-index is the same as the +choiceNodes’. + +As it is only possible (for now) to change choices from within the choiceNode, +make sure that all of the choices have some place for the cursor to stop at! + +This means that in `sn(nil, {...nodes...})` `nodes` has to contain e.g. an +`i(1)`, otherwise luasnip will just "jump through" the nodes, making it +impossible to change the choice. + +>lua + c(1, { + t"some text", -- textNodes are just stopped at. + i(nil, "some text"), -- likewise. + sn(nil, {t"some text"}) -- this will not work! + sn(nil, {i(1), t"some text"}) -- this will. + }) +< + +The active choice for a choiceNode can be changed by either calling one of +`ls.change_choice(1)` (forwards) or `ls.change_choice(-1)` (backwards), or by +calling `ls.set_choice(choice_indx)`. + +One way to easily interact with choiceNodes is binding `change_choice(1/-1)` to +keys: + +>lua + -- set keybinds for both INSERT and VISUAL. + vim.api.nvim_set_keymap("i", "", "luasnip-next-choice", {}) + vim.api.nvim_set_keymap("s", "", "luasnip-next-choice", {}) + vim.api.nvim_set_keymap("i", "", "luasnip-prev-choice", {}) + vim.api.nvim_set_keymap("s", "", "luasnip-prev-choice", {}) +< + +Apart from this, there is also a picker (see |luasnip-select_choice| where no +cycling is necessary and any choice can be selected right away, via +`vim.ui.select`. + + +============================================================================== +9. SnippetNode *luasnip-snippetnode* + +SnippetNodes directly insert their contents into the surrounding snippet. This +is useful for `choiceNode`s, which only accept one child, or `dynamicNode`s, +where nodes are created at runtime and inserted as a `snippetNode`. + +Their syntax is similar to `s`, however, where snippets require a table +specifying when to expand, `snippetNode`s, similar to `insertNode`s, expect a +jump-index. + +>lua + s("trig", sn(1, { + t("basically just text "), + i(1, "And an insertNode.") + })) +< + +`sn(jump_index, nodes, node_opts)` + +- `jump_index`: `number`, the usual |luasnip-jump-index|. +- `nodes`: `node[]|node`, just like for `s`. + Note that `snippetNode`s don’t accept an `i(0)`, so the jump-indices of the nodes + inside them have to be in `1,2,...,n`. +- `node_opts`: `table`: again, the keys common to all nodes (documented in + |luasnip-node|) are supported, but also + - `callbacks`, + - `child_ext_opts` and + - `merge_child_ext_opts`, + which are further explained in |luasnip-snippets|. + + +============================================================================== +10. IndentSnippetNode *luasnip-indentsnippetnode* + +By default, all nodes are indented at least as deep as the trigger. With these +nodes it’s possible to override that behaviour: + +>lua + s("isn", { + isn(1, { + t({"This is indented as deep as the trigger", + "and this is at the beginning of the next line"}) + }, "") + }) +< + +(Note the empty string passed to isn). + +Indent is only applied after linebreaks, so it’s not possible to remove +indent on the line where the snippet was triggered using `ISN` (That is +possible via regex triggers where the entire line before the trigger is +matched). + +Another nice use case for `ISN` is inserting text, e.g. `//` or some other +comment string before the nodes of the snippet: + +>lua + s("isn2", { + isn(1, t({"//This is", "A multiline", "comment"}), "$PARENT_INDENT//") + }) +< + +Here the `//` before `This is` is important, once again, because indent is only +applied after linebreaks. + +To enable such usage, `$PARENT_INDENT` in the indentstring is replaced by the +parent’s indent. + +`isn(jump_index, nodes, indentstring, node_opts)` + +All of these parameters except `indentstring` are exactly the same as in +|luasnip-snippetnode|. + +- `indentstring`: `string`, will be used to indent the nodes inside this + `snippetNode`. + All occurences of `"$PARENT_INDENT"` are replaced with the actual indent of + the parent. + + +============================================================================== +11. DynamicNode *luasnip-dynamicnode* + +Very similar to functionNode, but returns a snippetNode instead of just text, +which makes them very powerful as parts of the snippet can be changed based on +user input. + +`d(jump_index, function, node-references, opts)`: + +- `jump_index`: `number`, just like all jumpable nodes, its’ position in the + jump-list (|luasnip-basics-jump-index|). +- `function`: `fn(args, parent, old_state, user_args) -> snippetNode` This + function is called when the argnodes’ text changes. It should generate and + return (wrapped inside a `snippetNode`) nodes, which will be inserted at the + dynamicNode’s place. `args`, `parent` and `user_args` are also explained in + |luasnip-functionnode| + - `args`: `table of text` (`{{"node1line1", "node1line2"}, {"node2line1"}}`) + from nodes the `dynamicNode` depends on. + - `parent`: the immediate parent of the `dynamicNode`. + - `old_state`: a user-defined table. This table may contain anything; its + intended usage is to preserve information from the previously generated + `snippetNode`. If the `dynamicNode` depends on other nodes, it may be + reconstructed, which means all user input (text inserted in `insertNodes`, + changed choices) to the previous `dynamicNode` is lost. + The `old_state` table must be stored in `snippetNode` returned by + the function (`snippetNode.old_state`). + The second example below illustrates the usage of `old_state`. + - `user_args`: passed through from `dynamicNode`-opts; may have more than one + argument. +- `node_references`: `node_reference[]|node_references|nil`, + |luasnip-node-references| to the nodes the dynamicNode depends on: if any of + these trigger an update (for example, if the text inside them changes), the + `dynamicNode`s’ function will be executed, and the result inserted at the + `dynamicNode`s place. (`dynamicNode` behaves exactly the same as `functionNode` + in this regard). +- `opts`: In addition to the common |luasnip-node|-keys, there is, again, + - `user_args`, which is described in |luasnip-functionnode|. + +**Examples**: + +This `dynamicNode` inserts an `insertNode` which copies the text inside the +first `insertNode`. + +>lua + s("trig", { + t"text: ", i(1), t{"", "copy: "}, + d(2, function(args) + -- the returned snippetNode doesn't need a position; it's inserted + -- "inside" the dynamicNode. + return sn(nil, { + -- jump-indices are local to each snippetNode, so restart at 1. + i(1, args[1]) + }) + end, + {1}) + }) +< + +This snippet makes use of `old_state` to count the number of updates. + +To store/restore values generated by the `dynamicNode` or entered into +`insert/choiceNode`, consider using the shortly-introduced `restoreNode` +instead of `old_state`. + +>lua + local function count(_, _, old_state) + old_state = old_state or { + updates = 0 + } + + old_state.updates = old_state.updates + 1 + + local snip = sn(nil, { + t(tostring(old_state.updates)) + }) + + snip.old_state = old_state + return snip + end + + ls.add_snippets("all", + s("trig", { + i(1, "change to update"), + d(2, count, {1}) + }) + ) +< + +As with `functionNode`, `user_args` can be used to reuse similar `dynamicNode`- +functions. + + +============================================================================== +12. RestoreNode *luasnip-restorenode* + +This node can store and restore a snippetNode as is. This includes changed +choices and changed text. Its’ usage is best demonstrated by an example: + +>lua + s("paren_change", { + c(1, { + sn(nil, { t("("), r(1, "user_text"), t(")") }), + sn(nil, { t("["), r(1, "user_text"), t("]") }), + sn(nil, { t("{"), r(1, "user_text"), t("}") }), + }), + }, { + stored = { + -- key passed to restoreNodes. + ["user_text"] = i(1, "default_text") + } + }) +< + +Here the text entered into `user_text` is preserved upon changing choice. + +`r(jump_index, key, nodes, node_opts)`: + +- `jump_index`, when to jump to this node. +- `key`, `string`: `restoreNode`s with the same key share their content. +- `nodes`, `node[]|node`: the content of the `restoreNode`. + Can either be a single node, or a table of nodes (both of which will be + wrapped inside a `snippetNode`, except if the single node already is a + `snippetNode`). + The content for a given key may be defined multiple times, but if the + contents differ, it’s undefined which will actually be used. + If a key’s content is defined in a `dynamicNode`, it will not be initially + used for `restoreNodes` outside that `dynamicNode`. A way around this + limitation is defining the content in the `restoreNode` outside the + `dynamicNode`. + +The content for a key may also be defined in the `opts`-parameter of the +snippet-constructor, as seen in the example above. The `stored`-table accepts +the same values as the `nodes`-parameter passed to `r`. If no content is +defined for a key, it defaults to the empty `insertNode`. + +An important-to-know limitation of `restoreNode` is that, for a given key, only +one may be visible at a time. See this issue + for details. + +The `restoreNode` is especially useful for storing input across updates of a +`dynamicNode`. Consider this: + +>lua + local function simple_restore(args, _) + return sn(nil, {i(1, args[1]), i(2, "user_text")}) + end + + s("rest", { + i(1, "preset"), t{"",""}, + d(2, simple_restore, 1) + }) +< + +Every time the `i(1)` in the outer snippet is changed, the text inside the +`dynamicNode` is reset to `"user_text"`. This can be prevented by using a +`restoreNode`: + +>lua + local function simple_restore(args, _) + return sn(nil, {i(1, args[1]), r(2, "dyn", i(nil, "user_text"))}) + end + + s("rest", { + i(1, "preset"), t{"",""}, + d(2, simple_restore, 1) + }) +< + +Now the entered text is stored. + +`restoreNode`s indent is not influenced by `indentSnippetNodes` right now. If +that really bothers you feel free to open an issue. + + +============================================================================== +13. Key Indexer *luasnip-key-indexer* + +A very flexible way of referencing nodes (|luasnip-node-reference|). While the +straightforward way of addressing nodes via their |luasnip-jump-index| suffices +in most cases, a `dynamic/functionNode` can only depend on nodes in the same +snippet(Node), its siblings (since the index is interpreted as relative to +their parent). Accessing a node with a different parent is thus not possible. +Secondly, and less relevant, only nodes that actually have a jump-index can be +referred to (a `functionNode`, for example, cannot be depended on). Both of +these restrictions are lifted with `key_indexer`: It allows addressing nodes by +their key, which can be set when the node is constructed, and is wholly +independent of the nodes’ position in the snippet, thus enabling descriptive +labeling. + +The following snippets demonstrate the issue and the solution by using +`key_indexer`: + +First, the addressed problem of referring to nodes outside the `functionNode`s +parent: + +>lua + s("trig", { + i(1), c(2, { + sn(nil, { + t"cannot access the argnode :(", + f(function(args) + return args[1] + end, {???}) -- can't refer to i(1), since it isn't a sibling of `f`. + }), + t"sample_text" + }) + }) +< + +And the solution: first give the node we want to refer to a key, and then pass +the same to the `functionNode`. + +>lua + s("trig", { + i(1, "", {key = "i1-key"}), c(2, { + sn(nil, { i(1), + t"can access the argnode :)", + f(function(args) + return args[1] + end, k("i1-key") ) + }), + t"sample_text" + }) + }) +< + + +============================================================================== +14. Absolute Indexer *luasnip-absolute-indexer* + +`absolute_indexer` allows accessing nodes by their unique jump-index path from +the snippet-root. This makes it almost as powerful as |luasnip-key-indexer|, +but again removes the possibility of referring to non-jumpable nodes and makes +it all a bit more error-prone since the jump-index paths are hard to follow, +and (unfortunately) have to be a bit verbose (see the long example of +`absolute_indexer`-positions below). Consider just using |luasnip-key-indexer| +instead. + +(The solution-snippet from |luasnip-key-indexer|, but using `ai` instead.) + +>lua + s("trig", { + i(1), c(2, { + sn(nil, { i(1), + t"can access the argnode :)", + f(function(args) + return args[1] + end, ai(1) ) + }), + t"sample_text" + }) + }) +< + +There are some quirks in addressing nodes: + +>lua + s("trig", { + i(2), -- ai[2]: indices based on jump-index, not position. + sn(1, { -- ai[1] + i(1), -- ai[1][1] + t"lel", -- not addressable. + i(2) -- ai[1][2] + }), + c(3, { -- ai[3] + i(nil), -- ai[3][1] + t"lel", -- ai[3][2]: choices are always addressable. + }), + d(4, function() -- ai[4] + return sn(nil, { -- ai[4][0] + i(1), -- ai[4][0][1] + }) + end, {}), + r(5, "restore_key", -- ai[5] + i(1) -- ai[5][0][1]: restoreNodes always store snippetNodes. + ), + r(6, "restore_key_2", -- ai[6] + sn(nil, { -- ai[6][0] + i(1) -- ai[6][0][1] + }) + ) + }) +< + +Note specifically that the index of a dynamicNode differs from that of the +generated snippetNode, and that restoreNodes (internally) always store a +snippetNode, so even if the restoreNode only contains one node, that node has +to be accessed as `ai[restoreNodeIndx][0][1]`. + +`absolute_indexer`s’ can be constructed in different ways: + +- `ai[1][2][3]` +- `ai(1, 2, 3)` +- `ai{1, 2, 3}` + +are all the same node. + + +============================================================================== +15. MultiSnippet *luasnip-multisnippet* + +There are situations where it might be comfortable to access a snippet in +different ways. For example, one might want to enable auto-triggering in +regions where the snippets usage is common, while leaving it manual-only in +others. This is where `ms` should be used: A single snippet can be associated +with multiple `context`s (the `context`-table determines the conditions under +which a snippet may be triggered). This has the advantage (compared with just +registering copies) that all `context`s are backed by a single snippet, and not +multiple, and it’s (at least should be :D) more comfortable to use. + +`ms(contexts, nodes, opts) -> addable`: + +- `contexts`: table containing list of `contexts`, and some keywords. + `context` are described in |luasnip-snippets|, here they may also be tables + or strings. + So far, there is only one valid keyword: + - `common`: Accepts yet another context. + The options in `common` are applied to (but don’t override) the other + contexts specified in `contexts`. +- `nodes`: List of nodes, exactly like in |luasnip-snippets|. +- `opts`: Table, options for this function: + - `common_opts`: The snippet-options (see also |luasnip-snippets|) applied + to the snippet generated from `nodes`. + +The returned object is an `addable`, something which can be passed to +`add_snippets`, or returned from the lua-loader. + +**Examples**: + +>lua + ls.add_snippets("all", { + ms({"a", "b"}, {t"a or b"}) + }) +< + +>lua + ls.add_snippets("all", { + ms({ + common = {snippetType = "autosnippet"}, + "a", + "b" + }, { + t"a or b (but autotriggered!!)" + }) + }) +< + +>lua + ls.add_snippets("all", { + ms({ + common = {snippetType = "autosnippet"}, + {trig = "a", snippetType = "snippet"}, + "b", + {trig = "c", condition = function(line_to_cursor) + return line_to_cursor == "" + end} + }, { + t"a or b (but autotriggered!!)" + }) + }) +< + + +============================================================================== +16. Extras *luasnip-extras* + + +LAMBDA *luasnip-extras-lambda* + +A shortcut for `functionNode`s that only do very basic string manipulation. + +`l(lambda, argnodes)`: + +- `lambda`: An object created by applying string-operations to `l._n`, objects + representing the `n`th argnode. + For example: + - `l._1:gsub("a", "e")` replaces all occurences of "a" in the text of the + first argnode with "e", or + - `l._1 .. l._2` concatenates text of the first and second argnode. + If an argnode contains multiple lines of text, they are concatenated with + `"\n"` prior to any operation. +- `argnodes`, a |luasnip-node-reference|, just like in function- and + dynamicNode. + +There are many examples for `lambda` in `Examples/snippets.lua` + + +MATCH *luasnip-extras-match* + +`match` can insert text based on a predicate (again, a shorthand for +`functionNode`). + +`match(argnodes, condition, then, else)`: + +- `argnode`: A single |luasnip-node-reference|. May not be nil, or + a table. +- `condition` may be either of + - `string`: interpreted as a lua pattern. Matched on the `\n`-joined (in case + it’s multiline) text of the first argnode (`args[1]:match(condition)`). + - `function`: `fn(args, snip) -> bool`: takes the same parameters as the + `functionNode`-function, any value other than nil or false is interpreted + as a match. + - `lambda`: `l._n` is the `\n`-joined text of the nth argnode. + Useful if string manipulations have to be performed before the string is matched. + Should end with `match`, but any other truthy result will be interpreted + as matching. +- `then` is inserted if the condition matches, +- `else` if it does not. + +Both `then` and `else` can be either text, lambda or function (with the same +parameters as specified above). `then`’s default-value depends on the +`condition`: + +- `pattern`: Simply the return value from the `match`, e.g. the entire match, + or, if there were capture groups, the first capture group. +- `function`: the return value of the function if it is either a string, or a + table (if there is no `then`, the function cannot return a table containing + something other than strings). +- `lambda`: Simply the first value returned by the lambda. + +Examples: + +- `match(n, "^ABC$", "A")`. +- `match(n, lambda._1:match(lambda._1:reverse()), "PALINDROME")` + >lua + s("trig", { + i(1), t":", + i(2), t"::", + m({1, 2}, l._1:match("^"..l._2.."$"), l._1:gsub("a", "e")) + }) + < +- >lua + s("extras1", { + i(1), t { "", "" }, m(1, "^ABC$", "A") + }) + < + Inserts "A" if the node with jump-index `n` matches "ABC" exactly, nothing + otherwise. +- >lua + s("extras2", { + i(1, "INPUT"), t { "", "" }, m(1, l._1:match(l._1:reverse()), "PALINDROME") + }) + < + Inserts `"PALINDROME"` if i(1) contains a palindrome. +- >lua + s("extras3", { + i(1), t { "", "" }, i(2), t { "", "" }, + m({ 1, 2 }, l._1:match("^" .. l._2 .. "$"), l._1:gsub("a", "e")) + }) + < + This inserts the text of the node with jump-index 1, with all occurences of `a` + replaced with `e`, if the second insertNode matches the first exactly. + + +REPEAT *luasnip-extras-repeat* + +Inserts the text of the passed node. + +`rep(node_reference)` - `node_reference`, a single |luasnip-node-reference|. + +>lua + s("extras4", { i(1), t { "", "" }, extras.rep(1) }) +< + + +PARTIAL *luasnip-extras-partial* + +Evaluates a function on expand and inserts its value. + +`partial(fn, params...)` - `fn`: any function - `params`: varargs, any, will be +passed to `fn`. + +For example `partial(os.date, "%Y")` inserts the current year on expansion. + +>lua + s("extras5", { extras.partial(os.date, "%Y") }) +< + + +NONEMPTY *luasnip-extras-nonempty* + +Inserts text if the referenced node doesn’t contain any text. + +`nonempty(node_reference, not_empty, empty)`: + +- `node_reference`, a single |luasnip-node-reference|. +- `not_empty`, `string`: inserted if the node is not empty. +- `empty`, `string`: inserted if the node is empty. + +>lua + s("extras6", { i(1, ""), t { "", "" }, extras.nonempty(1, "not empty!", "empty!") }) +< + + +DYNAMIC LAMBDA *luasnip-extras-dynamic-lambda* + +Pretty much the same as lambda, but it inserts the resulting text as an +insertNode, and, as such, it can be quickly overridden. + +`dynamic_lambda(jump_indx, lambda, node_references)` - `jump_indx`, as usual, +the jump-indx. + +The remaining arguments carry over from lambda. + +>lua + s("extras7", { i(1), t { "", "" }, extras.dynamic_lambda(2, l._1 .. l._1, 1) }) +< + + +FMT *luasnip-extras-fmt* + +Authoring snippets can be quite clunky, especially since every second node is +probably a `textNode`, inserting a small number of characters between two more +complicated nodes. + +`fmt` can be used to define snippets in a much more readable way. This is +achieved by borrowing (as the name implies) from `format`-functionality (our +syntax is very similar to python’s +). + +`fmt` accepts a string and a table of nodes. Each occurrence of a delimiter +pair in the string is replaced by one node from the table, while text outside +the delimiters is turned into textNodes. + +Simple example: + +>lua + ls.add_snippets("all", { + -- important! fmt does not return a snippet, it returns a table of nodes. + s("example1", fmt("just an {iNode1}", { + iNode1 = i(1, "example") + })), + s("example2", fmt([[ + if {} then + {} + end + ]], { + -- i(1) is at nodes[1], i(2) at nodes[2]. + i(1, "not now"), i(2, "when") + })), + s("example3", fmt([[ + if <> then + <> + end + ]], { + -- i(1) is at nodes[1], i(2) at nodes[2]. + i(1, "not now"), i(2, "when") + }, { + delimiters = "<>" + })), + s("example4", fmt([[ + repeat {a} with the same key {a} + ]], { + a = i(1, "this will be repeat") + }, { + repeat_duplicates = true + })) + }) +< + +One important detail here is that the position of the delimiters does not, in +any way, correspond to the jump-index of the nodes! + +`fmt(format:string, nodes:table of nodes, opts:table|nil) -> table of nodes` + +- `format`: a string. Occurences of `{}` ( `{}` are customizable; more + on that later) are replaced with `content[]` (which should be a + node), while surrounding text becomes `textNode`s. + To escape a delimiter, repeat it (`"{{"`). + If no key is given (`{}`) are numbered automatically: + `"{} ? {} : {}"` becomes `"{1} ? {2} : {3}"`, while + `"{} ? {3} : {}"` becomes `"{1} ? {3} : {4}"` (the count restarts at each + numbered placeholder). + If a key appears more than once in `format`, the node in + `content[]` is inserted for the first, and copies of it for + subsequent occurences. +- `nodes`: just a table of nodes. +- `opts`: optional arguments: + - `delimiters`: string, two characters. Change `{}` to some other pair, e.g. + `"<>"`. + - `strict`: Warn about unused nodes (default true). + - `trim_empty`: remove empty (`"%s*"`) first and last line in `format`. Useful + when passing multiline strings via `[[]]` (default true). + - `dedent`: remove indent common to all lines in `format`. Again, makes + passing multiline-strings a bit nicer (default true). + - `repeat_duplicates`: repeat nodes when a key is reused instead of copying + the node if it has a jump-index, refer to |luasnip-basics-jump-index| to + know which nodes have a jump-index (default false). + +There is also `require("luasnip.extras.fmt").fmta`. This only differs from +`fmt` by using angle brackets (`<>`) as the default delimiter. + + +CONDITIONS *luasnip-extras-conditions* + +This module (`luasnip.extras.condition`) contains functions that can be passed +to a snippet’s `condition` or `show_condition`. These are grouped accordingly +into `luasnip.extras.conditions.expand` and `luasnip.extras.conditions.show`: + +**expand**: + +- `line_begin`: only expand if the cursor is at the beginning of the line. + +**show**: + +- `line_end`: only expand at the end of the line. +- `has_selected_text`: only expand if there’s selected text stored after pressing + `store_selection_keys`. + +Additionally, `expand` contains all conditions provided by `show`. + + +CONDITION OBJECTS ~ + +`luasnip.extras.conditions` also contains condition objects. These can, just +like functions, be passed to `condition` or `show_condition`, but can also be +combined with each other into logical expressions: + +- `-c1 -> not c1` +- `c1 * c2 -> c1 and c2` +- `c1 + c2 -> c1 or c2` +- `c1 - c2 -> c1 and not c2`: This is similar to set differences: + `A \ B = {a in A | a not in B}`. This makes `-(a + b) = -a - b` an identity + representing de Morgan’s law: `not (a or b) = not a and not b`. However, + since boolean algebra lacks an additive inverse, `a + (-b) = a - b` does not + hold. Thus, this is NOT the same as `c1 + (-c2)`. +- `c1 ^ c2 -> c1 xor(!=) c2` +- `c1 % c2 -> c1 xnor(==) c2`: This decision may seem weird, considering how + there is an overload for the `==`-operator. Unfortunately, it’s not possible + to use this for our purposes (some info + here ), + so we decided to make use of a more obscure symbol (which will hopefully avoid + false assumptions about its meaning). + +This makes logical combinations of conditions very readable. Compare + +>lua + condition = conditions.expand.line_end + conditions.expand.line_begin +< + +with the more verbose + +>lua + condition = function(...) return conditions.expand.line_end(...) or conditions.expand.line_begin(...) end +< + +The conditions provided in `show` and `expand` are already condition objects. +To create new ones, use +`require("luasnip.extras.conditions").make_condition(condition_fn)` + + +ON THE FLY-SNIPPETS *luasnip-extras-on-the-fly-snippets* + +Sometimes it’s desirable to create snippets tailored for exactly the current +situation. For example inserting repetitive, but just slightly different +invocations of some function, or supplying data in some schema. + +On-the-fly snippets enable exactly this use case: they can be quickly created +and expanded with as little disruption as possible. + +Since they should mainly fast to write and don’t necessarily need all bells +and whistles, they don’t make use of lsp/textmate-syntax, but a more +simplistic one: + +- `$anytext` denotes a placeholder (`insertNode`) with text "anytext". The text + also serves as a unique key: if there are multiple placeholders with the same + key, only the first will be editable, the others will just mirror it. +- … That’s it. `$` can be escaped by preceding it with a second `$`, all other + symbols will be interpreted literally. + +There is currently only one way to expand on-the-fly snippets: +`require('luasnip.extras.otf').on_the_fly("")` will interpret +whatever text is in the register `` as a snippet, and expand it +immediately. The idea behind this mechanism is that it enables a very immediate +way of supplying and retrieving (expanding) the snippet: write the snippet-body +into the buffer, cut/yank it into some register, and call +`on_the_fly("")` to expand the snippet. + +Here’s one set of example keybindings: + +>vim + " in the first call: passing the register is optional since `on_the_fly` + " defaults to the unnamed register, which will always contain the previously cut + " text. + vnoremap "eclua require('luasnip.extras.otf').on_the_fly("e") + inoremap lua require('luasnip.extras.otf').on_the_fly("e") +< + +Obviously, `` is arbritary and can be changed to any other key combo. +Another interesting application is allowing multiple on-the-fly snippets at the +same time by retrieving snippets from multiple registers: + +>vim + " For register a + vnoremap a "aclua require('luasnip.extras.otf').on_the_fly() + inoremap a lua require('luasnip.extras.otf').on_the_fly("a") + + + " For register b + vnoremap a "bc:lua require('luasnip.extras.otf').on_the_fly() + inoremap b lua require('luasnip.extras.otf').on_the_fly("b") +< + + +SELECT_CHOICE *luasnip-extras-select_choice* + +It’s possible to leverage `vim.ui.select` for selecting a choice directly, +without cycling through the available choices. All that is needed for this is +calling `require("luasnip.extras.select_choice")`, most likely via some +keybind, e.g. + +>vim + inoremap lua require("luasnip.extras.select_choice")() +< + +while inside a choiceNode. The `opts.kind` hint for `vim.ui.select` will be set +to `luasnip`. + + +FILETYPE-FUNCTIONS *luasnip-extras-filetype-functions* + +Contains some utility functions that can be passed to the `ft_func` or +`load_ft_func`-settings. + +- `from_filetype`: the default for `ft_func`. Simply returns the filetype(s) of + the buffer. +- `from_cursor_pos`: uses treesitter to determine the filetype at the cursor. + With that, it’s possible to expand snippets in injected regions, as long as + the treesitter parser supports them. If this is used in conjuction with + `lazy_load`, extra care must be taken that all the filetypes that can be + expanded in a given buffer are also returned by `load_ft_func` (otherwise their + snippets may not be loaded). This can easily be achieved with `extend_load_ft`. +- `extend_load_ft`: `fn(extend_ft:map) -> fn` A simple solution to the problem + described above is loading more filetypes than just that of the target buffer + when `lazy_load`ing. This can be done ergonomically via `extend_load_ft`: + calling it with a table where the keys are filetypes, and the values are the + filetypes that should be loaded additionaly returns a function that can be + passed to `load_ft_func` and takes care of extending the filetypes properly. + >lua + ls.setup({ + load_ft_func = + -- Also load both lua and json when a markdown-file is opened, + -- javascript for html. + -- Other filetypes just load themselves. + require("luasnip.extras.filetype_functions").extend_load_ft({ + markdown = {"lua", "json"}, + html = {"javascript"} + }) + }) + < + + +POSTFIX-SNIPPET *luasnip-extras-postfix-snippet* + +Postfix snippets, famously used in rust analyzer + and various IDEs, are a type of snippet +which alters text before the snippet’s trigger. While these can be +implemented using regTrig snippets, this helper makes the process easier in +most cases. + +The simplest example, which surrounds the text preceeding the `.br` with +brackets `[]`, looks like: + +>lua + postfix(".br", { + f(function(_, parent) + return "[" .. parent.snippet.env.POSTFIX_MATCH .. "]" + end, {}), + }) +< + +and is triggered with `xxx.br` and expands to `[xxx]`. + +Note the `parent.snippet.env.POSTFIX_MATCH` in the function node. This is +additional field generated by the postfix snippet. This field is generated by +extracting the text matched (using a configurable matching string, see below) +from before the trigger. In the case above, the field would equal `"xxx"`. This +is also usable within dynamic nodes. + +This field can also be used within lambdas and dynamic nodes. + +>lua + postfix(".br", { + l("[" .. l.POSTFIX_MATCH .. "]"), + }) +< + +>lua + postfix(".brd", { + d(1, function (_, parent) + return sn(nil, {t("[" .. parent.env.POSTFIX_MATCH .. "]")}) + end) + }) +< + +The arguments to `postfix` are identical to the arguments to `s` but with a few +extra options. + +The first argument can be either a string or a table. If it is a string, that +string will act as the trigger, and if it is a table it has the same valid keys +as the table in the same position for `s` except: + +- `wordTrig`: This key will be ignored if passed in, as it must always be + false for postfix snippets. +- `match_pattern`: The pattern that the line before the trigger is matched + against. The default match pattern is `"[%w%.%_%-]+$"`. Note the `$`. This + matches since only the line _up until_ the beginning of the trigger is + matched against the pattern, which makes the character immediately + preceeding the trigger match as the end of the string. + +Some other match strings, including the default, are available from the postfix +module. `require("luasnip.extras.postfix).matches`: + +- `default`: `[%w%.%_%-%"%']+$` +- `line`: `^.+$` + +The second argument is identical to the second argument for `s`, that is, a +table of nodes. + +The optional third argument is the same as the third (`opts`) argument to the +`s` function, but with one difference: + +The postfix snippet works using a callback on the pre_expand event of the +snippet. If you pass a callback on the pre_expand event (structure example +below) it will get run after the builtin callback. + +>lua + { + callbacks = { + [-1] = { + [events.pre_expand] = function(snippet, event_args) + -- function body to match before the dot + -- goes here + end + } + } + } +< + + +TREESITTER-POSTFIX-SNIPPET *luasnip-extras-treesitter-postfix-snippet* + +Instead of triggering a postfix-snippet when some pattern matches in front of +the trigger, it might be useful to match if some specific treesitter-nodes +surround/are in front of the trigger. While this functionality can also be +implemented by a cusutom `resolveExpandParams`, this helper simplifies the +common cases. + +This matching of treesitter-nodes can be done either + +- by providing a query and the name of the capture that should be in front of + the trigger (in most cases, the complete match, but requiring specific nodes + before/after the matched node may be useful as well), or +- by providing a function that manually walks the node-tree, and returns the + node in front of the trigger on success (for increased flexibility). + +A simple example, which surrounds the previous node’s text preceeding the +`.mv` with `std::move()` in cpp files, looks like: + +>lua + local treesitter_postfix = require("luasnip.extras.treesitter_postfix").treesitter_postfix + + treesitter_postfix({ + trig = ".mv", + matchTSNode = { + query = [[ + [ + (call_expression) + (identifier) + (template_function) + (subscript_expression) + (field_expression) + (user_defined_literal) + ] @prefix + ]] + query_lang = "cpp" + }, + },{ + f(function(_, parent) + local node_content = table.concat(parent.snippet.env.LS_TSMATCH, '\n') + local replaced_content = ("std::move(%s)"):format(node_content) + return vim.split(ret_str, "\n", { trimempty = false }) + end) + }) +< + +`LS_TSMATCH` is the treesitter-postfix equivalent to `POSTFIX_MATCH`, and is +populated with the match (in this case the text of a treesitter-node) in front +of the trigger. + +The arguments to `treesitter_postfix` are identical to the arguments to `s` but +with a few extra options. + +The first argument has to be a table, which defines at least `trig` and +`matchTSNode`. All keys from the regular `s` may be set here (except for +`wordTrig`, which will be ignored), and additionally the following: + +- `reparseBuffer`, `string?`: Sometimes the trigger may interfere with + treesitter recognizing queries correctly. With this option, the trigger may + either be removed from the live-buffer (`"live"`), from a copy of the buffer + (`"copy"`), or not at all (`nil`). +- `matchTSNode`: How to determine whether there is a matching node in front of + the cursor. There are two options: + - `fun(parser: LuaSnip.extra.TSParser, pos: { [1]: number, [2]: number }): LuaSnip.extra.NamedTSMatch?, TSNode? Manually determine whether there is a matching node that ends just before`pos`(the beginning of the trigger). Return`nil,nil`if there is no match, otherwise first return a table mapping names to nodes (the text, position and type of these will be provided via`snip.env`), and second the node that is the matched node. + - `LuaSnip.extra.MatchTSNodeOpts`, which represents a query and provides all + captures of the matched pattern in `NamedTSMatch`. It contains the following + options: + - `query`, `string`: The query, in textual form. + - `query_name`, `string`: The name of the runtime-query to be used (passed + to `query.get()`), defaults to `"luasnip"` (so one could create a + file which only contains queries used by luasnip, like + `$CONFDIR/queries//luasnip.scm`, which might make sense to define + general concepts independent of a single snippet). + `query` and `query_name` are mutually exclusive, only one of both shall be + defined. + - `query_lang`, `string`: The language of the query. This is the only + required parameter to this function, since there’s no sufficiently + straightforward way to determine the language of the query for us. + Consider using `extend_override` to define a `ts_postfix`-function that + automatically fills in the language for the filetype of the snippet-file. + - `match_captures`, `string|string[]`: The capture(s) to use for determining + the actual prefix (so the node that should be immediately in front of the + trigger). This defaults to just `"prefix"`. + - `select`, `string?|fun(): LuaSnip.extra.MatchSelector`: Since there may be + multiple matching captures in front of the cursor, there has to be some + way to select the node that will actually be used. + If this is a string, it has to be one of "any", "shortest", or "longest", + which mean that any, the shortest, or the longest match is used. + If it is a function, it must return a table with two fields, `record` and + `retrieve`. `record` is called with a TSMatch and a potential node for the + ts-match, and may return `true` to abort the selection-procedure. + `retrieve` must return either a TSMatch-TSNode-tuple (which is used as the + match) or `nil`, to signify that there is no match. + `lua/luasnip/extras/_treesitter.lua` contains the table + `builtin_tsnode_selectors`, which contains the implementations for + any/shortest/longest, which can be used as examples for more complicated + custom-selectors. + +The text of the matched node can be accessed as `snip.env.LS_TSMATCH`. The text +of the nodes returned as `NamedTSMatch` can be accessed as +`snip.env.LS_TSCAPTURE_`, and their range and type as +`snip.env.LS_TSDATA..range/type` (where range is a tuple +of row-col-tuples, both 0-indexed). + +For a query like + +>scm + (function_declaration + name: (identifier) @fname + parameters: (parameters) @params + body: (block) @body + ) @prefix +< + +matched against + +>lua + function add(a, b) + return a + b + end +< + +`snip.env` would contain: + +- `LS_TSMATCH`: `{ "function add(a, b)", "\treturn a + b", "end" }` +- `LS_TSDATA`: + >lua + { + body = { + range = { { 1, 1 }, { 1, 13 } }, + type = "block" + }, + fname = { + range = { { 0, 9 }, { 0, 12 } }, + type = "identifier" + }, + params = { + range = { { 0, 12 }, { 0, 18 } }, + type = "parameters" + }, + prefix = { + range = { { 0, 0 }, { 2, 3 } }, + type = "function_declaration" + } + } + < +- `LS_TSCAPTURE_FNAME`: `{ "add" }` +- `LS_TSCAPTURE_PARAMS`: `{ "(a, b)" }` +- `LS_TSCAPTURE_BODY`: `{ "return a + b" }` +- `LS_TSCAPTURE_PREFIX`: `{ "function add(a, b)", "\treturn a + b", "end" }` + +(note that all variables containing text of nodes are string-arrays, one entry +for each line) + +There is one important caveat when accessing `LS_TSDATA` in +function/dynamicNodes: It won’t contain the values as specified here while +generating docstrings (in fact, it won’t even be a table). Since docstrings +have to be generated without any runtime-information, we just have to provide +dummy-data in `env`, which will be some kind of string related to the name of +the env-variable. Since the structure of `LS_TSDATA` obviously does not fit +that model, we can’t really handle it in a nice way (at least yet). So, for +now, best include a check like `local static_evaluation = type(env.LS_TSDATA) +== "string"`, and behave accordingly if `static_evaluation` is true (for +example, return some value tailored for displaying it in a docstring). + +One more example, which actually uses a few captures: + +>lua + ts_post({ + matchTSNode = { + query = [[ + (function_declaration + name: (identifier) @fname + parameters: (parameters) @params + body: (block) @body + ) @prefix + ]], + query_lang = "lua", + }, + trig = ".var" + }, fmt([[ + local {} = function{} + {} + end + ]], { + l(l.LS_TSCAPTURE_FNAME), + l(l.LS_TSCAPTURE_PARAMS), + l(l.LS_TSCAPTURE_BODY), + })) +< + +The module `luasnip.extras.treesitter_postfix` contains a few functions that +may be useful for creating more efficient ts-postfix-snippets. Nested in +`builtin.tsnode_matcher` are: + +- `fun find_topmost_types(types: string[]): MatchTSNodeFunc`: Generates + a `LuaSnip.extra.MatchTSNodeFunc` which returns the last parent whose type + is in `types`. +- `fun find_first_types(types: string[]): MatchTSNodeFunc`: Similar to + `find_topmost_types`, only this one matches the first parent whose type is in + types. +- `find_nth_parent(n: number): MatchTSNodeFunc`: Simply matches the `n`-th + parent of the innermost node infront of the trigger. + +With `find_topmost_types`, the first example can be implemented more +efficiently (without needing a whole query): + +>lua + local postfix_builtin = require("luasnip.extras.treesitter_postfix").builtin + + ls.add_snippets("all", { + ts_post({ + matchTSNode = postfix_builtin.tsnode_matcher.find_topmost_types({ + "call_expression", + "identifier", + "template_function", + "subscript_expression", + "field_expression", + "user_defined_literal" + }), + trig = ".mv" + }, { + l(l_str.format("std::move(%s)", l.LS_TSMATCH)) + }) + }, {key = "asdf"}) +< + + +SNIPPET LIST *luasnip-extras-snippet-list* + +>lua + local sl = require("luasnip.extras.snippet_list") +< + +Makes an `open` function available to use to open currently available snippets +in a different buffer/window/tab. + +`sl.open(opts:table|nil)` + +- `opts`: optional arguments: + - `snip_info`: `snip_info(snippet) -> table representation of snippet` + - `printer`: `printer(snippets:table) -> any` + - `display`: `display(snippets:any)` + +Benefits include: syntax highlighting, searching, and customizability. + +Simple Example: + +>lua + sl.open() +< + +Customization Examples: + +>lua + -- making our own snip_info + local function snip_info(snippet) + return { name = snippet.name } + end + + -- using it + sl.open({snip_info = snip_info}) +< + +>lua + -- making our own printer + local function printer(snippets) + local res = "" + + for ft, snips in pairs(snippets) do + res = res .. ft .. "\n" + for _, snip in pairs(snips) do + res = res .. " " .. "Name: " .. snip.name .. "\n" + res = res .. " " .. "Desc: " .. snip.description[1] .. "\n" + res = res .. " " .. "Trigger: " .. snip.trigger .. "\n" + res = res .. " ----" .. "\n" + end + end + + return res + end + + + -- using it + sl.open({printer = printer}) +< + +>lua + -- making our own display + local function display(printer_result) + -- right vertical split + vim.cmd("botright vnew") + + -- get buf and win handle + local buf = vim.api.nvim_get_current_buf() + local win = vim.api.nvim_get_current_win() + + -- setting window and buffer options + vim.api.nvim_win_set_option(win, "foldmethod", "manual") + vim.api.nvim_buf_set_option(buf, "filetype", "javascript") + + vim.api.nvim_buf_set_option(buf, "buftype", "nofile") + vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe") + vim.api.nvim_buf_set_option(buf, "buflisted", false) + + vim.api.nvim_buf_set_name(buf, "Custom Display buf " .. buf) + + -- dump snippets + local replacement = vim.split(printer_result) + vim.api.nvim_buf_set_lines(buf, 0, 0, false, replacement) + end + + -- using it + sl.open({display = display}) +< + +There is a **caveat** with implementing your own printer and/or display +function. The **default** behavior for the printer function is to return a +string representation of the snippets. The display function uses the results +from the printer function, therefore by **default** the display function is +expecting that result to be a string. + +However, this doesn’t have to be the case. For example, you can implement +your own printer function that returns a table representation of the snippets +**but** you would have to then implement your own display function or some +other function in order to return the result as a string. + +An `options` table, which has some core functionality that can be used to +customize 'common' settings, is provided. + +- `sl.options`: options table: + - `display`: `display(opts:table|nil) -> function(printer_result:string)` + +You can see from the example above that making a custom display is a fairly +involved process. What if you just wanted to change a buffer option like the +name or just the filetype? This is where `sl.options.display` comes in. It +allows you to customize buffer and window options while keeping the default +behavior. + +`sl.options.display(opts:table|nil) -> function(printer_result:string)` + +- `opts`: optional arguments: + - `win_opts`: `table which has a {window_option = value} form` + - `buf_opts`: `table which has a {buffer_option = value} form` + - `get_name`: `get_name(buf) -> string` + +Let’s recreate the custom display example above: + +>lua + -- keeping the default display behavior but modifying window/buffer + local modified_default_display = sl.options.display({ + buf_opts = {filetype = "javascript"}, + win_opts = {foldmethod = "manual"}, + get_name = function(buf) return "Custom Display buf " .. buf end + }) + + -- using it + sl.open({display = modified_default_display}) +< + + +SNIPPET LOCATION *luasnip-extras-snippet-location* + +This module can consume a snippets |luasnip-source|, more specifically, jump to +the location referred by it. This is primarily implemented for snippet which +got their source from one of the loaders, but might also work for snippets +where the source was set manually. + +`require("luasnip.extras.snip_location")`: + +- `snip_location.jump_to_snippet(snip, opts)` + Jump to the definition of `snip`. + - `snip`: a snippet with attached source-data. + - `opts`: `nil|table`, optional arguments, valid keys are: + - `hl_duration_ms`: `number`, duration for which the definition should be highlighted, + in milliseconds. 0 disables the highlight. + - `edit_fn`: `function(file)`, this function will be called with the file + the snippet is located in, and is responsible for jumping to it. + We assume that after it has returned, the current buffer contains `file`. +- `snip_location.jump_to_active_snippet(opts)` + Jump to definition of active snippet. + - `opts`: `nil|table`, accepts the same keys as the `opts`-parameter of + `jump_to_snippet`. + + +============================================================================== +17. Extend Decorator *luasnip-extend-decorator* + +Most of luasnip’s functions have some arguments to control their behaviour. +Examples include `s`, where `wordTrig`, `regTrig`, … can be set in the first +argument to the function, or `fmt`, where the delimiter can be set in the third +argument. This is all good and well, but if these functions are often used with +non-default settings, it can become cumbersome to always explicitly set them. + +This is where the `extend_decorator` comes in: it can be used to create +decorated functions which always extend the arguments passed directly with +other previously defined ones. + +An example: + +>lua + local fmt = require("luasnip.extras.fmt").fmt + + fmt("{}", {i(1)}) -- -> list of nodes, containing just the i(1). + + -- when authoring snippets for some filetype where `{` and `}` are common, they + -- would always have to be escaped in the format-string. It might be preferable + -- to use other delimiters, like `<` and `>`. + + fmt("<>", {i(1)}, {delimiters = "<>"}) -- -> same as above. + + -- but it's quite annoying to always pass the `{delimiters = "<>"}`. + + -- with extend_decorator: + local fmt_angle = ls.extend_decorator.apply(fmt, {delimiters = "<>"}) + fmt_angle("<>", {i(1)}) -- -> same as above. + + -- the same also works with other functions provided by luasnip, for example all + -- node/snippet-constructors and `parse_snippet`. +< + +`extend_decorator.apply(fn, ...)` requires that `fn` is previously registered +via `extend_decorator.register`. (This is not limited to LuaSnip’s functions; +although, for usage outside of LuaSnip, best copy the source file: +`/lua/luasnip/util/extend_decorator.lua`). + +`register(fn, ...)`: + +- `fn`: the function. +- `...`: any number of tables. Each specifies how to extend an argument of `fn`. + The tables accept: + - arg_indx, `number` (required): the position of the parameter to override. + - extend, `fn(arg, extend_value) -> effective_arg` (optional): this function + is used to extend the args passed to the decorated function. + It defaults to a function which just extends the the arg-table with the + extend table (accepts `nil`). + This extend behaviour is adaptable to accomodate `s`, where the first + argument may be string or table. + +`apply(fn, ...) -> decorated_fn`: + +- `fn`: the function to decorate. +- `...`: The values to extend with. These should match the descriptions passed + in `register` (the argument first passed to `register` will be extended with + the first value passed here). + +One more example for registering a new function: + +>lua + local function somefn(arg1, arg2, opts1, opts2) + -- not important + end + + -- note the reversed arg_indx!! + extend_decorator.register(somefn, {arg_indx=4}, {arg_indx=3}) + local extended = extend_decorator.apply(somefn, + {key = "opts2 is extended with this"}, + {key = "and opts1 with this"}) + extended(...) +< + + +============================================================================== +18. LSP-Snippets *luasnip-lsp-snippets* + +LuaSnip is capable of parsing LSP-style snippets using +`ls.parser.parse_snippet(context, snippet_string, opts)`: + +>lua + ls.parser.parse_snippet({trig = "lsp"}, "$1 is ${2|hard,easy,challenging|}") +< + +`context` can be: - `string|table`: treated like the first argument to `ls.s`, +`parse_snippet` returns a snippet. - `number`: `parse_snippet` returns a +snippetNode, with the position `context`. - `nil`: `parse_snippet` returns a +flat table of nodes. This can be used like `fmt`. + +Nested placeholders(`"${1:this is ${2:nested}}"`) will be turned into +choiceNodes with: - the given snippet(`"this is ${1:nested}"`) and - an empty +insertNode + +This behaviour can be modified by changing `parser_nested_assembler` in +`ls.setup()`. + +LuaSnip will also modify some snippets that it is incapable of representing +accurately: + +- if the `$0` is a placeholder with something other than just text inside +- if the `$0` is a choice +- if the `$0` is not an immediate child of the snippet (it could be inside a + placeholder: `"${1: $0 }"`) + +To remedy those incompatibilities, the invalid `$0` will be replaced with a +tabstop/placeholder/choice which will be visited just before the new `$0`. This +new `$0` will be inserted at the (textually) earliest valid position behind the +invalid `$0`. + +`opts` can contain the following keys: - `trim_empty`: boolean, remove empty +lines from the snippet. Default true. - `dedent`: boolean, remove common indent +from the snippet’s lines. Default true. + +Both `trim_empty` and `dedent` will be disabled for snippets parsed via +`ls.lsp_expand`: it might prevent correct expansion of snippets sent by lsp. + + +SNIPMATE PARSER *luasnip-lsp-snippets-snipmate-parser* + +It is furthermore possible to parse SnipMate snippets (this includes support +for vimscript-evaluation!!) + +SnipMate snippets need to be parsed with a different function, +`ls.parser.parse_snipmate`: + +>lua + ls.parser.parse_snipmate("year", "The year is `strftime('%Y')`") +< + +`parse_snipmate` accepts the same arguments as `parse_snippet`, only the +snippet body is parsed differently. + + +TRANSFORMATIONS *luasnip-lsp-snippets-transformations* + +To apply Variable/Placeholder-transformations +, +luasnip needs to apply ECMAScript regexes. This is implemented by relying on +`jsregexp` . + +The easiest (but potentially error-prone) way to install it is by calling `make +install_jsregexp` in the repo root. + +This process can be automated by `packer.nvim`: + +>lua + use { "L3MON4D3/LuaSnip", run = "make install_jsregexp" } +< + +If this fails, first open an issue :P, and then try installing the +`jsregexp`-luarock. This is also possible via `packer.nvim`, although actual +usage may require a small workaround, see here + or here +. + +Alternatively, `jsregexp` can be cloned locally, `make`d, and the resulting +`jsregexp.so` placed in some place where nvim can find it (probably +`~/.config/nvim/lua/`). + +If `jsregexp` is not available, transformations are replaced by a simple copy. + + +============================================================================== +19. Variables *luasnip-variables* + +All `TM_something`-variables are supported with two additions: `LS_SELECT_RAW` +and `LS_SELECT_DEDENT`. These were introduced because `TM_SELECTED_TEXT` is +designed to be compatible with VSCode’s behavior, which can be +counterintuitive when the snippet can be expanded at places other than the +point where selection started (or when doing transformations on selected text). +Besides those we also provide `LS_TRIGGER` which contains the trigger of the +snippet, and `LS_CAPTURE_n` (where n is a positive integer) that contains the +n-th capture when using a regex with capture groups as `trig` in the snippet +definition. + +All variables can be used outside of lsp-parsed snippets as their values are +stored in a snippets’ `snip.env`-table: + +>lua + s("selected_text", f(function(args, snip) + local res, env = {}, snip.env + table.insert(res, "Selected Text (current line is " .. env.TM_LINE_NUMBER .. "):") + for _, ele in ipairs(env.LS_SELECT_RAW) do table.insert(res, ele) end + return res + end, {})) +< + +To use any `*SELECT*` variable, the `store_selection_keys` must be set via +`require("luasnip").config.setup({store_selection_keys=""})`. In this +case, hitting `` while in visual mode will populate the `*SELECT*`-vars +for the next snippet and then clear them. + + +ENVIRONMENT NAMESPACES *luasnip-variables-environment-namespaces* + +You can also add your own variables by using the `ls.env_namespace(name, opts)` +where: + +- `name`: `string` the names the namespace, can’t contain the character “_” +- `opts` is a table containing (in every case `EnvVal` is the same as `string|list[string]`: + - `vars`: `(fn(name:string)->EnvVal) | map[string, EnvVal]` + Is a function that receives a string and returns a value for the var with that name + or a table from var name to a value + (in this case, if the value is a function it will be executed lazily once per snippet expansion). + - `init`: `fn(info: table)->map[string, EnvVal]` Returns + a table of variables that will set to the environment of the snippet on expansion, + use this for vars that have to be calculated in that moment or that depend on each other. + The `info` table argument contains `pos` (0-based position of the cursor on expansion), + the `trigger` of the snippet and the `captures` list. + - `eager`: `list[string]` names of variables that will be taken from `vars` and appended eagerly (like those in init) + - `multiline_vars`: `(fn(name:string)->bool)|map[sting, bool]|bool|string[]` Says if certain vars are a table or just a string, + can be a function that get’s the name of the var and returns true if the var is a key, + a list of vars that are tables or a boolean for the full namespace, it’s false by default. Refer to + issue#510 for more information. + +The four fields of `opts` are optional but you need to provide either `init` or +`vars`, and `eager` can’t be without `vars`. Also, you can’t use namespaces +that override default vars. + +A simple example to make it more clear: + +>lua + local function random_lang() + return ({"LUA", "VIML", "VIML9"})[math.floor(math.random()/2 + 1.5)] + end + + ls.env_namespace("MY", {vars={ NAME="LuaSnip", LANG=random_lang }}) + + -- then you can use $MY_NAME and $MY_LANG in your snippets + + ls.env_namespace("SYS", {vars=os.getenv, eager={"HOME"}}) + + -- then you can use $SYS_HOME which was eagerly initialized but also $SYS_USER (or any other system environment var) in your snippets + + lsp.env_namespace("POS", {init=function(info) return {VAL=vim.inspect(info.pos)} end}) + + -- then you can use $POS_VAL in your snippets + + s("custom_env", d(1, function(args, parent) + local env = parent.snippet.env + return sn(nil, t { + "NAME: " .. env.MY_NAME, + "LANG: " .. env.MY_LANG, + "HOME: " .. env.SYS_HOME, + "USER: " .. env.SYS_USER, + "VAL: " .. env.POS_VAL + }) + end, {})) +< + + +LSP-VARIABLES *luasnip-variables-lsp-variables* + +All variables, even ones added via `env_namespace`, can be accessed in LSP +snippets as `$VAR_NAME`. + +The lsp-spec states: + +------------------------------------------------------------------------------ +With `$name` or `${name:default}` you can insert the value of a variable. When +a variable isn’t set, its default or the empty string is inserted. When a +variable is unknown (that is, its name isn’t defined) the name of the +variable is inserted and it is transformed into a placeholder. + +------------------------------------------------------------------------------ +The above necessiates a differentiation between `unknown` and `unset` +variables: + +For LuaSnip, a variable `VARNAME` is `unknown` when `env.VARNAME` returns `nil` +and `unset` if it returns an empty string. + +Consider this when adding env-variables which might be used in LSP snippets. + + +============================================================================== +20. Loaders *luasnip-loaders* + +Luasnip is capable of loading snippets from different formats, including both +the well-established VSCode and SnipMate format, as well as plain Lua files for +snippets written in Lua. + +All loaders (except the vscode-standalone-loader) share a similar interface: +`require("luasnip.loaders.from_{vscode,snipmate,lua}").{lazy_,}load(opts:table|nil)` + +where `opts` can contain the following keys: + +- `paths`: List of paths to load. Can be a table, or a single + comma-separated string. + The paths may begin with `~/` or `./` to indicate that the path is + relative to your `$HOME` or to the directory where your `$MYVIMRC` resides + (useful to add your snippets). + If not set, `runtimepath` is searched for + directories that contain snippets. This procedure differs slightly for + each loader: + - `lua`: the snippet-library has to be in a directory named + `"luasnippets"`. + - `snipmate`: similar to lua, but the directory has to be `"snippets"`. + - `vscode`: any directory in `runtimepath` that contains a + `package.json` contributing snippets. +- `lazy_paths`: behaves essentially like `paths`, with two exceptions: if it is + `nil`, it does not default to `runtimepath`, and the paths listed here do not + need to exist, and will be loaded on creation. + LuaSnip will do its best to determine the path that this should resolve to, + but since the resolving we do is not very sophisticated it may produce + incorrect paths. Definitely check the log if snippets are not loaded as + expected. +- `exclude`: List of languages to exclude, empty by default. +- `include`: List of languages to include, includes everything by default. +- `{override,default}_priority`: These keys are passed straight to the + `add_snippets`-calls (documented in |luasnip-api|) and can therefore change the + priority of snippets loaded from some colletion (or, in combination with + `{in,ex}clude`, only some of its snippets). +- `fs_event_providers`: `table?`, specifies which mechanisms + should be used to watch files for updates/creation. + If `autocmd` is set to `true`, a `BufWritePost`-hook watches files of this + collection, if `libuv` is set, the file-watcher-api exposed by libuv is used + to watch for updates. + Use `libuv` if you want snippets to update from other neovim-instances, and + `autocmd` if the collection resides on a filesystem where the libuv-watchers + may not work correctly. Or, of course, just enable both :D + By default, only `autocmd` is enabled. + +While `load` will immediately load the snippets, `lazy_load` will defer loading +until the snippets are actually needed (whenever a new buffer is created, or +the filetype is changed luasnip actually loads `lazy_load`ed snippets for the +filetypes associated with this buffer. This association can be changed by +customizing `load_ft_func` in `setup`: the option takes a function that, passed +a `bufnr`, returns the filetypes that should be loaded (`fn(bufnr) -> filetypes +(string[])`)). + +All of the loaders support reloading, so simply editing any file contributing +snippets will reload its snippets (according to `fs_event_providers` in the +instance where the file was edited, or in other instances as well). + +As an alternative (or addition) to automatic reloading, luasnip can also +process manual updates to files: Call +`require("luasnip.loaders").reload_file(path)` to reload the file at `path`. +This may be useful when the collection is controlled by some other plugin, or +when enabling the other reload-mechanisms is for some reason undesirable +(performance? minimalism?). + +For easy editing of these files, LuaSnip provides a `vim.ui.select`-based +dialog (|luasnip-loaders-edit_snippets|) where first the filetype, and then the +file can be selected. + + +SNIPPET-SPECIFIC FILETYPES *luasnip-loaders-snippet-specific-filetypes* + +Some loaders (vscode,lua) support giving snippets generated in some file their +own filetype (vscode via `scope`, lua via the underlying `filetype`-option for +snippets). These snippet-specific filetypes are not considered when determining +which files to `lazy_load` for some filetype, this is exclusively determined by +the `language` associated with a file in vscodes’ `package.json`, and the +file/directory-name in lua. + +- This can be resolved relatively easily in vscode, where the `language` + advertised in `package.json` can just be a superset of the `scope`s in the file. +- Another simplistic solution is to set the language to `all` (in lua, it might + make sense to create a directory `luasnippets/all/*.lua` to group these files + together). +- Another approach is to modify `load_ft_func` to load a custom filetype if the + snippets should be activated, and store the snippets in a file for that + filetype. This can be used to group snippets by e.g. framework, and load them + once a file belonging to such a framework is edited. + +**Example**: `react.lua` + +>lua + return { + s({filetype = "css", trig = ...}, ...), + s({filetype = "html", trig = ...}, ...), + s({filetype = "js", trig = ...}, ...), + } +< + +`luasnip_config.lua` + +>lua + load_ft_func = function(bufnr) + if "" then + -- will load `react.lua` for this buffer + return {"react"} + else + return require("luasnip.extras.filetype_functions").from_filetype_load + end + end +< + +See the |luasnip-troubleshooting-adding-snippets-loaders| section if one is +having issues adding snippets via loaders. + + +VS-CODE *luasnip-loaders-vs-code* + +As a reference on the structure of these snippet libraries, see +`friendly-snippets` . + +We support a small extension: snippets can contain LuaSnip-specific options in +the `luasnip`-table: + +>json + "example1": { + "prefix": "options", + "body": [ + "whoa! :O" + ], + "luasnip": { + "priority": 2000, + "autotrigger": true, + "wordTrig": false + } + } +< + +Files with the extension `jsonc` will be parsed as `jsonc`, json with comments +, while +`*.json` are parsed with a regular `json` parser, where comments are +disallowed. (the json-parser is a bit faster, so don’t default to `jsonc` if +it’s not necessary). + +**Example**: + +`~/.config/nvim/my_snippets/package.json`: + +>json + { + "name": "example-snippets", + "contributes": { + "snippets": [ + { + "language": [ + "all" + ], + "path": "./snippets/all.json" + }, + { + "language": [ + "lua" + ], + "path": "./lua.json" + } + ] + } + } +< + +`~/.config/nvim/my_snippets/snippets/all.json`: + +>json + { + "snip1": { + "prefix": "all1", + "body": [ + "expands? jumps? $1 $2 !" + ] + }, + "snip2": { + "prefix": "all2", + "body": [ + "multi $1", + "line $2", + "snippet$0" + ] + } + } +< + +`~/.config/nvim/my_snippets/lua.json`: + +>json + { + "snip1": { + "prefix": "lua", + "body": [ + "lualualua" + ] + } + } +< + +This collection can be loaded with any of + +>lua + -- don't pass any arguments, luasnip will find the collection because it is + -- (probably) in rtp. + require("luasnip.loaders.from_vscode").lazy_load() + -- specify the full path... + require("luasnip.loaders.from_vscode").lazy_load({paths = "~/.config/nvim/my_snippets"}) + -- or relative to the directory of $MYVIMRC + require("luasnip.loaders.from_vscode").load({paths = "./my_snippets"}) +< + + +STANDALONE ~ + +Beside snippet-libraries provided by packages, vscode also supports another +format which can be used for project-local snippets, or user-defined snippets, +`.code-snippets`. + +The layout of these files is almost identical to that of the package-provided +snippets, but there is one additional field supported in the +snippet-definitions, `scope`, with which the filetype of the snippet can be +set. If `scope` is not set, the snippet will be added to the global filetype +(`all`). + +`require("luasnip.loaders.from_vscode").load_standalone(opts)` + +- `opts`: `table`, can contain the following keys: + - `path`: `string`, Path to the `*.code-snippets`-file that should be loaded. + Just like the paths in `load`, this one can begin with a `"~/"` to be + relative to `$HOME`, and a `"./"` to be relative to the + neovim-config-directory. + - `{override,default}_priority`: These keys are passed straight to the + `add_snippets`-calls (documented in |luasnip-api|) and can be used to change + the priority of the loaded snippets. + - `lazy`: `boolean`, if it is set, the file does not have to exist when + `load_standalone` is called, and it will be loaded on creation. + `false` by default. + +**Example**: `a.code-snippets`: + +>jsonc + { + // a comment, since `.code-snippets` may contain jsonc. + "c/cpp-snippet": { + "prefix": [ + "trigger1", + "trigger2" + ], + "body": [ + "this is $1", + "my snippet $2" + ], + "description": "A description of the snippet.", + "scope": "c,cpp" + }, + "python-snippet": { + "prefix": "trig", + "body": [ + "this is $1", + "a different snippet $2" + ], + "description": "Another snippet-description.", + "scope": "python" + }, + "global snippet": { + "prefix": "trigg", + "body": [ + "this is $1", + "the last snippet $2" + ], + "description": "One last snippet-description.", + } + } +< + +This file can be loaded by calling + +>lua + require("luasnip.loaders.from_vscode").load_standalone({path = "a.code-snippets"}) +< + + +SNIPMATE *luasnip-loaders-snipmate* + +Luasnip does not support the full snipmate format: Only `./{ft}.snippets` and +`./{ft}/*.snippets` will be loaded. See honza/vim-snippets + for lots of examples. + +Like VSCode, the SnipMate format is also extended to make use of some of +LuaSnip’s more advanced capabilities: + +>snippets + priority 2000 + autosnippet options + whoa :O +< + +**Example**: + +`~/.config/nvim/snippets/c.snippets`: + +>snippets + # this is a comment + snippet c c-snippet + c! +< + +`~/.config/nvim/snippets/cpp.snippets`: + +>snippets + extends c + + snippet cpp cpp-snippet + cpp! +< + +This can, again, be loaded with any of + +>lua + require("luasnip.loaders.from_snipmate").load() + -- specify the full path... + require("luasnip.loaders.from_snipmate").lazy_load({paths = "~/.config/nvim/snippets"}) + -- or relative to the directory of $MYVIMRC + require("luasnip.loaders.from_snipmate").lazy_load({paths = "./snippets"}) +< + +Stuff to watch out for: + +- Using both `extends ` in `.snippets` and + `ls.filetype_extend("", {""})` leads to duplicate snippets. +- `${VISUAL}` will be replaced by `$TM_SELECTED_TEXT` to make the snippets + compatible with LuaSnip +- We do not implement eval using ` (backtick). This may be implemented in the + future. + + +LUA *luasnip-loaders-lua* + +Instead of adding all snippets via `add_snippets`, it’s possible to store +them in separate files and load all of those. The file-structure here is +exactly the supported snipmate-structure, e.g. `.lua` or `/*.lua` to +add snippets for the filetype ``. + +There are two ways to add snippets: + +- the files may return two lists of snippets, the snippets in the first are all + added as regular snippets, while the snippets in the second will be added as + autosnippets (both are the defaults, if a snippet defines a different + `snippetType`, that will have preference) +- snippets can also be appended to the global (only for these files - they are + not visible anywhere else) tables `ls_file_snippets` and + `ls_file_autosnippets`. This can be combined with a custom `snip_env` to define + and add snippets with one function call: + >lua + ls.setup({ + snip_env = { + s = function(...) + local snip = ls.s(...) + -- we can't just access the global `ls_file_snippets`, since it will be + -- resolved in the environment of the scope in which it was defined. + table.insert(getfenv(2).ls_file_snippets, snip) + end, + parse = function(...) + local snip = ls.parser.parse_snippet(...) + table.insert(getfenv(2).ls_file_snippets, snip) + end, + -- remaining definitions. + ... + }, + ... + }) + < + This is more flexible than the previous approach since the snippets don’t + have to be collected; they just have to be defined using the above `s` and + `parse`. + +As defining all of the snippet constructors (`s`, `c`, `t`, …) in every file +is rather cumbersome, LuaSnip will bring some globals into scope for executing +these files. By default, the names from `luasnip.config.snip_env` + +will be used, but it’s possible to customize them by setting `snip_env` in +`setup`. + +**Example**: + +`~/snippets/all.lua`: + +>lua + return { + s("trig", t("loaded!!")) + } +< + +`~/snippets/c.lua`: + +>lua + return { + s("ctrig", t("also loaded!!")) + }, { + s("autotrig", t("autotriggered, if enabled")) + } +< + +Load via + +>lua + require("luasnip.loaders.from_lua").load({paths = "~/snippets"}) +< + + +RELOADING WHEN EDITING REQUIRE’D FILES ~ + +While the lua-snippet-files will be reloaded on edit, this does not +automatically happen if a file the snippet-file depends on (eg. via `require`) +is changed. Since this still may still be desirable, there are two functions +exposed when a file is loaded by the lua-loader: `ls_tracked_dofile` and +`ls_tracked_dopackage`. They perform like `dofile` and (almost like) `require`, +but both register the loaded file internally as a dependency of the +snippet-file, so it can be reloaded when the loaded file is edited. As stated, +`ls_tracked_dofile` behaves exactly like `dofile`, but does the dependency-work +as well. `ls_tracked_dopackage` mimics `require` in that it does not take a +path, but a module-name like `"luasnip.loaders.from_lua"`, and then searches +the `runtimepath/lua`-directories, and path and cpath for the module. Unlike +`require`, the file will not be cached, since that would complicate the +reload-on-edit-behaviour. + + +EDIT_SNIPPETS *luasnip-loaders-edit_snippets* + +To easily edit snippets for the current session, the files loaded by any loader +can be quickly edited via +`require("luasnip.loaders").edit_snippet_files(opts:table|nil)` + +When called, it will open a `vim.ui.select`-dialog to select first a filetype, +and then (if there are multiple) the associated file to edit. + +`opts` contains four settings: + +- `ft_filter`: `fn(filetype:string) -> bool` Optionally filter initially listed + filetypes. `true` -> filetype will be listed, `false` -> not listed. Accepts + all filetypes by default. +- `format`: `fn(file:string, source_name:string) -> string|nil` `file` is simply + the path to the file, `source_name` is one of `"lua"`, `"snipmate"` or + `"vscode"`. If a string is returned, it is used as the title of the item, `nil` + on the other hand will filter out this item. The default simply replaces some + long strings (packer-path and config-path) in `file` with shorter, symbolic + names (`"$PLUGINS"`, `"$CONFIG"`), but this can be extended to + - filter files from some specific source/path + - more aggressively shorten paths using symbolic names, e.g. + `"$FRIENDLY_SNIPPETS"`. Example: hide the `*.lua` snippet files, and shorten + the path with `$LuaSnip`: + >lua + require "luasnip.loaders" .edit_snippet_files { + format = function(file, source_name) + if source_name == "lua" then return nil + else return file:gsub("/root/.config/nvim/luasnippets", "$LuaSnip") + end + end + } + < +- `edit`: `fn(file:string)` This function is supposed to open the file for + editing. The default is a simple `vim.cmd("edit " .. file)` (replace the + current buffer), but one could open the file in a split, a tab, or a floating + window, for example. +- `extend`: `fn(ft:string, ft_paths:string[]) -> (string,string)[]` This function + can be used to create additional choices for the file-selection. + - `ft`: The filetype snippet-files are queried for. + - `ft_paths`: list of paths to the known snippet files. + The function should return a list of `(string,string)`-tuples. The first of + each pair is the label that will appear in the selection-prompt, and the second + is the path that will be passed to the `edit()` function if that item was + selected. + This can be used to create a new snippet file for the current filetype: + >lua + require("luasnip.loaders").edit_snippet_files { + extend = function(ft, paths) + if #paths == 0 then + return { + { "$CONFIG/" .. ft .. ".snippets", + string.format("%s/%s.snippets", , ft) } + } + end + + return {} + end + } + < + +One comfortable way to call this function is registering it as a command: + +>vim + command! LuaSnipEdit :lua require("luasnip.loaders").edit_snippet_files() +< + + +============================================================================== +21. SnippetProxy *luasnip-snippetproxy* + +`SnippetProxy` is used internally to alleviate the upfront cost of loading +snippets from e.g. a SnipMate library or a VSCode package. This is achieved by +only parsing the snippet on expansion, not immediately after reading it from +some file. `SnippetProxy` may also be used from Lua directly to get the same +benefits: + +This will parse the snippet on startup: + +>lua + ls.parser.parse_snippet("trig", "a snippet $1!") +< + +while this will parse the snippet upon expansion: + +>lua + local sp = require("luasnip.nodes.snippetProxy") + sp("trig", "a snippet $1") +< + +`sp(context, body, opts) -> snippetProxy` + +- `context`: exactly the same as the first argument passed to `ls.s`. +- `body`: the snippet body. +- `opts`: accepts the same `opts` as `ls.s`, with some additions: + - `parse_fn`: the function for parsing the snippet. Defaults to + `ls.parser.parse_snippet` (the parser for LSP snippets), an alternative is + the parser for SnipMate snippets (`ls.parser.parse_snipmate`). + + +============================================================================== +22. ext_opts *luasnip-ext_opts* + +`ext_opts` can be used to set the `opts` (see `nvim_buf_set_extmark`) of the +extmarks used for marking node positions, either globally, per snippet or per +node. This means that they allow highlighting the text inside of nodes, or +adding virtual text to the line the node begins on. + +This is an example for the `node_ext_opts` used to set `ext_opts` of single +nodes: + +>lua + local ext_opts = { + -- these ext_opts are applied when the node is active (e.g. it has been + -- jumped into, and not out yet). + active = + -- this is the table actually passed to `nvim_buf_set_extmark`. + { + -- highlight the text inside the node red. + hl_group = "GruvboxRed" + }, + -- these ext_opts are applied when the node is not active, but + -- the snippet still is. + passive = { + -- add virtual text on the line of the node, behind all text. + virt_text = {{"virtual text!!", "GruvboxBlue"}} + }, + -- visited or unvisited are applied when a node was/was not jumped into. + visited = { + hl_group = "GruvboxBlue" + }, + unvisited = { + hl_group = "GruvboxGreen" + }, + -- and these are applied when both the node and the snippet are inactive. + snippet_passive = {} + } + + s("trig", { + i(1, "text1", { + node_ext_opts = ext_opts + }), + i(2, "text2", { + node_ext_opts = ext_opts + }) + }) +< + +In the above example, the text inside the insertNodes is higlighted in green if +they were not yet visited, in blue once they were, and red while they are. The +virtual text "virtual text!!" is visible as long as the snippet is active. + +To make defining `ext_opts` less verbose, more specific states inherit from +less specific ones: + +- `passive` inherits from `snippet_passive` +- `visited` and `unvisited` from `passive` +- `active` from `visited` + +To disable a key from a less specific state, it has to be explicitly set to its +default, e.g. to disable highlighting inherited from `passive` when the node +is `active`, `hl_group` should be set to `None`. + +------------------------------------------------------------------------------ +As stated earlier, these `ext_opts` can also be applied globally or for an +entire snippet. For this, it’s necessary to specify which kind of node a +given set of `ext_opts` should be applied to: + +>lua + local types = require("luasnip.util.types") + + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = {...}, + visited = {...}, + passive = {...}, + snippet_passive = {...} + }, + [types.choiceNode] = { + active = {...}, + unvisited = {...} + }, + [types.snippet] = { + passive = {...} + } + } + }) +< + +The above applies the given `ext_opts` to all nodes of these types, in all +snippets. + +>lua + local types = require("luasnip.util.types") + + s("trig", { i(1, "text1"), i(2, "text2") }, { + child_ext_opts = { + [types.insertNode] = { + passive = { + hl_group = "GruvboxAqua" + } + } + } + }) +< + +However, the `ext_opts` here are only applied to the `insertNodes` inside this +snippet. + +------------------------------------------------------------------------------ +By default, the `ext_opts` actually used for a node are created by extending +the `node_ext_opts` with the `effective_child_ext_opts[node.type]` of the +parent, which are in turn the parent’s `child_ext_opts` extended with the +global `ext_opts` (those set `ls.setup`). + +It’s possible to prevent both of these merges by passing +`merge_node/child_ext_opts=false` to the snippet/node-opts: + +>lua + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = {...} + } + } + }) + + s("trig", { + i(1, "text1", { + node_ext_opts = { + active = {...} + }, + merge_node_ext_opts = false + }), + i(2, "text2") + }, { + child_ext_opts = { + [types.insertNode] = { + passive = {...} + } + }, + merge_child_ext_opts = false + }) +< + +------------------------------------------------------------------------------ +The `hl_group` of the global `ext_opts` can also be set via standard highlight +groups: + +>lua + vim.cmd("hi link LuasnipInsertNodePassive GruvboxRed") + vim.cmd("hi link LuasnipSnippetPassive GruvboxBlue") + + -- needs to be called for resolving the effective ext_opts. + ls.setup({}) +< + +The names for the used highlight groups are +`"Luasnip{Passive,Active,SnippetPassive}"`, where `` can be any +kind of node in PascalCase (or "Snippet"). + +------------------------------------------------------------------------------ +One problem that might arise when nested nodes are highlighted is that the +highlight of inner nodes should be visible, e.g. above that of nodes they are +nested inside. + +This can be controlled using the `priority`-key in `ext_opts`. In +`nvim_buf_set_extmark`, that value is an absolute value, but here it is +relative to some base-priority, which is increased for each nesting level of +snippet(Nodes)s. + +Both the initial base-priority and its’ increase and can be controlled using +`ext_base_prio` and `ext_prio_increase`: + +>lua + ls.setup({ + ext_opts = { + [types.insertNode] = { + active = { + hl_group = "GruvboxBlue", + -- the priorities should be \in [0, ext_prio_increase). + priority = 1 + } + }, + [types.choiceNode] = { + active = { + hl_group = "GruvboxRed" + -- priority defaults to 0 + } + } + } + ext_base_prio = 200, + ext_prio_increase = 2 + }) +< + +Here the highlight of an insertNode nested directly inside a choiceNode is +always visible on top of it. + + +============================================================================== +23. Docstrings *luasnip-docstrings* + +Snippet docstrings can be queried using `snippet:get_docstring()`. The function +evaluates the snippet as if it was expanded regularly, which can be problematic +if e.g. a dynamicNode in the snippet relies on inputs other than the argument +nodes. `snip.env` and `snip.captures` are populated with the names of the +queried variable and the index of the capture respectively +(`snip.env.TM_SELECTED_TEXT` -> `'$TM_SELECTED_TEXT'`, `snip.captures[1]` -> +`'$CAPTURES1'`). Although this leads to more expressive docstrings, it can +cause errors in functions that e.g. rely on a capture being a number: + +>lua + s({trig = "(%d)", regTrig = true}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +This snippet works fine because `snippet.captures[1]` is always a number. +During docstring generation, however, `snippet.captures[1]` is `'$CAPTURES1'`, +which will cause an error in the functionNode. Issues with `snippet.captures` +can be prevented by specifying `docTrig` during snippet-definition: + +>lua + s({trig = "(%d)", regTrig = true, docTrig = "3"}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +`snippet.captures` and `snippet.trigger` will be populated as if actually +triggered with `3`. + +Other issues will have to be handled manually by checking the contents of e.g. +`snip.env` or predefining the docstring for the snippet: + +>lua + s({trig = "(%d)", regTrig = true, docstring = "repeatmerepeatmerepeatme"}, { + f(function(args, snip) + return string.rep("repeatme ", tonumber(snip.captures[1])) + end, {}) + }) +< + +Refer to #515 for a better +example to understand `docTrig` and `docstring`. + + +============================================================================== +24. Docstring-Cache *luasnip-docstring-cache* + +Although generation of docstrings is pretty fast, it’s preferable to not redo +it as long as the snippets haven’t changed. Using +`ls.store_snippet_docstrings(snippets)` and its counterpart +`ls.load_snippet_docstrings(snippets)`, they may be serialized from or +deserialized into the snippets. Both functions accept a table structsured like +this: `{ft1={snippets}, ft2={snippets}}`. Such a table containing all snippets +can be obtained via `ls.get_snippets()`. `load` should be called before any of +the `loader`-functions as snippets loaded from VSCode style packages already +have their `docstring` set (`docstrings` wouldn’t be overwritten, but +there’d be unnecessary calls). + +The cache is located at `stdpath("cache")/luasnip/docstrings.json` (probably +`~/.cache/nvim/luasnip/docstrings.json`). + + +============================================================================== +25. Events *luasnip-events* + +Events can be used to react to some action inside snippets. These callbacks can +be defined per snippet (`callbacks`-key in snippet constructor), per-node by +passing them as `node_callbacks` in `node_opts`, or globally (autocommand). + +`callbacks`: `fn(node[, event_args]) -> event_res` All callbacks receive the +`node` associated with the event and event-specific optional arguments, +`event_args`. `event_res` is only used in one event, `pre_expand`, where some +properties of the snippet can be changed. If multiple callbacks return +`event_res`, we only guarantee that one of them will be effective, not all of +them. + +`autocommand`: Luasnip uses `User`-events. Autocommands for these can be +registered using + +>vim + au User SomeUserEvent echom "SomeUserEvent was triggered" +< + +or + +>lua + vim.api.nvim_create_autocommand("User", { + patter = "SomeUserEvent", + command = "echom SomeUserEvent was triggered" + }) +< + +The node and `event_args` can be accessed through `require("luasnip").session`: + +- `node`: `session.event_node` +- `event_args`: `session.event_args` + +**Events**: + +- `enter/leave`: Called when a node is entered/left (for example when jumping + around in a snippet). + `User-event`: `"Luasnip{Enter,Leave}"`, with `` in + PascalCase, e.g. `InsertNode` or `DynamicNode`. + `event_args`: none +- `change_choice`: When the active choice in a choiceNode is changed. + `User-event`: `"LuasnipChangeChoice"` + `event_args`: none +- `pre_expand`: Called before a snippet is expanded. Modifying text is allowed, + the expand-position will be adjusted so the snippet expands at the same + position relative to existing text. + `User-event`: `"LuasnipPreExpand"` + `event_args`: + - `expand_pos`: `{, }`, position at which the snippet will be + expanded. `` and `` are both 0-indexed. + `event_res`: + - `env_override`: `map string->(string[]|string)`, override or extend the + snippet’s environment (`snip.env`). + +A pretty useless, beyond serving as an example here, application of these would +be printing e.g. the node’s text after entering: + +>lua + vim.api.nvim_create_autocmd("User", { + pattern = "LuasnipInsertNodeEnter", + callback = function() + local node = require("luasnip").session.event_node + print(table.concat(node:get_text(), "\n")) + end + }) +< + +or some information about expansions: + +>lua + vim.api.nvim_create_autocmd("User", { + pattern = "LuasnipPreExpand", + callback = function() + -- get event-parameters from `session`. + local snippet = require("luasnip").session.event_node + local expand_position = + require("luasnip").session.event_args.expand_pos + + print(string.format("expanding snippet %s at %s:%s", + table.concat(snippet:get_docstring(), "\n"), + expand_position[1], + expand_position[2] + )) + end + }) +< + + +============================================================================== +26. Cleanup *luasnip-cleanup* + +The function ls.cleanup() triggers the `LuasnipCleanup` user event, that you +can listen to do some kind of cleaning in your own snippets; by default it will +empty the snippets table and the caches of the lazy_load. + + +============================================================================== +27. Logging *luasnip-logging* + +Luasnip uses logging to report unexpected program states, and information on +what’s going on in general. If something does not work as expected, taking a +look at the log (and potentially increasing the loglevel) might give some good +hints towards what is going wrong. + +The log is stored in `/luasnip.log` +(`/luasnip.log` for Neovim versions where +`stdpath("log")` does not exist), and can be opened by calling `ls.log.open()`. +The loglevel (granularity of reported events) can be adjusted by calling +`ls.log.set_loglevel("error"|"warn"|"info"|"debug")`. `"debug"` has the highest +granularity, `"error"` the lowest, the default is `"warn"`. + +Once this log grows too large (10MiB, currently not adjustable), it will be +renamed to `luasnip.log.old`, and a new, empty log created in its place. If +there already exists a `luasnip.log.old`, it will be deleted. + +`ls.log.ping()` can be used to verify the log is working correctly: it will +print a short message to the log. + + +============================================================================== +28. Source *luasnip-source* + +It is possible to attach, to a snippet, information about its source. This can +be done either by the various loaders (if it is enabled in `ls.setup` +(|luasnip-config-options|, `loaders_store_source`)), or manually. The attached +data can be used by |luasnip-extras-snippet-location| to jump to the definition +of a snippet. + +It is also possible to get/set the source of a snippet via API: + +`ls.snippet_source`: + +- `get(snippet) -> source_data`: + Retrieve the source-data of `snippet`. `source_data` always contains the key + `file`, the file in which the snippet was defined, and may additionally + contain `line` or `line_end`, the first and last line of the definition. +- `set(snippet, source)`: + Set the source of a snippet. + - `snippet`: a snippet which was added via `ls.add_snippets`. + - `source`: a `source`-object, obtained from either `from_debuginfo` or + `from_location`. +- `from_location(file, opts) -> source`: + - `file`: `string`, The path to the file in which the snippet is defined. + - `opts`: `table|nil`, optional parameters for the source. + - `line`: `number`, the first line of the definition. 1-indexed. + - `line_end`: `number`, the final line of the definition. 1-indexed. +- `from_debuginfo(debuginfo) -> source`: + Generates source from the table returned by `debug.getinfo` (from now on + referred to as `debuginfo`). `debuginfo` has to be of a frame of a function + which is backed by a file, and has to contain this information, ie. has to be + generated by `debug.get_info(*, "Sl")` (at least `"Sl"`, it may also contain + more info). + + +============================================================================== +29. Config-Options *luasnip-config-options* + +These are the settings you can provide to `luasnip.setup()`: + +- `keep_roots`: Whether snippet-roots should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `link_roots`: Whether snippet-roots should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `exit_roots`: Whether snippet-roots should exit at reaching at their last node, + `$0`. This setting is only valid for root snippets, not child snippets. This + setting may avoid unexpected behavior by disallowing to jump earlier (finished) + snippets. Check |luasnip-basics-snippet-insertion| for more information on + snippet-roots. +- `link_children`: Whether children should be linked. See + |luasnip-basics-snippet-insertion| for more context. +- `history` (deprecated): if not nil, `keep_roots`, `link_roots`, and + `link_children` will be set to the value of `history`, and `exit_roots` will + set to inverse value of `history`. This is just to ensure + backwards-compatibility. +- `update_events`: Choose which events trigger an update of the active nodes’ + dependents. Default is just `'InsertLeave'`, `'TextChanged,TextChangedI'` would + update on every change. These, like all other `*_events` are passed to + `nvim_create_autocmd` as `events`, so they can be wrapped in a table, like + >lua + ls.setup({ + update_events = {"TextChanged", "TextChangedI"} + }) + < +- `region_check_events`: Events on which to leave the current snippet-root if the + cursor is outside its’ 'region'. Disabled by default, `'CursorMoved'`, + `'CursorHold'` or `'InsertEnter'` seem reasonable. +- `delete_check_events`: When to check if the current snippet was deleted, and if + so, remove it from the history. Off by default, `'TextChanged'` (perhaps + `'InsertLeave'`, to react to changes done in Insert mode) should work just fine + (alternatively, this can also be mapped using `luasnip-delete-check`). +- `store_selection_keys`: Mapping for populating `TM_SELECTED_TEXT` and related + variables (not set by default). If you want to set this mapping yourself, map + `ls.select_keys` (not a function, actually a string/key-combination) as a rhs. +- `enable_autosnippets`: Autosnippets are disabled by default to minimize + performance penalty if unused. Set to `true` to enable. +- `ext_opts`: Additional options passed to extmarks. Can be used to add + passive/active highlight on a per-node-basis (more info in DOC.md) +- `parser_nested_assembler`: Override the default behaviour of inserting a + `choiceNode` containing the nested snippet and an empty `insertNode` for nested + placeholders (`"${1: ${2: this is nested}}"`). For an example (behaviour more + similar to vscode), check here + +- `ft_func`: Source of possible filetypes for snippets. Defaults to a function, + which returns `vim.split(vim.bo.filetype, ".", true)`, but check + filetype_functions or the + |luasnip-extras-filetype-functions|-section for more options. +- `load_ft_func`: Function to determine which filetypes belong to a given buffer + (used for `lazy_loading`). `fn(bufnr) -> filetypes (string[])`. Again, there + are some examples in filetype_functions + . +- `snip_env`: The best way to author snippets in lua involves the lua-loader (see + |luasnip-loaders-lua|). Unfortunately, this requires that snippets are defined + in separate files, which means that common definitions like `s`, `i`, `sn`, + `t`, `fmt`, … have to be repeated in each of them, and that adding more + customized functions to ease writing snippets also requires some setup. + `snip_env` can be used to insert variables into exactly the places where + lua-snippets are defined (for now only the file loaded by the lua-loader). + Setting `snip_env` to `{ some_global = "a value" }` will add (amongst the + defaults stated at the beginning of this documentation) the global variable + `some_global` while evaluating these files. There are special keys which, when + set in `snip_env` change the behaviour of this option, and are not passed + through to the lua-files: + - `__snip_env_behaviour`, string: either `"set"` or `"extend"` (default + `"extend"`) + If this is `"extend"`, the variables defined in `snip_env` will complement (and + override) the defaults. If this is not desired, `"set"` will not include the + defaults, but only the variables set here. + One side-effect of this is that analysis-tools (most likely + `lua-language-server`) for lua will generate diagnostics for the usage of + undefined symbols. If you mind the (probably) large number of generated + warnings, consider adding the undefined globals to the globals recognized by + `lua-language-server` or add `---@diagnostic disable: undefined-global` + somewhere in the affected files. +- `loaders_store_source`, boolean, whether loaders should store the source of the + loaded snippets. Enabling this means that the definition of any snippet can be + jumped to via |luasnip-extras-snippet-location|, but also entails slightly + increased memory consumption (and load-time, but it’s not really noticeable). + + +============================================================================== +30. Troubleshooting *luasnip-troubleshooting* + + +ADDING SNIPPETS *luasnip-troubleshooting-adding-snippets* + +### Loaders + +- **Filetypes**. LuaSnip uses `all` as the global filetype. As most snippet + collections don’t explicitly target LuaSnip, they may not provide global + snippets for this filetype, but another, like `_` (`honza/vim-snippets`). In + these cases, it’s necessary to extend LuaSnip’s global filetype with the + collection’s global filetype: + >lua + ls.filetype_extend("all", { "_" }) + < + In general, if some snippets don’t show up when loading a collection, a good + first step is checking the filetype LuaSnip is actually looking into (print + them for the current buffer via `:lua + print(vim.inspect(require("luasnip").get_snippet_filetypes()))`), against the + one the missing snippet is provided for (in the collection). If there is indeed + a mismatch, `filetype_extend` can be used to also search the collection’s + filetype: + >lua + ls.filetype_extend("", { "" }) + < +- **Non-default ft_func loading**. As we only load `lazy_load`ed snippets on some + events, `lazy_load` will probably not play nice when a non-default `ft_func` is + used: if it depends on e.g. the cursor position, only the filetypes for the + cursor position when the `lazy_load` events are triggered will be loaded. Check + |luasnip-extras-filetype-function|’s `extend_load_ft` for a solution. + + +GENERAL ~ + +- **Snippets sharing triggers**. If multiple snippets could be triggered at the + current buffer-position, the snippet that was defined first in one’s + configuration will be expanded first. As a small, real-world LaTeX math + example, given the following two snippets with triggers `.ov` and `ov`: + >lua + postfix( -- Insert over-line command to text via post-fix + { trig = ".ov", snippetType = "autosnippet" }, + { + f(function(_, parent) + return "\\overline{" .. parent.snippet.env.POSTFIX_MATCH .. "}" + end, {}), + } + ), + s( -- Insert over-line command + { trig = "ov", snippetType="autosnippet" }, + fmt( + [[\overline{<>}]], + { i(1) }, + { delimiters = "<>" } + ) + ), + < + If one types `x` followed by `.ov`, the postfix snippet expands producing + `\overline{x}`. However, if the `postfix` snippet above is defined _after_ the + normal snippet `s`, then the same key press sequence produces `x.\overline{}`. + This behaviour can be overridden by explicitly providing a priority to such + snippets. For example, in the above code, if the `postfix` snippet was defined + after the normal snippet `s`, then adding `priority=1001` to the `postfix` + snippet will cause it to expand as if it were defined before the normal snippet + `s`. Snippet `priority` is discussed in the Snippets section + of the + documentation. + + +============================================================================== +31. API *luasnip-api* + +`require("luasnip")`: + +- `add_snippets(ft:string or nil, snippets:list or table, opts:table or nil)`: + Makes `snippets` (list of snippets) available in `ft`. If `ft` is `nil`, + `snippets` should be a table containing lists of snippets, the keys are + corresponding filetypes. `opts` may contain the following keys: + - `type`: type of `snippets`, `"snippets"` or `"autosnippets"` (ATTENTION: + plural form used here). This serves as default value for the `snippetType` + key of each snippet added by this call see |luasnip-snippets|. + - `key`: Key that identifies snippets added via this call. + If `add_snippets` is called with a key that was already used, the snippets + from that previous call will be removed. + This can be used to reload snippets: pass an unique key to each + `add_snippets` and just redo the `add_snippets`-call when the snippets have + changed. + - `override_priority`: set priority for all snippets. + - `default_priority`: set priority only for snippets without snippet priority. +- `clean_invalidated(opts: table or nil) -> bool`: clean invalidated snippets + from internal snippet storage. Invalidated snippets are still stored; it might + be useful to actually remove them as they still have to be iterated during + expansion. + `opts` may contain: + - `inv_limit`: how many invalidated snippets are allowed. If the number of + invalid snippets doesn’t exceed this threshold, they are not yet cleaned up. + A small number of invalidated snippets (<100) probably doesn’t affect runtime + at all, whereas recreating the internal snippet storage might. +- `get_id_snippet(id)`: returns snippet corresponding to id. +- `in_snippet()`: returns true if the cursor is inside the current snippet. +- `jumpable(direction)`: returns true if the current node has a next(`direction` + = 1) or previous(`direction` = -1), e.g. whether it’s possible to jump + forward or backward to another node. +- `jump(direction)`: returns true if the jump was successful. +- `expandable()`: true if a snippet can be expanded at the current cursor + position. +- `expand(opts)`: expands the snippet at(before) the cursor. `opts` may contain: + - `jump_into_func` passed through to `ls.snip_expand`, check its’ doc for a + description. +- `expand_or_jumpable()`: returns `expandable() or jumpable(1)` (exists only + because commonly, one key is used to both jump forward and expand). +- `expand_or_locally_jumpable()`: same as `expand_or_jumpable()` except jumpable + is ignored if the cursor is not inside the current snippet. +- `locally_jumpable(direction)`: same as `jumpable()` except it is ignored if the + cursor is not inside the current snippet. +- `expand_or_jump()`: returns true if jump/expand was succesful. +- `expand_auto()`: expands the autosnippets before the cursor (not necessary to + call manually, will be called via autocmd if `enable_autosnippets` is set in + the config). +- `snip_expand(snip, opts)`: expand `snip` at the current cursor position. `opts` + may contain the following keys: + - `clear_region`: A region of text to clear after expanding (but before jumping + into) snip. It has to be at this point (and therefore passed to this function) + as clearing before expansion will populate `TM_CURRENT_LINE` and + `TM_CURRENT_WORD` with wrong values (they would miss the snippet trigger) and + clearing after expansion may move the text currently under the cursor and have + it end up not at the `i(1)`, but a `#trigger` chars to its right. The actual + values used for clearing are `from` and `to`, both (0,0)-indexed + byte-positions. If the variables don’t have to be populated with the correct + values, it’s safe to remove the text manually. + - `expand_params`: table, override `trigger`, `captures` or environment of the + snippet. This is useful for manually expanding snippets where the trigger + passed via `trig` is not the text triggering the snippet, or those which expect + `captures` (basically, snippets with a non-plaintext `trigEngine`). + One example: ```lua snip_expand(snip, { trigger = "override_trigger", captures + = {"first capture", "second capture"}, env_override = { this_key = "some + value", other_key = {"multiple", "lines"}, TM_FILENAME = + "some_other_filename.lua" } }) + - `pos`: position (`{line, col}`), (0,0)-indexed (in bytes, as returned by + `nvim_win_get_cursor()`), where the snippet should be expanded. The snippet + will be put between `(line,col-1)` and `(line,col)`. The snippet will be + expanded at the current cursor if pos is nil. + - `jump_into_func`: fn(snippet) -> node: Callback responsible for jumping into + the snippet. The returned node is set as the new active node, ie. it is the + origin of the next jump. The default is basically this: `lua function(snip) -- + jump_into set the placeholder of the snippet, 1 -- to jump forwards. return + snip:jump_into(1)` while this can be used to only insert the snippet: `lua + function(snip) return snip.insert_nodes[0] end` + - `indent`: bool?, defaults to `true`. Whether LuaSnip will try to add additional + indents to fit current indent level in snippet expanding. This option is useful + when some LSP server already take indents into consideration. In such cases, + LuaSnip should not try to add additional indents. If you are using `nvim-cmp`, + sample config: + >lua + require("cmp").setup { + snippet = { + expand = function(args) + local indent_nodes = true + if vim.api.nvim_get_option_value("filetype", { buf = 0 }) == "dart" then + indent_nodes = false + end + require("luasnip").lsp_expand(args.body, { + indent = indent_nodes, + }) + end, + }, + } + < + `opts` and any of its parameters may be nil. +- `get_active_snip()`: returns the currently active snippet (not node!). +- `choice_active()`: true if inside a choiceNode. +- `change_choice(direction)`: changes the choice in the innermost currently + active choiceNode forward (`direction` = 1) or backward (`direction` = -1). +- `unlink_current()`: removes the current snippet from the jumplist (useful if + luasnip fails to automatically detect e.g. deletion of a snippet) and sets the + current node behind the snippet, or, if not possible, before it. +- `lsp_expand(snip_string, opts)`: expands the LSP snippet defined via + `snip_string` at the cursor. `opts` can have the same options as `opts` in + `snip_expand`. +- `active_update_dependents()`: update all function/dynamicNodes that have the + current node as an argnode (will actually only update them if the text in any + of the argnodes changed). +- `available(snip_info)`: returns a table of all snippets defined for the current + filetypes(s) (`{ft1={snip1, snip2}, ft2={snip3, snip4}}`). The structure of the + snippet is defined by `snip_info` which is a function (`snip_info(snip)`) that + takes in a snippet (`snip`), finds the desired information on it, and returns + it. `snip_info` is an optional argument as a default has already been defined. + You can use it for more granular control over the table of snippets that is + returned. +- `exit_out_of_region(node)`: checks whether the cursor is still within the range + of the root-snippet `node` belongs to. If yes, no change occurs; if no, the + root-snippet is exited and its `$0` will be the new active node. If a jump + causes an error (happens mostly because the text of a snippet was deleted), the + snippet is removed from the jumplist and the current node set to the + end/beginning of the next/previous snippet. +- `store_snippet_docstrings(snippet_table)`: Stores the docstrings of all + snippets in `snippet_table` to a file + (`stdpath("cache")/luasnip/docstrings.json`). Calling + `store_snippet_docstrings(snippet_table)` after adding/modifying snippets and + `load_snippet_docstrings(snippet_table)` on startup after all snippets have + been added to `snippet_table` is a way to avoide regenerating the (unchanged) + docstrings on each startup. (Depending on when the docstrings are required and + how luasnip is loaded, it may be more sensible to let them load lazily, + e.g. just before they are required). `snippet_table` should be laid out just + like `luasnip.snippets` (it will most likely always _be_ `luasnip.snippets`). +- `load_snippet_docstrings(snippet_table)`: Load docstrings for all snippets in + `snippet_table` from `stdpath("cache")/luasnip/docstrings.json`. The docstrings + are stored and restored via trigger, meaning if two snippets for one filetype + have the same (very unlikely to happen in actual usage), bugs could occur. + `snippet_table` should be laid out as described in `store_snippet_docstrings`. +- `unlink_current_if_deleted()`: Checks if the current snippet was deleted; if + so, it is removed from the jumplist. This is not 100% reliable as LuaSnip only + sees the extmarks and their beginning/end may not be on the same position, even + if all the text between them was deleted. +- `filetype_extend(filetype:string, extend_filetypes:table of string)`: Tells + luasnip that for a buffer with `ft=filetype`, snippets from `extend_filetypes` + should be searched as well. `extend_filetypes` is a lua-array (`{ft1, ft2, + ft3}`). `luasnip.filetype_extend("lua", {"c", "cpp"})` would search and expand + c and cpp snippets for lua files. +- `filetype_set(filetype:string, replace_filetypes:table of string)`: Similar to + `filetype_extend`, but where _append_ appended filetypes, _set_ sets them: + `filetype_set("lua", {"c"})` causes only c snippets to be expanded in lua + files; lua snippets aren’t even searched. +- `cleanup()`: clears all snippets. Not useful for regular usage, only when + authoring and testing snippets. +- `refresh_notify(ft:string)`: Triggers an autocmd that other plugins can hook + into to perform various cleanup for the refreshed filetype. Useful for + signaling that new snippets were added for the filetype `ft`. +- `set_choice(indx:number)`: Changes to the `indx`th choice. If no `choiceNode` + is active, an error is thrown. If the active `choiceNode` doesn’t have an + `indx`th choice, an error is thrown. +- `get_current_choices() -> string[]`: Returns a list of multiline-strings + (themselves lists, even if they have only one line), the `i`th string + corresponding to the `i`th choice of the currently active `choiceNode`. If no + `choiceNode` is active, an error is thrown. +- `setup_snip_env()`: Adds the variables defined (during `setup`) in `snip_env` + to the callers environment. +- `get_snip_env()`: Returns `snip_env`. +- `jump_destination(direction)`: Returns the node the next jump in `direction` + (either -1 or 1, for backwards, forwards respectively) leads to, or `nil` if + the destination could not be determined (most likely because there is no node + that can be jumped to in the given direction, or there is no active node). +- `activate_node(opts)`: Activate a node in any snippet. `opts` contains the + following options: + - `pos`, `{[1]: row, [2]: byte-column}?`: The position at which a node should + be activated. Defaults to the position of the cursor. + - `strict`, `bool?`: If set, throw an error if the node under the cursor can’t + be jumped into. If not set, fall back to any node of the snippet and enter + that instead. + - `select`, `bool?`: Whether the text inside the node should be selected. + Defaults to true. + +Not covered in this section are the various node-constructors exposed by the +module, their usage is shown either previously in this file or in +`Examples/snippets.lua` (in the repo). + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/ftplugin/snippets.vim b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/ftplugin/snippets.vim new file mode 100644 index 00000000..b13cc8af --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/ftplugin/snippets.vim @@ -0,0 +1,14 @@ +" Vim filetype plugin for SnipMate snippets (.snippets files) + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setl et< sts< cms< fdm< fde<" + +" Use hard tabs +setlocal noexpandtab softtabstop=0 + +setlocal commentstring=#\ %s +setlocal nospell diff --git a/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/luasnip-2.3.0-1.rockspec b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/luasnip-2.3.0-1.rockspec new file mode 100644 index 00000000..a392685b --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/luasnip-2.3.0-1.rockspec @@ -0,0 +1,37 @@ +local git_ref = 'v2.3.0' +local modrev = '2.3.0' +local specrev = '1' + +local repo_url = 'https://github.com/L3MON4D3/LuaSnip' + +rockspec_format = '3.0' +package = 'luasnip' +version = modrev ..'-'.. specrev + +description = { + summary = 'Snippet Engine for Neovim written in Lua.', + detailed = '', + labels = { 'lua', 'neovim', 'snippet-engine', 'snippets' } , + homepage = 'https://github.com/L3MON4D3/LuaSnip', + license = 'Apache-2.0' +} + +dependencies = { 'lua >= 5.1', 'jsregexp >= 0.0.5, <= 0.0.7' } + +test_dependencies = { } + +source = { + url = repo_url .. '/archive/' .. git_ref .. '.zip', + dir = 'LuaSnip-' .. '2.3.0', +} + +if modrev == 'scm' or modrev == 'dev' then + source = { + url = repo_url:gsub('https', 'git') + } +end + +build = { + type = 'builtin', + copy_directories = { 'doc', 'ftplugin', 'plugin', 'syntax' } , +} diff --git a/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/plugin/luasnip.lua b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/plugin/luasnip.lua new file mode 100644 index 00000000..420e4c0a --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/plugin/luasnip.lua @@ -0,0 +1,90 @@ +vim.filetype.add({ + extension = { snippets = "snippets" }, +}) + +local function silent_map(mode, lhs, rhs, desc) + vim.keymap.set(mode, lhs, rhs, { silent = true, desc = desc or "" }) +end + +silent_map("i", "luasnip-expand-or-jump", function() + require("luasnip").expand_or_jump() +end, "LuaSnip: Expand or jump in the current snippet") +silent_map("i", "luasnip-expand-snippet", function() + require("luasnip").expand() +end, "LuaSnip: Expand the current snippet") +silent_map("i", "luasnip-next-choice", function() + require("luasnip").change_choice(1) +end, "LuaSnip: Change to the next choice from the choiceNode") +silent_map("i", "luasnip-prev-choice", function() + require("luasnip").change_choice(-1) +end, "LuaSnip: Change to the previous choice from the choiceNode") +silent_map("i", "luasnip-jump-next", function() + require("luasnip").jump(1) +end, "LuaSnip: Jump to the next node") +silent_map("i", "luasnip-jump-prev", function() + require("luasnip").jump(-1) +end, "LuaSnip: Jump to the previous node") + +silent_map("n", "luasnip-delete-check", function() + require("luasnip").unlink_current_if_deleted() +end, "LuaSnip: Removes current snippet from jumplist") +silent_map("!", "luasnip-delete-check", function() + require("luasnip").unlink_current_if_deleted() +end, "LuaSnip: Removes current snippet from jumplist") + +silent_map("", "luasnip-expand-repeat", function() + require("luasnip").expand_repeat() +end, "LuaSnip: Repeat last node expansion") +silent_map("!", "luasnip-expand-repeat", function() + require("luasnip").expand_repeat() +end, "LuaSnip: Repeat last node expansion") + +silent_map("s", "luasnip-expand-or-jump", function() + require("luasnip").expand_or_jump() +end, "LuaSnip: Expand or jump in the current snippet") +silent_map("s", "luasnip-expand-snippet", function() + require("luasnip").expand() +end, "LuaSnip: Expand the current snippet") +silent_map("s", "luasnip-next-choice", function() + require("luasnip").change_choice(1) +end, "LuaSnip: Change to the next choice from the choiceNode") +silent_map("s", "luasnip-prev-choice", function() + require("luasnip").change_choice(-1) +end, "LuaSnip: Change to the previous choice from the choiceNode") +silent_map("s", "luasnip-jump-next", function() + require("luasnip").jump(1) +end, "LuaSnip: Jump to the next node") +silent_map("s", "luasnip-jump-prev", function() + require("luasnip").jump(-1) +end, "LuaSnip: Jump to the previous node") + +vim.api.nvim_create_user_command("LuaSnipUnlinkCurrent", function() + require("luasnip").unlink_current() +end, { force = true }) + +--stylua: ignore +vim.api.nvim_create_user_command("LuaSnipListAvailable", function() + ( + ( + vim.version + and type(vim.version) == "table" + and ( + ((vim.version().major == 0) and (vim.version().minor >= 9)) + or (vim.version().major > 0) ) + ) and vim.print + or vim.pretty_print + )(require("luasnip").available()) +end, { force = true }) + +require("luasnip.config")._setup() + +-- register these during startup so lazy_load will also load filetypes whose +-- events fired only before lazy_load is actually called. +-- (BufWinEnter -> lazy_load() wouldn't load any files without these). +vim.api.nvim_create_augroup("_luasnip_lazy_load", {}) +vim.api.nvim_create_autocmd({ "BufWinEnter", "FileType" }, { + callback = function(event) + require("luasnip.loaders").load_lazy_loaded(tonumber(event.buf)) + end, + group = "_luasnip_lazy_load", +}) diff --git a/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/plugin/luasnip.vim b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/plugin/luasnip.vim new file mode 100644 index 00000000..d8628ea7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/plugin/luasnip.vim @@ -0,0 +1,23 @@ +function! luasnip#expandable() + return luaeval('require("luasnip").expandable()') +endfunction + +function! luasnip#expand_or_jumpable() + return luaeval('require("luasnip").expand_or_jumpable()') +endfunction + +function! luasnip#expand_or_locally_jumpable() + return luaeval('require("luasnip").expand_or_locally_jumpable()') +endfunction + +function! luasnip#locally_jumpable(direction) + return luaeval('require("luasnip").locally_jumpable(_A)', a:direction) +endfunction + +function! luasnip#jumpable(direction) + return luaeval('require("luasnip").jumpable(_A)', a:direction) +endfunction + +function! luasnip#choice_active() + return luaeval('require("luasnip").choice_active()') +endfunction diff --git a/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/rock_manifest b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/rock_manifest new file mode 100644 index 00000000..2ed6b589 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/rock_manifest @@ -0,0 +1,116 @@ +rock_manifest = { + doc = { + ["luasnip.txt"] = "5014cf9a2bb7c7360fd34a1a02356207" + }, + ftplugin = { + ["snippets.vim"] = "ecd77fcf256c792985acf12ea7603322" + }, + lua = { + luasnip = { + ["_types.lua"] = "a1b1fc45d496f8ece3e17dc3541e5f93", + ["config.lua"] = "1bb0edf593b14b243b116d70cbb605c9", + ["default_config.lua"] = "51eea9c217eed18af81d580129c70461", + extras = { + ["_extra_types.lua"] = "b8f4a120d5abe22f0112efdcae358817", + ["_lambda.lua"] = "e94a2ad0606ed3c4276a573d4e7ab205", + ["_parser_combinator.lua"] = "bacc166557d1b5f9f03aff25a56bc801", + ["_treesitter.lua"] = "d9fb19599b9d95edab033fdda0684c32", + conditions = { + ["expand.lua"] = "35c3ab55ec8e9916ed7cde31cc807b08", + ["init.lua"] = "12f7e4b6fd6b5796c36ce61db5844efd", + ["show.lua"] = "0cd4059f6ba5582f409ced580e9fef13" + }, + ["expand_conditions.lua"] = "6ea7479cea2e5fac95a2045a6a283d4b", + ["filetype_functions.lua"] = "bdab365ff7bd2d7d148fdc6b3b78d9b4", + ["fmt.lua"] = "014768af82d3e7e58437e41335553eb6", + ["init.lua"] = "560335e3043e97a826fc8aee4b250fbc", + ["otf.lua"] = "8a95cdb7b582497542069bdd0886776b", + ["postfix.lua"] = "5e94359e6642b52d8ef6c9df3a90e167", + ["select_choice.lua"] = "8c924f05ee0d55ab9b0d9e5c603e1a52", + ["snip_location.lua"] = "bd0f8a7f1c61f6a001fa5781c15839d5", + ["snippet_list.lua"] = "fe61183934e0bb966b83461febdd1dcb", + ["treesitter_postfix.lua"] = "42a5143ad3c647d292b2183566fd6776" + }, + ["health.lua"] = "b6bd288f728f6897674347ad46917a5b", + ["init.lua"] = "96451aae98dbaf3ece53873298479172", + loaders = { + ["data.lua"] = "498490d7dfcf2f0374b0d20f429ba6fb", + ["from_lua.lua"] = "78d20ec3694e16581e21ed4948c26385", + ["from_snipmate.lua"] = "93e1cdc6e024549d9aa6bc917325de24", + ["from_vscode.lua"] = "0ff819764a09a735f9ea8ef51413ae83", + ["fs_watchers.lua"] = "b36b9f60988b568602350c41b032f9e6", + ["init.lua"] = "d470bc3c7bd4690199cf1c0d214782cf", + ["snippet_cache.lua"] = "e2b5cf9a46713fb3f108067100e77e0c", + ["types.lua"] = "89e18f0f21c1e77be74c1cbe85757d11", + ["util.lua"] = "77a85743643bf4d15cbe91af429908d5" + }, + nodes = { + ["absolute_indexer.lua"] = "efa73978bd91f2d90f2fc9ef53a9c38c", + ["choiceNode.lua"] = "c63618056997ec5aec6524fffff7f2fb", + ["duplicate.lua"] = "454e20ad45dbf371aa7d09aa21861f1c", + ["dynamicNode.lua"] = "28f4e7a46281dc3a2af0875ffc5ff58c", + ["functionNode.lua"] = "cf7cb4efb677a139618fd9255734873e", + ["insertNode.lua"] = "a25a723746e7ab5973901855de1d1f11", + ["key_indexer.lua"] = "d1c4887dfc10501f09b7851aea25f842", + ["multiSnippet.lua"] = "2eab1e75c5ee87096f03db006da31844", + ["node.lua"] = "c1d2f45dd25dcf5c1574ff63e0f9e88c", + ["restoreNode.lua"] = "9613ce23458968aa12737365dd302be7", + ["snippet.lua"] = "d6a31a62f45a460bc642822b6d0244f7", + ["snippetProxy.lua"] = "68262858f0f9a20a41640d5a11c43481", + ["textNode.lua"] = "c22395ab8305a581f021982cd88e2931", + util = { + ["trig_engines.lua"] = "a023c5ca92103478cbf40b7ffe2de903" + }, + ["util.lua"] = "a6be1172f1b37f2018460900b0ab987d" + }, + session = { + ["enqueueable_operations.lua"] = "2e4f57314f0573601e35943f56e8d4d8", + ["init.lua"] = "213d2ea8110e267278d62f5853151ceb", + snippet_collection = { + ["init.lua"] = "2d5015eb7cb5717f5aa44fdeebffbe59", + ["source.lua"] = "17f2f0c590d4deb57ae0e7af20c153ec" + } + }, + ["snippets.lua"] = "d41d8cd98f00b204e9800998ecf8427e", + util = { + ["_builtin_vars.lua"] = "cb7e73099c5711556f8df8821ca4a182", + ["auto_table.lua"] = "f9c5f84a99e71df229c4b6506a447727", + ["dict.lua"] = "83d98b784cfe6ab28c1d3727e7220110", + ["directed_graph.lua"] = "7eb06677cf726e6be7d64d470660677c", + ["environ.lua"] = "61b0b01947a335f749e854f039ec77ac", + ["events.lua"] = "cdac0c08202f1295a0bd9f5ee5909b3b", + ["ext_opts.lua"] = "55f3ee33870b070d50c3eae516b4724a", + ["extend_decorator.lua"] = "07576b8535b2729c9d70f5ba5b036a92", + ["functions.lua"] = "86ccff508ce6b6eeefc455308e7d4994", + ["jsonc.lua"] = "94fbde2a919a24f3957d004aaf7d136d", + ["jsregexp.lua"] = "59eb40a43fa328e82b086863dcbfa626", + ["lazy_table.lua"] = "7b0f31805982e74c3e693fd60ad42ec2", + ["log.lua"] = "ffe073da229ae489cc72e576c0ab6bee", + ["mark.lua"] = "135f7a32a6f1031ea0eb80688997f3d3", + parser = { + ["ast_parser.lua"] = "230087c74af6009d8a858259808f3e51", + ["ast_utils.lua"] = "7013bc099f5ed408c4cd49b29e4ce63c", + ["init.lua"] = "5ae80471a9893a45b12b77a35ecc8d81", + ["neovim_ast.lua"] = "08e136ffd26023ef3172ec2aed4ad2e9", + ["neovim_parser.lua"] = "c25f144947bceed6036e3d40b70bdef0" + }, + ["path.lua"] = "3767ba134238fa42469cfcbcfdf16147", + ["pattern_tokenizer.lua"] = "f4f99d27e6a6fb5385f583abc70beaab", + ["select.lua"] = "b0a8180922f7995a86ea9df7eabb162e", + ["str.lua"] = "06645f5bc876c73af9c4fd3296d620e0", + ["table.lua"] = "f4a54a5775133c776d65643be728cfdb", + ["time.lua"] = "54483e160266c85209e4399fbfc43e1b", + ["types.lua"] = "6605cc2d2293f7080d104c63663c0dac", + ["util.lua"] = "ac9ec42f0d014d908ff24c5af8172335" + } + } + }, + ["luasnip-2.3.0-1.rockspec"] = "51f9eecc66d3003eb668326f1e4a0e68", + plugin = { + ["luasnip.lua"] = "189a598faa80a372be83a52dc57fb491", + ["luasnip.vim"] = "e3d30107f8659679f6766d579ce5bf56" + }, + syntax = { + ["snippets.vim"] = "5ea760f9406519fc824e2c941ef4d858" + } +} diff --git a/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/syntax/snippets.vim b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/syntax/snippets.vim new file mode 100644 index 00000000..dcb39dd9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/luasnip/2.3.0-1/syntax/snippets.vim @@ -0,0 +1,29 @@ +" Syntax highlighting for .snippets files +" Hopefully this should make snippets a bit nicer to write! +syn match snipComment '^#.*' +syn match placeHolder '\${\d\+\(:.\{-}\)\=}' contains=snipCommand +syn match tabStop '\$\d\+' +syn match snipEscape '\\\\\|\\`' +syn match snipCommand '\%(\\\@ cover common +" cases with \t and " ". +syn match snipError "^[^#vsaep\t ].*$" + +hi link snippet Identifier +hi link snipComment Comment +hi link multiSnipText String +hi link snipKeyword Keyword +hi link snipEscape SpecialChar +hi link placeHolder Special +hi link tabStop Special +hi link snipCommand String +hi link snipError Error +hi link priority Number diff --git a/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/manifest b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/manifest new file mode 100644 index 00000000..b57e717f --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1-unstable-2024-05-24-rocks/manifest @@ -0,0 +1,363 @@ +commands = {} +dependencies = { + jsregexp = {}, + luasnip = { + ["2.3.0-1"] = { + { + constraints = { + { + op = ">=", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + }, + { + constraints = { + { + op = ">=", + version = { + 0, 0, 5, string = "0.0.5" + } + }, + { + op = "<=", + version = { + 0, 0, 7, string = "0.0.7" + } + } + }, + name = "jsregexp" + } + } + } +} +modules = { + ["luasnip._types"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.config"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.default_config"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras._extra_types"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras._lambda"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras._parser_combinator"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras._treesitter"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.conditions.expand"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.conditions.init"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.conditions.show"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.expand_conditions"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.filetype_functions"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.fmt"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.init"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.otf"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.postfix"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.select_choice"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.snip_location"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.snippet_list"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.extras.treesitter_postfix"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.health"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.init"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.loaders.data"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.loaders.from_lua"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.loaders.from_snipmate"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.loaders.from_vscode"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.loaders.fs_watchers"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.loaders.init"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.loaders.snippet_cache"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.loaders.types"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.loaders.util"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.absolute_indexer"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.choiceNode"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.duplicate"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.dynamicNode"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.functionNode"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.insertNode"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.key_indexer"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.multiSnippet"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.node"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.restoreNode"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.snippet"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.snippetProxy"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.textNode"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.util"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.nodes.util.trig_engines"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.session.enqueueable_operations"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.session.init"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.session.snippet_collection.init"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.session.snippet_collection.source"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.snippets"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util._builtin_vars"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.auto_table"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.dict"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.directed_graph"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.environ"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.events"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.ext_opts"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.extend_decorator"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.functions"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.jsonc"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.jsregexp"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.lazy_table"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.log"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.mark"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.parser.ast_parser"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.parser.ast_utils"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.parser.init"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.parser.neovim_ast"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.parser.neovim_parser"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.path"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.pattern_tokenizer"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.select"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.str"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.table"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.time"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.types"] = { + "luasnip/2.3.0-1" + }, + ["luasnip.util.util"] = { + "luasnip/2.3.0-1" + } +} +repository = { + luasnip = { + ["2.3.0-1"] = { + { + arch = "installed", + commands = {}, + dependencies = {}, + modules = { + ["luasnip._types"] = "luasnip/_types.lua", + ["luasnip.config"] = "luasnip/config.lua", + ["luasnip.default_config"] = "luasnip/default_config.lua", + ["luasnip.extras._extra_types"] = "luasnip/extras/_extra_types.lua", + ["luasnip.extras._lambda"] = "luasnip/extras/_lambda.lua", + ["luasnip.extras._parser_combinator"] = "luasnip/extras/_parser_combinator.lua", + ["luasnip.extras._treesitter"] = "luasnip/extras/_treesitter.lua", + ["luasnip.extras.conditions.expand"] = "luasnip/extras/conditions/expand.lua", + ["luasnip.extras.conditions.init"] = "luasnip/extras/conditions/init.lua", + ["luasnip.extras.conditions.show"] = "luasnip/extras/conditions/show.lua", + ["luasnip.extras.expand_conditions"] = "luasnip/extras/expand_conditions.lua", + ["luasnip.extras.filetype_functions"] = "luasnip/extras/filetype_functions.lua", + ["luasnip.extras.fmt"] = "luasnip/extras/fmt.lua", + ["luasnip.extras.init"] = "luasnip/extras/init.lua", + ["luasnip.extras.otf"] = "luasnip/extras/otf.lua", + ["luasnip.extras.postfix"] = "luasnip/extras/postfix.lua", + ["luasnip.extras.select_choice"] = "luasnip/extras/select_choice.lua", + ["luasnip.extras.snip_location"] = "luasnip/extras/snip_location.lua", + ["luasnip.extras.snippet_list"] = "luasnip/extras/snippet_list.lua", + ["luasnip.extras.treesitter_postfix"] = "luasnip/extras/treesitter_postfix.lua", + ["luasnip.health"] = "luasnip/health.lua", + ["luasnip.init"] = "luasnip/init.lua", + ["luasnip.loaders.data"] = "luasnip/loaders/data.lua", + ["luasnip.loaders.from_lua"] = "luasnip/loaders/from_lua.lua", + ["luasnip.loaders.from_snipmate"] = "luasnip/loaders/from_snipmate.lua", + ["luasnip.loaders.from_vscode"] = "luasnip/loaders/from_vscode.lua", + ["luasnip.loaders.fs_watchers"] = "luasnip/loaders/fs_watchers.lua", + ["luasnip.loaders.init"] = "luasnip/loaders/init.lua", + ["luasnip.loaders.snippet_cache"] = "luasnip/loaders/snippet_cache.lua", + ["luasnip.loaders.types"] = "luasnip/loaders/types.lua", + ["luasnip.loaders.util"] = "luasnip/loaders/util.lua", + ["luasnip.nodes.absolute_indexer"] = "luasnip/nodes/absolute_indexer.lua", + ["luasnip.nodes.choiceNode"] = "luasnip/nodes/choiceNode.lua", + ["luasnip.nodes.duplicate"] = "luasnip/nodes/duplicate.lua", + ["luasnip.nodes.dynamicNode"] = "luasnip/nodes/dynamicNode.lua", + ["luasnip.nodes.functionNode"] = "luasnip/nodes/functionNode.lua", + ["luasnip.nodes.insertNode"] = "luasnip/nodes/insertNode.lua", + ["luasnip.nodes.key_indexer"] = "luasnip/nodes/key_indexer.lua", + ["luasnip.nodes.multiSnippet"] = "luasnip/nodes/multiSnippet.lua", + ["luasnip.nodes.node"] = "luasnip/nodes/node.lua", + ["luasnip.nodes.restoreNode"] = "luasnip/nodes/restoreNode.lua", + ["luasnip.nodes.snippet"] = "luasnip/nodes/snippet.lua", + ["luasnip.nodes.snippetProxy"] = "luasnip/nodes/snippetProxy.lua", + ["luasnip.nodes.textNode"] = "luasnip/nodes/textNode.lua", + ["luasnip.nodes.util"] = "luasnip/nodes/util.lua", + ["luasnip.nodes.util.trig_engines"] = "luasnip/nodes/util/trig_engines.lua", + ["luasnip.session.enqueueable_operations"] = "luasnip/session/enqueueable_operations.lua", + ["luasnip.session.init"] = "luasnip/session/init.lua", + ["luasnip.session.snippet_collection.init"] = "luasnip/session/snippet_collection/init.lua", + ["luasnip.session.snippet_collection.source"] = "luasnip/session/snippet_collection/source.lua", + ["luasnip.snippets"] = "luasnip/snippets.lua", + ["luasnip.util._builtin_vars"] = "luasnip/util/_builtin_vars.lua", + ["luasnip.util.auto_table"] = "luasnip/util/auto_table.lua", + ["luasnip.util.dict"] = "luasnip/util/dict.lua", + ["luasnip.util.directed_graph"] = "luasnip/util/directed_graph.lua", + ["luasnip.util.environ"] = "luasnip/util/environ.lua", + ["luasnip.util.events"] = "luasnip/util/events.lua", + ["luasnip.util.ext_opts"] = "luasnip/util/ext_opts.lua", + ["luasnip.util.extend_decorator"] = "luasnip/util/extend_decorator.lua", + ["luasnip.util.functions"] = "luasnip/util/functions.lua", + ["luasnip.util.jsonc"] = "luasnip/util/jsonc.lua", + ["luasnip.util.jsregexp"] = "luasnip/util/jsregexp.lua", + ["luasnip.util.lazy_table"] = "luasnip/util/lazy_table.lua", + ["luasnip.util.log"] = "luasnip/util/log.lua", + ["luasnip.util.mark"] = "luasnip/util/mark.lua", + ["luasnip.util.parser.ast_parser"] = "luasnip/util/parser/ast_parser.lua", + ["luasnip.util.parser.ast_utils"] = "luasnip/util/parser/ast_utils.lua", + ["luasnip.util.parser.init"] = "luasnip/util/parser/init.lua", + ["luasnip.util.parser.neovim_ast"] = "luasnip/util/parser/neovim_ast.lua", + ["luasnip.util.parser.neovim_parser"] = "luasnip/util/parser/neovim_parser.lua", + ["luasnip.util.path"] = "luasnip/util/path.lua", + ["luasnip.util.pattern_tokenizer"] = "luasnip/util/pattern_tokenizer.lua", + ["luasnip.util.select"] = "luasnip/util/select.lua", + ["luasnip.util.str"] = "luasnip/util/str.lua", + ["luasnip.util.table"] = "luasnip/util/table.lua", + ["luasnip.util.time"] = "luasnip/util/time.lua", + ["luasnip.util.types"] = "luasnip/util/types.lua", + ["luasnip.util.util"] = "luasnip/util/util.lua" + } + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1.rockspec b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1.rockspec new file mode 100644 index 00000000..a392685b --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/luasnip-2.3.0-1.rockspec @@ -0,0 +1,37 @@ +local git_ref = 'v2.3.0' +local modrev = '2.3.0' +local specrev = '1' + +local repo_url = 'https://github.com/L3MON4D3/LuaSnip' + +rockspec_format = '3.0' +package = 'luasnip' +version = modrev ..'-'.. specrev + +description = { + summary = 'Snippet Engine for Neovim written in Lua.', + detailed = '', + labels = { 'lua', 'neovim', 'snippet-engine', 'snippets' } , + homepage = 'https://github.com/L3MON4D3/LuaSnip', + license = 'Apache-2.0' +} + +dependencies = { 'lua >= 5.1', 'jsregexp >= 0.0.5, <= 0.0.7' } + +test_dependencies = { } + +source = { + url = repo_url .. '/archive/' .. git_ref .. '.zip', + dir = 'LuaSnip-' .. '2.3.0', +} + +if modrev == 'scm' or modrev == 'dev' then + source = { + url = repo_url:gsub('https', 'git') + } +end + +build = { + type = 'builtin', + copy_directories = { 'doc', 'ftplugin', 'plugin', 'syntax' } , +} diff --git a/config/neovim/store/lazy-plugins/luasnip/nix-support/propagated-build-inputs b/config/neovim/store/lazy-plugins/luasnip/nix-support/propagated-build-inputs new file mode 100644 index 00000000..d8abd799 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/nix-support/propagated-build-inputs @@ -0,0 +1 @@ +/nix/store/qhhsw7qjik5gh1wkai703p6dq0ydp15r-lua5.1-jsregexp-0.0.7-1 /nix/store/mqbhz05llkddfb5wni0m48kw22ixxps4-lua-5.1.5 \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/luasnip/plugin/luasnip.lua b/config/neovim/store/lazy-plugins/luasnip/plugin/luasnip.lua new file mode 100644 index 00000000..420e4c0a --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/plugin/luasnip.lua @@ -0,0 +1,90 @@ +vim.filetype.add({ + extension = { snippets = "snippets" }, +}) + +local function silent_map(mode, lhs, rhs, desc) + vim.keymap.set(mode, lhs, rhs, { silent = true, desc = desc or "" }) +end + +silent_map("i", "luasnip-expand-or-jump", function() + require("luasnip").expand_or_jump() +end, "LuaSnip: Expand or jump in the current snippet") +silent_map("i", "luasnip-expand-snippet", function() + require("luasnip").expand() +end, "LuaSnip: Expand the current snippet") +silent_map("i", "luasnip-next-choice", function() + require("luasnip").change_choice(1) +end, "LuaSnip: Change to the next choice from the choiceNode") +silent_map("i", "luasnip-prev-choice", function() + require("luasnip").change_choice(-1) +end, "LuaSnip: Change to the previous choice from the choiceNode") +silent_map("i", "luasnip-jump-next", function() + require("luasnip").jump(1) +end, "LuaSnip: Jump to the next node") +silent_map("i", "luasnip-jump-prev", function() + require("luasnip").jump(-1) +end, "LuaSnip: Jump to the previous node") + +silent_map("n", "luasnip-delete-check", function() + require("luasnip").unlink_current_if_deleted() +end, "LuaSnip: Removes current snippet from jumplist") +silent_map("!", "luasnip-delete-check", function() + require("luasnip").unlink_current_if_deleted() +end, "LuaSnip: Removes current snippet from jumplist") + +silent_map("", "luasnip-expand-repeat", function() + require("luasnip").expand_repeat() +end, "LuaSnip: Repeat last node expansion") +silent_map("!", "luasnip-expand-repeat", function() + require("luasnip").expand_repeat() +end, "LuaSnip: Repeat last node expansion") + +silent_map("s", "luasnip-expand-or-jump", function() + require("luasnip").expand_or_jump() +end, "LuaSnip: Expand or jump in the current snippet") +silent_map("s", "luasnip-expand-snippet", function() + require("luasnip").expand() +end, "LuaSnip: Expand the current snippet") +silent_map("s", "luasnip-next-choice", function() + require("luasnip").change_choice(1) +end, "LuaSnip: Change to the next choice from the choiceNode") +silent_map("s", "luasnip-prev-choice", function() + require("luasnip").change_choice(-1) +end, "LuaSnip: Change to the previous choice from the choiceNode") +silent_map("s", "luasnip-jump-next", function() + require("luasnip").jump(1) +end, "LuaSnip: Jump to the next node") +silent_map("s", "luasnip-jump-prev", function() + require("luasnip").jump(-1) +end, "LuaSnip: Jump to the previous node") + +vim.api.nvim_create_user_command("LuaSnipUnlinkCurrent", function() + require("luasnip").unlink_current() +end, { force = true }) + +--stylua: ignore +vim.api.nvim_create_user_command("LuaSnipListAvailable", function() + ( + ( + vim.version + and type(vim.version) == "table" + and ( + ((vim.version().major == 0) and (vim.version().minor >= 9)) + or (vim.version().major > 0) ) + ) and vim.print + or vim.pretty_print + )(require("luasnip").available()) +end, { force = true }) + +require("luasnip.config")._setup() + +-- register these during startup so lazy_load will also load filetypes whose +-- events fired only before lazy_load is actually called. +-- (BufWinEnter -> lazy_load() wouldn't load any files without these). +vim.api.nvim_create_augroup("_luasnip_lazy_load", {}) +vim.api.nvim_create_autocmd({ "BufWinEnter", "FileType" }, { + callback = function(event) + require("luasnip.loaders").load_lazy_loaded(tonumber(event.buf)) + end, + group = "_luasnip_lazy_load", +}) diff --git a/config/neovim/store/lazy-plugins/luasnip/plugin/luasnip.vim b/config/neovim/store/lazy-plugins/luasnip/plugin/luasnip.vim new file mode 100644 index 00000000..d8628ea7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/plugin/luasnip.vim @@ -0,0 +1,23 @@ +function! luasnip#expandable() + return luaeval('require("luasnip").expandable()') +endfunction + +function! luasnip#expand_or_jumpable() + return luaeval('require("luasnip").expand_or_jumpable()') +endfunction + +function! luasnip#expand_or_locally_jumpable() + return luaeval('require("luasnip").expand_or_locally_jumpable()') +endfunction + +function! luasnip#locally_jumpable(direction) + return luaeval('require("luasnip").locally_jumpable(_A)', a:direction) +endfunction + +function! luasnip#jumpable(direction) + return luaeval('require("luasnip").jumpable(_A)', a:direction) +endfunction + +function! luasnip#choice_active() + return luaeval('require("luasnip").choice_active()') +endfunction diff --git a/config/neovim/store/lazy-plugins/luasnip/rock_manifest b/config/neovim/store/lazy-plugins/luasnip/rock_manifest new file mode 100644 index 00000000..2ed6b589 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/rock_manifest @@ -0,0 +1,116 @@ +rock_manifest = { + doc = { + ["luasnip.txt"] = "5014cf9a2bb7c7360fd34a1a02356207" + }, + ftplugin = { + ["snippets.vim"] = "ecd77fcf256c792985acf12ea7603322" + }, + lua = { + luasnip = { + ["_types.lua"] = "a1b1fc45d496f8ece3e17dc3541e5f93", + ["config.lua"] = "1bb0edf593b14b243b116d70cbb605c9", + ["default_config.lua"] = "51eea9c217eed18af81d580129c70461", + extras = { + ["_extra_types.lua"] = "b8f4a120d5abe22f0112efdcae358817", + ["_lambda.lua"] = "e94a2ad0606ed3c4276a573d4e7ab205", + ["_parser_combinator.lua"] = "bacc166557d1b5f9f03aff25a56bc801", + ["_treesitter.lua"] = "d9fb19599b9d95edab033fdda0684c32", + conditions = { + ["expand.lua"] = "35c3ab55ec8e9916ed7cde31cc807b08", + ["init.lua"] = "12f7e4b6fd6b5796c36ce61db5844efd", + ["show.lua"] = "0cd4059f6ba5582f409ced580e9fef13" + }, + ["expand_conditions.lua"] = "6ea7479cea2e5fac95a2045a6a283d4b", + ["filetype_functions.lua"] = "bdab365ff7bd2d7d148fdc6b3b78d9b4", + ["fmt.lua"] = "014768af82d3e7e58437e41335553eb6", + ["init.lua"] = "560335e3043e97a826fc8aee4b250fbc", + ["otf.lua"] = "8a95cdb7b582497542069bdd0886776b", + ["postfix.lua"] = "5e94359e6642b52d8ef6c9df3a90e167", + ["select_choice.lua"] = "8c924f05ee0d55ab9b0d9e5c603e1a52", + ["snip_location.lua"] = "bd0f8a7f1c61f6a001fa5781c15839d5", + ["snippet_list.lua"] = "fe61183934e0bb966b83461febdd1dcb", + ["treesitter_postfix.lua"] = "42a5143ad3c647d292b2183566fd6776" + }, + ["health.lua"] = "b6bd288f728f6897674347ad46917a5b", + ["init.lua"] = "96451aae98dbaf3ece53873298479172", + loaders = { + ["data.lua"] = "498490d7dfcf2f0374b0d20f429ba6fb", + ["from_lua.lua"] = "78d20ec3694e16581e21ed4948c26385", + ["from_snipmate.lua"] = "93e1cdc6e024549d9aa6bc917325de24", + ["from_vscode.lua"] = "0ff819764a09a735f9ea8ef51413ae83", + ["fs_watchers.lua"] = "b36b9f60988b568602350c41b032f9e6", + ["init.lua"] = "d470bc3c7bd4690199cf1c0d214782cf", + ["snippet_cache.lua"] = "e2b5cf9a46713fb3f108067100e77e0c", + ["types.lua"] = "89e18f0f21c1e77be74c1cbe85757d11", + ["util.lua"] = "77a85743643bf4d15cbe91af429908d5" + }, + nodes = { + ["absolute_indexer.lua"] = "efa73978bd91f2d90f2fc9ef53a9c38c", + ["choiceNode.lua"] = "c63618056997ec5aec6524fffff7f2fb", + ["duplicate.lua"] = "454e20ad45dbf371aa7d09aa21861f1c", + ["dynamicNode.lua"] = "28f4e7a46281dc3a2af0875ffc5ff58c", + ["functionNode.lua"] = "cf7cb4efb677a139618fd9255734873e", + ["insertNode.lua"] = "a25a723746e7ab5973901855de1d1f11", + ["key_indexer.lua"] = "d1c4887dfc10501f09b7851aea25f842", + ["multiSnippet.lua"] = "2eab1e75c5ee87096f03db006da31844", + ["node.lua"] = "c1d2f45dd25dcf5c1574ff63e0f9e88c", + ["restoreNode.lua"] = "9613ce23458968aa12737365dd302be7", + ["snippet.lua"] = "d6a31a62f45a460bc642822b6d0244f7", + ["snippetProxy.lua"] = "68262858f0f9a20a41640d5a11c43481", + ["textNode.lua"] = "c22395ab8305a581f021982cd88e2931", + util = { + ["trig_engines.lua"] = "a023c5ca92103478cbf40b7ffe2de903" + }, + ["util.lua"] = "a6be1172f1b37f2018460900b0ab987d" + }, + session = { + ["enqueueable_operations.lua"] = "2e4f57314f0573601e35943f56e8d4d8", + ["init.lua"] = "213d2ea8110e267278d62f5853151ceb", + snippet_collection = { + ["init.lua"] = "2d5015eb7cb5717f5aa44fdeebffbe59", + ["source.lua"] = "17f2f0c590d4deb57ae0e7af20c153ec" + } + }, + ["snippets.lua"] = "d41d8cd98f00b204e9800998ecf8427e", + util = { + ["_builtin_vars.lua"] = "cb7e73099c5711556f8df8821ca4a182", + ["auto_table.lua"] = "f9c5f84a99e71df229c4b6506a447727", + ["dict.lua"] = "83d98b784cfe6ab28c1d3727e7220110", + ["directed_graph.lua"] = "7eb06677cf726e6be7d64d470660677c", + ["environ.lua"] = "61b0b01947a335f749e854f039ec77ac", + ["events.lua"] = "cdac0c08202f1295a0bd9f5ee5909b3b", + ["ext_opts.lua"] = "55f3ee33870b070d50c3eae516b4724a", + ["extend_decorator.lua"] = "07576b8535b2729c9d70f5ba5b036a92", + ["functions.lua"] = "86ccff508ce6b6eeefc455308e7d4994", + ["jsonc.lua"] = "94fbde2a919a24f3957d004aaf7d136d", + ["jsregexp.lua"] = "59eb40a43fa328e82b086863dcbfa626", + ["lazy_table.lua"] = "7b0f31805982e74c3e693fd60ad42ec2", + ["log.lua"] = "ffe073da229ae489cc72e576c0ab6bee", + ["mark.lua"] = "135f7a32a6f1031ea0eb80688997f3d3", + parser = { + ["ast_parser.lua"] = "230087c74af6009d8a858259808f3e51", + ["ast_utils.lua"] = "7013bc099f5ed408c4cd49b29e4ce63c", + ["init.lua"] = "5ae80471a9893a45b12b77a35ecc8d81", + ["neovim_ast.lua"] = "08e136ffd26023ef3172ec2aed4ad2e9", + ["neovim_parser.lua"] = "c25f144947bceed6036e3d40b70bdef0" + }, + ["path.lua"] = "3767ba134238fa42469cfcbcfdf16147", + ["pattern_tokenizer.lua"] = "f4f99d27e6a6fb5385f583abc70beaab", + ["select.lua"] = "b0a8180922f7995a86ea9df7eabb162e", + ["str.lua"] = "06645f5bc876c73af9c4fd3296d620e0", + ["table.lua"] = "f4a54a5775133c776d65643be728cfdb", + ["time.lua"] = "54483e160266c85209e4399fbfc43e1b", + ["types.lua"] = "6605cc2d2293f7080d104c63663c0dac", + ["util.lua"] = "ac9ec42f0d014d908ff24c5af8172335" + } + } + }, + ["luasnip-2.3.0-1.rockspec"] = "51f9eecc66d3003eb668326f1e4a0e68", + plugin = { + ["luasnip.lua"] = "189a598faa80a372be83a52dc57fb491", + ["luasnip.vim"] = "e3d30107f8659679f6766d579ce5bf56" + }, + syntax = { + ["snippets.vim"] = "5ea760f9406519fc824e2c941ef4d858" + } +} diff --git a/config/neovim/store/lazy-plugins/luasnip/syntax/snippets.vim b/config/neovim/store/lazy-plugins/luasnip/syntax/snippets.vim new file mode 100644 index 00000000..dcb39dd9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/luasnip/syntax/snippets.vim @@ -0,0 +1,29 @@ +" Syntax highlighting for .snippets files +" Hopefully this should make snippets a bit nicer to write! +syn match snipComment '^#.*' +syn match placeHolder '\${\d\+\(:.\{-}\)\=}' contains=snipCommand +syn match tabStop '\$\d\+' +syn match snipEscape '\\\\\|\\`' +syn match snipCommand '\%(\\\@ cover common +" cases with \t and " ". +syn match snipError "^[^#vsaep\t ].*$" + +hi link snippet Identifier +hi link snipComment Comment +hi link multiSnipText String +hi link snipKeyword Keyword +hi link snipEscape SpecialChar +hi link placeHolder Special +hi link tabStop Special +hi link snipCommand String +hi link snipError Error +hi link priority Number diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/.github/ISSUE_TEMPLATE/bug_report.yml b/config/neovim/store/lazy-plugins/neodev.nvim/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..ef6290c2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,89 @@ +name: Bug Report +description: File a bug/issue +title: "bug: " +labels: [bug] +body: + - type: markdown + attributes: + value: | + **Before** reporting an issue, make sure to read the [documentation](https://github.com/folke/neodev.nvim) and search [existing issues](https://github.com/folke/neodev.nvim/issues). Usage questions such as ***"How do I...?"*** belong in [Discussions](https://github.com/folke/neodev.nvim/discussions) and will be closed. + - type: checkboxes + attributes: + label: Did you check docs and existing issues? + description: Make sure you checked all of the below before submitting an issue + options: + - label: I have read all the neodev.nvim docs + required: true + - label: I have searched the existing issues of neodev.nvim + required: true + - label: I have searched the existing issues of plugins related to this issue + required: true + - type: input + attributes: + label: "Neovim version (nvim -v)" + placeholder: "0.8.0 commit db1b0ee3b30f" + validations: + required: true + - type: input + attributes: + label: "Operating system/version" + placeholder: "MacOS 11.5" + validations: + required: true + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. Please include any related errors you see in Neovim. + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. + 2. + 3. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Repro + description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua` + value: | + -- DO NOT change the paths and don't remove the colorscheme + local root = vim.fn.fnamemodify("./.repro", ":p") + + -- set stdpaths to use .repro + for _, name in ipairs({ "config", "data", "state", "cache" }) do + vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name + end + + -- bootstrap lazy + local lazypath = root .. "/plugins/lazy.nvim" + if not vim.loop.fs_stat(lazypath) then + vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath, }) + end + vim.opt.runtimepath:prepend(lazypath) + + -- install plugins + local plugins = { + "folke/tokyonight.nvim", + "folke/neodev.nvim", + -- add any other plugins here + } + require("lazy").setup(plugins, { + root = root .. "/plugins", + }) + + vim.cmd.colorscheme("tokyonight") + -- add anything else here + render: Lua + validations: + required: false diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/.github/ISSUE_TEMPLATE/feature_request.yml b/config/neovim/store/lazy-plugins/neodev.nvim/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..501ab8e9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,36 @@ +name: Feature Request +description: Suggest a new feature +title: "feature: " +labels: [enhancement] +body: + - type: checkboxes + attributes: + label: Did you check the docs? + description: Make sure you read all the docs before submitting a feature request + options: + - label: I have read all the neodev.nvim docs + required: true + - type: textarea + validations: + required: true + attributes: + label: Is your feature request related to a problem? Please describe. + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + - type: textarea + validations: + required: true + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + - type: textarea + validations: + required: true + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + - type: textarea + validations: + required: false + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/neodev.nvim/.github/workflows/ci.yml new file mode 100644 index 00000000..b679f76b --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +name: CI +on: + push: + pull_request: + +jobs: + tests: + strategy: + matrix: + # os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Install Neovim + shell: bash + run: | + mkdir -p /tmp/nvim + wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage + cd /tmp/nvim + chmod a+x ./nvim.appimage + ./nvim.appimage --appimage-extract + echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + - name: Run Tests + run: | + nvim --version + [ ! -d tests ] && exit 0 + nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua', sequential = true}" + docs: + runs-on: ubuntu-latest + needs: tests + if: ${{ github.ref == 'refs/heads/main' }} + steps: + - uses: actions/checkout@v3 + - name: panvimdoc + uses: kdheepak/panvimdoc@main + with: + vimdoc: neodev.nvim + version: "Neovim >= 0.8.0" + demojify: true + treesitter: true + - name: Push changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "chore(build): auto-generate vimdoc" + commit_user_name: "github-actions[bot]" + commit_user_email: "github-actions[bot]@users.noreply.github.com" + commit_author: "github-actions[bot] " + release: + name: release + if: ${{ github.ref == 'refs/heads/main' }} + needs: + - docs + - tests + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v3 + id: release + with: + release-type: simple + package-name: neodev.nvim + - uses: actions/checkout@v3 + - name: tag stable versions + if: ${{ steps.release.outputs.release_created }} + run: | + git config user.name github-actions[bot] + git config user.email github-actions[bot]@users.noreply.github.com + git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" + git tag -d stable || true + git push origin :stable || true + git tag -a stable -m "Last Stable Release" + git push origin stable diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/.github/workflows/types.yml b/config/neovim/store/lazy-plugins/neodev.nvim/.github/workflows/types.yml new file mode 100644 index 00000000..15409950 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/.github/workflows/types.yml @@ -0,0 +1,64 @@ +name: Types +on: + workflow_dispatch: + schedule: + # Run this every hour + - cron: "0 * * * *" + +jobs: + build-types-nightly: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Setup ./data + run: mkdir ./data + - name: Install dependencies + run: | + sudo apt-get -y install doxygen luajit python3-msgpack + - name: Install Neovim + shell: bash + run: | + mkdir -p /tmp/nvim + wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage + cd /tmp/nvim + chmod a+x ./nvim.appimage + ./nvim.appimage --appimage-extract + echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + - name: Generate new docs + run: | + nvim --version + nvim -u NONE -E -R --headless --cmd "set rtp^=." --cmd "packloadall" --cmd "luafile lua/neodev/build/init.lua" --cmd q + - name: Push changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "chore: auto-generated types for Neovim nightly" + commit_user_name: "github-actions[bot]" + commit_user_email: "github-actions[bot]@users.noreply.github.com" + commit_author: "github-actions[bot] " + build-types-stable: + needs: build-types-nightly + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Pull Updates + run: git pull + - name: Install Neovim + shell: bash + run: | + mkdir -p /tmp/nvim + wget -q https://github.com/neovim/neovim/releases/download/stable/nvim.appimage -O /tmp/nvim/nvim.appimage + cd /tmp/nvim + chmod a+x ./nvim.appimage + ./nvim.appimage --appimage-extract + echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + - name: Generate new docs + run: | + nvim --version + nvim -u NONE -E -R --headless --cmd "set rtp^=." --cmd "packloadall" --cmd "luafile lua/neodev/build/init.lua" --cmd q + - name: Push changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "chore: auto-generated types for Neovim stable" + commit_user_name: "github-actions[bot]" + commit_user_email: "github-actions[bot]@users.noreply.github.com" + commit_author: "github-actions[bot] " diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/.gitignore b/config/neovim/store/lazy-plugins/neodev.nvim/.gitignore new file mode 100644 index 00000000..cc5457ab --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/.gitignore @@ -0,0 +1,8 @@ +tt.* +.tests +doc/tags +debug +.repro +foo.* +*.log +data diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/.neoconf.json b/config/neovim/store/lazy-plugins/neodev.nvim/.neoconf.json new file mode 100644 index 00000000..0690d1ad --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/.neoconf.json @@ -0,0 +1,18 @@ +{ + "neodev": { + "library": { + "enabled": true, + "plugins": ["neoconf.nvim", "nvim-lspconfig"] + } + }, + "neoconf": { + "plugins": { + "lua_ls": { + "enabled": true + } + } + }, + "lspconfig": { + "sumneko_lua": {} + } +} diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/BUILD.md b/config/neovim/store/lazy-plugins/neodev.nvim/BUILD.md new file mode 100644 index 00000000..41d744b4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/BUILD.md @@ -0,0 +1,11 @@ +# Updating Lua API Docs + +1. Clone the neovim repo somewhere on your system +2. Run `./scripts/gen_vimdoc.py` +3. Copy `neovim/runtime/doc/*.mpack` files to the **neodev.nvim** data directory +4. Open the file `neodev.nvim/lua/build/api.lua` in Neovim +5. Execute `:luafile %` +6. You'll see a lot of annotations that might be changed due to your local + system setup, so you can ignore those +7. Check if the changes you intended are present +8. Create a PR with your code changes, and **without** the new EmmyLua annotations diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/CHANGELOG.md b/config/neovim/store/lazy-plugins/neodev.nvim/CHANGELOG.md new file mode 100644 index 00000000..5191f76b --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/CHANGELOG.md @@ -0,0 +1,266 @@ +# Changelog + +## [2.5.2](https://github.com/folke/neodev.nvim/compare/v2.5.1...v2.5.2) (2023-03-24) + + +### Bug Fixes + +* parameter type for vim.fn.getline() ([#144](https://github.com/folke/neodev.nvim/issues/144)) ([ab56354](https://github.com/folke/neodev.nvim/commit/ab56354e0e85c3c3f20f770c699fb0038ce7bf42)) + +## [2.5.1](https://github.com/folke/neodev.nvim/compare/v2.5.0...v2.5.1) (2023-03-12) + + +### Bug Fixes + +* **lua_ls:** plugin name detection of symlinked plugins ([#140](https://github.com/folke/neodev.nvim/issues/140)) ([a60eaee](https://github.com/folke/neodev.nvim/commit/a60eaee19e0c3dcb7e54c64fe9bcfb71420a95c0)) + +## [2.5.0](https://github.com/folke/neodev.nvim/compare/v2.4.2...v2.5.0) (2023-03-10) + + +### Features + +* **luv:** use the luv addon instead of the deprecated 3rd party ([f06c113](https://github.com/folke/neodev.nvim/commit/f06c11344f76fadf2cd497b9490125dfc02946cb)) + +## [2.4.2](https://github.com/folke/neodev.nvim/compare/v2.4.1...v2.4.2) (2023-03-08) + + +### Bug Fixes + +* parameter types for searchpair and searchpairpos ([#130](https://github.com/folke/neodev.nvim/issues/130)) ([c87f69c](https://github.com/folke/neodev.nvim/commit/c87f69c856505ea778f6fd883a90397e55c8e59d)) + +## [2.4.1](https://github.com/folke/neodev.nvim/compare/v2.4.0...v2.4.1) (2023-02-28) + + +### Bug Fixes + +* parameter types for vim.wait and vim.fn.input ([#131](https://github.com/folke/neodev.nvim/issues/131)) ([55d3a74](https://github.com/folke/neodev.nvim/commit/55d3a747af136da527067e9fe59ad0bb938ecd50)) + +## [2.4.0](https://github.com/folke/neodev.nvim/compare/v2.3.0...v2.4.0) (2023-02-13) + + +### Features + +* added lua_ls luv builtin library. Fixes [#127](https://github.com/folke/neodev.nvim/issues/127) ([8c32d8b](https://github.com/folke/neodev.nvim/commit/8c32d8b4e765de26b27b76f0072e7c12038cde52)) + +## [2.3.0](https://github.com/folke/neodev.nvim/compare/v2.2.1...v2.3.0) (2023-02-13) + + +### Features + +* **luv:** more luv types ([39bb79b](https://github.com/folke/neodev.nvim/commit/39bb79b688300b0b4672ec83c564d72749482116)) + +## [2.2.1](https://github.com/folke/neodev.nvim/compare/v2.2.0...v2.2.1) (2023-02-12) + + +### Bug Fixes + +* s/sumneko_lua/lua_ls/ ([#124](https://github.com/folke/neodev.nvim/issues/124)) ([307d0fb](https://github.com/folke/neodev.nvim/commit/307d0fbce02068eebdaa4ef7da279fdb1bfe6d8e)) +* sumneko_lua -> lua_ls ([5076ebb](https://github.com/folke/neodev.nvim/commit/5076ebbcbfd0e2164d91ff2073a6f21a561804df)) + +## [2.2.0](https://github.com/folke/neodev.nvim/compare/v2.1.0...v2.2.0) (2023-02-10) + + +### Features + +* add more luv methods and types ([#122](https://github.com/folke/neodev.nvim/issues/122)) ([fc01efe](https://github.com/folke/neodev.nvim/commit/fc01efe11447a99808b06e362d95a296218fed68)) + +## [2.1.0](https://github.com/folke/neodev.nvim/compare/v2.0.1...v2.1.0) (2023-02-08) + + +### Features + +* Add `treesitter.query` and `treesitter.language` to stable ([#119](https://github.com/folke/neodev.nvim/issues/119)) ([4b6ade9](https://github.com/folke/neodev.nvim/commit/4b6ade9bb879aad4d1b2e94e82c36957262ead53)) +* add class to vim.api and vim.loop ([e27c03b](https://github.com/folke/neodev.nvim/commit/e27c03b3e312c08e2d4aeed2a0f76199a9729529)) + + +### Bug Fixes + +* handle cases where `vim.fn.stdpath("config")` does not exist. Fixes [#109](https://github.com/folke/neodev.nvim/issues/109) ([fc20483](https://github.com/folke/neodev.nvim/commit/fc20483383dac11b97df43c83db1bbbd35305172)) +* provide a class for vim.api ([#117](https://github.com/folke/neodev.nvim/issues/117)) ([9737bfc](https://github.com/folke/neodev.nvim/commit/9737bfc085cbb8879c19471a65b12fe9bd1ae878)) +* revert [#117](https://github.com/folke/neodev.nvim/issues/117) ([dd63031](https://github.com/folke/neodev.nvim/commit/dd630312eb978f554006a020554d64c197887644)) +* use integer for buffer, window, etc ([#116](https://github.com/folke/neodev.nvim/issues/116)) ([71e3e0c](https://github.com/folke/neodev.nvim/commit/71e3e0c2239593e29e06ecc725ba346e29d3186a)) + +## [2.0.1](https://github.com/folke/neodev.nvim/compare/v2.0.0...v2.0.1) (2023-01-15) + + +### Bug Fixes + +* ignore lua directory ([82c8586](https://github.com/folke/neodev.nvim/commit/82c85862e2aaa9c0b63f3176eb8dc513803f2865)) +* **lsp:** make sure everything works when lua root not found. Fixes [#108](https://github.com/folke/neodev.nvim/issues/108) ([31cba85](https://github.com/folke/neodev.nvim/commit/31cba8554a8e37ecf240fa2e242f0e43e72ae930)) + +## [2.0.0](https://github.com/folke/neodev.nvim/compare/v1.0.0...v2.0.0) (2023-01-11) + + +### ⚠ BREAKING CHANGES + +* `config.pathStrict` is now enabled by default. Needs sumneko >= 3.6.0. Much better performance + +### Features + +* **options:** allow setting an option to its type ([1569664](https://github.com/folke/neodev.nvim/commit/156966470d19a8b095f9ae620720be3fb85a3772)) + + +### Bug Fixes + +* simplified and improved plugin/config detection ([a34a9e7](https://github.com/folke/neodev.nvim/commit/a34a9e7e775f1513466940c31285292b7b8375de)) + + +### Performance Improvements + +* `config.pathStrict` is now enabled by default. Needs sumneko >= 3.6.0. Much better performance ([5ff32d4](https://github.com/folke/neodev.nvim/commit/5ff32d4d50491f94667733362a52d0fe178e4714)) + +## 1.0.0 (2023-01-04) + + +### ⚠ BREAKING CHANGES + +* removed config.plugin_library in favor of config.override for easier customization +* rewrite with easier setup and configuration. Backward compatible, but refer to the docs to use the optimized setup + +### Features + +* added [@meta](https://github.com/meta) to generated emmy-lua files ([97a3399](https://github.com/folke/neodev.nvim/commit/97a33996a1618bdc6647384fd605940c5adce743)) +* added [@see](https://github.com/see) for `seealso` from help docs ([1e8a2b4](https://github.com/folke/neodev.nvim/commit/1e8a2b4427a73cf3f0a51f42c52cc367e71f04f5)) +* added annotations for vim.filetype, vim.F, vim.fs, vim.health, vim.inspect ([2be3d3f](https://github.com/folke/neodev.nvim/commit/2be3d3ffc17609319090289561842a75dcbf5daf)) +* added config.runtime() for new vim.fn parser ([87adbea](https://github.com/folke/neodev.nvim/commit/87adbeafc4d2813447e6a75c8f209cb2d637b178)) +* added configuration option to set the runtime_path (slow). Fixes [#29](https://github.com/folke/neodev.nvim/issues/29) ([f7fecb7](https://github.com/folke/neodev.nvim/commit/f7fecb7deda6fe244b6a5b8edfca21128009baf5)) +* added docs for options and vim.fn functions ([4eb0e89](https://github.com/folke/neodev.nvim/commit/4eb0e894795251e5381c55ef87e7a0053ad0659c)) +* added function to check if a fiven fqname is a lua object ([3d1d469](https://github.com/folke/neodev.nvim/commit/3d1d4698a05be1099162812bf7893fb873f6a297)) +* added luv docs ([e38838a](https://github.com/folke/neodev.nvim/commit/e38838a558a5f80d6a9c8ad7d0044c884254463b)) +* added missing types ([76e7b48](https://github.com/folke/neodev.nvim/commit/76e7b48122a251b98057cc71823bdc50aa289016)) +* added option shortnames to vim.opt, o, go, bo, wo, opt_local, opt_global ([309eca5](https://github.com/folke/neodev.nvim/commit/309eca5584eba48d2b77f3668b3c5db3dee7e838)) +* added options ([9ec4a9c](https://github.com/folke/neodev.nvim/commit/9ec4a9c73b102163055d8d4e52edd833f9c22151)) +* added overrides for vim.fn.expand and vim.fn.sign_define. Fixes [#72](https://github.com/folke/neodev.nvim/issues/72) ([868db41](https://github.com/folke/neodev.nvim/commit/868db41830bae23c70b4ed044f64f60870bc8f37)) +* added overrides for vim.fn.glob ([43f51e9](https://github.com/folke/neodev.nvim/commit/43f51e9b86d1637644f0fbba5e1e11bef4341750)) +* added overrides for vim.loop.new_timer() ([d15370e](https://github.com/folke/neodev.nvim/commit/d15370ee520a169bf6224668ccca175489833948)) +* added possibility to define [@overload](https://github.com/overload) annotations ([a77f5db](https://github.com/folke/neodev.nvim/commit/a77f5dbfe2e972789c989e2f3909354b482705c0)) +* added possibility to override auto generated annotations ([9d6b75b](https://github.com/folke/neodev.nvim/commit/9d6b75ba119cf825fb92830188724e6f0f31e4ed)) +* added support for lazy.nvim ([6136979](https://github.com/folke/neodev.nvim/commit/61369790e4205b74f1667587b3dc5867716500eb)) +* added support for vim.opt methods. Fixes [#73](https://github.com/folke/neodev.nvim/issues/73) ([9e56a56](https://github.com/folke/neodev.nvim/commit/9e56a56301a297bf8d0c675349c101023a723c22)) +* added support for vimdoc code blocks ([6d8ce16](https://github.com/folke/neodev.nvim/commit/6d8ce1602a166c03695aaa976252bcb0fd49a7dc)) +* added types for vim.cmd.* ([9962a1d](https://github.com/folke/neodev.nvim/commit/9962a1dd0db41ab6745be2d9e4b3dc70d1aa188c)) +* added vim.diagnostic & vim.ui ([58796ba](https://github.com/folke/neodev.nvim/commit/58796ba3f0f7b3ab2197670998555f299cb3f471)) +* added vim.treesitter.highlighter ([5ca1178](https://github.com/folke/neodev.nvim/commit/5ca117883f37c64ee15f4d6a8ba8ca9a673bfffe)) +* added vim.treesitter.language ([8d2a950](https://github.com/folke/neodev.nvim/commit/8d2a950045450927d8f7652ef26548b28f3137c8)) +* added warnings on old-style setup ([17e73ce](https://github.com/folke/neodev.nvim/commit/17e73ce2f9616d5b967e9f3996f3c3b84912ef99)) +* added workspace support ([681c0fc](https://github.com/folke/neodev.nvim/commit/681c0fc46c49108ec4da9e190c7bfe0ec1393d83)) +* aded experimental option pathStrict. You need the latest sumneko nightly for this ([60f1ec9](https://github.com/folke/neodev.nvim/commit/60f1ec9d73c7f8fb5537050f38b8921698315e55)) +* allow config without using lspconfig. Fixes [#83](https://github.com/folke/neodev.nvim/issues/83) ([3b6b644](https://github.com/folke/neodev.nvim/commit/3b6b6442a036729d950f1b92924ac0f5827638ea)) +* better type infer ([6b9ae84](https://github.com/folke/neodev.nvim/commit/6b9ae848c4b804a9f9179abcc72ee38e824a2fd8)) +* debug log ([fdab800](https://github.com/folke/neodev.nvim/commit/fdab800ea2a9d4a62b67fe87109b937e6d85f5bf)) +* docs.fqn ([8f61232](https://github.com/folke/neodev.nvim/commit/8f61232f815e134efdf4cf06e1cd6342f4e369ab)) +* enable call snippets ([a267319](https://github.com/folke/neodev.nvim/commit/a26731909a6790ab80a4c7bc0c25b8d7402c35e9)) +* enable lua-dev by default for the Neovim runtime directory when needed ([0001b27](https://github.com/folke/neodev.nvim/commit/0001b27ddce0a1cc620d325577cba9f3b48f9cad)) +* fix indent of doc comments ([5abb32a](https://github.com/folke/neodev.nvim/commit/5abb32ab07a2e01726095dcc9bf224f874eeac69)) +* fixed detection of definitions for vim.keymap ([63bdf08](https://github.com/folke/neodev.nvim/commit/63bdf085b17ff55c938763ca1f6c346af119277c)) +* fixed option docs ([fb82dcc](https://github.com/folke/neodev.nvim/commit/fb82dcc58d7839a60a0da008049dc42c663bebae)) +* format code blocks as markdown ([e85a190](https://github.com/folke/neodev.nvim/commit/e85a19015ceb8498f6e80cb0094eb2dc45603bde)) +* full support of vim.go, wo, bo, o, opt, opt_local, opt_global ([261d29c](https://github.com/folke/neodev.nvim/commit/261d29c44328eb4b703e49ba4a6ae4e5297a96fc)) +* generated types for stable (Neovim 0.8.0) ([4366cad](https://github.com/folke/neodev.nvim/commit/4366cada45729a4224cbdf9b34f085b678c64796)) +* handle special Lua keywords ([142d456](https://github.com/folke/neodev.nvim/commit/142d456011e0a5df302e5cced535ecaa13be631a)) +* hidden option to make sumneko use a different neovim runtime for testing ([d55299f](https://github.com/folke/neodev.nvim/commit/d55299fea0655a416d94cb11badcd342ec54d7f4)) +* implemented optional parameters for lua api functions ([ee8e6af](https://github.com/folke/neodev.nvim/commit/ee8e6af506fc94763c59d336258de48b9f500988)) +* improved docs.functions ([c3a6a8c](https://github.com/folke/neodev.nvim/commit/c3a6a8c77af8dc04a064fe9b75d68bf3c56e4d4d)) +* improved vim.api and vim parsers ([60fce6d](https://github.com/folke/neodev.nvim/commit/60fce6dd0c60376f3a2d2253b314c6088ad067a9)) +* initial version ([26bd607](https://github.com/folke/neodev.nvim/commit/26bd607564940dc56575b792d799366a0da56c1f)) +* integrate with settings.nvim ([d95e691](https://github.com/folke/neodev.nvim/commit/d95e69166dbbef775140aecd02508db13c3606bb)) +* integration with neoconf.nvim ([77e8aec](https://github.com/folke/neodev.nvim/commit/77e8aec83549db4f575aa99cd8e2a88ac85db662)) +* keep original formatting when parsing docs ([6cf4af1](https://github.com/folke/neodev.nvim/commit/6cf4af1a026d63e676bddbdf8ad5b3fd726f1218)) +* lua-dev is now neodev ([b43d272](https://github.com/folke/neodev.nvim/commit/b43d2726829c0fe2d3950372e13177daaff14ea4)) +* lua-dev now properly sets up jsonls for .luarc.json, the sumneko local config files ([fb62007](https://github.com/folke/neodev.nvim/commit/fb620072c444404b2b55e9a0d8ba6d6f9e2dac69)) +* lua.txt doc parser ([0e7a16b](https://github.com/folke/neodev.nvim/commit/0e7a16b070c6be725573bc8cc2b0d69694cefc62)) +* merge defs of vim._editor in `vim` ([858bcd6](https://github.com/folke/neodev.nvim/commit/858bcd6840fa9578d14915f05fc15ca9b0959517)) +* merge in lspconfig options ([1f7ee38](https://github.com/folke/neodev.nvim/commit/1f7ee38d0f4e3972c3768e360979f5670aba2070)) +* more luv annotations ([b50621a](https://github.com/folke/neodev.nvim/commit/b50621a868004292372d91a03c88923b36659d99)) +* new options parser ([0c65251](https://github.com/folke/neodev.nvim/commit/0c65251ef6f93c795b6d44e339d97670a3500e8b)) +* new parser ([42e9c51](https://github.com/folke/neodev.nvim/commit/42e9c5126904d6715ece660224610f3a38e4b8d4)) +* new vim functions parser ([4d9ee97](https://github.com/folke/neodev.nvim/commit/4d9ee97048c6b541c363cc99dce24c3e77527631)) +* only load missing functions from mpack on nightly. hidden functions ([f0bf928](https://github.com/folke/neodev.nvim/commit/f0bf928719bf1b7eb0ee905e787c29e9a332e25f)) +* optionally supply a list of plugins to add to the workspace [#2](https://github.com/folke/neodev.nvim/issues/2) ([1fb6cc8](https://github.com/folke/neodev.nvim/commit/1fb6cc81ca98aab4d46ded1d39dcd29a44e48fcc)) +* parse luv return values from docs ([107b7d9](https://github.com/folke/neodev.nvim/commit/107b7d9413b8134a72ea838a624679f5a4e4fdf9)) +* proper typing for vim.g, vim.v, vim.b, vim.w and vim.t ([b16231e](https://github.com/folke/neodev.nvim/commit/b16231e7af55be112fcb867fd7b5eddf2993e9da)) +* properly configure runtime.path to make require work for plugins (slow). Fixes [#23](https://github.com/folke/neodev.nvim/issues/23) ([274f72b](https://github.com/folke/neodev.nvim/commit/274f72b6bc5c199f6d2d33956f9aa0603d4c3367)) +* properly parse optional params like (*opts) ([48cca93](https://github.com/folke/neodev.nvim/commit/48cca93b6d5da62db8da46c6d81b8fde2f4c8914)) +* removed config.plugin_library in favor of config.override for easier customization ([46a2eb0](https://github.com/folke/neodev.nvim/commit/46a2eb009062bef889dd05913f92c2c11a57b189)) +* replace code regions by markdown lua code blocks ([23df3f4](https://github.com/folke/neodev.nvim/commit/23df3f4f5403dbf24ccc1c8fc998db5298a0d377)) +* rewrite with easier setup and configuration. Backward compatible, but refer to the docs to use the optimized setup ([ae74bc9](https://github.com/folke/neodev.nvim/commit/ae74bc9987638da2e122aaaf155d6519f07e6197)) +* set correct default option ([e9e1da3](https://github.com/folke/neodev.nvim/commit/e9e1da34d8faa4843fd6d860bc12f552fe2e4a2a)) +* snippet option for setup ([#47](https://github.com/folke/neodev.nvim/issues/47)) ([8553f1c](https://github.com/folke/neodev.nvim/commit/8553f1c89aa53d2e9bb94b728bdf3ebd0abb625d)) +* types for nightly ([3e97b3e](https://github.com/folke/neodev.nvim/commit/3e97b3e87fae815863a6276e14af734102d28be8)) +* types for stable ([499bc32](https://github.com/folke/neodev.nvim/commit/499bc3286050ec5bf2332ee8c6cd726e10e75e6f)) +* updated all the docs for Neovim 0.6 ([1933c7e](https://github.com/folke/neodev.nvim/commit/1933c7e014e69484572b7fa1bf73bc51c42f10f4)) +* updated docs to 0.7 ([6063731](https://github.com/folke/neodev.nvim/commit/60637315d665652c59a6a4f99a82f2308d11dd8f)) +* updated emmy lua files ([5e6caa9](https://github.com/folke/neodev.nvim/commit/5e6caa92b83a07a516d481593344bed1e6eebd6a)) +* util.error ([c771c04](https://github.com/folke/neodev.nvim/commit/c771c040cb63cdbd2f01ae8698befb64011bbe93)) +* vim.fn functions ([b1e7776](https://github.com/folke/neodev.nvim/commit/b1e7776e4c7adeaa799002cb8ee25fecdc40f444)) +* vim.lua for nightly ([cae3dfa](https://github.com/folke/neodev.nvim/commit/cae3dfa0380c9a08a27c947f7de5ee719e087ff2)) +* vimdoc parser ([ac2a760](https://github.com/folke/neodev.nvim/commit/ac2a7601c8c5d2160512d74746286ea9f9580a09)) +* when param name is string, and unknown type, then use string ([ddb8162](https://github.com/folke/neodev.nvim/commit/ddb816294b62d7ab76ecbe0f26f2bfb8aef4a5e1)) + + +### Bug Fixes + +* add correct neovim config directory when pathstrict ([d20c601](https://github.com/folke/neodev.nvim/commit/d20c601836c05039926cbfec0b895a3028af70af)) +* add lua directories to workspace.library ([563e365](https://github.com/folke/neodev.nvim/commit/563e365b96ea5848e950e216929c08c69d3f4dda)) +* add vim itself to globals ([2c9311b](https://github.com/folke/neodev.nvim/commit/2c9311b5e45b2ad71843e7e9d49ce31f59cca630)) +* added .luarc.jsonc validation also to jsonls ([8e2f046](https://github.com/folke/neodev.nvim/commit/8e2f04638f9cb6c297d16c4feef85a6f7615fafb)) +* added # for parameter docs ([3c7dfda](https://github.com/folke/neodev.nvim/commit/3c7dfda68549b6152bc0aa3c04f81a87ee9af9f9)) +* added support for builtin libraries and meta/3rd libraries ([8d47e3a](https://github.com/folke/neodev.nvim/commit/8d47e3a488eb8a05f6699036f98f8a9a7f41776b)) +* added vim.log.levels ([4331626](https://github.com/folke/neodev.nvim/commit/4331626b02f636433b504b9ab6a8c11fb9de4a24)) +* alias `buffer` type ([#103](https://github.com/folke/neodev.nvim/issues/103)) ([23b58ff](https://github.com/folke/neodev.nvim/commit/23b58ff4e486d32fe9740dcdec0084ee419e1522)) +* append on_new_config instead of replacing it ([cff0972](https://github.com/folke/neodev.nvim/commit/cff09720d23f84fa4ae46005857d7c5d7a1a4844)) +* append to existing workspace.library instead of replacing ([b4bc26d](https://github.com/folke/neodev.nvim/commit/b4bc26d91ab17b84e8f15926c18b0c0abea3b690)) +* better detection of nvim config and plugins where root-dir is incorrect ([d6212c1](https://github.com/folke/neodev.nvim/commit/d6212c1527bb5fb4dbb593318cd937ad2d4d6eee)) +* better method of finding typed directory [#6](https://github.com/folke/neodev.nvim/issues/6) ([2e1a816](https://github.com/folke/neodev.nvim/commit/2e1a81633bab27e2535d1ccf5f47dda0ba49b0ce)) +* better package path ([03a44ec](https://github.com/folke/neodev.nvim/commit/03a44ec6a54b0a025a633978e8541584a02e46d9)) +* broke require path completion again ([9da602d](https://github.com/folke/neodev.nvim/commit/9da602d023ca1af73ac2ea3813798592c904566d)) +* correctly process global_local options ([2815a2a](https://github.com/folke/neodev.nvim/commit/2815a2acb5a0cd9a11aaadb8256af15c7bf7d513)) +* docs ([7b96f30](https://github.com/folke/neodev.nvim/commit/7b96f30ca6af998b4e3caa9c3886539e3ae83384)) +* **docs:** update sumneko_lua setup example ([#67](https://github.com/folke/neodev.nvim/issues/67)) ([8b2b228](https://github.com/folke/neodev.nvim/commit/8b2b228673a88baa9267cb4a9ceb5862b5ee5df4)) +* don't add arena parameters to functions ([99ee7f2](https://github.com/folke/neodev.nvim/commit/99ee7f21e07f9979b8cf14f24ce24d49aca11494)) +* don't include lua docs in generated docs. They are correctly set up through `types/vim.lua` ([130b35e](https://github.com/folke/neodev.nvim/commit/130b35e0f671c5729d106a4fe113129cb979e4b1)) +* don't skip anything from shared, otherwise builtin lua function docs are missing like notify ([24d8cf9](https://github.com/folke/neodev.nvim/commit/24d8cf99ccdaf8ef370f3f2165538fa296cd8122)) +* dont add root_dir to workspace library. Fixes [#21](https://github.com/folke/neodev.nvim/issues/21) ([e958850](https://github.com/folke/neodev.nvim/commit/e9588503e68fa32ac08b83d9cb7e42ec31b8907d)) +* fixed lua-dev schema for settings.nvim ([d6900dc](https://github.com/folke/neodev.nvim/commit/d6900dc94a40a3215d1a8debdaaa3036c3df17e5)) +* fixed requiring files and require path ([3c18da8](https://github.com/folke/neodev.nvim/commit/3c18da83e7d4e4199857721301b9ec52bd99e487)) +* get all the lua docs for internal lua functions ([6b0d9b0](https://github.com/folke/neodev.nvim/commit/6b0d9b0559defd9840402610824aeb9339ef1319)) +* improved emmy lua annotations ([4f5a10f](https://github.com/folke/neodev.nvim/commit/4f5a10f192c1fc2b080afc852a85403b1296da8e)) +* indentation from docs ([a4e5103](https://github.com/folke/neodev.nvim/commit/a4e5103fd91e9db6d9ac924df3fff94eaca8402a)) +* line context ([fbcdd6d](https://github.com/folke/neodev.nvim/commit/fbcdd6da18c124aa0f36a5981302ff29cd644115)) +* **luv:** also parse sync return values ([7b495ec](https://github.com/folke/neodev.nvim/commit/7b495ec693ad0bd42268f12f678fdeffa2f62b98)) +* make options generation deterministic ([136ec0e](https://github.com/folke/neodev.nvim/commit/136ec0eceaef775147034e35c35ff80ad407b7da)) +* more markdown code blocks ([5b620d9](https://github.com/folke/neodev.nvim/commit/5b620d9059d28ff75aadb50b9a2be80ba0b88272)) +* more neodev renames ([d23076d](https://github.com/folke/neodev.nvim/commit/d23076d66ab87cf2d2feae7d5ff4f3cf4f0c754d)) +* move settings to lua-dev ([c62617e](https://github.com/folke/neodev.nvim/commit/c62617ee4f15e04e0e5a96f3a824a3a1c838df53)) +* mpack functions can have multiple docs ([2c2293c](https://github.com/folke/neodev.nvim/commit/2c2293cede37c19ca2cc8349fafaa8a7fe132f7d)) +* multi-line comments for parameters ([acfa55b](https://github.com/folke/neodev.nvim/commit/acfa55b291c83e6fa40a006ea824d92d624c11b6)) +* new settings.nvim API ([6477ca9](https://github.com/folke/neodev.nvim/commit/6477ca95d081e2b2397a993699c8ff643c632f85)) +* options now have proper types ([d9bd9b4](https://github.com/folke/neodev.nvim/commit/d9bd9b488e83939e746b8150f2b684e72bb1275e)) +* other way to prevent name change issues ([d3f7002](https://github.com/folke/neodev.nvim/commit/d3f70023d925b0c2e3084bb08a42aad3b2c2f027)) +* parse incorrect function signatures with missing {} ([7eb0f15](https://github.com/folke/neodev.nvim/commit/7eb0f15fe8eaef3c4204e12cd3a94e183dd8f843)) +* prepend "lua/" to package path ([b24f225](https://github.com/folke/neodev.nvim/commit/b24f22588eab04d604be7816cf5dfe09f96e7245)) +* remove duplication of lua/ ([#66](https://github.com/folke/neodev.nvim/issues/66)) ([ec50266](https://github.com/folke/neodev.nvim/commit/ec50266039f0e71178be8c1d22211f48af498efe)) +* remove root_dir from library. otherwise rename is broken ([b9b721a](https://github.com/folke/neodev.nvim/commit/b9b721a5bed76374ae7d96a6a211737954e241c7)) +* remove vimconfig from workspace library. not needed [#1](https://github.com/folke/neodev.nvim/issues/1) ([0faabb9](https://github.com/folke/neodev.nvim/commit/0faabb95aec5af07f4416a8adb3ff6a218f6a855)) +* replace `dict(something)` by `dict`, otherwise sumneko fails to render the hover ([4667d53](https://github.com/folke/neodev.nvim/commit/4667d535e7d811f5003bf57f786a0ce05ce6f516)) +* replace invalid param name characters ([e5a553c](https://github.com/folke/neodev.nvim/commit/e5a553c6ffd661ab6f42be3f6992baf42f452445)) +* resolve realpath of neovim config ([42eab7e](https://github.com/folke/neodev.nvim/commit/42eab7e0a7950f0322794a580ff10f56d1d15ee7)) +* set proper runtime.path for require completion ([7840b31](https://github.com/folke/neodev.nvim/commit/7840b31c020c04076dde9fb9e45a9f653320b3e4)) +* sort functions before writing ([72c4e61](https://github.com/folke/neodev.nvim/commit/72c4e61a6d69e9907c271893a0822142fda1facf)) +* sort vim.fn functions before writing file ([cc55fd1](https://github.com/folke/neodev.nvim/commit/cc55fd1b8aa7da0d8f75e2fa711095388fc54f0f)) +* sumneko library should be a list instead of a dict ([14004d2](https://github.com/folke/neodev.nvim/commit/14004d29ff2d408367b1e6757b0923e67e319325)) +* types for stable ([901660d](https://github.com/folke/neodev.nvim/commit/901660d70dfa98f1afe9304bf8e580125f591f12)) +* use correct key for special lsp library paths ([f879e53](https://github.com/folke/neodev.nvim/commit/f879e53f9d4efcc0035a3b395a66ac5794954c74)) +* use default values for vim.opt ([#87](https://github.com/folke/neodev.nvim/issues/87)) ([66b67cf](https://github.com/folke/neodev.nvim/commit/66b67cf8833a98f5e4f92821e4320be82f160916)) +* use existing library on settings.Lua.workspace ([cf3b028](https://github.com/folke/neodev.nvim/commit/cf3b028043e86e0e659568aede9d0c8273800b3e)) +* use lazy.plugins() ([c87f3c9](https://github.com/folke/neodev.nvim/commit/c87f3c9ffb256846e2a51f0292537073ca62d4d0)) +* workspace.library uses a map ([350b579](https://github.com/folke/neodev.nvim/commit/350b579498a7eeeab33f0ff6496e4fa1d807469a)) + + +### Performance Improvements + +* add /lua to plugin directories ([6bf74db](https://github.com/folke/neodev.nvim/commit/6bf74dbe456711d410b905fb5fb4acb87fb4ce0c)) +* disable callSnippets for now. Too slow ([616b559](https://github.com/folke/neodev.nvim/commit/616b559dff0307adbe62606f806c2b568a6322d5)) +* disable legacy setup mechanism, but still show warning ([82423f5](https://github.com/folke/neodev.nvim/commit/82423f569d51c79733f5599fb11fb8df46b324d6)) +* dont use glob to find lua dirs. Check bufs instead. Fixes [#80](https://github.com/folke/neodev.nvim/issues/80) ([97ebf23](https://github.com/folke/neodev.nvim/commit/97ebf23c0d4f5a11f1d68a5abd468751b14980a1)) +* remove runtime path again. way too slow ([e151df6](https://github.com/folke/neodev.nvim/commit/e151df68973b3aca4c88f6d602f89574d7d32200)) diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/LICENSE b/config/neovim/store/lazy-plugins/neodev.nvim/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/README.md b/config/neovim/store/lazy-plugins/neodev.nvim/README.md new file mode 100644 index 00000000..863e7efb --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/README.md @@ -0,0 +1,143 @@ +# 💻 neodev.nvim + +Neovim setup for init.lua and plugin development with full signature help, docs and +completion for the nvim lua API. + +![image](https://user-images.githubusercontent.com/292349/201495543-ff532160-c8bd-4651-a16f-4fb682c9b945.png) + +## ✨ Features + +- Automatically configures **lua-language-server** for your **Neovim** config, **Neovim** runtime and plugin + directories +- [Annotations](https://github.com/LuaLS/lua-language-server/wiki/Annotations) for completion, hover and signatures of: + - Vim functions + - Neovim api functions + - `vim.opt` + - [vim.loop](https://github.com/luvit/luv) +- properly configures the `require` path. +- adds all plugins in `opt` and `start` to the workspace so you get completion + for all installed plugins +- properly configure the vim runtime + +## ⚡️ Requirements + +- Neovim >= 0.7.0 +- completion plugin like [nvim-cmp](https://github.com/hrsh7th/nvim-cmp) + +## 📦 Installation + +Install the plugin with your preferred package manager: + +### [lazy.nvim](https://github.com/folke/lazy.nvim) + +```lua +{ "folke/neodev.nvim", opts = {} } +``` + +### [vim-plug](https://github.com/junegunn/vim-plug) + +```vim +Plug 'folke/neodev.nvim' +``` + +## ⚙️ Configuration + +**neodev** comes with the following defaults: + +```lua +{ + library = { + enabled = true, -- when not enabled, neodev will not change any settings to the LSP server + -- these settings will be used for your Neovim config directory + runtime = true, -- runtime path + types = true, -- full signature, docs and completion of vim.api, vim.treesitter, vim.lsp and others + plugins = true, -- installed opt or start plugins in packpath + -- you can also specify the list of plugins to make available as a workspace library + -- plugins = { "nvim-treesitter", "plenary.nvim", "telescope.nvim" }, + }, + setup_jsonls = true, -- configures jsonls to provide completion for project specific .luarc.json files + -- for your Neovim config directory, the config.library settings will be used as is + -- for plugin directories (root_dirs having a /lua directory), config.library.plugins will be disabled + -- for any other directory, config.library.enabled will be set to false + override = function(root_dir, options) end, + -- With lspconfig, Neodev will automatically setup your lua-language-server + -- If you disable this, then you have to set {before_init=require("neodev.lsp").before_init} + -- in your lsp start options + lspconfig = true, + -- much faster, but needs a recent built of lua-language-server + -- needs lua-language-server >= 3.6.0 + pathStrict = true, +} +``` + +## 🚀 Setup + +**neodev** will **ONLY** change the **lua_ls** settings for: + +- your Neovim config directory +- your Neovim runtime directory +- any plugin directory (this is an lsp root_dir that contains a `/lua` + directory) + +For any other `root_dir`, **neodev** will **NOT** change any settings. + +> **TIP** with [neoconf.nvim](https://github.com/folke/neoconf.nvim), you can easily set project local **Neodev** settings. +> See the example [.neoconf.json](https://github.com/folke/neodev.nvim/blob/main/.neoconf.json) file in this repository + +```lua +-- IMPORTANT: make sure to setup neodev BEFORE lspconfig +require("neodev").setup({ + -- add any options here, or leave empty to use the default settings +}) + +-- then setup your lsp server as usual +local lspconfig = require('lspconfig') + +-- example to setup lua_ls and enable call snippets +lspconfig.lua_ls.setup({ + settings = { + Lua = { + completion = { + callSnippet = "Replace" + } + } + } +}) +``` + +
+Example for setting up **neodev** that overrides the settings for `/etc/nixos` + +```lua +-- You can override the default detection using the override function +-- EXAMPLE: If you want a certain directory to be configured differently, you can override its settings +require("neodev").setup({ + override = function(root_dir, library) + if root_dir:find("/etc/nixos", 1, true) == 1 then + library.enabled = true + library.plugins = true + end + end, +}) +``` + +
+ +It's possible to setup Neodev without lspconfig, by configuring the `before_init` +of the options passed to `vim.lsp.start`. + +
+Example without lspconfig + +```lua +-- dont run neodev.setup +vim.lsp.start({ + name = "lua-language-server", + cmd = { "lua-language-server" }, + before_init = require("neodev.lsp").before_init, + root_dir = vim.fn.getcwd(), + settings = { Lua = {} }, +}) +``` + +
diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/doc/lua-dev.txt b/config/neovim/store/lazy-plugins/neodev.nvim/doc/lua-dev.txt new file mode 100644 index 00000000..3fcae1a8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/doc/lua-dev.txt @@ -0,0 +1,163 @@ +*lua-dev.txt* For NVIM v0.5.0 Last change: 2022 October 14 + +============================================================================== +Table of Contents *lua-dev-table-of-contents* + +1. lua-dev |lua-dev-lua-dev| + - Features |lua-dev-features| + - Requirements |lua-dev-requirements| + - Installation |lua-dev-installation| + - Configuration |lua-dev-configuration| + - Setup |lua-dev-setup| + - How? |lua-dev-how?| + +============================================================================== +1. lua-dev *lua-dev-lua-dev* + +Dev setup for init.lua and plugin development with full signature help, docs +and completion for the nvim lua API. + +
+ +

image

+
+ +
+ +

image

+
+ +FEATURES *lua-dev-features* + + +- Automatically configures **lua-language-server** for your **Neovim** config, **Neovim** runtime and plugin + directories +- Annotations for completion, hover and signatures of: + - Vim functions + - Neovim api functions + - `vim.opt` + - vim.loop +- properly configures the `require` path. +- adds all plugins in `opt` and `start` to the workspace so you get completion + for all installed plugins +- properly configure the vim runtime + + +REQUIREMENTS *lua-dev-requirements* + + +- Neovim >= 0.7.0 +- completion plugin like nvim-cmp + + +INSTALLATION *lua-dev-installation* + +Install the plugin with your preferred package manager: + +PACKER ~ + +> + use "folke/lua-dev.nvim" +< + + +VIM-PLUG ~ + +> + Plug 'folke/lua-dev.nvim' +< + + +CONFIGURATION *lua-dev-configuration* + +**lua-dev** comes with the following defaults: + +> + { + library = { + enabled = true, -- when not enabled, lua-dev will not change any settings to the LSP server + -- these settings will be used for your Neovim config directory + runtime = true, -- runtime path + types = true, -- full signature, docs and completion of vim.api, vim.treesitter, vim.lsp and others + plugins = true, -- installed opt or start plugins in packpath + -- you can also specify the list of plugins to make available as a workspace library + -- plugins = { "nvim-treesitter", "plenary.nvim", "telescope.nvim" }, + }, + setup_jsonls = true, -- configures jsonls to provide completion for project specific .luarc.json files + -- for your Neovim config directory, the config.library settings will be used as is + -- for plugin directories (root_dirs having a /lua directory), config.library.plugins will be disabled + -- for any other directory, config.library.enabled will be set to false + override = function(root_dir, options) end, + + } +< + + +SETUP *lua-dev-setup* + +**lua-dev** will **ONLY** change the **lua_ls** settings for: + + +- your Neovim config directory +- your Neovim runtime directory +- any plugin directory (this is an lsp root_dir that contains a `/lua` + directory) + + +For any other `root_dir`, **lua-dev** will **NOT** change any settings. + +> + -- IMPORTANT: make sure to setup lua-dev BEFORE lspconfig + require("lua-dev").setup({ + -- add any options here, or leave empty to use the default settings + }) + + -- then setup your lsp server as usual + local lspconfig = require('lspconfig') + + -- example to setup sumneko and enable call snippets + lspconfig.lua_ls.setup({ + settings = { + Lua = { + completion = { + callSnippet = "Replace" + } + } + } + }) +< + + +Example for setting up **lua-dev** that overrides the settings for `/etc/nixos` + +> + -- You can override the default detection using the override function + -- EXAMPLE: If you want a certain directory to be configured differently, you can override its settings + require("lua-dev").setup({ + override = function(root_dir, library) + if require("lua-dev.util").has_file(root_dir, "/etc/nixos") then + library.enabled = true + library.plugins = true + end + end, + }) +< + + +HOW? *lua-dev-how?* + +**Neovim** includes a script + to +generate the nvim docs. That script also creates message pack files containing +all the API metadata in a structured way. Unfortunately these files are not +packaged in the releases. + +Using the message pack files, I converted all the API data to EmmyLua +annotations + and +make them available for the Sumneko LSP + as a workspace library. + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/doc/neodev.nvim.txt b/config/neovim/store/lazy-plugins/neodev.nvim/doc/neodev.nvim.txt new file mode 100644 index 00000000..22a0b165 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/doc/neodev.nvim.txt @@ -0,0 +1,167 @@ +*neodev.nvim.txt* For Neovim >= 0.8.0 Last change: 2024 March 29 + +============================================================================== +Table of Contents *neodev.nvim-table-of-contents* + +1. neodev.nvim |neodev.nvim-neodev.nvim| + - Features |neodev.nvim-neodev.nvim-features| + - Requirements |neodev.nvim-neodev.nvim-requirements| + - Installation |neodev.nvim-neodev.nvim-installation| + - Configuration |neodev.nvim-neodev.nvim-configuration| + - Setup |neodev.nvim-neodev.nvim-setup| +2. Links |neodev.nvim-links| + +============================================================================== +1. neodev.nvim *neodev.nvim-neodev.nvim* + +Neovim setup for init.lua and plugin development with full signature help, docs +and completion for the nvim lua API. + + +FEATURES *neodev.nvim-neodev.nvim-features* + +- Automatically configures **lua-language-server** for your **Neovim** config, **Neovim** runtime and plugin + directories +- Annotations for completion, hover and signatures of: + - Vim functions + - Neovim api functions + - `vim.opt` + - vim.loop +- properly configures the `require` path. +- adds all plugins in `opt` and `start` to the workspace so you get completion + for all installed plugins +- properly configure the vim runtime + + +REQUIREMENTS *neodev.nvim-neodev.nvim-requirements* + +- Neovim >= 0.7.0 +- completion plugin like nvim-cmp + + +INSTALLATION *neodev.nvim-neodev.nvim-installation* + +Install the plugin with your preferred package manager: + + +LAZY.NVIM ~ + +>lua + { "folke/neodev.nvim", opts = {} } +< + + +VIM-PLUG ~ + +>vim + Plug 'folke/neodev.nvim' +< + + +CONFIGURATION *neodev.nvim-neodev.nvim-configuration* + +**neodev** comes with the following defaults: + +>lua + { + library = { + enabled = true, -- when not enabled, neodev will not change any settings to the LSP server + -- these settings will be used for your Neovim config directory + runtime = true, -- runtime path + types = true, -- full signature, docs and completion of vim.api, vim.treesitter, vim.lsp and others + plugins = true, -- installed opt or start plugins in packpath + -- you can also specify the list of plugins to make available as a workspace library + -- plugins = { "nvim-treesitter", "plenary.nvim", "telescope.nvim" }, + }, + setup_jsonls = true, -- configures jsonls to provide completion for project specific .luarc.json files + -- for your Neovim config directory, the config.library settings will be used as is + -- for plugin directories (root_dirs having a /lua directory), config.library.plugins will be disabled + -- for any other directory, config.library.enabled will be set to false + override = function(root_dir, options) end, + -- With lspconfig, Neodev will automatically setup your lua-language-server + -- If you disable this, then you have to set {before_init=require("neodev.lsp").before_init} + -- in your lsp start options + lspconfig = true, + -- much faster, but needs a recent built of lua-language-server + -- needs lua-language-server >= 3.6.0 + pathStrict = true, + } +< + + +SETUP *neodev.nvim-neodev.nvim-setup* + +**neodev** will **ONLY** change the **lua_ls** settings for: + +- your Neovim config directory +- your Neovim runtime directory +- any plugin directory (this is an lsp root_dir that contains a `/lua` + directory) + +For any other `root_dir`, **neodev** will **NOT** change any settings. + + + **TIP** with neoconf.nvim , you can + easily set project local **Neodev** settings. See the example .neoconf.json + file in this + repository +>lua + -- IMPORTANT: make sure to setup neodev BEFORE lspconfig + require("neodev").setup({ + -- add any options here, or leave empty to use the default settings + }) + + -- then setup your lsp server as usual + local lspconfig = require('lspconfig') + + -- example to setup lua_ls and enable call snippets + lspconfig.lua_ls.setup({ + settings = { + Lua = { + completion = { + callSnippet = "Replace" + } + } + } + }) +< + +Example for setting up **neodev** that overrides the settings for `/etc/nixos` ~ + +>lua + -- You can override the default detection using the override function + -- EXAMPLE: If you want a certain directory to be configured differently, you can override its settings + require("neodev").setup({ + override = function(root_dir, library) + if root_dir:find("/etc/nixos", 1, true) == 1 then + library.enabled = true + library.plugins = true + end + end, + }) +< + +It’s possible to setup Neodev without lspconfig, by configuring the +`before_init` of the options passed to `vim.lsp.start`. + +Example without lspconfig ~ + +>lua + -- dont run neodev.setup + vim.lsp.start({ + name = "lua-language-server", + cmd = { "lua-language-server" }, + before_init = require("neodev.lsp").before_init, + root_dir = vim.fn.getcwd(), + settings = { Lua = {} }, + }) +< + +============================================================================== +2. Links *neodev.nvim-links* + +1. *image*: https://user-images.githubusercontent.com/292349/201495543-ff532160-c8bd-4651-a16f-4fb682c9b945.png + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/lua-dev.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/lua-dev.lua new file mode 100644 index 00000000..23193b7e --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/lua-dev.lua @@ -0,0 +1,6 @@ +vim.notify( + "'lua-dev' was renamed to 'neodev'. Please update your config.", + vim.log.levels.WARN, + { title = "neodev.nvim" } +) +return require("neodev") diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/annotations.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/annotations.lua new file mode 100644 index 00000000..209d1e61 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/annotations.lua @@ -0,0 +1,261 @@ +---@class LuaParam +---@field name? string +---@field type? string +---@field doc? string +---@field optional? boolean + +--- @class LuaFunction +--- @field name string, +--- @field doc string, +--- @field deprecated? boolean +--- @field overload? string[] +--- @field params LuaParam[] +--- @field return LuaParam[] + +local M = {} + +M.name2type = { + boolean = { "set" }, + buffer = { "buffer", "bufid", "bufnr", "buf" }, + window = { "win", "window", "winid", "winnr" }, + number = { "col", "lnum", "tabnr", "nr", "pos", "ns", "index", "from", "to", "start", "stop", "end_", "integer" }, + string = { "str", "text", "string" }, + ["table"] = { "opt", "opts", "options", "dict", "dictionary" }, + ["fun()"] = { "fn", "function", "callback", "func", "funcref", "luaref" }, + ["any[]"] = { "args", "list", "array" }, + ["nil"] = { "void", "none" }, +} +M.keywords = + { "or", "and", "repeat", "function", "end", "return", "do", "break", "else", "elseif", "for", "goto", "if", "while" } +M.nvim_types = { + window = "integer", + buffer = "integer", + tabpage = "integer", + job = "number", + channel = "integer", + sends = "number", + blob = "number", + object = "any", + float = "number", +} +M.lua_types = { + ["nil"] = "nil", + number = "number", + string = "string", + boolean = "boolean", + ["function"] = "fun()", + table = "table", +} + +function M.is_keyword(str) + return vim.tbl_contains(M.keywords, str) +end + +---@param str string +---@param first? string +function M.comment(str, first) + first = first or "" + local prefix = "-- " + return first .. prefix .. str:gsub("\n", "\n" .. prefix) +end + +---@param param LuaParam +function M.type(param) + local type = param.type and param.type ~= "" and param.type or "any" + + if type == "any" then + for t, names in pairs(M.name2type) do + if vim.tbl_contains(names, param.name) then + return t + end + end + end + for t, names in pairs(M.name2type) do + if vim.tbl_contains(names, type) then + return t + end + end + + if M.nvim_types[type] then + return type + end + + if M.lua_types[type] then + return M.lua_types[type] + end + + if type == "arrayof(string)" then + type = "string[]" + elseif type == "arrayof(integer, 2)" then + type = "number[]" + elseif type == "dictionaryof(luaref)" then + type = "table" + elseif type:find("^arrayof%(") then + return "any[]" + elseif type:find("^dict%(") or type:find("^dictionaryof%(") then + return "table" + end + return type +end + +---@param param LuaParam +function M.param(param) + local parts = {} + if param.name then + if M.is_keyword(param.name) then + param.name = param.name .. "_" + end + table.insert(parts, param.name .. (param.optional and "?" or "")) + end + + local type = M.type(param) + if type == "nil" then + return "" + end + + if type then + table.insert(parts, type) + end + + if param.doc then + table.insert(parts, "# " .. param.doc) + end + + if not param.doc and type == "any" and not param.optional then + return "" + end + + local ret = table.concat(parts, " ") + if not param.name then + return M.comment("@return " .. ret, "-") .. "\n" + else + return M.comment("@param " .. ret, "-") .. "\n" + end +end + +function M.fqn(name) + local real_fn = vim.tbl_get(_G, unpack(vim.split(name, ".", { plain = true }))) + if vim.api[name] then + return "vim.api." .. name + elseif vim[name] then + return "vim." .. name + elseif name:find("^[a-zA-Z_]+$") and vim.fn.exists("*" .. name) == 1 then + return "vim.fn." .. name + elseif name:find("^vim") and real_fn then + return name + end + -- if we get here, it means the function is RPC only, or no longer exists +end + +function M.is_lua(name) + local real_fn = vim.tbl_get(_G, unpack(vim.split(name, ".", { plain = true }))) + + -- some plugins (like Noice) wrap api functions. This deals with that + if real_fn and name:find("vim%.api%.") then + return false + elseif type(real_fn) == "function" then + local info = debug.getinfo(real_fn, "S") + return info.what == "Lua" + elseif type(real_fn) == "table" then + return true + elseif not real_fn then + return true + end + return false +end + +--- @param fun LuaFunction +function M.fun(fun) + local ret = "" + if fun.doc ~= "" then + -- make markdown lua code blocks for code regions + local ft = fun.name:find("vim.fn") and "vim" or "lua" + local lines = vim.split(fun.doc, "\n") + + local l = 1 + while l < #lines do + local line = lines[l] + local from, to, before, lang = line:find("^(%s*.*)>([a-z]*)%s*$") + if from then + before = (not before:find("^%s*$")) and before or nil + lang = lang ~= "" and lang or nil + for i = l + 1, #lines do + if lines[i]:find("^%S") or lines[i]:find("^%s*<") or i == #lines then + lines[l] = (before and (before .. "\n") or "") .. "```" .. (lang or ft) + if lines[i]:find("^%s*<%s*$") then + lines[i] = "```" + elseif lines[i]:find("^%s*<") then + lines[i] = "```\n" .. lines[i]:gsub("<", "") + else + lines[i] = lines[i] .. "\n```" + end + l = i + break + end + end + end + l = l + 1 + end + local doc = table.concat(lines, "\n") + doc = M.fix_indent(doc) + ret = ret .. (M.comment(doc)) .. "\n" + end + + if fun.seealso and #fun.seealso > 0 then + for _, also in ipairs(fun.seealso) do + ret = ret .. "--- @see " .. also .. "\n" + end + end + + local params = {} + + for _, param in pairs(fun.params) do + ret = ret .. M.param(param) + table.insert(params, param.name) + end + for _, r in pairs(fun["return"]) do + ret = ret .. M.param(r) + end + + local signature = "function %s(%s) end" + + -- handle special Lua names. Set as a field instead of a function + if M.is_keyword(fun.name:match("[^.]+$")) then + local prefix, name = fun.name:match("(.*)%.([^.]+)$") + fun.name = name + signature = prefix .. "[%q] = function(%s) end" + end + + if fun.overload then + for _, overload in ipairs(fun.overload) do + ret = ret .. "--- @overload " .. overload .. "\n" + end + end + + ret = ret .. signature:format(fun.name, table.concat(params, ", ")) + return ret .. "\n\n" +end + +---@param text string +function M.fix_indent(text) + local lines = vim.split(text, "\n") + local indent = 10 + for l, line in ipairs(lines) do + if not (line:find("^%s*$") or line:find("^```")) then + line = line:gsub(" ", "\t") + lines[l] = line + local prefix = line:match("^\t+") + if prefix then + indent = math.min(indent, #prefix) + end + end + end + if indent > 0 then + for l, line in ipairs(lines) do + lines[l] = line:gsub("^" .. ("\t"):rep(indent), ""):gsub("\t", " ") + end + end + return table.concat(lines, "\n") +end + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/api.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/api.lua new file mode 100644 index 00000000..559cc923 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/api.lua @@ -0,0 +1,80 @@ +local Annotations = require("neodev.build.annotations") +local Docs = require("neodev.build.docs") + +---@class NvimApiInfo +---@field functions NvimApiFunction[] + +---@class NvimApiFunction +---@field name string +---@field deprecated_since? number +---@field parameters {[1]: string, [2]:string}[] +---@field return_type string +---@field since number +---@field method? boolean + +---@class LuaApiFunction: LuaFunction +---@field params_index? table +---@field info? NvimApiFunction + +local M = {} + +---@return table +function M.get() + local functions = Docs.parse_functions("api", { + name = Annotations.fqn, + }) + ---@cast functions table + + -- add params index + for _, fun in pairs(functions) do + fun.params_index = {} + for _, param in ipairs(fun.params) do + local name = param.name + if name then + fun.params_index[name] = param + end + end + end + + ---@type NvimApiInfo + local info = vim.fn.api_info() + + -- add api info + for _, fun in ipairs(info.functions) do + if not fun.deprecated_since then + local name = Annotations.fqn(fun.name) + if name then + if not functions[name] then + functions[name] = { + doc = "", + name = name, + params = {}, + params_index = {}, + } + end + functions[name].info = fun + end + end + end + + -- merge api info + for _, fun in pairs(functions) do + if fun.info then + for _, param in ipairs(fun.info.parameters) do + if fun.params_index[param[2]] then + fun.params_index[param[2]].type = param[1]:lower() + else + fun.params_index[param[2]] = { name = param[2], type = param[1]:lower() } + end + end + local return_type = fun.info.return_type:lower() + fun["return"] = { { type = return_type == "nil" and nil or return_type } } + end + end + + return functions +end + +M.get() + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/docs.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/docs.lua new file mode 100644 index 00000000..08d14f67 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/docs.lua @@ -0,0 +1,340 @@ +local Util = require("neodev.util") +local Annotations = require("neodev.build.annotations") + +local M = {} + +M.function_pattern = "^(%S-%([^(]-%))" +M.function_signature_pattern = "^(%S-)%(([^(]-)%)" +M.vim_type_map = { + number = "number", + float = "float", + string = "string", + list = "any[]", + any = "any", + funcref = "fun()", + dict = "table", + none = "nil", + set = "boolean", + boolean = "boolean", +} + +---@param name string +function M.read(name) + local docs = vim.fn.expand("$VIMRUNTIME/doc") + local txtfile = docs .. "/" .. name .. ".txt" + + ---@type string[] + local lines = {} + for line in io.lines(txtfile) do + table.insert(lines, line) + end + return lines +end + +---@return string, string[] +function M.strip_tags(str) + local tags = {} + return str + :gsub( + "(%*%S-%*)", + ---@param tag string + function(tag) + tag = tag:sub(2, -2) + table.insert(tags, tag) + return "" + end + ) + :gsub("%s*$", ""), + tags +end + +---@param text string +function M.trim(text) + return text:gsub("^%s*\n", ""):gsub("\n+$", "") +end + +---@param name string +---@param opts { pattern: string, continuation?: string, context?: number} +function M.parse(name, opts) + opts = opts or {} + opts.continuation = opts.continuation or "^[%s<>]" + opts.context = opts.context or 1 + + local tags = {} + local line_tags = {} + local chunk_tags = {} + local chunk_match = {} + local chunk = {} + ---@type {tags:string[], text:string, match: string[]}[] + local ret = {} + + local function save() + if #chunk > 0 then + table.insert(ret, { + tags = vim.deepcopy(chunk_tags), + text = M.trim(table.concat(chunk, "\n")), + match = vim.deepcopy(chunk_match), + }) + end + chunk = {} + chunk_tags = {} + end + local lines = M.read(name) + for l, line in ipairs(lines) do + line, line_tags = M.strip_tags(line) + + if #line_tags > 0 then + tags = line_tags + end + + local context = line + for c = 1, opts.context do + if lines[l + c] then + context = context .. "\n" .. lines[l + c] + end + end + + local match = { context:match(opts.pattern) } + + if #match > 0 then + save() + chunk_match = match + chunk_tags = vim.deepcopy(tags) + table.insert(chunk, line) + elseif #chunk > 0 and (line:find(opts.continuation) or line:find("^%s*$")) then + table.insert(chunk, line) + else + save() + end + end + return ret +end + +---@return {name: string, params: {name:string, optional?:boolean}[], doc: string}? +---@return LuaFunction? +function M.parse_signature(line) + ---@type string, string, string + local name, sig, doc = line:match(M.function_signature_pattern .. "(.*)") + if name then + -- Parse args + local optional_from = sig:find("%[") + sig = sig:gsub("%[", "") + sig = sig:gsub("%]", "") + sig = sig:gsub("\n", " ") + sig = sig:gsub("\t", " ") + local params = {} + ---@type table + local index = {} + local from = 0 + local to = 0 + local param = "" + while from do + ---@type number, number, string + from, to, param = sig:find("{?([^ ,{}]+)}?", to + 1) + if from then + local optional = optional_from and from >= optional_from and true or nil + if param:sub(1, 1) == "*" then + optional = true + param = param:sub(2) + end + param = param:gsub("%-", "_") + if param:find("^%d+$") then + param = "p" .. param + end + + -- check for duplicate params + local p = param + local c = 1 + while index[param] do + param = p .. c + c = c + 1 + end + index[param] = true + + table.insert(params, { + name = param, + optional = optional, + }) + end + end + + return { name = name, params = params, doc = M.trim(doc) } + end +end + +function M.options() + ---@type table + local ret = {} + + local option_pattern = "^'(%S-)'%s*" + + local options = M.parse("options", { pattern = option_pattern }) + + for _, option in ipairs(options) do + local name = option.match[1] + local doc = option.text:gsub("'(%S-)'", "`'%1'` ") + ret[name] = doc + end + return ret +end + +---@param doc string +---@param opts? {filter?: (fun(name:string):boolean), name?: (fun(name:string):string)} +function M.parse_functions(doc, opts) + opts = opts or {} + ---@type table + local ret = {} + + local functions = M.parse(doc, { pattern = M.function_pattern, context = 2 }) + + for _, fun in ipairs(functions) do + local text = fun.text + -- replace function name by the function tag, to make sure it is fully qualified + for _, tag in ipairs(fun.tags) do + if tag:find("vim.*%(%)$") then + tag = tag:sub(1, -3) + local name = text:match(M.function_signature_pattern) + if tag:sub(-#name) == name then + text = text:gsub("^%S-%(", tag .. "(") + end + end + end + + local parse = M.parse_signature(text) + + if parse then + local name = parse.name + + if opts.name then + name = opts.name(name) + end + + if name and (opts.filter == nil or opts.filter(name)) then + ret[name] = { + name = name, + params = parse.params, + doc = parse.doc, + ["return"] = {}, + } + end + end + end + return ret +end + +function M.lua() + return M.parse_functions("lua", { + filter = function(name) + return not Annotations.is_lua(name) + end, + }) +end + +function M.luv() + local ret = M.parse_functions("luvref", { + filter = function(name) + return not Annotations.is_lua(name) + end, + name = function(name) + local ret = name:gsub("^uv%.", "vim.loop.") + return ret + end, + }) + Util.for_each(ret, function(_, fun) + local returns = fun.doc:match("%s*Returns: (.*)\n") + if not returns then + returns = fun.doc:match("%s*Returns %(sync version%): (.*)\n") + end + ---@type LuaParam + local retval = {} + if returns then + ---@diagnostic disable-next-line: no-unknown + for t in returns:gmatch("`(.-)`") do + if t == "nil" or t == "fail" then + retval.optional = true + elseif not retval.type then + retval.type = t:find("userdata") and "userdata" or t + end + end + end + if not vim.tbl_isempty(retval) then + fun["return"] = { retval } + end + end) + return ret +end + +function M.commands() + local pattern = "|:%S-|%s+:([a-z]%S-)%s+(.*)" + local builtins = M.parse("index", { pattern = pattern, context = 0, continuation = "^%s+" }) + ---@type table + local ret = {} + for _, builtin in ipairs(builtins) do + if vim.tbl_contains(builtin.tags, "ex-cmd-index") then + local cmd = builtin.match[1] + local desc = builtin.match[2] + local i = cmd:find("%[") + if i then + ret[cmd:sub(1, i - 1)] = desc + cmd = cmd:gsub("[%[%]]", "") + end + ret[cmd] = desc + end + end + return ret +end + +function M.functions() + local builtins = M.parse("builtin", { pattern = M.function_pattern, context = 2 }) + + ---@type table + local retvals = {} + + -- Parse return values from `:h builtin-function-list` + for _, builtin in ipairs(builtins) do + if vim.tbl_contains(builtin.tags, "builtin-function-list") then + local text = builtin.text + -- replace any whitespace after the function by a tab character + text = text:gsub(M.function_pattern .. "%s+", "%1\t") + -- replace consecutive whitespace by tabs + text = text:gsub("%s%s+", "\t") + ---@type string, string, string + local name, _args, retval = text:match(M.function_signature_pattern .. "\t(%w+)") + if name then + retval = retval:lower() + if M.vim_type_map[retval] then + retval = M.vim_type_map[retval] + if retval ~= "nil" then + retvals["vim.fn." .. name] = retval + end + else + Util.debug("Unknown retval: " .. retval) + end + else + Util.error("Couldnt parse builtin-function-list: " .. vim.inspect(builtin)) + end + end + end + + local ret = M.parse_functions("builtin", { + filter = function(name) + name = name:match("vim%.fn%.(.*)") + if name:find("%.") then + return false + end + return name and (vim.fn.exists("*" .. name) == 1) + end, + name = function(name) + return "vim.fn." .. name + end, + }) + for k, fun in pairs(ret) do + if retvals[k] then + fun["return"] = { { type = retvals[k]:lower() } } + end + end + + return ret +end + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/init.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/init.lua new file mode 100644 index 00000000..f0be5835 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/init.lua @@ -0,0 +1,126 @@ +local Config = require("neodev.config") +local Util = require("neodev.util") + +local Annotations = require("neodev.build.annotations") +local Api = require("neodev.build.api") +local Docs = require("neodev.build.docs") +local Mpack = require("neodev.build.mpack") +local Writer = require("neodev.build.writer") +local Options = require("neodev.build.options") + +local M = {} + +function M.api() + local api = Api.get() + + -- Only load mpack on nightly and add any missing functions + -- Typically hidden functions + if Config.version() == "nightly" then + local functions = Mpack.read("api.mpack") + + for k, v in pairs(functions) do + if not api[k] then + api[k] = v + end + end + end + + M.write("api", api) +end + +---@return table, string? +function M.override(fname) + local override = Config.root("/types/override/" .. fname .. ".lua") + if override then + local code = Util.read_file(override) + local mod = {} + local mod_code = code:match("\n(return.*)") or code:match("^(return.*)") + if mod_code then + mod = load(mod_code)() + end + code = code:gsub("\nreturn.*", "") + code = code:gsub("^return.*", "") + return mod, code + end + return {} +end + +---@param fname string +---@param functions table +function M.write(fname, functions) + local override, override_code = M.override(fname) + functions = vim.tbl_deep_extend("force", functions, override) + + local writer = Writer(fname) + if override_code then + writer:write(override_code .. "\n\n") + end + Util.for_each(functions, function(_, fun) + writer:write(Annotations.fun(fun)) + end) + + writer:close() +end + +function M.alias() + local writer = Writer("alias") + Util.for_each(Annotations.nvim_types, function(key, value) + writer:write(("---@alias %s %s"):format(key, value) .. "\n") + end) + writer:close() +end + +function M.commands() + local writer = Writer("cmd") + Util.for_each(Docs.commands(), function(cmd, desc) + writer:write(Annotations.comment(desc) .. "\n") + if Annotations.is_keyword(cmd) then + writer:write(("vim.cmd[%q] = function(...)end"):format(cmd) .. "\n\n") + else + writer:write(("function vim.cmd.%s(...)end"):format(cmd) .. "\n\n") + end + end) + writer:close() +end + +function M.clean() + local types = Config.types() + for _, f in pairs(vim.fn.expand(types .. "/*.lua", false, true)) do + if not f:find("/vim.lua", 1, true) then + vim.loop.fs_unlink(f) + end + end +end + +function M.uv() + local writer = Writer("uv") + writer:write(Util.fetch("https://raw.githubusercontent.com/Bilal2453/luvit-meta/main/library/uv.lua")) +end + +function M.lpeg() + local writer = Writer("lpeg") + writer:write(Util.fetch("https://raw.githubusercontent.com/LuaCATS/lpeg/main/library/lpeg.lua")) +end + +function M.build() + M.clean() + M.uv() + M.alias() + M.commands() + + if vim.fn.has("nvim-0.10") == 0 then + M.lpeg() + + Options.build() + + M.api() + + -- M.write("luv", Docs.luv()) + M.write("lua", Docs.lua()) + M.write("vim.fn", Docs.functions()) + end +end + +M.build() + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/mpack.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/mpack.lua new file mode 100644 index 00000000..345cfd30 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/mpack.lua @@ -0,0 +1,73 @@ +local Annotations = require("neodev.build.annotations") +local util = require("neodev.util") +local M = {} + +---@class MpackFunction +---@field doc string[] +---@field parameters {[1]: string, [2]:string}[] +---@field parameters_doc table +---@field return string[] +---@field seealso string[] +---@field signature string + +function M.read(mpack) + mpack = "data/" .. mpack + ---@type table + local data = vim.mpack.decode(util.read_file(mpack)) + + ---@type table + local ret = {} + for name, fun in pairs(data) do + name = Annotations.fqn(name) + if name then + ret[name] = M.process(name, fun) + end + end + return ret +end + +---@param name string +--- @param fun MpackFunction +--- @return LuaFunction +function M.process(name, fun) + ---@type LuaFunction + local ret = { + doc = (fun.doc and fun.doc[1]) and table.concat(fun.doc, "\n\n") or "", + name = name, + params = {}, + seealso = fun.seealso or {}, + ["return"] = {}, + } + + for _, r in pairs(fun["return"]) do + table.insert(ret["return"], { doc = r }) + end + + for i, p in ipairs(fun.parameters or {}) do + local type = p[1] + local pname = p[2] + local param = { name = pname } + if type ~= "" then + param.type = type:lower() + end + param.doc = fun.parameters_doc and fun.parameters_doc[pname] or nil + + if param.type and param.type:find("%*$") then + param.type = param.type:sub(1, -2) + param.optional = true + end + -- only include err param if it's documented + -- most nvim_ functions have an err param at the end, but these should not be included + local skip = i == #fun.parameters and (pname == "err" or pname == "error") + -- skip self params + if param.name == "self" or param.name == "" or param.name == "arena" then + skip = true + end + if not skip then + table.insert(ret.params, param) + end + end + return ret +end + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/options.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/options.lua new file mode 100644 index 00000000..99c9031c --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/options.lua @@ -0,0 +1,106 @@ +local Docs = require("neodev.build.docs") +local Writer = require("neodev.build.writer") +local Annotations = require("neodev.build.annotations") +local Util = require("neodev.util") +local Config = require("neodev.config") + +---@class OptionInfo +---@field allows_duplicates boolean +---@field commalist boolean +---@field default any +---@field flaglist boolean +---@field global_local boolean +---@field name string +---@field scope "global" | "win" | "buf" +---@field shortname string +---@field type string + +---@alias OptionsInfo table + +local M = {} + +M.metatype2lua = { + map = function(info) + return ("table"):format(info.type) + end, + set = function(info) + return ("%s[]"):format(info.type) + end, + array = function(info) + return ("%s[]"):format(info.type) + end, +} + +function M.build() + local writer = Writer("options") + + local docs = Docs.options() + + ---@type OptionsInfo + local info = vim.api.nvim_get_all_options_info() + + ---@param scope string + local function write(scope) + local var = "vim." .. scope:sub(1, 1) .. "o" + + writer:write("---@class " .. var .. "\n") + writer:write(var .. " = {}\n\n") + Util.for_each(info, function(name, option) + if option.scope == scope then + local default = vim.inspect(option.default) + local str = "" + if docs[name] then + str = str .. Annotations.comment(docs[name]) .. "\n" + end + str = str .. ("%s.%s = %s\n"):format(var, name, default) + if option.shortname ~= "" then + str = str .. ("%s.%s = %s.%s\n"):format(var, option.shortname, var, name) + end + writer:write(str) + end + end) + writer:write("\n\n") + end + + write("global") + write("win") + write("buf") + + -- Write vim.opt + Util.for_each(info, function(name, option) + local str = "" + if docs[name] then + str = str .. Annotations.comment(docs[name]) .. "\n" + end + ---@type string + local return_type = option.type + + pcall(function() + ---@diagnostic disable-next-line: no-unknown + return_type = vim.opt[name]._info.metatype + if M.metatype2lua[return_type] then + ---@diagnostic disable-next-line: no-unknown + return_type = M.metatype2lua[return_type](option) + end + end) + + str = str .. ("--- @class vim.opt.%s: vim.Option,%s\n"):format(name, return_type) + str = str .. ("--- @operator add: vim.opt.%s\n"):format(name) + str = str .. ("--- @operator sub: vim.opt.%s\n"):format(name) + str = str .. ("--- @operator pow: vim.opt.%s\n"):format(name) + str = str .. ("vim.opt.%s = %s\n"):format(name, vim.inspect(option.default)) + if option.shortname ~= "" then + str = str .. ("vim.opt.%s = vim.opt.%s\n"):format(option.shortname, name) + end + + str = str .. ("--- @return %s\nfunction vim.opt.%s:get()end\n\n"):format(return_type, name) + writer:write(str) + end) + + local code = Util.read_file(Config.root("/types/override/options.lua")) + writer:write(code .. "\n") + + writer:close() +end + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/writer.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/writer.lua new file mode 100644 index 00000000..222ddc46 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/build/writer.lua @@ -0,0 +1,53 @@ +local config = require("neodev.config") + +---@class AnnotationWriter +---@field name string +---@field fd? number +---@field fnum number +---@field size number +local M = {} +M.__index = M + +M.MAX_SIZE = 1024 * 200 + +---@return AnnotationWriter +function M.new(name) + local self = setmetatable({}, M) + self.name = name + self.size = 0 + self.fnum = 0 + return self +end + +function M:close() + if self.fd then + vim.loop.fs_close(self.fd) + end +end + +function M:check() + if not self.fd or self.size > M.MAX_SIZE then + self:close() + local types = config.types() + local fname = types .. "/" .. self.name + if self.fnum > 0 then + fname = fname .. "." .. self.fnum + end + self.fd = vim.loop.fs_open(fname .. ".lua", "w+", 420) + self.fnum = self.fnum + 1 + self.size = 0 + self:intro() + end +end + +function M:intro() + self:write("---@meta\n\n") +end + +function M:write(text) + self:check() + vim.loop.fs_write(self.fd, text, -1) + self.size = self.size + #text +end + +return M.new diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/config.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/config.lua new file mode 100644 index 00000000..dcef4dca --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/config.lua @@ -0,0 +1,57 @@ +local M = {} + +--- @class LuaDevOptions +M.defaults = { + library = { + enabled = true, -- when not enabled, neodev will not change any settings to the LSP server + -- these settings will be used for your neovim config directory + runtime = true, -- runtime path + types = true, -- full signature, docs and completion of vim.api, vim.treesitter, vim.lsp and others + ---@type boolean|string[] + plugins = true, -- installed opt or start plugins in packpath + -- you can also specify the list of plugins to make available as a workspace library + -- plugins = { "nvim-treesitter", "plenary.nvim", "telescope.nvim" }, + }, + setup_jsonls = true, -- configures jsonls to provide completion for .luarc.json files + -- for your neovim config directory, the config.library settings will be used as is + -- for plugin directories (root_dirs having a /lua directory), config.library.plugins will be disabled + -- for any other directory, config.library.enabled will be set to false + override = function(root_dir, options) end, + -- With lspconfig, Neodev will automatically setup your lua-language-server + -- If you disable this, then you have to set {before_init=require("neodev.lsp").before_init} + -- in your lsp start options + lspconfig = true, + -- much faster, but needs a recent built of lua-language-server + -- needs lua-language-server >= 3.6.0 + pathStrict = true, + debug = false, +} + +--- @type LuaDevOptions +M.options = {} + +function M.setup(options) + M.options = vim.tbl_deep_extend("force", {}, M.defaults, options or {}) +end + +function M.types() + return M.root("/types/" .. M.version()) +end + +---@param root? string +function M.root(root) + local f = debug.getinfo(1, "S").source:sub(2) + return vim.loop.fs_realpath(vim.fn.fnamemodify(f, ":h:h:h") .. "/" .. (root or "")) +end + +---@return "nightly" | "stable" +function M.version() + return vim.version().prerelease and "nightly" or "stable" +end + +---@return LuaDevOptions +function M.merge(options) + return vim.tbl_deep_extend("force", {}, M.options, options or {}) +end + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/init.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/init.lua new file mode 100644 index 00000000..35ce21fe --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/init.lua @@ -0,0 +1,39 @@ +local M = {} + +local function neoconf(config) + pcall(function() + require("neoconf.plugins").register({ + on_schema = function(schema) + schema:import("neodev", config.defaults) + schema:set("neodev.library.plugins", { + description = "true/false or an array of plugin names to enable", + anyOf = { + { type = "boolean" }, + { type = "array", items = { type = "string" } }, + }, + }) + end, + }) + end) +end + +---@param opts? LuaDevOptions +function M.setup(opts) + local config = require("neodev.config") + config.setup(opts) + + if config.options.lspconfig then + require("neodev.lsp").setup() + end + + neoconf(config) + + -- leave this for now for backward compatibility + return { + settings = { + legacy = true, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/lsp.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/lsp.lua new file mode 100644 index 00000000..c4785e5e --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/lsp.lua @@ -0,0 +1,115 @@ +local util = require("neodev.util") + +local M = {} + +function M.setup() + local opts = require("neodev.config").options + + local lsputil = require("lspconfig.util") + local hook = lsputil.add_hook_after + lsputil.on_setup = hook(lsputil.on_setup, function(config) + if opts.setup_jsonls and config.name == "jsonls" then + M.setup_jsonls(config) + end + if config.name == "lua_ls" then + config.on_new_config = hook(config.on_new_config, M.on_new_config) + -- config.before_init = hook(config.before_init, M.before_init) + end + end) +end + +function M.setup_jsonls(config) + local schemas = config.settings.json and config.settings.json.schemas or {} + table.insert(schemas, { + name = "LuaLS Settings", + url = "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", + fileMatch = { ".luarc.json", ".luarc.jsonc" }, + }) + config.settings = vim.tbl_deep_extend("force", config.settings, { + json = { + schemas = schemas, + validate = { + enable = true, + }, + }, + }) +end + +function M.before_init(params, config) + M.on_new_config(config, params.rootPath) +end + +function M.on_new_config(config, root_dir) + -- don't do anything when old style setup was used + if config.settings.legacy then + util.warn( + "You're using the old way of setting up neodev (previously lua-dev).\nPlease check the docs at https://github.com/folke/neodev.nvim#-setup" + ) + return + end + + local lua_root = util.find_root() + + local opts = require("neodev.config").merge() + + opts.library.enabled = util.is_nvim_config() + + if not opts.library.enabled and lua_root then + opts.library.enabled = true + opts.library.plugins = false + end + + pcall(function() + opts = require("neoconf").get("neodev", opts, { file = root_dir }) + end) + + pcall(opts.override, root_dir, opts.library) + + local library = config.settings + and config.settings.Lua + and config.settings.Lua.workspace + and config.settings.Lua.workspace.library + or {} + + local ignoreDir = config.settings + and config.settings.Lua + and config.settings.Lua.workspace + and config.settings.Lua.workspace.ignoreDir + or {} + + if opts.library.enabled then + config.handlers = vim.tbl_extend("force", {}, config.handlers or {}) + config.handlers["workspace/configuration"] = config.handlers["workspace/configuration"] + or function(err, result, ctx, cfg) + local ret = vim.lsp.handlers["workspace/configuration"](err, result, ctx, cfg) + ret = vim.deepcopy(ret) + -- when scopeUri is not set, then the requested config is for the fallback scope + -- Don't set workspace libraries for the fallback scope + -- Thanks to: https://github.com/LuaLS/lua-language-server/issues/1596#issuecomment-1855087288 + for i, item in ipairs(result.items) do + if type(ret[i]) == "table" then + if not item.scopeUri and ret[i].workspace then + ret[i].workspace.library = nil + end + end + end + return ret + end + + config.settings = + vim.tbl_deep_extend("force", config.settings or {}, require("neodev.luals").setup(opts, config.settings).settings) + for _, lib in ipairs(library) do + table.insert(config.settings.Lua.workspace.library, lib) + end + + if require("neodev.config").options.pathStrict and lua_root then + table.insert(config.settings.Lua.workspace.library, lua_root) + end + + for _, dir in ipairs(ignoreDir) do + table.insert(config.settings.Lua.workspace.ignoreDir, dir) + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/luals.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/luals.lua new file mode 100644 index 00000000..76ad4f5f --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/luals.lua @@ -0,0 +1,110 @@ +local config = require("neodev.config") + +local M = {} + +---@param opts LuaDevOptions +function M.library(opts) + opts = config.merge(opts) + local ret = {} + + if opts.library.types then + table.insert(ret, config.types()) + end + + local function add(lib, filter) + ---@diagnostic disable-next-line: param-type-mismatch + for _, p in ipairs(vim.fn.expand(lib .. "/lua", false, true)) do + local plugin_name = vim.fn.fnamemodify(p, ":h:t") + p = vim.loop.fs_realpath(p) + if p and (not filter or filter[plugin_name]) then + if config.options.pathStrict then + table.insert(ret, p) + else + table.insert(ret, vim.fn.fnamemodify(p, ":h")) + end + end + end + end + + if opts.library.runtime then + add(type(opts.library.runtime) == "string" and opts.library.runtime or "$VIMRUNTIME") + end + + if opts.library.plugins then + ---@type table + local filter + if type(opts.library.plugins) == "table" then + filter = {} + for _, p in pairs(opts.library.plugins) do + filter[p] = true + end + end + for _, site in pairs(vim.split(vim.o.packpath, ",")) do + add(site .. "/pack/*/opt/*", filter) + add(site .. "/pack/*/start/*", filter) + end + -- add support for lazy.nvim + if package.loaded["lazy"] then + for _, plugin in ipairs(require("lazy").plugins()) do + add(plugin.dir, filter) + end + end + end + + return ret +end + +---@param settings? lspconfig.settings.lua_ls +function M.path(settings) + if config.options.pathStrict then + return { "?.lua", "?/init.lua" } + end + + settings = settings or {} + local runtime = settings.Lua and settings.Lua.runtime or {} + local meta = runtime.meta or "${version} ${language} ${encoding}" + meta = meta:gsub("%${version}", runtime.version or "LuaJIT") + meta = meta:gsub("%${language}", "en-us") + meta = meta:gsub("%${encoding}", runtime.fileEncoding or "utf8") + + return { + -- paths for builtin libraries + ("meta/%s/?.lua"):format(meta), + ("meta/%s/?/init.lua"):format(meta), + -- paths for meta/3rd libraries + "library/?.lua", + "library/?/init.lua", + -- Neovim lua files, config and plugins + "lua/?.lua", + "lua/?/init.lua", + } +end + +---@param opts? LuaDevOptions +---@param settings? lspconfig.settings.lua_ls +function M.setup(opts, settings) + opts = config.merge(opts) + return { + ---@type lspconfig.settings.lua_ls + settings = { + Lua = { + runtime = { + version = "LuaJIT", + path = M.path(settings), + pathStrict = config.options.pathStrict, + }, + ---@diagnostic disable-next-line: undefined-field + completion = opts.snippet and { callSnippet = "Replace" } or nil, + workspace = { + -- Make the server aware of Neovim runtime files + library = M.library(opts), + -- when pathStrict=false, we need to add the other types to ignoreDir, + -- otherwise they get indexed + ignoreDir = { config.version() == "stable" and "types/nightly" or "types/stable", "lua" }, + }, + }, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/util.lua b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/util.lua new file mode 100644 index 00000000..c06fb941 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/lua/neodev/util.lua @@ -0,0 +1,87 @@ +local config = require("neodev.config") + +local M = {} + +--- find the root directory that has /lua +---@param path string? +---@return string? +function M.find_root(path) + path = path or vim.api.nvim_buf_get_name(0) + return vim.fs.find({ "lua" }, { path = path, upward = true, type = "directory" })[1] +end + +function M.fetch(url) + local fd = io.popen(string.format("curl -s -k %q", url)) + if not fd then + error(("Could not download %s"):format(url)) + end + local ret = fd:read("*a") + fd:close() + return ret +end + +function M.is_nvim_config() + local path = vim.loop.fs_realpath(vim.api.nvim_buf_get_name(0)) + if path then + path = vim.fs.normalize(path) + local config_root = vim.loop.fs_realpath(vim.fn.stdpath("config")) or vim.fn.stdpath("config") + config_root = vim.fs.normalize(config_root) + return path:find(config_root, 1, true) == 1 + end + return false +end + +function M.keys(tbl) + local ret = vim.tbl_keys(tbl) + table.sort(ret) + return ret +end + +---@generic K +---@generic V +---@param tbl table +---@param fn fun(key: K, value: V) +function M.for_each(tbl, fn) + local keys = M.keys(tbl) + for _, key in ipairs(keys) do + fn(key, tbl[key]) + end +end + +---@param file string +---@param flags? string +---@return string +function M.read_file(file, flags) + local fd = io.open(file, "r" .. (flags or "")) + if not fd then + error(("Could not open file %s for reading"):format(file)) + end + local data = fd:read("*a") + fd:close() + return data +end + +function M.write_file(file, data) + local fd = io.open(file, "w+") + if not fd then + error(("Could not open file %s for writing"):format(file)) + end + fd:write(data) + fd:close() +end + +function M.debug(msg) + if config.options.debug then + M.error(msg) + end +end + +function M.error(msg) + vim.notify_once(msg, vim.log.levels.ERROR, { title = "neodev.nvim" }) +end + +function M.warn(msg) + vim.notify_once(msg, vim.log.levels.WARN, { title = "neodev.nvim" }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/selene.toml b/config/neovim/store/lazy-plugins/neodev.nvim/selene.toml new file mode 100644 index 00000000..6540d6f9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/selene.toml @@ -0,0 +1 @@ +std="lua51+vim" diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/stylua.toml b/config/neovim/store/lazy-plugins/neodev.nvim/stylua.toml new file mode 100644 index 00000000..5d6c50dc --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/stylua.toml @@ -0,0 +1,3 @@ +indent_type = "Spaces" +indent_width = 2 +column_width = 120 \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/alias.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/alias.lua new file mode 100644 index 00000000..68f7752b --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/alias.lua @@ -0,0 +1,11 @@ +---@meta + +---@alias blob number +---@alias buffer integer +---@alias channel integer +---@alias float number +---@alias job number +---@alias object any +---@alias sends number +---@alias tabpage integer +---@alias window integer diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/cmd.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/cmd.lua new file mode 100644 index 00000000..bba787e7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/cmd.lua @@ -0,0 +1,3086 @@ +---@meta + +-- append text +function vim.cmd.a(...)end + +-- enter abbreviation +function vim.cmd.ab(...)end + +-- enter abbreviation +function vim.cmd.abbreviate(...)end + +-- remove all abbreviations +function vim.cmd.abc(...)end + +-- remove all abbreviations +function vim.cmd.abclear(...)end + +-- make split window appear left or above +function vim.cmd.abo(...)end + +-- make split window appear left or above +function vim.cmd.aboveleft(...)end + +-- open a window for each file in the argument +function vim.cmd.al(...)end + +-- open a window for each file in the argument +function vim.cmd.all(...)end + +-- enter new menu item for all modes +function vim.cmd.am(...)end + +-- enter new menu item for all modes +function vim.cmd.amenu(...)end + +-- enter a new menu for all modes that will not +function vim.cmd.an(...)end + +-- enter a new menu for all modes that will not +function vim.cmd.anoremenu(...)end + +-- append text +function vim.cmd.append(...)end + +-- print the argument list +function vim.cmd.ar(...)end + +-- add items to the argument list +function vim.cmd.arga(...)end + +-- add items to the argument list +function vim.cmd.argadd(...)end + +-- delete items from the argument list +function vim.cmd.argd(...)end + +-- remove duplicates from the argument list +function vim.cmd.argded(...)end + +-- remove duplicates from the argument list +function vim.cmd.argdedupe(...)end + +-- delete items from the argument list +function vim.cmd.argdelete(...)end + +-- do a command on all items in the argument list +function vim.cmd.argdo(...)end + +-- add item to the argument list and edit it +function vim.cmd.arge(...)end + +-- add item to the argument list and edit it +function vim.cmd.argedit(...)end + +-- define the global argument list +function vim.cmd.argg(...)end + +-- define the global argument list +function vim.cmd.argglobal(...)end + +-- define a local argument list +function vim.cmd.argl(...)end + +-- define a local argument list +function vim.cmd.arglocal(...)end + +-- print the argument list +function vim.cmd.args(...)end + +-- go to specific file in the argument list +function vim.cmd.argu(...)end + +-- go to specific file in the argument list +function vim.cmd.argument(...)end + +-- print ascii value of character under the cursor +function vim.cmd.as(...)end + +-- print ascii value of character under the cursor +function vim.cmd.ascii(...)end + +-- enter or show autocommands +function vim.cmd.au(...)end + +-- select the autocommand group to use +function vim.cmd.aug(...)end + +-- select the autocommand group to use +function vim.cmd.augroup(...)end + +-- remove menu for all modes +function vim.cmd.aun(...)end + +-- remove menu for all modes +function vim.cmd.aunmenu(...)end + +-- enter or show autocommands +function vim.cmd.autocmd(...)end + +-- go to specific buffer in the buffer list +function vim.cmd.b(...)end + +-- go to previous buffer in the buffer list +function vim.cmd.bN(...)end + +-- go to previous buffer in the buffer list +function vim.cmd.bNext(...)end + +-- open a window for each buffer in the buffer list +function vim.cmd.ba(...)end + +-- add buffer to the buffer list +function vim.cmd.bad(...)end + +-- add buffer to the buffer list +function vim.cmd.badd(...)end + +-- open a window for each buffer in the buffer list +function vim.cmd.ball(...)end + +-- like ":badd" but also set the alternate file +function vim.cmd.balt(...)end + +-- remove a buffer from the buffer list +function vim.cmd.bd(...)end + +-- remove a buffer from the buffer list +function vim.cmd.bdelete(...)end + +-- make split window appear right or below +function vim.cmd.bel(...)end + +-- make split window appear right or below +function vim.cmd.belowright(...)end + +-- go to first buffer in the buffer list +function vim.cmd.bf(...)end + +-- go to first buffer in the buffer list +function vim.cmd.bfirst(...)end + +-- go to last buffer in the buffer list +function vim.cmd.bl(...)end + +-- go to last buffer in the buffer list +function vim.cmd.blast(...)end + +-- go to next buffer in the buffer list that has +function vim.cmd.bm(...)end + +-- go to next buffer in the buffer list that has +function vim.cmd.bmodified(...)end + +-- go to next buffer in the buffer list +function vim.cmd.bn(...)end + +-- go to next buffer in the buffer list +function vim.cmd.bnext(...)end + +-- make split window appear at bottom or far right +function vim.cmd.bo(...)end + +-- make split window appear at bottom or far right +function vim.cmd.botright(...)end + +-- go to previous buffer in the buffer list +function vim.cmd.bp(...)end + +-- go to previous buffer in the buffer list +function vim.cmd.bprevious(...)end + +-- go to first buffer in the buffer list +function vim.cmd.br(...)end + +-- break out of while loop +function vim.cmd.brea(...)end + +-- break out of while loop +vim.cmd["break"] = function(...)end + +-- add a debugger breakpoint +function vim.cmd.breaka(...)end + +-- add a debugger breakpoint +function vim.cmd.breakadd(...)end + +-- delete a debugger breakpoint +function vim.cmd.breakd(...)end + +-- delete a debugger breakpoint +function vim.cmd.breakdel(...)end + +-- list debugger breakpoints +function vim.cmd.breakl(...)end + +-- list debugger breakpoints +function vim.cmd.breaklist(...)end + +-- go to first buffer in the buffer list +function vim.cmd.brewind(...)end + +-- use file selection dialog +function vim.cmd.bro(...)end + +-- use file selection dialog +function vim.cmd.browse(...)end + +-- execute command in each listed buffer +function vim.cmd.bufdo(...)end + +-- go to specific buffer in the buffer list +function vim.cmd.buffer(...)end + +-- list all files in the buffer list +function vim.cmd.buffers(...)end + +-- unload a specific buffer +function vim.cmd.bun(...)end + +-- unload a specific buffer +function vim.cmd.bunload(...)end + +-- really delete a buffer +function vim.cmd.bw(...)end + +-- really delete a buffer +function vim.cmd.bwipeout(...)end + +-- replace a line or series of lines +function vim.cmd.c(...)end + +-- go to previous error +function vim.cmd.cN(...)end + +-- go to previous error +function vim.cmd.cNext(...)end + +-- go to last error in previous file +function vim.cmd.cNf(...)end + +-- go to last error in previous file +function vim.cmd.cNfile(...)end + +-- like ":abbreviate" but for Command-line mode +function vim.cmd.ca(...)end + +-- like ":abbreviate" but for Command-line mode +function vim.cmd.cabbrev(...)end + +-- clear all abbreviations for Command-line mode +function vim.cmd.cabc(...)end + +-- clear all abbreviations for Command-line mode +function vim.cmd.cabclear(...)end + +-- go to error above current line +function vim.cmd.cabo(...)end + +-- go to error above current line +function vim.cmd.cabove(...)end + +-- add errors from buffer +function vim.cmd.cad(...)end + +-- add errors from buffer +function vim.cmd.caddbuffer(...)end + +-- add errors from expr +function vim.cmd.cadde(...)end + +-- add errors from expr +function vim.cmd.caddexpr(...)end + +-- add error message to current quickfix list +function vim.cmd.caddf(...)end + +-- add error message to current quickfix list +function vim.cmd.caddfile(...)end + +-- go to error after current cursor +function vim.cmd.caf(...)end + +-- go to error after current cursor +function vim.cmd.cafter(...)end + +-- call a function +function vim.cmd.cal(...)end + +-- call a function +function vim.cmd.call(...)end + +-- part of a :try command +function vim.cmd.cat(...)end + +-- part of a :try command +function vim.cmd.catch(...)end + +-- parse error messages and jump to first error +function vim.cmd.cb(...)end + +-- go to error before current cursor +function vim.cmd.cbef(...)end + +-- go to error before current cursor +function vim.cmd.cbefore(...)end + +-- go to error below current line +function vim.cmd.cbel(...)end + +-- go to error below current line +function vim.cmd.cbelow(...)end + +-- scroll to the bottom of the quickfix window +function vim.cmd.cbo(...)end + +-- scroll to the bottom of the quickfix window +function vim.cmd.cbottom(...)end + +-- parse error messages and jump to first error +function vim.cmd.cbuffer(...)end + +-- go to specific error +function vim.cmd.cc(...)end + +-- close quickfix window +function vim.cmd.ccl(...)end + +-- close quickfix window +function vim.cmd.cclose(...)end + +-- change directory +function vim.cmd.cd(...)end + +-- execute command in each valid error list entry +function vim.cmd.cdo(...)end + +-- format lines at the center +function vim.cmd.ce(...)end + +-- format lines at the center +function vim.cmd.center(...)end + +-- read errors from expr and jump to first +function vim.cmd.cex(...)end + +-- read errors from expr and jump to first +function vim.cmd.cexpr(...)end + +-- read file with error messages and jump to first +function vim.cmd.cf(...)end + +-- execute command in each file in error list +function vim.cmd.cfd(...)end + +-- execute command in each file in error list +function vim.cmd.cfdo(...)end + +-- read file with error messages and jump to first +function vim.cmd.cfile(...)end + +-- go to the specified error, default first one +function vim.cmd.cfir(...)end + +-- go to the specified error, default first one +function vim.cmd.cfirst(...)end + +-- read file with error messages +function vim.cmd.cg(...)end + +-- get errors from buffer +function vim.cmd.cgetb(...)end + +-- get errors from buffer +function vim.cmd.cgetbuffer(...)end + +-- get errors from expr +function vim.cmd.cgete(...)end + +-- get errors from expr +function vim.cmd.cgetexpr(...)end + +-- read file with error messages +function vim.cmd.cgetfile(...)end + +-- replace a line or series of lines +function vim.cmd.change(...)end + +-- print the change list +function vim.cmd.changes(...)end + +-- change directory +function vim.cmd.chd(...)end + +-- change directory +function vim.cmd.chdir(...)end + +-- run healthchecks +function vim.cmd.che(...)end + +-- run healthchecks +function vim.cmd.checkhealth(...)end + +-- list included files +function vim.cmd.checkp(...)end + +-- list included files +function vim.cmd.checkpath(...)end + +-- check timestamp of loaded buffers +function vim.cmd.checkt(...)end + +-- check timestamp of loaded buffers +function vim.cmd.checktime(...)end + +-- list the error lists +function vim.cmd.chi(...)end + +-- list the error lists +function vim.cmd.chistory(...)end + +-- list all errors +function vim.cmd.cl(...)end + +-- go to the specified error, default last one +function vim.cmd.cla(...)end + +-- go to the specified error, default last one +function vim.cmd.clast(...)end + +-- clear the jump list +function vim.cmd.cle(...)end + +-- clear the jump list +function vim.cmd.clearjumps(...)end + +-- list all errors +function vim.cmd.clist(...)end + +-- close current window +function vim.cmd.clo(...)end + +-- close current window +function vim.cmd.close(...)end + +-- like ":map" but for Command-line mode +function vim.cmd.cm(...)end + +-- like ":map" but for Command-line mode +function vim.cmd.cmap(...)end + +-- clear all mappings for Command-line mode +function vim.cmd.cmapc(...)end + +-- clear all mappings for Command-line mode +function vim.cmd.cmapclear(...)end + +-- add menu for Command-line mode +function vim.cmd.cme(...)end + +-- add menu for Command-line mode +function vim.cmd.cmenu(...)end + +-- go to next error +function vim.cmd.cn(...)end + +-- go to newer error list +function vim.cmd.cnew(...)end + +-- go to newer error list +function vim.cmd.cnewer(...)end + +-- go to next error +function vim.cmd.cnext(...)end + +-- go to first error in next file +function vim.cmd.cnf(...)end + +-- go to first error in next file +function vim.cmd.cnfile(...)end + +-- like ":noremap" but for Command-line mode +function vim.cmd.cno(...)end + +-- like ":noreabbrev" but for Command-line mode +function vim.cmd.cnorea(...)end + +-- like ":noreabbrev" but for Command-line mode +function vim.cmd.cnoreabbrev(...)end + +-- like ":noremap" but for Command-line mode +function vim.cmd.cnoremap(...)end + +-- like ":noremenu" but for Command-line mode +function vim.cmd.cnoreme(...)end + +-- like ":noremenu" but for Command-line mode +function vim.cmd.cnoremenu(...)end + +-- copy lines +function vim.cmd.co(...)end + +-- go to older error list +function vim.cmd.col(...)end + +-- go to older error list +function vim.cmd.colder(...)end + +-- load a specific color scheme +function vim.cmd.colo(...)end + +-- load a specific color scheme +function vim.cmd.colorscheme(...)end + +-- create user-defined command +function vim.cmd.com(...)end + +-- clear all user-defined commands +function vim.cmd.comc(...)end + +-- clear all user-defined commands +function vim.cmd.comclear(...)end + +-- create user-defined command +function vim.cmd.command(...)end + +-- do settings for a specific compiler +function vim.cmd.comp(...)end + +-- do settings for a specific compiler +function vim.cmd.compiler(...)end + +-- go back to :while +function vim.cmd.con(...)end + +-- prompt user when confirmation required +function vim.cmd.conf(...)end + +-- prompt user when confirmation required +function vim.cmd.confirm(...)end + +-- create a variable as a constant +function vim.cmd.cons(...)end + +-- create a variable as a constant +function vim.cmd.const(...)end + +-- go back to :while +function vim.cmd.continue(...)end + +-- open quickfix window +function vim.cmd.cope(...)end + +-- open quickfix window +function vim.cmd.copen(...)end + +-- copy lines +function vim.cmd.copy(...)end + +-- go to previous error +function vim.cmd.cp(...)end + +-- go to last error in previous file +function vim.cmd.cpf(...)end + +-- go to last error in previous file +function vim.cmd.cpfile(...)end + +-- go to previous error +function vim.cmd.cprevious(...)end + +-- quit Vim with an error code +function vim.cmd.cq(...)end + +-- quit Vim with an error code +function vim.cmd.cquit(...)end + +-- go to the specified error, default first one +function vim.cmd.cr(...)end + +-- go to the specified error, default first one +function vim.cmd.crewind(...)end + +-- like ":unmap" but for Command-line mode +function vim.cmd.cu(...)end + +-- like ":unabbrev" but for Command-line mode +function vim.cmd.cuna(...)end + +-- like ":unabbrev" but for Command-line mode +function vim.cmd.cunabbrev(...)end + +-- like ":unmap" but for Command-line mode +function vim.cmd.cunmap(...)end + +-- remove menu for Command-line mode +function vim.cmd.cunme(...)end + +-- remove menu for Command-line mode +function vim.cmd.cunmenu(...)end + +-- open or close quickfix window +function vim.cmd.cw(...)end + +-- open or close quickfix window +function vim.cmd.cwindow(...)end + +-- short for |:delete| with the 'p' flag +function vim.cmd.d(...)end + +-- run a command in debugging mode +function vim.cmd.deb(...)end + +-- run a command in debugging mode +function vim.cmd.debug(...)end + +-- read debug mode commands from normal input +function vim.cmd.debugg(...)end + +-- read debug mode commands from normal input +function vim.cmd.debuggreedy(...)end + +-- delete user-defined command +function vim.cmd.delc(...)end + +-- delete user-defined command +function vim.cmd.delcommand(...)end + +-- delete lines +function vim.cmd.delete(...)end + +-- short for |:delete| with the 'p' flag +function vim.cmd.deletep(...)end + +-- delete a user function +function vim.cmd.delf(...)end + +-- delete a user function +function vim.cmd.delfunction(...)end + +-- delete marks +function vim.cmd.delm(...)end + +-- delete marks +function vim.cmd.delmarks(...)end + +-- display registers +function vim.cmd.di(...)end + +-- update 'diff' buffers +function vim.cmd.dif(...)end + +-- remove differences in current buffer +function vim.cmd.diffg(...)end + +-- remove differences in current buffer +function vim.cmd.diffget(...)end + +-- switch off diff mode +function vim.cmd.diffo(...)end + +-- switch off diff mode +function vim.cmd.diffoff(...)end + +-- apply a patch and show differences +function vim.cmd.diffp(...)end + +-- apply a patch and show differences +function vim.cmd.diffpatch(...)end + +-- remove differences in other buffer +function vim.cmd.diffpu(...)end + +-- remove differences in other buffer +function vim.cmd.diffput(...)end + +-- show differences with another file +function vim.cmd.diffs(...)end + +-- show differences with another file +function vim.cmd.diffsplit(...)end + +-- make current window a diff window +function vim.cmd.diffthis(...)end + +-- update 'diff' buffers +function vim.cmd.diffupdate(...)end + +-- show or enter digraphs +function vim.cmd.dig(...)end + +-- show or enter digraphs +function vim.cmd.digraphs(...)end + +-- display registers +function vim.cmd.display(...)end + +-- jump to #define +function vim.cmd.dj(...)end + +-- jump to #define +function vim.cmd.djump(...)end + +-- short for |:delete| with the 'l' flag +function vim.cmd.dl(...)end + +-- list #defines +function vim.cmd.dli(...)end + +-- list #defines +function vim.cmd.dlist(...)end + +-- apply autocommands to current buffer +vim.cmd["do"] = function(...)end + +-- apply autocommands for all loaded buffers +function vim.cmd.doautoa(...)end + +-- apply autocommands for all loaded buffers +function vim.cmd.doautoall(...)end + +-- apply autocommands to current buffer +function vim.cmd.doautocmd(...)end + +-- jump to window editing file or edit file in +function vim.cmd.dr(...)end + +-- jump to window editing file or edit file in +function vim.cmd.drop(...)end + +-- list one #define +function vim.cmd.ds(...)end + +-- list one #define +function vim.cmd.dsearch(...)end + +-- split window and jump to #define +function vim.cmd.dsp(...)end + +-- split window and jump to #define +function vim.cmd.dsplit(...)end + +-- edit a file +function vim.cmd.e(...)end + +-- go to older change, undo +function vim.cmd.ea(...)end + +-- go to older change, undo +function vim.cmd.earlier(...)end + +-- echoes the result of expressions +function vim.cmd.ec(...)end + +-- echoes the result of expressions +function vim.cmd.echo(...)end + +-- like :echo, show like an error and use history +function vim.cmd.echoe(...)end + +-- like :echo, show like an error and use history +function vim.cmd.echoerr(...)end + +-- set highlighting for echo commands +function vim.cmd.echoh(...)end + +-- set highlighting for echo commands +function vim.cmd.echohl(...)end + +-- same as :echo, put message in history +function vim.cmd.echom(...)end + +-- same as :echo, put message in history +function vim.cmd.echomsg(...)end + +-- same as :echo, but without +function vim.cmd.echon(...)end + +-- edit a file +function vim.cmd.edit(...)end + +-- part of an :if command +function vim.cmd.el(...)end + +-- part of an :if command +vim.cmd["else"] = function(...)end + +-- part of an :if command +function vim.cmd.elsei(...)end + +-- part of an :if command +vim.cmd["elseif"] = function(...)end + +-- execute a menu by name +function vim.cmd.em(...)end + +-- execute a menu by name +function vim.cmd.emenu(...)end + +-- end previous :if +function vim.cmd.en(...)end + +-- end of a user function started with :function +function vim.cmd.endf(...)end + +-- end previous :for +function vim.cmd.endfo(...)end + +-- end previous :for +function vim.cmd.endfor(...)end + +-- end of a user function started with :function +function vim.cmd.endfunction(...)end + +-- end previous :if +function vim.cmd.endif(...)end + +-- end previous :try +function vim.cmd.endt(...)end + +-- end previous :try +function vim.cmd.endtry(...)end + +-- end previous :while +function vim.cmd.endw(...)end + +-- end previous :while +function vim.cmd.endwhile(...)end + +-- edit a new, unnamed buffer +function vim.cmd.ene(...)end + +-- edit a new, unnamed buffer +function vim.cmd.enew(...)end + +-- evaluate an expression and discard the result +function vim.cmd.ev(...)end + +-- evaluate an expression and discard the result +function vim.cmd.eval(...)end + +-- same as ":edit" +function vim.cmd.ex(...)end + +-- execute result of expressions +function vim.cmd.exe(...)end + +-- execute result of expressions +function vim.cmd.execute(...)end + +-- same as ":xit" +function vim.cmd.exi(...)end + +-- same as ":xit" +function vim.cmd.exit(...)end + +-- overview of Ex commands +function vim.cmd.exu(...)end + +-- overview of Ex commands +function vim.cmd.exusage(...)end + +-- show or set the current file name +function vim.cmd.f(...)end + +-- close floating window +function vim.cmd.fc(...)end + +-- close floating window +function vim.cmd.fclose(...)end + +-- show or set the current file name +function vim.cmd.file(...)end + +-- list all files in the buffer list +function vim.cmd.files(...)end + +-- switch file type detection on/off +function vim.cmd.filet(...)end + +-- switch file type detection on/off +function vim.cmd.filetype(...)end + +-- filter output of following command +function vim.cmd.filt(...)end + +-- filter output of following command +function vim.cmd.filter(...)end + +-- find file in 'path' and edit it +function vim.cmd.fin(...)end + +-- part of a :try command +function vim.cmd.fina(...)end + +-- part of a :try command +function vim.cmd.finally(...)end + +-- find file in 'path' and edit it +function vim.cmd.find(...)end + +-- quit sourcing a Vim script +function vim.cmd.fini(...)end + +-- quit sourcing a Vim script +function vim.cmd.finish(...)end + +-- go to the first file in the argument list +function vim.cmd.fir(...)end + +-- go to the first file in the argument list +function vim.cmd.first(...)end + +-- create a fold +function vim.cmd.fo(...)end + +-- create a fold +function vim.cmd.fold(...)end + +-- close folds +function vim.cmd.foldc(...)end + +-- close folds +function vim.cmd.foldclose(...)end + +-- execute command on lines not in a closed fold +function vim.cmd.foldd(...)end + +-- execute command on lines in a closed fold +function vim.cmd.folddoc(...)end + +-- execute command on lines in a closed fold +function vim.cmd.folddoclosed(...)end + +-- execute command on lines not in a closed fold +function vim.cmd.folddoopen(...)end + +-- open folds +function vim.cmd.foldo(...)end + +-- open folds +function vim.cmd.foldopen(...)end + +-- for loop +vim.cmd["for"] = function(...)end + +-- define a user function +function vim.cmd.fu(...)end + +-- define a user function +vim.cmd["function"] = function(...)end + +-- execute commands for matching lines +function vim.cmd.g(...)end + +-- execute commands for matching lines +function vim.cmd.global(...)end + +-- go to byte in the buffer +function vim.cmd.go(...)end + +-- go to byte in the buffer +vim.cmd["goto"] = function(...)end + +-- run 'grepprg' and jump to first match +function vim.cmd.gr(...)end + +-- run 'grepprg' and jump to first match +function vim.cmd.grep(...)end + +-- like :grep, but append to current list +function vim.cmd.grepa(...)end + +-- like :grep, but append to current list +function vim.cmd.grepadd(...)end + +-- start the GUI +function vim.cmd.gu(...)end + +-- start the GUI +function vim.cmd.gui(...)end + +-- start the GUI +function vim.cmd.gv(...)end + +-- start the GUI +function vim.cmd.gvim(...)end + +-- open a help window +function vim.cmd.h(...)end + +-- open a help window +function vim.cmd.help(...)end + +-- close one help window +function vim.cmd.helpc(...)end + +-- close one help window +function vim.cmd.helpclose(...)end + +-- like ":grep" but searches help files +function vim.cmd.helpg(...)end + +-- like ":grep" but searches help files +function vim.cmd.helpgrep(...)end + +-- generate help tags for a directory +function vim.cmd.helpt(...)end + +-- generate help tags for a directory +function vim.cmd.helptags(...)end + +-- specify highlighting methods +function vim.cmd.hi(...)end + +-- hide current buffer for a command +function vim.cmd.hid(...)end + +-- hide current buffer for a command +function vim.cmd.hide(...)end + +-- specify highlighting methods +function vim.cmd.highlight(...)end + +-- print a history list +function vim.cmd.his(...)end + +-- print a history list +function vim.cmd.history(...)end + +-- following window command work horizontally +function vim.cmd.hor(...)end + +-- following window command work horizontally +function vim.cmd.horizontal(...)end + +-- insert text +function vim.cmd.i(...)end + +-- like ":abbrev" but for Insert mode +function vim.cmd.ia(...)end + +-- like ":abbrev" but for Insert mode +function vim.cmd.iabbrev(...)end + +-- like ":abclear" but for Insert mode +function vim.cmd.iabc(...)end + +-- like ":abclear" but for Insert mode +function vim.cmd.iabclear(...)end + +-- execute commands when condition met +vim.cmd["if"] = function(...)end + +-- jump to definition of identifier +function vim.cmd.ij(...)end + +-- jump to definition of identifier +function vim.cmd.ijump(...)end + +-- list lines where identifier matches +function vim.cmd.il(...)end + +-- list lines where identifier matches +function vim.cmd.ilist(...)end + +-- like ":map" but for Insert mode +function vim.cmd.im(...)end + +-- like ":map" but for Insert mode +function vim.cmd.imap(...)end + +-- like ":mapclear" but for Insert mode +function vim.cmd.imapc(...)end + +-- like ":mapclear" but for Insert mode +function vim.cmd.imapclear(...)end + +-- add menu for Insert mode +function vim.cmd.ime(...)end + +-- add menu for Insert mode +function vim.cmd.imenu(...)end + +-- like ":noremap" but for Insert mode +function vim.cmd.ino(...)end + +-- like ":noreabbrev" but for Insert mode +function vim.cmd.inorea(...)end + +-- like ":noreabbrev" but for Insert mode +function vim.cmd.inoreabbrev(...)end + +-- like ":noremap" but for Insert mode +function vim.cmd.inoremap(...)end + +-- like ":noremenu" but for Insert mode +function vim.cmd.inoreme(...)end + +-- like ":noremenu" but for Insert mode +function vim.cmd.inoremenu(...)end + +-- insert text +function vim.cmd.insert(...)end + +-- print the introductory message +function vim.cmd.int(...)end + +-- print the introductory message +function vim.cmd.intro(...)end + +-- list one line where identifier matches +function vim.cmd.is(...)end + +-- list one line where identifier matches +function vim.cmd.isearch(...)end + +-- split window and jump to definition of +function vim.cmd.isp(...)end + +-- split window and jump to definition of +function vim.cmd.isplit(...)end + +-- like ":unmap" but for Insert mode +function vim.cmd.iu(...)end + +-- like ":unabbrev" but for Insert mode +function vim.cmd.iuna(...)end + +-- like ":unabbrev" but for Insert mode +function vim.cmd.iunabbrev(...)end + +-- like ":unmap" but for Insert mode +function vim.cmd.iunmap(...)end + +-- remove menu for Insert mode +function vim.cmd.iunme(...)end + +-- remove menu for Insert mode +function vim.cmd.iunmenu(...)end + +-- join lines +function vim.cmd.j(...)end + +-- join lines +function vim.cmd.join(...)end + +-- print the jump list +function vim.cmd.ju(...)end + +-- print the jump list +function vim.cmd.jumps(...)end + +-- set a mark +function vim.cmd.k(...)end + +-- following command keeps marks where they are +function vim.cmd.kee(...)end + +-- following command keeps the alternate file +function vim.cmd.keepa(...)end + +-- following command keeps the alternate file +function vim.cmd.keepalt(...)end + +-- following command keeps jumplist and marks +function vim.cmd.keepj(...)end + +-- following command keeps jumplist and marks +function vim.cmd.keepjumps(...)end + +-- following command keeps marks where they are +function vim.cmd.keepmarks(...)end + +-- following command keeps search pattern history +function vim.cmd.keepp(...)end + +-- following command keeps search pattern history +function vim.cmd.keeppatterns(...)end + +-- print lines +function vim.cmd.l(...)end + +-- go to previous entry in location list +function vim.cmd.lN(...)end + +-- go to previous entry in location list +function vim.cmd.lNext(...)end + +-- go to last entry in previous file +function vim.cmd.lNf(...)end + +-- go to last entry in previous file +function vim.cmd.lNfile(...)end + +-- go to the last file in the argument list +function vim.cmd.la(...)end + +-- go to location above current line +function vim.cmd.lab(...)end + +-- go to location above current line +function vim.cmd.labove(...)end + +-- add locations from expr +function vim.cmd.lad(...)end + +-- add locations from buffer +function vim.cmd.laddb(...)end + +-- add locations from buffer +function vim.cmd.laddbuffer(...)end + +-- add locations from expr +function vim.cmd.laddexpr(...)end + +-- add locations to current location list +function vim.cmd.laddf(...)end + +-- add locations to current location list +function vim.cmd.laddfile(...)end + +-- go to location after current cursor +function vim.cmd.laf(...)end + +-- go to location after current cursor +function vim.cmd.lafter(...)end + +-- set the language (locale) +function vim.cmd.lan(...)end + +-- set the language (locale) +function vim.cmd.language(...)end + +-- go to the last file in the argument list +function vim.cmd.last(...)end + +-- go to newer change, redo +function vim.cmd.lat(...)end + +-- go to newer change, redo +function vim.cmd.later(...)end + +-- parse locations and jump to first location +function vim.cmd.lb(...)end + +-- go to location before current cursor +function vim.cmd.lbef(...)end + +-- go to location before current cursor +function vim.cmd.lbefore(...)end + +-- go to location below current line +function vim.cmd.lbel(...)end + +-- go to location below current line +function vim.cmd.lbelow(...)end + +-- scroll to the bottom of the location window +function vim.cmd.lbo(...)end + +-- scroll to the bottom of the location window +function vim.cmd.lbottom(...)end + +-- parse locations and jump to first location +function vim.cmd.lbuffer(...)end + +-- change directory locally +function vim.cmd.lc(...)end + +-- change directory locally +function vim.cmd.lcd(...)end + +-- change directory locally +function vim.cmd.lch(...)end + +-- change directory locally +function vim.cmd.lchdir(...)end + +-- close location window +function vim.cmd.lcl(...)end + +-- close location window +function vim.cmd.lclose(...)end + +-- execute command in valid location list entries +function vim.cmd.ld(...)end + +-- execute command in valid location list entries +function vim.cmd.ldo(...)end + +-- left align lines +function vim.cmd.le(...)end + +-- left align lines +function vim.cmd.left(...)end + +-- make split window appear left or above +function vim.cmd.lefta(...)end + +-- make split window appear left or above +function vim.cmd.leftabove(...)end + +-- assign a value to a variable or option +function vim.cmd.let(...)end + +-- read locations from expr and jump to first +function vim.cmd.lex(...)end + +-- read locations from expr and jump to first +function vim.cmd.lexpr(...)end + +-- read file with locations and jump to first +function vim.cmd.lf(...)end + +-- execute command in each file in location list +function vim.cmd.lfd(...)end + +-- execute command in each file in location list +function vim.cmd.lfdo(...)end + +-- read file with locations and jump to first +function vim.cmd.lfile(...)end + +-- go to the specified location, default first one +function vim.cmd.lfir(...)end + +-- go to the specified location, default first one +function vim.cmd.lfirst(...)end + +-- read file with locations +function vim.cmd.lg(...)end + +-- get locations from buffer +function vim.cmd.lgetb(...)end + +-- get locations from buffer +function vim.cmd.lgetbuffer(...)end + +-- get locations from expr +function vim.cmd.lgete(...)end + +-- get locations from expr +function vim.cmd.lgetexpr(...)end + +-- read file with locations +function vim.cmd.lgetfile(...)end + +-- run 'grepprg' and jump to first match +function vim.cmd.lgr(...)end + +-- run 'grepprg' and jump to first match +function vim.cmd.lgrep(...)end + +-- like :grep, but append to current list +function vim.cmd.lgrepa(...)end + +-- like :grep, but append to current list +function vim.cmd.lgrepadd(...)end + +-- like ":helpgrep" but uses location list +function vim.cmd.lh(...)end + +-- like ":helpgrep" but uses location list +function vim.cmd.lhelpgrep(...)end + +-- list the location lists +function vim.cmd.lhi(...)end + +-- list the location lists +function vim.cmd.lhistory(...)end + +-- print lines +function vim.cmd.list(...)end + +-- go to specific location +function vim.cmd.ll(...)end + +-- go to the specified location, default last one +function vim.cmd.lla(...)end + +-- go to the specified location, default last one +function vim.cmd.llast(...)end + +-- list all locations +function vim.cmd.lli(...)end + +-- list all locations +function vim.cmd.llist(...)end + +-- like ":map!" but includes Lang-Arg mode +function vim.cmd.lm(...)end + +-- execute external command 'makeprg' and parse +function vim.cmd.lmak(...)end + +-- execute external command 'makeprg' and parse +function vim.cmd.lmake(...)end + +-- like ":map!" but includes Lang-Arg mode +function vim.cmd.lmap(...)end + +-- like ":mapclear!" but includes Lang-Arg mode +function vim.cmd.lmapc(...)end + +-- like ":mapclear!" but includes Lang-Arg mode +function vim.cmd.lmapclear(...)end + +-- like ":noremap!" but includes Lang-Arg mode +function vim.cmd.ln(...)end + +-- go to next location +function vim.cmd.lne(...)end + +-- go to newer location list +function vim.cmd.lnew(...)end + +-- go to newer location list +function vim.cmd.lnewer(...)end + +-- go to next location +function vim.cmd.lnext(...)end + +-- go to first location in next file +function vim.cmd.lnf(...)end + +-- go to first location in next file +function vim.cmd.lnfile(...)end + +-- like ":noremap!" but includes Lang-Arg mode +function vim.cmd.lnoremap(...)end + +-- load view for current window from a file +function vim.cmd.lo(...)end + +-- load the following keymaps until EOF +function vim.cmd.loadk(...)end + +-- load the following keymaps until EOF +function vim.cmd.loadkeymap(...)end + +-- load view for current window from a file +function vim.cmd.loadview(...)end + +-- following command keeps marks where they are +function vim.cmd.loc(...)end + +-- following command keeps marks where they are +function vim.cmd.lockmarks(...)end + +-- lock variables +function vim.cmd.lockv(...)end + +-- lock variables +function vim.cmd.lockvar(...)end + +-- go to older location list +function vim.cmd.lol(...)end + +-- go to older location list +function vim.cmd.lolder(...)end + +-- open location window +function vim.cmd.lope(...)end + +-- open location window +function vim.cmd.lopen(...)end + +-- go to previous location +function vim.cmd.lp(...)end + +-- go to last location in previous file +function vim.cmd.lpf(...)end + +-- go to last location in previous file +function vim.cmd.lpfile(...)end + +-- go to previous location +function vim.cmd.lprevious(...)end + +-- go to the specified location, default first one +function vim.cmd.lr(...)end + +-- go to the specified location, default first one +function vim.cmd.lrewind(...)end + +-- list all buffers +function vim.cmd.ls(...)end + +-- jump to tag and add matching tags to the +function vim.cmd.lt(...)end + +-- jump to tag and add matching tags to the +function vim.cmd.ltag(...)end + +-- like ":unmap!" but includes Lang-Arg mode +function vim.cmd.lu(...)end + +-- execute |Lua| command +function vim.cmd.lua(...)end + +-- execute Lua command for each line +function vim.cmd.luad(...)end + +-- execute Lua command for each line +function vim.cmd.luado(...)end + +-- execute |Lua| script file +function vim.cmd.luaf(...)end + +-- execute |Lua| script file +function vim.cmd.luafile(...)end + +-- like ":unmap!" but includes Lang-Arg mode +function vim.cmd.lunmap(...)end + +-- search for pattern in files +function vim.cmd.lv(...)end + +-- search for pattern in files +function vim.cmd.lvimgrep(...)end + +-- like :vimgrep, but append to current list +function vim.cmd.lvimgrepa(...)end + +-- like :vimgrep, but append to current list +function vim.cmd.lvimgrepadd(...)end + +-- open or close location window +function vim.cmd.lw(...)end + +-- open or close location window +function vim.cmd.lwindow(...)end + +-- move lines +function vim.cmd.m(...)end + +-- set a mark +function vim.cmd.ma(...)end + +-- execute external command 'makeprg' and parse +function vim.cmd.mak(...)end + +-- execute external command 'makeprg' and parse +function vim.cmd.make(...)end + +-- show or enter a mapping +function vim.cmd.map(...)end + +-- clear all mappings for Normal and Visual mode +function vim.cmd.mapc(...)end + +-- clear all mappings for Normal and Visual mode +function vim.cmd.mapclear(...)end + +-- set a mark +function vim.cmd.mark(...)end + +-- list all marks +function vim.cmd.marks(...)end + +-- define a match to highlight +function vim.cmd.mat(...)end + +-- define a match to highlight +function vim.cmd.match(...)end + +-- enter a new menu item +function vim.cmd.me(...)end + +-- enter a new menu item +function vim.cmd.menu(...)end + +-- add a menu translation item +function vim.cmd.menut(...)end + +-- add a menu translation item +function vim.cmd.menutranslate(...)end + +-- view previously displayed messages +function vim.cmd.mes(...)end + +-- view previously displayed messages +function vim.cmd.messages(...)end + +-- write current mappings and settings to a file +function vim.cmd.mk(...)end + +-- write current mappings and settings to a file +function vim.cmd.mkexrc(...)end + +-- write session info to a file +function vim.cmd.mks(...)end + +-- write session info to a file +function vim.cmd.mksession(...)end + +-- produce .spl spell file +function vim.cmd.mksp(...)end + +-- produce .spl spell file +function vim.cmd.mkspell(...)end + +-- write current mappings and settings to a file +function vim.cmd.mkv(...)end + +-- write view of current window to a file +function vim.cmd.mkvie(...)end + +-- write view of current window to a file +function vim.cmd.mkview(...)end + +-- write current mappings and settings to a file +function vim.cmd.mkvimrc(...)end + +-- show or change the screen mode +function vim.cmd.mod(...)end + +-- show or change the screen mode +function vim.cmd.mode(...)end + +-- move lines +function vim.cmd.move(...)end + +-- go to next file in the argument list +function vim.cmd.n(...)end + +-- create a new empty window +function vim.cmd.new(...)end + +-- go to next file in the argument list +function vim.cmd.next(...)end + +-- like ":map" but for Normal mode +function vim.cmd.nm(...)end + +-- like ":map" but for Normal mode +function vim.cmd.nmap(...)end + +-- clear all mappings for Normal mode +function vim.cmd.nmapc(...)end + +-- clear all mappings for Normal mode +function vim.cmd.nmapclear(...)end + +-- add menu for Normal mode +function vim.cmd.nme(...)end + +-- add menu for Normal mode +function vim.cmd.nmenu(...)end + +-- like ":noremap" but for Normal mode +function vim.cmd.nn(...)end + +-- like ":noremap" but for Normal mode +function vim.cmd.nnoremap(...)end + +-- like ":noremenu" but for Normal mode +function vim.cmd.nnoreme(...)end + +-- like ":noremenu" but for Normal mode +function vim.cmd.nnoremenu(...)end + +-- enter a mapping that will not be remapped +function vim.cmd.no(...)end + +-- following commands don't trigger autocommands +function vim.cmd.noa(...)end + +-- following commands don't trigger autocommands +function vim.cmd.noautocmd(...)end + +-- suspend 'hlsearch' highlighting +function vim.cmd.noh(...)end + +-- suspend 'hlsearch' highlighting +function vim.cmd.nohlsearch(...)end + +-- enter an abbreviation that will not be +function vim.cmd.norea(...)end + +-- enter an abbreviation that will not be +function vim.cmd.noreabbrev(...)end + +-- enter a mapping that will not be remapped +function vim.cmd.noremap(...)end + +-- enter a menu that will not be remapped +function vim.cmd.noreme(...)end + +-- enter a menu that will not be remapped +function vim.cmd.noremenu(...)end + +-- execute Normal mode commands +function vim.cmd.norm(...)end + +-- execute Normal mode commands +function vim.cmd.normal(...)end + +-- following commands don't create a swap file +function vim.cmd.nos(...)end + +-- following commands don't create a swap file +function vim.cmd.noswapfile(...)end + +-- print lines with line number +function vim.cmd.nu(...)end + +-- print lines with line number +function vim.cmd.number(...)end + +-- like ":unmap" but for Normal mode +function vim.cmd.nun(...)end + +-- like ":unmap" but for Normal mode +function vim.cmd.nunmap(...)end + +-- remove menu for Normal mode +function vim.cmd.nunme(...)end + +-- remove menu for Normal mode +function vim.cmd.nunmenu(...)end + +-- list files that have marks in the |shada| file +function vim.cmd.ol(...)end + +-- list files that have marks in the |shada| file +function vim.cmd.oldfiles(...)end + +-- like ":map" but for Operator-pending mode +function vim.cmd.om(...)end + +-- like ":map" but for Operator-pending mode +function vim.cmd.omap(...)end + +-- remove all mappings for Operator-pending mode +function vim.cmd.omapc(...)end + +-- remove all mappings for Operator-pending mode +function vim.cmd.omapclear(...)end + +-- add menu for Operator-pending mode +function vim.cmd.ome(...)end + +-- add menu for Operator-pending mode +function vim.cmd.omenu(...)end + +-- close all windows except the current one +function vim.cmd.on(...)end + +-- close all windows except the current one +function vim.cmd.only(...)end + +-- like ":noremap" but for Operator-pending mode +function vim.cmd.ono(...)end + +-- like ":noremap" but for Operator-pending mode +function vim.cmd.onoremap(...)end + +-- like ":noremenu" but for Operator-pending mode +function vim.cmd.onoreme(...)end + +-- like ":noremenu" but for Operator-pending mode +function vim.cmd.onoremenu(...)end + +-- open the options-window +function vim.cmd.opt(...)end + +-- open the options-window +function vim.cmd.options(...)end + +-- like ":unmap" but for Operator-pending mode +function vim.cmd.ou(...)end + +-- like ":unmap" but for Operator-pending mode +function vim.cmd.ounmap(...)end + +-- remove menu for Operator-pending mode +function vim.cmd.ounme(...)end + +-- remove menu for Operator-pending mode +function vim.cmd.ounmenu(...)end + +-- set new local syntax highlight for this window +function vim.cmd.ow(...)end + +-- set new local syntax highlight for this window +function vim.cmd.ownsyntax(...)end + +-- print lines +function vim.cmd.p(...)end + +-- add a plugin from 'packpath' +function vim.cmd.pa(...)end + +-- add a plugin from 'packpath' +function vim.cmd.packadd(...)end + +-- load all packages under 'packpath' +function vim.cmd.packl(...)end + +-- load all packages under 'packpath' +function vim.cmd.packloadall(...)end + +-- close preview window +function vim.cmd.pc(...)end + +-- close preview window +function vim.cmd.pclose(...)end + +-- execute perl command +function vim.cmd.pe(...)end + +-- edit file in the preview window +function vim.cmd.ped(...)end + +-- edit file in the preview window +function vim.cmd.pedit(...)end + +-- execute perl command +function vim.cmd.perl(...)end + +-- execute perl command for each line +function vim.cmd.perld(...)end + +-- execute perl command for each line +function vim.cmd.perldo(...)end + +-- execute perl script file +function vim.cmd.perlf(...)end + +-- execute perl script file +function vim.cmd.perlfile(...)end + +-- jump to older entry in tag stack +function vim.cmd.po(...)end + +-- jump to older entry in tag stack +function vim.cmd.pop(...)end + +-- popup a menu by name +function vim.cmd.popu(...)end + +-- popup a menu by name +function vim.cmd.popup(...)end + +-- ":pop" in preview window +function vim.cmd.pp(...)end + +-- ":pop" in preview window +function vim.cmd.ppop(...)end + +-- write all text to swap file +function vim.cmd.pre(...)end + +-- write all text to swap file +function vim.cmd.preserve(...)end + +-- go to previous file in argument list +function vim.cmd.prev(...)end + +-- go to previous file in argument list +function vim.cmd.previous(...)end + +-- print lines +function vim.cmd.print(...)end + +-- profiling functions and scripts +function vim.cmd.prof(...)end + +-- stop profiling a function or script +function vim.cmd.profd(...)end + +-- stop profiling a function or script +function vim.cmd.profdel(...)end + +-- profiling functions and scripts +function vim.cmd.profile(...)end + +-- like ":ijump" but shows match in preview window +function vim.cmd.ps(...)end + +-- like ":ijump" but shows match in preview window +function vim.cmd.psearch(...)end + +-- show tag in preview window +function vim.cmd.pt(...)end + +-- |:tNext| in preview window +function vim.cmd.ptN(...)end + +-- |:tNext| in preview window +function vim.cmd.ptNext(...)end + +-- show tag in preview window +function vim.cmd.ptag(...)end + +-- |:trewind| in preview window +function vim.cmd.ptf(...)end + +-- |:trewind| in preview window +function vim.cmd.ptfirst(...)end + +-- |:tjump| and show tag in preview window +function vim.cmd.ptj(...)end + +-- |:tjump| and show tag in preview window +function vim.cmd.ptjump(...)end + +-- |:tlast| in preview window +function vim.cmd.ptl(...)end + +-- |:tlast| in preview window +function vim.cmd.ptlast(...)end + +-- |:tnext| in preview window +function vim.cmd.ptn(...)end + +-- |:tnext| in preview window +function vim.cmd.ptnext(...)end + +-- |:tprevious| in preview window +function vim.cmd.ptp(...)end + +-- |:tprevious| in preview window +function vim.cmd.ptprevious(...)end + +-- |:trewind| in preview window +function vim.cmd.ptr(...)end + +-- |:trewind| in preview window +function vim.cmd.ptrewind(...)end + +-- |:tselect| and show tag in preview window +function vim.cmd.pts(...)end + +-- |:tselect| and show tag in preview window +function vim.cmd.ptselect(...)end + +-- insert contents of register in the text +function vim.cmd.pu(...)end + +-- insert contents of register in the text +function vim.cmd.put(...)end + +-- print current directory +function vim.cmd.pw(...)end + +-- print current directory +function vim.cmd.pwd(...)end + +-- execute Python command +function vim.cmd.py(...)end + +-- execute Python 3 command +function vim.cmd.py3(...)end + +-- execute Python 3 command for each line +function vim.cmd.py3d(...)end + +-- execute Python 3 command for each line +function vim.cmd.py3do(...)end + +-- execute Python 3 script file +function vim.cmd.py3f(...)end + +-- execute Python 3 script file +function vim.cmd.py3file(...)end + +-- execute Python command for each line +function vim.cmd.pyd(...)end + +-- execute Python command for each line +function vim.cmd.pydo(...)end + +-- execute Python script file +function vim.cmd.pyf(...)end + +-- execute Python script file +function vim.cmd.pyfile(...)end + +-- execute Python command +function vim.cmd.python(...)end + +-- same as :py3 +function vim.cmd.python3(...)end + +-- same as :pyx +function vim.cmd.pythonx(...)end + +-- execute |python_x| command +function vim.cmd.pyx(...)end + +-- execute |python_x| command for each line +function vim.cmd.pyxd(...)end + +-- execute |python_x| command for each line +function vim.cmd.pyxdo(...)end + +-- execute |python_x| script file +function vim.cmd.pyxf(...)end + +-- execute |python_x| script file +function vim.cmd.pyxfile(...)end + +-- quit current window (when one window quit Vim) +function vim.cmd.q(...)end + +-- quit Vim +function vim.cmd.qa(...)end + +-- quit Vim +function vim.cmd.qall(...)end + +-- quit current window (when one window quit Vim) +function vim.cmd.quit(...)end + +-- quit Vim +function vim.cmd.quita(...)end + +-- quit Vim +function vim.cmd.quitall(...)end + +-- read file into the text +function vim.cmd.r(...)end + +-- read file into the text +function vim.cmd.read(...)end + +-- recover a file from a swap file +function vim.cmd.rec(...)end + +-- recover a file from a swap file +function vim.cmd.recover(...)end + +-- redo one undone change +function vim.cmd.red(...)end + +-- redirect messages to a file or register +function vim.cmd.redi(...)end + +-- redirect messages to a file or register +function vim.cmd.redir(...)end + +-- redo one undone change +function vim.cmd.redo(...)end + +-- force a redraw of the display +function vim.cmd.redr(...)end + +-- force a redraw of the display +function vim.cmd.redraw(...)end + +-- force a redraw of the status line(s) and +function vim.cmd.redraws(...)end + +-- force a redraw of the status line(s) and +function vim.cmd.redrawstatus(...)end + +-- force a redraw of the tabline +function vim.cmd.redrawt(...)end + +-- force a redraw of the tabline +function vim.cmd.redrawtabline(...)end + +-- display the contents of registers +function vim.cmd.reg(...)end + +-- display the contents of registers +function vim.cmd.registers(...)end + +-- change current window height +function vim.cmd.res(...)end + +-- change current window height +function vim.cmd.resize(...)end + +-- change tab size +function vim.cmd.ret(...)end + +-- change tab size +function vim.cmd.retab(...)end + +-- return from a user function +function vim.cmd.retu(...)end + +-- return from a user function +vim.cmd["return"] = function(...)end + +-- go to the first file in the argument list +function vim.cmd.rew(...)end + +-- go to the first file in the argument list +function vim.cmd.rewind(...)end + +-- right align text +function vim.cmd.ri(...)end + +-- right align text +function vim.cmd.right(...)end + +-- make split window appear right or below +function vim.cmd.rightb(...)end + +-- make split window appear right or below +function vim.cmd.rightbelow(...)end + +-- read from |shada| file +function vim.cmd.rsh(...)end + +-- read from |shada| file +function vim.cmd.rshada(...)end + +-- source vim scripts in 'runtimepath' +function vim.cmd.ru(...)end + +-- execute Ruby command +function vim.cmd.rub(...)end + +-- execute Ruby command +function vim.cmd.ruby(...)end + +-- execute Ruby command for each line +function vim.cmd.rubyd(...)end + +-- execute Ruby command for each line +function vim.cmd.rubydo(...)end + +-- execute Ruby script file +function vim.cmd.rubyf(...)end + +-- execute Ruby script file +function vim.cmd.rubyfile(...)end + +-- read undo information from a file +function vim.cmd.rund(...)end + +-- read undo information from a file +function vim.cmd.rundo(...)end + +-- source vim scripts in 'runtimepath' +function vim.cmd.runtime(...)end + +-- find and replace text +function vim.cmd.s(...)end + +-- split window and go to previous file in +function vim.cmd.sN(...)end + +-- split window and go to previous file in +function vim.cmd.sNext(...)end + +-- split window and go to specific file in +function vim.cmd.sa(...)end + +-- open a window for each file in argument list +function vim.cmd.sal(...)end + +-- open a window for each file in argument list +function vim.cmd.sall(...)end + +-- execute a command in the sandbox +function vim.cmd.san(...)end + +-- execute a command in the sandbox +function vim.cmd.sandbox(...)end + +-- split window and go to specific file in +function vim.cmd.sargument(...)end + +-- save file under another name. +function vim.cmd.sav(...)end + +-- save file under another name. +function vim.cmd.saveas(...)end + +-- split window and go to specific file in the +function vim.cmd.sb(...)end + +-- split window and go to previous file in the +function vim.cmd.sbN(...)end + +-- split window and go to previous file in the +function vim.cmd.sbNext(...)end + +-- open a window for each file in the buffer list +function vim.cmd.sba(...)end + +-- open a window for each file in the buffer list +function vim.cmd.sball(...)end + +-- split window and go to first file in the +function vim.cmd.sbf(...)end + +-- split window and go to first file in the +function vim.cmd.sbfirst(...)end + +-- split window and go to last file in buffer +function vim.cmd.sbl(...)end + +-- split window and go to last file in buffer +function vim.cmd.sblast(...)end + +-- split window and go to modified file in the +function vim.cmd.sbm(...)end + +-- split window and go to modified file in the +function vim.cmd.sbmodified(...)end + +-- split window and go to next file in the buffer +function vim.cmd.sbn(...)end + +-- split window and go to next file in the buffer +function vim.cmd.sbnext(...)end + +-- split window and go to previous file in the +function vim.cmd.sbp(...)end + +-- split window and go to previous file in the +function vim.cmd.sbprevious(...)end + +-- split window and go to first file in the +function vim.cmd.sbr(...)end + +-- split window and go to first file in the +function vim.cmd.sbrewind(...)end + +-- split window and go to specific file in the +function vim.cmd.sbuffer(...)end + +-- list names of all sourced Vim scripts +function vim.cmd.scr(...)end + +-- encoding used in sourced Vim script +function vim.cmd.scripte(...)end + +-- encoding used in sourced Vim script +function vim.cmd.scriptencoding(...)end + +-- list names of all sourced Vim scripts +function vim.cmd.scriptnames(...)end + +-- show or set options +function vim.cmd.se(...)end + +-- show or set options +function vim.cmd.set(...)end + +-- set 'filetype', unless it was set already +function vim.cmd.setf(...)end + +-- set 'filetype', unless it was set already +function vim.cmd.setfiletype(...)end + +-- show global values of options +function vim.cmd.setg(...)end + +-- show global values of options +function vim.cmd.setglobal(...)end + +-- show or set options locally +function vim.cmd.setl(...)end + +-- show or set options locally +function vim.cmd.setlocal(...)end + +-- split current window and edit file in 'path' +function vim.cmd.sf(...)end + +-- split current window and edit file in 'path' +function vim.cmd.sfind(...)end + +-- split window and go to first file in the +function vim.cmd.sfir(...)end + +-- split window and go to first file in the +function vim.cmd.sfirst(...)end + +-- manipulate signs +function vim.cmd.sig(...)end + +-- manipulate signs +function vim.cmd.sign(...)end + +-- run a command silently +function vim.cmd.sil(...)end + +-- run a command silently +function vim.cmd.silent(...)end + +-- do nothing for a few seconds +function vim.cmd.sl(...)end + +-- split window and go to last file in the +function vim.cmd.sla(...)end + +-- split window and go to last file in the +function vim.cmd.slast(...)end + +-- do nothing for a few seconds +function vim.cmd.sleep(...)end + +-- :substitute with 'magic' +function vim.cmd.sm(...)end + +-- :substitute with 'magic' +function vim.cmd.smagic(...)end + +-- like ":map" but for Select mode +function vim.cmd.smap(...)end + +-- remove all mappings for Select mode +function vim.cmd.smapc(...)end + +-- remove all mappings for Select mode +function vim.cmd.smapclear(...)end + +-- add menu for Select mode +function vim.cmd.sme(...)end + +-- add menu for Select mode +function vim.cmd.smenu(...)end + +-- split window and go to next file in the +function vim.cmd.sn(...)end + +-- split window and go to next file in the +function vim.cmd.snext(...)end + +-- :substitute with 'nomagic' +function vim.cmd.sno(...)end + +-- :substitute with 'nomagic' +function vim.cmd.snomagic(...)end + +-- like ":noremap" but for Select mode +function vim.cmd.snor(...)end + +-- like ":noremap" but for Select mode +function vim.cmd.snoremap(...)end + +-- like ":noremenu" but for Select mode +function vim.cmd.snoreme(...)end + +-- like ":noremenu" but for Select mode +function vim.cmd.snoremenu(...)end + +-- read Vim or Ex commands from a file +function vim.cmd.so(...)end + +-- sort lines +function vim.cmd.sor(...)end + +-- sort lines +function vim.cmd.sort(...)end + +-- read Vim or Ex commands from a file +function vim.cmd.source(...)end + +-- split current window +function vim.cmd.sp(...)end + +-- add good word for spelling +function vim.cmd.spe(...)end + +-- split window and fill with all correct words +function vim.cmd.spelld(...)end + +-- split window and fill with all correct words +function vim.cmd.spelldump(...)end + +-- add good word for spelling +function vim.cmd.spellgood(...)end + +-- show info about loaded spell files +function vim.cmd.spelli(...)end + +-- show info about loaded spell files +function vim.cmd.spellinfo(...)end + +-- replace all bad words like last |z=| +function vim.cmd.spellr(...)end + +-- add rare word for spelling +function vim.cmd.spellra(...)end + +-- add rare word for spelling +function vim.cmd.spellrare(...)end + +-- replace all bad words like last |z=| +function vim.cmd.spellrepall(...)end + +-- remove good or bad word +function vim.cmd.spellu(...)end + +-- remove good or bad word +function vim.cmd.spellundo(...)end + +-- add spelling mistake +function vim.cmd.spellw(...)end + +-- add spelling mistake +function vim.cmd.spellwrong(...)end + +-- split current window +function vim.cmd.split(...)end + +-- split window and go to previous file in the +function vim.cmd.spr(...)end + +-- split window and go to previous file in the +function vim.cmd.sprevious(...)end + +-- split window and go to first file in the +function vim.cmd.sre(...)end + +-- split window and go to first file in the +function vim.cmd.srewind(...)end + +-- suspend the editor or escape to a shell +function vim.cmd.st(...)end + +-- split window and jump to a tag +function vim.cmd.sta(...)end + +-- split window and jump to a tag +function vim.cmd.stag(...)end + +-- start Insert mode +function vim.cmd.star(...)end + +-- start Virtual Replace mode +function vim.cmd.startg(...)end + +-- start Virtual Replace mode +function vim.cmd.startgreplace(...)end + +-- start Insert mode +function vim.cmd.startinsert(...)end + +-- start Replace mode +function vim.cmd.startr(...)end + +-- start Replace mode +function vim.cmd.startreplace(...)end + +-- do ":tjump" and split window +function vim.cmd.stj(...)end + +-- do ":tjump" and split window +function vim.cmd.stjump(...)end + +-- suspend the editor or escape to a shell +function vim.cmd.stop(...)end + +-- stop Insert mode +function vim.cmd.stopi(...)end + +-- stop Insert mode +function vim.cmd.stopinsert(...)end + +-- do ":tselect" and split window +function vim.cmd.sts(...)end + +-- do ":tselect" and split window +function vim.cmd.stselect(...)end + +-- find and replace text +function vim.cmd.substitute(...)end + +-- same as ":unhide" +function vim.cmd.sun(...)end + +-- same as ":unhide" +function vim.cmd.sunhide(...)end + +-- like ":unmap" but for Select mode +function vim.cmd.sunm(...)end + +-- like ":unmap" but for Select mode +function vim.cmd.sunmap(...)end + +-- remove menu for Select mode +function vim.cmd.sunme(...)end + +-- remove menu for Select mode +function vim.cmd.sunmenu(...)end + +-- same as ":stop" +function vim.cmd.sus(...)end + +-- same as ":stop" +function vim.cmd.suspend(...)end + +-- split window and edit file read-only +function vim.cmd.sv(...)end + +-- split window and edit file read-only +function vim.cmd.sview(...)end + +-- show the name of the current swap file +function vim.cmd.sw(...)end + +-- show the name of the current swap file +function vim.cmd.swapname(...)end + +-- syntax highlighting +function vim.cmd.sy(...)end + +-- sync scroll binding +function vim.cmd.sync(...)end + +-- sync scroll binding +function vim.cmd.syncbind(...)end + +-- syntax highlighting +function vim.cmd.syntax(...)end + +-- measure syntax highlighting speed +function vim.cmd.synti(...)end + +-- measure syntax highlighting speed +function vim.cmd.syntime(...)end + +-- same as ":copy" +function vim.cmd.t(...)end + +-- jump to previous matching tag +function vim.cmd.tN(...)end + +-- jump to previous matching tag +function vim.cmd.tNext(...)end + +-- jump to tag +function vim.cmd.ta(...)end + +-- create new tab when opening new window +function vim.cmd.tab(...)end + +-- go to previous tab page +function vim.cmd.tabN(...)end + +-- go to previous tab page +function vim.cmd.tabNext(...)end + +-- close current tab page +function vim.cmd.tabc(...)end + +-- close current tab page +function vim.cmd.tabclose(...)end + +-- execute command in each tab page +function vim.cmd.tabdo(...)end + +-- edit a file in a new tab page +function vim.cmd.tabe(...)end + +-- edit a file in a new tab page +function vim.cmd.tabedit(...)end + +-- find file in 'path', edit it in a new tab page +function vim.cmd.tabf(...)end + +-- find file in 'path', edit it in a new tab page +function vim.cmd.tabfind(...)end + +-- go to first tab page +function vim.cmd.tabfir(...)end + +-- go to first tab page +function vim.cmd.tabfirst(...)end + +-- go to last tab page +function vim.cmd.tabl(...)end + +-- go to last tab page +function vim.cmd.tablast(...)end + +-- move tab page to other position +function vim.cmd.tabm(...)end + +-- move tab page to other position +function vim.cmd.tabmove(...)end + +-- go to next tab page +function vim.cmd.tabn(...)end + +-- edit a file in a new tab page +function vim.cmd.tabnew(...)end + +-- go to next tab page +function vim.cmd.tabnext(...)end + +-- close all tab pages except the current one +function vim.cmd.tabo(...)end + +-- close all tab pages except the current one +function vim.cmd.tabonly(...)end + +-- go to previous tab page +function vim.cmd.tabp(...)end + +-- go to previous tab page +function vim.cmd.tabprevious(...)end + +-- go to first tab page +function vim.cmd.tabr(...)end + +-- go to first tab page +function vim.cmd.tabrewind(...)end + +-- list the tab pages and what they contain +function vim.cmd.tabs(...)end + +-- jump to tag +function vim.cmd.tag(...)end + +-- show the contents of the tag stack +function vim.cmd.tags(...)end + +-- change directory for tab page +function vim.cmd.tc(...)end + +-- change directory for tab page +function vim.cmd.tcd(...)end + +-- change directory for tab page +function vim.cmd.tch(...)end + +-- change directory for tab page +function vim.cmd.tchdir(...)end + +-- open a terminal buffer +function vim.cmd.te(...)end + +-- open a terminal buffer +function vim.cmd.terminal(...)end + +-- jump to first matching tag +function vim.cmd.tf(...)end + +-- jump to first matching tag +function vim.cmd.tfirst(...)end + +-- throw an exception +function vim.cmd.th(...)end + +-- throw an exception +function vim.cmd.throw(...)end + +-- like ":tselect", but jump directly when there +function vim.cmd.tj(...)end + +-- like ":tselect", but jump directly when there +function vim.cmd.tjump(...)end + +-- jump to last matching tag +function vim.cmd.tl(...)end + +-- jump to last matching tag +function vim.cmd.tlast(...)end + +-- add menu for |Terminal-mode| +function vim.cmd.tlm(...)end + +-- add menu for |Terminal-mode| +function vim.cmd.tlmenu(...)end + +-- like ":noremenu" but for |Terminal-mode| +function vim.cmd.tln(...)end + +-- like ":noremenu" but for |Terminal-mode| +function vim.cmd.tlnoremenu(...)end + +-- remove menu for |Terminal-mode| +function vim.cmd.tlu(...)end + +-- remove menu for |Terminal-mode| +function vim.cmd.tlunmenu(...)end + +-- define menu tooltip +function vim.cmd.tm(...)end + +-- like ":map" but for |Terminal-mode| +function vim.cmd.tma(...)end + +-- like ":map" but for |Terminal-mode| +function vim.cmd.tmap(...)end + +-- remove all mappings for |Terminal-mode| +function vim.cmd.tmapc(...)end + +-- remove all mappings for |Terminal-mode| +function vim.cmd.tmapclear(...)end + +-- define menu tooltip +function vim.cmd.tmenu(...)end + +-- jump to next matching tag +function vim.cmd.tn(...)end + +-- jump to next matching tag +function vim.cmd.tnext(...)end + +-- like ":noremap" but for |Terminal-mode| +function vim.cmd.tno(...)end + +-- like ":noremap" but for |Terminal-mode| +function vim.cmd.tnoremap(...)end + +-- make split window appear at top or far left +function vim.cmd.to(...)end + +-- make split window appear at top or far left +function vim.cmd.topleft(...)end + +-- jump to previous matching tag +function vim.cmd.tp(...)end + +-- jump to previous matching tag +function vim.cmd.tprevious(...)end + +-- jump to first matching tag +function vim.cmd.tr(...)end + +-- jump to first matching tag +function vim.cmd.trewind(...)end + +-- add or remove file from trust database +function vim.cmd.trust(...)end + +-- execute commands, abort on error or exception +function vim.cmd.try(...)end + +-- list matching tags and select one +function vim.cmd.ts(...)end + +-- list matching tags and select one +function vim.cmd.tselect(...)end + +-- remove menu tooltip +function vim.cmd.tu(...)end + +-- like ":unmap" but for |Terminal-mode| +function vim.cmd.tunma(...)end + +-- like ":unmap" but for |Terminal-mode| +function vim.cmd.tunmap(...)end + +-- remove menu tooltip +function vim.cmd.tunmenu(...)end + +-- undo last change(s) +function vim.cmd.u(...)end + +-- remove abbreviation +function vim.cmd.una(...)end + +-- remove abbreviation +function vim.cmd.unabbreviate(...)end + +-- undo last change(s) +function vim.cmd.undo(...)end + +-- join next change with previous undo block +function vim.cmd.undoj(...)end + +-- join next change with previous undo block +function vim.cmd.undojoin(...)end + +-- list leafs of the undo tree +function vim.cmd.undol(...)end + +-- list leafs of the undo tree +function vim.cmd.undolist(...)end + +-- open a window for each loaded file in the +function vim.cmd.unh(...)end + +-- open a window for each loaded file in the +function vim.cmd.unhide(...)end + +-- delete variable +function vim.cmd.unl(...)end + +-- delete variable +function vim.cmd.unlet(...)end + +-- unlock variables +function vim.cmd.unlo(...)end + +-- unlock variables +function vim.cmd.unlockvar(...)end + +-- remove mapping +function vim.cmd.unm(...)end + +-- remove mapping +function vim.cmd.unmap(...)end + +-- remove menu +function vim.cmd.unme(...)end + +-- remove menu +function vim.cmd.unmenu(...)end + +-- run a command not silently +function vim.cmd.uns(...)end + +-- run a command not silently +function vim.cmd.unsilent(...)end + +-- write buffer if modified +function vim.cmd.up(...)end + +-- write buffer if modified +function vim.cmd.update(...)end + +-- execute commands for not matching lines +function vim.cmd.v(...)end + +-- print version number and other info +function vim.cmd.ve(...)end + +-- execute command with 'verbose' set +function vim.cmd.verb(...)end + +-- execute command with 'verbose' set +function vim.cmd.verbose(...)end + +-- print version number and other info +function vim.cmd.version(...)end + +-- make following command split vertically +function vim.cmd.vert(...)end + +-- make following command split vertically +function vim.cmd.vertical(...)end + +-- execute commands for not matching lines +function vim.cmd.vglobal(...)end + +-- same as ":edit", but turns off "Ex" mode +function vim.cmd.vi(...)end + +-- edit a file read-only +function vim.cmd.vie(...)end + +-- edit a file read-only +function vim.cmd.view(...)end + +-- search for pattern in files +function vim.cmd.vim(...)end + +-- search for pattern in files +function vim.cmd.vimgrep(...)end + +-- like :vimgrep, but append to current list +function vim.cmd.vimgrepa(...)end + +-- like :vimgrep, but append to current list +function vim.cmd.vimgrepadd(...)end + +-- same as ":edit", but turns off "Ex" mode +function vim.cmd.visual(...)end + +-- overview of Normal mode commands +function vim.cmd.viu(...)end + +-- overview of Normal mode commands +function vim.cmd.viusage(...)end + +-- like ":map" but for Visual+Select mode +function vim.cmd.vm(...)end + +-- like ":map" but for Visual+Select mode +function vim.cmd.vmap(...)end + +-- remove all mappings for Visual+Select mode +function vim.cmd.vmapc(...)end + +-- remove all mappings for Visual+Select mode +function vim.cmd.vmapclear(...)end + +-- add menu for Visual+Select mode +function vim.cmd.vme(...)end + +-- add menu for Visual+Select mode +function vim.cmd.vmenu(...)end + +-- like ":noremap" but for Visual+Select mode +function vim.cmd.vn(...)end + +-- create a new empty window, vertically split +function vim.cmd.vne(...)end + +-- create a new empty window, vertically split +function vim.cmd.vnew(...)end + +-- like ":noremap" but for Visual+Select mode +function vim.cmd.vnoremap(...)end + +-- like ":noremenu" but for Visual+Select mode +function vim.cmd.vnoreme(...)end + +-- like ":noremenu" but for Visual+Select mode +function vim.cmd.vnoremenu(...)end + +-- split current window vertically +function vim.cmd.vs(...)end + +-- split current window vertically +function vim.cmd.vsplit(...)end + +-- like ":unmap" but for Visual+Select mode +function vim.cmd.vu(...)end + +-- like ":unmap" but for Visual+Select mode +function vim.cmd.vunmap(...)end + +-- remove menu for Visual+Select mode +function vim.cmd.vunme(...)end + +-- remove menu for Visual+Select mode +function vim.cmd.vunmenu(...)end + +-- write to a file +function vim.cmd.w(...)end + +-- write to a file and go to previous file in +function vim.cmd.wN(...)end + +-- write to a file and go to previous file in +function vim.cmd.wNext(...)end + +-- write all (changed) buffers +function vim.cmd.wa(...)end + +-- write all (changed) buffers +function vim.cmd.wall(...)end + +-- execute loop for as long as condition met +function vim.cmd.wh(...)end + +-- execute loop for as long as condition met +vim.cmd["while"] = function(...)end + +-- get or set window size (obsolete) +function vim.cmd.wi(...)end + +-- execute a Window (CTRL-W) command +function vim.cmd.winc(...)end + +-- execute a Window (CTRL-W) command +function vim.cmd.wincmd(...)end + +-- execute command in each window +function vim.cmd.windo(...)end + +-- get or set window position +function vim.cmd.winp(...)end + +-- get or set window position +function vim.cmd.winpos(...)end + +-- get or set window size (obsolete) +function vim.cmd.winsize(...)end + +-- write to a file and go to next file in +function vim.cmd.wn(...)end + +-- write to a file and go to next file in +function vim.cmd.wnext(...)end + +-- write to a file and go to previous file in +function vim.cmd.wp(...)end + +-- write to a file and go to previous file in +function vim.cmd.wprevious(...)end + +-- write to a file and quit window or Vim +function vim.cmd.wq(...)end + +-- write all changed buffers and quit Vim +function vim.cmd.wqa(...)end + +-- write all changed buffers and quit Vim +function vim.cmd.wqall(...)end + +-- write to a file +function vim.cmd.write(...)end + +-- write to ShaDa file +function vim.cmd.wsh(...)end + +-- write to ShaDa file +function vim.cmd.wshada(...)end + +-- write undo information to a file +function vim.cmd.wu(...)end + +-- write undo information to a file +function vim.cmd.wundo(...)end + +-- write if buffer changed and close window +function vim.cmd.x(...)end + +-- same as ":wqall" +function vim.cmd.xa(...)end + +-- same as ":wqall" +function vim.cmd.xall(...)end + +-- write if buffer changed and close window +function vim.cmd.xit(...)end + +-- like ":map" but for Visual mode +function vim.cmd.xm(...)end + +-- like ":map" but for Visual mode +function vim.cmd.xmap(...)end + +-- remove all mappings for Visual mode +function vim.cmd.xmapc(...)end + +-- remove all mappings for Visual mode +function vim.cmd.xmapclear(...)end + +-- add menu for Visual mode +function vim.cmd.xme(...)end + +-- add menu for Visual mode +function vim.cmd.xmenu(...)end + +-- like ":noremap" but for Visual mode +function vim.cmd.xn(...)end + +-- like ":noremap" but for Visual mode +function vim.cmd.xnoremap(...)end + +-- like ":noremenu" but for Visual mode +function vim.cmd.xnoreme(...)end + +-- like ":noremenu" but for Visual mode +function vim.cmd.xnoremenu(...)end + +-- like ":unmap" but for Visual mode +function vim.cmd.xu(...)end + +-- like ":unmap" but for Visual mode +function vim.cmd.xunmap(...)end + +-- remove menu for Visual mode +function vim.cmd.xunme(...)end + +-- remove menu for Visual mode +function vim.cmd.xunmenu(...)end + +-- yank lines into a register +function vim.cmd.y(...)end + +-- yank lines into a register +function vim.cmd.yank(...)end + +-- print some lines +function vim.cmd.z(...)end + diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/uv.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/uv.lua new file mode 100644 index 00000000..e7b4d607 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/uv.lua @@ -0,0 +1,4156 @@ +---@meta + +---@meta +---@diagnostic disable: duplicate-set-field + +-- TODO: define @source for all sections and methods + +-- TODO: things like uv.tty_set_mode has in the description- +-- "look below for possible values" and then we rely on lls to list the alias +-- we need a way to list those off when generating the docs + +--- +---The [luv](https://github.com/luvit/luv/) project provides access to the multi-platform support library +---[libuv](http://libuv.org/) in Lua code. It was primarily developed for the [luvit](https://github.com/luvit/luvit/) project as +---the built-in `uv` module, but can be used in other Lua environments. +--- +---More information about the core libuv library can be found at the original +---[libuv documentation page](http://docs.libuv.org/en/v1.x/). +--- +---### TCP Echo Server Example +--- +---Here is a small example showing a TCP echo server: +--- +---```lua +---local uv = require("luv") -- "luv" when stand-alone, "uv" in luvi apps +--- +---local server = uv.new_tcp() +---server:bind("127.0.0.1", 1337) +---server:listen(128, function (err) +--- assert(not err, err) +--- local client = uv.new_tcp() +--- server:accept(client) +--- client:read_start(function (err, chunk) +--- assert(not err, err) +--- if chunk then +--- client:write(chunk) +--- else +--- client:shutdown() +--- client:close() +--- end +--- end) +---end) +---print("TCP server listening at 127.0.0.1 port 1337") +---uv.run() -- an explicit run call is necessary outside of luvit +---``` +--- +---### Module Layout +--- +---The luv library contains a single Lua module referred to hereafter as `uv` for +---simplicity. This module consists mostly of functions with names corresponding to +---their original libuv versions. For example, the libuv function `uv_tcp_bind` has +---a luv version at `uv.tcp_bind`. Currently, only two non-function fields exists: +---`uv.constants` and `uv.errno`, which are tables. +--- +---### Functions vs Methods +--- +---In addition to having simple functions, luv provides an optional method-style +---API. For example, `uv.tcp_bind(server, host, port)` can alternatively be called +---as `server:bind(host, port)`. Note that the first argument `server` becomes the +---object and `tcp_` is removed from the function name. Method forms are +---documented below where they exist. +--- +---### Synchronous vs Asynchronous Functions +--- +---Functions that accept a callback are asynchronous. These functions may +---immediately return results to the caller to indicate their initial status, but +---their final execution is deferred until at least the next libuv loop iteration. +---After completion, their callbacks are executed with any results passed to it. +--- +---Functions that do not accept a callback are synchronous. These functions +---immediately return their results to the caller. +--- +---Some (generally FS and DNS) functions can behave either synchronously or +---asynchronously. If a callback is provided to these functions, they behave +---asynchronously; if no callback is provided, they behave synchronously. +--- +---### Pseudo-Types +--- +---Some unique types are defined. These are not actual types in Lua, but they are +---used here to facilitate documenting consistent behavior: +---- `fail`: an assertable `nil, string, string` tuple (see [Error handling][]) +---- `callable`: a `function`; or a `table` or `userdata` with a `__call` +--- metamethod +---- `buffer`: a `string` or a sequential `table` of `string`s +---- `threadargs`: variable arguments (`...`) of type `nil`, `boolean`, `number`, +--- `string`, or `userdata`, numbers of argument limited to 9. +--- +---@namespace +---@class uv +---@section Libuv in Lua +local uv = {} + +---@alias uv.aliases.buffer string|string[] + +---@alias uv.aliases.threadargs userdata|string|number|boolean|nil # numbers of argument limited to 9. + + + +--- +---@section Contents +--- +---This documentation is mostly a retelling of the [libuv API documentation][] +---within the context of luv's Lua API. Low-level implementation details and +---unexposed C functions and types are not documented here except for when they +---are relevant to behavior seen in the Lua module. +--- +--- - [Error handling][] +--- - [Version checking][] +--- - [`uv_loop_t`][] — Event loop +--- - [`uv_req_t`][] — Base request +--- - [`uv_handle_t`][] — Base handle +--- - [`uv_timer_t`][] — Timer handle +--- - [`uv_prepare_t`][] — Prepare handle +--- - [`uv_check_t`][] — Check handle +--- - [`uv_idle_t`][] — Idle handle +--- - [`uv_async_t`][] — Async handle +--- - [`uv_poll_t`][] — Poll handle +--- - [`uv_signal_t`][] — Signal handle +--- - [`uv_process_t`][] — Process handle +--- - [`uv_stream_t`][] — Stream handle +--- - [`uv_tcp_t`][] — TCP handle +--- - [`uv_pipe_t`][] — Pipe handle +--- - [`uv_tty_t`][] — TTY handle +--- - [`uv_udp_t`][] — UDP handle +--- - [`uv_fs_event_t`][] — FS Event handle +--- - [`uv_fs_poll_t`][] — FS Poll handle +--- - [File system operations][] +--- - [Thread pool work scheduling][] +--- - [DNS utility functions][] +--- - [Threading and synchronization utilities][] +--- - [Miscellaneous utilities][] +--- - [Metrics operations][] +--- + +-- TODO: above section should probably not be hardcoded + + + +--- +---In libuv, errors are negative numbered constants; however, while those errors are exposed through `uv.errno`, +---the functions used to handle them are not exposed to luv users. Instead, if an +---internal error is encountered, the luv function will return to the caller an +---assertable `nil, err, name` tuple. +--- +---- `nil` idiomatically indicates failure +---- `err` is a string with the format `{name}: {message}` +--- - `{name}` is the error name provided internally by `uv_err_name` +--- - `{message}` is a human-readable message provided internally by `uv_strerror` +---- `name` is the same string used to construct `err` +--- +---This tuple is referred to below as the `fail` pseudo-type. +--- +---When a function is called successfully, it will return either a value that is +---relevant to the operation of the function, or the integer `0` to indicate +---success, or sometimes nothing at all. These cases are documented below. +--- +---@alias uv.errno {E2BIG: integer, EACCES: integer, EADDRINUSE: integer, EADDRNOTAVAIL: integer, EAFNOSUPPORT: integer, EAGAIN: integer, EAI_ADDRFAMILY: integer, EAI_AGAIN: integer, EAI_BADFLAGS: integer, EAI_BADHINTS: integer, EAI_CANCELED: integer, EAI_FAIL: integer, EAI_FAMILY: integer, EAI_MEMORY: integer, EAI_NODATA: integer, EAI_NONAME: integer, EAI_OVERFLOW: integer, EAI_PROTOCOL: integer, EAI_SERVICE: integer, EAI_SOCKTYPE: integer, EALREADY: integer, EBADF: integer, EBUSY: integer, ECANCELED: integer, ECHARSET: integer, ECONNABORTED: integer, ECONNREFUSED: integer, ECONNRESET: integer, EDESTADDRREQ: integer, EEXIST: integer, EFAULT: integer, EFBIG: integer, EFTYPE: integer, EHOSTDOWN: integer, EHOSTUNREACH: integer, EILSEQ: integer, EINTR: integer, EINVAL: integer, EIO: integer, EISCONN: integer, EISDIR: integer, ELOOP: integer, EMFILE: integer, EMLINK: integer, EMSGSIZE: integer, ENAMETOOLONG: integer, ENETDOWN: integer, ENETUNREACH: integer, ENFILE: integer, ENOBUFS: integer, ENODATA: integer, ENODEV: integer, ENOENT: integer, ENOMEM: integer, ENONET: integer, ENOPROTOOPT: integer, ENOSPC: integer, ENOSYS: integer, ENOTCONN: integer, ENOTDIR: integer, ENOTEMPTY: integer, ENOTSOCK: integer, ENOTSUP: integer, ENOTTY: integer, ENXIO: integer, EOF: integer, EOVERFLOW: integer, EPERM: integer, EPIPE: integer, EPROTO: integer, EPROTONOSUPPORT: integer, EPROTOTYPE: integer, ERANGE: integer, EREMOTEIO: integer, EROFS: integer, ESHUTDOWN: integer, ESOCKTNOSUPPORT: integer, ESPIPE: integer, ESRCH: integer, ETIMEDOUT: integer, ETXTBSY: integer, EXDEV: integer, UNKNOWN: integer} +---@section Error Handling + +-- TODO: errno fields should have descriptions! + +--- +---A table value which exposes error constants as a map, where the key is the +---error name (without the `UV_` prefix) and its value is a negative number. +---See Libuv's "Error constants" page for further details. +---(https://docs.libuv.org/en/v1.x/errors.html#error-constants) +--- +---@type uv.errno +uv.errno = {} + + + +--- +---@section Version Checking +--- + +--- +---Returns the libuv version packed into a single integer. 8 bits are used for each +---component, with the patch number stored in the 8 least significant bits. For +---example, this would be 0x010203 in libuv 1.2.3. +--- +---@return integer +---@nodiscard +function uv.version() end + +--- +---Returns the libuv version number as a string. For example, this would be "1.2.3" +---in libuv 1.2.3. For non-release versions, the version suffix is included. +--- +---@return string +---@nodiscard +function uv.version_string() end + + + +--- +---The event loop is the central part of libuv's functionality. It takes care of +-- polling for I/O and scheduling callbacks to be run based on different sources of events. +--- +---In luv, there is an implicit uv loop for every Lua state that loads the library. +---You can use this library in an multi-threaded environment as long as each thread +---has it's own Lua state with its corresponding own uv loop. This loop is not +---directly exposed to users in the Lua module. +--- +---@class uv_loop_t: userdata +---@section Event loop +local uv_loop_t = {} + +---@alias uv.aliases.run_mode +---Runs the event loop until there are no more active and referenced handles or requests. +---Returns `true` if `uv.stop()` was called and there are still active handles or requests. +---Returns `false` in all other cases. +---|>'default' +---Poll for I/O once. Note that this function blocks if there are no +---pending callbacks. Returns `false` when done (no active handles or requests +---left), or `true` if more callbacks are expected (meaning you should run the +---event loop again sometime in the future). +---|'once' +---Poll for I/O once but don't block if there are no pending callbacks. +---Returns `false` if done (no active handles or requests left), +---or `true` if more callbacks are expected (meaning you should run the event loop again sometime in the future). +---|'nowait' + +---@alias uv.aliases.loop_configure_option +---Block a signal when polling for new events. +---The second argument to loop_configure() is the signal name (as a lowercase string) or the signal number. +---This operation is currently only implemented for `"sigprof"` signals, to suppress unnecessary wakeups when using a sampling profiler. +---Requesting other signals will fail with `EINVAL`. +---|'block_signal' +---Accumulate the amount of idle time the event loop spends in the event provider. +---This option is necessary to use `metrics_idle_time()`. +---|'metrics_idle_time' + +--- +---Closes all internal loop resources. In normal execution, the loop will +---automatically be closed when it is garbage collected by Lua, so it is not +---necessary to explicitly call `loop_close()`. Call this function only after the +---loop has finished executing and all open handles and requests have been closed, +---or it will return `EBUSY`. +--- +---@return 0|nil success, string? err_name, string? err_msg +function uv.loop_close() end + +--- +---This function runs the event loop. It will act differently depending on the +---specified mode: +---- `"default"`: Runs the event loop until there are no more active and +---referenced handles or requests. Returns `true` if `uv.stop()` was called and +---there are still active handles or requests. Returns `false` in all other +---cases. +---- `"once"`: Poll for I/O once. Note that this function blocks if there are no +---pending callbacks. Returns `false` when done (no active handles or requests +---left), or `true` if more callbacks are expected (meaning you should run the +---event loop again sometime in the future). +---- `"nowait"`: Poll for I/O once but don't block if there are no pending +---callbacks. Returns `false` if done (no active handles or requests left), +---or `true` if more callbacks are expected (meaning you should run the event +---loop again sometime in the future). +--- +---**Note**: Luvit will implicitly call `uv.run()` after loading user code, but if +---you use the luv bindings directly, you need to call this after registering +---your initial set of event callbacks to start the event loop. +--- +---@param mode uv.aliases.run_mode|nil +---@return boolean|nil, string? err_name, string? err_msg +function uv.run(mode) end + +--- +---Set additional loop options. You should normally call this before the first call +---to uv_run() unless mentioned otherwise. +---Supported options: +---- `"block_signal"`: Block a signal when polling for new events. The second argument +---to loop_configure() is the signal name (as a lowercase string) or the signal number. +---This operation is currently only implemented for `"sigprof"` signals, to suppress +---unnecessary wakeups when using a sampling profiler. Requesting other signals will +---fail with `EINVAL`. +---- `"metrics_idle_time"`: Accumulate the amount of idle time the event loop spends +---in the event provider. This option is necessary to use `metrics_idle_time()`. +---An example of a valid call to this function is: +---```lua +---uv.loop_configure("block_signal", "sigprof") +---``` +--- +---**Note**: Be prepared to handle the `ENOSYS` error; it means the loop option is +---not supported by the platform. +--- +---@param option uv.aliases.loop_configure_option +---@param ... any +---@return 0|nil success, string? err_name, string? err_msg +function uv.loop_configure(option, ...) end + +--- +---If the loop is running, returns a string indicating the mode in use. If the loop +---is not running, `nil` is returned instead. +--- +---@return uv.aliases.run_mode|nil +---@nodiscard +function uv.loop_mode() end + +--- +---Returns `true` if there are referenced active handles, active requests, or +---closing handles in the loop; otherwise, `false`. +--- +---@return boolean|nil, string? err_name, string? err_msg +---@nodiscard +function uv.loop_alive() end + +--- +---Stop the event loop, causing `uv.run()` to end as soon as possible. This +---will happen not sooner than the next loop iteration. If this function was called +---before blocking for I/O, the loop won't block for I/O on this iteration. +--- +function uv.stop() end + +--- +---Get backend file descriptor. Only kqueue, epoll, and event ports are supported. +---This can be used in conjunction with `uv.run("nowait")` to poll in one thread +---and run the event loop's callbacks in another +--- +---**Note**: Embedding a kqueue fd in another kqueue pollset doesn't work on all +---platforms. It's not an error to add the fd but it never generates events. +--- +---@return integer|nil +---@nodiscard +function uv.backend_fd() end + +--- +---Get the poll timeout. The return value is in milliseconds, or -1 for no timeout. +--- +---@return integer +---@nodiscard +function uv.backend_timeout() end + +--- +---Returns the current timestamp in milliseconds. The timestamp is cached at the +---start of the event loop tick, see `uv.update_time()` for details and rationale. +---The timestamp increases monotonically from some arbitrary point in time. Don't +---make assumptions about the starting point, you will only get disappointed. +--- +---**Note**: Use `uv.hrtime()` if you need sub-millisecond granularity. +--- +---@return integer +---@nodiscard +function uv.now() end + +--- +---Update the event loop's concept of "now". Libuv caches the current time at the +---start of the event loop tick in order to reduce the number of time-related +---system calls. +---You won't normally need to call this function unless you have callbacks that +---block the event loop for longer periods of time, where "longer" is somewhat +---subjective but probably on the order of a millisecond or more. +--- +function uv.update_time() end + +--- +---Walk the list of handles: `callback` will be executed with each handle. +--- +---Example usage of uv.walk to close all handles that aren't already closing. +---```lua +---uv.walk(function (handle) +--- if not handle:is_closing() then +--- handle:close() +--- end +---end) +---``` +--- +---@param callback fun(handle: uv.aliases.handle_instances) +function uv.walk(callback) end + + + +--- +---`uv_req_t` is the base type for all libuv request types. +--- +---@class uv_req_t: userdata +---@section Base request +local uv_req_t = {} + +---@alias uv.aliases.req_struct_name +---|'unknown' # 0 +---|'req' # 1 +---|'connect' # 2 +---|'write' # 3 +---|'shutdown' # 4 +---|'udp_send' # 5 +---|'fs' # 6 +---|'work' # 7 +---|'getaddrinfo' # 8 +---|'getnameinfo' # 9 +---|'random' # 10 + +---@alias uv.aliases.req_struct_type +---|0 # unknown +---|1 # req +---|2 # connect +---|3 # write +---|4 # shutdown +---|5 # udp_send +---|6 # fs +---|7 # work +---|8 # getaddrinfo +---|9 # getnameinfo +---|10 # random + +--- +---Cancel a pending request. Fails if the request is executing or has finished +---executing. Only cancellation of `uv_fs_t`, `uv_getaddrinfo_t`, +---`uv_getnameinfo_t` and `uv_work_t` requests is currently supported. +--- +---@param req uv_fs_t|uv_getaddrinfo_t|uv_getnameinfo_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.cancel(req) end +uv_req_t.cancel = uv.cancel + +--- +---Returns the name of the struct for a given request (e.g. `"fs"` for `uv_fs_t`) +---and the libuv enum integer for the request's type (`uv_req_type`). +--- +---@param req uv_req_t +---@return uv.aliases.req_struct_name +---@return uv.aliases.req_struct_type +function uv.req_get_type(req) end +uv_req_t.get_type = uv.req_get_type + + + +--- +---`uv_handle_t` is the base type for all libuv handle types. All API functions +---defined here work with any handle type. +--- +---@class uv_handle_t: userdata +---@section Base handle +local uv_handle_t = {} + +---@alias uv.aliases.handle_instances +---|uv_handle_t +---|uv_stream_t +---|uv_tcp_t +---|uv_pipe_t +---|uv_tty_t +---|uv_udp_t +---|uv_fs_event_t +---|uv_fs_poll_t + +---@alias uv.aliases.handle_struct_name +---|'unknown' # 0 +---|'"async"' # 1 +---|'check' # 2 +---|'fs_event' # 3 +---|'fs_poll' # 4 +---|'handle' # 5 +---|'idle' # 6 +---|'pipe' # 7 +---|'poll' # 8 +---|'prepare' # 9 +---|'process' # 10 +---|'stream' # 11 +---|'tcp' # 12 +---|'timer' # 13 +---|'tty' # 14 +---|'udp' # 15 +---|'signal' # 16 +---|'file' # 17 + +---@alias uv.aliases.handle_struct_type +---|0 # unknown +---|1 # async +---|2 # check +---|3 # fs_event +---|4 # fs_poll +---|5 # handle +---|6 # idle +---|7 # pipe +---|8 # poll +---|9 # prepare +---|10 # process +---|11 # stream +---|12 # tcp +---|13 # timer +---|14 # tty +---|15 # udp +---|16 # signal +---|17 # file + +--- +---Returns `true` if the handle is active, `false` if it's inactive. What "active” +---means depends on the type of handle: +--- +--- - A `uv_async_t` handle is always active and cannot be deactivated, except +--- by closing it with `uv.close()`. +--- +--- - A `uv_pipe_t`, `uv_tcp_t`, `uv_udp_t`, etc. handle - basically +--- any handle that deals with I/O - is active when it is doing something that +--- involves I/O, like reading, writing, connecting, accepting new connections, +--- etc. +--- +--- - A `uv_check_t`, `uv_idle_t`, `uv_timer_t`, etc. handle is active +--- when it has been started with a call to `uv.check_start()`, `uv.idle_start()`, +--- `uv.timer_start()` etc. until it has been stopped with a call to its +--- respective stop function. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@return boolean|nil, string? err_name, string? err_msg +---@nodiscard +function uv.is_active(handle) end +uv_handle_t.is_active = uv.is_active + +--- +---Returns `true` if the handle is closing or closed, `false` otherwise. +--- +---**Note**: This function should only be used between the initialization of the +---handle and the arrival of the close callback. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@return boolean|nil, string? err_name, string? err_msg +---@nodiscard +function uv.is_closing(handle) end +uv_handle_t.is_closing = uv.is_closing + +--- +---Request handle to be closed. `callback` will be called asynchronously after this +---call. This MUST be called on each handle before memory is released. +--- +---Handles that wrap file descriptors are closed immediately but `callback` will +---still be deferred to the next iteration of the event loop. It gives you a chance +---to free up any resources associated with the handle. +--- +---In-progress requests, like `uv_connect_t` or `uv_write_t`, are cancelled and +---have their callbacks called asynchronously with `ECANCELED`. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@param callback? function +function uv.close(handle, callback) end +uv_handle_t.close = uv.close + +--- +---Reference the given handle. References are idempotent, that is, if a handle is +---already referenced calling this function again will have no effect. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +function uv.ref(handle) end +uv_handle_t.ref = uv.ref + +--- +---Un-reference the given handle. References are idempotent, that is, if a handle +---is not referenced calling this function again will have no effect. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +function uv.unref(handle) end +uv_handle_t.unref = uv.unref + +--- +---Returns `true` if the handle referenced, `false` if not. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@return boolean|nil, string? err_name, string? err_msg +---@nodiscard +function uv.has_ref(handle) end +uv_handle_t.has_ref = uv.has_ref + +--- +---Gets or sets the size of the send buffer that the operating system uses for the socket. +--- +---If `size` is omitted (or `0`), this will return the current send buffer size; otherwise, this will use `size` to set the new send buffer size. +--- +---This function works for TCP, pipe and UDP handles on Unix and for TCP and UDP +---handles on Windows. +--- +---**Note**: Linux will set double the size and return double the size of the +---original set value. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.send_buffer_size(handle) end +---@param size? integer # (default: `0`) +---@return 0|nil, string? err_name, string? err_msg +function uv.send_buffer_size(handle, size) end +uv_handle_t.send_buffer_size = uv.send_buffer_size + +--- +---Gets or sets the size of the receive buffer that the operating system uses for the socket. +--- +---If `size` is omitted (or `0`), this will return the current send buffer size; otherwise, this will use `size` to set the new send buffer size. +--- +---This function works for TCP, pipe and UDP handles on Unix and for TCP and UDP +---handles on Windows. +--- +---**Note**: Linux will set double the size and return double the size of the +---original set value. +--- +---@param handle uv_handle_t `userdata` for sub-type of `uv_handle_t` +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.recv_buffer_size(handle) end +---@param size? integer # (default: `0`) +---@return 0|nil, string? err_name, string? err_msg +function uv.recv_buffer_size(handle, size) end +uv_handle_t.recv_buffer_size = uv.recv_buffer_size + +--- +---Gets the platform dependent file descriptor equivalent. +--- +---The following handles are supported: TCP, pipes, TTY, UDP and poll. Passing any +---other handle type will fail with `EINVAL`. +--- +---If a handle doesn't have an attached file descriptor yet or the handle itself +---has been closed, this function will return `EBADF`. +--- +---**Warning**: Be very careful when using this function. libuv assumes it's in +---control of the file descriptor so any change to it may lead to malfunction. +--- +---@param handle uv_handle_t `userdata` for sub-type of `uv_handle_t` +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.fileno(handle) end +uv_handle_t.fileno = uv.fileno + +--- +---Returns the name of the struct for a given handle (e.g. `"pipe"` for `uv_pipe_t`) +---and the libuv enum integer for the handle's type (`uv_handle_type`). +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@return uv.aliases.handle_struct_name +---@return uv.aliases.handle_struct_type +function uv.handle_get_type(handle) end +uv_handle_t.get_type = uv.handle_get_type + + + +--- +---@section Reference counting +--- +---The libuv event loop (if run in the default mode) will run until there are no +---active and referenced handles left. The user can force the loop to exit early by +---unreferencing handles which are active, for example by calling `uv.unref()` +---after calling `uv.timer_start()`. +--- +---A handle can be referenced or unreferenced, the refcounting scheme doesn't use a +---counter, so both operations are idempotent. +--- +---All handles are referenced when active by default, see `uv.is_active()` for a +---more detailed explanation on what being active involves. +--- + + + +--- +---Timer handles are used to schedule callbacks to be called in the future. +--- +---@class uv_timer_t: uv_handle_t +---@section Timer handle +local uv_timer_t = {} + +--- +---Creates and initializes a new `uv_timer_t`. Returns the Lua userdata wrapping it. +--- +---Some examples: +---```lua +----- Creating a simple setTimeout wrapper +---local function setTimeout(timeout, callback) +--- local timer = uv.new_timer() +--- timer:start(timeout, 0, function () +--- timer:stop() +--- timer:close() +--- callback() +--- end) +--- return timer +---end +--- +----- Creating a simple setInterval wrapper +---local function setInterval(interval, callback) +--- local timer = uv.new_timer() +--- timer:start(interval, interval, function () +--- callback() +--- end) +--- return timer +---end +--- +----- And clearInterval +---local function clearInterval(timer) +--- timer:stop() +--- timer:close() +---end +---``` +--- +---@return uv_timer_t +---@nodiscard +function uv.new_timer() end + +--- +---Start the timer. `timeout` and `repeat_n` are in milliseconds. +--- +---If `timeout` is zero, the callback fires on the next event loop iteration. If +---`repeat_n` is non-zero, the callback fires first after `timeout` milliseconds and +---then repeatedly after `repeat_n` milliseconds. +--- +---@param timer uv_timer_t +---@param timeout integer +---@param repeat_n integer +---@param callback fun() +---@return 0|nil success, string? err_name, string? err_msg +function uv.timer_start(timer, timeout, repeat_n, callback) end +uv_timer_t.start = uv.timer_start + +--- +---Stop the timer, the callback will not be called anymore. +--- +---@param timer uv_timer_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.timer_stop(timer) end +uv_timer_t.stop = uv.timer_stop + +--- +---Stop the timer, and if it is repeating restart it using the repeat value as the +---timeout. If the timer has never been started before it raises `EINVAL`. +--- +---@param timer uv_timer_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.timer_again(timer) end +uv_timer_t.again = uv.timer_again + +--- +---Set the repeat interval value in milliseconds. The timer will be scheduled to +---run on the given interval, regardless of the callback execution duration, and +---will follow normal timer semantics in the case of a time-slice overrun. +--- +---For example, if a 50 ms repeating timer first runs for 17 ms, it will be +---scheduled to run again 33 ms later. If other tasks consume more than the 33 ms +---following the first timer callback, then the callback will run as soon as +---possible. +--- +---@param timer uv_timer_t +---@param repeat_n integer +function uv.timer_set_repeat(timer, repeat_n) end +uv_timer_t.set_repeat = uv.timer_set_repeat + +--- +---Get the timer repeat value. +--- +---@param timer uv_timer_t +---@return integer +---@nodiscard +function uv.timer_get_repeat(timer) end +uv_timer_t.get_repeat = uv.timer_get_repeat + +--- +---Get the timer due value or 0 if it has expired. The time is relative to `uv.now()`. +--- +---**Note**: New in libuv version 1.40.0. +--- +---@param timer uv_timer_t +---@return integer +---@nodiscard +function uv.timer_get_due_in(timer) end +uv_timer_t.get_due_in = uv.timer_get_due_in + + + +--- +---Prepare handles will run the given callback once per loop iteration, right +---before polling for I/O. +--- +---```lua +---local prepare = uv.new_prepare() +---prepare:start(function() +--- print("Before I/O polling") +---end) +---``` +--- +---@class uv_prepare_t: uv_handle_t +---@section Prepare handle +local uv_prepare_t = {} + +--- +---Creates and initializes a new `uv_prepare_t`. +---Returns the Lua userdata wrapping it. +--- +---@return uv_prepare_t +---@nodiscard +function uv.new_prepare() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- confirmed to never return error see libuv/unix/loop-watcher + +--- +---Start the handle with the given callback. +--- +---@param prepare uv_prepare_t +---@param callback fun() +---@return 0|nil success, string? err_name, string? err_msg +function uv.prepare_start(prepare, callback) end +uv_prepare_t.start = uv.prepare_start + +--- +---Stop the handle, the callback will no longer be called. +--- +---@param prepare uv_prepare_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.prepare_stop(prepare) end +uv_prepare_t.stop = uv.prepare_stop + + +--- +---Check handles will run the given callback once per loop iteration, right after +---polling for I/O. +--- +---```lua +---local check = uv.new_check() +---check:start(function() +--- print("After I/O polling") +---end) +---``` +--- +---@class uv_check_t: uv_handle_t +---@section Check handle +local uv_check_t = {} + +--- +---Creates and initializes a new `uv_check_t`. Returns the Lua userdata wrapping it. +--- +---@return uv_check_t +---@nodiscard +function uv.new_check() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- confirmed to never error see libuv/unix/loop-watcher + +--- +---Start the handle with the given callback. +--- +---@param check uv_check_t +---@param callback fun() +---@return 0|nil success, string? err_name, string? err_msg +function uv.check_start(check, callback) end +uv_check_t.start = uv.check_start + +--- +---Stop the handle, the callback will no longer be called. +--- +---@param check uv_check_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.check_stop(check) end +uv_check_t.stop = uv.check_stop + + + +--- +---Idle handles will run the given callback once per loop iteration, right before +---the `uv_prepare_t` handles. +--- +---**Note**: The notable difference with prepare handles is that when there are +---active idle handles, the loop will perform a zero timeout poll instead of +---blocking for I/O. +--- +---**Warning**: Despite the name, idle handles will get their callbacks called on +---every loop iteration, not when the loop is actually "idle". +--- +---```lua +---local idle = uv.new_idle() +---idle:start(function() +--- print("Before I/O polling, no blocking") +---end) +---``` +--- +---@class uv_idle_t: uv_handle_t +---@section Idle handle +local uv_idle_t = {} + +--- +---Creates and initializes a new `uv_idle_t`. Returns the Lua userdata wrapping it. +--- +---@return uv_idle_t +---@nodiscard +function uv.new_idle() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- confirmed to never error see libuv/unix/loop-watcher + +--- +---Start the handle with the given callback. +--- +---@param idle uv_idle_t +---@param callback fun() +---@return 0|nil success, string? err_name, string? err_msg +function uv.idle_start(idle, callback) end +uv_idle_t.start = uv.idle_start + +--- +---Stop the handle, the callback will no longer be called. +--- +---@param idle uv_idle_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.idle_stop(idle) end +uv_idle_t.stop = uv.idle_stop + + + +--- +---Async handles allow the user to "wakeup" the event loop and get a callback +---called from another thread. +--- +---```lua +---local async +---async = uv.new_async(function() +--- print("async operation ran") +--- async:close() +---end) +--- +---async:send() +---``` +--- +---@class uv_async_t: uv_handle_t +---@section Async handle +local uv_async_t = {} + +--- +---Creates and initializes a new `uv_async_t`. Returns the Lua userdata wrapping +---it. A `nil` callback is allowed. +--- +---**Note**: Unlike other handle initialization functions, this immediately starts +---the handle. +--- +---@param callback fun(...: uv.aliases.threadargs)|nil +---@return uv_async_t|nil handle, string? err_name, string? err_msg +---@nodiscard +function uv.new_async(callback) end + +-- TODO: make sure that the above method can indeed return nil + error. +-- seems like this can technically fail when loop.async_io_watcher.fd is -1 +-- but not sure if that is ever practically true for luv +-- TODO: Luv code suggests that a callback is required, the docs seem to be wrong. + +--- +---Wakeup the event loop and call the async handle's callback. +--- +---**Note**: It's safe to call this function from any thread. The callback will be +---called on the loop thread. +--- +---**Warning**: libuv will coalesce calls to `uv.async_send(async)`, that is, not +---every call to it will yield an execution of the callback. For example: if +---`uv.async_send()` is called 5 times in a row before the callback is called, the +---callback will only be called once. If `uv.async_send()` is called again after +---the callback was called, it will be called again. +--- +---@param async uv_async_t +---@param ... uv.aliases.threadargs +---@return 0|nil success, string? err_name, string? err_msg +function uv.async_send(async, ...) end +uv_async_t.send = uv.async_send + + + +--- +---Poll handles are used to watch file descriptors for readability and writability, +---similar to the purpose of [poll(2)](http://linux.die.net/man/2/poll). +--- +---The purpose of poll handles is to enable integrating external libraries that +---rely on the event loop to signal it about the socket status changes, like c-ares +---or libssh2. Using `uv_poll_t` for any other purpose is not recommended; +---`uv_tcp_t`, `uv_udp_t`, etc. provide an implementation that is faster and more +---scalable than what can be achieved with `uv_poll_t`, especially on Windows. +--- +---It is possible that poll handles occasionally signal that a file descriptor is +---readable or writable even when it isn't. The user should therefore always be +---prepared to handle EAGAIN or equivalent when it attempts to read from or write +---to the fd. +--- +---It is not okay to have multiple active poll handles for the same socket, this +---can cause libuv to busyloop or otherwise malfunction. +--- +---The user should not close a file descriptor while it is being polled by an +---active poll handle. This can cause the handle to report an error, but it might +---also start polling another socket. However the fd can be safely closed +---immediately after a call to `uv.poll_stop()` or `uv.close()`. +--- +---**Note**: On windows only sockets can be polled with poll handles. On Unix any +---file descriptor that would be accepted by poll(2) can be used. +--- +---@class uv_poll_t: uv_handle_t +---@section Poll handle +local uv_poll_t = {} + +---@alias uv.aliases.poll_events +---|'"r"' +---|'"w"' +---|>'"rw"' +---|'"d"' +---|'"rd"' +---|'"wd"' +---|'"rwd"' +---|'"p"' +---|'"rp"' +---|'"wp"' +---|'"rwp"' +---|'"dp"' +---|'"rdp"' +---|'"wdp"' +---|'"rwdp"' + +--- +---Initialize the handle using a file descriptor. +---The file descriptor is set to non-blocking mode. +--- +---@param fd integer # the file descriptor +---@return uv_poll_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_poll(fd) end + +--- +---Initialize the handle using a socket descriptor. On Unix this is identical to +---`uv.new_poll()`. On windows it takes a SOCKET handle. +---The socket is set to non-blocking mode. +--- +---@param fd integer # the file descriptor +---@return uv_poll_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_socket_poll(fd) end + +--- +---Starts polling the file descriptor. +---See alias below for accepted `events`, where `r` is `READABLE`, `w` is `WRITABLE`, `d` is +---`DISCONNECT`, and `p` is `PRIORITIZED`. +---As soon as an event is detected +---the callback will be called with status set to 0, and the detected events set on +---the events field. +--- +---The user should not close the socket while the handle is active. If the user +---does that anyway, the callback may be called reporting an error status, but this +---is not guaranteed. +--- +---**Note** Calling `uv.poll_start()` on a handle that is already active is fine. +---Doing so will update the events mask that is being watched for. +--- +---@param poll uv_poll_t +---@param events uv.aliases.poll_events|nil # (default: `"rw"`) +---@param callback fun(err?: string, events?: uv.aliases.poll_events) +---@return 0|nil success, string? err_name, string? err_msg +function uv.poll_start(poll, events, callback) end +uv_poll_t.start = uv.poll_start + +--- +---Stop polling the file descriptor, the callback will no longer be called. +--- +---@param poll uv_poll_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.poll_stop(poll) end +uv_poll_t.stop = uv.poll_stop + + + +--- +---Signal handles implement Unix style signal handling on a per-event loop bases. +--- +---**Windows Notes:** +--- +---Reception of some signals is emulated on Windows: +--- - SIGINT is normally delivered when the user presses CTRL+C. However, like on +--- Unix, it is not generated when terminal raw mode is enabled. +--- - SIGBREAK is delivered when the user pressed CTRL + BREAK. +--- - SIGHUP is generated when the user closes the console window. On SIGHUP the +--- program is given approximately 10 seconds to perform cleanup. After that +--- Windows will unconditionally terminate it. +--- - SIGWINCH is raised whenever libuv detects that the console has been resized. +--- SIGWINCH is emulated by libuv when the program uses a uv_tty_t handle to write +--- to the console. SIGWINCH may not always be delivered in a timely manner; libuv +--- will only detect size changes when the cursor is being moved. When a readable +--- [`uv_tty_t`][] handle is used in raw mode, resizing the console buffer will +--- also trigger a SIGWINCH signal. +--- - Watchers for other signals can be successfully created, but these signals +--- are never received. These signals are: SIGILL, SIGABRT, SIGFPE, SIGSEGV, +--- SIGTERM and SIGKILL. +--- - Calls to raise() or abort() to programmatically raise a signal are not +--- detected by libuv; these will not trigger a signal watcher. +--- +---**Unix Notes:** +--- +--- - SIGKILL and SIGSTOP are impossible to catch. +--- - Handling SIGBUS, SIGFPE, SIGILL or SIGSEGV via libuv results into undefined +--- behavior. +--- - SIGABRT will not be caught by libuv if generated by abort(), e.g. through +--- assert(). +--- - On Linux SIGRT0 and SIGRT1 (signals 32 and 33) are used by the NPTL pthreads +--- library to manage threads. Installing watchers for those signals will lead to +--- unpredictable behavior and is strongly discouraged. Future versions of libuv +--- may simply reject them. +--- +---```lua +----- Create a new signal handler +---local signal = uv.new_signal() +----- Define a handler function +---uv.signal_start(signal, "sigint", function(signal) +--- print("got " .. signal .. ", shutting down") +--- os.exit(1) +---end) +---``` +--- +---@class uv_signal_t: uv_handle_t +---@section Signal handle +local uv_signal_t = {} + +---@alias uv.aliases.signals +---| "sigabrt" # Abort signal from abort(3) +---| "sigalrm" # Timer signal from alarm(2) +---| "sigbus" # Bus error (bad memory access) +---| "sigchld" # Child stopped or terminated +---| "sigcont" # Continue if stopped +---| "sigfpe" # Floating-point exception +---| "sighup" # Hangup detected on controlling terminal or death of controlling process +---| "sigill" # Illegal Instruction +---| "sigint" # Interrupt from keyboard +---| "sigio" # I/O now possible (4.2BSD) +---| "sigiot" # IOT trap. A synonym for sigabrt +---| "sigkill" # Kill signal +---| "sigpipe" # Broken pipe: write to pipe with no readers; see pipe(7) +---| "sigpoll" # Pollable event (Sys V); synonym for sigIO +---| "sigprof" # Profiling timer expired +---| "sigpwr" # Power failure (System V) +---| "sigquit" # Quit from keyboard +---| "sigsegv" # Invalid memory reference +---| "sigstkflt" # Stack fault on coprocessor +---| "sigstop" # Stop process +---| "sigtstp" # Stop typed at terminal +---| "sigsys" # Bad system call (SVr4); see also seccomp(2) +---| "sigterm" # Termination signal +---| "sigtrap" # Trace/breakpoint trap +---| "sigttin" # Terminal input for background process +---| "sigttou" # Terminal output for background process +---| "sigurg" # Urgent condition on socket (4.2BSD) +---| "sigusr1" # User-defined signal 1 +---| "sigusr2" # User-defined signal 2 +---| "sigvtalrm" # Virtual alarm clock (4.2BSD) +---| "sigxcpu" # CPU time limit exceeded (4.2BSD); see setrlimit(2) +---| "sigxfsz" # File size limit exceeded (4.2BSD);see setrlimit(2) +---| "sigwinch" # Window resize signal (4.3BSD, Sun) +---| "sigbreak" # CTRL + BREAK has been pressed +---| "siglost" # File lock lost + +--- +---Creates and initializes a new `uv_signal_t`. +---Returns the Lua userdata wrapping it. +--- +---@return uv_signal_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_signal() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- see uv_signal_init and uv__make_pipe, it will only fail if pipe2 fails +-- I belive it is very unlikely it would ever error for this call, but it is theortically possible + +--- +---Start the handle with the given callback, watching for the given signal. +--- +---@param signal uv_signal_t +---@param signum integer|uv.aliases.signals +---@param callback? fun(signum: uv.aliases.signals) +---@return 0|nil success, string? err_name, string? err_msg +function uv.signal_start(signal, signum, callback) end +uv_signal_t.start = uv.signal_start + +--- +---Same functionality as `uv.signal_start()` but the signal handler is reset the moment the signal is received. +--- +---@param signal uv_signal_t +---@param signum integer|uv.aliases.signals +---@param callback? fun(signum: uv.aliases.signals) +---@return 0|nil success, string? err_name, string? err_msg +function uv.signal_start_oneshot(signal, signum, callback) end +uv_signal_t.start_oneshot = uv.signal_start_oneshot + +--- +---Stop the handle, the callback will no longer be called. +--- +---@param signal uv_signal_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.signal_stop(signal) end +uv_signal_t.stop = uv.signal_stop + + + +--- +---Process handles will spawn a new process and allow the user to control it and +---establish communication channels with it using streams. +--- +---@class uv_process_t: uv_handle_t +---@section Process handle +local uv_process_t = {} + +---@alias uv.aliases.spawn_options {args?: string[], stdio?: table, env?: table, cwd?: string, uid?: integer, gid?: integer, verbatim?: boolean, detached?: boolean, hide?: boolean} + +-- TODO: add descriptions to above fields + +--- +---Disables inheritance for file descriptors / handles that this process inherited +---from its parent. The effect is that child processes spawned by this process +---don't accidentally inherit these handles. +---It is recommended to call this function as early in your program as possible, +---before the inherited file descriptors can be closed or duplicated. +--- +---**Note:** This function works on a best-effort basis: there is no guarantee that +---libuv can discover all file descriptors that were inherited. In general it does +---a better job on Windows than it does on Unix. +--- +function uv.disable_stdio_inheritance() end + +--- +---Initializes the process handle and starts the process. If the process is +---successfully spawned, this function will return the handle and pid of the child +---process. +---Possible reasons for failing to spawn would include (but not be limited to) the +---file to execute not existing, not having permissions to use the setuid or setgid +---specified, or not having enough memory to allocate for the new process. +--- +---```lua +---local stdin = uv.new_pipe() +---local stdout = uv.new_pipe() +---local stderr = uv.new_pipe() +--- +---print("stdin", stdin) +---print("stdout", stdout) +---print("stderr", stderr) +--- +---local handle, pid = uv.spawn("cat", { +--- stdio = {stdin, stdout, stderr} +---}, function(code, signal) -- on exit +--- print("exit code", code) +--- print("exit signal", signal) +---end) +--- +---print("process opened", handle, pid) +--- +---uv.read_start(stdout, function(err, data) +--- assert(not err, err) +--- if data then +--- print("stdout chunk", stdout, data) +--- else +--- print("stdout end", stdout) +--- end +---end) +--- +---uv.read_start(stderr, function(err, data) +--- assert(not err, err) +--- if data then +--- print("stderr chunk", stderr, data) +--- else +--- print("stderr end", stderr) +--- end +---end) +--- +---uv.write(stdin, "Hello World") +--- +---uv.shutdown(stdin, function() +--- print("stdin shutdown", stdin) +--- uv.close(handle, function() +--- print("process closed", handle, pid) +--- end) +---end) +---``` +--- +---The `options` table accepts the following fields: +--- +--- - `options.args` - Command line arguments as a list of strings. The first +--- string should *not* be the path to the program, since that is already +--- provided via `path`. On Windows, this uses CreateProcess which concatenates +--- the arguments into a string. This can cause some strange errors +--- (see `options.verbatim` below for Windows). +--- +--- - `options.stdio` - Set the file descriptors that will be made available to +--- the child process. The convention is that the first entries are stdin, stdout, +--- and stderr. (**Note**: On Windows, file descriptors after the third are +--- available to the child process only if the child processes uses the MSVCRT +--- runtime.) +--- +--- - `options.env` - Set environment variables for the new process. +--- +--- - `options.cwd` - Set the current working directory for the sub-process. +--- +--- - `options.uid` - Set the child process' user id. +--- +--- - `options.gid` - Set the child process' group id. +--- +--- - `options.verbatim` - If true, do not wrap any arguments in quotes, or +--- perform any other escaping, when converting the argument list into a command +--- line string. This option is only meaningful on Windows systems. On Unix it is +--- silently ignored. +--- +--- - `options.detached` - If true, spawn the child process in a detached state - +--- this will make it a process group leader, and will effectively enable the +--- child to keep running after the parent exits. Note that the child process +--- will still keep the parent's event loop alive unless the parent process calls +--- `uv.unref()` on the child's process handle. +--- +--- - `options.hide` - If true, hide the subprocess console window that would +--- normally be created. This option is only meaningful on Windows systems. On +--- Unix it is silently ignored. +---The `options.stdio` entries can take many shapes. +--- +--- - If they are numbers, then the child process inherits that same zero-indexed +--- fd from the parent process. +--- +--- - If `uv_stream_t` handles are passed in, those are used as a read-write pipe +--- or inherited stream depending if the stream has a valid fd. +--- +--- - Including `nil` placeholders means to ignore that fd in the child process. +--- +---When the child process exits, `on_exit` is called with an exit code and signal. +--- +---@param path string +---@param options uv.aliases.spawn_options +---@param on_exit fun(code: integer, signal: integer)? +---@return uv_process_t|nil, integer|string, string? +function uv.spawn(path, options, on_exit) end + +--- +---Sends the specified signal to the given process handle. +--- +---@param process uv_process_t +---@param signum? integer|uv.aliases.signals # (default: `"sigterm"`) +---@return 0|nil success, string? err_name, string? err_msg +function uv.process_kill(process, signum) end +uv_process_t.kill = uv.process_kill + +--- +---Sends the specified signal to the given PID. +--- +---@param pid integer +---@param signum? integer|uv.aliases.signals # (default: `"sigterm"`) +---@return 0|nil success, string? err_name, string? err_msg +function uv.kill(pid, signum) end + +--- +---Returns the handle's pid. +--- +---@param process uv_process_t +---@return integer +---@nodiscard +function uv.process_get_pid(process) end +uv_process_t.get_pid = uv.process_get_pid + + + +--- +---Stream handles provide an abstraction of a duplex communication channel. +---`uv_stream_t` is an abstract type, libuv provides 3 stream implementations +---in the form of `uv_tcp_t`, `uv_pipe_t` and `uv_tty_t`. +--- +---@class uv_stream_t: uv_handle_t +---@section Stream handle +local uv_stream_t = {} + +---@class uv_shutdown_t: uv_req_t + +---@class uv_write_t: uv_req_t + +--- +---Shutdown the outgoing (write) side of a duplex stream. It waits for pending +---write requests to complete. The callback is called after shutdown is complete. +--- +---@param stream uv_stream_t +---@param callback fun(err?: string)|nil +---@return uv_shutdown_t|nil stream, string? err_name, string? err_msg +function uv.shutdown(stream, callback) end +uv_stream_t.shutdown = uv.shutdown + +--- +---Start listening for incoming connections. `backlog` indicates the number of +---connections the kernel might queue, same as `listen(2)`. When a new incoming +---connection is received the callback is called. +--- +---@param stream uv_stream_t +---@param backlog integer +---@param callback fun(err?: string) +---@return 0|nil success, string? err_name, string? err_msg +function uv.listen(stream, backlog, callback) end +uv_stream_t.listen = uv.listen + +--- +---This call is used in conjunction with `uv.listen()` to accept incoming +---connections. Call this function after receiving a callback to accept the +---connection. +--- +---When the connection callback is called it is guaranteed that this function +---will complete successfully the first time. If you attempt to use it more than +---once, it may fail. It is suggested to only call this function once per +---connection call. +--- +---```lua +---server:listen(128, function (err) +--- local client = uv.new_tcp() +--- server:accept(client) +---end) +---``` +--- +---@param stream uv_stream_t +---@param client_stream uv_stream_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.accept(stream, client_stream) end +uv_stream_t.accept = uv.accept + +--- +---Read data from an incoming stream. The callback will be made several times until +---there is no more data to read or `uv.read_stop()` is called. When we've reached +---EOF, `data` will be `nil`. +--- +---```lua +---stream:read_start(function (err, chunk) +--- if err then +--- -- handle read error +--- elseif chunk then +--- -- handle data +--- else +--- -- handle disconnect +--- end +---end) +---``` +--- +---@param stream uv_stream_t +---@param callback fun(err?: string, data?: string) +---@return 0|nil success, string? err_name, string? err_msg +function uv.read_start(stream, callback) end +uv_stream_t.read_start = uv.read_start + +--- +---Stop reading data from the stream. The read callback will no longer be called. +---This function is idempotent and may be safely called on a stopped stream. +--- +---@param stream uv_stream_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.read_stop(stream) end +uv_stream_t.read_stop = uv.read_stop + +--- +---Write data to stream. +---`data` can either be a Lua string or a table of strings. If a table is passed +---in, the C backend will use writev to send all strings in a single system call. +---The optional `callback` is for knowing when the write is complete. +--- +---@param stream uv_stream_t +---@param data uv.aliases.buffer +---@param callback fun(err?: string)|nil +---@return uv_write_t|nil stream, string? err_name, string? err_msg +function uv.write(stream, data, callback) end +uv_stream_t.write = uv.write + +--- +---Extended write function for sending handles over a pipe. The pipe must be +---initialized with `ipc` option `true`. +--- +---**Note:** `send_handle` must be a TCP socket or pipe, which is a server or a +---connection (listening or connected state). Bound sockets or pipes will be +---assumed to be servers. +--- +---@param stream uv_stream_t +---@param data uv.aliases.buffer +---@param send_handle uv_tcp_t|uv_pipe_t +---@param callback fun()|nil +---@return uv_write_t|nil stream, string? err_name, string? err_msg +function uv.write2(stream, data, send_handle, callback) end +uv_stream_t.write2 = uv.write2 + +--- +---Same as `uv.write()`, but won't queue a write request if it can't be completed +---immediately. +---Will return number of bytes written (can be less than the supplied buffer size). +--- +---@param stream uv_stream_t +---@param data uv.aliases.buffer +---@return integer|nil, string? err_name, string? err_msg +function uv.try_write(stream, data) end +uv_stream_t.try_write = uv.try_write + +--- +---Like `uv.write2()`, but with the properties of `uv.try_write()`. Not supported on Windows, where it returns `UV_EAGAIN`. +---Will return number of bytes written (can be less than the supplied buffer size). +--- +---@param stream uv_stream_t +---@param data uv.aliases.buffer +---@param send_handle uv_tcp_t|uv_pipe_t +---@return integer|nil, string? err_name, string? err_msg +function uv.try_write2(stream, data, send_handle) end +uv_stream_t.try_write2 = uv.try_write2 + +--- +---Returns `true` if the stream is readable, `false` otherwise. +--- +---@param stream uv_stream_t +---@return boolean +---@nodiscard +function uv.is_readable(stream) end +uv_stream_t.is_readable = uv.is_readable + +--- +---Returns `true` if the stream is writable, `false` otherwise. +--- +---@param stream uv_stream_t +---@return boolean +---@nodiscard +function uv.is_writable(stream) end +uv_stream_t.is_writable = uv.is_writable + +--- +---Enable or disable blocking mode for a stream. +---When blocking mode is enabled all writes complete synchronously. The interface +---remains unchanged otherwise, e.g. completion or failure of the operation will +---still be reported through a callback which is made asynchronously. +--- +---**Warning**: Relying too much on this API is not recommended. It is likely to +---change significantly in the future. Currently this only works on Windows and +---only for `uv_pipe_t` handles. Also libuv currently makes no ordering guarantee +---when the blocking mode is changed after write requests have already been +---submitted. Therefore it is recommended to set the blocking mode immediately +---after opening or creating the stream. +--- +---@param stream uv_stream_t +---@param blocking boolean +---@return 0|nil success, string? err_name, string? err_msg +function uv.stream_set_blocking(stream, blocking) end +uv_stream_t.set_blocking = uv.stream_set_blocking + +--- +---Returns the stream's write queue size. +--- +---@return integer +---@nodiscard +function uv.stream_get_write_queue_size() end +uv_stream_t.get_write_queue_size = uv.stream_get_write_queue_size + + + +--- +---TCP handles are used to represent both TCP streams and servers. +--- +---@class uv_tcp_t: uv_stream_t +---@section TCP handle +local uv_tcp_t = {} + +---@class uv_connect_t: uv_req_t + +---@alias uv.aliases.network_family +---|'"unix"' +---|'"inet"' +---|'"inet6"' +---|'"ipx"' +---|'"netlink"' +---|'"x25"' +---|'"ax25"' +---|'"atmpvc"' +---|'"appletalk"' +---|'"packet"' + +---@alias uv.aliases.network_protocols +---|'"ip"' # internet protocol, pseudo protocol number +---|'"hopopt"' # hop-by-hop options for ipv6 +---|'"icmp"' # internet control message protocol +---|'"igmp"' # internet group management protocol +---|'"ggp"' # gateway-gateway protocol +---|'"ipv4"' # IPv4 encapsulation +---|'"st"' # ST datagram mode +---|'"tcp"' # transmission control protocol +---|'"cbt"' # CBT, Tony Ballardie +---|'"egp"' # exterior gateway protocol +---|'"igp"' # any private interior gateway (Cisco: for IGRP) +---|'"bbn-rcc"' # BBN RCC Monitoring +---|'"nvp"' # Network Voice Protocol +---|'"pup"' # PARC universal packet protocol +---|'"argus"' # ARGUS (deprecated) +---|'"emcon"' # EMCON +---|'"xnet"' # Cross Net Debugger +---|'"chaos"' # Chaos +---|'"udp"' # user datagram protocol +---|'"mux"' # Multiplexing protocol +---|'"dcn"' # DCN Measurement Subsystems +---|'"hmp"' # host monitoring protocol +---|'"prm"' # packet radio measurement protocol +---|'"xns-idp"' # Xerox NS IDP +---|'"trunk-1"' # Trunk-1 +---|'"trunk-2"' # Trunk-2 +---|'"leaf-1"' # Leaf-1 +---|'"leaf-2"' # Leaf-2 +---|'"rdp"' # "reliable datagram" protocol +---|'"irtp"' # Internet Reliable Transaction Protocol +---|'"iso-tp4"' # ISO Transport Protocol Class 4 +---|'"netblt"' # Bulk Data Transfer Protocol +---|'"mfe-nsp"' # MFE Network Services Protocol +---|'"merit-inp"' # MERIT Internodal Protocol +---|'"dccp"' # Datagram Congestion Control Protocol +---|'"3pc"' # Third Party Connect Protocol +---|'"idpr"' # Inter-Domain Policy Routing Protocol +---|'"xtp"' # Xpress Tranfer Protocol +---|'"ddp"' # Datagram Delivery Protocol +---|'"idpr-cmtp"' # IDPR Control Message Transport Proto +---|'"tp++"' # TP++ Transport Protocol +---|'"il"' # IL Transport Protocol +---|'"ipv6"' # IPv6 encapsulation +---|'"sdrp"' # Source Demand Routing Protocol +---|'"ipv6-route"' # Routing Header for IPv6 +---|'"ipv6-frag"' # Fragment Header for IPv6 +---|'"idrp"' # Inter-Domain Routing Protocol +---|'"rsvp"' # Resource ReSerVation Protocol +---|'"gre"' # Generic Routing Encapsulation +---|'"dsr"' # Dynamic Source Routing Protocol +---|'"bna"' # BNA +---|'"esp"' # Encap Security Payload +---|'"ipv6-crypt"' # Encryption Header for IPv6 (not in official list) +---|'"ah"' # Authentication Header +---|'"ipv6-auth"' # Authentication Header for IPv6 (not in official list) +---|'"i-nlsp"' # Integrated Net Layer Security TUBA +---|'"swipe"' # IP with Encryption +---|'"narp"' # NBMA Address Resolution Protocol +---|'"mobile"' # IP Mobility +---|'"tlsp"' # Transport Layer Security Protocol +---|'"skip"' # SKIP +---|'"ipv6-icmp"' # ICMP for IPv6 +---|'"ipv6-nonxt"' # No Next Header for IPv6 +---|'"ipv6-opts"' # Destination Options for IPv6 +---|'"#"' # any host internal protocol +---|'"cftp"' # CFTP +---|'"#"' # any local network +---|'"sat-expak"' # SATNET and Backroom EXPAK +---|'"kryptolan"' # Kryptolan +---|'"rvd"' # MIT Remote Virtual Disk Protocol +---|'"ippc"' # Internet Pluribus Packet Core +---|'"#"' # any distributed file system +---|'"sat-mon"' # SATNET Monitoring +---|'"visa"' # VISA Protocol +---|'"ipcv"' # Internet Packet Core Utility +---|'"cpnx"' # Computer Protocol Network Executive +---|'"cphb"' # Computer Protocol Heart Beat +---|'"wsn"' # Wang Span Network +---|'"pvp"' # Packet Video Protocol +---|'"br-sat-mon"' # Backroom SATNET Monitoring +---|'"sun-nd"' # SUN ND PROTOCOL-Temporary +---|'"wb-mon"' # WIDEBAND Monitoring +---|'"wb-expak"' # WIDEBAND EXPAK +---|'"iso-ip"' # ISO Internet Protocol +---|'"vmtp"' # Versatile Message Transport +---|'"secure-vmtp"' # SECURE-VMTP +---|'"vines"' # VINES +---|'"ttp"' # TTP +---|'"nsfnet-igp"' # NSFNET-IGP +---|'"dgp"' # Dissimilar Gateway Protocol +---|'"tcf"' # TCF +---|'"eigrp"' # Enhanced Interior Routing Protocol (Cisco) +---|'"ospf"' # Open Shortest Path First IGP +---|'"sprite-rpc"' # Sprite RPC Protocol +---|'"larp"' # Locus Address Resolution Protocol +---|'"mtp"' # Multicast Transport Protocol +---|'"ax.25"' # AX.25 Frames +---|'"ipip"' # Yet Another IP encapsulation +---|'"micp"' # Mobile Internetworking Control Pro. +---|'"scc-sp"' # Semaphore Communications Sec. Pro. +---|'"etherip"' # Ethernet-within-IP Encapsulation +---|'"encap"' # Yet Another IP encapsulation +---|'"#"' # any private encryption scheme +---|'"gmtp"' # GMTP +---|'"ifmp"' # Ipsilon Flow Management Protocol +---|'"pnni"' # PNNI over IP +---|'"pim"' # Protocol Independent Multicast +---|'"aris"' # ARIS +---|'"scps"' # SCPS +---|'"qnx"' # QNX +---|'"a/n"' # Active Networks +---|'"ipcomp"' # IP Payload Compression Protocol +---|'"snp"' # Sitara Networks Protocol +---|'"compaq-peer"' # Compaq Peer Protocol +---|'"ipx-in-ip"' # IPX in IP +---|'"vrrp"' # Virtual Router Redundancy Protocol +---|'"pgm"' # PGM Reliable Transport Protocol +---|'"#"' # any 0-hop protocol +---|'"l2tp"' # Layer Two Tunneling Protocol +---|'"ddx"' # D-II Data Exchange +---|'"iatp"' # Interactive Agent Transfer Protocol +---|'"stp"' # Schedule Transfer +---|'"srp"' # SpectraLink Radio Protocol +---|'"uti"' # UTI +---|'"smp"' # Simple Message Protocol +---|'"sm"' # SM (deprecated) +---|'"ptp"' # Performance Transparency Protocol +---|'"isis"' # ISIS over IPv4 +---|'"crtp"' # Combat Radio Transport Protocol +---|'"crudp"' # Combat Radio User Datagram +---|'"sps"' # Secure Packet Shield +---|'"pipe"' # Private IP Encapsulation within IP +---|'"sctp"' # Stream Control Transmission Protocol +---|'"fc"' # Fibre Channel +---|'"mobility-header"' # Mobility Header +---|'"manet"' # MANET Protocols +---|'"hip"' # Host Identity Protocol +---|'"shim6"' # Shim6 Protocol +---|'"wesp"' # Wrapped Encapsulating Security Payload +---|'"rohc"' # Robust Header Compression + +---@alias uv.aliases.tcp_socket_type +---|>'"stream"' +---|'"dgram"' +---|'"raw"' +---|'"rdm"' +---|'"seqpacket"' + +---@alias uv.aliases.tcp_bind_flags {ipv6only?: boolean} + +---@alias uv.aliases.getpeername_rtn {ip: string, family: uv.aliases.network_family, port: integer} + +---@alias uv.aliases.getsockname_rtn uv.aliases.getpeername_rtn + +---@alias uv.aliases.socketpair_flags {nonblock: boolean} + +--- +---Creates and initializes a new `uv_tcp_t`. +---Returns the Lua userdata wrapping it. +--- +---@param flags uv.aliases.network_family|integer +---@return uv_tcp_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_tcp(flags) end +---@return uv_tcp_t +---@nodiscard +function uv.new_tcp() end + +--- +---Open an existing file descriptor or SOCKET as a TCP handle. +--- +---**Note:** The passed file descriptor or SOCKET is not checked for its type, but it's required that it represents a valid stream socket. +--- +---@param tcp uv_tcp_t +---@param sock integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_open(tcp, sock) end +uv_tcp_t.open = uv.tcp_open + +--- +---Enable / disable Nagle's algorithm. +--- +---@param tcp uv_tcp_t +---@param enable boolean +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_nodelay(tcp, enable) end +uv_tcp_t.nodelay = uv.tcp_nodelay + +--- +---Enable / disable TCP keep-alive. `delay` is the initial delay in seconds, +---ignored when enable is `false`. +--- +---@param tcp uv_tcp_t +---@param enable boolean +---@param delay integer|nil +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_keepalive(tcp, enable, delay) end +uv_tcp_t.keepalive = uv.tcp_keepalive + +--- +---Enable / disable simultaneous asynchronous accept requests that are queued by +---the operating system when listening for new TCP connections. +--- +---This setting is used to tune a TCP server for the desired performance. Having +---simultaneous accepts can significantly improve the rate of accepting connections +---(which is why it is enabled by default) but may lead to uneven load distribution +---in multi-process setups. +--- +---@param tcp uv_tcp_t +---@param enable boolean +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_simultaneous_accepts(tcp, enable) end +uv_tcp_t.simultaneous_accepts = uv.tcp_simultaneous_accepts + +--- +---Bind the handle to an host and port. `host` should be an IP address and +---not a domain name. Any `flags` are set with a table with field `ipv6only` +---equal to `true` or `false`. +--- +---When the port is already taken, you can expect to see an `EADDRINUSE` error +---from either `uv.tcp_bind()`, `uv.listen()` or `uv.tcp_connect()`. That is, a +---successful call to this function does not guarantee that the call to `uv.listen()` +---or `uv.tcp_connect()` will succeed as well. +--- +---Use a port of `0` to let the OS assign an ephemeral port. You can look it up +---later using `uv.tcp_getsockname()`. +--- +---@param tcp uv_tcp_t +---@param host string +---@param port integer +---@param flags uv.aliases.tcp_bind_flags|nil +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_bind(tcp, host, port, flags) end +uv_tcp_t.bind = uv.tcp_bind + +--- +---Get the address of the peer connected to the handle. +--- +---@param tcp uv_tcp_t +---@return uv.aliases.getpeername_rtn|nil, string? err_name, string? err_msg +---@nodiscard +function uv.tcp_getpeername(tcp) end +uv_tcp_t.getpeername = uv.tcp_getpeername + +--- +---Get the current address to which the handle is bound. +--- +---@param tcp uv_tcp_t +---@return uv.aliases.getsockname_rtn|nil, string? err_name, string? err_msg +---@nodiscard +function uv.tcp_getsockname(tcp) end +uv_tcp_t.getsockname = uv.tcp_getsockname + +--- +---Establish an IPv4 or IPv6 TCP connection. +--- +---```lua +---local client = uv.new_tcp() +---client:connect("127.0.0.1", 8080, function (err) +--- -- check error and carry on. +---end) +---``` +--- +---@param tcp uv_tcp_t +---@param host string +---@param port integer +---@param callback fun(err?: string) +---@return uv_connect_t|nil stream, string? err_name, string? err_msg +function uv.tcp_connect(tcp, host, port, callback) end +uv_tcp_t.connect = uv.tcp_connect + +--- +---Please use `uv.stream_get_write_queue_size()` instead. +--- +---@param tcp uv_tcp_t +---@deprecated +function uv.tcp_write_queue_size(tcp) end +uv_tcp_t.write_queue_size = uv.tcp_write_queue_size + +--- +---Resets a TCP connection by sending a RST packet. This is accomplished by setting +---the SO_LINGER socket option with a linger interval of zero and then calling +---`uv.close()`. Due to some platform inconsistencies, mixing of `uv.shutdown()` +---and `uv.tcp_close_reset()` calls is not allowed. +--- +---@param tcp uv_tcp_t +---@param callback fun()|nil +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_close_reset(tcp, callback) end +uv_tcp_t.close_reset = uv.tcp_close_reset + +--- +---Create a pair of connected sockets with the specified properties. The resulting handles can be passed to `uv.tcp_open`, used with `uv.spawn`, or for any other purpose. +---When specified as a string, `socktype` must be one of `"stream"`, `"dgram"`, `"raw"`, +---`"rdm"`, or `"seqpacket"`. +--- +---When `protocol` is set to 0 or nil, it will be automatically chosen based on the socket's domain and type. When `protocol` is specified as a string, it will be looked up using the `getprotobyname(3)` function (examples: `"ip"`, `"icmp"`, `"tcp"`, `"udp"`, etc). +--- +---Flags: +---- `nonblock`: Opens the specified socket handle for `OVERLAPPED` or `FIONBIO`/`O_NONBLOCK` I/O usage. This is recommended for handles that will be used by libuv, and not usually recommended otherwise. +--- +---Equivalent to `socketpair(2)` with a domain of `AF_UNIX`. +--- +---```lua +----- Simple read/write with tcp +---local fds = uv.socketpair(nil, nil, {nonblock=true}, {nonblock=true}) +--- +---local sock1 = uv.new_tcp() +---sock1:open(fds[1]) +--- +---local sock2 = uv.new_tcp() +---sock2:open(fds[2]) +--- +---sock1:write("hello") +---sock2:read_start(function(err, chunk) +--- assert(not err, err) +--- print(chunk) +---end) +---``` +--- +---@param socktype uv.aliases.tcp_socket_type|integer|nil # (default: `"stream"`) +---@param protocol uv.aliases.network_protocols|integer|nil # (default: `0`) +---@param flags1 uv.aliases.socketpair_flags|nil # (nonblock default: `false`) +---@param flags2 uv.aliases.socketpair_flags|nil # (nonblock default: `false`) +---@return {[1]: integer, [2]: integer}|nil, string? err_name, string? err_msg # [1, 2] file descriptor +function uv.socketpair(socktype, protocol, flags1, flags2) end + + + +--- +---Pipe handles provide an abstraction over local domain sockets on Unix and named pipes on Windows. +--- +---```lua +---local pipe = uv.new_pipe(false) +--- +---pipe:bind('/tmp/sock.test') +--- +---pipe:listen(128, function() +--- local client = uv.new_pipe(false) +--- pipe:accept(client) +--- client:write("hello!\n") +--- client:close() +---end) +---``` +--- +---@class uv_pipe_t: uv_stream_t +---@section Pipe handle +local uv_pipe_t = {} + +---@alias uv.aliases.pipe_chmod_flags +---|'"r"' +---|'"w"' +---|'"rw"' +---|'"wr"' + +---@alias uv.aliases.pipe_flags {nonblock: boolean} # (nonblock default: `false`) + +---@alias uv.aliases.pipe_rtn {read: integer, write: integer} + +---@alias uv.aliases.pipe_2_flags {no_truncate: boolean} + +--- +---Creates and initializes a new `uv_pipe_t`. Returns the Lua userdata wrapping +---it. The `ipc` argument is a boolean to indicate if this pipe will be used for +---handle passing between processes. +--- +---@param ipc boolean|nil +---@return uv_pipe_t +---@nodiscard +function uv.new_pipe(ipc) end + +-- TODO: make sure the above method can indeed return nil + error message. +-- confirmed to never return error. + +--- +---Open an existing file descriptor or `uv_handle_t` as a pipe. +--- +---**Note**: The file descriptor is set to non-blocking mode. +--- +---@param pipe uv_pipe_t +---@param fd integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.pipe_open(pipe, fd) end +uv_pipe_t.open = uv.pipe_open + +--- +---Bind the pipe to a file path (Unix) or a name (Windows). +--- +---**Note**: Paths on Unix get truncated to sizeof(sockaddr_un.sun_path) bytes, +---typically between 92 and 108 bytes. +--- +---@param pipe uv_pipe_t +---@param name string +---@return 0|nil success, string? err_name, string? err_msg +function uv.pipe_bind(pipe, name) end +uv_pipe_t.bind = uv.pipe_bind + +--- +---Connect to the Unix domain socket or the named pipe. +--- +---**Note**: Paths on Unix get truncated to sizeof(sockaddr_un.sun_path) bytes, +---typically between 92 and 108 bytes. +--- +---@param pipe uv_pipe_t +---@param name string +---@param callback fun(err?: string)|nil +---@return uv_connect_t|nil stream, string? err_name, string? err_msg +function uv.pipe_connect(pipe, name, callback) end +uv_pipe_t.connect = uv.pipe_connect + +--- +---Get the name of the Unix domain socket or the named pipe. +--- +---@param pipe uv_pipe_t +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.pipe_getsockname(pipe) end +uv_pipe_t.getsockname = uv.pipe_getsockname + +--- +---Get the name of the Unix domain socket or the named pipe to which the handle is +---connected. +--- +---@param pipe uv_pipe_t +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.pipe_getpeername(pipe) end +uv_pipe_t.getpeername = uv.pipe_getpeername + +--- +---Set the number of pending pipe instance handles when the pipe server is waiting +---for connections. +--- +---**Note**: This setting applies to Windows only. +--- +---@param pipe uv_pipe_t +---@param count integer +function uv.pipe_pending_instances(pipe, count) end +uv_pipe_t.pending_instances = uv.pipe_pending_instances + +--- +---Returns the pending pipe count for the named pipe. +--- +---@param pipe uv_pipe_t +---@return integer +---@nodiscard +function uv.pipe_pending_count(pipe) end +uv_pipe_t.pending_count = uv.pipe_pending_count + +--- +---Used to receive handles over IPC pipes. +--- +---First - call `uv.pipe_pending_count()`, if it's > 0 then initialize a handle of +---the given type, returned by `uv.pipe_pending_type()` and call +---`uv.accept(pipe, handle)`. +--- +---@param pipe uv_pipe_t +---@return string +---@nodiscard +function uv.pipe_pending_type(pipe) end +uv_pipe_t.pending_type = uv.pipe_pending_type + +--- +---Alters pipe permissions, allowing it to be accessed from processes run by different users. +---Makes the pipe writable or readable by all users. +---See below for accepted flags, where `r` is `READABLE` and `w` is `WRITABLE`. +---This function is blocking. +--- +---@param pipe uv_pipe_t +---@param flags uv.aliases.pipe_chmod_flags +---@return 0|nil success, string? err_name, string? err_msg +function uv.pipe_chmod(pipe, flags) end +uv_pipe_t.chmod = uv.pipe_chmod + +--- +---Create a pair of connected pipe handles. Data may be written to the `write` fd and read from the `read` fd. +---The resulting handles can be passed to `pipe_open`, used with `spawn`, or for any other purpose. +--- +---Flags: +--- - `nonblock`: Opens the specified socket handle for `OVERLAPPED` or `FIONBIO`/`O_NONBLOCK` I/O usage. +--- This is recommended for handles that will be used by libuv, and not usually recommended otherwise. +--- +---Equivalent to `pipe(2)` with the `O_CLOEXEC` flag set. +--- +---```lua +----- Simple read/write with pipe_open +---local fds = uv.pipe({nonblock=true}, {nonblock=true}) +--- +---local read_pipe = uv.new_pipe() +---read_pipe:open(fds.read) +--- +---local write_pipe = uv.new_pipe() +---write_pipe:open(fds.write) +--- +---write_pipe:write("hello") +---read_pipe:read_start(function(err, chunk) +--- assert(not err, err) +--- print(chunk) +---end) +---``` +--- +---@param read_flags uv.aliases.pipe_flags|nil # (nonblock default: `false`) +---@param write_flags uv.aliases.pipe_flags|nil # (nonblock default: `false`) +---@return uv.aliases.pipe_rtn|nil, string? err_name, string? err_msg +---@nodiscard +function uv.pipe(read_flags, write_flags) end + +--- +---Bind the pipe to a file path (Unix) or a name (Windows). +--- +---`Flags`: +--- +--- - If `type(flags)` is `number`, it must be `0` or `uv.constants.PIPE_NO_TRUNCATE`. +--- - If `type(flags)` is `table`, it must be `{}` or `{ no_trunate = true|false }`. +--- - If `type(flags)` is `nil`, it use default value `0`. +--- - Returns `EINVAL` for unsupported flags without performing the bind operation. +--- +---Supports Linux abstract namespace sockets. namelen must include the leading '\0' byte but not the trailing nul byte. +--- +---**Note**: +---1. Paths on Unix get truncated to sizeof(sockaddr_un.sun_path) bytes, +---typically between 92 and 108 bytes. +---2. New in version 1.46.0. +--- +---@param pipe uv_pipe_t +---@param name string +---@param flags integer|uv.aliases.pipe_2_flags|nil # (default: 0) +---@return 0|nil success, string? err_name, string? err_msg +function uv.pipe_bind2(pipe, name, flags) end +uv_pipe_t.bind2 = uv.pipe_bind2 + +--- +---Connect to the Unix domain socket or the named pipe. +--- +---`Flags`: +--- +--- - If `type(flags)` is `number`, it must be `0` or `uv.constants.PIPE_NO_TRUNCATE`. +--- - If `type(flags)` is `table`, it must be `{}` or `{ no_trunate = true|false }`. +--- - If `type(flags)` is `nil`, it use default value `0`. +--- - Returns `EINVAL` for unsupported flags without performing the bind operation. +--- +---Supports Linux abstract namespace sockets. namelen must include the leading nul byte but not the trailing nul byte. +--- +---**Note**: +---1. Paths on Unix get truncated to sizeof(sockaddr_un.sun_path) bytes, +---typically between 92 and 108 bytes. +---2. New in version 1.46.0. +--- +---@param pipe uv_pipe_t +---@param name string +---@param flags integer|uv.aliases.pipe_2_flags|nil # (default: 0) +---@param callback fun(err?: string)|nil +---@return uv_connect_t|nil stream, string? err_name, string? err_msg +function uv.pipe_connect2(pipe, name, flags, callback) end +uv_pipe_t.connect2 = uv.pipe_connect2 + + + +--- +---TTY handles represent a stream for the console. +--- +---```lua +----- Simple echo program +---local stdin = uv.new_tty(0, true) +---local stdout = uv.new_tty(1, false) +--- +---stdin:read_start(function (err, data) +--- assert(not err, err) +--- if data then +--- stdout:write(data) +--- else +--- stdin:close() +--- stdout:close() +--- end +---end) +---``` +--- +---@class uv_tty_t: uv_stream_t +---@section TTY handle +local uv_tty_t = {} + +---@alias uv.aliases.tty_fd +---|0 # stdin +---|1 # stdout +---|2 # stderr + +---@alias uv.aliases.tty_mode +---|0 # UV_TTY_MODE_NORMAL: Initial/normal terminal mode +---|1 # UV_TTY_MODE_RAW: Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) +---|2 # UV_TTY_MODE_IO: Binary-safe I/O mode for IPC (Unix-only) + +---@alias uv.aliases.tty_vsterm_state +---|'supported' +---|'unsupported' + +--- +---Initialize a new TTY stream with the given file descriptor. +---See below for possible file descriptors. +--- +---On Unix this function will determine the path of the fd of the terminal using +---ttyname_r(3), open it, and use it if the passed file descriptor refers to a TTY. +--- +---This lets libuv put the tty in non-blocking mode without affecting other +---processes that share the tty. +--- +---This function is not thread safe on systems that don’t support ioctl TIOCGPTN or TIOCPTYGNAME, for instance OpenBSD and Solaris. +--- +---**Note:** If reopening the TTY fails, libuv falls back to blocking writes. +--- +---@param fd uv.aliases.tty_fd|integer +---@param readable boolean +---@return uv_tty_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_tty(fd, readable) end + +--- +---Set the TTY using the specified terminal mode. +--- +---Parameter `mode` is a C enum with the values below. +--- +---@param tty uv_tty_t +---@param mode uv.aliases.tty_mode +---@return 0|nil success, string? err_name, string? err_msg +function uv.tty_set_mode(tty, mode) end +uv_tty_t.set_mode = uv.tty_set_mode + +--- +---To be called when the program exits. Resets TTY settings to default values for +---the next process to take over. +---This function is async signal-safe on Unix platforms but can fail with error +---code `EBUSY` if you call it when execution is inside `uv.tty_set_mode()`. +--- +---@return 0|nil success, string? err_name, string? err_msg +function uv.tty_reset_mode() end + +--- +---Gets the current Window width and height. +--- +---@param tty uv_tty_t +---@return integer|nil width, integer|string height_or_errname, string? err_msg +---@nodiscard +function uv.tty_get_winsize(tty) end +uv_tty_t.get_winsize = uv.tty_get_winsize + +--- +---Controls whether console virtual terminal sequences are processed by libuv or +---console. Useful in particular for enabling ConEmu support of ANSI X3.64 and +---Xterm 256 colors. Otherwise Windows10 consoles are usually detected +---automatically. State should be one of: `"supported"` or `"unsupported"`. +--- +---This function is only meaningful on Windows systems. On Unix it is silently +---ignored. +--- +---@param state uv.aliases.tty_vsterm_state +function uv.tty_set_vterm_state(state) end + +--- +---Get the current state of whether console virtual terminal sequences are handled +---by libuv or the console. The return value is `"supported"` or `"unsupported"`. +---This function is not implemented on Unix, where it returns `ENOTSUP`. +--- +---@return uv.aliases.tty_vsterm_state|nil, string? err_name, string? err_msg +---@nodiscard +function uv.tty_get_vterm_state() end + + + +--- +---UDP handles encapsulate UDP communication for both clients and servers. +--- +---@class uv_udp_t: uv_handle_t +---@section UDP handle +local uv_udp_t = {} + +---@class uv_udp_send_t: userdata + +---@alias uv.aliases.new_udp_flags {family: uv.aliases.network_family, mmsgs: integer} + +---@alias uv.aliases.udp_bind_flags {ipv6only: boolean, reuseaddr: boolean} + +---@alias uv.aliases.udp_getsockname_rtn uv.aliases.getsockname_rtn + +---@alias uv.aliases.udp_getpeername_rtn uv.aliases.getpeername_rtn + +---@alias uv.aliases.udp_membership '"join"'|'"leave"' + +---@alias uv.aliases.udp_recv_start_callback_flags {partial: boolean|nil, mmsg_chunk: boolean|nil} + +--- +---Creates and initializes a new `uv_udp_t`. Returns the Lua userdata wrapping +---it. The actual socket is created lazily. +--- +---See below for accepted `family` values. +--- +---When specified, `mmsgs` determines the number of messages able to be received +---at one time via `recvmmsg(2)` (the allocated buffer will be sized to be able +---to fit the specified number of max size dgrams). Only has an effect on +---platforms that support `recvmmsg(2)`. +--- +---**Note:** For backwards compatibility reasons, `flags` can also be a string or +---integer. When it is a string, it will be treated like the `family` key above. +---When it is an integer, it will be used directly as the `flags` parameter when +---calling `uv_udp_init_ex`. +--- +---@param flags uv.aliases.new_udp_flags|uv.aliases.network_family|integer # (mmsgs default: `1`) +---@return uv_udp_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_udp(flags) end +---@return uv_udp_t +---@nodiscard +function uv.new_udp() end + +--- +---Returns the handle's send queue size. +--- +---@return integer +---@nodiscard +function uv.udp_get_send_queue_size() end +uv_udp_t.get_send_queue_size = uv.udp_get_send_queue_size + +--- +---Returns the handle's send queue count. +--- +---@return integer +---@nodiscard +function uv.udp_get_send_queue_count() end +uv_udp_t.get_send_queue_count = uv.udp_get_send_queue_count + +--- +---Opens an existing file descriptor or Windows SOCKET as a UDP handle. +--- +---Unix only: The only requirement of the sock argument is that it follows the +---datagram contract (works in unconnected mode, supports sendmsg()/recvmsg(), +---etc). In other words, other datagram-type sockets like raw sockets or netlink +---sockets can also be passed to this function. +--- +---The file descriptor is set to non-blocking mode. +--- +---**Note:** The passed file descriptor or SOCKET is not checked for its type, but +---it's required that it represents a valid datagram socket. +--- +---@param udp uv_udp_t +---@param fd integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_open(udp, fd) end +uv_udp_t.open = uv.udp_open + +--- +---Bind the UDP handle to an IP address and port. Any `flags` are set with a table +---with fields `reuseaddr` or `ipv6only` equal to `true` or `false`. +--- +---@param udp uv_udp_t +---@param host string +---@param port integer +---@param flags uv.aliases.udp_bind_flags|nil +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_bind(udp, host, port, flags) end +uv_udp_t.bind = uv.udp_bind + +--- +---Get the local IP and port of the UDP handle. +--- +---@param udp uv_udp_t +---@return uv.aliases.udp_getsockname_rtn|nil, string? err_name, string? err_msg +---@nodiscard +function uv.udp_getsockname(udp) end +uv_udp_t.getsockname = uv.udp_getsockname + +--- +---Get the remote IP and port of the UDP handle on connected UDP handles. +--- +---@param udp uv_udp_t +---@return uv.aliases.udp_getpeername_rtn|nil, string? err_name, string? err_msg +---@nodiscard +function uv.udp_getpeername(udp) end +uv_udp_t.getpeername = uv.udp_getpeername + +--- +---Set membership for a multicast address. `multicast_addr` is multicast address to +---set membership for. `interface_addr` is interface address. `membership` can be +---the string `"leave"` or `"join"`. +--- +---@param udp uv_udp_t +---@param multicast_addr string +---@param interface_addr string|nil +---@param membership uv.aliases.udp_membership +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_membership(udp, multicast_addr, interface_addr, membership) end +uv_udp_t.set_membership = uv.udp_set_membership + +--- +---Set membership for a source-specific multicast group. `multicast_addr` is multicast +---address to set membership for. `interface_addr` is interface address. `source_addr` +---is source address. `membership` can be the string `"leave"` or `"join"`. +--- +---@param udp uv_udp_t +---@param multicast_addr string +---@param interface_addr string|nil +---@param source_addr string +---@param membership uv.aliases.udp_membership +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_source_membership(udp, multicast_addr, interface_addr, source_addr, membership) end +uv_udp_t.set_source_membership = uv.udp_set_source_membership + +--- +---Set IP multicast loop flag. Makes multicast packets loop back to local +---sockets. +--- +---@param udp uv_udp_t +---@param on boolean +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_multicast_loop(udp, on) end +uv_udp_t.set_multicast_loop = uv.udp_set_multicast_loop + +--- +---Set the multicast ttl. +---`ttl` is an integer 1 through 255. +--- +---@param udp uv_udp_t +---@param ttl integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_multicast_ttl(udp, ttl) end +uv_udp_t.set_multicast_ttl = uv.udp_set_multicast_ttl + +--- +---Set the multicast interface to send or receive data on. +--- +---@param udp uv_udp_t +---@param interface_addr string +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_multicast_interface(udp, interface_addr) end +uv_udp_t.set_multicast_interface = uv.udp_set_multicast_interface + +--- +---Set broadcast on or off. +--- +---@param udp uv_udp_t +---@param on boolean +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_broadcast(udp, on) end +uv_udp_t.set_broadcast = uv.udp_set_broadcast + +--- +---Set the time to live. +---`ttl` is an integer 1 through 255. +--- +---@param udp uv_udp_t +---@param ttl integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_ttl(udp, ttl) end +uv_udp_t.set_ttl = uv.udp_set_ttl + +--- +---Send data over the UDP socket. If the socket has not previously been bound +---with `uv.udp_bind()` it will be bound to `0.0.0.0` (the "all interfaces" IPv4 +---address) and a random port number. +--- +---@param udp uv_udp_t +---@param data uv.aliases.buffer +---@param host string +---@param port integer +---@param callback fun(err?: string) +---@return uv_udp_send_t|nil stream, string? err_name, string? err_msg +function uv.udp_send(udp, data, host, port, callback) end +uv_udp_t.send = uv.udp_send + +--- +---Same as `uv.udp_send()`, but won't queue a send request if it can't be +---completed immediately. +--- +---@param udp uv_udp_t +---@param data uv.aliases.buffer +---@param host string +---@param port integer +---@return integer|nil, string? err_name, string? err_msg +function uv.udp_try_send(udp, data, host, port) end +uv_udp_t.try_send = uv.udp_try_send + +--- +---Prepare for receiving data. If the socket has not previously been bound with +---`uv.udp_bind()` it is bound to `0.0.0.0` (the "all interfaces" IPv4 address) +---and a random port number. +--- +---@param udp uv_udp_t +---@param callback fun(err?: string, data?: string, add?: uv.aliases.udp_getpeername_rtn, flags: uv.aliases.udp_recv_start_callback_flags) +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_recv_start(udp, callback) end +uv_udp_t.recv_start = uv.udp_recv_start + +--- +---Stop listening for incoming datagrams. +--- +---@param udp uv_udp_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_recv_stop(udp) end +uv_udp_t.recv_stop = uv.udp_recv_stop + +--- +---Associate the UDP handle to a remote address and port, so every message sent by +---this handle is automatically sent to that destination. Calling this function +---with a NULL addr disconnects the handle. Trying to call `uv.udp_connect()` on an +---already connected handle will result in an `EISCONN` error. Trying to disconnect +---a handle that is not connected will return an `ENOTCONN` error. +--- +---@param udp uv_udp_t +---@param host string +---@param port integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_connect(udp, host, port) end +uv_udp_t.connect = uv.udp_connect + + + +--- +---FS Event handles allow the user to monitor a given path for changes, for +---example, if the file was renamed or there was a generic change in it. This +---handle uses the best backend for the job on each platform. +--- +---@class uv_fs_event_t: uv_handle_t +---@section FS Event handle +local uv_fs_event_t = {} + +---@alias uv.aliases.fs_event_start_flags {watch_entry?: boolean, stat?: boolean, recursive?: boolean} + +---@alias uv.aliases.fs_event_start_callback_events {change?: boolean, rename?: boolean} + +--- +---Creates and initializes a new `uv_fs_event_t`. +---Returns the Lua userdata wrapping it. +--- +---@return uv_fs_event_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_fs_event() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- can fail in case the feature is not supported, see unix/no-fsevents.c + +--- +---Start the handle with the given callback, which will watch the specified path +---for changes. +--- +---@param fs_event uv_fs_event_t +---@param path string +---@param flags uv.aliases.fs_event_start_flags # (all flags have default of `false`) +---@param callback fun(err?: string, filename: string, events: uv.aliases.fs_event_start_callback_events) +---@return 0|nil success, string? err_name, string? err_msg +function uv.fs_event_start(fs_event, path, flags, callback) end +uv_fs_event_t.start = uv.fs_event_start + +--- +---Stop the handle, the callback will no longer be called. +--- +---@return 0|nil success, string? err_name, string? err_msg +function uv.fs_event_stop() end +uv_fs_event_t.stop = uv.fs_event_stop + +--- +---Get the path being monitored by the handle. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.fs_event_getpath() end +uv_fs_event_t.getpath = uv.fs_event_getpath + + + +--- +---FS Poll handles allow the user to monitor a given path for changes. Unlike +---`uv_fs_event_t`, fs poll handles use `stat` to detect when a file has changed so +---they can work on file systems where fs event handles can't. +--- +---@class uv_fs_poll_t: uv_handle_t +---@section FS Poll handle +local uv_fs_poll_t = {} + +--- +---Creates and initializes a new `uv_fs_poll_t`. +---Returns the Lua userdata wrapping it. +--- +---@return uv_fs_poll_t +---@nodiscard +function uv.new_fs_poll() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- confirmed to never return error see libuv/fs-poll#uv_fs_poll_init + +--- +---Check the file at `path` for changes every `interval` milliseconds. +--- +---**Note:** For maximum portability, use multi-second intervals. Sub-second +---intervals will not detect all changes on many file systems. +--- +---@param fs_poll uv_fs_poll_t +---@param path string +---@param interval integer +---@param callback fun(err?: string, prev: uv.aliases.fs_stat_table, curr: uv.aliases.fs_stat_table) +---@return 0|nil success, string? err_name, string? err_msg +function uv.fs_poll_start(fs_poll, path, interval, callback) end +uv_fs_poll_t.start = uv.fs_poll_start + +--- +---Stop the handle, the callback will no longer be called. +--- +---@return 0|nil success, string? err_name, string? err_msg +function uv.fs_poll_stop() end +uv_fs_poll_t.stop = uv.fs_poll_stop + +--- +---Get the path being monitored by the handle. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.fs_poll_getpath() end +uv_fs_poll_t.getpath = uv.fs_poll_getpath + + + +--- +---@section File system operations +--- +---Most file system functions can operate synchronously or asynchronously. When a synchronous version is called (by omitting a callback), the function will +---immediately return the results of the FS call. When an asynchronous version is +---called (by providing a callback), the function will immediately return a +---`uv_fs_t` and asynchronously execute its callback; if an error is encountered, the first and only argument passed to the callback will be the `err` error string; if the operation completes successfully, the first argument will be `nil` and the remaining arguments will be the results of the FS call. +--- +---Synchronous and asynchronous versions of `readFile` (with naive error handling) +---are implemented below as an example: +--- +---```lua +---local function readFileSync(path) +--- local fd = assert(uv.fs_open(path, "r", 438)) +--- local stat = assert(uv.fs_fstat(fd)) +--- local data = assert(uv.fs_read(fd, stat.size, 0)) +--- assert(uv.fs_close(fd)) +--- return data +---end +--- +---local data = readFileSync("main.lua") +---print("synchronous read", data) +---``` +--- +---```lua +---local function readFile(path, callback) +--- uv.fs_open(path, "r", 438, function(err, fd) +--- assert(not err, err) +--- uv.fs_fstat(fd, function(err, stat) +--- assert(not err, err) +--- uv.fs_read(fd, stat.size, 0, function(err, data) +--- assert(not err, err) +--- uv.fs_close(fd, function(err) +--- assert(not err, err) +--- return callback(data) +--- end) +--- end) +--- end) +--- end) +---end +--- +---readFile("main.lua", function(data) +--- print("asynchronous read", data) +---end) +---``` +--- + +---@class uv_fs_t: uv_req_t + +---@class luv_dir_t: userdata +local luv_dir_t = {} + +---@alias uv.aliases.fs_access_flags +---Open file for reading. +--- +---Fails if the file does not exist. +--- +---Mode: O_RDONLY. +---|'"r"' +---Open file for reading in synchronous mode. +---Instructs the operating system to bypass the local file system cache. +--- +---This is primarily useful for opening files on NFS mounts as it allows you to +---skip the potentially stale local cache. It has a very real impact on I/O +---performance so don't use this flag unless you need it. +--- +---Note that this doesn't turn this call into a synchronous blocking call. +--- +---Mode: O_RDONLY + O_SYNC. +---|'"rs"' +---Same as `'rs'`. +---|'"sr"' +---Open file for reading and writing. +--- +---Fails if the file does not exist. +--- +---Mode: O_RDWR. +---|'"r+"' +---Open file for reading and writing, telling the OS to open it synchronously. +--- +---See notes for `'rs'` about using this with caution. +--- +---Mode: O_RDWR + O_SYNC. +---|'"rs+"' +---Same as `'rs+'`. +---|'"sr+"' +---Open file for writing. +--- +---The file is created (if it does not exist) or truncated (if it exists). +--- +---Mode: O_TRUNC + O_CREAT + O_WRONLY. +---|'"w"' +---Open file for writing. +--- +---Fails if the file exists. +--- +---Mode: O_TRUNC + O_CREAT + O_WRONLY + O_EXCL. +---|'"wx"' +---Same as `'wx'`. +---|'"xw"' +---Open file for reading and writing. +--- +---The file is created (if it does not exist) or truncated (if it exists). +--- +---Mode: O_TRUNC + O_CREAT + O_RDWR. +---|'"w+"' +---Open file for reading and writing. +--- +---Fails if file exists. +--- +---Mode: O_TRUNC + O_CREAT + O_RDWR + O_EXCL. +---|'"wx+"' +---Same as `'wx+'`. +---|'"xw+"' +---Open file for appending. +--- +---The file is created if it does not exist. +--- +---Mode: O_APPEND + O_CREAT + O_WRONLY. +---|'"a"' +---Open file for appending. +--- +---Fails if the file exists. +--- +---Mode: O_APPEND + O_CREAT + O_WRONLY + O_EXCL. +---|'"ax"' +---Same as `'ax'`. +---|'"xa"' +---Open file for reading and appending. +--- +---The file is created if it does not exist. +--- +---Mode: O_APPEND + O_CREAT + O_RDWR. +---|'"a+"' +---Like `'a+'` but fails if `path` exists. +--- +---Mode: O_APPEND + O_CREAT + O_RDWR + O_EXCL +---|'"ax+"' +---Same as `'ax+'`. +---|'"xa+"' + +---@alias uv.aliases.fs_stat_types +---|'"file"' +---|'"directory"' +---|'"link"' +---|'"fifo"' +---|'"socket"' +---|'"char"' +---|'"block"' + +---@alias uv.aliases.fs_stat_table {gen: integer, flags: integer, atime: {nsec: integer, sec: integer}, ctime: {nsec: integer, sec: integer}, birthtime: {nsec: integer, sec: integer}, uid: integer, gid: integer, mtime: {nsec: integer, sec: integer}, size: integer, type?: uv.aliases.fs_stat_types, nlink: integer, dev: integer, mode: integer, rdev: integer, ino: integer, blksize: integer, blocks: integer} + +---@alias uv.aliases.fs_types +---|'"file"' +---|'"directory"' +---|'"link"' +---|'"fifo"' +---|'"socket"' +---|'"char"' +---|'"block"' +---|'"unknown"' + +---@alias uv.aliases.fs_access_mode +---Tests for readbility. +---|'R' +---Tests for writiblity. +---|'W' +---Tests for executibility. +---|'X' +---Tests for readbility and writiblity. +---|'RW' +---Tests for readbility and executibility. +---|'RX' +---Tests for writiblity and executibility. +---|'WX' +---Tests for writiblity and readbility and executibility. +---|'WRX' +---A bitwise OR mask. +---|integer + +---@alias uv.aliases.fs_readdir_entries {type: uv.aliases.fs_types, name: string} + +---@alias uv.aliases.fs_symlink_flags {dir: boolean, junction: boolean} + +---@alias uv.aliases.fs_copyfile_flags {excl: boolean, ficlone: boolean, ficlone_force: boolean} + +---@alias uv.aliases.fs_statfs_stats_type +---Always 0 on Windows, sun, MVS, OpenBSD, NetBSD, HAIKU, QNK. +---|0 +---ADFS_SUPER_MAGIC = 0xadf5 +---|44533 +---AFFS_SUPER_MAGIC = 0xadff +---|44543 +---AFS_SUPER_MAGIC = 0x5346414f +---|1397113167 +---ANON_INODE_FS_MAGIC = 0x09041934 | Anonymous inode FS (for pseudofiles that have no name; e.g., epoll, signalfd, bpf) +---|151263540 +---AUTOFS_SUPER_MAGIC = 0x0187 +---|391 +---BDEVFS_MAGIC = 0x62646576 +---|1650746742 +---BEFS_SUPER_MAGIC = 0x42465331 +---|1111905073 +---BFS_MAGIC = 0x1badface +---|464386766 +---BINFMTFS_MAGIC = 0x42494e4d +---|1112100429 +---BPF_FS_MAGIC = 0xcafe4a11 +---|3405662737 +---BTRFS_SUPER_MAGIC = 0x9123683e +---|2435016766 +---BTRFS_TEST_MAGIC = 0x73727279 +---|1936880249 +---CGROUP_SUPER_MAGIC = 0x27e0eb | Cgroup pseudo FS +---|2613483 +---CGROUP2_SUPER_MAGIC = 0x63677270 | Cgroup v2 pseudo FS +---|1667723888 +---CIFS_MAGIC_NUMBER = 0xff534d42 +---|4283649346 +---CODA_SUPER_MAGIC = 0x73757245 +---|1937076805 +---COH_SUPER_MAGIC = 0x012ff7b7 +---|19920823 +---CRAMFS_MAGIC = 0x28cd3d45 +---|684539205 +---DEBUGFS_MAGIC = 0x64626720 +---|1684170528 +---DEVFS_SUPER_MAGIC = 0x1373 | Linux 2.6.17 and earlier +---|4979 +---DEVPTS_SUPER_MAGIC = 0x1cd1 +---|7377 +---ECRYPTFS_SUPER_MAGIC = 0xf15f +---|61791 +---EFIVARFS_MAGIC = 0xde5e81e4 +---|3730735588 +---EFS_SUPER_MAGIC = 0x00414a53 +---|4278867 +---EXT_SUPER_MAGIC = 0x137d | Linux 2.0 and earlier +---|4989 +---EXT2_OLD_SUPER_MAGIC = 0xef51 +---|61265 +---EXT2_SUPER_MAGIC = 0xef53 +---|61267 +---EXT3_SUPER_MAGIC = 0xef53 +---|61267 +---EXT4_SUPER_MAGIC = 0xef53 +---|61267 +---F2FS_SUPER_MAGIC = 0xf2f52010 +---|4076150800 +---FUSE_SUPER_MAGIC = 0x65735546 +---|1702057286 +---FUTEXFS_SUPER_MAGIC = 0xbad1dea | Unused +---|195894762 +---HFS_SUPER_MAGIC = 0x4244 +---|16964 +---HOSTFS_SUPER_MAGIC = 0x00c0ffee +---|12648430 +---HPFS_SUPER_MAGIC = 0xf995e849 +---|4187351113 +---HUGETLBFS_MAGIC = 0x958458f6 +---|2508478710 +---ISOFS_SUPER_MAGIC = 0x9660 +---|38496 +---JFFS2_SUPER_MAGIC = 0x72b6 +---|29366 +---JFS_SUPER_MAGIC = 0x3153464a +---|827541066 +---MINIX_SUPER_MAGIC = 0x137f | original minix FS +---|4991 +---MINIX_SUPER_MAGIC2 = 0x138f | 30 char minix FS +---|5007 +---MINIX2_SUPER_MAGIC = 0x2468 | minix V2 FS +---|9320 +---MINIX2_SUPER_MAGIC2 = 0x2478 | minix V2 FS, 30 char names +---|9336 +---MINIX3_SUPER_MAGIC = 0x4d5a | minix V3 FS, 60 char names +---|19802 +---MQUEUE_MAGIC = 0x19800202 | POSIX message queue FS +---|427819522 +---MSDOS_SUPER_MAGIC = 0x4d44 +---|19780 +---MTD_INODE_FS_MAGIC = 0x11307854 +---|288389204 +---NCP_SUPER_MAGIC = 0x564c +---|22092 +---NFS_SUPER_MAGIC = 0x6969 +---|26985 +---NILFS_SUPER_MAGIC = 0x3434 +---|13364 +---NSFS_MAGIC = 0x6e736673 +---|1853056627 +---NTFS_SB_MAGIC = 0x5346544e +---|1397118030 +---OCFS2_SUPER_MAGIC = 0x7461636f +---|1952539503 +---OPENPROM_SUPER_MAGIC = 0x9fa1 +---|40865 +---OVERLAYFS_SUPER_MAGIC = 0x794c7630 +---|2035054128 +---PIPEFS_MAGIC = 0x50495045 +---|1346981957 +---PROC_SUPER_MAGIC = 0x9fa0 | /proc FS +---|40864 +---PSTOREFS_MAGIC = 0x6165676c +---|1634035564 +---QNX4_SUPER_MAGIC = 0x002f +---|47 +---QNX6_SUPER_MAGIC = 0x68191122 +---|1746473250 +---RAMFS_MAGIC = 0x858458f6 +---|2240043254 +---REISERFS_SUPER_MAGIC = 0x52654973 +---|1382369651 +---ROMFS_MAGIC = 0x7275 +---|29301 +---SECURITYFS_MAGIC = 0x73636673 +---|1935894131 +---SELINUX_MAGIC = 0xf97cff8c +---|4185718668 +---SMACK_MAGIC = 0x43415d53 +---|1128357203 +---SMB_SUPER_MAGIC = 0x517b +---|20859 +---SMB2_MAGIC_NUMBER = 0xfe534d42 +---|4266872130 +---SOCKFS_MAGIC = 0x534f434b +---|1397703499 +---SQUASHFS_MAGIC = 0x73717368 +---|1936814952 +---SYSFS_MAGIC = 0x62656572 +---|1650812274 +---SYSV2_SUPER_MAGIC = 0x012ff7b6 +---|19920822 +---SYSV4_SUPER_MAGIC = 0x012ff7b5 +---|19920821 +---TMPFS_MAGIC = 0x01021994 +---|16914836 +---TRACEFS_MAGIC = 0x74726163 +---|1953653091 +---UDF_SUPER_MAGIC = 0x15013346 +---|352400198 +---UFS_MAGIC = 0x00011954 +---|72020 +---USBDEVICE_SUPER_MAGIC = 0x9fa2 +---|40866 +---V9FS_MAGIC = 0x01021997 +---|16914839 +---VXFS_SUPER_MAGIC = 0xa501fcf5 +---|2768370933 +---XENFS_SUPER_MAGIC = 0xabba1974 +---|2881100148 +---XENIX_SUPER_MAGIC = 0x012ff7b4 +---|19920820 +---XFS_SUPER_MAGIC = 0x58465342 +---|1481003842 +---_XIAFS_SUPER_MAGIC = 0x012fd16d | Linux 2.0 and earlier +---|19911021 + +---@alias uv.aliases.fs_statfs_stats {type: uv.aliases.fs_statfs_stats_type, bzise: integer, blocks: integer, bfree: integer, bavail: integer, files: integer, ffree: integer} + +--- +---Equivalent to `close(2)`. +--- +---@param fd integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_close(fd, callback) end +---@param fd integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_close(fd) end + +--- +---Equivalent to `open(2)`. +---See below for available access `flags`. +--- +---**Note:** On Windows, libuv uses `CreateFileW` and thus the file is always +---opened in binary mode. Because of this, the `O_BINARY` and `O_TEXT` flags are +---not supported. +--- +---@param path string +---@param flags uv.aliases.fs_access_flags|integer +---@param mode integer +---@param callback fun(err: nil|string, fd: integer|nil) +---@return uv_fs_t +function uv.fs_open(path, flags, mode, callback) end +---@param path string +---@param flags uv.aliases.fs_access_flags|integer +---@param mode integer +---@return integer|nil fd, string? err_name, string? err_msg +---@nodiscard +function uv.fs_open(path, flags, mode) end + +--- +---Equivalent to `preadv(2)`. Returns any data. An empty string indicates EOF. +--- +---If `offset` is nil or omitted, it will default to `-1`, which indicates 'use and update the current file offset.' +--- +---**Note:** When `offset` is >= 0, the current file offset will not be updated by the read. +--- +---@param fd integer +---@param size integer +---@param offset integer|nil +---@param callback fun(err: nil|string, data: string|nil) +---@return uv_fs_t +function uv.fs_read(fd, size, offset, callback) end +---@param fd integer +---@param size integer +---@param callback fun(err: nil|string, data: string|nil) +---@return uv_fs_t +function uv.fs_read(fd, size, callback) end +---@param fd integer +---@param size integer +---@param offset integer|nil +---@return string|nil data, string? err_name, string? err_msg +---@nodiscard +function uv.fs_read(fd, size, offset) end + +--- +---Equivalent to `unlink(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_unlink(path, callback) end +---@param path string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_unlink(path) end + +--- +---Equivalent to `pwritev(2)`. Returns the number of bytes written. +--- +---If `offset` is nil or omitted, it will default to `-1`, which indicates 'use and update the current file offset.' +--- +---**Note:** When `offset` is >= 0, the current file offset will not be updated by the write. +--- +---@param fd integer +---@param data uv.aliases.buffer +---@param offset integer|nil +---@param callback fun(err: nil|string, bytes: integer|nil) +---@return uv_fs_t +function uv.fs_write(fd, data, offset, callback) end +---@param fd integer +---@param data uv.aliases.buffer +---@param callback fun(err: nil|string, bytes: integer|nil) +---@return uv_fs_t +function uv.fs_write(fd, data, callback) end +---@param fd integer +---@param data uv.aliases.buffer +---@param offset integer|nil +---@return integer|nil bytes, string? err_name, string? err_msg +function uv.fs_write(fd, data, offset) end + +--- +---Equivalent to `mkdir(2)`. +--- +---@param path string +---@param mode integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_mkdir(path, mode, callback) end +---@param path string +---@param mode integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_mkdir(path, mode) end + +--- +---Equivalent to `mkdtemp(3)`. +--- +---@param template string +---@param callback fun(err: nil|string, path: string|nil) +---@return uv_fs_t +function uv.fs_mkdtemp(template, callback) end +---@param template string +---@return string|nil path, string? err_name, string? err_msg +---@nodiscard +function uv.fs_mkdtemp(template) end + +--- +---Equivalent to `mkstemp(3)`. Returns a temporary file handle and filename. +--- +---@param template string +---@param callback fun(err: nil|string, fd: integer|nil, path: string|nil) +---@return uv_fs_t +function uv.fs_mkstemp(template, callback) end +---@param template string +---@return integer|nil fd, string path_or_errname, string? err_msg +---@nodiscard +function uv.fs_mkstemp(template) end + +--- +---Equivalent to `rmdir(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_rmdir(path, callback) end +---@param path string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_rmdir(path) end + +--- +---Equivalent to `scandir(3)`, with a slightly different API. Returns a handle that +---the user can pass to `uv.fs_scandir_next()`. +--- +---**Note:** This function can be used synchronously or asynchronously. The request +---userdata is always synchronously returned regardless of whether a callback is +---provided and the same userdata is passed to the callback if it is provided. +--- +---@param path string +---@param callback fun(err: nil|string, success: uv_fs_t|nil) +---@return uv_fs_t|nil, string? err_name, string? err_msg +function uv.fs_scandir(path, callback) end +---@param path string +---@return uv_fs_t|nil success, string? err_name, string? err_msg +---@nodiscard +function uv.fs_scandir(path) end + +--- +---Called on a `uv_fs_t` returned by `uv.fs_scandir()` to get the next directory +---entry data as a `name, type` pair. When there are no more entries, `nil` is +---returned. +--- +---**Note:** This function only has a synchronous version. See `uv.fs_opendir` and +---its related functions for an asynchronous version. +--- +---@param fs uv_fs_t +---@return string|nil, string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.fs_scandir_next(fs) end + +--- +---Equivalent to `stat(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, stat: uv.aliases.fs_stat_table|nil) +---@return uv_fs_t +function uv.fs_stat(path, callback) end +---@param path string +---@return uv.aliases.fs_stat_table|nil stat, string? err_name, string? err_msg +---@nodiscard +function uv.fs_stat(path) end + +--- +---Equivalent to `fstat(2)`. +--- +---@param fd integer +---@param callback fun(err: nil|string, stat: uv.aliases.fs_stat_table|nil) +---@return uv_fs_t +function uv.fs_fstat(fd, callback) end +---@param fd integer +---@return uv.aliases.fs_stat_table|nil stat, string? err_name, string? err_msg +---@nodiscard +function uv.fs_fstat(fd) end + +--- +---Equivalent to `lstat(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, stat: uv.aliases.fs_stat_table|nil) +---@return uv_fs_t +function uv.fs_lstat(path, callback) end +---@param path string +---@return uv.aliases.fs_stat_table|nil stat, string? err_name, string? err_msg +---@nodiscard +function uv.fs_lstat(path) end + +--- +---Equivalent to `rename(2)`. +--- +---@param path string +---@param new_path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_rename(path, new_path, callback) end +---@param path string +---@param new_path string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_rename(path, new_path) end + +--- +---Equivalent to `fsync(2)`. +--- +---@param fd integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_fsync(fd, callback) end +---@param fd integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_fsync(fd) end + +--- +---Equivalent to `fdatasync(2)`. +--- +---@param fd integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_fdatasync(fd, callback) end +---@param fd integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_fdatasync(fd) end + +--- +---Equivalent to `ftruncate(2)`. +--- +---@param fd integer +---@param offset integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_ftruncate(fd, offset, callback) end +---@param fd integer +---@param offset integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_ftruncate(fd, offset) end + +--- +---Limited equivalent to `sendfile(2)`. Returns the number of bytes written. +--- +---@param out_fd integer +---@param in_fd integer +---@param in_offset integer +---@param size integer +---@param callback fun(err: nil|string, bytes: integer|nil) +---@return uv_fs_t +function uv.fs_sendfile(out_fd, in_fd, in_offset, size, callback) end +---@param out_fd integer +---@param in_fd integer +---@param in_offset integer +---@param size integer +---@return integer|nil bytes, string? err_name, string? err_msg +function uv.fs_sendfile(out_fd, in_fd, in_offset, size) end + +--- +---Equivalent to `access(2)` on Unix. Windows uses `GetFileAttributesW()`. Access +---`mode` can be an integer or a string containing `"R"` or `"W"` or `"X"`. +--- +---Returns `true` or `false` indicating access permission. +--- +---@param path string +---@param mode uv.aliases.fs_access_mode|integer +---@param callback fun(err: nil|string, permission: boolean|nil) +---@return uv_fs_t +function uv.fs_access(path, mode, callback) end +---@param path string +---@param mode uv.aliases.fs_access_mode|integer +---@return boolean|nil permission, string? err_name, string? err_msg +---@nodiscard +function uv.fs_access(path, mode) end + +--- +---Equivalent to `chmod(2)`. +--- +---@param path string +---@param mode integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_chmod(path, mode, callback) end +---@param path string +---@param mode integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_chmod(path, mode) end + +--- +---Equivalent to `fchmod(2)`. +--- +---@param fd integer +---@param mode integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_fchmod(fd, mode, callback) end +---@param fd integer +---@param mode integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_fchmod(fd, mode) end + +--- +---Equivalent to `utime(2)`. +--- +---@param path string +---@param atime number +---@param mtime number +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_utime(path, atime, mtime, callback) end +---@param path string +---@param atime number +---@param mtime number +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_utime(path, atime, mtime) end + +--- +---Equivalent to `futime(2)`. +--- +---@param fd integer +---@param atime number +---@param mtime number +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_futime(fd, atime, mtime, callback) end +---@param fd integer +---@param atime number +---@param mtime number +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_futime(fd, atime, mtime) end + +--- +---Equivalent to `lutime(2)`. +--- +---@param path string +---@param atime number +---@param mtime number +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_lutime(path, atime, mtime, callback) end +---@param path string +---@param atime number +---@param mtime number +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_lutime(path, atime, mtime) end + +--- +---Equivalent to `link(2)`. +--- +---@param path string +---@param new_path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_link(path, new_path, callback) end +---@param path string +---@param new_path string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_link(path, new_path) end + +--- +---Equivalent to `symlink(2)`. +---If the `flags` parameter is omitted, then the 3rd parameter will be treated as the `callback`. +--- +---@param path string +---@param new_path string +---@param flags uv.aliases.fs_symlink_flags|integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_symlink(path, new_path, flags, callback) end +---@param path string +---@param new_path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_symlink(path, new_path, callback) end +---@param path string +---@param new_path string +---@param flags? uv.aliases.fs_symlink_flags|integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_symlink(path, new_path, flags) end + +--- +---Equivalent to `readlink(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, path: string|nil) +---@return uv_fs_t +function uv.fs_readlink(path, callback) end +---@param path string +---@return string|nil path, string? err_name, string? err_msg +---@nodiscard +function uv.fs_readlink(path) end + +--- +---Equivalent to `realpath(3)`. +--- +---@param path string +---@param callback fun(err: nil|string, path: string|nil) +---@return uv_fs_t +function uv.fs_realpath(path, callback) end +---@param path string +---@return string|nil path, string? err_name, string? err_msg +---@nodiscard +function uv.fs_realpath(path) end + +--- +---Equivalent to `chown(2)`. +--- +---@param path string +---@param uid integer +---@param gid integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_chown(path, uid, gid, callback) end +---@param path string +---@param uid integer +---@param gid integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_chown(path, uid, gid) end + +--- +---Equivalent to `fchown(2)`. +--- +---@param fd integer +---@param uid integer +---@param gid integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_fchown(fd, uid, gid, callback) end +---@param fd integer +---@param uid integer +---@param gid integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_fchown(fd, uid, gid) end + +--- +---Equivalent to `lchown(2)`. +--- +---@param fd integer +---@param uid integer +---@param gid integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_lchown(fd, uid, gid, callback) end +---@param fd integer +---@param uid integer +---@param gid integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_lchown(fd, uid, gid) end + +--- +---Copies a file from `path` to `new_path`. +---If the `flags` parameter is omitted, then the 3rd parameter will be treated as the `callback`. +--- +---@param path string +---@param new_path string +---@param flags uv.aliases.fs_copyfile_flags +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_copyfile(path, new_path, flags, callback) end +---@param path string +---@param new_path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_copyfile(path, new_path, callback) end +---@param path string +---@param new_path string +---@param flags? uv.aliases.fs_copyfile_flags +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_copyfile(path, new_path, flags) end + +--- +---Opens path as a directory stream. Returns a handle that the user can pass to +---`uv.fs_readdir()`. The `entries` parameter defines the maximum number of entries +---that should be returned by each call to `uv.fs_readdir()`. +--- +---@param path string +---@param entries integer|nil +---@param callback fun(err: nil|string, dir: luv_dir_t|nil) +---@return uv_fs_t +function uv.fs_opendir(path, entries, callback) end +---@param path string +---@param entries integer|nil +---@return luv_dir_t|nil dir, string? err_name, string? err_msg +---@nodiscard +function uv.fs_opendir(path, entries) end + +--- +---Iterates over the directory stream `luv_dir_t` returned by a successful +---`uv.fs_opendir()` call. A table of data tables is returned where the number +---of entries `n` is equal to or less than the `entries` parameter used in +---the associated `uv.fs_opendir()` call. +--- +---@param dir luv_dir_t +---@param callback fun(err: nil|string, entries: uv.aliases.fs_readdir_entries|nil) +---@return uv_fs_t +function uv.fs_readdir(dir, callback) end +---@param dir luv_dir_t +---@return uv.aliases.fs_readdir_entries|nil entries, string? err_name, string? err_msg +---@nodiscard +function uv.fs_readdir(dir) end +luv_dir_t.readdir = uv.fs_readdir + +--- +---Closes a directory stream returned by a successful `uv.fs_opendir()` call. +--- +---@param dir luv_dir_t +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_closedir(dir, callback) end +---@param dir luv_dir_t +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_closedir(dir) end +luv_dir_t.closedir = uv.fs_closedir + +--- +---Equivalent to `statfs(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, stats: uv.aliases.fs_statfs_stats|nil) +---@return uv_fs_t +function uv.fs_statfs(path, callback) end +---@param path string +---@return uv.aliases.fs_statfs_stats|nil stats, string? err_name, string? err_msg +---@nodiscard +function uv.fs_statfs(path) end + + + +--- +---Libuv provides a threadpool which can be used to run user code and get notified +---in the loop thread. This threadpool is internally used to run all file system +---operations, as well as `getaddrinfo` and `getnameinfo` requests. +--- +---```lua +---local function work_callback(a, b) +--- return a + b +---end +--- +---local function after_work_callback(c) +--- print("The result is: " .. c) +---end +--- +---local work = uv.new_work(work_callback, after_work_callback) +--- +---work:queue(1, 2) +--- +----- output: "The result is: 3" +---``` +--- +---@class luv_work_ctx_t: userdata +---@section Thread pool work scheduling +local luv_work_ctx_t = {} + +--- +---Creates and initializes a new `luv_work_ctx_t` (not `uv_work_t`). +---`work_callback` is a Lua function or a string containing Lua code or bytecode dumped from a function. +---Returns the Lua userdata wrapping it. +--- +---@generic T: uv.aliases.threadargs +---@param work_callback fun(...: T)|string # passed to/from `uv.queue_work(work_ctx, ...)` +---@param after_work_callback fun(...: uv.aliases.threadargs) # returned from `work_callback` +---@return luv_work_ctx_t +---@nodiscard +function uv.new_work(work_callback, after_work_callback) end + +-- TODO: make sure that the above method can indeed return nil + error. +-- confirmed to never return error see luv/work.c#luv_new_work + +--- +---Queues a work request which will run `work_callback` in a new Lua state in a +---thread from the threadpool with any additional arguments from `...`. Values +---returned from `work_callback` are passed to `after_work_callback`, which is +---called in the main loop thread. +--- +---@param work_ctx luv_work_ctx_t +---@param ... uv.aliases.threadargs +---@return boolean|nil success, string? err_name, string? err_msg +function uv.queue_work(work_ctx, ...) end +luv_work_ctx_t.queue = uv.queue_work + + + +--- +---@section DNS utility functions +--- + +---@class uv_getaddrinfo_t: uv_req_t + +---@alias uv.aliases.getaddrinfo_hint {family: uv.aliases.network_family|integer|nil, socktype: uv.aliases.tcp_socket_type|nil, protocol: uv.aliases.network_protocols|nil, addrconfig: boolean|nil, v4mapped: boolean|nil, all: boolean|nil, numberichost: boolean|nil, passive: boolean|nil, numericserv: boolean|nil, canonname: boolean|nil} + +---@alias uv.aliases.getaddrinfo_rtn {addr: string, family: uv.aliases.network_family, port: integer|nil, socktype: uv.aliases.tcp_socket_type, protocol: uv.aliases.network_protocols, canonname: string|nil}[] + +---@class uv_getnameinfo_t: uv_req_t + +---@alias uv_getnameinfo_address {ip: string|nil, port: integer|nil, family: uv.aliases.network_family|integer|nil} + +--- +---Equivalent to `getaddrinfo(3)`. +---Either `host` or `service` may be `nil` but not both. +--- +---@param host string|nil +---@param service string|nil +---@param hints uv.aliases.getaddrinfo_hint? +---@param callback fun(err?: string, addresses?: uv.aliases.getaddrinfo_rtn) +---@return uv_getaddrinfo_t +function uv.getaddrinfo(host, service, hints, callback) end +---@param host string|nil +---@param service string|nil +---@param hints uv.aliases.getaddrinfo_hint? +---@return uv.aliases.getaddrinfo_rtn|nil addresses, string? err_name, string? err_msg +function uv.getaddrinfo(host, service, hints) end + +--- +---Equivalent to `getnameinfo(3)`. +--- +---@param address uv_getnameinfo_address +---@param callback fun(err?: string, host?: string, service?: string) +---@return uv_getnameinfo_t +function uv.getnameinfo(address, callback) end +---@param address uv_getnameinfo_address +---@return string|nil host, string service_or_errname, string? err_msg +function uv.getnameinfo(address) end + + + +--- +---Libuv provides cross-platform implementations for multiple threading an +--- synchronization primitives. The API largely follows the pthreads API. +--- +---@class luv_thread_t: userdata +---@section Threading and synchronization utilities +local luv_thread_t = {} + +---@alias uv.aliases.thread_affinity {[integer]: boolean} + +--- +---Creates and initializes a `luv_thread_t` (not `uv_thread_t`). Returns the Lua +---userdata wrapping it and asynchronously executes `entry`, which can be +---either a Lua function or a string containing Lua code or bytecode dumped from a function. Additional arguments `...` +---are passed to the `entry` function and an optional `options` table may be +---provided. Currently accepted `option` fields are `stack_size`. +--- +---**Note** unsafe, please make sure the thread end of life before Lua state close. +--- +---@generic T: uv.aliases.threadargs +---@param options? {stack_size?: integer} +---@param entry fun(...: T)|string +---@vararg T # varargs passed to `entry` +---@return luv_thread_t?, string? err_name, string? err_msg +function uv.new_thread(options, entry, ...) end +---@generic T: uv.aliases.threadargs +---@param entry fun(...: T)|string +---@vararg T # varargs passed to `entry` +---@return luv_thread_t?, string? err_name, string? err_msg +function uv.new_thread(entry, ...) end + +-- TODO: make sure that the above method can indeed return nil + error. +-- new_thread may fail if pthread fails. + +--- +---Returns a boolean indicating whether two threads are the same. This function is +---equivalent to the `__eq` metamethod. +--- +---@param thread luv_thread_t +---@param other_thread luv_thread_t +---@return boolean +function uv.thread_equal(thread, other_thread) end +luv_thread_t.equal = uv.thread_equal + +--- +---Sets the specified thread's affinity setting. +--- +---`affinity` must be a table where each of the keys are a CPU number and the +---values are booleans that represent whether the `thread` should be eligible to +---run on that CPU. If the length of the `affinity` table is not greater than or +---equal to `uv.cpumask_size()`, any CPU numbers missing from the table will have +---their affinity set to `false`. If setting the affinity of more than +---`uv.cpumask_size()` CPUs is desired, `affinity` must be an array-like table +---with no gaps, since `#affinity` will be used as the `cpumask_size` if it is +---greater than `uv.cpumask_size()`. +--- +---If `get_old_affinity` is `true`, the previous affinity settings for the `thread` +---will be returned. Otherwise, `true` is returned after a successful call. +--- +---**Note:** Thread affinity setting is not atomic on Windows. Unsupported on macOS. +--- +---@param thread luv_thread_t +---@param affinity uv.aliases.thread_affinity +---@param get_old_affinity? boolean +---@return boolean|uv.aliases.thread_affinity|nil, string? err_name, string? err_msg +function uv.thread_setaffinity(thread, affinity, get_old_affinity) end +luv_thread_t.setaffinity = uv.thread_setaffinity + +-- TODO: the above probably needs to use overloads for the different returns. +-- TODO: the overload with the table return needs a @nodiscard. + +--- +---Gets the specified thread's affinity setting. +--- +---If `mask_size` is provided, it must be greater than or equal to +---`uv.cpumask_size()`. If the `mask_size` parameter is omitted, then the return +---of `uv.cpumask_size()` will be used. Returns an array-like table where each of +---the keys correspond to a CPU number and the values are booleans that represent +---whether the `thread` is eligible to run on that CPU. +--- +---**Note:** Thread affinity getting is not atomic on Windows. Unsupported on macOS. +--- +---@param thread luv_thread_t +---@param mask_size? integer +---@return table?, string? err_name, string? err_msg +---@nodiscard +function uv.thread_getaffinity(thread, mask_size) end +luv_thread_t.getaffinity = uv.thread_getaffinity + +--- +---Gets the CPU number on which the calling thread is running. +--- +---**Note:** The first CPU will be returned as the number 1, not 0. This allows for +---the number to correspond with the table keys used in `uv.thread_getaffinity` and +---`uv.thread_setaffinity`. +--- +---@return integer?, string? err_name, string? err_msg +---@nodiscard +function uv.thread_getcpu() end +luv_thread_t.getcpu = uv.thread_getcpu + +--- +---Returns the handle for the thread in which this is called. +--- +---@return luv_thread_t +---@nodiscard +function uv.thread_self() end + +--- +---Waits for the `thread` to finish executing its entry function. +--- +---@param thread luv_thread_t +---@return boolean?, string? err_name, string? err_msg +function uv.thread_join(thread) end +luv_thread_t.join = uv.thread_join + +--- +---Pauses the thread in which this is called for a number of milliseconds. +--- +---@param msec integer +function uv.sleep(msec) end + + + +--- +---@section Miscellaneous utilities +---@source misc.c +--- + +---@alias uv.aliases.os_passwd {username: string, uid: integer, gid: integer, shell: string, homedir: string} + +---@alias uv.aliases.os_uname {sysname: string, release: string, version: string, machine: string} + +---@alias uv.aliases.rusage {utime: {sec: integer, usec: integer}, stime: {sec: integer, usec: integer}, maxrss: integer, ixrss: integer, idrss: integer, isrss: integer, minflt: integer, majflt: integer, nswap: integer, inblock: integer, oublock: integer, msgsnd: integer, msgrcv: integer, nsignals: integer, nvcsw: integer, nivcsw: integer} + +---@alias uv.aliases.cpu_info {[integer]: {modal: string, speed: number, times: {user: number, nice: number, sys: number, idle: number, irq: number}}} + +---@alias uv.aliases.interface_addresses {[string]: {ip: string, family: uv.aliases.network_family, netmask: string, internal: boolean, mac: string}} + +---@alias uv.aliases.clock_gettime_rtn {sec: integer, nsec: integer} + +--- +---Returns the executable path. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.exepath() end + +--- +---Returns the current working directory. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.cwd() end + +--- +---Sets the current working directory with the string `cwd`. +--- +---@param cwd string +---@return 0|nil success, string? err_name, string? err_msg +function uv.chdir(cwd) end + +--- +---Returns the title of the current process. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.get_process_title() end + +--- +---Sets the title of the current process with the string `title`. +--- +---@param title string +---@return 0|nil success, string? err_name, string? err_msg +function uv.set_process_title(title) end + +--- +---Returns the current total system memory in bytes. +--- +---@return integer +---@nodiscard +function uv.get_total_memory() end + +--- +---Returns the current free system memory in bytes. +--- +---@return integer +---@nodiscard +function uv.get_free_memory() end + +--- +---Gets the amount of memory available to the process in bytes based on limits +---imposed by the OS. If there is no such constraint, or the constraint is unknown, +---0 is returned. Note that it is not unusual for this value to be less than or +---greater than the total system memory. +--- +---@return integer +---@nodiscard +function uv.get_constrained_memory() end + +--- +---Gets the amount of free memory that is still available to the process (in +---bytes). This differs from `uv.get_free_memory()` in that it takes into account +---any limits imposed by the OS. If there is no such constraint, or the constraint +---is unknown, the amount returned will be identical to `uv.get_free_memory()`. +--- +---@return number +---@nodiscard +function uv.get_available_memory() end + +--- +---Returns the resident set size (RSS) for the current process. +--- +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.resident_set_memory() end + +--- +---Returns the resource usage. +--- +---@return uv.aliases.rusage|nil, string? err_name, string? err_msg +---@nodiscard +function uv.getrusage() end + +--- +---Returns an estimate of the default amount of parallelism a program should use. Always returns a non-zero value. +--- +---On Linux, inspects the calling thread’s CPU affinity mask to determine if it has been pinned to specific CPUs. +--- +---On Windows, the available parallelism may be underreported on systems with more than 64 logical CPUs. +--- +---On other platforms, reports the number of CPUs that the operating system considers to be online. +--- +---@return integer +---@nodiscard +function uv.available_parallelism() end + +--- +---Returns information about the CPU(s) on the system as a table of tables for each +---CPU found. +--- +---@return uv.aliases.cpu_info|nil, string? err_name, string? err_msg +---@nodiscard +function uv.cpu_info() end + +--- +---Returns the maximum size of the mask used for process/thread affinities, or +---`ENOTSUP` if affinities are not supported on the current platform. +--- +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.cpumask_size() end + +--- +---**Deprecated:** Please use `uv.os_getpid()` instead. +--- +---@return integer +---@nodiscard +---@deprecated +function uv.getpid() end + +--- +---Returns the user ID of the process. +--- +---**Note:** This is not a libuv function and is not supported on Windows. +--- +---@return integer +---@nodiscard +function uv.getuid() end + +--- +---Returns the group ID of the process. +--- +---**Note:** This is not a libuv function and is not supported on Windows. +--- +---@return integer +---@nodiscard +function uv.getgid() end + +--- +---Sets the user ID of the process with the integer `id`. +--- +---**Note:** This is not a libuv function and is not supported on Windows. +--- +---@param id integer +function uv.setuid(id) end + +--- +---Sets the group ID of the process with the integer `id`. +--- +---**Note:** This is not a libuv function and is not supported on Windows. +--- +---@param id integer +function uv.setgid(id) end + +--- +---Returns a current high-resolution time in nanoseconds as a number. This is +---relative to an arbitrary time in the past. It is not related to the time of day +---and therefore not subject to clock drift. The primary use is for measuring +---time between intervals. +--- +---@return integer +---@nodiscard +function uv.hrtime() end + +--- +---Obtain the current system time from a high-resolution real-time or monotonic +---clock source. `clock_id` can be the string `"monotonic"` or `"realtime"`. +--- +---The real-time clock counts from the UNIX epoch (1970-01-01) and is subject +---to time adjustments; it can jump back in time. +--- +---The monotonic clock counts from an arbitrary point in the past and never +---jumps back in time. +--- +---@param clock_id string +---@return uv.aliases.clock_gettime_rtn?, string? err_name, string? err_msg +---@nodiscard +function uv.clock_gettime(clock_id) end + +--- +---Returns the current system uptime in seconds. +--- +---@return number|nil, string? err_name, string? err_msg +---@nodiscard +function uv.uptime() end + +--- +---Prints all handles associated with the main loop to stderr. The format is +---`[flags] handle-type handle-address`. Flags are `R` for referenced, `A` for +---active and `I` for internal. +--- +---**Note:** This is not available on Windows. +--- +---**Warning:** This function is meant for ad hoc debugging, there are no API/ABI +---stability guarantees. +--- +function uv.print_all_handles() end + +--- +---The same as `uv.print_all_handles()` except only active handles are printed. +--- +---**Note:** This is not available on Windows. +--- +---**Warning:** This function is meant for ad hoc debugging, there are no API/ABI +---stability guarantees. +--- +function uv.print_active_handles() end + +--- +---Used to detect what type of stream should be used with a given file +---descriptor `fd`. Usually this will be used during initialization to guess the +---type of the stdio streams. +--- +---@param fd integer +---@return uv.aliases.handle_struct_name|nil +---@nodiscard +function uv.guess_handle(fd) end + +--- +---Cross-platform implementation of `gettimeofday(2)`. Returns the seconds and +---microseconds of a unix time as a pair. +--- +---@return integer|nil, integer|string, string? +---@nodiscard +function uv.gettimeofday() end + +--- +---Returns address information about the network interfaces on the system in a +---table. Each table key is the name of the interface while each associated value +---is an array of address information where fields are `ip`, `family`, `netmask`, +---`internal`, and `mac`. +--- +---@return uv.aliases.interface_addresses +---@nodiscard +function uv.interface_addresses() end + +--- +---IPv6-capable implementation of `if_indextoname(3)`. +--- +---@param ifindex integer +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.if_indextoname(ifindex) end + +--- +---Retrieves a network interface identifier suitable for use in an IPv6 scoped +---address. On Windows, returns the numeric `ifindex` as a string. On all other +---platforms, `uv.if_indextoname()` is used. +--- +---@param ifindex integer +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.if_indextoiid(ifindex) end + +--- +---Returns the load average as a triad. Not supported on Windows. +--- +---@return number, number, number +---@nodiscard +function uv.loadavg() end + +--- +---Returns system information. +--- +---@return uv.aliases.os_uname +---@nodiscard +function uv.os_uname() end + +--- +---Returns the hostname. +--- +---@return string +---@nodiscard +function uv.os_gethostname() end + +--- +---Returns the environment variable specified by `name` as string. The internal +---buffer size can be set by defining `size`. If omitted, `LUAL_BUFFERSIZE` is +---used. If the environment variable exceeds the storage available in the internal +---buffer, `ENOBUFS` is returned. If no matching environment variable exists, +---`ENOENT` is returned. +--- +---**Warning:** This function is not thread safe. +--- +---@param name string +---@param size integer # (default = `LUAL_BUFFERSIZE`) +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.os_getenv(name, size) end + +--- +---Sets the environmental variable specified by `name` with the string `value`. +--- +---**Warning:** This function is not thread safe. +--- +---@param name string +---@param value string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.os_setenv(name, value) end + +--- +---Unsets the environmental variable specified by `name`. +--- +---**Warning:** This function is not thread safe. +--- +---@param name string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.os_unsetenv(name) end + +--- +---Returns all environmental variables as a dynamic table of names associated with +---their corresponding values. +--- +---**Warning:** This function is not thread safe. +--- +---@return {[string]: string} +---@nodiscard +function uv.os_environ() end + +--- +---**Warning:** This function is not thread safe. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.os_homedir() end + +--- +---**Warning:** This function is not thread safe. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.os_tmpdir() end + +--- +---Returns password file information. +--- +---@return uv.aliases.os_passwd +---@nodiscard +function uv.os_get_passwd() end + +--- +---Returns the current process ID. +--- +---@return integer +---@nodiscard +function uv.os_getpid() end + +--- +---Returns the parent process ID. +--- +---@return integer +---@nodiscard +function uv.os_getppid() end + +--- +---Returns the scheduling priority of the process specified by `pid`. +--- +---@param pid integer +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.os_getpriority(pid) end + +--- +---Sets the scheduling priority of the process specified by `pid`. The `priority` +---range is between -20 (high priority) and 19 (low priority). +--- +---@param pid integer +---@param priority integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.os_setpriority(pid, priority) end + +--- +---Fills a string of length `len` with cryptographically strong random bytes +---acquired from the system CSPRNG. `flags` is reserved for future extension +---and must currently be `nil` or `0` or `{}`. +--- +---Short reads are not possible. When less than `len` random bytes are available, +---a non-zero error value is returned or passed to the callback. If the callback +---is omitted, this function is completed synchronously. +---The synchronous version may block indefinitely when not enough entropy is +---available. The asynchronous version may not ever finish when the system is +---low on entropy. +--- +---@param len integer +---@param flags nil|0|{} +---@param callback fun(err?: string, bytes?: string) +---@return 0|nil success, string? err_name, string? err_msg +function uv.random(len, flags, callback) end +---@param len integer +---@param flags nil|0|{} +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.random(len, flags) end + +--- +---Returns the libuv error message and error name (both in string form, see `err` and `name` in Error Handling) equivalent to the given platform dependent error code: POSIX error codes on Unix (the ones stored in errno), and Win32 error codes on Windows (those returned by GetLastError() or WSAGetLastError()). +--- +---@param errcode integer +---@return string|nil, string|nil +---@source util.c +---@nodiscard +function uv.translate_sys_error(errcode) end + + + +--- +---@section Metrics operations +---@source metrics.c +--- + +---@alias uv.aliases.metric_info_rtn {loop_count: integer, events: integer, events_waiting: integer} + +--- +---Retrieve the amount of time the event loop has been idle in the kernel’s event +---provider (e.g. `epoll_wait`). The call is thread safe. +--- +---The return value is the accumulated time spent idle in the kernel’s event +---provider starting from when the `uv_loop_t` was configured to collect the idle time. +--- +---**Note:** The event loop will not begin accumulating the event provider’s idle +---time until calling `loop_configure` with `"metrics_idle_time"`. +--- +---@return integer +---@nodiscard +function uv.metrics_idle_time() end + +--- +---Get the metrics table from current set of event loop metrics. It is recommended +---to retrieve these metrics in a `prepare` callback (see `uv.new_prepare`, +---`uv.prepare_start`) in order to make sure there are no inconsistencies with the +---metrics counters. +--- +---@return uv.aliases.metric_info_rtn +---@nodiscard +function uv.metrics_info() end + + +-- [[ constants ]] + +-- TODO: make this its own section +-- TODO: how should this be reflected onto docs? field descriptions? + +---@alias uv.constants {O_RDONLY: integer, O_WRONLY: integer, O_RDWR: integer, O_APPEND: integer, O_CREAT: integer, O_DSYNC: integer, O_EXCL: integer, O_NOCTTY: integer, O_NONBLOCK: integer, O_RSYNC: integer, O_SYNC: integer, O_TRUNC: integer, SOCK_STREAM: integer, SOCK_DGRAM: integer, SOCK_SEQPACKET: integer, SOCK_RAW: integer, SOCK_RDM: integer, AF_UNIX: integer, AF_INET: integer, AF_INET6: integer, AF_IPX: integer, AF_NETLINK: integer, AF_X25: integer, AF_AX25: integer, AF_ATMPVC: integer, AF_APPLETALK: integer, AF_PACKET: integer, AI_ADDRCONFIG: integer, AI_V4MAPPED: integer, AI_ALL: integer, AI_NUMERICHOST: integer, AI_PASSIVE: integer, AI_NUMERICSERV: integer, SIGHUP: integer, SIGINT: integer, SIGQUIT: integer, SIGILL: integer, SIGTRAP: integer, SIGABRT: integer, SIGIOT: integer, SIGBUS: integer, SIGFPE: integer, SIGKILL: integer, SIGUSR1: integer, SIGSEGV: integer, SIGUSR2: integer, SIGPIPE: integer, SIGALRM: integer, SIGTERM: integer, SIGCHLD: integer, SIGSTKFLT: integer, SIGCONT: integer, SIGSTOP: integer, SIGTSTP: integer, SIGTTIN: integer, SIGWINCH: integer, SIGIO: integer, SIGPOLL: integer, SIGXFSZ: integer, SIGVTALRM: integer, SIGPROF: integer, UDP_RECVMMSG: integer, UDP_MMSG_CHUNK: integer, UDP_REUSEADDR: integer, UDP_PARTIAL: integer, UDP_IPV6ONLY: integer, TCP_IPV6ONLY: integer, UDP_MMSG_FREE: integer, SIGSYS: integer, SIGPWR: integer, SIGTTOU: integer, SIGURG: integer, SIGXCPU: integer} + +---@type uv.constants +uv.constants = {} + +return uv diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/vim.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/vim.lua new file mode 100644 index 00000000..7674b04c --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/nightly/vim.lua @@ -0,0 +1,40 @@ +---@meta + +vim = {} + +---@type uv +vim.loop = {} + +vim.F = require("vim.F") +vim.diagnostic = require("vim.diagnostic") +vim.filetype = require("vim.filetype") +vim.fs = require("vim.fs") +vim.health = require("vim.health") +vim.highlight = require("vim.highlight") +vim.inspect = require("vim.inspect").inspect +vim.iter = require("vim.iter") +vim.keymap = require("vim.keymap") +vim.lsp = require("vim.lsp") +vim.secure = require("vim.secure") +vim.treesitter = require("vim.treesitter") +vim.treesitter.highlighter = require("vim.treesitter.highlighter") +vim.treesitter.language = require("vim.treesitter.language") +vim.treesitter.query = require("vim.treesitter.query") +vim.ui = require("vim.ui") +vim.uri_from_bufnr = require("vim.uri").uri_from_bufnr +vim.uri_from_fname = require("vim.uri").uri_from_fname +vim.uri_to_bufnr = require("vim.uri").uri_to_bufnr +vim.uri_to_fname = require("vim.uri").uri_to_fname +vim.version = require("vim.version") +vim.lpeg = require("lpeg") + +---@type table +vim.g = {} +---@type table +vim.v = {} +---@type table> +vim.b = {} +---@type table> +vim.w = {} +---@type table> +vim.t = {} diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/override/api.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/override/api.lua new file mode 100644 index 00000000..1f6344cc --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/override/api.lua @@ -0,0 +1,2 @@ +---@class vim.api +vim.api = {} diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/override/lua.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/override/lua.lua new file mode 100644 index 00000000..fcf11a5d --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/override/lua.lua @@ -0,0 +1,13 @@ +return { + ["vim.wait"] = { + params = { + { name = "time", type = "number" }, + { name = "condition", type = "fun(): boolean", optional = true }, + { name = "interval", type = "number", optional = true }, + { name = "fast_only", type = "boolean", optional = true }, + }, + ["return"] = { + { type = "boolean, nil|number" }, + }, + }, +} diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/override/options.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/override/options.lua new file mode 100644 index 00000000..fd05c670 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/override/options.lua @@ -0,0 +1,27 @@ +---@type table +vim.go = {} + +---@type table +vim.bo = {} + +---@type table +vim.wo = {} + +---@type vim.go | vim.wo | vim.bo +vim.o = {} + +---@class vim.opt +vim.opt = {} + +---@type vim.opt +vim.opt_global = {} + +---@type vim.opt +vim.opt_local = {} + +---@class vim.Option +local Option = {} + +function Option:append(right) end +function Option:prepend(right) end +function Option:remove(right) end diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/override/vim.fn.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/override/vim.fn.lua new file mode 100644 index 00000000..1271074b --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/override/vim.fn.lua @@ -0,0 +1,73 @@ +return { + ["vim.fn.getline"] = { + params = { + { name = "lnum", type = "number|string" }, + }, + overload = { + "fun(lnum:number|string, end:number|string):string[]|nil[]", + }, + ["return"] = { + { type = "string" }, + }, + }, + ["vim.fn.expand"] = { + params = { + { name = "string", type = "string" }, + { name = "nosuf", type = "boolean", optional = true }, + }, + overload = { + "fun(string:string, nosuf?:boolean, list:true):string[]", + }, + ["return"] = { + { type = "string" }, + }, + }, + ["vim.fn.glob"] = { + params = { + { name = "expr", type = "string" }, + { name = "nosuf", type = "boolean", optional = true }, + }, + overload = { + "fun(expr:string, nosuf?:boolean, list:true):string[]", + "fun(expr:string, nosuf?:boolean, list:true, alllinks:true):string[]", + }, + ["return"] = { + { type = "string" }, + }, + }, + ["vim.fn.sign_define"] = { + overload = { + "fun(name:string, dict?:table)", + }, + }, + ["vim.fn.input"] = { + params = { + { name = "opts", type = "string|table" }, + }, + ["return"] = { + { type = "string" }, + }, + }, + ["vim.fn.searchpair"] = { + params = { + { name = "start", type = "string" }, + { name = "middle", type = "string", optional = true }, + { name = "end", type = "string" }, + { name = "flags", type = "string", optional = true }, + { name = "skip", type = "string", optional = true }, + { name = "stopline", type = "number", optional = true }, + { name = "timeout", type = "number", optional = true }, + }, + }, + ["vim.fn.searchpairpos"] = { + params = { + { name = "start", type = "string" }, + { name = "middle", type = "string", optional = true }, + { name = "end", type = "string" }, + { name = "flags", type = "string", optional = true }, + { name = "skip", type = "string", optional = true }, + { name = "stopline", type = "number", optional = true }, + { name = "timeout", type = "number", optional = true }, + }, + }, +} diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/alias.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/alias.lua new file mode 100644 index 00000000..68f7752b --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/alias.lua @@ -0,0 +1,11 @@ +---@meta + +---@alias blob number +---@alias buffer integer +---@alias channel integer +---@alias float number +---@alias job number +---@alias object any +---@alias sends number +---@alias tabpage integer +---@alias window integer diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/api.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/api.lua new file mode 100644 index 00000000..901e1d45 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/api.lua @@ -0,0 +1,3060 @@ +---@meta + +---@class vim.api +vim.api = {} + + +-- Find files in runtime directories +-- +-- Attributes: ~ +-- |api-fast| +-- +-- Parameters: ~ +-- • {pat} pattern of files to search for +-- • {all} whether to return all matches or only the first +-- • {opts} is_lua: only search lua subdirs +-- +-- Return: ~ +-- list of absolute paths to the found files +--- @param opts? table +function vim.api.nvim__get_runtime(pat, all, opts) end + +-- Returns object given as argument. +-- +-- This API function is used for testing. One should not rely on its presence +-- in plugins. +-- +-- Parameters: ~ +-- • {obj} Object to return. +-- +-- Return: ~ +-- its argument. +function vim.api.nvim__id(obj) end + +-- Returns array given as argument. +-- +-- This API function is used for testing. One should not rely on its presence +-- in plugins. +-- +-- Parameters: ~ +-- • {arr} Array to return. +-- +-- Return: ~ +-- its argument. +function vim.api.nvim__id_array(arr) end + +-- Returns dictionary given as argument. +-- +-- This API function is used for testing. One should not rely on its presence +-- in plugins. +-- +-- Parameters: ~ +-- • {dct} Dictionary to return. +-- +-- Return: ~ +-- its argument. +function vim.api.nvim__id_dictionary(dct) end + +-- Returns floating-point value given as argument. +-- +-- This API function is used for testing. One should not rely on its presence +-- in plugins. +-- +-- Parameters: ~ +-- • {flt} Value to return. +-- +-- Return: ~ +-- its argument. +function vim.api.nvim__id_float(flt) end + +-- NB: if your UI doesn't use hlstate, this will not return hlstate first +-- time. +--- @param col number +function vim.api.nvim__inspect_cell(grid, row, col) end + +-- Gets internal stats. +-- +-- Return: ~ +-- Map of various internal stats. +function vim.api.nvim__stats() end + +-- Adds a highlight to buffer. +-- +-- Useful for plugins that dynamically generate highlights to a buffer (like +-- a semantic highlighter or linter). The function adds a single highlight to +-- a buffer. Unlike |matchaddpos()| highlights follow changes to line +-- numbering (as lines are inserted/removed above the highlighted line), like +-- signs and marks do. +-- +-- Namespaces are used for batch deletion/updating of a set of highlights. To +-- create a namespace, use |nvim_create_namespace()| which returns a +-- namespace id. Pass it in to this function as `ns_id` to add highlights to +-- the namespace. All highlights in the same namespace can then be cleared +-- with single call to |nvim_buf_clear_namespace()|. If the highlight never +-- will be deleted by an API call, pass `ns_id = -1`. +-- +-- As a shorthand, `ns_id = 0` can be used to create a new namespace for the +-- highlight, the allocated id is then returned. If `hl_group` is the empty +-- string no highlight is added, but a new `ns_id` is still returned. This is +-- supported for backwards compatibility, new code should use +-- |nvim_create_namespace()| to create a new empty namespace. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {ns_id} namespace to use or -1 for ungrouped highlight +-- • {hl_group} Name of the highlight group to use +-- • {line} Line to highlight (zero-indexed) +-- • {col_start} Start of (byte-indexed) column range to highlight +-- • {col_end} End of (byte-indexed) column range to highlight, or -1 to +-- highlight to end of line +-- +-- Return: ~ +-- The ns_id that was used +--- @param buffer buffer +--- @param ns_id number +--- @param hl_group string +--- @param line number +--- @param col_start number +--- @param col_end number +--- @return number +function vim.api.nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) end + +-- Activates buffer-update events on a channel, or as Lua callbacks. +-- +-- Example (Lua): capture buffer updates in a global `events` variable (use "print(vim.inspect(events))" to see its contents): +-- ```lua +-- events = {} +-- vim.api.nvim_buf_attach(0, false, { +-- on_lines=function(...) table.insert(events, {...}) end}) +-- ``` +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {send_buffer} True if the initial notification should contain the +-- whole buffer: first notification will be +-- `nvim_buf_lines_event`. Else the first notification +-- will be `nvim_buf_changedtick_event`. Not for Lua +-- callbacks. +-- • {opts} Optional parameters. +-- • on_lines: Lua callback invoked on change. Return `true` to detach. Args: +-- • the string "lines" +-- • buffer handle +-- • b:changedtick +-- • first line that changed (zero-indexed) +-- • last line that was changed +-- • last line in the updated range +-- • byte count of previous contents +-- • deleted_codepoints (if `utf_sizes` is true) +-- • deleted_codeunits (if `utf_sizes` is true) +-- +-- • on_bytes: lua callback invoked on change. This +-- callback receives more granular information about the +-- change compared to on_lines. Return `true` to detach. Args: +-- • the string "bytes" +-- • buffer handle +-- • b:changedtick +-- • start row of the changed text (zero-indexed) +-- • start column of the changed text +-- • byte offset of the changed text (from the start of +-- the buffer) +-- • old end row of the changed text +-- • old end column of the changed text +-- • old end byte length of the changed text +-- • new end row of the changed text +-- • new end column of the changed text +-- • new end byte length of the changed text +-- +-- • on_changedtick: Lua callback invoked on changedtick +-- increment without text change. Args: +-- • the string "changedtick" +-- • buffer handle +-- • b:changedtick +-- +-- • on_detach: Lua callback invoked on detach. Args: +-- • the string "detach" +-- • buffer handle +-- +-- • on_reload: Lua callback invoked on reload. The entire +-- buffer content should be considered changed. Args: +-- • the string "reload" +-- • buffer handle +-- +-- • utf_sizes: include UTF-32 and UTF-16 size of the +-- replaced region, as args to `on_lines`. +-- • preview: also attach to command preview (i.e. +-- 'inccommand') events. +-- +-- Return: ~ +-- False if attach failed (invalid parameter, or buffer isn't loaded); +-- otherwise True. TODO: LUA_API_NO_EVAL +-- +-- See also: ~ +-- • |nvim_buf_detach()| +-- • |api-buffer-updates-lua| +--- @param buffer buffer +--- @param send_buffer boolean +--- @param opts table +--- @return boolean +function vim.api.nvim_buf_attach(buffer, send_buffer, opts) end + +-- call a function with buffer as temporary current buffer +-- +-- This temporarily switches current buffer to "buffer". If the current +-- window already shows "buffer", the window is not switched If a window +-- inside the current tabpage (including a float) already shows the buffer +-- One of these windows will be set as current window temporarily. Otherwise +-- a temporary scratch window (called the "autocmd window" for historical +-- reasons) will be used. +-- +-- This is useful e.g. to call vimL functions that only work with the current +-- buffer/window currently, like |termopen()|. +-- +-- Attributes: ~ +-- Lua |vim.api| only +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {fun} Function to call inside the buffer (currently lua callable +-- only) +-- +-- Return: ~ +-- Return value of function. NB: will deepcopy lua values currently, use +-- upvalues to send lua references in and out. +--- @param buffer buffer +--- @param fun fun() +--- @return object +function vim.api.nvim_buf_call(buffer, fun) end + +-- Clears |namespace|d objects (highlights, |extmarks|, virtual text) from a +-- region. +-- +-- Lines are 0-indexed. |api-indexing| To clear the namespace in the entire +-- buffer, specify line_start=0 and line_end=-1. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {ns_id} Namespace to clear, or -1 to clear all namespaces. +-- • {line_start} Start of range of lines to clear +-- • {line_end} End of range of lines to clear (exclusive) or -1 to +-- clear to end of buffer. +--- @param buffer buffer +--- @param ns_id number +--- @param line_start number +--- @param line_end number +function vim.api.nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end) end + +-- Creates a buffer-local command |user-commands|. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer. +-- +-- See also: ~ +-- • nvim_create_user_command +--- @param buffer buffer +--- @param name string +--- @param command object +--- @param opts? table +function vim.api.nvim_buf_create_user_command(buffer, name, command, opts) end + +-- Removes an |extmark|. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {ns_id} Namespace id from |nvim_create_namespace()| +-- • {id} Extmark id +-- +-- Return: ~ +-- true if the extmark was found, else false +--- @param buffer buffer +--- @param ns_id number +--- @param id number +--- @return boolean +function vim.api.nvim_buf_del_extmark(buffer, ns_id, id) end + +-- Unmaps a buffer-local |mapping| for the given mode. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- +-- See also: ~ +-- • |nvim_del_keymap()| +--- @param buffer buffer +--- @param mode string +--- @param lhs string +function vim.api.nvim_buf_del_keymap(buffer, mode, lhs) end + +-- Deletes a named mark in the buffer. See |mark-motions|. +-- +-- Note: +-- only deletes marks set in the buffer, if the mark is not set in the +-- buffer it will return false. +-- +-- Parameters: ~ +-- • {buffer} Buffer to set the mark on +-- • {name} Mark name +-- +-- Return: ~ +-- true if the mark was deleted, else false. +-- +-- See also: ~ +-- • |nvim_buf_set_mark()| +-- • |nvim_del_mark()| +--- @param buffer buffer +--- @param name string +--- @return boolean +function vim.api.nvim_buf_del_mark(buffer, name) end + +-- Delete a buffer-local user-defined command. +-- +-- Only commands created with |:command-buffer| or +-- |nvim_buf_create_user_command()| can be deleted with this function. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer. +-- • {name} Name of the command to delete. +--- @param buffer buffer +--- @param name string +function vim.api.nvim_buf_del_user_command(buffer, name) end + +-- Removes a buffer-scoped (b:) variable +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {name} Variable name +--- @param buffer buffer +--- @param name string +function vim.api.nvim_buf_del_var(buffer, name) end + +-- Deletes the buffer. See |:bwipeout| +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {opts} Optional parameters. Keys: +-- • force: Force deletion and ignore unsaved changes. +-- • unload: Unloaded only, do not delete. See |:bunload| +--- @param buffer buffer +--- @param opts table +function vim.api.nvim_buf_delete(buffer, opts) end + +-- Gets a changed tick of a buffer +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- +-- Return: ~ +-- `b:changedtick` value. +--- @param buffer buffer +--- @return number +function vim.api.nvim_buf_get_changedtick(buffer) end + +-- Gets a map of buffer-local |user-commands|. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {opts} Optional parameters. Currently not used. +-- +-- Return: ~ +-- Map of maps describing commands. +--- @param buffer buffer +--- @param opts? table +--- @return table +function vim.api.nvim_buf_get_commands(buffer, opts) end + +-- Gets the position (0-indexed) of an |extmark|. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {ns_id} Namespace id from |nvim_create_namespace()| +-- • {id} Extmark id +-- • {opts} Optional parameters. Keys: +-- • details: Whether to include the details dict +-- • hl_name: Whether to include highlight group name instead +-- of id, true if omitted +-- +-- Return: ~ +-- 0-indexed (row, col) tuple or empty list () if extmark id was absent +--- @param buffer buffer +--- @param ns_id number +--- @param id number +--- @param opts table +--- @return any[] +function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end + +-- Gets |extmarks| in "traversal order" from a |charwise| region defined by +-- buffer positions (inclusive, 0-indexed |api-indexing|). +-- +-- Region can be given as (row,col) tuples, or valid extmark ids (whose +-- positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1) +-- respectively, thus the following are equivalent: +-- ```lua +-- vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) +-- vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {}) +-- ``` +-- +-- If `end` is less than `start`, traversal works backwards. (Useful with +-- `limit`, to get the first marks prior to a given position.) +-- +-- Example: +-- ```lua +-- local api = vim.api +-- local pos = api.nvim_win_get_cursor(0) +-- local ns = api.nvim_create_namespace('my-plugin') +-- -- Create new extmark at line 1, column 1. +-- local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {}) +-- -- Create new extmark at line 3, column 1. +-- local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {}) +-- -- Get extmarks only from line 3. +-- local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) +-- -- Get all marks in this buffer + namespace. +-- local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {}) +-- print(vim.inspect(ms)) +-- ``` +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {ns_id} Namespace id from |nvim_create_namespace()| or -1 for all +-- namespaces +-- • {start} Start of range: a 0-indexed (row, col) or valid extmark id +-- (whose position defines the bound). |api-indexing| +-- • {end} End of range (inclusive): a 0-indexed (row, col) or valid +-- extmark id (whose position defines the bound). +-- |api-indexing| +-- • {opts} Optional parameters. Keys: +-- • limit: Maximum number of marks to return +-- • details: Whether to include the details dict +-- • hl_name: Whether to include highlight group name instead +-- of id, true if omitted +-- • type: Filter marks by type: "highlight", "sign", +-- "virt_text" and "virt_lines" +-- +-- Return: ~ +-- List of [extmark_id, row, col] tuples in "traversal order". +--- @param buffer buffer +--- @param ns_id number +--- @param start object +--- @param end_ object +--- @param opts table +--- @return any[] +function vim.api.nvim_buf_get_extmarks(buffer, ns_id, start, end_, opts) end + +-- Gets a list of buffer-local |mapping| definitions. +-- +-- Parameters: ~ +-- • {mode} Mode short-name ("n", "i", "v", ...) +-- • {buffer} Buffer handle, or 0 for current buffer +-- +-- Return: ~ +-- Array of |maparg()|-like dictionaries describing mappings. The +-- "buffer" key holds the associated buffer handle. +--- @param buffer buffer +--- @param mode string +--- @return any[] +function vim.api.nvim_buf_get_keymap(buffer, mode) end + +-- Gets a line-range from the buffer. +-- +-- Indexing is zero-based, end-exclusive. Negative indices are interpreted as +-- length+1+index: -1 refers to the index past the end. So to get the last +-- element use start=-2 and end=-1. +-- +-- Out-of-bounds indices are clamped to the nearest valid value, unless +-- `strict_indexing` is set. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {start} First line index +-- • {end} Last line index, exclusive +-- • {strict_indexing} Whether out-of-bounds should be an error. +-- +-- Return: ~ +-- Array of lines, or empty array for unloaded buffer. +--- @param buffer buffer +--- @param start number +--- @param end_ number +--- @param strict_indexing boolean +--- @return string[] +function vim.api.nvim_buf_get_lines(buffer, start, end_, strict_indexing) end + +-- Returns a tuple (row,col) representing the position of the named mark. See +-- |mark-motions|. +-- +-- Marks are (1,0)-indexed. |api-indexing| +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {name} Mark name +-- +-- Return: ~ +-- (row, col) tuple, (0, 0) if the mark is not set, or is an +-- uppercase/file mark set in another buffer. +-- +-- See also: ~ +-- • |nvim_buf_set_mark()| +-- • |nvim_buf_del_mark()| +--- @param buffer buffer +--- @param name string +--- @return number[] +function vim.api.nvim_buf_get_mark(buffer, name) end + +-- Gets the full file name for the buffer +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- +-- Return: ~ +-- Buffer name +--- @param buffer buffer +--- @return string +function vim.api.nvim_buf_get_name(buffer) end + +-- Returns the byte offset of a line (0-indexed). |api-indexing| +-- +-- Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is one byte. +-- 'fileformat' and 'fileencoding' are ignored. The line index just after the +-- last line gives the total byte-count of the buffer. A final EOL byte is +-- counted if it would be written, see 'eol'. +-- +-- Unlike |line2byte()|, throws error for out-of-bounds indexing. Returns -1 +-- for unloaded buffer. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {index} Line index +-- +-- Return: ~ +-- Integer byte offset, or -1 for unloaded buffer. +--- @param buffer buffer +--- @param index number +--- @return number +function vim.api.nvim_buf_get_offset(buffer, index) end + +-- Gets a buffer option value +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {name} Option name +-- +-- Return: ~ +-- Option value +--- @param buffer buffer +--- @param name string +--- @return object +function vim.api.nvim_buf_get_option(buffer, name) end + +-- Gets a range from the buffer. +-- +-- This differs from |nvim_buf_get_lines()| in that it allows retrieving only +-- portions of a line. +-- +-- Indexing is zero-based. Row indices are end-inclusive, and column indices +-- are end-exclusive. +-- +-- Prefer |nvim_buf_get_lines()| when retrieving entire lines. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {start_row} First line index +-- • {start_col} Starting column (byte offset) on first line +-- • {end_row} Last line index, inclusive +-- • {end_col} Ending column (byte offset) on last line, exclusive +-- • {opts} Optional parameters. Currently unused. +-- +-- Return: ~ +-- Array of lines, or empty array for unloaded buffer. +--- @param buffer buffer +--- @param start_row number +--- @param start_col number +--- @param end_row number +--- @param end_col number +--- @param opts table +--- @return string[] +function vim.api.nvim_buf_get_text(buffer, start_row, start_col, end_row, end_col, opts) end + +-- Gets a buffer-scoped (b:) variable. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {name} Variable name +-- +-- Return: ~ +-- Variable value +--- @param buffer buffer +--- @param name string +--- @return object +function vim.api.nvim_buf_get_var(buffer, name) end + +-- Checks if a buffer is valid and loaded. See |api-buffer| for more info +-- about unloaded buffers. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- +-- Return: ~ +-- true if the buffer is valid and loaded, false otherwise. +--- @param buffer buffer +--- @return boolean +function vim.api.nvim_buf_is_loaded(buffer) end + +-- Checks if a buffer is valid. +-- +-- Note: +-- Even if a buffer is valid it may have been unloaded. See |api-buffer| +-- for more info about unloaded buffers. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- +-- Return: ~ +-- true if the buffer is valid, false otherwise. +--- @param buffer buffer +--- @return boolean +function vim.api.nvim_buf_is_valid(buffer) end + +-- Returns the number of lines in the given buffer. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- +-- Return: ~ +-- Line count, or 0 for unloaded buffer. |api-buffer| +--- @param buffer buffer +--- @return number +function vim.api.nvim_buf_line_count(buffer) end + +-- Creates or updates an |extmark|. +-- +-- By default a new extmark is created when no id is passed in, but it is +-- also possible to create a new mark by passing in a previously unused id or +-- move an existing mark by passing in its id. The caller must then keep +-- track of existing and unused ids itself. (Useful over RPC, to avoid +-- waiting for the return value.) +-- +-- Using the optional arguments, it is possible to use this to highlight a +-- range of text, and also to associate virtual text to the mark. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {ns_id} Namespace id from |nvim_create_namespace()| +-- • {line} Line where to place the mark, 0-based. |api-indexing| +-- • {col} Column where to place the mark, 0-based. |api-indexing| +-- • {opts} Optional parameters. +-- • id : id of the extmark to edit. +-- • end_row : ending line of the mark, 0-based inclusive. +-- • end_col : ending col of the mark, 0-based exclusive. +-- • hl_group : name of the highlight group used to highlight +-- this mark. +-- • hl_eol : when true, for a multiline highlight covering the +-- EOL of a line, continue the highlight for the rest of the +-- screen line (just like for diff and cursorline highlight). +-- • virt_text : virtual text to link to this mark. A list of +-- [text, highlight] tuples, each representing a text chunk +-- with specified highlight. `highlight` element can either +-- be a single highlight group, or an array of multiple +-- highlight groups that will be stacked (highest priority +-- last). A highlight group can be supplied either as a +-- string or as an integer, the latter which can be obtained +-- using |nvim_get_hl_id_by_name()|. +-- • virt_text_pos : position of virtual text. Possible values: +-- • "eol": right after eol character (default) +-- • "overlay": display over the specified column, without +-- shifting the underlying text. +-- • "right_align": display right aligned in the window. +-- +-- • virt_text_win_col : position the virtual text at a fixed +-- window column (starting from the first text column) +-- • virt_text_hide : hide the virtual text when the background +-- text is selected or hidden due to horizontal scroll +-- 'nowrap' +-- • hl_mode : control how highlights are combined with the +-- highlights of the text. Currently only affects virt_text +-- highlights, but might affect `hl_group` in later versions. +-- • "replace": only show the virt_text color. This is the +-- default +-- • "combine": combine with background text color +-- • "blend": blend with background text color. +-- +-- • virt_lines : virtual lines to add next to this mark This +-- should be an array over lines, where each line in turn is +-- an array over [text, highlight] tuples. In general, buffer +-- and window options do not affect the display of the text. +-- In particular 'wrap' and 'linebreak' options do not take +-- effect, so the number of extra screen lines will always +-- match the size of the array. However the 'tabstop' buffer +-- option is still used for hard tabs. By default lines are +-- placed below the buffer line containing the mark. +-- • virt_lines_above: place virtual lines above instead. +-- • virt_lines_leftcol: Place extmarks in the leftmost column +-- of the window, bypassing sign and number columns. +-- • ephemeral : for use with |nvim_set_decoration_provider()| +-- callbacks. The mark will only be used for the current +-- redraw cycle, and not be permantently stored in the +-- buffer. +-- • right_gravity : boolean that indicates the direction the +-- extmark will be shifted in when new text is inserted (true +-- for right, false for left). Defaults to true. +-- • end_right_gravity : boolean that indicates the direction +-- the extmark end position (if it exists) will be shifted in +-- when new text is inserted (true for right, false for +-- left). Defaults to false. +-- • priority: a priority value for the highlight group or sign +-- attribute. For example treesitter highlighting uses a +-- value of 100. +-- • strict: boolean that indicates extmark should not be +-- placed if the line or column value is past the end of the +-- buffer or end of the line respectively. Defaults to true. +-- • sign_text: string of length 1-2 used to display in the +-- sign column. Note: ranges are unsupported and decorations +-- are only applied to start_row +-- • sign_hl_group: name of the highlight group used to +-- highlight the sign column text. Note: ranges are +-- unsupported and decorations are only applied to start_row +-- • number_hl_group: name of the highlight group used to +-- highlight the number column. Note: ranges are unsupported +-- and decorations are only applied to start_row +-- • line_hl_group: name of the highlight group used to +-- highlight the whole line. Note: ranges are unsupported and +-- decorations are only applied to start_row +-- • cursorline_hl_group: name of the highlight group used to +-- highlight the line when the cursor is on the same line as +-- the mark and 'cursorline' is enabled. Note: ranges are +-- unsupported and decorations are only applied to start_row +-- • conceal: string which should be either empty or a single +-- character. Enable concealing similar to |:syn-conceal|. +-- When a character is supplied it is used as |:syn-cchar|. +-- "hl_group" is used as highlight for the cchar if provided, +-- otherwise it defaults to |hl-Conceal|. +-- • spell: boolean indicating that spell checking should be +-- performed within this extmark +-- • ui_watched: boolean that indicates the mark should be +-- drawn by a UI. When set, the UI will receive win_extmark +-- events. Note: the mark is positioned by virt_text +-- attributes. Can be used together with virt_text. +-- +-- Return: ~ +-- Id of the created/updated extmark +--- @param buffer buffer +--- @param ns_id number +--- @param line number +--- @param col number +--- @param opts? table +--- @return number +function vim.api.nvim_buf_set_extmark(buffer, ns_id, line, col, opts) end + +-- Sets a buffer-local |mapping| for the given mode. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- +-- See also: ~ +-- • |nvim_set_keymap()| +--- @param buffer buffer +--- @param mode string +--- @param lhs string +--- @param rhs string +--- @param opts? table +function vim.api.nvim_buf_set_keymap(buffer, mode, lhs, rhs, opts) end + +-- Sets (replaces) a line-range in the buffer. +-- +-- Indexing is zero-based, end-exclusive. Negative indices are interpreted as +-- length+1+index: -1 refers to the index past the end. So to change or +-- delete the last element use start=-2 and end=-1. +-- +-- To insert lines at a given index, set `start` and `end` to the same index. +-- To delete a range of lines, set `replacement` to an empty array. +-- +-- Out-of-bounds indices are clamped to the nearest valid value, unless +-- `strict_indexing` is set. +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {start} First line index +-- • {end} Last line index, exclusive +-- • {strict_indexing} Whether out-of-bounds should be an error. +-- • {replacement} Array of lines to use as replacement +-- +-- See also: ~ +-- • |nvim_buf_set_text()| +--- @param buffer buffer +--- @param start number +--- @param end_ number +--- @param strict_indexing boolean +--- @param replacement string[] +function vim.api.nvim_buf_set_lines(buffer, start, end_, strict_indexing, replacement) end + +-- Sets a named mark in the given buffer, all marks are allowed +-- file/uppercase, visual, last change, etc. See |mark-motions|. +-- +-- Marks are (1,0)-indexed. |api-indexing| +-- +-- Note: +-- Passing 0 as line deletes the mark +-- +-- Parameters: ~ +-- • {buffer} Buffer to set the mark on +-- • {name} Mark name +-- • {line} Line number +-- • {col} Column/row number +-- • {opts} Optional parameters. Reserved for future use. +-- +-- Return: ~ +-- true if the mark was set, else false. +-- +-- See also: ~ +-- • |nvim_buf_del_mark()| +-- • |nvim_buf_get_mark()| +--- @param buffer buffer +--- @param name string +--- @param line number +--- @param col number +--- @param opts table +--- @return boolean +function vim.api.nvim_buf_set_mark(buffer, name, line, col, opts) end + +-- Sets the full file name for a buffer +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {name} Buffer name +--- @param buffer buffer +--- @param name string +function vim.api.nvim_buf_set_name(buffer, name) end + +-- Sets a buffer option value. Passing `nil` as value deletes the option +-- (only works if there's a global fallback) +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {name} Option name +-- • {value} Option value +--- @param buffer buffer +--- @param name string +--- @param value object +function vim.api.nvim_buf_set_option(buffer, name, value) end + +-- Sets (replaces) a range in the buffer +-- +-- This is recommended over |nvim_buf_set_lines()| when only modifying parts +-- of a line, as extmarks will be preserved on non-modified parts of the +-- touched lines. +-- +-- Indexing is zero-based. Row indices are end-inclusive, and column indices +-- are end-exclusive. +-- +-- To insert text at a given `(row, column)` location, use `start_row = +-- end_row = row` and `start_col = end_col = col`. To delete the text in a +-- range, use `replacement = {}`. +-- +-- Prefer |nvim_buf_set_lines()| if you are only adding or deleting entire +-- lines. +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {start_row} First line index +-- • {start_col} Starting column (byte offset) on first line +-- • {end_row} Last line index, inclusive +-- • {end_col} Ending column (byte offset) on last line, exclusive +-- • {replacement} Array of lines to use as replacement +-- +-- See also: ~ +-- • |nvim_buf_set_lines()| +--- @param buffer buffer +--- @param start_row number +--- @param start_col number +--- @param end_row number +--- @param end_col number +--- @param replacement string[] +function vim.api.nvim_buf_set_text(buffer, start_row, start_col, end_row, end_col, replacement) end + +-- Sets a buffer-scoped (b:) variable +-- +-- Parameters: ~ +-- • {buffer} Buffer handle, or 0 for current buffer +-- • {name} Variable name +-- • {value} Variable value +--- @param buffer buffer +--- @param name string +--- @param value object +function vim.api.nvim_buf_set_var(buffer, name, value) end + +-- Calls a VimL |Dictionary-function| with the given arguments. +-- +-- On execution error: fails with VimL error, updates v:errmsg. +-- +-- Parameters: ~ +-- • {dict} Dictionary, or String evaluating to a VimL |self| dict +-- • {fn} Name of the function defined on the VimL dict +-- • {args} Function arguments packed in an Array +-- +-- Return: ~ +-- Result of the function call +--- @param dict object +--- @param fn string +--- @param args any[] +--- @return object +function vim.api.nvim_call_dict_function(dict, fn, args) end + +-- Calls a VimL function with the given arguments. +-- +-- On execution error: fails with VimL error, updates v:errmsg. +-- +-- Parameters: ~ +-- • {fn} Function to call +-- • {args} Function arguments packed in an Array +-- +-- Return: ~ +-- Result of the function call +--- @param fn string +--- @param args any[] +--- @return object +function vim.api.nvim_call_function(fn, args) end + +-- Send data to channel `id`. For a job, it writes it to the stdin of the +-- process. For the stdio channel |channel-stdio|, it writes to Nvim's +-- stdout. For an internal terminal instance (|nvim_open_term()|) it writes +-- directly to terminal output. See |channel-bytes| for more information. +-- +-- This function writes raw data, not RPC messages. If the channel was +-- created with `rpc=true` then the channel expects RPC messages, use +-- |vim.rpcnotify()| and |vim.rpcrequest()| instead. +-- +-- Attributes: ~ +-- |RPC| only +-- Lua |vim.api| only +-- +-- Parameters: ~ +-- • {chan} id of the channel +-- • {data} data to write. 8-bit clean: can contain NUL bytes. +--- @param chan number +--- @param data string +function vim.api.nvim_chan_send(chan, data) end + +-- Clear all autocommands that match the corresponding {opts}. To delete a +-- particular autocmd, see |nvim_del_autocmd()|. +-- +-- Parameters: ~ +-- • {opts} Parameters +-- • event: (string|table) Examples: +-- • event: "pat1" +-- • event: { "pat1" } +-- • event: { "pat1", "pat2", "pat3" } +-- +-- • pattern: (string|table) +-- • pattern or patterns to match exactly. +-- • For example, if you have `*.py` as that pattern for the +-- autocmd, you must pass `*.py` exactly to clear it. +-- `test.py` will not match the pattern. +-- +-- • defaults to clearing all patterns. +-- • NOTE: Cannot be used with {buffer} +-- +-- • buffer: (bufnr) +-- • clear only |autocmd-buflocal| autocommands. +-- • NOTE: Cannot be used with {pattern} +-- +-- • group: (string|int) The augroup name or id. +-- • NOTE: If not passed, will only delete autocmds not in any group. +--- @param opts? table +function vim.api.nvim_clear_autocmds(opts) end + +-- Executes an Ex command. +-- +-- Unlike |nvim_command()| this command takes a structured Dictionary instead +-- of a String. This allows for easier construction and manipulation of an Ex +-- command. This also allows for things such as having spaces inside a +-- command argument, expanding filenames in a command that otherwise doesn't +-- expand filenames, etc. Command arguments may also be Number, Boolean or +-- String. +-- +-- The first argument may also be used instead of count for commands that +-- support it in order to make their usage simpler with |vim.cmd()|. For +-- example, instead of `vim.cmd.bdelete{ count = 2 }`, you may do +-- `vim.cmd.bdelete(2)`. +-- +-- On execution error: fails with VimL error, updates v:errmsg. +-- +-- Parameters: ~ +-- • {cmd} Command to execute. Must be a Dictionary that can contain the +-- same values as the return value of |nvim_parse_cmd()| except +-- "addr", "nargs" and "nextcmd" which are ignored if provided. +-- All values except for "cmd" are optional. +-- • {opts} Optional parameters. +-- • output: (boolean, default false) Whether to return command +-- output. +-- +-- Return: ~ +-- Command output (non-error, non-shell |:!|) if `output` is true, else +-- empty string. +-- +-- See also: ~ +-- • |nvim_exec2()| +-- • |nvim_command()| +--- @param cmd? table +--- @param opts? table +--- @return string +function vim.api.nvim_cmd(cmd, opts) end + +-- Executes an Ex command. +-- +-- On execution error: fails with VimL error, updates v:errmsg. +-- +-- Prefer using |nvim_cmd()| or |nvim_exec2()| over this. To evaluate +-- multiple lines of Vim script or an Ex command directly, use +-- |nvim_exec2()|. To construct an Ex command using a structured format and +-- then execute it, use |nvim_cmd()|. To modify an Ex command before +-- evaluating it, use |nvim_parse_cmd()| in conjunction with |nvim_cmd()|. +-- +-- Parameters: ~ +-- • {command} Ex command string +--- @param command string +function vim.api.nvim_command(command) end + +-- Create or get an autocommand group |autocmd-groups|. +-- +-- To get an existing group id, do: +-- ```lua +-- local id = vim.api.nvim_create_augroup("MyGroup", { +-- clear = false +-- }) +-- ``` +-- +-- Parameters: ~ +-- • {name} String: The name of the group +-- • {opts} Dictionary Parameters +-- • clear (bool) optional: defaults to true. Clear existing +-- commands if the group already exists |autocmd-groups|. +-- +-- Return: ~ +-- Integer id of the created group. +-- +-- See also: ~ +-- • |autocmd-groups| +--- @param name string +--- @param opts? table +--- @return number +function vim.api.nvim_create_augroup(name, opts) end + +-- Creates an |autocommand| event handler, defined by `callback` (Lua function or Vimscript function name string) or `command` (Ex command string). +-- +-- Example using Lua callback: +-- ```lua +-- vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, { +-- pattern = {"*.c", "*.h"}, +-- callback = function(ev) +-- print(string.format('event fired: s', vim.inspect(ev))) +-- end +-- }) +-- ``` +-- +-- Example using an Ex command as the handler: +-- ```lua +-- vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, { +-- pattern = {"*.c", "*.h"}, +-- command = "echo 'Entering a C or C++ file'", +-- }) +-- ``` +-- +-- Note: `pattern` is NOT automatically expanded (unlike with |:autocmd|), thus names like +-- "$HOME" and "~" must be expanded explicitly: +-- ```lua +-- pattern = vim.fn.expand("~") .. "/some/path/*.py" +-- ``` +-- +-- Parameters: ~ +-- • {event} (string|array) Event(s) that will trigger the handler +-- (`callback` or `command`). +-- • {opts} Options dict: +-- • group (string|integer) optional: autocommand group name or +-- id to match against. +-- • pattern (string|array) optional: pattern(s) to match +-- literally |autocmd-pattern|. +-- • buffer (integer) optional: buffer number for buffer-local +-- autocommands |autocmd-buflocal|. Cannot be used with +-- {pattern}. +-- • desc (string) optional: description (for documentation and +-- troubleshooting). +-- • callback (function|string) optional: Lua function (or +-- Vimscript function name, if string) called when the +-- event(s) is triggered. Lua callback can return true to +-- delete the autocommand, and receives a table argument with +-- these keys: +-- • id: (number) autocommand id +-- • event: (string) name of the triggered event +-- |autocmd-events| +-- • group: (number|nil) autocommand group id, if any +-- • match: (string) expanded value of || +-- • buf: (number) expanded value of || +-- • file: (string) expanded value of || +-- • data: (any) arbitrary data passed from +-- |nvim_exec_autocmds()| +-- +-- • command (string) optional: Vim command to execute on event. +-- Cannot be used with {callback} +-- • once (boolean) optional: defaults to false. Run the +-- autocommand only once |autocmd-once|. +-- • nested (boolean) optional: defaults to false. Run nested +-- autocommands |autocmd-nested|. +-- +-- Return: ~ +-- Autocommand id (number) +-- +-- See also: ~ +-- • |autocommand| +-- • |nvim_del_autocmd()| +--- @param event object +--- @param opts? table +--- @return number +function vim.api.nvim_create_autocmd(event, opts) end + +-- Creates a new, empty, unnamed buffer. +-- +-- Parameters: ~ +-- • {listed} Sets 'buflisted' +-- • {scratch} Creates a "throwaway" |scratch-buffer| for temporary work +-- (always 'nomodified'). Also sets 'nomodeline' on the +-- buffer. +-- +-- Return: ~ +-- Buffer handle, or 0 on error +-- +-- See also: ~ +-- • buf_open_scratch +--- @param listed boolean +--- @param scratch boolean +--- @return buffer +function vim.api.nvim_create_buf(listed, scratch) end + +-- Creates a new namespace or gets an existing one. +-- +-- Namespaces are used for buffer highlights and virtual text, see +-- |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|. +-- +-- Namespaces can be named or anonymous. If `name` matches an existing +-- namespace, the associated id is returned. If `name` is an empty string a +-- new, anonymous namespace is created. +-- +-- Parameters: ~ +-- • {name} Namespace name or empty string +-- +-- Return: ~ +-- Namespace id +--- @param name string +--- @return number +function vim.api.nvim_create_namespace(name) end + +-- Creates a global |user-commands| command. +-- +-- For Lua usage see |lua-guide-commands-create|. +-- +-- Example: +-- ```vim +-- :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true}) +-- :SayHello +-- Hello world! +-- ``` +-- +-- Parameters: ~ +-- • {name} Name of the new user command. Must begin with an uppercase +-- letter. +-- • {command} Replacement command to execute when this user command is +-- executed. When called from Lua, the command can also be a +-- Lua function. The function is called with a single table +-- argument that contains the following keys: +-- • name: (string) Command name +-- • args: (string) The args passed to the command, if any +-- || +-- • fargs: (table) The args split by unescaped whitespace +-- (when more than one argument is allowed), if any +-- || +-- • bang: (boolean) "true" if the command was executed with a +-- ! modifier || +-- • line1: (number) The starting line of the command range +-- || +-- • line2: (number) The final line of the command range +-- || +-- • range: (number) The number of items in the command range: +-- 0, 1, or 2 || +-- • count: (number) Any count supplied || +-- • reg: (string) The optional register, if specified || +-- • mods: (string) Command modifiers, if any || +-- • smods: (table) Command modifiers in a structured format. +-- Has the same structure as the "mods" key of +-- |nvim_parse_cmd()|. +-- • {opts} Optional |command-attributes|. +-- • Set boolean attributes such as |:command-bang| or +-- |:command-bar| to true (but not |:command-buffer|, use +-- |nvim_buf_create_user_command()| instead). +-- • "complete" |:command-complete| also accepts a Lua +-- function which works like +-- |:command-completion-customlist|. +-- • Other parameters: +-- • desc: (string) Used for listing the command when a Lua +-- function is used for {command}. +-- • force: (boolean, default true) Override any previous +-- definition. +-- • preview: (function) Preview callback for 'inccommand' +-- |:command-preview| +--- @param name string +--- @param command object +--- @param opts? table +function vim.api.nvim_create_user_command(name, command, opts) end + +-- Delete an autocommand group by id. +-- +-- To get a group id one can use |nvim_get_autocmds()|. +-- +-- NOTE: behavior differs from |:augroup-delete|. When deleting a group, +-- autocommands contained in this group will also be deleted and cleared. +-- This group will no longer exist. +-- +-- Parameters: ~ +-- • {id} Integer The id of the group. +-- +-- See also: ~ +-- • |nvim_del_augroup_by_name()| +-- • |nvim_create_augroup()| +--- @param id number +function vim.api.nvim_del_augroup_by_id(id) end + +-- Delete an autocommand group by name. +-- +-- NOTE: behavior differs from |:augroup-delete|. When deleting a group, +-- autocommands contained in this group will also be deleted and cleared. +-- This group will no longer exist. +-- +-- Parameters: ~ +-- • {name} String The name of the group. +-- +-- See also: ~ +-- • |autocmd-groups| +--- @param name string +function vim.api.nvim_del_augroup_by_name(name) end + +-- Delete an autocommand by id. +-- +-- NOTE: Only autocommands created via the API have an id. +-- +-- Parameters: ~ +-- • {id} Integer The id returned by nvim_create_autocmd +-- +-- See also: ~ +-- • |nvim_create_autocmd()| +--- @param id number +function vim.api.nvim_del_autocmd(id) end + +-- Deletes the current line. +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +function vim.api.nvim_del_current_line() end + +-- Unmaps a global |mapping| for the given mode. +-- +-- To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|. +-- +-- See also: ~ +-- • |nvim_set_keymap()| +--- @param mode string +--- @param lhs string +function vim.api.nvim_del_keymap(mode, lhs) end + +-- Deletes an uppercase/file named mark. See |mark-motions|. +-- +-- Note: +-- fails with error if a lowercase or buffer local named mark is used. +-- +-- Parameters: ~ +-- • {name} Mark name +-- +-- Return: ~ +-- true if the mark was deleted, else false. +-- +-- See also: ~ +-- • |nvim_buf_del_mark()| +-- • |nvim_get_mark()| +--- @param name string +--- @return boolean +function vim.api.nvim_del_mark(name) end + +-- Delete a user-defined command. +-- +-- Parameters: ~ +-- • {name} Name of the command to delete. +--- @param name string +function vim.api.nvim_del_user_command(name) end + +-- Removes a global (g:) variable. +-- +-- Parameters: ~ +-- • {name} Variable name +--- @param name string +function vim.api.nvim_del_var(name) end + +-- Echo a message. +-- +-- Parameters: ~ +-- • {chunks} A list of [text, hl_group] arrays, each representing a text +-- chunk with specified highlight. `hl_group` element can be +-- omitted for no highlight. +-- • {history} if true, add to |message-history|. +-- • {opts} Optional parameters. +-- • verbose: Message was printed as a result of 'verbose' +-- option if Nvim was invoked with -V3log_file, the message +-- will be redirected to the log_file and suppressed from +-- direct output. +--- @param chunks any[] +--- @param history boolean +--- @param opts? table +function vim.api.nvim_echo(chunks, history, opts) end + +-- Writes a message to the Vim error buffer. Does not append "\n", the +-- message is buffered (won't display) until a linefeed is written. +-- +-- Parameters: ~ +-- • {str} Message +--- @param str string +function vim.api.nvim_err_write(str) end + +-- Writes a message to the Vim error buffer. Appends "\n", so the buffer is +-- flushed (and displayed). +-- +-- Parameters: ~ +-- • {str} Message +-- +-- See also: ~ +-- • nvim_err_write() +--- @param str string +function vim.api.nvim_err_writeln(str) end + +-- Evaluates a VimL |expression|. Dictionaries and Lists are recursively +-- expanded. +-- +-- On execution error: fails with VimL error, updates v:errmsg. +-- +-- Parameters: ~ +-- • {expr} VimL expression string +-- +-- Return: ~ +-- Evaluation result or expanded object +--- @param expr string +--- @return object +function vim.api.nvim_eval(expr) end + +-- Evaluates statusline string. +-- +-- Attributes: ~ +-- |api-fast| +-- +-- Parameters: ~ +-- • {str} Statusline string (see 'statusline'). +-- • {opts} Optional parameters. +-- • winid: (number) |window-ID| of the window to use as context +-- for statusline. +-- • maxwidth: (number) Maximum width of statusline. +-- • fillchar: (string) Character to fill blank spaces in the +-- statusline (see 'fillchars'). Treated as single-width even +-- if it isn't. +-- • highlights: (boolean) Return highlight information. +-- • use_winbar: (boolean) Evaluate winbar instead of statusline. +-- • use_tabline: (boolean) Evaluate tabline instead of +-- statusline. When true, {winid} is ignored. Mutually +-- exclusive with {use_winbar}. +-- • use_statuscol_lnum: (number) Evaluate statuscolumn for this +-- line number instead of statusline. +-- +-- Return: ~ +-- Dictionary containing statusline information, with these keys: +-- • str: (string) Characters that will be displayed on the statusline. +-- • width: (number) Display width of the statusline. +-- • highlights: Array containing highlight information of the +-- statusline. Only included when the "highlights" key in {opts} is +-- true. Each element of the array is a |Dictionary| with these keys: +-- • start: (number) Byte index (0-based) of first character that uses +-- the highlight. +-- • group: (string) Name of highlight group. +--- @param str string +--- @param opts? table +--- @return table +function vim.api.nvim_eval_statusline(str, opts) end + +-- Executes Vimscript (multiline block of Ex commands), like anonymous +-- |:source|. +-- +-- Unlike |nvim_command()| this function supports heredocs, script-scope +-- (s:), etc. +-- +-- On execution error: fails with VimL error, updates v:errmsg. +-- +-- Parameters: ~ +-- • {src} Vimscript code +-- • {opts} Optional parameters. +-- • output: (boolean, default false) Whether to capture and +-- return all (non-error, non-shell |:!|) output. +-- +-- Return: ~ +-- Dictionary containing information about execution, with these keys: +-- • output: (string|nil) Output if `opts.output` is true. +-- +-- See also: ~ +-- • |execute()| +-- • |nvim_command()| +-- • |nvim_cmd()| +--- @param src string +--- @param opts? table +--- @return table +function vim.api.nvim_exec2(src, opts) end + +-- Execute all autocommands for {event} that match the corresponding {opts} +-- |autocmd-execute|. +-- +-- Parameters: ~ +-- • {event} (String|Array) The event or events to execute +-- • {opts} Dictionary of autocommand options: +-- • group (string|integer) optional: the autocommand group name +-- or id to match against. |autocmd-groups|. +-- • pattern (string|array) optional: defaults to "*" +-- |autocmd-pattern|. Cannot be used with {buffer}. +-- • buffer (integer) optional: buffer number +-- |autocmd-buflocal|. Cannot be used with {pattern}. +-- • modeline (bool) optional: defaults to true. Process the +-- modeline after the autocommands ||. +-- • data (any): arbitrary data to send to the autocommand +-- callback. See |nvim_create_autocmd()| for details. +-- +-- See also: ~ +-- • |:doautocmd| +--- @param event object +--- @param opts? table +function vim.api.nvim_exec_autocmds(event, opts) end + +-- Sends input-keys to Nvim, subject to various quirks controlled by `mode` +-- flags. This is a blocking call, unlike |nvim_input()|. +-- +-- On execution error: does not fail, but updates v:errmsg. +-- +-- To input sequences like use |nvim_replace_termcodes()| (typically +-- with escape_ks=false) to replace |keycodes|, then pass the result to +-- nvim_feedkeys(). +-- +-- Example: +-- ```vim +-- :let key = nvim_replace_termcodes("", v:true, v:false, v:true) +-- :call nvim_feedkeys(key, 'n', v:false) +-- ``` +-- +-- Parameters: ~ +-- • {keys} to be typed +-- • {mode} behavior flags, see |feedkeys()| +-- • {escape_ks} If true, escape K_SPECIAL bytes in `keys`. This should be +-- false if you already used |nvim_replace_termcodes()|, and +-- true otherwise. +-- +-- See also: ~ +-- • feedkeys() +-- • vim_strsave_escape_ks +--- @param keys string +--- @param mode string +--- @param escape_ks boolean +function vim.api.nvim_feedkeys(keys, mode, escape_ks) end + +-- Gets the option information for all options. +-- +-- The dictionary has the full option names as keys and option metadata +-- dictionaries as detailed at |nvim_get_option_info2()|. +-- +-- Return: ~ +-- dictionary of all options +--- @return table +function vim.api.nvim_get_all_options_info() end + +-- Get all autocommands that match the corresponding {opts}. +-- +-- These examples will get autocommands matching ALL the given criteria: +-- ```lua +-- -- Matches all criteria +-- autocommands = vim.api.nvim_get_autocmds({ +-- group = "MyGroup", +-- event = {"BufEnter", "BufWinEnter"}, +-- pattern = {"*.c", "*.h"} +-- }) +-- +-- -- All commands from one group +-- autocommands = vim.api.nvim_get_autocmds({ +-- group = "MyGroup", +-- }) +-- ``` +-- +-- NOTE: When multiple patterns or events are provided, it will find all the +-- autocommands that match any combination of them. +-- +-- Parameters: ~ +-- • {opts} Dictionary with at least one of the following: +-- • group (string|integer): the autocommand group name or id to +-- match against. +-- • event (string|array): event or events to match against +-- |autocmd-events|. +-- • pattern (string|array): pattern or patterns to match against +-- |autocmd-pattern|. Cannot be used with {buffer} +-- • buffer: Buffer number or list of buffer numbers for buffer +-- local autocommands |autocmd-buflocal|. Cannot be used with +-- {pattern} +-- +-- Return: ~ +-- Array of autocommands matching the criteria, with each item containing +-- the following fields: +-- • id (number): the autocommand id (only when defined with the API). +-- • group (integer): the autocommand group id. +-- • group_name (string): the autocommand group name. +-- • desc (string): the autocommand description. +-- • event (string): the autocommand event. +-- • command (string): the autocommand command. Note: this will be empty +-- if a callback is set. +-- • callback (function|string|nil): Lua function or name of a Vim script +-- function which is executed when this autocommand is triggered. +-- • once (boolean): whether the autocommand is only run once. +-- • pattern (string): the autocommand pattern. If the autocommand is +-- buffer local |autocmd-buffer-local|: +-- • buflocal (boolean): true if the autocommand is buffer local. +-- • buffer (number): the buffer number. +--- @param opts? table +--- @return any[] +function vim.api.nvim_get_autocmds(opts) end + +-- Gets information about a channel. +-- +-- Return: ~ +-- Dictionary describing a channel, with these keys: +-- • "id" Channel id. +-- • "argv" (optional) Job arguments list. +-- • "stream" Stream underlying the channel. +-- • "stdio" stdin and stdout of this Nvim instance +-- • "stderr" stderr of this Nvim instance +-- • "socket" TCP/IP socket or named pipe +-- • "job" Job with communication over its stdio. +-- +-- • "mode" How data received on the channel is interpreted. +-- • "bytes" Send and receive raw bytes. +-- • "terminal" |terminal| instance interprets ASCII sequences. +-- • "rpc" |RPC| communication on the channel is active. +-- +-- • "pty" (optional) Name of pseudoterminal. On a POSIX system this is a +-- device path like "/dev/pts/1". If the name is unknown, the key will +-- still be present if a pty is used (e.g. for conpty on Windows). +-- • "buffer" (optional) Buffer with connected |terminal| instance. +-- • "client" (optional) Info about the peer (client on the other end of +-- the RPC channel), if provided by it via |nvim_set_client_info()|. +--- @param chan number +--- @return table +function vim.api.nvim_get_chan_info(chan) end + +-- Returns the 24-bit RGB value of a |nvim_get_color_map()| color name or +-- "#rrggbb" hexadecimal string. +-- +-- Example: +-- ```vim +-- :echo nvim_get_color_by_name("Pink") +-- :echo nvim_get_color_by_name("#cbcbcb") +-- ``` +-- +-- Parameters: ~ +-- • {name} Color name or "#rrggbb" string +-- +-- Return: ~ +-- 24-bit RGB value, or -1 for invalid argument. +--- @param name string +--- @return number +function vim.api.nvim_get_color_by_name(name) end + +-- Returns a map of color names and RGB values. +-- +-- Keys are color names (e.g. "Aqua") and values are 24-bit RGB color values +-- (e.g. 65535). +-- +-- Return: ~ +-- Map of color names and RGB values. +--- @return table +function vim.api.nvim_get_color_map() end + +-- Gets a map of global (non-buffer-local) Ex commands. +-- +-- Currently only |user-commands| are supported, not builtin Ex commands. +-- +-- Parameters: ~ +-- • {opts} Optional parameters. Currently only supports {"builtin":false} +-- +-- Return: ~ +-- Map of maps describing commands. +--- @param opts? table +--- @return table +function vim.api.nvim_get_commands(opts) end + +-- Gets a map of the current editor state. +-- +-- Parameters: ~ +-- • {opts} Optional parameters. +-- • types: List of |context-types| ("regs", "jumps", "bufs", +-- "gvars", …) to gather, or empty for "all". +-- +-- Return: ~ +-- map of global |context|. +--- @param opts? table +--- @return table +function vim.api.nvim_get_context(opts) end + +-- Gets the current buffer. +-- +-- Return: ~ +-- Buffer handle +--- @return buffer +function vim.api.nvim_get_current_buf() end + +-- Gets the current line. +-- +-- Return: ~ +-- Current line string +--- @return string +function vim.api.nvim_get_current_line() end + +-- Gets the current tabpage. +-- +-- Return: ~ +-- Tabpage handle +--- @return tabpage +function vim.api.nvim_get_current_tabpage() end + +-- Gets the current window. +-- +-- Return: ~ +-- Window handle +--- @return window +function vim.api.nvim_get_current_win() end + +-- Gets all or specific highlight groups in a namespace. +-- +-- Parameters: ~ +-- • {ns_id} Get highlight groups for namespace ns_id +-- |nvim_get_namespaces()|. Use 0 to get global highlight groups +-- |:highlight|. +-- • {opts} Options dict: +-- • name: (string) Get a highlight definition by name. +-- • id: (integer) Get a highlight definition by id. +-- • link: (boolean, default true) Show linked group name +-- instead of effective definition |:hi-link|. +-- +-- Return: ~ +-- Highlight groups as a map from group name to a highlight definition +-- map as in |nvim_set_hl()|, or only a single highlight definition map +-- if requested by name or id. +-- +-- Note: +-- When the `link` attribute is defined in the highlight definition map, +-- other attributes will not be taking effect (see |:hi-link|). +--- @param ns_id number +--- @param opts? table +--- @return table +function vim.api.nvim_get_hl(ns_id, opts) end + +-- Gets a highlight group by name +-- +-- similar to |hlID()|, but allocates a new ID if not present. +--- @param name string +--- @return number +function vim.api.nvim_get_hl_id_by_name(name) end + +-- Gets a list of global (non-buffer-local) |mapping| definitions. +-- +-- Parameters: ~ +-- • {mode} Mode short-name ("n", "i", "v", ...) +-- +-- Return: ~ +-- Array of |maparg()|-like dictionaries describing mappings. The +-- "buffer" key is always zero. +--- @param mode string +--- @return any[] +function vim.api.nvim_get_keymap(mode) end + +-- Return a tuple (row, col, buffer, buffername) representing the position of +-- the uppercase/file named mark. See |mark-motions|. +-- +-- Marks are (1,0)-indexed. |api-indexing| +-- +-- Note: +-- fails with error if a lowercase or buffer local named mark is used. +-- +-- Parameters: ~ +-- • {name} Mark name +-- • {opts} Optional parameters. Reserved for future use. +-- +-- Return: ~ +-- 4-tuple (row, col, buffer, buffername), (0, 0, 0, '') if the mark is +-- not set. +-- +-- See also: ~ +-- • |nvim_buf_set_mark()| +-- • |nvim_del_mark()| +--- @param name string +--- @param opts table +--- @return any[] +function vim.api.nvim_get_mark(name, opts) end + +-- Gets the current mode. |mode()| "blocking" is true if Nvim is waiting for +-- input. +-- +-- Return: ~ +-- Dictionary { "mode": String, "blocking": Boolean } +-- +-- Attributes: ~ +-- |api-fast| +--- @return table +function vim.api.nvim_get_mode() end + +-- Gets existing, non-anonymous |namespace|s. +-- +-- Return: ~ +-- dict that maps from names to namespace ids. +--- @return table +function vim.api.nvim_get_namespaces() end + +-- Gets the global value of an option. +-- +-- Parameters: ~ +-- • {name} Option name +-- +-- Return: ~ +-- Option value (global) +--- @param name string +--- @return object +function vim.api.nvim_get_option(name) end + +--- @return table +function vim.api.nvim_get_option_info() end + +-- Gets the option information for one option from arbitrary buffer or window +-- +-- Resulting dictionary has keys: +-- • name: Name of the option (like 'filetype') +-- • shortname: Shortened name of the option (like 'ft') +-- • type: type of option ("string", "number" or "boolean") +-- • default: The default value for the option +-- • was_set: Whether the option was set. +-- • last_set_sid: Last set script id (if any) +-- • last_set_linenr: line number where option was set +-- • last_set_chan: Channel where option was set (0 for local) +-- • scope: one of "global", "win", or "buf" +-- • global_local: whether win or buf option has a global value +-- • commalist: List of comma separated values +-- • flaglist: List of single char flags +-- +-- When {scope} is not provided, the last set information applies to the +-- local value in the current buffer or window if it is available, otherwise +-- the global value information is returned. This behavior can be disabled by +-- explicitly specifying {scope} in the {opts} table. +-- +-- Parameters: ~ +-- • {name} Option name +-- • {opts} Optional parameters +-- • scope: One of "global" or "local". Analogous to |:setglobal| +-- and |:setlocal|, respectively. +-- • win: |window-ID|. Used for getting window local options. +-- • buf: Buffer number. Used for getting buffer local options. +-- Implies {scope} is "local". +-- +-- Return: ~ +-- Option Information +--- @param name string +--- @param opts? table +--- @return table +function vim.api.nvim_get_option_info2(name, opts) end + +-- Gets the value of an option. The behavior of this function matches that of +-- |:set|: the local value of an option is returned if it exists; otherwise, +-- the global value is returned. Local values always correspond to the +-- current buffer or window, unless "buf" or "win" is set in {opts}. +-- +-- Parameters: ~ +-- • {name} Option name +-- • {opts} Optional parameters +-- • scope: One of "global" or "local". Analogous to |:setglobal| +-- and |:setlocal|, respectively. +-- • win: |window-ID|. Used for getting window local options. +-- • buf: Buffer number. Used for getting buffer local options. +-- Implies {scope} is "local". +-- • filetype: |filetype|. Used to get the default option for a +-- specific filetype. Cannot be used with any other option. +-- Note: this will trigger |ftplugin| and all |FileType| +-- autocommands for the corresponding filetype. +-- +-- Return: ~ +-- Option value +--- @param name string +--- @param opts? table +--- @return object +function vim.api.nvim_get_option_value(name, opts) end + +-- Gets info describing process `pid`. +-- +-- Return: ~ +-- Map of process properties, or NIL if process not found. +--- @param pid number +--- @return object +function vim.api.nvim_get_proc(pid) end + +-- Gets the immediate children of process `pid`. +-- +-- Return: ~ +-- Array of child process ids, empty if process not found. +--- @param pid number +--- @return any[] +function vim.api.nvim_get_proc_children(pid) end + +-- Find files in runtime directories +-- +-- "name" can contain wildcards. For example +-- nvim_get_runtime_file("colors/*.vim", true) will return all color scheme +-- files. Always use forward slashes (/) in the search pattern for +-- subdirectories regardless of platform. +-- +-- It is not an error to not find any files. An empty array is returned then. +-- +-- Attributes: ~ +-- |api-fast| +-- +-- Parameters: ~ +-- • {name} pattern of files to search for +-- • {all} whether to return all matches or only the first +-- +-- Return: ~ +-- list of absolute paths to the found files +--- @param name string +--- @param all boolean +--- @return string[] +function vim.api.nvim_get_runtime_file(name, all) end + +-- Gets a global (g:) variable. +-- +-- Parameters: ~ +-- • {name} Variable name +-- +-- Return: ~ +-- Variable value +--- @param name string +--- @return object +function vim.api.nvim_get_var(name) end + +-- Gets a v: variable. +-- +-- Parameters: ~ +-- • {name} Variable name +-- +-- Return: ~ +-- Variable value +--- @param name string +--- @return object +function vim.api.nvim_get_vvar(name) end + +-- Queues raw user-input. Unlike |nvim_feedkeys()|, this uses a low-level +-- input buffer and the call is non-blocking (input is processed +-- asynchronously by the eventloop). +-- +-- On execution error: does not fail, but updates v:errmsg. +-- +-- Note: +-- |keycodes| like are translated, so "<" is special. To input a +-- literal "<", send . +-- +-- Note: +-- For mouse events use |nvim_input_mouse()|. The pseudokey form +-- "" is deprecated since |api-level| 6. +-- +-- Attributes: ~ +-- |api-fast| +-- +-- Parameters: ~ +-- • {keys} to be typed +-- +-- Return: ~ +-- Number of bytes actually written (can be fewer than requested if the +-- buffer becomes full). +--- @param keys string +--- @return number +function vim.api.nvim_input(keys) end + +-- Send mouse event from GUI. +-- +-- Non-blocking: does not wait on any result, but queues the event to be +-- processed soon by the event loop. +-- +-- Note: +-- Currently this doesn't support "scripting" multiple mouse events by +-- calling it multiple times in a loop: the intermediate mouse positions +-- will be ignored. It should be used to implement real-time mouse input +-- in a GUI. The deprecated pseudokey form ("") of +-- |nvim_input()| has the same limitation. +-- +-- Attributes: ~ +-- |api-fast| +-- +-- Parameters: ~ +-- • {button} Mouse button: one of "left", "right", "middle", "wheel", +-- "move". +-- • {action} For ordinary buttons, one of "press", "drag", "release". +-- For the wheel, one of "up", "down", "left", "right". +-- Ignored for "move". +-- • {modifier} String of modifiers each represented by a single char. The +-- same specifiers are used as for a key press, except that +-- the "-" separator is optional, so "C-A-", "c-a" and "CA" +-- can all be used to specify Ctrl+Alt+click. +-- • {grid} Grid number if the client uses |ui-multigrid|, else 0. +-- • {row} Mouse row-position (zero-based, like redraw events) +-- • {col} Mouse column-position (zero-based, like redraw events) +--- @param button string +--- @param action string +--- @param modifier string +--- @param grid number +--- @param row number +--- @param col number +function vim.api.nvim_input_mouse(button, action, modifier, grid, row, col) end + +-- Gets the current list of buffer handles +-- +-- Includes unlisted (unloaded/deleted) buffers, like `:ls!`. Use +-- |nvim_buf_is_loaded()| to check if a buffer is loaded. +-- +-- Return: ~ +-- List of buffer handles +--- @return any[] +function vim.api.nvim_list_bufs() end + +-- Get information about all open channels. +-- +-- Return: ~ +-- Array of Dictionaries, each describing a channel with the format +-- specified at |nvim_get_chan_info()|. +--- @return any[] +function vim.api.nvim_list_chans() end + +-- Gets the paths contained in 'runtimepath'. +-- +-- Return: ~ +-- List of paths +--- @return string[] +function vim.api.nvim_list_runtime_paths() end + +-- Gets the current list of tabpage handles. +-- +-- Return: ~ +-- List of tabpage handles +--- @return any[] +function vim.api.nvim_list_tabpages() end + +-- Gets a list of dictionaries representing attached UIs. +-- +-- Return: ~ +-- Array of UI dictionaries, each with these keys: +-- • "height" Requested height of the UI +-- • "width" Requested width of the UI +-- • "rgb" true if the UI uses RGB colors (false implies |cterm-colors|) +-- • "ext_..." Requested UI extensions, see |ui-option| +-- • "chan" |channel-id| of remote UI +--- @return any[] +function vim.api.nvim_list_uis() end + +-- Gets the current list of window handles. +-- +-- Return: ~ +-- List of window handles +--- @return any[] +function vim.api.nvim_list_wins() end + +-- Sets the current editor state from the given |context| map. +-- +-- Parameters: ~ +-- • {dict} |Context| map. +--- @param dict table +--- @return object +function vim.api.nvim_load_context(dict) end + +-- Notify the user with a message +-- +-- Relays the call to vim.notify . By default forwards your message in the +-- echo area but can be overridden to trigger desktop notifications. +-- +-- Parameters: ~ +-- • {msg} Message to display to the user +-- • {log_level} The log level +-- • {opts} Reserved for future use. +--- @param msg string +--- @param log_level number +--- @param opts table +--- @return object +function vim.api.nvim_notify(msg, log_level, opts) end + +-- Open a terminal instance in a buffer +-- +-- By default (and currently the only option) the terminal will not be +-- connected to an external process. Instead, input send on the channel will +-- be echoed directly by the terminal. This is useful to display ANSI +-- terminal sequences returned as part of a rpc message, or similar. +-- +-- Note: to directly initiate the terminal using the right size, display the +-- buffer in a configured window before calling this. For instance, for a +-- floating display, first create an empty buffer using |nvim_create_buf()|, +-- then display it using |nvim_open_win()|, and then call this function. Then +-- |nvim_chan_send()| can be called immediately to process sequences in a +-- virtual terminal having the intended size. +-- +-- Parameters: ~ +-- • {buffer} the buffer to use (expected to be empty) +-- • {opts} Optional parameters. +-- • on_input: lua callback for input sent, i e keypresses in +-- terminal mode. Note: keypresses are sent raw as they would +-- be to the pty master end. For instance, a carriage return +-- is sent as a "\r", not as a "\n". |textlock| applies. It +-- is possible to call |nvim_chan_send()| directly in the +-- callback however. ["input", term, bufnr, data] +-- +-- Return: ~ +-- Channel id, or 0 on error +--- @param buffer buffer +--- @param opts table +--- @return number +function vim.api.nvim_open_term(buffer, opts) end + +-- Open a new window. +-- +-- Currently this is used to open floating and external windows. Floats are +-- windows that are drawn above the split layout, at some anchor position in +-- some other window. Floats can be drawn internally or by external GUI with +-- the |ui-multigrid| extension. External windows are only supported with +-- multigrid GUIs, and are displayed as separate top-level windows. +-- +-- For a general overview of floats, see |api-floatwin|. +-- +-- Exactly one of `external` and `relative` must be specified. The `width` +-- and `height` of the new window must be specified. +-- +-- With relative=editor (row=0,col=0) refers to the top-left corner of the +-- screen-grid and (row=Lines-1,col=Columns-1) refers to the bottom-right +-- corner. Fractional values are allowed, but the builtin implementation +-- (used by non-multigrid UIs) will always round down to nearest integer. +-- +-- Out-of-bounds values, and configurations that make the float not fit +-- inside the main editor, are allowed. The builtin implementation truncates +-- values so floats are fully within the main screen grid. External GUIs +-- could let floats hover outside of the main window like a tooltip, but this +-- should not be used to specify arbitrary WM screen positions. +-- +-- Example (Lua): window-relative float +-- ```lua +-- vim.api.nvim_open_win(0, false, +-- {relative='win', row=3, col=3, width=12, height=3}) +-- ``` +-- +-- Example (Lua): buffer-relative float (travels as buffer is scrolled) +-- ```lua +-- vim.api.nvim_open_win(0, false, +-- {relative='win', width=12, height=3, bufpos={100,10}}) +-- ``` +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {buffer} Buffer to display, or 0 for current buffer +-- • {enter} Enter the window (make it the current window) +-- • {config} Map defining the window configuration. Keys: +-- • relative: Sets the window layout to "floating", placed at +-- (row,col) coordinates relative to: +-- • "editor" The global editor grid +-- • "win" Window given by the `win` field, or current +-- window. +-- • "cursor" Cursor position in current window. +-- • "mouse" Mouse position +-- +-- • win: |window-ID| for relative="win". +-- • anchor: Decides which corner of the float to place at +-- (row,col): +-- • "NW" northwest (default) +-- • "NE" northeast +-- • "SW" southwest +-- • "SE" southeast +-- +-- • width: Window width (in character cells). Minimum of 1. +-- • height: Window height (in character cells). Minimum of 1. +-- • bufpos: Places float relative to buffer text (only when +-- relative="win"). Takes a tuple of zero-indexed [line, +-- column]. `row` and `col` if given are applied relative to this position, else they +-- default to: +-- • `row=1` and `col=0` if `anchor` is "NW" or "NE" +-- • `row=0` and `col=0` if `anchor` is "SW" or "SE" (thus +-- like a tooltip near the buffer text). +-- +-- • row: Row position in units of "screen cell height", may be +-- fractional. +-- • col: Column position in units of "screen cell width", may +-- be fractional. +-- • focusable: Enable focus by user actions (wincmds, mouse +-- events). Defaults to true. Non-focusable windows can be +-- entered by |nvim_set_current_win()|. +-- • external: GUI should display the window as an external +-- top-level window. Currently accepts no other positioning +-- configuration together with this. +-- • zindex: Stacking order. floats with higher `zindex` go on top on floats with lower indices. Must be larger +-- than zero. The following screen elements have hard-coded +-- z-indices: +-- • 100: insert completion popupmenu +-- • 200: message scrollback +-- • 250: cmdline completion popupmenu (when +-- wildoptions+=pum) The default value for floats are 50. +-- In general, values below 100 are recommended, unless +-- there is a good reason to overshadow builtin elements. +-- +-- • style: Configure the appearance of the window. Currently +-- only takes one non-empty value: +-- • "minimal" Nvim will display the window with many UI +-- options disabled. This is useful when displaying a +-- temporary float where the text should not be edited. +-- Disables 'number', 'relativenumber', 'cursorline', +-- 'cursorcolumn', 'foldcolumn', 'spell' and 'list' +-- options. 'signcolumn' is changed to `auto` and +-- 'colorcolumn' is cleared. 'statuscolumn' is changed to +-- empty. The end-of-buffer region is hidden by setting +-- `eob` flag of 'fillchars' to a space char, and clearing +-- the |hl-EndOfBuffer| region in 'winhighlight'. +-- +-- • border: Style of (optional) window border. This can either +-- be a string or an array. The string values are +-- • "none": No border (default). +-- • "single": A single line box. +-- • "double": A double line box. +-- • "rounded": Like "single", but with rounded corners ("╭" +-- etc.). +-- • "solid": Adds padding by a single whitespace cell. +-- • "shadow": A drop shadow effect by blending with the +-- background. +-- • If it is an array, it should have a length of eight or +-- any divisor of eight. The array will specifify the eight +-- chars building up the border in a clockwise fashion +-- starting with the top-left corner. As an example, the +-- double box style could be specified as [ "╔", "═" ,"╗", +-- "║", "╝", "═", "╚", "║" ]. If the number of chars are +-- less than eight, they will be repeated. Thus an ASCII +-- border could be specified as [ "/", "-", "\\", "|" ], or +-- all chars the same as [ "x" ]. An empty string can be +-- used to turn off a specific border, for instance, [ "", +-- "", "", ">", "", "", "", "<" ] will only make vertical +-- borders but not horizontal ones. By default, +-- `FloatBorder` highlight is used, which links to +-- `WinSeparator` when not defined. It could also be +-- specified by character: [ ["+", "MyCorner"], ["x", +-- "MyBorder"] ]. +-- +-- • title: Title (optional) in window border, String or list. +-- List is [text, highlight] tuples. if is string the default +-- highlight group is `FloatTitle`. +-- • title_pos: Title position must set with title option. +-- value can be of `left` `center` `right` default is left. +-- • noautocmd: If true then no buffer-related autocommand +-- events such as |BufEnter|, |BufLeave| or |BufWinEnter| may +-- fire from calling this function. +-- +-- Return: ~ +-- Window handle, or 0 on error +--- @param buffer buffer +--- @param enter boolean +--- @param config? table +--- @return window +function vim.api.nvim_open_win(buffer, enter, config) end + +-- Writes a message to the Vim output buffer. Does not append "\n", the +-- message is buffered (won't display) until a linefeed is written. +-- +-- Parameters: ~ +-- • {str} Message +--- @param str string +function vim.api.nvim_out_write(str) end + +-- Parse command line. +-- +-- Doesn't check the validity of command arguments. +-- +-- Attributes: ~ +-- |api-fast| +-- +-- Parameters: ~ +-- • {str} Command line string to parse. Cannot contain "\n". +-- • {opts} Optional parameters. Reserved for future use. +-- +-- Return: ~ +-- Dictionary containing command information, with these keys: +-- • cmd: (string) Command name. +-- • range: (array) (optional) Command range (|| ||). +-- Omitted if command doesn't accept a range. Otherwise, has no +-- elements if no range was specified, one element if only a single +-- range item was specified, or two elements if both range items were +-- specified. +-- • count: (number) (optional) Command ||. Omitted if command +-- cannot take a count. +-- • reg: (string) (optional) Command ||. Omitted if command +-- cannot take a register. +-- • bang: (boolean) Whether command contains a || (!) modifier. +-- • args: (array) Command arguments. +-- • addr: (string) Value of |:command-addr|. Uses short name or "line" +-- for -addr=lines. +-- • nargs: (string) Value of |:command-nargs|. +-- • nextcmd: (string) Next command if there are multiple commands +-- separated by a |:bar|. Empty if there isn't a next command. +-- • magic: (dictionary) Which characters have special meaning in the +-- command arguments. +-- • file: (boolean) The command expands filenames. Which means +-- characters such as "%", "#" and wildcards are expanded. +-- • bar: (boolean) The "|" character is treated as a command separator +-- and the double quote character (") is treated as the start of a +-- comment. +-- +-- • mods: (dictionary) |:command-modifiers|. +-- • filter: (dictionary) |:filter|. +-- • pattern: (string) Filter pattern. Empty string if there is no +-- filter. +-- • force: (boolean) Whether filter is inverted or not. +-- +-- • silent: (boolean) |:silent|. +-- • emsg_silent: (boolean) |:silent!|. +-- • unsilent: (boolean) |:unsilent|. +-- • sandbox: (boolean) |:sandbox|. +-- • noautocmd: (boolean) |:noautocmd|. +-- • browse: (boolean) |:browse|. +-- • confirm: (boolean) |:confirm|. +-- • hide: (boolean) |:hide|. +-- • horizontal: (boolean) |:horizontal|. +-- • keepalt: (boolean) |:keepalt|. +-- • keepjumps: (boolean) |:keepjumps|. +-- • keepmarks: (boolean) |:keepmarks|. +-- • keeppatterns: (boolean) |:keeppatterns|. +-- • lockmarks: (boolean) |:lockmarks|. +-- • noswapfile: (boolean) |:noswapfile|. +-- • tab: (integer) |:tab|. -1 when omitted. +-- • verbose: (integer) |:verbose|. -1 when omitted. +-- • vertical: (boolean) |:vertical|. +-- • split: (string) Split modifier string, is an empty string when +-- there's no split modifier. If there is a split modifier it can be +-- one of: +-- • "aboveleft": |:aboveleft|. +-- • "belowright": |:belowright|. +-- • "topleft": |:topleft|. +-- • "botright": |:botright|. +--- @param str string +--- @param opts table +--- @return table +function vim.api.nvim_parse_cmd(str, opts) end + +-- Parse a VimL expression. +-- +-- Attributes: ~ +-- |api-fast| +-- +-- Parameters: ~ +-- • {expr} Expression to parse. Always treated as a single line. +-- • {flags} Flags: +-- • "m" if multiple expressions in a row are allowed (only +-- the first one will be parsed), +-- • "E" if EOC tokens are not allowed (determines whether +-- they will stop parsing process or be recognized as an +-- operator/space, though also yielding an error). +-- • "l" when needing to start parsing with lvalues for +-- ":let" or ":for". Common flag sets: +-- • "m" to parse like for ":echo". +-- • "E" to parse like for "=". +-- • empty string for ":call". +-- • "lm" to parse for ":let". +-- • {highlight} If true, return value will also include "highlight" key +-- containing array of 4-tuples (arrays) (Integer, Integer, +-- Integer, String), where first three numbers define the +-- highlighted region and represent line, starting column +-- and ending column (latter exclusive: one should highlight +-- region [start_col, end_col)). +-- +-- Return: ~ +-- +-- • AST: top-level dictionary with these keys: +-- • "error": Dictionary with error, present only if parser saw some +-- error. Contains the following keys: +-- • "message": String, error message in printf format, translated. +-- Must contain exactly one "%.*s". +-- • "arg": String, error message argument. +-- +-- • "len": Amount of bytes successfully parsed. With flags equal to "" +-- that should be equal to the length of expr string. (“Successfully +-- parsed” here means “participated in AST creation”, not “till the +-- first error”.) +-- • "ast": AST, either nil or a dictionary with these keys: +-- • "type": node type, one of the value names from ExprASTNodeType +-- stringified without "kExprNode" prefix. +-- • "start": a pair [line, column] describing where node is +-- "started" where "line" is always 0 (will not be 0 if you will be +-- using nvim_parse_viml() on e.g. ":let", but that is not present +-- yet). Both elements are Integers. +-- • "len": “length” of the node. This and "start" are there for +-- debugging purposes primary (debugging parser and providing debug +-- information). +-- • "children": a list of nodes described in top/"ast". There always +-- is zero, one or two children, key will not be present if node +-- has no children. Maximum number of children may be found in +-- node_maxchildren array. +-- +-- • Local values (present only for certain nodes): +-- • "scope": a single Integer, specifies scope for "Option" and +-- "PlainIdentifier" nodes. For "Option" it is one of ExprOptScope +-- values, for "PlainIdentifier" it is one of ExprVarScope values. +-- • "ident": identifier (without scope, if any), present for "Option", +-- "PlainIdentifier", "PlainKey" and "Environment" nodes. +-- • "name": Integer, register name (one character) or -1. Only present +-- for "Register" nodes. +-- • "cmp_type": String, comparison type, one of the value names from +-- ExprComparisonType, stringified without "kExprCmp" prefix. Only +-- present for "Comparison" nodes. +-- • "ccs_strategy": String, case comparison strategy, one of the value +-- names from ExprCaseCompareStrategy, stringified without +-- "kCCStrategy" prefix. Only present for "Comparison" nodes. +-- • "augmentation": String, augmentation type for "Assignment" nodes. +-- Is either an empty string, "Add", "Subtract" or "Concat" for "=", +-- "+=", "-=" or ".=" respectively. +-- • "invert": Boolean, true if result of comparison needs to be +-- inverted. Only present for "Comparison" nodes. +-- • "ivalue": Integer, integer value for "Integer" nodes. +-- • "fvalue": Float, floating-point value for "Float" nodes. +-- • "svalue": String, value for "SingleQuotedString" and +-- "DoubleQuotedString" nodes. +--- @param expr string +--- @param flags string +--- @param highlight boolean +--- @return table +function vim.api.nvim_parse_expression(expr, flags, highlight) end + +-- Pastes at cursor, in any mode. +-- +-- Invokes the `vim.paste` handler, which handles each mode appropriately. +-- Sets redo/undo. Faster than |nvim_input()|. Lines break at LF ("\n"). +-- +-- Errors ('nomodifiable', `vim.paste()` failure, …) are reflected in `err` +-- but do not affect the return value (which is strictly decided by +-- `vim.paste()`). On error, subsequent calls are ignored ("drained") until +-- the next paste is initiated (phase 1 or -1). +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {data} Multiline input. May be binary (containing NUL bytes). +-- • {crlf} Also break lines at CR and CRLF. +-- • {phase} -1: paste in a single call (i.e. without streaming). To +-- "stream" a paste, call `nvim_paste` sequentially with these `phase` values: +-- • 1: starts the paste (exactly once) +-- • 2: continues the paste (zero or more times) +-- • 3: ends the paste (exactly once) +-- +-- Return: ~ +-- +-- • true: Client may continue pasting. +-- • false: Client must cancel the paste. +--- @param data string +--- @param crlf boolean +--- @param phase number +--- @return boolean +function vim.api.nvim_paste(data, crlf, phase) end + +-- Puts text at cursor, in any mode. +-- +-- Compare |:put| and |p| which are always linewise. +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {lines} |readfile()|-style list of lines. |channel-lines| +-- • {type} Edit behavior: any |getregtype()| result, or: +-- • "b" |blockwise-visual| mode (may include width, e.g. "b3") +-- • "c" |charwise| mode +-- • "l" |linewise| mode +-- • "" guess by contents, see |setreg()| +-- • {after} If true insert after cursor (like |p|), or before (like +-- |P|). +-- • {follow} If true place cursor at end of inserted text. +--- @param lines string[] +--- @param type string +--- @param after boolean +--- @param follow boolean +function vim.api.nvim_put(lines, type, after, follow) end + +-- Replaces terminal codes and |keycodes| (, , ...) in a string with +-- the internal representation. +-- +-- Parameters: ~ +-- • {str} String to be converted. +-- • {from_part} Legacy Vim parameter. Usually true. +-- • {do_lt} Also translate . Ignored if `special` is false. +-- • {special} Replace |keycodes|, e.g. becomes a "\r" char. +-- +-- See also: ~ +-- • replace_termcodes +-- • cpoptions +--- @param str string +--- @param from_part boolean +--- @param do_lt boolean +--- @param special boolean +--- @return string +function vim.api.nvim_replace_termcodes(str, from_part, do_lt, special) end + +-- Selects an item in the completion popup menu. +-- +-- If neither |ins-completion| nor |cmdline-completion| popup menu is active +-- this API call is silently ignored. Useful for an external UI using +-- |ui-popupmenu| to control the popup menu with the mouse. Can also be used +-- in a mapping; use |:map-cmd| or a Lua mapping to ensure the mapping +-- doesn't end completion mode. +-- +-- Parameters: ~ +-- • {item} Index (zero-based) of the item to select. Value of -1 +-- selects nothing and restores the original text. +-- • {insert} For |ins-completion|, whether the selection should be +-- inserted in the buffer. Ignored for |cmdline-completion|. +-- • {finish} Finish the completion and dismiss the popup menu. Implies +-- {insert}. +-- • {opts} Optional parameters. Reserved for future use. +--- @param item number +--- @param insert boolean +--- @param finish boolean +--- @param opts table +function vim.api.nvim_select_popupmenu_item(item, insert, finish, opts) end + +-- Sets the current buffer. +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {buffer} Buffer handle +--- @param buffer buffer +function vim.api.nvim_set_current_buf(buffer) end + +-- Changes the global working directory. +-- +-- Parameters: ~ +-- • {dir} Directory path +--- @param dir string +function vim.api.nvim_set_current_dir(dir) end + +-- Sets the current line. +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {line} Line contents +--- @param line string +function vim.api.nvim_set_current_line(line) end + +-- Sets the current tabpage. +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {tabpage} Tabpage handle +--- @param tabpage tabpage +function vim.api.nvim_set_current_tabpage(tabpage) end + +-- Sets the current window. +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {window} Window handle +--- @param window window +function vim.api.nvim_set_current_win(window) end + +-- Set or change decoration provider for a |namespace| +-- +-- This is a very general purpose interface for having lua callbacks being +-- triggered during the redraw code. +-- +-- The expected usage is to set |extmarks| for the currently redrawn buffer. +-- |nvim_buf_set_extmark()| can be called to add marks on a per-window or +-- per-lines basis. Use the `ephemeral` key to only use the mark for the +-- current screen redraw (the callback will be called again for the next +-- redraw ). +-- +-- Note: this function should not be called often. Rather, the callbacks +-- themselves can be used to throttle unneeded callbacks. the `on_start` +-- callback can return `false` to disable the provider until the next redraw. +-- Similarly, return `false` in `on_win` will skip the `on_lines` calls for +-- that window (but any extmarks set in `on_win` will still be used). A +-- plugin managing multiple sources of decoration should ideally only set one +-- provider, and merge the sources internally. You can use multiple `ns_id` +-- for the extmarks set/modified inside the callback anyway. +-- +-- Note: doing anything other than setting extmarks is considered +-- experimental. Doing things like changing options are not expliticly +-- forbidden, but is likely to have unexpected consequences (such as 100% CPU +-- consumption). doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is +-- quite dubious for the moment. +-- +-- Attributes: ~ +-- Lua |vim.api| only +-- +-- Parameters: ~ +-- • {ns_id} Namespace id from |nvim_create_namespace()| +-- • {opts} Table of callbacks: +-- • on_start: called first on each screen redraw ["start", +-- tick] +-- • on_buf: called for each buffer being redrawn (before window +-- callbacks) ["buf", bufnr, tick] +-- • on_win: called when starting to redraw a specific window. +-- ["win", winid, bufnr, topline, botline_guess] +-- • on_line: called for each buffer line being redrawn. (The +-- interaction with fold lines is subject to change) ["win", +-- winid, bufnr, row] +-- • on_end: called at the end of a redraw cycle ["end", tick] +--- @param ns_id number +--- @param opts? table +function vim.api.nvim_set_decoration_provider(ns_id, opts) end + +-- Sets a highlight group. +-- +-- Note: +-- Unlike the `:highlight` command which can update a highlight group, +-- this function completely replaces the definition. For example: +-- `nvim_set_hl(0, 'Visual', {})` will clear the highlight group +-- 'Visual'. +-- +-- Note: +-- The fg and bg keys also accept the string values `"fg"` or `"bg"` +-- which act as aliases to the corresponding foreground and background +-- values of the Normal group. If the Normal group has not been defined, +-- using these values results in an error. +-- +-- Note: +-- If `link` is used in combination with other attributes; only the +-- `link` will take effect (see |:hi-link|). +-- +-- Parameters: ~ +-- • {ns_id} Namespace id for this highlight |nvim_create_namespace()|. +-- Use 0 to set a highlight group globally |:highlight|. +-- Highlights from non-global namespaces are not active by +-- default, use |nvim_set_hl_ns()| or |nvim_win_set_hl_ns()| to +-- activate them. +-- • {name} Highlight group name, e.g. "ErrorMsg" +-- • {val} Highlight definition map, accepts the following keys: +-- • fg (or foreground): color name or "#RRGGBB", see note. +-- • bg (or background): color name or "#RRGGBB", see note. +-- • sp (or special): color name or "#RRGGBB" +-- • blend: integer between 0 and 100 +-- • bold: boolean +-- • standout: boolean +-- • underline: boolean +-- • undercurl: boolean +-- • underdouble: boolean +-- • underdotted: boolean +-- • underdashed: boolean +-- • strikethrough: boolean +-- • italic: boolean +-- • reverse: boolean +-- • nocombine: boolean +-- • link: name of another highlight group to link to, see +-- |:hi-link|. +-- • default: Don't override existing definition |:hi-default| +-- • ctermfg: Sets foreground of cterm color |ctermfg| +-- • ctermbg: Sets background of cterm color |ctermbg| +-- • cterm: cterm attribute map, like |highlight-args|. If not +-- set, cterm attributes will match those from the attribute +-- map documented above. +--- @param ns_id number +--- @param name string +--- @param val? table +function vim.api.nvim_set_hl(ns_id, name, val) end + +-- Set active namespace for highlights defined with |nvim_set_hl()|. This can +-- be set for a single window, see |nvim_win_set_hl_ns()|. +-- +-- Parameters: ~ +-- • {ns_id} the namespace to use +--- @param ns_id number +function vim.api.nvim_set_hl_ns(ns_id) end + +-- Set active namespace for highlights defined with |nvim_set_hl()| while +-- redrawing. +-- +-- This function meant to be called while redrawing, primarily from +-- |nvim_set_decoration_provider()| on_win and on_line callbacks, which are +-- allowed to change the namespace during a redraw cycle. +-- +-- Attributes: ~ +-- |api-fast| +-- +-- Parameters: ~ +-- • {ns_id} the namespace to activate +--- @param ns_id number +function vim.api.nvim_set_hl_ns_fast(ns_id) end + +-- Sets a global |mapping| for the given mode. +-- +-- To set a buffer-local mapping, use |nvim_buf_set_keymap()|. +-- +-- Unlike |:map|, leading/trailing whitespace is accepted as part of the +-- {lhs} or {rhs}. Empty {rhs} is ||. |keycodes| are replaced as usual. +-- +-- Example: +-- ```vim +-- call nvim_set_keymap('n', ' ', '', {'nowait': v:true}) +-- ``` +-- +-- is equivalent to: +-- ```vim +-- nmap +-- ``` +-- +-- Parameters: ~ +-- • {mode} Mode short-name (map command prefix: "n", "i", "v", "x", …) or +-- "!" for |:map!|, or empty string for |:map|. +-- • {lhs} Left-hand-side |{lhs}| of the mapping. +-- • {rhs} Right-hand-side |{rhs}| of the mapping. +-- • {opts} Optional parameters map: Accepts all |:map-arguments| as keys +-- except ||, values are booleans (default false). Also: +-- • "noremap" non-recursive mapping |:noremap| +-- • "desc" human-readable description. +-- • "callback" Lua function called when the mapping is executed. +-- • "replace_keycodes" (boolean) When "expr" is true, replace +-- keycodes in the resulting string (see +-- |nvim_replace_termcodes()|). Returning nil from the Lua +-- "callback" is equivalent to returning an empty string. +--- @param mode string +--- @param lhs string +--- @param rhs string +--- @param opts? table +function vim.api.nvim_set_keymap(mode, lhs, rhs, opts) end + +-- Sets the global value of an option. +-- +-- Parameters: ~ +-- • {name} Option name +-- • {value} New option value +--- @param name string +--- @param value object +function vim.api.nvim_set_option(name, value) end + +-- Sets the value of an option. The behavior of this function matches that of +-- |:set|: for global-local options, both the global and local value are set +-- unless otherwise specified with {scope}. +-- +-- Note the options {win} and {buf} cannot be used together. +-- +-- Parameters: ~ +-- • {name} Option name +-- • {value} New option value +-- • {opts} Optional parameters +-- • scope: One of "global" or "local". Analogous to +-- |:setglobal| and |:setlocal|, respectively. +-- • win: |window-ID|. Used for setting window local option. +-- • buf: Buffer number. Used for setting buffer local option. +--- @param name string +--- @param value object +--- @param opts? table +function vim.api.nvim_set_option_value(name, value, opts) end + +-- Sets a global (g:) variable. +-- +-- Parameters: ~ +-- • {name} Variable name +-- • {value} Variable value +--- @param name string +--- @param value object +function vim.api.nvim_set_var(name, value) end + +-- Sets a v: variable, if it is not readonly. +-- +-- Parameters: ~ +-- • {name} Variable name +-- • {value} Variable value +--- @param name string +--- @param value object +function vim.api.nvim_set_vvar(name, value) end + +-- Calculates the number of display cells occupied by `text`. Control +-- characters including count as one cell. +-- +-- Parameters: ~ +-- • {text} Some text +-- +-- Return: ~ +-- Number of cells +--- @param text string +--- @return number +function vim.api.nvim_strwidth(text) end + +-- Removes a tab-scoped (t:) variable +-- +-- Parameters: ~ +-- • {tabpage} Tabpage handle, or 0 for current tabpage +-- • {name} Variable name +--- @param tabpage tabpage +--- @param name string +function vim.api.nvim_tabpage_del_var(tabpage, name) end + +-- Gets the tabpage number +-- +-- Parameters: ~ +-- • {tabpage} Tabpage handle, or 0 for current tabpage +-- +-- Return: ~ +-- Tabpage number +--- @param tabpage tabpage +--- @return number +function vim.api.nvim_tabpage_get_number(tabpage) end + +-- Gets a tab-scoped (t:) variable +-- +-- Parameters: ~ +-- • {tabpage} Tabpage handle, or 0 for current tabpage +-- • {name} Variable name +-- +-- Return: ~ +-- Variable value +--- @param tabpage tabpage +--- @param name string +--- @return object +function vim.api.nvim_tabpage_get_var(tabpage, name) end + +-- Gets the current window in a tabpage +-- +-- Parameters: ~ +-- • {tabpage} Tabpage handle, or 0 for current tabpage +-- +-- Return: ~ +-- Window handle +--- @param tabpage tabpage +--- @return window +function vim.api.nvim_tabpage_get_win(tabpage) end + +-- Checks if a tabpage is valid +-- +-- Parameters: ~ +-- • {tabpage} Tabpage handle, or 0 for current tabpage +-- +-- Return: ~ +-- true if the tabpage is valid, false otherwise +--- @param tabpage tabpage +--- @return boolean +function vim.api.nvim_tabpage_is_valid(tabpage) end + +-- Gets the windows in a tabpage +-- +-- Parameters: ~ +-- • {tabpage} Tabpage handle, or 0 for current tabpage +-- +-- Return: ~ +-- List of windows in `tabpage` +--- @param tabpage tabpage +--- @return any[] +function vim.api.nvim_tabpage_list_wins(tabpage) end + +-- Sets a tab-scoped (t:) variable +-- +-- Parameters: ~ +-- • {tabpage} Tabpage handle, or 0 for current tabpage +-- • {name} Variable name +-- • {value} Variable value +--- @param tabpage tabpage +--- @param name string +--- @param value object +function vim.api.nvim_tabpage_set_var(tabpage, name, value) end + +-- Calls a function with window as temporary current window. +-- +-- Attributes: ~ +-- Lua |vim.api| only +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {fun} Function to call inside the window (currently lua callable +-- only) +-- +-- Return: ~ +-- Return value of function. NB: will deepcopy lua values currently, use +-- upvalues to send lua references in and out. +-- +-- See also: ~ +-- • |win_execute()| +-- • |nvim_buf_call()| +--- @param window window +--- @param fun fun() +--- @return object +function vim.api.nvim_win_call(window, fun) end + +-- Closes the window (like |:close| with a |window-ID|). +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {force} Behave like `:close!` The last window of a buffer with +-- unwritten changes can be closed. The buffer will become +-- hidden, even if 'hidden' is not set. +--- @param window window +--- @param force boolean +function vim.api.nvim_win_close(window, force) end + +-- Removes a window-scoped (w:) variable +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {name} Variable name +--- @param window window +--- @param name string +function vim.api.nvim_win_del_var(window, name) end + +-- Gets the current buffer in a window +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- +-- Return: ~ +-- Buffer handle +--- @param window window +--- @return buffer +function vim.api.nvim_win_get_buf(window) end + +-- Gets window configuration. +-- +-- The returned value may be given to |nvim_open_win()|. +-- +-- `relative` is empty for normal windows. +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- +-- Return: ~ +-- Map defining the window configuration, see |nvim_open_win()| +--- @param window window +--- @return table +function vim.api.nvim_win_get_config(window) end + +-- Gets the (1,0)-indexed, buffer-relative cursor position for a given window +-- (different windows showing the same buffer have independent cursor +-- positions). |api-indexing| +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- +-- Return: ~ +-- (row, col) tuple +--- @param window window +--- @return number[] +function vim.api.nvim_win_get_cursor(window) end + +-- Gets the window height +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- +-- Return: ~ +-- Height as a count of rows +--- @param window window +--- @return number +function vim.api.nvim_win_get_height(window) end + +-- Gets the window number +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- +-- Return: ~ +-- Window number +--- @param window window +--- @return number +function vim.api.nvim_win_get_number(window) end + +-- Gets a window option value +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {name} Option name +-- +-- Return: ~ +-- Option value +--- @param window window +--- @param name string +--- @return object +function vim.api.nvim_win_get_option(window, name) end + +-- Gets the window position in display cells. First position is zero. +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- +-- Return: ~ +-- (row, col) tuple with the window position +--- @param window window +--- @return number[] +function vim.api.nvim_win_get_position(window) end + +-- Gets the window tabpage +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- +-- Return: ~ +-- Tabpage that contains the window +--- @param window window +--- @return tabpage +function vim.api.nvim_win_get_tabpage(window) end + +-- Gets a window-scoped (w:) variable +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {name} Variable name +-- +-- Return: ~ +-- Variable value +--- @param window window +--- @param name string +--- @return object +function vim.api.nvim_win_get_var(window, name) end + +-- Gets the window width +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- +-- Return: ~ +-- Width as a count of columns +--- @param window window +--- @return number +function vim.api.nvim_win_get_width(window) end + +-- Closes the window and hide the buffer it contains (like |:hide| with a +-- |window-ID|). +-- +-- Like |:hide| the buffer becomes hidden unless another window is editing +-- it, or 'bufhidden' is `unload`, `delete` or `wipe` as opposed to |:close| +-- or |nvim_win_close()|, which will close the buffer. +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +--- @param window window +function vim.api.nvim_win_hide(window) end + +-- Checks if a window is valid +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- +-- Return: ~ +-- true if the window is valid, false otherwise +--- @param window window +--- @return boolean +function vim.api.nvim_win_is_valid(window) end + +-- Sets the current buffer in a window, without side effects +-- +-- Attributes: ~ +-- not allowed when |textlock| is active +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {buffer} Buffer handle +--- @param window window +--- @param buffer buffer +function vim.api.nvim_win_set_buf(window, buffer) end + +-- Configures window layout. Currently only for floating and external windows +-- (including changing a split window to those layouts). +-- +-- When reconfiguring a floating window, absent option keys will not be +-- changed. `row`/`col` and `relative` must be reconfigured together. +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {config} Map defining the window configuration, see |nvim_open_win()| +-- +-- See also: ~ +-- • |nvim_open_win()| +--- @param window window +--- @param config? table +function vim.api.nvim_win_set_config(window, config) end + +-- Sets the (1,0)-indexed cursor position in the window. |api-indexing| This +-- scrolls the window even if it is not the current one. +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {pos} (row, col) tuple representing the new position +--- @param window window +--- @param pos number[] +function vim.api.nvim_win_set_cursor(window, pos) end + +-- Sets the window height. +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {height} Height as a count of rows +--- @param window window +--- @param height number +function vim.api.nvim_win_set_height(window, height) end + +-- Set highlight namespace for a window. This will use highlights defined +-- with |nvim_set_hl()| for this namespace, but fall back to global +-- highlights (ns=0) when missing. +-- +-- This takes precedence over the 'winhighlight' option. +-- +-- Parameters: ~ +-- • {ns_id} the namespace to use +--- @param window window +--- @param ns_id number +function vim.api.nvim_win_set_hl_ns(window, ns_id) end + +-- Sets a window option value. Passing `nil` as value deletes the option +-- (only works if there's a global fallback) +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {name} Option name +-- • {value} Option value +--- @param window window +--- @param name string +--- @param value object +function vim.api.nvim_win_set_option(window, name, value) end + +-- Sets a window-scoped (w:) variable +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {name} Variable name +-- • {value} Variable value +--- @param window window +--- @param name string +--- @param value object +function vim.api.nvim_win_set_var(window, name, value) end + +-- Sets the window width. This will only succeed if the screen is split +-- vertically. +-- +-- Parameters: ~ +-- • {window} Window handle, or 0 for current window +-- • {width} Width as a count of columns +--- @param window window +--- @param width number +function vim.api.nvim_win_set_width(window, width) end + diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/cmd.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/cmd.lua new file mode 100644 index 00000000..a9d1e845 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/cmd.lua @@ -0,0 +1,3086 @@ +---@meta + +-- append text +function vim.cmd.a(...)end + +-- enter abbreviation +function vim.cmd.ab(...)end + +-- enter abbreviation +function vim.cmd.abbreviate(...)end + +-- remove all abbreviations +function vim.cmd.abc(...)end + +-- remove all abbreviations +function vim.cmd.abclear(...)end + +-- make split window appear left or above +function vim.cmd.abo(...)end + +-- make split window appear left or above +function vim.cmd.aboveleft(...)end + +-- open a window for each file in the argument +function vim.cmd.al(...)end + +-- open a window for each file in the argument +function vim.cmd.all(...)end + +-- enter new menu item for all modes +function vim.cmd.am(...)end + +-- enter new menu item for all modes +function vim.cmd.amenu(...)end + +-- enter a new menu for all modes that will not +function vim.cmd.an(...)end + +-- enter a new menu for all modes that will not +function vim.cmd.anoremenu(...)end + +-- append text +function vim.cmd.append(...)end + +-- print the argument list +function vim.cmd.ar(...)end + +-- add items to the argument list +function vim.cmd.arga(...)end + +-- add items to the argument list +function vim.cmd.argadd(...)end + +-- delete items from the argument list +function vim.cmd.argd(...)end + +-- remove duplicates from the argument list +function vim.cmd.argded(...)end + +-- remove duplicates from the argument list +function vim.cmd.argdedupe(...)end + +-- delete items from the argument list +function vim.cmd.argdelete(...)end + +-- do a command on all items in the argument list +function vim.cmd.argdo(...)end + +-- add item to the argument list and edit it +function vim.cmd.arge(...)end + +-- add item to the argument list and edit it +function vim.cmd.argedit(...)end + +-- define the global argument list +function vim.cmd.argg(...)end + +-- define the global argument list +function vim.cmd.argglobal(...)end + +-- define a local argument list +function vim.cmd.argl(...)end + +-- define a local argument list +function vim.cmd.arglocal(...)end + +-- print the argument list +function vim.cmd.args(...)end + +-- go to specific file in the argument list +function vim.cmd.argu(...)end + +-- go to specific file in the argument list +function vim.cmd.argument(...)end + +-- print ascii value of character under the cursor +function vim.cmd.as(...)end + +-- print ascii value of character under the cursor +function vim.cmd.ascii(...)end + +-- enter or show autocommands +function vim.cmd.au(...)end + +-- select the autocommand group to use +function vim.cmd.aug(...)end + +-- select the autocommand group to use +function vim.cmd.augroup(...)end + +-- remove menu for all modes +function vim.cmd.aun(...)end + +-- remove menu for all modes +function vim.cmd.aunmenu(...)end + +-- enter or show autocommands +function vim.cmd.autocmd(...)end + +-- go to specific buffer in the buffer list +function vim.cmd.b(...)end + +-- go to previous buffer in the buffer list +function vim.cmd.bN(...)end + +-- go to previous buffer in the buffer list +function vim.cmd.bNext(...)end + +-- open a window for each buffer in the buffer list +function vim.cmd.ba(...)end + +-- add buffer to the buffer list +function vim.cmd.bad(...)end + +-- add buffer to the buffer list +function vim.cmd.badd(...)end + +-- open a window for each buffer in the buffer list +function vim.cmd.ball(...)end + +-- like ":badd" but also set the alternate file +function vim.cmd.balt(...)end + +-- remove a buffer from the buffer list +function vim.cmd.bd(...)end + +-- remove a buffer from the buffer list +function vim.cmd.bdelete(...)end + +-- set mouse and selection behavior +function vim.cmd.be(...)end + +-- set mouse and selection behavior +function vim.cmd.behave(...)end + +-- make split window appear right or below +function vim.cmd.bel(...)end + +-- make split window appear right or below +function vim.cmd.belowright(...)end + +-- go to first buffer in the buffer list +function vim.cmd.bf(...)end + +-- go to first buffer in the buffer list +function vim.cmd.bfirst(...)end + +-- go to last buffer in the buffer list +function vim.cmd.bl(...)end + +-- go to last buffer in the buffer list +function vim.cmd.blast(...)end + +-- go to next buffer in the buffer list that has +function vim.cmd.bm(...)end + +-- go to next buffer in the buffer list that has +function vim.cmd.bmodified(...)end + +-- go to next buffer in the buffer list +function vim.cmd.bn(...)end + +-- go to next buffer in the buffer list +function vim.cmd.bnext(...)end + +-- make split window appear at bottom or far right +function vim.cmd.bo(...)end + +-- make split window appear at bottom or far right +function vim.cmd.botright(...)end + +-- go to previous buffer in the buffer list +function vim.cmd.bp(...)end + +-- go to previous buffer in the buffer list +function vim.cmd.bprevious(...)end + +-- go to first buffer in the buffer list +function vim.cmd.br(...)end + +-- break out of while loop +function vim.cmd.brea(...)end + +-- break out of while loop +vim.cmd["break"] = function(...)end + +-- add a debugger breakpoint +function vim.cmd.breaka(...)end + +-- add a debugger breakpoint +function vim.cmd.breakadd(...)end + +-- delete a debugger breakpoint +function vim.cmd.breakd(...)end + +-- delete a debugger breakpoint +function vim.cmd.breakdel(...)end + +-- list debugger breakpoints +function vim.cmd.breakl(...)end + +-- list debugger breakpoints +function vim.cmd.breaklist(...)end + +-- go to first buffer in the buffer list +function vim.cmd.brewind(...)end + +-- use file selection dialog +function vim.cmd.bro(...)end + +-- use file selection dialog +function vim.cmd.browse(...)end + +-- execute command in each listed buffer +function vim.cmd.bufdo(...)end + +-- go to specific buffer in the buffer list +function vim.cmd.buffer(...)end + +-- list all files in the buffer list +function vim.cmd.buffers(...)end + +-- unload a specific buffer +function vim.cmd.bun(...)end + +-- unload a specific buffer +function vim.cmd.bunload(...)end + +-- really delete a buffer +function vim.cmd.bw(...)end + +-- really delete a buffer +function vim.cmd.bwipeout(...)end + +-- replace a line or series of lines +function vim.cmd.c(...)end + +-- go to previous error +function vim.cmd.cN(...)end + +-- go to previous error +function vim.cmd.cNext(...)end + +-- go to last error in previous file +function vim.cmd.cNf(...)end + +-- go to last error in previous file +function vim.cmd.cNfile(...)end + +-- like ":abbreviate" but for Command-line mode +function vim.cmd.ca(...)end + +-- like ":abbreviate" but for Command-line mode +function vim.cmd.cabbrev(...)end + +-- clear all abbreviations for Command-line mode +function vim.cmd.cabc(...)end + +-- clear all abbreviations for Command-line mode +function vim.cmd.cabclear(...)end + +-- go to error above current line +function vim.cmd.cabo(...)end + +-- go to error above current line +function vim.cmd.cabove(...)end + +-- add errors from buffer +function vim.cmd.cad(...)end + +-- add errors from buffer +function vim.cmd.caddbuffer(...)end + +-- add errors from expr +function vim.cmd.cadde(...)end + +-- add errors from expr +function vim.cmd.caddexpr(...)end + +-- add error message to current quickfix list +function vim.cmd.caddf(...)end + +-- add error message to current quickfix list +function vim.cmd.caddfile(...)end + +-- go to error after current cursor +function vim.cmd.caf(...)end + +-- go to error after current cursor +function vim.cmd.cafter(...)end + +-- call a function +function vim.cmd.cal(...)end + +-- call a function +function vim.cmd.call(...)end + +-- part of a :try command +function vim.cmd.cat(...)end + +-- part of a :try command +function vim.cmd.catch(...)end + +-- parse error messages and jump to first error +function vim.cmd.cb(...)end + +-- go to error before current cursor +function vim.cmd.cbef(...)end + +-- go to error before current cursor +function vim.cmd.cbefore(...)end + +-- go to error below current line +function vim.cmd.cbel(...)end + +-- go to error below current line +function vim.cmd.cbelow(...)end + +-- scroll to the bottom of the quickfix window +function vim.cmd.cbo(...)end + +-- scroll to the bottom of the quickfix window +function vim.cmd.cbottom(...)end + +-- parse error messages and jump to first error +function vim.cmd.cbuffer(...)end + +-- go to specific error +function vim.cmd.cc(...)end + +-- close quickfix window +function vim.cmd.ccl(...)end + +-- close quickfix window +function vim.cmd.cclose(...)end + +-- change directory +function vim.cmd.cd(...)end + +-- execute command in each valid error list entry +function vim.cmd.cdo(...)end + +-- format lines at the center +function vim.cmd.ce(...)end + +-- format lines at the center +function vim.cmd.center(...)end + +-- read errors from expr and jump to first +function vim.cmd.cex(...)end + +-- read errors from expr and jump to first +function vim.cmd.cexpr(...)end + +-- read file with error messages and jump to first +function vim.cmd.cf(...)end + +-- execute command in each file in error list +function vim.cmd.cfd(...)end + +-- execute command in each file in error list +function vim.cmd.cfdo(...)end + +-- read file with error messages and jump to first +function vim.cmd.cfile(...)end + +-- go to the specified error, default first one +function vim.cmd.cfir(...)end + +-- go to the specified error, default first one +function vim.cmd.cfirst(...)end + +-- read file with error messages +function vim.cmd.cg(...)end + +-- get errors from buffer +function vim.cmd.cgetb(...)end + +-- get errors from buffer +function vim.cmd.cgetbuffer(...)end + +-- get errors from expr +function vim.cmd.cgete(...)end + +-- get errors from expr +function vim.cmd.cgetexpr(...)end + +-- read file with error messages +function vim.cmd.cgetfile(...)end + +-- replace a line or series of lines +function vim.cmd.change(...)end + +-- print the change list +function vim.cmd.changes(...)end + +-- change directory +function vim.cmd.chd(...)end + +-- change directory +function vim.cmd.chdir(...)end + +-- run healthchecks +function vim.cmd.che(...)end + +-- run healthchecks +function vim.cmd.checkhealth(...)end + +-- list included files +function vim.cmd.checkp(...)end + +-- list included files +function vim.cmd.checkpath(...)end + +-- check timestamp of loaded buffers +function vim.cmd.checkt(...)end + +-- check timestamp of loaded buffers +function vim.cmd.checktime(...)end + +-- list the error lists +function vim.cmd.chi(...)end + +-- list the error lists +function vim.cmd.chistory(...)end + +-- list all errors +function vim.cmd.cl(...)end + +-- go to the specified error, default last one +function vim.cmd.cla(...)end + +-- go to the specified error, default last one +function vim.cmd.clast(...)end + +-- clear the jump list +function vim.cmd.cle(...)end + +-- clear the jump list +function vim.cmd.clearjumps(...)end + +-- list all errors +function vim.cmd.clist(...)end + +-- close current window +function vim.cmd.clo(...)end + +-- close current window +function vim.cmd.close(...)end + +-- like ":map" but for Command-line mode +function vim.cmd.cm(...)end + +-- like ":map" but for Command-line mode +function vim.cmd.cmap(...)end + +-- clear all mappings for Command-line mode +function vim.cmd.cmapc(...)end + +-- clear all mappings for Command-line mode +function vim.cmd.cmapclear(...)end + +-- add menu for Command-line mode +function vim.cmd.cme(...)end + +-- add menu for Command-line mode +function vim.cmd.cmenu(...)end + +-- go to next error +function vim.cmd.cn(...)end + +-- go to newer error list +function vim.cmd.cnew(...)end + +-- go to newer error list +function vim.cmd.cnewer(...)end + +-- go to next error +function vim.cmd.cnext(...)end + +-- go to first error in next file +function vim.cmd.cnf(...)end + +-- go to first error in next file +function vim.cmd.cnfile(...)end + +-- like ":noremap" but for Command-line mode +function vim.cmd.cno(...)end + +-- like ":noreabbrev" but for Command-line mode +function vim.cmd.cnorea(...)end + +-- like ":noreabbrev" but for Command-line mode +function vim.cmd.cnoreabbrev(...)end + +-- like ":noremap" but for Command-line mode +function vim.cmd.cnoremap(...)end + +-- like ":noremenu" but for Command-line mode +function vim.cmd.cnoreme(...)end + +-- like ":noremenu" but for Command-line mode +function vim.cmd.cnoremenu(...)end + +-- copy lines +function vim.cmd.co(...)end + +-- go to older error list +function vim.cmd.col(...)end + +-- go to older error list +function vim.cmd.colder(...)end + +-- load a specific color scheme +function vim.cmd.colo(...)end + +-- load a specific color scheme +function vim.cmd.colorscheme(...)end + +-- create user-defined command +function vim.cmd.com(...)end + +-- clear all user-defined commands +function vim.cmd.comc(...)end + +-- clear all user-defined commands +function vim.cmd.comclear(...)end + +-- create user-defined command +function vim.cmd.command(...)end + +-- do settings for a specific compiler +function vim.cmd.comp(...)end + +-- do settings for a specific compiler +function vim.cmd.compiler(...)end + +-- go back to :while +function vim.cmd.con(...)end + +-- prompt user when confirmation required +function vim.cmd.conf(...)end + +-- prompt user when confirmation required +function vim.cmd.confirm(...)end + +-- create a variable as a constant +function vim.cmd.cons(...)end + +-- create a variable as a constant +function vim.cmd.const(...)end + +-- go back to :while +function vim.cmd.continue(...)end + +-- open quickfix window +function vim.cmd.cope(...)end + +-- open quickfix window +function vim.cmd.copen(...)end + +-- copy lines +function vim.cmd.copy(...)end + +-- go to previous error +function vim.cmd.cp(...)end + +-- go to last error in previous file +function vim.cmd.cpf(...)end + +-- go to last error in previous file +function vim.cmd.cpfile(...)end + +-- go to previous error +function vim.cmd.cprevious(...)end + +-- quit Vim with an error code +function vim.cmd.cq(...)end + +-- quit Vim with an error code +function vim.cmd.cquit(...)end + +-- go to the specified error, default first one +function vim.cmd.cr(...)end + +-- go to the specified error, default first one +function vim.cmd.crewind(...)end + +-- like ":unmap" but for Command-line mode +function vim.cmd.cu(...)end + +-- like ":unabbrev" but for Command-line mode +function vim.cmd.cuna(...)end + +-- like ":unabbrev" but for Command-line mode +function vim.cmd.cunabbrev(...)end + +-- like ":unmap" but for Command-line mode +function vim.cmd.cunmap(...)end + +-- remove menu for Command-line mode +function vim.cmd.cunme(...)end + +-- remove menu for Command-line mode +function vim.cmd.cunmenu(...)end + +-- open or close quickfix window +function vim.cmd.cw(...)end + +-- open or close quickfix window +function vim.cmd.cwindow(...)end + +-- short for |:delete| with the 'p' flag +function vim.cmd.d(...)end + +-- run a command in debugging mode +function vim.cmd.deb(...)end + +-- run a command in debugging mode +function vim.cmd.debug(...)end + +-- read debug mode commands from normal input +function vim.cmd.debugg(...)end + +-- read debug mode commands from normal input +function vim.cmd.debuggreedy(...)end + +-- delete user-defined command +function vim.cmd.delc(...)end + +-- delete user-defined command +function vim.cmd.delcommand(...)end + +-- delete lines +function vim.cmd.delete(...)end + +-- short for |:delete| with the 'p' flag +function vim.cmd.deletep(...)end + +-- delete a user function +function vim.cmd.delf(...)end + +-- delete a user function +function vim.cmd.delfunction(...)end + +-- delete marks +function vim.cmd.delm(...)end + +-- delete marks +function vim.cmd.delmarks(...)end + +-- display registers +function vim.cmd.di(...)end + +-- update 'diff' buffers +function vim.cmd.dif(...)end + +-- remove differences in current buffer +function vim.cmd.diffg(...)end + +-- remove differences in current buffer +function vim.cmd.diffget(...)end + +-- switch off diff mode +function vim.cmd.diffo(...)end + +-- switch off diff mode +function vim.cmd.diffoff(...)end + +-- apply a patch and show differences +function vim.cmd.diffp(...)end + +-- apply a patch and show differences +function vim.cmd.diffpatch(...)end + +-- remove differences in other buffer +function vim.cmd.diffpu(...)end + +-- remove differences in other buffer +function vim.cmd.diffput(...)end + +-- show differences with another file +function vim.cmd.diffs(...)end + +-- show differences with another file +function vim.cmd.diffsplit(...)end + +-- make current window a diff window +function vim.cmd.diffthis(...)end + +-- update 'diff' buffers +function vim.cmd.diffupdate(...)end + +-- show or enter digraphs +function vim.cmd.dig(...)end + +-- show or enter digraphs +function vim.cmd.digraphs(...)end + +-- display registers +function vim.cmd.display(...)end + +-- jump to #define +function vim.cmd.dj(...)end + +-- jump to #define +function vim.cmd.djump(...)end + +-- short for |:delete| with the 'l' flag +function vim.cmd.dl(...)end + +-- list #defines +function vim.cmd.dli(...)end + +-- list #defines +function vim.cmd.dlist(...)end + +-- apply autocommands to current buffer +vim.cmd["do"] = function(...)end + +-- apply autocommands for all loaded buffers +function vim.cmd.doautoa(...)end + +-- apply autocommands for all loaded buffers +function vim.cmd.doautoall(...)end + +-- apply autocommands to current buffer +function vim.cmd.doautocmd(...)end + +-- jump to window editing file or edit file in +function vim.cmd.dr(...)end + +-- jump to window editing file or edit file in +function vim.cmd.drop(...)end + +-- list one #define +function vim.cmd.ds(...)end + +-- list one #define +function vim.cmd.dsearch(...)end + +-- split window and jump to #define +function vim.cmd.dsp(...)end + +-- split window and jump to #define +function vim.cmd.dsplit(...)end + +-- edit a file +function vim.cmd.e(...)end + +-- go to older change, undo +function vim.cmd.ea(...)end + +-- go to older change, undo +function vim.cmd.earlier(...)end + +-- echoes the result of expressions +function vim.cmd.ec(...)end + +-- echoes the result of expressions +function vim.cmd.echo(...)end + +-- like :echo, show like an error and use history +function vim.cmd.echoe(...)end + +-- like :echo, show like an error and use history +function vim.cmd.echoerr(...)end + +-- set highlighting for echo commands +function vim.cmd.echoh(...)end + +-- set highlighting for echo commands +function vim.cmd.echohl(...)end + +-- same as :echo, put message in history +function vim.cmd.echom(...)end + +-- same as :echo, put message in history +function vim.cmd.echomsg(...)end + +-- same as :echo, but without +function vim.cmd.echon(...)end + +-- edit a file +function vim.cmd.edit(...)end + +-- part of an :if command +function vim.cmd.el(...)end + +-- part of an :if command +vim.cmd["else"] = function(...)end + +-- part of an :if command +function vim.cmd.elsei(...)end + +-- part of an :if command +vim.cmd["elseif"] = function(...)end + +-- execute a menu by name +function vim.cmd.em(...)end + +-- execute a menu by name +function vim.cmd.emenu(...)end + +-- end previous :if +function vim.cmd.en(...)end + +-- end of a user function started with :function +function vim.cmd.endf(...)end + +-- end previous :for +function vim.cmd.endfo(...)end + +-- end previous :for +function vim.cmd.endfor(...)end + +-- end of a user function started with :function +function vim.cmd.endfunction(...)end + +-- end previous :if +function vim.cmd.endif(...)end + +-- end previous :try +function vim.cmd.endt(...)end + +-- end previous :try +function vim.cmd.endtry(...)end + +-- end previous :while +function vim.cmd.endw(...)end + +-- end previous :while +function vim.cmd.endwhile(...)end + +-- edit a new, unnamed buffer +function vim.cmd.ene(...)end + +-- edit a new, unnamed buffer +function vim.cmd.enew(...)end + +-- evaluate an expression and discard the result +function vim.cmd.ev(...)end + +-- evaluate an expression and discard the result +function vim.cmd.eval(...)end + +-- same as ":edit" +function vim.cmd.ex(...)end + +-- execute result of expressions +function vim.cmd.exe(...)end + +-- execute result of expressions +function vim.cmd.execute(...)end + +-- same as ":xit" +function vim.cmd.exi(...)end + +-- same as ":xit" +function vim.cmd.exit(...)end + +-- overview of Ex commands +function vim.cmd.exu(...)end + +-- overview of Ex commands +function vim.cmd.exusage(...)end + +-- show or set the current file name +function vim.cmd.f(...)end + +-- show or set the current file name +function vim.cmd.file(...)end + +-- list all files in the buffer list +function vim.cmd.files(...)end + +-- switch file type detection on/off +function vim.cmd.filet(...)end + +-- switch file type detection on/off +function vim.cmd.filetype(...)end + +-- filter output of following command +function vim.cmd.filt(...)end + +-- filter output of following command +function vim.cmd.filter(...)end + +-- find file in 'path' and edit it +function vim.cmd.fin(...)end + +-- part of a :try command +function vim.cmd.fina(...)end + +-- part of a :try command +function vim.cmd.finally(...)end + +-- find file in 'path' and edit it +function vim.cmd.find(...)end + +-- quit sourcing a Vim script +function vim.cmd.fini(...)end + +-- quit sourcing a Vim script +function vim.cmd.finish(...)end + +-- go to the first file in the argument list +function vim.cmd.fir(...)end + +-- go to the first file in the argument list +function vim.cmd.first(...)end + +-- create a fold +function vim.cmd.fo(...)end + +-- create a fold +function vim.cmd.fold(...)end + +-- close folds +function vim.cmd.foldc(...)end + +-- close folds +function vim.cmd.foldclose(...)end + +-- execute command on lines not in a closed fold +function vim.cmd.foldd(...)end + +-- execute command on lines in a closed fold +function vim.cmd.folddoc(...)end + +-- execute command on lines in a closed fold +function vim.cmd.folddoclosed(...)end + +-- execute command on lines not in a closed fold +function vim.cmd.folddoopen(...)end + +-- open folds +function vim.cmd.foldo(...)end + +-- open folds +function vim.cmd.foldopen(...)end + +-- for loop +vim.cmd["for"] = function(...)end + +-- define a user function +function vim.cmd.fu(...)end + +-- define a user function +vim.cmd["function"] = function(...)end + +-- execute commands for matching lines +function vim.cmd.g(...)end + +-- execute commands for matching lines +function vim.cmd.global(...)end + +-- go to byte in the buffer +function vim.cmd.go(...)end + +-- go to byte in the buffer +vim.cmd["goto"] = function(...)end + +-- run 'grepprg' and jump to first match +function vim.cmd.gr(...)end + +-- run 'grepprg' and jump to first match +function vim.cmd.grep(...)end + +-- like :grep, but append to current list +function vim.cmd.grepa(...)end + +-- like :grep, but append to current list +function vim.cmd.grepadd(...)end + +-- start the GUI +function vim.cmd.gu(...)end + +-- start the GUI +function vim.cmd.gui(...)end + +-- start the GUI +function vim.cmd.gv(...)end + +-- start the GUI +function vim.cmd.gvim(...)end + +-- open a help window +function vim.cmd.h(...)end + +-- open a help window +function vim.cmd.help(...)end + +-- close one help window +function vim.cmd.helpc(...)end + +-- close one help window +function vim.cmd.helpclose(...)end + +-- like ":grep" but searches help files +function vim.cmd.helpg(...)end + +-- like ":grep" but searches help files +function vim.cmd.helpgrep(...)end + +-- generate help tags for a directory +function vim.cmd.helpt(...)end + +-- generate help tags for a directory +function vim.cmd.helptags(...)end + +-- specify highlighting methods +function vim.cmd.hi(...)end + +-- hide current buffer for a command +function vim.cmd.hid(...)end + +-- hide current buffer for a command +function vim.cmd.hide(...)end + +-- specify highlighting methods +function vim.cmd.highlight(...)end + +-- print a history list +function vim.cmd.his(...)end + +-- print a history list +function vim.cmd.history(...)end + +-- following window command work horizontally +function vim.cmd.hor(...)end + +-- following window command work horizontally +function vim.cmd.horizontal(...)end + +-- insert text +function vim.cmd.i(...)end + +-- like ":abbrev" but for Insert mode +function vim.cmd.ia(...)end + +-- like ":abbrev" but for Insert mode +function vim.cmd.iabbrev(...)end + +-- like ":abclear" but for Insert mode +function vim.cmd.iabc(...)end + +-- like ":abclear" but for Insert mode +function vim.cmd.iabclear(...)end + +-- execute commands when condition met +vim.cmd["if"] = function(...)end + +-- jump to definition of identifier +function vim.cmd.ij(...)end + +-- jump to definition of identifier +function vim.cmd.ijump(...)end + +-- list lines where identifier matches +function vim.cmd.il(...)end + +-- list lines where identifier matches +function vim.cmd.ilist(...)end + +-- like ":map" but for Insert mode +function vim.cmd.im(...)end + +-- like ":map" but for Insert mode +function vim.cmd.imap(...)end + +-- like ":mapclear" but for Insert mode +function vim.cmd.imapc(...)end + +-- like ":mapclear" but for Insert mode +function vim.cmd.imapclear(...)end + +-- add menu for Insert mode +function vim.cmd.ime(...)end + +-- add menu for Insert mode +function vim.cmd.imenu(...)end + +-- like ":noremap" but for Insert mode +function vim.cmd.ino(...)end + +-- like ":noreabbrev" but for Insert mode +function vim.cmd.inorea(...)end + +-- like ":noreabbrev" but for Insert mode +function vim.cmd.inoreabbrev(...)end + +-- like ":noremap" but for Insert mode +function vim.cmd.inoremap(...)end + +-- like ":noremenu" but for Insert mode +function vim.cmd.inoreme(...)end + +-- like ":noremenu" but for Insert mode +function vim.cmd.inoremenu(...)end + +-- insert text +function vim.cmd.insert(...)end + +-- print the introductory message +function vim.cmd.int(...)end + +-- print the introductory message +function vim.cmd.intro(...)end + +-- list one line where identifier matches +function vim.cmd.is(...)end + +-- list one line where identifier matches +function vim.cmd.isearch(...)end + +-- split window and jump to definition of +function vim.cmd.isp(...)end + +-- split window and jump to definition of +function vim.cmd.isplit(...)end + +-- like ":unmap" but for Insert mode +function vim.cmd.iu(...)end + +-- like ":unabbrev" but for Insert mode +function vim.cmd.iuna(...)end + +-- like ":unabbrev" but for Insert mode +function vim.cmd.iunabbrev(...)end + +-- like ":unmap" but for Insert mode +function vim.cmd.iunmap(...)end + +-- remove menu for Insert mode +function vim.cmd.iunme(...)end + +-- remove menu for Insert mode +function vim.cmd.iunmenu(...)end + +-- join lines +function vim.cmd.j(...)end + +-- join lines +function vim.cmd.join(...)end + +-- print the jump list +function vim.cmd.ju(...)end + +-- print the jump list +function vim.cmd.jumps(...)end + +-- set a mark +function vim.cmd.k(...)end + +-- following command keeps marks where they are +function vim.cmd.kee(...)end + +-- following command keeps the alternate file +function vim.cmd.keepa(...)end + +-- following command keeps the alternate file +function vim.cmd.keepalt(...)end + +-- following command keeps jumplist and marks +function vim.cmd.keepj(...)end + +-- following command keeps jumplist and marks +function vim.cmd.keepjumps(...)end + +-- following command keeps marks where they are +function vim.cmd.keepmarks(...)end + +-- following command keeps search pattern history +function vim.cmd.keepp(...)end + +-- following command keeps search pattern history +function vim.cmd.keeppatterns(...)end + +-- print lines +function vim.cmd.l(...)end + +-- go to previous entry in location list +function vim.cmd.lN(...)end + +-- go to previous entry in location list +function vim.cmd.lNext(...)end + +-- go to last entry in previous file +function vim.cmd.lNf(...)end + +-- go to last entry in previous file +function vim.cmd.lNfile(...)end + +-- go to the last file in the argument list +function vim.cmd.la(...)end + +-- go to location above current line +function vim.cmd.lab(...)end + +-- go to location above current line +function vim.cmd.labove(...)end + +-- add locations from expr +function vim.cmd.lad(...)end + +-- add locations from buffer +function vim.cmd.laddb(...)end + +-- add locations from buffer +function vim.cmd.laddbuffer(...)end + +-- add locations from expr +function vim.cmd.laddexpr(...)end + +-- add locations to current location list +function vim.cmd.laddf(...)end + +-- add locations to current location list +function vim.cmd.laddfile(...)end + +-- go to location after current cursor +function vim.cmd.laf(...)end + +-- go to location after current cursor +function vim.cmd.lafter(...)end + +-- set the language (locale) +function vim.cmd.lan(...)end + +-- set the language (locale) +function vim.cmd.language(...)end + +-- go to the last file in the argument list +function vim.cmd.last(...)end + +-- go to newer change, redo +function vim.cmd.lat(...)end + +-- go to newer change, redo +function vim.cmd.later(...)end + +-- parse locations and jump to first location +function vim.cmd.lb(...)end + +-- go to location before current cursor +function vim.cmd.lbef(...)end + +-- go to location before current cursor +function vim.cmd.lbefore(...)end + +-- go to location below current line +function vim.cmd.lbel(...)end + +-- go to location below current line +function vim.cmd.lbelow(...)end + +-- scroll to the bottom of the location window +function vim.cmd.lbo(...)end + +-- scroll to the bottom of the location window +function vim.cmd.lbottom(...)end + +-- parse locations and jump to first location +function vim.cmd.lbuffer(...)end + +-- change directory locally +function vim.cmd.lc(...)end + +-- change directory locally +function vim.cmd.lcd(...)end + +-- change directory locally +function vim.cmd.lch(...)end + +-- change directory locally +function vim.cmd.lchdir(...)end + +-- close location window +function vim.cmd.lcl(...)end + +-- close location window +function vim.cmd.lclose(...)end + +-- execute command in valid location list entries +function vim.cmd.ld(...)end + +-- execute command in valid location list entries +function vim.cmd.ldo(...)end + +-- left align lines +function vim.cmd.le(...)end + +-- left align lines +function vim.cmd.left(...)end + +-- make split window appear left or above +function vim.cmd.lefta(...)end + +-- make split window appear left or above +function vim.cmd.leftabove(...)end + +-- assign a value to a variable or option +function vim.cmd.let(...)end + +-- read locations from expr and jump to first +function vim.cmd.lex(...)end + +-- read locations from expr and jump to first +function vim.cmd.lexpr(...)end + +-- read file with locations and jump to first +function vim.cmd.lf(...)end + +-- execute command in each file in location list +function vim.cmd.lfd(...)end + +-- execute command in each file in location list +function vim.cmd.lfdo(...)end + +-- read file with locations and jump to first +function vim.cmd.lfile(...)end + +-- go to the specified location, default first one +function vim.cmd.lfir(...)end + +-- go to the specified location, default first one +function vim.cmd.lfirst(...)end + +-- read file with locations +function vim.cmd.lg(...)end + +-- get locations from buffer +function vim.cmd.lgetb(...)end + +-- get locations from buffer +function vim.cmd.lgetbuffer(...)end + +-- get locations from expr +function vim.cmd.lgete(...)end + +-- get locations from expr +function vim.cmd.lgetexpr(...)end + +-- read file with locations +function vim.cmd.lgetfile(...)end + +-- run 'grepprg' and jump to first match +function vim.cmd.lgr(...)end + +-- run 'grepprg' and jump to first match +function vim.cmd.lgrep(...)end + +-- like :grep, but append to current list +function vim.cmd.lgrepa(...)end + +-- like :grep, but append to current list +function vim.cmd.lgrepadd(...)end + +-- like ":helpgrep" but uses location list +function vim.cmd.lh(...)end + +-- like ":helpgrep" but uses location list +function vim.cmd.lhelpgrep(...)end + +-- list the location lists +function vim.cmd.lhi(...)end + +-- list the location lists +function vim.cmd.lhistory(...)end + +-- print lines +function vim.cmd.list(...)end + +-- go to specific location +function vim.cmd.ll(...)end + +-- go to the specified location, default last one +function vim.cmd.lla(...)end + +-- go to the specified location, default last one +function vim.cmd.llast(...)end + +-- list all locations +function vim.cmd.lli(...)end + +-- list all locations +function vim.cmd.llist(...)end + +-- like ":map!" but includes Lang-Arg mode +function vim.cmd.lm(...)end + +-- execute external command 'makeprg' and parse +function vim.cmd.lmak(...)end + +-- execute external command 'makeprg' and parse +function vim.cmd.lmake(...)end + +-- like ":map!" but includes Lang-Arg mode +function vim.cmd.lmap(...)end + +-- like ":mapclear!" but includes Lang-Arg mode +function vim.cmd.lmapc(...)end + +-- like ":mapclear!" but includes Lang-Arg mode +function vim.cmd.lmapclear(...)end + +-- like ":noremap!" but includes Lang-Arg mode +function vim.cmd.ln(...)end + +-- go to next location +function vim.cmd.lne(...)end + +-- go to newer location list +function vim.cmd.lnew(...)end + +-- go to newer location list +function vim.cmd.lnewer(...)end + +-- go to next location +function vim.cmd.lnext(...)end + +-- go to first location in next file +function vim.cmd.lnf(...)end + +-- go to first location in next file +function vim.cmd.lnfile(...)end + +-- like ":noremap!" but includes Lang-Arg mode +function vim.cmd.lnoremap(...)end + +-- load view for current window from a file +function vim.cmd.lo(...)end + +-- load the following keymaps until EOF +function vim.cmd.loadk(...)end + +-- load the following keymaps until EOF +function vim.cmd.loadkeymap(...)end + +-- load view for current window from a file +function vim.cmd.loadview(...)end + +-- following command keeps marks where they are +function vim.cmd.loc(...)end + +-- following command keeps marks where they are +function vim.cmd.lockmarks(...)end + +-- lock variables +function vim.cmd.lockv(...)end + +-- lock variables +function vim.cmd.lockvar(...)end + +-- go to older location list +function vim.cmd.lol(...)end + +-- go to older location list +function vim.cmd.lolder(...)end + +-- open location window +function vim.cmd.lope(...)end + +-- open location window +function vim.cmd.lopen(...)end + +-- go to previous location +function vim.cmd.lp(...)end + +-- go to last location in previous file +function vim.cmd.lpf(...)end + +-- go to last location in previous file +function vim.cmd.lpfile(...)end + +-- go to previous location +function vim.cmd.lprevious(...)end + +-- go to the specified location, default first one +function vim.cmd.lr(...)end + +-- go to the specified location, default first one +function vim.cmd.lrewind(...)end + +-- list all buffers +function vim.cmd.ls(...)end + +-- jump to tag and add matching tags to the +function vim.cmd.lt(...)end + +-- jump to tag and add matching tags to the +function vim.cmd.ltag(...)end + +-- like ":unmap!" but includes Lang-Arg mode +function vim.cmd.lu(...)end + +-- execute |Lua| command +function vim.cmd.lua(...)end + +-- execute Lua command for each line +function vim.cmd.luad(...)end + +-- execute Lua command for each line +function vim.cmd.luado(...)end + +-- execute |Lua| script file +function vim.cmd.luaf(...)end + +-- execute |Lua| script file +function vim.cmd.luafile(...)end + +-- like ":unmap!" but includes Lang-Arg mode +function vim.cmd.lunmap(...)end + +-- search for pattern in files +function vim.cmd.lv(...)end + +-- search for pattern in files +function vim.cmd.lvimgrep(...)end + +-- like :vimgrep, but append to current list +function vim.cmd.lvimgrepa(...)end + +-- like :vimgrep, but append to current list +function vim.cmd.lvimgrepadd(...)end + +-- open or close location window +function vim.cmd.lw(...)end + +-- open or close location window +function vim.cmd.lwindow(...)end + +-- move lines +function vim.cmd.m(...)end + +-- set a mark +function vim.cmd.ma(...)end + +-- execute external command 'makeprg' and parse +function vim.cmd.mak(...)end + +-- execute external command 'makeprg' and parse +function vim.cmd.make(...)end + +-- show or enter a mapping +function vim.cmd.map(...)end + +-- clear all mappings for Normal and Visual mode +function vim.cmd.mapc(...)end + +-- clear all mappings for Normal and Visual mode +function vim.cmd.mapclear(...)end + +-- set a mark +function vim.cmd.mark(...)end + +-- list all marks +function vim.cmd.marks(...)end + +-- define a match to highlight +function vim.cmd.mat(...)end + +-- define a match to highlight +function vim.cmd.match(...)end + +-- enter a new menu item +function vim.cmd.me(...)end + +-- enter a new menu item +function vim.cmd.menu(...)end + +-- add a menu translation item +function vim.cmd.menut(...)end + +-- add a menu translation item +function vim.cmd.menutranslate(...)end + +-- view previously displayed messages +function vim.cmd.mes(...)end + +-- view previously displayed messages +function vim.cmd.messages(...)end + +-- write current mappings and settings to a file +function vim.cmd.mk(...)end + +-- write current mappings and settings to a file +function vim.cmd.mkexrc(...)end + +-- write session info to a file +function vim.cmd.mks(...)end + +-- write session info to a file +function vim.cmd.mksession(...)end + +-- produce .spl spell file +function vim.cmd.mksp(...)end + +-- produce .spl spell file +function vim.cmd.mkspell(...)end + +-- write current mappings and settings to a file +function vim.cmd.mkv(...)end + +-- write view of current window to a file +function vim.cmd.mkvie(...)end + +-- write view of current window to a file +function vim.cmd.mkview(...)end + +-- write current mappings and settings to a file +function vim.cmd.mkvimrc(...)end + +-- show or change the screen mode +function vim.cmd.mod(...)end + +-- show or change the screen mode +function vim.cmd.mode(...)end + +-- move lines +function vim.cmd.move(...)end + +-- go to next file in the argument list +function vim.cmd.n(...)end + +-- create a new empty window +function vim.cmd.new(...)end + +-- go to next file in the argument list +function vim.cmd.next(...)end + +-- like ":map" but for Normal mode +function vim.cmd.nm(...)end + +-- like ":map" but for Normal mode +function vim.cmd.nmap(...)end + +-- clear all mappings for Normal mode +function vim.cmd.nmapc(...)end + +-- clear all mappings for Normal mode +function vim.cmd.nmapclear(...)end + +-- add menu for Normal mode +function vim.cmd.nme(...)end + +-- add menu for Normal mode +function vim.cmd.nmenu(...)end + +-- like ":noremap" but for Normal mode +function vim.cmd.nn(...)end + +-- like ":noremap" but for Normal mode +function vim.cmd.nnoremap(...)end + +-- like ":noremenu" but for Normal mode +function vim.cmd.nnoreme(...)end + +-- like ":noremenu" but for Normal mode +function vim.cmd.nnoremenu(...)end + +-- enter a mapping that will not be remapped +function vim.cmd.no(...)end + +-- following commands don't trigger autocommands +function vim.cmd.noa(...)end + +-- following commands don't trigger autocommands +function vim.cmd.noautocmd(...)end + +-- suspend 'hlsearch' highlighting +function vim.cmd.noh(...)end + +-- suspend 'hlsearch' highlighting +function vim.cmd.nohlsearch(...)end + +-- enter an abbreviation that will not be +function vim.cmd.norea(...)end + +-- enter an abbreviation that will not be +function vim.cmd.noreabbrev(...)end + +-- enter a mapping that will not be remapped +function vim.cmd.noremap(...)end + +-- enter a menu that will not be remapped +function vim.cmd.noreme(...)end + +-- enter a menu that will not be remapped +function vim.cmd.noremenu(...)end + +-- execute Normal mode commands +function vim.cmd.norm(...)end + +-- execute Normal mode commands +function vim.cmd.normal(...)end + +-- following commands don't create a swap file +function vim.cmd.nos(...)end + +-- following commands don't create a swap file +function vim.cmd.noswapfile(...)end + +-- print lines with line number +function vim.cmd.nu(...)end + +-- print lines with line number +function vim.cmd.number(...)end + +-- like ":unmap" but for Normal mode +function vim.cmd.nun(...)end + +-- like ":unmap" but for Normal mode +function vim.cmd.nunmap(...)end + +-- remove menu for Normal mode +function vim.cmd.nunme(...)end + +-- remove menu for Normal mode +function vim.cmd.nunmenu(...)end + +-- list files that have marks in the |shada| file +function vim.cmd.ol(...)end + +-- list files that have marks in the |shada| file +function vim.cmd.oldfiles(...)end + +-- like ":map" but for Operator-pending mode +function vim.cmd.om(...)end + +-- like ":map" but for Operator-pending mode +function vim.cmd.omap(...)end + +-- remove all mappings for Operator-pending mode +function vim.cmd.omapc(...)end + +-- remove all mappings for Operator-pending mode +function vim.cmd.omapclear(...)end + +-- add menu for Operator-pending mode +function vim.cmd.ome(...)end + +-- add menu for Operator-pending mode +function vim.cmd.omenu(...)end + +-- close all windows except the current one +function vim.cmd.on(...)end + +-- close all windows except the current one +function vim.cmd.only(...)end + +-- like ":noremap" but for Operator-pending mode +function vim.cmd.ono(...)end + +-- like ":noremap" but for Operator-pending mode +function vim.cmd.onoremap(...)end + +-- like ":noremenu" but for Operator-pending mode +function vim.cmd.onoreme(...)end + +-- like ":noremenu" but for Operator-pending mode +function vim.cmd.onoremenu(...)end + +-- open the options-window +function vim.cmd.opt(...)end + +-- open the options-window +function vim.cmd.options(...)end + +-- like ":unmap" but for Operator-pending mode +function vim.cmd.ou(...)end + +-- like ":unmap" but for Operator-pending mode +function vim.cmd.ounmap(...)end + +-- remove menu for Operator-pending mode +function vim.cmd.ounme(...)end + +-- remove menu for Operator-pending mode +function vim.cmd.ounmenu(...)end + +-- set new local syntax highlight for this window +function vim.cmd.ow(...)end + +-- set new local syntax highlight for this window +function vim.cmd.ownsyntax(...)end + +-- print lines +function vim.cmd.p(...)end + +-- add a plugin from 'packpath' +function vim.cmd.pa(...)end + +-- add a plugin from 'packpath' +function vim.cmd.packadd(...)end + +-- load all packages under 'packpath' +function vim.cmd.packl(...)end + +-- load all packages under 'packpath' +function vim.cmd.packloadall(...)end + +-- close preview window +function vim.cmd.pc(...)end + +-- close preview window +function vim.cmd.pclose(...)end + +-- execute perl command +function vim.cmd.pe(...)end + +-- edit file in the preview window +function vim.cmd.ped(...)end + +-- edit file in the preview window +function vim.cmd.pedit(...)end + +-- execute perl command +function vim.cmd.perl(...)end + +-- execute perl command for each line +function vim.cmd.perld(...)end + +-- execute perl command for each line +function vim.cmd.perldo(...)end + +-- execute perl script file +function vim.cmd.perlf(...)end + +-- execute perl script file +function vim.cmd.perlfile(...)end + +-- jump to older entry in tag stack +function vim.cmd.po(...)end + +-- jump to older entry in tag stack +function vim.cmd.pop(...)end + +-- popup a menu by name +function vim.cmd.popu(...)end + +-- popup a menu by name +function vim.cmd.popup(...)end + +-- ":pop" in preview window +function vim.cmd.pp(...)end + +-- ":pop" in preview window +function vim.cmd.ppop(...)end + +-- write all text to swap file +function vim.cmd.pre(...)end + +-- write all text to swap file +function vim.cmd.preserve(...)end + +-- go to previous file in argument list +function vim.cmd.prev(...)end + +-- go to previous file in argument list +function vim.cmd.previous(...)end + +-- print lines +function vim.cmd.print(...)end + +-- profiling functions and scripts +function vim.cmd.prof(...)end + +-- stop profiling a function or script +function vim.cmd.profd(...)end + +-- stop profiling a function or script +function vim.cmd.profdel(...)end + +-- profiling functions and scripts +function vim.cmd.profile(...)end + +-- like ":ijump" but shows match in preview window +function vim.cmd.ps(...)end + +-- like ":ijump" but shows match in preview window +function vim.cmd.psearch(...)end + +-- show tag in preview window +function vim.cmd.pt(...)end + +-- |:tNext| in preview window +function vim.cmd.ptN(...)end + +-- |:tNext| in preview window +function vim.cmd.ptNext(...)end + +-- show tag in preview window +function vim.cmd.ptag(...)end + +-- |:trewind| in preview window +function vim.cmd.ptf(...)end + +-- |:trewind| in preview window +function vim.cmd.ptfirst(...)end + +-- |:tjump| and show tag in preview window +function vim.cmd.ptj(...)end + +-- |:tjump| and show tag in preview window +function vim.cmd.ptjump(...)end + +-- |:tlast| in preview window +function vim.cmd.ptl(...)end + +-- |:tlast| in preview window +function vim.cmd.ptlast(...)end + +-- |:tnext| in preview window +function vim.cmd.ptn(...)end + +-- |:tnext| in preview window +function vim.cmd.ptnext(...)end + +-- |:tprevious| in preview window +function vim.cmd.ptp(...)end + +-- |:tprevious| in preview window +function vim.cmd.ptprevious(...)end + +-- |:trewind| in preview window +function vim.cmd.ptr(...)end + +-- |:trewind| in preview window +function vim.cmd.ptrewind(...)end + +-- |:tselect| and show tag in preview window +function vim.cmd.pts(...)end + +-- |:tselect| and show tag in preview window +function vim.cmd.ptselect(...)end + +-- insert contents of register in the text +function vim.cmd.pu(...)end + +-- insert contents of register in the text +function vim.cmd.put(...)end + +-- print current directory +function vim.cmd.pw(...)end + +-- print current directory +function vim.cmd.pwd(...)end + +-- execute Python command +function vim.cmd.py(...)end + +-- execute Python 3 command +function vim.cmd.py3(...)end + +-- execute Python 3 command for each line +function vim.cmd.py3d(...)end + +-- execute Python 3 command for each line +function vim.cmd.py3do(...)end + +-- execute Python 3 script file +function vim.cmd.py3f(...)end + +-- execute Python 3 script file +function vim.cmd.py3file(...)end + +-- execute Python command for each line +function vim.cmd.pyd(...)end + +-- execute Python command for each line +function vim.cmd.pydo(...)end + +-- execute Python script file +function vim.cmd.pyf(...)end + +-- execute Python script file +function vim.cmd.pyfile(...)end + +-- execute Python command +function vim.cmd.python(...)end + +-- same as :py3 +function vim.cmd.python3(...)end + +-- same as :pyx +function vim.cmd.pythonx(...)end + +-- execute |python_x| command +function vim.cmd.pyx(...)end + +-- execute |python_x| command for each line +function vim.cmd.pyxd(...)end + +-- execute |python_x| command for each line +function vim.cmd.pyxdo(...)end + +-- execute |python_x| script file +function vim.cmd.pyxf(...)end + +-- execute |python_x| script file +function vim.cmd.pyxfile(...)end + +-- quit current window (when one window quit Vim) +function vim.cmd.q(...)end + +-- quit Vim +function vim.cmd.qa(...)end + +-- quit Vim +function vim.cmd.qall(...)end + +-- quit current window (when one window quit Vim) +function vim.cmd.quit(...)end + +-- quit Vim +function vim.cmd.quita(...)end + +-- quit Vim +function vim.cmd.quitall(...)end + +-- read file into the text +function vim.cmd.r(...)end + +-- read file into the text +function vim.cmd.read(...)end + +-- recover a file from a swap file +function vim.cmd.rec(...)end + +-- recover a file from a swap file +function vim.cmd.recover(...)end + +-- redo one undone change +function vim.cmd.red(...)end + +-- redirect messages to a file or register +function vim.cmd.redi(...)end + +-- redirect messages to a file or register +function vim.cmd.redir(...)end + +-- redo one undone change +function vim.cmd.redo(...)end + +-- force a redraw of the display +function vim.cmd.redr(...)end + +-- force a redraw of the display +function vim.cmd.redraw(...)end + +-- force a redraw of the status line(s) and +function vim.cmd.redraws(...)end + +-- force a redraw of the status line(s) and +function vim.cmd.redrawstatus(...)end + +-- force a redraw of the tabline +function vim.cmd.redrawt(...)end + +-- force a redraw of the tabline +function vim.cmd.redrawtabline(...)end + +-- display the contents of registers +function vim.cmd.reg(...)end + +-- display the contents of registers +function vim.cmd.registers(...)end + +-- change current window height +function vim.cmd.res(...)end + +-- change current window height +function vim.cmd.resize(...)end + +-- change tab size +function vim.cmd.ret(...)end + +-- change tab size +function vim.cmd.retab(...)end + +-- return from a user function +function vim.cmd.retu(...)end + +-- return from a user function +vim.cmd["return"] = function(...)end + +-- go to the first file in the argument list +function vim.cmd.rew(...)end + +-- go to the first file in the argument list +function vim.cmd.rewind(...)end + +-- right align text +function vim.cmd.ri(...)end + +-- right align text +function vim.cmd.right(...)end + +-- make split window appear right or below +function vim.cmd.rightb(...)end + +-- make split window appear right or below +function vim.cmd.rightbelow(...)end + +-- read from |shada| file +function vim.cmd.rsh(...)end + +-- read from |shada| file +function vim.cmd.rshada(...)end + +-- source vim scripts in 'runtimepath' +function vim.cmd.ru(...)end + +-- execute Ruby command +function vim.cmd.rub(...)end + +-- execute Ruby command +function vim.cmd.ruby(...)end + +-- execute Ruby command for each line +function vim.cmd.rubyd(...)end + +-- execute Ruby command for each line +function vim.cmd.rubydo(...)end + +-- execute Ruby script file +function vim.cmd.rubyf(...)end + +-- execute Ruby script file +function vim.cmd.rubyfile(...)end + +-- read undo information from a file +function vim.cmd.rund(...)end + +-- read undo information from a file +function vim.cmd.rundo(...)end + +-- source vim scripts in 'runtimepath' +function vim.cmd.runtime(...)end + +-- find and replace text +function vim.cmd.s(...)end + +-- split window and go to previous file in +function vim.cmd.sN(...)end + +-- split window and go to previous file in +function vim.cmd.sNext(...)end + +-- split window and go to specific file in +function vim.cmd.sa(...)end + +-- open a window for each file in argument list +function vim.cmd.sal(...)end + +-- open a window for each file in argument list +function vim.cmd.sall(...)end + +-- execute a command in the sandbox +function vim.cmd.san(...)end + +-- execute a command in the sandbox +function vim.cmd.sandbox(...)end + +-- split window and go to specific file in +function vim.cmd.sargument(...)end + +-- save file under another name. +function vim.cmd.sav(...)end + +-- save file under another name. +function vim.cmd.saveas(...)end + +-- split window and go to specific file in the +function vim.cmd.sb(...)end + +-- split window and go to previous file in the +function vim.cmd.sbN(...)end + +-- split window and go to previous file in the +function vim.cmd.sbNext(...)end + +-- open a window for each file in the buffer list +function vim.cmd.sba(...)end + +-- open a window for each file in the buffer list +function vim.cmd.sball(...)end + +-- split window and go to first file in the +function vim.cmd.sbf(...)end + +-- split window and go to first file in the +function vim.cmd.sbfirst(...)end + +-- split window and go to last file in buffer +function vim.cmd.sbl(...)end + +-- split window and go to last file in buffer +function vim.cmd.sblast(...)end + +-- split window and go to modified file in the +function vim.cmd.sbm(...)end + +-- split window and go to modified file in the +function vim.cmd.sbmodified(...)end + +-- split window and go to next file in the buffer +function vim.cmd.sbn(...)end + +-- split window and go to next file in the buffer +function vim.cmd.sbnext(...)end + +-- split window and go to previous file in the +function vim.cmd.sbp(...)end + +-- split window and go to previous file in the +function vim.cmd.sbprevious(...)end + +-- split window and go to first file in the +function vim.cmd.sbr(...)end + +-- split window and go to first file in the +function vim.cmd.sbrewind(...)end + +-- split window and go to specific file in the +function vim.cmd.sbuffer(...)end + +-- list names of all sourced Vim scripts +function vim.cmd.scr(...)end + +-- encoding used in sourced Vim script +function vim.cmd.scripte(...)end + +-- encoding used in sourced Vim script +function vim.cmd.scriptencoding(...)end + +-- list names of all sourced Vim scripts +function vim.cmd.scriptnames(...)end + +-- show or set options +function vim.cmd.se(...)end + +-- show or set options +function vim.cmd.set(...)end + +-- set 'filetype', unless it was set already +function vim.cmd.setf(...)end + +-- set 'filetype', unless it was set already +function vim.cmd.setfiletype(...)end + +-- show global values of options +function vim.cmd.setg(...)end + +-- show global values of options +function vim.cmd.setglobal(...)end + +-- show or set options locally +function vim.cmd.setl(...)end + +-- show or set options locally +function vim.cmd.setlocal(...)end + +-- split current window and edit file in 'path' +function vim.cmd.sf(...)end + +-- split current window and edit file in 'path' +function vim.cmd.sfind(...)end + +-- split window and go to first file in the +function vim.cmd.sfir(...)end + +-- split window and go to first file in the +function vim.cmd.sfirst(...)end + +-- manipulate signs +function vim.cmd.sig(...)end + +-- manipulate signs +function vim.cmd.sign(...)end + +-- run a command silently +function vim.cmd.sil(...)end + +-- run a command silently +function vim.cmd.silent(...)end + +-- do nothing for a few seconds +function vim.cmd.sl(...)end + +-- split window and go to last file in the +function vim.cmd.sla(...)end + +-- split window and go to last file in the +function vim.cmd.slast(...)end + +-- do nothing for a few seconds +function vim.cmd.sleep(...)end + +-- :substitute with 'magic' +function vim.cmd.sm(...)end + +-- :substitute with 'magic' +function vim.cmd.smagic(...)end + +-- like ":map" but for Select mode +function vim.cmd.smap(...)end + +-- remove all mappings for Select mode +function vim.cmd.smapc(...)end + +-- remove all mappings for Select mode +function vim.cmd.smapclear(...)end + +-- add menu for Select mode +function vim.cmd.sme(...)end + +-- add menu for Select mode +function vim.cmd.smenu(...)end + +-- split window and go to next file in the +function vim.cmd.sn(...)end + +-- split window and go to next file in the +function vim.cmd.snext(...)end + +-- :substitute with 'nomagic' +function vim.cmd.sno(...)end + +-- :substitute with 'nomagic' +function vim.cmd.snomagic(...)end + +-- like ":noremap" but for Select mode +function vim.cmd.snor(...)end + +-- like ":noremap" but for Select mode +function vim.cmd.snoremap(...)end + +-- like ":noremenu" but for Select mode +function vim.cmd.snoreme(...)end + +-- like ":noremenu" but for Select mode +function vim.cmd.snoremenu(...)end + +-- read Vim or Ex commands from a file +function vim.cmd.so(...)end + +-- sort lines +function vim.cmd.sor(...)end + +-- sort lines +function vim.cmd.sort(...)end + +-- read Vim or Ex commands from a file +function vim.cmd.source(...)end + +-- split current window +function vim.cmd.sp(...)end + +-- add good word for spelling +function vim.cmd.spe(...)end + +-- split window and fill with all correct words +function vim.cmd.spelld(...)end + +-- split window and fill with all correct words +function vim.cmd.spelldump(...)end + +-- add good word for spelling +function vim.cmd.spellgood(...)end + +-- show info about loaded spell files +function vim.cmd.spelli(...)end + +-- show info about loaded spell files +function vim.cmd.spellinfo(...)end + +-- replace all bad words like last |z=| +function vim.cmd.spellr(...)end + +-- add rare word for spelling +function vim.cmd.spellra(...)end + +-- add rare word for spelling +function vim.cmd.spellrare(...)end + +-- replace all bad words like last |z=| +function vim.cmd.spellrepall(...)end + +-- remove good or bad word +function vim.cmd.spellu(...)end + +-- remove good or bad word +function vim.cmd.spellundo(...)end + +-- add spelling mistake +function vim.cmd.spellw(...)end + +-- add spelling mistake +function vim.cmd.spellwrong(...)end + +-- split current window +function vim.cmd.split(...)end + +-- split window and go to previous file in the +function vim.cmd.spr(...)end + +-- split window and go to previous file in the +function vim.cmd.sprevious(...)end + +-- split window and go to first file in the +function vim.cmd.sre(...)end + +-- split window and go to first file in the +function vim.cmd.srewind(...)end + +-- suspend the editor or escape to a shell +function vim.cmd.st(...)end + +-- split window and jump to a tag +function vim.cmd.sta(...)end + +-- split window and jump to a tag +function vim.cmd.stag(...)end + +-- start Insert mode +function vim.cmd.star(...)end + +-- start Virtual Replace mode +function vim.cmd.startg(...)end + +-- start Virtual Replace mode +function vim.cmd.startgreplace(...)end + +-- start Insert mode +function vim.cmd.startinsert(...)end + +-- start Replace mode +function vim.cmd.startr(...)end + +-- start Replace mode +function vim.cmd.startreplace(...)end + +-- do ":tjump" and split window +function vim.cmd.stj(...)end + +-- do ":tjump" and split window +function vim.cmd.stjump(...)end + +-- suspend the editor or escape to a shell +function vim.cmd.stop(...)end + +-- stop Insert mode +function vim.cmd.stopi(...)end + +-- stop Insert mode +function vim.cmd.stopinsert(...)end + +-- do ":tselect" and split window +function vim.cmd.sts(...)end + +-- do ":tselect" and split window +function vim.cmd.stselect(...)end + +-- find and replace text +function vim.cmd.substitute(...)end + +-- same as ":unhide" +function vim.cmd.sun(...)end + +-- same as ":unhide" +function vim.cmd.sunhide(...)end + +-- like ":unmap" but for Select mode +function vim.cmd.sunm(...)end + +-- like ":unmap" but for Select mode +function vim.cmd.sunmap(...)end + +-- remove menu for Select mode +function vim.cmd.sunme(...)end + +-- remove menu for Select mode +function vim.cmd.sunmenu(...)end + +-- same as ":stop" +function vim.cmd.sus(...)end + +-- same as ":stop" +function vim.cmd.suspend(...)end + +-- split window and edit file read-only +function vim.cmd.sv(...)end + +-- split window and edit file read-only +function vim.cmd.sview(...)end + +-- show the name of the current swap file +function vim.cmd.sw(...)end + +-- show the name of the current swap file +function vim.cmd.swapname(...)end + +-- syntax highlighting +function vim.cmd.sy(...)end + +-- sync scroll binding +function vim.cmd.sync(...)end + +-- sync scroll binding +function vim.cmd.syncbind(...)end + +-- syntax highlighting +function vim.cmd.syntax(...)end + +-- measure syntax highlighting speed +function vim.cmd.synti(...)end + +-- measure syntax highlighting speed +function vim.cmd.syntime(...)end + +-- same as ":copy" +function vim.cmd.t(...)end + +-- jump to previous matching tag +function vim.cmd.tN(...)end + +-- jump to previous matching tag +function vim.cmd.tNext(...)end + +-- jump to tag +function vim.cmd.ta(...)end + +-- create new tab when opening new window +function vim.cmd.tab(...)end + +-- go to previous tab page +function vim.cmd.tabN(...)end + +-- go to previous tab page +function vim.cmd.tabNext(...)end + +-- close current tab page +function vim.cmd.tabc(...)end + +-- close current tab page +function vim.cmd.tabclose(...)end + +-- execute command in each tab page +function vim.cmd.tabdo(...)end + +-- edit a file in a new tab page +function vim.cmd.tabe(...)end + +-- edit a file in a new tab page +function vim.cmd.tabedit(...)end + +-- find file in 'path', edit it in a new tab page +function vim.cmd.tabf(...)end + +-- find file in 'path', edit it in a new tab page +function vim.cmd.tabfind(...)end + +-- go to first tab page +function vim.cmd.tabfir(...)end + +-- go to first tab page +function vim.cmd.tabfirst(...)end + +-- go to last tab page +function vim.cmd.tabl(...)end + +-- go to last tab page +function vim.cmd.tablast(...)end + +-- move tab page to other position +function vim.cmd.tabm(...)end + +-- move tab page to other position +function vim.cmd.tabmove(...)end + +-- go to next tab page +function vim.cmd.tabn(...)end + +-- edit a file in a new tab page +function vim.cmd.tabnew(...)end + +-- go to next tab page +function vim.cmd.tabnext(...)end + +-- close all tab pages except the current one +function vim.cmd.tabo(...)end + +-- close all tab pages except the current one +function vim.cmd.tabonly(...)end + +-- go to previous tab page +function vim.cmd.tabp(...)end + +-- go to previous tab page +function vim.cmd.tabprevious(...)end + +-- go to first tab page +function vim.cmd.tabr(...)end + +-- go to first tab page +function vim.cmd.tabrewind(...)end + +-- list the tab pages and what they contain +function vim.cmd.tabs(...)end + +-- jump to tag +function vim.cmd.tag(...)end + +-- show the contents of the tag stack +function vim.cmd.tags(...)end + +-- change directory for tab page +function vim.cmd.tc(...)end + +-- change directory for tab page +function vim.cmd.tcd(...)end + +-- change directory for tab page +function vim.cmd.tch(...)end + +-- change directory for tab page +function vim.cmd.tchdir(...)end + +-- open a terminal buffer +function vim.cmd.te(...)end + +-- open a terminal buffer +function vim.cmd.terminal(...)end + +-- jump to first matching tag +function vim.cmd.tf(...)end + +-- jump to first matching tag +function vim.cmd.tfirst(...)end + +-- throw an exception +function vim.cmd.th(...)end + +-- throw an exception +function vim.cmd.throw(...)end + +-- like ":tselect", but jump directly when there +function vim.cmd.tj(...)end + +-- like ":tselect", but jump directly when there +function vim.cmd.tjump(...)end + +-- jump to last matching tag +function vim.cmd.tl(...)end + +-- jump to last matching tag +function vim.cmd.tlast(...)end + +-- add menu for |Terminal-mode| +function vim.cmd.tlm(...)end + +-- add menu for |Terminal-mode| +function vim.cmd.tlmenu(...)end + +-- like ":noremenu" but for |Terminal-mode| +function vim.cmd.tln(...)end + +-- like ":noremenu" but for |Terminal-mode| +function vim.cmd.tlnoremenu(...)end + +-- remove menu for |Terminal-mode| +function vim.cmd.tlu(...)end + +-- remove menu for |Terminal-mode| +function vim.cmd.tlunmenu(...)end + +-- define menu tooltip +function vim.cmd.tm(...)end + +-- like ":map" but for |Terminal-mode| +function vim.cmd.tma(...)end + +-- like ":map" but for |Terminal-mode| +function vim.cmd.tmap(...)end + +-- remove all mappings for |Terminal-mode| +function vim.cmd.tmapc(...)end + +-- remove all mappings for |Terminal-mode| +function vim.cmd.tmapclear(...)end + +-- define menu tooltip +function vim.cmd.tmenu(...)end + +-- jump to next matching tag +function vim.cmd.tn(...)end + +-- jump to next matching tag +function vim.cmd.tnext(...)end + +-- like ":noremap" but for |Terminal-mode| +function vim.cmd.tno(...)end + +-- like ":noremap" but for |Terminal-mode| +function vim.cmd.tnoremap(...)end + +-- make split window appear at top or far left +function vim.cmd.to(...)end + +-- make split window appear at top or far left +function vim.cmd.topleft(...)end + +-- jump to previous matching tag +function vim.cmd.tp(...)end + +-- jump to previous matching tag +function vim.cmd.tprevious(...)end + +-- jump to first matching tag +function vim.cmd.tr(...)end + +-- jump to first matching tag +function vim.cmd.trewind(...)end + +-- add or remove file from trust database +function vim.cmd.trust(...)end + +-- execute commands, abort on error or exception +function vim.cmd.try(...)end + +-- list matching tags and select one +function vim.cmd.ts(...)end + +-- list matching tags and select one +function vim.cmd.tselect(...)end + +-- remove menu tooltip +function vim.cmd.tu(...)end + +-- like ":unmap" but for |Terminal-mode| +function vim.cmd.tunma(...)end + +-- like ":unmap" but for |Terminal-mode| +function vim.cmd.tunmap(...)end + +-- remove menu tooltip +function vim.cmd.tunmenu(...)end + +-- undo last change(s) +function vim.cmd.u(...)end + +-- remove abbreviation +function vim.cmd.una(...)end + +-- remove abbreviation +function vim.cmd.unabbreviate(...)end + +-- undo last change(s) +function vim.cmd.undo(...)end + +-- join next change with previous undo block +function vim.cmd.undoj(...)end + +-- join next change with previous undo block +function vim.cmd.undojoin(...)end + +-- list leafs of the undo tree +function vim.cmd.undol(...)end + +-- list leafs of the undo tree +function vim.cmd.undolist(...)end + +-- open a window for each loaded file in the +function vim.cmd.unh(...)end + +-- open a window for each loaded file in the +function vim.cmd.unhide(...)end + +-- delete variable +function vim.cmd.unl(...)end + +-- delete variable +function vim.cmd.unlet(...)end + +-- unlock variables +function vim.cmd.unlo(...)end + +-- unlock variables +function vim.cmd.unlockvar(...)end + +-- remove mapping +function vim.cmd.unm(...)end + +-- remove mapping +function vim.cmd.unmap(...)end + +-- remove menu +function vim.cmd.unme(...)end + +-- remove menu +function vim.cmd.unmenu(...)end + +-- run a command not silently +function vim.cmd.uns(...)end + +-- run a command not silently +function vim.cmd.unsilent(...)end + +-- write buffer if modified +function vim.cmd.up(...)end + +-- write buffer if modified +function vim.cmd.update(...)end + +-- execute commands for not matching lines +function vim.cmd.v(...)end + +-- print version number and other info +function vim.cmd.ve(...)end + +-- execute command with 'verbose' set +function vim.cmd.verb(...)end + +-- execute command with 'verbose' set +function vim.cmd.verbose(...)end + +-- print version number and other info +function vim.cmd.version(...)end + +-- make following command split vertically +function vim.cmd.vert(...)end + +-- make following command split vertically +function vim.cmd.vertical(...)end + +-- execute commands for not matching lines +function vim.cmd.vglobal(...)end + +-- same as ":edit", but turns off "Ex" mode +function vim.cmd.vi(...)end + +-- edit a file read-only +function vim.cmd.vie(...)end + +-- edit a file read-only +function vim.cmd.view(...)end + +-- search for pattern in files +function vim.cmd.vim(...)end + +-- search for pattern in files +function vim.cmd.vimgrep(...)end + +-- like :vimgrep, but append to current list +function vim.cmd.vimgrepa(...)end + +-- like :vimgrep, but append to current list +function vim.cmd.vimgrepadd(...)end + +-- same as ":edit", but turns off "Ex" mode +function vim.cmd.visual(...)end + +-- overview of Normal mode commands +function vim.cmd.viu(...)end + +-- overview of Normal mode commands +function vim.cmd.viusage(...)end + +-- like ":map" but for Visual+Select mode +function vim.cmd.vm(...)end + +-- like ":map" but for Visual+Select mode +function vim.cmd.vmap(...)end + +-- remove all mappings for Visual+Select mode +function vim.cmd.vmapc(...)end + +-- remove all mappings for Visual+Select mode +function vim.cmd.vmapclear(...)end + +-- add menu for Visual+Select mode +function vim.cmd.vme(...)end + +-- add menu for Visual+Select mode +function vim.cmd.vmenu(...)end + +-- like ":noremap" but for Visual+Select mode +function vim.cmd.vn(...)end + +-- create a new empty window, vertically split +function vim.cmd.vne(...)end + +-- create a new empty window, vertically split +function vim.cmd.vnew(...)end + +-- like ":noremap" but for Visual+Select mode +function vim.cmd.vnoremap(...)end + +-- like ":noremenu" but for Visual+Select mode +function vim.cmd.vnoreme(...)end + +-- like ":noremenu" but for Visual+Select mode +function vim.cmd.vnoremenu(...)end + +-- split current window vertically +function vim.cmd.vs(...)end + +-- split current window vertically +function vim.cmd.vsplit(...)end + +-- like ":unmap" but for Visual+Select mode +function vim.cmd.vu(...)end + +-- like ":unmap" but for Visual+Select mode +function vim.cmd.vunmap(...)end + +-- remove menu for Visual+Select mode +function vim.cmd.vunme(...)end + +-- remove menu for Visual+Select mode +function vim.cmd.vunmenu(...)end + +-- write to a file +function vim.cmd.w(...)end + +-- write to a file and go to previous file in +function vim.cmd.wN(...)end + +-- write to a file and go to previous file in +function vim.cmd.wNext(...)end + +-- write all (changed) buffers +function vim.cmd.wa(...)end + +-- write all (changed) buffers +function vim.cmd.wall(...)end + +-- execute loop for as long as condition met +function vim.cmd.wh(...)end + +-- execute loop for as long as condition met +vim.cmd["while"] = function(...)end + +-- get or set window size (obsolete) +function vim.cmd.wi(...)end + +-- execute a Window (CTRL-W) command +function vim.cmd.winc(...)end + +-- execute a Window (CTRL-W) command +function vim.cmd.wincmd(...)end + +-- execute command in each window +function vim.cmd.windo(...)end + +-- get or set window position +function vim.cmd.winp(...)end + +-- get or set window position +function vim.cmd.winpos(...)end + +-- get or set window size (obsolete) +function vim.cmd.winsize(...)end + +-- write to a file and go to next file in +function vim.cmd.wn(...)end + +-- write to a file and go to next file in +function vim.cmd.wnext(...)end + +-- write to a file and go to previous file in +function vim.cmd.wp(...)end + +-- write to a file and go to previous file in +function vim.cmd.wprevious(...)end + +-- write to a file and quit window or Vim +function vim.cmd.wq(...)end + +-- write all changed buffers and quit Vim +function vim.cmd.wqa(...)end + +-- write all changed buffers and quit Vim +function vim.cmd.wqall(...)end + +-- write to a file +function vim.cmd.write(...)end + +-- write to ShaDa file +function vim.cmd.wsh(...)end + +-- write to ShaDa file +function vim.cmd.wshada(...)end + +-- write undo information to a file +function vim.cmd.wu(...)end + +-- write undo information to a file +function vim.cmd.wundo(...)end + +-- write if buffer changed and close window +function vim.cmd.x(...)end + +-- same as ":wqall" +function vim.cmd.xa(...)end + +-- same as ":wqall" +function vim.cmd.xall(...)end + +-- write if buffer changed and close window +function vim.cmd.xit(...)end + +-- like ":map" but for Visual mode +function vim.cmd.xm(...)end + +-- like ":map" but for Visual mode +function vim.cmd.xmap(...)end + +-- remove all mappings for Visual mode +function vim.cmd.xmapc(...)end + +-- remove all mappings for Visual mode +function vim.cmd.xmapclear(...)end + +-- add menu for Visual mode +function vim.cmd.xme(...)end + +-- add menu for Visual mode +function vim.cmd.xmenu(...)end + +-- like ":noremap" but for Visual mode +function vim.cmd.xn(...)end + +-- like ":noremap" but for Visual mode +function vim.cmd.xnoremap(...)end + +-- like ":noremenu" but for Visual mode +function vim.cmd.xnoreme(...)end + +-- like ":noremenu" but for Visual mode +function vim.cmd.xnoremenu(...)end + +-- like ":unmap" but for Visual mode +function vim.cmd.xu(...)end + +-- like ":unmap" but for Visual mode +function vim.cmd.xunmap(...)end + +-- remove menu for Visual mode +function vim.cmd.xunme(...)end + +-- remove menu for Visual mode +function vim.cmd.xunmenu(...)end + +-- yank lines into a register +function vim.cmd.y(...)end + +-- yank lines into a register +function vim.cmd.yank(...)end + +-- print some lines +function vim.cmd.z(...)end + diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/lpeg.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/lpeg.lua new file mode 100644 index 00000000..1f5fa6c5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/lpeg.lua @@ -0,0 +1,640 @@ +---@meta + +--- +---@meta +---The definitions are developed in this repository: https://github.com/LuaCATS/lpeg + +--- +---This type definition is based on the [HTML documentation](http://www.inf.puc-rio.br/~roberto/lpeg/) of the LPeg library. A different HTML documentation can be found at http://stevedonovan.github.io/lua-stdlibs/modules/lpeg.html. +--- +---*LPeg* is a new pattern-matching library for Lua, +---based on +---[Parsing Expression Grammars](https://bford.info/packrat/) (PEGs). +---This text is a reference manual for the library. +---For a more formal treatment of LPeg, +---as well as some discussion about its implementation, +---see +---[A Text Pattern-Matching Tool based on Parsing Expression Grammars](http://www.inf.puc-rio.br/~roberto/docs/peg.pdf). +---(You may also be interested in my +---[talk about LPeg](https://vimeo.com/1485123) +---given at the III Lua Workshop.) +--- +---Following the Snobol tradition, +---LPeg defines patterns as first-class objects. +---That is, patterns are regular Lua values +---(represented by userdata). +---The library offers several functions to create +---and compose patterns. +---With the use of metamethods, +---several of these functions are provided as infix or prefix +---operators. +---On the one hand, +---the result is usually much more verbose than the typical +---encoding of patterns using the so called +---*regular expressions* +---(which typically are not regular expressions in the formal sense). +---On the other hand, +---first-class patterns allow much better documentation +---(as it is easy to comment the code, +---to break complex definitions in smaller parts, etc.) +---and are extensible, +---as we can define new functions to create and compose patterns. +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +local lpeg = {} + +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +---@class Pattern +---@operator unm: Pattern +---@operator add(Pattern): Pattern +---@operator sub(Pattern): Pattern +---@operator mul(Pattern): Pattern +---@operator mul(Capture): Pattern +---@operator div(string): Capture +---@operator div(number): Capture +---@operator div(table): Capture +---@operator div(function): Capture +---@operator pow(number): Pattern +---@operator mod(function): nil +local Pattern = {} + +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +---@alias Capture Pattern +---@operator add(Capture): Pattern +---@operator mul(Capture): Pattern +---@operator mul(Pattern): Pattern +---@operator div(string): Capture +---@operator div(number): Capture +---@operator div(table): Capture +---@operator div(function): Capture +---@operator pow(number): Pattern + +--- +---Match the given `pattern` against the `subject` string. +--- +---If the match succeeds, +---returns the index in the subject of the first character after the match, +---or the captured values +---(if the pattern captured any value). +--- +---An optional numeric argument `init` makes the match +---start at that position in the subject string. +---As usual in Lua libraries, +---a negative value counts from the end. +--- +---Unlike typical pattern-matching functions, +---`match` works only in anchored mode; +---that is, it tries to match the pattern with a prefix of +---the given subject string (at position `init`), +---not with an arbitrary substring of the subject. +---So, if we want to find a pattern anywhere in a string, +---we must either write a loop in Lua or write a pattern that +---matches anywhere. +---This second approach is easy and quite efficient; +--- +---__Example:__ +--- +---```lua +---local pattern = lpeg.R('az') ^ 1 * -1 +---assert(pattern:match('hello') == 6) +---assert(lpeg.match(pattern, 'hello') == 6) +---assert(pattern:match('1 hello') == nil) +---``` +--- +---@param pattern Pattern +---@param subject string +---@param init? integer +--- +---@return any ... +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.match(pattern, subject, init) end + +--- +---Match the given `pattern` against the `subject` string. +--- +---If the match succeeds, +---returns the index in the subject of the first character after the match, +---or the captured values +---(if the pattern captured any value). +--- +---An optional numeric argument `init` makes the match +---start at that position in the subject string. +---As usual in Lua libraries, +---a negative value counts from the end. +--- +---Unlike typical pattern-matching functions, +---`match` works only in anchored mode; +---that is, it tries to match the pattern with a prefix of +---the given subject string (at position `init`), +---not with an arbitrary substring of the subject. +---So, if we want to find a pattern anywhere in a string, +---we must either write a loop in Lua or write a pattern that +---matches anywhere. +---This second approach is easy and quite efficient; +--- +---__Example:__ +--- +---```lua +---local pattern = lpeg.R('az') ^ 1 * -1 +---assert(pattern:match('hello') == 6) +---assert(lpeg.match(pattern, 'hello') == 6) +---assert(pattern:match('1 hello') == nil) +---``` +--- +---@param subject string +---@param init? integer +--- +---@return any ... +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function Pattern:match(subject, init) end + +--- +---Return the string `"pattern"` if the given value is a pattern, otherwise `nil`. +--- +---@return 'pattern'|nil +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.type(value) end + +--- +---Return a string with the running version of LPeg. +--- +---@return string +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.version() end + +--- +---Set a limit for the size of the backtrack stack used by LPeg to +---track calls and choices. +--- +---The default limit is `400`. +---Most well-written patterns need little backtrack levels and +---therefore you seldom need to change this limit; +---before changing it you should try to rewrite your +---pattern to avoid the need for extra space. +---Nevertheless, a few useful patterns may overflow. +---Also, with recursive grammars, +---subjects with deep recursion may also need larger limits. +--- +---@param max integer +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.setmaxstack(max) end + +--- +---Convert the given value into a proper pattern. +--- +---The following rules are applied: +--- +---* If the argument is a pattern, +---it is returned unmodified. +--- +---* If the argument is a string, +---it is translated to a pattern that matches the string literally. +--- +---* If the argument is a non-negative number `n`, +---the result is a pattern that matches exactly `n` characters. +--- +---* If the argument is a negative number `-n`, +---the result is a pattern that +---succeeds only if the input string has less than `n` characters left: +---`lpeg.P(-n)` +---is equivalent to `-lpeg.P(n)` +---(see the unary minus operation). +--- +---* If the argument is a boolean, +---the result is a pattern that always succeeds or always fails +---(according to the boolean value), +---without consuming any input. +--- +---* If the argument is a table, +---it is interpreted as a grammar +---(see Grammars). +--- +---* If the argument is a function, +---returns a pattern equivalent to a +---match-time capture over the empty string. +--- +---@param value Pattern|string|integer|boolean|table|function +--- +---@return Pattern +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.P(value) end + +--- +---Return a pattern that +---matches only if the input string at the current position +---is preceded by `patt`. +--- +---Pattern `patt` must match only strings +---with some fixed length, +---and it cannot contain captures. +--- +---Like the `and` predicate, +---this pattern never consumes any input, +---independently of success or failure. +--- +---@param pattern Pattern|string|integer|boolean|table +--- +---@return Pattern +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.B(pattern) end + +--- +---Return a pattern that matches any single character +---belonging to one of the given ranges. +--- +---Each `range` is a string `xy` of length 2, +---representing all characters with code +---between the codes of `x` and `y` +---(both inclusive). +--- +---As an example, the pattern +---`lpeg.R('09')` matches any digit, +---and `lpeg.R('az', 'AZ')` matches any ASCII letter. +--- +---__Example:__ +--- +---```lua +---local pattern = lpeg.R('az') ^ 1 * -1 +---assert(pattern:match('hello') == 6) +---``` +--- +---@param ... string +--- +---@return Pattern +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.R(...) end + +--- +---Return a pattern that matches any single character that +---appears in the given string. +---(The `S` stands for Set.) +--- +---As an example, the pattern +---`lpeg.S('+-*/')` matches any arithmetic operator. +--- +---Note that, if `s` is a character +---(that is, a string of length 1), +---then `lpeg.P(s)` is equivalent to `lpeg.S(s)` +---which is equivalent to `lpeg.R(s..s)`. +---Note also that both `lpeg.S('')` and `lpeg.R()` +---are patterns that always fail. +--- +---@param string string +--- +---@return Pattern +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.S(string) end + +--- +---Create a non-terminal (a variable) for a grammar. +--- +---This operation creates a non-terminal (a variable) +---for a grammar. +---The created non-terminal refers to the rule indexed by `v` +---in the enclosing grammar. +--- +---__Example:__ +--- +---```lua +---local b = lpeg.P({'(' * ((1 - lpeg.S '()') + lpeg.V(1)) ^ 0 * ')'}) +---assert(b:match('((string))') == 11) +---assert(b:match('(') == nil) +---``` +--- +---@param v string|integer +--- +---@return Pattern +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.V(v) end + +--- +---@class Locale +---@field alnum userdata +---@field alpha userdata +---@field cntrl userdata +---@field digit userdata +---@field graph userdata +---@field lower userdata +---@field print userdata +---@field punct userdata +---@field space userdata +---@field upper userdata +---@field xdigit userdata + +--- +---Return a table with patterns for matching some character classes +---according to the current locale. +--- +---The table has fields named +---`alnum`, +---`alpha`, +---`cntrl`, +---`digit`, +---`graph`, +---`lower`, +---`print`, +---`punct`, +---`space`, +---`upper`, and +---`xdigit`, +---each one containing a correspondent pattern. +---Each pattern matches any single character that belongs to its class. +-- +---If called with an argument `table`, +---then it creates those fields inside the given table and +---returns that table. +--- +---__Example:__ +--- +---```lua +---lpeg.locale(lpeg) +---local space = lpeg.space ^ 0 +---local name = lpeg.C(lpeg.alpha ^ 1) * space +---local sep = lpeg.S(',;') * space +---local pair = lpeg.Cg(name * '=' * space * name) * sep ^ -1 +---local list = lpeg.Cf(lpeg.Ct('') * pair ^ 0, rawset) +---local t = list:match('a=b, c = hi; next = pi') +---assert(t.a == 'b') +---assert(t.c == 'hi') +---assert(t.next == 'pi') +--- +---local locale = lpeg.locale() +---assert(type(locale.digit) == 'userdata') +---``` +--- +---@param tab? table +--- +---@return Locale +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.locale(tab) end + +--- +---Create a simple capture. +--- +---Creates a simple capture, +---which captures the substring of the subject that matches `patt`. +---The captured value is a string. +---If `patt` has other captures, +---their values are returned after this one. +--- +---__Example:__ +--- +---```lua +---local function split (s, sep) +--- sep = lpeg.P(sep) +--- local elem = lpeg.C((1 - sep) ^ 0) +--- local p = elem * (sep * elem) ^ 0 +--- return lpeg.match(p, s) +---end +--- +---local a, b, c = split('a,b,c', ',') +---assert(a == 'a') +---assert(b == 'b') +---assert(c == 'c') +---``` +--- +---@param patt Pattern|string|integer|boolean|table|function +--- +---@return Capture +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.C(patt) end + +--- +---Create an argument capture. +--- +---This pattern matches the empty string and +---produces the value given as the nth extra +---argument given in the call to `lpeg.match`. +--- +---@param n integer +--- +---@return Capture +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.Carg(n) end + +--- +---Create a back capture. +--- +---This pattern matches the empty string and +---produces the values produced by the most recent +---group capture named `name` +---(where `name` can be any Lua value). +--- +---Most recent means the last +---complete +---outermost +---group capture with the given name. +---A Complete capture means that the entire pattern +---corresponding to the capture has matched. +---An Outermost capture means that the capture is not inside +---another complete capture. +--- +---In the same way that LPeg does not specify when it evaluates captures, +---it does not specify whether it reuses +---values previously produced by the group +---or re-evaluates them. +--- +---@param name any +--- +---@return Capture +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.Cb(name) end + +--- +---Create a constant capture. +--- +---This pattern matches the empty string and +---produces all given values as its captured values. +--- +---@param ... any +--- +---@return Capture +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.Cc(...) end + +--- +---Create a fold capture. +--- +---If `patt` produces a list of captures +---C1 C2 ... Cn, +---this capture will produce the value +---`func(...func(func(C1, C2), C3)...,Cn)`, +---that is, it will fold +---(or accumulate, or reduce) +---the captures from `patt` using function `func`. +--- +---This capture assumes that `patt` should produce +---at least one capture with at least one value (of any type), +---which becomes the initial value of an accumulator. +---(If you need a specific initial value, +---you may prefix a constant capture to `patt`.) +---For each subsequent capture, +---LPeg calls `func` +---with this accumulator as the first argument and all values produced +---by the capture as extra arguments; +---the first result from this call +---becomes the new value for the accumulator. +---The final value of the accumulator becomes the captured value. +--- +---__Example:__ +--- +---```lua +---local number = lpeg.R('09') ^ 1 / tonumber +---local list = number * (',' * number) ^ 0 +---local function add(acc, newvalue) return acc + newvalue end +---local sum = lpeg.Cf(list, add) +---assert(sum:match('10,30,43') == 83) +---``` +--- +---@param patt Pattern|string|number|boolean|table|function +---@param func fun(acc, newvalue): (acc: any) +--- +---@return Capture +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.Cf(patt, func) end + +--- +---Create a group capture. +--- +---It groups all values returned by `patt` +---into a single capture. +---The group may be anonymous (if no name is given) +---or named with the given name +---(which can be any non-nil Lua value). +--- +---@param patt Pattern|string|number|boolean|table|function +---@param name? string +--- +---@return Capture +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.Cg(patt, name) end + +--- +---Create a position capture. +--- +---It matches the empty string and +---captures the position in the subject where the match occurs. +---The captured value is a number. +--- +---__Example:__ +--- +---```lua +---local I = lpeg.Cp() +---local function anywhere(p) return lpeg.P({I * p * I + 1 * lpeg.V(1)}) end +--- +---local match_start, match_end = anywhere('world'):match('hello world!') +---assert(match_start == 7) +---assert(match_end == 12) +---``` +--- +---@return Capture +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.Cp() end + +--- +---Create a substitution capture. +--- +---This function creates a substitution capture, +---which captures the substring of the subject that matches `patt`, +---with substitutions. +---For any capture inside `patt` with a value, +---the substring that matched the capture is replaced by the capture value +---(which should be a string). +---The final captured value is the string resulting from +---all replacements. +--- +---__Example:__ +--- +---```lua +---local function gsub (s, patt, repl) +--- patt = lpeg.P(patt) +--- patt = lpeg.Cs((patt / repl + 1) ^ 0) +--- return lpeg.match(patt, s) +---end +---assert(gsub('Hello, xxx!', 'xxx', 'World') == 'Hello, World!') +---``` +--- +---@param patt Pattern|string|number|boolean|table|function +--- +---@return Capture +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.Cs(patt) end + +--- +---Create a table capture. +--- +---This capture returns a table with all values from all anonymous captures +---made by `patt` inside this table in successive integer keys, +---starting at 1. +---Moreover, +---for each named capture group created by `patt`, +---the first value of the group is put into the table +---with the group name as its key. +---The captured value is only the table. +--- +---@param patt Pattern|string|number|boolean|table|function +--- +---@return Capture +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.Ct(patt) end + +--- +---Create a match-time capture. +--- +---Unlike all other captures, +---this one is evaluated immediately when a match occurs +---(even if it is part of a larger pattern that fails later). +---It forces the immediate evaluation of all its nested captures +---and then calls `function`. +--- +---The given function gets as arguments the entire subject, +---the current position (after the match of `patt`), +---plus any capture values produced by `patt`. +--- +---The first value returned by `fn` +---defines how the match happens. +---If the call returns a number, +---the match succeeds +---and the returned number becomes the new current position. +---(Assuming a subject and current position `i`, +---the returned number must be in the range `[i, len(s) + 1]`.) +---If the call returns true, +---the match succeeds without consuming any input. +---(So, to return true is equivalent to return `i`.) +---If the call returns `false`, `nil`, or no value, +---the match fails. +--- +---Any extra values returned by the function become the +---values produced by the capture. +--- +---@param patt Pattern|string|number|boolean|table|function +---@param fn fun(s: string, i: integer, ...: any): (position: boolean|number, ...: any) +--- +---@return Capture +--- +---😱 [Types](https://github.com/LuaCATS/lpeg/blob/main/library/lpeg.lua) incomplete or incorrect? 🙏 [Please contribute!](https://github.com/LuaCATS/lpeg/pulls) +function lpeg.Cmt(patt, fn) end + +return lpeg diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/lua.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/lua.lua new file mode 100644 index 00000000..2b355bcc --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/lua.lua @@ -0,0 +1,310 @@ +---@meta + + + +-- Invokes |vim-function| or |user-function| {func} with arguments {...}. +-- See also |vim.fn|. +-- Equivalent to: +-- ```lua +-- vim.fn[func]({...}) +-- ``` +--- @param func fun() +function vim.call(func, ...) end + +-- Run diff on strings {a} and {b}. Any indices returned by this function, +-- either directly or via callback arguments, are 1-based. +-- +-- Examples: +-- ```lua +-- vim.diff('a\n', 'b\nc\n') +-- -- => +-- -- @@ -1 +1,2 @@ +-- -- -a +-- -- +b +-- -- +c +-- +-- vim.diff('a\n', 'b\nc\n', {result_type = 'indices'}) +-- -- => +-- -- { +-- -- {1, 1, 1, 2} +-- -- } +-- ``` +-- Parameters: ~ +-- • {a} First string to compare +-- • {b} Second string to compare +-- • {opts} Optional parameters: +-- • `on_hunk` (callback): +-- Invoked for each hunk in the diff. Return a negative number +-- to cancel the callback for any remaining hunks. +-- Args: +-- • `start_a` (integer): Start line of hunk in {a}. +-- • `count_a` (integer): Hunk size in {a}. +-- • `start_b` (integer): Start line of hunk in {b}. +-- • `count_b` (integer): Hunk size in {b}. +-- • `result_type` (string): Form of the returned diff: +-- • "unified": (default) String in unified format. +-- • "indices": Array of hunk locations. +-- Note: This option is ignored if `on_hunk` is used. +-- • `linematch` (boolean|integer): Run linematch on the resulting hunks +-- from xdiff. When integer, only hunks upto this size in +-- lines are run through linematch. Requires `result_type = indices`, +-- ignored otherwise. +-- • `algorithm` (string): +-- Diff algorithm to use. Values: +-- • "myers" the default algorithm +-- • "minimal" spend extra time to generate the +-- smallest possible diff +-- • "patience" patience diff algorithm +-- • "histogram" histogram diff algorithm +-- • `ctxlen` (integer): Context length +-- • `interhunkctxlen` (integer): +-- Inter hunk context length +-- • `ignore_whitespace` (boolean): +-- Ignore whitespace +-- • `ignore_whitespace_change` (boolean): +-- Ignore whitespace change +-- • `ignore_whitespace_change_at_eol` (boolean) +-- Ignore whitespace change at end-of-line. +-- • `ignore_cr_at_eol` (boolean) +-- Ignore carriage return at end-of-line +-- • `ignore_blank_lines` (boolean) +-- Ignore blank lines +-- • `indent_heuristic` (boolean): +-- Use the indent heuristic for the internal +-- diff library. +-- +-- Return: ~ +-- See {opts.result_type}. nil if {opts.on_hunk} is given. +--- @param opts table +function vim.diff(a, b, opts) end + +-- The result is a String, which is the text {str} converted from +-- encoding {from} to encoding {to}. When the conversion fails `nil` is +-- returned. When some characters could not be converted they +-- are replaced with "?". +-- The encoding names are whatever the iconv() library function +-- can accept, see ":Man 3 iconv". +-- +-- Parameters: ~ +-- • {str} (string) Text to convert +-- • {from} (string) Encoding of {str} +-- • {to} (string) Target encoding +-- +-- Returns: ~ +-- Converted string if conversion succeeds, `nil` otherwise. +--- @param str string +--- @param from number +--- @param to number +--- @param opts? table +function vim.iconv(str, from, to, opts) end + +-- Returns true if the code is executing as part of a "fast" event handler, +-- where most of the API is disabled. These are low-level events (e.g. +-- |lua-loop-callbacks|) which can be invoked whenever Nvim polls for input. +-- When this is `false` most API functions are callable (but may be subject +-- to other restrictions such as |textlock|). +function vim.in_fast_event() end + +-- Decodes (or "unpacks") the JSON-encoded {str} to a Lua object. +-- +-- {opts} is a table with the key `luanil = { object: bool, array: bool }` +-- that controls whether `null` in JSON objects or arrays should be converted +-- to Lua `nil` instead of `vim.NIL`. +--- @param str string +--- @param opts? table +function vim.json.decode(str, opts) end + +-- Encodes (or "packs") Lua object {obj} as JSON in a Lua string. +function vim.json.encode(obj) end + +-- Decodes (or "unpacks") the msgpack-encoded {str} to a Lua object. +--- @param str string +function vim.mpack.decode(str) end + +-- Encodes (or "packs") Lua object {obj} as msgpack in a Lua string. +function vim.mpack.encode(obj) end + +-- Parse the Vim regex {re} and return a regex object. Regexes are "magic" +-- and case-sensitive by default, regardless of 'magic' and 'ignorecase'. +-- They can be controlled with flags, see |/magic| and |/ignorecase|. +function vim.regex(re) end + +-- Sends {event} to {channel} via |RPC| and returns immediately. If {channel} +-- is 0, the event is broadcast to all channels. +-- +-- This function also works in a fast callback |lua-loop-callbacks|. +--- @param args? any[] +--- @param ...? any +function vim.rpcnotify(channel, method, args, ...) end + +-- Sends a request to {channel} to invoke {method} via |RPC| and blocks until +-- a response is received. +-- +-- Note: NIL values as part of the return value is represented as |vim.NIL| +-- special value +--- @param args? any[] +--- @param ...? any +function vim.rpcrequest(channel, method, args, ...) end + +-- Schedules {callback} to be invoked soon by the main event-loop. Useful +-- to avoid |textlock| or other temporary restrictions. +--- @param callback fun() +function vim.schedule(callback) end + +-- Check {str} for spelling errors. Similar to the Vimscript function +-- |spellbadword()|. +-- +-- Note: The behaviour of this function is dependent on: 'spelllang', +-- 'spellfile', 'spellcapcheck' and 'spelloptions' which can all be local to +-- the buffer. Consider calling this with |nvim_buf_call()|. +-- +-- Example: +-- ```lua +-- vim.spell.check("the quik brown fox") +-- -- => +-- -- { +-- -- {'quik', 'bad', 5} +-- -- } +-- ``` +-- Parameters: ~ +-- • {str} String to spell check. +-- +-- Return: ~ +-- List of tuples with three items: +-- - The badly spelled word. +-- - The type of the spelling error: +-- "bad" spelling mistake +-- "rare" rare word +-- "local" word only valid in another region +-- "caps" word should start with Capital +-- - The position in {str} where the word begins. +--- @param str string +function vim.spell.check(str) end + +-- Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not +-- supplied, it defaults to false (use UTF-32). Returns the byte index. +-- +-- Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. +-- An {index} in the middle of a UTF-16 sequence is rounded upwards to +-- the end of that sequence. +--- @param str string +--- @param index number +--- @param use_utf16? any +function vim.str_byteindex(str, index, use_utf16) end + +-- Convert byte index to UTF-32 and UTF-16 indices. If {index} is not +-- supplied, the length of the string is used. All indices are zero-based. +-- Returns two values: the UTF-32 and UTF-16 indices respectively. +-- +-- Embedded NUL bytes are treated as terminating the string. Invalid UTF-8 +-- bytes, and embedded surrogates are counted as one code point each. An +-- {index} in the middle of a UTF-8 sequence is rounded upwards to the end of +-- that sequence. +--- @param str string +--- @param index? number +function vim.str_utfindex(str, index) end + +-- Compares strings case-insensitively. Returns 0, 1 or -1 if strings are +-- equal, {a} is greater than {b} or {a} is lesser than {b}, respectively. +function vim.stricmp(a, b) end + +-- Attach to ui events, similar to |nvim_ui_attach()| but receive events +-- as lua callback. Can be used to implement screen elements like +-- popupmenu or message handling in lua. +-- +-- {options} should be a dictionary-like table, where `ext_...` options should +-- be set to true to receive events for the respective external element. +-- +-- {callback} receives event name plus additional parameters. See |ui-popupmenu| +-- and the sections below for event format for respective events. +-- +-- WARNING: This api is considered experimental. Usability will vary for +-- different screen elements. In particular `ext_messages` behavior is subject +-- to further changes and usability improvements. This is expected to be +-- used to handle messages when setting 'cmdheight' to zero (which is +-- likewise experimental). +-- +-- Example (stub for a |ui-popupmenu| implementation): +-- ```lua +-- +-- ns = vim.api.nvim_create_namespace('my_fancy_pum') +-- +-- vim.ui_attach(ns, {ext_popupmenu=true}, function(event, ...) +-- if event == "popupmenu_show" then +-- local items, selected, row, col, grid = ... +-- print("display pum ", #items) +-- elseif event == "popupmenu_select" then +-- local selected = ... +-- print("selected", selected) +-- elseif event == "popupmenu_hide" then +-- print("FIN") +-- end +-- end) +-- ``` +--- @param ns number +--- @param options table +--- @param callback fun() +function vim.ui_attach(ns, options, callback) end + +-- Detach a callback previously attached with |vim.ui_attach()| for the +-- given namespace {ns}. +--- @param ns number +function vim.ui_detach(ns) end + +-- Wait for {time} in milliseconds until {callback} returns `true`. +-- +-- Executes {callback} immediately and at approximately {interval} +-- milliseconds (default 200). Nvim still processes other events during +-- this time. +-- +-- Parameters: ~ +-- • {time} Number of milliseconds to wait +-- • {callback} Optional callback. Waits until {callback} returns true +-- • {interval} (Approximate) number of milliseconds to wait between polls +-- • {fast_only} If true, only |api-fast| events will be processed. +-- If called from while in an |api-fast| event, will +-- automatically be set to `true`. +-- +-- Returns: ~ +-- If {callback} returns `true` during the {time}: +-- `true, nil` +-- +-- If {callback} never returns `true` during the {time}: +-- `false, -1` +-- +-- If {callback} is interrupted during the {time}: +-- `false, -2` +-- +-- If {callback} errors, the error is raised. +-- +-- Examples: +-- ```lua +-- +-- --- +-- -- Wait for 100 ms, allowing other events to process +-- vim.wait(100, function() end) +-- +-- --- +-- -- Wait for 100 ms or until global variable set. +-- vim.wait(100, function() return vim.g.waiting_for_var end) +-- +-- --- +-- -- Wait for 1 second or until global variable set, checking every ~500 ms +-- vim.wait(1000, function() return vim.g.waiting_for_var end, 500) +-- +-- --- +-- -- Schedule a function to set a value in 100ms +-- vim.defer_fn(function() vim.g.timer_result = true end, 100) +-- +-- -- Would wait ten seconds if results blocked. Actually only waits 100 ms +-- if vim.wait(10000, function() return vim.g.timer_result end) then +-- print('Only waiting a little bit of time!') +-- end +-- ``` +--- @param time number +--- @param condition? fun(): boolean +--- @param interval? number +--- @param fast_only? boolean +--- @return boolean, nil|number +function vim.wait(time, condition, interval, fast_only) end + diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.1.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.1.lua new file mode 100644 index 00000000..9179a90d --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.1.lua @@ -0,0 +1,4400 @@ +---@meta + +-- `'cursorline'` `'cul'` boolean (default off) +-- local to window +-- Highlight the text line of the cursor with CursorLine |hl-CursorLine|. +-- Useful to easily spot the cursor. Will make screen redrawing slower. +-- When Visual mode is active the highlighting isn't used to make it +-- easier to see the selected text. +vim.wo.cursorline = false +vim.wo.cul = vim.wo.cursorline +-- `'cursorlineopt'` `'culopt'` string (default: "number,line") +-- local to window +-- Comma-separated list of settings for how `'cursorline'` is displayed. +-- Valid values: +-- "line" Highlight the text line of the cursor with +-- CursorLine |hl-CursorLine|. +-- "screenline" Highlight only the screen line of the cursor with +-- CursorLine |hl-CursorLine|. +-- "number" Highlight the line number of the cursor with +-- CursorLineNr |hl-CursorLineNr|. +-- +-- Special value: +-- "both" Alias for the values "line,number". +-- +-- "line" and "screenline" cannot be used together. +vim.wo.cursorlineopt = "both" +vim.wo.culopt = vim.wo.cursorlineopt +-- `'diff'` boolean (default off) +-- local to window +-- Join the current window in the group of windows that shows differences +-- between files. See |diff-mode|. +vim.wo.diff = false +-- `'fillchars'` `'fcs'` string (default "") +-- global or local to window |global-local| +-- Characters to fill the statuslines, vertical separators and special +-- lines in the window. +-- It is a comma-separated list of items. Each item has a name, a colon +-- and the value of that item: +-- +-- item default Used for ~ +-- stl ' ' or `'^'` statusline of the current window +-- stlnc ' ' or `'='` statusline of the non-current windows +-- wbr ' ' window bar +-- horiz `'─'` or `'-'` horizontal separators |:split| +-- horizup `'┴'` or `'-'` upwards facing horizontal separator +-- horizdown `'┬'` or `'-'` downwards facing horizontal separator +-- vert `'│'` or `'|'` vertical separators |:vsplit| +-- vertleft `'┤'` or `'|'` left facing vertical separator +-- vertright `'├'` or `'|'` right facing vertical separator +-- verthoriz `'┼'` or `'+'` overlapping vertical and horizontal +-- separator +-- fold `'·'` or `'-'` filling `'foldtext'` +-- foldopen `'-'` mark the beginning of a fold +-- foldclose `'+'` show a closed fold +-- foldsep `'│'` or `'|'` open fold middle marker +-- diff `'-'` deleted lines of the `'diff'` option +-- msgsep ' ' message separator `'display'` +-- eob `'~'` empty lines at the end of a buffer +-- lastline `'@'` `'display'` contains lastline/truncate +-- +-- Any one that is omitted will fall back to the default. For "stl" and +-- "stlnc" the space will be used when there is highlighting, `'^'` or `'='` +-- otherwise. +-- +-- Note that "horiz", "horizup", "horizdown", "vertleft", "vertright" and +-- "verthoriz" are only used when `'laststatus'` is 3, since only vertical +-- window separators are used otherwise. +-- +-- If `'ambiwidth'` is "double" then "horiz", "horizup", "horizdown", +-- "vert", "vertleft", "vertright", "verthoriz", "foldsep" and "fold" +-- default to single-byte alternatives. +-- +-- Example: > +-- :set fillchars=stl:^,stlnc:=,vert:│,fold:·,diff:- +-- < This is similar to the default, except that these characters will also +-- be used when there is highlighting. +-- +-- For the "stl", "stlnc", "foldopen", "foldclose" and "foldsep" items +-- single-byte and multibyte characters are supported. But double-width +-- characters are not supported. +-- +-- The highlighting used for these items: +-- item highlight group ~ +-- stl StatusLine |hl-StatusLine| +-- stlnc StatusLineNC |hl-StatusLineNC| +-- wbr WinBar |hl-WinBar| or |hl-WinBarNC| +-- horiz WinSeparator |hl-WinSeparator| +-- horizup WinSeparator |hl-WinSeparator| +-- horizdown WinSeparator |hl-WinSeparator| +-- vert WinSeparator |hl-WinSeparator| +-- vertleft WinSeparator |hl-WinSeparator| +-- vertright WinSeparator |hl-WinSeparator| +-- verthoriz WinSeparator |hl-WinSeparator| +-- fold Folded |hl-Folded| +-- diff DiffDelete |hl-DiffDelete| +-- eob EndOfBuffer |hl-EndOfBuffer| +-- lastline NonText |hl-NonText| +vim.wo.fillchars = "" +vim.wo.fcs = vim.wo.fillchars +-- `'foldcolumn'` `'fdc'` string (default "0") +-- local to window +-- When and how to draw the foldcolumn. Valid values are: +-- "auto": resize to the minimum amount of folds to display. +-- "auto:[1-9]": resize to accommodate multiple folds up to the +-- selected level +-- 0: to disable foldcolumn +-- "[1-9]": to display a fixed number of columns +-- See |folding|. +vim.wo.foldcolumn = "0" +vim.wo.fdc = vim.wo.foldcolumn +-- `'foldenable'` `'fen'` boolean (default on) +-- local to window +-- When off, all folds are open. This option can be used to quickly +-- switch between showing all text unfolded and viewing the text with +-- folds (including manually opened or closed folds). It can be toggled +-- with the |zi| command. The `'foldcolumn'` will remain blank when +-- `'foldenable'` is off. +-- This option is set by commands that create a new fold or close a fold. +-- See |folding|. +vim.wo.foldenable = true +vim.wo.fen = vim.wo.foldenable +-- `'foldexpr'` `'fde'` string (default: "0") +-- local to window +-- The expression used for when `'foldmethod'` is "expr". It is evaluated +-- for each line to obtain its fold level. See |fold-expr|. +-- +-- The expression will be evaluated in the |sandbox| if set from a +-- modeline, see |sandbox-option|. +-- This option can't be set from a |modeline| when the `'diff'` option is +-- on or the `'modelineexpr'` option is off. +-- +-- It is not allowed to change text or jump to another window while +-- evaluating `'foldexpr'` |textlock|. +vim.wo.foldexpr = "0" +vim.wo.fde = vim.wo.foldexpr +-- `'foldignore'` `'fdi'` string (default: "#") +-- local to window +-- Used only when `'foldmethod'` is "indent". Lines starting with +-- characters in `'foldignore'` will get their fold level from surrounding +-- lines. White space is skipped before checking for this character. +-- The default "#" works well for C programs. See |fold-indent|. +vim.wo.foldignore = "#" +vim.wo.fdi = vim.wo.foldignore +-- `'foldlevel'` `'fdl'` number (default: 0) +-- local to window +-- Sets the fold level: Folds with a higher level will be closed. +-- Setting this option to zero will close all folds. Higher numbers will +-- close fewer folds. +-- This option is set by commands like |zm|, |zM| and |zR|. +-- See |fold-foldlevel|. +vim.wo.foldlevel = 0 +vim.wo.fdl = vim.wo.foldlevel +-- `'foldmarker'` `'fmr'` string (default: "{{{,}}}") +-- local to window +-- The start and end marker used when `'foldmethod'` is "marker". There +-- must be one comma, which separates the start and end marker. The +-- marker is a literal string (a regular expression would be too slow). +-- See |fold-marker|. +vim.wo.foldmarker = "{{{,}}}" +vim.wo.fmr = vim.wo.foldmarker +-- `'foldmethod'` `'fdm'` string (default: "manual") +-- local to window +-- The kind of folding used for the current window. Possible values: +-- |fold-manual| manual Folds are created manually. +-- |fold-indent| indent Lines with equal indent form a fold. +-- |fold-expr| expr `'foldexpr'` gives the fold level of a line. +-- |fold-marker| marker Markers are used to specify folds. +-- |fold-syntax| syntax Syntax highlighting items specify folds. +-- |fold-diff| diff Fold text that is not changed. +vim.wo.foldmethod = "manual" +vim.wo.fdm = vim.wo.foldmethod +-- `'foldminlines'` `'fml'` number (default: 1) +-- local to window +-- Sets the number of screen lines above which a fold can be displayed +-- closed. Also for manually closed folds. With the default value of +-- one a fold can only be closed if it takes up two or more screen lines. +-- Set to zero to be able to close folds of just one screen line. +-- Note that this only has an effect on what is displayed. After using +-- "zc" to close a fold, which is displayed open because it's smaller +-- than `'foldminlines'` , a following "zc" may close a containing fold. +vim.wo.foldminlines = 1 +vim.wo.fml = vim.wo.foldminlines +-- `'foldnestmax'` `'fdn'` number (default: 20) +-- local to window +-- Sets the maximum nesting of folds for the "indent" and "syntax" +-- methods. This avoids that too many folds will be created. Using more +-- than 20 doesn't work, because the internal limit is 20. +vim.wo.foldnestmax = 20 +vim.wo.fdn = vim.wo.foldnestmax +-- `'foldtext'` `'fdt'` string (default: "foldtext()") +-- local to window +-- An expression which is used to specify the text displayed for a closed +-- fold. See |fold-foldtext|. +-- +-- The expression will be evaluated in the |sandbox| if set from a +-- modeline, see |sandbox-option|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- It is not allowed to change text or jump to another window while +-- evaluating `'foldtext'` |textlock|. +vim.wo.foldtext = "foldtext()" +vim.wo.fdt = vim.wo.foldtext +-- `'linebreak'` `'lbr'` boolean (default off) +-- local to window +-- If on, Vim will wrap long lines at a character in `'breakat'` rather +-- than at the last character that fits on the screen. Unlike +-- `'wrapmargin'` and `'textwidth'` , this does not insert s in the file, +-- it only affects the way the file is displayed, not its contents. +-- If `'breakindent'` is set, line is visually indented. Then, the value +-- of `'showbreak'` is used to put in front of wrapped lines. This option +-- is not used when the `'wrap'` option is off. +-- Note that characters after an are mostly not displayed +-- with the right amount of white space. +vim.wo.linebreak = false +vim.wo.lbr = vim.wo.linebreak +-- `'list'` boolean (default off) +-- local to window +-- List mode: By default, show tabs as ">", trailing spaces as "-", and +-- non-breakable space characters as "+". Useful to see the difference +-- between tabs and spaces and for trailing blanks. Further changed by +-- the `'listchars'` option. +-- +-- The cursor is displayed at the start of the space a Tab character +-- occupies, not at the end as usual in Normal mode. To get this cursor +-- position while displaying Tabs with spaces, use: > +-- :set list lcs=tab:\ \ +-- < +-- Note that list mode will also affect formatting (set with `'textwidth'` +-- or `'wrapmargin'` ) when `'cpoptions'` includes `'L'` . See `'listchars'` for +-- changing the way tabs are displayed. +vim.wo.list = false +-- `'listchars'` `'lcs'` string (default: "tab:> ,trail:-,nbsp:+") +-- global or local to window |global-local| +-- Strings to use in `'list'` mode and for the |:list| command. It is a +-- comma-separated list of string settings. +-- +-- +-- eol:c Character to show at the end of each line. When +-- omitted, there is no extra character at the end of the +-- line. +-- +-- tab:xy[z] Two or three characters to be used to show a tab. +-- The third character is optional. +-- +-- tab:xy The `'x'` is always used, then `'y'` as many times as will +-- fit. Thus "tab:>-" displays: > +-- > +-- >- +-- >-- +-- etc. +-- < +-- tab:xyz The `'z'` is always used, then `'x'` is prepended, and +-- then `'y'` is used as many times as will fit. Thus +-- "tab:<->" displays: > +-- > +-- <> +-- <-> +-- <--> +-- etc. +-- < +-- When "tab:" is omitted, a tab is shown as ^I. +-- +-- space:c Character to show for a space. When omitted, spaces +-- are left blank. +-- +-- multispace:c... +-- One or more characters to use cyclically to show for +-- multiple consecutive spaces. Overrides the "space" +-- setting, except for single spaces. When omitted, the +-- "space" setting is used. For example, +-- `:set listchars=multispace:---+` shows ten consecutive +-- spaces as: > +-- ---+---+-- +-- < +-- +-- lead:c Character to show for leading spaces. When omitted, +-- leading spaces are blank. Overrides the "space" and +-- "multispace" settings for leading spaces. You can +-- combine it with "tab:", for example: > +-- :set listchars+=tab:>-,lead:. +-- < +-- +-- leadmultispace:c... +-- Like the |lcs-multispace| value, but for leading +-- spaces only. Also overrides |lcs-lead| for leading +-- multiple spaces. +-- `:set listchars=leadmultispace:---+` shows ten +-- consecutive leading spaces as: > +-- ---+---+--XXX +-- < +-- Where "XXX" denotes the first non-blank characters in +-- the line. +-- +-- trail:c Character to show for trailing spaces. When omitted, +-- trailing spaces are blank. Overrides the "space" and +-- "multispace" settings for trailing spaces. +-- +-- extends:c Character to show in the last column, when `'wrap'` is +-- off and the line continues beyond the right of the +-- screen. +-- +-- precedes:c Character to show in the first visible column of the +-- physical line, when there is text preceding the +-- character visible in the first column. +-- +-- conceal:c Character to show in place of concealed text, when +-- `'conceallevel'` is set to 1. A space when omitted. +-- +-- nbsp:c Character to show for a non-breakable space character +-- (0xA0 (160 decimal) and U+202F). Left blank when +-- omitted. +-- +-- The characters `':'` and `','` should not be used. UTF-8 characters can +-- be used. All characters must be single width. +-- +-- Each character can be specified as hex: > +-- set listchars=eol:\\x24 +-- set listchars=eol:\\u21b5 +-- set listchars=eol:\\U000021b5 +-- < Note that a double backslash is used. The number of hex characters +-- must be exactly 2 for \\x, 4 for \\u and 8 for \\U. +-- +-- Examples: > +-- :set lcs=tab:>-,trail:- +-- :set lcs=tab:>-,eol:<,nbsp:% +-- :set lcs=extends:>,precedes:< +-- < |hl-NonText| highlighting will be used for "eol", "extends" and +-- "precedes". |hl-Whitespace| for "nbsp", "space", "tab", "multispace", +-- "lead" and "trail". +vim.wo.listchars = "tab:> ,trail:-,nbsp:+" +vim.wo.lcs = vim.wo.listchars +-- `'number'` `'nu'` boolean (default off) +-- local to window +-- Print the line number in front of each line. When the `'n'` option is +-- excluded from `'cpoptions'` a wrapped line will not use the column of +-- line numbers. +-- Use the `'numberwidth'` option to adjust the room for the line number. +-- When a long, wrapped line doesn't start with the first character, `'-'` +-- characters are put before the number. +-- For highlighting see |hl-LineNr|, |hl-CursorLineNr|, and the +-- |:sign-define| "numhl" argument. +-- +-- The `'relativenumber'` option changes the displayed number to be +-- relative to the cursor. Together with `'number'` there are these +-- four combinations (cursor in line 3): +-- +-- `'nonu'` `'nu'` `'nonu'` `'nu'` +-- `'nornu'` `'nornu'` `'rnu'` `'rnu'` +-- > +-- |apple | 1 apple | 2 apple | 2 apple +-- |pear | 2 pear | 1 pear | 1 pear +-- |nobody | 3 nobody | 0 nobody |3 nobody +-- |there | 4 there | 1 there | 1 there +-- < +vim.wo.number = false +vim.wo.nu = vim.wo.number +-- `'numberwidth'` `'nuw'` number (default: 4) +-- local to window +-- Minimal number of columns to use for the line number. Only relevant +-- when the `'number'` or `'relativenumber'` option is set or printing lines +-- with a line number. Since one space is always between the number and +-- the text, there is one less character for the number itself. +-- The value is the minimum width. A bigger width is used when needed to +-- fit the highest line number in the buffer respectively the number of +-- rows in the window, depending on whether `'number'` or `'relativenumber'` +-- is set. Thus with the Vim default of 4 there is room for a line number +-- up to 999. When the buffer has 1000 lines five columns will be used. +-- The minimum value is 1, the maximum value is 20. +vim.wo.numberwidth = 4 +vim.wo.nuw = vim.wo.numberwidth +-- `'previewwindow'` `'pvw'` boolean (default off) +-- local to window +-- Identifies the preview window. Only one window can have this option +-- set. It's normally not set directly, but by using one of the commands +-- |:ptag|, |:pedit|, etc. +vim.wo.previewwindow = false +vim.wo.pvw = vim.wo.previewwindow +-- `'relativenumber'` `'rnu'` boolean (default off) +-- local to window +-- Show the line number relative to the line with the cursor in front of +-- each line. Relative line numbers help you use the |count| you can +-- precede some vertical motion commands (e.g. j k + -) with, without +-- having to calculate it yourself. Especially useful in combination with +-- other commands (e.g. y d c < > gq gw =). +-- When the `'n'` option is excluded from `'cpoptions'` a wrapped +-- line will not use the column of line numbers. +-- The `'numberwidth'` option can be used to set the room used for the line +-- number. +-- When a long, wrapped line doesn't start with the first character, `'-'` +-- characters are put before the number. +-- See |hl-LineNr| and |hl-CursorLineNr| for the highlighting used for +-- the number. +-- +-- The number in front of the cursor line also depends on the value of +-- `'number'` , see |number_relativenumber| for all combinations of the two +-- options. +vim.wo.relativenumber = false +vim.wo.rnu = vim.wo.relativenumber +-- `'rightleft'` `'rl'` boolean (default off) +-- local to window +-- When on, display orientation becomes right-to-left, i.e., characters +-- that are stored in the file appear from the right to the left. +-- Using this option, it is possible to edit files for languages that +-- are written from the right to the left such as Hebrew and Arabic. +-- This option is per window, so it is possible to edit mixed files +-- simultaneously, or to view the same file in both ways (this is +-- useful whenever you have a mixed text file with both right-to-left +-- and left-to-right strings so that both sets are displayed properly +-- in different windows). Also see |rileft.txt|. +vim.wo.rightleft = false +vim.wo.rl = vim.wo.rightleft +-- `'rightleftcmd'` `'rlc'` string (default "search") +-- local to window +-- Each word in this option enables the command line editing to work in +-- right-to-left mode for a group of commands: +-- +-- search "/" and "?" commands +-- +-- This is useful for languages such as Hebrew, Arabic and Farsi. +-- The `'rightleft'` option must be set for `'rightleftcmd'` to take effect. +vim.wo.rightleftcmd = "search" +vim.wo.rlc = vim.wo.rightleftcmd +-- `'scroll'` `'scr'` number (default: half the window height) +-- local to window +-- Number of lines to scroll with CTRL-U and CTRL-D commands. Will be +-- set to half the number of lines in the window when the window size +-- changes. This may happen when enabling the |status-line| or +-- `'tabline'` option after setting the `'scroll'` option. +-- If you give a count to the CTRL-U or CTRL-D command it will +-- be used as the new value for `'scroll'` . Reset to half the window +-- height with ":set scroll=0". +vim.wo.scroll = 0 +vim.wo.scr = vim.wo.scroll +-- `'scrollbind'` `'scb'` boolean (default off) +-- local to window +-- See also |scroll-binding|. When this option is set, the current +-- window scrolls as other scrollbind windows (windows that also have +-- this option set) scroll. This option is useful for viewing the +-- differences between two versions of a file, see `'diff'` . +-- See |`'scrollopt'` | for options that determine how this option should be +-- interpreted. +-- This option is mostly reset when splitting a window to edit another +-- file. This means that ":split | edit file" results in two windows +-- with scroll-binding, but ":split file" does not. +vim.wo.scrollbind = false +vim.wo.scb = vim.wo.scrollbind +-- `'scrolloff'` `'so'` number (default 0) +-- global or local to window |global-local| +-- Minimal number of screen lines to keep above and below the cursor. +-- This will make some context visible around where you are working. If +-- you set it to a very large value (999) the cursor line will always be +-- in the middle of the window (except at the start or end of the file or +-- when long lines wrap). +-- After using the local value, go back the global value with one of +-- these two: > +-- setlocal scrolloff< +-- setlocal scrolloff=-1 +-- < For scrolling horizontally see `'sidescrolloff'` . +vim.wo.scrolloff = 0 +vim.wo.so = vim.wo.scrolloff +-- `'showbreak'` `'sbr'` string (default "") +-- global or local to window |global-local| +-- String to put at the start of lines that have been wrapped. Useful +-- values are "> " or "+++ ": > +-- :set showbreak=>\ +-- < Note the backslash to escape the trailing space. It's easier like +-- this: > +-- :let &showbreak = '+++ ' +-- < Only printable single-cell characters are allowed, excluding and +-- comma (in a future version the comma might be used to separate the +-- part that is shown at the end and at the start of a line). +-- The |hl-NonText| highlight group determines the highlighting. +-- Note that tabs after the showbreak will be displayed differently. +-- If you want the `'showbreak'` to appear in between line numbers, add the +-- "n" flag to `'cpoptions'` . +-- A window-local value overrules a global value. If the global value is +-- set and you want no value in the current window use NONE: > +-- :setlocal showbreak=NONE +-- < +vim.wo.showbreak = "" +vim.wo.sbr = vim.wo.showbreak +-- `'sidescrolloff'` `'siso'` number (default 0) +-- global or local to window |global-local| +-- The minimal number of screen columns to keep to the left and to the +-- right of the cursor if `'nowrap'` is set. Setting this option to a +-- value greater than 0 while having |`'sidescroll'` | also at a non-zero +-- value makes some context visible in the line you are scrolling in +-- horizontally (except at beginning of the line). Setting this option +-- to a large value (like 999) has the effect of keeping the cursor +-- horizontally centered in the window, as long as one does not come too +-- close to the beginning of the line. +-- After using the local value, go back the global value with one of +-- these two: > +-- setlocal sidescrolloff< +-- setlocal sidescrolloff=-1 +-- < +-- Example: Try this together with `'sidescroll'` and `'listchars'` as +-- in the following example to never allow the cursor to move +-- onto the "extends" character: > +-- +-- :set nowrap sidescroll=1 listchars=extends:>,precedes:< +-- :set sidescrolloff=1 +-- < +vim.wo.sidescrolloff = 0 +vim.wo.siso = vim.wo.sidescrolloff +-- `'signcolumn'` `'scl'` string (default "auto") +-- local to window +-- When and how to draw the signcolumn. Valid values are: +-- "auto" only when there is a sign to display +-- "auto:[1-9]" resize to accommodate multiple signs up to the +-- given number (maximum 9), e.g. "auto:4" +-- "auto:[1-8]-[2-9]" +-- resize to accommodate multiple signs up to the +-- given maximum number (maximum 9) while keeping +-- at least the given minimum (maximum 8) fixed +-- space. The minimum number should always be less +-- than the maximum number, e.g. "auto:2-5" +-- "no" never +-- "yes" always +-- "yes:[1-9]" always, with fixed space for signs up to the given +-- number (maximum 9), e.g. "yes:3" +-- "number" display signs in the `'number'` column. If the number +-- column is not present, then behaves like "auto". +-- +-- Note regarding "orphaned signs": with signcolumn numbers higher than +-- 1, deleting lines will also remove the associated signs automatically, +-- in contrast to the default Vim behavior of keeping and grouping them. +-- This is done in order for the signcolumn appearance not appear weird +-- during line deletion. +vim.wo.signcolumn = "auto" +vim.wo.scl = vim.wo.signcolumn +-- `'spell'` boolean (default off) +-- local to window +-- When on spell checking will be done. See |spell|. +-- The languages are specified with `'spelllang'` . +vim.wo.spell = false +-- `'statuscolumn'` `'stc'` string (default: empty) +-- local to window +-- EXPERIMENTAL +-- When non-empty, this option determines the content of the area to the +-- side of a window, normally containing the fold, sign and number columns. +-- The format of this option is like that of `'statusline'` . +-- +-- Some of the items from the `'statusline'` format are different for +-- `'statuscolumn'` : +-- +-- %l line number of currently drawn line +-- %r relative line number of currently drawn line +-- %s sign column for currently drawn line +-- %C fold column for currently drawn line +-- +-- NOTE: To draw the sign and fold columns, their items must be included in +-- `'statuscolumn'` . Even when they are not included, the status column width +-- will adapt to the `'signcolumn'` and `'foldcolumn'` width. +-- +-- The |v:lnum| variable holds the line number to be drawn. +-- The |v:relnum| variable holds the relative line number to be drawn. +-- The |v:virtnum| variable is negative when drawing virtual lines, zero +-- when drawing the actual buffer line, and positive when +-- drawing the wrapped part of a buffer line. +-- +-- NOTE: The %@ click execute function item is supported as well but the +-- specified function will be the same for each row in the same column. +-- It cannot be switched out through a dynamic `'statuscolumn'` format, the +-- handler should be written with this in mind. +-- +-- Examples: >vim +-- " Relative number with bar separator and click handlers: +-- :set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T +-- +-- " Right aligned relative cursor line number: +-- :let &stc='%=%{v:relnum?v:relnum:v:lnum} ' +-- +-- " Line numbers in hexadecimal for non wrapped part of lines: +-- :let &stc='%=%{v:virtnum>0?"":printf("%x",v:lnum)} ' +-- +-- " Human readable line numbers with thousands separator: +-- :let &stc=`'%{substitute(v:lnum,"\\d\\zs\\ze\\'` +-- . `'%(\\d\\d\\d\\)\\+$",",","g")}'` +-- +-- " Both relative and absolute line numbers with different +-- " highlighting for odd and even relative numbers: +-- :let &stc=`'%#NonText#%{&nu?v:lnum:""}'` . +-- '%=%{&rnu&&(v:lnum%2)?"\ ".v:relnum:""}' . +-- '%#LineNr#%{&rnu&&!(v:lnum%2)?"\ ".v:relnum:""}' +-- +-- < WARNING: this expression is evaluated for each screen line so defining +-- an expensive expression can negatively affect render performance. +vim.wo.statuscolumn = "" +vim.wo.stc = vim.wo.statuscolumn +-- `'statusline'` `'stl'` string (default empty) +-- global or local to window |global-local| +-- When non-empty, this option determines the content of the status line. +-- Also see |status-line|. +-- +-- The option consists of printf style `'%'` items interspersed with +-- normal text. Each status line item is of the form: +-- %-0{minwid}.{maxwid}{item} +-- All fields except the {item} are optional. A single percent sign can +-- be given as "%%". +-- +-- When the option starts with "%!" then it is used as an expression, +-- evaluated and the result is used as the option value. Example: > +-- :set statusline=%!MyStatusLine() +-- < The variable will be set to the |window-ID| of the +-- window that the status line belongs to. +-- The result can contain %{} items that will be evaluated too. +-- Note that the "%!" expression is evaluated in the context of the +-- current window and buffer, while %{} items are evaluated in the +-- context of the window that the statusline belongs to. +-- +-- When there is error while evaluating the option then it will be made +-- empty to avoid further errors. Otherwise screen updating would loop. +-- When the result contains unprintable characters the result is +-- unpredictable. +-- +-- Note that the only effect of `'ruler'` when this option is set (and +-- `'laststatus'` is 2 or 3) is controlling the output of |CTRL-G|. +-- +-- field meaning ~ +-- - Left justify the item. The default is right justified +-- when minwid is larger than the length of the item. +-- 0 Leading zeroes in numeric items. Overridden by "-". +-- minwid Minimum width of the item, padding as set by "-" & "0". +-- Value must be 50 or less. +-- maxwid Maximum width of the item. Truncation occurs with a "<" +-- on the left for text items. Numeric items will be +-- shifted down to maxwid-2 digits followed by ">"number +-- where number is the amount of missing digits, much like +-- an exponential notation. +-- item A one letter code as described below. +-- +-- Following is a description of the possible statusline items. The +-- second character in "item" is the type: +-- N for number +-- S for string +-- F for flags as described below +-- - not applicable +-- +-- item meaning ~ +-- f S Path to the file in the buffer, as typed or relative to current +-- directory. +-- F S Full path to the file in the buffer. +-- t S File name (tail) of file in the buffer. +-- m F Modified flag, text is "[+]"; "[-]" if `'modifiable'` is off. +-- M F Modified flag, text is ",+" or ",-". +-- r F Readonly flag, text is "[RO]". +-- R F Readonly flag, text is ",RO". +-- h F Help buffer flag, text is "[help]". +-- H F Help buffer flag, text is ",HLP". +-- w F Preview window flag, text is "[Preview]". +-- W F Preview window flag, text is ",PRV". +-- y F Type of file in the buffer, e.g., "[vim]". See `'filetype'` . +-- Y F Type of file in the buffer, e.g., ",VIM". See `'filetype'` . +-- q S "[Quickfix List]", "[Location List]" or empty. +-- k S Value of "b:keymap_name" or `'keymap'` when |:lmap| mappings are +-- being used: "" +-- n N Buffer number. +-- b N Value of character under cursor. +-- B N As above, in hexadecimal. +-- o N Byte number in file of byte under cursor, first byte is 1. +-- Mnemonic: Offset from start of file (with one added) +-- O N As above, in hexadecimal. +-- l N Line number. +-- L N Number of lines in buffer. +-- c N Column number (byte index). +-- v N Virtual column number (screen column). +-- V N Virtual column number as -{num}. Not displayed if equal to `'c'` . +-- p N Percentage through file in lines as in |CTRL-G|. +-- P S Percentage through file of displayed window. This is like the +-- percentage described for `'ruler'` . Always 3 in length, unless +-- translated. +-- S S `'showcmd'` content, see `'showcmdloc'` . +-- a S Argument list status as in default title. ({current} of {max}) +-- Empty if the argument file count is zero or one. +-- { NF Evaluate expression between "%{" and "}" and substitute result. +-- Note that there is no "%" before the closing "}". The +-- expression cannot contain a "}" character, call a function to +-- work around that. See |stl-%{| below. +-- `{%` - This is almost same as "{" except the result of the expression is +-- re-evaluated as a statusline format string. Thus if the +-- return value of expr contains "%" items they will get expanded. +-- The expression can contain the "}" character, the end of +-- expression is denoted by "%}". +-- For example: > +-- func! Stl_filename() abort +-- return "%t" +-- endfunc +-- < `stl=%{Stl_filename()}` results in `"%t"` +-- `stl=%{%Stl_filename()%}` results in `"Name of current file"` +-- %} - End of "{%" expression +-- ( - Start of item group. Can be used for setting the width and +-- alignment of a section. Must be followed by %) somewhere. +-- ) - End of item group. No width fields allowed. +-- T N For `'tabline'` : start of tab page N label. Use %T or %X to end +-- the label. Clicking this label with left mouse button switches +-- to the specified tab page. +-- X N For `'tabline'` : start of close tab N label. Use %X or %T to end +-- the label, e.g.: %3Xclose%X. Use %999X for a "close current +-- tab" label. Clicking this label with left mouse button closes +-- specified tab page. +-- @ N Start of execute function label. Use %X or %T to +-- end the label, e.g.: %10@SwitchBuffer@foo.c%X. Clicking this +-- label runs specified function: in the example when clicking once +-- using left mouse button on "foo.c" "SwitchBuffer(10, 1, `'l'` , +-- ' ')" expression will be run. Function receives the +-- following arguments in order: +-- 1. minwid field value or zero if no N was specified +-- 2. number of mouse clicks to detect multiple clicks +-- 3. mouse button used: "l", "r" or "m" for left, right or middle +-- button respectively; one should not rely on third argument +-- being only "l", "r" or "m": any other non-empty string value +-- that contains only ASCII lower case letters may be expected +-- for other mouse buttons +-- 4. modifiers pressed: string which contains "s" if shift +-- modifier was pressed, "c" for control, "a" for alt and "m" +-- for meta; currently if modifier is not pressed string +-- contains space instead, but one should not rely on presence +-- of spaces or specific order of modifiers: use |stridx()| to +-- test whether some modifier is present; string is guaranteed +-- to contain only ASCII letters and spaces, one letter per +-- modifier; "?" modifier may also be present, but its presence +-- is a bug that denotes that new mouse button recognition was +-- added without modifying code that reacts on mouse clicks on +-- this label. +-- Use |getmousepos()|.winid in the specified function to get the +-- corresponding window id of the clicked item. +-- < - Where to truncate line if too long. Default is at the start. +-- No width fields allowed. +-- = - Separation point between alignment sections. Each section will +-- be separated by an equal number of spaces. With one %= what +-- comes after it will be right-aligned. With two %= there is a +-- middle part, with white space left and right of it. +-- No width fields allowed. +-- # - Set highlight group. The name must follow and then a # again. +-- Thus use %#HLname# for highlight group HLname. The same +-- highlighting is used, also for the statusline of non-current +-- windows. +-- * - Set highlight group to User{N}, where {N} is taken from the +-- minwid field, e.g. %1*. Restore normal highlight with %* or %0*. +-- The difference between User{N} and StatusLine will be applied to +-- StatusLineNC for the statusline of non-current windows. +-- The number N must be between 1 and 9. See |hl-User1..9| +-- +-- When displaying a flag, Vim removes the leading comma, if any, when +-- that flag comes right after plaintext. This will make a nice display +-- when flags are used like in the examples below. +-- +-- When all items in a group becomes an empty string (i.e. flags that are +-- not set) and a minwid is not set for the group, the whole group will +-- become empty. This will make a group like the following disappear +-- completely from the statusline when none of the flags are set. > +-- :set statusline=...%(\ [%M%R%H]%)... +-- < Beware that an expression is evaluated each and every time the status +-- line is displayed. +-- +-- While evaluating %{} the current buffer and current window will be set +-- temporarily to that of the window (and buffer) whose statusline is +-- currently being drawn. The expression will evaluate in this context. +-- The variable "g:actual_curbuf" is set to the `bufnr()` number of the +-- real current buffer and "g:actual_curwin" to the |window-ID| of the +-- real current window. These values are strings. +-- +-- The `'statusline'` option will be evaluated in the |sandbox| if set from +-- a modeline, see |sandbox-option|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- It is not allowed to change text or jump to another window while +-- evaluating `'statusline'` |textlock|. +-- +-- If the statusline is not updated when you want it (e.g., after setting +-- a variable that's used in an expression), you can force an update by +-- using `:redrawstatus`. +-- +-- A result of all digits is regarded a number for display purposes. +-- Otherwise the result is taken as flag text and applied to the rules +-- described above. +-- +-- Watch out for errors in expressions. They may render Vim unusable! +-- If you are stuck, hold down `':'` or `'Q'` to get a prompt, then quit and +-- edit your vimrc or whatever with "vim --clean" to get it right. +-- +-- Examples: +-- Emulate standard status line with `'ruler'` set > +-- :set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P +-- < Similar, but add ASCII value of char under the cursor (like "ga") > +-- :set statusline=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P +-- < Display byte count and byte value, modified flag in red. > +-- :set statusline=%<%f%=\ [%1%n%R%H]\ %-19(%3l,%02c%03V%)%O`'%02b'` +-- :hi User1 term=inverse,bold cterm=inverse,bold ctermfg=red +-- < Display a ,GZ flag if a compressed file is loaded > +-- :set statusline=...%r%{VarExists(`'b:gzflag'` ,'\ [GZ]')}%h... +-- < In the |:autocmd|'s: > +-- :let b:gzflag = 1 +-- < And: > +-- :unlet b:gzflag +-- < And define this function: > +-- :function VarExists(var, val) +-- : if exists(a:var) | return a:val | else | return `''` | endif +-- :endfunction +-- < +vim.wo.statusline = "" +vim.wo.stl = vim.wo.statusline +-- `'virtualedit'` `'ve'` string (default "") +-- global or local to window |global-local| +-- A comma-separated list of these words: +-- block Allow virtual editing in Visual block mode. +-- insert Allow virtual editing in Insert mode. +-- all Allow virtual editing in all modes. +-- onemore Allow the cursor to move just past the end of the line +-- none When used as the local value, do not allow virtual +-- editing even when the global value is set. When used +-- as the global value, "none" is the same as "". +-- NONE Alternative spelling of "none". +-- +-- Virtual editing means that the cursor can be positioned where there is +-- no actual character. This can be halfway into a tab or beyond the end +-- of the line. Useful for selecting a rectangle in Visual mode and +-- editing a table. +-- "onemore" is not the same, it will only allow moving the cursor just +-- after the last character of the line. This makes some commands more +-- consistent. Previously the cursor was always past the end of the line +-- if the line was empty. But it is far from Vi compatible. It may also +-- break some plugins or Vim scripts. For example because |l| can move +-- the cursor after the last character. Use with care! +-- Using the `$` command will move to the last character in the line, not +-- past it. This may actually move the cursor to the left! +-- The `g$` command will move to the end of the screen line. +-- It doesn't make sense to combine "all" with "onemore", but you will +-- not get a warning for it. +-- When combined with other words, "none" is ignored. +vim.wo.virtualedit = "" +vim.wo.ve = vim.wo.virtualedit +-- `'winbar'` `'wbr'` string (default empty) +-- global or local to window |global-local| +-- When non-empty, this option enables the window bar and determines its +-- contents. The window bar is a bar that's shown at the top of every +-- window with it enabled. The value of `'winbar'` is evaluated like with +-- `'statusline'` . +-- +-- When changing something that is used in `'winbar'` that does not trigger +-- it to be updated, use |:redrawstatus|. +-- +-- Floating windows do not use the global value of `'winbar'` . The +-- window-local value of `'winbar'` must be set for a floating window to +-- have a window bar. +-- +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +vim.wo.winbar = "" +vim.wo.wbr = vim.wo.winbar +-- `'winblend'` `'winbl'` number (default 0) +-- local to window +-- Enables pseudo-transparency for a floating window. Valid values are in +-- the range of 0 for fully opaque window (disabled) to 100 for fully +-- transparent background. Values between 0-30 are typically most useful. +-- +-- UI-dependent. Works best with RGB colors. `'termguicolors'` +vim.wo.winblend = 0 +vim.wo.winbl = vim.wo.winblend +-- `'winfixheight'` `'wfh'` boolean (default off) +-- local to window +-- Keep the window height when windows are opened or closed and +-- `'equalalways'` is set. Also for |CTRL-W_=|. Set by default for the +-- |preview-window| and |quickfix-window|. +-- The height may be changed anyway when running out of room. +vim.wo.winfixheight = false +vim.wo.wfh = vim.wo.winfixheight +-- `'winfixwidth'` `'wfw'` boolean (default off) +-- local to window +-- Keep the window width when windows are opened or closed and +-- `'equalalways'` is set. Also for |CTRL-W_=|. +-- The width may be changed anyway when running out of room. +vim.wo.winfixwidth = false +vim.wo.wfw = vim.wo.winfixwidth +-- `'winhighlight'` `'winhl'` string (default empty) +-- local to window +-- Window-local highlights. Comma-delimited list of highlight +-- |group-name| pairs "{hl-from}:{hl-to},..." where each {hl-from} is +-- a |highlight-groups| item to be overridden by {hl-to} group in +-- the window. +-- +-- Note: highlight namespaces take precedence over `'winhighlight'` . +-- See |nvim_win_set_hl_ns()| and |nvim_set_hl()|. +-- +-- Highlights of vertical separators are determined by the window to the +-- left of the separator. The `'tabline'` highlight of a tabpage is +-- decided by the last-focused window of the tabpage. Highlights of +-- the popupmenu are determined by the current window. Highlights in the +-- message area cannot be overridden. +-- +-- Example: show a different color for non-current windows: > +-- set winhighlight=Normal:MyNormal,NormalNC:MyNormalNC +-- < +vim.wo.winhighlight = "" +vim.wo.winhl = vim.wo.winhighlight +-- `'wrap'` boolean (default on) +-- local to window +-- This option changes how text is displayed. It doesn't change the text +-- in the buffer, see `'textwidth'` for that. +-- When on, lines longer than the width of the window will wrap and +-- displaying continues on the next line. When off lines will not wrap +-- and only part of long lines will be displayed. When the cursor is +-- moved to a part that is not shown, the screen will scroll +-- horizontally. +-- The line will be broken in the middle of a word if necessary. See +-- `'linebreak'` to get the break at a word boundary. +-- To make scrolling horizontally a bit more useful, try this: > +-- :set sidescroll=5 +-- :set listchars+=precedes:<,extends:> +-- < See `'sidescroll'` , `'listchars'` and |wrap-off|. +-- This option can't be set from a |modeline| when the `'diff'` option is +-- on. +vim.wo.wrap = true + + +---@class vim.bo +vim.bo = {} + +-- `'autoindent'` `'ai'` boolean (default on) +-- local to buffer +-- Copy indent from current line when starting a new line (typing +-- in Insert mode or when using the "o" or "O" command). If you do not +-- type anything on the new line except or CTRL-D and then type +-- , CTRL-O or , the indent is deleted again. Moving the cursor +-- to another line has the same effect, unless the `'I'` flag is included +-- in `'cpoptions'` . +-- When autoindent is on, formatting (with the "gq" command or when you +-- reach `'textwidth'` in Insert mode) uses the indentation of the first +-- line. +-- When `'smartindent'` or `'cindent'` is on the indent is changed in +-- a different way. +-- {small difference from Vi: After the indent is deleted when typing +-- or , the cursor position when moving up or down is after the +-- deleted indent; Vi puts the cursor somewhere in the deleted indent}. +vim.bo.autoindent = true +vim.bo.ai = vim.bo.autoindent +-- `'autoread'` `'ar'` boolean (default on) +-- global or local to buffer |global-local| +-- When a file has been detected to have been changed outside of Vim and +-- it has not been changed inside of Vim, automatically read it again. +-- When the file has been deleted this is not done, so you have the text +-- from before it was deleted. When it appears again then it is read. +-- |timestamp| +-- If this option has a local value, use this command to switch back to +-- using the global value: > +-- :set autoread< +-- < +vim.bo.autoread = true +vim.bo.ar = vim.bo.autoread +-- `'backupcopy'` `'bkc'` string (default: "auto") +-- global or local to buffer |global-local| +-- When writing a file and a backup is made, this option tells how it's +-- done. This is a comma-separated list of words. +-- +-- The main values are: +-- "yes" make a copy of the file and overwrite the original one +-- "no" rename the file and write a new one +-- "auto" one of the previous, what works best +-- +-- Extra values that can be combined with the ones above are: +-- "breaksymlink" always break symlinks when writing +-- "breakhardlink" always break hardlinks when writing +-- +-- Making a copy and overwriting the original file: +-- - Takes extra time to copy the file. +-- + When the file has special attributes, is a (hard/symbolic) link or +-- has a resource fork, all this is preserved. +-- - When the file is a link the backup will have the name of the link, +-- not of the real file. +-- +-- Renaming the file and writing a new one: +-- + It's fast. +-- - Sometimes not all attributes of the file can be copied to the new +-- file. +-- - When the file is a link the new file will not be a link. +-- +-- The "auto" value is the middle way: When Vim sees that renaming the +-- file is possible without side effects (the attributes can be passed on +-- and the file is not a link) that is used. When problems are expected, +-- a copy will be made. +-- +-- The "breaksymlink" and "breakhardlink" values can be used in +-- combination with any of "yes", "no" and "auto". When included, they +-- force Vim to always break either symbolic or hard links by doing +-- exactly what the "no" option does, renaming the original file to +-- become the backup and writing a new file in its place. This can be +-- useful for example in source trees where all the files are symbolic or +-- hard links and any changes should stay in the local source tree, not +-- be propagated back to the original source. +-- +-- One situation where "no" and "auto" will cause problems: A program +-- that opens a file, invokes Vim to edit that file, and then tests if +-- the open file was changed (through the file descriptor) will check the +-- backup file instead of the newly created file. "crontab -e" is an +-- example. +-- +-- When a copy is made, the original file is truncated and then filled +-- with the new text. This means that protection bits, owner and +-- symbolic links of the original file are unmodified. The backup file, +-- however, is a new file, owned by the user who edited the file. The +-- group of the backup is set to the group of the original file. If this +-- fails, the protection bits for the group are made the same as for +-- others. +-- +-- When the file is renamed, this is the other way around: The backup has +-- the same attributes of the original file, and the newly written file +-- is owned by the current user. When the file was a (hard/symbolic) +-- link, the new file will not! That's why the "auto" value doesn't +-- rename when the file is a link. The owner and group of the newly +-- written file will be set to the same ones as the original file, but +-- the system may refuse to do this. In that case the "auto" value will +-- again not rename the file. +vim.bo.backupcopy = "auto" +vim.bo.bkc = vim.bo.backupcopy +-- `'binary'` `'bin'` boolean (default off) +-- local to buffer +-- This option should be set before editing a binary file. You can also +-- use the |-b| Vim argument. When this option is switched on a few +-- options will be changed (also when it already was on): +-- `'textwidth'` will be set to 0 +-- `'wrapmargin'` will be set to 0 +-- `'modeline'` will be off +-- `'expandtab'` will be off +-- Also, `'fileformat'` and `'fileformats'` options will not be used, the +-- file is read and written like `'fileformat'` was "unix" (a single +-- separates lines). +-- The `'fileencoding'` and `'fileencodings'` options will not be used, the +-- file is read without conversion. +-- NOTE: When you start editing a(nother) file while the `'bin'` option is +-- on, settings from autocommands may change the settings again (e.g., +-- `'textwidth'` ), causing trouble when editing. You might want to set +-- `'bin'` again when the file has been loaded. +-- The previous values of these options are remembered and restored when +-- `'bin'` is switched from on to off. Each buffer has its own set of +-- saved option values. +-- To edit a file with `'binary'` set you can use the |++bin| argument. +-- This avoids you have to do ":set bin", which would have effect for all +-- files you edit. +-- When writing a file the for the last line is only written if +-- there was one in the original file (normally Vim appends an to +-- the last line if there is none; this would make the file longer). See +-- the `'endofline'` option. +vim.bo.binary = false +vim.bo.bin = vim.bo.binary +-- `'bomb'` boolean (default off) +-- local to buffer +-- When writing a file and the following conditions are met, a BOM (Byte +-- Order Mark) is prepended to the file: +-- - this option is on +-- - the `'binary'` option is off +-- - `'fileencoding'` is "utf-8", "ucs-2", "ucs-4" or one of the little/big +-- endian variants. +-- Some applications use the BOM to recognize the encoding of the file. +-- Often used for UCS-2 files on MS-Windows. For other applications it +-- causes trouble, for example: "cat file1 file2" makes the BOM of file2 +-- appear halfway through the resulting file. Gcc doesn't accept a BOM. +-- When Vim reads a file and `'fileencodings'` starts with "ucs-bom", a +-- check for the presence of the BOM is done and `'bomb'` set accordingly. +-- Unless `'binary'` is set, it is removed from the first line, so that you +-- don't see it when editing. When you don't change the options, the BOM +-- will be restored when writing the file. +vim.bo.bomb = false +-- `'bufhidden'` `'bh'` string (default: "") +-- local to buffer +-- This option specifies what happens when a buffer is no longer +-- displayed in a window: +-- follow the global `'hidden'` option +-- hide hide the buffer (don't unload it), even if `'hidden'` is +-- not set +-- unload unload the buffer, even if `'hidden'` is set; the +-- |:hide| command will also unload the buffer +-- delete delete the buffer from the buffer list, even if +-- `'hidden'` is set; the |:hide| command will also delete +-- the buffer, making it behave like |:bdelete| +-- wipe wipe the buffer from the buffer list, even if +-- `'hidden'` is set; the |:hide| command will also wipe +-- out the buffer, making it behave like |:bwipeout| +-- +-- CAREFUL: when "unload", "delete" or "wipe" is used changes in a buffer +-- are lost without a warning. Also, these values may break autocommands +-- that switch between buffers temporarily. +-- This option is used together with `'buftype'` and `'swapfile'` to specify +-- special kinds of buffers. See |special-buffers|. +vim.bo.bufhidden = "" +vim.bo.bh = vim.bo.bufhidden +-- `'buflisted'` `'bl'` boolean (default: on) +-- local to buffer +-- When this option is set, the buffer shows up in the buffer list. If +-- it is reset it is not used for ":bnext", "ls", the Buffers menu, etc. +-- This option is reset by Vim for buffers that are only used to remember +-- a file name or marks. Vim sets it when starting to edit a buffer. +-- But not when moving to a buffer with ":buffer". +vim.bo.buflisted = true +vim.bo.bl = vim.bo.buflisted +-- `'buftype'` `'bt'` string (default: "") +-- local to buffer +-- The value of this option specifies the type of a buffer: +-- normal buffer +-- acwrite buffer will always be written with |BufWriteCmd|s +-- help help buffer (do not set this manually) +-- nofile buffer is not related to a file, will not be written +-- nowrite buffer will not be written +-- quickfix list of errors |:cwindow| or locations |:lwindow| +-- terminal |terminal-emulator| buffer +-- prompt buffer where only the last line can be edited, meant +-- to be used by a plugin, see |prompt-buffer| +-- +-- This option is used together with `'bufhidden'` and `'swapfile'` to +-- specify special kinds of buffers. See |special-buffers|. +-- Also see |win_gettype()|, which returns the type of the window. +-- +-- Be careful with changing this option, it can have many side effects! +-- One such effect is that Vim will not check the timestamp of the file, +-- if the file is changed by another program this will not be noticed. +-- +-- A "quickfix" buffer is only used for the error list and the location +-- list. This value is set by the |:cwindow| and |:lwindow| commands and +-- you are not supposed to change it. +-- +-- "nofile" and "nowrite" buffers are similar: +-- both: The buffer is not to be written to disk, ":w" doesn't +-- work (":w filename" does work though). +-- both: The buffer is never considered to be |`'modified'` |. +-- There is no warning when the changes will be lost, for +-- example when you quit Vim. +-- both: A swap file is only created when using too much memory +-- (when `'swapfile'` has been reset there is never a swap +-- file). +-- nofile only: The buffer name is fixed, it is not handled like a +-- file name. It is not modified in response to a |:cd| +-- command. +-- both: When using ":e bufname" and already editing "bufname" +-- the buffer is made empty and autocommands are +-- triggered as usual for |:edit|. +-- +-- "acwrite" implies that the buffer name is not related to a file, like +-- "nofile", but it will be written. Thus, in contrast to "nofile" and +-- "nowrite", ":w" does work and a modified buffer can't be abandoned +-- without saving. For writing there must be matching |BufWriteCmd|, +-- |FileWriteCmd| or |FileAppendCmd| autocommands. +vim.bo.buftype = "" +vim.bo.bt = vim.bo.buftype +-- `'channel'` number (default: 0) +-- local to buffer +-- |channel| connected to the buffer, or 0 if no channel is connected. +-- In a |:terminal| buffer this is the terminal channel. +-- Read-only. +vim.bo.channel = 0 +-- `'cindent'` `'cin'` boolean (default off) +-- local to buffer +-- Enables automatic C program indenting. See `'cinkeys'` to set the keys +-- that trigger reindenting in insert mode and `'cinoptions'` to set your +-- preferred indent style. +-- If `'indentexpr'` is not empty, it overrules `'cindent'` . +-- If `'lisp'` is not on and both `'indentexpr'` and `'equalprg'` are empty, +-- the "=" operator indents using this algorithm rather than calling an +-- external program. +-- See |C-indenting|. +-- When you don't like the way `'cindent'` works, try the `'smartindent'` +-- option or `'indentexpr'` . +vim.bo.cindent = false +vim.bo.cin = vim.bo.cindent +-- `'cinkeys'` `'cink'` string (default "0{,0},0),0],:,0#,!^F,o,O,e") +-- local to buffer +-- A list of keys that, when typed in Insert mode, cause reindenting of +-- the current line. Only used if `'cindent'` is on and `'indentexpr'` is +-- empty. +-- For the format of this option see |cinkeys-format|. +-- See |C-indenting|. +vim.bo.cinkeys = "0{,0},0),0],:,0#,!^F,o,O,e" +vim.bo.cink = vim.bo.cinkeys +-- `'cinoptions'` `'cino'` string (default "") +-- local to buffer +-- The `'cinoptions'` affect the way `'cindent'` reindents lines in a C +-- program. See |cinoptions-values| for the values of this option, and +-- |C-indenting| for info on C indenting in general. +vim.bo.cinoptions = "" +vim.bo.cino = vim.bo.cinoptions +-- `'cinscopedecls'` `'cinsd'` string (default "public,protected,private") +-- local to buffer +-- Keywords that are interpreted as a C++ scope declaration by |cino-g|. +-- Useful e.g. for working with the Qt framework that defines additional +-- scope declarations "signals", "public slots" and "private slots": > +-- set cinscopedecls+=signals,public\ slots,private\ slots +-- +-- < +vim.bo.cinscopedecls = "public,protected,private" +vim.bo.cinsd = vim.bo.cinscopedecls +-- `'cinwords'` `'cinw'` string (default "if,else,while,do,for,switch") +-- local to buffer +-- These keywords start an extra indent in the next line when +-- `'smartindent'` or `'cindent'` is set. For `'cindent'` this is only done at +-- an appropriate place (inside {}). +-- Note that `'ignorecase'` isn't used for `'cinwords'` . If case doesn't +-- matter, include the keyword both the uppercase and lowercase: +-- "if,If,IF". +vim.bo.cinwords = "if,else,while,do,for,switch" +vim.bo.cinw = vim.bo.cinwords +-- `'comments'` `'com'` string (default +-- "s1:/,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-") +-- local to buffer +-- A comma-separated list of strings that can start a comment line. See +-- |format-comments|. See |option-backslash| about using backslashes to +-- insert a space. +vim.bo.comments = "s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-" +vim.bo.com = vim.bo.comments +-- `'commentstring'` `'cms'` string (default "") +-- local to buffer +-- A template for a comment. The "%s" in the value is replaced with the +-- comment text. Currently only used to add markers for folding, see +-- |fold-marker|. +vim.bo.commentstring = "" +vim.bo.cms = vim.bo.commentstring +-- `'complete'` `'cpt'` string (default: ".,w,b,u,t") +-- local to buffer +-- This option specifies how keyword completion |ins-completion| works +-- when CTRL-P or CTRL-N are used. It is also used for whole-line +-- completion |i_CTRL-X_CTRL-L|. It indicates the type of completion +-- and the places to scan. It is a comma-separated list of flags: +-- . scan the current buffer (`'wrapscan'` is ignored) +-- w scan buffers from other windows +-- b scan other loaded buffers that are in the buffer list +-- u scan the unloaded buffers that are in the buffer list +-- U scan the buffers that are not in the buffer list +-- k scan the files given with the `'dictionary'` option +-- kspell use the currently active spell checking |spell| +-- k{dict} scan the file {dict}. Several "k" flags can be given, +-- patterns are valid too. For example: > +-- :set cpt=k/usr/dict/*,k~/spanish +-- < s scan the files given with the `'thesaurus'` option +-- s{tsr} scan the file {tsr}. Several "s" flags can be given, patterns +-- are valid too. +-- i scan current and included files +-- d scan current and included files for defined name or macro +-- |i_CTRL-X_CTRL-D| +-- ] tag completion +-- t same as "]" +-- +-- Unloaded buffers are not loaded, thus their autocmds |:autocmd| are +-- not executed, this may lead to unexpected completions from some files +-- (gzipped files for example). Unloaded buffers are not scanned for +-- whole-line completion. +-- +-- As you can see, CTRL-N and CTRL-P can be used to do any `'iskeyword'` - +-- based expansion (e.g., dictionary |i_CTRL-X_CTRL-K|, included patterns +-- |i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]| and normal expansions). +vim.bo.complete = ".,w,b,u,t" +vim.bo.cpt = vim.bo.complete +-- `'completefunc'` `'cfu'` string (default: empty) +-- local to buffer +-- This option specifies a function to be used for Insert mode completion +-- with CTRL-X CTRL-U. |i_CTRL-X_CTRL-U| +-- See |complete-functions| for an explanation of how the function is +-- invoked and what it should return. The value can be the name of a +-- function, a |lambda| or a |Funcref|. See |option-value-function| for +-- more information. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.bo.completefunc = "" +vim.bo.cfu = vim.bo.completefunc +-- `'copyindent'` `'ci'` boolean (default off) +-- local to buffer +-- Copy the structure of the existing lines indent when autoindenting a +-- new line. Normally the new indent is reconstructed by a series of +-- tabs followed by spaces as required (unless |`'expandtab'` | is enabled, +-- in which case only spaces are used). Enabling this option makes the +-- new line copy whatever characters were used for indenting on the +-- existing line. `'expandtab'` has no effect on these characters, a Tab +-- remains a Tab. If the new indent is greater than on the existing +-- line, the remaining space is filled in the normal manner. +-- See `'preserveindent'` . +vim.bo.copyindent = false +vim.bo.ci = vim.bo.copyindent +-- `'define'` `'def'` string (default "^\sdefine") +-- global or local to buffer |global-local| +-- Pattern to be used to find a macro definition. It is a search +-- pattern, just like for the "/" command. This option is used for the +-- commands like "[i" and "[d" |include-search|. The `'isident'` option is +-- used to recognize the defined name after the match: +-- {match with `'define'` }{non-ID chars}{defined name}{non-ID char} +-- See |option-backslash| about inserting backslashes to include a space +-- or backslash. +-- The default value is for C programs. For C++ this value would be +-- useful, to include const type declarations: > +-- ^\(#\s\s[a-z]*\) +-- < You can also use "\ze" just before the name and continue the pattern +-- to check what is following. E.g. for Javascript, if a function is +-- defined with `func_name = function(args)`: > +-- ^\s=\s*function( +-- < If the function is defined with `func_name : function() {...`: > +-- ^\s[:]\sfunction\s*( +-- < When using the ":set" command, you need to double the backslashes! +-- To avoid that use `:let` with a single quote string: > +-- let &l:define = `'^\s=\s*function('` +-- < +vim.bo.define = "^\\s*#\\s*define" +vim.bo.def = vim.bo.define +-- `'dictionary'` `'dict'` string (default "") +-- global or local to buffer |global-local| +-- List of file names, separated by commas, that are used to lookup words +-- for keyword completion commands |i_CTRL-X_CTRL-K|. Each file should +-- contain a list of words. This can be one word per line, or several +-- words per line, separated by non-keyword characters (white space is +-- preferred). Maximum line length is 510 bytes. +-- +-- When this option is empty or an entry "spell" is present, and spell +-- checking is enabled, words in the word lists for the currently active +-- `'spelllang'` are used. See |spell|. +-- +-- To include a comma in a file name precede it with a backslash. Spaces +-- after a comma are ignored, otherwise spaces are included in the file +-- name. See |option-backslash| about using backslashes. +-- This has nothing to do with the |Dictionary| variable type. +-- Where to find a list of words? +-- - BSD/macOS include the "/usr/share/dict/words" file. +-- - Try "apt install spell" to get the "/usr/share/dict/words" file on +-- apt-managed systems (Debian/Ubuntu). +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- directories from the list. This avoids problems when a future version +-- uses another default. +-- Backticks cannot be used in this option for security reasons. +vim.bo.dictionary = "" +vim.bo.dict = vim.bo.dictionary +-- `'endoffile'` `'eof'` boolean (default off) +-- local to buffer +-- Indicates that a CTRL-Z character was found at the end of the file +-- when reading it. Normally only happens when `'fileformat'` is "dos". +-- When writing a file and this option is off and the `'binary'` option +-- is on, or `'fixeol'` option is off, no CTRL-Z will be written at the +-- end of the file. +-- See |eol-and-eof| for example settings. +vim.bo.endoffile = false +vim.bo.eof = vim.bo.endoffile +-- `'endofline'` `'eol'` boolean (default on) +-- local to buffer +-- When writing a file and this option is off and the `'binary'` option +-- is on, or `'fixeol'` option is off, no will be written for the +-- last line in the file. This option is automatically set or reset when +-- starting to edit a new file, depending on whether file has an +-- for the last line in the file. Normally you don't have to set or +-- reset this option. +-- When `'binary'` is off and `'fixeol'` is on the value is not used when +-- writing the file. When `'binary'` is on or `'fixeol'` is off it is used +-- to remember the presence of a for the last line in the file, so +-- that when you write the file the situation from the original file can +-- be kept. But you can change it if you want to. +-- See |eol-and-eof| for example settings. +vim.bo.endofline = true +vim.bo.eol = vim.bo.endofline +-- `'equalprg'` `'ep'` string (default "") +-- global or local to buffer |global-local| +-- External program to use for "=" command. When this option is empty +-- the internal formatting functions are used; either `'lisp'` , `'cindent'` +-- or `'indentexpr'` . +-- Environment variables are expanded |:set_env|. See |option-backslash| +-- about including spaces and backslashes. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.bo.equalprg = "" +vim.bo.ep = vim.bo.equalprg +-- `'errorformat'` `'efm'` string (default is very long) +-- global or local to buffer |global-local| +-- Scanf-like description of the format for the lines in the error file +-- (see |errorformat|). +vim.bo.errorformat = "%*[^\"]\"%f\"%*\\D%l: %m,\"%f\"%*\\D%l: %m,%-G%f:%l: (Each undeclared identifier is reported only once,%-G%f:%l: for each function it appears in.),%-GIn file included from %f:%l:%c:,%-GIn file included from %f:%l:%c\\,,%-GIn file included from %f:%l:%c,%-GIn file included from %f:%l,%-G%*[ ]from %f:%l:%c,%-G%*[ ]from %f:%l:,%-G%*[ ]from %f:%l\\,,%-G%*[ ]from %f:%l,%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,\"%f\"\\, line %l%*\\D%c%*[^ ] %m,%D%*\\a[%*\\d]: Entering directory %*[`']%f',%X%*\\a[%*\\d]: Leaving directory %*[`']%f',%D%*\\a: Entering directory %*[`']%f',%X%*\\a: Leaving directory %*[`']%f',%DMaking %*\\a in %f,%f|%l| %m" +vim.bo.efm = vim.bo.errorformat +-- `'expandtab'` `'et'` boolean (default off) +-- local to buffer +-- In Insert mode: Use the appropriate number of spaces to insert a +-- . Spaces are used in indents with the `'>'` and `'<'` commands and +-- when `'autoindent'` is on. To insert a real tab when `'expandtab'` is +-- on, use CTRL-V. See also |:retab| and |ins-expandtab|. +vim.bo.expandtab = false +vim.bo.et = vim.bo.expandtab +-- `'fileencoding'` `'fenc'` string (default: "") +-- local to buffer +-- File-content encoding for the current buffer. Conversion is done with +-- iconv() or as specified with `'charconvert'` . +-- +-- When `'fileencoding'` is not UTF-8, conversion will be done when +-- writing the file. For reading see below. +-- When `'fileencoding'` is empty, the file will be saved with UTF-8 +-- encoding (no conversion when reading or writing a file). +-- +-- WARNING: Conversion to a non-Unicode encoding can cause loss of +-- information! +-- +-- See |encoding-names| for the possible values. Additionally, values may be +-- specified that can be handled by the converter, see +-- |mbyte-conversion|. +-- +-- When reading a file `'fileencoding'` will be set from `'fileencodings'` . +-- To read a file in a certain encoding it won't work by setting +-- `'fileencoding'` , use the |++enc| argument. One exception: when +-- `'fileencodings'` is empty the value of `'fileencoding'` is used. +-- For a new file the global value of `'fileencoding'` is used. +-- +-- Prepending "8bit-" and "2byte-" has no meaning here, they are ignored. +-- When the option is set, the value is converted to lowercase. Thus +-- you can set it with uppercase values too. `'_'` characters are +-- replaced with `'-'` . If a name is recognized from the list at +-- |encoding-names|, it is replaced by the standard name. For example +-- "ISO8859-2" becomes "iso-8859-2". +-- +-- When this option is set, after starting to edit a file, the `'modified'` +-- option is set, because the file would be different when written. +-- +-- Keep in mind that changing `'fenc'` from a modeline happens +-- AFTER the text has been read, thus it applies to when the file will be +-- written. If you do set `'fenc'` in a modeline, you might want to set +-- `'nomodified'` to avoid not being able to ":q". +-- +-- This option cannot be changed when `'modifiable'` is off. +vim.bo.fileencoding = "" +vim.bo.fenc = vim.bo.fileencoding +-- `'fileformat'` `'ff'` string (Windows default: "dos", +-- Unix default: "unix") +-- local to buffer +-- This gives the of the current buffer, which is used for +-- reading/writing the buffer from/to a file: +-- dos +-- unix +-- mac +-- When "dos" is used, CTRL-Z at the end of a file is ignored. +-- See |file-formats| and |file-read|. +-- For the character encoding of the file see `'fileencoding'` . +-- When `'binary'` is set, the value of `'fileformat'` is ignored, file I/O +-- works like it was set to "unix". +-- This option is set automatically when starting to edit a file and +-- `'fileformats'` is not empty and `'binary'` is off. +-- When this option is set, after starting to edit a file, the `'modified'` +-- option is set, because the file would be different when written. +-- This option cannot be changed when `'modifiable'` is off. +vim.bo.fileformat = "unix" +vim.bo.ff = vim.bo.fileformat +-- `'filetype'` `'ft'` string (default: "") +-- local to buffer +-- When this option is set, the FileType autocommand event is triggered. +-- All autocommands that match with the value of this option will be +-- executed. Thus the value of `'filetype'` is used in place of the file +-- name. +-- Otherwise this option does not always reflect the current file type. +-- This option is normally set when the file type is detected. To enable +-- this use the ":filetype on" command. |:filetype| +-- Setting this option to a different value is most useful in a modeline, +-- for a file for which the file type is not automatically recognized. +-- Example, for in an IDL file: > +-- /* vim: set filetype=idl : */ +-- < |FileType| |filetypes| +-- When a dot appears in the value then this separates two filetype +-- names. Example: > +-- /* vim: set filetype=c.doxygen : */ +-- < This will use the "c" filetype first, then the "doxygen" filetype. +-- This works both for filetype plugins and for syntax files. More than +-- one dot may appear. +-- This option is not copied to another buffer, independent of the `'s'` or +-- `'S'` flag in `'cpoptions'` . +-- Only normal file name characters can be used, "/\*?[|<>" are illegal. +vim.bo.filetype = "" +vim.bo.ft = vim.bo.filetype +-- `'fixendofline'` `'fixeol'` boolean (default on) +-- local to buffer +-- When writing a file and this option is on, at the end of file +-- will be restored if missing. Turn this option off if you want to +-- preserve the situation from the original file. +-- When the `'binary'` option is set the value of this option doesn't +-- matter. +-- See the `'endofline'` option. +-- See |eol-and-eof| for example settings. +vim.bo.fixendofline = true +vim.bo.fixeol = vim.bo.fixendofline +-- `'formatexpr'` `'fex'` string (default "") +-- local to buffer +-- Expression which is evaluated to format a range of lines for the |gq| +-- operator or automatic formatting (see `'formatoptions'` ). When this +-- option is empty `'formatprg'` is used. +-- +-- The |v:lnum| variable holds the first line to be formatted. +-- The |v:count| variable holds the number of lines to be formatted. +-- The |v:char| variable holds the character that is going to be +-- inserted if the expression is being evaluated due to +-- automatic formatting. This can be empty. Don't insert +-- it yet! +-- +-- Example: > +-- :set formatexpr=mylang#Format() +-- < This will invoke the mylang#Format() function in the +-- autoload/mylang.vim file in `'runtimepath'` . |autoload| +-- +-- The expression is also evaluated when `'textwidth'` is set and adding +-- text beyond that limit. This happens under the same conditions as +-- when internal formatting is used. Make sure the cursor is kept in the +-- same spot relative to the text then! The |mode()| function will +-- return "i" or "R" in this situation. +-- +-- When the expression evaluates to non-zero Vim will fall back to using +-- the internal format mechanism. +-- +-- If the expression starts with s: or ||, then it is replaced with +-- the script ID (|local-function|). Example: > +-- set formatexpr=s:MyFormatExpr() +-- set formatexpr=SomeFormatExpr() +-- < +-- The expression will be evaluated in the |sandbox| when set from a +-- modeline, see |sandbox-option|. That stops the option from working, +-- since changing the buffer text is not allowed. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- NOTE: This option is set to "" when `'compatible'` is set. +vim.bo.formatexpr = "" +vim.bo.fex = vim.bo.formatexpr +-- `'formatlistpat'` `'flp'` string (default: "^\s*\d\+[\]:.)}\t ]\s*") +-- local to buffer +-- A pattern that is used to recognize a list header. This is used for +-- the "n" flag in `'formatoptions'` . +-- The pattern must match exactly the text that will be the indent for +-- the line below it. You can use |/\ze| to mark the end of the match +-- while still checking more characters. There must be a character +-- following the pattern, when it matches the whole line it is handled +-- like there is no match. +-- The default recognizes a number, followed by an optional punctuation +-- character and white space. +vim.bo.formatlistpat = "^\\s*\\d\\+[\\]:.)}\\t ]\\s*" +vim.bo.flp = vim.bo.formatlistpat +-- `'formatoptions'` `'fo'` string (default: "tcqj") +-- local to buffer +-- This is a sequence of letters which describes how automatic +-- formatting is to be done. See |fo-table|. Commas can be inserted for +-- readability. +-- To avoid problems with flags that are added in the future, use the +-- "+=" and "-=" feature of ":set" |add-option-flags|. +vim.bo.formatoptions = "tcqj" +vim.bo.fo = vim.bo.formatoptions +-- `'formatprg'` `'fp'` string (default "") +-- global or local to buffer |global-local| +-- The name of an external program that will be used to format the lines +-- selected with the |gq| operator. The program must take the input on +-- stdin and produce the output on stdout. The Unix program "fmt" is +-- such a program. +-- If the `'formatexpr'` option is not empty it will be used instead. +-- Otherwise, if `'formatprg'` option is an empty string, the internal +-- format function will be used |C-indenting|. +-- Environment variables are expanded |:set_env|. See |option-backslash| +-- about including spaces and backslashes. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.bo.formatprg = "" +vim.bo.fp = vim.bo.formatprg +-- `'grepprg'` `'gp'` string (default "grep -n ", +-- Unix: "grep -n $* /dev/null") +-- global or local to buffer |global-local| +-- Program to use for the |:grep| command. This option may contain `'%'` +-- and `'#'` characters, which are expanded like when used in a command- +-- line. The placeholder "$*" is allowed to specify where the arguments +-- will be included. Environment variables are expanded |:set_env|. See +-- |option-backslash| about including spaces and backslashes. +-- When your "grep" accepts the "-H" argument, use this to make ":grep" +-- also work well with a single file: > +-- :set grepprg=grep\ -nH +-- < Special value: When `'grepprg'` is set to "internal" the |:grep| command +-- works like |:vimgrep|, |:lgrep| like |:lvimgrep|, |:grepadd| like +-- |:vimgrepadd| and |:lgrepadd| like |:lvimgrepadd|. +-- See also the section |:make_makeprg|, since most of the comments there +-- apply equally to `'grepprg'` . +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.bo.grepprg = "grep -n $* /dev/null" +vim.bo.gp = vim.bo.grepprg +-- `'iminsert'` `'imi'` number (default 0) +-- local to buffer +-- Specifies whether :lmap or an Input Method (IM) is to be used in +-- Insert mode. Valid values: +-- 0 :lmap is off and IM is off +-- 1 :lmap is ON and IM is off +-- 2 :lmap is off and IM is ON +-- To always reset the option to zero when leaving Insert mode with +-- this can be used: > +-- :inoremap :set iminsert=0 +-- < This makes :lmap and IM turn off automatically when leaving Insert +-- mode. +-- Note that this option changes when using CTRL-^ in Insert mode +-- |i_CTRL-^|. +-- The value is set to 1 when setting `'keymap'` to a valid keymap name. +-- It is also used for the argument of commands like "r" and "f". +vim.bo.iminsert = 0 +vim.bo.imi = vim.bo.iminsert +-- `'imsearch'` `'ims'` number (default -1) +-- local to buffer +-- Specifies whether :lmap or an Input Method (IM) is to be used when +-- entering a search pattern. Valid values: +-- -1 the value of `'iminsert'` is used, makes it look like +-- `'iminsert'` is also used when typing a search pattern +-- 0 :lmap is off and IM is off +-- 1 :lmap is ON and IM is off +-- 2 :lmap is off and IM is ON +-- Note that this option changes when using CTRL-^ in Command-line mode +-- |c_CTRL-^|. +-- The value is set to 1 when it is not -1 and setting the `'keymap'` +-- option to a valid keymap name. +vim.bo.imsearch = -1 +vim.bo.ims = vim.bo.imsearch +-- `'include'` `'inc'` string (default "^\sinclude") +-- global or local to buffer |global-local| +-- Pattern to be used to find an include command. It is a search +-- pattern, just like for the "/" command (See |pattern|). The default +-- value is for C programs. This option is used for the commands "[i", +-- "]I", "[d", etc. +-- Normally the `'isfname'` option is used to recognize the file name that +-- comes after the matched pattern. But if "\zs" appears in the pattern +-- then the text matched from "\zs" to the end, or until "\ze" if it +-- appears, is used as the file name. Use this to include characters +-- that are not in `'isfname'` , such as a space. You can then use +-- `'includeexpr'` to process the matched text. +-- See |option-backslash| about including spaces and backslashes. +vim.bo.include = "^\\s*#\\s*include" +vim.bo.inc = vim.bo.include +-- `'includeexpr'` `'inex'` string (default "") +-- local to buffer +-- Expression to be used to transform the string found with the `'include'` +-- option to a file name. Mostly useful to change "." to "/" for Java: > +-- :setlocal includeexpr=substitute(v:fname,`'\\.'` ,`'/'` ,`'g'` ) +-- < The "v:fname" variable will be set to the file name that was detected. +-- Note the double backslash: the `:set` command first halves them, then +-- one remains in the value, where "\." matches a dot literally. For +-- simple character replacements `tr()` avoids the need for escaping: > +-- :setlocal includeexpr=tr(v:fname,`'.'` ,`'/'` ) +-- < +-- Also used for the |gf| command if an unmodified file name can't be +-- found. Allows doing "gf" on the name after an `'include'` statement. +-- Also used for ||. +-- +-- If the expression starts with s: or ||, then it is replaced with +-- the script ID (|local-function|). Example: > +-- set includeexpr=s:MyIncludeExpr(v:fname) +-- set includeexpr=SomeIncludeExpr(v:fname) +-- < +-- The expression will be evaluated in the |sandbox| when set from a +-- modeline, see |sandbox-option|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- It is not allowed to change text or jump to another window while +-- evaluating `'includeexpr'` |textlock|. +vim.bo.includeexpr = "" +vim.bo.inex = vim.bo.includeexpr +-- `'indentexpr'` `'inde'` string (default "") +-- local to buffer +-- Expression which is evaluated to obtain the proper indent for a line. +-- It is used when a new line is created, for the |=| operator and +-- in Insert mode as specified with the `'indentkeys'` option. +-- When this option is not empty, it overrules the `'cindent'` and +-- `'smartindent'` indenting. When `'lisp'` is set, this option is +-- is only used when `'lispoptions'` contains "expr:1". +-- The expression is evaluated with |v:lnum| set to the line number for +-- which the indent is to be computed. The cursor is also in this line +-- when the expression is evaluated (but it may be moved around). +-- If the expression starts with s: or ||, then it is replaced with +-- the script ID (|local-function|). Example: > +-- set indentexpr=s:MyIndentExpr() +-- set indentexpr=SomeIndentExpr() +-- < +-- The expression must return the number of spaces worth of indent. It +-- can return "-1" to keep the current indent (this means `'autoindent'` is +-- used for the indent). +-- Functions useful for computing the indent are |indent()|, |cindent()| +-- and |lispindent()|. +-- The evaluation of the expression must not have side effects! It must +-- not change the text, jump to another window, etc. Afterwards the +-- cursor position is always restored, thus the cursor may be moved. +-- Normally this option would be set to call a function: > +-- :set indentexpr=GetMyIndent() +-- < Error messages will be suppressed, unless the `'debug'` option contains +-- "msg". +-- See |indent-expression|. +-- +-- The expression will be evaluated in the |sandbox| when set from a +-- modeline, see |sandbox-option|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- It is not allowed to change text or jump to another window while +-- evaluating `'indentexpr'` |textlock|. +vim.bo.indentexpr = "" +vim.bo.inde = vim.bo.indentexpr +-- `'indentkeys'` `'indk'` string (default "0{,0},0),0],:,0#,!^F,o,O,e") +-- local to buffer +-- A list of keys that, when typed in Insert mode, cause reindenting of +-- the current line. Only happens if `'indentexpr'` isn't empty. +-- The format is identical to `'cinkeys'` , see |indentkeys-format|. +-- See |C-indenting| and |indent-expression|. +vim.bo.indentkeys = "0{,0},0),0],:,0#,!^F,o,O,e" +vim.bo.indk = vim.bo.indentkeys +-- `'infercase'` `'inf'` boolean (default off) +-- local to buffer +-- When doing keyword completion in insert mode |ins-completion|, and +-- `'ignorecase'` is also on, the case of the match is adjusted depending +-- on the typed text. If the typed text contains a lowercase letter +-- where the match has an upper case letter, the completed part is made +-- lowercase. If the typed text has no lowercase letters and the match +-- has a lowercase letter where the typed text has an uppercase letter, +-- and there is a letter before it, the completed part is made uppercase. +-- With `'noinfercase'` the match is used as-is. +vim.bo.infercase = false +vim.bo.inf = vim.bo.infercase +-- `'iskeyword'` `'isk'` string (default: @,48-57,_,192-255) +-- local to buffer +-- Keywords are used in searching and recognizing with many commands: +-- "w", "*", "[i", etc. It is also used for "\k" in a |pattern|. See +-- `'isfname'` for a description of the format of this option. For `'@'` +-- characters above 255 check the "word" character class (any character +-- that is not white space or punctuation). +-- For C programs you could use "a-z,A-Z,48-57,_,.,-,>". +-- For a help file it is set to all non-blank printable characters except +-- `'*'` , `'"'` and `'|'` (so that CTRL-] on a command finds the help for that +-- command). +-- When the `'lisp'` option is on the `'-'` character is always included. +-- This option also influences syntax highlighting, unless the syntax +-- uses |:syn-iskeyword|. +vim.bo.iskeyword = "@,48-57,_,192-255" +vim.bo.isk = vim.bo.iskeyword +-- `'keymap'` `'kmp'` string (default "") +-- local to buffer +-- Name of a keyboard mapping. See |mbyte-keymap|. +-- Setting this option to a valid keymap name has the side effect of +-- setting `'iminsert'` to one, so that the keymap becomes effective. +-- `'imsearch'` is also set to one, unless it was -1 +-- Only normal file name characters can be used, "/\*?[|<>" are illegal. +vim.bo.keymap = "" +vim.bo.kmp = vim.bo.keymap +-- `'keywordprg'` `'kp'` string (default ":Man", Windows: ":help") +-- global or local to buffer |global-local| +-- Program to use for the |K| command. Environment variables are +-- expanded |:set_env|. ":help" may be used to access the Vim internal +-- help. (Note that previously setting the global option to the empty +-- value did this, which is now deprecated.) +-- When the first character is ":", the command is invoked as a Vim +-- Ex command prefixed with [count]. +-- When "man" or "man -s" is used, Vim will automatically translate +-- a [count] for the "K" command to a section number. +-- See |option-backslash| about including spaces and backslashes. +-- Example: > +-- :set keywordprg=man\ -s +-- :set keywordprg=:Man +-- < This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.bo.keywordprg = ":Man" +vim.bo.kp = vim.bo.keywordprg +-- `'lisp'` boolean (default off) +-- local to buffer +-- Lisp mode: When is typed in insert mode set the indent for +-- the next line to Lisp standards (well, sort of). Also happens with +-- "cc" or "S". `'autoindent'` must also be on for this to work. The `'p'` +-- flag in `'cpoptions'` changes the method of indenting: Vi compatible or +-- better. Also see `'lispwords'` . +-- The `'-'` character is included in keyword characters. Redefines the +-- "=" operator to use this same indentation algorithm rather than +-- calling an external program if `'equalprg'` is empty. +vim.bo.lisp = false +-- `'lispoptions'` `'lop'` string (default "") +-- local to buffer +-- Comma-separated list of items that influence the Lisp indenting when +-- enabled with the |`'lisp'` | option. Currently only one item is +-- supported: +-- expr:1 use `'indentexpr'` for Lisp indenting when it is set +-- expr:0 do not use `'indentexpr'` for Lisp indenting (default) +-- Note that when using `'indentexpr'` the `=` operator indents all the +-- lines, otherwise the first line is not indented (Vi-compatible). +vim.bo.lispoptions = "" +vim.bo.lop = vim.bo.lispoptions +-- `'lispwords'` `'lw'` string (default is very long) +-- global or local to buffer |global-local| +-- Comma-separated list of words that influence the Lisp indenting when +-- enabled with the |`'lisp'` | option. +vim.bo.lispwords = "defun,define,defmacro,set!,lambda,if,case,let,flet,let*,letrec,do,do*,define-syntax,let-syntax,letrec-syntax,destructuring-bind,defpackage,defparameter,defstruct,deftype,defvar,do-all-symbols,do-external-symbols,do-symbols,dolist,dotimes,ecase,etypecase,eval-when,labels,macrolet,multiple-value-bind,multiple-value-call,multiple-value-prog1,multiple-value-setq,prog1,progv,typecase,unless,unwind-protect,when,with-input-from-string,with-open-file,with-open-stream,with-output-to-string,with-package-iterator,define-condition,handler-bind,handler-case,restart-bind,restart-case,with-simple-restart,store-value,use-value,muffle-warning,abort,continue,with-slots,with-slots*,with-accessors,with-accessors*,defclass,defmethod,print-unreadable-object" +vim.bo.lw = vim.bo.lispwords +-- `'makeencoding'` `'menc'` string (default "") +-- global or local to buffer |global-local| +-- Encoding used for reading the output of external commands. When empty, +-- encoding is not converted. +-- This is used for `:make`, `:lmake`, `:grep`, `:lgrep`, `:grepadd`, +-- `:lgrepadd`, `:cfile`, `:cgetfile`, `:caddfile`, `:lfile`, `:lgetfile`, +-- and `:laddfile`. +-- +-- This would be mostly useful when you use MS-Windows. If iconv is +-- enabled, setting `'makeencoding'` to "char" has the same effect as +-- setting to the system locale encoding. Example: > +-- :set makeencoding=char " system locale is used +-- < +vim.bo.makeencoding = "" +vim.bo.menc = vim.bo.makeencoding +-- `'makeprg'` `'mp'` string (default "make") +-- global or local to buffer |global-local| +-- Program to use for the ":make" command. See |:make_makeprg|. +-- This option may contain `'%'` and `'#'` characters (see |:_%| and |:_#|), +-- which are expanded to the current and alternate file name. Use |::S| +-- to escape file names in case they contain special characters. +-- Environment variables are expanded |:set_env|. See |option-backslash| +-- about including spaces and backslashes. +-- Note that a `'|'` must be escaped twice: once for ":set" and once for +-- the interpretation of a command. When you use a filter called +-- "myfilter" do it like this: > +-- :set makeprg=gmake\ \\\|\ myfilter +-- < The placeholder "$*" can be given (even multiple times) to specify +-- where the arguments will be included, for example: > +-- :set makeprg=latex\ \\\\nonstopmode\ \\\\input\\{$*} +-- < This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.bo.makeprg = "make" +vim.bo.mp = vim.bo.makeprg +-- `'matchpairs'` `'mps'` string (default "(:),{:},[:]") +-- local to buffer +-- Characters that form pairs. The |%| command jumps from one to the +-- other. +-- Only character pairs are allowed that are different, thus you cannot +-- jump between two double quotes. +-- The characters must be separated by a colon. +-- The pairs must be separated by a comma. Example for including `'<'` and +-- `'>'` (for HTML): > +-- :set mps+=<:> +-- +-- < A more exotic example, to jump between the `'='` and `';'` in an +-- assignment, useful for languages like C and Java: > +-- :au FileType c,cpp,java set mps+==:; +-- +-- < For a more advanced way of using "%", see the matchit.vim plugin in +-- the $VIMRUNTIME/plugin directory. |add-local-help| +vim.bo.matchpairs = "(:),{:},[:]" +vim.bo.mps = vim.bo.matchpairs +-- `'modeline'` `'ml'` boolean (default: on (off for root)) +-- local to buffer +-- If `'modeline'` is on `'modelines'` gives the number of lines that is +-- checked for set commands. If `'modeline'` is off or `'modelines'` is zero +-- no lines are checked. See |modeline|. +vim.bo.modeline = true +vim.bo.ml = vim.bo.modeline +-- `'modifiable'` `'ma'` boolean (default on) +-- local to buffer +-- When off the buffer contents cannot be changed. The `'fileformat'` and +-- `'fileencoding'` options also can't be changed. +-- Can be reset on startup with the |-M| command line argument. +vim.bo.modifiable = true +vim.bo.ma = vim.bo.modifiable +-- `'modified'` `'mod'` boolean (default off) +-- local to buffer +-- When on, the buffer is considered to be modified. This option is set +-- when: +-- 1. A change was made to the text since it was last written. Using the +-- |undo| command to go back to the original text will reset the +-- option. But undoing changes that were made before writing the +-- buffer will set the option again, since the text is different from +-- when it was written. +-- 2. `'fileformat'` or `'fileencoding'` is different from its original +-- value. The original value is set when the buffer is read or +-- written. A ":set nomodified" command also resets the original +-- values to the current values and the `'modified'` option will be +-- reset. +-- Similarly for `'eol'` and `'bomb'` . +-- This option is not set when a change is made to the buffer as the +-- result of a BufNewFile, BufRead/BufReadPost, BufWritePost, +-- FileAppendPost or VimLeave autocommand event. See |gzip-example| for +-- an explanation. +-- When `'buftype'` is "nowrite" or "nofile" this option may be set, but +-- will be ignored. +-- Note that the text may actually be the same, e.g. `'modified'` is set +-- when using "rA" on an "A". +vim.bo.modified = false +vim.bo.mod = vim.bo.modified +-- `'nrformats'` `'nf'` string (default "bin,hex") +-- local to buffer +-- This defines what bases Vim will consider for numbers when using the +-- CTRL-A and CTRL-X commands for adding to and subtracting from a number +-- respectively; see |CTRL-A| for more info on these commands. +-- alpha If included, single alphabetical characters will be +-- incremented or decremented. This is useful for a list with a +-- letter index a), b), etc. +-- octal If included, numbers that start with a zero will be considered +-- to be octal. Example: Using CTRL-A on "007" results in "010". +-- hex If included, numbers starting with "0x" or "0X" will be +-- considered to be hexadecimal. Example: Using CTRL-X on +-- "0x100" results in "0x0ff". +-- bin If included, numbers starting with "0b" or "0B" will be +-- considered to be binary. Example: Using CTRL-X on +-- "0b1000" subtracts one, resulting in "0b0111". +-- unsigned If included, numbers are recognized as unsigned. Thus a +-- leading dash or negative sign won't be considered as part of +-- the number. Examples: +-- Using CTRL-X on "2020" in "9-2020" results in "9-2019" +-- (without "unsigned" it would become "9-2021"). +-- Using CTRL-A on "2020" in "9-2020" results in "9-2021" +-- (without "unsigned" it would become "9-2019"). +-- Using CTRL-X on "0" or CTRL-A on "18446744073709551615" +-- (2^64 - 1) has no effect, overflow is prevented. +-- Numbers which simply begin with a digit in the range 1-9 are always +-- considered decimal. This also happens for numbers that are not +-- recognized as octal or hex. +vim.bo.nrformats = "bin,hex" +vim.bo.nf = vim.bo.nrformats +-- `'omnifunc'` `'ofu'` string (default: empty) +-- local to buffer +-- This option specifies a function to be used for Insert mode omni +-- completion with CTRL-X CTRL-O. |i_CTRL-X_CTRL-O| +-- See |complete-functions| for an explanation of how the function is +-- invoked and what it should return. The value can be the name of a +-- function, a |lambda| or a |Funcref|. See |option-value-function| for +-- more information. +-- This option is usually set by a filetype plugin: +-- |:filetype-plugin-on| +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.bo.omnifunc = "" +vim.bo.ofu = vim.bo.omnifunc +-- `'path'` `'pa'` string (default on Unix: ".,/usr/include,," +-- other systems: ".,,") +-- global or local to buffer |global-local| +-- This is a list of directories which will be searched when using the +-- |gf|, [f, ]f, ^Wf, |:find|, |:sfind|, |:tabfind| and other commands, +-- provided that the file being searched for has a relative path (not +-- starting with "/", "./" or "../"). The directories in the `'path'` +-- option may be relative or absolute. +-- - Use commas to separate directory names: > +-- :set path=.,/usr/local/include,/usr/include +-- < - Spaces can also be used to separate directory names (for backwards +-- compatibility with version 3.0). To have a space in a directory +-- name, precede it with an extra backslash, and escape the space: > +-- :set path=.,/dir/with\\\ space +-- < - To include a comma in a directory name precede it with an extra +-- backslash: > +-- :set path=.,/dir/with\\,comma +-- < - To search relative to the directory of the current file, use: > +-- :set path=. +-- < - To search in the current directory use an empty string between two +-- commas: > +-- :set path=,, +-- < - A directory name may end in a `':'` or `'/'` . +-- - Environment variables are expanded |:set_env|. +-- - When using |netrw.vim| URLs can be used. For example, adding +-- "https://www.vim.org" will make ":find index.html" work. +-- - Search upwards and downwards in a directory tree using "*", "" and +-- ";". See |file-searching| for info and syntax. +-- - Careful with `'\'` characters, type two to get one in the option: > +-- :set path=.,c:\\include +-- < Or just use `'/'` instead: > +-- :set path=.,c:/include +-- < Don't forget "." or files won't even be found in the same directory as +-- the file! +-- The maximum length is limited. How much depends on the system, mostly +-- it is something like 256 or 1024 characters. +-- You can check if all the include files are found, using the value of +-- `'path'` , see |:checkpath|. +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- directories from the list. This avoids problems when a future version +-- uses another default. To remove the current directory use: > +-- :set path-= +-- < To add the current directory use: > +-- :set path+= +-- < To use an environment variable, you probably need to replace the +-- separator. Here is an example to append $INCL, in which directory +-- names are separated with a semi-colon: > +-- :let &path = &path .. "," .. substitute($INCL, `';'` , `','` , `'g'` ) +-- < Replace the `';'` with a `':'` or whatever separator is used. Note that +-- this doesn't work when $INCL contains a comma or white space. +vim.bo.path = ".,/usr/include,," +vim.bo.pa = vim.bo.path +-- `'preserveindent'` `'pi'` boolean (default off) +-- local to buffer +-- When changing the indent of the current line, preserve as much of the +-- indent structure as possible. Normally the indent is replaced by a +-- series of tabs followed by spaces as required (unless |`'expandtab'` | is +-- enabled, in which case only spaces are used). Enabling this option +-- means the indent will preserve as many existing characters as possible +-- for indenting, and only add additional tabs or spaces as required. +-- `'expandtab'` does not apply to the preserved white space, a Tab remains +-- a Tab. +-- NOTE: When using ">>" multiple times the resulting indent is a mix of +-- tabs and spaces. You might not like this. +-- Also see `'copyindent'` . +-- Use |:retab| to clean up white space. +vim.bo.preserveindent = false +vim.bo.pi = vim.bo.preserveindent +-- `'quoteescape'` `'qe'` string (default "\") +-- local to buffer +-- The characters that are used to escape quotes in a string. Used for +-- objects like a', a" and a` |a'|. +-- When one of the characters in this option is found inside a string, +-- the following character will be skipped. The default value makes the +-- text "foo\"bar\\" considered to be one string. +vim.bo.quoteescape = "\\" +vim.bo.qe = vim.bo.quoteescape +-- `'readonly'` `'ro'` boolean (default off) +-- local to buffer +-- If on, writes fail unless you use a `'!'` . Protects you from +-- accidentally overwriting a file. Default on when Vim is started +-- in read-only mode ("vim -R") or when the executable is called "view". +-- When using ":w!" the `'readonly'` option is reset for the current +-- buffer, unless the `'Z'` flag is in `'cpoptions'` . +-- When using the ":view" command the `'readonly'` option is set for the +-- newly edited buffer. +-- See `'modifiable'` for disallowing changes to the buffer. +vim.bo.readonly = false +vim.bo.ro = vim.bo.readonly +-- `'scrollback'` `'scbk'` number (default: 10000) +-- local to buffer +-- Maximum number of lines kept beyond the visible screen. Lines at the +-- top are deleted if new lines exceed this limit. +-- Minimum is 1, maximum is 100000. +-- Only in |terminal| buffers. +vim.bo.scrollback = -1 +vim.bo.scbk = vim.bo.scrollback +-- `'shiftwidth'` `'sw'` number (default 8) +-- local to buffer +-- Number of spaces to use for each step of (auto)indent. Used for +-- |`'cindent'` |, |>>|, |<<|, etc. +-- When zero the `'ts'` value will be used. Use the |shiftwidth()| +-- function to get the effective shiftwidth value. +vim.bo.shiftwidth = 8 +vim.bo.sw = vim.bo.shiftwidth +-- `'smartindent'` `'si'` boolean (default off) +-- local to buffer +-- Do smart autoindenting when starting a new line. Works for C-like +-- programs, but can also be used for other languages. `'cindent'` does +-- something like this, works better in most cases, but is more strict, +-- see |C-indenting|. When `'cindent'` is on or `'indentexpr'` is set, +-- setting `'si'` has no effect. `'indentexpr'` is a more advanced +-- alternative. +-- Normally `'autoindent'` should also be on when using `'smartindent'` . +-- An indent is automatically inserted: +-- - After a line ending in "{". +-- - After a line starting with a keyword from `'cinwords'` . +-- - Before a line starting with "}" (only with the "O" command). +-- When typing `'}'` as the first character in a new line, that line is +-- given the same indent as the matching "{". +-- When typing `'#'` as the first character in a new line, the indent for +-- that line is removed, the `'#'` is put in the first column. The indent +-- is restored for the next line. If you don't want this, use this +-- mapping: ":inoremap # X^H#", where ^H is entered with CTRL-V CTRL-H. +-- When using the ">>" command, lines starting with `'#'` are not shifted +-- right. +vim.bo.smartindent = false +vim.bo.si = vim.bo.smartindent +-- `'softtabstop'` `'sts'` number (default 0) +-- local to buffer +-- Number of spaces that a counts for while performing editing +-- operations, like inserting a or using . It "feels" like +-- s are being inserted, while in fact a mix of spaces and s is +-- used. This is useful to keep the `'ts'` setting at its standard value +-- of 8, while being able to edit like it is set to `'sts'` . However, +-- commands like "x" still work on the actual characters. +-- When `'sts'` is zero, this feature is off. +-- When `'sts'` is negative, the value of `'shiftwidth'` is used. +-- See also |ins-expandtab|. When `'expandtab'` is not set, the number of +-- spaces is minimized by using s. +-- The `'L'` flag in `'cpoptions'` changes how tabs are used when `'list'` is +-- set. +-- +-- The value of `'softtabstop'` will be ignored if |`'varsofttabstop'` | is set +-- to anything other than an empty string. +vim.bo.softtabstop = 0 +vim.bo.sts = vim.bo.softtabstop +-- `'spellcapcheck'` `'spc'` string (default "[.?!]\_[\])'" \t]\+") +-- local to buffer +-- Pattern to locate the end of a sentence. The following word will be +-- checked to start with a capital letter. If not then it is highlighted +-- with SpellCap |hl-SpellCap| (unless the word is also badly spelled). +-- When this check is not wanted make this option empty. +-- Only used when `'spell'` is set. +-- Be careful with special characters, see |option-backslash| about +-- including spaces and backslashes. +-- To set this option automatically depending on the language, see +-- |set-spc-auto|. +vim.bo.spellcapcheck = "[.?!]\\_[\\])'\"\t ]\\+" +vim.bo.spc = vim.bo.spellcapcheck +-- `'spellfile'` `'spf'` string (default empty) +-- local to buffer +-- Name of the word list file where words are added for the |zg| and |zw| +-- commands. It must end in ".{encoding}.add". You need to include the +-- path, otherwise the file is placed in the current directory. +-- The path may include characters from `'isfname'` , space, comma and `'@'` . +-- +-- It may also be a comma-separated list of names. A count before the +-- |zg| and |zw| commands can be used to access each. This allows using +-- a personal word list file and a project word list file. +-- When a word is added while this option is empty Vim will set it for +-- you: Using the first directory in `'runtimepath'` that is writable. If +-- there is no "spell" directory yet it will be created. For the file +-- name the first language name that appears in `'spelllang'` is used, +-- ignoring the region. +-- The resulting ".spl" file will be used for spell checking, it does not +-- have to appear in `'spelllang'` . +-- Normally one file is used for all regions, but you can add the region +-- name if you want to. However, it will then only be used when +-- `'spellfile'` is set to it, for entries in `'spelllang'` only files +-- without region name will be found. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.bo.spellfile = "" +vim.bo.spf = vim.bo.spellfile +-- `'spelllang'` `'spl'` string (default "en") +-- local to buffer +-- A comma-separated list of word list names. When the `'spell'` option is +-- on spellchecking will be done for these languages. Example: > +-- set spelllang=en_us,nl,medical +-- < This means US English, Dutch and medical words are recognized. Words +-- that are not recognized will be highlighted. +-- The word list name must consist of alphanumeric characters, a dash or +-- an underscore. It should not include a comma or dot. Using a dash is +-- recommended to separate the two letter language name from a +-- specification. Thus "en-rare" is used for rare English words. +-- A region name must come last and have the form "_xx", where "xx" is +-- the two-letter, lower case region name. You can use more than one +-- region by listing them: "en_us,en_ca" supports both US and Canadian +-- English, but not words specific for Australia, New Zealand or Great +-- Britain. (Note: currently en_au and en_nz dictionaries are older than +-- en_ca, en_gb and en_us). +-- If the name "cjk" is included East Asian characters are excluded from +-- spell checking. This is useful when editing text that also has Asian +-- words. +-- Note that the "medical" dictionary does not exist, it is just an +-- example of a longer name. +-- +-- As a special case the name of a .spl file can be given as-is. The +-- first "_xx" in the name is removed and used as the region name +-- (_xx is an underscore, two letters and followed by a non-letter). +-- This is mainly for testing purposes. You must make sure the correct +-- encoding is used, Vim doesn't check it. +-- How the related spell files are found is explained here: |spell-load|. +-- +-- If the |spellfile.vim| plugin is active and you use a language name +-- for which Vim cannot find the .spl file in `'runtimepath'` the plugin +-- will ask you if you want to download the file. +-- +-- After this option has been set successfully, Vim will source the files +-- "spell/LANG.vim" in `'runtimepath'` . "LANG" is the value of `'spelllang'` +-- up to the first character that is not an ASCII letter or number and +-- not a dash. Also see |set-spc-auto|. +vim.bo.spelllang = "en" +vim.bo.spl = vim.bo.spelllang +-- `'spelloptions'` `'spo'` string (default "") +-- local to buffer +-- A comma-separated list of options for spell checking: +-- camel When a word is CamelCased, assume "Cased" is a +-- separate word: every upper-case character in a word +-- that comes after a lower case character indicates the +-- start of a new word. +-- noplainbuffer Only spellcheck a buffer when `'syntax'` is enabled, +-- or when extmarks are set within the buffer. Only +-- designated regions of the buffer are spellchecked in +-- this case. +vim.bo.spelloptions = "" +vim.bo.spo = vim.bo.spelloptions +-- `'suffixesadd'` `'sua'` string (default "") +-- local to buffer +-- Comma-separated list of suffixes, which are used when searching for a +-- file for the "gf", "[I", etc. commands. Example: > +-- :set suffixesadd=.java +-- < +vim.bo.suffixesadd = "" +vim.bo.sua = vim.bo.suffixesadd +-- `'swapfile'` `'swf'` boolean (default on) +-- local to buffer +-- Use a swapfile for the buffer. This option can be reset when a +-- swapfile is not wanted for a specific buffer. For example, with +-- confidential information that even root must not be able to access. +-- Careful: All text will be in memory: +-- - Don't use this for big files. +-- - Recovery will be impossible! +-- A swapfile will only be present when |`'updatecount'` | is non-zero and +-- `'swapfile'` is set. +-- When `'swapfile'` is reset, the swap file for the current buffer is +-- immediately deleted. When `'swapfile'` is set, and `'updatecount'` is +-- non-zero, a swap file is immediately created. +-- Also see |swap-file|. +-- If you want to open a new buffer without creating a swap file for it, +-- use the |:noswapfile| modifier. +-- See `'directory'` for where the swap file is created. +-- +-- This option is used together with `'bufhidden'` and `'buftype'` to +-- specify special kinds of buffers. See |special-buffers|. +vim.bo.swapfile = true +vim.bo.swf = vim.bo.swapfile +-- `'synmaxcol'` `'smc'` number (default 3000) +-- local to buffer +-- Maximum column in which to search for syntax items. In long lines the +-- text after this column is not highlighted and following lines may not +-- be highlighted correctly, because the syntax state is cleared. +-- This helps to avoid very slow redrawing for an XML file that is one +-- long line. +-- Set to zero to remove the limit. +vim.bo.synmaxcol = 3000 +vim.bo.smc = vim.bo.synmaxcol +-- `'syntax'` `'syn'` string (default empty) +-- local to buffer +-- When this option is set, the syntax with this name is loaded, unless +-- syntax highlighting has been switched off with ":syntax off". +-- Otherwise this option does not always reflect the current syntax (the +-- b:current_syntax variable does). +-- This option is most useful in a modeline, for a file which syntax is +-- not automatically recognized. Example, in an IDL file: > +-- /* vim: set syntax=idl : */ +-- < When a dot appears in the value then this separates two filetype +-- names. Example: > +-- /* vim: set syntax=c.doxygen : */ +-- < This will use the "c" syntax first, then the "doxygen" syntax. +-- Note that the second one must be prepared to be loaded as an addition, +-- otherwise it will be skipped. More than one dot may appear. +-- To switch off syntax highlighting for the current file, use: > +-- :set syntax=OFF +-- < To switch syntax highlighting on according to the current value of the +-- `'filetype'` option: > +-- :set syntax=ON +-- < What actually happens when setting the `'syntax'` option is that the +-- Syntax autocommand event is triggered with the value as argument. +-- This option is not copied to another buffer, independent of the `'s'` or +-- `'S'` flag in `'cpoptions'` . +-- Only normal file name characters can be used, "/\*?[|<>" are illegal. +vim.bo.syntax = "" +vim.bo.syn = vim.bo.syntax +-- `'tabstop'` `'ts'` number (default 8) +-- local to buffer +-- Number of spaces that a in the file counts for. Also see +-- the |:retab| command, and the `'softtabstop'` option. +-- +-- Note: Setting `'tabstop'` to any other value than 8 can make your file +-- appear wrong in many places. +-- The value must be more than 0 and less than 10000. +-- +-- There are four main ways to use tabs in Vim: +-- 1. Always keep `'tabstop'` at 8, set `'softtabstop'` and `'shiftwidth'` to 4 +-- (or 3 or whatever you prefer) and use `'noexpandtab'` . Then Vim +-- will use a mix of tabs and spaces, but typing and will +-- behave like a tab appears every 4 (or 3) characters. +-- 2. Set `'tabstop'` and `'shiftwidth'` to whatever you prefer and use +-- `'expandtab'` . This way you will always insert spaces. The +-- formatting will never be messed up when `'tabstop'` is changed. +-- 3. Set `'tabstop'` and `'shiftwidth'` to whatever you prefer and use a +-- |modeline| to set these values when editing the file again. Only +-- works when using Vim to edit the file. +-- 4. Always set `'tabstop'` and `'shiftwidth'` to the same value, and +-- `'noexpandtab'` . This should then work (for initial indents only) +-- for any tabstop setting that people use. It might be nice to have +-- tabs after the first non-blank inserted as spaces if you do this +-- though. Otherwise aligned comments will be wrong when `'tabstop'` is +-- changed. +-- +-- The value of `'tabstop'` will be ignored if |`'vartabstop'` | is set to +-- anything other than an empty string. +vim.bo.tabstop = 8 +vim.bo.ts = vim.bo.tabstop +-- `'tagcase'` `'tc'` string (default "followic") +-- global or local to buffer |global-local| +-- This option specifies how case is handled when searching the tags +-- file: +-- followic Follow the `'ignorecase'` option +-- followscs Follow the `'smartcase'` and `'ignorecase'` options +-- ignore Ignore case +-- match Match case +-- smart Ignore case unless an upper case letter is used +vim.bo.tagcase = "followic" +vim.bo.tc = vim.bo.tagcase +-- `'tagfunc'` `'tfu'` string (default: empty) +-- local to buffer +-- This option specifies a function to be used to perform tag searches. +-- The function gets the tag pattern and should return a List of matching +-- tags. See |tag-function| for an explanation of how to write the +-- function and an example. The value can be the name of a function, a +-- |lambda| or a |Funcref|. See |option-value-function| for more +-- information. +vim.bo.tagfunc = "" +vim.bo.tfu = vim.bo.tagfunc +-- `'tags'` `'tag'` string (default "./tags;,tags") +-- global or local to buffer |global-local| +-- Filenames for the tag command, separated by spaces or commas. To +-- include a space or comma in a file name, precede it with a backslash +-- (see |option-backslash| about including spaces and backslashes). +-- When a file name starts with "./", the `'.'` is replaced with the path +-- of the current file. But only when the `'d'` flag is not included in +-- `'cpoptions'` . Environment variables are expanded |:set_env|. Also see +-- |tags-option|. +-- "*", "" and other wildcards can be used to search for tags files in +-- a directory tree. See |file-searching|. E.g., "/lib//tags" will +-- find all files named "tags" below "/lib". The filename itself cannot +-- contain wildcards, it is used as-is. E.g., "/lib//tags?" will find +-- files called "tags?". +-- The |tagfiles()| function can be used to get a list of the file names +-- actually used. +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- file names from the list. This avoids problems when a future version +-- uses another default. +vim.bo.tags = "./tags;,tags" +vim.bo.tag = vim.bo.tags +-- `'textwidth'` `'tw'` number (default 0) +-- local to buffer +-- Maximum width of text that is being inserted. A longer line will be +-- broken after white space to get this width. A zero value disables +-- this. +-- When `'textwidth'` is zero, `'wrapmargin'` may be used. See also +-- `'formatoptions'` and |ins-textwidth|. +-- When `'formatexpr'` is set it will be used to break the line. +vim.bo.textwidth = 0 +vim.bo.tw = vim.bo.textwidth +-- `'thesaurus'` `'tsr'` string (default "") +-- global or local to buffer |global-local| +-- List of file names, separated by commas, that are used to lookup words +-- for thesaurus completion commands |i_CTRL-X_CTRL-T|. See +-- |compl-thesaurus|. +-- +-- This option is not used if `'thesaurusfunc'` is set, either for the +-- buffer or globally. +-- +-- To include a comma in a file name precede it with a backslash. Spaces +-- after a comma are ignored, otherwise spaces are included in the file +-- name. See |option-backslash| about using backslashes. The use of +-- |:set+=| and |:set-=| is preferred when adding or removing directories +-- from the list. This avoids problems when a future version uses +-- another default. Backticks cannot be used in this option for security +-- reasons. +vim.bo.thesaurus = "" +vim.bo.tsr = vim.bo.thesaurus +-- `'thesaurusfunc'` `'tsrfu'` string (default: empty) +-- global or local to buffer |global-local| +-- This option specifies a function to be used for thesaurus completion +-- with CTRL-X CTRL-T. |i_CTRL-X_CTRL-T| See |compl-thesaurusfunc|. +-- The value can be the name of a function, a |lambda| or a |Funcref|. +-- See |option-value-function| for more information. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.bo.thesaurusfunc = "" +vim.bo.tsrfu = vim.bo.thesaurusfunc +-- `'undofile'` `'udf'` boolean (default off) +-- local to buffer +-- When on, Vim automatically saves undo history to an undo file when +-- writing a buffer to a file, and restores undo history from the same +-- file on buffer read. +-- The directory where the undo file is stored is specified by `'undodir'` . +-- For more information about this feature see |undo-persistence|. +-- The undo file is not read when `'undoreload'` causes the buffer from +-- before a reload to be saved for undo. +-- When `'undofile'` is turned off the undo file is NOT deleted. +vim.bo.undofile = false +vim.bo.udf = vim.bo.undofile +-- `'undolevels'` `'ul'` number (default 1000) +-- global or local to buffer |global-local| +-- Maximum number of changes that can be undone. Since undo information +-- is kept in memory, higher numbers will cause more memory to be used. +-- Nevertheless, a single change can already use a large amount of memory. +-- Set to 0 for Vi compatibility: One level of undo and "u" undoes +-- itself: > +-- set ul=0 +-- < But you can also get Vi compatibility by including the `'u'` flag in +-- `'cpoptions'` , and still be able to use CTRL-R to repeat undo. +-- Also see |undo-two-ways|. +-- Set to -1 for no undo at all. You might want to do this only for the +-- current buffer: > +-- setlocal ul=-1 +-- < This helps when you run out of memory for a single change. +-- +-- The local value is set to -123456 when the global value is to be used. +-- +-- Also see |clear-undo|. +vim.bo.undolevels = 1000 +vim.bo.ul = vim.bo.undolevels +-- `'varsofttabstop'` `'vsts'` string (default "") +-- local to buffer +-- A list of the number of spaces that a counts for while editing, +-- such as inserting a or using . It "feels" like variable- +-- width s are being inserted, while in fact a mixture of spaces +-- and s is used. Tab widths are separated with commas, with the +-- final value applying to all subsequent tabs. +-- +-- For example, when editing assembly language files where statements +-- start in the 9th column and comments in the 41st, it may be useful +-- to use the following: > +-- :set varsofttabstop=8,32,8 +-- < This will set soft tabstops with 8 and 8 + 32 spaces, and 8 more +-- for every column thereafter. +-- +-- Note that the value of |`'softtabstop'` | will be ignored while +-- `'varsofttabstop'` is set. +vim.bo.varsofttabstop = "" +vim.bo.vsts = vim.bo.varsofttabstop +-- `'vartabstop'` `'vts'` string (default "") +-- local to buffer +-- A list of the number of spaces that a in the file counts for, +-- separated by commas. Each value corresponds to one tab, with the +-- final value applying to all subsequent tabs. For example: > +-- :set vartabstop=4,20,10,8 +-- < This will make the first tab 4 spaces wide, the second 20 spaces, +-- the third 10 spaces, and all following tabs 8 spaces. +-- +-- Note that the value of |`'tabstop'` | will be ignored while `'vartabstop'` +-- is set. +vim.bo.vartabstop = "" +vim.bo.vts = vim.bo.vartabstop +-- `'wrapmargin'` `'wm'` number (default 0) +-- local to buffer +-- Number of characters from the right window border where wrapping +-- starts. When typing text beyond this limit, an will be inserted +-- and inserting continues on the next line. +-- Options that add a margin, such as `'number'` and `'foldcolumn'` , cause +-- the text width to be further reduced. +-- When `'textwidth'` is non-zero, this option is not used. +-- See also `'formatoptions'` and |ins-textwidth|. +vim.bo.wrapmargin = 0 +vim.bo.wm = vim.bo.wrapmargin + + +--- @class vim.opt.aleph: vim.Option,number +--- @operator add: vim.opt.aleph +--- @operator sub: vim.opt.aleph +--- @operator pow: vim.opt.aleph +vim.opt.aleph = 224 +vim.opt.al = vim.opt.aleph +--- @return number +function vim.opt.aleph:get()end + +-- `'allowrevins'` `'ari'` boolean (default off) +-- global +-- Allow CTRL-_ in Insert and Command-line mode. This is default off, to +-- avoid that users that accidentally type CTRL-_ instead of SHIFT-_ get +-- into reverse Insert mode, and don't know how to get out. See +-- `'revins'` . +--- @class vim.opt.allowrevins: vim.Option,boolean +--- @operator add: vim.opt.allowrevins +--- @operator sub: vim.opt.allowrevins +--- @operator pow: vim.opt.allowrevins +vim.opt.allowrevins = false +vim.opt.ari = vim.opt.allowrevins +--- @return boolean +function vim.opt.allowrevins:get()end + +-- `'ambiwidth'` `'ambw'` string (default: "single") +-- global +-- Tells Vim what to do with characters with East Asian Width Class +-- Ambiguous (such as Euro, Registered Sign, Copyright Sign, Greek +-- letters, Cyrillic letters). +-- +-- There are currently two possible values: +-- "single": Use the same width as characters in US-ASCII. This is +-- expected by most users. +-- "double": Use twice the width of ASCII characters. +-- +-- The value "double" cannot be used if `'listchars'` or `'fillchars'` +-- contains a character that would be double width. These errors may +-- also be given when calling setcellwidths(). +-- +-- The values are overruled for characters specified with +-- |setcellwidths()|. +-- +-- There are a number of CJK fonts for which the width of glyphs for +-- those characters are solely based on how many octets they take in +-- legacy/traditional CJK encodings. In those encodings, Euro, +-- Registered sign, Greek/Cyrillic letters are represented by two octets, +-- therefore those fonts have "wide" glyphs for them. This is also +-- true of some line drawing characters used to make tables in text +-- file. Therefore, when a CJK font is used for GUI Vim or +-- Vim is running inside a terminal (emulators) that uses a CJK font +-- (or Vim is run inside an xterm invoked with "-cjkwidth" option.), +-- this option should be set to "double" to match the width perceived +-- by Vim with the width of glyphs in the font. Perhaps it also has +-- to be set to "double" under CJK MS-Windows when the system locale is +-- set to one of CJK locales. See Unicode Standard Annex #11 +-- (https://www.unicode.org/reports/tr11). +--- @class vim.opt.ambiwidth: vim.Option,string +--- @operator add: vim.opt.ambiwidth +--- @operator sub: vim.opt.ambiwidth +--- @operator pow: vim.opt.ambiwidth +vim.opt.ambiwidth = "single" +vim.opt.ambw = vim.opt.ambiwidth +--- @return string +function vim.opt.ambiwidth:get()end + +-- `'arabic'` `'arab'` boolean (default off) +-- local to window +-- This option can be set to start editing Arabic text. +-- Setting this option will: +-- - Set the `'rightleft'` option, unless `'termbidi'` is set. +-- - Set the `'arabicshape'` option, unless `'termbidi'` is set. +-- - Set the `'keymap'` option to "arabic"; in Insert mode CTRL-^ toggles +-- between typing English and Arabic key mapping. +-- - Set the `'delcombine'` option +-- +-- Resetting this option will: +-- - Reset the `'rightleft'` option. +-- - Disable the use of `'keymap'` (without changing its value). +-- Note that `'arabicshape'` and `'delcombine'` are not reset (it is a global +-- option). +-- Also see |arabic.txt|. +--- @class vim.opt.arabic: vim.Option,boolean +--- @operator add: vim.opt.arabic +--- @operator sub: vim.opt.arabic +--- @operator pow: vim.opt.arabic +vim.opt.arabic = false +vim.opt.arab = vim.opt.arabic +--- @return boolean +function vim.opt.arabic:get()end + +-- `'arabicshape'` `'arshape'` boolean (default on) +-- global +-- When on and `'termbidi'` is off, the required visual character +-- corrections that need to take place for displaying the Arabic language +-- take effect. Shaping, in essence, gets enabled; the term is a broad +-- one which encompasses: +-- a) the changing/morphing of characters based on their location +-- within a word (initial, medial, final and stand-alone). +-- b) the enabling of the ability to compose characters +-- c) the enabling of the required combining of some characters +-- When disabled the display shows each character's true stand-alone +-- form. +-- Arabic is a complex language which requires other settings, for +-- further details see |arabic.txt|. +--- @class vim.opt.arabicshape: vim.Option,boolean +--- @operator add: vim.opt.arabicshape +--- @operator sub: vim.opt.arabicshape +--- @operator pow: vim.opt.arabicshape +vim.opt.arabicshape = true +vim.opt.arshape = vim.opt.arabicshape +--- @return boolean +function vim.opt.arabicshape:get()end + +-- `'autochdir'` `'acd'` boolean (default off) +-- global +-- When on, Vim will change the current working directory whenever you +-- open a file, switch buffers, delete a buffer or open/close a window. +-- It will change to the directory containing the file which was opened +-- or selected. When a buffer has no name it also has no directory, thus +-- the current directory won't change when navigating to it. +-- Note: When this option is on some plugins may not work. +--- @class vim.opt.autochdir: vim.Option,boolean +--- @operator add: vim.opt.autochdir +--- @operator sub: vim.opt.autochdir +--- @operator pow: vim.opt.autochdir +vim.opt.autochdir = false +vim.opt.acd = vim.opt.autochdir +--- @return boolean +function vim.opt.autochdir:get()end + +-- `'autoindent'` `'ai'` boolean (default on) +-- local to buffer +-- Copy indent from current line when starting a new line (typing +-- in Insert mode or when using the "o" or "O" command). If you do not +-- type anything on the new line except or CTRL-D and then type +-- , CTRL-O or , the indent is deleted again. Moving the cursor +-- to another line has the same effect, unless the `'I'` flag is included +-- in `'cpoptions'` . +-- When autoindent is on, formatting (with the "gq" command or when you +-- reach `'textwidth'` in Insert mode) uses the indentation of the first +-- line. +-- When `'smartindent'` or `'cindent'` is on the indent is changed in +-- a different way. +-- {small difference from Vi: After the indent is deleted when typing +-- or , the cursor position when moving up or down is after the +-- deleted indent; Vi puts the cursor somewhere in the deleted indent}. +--- @class vim.opt.autoindent: vim.Option,boolean +--- @operator add: vim.opt.autoindent +--- @operator sub: vim.opt.autoindent +--- @operator pow: vim.opt.autoindent +vim.opt.autoindent = true +vim.opt.ai = vim.opt.autoindent +--- @return boolean +function vim.opt.autoindent:get()end + +-- `'autoread'` `'ar'` boolean (default on) +-- global or local to buffer |global-local| +-- When a file has been detected to have been changed outside of Vim and +-- it has not been changed inside of Vim, automatically read it again. +-- When the file has been deleted this is not done, so you have the text +-- from before it was deleted. When it appears again then it is read. +-- |timestamp| +-- If this option has a local value, use this command to switch back to +-- using the global value: > +-- :set autoread< +-- < +--- @class vim.opt.autoread: vim.Option,boolean +--- @operator add: vim.opt.autoread +--- @operator sub: vim.opt.autoread +--- @operator pow: vim.opt.autoread +vim.opt.autoread = true +vim.opt.ar = vim.opt.autoread +--- @return boolean +function vim.opt.autoread:get()end + +-- `'autowrite'` `'aw'` boolean (default off) +-- global +-- Write the contents of the file, if it has been modified, on each +-- `:next`, `:rewind`, `:last`, `:first`, `:previous`, `:stop`, +-- `:suspend`, `:tag`, `:!`, `:make`, CTRL-] and CTRL-^ command; and when +-- a `:buffer`, CTRL-O, CTRL-I, '{A-Z0-9}, or `{A-Z0-9} command takes one +-- to another file. +-- A buffer is not written if it becomes hidden, e.g. when `'bufhidden'` is +-- set to "hide" and `:next` is used. +-- Note that for some commands the `'autowrite'` option is not used, see +-- `'autowriteall'` for that. +-- Some buffers will not be written, specifically when `'buftype'` is +-- "nowrite", "nofile", "terminal" or "prompt". +--- @class vim.opt.autowrite: vim.Option,boolean +--- @operator add: vim.opt.autowrite +--- @operator sub: vim.opt.autowrite +--- @operator pow: vim.opt.autowrite +vim.opt.autowrite = false +vim.opt.aw = vim.opt.autowrite +--- @return boolean +function vim.opt.autowrite:get()end + +-- `'autowriteall'` `'awa'` boolean (default off) +-- global +-- Like `'autowrite'` , but also used for commands ":edit", ":enew", ":quit", +-- ":qall", ":exit", ":xit", ":recover" and closing the Vim window. +-- Setting this option also implies that Vim behaves like `'autowrite'` has +-- been set. +--- @class vim.opt.autowriteall: vim.Option,boolean +--- @operator add: vim.opt.autowriteall +--- @operator sub: vim.opt.autowriteall +--- @operator pow: vim.opt.autowriteall +vim.opt.autowriteall = false +vim.opt.awa = vim.opt.autowriteall +--- @return boolean +function vim.opt.autowriteall:get()end + +-- `'background'` `'bg'` string (default "dark") +-- global +-- When set to "dark" or "light", adjusts the default color groups for +-- that background type. The |TUI| or other UI sets this on startup +-- (triggering |OptionSet|) if it can detect the background color. +-- +-- This option does NOT change the background color, it tells Nvim what +-- the "inherited" (terminal/GUI) background looks like. +-- See |:hi-normal| if you want to set the background color explicitly. +-- +-- When a color scheme is loaded (the "g:colors_name" variable is set) +-- setting `'background'` will cause the color scheme to be reloaded. If +-- the color scheme adjusts to the value of `'background'` this will work. +-- However, if the color scheme sets `'background'` itself the effect may +-- be undone. First delete the "g:colors_name" variable when needed. +-- +-- Normally this option would be set in the vimrc file. Possibly +-- depending on the terminal name. Example: > +-- :if $TERM ==# "xterm" +-- : set background=dark +-- :endif +-- < When this option is set, the default settings for the highlight groups +-- will change. To use other settings, place ":highlight" commands AFTER +-- the setting of the `'background'` option. +-- This option is also used in the "$VIMRUNTIME/syntax/syntax.vim" file +-- to select the colors for syntax highlighting. After changing this +-- option, you must load syntax.vim again to see the result. This can be +-- done with ":syntax on". +--- @class vim.opt.background: vim.Option,string +--- @operator add: vim.opt.background +--- @operator sub: vim.opt.background +--- @operator pow: vim.opt.background +vim.opt.background = "dark" +vim.opt.bg = vim.opt.background +--- @return string +function vim.opt.background:get()end + +-- `'backspace'` `'bs'` string (default "indent,eol,start") +-- global +-- Influences the working of , , CTRL-W and CTRL-U in Insert +-- mode. This is a list of items, separated by commas. Each item allows +-- a way to backspace over something: +-- value effect ~ +-- indent allow backspacing over autoindent +-- eol allow backspacing over line breaks (join lines) +-- start allow backspacing over the start of insert; CTRL-W and CTRL-U +-- stop once at the start of insert. +-- nostop like start, except CTRL-W and CTRL-U do not stop at the start of +-- insert. +-- +-- When the value is empty, Vi compatible backspacing is used, none of +-- the ways mentioned for the items above are possible. +-- +-- For backwards compatibility with version 5.4 and earlier: +-- value effect ~ +-- 0 same as ":set backspace=" (Vi compatible) +-- 1 same as ":set backspace=indent,eol" +-- 2 same as ":set backspace=indent,eol,start" +-- 3 same as ":set backspace=indent,eol,nostop" +--- @class vim.opt.backspace: vim.Option,string[] +--- @operator add: vim.opt.backspace +--- @operator sub: vim.opt.backspace +--- @operator pow: vim.opt.backspace +vim.opt.backspace = "indent,eol,start" +vim.opt.bs = vim.opt.backspace +--- @return string[] +function vim.opt.backspace:get()end + +-- `'backup'` `'bk'` boolean (default off) +-- global +-- Make a backup before overwriting a file. Leave it around after the +-- file has been successfully written. If you do not want to keep the +-- backup file, but you do want a backup while the file is being +-- written, reset this option and set the `'writebackup'` option (this is +-- the default). If you do not want a backup file at all reset both +-- options (use this if your file system is almost full). See the +-- |backup-table| for more explanations. +-- When the `'backupskip'` pattern matches, a backup is not made anyway. +-- When `'patchmode'` is set, the backup may be renamed to become the +-- oldest version of a file. +--- @class vim.opt.backup: vim.Option,boolean +--- @operator add: vim.opt.backup +--- @operator sub: vim.opt.backup +--- @operator pow: vim.opt.backup +vim.opt.backup = false +vim.opt.bk = vim.opt.backup +--- @return boolean +function vim.opt.backup:get()end + +-- `'backupcopy'` `'bkc'` string (default: "auto") +-- global or local to buffer |global-local| +-- When writing a file and a backup is made, this option tells how it's +-- done. This is a comma-separated list of words. +-- +-- The main values are: +-- "yes" make a copy of the file and overwrite the original one +-- "no" rename the file and write a new one +-- "auto" one of the previous, what works best +-- +-- Extra values that can be combined with the ones above are: +-- "breaksymlink" always break symlinks when writing +-- "breakhardlink" always break hardlinks when writing +-- +-- Making a copy and overwriting the original file: +-- - Takes extra time to copy the file. +-- + When the file has special attributes, is a (hard/symbolic) link or +-- has a resource fork, all this is preserved. +-- - When the file is a link the backup will have the name of the link, +-- not of the real file. +-- +-- Renaming the file and writing a new one: +-- + It's fast. +-- - Sometimes not all attributes of the file can be copied to the new +-- file. +-- - When the file is a link the new file will not be a link. +-- +-- The "auto" value is the middle way: When Vim sees that renaming the +-- file is possible without side effects (the attributes can be passed on +-- and the file is not a link) that is used. When problems are expected, +-- a copy will be made. +-- +-- The "breaksymlink" and "breakhardlink" values can be used in +-- combination with any of "yes", "no" and "auto". When included, they +-- force Vim to always break either symbolic or hard links by doing +-- exactly what the "no" option does, renaming the original file to +-- become the backup and writing a new file in its place. This can be +-- useful for example in source trees where all the files are symbolic or +-- hard links and any changes should stay in the local source tree, not +-- be propagated back to the original source. +-- +-- One situation where "no" and "auto" will cause problems: A program +-- that opens a file, invokes Vim to edit that file, and then tests if +-- the open file was changed (through the file descriptor) will check the +-- backup file instead of the newly created file. "crontab -e" is an +-- example. +-- +-- When a copy is made, the original file is truncated and then filled +-- with the new text. This means that protection bits, owner and +-- symbolic links of the original file are unmodified. The backup file, +-- however, is a new file, owned by the user who edited the file. The +-- group of the backup is set to the group of the original file. If this +-- fails, the protection bits for the group are made the same as for +-- others. +-- +-- When the file is renamed, this is the other way around: The backup has +-- the same attributes of the original file, and the newly written file +-- is owned by the current user. When the file was a (hard/symbolic) +-- link, the new file will not! That's why the "auto" value doesn't +-- rename when the file is a link. The owner and group of the newly +-- written file will be set to the same ones as the original file, but +-- the system may refuse to do this. In that case the "auto" value will +-- again not rename the file. +--- @class vim.opt.backupcopy: vim.Option,string[] +--- @operator add: vim.opt.backupcopy +--- @operator sub: vim.opt.backupcopy +--- @operator pow: vim.opt.backupcopy +vim.opt.backupcopy = "auto" +vim.opt.bkc = vim.opt.backupcopy +--- @return string[] +function vim.opt.backupcopy:get()end + +-- `'backupdir'` `'bdir'` string (default ".,$XDG_STATE_HOME/nvim/backup//") +-- global +-- List of directories for the backup file, separated with commas. +-- - The backup file will be created in the first directory in the list +-- where this is possible. If none of the directories exist Nvim will +-- attempt to create the last directory in the list. +-- - Empty means that no backup file will be created (`'patchmode'` is +-- impossible!). Writing may fail because of this. +-- - A directory "." means to put the backup file in the same directory +-- as the edited file. +-- - A directory starting with "./" (or ".\" for MS-Windows) means to put +-- the backup file relative to where the edited file is. The leading +-- "." is replaced with the path name of the edited file. +-- ("." inside a directory name has no special meaning). +-- - Spaces after the comma are ignored, other spaces are considered part +-- of the directory name. To have a space at the start of a directory +-- name, precede it with a backslash. +-- - To include a comma in a directory name precede it with a backslash. +-- - A directory name may end in an `'/'` . +-- - For Unix and Win32, if a directory ends in two path separators "//", +-- the swap file name will be built from the complete path to the file +-- with all path separators changed to percent `'%'` signs. This will +-- ensure file name uniqueness in the backup directory. +-- On Win32, it is also possible to end with "\\". However, When a +-- separating comma is following, you must use "//", since "\\" will +-- include the comma in the file name. Therefore it is recommended to +-- use `'//'` , instead of `'\\'` . +-- - Environment variables are expanded |:set_env|. +-- - Careful with `'\'` characters, type one before a space, type two to +-- get one in the option (see |option-backslash|), for example: > +-- :set bdir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces +-- < - For backwards compatibility with Vim version 3.0 a `'>'` at the start +-- of the option is removed. +-- See also `'backup'` and `'writebackup'` options. +-- If you want to hide your backup files on Unix, consider this value: > +-- :set backupdir=./.backup,~/.backup,.,/tmp +-- < You must create a ".backup" directory in each directory and in your +-- home directory for this to work properly. +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- directories from the list. This avoids problems when a future version +-- uses another default. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.backupdir: vim.Option,string[] +--- @operator add: vim.opt.backupdir +--- @operator sub: vim.opt.backupdir +--- @operator pow: vim.opt.backupdir +vim.opt.backupdir = ".,/home/runner/.local/state/nvim/backup//" +vim.opt.bdir = vim.opt.backupdir +--- @return string[] +function vim.opt.backupdir:get()end + +-- `'backupext'` `'bex'` string (default "~") +-- global +-- String which is appended to a file name to make the name of the +-- backup file. The default is quite unusual, because this avoids +-- accidentally overwriting existing files with a backup file. You might +-- prefer using ".bak", but make sure that you don't have files with +-- ".bak" that you want to keep. +-- Only normal file name characters can be used; "/\*?[|<>" are illegal. +-- +-- If you like to keep a lot of backups, you could use a BufWritePre +-- autocommand to change `'backupext'` just before writing the file to +-- include a timestamp. > +-- :au BufWritePre * let &bex = `'-'` .. strftime("%Y%b%d%X") .. `'~'` +-- < Use `'backupdir'` to put the backup in a different directory. +--- @class vim.opt.backupext: vim.Option,string +--- @operator add: vim.opt.backupext +--- @operator sub: vim.opt.backupext +--- @operator pow: vim.opt.backupext +vim.opt.backupext = "~" +vim.opt.bex = vim.opt.backupext +--- @return string +function vim.opt.backupext:get()end + +-- `'backupskip'` `'bsk'` string (default: "$TMPDIR/,$TEMP/*" +-- Unix: "/tmp/,$TMP/" +-- Mac: "/private/tmp/,$TMP/") +-- global +-- A list of file patterns. When one of the patterns matches with the +-- name of the file which is written, no backup file is created. Both +-- the specified file name and the full path name of the file are used. +-- The pattern is used like with |:autocmd|, see |autocmd-pattern|. +-- Watch out for special characters, see |option-backslash|. +-- When $TMPDIR, $TMP or $TEMP is not defined, it is not used for the +-- default value. "/tmp/*" is only used for Unix. +-- +-- WARNING: Not having a backup file means that when Vim fails to write +-- your buffer correctly and then, for whatever reason, Vim exits, you +-- lose both the original file and what you were writing. Only disable +-- backups if you don't care about losing the file. +-- +-- Note that environment variables are not expanded. If you want to use +-- $HOME you must expand it explicitly, e.g.: > +-- :let &backupskip = escape(expand(`'$HOME'` ), `'\'` ) .. `'/tmp/*'` +-- +-- < Note that the default also makes sure that "crontab -e" works (when a +-- backup would be made by renaming the original file crontab won't see +-- the newly created file). Also see `'backupcopy'` and |crontab|. +--- @class vim.opt.backupskip: vim.Option,string[] +--- @operator add: vim.opt.backupskip +--- @operator sub: vim.opt.backupskip +--- @operator pow: vim.opt.backupskip +vim.opt.backupskip = "/tmp/*" +vim.opt.bsk = vim.opt.backupskip +--- @return string[] +function vim.opt.backupskip:get()end + +-- `'belloff'` `'bo'` string (default "all") +-- global +-- Specifies for which events the bell will not be rung. It is a comma- +-- separated list of items. For each item that is present, the bell +-- will be silenced. This is most useful to specify specific events in +-- insert mode to be silenced. +-- +-- item meaning when present ~ +-- all All events. +-- backspace When hitting or and deleting results in an +-- error. +-- cursor Fail to move around using the cursor keys or +-- / in |Insert-mode|. +-- complete Error occurred when using |i_CTRL-X_CTRL-K| or +-- |i_CTRL-X_CTRL-T|. +-- copy Cannot copy char from insert mode using |i_CTRL-Y| or +-- |i_CTRL-E|. +-- ctrlg Unknown Char after in Insert mode. +-- error Other Error occurred (e.g. try to join last line) +-- (mostly used in |Normal-mode| or |Cmdline-mode|). +-- esc hitting in |Normal-mode|. +-- hangul Ignored. +-- lang Calling the beep module for Lua/Mzscheme/TCL. +-- mess No output available for |g<|. +-- showmatch Error occurred for `'showmatch'` function. +-- operator Empty region error |cpo-E|. +-- register Unknown register after in |Insert-mode|. +-- shell Bell from shell output |:!|. +-- spell Error happened on spell suggest. +-- wildmode More matches in |cmdline-completion| available +-- (depends on the `'wildmode'` setting). +-- +-- This is most useful to fine tune when in Insert mode the bell should +-- be rung. For Normal mode and Ex commands, the bell is often rung to +-- indicate that an error occurred. It can be silenced by adding the +-- "error" keyword. +--- @class vim.opt.belloff: vim.Option,string[] +--- @operator add: vim.opt.belloff +--- @operator sub: vim.opt.belloff +--- @operator pow: vim.opt.belloff +vim.opt.belloff = "all" +vim.opt.bo = vim.opt.belloff +--- @return string[] +function vim.opt.belloff:get()end + +-- `'binary'` `'bin'` boolean (default off) +-- local to buffer +-- This option should be set before editing a binary file. You can also +-- use the |-b| Vim argument. When this option is switched on a few +-- options will be changed (also when it already was on): +-- `'textwidth'` will be set to 0 +-- `'wrapmargin'` will be set to 0 +-- `'modeline'` will be off +-- `'expandtab'` will be off +-- Also, `'fileformat'` and `'fileformats'` options will not be used, the +-- file is read and written like `'fileformat'` was "unix" (a single +-- separates lines). +-- The `'fileencoding'` and `'fileencodings'` options will not be used, the +-- file is read without conversion. +-- NOTE: When you start editing a(nother) file while the `'bin'` option is +-- on, settings from autocommands may change the settings again (e.g., +-- `'textwidth'` ), causing trouble when editing. You might want to set +-- `'bin'` again when the file has been loaded. +-- The previous values of these options are remembered and restored when +-- `'bin'` is switched from on to off. Each buffer has its own set of +-- saved option values. +-- To edit a file with `'binary'` set you can use the |++bin| argument. +-- This avoids you have to do ":set bin", which would have effect for all +-- files you edit. +-- When writing a file the for the last line is only written if +-- there was one in the original file (normally Vim appends an to +-- the last line if there is none; this would make the file longer). See +-- the `'endofline'` option. +--- @class vim.opt.binary: vim.Option,boolean +--- @operator add: vim.opt.binary +--- @operator sub: vim.opt.binary +--- @operator pow: vim.opt.binary +vim.opt.binary = false +vim.opt.bin = vim.opt.binary +--- @return boolean +function vim.opt.binary:get()end + +-- `'bomb'` boolean (default off) +-- local to buffer +-- When writing a file and the following conditions are met, a BOM (Byte +-- Order Mark) is prepended to the file: +-- - this option is on +-- - the `'binary'` option is off +-- - `'fileencoding'` is "utf-8", "ucs-2", "ucs-4" or one of the little/big +-- endian variants. +-- Some applications use the BOM to recognize the encoding of the file. +-- Often used for UCS-2 files on MS-Windows. For other applications it +-- causes trouble, for example: "cat file1 file2" makes the BOM of file2 +-- appear halfway through the resulting file. Gcc doesn't accept a BOM. +-- When Vim reads a file and `'fileencodings'` starts with "ucs-bom", a +-- check for the presence of the BOM is done and `'bomb'` set accordingly. +-- Unless `'binary'` is set, it is removed from the first line, so that you +-- don't see it when editing. When you don't change the options, the BOM +-- will be restored when writing the file. +--- @class vim.opt.bomb: vim.Option,boolean +--- @operator add: vim.opt.bomb +--- @operator sub: vim.opt.bomb +--- @operator pow: vim.opt.bomb +vim.opt.bomb = false +--- @return boolean +function vim.opt.bomb:get()end + +-- `'breakat'` `'brk'` string (default " ^I!@*-+;:,./?") +-- global +-- This option lets you choose which characters might cause a line +-- break if `'linebreak'` is on. Only works for ASCII characters. +--- @class vim.opt.breakat: vim.Option,string[] +--- @operator add: vim.opt.breakat +--- @operator sub: vim.opt.breakat +--- @operator pow: vim.opt.breakat +vim.opt.breakat = " \t!@*-+;:,./?" +vim.opt.brk = vim.opt.breakat +--- @return string[] +function vim.opt.breakat:get()end + +-- `'breakindent'` `'bri'` boolean (default off) +-- local to window +-- Every wrapped line will continue visually indented (same amount of +-- space as the beginning of that line), thus preserving horizontal blocks +-- of text. +--- @class vim.opt.breakindent: vim.Option,boolean +--- @operator add: vim.opt.breakindent +--- @operator sub: vim.opt.breakindent +--- @operator pow: vim.opt.breakindent +vim.opt.breakindent = false +vim.opt.bri = vim.opt.breakindent +--- @return boolean +function vim.opt.breakindent:get()end + +-- `'breakindentopt'` `'briopt'` string (default empty) +-- local to window +-- Settings for `'breakindent'` . It can consist of the following optional +-- items and must be separated by a comma: +-- min:{n} Minimum text width that will be kept after +-- applying `'breakindent'` , even if the resulting +-- text should normally be narrower. This prevents +-- text indented almost to the right window border +-- occupying lot of vertical space when broken. +-- (default: 20) +-- shift:{n} After applying `'breakindent'` , the wrapped line's +-- beginning will be shifted by the given number of +-- characters. It permits dynamic French paragraph +-- indentation (negative) or emphasizing the line +-- continuation (positive). +-- (default: 0) +-- sbr Display the `'showbreak'` value before applying the +-- additional indent. +-- (default: off) +-- list:{n} Adds an additional indent for lines that match a +-- numbered or bulleted list (using the +-- `'formatlistpat'` setting). +-- list:-1 Uses the length of a match with `'formatlistpat'` +-- for indentation. +-- (default: 0) +-- column:{n} Indent at column {n}. Will overrule the other +-- sub-options. Note: an additional indent may be +-- added for the `'showbreak'` setting. +-- (default: off) +--- @class vim.opt.breakindentopt: vim.Option,string[] +--- @operator add: vim.opt.breakindentopt +--- @operator sub: vim.opt.breakindentopt +--- @operator pow: vim.opt.breakindentopt +vim.opt.breakindentopt = "" +vim.opt.briopt = vim.opt.breakindentopt +--- @return string[] +function vim.opt.breakindentopt:get()end + +-- `'browsedir'` `'bsdir'` string (default: "last") +-- global +-- Which directory to use for the file browser: +-- last Use same directory as with last file browser, where a +-- file was opened or saved. +-- buffer Use the directory of the related buffer. +-- current Use the current directory. +-- {path} Use the specified directory +--- @class vim.opt.browsedir: vim.Option,string +--- @operator add: vim.opt.browsedir +--- @operator sub: vim.opt.browsedir +--- @operator pow: vim.opt.browsedir +vim.opt.browsedir = "" +vim.opt.bsdir = vim.opt.browsedir +--- @return string +function vim.opt.browsedir:get()end + +-- `'bufhidden'` `'bh'` string (default: "") +-- local to buffer +-- This option specifies what happens when a buffer is no longer +-- displayed in a window: +-- follow the global `'hidden'` option +-- hide hide the buffer (don't unload it), even if `'hidden'` is +-- not set +-- unload unload the buffer, even if `'hidden'` is set; the +-- |:hide| command will also unload the buffer +-- delete delete the buffer from the buffer list, even if +-- `'hidden'` is set; the |:hide| command will also delete +-- the buffer, making it behave like |:bdelete| +-- wipe wipe the buffer from the buffer list, even if +-- `'hidden'` is set; the |:hide| command will also wipe +-- out the buffer, making it behave like |:bwipeout| +-- +-- CAREFUL: when "unload", "delete" or "wipe" is used changes in a buffer +-- are lost without a warning. Also, these values may break autocommands +-- that switch between buffers temporarily. +-- This option is used together with `'buftype'` and `'swapfile'` to specify +-- special kinds of buffers. See |special-buffers|. +--- @class vim.opt.bufhidden: vim.Option,string +--- @operator add: vim.opt.bufhidden +--- @operator sub: vim.opt.bufhidden +--- @operator pow: vim.opt.bufhidden +vim.opt.bufhidden = "" +vim.opt.bh = vim.opt.bufhidden +--- @return string +function vim.opt.bufhidden:get()end + +-- `'buflisted'` `'bl'` boolean (default: on) +-- local to buffer +-- When this option is set, the buffer shows up in the buffer list. If +-- it is reset it is not used for ":bnext", "ls", the Buffers menu, etc. +-- This option is reset by Vim for buffers that are only used to remember +-- a file name or marks. Vim sets it when starting to edit a buffer. +-- But not when moving to a buffer with ":buffer". +--- @class vim.opt.buflisted: vim.Option,boolean +--- @operator add: vim.opt.buflisted +--- @operator sub: vim.opt.buflisted +--- @operator pow: vim.opt.buflisted +vim.opt.buflisted = true +vim.opt.bl = vim.opt.buflisted +--- @return boolean +function vim.opt.buflisted:get()end + +-- `'buftype'` `'bt'` string (default: "") +-- local to buffer +-- The value of this option specifies the type of a buffer: +-- normal buffer +-- acwrite buffer will always be written with |BufWriteCmd|s +-- help help buffer (do not set this manually) +-- nofile buffer is not related to a file, will not be written +-- nowrite buffer will not be written +-- quickfix list of errors |:cwindow| or locations |:lwindow| +-- terminal |terminal-emulator| buffer +-- prompt buffer where only the last line can be edited, meant +-- to be used by a plugin, see |prompt-buffer| +-- +-- This option is used together with `'bufhidden'` and `'swapfile'` to +-- specify special kinds of buffers. See |special-buffers|. +-- Also see |win_gettype()|, which returns the type of the window. +-- +-- Be careful with changing this option, it can have many side effects! +-- One such effect is that Vim will not check the timestamp of the file, +-- if the file is changed by another program this will not be noticed. +-- +-- A "quickfix" buffer is only used for the error list and the location +-- list. This value is set by the |:cwindow| and |:lwindow| commands and +-- you are not supposed to change it. +-- +-- "nofile" and "nowrite" buffers are similar: +-- both: The buffer is not to be written to disk, ":w" doesn't +-- work (":w filename" does work though). +-- both: The buffer is never considered to be |`'modified'` |. +-- There is no warning when the changes will be lost, for +-- example when you quit Vim. +-- both: A swap file is only created when using too much memory +-- (when `'swapfile'` has been reset there is never a swap +-- file). +-- nofile only: The buffer name is fixed, it is not handled like a +-- file name. It is not modified in response to a |:cd| +-- command. +-- both: When using ":e bufname" and already editing "bufname" +-- the buffer is made empty and autocommands are +-- triggered as usual for |:edit|. +-- +-- "acwrite" implies that the buffer name is not related to a file, like +-- "nofile", but it will be written. Thus, in contrast to "nofile" and +-- "nowrite", ":w" does work and a modified buffer can't be abandoned +-- without saving. For writing there must be matching |BufWriteCmd|, +-- |FileWriteCmd| or |FileAppendCmd| autocommands. +--- @class vim.opt.buftype: vim.Option,string +--- @operator add: vim.opt.buftype +--- @operator sub: vim.opt.buftype +--- @operator pow: vim.opt.buftype +vim.opt.buftype = "" +vim.opt.bt = vim.opt.buftype +--- @return string +function vim.opt.buftype:get()end + +-- `'casemap'` `'cmp'` string (default: "internal,keepascii") +-- global +-- Specifies details about changing the case of letters. It may contain +-- these words, separated by a comma: +-- internal Use internal case mapping functions, the current +-- locale does not change the case mapping. When +-- "internal" is omitted, the towupper() and towlower() +-- system library functions are used when available. +-- keepascii For the ASCII characters (0x00 to 0x7f) use the US +-- case mapping, the current locale is not effective. +-- This probably only matters for Turkish. +--- @class vim.opt.casemap: vim.Option,string[] +--- @operator add: vim.opt.casemap +--- @operator sub: vim.opt.casemap +--- @operator pow: vim.opt.casemap +vim.opt.casemap = "internal,keepascii" +vim.opt.cmp = vim.opt.casemap +--- @return string[] +function vim.opt.casemap:get()end + +-- `'cdhome'` `'cdh'` boolean (default: off) +-- global +-- When on, |:cd|, |:tcd| and |:lcd| without an argument changes the +-- current working directory to the |$HOME| directory like in Unix. +-- When off, those commands just print the current directory name. +-- On Unix this option has no effect. +--- @class vim.opt.cdhome: vim.Option,boolean +--- @operator add: vim.opt.cdhome +--- @operator sub: vim.opt.cdhome +--- @operator pow: vim.opt.cdhome +vim.opt.cdhome = false +vim.opt.cdh = vim.opt.cdhome +--- @return boolean +function vim.opt.cdhome:get()end + +-- `'cdpath'` `'cd'` string (default: equivalent to $CDPATH or ",,") +-- global +-- This is a list of directories which will be searched when using the +-- |:cd|, |:tcd| and |:lcd| commands, provided that the directory being +-- searched for has a relative path, not an absolute part starting with +-- "/", "./" or "../", the `'cdpath'` option is not used then. +-- The `'cdpath'` option's value has the same form and semantics as +-- |`'path'` |. Also see |file-searching|. +-- The default value is taken from $CDPATH, with a "," prepended to look +-- in the current directory first. +-- If the default value taken from $CDPATH is not what you want, include +-- a modified version of the following command in your vimrc file to +-- override it: > +-- :let &cdpath = `','` .. substitute(substitute($CDPATH, '[, ]', `'\\\0'` , `'g'` ), `':'` , `','` , `'g'` ) +-- < This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +-- (parts of `'cdpath'` can be passed to the shell to expand file names). +--- @class vim.opt.cdpath: vim.Option,string[] +--- @operator add: vim.opt.cdpath +--- @operator sub: vim.opt.cdpath +--- @operator pow: vim.opt.cdpath +vim.opt.cdpath = ",," +vim.opt.cd = vim.opt.cdpath +--- @return string[] +function vim.opt.cdpath:get()end + +-- `'cedit'` string (default: CTRL-F) +-- global +-- The key used in Command-line Mode to open the command-line window. +-- Only non-printable keys are allowed. +-- The key can be specified as a single character, but it is difficult to +-- type. The preferred way is to use the <> notation. Examples: > +-- :exe "set cedit=\" +-- :exe "set cedit=\" +-- < |Nvi| also has this option, but it only uses the first character. +-- See |cmdwin|. +--- @class vim.opt.cedit: vim.Option,string +--- @operator add: vim.opt.cedit +--- @operator sub: vim.opt.cedit +--- @operator pow: vim.opt.cedit +vim.opt.cedit = "\6" +--- @return string +function vim.opt.cedit:get()end + +-- `'channel'` number (default: 0) +-- local to buffer +-- |channel| connected to the buffer, or 0 if no channel is connected. +-- In a |:terminal| buffer this is the terminal channel. +-- Read-only. +--- @class vim.opt.channel: vim.Option,number +--- @operator add: vim.opt.channel +--- @operator sub: vim.opt.channel +--- @operator pow: vim.opt.channel +vim.opt.channel = 0 +--- @return number +function vim.opt.channel:get()end + +-- `'charconvert'` `'ccv'` string (default "") +-- global +-- An expression that is used for character encoding conversion. It is +-- evaluated when a file that is to be read or has been written has a +-- different encoding from what is desired. +-- `'charconvert'` is not used when the internal iconv() function is +-- supported and is able to do the conversion. Using iconv() is +-- preferred, because it is much faster. +-- `'charconvert'` is not used when reading stdin |--|, because there is no +-- file to convert from. You will have to save the text in a file first. +-- The expression must return zero, false or an empty string for success, +-- non-zero or true for failure. +-- See |encoding-names| for possible encoding names. +-- Additionally, names given in `'fileencodings'` and `'fileencoding'` are +-- used. +-- Conversion between "latin1", "unicode", "ucs-2", "ucs-4" and "utf-8" +-- is done internally by Vim, `'charconvert'` is not used for this. +-- Also used for Unicode conversion. +-- Example: > +-- set charconvert=CharConvert() +-- fun CharConvert() +-- system("recode " +-- \ .. v:charconvert_from .. ".." .. v:charconvert_to +-- \ .. " <" .. v:fname_in .. " >" .. v:fname_out) +-- return v:shell_error +-- endfun +-- < The related Vim variables are: +-- v:charconvert_from name of the current encoding +-- v:charconvert_to name of the desired encoding +-- v:fname_in name of the input file +-- v:fname_out name of the output file +-- Note that v:fname_in and v:fname_out will never be the same. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.charconvert: vim.Option,string +--- @operator add: vim.opt.charconvert +--- @operator sub: vim.opt.charconvert +--- @operator pow: vim.opt.charconvert +vim.opt.charconvert = "" +vim.opt.ccv = vim.opt.charconvert +--- @return string +function vim.opt.charconvert:get()end + +-- `'cindent'` `'cin'` boolean (default off) +-- local to buffer +-- Enables automatic C program indenting. See `'cinkeys'` to set the keys +-- that trigger reindenting in insert mode and `'cinoptions'` to set your +-- preferred indent style. +-- If `'indentexpr'` is not empty, it overrules `'cindent'` . +-- If `'lisp'` is not on and both `'indentexpr'` and `'equalprg'` are empty, +-- the "=" operator indents using this algorithm rather than calling an +-- external program. +-- See |C-indenting|. +-- When you don't like the way `'cindent'` works, try the `'smartindent'` +-- option or `'indentexpr'` . +--- @class vim.opt.cindent: vim.Option,boolean +--- @operator add: vim.opt.cindent +--- @operator sub: vim.opt.cindent +--- @operator pow: vim.opt.cindent +vim.opt.cindent = false +vim.opt.cin = vim.opt.cindent +--- @return boolean +function vim.opt.cindent:get()end + +-- `'cinkeys'` `'cink'` string (default "0{,0},0),0],:,0#,!^F,o,O,e") +-- local to buffer +-- A list of keys that, when typed in Insert mode, cause reindenting of +-- the current line. Only used if `'cindent'` is on and `'indentexpr'` is +-- empty. +-- For the format of this option see |cinkeys-format|. +-- See |C-indenting|. +--- @class vim.opt.cinkeys: vim.Option,string[] +--- @operator add: vim.opt.cinkeys +--- @operator sub: vim.opt.cinkeys +--- @operator pow: vim.opt.cinkeys +vim.opt.cinkeys = "0{,0},0),0],:,0#,!^F,o,O,e" +vim.opt.cink = vim.opt.cinkeys +--- @return string[] +function vim.opt.cinkeys:get()end + +-- `'cinoptions'` `'cino'` string (default "") +-- local to buffer +-- The `'cinoptions'` affect the way `'cindent'` reindents lines in a C +-- program. See |cinoptions-values| for the values of this option, and +-- |C-indenting| for info on C indenting in general. +--- @class vim.opt.cinoptions: vim.Option,string[] +--- @operator add: vim.opt.cinoptions +--- @operator sub: vim.opt.cinoptions +--- @operator pow: vim.opt.cinoptions +vim.opt.cinoptions = "" +vim.opt.cino = vim.opt.cinoptions +--- @return string[] +function vim.opt.cinoptions:get()end + +-- `'cinscopedecls'` `'cinsd'` string (default "public,protected,private") +-- local to buffer +-- Keywords that are interpreted as a C++ scope declaration by |cino-g|. +-- Useful e.g. for working with the Qt framework that defines additional +-- scope declarations "signals", "public slots" and "private slots": > +-- set cinscopedecls+=signals,public\ slots,private\ slots +-- +-- < +--- @class vim.opt.cinscopedecls: vim.Option,string[] +--- @operator add: vim.opt.cinscopedecls +--- @operator sub: vim.opt.cinscopedecls +--- @operator pow: vim.opt.cinscopedecls +vim.opt.cinscopedecls = "public,protected,private" +vim.opt.cinsd = vim.opt.cinscopedecls +--- @return string[] +function vim.opt.cinscopedecls:get()end + +-- `'cinwords'` `'cinw'` string (default "if,else,while,do,for,switch") +-- local to buffer +-- These keywords start an extra indent in the next line when +-- `'smartindent'` or `'cindent'` is set. For `'cindent'` this is only done at +-- an appropriate place (inside {}). +-- Note that `'ignorecase'` isn't used for `'cinwords'` . If case doesn't +-- matter, include the keyword both the uppercase and lowercase: +-- "if,If,IF". +--- @class vim.opt.cinwords: vim.Option,string[] +--- @operator add: vim.opt.cinwords +--- @operator sub: vim.opt.cinwords +--- @operator pow: vim.opt.cinwords +vim.opt.cinwords = "if,else,while,do,for,switch" +vim.opt.cinw = vim.opt.cinwords +--- @return string[] +function vim.opt.cinwords:get()end + +-- `'clipboard'` `'cb'` string (default "") +-- global +-- This option is a list of comma-separated names. +-- These names are recognized: +-- +-- +-- unnamed When included, Vim will use the clipboard register "*" +-- for all yank, delete, change and put operations which +-- would normally go to the unnamed register. When a +-- register is explicitly specified, it will always be +-- used regardless of whether "unnamed" is in `'clipboard'` +-- or not. The clipboard register can always be +-- explicitly accessed using the "* notation. Also see +-- |clipboard|. +-- +-- +-- unnamedplus A variant of the "unnamed" flag which uses the +-- clipboard register "+" (|quoteplus|) instead of +-- register "*" for all yank, delete, change and put +-- operations which would normally go to the unnamed +-- register. When "unnamed" is also included to the +-- option, yank and delete operations (but not put) +-- will additionally copy the text into register +-- `'*'` . See |clipboard|. +--- @class vim.opt.clipboard: vim.Option,string[] +--- @operator add: vim.opt.clipboard +--- @operator sub: vim.opt.clipboard +--- @operator pow: vim.opt.clipboard +vim.opt.clipboard = "" +vim.opt.cb = vim.opt.clipboard +--- @return string[] +function vim.opt.clipboard:get()end + +-- `'cmdheight'` `'ch'` number (default 1) +-- global or local to tab page +-- Number of screen lines to use for the command-line. Helps avoiding +-- |hit-enter| prompts. +-- The value of this option is stored with the tab page, so that each tab +-- page can have a different value. +-- +-- When `'cmdheight'` is zero, there is no command-line unless it is being +-- used. The command-line will cover the last line of the screen when +-- shown. +-- +-- WARNING: `cmdheight=0` is considered experimental. Expect some +-- unwanted behaviour. Some `'shortmess'` flags and similar +-- mechanism might fail to take effect, causing unwanted hit-enter +-- prompts. Some informative messages, both from Nvim itself and +-- plugins, will not be displayed. +--- @class vim.opt.cmdheight: vim.Option,number +--- @operator add: vim.opt.cmdheight +--- @operator sub: vim.opt.cmdheight +--- @operator pow: vim.opt.cmdheight +vim.opt.cmdheight = 1 +vim.opt.ch = vim.opt.cmdheight +--- @return number +function vim.opt.cmdheight:get()end + +-- `'cmdwinheight'` `'cwh'` number (default 7) +-- global +-- Number of screen lines to use for the command-line window. |cmdwin| +--- @class vim.opt.cmdwinheight: vim.Option,number +--- @operator add: vim.opt.cmdwinheight +--- @operator sub: vim.opt.cmdwinheight +--- @operator pow: vim.opt.cmdwinheight +vim.opt.cmdwinheight = 7 +vim.opt.cwh = vim.opt.cmdwinheight +--- @return number +function vim.opt.cmdwinheight:get()end + +-- `'colorcolumn'` `'cc'` string (default "") +-- local to window +-- `'colorcolumn'` is a comma-separated list of screen columns that are +-- highlighted with ColorColumn |hl-ColorColumn|. Useful to align +-- text. Will make screen redrawing slower. +-- The screen column can be an absolute number, or a number preceded with +-- `'+'` or `'-'` , which is added to or subtracted from `'textwidth'` . > +-- +-- :set cc=+1 " highlight column after `'textwidth'` +-- :set cc=+1,+2,+3 " highlight three columns after `'textwidth'` +-- :hi ColorColumn ctermbg=lightgrey guibg=lightgrey +-- < +-- When `'textwidth'` is zero then the items with `'-'` and `'+'` are not used. +-- A maximum of 256 columns are highlighted. +--- @class vim.opt.colorcolumn: vim.Option,string[] +--- @operator add: vim.opt.colorcolumn +--- @operator sub: vim.opt.colorcolumn +--- @operator pow: vim.opt.colorcolumn +vim.opt.colorcolumn = "" +vim.opt.cc = vim.opt.colorcolumn +--- @return string[] +function vim.opt.colorcolumn:get()end + +-- `'columns'` `'co'` number (default 80 or terminal width) +-- global +-- Number of columns of the screen. Normally this is set by the terminal +-- initialization and does not have to be set by hand. +-- When Vim is running in the GUI or in a resizable window, setting this +-- option will cause the window size to be changed. When you only want +-- to use the size for the GUI, put the command in your |ginit.vim| file. +-- When you set this option and Vim is unable to change the physical +-- number of columns of the display, the display may be messed up. For +-- the GUI it is always possible and Vim limits the number of columns to +-- what fits on the screen. You can use this command to get the widest +-- window possible: > +-- :set columns=9999 +-- < Minimum value is 12, maximum value is 10000. +--- @class vim.opt.columns: vim.Option,number +--- @operator add: vim.opt.columns +--- @operator sub: vim.opt.columns +--- @operator pow: vim.opt.columns +vim.opt.columns = 80 +vim.opt.co = vim.opt.columns +--- @return number +function vim.opt.columns:get()end + +-- `'comments'` `'com'` string (default +-- "s1:/,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-") +-- local to buffer +-- A comma-separated list of strings that can start a comment line. See +-- |format-comments|. See |option-backslash| about using backslashes to +-- insert a space. +--- @class vim.opt.comments: vim.Option,string[] +--- @operator add: vim.opt.comments +--- @operator sub: vim.opt.comments +--- @operator pow: vim.opt.comments +vim.opt.comments = "s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-" +vim.opt.com = vim.opt.comments +--- @return string[] +function vim.opt.comments:get()end + +-- `'commentstring'` `'cms'` string (default "") +-- local to buffer +-- A template for a comment. The "%s" in the value is replaced with the +-- comment text. Currently only used to add markers for folding, see +-- |fold-marker|. +--- @class vim.opt.commentstring: vim.Option,string +--- @operator add: vim.opt.commentstring +--- @operator sub: vim.opt.commentstring +--- @operator pow: vim.opt.commentstring +vim.opt.commentstring = "" +vim.opt.cms = vim.opt.commentstring +--- @return string +function vim.opt.commentstring:get()end + +--- @class vim.opt.compatible: vim.Option,boolean +--- @operator add: vim.opt.compatible +--- @operator sub: vim.opt.compatible +--- @operator pow: vim.opt.compatible +vim.opt.compatible = false +vim.opt.cp = vim.opt.compatible +--- @return boolean +function vim.opt.compatible:get()end + +-- `'complete'` `'cpt'` string (default: ".,w,b,u,t") +-- local to buffer +-- This option specifies how keyword completion |ins-completion| works +-- when CTRL-P or CTRL-N are used. It is also used for whole-line +-- completion |i_CTRL-X_CTRL-L|. It indicates the type of completion +-- and the places to scan. It is a comma-separated list of flags: +-- . scan the current buffer (`'wrapscan'` is ignored) +-- w scan buffers from other windows +-- b scan other loaded buffers that are in the buffer list +-- u scan the unloaded buffers that are in the buffer list +-- U scan the buffers that are not in the buffer list +-- k scan the files given with the `'dictionary'` option +-- kspell use the currently active spell checking |spell| +-- k{dict} scan the file {dict}. Several "k" flags can be given, +-- patterns are valid too. For example: > +-- :set cpt=k/usr/dict/*,k~/spanish +-- < s scan the files given with the `'thesaurus'` option +-- s{tsr} scan the file {tsr}. Several "s" flags can be given, patterns +-- are valid too. +-- i scan current and included files +-- d scan current and included files for defined name or macro +-- |i_CTRL-X_CTRL-D| +-- ] tag completion +-- t same as "]" +-- +-- Unloaded buffers are not loaded, thus their autocmds |:autocmd| are +-- not executed, this may lead to unexpected completions from some files +-- (gzipped files for example). Unloaded buffers are not scanned for +-- whole-line completion. +-- +-- As you can see, CTRL-N and CTRL-P can be used to do any `'iskeyword'` - +-- based expansion (e.g., dictionary |i_CTRL-X_CTRL-K|, included patterns +-- |i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]| and normal expansions). +--- @class vim.opt.complete: vim.Option,string[] +--- @operator add: vim.opt.complete +--- @operator sub: vim.opt.complete +--- @operator pow: vim.opt.complete +vim.opt.complete = ".,w,b,u,t" +vim.opt.cpt = vim.opt.complete +--- @return string[] +function vim.opt.complete:get()end + +-- `'completefunc'` `'cfu'` string (default: empty) +-- local to buffer +-- This option specifies a function to be used for Insert mode completion +-- with CTRL-X CTRL-U. |i_CTRL-X_CTRL-U| +-- See |complete-functions| for an explanation of how the function is +-- invoked and what it should return. The value can be the name of a +-- function, a |lambda| or a |Funcref|. See |option-value-function| for +-- more information. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.completefunc: vim.Option,string +--- @operator add: vim.opt.completefunc +--- @operator sub: vim.opt.completefunc +--- @operator pow: vim.opt.completefunc +vim.opt.completefunc = "" +vim.opt.cfu = vim.opt.completefunc +--- @return string +function vim.opt.completefunc:get()end + +-- `'completeopt'` `'cot'` string (default: "menu,preview") +-- global +-- A comma-separated list of options for Insert mode completion +-- |ins-completion|. The supported values are: +-- +-- menu Use a popup menu to show the possible completions. The +-- menu is only shown when there is more than one match and +-- sufficient colors are available. |ins-completion-menu| +-- +-- menuone Use the popup menu also when there is only one match. +-- Useful when there is additional information about the +-- match, e.g., what file it comes from. +-- +-- longest Only insert the longest common text of the matches. If +-- the menu is displayed you can use CTRL-L to add more +-- characters. Whether case is ignored depends on the kind +-- of completion. For buffer text the `'ignorecase'` option is +-- used. +-- +-- preview Show extra information about the currently selected +-- completion in the preview window. Only works in +-- combination with "menu" or "menuone". +-- +-- noinsert Do not insert any text for a match until the user selects +-- a match from the menu. Only works in combination with +-- "menu" or "menuone". No effect if "longest" is present. +-- +-- noselect Do not select a match in the menu, force the user to +-- select one from the menu. Only works in combination with +-- "menu" or "menuone". +--- @class vim.opt.completeopt: vim.Option,string[] +--- @operator add: vim.opt.completeopt +--- @operator sub: vim.opt.completeopt +--- @operator pow: vim.opt.completeopt +vim.opt.completeopt = "menu,preview" +vim.opt.cot = vim.opt.completeopt +--- @return string[] +function vim.opt.completeopt:get()end + +-- `'completeslash'` `'csl'` string (default: "") +-- local to buffer +-- {only for MS-Windows} +-- When this option is set it overrules `'shellslash'` for completion: +-- - When this option is set to "slash", a forward slash is used for path +-- completion in insert mode. This is useful when editing HTML tag, or +-- Makefile with `'noshellslash'` on MS-Windows. +-- - When this option is set to "backslash", backslash is used. This is +-- useful when editing a batch file with `'shellslash'` set on MS-Windows. +-- - When this option is empty, same character is used as for +-- `'shellslash'` . +-- For Insert mode completion the buffer-local value is used. For +-- command line completion the global value is used. +--- @class vim.opt.completeslash: vim.Option,string +--- @operator add: vim.opt.completeslash +--- @operator sub: vim.opt.completeslash +--- @operator pow: vim.opt.completeslash +vim.opt.completeslash = "" +vim.opt.csl = vim.opt.completeslash +--- @return string +function vim.opt.completeslash:get()end + +-- `'concealcursor'` `'cocu'` string (default: "") +-- local to window +-- Sets the modes in which text in the cursor line can also be concealed. +-- When the current mode is listed then concealing happens just like in +-- other lines. +-- n Normal mode +-- v Visual mode +-- i Insert mode +-- c Command line editing, for `'incsearch'` +-- +-- `'v'` applies to all lines in the Visual area, not only the cursor. +-- A useful value is "nc". This is used in help files. So long as you +-- are moving around text is concealed, but when starting to insert text +-- or selecting a Visual area the concealed text is displayed, so that +-- you can see what you are doing. +-- Keep in mind that the cursor position is not always where it's +-- displayed. E.g., when moving vertically it may change column. +--- @class vim.opt.concealcursor: vim.Option,string +--- @operator add: vim.opt.concealcursor +--- @operator sub: vim.opt.concealcursor +--- @operator pow: vim.opt.concealcursor +vim.opt.concealcursor = "" +vim.opt.cocu = vim.opt.concealcursor +--- @return string +function vim.opt.concealcursor:get()end + +-- `'conceallevel'` `'cole'` number (default 0) +-- local to window +-- Determine how text with the "conceal" syntax attribute |:syn-conceal| +-- is shown: +-- +-- Value Effect ~ +-- 0 Text is shown normally +-- 1 Each block of concealed text is replaced with one +-- character. If the syntax item does not have a custom +-- replacement character defined (see |:syn-cchar|) the +-- character defined in `'listchars'` is used. +-- It is highlighted with the "Conceal" highlight group. +-- 2 Concealed text is completely hidden unless it has a +-- custom replacement character defined (see +-- |:syn-cchar|). +-- 3 Concealed text is completely hidden. +-- +-- Note: in the cursor line concealed text is not hidden, so that you can +-- edit and copy the text. This can be changed with the `'concealcursor'` +-- option. +--- @class vim.opt.conceallevel: vim.Option,number +--- @operator add: vim.opt.conceallevel +--- @operator sub: vim.opt.conceallevel +--- @operator pow: vim.opt.conceallevel +vim.opt.conceallevel = 0 +vim.opt.cole = vim.opt.conceallevel +--- @return number +function vim.opt.conceallevel:get()end + +-- `'confirm'` `'cf'` boolean (default off) +-- global +-- When `'confirm'` is on, certain operations that would normally +-- fail because of unsaved changes to a buffer, e.g. ":q" and ":e", +-- instead raise a dialog asking if you wish to save the current +-- file(s). You can still use a ! to unconditionally |abandon| a buffer. +-- If `'confirm'` is off you can still activate confirmation for one +-- command only (this is most useful in mappings) with the |:confirm| +-- command. +-- Also see the |confirm()| function and the `'v'` flag in `'guioptions'` . +--- @class vim.opt.confirm: vim.Option,boolean +--- @operator add: vim.opt.confirm +--- @operator sub: vim.opt.confirm +--- @operator pow: vim.opt.confirm +vim.opt.confirm = false +vim.opt.cf = vim.opt.confirm +--- @return boolean +function vim.opt.confirm:get()end + +-- `'copyindent'` `'ci'` boolean (default off) +-- local to buffer +-- Copy the structure of the existing lines indent when autoindenting a +-- new line. Normally the new indent is reconstructed by a series of +-- tabs followed by spaces as required (unless |`'expandtab'` | is enabled, +-- in which case only spaces are used). Enabling this option makes the +-- new line copy whatever characters were used for indenting on the +-- existing line. `'expandtab'` has no effect on these characters, a Tab +-- remains a Tab. If the new indent is greater than on the existing +-- line, the remaining space is filled in the normal manner. +-- See `'preserveindent'` . +--- @class vim.opt.copyindent: vim.Option,boolean +--- @operator add: vim.opt.copyindent +--- @operator sub: vim.opt.copyindent +--- @operator pow: vim.opt.copyindent +vim.opt.copyindent = false +vim.opt.ci = vim.opt.copyindent +--- @return boolean +function vim.opt.copyindent:get()end + +-- `'cpoptions'` `'cpo'` string (default: "aABceFs_") +-- global +-- A sequence of single character flags. When a character is present +-- this indicates Vi-compatible behavior. This is used for things where +-- not being Vi-compatible is mostly or sometimes preferred. +-- `'cpoptions'` stands for "compatible-options". +-- Commas can be added for readability. +-- To avoid problems with flags that are added in the future, use the +-- "+=" and "-=" feature of ":set" |add-option-flags|. +-- +-- contains behavior ~ +-- +-- a When included, a ":read" command with a file name +-- argument will set the alternate file name for the +-- current window. +-- +-- A When included, a ":write" command with a file name +-- argument will set the alternate file name for the +-- current window. +-- +-- b "\|" in a ":map" command is recognized as the end of +-- the map command. The `'\'` is included in the mapping, +-- the text after the `'|'` is interpreted as the next +-- command. Use a CTRL-V instead of a backslash to +-- include the `'|'` in the mapping. Applies to all +-- mapping, abbreviation, menu and autocmd commands. +-- See also |map_bar|. +-- +-- B A backslash has no special meaning in mappings, +-- abbreviations, user commands and the "to" part of the +-- menu commands. Remove this flag to be able to use a +-- backslash like a CTRL-V. For example, the command +-- ":map X \" results in X being mapped to: +-- `'B'` included: "\^[" (^[ is a real ) +-- `'B'` excluded: "" (5 characters) +-- +-- c Searching continues at the end of any match at the +-- cursor position, but not further than the start of the +-- next line. When not present searching continues +-- one character from the cursor position. With `'c'` +-- "abababababab" only gets three matches when repeating +-- "/abab", without `'c'` there are five matches. +-- +-- C Do not concatenate sourced lines that start with a +-- backslash. See |line-continuation|. +-- +-- d Using "./" in the `'tags'` option doesn't mean to use +-- the tags file relative to the current file, but the +-- tags file in the current directory. +-- +-- D Can't use CTRL-K to enter a digraph after Normal mode +-- commands with a character argument, like |r|, |f| and +-- |t|. +-- +-- e When executing a register with ":@r", always add a +-- to the last line, also when the register is not +-- linewise. If this flag is not present, the register +-- is not linewise and the last line does not end in a +-- , then the last line is put on the command-line +-- and can be edited before hitting . +-- +-- E It is an error when using "y", "d", "c", "g~", "gu" or +-- "gU" on an Empty region. The operators only work when +-- at least one character is to be operated on. Example: +-- This makes "y0" fail in the first column. +-- +-- f When included, a ":read" command with a file name +-- argument will set the file name for the current buffer, +-- if the current buffer doesn't have a file name yet. +-- +-- F When included, a ":write" command with a file name +-- argument will set the file name for the current +-- buffer, if the current buffer doesn't have a file name +-- yet. Also see |cpo-P|. +-- +-- i When included, interrupting the reading of a file will +-- leave it modified. +-- +-- I When moving the cursor up or down just after inserting +-- indent for `'autoindent'` , do not delete the indent. +-- +-- J A |sentence| has to be followed by two spaces after +-- the `'.'` , `'!'` or `'?'` . A is not recognized as +-- white space. +-- +-- K Don't wait for a key code to complete when it is +-- halfway through a mapping. This breaks mapping +-- when only part of the second has been +-- read. It enables cancelling the mapping by typing +-- . +-- +-- l Backslash in a [] range in a search pattern is taken +-- literally, only "\]", "\^", "\-" and "\\" are special. +-- See |/[]| +-- `'l'` included: "/[ \t]" finds , `'\'` and `'t'` +-- `'l'` excluded: "/[ \t]" finds and +-- +-- L When the `'list'` option is set, `'wrapmargin'` , +-- `'textwidth'` , `'softtabstop'` and Virtual Replace mode +-- (see |gR|) count a as two characters, instead of +-- the normal behavior of a . +-- +-- m When included, a showmatch will always wait half a +-- second. When not included, a showmatch will wait half +-- a second or until a character is typed. |`'showmatch'` | +-- +-- M When excluded, "%" matching will take backslashes into +-- account. Thus in "( \( )" and "\( ( \)" the outer +-- parenthesis match. When included "%" ignores +-- backslashes, which is Vi compatible. +-- +-- n When included, the column used for `'number'` and +-- `'relativenumber'` will also be used for text of wrapped +-- lines. +-- +-- o Line offset to search command is not remembered for +-- next search. +-- +-- O Don't complain if a file is being overwritten, even +-- when it didn't exist when editing it. This is a +-- protection against a file unexpectedly created by +-- someone else. Vi didn't complain about this. +-- +-- p Vi compatible Lisp indenting. When not present, a +-- slightly better algorithm is used. +-- +-- P When included, a ":write" command that appends to a +-- file will set the file name for the current buffer, if +-- the current buffer doesn't have a file name yet and +-- the `'F'` flag is also included |cpo-F|. +-- +-- q When joining multiple lines leave the cursor at the +-- position where it would be when joining two lines. +-- +-- r Redo ("." command) uses "/" to repeat a search +-- command, instead of the actually used search string. +-- +-- R Remove marks from filtered lines. Without this flag +-- marks are kept like |:keepmarks| was used. +-- +-- s Set buffer options when entering the buffer for the +-- first time. This is like it is in Vim version 3.0. +-- And it is the default. If not present the options are +-- set when the buffer is created. +-- +-- S Set buffer options always when entering a buffer +-- (except `'readonly'` , `'fileformat'` , `'filetype'` and +-- `'syntax'` ). This is the (most) Vi compatible setting. +-- The options are set to the values in the current +-- buffer. When you change an option and go to another +-- buffer, the value is copied. Effectively makes the +-- buffer options global to all buffers. +-- +-- `'s'` `'S'` copy buffer options +-- no no when buffer created +-- yes no when buffer first entered (default) +-- X yes each time when buffer entered (vi comp.) +-- +-- t Search pattern for the tag command is remembered for +-- "n" command. Otherwise Vim only puts the pattern in +-- the history for search pattern, but doesn't change the +-- last used search pattern. +-- +-- u Undo is Vi compatible. See |undo-two-ways|. +-- +-- v Backspaced characters remain visible on the screen in +-- Insert mode. Without this flag the characters are +-- erased from the screen right away. With this flag the +-- screen newly typed text overwrites backspaced +-- characters. +-- +-- W Don't overwrite a readonly file. When omitted, ":w!" +-- overwrites a readonly file, if possible. +-- +-- x on the command-line executes the command-line. +-- The default in Vim is to abandon the command-line, +-- because normally aborts a command. |c_| +-- +-- X When using a count with "R" the replaced text is +-- deleted only once. Also when repeating "R" with "." +-- and a count. +-- +-- y A yank command can be redone with ".". Think twice if +-- you really want to use this, it may break some +-- plugins, since most people expect "." to only repeat a +-- change. +-- +-- Z When using "w!" while the `'readonly'` option is set, +-- don't reset `'readonly'` . +-- +-- ! When redoing a filter command, use the last used +-- external command, whatever it was. Otherwise the last +-- used -filter- command is used. +-- +-- $ When making a change to one line, don't redisplay the +-- line, but put a `'$'` at the end of the changed text. +-- The changed text will be overwritten when you type the +-- new text. The line is redisplayed if you type any +-- command that moves the cursor from the insertion +-- point. +-- +-- % Vi-compatible matching is done for the "%" command. +-- Does not recognize "#if", "#endif", etc. +-- Does not recognize "/*" and "*/". +-- Parens inside single and double quotes are also +-- counted, causing a string that contains a paren to +-- disturb the matching. For example, in a line like +-- "if (strcmp("foo(", s))" the first paren does not +-- match the last one. When this flag is not included, +-- parens inside single and double quotes are treated +-- specially. When matching a paren outside of quotes, +-- everything inside quotes is ignored. When matching a +-- paren inside quotes, it will find the matching one (if +-- there is one). This works very well for C programs. +-- This flag is also used for other features, such as +-- C-indenting. +-- +-- + When included, a ":write file" command will reset the +-- `'modified'` flag of the buffer, even though the buffer +-- itself may still be different from its file. +-- +-- > When appending to a register, put a line break before +-- the appended text. +-- +-- ; When using |,| or |;| to repeat the last |t| search +-- and the cursor is right in front of the searched +-- character, the cursor won't move. When not included, +-- the cursor would skip over it and jump to the +-- following occurrence. +-- +-- _ When using |cw| on a word, do not include the +-- whitespace following the word in the motion. +--- @class vim.opt.cpoptions: vim.Option,string[] +--- @operator add: vim.opt.cpoptions +--- @operator sub: vim.opt.cpoptions +--- @operator pow: vim.opt.cpoptions +vim.opt.cpoptions = "aABceFs_" +vim.opt.cpo = vim.opt.cpoptions +--- @return string[] +function vim.opt.cpoptions:get()end + +-- `'cursorbind'` `'crb'` boolean (default off) +-- local to window +-- When this option is set, as the cursor in the current +-- window moves other cursorbound windows (windows that also have +-- this option set) move their cursors to the corresponding line and +-- column. This option is useful for viewing the +-- differences between two versions of a file (see `'diff'` ); in diff mode, +-- inserted and deleted lines (though not characters within a line) are +-- taken into account. +--- @class vim.opt.cursorbind: vim.Option,boolean +--- @operator add: vim.opt.cursorbind +--- @operator sub: vim.opt.cursorbind +--- @operator pow: vim.opt.cursorbind +vim.opt.cursorbind = false +vim.opt.crb = vim.opt.cursorbind +--- @return boolean +function vim.opt.cursorbind:get()end + +-- `'cursorcolumn'` `'cuc'` boolean (default off) +-- local to window +-- Highlight the screen column of the cursor with CursorColumn +-- |hl-CursorColumn|. Useful to align text. Will make screen redrawing +-- slower. +-- If you only want the highlighting in the current window you can use +-- these autocommands: > +-- au WinLeave * set nocursorline nocursorcolumn +-- au WinEnter * set cursorline cursorcolumn +-- < +--- @class vim.opt.cursorcolumn: vim.Option,boolean +--- @operator add: vim.opt.cursorcolumn +--- @operator sub: vim.opt.cursorcolumn +--- @operator pow: vim.opt.cursorcolumn +vim.opt.cursorcolumn = false +vim.opt.cuc = vim.opt.cursorcolumn +--- @return boolean +function vim.opt.cursorcolumn:get()end + +-- `'cursorline'` `'cul'` boolean (default off) +-- local to window +-- Highlight the text line of the cursor with CursorLine |hl-CursorLine|. +-- Useful to easily spot the cursor. Will make screen redrawing slower. +-- When Visual mode is active the highlighting isn't used to make it +-- easier to see the selected text. +--- @class vim.opt.cursorline: vim.Option,boolean +--- @operator add: vim.opt.cursorline +--- @operator sub: vim.opt.cursorline +--- @operator pow: vim.opt.cursorline +vim.opt.cursorline = false +vim.opt.cul = vim.opt.cursorline +--- @return boolean +function vim.opt.cursorline:get()end + +-- `'cursorlineopt'` `'culopt'` string (default: "number,line") +-- local to window +-- Comma-separated list of settings for how `'cursorline'` is displayed. +-- Valid values: +-- "line" Highlight the text line of the cursor with +-- CursorLine |hl-CursorLine|. +-- "screenline" Highlight only the screen line of the cursor with +-- CursorLine |hl-CursorLine|. +-- "number" Highlight the line number of the cursor with +-- CursorLineNr |hl-CursorLineNr|. +-- +-- Special value: +-- "both" Alias for the values "line,number". +-- +-- "line" and "screenline" cannot be used together. +--- @class vim.opt.cursorlineopt: vim.Option,string[] +--- @operator add: vim.opt.cursorlineopt +--- @operator sub: vim.opt.cursorlineopt +--- @operator pow: vim.opt.cursorlineopt +vim.opt.cursorlineopt = "both" +vim.opt.culopt = vim.opt.cursorlineopt +--- @return string[] +function vim.opt.cursorlineopt:get()end + +-- `'debug'` string (default "") +-- global +-- These values can be used: +-- msg Error messages that would otherwise be omitted will be given +-- anyway. +-- throw Error messages that would otherwise be omitted will be given +-- anyway and also throw an exception and set |v:errmsg|. +-- beep A message will be given when otherwise only a beep would be +-- produced. +-- The values can be combined, separated by a comma. +-- "msg" and "throw" are useful for debugging `'foldexpr'` , `'formatexpr'` or +-- `'indentexpr'` . +--- @class vim.opt.debug: vim.Option,string +--- @operator add: vim.opt.debug +--- @operator sub: vim.opt.debug +--- @operator pow: vim.opt.debug +vim.opt.debug = "" +--- @return string +function vim.opt.debug:get()end + +-- `'define'` `'def'` string (default "^\sdefine") +-- global or local to buffer |global-local| +-- Pattern to be used to find a macro definition. It is a search +-- pattern, just like for the "/" command. This option is used for the +-- commands like "[i" and "[d" |include-search|. The `'isident'` option is +-- used to recognize the defined name after the match: +-- {match with `'define'` }{non-ID chars}{defined name}{non-ID char} +-- See |option-backslash| about inserting backslashes to include a space +-- or backslash. +-- The default value is for C programs. For C++ this value would be +-- useful, to include const type declarations: > +-- ^\(#\s\s[a-z]*\) +-- < You can also use "\ze" just before the name and continue the pattern +-- to check what is following. E.g. for Javascript, if a function is +-- defined with `func_name = function(args)`: > +-- ^\s=\s*function( +-- < If the function is defined with `func_name : function() {...`: > +-- ^\s[:]\sfunction\s*( +-- < When using the ":set" command, you need to double the backslashes! +-- To avoid that use `:let` with a single quote string: > +-- let &l:define = `'^\s=\s*function('` +-- < +--- @class vim.opt.define: vim.Option,string +--- @operator add: vim.opt.define +--- @operator sub: vim.opt.define +--- @operator pow: vim.opt.define +vim.opt.define = "^\\s*#\\s*define" +vim.opt.def = vim.opt.define +--- @return string +function vim.opt.define:get()end + +-- `'delcombine'` `'deco'` boolean (default off) +-- global +-- If editing Unicode and this option is set, backspace and Normal mode +-- "x" delete each combining character on its own. When it is off (the +-- default) the character along with its combining characters are +-- deleted. +-- Note: When `'delcombine'` is set "xx" may work differently from "2x"! +-- +-- This is useful for Arabic, Hebrew and many other languages where one +-- may have combining characters overtop of base characters, and want +-- to remove only the combining ones. +--- @class vim.opt.delcombine: vim.Option,boolean +--- @operator add: vim.opt.delcombine +--- @operator sub: vim.opt.delcombine +--- @operator pow: vim.opt.delcombine +vim.opt.delcombine = false +vim.opt.deco = vim.opt.delcombine +--- @return boolean +function vim.opt.delcombine:get()end + +-- `'dictionary'` `'dict'` string (default "") +-- global or local to buffer |global-local| +-- List of file names, separated by commas, that are used to lookup words +-- for keyword completion commands |i_CTRL-X_CTRL-K|. Each file should +-- contain a list of words. This can be one word per line, or several +-- words per line, separated by non-keyword characters (white space is +-- preferred). Maximum line length is 510 bytes. +-- +-- When this option is empty or an entry "spell" is present, and spell +-- checking is enabled, words in the word lists for the currently active +-- `'spelllang'` are used. See |spell|. +-- +-- To include a comma in a file name precede it with a backslash. Spaces +-- after a comma are ignored, otherwise spaces are included in the file +-- name. See |option-backslash| about using backslashes. +-- This has nothing to do with the |Dictionary| variable type. +-- Where to find a list of words? +-- - BSD/macOS include the "/usr/share/dict/words" file. +-- - Try "apt install spell" to get the "/usr/share/dict/words" file on +-- apt-managed systems (Debian/Ubuntu). +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- directories from the list. This avoids problems when a future version +-- uses another default. +-- Backticks cannot be used in this option for security reasons. +--- @class vim.opt.dictionary: vim.Option,string[] +--- @operator add: vim.opt.dictionary +--- @operator sub: vim.opt.dictionary +--- @operator pow: vim.opt.dictionary +vim.opt.dictionary = "" +vim.opt.dict = vim.opt.dictionary +--- @return string[] +function vim.opt.dictionary:get()end + +-- `'diff'` boolean (default off) +-- local to window +-- Join the current window in the group of windows that shows differences +-- between files. See |diff-mode|. +--- @class vim.opt.diff: vim.Option,boolean +--- @operator add: vim.opt.diff +--- @operator sub: vim.opt.diff +--- @operator pow: vim.opt.diff +vim.opt.diff = false +--- @return boolean +function vim.opt.diff:get()end + +-- `'diffexpr'` `'dex'` string (default "") +-- global +-- Expression which is evaluated to obtain a diff file (either ed-style +-- or unified-style) from two versions of a file. See |diff-diffexpr|. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.diffexpr: vim.Option,string +--- @operator add: vim.opt.diffexpr +--- @operator sub: vim.opt.diffexpr +--- @operator pow: vim.opt.diffexpr +vim.opt.diffexpr = "" +vim.opt.dex = vim.opt.diffexpr +--- @return string +function vim.opt.diffexpr:get()end + +-- `'diffopt'` `'dip'` string (default "internal,filler,closeoff") +-- global +-- Option settings for diff mode. It can consist of the following items. +-- All are optional. Items must be separated by a comma. +-- +-- filler Show filler lines, to keep the text +-- synchronized with a window that has inserted +-- lines at the same position. Mostly useful +-- when windows are side-by-side and `'scrollbind'` +-- is set. +-- +-- context:{n} Use a context of {n} lines between a change +-- and a fold that contains unchanged lines. +-- When omitted a context of six lines is used. +-- When using zero the context is actually one, +-- since folds require a line in between, also +-- for a deleted line. +-- See |fold-diff|. +-- +-- iblank Ignore changes where lines are all blank. Adds +-- the "-B" flag to the "diff" command if +-- `'diffexpr'` is empty. Check the documentation +-- of the "diff" command for what this does +-- exactly. +-- NOTE: the diff windows will get out of sync, +-- because no differences between blank lines are +-- taken into account. +-- +-- icase Ignore changes in case of text. "a" and "A" +-- are considered the same. Adds the "-i" flag +-- to the "diff" command if `'diffexpr'` is empty. +-- +-- iwhite Ignore changes in amount of white space. Adds +-- the "-b" flag to the "diff" command if +-- `'diffexpr'` is empty. Check the documentation +-- of the "diff" command for what this does +-- exactly. It should ignore adding trailing +-- white space, but not leading white space. +-- +-- iwhiteall Ignore all white space changes. Adds +-- the "-w" flag to the "diff" command if +-- `'diffexpr'` is empty. Check the documentation +-- of the "diff" command for what this does +-- exactly. +-- +-- iwhiteeol Ignore white space changes at end of line. +-- Adds the "-Z" flag to the "diff" command if +-- `'diffexpr'` is empty. Check the documentation +-- of the "diff" command for what this does +-- exactly. +-- +-- horizontal Start diff mode with horizontal splits (unless +-- explicitly specified otherwise). +-- +-- vertical Start diff mode with vertical splits (unless +-- explicitly specified otherwise). +-- +-- closeoff When a window is closed where `'diff'` is set +-- and there is only one window remaining in the +-- same tab page with `'diff'` set, execute +-- `:diffoff` in that window. This undoes a +-- `:diffsplit` command. +-- +-- hiddenoff Do not use diff mode for a buffer when it +-- becomes hidden. +-- +-- foldcolumn:{n} Set the `'foldcolumn'` option to {n} when +-- starting diff mode. Without this 2 is used. +-- +-- followwrap Follow the `'wrap'` option and leave as it is. +-- +-- internal Use the internal diff library. This is +-- ignored when `'diffexpr'` is set. +-- When running out of memory when writing a +-- buffer this item will be ignored for diffs +-- involving that buffer. Set the `'verbose'` +-- option to see when this happens. +-- +-- indent-heuristic +-- Use the indent heuristic for the internal +-- diff library. +-- +-- linematch:{n} Enable a second stage diff on each generated +-- hunk in order to align lines. When the total +-- number of lines in a hunk exceeds {n}, the +-- second stage diff will not be performed as +-- very large hunks can cause noticeable lag. A +-- recommended setting is "linematch:60", as this +-- will enable alignment for a 2 buffer diff with +-- hunks of up to 30 lines each, or a 3 buffer +-- diff with hunks of up to 20 lines each. +-- +-- algorithm:{text} Use the specified diff algorithm with the +-- internal diff engine. Currently supported +-- algorithms are: +-- myers the default algorithm +-- minimal spend extra time to generate the +-- smallest possible diff +-- patience patience diff algorithm +-- histogram histogram diff algorithm +-- +-- Examples: > +-- :set diffopt=internal,filler,context:4 +-- :set diffopt= +-- :set diffopt=internal,filler,foldcolumn:3 +-- :set diffopt-=internal " do NOT use the internal diff parser +-- < +--- @class vim.opt.diffopt: vim.Option,string[] +--- @operator add: vim.opt.diffopt +--- @operator sub: vim.opt.diffopt +--- @operator pow: vim.opt.diffopt +vim.opt.diffopt = "internal,filler,closeoff" +vim.opt.dip = vim.opt.diffopt +--- @return string[] +function vim.opt.diffopt:get()end + +-- `'digraph'` `'dg'` boolean (default off) +-- global +-- Enable the entering of digraphs in Insert mode with {char1} +-- {char2}. See |digraphs|. +--- @class vim.opt.digraph: vim.Option,boolean +--- @operator add: vim.opt.digraph +--- @operator sub: vim.opt.digraph +--- @operator pow: vim.opt.digraph +vim.opt.digraph = false +vim.opt.dg = vim.opt.digraph +--- @return boolean +function vim.opt.digraph:get()end + +-- `'directory'` `'dir'` string (default "$XDG_STATE_HOME/nvim/swap//") +-- global +-- List of directory names for the swap file, separated with commas. +-- +-- Possible items: +-- - The swap file will be created in the first directory where this is +-- possible. If it is not possible in any directory, but last +-- directory listed in the option does not exist, it is created. +-- - Empty means that no swap file will be used (recovery is +-- impossible!) and no |E303| error will be given. +-- - A directory "." means to put the swap file in the same directory as +-- the edited file. On Unix, a dot is prepended to the file name, so +-- it doesn't show in a directory listing. On MS-Windows the "hidden" +-- attribute is set and a dot prepended if possible. +-- - A directory starting with "./" (or ".\" for MS-Windows) means to put +-- the swap file relative to where the edited file is. The leading "." +-- is replaced with the path name of the edited file. +-- - For Unix and Win32, if a directory ends in two path separators "//", +-- the swap file name will be built from the complete path to the file +-- with all path separators replaced by percent `'%'` signs (including +-- the colon following the drive letter on Win32). This will ensure +-- file name uniqueness in the preserve directory. +-- On Win32, it is also possible to end with "\\". However, When a +-- separating comma is following, you must use "//", since "\\" will +-- include the comma in the file name. Therefore it is recommended to +-- use `'//'` , instead of `'\\'` . +-- - Spaces after the comma are ignored, other spaces are considered part +-- of the directory name. To have a space at the start of a directory +-- name, precede it with a backslash. +-- - To include a comma in a directory name precede it with a backslash. +-- - A directory name may end in an `':'` or `'/'` . +-- - Environment variables are expanded |:set_env|. +-- - Careful with `'\'` characters, type one before a space, type two to +-- get one in the option (see |option-backslash|), for example: > +-- :set dir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces +-- < - For backwards compatibility with Vim version 3.0 a `'>'` at the start +-- of the option is removed. +-- Using "." first in the list is recommended. This means that editing +-- the same file twice will result in a warning. Using "/tmp" on Unix is +-- discouraged: When the system crashes you lose the swap file. +-- "/var/tmp" is often not cleared when rebooting, thus is a better +-- choice than "/tmp". But others on the computer may be able to see the +-- files, and it can contain a lot of files, your swap files get lost in +-- the crowd. That is why a "tmp" directory in your home directory is +-- tried first. +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- directories from the list. This avoids problems when a future version +-- uses another default. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.directory: vim.Option,string[] +--- @operator add: vim.opt.directory +--- @operator sub: vim.opt.directory +--- @operator pow: vim.opt.directory +vim.opt.directory = "/home/runner/.local/state/nvim/swap//" +vim.opt.dir = vim.opt.directory +--- @return string[] +function vim.opt.directory:get()end + diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.2.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.2.lua new file mode 100644 index 00000000..f401b8cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.2.lua @@ -0,0 +1,4662 @@ +---@meta + +-- `'display'` `'dy'` string (default "lastline") +-- global +-- Change the way text is displayed. This is a comma-separated list of +-- flags: +-- lastline When included, as much as possible of the last line +-- in a window will be displayed. "@@@" is put in the +-- last columns of the last screen line to indicate the +-- rest of the line is not displayed. +-- truncate Like "lastline", but "@@@" is displayed in the first +-- column of the last screen line. Overrules "lastline". +-- uhex Show unprintable characters hexadecimal as +-- instead of using ^C and ~C. +-- msgsep Obsolete flag. Allowed but takes no effect. |msgsep| +-- +-- When neither "lastline" nor "truncate" is included, a last line that +-- doesn't fit is replaced with "@" lines. +-- +-- The "@" character can be changed by setting the "lastline" item in +-- `'fillchars'` . The character is highlighted with |hl-NonText|. +--- @class vim.opt.display: vim.Option,string[] +--- @operator add: vim.opt.display +--- @operator sub: vim.opt.display +--- @operator pow: vim.opt.display +vim.opt.display = "lastline" +vim.opt.dy = vim.opt.display +--- @return string[] +function vim.opt.display:get()end + +-- `'eadirection'` `'ead'` string (default "both") +-- global +-- Tells when the `'equalalways'` option applies: +-- ver vertically, width of windows is not affected +-- hor horizontally, height of windows is not affected +-- both width and height of windows is affected +--- @class vim.opt.eadirection: vim.Option,string +--- @operator add: vim.opt.eadirection +--- @operator sub: vim.opt.eadirection +--- @operator pow: vim.opt.eadirection +vim.opt.eadirection = "both" +vim.opt.ead = vim.opt.eadirection +--- @return string +function vim.opt.eadirection:get()end + +--- @class vim.opt.edcompatible: vim.Option,boolean +--- @operator add: vim.opt.edcompatible +--- @operator sub: vim.opt.edcompatible +--- @operator pow: vim.opt.edcompatible +vim.opt.edcompatible = false +vim.opt.ed = vim.opt.edcompatible +--- @return boolean +function vim.opt.edcompatible:get()end + +-- `'emoji'` `'emo'` boolean (default: on) +-- global +-- When on all Unicode emoji characters are considered to be full width. +-- This excludes "text emoji" characters, which are normally displayed as +-- single width. Unfortunately there is no good specification for this +-- and it has been determined on trial-and-error basis. Use the +-- |setcellwidths()| function to change the behavior. +--- @class vim.opt.emoji: vim.Option,boolean +--- @operator add: vim.opt.emoji +--- @operator sub: vim.opt.emoji +--- @operator pow: vim.opt.emoji +vim.opt.emoji = true +vim.opt.emo = vim.opt.emoji +--- @return boolean +function vim.opt.emoji:get()end + +-- `'encoding'` `'enc'` +-- String-encoding used internally and for |RPC| communication. +-- Always UTF-8. +-- +-- See `'fileencoding'` to control file-content encoding. +--- @class vim.opt.encoding: vim.Option,string +--- @operator add: vim.opt.encoding +--- @operator sub: vim.opt.encoding +--- @operator pow: vim.opt.encoding +vim.opt.encoding = "utf-8" +vim.opt.enc = vim.opt.encoding +--- @return string +function vim.opt.encoding:get()end + +-- `'endoffile'` `'eof'` boolean (default off) +-- local to buffer +-- Indicates that a CTRL-Z character was found at the end of the file +-- when reading it. Normally only happens when `'fileformat'` is "dos". +-- When writing a file and this option is off and the `'binary'` option +-- is on, or `'fixeol'` option is off, no CTRL-Z will be written at the +-- end of the file. +-- See |eol-and-eof| for example settings. +--- @class vim.opt.endoffile: vim.Option,boolean +--- @operator add: vim.opt.endoffile +--- @operator sub: vim.opt.endoffile +--- @operator pow: vim.opt.endoffile +vim.opt.endoffile = false +vim.opt.eof = vim.opt.endoffile +--- @return boolean +function vim.opt.endoffile:get()end + +-- `'endofline'` `'eol'` boolean (default on) +-- local to buffer +-- When writing a file and this option is off and the `'binary'` option +-- is on, or `'fixeol'` option is off, no will be written for the +-- last line in the file. This option is automatically set or reset when +-- starting to edit a new file, depending on whether file has an +-- for the last line in the file. Normally you don't have to set or +-- reset this option. +-- When `'binary'` is off and `'fixeol'` is on the value is not used when +-- writing the file. When `'binary'` is on or `'fixeol'` is off it is used +-- to remember the presence of a for the last line in the file, so +-- that when you write the file the situation from the original file can +-- be kept. But you can change it if you want to. +-- See |eol-and-eof| for example settings. +--- @class vim.opt.endofline: vim.Option,boolean +--- @operator add: vim.opt.endofline +--- @operator sub: vim.opt.endofline +--- @operator pow: vim.opt.endofline +vim.opt.endofline = true +vim.opt.eol = vim.opt.endofline +--- @return boolean +function vim.opt.endofline:get()end + +-- `'equalalways'` `'ea'` boolean (default on) +-- global +-- When on, all the windows are automatically made the same size after +-- splitting or closing a window. This also happens the moment the +-- option is switched on. When off, splitting a window will reduce the +-- size of the current window and leave the other windows the same. When +-- closing a window the extra lines are given to the window next to it +-- (depending on `'splitbelow'` and `'splitright'` ). +-- When mixing vertically and horizontally split windows, a minimal size +-- is computed and some windows may be larger if there is room. The +-- `'eadirection'` option tells in which direction the size is affected. +-- Changing the height and width of a window can be avoided by setting +-- `'winfixheight'` and `'winfixwidth'` , respectively. +-- If a window size is specified when creating a new window sizes are +-- currently not equalized (it's complicated, but may be implemented in +-- the future). +--- @class vim.opt.equalalways: vim.Option,boolean +--- @operator add: vim.opt.equalalways +--- @operator sub: vim.opt.equalalways +--- @operator pow: vim.opt.equalalways +vim.opt.equalalways = true +vim.opt.ea = vim.opt.equalalways +--- @return boolean +function vim.opt.equalalways:get()end + +-- `'equalprg'` `'ep'` string (default "") +-- global or local to buffer |global-local| +-- External program to use for "=" command. When this option is empty +-- the internal formatting functions are used; either `'lisp'` , `'cindent'` +-- or `'indentexpr'` . +-- Environment variables are expanded |:set_env|. See |option-backslash| +-- about including spaces and backslashes. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.equalprg: vim.Option,string +--- @operator add: vim.opt.equalprg +--- @operator sub: vim.opt.equalprg +--- @operator pow: vim.opt.equalprg +vim.opt.equalprg = "" +vim.opt.ep = vim.opt.equalprg +--- @return string +function vim.opt.equalprg:get()end + +-- `'errorbells'` `'eb'` boolean (default off) +-- global +-- Ring the bell (beep or screen flash) for error messages. This only +-- makes a difference for error messages, the bell will be used always +-- for a lot of errors without a message (e.g., hitting in Normal +-- mode). See `'visualbell'` to make the bell behave like a screen flash +-- or do nothing. See `'belloff'` to finetune when to ring the bell. +--- @class vim.opt.errorbells: vim.Option,boolean +--- @operator add: vim.opt.errorbells +--- @operator sub: vim.opt.errorbells +--- @operator pow: vim.opt.errorbells +vim.opt.errorbells = false +vim.opt.eb = vim.opt.errorbells +--- @return boolean +function vim.opt.errorbells:get()end + +-- `'errorfile'` `'ef'` string (default: "errors.err") +-- global +-- Name of the errorfile for the QuickFix mode (see |:cf|). +-- When the "-q" command-line argument is used, `'errorfile'` is set to the +-- following argument. See |-q|. +-- NOT used for the ":make" command. See `'makeef'` for that. +-- Environment variables are expanded |:set_env|. +-- See |option-backslash| about including spaces and backslashes. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.errorfile: vim.Option,string +--- @operator add: vim.opt.errorfile +--- @operator sub: vim.opt.errorfile +--- @operator pow: vim.opt.errorfile +vim.opt.errorfile = "errors.err" +vim.opt.ef = vim.opt.errorfile +--- @return string +function vim.opt.errorfile:get()end + +-- `'errorformat'` `'efm'` string (default is very long) +-- global or local to buffer |global-local| +-- Scanf-like description of the format for the lines in the error file +-- (see |errorformat|). +--- @class vim.opt.errorformat: vim.Option,string[] +--- @operator add: vim.opt.errorformat +--- @operator sub: vim.opt.errorformat +--- @operator pow: vim.opt.errorformat +vim.opt.errorformat = "%*[^\"]\"%f\"%*\\D%l: %m,\"%f\"%*\\D%l: %m,%-G%f:%l: (Each undeclared identifier is reported only once,%-G%f:%l: for each function it appears in.),%-GIn file included from %f:%l:%c:,%-GIn file included from %f:%l:%c\\,,%-GIn file included from %f:%l:%c,%-GIn file included from %f:%l,%-G%*[ ]from %f:%l:%c,%-G%*[ ]from %f:%l:,%-G%*[ ]from %f:%l\\,,%-G%*[ ]from %f:%l,%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,\"%f\"\\, line %l%*\\D%c%*[^ ] %m,%D%*\\a[%*\\d]: Entering directory %*[`']%f',%X%*\\a[%*\\d]: Leaving directory %*[`']%f',%D%*\\a: Entering directory %*[`']%f',%X%*\\a: Leaving directory %*[`']%f',%DMaking %*\\a in %f,%f|%l| %m" +vim.opt.efm = vim.opt.errorformat +--- @return string[] +function vim.opt.errorformat:get()end + +-- `'eventignore'` `'ei'` string (default "") +-- global +-- A list of autocommand event names, which are to be ignored. +-- When set to "all" or when "all" is one of the items, all autocommand +-- events are ignored, autocommands will not be executed. +-- Otherwise this is a comma-separated list of event names. Example: > +-- :set ei=WinEnter,WinLeave +-- < +--- @class vim.opt.eventignore: vim.Option,string[] +--- @operator add: vim.opt.eventignore +--- @operator sub: vim.opt.eventignore +--- @operator pow: vim.opt.eventignore +vim.opt.eventignore = "" +vim.opt.ei = vim.opt.eventignore +--- @return string[] +function vim.opt.eventignore:get()end + +-- `'expandtab'` `'et'` boolean (default off) +-- local to buffer +-- In Insert mode: Use the appropriate number of spaces to insert a +-- . Spaces are used in indents with the `'>'` and `'<'` commands and +-- when `'autoindent'` is on. To insert a real tab when `'expandtab'` is +-- on, use CTRL-V. See also |:retab| and |ins-expandtab|. +--- @class vim.opt.expandtab: vim.Option,boolean +--- @operator add: vim.opt.expandtab +--- @operator sub: vim.opt.expandtab +--- @operator pow: vim.opt.expandtab +vim.opt.expandtab = false +vim.opt.et = vim.opt.expandtab +--- @return boolean +function vim.opt.expandtab:get()end + +-- `'exrc'` `'ex'` boolean (default off) +-- global +-- Automatically execute .nvim.lua, .nvimrc, and .exrc files in the +-- current directory, if the file is in the |trust| list. Use |:trust| to +-- manage trusted files. See also |vim.secure.read()|. +-- +-- Compare `'exrc'` to |editorconfig|: +-- - `'exrc'` can execute any code; editorconfig only specifies settings. +-- - `'exrc'` is Nvim-specific; editorconfig works in other editors. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.exrc: vim.Option,boolean +--- @operator add: vim.opt.exrc +--- @operator sub: vim.opt.exrc +--- @operator pow: vim.opt.exrc +vim.opt.exrc = false +vim.opt.ex = vim.opt.exrc +--- @return boolean +function vim.opt.exrc:get()end + +-- `'fileencoding'` `'fenc'` string (default: "") +-- local to buffer +-- File-content encoding for the current buffer. Conversion is done with +-- iconv() or as specified with `'charconvert'` . +-- +-- When `'fileencoding'` is not UTF-8, conversion will be done when +-- writing the file. For reading see below. +-- When `'fileencoding'` is empty, the file will be saved with UTF-8 +-- encoding (no conversion when reading or writing a file). +-- +-- WARNING: Conversion to a non-Unicode encoding can cause loss of +-- information! +-- +-- See |encoding-names| for the possible values. Additionally, values may be +-- specified that can be handled by the converter, see +-- |mbyte-conversion|. +-- +-- When reading a file `'fileencoding'` will be set from `'fileencodings'` . +-- To read a file in a certain encoding it won't work by setting +-- `'fileencoding'` , use the |++enc| argument. One exception: when +-- `'fileencodings'` is empty the value of `'fileencoding'` is used. +-- For a new file the global value of `'fileencoding'` is used. +-- +-- Prepending "8bit-" and "2byte-" has no meaning here, they are ignored. +-- When the option is set, the value is converted to lowercase. Thus +-- you can set it with uppercase values too. `'_'` characters are +-- replaced with `'-'` . If a name is recognized from the list at +-- |encoding-names|, it is replaced by the standard name. For example +-- "ISO8859-2" becomes "iso-8859-2". +-- +-- When this option is set, after starting to edit a file, the `'modified'` +-- option is set, because the file would be different when written. +-- +-- Keep in mind that changing `'fenc'` from a modeline happens +-- AFTER the text has been read, thus it applies to when the file will be +-- written. If you do set `'fenc'` in a modeline, you might want to set +-- `'nomodified'` to avoid not being able to ":q". +-- +-- This option cannot be changed when `'modifiable'` is off. +--- @class vim.opt.fileencoding: vim.Option,string +--- @operator add: vim.opt.fileencoding +--- @operator sub: vim.opt.fileencoding +--- @operator pow: vim.opt.fileencoding +vim.opt.fileencoding = "" +vim.opt.fenc = vim.opt.fileencoding +--- @return string +function vim.opt.fileencoding:get()end + +-- `'fileencodings'` `'fencs'` string (default: "ucs-bom,utf-8,default,latin1") +-- global +-- This is a list of character encodings considered when starting to edit +-- an existing file. When a file is read, Vim tries to use the first +-- mentioned character encoding. If an error is detected, the next one +-- in the list is tried. When an encoding is found that works, +-- `'fileencoding'` is set to it. If all fail, `'fileencoding'` is set to +-- an empty string, which means that UTF-8 is used. +-- WARNING: Conversion can cause loss of information! You can use +-- the |++bad| argument to specify what is done with characters +-- that can't be converted. +-- For an empty file or a file with only ASCII characters most encodings +-- will work and the first entry of `'fileencodings'` will be used (except +-- "ucs-bom", which requires the BOM to be present). If you prefer +-- another encoding use an BufReadPost autocommand event to test if your +-- preferred encoding is to be used. Example: > +-- au BufReadPost * if search(`'\S'` , `'w'` ) == 0 | +-- \ set fenc=iso-2022-jp | endif +-- < This sets `'fileencoding'` to "iso-2022-jp" if the file does not contain +-- non-blank characters. +-- When the |++enc| argument is used then the value of `'fileencodings'` is +-- not used. +-- Note that `'fileencodings'` is not used for a new file, the global value +-- of `'fileencoding'` is used instead. You can set it with: > +-- :setglobal fenc=iso-8859-2 +-- < This means that a non-existing file may get a different encoding than +-- an empty file. +-- The special value "ucs-bom" can be used to check for a Unicode BOM +-- (Byte Order Mark) at the start of the file. It must not be preceded +-- by "utf-8" or another Unicode encoding for this to work properly. +-- An entry for an 8-bit encoding (e.g., "latin1") should be the last, +-- because Vim cannot detect an error, thus the encoding is always +-- accepted. +-- The special value "default" can be used for the encoding from the +-- environment. It is useful when your environment uses a non-latin1 +-- encoding, such as Russian. +-- When a file contains an illegal UTF-8 byte sequence it won't be +-- recognized as "utf-8". You can use the |8g8| command to find the +-- illegal byte sequence. +-- WRONG VALUES: WHAT'S WRONG: +-- latin1,utf-8 "latin1" will always be used +-- utf-8,ucs-bom,latin1 BOM won't be recognized in an utf-8 +-- file +-- cp1250,latin1 "cp1250" will always be used +-- If `'fileencodings'` is empty, `'fileencoding'` is not modified. +-- See `'fileencoding'` for the possible values. +-- Setting this option does not have an effect until the next time a file +-- is read. +--- @class vim.opt.fileencodings: vim.Option,string[] +--- @operator add: vim.opt.fileencodings +--- @operator sub: vim.opt.fileencodings +--- @operator pow: vim.opt.fileencodings +vim.opt.fileencodings = "ucs-bom,utf-8,default,latin1" +vim.opt.fencs = vim.opt.fileencodings +--- @return string[] +function vim.opt.fileencodings:get()end + +-- `'fileformat'` `'ff'` string (Windows default: "dos", +-- Unix default: "unix") +-- local to buffer +-- This gives the of the current buffer, which is used for +-- reading/writing the buffer from/to a file: +-- dos +-- unix +-- mac +-- When "dos" is used, CTRL-Z at the end of a file is ignored. +-- See |file-formats| and |file-read|. +-- For the character encoding of the file see `'fileencoding'` . +-- When `'binary'` is set, the value of `'fileformat'` is ignored, file I/O +-- works like it was set to "unix". +-- This option is set automatically when starting to edit a file and +-- `'fileformats'` is not empty and `'binary'` is off. +-- When this option is set, after starting to edit a file, the `'modified'` +-- option is set, because the file would be different when written. +-- This option cannot be changed when `'modifiable'` is off. +--- @class vim.opt.fileformat: vim.Option,string +--- @operator add: vim.opt.fileformat +--- @operator sub: vim.opt.fileformat +--- @operator pow: vim.opt.fileformat +vim.opt.fileformat = "unix" +vim.opt.ff = vim.opt.fileformat +--- @return string +function vim.opt.fileformat:get()end + +-- `'fileformats'` `'ffs'` string (default: +-- Win32: "dos,unix", +-- Unix: "unix,dos") +-- global +-- This gives the end-of-line () formats that will be tried when +-- starting to edit a new buffer and when reading a file into an existing +-- buffer: +-- - When empty, the format defined with `'fileformat'` will be used +-- always. It is not set automatically. +-- - When set to one name, that format will be used whenever a new buffer +-- is opened. `'fileformat'` is set accordingly for that buffer. The +-- `'fileformats'` name will be used when a file is read into an existing +-- buffer, no matter what `'fileformat'` for that buffer is set to. +-- - When more than one name is present, separated by commas, automatic +-- detection will be done when reading a file. When starting to +-- edit a file, a check is done for the : +-- 1. If all lines end in , and `'fileformats'` includes "dos", +-- `'fileformat'` is set to "dos". +-- 2. If a is found and `'fileformats'` includes "unix", `'fileformat'` +-- is set to "unix". Note that when a is found without a +-- preceding , "unix" is preferred over "dos". +-- 3. If `'fileformat'` has not yet been set, and if a is found, and +-- if `'fileformats'` includes "mac", `'fileformat'` is set to "mac". +-- This means that "mac" is only chosen when: +-- "unix" is not present or no is found in the file, and +-- "dos" is not present or no is found in the file. +-- Except: if "unix" was chosen, but there is a before +-- the first , and there appear to be more s than s in +-- the first few lines, "mac" is used. +-- 4. If `'fileformat'` is still not set, the first name from +-- `'fileformats'` is used. +-- When reading a file into an existing buffer, the same is done, but +-- this happens like `'fileformat'` has been set appropriately for that +-- file only, the option is not changed. +-- When `'binary'` is set, the value of `'fileformats'` is not used. +-- +-- When Vim starts up with an empty buffer the first item is used. You +-- can overrule this by setting `'fileformat'` in your .vimrc. +-- +-- For systems with a Dos-like (), when reading files that +-- are ":source"ed and for vimrc files, automatic detection may be +-- done: +-- - When `'fileformats'` is empty, there is no automatic detection. Dos +-- format will be used. +-- - When `'fileformats'` is set to one or more names, automatic detection +-- is done. This is based on the first in the file: If there is a +-- in front of it, Dos format is used, otherwise Unix format is +-- used. +-- Also see |file-formats|. +--- @class vim.opt.fileformats: vim.Option,string[] +--- @operator add: vim.opt.fileformats +--- @operator sub: vim.opt.fileformats +--- @operator pow: vim.opt.fileformats +vim.opt.fileformats = "unix,dos" +vim.opt.ffs = vim.opt.fileformats +--- @return string[] +function vim.opt.fileformats:get()end + +-- `'fileignorecase'` `'fic'` boolean (default on for systems where case in file +-- names is normally ignored) +-- global +-- When set case is ignored when using file names and directories. +-- See `'wildignorecase'` for only ignoring case when doing completion. +--- @class vim.opt.fileignorecase: vim.Option,boolean +--- @operator add: vim.opt.fileignorecase +--- @operator sub: vim.opt.fileignorecase +--- @operator pow: vim.opt.fileignorecase +vim.opt.fileignorecase = false +vim.opt.fic = vim.opt.fileignorecase +--- @return boolean +function vim.opt.fileignorecase:get()end + +-- `'filetype'` `'ft'` string (default: "") +-- local to buffer +-- When this option is set, the FileType autocommand event is triggered. +-- All autocommands that match with the value of this option will be +-- executed. Thus the value of `'filetype'` is used in place of the file +-- name. +-- Otherwise this option does not always reflect the current file type. +-- This option is normally set when the file type is detected. To enable +-- this use the ":filetype on" command. |:filetype| +-- Setting this option to a different value is most useful in a modeline, +-- for a file for which the file type is not automatically recognized. +-- Example, for in an IDL file: > +-- /* vim: set filetype=idl : */ +-- < |FileType| |filetypes| +-- When a dot appears in the value then this separates two filetype +-- names. Example: > +-- /* vim: set filetype=c.doxygen : */ +-- < This will use the "c" filetype first, then the "doxygen" filetype. +-- This works both for filetype plugins and for syntax files. More than +-- one dot may appear. +-- This option is not copied to another buffer, independent of the `'s'` or +-- `'S'` flag in `'cpoptions'` . +-- Only normal file name characters can be used, "/\*?[|<>" are illegal. +--- @class vim.opt.filetype: vim.Option,string +--- @operator add: vim.opt.filetype +--- @operator sub: vim.opt.filetype +--- @operator pow: vim.opt.filetype +vim.opt.filetype = "" +vim.opt.ft = vim.opt.filetype +--- @return string +function vim.opt.filetype:get()end + +-- `'fillchars'` `'fcs'` string (default "") +-- global or local to window |global-local| +-- Characters to fill the statuslines, vertical separators and special +-- lines in the window. +-- It is a comma-separated list of items. Each item has a name, a colon +-- and the value of that item: +-- +-- item default Used for ~ +-- stl ' ' or `'^'` statusline of the current window +-- stlnc ' ' or `'='` statusline of the non-current windows +-- wbr ' ' window bar +-- horiz `'─'` or `'-'` horizontal separators |:split| +-- horizup `'┴'` or `'-'` upwards facing horizontal separator +-- horizdown `'┬'` or `'-'` downwards facing horizontal separator +-- vert `'│'` or `'|'` vertical separators |:vsplit| +-- vertleft `'┤'` or `'|'` left facing vertical separator +-- vertright `'├'` or `'|'` right facing vertical separator +-- verthoriz `'┼'` or `'+'` overlapping vertical and horizontal +-- separator +-- fold `'·'` or `'-'` filling `'foldtext'` +-- foldopen `'-'` mark the beginning of a fold +-- foldclose `'+'` show a closed fold +-- foldsep `'│'` or `'|'` open fold middle marker +-- diff `'-'` deleted lines of the `'diff'` option +-- msgsep ' ' message separator `'display'` +-- eob `'~'` empty lines at the end of a buffer +-- lastline `'@'` `'display'` contains lastline/truncate +-- +-- Any one that is omitted will fall back to the default. For "stl" and +-- "stlnc" the space will be used when there is highlighting, `'^'` or `'='` +-- otherwise. +-- +-- Note that "horiz", "horizup", "horizdown", "vertleft", "vertright" and +-- "verthoriz" are only used when `'laststatus'` is 3, since only vertical +-- window separators are used otherwise. +-- +-- If `'ambiwidth'` is "double" then "horiz", "horizup", "horizdown", +-- "vert", "vertleft", "vertright", "verthoriz", "foldsep" and "fold" +-- default to single-byte alternatives. +-- +-- Example: > +-- :set fillchars=stl:^,stlnc:=,vert:│,fold:·,diff:- +-- < This is similar to the default, except that these characters will also +-- be used when there is highlighting. +-- +-- For the "stl", "stlnc", "foldopen", "foldclose" and "foldsep" items +-- single-byte and multibyte characters are supported. But double-width +-- characters are not supported. +-- +-- The highlighting used for these items: +-- item highlight group ~ +-- stl StatusLine |hl-StatusLine| +-- stlnc StatusLineNC |hl-StatusLineNC| +-- wbr WinBar |hl-WinBar| or |hl-WinBarNC| +-- horiz WinSeparator |hl-WinSeparator| +-- horizup WinSeparator |hl-WinSeparator| +-- horizdown WinSeparator |hl-WinSeparator| +-- vert WinSeparator |hl-WinSeparator| +-- vertleft WinSeparator |hl-WinSeparator| +-- vertright WinSeparator |hl-WinSeparator| +-- verthoriz WinSeparator |hl-WinSeparator| +-- fold Folded |hl-Folded| +-- diff DiffDelete |hl-DiffDelete| +-- eob EndOfBuffer |hl-EndOfBuffer| +-- lastline NonText |hl-NonText| +--- @class vim.opt.fillchars: vim.Option,table +--- @operator add: vim.opt.fillchars +--- @operator sub: vim.opt.fillchars +--- @operator pow: vim.opt.fillchars +vim.opt.fillchars = "" +vim.opt.fcs = vim.opt.fillchars +--- @return table +function vim.opt.fillchars:get()end + +-- `'fixendofline'` `'fixeol'` boolean (default on) +-- local to buffer +-- When writing a file and this option is on, at the end of file +-- will be restored if missing. Turn this option off if you want to +-- preserve the situation from the original file. +-- When the `'binary'` option is set the value of this option doesn't +-- matter. +-- See the `'endofline'` option. +-- See |eol-and-eof| for example settings. +--- @class vim.opt.fixendofline: vim.Option,boolean +--- @operator add: vim.opt.fixendofline +--- @operator sub: vim.opt.fixendofline +--- @operator pow: vim.opt.fixendofline +vim.opt.fixendofline = true +vim.opt.fixeol = vim.opt.fixendofline +--- @return boolean +function vim.opt.fixendofline:get()end + +-- `'foldclose'` `'fcl'` string (default "") +-- global +-- When set to "all", a fold is closed when the cursor isn't in it and +-- its level is higher than `'foldlevel'` . Useful if you want folds to +-- automatically close when moving out of them. +--- @class vim.opt.foldclose: vim.Option,string[] +--- @operator add: vim.opt.foldclose +--- @operator sub: vim.opt.foldclose +--- @operator pow: vim.opt.foldclose +vim.opt.foldclose = "" +vim.opt.fcl = vim.opt.foldclose +--- @return string[] +function vim.opt.foldclose:get()end + +-- `'foldcolumn'` `'fdc'` string (default "0") +-- local to window +-- When and how to draw the foldcolumn. Valid values are: +-- "auto": resize to the minimum amount of folds to display. +-- "auto:[1-9]": resize to accommodate multiple folds up to the +-- selected level +-- 0: to disable foldcolumn +-- "[1-9]": to display a fixed number of columns +-- See |folding|. +--- @class vim.opt.foldcolumn: vim.Option,string +--- @operator add: vim.opt.foldcolumn +--- @operator sub: vim.opt.foldcolumn +--- @operator pow: vim.opt.foldcolumn +vim.opt.foldcolumn = "0" +vim.opt.fdc = vim.opt.foldcolumn +--- @return string +function vim.opt.foldcolumn:get()end + +-- `'foldenable'` `'fen'` boolean (default on) +-- local to window +-- When off, all folds are open. This option can be used to quickly +-- switch between showing all text unfolded and viewing the text with +-- folds (including manually opened or closed folds). It can be toggled +-- with the |zi| command. The `'foldcolumn'` will remain blank when +-- `'foldenable'` is off. +-- This option is set by commands that create a new fold or close a fold. +-- See |folding|. +--- @class vim.opt.foldenable: vim.Option,boolean +--- @operator add: vim.opt.foldenable +--- @operator sub: vim.opt.foldenable +--- @operator pow: vim.opt.foldenable +vim.opt.foldenable = true +vim.opt.fen = vim.opt.foldenable +--- @return boolean +function vim.opt.foldenable:get()end + +-- `'foldexpr'` `'fde'` string (default: "0") +-- local to window +-- The expression used for when `'foldmethod'` is "expr". It is evaluated +-- for each line to obtain its fold level. See |fold-expr|. +-- +-- The expression will be evaluated in the |sandbox| if set from a +-- modeline, see |sandbox-option|. +-- This option can't be set from a |modeline| when the `'diff'` option is +-- on or the `'modelineexpr'` option is off. +-- +-- It is not allowed to change text or jump to another window while +-- evaluating `'foldexpr'` |textlock|. +--- @class vim.opt.foldexpr: vim.Option,string +--- @operator add: vim.opt.foldexpr +--- @operator sub: vim.opt.foldexpr +--- @operator pow: vim.opt.foldexpr +vim.opt.foldexpr = "0" +vim.opt.fde = vim.opt.foldexpr +--- @return string +function vim.opt.foldexpr:get()end + +-- `'foldignore'` `'fdi'` string (default: "#") +-- local to window +-- Used only when `'foldmethod'` is "indent". Lines starting with +-- characters in `'foldignore'` will get their fold level from surrounding +-- lines. White space is skipped before checking for this character. +-- The default "#" works well for C programs. See |fold-indent|. +--- @class vim.opt.foldignore: vim.Option,string +--- @operator add: vim.opt.foldignore +--- @operator sub: vim.opt.foldignore +--- @operator pow: vim.opt.foldignore +vim.opt.foldignore = "#" +vim.opt.fdi = vim.opt.foldignore +--- @return string +function vim.opt.foldignore:get()end + +-- `'foldlevel'` `'fdl'` number (default: 0) +-- local to window +-- Sets the fold level: Folds with a higher level will be closed. +-- Setting this option to zero will close all folds. Higher numbers will +-- close fewer folds. +-- This option is set by commands like |zm|, |zM| and |zR|. +-- See |fold-foldlevel|. +--- @class vim.opt.foldlevel: vim.Option,number +--- @operator add: vim.opt.foldlevel +--- @operator sub: vim.opt.foldlevel +--- @operator pow: vim.opt.foldlevel +vim.opt.foldlevel = 0 +vim.opt.fdl = vim.opt.foldlevel +--- @return number +function vim.opt.foldlevel:get()end + +-- `'foldlevelstart'` `'fdls'` number (default: -1) +-- global +-- Sets `'foldlevel'` when starting to edit another buffer in a window. +-- Useful to always start editing with all folds closed (value zero), +-- some folds closed (one) or no folds closed (99). +-- This is done before reading any modeline, thus a setting in a modeline +-- overrules this option. Starting to edit a file for |diff-mode| also +-- ignores this option and closes all folds. +-- It is also done before BufReadPre autocommands, to allow an autocmd to +-- overrule the `'foldlevel'` value for specific files. +-- When the value is negative, it is not used. +--- @class vim.opt.foldlevelstart: vim.Option,number +--- @operator add: vim.opt.foldlevelstart +--- @operator sub: vim.opt.foldlevelstart +--- @operator pow: vim.opt.foldlevelstart +vim.opt.foldlevelstart = -1 +vim.opt.fdls = vim.opt.foldlevelstart +--- @return number +function vim.opt.foldlevelstart:get()end + +-- `'foldmarker'` `'fmr'` string (default: "{{{,}}}") +-- local to window +-- The start and end marker used when `'foldmethod'` is "marker". There +-- must be one comma, which separates the start and end marker. The +-- marker is a literal string (a regular expression would be too slow). +-- See |fold-marker|. +--- @class vim.opt.foldmarker: vim.Option,string[] +--- @operator add: vim.opt.foldmarker +--- @operator sub: vim.opt.foldmarker +--- @operator pow: vim.opt.foldmarker +vim.opt.foldmarker = "{{{,}}}" +vim.opt.fmr = vim.opt.foldmarker +--- @return string[] +function vim.opt.foldmarker:get()end + +-- `'foldmethod'` `'fdm'` string (default: "manual") +-- local to window +-- The kind of folding used for the current window. Possible values: +-- |fold-manual| manual Folds are created manually. +-- |fold-indent| indent Lines with equal indent form a fold. +-- |fold-expr| expr `'foldexpr'` gives the fold level of a line. +-- |fold-marker| marker Markers are used to specify folds. +-- |fold-syntax| syntax Syntax highlighting items specify folds. +-- |fold-diff| diff Fold text that is not changed. +--- @class vim.opt.foldmethod: vim.Option,string +--- @operator add: vim.opt.foldmethod +--- @operator sub: vim.opt.foldmethod +--- @operator pow: vim.opt.foldmethod +vim.opt.foldmethod = "manual" +vim.opt.fdm = vim.opt.foldmethod +--- @return string +function vim.opt.foldmethod:get()end + +-- `'foldminlines'` `'fml'` number (default: 1) +-- local to window +-- Sets the number of screen lines above which a fold can be displayed +-- closed. Also for manually closed folds. With the default value of +-- one a fold can only be closed if it takes up two or more screen lines. +-- Set to zero to be able to close folds of just one screen line. +-- Note that this only has an effect on what is displayed. After using +-- "zc" to close a fold, which is displayed open because it's smaller +-- than `'foldminlines'` , a following "zc" may close a containing fold. +--- @class vim.opt.foldminlines: vim.Option,number +--- @operator add: vim.opt.foldminlines +--- @operator sub: vim.opt.foldminlines +--- @operator pow: vim.opt.foldminlines +vim.opt.foldminlines = 1 +vim.opt.fml = vim.opt.foldminlines +--- @return number +function vim.opt.foldminlines:get()end + +-- `'foldnestmax'` `'fdn'` number (default: 20) +-- local to window +-- Sets the maximum nesting of folds for the "indent" and "syntax" +-- methods. This avoids that too many folds will be created. Using more +-- than 20 doesn't work, because the internal limit is 20. +--- @class vim.opt.foldnestmax: vim.Option,number +--- @operator add: vim.opt.foldnestmax +--- @operator sub: vim.opt.foldnestmax +--- @operator pow: vim.opt.foldnestmax +vim.opt.foldnestmax = 20 +vim.opt.fdn = vim.opt.foldnestmax +--- @return number +function vim.opt.foldnestmax:get()end + +-- `'foldopen'` `'fdo'` string (default: "block,hor,mark,percent,quickfix, +-- search,tag,undo") +-- global +-- Specifies for which type of commands folds will be opened, if the +-- command moves the cursor into a closed fold. It is a comma-separated +-- list of items. +-- NOTE: When the command is part of a mapping this option is not used. +-- Add the |zv| command to the mapping to get the same effect. +-- (rationale: the mapping may want to control opening folds itself) +-- +-- item commands ~ +-- all any +-- block "(", "{", "[[", "[{", etc. +-- hor horizontal movements: "l", "w", "fx", etc. +-- insert any command in Insert mode +-- jump far jumps: "G", "gg", etc. +-- mark jumping to a mark: "'m", CTRL-O, etc. +-- percent "%" +-- quickfix ":cn", ":crew", ":make", etc. +-- search search for a pattern: "/", "n", "*", "gd", etc. +-- (not for a search pattern in a ":" command) +-- Also for |[s| and |]s|. +-- tag jumping to a tag: ":ta", CTRL-T, etc. +-- undo undo or redo: "u" and CTRL-R +-- When a movement command is used for an operator (e.g., "dl" or "y%") +-- this option is not used. This means the operator will include the +-- whole closed fold. +-- Note that vertical movements are not here, because it would make it +-- very difficult to move onto a closed fold. +-- In insert mode the folds containing the cursor will always be open +-- when text is inserted. +-- To close folds you can re-apply `'foldlevel'` with the |zx| command or +-- set the `'foldclose'` option to "all". +--- @class vim.opt.foldopen: vim.Option,string[] +--- @operator add: vim.opt.foldopen +--- @operator sub: vim.opt.foldopen +--- @operator pow: vim.opt.foldopen +vim.opt.foldopen = "block,hor,mark,percent,quickfix,search,tag,undo" +vim.opt.fdo = vim.opt.foldopen +--- @return string[] +function vim.opt.foldopen:get()end + +-- `'foldtext'` `'fdt'` string (default: "foldtext()") +-- local to window +-- An expression which is used to specify the text displayed for a closed +-- fold. See |fold-foldtext|. +-- +-- The expression will be evaluated in the |sandbox| if set from a +-- modeline, see |sandbox-option|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- It is not allowed to change text or jump to another window while +-- evaluating `'foldtext'` |textlock|. +--- @class vim.opt.foldtext: vim.Option,string +--- @operator add: vim.opt.foldtext +--- @operator sub: vim.opt.foldtext +--- @operator pow: vim.opt.foldtext +vim.opt.foldtext = "foldtext()" +vim.opt.fdt = vim.opt.foldtext +--- @return string +function vim.opt.foldtext:get()end + +-- `'formatexpr'` `'fex'` string (default "") +-- local to buffer +-- Expression which is evaluated to format a range of lines for the |gq| +-- operator or automatic formatting (see `'formatoptions'` ). When this +-- option is empty `'formatprg'` is used. +-- +-- The |v:lnum| variable holds the first line to be formatted. +-- The |v:count| variable holds the number of lines to be formatted. +-- The |v:char| variable holds the character that is going to be +-- inserted if the expression is being evaluated due to +-- automatic formatting. This can be empty. Don't insert +-- it yet! +-- +-- Example: > +-- :set formatexpr=mylang#Format() +-- < This will invoke the mylang#Format() function in the +-- autoload/mylang.vim file in `'runtimepath'` . |autoload| +-- +-- The expression is also evaluated when `'textwidth'` is set and adding +-- text beyond that limit. This happens under the same conditions as +-- when internal formatting is used. Make sure the cursor is kept in the +-- same spot relative to the text then! The |mode()| function will +-- return "i" or "R" in this situation. +-- +-- When the expression evaluates to non-zero Vim will fall back to using +-- the internal format mechanism. +-- +-- If the expression starts with s: or ||, then it is replaced with +-- the script ID (|local-function|). Example: > +-- set formatexpr=s:MyFormatExpr() +-- set formatexpr=SomeFormatExpr() +-- < +-- The expression will be evaluated in the |sandbox| when set from a +-- modeline, see |sandbox-option|. That stops the option from working, +-- since changing the buffer text is not allowed. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- NOTE: This option is set to "" when `'compatible'` is set. +--- @class vim.opt.formatexpr: vim.Option,string +--- @operator add: vim.opt.formatexpr +--- @operator sub: vim.opt.formatexpr +--- @operator pow: vim.opt.formatexpr +vim.opt.formatexpr = "" +vim.opt.fex = vim.opt.formatexpr +--- @return string +function vim.opt.formatexpr:get()end + +-- `'formatlistpat'` `'flp'` string (default: "^\s*\d\+[\]:.)}\t ]\s*") +-- local to buffer +-- A pattern that is used to recognize a list header. This is used for +-- the "n" flag in `'formatoptions'` . +-- The pattern must match exactly the text that will be the indent for +-- the line below it. You can use |/\ze| to mark the end of the match +-- while still checking more characters. There must be a character +-- following the pattern, when it matches the whole line it is handled +-- like there is no match. +-- The default recognizes a number, followed by an optional punctuation +-- character and white space. +--- @class vim.opt.formatlistpat: vim.Option,string +--- @operator add: vim.opt.formatlistpat +--- @operator sub: vim.opt.formatlistpat +--- @operator pow: vim.opt.formatlistpat +vim.opt.formatlistpat = "^\\s*\\d\\+[\\]:.)}\\t ]\\s*" +vim.opt.flp = vim.opt.formatlistpat +--- @return string +function vim.opt.formatlistpat:get()end + +-- `'formatoptions'` `'fo'` string (default: "tcqj") +-- local to buffer +-- This is a sequence of letters which describes how automatic +-- formatting is to be done. See |fo-table|. Commas can be inserted for +-- readability. +-- To avoid problems with flags that are added in the future, use the +-- "+=" and "-=" feature of ":set" |add-option-flags|. +--- @class vim.opt.formatoptions: vim.Option,string[] +--- @operator add: vim.opt.formatoptions +--- @operator sub: vim.opt.formatoptions +--- @operator pow: vim.opt.formatoptions +vim.opt.formatoptions = "tcqj" +vim.opt.fo = vim.opt.formatoptions +--- @return string[] +function vim.opt.formatoptions:get()end + +-- `'formatprg'` `'fp'` string (default "") +-- global or local to buffer |global-local| +-- The name of an external program that will be used to format the lines +-- selected with the |gq| operator. The program must take the input on +-- stdin and produce the output on stdout. The Unix program "fmt" is +-- such a program. +-- If the `'formatexpr'` option is not empty it will be used instead. +-- Otherwise, if `'formatprg'` option is an empty string, the internal +-- format function will be used |C-indenting|. +-- Environment variables are expanded |:set_env|. See |option-backslash| +-- about including spaces and backslashes. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.formatprg: vim.Option,string +--- @operator add: vim.opt.formatprg +--- @operator sub: vim.opt.formatprg +--- @operator pow: vim.opt.formatprg +vim.opt.formatprg = "" +vim.opt.fp = vim.opt.formatprg +--- @return string +function vim.opt.formatprg:get()end + +-- `'fsync'` `'fs'` boolean (default on) +-- global +-- When on, the OS function fsync() will be called after saving a file +-- (|:write|, |writefile()|, …), |swap-file|, |undo-persistence| and |shada-file|. +-- This flushes the file to disk, ensuring that it is safely written. +-- Slow on some systems: writing buffers, quitting Nvim, and other +-- operations may sometimes take a few seconds. +-- +-- Files are ALWAYS flushed (`'fsync'` is ignored) when: +-- - |CursorHold| event is triggered +-- - |:preserve| is called +-- - system signals low battery life +-- - Nvim exits abnormally +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.fsync: vim.Option,boolean +--- @operator add: vim.opt.fsync +--- @operator sub: vim.opt.fsync +--- @operator pow: vim.opt.fsync +vim.opt.fsync = true +vim.opt.fs = vim.opt.fsync +--- @return boolean +function vim.opt.fsync:get()end + +-- `'gdefault'` `'gd'` boolean (default off) +-- global +-- When on, the ":substitute" flag `'g'` is default on. This means that +-- all matches in a line are substituted instead of one. When a `'g'` flag +-- is given to a ":substitute" command, this will toggle the substitution +-- of all or one match. See |complex-change|. +-- +-- command `'gdefault'` on `'gdefault'` off ~ +-- :s/// subst. all subst. one +-- :s///g subst. one subst. all +-- :s///gg subst. all subst. one +-- +-- DEPRECATED: Setting this option may break plugins that are not aware +-- of this option. Also, many users get confused that adding the /g flag +-- has the opposite effect of that it normally does. +--- @class vim.opt.gdefault: vim.Option,boolean +--- @operator add: vim.opt.gdefault +--- @operator sub: vim.opt.gdefault +--- @operator pow: vim.opt.gdefault +vim.opt.gdefault = false +vim.opt.gd = vim.opt.gdefault +--- @return boolean +function vim.opt.gdefault:get()end + +-- `'grepformat'` `'gfm'` string (default "%f:%l:%m,%f:%l%m,%f %l%m") +-- global +-- Format to recognize for the ":grep" command output. +-- This is a scanf-like string that uses the same format as the +-- `'errorformat'` option: see |errorformat|. +--- @class vim.opt.grepformat: vim.Option,string[] +--- @operator add: vim.opt.grepformat +--- @operator sub: vim.opt.grepformat +--- @operator pow: vim.opt.grepformat +vim.opt.grepformat = "%f:%l:%m,%f:%l%m,%f %l%m" +vim.opt.gfm = vim.opt.grepformat +--- @return string[] +function vim.opt.grepformat:get()end + +-- `'grepprg'` `'gp'` string (default "grep -n ", +-- Unix: "grep -n $* /dev/null") +-- global or local to buffer |global-local| +-- Program to use for the |:grep| command. This option may contain `'%'` +-- and `'#'` characters, which are expanded like when used in a command- +-- line. The placeholder "$*" is allowed to specify where the arguments +-- will be included. Environment variables are expanded |:set_env|. See +-- |option-backslash| about including spaces and backslashes. +-- When your "grep" accepts the "-H" argument, use this to make ":grep" +-- also work well with a single file: > +-- :set grepprg=grep\ -nH +-- < Special value: When `'grepprg'` is set to "internal" the |:grep| command +-- works like |:vimgrep|, |:lgrep| like |:lvimgrep|, |:grepadd| like +-- |:vimgrepadd| and |:lgrepadd| like |:lvimgrepadd|. +-- See also the section |:make_makeprg|, since most of the comments there +-- apply equally to `'grepprg'` . +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.grepprg: vim.Option,string +--- @operator add: vim.opt.grepprg +--- @operator sub: vim.opt.grepprg +--- @operator pow: vim.opt.grepprg +vim.opt.grepprg = "grep -n $* /dev/null" +vim.opt.gp = vim.opt.grepprg +--- @return string +function vim.opt.grepprg:get()end + +-- `'guicursor'` `'gcr'` string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20") +-- global +-- Configures the cursor style for each mode. Works in the GUI and many +-- terminals. See |tui-cursor-shape|. +-- +-- To disable cursor-styling, reset the option: > +-- :set guicursor= +-- +-- < To enable mode shapes, "Cursor" highlight, and blinking: > +-- :set guicursor=n-v-c:block,i-ci-ve:ver25,r-cr:hor20,o:hor50 +-- \,a:blinkwait700-blinkoff400-blinkon250-Cursor/lCursor +-- \,sm:block-blinkwait175-blinkoff150-blinkon175 +-- +-- < The option is a comma-separated list of parts. Each part consists of a +-- mode-list and an argument-list: +-- mode-list:argument-list,mode-list:argument-list,.. +-- The mode-list is a dash separated list of these modes: +-- n Normal mode +-- v Visual mode +-- ve Visual mode with `'selection'` "exclusive" (same as `'v'` , +-- if not specified) +-- o Operator-pending mode +-- i Insert mode +-- r Replace mode +-- c Command-line Normal (append) mode +-- ci Command-line Insert mode +-- cr Command-line Replace mode +-- sm showmatch in Insert mode +-- a all modes +-- The argument-list is a dash separated list of these arguments: +-- hor{N} horizontal bar, {N} percent of the character height +-- ver{N} vertical bar, {N} percent of the character width +-- block block cursor, fills the whole character +-- - Only one of the above three should be present. +-- - Default is "block" for each mode. +-- blinkwait{N} +-- blinkon{N} +-- blinkoff{N} +-- blink times for cursor: blinkwait is the delay before +-- the cursor starts blinking, blinkon is the time that +-- the cursor is shown and blinkoff is the time that the +-- cursor is not shown. Times are in msec. When one of +-- the numbers is zero, there is no blinking. E.g.: > +-- :set guicursor=n:blinkon0 +-- < - Default is "blinkon0" for each mode. +-- {group-name} +-- Highlight group that decides the color and font of the +-- cursor. +-- In the |TUI|: +-- - |inverse|/reverse and no group-name are interpreted +-- as "host-terminal default cursor colors" which +-- typically means "inverted bg and fg colors". +-- - |ctermfg| and |guifg| are ignored. +-- {group-name}/{group-name} +-- Two highlight group names, the first is used when +-- no language mappings are used, the other when they +-- are. |language-mapping| +-- +-- Examples of parts: +-- n-c-v:block-nCursor In Normal, Command-line and Visual mode, use a +-- block cursor with colors from the "nCursor" +-- highlight group +-- n-v-c-sm:block,i-ci-ve:ver25-Cursor,r-cr-o:hor20 +-- In Normal et al. modes, use a block cursor +-- with the default colors defined by the host +-- terminal. In Insert-likes modes, use +-- a vertical bar cursor with colors from +-- "Cursor" highlight group. In Replace-likes +-- modes, use a underline cursor with +-- default colors. +-- i-ci:ver30-iCursor-blinkwait300-blinkon200-blinkoff150 +-- In Insert and Command-line Insert mode, use a +-- 30% vertical bar cursor with colors from the +-- "iCursor" highlight group. Blink a bit +-- faster. +-- +-- The `'a'` mode is different. It will set the given argument-list for +-- all modes. It does not reset anything to defaults. This can be used +-- to do a common setting for all modes. For example, to switch off +-- blinking: "a:blinkon0" +-- +-- Examples of cursor highlighting: > +-- :highlight Cursor gui=reverse guifg=NONE guibg=NONE +-- :highlight Cursor gui=NONE guifg=bg guibg=fg +-- < +--- @class vim.opt.guicursor: vim.Option,string[] +--- @operator add: vim.opt.guicursor +--- @operator sub: vim.opt.guicursor +--- @operator pow: vim.opt.guicursor +vim.opt.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20" +vim.opt.gcr = vim.opt.guicursor +--- @return string[] +function vim.opt.guicursor:get()end + +-- `'guifont'` `'gfn'` string (default "") +-- global +-- This is a list of fonts which will be used for the GUI version of Vim. +-- In its simplest form the value is just one font name. When +-- the font cannot be found you will get an error message. To try other +-- font names a list can be specified, font names separated with commas. +-- The first valid font is used. +-- +-- Spaces after a comma are ignored. To include a comma in a font name +-- precede it with a backslash. Setting an option requires an extra +-- backslash before a space and a backslash. See also +-- |option-backslash|. For example: > +-- :set guifont=Screen15,\ 7x13,font\\,with\\,commas +-- < will make Vim try to use the font "Screen15" first, and if it fails it +-- will try to use "7x13" and then "font,with,commas" instead. +-- +-- If none of the fonts can be loaded, Vim will keep the current setting. +-- If an empty font list is given, Vim will try using other resource +-- settings (for X, it will use the Vim.font resource), and finally it +-- will try some builtin default which should always be there ("7x13" in +-- the case of X). The font names given should be "normal" fonts. Vim +-- will try to find the related bold and italic fonts. +-- +-- For Win32 and Mac OS: > +-- :set guifont=* +-- < will bring up a font requester, where you can pick the font you want. +-- +-- The font name depends on the GUI used. +-- +-- For Mac OSX you can use something like this: > +-- :set guifont=Monaco:h10 +-- < +-- Note that the fonts must be mono-spaced (all characters have the same +-- width). +-- +-- To preview a font on X11, you might be able to use the "xfontsel" +-- program. The "xlsfonts" program gives a list of all available fonts. +-- +-- For the Win32 GUI +-- - takes these options in the font name: +-- hXX - height is XX (points, can be floating-point) +-- wXX - width is XX (points, can be floating-point) +-- b - bold +-- i - italic +-- u - underline +-- s - strikeout +-- cXX - character set XX. Valid charsets are: ANSI, ARABIC, +-- BALTIC, CHINESEBIG5, DEFAULT, EASTEUROPE, GB2312, GREEK, +-- HANGEUL, HEBREW, JOHAB, MAC, OEM, RUSSIAN, SHIFTJIS, +-- SYMBOL, THAI, TURKISH, VIETNAMESE ANSI and BALTIC. +-- Normally you would use "cDEFAULT". +-- +-- Use a `':'` to separate the options. +-- - A `'_'` can be used in the place of a space, so you don't need to use +-- backslashes to escape the spaces. +-- - Examples: > +-- :set guifont=courier_new:h12:w5:b:cRUSSIAN +-- :set guifont=Andale_Mono:h7.5:w4.5 +-- < +--- @class vim.opt.guifont: vim.Option,string[] +--- @operator add: vim.opt.guifont +--- @operator sub: vim.opt.guifont +--- @operator pow: vim.opt.guifont +vim.opt.guifont = "" +vim.opt.gfn = vim.opt.guifont +--- @return string[] +function vim.opt.guifont:get()end + +-- `'guifontwide'` `'gfw'` string (default "") +-- global +-- Comma-separated list of fonts to be used for double-width characters. +-- The first font that can be loaded is used. +-- Note: The size of these fonts must be exactly twice as wide as the one +-- specified with `'guifont'` and the same height. +-- +-- When `'guifont'` has a valid font and `'guifontwide'` is empty Vim will +-- attempt to set `'guifontwide'` to a matching double-width font. +--- @class vim.opt.guifontwide: vim.Option,string[] +--- @operator add: vim.opt.guifontwide +--- @operator sub: vim.opt.guifontwide +--- @operator pow: vim.opt.guifontwide +vim.opt.guifontwide = "" +vim.opt.gfw = vim.opt.guifontwide +--- @return string[] +function vim.opt.guifontwide:get()end + +-- `'guioptions'` `'go'` string (default "egmrLT" (MS-Windows)) +-- global +-- This option only has an effect in the GUI version of Vim. It is a +-- sequence of letters which describes what components and options of the +-- GUI should be used. +-- To avoid problems with flags that are added in the future, use the +-- "+=" and "-=" feature of ":set" |add-option-flags|. +-- +-- Valid letters are as follows: +-- +-- `'a'` Autoselect: If present, then whenever VISUAL mode is started, +-- or the Visual area extended, Vim tries to become the owner of +-- the windowing system's global selection. This means that the +-- Visually highlighted text is available for pasting into other +-- applications as well as into Vim itself. When the Visual mode +-- ends, possibly due to an operation on the text, or when an +-- application wants to paste the selection, the highlighted text +-- is automatically yanked into the "* selection register. +-- Thus the selection is still available for pasting into other +-- applications after the VISUAL mode has ended. +-- If not present, then Vim won't become the owner of the +-- windowing system's global selection unless explicitly told to +-- by a yank or delete operation for the "* register. +-- The same applies to the modeless selection. +-- +-- `'P'` Like autoselect but using the "+ register instead of the "* +-- register. +-- +-- `'A'` Autoselect for the modeless selection. Like `'a'` , but only +-- applies to the modeless selection. +-- +-- `'guioptions'` autoselect Visual autoselect modeless ~ +-- "" - - +-- "a" yes yes +-- "A" - yes +-- "aA" yes yes +-- +-- +-- `'c'` Use console dialogs instead of popup dialogs for simple +-- choices. +-- +-- `'d'` Use dark theme variant if available. +-- +-- `'e'` Add tab pages when indicated with `'showtabline'` . +-- `'guitablabel'` can be used to change the text in the labels. +-- When `'e'` is missing a non-GUI tab pages line may be used. +-- The GUI tabs are only supported on some systems, currently +-- Mac OS/X and MS-Windows. +-- +-- `'i'` Use a Vim icon. +-- +-- `'m'` Menu bar is present. +-- +-- `'M'` The system menu "$VIMRUNTIME/menu.vim" is not sourced. Note +-- that this flag must be added in the vimrc file, before +-- switching on syntax or filetype recognition (when the |gvimrc| +-- file is sourced the system menu has already been loaded; the +-- `:syntax on` and `:filetype on` commands load the menu too). +-- +-- `'g'` Grey menu items: Make menu items that are not active grey. If +-- `'g'` is not included inactive menu items are not shown at all. +-- +-- `'T'` Include Toolbar. Currently only in Win32 GUI. +-- +-- `'r'` Right-hand scrollbar is always present. +-- +-- `'R'` Right-hand scrollbar is present when there is a vertically +-- split window. +-- +-- `'l'` Left-hand scrollbar is always present. +-- +-- `'L'` Left-hand scrollbar is present when there is a vertically +-- split window. +-- +-- `'b'` Bottom (horizontal) scrollbar is present. Its size depends on +-- the longest visible line, or on the cursor line if the `'h'` +-- flag is included. |gui-horiz-scroll| +-- +-- `'h'` Limit horizontal scrollbar size to the length of the cursor +-- line. Reduces computations. |gui-horiz-scroll| +-- +-- And yes, you may even have scrollbars on the left AND the right if +-- you really want to :-). See |gui-scrollbars| for more information. +-- +-- +-- `'v'` Use a vertical button layout for dialogs. When not included, +-- a horizontal layout is preferred, but when it doesn't fit a +-- vertical layout is used anyway. Not supported in GTK 3. +-- +-- `'p'` Use Pointer callbacks for X11 GUI. This is required for some +-- window managers. If the cursor is not blinking or hollow at +-- the right moment, try adding this flag. This must be done +-- before starting the GUI. Set it in your |gvimrc|. Adding or +-- removing it after the GUI has started has no effect. +-- +-- `'k'` Keep the GUI window size when adding/removing a scrollbar, or +-- toolbar, tabline, etc. Instead, the behavior is similar to +-- when the window is maximized and will adjust `'lines'` and +-- `'columns'` to fit to the window. Without the `'k'` flag Vim will +-- try to keep `'lines'` and `'columns'` the same when adding and +-- removing GUI components. +--- @class vim.opt.guioptions: vim.Option,string +--- @operator add: vim.opt.guioptions +--- @operator sub: vim.opt.guioptions +--- @operator pow: vim.opt.guioptions +vim.opt.guioptions = "" +vim.opt.go = vim.opt.guioptions +--- @return string +function vim.opt.guioptions:get()end + +-- `'guitablabel'` `'gtl'` string (default empty) +-- global +-- When non-empty describes the text to use in a label of the GUI tab +-- pages line. When empty and when the result is empty Vim will use a +-- default label. See |setting-guitablabel| for more info. +-- +-- The format of this option is like that of `'statusline'` . +-- `'guitabtooltip'` is used for the tooltip, see below. +-- The expression will be evaluated in the |sandbox| when set from a +-- modeline, see |sandbox-option|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- Only used when the GUI tab pages line is displayed. `'e'` must be +-- present in `'guioptions'` . For the non-GUI tab pages line `'tabline'` is +-- used. +--- @class vim.opt.guitablabel: vim.Option,string +--- @operator add: vim.opt.guitablabel +--- @operator sub: vim.opt.guitablabel +--- @operator pow: vim.opt.guitablabel +vim.opt.guitablabel = "" +vim.opt.gtl = vim.opt.guitablabel +--- @return string +function vim.opt.guitablabel:get()end + +-- `'guitabtooltip'` `'gtt'` string (default empty) +-- global +-- When non-empty describes the text to use in a tooltip for the GUI tab +-- pages line. When empty Vim will use a default tooltip. +-- This option is otherwise just like `'guitablabel'` above. +-- You can include a line break. Simplest method is to use |:let|: > +-- :let &guitabtooltip = "line one\nline two" +-- < +--- @class vim.opt.guitabtooltip: vim.Option,string +--- @operator add: vim.opt.guitabtooltip +--- @operator sub: vim.opt.guitabtooltip +--- @operator pow: vim.opt.guitabtooltip +vim.opt.guitabtooltip = "" +vim.opt.gtt = vim.opt.guitabtooltip +--- @return string +function vim.opt.guitabtooltip:get()end + +-- `'helpfile'` `'hf'` string (default (MS-Windows) "$VIMRUNTIME\doc\help.txt" +-- (others) "$VIMRUNTIME/doc/help.txt") +-- global +-- Name of the main help file. All distributed help files should be +-- placed together in one directory. Additionally, all "doc" directories +-- in `'runtimepath'` will be used. +-- Environment variables are expanded |:set_env|. For example: +-- "$VIMRUNTIME/doc/help.txt". If $VIMRUNTIME is not set, $VIM is also +-- tried. Also see |$VIMRUNTIME| and |option-backslash| about including +-- spaces and backslashes. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.helpfile: vim.Option,string +--- @operator add: vim.opt.helpfile +--- @operator sub: vim.opt.helpfile +--- @operator pow: vim.opt.helpfile +vim.opt.helpfile = "/tmp/nvim/squashfs-root/usr/share/nvim/runtime/doc/help.txt" +vim.opt.hf = vim.opt.helpfile +--- @return string +function vim.opt.helpfile:get()end + +-- `'helpheight'` `'hh'` number (default 20) +-- global +-- Minimal initial height of the help window when it is opened with the +-- ":help" command. The initial height of the help window is half of the +-- current window, or (when the `'ea'` option is on) the same as other +-- windows. When the height is less than `'helpheight'` , the height is +-- set to `'helpheight'` . Set to zero to disable. +--- @class vim.opt.helpheight: vim.Option,number +--- @operator add: vim.opt.helpheight +--- @operator sub: vim.opt.helpheight +--- @operator pow: vim.opt.helpheight +vim.opt.helpheight = 20 +vim.opt.hh = vim.opt.helpheight +--- @return number +function vim.opt.helpheight:get()end + +-- `'helplang'` `'hlg'` string (default: messages language or empty) +-- global +-- Comma-separated list of languages. Vim will use the first language +-- for which the desired help can be found. The English help will always +-- be used as a last resort. You can add "en" to prefer English over +-- another language, but that will only find tags that exist in that +-- language and not in the English help. +-- Example: > +-- :set helplang=de,it +-- < This will first search German, then Italian and finally English help +-- files. +-- When using |CTRL-]| and ":help!" in a non-English help file Vim will +-- try to find the tag in the current language before using this option. +-- See |help-translated|. +--- @class vim.opt.helplang: vim.Option,string[] +--- @operator add: vim.opt.helplang +--- @operator sub: vim.opt.helplang +--- @operator pow: vim.opt.helplang +vim.opt.helplang = "" +vim.opt.hlg = vim.opt.helplang +--- @return string[] +function vim.opt.helplang:get()end + +-- `'hidden'` `'hid'` boolean (default on) +-- global +-- When off a buffer is unloaded (including loss of undo information) +-- when it is |abandon|ed. When on a buffer becomes hidden when it is +-- |abandon|ed. A buffer displayed in another window does not become +-- hidden, of course. +-- +-- Commands that move through the buffer list sometimes hide a buffer +-- although the `'hidden'` option is off when these three are true: +-- - the buffer is modified +-- - `'autowrite'` is off or writing is not possible +-- - the `'!'` flag was used +-- Also see |windows|. +-- +-- To hide a specific buffer use the `'bufhidden'` option. +-- `'hidden'` is set for one command with ":hide {command}" |:hide|. +--- @class vim.opt.hidden: vim.Option,boolean +--- @operator add: vim.opt.hidden +--- @operator sub: vim.opt.hidden +--- @operator pow: vim.opt.hidden +vim.opt.hidden = true +vim.opt.hid = vim.opt.hidden +--- @return boolean +function vim.opt.hidden:get()end + +--- @class vim.opt.highlight: vim.Option,string[] +--- @operator add: vim.opt.highlight +--- @operator sub: vim.opt.highlight +--- @operator pow: vim.opt.highlight +vim.opt.highlight = "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr,G:CursorLineSign,O:CursorLineFoldr:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,0:Whitespace,I:NormalNC" +vim.opt.hl = vim.opt.highlight +--- @return string[] +function vim.opt.highlight:get()end + +-- `'history'` `'hi'` number (default: 10000) +-- global +-- A history of ":" commands, and a history of previous search patterns +-- is remembered. This option decides how many entries may be stored in +-- each of these histories (see |cmdline-editing|). +-- The maximum value is 10000. +--- @class vim.opt.history: vim.Option,number +--- @operator add: vim.opt.history +--- @operator sub: vim.opt.history +--- @operator pow: vim.opt.history +vim.opt.history = 10000 +vim.opt.hi = vim.opt.history +--- @return number +function vim.opt.history:get()end + +--- @class vim.opt.hkmap: vim.Option,boolean +--- @operator add: vim.opt.hkmap +--- @operator sub: vim.opt.hkmap +--- @operator pow: vim.opt.hkmap +vim.opt.hkmap = false +vim.opt.hk = vim.opt.hkmap +--- @return boolean +function vim.opt.hkmap:get()end + +--- @class vim.opt.hkmapp: vim.Option,boolean +--- @operator add: vim.opt.hkmapp +--- @operator sub: vim.opt.hkmapp +--- @operator pow: vim.opt.hkmapp +vim.opt.hkmapp = false +vim.opt.hkp = vim.opt.hkmapp +--- @return boolean +function vim.opt.hkmapp:get()end + +-- `'hlsearch'` `'hls'` boolean (default on) +-- global +-- When there is a previous search pattern, highlight all its matches. +-- The |hl-Search| highlight group determines the highlighting for all +-- matches not under the cursor while the |hl-CurSearch| highlight group +-- (if defined) determines the highlighting for the match under the +-- cursor. If |hl-CurSearch| is not defined, then |hl-Search| is used for +-- both. Note that only the matching text is highlighted, any offsets +-- are not applied. +-- See also: `'incsearch'` and |:match|. +-- When you get bored looking at the highlighted matches, you can turn it +-- off with |:nohlsearch|. This does not change the option value, as +-- soon as you use a search command, the highlighting comes back. +-- `'redrawtime'` specifies the maximum time spent on finding matches. +-- When the search pattern can match an end-of-line, Vim will try to +-- highlight all of the matched text. However, this depends on where the +-- search starts. This will be the first line in the window or the first +-- line below a closed fold. A match in a previous line which is not +-- drawn may not continue in a newly drawn line. +-- You can specify whether the highlight status is restored on startup +-- with the `'h'` flag in `'shada'` |shada-h|. +--- @class vim.opt.hlsearch: vim.Option,boolean +--- @operator add: vim.opt.hlsearch +--- @operator sub: vim.opt.hlsearch +--- @operator pow: vim.opt.hlsearch +vim.opt.hlsearch = true +vim.opt.hls = vim.opt.hlsearch +--- @return boolean +function vim.opt.hlsearch:get()end + +-- `'icon'` boolean (default off, on when title can be restored) +-- global +-- When on, the icon text of the window will be set to the value of +-- `'iconstring'` (if it is not empty), or to the name of the file +-- currently being edited. Only the last part of the name is used. +-- Overridden by the `'iconstring'` option. +-- Only works if the terminal supports setting window icons. +--- @class vim.opt.icon: vim.Option,boolean +--- @operator add: vim.opt.icon +--- @operator sub: vim.opt.icon +--- @operator pow: vim.opt.icon +vim.opt.icon = false +--- @return boolean +function vim.opt.icon:get()end + +-- `'iconstring'` string (default "") +-- global +-- When this option is not empty, it will be used for the icon text of +-- the window. This happens only when the `'icon'` option is on. +-- Only works if the terminal supports setting window icon text +-- When this option contains printf-style `'%'` items, they will be +-- expanded according to the rules used for `'statusline'` . See +-- `'titlestring'` for example settings. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +--- @class vim.opt.iconstring: vim.Option,string +--- @operator add: vim.opt.iconstring +--- @operator sub: vim.opt.iconstring +--- @operator pow: vim.opt.iconstring +vim.opt.iconstring = "" +--- @return string +function vim.opt.iconstring:get()end + +-- `'ignorecase'` `'ic'` boolean (default off) +-- global +-- Ignore case in search patterns. Also used when searching in the tags +-- file. +-- Also see `'smartcase'` and `'tagcase'` . +-- Can be overruled by using "\c" or "\C" in the pattern, see +-- |/ignorecase|. +--- @class vim.opt.ignorecase: vim.Option,boolean +--- @operator add: vim.opt.ignorecase +--- @operator sub: vim.opt.ignorecase +--- @operator pow: vim.opt.ignorecase +vim.opt.ignorecase = false +vim.opt.ic = vim.opt.ignorecase +--- @return boolean +function vim.opt.ignorecase:get()end + +-- `'imcmdline'` `'imc'` boolean (default off) +-- global +-- When set the Input Method is always on when starting to edit a command +-- line, unless entering a search pattern (see `'imsearch'` for that). +-- Setting this option is useful when your input method allows entering +-- English characters directly, e.g., when it's used to type accented +-- characters with dead keys. +--- @class vim.opt.imcmdline: vim.Option,boolean +--- @operator add: vim.opt.imcmdline +--- @operator sub: vim.opt.imcmdline +--- @operator pow: vim.opt.imcmdline +vim.opt.imcmdline = false +vim.opt.imc = vim.opt.imcmdline +--- @return boolean +function vim.opt.imcmdline:get()end + +-- `'imdisable'` `'imd'` boolean (default off, on for some systems (SGI)) +-- global +-- When set the Input Method is never used. This is useful to disable +-- the IM when it doesn't work properly. +-- Currently this option is on by default for SGI/IRIX machines. This +-- may change in later releases. +--- @class vim.opt.imdisable: vim.Option,boolean +--- @operator add: vim.opt.imdisable +--- @operator sub: vim.opt.imdisable +--- @operator pow: vim.opt.imdisable +vim.opt.imdisable = false +vim.opt.imd = vim.opt.imdisable +--- @return boolean +function vim.opt.imdisable:get()end + +-- `'iminsert'` `'imi'` number (default 0) +-- local to buffer +-- Specifies whether :lmap or an Input Method (IM) is to be used in +-- Insert mode. Valid values: +-- 0 :lmap is off and IM is off +-- 1 :lmap is ON and IM is off +-- 2 :lmap is off and IM is ON +-- To always reset the option to zero when leaving Insert mode with +-- this can be used: > +-- :inoremap :set iminsert=0 +-- < This makes :lmap and IM turn off automatically when leaving Insert +-- mode. +-- Note that this option changes when using CTRL-^ in Insert mode +-- |i_CTRL-^|. +-- The value is set to 1 when setting `'keymap'` to a valid keymap name. +-- It is also used for the argument of commands like "r" and "f". +--- @class vim.opt.iminsert: vim.Option,number +--- @operator add: vim.opt.iminsert +--- @operator sub: vim.opt.iminsert +--- @operator pow: vim.opt.iminsert +vim.opt.iminsert = 0 +vim.opt.imi = vim.opt.iminsert +--- @return number +function vim.opt.iminsert:get()end + +-- `'imsearch'` `'ims'` number (default -1) +-- local to buffer +-- Specifies whether :lmap or an Input Method (IM) is to be used when +-- entering a search pattern. Valid values: +-- -1 the value of `'iminsert'` is used, makes it look like +-- `'iminsert'` is also used when typing a search pattern +-- 0 :lmap is off and IM is off +-- 1 :lmap is ON and IM is off +-- 2 :lmap is off and IM is ON +-- Note that this option changes when using CTRL-^ in Command-line mode +-- |c_CTRL-^|. +-- The value is set to 1 when it is not -1 and setting the `'keymap'` +-- option to a valid keymap name. +--- @class vim.opt.imsearch: vim.Option,number +--- @operator add: vim.opt.imsearch +--- @operator sub: vim.opt.imsearch +--- @operator pow: vim.opt.imsearch +vim.opt.imsearch = -1 +vim.opt.ims = vim.opt.imsearch +--- @return number +function vim.opt.imsearch:get()end + +-- `'inccommand'` `'icm'` string (default "nosplit") +-- global +-- +-- When nonempty, shows the effects of |:substitute|, |:smagic|, +-- |:snomagic| and user commands with the |:command-preview| flag as you +-- type. +-- +-- Possible values: +-- nosplit Shows the effects of a command incrementally in the +-- buffer. +-- split Like "nosplit", but also shows partial off-screen +-- results in a preview window. +-- +-- If the preview for built-in commands is too slow (exceeds +-- `'redrawtime'` ) then `'inccommand'` is automatically disabled until +-- |Command-line-mode| is done. +--- @class vim.opt.inccommand: vim.Option,string +--- @operator add: vim.opt.inccommand +--- @operator sub: vim.opt.inccommand +--- @operator pow: vim.opt.inccommand +vim.opt.inccommand = "nosplit" +vim.opt.icm = vim.opt.inccommand +--- @return string +function vim.opt.inccommand:get()end + +-- `'include'` `'inc'` string (default "^\sinclude") +-- global or local to buffer |global-local| +-- Pattern to be used to find an include command. It is a search +-- pattern, just like for the "/" command (See |pattern|). The default +-- value is for C programs. This option is used for the commands "[i", +-- "]I", "[d", etc. +-- Normally the `'isfname'` option is used to recognize the file name that +-- comes after the matched pattern. But if "\zs" appears in the pattern +-- then the text matched from "\zs" to the end, or until "\ze" if it +-- appears, is used as the file name. Use this to include characters +-- that are not in `'isfname'` , such as a space. You can then use +-- `'includeexpr'` to process the matched text. +-- See |option-backslash| about including spaces and backslashes. +--- @class vim.opt.include: vim.Option,string +--- @operator add: vim.opt.include +--- @operator sub: vim.opt.include +--- @operator pow: vim.opt.include +vim.opt.include = "^\\s*#\\s*include" +vim.opt.inc = vim.opt.include +--- @return string +function vim.opt.include:get()end + +-- `'includeexpr'` `'inex'` string (default "") +-- local to buffer +-- Expression to be used to transform the string found with the `'include'` +-- option to a file name. Mostly useful to change "." to "/" for Java: > +-- :setlocal includeexpr=substitute(v:fname,`'\\.'` ,`'/'` ,`'g'` ) +-- < The "v:fname" variable will be set to the file name that was detected. +-- Note the double backslash: the `:set` command first halves them, then +-- one remains in the value, where "\." matches a dot literally. For +-- simple character replacements `tr()` avoids the need for escaping: > +-- :setlocal includeexpr=tr(v:fname,`'.'` ,`'/'` ) +-- < +-- Also used for the |gf| command if an unmodified file name can't be +-- found. Allows doing "gf" on the name after an `'include'` statement. +-- Also used for ||. +-- +-- If the expression starts with s: or ||, then it is replaced with +-- the script ID (|local-function|). Example: > +-- set includeexpr=s:MyIncludeExpr(v:fname) +-- set includeexpr=SomeIncludeExpr(v:fname) +-- < +-- The expression will be evaluated in the |sandbox| when set from a +-- modeline, see |sandbox-option|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- It is not allowed to change text or jump to another window while +-- evaluating `'includeexpr'` |textlock|. +--- @class vim.opt.includeexpr: vim.Option,string +--- @operator add: vim.opt.includeexpr +--- @operator sub: vim.opt.includeexpr +--- @operator pow: vim.opt.includeexpr +vim.opt.includeexpr = "" +vim.opt.inex = vim.opt.includeexpr +--- @return string +function vim.opt.includeexpr:get()end + +-- `'incsearch'` `'is'` boolean (default on) +-- global +-- While typing a search command, show where the pattern, as it was typed +-- so far, matches. The matched string is highlighted. If the pattern +-- is invalid or not found, nothing is shown. The screen will be updated +-- often, this is only useful on fast terminals. +-- Note that the match will be shown, but the cursor will return to its +-- original position when no match is found and when pressing . You +-- still need to finish the search command with to move the +-- cursor to the match. +-- You can use the CTRL-G and CTRL-T keys to move to the next and +-- previous match. |c_CTRL-G| |c_CTRL-T| +-- Vim only searches for about half a second. With a complicated +-- pattern and/or a lot of text the match may not be found. This is to +-- avoid that Vim hangs while you are typing the pattern. +-- The |hl-IncSearch| highlight group determines the highlighting. +-- When `'hlsearch'` is on, all matched strings are highlighted too while +-- typing a search command. See also: `'hlsearch'` . +-- If you don't want to turn `'hlsearch'` on, but want to highlight all +-- matches while searching, you can turn on and off `'hlsearch'` with +-- autocmd. Example: > +-- augroup vimrc-incsearch-highlight +-- autocmd! +-- autocmd CmdlineEnter /,\? :set hlsearch +-- autocmd CmdlineLeave /,\? :set nohlsearch +-- augroup END +-- < +-- CTRL-L can be used to add one character from after the current match +-- to the command line. If `'ignorecase'` and `'smartcase'` are set and the +-- command line has no uppercase characters, the added character is +-- converted to lowercase. +-- CTRL-R CTRL-W can be used to add the word at the end of the current +-- match, excluding the characters that were already typed. +--- @class vim.opt.incsearch: vim.Option,boolean +--- @operator add: vim.opt.incsearch +--- @operator sub: vim.opt.incsearch +--- @operator pow: vim.opt.incsearch +vim.opt.incsearch = true +vim.opt.is = vim.opt.incsearch +--- @return boolean +function vim.opt.incsearch:get()end + +-- `'indentexpr'` `'inde'` string (default "") +-- local to buffer +-- Expression which is evaluated to obtain the proper indent for a line. +-- It is used when a new line is created, for the |=| operator and +-- in Insert mode as specified with the `'indentkeys'` option. +-- When this option is not empty, it overrules the `'cindent'` and +-- `'smartindent'` indenting. When `'lisp'` is set, this option is +-- is only used when `'lispoptions'` contains "expr:1". +-- The expression is evaluated with |v:lnum| set to the line number for +-- which the indent is to be computed. The cursor is also in this line +-- when the expression is evaluated (but it may be moved around). +-- If the expression starts with s: or ||, then it is replaced with +-- the script ID (|local-function|). Example: > +-- set indentexpr=s:MyIndentExpr() +-- set indentexpr=SomeIndentExpr() +-- < +-- The expression must return the number of spaces worth of indent. It +-- can return "-1" to keep the current indent (this means `'autoindent'` is +-- used for the indent). +-- Functions useful for computing the indent are |indent()|, |cindent()| +-- and |lispindent()|. +-- The evaluation of the expression must not have side effects! It must +-- not change the text, jump to another window, etc. Afterwards the +-- cursor position is always restored, thus the cursor may be moved. +-- Normally this option would be set to call a function: > +-- :set indentexpr=GetMyIndent() +-- < Error messages will be suppressed, unless the `'debug'` option contains +-- "msg". +-- See |indent-expression|. +-- +-- The expression will be evaluated in the |sandbox| when set from a +-- modeline, see |sandbox-option|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- It is not allowed to change text or jump to another window while +-- evaluating `'indentexpr'` |textlock|. +--- @class vim.opt.indentexpr: vim.Option,string +--- @operator add: vim.opt.indentexpr +--- @operator sub: vim.opt.indentexpr +--- @operator pow: vim.opt.indentexpr +vim.opt.indentexpr = "" +vim.opt.inde = vim.opt.indentexpr +--- @return string +function vim.opt.indentexpr:get()end + +-- `'indentkeys'` `'indk'` string (default "0{,0},0),0],:,0#,!^F,o,O,e") +-- local to buffer +-- A list of keys that, when typed in Insert mode, cause reindenting of +-- the current line. Only happens if `'indentexpr'` isn't empty. +-- The format is identical to `'cinkeys'` , see |indentkeys-format|. +-- See |C-indenting| and |indent-expression|. +--- @class vim.opt.indentkeys: vim.Option,string[] +--- @operator add: vim.opt.indentkeys +--- @operator sub: vim.opt.indentkeys +--- @operator pow: vim.opt.indentkeys +vim.opt.indentkeys = "0{,0},0),0],:,0#,!^F,o,O,e" +vim.opt.indk = vim.opt.indentkeys +--- @return string[] +function vim.opt.indentkeys:get()end + +-- `'infercase'` `'inf'` boolean (default off) +-- local to buffer +-- When doing keyword completion in insert mode |ins-completion|, and +-- `'ignorecase'` is also on, the case of the match is adjusted depending +-- on the typed text. If the typed text contains a lowercase letter +-- where the match has an upper case letter, the completed part is made +-- lowercase. If the typed text has no lowercase letters and the match +-- has a lowercase letter where the typed text has an uppercase letter, +-- and there is a letter before it, the completed part is made uppercase. +-- With `'noinfercase'` the match is used as-is. +--- @class vim.opt.infercase: vim.Option,boolean +--- @operator add: vim.opt.infercase +--- @operator sub: vim.opt.infercase +--- @operator pow: vim.opt.infercase +vim.opt.infercase = false +vim.opt.inf = vim.opt.infercase +--- @return boolean +function vim.opt.infercase:get()end + +--- @class vim.opt.insertmode: vim.Option,boolean +--- @operator add: vim.opt.insertmode +--- @operator sub: vim.opt.insertmode +--- @operator pow: vim.opt.insertmode +vim.opt.insertmode = false +vim.opt.im = vim.opt.insertmode +--- @return boolean +function vim.opt.insertmode:get()end + +-- `'isfname'` `'isf'` string (default for Windows: +-- "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=" +-- otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,=") +-- global +-- The characters specified by this option are included in file names and +-- path names. Filenames are used for commands like "gf", "[i" and in +-- the tags file. It is also used for "\f" in a |pattern|. +-- Multi-byte characters 256 and above are always included, only the +-- characters up to 255 are specified with this option. +-- For UTF-8 the characters 0xa0 to 0xff are included as well. +-- Think twice before adding white space to this option. Although a +-- space may appear inside a file name, the effect will be that Vim +-- doesn't know where a file name starts or ends when doing completion. +-- It most likely works better without a space in `'isfname'` . +-- +-- Note that on systems using a backslash as path separator, Vim tries to +-- do its best to make it work as you would expect. That is a bit +-- tricky, since Vi originally used the backslash to escape special +-- characters. Vim will not remove a backslash in front of a normal file +-- name character on these systems, but it will on Unix and alikes. The +-- `'&'` and `'^'` are not included by default, because these are special for +-- cmd.exe. +-- +-- The format of this option is a list of parts, separated with commas. +-- Each part can be a single character number or a range. A range is two +-- character numbers with `'-'` in between. A character number can be a +-- decimal number between 0 and 255 or the ASCII character itself (does +-- not work for digits). Example: +-- "_,-,128-140,#-43" (include `'_'` and `'-'` and the range +-- 128 to 140 and `'#'` to 43) +-- If a part starts with `'^'` , the following character number or range +-- will be excluded from the option. The option is interpreted from left +-- to right. Put the excluded character after the range where it is +-- included. To include `'^'` itself use it as the last character of the +-- option or the end of a range. Example: +-- "^a-z,#,^" (exclude `'a'` to `'z'` , include `'#'` and `'^'` ) +-- If the character is `'@'` , all characters where isalpha() returns TRUE +-- are included. Normally these are the characters a to z and A to Z, +-- plus accented characters. To include `'@'` itself use "@-@". Examples: +-- "@,^a-z" All alphabetic characters, excluding lower +-- case ASCII letters. +-- "a-z,A-Z,@-@" All letters plus the `'@'` character. +-- A comma can be included by using it where a character number is +-- expected. Example: +-- "48-57,,,_" Digits, comma and underscore. +-- A comma can be excluded by prepending a `'^'` . Example: +-- " -~,^,,9" All characters from space to `'~'` , excluding +-- comma, plus . +-- See |option-backslash| about including spaces and backslashes. +--- @class vim.opt.isfname: vim.Option,string[] +--- @operator add: vim.opt.isfname +--- @operator sub: vim.opt.isfname +--- @operator pow: vim.opt.isfname +vim.opt.isfname = "@,48-57,/,.,-,_,+,,,#,$,%,~,=" +vim.opt.isf = vim.opt.isfname +--- @return string[] +function vim.opt.isfname:get()end + +-- `'isident'` `'isi'` string (default for Windows: +-- "@,48-57,_,128-167,224-235" +-- otherwise: "@,48-57,_,192-255") +-- global +-- The characters given by this option are included in identifiers. +-- Identifiers are used in recognizing environment variables and after a +-- match of the `'define'` option. It is also used for "\i" in a +-- |pattern|. See `'isfname'` for a description of the format of this +-- option. For `'@'` only characters up to 255 are used. +-- Careful: If you change this option, it might break expanding +-- environment variables. E.g., when `'/'` is included and Vim tries to +-- expand "$HOME/.local/state/nvim/shada/main.shada". Maybe you should +-- change `'iskeyword'` instead. +--- @class vim.opt.isident: vim.Option,string[] +--- @operator add: vim.opt.isident +--- @operator sub: vim.opt.isident +--- @operator pow: vim.opt.isident +vim.opt.isident = "@,48-57,_,192-255" +vim.opt.isi = vim.opt.isident +--- @return string[] +function vim.opt.isident:get()end + +-- `'iskeyword'` `'isk'` string (default: @,48-57,_,192-255) +-- local to buffer +-- Keywords are used in searching and recognizing with many commands: +-- "w", "*", "[i", etc. It is also used for "\k" in a |pattern|. See +-- `'isfname'` for a description of the format of this option. For `'@'` +-- characters above 255 check the "word" character class (any character +-- that is not white space or punctuation). +-- For C programs you could use "a-z,A-Z,48-57,_,.,-,>". +-- For a help file it is set to all non-blank printable characters except +-- `'*'` , `'"'` and `'|'` (so that CTRL-] on a command finds the help for that +-- command). +-- When the `'lisp'` option is on the `'-'` character is always included. +-- This option also influences syntax highlighting, unless the syntax +-- uses |:syn-iskeyword|. +--- @class vim.opt.iskeyword: vim.Option,string[] +--- @operator add: vim.opt.iskeyword +--- @operator sub: vim.opt.iskeyword +--- @operator pow: vim.opt.iskeyword +vim.opt.iskeyword = "@,48-57,_,192-255" +vim.opt.isk = vim.opt.iskeyword +--- @return string[] +function vim.opt.iskeyword:get()end + +-- `'isprint'` `'isp'` string (default: "@,161-255") +-- global +-- The characters given by this option are displayed directly on the +-- screen. It is also used for "\p" in a |pattern|. The characters from +-- space (ASCII 32) to `'~'` (ASCII 126) are always displayed directly, +-- even when they are not included in `'isprint'` or excluded. See +-- `'isfname'` for a description of the format of this option. +-- +-- Non-printable characters are displayed with two characters: +-- 0 - 31 "^@" - "^_" +-- 32 - 126 always single characters +-- 127 "^?" +-- 128 - 159 "~@" - "~_" +-- 160 - 254 "| " - "|~" +-- 255 "~?" +-- Illegal bytes from 128 to 255 (invalid UTF-8) are +-- displayed as , with the hexadecimal value of the byte. +-- When `'display'` contains "uhex" all unprintable characters are +-- displayed as . +-- The SpecialKey highlighting will be used for unprintable characters. +-- |hl-SpecialKey| +-- +-- Multi-byte characters 256 and above are always included, only the +-- characters up to 255 are specified with this option. When a character +-- is printable but it is not available in the current font, a +-- replacement character will be shown. +-- Unprintable and zero-width Unicode characters are displayed as . +-- There is no option to specify these characters. +--- @class vim.opt.isprint: vim.Option,string[] +--- @operator add: vim.opt.isprint +--- @operator sub: vim.opt.isprint +--- @operator pow: vim.opt.isprint +vim.opt.isprint = "@,161-255" +vim.opt.isp = vim.opt.isprint +--- @return string[] +function vim.opt.isprint:get()end + +-- `'joinspaces'` `'js'` boolean (default off) +-- global +-- Insert two spaces after a `'.'` , `'?'` and `'!'` with a join command. +-- Otherwise only one space is inserted. +--- @class vim.opt.joinspaces: vim.Option,boolean +--- @operator add: vim.opt.joinspaces +--- @operator sub: vim.opt.joinspaces +--- @operator pow: vim.opt.joinspaces +vim.opt.joinspaces = false +vim.opt.js = vim.opt.joinspaces +--- @return boolean +function vim.opt.joinspaces:get()end + +-- `'jumpoptions'` `'jop'` string (default "") +-- global +-- List of words that change the behavior of the |jumplist|. +-- stack Make the jumplist behave like the tagstack or like a +-- web browser. Relative location of entries in the +-- jumplist is preserved at the cost of discarding +-- subsequent entries when navigating backwards in the +-- jumplist and then jumping to a location. +-- |jumplist-stack| +-- +-- view When moving through the jumplist, |changelist|, +-- |alternate-file| or using |mark-motions| try to +-- restore the |mark-view| in which the action occurred. +--- @class vim.opt.jumpoptions: vim.Option,string[] +--- @operator add: vim.opt.jumpoptions +--- @operator sub: vim.opt.jumpoptions +--- @operator pow: vim.opt.jumpoptions +vim.opt.jumpoptions = "" +vim.opt.jop = vim.opt.jumpoptions +--- @return string[] +function vim.opt.jumpoptions:get()end + +-- `'keymap'` `'kmp'` string (default "") +-- local to buffer +-- Name of a keyboard mapping. See |mbyte-keymap|. +-- Setting this option to a valid keymap name has the side effect of +-- setting `'iminsert'` to one, so that the keymap becomes effective. +-- `'imsearch'` is also set to one, unless it was -1 +-- Only normal file name characters can be used, "/\*?[|<>" are illegal. +--- @class vim.opt.keymap: vim.Option,string +--- @operator add: vim.opt.keymap +--- @operator sub: vim.opt.keymap +--- @operator pow: vim.opt.keymap +vim.opt.keymap = "" +vim.opt.kmp = vim.opt.keymap +--- @return string +function vim.opt.keymap:get()end + +-- `'keymodel'` `'km'` string (default "") +-- global +-- List of comma-separated words, which enable special things that keys +-- can do. These values can be used: +-- startsel Using a shifted special key starts selection (either +-- Select mode or Visual mode, depending on "key" being +-- present in `'selectmode'` ). +-- stopsel Using a not-shifted special key stops selection. +-- Special keys in this context are the cursor keys, , , +-- and . +-- The `'keymodel'` option is set by the |:behave| command. +--- @class vim.opt.keymodel: vim.Option,string[] +--- @operator add: vim.opt.keymodel +--- @operator sub: vim.opt.keymodel +--- @operator pow: vim.opt.keymodel +vim.opt.keymodel = "" +vim.opt.km = vim.opt.keymodel +--- @return string[] +function vim.opt.keymodel:get()end + +-- `'keywordprg'` `'kp'` string (default ":Man", Windows: ":help") +-- global or local to buffer |global-local| +-- Program to use for the |K| command. Environment variables are +-- expanded |:set_env|. ":help" may be used to access the Vim internal +-- help. (Note that previously setting the global option to the empty +-- value did this, which is now deprecated.) +-- When the first character is ":", the command is invoked as a Vim +-- Ex command prefixed with [count]. +-- When "man" or "man -s" is used, Vim will automatically translate +-- a [count] for the "K" command to a section number. +-- See |option-backslash| about including spaces and backslashes. +-- Example: > +-- :set keywordprg=man\ -s +-- :set keywordprg=:Man +-- < This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.keywordprg: vim.Option,string +--- @operator add: vim.opt.keywordprg +--- @operator sub: vim.opt.keywordprg +--- @operator pow: vim.opt.keywordprg +vim.opt.keywordprg = ":Man" +vim.opt.kp = vim.opt.keywordprg +--- @return string +function vim.opt.keywordprg:get()end + +-- `'langmap'` `'lmap'` string (default "") +-- global +-- This option allows switching your keyboard into a special language +-- mode. When you are typing text in Insert mode the characters are +-- inserted directly. When in Normal mode the `'langmap'` option takes +-- care of translating these special characters to the original meaning +-- of the key. This means you don't have to change the keyboard mode to +-- be able to execute Normal mode commands. +-- This is the opposite of the `'keymap'` option, where characters are +-- mapped in Insert mode. +-- Also consider setting `'langremap'` to off, to prevent `'langmap'` from +-- applying to characters resulting from a mapping. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +-- +-- Example (for Greek, in UTF-8): > +-- :set langmap=ΑA,ΒB,ΨC,ΔD,ΕE,ΦF,ΓG,ΗH,ΙI,ΞJ,ΚK,ΛL,ΜM,ΝN,ΟO,ΠP,QQ,ΡR,ΣS,ΤT,ΘU,ΩV,WW,ΧX,ΥY,ΖZ,αa,βb,ψc,δd,εe,φf,γg,ηh,ιi,ξj,κk,λl,μm,νn,οo,πp,qq,ρr,σs,τt,θu,ωv,ςw,χx,υy,ζz +-- < Example (exchanges meaning of z and y for commands): > +-- :set langmap=zy,yz,ZY,YZ +-- < +-- The `'langmap'` option is a list of parts, separated with commas. Each +-- part can be in one of two forms: +-- 1. A list of pairs. Each pair is a "from" character immediately +-- followed by the "to" character. Examples: "aA", "aAbBcC". +-- 2. A list of "from" characters, a semi-colon and a list of "to" +-- characters. Example: "abc;ABC" +-- Example: "aA,fgh;FGH,cCdDeE" +-- Special characters need to be preceded with a backslash. These are +-- ";", `','` , `'"'` , `'|'` and backslash itself. +-- +-- This will allow you to activate vim actions without having to switch +-- back and forth between the languages. Your language characters will +-- be understood as normal vim English characters (according to the +-- langmap mappings) in the following cases: +-- o Normal/Visual mode (commands, buffer/register names, user mappings) +-- o Insert/Replace Mode: Register names after CTRL-R +-- o Insert/Replace Mode: Mappings +-- Characters entered in Command-line mode will NOT be affected by +-- this option. Note that this option can be changed at any time +-- allowing to switch between mappings for different languages/encodings. +-- Use a mapping to avoid having to type it each time! +--- @class vim.opt.langmap: vim.Option,string[] +--- @operator add: vim.opt.langmap +--- @operator sub: vim.opt.langmap +--- @operator pow: vim.opt.langmap +vim.opt.langmap = "" +vim.opt.lmap = vim.opt.langmap +--- @return string[] +function vim.opt.langmap:get()end + +-- `'langmenu'` `'lm'` string (default "") +-- global +-- Language to use for menu translation. Tells which file is loaded +-- from the "lang" directory in `'runtimepath'` : > +-- "lang/menu_" .. &langmenu .. ".vim" +-- < (without the spaces). For example, to always use the Dutch menus, no +-- matter what $LANG is set to: > +-- :set langmenu=nl_NL.ISO_8859-1 +-- < When `'langmenu'` is empty, |v:lang| is used. +-- Only normal file name characters can be used, "/\*?[|<>" are illegal. +-- If your $LANG is set to a non-English language but you do want to use +-- the English menus: > +-- :set langmenu=none +-- < This option must be set before loading menus, switching on filetype +-- detection or syntax highlighting. Once the menus are defined setting +-- this option has no effect. But you could do this: > +-- :source $VIMRUNTIME/delmenu.vim +-- :set langmenu=de_DE.ISO_8859-1 +-- :source $VIMRUNTIME/menu.vim +-- < Warning: This deletes all menus that you defined yourself! +--- @class vim.opt.langmenu: vim.Option,string +--- @operator add: vim.opt.langmenu +--- @operator sub: vim.opt.langmenu +--- @operator pow: vim.opt.langmenu +vim.opt.langmenu = "" +vim.opt.lm = vim.opt.langmenu +--- @return string +function vim.opt.langmenu:get()end + +--- @class vim.opt.langnoremap: vim.Option,boolean +--- @operator add: vim.opt.langnoremap +--- @operator sub: vim.opt.langnoremap +--- @operator pow: vim.opt.langnoremap +vim.opt.langnoremap = true +vim.opt.lnr = vim.opt.langnoremap +--- @return boolean +function vim.opt.langnoremap:get()end + +-- `'langremap'` `'lrm'` boolean (default off) +-- global +-- When off, setting `'langmap'` does not apply to characters resulting from +-- a mapping. If setting `'langmap'` disables some of your mappings, make +-- sure this option is off. +--- @class vim.opt.langremap: vim.Option,boolean +--- @operator add: vim.opt.langremap +--- @operator sub: vim.opt.langremap +--- @operator pow: vim.opt.langremap +vim.opt.langremap = false +vim.opt.lrm = vim.opt.langremap +--- @return boolean +function vim.opt.langremap:get()end + +-- `'laststatus'` `'ls'` number (default 2) +-- global +-- The value of this option influences when the last window will have a +-- status line: +-- 0: never +-- 1: only if there are at least two windows +-- 2: always +-- 3: always and ONLY the last window +-- The screen looks nicer with a status line if you have several +-- windows, but it takes another screen line. |status-line| +--- @class vim.opt.laststatus: vim.Option,number +--- @operator add: vim.opt.laststatus +--- @operator sub: vim.opt.laststatus +--- @operator pow: vim.opt.laststatus +vim.opt.laststatus = 2 +vim.opt.ls = vim.opt.laststatus +--- @return number +function vim.opt.laststatus:get()end + +-- `'lazyredraw'` `'lz'` boolean (default off) +-- global +-- When this option is set, the screen will not be redrawn while +-- executing macros, registers and other commands that have not been +-- typed. Also, updating the window title is postponed. To force an +-- update use |:redraw|. +-- This may occasionally cause display errors. It is only meant to be set +-- temporarily when performing an operation where redrawing may cause +-- flickering or cause a slow down. +--- @class vim.opt.lazyredraw: vim.Option,boolean +--- @operator add: vim.opt.lazyredraw +--- @operator sub: vim.opt.lazyredraw +--- @operator pow: vim.opt.lazyredraw +vim.opt.lazyredraw = false +vim.opt.lz = vim.opt.lazyredraw +--- @return boolean +function vim.opt.lazyredraw:get()end + +-- `'linebreak'` `'lbr'` boolean (default off) +-- local to window +-- If on, Vim will wrap long lines at a character in `'breakat'` rather +-- than at the last character that fits on the screen. Unlike +-- `'wrapmargin'` and `'textwidth'` , this does not insert s in the file, +-- it only affects the way the file is displayed, not its contents. +-- If `'breakindent'` is set, line is visually indented. Then, the value +-- of `'showbreak'` is used to put in front of wrapped lines. This option +-- is not used when the `'wrap'` option is off. +-- Note that characters after an are mostly not displayed +-- with the right amount of white space. +--- @class vim.opt.linebreak: vim.Option,boolean +--- @operator add: vim.opt.linebreak +--- @operator sub: vim.opt.linebreak +--- @operator pow: vim.opt.linebreak +vim.opt.linebreak = false +vim.opt.lbr = vim.opt.linebreak +--- @return boolean +function vim.opt.linebreak:get()end + +-- `'lines'` number (default 24 or terminal height) +-- global +-- Number of lines of the Vim window. +-- Normally you don't need to set this. It is done automatically by the +-- terminal initialization code. +-- When Vim is running in the GUI or in a resizable window, setting this +-- option will cause the window size to be changed. When you only want +-- to use the size for the GUI, put the command in your |gvimrc| file. +-- Vim limits the number of lines to what fits on the screen. You can +-- use this command to get the tallest window possible: > +-- :set lines=999 +-- < Minimum value is 2, maximum value is 1000. +--- @class vim.opt.lines: vim.Option,number +--- @operator add: vim.opt.lines +--- @operator sub: vim.opt.lines +--- @operator pow: vim.opt.lines +vim.opt.lines = 24 +--- @return number +function vim.opt.lines:get()end + +-- `'linespace'` `'lsp'` number (default 0) +-- global +-- {only in the GUI} +-- Number of pixel lines inserted between characters. Useful if the font +-- uses the full character cell height, making lines touch each other. +-- When non-zero there is room for underlining. +-- With some fonts there can be too much room between lines (to have +-- space for ascents and descents). Then it makes sense to set +-- `'linespace'` to a negative value. This may cause display problems +-- though! +--- @class vim.opt.linespace: vim.Option,number +--- @operator add: vim.opt.linespace +--- @operator sub: vim.opt.linespace +--- @operator pow: vim.opt.linespace +vim.opt.linespace = 0 +vim.opt.lsp = vim.opt.linespace +--- @return number +function vim.opt.linespace:get()end + +-- `'lisp'` boolean (default off) +-- local to buffer +-- Lisp mode: When is typed in insert mode set the indent for +-- the next line to Lisp standards (well, sort of). Also happens with +-- "cc" or "S". `'autoindent'` must also be on for this to work. The `'p'` +-- flag in `'cpoptions'` changes the method of indenting: Vi compatible or +-- better. Also see `'lispwords'` . +-- The `'-'` character is included in keyword characters. Redefines the +-- "=" operator to use this same indentation algorithm rather than +-- calling an external program if `'equalprg'` is empty. +--- @class vim.opt.lisp: vim.Option,boolean +--- @operator add: vim.opt.lisp +--- @operator sub: vim.opt.lisp +--- @operator pow: vim.opt.lisp +vim.opt.lisp = false +--- @return boolean +function vim.opt.lisp:get()end + +-- `'lispoptions'` `'lop'` string (default "") +-- local to buffer +-- Comma-separated list of items that influence the Lisp indenting when +-- enabled with the |`'lisp'` | option. Currently only one item is +-- supported: +-- expr:1 use `'indentexpr'` for Lisp indenting when it is set +-- expr:0 do not use `'indentexpr'` for Lisp indenting (default) +-- Note that when using `'indentexpr'` the `=` operator indents all the +-- lines, otherwise the first line is not indented (Vi-compatible). +--- @class vim.opt.lispoptions: vim.Option,string[] +--- @operator add: vim.opt.lispoptions +--- @operator sub: vim.opt.lispoptions +--- @operator pow: vim.opt.lispoptions +vim.opt.lispoptions = "" +vim.opt.lop = vim.opt.lispoptions +--- @return string[] +function vim.opt.lispoptions:get()end + +-- `'lispwords'` `'lw'` string (default is very long) +-- global or local to buffer |global-local| +-- Comma-separated list of words that influence the Lisp indenting when +-- enabled with the |`'lisp'` | option. +--- @class vim.opt.lispwords: vim.Option,string[] +--- @operator add: vim.opt.lispwords +--- @operator sub: vim.opt.lispwords +--- @operator pow: vim.opt.lispwords +vim.opt.lispwords = "defun,define,defmacro,set!,lambda,if,case,let,flet,let*,letrec,do,do*,define-syntax,let-syntax,letrec-syntax,destructuring-bind,defpackage,defparameter,defstruct,deftype,defvar,do-all-symbols,do-external-symbols,do-symbols,dolist,dotimes,ecase,etypecase,eval-when,labels,macrolet,multiple-value-bind,multiple-value-call,multiple-value-prog1,multiple-value-setq,prog1,progv,typecase,unless,unwind-protect,when,with-input-from-string,with-open-file,with-open-stream,with-output-to-string,with-package-iterator,define-condition,handler-bind,handler-case,restart-bind,restart-case,with-simple-restart,store-value,use-value,muffle-warning,abort,continue,with-slots,with-slots*,with-accessors,with-accessors*,defclass,defmethod,print-unreadable-object" +vim.opt.lw = vim.opt.lispwords +--- @return string[] +function vim.opt.lispwords:get()end + +-- `'list'` boolean (default off) +-- local to window +-- List mode: By default, show tabs as ">", trailing spaces as "-", and +-- non-breakable space characters as "+". Useful to see the difference +-- between tabs and spaces and for trailing blanks. Further changed by +-- the `'listchars'` option. +-- +-- The cursor is displayed at the start of the space a Tab character +-- occupies, not at the end as usual in Normal mode. To get this cursor +-- position while displaying Tabs with spaces, use: > +-- :set list lcs=tab:\ \ +-- < +-- Note that list mode will also affect formatting (set with `'textwidth'` +-- or `'wrapmargin'` ) when `'cpoptions'` includes `'L'` . See `'listchars'` for +-- changing the way tabs are displayed. +--- @class vim.opt.list: vim.Option,boolean +--- @operator add: vim.opt.list +--- @operator sub: vim.opt.list +--- @operator pow: vim.opt.list +vim.opt.list = false +--- @return boolean +function vim.opt.list:get()end + +-- `'listchars'` `'lcs'` string (default: "tab:> ,trail:-,nbsp:+") +-- global or local to window |global-local| +-- Strings to use in `'list'` mode and for the |:list| command. It is a +-- comma-separated list of string settings. +-- +-- +-- eol:c Character to show at the end of each line. When +-- omitted, there is no extra character at the end of the +-- line. +-- +-- tab:xy[z] Two or three characters to be used to show a tab. +-- The third character is optional. +-- +-- tab:xy The `'x'` is always used, then `'y'` as many times as will +-- fit. Thus "tab:>-" displays: > +-- > +-- >- +-- >-- +-- etc. +-- < +-- tab:xyz The `'z'` is always used, then `'x'` is prepended, and +-- then `'y'` is used as many times as will fit. Thus +-- "tab:<->" displays: > +-- > +-- <> +-- <-> +-- <--> +-- etc. +-- < +-- When "tab:" is omitted, a tab is shown as ^I. +-- +-- space:c Character to show for a space. When omitted, spaces +-- are left blank. +-- +-- multispace:c... +-- One or more characters to use cyclically to show for +-- multiple consecutive spaces. Overrides the "space" +-- setting, except for single spaces. When omitted, the +-- "space" setting is used. For example, +-- `:set listchars=multispace:---+` shows ten consecutive +-- spaces as: > +-- ---+---+-- +-- < +-- +-- lead:c Character to show for leading spaces. When omitted, +-- leading spaces are blank. Overrides the "space" and +-- "multispace" settings for leading spaces. You can +-- combine it with "tab:", for example: > +-- :set listchars+=tab:>-,lead:. +-- < +-- +-- leadmultispace:c... +-- Like the |lcs-multispace| value, but for leading +-- spaces only. Also overrides |lcs-lead| for leading +-- multiple spaces. +-- `:set listchars=leadmultispace:---+` shows ten +-- consecutive leading spaces as: > +-- ---+---+--XXX +-- < +-- Where "XXX" denotes the first non-blank characters in +-- the line. +-- +-- trail:c Character to show for trailing spaces. When omitted, +-- trailing spaces are blank. Overrides the "space" and +-- "multispace" settings for trailing spaces. +-- +-- extends:c Character to show in the last column, when `'wrap'` is +-- off and the line continues beyond the right of the +-- screen. +-- +-- precedes:c Character to show in the first visible column of the +-- physical line, when there is text preceding the +-- character visible in the first column. +-- +-- conceal:c Character to show in place of concealed text, when +-- `'conceallevel'` is set to 1. A space when omitted. +-- +-- nbsp:c Character to show for a non-breakable space character +-- (0xA0 (160 decimal) and U+202F). Left blank when +-- omitted. +-- +-- The characters `':'` and `','` should not be used. UTF-8 characters can +-- be used. All characters must be single width. +-- +-- Each character can be specified as hex: > +-- set listchars=eol:\\x24 +-- set listchars=eol:\\u21b5 +-- set listchars=eol:\\U000021b5 +-- < Note that a double backslash is used. The number of hex characters +-- must be exactly 2 for \\x, 4 for \\u and 8 for \\U. +-- +-- Examples: > +-- :set lcs=tab:>-,trail:- +-- :set lcs=tab:>-,eol:<,nbsp:% +-- :set lcs=extends:>,precedes:< +-- < |hl-NonText| highlighting will be used for "eol", "extends" and +-- "precedes". |hl-Whitespace| for "nbsp", "space", "tab", "multispace", +-- "lead" and "trail". +--- @class vim.opt.listchars: vim.Option,table +--- @operator add: vim.opt.listchars +--- @operator sub: vim.opt.listchars +--- @operator pow: vim.opt.listchars +vim.opt.listchars = "tab:> ,trail:-,nbsp:+" +vim.opt.lcs = vim.opt.listchars +--- @return table +function vim.opt.listchars:get()end + +-- `'loadplugins'` `'lpl'` boolean (default on) +-- global +-- When on the plugin scripts are loaded when starting up |load-plugins|. +-- This option can be reset in your |vimrc| file to disable the loading +-- of plugins. +-- Note that using the "-u NONE" and "--noplugin" command line arguments +-- reset this option. |-u| |--noplugin| +--- @class vim.opt.loadplugins: vim.Option,boolean +--- @operator add: vim.opt.loadplugins +--- @operator sub: vim.opt.loadplugins +--- @operator pow: vim.opt.loadplugins +vim.opt.loadplugins = true +vim.opt.lpl = vim.opt.loadplugins +--- @return boolean +function vim.opt.loadplugins:get()end + +-- `'magic'` boolean (default on) +-- global +-- Changes the special characters that can be used in search patterns. +-- See |pattern|. +-- WARNING: Switching this option off most likely breaks plugins! That +-- is because many patterns assume it's on and will fail when it's off. +-- Only switch it off when working with old Vi scripts. In any other +-- situation write patterns that work when `'magic'` is on. Include "\M" +-- when you want to |/\M|. +--- @class vim.opt.magic: vim.Option,boolean +--- @operator add: vim.opt.magic +--- @operator sub: vim.opt.magic +--- @operator pow: vim.opt.magic +vim.opt.magic = true +--- @return boolean +function vim.opt.magic:get()end + +-- `'makeef'` `'mef'` string (default: "") +-- global +-- Name of the errorfile for the |:make| command (see |:make_makeprg|) +-- and the |:grep| command. +-- When it is empty, an internally generated temp file will be used. +-- When "##" is included, it is replaced by a number to make the name +-- unique. This makes sure that the ":make" command doesn't overwrite an +-- existing file. +-- NOT used for the ":cf" command. See `'errorfile'` for that. +-- Environment variables are expanded |:set_env|. +-- See |option-backslash| about including spaces and backslashes. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.makeef: vim.Option,string +--- @operator add: vim.opt.makeef +--- @operator sub: vim.opt.makeef +--- @operator pow: vim.opt.makeef +vim.opt.makeef = "" +vim.opt.mef = vim.opt.makeef +--- @return string +function vim.opt.makeef:get()end + +-- `'makeencoding'` `'menc'` string (default "") +-- global or local to buffer |global-local| +-- Encoding used for reading the output of external commands. When empty, +-- encoding is not converted. +-- This is used for `:make`, `:lmake`, `:grep`, `:lgrep`, `:grepadd`, +-- `:lgrepadd`, `:cfile`, `:cgetfile`, `:caddfile`, `:lfile`, `:lgetfile`, +-- and `:laddfile`. +-- +-- This would be mostly useful when you use MS-Windows. If iconv is +-- enabled, setting `'makeencoding'` to "char" has the same effect as +-- setting to the system locale encoding. Example: > +-- :set makeencoding=char " system locale is used +-- < +--- @class vim.opt.makeencoding: vim.Option,string +--- @operator add: vim.opt.makeencoding +--- @operator sub: vim.opt.makeencoding +--- @operator pow: vim.opt.makeencoding +vim.opt.makeencoding = "" +vim.opt.menc = vim.opt.makeencoding +--- @return string +function vim.opt.makeencoding:get()end + +-- `'makeprg'` `'mp'` string (default "make") +-- global or local to buffer |global-local| +-- Program to use for the ":make" command. See |:make_makeprg|. +-- This option may contain `'%'` and `'#'` characters (see |:_%| and |:_#|), +-- which are expanded to the current and alternate file name. Use |::S| +-- to escape file names in case they contain special characters. +-- Environment variables are expanded |:set_env|. See |option-backslash| +-- about including spaces and backslashes. +-- Note that a `'|'` must be escaped twice: once for ":set" and once for +-- the interpretation of a command. When you use a filter called +-- "myfilter" do it like this: > +-- :set makeprg=gmake\ \\\|\ myfilter +-- < The placeholder "$*" can be given (even multiple times) to specify +-- where the arguments will be included, for example: > +-- :set makeprg=latex\ \\\\nonstopmode\ \\\\input\\{$*} +-- < This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.makeprg: vim.Option,string +--- @operator add: vim.opt.makeprg +--- @operator sub: vim.opt.makeprg +--- @operator pow: vim.opt.makeprg +vim.opt.makeprg = "make" +vim.opt.mp = vim.opt.makeprg +--- @return string +function vim.opt.makeprg:get()end + +-- `'matchpairs'` `'mps'` string (default "(:),{:},[:]") +-- local to buffer +-- Characters that form pairs. The |%| command jumps from one to the +-- other. +-- Only character pairs are allowed that are different, thus you cannot +-- jump between two double quotes. +-- The characters must be separated by a colon. +-- The pairs must be separated by a comma. Example for including `'<'` and +-- `'>'` (for HTML): > +-- :set mps+=<:> +-- +-- < A more exotic example, to jump between the `'='` and `';'` in an +-- assignment, useful for languages like C and Java: > +-- :au FileType c,cpp,java set mps+==:; +-- +-- < For a more advanced way of using "%", see the matchit.vim plugin in +-- the $VIMRUNTIME/plugin directory. |add-local-help| +--- @class vim.opt.matchpairs: vim.Option,string[] +--- @operator add: vim.opt.matchpairs +--- @operator sub: vim.opt.matchpairs +--- @operator pow: vim.opt.matchpairs +vim.opt.matchpairs = "(:),{:},[:]" +vim.opt.mps = vim.opt.matchpairs +--- @return string[] +function vim.opt.matchpairs:get()end + +-- `'matchtime'` `'mat'` number (default 5) +-- global +-- Tenths of a second to show the matching paren, when `'showmatch'` is +-- set. Note that this is not in milliseconds, like other options that +-- set a time. This is to be compatible with Nvi. +--- @class vim.opt.matchtime: vim.Option,number +--- @operator add: vim.opt.matchtime +--- @operator sub: vim.opt.matchtime +--- @operator pow: vim.opt.matchtime +vim.opt.matchtime = 5 +vim.opt.mat = vim.opt.matchtime +--- @return number +function vim.opt.matchtime:get()end + +--- @class vim.opt.maxcombine: vim.Option,number +--- @operator add: vim.opt.maxcombine +--- @operator sub: vim.opt.maxcombine +--- @operator pow: vim.opt.maxcombine +vim.opt.maxcombine = 6 +vim.opt.mco = vim.opt.maxcombine +--- @return number +function vim.opt.maxcombine:get()end + +-- `'maxfuncdepth'` `'mfd'` number (default 100) +-- global +-- Maximum depth of function calls for user functions. This normally +-- catches endless recursion. When using a recursive function with +-- more depth, set `'maxfuncdepth'` to a bigger number. But this will use +-- more memory, there is the danger of failing when memory is exhausted. +-- Increasing this limit above 200 also changes the maximum for Ex +-- command recursion, see |E169|. +-- See also |:function|. +--- @class vim.opt.maxfuncdepth: vim.Option,number +--- @operator add: vim.opt.maxfuncdepth +--- @operator sub: vim.opt.maxfuncdepth +--- @operator pow: vim.opt.maxfuncdepth +vim.opt.maxfuncdepth = 100 +vim.opt.mfd = vim.opt.maxfuncdepth +--- @return number +function vim.opt.maxfuncdepth:get()end + +-- `'maxmapdepth'` `'mmd'` number (default 1000) +-- global +-- Maximum number of times a mapping is done without resulting in a +-- character to be used. This normally catches endless mappings, like +-- ":map x y" with ":map y x". It still does not catch ":map g wg", +-- because the `'w'` is used before the next mapping is done. See also +-- |key-mapping|. +--- @class vim.opt.maxmapdepth: vim.Option,number +--- @operator add: vim.opt.maxmapdepth +--- @operator sub: vim.opt.maxmapdepth +--- @operator pow: vim.opt.maxmapdepth +vim.opt.maxmapdepth = 1000 +vim.opt.mmd = vim.opt.maxmapdepth +--- @return number +function vim.opt.maxmapdepth:get()end + +-- `'maxmempattern'` `'mmp'` number (default 1000) +-- global +-- Maximum amount of memory (in Kbyte) to use for pattern matching. +-- The maximum value is about 2000000. Use this to work without a limit. +-- +-- When Vim runs into the limit it gives an error message and mostly +-- behaves like CTRL-C was typed. +-- Running into the limit often means that the pattern is very +-- inefficient or too complex. This may already happen with the pattern +-- "\(.\)*" on a very long line. ".*" works much better. +-- Might also happen on redraw, when syntax rules try to match a complex +-- text structure. +-- Vim may run out of memory before hitting the `'maxmempattern'` limit, in +-- which case you get an "Out of memory" error instead. +--- @class vim.opt.maxmempattern: vim.Option,number +--- @operator add: vim.opt.maxmempattern +--- @operator sub: vim.opt.maxmempattern +--- @operator pow: vim.opt.maxmempattern +vim.opt.maxmempattern = 1000 +vim.opt.mmp = vim.opt.maxmempattern +--- @return number +function vim.opt.maxmempattern:get()end + +-- `'menuitems'` `'mis'` number (default 25) +-- global +-- Maximum number of items to use in a menu. Used for menus that are +-- generated from a list of items, e.g., the Buffers menu. Changing this +-- option has no direct effect, the menu must be refreshed first. +--- @class vim.opt.menuitems: vim.Option,number +--- @operator add: vim.opt.menuitems +--- @operator sub: vim.opt.menuitems +--- @operator pow: vim.opt.menuitems +vim.opt.menuitems = 25 +vim.opt.mis = vim.opt.menuitems +--- @return number +function vim.opt.menuitems:get()end + +-- `'mkspellmem'` `'msm'` string (default "460000,2000,500") +-- global +-- Parameters for |:mkspell|. This tunes when to start compressing the +-- word tree. Compression can be slow when there are many words, but +-- it's needed to avoid running out of memory. The amount of memory used +-- per word depends very much on how similar the words are, that's why +-- this tuning is complicated. +-- +-- There are three numbers, separated by commas: +-- {start},{inc},{added} +-- +-- For most languages the uncompressed word tree fits in memory. {start} +-- gives the amount of memory in Kbyte that can be used before any +-- compression is done. It should be a bit smaller than the amount of +-- memory that is available to Vim. +-- +-- When going over the {start} limit the {inc} number specifies the +-- amount of memory in Kbyte that can be allocated before another +-- compression is done. A low number means compression is done after +-- less words are added, which is slow. A high number means more memory +-- will be allocated. +-- +-- After doing compression, {added} times 1024 words can be added before +-- the {inc} limit is ignored and compression is done when any extra +-- amount of memory is needed. A low number means there is a smaller +-- chance of hitting the {inc} limit, less memory is used but it's +-- slower. +-- +-- The languages for which these numbers are important are Italian and +-- Hungarian. The default works for when you have about 512 Mbyte. If +-- you have 1 Gbyte you could use: > +-- :set mkspellmem=900000,3000,800 +-- < If you have less than 512 Mbyte |:mkspell| may fail for some +-- languages, no matter what you set `'mkspellmem'` to. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|. +--- @class vim.opt.mkspellmem: vim.Option,string +--- @operator add: vim.opt.mkspellmem +--- @operator sub: vim.opt.mkspellmem +--- @operator pow: vim.opt.mkspellmem +vim.opt.mkspellmem = "460000,2000,500" +vim.opt.msm = vim.opt.mkspellmem +--- @return string +function vim.opt.mkspellmem:get()end + +-- `'modeline'` `'ml'` boolean (default: on (off for root)) +-- local to buffer +-- If `'modeline'` is on `'modelines'` gives the number of lines that is +-- checked for set commands. If `'modeline'` is off or `'modelines'` is zero +-- no lines are checked. See |modeline|. +--- @class vim.opt.modeline: vim.Option,boolean +--- @operator add: vim.opt.modeline +--- @operator sub: vim.opt.modeline +--- @operator pow: vim.opt.modeline +vim.opt.modeline = true +vim.opt.ml = vim.opt.modeline +--- @return boolean +function vim.opt.modeline:get()end + +-- `'modelineexpr'` `'mle'` boolean (default: off) +-- global +-- When on allow some options that are an expression to be set in the +-- modeline. Check the option for whether it is affected by +-- `'modelineexpr'` . Also see |modeline|. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.modelineexpr: vim.Option,boolean +--- @operator add: vim.opt.modelineexpr +--- @operator sub: vim.opt.modelineexpr +--- @operator pow: vim.opt.modelineexpr +vim.opt.modelineexpr = false +vim.opt.mle = vim.opt.modelineexpr +--- @return boolean +function vim.opt.modelineexpr:get()end + +-- `'modelines'` `'mls'` number (default 5) +-- global +-- If `'modeline'` is on `'modelines'` gives the number of lines that is +-- checked for set commands. If `'modeline'` is off or `'modelines'` is zero +-- no lines are checked. See |modeline|. +--- @class vim.opt.modelines: vim.Option,number +--- @operator add: vim.opt.modelines +--- @operator sub: vim.opt.modelines +--- @operator pow: vim.opt.modelines +vim.opt.modelines = 5 +vim.opt.mls = vim.opt.modelines +--- @return number +function vim.opt.modelines:get()end + +-- `'modifiable'` `'ma'` boolean (default on) +-- local to buffer +-- When off the buffer contents cannot be changed. The `'fileformat'` and +-- `'fileencoding'` options also can't be changed. +-- Can be reset on startup with the |-M| command line argument. +--- @class vim.opt.modifiable: vim.Option,boolean +--- @operator add: vim.opt.modifiable +--- @operator sub: vim.opt.modifiable +--- @operator pow: vim.opt.modifiable +vim.opt.modifiable = true +vim.opt.ma = vim.opt.modifiable +--- @return boolean +function vim.opt.modifiable:get()end + +-- `'modified'` `'mod'` boolean (default off) +-- local to buffer +-- When on, the buffer is considered to be modified. This option is set +-- when: +-- 1. A change was made to the text since it was last written. Using the +-- |undo| command to go back to the original text will reset the +-- option. But undoing changes that were made before writing the +-- buffer will set the option again, since the text is different from +-- when it was written. +-- 2. `'fileformat'` or `'fileencoding'` is different from its original +-- value. The original value is set when the buffer is read or +-- written. A ":set nomodified" command also resets the original +-- values to the current values and the `'modified'` option will be +-- reset. +-- Similarly for `'eol'` and `'bomb'` . +-- This option is not set when a change is made to the buffer as the +-- result of a BufNewFile, BufRead/BufReadPost, BufWritePost, +-- FileAppendPost or VimLeave autocommand event. See |gzip-example| for +-- an explanation. +-- When `'buftype'` is "nowrite" or "nofile" this option may be set, but +-- will be ignored. +-- Note that the text may actually be the same, e.g. `'modified'` is set +-- when using "rA" on an "A". +--- @class vim.opt.modified: vim.Option,boolean +--- @operator add: vim.opt.modified +--- @operator sub: vim.opt.modified +--- @operator pow: vim.opt.modified +vim.opt.modified = false +vim.opt.mod = vim.opt.modified +--- @return boolean +function vim.opt.modified:get()end + +-- `'more'` boolean (default: on) +-- global +-- When on, listings pause when the whole screen is filled. You will get +-- the |more-prompt|. When this option is off there are no pauses, the +-- listing continues until finished. +--- @class vim.opt.more: vim.Option,boolean +--- @operator add: vim.opt.more +--- @operator sub: vim.opt.more +--- @operator pow: vim.opt.more +vim.opt.more = true +--- @return boolean +function vim.opt.more:get()end + +-- `'mouse'` string (default "nvi") +-- global +-- +-- Enables mouse support. For example, to enable the mouse in Normal mode +-- and Visual mode: > +-- :set mouse=nv +-- < +-- To temporarily disable mouse support, hold the shift key while using +-- the mouse. +-- +-- Mouse support can be enabled for different modes: +-- n Normal mode +-- v Visual mode +-- i Insert mode +-- c Command-line mode +-- h all previous modes when editing a help file +-- a all previous modes +-- r for |hit-enter| and |more-prompt| prompt +-- +-- Left-click anywhere in a text buffer to place the cursor there. This +-- works with operators too, e.g. type |d| then left-click to delete text +-- from the current cursor position to the position where you clicked. +-- +-- Drag the |status-line| or vertical separator of a window to resize it. +-- +-- If enabled for "v" (Visual mode) then double-click selects word-wise, +-- triple-click makes it line-wise, and quadruple-click makes it +-- rectangular block-wise. +-- +-- For scrolling with a mouse wheel see |scroll-mouse-wheel|. +-- +-- Note: When enabling the mouse in a terminal, copy/paste will use the +-- "* register if possible. See also `'clipboard'` . +-- +-- Related options: +-- `'mousefocus'` window focus follows mouse pointer +-- `'mousemodel'` what mouse button does which action +-- `'mousehide'` hide mouse pointer while typing text +-- `'selectmode'` whether to start Select mode or Visual mode +-- +-- The :behave command provides some "profiles" for mouse behavior. +-- +-- :be[have] {model} Set behavior for mouse and selection. Valid +-- arguments are: +-- mswin MS-Windows behavior +-- xterm Xterm behavior +-- +-- Using ":behave" changes these options: +-- option mswin xterm ~ +-- `'selectmode'` "mouse,key" "" +-- `'mousemodel'` "popup" "extend" +-- `'keymodel'` "startsel,stopsel" "" +-- `'selection'` "exclusive" "inclusive" +--- @class vim.opt.mouse: vim.Option,string[] +--- @operator add: vim.opt.mouse +--- @operator sub: vim.opt.mouse +--- @operator pow: vim.opt.mouse +vim.opt.mouse = "nvi" +--- @return string[] +function vim.opt.mouse:get()end + +-- `'mousefocus'` `'mousef'` boolean (default off) +-- global +-- The window that the mouse pointer is on is automatically activated. +-- When changing the window layout or window focus in another way, the +-- mouse pointer is moved to the window with keyboard focus. Off is the +-- default because it makes using the pull down menus a little goofy, as +-- a pointer transit may activate a window unintentionally. +--- @class vim.opt.mousefocus: vim.Option,boolean +--- @operator add: vim.opt.mousefocus +--- @operator sub: vim.opt.mousefocus +--- @operator pow: vim.opt.mousefocus +vim.opt.mousefocus = false +vim.opt.mousef = vim.opt.mousefocus +--- @return boolean +function vim.opt.mousefocus:get()end + +-- `'mousehide'` `'mh'` boolean (default on) +-- global +-- {only works in the GUI} +-- When on, the mouse pointer is hidden when characters are typed. +-- The mouse pointer is restored when the mouse is moved. +--- @class vim.opt.mousehide: vim.Option,boolean +--- @operator add: vim.opt.mousehide +--- @operator sub: vim.opt.mousehide +--- @operator pow: vim.opt.mousehide +vim.opt.mousehide = true +vim.opt.mh = vim.opt.mousehide +--- @return boolean +function vim.opt.mousehide:get()end + +-- `'mousemodel'` `'mousem'` string (default "popup_setpos") +-- global +-- Sets the model to use for the mouse. The name mostly specifies what +-- the right mouse button is used for: +-- extend Right mouse button extends a selection. This works +-- like in an xterm. +-- popup Right mouse button pops up a menu. The shifted left +-- mouse button extends a selection. This works like +-- with Microsoft Windows. +-- popup_setpos Like "popup", but the cursor will be moved to the +-- position where the mouse was clicked, and thus the +-- selected operation will act upon the clicked object. +-- If clicking inside a selection, that selection will +-- be acted upon, i.e. no cursor move. This implies of +-- course, that right clicking outside a selection will +-- end Visual mode. +-- Overview of what button does what for each model: +-- mouse extend popup(_setpos) ~ +-- left click place cursor place cursor +-- left drag start selection start selection +-- shift-left search word extend selection +-- right click extend selection popup menu (place cursor) +-- right drag extend selection - +-- middle click paste paste +-- +-- In the "popup" model the right mouse button produces a pop-up menu. +-- Nvim creates a default |popup-menu| but you can redefine it. +-- +-- Note that you can further refine the meaning of buttons with mappings. +-- See |mouse-overview|. But mappings are NOT used for modeless selection. +-- +-- Example: > +-- :map +-- :map +-- :map +-- :map <2-S-LeftMouse> <2-RightMouse> +-- :map <2-S-LeftDrag> <2-RightDrag> +-- :map <2-S-LeftRelease> <2-RightRelease> +-- :map <3-S-LeftMouse> <3-RightMouse> +-- :map <3-S-LeftDrag> <3-RightDrag> +-- :map <3-S-LeftRelease> <3-RightRelease> +-- :map <4-S-LeftMouse> <4-RightMouse> +-- :map <4-S-LeftDrag> <4-RightDrag> +-- :map <4-S-LeftRelease> <4-RightRelease> +-- < +-- Mouse commands requiring the CTRL modifier can be simulated by typing +-- the "g" key before using the mouse: +-- "g" is " (jump to tag under mouse click) +-- "g" is " ("CTRL-T") +-- +-- The `'mousemodel'` option is set by the |:behave| command. +--- @class vim.opt.mousemodel: vim.Option,string +--- @operator add: vim.opt.mousemodel +--- @operator sub: vim.opt.mousemodel +--- @operator pow: vim.opt.mousemodel +vim.opt.mousemodel = "popup_setpos" +vim.opt.mousem = vim.opt.mousemodel +--- @return string +function vim.opt.mousemodel:get()end + +-- `'mousemoveevent'` `'mousemev'` boolean (default off) +-- global +-- When on, mouse move events are delivered to the input queue and are +-- available for mapping. The default, off, avoids the mouse movement +-- overhead except when needed. +-- Warning: Setting this option can make pending mappings to be aborted +-- when the mouse is moved. +--- @class vim.opt.mousemoveevent: vim.Option,boolean +--- @operator add: vim.opt.mousemoveevent +--- @operator sub: vim.opt.mousemoveevent +--- @operator pow: vim.opt.mousemoveevent +vim.opt.mousemoveevent = false +vim.opt.mousemev = vim.opt.mousemoveevent +--- @return boolean +function vim.opt.mousemoveevent:get()end + +-- `'mousescroll'` string (default "ver:3,hor:6") +-- global +-- This option controls the number of lines / columns to scroll by when +-- scrolling with a mouse. The option is a comma separated list of parts. +-- Each part consists of a direction and a count as follows: +-- direction:count,direction:count +-- Direction is one of either "hor" or "ver". "hor" controls horizontal +-- scrolling and "ver" controls vertical scrolling. Count sets the amount +-- to scroll by for the given direction, it should be a non negative +-- integer. Each direction should be set at most once. If a direction +-- is omitted, a default value is used (6 for horizontal scrolling and 3 +-- for vertical scrolling). You can disable mouse scrolling by using +-- a count of 0. +-- +-- Example: > +-- :set mousescroll=ver:5,hor:2 +-- < Will make Nvim scroll 5 lines at a time when scrolling vertically, and +-- scroll 2 columns at a time when scrolling horizontally. +--- @class vim.opt.mousescroll: vim.Option,string[] +--- @operator add: vim.opt.mousescroll +--- @operator sub: vim.opt.mousescroll +--- @operator pow: vim.opt.mousescroll +vim.opt.mousescroll = "ver:3,hor:6" +--- @return string[] +function vim.opt.mousescroll:get()end + +-- `'mouseshape'` `'mouses'` string (default "i:beam,r:beam,s:updown,sd:cross, +-- m:no,ml:up-arrow,v:rightup-arrow") +-- global +-- This option tells Vim what the mouse pointer should look like in +-- different modes. The option is a comma-separated list of parts, much +-- like used for `'guicursor'` . Each part consist of a mode/location-list +-- and an argument-list: +-- mode-list:shape,mode-list:shape,.. +-- The mode-list is a dash separated list of these modes/locations: +-- In a normal window: ~ +-- n Normal mode +-- v Visual mode +-- ve Visual mode with `'selection'` "exclusive" (same as `'v'` , +-- if not specified) +-- o Operator-pending mode +-- i Insert mode +-- r Replace mode +-- +-- Others: ~ +-- c appending to the command-line +-- ci inserting in the command-line +-- cr replacing in the command-line +-- m at the 'Hit ENTER' or `'More'` prompts +-- ml idem, but cursor in the last line +-- e any mode, pointer below last window +-- s any mode, pointer on a status line +-- sd any mode, while dragging a status line +-- vs any mode, pointer on a vertical separator line +-- vd any mode, while dragging a vertical separator line +-- a everywhere +-- +-- The shape is one of the following: +-- avail name looks like ~ +-- w x arrow Normal mouse pointer +-- w x blank no pointer at all (use with care!) +-- w x beam I-beam +-- w x updown up-down sizing arrows +-- w x leftright left-right sizing arrows +-- w x busy The system's usual busy pointer +-- w x no The system's usual "no input" pointer +-- x udsizing indicates up-down resizing +-- x lrsizing indicates left-right resizing +-- x crosshair like a big thin + +-- x hand1 black hand +-- x hand2 white hand +-- x pencil what you write with +-- x question big ? +-- x rightup-arrow arrow pointing right-up +-- w x up-arrow arrow pointing up +-- x any X11 pointer number (see X11/cursorfont.h) +-- +-- The "avail" column contains a `'w'` if the shape is available for Win32, +-- x for X11. +-- Any modes not specified or shapes not available use the normal mouse +-- pointer. +-- +-- Example: > +-- :set mouseshape=s:udsizing,m:no +-- < will make the mouse turn to a sizing arrow over the status lines and +-- indicate no input when the hit-enter prompt is displayed (since +-- clicking the mouse has no effect in this state.) +--- @class vim.opt.mouseshape: vim.Option,string +--- @operator add: vim.opt.mouseshape +--- @operator sub: vim.opt.mouseshape +--- @operator pow: vim.opt.mouseshape +vim.opt.mouseshape = "" +vim.opt.mouses = vim.opt.mouseshape +--- @return string +function vim.opt.mouseshape:get()end + +-- `'mousetime'` `'mouset'` number (default 500) +-- global +-- Defines the maximum time in msec between two mouse clicks for the +-- second click to be recognized as a multi click. +--- @class vim.opt.mousetime: vim.Option,number +--- @operator add: vim.opt.mousetime +--- @operator sub: vim.opt.mousetime +--- @operator pow: vim.opt.mousetime +vim.opt.mousetime = 500 +vim.opt.mouset = vim.opt.mousetime +--- @return number +function vim.opt.mousetime:get()end + +-- `'nrformats'` `'nf'` string (default "bin,hex") +-- local to buffer +-- This defines what bases Vim will consider for numbers when using the +-- CTRL-A and CTRL-X commands for adding to and subtracting from a number +-- respectively; see |CTRL-A| for more info on these commands. +-- alpha If included, single alphabetical characters will be +-- incremented or decremented. This is useful for a list with a +-- letter index a), b), etc. +-- octal If included, numbers that start with a zero will be considered +-- to be octal. Example: Using CTRL-A on "007" results in "010". +-- hex If included, numbers starting with "0x" or "0X" will be +-- considered to be hexadecimal. Example: Using CTRL-X on +-- "0x100" results in "0x0ff". +-- bin If included, numbers starting with "0b" or "0B" will be +-- considered to be binary. Example: Using CTRL-X on +-- "0b1000" subtracts one, resulting in "0b0111". +-- unsigned If included, numbers are recognized as unsigned. Thus a +-- leading dash or negative sign won't be considered as part of +-- the number. Examples: +-- Using CTRL-X on "2020" in "9-2020" results in "9-2019" +-- (without "unsigned" it would become "9-2021"). +-- Using CTRL-A on "2020" in "9-2020" results in "9-2021" +-- (without "unsigned" it would become "9-2019"). +-- Using CTRL-X on "0" or CTRL-A on "18446744073709551615" +-- (2^64 - 1) has no effect, overflow is prevented. +-- Numbers which simply begin with a digit in the range 1-9 are always +-- considered decimal. This also happens for numbers that are not +-- recognized as octal or hex. +--- @class vim.opt.nrformats: vim.Option,string[] +--- @operator add: vim.opt.nrformats +--- @operator sub: vim.opt.nrformats +--- @operator pow: vim.opt.nrformats +vim.opt.nrformats = "bin,hex" +vim.opt.nf = vim.opt.nrformats +--- @return string[] +function vim.opt.nrformats:get()end + +-- `'number'` `'nu'` boolean (default off) +-- local to window +-- Print the line number in front of each line. When the `'n'` option is +-- excluded from `'cpoptions'` a wrapped line will not use the column of +-- line numbers. +-- Use the `'numberwidth'` option to adjust the room for the line number. +-- When a long, wrapped line doesn't start with the first character, `'-'` +-- characters are put before the number. +-- For highlighting see |hl-LineNr|, |hl-CursorLineNr|, and the +-- |:sign-define| "numhl" argument. +-- +-- The `'relativenumber'` option changes the displayed number to be +-- relative to the cursor. Together with `'number'` there are these +-- four combinations (cursor in line 3): +-- +-- `'nonu'` `'nu'` `'nonu'` `'nu'` +-- `'nornu'` `'nornu'` `'rnu'` `'rnu'` +-- > +-- |apple | 1 apple | 2 apple | 2 apple +-- |pear | 2 pear | 1 pear | 1 pear +-- |nobody | 3 nobody | 0 nobody |3 nobody +-- |there | 4 there | 1 there | 1 there +-- < +--- @class vim.opt.number: vim.Option,boolean +--- @operator add: vim.opt.number +--- @operator sub: vim.opt.number +--- @operator pow: vim.opt.number +vim.opt.number = false +vim.opt.nu = vim.opt.number +--- @return boolean +function vim.opt.number:get()end + +-- `'numberwidth'` `'nuw'` number (default: 4) +-- local to window +-- Minimal number of columns to use for the line number. Only relevant +-- when the `'number'` or `'relativenumber'` option is set or printing lines +-- with a line number. Since one space is always between the number and +-- the text, there is one less character for the number itself. +-- The value is the minimum width. A bigger width is used when needed to +-- fit the highest line number in the buffer respectively the number of +-- rows in the window, depending on whether `'number'` or `'relativenumber'` +-- is set. Thus with the Vim default of 4 there is room for a line number +-- up to 999. When the buffer has 1000 lines five columns will be used. +-- The minimum value is 1, the maximum value is 20. +--- @class vim.opt.numberwidth: vim.Option,number +--- @operator add: vim.opt.numberwidth +--- @operator sub: vim.opt.numberwidth +--- @operator pow: vim.opt.numberwidth +vim.opt.numberwidth = 4 +vim.opt.nuw = vim.opt.numberwidth +--- @return number +function vim.opt.numberwidth:get()end + +-- `'omnifunc'` `'ofu'` string (default: empty) +-- local to buffer +-- This option specifies a function to be used for Insert mode omni +-- completion with CTRL-X CTRL-O. |i_CTRL-X_CTRL-O| +-- See |complete-functions| for an explanation of how the function is +-- invoked and what it should return. The value can be the name of a +-- function, a |lambda| or a |Funcref|. See |option-value-function| for +-- more information. +-- This option is usually set by a filetype plugin: +-- |:filetype-plugin-on| +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.omnifunc: vim.Option,string +--- @operator add: vim.opt.omnifunc +--- @operator sub: vim.opt.omnifunc +--- @operator pow: vim.opt.omnifunc +vim.opt.omnifunc = "" +vim.opt.ofu = vim.opt.omnifunc +--- @return string +function vim.opt.omnifunc:get()end + +-- `'opendevice'` `'odev'` boolean (default off) +-- global +-- {only for Windows} +-- Enable reading and writing from devices. This may get Vim stuck on a +-- device that can be opened but doesn't actually do the I/O. Therefore +-- it is off by default. +-- Note that on Windows editing "aux.h", "lpt1.txt" and the like also +-- result in editing a device. +--- @class vim.opt.opendevice: vim.Option,boolean +--- @operator add: vim.opt.opendevice +--- @operator sub: vim.opt.opendevice +--- @operator pow: vim.opt.opendevice +vim.opt.opendevice = false +vim.opt.odev = vim.opt.opendevice +--- @return boolean +function vim.opt.opendevice:get()end + +-- `'operatorfunc'` `'opfunc'` string (default: empty) +-- global +-- This option specifies a function to be called by the |g@| operator. +-- See |:map-operator| for more info and an example. The value can be +-- the name of a function, a |lambda| or a |Funcref|. See +-- |option-value-function| for more information. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.operatorfunc: vim.Option,string +--- @operator add: vim.opt.operatorfunc +--- @operator sub: vim.opt.operatorfunc +--- @operator pow: vim.opt.operatorfunc +vim.opt.operatorfunc = "" +vim.opt.opfunc = vim.opt.operatorfunc +--- @return string +function vim.opt.operatorfunc:get()end + +-- `'packpath'` `'pp'` string (default: see `'runtimepath'` ) +-- Directories used to find packages. See |packages| and |rtp-packages|. +--- @class vim.opt.packpath: vim.Option,string[] +--- @operator add: vim.opt.packpath +--- @operator sub: vim.opt.packpath +--- @operator pow: vim.opt.packpath +vim.opt.packpath = "/home/runner/.config/nvim,/etc/xdg/nvim,/home/runner/.local/share/nvim/site,/usr/local/share/nvim/site,/usr/share/nvim/site,/tmp/nvim/squashfs-root/usr/share/nvim/runtime,/tmp/nvim/squashfs-root/usr/lib/nvim,/usr/share/nvim/site/after,/usr/local/share/nvim/site/after,/home/runner/.local/share/nvim/site/after,/etc/xdg/nvim/after,/home/runner/.config/nvim/after" +vim.opt.pp = vim.opt.packpath +--- @return string[] +function vim.opt.packpath:get()end + +-- `'paragraphs'` `'para'` string (default "IPLPPPQPP TPHPLIPpLpItpplpipbp") +-- global +-- Specifies the nroff macros that separate paragraphs. These are pairs +-- of two letters (see |object-motions|). +--- @class vim.opt.paragraphs: vim.Option,string +--- @operator add: vim.opt.paragraphs +--- @operator sub: vim.opt.paragraphs +--- @operator pow: vim.opt.paragraphs +vim.opt.paragraphs = "IPLPPPQPP TPHPLIPpLpItpplpipbp" +vim.opt.para = vim.opt.paragraphs +--- @return string +function vim.opt.paragraphs:get()end + +--- @class vim.opt.paste: vim.Option,boolean +--- @operator add: vim.opt.paste +--- @operator sub: vim.opt.paste +--- @operator pow: vim.opt.paste +vim.opt.paste = false +--- @return boolean +function vim.opt.paste:get()end + +--- @class vim.opt.pastetoggle: vim.Option,string +--- @operator add: vim.opt.pastetoggle +--- @operator sub: vim.opt.pastetoggle +--- @operator pow: vim.opt.pastetoggle +vim.opt.pastetoggle = "" +vim.opt.pt = vim.opt.pastetoggle +--- @return string +function vim.opt.pastetoggle:get()end + +-- `'patchexpr'` `'pex'` string (default "") +-- global +-- Expression which is evaluated to apply a patch to a file and generate +-- the resulting new version of the file. See |diff-patchexpr|. +--- @class vim.opt.patchexpr: vim.Option,string +--- @operator add: vim.opt.patchexpr +--- @operator sub: vim.opt.patchexpr +--- @operator pow: vim.opt.patchexpr +vim.opt.patchexpr = "" +vim.opt.pex = vim.opt.patchexpr +--- @return string +function vim.opt.patchexpr:get()end + +-- `'patchmode'` `'pm'` string (default "") +-- global +-- When non-empty the oldest version of a file is kept. This can be used +-- to keep the original version of a file if you are changing files in a +-- source distribution. Only the first time that a file is written a +-- copy of the original file will be kept. The name of the copy is the +-- name of the original file with the string in the `'patchmode'` option +-- appended. This option should start with a dot. Use a string like +-- ".orig" or ".org". `'backupdir'` must not be empty for this to work +-- (Detail: The backup file is renamed to the patchmode file after the +-- new file has been successfully written, that's why it must be possible +-- to write a backup file). If there was no file to be backed up, an +-- empty file is created. +-- When the `'backupskip'` pattern matches, a patchmode file is not made. +-- Using `'patchmode'` for compressed files appends the extension at the +-- end (e.g., "file.gz.orig"), thus the resulting name isn't always +-- recognized as a compressed file. +-- Only normal file name characters can be used, "/\*?[|<>" are illegal. +--- @class vim.opt.patchmode: vim.Option,string +--- @operator add: vim.opt.patchmode +--- @operator sub: vim.opt.patchmode +--- @operator pow: vim.opt.patchmode +vim.opt.patchmode = "" +vim.opt.pm = vim.opt.patchmode +--- @return string +function vim.opt.patchmode:get()end + +-- `'path'` `'pa'` string (default on Unix: ".,/usr/include,," +-- other systems: ".,,") +-- global or local to buffer |global-local| +-- This is a list of directories which will be searched when using the +-- |gf|, [f, ]f, ^Wf, |:find|, |:sfind|, |:tabfind| and other commands, +-- provided that the file being searched for has a relative path (not +-- starting with "/", "./" or "../"). The directories in the `'path'` +-- option may be relative or absolute. +-- - Use commas to separate directory names: > +-- :set path=.,/usr/local/include,/usr/include +-- < - Spaces can also be used to separate directory names (for backwards +-- compatibility with version 3.0). To have a space in a directory +-- name, precede it with an extra backslash, and escape the space: > +-- :set path=.,/dir/with\\\ space +-- < - To include a comma in a directory name precede it with an extra +-- backslash: > +-- :set path=.,/dir/with\\,comma +-- < - To search relative to the directory of the current file, use: > +-- :set path=. +-- < - To search in the current directory use an empty string between two +-- commas: > +-- :set path=,, +-- < - A directory name may end in a `':'` or `'/'` . +-- - Environment variables are expanded |:set_env|. +-- - When using |netrw.vim| URLs can be used. For example, adding +-- "https://www.vim.org" will make ":find index.html" work. +-- - Search upwards and downwards in a directory tree using "*", "" and +-- ";". See |file-searching| for info and syntax. +-- - Careful with `'\'` characters, type two to get one in the option: > +-- :set path=.,c:\\include +-- < Or just use `'/'` instead: > +-- :set path=.,c:/include +-- < Don't forget "." or files won't even be found in the same directory as +-- the file! +-- The maximum length is limited. How much depends on the system, mostly +-- it is something like 256 or 1024 characters. +-- You can check if all the include files are found, using the value of +-- `'path'` , see |:checkpath|. +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- directories from the list. This avoids problems when a future version +-- uses another default. To remove the current directory use: > +-- :set path-= +-- < To add the current directory use: > +-- :set path+= +-- < To use an environment variable, you probably need to replace the +-- separator. Here is an example to append $INCL, in which directory +-- names are separated with a semi-colon: > +-- :let &path = &path .. "," .. substitute($INCL, `';'` , `','` , `'g'` ) +-- < Replace the `';'` with a `':'` or whatever separator is used. Note that +-- this doesn't work when $INCL contains a comma or white space. +--- @class vim.opt.path: vim.Option,string[] +--- @operator add: vim.opt.path +--- @operator sub: vim.opt.path +--- @operator pow: vim.opt.path +vim.opt.path = ".,/usr/include,," +vim.opt.pa = vim.opt.path +--- @return string[] +function vim.opt.path:get()end + +-- `'preserveindent'` `'pi'` boolean (default off) +-- local to buffer +-- When changing the indent of the current line, preserve as much of the +-- indent structure as possible. Normally the indent is replaced by a +-- series of tabs followed by spaces as required (unless |`'expandtab'` | is +-- enabled, in which case only spaces are used). Enabling this option +-- means the indent will preserve as many existing characters as possible +-- for indenting, and only add additional tabs or spaces as required. +-- `'expandtab'` does not apply to the preserved white space, a Tab remains +-- a Tab. +-- NOTE: When using ">>" multiple times the resulting indent is a mix of +-- tabs and spaces. You might not like this. +-- Also see `'copyindent'` . +-- Use |:retab| to clean up white space. +--- @class vim.opt.preserveindent: vim.Option,boolean +--- @operator add: vim.opt.preserveindent +--- @operator sub: vim.opt.preserveindent +--- @operator pow: vim.opt.preserveindent +vim.opt.preserveindent = false +vim.opt.pi = vim.opt.preserveindent +--- @return boolean +function vim.opt.preserveindent:get()end + +-- `'previewheight'` `'pvh'` number (default 12) +-- global +-- Default height for a preview window. Used for |:ptag| and associated +-- commands. Used for |CTRL-W_}| when no count is given. +--- @class vim.opt.previewheight: vim.Option,number +--- @operator add: vim.opt.previewheight +--- @operator sub: vim.opt.previewheight +--- @operator pow: vim.opt.previewheight +vim.opt.previewheight = 12 +vim.opt.pvh = vim.opt.previewheight +--- @return number +function vim.opt.previewheight:get()end + +-- `'previewwindow'` `'pvw'` boolean (default off) +-- local to window +-- Identifies the preview window. Only one window can have this option +-- set. It's normally not set directly, but by using one of the commands +-- |:ptag|, |:pedit|, etc. +--- @class vim.opt.previewwindow: vim.Option,boolean +--- @operator add: vim.opt.previewwindow +--- @operator sub: vim.opt.previewwindow +--- @operator pow: vim.opt.previewwindow +vim.opt.previewwindow = false +vim.opt.pvw = vim.opt.previewwindow +--- @return boolean +function vim.opt.previewwindow:get()end + +--- @class vim.opt.prompt: vim.Option,boolean +--- @operator add: vim.opt.prompt +--- @operator sub: vim.opt.prompt +--- @operator pow: vim.opt.prompt +vim.opt.prompt = true +--- @return boolean +function vim.opt.prompt:get()end + +-- `'pumblend'` `'pb'` number (default 0) +-- global +-- Enables pseudo-transparency for the |popup-menu|. Valid values are in +-- the range of 0 for fully opaque popupmenu (disabled) to 100 for fully +-- transparent background. Values between 0-30 are typically most useful. +-- +-- It is possible to override the level for individual highlights within +-- the popupmenu using |highlight-blend|. For instance, to enable +-- transparency but force the current selected element to be fully opaque: > +-- +-- :set pumblend=15 +-- :hi PmenuSel blend=0 +-- < +-- UI-dependent. Works best with RGB colors. `'termguicolors'` +--- @class vim.opt.pumblend: vim.Option,number +--- @operator add: vim.opt.pumblend +--- @operator sub: vim.opt.pumblend +--- @operator pow: vim.opt.pumblend +vim.opt.pumblend = 0 +vim.opt.pb = vim.opt.pumblend +--- @return number +function vim.opt.pumblend:get()end + +-- `'pumheight'` `'ph'` number (default 0) +-- global +-- Maximum number of items to show in the popup menu +-- (|ins-completion-menu|). Zero means "use available screen space". +--- @class vim.opt.pumheight: vim.Option,number +--- @operator add: vim.opt.pumheight +--- @operator sub: vim.opt.pumheight +--- @operator pow: vim.opt.pumheight +vim.opt.pumheight = 0 +vim.opt.ph = vim.opt.pumheight +--- @return number +function vim.opt.pumheight:get()end + +-- `'pumwidth'` `'pw'` number (default 15) +-- global +-- Minimum width for the popup menu (|ins-completion-menu|). If the +-- cursor column + `'pumwidth'` exceeds screen width, the popup menu is +-- nudged to fit on the screen. +--- @class vim.opt.pumwidth: vim.Option,number +--- @operator add: vim.opt.pumwidth +--- @operator sub: vim.opt.pumwidth +--- @operator pow: vim.opt.pumwidth +vim.opt.pumwidth = 15 +vim.opt.pw = vim.opt.pumwidth +--- @return number +function vim.opt.pumwidth:get()end + +-- `'pyxversion'` `'pyx'` number (default 3) +-- global +-- Specifies the python version used for pyx* functions and commands +-- |python_x|. As only Python 3 is supported, this always has the value +-- `3`. Setting any other value is an error. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.pyxversion: vim.Option,number +--- @operator add: vim.opt.pyxversion +--- @operator sub: vim.opt.pyxversion +--- @operator pow: vim.opt.pyxversion +vim.opt.pyxversion = 3 +vim.opt.pyx = vim.opt.pyxversion +--- @return number +function vim.opt.pyxversion:get()end + +-- `'quickfixtextfunc'` `'qftf'` string (default "") +-- global +-- This option specifies a function to be used to get the text to display +-- in the quickfix and location list windows. This can be used to +-- customize the information displayed in the quickfix or location window +-- for each entry in the corresponding quickfix or location list. See +-- |quickfix-window-function| for an explanation of how to write the +-- function and an example. The value can be the name of a function, a +-- |lambda| or a |Funcref|. See |option-value-function| for more +-- information. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.quickfixtextfunc: vim.Option,string +--- @operator add: vim.opt.quickfixtextfunc +--- @operator sub: vim.opt.quickfixtextfunc +--- @operator pow: vim.opt.quickfixtextfunc +vim.opt.quickfixtextfunc = "" +vim.opt.qftf = vim.opt.quickfixtextfunc +--- @return string +function vim.opt.quickfixtextfunc:get()end + +-- `'quoteescape'` `'qe'` string (default "\") +-- local to buffer +-- The characters that are used to escape quotes in a string. Used for +-- objects like a', a" and a` |a'|. +-- When one of the characters in this option is found inside a string, +-- the following character will be skipped. The default value makes the +-- text "foo\"bar\\" considered to be one string. +--- @class vim.opt.quoteescape: vim.Option,string +--- @operator add: vim.opt.quoteescape +--- @operator sub: vim.opt.quoteescape +--- @operator pow: vim.opt.quoteescape +vim.opt.quoteescape = "\\" +vim.opt.qe = vim.opt.quoteescape +--- @return string +function vim.opt.quoteescape:get()end + +-- `'readonly'` `'ro'` boolean (default off) +-- local to buffer +-- If on, writes fail unless you use a `'!'` . Protects you from +-- accidentally overwriting a file. Default on when Vim is started +-- in read-only mode ("vim -R") or when the executable is called "view". +-- When using ":w!" the `'readonly'` option is reset for the current +-- buffer, unless the `'Z'` flag is in `'cpoptions'` . +-- When using the ":view" command the `'readonly'` option is set for the +-- newly edited buffer. +-- See `'modifiable'` for disallowing changes to the buffer. +--- @class vim.opt.readonly: vim.Option,boolean +--- @operator add: vim.opt.readonly +--- @operator sub: vim.opt.readonly +--- @operator pow: vim.opt.readonly +vim.opt.readonly = false +vim.opt.ro = vim.opt.readonly +--- @return boolean +function vim.opt.readonly:get()end + +-- `'redrawdebug'` `'rdb'` string (default `''` ) +-- global +-- Flags to change the way redrawing works, for debugging purposes. +-- Most useful with `'writedelay'` set to some reasonable value. +-- Supports the following flags: +-- compositor Indicate each redraw event handled by the compositor +-- by briefly flashing the redrawn regions in colors +-- indicating the redraw type. These are the highlight +-- groups used (and their default colors): +-- RedrawDebugNormal gui=reverse normal redraw passed through +-- RedrawDebugClear guibg=Yellow clear event passed through +-- RedrawDebugComposed guibg=Green redraw event modified by the +-- compositor (due to +-- overlapping grids, etc) +-- RedrawDebugRecompose guibg=Red redraw generated by the +-- compositor itself, due to a +-- grid being moved or deleted. +-- line introduce a delay after each line drawn on the screen. +-- When using the TUI or another single-grid UI, "compositor" +-- gives more information and should be preferred (every +-- line is processed as a separate event by the compositor) +-- flush introduce a delay after each "flush" event. +-- nothrottle Turn off throttling of the message grid. This is an +-- optimization that joins many small scrolls to one +-- larger scroll when drawing the message area (with +-- `'display'` msgsep flag active). +-- invalid Enable stricter checking (abort) of inconsistencies +-- of the internal screen state. This is mostly +-- useful when running nvim inside a debugger (and +-- the test suite). +-- nodelta Send all internally redrawn cells to the UI, even if +-- they are unchanged from the already displayed state. +--- @class vim.opt.redrawdebug: vim.Option,string[] +--- @operator add: vim.opt.redrawdebug +--- @operator sub: vim.opt.redrawdebug +--- @operator pow: vim.opt.redrawdebug +vim.opt.redrawdebug = "" +vim.opt.rdb = vim.opt.redrawdebug +--- @return string[] +function vim.opt.redrawdebug:get()end + +-- `'redrawtime'` `'rdt'` number (default 2000) +-- global +-- Time in milliseconds for redrawing the display. Applies to +-- `'hlsearch'` , `'inccommand'` , |:match| highlighting and syntax +-- highlighting. +-- When redrawing takes more than this many milliseconds no further +-- matches will be highlighted. +-- For syntax highlighting the time applies per window. When over the +-- limit syntax highlighting is disabled until |CTRL-L| is used. +-- This is used to avoid that Vim hangs when using a very complicated +-- pattern. +--- @class vim.opt.redrawtime: vim.Option,number +--- @operator add: vim.opt.redrawtime +--- @operator sub: vim.opt.redrawtime +--- @operator pow: vim.opt.redrawtime +vim.opt.redrawtime = 2000 +vim.opt.rdt = vim.opt.redrawtime +--- @return number +function vim.opt.redrawtime:get()end + +-- `'regexpengine'` `'re'` number (default 0) +-- global +-- This selects the default regexp engine. |two-engines| +-- The possible values are: +-- 0 automatic selection +-- 1 old engine +-- 2 NFA engine +-- Note that when using the NFA engine and the pattern contains something +-- that is not supported the pattern will not match. This is only useful +-- for debugging the regexp engine. +-- Using automatic selection enables Vim to switch the engine, if the +-- default engine becomes too costly. E.g., when the NFA engine uses too +-- many states. This should prevent Vim from hanging on a combination of +-- a complex pattern with long text. +--- @class vim.opt.regexpengine: vim.Option,number +--- @operator add: vim.opt.regexpengine +--- @operator sub: vim.opt.regexpengine +--- @operator pow: vim.opt.regexpengine +vim.opt.regexpengine = 0 +vim.opt.re = vim.opt.regexpengine +--- @return number +function vim.opt.regexpengine:get()end + +-- `'relativenumber'` `'rnu'` boolean (default off) +-- local to window +-- Show the line number relative to the line with the cursor in front of +-- each line. Relative line numbers help you use the |count| you can +-- precede some vertical motion commands (e.g. j k + -) with, without +-- having to calculate it yourself. Especially useful in combination with +-- other commands (e.g. y d c < > gq gw =). +-- When the `'n'` option is excluded from `'cpoptions'` a wrapped +-- line will not use the column of line numbers. +-- The `'numberwidth'` option can be used to set the room used for the line +-- number. +-- When a long, wrapped line doesn't start with the first character, `'-'` +-- characters are put before the number. +-- See |hl-LineNr| and |hl-CursorLineNr| for the highlighting used for +-- the number. +-- +-- The number in front of the cursor line also depends on the value of +-- `'number'` , see |number_relativenumber| for all combinations of the two +-- options. +--- @class vim.opt.relativenumber: vim.Option,boolean +--- @operator add: vim.opt.relativenumber +--- @operator sub: vim.opt.relativenumber +--- @operator pow: vim.opt.relativenumber +vim.opt.relativenumber = false +vim.opt.rnu = vim.opt.relativenumber +--- @return boolean +function vim.opt.relativenumber:get()end + +--- @class vim.opt.remap: vim.Option,boolean +--- @operator add: vim.opt.remap +--- @operator sub: vim.opt.remap +--- @operator pow: vim.opt.remap +vim.opt.remap = true +--- @return boolean +function vim.opt.remap:get()end + +-- `'report'` number (default 2) +-- global +-- Threshold for reporting number of lines changed. When the number of +-- changed lines is more than `'report'` a message will be given for most +-- ":" commands. If you want it always, set `'report'` to 0. +-- For the ":substitute" command the number of substitutions is used +-- instead of the number of lines. +--- @class vim.opt.report: vim.Option,number +--- @operator add: vim.opt.report +--- @operator sub: vim.opt.report +--- @operator pow: vim.opt.report +vim.opt.report = 2 +--- @return number +function vim.opt.report:get()end + +-- `'revins'` `'ri'` boolean (default off) +-- global +-- Inserting characters in Insert mode will work backwards. See "typing +-- backwards" |ins-reverse|. This option can be toggled with the CTRL-_ +-- command in Insert mode, when `'allowrevins'` is set. +--- @class vim.opt.revins: vim.Option,boolean +--- @operator add: vim.opt.revins +--- @operator sub: vim.opt.revins +--- @operator pow: vim.opt.revins +vim.opt.revins = false +vim.opt.ri = vim.opt.revins +--- @return boolean +function vim.opt.revins:get()end + +-- `'rightleft'` `'rl'` boolean (default off) +-- local to window +-- When on, display orientation becomes right-to-left, i.e., characters +-- that are stored in the file appear from the right to the left. +-- Using this option, it is possible to edit files for languages that +-- are written from the right to the left such as Hebrew and Arabic. +-- This option is per window, so it is possible to edit mixed files +-- simultaneously, or to view the same file in both ways (this is +-- useful whenever you have a mixed text file with both right-to-left +-- and left-to-right strings so that both sets are displayed properly +-- in different windows). Also see |rileft.txt|. +--- @class vim.opt.rightleft: vim.Option,boolean +--- @operator add: vim.opt.rightleft +--- @operator sub: vim.opt.rightleft +--- @operator pow: vim.opt.rightleft +vim.opt.rightleft = false +vim.opt.rl = vim.opt.rightleft +--- @return boolean +function vim.opt.rightleft:get()end + +-- `'rightleftcmd'` `'rlc'` string (default "search") +-- local to window +-- Each word in this option enables the command line editing to work in +-- right-to-left mode for a group of commands: +-- +-- search "/" and "?" commands +-- +-- This is useful for languages such as Hebrew, Arabic and Farsi. +-- The `'rightleft'` option must be set for `'rightleftcmd'` to take effect. +--- @class vim.opt.rightleftcmd: vim.Option,string +--- @operator add: vim.opt.rightleftcmd +--- @operator sub: vim.opt.rightleftcmd +--- @operator pow: vim.opt.rightleftcmd +vim.opt.rightleftcmd = "search" +vim.opt.rlc = vim.opt.rightleftcmd +--- @return string +function vim.opt.rightleftcmd:get()end + +-- `'ruler'` `'ru'` boolean (default on) +-- global +-- Show the line and column number of the cursor position, separated by a +-- comma. When there is room, the relative position of the displayed +-- text in the file is shown on the far right: +-- Top first line is visible +-- Bot last line is visible +-- All first and last line are visible +-- 45% relative position in the file +-- If `'rulerformat'` is set, it will determine the contents of the ruler. +-- Each window has its own ruler. If a window has a status line, the +-- ruler is shown there. If a window doesn't have a status line and +-- `'cmdheight'` is zero, the ruler is not shown. Otherwise it is shown in +-- the last line of the screen. If the statusline is given by +-- `'statusline'` (i.e. not empty), this option takes precedence over +-- `'ruler'` and `'rulerformat'` . +-- If the number of characters displayed is different from the number of +-- bytes in the text (e.g., for a TAB or a multibyte character), both +-- the text column (byte number) and the screen column are shown, +-- separated with a dash. +-- For an empty line "0-1" is shown. +-- For an empty buffer the line number will also be zero: "0,0-1". +-- If you don't want to see the ruler all the time but want to know where +-- you are, use "g CTRL-G" |g_CTRL-G|. +--- @class vim.opt.ruler: vim.Option,boolean +--- @operator add: vim.opt.ruler +--- @operator sub: vim.opt.ruler +--- @operator pow: vim.opt.ruler +vim.opt.ruler = true +vim.opt.ru = vim.opt.ruler +--- @return boolean +function vim.opt.ruler:get()end + +-- `'rulerformat'` `'ruf'` string (default empty) +-- global +-- When this option is not empty, it determines the content of the ruler +-- string, as displayed for the `'ruler'` option. +-- The format of this option is like that of `'statusline'` . +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- The default ruler width is 17 characters. To make the ruler 15 +-- characters wide, put "%15(" at the start and "%)" at the end. +-- Example: > +-- :set rulerformat=%15(%c%V\ %p%%%) +-- < +--- @class vim.opt.rulerformat: vim.Option,string +--- @operator add: vim.opt.rulerformat +--- @operator sub: vim.opt.rulerformat +--- @operator pow: vim.opt.rulerformat +vim.opt.rulerformat = "" +vim.opt.ruf = vim.opt.rulerformat +--- @return string +function vim.opt.rulerformat:get()end + +-- `'runtimepath'` `'rtp'` string (default: "$XDG_CONFIG_HOME/nvim, +-- $XDG_CONFIG_DIRS[1]/nvim, +-- $XDG_CONFIG_DIRS[2]/nvim, +-- … +-- $XDG_DATA_HOME/nvim[-data]/site, +-- $XDG_DATA_DIRS[1]/nvim/site, +-- $XDG_DATA_DIRS[2]/nvim/site, +-- … +-- $VIMRUNTIME, +-- … +-- $XDG_DATA_DIRS[2]/nvim/site/after, +-- $XDG_DATA_DIRS[1]/nvim/site/after, +-- $XDG_DATA_HOME/nvim[-data]/site/after, +-- … +-- $XDG_CONFIG_DIRS[2]/nvim/after, +-- $XDG_CONFIG_DIRS[1]/nvim/after, +-- $XDG_CONFIG_HOME/nvim/after") +-- global +-- List of directories to be searched for these runtime files: +-- filetype.lua filetypes |new-filetype| +-- autoload/ automatically loaded scripts |autoload-functions| +-- colors/ color scheme files |:colorscheme| +-- compiler/ compiler files |:compiler| +-- doc/ documentation |write-local-help| +-- ftplugin/ filetype plugins |write-filetype-plugin| +-- indent/ indent scripts |indent-expression| +-- keymap/ key mapping files |mbyte-keymap| +-- lang/ menu translations |:menutrans| +-- lua/ |Lua| plugins +-- menu.vim GUI menus |menu.vim| +-- pack/ packages |:packadd| +-- parser/ |treesitter| syntax parsers +-- plugin/ plugin scripts |write-plugin| +-- queries/ |treesitter| queries +-- rplugin/ |remote-plugin| scripts +-- spell/ spell checking files |spell| +-- syntax/ syntax files |mysyntaxfile| +-- tutor/ tutorial files |:Tutor| +-- +-- And any other file searched for with the |:runtime| command. +-- +-- Defaults are setup to search these locations: +-- 1. Your home directory, for personal preferences. +-- Given by `stdpath("config")`. |$XDG_CONFIG_HOME| +-- 2. Directories which must contain configuration files according to +-- |xdg| ($XDG_CONFIG_DIRS, defaults to /etc/xdg). This also contains +-- preferences from system administrator. +-- 3. Data home directory, for plugins installed by user. +-- Given by `stdpath("data")/site`. |$XDG_DATA_HOME| +-- 4. nvim/site subdirectories for each directory in $XDG_DATA_DIRS. +-- This is for plugins which were installed by system administrator, +-- but are not part of the Nvim distribution. XDG_DATA_DIRS defaults +-- to /usr/local/share/:/usr/share/, so system administrators are +-- expected to install site plugins to /usr/share/nvim/site. +-- 5. Session state directory, for state data such as swap, backupdir, +-- viewdir, undodir, etc. +-- Given by `stdpath("state")`. |$XDG_STATE_HOME| +-- 6. $VIMRUNTIME, for files distributed with Nvim. +-- +-- 7, 8, 9, 10. In after/ subdirectories of 1, 2, 3 and 4, with reverse +-- ordering. This is for preferences to overrule or add to the +-- distributed defaults or system-wide settings (rarely needed). +-- +-- +-- "start" packages will also be searched (|runtime-search-path|) for +-- runtime files after these, though such packages are not explicitly +-- reported in &runtimepath. But "opt" packages are explicitly added to +-- &runtimepath by |:packadd|. +-- +-- Note that, unlike `'path'` , no wildcards like "" are allowed. Normal +-- wildcards are allowed, but can significantly slow down searching for +-- runtime files. For speed, use as few items as possible and avoid +-- wildcards. +-- See |:runtime|. +-- Example: > +-- :set runtimepath=~/vimruntime,/mygroup/vim,$VIMRUNTIME +-- < This will use the directory "~/vimruntime" first (containing your +-- personal Nvim runtime files), then "/mygroup/vim", and finally +-- "$VIMRUNTIME" (the default runtime files). +-- You can put a directory before $VIMRUNTIME to find files which replace +-- distributed runtime files. You can put a directory after $VIMRUNTIME +-- to find files which add to distributed runtime files. +-- +-- With |--clean| the home directory entries are not included. +--- @class vim.opt.runtimepath: vim.Option,string[] +--- @operator add: vim.opt.runtimepath +--- @operator sub: vim.opt.runtimepath +--- @operator pow: vim.opt.runtimepath +vim.opt.runtimepath = "/home/runner/.config/nvim,/etc/xdg/nvim,/home/runner/.local/share/nvim/site,/usr/local/share/nvim/site,/usr/share/nvim/site,/tmp/nvim/squashfs-root/usr/share/nvim/runtime,/tmp/nvim/squashfs-root/usr/lib/nvim,/usr/share/nvim/site/after,/usr/local/share/nvim/site/after,/home/runner/.local/share/nvim/site/after,/etc/xdg/nvim/after,/home/runner/.config/nvim/after" +vim.opt.rtp = vim.opt.runtimepath +--- @return string[] +function vim.opt.runtimepath:get()end + +-- `'scroll'` `'scr'` number (default: half the window height) +-- local to window +-- Number of lines to scroll with CTRL-U and CTRL-D commands. Will be +-- set to half the number of lines in the window when the window size +-- changes. This may happen when enabling the |status-line| or +-- `'tabline'` option after setting the `'scroll'` option. +-- If you give a count to the CTRL-U or CTRL-D command it will +-- be used as the new value for `'scroll'` . Reset to half the window +-- height with ":set scroll=0". +--- @class vim.opt.scroll: vim.Option,number +--- @operator add: vim.opt.scroll +--- @operator sub: vim.opt.scroll +--- @operator pow: vim.opt.scroll +vim.opt.scroll = 0 +vim.opt.scr = vim.opt.scroll +--- @return number +function vim.opt.scroll:get()end + +-- `'scrollback'` `'scbk'` number (default: 10000) +-- local to buffer +-- Maximum number of lines kept beyond the visible screen. Lines at the +-- top are deleted if new lines exceed this limit. +-- Minimum is 1, maximum is 100000. +-- Only in |terminal| buffers. +--- @class vim.opt.scrollback: vim.Option,number +--- @operator add: vim.opt.scrollback +--- @operator sub: vim.opt.scrollback +--- @operator pow: vim.opt.scrollback +vim.opt.scrollback = -1 +vim.opt.scbk = vim.opt.scrollback +--- @return number +function vim.opt.scrollback:get()end + +-- `'scrollbind'` `'scb'` boolean (default off) +-- local to window +-- See also |scroll-binding|. When this option is set, the current +-- window scrolls as other scrollbind windows (windows that also have +-- this option set) scroll. This option is useful for viewing the +-- differences between two versions of a file, see `'diff'` . +-- See |`'scrollopt'` | for options that determine how this option should be +-- interpreted. +-- This option is mostly reset when splitting a window to edit another +-- file. This means that ":split | edit file" results in two windows +-- with scroll-binding, but ":split file" does not. +--- @class vim.opt.scrollbind: vim.Option,boolean +--- @operator add: vim.opt.scrollbind +--- @operator sub: vim.opt.scrollbind +--- @operator pow: vim.opt.scrollbind +vim.opt.scrollbind = false +vim.opt.scb = vim.opt.scrollbind +--- @return boolean +function vim.opt.scrollbind:get()end + +-- `'scrolljump'` `'sj'` number (default 1) +-- global +-- Minimal number of lines to scroll when the cursor gets off the +-- screen (e.g., with "j"). Not used for scroll commands (e.g., CTRL-E, +-- CTRL-D). Useful if your terminal scrolls very slowly. +-- When set to a negative number from -1 to -100 this is used as the +-- percentage of the window height. Thus -50 scrolls half the window +-- height. +--- @class vim.opt.scrolljump: vim.Option,number +--- @operator add: vim.opt.scrolljump +--- @operator sub: vim.opt.scrolljump +--- @operator pow: vim.opt.scrolljump +vim.opt.scrolljump = 1 +vim.opt.sj = vim.opt.scrolljump +--- @return number +function vim.opt.scrolljump:get()end + +-- `'scrolloff'` `'so'` number (default 0) +-- global or local to window |global-local| +-- Minimal number of screen lines to keep above and below the cursor. +-- This will make some context visible around where you are working. If +-- you set it to a very large value (999) the cursor line will always be +-- in the middle of the window (except at the start or end of the file or +-- when long lines wrap). +-- After using the local value, go back the global value with one of +-- these two: > +-- setlocal scrolloff< +-- setlocal scrolloff=-1 +-- < For scrolling horizontally see `'sidescrolloff'` . +--- @class vim.opt.scrolloff: vim.Option,number +--- @operator add: vim.opt.scrolloff +--- @operator sub: vim.opt.scrolloff +--- @operator pow: vim.opt.scrolloff +vim.opt.scrolloff = 0 +vim.opt.so = vim.opt.scrolloff +--- @return number +function vim.opt.scrolloff:get()end + +-- `'scrollopt'` `'sbo'` string (default "ver,jump") +-- global +-- This is a comma-separated list of words that specifies how +-- `'scrollbind'` windows should behave. `'sbo'` stands for ScrollBind +-- Options. +-- The following words are available: +-- ver Bind vertical scrolling for `'scrollbind'` windows +-- hor Bind horizontal scrolling for `'scrollbind'` windows +-- jump Applies to the offset between two windows for vertical +-- scrolling. This offset is the difference in the first +-- displayed line of the bound windows. When moving +-- around in a window, another `'scrollbind'` window may +-- reach a position before the start or after the end of +-- the buffer. The offset is not changed though, when +-- moving back the `'scrollbind'` window will try to scroll +-- to the desired position when possible. +-- When now making that window the current one, two +-- things can be done with the relative offset: +-- 1. When "jump" is not included, the relative offset is +-- adjusted for the scroll position in the new current +-- window. When going back to the other window, the +-- new relative offset will be used. +-- 2. When "jump" is included, the other windows are +-- scrolled to keep the same relative offset. When +-- going back to the other window, it still uses the +-- same relative offset. +-- Also see |scroll-binding|. +-- When `'diff'` mode is active there always is vertical scroll binding, +-- even when "ver" isn't there. +--- @class vim.opt.scrollopt: vim.Option,string[] +--- @operator add: vim.opt.scrollopt +--- @operator sub: vim.opt.scrollopt +--- @operator pow: vim.opt.scrollopt +vim.opt.scrollopt = "ver,jump" +vim.opt.sbo = vim.opt.scrollopt +--- @return string[] +function vim.opt.scrollopt:get()end + +-- `'sections'` `'sect'` string (default "SHNHH HUnhsh") +-- global +-- Specifies the nroff macros that separate sections. These are pairs of +-- two letters (See |object-motions|). The default makes a section start +-- at the nroff macros ".SH", ".NH", ".H", ".HU", ".nh" and ".sh". +--- @class vim.opt.sections: vim.Option,string +--- @operator add: vim.opt.sections +--- @operator sub: vim.opt.sections +--- @operator pow: vim.opt.sections +vim.opt.sections = "SHNHH HUnhsh" +vim.opt.sect = vim.opt.sections +--- @return string +function vim.opt.sections:get()end + +--- @class vim.opt.secure: vim.Option,boolean +--- @operator add: vim.opt.secure +--- @operator sub: vim.opt.secure +--- @operator pow: vim.opt.secure +vim.opt.secure = false +--- @return boolean +function vim.opt.secure:get()end + +-- `'selection'` `'sel'` string (default "inclusive") +-- global +-- This option defines the behavior of the selection. It is only used +-- in Visual and Select mode. +-- Possible values: +-- value past line inclusive ~ +-- old no yes +-- inclusive yes yes +-- exclusive yes no +-- "past line" means that the cursor is allowed to be positioned one +-- character past the line. +-- "inclusive" means that the last character of the selection is included +-- in an operation. For example, when "x" is used to delete the +-- selection. +-- When "old" is used and `'virtualedit'` allows the cursor to move past +-- the end of line the line break still isn't included. +-- Note that when "exclusive" is used and selecting from the end +-- backwards, you cannot include the last character of a line, when +-- starting in Normal mode and `'virtualedit'` empty. +-- +-- The `'selection'` option is set by the |:behave| command. +--- @class vim.opt.selection: vim.Option,string +--- @operator add: vim.opt.selection +--- @operator sub: vim.opt.selection +--- @operator pow: vim.opt.selection +vim.opt.selection = "inclusive" +vim.opt.sel = vim.opt.selection +--- @return string +function vim.opt.selection:get()end + +-- `'selectmode'` `'slm'` string (default "") +-- global +-- This is a comma-separated list of words, which specifies when to start +-- Select mode instead of Visual mode, when a selection is started. +-- Possible values: +-- mouse when using the mouse +-- key when using shifted special keys +-- cmd when using "v", "V" or CTRL-V +-- See |Select-mode|. +-- The `'selectmode'` option is set by the |:behave| command. +--- @class vim.opt.selectmode: vim.Option,string[] +--- @operator add: vim.opt.selectmode +--- @operator sub: vim.opt.selectmode +--- @operator pow: vim.opt.selectmode +vim.opt.selectmode = "" +vim.opt.slm = vim.opt.selectmode +--- @return string[] +function vim.opt.selectmode:get()end + +-- `'sessionoptions'` `'ssop'` string (default: "blank,buffers,curdir,folds, +-- help,tabpages,winsize,terminal") +-- global +-- Changes the effect of the |:mksession| command. It is a comma- +-- separated list of words. Each word enables saving and restoring +-- something: +-- word save and restore ~ +-- blank empty windows +-- buffers hidden and unloaded buffers, not just those in windows +-- curdir the current directory +-- folds manually created folds, opened/closed folds and local +-- fold options +-- globals global variables that start with an uppercase letter +-- and contain at least one lowercase letter. Only +-- String and Number types are stored. +-- help the help window +-- localoptions options and mappings local to a window or buffer (not +-- global values for local options) +-- options all options and mappings (also global values for local +-- options) +-- skiprtp exclude `'runtimepath'` and `'packpath'` from the options +-- resize size of the Vim window: `'lines'` and `'columns'` +-- sesdir the directory in which the session file is located +-- will become the current directory (useful with +-- projects accessed over a network from different +-- systems) +-- tabpages all tab pages; without this only the current tab page +-- is restored, so that you can make a session for each +-- tab page separately +-- terminal include terminal windows where the command can be +-- restored +-- winpos position of the whole Vim window +-- winsize window sizes +-- slash |deprecated| Always enabled. Uses "/" in filenames. +-- unix |deprecated| Always enabled. Uses "\n" line endings. +-- +-- Don't include both "curdir" and "sesdir". When neither is included +-- filenames are stored as absolute paths. +-- If you leave out "options" many things won't work well after restoring +-- the session. +--- @class vim.opt.sessionoptions: vim.Option,string[] +--- @operator add: vim.opt.sessionoptions +--- @operator sub: vim.opt.sessionoptions +--- @operator pow: vim.opt.sessionoptions +vim.opt.sessionoptions = "blank,buffers,curdir,folds,help,tabpages,winsize,terminal" +vim.opt.ssop = vim.opt.sessionoptions +--- @return string[] +function vim.opt.sessionoptions:get()end + +-- `'shada'` `'sd'` string (default for +-- Win32: !,'100,<50,s10,h,rA:,rB: +-- others: !,'100,<50,s10,h) +-- global +-- When non-empty, the shada file is read upon startup and written +-- when exiting Vim (see |shada-file|). The string should be a comma- +-- separated list of parameters, each consisting of a single character +-- identifying the particular parameter, followed by a number or string +-- which specifies the value of that parameter. If a particular +-- character is left out, then the default value is used for that +-- parameter. The following is a list of the identifying characters and +-- the effect of their value. +-- CHAR VALUE ~ +-- +-- ! When included, save and restore global variables that start +-- with an uppercase letter, and don't contain a lowercase +-- letter. Thus "KEEPTHIS and "K_L_M" are stored, but "KeepThis" +-- and "_K_L_M" are not. Nested List and Dict items may not be +-- read back correctly, you end up with an empty item. +-- +-- " Maximum number of lines saved for each register. Old name of +-- the `'<'` item, with the disadvantage that you need to put a +-- backslash before the ", otherwise it will be recognized as the +-- start of a comment! +-- +-- % When included, save and restore the buffer list. If Vim is +-- started with a file name argument, the buffer list is not +-- restored. If Vim is started without a file name argument, the +-- buffer list is restored from the shada file. Quickfix +-- (`'buftype'` ), unlisted (`'buflisted'` ), unnamed and buffers on +-- removable media (|shada-r|) are not saved. +-- When followed by a number, the number specifies the maximum +-- number of buffers that are stored. Without a number all +-- buffers are stored. +-- +-- ' Maximum number of previously edited files for which the marks +-- are remembered. This parameter must always be included when +-- `'shada'` is non-empty. +-- Including this item also means that the |jumplist| and the +-- |changelist| are stored in the shada file. +-- +-- / Maximum number of items in the search pattern history to be +-- saved. If non-zero, then the previous search and substitute +-- patterns are also saved. When not included, the value of +-- `'history'` is used. +-- +-- : Maximum number of items in the command-line history to be +-- saved. When not included, the value of `'history'` is used. +-- +-- < Maximum number of lines saved for each register. If zero then +-- registers are not saved. When not included, all lines are +-- saved. `'"'` is the old name for this item. +-- Also see the `'s'` item below: limit specified in KiB. +-- +-- @ Maximum number of items in the input-line history to be +-- saved. When not included, the value of `'history'` is used. +-- +-- c Dummy option, kept for compatibility reasons. Has no actual +-- effect: ShaDa always uses UTF-8 and `'encoding'` value is fixed +-- to UTF-8 as well. +-- +-- f Whether file marks need to be stored. If zero, file marks ('0 +-- to '9, 'A to 'Z) are not stored. When not present or when +-- non-zero, they are all stored. '0 is used for the current +-- cursor position (when exiting or when doing |:wshada|). +-- +-- h Disable the effect of `'hlsearch'` when loading the shada +-- file. When not included, it depends on whether ":nohlsearch" +-- has been used since the last search command. +-- +-- n Name of the shada file. The name must immediately follow +-- the `'n'` . Must be at the end of the option! If the +-- `'shadafile'` option is set, that file name overrides the one +-- given here with `'shada'` . Environment variables are +-- expanded when opening the file, not when setting the option. +-- +-- r Removable media. The argument is a string (up to the next +-- `','` ). This parameter can be given several times. Each +-- specifies the start of a path for which no marks will be +-- stored. This is to avoid removable media. For Windows you +-- could use "ra:,rb:". You can also use it for temp files, +-- e.g., for Unix: "r/tmp". Case is ignored. +-- +-- s Maximum size of an item contents in KiB. If zero then nothing +-- is saved. Unlike Vim this applies to all items, except for +-- the buffer list and header. Full item size is off by three +-- unsigned integers: with `s10` maximum item size may be 1 byte +-- (type: 7-bit integer) + 9 bytes (timestamp: up to 64-bit +-- integer) + 3 bytes (item size: up to 16-bit integer because +-- 2^8 < 10240 < 2^16) + 10240 bytes (requested maximum item +-- contents size) = 10253 bytes. +-- +-- Example: > +-- :set shada='50,<1000,s100,:0,n~/nvim/shada +-- < +-- '50 Marks will be remembered for the last 50 files you +-- edited. +-- <1000 Contents of registers (up to 1000 lines each) will be +-- remembered. +-- s100 Items with contents occupying more then 100 KiB are +-- skipped. +-- :0 Command-line history will not be saved. +-- n~/nvim/shada The name of the file to use is "~/nvim/shada". +-- no / Since `'/'` is not specified, the default will be used, +-- that is, save all of the search history, and also the +-- previous search and substitute patterns. +-- no % The buffer list will not be saved nor read back. +-- no h `'hlsearch'` highlighting will be restored. +-- +-- When setting `'shada'` from an empty value you can use |:rshada| to +-- load the contents of the file, this is not done automatically. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.shada: vim.Option,string[] +--- @operator add: vim.opt.shada +--- @operator sub: vim.opt.shada +--- @operator pow: vim.opt.shada +vim.opt.shada = "!,'100,<50,s10,h" +vim.opt.sd = vim.opt.shada +--- @return string[] +function vim.opt.shada:get()end + +-- `'shadafile'` `'sdf'` string (default: "") +-- global +-- When non-empty, overrides the file name used for |shada| (viminfo). +-- When equal to "NONE" no shada file will be read or written. +-- This option can be set with the |-i| command line flag. The |--clean| +-- command line flag sets it to "NONE". +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.shadafile: vim.Option,string[] +--- @operator add: vim.opt.shadafile +--- @operator sub: vim.opt.shadafile +--- @operator pow: vim.opt.shadafile +vim.opt.shadafile = "" +vim.opt.sdf = vim.opt.shadafile +--- @return string[] +function vim.opt.shadafile:get()end + +-- `'shell'` `'sh'` string (default $SHELL or "sh", Win32: "cmd.exe") +-- global +-- Name of the shell to use for ! and :! commands. When changing the +-- value also check these options: `'shellpipe'` , `'shellslash'` +-- `'shellredir'` , `'shellquote'` , `'shellxquote'` and `'shellcmdflag'` . +-- It is allowed to give an argument to the command, e.g. "csh -f". +-- See |option-backslash| about including spaces and backslashes. +-- Environment variables are expanded |:set_env|. +-- +-- If the name of the shell contains a space, you need to enclose it in +-- quotes. Example with quotes: > +-- :set shell=\"c:\program\ files\unix\sh.exe\"\ -f +-- < Note the backslash before each quote (to avoid starting a comment) and +-- each space (to avoid ending the option value), so better use |:let-&| +-- like this: > +-- :let &shell='"C:\Program Files\unix\sh.exe" -f' +-- < Also note that the "-f" is not inside the quotes, because it is not +-- part of the command name. +-- +-- Rules regarding quotes: +-- 1. Option is split on space and tab characters that are not inside +-- quotes: "abc def" runs shell named "abc" with additional argument +-- "def", '"abc def"' runs shell named "abc def" with no additional +-- arguments (here and below: additional means “additional to +-- `'shellcmdflag'` ”). +-- 2. Quotes in option may be present in any position and any number: +-- `'"abc"'` , `'"a"bc'` , `'a"b"c'` , `'ab"c"'` and `'"a"b"c"'` are all equivalent +-- to just "abc". +-- 3. Inside quotes backslash preceding backslash means one backslash. +-- Backslash preceding quote means one quote. Backslash preceding +-- anything else means backslash and next character literally: +-- `'"a\\b"'` is the same as "a\b", `'"a\\"b"'` runs shell named literally +-- `'a"b'` , `'"a\b"'` is the same as "a\b" again. +-- 4. Outside of quotes backslash always means itself, it cannot be used +-- to escape quote: `'a\"b"'` is the same as "a\b". +-- Note that such processing is done after |:set| did its own round of +-- unescaping, so to keep yourself sane use |:let-&| like shown above. +-- +-- To use PowerShell: > +-- let &shell = executable(`'pwsh'` ) ? `'pwsh'` : `'powershell'` +-- let &shellcmdflag = '-NoLogo -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();$PSDefaultParameterValues[`''` Out-File:Encoding`''` ]=`''` utf8`''` ;' +-- let &shellredir = '2>&1 | %%{ "$_" } | Out-File %s; exit $LastExitCode' +-- let &shellpipe = '2>&1 | %%{ "$_" } | Tee-Object %s; exit $LastExitCode' +-- set shellquote= shellxquote= +-- +-- < This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.shell: vim.Option,string +--- @operator add: vim.opt.shell +--- @operator sub: vim.opt.shell +--- @operator pow: vim.opt.shell +vim.opt.shell = "sh" +vim.opt.sh = vim.opt.shell +--- @return string +function vim.opt.shell:get()end + +-- `'shellcmdflag'` `'shcf'` string (default: "-c"; Windows: "/s /c") +-- global +-- Flag passed to the shell to execute "!" and ":!" commands; e.g., +-- `bash.exe -c ls` or `cmd.exe /s /c "dir"`. For MS-Windows, the +-- default is set according to the value of `'shell'` , to reduce the need +-- to set this option by the user. +-- On Unix it can have more than one flag. Each white space separated +-- part is passed as an argument to the shell command. +-- See |option-backslash| about including spaces and backslashes. +-- See |shell-unquoting| which talks about separating this option into +-- multiple arguments. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.shellcmdflag: vim.Option,string +--- @operator add: vim.opt.shellcmdflag +--- @operator sub: vim.opt.shellcmdflag +--- @operator pow: vim.opt.shellcmdflag +vim.opt.shellcmdflag = "-c" +vim.opt.shcf = vim.opt.shellcmdflag +--- @return string +function vim.opt.shellcmdflag:get()end + +-- `'shellpipe'` `'sp'` string (default ">", "| tee", "|& tee" or "2>&1| tee") +-- global +-- String to be used to put the output of the ":make" command in the +-- error file. See also |:make_makeprg|. See |option-backslash| about +-- including spaces and backslashes. +-- The name of the temporary file can be represented by "%s" if necessary +-- (the file name is appended automatically if no %s appears in the value +-- of this option). +-- For MS-Windows the default is "2>&1| tee". The stdout and stderr are +-- saved in a file and echoed to the screen. +-- For Unix the default is "| tee". The stdout of the compiler is saved +-- in a file and echoed to the screen. If the `'shell'` option is "csh" or +-- "tcsh" after initializations, the default becomes "|& tee". If the +-- `'shell'` option is "sh", "ksh", "mksh", "pdksh", "zsh", "zsh-beta", +-- "bash", "fish", "ash" or "dash" the default becomes "2>&1| tee". This +-- means that stderr is also included. Before using the `'shell'` option a +-- path is removed, thus "/bin/sh" uses "sh". +-- The initialization of this option is done after reading the vimrc +-- and the other initializations, so that when the `'shell'` option is set +-- there, the `'shellpipe'` option changes automatically, unless it was +-- explicitly set before. +-- When `'shellpipe'` is set to an empty string, no redirection of the +-- ":make" output will be done. This is useful if you use a `'makeprg'` +-- that writes to `'makeef'` by itself. If you want no piping, but do +-- want to include the `'makeef'` , set `'shellpipe'` to a single space. +-- Don't forget to precede the space with a backslash: ":set sp=\ ". +-- In the future pipes may be used for filtering and this option will +-- become obsolete (at least for Unix). +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.shellpipe: vim.Option,string +--- @operator add: vim.opt.shellpipe +--- @operator sub: vim.opt.shellpipe +--- @operator pow: vim.opt.shellpipe +vim.opt.shellpipe = "| tee" +vim.opt.sp = vim.opt.shellpipe +--- @return string +function vim.opt.shellpipe:get()end + +-- `'shellquote'` `'shq'` string (default: ""; Windows, when `'shell'` +-- contains "sh" somewhere: "\"") +-- global +-- Quoting character(s), put around the command passed to the shell, for +-- the "!" and ":!" commands. The redirection is kept outside of the +-- quoting. See `'shellxquote'` to include the redirection. It's +-- probably not useful to set both options. +-- This is an empty string by default. Only known to be useful for +-- third-party shells on Windows systems, such as the MKS Korn Shell +-- or bash, where it should be "\"". The default is adjusted according +-- the value of `'shell'` , to reduce the need to set this option by the +-- user. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.shellquote: vim.Option,string +--- @operator add: vim.opt.shellquote +--- @operator sub: vim.opt.shellquote +--- @operator pow: vim.opt.shellquote +vim.opt.shellquote = "" +vim.opt.shq = vim.opt.shellquote +--- @return string +function vim.opt.shellquote:get()end + +-- `'shellredir'` `'srr'` string (default ">", ">&" or ">%s 2>&1") +-- global +-- String to be used to put the output of a filter command in a temporary +-- file. See also |:!|. See |option-backslash| about including spaces +-- and backslashes. +-- The name of the temporary file can be represented by "%s" if necessary +-- (the file name is appended automatically if no %s appears in the value +-- of this option). +-- The default is ">". For Unix, if the `'shell'` option is "csh" or +-- "tcsh" during initializations, the default becomes ">&". If the +-- `'shell'` option is "sh", "ksh", "mksh", "pdksh", "zsh", "zsh-beta", +-- "bash" or "fish", the default becomes ">%s 2>&1". This means that +-- stderr is also included. For Win32, the Unix checks are done and +-- additionally "cmd" is checked for, which makes the default ">%s 2>&1". +-- Also, the same names with ".exe" appended are checked for. +-- The initialization of this option is done after reading the vimrc +-- and the other initializations, so that when the `'shell'` option is set +-- there, the `'shellredir'` option changes automatically unless it was +-- explicitly set before. +-- In the future pipes may be used for filtering and this option will +-- become obsolete (at least for Unix). +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.shellredir: vim.Option,string +--- @operator add: vim.opt.shellredir +--- @operator sub: vim.opt.shellredir +--- @operator pow: vim.opt.shellredir +vim.opt.shellredir = ">" +vim.opt.srr = vim.opt.shellredir +--- @return string +function vim.opt.shellredir:get()end + +-- `'shellslash'` `'ssl'` boolean (default off) +-- global +-- {only for MS-Windows} +-- When set, a forward slash is used when expanding file names. This is +-- useful when a Unix-like shell is used instead of cmd.exe. Backward +-- slashes can still be typed, but they are changed to forward slashes by +-- Vim. +-- Note that setting or resetting this option has no effect for some +-- existing file names, thus this option needs to be set before opening +-- any file for best results. This might change in the future. +-- `'shellslash'` only works when a backslash can be used as a path +-- separator. To test if this is so use: > +-- if exists(`'+shellslash'` ) +-- < Also see `'completeslash'` . +--- @class vim.opt.shellslash: vim.Option,boolean +--- @operator add: vim.opt.shellslash +--- @operator sub: vim.opt.shellslash +--- @operator pow: vim.opt.shellslash +vim.opt.shellslash = false +vim.opt.ssl = vim.opt.shellslash +--- @return boolean +function vim.opt.shellslash:get()end + +-- `'shelltemp'` `'stmp'` boolean (default on) +-- global +-- When on, use temp files for shell commands. When off use a pipe. +-- When using a pipe is not possible temp files are used anyway. +-- The advantage of using a pipe is that nobody can read the temp file +-- and the `'shell'` command does not need to support redirection. +-- The advantage of using a temp file is that the file type and encoding +-- can be detected. +-- The |FilterReadPre|, |FilterReadPost| and |FilterWritePre|, +-- |FilterWritePost| autocommands event are not triggered when +-- `'shelltemp'` is off. +-- |system()| does not respect this option, it always uses pipes. +--- @class vim.opt.shelltemp: vim.Option,boolean +--- @operator add: vim.opt.shelltemp +--- @operator sub: vim.opt.shelltemp +--- @operator pow: vim.opt.shelltemp +vim.opt.shelltemp = true +vim.opt.stmp = vim.opt.shelltemp +--- @return boolean +function vim.opt.shelltemp:get()end + +-- `'shellxescape'` `'sxe'` string (default: "") +-- global +-- When `'shellxquote'` is set to "(" then the characters listed in this +-- option will be escaped with a `'^'` character. This makes it possible +-- to execute most external commands with cmd.exe. +--- @class vim.opt.shellxescape: vim.Option,string +--- @operator add: vim.opt.shellxescape +--- @operator sub: vim.opt.shellxescape +--- @operator pow: vim.opt.shellxescape +vim.opt.shellxescape = "" +vim.opt.sxe = vim.opt.shellxescape +--- @return string +function vim.opt.shellxescape:get()end + +-- `'shellxquote'` `'sxq'` string (default: "", Windows: "\"") +-- global +-- Quoting character(s), put around the command passed to the shell, for +-- the "!" and ":!" commands. Includes the redirection. See +-- `'shellquote'` to exclude the redirection. It's probably not useful +-- to set both options. +-- When the value is `'('` then `')'` is appended. When the value is `'"('` +-- then `')"'` is appended. +-- When the value is `'('` then also see `'shellxescape'` . +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.shellxquote: vim.Option,string +--- @operator add: vim.opt.shellxquote +--- @operator sub: vim.opt.shellxquote +--- @operator pow: vim.opt.shellxquote +vim.opt.shellxquote = "" +vim.opt.sxq = vim.opt.shellxquote +--- @return string +function vim.opt.shellxquote:get()end + +-- `'shiftround'` `'sr'` boolean (default off) +-- global +-- Round indent to multiple of `'shiftwidth'` . Applies to > and < +-- commands. CTRL-T and CTRL-D in Insert mode always round the indent to +-- a multiple of `'shiftwidth'` (this is Vi compatible). +--- @class vim.opt.shiftround: vim.Option,boolean +--- @operator add: vim.opt.shiftround +--- @operator sub: vim.opt.shiftround +--- @operator pow: vim.opt.shiftround +vim.opt.shiftround = false +vim.opt.sr = vim.opt.shiftround +--- @return boolean +function vim.opt.shiftround:get()end + +-- `'shiftwidth'` `'sw'` number (default 8) +-- local to buffer +-- Number of spaces to use for each step of (auto)indent. Used for +-- |`'cindent'` |, |>>|, |<<|, etc. +-- When zero the `'ts'` value will be used. Use the |shiftwidth()| +-- function to get the effective shiftwidth value. +--- @class vim.opt.shiftwidth: vim.Option,number +--- @operator add: vim.opt.shiftwidth +--- @operator sub: vim.opt.shiftwidth +--- @operator pow: vim.opt.shiftwidth +vim.opt.shiftwidth = 8 +vim.opt.sw = vim.opt.shiftwidth +--- @return number +function vim.opt.shiftwidth:get()end + +-- `'shortmess'` `'shm'` string (default "filnxtToOF") +-- global +-- This option helps to avoid all the |hit-enter| prompts caused by file +-- messages, for example with CTRL-G, and to avoid some other messages. +-- It is a list of flags: +-- flag meaning when present ~ +-- f use "(3 of 5)" instead of "(file 3 of 5)" +-- i use "[noeol]" instead of "[Incomplete last line]" +-- l use "999L, 888B" instead of "999 lines, 888 bytes" +-- m use "[+]" instead of "[Modified]" +-- n use "[New]" instead of "[New File]" +-- r use "[RO]" instead of "[readonly]" +-- w use "[w]" instead of "written" for file write message +-- and "[a]" instead of "appended" for ':w >> file' command +-- x use "[dos]" instead of "[dos format]", "[unix]" +-- instead of "[unix format]" and "[mac]" instead of "[mac +-- format]" +-- a all of the above abbreviations +-- +-- o overwrite message for writing a file with subsequent +-- message for reading a file (useful for ":wn" or when +-- `'autowrite'` on) +-- O message for reading a file overwrites any previous +-- message; also for quickfix message (e.g., ":cn") +-- s don't give "search hit BOTTOM, continuing at TOP" or +-- "search hit TOP, continuing at BOTTOM" messages; when using +-- the search count do not show "W" after the count message (see +-- S below) +-- t truncate file message at the start if it is too long +-- to fit on the command-line, "<" will appear in the left most +-- column; ignored in Ex mode +-- T truncate other messages in the middle if they are too +-- long to fit on the command line; "..." will appear in the +-- middle; ignored in Ex mode +-- W don't give "written" or "[w]" when writing a file +-- A don't give the "ATTENTION" message when an existing +-- swap file is found +-- I don't give the intro message when starting Vim, +-- see |:intro| +-- c don't give |ins-completion-menu| messages; for +-- example, "-- XXX completion (YYY)", "match 1 of 2", "The only +-- match", "Pattern not found", "Back at original", etc. +-- C don't give messages while scanning for ins-completion +-- items, for instance "scanning tags" +-- q use "recording" instead of "recording @a" +-- F don't give the file info when editing a file, like +-- `:silent` was used for the command +-- S do not show search count message when searching, e.g. +-- "[1/5]" +-- +-- This gives you the opportunity to avoid that a change between buffers +-- requires you to hit , but still gives as useful a message as +-- possible for the space available. To get the whole message that you +-- would have got with `'shm'` empty, use ":file!" +-- Useful values: +-- shm= No abbreviation of message. +-- shm=a Abbreviation, but no loss of information. +-- shm=at Abbreviation, and truncate message when necessary. +--- @class vim.opt.shortmess: vim.Option,string[] +--- @operator add: vim.opt.shortmess +--- @operator sub: vim.opt.shortmess +--- @operator pow: vim.opt.shortmess +vim.opt.shortmess = "filnxtToOF" +vim.opt.shm = vim.opt.shortmess +--- @return string[] +function vim.opt.shortmess:get()end + diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.3.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.3.lua new file mode 100644 index 00000000..ceda8ce9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.3.lua @@ -0,0 +1,2549 @@ +---@meta + +-- `'showbreak'` `'sbr'` string (default "") +-- global or local to window |global-local| +-- String to put at the start of lines that have been wrapped. Useful +-- values are "> " or "+++ ": > +-- :set showbreak=>\ +-- < Note the backslash to escape the trailing space. It's easier like +-- this: > +-- :let &showbreak = '+++ ' +-- < Only printable single-cell characters are allowed, excluding and +-- comma (in a future version the comma might be used to separate the +-- part that is shown at the end and at the start of a line). +-- The |hl-NonText| highlight group determines the highlighting. +-- Note that tabs after the showbreak will be displayed differently. +-- If you want the `'showbreak'` to appear in between line numbers, add the +-- "n" flag to `'cpoptions'` . +-- A window-local value overrules a global value. If the global value is +-- set and you want no value in the current window use NONE: > +-- :setlocal showbreak=NONE +-- < +--- @class vim.opt.showbreak: vim.Option,string +--- @operator add: vim.opt.showbreak +--- @operator sub: vim.opt.showbreak +--- @operator pow: vim.opt.showbreak +vim.opt.showbreak = "" +vim.opt.sbr = vim.opt.showbreak +--- @return string +function vim.opt.showbreak:get()end + +-- `'showcmd'` `'sc'` boolean (default: on) +-- global +-- Show (partial) command in the last line of the screen. Set this +-- option off if your terminal is slow. +-- In Visual mode the size of the selected area is shown: +-- - When selecting characters within a line, the number of characters. +-- If the number of bytes is different it is also displayed: "2-6" +-- means two characters and six bytes. +-- - When selecting more than one line, the number of lines. +-- - When selecting a block, the size in screen characters: +-- {lines}x{columns}. +-- This information can be displayed in an alternative location using the +-- `'showcmdloc'` option, useful when `'cmdheight'` is 0. +--- @class vim.opt.showcmd: vim.Option,boolean +--- @operator add: vim.opt.showcmd +--- @operator sub: vim.opt.showcmd +--- @operator pow: vim.opt.showcmd +vim.opt.showcmd = true +vim.opt.sc = vim.opt.showcmd +--- @return boolean +function vim.opt.showcmd:get()end + +-- `'showcmdloc'` `'sloc'` string (default "last") +-- global +-- This option can be used to display the (partially) entered command in +-- another location. Possible values are: +-- last Last line of the screen (default). +-- statusline Status line of the current window. +-- tabline First line of the screen if `'showtabline'` is enabled. +-- Setting this option to "statusline" or "tabline" means that these will +-- be redrawn whenever the command changes, which can be on every key +-- pressed. +-- The %S `'statusline'` item can be used in `'statusline'` or `'tabline'` to +-- place the text. Without a custom `'statusline'` or `'tabline'` it will be +-- displayed in a convenient location. +--- @class vim.opt.showcmdloc: vim.Option,string +--- @operator add: vim.opt.showcmdloc +--- @operator sub: vim.opt.showcmdloc +--- @operator pow: vim.opt.showcmdloc +vim.opt.showcmdloc = "last" +vim.opt.sloc = vim.opt.showcmdloc +--- @return string +function vim.opt.showcmdloc:get()end + +-- `'showfulltag'` `'sft'` boolean (default off) +-- global +-- When completing a word in insert mode (see |ins-completion|) from the +-- tags file, show both the tag name and a tidied-up form of the search +-- pattern (if there is one) as possible matches. Thus, if you have +-- matched a C function, you can see a template for what arguments are +-- required (coding style permitting). +-- Note that this doesn't work well together with having "longest" in +-- `'completeopt'` , because the completion from the search pattern may not +-- match the typed text. +--- @class vim.opt.showfulltag: vim.Option,boolean +--- @operator add: vim.opt.showfulltag +--- @operator sub: vim.opt.showfulltag +--- @operator pow: vim.opt.showfulltag +vim.opt.showfulltag = false +vim.opt.sft = vim.opt.showfulltag +--- @return boolean +function vim.opt.showfulltag:get()end + +-- `'showmatch'` `'sm'` boolean (default off) +-- global +-- When a bracket is inserted, briefly jump to the matching one. The +-- jump is only done if the match can be seen on the screen. The time to +-- show the match can be set with `'matchtime'` . +-- A Beep is given if there is no match (no matter if the match can be +-- seen or not). +-- When the `'m'` flag is not included in `'cpoptions'` , typing a character +-- will immediately move the cursor back to where it belongs. +-- See the "sm" field in `'guicursor'` for setting the cursor shape and +-- blinking when showing the match. +-- The `'matchpairs'` option can be used to specify the characters to show +-- matches for. `'rightleft'` and `'revins'` are used to look for opposite +-- matches. +-- Also see the matchparen plugin for highlighting the match when moving +-- around |pi_paren.txt|. +-- Note: Use of the short form is rated PG. +--- @class vim.opt.showmatch: vim.Option,boolean +--- @operator add: vim.opt.showmatch +--- @operator sub: vim.opt.showmatch +--- @operator pow: vim.opt.showmatch +vim.opt.showmatch = false +vim.opt.sm = vim.opt.showmatch +--- @return boolean +function vim.opt.showmatch:get()end + +-- `'showmode'` `'smd'` boolean (default: on) +-- global +-- If in Insert, Replace or Visual mode put a message on the last line. +-- The |hl-ModeMsg| highlight group determines the highlighting. +-- The option has no effect when `'cmdheight'` is zero. +--- @class vim.opt.showmode: vim.Option,boolean +--- @operator add: vim.opt.showmode +--- @operator sub: vim.opt.showmode +--- @operator pow: vim.opt.showmode +vim.opt.showmode = true +vim.opt.smd = vim.opt.showmode +--- @return boolean +function vim.opt.showmode:get()end + +-- `'showtabline'` `'stal'` number (default 1) +-- global +-- The value of this option specifies when the line with tab page labels +-- will be displayed: +-- 0: never +-- 1: only if there are at least two tab pages +-- 2: always +-- This is both for the GUI and non-GUI implementation of the tab pages +-- line. +-- See |tab-page| for more information about tab pages. +--- @class vim.opt.showtabline: vim.Option,number +--- @operator add: vim.opt.showtabline +--- @operator sub: vim.opt.showtabline +--- @operator pow: vim.opt.showtabline +vim.opt.showtabline = 1 +vim.opt.stal = vim.opt.showtabline +--- @return number +function vim.opt.showtabline:get()end + +-- `'sidescroll'` `'ss'` number (default 1) +-- global +-- The minimal number of columns to scroll horizontally. Used only when +-- the `'wrap'` option is off and the cursor is moved off of the screen. +-- When it is zero the cursor will be put in the middle of the screen. +-- When using a slow terminal set it to a large number or 0. Not used +-- for "zh" and "zl" commands. +--- @class vim.opt.sidescroll: vim.Option,number +--- @operator add: vim.opt.sidescroll +--- @operator sub: vim.opt.sidescroll +--- @operator pow: vim.opt.sidescroll +vim.opt.sidescroll = 1 +vim.opt.ss = vim.opt.sidescroll +--- @return number +function vim.opt.sidescroll:get()end + +-- `'sidescrolloff'` `'siso'` number (default 0) +-- global or local to window |global-local| +-- The minimal number of screen columns to keep to the left and to the +-- right of the cursor if `'nowrap'` is set. Setting this option to a +-- value greater than 0 while having |`'sidescroll'` | also at a non-zero +-- value makes some context visible in the line you are scrolling in +-- horizontally (except at beginning of the line). Setting this option +-- to a large value (like 999) has the effect of keeping the cursor +-- horizontally centered in the window, as long as one does not come too +-- close to the beginning of the line. +-- After using the local value, go back the global value with one of +-- these two: > +-- setlocal sidescrolloff< +-- setlocal sidescrolloff=-1 +-- < +-- Example: Try this together with `'sidescroll'` and `'listchars'` as +-- in the following example to never allow the cursor to move +-- onto the "extends" character: > +-- +-- :set nowrap sidescroll=1 listchars=extends:>,precedes:< +-- :set sidescrolloff=1 +-- < +--- @class vim.opt.sidescrolloff: vim.Option,number +--- @operator add: vim.opt.sidescrolloff +--- @operator sub: vim.opt.sidescrolloff +--- @operator pow: vim.opt.sidescrolloff +vim.opt.sidescrolloff = 0 +vim.opt.siso = vim.opt.sidescrolloff +--- @return number +function vim.opt.sidescrolloff:get()end + +-- `'signcolumn'` `'scl'` string (default "auto") +-- local to window +-- When and how to draw the signcolumn. Valid values are: +-- "auto" only when there is a sign to display +-- "auto:[1-9]" resize to accommodate multiple signs up to the +-- given number (maximum 9), e.g. "auto:4" +-- "auto:[1-8]-[2-9]" +-- resize to accommodate multiple signs up to the +-- given maximum number (maximum 9) while keeping +-- at least the given minimum (maximum 8) fixed +-- space. The minimum number should always be less +-- than the maximum number, e.g. "auto:2-5" +-- "no" never +-- "yes" always +-- "yes:[1-9]" always, with fixed space for signs up to the given +-- number (maximum 9), e.g. "yes:3" +-- "number" display signs in the `'number'` column. If the number +-- column is not present, then behaves like "auto". +-- +-- Note regarding "orphaned signs": with signcolumn numbers higher than +-- 1, deleting lines will also remove the associated signs automatically, +-- in contrast to the default Vim behavior of keeping and grouping them. +-- This is done in order for the signcolumn appearance not appear weird +-- during line deletion. +--- @class vim.opt.signcolumn: vim.Option,string +--- @operator add: vim.opt.signcolumn +--- @operator sub: vim.opt.signcolumn +--- @operator pow: vim.opt.signcolumn +vim.opt.signcolumn = "auto" +vim.opt.scl = vim.opt.signcolumn +--- @return string +function vim.opt.signcolumn:get()end + +-- `'smartcase'` `'scs'` boolean (default off) +-- global +-- Override the `'ignorecase'` option if the search pattern contains upper +-- case characters. Only used when the search pattern is typed and +-- `'ignorecase'` option is on. Used for the commands "/", "?", "n", "N", +-- ":g" and ":s". Not used for "*", "#", "gd", tag search, etc. After +-- "*" and "#" you can make `'smartcase'` used by doing a "/" command, +-- recalling the search pattern from history and hitting . +--- @class vim.opt.smartcase: vim.Option,boolean +--- @operator add: vim.opt.smartcase +--- @operator sub: vim.opt.smartcase +--- @operator pow: vim.opt.smartcase +vim.opt.smartcase = false +vim.opt.scs = vim.opt.smartcase +--- @return boolean +function vim.opt.smartcase:get()end + +-- `'smartindent'` `'si'` boolean (default off) +-- local to buffer +-- Do smart autoindenting when starting a new line. Works for C-like +-- programs, but can also be used for other languages. `'cindent'` does +-- something like this, works better in most cases, but is more strict, +-- see |C-indenting|. When `'cindent'` is on or `'indentexpr'` is set, +-- setting `'si'` has no effect. `'indentexpr'` is a more advanced +-- alternative. +-- Normally `'autoindent'` should also be on when using `'smartindent'` . +-- An indent is automatically inserted: +-- - After a line ending in "{". +-- - After a line starting with a keyword from `'cinwords'` . +-- - Before a line starting with "}" (only with the "O" command). +-- When typing `'}'` as the first character in a new line, that line is +-- given the same indent as the matching "{". +-- When typing `'#'` as the first character in a new line, the indent for +-- that line is removed, the `'#'` is put in the first column. The indent +-- is restored for the next line. If you don't want this, use this +-- mapping: ":inoremap # X^H#", where ^H is entered with CTRL-V CTRL-H. +-- When using the ">>" command, lines starting with `'#'` are not shifted +-- right. +--- @class vim.opt.smartindent: vim.Option,boolean +--- @operator add: vim.opt.smartindent +--- @operator sub: vim.opt.smartindent +--- @operator pow: vim.opt.smartindent +vim.opt.smartindent = false +vim.opt.si = vim.opt.smartindent +--- @return boolean +function vim.opt.smartindent:get()end + +-- `'smarttab'` `'sta'` boolean (default on) +-- global +-- When on, a in front of a line inserts blanks according to +-- `'shiftwidth'` . `'tabstop'` or `'softtabstop'` is used in other places. A +-- will delete a `'shiftwidth'` worth of space at the start of the +-- line. +-- When off, a always inserts blanks according to `'tabstop'` or +-- `'softtabstop'` . `'shiftwidth'` is only used for shifting text left or +-- right |shift-left-right|. +-- What gets inserted (a or spaces) depends on the `'expandtab'` +-- option. Also see |ins-expandtab|. When `'expandtab'` is not set, the +-- number of spaces is minimized by using s. +--- @class vim.opt.smarttab: vim.Option,boolean +--- @operator add: vim.opt.smarttab +--- @operator sub: vim.opt.smarttab +--- @operator pow: vim.opt.smarttab +vim.opt.smarttab = true +vim.opt.sta = vim.opt.smarttab +--- @return boolean +function vim.opt.smarttab:get()end + +-- `'softtabstop'` `'sts'` number (default 0) +-- local to buffer +-- Number of spaces that a counts for while performing editing +-- operations, like inserting a or using . It "feels" like +-- s are being inserted, while in fact a mix of spaces and s is +-- used. This is useful to keep the `'ts'` setting at its standard value +-- of 8, while being able to edit like it is set to `'sts'` . However, +-- commands like "x" still work on the actual characters. +-- When `'sts'` is zero, this feature is off. +-- When `'sts'` is negative, the value of `'shiftwidth'` is used. +-- See also |ins-expandtab|. When `'expandtab'` is not set, the number of +-- spaces is minimized by using s. +-- The `'L'` flag in `'cpoptions'` changes how tabs are used when `'list'` is +-- set. +-- +-- The value of `'softtabstop'` will be ignored if |`'varsofttabstop'` | is set +-- to anything other than an empty string. +--- @class vim.opt.softtabstop: vim.Option,number +--- @operator add: vim.opt.softtabstop +--- @operator sub: vim.opt.softtabstop +--- @operator pow: vim.opt.softtabstop +vim.opt.softtabstop = 0 +vim.opt.sts = vim.opt.softtabstop +--- @return number +function vim.opt.softtabstop:get()end + +-- `'spell'` boolean (default off) +-- local to window +-- When on spell checking will be done. See |spell|. +-- The languages are specified with `'spelllang'` . +--- @class vim.opt.spell: vim.Option,boolean +--- @operator add: vim.opt.spell +--- @operator sub: vim.opt.spell +--- @operator pow: vim.opt.spell +vim.opt.spell = false +--- @return boolean +function vim.opt.spell:get()end + +-- `'spellcapcheck'` `'spc'` string (default "[.?!]\_[\])'" \t]\+") +-- local to buffer +-- Pattern to locate the end of a sentence. The following word will be +-- checked to start with a capital letter. If not then it is highlighted +-- with SpellCap |hl-SpellCap| (unless the word is also badly spelled). +-- When this check is not wanted make this option empty. +-- Only used when `'spell'` is set. +-- Be careful with special characters, see |option-backslash| about +-- including spaces and backslashes. +-- To set this option automatically depending on the language, see +-- |set-spc-auto|. +--- @class vim.opt.spellcapcheck: vim.Option,string +--- @operator add: vim.opt.spellcapcheck +--- @operator sub: vim.opt.spellcapcheck +--- @operator pow: vim.opt.spellcapcheck +vim.opt.spellcapcheck = "[.?!]\\_[\\])'\"\t ]\\+" +vim.opt.spc = vim.opt.spellcapcheck +--- @return string +function vim.opt.spellcapcheck:get()end + +-- `'spellfile'` `'spf'` string (default empty) +-- local to buffer +-- Name of the word list file where words are added for the |zg| and |zw| +-- commands. It must end in ".{encoding}.add". You need to include the +-- path, otherwise the file is placed in the current directory. +-- The path may include characters from `'isfname'` , space, comma and `'@'` . +-- +-- It may also be a comma-separated list of names. A count before the +-- |zg| and |zw| commands can be used to access each. This allows using +-- a personal word list file and a project word list file. +-- When a word is added while this option is empty Vim will set it for +-- you: Using the first directory in `'runtimepath'` that is writable. If +-- there is no "spell" directory yet it will be created. For the file +-- name the first language name that appears in `'spelllang'` is used, +-- ignoring the region. +-- The resulting ".spl" file will be used for spell checking, it does not +-- have to appear in `'spelllang'` . +-- Normally one file is used for all regions, but you can add the region +-- name if you want to. However, it will then only be used when +-- `'spellfile'` is set to it, for entries in `'spelllang'` only files +-- without region name will be found. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.spellfile: vim.Option,string[] +--- @operator add: vim.opt.spellfile +--- @operator sub: vim.opt.spellfile +--- @operator pow: vim.opt.spellfile +vim.opt.spellfile = "" +vim.opt.spf = vim.opt.spellfile +--- @return string[] +function vim.opt.spellfile:get()end + +-- `'spelllang'` `'spl'` string (default "en") +-- local to buffer +-- A comma-separated list of word list names. When the `'spell'` option is +-- on spellchecking will be done for these languages. Example: > +-- set spelllang=en_us,nl,medical +-- < This means US English, Dutch and medical words are recognized. Words +-- that are not recognized will be highlighted. +-- The word list name must consist of alphanumeric characters, a dash or +-- an underscore. It should not include a comma or dot. Using a dash is +-- recommended to separate the two letter language name from a +-- specification. Thus "en-rare" is used for rare English words. +-- A region name must come last and have the form "_xx", where "xx" is +-- the two-letter, lower case region name. You can use more than one +-- region by listing them: "en_us,en_ca" supports both US and Canadian +-- English, but not words specific for Australia, New Zealand or Great +-- Britain. (Note: currently en_au and en_nz dictionaries are older than +-- en_ca, en_gb and en_us). +-- If the name "cjk" is included East Asian characters are excluded from +-- spell checking. This is useful when editing text that also has Asian +-- words. +-- Note that the "medical" dictionary does not exist, it is just an +-- example of a longer name. +-- +-- As a special case the name of a .spl file can be given as-is. The +-- first "_xx" in the name is removed and used as the region name +-- (_xx is an underscore, two letters and followed by a non-letter). +-- This is mainly for testing purposes. You must make sure the correct +-- encoding is used, Vim doesn't check it. +-- How the related spell files are found is explained here: |spell-load|. +-- +-- If the |spellfile.vim| plugin is active and you use a language name +-- for which Vim cannot find the .spl file in `'runtimepath'` the plugin +-- will ask you if you want to download the file. +-- +-- After this option has been set successfully, Vim will source the files +-- "spell/LANG.vim" in `'runtimepath'` . "LANG" is the value of `'spelllang'` +-- up to the first character that is not an ASCII letter or number and +-- not a dash. Also see |set-spc-auto|. +--- @class vim.opt.spelllang: vim.Option,string[] +--- @operator add: vim.opt.spelllang +--- @operator sub: vim.opt.spelllang +--- @operator pow: vim.opt.spelllang +vim.opt.spelllang = "en" +vim.opt.spl = vim.opt.spelllang +--- @return string[] +function vim.opt.spelllang:get()end + +-- `'spelloptions'` `'spo'` string (default "") +-- local to buffer +-- A comma-separated list of options for spell checking: +-- camel When a word is CamelCased, assume "Cased" is a +-- separate word: every upper-case character in a word +-- that comes after a lower case character indicates the +-- start of a new word. +-- noplainbuffer Only spellcheck a buffer when `'syntax'` is enabled, +-- or when extmarks are set within the buffer. Only +-- designated regions of the buffer are spellchecked in +-- this case. +--- @class vim.opt.spelloptions: vim.Option,string[] +--- @operator add: vim.opt.spelloptions +--- @operator sub: vim.opt.spelloptions +--- @operator pow: vim.opt.spelloptions +vim.opt.spelloptions = "" +vim.opt.spo = vim.opt.spelloptions +--- @return string[] +function vim.opt.spelloptions:get()end + +-- `'spellsuggest'` `'sps'` string (default "best") +-- global +-- Methods used for spelling suggestions. Both for the |z=| command and +-- the |spellsuggest()| function. This is a comma-separated list of +-- items: +-- +-- best Internal method that works best for English. Finds +-- changes like "fast" and uses a bit of sound-a-like +-- scoring to improve the ordering. +-- +-- double Internal method that uses two methods and mixes the +-- results. The first method is "fast", the other method +-- computes how much the suggestion sounds like the bad +-- word. That only works when the language specifies +-- sound folding. Can be slow and doesn't always give +-- better results. +-- +-- fast Internal method that only checks for simple changes: +-- character inserts/deletes/swaps. Works well for +-- simple typing mistakes. +-- +-- {number} The maximum number of suggestions listed for |z=|. +-- Not used for |spellsuggest()|. The number of +-- suggestions is never more than the value of `'lines'` +-- minus two. +-- +-- timeout:{millisec} Limit the time searching for suggestions to +-- {millisec} milli seconds. Applies to the following +-- methods. When omitted the limit is 5000. When +-- negative there is no limit. +-- +-- file:{filename} Read file {filename}, which must have two columns, +-- separated by a slash. The first column contains the +-- bad word, the second column the suggested good word. +-- Example: +-- theribal/terrible ~ +-- Use this for common mistakes that do not appear at the +-- top of the suggestion list with the internal methods. +-- Lines without a slash are ignored, use this for +-- comments. +-- The word in the second column must be correct, +-- otherwise it will not be used. Add the word to an +-- ".add" file if it is currently flagged as a spelling +-- mistake. +-- The file is used for all languages. +-- +-- expr:{expr} Evaluate expression {expr}. Use a function to avoid +-- trouble with spaces. |v:val| holds the badly spelled +-- word. The expression must evaluate to a List of +-- Lists, each with a suggestion and a score. +-- Example: +-- [[`'the'` , 33], [`'that'` , 44]] ~ +-- Set `'verbose'` and use |z=| to see the scores that the +-- internal methods use. A lower score is better. +-- This may invoke |spellsuggest()| if you temporarily +-- set `'spellsuggest'` to exclude the "expr:" part. +-- Errors are silently ignored, unless you set the +-- `'verbose'` option to a non-zero value. +-- +-- Only one of "best", "double" or "fast" may be used. The others may +-- appear several times in any order. Example: > +-- :set sps=file:~/.config/nvim/sugg,best,expr:MySuggest() +-- < +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.spellsuggest: vim.Option,string[] +--- @operator add: vim.opt.spellsuggest +--- @operator sub: vim.opt.spellsuggest +--- @operator pow: vim.opt.spellsuggest +vim.opt.spellsuggest = "best" +vim.opt.sps = vim.opt.spellsuggest +--- @return string[] +function vim.opt.spellsuggest:get()end + +-- `'splitbelow'` `'sb'` boolean (default off) +-- global +-- When on, splitting a window will put the new window below the current +-- one. |:split| +--- @class vim.opt.splitbelow: vim.Option,boolean +--- @operator add: vim.opt.splitbelow +--- @operator sub: vim.opt.splitbelow +--- @operator pow: vim.opt.splitbelow +vim.opt.splitbelow = false +vim.opt.sb = vim.opt.splitbelow +--- @return boolean +function vim.opt.splitbelow:get()end + +-- `'splitkeep'` `'spk'` string (default "cursor") +-- global +-- The value of this option determines the scroll behavior when opening, +-- closing or resizing horizontal splits. +-- +-- Possible values are: +-- cursor Keep the same relative cursor position. +-- screen Keep the text on the same screen line. +-- topline Keep the topline the same. +-- +-- For the "screen" and "topline" values, the cursor position will be +-- changed when necessary. In this case, the jumplist will be populated +-- with the previous cursor position. For "screen", the text cannot always +-- be kept on the same screen line when `'wrap'` is enabled. +--- @class vim.opt.splitkeep: vim.Option,string +--- @operator add: vim.opt.splitkeep +--- @operator sub: vim.opt.splitkeep +--- @operator pow: vim.opt.splitkeep +vim.opt.splitkeep = "cursor" +vim.opt.spk = vim.opt.splitkeep +--- @return string +function vim.opt.splitkeep:get()end + +-- `'splitright'` `'spr'` boolean (default off) +-- global +-- When on, splitting a window will put the new window right of the +-- current one. |:vsplit| +--- @class vim.opt.splitright: vim.Option,boolean +--- @operator add: vim.opt.splitright +--- @operator sub: vim.opt.splitright +--- @operator pow: vim.opt.splitright +vim.opt.splitright = false +vim.opt.spr = vim.opt.splitright +--- @return boolean +function vim.opt.splitright:get()end + +-- `'startofline'` `'sol'` boolean (default off) +-- global +-- When "on" the commands listed below move the cursor to the first +-- non-blank of the line. When off the cursor is kept in the same column +-- (if possible). This applies to the commands: CTRL-D, CTRL-U, CTRL-B, +-- CTRL-F, "G", "H", "M", "L", gg, and to the commands "d", "<<" and ">>" +-- with a linewise operator, with "%" with a count and to buffer changing +-- commands (CTRL-^, :bnext, :bNext, etc.). Also for an Ex command that +-- only has a line number, e.g., ":25" or ":+". +-- In case of buffer changing commands the cursor is placed at the column +-- where it was the last time the buffer was edited. +--- @class vim.opt.startofline: vim.Option,boolean +--- @operator add: vim.opt.startofline +--- @operator sub: vim.opt.startofline +--- @operator pow: vim.opt.startofline +vim.opt.startofline = false +vim.opt.sol = vim.opt.startofline +--- @return boolean +function vim.opt.startofline:get()end + +-- `'statuscolumn'` `'stc'` string (default: empty) +-- local to window +-- EXPERIMENTAL +-- When non-empty, this option determines the content of the area to the +-- side of a window, normally containing the fold, sign and number columns. +-- The format of this option is like that of `'statusline'` . +-- +-- Some of the items from the `'statusline'` format are different for +-- `'statuscolumn'` : +-- +-- %l line number of currently drawn line +-- %r relative line number of currently drawn line +-- %s sign column for currently drawn line +-- %C fold column for currently drawn line +-- +-- NOTE: To draw the sign and fold columns, their items must be included in +-- `'statuscolumn'` . Even when they are not included, the status column width +-- will adapt to the `'signcolumn'` and `'foldcolumn'` width. +-- +-- The |v:lnum| variable holds the line number to be drawn. +-- The |v:relnum| variable holds the relative line number to be drawn. +-- The |v:virtnum| variable is negative when drawing virtual lines, zero +-- when drawing the actual buffer line, and positive when +-- drawing the wrapped part of a buffer line. +-- +-- NOTE: The %@ click execute function item is supported as well but the +-- specified function will be the same for each row in the same column. +-- It cannot be switched out through a dynamic `'statuscolumn'` format, the +-- handler should be written with this in mind. +-- +-- Examples: >vim +-- " Relative number with bar separator and click handlers: +-- :set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T +-- +-- " Right aligned relative cursor line number: +-- :let &stc='%=%{v:relnum?v:relnum:v:lnum} ' +-- +-- " Line numbers in hexadecimal for non wrapped part of lines: +-- :let &stc='%=%{v:virtnum>0?"":printf("%x",v:lnum)} ' +-- +-- " Human readable line numbers with thousands separator: +-- :let &stc=`'%{substitute(v:lnum,"\\d\\zs\\ze\\'` +-- . `'%(\\d\\d\\d\\)\\+$",",","g")}'` +-- +-- " Both relative and absolute line numbers with different +-- " highlighting for odd and even relative numbers: +-- :let &stc=`'%#NonText#%{&nu?v:lnum:""}'` . +-- '%=%{&rnu&&(v:lnum%2)?"\ ".v:relnum:""}' . +-- '%#LineNr#%{&rnu&&!(v:lnum%2)?"\ ".v:relnum:""}' +-- +-- < WARNING: this expression is evaluated for each screen line so defining +-- an expensive expression can negatively affect render performance. +--- @class vim.opt.statuscolumn: vim.Option,string +--- @operator add: vim.opt.statuscolumn +--- @operator sub: vim.opt.statuscolumn +--- @operator pow: vim.opt.statuscolumn +vim.opt.statuscolumn = "" +vim.opt.stc = vim.opt.statuscolumn +--- @return string +function vim.opt.statuscolumn:get()end + +-- `'statusline'` `'stl'` string (default empty) +-- global or local to window |global-local| +-- When non-empty, this option determines the content of the status line. +-- Also see |status-line|. +-- +-- The option consists of printf style `'%'` items interspersed with +-- normal text. Each status line item is of the form: +-- %-0{minwid}.{maxwid}{item} +-- All fields except the {item} are optional. A single percent sign can +-- be given as "%%". +-- +-- When the option starts with "%!" then it is used as an expression, +-- evaluated and the result is used as the option value. Example: > +-- :set statusline=%!MyStatusLine() +-- < The variable will be set to the |window-ID| of the +-- window that the status line belongs to. +-- The result can contain %{} items that will be evaluated too. +-- Note that the "%!" expression is evaluated in the context of the +-- current window and buffer, while %{} items are evaluated in the +-- context of the window that the statusline belongs to. +-- +-- When there is error while evaluating the option then it will be made +-- empty to avoid further errors. Otherwise screen updating would loop. +-- When the result contains unprintable characters the result is +-- unpredictable. +-- +-- Note that the only effect of `'ruler'` when this option is set (and +-- `'laststatus'` is 2 or 3) is controlling the output of |CTRL-G|. +-- +-- field meaning ~ +-- - Left justify the item. The default is right justified +-- when minwid is larger than the length of the item. +-- 0 Leading zeroes in numeric items. Overridden by "-". +-- minwid Minimum width of the item, padding as set by "-" & "0". +-- Value must be 50 or less. +-- maxwid Maximum width of the item. Truncation occurs with a "<" +-- on the left for text items. Numeric items will be +-- shifted down to maxwid-2 digits followed by ">"number +-- where number is the amount of missing digits, much like +-- an exponential notation. +-- item A one letter code as described below. +-- +-- Following is a description of the possible statusline items. The +-- second character in "item" is the type: +-- N for number +-- S for string +-- F for flags as described below +-- - not applicable +-- +-- item meaning ~ +-- f S Path to the file in the buffer, as typed or relative to current +-- directory. +-- F S Full path to the file in the buffer. +-- t S File name (tail) of file in the buffer. +-- m F Modified flag, text is "[+]"; "[-]" if `'modifiable'` is off. +-- M F Modified flag, text is ",+" or ",-". +-- r F Readonly flag, text is "[RO]". +-- R F Readonly flag, text is ",RO". +-- h F Help buffer flag, text is "[help]". +-- H F Help buffer flag, text is ",HLP". +-- w F Preview window flag, text is "[Preview]". +-- W F Preview window flag, text is ",PRV". +-- y F Type of file in the buffer, e.g., "[vim]". See `'filetype'` . +-- Y F Type of file in the buffer, e.g., ",VIM". See `'filetype'` . +-- q S "[Quickfix List]", "[Location List]" or empty. +-- k S Value of "b:keymap_name" or `'keymap'` when |:lmap| mappings are +-- being used: "" +-- n N Buffer number. +-- b N Value of character under cursor. +-- B N As above, in hexadecimal. +-- o N Byte number in file of byte under cursor, first byte is 1. +-- Mnemonic: Offset from start of file (with one added) +-- O N As above, in hexadecimal. +-- l N Line number. +-- L N Number of lines in buffer. +-- c N Column number (byte index). +-- v N Virtual column number (screen column). +-- V N Virtual column number as -{num}. Not displayed if equal to `'c'` . +-- p N Percentage through file in lines as in |CTRL-G|. +-- P S Percentage through file of displayed window. This is like the +-- percentage described for `'ruler'` . Always 3 in length, unless +-- translated. +-- S S `'showcmd'` content, see `'showcmdloc'` . +-- a S Argument list status as in default title. ({current} of {max}) +-- Empty if the argument file count is zero or one. +-- { NF Evaluate expression between "%{" and "}" and substitute result. +-- Note that there is no "%" before the closing "}". The +-- expression cannot contain a "}" character, call a function to +-- work around that. See |stl-%{| below. +-- `{%` - This is almost same as "{" except the result of the expression is +-- re-evaluated as a statusline format string. Thus if the +-- return value of expr contains "%" items they will get expanded. +-- The expression can contain the "}" character, the end of +-- expression is denoted by "%}". +-- For example: > +-- func! Stl_filename() abort +-- return "%t" +-- endfunc +-- < `stl=%{Stl_filename()}` results in `"%t"` +-- `stl=%{%Stl_filename()%}` results in `"Name of current file"` +-- %} - End of "{%" expression +-- ( - Start of item group. Can be used for setting the width and +-- alignment of a section. Must be followed by %) somewhere. +-- ) - End of item group. No width fields allowed. +-- T N For `'tabline'` : start of tab page N label. Use %T or %X to end +-- the label. Clicking this label with left mouse button switches +-- to the specified tab page. +-- X N For `'tabline'` : start of close tab N label. Use %X or %T to end +-- the label, e.g.: %3Xclose%X. Use %999X for a "close current +-- tab" label. Clicking this label with left mouse button closes +-- specified tab page. +-- @ N Start of execute function label. Use %X or %T to +-- end the label, e.g.: %10@SwitchBuffer@foo.c%X. Clicking this +-- label runs specified function: in the example when clicking once +-- using left mouse button on "foo.c" "SwitchBuffer(10, 1, `'l'` , +-- ' ')" expression will be run. Function receives the +-- following arguments in order: +-- 1. minwid field value or zero if no N was specified +-- 2. number of mouse clicks to detect multiple clicks +-- 3. mouse button used: "l", "r" or "m" for left, right or middle +-- button respectively; one should not rely on third argument +-- being only "l", "r" or "m": any other non-empty string value +-- that contains only ASCII lower case letters may be expected +-- for other mouse buttons +-- 4. modifiers pressed: string which contains "s" if shift +-- modifier was pressed, "c" for control, "a" for alt and "m" +-- for meta; currently if modifier is not pressed string +-- contains space instead, but one should not rely on presence +-- of spaces or specific order of modifiers: use |stridx()| to +-- test whether some modifier is present; string is guaranteed +-- to contain only ASCII letters and spaces, one letter per +-- modifier; "?" modifier may also be present, but its presence +-- is a bug that denotes that new mouse button recognition was +-- added without modifying code that reacts on mouse clicks on +-- this label. +-- Use |getmousepos()|.winid in the specified function to get the +-- corresponding window id of the clicked item. +-- < - Where to truncate line if too long. Default is at the start. +-- No width fields allowed. +-- = - Separation point between alignment sections. Each section will +-- be separated by an equal number of spaces. With one %= what +-- comes after it will be right-aligned. With two %= there is a +-- middle part, with white space left and right of it. +-- No width fields allowed. +-- # - Set highlight group. The name must follow and then a # again. +-- Thus use %#HLname# for highlight group HLname. The same +-- highlighting is used, also for the statusline of non-current +-- windows. +-- * - Set highlight group to User{N}, where {N} is taken from the +-- minwid field, e.g. %1*. Restore normal highlight with %* or %0*. +-- The difference between User{N} and StatusLine will be applied to +-- StatusLineNC for the statusline of non-current windows. +-- The number N must be between 1 and 9. See |hl-User1..9| +-- +-- When displaying a flag, Vim removes the leading comma, if any, when +-- that flag comes right after plaintext. This will make a nice display +-- when flags are used like in the examples below. +-- +-- When all items in a group becomes an empty string (i.e. flags that are +-- not set) and a minwid is not set for the group, the whole group will +-- become empty. This will make a group like the following disappear +-- completely from the statusline when none of the flags are set. > +-- :set statusline=...%(\ [%M%R%H]%)... +-- < Beware that an expression is evaluated each and every time the status +-- line is displayed. +-- +-- While evaluating %{} the current buffer and current window will be set +-- temporarily to that of the window (and buffer) whose statusline is +-- currently being drawn. The expression will evaluate in this context. +-- The variable "g:actual_curbuf" is set to the `bufnr()` number of the +-- real current buffer and "g:actual_curwin" to the |window-ID| of the +-- real current window. These values are strings. +-- +-- The `'statusline'` option will be evaluated in the |sandbox| if set from +-- a modeline, see |sandbox-option|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- It is not allowed to change text or jump to another window while +-- evaluating `'statusline'` |textlock|. +-- +-- If the statusline is not updated when you want it (e.g., after setting +-- a variable that's used in an expression), you can force an update by +-- using `:redrawstatus`. +-- +-- A result of all digits is regarded a number for display purposes. +-- Otherwise the result is taken as flag text and applied to the rules +-- described above. +-- +-- Watch out for errors in expressions. They may render Vim unusable! +-- If you are stuck, hold down `':'` or `'Q'` to get a prompt, then quit and +-- edit your vimrc or whatever with "vim --clean" to get it right. +-- +-- Examples: +-- Emulate standard status line with `'ruler'` set > +-- :set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P +-- < Similar, but add ASCII value of char under the cursor (like "ga") > +-- :set statusline=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P +-- < Display byte count and byte value, modified flag in red. > +-- :set statusline=%<%f%=\ [%1%n%R%H]\ %-19(%3l,%02c%03V%)%O`'%02b'` +-- :hi User1 term=inverse,bold cterm=inverse,bold ctermfg=red +-- < Display a ,GZ flag if a compressed file is loaded > +-- :set statusline=...%r%{VarExists(`'b:gzflag'` ,'\ [GZ]')}%h... +-- < In the |:autocmd|'s: > +-- :let b:gzflag = 1 +-- < And: > +-- :unlet b:gzflag +-- < And define this function: > +-- :function VarExists(var, val) +-- : if exists(a:var) | return a:val | else | return `''` | endif +-- :endfunction +-- < +--- @class vim.opt.statusline: vim.Option,string +--- @operator add: vim.opt.statusline +--- @operator sub: vim.opt.statusline +--- @operator pow: vim.opt.statusline +vim.opt.statusline = "" +vim.opt.stl = vim.opt.statusline +--- @return string +function vim.opt.statusline:get()end + +-- `'suffixes'` `'su'` string (default ".bak,~,.o,.h,.info,.swp,.obj") +-- global +-- Files with these suffixes get a lower priority when multiple files +-- match a wildcard. See |suffixes|. Commas can be used to separate the +-- suffixes. Spaces after the comma are ignored. A dot is also seen as +-- the start of a suffix. To avoid a dot or comma being recognized as a +-- separator, precede it with a backslash (see |option-backslash| about +-- including spaces and backslashes). +-- See `'wildignore'` for completely ignoring files. +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- suffixes from the list. This avoids problems when a future version +-- uses another default. +--- @class vim.opt.suffixes: vim.Option,string[] +--- @operator add: vim.opt.suffixes +--- @operator sub: vim.opt.suffixes +--- @operator pow: vim.opt.suffixes +vim.opt.suffixes = ".bak,~,.o,.h,.info,.swp,.obj" +vim.opt.su = vim.opt.suffixes +--- @return string[] +function vim.opt.suffixes:get()end + +-- `'suffixesadd'` `'sua'` string (default "") +-- local to buffer +-- Comma-separated list of suffixes, which are used when searching for a +-- file for the "gf", "[I", etc. commands. Example: > +-- :set suffixesadd=.java +-- < +--- @class vim.opt.suffixesadd: vim.Option,string[] +--- @operator add: vim.opt.suffixesadd +--- @operator sub: vim.opt.suffixesadd +--- @operator pow: vim.opt.suffixesadd +vim.opt.suffixesadd = "" +vim.opt.sua = vim.opt.suffixesadd +--- @return string[] +function vim.opt.suffixesadd:get()end + +-- `'swapfile'` `'swf'` boolean (default on) +-- local to buffer +-- Use a swapfile for the buffer. This option can be reset when a +-- swapfile is not wanted for a specific buffer. For example, with +-- confidential information that even root must not be able to access. +-- Careful: All text will be in memory: +-- - Don't use this for big files. +-- - Recovery will be impossible! +-- A swapfile will only be present when |`'updatecount'` | is non-zero and +-- `'swapfile'` is set. +-- When `'swapfile'` is reset, the swap file for the current buffer is +-- immediately deleted. When `'swapfile'` is set, and `'updatecount'` is +-- non-zero, a swap file is immediately created. +-- Also see |swap-file|. +-- If you want to open a new buffer without creating a swap file for it, +-- use the |:noswapfile| modifier. +-- See `'directory'` for where the swap file is created. +-- +-- This option is used together with `'bufhidden'` and `'buftype'` to +-- specify special kinds of buffers. See |special-buffers|. +--- @class vim.opt.swapfile: vim.Option,boolean +--- @operator add: vim.opt.swapfile +--- @operator sub: vim.opt.swapfile +--- @operator pow: vim.opt.swapfile +vim.opt.swapfile = true +vim.opt.swf = vim.opt.swapfile +--- @return boolean +function vim.opt.swapfile:get()end + +-- `'switchbuf'` `'swb'` string (default "uselast") +-- global +-- This option controls the behavior when switching between buffers. +-- Mostly for |quickfix| commands some values are also used for other +-- commands, as mentioned below. +-- Possible values (comma-separated list): +-- useopen If included, jump to the first open window that +-- contains the specified buffer (if there is one). +-- Otherwise: Do not examine other windows. +-- This setting is checked with |quickfix| commands, when +-- jumping to errors (":cc", ":cn", "cp", etc.). It is +-- also used in all buffer related split commands, for +-- example ":sbuffer", ":sbnext", or ":sbrewind". +-- usetab Like "useopen", but also consider windows in other tab +-- pages. +-- split If included, split the current window before loading +-- a buffer for a |quickfix| command that display errors. +-- Otherwise: do not split, use current window (when used +-- in the quickfix window: the previously used window or +-- split if there is no other window). +-- vsplit Just like "split" but split vertically. +-- newtab Like "split", but open a new tab page. Overrules +-- "split" when both are present. +-- uselast If included, jump to the previously used window when +-- jumping to errors with |quickfix| commands. +--- @class vim.opt.switchbuf: vim.Option,string[] +--- @operator add: vim.opt.switchbuf +--- @operator sub: vim.opt.switchbuf +--- @operator pow: vim.opt.switchbuf +vim.opt.switchbuf = "uselast" +vim.opt.swb = vim.opt.switchbuf +--- @return string[] +function vim.opt.switchbuf:get()end + +-- `'synmaxcol'` `'smc'` number (default 3000) +-- local to buffer +-- Maximum column in which to search for syntax items. In long lines the +-- text after this column is not highlighted and following lines may not +-- be highlighted correctly, because the syntax state is cleared. +-- This helps to avoid very slow redrawing for an XML file that is one +-- long line. +-- Set to zero to remove the limit. +--- @class vim.opt.synmaxcol: vim.Option,number +--- @operator add: vim.opt.synmaxcol +--- @operator sub: vim.opt.synmaxcol +--- @operator pow: vim.opt.synmaxcol +vim.opt.synmaxcol = 3000 +vim.opt.smc = vim.opt.synmaxcol +--- @return number +function vim.opt.synmaxcol:get()end + +-- `'syntax'` `'syn'` string (default empty) +-- local to buffer +-- When this option is set, the syntax with this name is loaded, unless +-- syntax highlighting has been switched off with ":syntax off". +-- Otherwise this option does not always reflect the current syntax (the +-- b:current_syntax variable does). +-- This option is most useful in a modeline, for a file which syntax is +-- not automatically recognized. Example, in an IDL file: > +-- /* vim: set syntax=idl : */ +-- < When a dot appears in the value then this separates two filetype +-- names. Example: > +-- /* vim: set syntax=c.doxygen : */ +-- < This will use the "c" syntax first, then the "doxygen" syntax. +-- Note that the second one must be prepared to be loaded as an addition, +-- otherwise it will be skipped. More than one dot may appear. +-- To switch off syntax highlighting for the current file, use: > +-- :set syntax=OFF +-- < To switch syntax highlighting on according to the current value of the +-- `'filetype'` option: > +-- :set syntax=ON +-- < What actually happens when setting the `'syntax'` option is that the +-- Syntax autocommand event is triggered with the value as argument. +-- This option is not copied to another buffer, independent of the `'s'` or +-- `'S'` flag in `'cpoptions'` . +-- Only normal file name characters can be used, "/\*?[|<>" are illegal. +--- @class vim.opt.syntax: vim.Option,string +--- @operator add: vim.opt.syntax +--- @operator sub: vim.opt.syntax +--- @operator pow: vim.opt.syntax +vim.opt.syntax = "" +vim.opt.syn = vim.opt.syntax +--- @return string +function vim.opt.syntax:get()end + +-- `'tabline'` `'tal'` string (default empty) +-- global +-- When non-empty, this option determines the content of the tab pages +-- line at the top of the Vim window. When empty Vim will use a default +-- tab pages line. See |setting-tabline| for more info. +-- +-- The tab pages line only appears as specified with the `'showtabline'` +-- option and only when there is no GUI tab line. When `'e'` is in +-- `'guioptions'` and the GUI supports a tab line `'guitablabel'` is used +-- instead. Note that the two tab pages lines are very different. +-- +-- The value is evaluated like with `'statusline'` . You can use +-- |tabpagenr()|, |tabpagewinnr()| and |tabpagebuflist()| to figure out +-- the text to be displayed. Use "%1T" for the first label, "%2T" for +-- the second one, etc. Use "%X" items for closing labels. +-- +-- When changing something that is used in `'tabline'` that does not +-- trigger it to be updated, use |:redrawtabline|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- Keep in mind that only one of the tab pages is the current one, others +-- are invisible and you can't jump to their windows. +--- @class vim.opt.tabline: vim.Option,string +--- @operator add: vim.opt.tabline +--- @operator sub: vim.opt.tabline +--- @operator pow: vim.opt.tabline +vim.opt.tabline = "" +vim.opt.tal = vim.opt.tabline +--- @return string +function vim.opt.tabline:get()end + +-- `'tabpagemax'` `'tpm'` number (default 50) +-- global +-- Maximum number of tab pages to be opened by the |-p| command line +-- argument or the ":tab all" command. |tabpage| +--- @class vim.opt.tabpagemax: vim.Option,number +--- @operator add: vim.opt.tabpagemax +--- @operator sub: vim.opt.tabpagemax +--- @operator pow: vim.opt.tabpagemax +vim.opt.tabpagemax = 50 +vim.opt.tpm = vim.opt.tabpagemax +--- @return number +function vim.opt.tabpagemax:get()end + +-- `'tabstop'` `'ts'` number (default 8) +-- local to buffer +-- Number of spaces that a in the file counts for. Also see +-- the |:retab| command, and the `'softtabstop'` option. +-- +-- Note: Setting `'tabstop'` to any other value than 8 can make your file +-- appear wrong in many places. +-- The value must be more than 0 and less than 10000. +-- +-- There are four main ways to use tabs in Vim: +-- 1. Always keep `'tabstop'` at 8, set `'softtabstop'` and `'shiftwidth'` to 4 +-- (or 3 or whatever you prefer) and use `'noexpandtab'` . Then Vim +-- will use a mix of tabs and spaces, but typing and will +-- behave like a tab appears every 4 (or 3) characters. +-- 2. Set `'tabstop'` and `'shiftwidth'` to whatever you prefer and use +-- `'expandtab'` . This way you will always insert spaces. The +-- formatting will never be messed up when `'tabstop'` is changed. +-- 3. Set `'tabstop'` and `'shiftwidth'` to whatever you prefer and use a +-- |modeline| to set these values when editing the file again. Only +-- works when using Vim to edit the file. +-- 4. Always set `'tabstop'` and `'shiftwidth'` to the same value, and +-- `'noexpandtab'` . This should then work (for initial indents only) +-- for any tabstop setting that people use. It might be nice to have +-- tabs after the first non-blank inserted as spaces if you do this +-- though. Otherwise aligned comments will be wrong when `'tabstop'` is +-- changed. +-- +-- The value of `'tabstop'` will be ignored if |`'vartabstop'` | is set to +-- anything other than an empty string. +--- @class vim.opt.tabstop: vim.Option,number +--- @operator add: vim.opt.tabstop +--- @operator sub: vim.opt.tabstop +--- @operator pow: vim.opt.tabstop +vim.opt.tabstop = 8 +vim.opt.ts = vim.opt.tabstop +--- @return number +function vim.opt.tabstop:get()end + +-- `'tagbsearch'` `'tbs'` boolean (default on) +-- global +-- When searching for a tag (e.g., for the |:ta| command), Vim can either +-- use a binary search or a linear search in a tags file. Binary +-- searching makes searching for a tag a LOT faster, but a linear search +-- will find more tags if the tags file wasn't properly sorted. +-- Vim normally assumes that your tags files are sorted, or indicate that +-- they are not sorted. Only when this is not the case does the +-- `'tagbsearch'` option need to be switched off. +-- +-- When `'tagbsearch'` is on, binary searching is first used in the tags +-- files. In certain situations, Vim will do a linear search instead for +-- certain files, or retry all files with a linear search. When +-- `'tagbsearch'` is off, only a linear search is done. +-- +-- Linear searching is done anyway, for one file, when Vim finds a line +-- at the start of the file indicating that it's not sorted: > +-- !_TAG_FILE_SORTED 0 /some comment/ +-- < [The whitespace before and after the `'0'` must be a single ] +-- +-- When a binary search was done and no match was found in any of the +-- files listed in `'tags'` , and case is ignored or a pattern is used +-- instead of a normal tag name, a retry is done with a linear search. +-- Tags in unsorted tags files, and matches with different case will only +-- be found in the retry. +-- +-- If a tag file indicates that it is case-fold sorted, the second, +-- linear search can be avoided when case is ignored. Use a value of `'2'` +-- in the "!_TAG_FILE_SORTED" line for this. A tag file can be case-fold +-- sorted with the -f switch to "sort" in most unices, as in the command: +-- "sort -f -o tags tags". For Universal ctags and Exuberant ctags +-- version 5.x or higher (at least 5.5) the --sort=foldcase switch can be +-- used for this as well. Note that case must be folded to uppercase for +-- this to work. +-- +-- By default, tag searches are case-sensitive. Case is ignored when +-- `'ignorecase'` is set and `'tagcase'` is "followic", or when `'tagcase'` is +-- "ignore". +-- Also when `'tagcase'` is "followscs" and `'smartcase'` is set, or +-- `'tagcase'` is "smart", and the pattern contains only lowercase +-- characters. +-- +-- When `'tagbsearch'` is off, tags searching is slower when a full match +-- exists, but faster when no full match exists. Tags in unsorted tags +-- files may only be found with `'tagbsearch'` off. +-- When the tags file is not sorted, or sorted in a wrong way (not on +-- ASCII byte value), `'tagbsearch'` should be off, or the line given above +-- must be included in the tags file. +-- This option doesn't affect commands that find all matching tags (e.g., +-- command-line completion and ":help"). +--- @class vim.opt.tagbsearch: vim.Option,boolean +--- @operator add: vim.opt.tagbsearch +--- @operator sub: vim.opt.tagbsearch +--- @operator pow: vim.opt.tagbsearch +vim.opt.tagbsearch = true +vim.opt.tbs = vim.opt.tagbsearch +--- @return boolean +function vim.opt.tagbsearch:get()end + +-- `'tagcase'` `'tc'` string (default "followic") +-- global or local to buffer |global-local| +-- This option specifies how case is handled when searching the tags +-- file: +-- followic Follow the `'ignorecase'` option +-- followscs Follow the `'smartcase'` and `'ignorecase'` options +-- ignore Ignore case +-- match Match case +-- smart Ignore case unless an upper case letter is used +--- @class vim.opt.tagcase: vim.Option,string +--- @operator add: vim.opt.tagcase +--- @operator sub: vim.opt.tagcase +--- @operator pow: vim.opt.tagcase +vim.opt.tagcase = "followic" +vim.opt.tc = vim.opt.tagcase +--- @return string +function vim.opt.tagcase:get()end + +-- `'tagfunc'` `'tfu'` string (default: empty) +-- local to buffer +-- This option specifies a function to be used to perform tag searches. +-- The function gets the tag pattern and should return a List of matching +-- tags. See |tag-function| for an explanation of how to write the +-- function and an example. The value can be the name of a function, a +-- |lambda| or a |Funcref|. See |option-value-function| for more +-- information. +--- @class vim.opt.tagfunc: vim.Option,string +--- @operator add: vim.opt.tagfunc +--- @operator sub: vim.opt.tagfunc +--- @operator pow: vim.opt.tagfunc +vim.opt.tagfunc = "" +vim.opt.tfu = vim.opt.tagfunc +--- @return string +function vim.opt.tagfunc:get()end + +-- `'taglength'` `'tl'` number (default 0) +-- global +-- If non-zero, tags are significant up to this number of characters. +--- @class vim.opt.taglength: vim.Option,number +--- @operator add: vim.opt.taglength +--- @operator sub: vim.opt.taglength +--- @operator pow: vim.opt.taglength +vim.opt.taglength = 0 +vim.opt.tl = vim.opt.taglength +--- @return number +function vim.opt.taglength:get()end + +-- `'tagrelative'` `'tr'` boolean (default: on) +-- global +-- If on and using a tags file in another directory, file names in that +-- tags file are relative to the directory where the tags file is. +--- @class vim.opt.tagrelative: vim.Option,boolean +--- @operator add: vim.opt.tagrelative +--- @operator sub: vim.opt.tagrelative +--- @operator pow: vim.opt.tagrelative +vim.opt.tagrelative = true +vim.opt.tr = vim.opt.tagrelative +--- @return boolean +function vim.opt.tagrelative:get()end + +-- `'tags'` `'tag'` string (default "./tags;,tags") +-- global or local to buffer |global-local| +-- Filenames for the tag command, separated by spaces or commas. To +-- include a space or comma in a file name, precede it with a backslash +-- (see |option-backslash| about including spaces and backslashes). +-- When a file name starts with "./", the `'.'` is replaced with the path +-- of the current file. But only when the `'d'` flag is not included in +-- `'cpoptions'` . Environment variables are expanded |:set_env|. Also see +-- |tags-option|. +-- "*", "" and other wildcards can be used to search for tags files in +-- a directory tree. See |file-searching|. E.g., "/lib//tags" will +-- find all files named "tags" below "/lib". The filename itself cannot +-- contain wildcards, it is used as-is. E.g., "/lib//tags?" will find +-- files called "tags?". +-- The |tagfiles()| function can be used to get a list of the file names +-- actually used. +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- file names from the list. This avoids problems when a future version +-- uses another default. +--- @class vim.opt.tags: vim.Option,string[] +--- @operator add: vim.opt.tags +--- @operator sub: vim.opt.tags +--- @operator pow: vim.opt.tags +vim.opt.tags = "./tags;,tags" +vim.opt.tag = vim.opt.tags +--- @return string[] +function vim.opt.tags:get()end + +-- `'tagstack'` `'tgst'` boolean (default on) +-- global +-- When on, the |tagstack| is used normally. When off, a ":tag" or +-- ":tselect" command with an argument will not push the tag onto the +-- tagstack. A following ":tag" without an argument, a ":pop" command or +-- any other command that uses the tagstack will use the unmodified +-- tagstack, but does change the pointer to the active entry. +-- Resetting this option is useful when using a ":tag" command in a +-- mapping which should not change the tagstack. +--- @class vim.opt.tagstack: vim.Option,boolean +--- @operator add: vim.opt.tagstack +--- @operator sub: vim.opt.tagstack +--- @operator pow: vim.opt.tagstack +vim.opt.tagstack = true +vim.opt.tgst = vim.opt.tagstack +--- @return boolean +function vim.opt.tagstack:get()end + +-- `'termbidi'` `'tbidi'` boolean (default off) +-- global +-- The terminal is in charge of Bi-directionality of text (as specified +-- by Unicode). The terminal is also expected to do the required shaping +-- that some languages (such as Arabic) require. +-- Setting this option implies that `'rightleft'` will not be set when +-- `'arabic'` is set and the value of `'arabicshape'` will be ignored. +-- Note that setting `'termbidi'` has the immediate effect that +-- `'arabicshape'` is ignored, but `'rightleft'` isn't changed automatically. +-- For further details see |arabic.txt|. +--- @class vim.opt.termbidi: vim.Option,boolean +--- @operator add: vim.opt.termbidi +--- @operator sub: vim.opt.termbidi +--- @operator pow: vim.opt.termbidi +vim.opt.termbidi = false +vim.opt.tbidi = vim.opt.termbidi +--- @return boolean +function vim.opt.termbidi:get()end + +--- @class vim.opt.termencoding: vim.Option,string +--- @operator add: vim.opt.termencoding +--- @operator sub: vim.opt.termencoding +--- @operator pow: vim.opt.termencoding +vim.opt.termencoding = "" +vim.opt.tenc = vim.opt.termencoding +--- @return string +function vim.opt.termencoding:get()end + +-- `'termguicolors'` `'tgc'` boolean (default off) +-- global +-- Enables 24-bit RGB color in the |TUI|. Uses "gui" |:highlight| +-- attributes instead of "cterm" attributes. |guifg| +-- Requires an ISO-8613-3 compatible terminal. +--- @class vim.opt.termguicolors: vim.Option,boolean +--- @operator add: vim.opt.termguicolors +--- @operator sub: vim.opt.termguicolors +--- @operator pow: vim.opt.termguicolors +vim.opt.termguicolors = false +vim.opt.tgc = vim.opt.termguicolors +--- @return boolean +function vim.opt.termguicolors:get()end + +-- `'termpastefilter'` `'tpf'` string (default: "BS,HT,ESC,DEL") +-- global +-- A comma-separated list of options for specifying control characters +-- to be removed from the text pasted into the terminal window. The +-- supported values are: +-- +-- BS Backspace +-- +-- HT TAB +-- +-- FF Form feed +-- +-- ESC Escape +-- +-- DEL DEL +-- +-- C0 Other control characters, excluding Line feed and +-- Carriage return < ' ' +-- +-- C1 Control characters 0x80...0x9F +--- @class vim.opt.termpastefilter: vim.Option,string[] +--- @operator add: vim.opt.termpastefilter +--- @operator sub: vim.opt.termpastefilter +--- @operator pow: vim.opt.termpastefilter +vim.opt.termpastefilter = "BS,HT,ESC,DEL" +vim.opt.tpf = vim.opt.termpastefilter +--- @return string[] +function vim.opt.termpastefilter:get()end + +--- @class vim.opt.terse: vim.Option,boolean +--- @operator add: vim.opt.terse +--- @operator sub: vim.opt.terse +--- @operator pow: vim.opt.terse +vim.opt.terse = false +--- @return boolean +function vim.opt.terse:get()end + +-- `'textwidth'` `'tw'` number (default 0) +-- local to buffer +-- Maximum width of text that is being inserted. A longer line will be +-- broken after white space to get this width. A zero value disables +-- this. +-- When `'textwidth'` is zero, `'wrapmargin'` may be used. See also +-- `'formatoptions'` and |ins-textwidth|. +-- When `'formatexpr'` is set it will be used to break the line. +--- @class vim.opt.textwidth: vim.Option,number +--- @operator add: vim.opt.textwidth +--- @operator sub: vim.opt.textwidth +--- @operator pow: vim.opt.textwidth +vim.opt.textwidth = 0 +vim.opt.tw = vim.opt.textwidth +--- @return number +function vim.opt.textwidth:get()end + +-- `'thesaurus'` `'tsr'` string (default "") +-- global or local to buffer |global-local| +-- List of file names, separated by commas, that are used to lookup words +-- for thesaurus completion commands |i_CTRL-X_CTRL-T|. See +-- |compl-thesaurus|. +-- +-- This option is not used if `'thesaurusfunc'` is set, either for the +-- buffer or globally. +-- +-- To include a comma in a file name precede it with a backslash. Spaces +-- after a comma are ignored, otherwise spaces are included in the file +-- name. See |option-backslash| about using backslashes. The use of +-- |:set+=| and |:set-=| is preferred when adding or removing directories +-- from the list. This avoids problems when a future version uses +-- another default. Backticks cannot be used in this option for security +-- reasons. +--- @class vim.opt.thesaurus: vim.Option,string[] +--- @operator add: vim.opt.thesaurus +--- @operator sub: vim.opt.thesaurus +--- @operator pow: vim.opt.thesaurus +vim.opt.thesaurus = "" +vim.opt.tsr = vim.opt.thesaurus +--- @return string[] +function vim.opt.thesaurus:get()end + +-- `'thesaurusfunc'` `'tsrfu'` string (default: empty) +-- global or local to buffer |global-local| +-- This option specifies a function to be used for thesaurus completion +-- with CTRL-X CTRL-T. |i_CTRL-X_CTRL-T| See |compl-thesaurusfunc|. +-- The value can be the name of a function, a |lambda| or a |Funcref|. +-- See |option-value-function| for more information. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.thesaurusfunc: vim.Option,string +--- @operator add: vim.opt.thesaurusfunc +--- @operator sub: vim.opt.thesaurusfunc +--- @operator pow: vim.opt.thesaurusfunc +vim.opt.thesaurusfunc = "" +vim.opt.tsrfu = vim.opt.thesaurusfunc +--- @return string +function vim.opt.thesaurusfunc:get()end + +-- `'tildeop'` `'top'` boolean (default off) +-- global +-- When on: The tilde command "~" behaves like an operator. +--- @class vim.opt.tildeop: vim.Option,boolean +--- @operator add: vim.opt.tildeop +--- @operator sub: vim.opt.tildeop +--- @operator pow: vim.opt.tildeop +vim.opt.tildeop = false +vim.opt.top = vim.opt.tildeop +--- @return boolean +function vim.opt.tildeop:get()end + +-- `'timeout'` `'to'` boolean (default on) +-- global +-- This option and `'timeoutlen'` determine the behavior when part of a +-- mapped key sequence has been received. For example, if is +-- pressed and `'timeout'` is set, Nvim will wait `'timeoutlen'` milliseconds +-- for any key that can follow in a mapping. +--- @class vim.opt.timeout: vim.Option,boolean +--- @operator add: vim.opt.timeout +--- @operator sub: vim.opt.timeout +--- @operator pow: vim.opt.timeout +vim.opt.timeout = true +vim.opt.to = vim.opt.timeout +--- @return boolean +function vim.opt.timeout:get()end + +-- `'timeoutlen'` `'tm'` number (default 1000) +-- global +-- Time in milliseconds to wait for a mapped sequence to complete. +--- @class vim.opt.timeoutlen: vim.Option,number +--- @operator add: vim.opt.timeoutlen +--- @operator sub: vim.opt.timeoutlen +--- @operator pow: vim.opt.timeoutlen +vim.opt.timeoutlen = 1000 +vim.opt.tm = vim.opt.timeoutlen +--- @return number +function vim.opt.timeoutlen:get()end + +-- `'title'` boolean (default off) +-- global +-- When on, the title of the window will be set to the value of +-- `'titlestring'` (if it is not empty), or to: +-- filename [+=-] (path) - NVIM +-- Where: +-- filename the name of the file being edited +-- - indicates the file cannot be modified, `'ma'` off +-- + indicates the file was modified +-- = indicates the file is read-only +-- =+ indicates the file is read-only and modified +-- (path) is the path of the file being edited +-- - NVIM the server name |v:servername| or "NVIM" +--- @class vim.opt.title: vim.Option,boolean +--- @operator add: vim.opt.title +--- @operator sub: vim.opt.title +--- @operator pow: vim.opt.title +vim.opt.title = false +--- @return boolean +function vim.opt.title:get()end + +-- `'titlelen'` number (default 85) +-- global +-- Gives the percentage of `'columns'` to use for the length of the window +-- title. When the title is longer, only the end of the path name is +-- shown. A `'<'` character before the path name is used to indicate this. +-- Using a percentage makes this adapt to the width of the window. But +-- it won't work perfectly, because the actual number of characters +-- available also depends on the font used and other things in the title +-- bar. When `'titlelen'` is zero the full path is used. Otherwise, +-- values from 1 to 30000 percent can be used. +-- `'titlelen'` is also used for the `'titlestring'` option. +--- @class vim.opt.titlelen: vim.Option,number +--- @operator add: vim.opt.titlelen +--- @operator sub: vim.opt.titlelen +--- @operator pow: vim.opt.titlelen +vim.opt.titlelen = 85 +--- @return number +function vim.opt.titlelen:get()end + +-- `'titleold'` string (default "") +-- global +-- If not empty, this option will be used to set the window title when +-- exiting. Only if `'title'` is enabled. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.titleold: vim.Option,string +--- @operator add: vim.opt.titleold +--- @operator sub: vim.opt.titleold +--- @operator pow: vim.opt.titleold +vim.opt.titleold = "" +--- @return string +function vim.opt.titleold:get()end + +-- `'titlestring'` string (default "") +-- global +-- When this option is not empty, it will be used for the title of the +-- window. This happens only when the `'title'` option is on. +-- +-- When this option contains printf-style `'%'` items, they will be +-- expanded according to the rules used for `'statusline'` . +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- Example: > +-- :auto BufEnter * let &titlestring = hostname() .. "/" .. expand("%:p") +-- :set title titlestring=%<%F%=%l/%L-%P titlelen=70 +-- < The value of `'titlelen'` is used to align items in the middle or right +-- of the available space. +-- Some people prefer to have the file name first: > +-- :set titlestring=%t%(\ %M%)%(\ (%{expand(\"%:~:.:h\")})%)%(\ %a%) +-- < Note the use of "%{ }" and an expression to get the path of the file, +-- without the file name. The "%( %)" constructs are used to add a +-- separating space only when needed. +-- NOTE: Use of special characters in `'titlestring'` may cause the display +-- to be garbled (e.g., when it contains a CR or NL character). +--- @class vim.opt.titlestring: vim.Option,string +--- @operator add: vim.opt.titlestring +--- @operator sub: vim.opt.titlestring +--- @operator pow: vim.opt.titlestring +vim.opt.titlestring = "" +--- @return string +function vim.opt.titlestring:get()end + +-- `'ttimeout'` boolean (default on) +-- global +-- This option and `'ttimeoutlen'` determine the behavior when part of a +-- key code sequence has been received by the |TUI|. +-- +-- For example if (the \x1b byte) is received and `'ttimeout'` is +-- set, Nvim waits `'ttimeoutlen'` milliseconds for the terminal to +-- complete a key code sequence. If no input arrives before the timeout, +-- a single is assumed. Many TUI cursor key codes start with . +-- +-- On very slow systems this may fail, causing cursor keys not to work +-- sometimes. If you discover this problem you can ":set ttimeoutlen=9999". +-- Nvim will wait for the next character to arrive after an . +--- @class vim.opt.ttimeout: vim.Option,boolean +--- @operator add: vim.opt.ttimeout +--- @operator sub: vim.opt.ttimeout +--- @operator pow: vim.opt.ttimeout +vim.opt.ttimeout = true +--- @return boolean +function vim.opt.ttimeout:get()end + +-- `'ttimeoutlen'` `'ttm'` number (default 50) +-- global +-- Time in milliseconds to wait for a key code sequence to complete. Also +-- used for CTRL-\ CTRL-N and CTRL-\ CTRL-G when part of a command has +-- been typed. +--- @class vim.opt.ttimeoutlen: vim.Option,number +--- @operator add: vim.opt.ttimeoutlen +--- @operator sub: vim.opt.ttimeoutlen +--- @operator pow: vim.opt.ttimeoutlen +vim.opt.ttimeoutlen = 50 +vim.opt.ttm = vim.opt.ttimeoutlen +--- @return number +function vim.opt.ttimeoutlen:get()end + +--- @class vim.opt.ttyfast: vim.Option,boolean +--- @operator add: vim.opt.ttyfast +--- @operator sub: vim.opt.ttyfast +--- @operator pow: vim.opt.ttyfast +vim.opt.ttyfast = true +vim.opt.tf = vim.opt.ttyfast +--- @return boolean +function vim.opt.ttyfast:get()end + +-- `'undodir'` `'udir'` string (default "$XDG_STATE_HOME/nvim/undo//") +-- global +-- List of directory names for undo files, separated with commas. +-- See `'backupdir'` for details of the format. +-- "." means using the directory of the file. The undo file name for +-- "file.txt" is ".file.txt.un~". +-- For other directories the file name is the full path of the edited +-- file, with path separators replaced with "%". +-- When writing: The first directory that exists is used. "." always +-- works, no directories after "." will be used for writing. If none of +-- the directories exist Nvim will attempt to create the last directory in +-- the list. +-- When reading all entries are tried to find an undo file. The first +-- undo file that exists is used. When it cannot be read an error is +-- given, no further entry is used. +-- See |undo-persistence|. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +-- +-- Note that unlike `'directory'` and `'backupdir'` , `'undodir'` always acts as +-- though the trailing slashes are present (see `'backupdir'` for what this +-- means). +--- @class vim.opt.undodir: vim.Option,string[] +--- @operator add: vim.opt.undodir +--- @operator sub: vim.opt.undodir +--- @operator pow: vim.opt.undodir +vim.opt.undodir = "/home/runner/.local/state/nvim/undo//" +vim.opt.udir = vim.opt.undodir +--- @return string[] +function vim.opt.undodir:get()end + +-- `'undofile'` `'udf'` boolean (default off) +-- local to buffer +-- When on, Vim automatically saves undo history to an undo file when +-- writing a buffer to a file, and restores undo history from the same +-- file on buffer read. +-- The directory where the undo file is stored is specified by `'undodir'` . +-- For more information about this feature see |undo-persistence|. +-- The undo file is not read when `'undoreload'` causes the buffer from +-- before a reload to be saved for undo. +-- When `'undofile'` is turned off the undo file is NOT deleted. +--- @class vim.opt.undofile: vim.Option,boolean +--- @operator add: vim.opt.undofile +--- @operator sub: vim.opt.undofile +--- @operator pow: vim.opt.undofile +vim.opt.undofile = false +vim.opt.udf = vim.opt.undofile +--- @return boolean +function vim.opt.undofile:get()end + +-- `'undolevels'` `'ul'` number (default 1000) +-- global or local to buffer |global-local| +-- Maximum number of changes that can be undone. Since undo information +-- is kept in memory, higher numbers will cause more memory to be used. +-- Nevertheless, a single change can already use a large amount of memory. +-- Set to 0 for Vi compatibility: One level of undo and "u" undoes +-- itself: > +-- set ul=0 +-- < But you can also get Vi compatibility by including the `'u'` flag in +-- `'cpoptions'` , and still be able to use CTRL-R to repeat undo. +-- Also see |undo-two-ways|. +-- Set to -1 for no undo at all. You might want to do this only for the +-- current buffer: > +-- setlocal ul=-1 +-- < This helps when you run out of memory for a single change. +-- +-- The local value is set to -123456 when the global value is to be used. +-- +-- Also see |clear-undo|. +--- @class vim.opt.undolevels: vim.Option,number +--- @operator add: vim.opt.undolevels +--- @operator sub: vim.opt.undolevels +--- @operator pow: vim.opt.undolevels +vim.opt.undolevels = 1000 +vim.opt.ul = vim.opt.undolevels +--- @return number +function vim.opt.undolevels:get()end + +-- `'undoreload'` `'ur'` number (default 10000) +-- global +-- Save the whole buffer for undo when reloading it. This applies to the +-- ":e!" command and reloading for when the buffer changed outside of +-- Vim. |FileChangedShell| +-- The save only happens when this option is negative or when the number +-- of lines is smaller than the value of this option. +-- Set this option to zero to disable undo for a reload. +-- +-- When saving undo for a reload, any undo file is not read. +-- +-- Note that this causes the whole buffer to be stored in memory. Set +-- this option to a lower value if you run out of memory. +--- @class vim.opt.undoreload: vim.Option,number +--- @operator add: vim.opt.undoreload +--- @operator sub: vim.opt.undoreload +--- @operator pow: vim.opt.undoreload +vim.opt.undoreload = 10000 +vim.opt.ur = vim.opt.undoreload +--- @return number +function vim.opt.undoreload:get()end + +-- `'updatecount'` `'uc'` number (default: 200) +-- global +-- After typing this many characters the swap file will be written to +-- disk. When zero, no swap file will be created at all (see chapter on +-- recovery |crash-recovery|). `'updatecount'` is set to zero by starting +-- Vim with the "-n" option, see |startup|. When editing in readonly +-- mode this option will be initialized to 10000. +-- The swapfile can be disabled per buffer with |`'swapfile'` |. +-- When `'updatecount'` is set from zero to non-zero, swap files are +-- created for all buffers that have `'swapfile'` set. When `'updatecount'` +-- is set to zero, existing swap files are not deleted. +-- This option has no meaning in buffers where |`'buftype'` | is "nofile" +-- or "nowrite". +--- @class vim.opt.updatecount: vim.Option,number +--- @operator add: vim.opt.updatecount +--- @operator sub: vim.opt.updatecount +--- @operator pow: vim.opt.updatecount +vim.opt.updatecount = 200 +vim.opt.uc = vim.opt.updatecount +--- @return number +function vim.opt.updatecount:get()end + +-- `'updatetime'` `'ut'` number (default 4000) +-- global +-- If this many milliseconds nothing is typed the swap file will be +-- written to disk (see |crash-recovery|). Also used for the +-- |CursorHold| autocommand event. +--- @class vim.opt.updatetime: vim.Option,number +--- @operator add: vim.opt.updatetime +--- @operator sub: vim.opt.updatetime +--- @operator pow: vim.opt.updatetime +vim.opt.updatetime = 4000 +vim.opt.ut = vim.opt.updatetime +--- @return number +function vim.opt.updatetime:get()end + +-- `'varsofttabstop'` `'vsts'` string (default "") +-- local to buffer +-- A list of the number of spaces that a counts for while editing, +-- such as inserting a or using . It "feels" like variable- +-- width s are being inserted, while in fact a mixture of spaces +-- and s is used. Tab widths are separated with commas, with the +-- final value applying to all subsequent tabs. +-- +-- For example, when editing assembly language files where statements +-- start in the 9th column and comments in the 41st, it may be useful +-- to use the following: > +-- :set varsofttabstop=8,32,8 +-- < This will set soft tabstops with 8 and 8 + 32 spaces, and 8 more +-- for every column thereafter. +-- +-- Note that the value of |`'softtabstop'` | will be ignored while +-- `'varsofttabstop'` is set. +--- @class vim.opt.varsofttabstop: vim.Option,string[] +--- @operator add: vim.opt.varsofttabstop +--- @operator sub: vim.opt.varsofttabstop +--- @operator pow: vim.opt.varsofttabstop +vim.opt.varsofttabstop = "" +vim.opt.vsts = vim.opt.varsofttabstop +--- @return string[] +function vim.opt.varsofttabstop:get()end + +-- `'vartabstop'` `'vts'` string (default "") +-- local to buffer +-- A list of the number of spaces that a in the file counts for, +-- separated by commas. Each value corresponds to one tab, with the +-- final value applying to all subsequent tabs. For example: > +-- :set vartabstop=4,20,10,8 +-- < This will make the first tab 4 spaces wide, the second 20 spaces, +-- the third 10 spaces, and all following tabs 8 spaces. +-- +-- Note that the value of |`'tabstop'` | will be ignored while `'vartabstop'` +-- is set. +--- @class vim.opt.vartabstop: vim.Option,string[] +--- @operator add: vim.opt.vartabstop +--- @operator sub: vim.opt.vartabstop +--- @operator pow: vim.opt.vartabstop +vim.opt.vartabstop = "" +vim.opt.vts = vim.opt.vartabstop +--- @return string[] +function vim.opt.vartabstop:get()end + +-- `'verbose'` `'vbs'` number (default 0) +-- global +-- Sets the verbosity level. Also set by |-V| and |:verbose|. +-- +-- Tracing of options in Lua scripts is activated at level 1; Lua scripts +-- are not traced with verbose=0, for performance. +-- +-- If greater than or equal to a given level, Nvim produces the following +-- messages: +-- +-- Level Messages ~ +-- ---------------------------------------------------------------------- +-- 1 Lua assignments to options, mappings, etc. +-- 2 When a file is ":source"'ed, or |shada| file is read or written. +-- 3 UI info, terminal capabilities. +-- 4 Shell commands. +-- 5 Every searched tags file and include file. +-- 8 Files for which a group of autocommands is executed. +-- 9 Executed autocommands. +-- 11 Finding items in a path. +-- 12 Vimscript function calls. +-- 13 When an exception is thrown, caught, finished, or discarded. +-- 14 Anything pending in a ":finally" clause. +-- 15 Ex commands from a script (truncated at 200 characters). +-- 16 Ex commands. +-- +-- If `'verbosefile'` is set then the verbose messages are not displayed. +--- @class vim.opt.verbose: vim.Option,number +--- @operator add: vim.opt.verbose +--- @operator sub: vim.opt.verbose +--- @operator pow: vim.opt.verbose +vim.opt.verbose = 0 +vim.opt.vbs = vim.opt.verbose +--- @return number +function vim.opt.verbose:get()end + +-- `'verbosefile'` `'vfile'` string (default empty) +-- global +-- When not empty all messages are written in a file with this name. +-- When the file exists messages are appended. +-- Writing to the file ends when Vim exits or when `'verbosefile'` is made +-- empty. Writes are buffered, thus may not show up for some time. +-- Setting `'verbosefile'` to a new value is like making it empty first. +-- The difference with |:redir| is that verbose messages are not +-- displayed when `'verbosefile'` is set. +--- @class vim.opt.verbosefile: vim.Option,string +--- @operator add: vim.opt.verbosefile +--- @operator sub: vim.opt.verbosefile +--- @operator pow: vim.opt.verbosefile +vim.opt.verbosefile = "" +vim.opt.vfile = vim.opt.verbosefile +--- @return string +function vim.opt.verbosefile:get()end + +-- `'viewdir'` `'vdir'` string (default: "$XDG_STATE_HOME/nvim/view//") +-- global +-- Name of the directory where to store files for |:mkview|. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +--- @class vim.opt.viewdir: vim.Option,string +--- @operator add: vim.opt.viewdir +--- @operator sub: vim.opt.viewdir +--- @operator pow: vim.opt.viewdir +vim.opt.viewdir = "/home/runner/.local/state/nvim/view//" +vim.opt.vdir = vim.opt.viewdir +--- @return string +function vim.opt.viewdir:get()end + +-- `'viewoptions'` `'vop'` string (default: "folds,cursor,curdir") +-- global +-- Changes the effect of the |:mkview| command. It is a comma-separated +-- list of words. Each word enables saving and restoring something: +-- word save and restore ~ +-- cursor cursor position in file and in window +-- curdir local current directory, if set with |:lcd| +-- folds manually created folds, opened/closed folds and local +-- fold options +-- options options and mappings local to a window or buffer (not +-- global values for local options) +-- localoptions same as "options" +-- slash |deprecated| Always enabled. Uses "/" in filenames. +-- unix |deprecated| Always enabled. Uses "\n" line endings. +--- @class vim.opt.viewoptions: vim.Option,string[] +--- @operator add: vim.opt.viewoptions +--- @operator sub: vim.opt.viewoptions +--- @operator pow: vim.opt.viewoptions +vim.opt.viewoptions = "folds,cursor,curdir" +vim.opt.vop = vim.opt.viewoptions +--- @return string[] +function vim.opt.viewoptions:get()end + +--- @class vim.opt.viminfo: vim.Option,string[] +--- @operator add: vim.opt.viminfo +--- @operator sub: vim.opt.viminfo +--- @operator pow: vim.opt.viminfo +vim.opt.viminfo = "" +vim.opt.vi = vim.opt.viminfo +--- @return string[] +function vim.opt.viminfo:get()end + +--- @class vim.opt.viminfofile: vim.Option,string[] +--- @operator add: vim.opt.viminfofile +--- @operator sub: vim.opt.viminfofile +--- @operator pow: vim.opt.viminfofile +vim.opt.viminfofile = "" +vim.opt.vif = vim.opt.viminfofile +--- @return string[] +function vim.opt.viminfofile:get()end + +-- `'virtualedit'` `'ve'` string (default "") +-- global or local to window |global-local| +-- A comma-separated list of these words: +-- block Allow virtual editing in Visual block mode. +-- insert Allow virtual editing in Insert mode. +-- all Allow virtual editing in all modes. +-- onemore Allow the cursor to move just past the end of the line +-- none When used as the local value, do not allow virtual +-- editing even when the global value is set. When used +-- as the global value, "none" is the same as "". +-- NONE Alternative spelling of "none". +-- +-- Virtual editing means that the cursor can be positioned where there is +-- no actual character. This can be halfway into a tab or beyond the end +-- of the line. Useful for selecting a rectangle in Visual mode and +-- editing a table. +-- "onemore" is not the same, it will only allow moving the cursor just +-- after the last character of the line. This makes some commands more +-- consistent. Previously the cursor was always past the end of the line +-- if the line was empty. But it is far from Vi compatible. It may also +-- break some plugins or Vim scripts. For example because |l| can move +-- the cursor after the last character. Use with care! +-- Using the `$` command will move to the last character in the line, not +-- past it. This may actually move the cursor to the left! +-- The `g$` command will move to the end of the screen line. +-- It doesn't make sense to combine "all" with "onemore", but you will +-- not get a warning for it. +-- When combined with other words, "none" is ignored. +--- @class vim.opt.virtualedit: vim.Option,string[] +--- @operator add: vim.opt.virtualedit +--- @operator sub: vim.opt.virtualedit +--- @operator pow: vim.opt.virtualedit +vim.opt.virtualedit = "" +vim.opt.ve = vim.opt.virtualedit +--- @return string[] +function vim.opt.virtualedit:get()end + +-- `'visualbell'` `'vb'` boolean (default off) +-- global +-- Use visual bell instead of beeping. Also see `'errorbells'` . +--- @class vim.opt.visualbell: vim.Option,boolean +--- @operator add: vim.opt.visualbell +--- @operator sub: vim.opt.visualbell +--- @operator pow: vim.opt.visualbell +vim.opt.visualbell = false +vim.opt.vb = vim.opt.visualbell +--- @return boolean +function vim.opt.visualbell:get()end + +-- `'warn'` boolean (default on) +-- global +-- Give a warning message when a shell command is used while the buffer +-- has been changed. +--- @class vim.opt.warn: vim.Option,boolean +--- @operator add: vim.opt.warn +--- @operator sub: vim.opt.warn +--- @operator pow: vim.opt.warn +vim.opt.warn = true +--- @return boolean +function vim.opt.warn:get()end + +-- `'whichwrap'` `'ww'` string (default: "b,s") +-- global +-- Allow specified keys that move the cursor left/right to move to the +-- previous/next line when the cursor is on the first/last character in +-- the line. Concatenate characters to allow this for these keys: +-- char key mode ~ +-- b Normal and Visual +-- s Normal and Visual +-- h "h" Normal and Visual (not recommended) +-- l "l" Normal and Visual (not recommended) +-- < Normal and Visual +-- > Normal and Visual +-- ~ "~" Normal +-- [ Insert and Replace +-- ] Insert and Replace +-- For example: > +-- :set ww=<,>,[,] +-- < allows wrap only when cursor keys are used. +-- When the movement keys are used in combination with a delete or change +-- operator, the also counts for a character. This makes "3h" +-- different from "3dh" when the cursor crosses the end of a line. This +-- is also true for "x" and "X", because they do the same as "dl" and +-- "dh". If you use this, you may also want to use the mapping +-- ":map X" to make backspace delete the character in front of the +-- cursor. +-- When `'l'` is included and it is used after an operator at the end of a +-- line (not an empty line) then it will not move to the next line. This +-- makes "dl", "cl", "yl" etc. work normally. +--- @class vim.opt.whichwrap: vim.Option,string[] +--- @operator add: vim.opt.whichwrap +--- @operator sub: vim.opt.whichwrap +--- @operator pow: vim.opt.whichwrap +vim.opt.whichwrap = "b,s" +vim.opt.ww = vim.opt.whichwrap +--- @return string[] +function vim.opt.whichwrap:get()end + +-- `'wildchar'` `'wc'` number (default: ) +-- global +-- Character you have to type to start wildcard expansion in the +-- command-line, as specified with `'wildmode'` . +-- More info here: |cmdline-completion|. +-- The character is not recognized when used inside a macro. See +-- `'wildcharm'` for that. +-- Some keys will not work, such as CTRL-C, and Enter. +-- Although `'wc'` is a number option, you can set it to a special key: > +-- :set wc= +-- < +--- @class vim.opt.wildchar: vim.Option,number +--- @operator add: vim.opt.wildchar +--- @operator sub: vim.opt.wildchar +--- @operator pow: vim.opt.wildchar +vim.opt.wildchar = 9 +vim.opt.wc = vim.opt.wildchar +--- @return number +function vim.opt.wildchar:get()end + +-- `'wildcharm'` `'wcm'` number (default: none (0)) +-- global +-- `'wildcharm'` works exactly like `'wildchar'` , except that it is +-- recognized when used inside a macro. You can find "spare" command-line +-- keys suitable for this option by looking at |ex-edit-index|. Normally +-- you'll never actually type `'wildcharm'` , just use it in mappings that +-- automatically invoke completion mode, e.g.: > +-- :set wcm= +-- :cnoremap ss so $vim/sessions/*.vim +-- < Then after typing :ss you can use CTRL-P & CTRL-N. +--- @class vim.opt.wildcharm: vim.Option,number +--- @operator add: vim.opt.wildcharm +--- @operator sub: vim.opt.wildcharm +--- @operator pow: vim.opt.wildcharm +vim.opt.wildcharm = 0 +vim.opt.wcm = vim.opt.wildcharm +--- @return number +function vim.opt.wildcharm:get()end + +-- `'wildignore'` `'wig'` string (default "") +-- global +-- A list of file patterns. A file that matches with one of these +-- patterns is ignored when expanding |wildcards|, completing file or +-- directory names, and influences the result of |expand()|, |glob()| and +-- |globpath()| unless a flag is passed to disable this. +-- The pattern is used like with |:autocmd|, see |autocmd-pattern|. +-- Also see `'suffixes'` . +-- Example: > +-- :set wildignore=.obj +-- < The use of |:set+=| and |:set-=| is preferred when adding or removing +-- a pattern from the list. This avoids problems when a future version +-- uses another default. +--- @class vim.opt.wildignore: vim.Option,string[] +--- @operator add: vim.opt.wildignore +--- @operator sub: vim.opt.wildignore +--- @operator pow: vim.opt.wildignore +vim.opt.wildignore = "" +vim.opt.wig = vim.opt.wildignore +--- @return string[] +function vim.opt.wildignore:get()end + +-- `'wildignorecase'` `'wic'` boolean (default off) +-- global +-- When set case is ignored when completing file names and directories. +-- Has no effect when `'fileignorecase'` is set. +-- Does not apply when the shell is used to expand wildcards, which +-- happens when there are special characters. +--- @class vim.opt.wildignorecase: vim.Option,boolean +--- @operator add: vim.opt.wildignorecase +--- @operator sub: vim.opt.wildignorecase +--- @operator pow: vim.opt.wildignorecase +vim.opt.wildignorecase = false +vim.opt.wic = vim.opt.wildignorecase +--- @return boolean +function vim.opt.wildignorecase:get()end + +-- `'wildmenu'` `'wmnu'` boolean (default on) +-- global +-- When `'wildmenu'` is on, command-line completion operates in an enhanced +-- mode. On pressing `'wildchar'` (usually ) to invoke completion, +-- the possible matches are shown. +-- When `'wildoptions'` contains "pum", then the completion matches are +-- shown in a popup menu. Otherwise they are displayed just above the +-- command line, with the first match highlighted (overwriting the status +-- line, if there is one). +-- Keys that show the previous/next match, such as or +-- CTRL-P/CTRL-N, cause the highlight to move to the appropriate match. +-- `'wildmode'` must specify "full": "longest" and "list" do not start +-- `'wildmenu'` mode. You can check the current mode with |wildmenumode()|. +-- The menu is cancelled when a key is hit that is not used for selecting +-- a completion. +-- +-- While the menu is active these keys have special meanings: +-- +-- CTRL-Y - accept the currently selected match and stop +-- completion. +-- CTRL-E - end completion, go back to what was there before +-- selecting a match. +-- - select previous/next match (like CTRL-P/CTRL-N) +-- - in filename/menu name completion: move into a +-- subdirectory or submenu. +-- - in menu completion, when the cursor is just after a +-- dot: move into a submenu. +-- - in filename/menu name completion: move up into +-- parent directory or parent menu. +-- +-- If you want and to move the cursor instead of selecting +-- a different match, use this: > +-- :cnoremap +-- :cnoremap +-- < +-- |hl-WildMenu| highlights the current match. +--- @class vim.opt.wildmenu: vim.Option,boolean +--- @operator add: vim.opt.wildmenu +--- @operator sub: vim.opt.wildmenu +--- @operator pow: vim.opt.wildmenu +vim.opt.wildmenu = true +vim.opt.wmnu = vim.opt.wildmenu +--- @return boolean +function vim.opt.wildmenu:get()end + +-- `'wildmode'` `'wim'` string (default: "full") +-- global +-- Completion mode that is used for the character specified with +-- `'wildchar'` . It is a comma-separated list of up to four parts. Each +-- part specifies what to do for each consecutive use of `'wildchar'` . The +-- first part specifies the behavior for the first use of `'wildchar'` , +-- The second part for the second use, etc. +-- +-- Each part consists of a colon separated list consisting of the +-- following possible values: +-- "" Complete only the first match. +-- "full" Complete the next full match. After the last match, +-- the original string is used and then the first match +-- again. Will also start `'wildmenu'` if it is enabled. +-- "longest" Complete till longest common string. If this doesn't +-- result in a longer string, use the next part. +-- "list" When more than one match, list all matches. +-- "lastused" When completing buffer names and more than one buffer +-- matches, sort buffers by time last used (other than +-- the current buffer). +-- When there is only a single match, it is fully completed in all cases. +-- +-- Examples of useful colon-separated values: +-- "longest:full" Like "longest", but also start `'wildmenu'` if it is +-- enabled. Will not complete to the next full match. +-- "list:full" When more than one match, list all matches and +-- complete first match. +-- "list:longest" When more than one match, list all matches and +-- complete till longest common string. +-- "list:lastused" When more than one buffer matches, list all matches +-- and sort buffers by time last used (other than the +-- current buffer). +-- +-- Examples: > +-- :set wildmode=full +-- < Complete first full match, next match, etc. (the default) > +-- :set wildmode=longest,full +-- < Complete longest common string, then each full match > +-- :set wildmode=list:full +-- < List all matches and complete each full match > +-- :set wildmode=list,full +-- < List all matches without completing, then each full match > +-- :set wildmode=longest,list +-- < Complete longest common string, then list alternatives. +-- More info here: |cmdline-completion|. +--- @class vim.opt.wildmode: vim.Option,string[] +--- @operator add: vim.opt.wildmode +--- @operator sub: vim.opt.wildmode +--- @operator pow: vim.opt.wildmode +vim.opt.wildmode = "full" +vim.opt.wim = vim.opt.wildmode +--- @return string[] +function vim.opt.wildmode:get()end + +-- `'wildoptions'` `'wop'` string (default "pum,tagfile") +-- global +-- A list of words that change how |cmdline-completion| is done. +-- The following values are supported: +-- fuzzy Use |fuzzy-matching| to find completion matches. When +-- this value is specified, wildcard expansion will not +-- be used for completion. The matches will be sorted by +-- the "best match" rather than alphabetically sorted. +-- This will find more matches than the wildcard +-- expansion. Currently fuzzy matching based completion +-- is not supported for file and directory names and +-- instead wildcard expansion is used. +-- pum Display the completion matches using the popup menu +-- in the same style as the |ins-completion-menu|. +-- tagfile When using CTRL-D to list matching tags, the kind of +-- tag and the file of the tag is listed. Only one match +-- is displayed per line. Often used tag kinds are: +-- d #define +-- f function +--- @class vim.opt.wildoptions: vim.Option,string[] +--- @operator add: vim.opt.wildoptions +--- @operator sub: vim.opt.wildoptions +--- @operator pow: vim.opt.wildoptions +vim.opt.wildoptions = "pum,tagfile" +vim.opt.wop = vim.opt.wildoptions +--- @return string[] +function vim.opt.wildoptions:get()end + +-- `'winaltkeys'` `'wak'` string (default "menu") +-- global +-- {only used in Win32} +-- Some GUI versions allow the access to menu entries by using the ALT +-- key in combination with a character that appears underlined in the +-- menu. This conflicts with the use of the ALT key for mappings and +-- entering special characters. This option tells what to do: +-- no Don't use ALT keys for menus. ALT key combinations can be +-- mapped, but there is no automatic handling. +-- yes ALT key handling is done by the windowing system. ALT key +-- combinations cannot be mapped. +-- menu Using ALT in combination with a character that is a menu +-- shortcut key, will be handled by the windowing system. Other +-- keys can be mapped. +-- If the menu is disabled by excluding `'m'` from `'guioptions'` , the ALT +-- key is never used for the menu. +-- This option is not used for ; on Win32. +--- @class vim.opt.winaltkeys: vim.Option,string +--- @operator add: vim.opt.winaltkeys +--- @operator sub: vim.opt.winaltkeys +--- @operator pow: vim.opt.winaltkeys +vim.opt.winaltkeys = "menu" +vim.opt.wak = vim.opt.winaltkeys +--- @return string +function vim.opt.winaltkeys:get()end + +-- `'winbar'` `'wbr'` string (default empty) +-- global or local to window |global-local| +-- When non-empty, this option enables the window bar and determines its +-- contents. The window bar is a bar that's shown at the top of every +-- window with it enabled. The value of `'winbar'` is evaluated like with +-- `'statusline'` . +-- +-- When changing something that is used in `'winbar'` that does not trigger +-- it to be updated, use |:redrawstatus|. +-- +-- Floating windows do not use the global value of `'winbar'` . The +-- window-local value of `'winbar'` must be set for a floating window to +-- have a window bar. +-- +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +--- @class vim.opt.winbar: vim.Option,string +--- @operator add: vim.opt.winbar +--- @operator sub: vim.opt.winbar +--- @operator pow: vim.opt.winbar +vim.opt.winbar = "" +vim.opt.wbr = vim.opt.winbar +--- @return string +function vim.opt.winbar:get()end + +-- `'winblend'` `'winbl'` number (default 0) +-- local to window +-- Enables pseudo-transparency for a floating window. Valid values are in +-- the range of 0 for fully opaque window (disabled) to 100 for fully +-- transparent background. Values between 0-30 are typically most useful. +-- +-- UI-dependent. Works best with RGB colors. `'termguicolors'` +--- @class vim.opt.winblend: vim.Option,number +--- @operator add: vim.opt.winblend +--- @operator sub: vim.opt.winblend +--- @operator pow: vim.opt.winblend +vim.opt.winblend = 0 +vim.opt.winbl = vim.opt.winblend +--- @return number +function vim.opt.winblend:get()end + +-- `'window'` `'wi'` number (default screen height - 1) +-- global +-- Window height used for |CTRL-F| and |CTRL-B| when there is only one +-- window and the value is smaller than `'lines'` minus one. The screen +-- will scroll `'window'` minus two lines, with a minimum of one. +-- When `'window'` is equal to `'lines'` minus one CTRL-F and CTRL-B scroll +-- in a much smarter way, taking care of wrapping lines. +-- When resizing the Vim window, the value is smaller than 1 or more than +-- or equal to `'lines'` it will be set to `'lines'` minus 1. +-- Note: Do not confuse this with the height of the Vim window, use +-- `'lines'` for that. +--- @class vim.opt.window: vim.Option,number +--- @operator add: vim.opt.window +--- @operator sub: vim.opt.window +--- @operator pow: vim.opt.window +vim.opt.window = 23 +vim.opt.wi = vim.opt.window +--- @return number +function vim.opt.window:get()end + +-- `'winfixheight'` `'wfh'` boolean (default off) +-- local to window +-- Keep the window height when windows are opened or closed and +-- `'equalalways'` is set. Also for |CTRL-W_=|. Set by default for the +-- |preview-window| and |quickfix-window|. +-- The height may be changed anyway when running out of room. +--- @class vim.opt.winfixheight: vim.Option,boolean +--- @operator add: vim.opt.winfixheight +--- @operator sub: vim.opt.winfixheight +--- @operator pow: vim.opt.winfixheight +vim.opt.winfixheight = false +vim.opt.wfh = vim.opt.winfixheight +--- @return boolean +function vim.opt.winfixheight:get()end + +-- `'winfixwidth'` `'wfw'` boolean (default off) +-- local to window +-- Keep the window width when windows are opened or closed and +-- `'equalalways'` is set. Also for |CTRL-W_=|. +-- The width may be changed anyway when running out of room. +--- @class vim.opt.winfixwidth: vim.Option,boolean +--- @operator add: vim.opt.winfixwidth +--- @operator sub: vim.opt.winfixwidth +--- @operator pow: vim.opt.winfixwidth +vim.opt.winfixwidth = false +vim.opt.wfw = vim.opt.winfixwidth +--- @return boolean +function vim.opt.winfixwidth:get()end + +-- `'winheight'` `'wh'` number (default 1) +-- global +-- Minimal number of lines for the current window. This is not a hard +-- minimum, Vim will use fewer lines if there is not enough room. If the +-- focus goes to a window that is smaller, its size is increased, at the +-- cost of the height of other windows. +-- Set `'winheight'` to a small number for normal editing. +-- Set it to 999 to make the current window fill most of the screen. +-- Other windows will be only `'winminheight'` high. This has the drawback +-- that ":all" will create only two windows. To avoid "vim -o 1 2 3 4" +-- to create only two windows, set the option after startup is done, +-- using the |VimEnter| event: > +-- au VimEnter * set winheight=999 +-- < Minimum value is 1. +-- The height is not adjusted after one of the commands that change the +-- height of the current window. +-- `'winheight'` applies to the current window. Use `'winminheight'` to set +-- the minimal height for other windows. +--- @class vim.opt.winheight: vim.Option,number +--- @operator add: vim.opt.winheight +--- @operator sub: vim.opt.winheight +--- @operator pow: vim.opt.winheight +vim.opt.winheight = 1 +vim.opt.wh = vim.opt.winheight +--- @return number +function vim.opt.winheight:get()end + +-- `'winhighlight'` `'winhl'` string (default empty) +-- local to window +-- Window-local highlights. Comma-delimited list of highlight +-- |group-name| pairs "{hl-from}:{hl-to},..." where each {hl-from} is +-- a |highlight-groups| item to be overridden by {hl-to} group in +-- the window. +-- +-- Note: highlight namespaces take precedence over `'winhighlight'` . +-- See |nvim_win_set_hl_ns()| and |nvim_set_hl()|. +-- +-- Highlights of vertical separators are determined by the window to the +-- left of the separator. The `'tabline'` highlight of a tabpage is +-- decided by the last-focused window of the tabpage. Highlights of +-- the popupmenu are determined by the current window. Highlights in the +-- message area cannot be overridden. +-- +-- Example: show a different color for non-current windows: > +-- set winhighlight=Normal:MyNormal,NormalNC:MyNormalNC +-- < +--- @class vim.opt.winhighlight: vim.Option,table +--- @operator add: vim.opt.winhighlight +--- @operator sub: vim.opt.winhighlight +--- @operator pow: vim.opt.winhighlight +vim.opt.winhighlight = "" +vim.opt.winhl = vim.opt.winhighlight +--- @return table +function vim.opt.winhighlight:get()end + +-- `'winminheight'` `'wmh'` number (default 1) +-- global +-- The minimal height of a window, when it's not the current window. +-- This is a hard minimum, windows will never become smaller. +-- When set to zero, windows may be "squashed" to zero lines (i.e. just a +-- status bar) if necessary. They will return to at least one line when +-- they become active (since the cursor has to have somewhere to go.) +-- Use `'winheight'` to set the minimal height of the current window. +-- This option is only checked when making a window smaller. Don't use a +-- large number, it will cause errors when opening more than a few +-- windows. A value of 0 to 3 is reasonable. +--- @class vim.opt.winminheight: vim.Option,number +--- @operator add: vim.opt.winminheight +--- @operator sub: vim.opt.winminheight +--- @operator pow: vim.opt.winminheight +vim.opt.winminheight = 1 +vim.opt.wmh = vim.opt.winminheight +--- @return number +function vim.opt.winminheight:get()end + +-- `'winminwidth'` `'wmw'` number (default 1) +-- global +-- The minimal width of a window, when it's not the current window. +-- This is a hard minimum, windows will never become smaller. +-- When set to zero, windows may be "squashed" to zero columns (i.e. just +-- a vertical separator) if necessary. They will return to at least one +-- line when they become active (since the cursor has to have somewhere +-- to go.) +-- Use `'winwidth'` to set the minimal width of the current window. +-- This option is only checked when making a window smaller. Don't use a +-- large number, it will cause errors when opening more than a few +-- windows. A value of 0 to 12 is reasonable. +--- @class vim.opt.winminwidth: vim.Option,number +--- @operator add: vim.opt.winminwidth +--- @operator sub: vim.opt.winminwidth +--- @operator pow: vim.opt.winminwidth +vim.opt.winminwidth = 1 +vim.opt.wmw = vim.opt.winminwidth +--- @return number +function vim.opt.winminwidth:get()end + +-- `'winwidth'` `'wiw'` number (default 20) +-- global +-- Minimal number of columns for the current window. This is not a hard +-- minimum, Vim will use fewer columns if there is not enough room. If +-- the current window is smaller, its size is increased, at the cost of +-- the width of other windows. Set it to 999 to make the current window +-- always fill the screen. Set it to a small number for normal editing. +-- The width is not adjusted after one of the commands to change the +-- width of the current window. +-- `'winwidth'` applies to the current window. Use `'winminwidth'` to set +-- the minimal width for other windows. +--- @class vim.opt.winwidth: vim.Option,number +--- @operator add: vim.opt.winwidth +--- @operator sub: vim.opt.winwidth +--- @operator pow: vim.opt.winwidth +vim.opt.winwidth = 20 +vim.opt.wiw = vim.opt.winwidth +--- @return number +function vim.opt.winwidth:get()end + +-- `'wrap'` boolean (default on) +-- local to window +-- This option changes how text is displayed. It doesn't change the text +-- in the buffer, see `'textwidth'` for that. +-- When on, lines longer than the width of the window will wrap and +-- displaying continues on the next line. When off lines will not wrap +-- and only part of long lines will be displayed. When the cursor is +-- moved to a part that is not shown, the screen will scroll +-- horizontally. +-- The line will be broken in the middle of a word if necessary. See +-- `'linebreak'` to get the break at a word boundary. +-- To make scrolling horizontally a bit more useful, try this: > +-- :set sidescroll=5 +-- :set listchars+=precedes:<,extends:> +-- < See `'sidescroll'` , `'listchars'` and |wrap-off|. +-- This option can't be set from a |modeline| when the `'diff'` option is +-- on. +--- @class vim.opt.wrap: vim.Option,boolean +--- @operator add: vim.opt.wrap +--- @operator sub: vim.opt.wrap +--- @operator pow: vim.opt.wrap +vim.opt.wrap = true +--- @return boolean +function vim.opt.wrap:get()end + +-- `'wrapmargin'` `'wm'` number (default 0) +-- local to buffer +-- Number of characters from the right window border where wrapping +-- starts. When typing text beyond this limit, an will be inserted +-- and inserting continues on the next line. +-- Options that add a margin, such as `'number'` and `'foldcolumn'` , cause +-- the text width to be further reduced. +-- When `'textwidth'` is non-zero, this option is not used. +-- See also `'formatoptions'` and |ins-textwidth|. +--- @class vim.opt.wrapmargin: vim.Option,number +--- @operator add: vim.opt.wrapmargin +--- @operator sub: vim.opt.wrapmargin +--- @operator pow: vim.opt.wrapmargin +vim.opt.wrapmargin = 0 +vim.opt.wm = vim.opt.wrapmargin +--- @return number +function vim.opt.wrapmargin:get()end + +-- `'wrapscan'` `'ws'` boolean (default on) +-- global +-- Searches wrap around the end of the file. Also applies to |]s| and +-- |[s|, searching for spelling mistakes. +--- @class vim.opt.wrapscan: vim.Option,boolean +--- @operator add: vim.opt.wrapscan +--- @operator sub: vim.opt.wrapscan +--- @operator pow: vim.opt.wrapscan +vim.opt.wrapscan = true +vim.opt.ws = vim.opt.wrapscan +--- @return boolean +function vim.opt.wrapscan:get()end + +-- `'write'` boolean (default on) +-- global +-- Allows writing files. When not set, writing a file is not allowed. +-- Can be used for a view-only mode, where modifications to the text are +-- still allowed. Can be reset with the |-m| or |-M| command line +-- argument. Filtering text is still possible, even though this requires +-- writing a temporary file. +--- @class vim.opt.write: vim.Option,boolean +--- @operator add: vim.opt.write +--- @operator sub: vim.opt.write +--- @operator pow: vim.opt.write +vim.opt.write = true +--- @return boolean +function vim.opt.write:get()end + +-- `'writeany'` `'wa'` boolean (default off) +-- global +-- Allows writing to any file with no need for "!" override. +--- @class vim.opt.writeany: vim.Option,boolean +--- @operator add: vim.opt.writeany +--- @operator sub: vim.opt.writeany +--- @operator pow: vim.opt.writeany +vim.opt.writeany = false +vim.opt.wa = vim.opt.writeany +--- @return boolean +function vim.opt.writeany:get()end + +-- `'writebackup'` `'wb'` boolean (default on) +-- global +-- Make a backup before overwriting a file. The backup is removed after +-- the file was successfully written, unless the `'backup'` option is +-- also on. +-- WARNING: Switching this option off means that when Vim fails to write +-- your buffer correctly and then, for whatever reason, Vim exits, you +-- lose both the original file and what you were writing. Only reset +-- this option if your file system is almost full and it makes the write +-- fail (and make sure not to exit Vim until the write was successful). +-- See |backup-table| for another explanation. +-- When the `'backupskip'` pattern matches, a backup is not made anyway. +-- Depending on `'backupcopy'` the backup is a new file or the original +-- file renamed (and a new file is written). +--- @class vim.opt.writebackup: vim.Option,boolean +--- @operator add: vim.opt.writebackup +--- @operator sub: vim.opt.writebackup +--- @operator pow: vim.opt.writebackup +vim.opt.writebackup = true +vim.opt.wb = vim.opt.writebackup +--- @return boolean +function vim.opt.writebackup:get()end + +--- @class vim.opt.writedelay: vim.Option,number +--- @operator add: vim.opt.writedelay +--- @operator sub: vim.opt.writedelay +--- @operator pow: vim.opt.writedelay +vim.opt.writedelay = 0 +vim.opt.wd = vim.opt.writedelay +--- @return number +function vim.opt.writedelay:get()end + +---@type table +vim.go = {} + +---@type table +vim.bo = {} + +---@type table +vim.wo = {} + +---@type vim.go | vim.wo | vim.bo +vim.o = {} + +---@class vim.opt +vim.opt = {} + +---@type vim.opt +vim.opt_global = {} + +---@type vim.opt +vim.opt_local = {} + +---@class vim.Option +local Option = {} + +function Option:append(right) end +function Option:prepend(right) end +function Option:remove(right) end + diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.lua new file mode 100644 index 00000000..4e9bc771 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/options.lua @@ -0,0 +1,4300 @@ +---@meta + +---@class vim.go +vim.go = {} + +vim.go.aleph = 224 +vim.go.al = vim.go.aleph +-- `'allowrevins'` `'ari'` boolean (default off) +-- global +-- Allow CTRL-_ in Insert and Command-line mode. This is default off, to +-- avoid that users that accidentally type CTRL-_ instead of SHIFT-_ get +-- into reverse Insert mode, and don't know how to get out. See +-- `'revins'` . +vim.go.allowrevins = false +vim.go.ari = vim.go.allowrevins +-- `'ambiwidth'` `'ambw'` string (default: "single") +-- global +-- Tells Vim what to do with characters with East Asian Width Class +-- Ambiguous (such as Euro, Registered Sign, Copyright Sign, Greek +-- letters, Cyrillic letters). +-- +-- There are currently two possible values: +-- "single": Use the same width as characters in US-ASCII. This is +-- expected by most users. +-- "double": Use twice the width of ASCII characters. +-- +-- The value "double" cannot be used if `'listchars'` or `'fillchars'` +-- contains a character that would be double width. These errors may +-- also be given when calling setcellwidths(). +-- +-- The values are overruled for characters specified with +-- |setcellwidths()|. +-- +-- There are a number of CJK fonts for which the width of glyphs for +-- those characters are solely based on how many octets they take in +-- legacy/traditional CJK encodings. In those encodings, Euro, +-- Registered sign, Greek/Cyrillic letters are represented by two octets, +-- therefore those fonts have "wide" glyphs for them. This is also +-- true of some line drawing characters used to make tables in text +-- file. Therefore, when a CJK font is used for GUI Vim or +-- Vim is running inside a terminal (emulators) that uses a CJK font +-- (or Vim is run inside an xterm invoked with "-cjkwidth" option.), +-- this option should be set to "double" to match the width perceived +-- by Vim with the width of glyphs in the font. Perhaps it also has +-- to be set to "double" under CJK MS-Windows when the system locale is +-- set to one of CJK locales. See Unicode Standard Annex #11 +-- (https://www.unicode.org/reports/tr11). +vim.go.ambiwidth = "single" +vim.go.ambw = vim.go.ambiwidth +-- `'arabicshape'` `'arshape'` boolean (default on) +-- global +-- When on and `'termbidi'` is off, the required visual character +-- corrections that need to take place for displaying the Arabic language +-- take effect. Shaping, in essence, gets enabled; the term is a broad +-- one which encompasses: +-- a) the changing/morphing of characters based on their location +-- within a word (initial, medial, final and stand-alone). +-- b) the enabling of the ability to compose characters +-- c) the enabling of the required combining of some characters +-- When disabled the display shows each character's true stand-alone +-- form. +-- Arabic is a complex language which requires other settings, for +-- further details see |arabic.txt|. +vim.go.arabicshape = true +vim.go.arshape = vim.go.arabicshape +-- `'autochdir'` `'acd'` boolean (default off) +-- global +-- When on, Vim will change the current working directory whenever you +-- open a file, switch buffers, delete a buffer or open/close a window. +-- It will change to the directory containing the file which was opened +-- or selected. When a buffer has no name it also has no directory, thus +-- the current directory won't change when navigating to it. +-- Note: When this option is on some plugins may not work. +vim.go.autochdir = false +vim.go.acd = vim.go.autochdir +-- `'autowrite'` `'aw'` boolean (default off) +-- global +-- Write the contents of the file, if it has been modified, on each +-- `:next`, `:rewind`, `:last`, `:first`, `:previous`, `:stop`, +-- `:suspend`, `:tag`, `:!`, `:make`, CTRL-] and CTRL-^ command; and when +-- a `:buffer`, CTRL-O, CTRL-I, '{A-Z0-9}, or `{A-Z0-9} command takes one +-- to another file. +-- A buffer is not written if it becomes hidden, e.g. when `'bufhidden'` is +-- set to "hide" and `:next` is used. +-- Note that for some commands the `'autowrite'` option is not used, see +-- `'autowriteall'` for that. +-- Some buffers will not be written, specifically when `'buftype'` is +-- "nowrite", "nofile", "terminal" or "prompt". +vim.go.autowrite = false +vim.go.aw = vim.go.autowrite +-- `'autowriteall'` `'awa'` boolean (default off) +-- global +-- Like `'autowrite'` , but also used for commands ":edit", ":enew", ":quit", +-- ":qall", ":exit", ":xit", ":recover" and closing the Vim window. +-- Setting this option also implies that Vim behaves like `'autowrite'` has +-- been set. +vim.go.autowriteall = false +vim.go.awa = vim.go.autowriteall +-- `'background'` `'bg'` string (default "dark") +-- global +-- When set to "dark" or "light", adjusts the default color groups for +-- that background type. The |TUI| or other UI sets this on startup +-- (triggering |OptionSet|) if it can detect the background color. +-- +-- This option does NOT change the background color, it tells Nvim what +-- the "inherited" (terminal/GUI) background looks like. +-- See |:hi-normal| if you want to set the background color explicitly. +-- +-- When a color scheme is loaded (the "g:colors_name" variable is set) +-- setting `'background'` will cause the color scheme to be reloaded. If +-- the color scheme adjusts to the value of `'background'` this will work. +-- However, if the color scheme sets `'background'` itself the effect may +-- be undone. First delete the "g:colors_name" variable when needed. +-- +-- Normally this option would be set in the vimrc file. Possibly +-- depending on the terminal name. Example: > +-- :if $TERM ==# "xterm" +-- : set background=dark +-- :endif +-- < When this option is set, the default settings for the highlight groups +-- will change. To use other settings, place ":highlight" commands AFTER +-- the setting of the `'background'` option. +-- This option is also used in the "$VIMRUNTIME/syntax/syntax.vim" file +-- to select the colors for syntax highlighting. After changing this +-- option, you must load syntax.vim again to see the result. This can be +-- done with ":syntax on". +vim.go.background = "dark" +vim.go.bg = vim.go.background +-- `'backspace'` `'bs'` string (default "indent,eol,start") +-- global +-- Influences the working of , , CTRL-W and CTRL-U in Insert +-- mode. This is a list of items, separated by commas. Each item allows +-- a way to backspace over something: +-- value effect ~ +-- indent allow backspacing over autoindent +-- eol allow backspacing over line breaks (join lines) +-- start allow backspacing over the start of insert; CTRL-W and CTRL-U +-- stop once at the start of insert. +-- nostop like start, except CTRL-W and CTRL-U do not stop at the start of +-- insert. +-- +-- When the value is empty, Vi compatible backspacing is used, none of +-- the ways mentioned for the items above are possible. +-- +-- For backwards compatibility with version 5.4 and earlier: +-- value effect ~ +-- 0 same as ":set backspace=" (Vi compatible) +-- 1 same as ":set backspace=indent,eol" +-- 2 same as ":set backspace=indent,eol,start" +-- 3 same as ":set backspace=indent,eol,nostop" +vim.go.backspace = "indent,eol,start" +vim.go.bs = vim.go.backspace +-- `'backup'` `'bk'` boolean (default off) +-- global +-- Make a backup before overwriting a file. Leave it around after the +-- file has been successfully written. If you do not want to keep the +-- backup file, but you do want a backup while the file is being +-- written, reset this option and set the `'writebackup'` option (this is +-- the default). If you do not want a backup file at all reset both +-- options (use this if your file system is almost full). See the +-- |backup-table| for more explanations. +-- When the `'backupskip'` pattern matches, a backup is not made anyway. +-- When `'patchmode'` is set, the backup may be renamed to become the +-- oldest version of a file. +vim.go.backup = false +vim.go.bk = vim.go.backup +-- `'backupdir'` `'bdir'` string (default ".,$XDG_STATE_HOME/nvim/backup//") +-- global +-- List of directories for the backup file, separated with commas. +-- - The backup file will be created in the first directory in the list +-- where this is possible. If none of the directories exist Nvim will +-- attempt to create the last directory in the list. +-- - Empty means that no backup file will be created (`'patchmode'` is +-- impossible!). Writing may fail because of this. +-- - A directory "." means to put the backup file in the same directory +-- as the edited file. +-- - A directory starting with "./" (or ".\" for MS-Windows) means to put +-- the backup file relative to where the edited file is. The leading +-- "." is replaced with the path name of the edited file. +-- ("." inside a directory name has no special meaning). +-- - Spaces after the comma are ignored, other spaces are considered part +-- of the directory name. To have a space at the start of a directory +-- name, precede it with a backslash. +-- - To include a comma in a directory name precede it with a backslash. +-- - A directory name may end in an `'/'` . +-- - For Unix and Win32, if a directory ends in two path separators "//", +-- the swap file name will be built from the complete path to the file +-- with all path separators changed to percent `'%'` signs. This will +-- ensure file name uniqueness in the backup directory. +-- On Win32, it is also possible to end with "\\". However, When a +-- separating comma is following, you must use "//", since "\\" will +-- include the comma in the file name. Therefore it is recommended to +-- use `'//'` , instead of `'\\'` . +-- - Environment variables are expanded |:set_env|. +-- - Careful with `'\'` characters, type one before a space, type two to +-- get one in the option (see |option-backslash|), for example: > +-- :set bdir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces +-- < - For backwards compatibility with Vim version 3.0 a `'>'` at the start +-- of the option is removed. +-- See also `'backup'` and `'writebackup'` options. +-- If you want to hide your backup files on Unix, consider this value: > +-- :set backupdir=./.backup,~/.backup,.,/tmp +-- < You must create a ".backup" directory in each directory and in your +-- home directory for this to work properly. +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- directories from the list. This avoids problems when a future version +-- uses another default. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.backupdir = ".,/home/runner/.local/state/nvim/backup//" +vim.go.bdir = vim.go.backupdir +-- `'backupext'` `'bex'` string (default "~") +-- global +-- String which is appended to a file name to make the name of the +-- backup file. The default is quite unusual, because this avoids +-- accidentally overwriting existing files with a backup file. You might +-- prefer using ".bak", but make sure that you don't have files with +-- ".bak" that you want to keep. +-- Only normal file name characters can be used; "/\*?[|<>" are illegal. +-- +-- If you like to keep a lot of backups, you could use a BufWritePre +-- autocommand to change `'backupext'` just before writing the file to +-- include a timestamp. > +-- :au BufWritePre * let &bex = `'-'` .. strftime("%Y%b%d%X") .. `'~'` +-- < Use `'backupdir'` to put the backup in a different directory. +vim.go.backupext = "~" +vim.go.bex = vim.go.backupext +-- `'backupskip'` `'bsk'` string (default: "$TMPDIR/,$TEMP/*" +-- Unix: "/tmp/,$TMP/" +-- Mac: "/private/tmp/,$TMP/") +-- global +-- A list of file patterns. When one of the patterns matches with the +-- name of the file which is written, no backup file is created. Both +-- the specified file name and the full path name of the file are used. +-- The pattern is used like with |:autocmd|, see |autocmd-pattern|. +-- Watch out for special characters, see |option-backslash|. +-- When $TMPDIR, $TMP or $TEMP is not defined, it is not used for the +-- default value. "/tmp/*" is only used for Unix. +-- +-- WARNING: Not having a backup file means that when Vim fails to write +-- your buffer correctly and then, for whatever reason, Vim exits, you +-- lose both the original file and what you were writing. Only disable +-- backups if you don't care about losing the file. +-- +-- Note that environment variables are not expanded. If you want to use +-- $HOME you must expand it explicitly, e.g.: > +-- :let &backupskip = escape(expand(`'$HOME'` ), `'\'` ) .. `'/tmp/*'` +-- +-- < Note that the default also makes sure that "crontab -e" works (when a +-- backup would be made by renaming the original file crontab won't see +-- the newly created file). Also see `'backupcopy'` and |crontab|. +vim.go.backupskip = "/tmp/*" +vim.go.bsk = vim.go.backupskip +-- `'belloff'` `'bo'` string (default "all") +-- global +-- Specifies for which events the bell will not be rung. It is a comma- +-- separated list of items. For each item that is present, the bell +-- will be silenced. This is most useful to specify specific events in +-- insert mode to be silenced. +-- +-- item meaning when present ~ +-- all All events. +-- backspace When hitting or and deleting results in an +-- error. +-- cursor Fail to move around using the cursor keys or +-- / in |Insert-mode|. +-- complete Error occurred when using |i_CTRL-X_CTRL-K| or +-- |i_CTRL-X_CTRL-T|. +-- copy Cannot copy char from insert mode using |i_CTRL-Y| or +-- |i_CTRL-E|. +-- ctrlg Unknown Char after in Insert mode. +-- error Other Error occurred (e.g. try to join last line) +-- (mostly used in |Normal-mode| or |Cmdline-mode|). +-- esc hitting in |Normal-mode|. +-- hangul Ignored. +-- lang Calling the beep module for Lua/Mzscheme/TCL. +-- mess No output available for |g<|. +-- showmatch Error occurred for `'showmatch'` function. +-- operator Empty region error |cpo-E|. +-- register Unknown register after in |Insert-mode|. +-- shell Bell from shell output |:!|. +-- spell Error happened on spell suggest. +-- wildmode More matches in |cmdline-completion| available +-- (depends on the `'wildmode'` setting). +-- +-- This is most useful to fine tune when in Insert mode the bell should +-- be rung. For Normal mode and Ex commands, the bell is often rung to +-- indicate that an error occurred. It can be silenced by adding the +-- "error" keyword. +vim.go.belloff = "all" +vim.go.bo = vim.go.belloff +-- `'breakat'` `'brk'` string (default " ^I!@*-+;:,./?") +-- global +-- This option lets you choose which characters might cause a line +-- break if `'linebreak'` is on. Only works for ASCII characters. +vim.go.breakat = " \t!@*-+;:,./?" +vim.go.brk = vim.go.breakat +-- `'browsedir'` `'bsdir'` string (default: "last") +-- global +-- Which directory to use for the file browser: +-- last Use same directory as with last file browser, where a +-- file was opened or saved. +-- buffer Use the directory of the related buffer. +-- current Use the current directory. +-- {path} Use the specified directory +vim.go.browsedir = "" +vim.go.bsdir = vim.go.browsedir +-- `'casemap'` `'cmp'` string (default: "internal,keepascii") +-- global +-- Specifies details about changing the case of letters. It may contain +-- these words, separated by a comma: +-- internal Use internal case mapping functions, the current +-- locale does not change the case mapping. When +-- "internal" is omitted, the towupper() and towlower() +-- system library functions are used when available. +-- keepascii For the ASCII characters (0x00 to 0x7f) use the US +-- case mapping, the current locale is not effective. +-- This probably only matters for Turkish. +vim.go.casemap = "internal,keepascii" +vim.go.cmp = vim.go.casemap +-- `'cdhome'` `'cdh'` boolean (default: off) +-- global +-- When on, |:cd|, |:tcd| and |:lcd| without an argument changes the +-- current working directory to the |$HOME| directory like in Unix. +-- When off, those commands just print the current directory name. +-- On Unix this option has no effect. +vim.go.cdhome = false +vim.go.cdh = vim.go.cdhome +-- `'cdpath'` `'cd'` string (default: equivalent to $CDPATH or ",,") +-- global +-- This is a list of directories which will be searched when using the +-- |:cd|, |:tcd| and |:lcd| commands, provided that the directory being +-- searched for has a relative path, not an absolute part starting with +-- "/", "./" or "../", the `'cdpath'` option is not used then. +-- The `'cdpath'` option's value has the same form and semantics as +-- |`'path'` |. Also see |file-searching|. +-- The default value is taken from $CDPATH, with a "," prepended to look +-- in the current directory first. +-- If the default value taken from $CDPATH is not what you want, include +-- a modified version of the following command in your vimrc file to +-- override it: > +-- :let &cdpath = `','` .. substitute(substitute($CDPATH, '[, ]', `'\\\0'` , `'g'` ), `':'` , `','` , `'g'` ) +-- < This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +-- (parts of `'cdpath'` can be passed to the shell to expand file names). +vim.go.cdpath = ",," +vim.go.cd = vim.go.cdpath +-- `'cedit'` string (default: CTRL-F) +-- global +-- The key used in Command-line Mode to open the command-line window. +-- Only non-printable keys are allowed. +-- The key can be specified as a single character, but it is difficult to +-- type. The preferred way is to use the <> notation. Examples: > +-- :exe "set cedit=\" +-- :exe "set cedit=\" +-- < |Nvi| also has this option, but it only uses the first character. +-- See |cmdwin|. +vim.go.cedit = "\6" +-- `'charconvert'` `'ccv'` string (default "") +-- global +-- An expression that is used for character encoding conversion. It is +-- evaluated when a file that is to be read or has been written has a +-- different encoding from what is desired. +-- `'charconvert'` is not used when the internal iconv() function is +-- supported and is able to do the conversion. Using iconv() is +-- preferred, because it is much faster. +-- `'charconvert'` is not used when reading stdin |--|, because there is no +-- file to convert from. You will have to save the text in a file first. +-- The expression must return zero, false or an empty string for success, +-- non-zero or true for failure. +-- See |encoding-names| for possible encoding names. +-- Additionally, names given in `'fileencodings'` and `'fileencoding'` are +-- used. +-- Conversion between "latin1", "unicode", "ucs-2", "ucs-4" and "utf-8" +-- is done internally by Vim, `'charconvert'` is not used for this. +-- Also used for Unicode conversion. +-- Example: > +-- set charconvert=CharConvert() +-- fun CharConvert() +-- system("recode " +-- \ .. v:charconvert_from .. ".." .. v:charconvert_to +-- \ .. " <" .. v:fname_in .. " >" .. v:fname_out) +-- return v:shell_error +-- endfun +-- < The related Vim variables are: +-- v:charconvert_from name of the current encoding +-- v:charconvert_to name of the desired encoding +-- v:fname_in name of the input file +-- v:fname_out name of the output file +-- Note that v:fname_in and v:fname_out will never be the same. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.charconvert = "" +vim.go.ccv = vim.go.charconvert +-- `'clipboard'` `'cb'` string (default "") +-- global +-- This option is a list of comma-separated names. +-- These names are recognized: +-- +-- +-- unnamed When included, Vim will use the clipboard register "*" +-- for all yank, delete, change and put operations which +-- would normally go to the unnamed register. When a +-- register is explicitly specified, it will always be +-- used regardless of whether "unnamed" is in `'clipboard'` +-- or not. The clipboard register can always be +-- explicitly accessed using the "* notation. Also see +-- |clipboard|. +-- +-- +-- unnamedplus A variant of the "unnamed" flag which uses the +-- clipboard register "+" (|quoteplus|) instead of +-- register "*" for all yank, delete, change and put +-- operations which would normally go to the unnamed +-- register. When "unnamed" is also included to the +-- option, yank and delete operations (but not put) +-- will additionally copy the text into register +-- `'*'` . See |clipboard|. +vim.go.clipboard = "" +vim.go.cb = vim.go.clipboard +-- `'cmdheight'` `'ch'` number (default 1) +-- global or local to tab page +-- Number of screen lines to use for the command-line. Helps avoiding +-- |hit-enter| prompts. +-- The value of this option is stored with the tab page, so that each tab +-- page can have a different value. +-- +-- When `'cmdheight'` is zero, there is no command-line unless it is being +-- used. The command-line will cover the last line of the screen when +-- shown. +-- +-- WARNING: `cmdheight=0` is considered experimental. Expect some +-- unwanted behaviour. Some `'shortmess'` flags and similar +-- mechanism might fail to take effect, causing unwanted hit-enter +-- prompts. Some informative messages, both from Nvim itself and +-- plugins, will not be displayed. +vim.go.cmdheight = 1 +vim.go.ch = vim.go.cmdheight +-- `'cmdwinheight'` `'cwh'` number (default 7) +-- global +-- Number of screen lines to use for the command-line window. |cmdwin| +vim.go.cmdwinheight = 7 +vim.go.cwh = vim.go.cmdwinheight +-- `'columns'` `'co'` number (default 80 or terminal width) +-- global +-- Number of columns of the screen. Normally this is set by the terminal +-- initialization and does not have to be set by hand. +-- When Vim is running in the GUI or in a resizable window, setting this +-- option will cause the window size to be changed. When you only want +-- to use the size for the GUI, put the command in your |ginit.vim| file. +-- When you set this option and Vim is unable to change the physical +-- number of columns of the display, the display may be messed up. For +-- the GUI it is always possible and Vim limits the number of columns to +-- what fits on the screen. You can use this command to get the widest +-- window possible: > +-- :set columns=9999 +-- < Minimum value is 12, maximum value is 10000. +vim.go.columns = 80 +vim.go.co = vim.go.columns +vim.go.compatible = false +vim.go.cp = vim.go.compatible +-- `'completeopt'` `'cot'` string (default: "menu,preview") +-- global +-- A comma-separated list of options for Insert mode completion +-- |ins-completion|. The supported values are: +-- +-- menu Use a popup menu to show the possible completions. The +-- menu is only shown when there is more than one match and +-- sufficient colors are available. |ins-completion-menu| +-- +-- menuone Use the popup menu also when there is only one match. +-- Useful when there is additional information about the +-- match, e.g., what file it comes from. +-- +-- longest Only insert the longest common text of the matches. If +-- the menu is displayed you can use CTRL-L to add more +-- characters. Whether case is ignored depends on the kind +-- of completion. For buffer text the `'ignorecase'` option is +-- used. +-- +-- preview Show extra information about the currently selected +-- completion in the preview window. Only works in +-- combination with "menu" or "menuone". +-- +-- noinsert Do not insert any text for a match until the user selects +-- a match from the menu. Only works in combination with +-- "menu" or "menuone". No effect if "longest" is present. +-- +-- noselect Do not select a match in the menu, force the user to +-- select one from the menu. Only works in combination with +-- "menu" or "menuone". +vim.go.completeopt = "menu,preview" +vim.go.cot = vim.go.completeopt +-- `'completeslash'` `'csl'` string (default: "") +-- local to buffer +-- {only for MS-Windows} +-- When this option is set it overrules `'shellslash'` for completion: +-- - When this option is set to "slash", a forward slash is used for path +-- completion in insert mode. This is useful when editing HTML tag, or +-- Makefile with `'noshellslash'` on MS-Windows. +-- - When this option is set to "backslash", backslash is used. This is +-- useful when editing a batch file with `'shellslash'` set on MS-Windows. +-- - When this option is empty, same character is used as for +-- `'shellslash'` . +-- For Insert mode completion the buffer-local value is used. For +-- command line completion the global value is used. +vim.go.completeslash = "" +vim.go.csl = vim.go.completeslash +-- `'confirm'` `'cf'` boolean (default off) +-- global +-- When `'confirm'` is on, certain operations that would normally +-- fail because of unsaved changes to a buffer, e.g. ":q" and ":e", +-- instead raise a dialog asking if you wish to save the current +-- file(s). You can still use a ! to unconditionally |abandon| a buffer. +-- If `'confirm'` is off you can still activate confirmation for one +-- command only (this is most useful in mappings) with the |:confirm| +-- command. +-- Also see the |confirm()| function and the `'v'` flag in `'guioptions'` . +vim.go.confirm = false +vim.go.cf = vim.go.confirm +-- `'cpoptions'` `'cpo'` string (default: "aABceFs_") +-- global +-- A sequence of single character flags. When a character is present +-- this indicates Vi-compatible behavior. This is used for things where +-- not being Vi-compatible is mostly or sometimes preferred. +-- `'cpoptions'` stands for "compatible-options". +-- Commas can be added for readability. +-- To avoid problems with flags that are added in the future, use the +-- "+=" and "-=" feature of ":set" |add-option-flags|. +-- +-- contains behavior ~ +-- +-- a When included, a ":read" command with a file name +-- argument will set the alternate file name for the +-- current window. +-- +-- A When included, a ":write" command with a file name +-- argument will set the alternate file name for the +-- current window. +-- +-- b "\|" in a ":map" command is recognized as the end of +-- the map command. The `'\'` is included in the mapping, +-- the text after the `'|'` is interpreted as the next +-- command. Use a CTRL-V instead of a backslash to +-- include the `'|'` in the mapping. Applies to all +-- mapping, abbreviation, menu and autocmd commands. +-- See also |map_bar|. +-- +-- B A backslash has no special meaning in mappings, +-- abbreviations, user commands and the "to" part of the +-- menu commands. Remove this flag to be able to use a +-- backslash like a CTRL-V. For example, the command +-- ":map X \" results in X being mapped to: +-- `'B'` included: "\^[" (^[ is a real ) +-- `'B'` excluded: "" (5 characters) +-- +-- c Searching continues at the end of any match at the +-- cursor position, but not further than the start of the +-- next line. When not present searching continues +-- one character from the cursor position. With `'c'` +-- "abababababab" only gets three matches when repeating +-- "/abab", without `'c'` there are five matches. +-- +-- C Do not concatenate sourced lines that start with a +-- backslash. See |line-continuation|. +-- +-- d Using "./" in the `'tags'` option doesn't mean to use +-- the tags file relative to the current file, but the +-- tags file in the current directory. +-- +-- D Can't use CTRL-K to enter a digraph after Normal mode +-- commands with a character argument, like |r|, |f| and +-- |t|. +-- +-- e When executing a register with ":@r", always add a +-- to the last line, also when the register is not +-- linewise. If this flag is not present, the register +-- is not linewise and the last line does not end in a +-- , then the last line is put on the command-line +-- and can be edited before hitting . +-- +-- E It is an error when using "y", "d", "c", "g~", "gu" or +-- "gU" on an Empty region. The operators only work when +-- at least one character is to be operated on. Example: +-- This makes "y0" fail in the first column. +-- +-- f When included, a ":read" command with a file name +-- argument will set the file name for the current buffer, +-- if the current buffer doesn't have a file name yet. +-- +-- F When included, a ":write" command with a file name +-- argument will set the file name for the current +-- buffer, if the current buffer doesn't have a file name +-- yet. Also see |cpo-P|. +-- +-- i When included, interrupting the reading of a file will +-- leave it modified. +-- +-- I When moving the cursor up or down just after inserting +-- indent for `'autoindent'` , do not delete the indent. +-- +-- J A |sentence| has to be followed by two spaces after +-- the `'.'` , `'!'` or `'?'` . A is not recognized as +-- white space. +-- +-- K Don't wait for a key code to complete when it is +-- halfway through a mapping. This breaks mapping +-- when only part of the second has been +-- read. It enables cancelling the mapping by typing +-- . +-- +-- l Backslash in a [] range in a search pattern is taken +-- literally, only "\]", "\^", "\-" and "\\" are special. +-- See |/[]| +-- `'l'` included: "/[ \t]" finds , `'\'` and `'t'` +-- `'l'` excluded: "/[ \t]" finds and +-- +-- L When the `'list'` option is set, `'wrapmargin'` , +-- `'textwidth'` , `'softtabstop'` and Virtual Replace mode +-- (see |gR|) count a as two characters, instead of +-- the normal behavior of a . +-- +-- m When included, a showmatch will always wait half a +-- second. When not included, a showmatch will wait half +-- a second or until a character is typed. |`'showmatch'` | +-- +-- M When excluded, "%" matching will take backslashes into +-- account. Thus in "( \( )" and "\( ( \)" the outer +-- parenthesis match. When included "%" ignores +-- backslashes, which is Vi compatible. +-- +-- n When included, the column used for `'number'` and +-- `'relativenumber'` will also be used for text of wrapped +-- lines. +-- +-- o Line offset to search command is not remembered for +-- next search. +-- +-- O Don't complain if a file is being overwritten, even +-- when it didn't exist when editing it. This is a +-- protection against a file unexpectedly created by +-- someone else. Vi didn't complain about this. +-- +-- p Vi compatible Lisp indenting. When not present, a +-- slightly better algorithm is used. +-- +-- P When included, a ":write" command that appends to a +-- file will set the file name for the current buffer, if +-- the current buffer doesn't have a file name yet and +-- the `'F'` flag is also included |cpo-F|. +-- +-- q When joining multiple lines leave the cursor at the +-- position where it would be when joining two lines. +-- +-- r Redo ("." command) uses "/" to repeat a search +-- command, instead of the actually used search string. +-- +-- R Remove marks from filtered lines. Without this flag +-- marks are kept like |:keepmarks| was used. +-- +-- s Set buffer options when entering the buffer for the +-- first time. This is like it is in Vim version 3.0. +-- And it is the default. If not present the options are +-- set when the buffer is created. +-- +-- S Set buffer options always when entering a buffer +-- (except `'readonly'` , `'fileformat'` , `'filetype'` and +-- `'syntax'` ). This is the (most) Vi compatible setting. +-- The options are set to the values in the current +-- buffer. When you change an option and go to another +-- buffer, the value is copied. Effectively makes the +-- buffer options global to all buffers. +-- +-- `'s'` `'S'` copy buffer options +-- no no when buffer created +-- yes no when buffer first entered (default) +-- X yes each time when buffer entered (vi comp.) +-- +-- t Search pattern for the tag command is remembered for +-- "n" command. Otherwise Vim only puts the pattern in +-- the history for search pattern, but doesn't change the +-- last used search pattern. +-- +-- u Undo is Vi compatible. See |undo-two-ways|. +-- +-- v Backspaced characters remain visible on the screen in +-- Insert mode. Without this flag the characters are +-- erased from the screen right away. With this flag the +-- screen newly typed text overwrites backspaced +-- characters. +-- +-- W Don't overwrite a readonly file. When omitted, ":w!" +-- overwrites a readonly file, if possible. +-- +-- x on the command-line executes the command-line. +-- The default in Vim is to abandon the command-line, +-- because normally aborts a command. |c_| +-- +-- X When using a count with "R" the replaced text is +-- deleted only once. Also when repeating "R" with "." +-- and a count. +-- +-- y A yank command can be redone with ".". Think twice if +-- you really want to use this, it may break some +-- plugins, since most people expect "." to only repeat a +-- change. +-- +-- Z When using "w!" while the `'readonly'` option is set, +-- don't reset `'readonly'` . +-- +-- ! When redoing a filter command, use the last used +-- external command, whatever it was. Otherwise the last +-- used -filter- command is used. +-- +-- $ When making a change to one line, don't redisplay the +-- line, but put a `'$'` at the end of the changed text. +-- The changed text will be overwritten when you type the +-- new text. The line is redisplayed if you type any +-- command that moves the cursor from the insertion +-- point. +-- +-- % Vi-compatible matching is done for the "%" command. +-- Does not recognize "#if", "#endif", etc. +-- Does not recognize "/*" and "*/". +-- Parens inside single and double quotes are also +-- counted, causing a string that contains a paren to +-- disturb the matching. For example, in a line like +-- "if (strcmp("foo(", s))" the first paren does not +-- match the last one. When this flag is not included, +-- parens inside single and double quotes are treated +-- specially. When matching a paren outside of quotes, +-- everything inside quotes is ignored. When matching a +-- paren inside quotes, it will find the matching one (if +-- there is one). This works very well for C programs. +-- This flag is also used for other features, such as +-- C-indenting. +-- +-- + When included, a ":write file" command will reset the +-- `'modified'` flag of the buffer, even though the buffer +-- itself may still be different from its file. +-- +-- > When appending to a register, put a line break before +-- the appended text. +-- +-- ; When using |,| or |;| to repeat the last |t| search +-- and the cursor is right in front of the searched +-- character, the cursor won't move. When not included, +-- the cursor would skip over it and jump to the +-- following occurrence. +-- +-- _ When using |cw| on a word, do not include the +-- whitespace following the word in the motion. +vim.go.cpoptions = "aABceFs_" +vim.go.cpo = vim.go.cpoptions +-- `'debug'` string (default "") +-- global +-- These values can be used: +-- msg Error messages that would otherwise be omitted will be given +-- anyway. +-- throw Error messages that would otherwise be omitted will be given +-- anyway and also throw an exception and set |v:errmsg|. +-- beep A message will be given when otherwise only a beep would be +-- produced. +-- The values can be combined, separated by a comma. +-- "msg" and "throw" are useful for debugging `'foldexpr'` , `'formatexpr'` or +-- `'indentexpr'` . +vim.go.debug = "" +-- `'delcombine'` `'deco'` boolean (default off) +-- global +-- If editing Unicode and this option is set, backspace and Normal mode +-- "x" delete each combining character on its own. When it is off (the +-- default) the character along with its combining characters are +-- deleted. +-- Note: When `'delcombine'` is set "xx" may work differently from "2x"! +-- +-- This is useful for Arabic, Hebrew and many other languages where one +-- may have combining characters overtop of base characters, and want +-- to remove only the combining ones. +vim.go.delcombine = false +vim.go.deco = vim.go.delcombine +-- `'diffexpr'` `'dex'` string (default "") +-- global +-- Expression which is evaluated to obtain a diff file (either ed-style +-- or unified-style) from two versions of a file. See |diff-diffexpr|. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.diffexpr = "" +vim.go.dex = vim.go.diffexpr +-- `'diffopt'` `'dip'` string (default "internal,filler,closeoff") +-- global +-- Option settings for diff mode. It can consist of the following items. +-- All are optional. Items must be separated by a comma. +-- +-- filler Show filler lines, to keep the text +-- synchronized with a window that has inserted +-- lines at the same position. Mostly useful +-- when windows are side-by-side and `'scrollbind'` +-- is set. +-- +-- context:{n} Use a context of {n} lines between a change +-- and a fold that contains unchanged lines. +-- When omitted a context of six lines is used. +-- When using zero the context is actually one, +-- since folds require a line in between, also +-- for a deleted line. +-- See |fold-diff|. +-- +-- iblank Ignore changes where lines are all blank. Adds +-- the "-B" flag to the "diff" command if +-- `'diffexpr'` is empty. Check the documentation +-- of the "diff" command for what this does +-- exactly. +-- NOTE: the diff windows will get out of sync, +-- because no differences between blank lines are +-- taken into account. +-- +-- icase Ignore changes in case of text. "a" and "A" +-- are considered the same. Adds the "-i" flag +-- to the "diff" command if `'diffexpr'` is empty. +-- +-- iwhite Ignore changes in amount of white space. Adds +-- the "-b" flag to the "diff" command if +-- `'diffexpr'` is empty. Check the documentation +-- of the "diff" command for what this does +-- exactly. It should ignore adding trailing +-- white space, but not leading white space. +-- +-- iwhiteall Ignore all white space changes. Adds +-- the "-w" flag to the "diff" command if +-- `'diffexpr'` is empty. Check the documentation +-- of the "diff" command for what this does +-- exactly. +-- +-- iwhiteeol Ignore white space changes at end of line. +-- Adds the "-Z" flag to the "diff" command if +-- `'diffexpr'` is empty. Check the documentation +-- of the "diff" command for what this does +-- exactly. +-- +-- horizontal Start diff mode with horizontal splits (unless +-- explicitly specified otherwise). +-- +-- vertical Start diff mode with vertical splits (unless +-- explicitly specified otherwise). +-- +-- closeoff When a window is closed where `'diff'` is set +-- and there is only one window remaining in the +-- same tab page with `'diff'` set, execute +-- `:diffoff` in that window. This undoes a +-- `:diffsplit` command. +-- +-- hiddenoff Do not use diff mode for a buffer when it +-- becomes hidden. +-- +-- foldcolumn:{n} Set the `'foldcolumn'` option to {n} when +-- starting diff mode. Without this 2 is used. +-- +-- followwrap Follow the `'wrap'` option and leave as it is. +-- +-- internal Use the internal diff library. This is +-- ignored when `'diffexpr'` is set. +-- When running out of memory when writing a +-- buffer this item will be ignored for diffs +-- involving that buffer. Set the `'verbose'` +-- option to see when this happens. +-- +-- indent-heuristic +-- Use the indent heuristic for the internal +-- diff library. +-- +-- linematch:{n} Enable a second stage diff on each generated +-- hunk in order to align lines. When the total +-- number of lines in a hunk exceeds {n}, the +-- second stage diff will not be performed as +-- very large hunks can cause noticeable lag. A +-- recommended setting is "linematch:60", as this +-- will enable alignment for a 2 buffer diff with +-- hunks of up to 30 lines each, or a 3 buffer +-- diff with hunks of up to 20 lines each. +-- +-- algorithm:{text} Use the specified diff algorithm with the +-- internal diff engine. Currently supported +-- algorithms are: +-- myers the default algorithm +-- minimal spend extra time to generate the +-- smallest possible diff +-- patience patience diff algorithm +-- histogram histogram diff algorithm +-- +-- Examples: > +-- :set diffopt=internal,filler,context:4 +-- :set diffopt= +-- :set diffopt=internal,filler,foldcolumn:3 +-- :set diffopt-=internal " do NOT use the internal diff parser +-- < +vim.go.diffopt = "internal,filler,closeoff" +vim.go.dip = vim.go.diffopt +-- `'digraph'` `'dg'` boolean (default off) +-- global +-- Enable the entering of digraphs in Insert mode with {char1} +-- {char2}. See |digraphs|. +vim.go.digraph = false +vim.go.dg = vim.go.digraph +-- `'directory'` `'dir'` string (default "$XDG_STATE_HOME/nvim/swap//") +-- global +-- List of directory names for the swap file, separated with commas. +-- +-- Possible items: +-- - The swap file will be created in the first directory where this is +-- possible. If it is not possible in any directory, but last +-- directory listed in the option does not exist, it is created. +-- - Empty means that no swap file will be used (recovery is +-- impossible!) and no |E303| error will be given. +-- - A directory "." means to put the swap file in the same directory as +-- the edited file. On Unix, a dot is prepended to the file name, so +-- it doesn't show in a directory listing. On MS-Windows the "hidden" +-- attribute is set and a dot prepended if possible. +-- - A directory starting with "./" (or ".\" for MS-Windows) means to put +-- the swap file relative to where the edited file is. The leading "." +-- is replaced with the path name of the edited file. +-- - For Unix and Win32, if a directory ends in two path separators "//", +-- the swap file name will be built from the complete path to the file +-- with all path separators replaced by percent `'%'` signs (including +-- the colon following the drive letter on Win32). This will ensure +-- file name uniqueness in the preserve directory. +-- On Win32, it is also possible to end with "\\". However, When a +-- separating comma is following, you must use "//", since "\\" will +-- include the comma in the file name. Therefore it is recommended to +-- use `'//'` , instead of `'\\'` . +-- - Spaces after the comma are ignored, other spaces are considered part +-- of the directory name. To have a space at the start of a directory +-- name, precede it with a backslash. +-- - To include a comma in a directory name precede it with a backslash. +-- - A directory name may end in an `':'` or `'/'` . +-- - Environment variables are expanded |:set_env|. +-- - Careful with `'\'` characters, type one before a space, type two to +-- get one in the option (see |option-backslash|), for example: > +-- :set dir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces +-- < - For backwards compatibility with Vim version 3.0 a `'>'` at the start +-- of the option is removed. +-- Using "." first in the list is recommended. This means that editing +-- the same file twice will result in a warning. Using "/tmp" on Unix is +-- discouraged: When the system crashes you lose the swap file. +-- "/var/tmp" is often not cleared when rebooting, thus is a better +-- choice than "/tmp". But others on the computer may be able to see the +-- files, and it can contain a lot of files, your swap files get lost in +-- the crowd. That is why a "tmp" directory in your home directory is +-- tried first. +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- directories from the list. This avoids problems when a future version +-- uses another default. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.directory = "/home/runner/.local/state/nvim/swap//" +vim.go.dir = vim.go.directory +-- `'display'` `'dy'` string (default "lastline") +-- global +-- Change the way text is displayed. This is a comma-separated list of +-- flags: +-- lastline When included, as much as possible of the last line +-- in a window will be displayed. "@@@" is put in the +-- last columns of the last screen line to indicate the +-- rest of the line is not displayed. +-- truncate Like "lastline", but "@@@" is displayed in the first +-- column of the last screen line. Overrules "lastline". +-- uhex Show unprintable characters hexadecimal as +-- instead of using ^C and ~C. +-- msgsep Obsolete flag. Allowed but takes no effect. |msgsep| +-- +-- When neither "lastline" nor "truncate" is included, a last line that +-- doesn't fit is replaced with "@" lines. +-- +-- The "@" character can be changed by setting the "lastline" item in +-- `'fillchars'` . The character is highlighted with |hl-NonText|. +vim.go.display = "lastline" +vim.go.dy = vim.go.display +-- `'eadirection'` `'ead'` string (default "both") +-- global +-- Tells when the `'equalalways'` option applies: +-- ver vertically, width of windows is not affected +-- hor horizontally, height of windows is not affected +-- both width and height of windows is affected +vim.go.eadirection = "both" +vim.go.ead = vim.go.eadirection +vim.go.edcompatible = false +vim.go.ed = vim.go.edcompatible +-- `'emoji'` `'emo'` boolean (default: on) +-- global +-- When on all Unicode emoji characters are considered to be full width. +-- This excludes "text emoji" characters, which are normally displayed as +-- single width. Unfortunately there is no good specification for this +-- and it has been determined on trial-and-error basis. Use the +-- |setcellwidths()| function to change the behavior. +vim.go.emoji = true +vim.go.emo = vim.go.emoji +-- `'encoding'` `'enc'` +-- String-encoding used internally and for |RPC| communication. +-- Always UTF-8. +-- +-- See `'fileencoding'` to control file-content encoding. +vim.go.encoding = "utf-8" +vim.go.enc = vim.go.encoding +-- `'equalalways'` `'ea'` boolean (default on) +-- global +-- When on, all the windows are automatically made the same size after +-- splitting or closing a window. This also happens the moment the +-- option is switched on. When off, splitting a window will reduce the +-- size of the current window and leave the other windows the same. When +-- closing a window the extra lines are given to the window next to it +-- (depending on `'splitbelow'` and `'splitright'` ). +-- When mixing vertically and horizontally split windows, a minimal size +-- is computed and some windows may be larger if there is room. The +-- `'eadirection'` option tells in which direction the size is affected. +-- Changing the height and width of a window can be avoided by setting +-- `'winfixheight'` and `'winfixwidth'` , respectively. +-- If a window size is specified when creating a new window sizes are +-- currently not equalized (it's complicated, but may be implemented in +-- the future). +vim.go.equalalways = true +vim.go.ea = vim.go.equalalways +-- `'errorbells'` `'eb'` boolean (default off) +-- global +-- Ring the bell (beep or screen flash) for error messages. This only +-- makes a difference for error messages, the bell will be used always +-- for a lot of errors without a message (e.g., hitting in Normal +-- mode). See `'visualbell'` to make the bell behave like a screen flash +-- or do nothing. See `'belloff'` to finetune when to ring the bell. +vim.go.errorbells = false +vim.go.eb = vim.go.errorbells +-- `'errorfile'` `'ef'` string (default: "errors.err") +-- global +-- Name of the errorfile for the QuickFix mode (see |:cf|). +-- When the "-q" command-line argument is used, `'errorfile'` is set to the +-- following argument. See |-q|. +-- NOT used for the ":make" command. See `'makeef'` for that. +-- Environment variables are expanded |:set_env|. +-- See |option-backslash| about including spaces and backslashes. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.errorfile = "errors.err" +vim.go.ef = vim.go.errorfile +-- `'eventignore'` `'ei'` string (default "") +-- global +-- A list of autocommand event names, which are to be ignored. +-- When set to "all" or when "all" is one of the items, all autocommand +-- events are ignored, autocommands will not be executed. +-- Otherwise this is a comma-separated list of event names. Example: > +-- :set ei=WinEnter,WinLeave +-- < +vim.go.eventignore = "" +vim.go.ei = vim.go.eventignore +-- `'exrc'` `'ex'` boolean (default off) +-- global +-- Automatically execute .nvim.lua, .nvimrc, and .exrc files in the +-- current directory, if the file is in the |trust| list. Use |:trust| to +-- manage trusted files. See also |vim.secure.read()|. +-- +-- Compare `'exrc'` to |editorconfig|: +-- - `'exrc'` can execute any code; editorconfig only specifies settings. +-- - `'exrc'` is Nvim-specific; editorconfig works in other editors. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.exrc = false +vim.go.ex = vim.go.exrc +-- `'fileencodings'` `'fencs'` string (default: "ucs-bom,utf-8,default,latin1") +-- global +-- This is a list of character encodings considered when starting to edit +-- an existing file. When a file is read, Vim tries to use the first +-- mentioned character encoding. If an error is detected, the next one +-- in the list is tried. When an encoding is found that works, +-- `'fileencoding'` is set to it. If all fail, `'fileencoding'` is set to +-- an empty string, which means that UTF-8 is used. +-- WARNING: Conversion can cause loss of information! You can use +-- the |++bad| argument to specify what is done with characters +-- that can't be converted. +-- For an empty file or a file with only ASCII characters most encodings +-- will work and the first entry of `'fileencodings'` will be used (except +-- "ucs-bom", which requires the BOM to be present). If you prefer +-- another encoding use an BufReadPost autocommand event to test if your +-- preferred encoding is to be used. Example: > +-- au BufReadPost * if search(`'\S'` , `'w'` ) == 0 | +-- \ set fenc=iso-2022-jp | endif +-- < This sets `'fileencoding'` to "iso-2022-jp" if the file does not contain +-- non-blank characters. +-- When the |++enc| argument is used then the value of `'fileencodings'` is +-- not used. +-- Note that `'fileencodings'` is not used for a new file, the global value +-- of `'fileencoding'` is used instead. You can set it with: > +-- :setglobal fenc=iso-8859-2 +-- < This means that a non-existing file may get a different encoding than +-- an empty file. +-- The special value "ucs-bom" can be used to check for a Unicode BOM +-- (Byte Order Mark) at the start of the file. It must not be preceded +-- by "utf-8" or another Unicode encoding for this to work properly. +-- An entry for an 8-bit encoding (e.g., "latin1") should be the last, +-- because Vim cannot detect an error, thus the encoding is always +-- accepted. +-- The special value "default" can be used for the encoding from the +-- environment. It is useful when your environment uses a non-latin1 +-- encoding, such as Russian. +-- When a file contains an illegal UTF-8 byte sequence it won't be +-- recognized as "utf-8". You can use the |8g8| command to find the +-- illegal byte sequence. +-- WRONG VALUES: WHAT'S WRONG: +-- latin1,utf-8 "latin1" will always be used +-- utf-8,ucs-bom,latin1 BOM won't be recognized in an utf-8 +-- file +-- cp1250,latin1 "cp1250" will always be used +-- If `'fileencodings'` is empty, `'fileencoding'` is not modified. +-- See `'fileencoding'` for the possible values. +-- Setting this option does not have an effect until the next time a file +-- is read. +vim.go.fileencodings = "ucs-bom,utf-8,default,latin1" +vim.go.fencs = vim.go.fileencodings +-- `'fileformats'` `'ffs'` string (default: +-- Win32: "dos,unix", +-- Unix: "unix,dos") +-- global +-- This gives the end-of-line () formats that will be tried when +-- starting to edit a new buffer and when reading a file into an existing +-- buffer: +-- - When empty, the format defined with `'fileformat'` will be used +-- always. It is not set automatically. +-- - When set to one name, that format will be used whenever a new buffer +-- is opened. `'fileformat'` is set accordingly for that buffer. The +-- `'fileformats'` name will be used when a file is read into an existing +-- buffer, no matter what `'fileformat'` for that buffer is set to. +-- - When more than one name is present, separated by commas, automatic +-- detection will be done when reading a file. When starting to +-- edit a file, a check is done for the : +-- 1. If all lines end in , and `'fileformats'` includes "dos", +-- `'fileformat'` is set to "dos". +-- 2. If a is found and `'fileformats'` includes "unix", `'fileformat'` +-- is set to "unix". Note that when a is found without a +-- preceding , "unix" is preferred over "dos". +-- 3. If `'fileformat'` has not yet been set, and if a is found, and +-- if `'fileformats'` includes "mac", `'fileformat'` is set to "mac". +-- This means that "mac" is only chosen when: +-- "unix" is not present or no is found in the file, and +-- "dos" is not present or no is found in the file. +-- Except: if "unix" was chosen, but there is a before +-- the first , and there appear to be more s than s in +-- the first few lines, "mac" is used. +-- 4. If `'fileformat'` is still not set, the first name from +-- `'fileformats'` is used. +-- When reading a file into an existing buffer, the same is done, but +-- this happens like `'fileformat'` has been set appropriately for that +-- file only, the option is not changed. +-- When `'binary'` is set, the value of `'fileformats'` is not used. +-- +-- When Vim starts up with an empty buffer the first item is used. You +-- can overrule this by setting `'fileformat'` in your .vimrc. +-- +-- For systems with a Dos-like (), when reading files that +-- are ":source"ed and for vimrc files, automatic detection may be +-- done: +-- - When `'fileformats'` is empty, there is no automatic detection. Dos +-- format will be used. +-- - When `'fileformats'` is set to one or more names, automatic detection +-- is done. This is based on the first in the file: If there is a +-- in front of it, Dos format is used, otherwise Unix format is +-- used. +-- Also see |file-formats|. +vim.go.fileformats = "unix,dos" +vim.go.ffs = vim.go.fileformats +-- `'fileignorecase'` `'fic'` boolean (default on for systems where case in file +-- names is normally ignored) +-- global +-- When set case is ignored when using file names and directories. +-- See `'wildignorecase'` for only ignoring case when doing completion. +vim.go.fileignorecase = false +vim.go.fic = vim.go.fileignorecase +-- `'foldclose'` `'fcl'` string (default "") +-- global +-- When set to "all", a fold is closed when the cursor isn't in it and +-- its level is higher than `'foldlevel'` . Useful if you want folds to +-- automatically close when moving out of them. +vim.go.foldclose = "" +vim.go.fcl = vim.go.foldclose +-- `'foldlevelstart'` `'fdls'` number (default: -1) +-- global +-- Sets `'foldlevel'` when starting to edit another buffer in a window. +-- Useful to always start editing with all folds closed (value zero), +-- some folds closed (one) or no folds closed (99). +-- This is done before reading any modeline, thus a setting in a modeline +-- overrules this option. Starting to edit a file for |diff-mode| also +-- ignores this option and closes all folds. +-- It is also done before BufReadPre autocommands, to allow an autocmd to +-- overrule the `'foldlevel'` value for specific files. +-- When the value is negative, it is not used. +vim.go.foldlevelstart = -1 +vim.go.fdls = vim.go.foldlevelstart +-- `'foldopen'` `'fdo'` string (default: "block,hor,mark,percent,quickfix, +-- search,tag,undo") +-- global +-- Specifies for which type of commands folds will be opened, if the +-- command moves the cursor into a closed fold. It is a comma-separated +-- list of items. +-- NOTE: When the command is part of a mapping this option is not used. +-- Add the |zv| command to the mapping to get the same effect. +-- (rationale: the mapping may want to control opening folds itself) +-- +-- item commands ~ +-- all any +-- block "(", "{", "[[", "[{", etc. +-- hor horizontal movements: "l", "w", "fx", etc. +-- insert any command in Insert mode +-- jump far jumps: "G", "gg", etc. +-- mark jumping to a mark: "'m", CTRL-O, etc. +-- percent "%" +-- quickfix ":cn", ":crew", ":make", etc. +-- search search for a pattern: "/", "n", "*", "gd", etc. +-- (not for a search pattern in a ":" command) +-- Also for |[s| and |]s|. +-- tag jumping to a tag: ":ta", CTRL-T, etc. +-- undo undo or redo: "u" and CTRL-R +-- When a movement command is used for an operator (e.g., "dl" or "y%") +-- this option is not used. This means the operator will include the +-- whole closed fold. +-- Note that vertical movements are not here, because it would make it +-- very difficult to move onto a closed fold. +-- In insert mode the folds containing the cursor will always be open +-- when text is inserted. +-- To close folds you can re-apply `'foldlevel'` with the |zx| command or +-- set the `'foldclose'` option to "all". +vim.go.foldopen = "block,hor,mark,percent,quickfix,search,tag,undo" +vim.go.fdo = vim.go.foldopen +-- `'fsync'` `'fs'` boolean (default on) +-- global +-- When on, the OS function fsync() will be called after saving a file +-- (|:write|, |writefile()|, …), |swap-file|, |undo-persistence| and |shada-file|. +-- This flushes the file to disk, ensuring that it is safely written. +-- Slow on some systems: writing buffers, quitting Nvim, and other +-- operations may sometimes take a few seconds. +-- +-- Files are ALWAYS flushed (`'fsync'` is ignored) when: +-- - |CursorHold| event is triggered +-- - |:preserve| is called +-- - system signals low battery life +-- - Nvim exits abnormally +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.fsync = true +vim.go.fs = vim.go.fsync +-- `'gdefault'` `'gd'` boolean (default off) +-- global +-- When on, the ":substitute" flag `'g'` is default on. This means that +-- all matches in a line are substituted instead of one. When a `'g'` flag +-- is given to a ":substitute" command, this will toggle the substitution +-- of all or one match. See |complex-change|. +-- +-- command `'gdefault'` on `'gdefault'` off ~ +-- :s/// subst. all subst. one +-- :s///g subst. one subst. all +-- :s///gg subst. all subst. one +-- +-- DEPRECATED: Setting this option may break plugins that are not aware +-- of this option. Also, many users get confused that adding the /g flag +-- has the opposite effect of that it normally does. +vim.go.gdefault = false +vim.go.gd = vim.go.gdefault +-- `'grepformat'` `'gfm'` string (default "%f:%l:%m,%f:%l%m,%f %l%m") +-- global +-- Format to recognize for the ":grep" command output. +-- This is a scanf-like string that uses the same format as the +-- `'errorformat'` option: see |errorformat|. +vim.go.grepformat = "%f:%l:%m,%f:%l%m,%f %l%m" +vim.go.gfm = vim.go.grepformat +-- `'guicursor'` `'gcr'` string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20") +-- global +-- Configures the cursor style for each mode. Works in the GUI and many +-- terminals. See |tui-cursor-shape|. +-- +-- To disable cursor-styling, reset the option: > +-- :set guicursor= +-- +-- < To enable mode shapes, "Cursor" highlight, and blinking: > +-- :set guicursor=n-v-c:block,i-ci-ve:ver25,r-cr:hor20,o:hor50 +-- \,a:blinkwait700-blinkoff400-blinkon250-Cursor/lCursor +-- \,sm:block-blinkwait175-blinkoff150-blinkon175 +-- +-- < The option is a comma-separated list of parts. Each part consists of a +-- mode-list and an argument-list: +-- mode-list:argument-list,mode-list:argument-list,.. +-- The mode-list is a dash separated list of these modes: +-- n Normal mode +-- v Visual mode +-- ve Visual mode with `'selection'` "exclusive" (same as `'v'` , +-- if not specified) +-- o Operator-pending mode +-- i Insert mode +-- r Replace mode +-- c Command-line Normal (append) mode +-- ci Command-line Insert mode +-- cr Command-line Replace mode +-- sm showmatch in Insert mode +-- a all modes +-- The argument-list is a dash separated list of these arguments: +-- hor{N} horizontal bar, {N} percent of the character height +-- ver{N} vertical bar, {N} percent of the character width +-- block block cursor, fills the whole character +-- - Only one of the above three should be present. +-- - Default is "block" for each mode. +-- blinkwait{N} +-- blinkon{N} +-- blinkoff{N} +-- blink times for cursor: blinkwait is the delay before +-- the cursor starts blinking, blinkon is the time that +-- the cursor is shown and blinkoff is the time that the +-- cursor is not shown. Times are in msec. When one of +-- the numbers is zero, there is no blinking. E.g.: > +-- :set guicursor=n:blinkon0 +-- < - Default is "blinkon0" for each mode. +-- {group-name} +-- Highlight group that decides the color and font of the +-- cursor. +-- In the |TUI|: +-- - |inverse|/reverse and no group-name are interpreted +-- as "host-terminal default cursor colors" which +-- typically means "inverted bg and fg colors". +-- - |ctermfg| and |guifg| are ignored. +-- {group-name}/{group-name} +-- Two highlight group names, the first is used when +-- no language mappings are used, the other when they +-- are. |language-mapping| +-- +-- Examples of parts: +-- n-c-v:block-nCursor In Normal, Command-line and Visual mode, use a +-- block cursor with colors from the "nCursor" +-- highlight group +-- n-v-c-sm:block,i-ci-ve:ver25-Cursor,r-cr-o:hor20 +-- In Normal et al. modes, use a block cursor +-- with the default colors defined by the host +-- terminal. In Insert-likes modes, use +-- a vertical bar cursor with colors from +-- "Cursor" highlight group. In Replace-likes +-- modes, use a underline cursor with +-- default colors. +-- i-ci:ver30-iCursor-blinkwait300-blinkon200-blinkoff150 +-- In Insert and Command-line Insert mode, use a +-- 30% vertical bar cursor with colors from the +-- "iCursor" highlight group. Blink a bit +-- faster. +-- +-- The `'a'` mode is different. It will set the given argument-list for +-- all modes. It does not reset anything to defaults. This can be used +-- to do a common setting for all modes. For example, to switch off +-- blinking: "a:blinkon0" +-- +-- Examples of cursor highlighting: > +-- :highlight Cursor gui=reverse guifg=NONE guibg=NONE +-- :highlight Cursor gui=NONE guifg=bg guibg=fg +-- < +vim.go.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20" +vim.go.gcr = vim.go.guicursor +-- `'guifont'` `'gfn'` string (default "") +-- global +-- This is a list of fonts which will be used for the GUI version of Vim. +-- In its simplest form the value is just one font name. When +-- the font cannot be found you will get an error message. To try other +-- font names a list can be specified, font names separated with commas. +-- The first valid font is used. +-- +-- Spaces after a comma are ignored. To include a comma in a font name +-- precede it with a backslash. Setting an option requires an extra +-- backslash before a space and a backslash. See also +-- |option-backslash|. For example: > +-- :set guifont=Screen15,\ 7x13,font\\,with\\,commas +-- < will make Vim try to use the font "Screen15" first, and if it fails it +-- will try to use "7x13" and then "font,with,commas" instead. +-- +-- If none of the fonts can be loaded, Vim will keep the current setting. +-- If an empty font list is given, Vim will try using other resource +-- settings (for X, it will use the Vim.font resource), and finally it +-- will try some builtin default which should always be there ("7x13" in +-- the case of X). The font names given should be "normal" fonts. Vim +-- will try to find the related bold and italic fonts. +-- +-- For Win32 and Mac OS: > +-- :set guifont=* +-- < will bring up a font requester, where you can pick the font you want. +-- +-- The font name depends on the GUI used. +-- +-- For Mac OSX you can use something like this: > +-- :set guifont=Monaco:h10 +-- < +-- Note that the fonts must be mono-spaced (all characters have the same +-- width). +-- +-- To preview a font on X11, you might be able to use the "xfontsel" +-- program. The "xlsfonts" program gives a list of all available fonts. +-- +-- For the Win32 GUI +-- - takes these options in the font name: +-- hXX - height is XX (points, can be floating-point) +-- wXX - width is XX (points, can be floating-point) +-- b - bold +-- i - italic +-- u - underline +-- s - strikeout +-- cXX - character set XX. Valid charsets are: ANSI, ARABIC, +-- BALTIC, CHINESEBIG5, DEFAULT, EASTEUROPE, GB2312, GREEK, +-- HANGEUL, HEBREW, JOHAB, MAC, OEM, RUSSIAN, SHIFTJIS, +-- SYMBOL, THAI, TURKISH, VIETNAMESE ANSI and BALTIC. +-- Normally you would use "cDEFAULT". +-- +-- Use a `':'` to separate the options. +-- - A `'_'` can be used in the place of a space, so you don't need to use +-- backslashes to escape the spaces. +-- - Examples: > +-- :set guifont=courier_new:h12:w5:b:cRUSSIAN +-- :set guifont=Andale_Mono:h7.5:w4.5 +-- < +vim.go.guifont = "" +vim.go.gfn = vim.go.guifont +-- `'guifontwide'` `'gfw'` string (default "") +-- global +-- Comma-separated list of fonts to be used for double-width characters. +-- The first font that can be loaded is used. +-- Note: The size of these fonts must be exactly twice as wide as the one +-- specified with `'guifont'` and the same height. +-- +-- When `'guifont'` has a valid font and `'guifontwide'` is empty Vim will +-- attempt to set `'guifontwide'` to a matching double-width font. +vim.go.guifontwide = "" +vim.go.gfw = vim.go.guifontwide +-- `'guioptions'` `'go'` string (default "egmrLT" (MS-Windows)) +-- global +-- This option only has an effect in the GUI version of Vim. It is a +-- sequence of letters which describes what components and options of the +-- GUI should be used. +-- To avoid problems with flags that are added in the future, use the +-- "+=" and "-=" feature of ":set" |add-option-flags|. +-- +-- Valid letters are as follows: +-- +-- `'a'` Autoselect: If present, then whenever VISUAL mode is started, +-- or the Visual area extended, Vim tries to become the owner of +-- the windowing system's global selection. This means that the +-- Visually highlighted text is available for pasting into other +-- applications as well as into Vim itself. When the Visual mode +-- ends, possibly due to an operation on the text, or when an +-- application wants to paste the selection, the highlighted text +-- is automatically yanked into the "* selection register. +-- Thus the selection is still available for pasting into other +-- applications after the VISUAL mode has ended. +-- If not present, then Vim won't become the owner of the +-- windowing system's global selection unless explicitly told to +-- by a yank or delete operation for the "* register. +-- The same applies to the modeless selection. +-- +-- `'P'` Like autoselect but using the "+ register instead of the "* +-- register. +-- +-- `'A'` Autoselect for the modeless selection. Like `'a'` , but only +-- applies to the modeless selection. +-- +-- `'guioptions'` autoselect Visual autoselect modeless ~ +-- "" - - +-- "a" yes yes +-- "A" - yes +-- "aA" yes yes +-- +-- +-- `'c'` Use console dialogs instead of popup dialogs for simple +-- choices. +-- +-- `'d'` Use dark theme variant if available. +-- +-- `'e'` Add tab pages when indicated with `'showtabline'` . +-- `'guitablabel'` can be used to change the text in the labels. +-- When `'e'` is missing a non-GUI tab pages line may be used. +-- The GUI tabs are only supported on some systems, currently +-- Mac OS/X and MS-Windows. +-- +-- `'i'` Use a Vim icon. +-- +-- `'m'` Menu bar is present. +-- +-- `'M'` The system menu "$VIMRUNTIME/menu.vim" is not sourced. Note +-- that this flag must be added in the vimrc file, before +-- switching on syntax or filetype recognition (when the |gvimrc| +-- file is sourced the system menu has already been loaded; the +-- `:syntax on` and `:filetype on` commands load the menu too). +-- +-- `'g'` Grey menu items: Make menu items that are not active grey. If +-- `'g'` is not included inactive menu items are not shown at all. +-- +-- `'T'` Include Toolbar. Currently only in Win32 GUI. +-- +-- `'r'` Right-hand scrollbar is always present. +-- +-- `'R'` Right-hand scrollbar is present when there is a vertically +-- split window. +-- +-- `'l'` Left-hand scrollbar is always present. +-- +-- `'L'` Left-hand scrollbar is present when there is a vertically +-- split window. +-- +-- `'b'` Bottom (horizontal) scrollbar is present. Its size depends on +-- the longest visible line, or on the cursor line if the `'h'` +-- flag is included. |gui-horiz-scroll| +-- +-- `'h'` Limit horizontal scrollbar size to the length of the cursor +-- line. Reduces computations. |gui-horiz-scroll| +-- +-- And yes, you may even have scrollbars on the left AND the right if +-- you really want to :-). See |gui-scrollbars| for more information. +-- +-- +-- `'v'` Use a vertical button layout for dialogs. When not included, +-- a horizontal layout is preferred, but when it doesn't fit a +-- vertical layout is used anyway. Not supported in GTK 3. +-- +-- `'p'` Use Pointer callbacks for X11 GUI. This is required for some +-- window managers. If the cursor is not blinking or hollow at +-- the right moment, try adding this flag. This must be done +-- before starting the GUI. Set it in your |gvimrc|. Adding or +-- removing it after the GUI has started has no effect. +-- +-- `'k'` Keep the GUI window size when adding/removing a scrollbar, or +-- toolbar, tabline, etc. Instead, the behavior is similar to +-- when the window is maximized and will adjust `'lines'` and +-- `'columns'` to fit to the window. Without the `'k'` flag Vim will +-- try to keep `'lines'` and `'columns'` the same when adding and +-- removing GUI components. +vim.go.guioptions = "" +vim.go.go = vim.go.guioptions +-- `'guitablabel'` `'gtl'` string (default empty) +-- global +-- When non-empty describes the text to use in a label of the GUI tab +-- pages line. When empty and when the result is empty Vim will use a +-- default label. See |setting-guitablabel| for more info. +-- +-- The format of this option is like that of `'statusline'` . +-- `'guitabtooltip'` is used for the tooltip, see below. +-- The expression will be evaluated in the |sandbox| when set from a +-- modeline, see |sandbox-option|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- Only used when the GUI tab pages line is displayed. `'e'` must be +-- present in `'guioptions'` . For the non-GUI tab pages line `'tabline'` is +-- used. +vim.go.guitablabel = "" +vim.go.gtl = vim.go.guitablabel +-- `'guitabtooltip'` `'gtt'` string (default empty) +-- global +-- When non-empty describes the text to use in a tooltip for the GUI tab +-- pages line. When empty Vim will use a default tooltip. +-- This option is otherwise just like `'guitablabel'` above. +-- You can include a line break. Simplest method is to use |:let|: > +-- :let &guitabtooltip = "line one\nline two" +-- < +vim.go.guitabtooltip = "" +vim.go.gtt = vim.go.guitabtooltip +-- `'helpfile'` `'hf'` string (default (MS-Windows) "$VIMRUNTIME\doc\help.txt" +-- (others) "$VIMRUNTIME/doc/help.txt") +-- global +-- Name of the main help file. All distributed help files should be +-- placed together in one directory. Additionally, all "doc" directories +-- in `'runtimepath'` will be used. +-- Environment variables are expanded |:set_env|. For example: +-- "$VIMRUNTIME/doc/help.txt". If $VIMRUNTIME is not set, $VIM is also +-- tried. Also see |$VIMRUNTIME| and |option-backslash| about including +-- spaces and backslashes. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.helpfile = "/tmp/nvim/squashfs-root/usr/share/nvim/runtime/doc/help.txt" +vim.go.hf = vim.go.helpfile +-- `'helpheight'` `'hh'` number (default 20) +-- global +-- Minimal initial height of the help window when it is opened with the +-- ":help" command. The initial height of the help window is half of the +-- current window, or (when the `'ea'` option is on) the same as other +-- windows. When the height is less than `'helpheight'` , the height is +-- set to `'helpheight'` . Set to zero to disable. +vim.go.helpheight = 20 +vim.go.hh = vim.go.helpheight +-- `'helplang'` `'hlg'` string (default: messages language or empty) +-- global +-- Comma-separated list of languages. Vim will use the first language +-- for which the desired help can be found. The English help will always +-- be used as a last resort. You can add "en" to prefer English over +-- another language, but that will only find tags that exist in that +-- language and not in the English help. +-- Example: > +-- :set helplang=de,it +-- < This will first search German, then Italian and finally English help +-- files. +-- When using |CTRL-]| and ":help!" in a non-English help file Vim will +-- try to find the tag in the current language before using this option. +-- See |help-translated|. +vim.go.helplang = "" +vim.go.hlg = vim.go.helplang +-- `'hidden'` `'hid'` boolean (default on) +-- global +-- When off a buffer is unloaded (including loss of undo information) +-- when it is |abandon|ed. When on a buffer becomes hidden when it is +-- |abandon|ed. A buffer displayed in another window does not become +-- hidden, of course. +-- +-- Commands that move through the buffer list sometimes hide a buffer +-- although the `'hidden'` option is off when these three are true: +-- - the buffer is modified +-- - `'autowrite'` is off or writing is not possible +-- - the `'!'` flag was used +-- Also see |windows|. +-- +-- To hide a specific buffer use the `'bufhidden'` option. +-- `'hidden'` is set for one command with ":hide {command}" |:hide|. +vim.go.hidden = true +vim.go.hid = vim.go.hidden +vim.go.highlight = "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr,G:CursorLineSign,O:CursorLineFoldr:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,0:Whitespace,I:NormalNC" +vim.go.hl = vim.go.highlight +-- `'history'` `'hi'` number (default: 10000) +-- global +-- A history of ":" commands, and a history of previous search patterns +-- is remembered. This option decides how many entries may be stored in +-- each of these histories (see |cmdline-editing|). +-- The maximum value is 10000. +vim.go.history = 10000 +vim.go.hi = vim.go.history +vim.go.hkmap = false +vim.go.hk = vim.go.hkmap +vim.go.hkmapp = false +vim.go.hkp = vim.go.hkmapp +-- `'hlsearch'` `'hls'` boolean (default on) +-- global +-- When there is a previous search pattern, highlight all its matches. +-- The |hl-Search| highlight group determines the highlighting for all +-- matches not under the cursor while the |hl-CurSearch| highlight group +-- (if defined) determines the highlighting for the match under the +-- cursor. If |hl-CurSearch| is not defined, then |hl-Search| is used for +-- both. Note that only the matching text is highlighted, any offsets +-- are not applied. +-- See also: `'incsearch'` and |:match|. +-- When you get bored looking at the highlighted matches, you can turn it +-- off with |:nohlsearch|. This does not change the option value, as +-- soon as you use a search command, the highlighting comes back. +-- `'redrawtime'` specifies the maximum time spent on finding matches. +-- When the search pattern can match an end-of-line, Vim will try to +-- highlight all of the matched text. However, this depends on where the +-- search starts. This will be the first line in the window or the first +-- line below a closed fold. A match in a previous line which is not +-- drawn may not continue in a newly drawn line. +-- You can specify whether the highlight status is restored on startup +-- with the `'h'` flag in `'shada'` |shada-h|. +vim.go.hlsearch = true +vim.go.hls = vim.go.hlsearch +-- `'icon'` boolean (default off, on when title can be restored) +-- global +-- When on, the icon text of the window will be set to the value of +-- `'iconstring'` (if it is not empty), or to the name of the file +-- currently being edited. Only the last part of the name is used. +-- Overridden by the `'iconstring'` option. +-- Only works if the terminal supports setting window icons. +vim.go.icon = false +-- `'iconstring'` string (default "") +-- global +-- When this option is not empty, it will be used for the icon text of +-- the window. This happens only when the `'icon'` option is on. +-- Only works if the terminal supports setting window icon text +-- When this option contains printf-style `'%'` items, they will be +-- expanded according to the rules used for `'statusline'` . See +-- `'titlestring'` for example settings. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +vim.go.iconstring = "" +-- `'ignorecase'` `'ic'` boolean (default off) +-- global +-- Ignore case in search patterns. Also used when searching in the tags +-- file. +-- Also see `'smartcase'` and `'tagcase'` . +-- Can be overruled by using "\c" or "\C" in the pattern, see +-- |/ignorecase|. +vim.go.ignorecase = false +vim.go.ic = vim.go.ignorecase +-- `'imcmdline'` `'imc'` boolean (default off) +-- global +-- When set the Input Method is always on when starting to edit a command +-- line, unless entering a search pattern (see `'imsearch'` for that). +-- Setting this option is useful when your input method allows entering +-- English characters directly, e.g., when it's used to type accented +-- characters with dead keys. +vim.go.imcmdline = false +vim.go.imc = vim.go.imcmdline +-- `'imdisable'` `'imd'` boolean (default off, on for some systems (SGI)) +-- global +-- When set the Input Method is never used. This is useful to disable +-- the IM when it doesn't work properly. +-- Currently this option is on by default for SGI/IRIX machines. This +-- may change in later releases. +vim.go.imdisable = false +vim.go.imd = vim.go.imdisable +-- `'inccommand'` `'icm'` string (default "nosplit") +-- global +-- +-- When nonempty, shows the effects of |:substitute|, |:smagic|, +-- |:snomagic| and user commands with the |:command-preview| flag as you +-- type. +-- +-- Possible values: +-- nosplit Shows the effects of a command incrementally in the +-- buffer. +-- split Like "nosplit", but also shows partial off-screen +-- results in a preview window. +-- +-- If the preview for built-in commands is too slow (exceeds +-- `'redrawtime'` ) then `'inccommand'` is automatically disabled until +-- |Command-line-mode| is done. +vim.go.inccommand = "nosplit" +vim.go.icm = vim.go.inccommand +-- `'incsearch'` `'is'` boolean (default on) +-- global +-- While typing a search command, show where the pattern, as it was typed +-- so far, matches. The matched string is highlighted. If the pattern +-- is invalid or not found, nothing is shown. The screen will be updated +-- often, this is only useful on fast terminals. +-- Note that the match will be shown, but the cursor will return to its +-- original position when no match is found and when pressing . You +-- still need to finish the search command with to move the +-- cursor to the match. +-- You can use the CTRL-G and CTRL-T keys to move to the next and +-- previous match. |c_CTRL-G| |c_CTRL-T| +-- Vim only searches for about half a second. With a complicated +-- pattern and/or a lot of text the match may not be found. This is to +-- avoid that Vim hangs while you are typing the pattern. +-- The |hl-IncSearch| highlight group determines the highlighting. +-- When `'hlsearch'` is on, all matched strings are highlighted too while +-- typing a search command. See also: `'hlsearch'` . +-- If you don't want to turn `'hlsearch'` on, but want to highlight all +-- matches while searching, you can turn on and off `'hlsearch'` with +-- autocmd. Example: > +-- augroup vimrc-incsearch-highlight +-- autocmd! +-- autocmd CmdlineEnter /,\? :set hlsearch +-- autocmd CmdlineLeave /,\? :set nohlsearch +-- augroup END +-- < +-- CTRL-L can be used to add one character from after the current match +-- to the command line. If `'ignorecase'` and `'smartcase'` are set and the +-- command line has no uppercase characters, the added character is +-- converted to lowercase. +-- CTRL-R CTRL-W can be used to add the word at the end of the current +-- match, excluding the characters that were already typed. +vim.go.incsearch = true +vim.go.is = vim.go.incsearch +vim.go.insertmode = false +vim.go.im = vim.go.insertmode +-- `'isfname'` `'isf'` string (default for Windows: +-- "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=" +-- otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,=") +-- global +-- The characters specified by this option are included in file names and +-- path names. Filenames are used for commands like "gf", "[i" and in +-- the tags file. It is also used for "\f" in a |pattern|. +-- Multi-byte characters 256 and above are always included, only the +-- characters up to 255 are specified with this option. +-- For UTF-8 the characters 0xa0 to 0xff are included as well. +-- Think twice before adding white space to this option. Although a +-- space may appear inside a file name, the effect will be that Vim +-- doesn't know where a file name starts or ends when doing completion. +-- It most likely works better without a space in `'isfname'` . +-- +-- Note that on systems using a backslash as path separator, Vim tries to +-- do its best to make it work as you would expect. That is a bit +-- tricky, since Vi originally used the backslash to escape special +-- characters. Vim will not remove a backslash in front of a normal file +-- name character on these systems, but it will on Unix and alikes. The +-- `'&'` and `'^'` are not included by default, because these are special for +-- cmd.exe. +-- +-- The format of this option is a list of parts, separated with commas. +-- Each part can be a single character number or a range. A range is two +-- character numbers with `'-'` in between. A character number can be a +-- decimal number between 0 and 255 or the ASCII character itself (does +-- not work for digits). Example: +-- "_,-,128-140,#-43" (include `'_'` and `'-'` and the range +-- 128 to 140 and `'#'` to 43) +-- If a part starts with `'^'` , the following character number or range +-- will be excluded from the option. The option is interpreted from left +-- to right. Put the excluded character after the range where it is +-- included. To include `'^'` itself use it as the last character of the +-- option or the end of a range. Example: +-- "^a-z,#,^" (exclude `'a'` to `'z'` , include `'#'` and `'^'` ) +-- If the character is `'@'` , all characters where isalpha() returns TRUE +-- are included. Normally these are the characters a to z and A to Z, +-- plus accented characters. To include `'@'` itself use "@-@". Examples: +-- "@,^a-z" All alphabetic characters, excluding lower +-- case ASCII letters. +-- "a-z,A-Z,@-@" All letters plus the `'@'` character. +-- A comma can be included by using it where a character number is +-- expected. Example: +-- "48-57,,,_" Digits, comma and underscore. +-- A comma can be excluded by prepending a `'^'` . Example: +-- " -~,^,,9" All characters from space to `'~'` , excluding +-- comma, plus . +-- See |option-backslash| about including spaces and backslashes. +vim.go.isfname = "@,48-57,/,.,-,_,+,,,#,$,%,~,=" +vim.go.isf = vim.go.isfname +-- `'isident'` `'isi'` string (default for Windows: +-- "@,48-57,_,128-167,224-235" +-- otherwise: "@,48-57,_,192-255") +-- global +-- The characters given by this option are included in identifiers. +-- Identifiers are used in recognizing environment variables and after a +-- match of the `'define'` option. It is also used for "\i" in a +-- |pattern|. See `'isfname'` for a description of the format of this +-- option. For `'@'` only characters up to 255 are used. +-- Careful: If you change this option, it might break expanding +-- environment variables. E.g., when `'/'` is included and Vim tries to +-- expand "$HOME/.local/state/nvim/shada/main.shada". Maybe you should +-- change `'iskeyword'` instead. +vim.go.isident = "@,48-57,_,192-255" +vim.go.isi = vim.go.isident +-- `'isprint'` `'isp'` string (default: "@,161-255") +-- global +-- The characters given by this option are displayed directly on the +-- screen. It is also used for "\p" in a |pattern|. The characters from +-- space (ASCII 32) to `'~'` (ASCII 126) are always displayed directly, +-- even when they are not included in `'isprint'` or excluded. See +-- `'isfname'` for a description of the format of this option. +-- +-- Non-printable characters are displayed with two characters: +-- 0 - 31 "^@" - "^_" +-- 32 - 126 always single characters +-- 127 "^?" +-- 128 - 159 "~@" - "~_" +-- 160 - 254 "| " - "|~" +-- 255 "~?" +-- Illegal bytes from 128 to 255 (invalid UTF-8) are +-- displayed as , with the hexadecimal value of the byte. +-- When `'display'` contains "uhex" all unprintable characters are +-- displayed as . +-- The SpecialKey highlighting will be used for unprintable characters. +-- |hl-SpecialKey| +-- +-- Multi-byte characters 256 and above are always included, only the +-- characters up to 255 are specified with this option. When a character +-- is printable but it is not available in the current font, a +-- replacement character will be shown. +-- Unprintable and zero-width Unicode characters are displayed as . +-- There is no option to specify these characters. +vim.go.isprint = "@,161-255" +vim.go.isp = vim.go.isprint +-- `'joinspaces'` `'js'` boolean (default off) +-- global +-- Insert two spaces after a `'.'` , `'?'` and `'!'` with a join command. +-- Otherwise only one space is inserted. +vim.go.joinspaces = false +vim.go.js = vim.go.joinspaces +-- `'jumpoptions'` `'jop'` string (default "") +-- global +-- List of words that change the behavior of the |jumplist|. +-- stack Make the jumplist behave like the tagstack or like a +-- web browser. Relative location of entries in the +-- jumplist is preserved at the cost of discarding +-- subsequent entries when navigating backwards in the +-- jumplist and then jumping to a location. +-- |jumplist-stack| +-- +-- view When moving through the jumplist, |changelist|, +-- |alternate-file| or using |mark-motions| try to +-- restore the |mark-view| in which the action occurred. +vim.go.jumpoptions = "" +vim.go.jop = vim.go.jumpoptions +-- `'keymodel'` `'km'` string (default "") +-- global +-- List of comma-separated words, which enable special things that keys +-- can do. These values can be used: +-- startsel Using a shifted special key starts selection (either +-- Select mode or Visual mode, depending on "key" being +-- present in `'selectmode'` ). +-- stopsel Using a not-shifted special key stops selection. +-- Special keys in this context are the cursor keys, , , +-- and . +-- The `'keymodel'` option is set by the |:behave| command. +vim.go.keymodel = "" +vim.go.km = vim.go.keymodel +-- `'langmap'` `'lmap'` string (default "") +-- global +-- This option allows switching your keyboard into a special language +-- mode. When you are typing text in Insert mode the characters are +-- inserted directly. When in Normal mode the `'langmap'` option takes +-- care of translating these special characters to the original meaning +-- of the key. This means you don't have to change the keyboard mode to +-- be able to execute Normal mode commands. +-- This is the opposite of the `'keymap'` option, where characters are +-- mapped in Insert mode. +-- Also consider setting `'langremap'` to off, to prevent `'langmap'` from +-- applying to characters resulting from a mapping. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +-- +-- Example (for Greek, in UTF-8): > +-- :set langmap=ΑA,ΒB,ΨC,ΔD,ΕE,ΦF,ΓG,ΗH,ΙI,ΞJ,ΚK,ΛL,ΜM,ΝN,ΟO,ΠP,QQ,ΡR,ΣS,ΤT,ΘU,ΩV,WW,ΧX,ΥY,ΖZ,αa,βb,ψc,δd,εe,φf,γg,ηh,ιi,ξj,κk,λl,μm,νn,οo,πp,qq,ρr,σs,τt,θu,ωv,ςw,χx,υy,ζz +-- < Example (exchanges meaning of z and y for commands): > +-- :set langmap=zy,yz,ZY,YZ +-- < +-- The `'langmap'` option is a list of parts, separated with commas. Each +-- part can be in one of two forms: +-- 1. A list of pairs. Each pair is a "from" character immediately +-- followed by the "to" character. Examples: "aA", "aAbBcC". +-- 2. A list of "from" characters, a semi-colon and a list of "to" +-- characters. Example: "abc;ABC" +-- Example: "aA,fgh;FGH,cCdDeE" +-- Special characters need to be preceded with a backslash. These are +-- ";", `','` , `'"'` , `'|'` and backslash itself. +-- +-- This will allow you to activate vim actions without having to switch +-- back and forth between the languages. Your language characters will +-- be understood as normal vim English characters (according to the +-- langmap mappings) in the following cases: +-- o Normal/Visual mode (commands, buffer/register names, user mappings) +-- o Insert/Replace Mode: Register names after CTRL-R +-- o Insert/Replace Mode: Mappings +-- Characters entered in Command-line mode will NOT be affected by +-- this option. Note that this option can be changed at any time +-- allowing to switch between mappings for different languages/encodings. +-- Use a mapping to avoid having to type it each time! +vim.go.langmap = "" +vim.go.lmap = vim.go.langmap +-- `'langmenu'` `'lm'` string (default "") +-- global +-- Language to use for menu translation. Tells which file is loaded +-- from the "lang" directory in `'runtimepath'` : > +-- "lang/menu_" .. &langmenu .. ".vim" +-- < (without the spaces). For example, to always use the Dutch menus, no +-- matter what $LANG is set to: > +-- :set langmenu=nl_NL.ISO_8859-1 +-- < When `'langmenu'` is empty, |v:lang| is used. +-- Only normal file name characters can be used, "/\*?[|<>" are illegal. +-- If your $LANG is set to a non-English language but you do want to use +-- the English menus: > +-- :set langmenu=none +-- < This option must be set before loading menus, switching on filetype +-- detection or syntax highlighting. Once the menus are defined setting +-- this option has no effect. But you could do this: > +-- :source $VIMRUNTIME/delmenu.vim +-- :set langmenu=de_DE.ISO_8859-1 +-- :source $VIMRUNTIME/menu.vim +-- < Warning: This deletes all menus that you defined yourself! +vim.go.langmenu = "" +vim.go.lm = vim.go.langmenu +vim.go.langnoremap = true +vim.go.lnr = vim.go.langnoremap +-- `'langremap'` `'lrm'` boolean (default off) +-- global +-- When off, setting `'langmap'` does not apply to characters resulting from +-- a mapping. If setting `'langmap'` disables some of your mappings, make +-- sure this option is off. +vim.go.langremap = false +vim.go.lrm = vim.go.langremap +-- `'laststatus'` `'ls'` number (default 2) +-- global +-- The value of this option influences when the last window will have a +-- status line: +-- 0: never +-- 1: only if there are at least two windows +-- 2: always +-- 3: always and ONLY the last window +-- The screen looks nicer with a status line if you have several +-- windows, but it takes another screen line. |status-line| +vim.go.laststatus = 2 +vim.go.ls = vim.go.laststatus +-- `'lazyredraw'` `'lz'` boolean (default off) +-- global +-- When this option is set, the screen will not be redrawn while +-- executing macros, registers and other commands that have not been +-- typed. Also, updating the window title is postponed. To force an +-- update use |:redraw|. +-- This may occasionally cause display errors. It is only meant to be set +-- temporarily when performing an operation where redrawing may cause +-- flickering or cause a slow down. +vim.go.lazyredraw = false +vim.go.lz = vim.go.lazyredraw +-- `'lines'` number (default 24 or terminal height) +-- global +-- Number of lines of the Vim window. +-- Normally you don't need to set this. It is done automatically by the +-- terminal initialization code. +-- When Vim is running in the GUI or in a resizable window, setting this +-- option will cause the window size to be changed. When you only want +-- to use the size for the GUI, put the command in your |gvimrc| file. +-- Vim limits the number of lines to what fits on the screen. You can +-- use this command to get the tallest window possible: > +-- :set lines=999 +-- < Minimum value is 2, maximum value is 1000. +vim.go.lines = 24 +-- `'linespace'` `'lsp'` number (default 0) +-- global +-- {only in the GUI} +-- Number of pixel lines inserted between characters. Useful if the font +-- uses the full character cell height, making lines touch each other. +-- When non-zero there is room for underlining. +-- With some fonts there can be too much room between lines (to have +-- space for ascents and descents). Then it makes sense to set +-- `'linespace'` to a negative value. This may cause display problems +-- though! +vim.go.linespace = 0 +vim.go.lsp = vim.go.linespace +-- `'loadplugins'` `'lpl'` boolean (default on) +-- global +-- When on the plugin scripts are loaded when starting up |load-plugins|. +-- This option can be reset in your |vimrc| file to disable the loading +-- of plugins. +-- Note that using the "-u NONE" and "--noplugin" command line arguments +-- reset this option. |-u| |--noplugin| +vim.go.loadplugins = true +vim.go.lpl = vim.go.loadplugins +-- `'magic'` boolean (default on) +-- global +-- Changes the special characters that can be used in search patterns. +-- See |pattern|. +-- WARNING: Switching this option off most likely breaks plugins! That +-- is because many patterns assume it's on and will fail when it's off. +-- Only switch it off when working with old Vi scripts. In any other +-- situation write patterns that work when `'magic'` is on. Include "\M" +-- when you want to |/\M|. +vim.go.magic = true +-- `'makeef'` `'mef'` string (default: "") +-- global +-- Name of the errorfile for the |:make| command (see |:make_makeprg|) +-- and the |:grep| command. +-- When it is empty, an internally generated temp file will be used. +-- When "##" is included, it is replaced by a number to make the name +-- unique. This makes sure that the ":make" command doesn't overwrite an +-- existing file. +-- NOT used for the ":cf" command. See `'errorfile'` for that. +-- Environment variables are expanded |:set_env|. +-- See |option-backslash| about including spaces and backslashes. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.makeef = "" +vim.go.mef = vim.go.makeef +-- `'matchtime'` `'mat'` number (default 5) +-- global +-- Tenths of a second to show the matching paren, when `'showmatch'` is +-- set. Note that this is not in milliseconds, like other options that +-- set a time. This is to be compatible with Nvi. +vim.go.matchtime = 5 +vim.go.mat = vim.go.matchtime +vim.go.maxcombine = 6 +vim.go.mco = vim.go.maxcombine +-- `'maxfuncdepth'` `'mfd'` number (default 100) +-- global +-- Maximum depth of function calls for user functions. This normally +-- catches endless recursion. When using a recursive function with +-- more depth, set `'maxfuncdepth'` to a bigger number. But this will use +-- more memory, there is the danger of failing when memory is exhausted. +-- Increasing this limit above 200 also changes the maximum for Ex +-- command recursion, see |E169|. +-- See also |:function|. +vim.go.maxfuncdepth = 100 +vim.go.mfd = vim.go.maxfuncdepth +-- `'maxmapdepth'` `'mmd'` number (default 1000) +-- global +-- Maximum number of times a mapping is done without resulting in a +-- character to be used. This normally catches endless mappings, like +-- ":map x y" with ":map y x". It still does not catch ":map g wg", +-- because the `'w'` is used before the next mapping is done. See also +-- |key-mapping|. +vim.go.maxmapdepth = 1000 +vim.go.mmd = vim.go.maxmapdepth +-- `'maxmempattern'` `'mmp'` number (default 1000) +-- global +-- Maximum amount of memory (in Kbyte) to use for pattern matching. +-- The maximum value is about 2000000. Use this to work without a limit. +-- +-- When Vim runs into the limit it gives an error message and mostly +-- behaves like CTRL-C was typed. +-- Running into the limit often means that the pattern is very +-- inefficient or too complex. This may already happen with the pattern +-- "\(.\)*" on a very long line. ".*" works much better. +-- Might also happen on redraw, when syntax rules try to match a complex +-- text structure. +-- Vim may run out of memory before hitting the `'maxmempattern'` limit, in +-- which case you get an "Out of memory" error instead. +vim.go.maxmempattern = 1000 +vim.go.mmp = vim.go.maxmempattern +-- `'menuitems'` `'mis'` number (default 25) +-- global +-- Maximum number of items to use in a menu. Used for menus that are +-- generated from a list of items, e.g., the Buffers menu. Changing this +-- option has no direct effect, the menu must be refreshed first. +vim.go.menuitems = 25 +vim.go.mis = vim.go.menuitems +-- `'mkspellmem'` `'msm'` string (default "460000,2000,500") +-- global +-- Parameters for |:mkspell|. This tunes when to start compressing the +-- word tree. Compression can be slow when there are many words, but +-- it's needed to avoid running out of memory. The amount of memory used +-- per word depends very much on how similar the words are, that's why +-- this tuning is complicated. +-- +-- There are three numbers, separated by commas: +-- {start},{inc},{added} +-- +-- For most languages the uncompressed word tree fits in memory. {start} +-- gives the amount of memory in Kbyte that can be used before any +-- compression is done. It should be a bit smaller than the amount of +-- memory that is available to Vim. +-- +-- When going over the {start} limit the {inc} number specifies the +-- amount of memory in Kbyte that can be allocated before another +-- compression is done. A low number means compression is done after +-- less words are added, which is slow. A high number means more memory +-- will be allocated. +-- +-- After doing compression, {added} times 1024 words can be added before +-- the {inc} limit is ignored and compression is done when any extra +-- amount of memory is needed. A low number means there is a smaller +-- chance of hitting the {inc} limit, less memory is used but it's +-- slower. +-- +-- The languages for which these numbers are important are Italian and +-- Hungarian. The default works for when you have about 512 Mbyte. If +-- you have 1 Gbyte you could use: > +-- :set mkspellmem=900000,3000,800 +-- < If you have less than 512 Mbyte |:mkspell| may fail for some +-- languages, no matter what you set `'mkspellmem'` to. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|. +vim.go.mkspellmem = "460000,2000,500" +vim.go.msm = vim.go.mkspellmem +-- `'modelineexpr'` `'mle'` boolean (default: off) +-- global +-- When on allow some options that are an expression to be set in the +-- modeline. Check the option for whether it is affected by +-- `'modelineexpr'` . Also see |modeline|. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.modelineexpr = false +vim.go.mle = vim.go.modelineexpr +-- `'modelines'` `'mls'` number (default 5) +-- global +-- If `'modeline'` is on `'modelines'` gives the number of lines that is +-- checked for set commands. If `'modeline'` is off or `'modelines'` is zero +-- no lines are checked. See |modeline|. +vim.go.modelines = 5 +vim.go.mls = vim.go.modelines +-- `'more'` boolean (default: on) +-- global +-- When on, listings pause when the whole screen is filled. You will get +-- the |more-prompt|. When this option is off there are no pauses, the +-- listing continues until finished. +vim.go.more = true +-- `'mouse'` string (default "nvi") +-- global +-- +-- Enables mouse support. For example, to enable the mouse in Normal mode +-- and Visual mode: > +-- :set mouse=nv +-- < +-- To temporarily disable mouse support, hold the shift key while using +-- the mouse. +-- +-- Mouse support can be enabled for different modes: +-- n Normal mode +-- v Visual mode +-- i Insert mode +-- c Command-line mode +-- h all previous modes when editing a help file +-- a all previous modes +-- r for |hit-enter| and |more-prompt| prompt +-- +-- Left-click anywhere in a text buffer to place the cursor there. This +-- works with operators too, e.g. type |d| then left-click to delete text +-- from the current cursor position to the position where you clicked. +-- +-- Drag the |status-line| or vertical separator of a window to resize it. +-- +-- If enabled for "v" (Visual mode) then double-click selects word-wise, +-- triple-click makes it line-wise, and quadruple-click makes it +-- rectangular block-wise. +-- +-- For scrolling with a mouse wheel see |scroll-mouse-wheel|. +-- +-- Note: When enabling the mouse in a terminal, copy/paste will use the +-- "* register if possible. See also `'clipboard'` . +-- +-- Related options: +-- `'mousefocus'` window focus follows mouse pointer +-- `'mousemodel'` what mouse button does which action +-- `'mousehide'` hide mouse pointer while typing text +-- `'selectmode'` whether to start Select mode or Visual mode +-- +-- The :behave command provides some "profiles" for mouse behavior. +-- +-- :be[have] {model} Set behavior for mouse and selection. Valid +-- arguments are: +-- mswin MS-Windows behavior +-- xterm Xterm behavior +-- +-- Using ":behave" changes these options: +-- option mswin xterm ~ +-- `'selectmode'` "mouse,key" "" +-- `'mousemodel'` "popup" "extend" +-- `'keymodel'` "startsel,stopsel" "" +-- `'selection'` "exclusive" "inclusive" +vim.go.mouse = "nvi" +-- `'mousefocus'` `'mousef'` boolean (default off) +-- global +-- The window that the mouse pointer is on is automatically activated. +-- When changing the window layout or window focus in another way, the +-- mouse pointer is moved to the window with keyboard focus. Off is the +-- default because it makes using the pull down menus a little goofy, as +-- a pointer transit may activate a window unintentionally. +vim.go.mousefocus = false +vim.go.mousef = vim.go.mousefocus +-- `'mousehide'` `'mh'` boolean (default on) +-- global +-- {only works in the GUI} +-- When on, the mouse pointer is hidden when characters are typed. +-- The mouse pointer is restored when the mouse is moved. +vim.go.mousehide = true +vim.go.mh = vim.go.mousehide +-- `'mousemodel'` `'mousem'` string (default "popup_setpos") +-- global +-- Sets the model to use for the mouse. The name mostly specifies what +-- the right mouse button is used for: +-- extend Right mouse button extends a selection. This works +-- like in an xterm. +-- popup Right mouse button pops up a menu. The shifted left +-- mouse button extends a selection. This works like +-- with Microsoft Windows. +-- popup_setpos Like "popup", but the cursor will be moved to the +-- position where the mouse was clicked, and thus the +-- selected operation will act upon the clicked object. +-- If clicking inside a selection, that selection will +-- be acted upon, i.e. no cursor move. This implies of +-- course, that right clicking outside a selection will +-- end Visual mode. +-- Overview of what button does what for each model: +-- mouse extend popup(_setpos) ~ +-- left click place cursor place cursor +-- left drag start selection start selection +-- shift-left search word extend selection +-- right click extend selection popup menu (place cursor) +-- right drag extend selection - +-- middle click paste paste +-- +-- In the "popup" model the right mouse button produces a pop-up menu. +-- Nvim creates a default |popup-menu| but you can redefine it. +-- +-- Note that you can further refine the meaning of buttons with mappings. +-- See |mouse-overview|. But mappings are NOT used for modeless selection. +-- +-- Example: > +-- :map +-- :map +-- :map +-- :map <2-S-LeftMouse> <2-RightMouse> +-- :map <2-S-LeftDrag> <2-RightDrag> +-- :map <2-S-LeftRelease> <2-RightRelease> +-- :map <3-S-LeftMouse> <3-RightMouse> +-- :map <3-S-LeftDrag> <3-RightDrag> +-- :map <3-S-LeftRelease> <3-RightRelease> +-- :map <4-S-LeftMouse> <4-RightMouse> +-- :map <4-S-LeftDrag> <4-RightDrag> +-- :map <4-S-LeftRelease> <4-RightRelease> +-- < +-- Mouse commands requiring the CTRL modifier can be simulated by typing +-- the "g" key before using the mouse: +-- "g" is " (jump to tag under mouse click) +-- "g" is " ("CTRL-T") +-- +-- The `'mousemodel'` option is set by the |:behave| command. +vim.go.mousemodel = "popup_setpos" +vim.go.mousem = vim.go.mousemodel +-- `'mousemoveevent'` `'mousemev'` boolean (default off) +-- global +-- When on, mouse move events are delivered to the input queue and are +-- available for mapping. The default, off, avoids the mouse movement +-- overhead except when needed. +-- Warning: Setting this option can make pending mappings to be aborted +-- when the mouse is moved. +vim.go.mousemoveevent = false +vim.go.mousemev = vim.go.mousemoveevent +-- `'mousescroll'` string (default "ver:3,hor:6") +-- global +-- This option controls the number of lines / columns to scroll by when +-- scrolling with a mouse. The option is a comma separated list of parts. +-- Each part consists of a direction and a count as follows: +-- direction:count,direction:count +-- Direction is one of either "hor" or "ver". "hor" controls horizontal +-- scrolling and "ver" controls vertical scrolling. Count sets the amount +-- to scroll by for the given direction, it should be a non negative +-- integer. Each direction should be set at most once. If a direction +-- is omitted, a default value is used (6 for horizontal scrolling and 3 +-- for vertical scrolling). You can disable mouse scrolling by using +-- a count of 0. +-- +-- Example: > +-- :set mousescroll=ver:5,hor:2 +-- < Will make Nvim scroll 5 lines at a time when scrolling vertically, and +-- scroll 2 columns at a time when scrolling horizontally. +vim.go.mousescroll = "ver:3,hor:6" +-- `'mouseshape'` `'mouses'` string (default "i:beam,r:beam,s:updown,sd:cross, +-- m:no,ml:up-arrow,v:rightup-arrow") +-- global +-- This option tells Vim what the mouse pointer should look like in +-- different modes. The option is a comma-separated list of parts, much +-- like used for `'guicursor'` . Each part consist of a mode/location-list +-- and an argument-list: +-- mode-list:shape,mode-list:shape,.. +-- The mode-list is a dash separated list of these modes/locations: +-- In a normal window: ~ +-- n Normal mode +-- v Visual mode +-- ve Visual mode with `'selection'` "exclusive" (same as `'v'` , +-- if not specified) +-- o Operator-pending mode +-- i Insert mode +-- r Replace mode +-- +-- Others: ~ +-- c appending to the command-line +-- ci inserting in the command-line +-- cr replacing in the command-line +-- m at the 'Hit ENTER' or `'More'` prompts +-- ml idem, but cursor in the last line +-- e any mode, pointer below last window +-- s any mode, pointer on a status line +-- sd any mode, while dragging a status line +-- vs any mode, pointer on a vertical separator line +-- vd any mode, while dragging a vertical separator line +-- a everywhere +-- +-- The shape is one of the following: +-- avail name looks like ~ +-- w x arrow Normal mouse pointer +-- w x blank no pointer at all (use with care!) +-- w x beam I-beam +-- w x updown up-down sizing arrows +-- w x leftright left-right sizing arrows +-- w x busy The system's usual busy pointer +-- w x no The system's usual "no input" pointer +-- x udsizing indicates up-down resizing +-- x lrsizing indicates left-right resizing +-- x crosshair like a big thin + +-- x hand1 black hand +-- x hand2 white hand +-- x pencil what you write with +-- x question big ? +-- x rightup-arrow arrow pointing right-up +-- w x up-arrow arrow pointing up +-- x any X11 pointer number (see X11/cursorfont.h) +-- +-- The "avail" column contains a `'w'` if the shape is available for Win32, +-- x for X11. +-- Any modes not specified or shapes not available use the normal mouse +-- pointer. +-- +-- Example: > +-- :set mouseshape=s:udsizing,m:no +-- < will make the mouse turn to a sizing arrow over the status lines and +-- indicate no input when the hit-enter prompt is displayed (since +-- clicking the mouse has no effect in this state.) +vim.go.mouseshape = "" +vim.go.mouses = vim.go.mouseshape +-- `'mousetime'` `'mouset'` number (default 500) +-- global +-- Defines the maximum time in msec between two mouse clicks for the +-- second click to be recognized as a multi click. +vim.go.mousetime = 500 +vim.go.mouset = vim.go.mousetime +-- `'opendevice'` `'odev'` boolean (default off) +-- global +-- {only for Windows} +-- Enable reading and writing from devices. This may get Vim stuck on a +-- device that can be opened but doesn't actually do the I/O. Therefore +-- it is off by default. +-- Note that on Windows editing "aux.h", "lpt1.txt" and the like also +-- result in editing a device. +vim.go.opendevice = false +vim.go.odev = vim.go.opendevice +-- `'operatorfunc'` `'opfunc'` string (default: empty) +-- global +-- This option specifies a function to be called by the |g@| operator. +-- See |:map-operator| for more info and an example. The value can be +-- the name of a function, a |lambda| or a |Funcref|. See +-- |option-value-function| for more information. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.operatorfunc = "" +vim.go.opfunc = vim.go.operatorfunc +-- `'packpath'` `'pp'` string (default: see `'runtimepath'` ) +-- Directories used to find packages. See |packages| and |rtp-packages|. +vim.go.packpath = "/home/runner/.config/nvim,/etc/xdg/nvim,/home/runner/.local/share/nvim/site,/usr/local/share/nvim/site,/usr/share/nvim/site,/tmp/nvim/squashfs-root/usr/share/nvim/runtime,/tmp/nvim/squashfs-root/usr/lib/nvim,/usr/share/nvim/site/after,/usr/local/share/nvim/site/after,/home/runner/.local/share/nvim/site/after,/etc/xdg/nvim/after,/home/runner/.config/nvim/after" +vim.go.pp = vim.go.packpath +-- `'paragraphs'` `'para'` string (default "IPLPPPQPP TPHPLIPpLpItpplpipbp") +-- global +-- Specifies the nroff macros that separate paragraphs. These are pairs +-- of two letters (see |object-motions|). +vim.go.paragraphs = "IPLPPPQPP TPHPLIPpLpItpplpipbp" +vim.go.para = vim.go.paragraphs +vim.go.paste = false +vim.go.pastetoggle = "" +vim.go.pt = vim.go.pastetoggle +-- `'patchexpr'` `'pex'` string (default "") +-- global +-- Expression which is evaluated to apply a patch to a file and generate +-- the resulting new version of the file. See |diff-patchexpr|. +vim.go.patchexpr = "" +vim.go.pex = vim.go.patchexpr +-- `'patchmode'` `'pm'` string (default "") +-- global +-- When non-empty the oldest version of a file is kept. This can be used +-- to keep the original version of a file if you are changing files in a +-- source distribution. Only the first time that a file is written a +-- copy of the original file will be kept. The name of the copy is the +-- name of the original file with the string in the `'patchmode'` option +-- appended. This option should start with a dot. Use a string like +-- ".orig" or ".org". `'backupdir'` must not be empty for this to work +-- (Detail: The backup file is renamed to the patchmode file after the +-- new file has been successfully written, that's why it must be possible +-- to write a backup file). If there was no file to be backed up, an +-- empty file is created. +-- When the `'backupskip'` pattern matches, a patchmode file is not made. +-- Using `'patchmode'` for compressed files appends the extension at the +-- end (e.g., "file.gz.orig"), thus the resulting name isn't always +-- recognized as a compressed file. +-- Only normal file name characters can be used, "/\*?[|<>" are illegal. +vim.go.patchmode = "" +vim.go.pm = vim.go.patchmode +-- `'previewheight'` `'pvh'` number (default 12) +-- global +-- Default height for a preview window. Used for |:ptag| and associated +-- commands. Used for |CTRL-W_}| when no count is given. +vim.go.previewheight = 12 +vim.go.pvh = vim.go.previewheight +vim.go.prompt = true +-- `'pumblend'` `'pb'` number (default 0) +-- global +-- Enables pseudo-transparency for the |popup-menu|. Valid values are in +-- the range of 0 for fully opaque popupmenu (disabled) to 100 for fully +-- transparent background. Values between 0-30 are typically most useful. +-- +-- It is possible to override the level for individual highlights within +-- the popupmenu using |highlight-blend|. For instance, to enable +-- transparency but force the current selected element to be fully opaque: > +-- +-- :set pumblend=15 +-- :hi PmenuSel blend=0 +-- < +-- UI-dependent. Works best with RGB colors. `'termguicolors'` +vim.go.pumblend = 0 +vim.go.pb = vim.go.pumblend +-- `'pumheight'` `'ph'` number (default 0) +-- global +-- Maximum number of items to show in the popup menu +-- (|ins-completion-menu|). Zero means "use available screen space". +vim.go.pumheight = 0 +vim.go.ph = vim.go.pumheight +-- `'pumwidth'` `'pw'` number (default 15) +-- global +-- Minimum width for the popup menu (|ins-completion-menu|). If the +-- cursor column + `'pumwidth'` exceeds screen width, the popup menu is +-- nudged to fit on the screen. +vim.go.pumwidth = 15 +vim.go.pw = vim.go.pumwidth +-- `'pyxversion'` `'pyx'` number (default 3) +-- global +-- Specifies the python version used for pyx* functions and commands +-- |python_x|. As only Python 3 is supported, this always has the value +-- `3`. Setting any other value is an error. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.pyxversion = 3 +vim.go.pyx = vim.go.pyxversion +-- `'quickfixtextfunc'` `'qftf'` string (default "") +-- global +-- This option specifies a function to be used to get the text to display +-- in the quickfix and location list windows. This can be used to +-- customize the information displayed in the quickfix or location window +-- for each entry in the corresponding quickfix or location list. See +-- |quickfix-window-function| for an explanation of how to write the +-- function and an example. The value can be the name of a function, a +-- |lambda| or a |Funcref|. See |option-value-function| for more +-- information. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.quickfixtextfunc = "" +vim.go.qftf = vim.go.quickfixtextfunc +-- `'redrawdebug'` `'rdb'` string (default `''` ) +-- global +-- Flags to change the way redrawing works, for debugging purposes. +-- Most useful with `'writedelay'` set to some reasonable value. +-- Supports the following flags: +-- compositor Indicate each redraw event handled by the compositor +-- by briefly flashing the redrawn regions in colors +-- indicating the redraw type. These are the highlight +-- groups used (and their default colors): +-- RedrawDebugNormal gui=reverse normal redraw passed through +-- RedrawDebugClear guibg=Yellow clear event passed through +-- RedrawDebugComposed guibg=Green redraw event modified by the +-- compositor (due to +-- overlapping grids, etc) +-- RedrawDebugRecompose guibg=Red redraw generated by the +-- compositor itself, due to a +-- grid being moved or deleted. +-- line introduce a delay after each line drawn on the screen. +-- When using the TUI or another single-grid UI, "compositor" +-- gives more information and should be preferred (every +-- line is processed as a separate event by the compositor) +-- flush introduce a delay after each "flush" event. +-- nothrottle Turn off throttling of the message grid. This is an +-- optimization that joins many small scrolls to one +-- larger scroll when drawing the message area (with +-- `'display'` msgsep flag active). +-- invalid Enable stricter checking (abort) of inconsistencies +-- of the internal screen state. This is mostly +-- useful when running nvim inside a debugger (and +-- the test suite). +-- nodelta Send all internally redrawn cells to the UI, even if +-- they are unchanged from the already displayed state. +vim.go.redrawdebug = "" +vim.go.rdb = vim.go.redrawdebug +-- `'redrawtime'` `'rdt'` number (default 2000) +-- global +-- Time in milliseconds for redrawing the display. Applies to +-- `'hlsearch'` , `'inccommand'` , |:match| highlighting and syntax +-- highlighting. +-- When redrawing takes more than this many milliseconds no further +-- matches will be highlighted. +-- For syntax highlighting the time applies per window. When over the +-- limit syntax highlighting is disabled until |CTRL-L| is used. +-- This is used to avoid that Vim hangs when using a very complicated +-- pattern. +vim.go.redrawtime = 2000 +vim.go.rdt = vim.go.redrawtime +-- `'regexpengine'` `'re'` number (default 0) +-- global +-- This selects the default regexp engine. |two-engines| +-- The possible values are: +-- 0 automatic selection +-- 1 old engine +-- 2 NFA engine +-- Note that when using the NFA engine and the pattern contains something +-- that is not supported the pattern will not match. This is only useful +-- for debugging the regexp engine. +-- Using automatic selection enables Vim to switch the engine, if the +-- default engine becomes too costly. E.g., when the NFA engine uses too +-- many states. This should prevent Vim from hanging on a combination of +-- a complex pattern with long text. +vim.go.regexpengine = 0 +vim.go.re = vim.go.regexpengine +vim.go.remap = true +-- `'report'` number (default 2) +-- global +-- Threshold for reporting number of lines changed. When the number of +-- changed lines is more than `'report'` a message will be given for most +-- ":" commands. If you want it always, set `'report'` to 0. +-- For the ":substitute" command the number of substitutions is used +-- instead of the number of lines. +vim.go.report = 2 +-- `'revins'` `'ri'` boolean (default off) +-- global +-- Inserting characters in Insert mode will work backwards. See "typing +-- backwards" |ins-reverse|. This option can be toggled with the CTRL-_ +-- command in Insert mode, when `'allowrevins'` is set. +vim.go.revins = false +vim.go.ri = vim.go.revins +-- `'ruler'` `'ru'` boolean (default on) +-- global +-- Show the line and column number of the cursor position, separated by a +-- comma. When there is room, the relative position of the displayed +-- text in the file is shown on the far right: +-- Top first line is visible +-- Bot last line is visible +-- All first and last line are visible +-- 45% relative position in the file +-- If `'rulerformat'` is set, it will determine the contents of the ruler. +-- Each window has its own ruler. If a window has a status line, the +-- ruler is shown there. If a window doesn't have a status line and +-- `'cmdheight'` is zero, the ruler is not shown. Otherwise it is shown in +-- the last line of the screen. If the statusline is given by +-- `'statusline'` (i.e. not empty), this option takes precedence over +-- `'ruler'` and `'rulerformat'` . +-- If the number of characters displayed is different from the number of +-- bytes in the text (e.g., for a TAB or a multibyte character), both +-- the text column (byte number) and the screen column are shown, +-- separated with a dash. +-- For an empty line "0-1" is shown. +-- For an empty buffer the line number will also be zero: "0,0-1". +-- If you don't want to see the ruler all the time but want to know where +-- you are, use "g CTRL-G" |g_CTRL-G|. +vim.go.ruler = true +vim.go.ru = vim.go.ruler +-- `'rulerformat'` `'ruf'` string (default empty) +-- global +-- When this option is not empty, it determines the content of the ruler +-- string, as displayed for the `'ruler'` option. +-- The format of this option is like that of `'statusline'` . +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- The default ruler width is 17 characters. To make the ruler 15 +-- characters wide, put "%15(" at the start and "%)" at the end. +-- Example: > +-- :set rulerformat=%15(%c%V\ %p%%%) +-- < +vim.go.rulerformat = "" +vim.go.ruf = vim.go.rulerformat +-- `'runtimepath'` `'rtp'` string (default: "$XDG_CONFIG_HOME/nvim, +-- $XDG_CONFIG_DIRS[1]/nvim, +-- $XDG_CONFIG_DIRS[2]/nvim, +-- … +-- $XDG_DATA_HOME/nvim[-data]/site, +-- $XDG_DATA_DIRS[1]/nvim/site, +-- $XDG_DATA_DIRS[2]/nvim/site, +-- … +-- $VIMRUNTIME, +-- … +-- $XDG_DATA_DIRS[2]/nvim/site/after, +-- $XDG_DATA_DIRS[1]/nvim/site/after, +-- $XDG_DATA_HOME/nvim[-data]/site/after, +-- … +-- $XDG_CONFIG_DIRS[2]/nvim/after, +-- $XDG_CONFIG_DIRS[1]/nvim/after, +-- $XDG_CONFIG_HOME/nvim/after") +-- global +-- List of directories to be searched for these runtime files: +-- filetype.lua filetypes |new-filetype| +-- autoload/ automatically loaded scripts |autoload-functions| +-- colors/ color scheme files |:colorscheme| +-- compiler/ compiler files |:compiler| +-- doc/ documentation |write-local-help| +-- ftplugin/ filetype plugins |write-filetype-plugin| +-- indent/ indent scripts |indent-expression| +-- keymap/ key mapping files |mbyte-keymap| +-- lang/ menu translations |:menutrans| +-- lua/ |Lua| plugins +-- menu.vim GUI menus |menu.vim| +-- pack/ packages |:packadd| +-- parser/ |treesitter| syntax parsers +-- plugin/ plugin scripts |write-plugin| +-- queries/ |treesitter| queries +-- rplugin/ |remote-plugin| scripts +-- spell/ spell checking files |spell| +-- syntax/ syntax files |mysyntaxfile| +-- tutor/ tutorial files |:Tutor| +-- +-- And any other file searched for with the |:runtime| command. +-- +-- Defaults are setup to search these locations: +-- 1. Your home directory, for personal preferences. +-- Given by `stdpath("config")`. |$XDG_CONFIG_HOME| +-- 2. Directories which must contain configuration files according to +-- |xdg| ($XDG_CONFIG_DIRS, defaults to /etc/xdg). This also contains +-- preferences from system administrator. +-- 3. Data home directory, for plugins installed by user. +-- Given by `stdpath("data")/site`. |$XDG_DATA_HOME| +-- 4. nvim/site subdirectories for each directory in $XDG_DATA_DIRS. +-- This is for plugins which were installed by system administrator, +-- but are not part of the Nvim distribution. XDG_DATA_DIRS defaults +-- to /usr/local/share/:/usr/share/, so system administrators are +-- expected to install site plugins to /usr/share/nvim/site. +-- 5. Session state directory, for state data such as swap, backupdir, +-- viewdir, undodir, etc. +-- Given by `stdpath("state")`. |$XDG_STATE_HOME| +-- 6. $VIMRUNTIME, for files distributed with Nvim. +-- +-- 7, 8, 9, 10. In after/ subdirectories of 1, 2, 3 and 4, with reverse +-- ordering. This is for preferences to overrule or add to the +-- distributed defaults or system-wide settings (rarely needed). +-- +-- +-- "start" packages will also be searched (|runtime-search-path|) for +-- runtime files after these, though such packages are not explicitly +-- reported in &runtimepath. But "opt" packages are explicitly added to +-- &runtimepath by |:packadd|. +-- +-- Note that, unlike `'path'` , no wildcards like "" are allowed. Normal +-- wildcards are allowed, but can significantly slow down searching for +-- runtime files. For speed, use as few items as possible and avoid +-- wildcards. +-- See |:runtime|. +-- Example: > +-- :set runtimepath=~/vimruntime,/mygroup/vim,$VIMRUNTIME +-- < This will use the directory "~/vimruntime" first (containing your +-- personal Nvim runtime files), then "/mygroup/vim", and finally +-- "$VIMRUNTIME" (the default runtime files). +-- You can put a directory before $VIMRUNTIME to find files which replace +-- distributed runtime files. You can put a directory after $VIMRUNTIME +-- to find files which add to distributed runtime files. +-- +-- With |--clean| the home directory entries are not included. +vim.go.runtimepath = "/home/runner/.config/nvim,/etc/xdg/nvim,/home/runner/.local/share/nvim/site,/usr/local/share/nvim/site,/usr/share/nvim/site,/tmp/nvim/squashfs-root/usr/share/nvim/runtime,/tmp/nvim/squashfs-root/usr/lib/nvim,/usr/share/nvim/site/after,/usr/local/share/nvim/site/after,/home/runner/.local/share/nvim/site/after,/etc/xdg/nvim/after,/home/runner/.config/nvim/after" +vim.go.rtp = vim.go.runtimepath +-- `'scrolljump'` `'sj'` number (default 1) +-- global +-- Minimal number of lines to scroll when the cursor gets off the +-- screen (e.g., with "j"). Not used for scroll commands (e.g., CTRL-E, +-- CTRL-D). Useful if your terminal scrolls very slowly. +-- When set to a negative number from -1 to -100 this is used as the +-- percentage of the window height. Thus -50 scrolls half the window +-- height. +vim.go.scrolljump = 1 +vim.go.sj = vim.go.scrolljump +-- `'scrollopt'` `'sbo'` string (default "ver,jump") +-- global +-- This is a comma-separated list of words that specifies how +-- `'scrollbind'` windows should behave. `'sbo'` stands for ScrollBind +-- Options. +-- The following words are available: +-- ver Bind vertical scrolling for `'scrollbind'` windows +-- hor Bind horizontal scrolling for `'scrollbind'` windows +-- jump Applies to the offset between two windows for vertical +-- scrolling. This offset is the difference in the first +-- displayed line of the bound windows. When moving +-- around in a window, another `'scrollbind'` window may +-- reach a position before the start or after the end of +-- the buffer. The offset is not changed though, when +-- moving back the `'scrollbind'` window will try to scroll +-- to the desired position when possible. +-- When now making that window the current one, two +-- things can be done with the relative offset: +-- 1. When "jump" is not included, the relative offset is +-- adjusted for the scroll position in the new current +-- window. When going back to the other window, the +-- new relative offset will be used. +-- 2. When "jump" is included, the other windows are +-- scrolled to keep the same relative offset. When +-- going back to the other window, it still uses the +-- same relative offset. +-- Also see |scroll-binding|. +-- When `'diff'` mode is active there always is vertical scroll binding, +-- even when "ver" isn't there. +vim.go.scrollopt = "ver,jump" +vim.go.sbo = vim.go.scrollopt +-- `'sections'` `'sect'` string (default "SHNHH HUnhsh") +-- global +-- Specifies the nroff macros that separate sections. These are pairs of +-- two letters (See |object-motions|). The default makes a section start +-- at the nroff macros ".SH", ".NH", ".H", ".HU", ".nh" and ".sh". +vim.go.sections = "SHNHH HUnhsh" +vim.go.sect = vim.go.sections +vim.go.secure = false +-- `'selection'` `'sel'` string (default "inclusive") +-- global +-- This option defines the behavior of the selection. It is only used +-- in Visual and Select mode. +-- Possible values: +-- value past line inclusive ~ +-- old no yes +-- inclusive yes yes +-- exclusive yes no +-- "past line" means that the cursor is allowed to be positioned one +-- character past the line. +-- "inclusive" means that the last character of the selection is included +-- in an operation. For example, when "x" is used to delete the +-- selection. +-- When "old" is used and `'virtualedit'` allows the cursor to move past +-- the end of line the line break still isn't included. +-- Note that when "exclusive" is used and selecting from the end +-- backwards, you cannot include the last character of a line, when +-- starting in Normal mode and `'virtualedit'` empty. +-- +-- The `'selection'` option is set by the |:behave| command. +vim.go.selection = "inclusive" +vim.go.sel = vim.go.selection +-- `'selectmode'` `'slm'` string (default "") +-- global +-- This is a comma-separated list of words, which specifies when to start +-- Select mode instead of Visual mode, when a selection is started. +-- Possible values: +-- mouse when using the mouse +-- key when using shifted special keys +-- cmd when using "v", "V" or CTRL-V +-- See |Select-mode|. +-- The `'selectmode'` option is set by the |:behave| command. +vim.go.selectmode = "" +vim.go.slm = vim.go.selectmode +-- `'sessionoptions'` `'ssop'` string (default: "blank,buffers,curdir,folds, +-- help,tabpages,winsize,terminal") +-- global +-- Changes the effect of the |:mksession| command. It is a comma- +-- separated list of words. Each word enables saving and restoring +-- something: +-- word save and restore ~ +-- blank empty windows +-- buffers hidden and unloaded buffers, not just those in windows +-- curdir the current directory +-- folds manually created folds, opened/closed folds and local +-- fold options +-- globals global variables that start with an uppercase letter +-- and contain at least one lowercase letter. Only +-- String and Number types are stored. +-- help the help window +-- localoptions options and mappings local to a window or buffer (not +-- global values for local options) +-- options all options and mappings (also global values for local +-- options) +-- skiprtp exclude `'runtimepath'` and `'packpath'` from the options +-- resize size of the Vim window: `'lines'` and `'columns'` +-- sesdir the directory in which the session file is located +-- will become the current directory (useful with +-- projects accessed over a network from different +-- systems) +-- tabpages all tab pages; without this only the current tab page +-- is restored, so that you can make a session for each +-- tab page separately +-- terminal include terminal windows where the command can be +-- restored +-- winpos position of the whole Vim window +-- winsize window sizes +-- slash |deprecated| Always enabled. Uses "/" in filenames. +-- unix |deprecated| Always enabled. Uses "\n" line endings. +-- +-- Don't include both "curdir" and "sesdir". When neither is included +-- filenames are stored as absolute paths. +-- If you leave out "options" many things won't work well after restoring +-- the session. +vim.go.sessionoptions = "blank,buffers,curdir,folds,help,tabpages,winsize,terminal" +vim.go.ssop = vim.go.sessionoptions +-- `'shada'` `'sd'` string (default for +-- Win32: !,'100,<50,s10,h,rA:,rB: +-- others: !,'100,<50,s10,h) +-- global +-- When non-empty, the shada file is read upon startup and written +-- when exiting Vim (see |shada-file|). The string should be a comma- +-- separated list of parameters, each consisting of a single character +-- identifying the particular parameter, followed by a number or string +-- which specifies the value of that parameter. If a particular +-- character is left out, then the default value is used for that +-- parameter. The following is a list of the identifying characters and +-- the effect of their value. +-- CHAR VALUE ~ +-- +-- ! When included, save and restore global variables that start +-- with an uppercase letter, and don't contain a lowercase +-- letter. Thus "KEEPTHIS and "K_L_M" are stored, but "KeepThis" +-- and "_K_L_M" are not. Nested List and Dict items may not be +-- read back correctly, you end up with an empty item. +-- +-- " Maximum number of lines saved for each register. Old name of +-- the `'<'` item, with the disadvantage that you need to put a +-- backslash before the ", otherwise it will be recognized as the +-- start of a comment! +-- +-- % When included, save and restore the buffer list. If Vim is +-- started with a file name argument, the buffer list is not +-- restored. If Vim is started without a file name argument, the +-- buffer list is restored from the shada file. Quickfix +-- (`'buftype'` ), unlisted (`'buflisted'` ), unnamed and buffers on +-- removable media (|shada-r|) are not saved. +-- When followed by a number, the number specifies the maximum +-- number of buffers that are stored. Without a number all +-- buffers are stored. +-- +-- ' Maximum number of previously edited files for which the marks +-- are remembered. This parameter must always be included when +-- `'shada'` is non-empty. +-- Including this item also means that the |jumplist| and the +-- |changelist| are stored in the shada file. +-- +-- / Maximum number of items in the search pattern history to be +-- saved. If non-zero, then the previous search and substitute +-- patterns are also saved. When not included, the value of +-- `'history'` is used. +-- +-- : Maximum number of items in the command-line history to be +-- saved. When not included, the value of `'history'` is used. +-- +-- < Maximum number of lines saved for each register. If zero then +-- registers are not saved. When not included, all lines are +-- saved. `'"'` is the old name for this item. +-- Also see the `'s'` item below: limit specified in KiB. +-- +-- @ Maximum number of items in the input-line history to be +-- saved. When not included, the value of `'history'` is used. +-- +-- c Dummy option, kept for compatibility reasons. Has no actual +-- effect: ShaDa always uses UTF-8 and `'encoding'` value is fixed +-- to UTF-8 as well. +-- +-- f Whether file marks need to be stored. If zero, file marks ('0 +-- to '9, 'A to 'Z) are not stored. When not present or when +-- non-zero, they are all stored. '0 is used for the current +-- cursor position (when exiting or when doing |:wshada|). +-- +-- h Disable the effect of `'hlsearch'` when loading the shada +-- file. When not included, it depends on whether ":nohlsearch" +-- has been used since the last search command. +-- +-- n Name of the shada file. The name must immediately follow +-- the `'n'` . Must be at the end of the option! If the +-- `'shadafile'` option is set, that file name overrides the one +-- given here with `'shada'` . Environment variables are +-- expanded when opening the file, not when setting the option. +-- +-- r Removable media. The argument is a string (up to the next +-- `','` ). This parameter can be given several times. Each +-- specifies the start of a path for which no marks will be +-- stored. This is to avoid removable media. For Windows you +-- could use "ra:,rb:". You can also use it for temp files, +-- e.g., for Unix: "r/tmp". Case is ignored. +-- +-- s Maximum size of an item contents in KiB. If zero then nothing +-- is saved. Unlike Vim this applies to all items, except for +-- the buffer list and header. Full item size is off by three +-- unsigned integers: with `s10` maximum item size may be 1 byte +-- (type: 7-bit integer) + 9 bytes (timestamp: up to 64-bit +-- integer) + 3 bytes (item size: up to 16-bit integer because +-- 2^8 < 10240 < 2^16) + 10240 bytes (requested maximum item +-- contents size) = 10253 bytes. +-- +-- Example: > +-- :set shada='50,<1000,s100,:0,n~/nvim/shada +-- < +-- '50 Marks will be remembered for the last 50 files you +-- edited. +-- <1000 Contents of registers (up to 1000 lines each) will be +-- remembered. +-- s100 Items with contents occupying more then 100 KiB are +-- skipped. +-- :0 Command-line history will not be saved. +-- n~/nvim/shada The name of the file to use is "~/nvim/shada". +-- no / Since `'/'` is not specified, the default will be used, +-- that is, save all of the search history, and also the +-- previous search and substitute patterns. +-- no % The buffer list will not be saved nor read back. +-- no h `'hlsearch'` highlighting will be restored. +-- +-- When setting `'shada'` from an empty value you can use |:rshada| to +-- load the contents of the file, this is not done automatically. +-- +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.shada = "!,'100,<50,s10,h" +vim.go.sd = vim.go.shada +-- `'shadafile'` `'sdf'` string (default: "") +-- global +-- When non-empty, overrides the file name used for |shada| (viminfo). +-- When equal to "NONE" no shada file will be read or written. +-- This option can be set with the |-i| command line flag. The |--clean| +-- command line flag sets it to "NONE". +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.shadafile = "" +vim.go.sdf = vim.go.shadafile +-- `'shell'` `'sh'` string (default $SHELL or "sh", Win32: "cmd.exe") +-- global +-- Name of the shell to use for ! and :! commands. When changing the +-- value also check these options: `'shellpipe'` , `'shellslash'` +-- `'shellredir'` , `'shellquote'` , `'shellxquote'` and `'shellcmdflag'` . +-- It is allowed to give an argument to the command, e.g. "csh -f". +-- See |option-backslash| about including spaces and backslashes. +-- Environment variables are expanded |:set_env|. +-- +-- If the name of the shell contains a space, you need to enclose it in +-- quotes. Example with quotes: > +-- :set shell=\"c:\program\ files\unix\sh.exe\"\ -f +-- < Note the backslash before each quote (to avoid starting a comment) and +-- each space (to avoid ending the option value), so better use |:let-&| +-- like this: > +-- :let &shell='"C:\Program Files\unix\sh.exe" -f' +-- < Also note that the "-f" is not inside the quotes, because it is not +-- part of the command name. +-- +-- Rules regarding quotes: +-- 1. Option is split on space and tab characters that are not inside +-- quotes: "abc def" runs shell named "abc" with additional argument +-- "def", '"abc def"' runs shell named "abc def" with no additional +-- arguments (here and below: additional means “additional to +-- `'shellcmdflag'` ”). +-- 2. Quotes in option may be present in any position and any number: +-- `'"abc"'` , `'"a"bc'` , `'a"b"c'` , `'ab"c"'` and `'"a"b"c"'` are all equivalent +-- to just "abc". +-- 3. Inside quotes backslash preceding backslash means one backslash. +-- Backslash preceding quote means one quote. Backslash preceding +-- anything else means backslash and next character literally: +-- `'"a\\b"'` is the same as "a\b", `'"a\\"b"'` runs shell named literally +-- `'a"b'` , `'"a\b"'` is the same as "a\b" again. +-- 4. Outside of quotes backslash always means itself, it cannot be used +-- to escape quote: `'a\"b"'` is the same as "a\b". +-- Note that such processing is done after |:set| did its own round of +-- unescaping, so to keep yourself sane use |:let-&| like shown above. +-- +-- To use PowerShell: > +-- let &shell = executable(`'pwsh'` ) ? `'pwsh'` : `'powershell'` +-- let &shellcmdflag = '-NoLogo -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();$PSDefaultParameterValues[`''` Out-File:Encoding`''` ]=`''` utf8`''` ;' +-- let &shellredir = '2>&1 | %%{ "$_" } | Out-File %s; exit $LastExitCode' +-- let &shellpipe = '2>&1 | %%{ "$_" } | Tee-Object %s; exit $LastExitCode' +-- set shellquote= shellxquote= +-- +-- < This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.shell = "sh" +vim.go.sh = vim.go.shell +-- `'shellcmdflag'` `'shcf'` string (default: "-c"; Windows: "/s /c") +-- global +-- Flag passed to the shell to execute "!" and ":!" commands; e.g., +-- `bash.exe -c ls` or `cmd.exe /s /c "dir"`. For MS-Windows, the +-- default is set according to the value of `'shell'` , to reduce the need +-- to set this option by the user. +-- On Unix it can have more than one flag. Each white space separated +-- part is passed as an argument to the shell command. +-- See |option-backslash| about including spaces and backslashes. +-- See |shell-unquoting| which talks about separating this option into +-- multiple arguments. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.shellcmdflag = "-c" +vim.go.shcf = vim.go.shellcmdflag +-- `'shellpipe'` `'sp'` string (default ">", "| tee", "|& tee" or "2>&1| tee") +-- global +-- String to be used to put the output of the ":make" command in the +-- error file. See also |:make_makeprg|. See |option-backslash| about +-- including spaces and backslashes. +-- The name of the temporary file can be represented by "%s" if necessary +-- (the file name is appended automatically if no %s appears in the value +-- of this option). +-- For MS-Windows the default is "2>&1| tee". The stdout and stderr are +-- saved in a file and echoed to the screen. +-- For Unix the default is "| tee". The stdout of the compiler is saved +-- in a file and echoed to the screen. If the `'shell'` option is "csh" or +-- "tcsh" after initializations, the default becomes "|& tee". If the +-- `'shell'` option is "sh", "ksh", "mksh", "pdksh", "zsh", "zsh-beta", +-- "bash", "fish", "ash" or "dash" the default becomes "2>&1| tee". This +-- means that stderr is also included. Before using the `'shell'` option a +-- path is removed, thus "/bin/sh" uses "sh". +-- The initialization of this option is done after reading the vimrc +-- and the other initializations, so that when the `'shell'` option is set +-- there, the `'shellpipe'` option changes automatically, unless it was +-- explicitly set before. +-- When `'shellpipe'` is set to an empty string, no redirection of the +-- ":make" output will be done. This is useful if you use a `'makeprg'` +-- that writes to `'makeef'` by itself. If you want no piping, but do +-- want to include the `'makeef'` , set `'shellpipe'` to a single space. +-- Don't forget to precede the space with a backslash: ":set sp=\ ". +-- In the future pipes may be used for filtering and this option will +-- become obsolete (at least for Unix). +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.shellpipe = "| tee" +vim.go.sp = vim.go.shellpipe +-- `'shellquote'` `'shq'` string (default: ""; Windows, when `'shell'` +-- contains "sh" somewhere: "\"") +-- global +-- Quoting character(s), put around the command passed to the shell, for +-- the "!" and ":!" commands. The redirection is kept outside of the +-- quoting. See `'shellxquote'` to include the redirection. It's +-- probably not useful to set both options. +-- This is an empty string by default. Only known to be useful for +-- third-party shells on Windows systems, such as the MKS Korn Shell +-- or bash, where it should be "\"". The default is adjusted according +-- the value of `'shell'` , to reduce the need to set this option by the +-- user. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.shellquote = "" +vim.go.shq = vim.go.shellquote +-- `'shellredir'` `'srr'` string (default ">", ">&" or ">%s 2>&1") +-- global +-- String to be used to put the output of a filter command in a temporary +-- file. See also |:!|. See |option-backslash| about including spaces +-- and backslashes. +-- The name of the temporary file can be represented by "%s" if necessary +-- (the file name is appended automatically if no %s appears in the value +-- of this option). +-- The default is ">". For Unix, if the `'shell'` option is "csh" or +-- "tcsh" during initializations, the default becomes ">&". If the +-- `'shell'` option is "sh", "ksh", "mksh", "pdksh", "zsh", "zsh-beta", +-- "bash" or "fish", the default becomes ">%s 2>&1". This means that +-- stderr is also included. For Win32, the Unix checks are done and +-- additionally "cmd" is checked for, which makes the default ">%s 2>&1". +-- Also, the same names with ".exe" appended are checked for. +-- The initialization of this option is done after reading the vimrc +-- and the other initializations, so that when the `'shell'` option is set +-- there, the `'shellredir'` option changes automatically unless it was +-- explicitly set before. +-- In the future pipes may be used for filtering and this option will +-- become obsolete (at least for Unix). +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.shellredir = ">" +vim.go.srr = vim.go.shellredir +-- `'shellslash'` `'ssl'` boolean (default off) +-- global +-- {only for MS-Windows} +-- When set, a forward slash is used when expanding file names. This is +-- useful when a Unix-like shell is used instead of cmd.exe. Backward +-- slashes can still be typed, but they are changed to forward slashes by +-- Vim. +-- Note that setting or resetting this option has no effect for some +-- existing file names, thus this option needs to be set before opening +-- any file for best results. This might change in the future. +-- `'shellslash'` only works when a backslash can be used as a path +-- separator. To test if this is so use: > +-- if exists(`'+shellslash'` ) +-- < Also see `'completeslash'` . +vim.go.shellslash = false +vim.go.ssl = vim.go.shellslash +-- `'shelltemp'` `'stmp'` boolean (default on) +-- global +-- When on, use temp files for shell commands. When off use a pipe. +-- When using a pipe is not possible temp files are used anyway. +-- The advantage of using a pipe is that nobody can read the temp file +-- and the `'shell'` command does not need to support redirection. +-- The advantage of using a temp file is that the file type and encoding +-- can be detected. +-- The |FilterReadPre|, |FilterReadPost| and |FilterWritePre|, +-- |FilterWritePost| autocommands event are not triggered when +-- `'shelltemp'` is off. +-- |system()| does not respect this option, it always uses pipes. +vim.go.shelltemp = true +vim.go.stmp = vim.go.shelltemp +-- `'shellxescape'` `'sxe'` string (default: "") +-- global +-- When `'shellxquote'` is set to "(" then the characters listed in this +-- option will be escaped with a `'^'` character. This makes it possible +-- to execute most external commands with cmd.exe. +vim.go.shellxescape = "" +vim.go.sxe = vim.go.shellxescape +-- `'shellxquote'` `'sxq'` string (default: "", Windows: "\"") +-- global +-- Quoting character(s), put around the command passed to the shell, for +-- the "!" and ":!" commands. Includes the redirection. See +-- `'shellquote'` to exclude the redirection. It's probably not useful +-- to set both options. +-- When the value is `'('` then `')'` is appended. When the value is `'"('` +-- then `')"'` is appended. +-- When the value is `'('` then also see `'shellxescape'` . +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.shellxquote = "" +vim.go.sxq = vim.go.shellxquote +-- `'shiftround'` `'sr'` boolean (default off) +-- global +-- Round indent to multiple of `'shiftwidth'` . Applies to > and < +-- commands. CTRL-T and CTRL-D in Insert mode always round the indent to +-- a multiple of `'shiftwidth'` (this is Vi compatible). +vim.go.shiftround = false +vim.go.sr = vim.go.shiftround +-- `'shortmess'` `'shm'` string (default "filnxtToOF") +-- global +-- This option helps to avoid all the |hit-enter| prompts caused by file +-- messages, for example with CTRL-G, and to avoid some other messages. +-- It is a list of flags: +-- flag meaning when present ~ +-- f use "(3 of 5)" instead of "(file 3 of 5)" +-- i use "[noeol]" instead of "[Incomplete last line]" +-- l use "999L, 888B" instead of "999 lines, 888 bytes" +-- m use "[+]" instead of "[Modified]" +-- n use "[New]" instead of "[New File]" +-- r use "[RO]" instead of "[readonly]" +-- w use "[w]" instead of "written" for file write message +-- and "[a]" instead of "appended" for ':w >> file' command +-- x use "[dos]" instead of "[dos format]", "[unix]" +-- instead of "[unix format]" and "[mac]" instead of "[mac +-- format]" +-- a all of the above abbreviations +-- +-- o overwrite message for writing a file with subsequent +-- message for reading a file (useful for ":wn" or when +-- `'autowrite'` on) +-- O message for reading a file overwrites any previous +-- message; also for quickfix message (e.g., ":cn") +-- s don't give "search hit BOTTOM, continuing at TOP" or +-- "search hit TOP, continuing at BOTTOM" messages; when using +-- the search count do not show "W" after the count message (see +-- S below) +-- t truncate file message at the start if it is too long +-- to fit on the command-line, "<" will appear in the left most +-- column; ignored in Ex mode +-- T truncate other messages in the middle if they are too +-- long to fit on the command line; "..." will appear in the +-- middle; ignored in Ex mode +-- W don't give "written" or "[w]" when writing a file +-- A don't give the "ATTENTION" message when an existing +-- swap file is found +-- I don't give the intro message when starting Vim, +-- see |:intro| +-- c don't give |ins-completion-menu| messages; for +-- example, "-- XXX completion (YYY)", "match 1 of 2", "The only +-- match", "Pattern not found", "Back at original", etc. +-- C don't give messages while scanning for ins-completion +-- items, for instance "scanning tags" +-- q use "recording" instead of "recording @a" +-- F don't give the file info when editing a file, like +-- `:silent` was used for the command +-- S do not show search count message when searching, e.g. +-- "[1/5]" +-- +-- This gives you the opportunity to avoid that a change between buffers +-- requires you to hit , but still gives as useful a message as +-- possible for the space available. To get the whole message that you +-- would have got with `'shm'` empty, use ":file!" +-- Useful values: +-- shm= No abbreviation of message. +-- shm=a Abbreviation, but no loss of information. +-- shm=at Abbreviation, and truncate message when necessary. +vim.go.shortmess = "filnxtToOF" +vim.go.shm = vim.go.shortmess +-- `'showcmd'` `'sc'` boolean (default: on) +-- global +-- Show (partial) command in the last line of the screen. Set this +-- option off if your terminal is slow. +-- In Visual mode the size of the selected area is shown: +-- - When selecting characters within a line, the number of characters. +-- If the number of bytes is different it is also displayed: "2-6" +-- means two characters and six bytes. +-- - When selecting more than one line, the number of lines. +-- - When selecting a block, the size in screen characters: +-- {lines}x{columns}. +-- This information can be displayed in an alternative location using the +-- `'showcmdloc'` option, useful when `'cmdheight'` is 0. +vim.go.showcmd = true +vim.go.sc = vim.go.showcmd +-- `'showcmdloc'` `'sloc'` string (default "last") +-- global +-- This option can be used to display the (partially) entered command in +-- another location. Possible values are: +-- last Last line of the screen (default). +-- statusline Status line of the current window. +-- tabline First line of the screen if `'showtabline'` is enabled. +-- Setting this option to "statusline" or "tabline" means that these will +-- be redrawn whenever the command changes, which can be on every key +-- pressed. +-- The %S `'statusline'` item can be used in `'statusline'` or `'tabline'` to +-- place the text. Without a custom `'statusline'` or `'tabline'` it will be +-- displayed in a convenient location. +vim.go.showcmdloc = "last" +vim.go.sloc = vim.go.showcmdloc +-- `'showfulltag'` `'sft'` boolean (default off) +-- global +-- When completing a word in insert mode (see |ins-completion|) from the +-- tags file, show both the tag name and a tidied-up form of the search +-- pattern (if there is one) as possible matches. Thus, if you have +-- matched a C function, you can see a template for what arguments are +-- required (coding style permitting). +-- Note that this doesn't work well together with having "longest" in +-- `'completeopt'` , because the completion from the search pattern may not +-- match the typed text. +vim.go.showfulltag = false +vim.go.sft = vim.go.showfulltag +-- `'showmatch'` `'sm'` boolean (default off) +-- global +-- When a bracket is inserted, briefly jump to the matching one. The +-- jump is only done if the match can be seen on the screen. The time to +-- show the match can be set with `'matchtime'` . +-- A Beep is given if there is no match (no matter if the match can be +-- seen or not). +-- When the `'m'` flag is not included in `'cpoptions'` , typing a character +-- will immediately move the cursor back to where it belongs. +-- See the "sm" field in `'guicursor'` for setting the cursor shape and +-- blinking when showing the match. +-- The `'matchpairs'` option can be used to specify the characters to show +-- matches for. `'rightleft'` and `'revins'` are used to look for opposite +-- matches. +-- Also see the matchparen plugin for highlighting the match when moving +-- around |pi_paren.txt|. +-- Note: Use of the short form is rated PG. +vim.go.showmatch = false +vim.go.sm = vim.go.showmatch +-- `'showmode'` `'smd'` boolean (default: on) +-- global +-- If in Insert, Replace or Visual mode put a message on the last line. +-- The |hl-ModeMsg| highlight group determines the highlighting. +-- The option has no effect when `'cmdheight'` is zero. +vim.go.showmode = true +vim.go.smd = vim.go.showmode +-- `'showtabline'` `'stal'` number (default 1) +-- global +-- The value of this option specifies when the line with tab page labels +-- will be displayed: +-- 0: never +-- 1: only if there are at least two tab pages +-- 2: always +-- This is both for the GUI and non-GUI implementation of the tab pages +-- line. +-- See |tab-page| for more information about tab pages. +vim.go.showtabline = 1 +vim.go.stal = vim.go.showtabline +-- `'sidescroll'` `'ss'` number (default 1) +-- global +-- The minimal number of columns to scroll horizontally. Used only when +-- the `'wrap'` option is off and the cursor is moved off of the screen. +-- When it is zero the cursor will be put in the middle of the screen. +-- When using a slow terminal set it to a large number or 0. Not used +-- for "zh" and "zl" commands. +vim.go.sidescroll = 1 +vim.go.ss = vim.go.sidescroll +-- `'smartcase'` `'scs'` boolean (default off) +-- global +-- Override the `'ignorecase'` option if the search pattern contains upper +-- case characters. Only used when the search pattern is typed and +-- `'ignorecase'` option is on. Used for the commands "/", "?", "n", "N", +-- ":g" and ":s". Not used for "*", "#", "gd", tag search, etc. After +-- "*" and "#" you can make `'smartcase'` used by doing a "/" command, +-- recalling the search pattern from history and hitting . +vim.go.smartcase = false +vim.go.scs = vim.go.smartcase +-- `'smarttab'` `'sta'` boolean (default on) +-- global +-- When on, a in front of a line inserts blanks according to +-- `'shiftwidth'` . `'tabstop'` or `'softtabstop'` is used in other places. A +-- will delete a `'shiftwidth'` worth of space at the start of the +-- line. +-- When off, a always inserts blanks according to `'tabstop'` or +-- `'softtabstop'` . `'shiftwidth'` is only used for shifting text left or +-- right |shift-left-right|. +-- What gets inserted (a or spaces) depends on the `'expandtab'` +-- option. Also see |ins-expandtab|. When `'expandtab'` is not set, the +-- number of spaces is minimized by using s. +vim.go.smarttab = true +vim.go.sta = vim.go.smarttab +-- `'spellsuggest'` `'sps'` string (default "best") +-- global +-- Methods used for spelling suggestions. Both for the |z=| command and +-- the |spellsuggest()| function. This is a comma-separated list of +-- items: +-- +-- best Internal method that works best for English. Finds +-- changes like "fast" and uses a bit of sound-a-like +-- scoring to improve the ordering. +-- +-- double Internal method that uses two methods and mixes the +-- results. The first method is "fast", the other method +-- computes how much the suggestion sounds like the bad +-- word. That only works when the language specifies +-- sound folding. Can be slow and doesn't always give +-- better results. +-- +-- fast Internal method that only checks for simple changes: +-- character inserts/deletes/swaps. Works well for +-- simple typing mistakes. +-- +-- {number} The maximum number of suggestions listed for |z=|. +-- Not used for |spellsuggest()|. The number of +-- suggestions is never more than the value of `'lines'` +-- minus two. +-- +-- timeout:{millisec} Limit the time searching for suggestions to +-- {millisec} milli seconds. Applies to the following +-- methods. When omitted the limit is 5000. When +-- negative there is no limit. +-- +-- file:{filename} Read file {filename}, which must have two columns, +-- separated by a slash. The first column contains the +-- bad word, the second column the suggested good word. +-- Example: +-- theribal/terrible ~ +-- Use this for common mistakes that do not appear at the +-- top of the suggestion list with the internal methods. +-- Lines without a slash are ignored, use this for +-- comments. +-- The word in the second column must be correct, +-- otherwise it will not be used. Add the word to an +-- ".add" file if it is currently flagged as a spelling +-- mistake. +-- The file is used for all languages. +-- +-- expr:{expr} Evaluate expression {expr}. Use a function to avoid +-- trouble with spaces. |v:val| holds the badly spelled +-- word. The expression must evaluate to a List of +-- Lists, each with a suggestion and a score. +-- Example: +-- [[`'the'` , 33], [`'that'` , 44]] ~ +-- Set `'verbose'` and use |z=| to see the scores that the +-- internal methods use. A lower score is better. +-- This may invoke |spellsuggest()| if you temporarily +-- set `'spellsuggest'` to exclude the "expr:" part. +-- Errors are silently ignored, unless you set the +-- `'verbose'` option to a non-zero value. +-- +-- Only one of "best", "double" or "fast" may be used. The others may +-- appear several times in any order. Example: > +-- :set sps=file:~/.config/nvim/sugg,best,expr:MySuggest() +-- < +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.spellsuggest = "best" +vim.go.sps = vim.go.spellsuggest +-- `'splitbelow'` `'sb'` boolean (default off) +-- global +-- When on, splitting a window will put the new window below the current +-- one. |:split| +vim.go.splitbelow = false +vim.go.sb = vim.go.splitbelow +-- `'splitkeep'` `'spk'` string (default "cursor") +-- global +-- The value of this option determines the scroll behavior when opening, +-- closing or resizing horizontal splits. +-- +-- Possible values are: +-- cursor Keep the same relative cursor position. +-- screen Keep the text on the same screen line. +-- topline Keep the topline the same. +-- +-- For the "screen" and "topline" values, the cursor position will be +-- changed when necessary. In this case, the jumplist will be populated +-- with the previous cursor position. For "screen", the text cannot always +-- be kept on the same screen line when `'wrap'` is enabled. +vim.go.splitkeep = "cursor" +vim.go.spk = vim.go.splitkeep +-- `'splitright'` `'spr'` boolean (default off) +-- global +-- When on, splitting a window will put the new window right of the +-- current one. |:vsplit| +vim.go.splitright = false +vim.go.spr = vim.go.splitright +-- `'startofline'` `'sol'` boolean (default off) +-- global +-- When "on" the commands listed below move the cursor to the first +-- non-blank of the line. When off the cursor is kept in the same column +-- (if possible). This applies to the commands: CTRL-D, CTRL-U, CTRL-B, +-- CTRL-F, "G", "H", "M", "L", gg, and to the commands "d", "<<" and ">>" +-- with a linewise operator, with "%" with a count and to buffer changing +-- commands (CTRL-^, :bnext, :bNext, etc.). Also for an Ex command that +-- only has a line number, e.g., ":25" or ":+". +-- In case of buffer changing commands the cursor is placed at the column +-- where it was the last time the buffer was edited. +vim.go.startofline = false +vim.go.sol = vim.go.startofline +-- `'suffixes'` `'su'` string (default ".bak,~,.o,.h,.info,.swp,.obj") +-- global +-- Files with these suffixes get a lower priority when multiple files +-- match a wildcard. See |suffixes|. Commas can be used to separate the +-- suffixes. Spaces after the comma are ignored. A dot is also seen as +-- the start of a suffix. To avoid a dot or comma being recognized as a +-- separator, precede it with a backslash (see |option-backslash| about +-- including spaces and backslashes). +-- See `'wildignore'` for completely ignoring files. +-- The use of |:set+=| and |:set-=| is preferred when adding or removing +-- suffixes from the list. This avoids problems when a future version +-- uses another default. +vim.go.suffixes = ".bak,~,.o,.h,.info,.swp,.obj" +vim.go.su = vim.go.suffixes +-- `'switchbuf'` `'swb'` string (default "uselast") +-- global +-- This option controls the behavior when switching between buffers. +-- Mostly for |quickfix| commands some values are also used for other +-- commands, as mentioned below. +-- Possible values (comma-separated list): +-- useopen If included, jump to the first open window that +-- contains the specified buffer (if there is one). +-- Otherwise: Do not examine other windows. +-- This setting is checked with |quickfix| commands, when +-- jumping to errors (":cc", ":cn", "cp", etc.). It is +-- also used in all buffer related split commands, for +-- example ":sbuffer", ":sbnext", or ":sbrewind". +-- usetab Like "useopen", but also consider windows in other tab +-- pages. +-- split If included, split the current window before loading +-- a buffer for a |quickfix| command that display errors. +-- Otherwise: do not split, use current window (when used +-- in the quickfix window: the previously used window or +-- split if there is no other window). +-- vsplit Just like "split" but split vertically. +-- newtab Like "split", but open a new tab page. Overrules +-- "split" when both are present. +-- uselast If included, jump to the previously used window when +-- jumping to errors with |quickfix| commands. +vim.go.switchbuf = "uselast" +vim.go.swb = vim.go.switchbuf +-- `'tabline'` `'tal'` string (default empty) +-- global +-- When non-empty, this option determines the content of the tab pages +-- line at the top of the Vim window. When empty Vim will use a default +-- tab pages line. See |setting-tabline| for more info. +-- +-- The tab pages line only appears as specified with the `'showtabline'` +-- option and only when there is no GUI tab line. When `'e'` is in +-- `'guioptions'` and the GUI supports a tab line `'guitablabel'` is used +-- instead. Note that the two tab pages lines are very different. +-- +-- The value is evaluated like with `'statusline'` . You can use +-- |tabpagenr()|, |tabpagewinnr()| and |tabpagebuflist()| to figure out +-- the text to be displayed. Use "%1T" for the first label, "%2T" for +-- the second one, etc. Use "%X" items for closing labels. +-- +-- When changing something that is used in `'tabline'` that does not +-- trigger it to be updated, use |:redrawtabline|. +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- Keep in mind that only one of the tab pages is the current one, others +-- are invisible and you can't jump to their windows. +vim.go.tabline = "" +vim.go.tal = vim.go.tabline +-- `'tabpagemax'` `'tpm'` number (default 50) +-- global +-- Maximum number of tab pages to be opened by the |-p| command line +-- argument or the ":tab all" command. |tabpage| +vim.go.tabpagemax = 50 +vim.go.tpm = vim.go.tabpagemax +-- `'tagbsearch'` `'tbs'` boolean (default on) +-- global +-- When searching for a tag (e.g., for the |:ta| command), Vim can either +-- use a binary search or a linear search in a tags file. Binary +-- searching makes searching for a tag a LOT faster, but a linear search +-- will find more tags if the tags file wasn't properly sorted. +-- Vim normally assumes that your tags files are sorted, or indicate that +-- they are not sorted. Only when this is not the case does the +-- `'tagbsearch'` option need to be switched off. +-- +-- When `'tagbsearch'` is on, binary searching is first used in the tags +-- files. In certain situations, Vim will do a linear search instead for +-- certain files, or retry all files with a linear search. When +-- `'tagbsearch'` is off, only a linear search is done. +-- +-- Linear searching is done anyway, for one file, when Vim finds a line +-- at the start of the file indicating that it's not sorted: > +-- !_TAG_FILE_SORTED 0 /some comment/ +-- < [The whitespace before and after the `'0'` must be a single ] +-- +-- When a binary search was done and no match was found in any of the +-- files listed in `'tags'` , and case is ignored or a pattern is used +-- instead of a normal tag name, a retry is done with a linear search. +-- Tags in unsorted tags files, and matches with different case will only +-- be found in the retry. +-- +-- If a tag file indicates that it is case-fold sorted, the second, +-- linear search can be avoided when case is ignored. Use a value of `'2'` +-- in the "!_TAG_FILE_SORTED" line for this. A tag file can be case-fold +-- sorted with the -f switch to "sort" in most unices, as in the command: +-- "sort -f -o tags tags". For Universal ctags and Exuberant ctags +-- version 5.x or higher (at least 5.5) the --sort=foldcase switch can be +-- used for this as well. Note that case must be folded to uppercase for +-- this to work. +-- +-- By default, tag searches are case-sensitive. Case is ignored when +-- `'ignorecase'` is set and `'tagcase'` is "followic", or when `'tagcase'` is +-- "ignore". +-- Also when `'tagcase'` is "followscs" and `'smartcase'` is set, or +-- `'tagcase'` is "smart", and the pattern contains only lowercase +-- characters. +-- +-- When `'tagbsearch'` is off, tags searching is slower when a full match +-- exists, but faster when no full match exists. Tags in unsorted tags +-- files may only be found with `'tagbsearch'` off. +-- When the tags file is not sorted, or sorted in a wrong way (not on +-- ASCII byte value), `'tagbsearch'` should be off, or the line given above +-- must be included in the tags file. +-- This option doesn't affect commands that find all matching tags (e.g., +-- command-line completion and ":help"). +vim.go.tagbsearch = true +vim.go.tbs = vim.go.tagbsearch +-- `'taglength'` `'tl'` number (default 0) +-- global +-- If non-zero, tags are significant up to this number of characters. +vim.go.taglength = 0 +vim.go.tl = vim.go.taglength +-- `'tagrelative'` `'tr'` boolean (default: on) +-- global +-- If on and using a tags file in another directory, file names in that +-- tags file are relative to the directory where the tags file is. +vim.go.tagrelative = true +vim.go.tr = vim.go.tagrelative +-- `'tagstack'` `'tgst'` boolean (default on) +-- global +-- When on, the |tagstack| is used normally. When off, a ":tag" or +-- ":tselect" command with an argument will not push the tag onto the +-- tagstack. A following ":tag" without an argument, a ":pop" command or +-- any other command that uses the tagstack will use the unmodified +-- tagstack, but does change the pointer to the active entry. +-- Resetting this option is useful when using a ":tag" command in a +-- mapping which should not change the tagstack. +vim.go.tagstack = true +vim.go.tgst = vim.go.tagstack +-- `'termbidi'` `'tbidi'` boolean (default off) +-- global +-- The terminal is in charge of Bi-directionality of text (as specified +-- by Unicode). The terminal is also expected to do the required shaping +-- that some languages (such as Arabic) require. +-- Setting this option implies that `'rightleft'` will not be set when +-- `'arabic'` is set and the value of `'arabicshape'` will be ignored. +-- Note that setting `'termbidi'` has the immediate effect that +-- `'arabicshape'` is ignored, but `'rightleft'` isn't changed automatically. +-- For further details see |arabic.txt|. +vim.go.termbidi = false +vim.go.tbidi = vim.go.termbidi +vim.go.termencoding = "" +vim.go.tenc = vim.go.termencoding +-- `'termguicolors'` `'tgc'` boolean (default off) +-- global +-- Enables 24-bit RGB color in the |TUI|. Uses "gui" |:highlight| +-- attributes instead of "cterm" attributes. |guifg| +-- Requires an ISO-8613-3 compatible terminal. +vim.go.termguicolors = false +vim.go.tgc = vim.go.termguicolors +-- `'termpastefilter'` `'tpf'` string (default: "BS,HT,ESC,DEL") +-- global +-- A comma-separated list of options for specifying control characters +-- to be removed from the text pasted into the terminal window. The +-- supported values are: +-- +-- BS Backspace +-- +-- HT TAB +-- +-- FF Form feed +-- +-- ESC Escape +-- +-- DEL DEL +-- +-- C0 Other control characters, excluding Line feed and +-- Carriage return < ' ' +-- +-- C1 Control characters 0x80...0x9F +vim.go.termpastefilter = "BS,HT,ESC,DEL" +vim.go.tpf = vim.go.termpastefilter +vim.go.terse = false +-- `'tildeop'` `'top'` boolean (default off) +-- global +-- When on: The tilde command "~" behaves like an operator. +vim.go.tildeop = false +vim.go.top = vim.go.tildeop +-- `'timeout'` `'to'` boolean (default on) +-- global +-- This option and `'timeoutlen'` determine the behavior when part of a +-- mapped key sequence has been received. For example, if is +-- pressed and `'timeout'` is set, Nvim will wait `'timeoutlen'` milliseconds +-- for any key that can follow in a mapping. +vim.go.timeout = true +vim.go.to = vim.go.timeout +-- `'timeoutlen'` `'tm'` number (default 1000) +-- global +-- Time in milliseconds to wait for a mapped sequence to complete. +vim.go.timeoutlen = 1000 +vim.go.tm = vim.go.timeoutlen +-- `'title'` boolean (default off) +-- global +-- When on, the title of the window will be set to the value of +-- `'titlestring'` (if it is not empty), or to: +-- filename [+=-] (path) - NVIM +-- Where: +-- filename the name of the file being edited +-- - indicates the file cannot be modified, `'ma'` off +-- + indicates the file was modified +-- = indicates the file is read-only +-- =+ indicates the file is read-only and modified +-- (path) is the path of the file being edited +-- - NVIM the server name |v:servername| or "NVIM" +vim.go.title = false +-- `'titlelen'` number (default 85) +-- global +-- Gives the percentage of `'columns'` to use for the length of the window +-- title. When the title is longer, only the end of the path name is +-- shown. A `'<'` character before the path name is used to indicate this. +-- Using a percentage makes this adapt to the width of the window. But +-- it won't work perfectly, because the actual number of characters +-- available also depends on the font used and other things in the title +-- bar. When `'titlelen'` is zero the full path is used. Otherwise, +-- values from 1 to 30000 percent can be used. +-- `'titlelen'` is also used for the `'titlestring'` option. +vim.go.titlelen = 85 +-- `'titleold'` string (default "") +-- global +-- If not empty, this option will be used to set the window title when +-- exiting. Only if `'title'` is enabled. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.titleold = "" +-- `'titlestring'` string (default "") +-- global +-- When this option is not empty, it will be used for the title of the +-- window. This happens only when the `'title'` option is on. +-- +-- When this option contains printf-style `'%'` items, they will be +-- expanded according to the rules used for `'statusline'` . +-- This option cannot be set in a modeline when `'modelineexpr'` is off. +-- +-- Example: > +-- :auto BufEnter * let &titlestring = hostname() .. "/" .. expand("%:p") +-- :set title titlestring=%<%F%=%l/%L-%P titlelen=70 +-- < The value of `'titlelen'` is used to align items in the middle or right +-- of the available space. +-- Some people prefer to have the file name first: > +-- :set titlestring=%t%(\ %M%)%(\ (%{expand(\"%:~:.:h\")})%)%(\ %a%) +-- < Note the use of "%{ }" and an expression to get the path of the file, +-- without the file name. The "%( %)" constructs are used to add a +-- separating space only when needed. +-- NOTE: Use of special characters in `'titlestring'` may cause the display +-- to be garbled (e.g., when it contains a CR or NL character). +vim.go.titlestring = "" +-- `'ttimeout'` boolean (default on) +-- global +-- This option and `'ttimeoutlen'` determine the behavior when part of a +-- key code sequence has been received by the |TUI|. +-- +-- For example if (the \x1b byte) is received and `'ttimeout'` is +-- set, Nvim waits `'ttimeoutlen'` milliseconds for the terminal to +-- complete a key code sequence. If no input arrives before the timeout, +-- a single is assumed. Many TUI cursor key codes start with . +-- +-- On very slow systems this may fail, causing cursor keys not to work +-- sometimes. If you discover this problem you can ":set ttimeoutlen=9999". +-- Nvim will wait for the next character to arrive after an . +vim.go.ttimeout = true +-- `'ttimeoutlen'` `'ttm'` number (default 50) +-- global +-- Time in milliseconds to wait for a key code sequence to complete. Also +-- used for CTRL-\ CTRL-N and CTRL-\ CTRL-G when part of a command has +-- been typed. +vim.go.ttimeoutlen = 50 +vim.go.ttm = vim.go.ttimeoutlen +vim.go.ttyfast = true +vim.go.tf = vim.go.ttyfast +-- `'undodir'` `'udir'` string (default "$XDG_STATE_HOME/nvim/undo//") +-- global +-- List of directory names for undo files, separated with commas. +-- See `'backupdir'` for details of the format. +-- "." means using the directory of the file. The undo file name for +-- "file.txt" is ".file.txt.un~". +-- For other directories the file name is the full path of the edited +-- file, with path separators replaced with "%". +-- When writing: The first directory that exists is used. "." always +-- works, no directories after "." will be used for writing. If none of +-- the directories exist Nvim will attempt to create the last directory in +-- the list. +-- When reading all entries are tried to find an undo file. The first +-- undo file that exists is used. When it cannot be read an error is +-- given, no further entry is used. +-- See |undo-persistence|. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +-- +-- Note that unlike `'directory'` and `'backupdir'` , `'undodir'` always acts as +-- though the trailing slashes are present (see `'backupdir'` for what this +-- means). +vim.go.undodir = "/home/runner/.local/state/nvim/undo//" +vim.go.udir = vim.go.undodir +-- `'undoreload'` `'ur'` number (default 10000) +-- global +-- Save the whole buffer for undo when reloading it. This applies to the +-- ":e!" command and reloading for when the buffer changed outside of +-- Vim. |FileChangedShell| +-- The save only happens when this option is negative or when the number +-- of lines is smaller than the value of this option. +-- Set this option to zero to disable undo for a reload. +-- +-- When saving undo for a reload, any undo file is not read. +-- +-- Note that this causes the whole buffer to be stored in memory. Set +-- this option to a lower value if you run out of memory. +vim.go.undoreload = 10000 +vim.go.ur = vim.go.undoreload +-- `'updatecount'` `'uc'` number (default: 200) +-- global +-- After typing this many characters the swap file will be written to +-- disk. When zero, no swap file will be created at all (see chapter on +-- recovery |crash-recovery|). `'updatecount'` is set to zero by starting +-- Vim with the "-n" option, see |startup|. When editing in readonly +-- mode this option will be initialized to 10000. +-- The swapfile can be disabled per buffer with |`'swapfile'` |. +-- When `'updatecount'` is set from zero to non-zero, swap files are +-- created for all buffers that have `'swapfile'` set. When `'updatecount'` +-- is set to zero, existing swap files are not deleted. +-- This option has no meaning in buffers where |`'buftype'` | is "nofile" +-- or "nowrite". +vim.go.updatecount = 200 +vim.go.uc = vim.go.updatecount +-- `'updatetime'` `'ut'` number (default 4000) +-- global +-- If this many milliseconds nothing is typed the swap file will be +-- written to disk (see |crash-recovery|). Also used for the +-- |CursorHold| autocommand event. +vim.go.updatetime = 4000 +vim.go.ut = vim.go.updatetime +-- `'verbose'` `'vbs'` number (default 0) +-- global +-- Sets the verbosity level. Also set by |-V| and |:verbose|. +-- +-- Tracing of options in Lua scripts is activated at level 1; Lua scripts +-- are not traced with verbose=0, for performance. +-- +-- If greater than or equal to a given level, Nvim produces the following +-- messages: +-- +-- Level Messages ~ +-- ---------------------------------------------------------------------- +-- 1 Lua assignments to options, mappings, etc. +-- 2 When a file is ":source"'ed, or |shada| file is read or written. +-- 3 UI info, terminal capabilities. +-- 4 Shell commands. +-- 5 Every searched tags file and include file. +-- 8 Files for which a group of autocommands is executed. +-- 9 Executed autocommands. +-- 11 Finding items in a path. +-- 12 Vimscript function calls. +-- 13 When an exception is thrown, caught, finished, or discarded. +-- 14 Anything pending in a ":finally" clause. +-- 15 Ex commands from a script (truncated at 200 characters). +-- 16 Ex commands. +-- +-- If `'verbosefile'` is set then the verbose messages are not displayed. +vim.go.verbose = 0 +vim.go.vbs = vim.go.verbose +-- `'verbosefile'` `'vfile'` string (default empty) +-- global +-- When not empty all messages are written in a file with this name. +-- When the file exists messages are appended. +-- Writing to the file ends when Vim exits or when `'verbosefile'` is made +-- empty. Writes are buffered, thus may not show up for some time. +-- Setting `'verbosefile'` to a new value is like making it empty first. +-- The difference with |:redir| is that verbose messages are not +-- displayed when `'verbosefile'` is set. +vim.go.verbosefile = "" +vim.go.vfile = vim.go.verbosefile +-- `'viewdir'` `'vdir'` string (default: "$XDG_STATE_HOME/nvim/view//") +-- global +-- Name of the directory where to store files for |:mkview|. +-- This option cannot be set from a |modeline| or in the |sandbox|, for +-- security reasons. +vim.go.viewdir = "/home/runner/.local/state/nvim/view//" +vim.go.vdir = vim.go.viewdir +-- `'viewoptions'` `'vop'` string (default: "folds,cursor,curdir") +-- global +-- Changes the effect of the |:mkview| command. It is a comma-separated +-- list of words. Each word enables saving and restoring something: +-- word save and restore ~ +-- cursor cursor position in file and in window +-- curdir local current directory, if set with |:lcd| +-- folds manually created folds, opened/closed folds and local +-- fold options +-- options options and mappings local to a window or buffer (not +-- global values for local options) +-- localoptions same as "options" +-- slash |deprecated| Always enabled. Uses "/" in filenames. +-- unix |deprecated| Always enabled. Uses "\n" line endings. +vim.go.viewoptions = "folds,cursor,curdir" +vim.go.vop = vim.go.viewoptions +vim.go.viminfo = "" +vim.go.vi = vim.go.viminfo +vim.go.viminfofile = "" +vim.go.vif = vim.go.viminfofile +-- `'visualbell'` `'vb'` boolean (default off) +-- global +-- Use visual bell instead of beeping. Also see `'errorbells'` . +vim.go.visualbell = false +vim.go.vb = vim.go.visualbell +-- `'warn'` boolean (default on) +-- global +-- Give a warning message when a shell command is used while the buffer +-- has been changed. +vim.go.warn = true +-- `'whichwrap'` `'ww'` string (default: "b,s") +-- global +-- Allow specified keys that move the cursor left/right to move to the +-- previous/next line when the cursor is on the first/last character in +-- the line. Concatenate characters to allow this for these keys: +-- char key mode ~ +-- b Normal and Visual +-- s Normal and Visual +-- h "h" Normal and Visual (not recommended) +-- l "l" Normal and Visual (not recommended) +-- < Normal and Visual +-- > Normal and Visual +-- ~ "~" Normal +-- [ Insert and Replace +-- ] Insert and Replace +-- For example: > +-- :set ww=<,>,[,] +-- < allows wrap only when cursor keys are used. +-- When the movement keys are used in combination with a delete or change +-- operator, the also counts for a character. This makes "3h" +-- different from "3dh" when the cursor crosses the end of a line. This +-- is also true for "x" and "X", because they do the same as "dl" and +-- "dh". If you use this, you may also want to use the mapping +-- ":map X" to make backspace delete the character in front of the +-- cursor. +-- When `'l'` is included and it is used after an operator at the end of a +-- line (not an empty line) then it will not move to the next line. This +-- makes "dl", "cl", "yl" etc. work normally. +vim.go.whichwrap = "b,s" +vim.go.ww = vim.go.whichwrap +-- `'wildchar'` `'wc'` number (default: ) +-- global +-- Character you have to type to start wildcard expansion in the +-- command-line, as specified with `'wildmode'` . +-- More info here: |cmdline-completion|. +-- The character is not recognized when used inside a macro. See +-- `'wildcharm'` for that. +-- Some keys will not work, such as CTRL-C, and Enter. +-- Although `'wc'` is a number option, you can set it to a special key: > +-- :set wc= +-- < +vim.go.wildchar = 9 +vim.go.wc = vim.go.wildchar +-- `'wildcharm'` `'wcm'` number (default: none (0)) +-- global +-- `'wildcharm'` works exactly like `'wildchar'` , except that it is +-- recognized when used inside a macro. You can find "spare" command-line +-- keys suitable for this option by looking at |ex-edit-index|. Normally +-- you'll never actually type `'wildcharm'` , just use it in mappings that +-- automatically invoke completion mode, e.g.: > +-- :set wcm= +-- :cnoremap ss so $vim/sessions/*.vim +-- < Then after typing :ss you can use CTRL-P & CTRL-N. +vim.go.wildcharm = 0 +vim.go.wcm = vim.go.wildcharm +-- `'wildignore'` `'wig'` string (default "") +-- global +-- A list of file patterns. A file that matches with one of these +-- patterns is ignored when expanding |wildcards|, completing file or +-- directory names, and influences the result of |expand()|, |glob()| and +-- |globpath()| unless a flag is passed to disable this. +-- The pattern is used like with |:autocmd|, see |autocmd-pattern|. +-- Also see `'suffixes'` . +-- Example: > +-- :set wildignore=.obj +-- < The use of |:set+=| and |:set-=| is preferred when adding or removing +-- a pattern from the list. This avoids problems when a future version +-- uses another default. +vim.go.wildignore = "" +vim.go.wig = vim.go.wildignore +-- `'wildignorecase'` `'wic'` boolean (default off) +-- global +-- When set case is ignored when completing file names and directories. +-- Has no effect when `'fileignorecase'` is set. +-- Does not apply when the shell is used to expand wildcards, which +-- happens when there are special characters. +vim.go.wildignorecase = false +vim.go.wic = vim.go.wildignorecase +-- `'wildmenu'` `'wmnu'` boolean (default on) +-- global +-- When `'wildmenu'` is on, command-line completion operates in an enhanced +-- mode. On pressing `'wildchar'` (usually ) to invoke completion, +-- the possible matches are shown. +-- When `'wildoptions'` contains "pum", then the completion matches are +-- shown in a popup menu. Otherwise they are displayed just above the +-- command line, with the first match highlighted (overwriting the status +-- line, if there is one). +-- Keys that show the previous/next match, such as or +-- CTRL-P/CTRL-N, cause the highlight to move to the appropriate match. +-- `'wildmode'` must specify "full": "longest" and "list" do not start +-- `'wildmenu'` mode. You can check the current mode with |wildmenumode()|. +-- The menu is cancelled when a key is hit that is not used for selecting +-- a completion. +-- +-- While the menu is active these keys have special meanings: +-- +-- CTRL-Y - accept the currently selected match and stop +-- completion. +-- CTRL-E - end completion, go back to what was there before +-- selecting a match. +-- - select previous/next match (like CTRL-P/CTRL-N) +-- - in filename/menu name completion: move into a +-- subdirectory or submenu. +-- - in menu completion, when the cursor is just after a +-- dot: move into a submenu. +-- - in filename/menu name completion: move up into +-- parent directory or parent menu. +-- +-- If you want and to move the cursor instead of selecting +-- a different match, use this: > +-- :cnoremap +-- :cnoremap +-- < +-- |hl-WildMenu| highlights the current match. +vim.go.wildmenu = true +vim.go.wmnu = vim.go.wildmenu +-- `'wildmode'` `'wim'` string (default: "full") +-- global +-- Completion mode that is used for the character specified with +-- `'wildchar'` . It is a comma-separated list of up to four parts. Each +-- part specifies what to do for each consecutive use of `'wildchar'` . The +-- first part specifies the behavior for the first use of `'wildchar'` , +-- The second part for the second use, etc. +-- +-- Each part consists of a colon separated list consisting of the +-- following possible values: +-- "" Complete only the first match. +-- "full" Complete the next full match. After the last match, +-- the original string is used and then the first match +-- again. Will also start `'wildmenu'` if it is enabled. +-- "longest" Complete till longest common string. If this doesn't +-- result in a longer string, use the next part. +-- "list" When more than one match, list all matches. +-- "lastused" When completing buffer names and more than one buffer +-- matches, sort buffers by time last used (other than +-- the current buffer). +-- When there is only a single match, it is fully completed in all cases. +-- +-- Examples of useful colon-separated values: +-- "longest:full" Like "longest", but also start `'wildmenu'` if it is +-- enabled. Will not complete to the next full match. +-- "list:full" When more than one match, list all matches and +-- complete first match. +-- "list:longest" When more than one match, list all matches and +-- complete till longest common string. +-- "list:lastused" When more than one buffer matches, list all matches +-- and sort buffers by time last used (other than the +-- current buffer). +-- +-- Examples: > +-- :set wildmode=full +-- < Complete first full match, next match, etc. (the default) > +-- :set wildmode=longest,full +-- < Complete longest common string, then each full match > +-- :set wildmode=list:full +-- < List all matches and complete each full match > +-- :set wildmode=list,full +-- < List all matches without completing, then each full match > +-- :set wildmode=longest,list +-- < Complete longest common string, then list alternatives. +-- More info here: |cmdline-completion|. +vim.go.wildmode = "full" +vim.go.wim = vim.go.wildmode +-- `'wildoptions'` `'wop'` string (default "pum,tagfile") +-- global +-- A list of words that change how |cmdline-completion| is done. +-- The following values are supported: +-- fuzzy Use |fuzzy-matching| to find completion matches. When +-- this value is specified, wildcard expansion will not +-- be used for completion. The matches will be sorted by +-- the "best match" rather than alphabetically sorted. +-- This will find more matches than the wildcard +-- expansion. Currently fuzzy matching based completion +-- is not supported for file and directory names and +-- instead wildcard expansion is used. +-- pum Display the completion matches using the popup menu +-- in the same style as the |ins-completion-menu|. +-- tagfile When using CTRL-D to list matching tags, the kind of +-- tag and the file of the tag is listed. Only one match +-- is displayed per line. Often used tag kinds are: +-- d #define +-- f function +vim.go.wildoptions = "pum,tagfile" +vim.go.wop = vim.go.wildoptions +-- `'winaltkeys'` `'wak'` string (default "menu") +-- global +-- {only used in Win32} +-- Some GUI versions allow the access to menu entries by using the ALT +-- key in combination with a character that appears underlined in the +-- menu. This conflicts with the use of the ALT key for mappings and +-- entering special characters. This option tells what to do: +-- no Don't use ALT keys for menus. ALT key combinations can be +-- mapped, but there is no automatic handling. +-- yes ALT key handling is done by the windowing system. ALT key +-- combinations cannot be mapped. +-- menu Using ALT in combination with a character that is a menu +-- shortcut key, will be handled by the windowing system. Other +-- keys can be mapped. +-- If the menu is disabled by excluding `'m'` from `'guioptions'` , the ALT +-- key is never used for the menu. +-- This option is not used for ; on Win32. +vim.go.winaltkeys = "menu" +vim.go.wak = vim.go.winaltkeys +-- `'window'` `'wi'` number (default screen height - 1) +-- global +-- Window height used for |CTRL-F| and |CTRL-B| when there is only one +-- window and the value is smaller than `'lines'` minus one. The screen +-- will scroll `'window'` minus two lines, with a minimum of one. +-- When `'window'` is equal to `'lines'` minus one CTRL-F and CTRL-B scroll +-- in a much smarter way, taking care of wrapping lines. +-- When resizing the Vim window, the value is smaller than 1 or more than +-- or equal to `'lines'` it will be set to `'lines'` minus 1. +-- Note: Do not confuse this with the height of the Vim window, use +-- `'lines'` for that. +vim.go.window = 23 +vim.go.wi = vim.go.window +-- `'winheight'` `'wh'` number (default 1) +-- global +-- Minimal number of lines for the current window. This is not a hard +-- minimum, Vim will use fewer lines if there is not enough room. If the +-- focus goes to a window that is smaller, its size is increased, at the +-- cost of the height of other windows. +-- Set `'winheight'` to a small number for normal editing. +-- Set it to 999 to make the current window fill most of the screen. +-- Other windows will be only `'winminheight'` high. This has the drawback +-- that ":all" will create only two windows. To avoid "vim -o 1 2 3 4" +-- to create only two windows, set the option after startup is done, +-- using the |VimEnter| event: > +-- au VimEnter * set winheight=999 +-- < Minimum value is 1. +-- The height is not adjusted after one of the commands that change the +-- height of the current window. +-- `'winheight'` applies to the current window. Use `'winminheight'` to set +-- the minimal height for other windows. +vim.go.winheight = 1 +vim.go.wh = vim.go.winheight +-- `'winminheight'` `'wmh'` number (default 1) +-- global +-- The minimal height of a window, when it's not the current window. +-- This is a hard minimum, windows will never become smaller. +-- When set to zero, windows may be "squashed" to zero lines (i.e. just a +-- status bar) if necessary. They will return to at least one line when +-- they become active (since the cursor has to have somewhere to go.) +-- Use `'winheight'` to set the minimal height of the current window. +-- This option is only checked when making a window smaller. Don't use a +-- large number, it will cause errors when opening more than a few +-- windows. A value of 0 to 3 is reasonable. +vim.go.winminheight = 1 +vim.go.wmh = vim.go.winminheight +-- `'winminwidth'` `'wmw'` number (default 1) +-- global +-- The minimal width of a window, when it's not the current window. +-- This is a hard minimum, windows will never become smaller. +-- When set to zero, windows may be "squashed" to zero columns (i.e. just +-- a vertical separator) if necessary. They will return to at least one +-- line when they become active (since the cursor has to have somewhere +-- to go.) +-- Use `'winwidth'` to set the minimal width of the current window. +-- This option is only checked when making a window smaller. Don't use a +-- large number, it will cause errors when opening more than a few +-- windows. A value of 0 to 12 is reasonable. +vim.go.winminwidth = 1 +vim.go.wmw = vim.go.winminwidth +-- `'winwidth'` `'wiw'` number (default 20) +-- global +-- Minimal number of columns for the current window. This is not a hard +-- minimum, Vim will use fewer columns if there is not enough room. If +-- the current window is smaller, its size is increased, at the cost of +-- the width of other windows. Set it to 999 to make the current window +-- always fill the screen. Set it to a small number for normal editing. +-- The width is not adjusted after one of the commands to change the +-- width of the current window. +-- `'winwidth'` applies to the current window. Use `'winminwidth'` to set +-- the minimal width for other windows. +vim.go.winwidth = 20 +vim.go.wiw = vim.go.winwidth +-- `'wrapscan'` `'ws'` boolean (default on) +-- global +-- Searches wrap around the end of the file. Also applies to |]s| and +-- |[s|, searching for spelling mistakes. +vim.go.wrapscan = true +vim.go.ws = vim.go.wrapscan +-- `'write'` boolean (default on) +-- global +-- Allows writing files. When not set, writing a file is not allowed. +-- Can be used for a view-only mode, where modifications to the text are +-- still allowed. Can be reset with the |-m| or |-M| command line +-- argument. Filtering text is still possible, even though this requires +-- writing a temporary file. +vim.go.write = true +-- `'writeany'` `'wa'` boolean (default off) +-- global +-- Allows writing to any file with no need for "!" override. +vim.go.writeany = false +vim.go.wa = vim.go.writeany +-- `'writebackup'` `'wb'` boolean (default on) +-- global +-- Make a backup before overwriting a file. The backup is removed after +-- the file was successfully written, unless the `'backup'` option is +-- also on. +-- WARNING: Switching this option off means that when Vim fails to write +-- your buffer correctly and then, for whatever reason, Vim exits, you +-- lose both the original file and what you were writing. Only reset +-- this option if your file system is almost full and it makes the write +-- fail (and make sure not to exit Vim until the write was successful). +-- See |backup-table| for another explanation. +-- When the `'backupskip'` pattern matches, a backup is not made anyway. +-- Depending on `'backupcopy'` the backup is a new file or the original +-- file renamed (and a new file is written). +vim.go.writebackup = true +vim.go.wb = vim.go.writebackup +vim.go.writedelay = 0 +vim.go.wd = vim.go.writedelay + + +---@class vim.wo +vim.wo = {} + +-- `'arabic'` `'arab'` boolean (default off) +-- local to window +-- This option can be set to start editing Arabic text. +-- Setting this option will: +-- - Set the `'rightleft'` option, unless `'termbidi'` is set. +-- - Set the `'arabicshape'` option, unless `'termbidi'` is set. +-- - Set the `'keymap'` option to "arabic"; in Insert mode CTRL-^ toggles +-- between typing English and Arabic key mapping. +-- - Set the `'delcombine'` option +-- +-- Resetting this option will: +-- - Reset the `'rightleft'` option. +-- - Disable the use of `'keymap'` (without changing its value). +-- Note that `'arabicshape'` and `'delcombine'` are not reset (it is a global +-- option). +-- Also see |arabic.txt|. +vim.wo.arabic = false +vim.wo.arab = vim.wo.arabic +-- `'breakindent'` `'bri'` boolean (default off) +-- local to window +-- Every wrapped line will continue visually indented (same amount of +-- space as the beginning of that line), thus preserving horizontal blocks +-- of text. +vim.wo.breakindent = false +vim.wo.bri = vim.wo.breakindent +-- `'breakindentopt'` `'briopt'` string (default empty) +-- local to window +-- Settings for `'breakindent'` . It can consist of the following optional +-- items and must be separated by a comma: +-- min:{n} Minimum text width that will be kept after +-- applying `'breakindent'` , even if the resulting +-- text should normally be narrower. This prevents +-- text indented almost to the right window border +-- occupying lot of vertical space when broken. +-- (default: 20) +-- shift:{n} After applying `'breakindent'` , the wrapped line's +-- beginning will be shifted by the given number of +-- characters. It permits dynamic French paragraph +-- indentation (negative) or emphasizing the line +-- continuation (positive). +-- (default: 0) +-- sbr Display the `'showbreak'` value before applying the +-- additional indent. +-- (default: off) +-- list:{n} Adds an additional indent for lines that match a +-- numbered or bulleted list (using the +-- `'formatlistpat'` setting). +-- list:-1 Uses the length of a match with `'formatlistpat'` +-- for indentation. +-- (default: 0) +-- column:{n} Indent at column {n}. Will overrule the other +-- sub-options. Note: an additional indent may be +-- added for the `'showbreak'` setting. +-- (default: off) +vim.wo.breakindentopt = "" +vim.wo.briopt = vim.wo.breakindentopt +-- `'colorcolumn'` `'cc'` string (default "") +-- local to window +-- `'colorcolumn'` is a comma-separated list of screen columns that are +-- highlighted with ColorColumn |hl-ColorColumn|. Useful to align +-- text. Will make screen redrawing slower. +-- The screen column can be an absolute number, or a number preceded with +-- `'+'` or `'-'` , which is added to or subtracted from `'textwidth'` . > +-- +-- :set cc=+1 " highlight column after `'textwidth'` +-- :set cc=+1,+2,+3 " highlight three columns after `'textwidth'` +-- :hi ColorColumn ctermbg=lightgrey guibg=lightgrey +-- < +-- When `'textwidth'` is zero then the items with `'-'` and `'+'` are not used. +-- A maximum of 256 columns are highlighted. +vim.wo.colorcolumn = "" +vim.wo.cc = vim.wo.colorcolumn +-- `'concealcursor'` `'cocu'` string (default: "") +-- local to window +-- Sets the modes in which text in the cursor line can also be concealed. +-- When the current mode is listed then concealing happens just like in +-- other lines. +-- n Normal mode +-- v Visual mode +-- i Insert mode +-- c Command line editing, for `'incsearch'` +-- +-- `'v'` applies to all lines in the Visual area, not only the cursor. +-- A useful value is "nc". This is used in help files. So long as you +-- are moving around text is concealed, but when starting to insert text +-- or selecting a Visual area the concealed text is displayed, so that +-- you can see what you are doing. +-- Keep in mind that the cursor position is not always where it's +-- displayed. E.g., when moving vertically it may change column. +vim.wo.concealcursor = "" +vim.wo.cocu = vim.wo.concealcursor +-- `'conceallevel'` `'cole'` number (default 0) +-- local to window +-- Determine how text with the "conceal" syntax attribute |:syn-conceal| +-- is shown: +-- +-- Value Effect ~ +-- 0 Text is shown normally +-- 1 Each block of concealed text is replaced with one +-- character. If the syntax item does not have a custom +-- replacement character defined (see |:syn-cchar|) the +-- character defined in `'listchars'` is used. +-- It is highlighted with the "Conceal" highlight group. +-- 2 Concealed text is completely hidden unless it has a +-- custom replacement character defined (see +-- |:syn-cchar|). +-- 3 Concealed text is completely hidden. +-- +-- Note: in the cursor line concealed text is not hidden, so that you can +-- edit and copy the text. This can be changed with the `'concealcursor'` +-- option. +vim.wo.conceallevel = 0 +vim.wo.cole = vim.wo.conceallevel +-- `'cursorbind'` `'crb'` boolean (default off) +-- local to window +-- When this option is set, as the cursor in the current +-- window moves other cursorbound windows (windows that also have +-- this option set) move their cursors to the corresponding line and +-- column. This option is useful for viewing the +-- differences between two versions of a file (see `'diff'` ); in diff mode, +-- inserted and deleted lines (though not characters within a line) are +-- taken into account. +vim.wo.cursorbind = false +vim.wo.crb = vim.wo.cursorbind +-- `'cursorcolumn'` `'cuc'` boolean (default off) +-- local to window +-- Highlight the screen column of the cursor with CursorColumn +-- |hl-CursorColumn|. Useful to align text. Will make screen redrawing +-- slower. +-- If you only want the highlighting in the current window you can use +-- these autocommands: > +-- au WinLeave * set nocursorline nocursorcolumn +-- au WinEnter * set cursorline cursorcolumn +-- < +vim.wo.cursorcolumn = false +vim.wo.cuc = vim.wo.cursorcolumn diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/uv.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/uv.lua new file mode 100644 index 00000000..e7b4d607 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/uv.lua @@ -0,0 +1,4156 @@ +---@meta + +---@meta +---@diagnostic disable: duplicate-set-field + +-- TODO: define @source for all sections and methods + +-- TODO: things like uv.tty_set_mode has in the description- +-- "look below for possible values" and then we rely on lls to list the alias +-- we need a way to list those off when generating the docs + +--- +---The [luv](https://github.com/luvit/luv/) project provides access to the multi-platform support library +---[libuv](http://libuv.org/) in Lua code. It was primarily developed for the [luvit](https://github.com/luvit/luvit/) project as +---the built-in `uv` module, but can be used in other Lua environments. +--- +---More information about the core libuv library can be found at the original +---[libuv documentation page](http://docs.libuv.org/en/v1.x/). +--- +---### TCP Echo Server Example +--- +---Here is a small example showing a TCP echo server: +--- +---```lua +---local uv = require("luv") -- "luv" when stand-alone, "uv" in luvi apps +--- +---local server = uv.new_tcp() +---server:bind("127.0.0.1", 1337) +---server:listen(128, function (err) +--- assert(not err, err) +--- local client = uv.new_tcp() +--- server:accept(client) +--- client:read_start(function (err, chunk) +--- assert(not err, err) +--- if chunk then +--- client:write(chunk) +--- else +--- client:shutdown() +--- client:close() +--- end +--- end) +---end) +---print("TCP server listening at 127.0.0.1 port 1337") +---uv.run() -- an explicit run call is necessary outside of luvit +---``` +--- +---### Module Layout +--- +---The luv library contains a single Lua module referred to hereafter as `uv` for +---simplicity. This module consists mostly of functions with names corresponding to +---their original libuv versions. For example, the libuv function `uv_tcp_bind` has +---a luv version at `uv.tcp_bind`. Currently, only two non-function fields exists: +---`uv.constants` and `uv.errno`, which are tables. +--- +---### Functions vs Methods +--- +---In addition to having simple functions, luv provides an optional method-style +---API. For example, `uv.tcp_bind(server, host, port)` can alternatively be called +---as `server:bind(host, port)`. Note that the first argument `server` becomes the +---object and `tcp_` is removed from the function name. Method forms are +---documented below where they exist. +--- +---### Synchronous vs Asynchronous Functions +--- +---Functions that accept a callback are asynchronous. These functions may +---immediately return results to the caller to indicate their initial status, but +---their final execution is deferred until at least the next libuv loop iteration. +---After completion, their callbacks are executed with any results passed to it. +--- +---Functions that do not accept a callback are synchronous. These functions +---immediately return their results to the caller. +--- +---Some (generally FS and DNS) functions can behave either synchronously or +---asynchronously. If a callback is provided to these functions, they behave +---asynchronously; if no callback is provided, they behave synchronously. +--- +---### Pseudo-Types +--- +---Some unique types are defined. These are not actual types in Lua, but they are +---used here to facilitate documenting consistent behavior: +---- `fail`: an assertable `nil, string, string` tuple (see [Error handling][]) +---- `callable`: a `function`; or a `table` or `userdata` with a `__call` +--- metamethod +---- `buffer`: a `string` or a sequential `table` of `string`s +---- `threadargs`: variable arguments (`...`) of type `nil`, `boolean`, `number`, +--- `string`, or `userdata`, numbers of argument limited to 9. +--- +---@namespace +---@class uv +---@section Libuv in Lua +local uv = {} + +---@alias uv.aliases.buffer string|string[] + +---@alias uv.aliases.threadargs userdata|string|number|boolean|nil # numbers of argument limited to 9. + + + +--- +---@section Contents +--- +---This documentation is mostly a retelling of the [libuv API documentation][] +---within the context of luv's Lua API. Low-level implementation details and +---unexposed C functions and types are not documented here except for when they +---are relevant to behavior seen in the Lua module. +--- +--- - [Error handling][] +--- - [Version checking][] +--- - [`uv_loop_t`][] — Event loop +--- - [`uv_req_t`][] — Base request +--- - [`uv_handle_t`][] — Base handle +--- - [`uv_timer_t`][] — Timer handle +--- - [`uv_prepare_t`][] — Prepare handle +--- - [`uv_check_t`][] — Check handle +--- - [`uv_idle_t`][] — Idle handle +--- - [`uv_async_t`][] — Async handle +--- - [`uv_poll_t`][] — Poll handle +--- - [`uv_signal_t`][] — Signal handle +--- - [`uv_process_t`][] — Process handle +--- - [`uv_stream_t`][] — Stream handle +--- - [`uv_tcp_t`][] — TCP handle +--- - [`uv_pipe_t`][] — Pipe handle +--- - [`uv_tty_t`][] — TTY handle +--- - [`uv_udp_t`][] — UDP handle +--- - [`uv_fs_event_t`][] — FS Event handle +--- - [`uv_fs_poll_t`][] — FS Poll handle +--- - [File system operations][] +--- - [Thread pool work scheduling][] +--- - [DNS utility functions][] +--- - [Threading and synchronization utilities][] +--- - [Miscellaneous utilities][] +--- - [Metrics operations][] +--- + +-- TODO: above section should probably not be hardcoded + + + +--- +---In libuv, errors are negative numbered constants; however, while those errors are exposed through `uv.errno`, +---the functions used to handle them are not exposed to luv users. Instead, if an +---internal error is encountered, the luv function will return to the caller an +---assertable `nil, err, name` tuple. +--- +---- `nil` idiomatically indicates failure +---- `err` is a string with the format `{name}: {message}` +--- - `{name}` is the error name provided internally by `uv_err_name` +--- - `{message}` is a human-readable message provided internally by `uv_strerror` +---- `name` is the same string used to construct `err` +--- +---This tuple is referred to below as the `fail` pseudo-type. +--- +---When a function is called successfully, it will return either a value that is +---relevant to the operation of the function, or the integer `0` to indicate +---success, or sometimes nothing at all. These cases are documented below. +--- +---@alias uv.errno {E2BIG: integer, EACCES: integer, EADDRINUSE: integer, EADDRNOTAVAIL: integer, EAFNOSUPPORT: integer, EAGAIN: integer, EAI_ADDRFAMILY: integer, EAI_AGAIN: integer, EAI_BADFLAGS: integer, EAI_BADHINTS: integer, EAI_CANCELED: integer, EAI_FAIL: integer, EAI_FAMILY: integer, EAI_MEMORY: integer, EAI_NODATA: integer, EAI_NONAME: integer, EAI_OVERFLOW: integer, EAI_PROTOCOL: integer, EAI_SERVICE: integer, EAI_SOCKTYPE: integer, EALREADY: integer, EBADF: integer, EBUSY: integer, ECANCELED: integer, ECHARSET: integer, ECONNABORTED: integer, ECONNREFUSED: integer, ECONNRESET: integer, EDESTADDRREQ: integer, EEXIST: integer, EFAULT: integer, EFBIG: integer, EFTYPE: integer, EHOSTDOWN: integer, EHOSTUNREACH: integer, EILSEQ: integer, EINTR: integer, EINVAL: integer, EIO: integer, EISCONN: integer, EISDIR: integer, ELOOP: integer, EMFILE: integer, EMLINK: integer, EMSGSIZE: integer, ENAMETOOLONG: integer, ENETDOWN: integer, ENETUNREACH: integer, ENFILE: integer, ENOBUFS: integer, ENODATA: integer, ENODEV: integer, ENOENT: integer, ENOMEM: integer, ENONET: integer, ENOPROTOOPT: integer, ENOSPC: integer, ENOSYS: integer, ENOTCONN: integer, ENOTDIR: integer, ENOTEMPTY: integer, ENOTSOCK: integer, ENOTSUP: integer, ENOTTY: integer, ENXIO: integer, EOF: integer, EOVERFLOW: integer, EPERM: integer, EPIPE: integer, EPROTO: integer, EPROTONOSUPPORT: integer, EPROTOTYPE: integer, ERANGE: integer, EREMOTEIO: integer, EROFS: integer, ESHUTDOWN: integer, ESOCKTNOSUPPORT: integer, ESPIPE: integer, ESRCH: integer, ETIMEDOUT: integer, ETXTBSY: integer, EXDEV: integer, UNKNOWN: integer} +---@section Error Handling + +-- TODO: errno fields should have descriptions! + +--- +---A table value which exposes error constants as a map, where the key is the +---error name (without the `UV_` prefix) and its value is a negative number. +---See Libuv's "Error constants" page for further details. +---(https://docs.libuv.org/en/v1.x/errors.html#error-constants) +--- +---@type uv.errno +uv.errno = {} + + + +--- +---@section Version Checking +--- + +--- +---Returns the libuv version packed into a single integer. 8 bits are used for each +---component, with the patch number stored in the 8 least significant bits. For +---example, this would be 0x010203 in libuv 1.2.3. +--- +---@return integer +---@nodiscard +function uv.version() end + +--- +---Returns the libuv version number as a string. For example, this would be "1.2.3" +---in libuv 1.2.3. For non-release versions, the version suffix is included. +--- +---@return string +---@nodiscard +function uv.version_string() end + + + +--- +---The event loop is the central part of libuv's functionality. It takes care of +-- polling for I/O and scheduling callbacks to be run based on different sources of events. +--- +---In luv, there is an implicit uv loop for every Lua state that loads the library. +---You can use this library in an multi-threaded environment as long as each thread +---has it's own Lua state with its corresponding own uv loop. This loop is not +---directly exposed to users in the Lua module. +--- +---@class uv_loop_t: userdata +---@section Event loop +local uv_loop_t = {} + +---@alias uv.aliases.run_mode +---Runs the event loop until there are no more active and referenced handles or requests. +---Returns `true` if `uv.stop()` was called and there are still active handles or requests. +---Returns `false` in all other cases. +---|>'default' +---Poll for I/O once. Note that this function blocks if there are no +---pending callbacks. Returns `false` when done (no active handles or requests +---left), or `true` if more callbacks are expected (meaning you should run the +---event loop again sometime in the future). +---|'once' +---Poll for I/O once but don't block if there are no pending callbacks. +---Returns `false` if done (no active handles or requests left), +---or `true` if more callbacks are expected (meaning you should run the event loop again sometime in the future). +---|'nowait' + +---@alias uv.aliases.loop_configure_option +---Block a signal when polling for new events. +---The second argument to loop_configure() is the signal name (as a lowercase string) or the signal number. +---This operation is currently only implemented for `"sigprof"` signals, to suppress unnecessary wakeups when using a sampling profiler. +---Requesting other signals will fail with `EINVAL`. +---|'block_signal' +---Accumulate the amount of idle time the event loop spends in the event provider. +---This option is necessary to use `metrics_idle_time()`. +---|'metrics_idle_time' + +--- +---Closes all internal loop resources. In normal execution, the loop will +---automatically be closed when it is garbage collected by Lua, so it is not +---necessary to explicitly call `loop_close()`. Call this function only after the +---loop has finished executing and all open handles and requests have been closed, +---or it will return `EBUSY`. +--- +---@return 0|nil success, string? err_name, string? err_msg +function uv.loop_close() end + +--- +---This function runs the event loop. It will act differently depending on the +---specified mode: +---- `"default"`: Runs the event loop until there are no more active and +---referenced handles or requests. Returns `true` if `uv.stop()` was called and +---there are still active handles or requests. Returns `false` in all other +---cases. +---- `"once"`: Poll for I/O once. Note that this function blocks if there are no +---pending callbacks. Returns `false` when done (no active handles or requests +---left), or `true` if more callbacks are expected (meaning you should run the +---event loop again sometime in the future). +---- `"nowait"`: Poll for I/O once but don't block if there are no pending +---callbacks. Returns `false` if done (no active handles or requests left), +---or `true` if more callbacks are expected (meaning you should run the event +---loop again sometime in the future). +--- +---**Note**: Luvit will implicitly call `uv.run()` after loading user code, but if +---you use the luv bindings directly, you need to call this after registering +---your initial set of event callbacks to start the event loop. +--- +---@param mode uv.aliases.run_mode|nil +---@return boolean|nil, string? err_name, string? err_msg +function uv.run(mode) end + +--- +---Set additional loop options. You should normally call this before the first call +---to uv_run() unless mentioned otherwise. +---Supported options: +---- `"block_signal"`: Block a signal when polling for new events. The second argument +---to loop_configure() is the signal name (as a lowercase string) or the signal number. +---This operation is currently only implemented for `"sigprof"` signals, to suppress +---unnecessary wakeups when using a sampling profiler. Requesting other signals will +---fail with `EINVAL`. +---- `"metrics_idle_time"`: Accumulate the amount of idle time the event loop spends +---in the event provider. This option is necessary to use `metrics_idle_time()`. +---An example of a valid call to this function is: +---```lua +---uv.loop_configure("block_signal", "sigprof") +---``` +--- +---**Note**: Be prepared to handle the `ENOSYS` error; it means the loop option is +---not supported by the platform. +--- +---@param option uv.aliases.loop_configure_option +---@param ... any +---@return 0|nil success, string? err_name, string? err_msg +function uv.loop_configure(option, ...) end + +--- +---If the loop is running, returns a string indicating the mode in use. If the loop +---is not running, `nil` is returned instead. +--- +---@return uv.aliases.run_mode|nil +---@nodiscard +function uv.loop_mode() end + +--- +---Returns `true` if there are referenced active handles, active requests, or +---closing handles in the loop; otherwise, `false`. +--- +---@return boolean|nil, string? err_name, string? err_msg +---@nodiscard +function uv.loop_alive() end + +--- +---Stop the event loop, causing `uv.run()` to end as soon as possible. This +---will happen not sooner than the next loop iteration. If this function was called +---before blocking for I/O, the loop won't block for I/O on this iteration. +--- +function uv.stop() end + +--- +---Get backend file descriptor. Only kqueue, epoll, and event ports are supported. +---This can be used in conjunction with `uv.run("nowait")` to poll in one thread +---and run the event loop's callbacks in another +--- +---**Note**: Embedding a kqueue fd in another kqueue pollset doesn't work on all +---platforms. It's not an error to add the fd but it never generates events. +--- +---@return integer|nil +---@nodiscard +function uv.backend_fd() end + +--- +---Get the poll timeout. The return value is in milliseconds, or -1 for no timeout. +--- +---@return integer +---@nodiscard +function uv.backend_timeout() end + +--- +---Returns the current timestamp in milliseconds. The timestamp is cached at the +---start of the event loop tick, see `uv.update_time()` for details and rationale. +---The timestamp increases monotonically from some arbitrary point in time. Don't +---make assumptions about the starting point, you will only get disappointed. +--- +---**Note**: Use `uv.hrtime()` if you need sub-millisecond granularity. +--- +---@return integer +---@nodiscard +function uv.now() end + +--- +---Update the event loop's concept of "now". Libuv caches the current time at the +---start of the event loop tick in order to reduce the number of time-related +---system calls. +---You won't normally need to call this function unless you have callbacks that +---block the event loop for longer periods of time, where "longer" is somewhat +---subjective but probably on the order of a millisecond or more. +--- +function uv.update_time() end + +--- +---Walk the list of handles: `callback` will be executed with each handle. +--- +---Example usage of uv.walk to close all handles that aren't already closing. +---```lua +---uv.walk(function (handle) +--- if not handle:is_closing() then +--- handle:close() +--- end +---end) +---``` +--- +---@param callback fun(handle: uv.aliases.handle_instances) +function uv.walk(callback) end + + + +--- +---`uv_req_t` is the base type for all libuv request types. +--- +---@class uv_req_t: userdata +---@section Base request +local uv_req_t = {} + +---@alias uv.aliases.req_struct_name +---|'unknown' # 0 +---|'req' # 1 +---|'connect' # 2 +---|'write' # 3 +---|'shutdown' # 4 +---|'udp_send' # 5 +---|'fs' # 6 +---|'work' # 7 +---|'getaddrinfo' # 8 +---|'getnameinfo' # 9 +---|'random' # 10 + +---@alias uv.aliases.req_struct_type +---|0 # unknown +---|1 # req +---|2 # connect +---|3 # write +---|4 # shutdown +---|5 # udp_send +---|6 # fs +---|7 # work +---|8 # getaddrinfo +---|9 # getnameinfo +---|10 # random + +--- +---Cancel a pending request. Fails if the request is executing or has finished +---executing. Only cancellation of `uv_fs_t`, `uv_getaddrinfo_t`, +---`uv_getnameinfo_t` and `uv_work_t` requests is currently supported. +--- +---@param req uv_fs_t|uv_getaddrinfo_t|uv_getnameinfo_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.cancel(req) end +uv_req_t.cancel = uv.cancel + +--- +---Returns the name of the struct for a given request (e.g. `"fs"` for `uv_fs_t`) +---and the libuv enum integer for the request's type (`uv_req_type`). +--- +---@param req uv_req_t +---@return uv.aliases.req_struct_name +---@return uv.aliases.req_struct_type +function uv.req_get_type(req) end +uv_req_t.get_type = uv.req_get_type + + + +--- +---`uv_handle_t` is the base type for all libuv handle types. All API functions +---defined here work with any handle type. +--- +---@class uv_handle_t: userdata +---@section Base handle +local uv_handle_t = {} + +---@alias uv.aliases.handle_instances +---|uv_handle_t +---|uv_stream_t +---|uv_tcp_t +---|uv_pipe_t +---|uv_tty_t +---|uv_udp_t +---|uv_fs_event_t +---|uv_fs_poll_t + +---@alias uv.aliases.handle_struct_name +---|'unknown' # 0 +---|'"async"' # 1 +---|'check' # 2 +---|'fs_event' # 3 +---|'fs_poll' # 4 +---|'handle' # 5 +---|'idle' # 6 +---|'pipe' # 7 +---|'poll' # 8 +---|'prepare' # 9 +---|'process' # 10 +---|'stream' # 11 +---|'tcp' # 12 +---|'timer' # 13 +---|'tty' # 14 +---|'udp' # 15 +---|'signal' # 16 +---|'file' # 17 + +---@alias uv.aliases.handle_struct_type +---|0 # unknown +---|1 # async +---|2 # check +---|3 # fs_event +---|4 # fs_poll +---|5 # handle +---|6 # idle +---|7 # pipe +---|8 # poll +---|9 # prepare +---|10 # process +---|11 # stream +---|12 # tcp +---|13 # timer +---|14 # tty +---|15 # udp +---|16 # signal +---|17 # file + +--- +---Returns `true` if the handle is active, `false` if it's inactive. What "active” +---means depends on the type of handle: +--- +--- - A `uv_async_t` handle is always active and cannot be deactivated, except +--- by closing it with `uv.close()`. +--- +--- - A `uv_pipe_t`, `uv_tcp_t`, `uv_udp_t`, etc. handle - basically +--- any handle that deals with I/O - is active when it is doing something that +--- involves I/O, like reading, writing, connecting, accepting new connections, +--- etc. +--- +--- - A `uv_check_t`, `uv_idle_t`, `uv_timer_t`, etc. handle is active +--- when it has been started with a call to `uv.check_start()`, `uv.idle_start()`, +--- `uv.timer_start()` etc. until it has been stopped with a call to its +--- respective stop function. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@return boolean|nil, string? err_name, string? err_msg +---@nodiscard +function uv.is_active(handle) end +uv_handle_t.is_active = uv.is_active + +--- +---Returns `true` if the handle is closing or closed, `false` otherwise. +--- +---**Note**: This function should only be used between the initialization of the +---handle and the arrival of the close callback. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@return boolean|nil, string? err_name, string? err_msg +---@nodiscard +function uv.is_closing(handle) end +uv_handle_t.is_closing = uv.is_closing + +--- +---Request handle to be closed. `callback` will be called asynchronously after this +---call. This MUST be called on each handle before memory is released. +--- +---Handles that wrap file descriptors are closed immediately but `callback` will +---still be deferred to the next iteration of the event loop. It gives you a chance +---to free up any resources associated with the handle. +--- +---In-progress requests, like `uv_connect_t` or `uv_write_t`, are cancelled and +---have their callbacks called asynchronously with `ECANCELED`. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@param callback? function +function uv.close(handle, callback) end +uv_handle_t.close = uv.close + +--- +---Reference the given handle. References are idempotent, that is, if a handle is +---already referenced calling this function again will have no effect. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +function uv.ref(handle) end +uv_handle_t.ref = uv.ref + +--- +---Un-reference the given handle. References are idempotent, that is, if a handle +---is not referenced calling this function again will have no effect. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +function uv.unref(handle) end +uv_handle_t.unref = uv.unref + +--- +---Returns `true` if the handle referenced, `false` if not. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@return boolean|nil, string? err_name, string? err_msg +---@nodiscard +function uv.has_ref(handle) end +uv_handle_t.has_ref = uv.has_ref + +--- +---Gets or sets the size of the send buffer that the operating system uses for the socket. +--- +---If `size` is omitted (or `0`), this will return the current send buffer size; otherwise, this will use `size` to set the new send buffer size. +--- +---This function works for TCP, pipe and UDP handles on Unix and for TCP and UDP +---handles on Windows. +--- +---**Note**: Linux will set double the size and return double the size of the +---original set value. +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.send_buffer_size(handle) end +---@param size? integer # (default: `0`) +---@return 0|nil, string? err_name, string? err_msg +function uv.send_buffer_size(handle, size) end +uv_handle_t.send_buffer_size = uv.send_buffer_size + +--- +---Gets or sets the size of the receive buffer that the operating system uses for the socket. +--- +---If `size` is omitted (or `0`), this will return the current send buffer size; otherwise, this will use `size` to set the new send buffer size. +--- +---This function works for TCP, pipe and UDP handles on Unix and for TCP and UDP +---handles on Windows. +--- +---**Note**: Linux will set double the size and return double the size of the +---original set value. +--- +---@param handle uv_handle_t `userdata` for sub-type of `uv_handle_t` +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.recv_buffer_size(handle) end +---@param size? integer # (default: `0`) +---@return 0|nil, string? err_name, string? err_msg +function uv.recv_buffer_size(handle, size) end +uv_handle_t.recv_buffer_size = uv.recv_buffer_size + +--- +---Gets the platform dependent file descriptor equivalent. +--- +---The following handles are supported: TCP, pipes, TTY, UDP and poll. Passing any +---other handle type will fail with `EINVAL`. +--- +---If a handle doesn't have an attached file descriptor yet or the handle itself +---has been closed, this function will return `EBADF`. +--- +---**Warning**: Be very careful when using this function. libuv assumes it's in +---control of the file descriptor so any change to it may lead to malfunction. +--- +---@param handle uv_handle_t `userdata` for sub-type of `uv_handle_t` +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.fileno(handle) end +uv_handle_t.fileno = uv.fileno + +--- +---Returns the name of the struct for a given handle (e.g. `"pipe"` for `uv_pipe_t`) +---and the libuv enum integer for the handle's type (`uv_handle_type`). +--- +---@param handle uv_handle_t # `userdata` for sub-type of `uv_handle_t` +---@return uv.aliases.handle_struct_name +---@return uv.aliases.handle_struct_type +function uv.handle_get_type(handle) end +uv_handle_t.get_type = uv.handle_get_type + + + +--- +---@section Reference counting +--- +---The libuv event loop (if run in the default mode) will run until there are no +---active and referenced handles left. The user can force the loop to exit early by +---unreferencing handles which are active, for example by calling `uv.unref()` +---after calling `uv.timer_start()`. +--- +---A handle can be referenced or unreferenced, the refcounting scheme doesn't use a +---counter, so both operations are idempotent. +--- +---All handles are referenced when active by default, see `uv.is_active()` for a +---more detailed explanation on what being active involves. +--- + + + +--- +---Timer handles are used to schedule callbacks to be called in the future. +--- +---@class uv_timer_t: uv_handle_t +---@section Timer handle +local uv_timer_t = {} + +--- +---Creates and initializes a new `uv_timer_t`. Returns the Lua userdata wrapping it. +--- +---Some examples: +---```lua +----- Creating a simple setTimeout wrapper +---local function setTimeout(timeout, callback) +--- local timer = uv.new_timer() +--- timer:start(timeout, 0, function () +--- timer:stop() +--- timer:close() +--- callback() +--- end) +--- return timer +---end +--- +----- Creating a simple setInterval wrapper +---local function setInterval(interval, callback) +--- local timer = uv.new_timer() +--- timer:start(interval, interval, function () +--- callback() +--- end) +--- return timer +---end +--- +----- And clearInterval +---local function clearInterval(timer) +--- timer:stop() +--- timer:close() +---end +---``` +--- +---@return uv_timer_t +---@nodiscard +function uv.new_timer() end + +--- +---Start the timer. `timeout` and `repeat_n` are in milliseconds. +--- +---If `timeout` is zero, the callback fires on the next event loop iteration. If +---`repeat_n` is non-zero, the callback fires first after `timeout` milliseconds and +---then repeatedly after `repeat_n` milliseconds. +--- +---@param timer uv_timer_t +---@param timeout integer +---@param repeat_n integer +---@param callback fun() +---@return 0|nil success, string? err_name, string? err_msg +function uv.timer_start(timer, timeout, repeat_n, callback) end +uv_timer_t.start = uv.timer_start + +--- +---Stop the timer, the callback will not be called anymore. +--- +---@param timer uv_timer_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.timer_stop(timer) end +uv_timer_t.stop = uv.timer_stop + +--- +---Stop the timer, and if it is repeating restart it using the repeat value as the +---timeout. If the timer has never been started before it raises `EINVAL`. +--- +---@param timer uv_timer_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.timer_again(timer) end +uv_timer_t.again = uv.timer_again + +--- +---Set the repeat interval value in milliseconds. The timer will be scheduled to +---run on the given interval, regardless of the callback execution duration, and +---will follow normal timer semantics in the case of a time-slice overrun. +--- +---For example, if a 50 ms repeating timer first runs for 17 ms, it will be +---scheduled to run again 33 ms later. If other tasks consume more than the 33 ms +---following the first timer callback, then the callback will run as soon as +---possible. +--- +---@param timer uv_timer_t +---@param repeat_n integer +function uv.timer_set_repeat(timer, repeat_n) end +uv_timer_t.set_repeat = uv.timer_set_repeat + +--- +---Get the timer repeat value. +--- +---@param timer uv_timer_t +---@return integer +---@nodiscard +function uv.timer_get_repeat(timer) end +uv_timer_t.get_repeat = uv.timer_get_repeat + +--- +---Get the timer due value or 0 if it has expired. The time is relative to `uv.now()`. +--- +---**Note**: New in libuv version 1.40.0. +--- +---@param timer uv_timer_t +---@return integer +---@nodiscard +function uv.timer_get_due_in(timer) end +uv_timer_t.get_due_in = uv.timer_get_due_in + + + +--- +---Prepare handles will run the given callback once per loop iteration, right +---before polling for I/O. +--- +---```lua +---local prepare = uv.new_prepare() +---prepare:start(function() +--- print("Before I/O polling") +---end) +---``` +--- +---@class uv_prepare_t: uv_handle_t +---@section Prepare handle +local uv_prepare_t = {} + +--- +---Creates and initializes a new `uv_prepare_t`. +---Returns the Lua userdata wrapping it. +--- +---@return uv_prepare_t +---@nodiscard +function uv.new_prepare() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- confirmed to never return error see libuv/unix/loop-watcher + +--- +---Start the handle with the given callback. +--- +---@param prepare uv_prepare_t +---@param callback fun() +---@return 0|nil success, string? err_name, string? err_msg +function uv.prepare_start(prepare, callback) end +uv_prepare_t.start = uv.prepare_start + +--- +---Stop the handle, the callback will no longer be called. +--- +---@param prepare uv_prepare_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.prepare_stop(prepare) end +uv_prepare_t.stop = uv.prepare_stop + + +--- +---Check handles will run the given callback once per loop iteration, right after +---polling for I/O. +--- +---```lua +---local check = uv.new_check() +---check:start(function() +--- print("After I/O polling") +---end) +---``` +--- +---@class uv_check_t: uv_handle_t +---@section Check handle +local uv_check_t = {} + +--- +---Creates and initializes a new `uv_check_t`. Returns the Lua userdata wrapping it. +--- +---@return uv_check_t +---@nodiscard +function uv.new_check() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- confirmed to never error see libuv/unix/loop-watcher + +--- +---Start the handle with the given callback. +--- +---@param check uv_check_t +---@param callback fun() +---@return 0|nil success, string? err_name, string? err_msg +function uv.check_start(check, callback) end +uv_check_t.start = uv.check_start + +--- +---Stop the handle, the callback will no longer be called. +--- +---@param check uv_check_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.check_stop(check) end +uv_check_t.stop = uv.check_stop + + + +--- +---Idle handles will run the given callback once per loop iteration, right before +---the `uv_prepare_t` handles. +--- +---**Note**: The notable difference with prepare handles is that when there are +---active idle handles, the loop will perform a zero timeout poll instead of +---blocking for I/O. +--- +---**Warning**: Despite the name, idle handles will get their callbacks called on +---every loop iteration, not when the loop is actually "idle". +--- +---```lua +---local idle = uv.new_idle() +---idle:start(function() +--- print("Before I/O polling, no blocking") +---end) +---``` +--- +---@class uv_idle_t: uv_handle_t +---@section Idle handle +local uv_idle_t = {} + +--- +---Creates and initializes a new `uv_idle_t`. Returns the Lua userdata wrapping it. +--- +---@return uv_idle_t +---@nodiscard +function uv.new_idle() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- confirmed to never error see libuv/unix/loop-watcher + +--- +---Start the handle with the given callback. +--- +---@param idle uv_idle_t +---@param callback fun() +---@return 0|nil success, string? err_name, string? err_msg +function uv.idle_start(idle, callback) end +uv_idle_t.start = uv.idle_start + +--- +---Stop the handle, the callback will no longer be called. +--- +---@param idle uv_idle_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.idle_stop(idle) end +uv_idle_t.stop = uv.idle_stop + + + +--- +---Async handles allow the user to "wakeup" the event loop and get a callback +---called from another thread. +--- +---```lua +---local async +---async = uv.new_async(function() +--- print("async operation ran") +--- async:close() +---end) +--- +---async:send() +---``` +--- +---@class uv_async_t: uv_handle_t +---@section Async handle +local uv_async_t = {} + +--- +---Creates and initializes a new `uv_async_t`. Returns the Lua userdata wrapping +---it. A `nil` callback is allowed. +--- +---**Note**: Unlike other handle initialization functions, this immediately starts +---the handle. +--- +---@param callback fun(...: uv.aliases.threadargs)|nil +---@return uv_async_t|nil handle, string? err_name, string? err_msg +---@nodiscard +function uv.new_async(callback) end + +-- TODO: make sure that the above method can indeed return nil + error. +-- seems like this can technically fail when loop.async_io_watcher.fd is -1 +-- but not sure if that is ever practically true for luv +-- TODO: Luv code suggests that a callback is required, the docs seem to be wrong. + +--- +---Wakeup the event loop and call the async handle's callback. +--- +---**Note**: It's safe to call this function from any thread. The callback will be +---called on the loop thread. +--- +---**Warning**: libuv will coalesce calls to `uv.async_send(async)`, that is, not +---every call to it will yield an execution of the callback. For example: if +---`uv.async_send()` is called 5 times in a row before the callback is called, the +---callback will only be called once. If `uv.async_send()` is called again after +---the callback was called, it will be called again. +--- +---@param async uv_async_t +---@param ... uv.aliases.threadargs +---@return 0|nil success, string? err_name, string? err_msg +function uv.async_send(async, ...) end +uv_async_t.send = uv.async_send + + + +--- +---Poll handles are used to watch file descriptors for readability and writability, +---similar to the purpose of [poll(2)](http://linux.die.net/man/2/poll). +--- +---The purpose of poll handles is to enable integrating external libraries that +---rely on the event loop to signal it about the socket status changes, like c-ares +---or libssh2. Using `uv_poll_t` for any other purpose is not recommended; +---`uv_tcp_t`, `uv_udp_t`, etc. provide an implementation that is faster and more +---scalable than what can be achieved with `uv_poll_t`, especially on Windows. +--- +---It is possible that poll handles occasionally signal that a file descriptor is +---readable or writable even when it isn't. The user should therefore always be +---prepared to handle EAGAIN or equivalent when it attempts to read from or write +---to the fd. +--- +---It is not okay to have multiple active poll handles for the same socket, this +---can cause libuv to busyloop or otherwise malfunction. +--- +---The user should not close a file descriptor while it is being polled by an +---active poll handle. This can cause the handle to report an error, but it might +---also start polling another socket. However the fd can be safely closed +---immediately after a call to `uv.poll_stop()` or `uv.close()`. +--- +---**Note**: On windows only sockets can be polled with poll handles. On Unix any +---file descriptor that would be accepted by poll(2) can be used. +--- +---@class uv_poll_t: uv_handle_t +---@section Poll handle +local uv_poll_t = {} + +---@alias uv.aliases.poll_events +---|'"r"' +---|'"w"' +---|>'"rw"' +---|'"d"' +---|'"rd"' +---|'"wd"' +---|'"rwd"' +---|'"p"' +---|'"rp"' +---|'"wp"' +---|'"rwp"' +---|'"dp"' +---|'"rdp"' +---|'"wdp"' +---|'"rwdp"' + +--- +---Initialize the handle using a file descriptor. +---The file descriptor is set to non-blocking mode. +--- +---@param fd integer # the file descriptor +---@return uv_poll_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_poll(fd) end + +--- +---Initialize the handle using a socket descriptor. On Unix this is identical to +---`uv.new_poll()`. On windows it takes a SOCKET handle. +---The socket is set to non-blocking mode. +--- +---@param fd integer # the file descriptor +---@return uv_poll_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_socket_poll(fd) end + +--- +---Starts polling the file descriptor. +---See alias below for accepted `events`, where `r` is `READABLE`, `w` is `WRITABLE`, `d` is +---`DISCONNECT`, and `p` is `PRIORITIZED`. +---As soon as an event is detected +---the callback will be called with status set to 0, and the detected events set on +---the events field. +--- +---The user should not close the socket while the handle is active. If the user +---does that anyway, the callback may be called reporting an error status, but this +---is not guaranteed. +--- +---**Note** Calling `uv.poll_start()` on a handle that is already active is fine. +---Doing so will update the events mask that is being watched for. +--- +---@param poll uv_poll_t +---@param events uv.aliases.poll_events|nil # (default: `"rw"`) +---@param callback fun(err?: string, events?: uv.aliases.poll_events) +---@return 0|nil success, string? err_name, string? err_msg +function uv.poll_start(poll, events, callback) end +uv_poll_t.start = uv.poll_start + +--- +---Stop polling the file descriptor, the callback will no longer be called. +--- +---@param poll uv_poll_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.poll_stop(poll) end +uv_poll_t.stop = uv.poll_stop + + + +--- +---Signal handles implement Unix style signal handling on a per-event loop bases. +--- +---**Windows Notes:** +--- +---Reception of some signals is emulated on Windows: +--- - SIGINT is normally delivered when the user presses CTRL+C. However, like on +--- Unix, it is not generated when terminal raw mode is enabled. +--- - SIGBREAK is delivered when the user pressed CTRL + BREAK. +--- - SIGHUP is generated when the user closes the console window. On SIGHUP the +--- program is given approximately 10 seconds to perform cleanup. After that +--- Windows will unconditionally terminate it. +--- - SIGWINCH is raised whenever libuv detects that the console has been resized. +--- SIGWINCH is emulated by libuv when the program uses a uv_tty_t handle to write +--- to the console. SIGWINCH may not always be delivered in a timely manner; libuv +--- will only detect size changes when the cursor is being moved. When a readable +--- [`uv_tty_t`][] handle is used in raw mode, resizing the console buffer will +--- also trigger a SIGWINCH signal. +--- - Watchers for other signals can be successfully created, but these signals +--- are never received. These signals are: SIGILL, SIGABRT, SIGFPE, SIGSEGV, +--- SIGTERM and SIGKILL. +--- - Calls to raise() or abort() to programmatically raise a signal are not +--- detected by libuv; these will not trigger a signal watcher. +--- +---**Unix Notes:** +--- +--- - SIGKILL and SIGSTOP are impossible to catch. +--- - Handling SIGBUS, SIGFPE, SIGILL or SIGSEGV via libuv results into undefined +--- behavior. +--- - SIGABRT will not be caught by libuv if generated by abort(), e.g. through +--- assert(). +--- - On Linux SIGRT0 and SIGRT1 (signals 32 and 33) are used by the NPTL pthreads +--- library to manage threads. Installing watchers for those signals will lead to +--- unpredictable behavior and is strongly discouraged. Future versions of libuv +--- may simply reject them. +--- +---```lua +----- Create a new signal handler +---local signal = uv.new_signal() +----- Define a handler function +---uv.signal_start(signal, "sigint", function(signal) +--- print("got " .. signal .. ", shutting down") +--- os.exit(1) +---end) +---``` +--- +---@class uv_signal_t: uv_handle_t +---@section Signal handle +local uv_signal_t = {} + +---@alias uv.aliases.signals +---| "sigabrt" # Abort signal from abort(3) +---| "sigalrm" # Timer signal from alarm(2) +---| "sigbus" # Bus error (bad memory access) +---| "sigchld" # Child stopped or terminated +---| "sigcont" # Continue if stopped +---| "sigfpe" # Floating-point exception +---| "sighup" # Hangup detected on controlling terminal or death of controlling process +---| "sigill" # Illegal Instruction +---| "sigint" # Interrupt from keyboard +---| "sigio" # I/O now possible (4.2BSD) +---| "sigiot" # IOT trap. A synonym for sigabrt +---| "sigkill" # Kill signal +---| "sigpipe" # Broken pipe: write to pipe with no readers; see pipe(7) +---| "sigpoll" # Pollable event (Sys V); synonym for sigIO +---| "sigprof" # Profiling timer expired +---| "sigpwr" # Power failure (System V) +---| "sigquit" # Quit from keyboard +---| "sigsegv" # Invalid memory reference +---| "sigstkflt" # Stack fault on coprocessor +---| "sigstop" # Stop process +---| "sigtstp" # Stop typed at terminal +---| "sigsys" # Bad system call (SVr4); see also seccomp(2) +---| "sigterm" # Termination signal +---| "sigtrap" # Trace/breakpoint trap +---| "sigttin" # Terminal input for background process +---| "sigttou" # Terminal output for background process +---| "sigurg" # Urgent condition on socket (4.2BSD) +---| "sigusr1" # User-defined signal 1 +---| "sigusr2" # User-defined signal 2 +---| "sigvtalrm" # Virtual alarm clock (4.2BSD) +---| "sigxcpu" # CPU time limit exceeded (4.2BSD); see setrlimit(2) +---| "sigxfsz" # File size limit exceeded (4.2BSD);see setrlimit(2) +---| "sigwinch" # Window resize signal (4.3BSD, Sun) +---| "sigbreak" # CTRL + BREAK has been pressed +---| "siglost" # File lock lost + +--- +---Creates and initializes a new `uv_signal_t`. +---Returns the Lua userdata wrapping it. +--- +---@return uv_signal_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_signal() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- see uv_signal_init and uv__make_pipe, it will only fail if pipe2 fails +-- I belive it is very unlikely it would ever error for this call, but it is theortically possible + +--- +---Start the handle with the given callback, watching for the given signal. +--- +---@param signal uv_signal_t +---@param signum integer|uv.aliases.signals +---@param callback? fun(signum: uv.aliases.signals) +---@return 0|nil success, string? err_name, string? err_msg +function uv.signal_start(signal, signum, callback) end +uv_signal_t.start = uv.signal_start + +--- +---Same functionality as `uv.signal_start()` but the signal handler is reset the moment the signal is received. +--- +---@param signal uv_signal_t +---@param signum integer|uv.aliases.signals +---@param callback? fun(signum: uv.aliases.signals) +---@return 0|nil success, string? err_name, string? err_msg +function uv.signal_start_oneshot(signal, signum, callback) end +uv_signal_t.start_oneshot = uv.signal_start_oneshot + +--- +---Stop the handle, the callback will no longer be called. +--- +---@param signal uv_signal_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.signal_stop(signal) end +uv_signal_t.stop = uv.signal_stop + + + +--- +---Process handles will spawn a new process and allow the user to control it and +---establish communication channels with it using streams. +--- +---@class uv_process_t: uv_handle_t +---@section Process handle +local uv_process_t = {} + +---@alias uv.aliases.spawn_options {args?: string[], stdio?: table, env?: table, cwd?: string, uid?: integer, gid?: integer, verbatim?: boolean, detached?: boolean, hide?: boolean} + +-- TODO: add descriptions to above fields + +--- +---Disables inheritance for file descriptors / handles that this process inherited +---from its parent. The effect is that child processes spawned by this process +---don't accidentally inherit these handles. +---It is recommended to call this function as early in your program as possible, +---before the inherited file descriptors can be closed or duplicated. +--- +---**Note:** This function works on a best-effort basis: there is no guarantee that +---libuv can discover all file descriptors that were inherited. In general it does +---a better job on Windows than it does on Unix. +--- +function uv.disable_stdio_inheritance() end + +--- +---Initializes the process handle and starts the process. If the process is +---successfully spawned, this function will return the handle and pid of the child +---process. +---Possible reasons for failing to spawn would include (but not be limited to) the +---file to execute not existing, not having permissions to use the setuid or setgid +---specified, or not having enough memory to allocate for the new process. +--- +---```lua +---local stdin = uv.new_pipe() +---local stdout = uv.new_pipe() +---local stderr = uv.new_pipe() +--- +---print("stdin", stdin) +---print("stdout", stdout) +---print("stderr", stderr) +--- +---local handle, pid = uv.spawn("cat", { +--- stdio = {stdin, stdout, stderr} +---}, function(code, signal) -- on exit +--- print("exit code", code) +--- print("exit signal", signal) +---end) +--- +---print("process opened", handle, pid) +--- +---uv.read_start(stdout, function(err, data) +--- assert(not err, err) +--- if data then +--- print("stdout chunk", stdout, data) +--- else +--- print("stdout end", stdout) +--- end +---end) +--- +---uv.read_start(stderr, function(err, data) +--- assert(not err, err) +--- if data then +--- print("stderr chunk", stderr, data) +--- else +--- print("stderr end", stderr) +--- end +---end) +--- +---uv.write(stdin, "Hello World") +--- +---uv.shutdown(stdin, function() +--- print("stdin shutdown", stdin) +--- uv.close(handle, function() +--- print("process closed", handle, pid) +--- end) +---end) +---``` +--- +---The `options` table accepts the following fields: +--- +--- - `options.args` - Command line arguments as a list of strings. The first +--- string should *not* be the path to the program, since that is already +--- provided via `path`. On Windows, this uses CreateProcess which concatenates +--- the arguments into a string. This can cause some strange errors +--- (see `options.verbatim` below for Windows). +--- +--- - `options.stdio` - Set the file descriptors that will be made available to +--- the child process. The convention is that the first entries are stdin, stdout, +--- and stderr. (**Note**: On Windows, file descriptors after the third are +--- available to the child process only if the child processes uses the MSVCRT +--- runtime.) +--- +--- - `options.env` - Set environment variables for the new process. +--- +--- - `options.cwd` - Set the current working directory for the sub-process. +--- +--- - `options.uid` - Set the child process' user id. +--- +--- - `options.gid` - Set the child process' group id. +--- +--- - `options.verbatim` - If true, do not wrap any arguments in quotes, or +--- perform any other escaping, when converting the argument list into a command +--- line string. This option is only meaningful on Windows systems. On Unix it is +--- silently ignored. +--- +--- - `options.detached` - If true, spawn the child process in a detached state - +--- this will make it a process group leader, and will effectively enable the +--- child to keep running after the parent exits. Note that the child process +--- will still keep the parent's event loop alive unless the parent process calls +--- `uv.unref()` on the child's process handle. +--- +--- - `options.hide` - If true, hide the subprocess console window that would +--- normally be created. This option is only meaningful on Windows systems. On +--- Unix it is silently ignored. +---The `options.stdio` entries can take many shapes. +--- +--- - If they are numbers, then the child process inherits that same zero-indexed +--- fd from the parent process. +--- +--- - If `uv_stream_t` handles are passed in, those are used as a read-write pipe +--- or inherited stream depending if the stream has a valid fd. +--- +--- - Including `nil` placeholders means to ignore that fd in the child process. +--- +---When the child process exits, `on_exit` is called with an exit code and signal. +--- +---@param path string +---@param options uv.aliases.spawn_options +---@param on_exit fun(code: integer, signal: integer)? +---@return uv_process_t|nil, integer|string, string? +function uv.spawn(path, options, on_exit) end + +--- +---Sends the specified signal to the given process handle. +--- +---@param process uv_process_t +---@param signum? integer|uv.aliases.signals # (default: `"sigterm"`) +---@return 0|nil success, string? err_name, string? err_msg +function uv.process_kill(process, signum) end +uv_process_t.kill = uv.process_kill + +--- +---Sends the specified signal to the given PID. +--- +---@param pid integer +---@param signum? integer|uv.aliases.signals # (default: `"sigterm"`) +---@return 0|nil success, string? err_name, string? err_msg +function uv.kill(pid, signum) end + +--- +---Returns the handle's pid. +--- +---@param process uv_process_t +---@return integer +---@nodiscard +function uv.process_get_pid(process) end +uv_process_t.get_pid = uv.process_get_pid + + + +--- +---Stream handles provide an abstraction of a duplex communication channel. +---`uv_stream_t` is an abstract type, libuv provides 3 stream implementations +---in the form of `uv_tcp_t`, `uv_pipe_t` and `uv_tty_t`. +--- +---@class uv_stream_t: uv_handle_t +---@section Stream handle +local uv_stream_t = {} + +---@class uv_shutdown_t: uv_req_t + +---@class uv_write_t: uv_req_t + +--- +---Shutdown the outgoing (write) side of a duplex stream. It waits for pending +---write requests to complete. The callback is called after shutdown is complete. +--- +---@param stream uv_stream_t +---@param callback fun(err?: string)|nil +---@return uv_shutdown_t|nil stream, string? err_name, string? err_msg +function uv.shutdown(stream, callback) end +uv_stream_t.shutdown = uv.shutdown + +--- +---Start listening for incoming connections. `backlog` indicates the number of +---connections the kernel might queue, same as `listen(2)`. When a new incoming +---connection is received the callback is called. +--- +---@param stream uv_stream_t +---@param backlog integer +---@param callback fun(err?: string) +---@return 0|nil success, string? err_name, string? err_msg +function uv.listen(stream, backlog, callback) end +uv_stream_t.listen = uv.listen + +--- +---This call is used in conjunction with `uv.listen()` to accept incoming +---connections. Call this function after receiving a callback to accept the +---connection. +--- +---When the connection callback is called it is guaranteed that this function +---will complete successfully the first time. If you attempt to use it more than +---once, it may fail. It is suggested to only call this function once per +---connection call. +--- +---```lua +---server:listen(128, function (err) +--- local client = uv.new_tcp() +--- server:accept(client) +---end) +---``` +--- +---@param stream uv_stream_t +---@param client_stream uv_stream_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.accept(stream, client_stream) end +uv_stream_t.accept = uv.accept + +--- +---Read data from an incoming stream. The callback will be made several times until +---there is no more data to read or `uv.read_stop()` is called. When we've reached +---EOF, `data` will be `nil`. +--- +---```lua +---stream:read_start(function (err, chunk) +--- if err then +--- -- handle read error +--- elseif chunk then +--- -- handle data +--- else +--- -- handle disconnect +--- end +---end) +---``` +--- +---@param stream uv_stream_t +---@param callback fun(err?: string, data?: string) +---@return 0|nil success, string? err_name, string? err_msg +function uv.read_start(stream, callback) end +uv_stream_t.read_start = uv.read_start + +--- +---Stop reading data from the stream. The read callback will no longer be called. +---This function is idempotent and may be safely called on a stopped stream. +--- +---@param stream uv_stream_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.read_stop(stream) end +uv_stream_t.read_stop = uv.read_stop + +--- +---Write data to stream. +---`data` can either be a Lua string or a table of strings. If a table is passed +---in, the C backend will use writev to send all strings in a single system call. +---The optional `callback` is for knowing when the write is complete. +--- +---@param stream uv_stream_t +---@param data uv.aliases.buffer +---@param callback fun(err?: string)|nil +---@return uv_write_t|nil stream, string? err_name, string? err_msg +function uv.write(stream, data, callback) end +uv_stream_t.write = uv.write + +--- +---Extended write function for sending handles over a pipe. The pipe must be +---initialized with `ipc` option `true`. +--- +---**Note:** `send_handle` must be a TCP socket or pipe, which is a server or a +---connection (listening or connected state). Bound sockets or pipes will be +---assumed to be servers. +--- +---@param stream uv_stream_t +---@param data uv.aliases.buffer +---@param send_handle uv_tcp_t|uv_pipe_t +---@param callback fun()|nil +---@return uv_write_t|nil stream, string? err_name, string? err_msg +function uv.write2(stream, data, send_handle, callback) end +uv_stream_t.write2 = uv.write2 + +--- +---Same as `uv.write()`, but won't queue a write request if it can't be completed +---immediately. +---Will return number of bytes written (can be less than the supplied buffer size). +--- +---@param stream uv_stream_t +---@param data uv.aliases.buffer +---@return integer|nil, string? err_name, string? err_msg +function uv.try_write(stream, data) end +uv_stream_t.try_write = uv.try_write + +--- +---Like `uv.write2()`, but with the properties of `uv.try_write()`. Not supported on Windows, where it returns `UV_EAGAIN`. +---Will return number of bytes written (can be less than the supplied buffer size). +--- +---@param stream uv_stream_t +---@param data uv.aliases.buffer +---@param send_handle uv_tcp_t|uv_pipe_t +---@return integer|nil, string? err_name, string? err_msg +function uv.try_write2(stream, data, send_handle) end +uv_stream_t.try_write2 = uv.try_write2 + +--- +---Returns `true` if the stream is readable, `false` otherwise. +--- +---@param stream uv_stream_t +---@return boolean +---@nodiscard +function uv.is_readable(stream) end +uv_stream_t.is_readable = uv.is_readable + +--- +---Returns `true` if the stream is writable, `false` otherwise. +--- +---@param stream uv_stream_t +---@return boolean +---@nodiscard +function uv.is_writable(stream) end +uv_stream_t.is_writable = uv.is_writable + +--- +---Enable or disable blocking mode for a stream. +---When blocking mode is enabled all writes complete synchronously. The interface +---remains unchanged otherwise, e.g. completion or failure of the operation will +---still be reported through a callback which is made asynchronously. +--- +---**Warning**: Relying too much on this API is not recommended. It is likely to +---change significantly in the future. Currently this only works on Windows and +---only for `uv_pipe_t` handles. Also libuv currently makes no ordering guarantee +---when the blocking mode is changed after write requests have already been +---submitted. Therefore it is recommended to set the blocking mode immediately +---after opening or creating the stream. +--- +---@param stream uv_stream_t +---@param blocking boolean +---@return 0|nil success, string? err_name, string? err_msg +function uv.stream_set_blocking(stream, blocking) end +uv_stream_t.set_blocking = uv.stream_set_blocking + +--- +---Returns the stream's write queue size. +--- +---@return integer +---@nodiscard +function uv.stream_get_write_queue_size() end +uv_stream_t.get_write_queue_size = uv.stream_get_write_queue_size + + + +--- +---TCP handles are used to represent both TCP streams and servers. +--- +---@class uv_tcp_t: uv_stream_t +---@section TCP handle +local uv_tcp_t = {} + +---@class uv_connect_t: uv_req_t + +---@alias uv.aliases.network_family +---|'"unix"' +---|'"inet"' +---|'"inet6"' +---|'"ipx"' +---|'"netlink"' +---|'"x25"' +---|'"ax25"' +---|'"atmpvc"' +---|'"appletalk"' +---|'"packet"' + +---@alias uv.aliases.network_protocols +---|'"ip"' # internet protocol, pseudo protocol number +---|'"hopopt"' # hop-by-hop options for ipv6 +---|'"icmp"' # internet control message protocol +---|'"igmp"' # internet group management protocol +---|'"ggp"' # gateway-gateway protocol +---|'"ipv4"' # IPv4 encapsulation +---|'"st"' # ST datagram mode +---|'"tcp"' # transmission control protocol +---|'"cbt"' # CBT, Tony Ballardie +---|'"egp"' # exterior gateway protocol +---|'"igp"' # any private interior gateway (Cisco: for IGRP) +---|'"bbn-rcc"' # BBN RCC Monitoring +---|'"nvp"' # Network Voice Protocol +---|'"pup"' # PARC universal packet protocol +---|'"argus"' # ARGUS (deprecated) +---|'"emcon"' # EMCON +---|'"xnet"' # Cross Net Debugger +---|'"chaos"' # Chaos +---|'"udp"' # user datagram protocol +---|'"mux"' # Multiplexing protocol +---|'"dcn"' # DCN Measurement Subsystems +---|'"hmp"' # host monitoring protocol +---|'"prm"' # packet radio measurement protocol +---|'"xns-idp"' # Xerox NS IDP +---|'"trunk-1"' # Trunk-1 +---|'"trunk-2"' # Trunk-2 +---|'"leaf-1"' # Leaf-1 +---|'"leaf-2"' # Leaf-2 +---|'"rdp"' # "reliable datagram" protocol +---|'"irtp"' # Internet Reliable Transaction Protocol +---|'"iso-tp4"' # ISO Transport Protocol Class 4 +---|'"netblt"' # Bulk Data Transfer Protocol +---|'"mfe-nsp"' # MFE Network Services Protocol +---|'"merit-inp"' # MERIT Internodal Protocol +---|'"dccp"' # Datagram Congestion Control Protocol +---|'"3pc"' # Third Party Connect Protocol +---|'"idpr"' # Inter-Domain Policy Routing Protocol +---|'"xtp"' # Xpress Tranfer Protocol +---|'"ddp"' # Datagram Delivery Protocol +---|'"idpr-cmtp"' # IDPR Control Message Transport Proto +---|'"tp++"' # TP++ Transport Protocol +---|'"il"' # IL Transport Protocol +---|'"ipv6"' # IPv6 encapsulation +---|'"sdrp"' # Source Demand Routing Protocol +---|'"ipv6-route"' # Routing Header for IPv6 +---|'"ipv6-frag"' # Fragment Header for IPv6 +---|'"idrp"' # Inter-Domain Routing Protocol +---|'"rsvp"' # Resource ReSerVation Protocol +---|'"gre"' # Generic Routing Encapsulation +---|'"dsr"' # Dynamic Source Routing Protocol +---|'"bna"' # BNA +---|'"esp"' # Encap Security Payload +---|'"ipv6-crypt"' # Encryption Header for IPv6 (not in official list) +---|'"ah"' # Authentication Header +---|'"ipv6-auth"' # Authentication Header for IPv6 (not in official list) +---|'"i-nlsp"' # Integrated Net Layer Security TUBA +---|'"swipe"' # IP with Encryption +---|'"narp"' # NBMA Address Resolution Protocol +---|'"mobile"' # IP Mobility +---|'"tlsp"' # Transport Layer Security Protocol +---|'"skip"' # SKIP +---|'"ipv6-icmp"' # ICMP for IPv6 +---|'"ipv6-nonxt"' # No Next Header for IPv6 +---|'"ipv6-opts"' # Destination Options for IPv6 +---|'"#"' # any host internal protocol +---|'"cftp"' # CFTP +---|'"#"' # any local network +---|'"sat-expak"' # SATNET and Backroom EXPAK +---|'"kryptolan"' # Kryptolan +---|'"rvd"' # MIT Remote Virtual Disk Protocol +---|'"ippc"' # Internet Pluribus Packet Core +---|'"#"' # any distributed file system +---|'"sat-mon"' # SATNET Monitoring +---|'"visa"' # VISA Protocol +---|'"ipcv"' # Internet Packet Core Utility +---|'"cpnx"' # Computer Protocol Network Executive +---|'"cphb"' # Computer Protocol Heart Beat +---|'"wsn"' # Wang Span Network +---|'"pvp"' # Packet Video Protocol +---|'"br-sat-mon"' # Backroom SATNET Monitoring +---|'"sun-nd"' # SUN ND PROTOCOL-Temporary +---|'"wb-mon"' # WIDEBAND Monitoring +---|'"wb-expak"' # WIDEBAND EXPAK +---|'"iso-ip"' # ISO Internet Protocol +---|'"vmtp"' # Versatile Message Transport +---|'"secure-vmtp"' # SECURE-VMTP +---|'"vines"' # VINES +---|'"ttp"' # TTP +---|'"nsfnet-igp"' # NSFNET-IGP +---|'"dgp"' # Dissimilar Gateway Protocol +---|'"tcf"' # TCF +---|'"eigrp"' # Enhanced Interior Routing Protocol (Cisco) +---|'"ospf"' # Open Shortest Path First IGP +---|'"sprite-rpc"' # Sprite RPC Protocol +---|'"larp"' # Locus Address Resolution Protocol +---|'"mtp"' # Multicast Transport Protocol +---|'"ax.25"' # AX.25 Frames +---|'"ipip"' # Yet Another IP encapsulation +---|'"micp"' # Mobile Internetworking Control Pro. +---|'"scc-sp"' # Semaphore Communications Sec. Pro. +---|'"etherip"' # Ethernet-within-IP Encapsulation +---|'"encap"' # Yet Another IP encapsulation +---|'"#"' # any private encryption scheme +---|'"gmtp"' # GMTP +---|'"ifmp"' # Ipsilon Flow Management Protocol +---|'"pnni"' # PNNI over IP +---|'"pim"' # Protocol Independent Multicast +---|'"aris"' # ARIS +---|'"scps"' # SCPS +---|'"qnx"' # QNX +---|'"a/n"' # Active Networks +---|'"ipcomp"' # IP Payload Compression Protocol +---|'"snp"' # Sitara Networks Protocol +---|'"compaq-peer"' # Compaq Peer Protocol +---|'"ipx-in-ip"' # IPX in IP +---|'"vrrp"' # Virtual Router Redundancy Protocol +---|'"pgm"' # PGM Reliable Transport Protocol +---|'"#"' # any 0-hop protocol +---|'"l2tp"' # Layer Two Tunneling Protocol +---|'"ddx"' # D-II Data Exchange +---|'"iatp"' # Interactive Agent Transfer Protocol +---|'"stp"' # Schedule Transfer +---|'"srp"' # SpectraLink Radio Protocol +---|'"uti"' # UTI +---|'"smp"' # Simple Message Protocol +---|'"sm"' # SM (deprecated) +---|'"ptp"' # Performance Transparency Protocol +---|'"isis"' # ISIS over IPv4 +---|'"crtp"' # Combat Radio Transport Protocol +---|'"crudp"' # Combat Radio User Datagram +---|'"sps"' # Secure Packet Shield +---|'"pipe"' # Private IP Encapsulation within IP +---|'"sctp"' # Stream Control Transmission Protocol +---|'"fc"' # Fibre Channel +---|'"mobility-header"' # Mobility Header +---|'"manet"' # MANET Protocols +---|'"hip"' # Host Identity Protocol +---|'"shim6"' # Shim6 Protocol +---|'"wesp"' # Wrapped Encapsulating Security Payload +---|'"rohc"' # Robust Header Compression + +---@alias uv.aliases.tcp_socket_type +---|>'"stream"' +---|'"dgram"' +---|'"raw"' +---|'"rdm"' +---|'"seqpacket"' + +---@alias uv.aliases.tcp_bind_flags {ipv6only?: boolean} + +---@alias uv.aliases.getpeername_rtn {ip: string, family: uv.aliases.network_family, port: integer} + +---@alias uv.aliases.getsockname_rtn uv.aliases.getpeername_rtn + +---@alias uv.aliases.socketpair_flags {nonblock: boolean} + +--- +---Creates and initializes a new `uv_tcp_t`. +---Returns the Lua userdata wrapping it. +--- +---@param flags uv.aliases.network_family|integer +---@return uv_tcp_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_tcp(flags) end +---@return uv_tcp_t +---@nodiscard +function uv.new_tcp() end + +--- +---Open an existing file descriptor or SOCKET as a TCP handle. +--- +---**Note:** The passed file descriptor or SOCKET is not checked for its type, but it's required that it represents a valid stream socket. +--- +---@param tcp uv_tcp_t +---@param sock integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_open(tcp, sock) end +uv_tcp_t.open = uv.tcp_open + +--- +---Enable / disable Nagle's algorithm. +--- +---@param tcp uv_tcp_t +---@param enable boolean +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_nodelay(tcp, enable) end +uv_tcp_t.nodelay = uv.tcp_nodelay + +--- +---Enable / disable TCP keep-alive. `delay` is the initial delay in seconds, +---ignored when enable is `false`. +--- +---@param tcp uv_tcp_t +---@param enable boolean +---@param delay integer|nil +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_keepalive(tcp, enable, delay) end +uv_tcp_t.keepalive = uv.tcp_keepalive + +--- +---Enable / disable simultaneous asynchronous accept requests that are queued by +---the operating system when listening for new TCP connections. +--- +---This setting is used to tune a TCP server for the desired performance. Having +---simultaneous accepts can significantly improve the rate of accepting connections +---(which is why it is enabled by default) but may lead to uneven load distribution +---in multi-process setups. +--- +---@param tcp uv_tcp_t +---@param enable boolean +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_simultaneous_accepts(tcp, enable) end +uv_tcp_t.simultaneous_accepts = uv.tcp_simultaneous_accepts + +--- +---Bind the handle to an host and port. `host` should be an IP address and +---not a domain name. Any `flags` are set with a table with field `ipv6only` +---equal to `true` or `false`. +--- +---When the port is already taken, you can expect to see an `EADDRINUSE` error +---from either `uv.tcp_bind()`, `uv.listen()` or `uv.tcp_connect()`. That is, a +---successful call to this function does not guarantee that the call to `uv.listen()` +---or `uv.tcp_connect()` will succeed as well. +--- +---Use a port of `0` to let the OS assign an ephemeral port. You can look it up +---later using `uv.tcp_getsockname()`. +--- +---@param tcp uv_tcp_t +---@param host string +---@param port integer +---@param flags uv.aliases.tcp_bind_flags|nil +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_bind(tcp, host, port, flags) end +uv_tcp_t.bind = uv.tcp_bind + +--- +---Get the address of the peer connected to the handle. +--- +---@param tcp uv_tcp_t +---@return uv.aliases.getpeername_rtn|nil, string? err_name, string? err_msg +---@nodiscard +function uv.tcp_getpeername(tcp) end +uv_tcp_t.getpeername = uv.tcp_getpeername + +--- +---Get the current address to which the handle is bound. +--- +---@param tcp uv_tcp_t +---@return uv.aliases.getsockname_rtn|nil, string? err_name, string? err_msg +---@nodiscard +function uv.tcp_getsockname(tcp) end +uv_tcp_t.getsockname = uv.tcp_getsockname + +--- +---Establish an IPv4 or IPv6 TCP connection. +--- +---```lua +---local client = uv.new_tcp() +---client:connect("127.0.0.1", 8080, function (err) +--- -- check error and carry on. +---end) +---``` +--- +---@param tcp uv_tcp_t +---@param host string +---@param port integer +---@param callback fun(err?: string) +---@return uv_connect_t|nil stream, string? err_name, string? err_msg +function uv.tcp_connect(tcp, host, port, callback) end +uv_tcp_t.connect = uv.tcp_connect + +--- +---Please use `uv.stream_get_write_queue_size()` instead. +--- +---@param tcp uv_tcp_t +---@deprecated +function uv.tcp_write_queue_size(tcp) end +uv_tcp_t.write_queue_size = uv.tcp_write_queue_size + +--- +---Resets a TCP connection by sending a RST packet. This is accomplished by setting +---the SO_LINGER socket option with a linger interval of zero and then calling +---`uv.close()`. Due to some platform inconsistencies, mixing of `uv.shutdown()` +---and `uv.tcp_close_reset()` calls is not allowed. +--- +---@param tcp uv_tcp_t +---@param callback fun()|nil +---@return 0|nil success, string? err_name, string? err_msg +function uv.tcp_close_reset(tcp, callback) end +uv_tcp_t.close_reset = uv.tcp_close_reset + +--- +---Create a pair of connected sockets with the specified properties. The resulting handles can be passed to `uv.tcp_open`, used with `uv.spawn`, or for any other purpose. +---When specified as a string, `socktype` must be one of `"stream"`, `"dgram"`, `"raw"`, +---`"rdm"`, or `"seqpacket"`. +--- +---When `protocol` is set to 0 or nil, it will be automatically chosen based on the socket's domain and type. When `protocol` is specified as a string, it will be looked up using the `getprotobyname(3)` function (examples: `"ip"`, `"icmp"`, `"tcp"`, `"udp"`, etc). +--- +---Flags: +---- `nonblock`: Opens the specified socket handle for `OVERLAPPED` or `FIONBIO`/`O_NONBLOCK` I/O usage. This is recommended for handles that will be used by libuv, and not usually recommended otherwise. +--- +---Equivalent to `socketpair(2)` with a domain of `AF_UNIX`. +--- +---```lua +----- Simple read/write with tcp +---local fds = uv.socketpair(nil, nil, {nonblock=true}, {nonblock=true}) +--- +---local sock1 = uv.new_tcp() +---sock1:open(fds[1]) +--- +---local sock2 = uv.new_tcp() +---sock2:open(fds[2]) +--- +---sock1:write("hello") +---sock2:read_start(function(err, chunk) +--- assert(not err, err) +--- print(chunk) +---end) +---``` +--- +---@param socktype uv.aliases.tcp_socket_type|integer|nil # (default: `"stream"`) +---@param protocol uv.aliases.network_protocols|integer|nil # (default: `0`) +---@param flags1 uv.aliases.socketpair_flags|nil # (nonblock default: `false`) +---@param flags2 uv.aliases.socketpair_flags|nil # (nonblock default: `false`) +---@return {[1]: integer, [2]: integer}|nil, string? err_name, string? err_msg # [1, 2] file descriptor +function uv.socketpair(socktype, protocol, flags1, flags2) end + + + +--- +---Pipe handles provide an abstraction over local domain sockets on Unix and named pipes on Windows. +--- +---```lua +---local pipe = uv.new_pipe(false) +--- +---pipe:bind('/tmp/sock.test') +--- +---pipe:listen(128, function() +--- local client = uv.new_pipe(false) +--- pipe:accept(client) +--- client:write("hello!\n") +--- client:close() +---end) +---``` +--- +---@class uv_pipe_t: uv_stream_t +---@section Pipe handle +local uv_pipe_t = {} + +---@alias uv.aliases.pipe_chmod_flags +---|'"r"' +---|'"w"' +---|'"rw"' +---|'"wr"' + +---@alias uv.aliases.pipe_flags {nonblock: boolean} # (nonblock default: `false`) + +---@alias uv.aliases.pipe_rtn {read: integer, write: integer} + +---@alias uv.aliases.pipe_2_flags {no_truncate: boolean} + +--- +---Creates and initializes a new `uv_pipe_t`. Returns the Lua userdata wrapping +---it. The `ipc` argument is a boolean to indicate if this pipe will be used for +---handle passing between processes. +--- +---@param ipc boolean|nil +---@return uv_pipe_t +---@nodiscard +function uv.new_pipe(ipc) end + +-- TODO: make sure the above method can indeed return nil + error message. +-- confirmed to never return error. + +--- +---Open an existing file descriptor or `uv_handle_t` as a pipe. +--- +---**Note**: The file descriptor is set to non-blocking mode. +--- +---@param pipe uv_pipe_t +---@param fd integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.pipe_open(pipe, fd) end +uv_pipe_t.open = uv.pipe_open + +--- +---Bind the pipe to a file path (Unix) or a name (Windows). +--- +---**Note**: Paths on Unix get truncated to sizeof(sockaddr_un.sun_path) bytes, +---typically between 92 and 108 bytes. +--- +---@param pipe uv_pipe_t +---@param name string +---@return 0|nil success, string? err_name, string? err_msg +function uv.pipe_bind(pipe, name) end +uv_pipe_t.bind = uv.pipe_bind + +--- +---Connect to the Unix domain socket or the named pipe. +--- +---**Note**: Paths on Unix get truncated to sizeof(sockaddr_un.sun_path) bytes, +---typically between 92 and 108 bytes. +--- +---@param pipe uv_pipe_t +---@param name string +---@param callback fun(err?: string)|nil +---@return uv_connect_t|nil stream, string? err_name, string? err_msg +function uv.pipe_connect(pipe, name, callback) end +uv_pipe_t.connect = uv.pipe_connect + +--- +---Get the name of the Unix domain socket or the named pipe. +--- +---@param pipe uv_pipe_t +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.pipe_getsockname(pipe) end +uv_pipe_t.getsockname = uv.pipe_getsockname + +--- +---Get the name of the Unix domain socket or the named pipe to which the handle is +---connected. +--- +---@param pipe uv_pipe_t +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.pipe_getpeername(pipe) end +uv_pipe_t.getpeername = uv.pipe_getpeername + +--- +---Set the number of pending pipe instance handles when the pipe server is waiting +---for connections. +--- +---**Note**: This setting applies to Windows only. +--- +---@param pipe uv_pipe_t +---@param count integer +function uv.pipe_pending_instances(pipe, count) end +uv_pipe_t.pending_instances = uv.pipe_pending_instances + +--- +---Returns the pending pipe count for the named pipe. +--- +---@param pipe uv_pipe_t +---@return integer +---@nodiscard +function uv.pipe_pending_count(pipe) end +uv_pipe_t.pending_count = uv.pipe_pending_count + +--- +---Used to receive handles over IPC pipes. +--- +---First - call `uv.pipe_pending_count()`, if it's > 0 then initialize a handle of +---the given type, returned by `uv.pipe_pending_type()` and call +---`uv.accept(pipe, handle)`. +--- +---@param pipe uv_pipe_t +---@return string +---@nodiscard +function uv.pipe_pending_type(pipe) end +uv_pipe_t.pending_type = uv.pipe_pending_type + +--- +---Alters pipe permissions, allowing it to be accessed from processes run by different users. +---Makes the pipe writable or readable by all users. +---See below for accepted flags, where `r` is `READABLE` and `w` is `WRITABLE`. +---This function is blocking. +--- +---@param pipe uv_pipe_t +---@param flags uv.aliases.pipe_chmod_flags +---@return 0|nil success, string? err_name, string? err_msg +function uv.pipe_chmod(pipe, flags) end +uv_pipe_t.chmod = uv.pipe_chmod + +--- +---Create a pair of connected pipe handles. Data may be written to the `write` fd and read from the `read` fd. +---The resulting handles can be passed to `pipe_open`, used with `spawn`, or for any other purpose. +--- +---Flags: +--- - `nonblock`: Opens the specified socket handle for `OVERLAPPED` or `FIONBIO`/`O_NONBLOCK` I/O usage. +--- This is recommended for handles that will be used by libuv, and not usually recommended otherwise. +--- +---Equivalent to `pipe(2)` with the `O_CLOEXEC` flag set. +--- +---```lua +----- Simple read/write with pipe_open +---local fds = uv.pipe({nonblock=true}, {nonblock=true}) +--- +---local read_pipe = uv.new_pipe() +---read_pipe:open(fds.read) +--- +---local write_pipe = uv.new_pipe() +---write_pipe:open(fds.write) +--- +---write_pipe:write("hello") +---read_pipe:read_start(function(err, chunk) +--- assert(not err, err) +--- print(chunk) +---end) +---``` +--- +---@param read_flags uv.aliases.pipe_flags|nil # (nonblock default: `false`) +---@param write_flags uv.aliases.pipe_flags|nil # (nonblock default: `false`) +---@return uv.aliases.pipe_rtn|nil, string? err_name, string? err_msg +---@nodiscard +function uv.pipe(read_flags, write_flags) end + +--- +---Bind the pipe to a file path (Unix) or a name (Windows). +--- +---`Flags`: +--- +--- - If `type(flags)` is `number`, it must be `0` or `uv.constants.PIPE_NO_TRUNCATE`. +--- - If `type(flags)` is `table`, it must be `{}` or `{ no_trunate = true|false }`. +--- - If `type(flags)` is `nil`, it use default value `0`. +--- - Returns `EINVAL` for unsupported flags without performing the bind operation. +--- +---Supports Linux abstract namespace sockets. namelen must include the leading '\0' byte but not the trailing nul byte. +--- +---**Note**: +---1. Paths on Unix get truncated to sizeof(sockaddr_un.sun_path) bytes, +---typically between 92 and 108 bytes. +---2. New in version 1.46.0. +--- +---@param pipe uv_pipe_t +---@param name string +---@param flags integer|uv.aliases.pipe_2_flags|nil # (default: 0) +---@return 0|nil success, string? err_name, string? err_msg +function uv.pipe_bind2(pipe, name, flags) end +uv_pipe_t.bind2 = uv.pipe_bind2 + +--- +---Connect to the Unix domain socket or the named pipe. +--- +---`Flags`: +--- +--- - If `type(flags)` is `number`, it must be `0` or `uv.constants.PIPE_NO_TRUNCATE`. +--- - If `type(flags)` is `table`, it must be `{}` or `{ no_trunate = true|false }`. +--- - If `type(flags)` is `nil`, it use default value `0`. +--- - Returns `EINVAL` for unsupported flags without performing the bind operation. +--- +---Supports Linux abstract namespace sockets. namelen must include the leading nul byte but not the trailing nul byte. +--- +---**Note**: +---1. Paths on Unix get truncated to sizeof(sockaddr_un.sun_path) bytes, +---typically between 92 and 108 bytes. +---2. New in version 1.46.0. +--- +---@param pipe uv_pipe_t +---@param name string +---@param flags integer|uv.aliases.pipe_2_flags|nil # (default: 0) +---@param callback fun(err?: string)|nil +---@return uv_connect_t|nil stream, string? err_name, string? err_msg +function uv.pipe_connect2(pipe, name, flags, callback) end +uv_pipe_t.connect2 = uv.pipe_connect2 + + + +--- +---TTY handles represent a stream for the console. +--- +---```lua +----- Simple echo program +---local stdin = uv.new_tty(0, true) +---local stdout = uv.new_tty(1, false) +--- +---stdin:read_start(function (err, data) +--- assert(not err, err) +--- if data then +--- stdout:write(data) +--- else +--- stdin:close() +--- stdout:close() +--- end +---end) +---``` +--- +---@class uv_tty_t: uv_stream_t +---@section TTY handle +local uv_tty_t = {} + +---@alias uv.aliases.tty_fd +---|0 # stdin +---|1 # stdout +---|2 # stderr + +---@alias uv.aliases.tty_mode +---|0 # UV_TTY_MODE_NORMAL: Initial/normal terminal mode +---|1 # UV_TTY_MODE_RAW: Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) +---|2 # UV_TTY_MODE_IO: Binary-safe I/O mode for IPC (Unix-only) + +---@alias uv.aliases.tty_vsterm_state +---|'supported' +---|'unsupported' + +--- +---Initialize a new TTY stream with the given file descriptor. +---See below for possible file descriptors. +--- +---On Unix this function will determine the path of the fd of the terminal using +---ttyname_r(3), open it, and use it if the passed file descriptor refers to a TTY. +--- +---This lets libuv put the tty in non-blocking mode without affecting other +---processes that share the tty. +--- +---This function is not thread safe on systems that don’t support ioctl TIOCGPTN or TIOCPTYGNAME, for instance OpenBSD and Solaris. +--- +---**Note:** If reopening the TTY fails, libuv falls back to blocking writes. +--- +---@param fd uv.aliases.tty_fd|integer +---@param readable boolean +---@return uv_tty_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_tty(fd, readable) end + +--- +---Set the TTY using the specified terminal mode. +--- +---Parameter `mode` is a C enum with the values below. +--- +---@param tty uv_tty_t +---@param mode uv.aliases.tty_mode +---@return 0|nil success, string? err_name, string? err_msg +function uv.tty_set_mode(tty, mode) end +uv_tty_t.set_mode = uv.tty_set_mode + +--- +---To be called when the program exits. Resets TTY settings to default values for +---the next process to take over. +---This function is async signal-safe on Unix platforms but can fail with error +---code `EBUSY` if you call it when execution is inside `uv.tty_set_mode()`. +--- +---@return 0|nil success, string? err_name, string? err_msg +function uv.tty_reset_mode() end + +--- +---Gets the current Window width and height. +--- +---@param tty uv_tty_t +---@return integer|nil width, integer|string height_or_errname, string? err_msg +---@nodiscard +function uv.tty_get_winsize(tty) end +uv_tty_t.get_winsize = uv.tty_get_winsize + +--- +---Controls whether console virtual terminal sequences are processed by libuv or +---console. Useful in particular for enabling ConEmu support of ANSI X3.64 and +---Xterm 256 colors. Otherwise Windows10 consoles are usually detected +---automatically. State should be one of: `"supported"` or `"unsupported"`. +--- +---This function is only meaningful on Windows systems. On Unix it is silently +---ignored. +--- +---@param state uv.aliases.tty_vsterm_state +function uv.tty_set_vterm_state(state) end + +--- +---Get the current state of whether console virtual terminal sequences are handled +---by libuv or the console. The return value is `"supported"` or `"unsupported"`. +---This function is not implemented on Unix, where it returns `ENOTSUP`. +--- +---@return uv.aliases.tty_vsterm_state|nil, string? err_name, string? err_msg +---@nodiscard +function uv.tty_get_vterm_state() end + + + +--- +---UDP handles encapsulate UDP communication for both clients and servers. +--- +---@class uv_udp_t: uv_handle_t +---@section UDP handle +local uv_udp_t = {} + +---@class uv_udp_send_t: userdata + +---@alias uv.aliases.new_udp_flags {family: uv.aliases.network_family, mmsgs: integer} + +---@alias uv.aliases.udp_bind_flags {ipv6only: boolean, reuseaddr: boolean} + +---@alias uv.aliases.udp_getsockname_rtn uv.aliases.getsockname_rtn + +---@alias uv.aliases.udp_getpeername_rtn uv.aliases.getpeername_rtn + +---@alias uv.aliases.udp_membership '"join"'|'"leave"' + +---@alias uv.aliases.udp_recv_start_callback_flags {partial: boolean|nil, mmsg_chunk: boolean|nil} + +--- +---Creates and initializes a new `uv_udp_t`. Returns the Lua userdata wrapping +---it. The actual socket is created lazily. +--- +---See below for accepted `family` values. +--- +---When specified, `mmsgs` determines the number of messages able to be received +---at one time via `recvmmsg(2)` (the allocated buffer will be sized to be able +---to fit the specified number of max size dgrams). Only has an effect on +---platforms that support `recvmmsg(2)`. +--- +---**Note:** For backwards compatibility reasons, `flags` can also be a string or +---integer. When it is a string, it will be treated like the `family` key above. +---When it is an integer, it will be used directly as the `flags` parameter when +---calling `uv_udp_init_ex`. +--- +---@param flags uv.aliases.new_udp_flags|uv.aliases.network_family|integer # (mmsgs default: `1`) +---@return uv_udp_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_udp(flags) end +---@return uv_udp_t +---@nodiscard +function uv.new_udp() end + +--- +---Returns the handle's send queue size. +--- +---@return integer +---@nodiscard +function uv.udp_get_send_queue_size() end +uv_udp_t.get_send_queue_size = uv.udp_get_send_queue_size + +--- +---Returns the handle's send queue count. +--- +---@return integer +---@nodiscard +function uv.udp_get_send_queue_count() end +uv_udp_t.get_send_queue_count = uv.udp_get_send_queue_count + +--- +---Opens an existing file descriptor or Windows SOCKET as a UDP handle. +--- +---Unix only: The only requirement of the sock argument is that it follows the +---datagram contract (works in unconnected mode, supports sendmsg()/recvmsg(), +---etc). In other words, other datagram-type sockets like raw sockets or netlink +---sockets can also be passed to this function. +--- +---The file descriptor is set to non-blocking mode. +--- +---**Note:** The passed file descriptor or SOCKET is not checked for its type, but +---it's required that it represents a valid datagram socket. +--- +---@param udp uv_udp_t +---@param fd integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_open(udp, fd) end +uv_udp_t.open = uv.udp_open + +--- +---Bind the UDP handle to an IP address and port. Any `flags` are set with a table +---with fields `reuseaddr` or `ipv6only` equal to `true` or `false`. +--- +---@param udp uv_udp_t +---@param host string +---@param port integer +---@param flags uv.aliases.udp_bind_flags|nil +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_bind(udp, host, port, flags) end +uv_udp_t.bind = uv.udp_bind + +--- +---Get the local IP and port of the UDP handle. +--- +---@param udp uv_udp_t +---@return uv.aliases.udp_getsockname_rtn|nil, string? err_name, string? err_msg +---@nodiscard +function uv.udp_getsockname(udp) end +uv_udp_t.getsockname = uv.udp_getsockname + +--- +---Get the remote IP and port of the UDP handle on connected UDP handles. +--- +---@param udp uv_udp_t +---@return uv.aliases.udp_getpeername_rtn|nil, string? err_name, string? err_msg +---@nodiscard +function uv.udp_getpeername(udp) end +uv_udp_t.getpeername = uv.udp_getpeername + +--- +---Set membership for a multicast address. `multicast_addr` is multicast address to +---set membership for. `interface_addr` is interface address. `membership` can be +---the string `"leave"` or `"join"`. +--- +---@param udp uv_udp_t +---@param multicast_addr string +---@param interface_addr string|nil +---@param membership uv.aliases.udp_membership +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_membership(udp, multicast_addr, interface_addr, membership) end +uv_udp_t.set_membership = uv.udp_set_membership + +--- +---Set membership for a source-specific multicast group. `multicast_addr` is multicast +---address to set membership for. `interface_addr` is interface address. `source_addr` +---is source address. `membership` can be the string `"leave"` or `"join"`. +--- +---@param udp uv_udp_t +---@param multicast_addr string +---@param interface_addr string|nil +---@param source_addr string +---@param membership uv.aliases.udp_membership +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_source_membership(udp, multicast_addr, interface_addr, source_addr, membership) end +uv_udp_t.set_source_membership = uv.udp_set_source_membership + +--- +---Set IP multicast loop flag. Makes multicast packets loop back to local +---sockets. +--- +---@param udp uv_udp_t +---@param on boolean +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_multicast_loop(udp, on) end +uv_udp_t.set_multicast_loop = uv.udp_set_multicast_loop + +--- +---Set the multicast ttl. +---`ttl` is an integer 1 through 255. +--- +---@param udp uv_udp_t +---@param ttl integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_multicast_ttl(udp, ttl) end +uv_udp_t.set_multicast_ttl = uv.udp_set_multicast_ttl + +--- +---Set the multicast interface to send or receive data on. +--- +---@param udp uv_udp_t +---@param interface_addr string +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_multicast_interface(udp, interface_addr) end +uv_udp_t.set_multicast_interface = uv.udp_set_multicast_interface + +--- +---Set broadcast on or off. +--- +---@param udp uv_udp_t +---@param on boolean +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_broadcast(udp, on) end +uv_udp_t.set_broadcast = uv.udp_set_broadcast + +--- +---Set the time to live. +---`ttl` is an integer 1 through 255. +--- +---@param udp uv_udp_t +---@param ttl integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_set_ttl(udp, ttl) end +uv_udp_t.set_ttl = uv.udp_set_ttl + +--- +---Send data over the UDP socket. If the socket has not previously been bound +---with `uv.udp_bind()` it will be bound to `0.0.0.0` (the "all interfaces" IPv4 +---address) and a random port number. +--- +---@param udp uv_udp_t +---@param data uv.aliases.buffer +---@param host string +---@param port integer +---@param callback fun(err?: string) +---@return uv_udp_send_t|nil stream, string? err_name, string? err_msg +function uv.udp_send(udp, data, host, port, callback) end +uv_udp_t.send = uv.udp_send + +--- +---Same as `uv.udp_send()`, but won't queue a send request if it can't be +---completed immediately. +--- +---@param udp uv_udp_t +---@param data uv.aliases.buffer +---@param host string +---@param port integer +---@return integer|nil, string? err_name, string? err_msg +function uv.udp_try_send(udp, data, host, port) end +uv_udp_t.try_send = uv.udp_try_send + +--- +---Prepare for receiving data. If the socket has not previously been bound with +---`uv.udp_bind()` it is bound to `0.0.0.0` (the "all interfaces" IPv4 address) +---and a random port number. +--- +---@param udp uv_udp_t +---@param callback fun(err?: string, data?: string, add?: uv.aliases.udp_getpeername_rtn, flags: uv.aliases.udp_recv_start_callback_flags) +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_recv_start(udp, callback) end +uv_udp_t.recv_start = uv.udp_recv_start + +--- +---Stop listening for incoming datagrams. +--- +---@param udp uv_udp_t +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_recv_stop(udp) end +uv_udp_t.recv_stop = uv.udp_recv_stop + +--- +---Associate the UDP handle to a remote address and port, so every message sent by +---this handle is automatically sent to that destination. Calling this function +---with a NULL addr disconnects the handle. Trying to call `uv.udp_connect()` on an +---already connected handle will result in an `EISCONN` error. Trying to disconnect +---a handle that is not connected will return an `ENOTCONN` error. +--- +---@param udp uv_udp_t +---@param host string +---@param port integer +---@return 0|nil success, string? err_name, string? err_msg +function uv.udp_connect(udp, host, port) end +uv_udp_t.connect = uv.udp_connect + + + +--- +---FS Event handles allow the user to monitor a given path for changes, for +---example, if the file was renamed or there was a generic change in it. This +---handle uses the best backend for the job on each platform. +--- +---@class uv_fs_event_t: uv_handle_t +---@section FS Event handle +local uv_fs_event_t = {} + +---@alias uv.aliases.fs_event_start_flags {watch_entry?: boolean, stat?: boolean, recursive?: boolean} + +---@alias uv.aliases.fs_event_start_callback_events {change?: boolean, rename?: boolean} + +--- +---Creates and initializes a new `uv_fs_event_t`. +---Returns the Lua userdata wrapping it. +--- +---@return uv_fs_event_t|nil, string? err_name, string? err_msg +---@nodiscard +function uv.new_fs_event() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- can fail in case the feature is not supported, see unix/no-fsevents.c + +--- +---Start the handle with the given callback, which will watch the specified path +---for changes. +--- +---@param fs_event uv_fs_event_t +---@param path string +---@param flags uv.aliases.fs_event_start_flags # (all flags have default of `false`) +---@param callback fun(err?: string, filename: string, events: uv.aliases.fs_event_start_callback_events) +---@return 0|nil success, string? err_name, string? err_msg +function uv.fs_event_start(fs_event, path, flags, callback) end +uv_fs_event_t.start = uv.fs_event_start + +--- +---Stop the handle, the callback will no longer be called. +--- +---@return 0|nil success, string? err_name, string? err_msg +function uv.fs_event_stop() end +uv_fs_event_t.stop = uv.fs_event_stop + +--- +---Get the path being monitored by the handle. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.fs_event_getpath() end +uv_fs_event_t.getpath = uv.fs_event_getpath + + + +--- +---FS Poll handles allow the user to monitor a given path for changes. Unlike +---`uv_fs_event_t`, fs poll handles use `stat` to detect when a file has changed so +---they can work on file systems where fs event handles can't. +--- +---@class uv_fs_poll_t: uv_handle_t +---@section FS Poll handle +local uv_fs_poll_t = {} + +--- +---Creates and initializes a new `uv_fs_poll_t`. +---Returns the Lua userdata wrapping it. +--- +---@return uv_fs_poll_t +---@nodiscard +function uv.new_fs_poll() end + +-- TODO: make sure that the above method can indeed return nil + error. +-- confirmed to never return error see libuv/fs-poll#uv_fs_poll_init + +--- +---Check the file at `path` for changes every `interval` milliseconds. +--- +---**Note:** For maximum portability, use multi-second intervals. Sub-second +---intervals will not detect all changes on many file systems. +--- +---@param fs_poll uv_fs_poll_t +---@param path string +---@param interval integer +---@param callback fun(err?: string, prev: uv.aliases.fs_stat_table, curr: uv.aliases.fs_stat_table) +---@return 0|nil success, string? err_name, string? err_msg +function uv.fs_poll_start(fs_poll, path, interval, callback) end +uv_fs_poll_t.start = uv.fs_poll_start + +--- +---Stop the handle, the callback will no longer be called. +--- +---@return 0|nil success, string? err_name, string? err_msg +function uv.fs_poll_stop() end +uv_fs_poll_t.stop = uv.fs_poll_stop + +--- +---Get the path being monitored by the handle. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.fs_poll_getpath() end +uv_fs_poll_t.getpath = uv.fs_poll_getpath + + + +--- +---@section File system operations +--- +---Most file system functions can operate synchronously or asynchronously. When a synchronous version is called (by omitting a callback), the function will +---immediately return the results of the FS call. When an asynchronous version is +---called (by providing a callback), the function will immediately return a +---`uv_fs_t` and asynchronously execute its callback; if an error is encountered, the first and only argument passed to the callback will be the `err` error string; if the operation completes successfully, the first argument will be `nil` and the remaining arguments will be the results of the FS call. +--- +---Synchronous and asynchronous versions of `readFile` (with naive error handling) +---are implemented below as an example: +--- +---```lua +---local function readFileSync(path) +--- local fd = assert(uv.fs_open(path, "r", 438)) +--- local stat = assert(uv.fs_fstat(fd)) +--- local data = assert(uv.fs_read(fd, stat.size, 0)) +--- assert(uv.fs_close(fd)) +--- return data +---end +--- +---local data = readFileSync("main.lua") +---print("synchronous read", data) +---``` +--- +---```lua +---local function readFile(path, callback) +--- uv.fs_open(path, "r", 438, function(err, fd) +--- assert(not err, err) +--- uv.fs_fstat(fd, function(err, stat) +--- assert(not err, err) +--- uv.fs_read(fd, stat.size, 0, function(err, data) +--- assert(not err, err) +--- uv.fs_close(fd, function(err) +--- assert(not err, err) +--- return callback(data) +--- end) +--- end) +--- end) +--- end) +---end +--- +---readFile("main.lua", function(data) +--- print("asynchronous read", data) +---end) +---``` +--- + +---@class uv_fs_t: uv_req_t + +---@class luv_dir_t: userdata +local luv_dir_t = {} + +---@alias uv.aliases.fs_access_flags +---Open file for reading. +--- +---Fails if the file does not exist. +--- +---Mode: O_RDONLY. +---|'"r"' +---Open file for reading in synchronous mode. +---Instructs the operating system to bypass the local file system cache. +--- +---This is primarily useful for opening files on NFS mounts as it allows you to +---skip the potentially stale local cache. It has a very real impact on I/O +---performance so don't use this flag unless you need it. +--- +---Note that this doesn't turn this call into a synchronous blocking call. +--- +---Mode: O_RDONLY + O_SYNC. +---|'"rs"' +---Same as `'rs'`. +---|'"sr"' +---Open file for reading and writing. +--- +---Fails if the file does not exist. +--- +---Mode: O_RDWR. +---|'"r+"' +---Open file for reading and writing, telling the OS to open it synchronously. +--- +---See notes for `'rs'` about using this with caution. +--- +---Mode: O_RDWR + O_SYNC. +---|'"rs+"' +---Same as `'rs+'`. +---|'"sr+"' +---Open file for writing. +--- +---The file is created (if it does not exist) or truncated (if it exists). +--- +---Mode: O_TRUNC + O_CREAT + O_WRONLY. +---|'"w"' +---Open file for writing. +--- +---Fails if the file exists. +--- +---Mode: O_TRUNC + O_CREAT + O_WRONLY + O_EXCL. +---|'"wx"' +---Same as `'wx'`. +---|'"xw"' +---Open file for reading and writing. +--- +---The file is created (if it does not exist) or truncated (if it exists). +--- +---Mode: O_TRUNC + O_CREAT + O_RDWR. +---|'"w+"' +---Open file for reading and writing. +--- +---Fails if file exists. +--- +---Mode: O_TRUNC + O_CREAT + O_RDWR + O_EXCL. +---|'"wx+"' +---Same as `'wx+'`. +---|'"xw+"' +---Open file for appending. +--- +---The file is created if it does not exist. +--- +---Mode: O_APPEND + O_CREAT + O_WRONLY. +---|'"a"' +---Open file for appending. +--- +---Fails if the file exists. +--- +---Mode: O_APPEND + O_CREAT + O_WRONLY + O_EXCL. +---|'"ax"' +---Same as `'ax'`. +---|'"xa"' +---Open file for reading and appending. +--- +---The file is created if it does not exist. +--- +---Mode: O_APPEND + O_CREAT + O_RDWR. +---|'"a+"' +---Like `'a+'` but fails if `path` exists. +--- +---Mode: O_APPEND + O_CREAT + O_RDWR + O_EXCL +---|'"ax+"' +---Same as `'ax+'`. +---|'"xa+"' + +---@alias uv.aliases.fs_stat_types +---|'"file"' +---|'"directory"' +---|'"link"' +---|'"fifo"' +---|'"socket"' +---|'"char"' +---|'"block"' + +---@alias uv.aliases.fs_stat_table {gen: integer, flags: integer, atime: {nsec: integer, sec: integer}, ctime: {nsec: integer, sec: integer}, birthtime: {nsec: integer, sec: integer}, uid: integer, gid: integer, mtime: {nsec: integer, sec: integer}, size: integer, type?: uv.aliases.fs_stat_types, nlink: integer, dev: integer, mode: integer, rdev: integer, ino: integer, blksize: integer, blocks: integer} + +---@alias uv.aliases.fs_types +---|'"file"' +---|'"directory"' +---|'"link"' +---|'"fifo"' +---|'"socket"' +---|'"char"' +---|'"block"' +---|'"unknown"' + +---@alias uv.aliases.fs_access_mode +---Tests for readbility. +---|'R' +---Tests for writiblity. +---|'W' +---Tests for executibility. +---|'X' +---Tests for readbility and writiblity. +---|'RW' +---Tests for readbility and executibility. +---|'RX' +---Tests for writiblity and executibility. +---|'WX' +---Tests for writiblity and readbility and executibility. +---|'WRX' +---A bitwise OR mask. +---|integer + +---@alias uv.aliases.fs_readdir_entries {type: uv.aliases.fs_types, name: string} + +---@alias uv.aliases.fs_symlink_flags {dir: boolean, junction: boolean} + +---@alias uv.aliases.fs_copyfile_flags {excl: boolean, ficlone: boolean, ficlone_force: boolean} + +---@alias uv.aliases.fs_statfs_stats_type +---Always 0 on Windows, sun, MVS, OpenBSD, NetBSD, HAIKU, QNK. +---|0 +---ADFS_SUPER_MAGIC = 0xadf5 +---|44533 +---AFFS_SUPER_MAGIC = 0xadff +---|44543 +---AFS_SUPER_MAGIC = 0x5346414f +---|1397113167 +---ANON_INODE_FS_MAGIC = 0x09041934 | Anonymous inode FS (for pseudofiles that have no name; e.g., epoll, signalfd, bpf) +---|151263540 +---AUTOFS_SUPER_MAGIC = 0x0187 +---|391 +---BDEVFS_MAGIC = 0x62646576 +---|1650746742 +---BEFS_SUPER_MAGIC = 0x42465331 +---|1111905073 +---BFS_MAGIC = 0x1badface +---|464386766 +---BINFMTFS_MAGIC = 0x42494e4d +---|1112100429 +---BPF_FS_MAGIC = 0xcafe4a11 +---|3405662737 +---BTRFS_SUPER_MAGIC = 0x9123683e +---|2435016766 +---BTRFS_TEST_MAGIC = 0x73727279 +---|1936880249 +---CGROUP_SUPER_MAGIC = 0x27e0eb | Cgroup pseudo FS +---|2613483 +---CGROUP2_SUPER_MAGIC = 0x63677270 | Cgroup v2 pseudo FS +---|1667723888 +---CIFS_MAGIC_NUMBER = 0xff534d42 +---|4283649346 +---CODA_SUPER_MAGIC = 0x73757245 +---|1937076805 +---COH_SUPER_MAGIC = 0x012ff7b7 +---|19920823 +---CRAMFS_MAGIC = 0x28cd3d45 +---|684539205 +---DEBUGFS_MAGIC = 0x64626720 +---|1684170528 +---DEVFS_SUPER_MAGIC = 0x1373 | Linux 2.6.17 and earlier +---|4979 +---DEVPTS_SUPER_MAGIC = 0x1cd1 +---|7377 +---ECRYPTFS_SUPER_MAGIC = 0xf15f +---|61791 +---EFIVARFS_MAGIC = 0xde5e81e4 +---|3730735588 +---EFS_SUPER_MAGIC = 0x00414a53 +---|4278867 +---EXT_SUPER_MAGIC = 0x137d | Linux 2.0 and earlier +---|4989 +---EXT2_OLD_SUPER_MAGIC = 0xef51 +---|61265 +---EXT2_SUPER_MAGIC = 0xef53 +---|61267 +---EXT3_SUPER_MAGIC = 0xef53 +---|61267 +---EXT4_SUPER_MAGIC = 0xef53 +---|61267 +---F2FS_SUPER_MAGIC = 0xf2f52010 +---|4076150800 +---FUSE_SUPER_MAGIC = 0x65735546 +---|1702057286 +---FUTEXFS_SUPER_MAGIC = 0xbad1dea | Unused +---|195894762 +---HFS_SUPER_MAGIC = 0x4244 +---|16964 +---HOSTFS_SUPER_MAGIC = 0x00c0ffee +---|12648430 +---HPFS_SUPER_MAGIC = 0xf995e849 +---|4187351113 +---HUGETLBFS_MAGIC = 0x958458f6 +---|2508478710 +---ISOFS_SUPER_MAGIC = 0x9660 +---|38496 +---JFFS2_SUPER_MAGIC = 0x72b6 +---|29366 +---JFS_SUPER_MAGIC = 0x3153464a +---|827541066 +---MINIX_SUPER_MAGIC = 0x137f | original minix FS +---|4991 +---MINIX_SUPER_MAGIC2 = 0x138f | 30 char minix FS +---|5007 +---MINIX2_SUPER_MAGIC = 0x2468 | minix V2 FS +---|9320 +---MINIX2_SUPER_MAGIC2 = 0x2478 | minix V2 FS, 30 char names +---|9336 +---MINIX3_SUPER_MAGIC = 0x4d5a | minix V3 FS, 60 char names +---|19802 +---MQUEUE_MAGIC = 0x19800202 | POSIX message queue FS +---|427819522 +---MSDOS_SUPER_MAGIC = 0x4d44 +---|19780 +---MTD_INODE_FS_MAGIC = 0x11307854 +---|288389204 +---NCP_SUPER_MAGIC = 0x564c +---|22092 +---NFS_SUPER_MAGIC = 0x6969 +---|26985 +---NILFS_SUPER_MAGIC = 0x3434 +---|13364 +---NSFS_MAGIC = 0x6e736673 +---|1853056627 +---NTFS_SB_MAGIC = 0x5346544e +---|1397118030 +---OCFS2_SUPER_MAGIC = 0x7461636f +---|1952539503 +---OPENPROM_SUPER_MAGIC = 0x9fa1 +---|40865 +---OVERLAYFS_SUPER_MAGIC = 0x794c7630 +---|2035054128 +---PIPEFS_MAGIC = 0x50495045 +---|1346981957 +---PROC_SUPER_MAGIC = 0x9fa0 | /proc FS +---|40864 +---PSTOREFS_MAGIC = 0x6165676c +---|1634035564 +---QNX4_SUPER_MAGIC = 0x002f +---|47 +---QNX6_SUPER_MAGIC = 0x68191122 +---|1746473250 +---RAMFS_MAGIC = 0x858458f6 +---|2240043254 +---REISERFS_SUPER_MAGIC = 0x52654973 +---|1382369651 +---ROMFS_MAGIC = 0x7275 +---|29301 +---SECURITYFS_MAGIC = 0x73636673 +---|1935894131 +---SELINUX_MAGIC = 0xf97cff8c +---|4185718668 +---SMACK_MAGIC = 0x43415d53 +---|1128357203 +---SMB_SUPER_MAGIC = 0x517b +---|20859 +---SMB2_MAGIC_NUMBER = 0xfe534d42 +---|4266872130 +---SOCKFS_MAGIC = 0x534f434b +---|1397703499 +---SQUASHFS_MAGIC = 0x73717368 +---|1936814952 +---SYSFS_MAGIC = 0x62656572 +---|1650812274 +---SYSV2_SUPER_MAGIC = 0x012ff7b6 +---|19920822 +---SYSV4_SUPER_MAGIC = 0x012ff7b5 +---|19920821 +---TMPFS_MAGIC = 0x01021994 +---|16914836 +---TRACEFS_MAGIC = 0x74726163 +---|1953653091 +---UDF_SUPER_MAGIC = 0x15013346 +---|352400198 +---UFS_MAGIC = 0x00011954 +---|72020 +---USBDEVICE_SUPER_MAGIC = 0x9fa2 +---|40866 +---V9FS_MAGIC = 0x01021997 +---|16914839 +---VXFS_SUPER_MAGIC = 0xa501fcf5 +---|2768370933 +---XENFS_SUPER_MAGIC = 0xabba1974 +---|2881100148 +---XENIX_SUPER_MAGIC = 0x012ff7b4 +---|19920820 +---XFS_SUPER_MAGIC = 0x58465342 +---|1481003842 +---_XIAFS_SUPER_MAGIC = 0x012fd16d | Linux 2.0 and earlier +---|19911021 + +---@alias uv.aliases.fs_statfs_stats {type: uv.aliases.fs_statfs_stats_type, bzise: integer, blocks: integer, bfree: integer, bavail: integer, files: integer, ffree: integer} + +--- +---Equivalent to `close(2)`. +--- +---@param fd integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_close(fd, callback) end +---@param fd integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_close(fd) end + +--- +---Equivalent to `open(2)`. +---See below for available access `flags`. +--- +---**Note:** On Windows, libuv uses `CreateFileW` and thus the file is always +---opened in binary mode. Because of this, the `O_BINARY` and `O_TEXT` flags are +---not supported. +--- +---@param path string +---@param flags uv.aliases.fs_access_flags|integer +---@param mode integer +---@param callback fun(err: nil|string, fd: integer|nil) +---@return uv_fs_t +function uv.fs_open(path, flags, mode, callback) end +---@param path string +---@param flags uv.aliases.fs_access_flags|integer +---@param mode integer +---@return integer|nil fd, string? err_name, string? err_msg +---@nodiscard +function uv.fs_open(path, flags, mode) end + +--- +---Equivalent to `preadv(2)`. Returns any data. An empty string indicates EOF. +--- +---If `offset` is nil or omitted, it will default to `-1`, which indicates 'use and update the current file offset.' +--- +---**Note:** When `offset` is >= 0, the current file offset will not be updated by the read. +--- +---@param fd integer +---@param size integer +---@param offset integer|nil +---@param callback fun(err: nil|string, data: string|nil) +---@return uv_fs_t +function uv.fs_read(fd, size, offset, callback) end +---@param fd integer +---@param size integer +---@param callback fun(err: nil|string, data: string|nil) +---@return uv_fs_t +function uv.fs_read(fd, size, callback) end +---@param fd integer +---@param size integer +---@param offset integer|nil +---@return string|nil data, string? err_name, string? err_msg +---@nodiscard +function uv.fs_read(fd, size, offset) end + +--- +---Equivalent to `unlink(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_unlink(path, callback) end +---@param path string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_unlink(path) end + +--- +---Equivalent to `pwritev(2)`. Returns the number of bytes written. +--- +---If `offset` is nil or omitted, it will default to `-1`, which indicates 'use and update the current file offset.' +--- +---**Note:** When `offset` is >= 0, the current file offset will not be updated by the write. +--- +---@param fd integer +---@param data uv.aliases.buffer +---@param offset integer|nil +---@param callback fun(err: nil|string, bytes: integer|nil) +---@return uv_fs_t +function uv.fs_write(fd, data, offset, callback) end +---@param fd integer +---@param data uv.aliases.buffer +---@param callback fun(err: nil|string, bytes: integer|nil) +---@return uv_fs_t +function uv.fs_write(fd, data, callback) end +---@param fd integer +---@param data uv.aliases.buffer +---@param offset integer|nil +---@return integer|nil bytes, string? err_name, string? err_msg +function uv.fs_write(fd, data, offset) end + +--- +---Equivalent to `mkdir(2)`. +--- +---@param path string +---@param mode integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_mkdir(path, mode, callback) end +---@param path string +---@param mode integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_mkdir(path, mode) end + +--- +---Equivalent to `mkdtemp(3)`. +--- +---@param template string +---@param callback fun(err: nil|string, path: string|nil) +---@return uv_fs_t +function uv.fs_mkdtemp(template, callback) end +---@param template string +---@return string|nil path, string? err_name, string? err_msg +---@nodiscard +function uv.fs_mkdtemp(template) end + +--- +---Equivalent to `mkstemp(3)`. Returns a temporary file handle and filename. +--- +---@param template string +---@param callback fun(err: nil|string, fd: integer|nil, path: string|nil) +---@return uv_fs_t +function uv.fs_mkstemp(template, callback) end +---@param template string +---@return integer|nil fd, string path_or_errname, string? err_msg +---@nodiscard +function uv.fs_mkstemp(template) end + +--- +---Equivalent to `rmdir(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_rmdir(path, callback) end +---@param path string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_rmdir(path) end + +--- +---Equivalent to `scandir(3)`, with a slightly different API. Returns a handle that +---the user can pass to `uv.fs_scandir_next()`. +--- +---**Note:** This function can be used synchronously or asynchronously. The request +---userdata is always synchronously returned regardless of whether a callback is +---provided and the same userdata is passed to the callback if it is provided. +--- +---@param path string +---@param callback fun(err: nil|string, success: uv_fs_t|nil) +---@return uv_fs_t|nil, string? err_name, string? err_msg +function uv.fs_scandir(path, callback) end +---@param path string +---@return uv_fs_t|nil success, string? err_name, string? err_msg +---@nodiscard +function uv.fs_scandir(path) end + +--- +---Called on a `uv_fs_t` returned by `uv.fs_scandir()` to get the next directory +---entry data as a `name, type` pair. When there are no more entries, `nil` is +---returned. +--- +---**Note:** This function only has a synchronous version. See `uv.fs_opendir` and +---its related functions for an asynchronous version. +--- +---@param fs uv_fs_t +---@return string|nil, string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.fs_scandir_next(fs) end + +--- +---Equivalent to `stat(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, stat: uv.aliases.fs_stat_table|nil) +---@return uv_fs_t +function uv.fs_stat(path, callback) end +---@param path string +---@return uv.aliases.fs_stat_table|nil stat, string? err_name, string? err_msg +---@nodiscard +function uv.fs_stat(path) end + +--- +---Equivalent to `fstat(2)`. +--- +---@param fd integer +---@param callback fun(err: nil|string, stat: uv.aliases.fs_stat_table|nil) +---@return uv_fs_t +function uv.fs_fstat(fd, callback) end +---@param fd integer +---@return uv.aliases.fs_stat_table|nil stat, string? err_name, string? err_msg +---@nodiscard +function uv.fs_fstat(fd) end + +--- +---Equivalent to `lstat(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, stat: uv.aliases.fs_stat_table|nil) +---@return uv_fs_t +function uv.fs_lstat(path, callback) end +---@param path string +---@return uv.aliases.fs_stat_table|nil stat, string? err_name, string? err_msg +---@nodiscard +function uv.fs_lstat(path) end + +--- +---Equivalent to `rename(2)`. +--- +---@param path string +---@param new_path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_rename(path, new_path, callback) end +---@param path string +---@param new_path string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_rename(path, new_path) end + +--- +---Equivalent to `fsync(2)`. +--- +---@param fd integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_fsync(fd, callback) end +---@param fd integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_fsync(fd) end + +--- +---Equivalent to `fdatasync(2)`. +--- +---@param fd integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_fdatasync(fd, callback) end +---@param fd integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_fdatasync(fd) end + +--- +---Equivalent to `ftruncate(2)`. +--- +---@param fd integer +---@param offset integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_ftruncate(fd, offset, callback) end +---@param fd integer +---@param offset integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_ftruncate(fd, offset) end + +--- +---Limited equivalent to `sendfile(2)`. Returns the number of bytes written. +--- +---@param out_fd integer +---@param in_fd integer +---@param in_offset integer +---@param size integer +---@param callback fun(err: nil|string, bytes: integer|nil) +---@return uv_fs_t +function uv.fs_sendfile(out_fd, in_fd, in_offset, size, callback) end +---@param out_fd integer +---@param in_fd integer +---@param in_offset integer +---@param size integer +---@return integer|nil bytes, string? err_name, string? err_msg +function uv.fs_sendfile(out_fd, in_fd, in_offset, size) end + +--- +---Equivalent to `access(2)` on Unix. Windows uses `GetFileAttributesW()`. Access +---`mode` can be an integer or a string containing `"R"` or `"W"` or `"X"`. +--- +---Returns `true` or `false` indicating access permission. +--- +---@param path string +---@param mode uv.aliases.fs_access_mode|integer +---@param callback fun(err: nil|string, permission: boolean|nil) +---@return uv_fs_t +function uv.fs_access(path, mode, callback) end +---@param path string +---@param mode uv.aliases.fs_access_mode|integer +---@return boolean|nil permission, string? err_name, string? err_msg +---@nodiscard +function uv.fs_access(path, mode) end + +--- +---Equivalent to `chmod(2)`. +--- +---@param path string +---@param mode integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_chmod(path, mode, callback) end +---@param path string +---@param mode integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_chmod(path, mode) end + +--- +---Equivalent to `fchmod(2)`. +--- +---@param fd integer +---@param mode integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_fchmod(fd, mode, callback) end +---@param fd integer +---@param mode integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_fchmod(fd, mode) end + +--- +---Equivalent to `utime(2)`. +--- +---@param path string +---@param atime number +---@param mtime number +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_utime(path, atime, mtime, callback) end +---@param path string +---@param atime number +---@param mtime number +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_utime(path, atime, mtime) end + +--- +---Equivalent to `futime(2)`. +--- +---@param fd integer +---@param atime number +---@param mtime number +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_futime(fd, atime, mtime, callback) end +---@param fd integer +---@param atime number +---@param mtime number +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_futime(fd, atime, mtime) end + +--- +---Equivalent to `lutime(2)`. +--- +---@param path string +---@param atime number +---@param mtime number +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_lutime(path, atime, mtime, callback) end +---@param path string +---@param atime number +---@param mtime number +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_lutime(path, atime, mtime) end + +--- +---Equivalent to `link(2)`. +--- +---@param path string +---@param new_path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_link(path, new_path, callback) end +---@param path string +---@param new_path string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_link(path, new_path) end + +--- +---Equivalent to `symlink(2)`. +---If the `flags` parameter is omitted, then the 3rd parameter will be treated as the `callback`. +--- +---@param path string +---@param new_path string +---@param flags uv.aliases.fs_symlink_flags|integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_symlink(path, new_path, flags, callback) end +---@param path string +---@param new_path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_symlink(path, new_path, callback) end +---@param path string +---@param new_path string +---@param flags? uv.aliases.fs_symlink_flags|integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_symlink(path, new_path, flags) end + +--- +---Equivalent to `readlink(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, path: string|nil) +---@return uv_fs_t +function uv.fs_readlink(path, callback) end +---@param path string +---@return string|nil path, string? err_name, string? err_msg +---@nodiscard +function uv.fs_readlink(path) end + +--- +---Equivalent to `realpath(3)`. +--- +---@param path string +---@param callback fun(err: nil|string, path: string|nil) +---@return uv_fs_t +function uv.fs_realpath(path, callback) end +---@param path string +---@return string|nil path, string? err_name, string? err_msg +---@nodiscard +function uv.fs_realpath(path) end + +--- +---Equivalent to `chown(2)`. +--- +---@param path string +---@param uid integer +---@param gid integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_chown(path, uid, gid, callback) end +---@param path string +---@param uid integer +---@param gid integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_chown(path, uid, gid) end + +--- +---Equivalent to `fchown(2)`. +--- +---@param fd integer +---@param uid integer +---@param gid integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_fchown(fd, uid, gid, callback) end +---@param fd integer +---@param uid integer +---@param gid integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_fchown(fd, uid, gid) end + +--- +---Equivalent to `lchown(2)`. +--- +---@param fd integer +---@param uid integer +---@param gid integer +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_lchown(fd, uid, gid, callback) end +---@param fd integer +---@param uid integer +---@param gid integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_lchown(fd, uid, gid) end + +--- +---Copies a file from `path` to `new_path`. +---If the `flags` parameter is omitted, then the 3rd parameter will be treated as the `callback`. +--- +---@param path string +---@param new_path string +---@param flags uv.aliases.fs_copyfile_flags +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_copyfile(path, new_path, flags, callback) end +---@param path string +---@param new_path string +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_copyfile(path, new_path, callback) end +---@param path string +---@param new_path string +---@param flags? uv.aliases.fs_copyfile_flags +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_copyfile(path, new_path, flags) end + +--- +---Opens path as a directory stream. Returns a handle that the user can pass to +---`uv.fs_readdir()`. The `entries` parameter defines the maximum number of entries +---that should be returned by each call to `uv.fs_readdir()`. +--- +---@param path string +---@param entries integer|nil +---@param callback fun(err: nil|string, dir: luv_dir_t|nil) +---@return uv_fs_t +function uv.fs_opendir(path, entries, callback) end +---@param path string +---@param entries integer|nil +---@return luv_dir_t|nil dir, string? err_name, string? err_msg +---@nodiscard +function uv.fs_opendir(path, entries) end + +--- +---Iterates over the directory stream `luv_dir_t` returned by a successful +---`uv.fs_opendir()` call. A table of data tables is returned where the number +---of entries `n` is equal to or less than the `entries` parameter used in +---the associated `uv.fs_opendir()` call. +--- +---@param dir luv_dir_t +---@param callback fun(err: nil|string, entries: uv.aliases.fs_readdir_entries|nil) +---@return uv_fs_t +function uv.fs_readdir(dir, callback) end +---@param dir luv_dir_t +---@return uv.aliases.fs_readdir_entries|nil entries, string? err_name, string? err_msg +---@nodiscard +function uv.fs_readdir(dir) end +luv_dir_t.readdir = uv.fs_readdir + +--- +---Closes a directory stream returned by a successful `uv.fs_opendir()` call. +--- +---@param dir luv_dir_t +---@param callback fun(err: nil|string, success: boolean|nil) +---@return uv_fs_t +function uv.fs_closedir(dir, callback) end +---@param dir luv_dir_t +---@return boolean|nil success, string? err_name, string? err_msg +function uv.fs_closedir(dir) end +luv_dir_t.closedir = uv.fs_closedir + +--- +---Equivalent to `statfs(2)`. +--- +---@param path string +---@param callback fun(err: nil|string, stats: uv.aliases.fs_statfs_stats|nil) +---@return uv_fs_t +function uv.fs_statfs(path, callback) end +---@param path string +---@return uv.aliases.fs_statfs_stats|nil stats, string? err_name, string? err_msg +---@nodiscard +function uv.fs_statfs(path) end + + + +--- +---Libuv provides a threadpool which can be used to run user code and get notified +---in the loop thread. This threadpool is internally used to run all file system +---operations, as well as `getaddrinfo` and `getnameinfo` requests. +--- +---```lua +---local function work_callback(a, b) +--- return a + b +---end +--- +---local function after_work_callback(c) +--- print("The result is: " .. c) +---end +--- +---local work = uv.new_work(work_callback, after_work_callback) +--- +---work:queue(1, 2) +--- +----- output: "The result is: 3" +---``` +--- +---@class luv_work_ctx_t: userdata +---@section Thread pool work scheduling +local luv_work_ctx_t = {} + +--- +---Creates and initializes a new `luv_work_ctx_t` (not `uv_work_t`). +---`work_callback` is a Lua function or a string containing Lua code or bytecode dumped from a function. +---Returns the Lua userdata wrapping it. +--- +---@generic T: uv.aliases.threadargs +---@param work_callback fun(...: T)|string # passed to/from `uv.queue_work(work_ctx, ...)` +---@param after_work_callback fun(...: uv.aliases.threadargs) # returned from `work_callback` +---@return luv_work_ctx_t +---@nodiscard +function uv.new_work(work_callback, after_work_callback) end + +-- TODO: make sure that the above method can indeed return nil + error. +-- confirmed to never return error see luv/work.c#luv_new_work + +--- +---Queues a work request which will run `work_callback` in a new Lua state in a +---thread from the threadpool with any additional arguments from `...`. Values +---returned from `work_callback` are passed to `after_work_callback`, which is +---called in the main loop thread. +--- +---@param work_ctx luv_work_ctx_t +---@param ... uv.aliases.threadargs +---@return boolean|nil success, string? err_name, string? err_msg +function uv.queue_work(work_ctx, ...) end +luv_work_ctx_t.queue = uv.queue_work + + + +--- +---@section DNS utility functions +--- + +---@class uv_getaddrinfo_t: uv_req_t + +---@alias uv.aliases.getaddrinfo_hint {family: uv.aliases.network_family|integer|nil, socktype: uv.aliases.tcp_socket_type|nil, protocol: uv.aliases.network_protocols|nil, addrconfig: boolean|nil, v4mapped: boolean|nil, all: boolean|nil, numberichost: boolean|nil, passive: boolean|nil, numericserv: boolean|nil, canonname: boolean|nil} + +---@alias uv.aliases.getaddrinfo_rtn {addr: string, family: uv.aliases.network_family, port: integer|nil, socktype: uv.aliases.tcp_socket_type, protocol: uv.aliases.network_protocols, canonname: string|nil}[] + +---@class uv_getnameinfo_t: uv_req_t + +---@alias uv_getnameinfo_address {ip: string|nil, port: integer|nil, family: uv.aliases.network_family|integer|nil} + +--- +---Equivalent to `getaddrinfo(3)`. +---Either `host` or `service` may be `nil` but not both. +--- +---@param host string|nil +---@param service string|nil +---@param hints uv.aliases.getaddrinfo_hint? +---@param callback fun(err?: string, addresses?: uv.aliases.getaddrinfo_rtn) +---@return uv_getaddrinfo_t +function uv.getaddrinfo(host, service, hints, callback) end +---@param host string|nil +---@param service string|nil +---@param hints uv.aliases.getaddrinfo_hint? +---@return uv.aliases.getaddrinfo_rtn|nil addresses, string? err_name, string? err_msg +function uv.getaddrinfo(host, service, hints) end + +--- +---Equivalent to `getnameinfo(3)`. +--- +---@param address uv_getnameinfo_address +---@param callback fun(err?: string, host?: string, service?: string) +---@return uv_getnameinfo_t +function uv.getnameinfo(address, callback) end +---@param address uv_getnameinfo_address +---@return string|nil host, string service_or_errname, string? err_msg +function uv.getnameinfo(address) end + + + +--- +---Libuv provides cross-platform implementations for multiple threading an +--- synchronization primitives. The API largely follows the pthreads API. +--- +---@class luv_thread_t: userdata +---@section Threading and synchronization utilities +local luv_thread_t = {} + +---@alias uv.aliases.thread_affinity {[integer]: boolean} + +--- +---Creates and initializes a `luv_thread_t` (not `uv_thread_t`). Returns the Lua +---userdata wrapping it and asynchronously executes `entry`, which can be +---either a Lua function or a string containing Lua code or bytecode dumped from a function. Additional arguments `...` +---are passed to the `entry` function and an optional `options` table may be +---provided. Currently accepted `option` fields are `stack_size`. +--- +---**Note** unsafe, please make sure the thread end of life before Lua state close. +--- +---@generic T: uv.aliases.threadargs +---@param options? {stack_size?: integer} +---@param entry fun(...: T)|string +---@vararg T # varargs passed to `entry` +---@return luv_thread_t?, string? err_name, string? err_msg +function uv.new_thread(options, entry, ...) end +---@generic T: uv.aliases.threadargs +---@param entry fun(...: T)|string +---@vararg T # varargs passed to `entry` +---@return luv_thread_t?, string? err_name, string? err_msg +function uv.new_thread(entry, ...) end + +-- TODO: make sure that the above method can indeed return nil + error. +-- new_thread may fail if pthread fails. + +--- +---Returns a boolean indicating whether two threads are the same. This function is +---equivalent to the `__eq` metamethod. +--- +---@param thread luv_thread_t +---@param other_thread luv_thread_t +---@return boolean +function uv.thread_equal(thread, other_thread) end +luv_thread_t.equal = uv.thread_equal + +--- +---Sets the specified thread's affinity setting. +--- +---`affinity` must be a table where each of the keys are a CPU number and the +---values are booleans that represent whether the `thread` should be eligible to +---run on that CPU. If the length of the `affinity` table is not greater than or +---equal to `uv.cpumask_size()`, any CPU numbers missing from the table will have +---their affinity set to `false`. If setting the affinity of more than +---`uv.cpumask_size()` CPUs is desired, `affinity` must be an array-like table +---with no gaps, since `#affinity` will be used as the `cpumask_size` if it is +---greater than `uv.cpumask_size()`. +--- +---If `get_old_affinity` is `true`, the previous affinity settings for the `thread` +---will be returned. Otherwise, `true` is returned after a successful call. +--- +---**Note:** Thread affinity setting is not atomic on Windows. Unsupported on macOS. +--- +---@param thread luv_thread_t +---@param affinity uv.aliases.thread_affinity +---@param get_old_affinity? boolean +---@return boolean|uv.aliases.thread_affinity|nil, string? err_name, string? err_msg +function uv.thread_setaffinity(thread, affinity, get_old_affinity) end +luv_thread_t.setaffinity = uv.thread_setaffinity + +-- TODO: the above probably needs to use overloads for the different returns. +-- TODO: the overload with the table return needs a @nodiscard. + +--- +---Gets the specified thread's affinity setting. +--- +---If `mask_size` is provided, it must be greater than or equal to +---`uv.cpumask_size()`. If the `mask_size` parameter is omitted, then the return +---of `uv.cpumask_size()` will be used. Returns an array-like table where each of +---the keys correspond to a CPU number and the values are booleans that represent +---whether the `thread` is eligible to run on that CPU. +--- +---**Note:** Thread affinity getting is not atomic on Windows. Unsupported on macOS. +--- +---@param thread luv_thread_t +---@param mask_size? integer +---@return table?, string? err_name, string? err_msg +---@nodiscard +function uv.thread_getaffinity(thread, mask_size) end +luv_thread_t.getaffinity = uv.thread_getaffinity + +--- +---Gets the CPU number on which the calling thread is running. +--- +---**Note:** The first CPU will be returned as the number 1, not 0. This allows for +---the number to correspond with the table keys used in `uv.thread_getaffinity` and +---`uv.thread_setaffinity`. +--- +---@return integer?, string? err_name, string? err_msg +---@nodiscard +function uv.thread_getcpu() end +luv_thread_t.getcpu = uv.thread_getcpu + +--- +---Returns the handle for the thread in which this is called. +--- +---@return luv_thread_t +---@nodiscard +function uv.thread_self() end + +--- +---Waits for the `thread` to finish executing its entry function. +--- +---@param thread luv_thread_t +---@return boolean?, string? err_name, string? err_msg +function uv.thread_join(thread) end +luv_thread_t.join = uv.thread_join + +--- +---Pauses the thread in which this is called for a number of milliseconds. +--- +---@param msec integer +function uv.sleep(msec) end + + + +--- +---@section Miscellaneous utilities +---@source misc.c +--- + +---@alias uv.aliases.os_passwd {username: string, uid: integer, gid: integer, shell: string, homedir: string} + +---@alias uv.aliases.os_uname {sysname: string, release: string, version: string, machine: string} + +---@alias uv.aliases.rusage {utime: {sec: integer, usec: integer}, stime: {sec: integer, usec: integer}, maxrss: integer, ixrss: integer, idrss: integer, isrss: integer, minflt: integer, majflt: integer, nswap: integer, inblock: integer, oublock: integer, msgsnd: integer, msgrcv: integer, nsignals: integer, nvcsw: integer, nivcsw: integer} + +---@alias uv.aliases.cpu_info {[integer]: {modal: string, speed: number, times: {user: number, nice: number, sys: number, idle: number, irq: number}}} + +---@alias uv.aliases.interface_addresses {[string]: {ip: string, family: uv.aliases.network_family, netmask: string, internal: boolean, mac: string}} + +---@alias uv.aliases.clock_gettime_rtn {sec: integer, nsec: integer} + +--- +---Returns the executable path. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.exepath() end + +--- +---Returns the current working directory. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.cwd() end + +--- +---Sets the current working directory with the string `cwd`. +--- +---@param cwd string +---@return 0|nil success, string? err_name, string? err_msg +function uv.chdir(cwd) end + +--- +---Returns the title of the current process. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.get_process_title() end + +--- +---Sets the title of the current process with the string `title`. +--- +---@param title string +---@return 0|nil success, string? err_name, string? err_msg +function uv.set_process_title(title) end + +--- +---Returns the current total system memory in bytes. +--- +---@return integer +---@nodiscard +function uv.get_total_memory() end + +--- +---Returns the current free system memory in bytes. +--- +---@return integer +---@nodiscard +function uv.get_free_memory() end + +--- +---Gets the amount of memory available to the process in bytes based on limits +---imposed by the OS. If there is no such constraint, or the constraint is unknown, +---0 is returned. Note that it is not unusual for this value to be less than or +---greater than the total system memory. +--- +---@return integer +---@nodiscard +function uv.get_constrained_memory() end + +--- +---Gets the amount of free memory that is still available to the process (in +---bytes). This differs from `uv.get_free_memory()` in that it takes into account +---any limits imposed by the OS. If there is no such constraint, or the constraint +---is unknown, the amount returned will be identical to `uv.get_free_memory()`. +--- +---@return number +---@nodiscard +function uv.get_available_memory() end + +--- +---Returns the resident set size (RSS) for the current process. +--- +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.resident_set_memory() end + +--- +---Returns the resource usage. +--- +---@return uv.aliases.rusage|nil, string? err_name, string? err_msg +---@nodiscard +function uv.getrusage() end + +--- +---Returns an estimate of the default amount of parallelism a program should use. Always returns a non-zero value. +--- +---On Linux, inspects the calling thread’s CPU affinity mask to determine if it has been pinned to specific CPUs. +--- +---On Windows, the available parallelism may be underreported on systems with more than 64 logical CPUs. +--- +---On other platforms, reports the number of CPUs that the operating system considers to be online. +--- +---@return integer +---@nodiscard +function uv.available_parallelism() end + +--- +---Returns information about the CPU(s) on the system as a table of tables for each +---CPU found. +--- +---@return uv.aliases.cpu_info|nil, string? err_name, string? err_msg +---@nodiscard +function uv.cpu_info() end + +--- +---Returns the maximum size of the mask used for process/thread affinities, or +---`ENOTSUP` if affinities are not supported on the current platform. +--- +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.cpumask_size() end + +--- +---**Deprecated:** Please use `uv.os_getpid()` instead. +--- +---@return integer +---@nodiscard +---@deprecated +function uv.getpid() end + +--- +---Returns the user ID of the process. +--- +---**Note:** This is not a libuv function and is not supported on Windows. +--- +---@return integer +---@nodiscard +function uv.getuid() end + +--- +---Returns the group ID of the process. +--- +---**Note:** This is not a libuv function and is not supported on Windows. +--- +---@return integer +---@nodiscard +function uv.getgid() end + +--- +---Sets the user ID of the process with the integer `id`. +--- +---**Note:** This is not a libuv function and is not supported on Windows. +--- +---@param id integer +function uv.setuid(id) end + +--- +---Sets the group ID of the process with the integer `id`. +--- +---**Note:** This is not a libuv function and is not supported on Windows. +--- +---@param id integer +function uv.setgid(id) end + +--- +---Returns a current high-resolution time in nanoseconds as a number. This is +---relative to an arbitrary time in the past. It is not related to the time of day +---and therefore not subject to clock drift. The primary use is for measuring +---time between intervals. +--- +---@return integer +---@nodiscard +function uv.hrtime() end + +--- +---Obtain the current system time from a high-resolution real-time or monotonic +---clock source. `clock_id` can be the string `"monotonic"` or `"realtime"`. +--- +---The real-time clock counts from the UNIX epoch (1970-01-01) and is subject +---to time adjustments; it can jump back in time. +--- +---The monotonic clock counts from an arbitrary point in the past and never +---jumps back in time. +--- +---@param clock_id string +---@return uv.aliases.clock_gettime_rtn?, string? err_name, string? err_msg +---@nodiscard +function uv.clock_gettime(clock_id) end + +--- +---Returns the current system uptime in seconds. +--- +---@return number|nil, string? err_name, string? err_msg +---@nodiscard +function uv.uptime() end + +--- +---Prints all handles associated with the main loop to stderr. The format is +---`[flags] handle-type handle-address`. Flags are `R` for referenced, `A` for +---active and `I` for internal. +--- +---**Note:** This is not available on Windows. +--- +---**Warning:** This function is meant for ad hoc debugging, there are no API/ABI +---stability guarantees. +--- +function uv.print_all_handles() end + +--- +---The same as `uv.print_all_handles()` except only active handles are printed. +--- +---**Note:** This is not available on Windows. +--- +---**Warning:** This function is meant for ad hoc debugging, there are no API/ABI +---stability guarantees. +--- +function uv.print_active_handles() end + +--- +---Used to detect what type of stream should be used with a given file +---descriptor `fd`. Usually this will be used during initialization to guess the +---type of the stdio streams. +--- +---@param fd integer +---@return uv.aliases.handle_struct_name|nil +---@nodiscard +function uv.guess_handle(fd) end + +--- +---Cross-platform implementation of `gettimeofday(2)`. Returns the seconds and +---microseconds of a unix time as a pair. +--- +---@return integer|nil, integer|string, string? +---@nodiscard +function uv.gettimeofday() end + +--- +---Returns address information about the network interfaces on the system in a +---table. Each table key is the name of the interface while each associated value +---is an array of address information where fields are `ip`, `family`, `netmask`, +---`internal`, and `mac`. +--- +---@return uv.aliases.interface_addresses +---@nodiscard +function uv.interface_addresses() end + +--- +---IPv6-capable implementation of `if_indextoname(3)`. +--- +---@param ifindex integer +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.if_indextoname(ifindex) end + +--- +---Retrieves a network interface identifier suitable for use in an IPv6 scoped +---address. On Windows, returns the numeric `ifindex` as a string. On all other +---platforms, `uv.if_indextoname()` is used. +--- +---@param ifindex integer +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.if_indextoiid(ifindex) end + +--- +---Returns the load average as a triad. Not supported on Windows. +--- +---@return number, number, number +---@nodiscard +function uv.loadavg() end + +--- +---Returns system information. +--- +---@return uv.aliases.os_uname +---@nodiscard +function uv.os_uname() end + +--- +---Returns the hostname. +--- +---@return string +---@nodiscard +function uv.os_gethostname() end + +--- +---Returns the environment variable specified by `name` as string. The internal +---buffer size can be set by defining `size`. If omitted, `LUAL_BUFFERSIZE` is +---used. If the environment variable exceeds the storage available in the internal +---buffer, `ENOBUFS` is returned. If no matching environment variable exists, +---`ENOENT` is returned. +--- +---**Warning:** This function is not thread safe. +--- +---@param name string +---@param size integer # (default = `LUAL_BUFFERSIZE`) +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.os_getenv(name, size) end + +--- +---Sets the environmental variable specified by `name` with the string `value`. +--- +---**Warning:** This function is not thread safe. +--- +---@param name string +---@param value string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.os_setenv(name, value) end + +--- +---Unsets the environmental variable specified by `name`. +--- +---**Warning:** This function is not thread safe. +--- +---@param name string +---@return boolean|nil success, string? err_name, string? err_msg +function uv.os_unsetenv(name) end + +--- +---Returns all environmental variables as a dynamic table of names associated with +---their corresponding values. +--- +---**Warning:** This function is not thread safe. +--- +---@return {[string]: string} +---@nodiscard +function uv.os_environ() end + +--- +---**Warning:** This function is not thread safe. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.os_homedir() end + +--- +---**Warning:** This function is not thread safe. +--- +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.os_tmpdir() end + +--- +---Returns password file information. +--- +---@return uv.aliases.os_passwd +---@nodiscard +function uv.os_get_passwd() end + +--- +---Returns the current process ID. +--- +---@return integer +---@nodiscard +function uv.os_getpid() end + +--- +---Returns the parent process ID. +--- +---@return integer +---@nodiscard +function uv.os_getppid() end + +--- +---Returns the scheduling priority of the process specified by `pid`. +--- +---@param pid integer +---@return integer|nil, string? err_name, string? err_msg +---@nodiscard +function uv.os_getpriority(pid) end + +--- +---Sets the scheduling priority of the process specified by `pid`. The `priority` +---range is between -20 (high priority) and 19 (low priority). +--- +---@param pid integer +---@param priority integer +---@return boolean|nil success, string? err_name, string? err_msg +function uv.os_setpriority(pid, priority) end + +--- +---Fills a string of length `len` with cryptographically strong random bytes +---acquired from the system CSPRNG. `flags` is reserved for future extension +---and must currently be `nil` or `0` or `{}`. +--- +---Short reads are not possible. When less than `len` random bytes are available, +---a non-zero error value is returned or passed to the callback. If the callback +---is omitted, this function is completed synchronously. +---The synchronous version may block indefinitely when not enough entropy is +---available. The asynchronous version may not ever finish when the system is +---low on entropy. +--- +---@param len integer +---@param flags nil|0|{} +---@param callback fun(err?: string, bytes?: string) +---@return 0|nil success, string? err_name, string? err_msg +function uv.random(len, flags, callback) end +---@param len integer +---@param flags nil|0|{} +---@return string|nil, string? err_name, string? err_msg +---@nodiscard +function uv.random(len, flags) end + +--- +---Returns the libuv error message and error name (both in string form, see `err` and `name` in Error Handling) equivalent to the given platform dependent error code: POSIX error codes on Unix (the ones stored in errno), and Win32 error codes on Windows (those returned by GetLastError() or WSAGetLastError()). +--- +---@param errcode integer +---@return string|nil, string|nil +---@source util.c +---@nodiscard +function uv.translate_sys_error(errcode) end + + + +--- +---@section Metrics operations +---@source metrics.c +--- + +---@alias uv.aliases.metric_info_rtn {loop_count: integer, events: integer, events_waiting: integer} + +--- +---Retrieve the amount of time the event loop has been idle in the kernel’s event +---provider (e.g. `epoll_wait`). The call is thread safe. +--- +---The return value is the accumulated time spent idle in the kernel’s event +---provider starting from when the `uv_loop_t` was configured to collect the idle time. +--- +---**Note:** The event loop will not begin accumulating the event provider’s idle +---time until calling `loop_configure` with `"metrics_idle_time"`. +--- +---@return integer +---@nodiscard +function uv.metrics_idle_time() end + +--- +---Get the metrics table from current set of event loop metrics. It is recommended +---to retrieve these metrics in a `prepare` callback (see `uv.new_prepare`, +---`uv.prepare_start`) in order to make sure there are no inconsistencies with the +---metrics counters. +--- +---@return uv.aliases.metric_info_rtn +---@nodiscard +function uv.metrics_info() end + + +-- [[ constants ]] + +-- TODO: make this its own section +-- TODO: how should this be reflected onto docs? field descriptions? + +---@alias uv.constants {O_RDONLY: integer, O_WRONLY: integer, O_RDWR: integer, O_APPEND: integer, O_CREAT: integer, O_DSYNC: integer, O_EXCL: integer, O_NOCTTY: integer, O_NONBLOCK: integer, O_RSYNC: integer, O_SYNC: integer, O_TRUNC: integer, SOCK_STREAM: integer, SOCK_DGRAM: integer, SOCK_SEQPACKET: integer, SOCK_RAW: integer, SOCK_RDM: integer, AF_UNIX: integer, AF_INET: integer, AF_INET6: integer, AF_IPX: integer, AF_NETLINK: integer, AF_X25: integer, AF_AX25: integer, AF_ATMPVC: integer, AF_APPLETALK: integer, AF_PACKET: integer, AI_ADDRCONFIG: integer, AI_V4MAPPED: integer, AI_ALL: integer, AI_NUMERICHOST: integer, AI_PASSIVE: integer, AI_NUMERICSERV: integer, SIGHUP: integer, SIGINT: integer, SIGQUIT: integer, SIGILL: integer, SIGTRAP: integer, SIGABRT: integer, SIGIOT: integer, SIGBUS: integer, SIGFPE: integer, SIGKILL: integer, SIGUSR1: integer, SIGSEGV: integer, SIGUSR2: integer, SIGPIPE: integer, SIGALRM: integer, SIGTERM: integer, SIGCHLD: integer, SIGSTKFLT: integer, SIGCONT: integer, SIGSTOP: integer, SIGTSTP: integer, SIGTTIN: integer, SIGWINCH: integer, SIGIO: integer, SIGPOLL: integer, SIGXFSZ: integer, SIGVTALRM: integer, SIGPROF: integer, UDP_RECVMMSG: integer, UDP_MMSG_CHUNK: integer, UDP_REUSEADDR: integer, UDP_PARTIAL: integer, UDP_IPV6ONLY: integer, TCP_IPV6ONLY: integer, UDP_MMSG_FREE: integer, SIGSYS: integer, SIGPWR: integer, SIGTTOU: integer, SIGURG: integer, SIGXCPU: integer} + +---@type uv.constants +uv.constants = {} + +return uv diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/vim.fn.1.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/vim.fn.1.lua new file mode 100644 index 00000000..a95672cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/vim.fn.1.lua @@ -0,0 +1,4841 @@ +---@meta + +-- Return the minimum value of all items in {expr}. Example: +-- ```vim +-- echo min([apples, pears, oranges]) +-- +-- ``` +-- {expr} can be a |List| or a |Dictionary|. For a Dictionary, +-- it returns the minimum of all values in the Dictionary. +-- If {expr} is neither a List nor a Dictionary, or one of the +-- items in {expr} cannot be used as a Number this results in +-- an error. An empty |List| or |Dictionary| results in zero. +-- +-- Can also be used as a |method|: +-- ```vim +-- mylist->min() +-- +-- ``` +--- @return number +function vim.fn.min(expr) end + +-- Create directory {name}. +-- +-- When {flags} is present it must be a string. An empty string +-- has no effect. +-- +-- If {flags} is "p" then intermediate directories are created as +-- necessary. +-- +-- If {prot} is given it is used to set the protection bits of +-- the new directory. The default is 0o755 (rwxr-xr-x: r/w for +-- the user, readable for others). Use 0o700 to make it +-- unreadable for others. +-- +-- {prot} is applied for all parts of {name}. Thus if you create +-- /tmp/foo/bar then /tmp/foo will be created with 0o700. Example: +-- ```vim +-- :call mkdir($HOME .. "/tmp/foo/bar", "p", 0o700) +-- +-- ``` +-- This function is not available in the |sandbox|. +-- +-- If you try to create an existing directory with {flags} set to +-- "p" mkdir() will silently exit. +-- +-- The function result is a Number, which is TRUE if the call was +-- successful or FALSE if the directory creation failed or partly +-- failed. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetName()->mkdir() +-- ``` +--- @param flags? any +--- @param prot? any +--- @return number +function vim.fn.mkdir(name, flags, prot) end + +-- Return a string that indicates the current mode. +-- If [expr] is supplied and it evaluates to a non-zero Number or +-- a non-empty String (|non-zero-arg|), then the full mode is +-- returned, otherwise only the first letter is returned. +-- +-- n Normal +-- no Operator-pending +-- nov Operator-pending (forced charwise |o_v|) +-- noV Operator-pending (forced linewise |o_V|) +-- noCTRL-V Operator-pending (forced blockwise |o_CTRL-V|) +-- CTRL-V is one character +-- niI Normal using |i_CTRL-O| in |Insert-mode| +-- niR Normal using |i_CTRL-O| in |Replace-mode| +-- niV Normal using |i_CTRL-O| in |Virtual-Replace-mode| +-- nt Normal in |terminal-emulator| (insert goes to +-- Terminal mode) +-- ntT Normal using |t_CTRL-\_CTRL-O| in |Terminal-mode| +-- v Visual by character +-- vs Visual by character using |v_CTRL-O| in Select mode +-- V Visual by line +-- Vs Visual by line using |v_CTRL-O| in Select mode +-- CTRL-V Visual blockwise +-- CTRL-Vs Visual blockwise using |v_CTRL-O| in Select mode +-- s Select by character +-- S Select by line +-- CTRL-S Select blockwise +-- i Insert +-- ic Insert mode completion |compl-generic| +-- ix Insert mode |i_CTRL-X| completion +-- R Replace |R| +-- Rc Replace mode completion |compl-generic| +-- Rx Replace mode |i_CTRL-X| completion +-- Rv Virtual Replace |gR| +-- Rvc Virtual Replace mode completion |compl-generic| +-- Rvx Virtual Replace mode |i_CTRL-X| completion +-- c Command-line editing +-- cv Vim Ex mode |gQ| +-- r Hit-enter prompt +-- rm The -- more -- prompt +-- r? A |:confirm| query of some sort +-- ! Shell or external command is executing +-- t Terminal mode: keys go to the job +-- +-- This is useful in the 'statusline' option or RPC calls. In +-- most other places it always returns "c" or "n". +-- Note that in the future more modes and more specific modes may +-- be added. It's better not to compare the whole string but only +-- the leading character(s). +-- Also see |visualmode()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- DoFull()->mode() +-- ``` +--- @param expr? any +--- @return string +function vim.fn.mode(expr) end + +-- Convert a list of VimL objects to msgpack. Returned value is a +-- |readfile()|-style list. When {type} contains "B", a |Blob| is +-- returned instead. Example: +-- ```vim +-- call writefile(msgpackdump([{}]), 'fname.mpack', 'b') +-- ``` +-- or, using a |Blob|: > +-- call writefile(msgpackdump([{}], 'B'), 'fname.mpack') +-- < +-- This will write the single 0x80 byte to a `fname.mpack` file +-- (dictionary with zero items is represented by 0x80 byte in +-- messagepack). +-- +-- Limitations: +-- 1. |Funcref|s cannot be dumped. +-- 2. Containers that reference themselves cannot be dumped. +-- 3. Dictionary keys are always dumped as STR strings. +-- 4. Other strings and |Blob|s are always dumped as BIN strings. +-- 5. Points 3. and 4. do not apply to |msgpack-special-dict|s. +--- @param list any[] +--- @param type? any +--- @return any[] +function vim.fn.msgpackdump(list, type) end + +-- Convert a |readfile()|-style list or a |Blob| to a list of +-- VimL objects. +-- Example: +-- ```vim +-- let fname = expand('~/.config/nvim/shada/main.shada') +-- let mpack = readfile(fname, 'b') +-- let shada_objects = msgpackparse(mpack) +-- ``` +-- This will read ~/.config/nvim/shada/main.shada file to +-- `shada_objects` list. +-- +-- Limitations: +-- 1. Mapping ordering is not preserved unless messagepack +-- mapping is dumped using generic mapping +-- (|msgpack-special-map|). +-- 2. Since the parser aims to preserve all data untouched +-- (except for 1.) some strings are parsed to +-- |msgpack-special-dict| format which is not convenient to +-- use. +-- +-- Some messagepack strings may be parsed to special +-- dictionaries. Special dictionaries are dictionaries which +-- +-- 1. Contain exactly two keys: `_TYPE` and `_VAL`. +-- 2. `_TYPE` key is one of the types found in |v:msgpack_types| +-- variable. +-- 3. Value for `_VAL` has the following format (Key column +-- contains name of the key from |v:msgpack_types|): +-- +-- Key Value ~ +-- nil Zero, ignored when dumping. Not returned by +-- |msgpackparse()| since |v:null| was introduced. +-- boolean One or zero. When dumping it is only checked that +-- value is a |Number|. Not returned by |msgpackparse()| +-- since |v:true| and |v:false| were introduced. +-- integer |List| with four numbers: sign (-1 or 1), highest two +-- bits, number with bits from 62nd to 31st, lowest 31 +-- bits. I.e. to get actual number one will need to use +-- code like +-- ```vim +-- _VAL[0] * ((_VAL[1] << 62) +-- & (_VAL[2] << 31) +-- & _VAL[3]) +-- ``` +-- Special dictionary with this type will appear in +-- |msgpackparse()| output under one of the following +-- circumstances: +-- 1. |Number| is 32-bit and value is either above +-- INT32_MAX or below INT32_MIN. +-- 2. |Number| is 64-bit and value is above INT64_MAX. It +-- cannot possibly be below INT64_MIN because msgpack +-- C parser does not support such values. +-- float |Float|. This value cannot possibly appear in +-- |msgpackparse()| output. +-- string |readfile()|-style list of strings. This value will +-- appear in |msgpackparse()| output if string contains +-- zero byte or if string is a mapping key and mapping is +-- being represented as special dictionary for other +-- reasons. +-- binary |String|, or |Blob| if binary string contains zero +-- byte. This value cannot appear in |msgpackparse()| +-- output since blobs were introduced. +-- array |List|. This value cannot appear in |msgpackparse()| +-- output. +-- +-- map |List| of |List|s with two items (key and value) each. +-- This value will appear in |msgpackparse()| output if +-- parsed mapping contains one of the following keys: +-- 1. Any key that is not a string (including keys which +-- are binary strings). +-- 2. String with NUL byte inside. +-- 3. Duplicate key. +-- 4. Empty key. +-- ext |List| with two values: first is a signed integer +-- representing extension type. Second is +-- |readfile()|-style list of strings. +--- @return any[] +function vim.fn.msgpackparse(data) end + +-- Return the line number of the first line at or below {lnum} +-- that is not blank. Example: +-- ```vim +-- if getline(nextnonblank(1)) =~ "Java" +-- ``` +-- When {lnum} is invalid or there is no non-blank line at or +-- below it, zero is returned. +-- {lnum} is used like with |getline()|. +-- See also |prevnonblank()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetLnum()->nextnonblank() +-- ``` +--- @param lnum number +--- @return number +function vim.fn.nextnonblank(lnum) end + +-- Return a string with a single character, which has the number +-- value {expr}. Examples: +-- ```vim +-- nr2char(64) returns "@" +-- nr2char(32) returns " " +-- ``` +-- Example for "utf-8": > +-- nr2char(300) returns I with bow character +-- < UTF-8 encoding is always used, {utf8} option has no effect, +-- and exists only for backwards-compatibility. +-- Note that a NUL character in the file is specified with +-- nr2char(10), because NULs are represented with newline +-- characters. nr2char(0) is a real NUL and terminates the +-- string, thus results in an empty string. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetNumber()->nr2char() +-- ``` +--- @param utf8? any +--- @return string +function vim.fn.nr2char(expr, utf8) end + +-- Bitwise OR on the two arguments. The arguments are converted +-- to a number. A List, Dict or Float argument causes an error. +-- Also see `and()` and `xor()`. +-- Example: +-- ```vim +-- :let bits = or(bits, 0x80) +-- ``` +-- Can also be used as a |method|: > +-- :let bits = bits->or(0x80) +-- +-- < Rationale: The reason this is a function and not using the "|" +-- character like many languages, is that Vi has always used "|" +-- to separate commands. In many places it would not be clear if +-- "|" is an operator or a command separator. +--- @return number +vim.fn["or"] = function(expr, expr1) end + +-- Shorten directory names in the path {path} and return the +-- result. The tail, the file name, is kept as-is. The other +-- components in the path are reduced to {len} letters in length. +-- If {len} is omitted or smaller than 1 then 1 is used (single +-- letters). Leading '~' and '.' characters are kept. Examples: +-- ```vim +-- :echo pathshorten('~/.config/nvim/autoload/file1.vim') +-- ``` +-- ~/.c/n/a/file1.vim ~ +-- ```vim +-- :echo pathshorten('~/.config/nvim/autoload/file2.vim', 2) +-- ``` +-- ~/.co/nv/au/file2.vim ~ +-- It doesn't matter if the path exists or not. +-- Returns an empty string on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetDirectories()->pathshorten() +-- ``` +--- @param len? any +--- @return string +function vim.fn.pathshorten(path, len) end + +-- Evaluate |perl| expression {expr} and return its result +-- converted to Vim data structures. +-- Numbers and strings are returned as they are (strings are +-- copied though). +-- Lists are represented as Vim |List| type. +-- Dictionaries are represented as Vim |Dictionary| type, +-- non-string keys result in error. +-- +-- Note: If you want an array or hash, {expr} must return a +-- reference to it. +-- Example: +-- ```vim +-- :echo perleval('[1 .. 4]') +-- ``` +-- [1, 2, 3, 4] +-- +-- Can also be used as a |method|: +-- ```vim +-- GetExpr()->perleval() +-- ``` +function vim.fn.perleval(expr) end + +-- Return the power of {x} to the exponent {y} as a |Float|. +-- {x} and {y} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {x} or {y} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo pow(3, 3) +-- ``` +-- 27.0 > +-- :echo pow(2, 16) +-- < 65536.0 +-- ```vim +-- :echo pow(32, 0.20) +-- ``` +-- 2.0 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->pow(3) +-- ``` +--- @return float +function vim.fn.pow(x, y) end + +-- Return the line number of the first line at or above {lnum} +-- that is not blank. Example: +-- ```vim +-- let ind = indent(prevnonblank(v:lnum - 1)) +-- ``` +-- When {lnum} is invalid or there is no non-blank line at or +-- above it, zero is returned. +-- {lnum} is used like with |getline()|. +-- Also see |nextnonblank()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetLnum()->prevnonblank() +-- ``` +--- @param lnum number +--- @return number +function vim.fn.prevnonblank(lnum) end + +-- Return a String with {fmt}, where "%" items are replaced by +-- the formatted form of their respective arguments. Example: +-- ```vim +-- printf("%4d: E%d %.30s", lnum, errno, msg) +-- ``` +-- May result in: +-- " 99: E42 asdfasdfasdfasdfasdfasdfasdfas" ~ +-- +-- When used as a |method| the base is passed as the second +-- argument: +-- ```vim +-- Compute()->printf("result: %d") +-- ``` +-- You can use `call()` to pass the items as a list. +-- +-- Often used items are: +-- %s string +-- %6S string right-aligned in 6 display cells +-- %6s string right-aligned in 6 bytes +-- %.9s string truncated to 9 bytes +-- %c single byte +-- %d decimal number +-- %5d decimal number padded with spaces to 5 characters +-- %b binary number +-- %08b binary number padded with zeros to at least 8 characters +-- %B binary number using upper case letters +-- %x hex number +-- %04x hex number padded with zeros to at least 4 characters +-- %X hex number using upper case letters +-- %o octal number +-- %f floating point number as 12.23, inf, -inf or nan +-- %F floating point number as 12.23, INF, -INF or NAN +-- %e floating point number as 1.23e3, inf, -inf or nan +-- %E floating point number as 1.23E3, INF, -INF or NAN +-- %g floating point number, as %f or %e depending on value +-- %G floating point number, as %F or %E depending on value +-- %% the % character itself +-- %p representation of the pointer to the container +-- +-- Conversion specifications start with '%' and end with the +-- conversion type. All other characters are copied unchanged to +-- the result. +-- +-- The "%" starts a conversion specification. The following +-- arguments appear in sequence: +-- +-- % [flags] [field-width] [.precision] type +-- +-- flags +-- Zero or more of the following flags: +-- +-- # The value should be converted to an "alternate +-- form". For c, d, and s conversions, this option +-- has no effect. For o conversions, the precision +-- of the number is increased to force the first +-- character of the output string to a zero (except +-- if a zero value is printed with an explicit +-- precision of zero). +-- For x and X conversions, a non-zero result has +-- the string "0x" (or "0X" for X conversions) +-- prepended to it. +-- +-- 0 (zero) Zero padding. For all conversions the converted +-- value is padded on the left with zeros rather +-- than blanks. If a precision is given with a +-- numeric conversion (d, o, x, and X), the 0 flag +-- is ignored. +-- +-- - A negative field width flag; the converted value +-- is to be left adjusted on the field boundary. +-- The converted value is padded on the right with +-- blanks, rather than on the left with blanks or +-- zeros. A - overrides a 0 if both are given. +-- +-- ' ' (space) A blank should be left before a positive +-- number produced by a signed conversion (d). +-- +-- + A sign must always be placed before a number +-- produced by a signed conversion. A + overrides +-- a space if both are used. +-- +-- field-width +-- An optional decimal digit string specifying a minimum +-- field width. If the converted value has fewer bytes +-- than the field width, it will be padded with spaces on +-- the left (or right, if the left-adjustment flag has +-- been given) to fill out the field width. For the S +-- conversion the count is in cells. +-- +-- .precision +-- An optional precision, in the form of a period '.' +-- followed by an optional digit string. If the digit +-- string is omitted, the precision is taken as zero. +-- This gives the minimum number of digits to appear for +-- d, o, x, and X conversions, the maximum number of +-- bytes to be printed from a string for s conversions, +-- or the maximum number of cells to be printed from a +-- string for S conversions. +-- For floating point it is the number of digits after +-- the decimal point. +-- +-- type +-- A character that specifies the type of conversion to +-- be applied, see below. +-- +-- A field width or precision, or both, may be indicated by an +-- asterisk '*' instead of a digit string. In this case, a +-- Number argument supplies the field width or precision. A +-- negative field width is treated as a left adjustment flag +-- followed by a positive field width; a negative precision is +-- treated as though it were missing. Example: +-- ```vim +-- :echo printf("%d: %.*s", nr, width, line) +-- ``` +-- This limits the length of the text used from "line" to +-- "width" bytes. +-- +-- The conversion specifiers and their meanings are: +-- +-- +-- dbBoxX The Number argument is converted to signed decimal (d), +-- unsigned binary (b and B), unsigned octal (o), or +-- unsigned hexadecimal (x and X) notation. The letters +-- "abcdef" are used for x conversions; the letters +-- "ABCDEF" are used for X conversions. The precision, if +-- any, gives the minimum number of digits that must +-- appear; if the converted value requires fewer digits, it +-- is padded on the left with zeros. In no case does a +-- non-existent or small field width cause truncation of a +-- numeric field; if the result of a conversion is wider +-- than the field width, the field is expanded to contain +-- the conversion result. +-- The 'h' modifier indicates the argument is 16 bits. +-- The 'l' modifier indicates the argument is 32 bits. +-- The 'L' modifier indicates the argument is 64 bits. +-- Generally, these modifiers are not useful. They are +-- ignored when type is known from the argument. +-- +-- i alias for d +-- D alias for ld +-- U alias for lu +-- O alias for lo +-- +-- +-- c The Number argument is converted to a byte, and the +-- resulting character is written. +-- +-- +-- s The text of the String argument is used. If a +-- precision is specified, no more bytes than the number +-- specified are used. +-- If the argument is not a String type, it is +-- automatically converted to text with the same format +-- as ":echo". +-- +-- S The text of the String argument is used. If a +-- precision is specified, no more display cells than the +-- number specified are used. +-- +-- +-- f F The Float argument is converted into a string of the +-- form 123.456. The precision specifies the number of +-- digits after the decimal point. When the precision is +-- zero the decimal point is omitted. When the precision +-- is not specified 6 is used. A really big number +-- (out of range or dividing by zero) results in "inf" +-- or "-inf" with %f (INF or -INF with %F). +-- "0.0 / 0.0" results in "nan" with %f (NAN with %F). +-- Example: +-- ```vim +-- echo printf("%.2f", 12.115) +-- ``` +-- 12.12 +-- Note that roundoff depends on the system libraries. +-- Use |round()| when in doubt. +-- +-- +-- e E The Float argument is converted into a string of the +-- form 1.234e+03 or 1.234E+03 when using 'E'. The +-- precision specifies the number of digits after the +-- decimal point, like with 'f'. +-- +-- +-- g G The Float argument is converted like with 'f' if the +-- value is between 0.001 (inclusive) and 10000000.0 +-- (exclusive). Otherwise 'e' is used for 'g' and 'E' +-- for 'G'. When no precision is specified superfluous +-- zeroes and '+' signs are removed, except for the zero +-- immediately after the decimal point. Thus 10000000.0 +-- results in 1.0e7. +-- +-- +-- % A '%' is written. No argument is converted. The +-- complete conversion specification is "%%". +-- +-- When a Number argument is expected a String argument is also +-- accepted and automatically converted. +-- When a Float or String argument is expected a Number argument +-- is also accepted and automatically converted. +-- Any other argument type results in an error message. +-- +-- +-- The number of {exprN} arguments must exactly match the number +-- of "%" items. If there are not sufficient or too many +-- arguments an error is given. Up to 18 arguments can be used. +--- @return string +function vim.fn.printf(fmt, expr1, ...) end + +-- Returns the effective prompt text for buffer {buf}. {buf} can +-- be a buffer name or number. See |prompt-buffer|. +-- +-- If the buffer doesn't exist or isn't a prompt buffer, an empty +-- string is returned. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetBuffer()->prompt_getprompt() +-- ``` +--- @param buf buffer +--- @return string +function vim.fn.prompt_getprompt(buf) end + +-- Set prompt callback for buffer {buf} to {expr}. When {expr} +-- is an empty string the callback is removed. This has only +-- effect if {buf} has 'buftype' set to "prompt". +-- +-- The callback is invoked when pressing Enter. The current +-- buffer will always be the prompt buffer. A new line for a +-- prompt is added before invoking the callback, thus the prompt +-- for which the callback was invoked will be in the last but one +-- line. +-- If the callback wants to add text to the buffer, it must +-- insert it above the last line, since that is where the current +-- prompt is. This can also be done asynchronously. +-- The callback is invoked with one argument, which is the text +-- that was entered at the prompt. This can be an empty string +-- if the user only typed Enter. +-- Example: +-- ```vim +-- call prompt_setcallback(bufnr(''), function('s:TextEntered')) +-- func s:TextEntered(text) +-- if a:text == 'exit' || a:text == 'quit' +-- stopinsert +-- close +-- else +-- call append(line('$') - 1, 'Entered: "' .. a:text .. '"') +-- " Reset 'modified' to allow the buffer to be closed. +-- set nomodified +-- endif +-- endfunc +-- +-- ``` +-- Can also be used as a |method|: > +-- GetBuffer()->prompt_setcallback(callback) +--- @param buf buffer +function vim.fn.prompt_setcallback(buf, expr) end + +-- Set a callback for buffer {buf} to {expr}. When {expr} is an +-- empty string the callback is removed. This has only effect if +-- {buf} has 'buftype' set to "prompt". +-- +-- This callback will be invoked when pressing CTRL-C in Insert +-- mode. Without setting a callback Vim will exit Insert mode, +-- as in any buffer. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetBuffer()->prompt_setinterrupt(callback) +-- ``` +--- @param buf buffer +function vim.fn.prompt_setinterrupt(buf, expr) end + +-- Set prompt for buffer {buf} to {text}. You most likely want +-- {text} to end in a space. +-- The result is only visible if {buf} has 'buftype' set to +-- "prompt". Example: +-- ```vim +-- call prompt_setprompt(bufnr(''), 'command: ') +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- GetBuffer()->prompt_setprompt('command: ') +-- ``` +--- @param buf buffer +--- @param text string +function vim.fn.prompt_setprompt(buf, text) end + +-- If the popup menu (see |ins-completion-menu|) is not visible, +-- returns an empty |Dictionary|, otherwise, returns a +-- |Dictionary| with the following keys: +-- height nr of items visible +-- width screen cells +-- row top screen row (0 first row) +-- col leftmost screen column (0 first col) +-- size total nr of items +-- scrollbar |TRUE| if scrollbar is visible +-- +-- The values are the same as in |v:event| during |CompleteChanged|. +--- @return table +function vim.fn.pum_getpos() end + +-- Returns non-zero when the popup menu is visible, zero +-- otherwise. See |ins-completion-menu|. +-- This can be used to avoid some things that would remove the +-- popup menu. +--- @return number +function vim.fn.pumvisible() end + +-- Evaluate Python expression {expr} and return its result +-- converted to Vim data structures. +-- Numbers and strings are returned as they are (strings are +-- copied though, Unicode strings are additionally converted to +-- UTF-8). +-- Lists are represented as Vim |List| type. +-- Dictionaries are represented as Vim |Dictionary| type with +-- keys converted to strings. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetExpr()->py3eval() +-- ``` +function vim.fn.py3eval(expr) end + +-- Evaluate Python expression {expr} and return its result +-- converted to Vim data structures. +-- Numbers and strings are returned as they are (strings are +-- copied though). +-- Lists are represented as Vim |List| type. +-- Dictionaries are represented as Vim |Dictionary| type, +-- non-string keys result in error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetExpr()->pyeval() +-- ``` +function vim.fn.pyeval(expr) end + +-- Evaluate Python expression {expr} and return its result +-- converted to Vim data structures. +-- Uses Python 2 or 3, see |python_x| and 'pyxversion'. +-- See also: |pyeval()|, |py3eval()| +-- +-- Can also be used as a |method|: +-- ```vim +-- GetExpr()->pyxeval() +-- ``` +function vim.fn.pyxeval(expr) end + +-- Return a pseudo-random Number generated with an xoshiro128 +-- algorithm using seed {expr}. The returned number is 32 bits, +-- also on 64 bits systems, for consistency. +-- {expr} can be initialized by |srand()| and will be updated by +-- rand(). If {expr} is omitted, an internal seed value is used +-- and updated. +-- Returns -1 if {expr} is invalid. +-- +-- Examples: +-- ```vim +-- :echo rand() +-- :let seed = srand() +-- :echo rand(seed) +-- :echo rand(seed) % 16 " random number 0 - 15 +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- seed->rand() +-- ``` +--- @param expr? any +--- @return number +function vim.fn.rand(expr) end + +-- Returns a |List| with Numbers: +-- - If only {expr} is specified: [0, 1, ..., {expr} - 1] +-- - If {max} is specified: [{expr}, {expr} + 1, ..., {max}] +-- - If {stride} is specified: [{expr}, {expr} + {stride}, ..., +-- {max}] (increasing {expr} with {stride} each time, not +-- producing a value past {max}). +-- When the maximum is one before the start the result is an +-- empty list. When the maximum is more than one before the +-- start this is an error. +-- Examples: +-- ```vim +-- range(4) " [0, 1, 2, 3] +-- range(2, 4) " [2, 3, 4] +-- range(2, 9, 3) " [2, 5, 8] +-- range(2, -2, -1) " [2, 1, 0, -1, -2] +-- range(0) " [] +-- range(2, 0) " error! +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- GetExpr()->range() +-- ``` +--- @param max? any +--- @param stride? any +--- @return any[] +function vim.fn.range(expr, max, stride) end + +-- Read file {fname} in binary mode and return a |Blob|. +-- If {offset} is specified, read the file from the specified +-- offset. If it is a negative value, it is used as an offset +-- from the end of the file. E.g., to read the last 12 bytes: +-- ```vim +-- readblob('file.bin', -12) +-- ``` +-- If {size} is specified, only the specified size will be read. +-- E.g. to read the first 100 bytes of a file: +-- ```vim +-- readblob('file.bin', 0, 100) +-- ``` +-- If {size} is -1 or omitted, the whole data starting from +-- {offset} will be read. +-- This can be also used to read the data from a character device +-- on Unix when {size} is explicitly set. Only if the device +-- supports seeking {offset} can be used. Otherwise it should be +-- zero. E.g. to read 10 bytes from a serial console: +-- ```vim +-- readblob('/dev/ttyS0', 0, 10) +-- ``` +-- When the file can't be opened an error message is given and +-- the result is an empty |Blob|. +-- When the offset is beyond the end of the file the result is an +-- empty blob. +-- When trying to read more bytes than are available the result +-- is truncated. +-- Also see |readfile()| and |writefile()|. +--- @param offset? any +--- @param size? any +function vim.fn.readblob(fname, offset, size) end + +-- Return a list with file and directory names in {directory}. +-- You can also use |glob()| if you don't need to do complicated +-- things, such as limiting the number of matches. +-- +-- When {expr} is omitted all entries are included. +-- When {expr} is given, it is evaluated to check what to do: +-- If {expr} results in -1 then no further entries will +-- be handled. +-- If {expr} results in 0 then this entry will not be +-- added to the list. +-- If {expr} results in 1 then this entry will be added +-- to the list. +-- Each time {expr} is evaluated |v:val| is set to the entry name. +-- When {expr} is a function the name is passed as the argument. +-- For example, to get a list of files ending in ".txt": +-- ```vim +-- readdir(dirname, {n -> n =~ '.txt$'}) +-- ``` +-- To skip hidden and backup files: > +-- readdir(dirname, {n -> n !~ '^\.\|\~$'}) +-- +-- < If you want to get a directory tree: +-- ```vim +-- function! s:tree(dir) +-- return {a:dir : map(readdir(a:dir), +-- \ {_, x -> isdirectory(x) ? +-- \ {x : s:tree(a:dir .. '/' .. x)} : x})} +-- endfunction +-- echo s:tree(".") +-- ``` +-- Returns an empty List on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetDirName()->readdir() +-- ``` +--- @param expr? any +--- @return any[] +function vim.fn.readdir(directory, expr) end + +-- Read file {fname} and return a |List|, each line of the file +-- as an item. Lines are broken at NL characters. Macintosh +-- files separated with CR will result in a single long line +-- (unless a NL appears somewhere). +-- All NUL characters are replaced with a NL character. +-- When {type} contains "b" binary mode is used: +-- - When the last line ends in a NL an extra empty list item is +-- added. +-- - No CR characters are removed. +-- Otherwise: +-- - CR characters that appear before a NL are removed. +-- - Whether the last line ends in a NL or not does not matter. +-- - Any UTF-8 byte order mark is removed from the text. +-- When {max} is given this specifies the maximum number of lines +-- to be read. Useful if you only want to check the first ten +-- lines of a file: +-- ```vim +-- :for line in readfile(fname, '', 10) +-- : if line =~ 'Date' | echo line | endif +-- :endfor +-- ``` +-- When {max} is negative -{max} lines from the end of the file +-- are returned, or as many as there are. +-- When {max} is zero the result is an empty list. +-- Note that without {max} the whole file is read into memory. +-- Also note that there is no recognition of encoding. Read a +-- file into a buffer if you need to. +-- Deprecated (use |readblob()| instead): When {type} contains +-- "B" a |Blob| is returned with the binary data of the file +-- unmodified. +-- When the file can't be opened an error message is given and +-- the result is an empty list. +-- Also see |writefile()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetFileName()->readfile() +-- ``` +--- @param type? any +--- @param max? any +--- @return any[] +function vim.fn.readfile(fname, type, max) end + +-- {func} is called for every item in {object}, which can be a +-- |List| or a |Blob|. {func} is called with two arguments: the +-- result so far and current item. After processing all items +-- the result is returned. +-- +-- {initial} is the initial result. When omitted, the first item +-- in {object} is used and {func} is first called for the second +-- item. If {initial} is not given and {object} is empty no +-- result can be computed, an E998 error is given. +-- +-- Examples: +-- ```vim +-- echo reduce([1, 3, 5], { acc, val -> acc + val }) +-- echo reduce(['x', 'y'], { acc, val -> acc .. val }, 'a') +-- echo reduce(0z1122, { acc, val -> 2 * acc + val }) +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- echo mylist->reduce({ acc, val -> acc + val }, 0) +-- ``` +--- @param func fun() +--- @param initial? any +function vim.fn.reduce(object, func, initial) end + +-- Returns the single letter name of the register being executed. +-- Returns an empty string when no register is being executed. +-- See |@|. +--- @return string +function vim.fn.reg_executing() end + +-- Returns the single letter name of the last recorded register. +-- Returns an empty string when nothing was recorded yet. +-- See |q| and |Q|. +--- @return string +function vim.fn.reg_recorded() end + +-- Returns the single letter name of the register being recorded. +-- Returns an empty string when not recording. See |q|. +--- @return string +function vim.fn.reg_recording() end + +-- Return an item that represents a time value. The item is a +-- list with items that depend on the system. +-- The item can be passed to |reltimestr()| to convert it to a +-- string or |reltimefloat()| to convert to a Float. +-- +-- Without an argument it returns the current "relative time", an +-- implementation-defined value meaningful only when used as an +-- argument to |reltime()|, |reltimestr()| and |reltimefloat()|. +-- +-- With one argument it returns the time passed since the time +-- specified in the argument. +-- With two arguments it returns the time passed between {start} +-- and {end}. +-- +-- The {start} and {end} arguments must be values returned by +-- reltime(). Returns zero on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetStart()->reltime() +-- ``` +-- Note: |localtime()| returns the current (non-relative) time. +--- @param start number +--- @param end_ number +--- @return any[] +function vim.fn.reltime(start, end_) end + +-- Return a Float that represents the time value of {time}. +-- Unit of time is seconds. +-- Example: +-- let start = reltime() +-- call MyFunction() +-- let seconds = reltimefloat(reltime(start)) +-- See the note of reltimestr() about overhead. +-- Also see |profiling|. +-- If there is an error an empty string is returned +-- +-- Can also be used as a |method|: +-- ```vim +-- reltime(start)->reltimefloat() +-- ``` +--- @return float +function vim.fn.reltimefloat(time) end + +-- Return a String that represents the time value of {time}. +-- This is the number of seconds, a dot and the number of +-- microseconds. Example: +-- ```vim +-- let start = reltime() +-- call MyFunction() +-- echo reltimestr(reltime(start)) +-- ``` +-- Note that overhead for the commands will be added to the time. +-- Leading spaces are used to make the string align nicely. You +-- can use split() to remove it. +-- ```vim +-- echo split(reltimestr(reltime(start)))[0] +-- ``` +-- Also see |profiling|. +-- If there is an error an empty string is returned +-- +-- Can also be used as a |method|: +-- ```vim +-- reltime(start)->reltimestr() +-- ``` +--- @return string +function vim.fn.reltimestr(time) end + +-- Remove the entry from {dict} with key {key} and return it. +-- Example: +-- ```vim +-- :echo "removed " .. remove(dict, "one") +-- ``` +-- If there is no {key} in {dict} this is an error. +-- Returns zero on error. +--- @param dict table +function vim.fn.remove(dict, key) end + +-- Rename the file by the name {from} to the name {to}. This +-- should also work to move files across file systems. The +-- result is a Number, which is 0 if the file was renamed +-- successfully, and non-zero when the renaming failed. +-- NOTE: If {to} exists it is overwritten without warning. +-- This function is not available in the |sandbox|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetOldName()->rename(newname) +-- ``` +--- @param from number +--- @param to number +--- @return number +function vim.fn.rename(from, to) end + +-- Repeat {expr} {count} times and return the concatenated +-- result. Example: +-- ```vim +-- :let separator = repeat('-', 80) +-- ``` +-- When {count} is zero or negative the result is empty. +-- When {expr} is a |List| or a |Blob| the result is {expr} +-- concatenated {count} times. Example: +-- ```vim +-- :let longlist = repeat(['a', 'b'], 3) +-- ``` +-- Results in ['a', 'b', 'a', 'b', 'a', 'b']. +-- +-- Can also be used as a |method|: +-- ```vim +-- mylist->repeat(count) +-- ``` +--- @return any[] +vim.fn["repeat"] = function(expr, count) end + +-- On MS-Windows, when {filename} is a shortcut (a .lnk file), +-- returns the path the shortcut points to in a simplified form. +-- On Unix, repeat resolving symbolic links in all path +-- components of {filename} and return the simplified result. +-- To cope with link cycles, resolving of symbolic links is +-- stopped after 100 iterations. +-- On other systems, return the simplified {filename}. +-- The simplification step is done as by |simplify()|. +-- resolve() keeps a leading path component specifying the +-- current directory (provided the result is still a relative +-- path name) and also keeps a trailing path separator. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetName()->resolve() +-- ``` +--- @return string +function vim.fn.resolve(filename) end + +-- Reverse the order of items in {object} in-place. +-- {object} can be a |List| or a |Blob|. +-- Returns {object}. +-- Returns zero if {object} is not a List or a Blob. +-- If you want an object to remain unmodified make a copy first: +-- ```vim +-- :let revlist = reverse(copy(mylist)) +-- ``` +-- Can also be used as a |method|: > +-- mylist->reverse() +--- @return any[] +function vim.fn.reverse(object) end + +-- Round off {expr} to the nearest integral value and return it +-- as a |Float|. If {expr} lies halfway between two integral +-- values, then use the larger one (away from zero). +-- {expr} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- echo round(0.456) +-- ``` +-- 0.0 > +-- echo round(4.5) +-- < 5.0 +-- ```vim +-- echo round(-4.5) +-- ``` +-- -5.0 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->round() +-- ``` +--- @return float +function vim.fn.round(expr) end + +-- Sends {event} to {channel} via |RPC| and returns immediately. +-- If {channel} is 0, the event is broadcast to all channels. +-- Example: +-- ```vim +-- :au VimLeave call rpcnotify(0, "leaving") +-- ``` +--- @param args? any[] +--- @param ...? any +function vim.fn.rpcnotify(channel, event, args, ...) end + +-- Sends a request to {channel} to invoke {method} via +-- |RPC| and blocks until a response is received. +-- Example: +-- ```vim +-- :let result = rpcrequest(rpc_chan, "func", 1, 2, 3) +-- ``` +--- @param args? any[] +--- @param ...? any +function vim.fn.rpcrequest(channel, method, args, ...) end + +-- Deprecated. Replace +-- ```vim +-- :let id = rpcstart('prog', ['arg1', 'arg2']) +-- ``` +-- with > +-- :let id = jobstart(['prog', 'arg1', 'arg2'], {'rpc': v:true}) +--- @param argv? any +function vim.fn.rpcstart(prog, argv) end + +-- Evaluate Ruby expression {expr} and return its result +-- converted to Vim data structures. +-- Numbers, floats and strings are returned as they are (strings +-- are copied though). +-- Arrays are represented as Vim |List| type. +-- Hashes are represented as Vim |Dictionary| type. +-- Other objects are represented as strings resulted from their +-- "Object#to_s" method. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetRubyExpr()->rubyeval() +-- ``` +function vim.fn.rubyeval(expr) end + +-- Like |screenchar()|, but return the attribute. This is a rather +-- arbitrary number that can only be used to compare to the +-- attribute at other positions. +-- Returns -1 when row or col is out of range. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetRow()->screenattr(col) +-- ``` +--- @param col number +--- @return number +function vim.fn.screenattr(row, col) end + +-- The result is a Number, which is the character at position +-- [row, col] on the screen. This works for every possible +-- screen position, also status lines, window separators and the +-- command line. The top left position is row one, column one +-- The character excludes composing characters. For double-byte +-- encodings it may only be the first byte. +-- This is mainly to be used for testing. +-- Returns -1 when row or col is out of range. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetRow()->screenchar(col) +-- ``` +--- @param col number +--- @return number +function vim.fn.screenchar(row, col) end + +-- The result is a List of Numbers. The first number is the same +-- as what |screenchar()| returns. Further numbers are +-- composing characters on top of the base character. +-- This is mainly to be used for testing. +-- Returns an empty List when row or col is out of range. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetRow()->screenchars(col) +-- ``` +--- @param col number +--- @return any[] +function vim.fn.screenchars(row, col) end + +-- The result is a Number, which is the current screen column of +-- the cursor. The leftmost column has number 1. +-- This function is mainly used for testing. +-- +-- Note: Always returns the current screen column, thus if used +-- in a command (e.g. ":echo screencol()") it will return the +-- column inside the command line, which is 1 when the command is +-- executed. To get the cursor position in the file use one of +-- the following mappings: +-- ```vim +-- nnoremap GG ":echom " .. screencol() .. "\n" +-- nnoremap GG :echom screencol() +-- noremap GG echom screencol() +-- ``` +--- @return number +function vim.fn.screencol() end + +-- The result is a Dict with the screen position of the text +-- character in window {winid} at buffer line {lnum} and column +-- {col}. {col} is a one-based byte index. +-- The Dict has these members: +-- row screen row +-- col first screen column +-- endcol last screen column +-- curscol cursor screen column +-- If the specified position is not visible, all values are zero. +-- The "endcol" value differs from "col" when the character +-- occupies more than one screen cell. E.g. for a Tab "col" can +-- be 1 and "endcol" can be 8. +-- The "curscol" value is where the cursor would be placed. For +-- a Tab it would be the same as "endcol", while for a double +-- width character it would be the same as "col". +-- The |conceal| feature is ignored here, the column numbers are +-- as if 'conceallevel' is zero. You can set the cursor to the +-- right position and use |screencol()| to get the value with +-- |conceal| taken into account. +-- If the position is in a closed fold the screen position of the +-- first character is returned, {col} is not used. +-- Returns an empty Dict if {winid} is invalid. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinid()->screenpos(lnum, col) +-- ``` +--- @param winid window +--- @param lnum number +--- @param col number +--- @return table +function vim.fn.screenpos(winid, lnum, col) end + +-- The result is a Number, which is the current screen row of the +-- cursor. The top line has number one. +-- This function is mainly used for testing. +-- Alternatively you can use |winline()|. +-- +-- Note: Same restrictions as with |screencol()|. +--- @return number +function vim.fn.screenrow() end + +-- The result is a String that contains the base character and +-- any composing characters at position [row, col] on the screen. +-- This is like |screenchars()| but returning a String with the +-- characters. +-- This is mainly to be used for testing. +-- Returns an empty String when row or col is out of range. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetRow()->screenstring(col) +-- ``` +--- @param col number +--- @return string +function vim.fn.screenstring(row, col) end + +-- Search for regexp pattern {pattern}. The search starts at the +-- cursor position (you can use |cursor()| to set it). +-- +-- When a match has been found its line number is returned. +-- If there is no match a 0 is returned and the cursor doesn't +-- move. No error message is given. +-- +-- {flags} is a String, which can contain these character flags: +-- 'b' search Backward instead of forward +-- 'c' accept a match at the Cursor position +-- 'e' move to the End of the match +-- 'n' do Not move the cursor +-- 'p' return number of matching sub-Pattern (see below) +-- 's' Set the ' mark at the previous location of the cursor +-- 'w' Wrap around the end of the file +-- 'W' don't Wrap around the end of the file +-- 'z' start searching at the cursor column instead of Zero +-- If neither 'w' or 'W' is given, the 'wrapscan' option applies. +-- +-- If the 's' flag is supplied, the ' mark is set, only if the +-- cursor is moved. The 's' flag cannot be combined with the 'n' +-- flag. +-- +-- 'ignorecase', 'smartcase' and 'magic' are used. +-- +-- When the 'z' flag is not given, forward searching always +-- starts in column zero and then matches before the cursor are +-- skipped. When the 'c' flag is present in 'cpo' the next +-- search starts after the match. Without the 'c' flag the next +-- search starts one column after the start of the match. This +-- matters for overlapping matches. See |cpo-c|. You can also +-- insert "\ze" to change where the match ends, see |/\ze|. +-- +-- When searching backwards and the 'z' flag is given then the +-- search starts in column zero, thus no match in the current +-- line will be found (unless wrapping around the end of the +-- file). +-- +-- When the {stopline} argument is given then the search stops +-- after searching this line. This is useful to restrict the +-- search to a range of lines. Examples: +-- ```vim +-- let match = search('(', 'b', line("w0")) +-- let end = search('END', '', line("w$")) +-- ``` +-- When {stopline} is used and it is not zero this also implies +-- that the search does not wrap around the end of the file. +-- A zero value is equal to not giving the argument. +-- +-- When the {timeout} argument is given the search stops when +-- more than this many milliseconds have passed. Thus when +-- {timeout} is 500 the search stops after half a second. +-- The value must not be negative. A zero value is like not +-- giving the argument. +-- +-- If the {skip} expression is given it is evaluated with the +-- cursor positioned on the start of a match. If it evaluates to +-- non-zero this match is skipped. This can be used, for +-- example, to skip a match in a comment or a string. +-- {skip} can be a string, which is evaluated as an expression, a +-- function reference or a lambda. +-- When {skip} is omitted or empty, every match is accepted. +-- When evaluating {skip} causes an error the search is aborted +-- and -1 returned. +-- +-- With the 'p' flag the returned value is one more than the +-- first sub-match in \(\). One if none of them matched but the +-- whole pattern did match. +-- To get the column number too use |searchpos()|. +-- +-- The cursor will be positioned at the match, unless the 'n' +-- flag is used. +-- +-- Example (goes over all files in the argument list): +-- ```vim +-- :let n = 1 +-- :while n <= argc() " loop over all files in arglist +-- : exe "argument " .. n +-- : " start at the last char in the file and wrap for the +-- : " first search to find match at start of file +-- : normal G$ +-- : let flags = "w" +-- : while search("foo", flags) > 0 +-- : s/foo/bar/g +-- : let flags = "W" +-- : endwhile +-- : update " write the file if modified +-- : let n = n + 1 +-- :endwhile +-- ``` +-- Example for using some flags: +-- ```vim +-- :echo search('\search() +-- ``` +--- @param flags? any +--- @param stopline? any +--- @param timeout? any +--- @param skip? any +--- @return number +function vim.fn.search(pattern, flags, stopline, timeout, skip) end + +-- Get or update the last search count, like what is displayed +-- without the "S" flag in 'shortmess'. This works even if +-- 'shortmess' does contain the "S" flag. +-- +-- This returns a Dictionary. The dictionary is empty if the +-- previous pattern was not set and "pattern" was not specified. +-- +-- key type meaning ~ +-- current |Number| current position of match; +-- 0 if the cursor position is +-- before the first match +-- exact_match |Boolean| 1 if "current" is matched on +-- "pos", otherwise 0 +-- total |Number| total count of matches found +-- incomplete |Number| 0: search was fully completed +-- 1: recomputing was timed out +-- 2: max count exceeded +-- +-- For {options} see further down. +-- +-- To get the last search count when |n| or |N| was pressed, call +-- this function with `recompute: 0` . This sometimes returns +-- wrong information because |n| and |N|'s maximum count is 99. +-- If it exceeded 99 the result must be max count + 1 (100). If +-- you want to get correct information, specify `recompute: 1`: +-- ```vim +-- +-- " result == maxcount + 1 (100) when many matches +-- let result = searchcount(#{recompute: 0}) +-- +-- " Below returns correct result (recompute defaults +-- " to 1) +-- let result = searchcount() +-- ``` +-- The function is useful to add the count to 'statusline': +-- ```vim +-- function! LastSearchCount() abort +-- let result = searchcount(#{recompute: 0}) +-- if empty(result) +-- return '' +-- endif +-- if result.incomplete ==# 1 " timed out +-- return printf(' /%s [?/??]', @/) +-- elseif result.incomplete ==# 2 " max count exceeded +-- if result.total > result.maxcount && +-- \ result.current > result.maxcount +-- return printf(' /%s [>%d/>%d]', @/, +-- \ result.current, result.total) +-- elseif result.total > result.maxcount +-- return printf(' /%s [%d/>%d]', @/, +-- \ result.current, result.total) +-- endif +-- endif +-- return printf(' /%s [%d/%d]', @/, +-- \ result.current, result.total) +-- endfunction +-- let &statusline ..= '%{LastSearchCount()}' +-- +-- " Or if you want to show the count only when +-- " 'hlsearch' was on +-- " let &statusline ..= +-- " \ '%{v:hlsearch ? LastSearchCount() : ""}' +-- ``` +-- You can also update the search count, which can be useful in a +-- |CursorMoved| or |CursorMovedI| autocommand: +-- ```vim +-- +-- autocmd CursorMoved,CursorMovedI * +-- \ let s:searchcount_timer = timer_start( +-- \ 200, function('s:update_searchcount')) +-- function! s:update_searchcount(timer) abort +-- if a:timer ==# s:searchcount_timer +-- call searchcount(#{ +-- \ recompute: 1, maxcount: 0, timeout: 100}) +-- redrawstatus +-- endif +-- endfunction +-- ``` +-- This can also be used to count matched texts with specified +-- pattern in the current buffer using "pattern": +-- ```vim +-- +-- " Count '\' in this buffer +-- " (Note that it also updates search count) +-- let result = searchcount(#{pattern: '\'}) +-- +-- " To restore old search count by old pattern, +-- " search again +-- call searchcount() +-- ``` +-- {options} must be a Dictionary. It can contain: +-- key type meaning ~ +-- recompute |Boolean| if |TRUE|, recompute the count +-- like |n| or |N| was executed. +-- otherwise returns the last +-- computed result (when |n| or +-- |N| was used when "S" is not +-- in 'shortmess', or this +-- function was called). +-- (default: |TRUE|) +-- pattern |String| recompute if this was given +-- and different with |@/|. +-- this works as same as the +-- below command is executed +-- before calling this function +-- ```vim +-- let @/ = pattern +-- ``` +-- (default: |@/|) +-- timeout |Number| 0 or negative number is no +-- timeout. timeout milliseconds +-- for recomputing the result +-- (default: 0) +-- maxcount |Number| 0 or negative number is no +-- limit. max count of matched +-- text while recomputing the +-- result. if search exceeded +-- total count, "total" value +-- becomes `maxcount + 1` +-- (default: 0) +-- pos |List| `[lnum, col, off]` value +-- when recomputing the result. +-- this changes "current" result +-- value. see |cursor()|, |getpos()| +-- (default: cursor's position) +-- +-- Can also be used as a |method|: +-- ```vim +-- GetSearchOpts()->searchcount() +-- ``` +--- @param options? table +--- @return table +function vim.fn.searchcount(options) end + +-- Search for the declaration of {name}. +-- +-- With a non-zero {global} argument it works like |gD|, find +-- first match in the file. Otherwise it works like |gd|, find +-- first match in the function. +-- +-- With a non-zero {thisblock} argument matches in a {} block +-- that ends before the cursor position are ignored. Avoids +-- finding variable declarations only valid in another scope. +-- +-- Moves the cursor to the found match. +-- Returns zero for success, non-zero for failure. +-- Example: +-- ```vim +-- if searchdecl('myvar') == 0 +-- echo getline('.') +-- endif +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- GetName()->searchdecl() +-- ``` +--- @param global? any +--- @param thisblock? any +--- @return number +function vim.fn.searchdecl(name, global, thisblock) end + +-- Search for the match of a nested start-end pair. This can be +-- used to find the "endif" that matches an "if", while other +-- if/endif pairs in between are ignored. +-- The search starts at the cursor. The default is to search +-- forward, include 'b' in {flags} to search backward. +-- If a match is found, the cursor is positioned at it and the +-- line number is returned. If no match is found 0 or -1 is +-- returned and the cursor doesn't move. No error message is +-- given. +-- +-- {start}, {middle} and {end} are patterns, see |pattern|. They +-- must not contain \( \) pairs. Use of \%( \) is allowed. When +-- {middle} is not empty, it is found when searching from either +-- direction, but only when not in a nested start-end pair. A +-- typical use is: +-- ```vim +-- searchpair('\', '\', '\') +-- ``` +-- By leaving {middle} empty the "else" is skipped. +-- +-- {flags} 'b', 'c', 'n', 's', 'w' and 'W' are used like with +-- |search()|. Additionally: +-- 'r' Repeat until no more matches found; will find the +-- outer pair. Implies the 'W' flag. +-- 'm' Return number of matches instead of line number with +-- the match; will be > 1 when 'r' is used. +-- Note: it's nearly always a good idea to use the 'W' flag, to +-- avoid wrapping around the end of the file. +-- +-- When a match for {start}, {middle} or {end} is found, the +-- {skip} expression is evaluated with the cursor positioned on +-- the start of the match. It should return non-zero if this +-- match is to be skipped. E.g., because it is inside a comment +-- or a string. +-- When {skip} is omitted or empty, every match is accepted. +-- When evaluating {skip} causes an error the search is aborted +-- and -1 returned. +-- {skip} can be a string, a lambda, a funcref or a partial. +-- Anything else makes the function fail. +-- +-- For {stopline} and {timeout} see |search()|. +-- +-- The value of 'ignorecase' is used. 'magic' is ignored, the +-- patterns are used like it's on. +-- +-- The search starts exactly at the cursor. A match with +-- {start}, {middle} or {end} at the next character, in the +-- direction of searching, is the first one found. Example: +-- ```vim +-- if 1 +-- if 2 +-- endif 2 +-- endif 1 +-- ``` +-- When starting at the "if 2", with the cursor on the "i", and +-- searching forwards, the "endif 2" is found. When starting on +-- the character just before the "if 2", the "endif 1" will be +-- found. That's because the "if 2" will be found first, and +-- then this is considered to be a nested if/endif from "if 2" to +-- "endif 2". +-- When searching backwards and {end} is more than one character, +-- it may be useful to put "\zs" at the end of the pattern, so +-- that when the cursor is inside a match with the end it finds +-- the matching start. +-- +-- Example, to find the "endif" command in a Vim script: +-- ```vim +-- +-- :echo searchpair('\', '\', '\', 'W', +-- \ 'getline(".") =~ "^\\s*\""') +-- +-- ``` +-- The cursor must be at or after the "if" for which a match is +-- to be found. Note that single-quote strings are used to avoid +-- having to double the backslashes. The skip expression only +-- catches comments at the start of a line, not after a command. +-- Also, a word "en" or "if" halfway through a line is considered +-- a match. +-- Another example, to search for the matching "{" of a "}": +-- ```vim +-- +-- :echo searchpair('{', '', '}', 'bW') +-- +-- ``` +-- This works when the cursor is at or before the "}" for which a +-- match is to be found. To reject matches that syntax +-- highlighting recognized as strings: +-- ```vim +-- +-- :echo searchpair('{', '', '}', 'bW', +-- \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"') +-- ``` +--- @param start string +--- @param middle? string +--- @param end_ string +--- @param flags? string +--- @param skip? string +--- @param stopline? number +--- @param timeout? number +--- @return number +function vim.fn.searchpair(start, middle, end_, flags, skip, stopline, timeout) end + +-- Same as |searchpair()|, but returns a |List| with the line and +-- column position of the match. The first element of the |List| +-- is the line number and the second element is the byte index of +-- the column position of the match. If no match is found, +-- returns [0, 0]. +-- ```vim +-- +-- :let [lnum,col] = searchpairpos('{', '', '}', 'n') +-- ``` +-- See |match-parens| for a bigger and more useful example. +--- @param start string +--- @param middle? string +--- @param end_ string +--- @param flags? string +--- @param skip? string +--- @param stopline? number +--- @param timeout? number +--- @return any[] +function vim.fn.searchpairpos(start, middle, end_, flags, skip, stopline, timeout) end + +-- Same as |search()|, but returns a |List| with the line and +-- column position of the match. The first element of the |List| +-- is the line number and the second element is the byte index of +-- the column position of the match. If no match is found, +-- returns [0, 0]. +-- Example: +-- ```vim +-- :let [lnum, col] = searchpos('mypattern', 'n') +-- +-- ``` +-- When the 'p' flag is given then there is an extra item with +-- the sub-pattern match number |search()-sub-match|. Example: +-- ```vim +-- :let [lnum, col, submatch] = searchpos('\(\l\)\|\(\u\)', 'np') +-- ``` +-- In this example "submatch" is 2 when a lowercase letter is +-- found |/\l|, 3 when an uppercase letter is found |/\u|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetPattern()->searchpos() +-- ``` +--- @param flags? any +--- @param stopline? any +--- @param timeout? any +--- @param skip? any +--- @return any[] +function vim.fn.searchpos(pattern, flags, stopline, timeout, skip) end + +-- Returns a list of server addresses, or empty if all servers +-- were stopped. |serverstart()| |serverstop()| +-- Example: +-- ```vim +-- :echo serverlist() +-- ``` +--- @return string +function vim.fn.serverlist() end + +-- Opens a socket or named pipe at {address} and listens for +-- |RPC| messages. Clients can send |API| commands to the +-- returned address to control Nvim. +-- +-- Returns the address string (which may differ from the +-- {address} argument, see below). +-- +-- - If {address} has a colon (":") it is a TCP/IPv4/IPv6 address +-- where the last ":" separates host and port (empty or zero +-- assigns a random port). +-- - Else {address} is the path to a named pipe (except on Windows). +-- - If {address} has no slashes ("/") it is treated as the +-- "name" part of a generated path in this format: +-- ```vim +-- stdpath("run").."/{name}.{pid}.{counter}" +-- ``` +-- - If {address} is omitted the name is "nvim". > +-- :echo serverstart() +-- => /tmp/nvim.bram/oknANW/nvim.15430.5 +-- +-- < Example bash command to list all Nvim servers: +-- ```vim +-- ls ${XDG_RUNTIME_DIR:-${TMPDIR}nvim.${USER}}/.0 +-- +-- ``` +-- Example named pipe: > +-- if has('win32') +-- echo serverstart('\\.\pipe\nvim-pipe-1234') +-- else +-- echo serverstart('nvim.sock') +-- endif +-- < +-- Example TCP/IP address: +-- ```vim +-- echo serverstart('::1:12345') +-- ``` +--- @param address? any +function vim.fn.serverstart(address) end + +-- Closes the pipe or socket at {address}. +-- Returns TRUE if {address} is valid, else FALSE. +-- If |v:servername| is stopped it is set to the next available +-- address in |serverlist()|. +function vim.fn.serverstop(address) end + +-- Set line {lnum} to {text} in buffer {buf}. This works like +-- |setline()| for the specified buffer. +-- +-- This function works only for loaded buffers. First call +-- |bufload()| if needed. +-- +-- To insert lines use |appendbufline()|. +-- +-- {text} can be a string to set one line, or a list of strings +-- to set multiple lines. If the list extends below the last +-- line then those lines are added. +-- +-- For the use of {buf}, see |bufname()| above. +-- +-- {lnum} is used like with |setline()|. +-- Use "$" to refer to the last line in buffer {buf}. +-- When {lnum} is just below the last line the {text} will be +-- added below the last line. +-- On success 0 is returned, on failure 1 is returned. +-- +-- If {buf} is not a valid buffer or {lnum} is not valid, an +-- error message is given. +-- +-- Can also be used as a |method|, the base is passed as the +-- third argument: +-- ```vim +-- GetText()->setbufline(buf, lnum) +-- ``` +--- @param buf buffer +--- @param lnum number +--- @param text string +--- @return number +function vim.fn.setbufline(buf, lnum, text) end + +-- Set option or local variable {varname} in buffer {buf} to +-- {val}. +-- This also works for a global or local window option, but it +-- doesn't work for a global or local window variable. +-- For a local window option the global value is unchanged. +-- For the use of {buf}, see |bufname()| above. +-- The {varname} argument is a string. +-- Note that the variable name without "b:" must be used. +-- Examples: +-- ```vim +-- :call setbufvar(1, "&mod", 1) +-- :call setbufvar("todo", "myvar", "foobar") +-- ``` +-- This function is not available in the |sandbox|. +-- +-- Can also be used as a |method|, the base is passed as the +-- third argument: +-- ```vim +-- GetValue()->setbufvar(buf, varname) +-- ``` +--- @param buf buffer +--- @return boolean +function vim.fn.setbufvar(buf, varname, val) end + +-- Specify overrides for cell widths of character ranges. This +-- tells Vim how wide characters are when displayed in the +-- terminal, counted in screen cells. The values override +-- 'ambiwidth'. Example: +-- ```vim +-- call setcellwidths([ +-- \ [0x111, 0x111, 1], +-- \ [0x2194, 0x2199, 2], +-- \ ]) +-- +-- ``` +-- The {list} argument is a List of Lists with each three +-- numbers: [{low}, {high}, {width}]. +-- {low} and {high} can be the same, in which case this refers to +-- one character. Otherwise it is the range of characters from +-- {low} to {high} (inclusive). +-- Only characters with value 0x80 and higher can be used. +-- +-- {width} must be either 1 or 2, indicating the character width +-- in screen cells. +-- An error is given if the argument is invalid, also when a +-- range overlaps with another. +-- +-- If the new value causes 'fillchars' or 'listchars' to become +-- invalid it is rejected and an error is given. +-- +-- To clear the overrides pass an empty {list}: +-- ```vim +-- call setcellwidths([]) +-- +-- ``` +-- You can use the script $VIMRUNTIME/tools/emoji_list.vim to see +-- the effect for known emoji characters. Move the cursor +-- through the text to check if the cell widths of your terminal +-- match with what Vim knows about each emoji. If it doesn't +-- look right you need to adjust the {list} argument. +--- @param list any[] +function vim.fn.setcellwidths(list) end + +-- Same as |setpos()| but uses the specified column number as the +-- character index instead of the byte index in the line. +-- +-- Example: +-- With the text "여보세요" in line 8: +-- ```vim +-- call setcharpos('.', [0, 8, 4, 0]) +-- ``` +-- positions the cursor on the fourth character '요'. > +-- call setpos('.', [0, 8, 4, 0]) +-- < positions the cursor on the second character '보'. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetPosition()->setcharpos('.') +-- ``` +--- @param list any[] +--- @return number +function vim.fn.setcharpos(expr, list) end + +-- Set the current character search information to {dict}, +-- which contains one or more of the following entries: +-- +-- char character which will be used for a subsequent +-- |,| or |;| command; an empty string clears the +-- character search +-- forward direction of character search; 1 for forward, +-- 0 for backward +-- until type of character search; 1 for a |t| or |T| +-- character search, 0 for an |f| or |F| +-- character search +-- +-- This can be useful to save/restore a user's character search +-- from a script: +-- ```vim +-- :let prevsearch = getcharsearch() +-- :" Perform a command which clobbers user's search +-- :call setcharsearch(prevsearch) +-- ``` +-- Also see |getcharsearch()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- SavedSearch()->setcharsearch() +-- ``` +--- @param dict table +--- @return table +function vim.fn.setcharsearch(dict) end + +-- Set the command line to {str} and set the cursor position to +-- {pos}. +-- If {pos} is omitted, the cursor is positioned after the text. +-- Returns 0 when successful, 1 when not editing the command +-- line. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->setcmdline() +-- ``` +--- @param str string +--- @param pos? number +--- @return number +function vim.fn.setcmdline(str, pos) end + +-- Set the cursor position in the command line to byte position +-- {pos}. The first position is 1. +-- Use |getcmdpos()| to obtain the current position. +-- Only works while editing the command line, thus you must use +-- |c_CTRL-\_e|, |c_CTRL-R_=| or |c_CTRL-R_CTRL-R| with '='. For +-- |c_CTRL-\_e| and |c_CTRL-R_CTRL-R| with '=' the position is +-- set after the command line is set to the expression. For +-- |c_CTRL-R_=| it is set after evaluating the expression but +-- before inserting the resulting text. +-- When the number is too big the cursor is put at the end of the +-- line. A number smaller than one has undefined results. +-- Returns 0 when successful, 1 when not editing the command +-- line. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetPos()->setcmdpos() +-- ``` +--- @param pos number +--- @return number +function vim.fn.setcmdpos(pos) end + +-- Same as |cursor()| but uses the specified column number as the +-- character index instead of the byte index in the line. +-- +-- Example: +-- With the text "여보세요" in line 4: +-- ```vim +-- call setcursorcharpos(4, 3) +-- ``` +-- positions the cursor on the third character '세'. > +-- call cursor(4, 3) +-- < positions the cursor on the first character '여'. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetCursorPos()->setcursorcharpos() +-- ``` +--- @param list any[] +--- @return number +function vim.fn.setcursorcharpos(list) end + +-- Set environment variable {name} to {val}. Example: +-- ```vim +-- call setenv('HOME', '/home/myhome') +-- +-- ``` +-- When {val} is |v:null| the environment variable is deleted. +-- See also |expr-env|. +-- +-- Can also be used as a |method|, the base is passed as the +-- second argument: +-- ```vim +-- GetPath()->setenv('PATH') +-- ``` +function vim.fn.setenv(name, val) end + +-- Set the file permissions for {fname} to {mode}. +-- {mode} must be a string with 9 characters. It is of the form +-- "rwxrwxrwx", where each group of "rwx" flags represent, in +-- turn, the permissions of the owner of the file, the group the +-- file belongs to, and other users. A '-' character means the +-- permission is off, any other character means on. Multi-byte +-- characters are not supported. +-- +-- For example "rw-r-----" means read-write for the user, +-- readable by the group, not accessible by others. "xx-x-----" +-- would do the same thing. +-- +-- Returns non-zero for success, zero for failure. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetFilename()->setfperm(mode) +-- ``` +-- To read permissions see |getfperm()|. +function vim.fn.setfperm(fname, mode) end + +-- Set line {lnum} of the current buffer to {text}. To insert +-- lines use |append()|. To set lines in another buffer use +-- |setbufline()|. +-- +-- {lnum} is used like with |getline()|. +-- When {lnum} is just below the last line the {text} will be +-- added below the last line. +-- {text} can be any type or a List of any type, each item is +-- converted to a String. +-- +-- If this succeeds, FALSE is returned. If this fails (most likely +-- because {lnum} is invalid) TRUE is returned. +-- +-- Example: +-- ```vim +-- :call setline(5, strftime("%c")) +-- +-- ``` +-- When {text} is a |List| then line {lnum} and following lines +-- will be set to the items in the list. Example: +-- ```vim +-- :call setline(5, ['aaa', 'bbb', 'ccc']) +-- ``` +-- This is equivalent to: > +-- :for [n, l] in [[5, 'aaa'], [6, 'bbb'], [7, 'ccc']] +-- : call setline(n, l) +-- :endfor +-- +-- < Note: The '[ and '] marks are not set. +-- +-- Can also be used as a |method|, the base is passed as the +-- second argument: +-- ```vim +-- GetText()->setline(lnum) +-- ``` +--- @param lnum number +--- @param text string +--- @return number +function vim.fn.setline(lnum, text) end + +-- Create or replace or add to the location list for window {nr}. +-- {nr} can be the window number or the |window-ID|. +-- When {nr} is zero the current window is used. +-- +-- For a location list window, the displayed location list is +-- modified. For an invalid window number {nr}, -1 is returned. +-- Otherwise, same as |setqflist()|. +-- Also see |location-list|. +-- +-- For {action} see |setqflist-action|. +-- +-- If the optional {what} dictionary argument is supplied, then +-- only the items listed in {what} are set. Refer to |setqflist()| +-- for the list of supported keys in {what}. +-- +-- Can also be used as a |method|, the base is passed as the +-- second argument: +-- ```vim +-- GetLoclist()->setloclist(winnr) +-- ``` +--- @param nr number +--- @param list any[] +--- @param action? any +--- @param what? any +--- @return number +function vim.fn.setloclist(nr, list, action, what) end + +-- Restores a list of matches saved by |getmatches()| for the +-- current window. Returns 0 if successful, otherwise -1. All +-- current matches are cleared before the list is restored. See +-- example for |getmatches()|. +-- If {win} is specified, use the window with this number or +-- window ID instead of the current window. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetMatches()->setmatches() +-- ``` +--- @param list any[] +--- @param win? window +--- @return number +function vim.fn.setmatches(list, win) end + +-- Set the position for String {expr}. Possible values: +-- . the cursor +-- 'x mark x +-- +-- {list} must be a |List| with four or five numbers: +-- [bufnum, lnum, col, off] +-- [bufnum, lnum, col, off, curswant] +-- +-- "bufnum" is the buffer number. Zero can be used for the +-- current buffer. When setting an uppercase mark "bufnum" is +-- used for the mark position. For other marks it specifies the +-- buffer to set the mark in. You can use the |bufnr()| function +-- to turn a file name into a buffer number. +-- For setting the cursor and the ' mark "bufnum" is ignored, +-- since these are associated with a window, not a buffer. +-- Does not change the jumplist. +-- +-- "lnum" and "col" are the position in the buffer. The first +-- column is 1. Use a zero "lnum" to delete a mark. If "col" is +-- smaller than 1 then 1 is used. To use the character count +-- instead of the byte count, use |setcharpos()|. +-- +-- The "off" number is only used when 'virtualedit' is set. Then +-- it is the offset in screen columns from the start of the +-- character. E.g., a position within a or after the last +-- character. +-- +-- The "curswant" number is only used when setting the cursor +-- position. It sets the preferred column for when moving the +-- cursor vertically. When the "curswant" number is missing the +-- preferred column is not set. When it is present and setting a +-- mark position it is not used. +-- +-- Note that for '< and '> changing the line number may result in +-- the marks to be effectively be swapped, so that '< is always +-- before '>. +-- +-- Returns 0 when the position could be set, -1 otherwise. +-- An error message is given if {expr} is invalid. +-- +-- Also see |setcharpos()|, |getpos()| and |getcurpos()|. +-- +-- This does not restore the preferred column for moving +-- vertically; if you set the cursor position with this, |j| and +-- |k| motions will jump to previous columns! Use |cursor()| to +-- also set the preferred column. Also see the "curswant" key in +-- |winrestview()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetPosition()->setpos('.') +-- ``` +--- @param list any[] +--- @return number +function vim.fn.setpos(expr, list) end + +-- Create or replace or add to the quickfix list. +-- +-- If the optional {what} dictionary argument is supplied, then +-- only the items listed in {what} are set. The first {list} +-- argument is ignored. See below for the supported items in +-- {what}. +-- +-- When {what} is not present, the items in {list} are used. Each +-- item must be a dictionary. Non-dictionary items in {list} are +-- ignored. Each dictionary item can contain the following +-- entries: +-- +-- bufnr buffer number; must be the number of a valid +-- buffer +-- filename name of a file; only used when "bufnr" is not +-- present or it is invalid. +-- module name of a module; if given it will be used in +-- quickfix error window instead of the filename. +-- lnum line number in the file +-- end_lnum end of lines, if the item spans multiple lines +-- pattern search pattern used to locate the error +-- col column number +-- vcol when non-zero: "col" is visual column +-- when zero: "col" is byte index +-- end_col end column, if the item spans multiple columns +-- nr error number +-- text description of the error +-- type single-character error type, 'E', 'W', etc. +-- valid recognized error message +-- +-- The "col", "vcol", "nr", "type" and "text" entries are +-- optional. Either "lnum" or "pattern" entry can be used to +-- locate a matching error line. +-- If the "filename" and "bufnr" entries are not present or +-- neither the "lnum" or "pattern" entries are present, then the +-- item will not be handled as an error line. +-- If both "pattern" and "lnum" are present then "pattern" will +-- be used. +-- If the "valid" entry is not supplied, then the valid flag is +-- set when "bufnr" is a valid buffer or "filename" exists. +-- If you supply an empty {list}, the quickfix list will be +-- cleared. +-- Note that the list is not exactly the same as what +-- |getqflist()| returns. +-- +-- {action} values: +-- 'a' The items from {list} are added to the existing +-- quickfix list. If there is no existing list, then a +-- new list is created. +-- +-- 'r' The items from the current quickfix list are replaced +-- with the items from {list}. This can also be used to +-- clear the list: +-- ```vim +-- :call setqflist([], 'r') +-- ``` +-- 'f' All the quickfix lists in the quickfix stack are +-- freed. +-- +-- If {action} is not present or is set to ' ', then a new list +-- is created. The new quickfix list is added after the current +-- quickfix list in the stack and all the following lists are +-- freed. To add a new quickfix list at the end of the stack, +-- set "nr" in {what} to "$". +-- +-- The following items can be specified in dictionary {what}: +-- context quickfix list context. See |quickfix-context| +-- efm errorformat to use when parsing text from +-- "lines". If this is not present, then the +-- 'errorformat' option value is used. +-- See |quickfix-parse| +-- id quickfix list identifier |quickfix-ID| +-- idx index of the current entry in the quickfix +-- list specified by "id" or "nr". If set to '$', +-- then the last entry in the list is set as the +-- current entry. See |quickfix-index| +-- items list of quickfix entries. Same as the {list} +-- argument. +-- lines use 'errorformat' to parse a list of lines and +-- add the resulting entries to the quickfix list +-- {nr} or {id}. Only a |List| value is supported. +-- See |quickfix-parse| +-- nr list number in the quickfix stack; zero +-- means the current quickfix list and "$" means +-- the last quickfix list. +-- quickfixtextfunc +-- function to get the text to display in the +-- quickfix window. The value can be the name of +-- a function or a funcref or a lambda. Refer to +-- |quickfix-window-function| for an explanation +-- of how to write the function and an example. +-- title quickfix list title text. See |quickfix-title| +-- Unsupported keys in {what} are ignored. +-- If the "nr" item is not present, then the current quickfix list +-- is modified. When creating a new quickfix list, "nr" can be +-- set to a value one greater than the quickfix stack size. +-- When modifying a quickfix list, to guarantee that the correct +-- list is modified, "id" should be used instead of "nr" to +-- specify the list. +-- +-- Examples (See also |setqflist-examples|): +-- ```vim +-- :call setqflist([], 'r', {'title': 'My search'}) +-- :call setqflist([], 'r', {'nr': 2, 'title': 'Errors'}) +-- :call setqflist([], 'a', {'id':qfid, 'lines':["F1:10:L10"]}) +-- ``` +-- Returns zero for success, -1 for failure. +-- +-- This function can be used to create a quickfix list +-- independent of the 'errorformat' setting. Use a command like +-- `:cc 1` to jump to the first position. +-- +-- Can also be used as a |method|, the base is passed as the +-- second argument: +-- ```vim +-- GetErrorlist()->setqflist() +-- ``` +--- @param list any[] +--- @param action? any +--- @param what? any +--- @return number +function vim.fn.setqflist(list, action, what) end + +-- Set the register {regname} to {value}. +-- If {regname} is "" or "@", the unnamed register '"' is used. +-- The {regname} argument is a string. +-- +-- {value} may be any value returned by |getreg()| or +-- |getreginfo()|, including a |List| or |Dict|. +-- If {options} contains "a" or {regname} is upper case, +-- then the value is appended. +-- +-- {options} can also contain a register type specification: +-- "c" or "v" |charwise| mode +-- "l" or "V" |linewise| mode +-- "b" or "" |blockwise-visual| mode +-- If a number immediately follows "b" or "" then this is +-- used as the width of the selection - if it is not specified +-- then the width of the block is set to the number of characters +-- in the longest line (counting a as 1 character). +-- If {options} contains "u" or '"', then the unnamed register is +-- set to point to register {regname}. +-- +-- If {options} contains no register settings, then the default +-- is to use character mode unless {value} ends in a for +-- string {value} and linewise mode for list {value}. Blockwise +-- mode is never selected automatically. +-- Returns zero for success, non-zero for failure. +-- +-- +-- Note: you may not use |List| containing more than one item to +-- set search and expression registers. Lists containing no +-- items act like empty strings. +-- +-- Examples: +-- ```vim +-- :call setreg(v:register, @*) +-- :call setreg('*', @%, 'ac') +-- :call setreg('a', "1\n2\n3", 'b5') +-- :call setreg('"', { 'points_to': 'a'}) +-- +-- ``` +-- This example shows using the functions to save and restore a +-- register: +-- ```vim +-- :let var_a = getreginfo() +-- :call setreg('a', var_a) +-- ``` +-- or: > +-- :let var_a = getreg('a', 1, 1) +-- :let var_amode = getregtype('a') +-- .... +-- :call setreg('a', var_a, var_amode) +-- < Note: you may not reliably restore register value +-- without using the third argument to |getreg()| as without it +-- newlines are represented as newlines AND Nul bytes are +-- represented as newlines as well, see |NL-used-for-Nul|. +-- +-- You can also change the type of a register by appending +-- nothing: +-- ```vim +-- :call setreg('a', '', 'al') +-- +-- ``` +-- Can also be used as a |method|, the base is passed as the +-- second argument: +-- ```vim +-- GetText()->setreg('a') +-- ``` +--- @param options? table +--- @return number +function vim.fn.setreg(regname, value, options) end + +-- Set tab-local variable {varname} to {val} in tab page {tabnr}. +-- |t:var| +-- The {varname} argument is a string. +-- Note that the variable name without "t:" must be used. +-- Tabs are numbered starting with one. +-- This function is not available in the |sandbox|. +-- +-- Can also be used as a |method|, the base is passed as the +-- third argument: +-- ```vim +-- GetValue()->settabvar(tab, name) +-- ``` +--- @param tabnr number +--- @return boolean +function vim.fn.settabvar(tabnr, varname, val) end + +-- Set option or local variable {varname} in window {winnr} to +-- {val}. +-- Tabs are numbered starting with one. For the current tabpage +-- use |setwinvar()|. +-- {winnr} can be the window number or the |window-ID|. +-- When {winnr} is zero the current window is used. +-- This also works for a global or local buffer option, but it +-- doesn't work for a global or local buffer variable. +-- For a local buffer option the global value is unchanged. +-- Note that the variable name without "w:" must be used. +-- Examples: +-- ```vim +-- :call settabwinvar(1, 1, "&list", 0) +-- :call settabwinvar(3, 2, "myvar", "foobar") +-- ``` +-- This function is not available in the |sandbox|. +-- +-- Can also be used as a |method|, the base is passed as the +-- fourth argument: +-- ```vim +-- GetValue()->settabwinvar(tab, winnr, name) +-- ``` +--- @param tabnr number +--- @param winnr window +--- @return boolean +function vim.fn.settabwinvar(tabnr, winnr, varname, val) end + +-- Modify the tag stack of the window {nr} using {dict}. +-- {nr} can be the window number or the |window-ID|. +-- +-- For a list of supported items in {dict}, refer to +-- |gettagstack()|. "curidx" takes effect before changing the tag +-- stack. +-- +-- How the tag stack is modified depends on the {action} +-- argument: +-- - If {action} is not present or is set to 'r', then the tag +-- stack is replaced. +-- - If {action} is set to 'a', then new entries from {dict} are +-- pushed (added) onto the tag stack. +-- - If {action} is set to 't', then all the entries from the +-- current entry in the tag stack or "curidx" in {dict} are +-- removed and then new entries are pushed to the stack. +-- +-- The current index is set to one after the length of the tag +-- stack after the modification. +-- +-- Returns zero for success, -1 for failure. +-- +-- Examples (for more examples see |tagstack-examples|): +-- Empty the tag stack of window 3: +-- ```vim +-- call settagstack(3, {'items' : []}) +-- +-- ``` +-- Save and restore the tag stack: > +-- let stack = gettagstack(1003) +-- " do something else +-- call settagstack(1003, stack) +-- unlet stack +-- < +-- Can also be used as a |method|, the base is passed as the +-- second argument: +-- ```vim +-- GetStack()->settagstack(winnr) +-- ``` +--- @param nr number +--- @param dict table +--- @param action? any +--- @return number +function vim.fn.settagstack(nr, dict, action) end + +-- Like |settabwinvar()| for the current tab page. +-- Examples: +-- ```vim +-- :call setwinvar(1, "&list", 0) +-- :call setwinvar(2, "myvar", "foobar") +-- +-- ``` +-- Can also be used as a |method|, the base is passed as the +-- third argument: +-- ```vim +-- GetValue()->setwinvar(winnr, name) +-- ``` +--- @param nr number +--- @return boolean +function vim.fn.setwinvar(nr, varname, val) end + +-- Returns a String with 64 hex characters, which is the SHA256 +-- checksum of {string}. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->sha256() +-- ``` +--- @param string string +--- @return string +function vim.fn.sha256(string) end + +-- Escape {string} for use as a shell command argument. +-- +-- On Windows when 'shellslash' is not set, encloses {string} in +-- double-quotes and doubles all double-quotes within {string}. +-- Otherwise encloses {string} in single-quotes and replaces all +-- "'" with "'\''". +-- +-- If {special} is a |non-zero-arg|: +-- - Special items such as "!", "%", "#" and "" will be +-- preceded by a backslash. The backslash will be removed again +-- by the |:!| command. +-- - The character is escaped. +-- +-- If 'shell' contains "csh" in the tail: +-- - The "!" character will be escaped. This is because csh and +-- tcsh use "!" for history replacement even in single-quotes. +-- - The character is escaped (twice if {special} is +-- a |non-zero-arg|). +-- +-- If 'shell' contains "fish" in the tail, the "\" character will +-- be escaped because in fish it is used as an escape character +-- inside single quotes. +-- +-- Example of use with a |:!| command: +-- ```vim +-- :exe '!dir ' .. shellescape(expand(''), 1) +-- ``` +-- This results in a directory listing for the file under the +-- cursor. Example of use with |system()|: +-- ```vim +-- :call system("chmod +w -- " .. shellescape(expand("%"))) +-- ``` +-- See also |::S|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetCommand()->shellescape() +-- ``` +--- @param string string +--- @param special? any +--- @return string +function vim.fn.shellescape(string, special) end + +-- Returns the effective value of 'shiftwidth'. This is the +-- 'shiftwidth' value unless it is zero, in which case it is the +-- 'tabstop' value. To be backwards compatible in indent +-- plugins, use this: +-- ```vim +-- if exists('*shiftwidth') +-- func s:sw() +-- return shiftwidth() +-- endfunc +-- else +-- func s:sw() +-- return &sw +-- endfunc +-- endif +-- ``` +-- And then use s:sw() instead of &sw. +-- +-- When there is one argument {col} this is used as column number +-- for which to return the 'shiftwidth' value. This matters for the +-- 'vartabstop' feature. If no {col} argument is given, column 1 +-- will be assumed. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetColumn()->shiftwidth() +-- ``` +--- @param col? number +--- @return number +function vim.fn.shiftwidth(col) end + +-- List define or update a list of signs +--- @param list any[] +--- @return any[] +--- @overload fun(name:string, dict?:table) +function vim.fn.sign_define(list) end + +-- List get a list of defined signs +--- @param name? any +--- @return any[] +function vim.fn.sign_getdefined(name) end + +-- List get a list of placed signs +--- @param buf? buffer +--- @param dict? table +--- @return any[] +function vim.fn.sign_getplaced(buf, dict) end + +-- Number jump to a sign +--- @param buf buffer +--- @return number +function vim.fn.sign_jump(id, group, buf) end + +-- Number place a sign +--- @param buf buffer +--- @param dict? table +--- @return number +function vim.fn.sign_place(id, group, name, buf, dict) end + +-- List place a list of signs +--- @param list any[] +--- @return any[] +function vim.fn.sign_placelist(list) end + +-- List undefine a list of signs +--- @param list any[] +--- @return any[] +function vim.fn.sign_undefine(list) end + +-- Number unplace a sign +--- @param dict? table +--- @return number +function vim.fn.sign_unplace(group, dict) end + +-- List unplace a list of signs +--- @param list any[] +--- @return any[] +function vim.fn.sign_unplacelist(list) end + +-- Simplify the file name as much as possible without changing +-- the meaning. Shortcuts (on MS-Windows) or symbolic links (on +-- Unix) are not resolved. If the first path component in +-- {filename} designates the current directory, this will be +-- valid for the result as well. A trailing path separator is +-- not removed either. On Unix "//path" is unchanged, but +-- "///path" is simplified to "/path" (this follows the Posix +-- standard). +-- Example: +-- ```vim +-- simplify("./dir/.././/file/") == "./file/" +-- ``` +-- Note: The combination "dir/.." is only removed if "dir" is +-- a searchable directory or does not exist. On Unix, it is also +-- removed when "dir" is a symbolic link within the same +-- directory. In order to resolve all the involved symbolic +-- links before simplifying the path name, use |resolve()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetName()->simplify() +-- ``` +--- @return string +function vim.fn.simplify(filename) end + +-- Return the sine of {expr}, measured in radians, as a |Float|. +-- {expr} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo sin(100) +-- ``` +-- -0.506366 > +-- :echo sin(-4.01) +-- < 0.763301 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->sin() +-- ``` +--- @return float +function vim.fn.sin(expr) end + +-- Return the hyperbolic sine of {expr} as a |Float| in the range +-- [-inf, inf]. +-- {expr} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo sinh(0.5) +-- ``` +-- 0.521095 > +-- :echo sinh(-0.9) +-- < -1.026517 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->sinh() +-- ``` +--- @return float +function vim.fn.sinh(expr) end + +-- Connect a socket to an address. If {mode} is "pipe" then +-- {address} should be the path of a local domain socket (on +-- unix) or named pipe (on Windows). If {mode} is "tcp" then +-- {address} should be of the form "host:port" where the host +-- should be an ip adderess or host name, and port the port +-- number. +-- +-- For "pipe" mode, see |luv-pipe-handle|. For "tcp" mode, see +-- |luv-tcp-handle|. +-- +-- Returns a |channel| ID. Close the socket with |chanclose()|. +-- Use |chansend()| to send data over a bytes socket, and +-- |rpcrequest()| and |rpcnotify()| to communicate with a RPC +-- socket. +-- +-- {opts} is an optional dictionary with these keys: +-- |on_data| : callback invoked when data was read from socket +-- data_buffered : read socket data in |channel-buffered| mode. +-- rpc : If set, |msgpack-rpc| will be used to communicate +-- over the socket. +-- Returns: +-- - The channel ID on success (greater than zero) +-- - 0 on invalid arguments or connection failure. +--- @param opts? table +--- @return number +function vim.fn.sockconnect(mode, address, opts) end + +-- Sort the items in {list} in-place. Returns {list}. +-- +-- If you want a list to remain unmodified make a copy first: +-- ```vim +-- :let sortedlist = sort(copy(mylist)) +-- +-- ``` +-- When {func} is omitted, is empty or zero, then sort() uses the +-- string representation of each item to sort on. Numbers sort +-- after Strings, |Lists| after Numbers. For sorting text in the +-- current buffer use |:sort|. +-- +-- When {func} is given and it is '1' or 'i' then case is +-- ignored. +-- +-- When {func} is given and it is 'l' then the current collation +-- locale is used for ordering. Implementation details: strcoll() +-- is used to compare strings. See |:language| check or set the +-- collation locale. |v:collate| can also be used to check the +-- current locale. Sorting using the locale typically ignores +-- case. Example: +-- ```vim +-- " ö is sorted similarly to o with English locale. +-- :language collate en_US.UTF8 +-- :echo sort(['n', 'o', 'O', 'ö', 'p', 'z'], 'l') +-- ``` +-- ['n', 'o', 'O', 'ö', 'p', 'z'] ~ +-- ```vim +-- " ö is sorted after z with Swedish locale. +-- :language collate sv_SE.UTF8 +-- :echo sort(['n', 'o', 'O', 'ö', 'p', 'z'], 'l') +-- ``` +-- ['n', 'o', 'O', 'p', 'z', 'ö'] ~ +-- This does not work properly on Mac. +-- +-- When {func} is given and it is 'n' then all items will be +-- sorted numerical (Implementation detail: this uses the +-- strtod() function to parse numbers, Strings, Lists, Dicts and +-- Funcrefs will be considered as being 0). +-- +-- When {func} is given and it is 'N' then all items will be +-- sorted numerical. This is like 'n' but a string containing +-- digits will be used as the number they represent. +-- +-- When {func} is given and it is 'f' then all items will be +-- sorted numerical. All values must be a Number or a Float. +-- +-- When {func} is a |Funcref| or a function name, this function +-- is called to compare items. The function is invoked with two +-- items as argument and must return zero if they are equal, 1 or +-- bigger if the first one sorts after the second one, -1 or +-- smaller if the first one sorts before the second one. +-- +-- {dict} is for functions with the "dict" attribute. It will be +-- used to set the local variable "self". |Dictionary-function| +-- +-- The sort is stable, items which compare equal (as number or as +-- string) will keep their relative position. E.g., when sorting +-- on numbers, text strings will sort next to each other, in the +-- same order as they were originally. +-- +-- Can also be used as a |method|: +-- ```vim +-- mylist->sort() +-- +-- ``` +-- Also see |uniq()|. +-- +-- Example: +-- ```vim +-- func MyCompare(i1, i2) +-- return a:i1 == a:i2 ? 0 : a:i1 > a:i2 ? 1 : -1 +-- endfunc +-- eval mylist->sort("MyCompare") +-- ``` +-- A shorter compare version for this specific simple case, which +-- ignores overflow: +-- ```vim +-- func MyCompare(i1, i2) +-- return a:i1 - a:i2 +-- endfunc +-- ``` +-- For a simple expression you can use a lambda: > +-- eval mylist->sort({i1, i2 -> i1 - i2}) +-- < +--- @param list any[] +--- @param func? fun() +--- @param dict? table +--- @return any[] +function vim.fn.sort(list, func, dict) end + +-- Return the sound-folded equivalent of {word}. Uses the first +-- language in 'spelllang' for the current window that supports +-- soundfolding. 'spell' must be set. When no sound folding is +-- possible the {word} is returned unmodified. +-- This can be used for making spelling suggestions. Note that +-- the method can be quite slow. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWord()->soundfold() +-- ``` +--- @return string +function vim.fn.soundfold(word) end + +-- Without argument: The result is the badly spelled word under +-- or after the cursor. The cursor is moved to the start of the +-- bad word. When no bad word is found in the cursor line the +-- result is an empty string and the cursor doesn't move. +-- +-- With argument: The result is the first word in {sentence} that +-- is badly spelled. If there are no spelling mistakes the +-- result is an empty string. +-- +-- The return value is a list with two items: +-- - The badly spelled word or an empty string. +-- - The type of the spelling error: +-- "bad" spelling mistake +-- "rare" rare word +-- "local" word only valid in another region +-- "caps" word should start with Capital +-- Example: +-- ```vim +-- echo spellbadword("the quik brown fox") +-- ``` +-- ['quik', 'bad'] ~ +-- +-- The spelling information for the current window and the value +-- of 'spelllang' are used. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->spellbadword() +-- ``` +--- @param sentence? any +--- @return string +function vim.fn.spellbadword(sentence) end + +-- Return a |List| with spelling suggestions to replace {word}. +-- When {max} is given up to this number of suggestions are +-- returned. Otherwise up to 25 suggestions are returned. +-- +-- When the {capital} argument is given and it's non-zero only +-- suggestions with a leading capital will be given. Use this +-- after a match with 'spellcapcheck'. +-- +-- {word} can be a badly spelled word followed by other text. +-- This allows for joining two words that were split. The +-- suggestions also include the following text, thus you can +-- replace a line. +-- +-- {word} may also be a good word. Similar words will then be +-- returned. {word} itself is not included in the suggestions, +-- although it may appear capitalized. +-- +-- The spelling information for the current window is used. The +-- values of 'spelllang' and 'spellsuggest' are used. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWord()->spellsuggest() +-- ``` +--- @param max? any +--- @param capital? any +--- @return any[] +function vim.fn.spellsuggest(word, max, capital) end + +-- Make a |List| out of {string}. When {pattern} is omitted or +-- empty each white-separated sequence of characters becomes an +-- item. +-- Otherwise the string is split where {pattern} matches, +-- removing the matched characters. 'ignorecase' is not used +-- here, add \c to ignore case. |/\c| +-- When the first or last item is empty it is omitted, unless the +-- {keepempty} argument is given and it's non-zero. +-- Other empty items are kept when {pattern} matches at least one +-- character or when {keepempty} is non-zero. +-- Example: +-- ```vim +-- :let words = split(getline('.'), '\W\+') +-- ``` +-- To split a string in individual characters: > +-- :for c in split(mystring, '\zs') +-- < If you want to keep the separator you can also use '\zs' at +-- the end of the pattern: +-- ```vim +-- :echo split('abc:def:ghi', ':\zs') +-- ``` +-- ['abc:', 'def:', 'ghi'] ~ +-- Splitting a table where the first element can be empty: +-- ```vim +-- :let items = split(line, ':', 1) +-- ``` +-- The opposite function is |join()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetString()->split() +-- ``` +--- @param string string +--- @param pattern? any +--- @param keepempty? any +--- @return any[] +function vim.fn.split(string, pattern, keepempty) end + +-- Return the non-negative square root of Float {expr} as a +-- |Float|. +-- {expr} must evaluate to a |Float| or a |Number|. When {expr} +-- is negative the result is NaN (Not a Number). Returns 0.0 if +-- {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo sqrt(100) +-- ``` +-- 10.0 > +-- :echo sqrt(-4.01) +-- < str2float("nan") +-- NaN may be different, it depends on system libraries. +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->sqrt() +-- ``` +--- @return float +function vim.fn.sqrt(expr) end + +-- Initialize seed used by |rand()|: +-- - If {expr} is not given, seed values are initialized by +-- reading from /dev/urandom, if possible, or using time(NULL) +-- a.k.a. epoch time otherwise; this only has second accuracy. +-- - If {expr} is given it must be a Number. It is used to +-- initialize the seed values. This is useful for testing or +-- when a predictable sequence is intended. +-- +-- Examples: +-- ```vim +-- :let seed = srand() +-- :let seed = srand(userinput) +-- :echo rand(seed) +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- userinput->srand() +-- ``` +--- @param expr? any +--- @return any[] +function vim.fn.srand(expr) end + +-- With |--headless| this opens stdin and stdout as a |channel|. +-- May be called only once. See |channel-stdio|. stderr is not +-- handled by this function, see |v:stderr|. +-- +-- Close the stdio handles with |chanclose()|. Use |chansend()| +-- to send data to stdout, and |rpcrequest()| and |rpcnotify()| +-- to communicate over RPC. +-- +-- {opts} is a dictionary with these keys: +-- |on_stdin| : callback invoked when stdin is written to. +-- on_print : callback invoked when Nvim needs to print a +-- message, with the message (whose type is string) +-- as sole argument. +-- stdin_buffered : read stdin in |channel-buffered| mode. +-- rpc : If set, |msgpack-rpc| will be used to communicate +-- over stdio +-- Returns: +-- - |channel-id| on success (value is always 1) +-- - 0 on invalid arguments +--- @param opts table +--- @return number +function vim.fn.stdioopen(opts) end + +-- Returns |standard-path| locations of various default files and +-- directories. +-- +-- {what} Type Description ~ +-- cache String Cache directory: arbitrary temporary +-- storage for plugins, etc. +-- config String User configuration directory. |init.vim| +-- is stored here. +-- config_dirs List Other configuration directories. +-- data String User data directory. +-- data_dirs List Other data directories. +-- log String Logs directory (for use by plugins too). +-- run String Run directory: temporary, local storage +-- for sockets, named pipes, etc. +-- state String Session state directory: storage for file +-- drafts, swap, undo, |shada|. +-- +-- Example: +-- ```vim +-- :echo stdpath("config") +-- ``` +--- @return string +function vim.fn.stdpath(what) end + +-- Convert String {string} to a Float. This mostly works the +-- same as when using a floating point number in an expression, +-- see |floating-point-format|. But it's a bit more permissive. +-- E.g., "1e40" is accepted, while in an expression you need to +-- write "1.0e40". The hexadecimal form "0x123" is also +-- accepted, but not others, like binary or octal. +-- When {quoted} is present and non-zero then embedded single +-- quotes before the dot are ignored, thus "1'000.0" is a +-- thousand. +-- Text after the number is silently ignored. +-- The decimal point is always '.', no matter what the locale is +-- set to. A comma ends the number: "12,345.67" is converted to +-- 12.0. You can strip out thousands separators with +-- |substitute()|: +-- ```vim +-- let f = str2float(substitute(text, ',', '', 'g')) +-- ``` +-- Returns 0.0 if the conversion fails. +-- +-- Can also be used as a |method|: +-- ```vim +-- let f = text->substitute(',', '', 'g')->str2float() +-- ``` +--- @param string string +--- @param quoted? any +--- @return float +function vim.fn.str2float(string, quoted) end + +-- Return a list containing the number values which represent +-- each character in String {string}. Examples: +-- ```vim +-- str2list(" ") returns [32] +-- str2list("ABC") returns [65, 66, 67] +-- ``` +-- |list2str()| does the opposite. +-- +-- UTF-8 encoding is always used, {utf8} option has no effect, +-- and exists only for backwards-compatibility. +-- With UTF-8 composing characters are handled properly: +-- ```vim +-- str2list("á") returns [97, 769] +-- +-- ``` +-- Can also be used as a |method|: > +-- GetString()->str2list() +--- @param string string +--- @param utf8? any +--- @return any[] +function vim.fn.str2list(string, utf8) end + +-- Convert string {string} to a number. +-- {base} is the conversion base, it can be 2, 8, 10 or 16. +-- When {quoted} is present and non-zero then embedded single +-- quotes are ignored, thus "1'000'000" is a million. +-- +-- When {base} is omitted base 10 is used. This also means that +-- a leading zero doesn't cause octal conversion to be used, as +-- with the default String to Number conversion. Example: +-- ```vim +-- let nr = str2nr('0123') +-- ``` +-- When {base} is 16 a leading "0x" or "0X" is ignored. With a +-- different base the result will be zero. Similarly, when +-- {base} is 8 a leading "0", "0o" or "0O" is ignored, and when +-- {base} is 2 a leading "0b" or "0B" is ignored. +-- Text after the number is silently ignored. +-- +-- Returns 0 if {string} is empty or on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->str2nr() +-- ``` +--- @param string string +--- @param base? any +--- @return number +function vim.fn.str2nr(string, base) end + +-- The result is a Number, which is the number of characters +-- in String {string}. Composing characters are ignored. +-- |strchars()| can count the number of characters, counting +-- composing characters separately. +-- +-- Returns 0 if {string} is empty or on error. +-- +-- Also see |strlen()|, |strdisplaywidth()| and |strwidth()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->strcharlen() +-- ``` +--- @param string string +--- @return number +function vim.fn.strcharlen(string) end + +-- Like |strpart()| but using character index and length instead +-- of byte index and length. Composing characters are counted +-- separately. +-- When a character index is used where a character does not +-- exist it is assumed to be one character. For example: +-- ```vim +-- strcharpart('abc', -1, 2) +-- ``` +-- results in 'a'. +-- +-- Returns an empty string on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->strcharpart(5) +-- ``` +--- @param start number +--- @param len? any +--- @return string +function vim.fn.strcharpart(src, start, len) end + +-- The result is a Number, which is the number of characters +-- in String {string}. +-- When {skipcc} is omitted or zero, composing characters are +-- counted separately. +-- When {skipcc} set to 1, Composing characters are ignored. +-- |strcharlen()| always does this. +-- +-- Returns zero on error. +-- +-- Also see |strlen()|, |strdisplaywidth()| and |strwidth()|. +-- +-- {skipcc} is only available after 7.4.755. For backward +-- compatibility, you can define a wrapper function: +-- ```vim +-- if has("patch-7.4.755") +-- function s:strchars(str, skipcc) +-- return strchars(a:str, a:skipcc) +-- endfunction +-- else +-- function s:strchars(str, skipcc) +-- if a:skipcc +-- return strlen(substitute(a:str, ".", "x", "g")) +-- else +-- return strchars(a:str) +-- endif +-- endfunction +-- endif +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- GetText()->strchars() +-- ``` +--- @param string string +--- @param skipcc? any +--- @return number +function vim.fn.strchars(string, skipcc) end + +-- The result is a Number, which is the number of display cells +-- String {string} occupies on the screen when it starts at {col} +-- (first column is zero). When {col} is omitted zero is used. +-- Otherwise it is the screen column where to start. This +-- matters for Tab characters. +-- The option settings of the current window are used. This +-- matters for anything that's displayed differently, such as +-- 'tabstop' and 'display'. +-- When {string} contains characters with East Asian Width Class +-- Ambiguous, this function's return value depends on 'ambiwidth'. +-- Returns zero on error. +-- Also see |strlen()|, |strwidth()| and |strchars()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->strdisplaywidth() +-- ``` +--- @param string string +--- @param col? number +--- @return number +function vim.fn.strdisplaywidth(string, col) end + +-- The result is a String, which is a formatted date and time, as +-- specified by the {format} string. The given {time} is used, +-- or the current time if no time is given. The accepted +-- {format} depends on your system, thus this is not portable! +-- See the manual page of the C function strftime() for the +-- format. The maximum length of the result is 80 characters. +-- See also |localtime()|, |getftime()| and |strptime()|. +-- The language can be changed with the |:language| command. +-- Examples: +-- ```vim +-- :echo strftime("%c") Sun Apr 27 11:49:23 1997 +-- :echo strftime("%Y %b %d %X") 1997 Apr 27 11:53:25 +-- :echo strftime("%y%m%d %T") 970427 11:53:55 +-- :echo strftime("%H:%M") 11:55 +-- :echo strftime("%c", getftime("file.c")) +-- Show mod time of file.c. +-- +-- ``` +-- Can also be used as a |method|: > +-- GetFormat()->strftime() +--- @param time? any +--- @return string +function vim.fn.strftime(format, time) end + +-- Get a Number corresponding to the character at {index} in +-- {str}. This uses a zero-based character index, not a byte +-- index. Composing characters are considered separate +-- characters here. Use |nr2char()| to convert the Number to a +-- String. +-- Returns -1 if {index} is invalid. +-- Also see |strcharpart()| and |strchars()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->strgetchar(5) +-- ``` +--- @param str string +--- @param index number +--- @return number +function vim.fn.strgetchar(str, index) end + +-- The result is a Number, which gives the byte index in +-- {haystack} of the first occurrence of the String {needle}. +-- If {start} is specified, the search starts at index {start}. +-- This can be used to find a second match: +-- ```vim +-- :let colon1 = stridx(line, ":") +-- :let colon2 = stridx(line, ":", colon1 + 1) +-- ``` +-- The search is done case-sensitive. +-- For pattern searches use |match()|. +-- -1 is returned if the {needle} does not occur in {haystack}. +-- See also |strridx()|. +-- Examples: +-- ```vim +-- :echo stridx("An Example", "Example") 3 +-- :echo stridx("Starting point", "Start") 0 +-- :echo stridx("Starting point", "start") -1 +-- ``` +-- stridx() works similar to the C function strstr(). When used +-- with a single character it works similar to strchr(). +-- +-- Can also be used as a |method|: +-- ```vim +-- GetHaystack()->stridx(needle) +-- ``` +--- @param start? number +--- @return number +function vim.fn.stridx(haystack, needle, start) end + +-- Return {expr} converted to a String. If {expr} is a Number, +-- Float, String, Blob or a composition of them, then the result +-- can be parsed back with |eval()|. +-- {expr} type result ~ +-- String 'string' +-- Number 123 +-- Float 123.123456 or 1.123456e8 or +-- `str2float('inf')` +-- Funcref `function('name')` +-- Blob 0z00112233.44556677.8899 +-- List [item, item] +-- Dictionary {key: value, key: value} +-- Note that in String values the ' character is doubled. +-- Also see |strtrans()|. +-- Note 2: Output format is mostly compatible with YAML, except +-- for infinite and NaN floating-point values representations +-- which use |str2float()|. Strings are also dumped literally, +-- only single quote is escaped, which does not allow using YAML +-- for parsing back binary strings. |eval()| should always work for +-- strings and floats though and this is the only official +-- method, use |msgpackdump()| or |json_encode()| if you need to +-- share data with other application. +-- +-- Can also be used as a |method|: +-- ```vim +-- mylist->string() +-- ``` +--- @return string +function vim.fn.string(expr) end + +-- The result is a Number, which is the length of the String +-- {string} in bytes. +-- If the argument is a Number it is first converted to a String. +-- For other types an error is given and zero is returned. +-- If you want to count the number of multibyte characters use +-- |strchars()|. +-- Also see |len()|, |strdisplaywidth()| and |strwidth()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetString()->strlen() +-- ``` +--- @param string string +--- @return number +function vim.fn.strlen(string) end + +-- The result is a String, which is part of {src}, starting from +-- byte {start}, with the byte length {len}. +-- When {chars} is present and TRUE then {len} is the number of +-- characters positions (composing characters are not counted +-- separately, thus "1" means one base character and any +-- following composing characters). +-- To count {start} as characters instead of bytes use +-- |strcharpart()|. +-- +-- When bytes are selected which do not exist, this doesn't +-- result in an error, the bytes are simply omitted. +-- If {len} is missing, the copy continues from {start} till the +-- end of the {src}. +-- ```vim +-- strpart("abcdefg", 3, 2) == "de" +-- strpart("abcdefg", -2, 4) == "ab" +-- strpart("abcdefg", 5, 4) == "fg" +-- strpart("abcdefg", 3) == "defg" +-- +-- ``` +-- Note: To get the first character, {start} must be 0. For +-- example, to get the character under the cursor: +-- ```vim +-- strpart(getline("."), col(".") - 1, 1, v:true) +-- ``` +-- Returns an empty string on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->strpart(5) +-- ``` +--- @param start number +--- @param len? any +--- @param chars? any +--- @return string +function vim.fn.strpart(src, start, len, chars) end + +-- The result is a Number, which is a unix timestamp representing +-- the date and time in {timestring}, which is expected to match +-- the format specified in {format}. +-- +-- The accepted {format} depends on your system, thus this is not +-- portable! See the manual page of the C function strptime() +-- for the format. Especially avoid "%c". The value of $TZ also +-- matters. +-- +-- If the {timestring} cannot be parsed with {format} zero is +-- returned. If you do not know the format of {timestring} you +-- can try different {format} values until you get a non-zero +-- result. +-- +-- See also |strftime()|. +-- Examples: +-- ```vim +-- :echo strptime("%Y %b %d %X", "1997 Apr 27 11:49:23") +-- ``` +-- 862156163 > +-- :echo strftime("%c", strptime("%y%m%d %T", "970427 11:53:55")) +-- < Sun Apr 27 11:53:55 1997 +-- ```vim +-- :echo strftime("%c", strptime("%Y%m%d%H%M%S", "19970427115355") + 3600) +-- ``` +-- Sun Apr 27 12:53:55 1997 +-- +-- Can also be used as a |method|: +-- ```vim +-- GetFormat()->strptime(timestring) +-- ``` +--- @return number +function vim.fn.strptime(format, timestring) end + +-- The result is a Number, which gives the byte index in +-- {haystack} of the last occurrence of the String {needle}. +-- When {start} is specified, matches beyond this index are +-- ignored. This can be used to find a match before a previous +-- match: +-- ```vim +-- :let lastcomma = strridx(line, ",") +-- :let comma2 = strridx(line, ",", lastcomma - 1) +-- ``` +-- The search is done case-sensitive. +-- For pattern searches use |match()|. +-- -1 is returned if the {needle} does not occur in {haystack}. +-- If the {needle} is empty the length of {haystack} is returned. +-- See also |stridx()|. Examples: +-- ```vim +-- :echo strridx("an angry armadillo", "an") 3 +-- ``` +-- When used with a single character it works similar to the C +-- function strrchr(). +-- +-- Can also be used as a |method|: +-- ```vim +-- GetHaystack()->strridx(needle) +-- ``` +--- @param start? number +--- @return number +function vim.fn.strridx(haystack, needle, start) end + +-- The result is a String, which is {string} with all unprintable +-- characters translated into printable characters |'isprint'|. +-- Like they are shown in a window. Example: +-- ```vim +-- echo strtrans(@a) +-- ``` +-- This displays a newline in register a as "^@" instead of +-- starting a new line. +-- +-- Returns an empty string on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetString()->strtrans() +-- ``` +--- @param string string +--- @return string +function vim.fn.strtrans(string) end + +-- The result is a Number, which is the number of display cells +-- String {string} occupies. A Tab character is counted as one +-- cell, alternatively use |strdisplaywidth()|. +-- When {string} contains characters with East Asian Width Class +-- Ambiguous, this function's return value depends on 'ambiwidth'. +-- Returns zero on error. +-- Also see |strlen()|, |strdisplaywidth()| and |strchars()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetString()->strwidth() +-- ``` +--- @param string string +--- @return number +function vim.fn.strwidth(string) end + +-- Only for an expression in a |:substitute| command or +-- substitute() function. +-- Returns the {nr}th submatch of the matched text. When {nr} +-- is 0 the whole matched text is returned. +-- Note that a NL in the string can stand for a line break of a +-- multi-line match or a NUL character in the text. +-- Also see |sub-replace-expression|. +-- +-- If {list} is present and non-zero then submatch() returns +-- a list of strings, similar to |getline()| with two arguments. +-- NL characters in the text represent NUL characters in the +-- text. +-- Only returns more than one item for |:substitute|, inside +-- |substitute()| this list will always contain one or zero +-- items, since there are no real line breaks. +-- +-- When substitute() is used recursively only the submatches in +-- the current (deepest) call can be obtained. +-- +-- Returns an empty string or list on error. +-- +-- Examples: +-- ```vim +-- :s/\d\+/\=submatch(0) + 1/ +-- :echo substitute(text, '\d\+', '\=submatch(0) + 1', '') +-- ``` +-- This finds the first number in the line and adds one to it. +-- A line break is included as a newline character. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetNr()->submatch() +-- ``` +--- @param nr number +--- @param list? any[] +--- @return string +function vim.fn.submatch(nr, list) end + +-- The result is a String, which is a copy of {string}, in which +-- the first match of {pat} is replaced with {sub}. +-- When {flags} is "g", all matches of {pat} in {string} are +-- replaced. Otherwise {flags} should be "". +-- +-- This works like the ":substitute" command (without any flags). +-- But the matching with {pat} is always done like the 'magic' +-- option is set and 'cpoptions' is empty (to make scripts +-- portable). 'ignorecase' is still relevant, use |/\c| or |/\C| +-- if you want to ignore or match case and ignore 'ignorecase'. +-- 'smartcase' is not used. See |string-match| for how {pat} is +-- used. +-- +-- A "~" in {sub} is not replaced with the previous {sub}. +-- Note that some codes in {sub} have a special meaning +-- |sub-replace-special|. For example, to replace something with +-- "\n" (two characters), use "\\\\n" or '\\n'. +-- +-- When {pat} does not match in {string}, {string} is returned +-- unmodified. +-- +-- Example: +-- ```vim +-- :let &path = substitute(&path, ",\\=[^,]*$", "", "") +-- ``` +-- This removes the last component of the 'path' option. > +-- :echo substitute("testing", ".*", "\\U\\0", "") +-- < results in "TESTING". +-- +-- When {sub} starts with "\=", the remainder is interpreted as +-- an expression. See |sub-replace-expression|. Example: +-- ```vim +-- :echo substitute(s, '%\(\x\x\)', +-- \ '\=nr2char("0x" .. submatch(1))', 'g') +-- +-- ``` +-- When {sub} is a Funcref that function is called, with one +-- optional argument. Example: +-- ```vim +-- :echo substitute(s, '%\(\x\x\)', SubNr, 'g') +-- ``` +-- The optional argument is a list which contains the whole +-- matched string and up to nine submatches, like what +-- |submatch()| returns. Example: +-- ```vim +-- :echo substitute(s, '%\(\x\x\)', {m -> '0x' .. m[1]}, 'g') +-- +-- ``` +-- Returns an empty string on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetString()->substitute(pat, sub, flags) +-- ``` +--- @param string string +--- @return string +function vim.fn.substitute(string, pat, sub, flags) end + +-- The result is a dictionary, which holds information about the +-- swapfile {fname}. The available fields are: +-- version Vim version +-- user user name +-- host host name +-- fname original file name +-- pid PID of the Vim process that created the swap +-- file +-- mtime last modification time in seconds +-- inode Optional: INODE number of the file +-- dirty 1 if file was modified, 0 if not +-- In case of failure an "error" item is added with the reason: +-- Cannot open file: file not found or in accessible +-- Cannot read file: cannot read first block +-- Not a swap file: does not contain correct block ID +-- Magic number mismatch: Info in first block is invalid +-- +-- Can also be used as a |method|: +-- ```vim +-- GetFilename()->swapinfo() +-- ``` +--- @return table +function vim.fn.swapinfo(fname) end + +-- The result is the swap file path of the buffer {buf}. +-- For the use of {buf}, see |bufname()| above. +-- If buffer {buf} is the current buffer, the result is equal to +-- |:swapname| (unless there is no swap file). +-- If buffer {buf} has no swap file, returns an empty string. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetBufname()->swapname() +-- ``` +--- @param buf buffer +--- @return string +function vim.fn.swapname(buf) end + +-- The result is a Number, which is the syntax ID at the position +-- {lnum} and {col} in the current window. +-- The syntax ID can be used with |synIDattr()| and +-- |synIDtrans()| to obtain syntax information about text. +-- +-- {col} is 1 for the leftmost column, {lnum} is 1 for the first +-- line. 'synmaxcol' applies, in a longer line zero is returned. +-- Note that when the position is after the last character, +-- that's where the cursor can be in Insert mode, synID() returns +-- zero. {lnum} is used like with |getline()|. +-- +-- When {trans} is |TRUE|, transparent items are reduced to the +-- item that they reveal. This is useful when wanting to know +-- the effective color. When {trans} is |FALSE|, the transparent +-- item is returned. This is useful when wanting to know which +-- syntax item is effective (e.g. inside parens). +-- Warning: This function can be very slow. Best speed is +-- obtained by going through the file in forward direction. +-- +-- Returns zero on error. +-- +-- Example (echoes the name of the syntax item under the cursor): +-- ```vim +-- :echo synIDattr(synID(line("."), col("."), 1), "name") +-- ``` +--- @param lnum number +--- @param col number +--- @return number +function vim.fn.synID(lnum, col, trans) end + +-- The result is a String, which is the {what} attribute of +-- syntax ID {synID}. This can be used to obtain information +-- about a syntax item. +-- {mode} can be "gui" or "cterm", to get the attributes +-- for that mode. When {mode} is omitted, or an invalid value is +-- used, the attributes for the currently active highlighting are +-- used (GUI or cterm). +-- Use synIDtrans() to follow linked highlight groups. +-- {what} result +-- "name" the name of the syntax item +-- "fg" foreground color (GUI: color name used to set +-- the color, cterm: color number as a string, +-- term: empty string) +-- "bg" background color (as with "fg") +-- "font" font name (only available in the GUI) +-- |highlight-font| +-- "sp" special color (as with "fg") |guisp| +-- "fg#" like "fg", but for the GUI and the GUI is +-- running the name in "#RRGGBB" form +-- "bg#" like "fg#" for "bg" +-- "sp#" like "fg#" for "sp" +-- "bold" "1" if bold +-- "italic" "1" if italic +-- "reverse" "1" if reverse +-- "inverse" "1" if inverse (= reverse) +-- "standout" "1" if standout +-- "underline" "1" if underlined +-- "undercurl" "1" if undercurled +-- "underdouble" "1" if double underlined +-- "underdotted" "1" if dotted underlined +-- "underdashed" "1" if dashed underlined +-- "strikethrough" "1" if struckthrough +-- "altfont" "1" if alternative font +-- "nocombine" "1" if nocombine +-- +-- Returns an empty string on error. +-- +-- Example (echoes the color of the syntax item under the +-- cursor): +-- ```vim +-- :echo synIDattr(synIDtrans(synID(line("."), col("."), 1)), "fg") +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- :echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg") +-- ``` +--- @param mode? any +--- @return string +function vim.fn.synIDattr(synID, what, mode) end + +-- The result is a Number, which is the translated syntax ID of +-- {synID}. This is the syntax group ID of what is being used to +-- highlight the character. Highlight links given with +-- ":highlight link" are followed. +-- +-- Returns zero on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- :echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg") +-- ``` +--- @return number +function vim.fn.synIDtrans(synID) end + +-- The result is a |List| with currently three items: +-- 1. The first item in the list is 0 if the character at the +-- position {lnum} and {col} is not part of a concealable +-- region, 1 if it is. {lnum} is used like with |getline()|. +-- 2. The second item in the list is a string. If the first item +-- is 1, the second item contains the text which will be +-- displayed in place of the concealed text, depending on the +-- current setting of 'conceallevel' and 'listchars'. +-- 3. The third and final item in the list is a number +-- representing the specific syntax region matched in the +-- line. When the character is not concealed the value is +-- zero. This allows detection of the beginning of a new +-- concealable region if there are two consecutive regions +-- with the same replacement character. For an example, if +-- the text is "123456" and both "23" and "45" are concealed +-- and replaced by the character "X", then: +-- call returns ~ +-- synconcealed(lnum, 1) [0, '', 0] +-- synconcealed(lnum, 2) [1, 'X', 1] +-- synconcealed(lnum, 3) [1, 'X', 1] +-- synconcealed(lnum, 4) [1, 'X', 2] +-- synconcealed(lnum, 5) [1, 'X', 2] +-- synconcealed(lnum, 6) [0, '', 0] +--- @param lnum number +--- @param col number +--- @return any[] +function vim.fn.synconcealed(lnum, col) end + +-- Return a |List|, which is the stack of syntax items at the +-- position {lnum} and {col} in the current window. {lnum} is +-- used like with |getline()|. Each item in the List is an ID +-- like what |synID()| returns. +-- The first item in the List is the outer region, following are +-- items contained in that one. The last one is what |synID()| +-- returns, unless not the whole item is highlighted or it is a +-- transparent item. +-- This function is useful for debugging a syntax file. +-- Example that shows the syntax stack under the cursor: +-- ```vim +-- for id in synstack(line("."), col(".")) +-- echo synIDattr(id, "name") +-- endfor +-- ``` +-- When the position specified with {lnum} and {col} is invalid +-- an empty list is returned. The position just after the last +-- character in a line and the first column in an empty line are +-- valid positions. +--- @param lnum number +--- @param col number +--- @return any[] +function vim.fn.synstack(lnum, col) end + +-- Gets the output of {cmd} as a |string| (|systemlist()| returns +-- a |List|) and sets |v:shell_error| to the error code. +-- {cmd} is treated as in |jobstart()|: +-- If {cmd} is a List it runs directly (no 'shell'). +-- If {cmd} is a String it runs in the 'shell', like this: +-- ```vim +-- :call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}']) +-- +-- ``` +-- Not to be used for interactive commands. +-- +-- Result is a String, filtered to avoid platform-specific quirks: +-- - is replaced with +-- :echo system(['ls', expand('%:h')]) +-- +-- ``` +-- If {input} is a string it is written to a pipe and passed as +-- stdin to the command. The string is written as-is, line +-- separators are not changed. +-- If {input} is a |List| it is written to the pipe as +-- |writefile()| does with {binary} set to "b" (i.e. with +-- a newline between each list item, and newlines inside list +-- items converted to NULs). +-- When {input} is given and is a valid buffer id, the content of +-- the buffer is written to the file line by line, each line +-- terminated by NL (and NUL where the text has NL). +-- +-- Note: system() cannot write to or read from backgrounded ("&") +-- shell commands, e.g.: +-- ```vim +-- :echo system("cat - &", "foo") +-- ``` +-- which is equivalent to: > +-- $ echo foo | bash -c 'cat - &' +-- < The pipes are disconnected (unless overridden by shell +-- redirection syntax) before input can reach it. Use +-- |jobstart()| instead. +-- +-- Note: Use |shellescape()| or |::S| with |expand()| or +-- |fnamemodify()| to escape special characters in a command +-- argument. 'shellquote' and 'shellxquote' must be properly +-- configured. Example: +-- ```vim +-- :echo system('ls '..shellescape(expand('%:h'))) +-- :echo system('ls '..expand('%:h:S')) +-- +-- ``` +-- Unlike ":!cmd" there is no automatic check for changed files. +-- Use |:checktime| to force a check. +-- +-- Can also be used as a |method|: +-- ```vim +-- :echo GetCmd()->system() +-- ``` +--- @param input? any +--- @return string +function vim.fn.system(cmd, input) end + +-- Same as |system()|, but returns a |List| with lines (parts of +-- output separated by NL) with NULs transformed into NLs. Output +-- is the same as |readfile()| will output with {binary} argument +-- set to "b", except that a final newline is not preserved, +-- unless {keepempty} is non-zero. +-- Note that on MS-Windows you may get trailing CR characters. +-- +-- To see the difference between "echo hello" and "echo -n hello" +-- use |system()| and |split()|: +-- ```vim +-- echo split(system('echo hello'), '\n', 1) +-- ``` +-- Returns an empty string on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- :echo GetCmd()->systemlist() +-- ``` +--- @param input? any +--- @param keepempty? any +--- @return any[] +function vim.fn.systemlist(cmd, input, keepempty) end + +-- The result is a |List|, where each item is the number of the +-- buffer associated with each window in the current tab page. +-- {arg} specifies the number of the tab page to be used. When +-- omitted the current tab page is used. +-- When {arg} is invalid the number zero is returned. +-- To get a list of all buffers in all tabs use this: +-- ```vim +-- let buflist = [] +-- for i in range(tabpagenr('$')) +-- call extend(buflist, tabpagebuflist(i + 1)) +-- endfor +-- ``` +-- Note that a buffer may appear in more than one window. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetTabpage()->tabpagebuflist() +-- ``` +--- @param arg? any +--- @return any[] +function vim.fn.tabpagebuflist(arg) end + +-- The result is a Number, which is the number of the current +-- tab page. The first tab page has number 1. +-- +-- The optional argument {arg} supports the following values: +-- $ the number of the last tab page (the tab page +-- count). +-- # the number of the last accessed tab page +-- (where |g| goes to). If there is no +-- previous tab page, 0 is returned. +-- The number can be used with the |:tab| command. +-- +-- Returns zero on error. +--- @param arg? any +--- @return number +function vim.fn.tabpagenr(arg) end + +-- Like |winnr()| but for tab page {tabarg}. +-- {tabarg} specifies the number of tab page to be used. +-- {arg} is used like with |winnr()|: +-- - When omitted the current window number is returned. This is +-- the window which will be used when going to this tab page. +-- - When "$" the number of windows is returned. +-- - When "#" the previous window nr is returned. +-- Useful examples: +-- ```vim +-- tabpagewinnr(1) " current window of tab page 1 +-- tabpagewinnr(4, '$') " number of windows in tab page 4 +-- ``` +-- When {tabarg} is invalid zero is returned. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetTabpage()->tabpagewinnr() +-- ``` +--- @param arg? any +--- @return number +function vim.fn.tabpagewinnr(tabarg, arg) end + +-- Returns a |List| with the file names used to search for tags +-- for the current buffer. This is the 'tags' option expanded. +--- @return any[] +function vim.fn.tagfiles() end + +-- Returns a |List| of tags matching the regular expression {expr}. +-- +-- If {filename} is passed it is used to prioritize the results +-- in the same way that |:tselect| does. See |tag-priority|. +-- {filename} should be the full path of the file. +-- +-- Each list item is a dictionary with at least the following +-- entries: +-- name Name of the tag. +-- filename Name of the file where the tag is +-- defined. It is either relative to the +-- current directory or a full path. +-- cmd Ex command used to locate the tag in +-- the file. +-- kind Type of the tag. The value for this +-- entry depends on the language specific +-- kind values. Only available when +-- using a tags file generated by +-- Universal/Exuberant ctags or hdrtag. +-- static A file specific tag. Refer to +-- |static-tag| for more information. +-- More entries may be present, depending on the content of the +-- tags file: access, implementation, inherits and signature. +-- Refer to the ctags documentation for information about these +-- fields. For C code the fields "struct", "class" and "enum" +-- may appear, they give the name of the entity the tag is +-- contained in. +-- +-- The ex-command "cmd" can be either an ex search pattern, a +-- line number or a line number followed by a byte number. +-- +-- If there are no matching tags, then an empty list is returned. +-- +-- To get an exact tag match, the anchors '^' and '$' should be +-- used in {expr}. This also make the function work faster. +-- Refer to |tag-regexp| for more information about the tag +-- search regular expression pattern. +-- +-- Refer to |'tags'| for information about how the tags file is +-- located by Vim. Refer to |tags-file-format| for the format of +-- the tags file generated by the different ctags tools. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetTagpattern()->taglist() +-- ``` +--- @param filename? any +--- @return any[] +function vim.fn.taglist(expr, filename) end + +-- Return the tangent of {expr}, measured in radians, as a |Float| +-- in the range [-inf, inf]. +-- {expr} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo tan(10) +-- ``` +-- 0.648361 > +-- :echo tan(-4.01) +-- < -1.181502 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->tan() +-- ``` +--- @return float +function vim.fn.tan(expr) end + +-- Return the hyperbolic tangent of {expr} as a |Float| in the +-- range [-1, 1]. +-- {expr} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo tanh(0.5) +-- ``` +-- 0.462117 > +-- :echo tanh(-1) +-- < -0.761594 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->tanh() +-- ``` +--- @return float +function vim.fn.tanh(expr) end + +-- Generates a (non-existent) filename located in the Nvim root +-- |tempdir|. Scripts can use the filename as a temporary file. +-- Example: +-- ```vim +-- :let tmpfile = tempname() +-- :exe "redir > " .. tmpfile +-- ``` +--- @return string +function vim.fn.tempname() end + +-- Spawns {cmd} in a new pseudo-terminal session connected +-- to the current (unmodified) buffer. Parameters and behavior +-- are the same as |jobstart()| except "pty", "width", "height", +-- and "TERM" are ignored: "height" and "width" are taken from +-- the current window. +-- Returns the same values as |jobstart()|. +-- +-- Terminal environment is initialized as in ||jobstart-env|, +-- except $TERM is set to "xterm-256color". Full behavior is +-- described in |terminal|. +--- @param opts? table +function vim.fn.termopen(cmd, opts) end + +-- none free memory right now for testing +function vim.fn.test_garbagecollect_now() end + +-- Return a list with information about timers. +-- When {id} is given only information about this timer is +-- returned. When timer {id} does not exist an empty list is +-- returned. +-- When {id} is omitted information about all timers is returned. +-- +-- For each timer the information is stored in a |Dictionary| with +-- these items: +-- "id" the timer ID +-- "time" time the timer was started with +-- "repeat" number of times the timer will still fire; +-- -1 means forever +-- "callback" the callback +-- +-- Can also be used as a |method|: +-- ```vim +-- GetTimer()->timer_info() +-- ``` +--- @param id? any +--- @return any[] +function vim.fn.timer_info(id) end + +-- Pause or unpause a timer. A paused timer does not invoke its +-- callback when its time expires. Unpausing a timer may cause +-- the callback to be invoked almost immediately if enough time +-- has passed. +-- +-- Pausing a timer is useful to avoid the callback to be called +-- for a short time. +-- +-- If {paused} evaluates to a non-zero Number or a non-empty +-- String, then the timer is paused, otherwise it is unpaused. +-- See |non-zero-arg|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetTimer()->timer_pause(1) +-- ``` +function vim.fn.timer_pause(timer, paused) end + +-- Create a timer and return the timer ID. +-- +-- {time} is the waiting time in milliseconds. This is the +-- minimum time before invoking the callback. When the system is +-- busy or Vim is not waiting for input the time will be longer. +-- Zero can be used to execute the callback when Vim is back in +-- the main loop. +-- +-- {callback} is the function to call. It can be the name of a +-- function or a |Funcref|. It is called with one argument, which +-- is the timer ID. The callback is only invoked when Vim is +-- waiting for input. +-- +-- {options} is a dictionary. Supported entries: +-- "repeat" Number of times to repeat the callback. +-- -1 means forever. Default is 1. +-- If the timer causes an error three times in a +-- row the repeat is cancelled. +-- +-- Returns -1 on error. +-- +-- Example: +-- ```vim +-- func MyHandler(timer) +-- echo 'Handler called' +-- endfunc +-- let timer = timer_start(500, 'MyHandler', +-- \ {'repeat': 3}) +-- ``` +-- This invokes MyHandler() three times at 500 msec intervals. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetMsec()->timer_start(callback) +-- +-- ``` +-- Not available in the |sandbox|. +--- @param callback fun() +--- @param options? table +--- @return number +function vim.fn.timer_start(time, callback, options) end + +-- Stop a timer. The timer callback will no longer be invoked. +-- {timer} is an ID returned by timer_start(), thus it must be a +-- Number. If {timer} does not exist there is no error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetTimer()->timer_stop() +-- ``` +function vim.fn.timer_stop(timer) end + +-- Stop all timers. The timer callbacks will no longer be +-- invoked. Useful if some timers is misbehaving. If there are +-- no timers there is no error. +function vim.fn.timer_stopall() end + +-- The result is a copy of the String given, with all uppercase +-- characters turned into lowercase (just like applying |gu| to +-- the string). Returns an empty string on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->tolower() +-- ``` +--- @return string +function vim.fn.tolower(expr) end + +-- The result is a copy of the String given, with all lowercase +-- characters turned into uppercase (just like applying |gU| to +-- the string). Returns an empty string on error. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->toupper() +-- ``` +--- @return string +function vim.fn.toupper(expr) end + +-- The result is a copy of the {src} string with all characters +-- which appear in {fromstr} replaced by the character in that +-- position in the {tostr} string. Thus the first character in +-- {fromstr} is translated into the first character in {tostr} +-- and so on. Exactly like the unix "tr" command. +-- This code also deals with multibyte characters properly. +-- +-- Returns an empty string on error. +-- +-- Examples: +-- ```vim +-- echo tr("hello there", "ht", "HT") +-- ``` +-- returns "Hello THere" > +-- echo tr("", "<>", "{}") +-- < returns "{blob}" +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->tr(from, to) +-- ``` +--- @return string +function vim.fn.tr(src, fromstr, tostr) end + +-- Return {text} as a String where any character in {mask} is +-- removed from the beginning and/or end of {text}. +-- If {mask} is not given, {mask} is all characters up to 0x20, +-- which includes Tab, space, NL and CR, plus the non-breaking +-- space character 0xa0. +-- The optional {dir} argument specifies where to remove the +-- characters: +-- 0 remove from the beginning and end of {text} +-- 1 remove only at the beginning of {text} +-- 2 remove only at the end of {text} +-- When omitted both ends are trimmed. +-- This function deals with multibyte characters properly. +-- Returns an empty string on error. +-- +-- Examples: +-- ```vim +-- echo trim(" some text ") +-- ``` +-- returns "some text" > +-- echo trim(" \r\t\t\r RESERVE \t\n\x0B\xA0") .. "_TAIL" +-- < returns "RESERVE_TAIL" +-- ```vim +-- echo trim("rmX>rrm", "rm<>") +-- ``` +-- returns "Xrm>X" (characters in the middle are not removed) > +-- echo trim(" vim ", " ", 2) +-- < returns " vim" +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->trim() +-- ``` +--- @param text string +--- @param mask? any +--- @param dir? any +--- @return string +function vim.fn.trim(text, mask, dir) end + +-- Return the largest integral value with magnitude less than or +-- equal to {expr} as a |Float| (truncate towards zero). +-- {expr} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- echo trunc(1.456) +-- ``` +-- 1.0 > +-- echo trunc(-5.456) +-- < -5.0 +-- ```vim +-- echo trunc(4.0) +-- ``` +-- 4.0 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->trunc() +-- ``` +--- @return float +function vim.fn.trunc(expr) end + +-- The result is a Number representing the type of {expr}. +-- Instead of using the number directly, it is better to use the +-- v:t_ variable that has the value: +-- Number: 0 (|v:t_number|) +-- String: 1 (|v:t_string|) +-- Funcref: 2 (|v:t_func|) +-- List: 3 (|v:t_list|) +-- Dictionary: 4 (|v:t_dict|) +-- Float: 5 (|v:t_float|) +-- Boolean: 6 (|v:true| and |v:false|) +-- Null: 7 (|v:null|) +-- Blob: 10 (|v:t_blob|) +-- For backward compatibility, this method can be used: +-- ```vim +-- :if type(myvar) == type(0) +-- :if type(myvar) == type("") +-- :if type(myvar) == type(function("tr")) +-- :if type(myvar) == type([]) +-- :if type(myvar) == type({}) +-- :if type(myvar) == type(0.0) +-- :if type(myvar) == type(v:true) +-- ``` +-- In place of checking for |v:null| type it is better to check +-- for |v:null| directly as it is the only value of this type: +-- ```vim +-- :if myvar is v:null +-- ``` +-- To check if the v:t_ variables exist use this: > +-- :if exists('v:t_number') +-- +-- < Can also be used as a |method|: +-- ```vim +-- mylist->type() +-- ``` +--- @return number +function vim.fn.type(expr) end + +-- Return the name of the undo file that would be used for a file +-- with name {name} when writing. This uses the 'undodir' +-- option, finding directories that exist. It does not check if +-- the undo file exists. +-- {name} is always expanded to the full path, since that is what +-- is used internally. +-- If {name} is empty undofile() returns an empty string, since a +-- buffer without a file name will not write an undo file. +-- Useful in combination with |:wundo| and |:rundo|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetFilename()->undofile() +-- ``` +--- @return string +function vim.fn.undofile(name) end + +-- Return the current state of the undo tree in a dictionary with +-- the following items: +-- "seq_last" The highest undo sequence number used. +-- "seq_cur" The sequence number of the current position in +-- the undo tree. This differs from "seq_last" +-- when some changes were undone. +-- "time_cur" Time last used for |:earlier| and related +-- commands. Use |strftime()| to convert to +-- something readable. +-- "save_last" Number of the last file write. Zero when no +-- write yet. +-- "save_cur" Number of the current position in the undo +-- tree. +-- "synced" Non-zero when the last undo block was synced. +-- This happens when waiting from input from the +-- user. See |undo-blocks|. +-- "entries" A list of dictionaries with information about +-- undo blocks. +-- +-- The first item in the "entries" list is the oldest undo item. +-- Each List item is a |Dictionary| with these items: +-- "seq" Undo sequence number. Same as what appears in +-- |:undolist|. +-- "time" Timestamp when the change happened. Use +-- |strftime()| to convert to something readable. +-- "newhead" Only appears in the item that is the last one +-- that was added. This marks the last change +-- and where further changes will be added. +-- "curhead" Only appears in the item that is the last one +-- that was undone. This marks the current +-- position in the undo tree, the block that will +-- be used by a redo command. When nothing was +-- undone after the last change this item will +-- not appear anywhere. +-- "save" Only appears on the last block before a file +-- write. The number is the write count. The +-- first write has number 1, the last one the +-- "save_last" mentioned above. +-- "alt" Alternate entry. This is again a List of undo +-- blocks. Each item may again have an "alt" +-- item. +--- @return any[] +function vim.fn.undotree() end + +-- Remove second and succeeding copies of repeated adjacent +-- {list} items in-place. Returns {list}. If you want a list +-- to remain unmodified make a copy first: +-- ```vim +-- :let newlist = uniq(copy(mylist)) +-- ``` +-- The default compare function uses the string representation of +-- each item. For the use of {func} and {dict} see |sort()|. +-- +-- Returns zero if {list} is not a |List|. +-- +-- Can also be used as a |method|: +-- ```vim +-- mylist->uniq() +-- ``` +--- @param list any[] +--- @param func? fun() +--- @param dict? table +--- @return any[] +function vim.fn.uniq(list, func, dict) end + +-- Return a |List| with all the values of {dict}. The |List| is +-- in arbitrary order. Also see |items()| and |keys()|. +-- Returns zero if {dict} is not a |Dict|. +-- +-- Can also be used as a |method|: +-- ```vim +-- mydict->values() +-- ``` +--- @param dict table +--- @return any[] +function vim.fn.values(dict) end + +-- The result is a Number, which is the screen column of the file +-- position given with {expr}. That is, the last screen position +-- occupied by the character at that position, when the screen +-- would be of unlimited width. When there is a at the +-- position, the returned Number will be the column at the end of +-- the . For example, for a in column 1, with 'ts' +-- set to 8, it returns 8. |conceal| is ignored. +-- For the byte position use |col()|. +-- For the use of {expr} see |col()|. +-- When 'virtualedit' is used {expr} can be [lnum, col, off], where +-- "off" is the offset in screen columns from the start of the +-- character. E.g., a position within a or after the last +-- character. When "off" is omitted zero is used. +-- When Virtual editing is active in the current mode, a position +-- beyond the end of the line can be returned. |'virtualedit'| +-- The accepted positions are: +-- . the cursor position +-- $ the end of the cursor line (the result is the +-- number of displayed characters in the cursor line +-- plus one) +-- 'x position of mark x (if the mark is not set, 0 is +-- returned) +-- v In Visual mode: the start of the Visual area (the +-- cursor is the end). When not in Visual mode +-- returns the cursor position. Differs from |'<| in +-- that it's updated right away. +-- Note that only marks in the current file can be used. +-- Examples: +-- ```vim +-- virtcol(".") with text "foo^Lbar", with cursor on the "^L", returns 5 +-- virtcol("$") with text "foo^Lbar", returns 9 +-- virtcol("'t") with text " there", with 't at 'h', returns 6 +-- ``` +-- The first column is 1. 0 is returned for an error. +-- A more advanced example that echoes the maximum length of +-- all lines: +-- ```vim +-- echo max(map(range(1, line('$')), "virtcol([v:val, '$'])")) +-- +-- ``` +-- Can also be used as a |method|: > +-- GetPos()->virtcol() +--- @return number +function vim.fn.virtcol(expr) end + +-- The result is a Number, which is the byte index of the +-- character in window {winid} at buffer line {lnum} and virtual +-- column {col}. +-- +-- If {col} is greater than the last virtual column in line +-- {lnum}, then the byte index of the character at the last +-- virtual column is returned. +-- +-- The {winid} argument can be the window number or the +-- |window-ID|. If this is zero, then the current window is used. +-- +-- Returns -1 if the window {winid} doesn't exist or the buffer +-- line {lnum} or virtual column {col} is invalid. +-- +-- See also |screenpos()|, |virtcol()| and |col()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinid()->virtcol2col(lnum, col) +-- ``` +--- @param winid window +--- @param lnum number +--- @param col number +--- @return number +function vim.fn.virtcol2col(winid, lnum, col) end + +-- The result is a String, which describes the last Visual mode +-- used in the current buffer. Initially it returns an empty +-- string, but once Visual mode has been used, it returns "v", +-- "V", or "" (a single CTRL-V character) for +-- character-wise, line-wise, or block-wise Visual mode +-- respectively. +-- Example: +-- ```vim +-- :exe "normal " .. visualmode() +-- ``` +-- This enters the same Visual mode as before. It is also useful +-- in scripts if you wish to act differently depending on the +-- Visual mode that was used. +-- If Visual mode is active, use |mode()| to get the Visual mode +-- (e.g., in a |:vmap|). +-- If {expr} is supplied and it evaluates to a non-zero Number or +-- a non-empty String, then the Visual mode will be cleared and +-- the old value is returned. See |non-zero-arg|. +--- @param expr? any +--- @return string +function vim.fn.visualmode(expr) end + +-- Waits until {condition} evaluates to |TRUE|, where {condition} +-- is a |Funcref| or |string| containing an expression. +-- +-- {timeout} is the maximum waiting time in milliseconds, -1 +-- means forever. +-- +-- Condition is evaluated on user events, internal events, and +-- every {interval} milliseconds (default: 200). +-- +-- Returns a status integer: +-- 0 if the condition was satisfied before timeout +-- -1 if the timeout was exceeded +-- -2 if the function was interrupted (by |CTRL-C|) +-- -3 if an error occurred +--- @param interval? any +--- @return number +function vim.fn.wait(timeout, condition, interval) end + +-- Returns |TRUE| when the wildmenu is active and |FALSE| +-- otherwise. See 'wildmenu' and 'wildmode'. +-- This can be used in mappings to handle the 'wildcharm' option +-- gracefully. (Makes only sense with |mapmode-c| mappings). +-- +-- For example to make work like in wildmode, use: +-- ```vim +-- :cnoremap wildmenumode() ? "\\" : "\" +-- ``` +-- (Note, this needs the 'wildcharm' option set appropriately). +--- @return number +function vim.fn.wildmenumode() end + +-- Like `execute()` but in the context of window {id}. +-- The window will temporarily be made the current window, +-- without triggering autocommands or changing directory. When +-- executing {command} autocommands will be triggered, this may +-- have unexpected side effects. Use |:noautocmd| if needed. +-- Example: +-- ```vim +-- call win_execute(winid, 'syntax enable') +-- ``` +-- Doing the same with `setwinvar()` would not trigger +-- autocommands and not actually show syntax highlighting. +-- +-- When window {id} does not exist then no error is given and +-- an empty string is returned. +-- +-- Can also be used as a |method|, the base is passed as the +-- second argument: +-- ```vim +-- GetCommand()->win_execute(winid) +-- ``` +--- @param silent? any +--- @return string +function vim.fn.win_execute(id, command, silent) end + +-- Returns a |List| with |window-ID|s for windows that contain +-- buffer {bufnr}. When there is none the list is empty. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetBufnr()->win_findbuf() +-- ``` +--- @param bufnr buffer +--- @return any[] +function vim.fn.win_findbuf(bufnr) end + +-- Get the |window-ID| for the specified window. +-- When {win} is missing use the current window. +-- With {win} this is the window number. The top window has +-- number 1. +-- Without {tab} use the current tab, otherwise the tab with +-- number {tab}. The first tab has number one. +-- Return zero if the window cannot be found. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinnr()->win_getid() +-- ``` +--- @param win? window +--- @param tab? any +--- @return number +function vim.fn.win_getid(win, tab) end + +-- Return the type of the window: +-- "autocmd" autocommand window. Temporary window +-- used to execute autocommands. +-- "command" command-line window |cmdwin| +-- (empty) normal window +-- "loclist" |location-list-window| +-- "popup" floating window |api-floatwin| +-- "preview" preview window |preview-window| +-- "quickfix" |quickfix-window| +-- "unknown" window {nr} not found +-- +-- When {nr} is omitted return the type of the current window. +-- When {nr} is given return the type of this window by number or +-- |window-ID|. +-- +-- Also see the 'buftype' option. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinid()->win_gettype() +-- ``` +--- @param nr? number +--- @return string +function vim.fn.win_gettype(nr) end + +-- Go to window with ID {expr}. This may also change the current +-- tabpage. +-- Return TRUE if successful, FALSE if the window cannot be found. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinid()->win_gotoid() +-- ``` +--- @return number +function vim.fn.win_gotoid(expr) end + +-- Return a list with the tab number and window number of window +-- with ID {expr}: [tabnr, winnr]. +-- Return [0, 0] if the window cannot be found. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinid()->win_id2tabwin() +-- ``` +--- @return any[] +function vim.fn.win_id2tabwin(expr) end + +-- Return the window number of window with ID {expr}. +-- Return 0 if the window cannot be found in the current tabpage. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinid()->win_id2win() +-- ``` +--- @return number +function vim.fn.win_id2win(expr) end + +-- Move window {nr}'s vertical separator (i.e., the right border) +-- by {offset} columns, as if being dragged by the mouse. {nr} +-- can be a window number or |window-ID|. A positive {offset} +-- moves right and a negative {offset} moves left. Moving a +-- window's vertical separator will change the width of the +-- window and the width of other windows adjacent to the vertical +-- separator. The magnitude of movement may be smaller than +-- specified (e.g., as a consequence of maintaining +-- 'winminwidth'). Returns TRUE if the window can be found and +-- FALSE otherwise. +-- This will fail for the rightmost window and a full-width +-- window, since it has no separator on the right. +-- Only works for the current tab page. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinnr()->win_move_separator(offset) +-- ``` +--- @param nr number +--- @return number +function vim.fn.win_move_separator(nr, offset) end + +-- Move window {nr}'s status line (i.e., the bottom border) by +-- {offset} rows, as if being dragged by the mouse. {nr} can be a +-- window number or |window-ID|. A positive {offset} moves down +-- and a negative {offset} moves up. Moving a window's status +-- line will change the height of the window and the height of +-- other windows adjacent to the status line. The magnitude of +-- movement may be smaller than specified (e.g., as a consequence +-- of maintaining 'winminheight'). Returns TRUE if the window can +-- be found and FALSE otherwise. +-- Only works for the current tab page. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinnr()->win_move_statusline(offset) +-- ``` +--- @param nr number +--- @return number +function vim.fn.win_move_statusline(nr, offset) end + +-- Return the screen position of window {nr} as a list with two +-- numbers: [row, col]. The first window always has position +-- [1, 1], unless there is a tabline, then it is [2, 1]. +-- {nr} can be the window number or the |window-ID|. Use zero +-- for the current window. +-- Returns [0, 0] if the window cannot be found in the current +-- tabpage. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinid()->win_screenpos() +-- ``` +--- @param nr number +--- @return any[] +function vim.fn.win_screenpos(nr) end + +-- Move the window {nr} to a new split of the window {target}. +-- This is similar to moving to {target}, creating a new window +-- using |:split| but having the same contents as window {nr}, and +-- then closing {nr}. +-- +-- Both {nr} and {target} can be window numbers or |window-ID|s. +-- Both must be in the current tab page. +-- +-- Returns zero for success, non-zero for failure. +-- +-- {options} is a |Dictionary| with the following optional entries: +-- "vertical" When TRUE, the split is created vertically, +-- like with |:vsplit|. +-- "rightbelow" When TRUE, the split is made below or to the +-- right (if vertical). When FALSE, it is done +-- above or to the left (if vertical). When not +-- present, the values of 'splitbelow' and +-- 'splitright' are used. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinid()->win_splitmove(target) +-- ``` +--- @param nr number +--- @param options? table +--- @return number +function vim.fn.win_splitmove(nr, target, options) end + +-- The result is a Number, which is the number of the buffer +-- associated with window {nr}. {nr} can be the window number or +-- the |window-ID|. +-- When {nr} is zero, the number of the buffer in the current +-- window is returned. +-- When window {nr} doesn't exist, -1 is returned. +-- Example: +-- ```vim +-- :echo "The file in the current window is " .. bufname(winbufnr(0)) +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- FindWindow()->winbufnr()->bufname() +-- ``` +--- @param nr number +--- @return number +function vim.fn.winbufnr(nr) end + +-- The result is a Number, which is the virtual column of the +-- cursor in the window. This is counting screen cells from the +-- left side of the window. The leftmost column is one. +--- @return number +function vim.fn.wincol() end + +-- The result is a String. For MS-Windows it indicates the OS +-- version. E.g, Windows 10 is "10.0", Windows 8 is "6.2", +-- Windows XP is "5.1". For non-MS-Windows systems the result is +-- an empty string. +--- @return string +function vim.fn.windowsversion() end + +-- The result is a Number, which is the height of window {nr}. +-- {nr} can be the window number or the |window-ID|. +-- When {nr} is zero, the height of the current window is +-- returned. When window {nr} doesn't exist, -1 is returned. +-- An existing window always has a height of zero or more. +-- This excludes any window toolbar line. +-- Examples: +-- ```vim +-- :echo "The current window has " .. winheight(0) .. " lines." +-- +-- ``` +-- Can also be used as a |method|: > +-- GetWinid()->winheight() +-- < +--- @param nr number +--- @return number +function vim.fn.winheight(nr) end + +-- The result is a nested List containing the layout of windows +-- in a tabpage. +-- +-- Without {tabnr} use the current tabpage, otherwise the tabpage +-- with number {tabnr}. If the tabpage {tabnr} is not found, +-- returns an empty list. +-- +-- For a leaf window, it returns: +-- ["leaf", {winid}] +-- For horizontally split windows, which form a column, it +-- returns: +-- ["col", [{nested list of windows}]] +-- For vertically split windows, which form a row, it returns: +-- ["row", [{nested list of windows}]] +-- +-- Example: +-- ```vim +-- " Only one window in the tab page +-- :echo winlayout() +-- ['leaf', 1000] +-- " Two horizontally split windows +-- :echo winlayout() +-- ['col', [['leaf', 1000], ['leaf', 1001]]] +-- " The second tab page, with three horizontally split +-- " windows, with two vertically split windows in the +-- " middle window +-- :echo winlayout(2) +-- ['col', [['leaf', 1002], ['row', [['leaf', 1003], +-- ['leaf', 1001]]], ['leaf', 1000]]] +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- GetTabnr()->winlayout() +-- ``` +--- @param tabnr? number +--- @return any[] +function vim.fn.winlayout(tabnr) end + +-- The result is a Number, which is the screen line of the cursor +-- in the window. This is counting screen lines from the top of +-- the window. The first line is one. +-- If the cursor was moved the view on the file will be updated +-- first, this may cause a scroll. +--- @return number +function vim.fn.winline() end + +-- The result is a Number, which is the number of the current +-- window. The top window has number 1. +-- Returns zero for a popup window. +-- +-- The optional argument {arg} supports the following values: +-- $ the number of the last window (the window +-- count). +-- # the number of the last accessed window (where +-- |CTRL-W_p| goes to). If there is no previous +-- window or it is in another tab page 0 is +-- returned. +-- {N}j the number of the Nth window below the +-- current window (where |CTRL-W_j| goes to). +-- {N}k the number of the Nth window above the current +-- window (where |CTRL-W_k| goes to). +-- {N}h the number of the Nth window left of the +-- current window (where |CTRL-W_h| goes to). +-- {N}l the number of the Nth window right of the +-- current window (where |CTRL-W_l| goes to). +-- The number can be used with |CTRL-W_w| and ":wincmd w" +-- |:wincmd|. +-- When {arg} is invalid an error is given and zero is returned. +-- Also see |tabpagewinnr()| and |win_getid()|. +-- Examples: +-- ```vim +-- let window_count = winnr('$') +-- let prev_window = winnr('#') +-- let wnum = winnr('3k') +-- +-- ``` +-- Can also be used as a |method|: > +-- GetWinval()->winnr() +-- < +--- @param arg? any +--- @return number +function vim.fn.winnr(arg) end + +-- Returns a sequence of |:resize| commands that should restore +-- the current window sizes. Only works properly when no windows +-- are opened or closed and the current window and tab page is +-- unchanged. +-- Example: +-- ```vim +-- :let cmd = winrestcmd() +-- :call MessWithWindowSizes() +-- :exe cmd +-- ``` +--- @return string +function vim.fn.winrestcmd() end + +-- Uses the |Dictionary| returned by |winsaveview()| to restore +-- the view of the current window. +-- Note: The {dict} does not have to contain all values, that are +-- returned by |winsaveview()|. If values are missing, those +-- settings won't be restored. So you can use: +-- ```vim +-- :call winrestview({'curswant': 4}) +-- ``` +-- This will only set the curswant value (the column the cursor +-- wants to move on vertical movements) of the cursor to column 5 +-- (yes, that is 5), while all other settings will remain the +-- same. This is useful, if you set the cursor position manually. +-- +-- If you have changed the values the result is unpredictable. +-- If the window size changed the result won't be the same. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetView()->winrestview() +-- ``` +--- @param dict table +function vim.fn.winrestview(dict) end + +-- Returns a |Dictionary| that contains information to restore +-- the view of the current window. Use |winrestview()| to +-- restore the view. +-- This is useful if you have a mapping that jumps around in the +-- buffer and you want to go back to the original view. +-- This does not save fold information. Use the 'foldenable' +-- option to temporarily switch off folding, so that folds are +-- not opened when moving around. This may have side effects. +-- The return value includes: +-- lnum cursor line number +-- col cursor column (Note: the first column +-- zero, as opposed to what |getcurpos()| +-- returns) +-- coladd cursor column offset for 'virtualedit' +-- curswant column for vertical movement (Note: +-- the first column is zero, as opposed +-- to what |getcurpos()| returns). After +-- |$| command it will be a very large +-- number equal to |v:maxcol|. +-- topline first line in the window +-- topfill filler lines, only in diff mode +-- leftcol first column displayed; only used when +-- 'wrap' is off +-- skipcol columns skipped +-- Note that no option values are saved. +--- @return table +function vim.fn.winsaveview() end + +-- The result is a Number, which is the width of window {nr}. +-- {nr} can be the window number or the |window-ID|. +-- When {nr} is zero, the width of the current window is +-- returned. When window {nr} doesn't exist, -1 is returned. +-- An existing window always has a width of zero or more. +-- Examples: +-- ```vim +-- :echo "The current window has " .. winwidth(0) .. " columns." +-- :if winwidth(0) <= 50 +-- : 50 wincmd | +-- :endif +-- ``` +-- For getting the terminal or screen size, see the 'columns' +-- option. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWinid()->winwidth() +-- ``` +--- @param nr number +--- @return number +function vim.fn.winwidth(nr) end + +-- The result is a dictionary of byte/chars/word statistics for +-- the current buffer. This is the same info as provided by +-- |g_CTRL-G| +-- The return value includes: +-- bytes Number of bytes in the buffer +-- chars Number of chars in the buffer +-- words Number of words in the buffer +-- cursor_bytes Number of bytes before cursor position +-- (not in Visual mode) +-- cursor_chars Number of chars before cursor position +-- (not in Visual mode) +-- cursor_words Number of words before cursor position +-- (not in Visual mode) +-- visual_bytes Number of bytes visually selected +-- (only in Visual mode) +-- visual_chars Number of chars visually selected +-- (only in Visual mode) +-- visual_words Number of words visually selected +-- (only in Visual mode) +--- @return table +function vim.fn.wordcount() end + +-- When {object} is a |List| write it to file {fname}. Each list +-- item is separated with a NL. Each list item must be a String +-- or Number. +-- When {flags} contains "b" then binary mode is used: There will +-- not be a NL after the last list item. An empty item at the +-- end does cause the last line in the file to end in a NL. +-- +-- When {object} is a |Blob| write the bytes to file {fname} +-- unmodified. +-- +-- When {flags} contains "a" then append mode is used, lines are +-- appended to the file: +-- ```vim +-- :call writefile(["foo"], "event.log", "a") +-- :call writefile(["bar"], "event.log", "a") +-- ``` +-- When {flags} contains "S" fsync() call is not used, with "s" +-- it is used, 'fsync' option applies by default. No fsync() +-- means that writefile() will finish faster, but writes may be +-- left in OS buffers and not yet written to disk. Such changes +-- will disappear if system crashes before OS does writing. +-- +-- All NL characters are replaced with a NUL character. +-- Inserting CR characters needs to be done before passing {list} +-- to writefile(). +-- An existing file is overwritten, if possible. +-- When the write fails -1 is returned, otherwise 0. There is an +-- error message if the file can't be created or when writing +-- fails. +-- Also see |readfile()|. +-- To copy a file byte for byte: +-- ```vim +-- :let fl = readfile("foo", "b") +-- :call writefile(fl, "foocopy", "b") +-- +-- ``` +-- Can also be used as a |method|: > +-- GetText()->writefile("thefile") +--- @param flags? any +--- @return number +function vim.fn.writefile(object, fname, flags) end + +-- Bitwise XOR on the two arguments. The arguments are converted +-- to a number. A List, Dict or Float argument causes an error. +-- Also see `and()` and `or()`. +-- Example: +-- ```vim +-- :let bits = xor(bits, 0x80) +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- :let bits = bits->xor(0x80) +-- ``` +--- @return number +function vim.fn.xor(expr, expr1) end + diff --git a/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/vim.fn.lua b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/vim.fn.lua new file mode 100644 index 00000000..12f91dc6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/neodev.nvim/types/stable/vim.fn.lua @@ -0,0 +1,6008 @@ +---@meta + + + +-- Return the absolute value of {expr}. When {expr} evaluates to +-- a |Float| abs() returns a |Float|. When {expr} can be +-- converted to a |Number| abs() returns a |Number|. Otherwise +-- abs() gives an error message and returns -1. +-- Examples: +-- ```vim +-- echo abs(1.456) +-- ``` +-- 1.456 > +-- echo abs(-5.456) +-- < 5.456 +-- ```vim +-- echo abs(-4) +-- ``` +-- 4 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->abs() +-- ``` +--- @return float +function vim.fn.abs(expr) end + +-- Return the arc cosine of {expr} measured in radians, as a +-- |Float| in the range of [0, pi]. +-- {expr} must evaluate to a |Float| or a |Number| in the range +-- [-1, 1]. +-- Returns NaN if {expr} is outside the range [-1, 1]. Returns +-- 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo acos(0) +-- ``` +-- 1.570796 > +-- :echo acos(-0.5) +-- < 2.094395 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->acos() +-- ``` +--- @return float +function vim.fn.acos(expr) end + +-- Append the item {expr} to |List| or |Blob| {object}. Returns +-- the resulting |List| or |Blob|. Examples: +-- ```vim +-- :let alist = add([1, 2, 3], item) +-- :call add(mylist, "woodstock") +-- ``` +-- Note that when {expr} is a |List| it is appended as a single +-- item. Use |extend()| to concatenate |Lists|. +-- When {object} is a |Blob| then {expr} must be a number. +-- Use |insert()| to add an item at another position. +-- Returns 1 if {object} is not a |List| or a |Blob|. +-- +-- Can also be used as a |method|: +-- ```vim +-- mylist->add(val1)->add(val2) +-- ``` +--- @return any[] +function vim.fn.add(object, expr) end + +-- Bitwise AND on the two arguments. The arguments are converted +-- to a number. A List, Dict or Float argument causes an error. +-- Example: +-- ```vim +-- :let flag = and(bits, 0x80) +-- ``` +-- Can also be used as a |method|: > +-- :let flag = bits->and(0x80) +--- @return number +vim.fn["and"] = function(expr, expr1) end + +-- Returns Dictionary of |api-metadata|. +-- +-- View it in a nice human-readable format: +-- ```vim +-- :lua print(vim.inspect(vim.fn.api_info())) +-- ``` +--- @return table +function vim.fn.api_info() end + +-- When {text} is a |List|: Append each item of the |List| as a +-- text line below line {lnum} in the current buffer. +-- Otherwise append {text} as one text line below line {lnum} in +-- the current buffer. +-- Any type of item is accepted and converted to a String. +-- {lnum} can be zero to insert a line before the first one. +-- {lnum} is used like with |getline()|. +-- Returns 1 for failure ({lnum} out of range or out of memory), +-- 0 for success. Example: +-- ```vim +-- :let failed = append(line('$'), "# THE END") +-- :let failed = append(0, ["Chapter 1", "the beginning"]) +-- +-- ``` +-- Can also be used as a |method| after a List: > +-- mylist->append(lnum) +--- @param lnum number +--- @param text string +--- @return number +function vim.fn.append(lnum, text) end + +-- Like |append()| but append the text in buffer {expr}. +-- +-- This function works only for loaded buffers. First call +-- |bufload()| if needed. +-- +-- For the use of {buf}, see |bufname()|. +-- +-- {lnum} is the line number to append below. Note that using +-- |line()| would use the current buffer, not the one appending +-- to. Use "$" to append at the end of the buffer. Other string +-- values are not supported. +-- +-- On success 0 is returned, on failure 1 is returned. +-- +-- If {buf} is not a valid buffer or {lnum} is not valid, an +-- error message is given. Example: +-- ```vim +-- :let failed = appendbufline(13, 0, "# THE START") +-- ``` +-- Can also be used as a |method| after a List: +-- ```vim +-- mylist->appendbufline(buf, lnum) +-- ``` +--- @param buf buffer +--- @param lnum number +--- @param text string +--- @return number +function vim.fn.appendbufline(buf, lnum, text) end + +-- The result is the number of files in the argument list. See +-- |arglist|. +-- If {winid} is not supplied, the argument list of the current +-- window is used. +-- If {winid} is -1, the global argument list is used. +-- Otherwise {winid} specifies the window of which the argument +-- list is used: either the window number or the window ID. +-- Returns -1 if the {winid} argument is invalid. +--- @param winid? window +--- @return number +function vim.fn.argc(winid) end + +-- The result is the current index in the argument list. 0 is +-- the first file. argc() - 1 is the last one. See |arglist|. +--- @return number +function vim.fn.argidx() end + +-- Return the argument list ID. This is a number which +-- identifies the argument list being used. Zero is used for the +-- global argument list. See |arglist|. +-- Returns -1 if the arguments are invalid. +-- +-- Without arguments use the current window. +-- With {winnr} only use this window in the current tab page. +-- With {winnr} and {tabnr} use the window in the specified tab +-- page. +-- {winnr} can be the window number or the |window-ID|. +--- @param winnr? window +--- @param tabnr? number +--- @return number +function vim.fn.arglistid(winnr, tabnr) end + +-- The result is the {nr}th file in the argument list. See +-- |arglist|. "argv(0)" is the first one. Example: +-- ```vim +-- :let i = 0 +-- :while i < argc() +-- : let f = escape(fnameescape(argv(i)), '.') +-- : exe 'amenu Arg.' .. f .. ' :e ' .. f .. '' +-- : let i = i + 1 +-- :endwhile +-- ``` +-- Without the {nr} argument, or when {nr} is -1, a |List| with +-- the whole |arglist| is returned. +-- +-- The {winid} argument specifies the window ID, see |argc()|. +-- For the Vim command line arguments see |v:argv|. +-- +-- Returns an empty string if {nr}th argument is not present in +-- the argument list. Returns an empty List if the {winid} +-- argument is invalid. +--- @param nr? number +--- @param winid? window +--- @return any[] +function vim.fn.argv(nr, winid) end + +-- Return the arc sine of {expr} measured in radians, as a |Float| +-- in the range of [-pi/2, pi/2]. +-- {expr} must evaluate to a |Float| or a |Number| in the range +-- [-1, 1]. +-- Returns NaN if {expr} is outside the range [-1, 1]. Returns +-- 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo asin(0.8) +-- ``` +-- 0.927295 > +-- :echo asin(-0.5) +-- < -0.523599 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->asin() +-- ``` +--- @return float +function vim.fn.asin(expr) end + +-- Number assert {cmd} causes a beep +--- @return number +function vim.fn.assert_beeps(cmd) end + +-- Number assert {exp} is equal to {act} +--- @param msg? any +--- @return number +function vim.fn.assert_equal(exp, act, msg) end + +-- Number assert file contents are equal +--- @param msg? any +--- @return number +function vim.fn.assert_equalfile(fname_one, fname_two, msg) end + +-- Number assert {error} is in v:exception +--- @param msg? any +--- @return number +function vim.fn.assert_exception(error, msg) end + +-- Number assert {cmd} fails +--- @param error? any +--- @return number +function vim.fn.assert_fails(cmd, error) end + +-- Number assert {actual} is false +--- @param msg? any +--- @return number +function vim.fn.assert_false(actual, msg) end + +-- Number assert {actual} is inside the range +--- @param msg? any +--- @return number +function vim.fn.assert_inrange(lower, upper, actual, msg) end + +-- Number assert {pat} matches {text} +--- @param text string +--- @param msg? any +--- @return number +function vim.fn.assert_match(pat, text, msg) end + +-- Number assert {cmd} does not cause a beep +--- @return number +function vim.fn.assert_nobeep(cmd) end + +-- Number assert {exp} is not equal {act} +--- @param msg? any +--- @return number +function vim.fn.assert_notequal(exp, act, msg) end + +-- Number assert {pat} not matches {text} +--- @param text string +--- @param msg? any +--- @return number +function vim.fn.assert_notmatch(pat, text, msg) end + +-- Number report a test failure +--- @return number +function vim.fn.assert_report(msg) end + +-- Number assert {actual} is true +--- @param msg? any +--- @return number +function vim.fn.assert_true(actual, msg) end + +-- Return the principal value of the arc tangent of {expr}, in +-- the range [-pi/2, +pi/2] radians, as a |Float|. +-- {expr} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo atan(100) +-- ``` +-- 1.560797 > +-- :echo atan(-4.01) +-- < -1.326405 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->atan() +-- ``` +--- @return float +function vim.fn.atan(expr) end + +-- Return the arc tangent of {expr1} / {expr2}, measured in +-- radians, as a |Float| in the range [-pi, pi]. +-- {expr1} and {expr2} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr1} or {expr2} is not a |Float| or a +-- |Number|. +-- Examples: +-- ```vim +-- :echo atan2(-1, 1) +-- ``` +-- -0.785398 > +-- :echo atan2(1, -1) +-- < 2.356194 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->atan2(1) +-- ``` +--- @return float +function vim.fn.atan2(expr1, expr2) end + +-- Return a List containing the number value of each byte in Blob +-- {blob}. Examples: +-- ```vim +-- blob2list(0z0102.0304) returns [1, 2, 3, 4] +-- blob2list(0z) returns [] +-- ``` +-- Returns an empty List on error. |list2blob()| does the +-- opposite. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetBlob()->blob2list() +-- ``` +--- @return any[] +function vim.fn.blob2list(blob) end + +-- Put up a file requester. This only works when "has("browse")" +-- returns |TRUE| (only in some GUI versions). +-- The input fields are: +-- {save} when |TRUE|, select file to write +-- {title} title for the requester +-- {initdir} directory to start browsing in +-- {default} default file name +-- An empty string is returned when the "Cancel" button is hit, +-- something went wrong, or browsing is not possible. +--- @return string +function vim.fn.browse(save, title, initdir, default) end + +-- Put up a directory requester. This only works when +-- "has("browse")" returns |TRUE| (only in some GUI versions). +-- On systems where a directory browser is not supported a file +-- browser is used. In that case: select a file in the directory +-- to be used. +-- The input fields are: +-- {title} title for the requester +-- {initdir} directory to start browsing in +-- When the "Cancel" button is hit, something went wrong, or +-- browsing is not possible, an empty string is returned. +--- @return string +function vim.fn.browsedir(title, initdir) end + +-- Add a buffer to the buffer list with name {name} (must be a +-- String). +-- If a buffer for file {name} already exists, return that buffer +-- number. Otherwise return the buffer number of the newly +-- created buffer. When {name} is an empty string then a new +-- buffer is always created. +-- The buffer will not have 'buflisted' set and not be loaded +-- yet. To add some text to the buffer use this: +-- ```vim +-- let bufnr = bufadd('someName') +-- call bufload(bufnr) +-- call setbufline(bufnr, 1, ['some', 'text']) +-- ``` +-- Returns 0 on error. +-- Can also be used as a |method|: +-- ```vim +-- let bufnr = 'somename'->bufadd() +-- ``` +--- @return number +function vim.fn.bufadd(name) end + +-- The result is a Number, which is |TRUE| if a buffer called +-- {buf} exists. +-- If the {buf} argument is a number, buffer numbers are used. +-- Number zero is the alternate buffer for the current window. +-- +-- If the {buf} argument is a string it must match a buffer name +-- exactly. The name can be: +-- - Relative to the current directory. +-- - A full path. +-- - The name of a buffer with 'buftype' set to "nofile". +-- - A URL name. +-- Unlisted buffers will be found. +-- Note that help files are listed by their short name in the +-- output of |:buffers|, but bufexists() requires using their +-- long name to be able to find them. +-- bufexists() may report a buffer exists, but to use the name +-- with a |:buffer| command you may need to use |expand()|. Esp +-- for MS-Windows 8.3 names in the form "c:\DOCUME~1" +-- Use "bufexists(0)" to test for the existence of an alternate +-- file name. +-- +-- Can also be used as a |method|: +-- ```vim +-- let exists = 'somename'->bufexists() +-- ``` +--- @param buf buffer +--- @return number +function vim.fn.bufexists(buf) end + +-- The result is a Number, which is |TRUE| if a buffer called +-- {buf} exists and is listed (has the 'buflisted' option set). +-- The {buf} argument is used like with |bufexists()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- let listed = 'somename'->buflisted() +-- ``` +--- @param buf buffer +--- @return number +function vim.fn.buflisted(buf) end + +-- Ensure the buffer {buf} is loaded. When the buffer name +-- refers to an existing file then the file is read. Otherwise +-- the buffer will be empty. If the buffer was already loaded +-- then there is no change. If the buffer is not related to a +-- file the no file is read (e.g., when 'buftype' is "nofile"). +-- If there is an existing swap file for the file of the buffer, +-- there will be no dialog, the buffer will be loaded anyway. +-- The {buf} argument is used like with |bufexists()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- eval 'somename'->bufload() +-- ``` +--- @param buf buffer +--- @return number +function vim.fn.bufload(buf) end + +-- The result is a Number, which is |TRUE| if a buffer called +-- {buf} exists and is loaded (shown in a window or hidden). +-- The {buf} argument is used like with |bufexists()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- let loaded = 'somename'->bufloaded() +-- ``` +--- @param buf buffer +--- @return number +function vim.fn.bufloaded(buf) end + +-- The result is the name of a buffer. Mostly as it is displayed +-- by the `:ls` command, but not using special names such as +-- "[No Name]". +-- If {buf} is omitted the current buffer is used. +-- If {buf} is a Number, that buffer number's name is given. +-- Number zero is the alternate buffer for the current window. +-- If {buf} is a String, it is used as a |file-pattern| to match +-- with the buffer names. This is always done like 'magic' is +-- set and 'cpoptions' is empty. When there is more than one +-- match an empty string is returned. +-- "" or "%" can be used for the current buffer, "#" for the +-- alternate buffer. +-- A full match is preferred, otherwise a match at the start, end +-- or middle of the buffer name is accepted. If you only want a +-- full match then put "^" at the start and "$" at the end of the +-- pattern. +-- Listed buffers are found first. If there is a single match +-- with a listed buffer, that one is returned. Next unlisted +-- buffers are searched for. +-- If the {buf} is a String, but you want to use it as a buffer +-- number, force it to be a Number by adding zero to it: +-- ```vim +-- :echo bufname("3" + 0) +-- ``` +-- Can also be used as a |method|: > +-- echo bufnr->bufname() +-- +-- < If the buffer doesn't exist, or doesn't have a name, an empty +-- string is returned. +-- ```vim +-- bufname("#") alternate buffer name +-- bufname(3) name of buffer 3 +-- bufname("%") name of current buffer +-- bufname("file2") name of buffer where "file2" matches. +-- ``` +--- @param buf? buffer +--- @return string +function vim.fn.bufname(buf) end + +-- The result is the number of a buffer, as it is displayed by +-- the `:ls` command. For the use of {buf}, see |bufname()| +-- above. +-- If the buffer doesn't exist, -1 is returned. Or, if the +-- {create} argument is present and TRUE, a new, unlisted, +-- buffer is created and its number is returned. +-- bufnr("$") is the last buffer: +-- ```vim +-- :let last_buffer = bufnr("$") +-- ``` +-- The result is a Number, which is the highest buffer number +-- of existing buffers. Note that not all buffers with a smaller +-- number necessarily exist, because ":bwipeout" may have removed +-- them. Use bufexists() to test for the existence of a buffer. +-- +-- Can also be used as a |method|: +-- ```vim +-- echo bufref->bufnr() +-- ``` +--- @param buf? buffer +--- @param create? any +--- @return number +function vim.fn.bufnr(buf, create) end + +-- The result is a Number, which is the |window-ID| of the first +-- window associated with buffer {buf}. For the use of {buf}, +-- see |bufname()| above. If buffer {buf} doesn't exist or +-- there is no such window, -1 is returned. Example: +-- ```vim +-- +-- echo "A window containing buffer 1 is " .. (bufwinid(1)) +-- ``` +-- Only deals with the current tab page. See |win_findbuf()| for +-- finding more. +-- +-- Can also be used as a |method|: +-- ```vim +-- FindBuffer()->bufwinid() +-- ``` +--- @param buf buffer +--- @return number +function vim.fn.bufwinid(buf) end + +-- Like |bufwinid()| but return the window number instead of the +-- |window-ID|. +-- If buffer {buf} doesn't exist or there is no such window, -1 +-- is returned. Example: +-- ```vim +-- +-- echo "A window containing buffer 1 is " .. (bufwinnr(1)) +-- +-- ``` +-- The number can be used with |CTRL-W_w| and ":wincmd w" +-- |:wincmd|. +-- +-- Can also be used as a |method|: +-- ```vim +-- FindBuffer()->bufwinnr() +-- ``` +--- @param buf buffer +--- @return number +function vim.fn.bufwinnr(buf) end + +-- Return the line number that contains the character at byte +-- count {byte} in the current buffer. This includes the +-- end-of-line character, depending on the 'fileformat' option +-- for the current buffer. The first character has byte count +-- one. +-- Also see |line2byte()|, |go| and |:goto|. +-- +-- Returns -1 if the {byte} value is invalid. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetOffset()->byte2line() +-- ``` +--- @return number +function vim.fn.byte2line(byte) end + +-- Return byte index of the {nr}th character in the String +-- {expr}. Use zero for the first character, it then returns +-- zero. +-- If there are no multibyte characters the returned value is +-- equal to {nr}. +-- Composing characters are not counted separately, their byte +-- length is added to the preceding base character. See +-- |byteidxcomp()| below for counting composing characters +-- separately. +-- Example : +-- ```vim +-- echo matchstr(str, ".", byteidx(str, 3)) +-- ``` +-- will display the fourth character. Another way to do the +-- same: +-- ```vim +-- let s = strpart(str, byteidx(str, 3)) +-- echo strpart(s, 0, byteidx(s, 1)) +-- ``` +-- Also see |strgetchar()| and |strcharpart()|. +-- +-- If there are less than {nr} characters -1 is returned. +-- If there are exactly {nr} characters the length of the string +-- in bytes is returned. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetName()->byteidx(idx) +-- ``` +--- @param nr number +--- @return number +function vim.fn.byteidx(expr, nr) end + +-- Like byteidx(), except that a composing character is counted +-- as a separate character. Example: +-- ```vim +-- let s = 'e' .. nr2char(0x301) +-- echo byteidx(s, 1) +-- echo byteidxcomp(s, 1) +-- echo byteidxcomp(s, 2) +-- ``` +-- The first and third echo result in 3 ('e' plus composing +-- character is 3 bytes), the second echo results in 1 ('e' is +-- one byte). +-- +-- Can also be used as a |method|: +-- ```vim +-- GetName()->byteidxcomp(idx) +-- ``` +--- @param nr number +--- @return number +function vim.fn.byteidxcomp(expr, nr) end + +-- Call function {func} with the items in |List| {arglist} as +-- arguments. +-- {func} can either be a |Funcref| or the name of a function. +-- a:firstline and a:lastline are set to the cursor line. +-- Returns the return value of the called function. +-- {dict} is for functions with the "dict" attribute. It will be +-- used to set the local variable "self". |Dictionary-function| +-- +-- Can also be used as a |method|: +-- ```vim +-- GetFunc()->call([arg, arg], dict) +-- ``` +--- @param func fun() +--- @param dict? table +function vim.fn.call(func, arglist, dict) end + +-- Return the smallest integral value greater than or equal to +-- {expr} as a |Float| (round up). +-- {expr} must evaluate to a |Float| or a |Number|. +-- Examples: +-- ```vim +-- echo ceil(1.456) +-- ``` +-- 2.0 > +-- echo ceil(-5.456) +-- < -5.0 +-- ```vim +-- echo ceil(4.0) +-- ``` +-- 4.0 +-- +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->ceil() +-- ``` +--- @return float +function vim.fn.ceil(expr) end + +-- Close a channel or a specific stream associated with it. +-- For a job, {stream} can be one of "stdin", "stdout", +-- "stderr" or "rpc" (closes stdin/stdout for a job started +-- with `"rpc":v:true`) If {stream} is omitted, all streams +-- are closed. If the channel is a pty, this will then close the +-- pty master, sending SIGHUP to the job process. +-- For a socket, there is only one stream, and {stream} should be +-- omitted. +--- @param stream? any +--- @return number +function vim.fn.chanclose(id, stream) end + +-- Return the number of the most recent change. This is the same +-- number as what is displayed with |:undolist| and can be used +-- with the |:undo| command. +-- When a change was made it is the number of that change. After +-- redo it is the number of the redone change. After undo it is +-- one less than the number of the undone change. +-- Returns 0 if the undo list is empty. +--- @return number +function vim.fn.changenr() end + +-- Send data to channel {id}. For a job, it writes it to the +-- stdin of the process. For the stdio channel |channel-stdio|, +-- it writes to Nvim's stdout. Returns the number of bytes +-- written if the write succeeded, 0 otherwise. +-- See |channel-bytes| for more information. +-- +-- {data} may be a string, string convertible, |Blob|, or a list. +-- If {data} is a list, the items will be joined by newlines; any +-- newlines in an item will be sent as NUL. To send a final +-- newline, include a final empty string. Example: +-- ```vim +-- :call chansend(id, ["abc", "123\n456", ""]) +-- ``` +-- will send "abcNL>123NUL>456NL>". +-- +-- chansend() writes raw data, not RPC messages. If the channel +-- was created with `"rpc":v:true` then the channel expects RPC +-- messages, use |rpcnotify()| and |rpcrequest()| instead. +--- @return number +function vim.fn.chansend(id, data) end + +-- Return Number value of the first char in {string}. +-- Examples: +-- ```vim +-- char2nr(" ") returns 32 +-- char2nr("ABC") returns 65 +-- char2nr("á") returns 225 +-- char2nr("á"[0]) returns 195 +-- char2nr("\") returns 128 +-- ``` +-- Non-ASCII characters are always treated as UTF-8 characters. +-- {utf8} is ignored, it exists only for backwards-compatibility. +-- A combining character is a separate character. +-- |nr2char()| does the opposite. +-- +-- Returns 0 if {string} is not a |String|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetChar()->char2nr() +-- ``` +--- @param string string +--- @param utf8? any +--- @return number +function vim.fn.char2nr(string, utf8) end + +-- Return the character class of the first character in {string}. +-- The character class is one of: +-- 0 blank +-- 1 punctuation +-- 2 word character +-- 3 emoji +-- other specific Unicode class +-- The class is used in patterns and word motions. +-- Returns 0 if {string} is not a |String|. +--- @param string string +--- @return number +function vim.fn.charclass(string) end + +-- Same as |col()| but returns the character index of the column +-- position given with {expr} instead of the byte position. +-- +-- Example: +-- With the cursor on '세' in line 5 with text "여보세요": +-- ```vim +-- charcol('.') returns 3 +-- col('.') returns 7 +-- +-- ``` +-- Can also be used as a |method|: > +-- GetPos()->col() +-- < +--- @param winid? window +--- @return number +function vim.fn.charcol(expr, winid) end + +-- Return the character index of the byte at {idx} in {string}. +-- The index of the first character is zero. +-- If there are no multibyte characters the returned value is +-- equal to {idx}. +-- When {countcc} is omitted or |FALSE|, then composing characters +-- are not counted separately, their byte length is +-- added to the preceding base character. +-- When {countcc} is |TRUE|, then composing characters are +-- counted as separate characters. +-- Returns -1 if the arguments are invalid or if {idx} is greater +-- than the index of the last byte in {string}. An error is +-- given if the first argument is not a string, the second +-- argument is not a number or when the third argument is present +-- and is not zero or one. +-- See |byteidx()| and |byteidxcomp()| for getting the byte index +-- from the character index. +-- Examples: +-- ```vim +-- echo charidx('áb́ć', 3) returns 1 +-- echo charidx('áb́ć', 6, 1) returns 4 +-- echo charidx('áb́ć', 16) returns -1 +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- GetName()->charidx(idx) +-- ``` +--- @param string string +--- @param countcc? any +--- @return number +function vim.fn.charidx(string, idx, countcc) end + +-- Change the current working directory to {dir}. The scope of +-- the directory change depends on the directory of the current +-- window: +-- - If the current window has a window-local directory +-- (|:lcd|), then changes the window local directory. +-- - Otherwise, if the current tabpage has a local +-- directory (|:tcd|) then changes the tabpage local +-- directory. +-- - Otherwise, changes the global directory. +-- {dir} must be a String. +-- If successful, returns the previous working directory. Pass +-- this to another chdir() to restore the directory. +-- On failure, returns an empty string. +-- +-- Example: +-- ```vim +-- let save_dir = chdir(newdir) +-- if save_dir != "" +-- " ... do some work +-- call chdir(save_dir) +-- endif +-- +-- ``` +-- Can also be used as a |method|: > +-- GetDir()->chdir() +-- < +--- @return string +function vim.fn.chdir(dir) end + +-- Get the amount of indent for line {lnum} according the C +-- indenting rules, as with 'cindent'. +-- The indent is counted in spaces, the value of 'tabstop' is +-- relevant. {lnum} is used just like in |getline()|. +-- When {lnum} is invalid -1 is returned. +-- See |C-indenting|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetLnum()->cindent() +-- ``` +--- @param lnum number +--- @return number +function vim.fn.cindent(lnum) end + +-- Clears all matches previously defined for the current window +-- by |matchadd()| and the |:match| commands. +-- If {win} is specified, use the window with this number or +-- window ID instead of the current window. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetWin()->clearmatches() +-- ``` +--- @param win? window +function vim.fn.clearmatches(win) end + +-- The result is a Number, which is the byte index of the column +-- position given with {expr}. The accepted positions are: +-- . the cursor position +-- $ the end of the cursor line (the result is the +-- number of bytes in the cursor line plus one) +-- 'x position of mark x (if the mark is not set, 0 is +-- returned) +-- v In Visual mode: the start of the Visual area (the +-- cursor is the end). When not in Visual mode +-- returns the cursor position. Differs from |'<| in +-- that it's updated right away. +-- Additionally {expr} can be [lnum, col]: a |List| with the line +-- and column number. Most useful when the column is "$", to get +-- the last column of a specific line. When "lnum" or "col" is +-- out of range then col() returns zero. +-- With the optional {winid} argument the values are obtained for +-- that window instead of the current window. +-- To get the line number use |line()|. To get both use +-- |getpos()|. +-- For the screen column position use |virtcol()|. For the +-- character position use |charcol()|. +-- Note that only marks in the current file can be used. +-- Examples: +-- ```vim +-- col(".") column of cursor +-- col("$") length of cursor line plus one +-- col("'t") column of mark t +-- col("'" .. markname) column of mark markname +-- ``` +-- The first column is 1. Returns 0 if {expr} is invalid or when +-- the window with ID {winid} is not found. +-- For an uppercase mark the column may actually be in another +-- buffer. +-- For the cursor position, when 'virtualedit' is active, the +-- column is one higher if the cursor is after the end of the +-- line. Also, when using a mapping the cursor isn't +-- moved, this can be used to obtain the column in Insert mode: +-- ```vim +-- :imap echo col(".").."\n" +-- +-- ``` +-- Can also be used as a |method|: > +-- GetPos()->col() +-- < +--- @param winid? window +--- @return number +function vim.fn.col(expr, winid) end + +-- Set the matches for Insert mode completion. +-- Can only be used in Insert mode. You need to use a mapping +-- with CTRL-R = (see |i_CTRL-R|). It does not work after CTRL-O +-- or with an expression mapping. +-- {startcol} is the byte offset in the line where the completed +-- text start. The text up to the cursor is the original text +-- that will be replaced by the matches. Use col('.') for an +-- empty string. "col('.') - 1" will replace one character by a +-- match. +-- {matches} must be a |List|. Each |List| item is one match. +-- See |complete-items| for the kind of items that are possible. +-- "longest" in 'completeopt' is ignored. +-- Note that the after calling this function you need to avoid +-- inserting anything that would cause completion to stop. +-- The match can be selected with CTRL-N and CTRL-P as usual with +-- Insert mode completion. The popup menu will appear if +-- specified, see |ins-completion-menu|. +-- Example: +-- ```vim +-- inoremap =ListMonths() +-- +-- func! ListMonths() +-- call complete(col('.'), ['January', 'February', 'March', +-- \ 'April', 'May', 'June', 'July', 'August', 'September', +-- \ 'October', 'November', 'December']) +-- return '' +-- endfunc +-- ``` +-- This isn't very useful, but it shows how it works. Note that +-- an empty string is returned to avoid a zero being inserted. +-- +-- Can also be used as a |method|, the base is passed as the +-- second argument: +-- ```vim +-- GetMatches()->complete(col('.')) +-- ``` +function vim.fn.complete(startcol, matches) end + +-- Add {expr} to the list of matches. Only to be used by the +-- function specified with the 'completefunc' option. +-- Returns 0 for failure (empty string or out of memory), +-- 1 when the match was added, 2 when the match was already in +-- the list. +-- See |complete-functions| for an explanation of {expr}. It is +-- the same as one item in the list that 'omnifunc' would return. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetMoreMatches()->complete_add() +-- ``` +--- @return number +function vim.fn.complete_add(expr) end + +-- Check for a key typed while looking for completion matches. +-- This is to be used when looking for matches takes some time. +-- Returns |TRUE| when searching for matches is to be aborted, +-- zero otherwise. +-- Only to be used by the function specified with the +-- 'completefunc' option. +--- @return number +function vim.fn.complete_check() end + +-- Returns a |Dictionary| with information about Insert mode +-- completion. See |ins-completion|. +-- The items are: +-- mode Current completion mode name string. +-- See |complete_info_mode| for the values. +-- pum_visible |TRUE| if popup menu is visible. +-- See |pumvisible()|. +-- items List of completion matches. Each item is a +-- dictionary containing the entries "word", +-- "abbr", "menu", "kind", "info" and "user_data". +-- See |complete-items|. +-- selected Selected item index. First index is zero. +-- Index is -1 if no item is selected (showing +-- typed text only, or the last completion after +-- no item is selected when using the or +-- keys) +-- inserted Inserted string. [NOT IMPLEMENTED YET] +-- +-- +-- mode values are: +-- "" Not in completion mode +-- "keyword" Keyword completion |i_CTRL-X_CTRL-N| +-- "ctrl_x" Just pressed CTRL-X |i_CTRL-X| +-- "scroll" Scrolling with |i_CTRL-X_CTRL-E| or +-- |i_CTRL-X_CTRL-Y| +-- "whole_line" Whole lines |i_CTRL-X_CTRL-L| +-- "files" File names |i_CTRL-X_CTRL-F| +-- "tags" Tags |i_CTRL-X_CTRL-]| +-- "path_defines" Definition completion |i_CTRL-X_CTRL-D| +-- "path_patterns" Include completion |i_CTRL-X_CTRL-I| +-- "dictionary" Dictionary |i_CTRL-X_CTRL-K| +-- "thesaurus" Thesaurus |i_CTRL-X_CTRL-T| +-- "cmdline" Vim Command line |i_CTRL-X_CTRL-V| +-- "function" User defined completion |i_CTRL-X_CTRL-U| +-- "omni" Omni completion |i_CTRL-X_CTRL-O| +-- "spell" Spelling suggestions |i_CTRL-X_s| +-- "eval" |complete()| completion +-- "unknown" Other internal modes +-- +-- If the optional {what} list argument is supplied, then only +-- the items listed in {what} are returned. Unsupported items in +-- {what} are silently ignored. +-- +-- To get the position and size of the popup menu, see +-- |pum_getpos()|. It's also available in |v:event| during the +-- |CompleteChanged| event. +-- +-- Returns an empty |Dictionary| on error. +-- +-- Examples: +-- ```vim +-- " Get all items +-- call complete_info() +-- " Get only 'mode' +-- call complete_info(['mode']) +-- " Get only 'mode' and 'pum_visible' +-- call complete_info(['mode', 'pum_visible']) +-- +-- ``` +-- Can also be used as a |method|: > +-- GetItems()->complete_info() +-- < +--- @param what? any +--- @return table +function vim.fn.complete_info(what) end + +-- confirm() offers the user a dialog, from which a choice can be +-- made. It returns the number of the choice. For the first +-- choice this is 1. +-- +-- {msg} is displayed in a dialog with {choices} as the +-- alternatives. When {choices} is missing or empty, "&OK" is +-- used (and translated). +-- {msg} is a String, use '\n' to include a newline. Only on +-- some systems the string is wrapped when it doesn't fit. +-- +-- {choices} is a String, with the individual choices separated +-- by '\n', e.g. +-- ```vim +-- confirm("Save changes?", "&Yes\n&No\n&Cancel") +-- ``` +-- The letter after the '&' is the shortcut key for that choice. +-- Thus you can type 'c' to select "Cancel". The shortcut does +-- not need to be the first letter: +-- ```vim +-- confirm("file has been modified", "&Save\nSave &All") +-- ``` +-- For the console, the first letter of each choice is used as +-- the default shortcut key. Case is ignored. +-- +-- The optional {type} String argument gives the type of dialog. +-- It can be one of these values: "Error", "Question", "Info", +-- "Warning" or "Generic". Only the first character is relevant. +-- When {type} is omitted, "Generic" is used. +-- +-- The optional {type} argument gives the type of dialog. This +-- is only used for the icon of the Win32 GUI. It can be one of +-- these values: "Error", "Question", "Info", "Warning" or +-- "Generic". Only the first character is relevant. +-- When {type} is omitted, "Generic" is used. +-- +-- If the user aborts the dialog by pressing , CTRL-C, +-- or another valid interrupt key, confirm() returns 0. +-- +-- An example: +-- ```vim +-- let choice = confirm("What do you want?", +-- \ "&Apples\n&Oranges\n&Bananas", 2) +-- if choice == 0 +-- echo "make up your mind!" +-- elseif choice == 3 +-- echo "tasteful" +-- else +-- echo "I prefer bananas myself." +-- endif +-- ``` +-- In a GUI dialog, buttons are used. The layout of the buttons +-- depends on the 'v' flag in 'guioptions'. If it is included, +-- the buttons are always put vertically. Otherwise, confirm() +-- tries to put the buttons in one horizontal line. If they +-- don't fit, a vertical layout is used anyway. For some systems +-- the horizontal layout is always used. +-- +-- Can also be used as a |method|in: +-- ```vim +-- BuildMessage()->confirm("&Yes\n&No") +-- ``` +--- @param choices? any +--- @param default? any +--- @param type? any +--- @return number +function vim.fn.confirm(msg, choices, default, type) end + +-- Make a copy of {expr}. For Numbers and Strings this isn't +-- different from using {expr} directly. +-- When {expr} is a |List| a shallow copy is created. This means +-- that the original |List| can be changed without changing the +-- copy, and vice versa. But the items are identical, thus +-- changing an item changes the contents of both |Lists|. +-- A |Dictionary| is copied in a similar way as a |List|. +-- Also see |deepcopy()|. +-- Can also be used as a |method|: +-- ```vim +-- mylist->copy() +-- ``` +function vim.fn.copy(expr) end + +-- Return the cosine of {expr}, measured in radians, as a |Float|. +-- {expr} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo cos(100) +-- ``` +-- 0.862319 > +-- :echo cos(-4.01) +-- < -0.646043 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->cos() +-- ``` +--- @return float +function vim.fn.cos(expr) end + +-- Return the hyperbolic cosine of {expr} as a |Float| in the range +-- [1, inf]. +-- {expr} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo cosh(0.5) +-- ``` +-- 1.127626 > +-- :echo cosh(-0.5) +-- < -1.127626 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->cosh() +-- ``` +--- @return float +function vim.fn.cosh(expr) end + +-- Return the number of times an item with value {expr} appears +-- in |String|, |List| or |Dictionary| {comp}. +-- +-- If {start} is given then start with the item with this index. +-- {start} can only be used with a |List|. +-- +-- When {ic} is given and it's |TRUE| then case is ignored. +-- +-- When {comp} is a string then the number of not overlapping +-- occurrences of {expr} is returned. Zero is returned when +-- {expr} is an empty string. +-- +-- Can also be used as a |method|: +-- ```vim +-- mylist->count(val) +-- ``` +--- @param ic? any +--- @param start? number +--- @return number +function vim.fn.count(comp, expr, ic, start) end + +-- Returns a |Dictionary| representing the |context| at {index} +-- from the top of the |context-stack| (see |context-dict|). +-- If {index} is not given, it is assumed to be 0 (i.e.: top). +--- @param index? number +--- @return table +function vim.fn.ctxget(index) end + +-- Pops and restores the |context| at the top of the +-- |context-stack|. +function vim.fn.ctxpop() end + +-- Pushes the current editor state (|context|) on the +-- |context-stack|. +-- If {types} is given and is a |List| of |String|s, it specifies +-- which |context-types| to include in the pushed context. +-- Otherwise, all context types are included. +--- @param types? any +function vim.fn.ctxpush(types) end + +-- Sets the |context| at {index} from the top of the +-- |context-stack| to that represented by {context}. +-- {context} is a Dictionary with context data (|context-dict|). +-- If {index} is not given, it is assumed to be 0 (i.e.: top). +--- @param index? number +function vim.fn.ctxset(context, index) end + +-- Returns the size of the |context-stack|. +--- @return number +function vim.fn.ctxsize() end + +-- Positions the cursor at the column (byte count) {col} in the +-- line {lnum}. The first column is one. +-- +-- When there is one argument {list} this is used as a |List| +-- with two, three or four item: +-- [{lnum}, {col}] +-- [{lnum}, {col}, {off}] +-- [{lnum}, {col}, {off}, {curswant}] +-- This is like the return value of |getpos()| or |getcurpos()|, +-- but without the first item. +-- +-- To position the cursor using the character count, use +-- |setcursorcharpos()|. +-- +-- Does not change the jumplist. +-- {lnum} is used like with |getline()|, except that if {lnum} is +-- zero, the cursor will stay in the current line. +-- If {lnum} is greater than the number of lines in the buffer, +-- the cursor will be positioned at the last line in the buffer. +-- If {col} is greater than the number of bytes in the line, +-- the cursor will be positioned at the last character in the +-- line. +-- If {col} is zero, the cursor will stay in the current column. +-- If {curswant} is given it is used to set the preferred column +-- for vertical movement. Otherwise {col} is used. +-- +-- When 'virtualedit' is used {off} specifies the offset in +-- screen columns from the start of the character. E.g., a +-- position within a or after the last character. +-- Returns 0 when the position could be set, -1 otherwise. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetCursorPos()->cursor() +-- ``` +--- @param list any[] +--- @return number +function vim.fn.cursor(list) end + +-- Specifically used to interrupt a program being debugged. It +-- will cause process {pid} to get a SIGTRAP. Behavior for other +-- processes is undefined. See |terminal-debug|. +-- (Sends a SIGINT to a process {pid} other than MS-Windows) +-- +-- Returns |TRUE| if successfully interrupted the program. +-- Otherwise returns |FALSE|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetPid()->debugbreak() +-- ``` +--- @return number +function vim.fn.debugbreak(pid) end + +-- Make a copy of {expr}. For Numbers and Strings this isn't +-- different from using {expr} directly. +-- When {expr} is a |List| a full copy is created. This means +-- that the original |List| can be changed without changing the +-- copy, and vice versa. When an item is a |List|, a copy for it +-- is made, recursively. Thus changing an item in the copy does +-- not change the contents of the original |List|. +-- +-- When {noref} is omitted or zero a contained |List| or +-- |Dictionary| is only copied once. All references point to +-- this single copy. With {noref} set to 1 every occurrence of a +-- |List| or |Dictionary| results in a new copy. This also means +-- that a cyclic reference causes deepcopy() to fail. +-- +-- Nesting is possible up to 100 levels. When there is an item +-- that refers back to a higher level making a deep copy with +-- {noref} set to 1 will fail. +-- Also see |copy()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetObject()->deepcopy() +-- ``` +--- @param noref? any +function vim.fn.deepcopy(expr, noref) end + +-- Without {flags} or with {flags} empty: Deletes the file by the +-- name {fname}. +-- +-- This also works when {fname} is a symbolic link. The symbolic +-- link itself is deleted, not what it points to. +-- +-- When {flags} is "d": Deletes the directory by the name +-- {fname}. This fails when directory {fname} is not empty. +-- +-- When {flags} is "rf": Deletes the directory by the name +-- {fname} and everything in it, recursively. BE CAREFUL! +-- Note: on MS-Windows it is not possible to delete a directory +-- that is being used. +-- +-- The result is a Number, which is 0/false if the delete +-- operation was successful and -1/true when the deletion failed +-- or partly failed. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetName()->delete() +-- ``` +--- @param flags? any +--- @return number +function vim.fn.delete(fname, flags) end + +-- Delete lines {first} to {last} (inclusive) from buffer {buf}. +-- If {last} is omitted then delete line {first} only. +-- On success 0 is returned, on failure 1 is returned. +-- +-- This function works only for loaded buffers. First call +-- |bufload()| if needed. +-- +-- For the use of {buf}, see |bufname()| above. +-- +-- {first} and {last} are used like with |getline()|. Note that +-- when using |line()| this refers to the current buffer. Use "$" +-- to refer to the last line in buffer {buf}. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetBuffer()->deletebufline(1) +-- ``` +--- @param buf buffer +--- @param last? any +--- @return number +function vim.fn.deletebufline(buf, first, last) end + +-- Adds a watcher to a dictionary. A dictionary watcher is +-- identified by three components: +-- +-- - A dictionary({dict}); +-- - A key pattern({pattern}). +-- - A function({callback}). +-- +-- After this is called, every change on {dict} and on keys +-- matching {pattern} will result in {callback} being invoked. +-- +-- For example, to watch all global variables: +-- ```vim +-- silent! call dictwatcherdel(g:, '*', 'OnDictChanged') +-- function! OnDictChanged(d,k,z) +-- echomsg string(a:k) string(a:z) +-- endfunction +-- call dictwatcheradd(g:, '*', 'OnDictChanged') +-- ``` +-- For now {pattern} only accepts very simple patterns that can +-- contain a "*" at the end of the string, in which case it will +-- match every key that begins with the substring before the "*". +-- That means if "*" is not the last character of {pattern}, only +-- keys that are exactly equal as {pattern} will be matched. +-- +-- The {callback} receives three arguments: +-- +-- - The dictionary being watched. +-- - The key which changed. +-- - A dictionary containing the new and old values for the key. +-- +-- The type of change can be determined by examining the keys +-- present on the third argument: +-- +-- - If contains both `old` and `new`, the key was updated. +-- - If it contains only `new`, the key was added. +-- - If it contains only `old`, the key was deleted. +-- +-- This function can be used by plugins to implement options with +-- validation and parsing logic. +--- @param dict table +--- @param callback fun() +function vim.fn.dictwatcheradd(dict, pattern, callback) end + +-- Removes a watcher added with |dictwatcheradd()|. All three +-- arguments must match the ones passed to |dictwatcheradd()| in +-- order for the watcher to be successfully deleted. +--- @param dict table +--- @param callback fun() +function vim.fn.dictwatcherdel(dict, pattern, callback) end + +-- Returns |TRUE| when autocommands are being executed and the +-- FileType event has been triggered at least once. Can be used +-- to avoid triggering the FileType event again in the scripts +-- that detect the file type. |FileType| +-- Returns |FALSE| when `:setf FALLBACK` was used. +-- When editing another file, the counter is reset, thus this +-- really checks if the FileType event has been triggered for the +-- current buffer. This allows an autocommand that starts +-- editing another buffer to set 'filetype' and load a syntax +-- file. +--- @return number +function vim.fn.did_filetype() end + +-- Returns the number of filler lines above line {lnum}. +-- These are the lines that were inserted at this point in +-- another diff'ed window. These filler lines are shown in the +-- display but don't exist in the buffer. +-- {lnum} is used like with |getline()|. Thus "." is the current +-- line, "'m" mark m, etc. +-- Returns 0 if the current window is not in diff mode. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetLnum()->diff_filler() +-- ``` +--- @param lnum number +--- @return number +function vim.fn.diff_filler(lnum) end + +-- Returns the highlight ID for diff mode at line {lnum} column +-- {col} (byte index). When the current line does not have a +-- diff change zero is returned. +-- {lnum} is used like with |getline()|. Thus "." is the current +-- line, "'m" mark m, etc. +-- {col} is 1 for the leftmost column, {lnum} is 1 for the first +-- line. +-- The highlight ID can be used with |synIDattr()| to obtain +-- syntax information about the highlighting. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetLnum()->diff_hlID(col) +-- ``` +--- @param lnum number +--- @param col number +--- @return number +function vim.fn.diff_hlID(lnum, col) end + +-- Return the digraph of {chars}. This should be a string with +-- exactly two characters. If {chars} are not just two +-- characters, or the digraph of {chars} does not exist, an error +-- is given and an empty string is returned. +-- +-- Also see |digraph_getlist()|. +-- +-- Examples: +-- ```vim +-- " Get a built-in digraph +-- :echo digraph_get('00') " Returns '∞' +-- +-- " Get a user-defined digraph +-- :call digraph_set('aa', 'あ') +-- :echo digraph_get('aa') " Returns 'あ' +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- GetChars()->digraph_get() +-- ``` +--- @return string +function vim.fn.digraph_get(chars) end + +-- Return a list of digraphs. If the {listall} argument is given +-- and it is TRUE, return all digraphs, including the default +-- digraphs. Otherwise, return only user-defined digraphs. +-- +-- Also see |digraph_get()|. +-- +-- Examples: +-- ```vim +-- " Get user-defined digraphs +-- :echo digraph_getlist() +-- +-- " Get all the digraphs, including default digraphs +-- :echo digraph_getlist(1) +-- ``` +-- Can also be used as a |method|: +-- ```vim +-- GetNumber()->digraph_getlist() +-- ``` +--- @param listall? any +--- @return any[] +function vim.fn.digraph_getlist(listall) end + +-- Add digraph {chars} to the list. {chars} must be a string +-- with two characters. {digraph} is a string with one UTF-8 +-- encoded character. +-- Be careful, composing characters are NOT ignored. This +-- function is similar to |:digraphs| command, but useful to add +-- digraphs start with a white space. +-- +-- The function result is v:true if |digraph| is registered. If +-- this fails an error message is given and v:false is returned. +-- +-- If you want to define multiple digraphs at once, you can use +-- |digraph_setlist()|. +-- +-- Example: +-- ```vim +-- call digraph_set(' ', 'あ') +-- ``` +-- Can be used as a |method|: +-- ```vim +-- GetString()->digraph_set('あ') +-- ``` +--- @return boolean +function vim.fn.digraph_set(chars, digraph) end + +-- Similar to |digraph_set()| but this function can add multiple +-- digraphs at once. {digraphlist} is a list composed of lists, +-- where each list contains two strings with {chars} and +-- {digraph} as in |digraph_set()|. +-- Example: +-- ```vim +-- call digraph_setlist([['aa', 'あ'], ['ii', 'い']]) +-- ``` +-- It is similar to the following: +-- ```vim +-- for [chars, digraph] in [['aa', 'あ'], ['ii', 'い']] +-- call digraph_set(chars, digraph) +-- endfor +-- ``` +-- Except that the function returns after the first error, +-- following digraphs will not be added. +-- +-- Can be used as a |method|: +-- ```vim +-- GetList()->digraph_setlist() +-- ``` +--- @return boolean +function vim.fn.digraph_setlist(digraphlist) end + +-- Return the Number 1 if {expr} is empty, zero otherwise. +-- - A |List| or |Dictionary| is empty when it does not have any +-- items. +-- - A |String| is empty when its length is zero. +-- - A |Number| and |Float| are empty when their value is zero. +-- - |v:false| and |v:null| are empty, |v:true| is not. +-- - A |Blob| is empty when its length is zero. +-- +-- Can also be used as a |method|: +-- ```vim +-- mylist->empty() +-- ``` +--- @return number +function vim.fn.empty(expr) end + +-- Return all of environment variables as dictionary. You can +-- check if an environment variable exists like this: +-- ```vim +-- :echo has_key(environ(), 'HOME') +-- ``` +-- Note that the variable name may be CamelCase; to ignore case +-- use this: +-- ```vim +-- :echo index(keys(environ()), 'HOME', 0, 1) != -1 +-- ``` +--- @return table +function vim.fn.environ() end + +-- Escape the characters in {chars} that occur in {string} with a +-- backslash. Example: +-- ```vim +-- :echo escape('c:\program files\vim', ' \') +-- ``` +-- results in: > +-- c:\\program\ files\\vim +-- < Also see |shellescape()| and |fnameescape()|. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetText()->escape(' \') +-- ``` +--- @param string string +--- @return string +function vim.fn.escape(string, chars) end + +-- Evaluate {string} and return the result. Especially useful to +-- turn the result of |string()| back into the original value. +-- This works for Numbers, Floats, Strings, Blobs and composites +-- of them. Also works for |Funcref|s that refer to existing +-- functions. +-- +-- Can also be used as a |method|: +-- ```vim +-- argv->join()->eval() +-- ``` +--- @param string string +function vim.fn.eval(string) end + +-- Returns 1 when inside an event handler. That is that Vim got +-- interrupted while waiting for the user to type a character, +-- e.g., when dropping a file on Vim. This means interactive +-- commands cannot be used. Otherwise zero is returned. +--- @return number +function vim.fn.eventhandler() end + +-- This function checks if an executable with the name {expr} +-- exists. {expr} must be the name of the program without any +-- arguments. +-- executable() uses the value of $PATH and/or the normal +-- searchpath for programs. +-- On MS-Windows the ".exe", ".bat", etc. can optionally be +-- included. Then the extensions in $PATHEXT are tried. Thus if +-- "foo.exe" does not exist, "foo.exe.bat" can be found. If +-- $PATHEXT is not set then ".exe;.com;.bat;.cmd" is used. A dot +-- by itself can be used in $PATHEXT to try using the name +-- without an extension. When 'shell' looks like a Unix shell, +-- then the name is also tried without adding an extension. +-- On MS-Windows it only checks if the file exists and is not a +-- directory, not if it's really executable. +-- On Windows an executable in the same directory as Vim is +-- always found (it is added to $PATH at |startup|). +-- The result is a Number: +-- 1 exists +-- 0 does not exist +-- -1 not implemented on this system +-- |exepath()| can be used to get the full path of an executable. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetCommand()->executable() +-- ``` +--- @return number +function vim.fn.executable(expr) end + +-- Execute {command} and capture its output. +-- If {command} is a |String|, returns {command} output. +-- If {command} is a |List|, returns concatenated outputs. +-- Examples: +-- ```vim +-- echo execute('echon "foo"') +-- ``` +-- foo > +-- echo execute(['echon "foo"', 'echon "bar"']) +-- < foobar +-- +-- The optional {silent} argument can have these values: +-- "" no `:silent` used +-- "silent" `:silent` used +-- "silent!" `:silent!` used +-- The default is "silent". Note that with "silent!", unlike +-- `:redir`, error messages are dropped. +-- +-- To get a list of lines use |split()| on the result: +-- ```vim +-- execute('args')->split("\n") +-- +-- ``` +-- This function is not available in the |sandbox|. +-- Note: If nested, an outer execute() will not observe output of +-- the inner calls. +-- Note: Text attributes (highlights) are not captured. +-- To execute a command in another window than the current one +-- use `win_execute()`. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetCommand()->execute() +-- ``` +--- @param silent? any +--- @return string +function vim.fn.execute(command, silent) end + +-- Returns the full path of {expr} if it is an executable and +-- given as a (partial or full) path or is found in $PATH. +-- Returns empty string otherwise. +-- If {expr} starts with "./" the |current-directory| is used. +-- +-- Can also be used as a |method|: +-- ```vim +-- GetCommand()->exepath() +-- ``` +--- @return string +function vim.fn.exepath(expr) end + +-- The result is a Number, which is |TRUE| if {expr} is +-- defined, zero otherwise. +-- +-- For checking for a supported feature use |has()|. +-- For checking if a file exists use |filereadable()|. +-- +-- The {expr} argument is a string, which contains one of these: +-- varname internal variable (see +-- dict.key |internal-variables|). Also works +-- list[i] for |curly-braces-names|, |Dictionary| +-- entries, |List| items, etc. +-- Beware that evaluating an index may +-- cause an error message for an invalid +-- expression. E.g.: +-- ```vim +-- :let l = [1, 2, 3] +-- :echo exists("l[5]") +-- ``` +-- 0 > +-- :echo exists("l[xx]") +-- < E121: Undefined variable: xx +-- 0 +-- &option-name Vim option (only checks if it exists, +-- not if it really works) +-- +option-name Vim option that works. +-- $ENVNAME environment variable (could also be +-- done by comparing with an empty +-- string) +-- *funcname built-in function (see |functions|) +-- or user defined function (see +-- |user-function|). Also works for a +-- variable that is a Funcref. +-- :cmdname Ex command: built-in command, user +-- command or command modifier |:command|. +-- Returns: +-- 1 for match with start of a command +-- 2 full match with a command +-- 3 matches several user commands +-- To check for a supported command +-- always check the return value to be 2. +-- :2match The |:2match| command. +-- :3match The |:3match| command (but you +-- probably should not use it, it is +-- reserved for internal usage) +-- #event autocommand defined for this event +-- #event#pattern autocommand defined for this event and +-- pattern (the pattern is taken +-- literally and compared to the +-- autocommand patterns character by +-- character) +-- #group autocommand group exists +-- #group#event autocommand defined for this group and +-- event. +-- #group#event#pattern +-- autocommand defined for this group, +-- event and pattern. +-- ##event autocommand for this event is +-- supported. +-- +-- Examples: +-- ```vim +-- exists("&mouse") +-- exists("$HOSTNAME") +-- exists("*strftime") +-- exists("*s:MyFunc") +-- exists("*MyFunc") +-- exists("bufcount") +-- exists(":Make") +-- exists("#CursorHold") +-- exists("#BufReadPre#*.gz") +-- exists("#filetypeindent") +-- exists("#filetypeindent#FileType") +-- exists("#filetypeindent#FileType#*") +-- exists("##ColorScheme") +-- ``` +-- There must be no space between the symbol (&/$/*/#) and the +-- name. +-- There must be no extra characters after the name, although in +-- a few cases this is ignored. That may become stricter in the +-- future, thus don't count on it! +-- Working example: +-- ```vim +-- exists(":make") +-- ``` +-- NOT working example: > +-- exists(":make install") +-- +-- < Note that the argument must be a string, not the name of the +-- variable itself. For example: +-- ```vim +-- exists(bufcount) +-- ``` +-- This doesn't check for existence of the "bufcount" variable, +-- but gets the value of "bufcount", and checks if that exists. +-- +-- Can also be used as a |method|: +-- ```vim +-- Varname()->exists() +-- ``` +--- @return number +function vim.fn.exists(expr) end + +-- Return the exponential of {expr} as a |Float| in the range +-- [0, inf]. +-- {expr} must evaluate to a |Float| or a |Number|. +-- Returns 0.0 if {expr} is not a |Float| or a |Number|. +-- Examples: +-- ```vim +-- :echo exp(2) +-- ``` +-- 7.389056 > +-- :echo exp(-1) +-- < 0.367879 +-- +-- Can also be used as a |method|: +-- ```vim +-- Compute()->exp() +-- ``` +--- @return float +function vim.fn.exp(expr) end + +-- Expand wildcards and the following special keywords in +-- {string}. 'wildignorecase' applies. +-- +-- If {list} is given and it is |TRUE|, a List will be returned. +-- Otherwise the result is a String and when there are several +-- matches, they are separated by characters. +-- +-- If the expansion fails, the result is an empty string. A name +-- for a non-existing file is not included, unless {string} does +-- not start with '%', '#' or '<', see below. +-- +-- When {string} starts with '%', '#' or '<', the expansion is +-- done like for the |cmdline-special| variables with their +-- associated modifiers. Here is a short overview: +-- +-- % current file name +-- # alternate file name +-- #n alternate file name n +-- file name under the cursor +-- autocmd file name +-- autocmd buffer number (as a String!) +-- autocmd matched name +-- C expression under the cursor +-- sourced script file or function name +-- sourced script line number or function +-- line number +-- script file line number, also when in +-- a function +-- "123_" where "123" is the +-- current script ID || +-- +``` + +```query +(script_element + (raw_text) @injection.content + (#set! injection.language "javascript")) ; set the parser language for @injection.content region to javascript +``` + +For regions that don't have a corresponding `@injection.language`, you need to manually set the language +through `(#set injection.language "lang_name")` + +To combine all matches of a pattern as one single block of content, add `(#set! injection.combined)` to such pattern + +### Indents + +```query +@indent.begin ; indent children when matching this node +@indent.end ; marks the end of indented block +@indent.align ; behaves like python aligned/hanging indent +@indent.dedent ; dedent children when matching this node +@indent.branch ; dedent itself when matching this node +@indent.ignore ; do not indent in this node +@indent.auto ; behaves like 'autoindent' buffer option +@indent.zero ; sets this node at position 0 (no indent) +``` + +[Matrix channel]: https://matrix.to/#/#nvim-treesitter:matrix.org diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/LICENSE b/config/neovim/store/lazy-plugins/nvim-treesitter/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/Makefile b/config/neovim/store/lazy-plugins/nvim-treesitter/Makefile new file mode 100644 index 00000000..338c7546 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/Makefile @@ -0,0 +1,7 @@ +# https://github.com/luarocks/luarocks/wiki/Creating-a-Makefile-that-plays-nice-with-LuaRocks +build: + echo "Do nothing" + +install: + mkdir -p $(INST_LUADIR) + cp -r lua/* $(INST_LUADIR) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/README.md b/config/neovim/store/lazy-plugins/nvim-treesitter/README.md new file mode 100644 index 00000000..6887b6f7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/README.md @@ -0,0 +1,810 @@ +
+

nvim-treesitter

+

+ + Matrix Chat + + + Linting and Style + + + Syntax files + +

+
+ +
+

+ Logo +

+

+ Treesitter + configurations and abstraction layer for + Neovim. +

+

+ + Logo by @steelsojka + +

+
+ +The goal of `nvim-treesitter` is both to provide a simple and easy way to use the interface for [tree-sitter](https://github.com/tree-sitter/tree-sitter) in Neovim and to provide some basic functionality such as highlighting based on it: + +![example-cpp](https://user-images.githubusercontent.com/2361214/202753610-e923bf4e-e88f-494b-bb1e-d22a7688446f.png) + +Traditional highlighting (left) vs Treesitter-based highlighting (right). +More examples can be found in [our gallery](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Gallery). + +**Warning: Treesitter and nvim-treesitter highlighting are an experimental feature of Neovim. +Please consider the experience with this plug-in as experimental until Tree-Sitter support in Neovim is stable! +We recommend using the nightly builds of Neovim if possible. +You can find the current roadmap [here](https://github.com/nvim-treesitter/nvim-treesitter/issues/4767). +The roadmap and all features of this plugin are open to change, and any suggestion will be highly appreciated!** + +Nvim-treesitter is based on three interlocking features: [**language parsers**](#language-parsers), [**queries**](#adding-queries), and [**modules**](#available-modules), where _modules_ provide features – e.g., highlighting – based on _queries_ for syntax objects extracted from a given buffer by _language parsers_. +Users will generally only need to interact with parsers and modules as explained in the next section. +For more detailed information on setting these up, see ["Advanced setup"](#advanced-setup). + +--- + +### Table of contents + +- [Quickstart](#quickstart) +- [Supported languages](#supported-languages) +- [Available modules](#available-modules) +- [Advanced setup](#advanced-setup) +- [Extra features](#extra-features) +- [Troubleshooting](#troubleshooting) + +--- + +# Quickstart + +## Requirements + +- **Neovim 0.9.2** or later ([nightly](https://github.com/neovim/neovim#install-from-source) recommended) +- `tar` and `curl` in your path (or alternatively `git`) +- A C compiler in your path and libstdc++ installed ([Windows users please read this!](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Windows-support)). + +## Installation + +You can install `nvim-treesitter` with your favorite package manager (or using the native `package` feature of vim, see `:h packages`). + +**NOTE: This plugin is only guaranteed to work with specific versions of language parsers** (as specified in the `lockfile.json`). **When upgrading the plugin, you must make sure that all installed parsers are updated to the latest version** via `:TSUpdate`. +It is strongly recommended to automate this; e.g., if you are using [vim-plug](https://github.com/junegunn/vim-plug), put this in your `init.vim` file: + +```vim +Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'} +``` + +For other plugin managers such as `packer.nvim`, see this [Installation page from the wiki](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Installation) (Note that this page is community maintained). + +## Language parsers + +Treesitter uses a different _parser_ for every language, which needs to be generated via `tree-sitter-cli` from a `grammar.js` file, then compiled to a `.so` library that needs to be placed in neovim's `runtimepath` (typically under `parser/{language}.so`). +To simplify this, `nvim-treesitter` provides commands to automate this process. +If the language is already [supported by `nvim-treesitter`](#supported-languages), you can install it with + +```vim +:TSInstall +``` + +This command supports tab expansion. +You can also get a list of all available languages and their installation status with `:TSInstallInfo`. +Parsers not on this list can be added manually by following the steps described under ["Adding parsers"](#adding-parsers) below. + +To make sure a parser is at the latest compatible version (as specified in `nvim-treesitter`'s `lockfile.json`), use `:TSUpdate {language}`. To update all parsers unconditionally, use `:TSUpdate all` or just `:TSUpdate`. + +## Modules + +Each module provides a distinct tree-sitter-based feature such as [highlighting](#highlight), [indentation](#indentation), or [folding](#folding); see [`:h nvim-treesitter-modules`](doc/nvim-treesitter.txt) or ["Available modules"](#available-modules) below for a list of modules and their options. + +Following examples assume that you are configuring neovim with lua. If you are using vimscript, see `:h lua-heredoc`. +All modules are disabled by default and need to be activated explicitly in your `init.lua`, e.g., via + +```lua +require'nvim-treesitter.configs'.setup { + -- A list of parser names, or "all" (the five listed parsers should always be installed) + ensure_installed = { "c", "lua", "vim", "vimdoc", "query" }, + + -- Install parsers synchronously (only applied to `ensure_installed`) + sync_install = false, + + -- Automatically install missing parsers when entering buffer + -- Recommendation: set to false if you don't have `tree-sitter` CLI installed locally + auto_install = true, + + -- List of parsers to ignore installing (or "all") + ignore_install = { "javascript" }, + + ---- If you need to change the installation directory of the parsers (see -> Advanced Setup) + -- parser_install_dir = "/some/path/to/store/parsers", -- Remember to run vim.opt.runtimepath:append("/some/path/to/store/parsers")! + + highlight = { + enable = true, + + -- NOTE: these are the names of the parsers and not the filetype. (for example if you want to + -- disable highlighting for the `tex` filetype, you need to include `latex` in this list as this is + -- the name of the parser) + -- list of language that will be disabled + disable = { "c", "rust" }, + -- Or use a function for more flexibility, e.g. to disable slow treesitter highlight for large files + disable = function(lang, buf) + local max_filesize = 100 * 1024 -- 100 KB + local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf)) + if ok and stats and stats.size > max_filesize then + return true + end + end, + + -- Setting this to true will run `:h syntax` and tree-sitter at the same time. + -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation). + -- Using this option may slow down your editor, and you may see some duplicate highlights. + -- Instead of true it can also be a list of languages + additional_vim_regex_highlighting = false, + }, +} +``` + +Each module can also be enabled or disabled interactively through the following commands: + +```vim +:TSBufEnable {module} " enable module on current buffer +:TSBufDisable {module} " disable module on current buffer +:TSEnable {module} [{ft}] " enable module on every buffer. If filetype is specified, enable only for this filetype. +:TSDisable {module} [{ft}] " disable module on every buffer. If filetype is specified, disable only for this filetype. +:TSModuleInfo [{module}] " list information about modules state for each filetype +``` + +Check [`:h nvim-treesitter-commands`](doc/nvim-treesitter.txt) for a list of all available commands. +It may be necessary to reload the buffer (e.g., via `:e`) after enabling a module interactively. + +# Supported languages + +For `nvim-treesitter` to support a specific feature for a specific language requires both a parser for that language and an appropriate language-specific query file for that feature. + +The following is a list of languages for which a parser can be installed through `:TSInstall`; a checked box means that `nvim-treesitter` also contains queries at least for the `highlight` module. + +Experimental parsers are parsers that have a maintainer but are not stable enough for +daily use yet. + +We are looking for maintainers to add more parsers and to write query files for their languages. Check our [tracking issue](https://github.com/nvim-treesitter/nvim-treesitter/issues/2282) for open language requests. + + + +- [x] [ada](https://github.com/briot/tree-sitter-ada) (maintained by @briot) +- [x] [agda](https://github.com/tree-sitter/tree-sitter-agda) (maintained by @Decodetalkers) +- [x] [angular](https://github.com/dlvandenberg/tree-sitter-angular) (experimental, maintained by @dlvandenberg) +- [x] [apex](https://github.com/aheber/tree-sitter-sfapex) (maintained by @aheber) +- [x] [arduino](https://github.com/ObserverOfTime/tree-sitter-arduino) (maintained by @ObserverOfTime) +- [x] [asm](https://github.com/RubixDev/tree-sitter-asm) (maintained by @RubixDev) +- [x] [astro](https://github.com/virchau13/tree-sitter-astro) (maintained by @virchau13) +- [x] [authzed](https://github.com/mleonidas/tree-sitter-authzed) (maintained by @mattpolzin) +- [ ] [awk](https://github.com/Beaglefoot/tree-sitter-awk) +- [x] [bash](https://github.com/tree-sitter/tree-sitter-bash) (maintained by @TravonteD) +- [x] [bass](https://github.com/vito/tree-sitter-bass) (maintained by @amaanq) +- [x] [beancount](https://github.com/polarmutex/tree-sitter-beancount) (maintained by @polarmutex) +- [x] [bibtex](https://github.com/latex-lsp/tree-sitter-bibtex) (maintained by @theHamsta, @clason) +- [x] [bicep](https://github.com/amaanq/tree-sitter-bicep) (maintained by @amaanq) +- [x] [bitbake](https://github.com/amaanq/tree-sitter-bitbake) (maintained by @amaanq) +- [x] [blueprint](https://gitlab.com/gabmus/tree-sitter-blueprint.git) (experimental, maintained by @gabmus) +- [x] [bp](https://github.com/ambroisie/tree-sitter-bp) (maintained by @ambroisie) +- [x] [c](https://github.com/tree-sitter/tree-sitter-c) (maintained by @amaanq) +- [x] [c_sharp](https://github.com/tree-sitter/tree-sitter-c-sharp) (maintained by @amaanq) +- [x] [cairo](https://github.com/amaanq/tree-sitter-cairo) (maintained by @amaanq) +- [x] [capnp](https://github.com/amaanq/tree-sitter-capnp) (maintained by @amaanq) +- [x] [chatito](https://github.com/ObserverOfTime/tree-sitter-chatito) (maintained by @ObserverOfTime) +- [x] [clojure](https://github.com/sogaiu/tree-sitter-clojure) (maintained by @NoahTheDuke) +- [x] [cmake](https://github.com/uyha/tree-sitter-cmake) (maintained by @uyha) +- [x] [comment](https://github.com/stsewd/tree-sitter-comment) (maintained by @stsewd) +- [x] [commonlisp](https://github.com/theHamsta/tree-sitter-commonlisp) (maintained by @theHamsta) +- [x] [cooklang](https://github.com/addcninblue/tree-sitter-cooklang) (maintained by @addcninblue) +- [x] [corn](https://github.com/jakestanger/tree-sitter-corn) (maintained by @jakestanger) +- [x] [cpon](https://github.com/amaanq/tree-sitter-cpon) (maintained by @amaanq) +- [x] [cpp](https://github.com/tree-sitter/tree-sitter-cpp) (maintained by @theHamsta) +- [x] [css](https://github.com/tree-sitter/tree-sitter-css) (maintained by @TravonteD) +- [x] [csv](https://github.com/amaanq/tree-sitter-csv) (maintained by @amaanq) +- [x] [cuda](https://github.com/theHamsta/tree-sitter-cuda) (maintained by @theHamsta) +- [x] [cue](https://github.com/eonpatapon/tree-sitter-cue) (maintained by @amaanq) +- [x] [d](https://github.com/gdamore/tree-sitter-d) (maintained by @amaanq) +- [x] [dart](https://github.com/UserNobody14/tree-sitter-dart) (maintained by @akinsho) +- [x] [devicetree](https://github.com/joelspadin/tree-sitter-devicetree) (maintained by @jedrzejboczar) +- [x] [dhall](https://github.com/jbellerb/tree-sitter-dhall) (maintained by @amaanq) +- [x] [diff](https://github.com/the-mikedavis/tree-sitter-diff) (maintained by @gbprod) +- [x] [disassembly](https://github.com/ColinKennedy/tree-sitter-disassembly) (maintained by @ColinKennedy) +- [x] [djot](https://github.com/treeman/tree-sitter-djot) (maintained by @NoahTheDuke) +- [x] [dockerfile](https://github.com/camdencheek/tree-sitter-dockerfile) (maintained by @camdencheek) +- [x] [dot](https://github.com/rydesun/tree-sitter-dot) (maintained by @rydesun) +- [x] [doxygen](https://github.com/amaanq/tree-sitter-doxygen) (maintained by @amaanq) +- [x] [dtd](https://github.com/tree-sitter-grammars/tree-sitter-xml) (maintained by @ObserverOfTime) +- [x] [earthfile](https://github.com/glehmann/tree-sitter-earthfile) (maintained by @glehmann) +- [x] [ebnf](https://github.com/RubixDev/ebnf) (experimental, maintained by @RubixDev) +- [x] [eds](https://github.com/uyha/tree-sitter-eds) (maintained by @uyha) +- [x] [eex](https://github.com/connorlay/tree-sitter-eex) (maintained by @connorlay) +- [x] [elixir](https://github.com/elixir-lang/tree-sitter-elixir) (maintained by @connorlay) +- [x] [elm](https://github.com/elm-tooling/tree-sitter-elm) (maintained by @zweimach) +- [x] [elsa](https://github.com/glapa-grossklag/tree-sitter-elsa) (maintained by @glapa-grossklag, @amaanq) +- [x] [elvish](https://github.com/elves/tree-sitter-elvish) (maintained by @elves) +- [ ] [embedded_template](https://github.com/tree-sitter/tree-sitter-embedded-template) +- [x] [erlang](https://github.com/WhatsApp/tree-sitter-erlang) (maintained by @filmor) +- [x] [facility](https://github.com/FacilityApi/tree-sitter-facility) (maintained by @bryankenote) +- [x] [faust](https://github.com/khiner/tree-sitter-faust) (maintained by @khiner) +- [x] [fennel](https://github.com/alexmozaidze/tree-sitter-fennel) (maintained by @alexmozaidze) +- [x] [fidl](https://github.com/google/tree-sitter-fidl) (maintained by @chaopeng) +- [x] [firrtl](https://github.com/amaanq/tree-sitter-firrtl) (maintained by @amaanq) +- [x] [fish](https://github.com/ram02z/tree-sitter-fish) (maintained by @ram02z) +- [x] [foam](https://github.com/FoamScience/tree-sitter-foam) (experimental, maintained by @FoamScience) +- [x] [forth](https://github.com/AlexanderBrevig/tree-sitter-forth) (maintained by @amaanq) +- [x] [fortran](https://github.com/stadelmanma/tree-sitter-fortran) (maintained by @amaanq) +- [x] [fsh](https://github.com/mgramigna/tree-sitter-fsh) (maintained by @mgramigna) +- [x] [func](https://github.com/amaanq/tree-sitter-func) (maintained by @amaanq) +- [x] [fusion](https://gitlab.com/jirgn/tree-sitter-fusion.git) (maintained by @jirgn) +- [x] [Godot (gdscript)](https://github.com/PrestonKnopp/tree-sitter-gdscript) (maintained by @PrestonKnopp) +- [x] [gdshader](https://github.com/GodOfAvacyn/tree-sitter-gdshader) (maintained by @godofavacyn) +- [x] [git_config](https://github.com/the-mikedavis/tree-sitter-git-config) (maintained by @amaanq) +- [x] [git_rebase](https://github.com/the-mikedavis/tree-sitter-git-rebase) (maintained by @gbprod) +- [x] [gitattributes](https://github.com/ObserverOfTime/tree-sitter-gitattributes) (maintained by @ObserverOfTime) +- [x] [gitcommit](https://github.com/gbprod/tree-sitter-gitcommit) (maintained by @gbprod) +- [x] [gitignore](https://github.com/shunsambongi/tree-sitter-gitignore) (maintained by @theHamsta) +- [x] [gleam](https://github.com/gleam-lang/tree-sitter-gleam) (maintained by @amaanq) +- [x] [Glimmer and Ember](https://github.com/alexlafroscia/tree-sitter-glimmer) (maintained by @NullVoxPopuli) +- [x] [glsl](https://github.com/theHamsta/tree-sitter-glsl) (maintained by @theHamsta) +- [x] [GN (Generate Ninja)](https://github.com/amaanq/tree-sitter-gn) (maintained by @amaanq) +- [x] [gnuplot](https://github.com/dpezto/tree-sitter-gnuplot) (maintained by @dpezto) +- [x] [go](https://github.com/tree-sitter/tree-sitter-go) (maintained by @theHamsta, @WinWisely268) +- [x] [Godot Resources (gdresource)](https://github.com/PrestonKnopp/tree-sitter-godot-resource) (maintained by @pierpo) +- [x] [gomod](https://github.com/camdencheek/tree-sitter-go-mod) (maintained by @camdencheek) +- [x] [gosum](https://github.com/amaanq/tree-sitter-go-sum) (maintained by @amaanq) +- [x] [gotmpl](https://github.com/ngalaiko/tree-sitter-go-template) (maintained by @qvalentin) +- [x] [gowork](https://github.com/omertuc/tree-sitter-go-work) (maintained by @omertuc) +- [x] [gpg](https://github.com/ObserverOfTime/tree-sitter-gpg-config) (maintained by @ObserverOfTime) +- [x] [graphql](https://github.com/bkegley/tree-sitter-graphql) (maintained by @bkegley) +- [x] [groovy](https://github.com/murtaza64/tree-sitter-groovy) (maintained by @murtaza64) +- [x] [gstlaunch](https://github.com/theHamsta/tree-sitter-gstlaunch) (maintained by @theHamsta) +- [ ] [hack](https://github.com/slackhq/tree-sitter-hack) +- [x] [hare](https://github.com/amaanq/tree-sitter-hare) (maintained by @amaanq) +- [x] [haskell](https://github.com/tree-sitter/tree-sitter-haskell) (maintained by @mrcjkb) +- [x] [haskell_persistent](https://github.com/MercuryTechnologies/tree-sitter-haskell-persistent) (maintained by @lykahb) +- [x] [hcl](https://github.com/MichaHoffmann/tree-sitter-hcl) (maintained by @MichaHoffmann) +- [x] [heex](https://github.com/connorlay/tree-sitter-heex) (maintained by @connorlay) +- [x] [helm](https://github.com/ngalaiko/tree-sitter-go-template) (maintained by @qvalentin) +- [x] [hjson](https://github.com/winston0410/tree-sitter-hjson) (maintained by @winston0410) +- [x] [hlsl](https://github.com/theHamsta/tree-sitter-hlsl) (maintained by @theHamsta) +- [x] [hlsplaylist](https://github.com/Freed-Wu/tree-sitter-hlsplaylist) (maintained by @Freed-Wu) +- [x] [hocon](https://github.com/antosha417/tree-sitter-hocon) (maintained by @antosha417) +- [x] [hoon](https://github.com/urbit-pilled/tree-sitter-hoon) (experimental, maintained by @urbit-pilled) +- [x] [html](https://github.com/tree-sitter/tree-sitter-html) (maintained by @TravonteD) +- [x] [htmldjango](https://github.com/interdependence/tree-sitter-htmldjango) (experimental, maintained by @ObserverOfTime) +- [x] [http](https://github.com/rest-nvim/tree-sitter-http) (maintained by @amaanq, @NTBBloodbath) +- [x] [hurl](https://github.com/pfeiferj/tree-sitter-hurl) (maintained by @pfeiferj) +- [x] [hyprlang](https://github.com/luckasRanarison/tree-sitter-hyprlang) (maintained by @luckasRanarison) +- [x] [idl](https://github.com/cathaysia/tree-sitter-idl) (maintained by @cathaysa) +- [x] [ini](https://github.com/justinmk/tree-sitter-ini) (experimental, maintained by @theHamsta) +- [x] [inko](https://github.com/inko-lang/tree-sitter-inko) (maintained by @yorickpeterse) +- [x] [ispc](https://github.com/fab4100/tree-sitter-ispc) (maintained by @fab4100) +- [x] [janet_simple](https://github.com/sogaiu/tree-sitter-janet-simple) (maintained by @sogaiu) +- [x] [java](https://github.com/tree-sitter/tree-sitter-java) (maintained by @p00f) +- [x] [javascript](https://github.com/tree-sitter/tree-sitter-javascript) (maintained by @steelsojka) +- [x] [jq](https://github.com/flurie/tree-sitter-jq) (maintained by @ObserverOfTime) +- [x] [jsdoc](https://github.com/tree-sitter/tree-sitter-jsdoc) (maintained by @steelsojka) +- [x] [json](https://github.com/tree-sitter/tree-sitter-json) (maintained by @steelsojka) +- [x] [json5](https://github.com/Joakker/tree-sitter-json5) (maintained by @Joakker) +- [x] [JSON with comments](https://gitlab.com/WhyNotHugo/tree-sitter-jsonc.git) (maintained by @WhyNotHugo) +- [x] [jsonnet](https://github.com/sourcegraph/tree-sitter-jsonnet) (maintained by @nawordar) +- [x] [julia](https://github.com/tree-sitter/tree-sitter-julia) (maintained by @theHamsta) +- [x] [just](https://github.com/IndianBoy42/tree-sitter-just) (maintained by @Hubro) +- [x] [kconfig](https://github.com/amaanq/tree-sitter-kconfig) (maintained by @amaanq) +- [x] [kdl](https://github.com/amaanq/tree-sitter-kdl) (maintained by @amaanq) +- [x] [kotlin](https://github.com/fwcd/tree-sitter-kotlin) (maintained by @SalBakraa) +- [x] [koto](https://github.com/koto-lang/tree-sitter-koto) (maintained by @irh) +- [x] [kusto](https://github.com/Willem-J-an/tree-sitter-kusto) (maintained by @Willem-J-an) +- [x] [lalrpop](https://github.com/traxys/tree-sitter-lalrpop) (maintained by @traxys) +- [x] [latex](https://github.com/latex-lsp/tree-sitter-latex) (maintained by @theHamsta, @clason) +- [x] [ledger](https://github.com/cbarrete/tree-sitter-ledger) (maintained by @cbarrete) +- [x] [leo](https://github.com/r001/tree-sitter-leo) (maintained by @r001) +- [x] [linkerscript](https://github.com/amaanq/tree-sitter-linkerscript) (maintained by @amaanq) +- [x] [liquid](https://github.com/hankthetank27/tree-sitter-liquid) (maintained by @hankthetank27) +- [x] [liquidsoap](https://github.com/savonet/tree-sitter-liquidsoap) (maintained by @toots) +- [x] [llvm](https://github.com/benwilliamgraham/tree-sitter-llvm) (maintained by @benwilliamgraham) +- [x] [lua](https://github.com/MunifTanjim/tree-sitter-lua) (maintained by @muniftanjim) +- [x] [luadoc](https://github.com/amaanq/tree-sitter-luadoc) (maintained by @amaanq) +- [x] [lua patterns](https://github.com/amaanq/tree-sitter-luap) (maintained by @amaanq) +- [x] [luau](https://github.com/amaanq/tree-sitter-luau) (maintained by @amaanq) +- [x] [m68k](https://github.com/grahambates/tree-sitter-m68k) (maintained by @grahambates) +- [x] [make](https://github.com/alemuller/tree-sitter-make) (maintained by @lewis6991) +- [x] [markdown (basic highlighting)](https://github.com/MDeiml/tree-sitter-markdown) (experimental, maintained by @MDeiml) +- [x] [markdown_inline (needed for full highlighting)](https://github.com/MDeiml/tree-sitter-markdown) (experimental, maintained by @MDeiml) +- [x] [matlab](https://github.com/acristoffers/tree-sitter-matlab) (maintained by @acristoffers) +- [x] [menhir](https://github.com/Kerl13/tree-sitter-menhir) (maintained by @Kerl13) +- [ ] [mermaid](https://github.com/monaqa/tree-sitter-mermaid) (experimental) +- [x] [meson](https://github.com/Decodetalkers/tree-sitter-meson) (maintained by @Decodetalkers) +- [x] [mlir](https://github.com/artagnon/tree-sitter-mlir) (experimental, maintained by @artagnon) +- [x] [muttrc](https://github.com/neomutt/tree-sitter-muttrc) (maintained by @Freed-Wu) +- [x] [nasm](https://github.com/naclsn/tree-sitter-nasm) (maintained by @ObserverOfTime) +- [ ] [nickel](https://github.com/nickel-lang/tree-sitter-nickel) +- [x] [nim](https://github.com/alaviss/tree-sitter-nim) (maintained by @aMOPel) +- [x] [nim_format_string](https://github.com/aMOPel/tree-sitter-nim-format-string) (maintained by @aMOPel) +- [x] [ninja](https://github.com/alemuller/tree-sitter-ninja) (maintained by @alemuller) +- [x] [nix](https://github.com/cstrahan/tree-sitter-nix) (maintained by @leo60228) +- [x] [norg](https://github.com/nvim-neorg/tree-sitter-norg) (maintained by @JoeyGrajciar, @vhyrro) +- [x] [nqc](https://github.com/amaanq/tree-sitter-nqc) (maintained by @amaanq) +- [x] [objc](https://github.com/amaanq/tree-sitter-objc) (maintained by @amaanq) +- [x] [objdump](https://github.com/ColinKennedy/tree-sitter-objdump) (maintained by @ColinKennedy) +- [x] [ocaml](https://github.com/tree-sitter/tree-sitter-ocaml) (maintained by @undu) +- [x] [ocaml_interface](https://github.com/tree-sitter/tree-sitter-ocaml) (maintained by @undu) +- [x] [ocamllex](https://github.com/atom-ocaml/tree-sitter-ocamllex) (maintained by @undu) +- [x] [odin](https://github.com/amaanq/tree-sitter-odin) (maintained by @amaanq) +- [ ] [org](https://github.com/milisims/tree-sitter-org) +- [x] [pascal](https://github.com/Isopod/tree-sitter-pascal.git) (maintained by @Isopod) +- [x] [passwd](https://github.com/ath3/tree-sitter-passwd) (maintained by @amaanq) +- [x] [pem](https://github.com/ObserverOfTime/tree-sitter-pem) (maintained by @ObserverOfTime) +- [x] [perl](https://github.com/tree-sitter-perl/tree-sitter-perl) (maintained by @RabbiVeesh, @LeoNerd) +- [x] [php](https://github.com/tree-sitter/tree-sitter-php) (maintained by @tk-shirasaka) +- [x] [php_only](https://github.com/tree-sitter/tree-sitter-php) (maintained by @tk-shirasaka) +- [x] [phpdoc](https://github.com/claytonrcarter/tree-sitter-phpdoc) (experimental, maintained by @mikehaertl) +- [x] [pioasm](https://github.com/leo60228/tree-sitter-pioasm) (maintained by @leo60228) +- [x] [po](https://github.com/erasin/tree-sitter-po) (maintained by @amaanq) +- [x] [pod](https://github.com/tree-sitter-perl/tree-sitter-pod) (maintained by @RabbiVeesh, @LeoNerd) +- [x] [Path of Exile item filter](https://github.com/ObserverOfTime/tree-sitter-poe-filter) (experimental, maintained by @ObserverOfTime) +- [x] [pony](https://github.com/amaanq/tree-sitter-pony) (maintained by @amaanq, @mfelsche) +- [x] [printf](https://github.com/ObserverOfTime/tree-sitter-printf) (maintained by @ObserverOfTime) +- [x] [prisma](https://github.com/victorhqc/tree-sitter-prisma) (maintained by @elianiva) +- [x] [promql](https://github.com/MichaHoffmann/tree-sitter-promql) (maintained by @MichaHoffmann) +- [x] [properties](https://github.com/tree-sitter-grammars/tree-sitter-properties) (maintained by @ObserverOfTime) +- [x] [proto](https://github.com/treywood/tree-sitter-proto) (maintained by @treywood) +- [x] [prql](https://github.com/PRQL/tree-sitter-prql) (maintained by @matthias-Q) +- [x] [psv](https://github.com/amaanq/tree-sitter-csv) (maintained by @amaanq) +- [x] [pug](https://github.com/zealot128/tree-sitter-pug) (experimental, maintained by @zealot128) +- [x] [puppet](https://github.com/amaanq/tree-sitter-puppet) (maintained by @amaanq) +- [x] [purescript](https://github.com/postsolar/tree-sitter-purescript) (maintained by @postsolar) +- [x] [PyPA manifest](https://github.com/ObserverOfTime/tree-sitter-pymanifest) (maintained by @ObserverOfTime) +- [x] [python](https://github.com/tree-sitter/tree-sitter-python) (maintained by @stsewd, @theHamsta) +- [x] [ql](https://github.com/tree-sitter/tree-sitter-ql) (maintained by @pwntester) +- [x] [qmldir](https://github.com/Decodetalkers/tree-sitter-qmldir) (maintained by @amaanq) +- [x] [qmljs](https://github.com/yuja/tree-sitter-qmljs) (maintained by @Decodetalkers) +- [x] [Tree-Sitter query language](https://github.com/nvim-treesitter/tree-sitter-query) (maintained by @steelsojka) +- [x] [r](https://github.com/r-lib/tree-sitter-r) (maintained by @echasnovski) +- [ ] [racket](https://github.com/6cdh/tree-sitter-racket) +- [x] [rasi](https://github.com/Fymyte/tree-sitter-rasi) (maintained by @Fymyte) +- [x] [rbs](https://github.com/joker1007/tree-sitter-rbs) (maintained by @joker1007) +- [x] [re2c](https://github.com/amaanq/tree-sitter-re2c) (maintained by @amaanq) +- [x] [readline](https://github.com/ribru17/tree-sitter-readline) (maintained by @ribru17) +- [x] [regex](https://github.com/tree-sitter/tree-sitter-regex) (maintained by @theHamsta) +- [x] [rego](https://github.com/FallenAngel97/tree-sitter-rego) (maintained by @FallenAngel97) +- [x] [pip requirements](https://github.com/ObserverOfTime/tree-sitter-requirements) (maintained by @ObserverOfTime) +- [x] [rnoweb](https://github.com/bamonroe/tree-sitter-rnoweb) (maintained by @bamonroe) +- [x] [robot](https://github.com/Hubro/tree-sitter-robot) (maintained by @Hubro) +- [x] [roc](https://github.com/nat-418/tree-sitter-roc) (maintained by @nat-418) +- [x] [ron](https://github.com/amaanq/tree-sitter-ron) (maintained by @amaanq) +- [x] [rst](https://github.com/stsewd/tree-sitter-rst) (maintained by @stsewd) +- [x] [ruby](https://github.com/tree-sitter/tree-sitter-ruby) (maintained by @TravonteD) +- [x] [rust](https://github.com/tree-sitter/tree-sitter-rust) (maintained by @amaanq) +- [x] [scala](https://github.com/tree-sitter/tree-sitter-scala) (maintained by @stevanmilic) +- [x] [scfg](https://git.sr.ht/~rockorager/tree-sitter-scfg) (maintained by @WhyNotHugo) +- [ ] [scheme](https://github.com/6cdh/tree-sitter-scheme) +- [x] [scss](https://github.com/serenadeai/tree-sitter-scss) (maintained by @elianiva) +- [x] [slang](https://github.com/theHamsta/tree-sitter-slang) (experimental, maintained by @theHamsta) +- [x] [slint](https://github.com/slint-ui/tree-sitter-slint) (maintained by @hunger) +- [x] [smali](https://github.com/tree-sitter-grammars/tree-sitter-smali) (maintained by @amaanq) +- [x] [smithy](https://github.com/indoorvivants/tree-sitter-smithy) (maintained by @amaanq, @keynmol) +- [ ] [snakemake](https://github.com/osthomas/tree-sitter-snakemake) (experimental) +- [x] [solidity](https://github.com/JoranHonig/tree-sitter-solidity) (maintained by @amaanq) +- [x] [soql](https://github.com/aheber/tree-sitter-sfapex) (maintained by @aheber) +- [x] [sosl](https://github.com/aheber/tree-sitter-sfapex) (maintained by @aheber) +- [x] [sourcepawn](https://github.com/nilshelmig/tree-sitter-sourcepawn) (maintained by @Sarrus1) +- [x] [sparql](https://github.com/BonaBeavis/tree-sitter-sparql) (maintained by @BonaBeavis) +- [x] [sql](https://github.com/derekstride/tree-sitter-sql) (maintained by @derekstride) +- [x] [squirrel](https://github.com/amaanq/tree-sitter-squirrel) (maintained by @amaanq) +- [x] [ssh_config](https://github.com/ObserverOfTime/tree-sitter-ssh-config) (maintained by @ObserverOfTime) +- [x] [starlark](https://github.com/amaanq/tree-sitter-starlark) (maintained by @amaanq) +- [x] [strace](https://github.com/sigmaSd/tree-sitter-strace) (maintained by @amaanq) +- [x] [styled](https://github.com/mskelton/tree-sitter-styled) (maintained by @mskelton) +- [x] [supercollider](https://github.com/madskjeldgaard/tree-sitter-supercollider) (maintained by @madskjeldgaard) +- [x] [surface](https://github.com/connorlay/tree-sitter-surface) (maintained by @connorlay) +- [x] [svelte](https://github.com/tree-sitter-grammars/tree-sitter-svelte) (maintained by @amaanq) +- [x] [swift](https://github.com/alex-pinkus/tree-sitter-swift) (maintained by @alex-pinkus) +- [x] [sxhkdrc](https://github.com/RaafatTurki/tree-sitter-sxhkdrc) (maintained by @RaafatTurki) +- [x] [systemtap](https://github.com/ok-ryoko/tree-sitter-systemtap) (maintained by @ok-ryoko) +- [x] [t32](https://gitlab.com/xasc/tree-sitter-t32.git) (maintained by @xasc) +- [x] [tablegen](https://github.com/amaanq/tree-sitter-tablegen) (maintained by @amaanq) +- [x] [tact](https://github.com/tact-lang/tree-sitter-tact) (maintained by @novusnota) +- [x] [tcl](https://github.com/tree-sitter-grammars/tree-sitter-tcl) (maintained by @lewis6991) +- [x] [teal](https://github.com/euclidianAce/tree-sitter-teal) (maintained by @euclidianAce) +- [x] [templ](https://github.com/vrischmann/tree-sitter-templ) (maintained by @vrischmann) +- [x] [terraform](https://github.com/MichaHoffmann/tree-sitter-hcl) (maintained by @MichaHoffmann) +- [x] [textproto](https://github.com/PorterAtGoogle/tree-sitter-textproto) (maintained by @Porter) +- [x] [thrift](https://github.com/duskmoon314/tree-sitter-thrift) (maintained by @amaanq, @duskmoon314) +- [x] [tiger](https://github.com/ambroisie/tree-sitter-tiger) (maintained by @ambroisie) +- [x] [tlaplus](https://github.com/tlaplus-community/tree-sitter-tlaplus) (maintained by @ahelwer, @susliko) +- [x] [tmux](https://github.com/Freed-Wu/tree-sitter-tmux) (maintained by @Freed-Wu) +- [x] [todotxt](https://github.com/arnarg/tree-sitter-todotxt.git) (experimental, maintained by @arnarg) +- [x] [toml](https://github.com/tree-sitter-grammars/tree-sitter-toml) (maintained by @tk-shirasaka) +- [x] [tsv](https://github.com/amaanq/tree-sitter-csv) (maintained by @amaanq) +- [x] [tsx](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka) +- [x] [turtle](https://github.com/BonaBeavis/tree-sitter-turtle) (maintained by @BonaBeavis) +- [x] [twig](https://github.com/gbprod/tree-sitter-twig) (maintained by @gbprod) +- [x] [typescript](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka) +- [x] [typespec](https://github.com/happenslol/tree-sitter-typespec) (maintained by @happenslol) +- [x] [typoscript](https://github.com/Teddytrombone/tree-sitter-typoscript) (maintained by @Teddytrombone) +- [x] [typst](https://github.com/uben0/tree-sitter-typst) (maintained by @uben0, @RaafatTurki) +- [x] [udev](https://github.com/ObserverOfTime/tree-sitter-udev) (maintained by @ObserverOfTime) +- [x] [ungrammar](https://github.com/Philipp-M/tree-sitter-ungrammar) (maintained by @Philipp-M, @amaanq) +- [x] [unison](https://github.com/kylegoetz/tree-sitter-unison) (maintained by @tapegram) +- [x] [usd](https://github.com/ColinKennedy/tree-sitter-usd) (maintained by @ColinKennedy) +- [x] [uxn tal](https://github.com/amaanq/tree-sitter-uxntal) (maintained by @amaanq) +- [x] [v](https://github.com/vlang/v-analyzer) (maintained by @kkharji, @amaanq) +- [x] [vala](https://github.com/vala-lang/tree-sitter-vala) (maintained by @Prince781) +- [x] [vento](https://github.com/ventojs/tree-sitter-vento) (maintained by @wrapperup, @oscarotero) +- [x] [verilog](https://github.com/tree-sitter/tree-sitter-verilog) (maintained by @zegervdv) +- [x] [vhs](https://github.com/charmbracelet/tree-sitter-vhs) (maintained by @caarlos0) +- [x] [vim](https://github.com/neovim/tree-sitter-vim) (maintained by @clason) +- [x] [vimdoc](https://github.com/neovim/tree-sitter-vimdoc) (maintained by @clason) +- [x] [vue](https://github.com/tree-sitter-grammars/tree-sitter-vue) (maintained by @WhyNotHugo, @lucario387) +- [x] [wgsl](https://github.com/szebniok/tree-sitter-wgsl) (maintained by @szebniok) +- [x] [wgsl_bevy](https://github.com/theHamsta/tree-sitter-wgsl-bevy) (maintained by @theHamsta) +- [x] [wing](https://github.com/winglang/tree-sitter-wing) (maintained by @gshpychka, @MarkMcCulloh) +- [x] [wit](https://github.com/liamwh/tree-sitter-wit) (maintained by @liamwh) +- [x] [xcompose](https://github.com/ObserverOfTime/tree-sitter-xcompose) (maintained by @ObserverOfTime) +- [x] [xml](https://github.com/tree-sitter-grammars/tree-sitter-xml) (maintained by @ObserverOfTime) +- [x] [yaml](https://github.com/tree-sitter-grammars/tree-sitter-yaml) (maintained by @amaanq) +- [x] [yang](https://github.com/Hubro/tree-sitter-yang) (maintained by @Hubro) +- [x] [yuck](https://github.com/Philipp-M/tree-sitter-yuck) (maintained by @Philipp-M, @amaanq) +- [x] [zathurarc](https://github.com/Freed-Wu/tree-sitter-zathurarc) (maintained by @Freed-Wu) +- [x] [zig](https://github.com/maxxnino/tree-sitter-zig) (maintained by @maxxnino) + + +For related information on the supported languages, including related plugins, see [this wiki page](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Supported-Languages-Information). + +# Available modules + +Modules provide the top-level features of `nvim-treesitter`. +The following is a list of modules included in `nvim-treesitter` and their configuration via `init.lua` (where multiple modules can be combined in a single call to `setup`). +Note that not all modules work for all languages (depending on the queries available for them). +Additional modules can be provided as [external plugins](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Extra-modules-and-plugins). + +#### Highlight + +Consistent syntax highlighting. + +```lua +require'nvim-treesitter.configs'.setup { + highlight = { + enable = true, + -- Setting this to true will run `:h syntax` and tree-sitter at the same time. + -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation). + -- Using this option may slow down your editor, and you may see some duplicate highlights. + -- Instead of true it can also be a list of languages + additional_vim_regex_highlighting = false, + }, +} +``` + +To customize the syntax highlighting of a capture, simply define or link a highlight group of the same name: + +```lua +-- Highlight the @foo.bar capture group with the "Identifier" highlight group +vim.api.nvim_set_hl(0, "@foo.bar", { link = "Identifier" }) +``` + +For a language-specific highlight, append the name of the language: + +```lua +-- Highlight @foo.bar as "Identifier" only in Lua files +vim.api.nvim_set_hl(0, "@foo.bar.lua", { link = "Identifier" }) +``` + +See `:h treesitter-highlight-groups` for details. + +#### Incremental selection + +Incremental selection based on the named nodes from the grammar. + +```lua +require'nvim-treesitter.configs'.setup { + incremental_selection = { + enable = true, + keymaps = { + init_selection = "gnn", -- set to `false` to disable one of the mappings + node_incremental = "grn", + scope_incremental = "grc", + node_decremental = "grm", + }, + }, +} +``` + +#### Indentation + +Indentation based on treesitter for the `=` operator. +**NOTE: This is an experimental feature**. + +```lua +require'nvim-treesitter.configs'.setup { + indent = { + enable = true + } +} +``` + +#### Folding + +Tree-sitter based folding. _(Technically not a module because it's per windows and not per buffer.)_ + +```vim +set foldmethod=expr +set foldexpr=nvim_treesitter#foldexpr() +set nofoldenable " Disable folding at startup. +``` + +This will respect your `foldminlines` and `foldnestmax` settings. + +# Advanced setup + +## Changing the parser install directory + +If you want to install the parsers to a custom directory you can specify this +directory with `parser_install_dir` option in that is passed to `setup`. +`nvim-treesitter` will then install the parser files into this directory. + +This directory must be writeable and must be explicitly added to the +`runtimepath`. For example: + +```lua + vim.opt.runtimepath:append("/some/path/to/store/parsers") + + require'nvim-treesitter.configs'.setup { + parser_install_dir = "/some/path/to/store/parsers", + + ... + + } +``` + +If this option is not included in the setup options, or is explicitly set to +`nil` then the default install directories will be used. If this value is set +the default directories will be ignored. + +Bear in mind that any parser installed into a parser folder on the runtime path +will still be considered installed. (For example if +"~/.local/share/nvim/site/parser/c.so" exists then the "c" parser will be +considered installed, even though it is not in `parser_install_dir`) + +The default paths are: + +1. first the package folder. Where `nvim-treesitter` is installed. +2. second the site directory. This is the "site" subdirectory of `stdpath("data")`. + +## Adding parsers + +If you have a parser that is not on the list of supported languages (either as a repository on Github or in a local directory), you can add it manually for use by `nvim-treesitter` as follows: + +1. Clone the repository or [create a new project](https://tree-sitter.github.io/tree-sitter/creating-parsers#project-setup) in, say, `~/projects/tree-sitter-zimbu`. Make sure that the `tree-sitter-cli` executable is installed and in your path; see for installation instructions. +2. Run `tree-sitter generate` in this directory (followed by `tree-sitter test` for good measure). +3. Add the following snippet to your `init.lua`: + +```lua +local parser_config = require "nvim-treesitter.parsers".get_parser_configs() +parser_config.zimbu = { + install_info = { + url = "~/projects/tree-sitter-zimbu", -- local path or git repo + files = {"src/parser.c"}, -- note that some parsers also require src/scanner.c or src/scanner.cc + -- optional entries: + branch = "main", -- default branch in case of git repo if different from master + generate_requires_npm = false, -- if stand-alone parser without npm dependencies + requires_generate_from_grammar = false, -- if folder contains pre-generated src/parser.c + }, + filetype = "zu", -- if filetype does not match the parser name +} +``` + +If you wish to set a specific parser for a filetype, you should use `vim.treesitter.language.register()`: + +```lua +vim.treesitter.language.register('python', 'someft') -- the someft filetype will use the python parser and queries. +``` + +Note this requires Nvim v0.9. + +4. Start `nvim` and `:TSInstall zimbu`. + +You can also skip step 2 and use `:TSInstallFromGrammar zimbu` to install directly from a `grammar.js` in the top-level directory specified by `url`. +Once the parser is installed, you can update it (from the latest revision of the `main` branch if `url` is a Github repository) with `:TSUpdate zimbu`. + +Note that neither `:TSInstall` nor `:TSInstallFromGrammar` copy query files from the grammar repository. +If you want your installed grammar to be useful, you must manually [add query files](#adding-queries) to your local nvim-treesitter installation. +Note also that module functionality is only triggered if your language's filetype is correctly identified. +If Neovim does not detect your language's filetype by default, you can use [Neovim's `vim.filetype.add()`]() to add a custom detection rule. + +If you use a git repository for your parser and want to use a specific version, you can set the `revision` key +in the `install_info` table for you parser config. + +## Adding queries + +Queries are what `nvim-treesitter` uses to extract information from the syntax tree; +they are located in the `queries/{language}/*` runtime directories (see `:h rtp`), +like the `queries` folder of this plugin, e.g. `queries/{language}/{locals,highlights,textobjects}.scm`. +Other modules may require additional queries such as `folding.scm`. You can find a +list of all supported capture names in [CONTRIBUTING.md](https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md#parser-configurations). + +All queries found in the runtime directories will be combined. +By convention, if you want to write a query, use the `queries/` directory, +but if you want to extend a query use the `after/queries/` directory. + +If you want to completely override a query, you can use `:h set_query()`. +For example, to override the `injections` queries from `c` with your own: + +```lua +require("vim.treesitter.query").set_query("c", "injections", "(comment) @comment") +``` + +Note: when using `set_query`, all queries in the runtime directories will be ignored. + +## Adding modules + +If you wish you write your own module, you need to support + +- tree-sitter language detection support; +- attaching and detaching to buffers; +- all nvim-treesitter commands. + +At the top level, you can use the `define_modules` function to define one or more modules or module groups: + +```lua +require'nvim-treesitter'.define_modules { + my_cool_plugin = { + attach = function(bufnr, lang) + -- Do cool stuff here + end, + detach = function(bufnr) + -- Undo cool stuff here + end, + is_supported = function(lang) + -- Check if the language is supported + end + } +} +``` + +with the following properties: + +- `module_path` specifies a require path (string) that exports a module with an `attach` and `detach` function. This is not required if the functions are on this definition. +- `enable` determines if the module is enabled by default. This is usually overridden by the user. +- `disable` takes a list of languages that this module is disabled for. This is usually overridden by the user. +- `is_supported` takes a function that takes a language and determines if this module supports that language. +- `attach` takes a function that attaches to a buffer. This is required if `module_path` is not provided. +- `detach` takes a function that detaches from a buffer. This is required if `module_path` is not provided. + +# Extra features + +### Statusline indicator + +```vim +echo nvim_treesitter#statusline(90) " 90 can be any length +module->expression_statement->call->identifier +``` + +### Utilities + +You can get some utility functions with + +```lua +local ts_utils = require 'nvim-treesitter.ts_utils' +``` + +Check [`:h nvim-treesitter-utils`](doc/nvim-treesitter.txt) for more information. + +# Troubleshooting + +Before doing anything, make sure you have the latest version of this plugin and run `:checkhealth nvim-treesitter`. +It can also help to update the parsers via `:TSUpdate`. + +#### Feature `X` does not work for `{language}`... + +First, check the `health#nvim_treesitter#check` and the `health#treesitter#check` sections of `:checkhealth` for any warning. +If there is one, it's highly likely that this is the cause of the problem. + +Next check the `## Parser/Features` subsection of the `health#nvim_treesitter#check` section of `:checkhealth` to ensure the desired module is enabled for your language. +If not, you might be missing query files; see [Adding queries](#adding-queries). + +Finally, ensure Neovim is correctly identifying your language's filetype using the `:echo &filetype` command while one of your language's files is open in Neovim. +If not, add a short Vimscript file to nvim-treesitter's `ftdetect` runtime directory following [Neovim's documentation](https://neovim.io/doc/user/filetype.html#new-filetype) on filetype detection. +You can also quickly & temporarily set the filetype for a single buffer with the `:set filetype=langname` command to test whether it fixes the problem. + +If everything is okay, then it might be an actual error. +In that case, feel free to [open an issue here](https://github.com/nvim-treesitter/nvim-treesitter/issues/new/choose). + +#### I get `module 'vim.treesitter.query' not found` + +Make sure you have the latest version of Neovim. + +#### I get `Error detected while processing .../plugin/nvim-treesitter.vim` every time I open Neovim + +This is probably due to a change in a parser's grammar or its queries. +Try updating the parser that you suspect has changed (`:TSUpdate {language}`) or all of them (`:TSUpdate`). +If the error persists after updating all parsers, +please [open an issue](https://github.com/nvim-treesitter/nvim-treesitter/issues/new/choose). + +#### I get `query error: invalid node type at position` + +This could be due a query file outside this plugin using outdated nodes, +or due to an outdated parser. + +- Make sure you have the parsers up to date with `:TSUpdate` +- Make sure you don't have more than one `parser` runtime directory. + You can execute this command `:echo nvim_get_runtime_file('parser', v:true)` to find all runtime directories. + If you get more than one path, remove the ones that are outside this plugin (`nvim-treesitter` directory), + so the correct version of the parser is used. + +#### I experience weird highlighting issues similar to [#78](https://github.com/nvim-treesitter/nvim-treesitter/issues/78) + +This is a well known issue, which arises when the tree and the buffer have gotten out of sync. +As this is an upstream issue, we don't have any definite fix. +To get around this, you can force reparsing the buffer with + +```vim +:write | edit | TSBufEnable highlight +``` + +This will save, restore and enable highlighting for the current buffer. + +#### I experience bugs when using `nvim-treesitter`'s `foldexpr` similar to [#194](https://github.com/nvim-treesitter/nvim-treesitter/issues/194) + +This might happen, and is known to happen, with `vim-clap`. +To avoid these kind of errors, please use `setlocal` instead of `set` for the respective filetypes. + +#### I run into errors like `module 'nvim-treesitter.configs' not found` at startup + +This is because of `rtp` management in `nvim`, adding `packadd +nvim-treesitter` should fix the issue. + +#### I want to use Git instead of curl for downloading the parsers + +In your Lua config: + +```lua +require("nvim-treesitter.install").prefer_git = true +``` + +#### I want to use a HTTP proxy for downloading the parsers + +You can either configure curl to use additional CLI arguments in your Lua config: + +```lua +require("nvim-treesitter.install").command_extra_args = { + curl = { "--proxy", "" }, +} +``` + +or you can configure git via `.gitconfig` and use git instead of curl + +```lua +require("nvim-treesitter.install").prefer_git = true +``` + +#### I want to use a mirror instead of "https://github.com/" + +In your Lua config: + +```lua +for _, config in pairs(require("nvim-treesitter.parsers").get_parser_configs()) do + config.install_info.url = config.install_info.url:gsub("https://github.com/", "something else") +end + +require'nvim-treesitter.configs'.setup { + -- + -- +} +``` + +#### Using an existing parser for another filetype + +For example, to use the `bash` tree-sitter to highlight file with +`filetype=apkbuild`, use: + +```lua +vim.treesitter.language.register("bash", "apkbuild") +``` + +The `bash` tree-sitter must be installed following the usual procedure [as +described above](#language-parsers). diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/assets/logo.png b/config/neovim/store/lazy-plugins/nvim-treesitter/assets/logo.png new file mode 100644 index 00000000..a60e536b Binary files /dev/null and b/config/neovim/store/lazy-plugins/nvim-treesitter/assets/logo.png differ diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/autoload/nvim_treesitter.vim b/config/neovim/store/lazy-plugins/nvim-treesitter/autoload/nvim_treesitter.vim new file mode 100644 index 00000000..90953985 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/autoload/nvim_treesitter.vim @@ -0,0 +1,27 @@ +function! nvim_treesitter#statusline(...) abort + return luaeval("require'nvim-treesitter.statusline'.statusline(_A)", get(a:, 1, {})) +endfunction + +function! nvim_treesitter#foldexpr() abort + return luaeval(printf('require"nvim-treesitter.fold".get_fold_indic(%d)', v:lnum)) +endfunction + +function! nvim_treesitter#installable_parsers(arglead, cmdline, cursorpos) abort + return join(luaeval("require'nvim-treesitter.parsers'.available_parsers()") + ['all'], "\n") +endfunction + +function! nvim_treesitter#installed_parsers(arglead, cmdline, cursorpos) abort + return join(luaeval("require'nvim-treesitter.info'.installed_parsers()") + ['all'], "\n") +endfunction + +function! nvim_treesitter#available_modules(arglead, cmdline, cursorpos) abort + return join(luaeval("require'nvim-treesitter.configs'.available_modules()"), "\n") +endfunction + +function! nvim_treesitter#available_query_groups(arglead, cmdline, cursorpos) abort + return join(luaeval("require'nvim-treesitter.query'.available_query_groups()"), "\n") +endfunction + +function! nvim_treesitter#indent() abort + return luaeval(printf('require"nvim-treesitter.indent".get_indent(%d)', v:lnum)) +endfunction diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/contrib/nvim-treesitter-luarocks.template b/config/neovim/store/lazy-plugins/nvim-treesitter/contrib/nvim-treesitter-luarocks.template new file mode 100644 index 00000000..8109f5f2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/contrib/nvim-treesitter-luarocks.template @@ -0,0 +1,41 @@ +local git_ref = '$git_ref' +local modrev = '$modrev' +local specrev = '-1' + +local repo_url = '$repo_url' + +rockspec_format = '3.0' +package = '$package' +version = modrev .. specrev + +description = { + summary = 'Nvim Treesitter configurations and abstraction layer', + detailed = $detailed_description, + labels = { 'neovim' }, + homepage = 'https://github.com/nvim-treesitter/nvim-treesitter', + license = 'Apache-2.0', +} + +dependencies = { + 'lua >= 5.1', +} + +-- source = file:///. + +source = { + url = repo_url .. '/archive/' .. git_ref .. '.zip', + dir = '$repo_name-' .. '$archive_dir_suffix', +} + +build = { + type = 'make', + build_pass = false, + install_variables = { + INST_PREFIX='$(PREFIX)', + INST_BINDIR='$(BINDIR)', + INST_LIBDIR='$(LIBDIR)', + INST_LUADIR='$(LUADIR)', + INST_CONFDIR='$(CONFDIR)', + }, + copy_directories = $copy_directories, +} diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/contrib/nvim-treesitter-scm-1.rockspec b/config/neovim/store/lazy-plugins/nvim-treesitter/contrib/nvim-treesitter-scm-1.rockspec new file mode 100644 index 00000000..a901c5b1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/contrib/nvim-treesitter-scm-1.rockspec @@ -0,0 +1,36 @@ +local MODREV, SPECREV = 'scm', '-1' +rockspec_format = '3.0' +package = 'nvim-treesitter' +version = MODREV .. SPECREV + +description = { + summary = 'Nvim Treesitter configurations and abstraction layer', + labels = { 'neovim' }, + homepage = 'https://github.com/nvim-treesitter/nvim-treesitter', + license = 'Apache-2.0', +} + +dependencies = { + 'lua >= 5.1', +} + +source = { + url = 'git://github.com/nvim-treesitter/nvim-treesitter', +} + +build = { + type = 'make', + install_variables = { + INST_PREFIX='$(PREFIX)', + INST_BINDIR='$(BINDIR)', + INST_LIBDIR='$(LIBDIR)', + INST_LUADIR='$(LUADIR)', + INST_CONFDIR='$(CONFDIR)', + }, + copy_directories = { + 'autoload', + 'doc', + 'plugin', + 'queries' + } +} diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/doc/nvim-treesitter.txt b/config/neovim/store/lazy-plugins/nvim-treesitter/doc/nvim-treesitter.txt new file mode 100644 index 00000000..f7a91b84 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/doc/nvim-treesitter.txt @@ -0,0 +1,590 @@ +*nvim-treesitter* Treesitter configurations and abstraction layer for Neovim. + +Minimum version of neovim: nightly + +Authors: + Kiyan Yazdani + Thomas Vigouroux + Stephan Seitz + Steven Sojka + Santos Gallegos + https://github.com/nvim-treesitter/nvim-treesitter/graphs/contributors + + Type |gO| to see the table of contents. + +============================================================================== +INTRODUCTION *nvim-treesitter-intro* + +nvim-treesitter wraps the Neovim treesitter API to provide functionalities +such as highlighting and incremental selection, and a command to easily +install parsers. + +============================================================================== +QUICK START *nvim-treesitter-quickstart* + +Install the parser for your language + +> + :TSInstall {language} +< + +To get a list of supported languages + +> + :TSInstallInfo +< + +By default, everything is disabled. +To enable supported features, put this in your `init.lua` file: + +> + require'nvim-treesitter.configs'.setup { + -- A directory to install the parsers into. + -- If this is excluded or nil parsers are installed + -- to either the package dir, or the "site" dir. + -- If a custom path is used (not nil) it must be added to the runtimepath. + parser_install_dir = "/some/path/to/store/parsers", + + -- A list of parser names, or "all" + ensure_installed = { "c", "lua", "rust" }, + + -- Install parsers synchronously (only applied to `ensure_installed`) + sync_install = false, + + -- Automatically install missing parsers when entering buffer + auto_install = false, + + -- List of parsers to ignore installing (for "all") + ignore_install = { "javascript" }, + + highlight = { + -- `false` will disable the whole extension + enable = true, + + -- list of language that will be disabled + disable = { "c", "rust" }, + + -- Setting this to true will run `:h syntax` and tree-sitter at the same time. + -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation). + -- Using this option may slow down your editor, and you may see some duplicate highlights. + -- Instead of true it can also be a list of languages + additional_vim_regex_highlighting = false, + }, + } + vim.opt.runtimepath:append("/some/path/to/store/parsers") +< + +See |nvim-treesitter-modules| for a list of all available modules and its options. + +============================================================================== +MODULES *nvim-treesitter-modules* + +|nvim-treesitter| provides several functionalities via modules (and submodules), +each module makes use of the query files defined for each language, + +All modules are disabled by default, and some provide default keymaps. +Each module corresponds to an entry in the dictionary passed to the +`nvim-treesitter.configs.setup` function, this should be in your `init.lua` file. + +> + require'nvim-treesitter.configs'.setup { + -- Modules and its options go here + highlight = { enable = true }, + incremental_selection = { enable = true }, + textobjects = { enable = true }, + } +< + +All modules share some common options, like `enable` and `disable`. +When `enable` is `true` this will enable the module for all supported languages, +if you want to disable the module for some languages you can pass a list to the `disable` option. + +> + require'nvim-treesitter.configs'.setup { + highlight = { + enable = true, + disable = { "cpp", "lua" }, + }, + } +< + +For more fine-grained control, `disable` can also take a function and +whenever it returns `true`, the module is disabled for that buffer. +The function is called once when a module starts in a buffer and receives the +language and buffer number as arguments: + +> + require'nvim-treesitter.configs'.setup { + highlight = { + enable = true, + disable = function(lang, bufnr) -- Disable in large C++ buffers + return lang == "cpp" and vim.api.nvim_buf_line_count(bufnr) > 50000 + end, + }, + } +< + +Options that define or accept a keymap use the same format you use to define +keymaps in Neovim, so you can write keymaps as `gd`, `a`, `a` +`` (control + a), `` (alt + n), `` (enter), etc. + +External plugins can provide their own modules with their own options, +those can also be configured using the `nvim-treesitter.configs.setup` +function. + +------------------------------------------------------------------------------ +HIGHLIGHT *nvim-treesitter-highlight-mod* + +Consistent syntax highlighting. + +Query files: `highlights.scm`. +Supported options: + +- enable: `true` or `false`. +- disable: list of languages. +- additional_vim_regex_highlighting: `true` or `false`, or a list of languages. + Set this to `true` if you depend on 'syntax' being enabled + (like for indentation). Using this option may slow down your editor, + and you may see some duplicate highlights. + Defaults to `false`. + +> + require'nvim-treesitter.configs'.setup { + highlight = { + enable = true, + custom_captures = { + -- Highlight the @foo.bar capture group with the "Identifier" highlight group. + ["foo.bar"] = "Identifier", + }, + -- Setting this to true or a list of languages will run `:h syntax` and tree-sitter at the same time. + additional_vim_regex_highlighting = false, + }, + } +< + +You can also set custom highlight captures +> + lua < + require'nvim-treesitter.configs'.setup { + incremental_selection = { + enable = true, + keymaps = { + init_selection = "gnn", + node_incremental = "grn", + scope_incremental = "grc", + node_decremental = "grm", + }, + }, + } +< + +------------------------------------------------------------------------------ +INDENTATION *nvim-treesitter-indentation-mod* + +Indentation based on treesitter for the |=| operator. +NOTE: this is an experimental feature. + +Query files: `indents.scm`. +Supported options: +- enable: `true` or `false`. +- disable: list of languages. +> + require'nvim-treesitter.configs'.setup { + indent = { + enable = true + }, + } + +`@indent` *nvim-treesitter-indentation-queries* +Queries can use the following captures: `@indent.begin` and `@indent.dedent`, +`@indent.branch`, `@indent.end` or `@indent.align`. An `@indent.ignore` capture tells +treesitter to ignore indentation and a `@indent.zero` capture sets +the indentation to 0. + +`@indent.begin` *nvim-treesitter-indentation-indent.begin* +The `@indent.begin` specifies that the next line should be indented. Multiple +indents on the same line get collapsed. Eg. + +> + ( + (if_statement) + (ERROR "else") @indent.begin + ) +< +Indent can also have `indent.immediate` set using a `#set!` directive, which +permits the next line to indent even when the block intended to be indented +has no content yet, improving interactive typing. + +eg for python: +> + ((if_statement) @indent.begin + (#set! indent.immediate 1)) +< + +Will allow: +> + if True: + # Auto indent to here + +`@indent.end` *nvim-treesitter-indentation-indent.end* +An `@indent.end` capture is used to specify that the indented region ends and +any text subsequent to the capture should be dedented. + +`@indent.branch` *nvim-treesitter-indentation-indent.branch* +An `@indent.branch` capture is used to specify that a dedented region starts +at the line including the captured nodes. + +`@indent.dedent` *nvim-treesitter-indentation-indent.dedent* +A `@indent.dedent` capture specifies dedenting starting on the next line. +> +`@indent.align` *nvim-treesitter-indentation-aligned_indent.align* +Aligned indent blocks may be specified with the `@indent.align` capture. +This permits + +> + foo(a, + b, + c) +< +As well as +> + foo( + a, + b, + c) +< +and finally +> + foo( + a, + b, + c + ) +< +To specify the delimiters to use `indent.open_delimiter` and +`indent.close_delimiter` should be used. Eg. +> + ((argument_list) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) +< + +For some languages the last line of an `indent.align` block must not be +the same indent as the natural next line. + +For example in python: + +> + if (a > b and + c < d): + pass + +Is not correct, whereas +> + if (a > b and + c < d): + pass + +Would be correctly indented. This behavior may be chosen using +`indent.avoid_last_matching_next`. Eg. + +> + (if_statement + condition: (parenthesized_expression) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")") + (#set! indent.avoid_last_matching_next 1) + ) +< +Could be used to specify that the last line of an `@indent.align` capture +should be additionally indented to avoid clashing with the indent of the first +line of the block inside an if. + +============================================================================== +COMMANDS *nvim-treesitter-commands* + + *:TSInstall* +:TSInstall {language} ...~ + +Install one or more treesitter parsers. +You can use |:TSInstall| `all` to install all parsers. Use |:TSInstall!| to +force the reinstallation of already installed parsers. + *:TSInstallSync* +:TSInstallSync {language} ...~ + +Perform the |:TSInstall| operation synchronously. + + *:TSInstallInfo* +:TSInstallInfo~ + +List information about currently installed parsers + + *:TSUpdate* +:TSUpdate {language} ...~ + +Update the installed parser for one more {language} or all installed parsers +if {language} is omitted. The specified parser is installed if it is not already +installed. + + *:TSUpdateSync* +:TSUpdateSync {language} ...~ + +Perform the |:TSUpdate| operation synchronously. + + *:TSUninstall* +:TSUninstall {language} ...~ + +Deletes the parser for one or more {language}. You can use 'all' for language +to uninstall all parsers. + + *:TSBufEnable* +:TSBufEnable {module}~ + +Enable {module} on the current buffer. +A list of modules can be found at |:TSModuleInfo| + + *:TSBufDisable* +:TSBufDisable {module}~ + +Disable {module} on the current buffer. +A list of modules can be found at |:TSModuleInfo| + + *:TSBufToggle* +:TSBufToggle {module}~ + +Toggle (enable if disabled, disable if enabled) {module} on the current +buffer. +A list of modules can be found at |:TSModuleInfo| + + *:TSEnable* +:TSEnable {module} [{language}]~ + +Enable {module} for the session. +If {language} is specified, enable module for the session only for this +particular language. +A list of modules can be found at |:TSModuleInfo| +A list of languages can be found at |:TSInstallInfo| + + *:TSDisable* +:TSDisable {module} [{language}]~ + +Disable {module} for the session. +If {language} is specified, disable module for the session only for this +particular language. +A list of modules can be found at |:TSModuleInfo| +A list of languages can be found at |:TSInstallInfo| + + *:TSToggle* +:TSToggle {module} [{language}]~ + +Toggle (enable if disabled, disable if enabled) {module} for the session. +If {language} is specified, toggle module for the session only for this +particular language. +A list of modules can be found at |:TSModuleInfo| +A list of languages can be found at |:TSInstallInfo| + + *:TSModuleInfo* +:TSModuleInfo [{module}]~ + +List the state for the given module or all modules for the current session in +a new buffer. + +These highlight groups are used by default: +> + highlight default TSModuleInfoGood guifg=LightGreen gui=bold + highlight default TSModuleInfoBad guifg=Crimson + highlight default link TSModuleInfoHeader Type + highlight default link TSModuleInfoNamespace Statement + highlight default link TSModuleInfoParser Identifier +< + + *:TSEditQuery* +:TSEditQuery {query-group} [{lang}]~ + +Edit the query file for a {query-group} (e.g. highlights, locals) for given +{lang}. If there are multiple files, the user is prompted to select one of them. +If no such file exists, a buffer for a new file in the user's config directory +is created. If {lang} is not specified, the language of the current buffer +is used. + + *:TSEditQueryUserAfter* +:TSEditQueryUserAfter {query-group} [{lang}]~ + +Same as |:TSEditQuery| but edits a file in the `after` directory of the +user's config directory. Useful to add custom extensions for the queries +provided by a plugin. + +============================================================================== +UTILS *nvim-treesitter-utils* + +Nvim treesitter has some wrapper functions that you can retrieve with: +> + local ts_utils = require 'nvim-treesitter.ts_utils' +< +Methods + *ts_utils.get_node_at_cursor* +get_node_at_cursor(winnr)~ + +`winnr` will be 0 if nil. +Returns the node under the cursor. + + *ts_utils.is_parent* +is_parent(dest, source)~ + +Determines whether `dest` is a parent of `source`. +Returns a boolean. + + *ts_utils.get_named_children* +get_named_children(node)~ + +Returns a table of named children of `node`. + + *ts_utils.get_next_node* +get_next_node(node, allow_switch_parent, allow_next_parent)~ + +Returns the next node within the same parent. +If no node is found, returns `nil`. +If `allow_switch_parent` is true, it will allow switching parent +when the node is the last node. +If `allow_next_parent` is true, it will allow next parent if +the node is the last node and the next parent doesn't have children. + + *ts_utils.get_previous_node* +get_previous_node(node, allow_switch_parents, allow_prev_parent)~ + +Returns the previous node within the same parent. +`allow_switch_parent` and `allow_prev_parent` follow the same rule +as |ts_utils.get_next_node| but if the node is the first node. + + *ts_utils.goto_node* +goto_node(node, goto_end, avoid_set_jump)~ + +Sets cursor to the position of `node` in the current windows. +If `goto_end` is truthy, the cursor is set to the end the node range. +Setting `avoid_set_jump` to `true`, avoids setting the current cursor position +to the jump list. + + *ts_utils.swap_nodes* +swap_nodes(node_or_range1, node_or_range2, bufnr, cursor_to_second)~ + +Swaps the nodes or ranges. +set `cursor_to_second` to true to move the cursor to the second node + + *ts_utils.memoize_by_buf_tick* +memoize_by_buf_tick(fn, options)~ + +Caches the return value for a function and returns the cache value if the tick +of the buffer has not changed from the previous. + + `fn`: a function that takes any arguments + and returns a value to store. + `options?`: + - `bufnr`: a function/value that extracts the bufnr from the given arguments. + - `key`: a function/value that extracts the cache key from the given arguments. + `returns`: a function to call with bufnr as argument to + retrieve the value from the cache + + *ts_utils.node_to_lsp_range* +node_to_lsp_range(node)~ + +Get an lsp formatted range from a node range + + *ts_utils.node_length* +node_length(node)~ + +Get the byte length of node range + + *ts_utils.update_selection* +update_selection(buf, node)~ + +Set the selection to the node range + + *ts_utils.highlight_range* +highlight_range(range, buf, hl_namespace, hl_group)~ + +Set a highlight that spans the given range + + *ts_utils.highlight_node* +highlight_node(node, buf, hl_namespace, hl_group)~ + +Set a highlight that spans the given node's range + +============================================================================== +FUNCTIONS *nvim-treesitter-functions* + + *nvim_treesitter#statusline()* +nvim_treesitter#statusline(opts)~ + +Returns a string describing the current position in the file. This +could be used as a statusline indicator. +Default options (lua syntax): +> + { + indicator_size = 100, + type_patterns = {'class', 'function', 'method'}, + transform_fn = function(line, _node) return line:gsub('%s*[%[%(%{]*%s*$', '') end, + separator = ' -> ', + allow_duplicates = false + } +< +- `indicator_size` - How long should the string be. If longer, it is cut from + the beginning. +- `type_patterns` - Which node type patterns to match. +- `transform_fn` - Function used to transform the single item in line. By + default it removes opening brackets and spaces from end. Takes two arguments: + the text of the line in question, and the corresponding treesitter node. +- `separator` - Separator between nodes. +- `allow_duplicates` - Whether or not to remove duplicate components. + + *nvim_treesitter#foldexpr()* +nvim_treesitter#foldexpr()~ + +Functions to be used to determine the fold level at a given line number. +To use it: > + set foldmethod=expr + set foldexpr=nvim_treesitter#foldexpr() +< + +This will respect your 'foldminlines' and 'foldnestmax' settings. + +Note: This is highly experimental, and folding can break on some types of + edits. If you encounter such breakage, hitting `zx` should fix folding. + In any case, feel free to open an issue with the reproducing steps. + +============================================================================== +PERFORMANCE *nvim-treesitter-performance* + +`nvim-treesitter` checks the 'runtimepath' on startup in order to discover +available parsers and queries and index them. As a consequence, a very long +'runtimepath' might result in delayed startup times. + + +vim:tw=78:ts=8:expandtab:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lockfile.json b/config/neovim/store/lazy-plugins/nvim-treesitter/lockfile.json new file mode 100644 index 00000000..894fb999 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lockfile.json @@ -0,0 +1,839 @@ +{ + "ada": { + "revision": "e8e2515465cc2d7c444498e68bdb9f1d86767f95" + }, + "agda": { + "revision": "d3dc807692e6bca671d4491b3bf5c67eeca8c016" + }, + "angular": { + "revision": "10f21f3f1b10584e62ecc113ab3cda1196d0ceb8" + }, + "apex": { + "revision": "c99ad4b16d112fea91745e3f1b769754239fdaba" + }, + "arduino": { + "revision": "babb6d4da69b359bbb80adbf1fe39c0da9175210" + }, + "asm": { + "revision": "b0306e9bb2ebe01c6562f1aef265cc42ccc53070" + }, + "astro": { + "revision": "4be180759ec13651f72bacee65fa477c64222a1a" + }, + "authzed": { + "revision": "1dec7e1af96c56924e3322cd85fdce15d0a31d00" + }, + "awk": { + "revision": "ba7472152d79a8c916550c80fdbfd5724d07a0c9" + }, + "bash": { + "revision": "2fbd860f802802ca76a6661ce025b3a3bca2d3ed" + }, + "bass": { + "revision": "28dc7059722be090d04cd751aed915b2fee2f89a" + }, + "beancount": { + "revision": "c25f8034c977681653a8acd541c8b4877a58f474" + }, + "bibtex": { + "revision": "ccfd77db0ed799b6c22c214fe9d2937f47bc8b34" + }, + "bicep": { + "revision": "0092c7d1bd6bb22ce0a6f78497d50ea2b87f19c0" + }, + "bitbake": { + "revision": "a5d04fdb5a69a02b8fa8eb5525a60dfb5309b73b" + }, + "blueprint": { + "revision": "60ba73739c6083c693d86a1a7cf039c07eb4ed59" + }, + "bp": { + "revision": "2326d709fb9cf73cf124fdbc803c267f851721a4" + }, + "c": { + "revision": "82fb86aa544843bd17a9f0f3dc16edf645a34349" + }, + "c_sharp": { + "revision": "82fa8f05f41a33e9bc830f85d74a9548f0291738" + }, + "cairo": { + "revision": "6238f609bea233040fe927858156dee5515a0745" + }, + "capnp": { + "revision": "7b0883c03e5edd34ef7bcf703194204299d7099f" + }, + "chatito": { + "revision": "a461f20dedb43905febb12c1635bc7d2e43e96f0" + }, + "clojure": { + "revision": "f4236d4da8aa92bc105d9c118746474c608e6af7" + }, + "cmake": { + "revision": "20ffd6d3b4da1acdbf2d08204b2130a5b2f7c4b3" + }, + "comment": { + "revision": "5d8b29f6ef3bf64d59430dcfe76b31cc44b5abfd" + }, + "commonlisp": { + "revision": "bf2a65b1c119898a1a17389e07f2a399c05cdc0c" + }, + "cooklang": { + "revision": "4ebe237c1cf64cf3826fc249e9ec0988fe07e58e" + }, + "corn": { + "revision": "604d73c38d4c28ca68e9e441ffd405d68cb63051" + }, + "cpon": { + "revision": "594289eadfec719198e560f9d7fd243c4db678d5" + }, + "cpp": { + "revision": "2369fa991eba294e9238e28280ffcd58132f94bc" + }, + "css": { + "revision": "f6be52c3d1cdb1c5e4dd7d8bce0a57497f55d6af" + }, + "csv": { + "revision": "7eb7297823605392d2bbcc4c09b1cd18d6fa9529" + }, + "cuda": { + "revision": "e7878a9cf4157e9d6c8013ff5605c9f26d62894d" + }, + "cue": { + "revision": "8a5f273bfa281c66354da562f2307c2d394b6c81" + }, + "d": { + "revision": "750dde90ed9cdbd82493bc28478d8ab1976b0e9f" + }, + "dart": { + "revision": "ac0bb849ccd1a923963af47573b5e396736ff582" + }, + "devicetree": { + "revision": "fb07e6044ffd36932c57a5be01ba5d6b8a9337bb" + }, + "dhall": { + "revision": "affb6ee38d629c9296749767ab832d69bb0d9ea8" + }, + "diff": { + "revision": "629676fc3919606964231b2c7b9677d6998a2cb4" + }, + "disassembly": { + "revision": "0229c0211dba909c5d45129ac784a3f4d49c243a" + }, + "djot": { + "revision": "0e9a836ec47612ade15645fb1680adb549894a6c" + }, + "dockerfile": { + "revision": "087daa20438a6cc01fa5e6fe6906d77c869d19fe" + }, + "dot": { + "revision": "9ab85550c896d8b294d9b9ca1e30698736f08cea" + }, + "doxygen": { + "revision": "4a30eba5d047d6a8c5b005202b4848c0b33d76ca" + }, + "dtd": { + "revision": "648183d86f6f8ffb240ea11b4c6873f6f45d8b67" + }, + "earthfile": { + "revision": "91fc9434283aec06139e37fc007ad00922f278b4" + }, + "ebnf": { + "revision": "8e635b0b723c620774dfb8abf382a7f531894b40" + }, + "eds": { + "revision": "fde62029d4c715562230070b9af51a9500c2ce10" + }, + "eex": { + "revision": "f742f2fe327463335e8671a87c0b9b396905d1d1" + }, + "elixir": { + "revision": "de690fa8a028f122af46d9d2685679fe5f2d7d60" + }, + "elm": { + "revision": "09dbf221d7491dc8d8839616b27c21b9c025c457" + }, + "elsa": { + "revision": "0a66b2b3f3c1915e67ad2ef9f7dbd2a84820d9d7" + }, + "elvish": { + "revision": "5e7210d945425b77f82cbaebc5af4dd3e1ad40f5" + }, + "embedded_template": { + "revision": "38d5004a797298dc42c85e7706c5ceac46a3f29f" + }, + "erlang": { + "revision": "98ea1f9c957b2ad520415eecb5a5b406e931101e" + }, + "facility": { + "revision": "a52579670e2b14ec03d410c3c980fafaf6d659c4" + }, + "faust": { + "revision": "f3b9274514b5f9bf6b0dd4a01c30f9cc15c58bc4" + }, + "fennel": { + "revision": "8ad17704b3c2469155947d4e8fcb618cf1328459" + }, + "fidl": { + "revision": "0a8910f293268e27ff554357c229ba172b0eaed2" + }, + "firrtl": { + "revision": "8503d3a0fe0f9e427863cb0055699ff2d29ae5f5" + }, + "fish": { + "revision": "a78aef9abc395c600c38a037ac779afc7e3cc9e0" + }, + "foam": { + "revision": "04664b40c0dadb7ef37028acf3422c63271d377b" + }, + "forth": { + "revision": "90189238385cf636b9ee99ce548b9e5b5e569d48" + }, + "fortran": { + "revision": "f73d473e3530862dee7cbb38520f28824e7804f6" + }, + "fsh": { + "revision": "fad2e175099a45efbc98f000cc196d3674cc45e0" + }, + "func": { + "revision": "f780ca55e65e7d7360d0229331763e16c452fc98" + }, + "fusion": { + "revision": "19db2f47ba4c3a0f6238d4ae0e2abfca16e61dd6" + }, + "gdscript": { + "revision": "1f1e782fe2600f50ae57b53876505b8282388d77" + }, + "gdshader": { + "revision": "ffd9f958df13cae04593781d7d2562295a872455" + }, + "git_config": { + "revision": "9c2a1b7894e6d9eedfe99805b829b4ecd871375e" + }, + "git_rebase": { + "revision": "d8a4207ebbc47bd78bacdf48f883db58283f9fd8" + }, + "gitattributes": { + "revision": "41940e199ba5763abea1d21b4f717014b45f01ea" + }, + "gitcommit": { + "revision": "edd817e0532f179b7f7f371dc180629070945f0c" + }, + "gitignore": { + "revision": "f4685bf11ac466dd278449bcfe5fd014e94aa504" + }, + "gleam": { + "revision": "8432ffe32ccd360534837256747beb5b1c82fca1" + }, + "glimmer": { + "revision": "6b25d265c990139353e1f7f97baf84987ebb7bf0" + }, + "glsl": { + "revision": "33a16b6ff4d7206a16f2dc96c40e149c657db65f" + }, + "gn": { + "revision": "bc06955bc1e3c9ff8e9b2b2a55b38b94da923c05" + }, + "gnuplot": { + "revision": "3c895f5d9c0b3a3c7e02383766b462c21913c000" + }, + "go": { + "revision": "7ee8d928db5202f6831a78f8112fd693bf69f98b" + }, + "godot_resource": { + "revision": "2ffb90de47417018651fc3b970e5f6b67214dc9d" + }, + "gomod": { + "revision": "bbe2fe3be4b87e06a613e685250f473d2267f430" + }, + "gosum": { + "revision": "e2ac513b2240c7ff1069ae33b2df29ce90777c11" + }, + "gotmpl": { + "revision": "9d3f6e526dd074b9edae9070b7bb778f00e87a5b" + }, + "gowork": { + "revision": "949a8a470559543857a62102c84700d291fc984c" + }, + "gpg": { + "revision": "f99323fb8f3f10b6c69db0c2f6d0a14bd7330675" + }, + "graphql": { + "revision": "5e66e961eee421786bdda8495ed1db045e06b5fe" + }, + "groovy": { + "revision": "6c5c8813233fe326e24c5ef032858d13f8006a8d" + }, + "gstlaunch": { + "revision": "549aef253fd38a53995cda1bf55c501174372bf7" + }, + "hack": { + "revision": "fca1e294f6dce8ec5659233a6a21f5bd0ed5b4f2" + }, + "hare": { + "revision": "070524937539eb8bb4f10debd9c83b66c434f3a2" + }, + "haskell": { + "revision": "a50070d5bb5bd5c1281740a6102ecf1f4b0c4f19" + }, + "haskell_persistent": { + "revision": "577259b4068b2c281c9ebf94c109bd50a74d5857" + }, + "hcl": { + "revision": "e936d3fef8bac884661472dce71ad82284761eb1" + }, + "heex": { + "revision": "b5ad6e34eea18a15bbd1466ca707a17f9bff7b93" + }, + "helm": { + "revision": "9d3f6e526dd074b9edae9070b7bb778f00e87a5b" + }, + "hjson": { + "revision": "02fa3b79b3ff9a296066da6277adfc3f26cbc9e0" + }, + "hlsl": { + "revision": "a84e8d4f675d0006f7c07f6c7bcea2fca04cdb6e" + }, + "hlsplaylist": { + "revision": "64f19029339e75d6762feae39e7878595860c980" + }, + "hocon": { + "revision": "c390f10519ae69fdb03b3e5764f5592fb6924bcc" + }, + "hoon": { + "revision": "a24c5a39d1d7e993a8bee913c8e8b6a652ca5ae8" + }, + "html": { + "revision": "e4d834eb4918df01dcad5c27d1b15d56e3bd94cd" + }, + "htmldjango": { + "revision": "ea71012d3fe14dd0b69f36be4f96bdfe9155ebae" + }, + "http": { + "revision": "8d22f33faa5aa95c6526606fb656ada342e59e40" + }, + "hurl": { + "revision": "ad705af8c44c737bdb965fc081329c50716d2d03" + }, + "hyprlang": { + "revision": "e5da7d0aa44403153e0394d87d9edea4e5bd6609" + }, + "idl": { + "revision": "b5b53e2ca0521b98277d5e4b109f0b0e362e850e" + }, + "ini": { + "revision": "bcb84a2d4bcd6f55b911c42deade75c8f90cb0c5" + }, + "inko": { + "revision": "7860637ce1b43f5f79cfb7cc3311bf3234e9479f" + }, + "ispc": { + "revision": "9b2f9aec2106b94b4e099fe75e73ebd8ae707c04" + }, + "janet_simple": { + "revision": "f3d6e09cc47e76833f23f83b2c59ea0878660953" + }, + "java": { + "revision": "953abfc8bb3eb2f578e1f461edba4a9885f974b8" + }, + "javascript": { + "revision": "a5de24dc7939cb07a758f8d89c089cfdb6f479aa" + }, + "jq": { + "revision": "13990f530e8e6709b7978503da9bc8701d366791" + }, + "jsdoc": { + "revision": "49fde205b59a1d9792efc21ee0b6d50bbd35ff14" + }, + "json": { + "revision": "94f5c527b2965465956c2000ed6134dd24daf2a7" + }, + "json5": { + "revision": "ab0ba8229d639ec4f3fa5f674c9133477f4b77bd" + }, + "jsonc": { + "revision": "02b01653c8a1c198ae7287d566efa86a135b30d5" + }, + "jsonnet": { + "revision": "d34615fa12cc1d1cfc1f1f1a80acc9db80ee4596" + }, + "julia": { + "revision": "acd5ca12cc278df7960629c2429a096c7ac4bbea" + }, + "just": { + "revision": "fd814fc6c579f68c2a642f5e0268cf69daae92d7" + }, + "kconfig": { + "revision": "486fea71f61ad9f3fd4072a118402e97fe88d26c" + }, + "kdl": { + "revision": "49fb89a854d93b58a65a19724ac307195ca11941" + }, + "kotlin": { + "revision": "c9cb8504b81684375e7beb8907517dbd6947a1be" + }, + "koto": { + "revision": "919440e1376109bab4edac52594c17c02ae0be5a" + }, + "kusto": { + "revision": "8353a1296607d6ba33db7c7e312226e5fc83e8ce" + }, + "lalrpop": { + "revision": "854a40e99f7c70258e522bdb8ab584ede6196e2e" + }, + "latex": { + "revision": "cd82eb40d31bdfe65f846f4e06292d6c804b5e0e" + }, + "ledger": { + "revision": "8a841fb20ce683bfbb3469e6ba67f2851cfdf94a" + }, + "leo": { + "revision": "304611b5eaf53aca07459a0a03803b83b6dfd3b3" + }, + "linkerscript": { + "revision": "f99011a3554213b654985a4b0a65b3b032ec4621" + }, + "liquid": { + "revision": "293369818da219d97327908aab33707b04b63fd9" + }, + "liquidsoap": { + "revision": "14feafa91630afb1ab9988cf9b738b7ea29f3f89" + }, + "llvm": { + "revision": "1b96e58faf558ce057d4dc664b904528aee743cb" + }, + "lua": { + "revision": "a24dab177e58c9c6832f96b9a73102a0cfbced4a" + }, + "luadoc": { + "revision": "873612aadd3f684dd4e631bdf42ea8990c57634e" + }, + "luap": { + "revision": "31461ae9bd0866cb5117cfe5de71189854fd0f3e" + }, + "luau": { + "revision": "5aa9b88a8e3327276ec6e72de997f04ac80b1ae4" + }, + "m68k": { + "revision": "d097b123f19c6eaba2bf181c05420d88b9fc489d" + }, + "make": { + "revision": "a4b9187417d6be349ee5fd4b6e77b4172c6827dd" + }, + "markdown": { + "revision": "7fe453beacecf02c86f7736439f238f5bb8b5c9b" + }, + "markdown_inline": { + "revision": "7fe453beacecf02c86f7736439f238f5bb8b5c9b" + }, + "matlab": { + "revision": "79d8b25f57b48f83ae1333aff6723b83c9532e37" + }, + "menhir": { + "revision": "be8866a6bcc2b563ab0de895af69daeffa88fe70" + }, + "mermaid": { + "revision": "90ae195b31933ceb9d079abfa8a3ad0a36fee4cc" + }, + "meson": { + "revision": "bd17c824196ce70800f64ad39cfddd1b17acc13f" + }, + "mlir": { + "revision": "00c32d8562dc957b187da110a3443307962b8da8" + }, + "muttrc": { + "revision": "90ef60852c410bd964cd3b954366e4c403c17314" + }, + "nasm": { + "revision": "570f3d7be01fffc751237f4cfcf52d04e20532d1" + }, + "nickel": { + "revision": "58baf89db8fdae54a84bcf22c80ff10ee3f929ed" + }, + "nim": { + "revision": "961c2798cec9250c44f7d7225ddb33d47d25856a" + }, + "nim_format_string": { + "revision": "d45f75022d147cda056e98bfba68222c9c8eca3a" + }, + "ninja": { + "revision": "0a95cfdc0745b6ae82f60d3a339b37f19b7b9267" + }, + "nix": { + "revision": "b3cda619248e7dd0f216088bd152f59ce0bbe488" + }, + "norg": { + "revision": "aa1a1a7ded81a094cc3d5cb14bea6f34b86d8688" + }, + "nqc": { + "revision": "14e6da1627aaef21d2b2aa0c37d04269766dcc1d" + }, + "objc": { + "revision": "62e61b6f5c0289c376d61a8c91faf6435cde9012" + }, + "objdump": { + "revision": "28d3b2e25a0b1881d1b47ed1924ca276c7003d45" + }, + "ocaml": { + "revision": "0b12614ded3ec7ed7ab7933a9ba4f695ba4c342e" + }, + "ocaml_interface": { + "revision": "0b12614ded3ec7ed7ab7933a9ba4f695ba4c342e" + }, + "ocamllex": { + "revision": "4b9898ccbf198602bb0dec9cd67cc1d2c0a4fad2" + }, + "odin": { + "revision": "d37b8f24f653378b268ec18404e9c14ad355b128" + }, + "org": { + "revision": "64cfbc213f5a83da17632c95382a5a0a2f3357c1" + }, + "pascal": { + "revision": "a9ee969dec5b2e3b2ccccc5954fec04100c7619e" + }, + "passwd": { + "revision": "20239395eacdc2e0923a7e5683ad3605aee7b716" + }, + "pem": { + "revision": "217ff2af3f2db15a79ab7e3d21ea1e0c17e71a1a" + }, + "perl": { + "revision": "309cb8d33bcfd0a81050b21be08f9eb3f2fe2138" + }, + "php": { + "revision": "b38c53537769df05871643c9688c264074fb6076" + }, + "php_only": { + "revision": "b38c53537769df05871643c9688c264074fb6076" + }, + "phpdoc": { + "revision": "1d0e255b37477d0ca46f1c9e9268c8fa76c0b3fc" + }, + "pioasm": { + "revision": "924aadaf5dea2a6074d72027b064f939acf32e20" + }, + "po": { + "revision": "bd860a0f57f697162bf28e576674be9c1500db5e" + }, + "pod": { + "revision": "39da859947b94abdee43e431368e1ae975c0a424" + }, + "poe_filter": { + "revision": "592476d81f95d2451f2ca107dc872224c76fecdf" + }, + "pony": { + "revision": "73ff874ae4c9e9b45462673cbc0a1e350e2522a7" + }, + "printf": { + "revision": "0e0aceabbf607ea09e03562f5d8a56f048ddea3d" + }, + "prisma": { + "revision": "eca2596a355b1a9952b4f80f8f9caed300a272b5" + }, + "promql": { + "revision": "77625d78eebc3ffc44d114a07b2f348dff3061b0" + }, + "properties": { + "revision": "9d09f5f200c356c50c4103d36441309fd61b48d1" + }, + "proto": { + "revision": "e9f6b43f6844bd2189b50a422d4e2094313f6aa3" + }, + "prql": { + "revision": "09e158cd3650581c0af4c49c2e5b10c4834c8646" + }, + "psv": { + "revision": "7eb7297823605392d2bbcc4c09b1cd18d6fa9529" + }, + "pug": { + "revision": "a7ff31a38908df9b9f34828d21d6ca5e12413e18" + }, + "puppet": { + "revision": "584522f32495d648b18a53ccb52d988e60de127d" + }, + "purescript": { + "revision": "daf9b3e2be18b0b2996a1281f7783e0d041d8b80" + }, + "pymanifest": { + "revision": "e3b82b78721aee07f676dac8473ae69db51debcf" + }, + "python": { + "revision": "71778c2a472ed00a64abf4219544edbf8e4b86d7" + }, + "ql": { + "revision": "42becd6f8f7bae82c818fa3abb1b6ff34b552310" + }, + "qmldir": { + "revision": "6b2b5e41734bd6f07ea4c36ac20fb6f14061c841" + }, + "qmljs": { + "revision": "2c57cac27e207425f8df15327884434cb12365a3" + }, + "query": { + "revision": "a12c4a1cd8aa6e0340ecb7089a05cd345a12bae3" + }, + "r": { + "revision": "391400572538ff9854341a175ed8ab4b1e45f44b" + }, + "racket": { + "revision": "171f52a8c0ed635b85cd42d1e36d82f1066a03b4" + }, + "rasi": { + "revision": "6c9bbcfdf5f0f553d9ebc01750a3aa247a37b8aa" + }, + "rbs": { + "revision": "e5e807a50fcd104a2d740d354632fc671700a0bf" + }, + "re2c": { + "revision": "47aa19cf5f7aba2ed30e2b377f7172df76e819a6" + }, + "readline": { + "revision": "3d4768b04d7cfaf40533e12b28672603428b8f31" + }, + "regex": { + "revision": "47007f195752d8e57bda80b0b6cdb2d173a9f7d7" + }, + "rego": { + "revision": "9ac75e71b2d791e0aadeef68098319d86a2a14cf" + }, + "requirements": { + "revision": "360c6a6b31076a482663806f7a8241de9cad6b4e" + }, + "rnoweb": { + "revision": "1a74dc0ed731ad07db39f063e2c5a6fe528cae7f" + }, + "robot": { + "revision": "322e4cc65754d2b3fdef4f2f8a71e0762e3d13af" + }, + "roc": { + "revision": "7df2c0892e62efb81a7504d9799d4e0d0443d241" + }, + "ron": { + "revision": "78938553b93075e638035f624973083451b29055" + }, + "rst": { + "revision": "5120f6e59284cb8b85b450bd2db0bd352635ba9f" + }, + "ruby": { + "revision": "dc2d7d6b50f9975bc3c35bbec0ba11b2617b736b" + }, + "rust": { + "revision": "9c84af007b0f144954adb26b3f336495cbb320a7" + }, + "scala": { + "revision": "b76db435a7f876cf1ede837d66054c534783c72f" + }, + "scfg": { + "revision": "6deae0cbb458c849a4d1e2985093e9c9c32d7fd0" + }, + "scheme": { + "revision": "8f9dff3d038f09934db5ea113cebc59c74447743" + }, + "scss": { + "revision": "c478c6868648eff49eb04a4df90d703dc45b312a" + }, + "slang": { + "revision": "989bfe5ae69e7bad13454b8f52e4ab0c343d8ded" + }, + "slint": { + "revision": "0701312b74b87fe20e61aa662ba41c5815b5d428" + }, + "smali": { + "revision": "fdfa6a1febc43c7467aa7e937b87b607956f2346" + }, + "smithy": { + "revision": "fa898ac0885d1da9a253695c3e0e91f5efc587cd" + }, + "snakemake": { + "revision": "ba1b3868eaa960b945593404af9a7c2f296d3643" + }, + "solidity": { + "revision": "b5a23ead0f69d38b5c9a630f52f5c129132c16ed" + }, + "soql": { + "revision": "c99ad4b16d112fea91745e3f1b769754239fdaba" + }, + "sosl": { + "revision": "c99ad4b16d112fea91745e3f1b769754239fdaba" + }, + "sourcepawn": { + "revision": "227656e72a5f0d430bb82a467bc8078f078bba84" + }, + "sparql": { + "revision": "05f949d3c1c15e3261473a244d3ce87777374dec" + }, + "sql": { + "revision": "25f94f998de79bae9df28add9782f9ea6ea0e2b8" + }, + "squirrel": { + "revision": "0a50d31098e83c668d34d1160a0f6c7d23b571cc" + }, + "ssh_config": { + "revision": "77450e8bce8853921512348f83c73c168c71fdfb" + }, + "starlark": { + "revision": "018d0e09d9d0f0dd6740a37682b8ee4512e8b2ac" + }, + "strace": { + "revision": "d819cdd5dbe455bd3c859193633c8d91c0df7c36" + }, + "styled": { + "revision": "c68a4572e2d6403b6e99066c9a113b43f4a07a27" + }, + "supercollider": { + "revision": "affa4389186b6939d89673e1e9d2b28364f5ca6f" + }, + "surface": { + "revision": "f4586b35ac8548667a9aaa4eae44456c1f43d032" + }, + "svelte": { + "revision": "2c97326cd96b7c3016c3d249e8dc244c89b4abeb" + }, + "swift": { + "revision": "03af4d057afc56edf6a703e6606b86f782353f22" + }, + "sxhkdrc": { + "revision": "440d5f913d9465c9c776a1bd92334d32febcf065" + }, + "systemtap": { + "revision": "1af543a96d060b1f808982037bfc54cc02218edd" + }, + "t32": { + "revision": "6182836f4128725f1e74ce986840d7317021a015" + }, + "tablegen": { + "revision": "b1170880c61355aaf38fc06f4af7d3c55abdabc4" + }, + "tact": { + "revision": "034df2162ed7b654efd999942e266be713c7cde0" + }, + "tcl": { + "revision": "8784024358c233efd0f3a6fd9e7a3c5852e628bc" + }, + "teal": { + "revision": "33482c92a0dfa694491d34e167a1d2f52b0dccb1" + }, + "templ": { + "revision": "cf84ea53e2e2531f23009d676ac206090c1e2392" + }, + "terraform": { + "revision": "e936d3fef8bac884661472dce71ad82284761eb1" + }, + "textproto": { + "revision": "8dacf02aa402892c91079f8577998ed5148c0496" + }, + "thrift": { + "revision": "68fd0d80943a828d9e6f49c58a74be1e9ca142cf" + }, + "tiger": { + "revision": "a7f11d946b44244f71df41d2a78af0665d618dae" + }, + "tlaplus": { + "revision": "200f9dab6b23f3b9bb8f67fc811221517f56c373" + }, + "tmux": { + "revision": "9138ea508410e0f34da2666609f600f65e944f22" + }, + "todotxt": { + "revision": "3937c5cd105ec4127448651a21aef45f52d19609" + }, + "toml": { + "revision": "16a30c83ce427385b8d14939c45c137fcfca6c42" + }, + "tsv": { + "revision": "7eb7297823605392d2bbcc4c09b1cd18d6fa9529" + }, + "tsx": { + "revision": "4ad3010c91d700026d036b5230e2d99ba94ae8a4" + }, + "turtle": { + "revision": "085437f5cb117703b7f520dd92161140a684f092" + }, + "twig": { + "revision": "eaf80e6af969e25993576477a9dbdba3e48c1305" + }, + "typescript": { + "revision": "4ad3010c91d700026d036b5230e2d99ba94ae8a4" + }, + "typespec": { + "revision": "fd9a83c6c0aaaff4b1354454b5b9f130f59dd553" + }, + "typoscript": { + "revision": "43b221c0b76e77244efdaa9963e402a17c930fbc" + }, + "typst": { + "revision": "3924cb9ed9e0e62ce7df9c4fe0faa4c234795999" + }, + "udev": { + "revision": "8f58696e79092b4ad6bf197415bbd0970acf15cd" + }, + "ungrammar": { + "revision": "debd26fed283d80456ebafa33a06957b0c52e451" + }, + "unison": { + "revision": "59d36a09282be7e4d3374854126590f3dcebee6e" + }, + "usd": { + "revision": "4e0875f724d94d0c2ff36f9b8cb0b12f8b20d216" + }, + "uxntal": { + "revision": "ad9b638b914095320de85d59c49ab271603af048" + }, + "v": { + "revision": "7e11a6f8f369df935664fadd2f0c99d90fe3226f" + }, + "vala": { + "revision": "8f690bfa639f2b83d1fb938ed3dd98a7ba453e8b" + }, + "vento": { + "revision": "3321077d7446c1b3b017c294fd56ce028ed817fe" + }, + "verilog": { + "revision": "075ebfc84543675f12e79a955f79d717772dcef3" + }, + "vhs": { + "revision": "90028bbadb267ead5b87830380f6a825147f0c0f" + }, + "vim": { + "revision": "b448ca63f972ade12c373c808acdd2bf972937db" + }, + "vimdoc": { + "revision": "b711df784dd43d0a8ed8ddbfca0ddcc3239d94b4" + }, + "vue": { + "revision": "22bdfa6c9fc0f5ffa44c6e938ec46869ac8a99ff" + }, + "wgsl": { + "revision": "40259f3c77ea856841a4e0c4c807705f3e4a2b65" + }, + "wgsl_bevy": { + "revision": "1e12c7925c41bb09818d86e30cd78644fde7d31a" + }, + "wing": { + "revision": "bd1d35cf3e013dc7e189b46a593bdc2b281b0dd7" + }, + "wit": { + "revision": "cab94791450524a542324d8cbe8017d69c516d8e" + }, + "xcompose": { + "revision": "2383cc69a2c42cfade41c7cb971fb3862bec6df1" + }, + "xml": { + "revision": "648183d86f6f8ffb240ea11b4c6873f6f45d8b67" + }, + "yaml": { + "revision": "7b03feefd36b5f155465ca736c6304aca983b267" + }, + "yang": { + "revision": "2c0e6be8dd4dcb961c345fa35c309ad4f5bd3502" + }, + "yuck": { + "revision": "e877f6ade4b77d5ef8787075141053631ba12318" + }, + "zathurarc": { + "revision": "e9e8de071ab79ed1f6e3365f05fcf890b6d85a2f" + }, + "zig": { + "revision": "0d08703e4c3f426ec61695d7617415fff97029bd" + } +} diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter.lua new file mode 100644 index 00000000..963fe730 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter.lua @@ -0,0 +1,22 @@ +local install = require "nvim-treesitter.install" +local utils = require "nvim-treesitter.utils" +local info = require "nvim-treesitter.info" +local configs = require "nvim-treesitter.configs" +local statusline = require "nvim-treesitter.statusline" + +-- Registers all query predicates +require "nvim-treesitter.query_predicates" + +local M = {} + +function M.setup() + utils.setup_commands("install", install.commands) + utils.setup_commands("info", info.commands) + utils.setup_commands("configs", configs.commands) + configs.init() +end + +M.define_modules = configs.define_modules +M.statusline = statusline.statusline + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/caching.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/caching.lua new file mode 100644 index 00000000..7733202e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/caching.lua @@ -0,0 +1,71 @@ +local api = vim.api + +local M = {} + +-- Creates a cache table for buffers keyed by a type name. +-- Cache entries attach to the buffer and cleanup entries +-- as buffers are detached. +function M.create_buffer_cache() + local cache = {} + + ---@type table> + local items = setmetatable({}, { + __index = function(tbl, key) + rawset(tbl, key, {}) + return rawget(tbl, key) + end, + }) + + ---@type table + local loaded_buffers = {} + + ---@param type_name string + ---@param bufnr integer + ---@param value any + function cache.set(type_name, bufnr, value) + if not loaded_buffers[bufnr] then + loaded_buffers[bufnr] = true + -- Clean up the cache if the buffer is detached + -- to avoid memory leaks + api.nvim_buf_attach(bufnr, false, { + on_detach = function() + cache.clear_buffer(bufnr) + loaded_buffers[bufnr] = nil + return true + end, + on_reload = function() end, -- this is needed to prevent on_detach being called on buffer reload + }) + end + + items[bufnr][type_name] = value + end + + ---@param type_name string + ---@param bufnr integer + ---@return any + function cache.get(type_name, bufnr) + return items[bufnr][type_name] + end + + ---@param type_name string + ---@param bufnr integer + ---@return boolean + function cache.has(type_name, bufnr) + return cache.get(type_name, bufnr) ~= nil + end + + ---@param type_name string + ---@param bufnr integer + function cache.remove(type_name, bufnr) + items[bufnr][type_name] = nil + end + + ---@param bufnr integer + function cache.clear_buffer(bufnr) + items[bufnr] = nil + end + + return cache +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/compat.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/compat.lua new file mode 100644 index 00000000..0ad01003 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/compat.lua @@ -0,0 +1,39 @@ +-- Shim module to address deprecations across nvim versions +local ts = vim.treesitter +local tsq = ts.query + +local M = {} + +function M.get_query_files(lang, query_group, is_included) + return (tsq.get_files or tsq.get_query_files)(lang, query_group, is_included) +end + +function M.get_query(lang, query_name) + return (tsq.get or tsq.get_query)(lang, query_name) +end + +function M.parse_query(lang, query) + return (tsq.parse or tsq.parse_query)(lang, query) +end + +function M.get_range(node, source, metadata) + return (ts.get_range or tsq.get_range)(node, source, metadata) +end + +function M.get_node_text(node, bufnr) + return (ts.get_node_text or tsq.get_node_text)(node, bufnr) +end + +function M.require_language(lang, opts) + return (ts.language.add or ts.language.require_language)(lang, opts) +end + +function M.flatten(t) + if vim.fn.has "nvim-0.11" == 1 then + return vim.iter(t):flatten():totable() + else + return vim.tbl_flatten(t) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/configs.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/configs.lua new file mode 100644 index 00000000..a3ec30fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/configs.lua @@ -0,0 +1,616 @@ +local api = vim.api + +local queries = require "nvim-treesitter.query" +local ts = require "nvim-treesitter.compat" +local parsers = require "nvim-treesitter.parsers" +local utils = require "nvim-treesitter.utils" +local caching = require "nvim-treesitter.caching" + +local M = {} + +---@class TSConfig +---@field modules {[string]:TSModule} +---@field sync_install boolean +---@field ensure_installed string[]|string +---@field ignore_install string[] +---@field auto_install boolean +---@field parser_install_dir string|nil + +---@type TSConfig +local config = { + modules = {}, + sync_install = false, + ensure_installed = {}, + auto_install = false, + ignore_install = {}, + parser_install_dir = nil, +} + +-- List of modules that need to be setup on initialization. +---@type TSModule[][] +local queued_modules_defs = {} +-- Whether we've initialized the plugin yet. +local is_initialized = false + +---@class TSModule +---@field module_path string +---@field enable boolean|string[]|function(string): boolean +---@field disable boolean|string[]|function(string): boolean +---@field keymaps table +---@field is_supported function(string): boolean +---@field attach function(string) +---@field detach function(string) +---@field enabled_buffers table +---@field additional_vim_regex_highlighting boolean|string[] + +---@type {[string]: TSModule} +local builtin_modules = { + highlight = { + module_path = "nvim-treesitter.highlight", + -- @deprecated: use `highlight.set_custom_captures` instead + custom_captures = {}, + enable = false, + is_supported = function(lang) + return queries.has_highlights(lang) + end, + additional_vim_regex_highlighting = false, + }, + incremental_selection = { + module_path = "nvim-treesitter.incremental_selection", + enable = false, + keymaps = { + init_selection = "gnn", -- set to `false` to disable one of the mappings + node_incremental = "grn", + scope_incremental = "grc", + node_decremental = "grm", + }, + is_supported = function() + return true + end, + }, + indent = { + module_path = "nvim-treesitter.indent", + enable = false, + is_supported = queries.has_indents, + }, +} + +local attached_buffers_by_module = caching.create_buffer_cache() + +---Resolves a module by requiring the `module_path` or using the module definition. +---@param mod_name string +---@return TSModule|nil +local function resolve_module(mod_name) + local config_mod = M.get_module(mod_name) + + if not config_mod then + return + end + + if type(config_mod.attach) == "function" and type(config_mod.detach) == "function" then + return config_mod + elseif type(config_mod.module_path) == "string" then + return require(config_mod.module_path) + end +end + +---Enables and attaches the module to a buffer for lang. +---@param mod string path to module +---@param bufnr integer|nil buffer number, defaults to current buffer +---@param lang string|nil language, defaults to current language +local function enable_module(mod, bufnr, lang) + local module = M.get_module(mod) + if not module then + return + end + + bufnr = bufnr or api.nvim_get_current_buf() + lang = lang or parsers.get_buf_lang(bufnr) + + if not module.enable then + if module.enabled_buffers then + module.enabled_buffers[bufnr] = true + else + module.enabled_buffers = { [bufnr] = true } + end + end + + M.attach_module(mod, bufnr, lang) +end + +---Enables autocomands for the module. +---After the module is loaded `loaded` will be set to true for the module. +---@param mod string path to module +local function enable_mod_conf_autocmd(mod) + local config_mod = M.get_module(mod) + if not config_mod or config_mod.loaded then + return + end + + api.nvim_create_autocmd("FileType", { + group = api.nvim_create_augroup("NvimTreesitter-" .. mod, {}), + callback = function(args) + require("nvim-treesitter.configs").reattach_module(mod, args.buf) + end, + desc = "Reattach module", + }) + + config_mod.loaded = true +end + +---Enables the module globally and for all current buffers. +---After enabled, `enable` will be set to true for the module. +---@param mod string path to module +local function enable_all(mod) + local config_mod = M.get_module(mod) + if not config_mod then + return + end + + enable_mod_conf_autocmd(mod) + config_mod.enable = true + config_mod.enabled_buffers = nil + + for _, bufnr in pairs(api.nvim_list_bufs()) do + enable_module(mod, bufnr) + end +end + +---Disables and detaches the module for a buffer. +---@param mod string path to module +---@param bufnr integer buffer number, defaults to current buffer +local function disable_module(mod, bufnr) + local module = M.get_module(mod) + if not module then + return + end + + bufnr = bufnr or api.nvim_get_current_buf() + if module.enabled_buffers then + module.enabled_buffers[bufnr] = false + end + M.detach_module(mod, bufnr) +end + +---Disables autocomands for the module. +---After the module is unloaded `loaded` will be set to false for the module. +---@param mod string path to module +local function disable_mod_conf_autocmd(mod) + local config_mod = M.get_module(mod) + if not config_mod or not config_mod.loaded then + return + end + api.nvim_clear_autocmds { event = "FileType", group = "NvimTreesitter-" .. mod } + config_mod.loaded = false +end + +---Disables the module globally and for all current buffers. +---After disabled, `enable` will be set to false for the module. +---@param mod string path to module +local function disable_all(mod) + local config_mod = M.get_module(mod) + if not config_mod then + return + end + + config_mod.enabled_buffers = nil + disable_mod_conf_autocmd(mod) + config_mod.enable = false + + for _, bufnr in pairs(api.nvim_list_bufs()) do + disable_module(mod, bufnr) + end +end + +---Toggles a module for a buffer +---@param mod string path to module +---@param bufnr integer buffer number, defaults to current buffer +---@param lang string language, defaults to current language +local function toggle_module(mod, bufnr, lang) + bufnr = bufnr or api.nvim_get_current_buf() + lang = lang or parsers.get_buf_lang(bufnr) + + if attached_buffers_by_module.has(mod, bufnr) then + disable_module(mod, bufnr) + else + enable_module(mod, bufnr, lang) + end +end + +-- Toggles the module globally and for all current buffers. +-- @param mod path to module +local function toggle_all(mod) + local config_mod = M.get_module(mod) + if not config_mod then + return + end + + if config_mod.enable then + disable_all(mod) + else + enable_all(mod) + end +end + +---Recurses through all modules including submodules +---@param accumulator function called for each module +---@param root {[string]: TSModule}|nil root configuration table to start at +---@param path string|nil prefix path +local function recurse_modules(accumulator, root, path) + root = root or config.modules + + for name, module in pairs(root) do + local new_path = path and (path .. "." .. name) or name + + if M.is_module(module) then + accumulator(name, module, new_path, root) + elseif type(module) == "table" then + recurse_modules(accumulator, module, new_path) + end + end +end + +-- Shows current configuration of all nvim-treesitter modules +---@param process_function function used as the `process` parameter +--- for vim.inspect (https://github.com/kikito/inspect.lua#optionsprocess) +local function config_info(process_function) + process_function = process_function + or function(item, path) + if path[#path] == vim.inspect.METATABLE then + return + end + if path[#path] == "is_supported" then + return + end + return item + end + print(vim.inspect(config, { process = process_function })) +end + +---@param query_group string +---@param lang string +function M.edit_query_file(query_group, lang) + lang = lang or parsers.get_buf_lang() + local files = ts.get_query_files(lang, query_group, true) + if #files == 0 then + utils.notify "No query file found! Creating a new one!" + M.edit_query_file_user_after(query_group, lang) + elseif #files == 1 then + vim.cmd(":edit " .. files[1]) + else + vim.ui.select(files, { prompt = "Select a file:" }, function(file) + if file then + vim.cmd(":edit " .. file) + end + end) + end +end + +---@param query_group string +---@param lang string +function M.edit_query_file_user_after(query_group, lang) + lang = lang or parsers.get_buf_lang() + local folder = utils.join_path(vim.fn.stdpath "config", "after", "queries", lang) + local file = utils.join_path(folder, query_group .. ".scm") + if vim.fn.isdirectory(folder) ~= 1 then + vim.ui.select({ "Yes", "No" }, { prompt = '"' .. folder .. '" does not exist. Create it?' }, function(choice) + if choice == "Yes" then + vim.fn.mkdir(folder, "p", "0755") + vim.cmd(":edit " .. file) + end + end) + else + vim.cmd(":edit " .. file) + end +end + +M.commands = { + TSBufEnable = { + run = enable_module, + args = { + "-nargs=1", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSBufDisable = { + run = disable_module, + args = { + "-nargs=1", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSBufToggle = { + run = toggle_module, + args = { + "-nargs=1", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSEnable = { + run = enable_all, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSDisable = { + run = disable_all, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSToggle = { + run = toggle_all, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSConfigInfo = { + run = config_info, + args = { + "-nargs=0", + }, + }, + TSEditQuery = { + run = M.edit_query_file, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#available_query_groups", + }, + }, + TSEditQueryUserAfter = { + run = M.edit_query_file_user_after, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#available_query_groups", + }, + }, +} + +---@param mod string module +---@param lang string the language of the buffer +---@param bufnr integer the buffer +function M.is_enabled(mod, lang, bufnr) + if not parsers.has_parser(lang) then + return false + end + + local module_config = M.get_module(mod) + if not module_config then + return false + end + + local buffer_enabled = module_config.enabled_buffers and module_config.enabled_buffers[bufnr] + local config_enabled = module_config.enable or buffer_enabled + if not config_enabled or not module_config.is_supported(lang) then + return false + end + + local disable = module_config.disable + if type(disable) == "function" then + if disable(lang, bufnr) then + return false + end + elseif type(disable) == "table" then + -- Otherwise it's a list of languages + for _, parser in pairs(disable) do + if lang == parser then + return false + end + end + end + + return true +end + +---Setup call for users to override module configurations. +---@param user_data TSConfig module overrides +function M.setup(user_data) + config.modules = vim.tbl_deep_extend("force", config.modules, user_data) + config.ignore_install = user_data.ignore_install or {} + config.parser_install_dir = user_data.parser_install_dir or nil + if config.parser_install_dir then + config.parser_install_dir = vim.fn.expand(config.parser_install_dir, ":p") + end + + config.auto_install = user_data.auto_install or false + if config.auto_install then + require("nvim-treesitter.install").setup_auto_install() + end + + local ensure_installed = user_data.ensure_installed or {} + if #ensure_installed > 0 then + if user_data.sync_install then + require("nvim-treesitter.install").ensure_installed_sync(ensure_installed) + else + require("nvim-treesitter.install").ensure_installed(ensure_installed) + end + end + + config.modules.ensure_installed = nil + config.ensure_installed = ensure_installed + + recurse_modules(function(_, _, new_path) + local data = utils.get_at_path(config.modules, new_path) + if data.enable then + enable_all(new_path) + end + end, config.modules) +end + +-- Defines a table of modules that can be attached/detached to buffers +-- based on language support. A module consist of the following properties: +---* @enable Whether the modules is enabled. Can be true or false. +---* @disable A list of languages to disable the module for. Only relevant if enable is true. +---* @keymaps A list of user mappings for a given module if relevant. +---* @is_supported A function which, given a ft, will return true if the ft works on the module. +---* @module_path A string path to a module file using `require`. The exported module must contain +--- an `attach` and `detach` function. This path is not required if `attach` and `detach` +--- functions are provided directly on the module definition. +---* @attach An attach function that is called for each buffer that the module is enabled for. This is required +--- if a `module_path` is not specified. +---* @detach A detach function that is called for each buffer that the module is enabled for. This is required +--- if a `module_path` is not specified. +-- +-- Modules are not setup until `init` is invoked by the plugin. This allows modules to be defined in any order +-- and can be loaded lazily. +-- +---* @example +---require"nvim-treesitter".define_modules { +--- my_cool_module = { +--- attach = function() +--- do_some_cool_setup() +--- end, +--- detach = function() +--- do_some_cool_teardown() +--- end +--- } +---} +---@param mod_defs TSModule[] +function M.define_modules(mod_defs) + if not is_initialized then + table.insert(queued_modules_defs, mod_defs) + return + end + + recurse_modules(function(key, mod, _, group) + group[key] = vim.tbl_extend("keep", mod, { + enable = false, + disable = {}, + is_supported = function() + return true + end, + }) + end, mod_defs) + + config.modules = vim.tbl_deep_extend("keep", config.modules, mod_defs) + + for _, mod in ipairs(M.available_modules(mod_defs)) do + local module_config = M.get_module(mod) + if module_config and module_config.enable then + enable_mod_conf_autocmd(mod) + end + end +end + +---Attaches a module to a buffer +---@param mod_name string the module name +---@param bufnr integer the buffer +---@param lang string the language of the buffer +function M.attach_module(mod_name, bufnr, lang) + bufnr = bufnr or api.nvim_get_current_buf() + lang = lang or parsers.get_buf_lang(bufnr) + local resolved_mod = resolve_module(mod_name) + + if resolved_mod and not attached_buffers_by_module.has(mod_name, bufnr) and M.is_enabled(mod_name, lang, bufnr) then + attached_buffers_by_module.set(mod_name, bufnr, true) + resolved_mod.attach(bufnr, lang) + end +end + +-- Detaches a module to a buffer +---@param mod_name string the module name +---@param bufnr integer the buffer +function M.detach_module(mod_name, bufnr) + local resolved_mod = resolve_module(mod_name) + bufnr = bufnr or api.nvim_get_current_buf() + + if resolved_mod and attached_buffers_by_module.has(mod_name, bufnr) then + attached_buffers_by_module.remove(mod_name, bufnr) + resolved_mod.detach(bufnr) + end +end + +-- Same as attach_module, but if the module is already attached, detach it first. +---@param mod_name string the module name +---@param bufnr integer the buffer +---@param lang string the language of the buffer +function M.reattach_module(mod_name, bufnr, lang) + M.detach_module(mod_name, bufnr) + M.attach_module(mod_name, bufnr, lang) +end + +-- Gets available modules +---@param root {[string]:TSModule}|nil table to find modules +---@return string[] modules list of module paths +function M.available_modules(root) + local modules = {} + + recurse_modules(function(_, _, path) + table.insert(modules, path) + end, root) + + return modules +end + +---Gets a module config by path +---@param mod_path string path to the module +---@return TSModule|nil: the module or nil +function M.get_module(mod_path) + local mod = utils.get_at_path(config.modules, mod_path) + + return M.is_module(mod) and mod or nil +end + +-- Determines whether the provided table is a module. +-- A module should contain an attach and detach function. +---@param mod table|nil the module table +---@return boolean +function M.is_module(mod) + return type(mod) == "table" + and ((type(mod.attach) == "function" and type(mod.detach) == "function") or type(mod.module_path) == "string") +end + +-- Initializes built-in modules and any queued modules +-- registered by plugins or the user. +function M.init() + is_initialized = true + M.define_modules(builtin_modules) + + for _, mod_def in ipairs(queued_modules_defs) do + M.define_modules(mod_def) + end +end + +-- If parser_install_dir is not nil is used or created. +-- If parser_install_dir is nil try the package dir of the nvim-treesitter +-- plugin first, followed by the "site" dir from "runtimepath". "site" dir will +-- be created if it doesn't exist. Using only the package dir won't work when +-- the plugin is installed with Nix, since the "/nix/store" is read-only. +---@param folder_name string|nil +---@return string|nil, string|nil +function M.get_parser_install_dir(folder_name) + folder_name = folder_name or "parser" + + local install_dir = config.parser_install_dir or utils.get_package_path() + local parser_dir = utils.join_path(install_dir, folder_name) + + return utils.create_or_reuse_writable_dir( + parser_dir, + utils.join_space("Could not create parser dir '", parser_dir, "': "), + utils.join_space( + "Parser dir '", + parser_dir, + "' should be read/write (see README on how to configure an alternative install location)" + ) + ) +end + +function M.get_parser_info_dir() + return M.get_parser_install_dir "parser-info" +end + +function M.get_ignored_parser_installs() + return config.ignore_install or {} +end + +function M.get_ensure_installed_parsers() + if type(config.ensure_installed) == "string" then + return { config.ensure_installed } + end + return config.ensure_installed or {} +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/fold.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/fold.lua new file mode 100644 index 00000000..75959987 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/fold.lua @@ -0,0 +1,123 @@ +local api = vim.api +local tsutils = require "nvim-treesitter.ts_utils" +local query = require "nvim-treesitter.query" +local parsers = require "nvim-treesitter.parsers" + +local M = {} + +-- This is cached on buf tick to avoid computing that multiple times +-- Especially not for every line in the file when `zx` is hit +local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr) + local max_fold_level = api.nvim_win_get_option(0, "foldnestmax") + local trim_level = function(level) + if level > max_fold_level then + return max_fold_level + end + return level + end + + local parser = parsers.get_parser(bufnr) + + if not parser then + return {} + end + + local matches = query.get_capture_matches_recursively(bufnr, function(lang) + if query.has_folds(lang) then + return "@fold", "folds" + elseif query.has_locals(lang) then + return "@scope", "locals" + end + end) + + -- start..stop is an inclusive range + + ---@type table + local start_counts = {} + ---@type table + local stop_counts = {} + + local prev_start = -1 + local prev_stop = -1 + + local min_fold_lines = api.nvim_win_get_option(0, "foldminlines") + + for _, match in ipairs(matches) do + local start, stop, stop_col ---@type integer, integer, integer + if match.metadata and match.metadata.range then + start, _, stop, stop_col = unpack(match.metadata.range) ---@type integer, integer, integer, integer + else + start, _, stop, stop_col = match.node:range() ---@type integer, integer, integer, integer + end + + if stop_col == 0 then + stop = stop - 1 + end + + local fold_length = stop - start + 1 + local should_fold = fold_length > min_fold_lines + + -- Fold only multiline nodes that are not exactly the same as previously met folds + -- Checking against just the previously found fold is sufficient if nodes + -- are returned in preorder or postorder when traversing tree + if should_fold and not (start == prev_start and stop == prev_stop) then + start_counts[start] = (start_counts[start] or 0) + 1 + stop_counts[stop] = (stop_counts[stop] or 0) + 1 + prev_start = start + prev_stop = stop + end + end + + ---@type string[] + local levels = {} + local current_level = 0 + + -- We now have the list of fold opening and closing, fill the gaps and mark where fold start + for lnum = 0, api.nvim_buf_line_count(bufnr) do + local prefix = "" + + local last_trimmed_level = trim_level(current_level) + current_level = current_level + (start_counts[lnum] or 0) + local trimmed_level = trim_level(current_level) + current_level = current_level - (stop_counts[lnum] or 0) + local next_trimmed_level = trim_level(current_level) + + -- Determine if it's the start/end of a fold + -- NB: vim's fold-expr interface does not have a mechanism to indicate that + -- two (or more) folds start at this line, so it cannot distinguish between + -- ( \n ( \n )) \n (( \n ) \n ) + -- versus + -- ( \n ( \n ) \n ( \n ) \n ) + -- If it did have such a mechanism, (trimmed_level - last_trimmed_level) + -- would be the correct number of starts to pass on. + if trimmed_level - last_trimmed_level > 0 then + prefix = ">" + elseif trimmed_level - next_trimmed_level > 0 then + -- Ending marks tend to confuse vim more than it helps, particularly when + -- the fold level changes by at least 2; we can uncomment this if + -- vim's behavior gets fixed. + -- prefix = "<" + prefix = "" + end + + levels[lnum + 1] = prefix .. tostring(trimmed_level) + end + + return levels +end) + +---@param lnum integer +---@return string +function M.get_fold_indic(lnum) + if not parsers.has_parser() or not lnum then + return "0" + end + + local buf = api.nvim_get_current_buf() + + local levels = folds_levels(buf) or {} + + return levels[lnum] or "0" +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/health.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/health.lua new file mode 100644 index 00000000..be265f7d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/health.lua @@ -0,0 +1,176 @@ +local api = vim.api +local fn = vim.fn + +local queries = require "nvim-treesitter.query" +local info = require "nvim-treesitter.info" +local shell = require "nvim-treesitter.shell_command_selectors" +local install = require "nvim-treesitter.install" +local utils = require "nvim-treesitter.utils" +local ts = require "nvim-treesitter.compat" + +local health = vim.health or require "health" + +-- "report_" prefix has been deprecated, use the recommended replacements if they exist. +local _start = health.start or health.report_start +local _ok = health.ok or health.report_ok +local _warn = health.warn or health.report_warn +local _error = health.error or health.report_error + +local M = {} + +local NVIM_TREESITTER_MINIMUM_ABI = 13 + +local function install_health() + _start "Installation" + + if fn.has "nvim-0.9.2" ~= 1 then + _error "Nvim-treesitter requires Nvim 0.9.2 or newer" + end + + if fn.executable "tree-sitter" == 0 then + _warn( + "`tree-sitter` executable not found (parser generator, only needed for :TSInstallFromGrammar," + .. " not required for :TSInstall)" + ) + else + _ok( + "`tree-sitter` found " + .. (utils.ts_cli_version() or "(unknown version)") + .. " (parser generator, only needed for :TSInstallFromGrammar)" + ) + end + + if fn.executable "node" == 0 then + _warn("`node` executable not found (only needed for :TSInstallFromGrammar," .. " not required for :TSInstall)") + else + local handle = io.popen "node --version" + local result = handle:read "*a" + handle:close() + local version = vim.split(result, "\n")[1] + _ok("`node` found " .. version .. " (only needed for :TSInstallFromGrammar)") + end + + if fn.executable "git" == 0 then + _error("`git` executable not found.", { + "Install it with your package manager.", + "Check that your `$PATH` is set correctly.", + }) + else + _ok "`git` executable found." + end + + local cc = shell.select_executable(install.compilers) + if not cc then + _error("`cc` executable not found.", { + "Check that any of " + .. vim.inspect(install.compilers) + .. " is in your $PATH" + .. ' or set the environment variable CC or `require"nvim-treesitter.install".compilers` explicitly!', + }) + else + local version = vim.fn.systemlist(cc .. (cc == "cl" and "" or " --version"))[1] + _ok( + "`" + .. cc + .. "` executable found. Selected from " + .. vim.inspect(install.compilers) + .. (version and ("\nVersion: " .. version) or "") + ) + end + if vim.treesitter.language_version then + if vim.treesitter.language_version >= NVIM_TREESITTER_MINIMUM_ABI then + _ok( + "Neovim was compiled with tree-sitter runtime ABI version " + .. vim.treesitter.language_version + .. " (required >=" + .. NVIM_TREESITTER_MINIMUM_ABI + .. "). Parsers must be compatible with runtime ABI." + ) + else + _error( + "Neovim was compiled with tree-sitter runtime ABI version " + .. vim.treesitter.language_version + .. ".\n" + .. "nvim-treesitter expects at least ABI version " + .. NVIM_TREESITTER_MINIMUM_ABI + .. "\n" + .. "Please make sure that Neovim is linked against are recent tree-sitter runtime when building" + .. " or raise an issue at your Neovim packager. Parsers must be compatible with runtime ABI." + ) + end + end + + _start("OS Info:\n" .. vim.inspect(vim.loop.os_uname())) +end + +local function query_status(lang, query_group) + local ok, err = pcall(queries.get_query, lang, query_group) + if not ok then + return "x", err + elseif not err then + return "." + else + return "✓" + end +end + +function M.check() + local error_collection = {} + -- Installation dependency checks + install_health() + queries.invalidate_query_cache() + -- Parser installation checks + local parser_installation = { "Parser/Features" .. string.rep(" ", 9) .. "H L F I J" } + for _, parser_name in pairs(info.installed_parsers()) do + local installed = #api.nvim_get_runtime_file("parser/" .. parser_name .. ".so", false) + + -- Only append information about installed parsers + if installed >= 1 then + local multiple_parsers = installed > 1 and "+" or "" + local out = " - " .. parser_name .. multiple_parsers .. string.rep(" ", 20 - (#parser_name + #multiple_parsers)) + for _, query_group in pairs(queries.built_in_query_groups) do + local status, err = query_status(parser_name, query_group) + out = out .. status .. " " + if err then + table.insert(error_collection, { parser_name, query_group, err }) + end + end + table.insert(parser_installation, vim.fn.trim(out, " ", 2)) + end + end + local legend = [[ + + Legend: H[ighlight], L[ocals], F[olds], I[ndents], In[j]ections + +) multiple parsers found, only one will be used + x) errors found in the query, try to run :TSUpdate {lang}]] + table.insert(parser_installation, legend) + -- Finally call the report function + _start(table.concat(parser_installation, "\n")) + if #error_collection > 0 then + _start "The following errors have been detected:" + for _, p in ipairs(error_collection) do + local lang, type, err = unpack(p) + local lines = {} + table.insert(lines, lang .. "(" .. type .. "): " .. err) + local files = ts.get_query_files(lang, type) + if #files > 0 then + table.insert(lines, lang .. "(" .. type .. ") is concatenated from the following files:") + for _, file in ipairs(files) do + local fd = io.open(file, "r") + if fd then + local ok, file_err = pcall(ts.parse_query, lang, fd:read "*a") + if ok then + table.insert(lines, '| [OK]:"' .. file .. '"') + else + table.insert(lines, '| [ERROR]:"' .. file .. '", failed to load: ' .. file_err) + end + fd:close() + end + end + end + _error(table.concat(lines, "\n")) + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/highlight.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/highlight.lua new file mode 100644 index 00000000..5a3cc2e8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/highlight.lua @@ -0,0 +1,49 @@ +local configs = require "nvim-treesitter.configs" + +local M = {} + +---@param config TSModule +---@param lang string +---@return boolean +local function should_enable_vim_regex(config, lang) + local additional_hl = config.additional_vim_regex_highlighting + local is_table = type(additional_hl) == "table" + + ---@diagnostic disable-next-line: param-type-mismatch + return additional_hl and (not is_table or vim.tbl_contains(additional_hl, lang)) +end + +---@param bufnr integer +---@param lang string +function M.attach(bufnr, lang) + local config = configs.get_module "highlight" + vim.treesitter.start(bufnr, lang) + if config and should_enable_vim_regex(config, lang) then + vim.bo[bufnr].syntax = "ON" + end +end + +---@param bufnr integer +function M.detach(bufnr) + vim.treesitter.stop(bufnr) +end + +---@deprecated +function M.start(...) + vim.notify( + "`nvim-treesitter.highlight.start` is deprecated: use `nvim-treesitter.highlight.attach` or `vim.treesitter.start`", + vim.log.levels.WARN + ) + M.attach(...) +end + +---@deprecated +function M.stop(...) + vim.notify( + "`nvim-treesitter.highlight.stop` is deprecated: use `nvim-treesitter.highlight.detach` or `vim.treesitter.stop`", + vim.log.levels.WARN + ) + M.detach(...) +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/incremental_selection.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/incremental_selection.lua new file mode 100644 index 00000000..78f0915c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/incremental_selection.lua @@ -0,0 +1,167 @@ +local api = vim.api + +local configs = require "nvim-treesitter.configs" +local ts_utils = require "nvim-treesitter.ts_utils" +local locals = require "nvim-treesitter.locals" +local parsers = require "nvim-treesitter.parsers" +local queries = require "nvim-treesitter.query" +local utils = require "nvim-treesitter.utils" + +local M = {} + +---@type table> +local selections = {} + +function M.init_selection() + local buf = api.nvim_get_current_buf() + local node = ts_utils.get_node_at_cursor() + selections[buf] = { [1] = node } + ts_utils.update_selection(buf, node) +end + +-- Get the range of the current visual selection. +-- +-- The range starts with 1 and the ending is inclusive. +---@return integer, integer, integer, integer +local function visual_selection_range() + local _, csrow, cscol, _ = unpack(vim.fn.getpos "v") ---@type integer, integer, integer, integer + local _, cerow, cecol, _ = unpack(vim.fn.getpos ".") ---@type integer, integer, integer, integer + + local start_row, start_col, end_row, end_col ---@type integer, integer, integer, integer + + if csrow < cerow or (csrow == cerow and cscol <= cecol) then + start_row = csrow + start_col = cscol + end_row = cerow + end_col = cecol + else + start_row = cerow + start_col = cecol + end_row = csrow + end_col = cscol + end + + return start_row, start_col, end_row, end_col +end + +---@param node TSNode +---@return boolean +local function range_matches(node) + local csrow, cscol, cerow, cecol = visual_selection_range() + local srow, scol, erow, ecol = ts_utils.get_vim_range { node:range() } + return srow == csrow and scol == cscol and erow == cerow and ecol == cecol +end + +---@param get_parent fun(node: TSNode): TSNode|nil +---@return fun():nil +local function select_incremental(get_parent) + return function() + local buf = api.nvim_get_current_buf() + local nodes = selections[buf] + + local csrow, cscol, cerow, cecol = visual_selection_range() + -- Initialize incremental selection with current selection + if not nodes or #nodes == 0 or not range_matches(nodes[#nodes]) then + local root = parsers.get_parser():parse()[1]:root() + local node = root:named_descendant_for_range(csrow - 1, cscol - 1, cerow - 1, cecol) + ts_utils.update_selection(buf, node) + if nodes and #nodes > 0 then + table.insert(selections[buf], node) + else + selections[buf] = { [1] = node } + end + return + end + + -- Find a node that changes the current selection. + local node = nodes[#nodes] ---@type TSNode + while true do + local parent = get_parent(node) + if not parent or parent == node then + -- Keep searching in the main tree + -- TODO: we should search on the parent tree of the current node. + local root = parsers.get_parser():parse()[1]:root() + parent = root:named_descendant_for_range(csrow - 1, cscol - 1, cerow - 1, cecol) + if not parent or root == node or parent == node then + ts_utils.update_selection(buf, node) + return + end + end + node = parent + local srow, scol, erow, ecol = ts_utils.get_vim_range { node:range() } + local same_range = (srow == csrow and scol == cscol and erow == cerow and ecol == cecol) + if not same_range then + table.insert(selections[buf], node) + if node ~= nodes[#nodes] then + table.insert(nodes, node) + end + ts_utils.update_selection(buf, node) + return + end + end + end +end + +M.node_incremental = select_incremental(function(node) + return node:parent() or node +end) + +M.scope_incremental = select_incremental(function(node) + local lang = parsers.get_buf_lang() + if queries.has_locals(lang) then + return locals.containing_scope(node:parent() or node) + else + return node + end +end) + +function M.node_decremental() + local buf = api.nvim_get_current_buf() + local nodes = selections[buf] + if not nodes or #nodes < 2 then + return + end + + table.remove(selections[buf]) + local node = nodes[#nodes] ---@type TSNode + ts_utils.update_selection(buf, node) +end + +local FUNCTION_DESCRIPTIONS = { + init_selection = "Start selecting nodes with nvim-treesitter", + node_incremental = "Increment selection to named node", + scope_incremental = "Increment selection to surrounding scope", + node_decremental = "Shrink selection to previous named node", +} + +---@param bufnr integer +function M.attach(bufnr) + local config = configs.get_module "incremental_selection" + for funcname, mapping in pairs(config.keymaps) do + if mapping then + local mode = funcname == "init_selection" and "n" or "x" + local rhs = M[funcname] ---@type function + + if not rhs then + utils.notify("Unknown keybinding: " .. funcname .. debug.traceback(), vim.log.levels.ERROR) + else + vim.keymap.set(mode, mapping, rhs, { buffer = bufnr, silent = true, desc = FUNCTION_DESCRIPTIONS[funcname] }) + end + end + end +end + +function M.detach(bufnr) + local config = configs.get_module "incremental_selection" + for f, mapping in pairs(config.keymaps) do + if mapping then + local mode = f == "init_selection" and "n" or "x" + local ok, err = pcall(vim.keymap.del, mode, mapping, { buffer = bufnr }) + if not ok then + utils.notify(string.format('%s "%s" for mode %s', err, mapping, mode), vim.log.levels.ERROR) + end + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/indent.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/indent.lua new file mode 100644 index 00000000..a9e5e6ce --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/indent.lua @@ -0,0 +1,359 @@ +local ts = vim.treesitter +local parsers = require "nvim-treesitter.parsers" + +local M = {} + +M.avoid_force_reparsing = { + yaml = true, +} + +M.comment_parsers = { + comment = true, + jsdoc = true, + phpdoc = true, +} + +local function getline(lnum) + return vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, false)[1] or "" +end + +---@param root TSNode +---@param lnum integer +---@param col? integer +---@return TSNode +local function get_first_node_at_line(root, lnum, col) + col = col or vim.fn.indent(lnum) + return root:descendant_for_range(lnum - 1, col, lnum - 1, col + 1) +end + +---@param root TSNode +---@param lnum integer +---@param col? integer +---@return TSNode +local function get_last_node_at_line(root, lnum, col) + col = col or (#getline(lnum) - 1) + return root:descendant_for_range(lnum - 1, col, lnum - 1, col + 1) +end + +---@param node TSNode +---@return number +local function node_length(node) + local _, _, start_byte = node:start() + local _, _, end_byte = node:end_() + return end_byte - start_byte +end + +---@param bufnr integer +---@param node TSNode +---@param delimiter string +---@return TSNode|nil child +---@return boolean|nil is_end +local function find_delimiter(bufnr, node, delimiter) + for child, _ in node:iter_children() do + if child:type() == delimiter then + local linenr = child:start() + local line = vim.api.nvim_buf_get_lines(bufnr, linenr, linenr + 1, false)[1] + local end_char = { child:end_() } + local trimmed_after_delim + local escaped_delimiter = delimiter:gsub("[%-%.%+%[%]%(%)%$%^%%%?%*]", "%%%1") + trimmed_after_delim, _ = line:sub(end_char[2] + 1):gsub("[%s" .. escaped_delimiter .. "]*", "") + return child, #trimmed_after_delim == 0 + end + end +end + +---Memoize a function using hash_fn to hash the arguments. +---@generic F: function +---@param fn F +---@param hash_fn fun(...): any +---@return F +local function memoize(fn, hash_fn) + local cache = setmetatable({}, { __mode = "kv" }) ---@type table + + return function(...) + local key = hash_fn(...) + if cache[key] == nil then + local v = fn(...) ---@type any + cache[key] = v ~= nil and v or vim.NIL + end + + local v = cache[key] + return v ~= vim.NIL and v or nil + end +end + +local get_indents = memoize(function(bufnr, root, lang) + local map = { + ["indent.auto"] = {}, + ["indent.begin"] = {}, + ["indent.end"] = {}, + ["indent.dedent"] = {}, + ["indent.branch"] = {}, + ["indent.ignore"] = {}, + ["indent.align"] = {}, + ["indent.zero"] = {}, + } + + --TODO(clason): remove when dropping Nvim 0.8 compat + local query = (ts.query.get or ts.get_query)(lang, "indents") + if not query then + return map + end + for id, node, metadata in query:iter_captures(root, bufnr) do + if query.captures[id]:sub(1, 1) ~= "_" then + map[query.captures[id]][node:id()] = metadata or {} + end + end + + return map +end, function(bufnr, root, lang) + return tostring(bufnr) .. root:id() .. "_" .. lang +end) + +---@param lnum number (1-indexed) +function M.get_indent(lnum) + local bufnr = vim.api.nvim_get_current_buf() + local parser = parsers.get_parser(bufnr) + if not parser or not lnum then + return -1 + end + + --TODO(clason): replace when dropping Nvim 0.8 compat + local root_lang = parsers.get_buf_lang(bufnr) + + -- some languages like Python will actually have worse results when re-parsing at opened new line + if not M.avoid_force_reparsing[root_lang] then + -- Reparse in case we got triggered by ":h indentkeys" + parser:parse { vim.fn.line "w0" - 1, vim.fn.line "w$" } + end + + -- Get language tree with smallest range around node that's not a comment parser + local root, lang_tree ---@type TSNode, LanguageTree + parser:for_each_tree(function(tstree, tree) + if not tstree or M.comment_parsers[tree:lang()] then + return + end + local local_root = tstree:root() + if ts.is_in_node_range(local_root, lnum - 1, 0) then + if not root or node_length(root) >= node_length(local_root) then + root = local_root + lang_tree = tree + end + end + end) + + -- Not likely, but just in case... + if not root then + return 0 + end + + local q = get_indents(vim.api.nvim_get_current_buf(), root, lang_tree:lang()) + local is_empty_line = string.match(getline(lnum), "^%s*$") ~= nil + local node ---@type TSNode + if is_empty_line then + local prevlnum = vim.fn.prevnonblank(lnum) + local indent = vim.fn.indent(prevlnum) + local prevline = vim.trim(getline(prevlnum)) + -- The final position can be trailing spaces, which should not affect indentation + node = get_last_node_at_line(root, prevlnum, indent + #prevline - 1) + if node:type():match "comment" then + -- The final node we capture of the previous line can be a comment node, which should also be ignored + -- Unless the last line is an entire line of comment, ignore the comment range and find the last node again + local first_node = get_first_node_at_line(root, prevlnum, indent) + local _, scol, _, _ = node:range() + if first_node:id() ~= node:id() then + -- In case the last captured node is a trailing comment node, re-trim the string + prevline = vim.trim(prevline:sub(1, scol - indent)) + -- Add back indent as indent of prevline was trimmed away + local col = indent + #prevline - 1 + node = get_last_node_at_line(root, prevlnum, col) + end + end + if q["indent.end"][node:id()] then + node = get_first_node_at_line(root, lnum) + end + else + node = get_first_node_at_line(root, lnum) + end + + local indent_size = vim.fn.shiftwidth() + local indent = 0 + local _, _, root_start = root:start() + if root_start ~= 0 then + -- injected tree + indent = vim.fn.indent(root:start() + 1) + end + + -- tracks to ensure multiple indent levels are not applied for same line + local is_processed_by_row = {} + + if q["indent.zero"][node:id()] then + return 0 + end + + while node do + -- do 'autoindent' if not marked as @indent + if + not q["indent.begin"][node:id()] + and not q["indent.align"][node:id()] + and q["indent.auto"][node:id()] + and node:start() < lnum - 1 + and lnum - 1 <= node:end_() + then + return -1 + end + + -- Do not indent if we are inside an @ignore block. + -- If a node spans from L1,C1 to L2,C2, we know that lines where L1 < line <= L2 would + -- have their indentations contained by the node. + if + not q["indent.begin"][node:id()] + and q["indent.ignore"][node:id()] + and node:start() < lnum - 1 + and lnum - 1 <= node:end_() + then + return 0 + end + + local srow, _, erow = node:range() + + local is_processed = false + + if + not is_processed_by_row[srow] + and ((q["indent.branch"][node:id()] and srow == lnum - 1) or (q["indent.dedent"][node:id()] and srow ~= lnum - 1)) + then + indent = indent - indent_size + is_processed = true + end + + -- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum) + local should_process = not is_processed_by_row[srow] + local is_in_err = false + if should_process then + local parent = node:parent() + is_in_err = parent and parent:has_error() + end + if + should_process + and ( + q["indent.begin"][node:id()] + and (srow ~= erow or is_in_err or q["indent.begin"][node:id()]["indent.immediate"]) + and (srow ~= lnum - 1 or q["indent.begin"][node:id()]["indent.start_at_same_line"]) + ) + then + indent = indent + indent_size + is_processed = true + end + + if is_in_err and not q["indent.align"][node:id()] then + -- only when the node is in error, promote the + -- first child's aligned indent to the error node + -- to work around ((ERROR "X" . (_)) @aligned_indent (#set! "delimiter" "AB")) + -- matching for all X, instead set do + -- (ERROR "X" @aligned_indent (#set! "delimiter" "AB") . (_)) + -- and we will fish it out here. + for c in node:iter_children() do + if q["indent.align"][c:id()] then + q["indent.align"][node:id()] = q["indent.align"][c:id()] + break + end + end + end + -- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum) + if should_process and q["indent.align"][node:id()] and (srow ~= erow or is_in_err) and (srow ~= lnum - 1) then + local metadata = q["indent.align"][node:id()] + local o_delim_node, o_is_last_in_line ---@type TSNode|nil, boolean|nil + local c_delim_node, c_is_last_in_line ---@type TSNode|nil, boolean|nil, boolean|nil + local indent_is_absolute = false + if metadata["indent.open_delimiter"] then + o_delim_node, o_is_last_in_line = find_delimiter(bufnr, node, metadata["indent.open_delimiter"]) + else + o_delim_node = node + end + if metadata["indent.close_delimiter"] then + c_delim_node, c_is_last_in_line = find_delimiter(bufnr, node, metadata["indent.close_delimiter"]) + else + c_delim_node = node + end + + if o_delim_node then + local o_srow, o_scol = o_delim_node:start() + local c_srow = nil + if c_delim_node then + c_srow, _ = c_delim_node:start() + end + if o_is_last_in_line then + -- hanging indent (previous line ended with starting delimiter) + -- should be processed like indent + if should_process then + indent = indent + indent_size * 1 + if c_is_last_in_line then + -- If current line is outside the range of a node marked with `@aligned_indent` + -- Then its indent level shouldn't be affected by `@aligned_indent` node + if c_srow and c_srow < lnum - 1 then + indent = math.max(indent - indent_size, 0) + end + end + end + else + -- aligned indent + if c_is_last_in_line and c_srow and o_srow ~= c_srow and c_srow < lnum - 1 then + -- If current line is outside the range of a node marked with `@aligned_indent` + -- Then its indent level shouldn't be affected by `@aligned_indent` node + indent = math.max(indent - indent_size, 0) + else + indent = o_scol + (metadata["indent.increment"] or 1) + indent_is_absolute = true + end + end + -- deal with the final line + local avoid_last_matching_next = false + if c_srow and c_srow ~= o_srow and c_srow == lnum - 1 then + -- delims end on current line, and are not open and closed same line. + -- then this last line may need additional indent to avoid clashes + -- with the next. `indent.avoid_last_matching_next` controls this behavior, + -- for example this is needed for function parameters. + avoid_last_matching_next = metadata["indent.avoid_last_matching_next"] or false + end + if avoid_last_matching_next then + -- last line must be indented more in cases where + -- it would be same indent as next line (we determine this as one + -- width more than the open indent to avoid confusing with any + -- hanging indents) + if indent <= vim.fn.indent(o_srow + 1) + indent_size then + indent = indent + indent_size * 1 + else + indent = indent + end + end + is_processed = true + if indent_is_absolute then + -- don't allow further indenting by parent nodes, this is an absolute position + return indent + end + end + end + + is_processed_by_row[srow] = is_processed_by_row[srow] or is_processed + + node = node:parent() + end + + return indent +end + +---@type table +local indent_funcs = {} + +---@param bufnr integer +function M.attach(bufnr) + indent_funcs[bufnr] = vim.bo.indentexpr + vim.bo.indentexpr = "nvim_treesitter#indent()" +end + +function M.detach(bufnr) + vim.bo.indentexpr = indent_funcs[bufnr] +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/info.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/info.lua new file mode 100644 index 00000000..6e94b357 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/info.lua @@ -0,0 +1,190 @@ +local api = vim.api +local configs = require "nvim-treesitter.configs" +local parsers = require "nvim-treesitter.parsers" + +local M = {} + +local function install_info() + local max_len = 0 + for _, ft in pairs(parsers.available_parsers()) do + if #ft > max_len then + max_len = #ft + end + end + + local parser_list = parsers.available_parsers() + table.sort(parser_list) + for _, lang in pairs(parser_list) do + local is_installed = #api.nvim_get_runtime_file("parser/" .. lang .. ".so", false) > 0 + api.nvim_out_write(lang .. string.rep(" ", max_len - #lang + 1)) + if is_installed then + api.nvim_out_write "[✓] installed\n" + elseif pcall(vim.treesitter.inspect_lang, lang) then + api.nvim_out_write "[✗] not installed (but still loaded. Restart Neovim!)\n" + else + api.nvim_out_write "[✗] not installed\n" + end + end +end + +-- Sort a list of modules into namespaces. +-- {'mod1', 'mod2.sub1', 'mod2.sub2', 'mod3'} +-- -> +-- { default = {'mod1', 'mod3'}, mod2 = {'sub1', 'sub2'}} +---@param modulelist string[] +---@return table +local function namespace_modules(modulelist) + local modules = {} + for _, module in ipairs(modulelist) do + if module:find "%." then + local namespace, submodule = module:match "^(.*)%.(.*)$" + if not modules[namespace] then + modules[namespace] = {} + end + table.insert(modules[namespace], submodule) + else + if not modules.default then + modules.default = {} + end + table.insert(modules.default, module) + end + end + return modules +end + +---@param list string[] +---@return integer length +local function longest_string_length(list) + local length = 0 + for _, value in ipairs(list) do + if #value > length then + length = #value + end + end + return length +end + +---@param curbuf integer +---@param origbuf integer +---@param parserlist string[] +---@param namespace string +---@param modulelist string[] +local function append_module_table(curbuf, origbuf, parserlist, namespace, modulelist) + local maxlen_parser = longest_string_length(parserlist) + table.sort(modulelist) + + -- header + local header = ">> " .. namespace .. string.rep(" ", maxlen_parser - #namespace - 1) + for _, module in pairs(modulelist) do + header = header .. module .. " " + end + api.nvim_buf_set_lines(curbuf, -1, -1, true, { header }) + + -- actual table + for _, parser in ipairs(parserlist) do + local padding = string.rep(" ", maxlen_parser - #parser + 2) + local line = parser .. padding + local namespace_prefix = (namespace == "default") and "" or namespace .. "." + for _, module in pairs(modulelist) do + local modlen = #module + module = namespace_prefix .. module + if configs.is_enabled(module, parser, origbuf) then + line = line .. "✓" + else + line = line .. "✗" + end + line = line .. string.rep(" ", modlen + 1) + end + api.nvim_buf_set_lines(curbuf, -1, -1, true, { line }) + end + + api.nvim_buf_set_lines(curbuf, -1, -1, true, { "" }) +end + +local function print_info_modules(parserlist, module) + local origbuf = api.nvim_get_current_buf() + api.nvim_command "enew" + local curbuf = api.nvim_get_current_buf() + + local modules + if module then + modules = namespace_modules { module } + else + modules = namespace_modules(configs.available_modules()) + end + + ---@type string[] + local namespaces = {} + for k, _ in pairs(modules) do + table.insert(namespaces, k) + end + table.sort(namespaces) + + table.sort(parserlist) + for _, namespace in ipairs(namespaces) do + append_module_table(curbuf, origbuf, parserlist, namespace, modules[namespace]) + end + + api.nvim_buf_set_option(curbuf, "modified", false) + api.nvim_buf_set_option(curbuf, "buftype", "nofile") + vim.cmd [[ + syntax match TSModuleInfoGood /✓/ + syntax match TSModuleInfoBad /✗/ + syntax match TSModuleInfoHeader /^>>.*$/ contains=TSModuleInfoNamespace + syntax match TSModuleInfoNamespace /^>> \w*/ contained + syntax match TSModuleInfoParser /^[^> ]*\ze / + ]] + + local highlights = { + TSModuleInfoGood = { fg = "LightGreen", bold = true, default = true }, + TSModuleInfoBad = { fg = "Crimson", default = true }, + TSModuleInfoHeader = { link = "Type", default = true }, + TSModuleInfoNamespace = { link = "Statement", default = true }, + TSModuleInfoParser = { link = "Identifier", default = true }, + } + for k, v in pairs(highlights) do + api.nvim_set_hl(0, k, v) + end +end + +local function module_info(module) + if module and not configs.get_module(module) then + return + end + + local parserlist = parsers.available_parsers() + if module then + print_info_modules(parserlist, module) + else + print_info_modules(parserlist) + end +end + +---@return string[] +function M.installed_parsers() + local installed = {} + for _, p in pairs(parsers.available_parsers()) do + if parsers.has_parser(p) then + table.insert(installed, p) + end + end + return installed +end + +M.commands = { + TSInstallInfo = { + run = install_info, + args = { + "-nargs=0", + }, + }, + TSModuleInfo = { + run = module_info, + args = { + "-nargs=?", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, +} + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/install.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/install.lua new file mode 100644 index 00000000..ec7d6245 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/install.lua @@ -0,0 +1,777 @@ +local api = vim.api +local fn = vim.fn +local luv = vim.loop + +local utils = require "nvim-treesitter.utils" +local parsers = require "nvim-treesitter.parsers" +local info = require "nvim-treesitter.info" +local configs = require "nvim-treesitter.configs" +local shell = require "nvim-treesitter.shell_command_selectors" +local compat = require "nvim-treesitter.compat" + +local M = {} + +---@class LockfileInfo +---@field revision string + +---@type table +local lockfile = {} + +M.compilers = { vim.fn.getenv "CC", "cc", "gcc", "clang", "cl", "zig" } +M.prefer_git = fn.has "win32" == 1 +M.command_extra_args = {} +M.ts_generate_args = nil + +local started_commands = 0 +local finished_commands = 0 +local failed_commands = 0 +local complete_std_output = {} +local complete_error_output = {} + +local function reset_progress_counter() + if started_commands ~= finished_commands then + return + end + started_commands = 0 + finished_commands = 0 + failed_commands = 0 + complete_std_output = {} + complete_error_output = {} +end + +local function get_job_status() + return "[nvim-treesitter] [" + .. finished_commands + .. "/" + .. started_commands + .. (failed_commands > 0 and ", failed: " .. failed_commands or "") + .. "]" +end + +---@param lang string +---@return function +local function reattach_if_possible_fn(lang, error_on_fail) + return function() + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + if parsers.get_buf_lang(buf) == lang then + vim._ts_remove_language(lang) + local ok, err + if vim.treesitter.language.add then + local ft = vim.bo[buf].filetype + ok, err = pcall(vim.treesitter.language.add, lang, { filetype = ft }) + else + ok, err = pcall(compat.require_language, lang) + end + if not ok and error_on_fail then + vim.notify("Could not load parser for " .. lang .. ": " .. vim.inspect(err)) + end + for _, mod in ipairs(require("nvim-treesitter.configs").available_modules()) do + if ok then + require("nvim-treesitter.configs").reattach_module(mod, buf, lang) + else + require("nvim-treesitter.configs").detach_module(mod, buf) + end + end + end + end + end +end + +---@param lang string +---@param validate boolean|nil +---@return InstallInfo +local function get_parser_install_info(lang, validate) + local parser_config = parsers.get_parser_configs()[lang] + + if not parser_config then + error('Parser not available for language "' .. lang .. '"') + end + + local install_info = parser_config.install_info + + if validate then + vim.validate { + url = { install_info.url, "string" }, + files = { install_info.files, "table" }, + } + end + + return install_info +end + +local function load_lockfile() + local filename = utils.join_path(utils.get_package_path(), "lockfile.json") + lockfile = vim.fn.filereadable(filename) == 1 and vim.fn.json_decode(vim.fn.readfile(filename)) or {} +end + +local function is_ignored_parser(lang) + return vim.tbl_contains(configs.get_ignored_parser_installs(), lang) +end + +---@param lang string +---@return string|nil +local function get_revision(lang) + if #lockfile == 0 then + load_lockfile() + end + + local install_info = get_parser_install_info(lang) + if install_info.revision then + return install_info.revision + end + + if lockfile[lang] then + return lockfile[lang].revision + end +end + +---@param lang string +---@return string|nil +local function get_installed_revision(lang) + local lang_file = utils.join_path(configs.get_parser_info_dir(), lang .. ".revision") + if vim.fn.filereadable(lang_file) == 1 then + return vim.fn.readfile(lang_file)[1] + end +end + +-- Clean path for use in a prefix comparison +---@param input string +---@return string +local function clean_path(input) + local pth = vim.fn.fnamemodify(input, ":p") + if fn.has "win32" == 1 then + pth = pth:gsub("/", "\\") + end + return pth +end + +-- Checks if parser is installed with nvim-treesitter +---@param lang string +---@return boolean +local function is_installed(lang) + local matched_parsers = vim.api.nvim_get_runtime_file("parser/" .. lang .. ".so", true) or {} + local install_dir = configs.get_parser_install_dir() + if not install_dir then + return false + end + install_dir = clean_path(install_dir) + for _, path in ipairs(matched_parsers) do + local abspath = clean_path(path) + if vim.startswith(abspath, install_dir) then + return true + end + end + return false +end + +---@param lang string +---@return boolean +local function needs_update(lang) + local revision = get_revision(lang) + return not revision or revision ~= get_installed_revision(lang) +end + +---@return string[] +local function outdated_parsers() + return vim.tbl_filter(function(lang) ---@param lang string + return is_installed(lang) and needs_update(lang) + end, info.installed_parsers()) +end + +---@param handle userdata +---@param is_stderr boolean +local function onread(handle, is_stderr) + return function(_, data) + if data then + if is_stderr then + complete_error_output[handle] = (complete_error_output[handle] or "") .. data + else + complete_std_output[handle] = (complete_std_output[handle] or "") .. data + end + end + end +end + +function M.iter_cmd(cmd_list, i, lang, success_message) + if i == 1 then + started_commands = started_commands + 1 + end + if i == #cmd_list + 1 then + finished_commands = finished_commands + 1 + return print(get_job_status() .. " " .. success_message) + end + + local attr = cmd_list[i] + if attr.info then + print(get_job_status() .. " " .. attr.info) + end + + if attr.opts and attr.opts.args and M.command_extra_args[attr.cmd] then + vim.list_extend(attr.opts.args, M.command_extra_args[attr.cmd]) + end + + if type(attr.cmd) == "function" then + local ok, err = pcall(attr.cmd) + if ok then + M.iter_cmd(cmd_list, i + 1, lang, success_message) + else + failed_commands = failed_commands + 1 + finished_commands = finished_commands + 1 + return api.nvim_err_writeln( + (attr.err or ("Failed to execute the following command:\n" .. vim.inspect(attr))) .. "\n" .. vim.inspect(err) + ) + end + else + local handle + local stdout = luv.new_pipe(false) + local stderr = luv.new_pipe(false) + attr.opts.stdio = { nil, stdout, stderr } + ---@type userdata + handle = luv.spawn( + attr.cmd, + attr.opts, + vim.schedule_wrap(function(code) + if code ~= 0 then + stdout:read_stop() + stderr:read_stop() + end + stdout:close() + stderr:close() + handle:close() + if code ~= 0 then + failed_commands = failed_commands + 1 + finished_commands = finished_commands + 1 + if complete_std_output[handle] and complete_std_output[handle] ~= "" then + print(complete_std_output[handle]) + end + + local err_msg = complete_error_output[handle] or "" + api.nvim_err_writeln( + "nvim-treesitter[" + .. lang + .. "]: " + .. (attr.err or ("Failed to execute the following command:\n" .. vim.inspect(attr))) + .. "\n" + .. err_msg + ) + return + end + M.iter_cmd(cmd_list, i + 1, lang, success_message) + end) + ) + luv.read_start(stdout, onread(handle, false)) + luv.read_start(stderr, onread(handle, true)) + end +end + +---@param cmd Command +---@return string command +local function get_command(cmd) + local options = "" + if cmd.opts and cmd.opts.args then + if M.command_extra_args[cmd.cmd] then + vim.list_extend(cmd.opts.args, M.command_extra_args[cmd.cmd]) + end + for _, opt in ipairs(cmd.opts.args) do + options = string.format("%s %s", options, opt) + end + end + + local command = string.format("%s %s", cmd.cmd, options) + if cmd.opts and cmd.opts.cwd then + command = shell.make_directory_change_for_command(cmd.opts.cwd, command) + end + return command +end + +---@param cmd_list Command[] +---@return boolean +local function iter_cmd_sync(cmd_list) + for _, cmd in ipairs(cmd_list) do + if cmd.info then + print(cmd.info) + end + + if type(cmd.cmd) == "function" then + cmd.cmd() + else + local ret = vim.fn.system(get_command(cmd)) + if vim.v.shell_error ~= 0 then + print(ret) + api.nvim_err_writeln( + (cmd.err and cmd.err .. "\n" or "") .. "Failed to execute the following command:\n" .. vim.inspect(cmd) + ) + return false + end + end + end + + return true +end + +---@param cache_folder string +---@param install_folder string +---@param lang string +---@param repo InstallInfo +---@param with_sync boolean +---@param generate_from_grammar boolean +local function run_install(cache_folder, install_folder, lang, repo, with_sync, generate_from_grammar) + parsers.reset_cache() + + local path_sep = utils.get_path_sep() + + local project_name = "tree-sitter-" .. lang + local maybe_local_path = vim.fn.expand(repo.url) + local from_local_path = vim.fn.isdirectory(maybe_local_path) == 1 + if from_local_path then + repo.url = maybe_local_path + end + + ---@type string compile_location only needed for typescript installs. + local compile_location + if from_local_path then + compile_location = repo.url + if repo.location then + compile_location = utils.join_path(compile_location, repo.location) + end + else + local repo_location = project_name + if repo.location then + repo_location = repo_location .. "/" .. repo.location + end + repo_location = repo_location:gsub("/", path_sep) + compile_location = utils.join_path(cache_folder, repo_location) + end + local parser_lib_name = utils.join_path(install_folder, lang) .. ".so" + + generate_from_grammar = repo.requires_generate_from_grammar or generate_from_grammar + + if generate_from_grammar and vim.fn.executable "tree-sitter" ~= 1 then + api.nvim_err_writeln "tree-sitter CLI not found: `tree-sitter` is not executable!" + if repo.requires_generate_from_grammar then + api.nvim_err_writeln( + "tree-sitter CLI is needed because `" + .. lang + .. "` is marked that it needs " + .. "to be generated from the grammar definitions to be compatible with nvim!" + ) + end + return + else + if not M.ts_generate_args then + local ts_cli_version = utils.ts_cli_version() + if ts_cli_version and vim.split(ts_cli_version, " ")[1] > "0.20.2" then + M.ts_generate_args = { "generate", "--no-bindings", "--abi", vim.treesitter.language_version } + else + M.ts_generate_args = { "generate", "--no-bindings" } + end + end + end + if generate_from_grammar and vim.fn.executable "node" ~= 1 then + api.nvim_err_writeln "Node JS not found: `node` is not executable!" + return + end + local cc = shell.select_executable(M.compilers) + if not cc then + api.nvim_err_writeln('No C compiler found! "' .. table.concat( + vim.tbl_filter(function(c) ---@param c string + return type(c) == "string" + end, M.compilers), + '", "' + ) .. '" are not executable.') + return + end + + local revision = repo.revision + if not revision then + revision = get_revision(lang) + end + + ---@class Command + ---@field cmd string + ---@field info string + ---@field err string + ---@field opts CmdOpts + + ---@class CmdOpts + ---@field args string[] + ---@field cwd string + + ---@type Command[] + local command_list = {} + if not from_local_path then + vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) }) + vim.list_extend( + command_list, + shell.select_download_commands(repo, project_name, cache_folder, revision, M.prefer_git) + ) + end + if generate_from_grammar then + if repo.generate_requires_npm then + if vim.fn.executable "npm" ~= 1 then + api.nvim_err_writeln("`" .. lang .. "` requires NPM to be installed from grammar.js") + return + end + vim.list_extend(command_list, { + { + cmd = "npm", + info = "Installing NPM dependencies of " .. lang .. " parser", + err = "Error during `npm install` (required for parser generation of " .. lang .. " with npm dependencies)", + opts = { + args = { "install" }, + cwd = compile_location, + }, + }, + }) + end + vim.list_extend(command_list, { + { + cmd = vim.fn.exepath "tree-sitter", + info = "Generating source files from grammar.js...", + err = 'Error during "tree-sitter generate"', + opts = { + args = M.ts_generate_args, + cwd = compile_location, + }, + }, + }) + end + vim.list_extend(command_list, { + shell.select_compile_command(repo, cc, compile_location), + shell.select_mv_cmd("parser.so", parser_lib_name, compile_location), + { + cmd = function() + vim.fn.writefile({ revision or "" }, utils.join_path(configs.get_parser_info_dir() or "", lang .. ".revision")) + end, + }, + { -- auto-attach modules after installation + cmd = reattach_if_possible_fn(lang, true), + }, + }) + if not from_local_path then + vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) }) + end + + if with_sync then + if iter_cmd_sync(command_list) == true then + print("Treesitter parser for " .. lang .. " has been installed") + end + else + M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been installed") + end +end + +---@param lang string +---@param ask_reinstall boolean|string +---@param cache_folder string +---@param install_folder string +---@param with_sync boolean +---@param generate_from_grammar boolean +local function install_lang(lang, ask_reinstall, cache_folder, install_folder, with_sync, generate_from_grammar) + if is_installed(lang) and ask_reinstall ~= "force" then + if not ask_reinstall then + return + end + + local yesno = fn.input(lang .. " parser already available: would you like to reinstall ? y/n: ") + print "\n " + if not string.match(yesno, "^y.*") then + return + end + end + + local ok, install_info = pcall(get_parser_install_info, lang, true) + if not ok then + vim.notify("Installation not possible: " .. install_info, vim.log.levels.ERROR) + if not parsers.get_parser_configs()[lang] then + vim.notify( + "See https://github.com/nvim-treesitter/nvim-treesitter/#adding-parsers on how to add a new parser!", + vim.log.levels.INFO + ) + end + return + end + + run_install(cache_folder, install_folder, lang, install_info, with_sync, generate_from_grammar) +end + +---@class InstallOptions +---@field with_sync boolean +---@field ask_reinstall boolean|string +---@field generate_from_grammar boolean +---@field exclude_configured_parsers boolean + +-- Install a parser +---@param options? InstallOptions +---@return function +local function install(options) + options = options or {} + local with_sync = options.with_sync + local ask_reinstall = options.ask_reinstall + local generate_from_grammar = options.generate_from_grammar + local exclude_configured_parsers = options.exclude_configured_parsers + + return function(...) + if fn.executable "git" == 0 then + return api.nvim_err_writeln "Git is required on your system to run this command" + end + + local cache_folder, err = utils.get_cache_dir() + if err then + return api.nvim_err_writeln(err) + end + assert(cache_folder) + + local install_folder + install_folder, err = configs.get_parser_install_dir() + if err then + return api.nvim_err_writeln(err) + end + install_folder = install_folder and clean_path(install_folder) + assert(install_folder) + + local languages ---@type string[] + local ask ---@type boolean|string + if ... == "all" then + languages = parsers.available_parsers() + ask = false + else + languages = compat.flatten { ... } + ask = ask_reinstall + end + + if exclude_configured_parsers then + languages = utils.difference(languages, configs.get_ignored_parser_installs()) + end + + if #languages > 1 then + reset_progress_counter() + end + + for _, lang in ipairs(languages) do + install_lang(lang, ask, cache_folder, install_folder, with_sync, generate_from_grammar) + end + end +end + +function M.setup_auto_install() + vim.api.nvim_create_autocmd("FileType", { + pattern = { "*" }, + group = vim.api.nvim_create_augroup("NvimTreesitter-auto_install", { clear = true }), + callback = function() + local lang = parsers.get_buf_lang() + if parsers.get_parser_configs()[lang] and not is_installed(lang) and not is_ignored_parser(lang) then + install() { lang } + end + end, + }) +end + +function M.update(options) + options = options or {} + return function(...) + M.lockfile = {} + reset_progress_counter() + if ... and ... ~= "all" then + ---@type string[] + local languages = compat.flatten { ... } + local installed = 0 + for _, lang in ipairs(languages) do + if (not is_installed(lang)) or (needs_update(lang)) then + installed = installed + 1 + install { + ask_reinstall = "force", + with_sync = options.with_sync, + }(lang) + end + end + if installed == 0 then + utils.notify "Parsers are up-to-date!" + end + else + local parsers_to_update = outdated_parsers() or info.installed_parsers() + if #parsers_to_update == 0 then + utils.notify "All parsers are up-to-date!" + end + for _, lang in pairs(parsers_to_update) do + install { + ask_reinstall = "force", + exclude_configured_parsers = true, + with_sync = options.with_sync, + }(lang) + end + end + end +end + +function M.uninstall(...) + if vim.tbl_contains({ "all" }, ...) then + reset_progress_counter() + local installed = info.installed_parsers() + M.uninstall(installed) + elseif ... then + local ensure_installed_parsers = configs.get_ensure_installed_parsers() + if ensure_installed_parsers == "all" then + ensure_installed_parsers = parsers.available_parsers() + end + ensure_installed_parsers = utils.difference(ensure_installed_parsers, configs.get_ignored_parser_installs()) + + ---@type string[] + local languages = compat.flatten { ... } + for _, lang in ipairs(languages) do + local install_dir, err = configs.get_parser_install_dir() + if err then + return api.nvim_err_writeln(err) + end + install_dir = install_dir and clean_path(install_dir) + + if vim.tbl_contains(ensure_installed_parsers, lang) then + vim.notify( + "Uninstalling " + .. lang + .. '. But the parser is still configured in "ensure_installed" setting of nvim-treesitter.' + .. " Please consider updating your config!", + vim.log.levels.ERROR + ) + end + + local parser_lib = utils.join_path(install_dir, lang) .. ".so" + local all_parsers = vim.api.nvim_get_runtime_file("parser/" .. lang .. ".so", true) + if vim.fn.filereadable(parser_lib) == 1 then + local command_list = { + shell.select_rm_file_cmd(parser_lib, "Uninstalling parser for " .. lang), + { + cmd = function() + local all_parsers_after_deletion = vim.api.nvim_get_runtime_file("parser/" .. lang .. ".so", true) + if #all_parsers_after_deletion > 0 then + vim.notify( + "Tried to uninstall parser for " + .. lang + .. "! But the parser is still installed (not by nvim-treesitter):" + .. table.concat(all_parsers_after_deletion, ", "), + vim.log.levels.ERROR + ) + end + end, + }, + { -- auto-reattach or detach modules after uninstallation + cmd = reattach_if_possible_fn(lang, false), + }, + } + M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been uninstalled") + elseif #all_parsers > 0 then + vim.notify( + "Parser for " + .. lang + .. " is installed! But not by nvim-treesitter! Please manually remove the following files: " + .. table.concat(all_parsers, ", "), + vim.log.levels.ERROR + ) + end + end + end +end + +function M.write_lockfile(verbose, skip_langs) + local sorted_parsers = {} ---@type Parser[] + -- Load previous lockfile + load_lockfile() + skip_langs = skip_langs or {} + + for k, v in pairs(parsers.get_parser_configs()) do + table.insert(sorted_parsers, { name = k, parser = v }) + end + + ---@param a Parser + ---@param b Parser + table.sort(sorted_parsers, function(a, b) + return a.name < b.name + end) + + for _, v in ipairs(sorted_parsers) do + if not vim.tbl_contains(skip_langs, v.name) then + -- I'm sure this can be done in aync way with iter_cmd + local sha ---@type string + if v.parser.install_info.branch then + sha = vim.split( + vim.fn.systemlist( + "git ls-remote " .. v.parser.install_info.url .. " | grep refs/heads/" .. v.parser.install_info.branch + )[1], + "\t" + )[1] + else + sha = vim.split(vim.fn.systemlist("git ls-remote " .. v.parser.install_info.url)[1], "\t")[1] + end + lockfile[v.name] = { revision = sha } + if verbose then + print(v.name .. ": " .. sha) + end + else + print("Skipping " .. v.name) + end + end + + if verbose then + print(vim.inspect(lockfile)) + end + vim.fn.writefile( + vim.fn.split(vim.fn.json_encode(lockfile), "\n"), + utils.join_path(utils.get_package_path(), "lockfile.json") + ) +end + +M.ensure_installed = install { exclude_configured_parsers = true } +M.ensure_installed_sync = install { with_sync = true, exclude_configured_parsers = true } + +M.commands = { + TSInstall = { + run = install { ask_reinstall = true }, + ["run!"] = install { ask_reinstall = "force" }, + args = { + "-nargs=+", + "-bang", + "-complete=custom,nvim_treesitter#installable_parsers", + }, + }, + TSInstallFromGrammar = { + run = install { generate_from_grammar = true, ask_reinstall = true }, + ["run!"] = install { generate_from_grammar = true, ask_reinstall = "force" }, + args = { + "-nargs=+", + "-bang", + "-complete=custom,nvim_treesitter#installable_parsers", + }, + }, + TSInstallSync = { + run = install { with_sync = true, ask_reinstall = true }, + ["run!"] = install { with_sync = true, ask_reinstall = "force" }, + args = { + "-nargs=+", + "-bang", + "-complete=custom,nvim_treesitter#installable_parsers", + }, + }, + TSUpdate = { + run = M.update {}, + args = { + "-nargs=*", + "-complete=custom,nvim_treesitter#installed_parsers", + }, + }, + TSUpdateSync = { + run = M.update { with_sync = true }, + args = { + "-nargs=*", + "-complete=custom,nvim_treesitter#installed_parsers", + }, + }, + TSUninstall = { + run = M.uninstall, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#installed_parsers", + }, + }, +} + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/locals.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/locals.lua new file mode 100644 index 00000000..fed835bb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/locals.lua @@ -0,0 +1,365 @@ +-- Functions to handle locals +-- Locals are a generalization of definition and scopes +-- its the way nvim-treesitter uses to "understand" the code + +local queries = require "nvim-treesitter.query" +local ts_utils = require "nvim-treesitter.ts_utils" +local ts = vim.treesitter +local api = vim.api + +local M = {} + +function M.collect_locals(bufnr) + return queries.collect_group_results(bufnr, "locals") +end + +-- Iterates matches from a locals query file. +-- @param bufnr the buffer +-- @param root the root node +function M.iter_locals(bufnr, root) + return queries.iter_group_results(bufnr, "locals", root) +end + +---@param bufnr integer +---@return any +function M.get_locals(bufnr) + return queries.get_matches(bufnr, "locals") +end + +-- Creates unique id for a node based on text and range +---@param scope TSNode: the scope node of the definition +---@param node_text string: the node text to use +---@return string: a string id +function M.get_definition_id(scope, node_text) + -- Add a valid starting character in case node text doesn't start with a valid one. + return table.concat({ "k", node_text or "", scope:range() }, "_") +end + +function M.get_definitions(bufnr) + local locals = M.get_locals(bufnr) + + local defs = {} + + for _, loc in ipairs(locals) do + if loc["local"]["definition"] then + table.insert(defs, loc["local"]["definition"]) + end + end + + return defs +end + +function M.get_scopes(bufnr) + local locals = M.get_locals(bufnr) + + local scopes = {} + + for _, loc in ipairs(locals) do + if loc["local"]["scope"] and loc["local"]["scope"].node then + table.insert(scopes, loc["local"]["scope"].node) + end + end + + return scopes +end + +function M.get_references(bufnr) + local locals = M.get_locals(bufnr) + + local refs = {} + + for _, loc in ipairs(locals) do + if loc["local"]["reference"] and loc["local"]["reference"].node then + table.insert(refs, loc["local"]["reference"].node) + end + end + + return refs +end + +-- Gets a table with all the scopes containing a node +-- The order is from most specific to least (bottom up) +---@param node TSNode +---@param bufnr integer +---@return TSNode[] +function M.get_scope_tree(node, bufnr) + local scopes = {} ---@type TSNode[] + + for scope in M.iter_scope_tree(node, bufnr) do + table.insert(scopes, scope) + end + + return scopes +end + +-- Iterates over a nodes scopes moving from the bottom up +---@param node TSNode +---@param bufnr integer +---@return fun(): TSNode|nil +function M.iter_scope_tree(node, bufnr) + local last_node = node + return function() + if not last_node then + return + end + + local scope = M.containing_scope(last_node, bufnr, false) or ts_utils.get_root_for_node(node) + + last_node = scope:parent() + + return scope + end +end + +-- Gets a table of all nodes and their 'kinds' from a locals list +---@param local_def any: the local list result +---@return table: a list of node entries +function M.get_local_nodes(local_def) + local result = {} + + M.recurse_local_nodes(local_def, function(def, _node, kind) + table.insert(result, vim.tbl_extend("keep", { kind = kind }, def)) + end) + + return result +end + +-- Recurse locals results until a node is found. +-- The accumulator function is given +-- * The table of the node +-- * The node +-- * The full definition match `@definition.var.something` -> 'var.something' +-- * The last definition match `@definition.var.something` -> 'something' +---@param local_def any The locals result +---@param accumulator function The accumulator function +---@param full_match? string The full match path to append to +---@param last_match? string The last match +function M.recurse_local_nodes(local_def, accumulator, full_match, last_match) + if type(local_def) ~= "table" then + return + end + + if local_def.node then + accumulator(local_def, local_def.node, full_match, last_match) + else + for match_key, def in pairs(local_def) do + M.recurse_local_nodes(def, accumulator, full_match and (full_match .. "." .. match_key) or match_key, match_key) + end + end +end + +-- Get a single dimension table to look definition nodes. +-- Keys are generated by using the range of the containing scope and the text of the definition node. +-- This makes looking up a definition for a given scope a simple key lookup. +-- +-- This is memoized by buffer tick. If the function is called in succession +-- without the buffer tick changing, then the previous result will be used +-- since the syntax tree hasn't changed. +-- +-- Usage lookups require finding the definition of the node, so `find_definition` +-- is called very frequently, which is why this lookup must be fast as possible. +-- +---@param bufnr integer: the buffer +---@return table result: a table for looking up definitions +M.get_definitions_lookup_table = ts_utils.memoize_by_buf_tick(function(bufnr) + local definitions = M.get_definitions(bufnr) + local result = {} + + for _, definition in ipairs(definitions) do + for _, node_entry in ipairs(M.get_local_nodes(definition)) do + local scopes = M.get_definition_scopes(node_entry.node, bufnr, node_entry.scope) + -- Always use the highest valid scope + local scope = scopes[#scopes] + local node_text = ts.get_node_text(node_entry.node, bufnr) + local id = M.get_definition_id(scope, node_text) + + result[id] = node_entry + end + end + + return result +end) + +-- Gets all the scopes of a definition based on the scope type +-- Scope types can be +-- +-- "parent": Uses the parent of the containing scope, basically, skipping a scope +-- "global": Uses the top most scope +-- "local": Uses the containing scope of the definition. This is the default +-- +---@param node TSNode: the definition node +---@param bufnr integer: the buffer +---@param scope_type string: the scope type +function M.get_definition_scopes(node, bufnr, scope_type) + local scopes = {} + local scope_count = 1 ---@type integer|nil + + -- Definition is valid for the containing scope + -- and the containing scope of that scope + if scope_type == "parent" then + scope_count = 2 + -- Definition is valid in all parent scopes + elseif scope_type == "global" then + scope_count = nil + end + + local i = 0 + for scope in M.iter_scope_tree(node, bufnr) do + table.insert(scopes, scope) + i = i + 1 + + if scope_count and i >= scope_count then + break + end + end + + return scopes +end + +---@param node TSNode +---@param bufnr integer +---@return TSNode node +---@return TSNode scope +---@return string|nil kind +function M.find_definition(node, bufnr) + local def_lookup = M.get_definitions_lookup_table(bufnr) + local node_text = ts.get_node_text(node, bufnr) + + for scope in M.iter_scope_tree(node, bufnr) do + local id = M.get_definition_id(scope, node_text) + + if def_lookup[id] then + local entry = def_lookup[id] + + return entry.node, scope, entry.kind + end + end + + return node, ts_utils.get_root_for_node(node), nil +end + +-- Finds usages of a node in a given scope. +---@param node TSNode the node to find usages for +---@param scope_node TSNode the node to look within +---@return TSNode[]: a list of nodes +function M.find_usages(node, scope_node, bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + local node_text = ts.get_node_text(node, bufnr) + + if not node_text or #node_text < 1 then + return {} + end + + local scope_node = scope_node or ts_utils.get_root_for_node(node) + local usages = {} + + for match in M.iter_locals(bufnr, scope_node) do + match = match["local"] + if match.reference and match.reference.node and ts.get_node_text(match.reference.node, bufnr) == node_text then + local def_node, _, kind = M.find_definition(match.reference.node, bufnr) + + if kind == nil or def_node == node then + table.insert(usages, match.reference.node) + end + end + end + + return usages +end + +---@param node TSNode +---@param bufnr? integer +---@param allow_scope? boolean +---@return TSNode|nil +function M.containing_scope(node, bufnr, allow_scope) + local bufnr = bufnr or api.nvim_get_current_buf() + local allow_scope = allow_scope == nil or allow_scope == true + + local scopes = M.get_scopes(bufnr) + if not node or not scopes then + return + end + + local iter_node = node + + while iter_node ~= nil and not vim.tbl_contains(scopes, iter_node) do + iter_node = iter_node:parent() + end + + return iter_node or (allow_scope and node or nil) +end + +function M.nested_scope(node, cursor_pos) + local bufnr = api.nvim_get_current_buf() + + local scopes = M.get_scopes(bufnr) + if not node or not scopes then + return + end + + local row = cursor_pos.row ---@type integer + local col = cursor_pos.col ---@type integer + local scope = M.containing_scope(node) + + for _, child in ipairs(ts_utils.get_named_children(scope)) do + local row_, col_ = child:start() + if vim.tbl_contains(scopes, child) and ((row_ + 1 == row and col_ > col) or row_ + 1 > row) then + return child + end + end +end + +function M.next_scope(node) + local bufnr = api.nvim_get_current_buf() + + local scopes = M.get_scopes(bufnr) + if not node or not scopes then + return + end + + local scope = M.containing_scope(node) + + local parent = scope:parent() + if not parent then + return + end + + local is_prev = true + for _, child in ipairs(ts_utils.get_named_children(parent)) do + if child == scope then + is_prev = false + elseif not is_prev and vim.tbl_contains(scopes, child) then + return child + end + end +end + +---@param node TSNode +---@return TSNode|nil +function M.previous_scope(node) + local bufnr = api.nvim_get_current_buf() + + local scopes = M.get_scopes(bufnr) + if not node or not scopes then + return + end + + local scope = M.containing_scope(node) + + local parent = scope:parent() + if not parent then + return + end + + local is_prev = true + local children = ts_utils.get_named_children(parent) + for i = #children, 1, -1 do + if children[i] == scope then + is_prev = false + elseif not is_prev and vim.tbl_contains(scopes, children[i]) then + return children[i] + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/parsers.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/parsers.lua new file mode 100644 index 00000000..d2abb9d5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/parsers.lua @@ -0,0 +1,2518 @@ +local api = vim.api +local ts = vim.treesitter + +local new_lang_api = ts.language.register ~= nil + +local filetype_to_parsername = {} + +if new_lang_api then + filetype_to_parsername = setmetatable({}, { + __newindex = function(_, k, v) + require("nvim-treesitter.utils").notify( + "filetype_to_parsername is deprecated, please use 'vim.treesitter.language.register'", + vim.log.levels.WARN + ) + ts.language.register(v, k) + end, + }) +end + +local function register_lang(lang, ft) + if new_lang_api then + ts.language.register(lang, ft) + return + end + filetype_to_parsername[ft] = lang +end + +for ft, lang in pairs { + automake = "make", + javascriptreact = "javascript", + ecma = "javascript", + jsx = "javascript", + sh = "bash", + html_tags = "html", + ["typescript.tsx"] = "tsx", + ["html.handlebars"] = "glimmer", + systemverilog = "verilog", + pandoc = "markdown", + rmd = "markdown", + quarto = "markdown", + dosini = "ini", + confini = "ini", + svg = "xml", + xsd = "xml", + xslt = "xml", + expect = "tcl", + mysql = "sql", + sbt = "scala", + neomuttrc = "muttrc", +} do + register_lang(lang, ft) +end + +---@class InstallInfo +---@field url string +---@field branch string|nil +---@field revision string|nil +---@field files string[] +---@field generate_requires_npm boolean|nil +---@field requires_generate_from_grammar boolean|nil +---@field location string|nil +---@field use_makefile boolean|nil +---@field cxx_standard string|nil + +---@class ParserInfo +---@field install_info InstallInfo +---@field filetype string +---@field maintainers string[] +---@field experimental boolean|nil +---@field readme_name string|nil + +---@type ParserInfo[] +local list = setmetatable({}, { + __newindex = function(table, parsername, parserconfig) + rawset(table, parsername, parserconfig) + register_lang(parsername, parserconfig.filetype or parsername) + end, +}) + +list.ada = { + install_info = { + url = "https://github.com/briot/tree-sitter-ada", + files = { "src/parser.c" }, + }, + maintainers = { "@briot" }, +} + +list.agda = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-agda", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@Decodetalkers" }, +} + +list.angular = { + install_info = { + url = "https://github.com/dlvandenberg/tree-sitter-angular", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@dlvandenberg" }, + experimental = true, +} + +list.apex = { + install_info = { + url = "https://github.com/aheber/tree-sitter-sfapex", + files = { "src/parser.c" }, + location = "apex", + }, + maintainers = { "@aheber" }, +} + +list.arduino = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-arduino", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.asm = { + install_info = { + url = "https://github.com/RubixDev/tree-sitter-asm", + files = { "src/parser.c" }, + }, + maintainers = { "@RubixDev" }, +} + +list.astro = { + install_info = { + url = "https://github.com/virchau13/tree-sitter-astro", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@virchau13" }, +} + +list.authzed = { + install_info = { + url = "https://github.com/mleonidas/tree-sitter-authzed", + files = { "src/parser.c" }, + }, + maintainers = { "@mattpolzin" }, +} + +list.awk = { + install_info = { + url = "https://github.com/Beaglefoot/tree-sitter-awk", + files = { "src/parser.c", "src/scanner.c" }, + }, +} + +list.bash = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-bash", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "sh", + maintainers = { "@TravonteD" }, +} + +list.bass = { + install_info = { + url = "https://github.com/vito/tree-sitter-bass", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.beancount = { + install_info = { + url = "https://github.com/polarmutex/tree-sitter-beancount", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@polarmutex" }, +} + +list.bibtex = { + install_info = { + url = "https://github.com/latex-lsp/tree-sitter-bibtex", + files = { "src/parser.c" }, + }, + filetype = "bib", + maintainers = { "@theHamsta", "@clason" }, +} + +list.bicep = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-bicep", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.bitbake = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-bitbake", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.blueprint = { + install_info = { + url = "https://gitlab.com/gabmus/tree-sitter-blueprint.git", + files = { "src/parser.c" }, + }, + maintainers = { "@gabmus" }, + experimental = true, +} + +list.bp = { + install_info = { + url = "https://github.com/ambroisie/tree-sitter-bp", + files = { "src/parser.c" }, + }, + maintainers = { "@ambroisie" }, +} + +list.c = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-c", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.c_sharp = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-c-sharp", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "cs", + maintainers = { "@amaanq" }, +} + +list.cairo = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-cairo", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.capnp = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-capnp", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.chatito = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-chatito", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.clojure = { + install_info = { + url = "https://github.com/sogaiu/tree-sitter-clojure", + files = { "src/parser.c" }, + }, + maintainers = { "@NoahTheDuke" }, +} + +list.cmake = { + install_info = { + url = "https://github.com/uyha/tree-sitter-cmake", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@uyha" }, +} + +list.comment = { + install_info = { + url = "https://github.com/stsewd/tree-sitter-comment", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@stsewd" }, +} + +list.commonlisp = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-commonlisp", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + filetype = "lisp", + maintainers = { "@theHamsta" }, +} + +list.cooklang = { + install_info = { + url = "https://github.com/addcninblue/tree-sitter-cooklang", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@addcninblue" }, +} + +list.corn = { + install_info = { + url = "https://github.com/jakestanger/tree-sitter-corn", + files = { "src/parser.c" }, + }, + maintainers = { "@jakestanger" }, +} + +list.cpon = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-cpon", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.cpp = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-cpp", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@theHamsta" }, +} + +list.css = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-css", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@TravonteD" }, +} + +list.csv = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-csv", + files = { "src/parser.c" }, + location = "csv", + }, + maintainers = { "@amaanq" }, +} + +list.cuda = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-cuda", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@theHamsta" }, +} + +list.cue = { + install_info = { + url = "https://github.com/eonpatapon/tree-sitter-cue", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.d = { + install_info = { + url = "https://github.com/gdamore/tree-sitter-d", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.dart = { + install_info = { + url = "https://github.com/UserNobody14/tree-sitter-dart", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@akinsho" }, +} + +list.devicetree = { + install_info = { + url = "https://github.com/joelspadin/tree-sitter-devicetree", + files = { "src/parser.c" }, + }, + filetype = "dts", + maintainers = { "@jedrzejboczar" }, +} + +list.dhall = { + install_info = { + url = "https://github.com/jbellerb/tree-sitter-dhall", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.diff = { + install_info = { + url = "https://github.com/the-mikedavis/tree-sitter-diff", + files = { "src/parser.c" }, + }, + filetype = "gitdiff", + maintainers = { "@gbprod" }, +} + +list.disassembly = { + install_info = { + url = "https://github.com/ColinKennedy/tree-sitter-disassembly", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ColinKennedy" }, +} + +list.djot = { + install_info = { + url = "https://github.com/treeman/tree-sitter-djot", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@NoahTheDuke" }, +} + +list.dockerfile = { + install_info = { + url = "https://github.com/camdencheek/tree-sitter-dockerfile", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@camdencheek" }, +} + +list.dot = { + install_info = { + url = "https://github.com/rydesun/tree-sitter-dot", + files = { "src/parser.c" }, + }, + maintainers = { "@rydesun" }, +} + +list.doxygen = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-doxygen", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.dtd = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-xml", + files = { "src/parser.c", "src/scanner.c" }, + location = "dtd", + }, + maintainers = { "@ObserverOfTime" }, +} + +list.earthfile = { + install_info = { + url = "https://github.com/glehmann/tree-sitter-earthfile", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@glehmann" }, +} + +list.ebnf = { + install_info = { + url = "https://github.com/RubixDev/ebnf", + files = { "src/parser.c" }, + location = "crates/tree-sitter-ebnf", + }, + maintainers = { "@RubixDev" }, + experimental = true, +} + +list.eds = { + install_info = { + url = "https://github.com/uyha/tree-sitter-eds", + files = { "src/parser.c" }, + }, + maintainers = { "@uyha" }, +} + +list.eex = { + install_info = { + url = "https://github.com/connorlay/tree-sitter-eex", + files = { "src/parser.c" }, + }, + filetype = "eelixir", + maintainers = { "@connorlay" }, +} + +list.elixir = { + install_info = { + url = "https://github.com/elixir-lang/tree-sitter-elixir", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@connorlay" }, +} + +list.elm = { + install_info = { + url = "https://github.com/elm-tooling/tree-sitter-elm", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@zweimach" }, +} + +list.elsa = { + install_info = { + url = "https://github.com/glapa-grossklag/tree-sitter-elsa", + files = { "src/parser.c" }, + }, + maintainers = { "@glapa-grossklag", "@amaanq" }, +} + +list.elvish = { + install_info = { + url = "https://github.com/elves/tree-sitter-elvish", + files = { "src/parser.c" }, + }, + maintainers = { "@elves" }, +} + +list.embedded_template = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-embedded-template", + files = { "src/parser.c" }, + }, + filetype = "eruby", +} + +list.erlang = { + install_info = { + url = "https://github.com/WhatsApp/tree-sitter-erlang", + files = { "src/parser.c" }, + }, + maintainers = { "@filmor" }, +} + +list.facility = { + install_info = { + url = "https://github.com/FacilityApi/tree-sitter-facility", + files = { "src/parser.c" }, + }, + filetype = "fsd", + maintainers = { "@bryankenote" }, +} + +list.faust = { + install_info = { + url = "https://github.com/khiner/tree-sitter-faust", + files = { "src/parser.c" }, + }, + filetype = "dsp", + maintainers = { "@khiner" }, +} + +list.fennel = { + install_info = { + url = "https://github.com/alexmozaidze/tree-sitter-fennel", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@alexmozaidze" }, +} + +list.fidl = { + install_info = { + url = "https://github.com/google/tree-sitter-fidl", + files = { "src/parser.c" }, + }, + maintainers = { "@chaopeng" }, +} + +list.firrtl = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-firrtl", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.fish = { + install_info = { + url = "https://github.com/ram02z/tree-sitter-fish", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ram02z" }, +} + +list.foam = { + install_info = { + url = "https://github.com/FoamScience/tree-sitter-foam", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@FoamScience" }, + -- Queries might change over time on the grammar's side + -- Otherwise everything runs fine + experimental = true, +} + +list.forth = { + install_info = { + url = "https://github.com/AlexanderBrevig/tree-sitter-forth", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.fortran = { + install_info = { + url = "https://github.com/stadelmanma/tree-sitter-fortran", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.fsh = { + install_info = { + url = "https://github.com/mgramigna/tree-sitter-fsh", + files = { "src/parser.c" }, + }, + maintainers = { "@mgramigna" }, +} + +list.func = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-func", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.fusion = { + install_info = { + url = "https://gitlab.com/jirgn/tree-sitter-fusion.git", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@jirgn" }, +} + +list.gdscript = { + install_info = { + url = "https://github.com/PrestonKnopp/tree-sitter-gdscript", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@PrestonKnopp" }, + readme_name = "Godot (gdscript)", +} + +list.gdshader = { + install_info = { + url = "https://github.com/GodOfAvacyn/tree-sitter-gdshader", + files = { "src/parser.c" }, + }, + filetype = "gdshaderinc", + maintainers = { "@godofavacyn" }, +} + +list.git_rebase = { + install_info = { + url = "https://github.com/the-mikedavis/tree-sitter-git-rebase", + files = { "src/parser.c" }, + }, + filetype = "gitrebase", + maintainers = { "@gbprod" }, +} + +list.gitattributes = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-gitattributes", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.gitcommit = { + install_info = { + url = "https://github.com/gbprod/tree-sitter-gitcommit", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@gbprod" }, +} + +list.git_config = { + install_info = { + url = "https://github.com/the-mikedavis/tree-sitter-git-config", + files = { "src/parser.c" }, + }, + filetype = "gitconfig", + maintainers = { "@amaanq" }, + readme_name = "git_config", +} + +list.gitignore = { + install_info = { + url = "https://github.com/shunsambongi/tree-sitter-gitignore", + files = { "src/parser.c" }, + }, + maintainers = { "@theHamsta" }, +} + +list.gleam = { + install_info = { + url = "https://github.com/gleam-lang/tree-sitter-gleam", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.glimmer = { + install_info = { + url = "https://github.com/alexlafroscia/tree-sitter-glimmer", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "handlebars", + maintainers = { "@NullVoxPopuli" }, + readme_name = "Glimmer and Ember", +} + +list.glsl = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-glsl", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + maintainers = { "@theHamsta" }, +} + +list.gn = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-gn", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, + readme_name = "GN (Generate Ninja)", +} + +list.gnuplot = { + install_info = { + url = "https://github.com/dpezto/tree-sitter-gnuplot", + files = { "src/parser.c" }, + }, + maintainers = { "@dpezto" }, +} + +list.go = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-go", + files = { "src/parser.c" }, + }, + maintainers = { "@theHamsta", "@WinWisely268" }, +} + +list.godot_resource = { + install_info = { + url = "https://github.com/PrestonKnopp/tree-sitter-godot-resource", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "gdresource", + maintainers = { "@pierpo" }, + readme_name = "Godot Resources (gdresource)", +} + +list.gomod = { + install_info = { + url = "https://github.com/camdencheek/tree-sitter-go-mod", + files = { "src/parser.c" }, + }, + maintainers = { "@camdencheek" }, +} + +list.gosum = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-go-sum", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.gowork = { + install_info = { + url = "https://github.com/omertuc/tree-sitter-go-work", + files = { "src/parser.c" }, + }, + maintainers = { "@omertuc" }, +} + +list.gotmpl = { + install_info = { + url = "https://github.com/ngalaiko/tree-sitter-go-template", + files = { "src/parser.c" }, + }, + maintainers = { "@qvalentin" }, +} + +list.gpg = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-gpg-config", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.groovy = { + install_info = { + url = "https://github.com/murtaza64/tree-sitter-groovy", + files = { "src/parser.c" }, + }, + maintainers = { "@murtaza64" }, +} + +list.graphql = { + install_info = { + url = "https://github.com/bkegley/tree-sitter-graphql", + files = { "src/parser.c" }, + }, + maintainers = { "@bkegley" }, +} + +list.gstlaunch = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-gstlaunch", + files = { "src/parser.c" }, + }, + maintainers = { "@theHamsta" }, +} + +list.hack = { + install_info = { + url = "https://github.com/slackhq/tree-sitter-hack", + files = { "src/parser.c", "src/scanner.c" }, + }, +} + +list.hare = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-hare", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.haskell = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-haskell", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@mrcjkb" }, +} + +list.haskell_persistent = { + install_info = { + url = "https://github.com/MercuryTechnologies/tree-sitter-haskell-persistent", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "haskellpersistent", + maintainers = { "@lykahb" }, +} + +list.hcl = { + install_info = { + url = "https://github.com/MichaHoffmann/tree-sitter-hcl", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@MichaHoffmann" }, +} + +list.heex = { + install_info = { + url = "https://github.com/connorlay/tree-sitter-heex", + files = { "src/parser.c" }, + }, + maintainers = { "@connorlay" }, +} + +list.helm = { + install_info = { + url = "https://github.com/ngalaiko/tree-sitter-go-template", + location = "dialects/helm", + files = { "src/parser.c" }, + }, + maintainers = { "@qvalentin" }, +} + +list.hjson = { + install_info = { + url = "https://github.com/winston0410/tree-sitter-hjson", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + maintainers = { "@winston0410" }, +} + +list.hlsl = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-hlsl", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@theHamsta" }, +} + +list.hocon = { + install_info = { + url = "https://github.com/antosha417/tree-sitter-hocon", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + maintainers = { "@antosha417" }, +} + +list.hoon = { + install_info = { + url = "https://github.com/urbit-pilled/tree-sitter-hoon", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@urbit-pilled" }, + experimental = true, +} + +list.html = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-html", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@TravonteD" }, +} + +list.htmldjango = { + install_info = { + url = "https://github.com/interdependence/tree-sitter-htmldjango", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, + experimental = true, +} + +list.http = { + install_info = { + url = "https://github.com/rest-nvim/tree-sitter-http", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq", "@NTBBloodbath" }, +} + +list.hurl = { + install_info = { + url = "https://github.com/pfeiferj/tree-sitter-hurl", + files = { "src/parser.c" }, + }, + maintainers = { "@pfeiferj" }, +} + +list.hyprlang = { + install_info = { + url = "https://github.com/luckasRanarison/tree-sitter-hyprlang", + files = { "src/parser.c" }, + }, + maintainers = { "@luckasRanarison" }, +} + +list.idl = { + install_info = { + url = "https://github.com/cathaysia/tree-sitter-idl", + files = { "src/parser.c" }, + }, + maintainers = { "@cathaysa" }, +} + +list.ini = { + install_info = { + url = "https://github.com/justinmk/tree-sitter-ini", + files = { "src/parser.c" }, + }, + maintainers = { "@theHamsta" }, + experimental = true, +} + +list.inko = { + install_info = { + url = "https://github.com/inko-lang/tree-sitter-inko", + files = { "src/parser.c" }, + }, + maintainers = { "@yorickpeterse" }, +} + +list.ispc = { + install_info = { + url = "https://github.com/fab4100/tree-sitter-ispc", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + maintainers = { "@fab4100" }, +} + +list.janet_simple = { + install_info = { + url = "https://github.com/sogaiu/tree-sitter-janet-simple", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "janet", + maintainers = { "@sogaiu" }, +} + +list.java = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-java", + files = { "src/parser.c" }, + }, + maintainers = { "@p00f" }, +} + +list.javascript = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-javascript", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@steelsojka" }, +} + +list.jq = { + install_info = { + url = "https://github.com/flurie/tree-sitter-jq", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.jsdoc = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-jsdoc", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@steelsojka" }, +} + +list.json = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-json", + files = { "src/parser.c" }, + }, + maintainers = { "@steelsojka" }, +} + +list.json5 = { + install_info = { + url = "https://github.com/Joakker/tree-sitter-json5", + files = { "src/parser.c" }, + }, + maintainers = { "@Joakker" }, +} + +list.jsonc = { + install_info = { + url = "https://gitlab.com/WhyNotHugo/tree-sitter-jsonc.git", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + maintainers = { "@WhyNotHugo" }, + readme_name = "JSON with comments", +} + +list.jsonnet = { + install_info = { + url = "https://github.com/sourcegraph/tree-sitter-jsonnet", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@nawordar" }, +} + +list.julia = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-julia", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@theHamsta" }, +} + +list.just = { + install_info = { + url = "https://github.com/IndianBoy42/tree-sitter-just", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@Hubro" }, +} + +list.kconfig = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-kconfig", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.kdl = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-kdl", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.kotlin = { + install_info = { + url = "https://github.com/fwcd/tree-sitter-kotlin", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@SalBakraa" }, +} + +list.koto = { + install_info = { + url = "https://github.com/koto-lang/tree-sitter-koto", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@irh" }, +} + +list.kusto = { + install_info = { + url = "https://github.com/Willem-J-an/tree-sitter-kusto", + files = { "src/parser.c" }, + }, + maintainers = { "@Willem-J-an" }, +} + +list.lalrpop = { + install_info = { + url = "https://github.com/traxys/tree-sitter-lalrpop", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@traxys" }, +} + +list.latex = { + install_info = { + url = "https://github.com/latex-lsp/tree-sitter-latex", + files = { "src/parser.c", "src/scanner.c" }, + requires_generate_from_grammar = true, + }, + filetype = "tex", + maintainers = { "@theHamsta", "@clason" }, +} + +list.ledger = { + install_info = { + url = "https://github.com/cbarrete/tree-sitter-ledger", + files = { "src/parser.c" }, + }, + maintainers = { "@cbarrete" }, +} + +list.leo = { + install_info = { + url = "https://github.com/r001/tree-sitter-leo", + files = { "src/parser.c" }, + }, + maintainers = { "@r001" }, +} + +list.llvm = { + install_info = { + url = "https://github.com/benwilliamgraham/tree-sitter-llvm", + files = { "src/parser.c" }, + }, + maintainers = { "@benwilliamgraham" }, +} + +list.linkerscript = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-linkerscript", + files = { "src/parser.c" }, + }, + filetype = "ld", + maintainers = { "@amaanq" }, +} + +list.liquid = { + install_info = { + url = "https://github.com/hankthetank27/tree-sitter-liquid", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@hankthetank27" }, +} + +list.liquidsoap = { + install_info = { + url = "https://github.com/savonet/tree-sitter-liquidsoap", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@toots" }, +} + +list.lua = { + install_info = { + url = "https://github.com/MunifTanjim/tree-sitter-lua", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@muniftanjim" }, +} + +list.luadoc = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-luadoc", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.luap = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-luap", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, + readme_name = "lua patterns", +} + +list.luau = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-luau", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.hlsplaylist = { + install_info = { + url = "https://github.com/Freed-Wu/tree-sitter-hlsplaylist", + files = { "src/parser.c" }, + }, + maintainers = { "@Freed-Wu" }, +} + +list.m68k = { + install_info = { + url = "https://github.com/grahambates/tree-sitter-m68k", + files = { "src/parser.c" }, + }, + filetype = "asm68k", + maintainers = { "@grahambates" }, +} + +list.make = { + install_info = { + url = "https://github.com/alemuller/tree-sitter-make", + files = { "src/parser.c" }, + }, + maintainers = { "@lewis6991" }, +} + +list.markdown = { + install_info = { + url = "https://github.com/MDeiml/tree-sitter-markdown", + location = "tree-sitter-markdown", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@MDeiml" }, + readme_name = "markdown (basic highlighting)", + experimental = true, +} + +list.markdown_inline = { + install_info = { + url = "https://github.com/MDeiml/tree-sitter-markdown", + location = "tree-sitter-markdown-inline", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@MDeiml" }, + readme_name = "markdown_inline (needed for full highlighting)", + experimental = true, +} + +list.matlab = { + install_info = { + url = "https://github.com/acristoffers/tree-sitter-matlab", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@acristoffers" }, +} + +list.menhir = { + install_info = { + url = "https://github.com/Kerl13/tree-sitter-menhir", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@Kerl13" }, +} + +list.mermaid = { + install_info = { + url = "https://github.com/monaqa/tree-sitter-mermaid", + files = { "src/parser.c" }, + }, + experimental = true, +} + +list.meson = { + install_info = { + url = "https://github.com/Decodetalkers/tree-sitter-meson", + files = { "src/parser.c" }, + }, + maintainers = { "@Decodetalkers" }, +} + +list.mlir = { + install_info = { + url = "https://github.com/artagnon/tree-sitter-mlir", + files = { "src/parser.c" }, + requires_generate_from_grammar = true, + }, + experimental = true, + maintainers = { "@artagnon" }, +} + +list.muttrc = { + install_info = { + url = "https://github.com/neomutt/tree-sitter-muttrc", + files = { "src/parser.c" }, + }, + maintainers = { "@Freed-Wu" }, +} + +list.nasm = { + install_info = { + url = "https://github.com/naclsn/tree-sitter-nasm", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.nickel = { + install_info = { + url = "https://github.com/nickel-lang/tree-sitter-nickel", + files = { "src/parser.c", "src/scanner.c" }, + }, +} + +list.nim = { + install_info = { + url = "https://github.com/alaviss/tree-sitter-nim", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@aMOPel" }, +} + +list.nim_format_string = { + install_info = { + url = "https://github.com/aMOPel/tree-sitter-nim-format-string", + files = { "src/parser.c" }, + }, + maintainers = { "@aMOPel" }, +} + +list.ninja = { + install_info = { + url = "https://github.com/alemuller/tree-sitter-ninja", + files = { "src/parser.c" }, + }, + maintainers = { "@alemuller" }, +} + +list.nix = { + install_info = { + url = "https://github.com/cstrahan/tree-sitter-nix", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@leo60228" }, +} + +list.norg = { + install_info = { + url = "https://github.com/nvim-neorg/tree-sitter-norg", + files = { "src/parser.c", "src/scanner.cc" }, + cxx_standard = "c++14", + use_makefile = true, + }, + maintainers = { "@JoeyGrajciar", "@vhyrro" }, +} + +list.nqc = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-nqc", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.objc = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-objc", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.objdump = { + install_info = { + url = "https://github.com/ColinKennedy/tree-sitter-objdump", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ColinKennedy" }, +} + +list.ocaml = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-ocaml", + files = { "src/parser.c", "src/scanner.c" }, + location = "grammars/ocaml", + }, + maintainers = { "@undu" }, +} + +list.ocaml_interface = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-ocaml", + files = { "src/parser.c", "src/scanner.c" }, + location = "grammars/interface", + }, + filetype = "ocamlinterface", + maintainers = { "@undu" }, +} + +list.ocamllex = { + install_info = { + url = "https://github.com/atom-ocaml/tree-sitter-ocamllex", + files = { "src/parser.c", "src/scanner.c" }, + requires_generate_from_grammar = true, + }, + maintainers = { "@undu" }, +} + +list.odin = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-odin", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.org = { + install_info = { + url = "https://github.com/milisims/tree-sitter-org", + files = { "src/parser.c", "src/scanner.c" }, + }, +} + +list.pascal = { + install_info = { + url = "https://github.com/Isopod/tree-sitter-pascal.git", + files = { "src/parser.c" }, + }, + maintainers = { "@Isopod" }, +} + +list.passwd = { + install_info = { + url = "https://github.com/ath3/tree-sitter-passwd", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.pem = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-pem", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.perl = { + install_info = { + url = "https://github.com/tree-sitter-perl/tree-sitter-perl", + files = { "src/parser.c", "src/scanner.c" }, + branch = "release", + }, + maintainers = { "@RabbiVeesh", "@LeoNerd" }, +} + +list.php = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-php", + location = "php", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@tk-shirasaka" }, +} + +list.php_only = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-php", + location = "php_only", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@tk-shirasaka" }, +} + +-- Parsers for injections +list.phpdoc = { + install_info = { + url = "https://github.com/claytonrcarter/tree-sitter-phpdoc", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@mikehaertl" }, + experimental = true, +} + +list.pioasm = { + install_info = { + url = "https://github.com/leo60228/tree-sitter-pioasm", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@leo60228" }, +} + +list.po = { + install_info = { + url = "https://github.com/erasin/tree-sitter-po", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.pod = { + install_info = { + url = "https://github.com/tree-sitter-perl/tree-sitter-pod", + files = { "src/parser.c", "src/scanner.c" }, + branch = "release", + }, + maintainers = { "@RabbiVeesh", "@LeoNerd" }, +} + +list.poe_filter = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-poe-filter", + files = { "src/parser.c" }, + }, + filetype = "poefilter", + maintainers = { "@ObserverOfTime" }, + readme_name = "Path of Exile item filter", + experimental = true, +} + +list.pony = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-pony", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq", "@mfelsche" }, +} + +list.printf = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-printf", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.prisma = { + install_info = { + url = "https://github.com/victorhqc/tree-sitter-prisma", + files = { "src/parser.c" }, + }, + maintainers = { "@elianiva" }, +} + +list.promql = { + install_info = { + url = "https://github.com/MichaHoffmann/tree-sitter-promql", + files = { "src/parser.c" }, + experimental = true, + }, + maintainers = { "@MichaHoffmann" }, +} + +list.properties = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-properties", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "jproperties", + maintainers = { "@ObserverOfTime" }, +} + +list.proto = { + install_info = { + url = "https://github.com/treywood/tree-sitter-proto", + files = { "src/parser.c" }, + }, + maintainers = { "@treywood" }, +} + +list.prql = { + install_info = { + url = "https://github.com/PRQL/tree-sitter-prql", + files = { "src/parser.c" }, + }, + maintainers = { "@matthias-Q" }, +} + +list.psv = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-csv", + files = { "src/parser.c" }, + location = "psv", + }, + maintainers = { "@amaanq" }, +} + +list.pug = { + install_info = { + url = "https://github.com/zealot128/tree-sitter-pug", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@zealot128" }, + experimental = true, +} + +list.puppet = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-puppet", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.purescript = { + install_info = { + url = "https://github.com/postsolar/tree-sitter-purescript", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@postsolar" }, +} + +list.pymanifest = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-pymanifest", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, + readme_name = "PyPA manifest", +} + +list.python = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-python", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@stsewd", "@theHamsta" }, +} + +list.ql = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-ql", + files = { "src/parser.c" }, + }, + maintainers = { "@pwntester" }, +} + +list.qmldir = { + install_info = { + url = "https://github.com/Decodetalkers/tree-sitter-qmldir", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.qmljs = { + install_info = { + url = "https://github.com/yuja/tree-sitter-qmljs", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "qml", + maintainers = { "@Decodetalkers" }, +} + +list.query = { + install_info = { + url = "https://github.com/nvim-treesitter/tree-sitter-query", + files = { "src/parser.c" }, + }, + maintainers = { "@steelsojka" }, + readme_name = "Tree-Sitter query language", +} + +list.r = { + install_info = { + url = "https://github.com/r-lib/tree-sitter-r", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@echasnovski" }, +} + +list.racket = { + install_info = { + url = "https://github.com/6cdh/tree-sitter-racket", + files = { "src/parser.c", "src/scanner.c" }, + }, +} + +list.rasi = { + install_info = { + url = "https://github.com/Fymyte/tree-sitter-rasi", + files = { "src/parser.c" }, + }, + maintainers = { "@Fymyte" }, +} + +list.rbs = { + install_info = { + url = "https://github.com/joker1007/tree-sitter-rbs", + files = { "src/parser.c" }, + }, + maintainers = { "@joker1007" }, +} + +list.re2c = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-re2c", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.readline = { + install_info = { + url = "https://github.com/ribru17/tree-sitter-readline", + files = { "src/parser.c" }, + }, + maintainers = { "@ribru17" }, +} + +list.regex = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-regex", + files = { "src/parser.c" }, + }, + maintainers = { "@theHamsta" }, +} + +list.rego = { + install_info = { + url = "https://github.com/FallenAngel97/tree-sitter-rego", + files = { "src/parser.c" }, + }, + maintainers = { "@FallenAngel97" }, +} + +list.requirements = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-requirements", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, + readme_name = "pip requirements", +} + +list.rnoweb = { + install_info = { + url = "https://github.com/bamonroe/tree-sitter-rnoweb", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@bamonroe" }, +} + +list.robot = { + install_info = { + url = "https://github.com/Hubro/tree-sitter-robot", + files = { "src/parser.c" }, + }, + maintainers = { "@Hubro" }, +} + +list.roc = { + install_info = { + url = "https://github.com/nat-418/tree-sitter-roc", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@nat-418" }, +} + +list.ron = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-ron", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.rst = { + install_info = { + url = "https://github.com/stsewd/tree-sitter-rst", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@stsewd" }, +} + +list.ruby = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-ruby", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@TravonteD" }, +} + +list.rust = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-rust", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.scala = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-scala", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@stevanmilic" }, +} + +list.scfg = { + install_info = { + url = "https://git.sr.ht/~rockorager/tree-sitter-scfg", + files = { "src/parser.c" }, + requires_generate_from_grammar = true, + }, + maintainers = { "@WhyNotHugo" }, +} + +list.scheme = { + install_info = { + url = "https://github.com/6cdh/tree-sitter-scheme", + files = { "src/parser.c" }, + }, +} + +list.scss = { + install_info = { + url = "https://github.com/serenadeai/tree-sitter-scss", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@elianiva" }, +} + +list.slang = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-slang", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + filetype = "shaderslang", + maintainers = { "@theHamsta" }, + experimental = true, +} + +list.slint = { + install_info = { + url = "https://github.com/slint-ui/tree-sitter-slint", + files = { "src/parser.c" }, + }, + maintainers = { "@hunger" }, +} + +list.smali = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-smali", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.snakemake = { + install_info = { + url = "https://github.com/osthomas/tree-sitter-snakemake", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainer = { "@osthomas" }, + experimental = true, +} + +list.smithy = { + install_info = { + url = "https://github.com/indoorvivants/tree-sitter-smithy", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq", "@keynmol" }, +} + +list.solidity = { + install_info = { + url = "https://github.com/JoranHonig/tree-sitter-solidity", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.soql = { + install_info = { + url = "https://github.com/aheber/tree-sitter-sfapex", + files = { "src/parser.c" }, + location = "soql", + }, + maintainers = { "@aheber" }, +} + +list.sosl = { + install_info = { + url = "https://github.com/aheber/tree-sitter-sfapex", + files = { "src/parser.c" }, + location = "sosl", + }, + maintainers = { "@aheber" }, +} + +list.sourcepawn = { + install_info = { + url = "https://github.com/nilshelmig/tree-sitter-sourcepawn", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@Sarrus1" }, + tier = 3, +} + +list.sparql = { + install_info = { + url = "https://github.com/BonaBeavis/tree-sitter-sparql", + files = { "src/parser.c" }, + }, + maintainers = { "@BonaBeavis" }, +} + +list.sql = { + install_info = { + url = "https://github.com/derekstride/tree-sitter-sql", + files = { "src/parser.c", "src/scanner.c" }, + branch = "gh-pages", + }, + maintainers = { "@derekstride" }, +} + +list.squirrel = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-squirrel", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.ssh_config = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-ssh-config", + files = { "src/parser.c" }, + }, + filetype = "sshconfig", + maintainers = { "@ObserverOfTime" }, +} + +list.starlark = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-starlark", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "bzl", + maintainers = { "@amaanq" }, +} + +list.strace = { + install_info = { + url = "https://github.com/sigmaSd/tree-sitter-strace", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.styled = { + install_info = { + url = "https://github.com/mskelton/tree-sitter-styled", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@mskelton" }, +} + +list.supercollider = { + install_info = { + url = "https://github.com/madskjeldgaard/tree-sitter-supercollider", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@madskjeldgaard" }, +} + +list.surface = { + install_info = { + url = "https://github.com/connorlay/tree-sitter-surface", + files = { "src/parser.c" }, + }, + filetype = "sface", + maintainers = { "@connorlay" }, +} + +list.svelte = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-svelte", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.swift = { + install_info = { + url = "https://github.com/alex-pinkus/tree-sitter-swift", + files = { "src/parser.c", "src/scanner.c" }, + requires_generate_from_grammar = true, + }, + maintainers = { "@alex-pinkus" }, +} + +list.sxhkdrc = { + install_info = { + url = "https://github.com/RaafatTurki/tree-sitter-sxhkdrc", + files = { "src/parser.c" }, + }, + maintainers = { "@RaafatTurki" }, +} + +list.systemtap = { + install_info = { + url = "https://github.com/ok-ryoko/tree-sitter-systemtap", + files = { "src/parser.c" }, + }, + maintainers = { "@ok-ryoko" }, +} + +list.t32 = { + install_info = { + url = "https://gitlab.com/xasc/tree-sitter-t32.git", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "trace32", + maintainers = { "@xasc" }, +} + +list.tablegen = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-tablegen", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.tact = { + install_info = { + url = "https://github.com/tact-lang/tree-sitter-tact", + files = { "src/parser.c" }, + }, + maintainers = { "@novusnota" }, +} + +list.teal = { + install_info = { + url = "https://github.com/euclidianAce/tree-sitter-teal", + files = { "src/parser.c", "src/scanner.c" }, + requires_generate_from_grammar = true, + }, + maintainers = { "@euclidianAce" }, +} + +list.tcl = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-tcl", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@lewis6991" }, +} + +list.terraform = { + install_info = { + url = "https://github.com/MichaHoffmann/tree-sitter-hcl", + files = { "src/parser.c", "src/scanner.c" }, + location = "dialects/terraform", + }, + maintainers = { "@MichaHoffmann" }, +} + +list.textproto = { + install_info = { + url = "https://github.com/PorterAtGoogle/tree-sitter-textproto", + files = { "src/parser.c" }, + }, + filetype = "pbtxt", + maintainers = { "@Porter" }, +} + +list.thrift = { + install_info = { + url = "https://github.com/duskmoon314/tree-sitter-thrift", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq", "@duskmoon314" }, +} + +list.tiger = { + install_info = { + url = "https://github.com/ambroisie/tree-sitter-tiger", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ambroisie" }, +} + +list.tlaplus = { + install_info = { + url = "https://github.com/tlaplus-community/tree-sitter-tlaplus", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "tla", + maintainers = { "@ahelwer", "@susliko" }, +} + +list.tmux = { + install_info = { + url = "https://github.com/Freed-Wu/tree-sitter-tmux", + files = { "src/parser.c" }, + }, + maintainers = { "@Freed-Wu" }, +} + +list.todotxt = { + install_info = { + url = "https://github.com/arnarg/tree-sitter-todotxt.git", + files = { "src/parser.c" }, + }, + maintainers = { "@arnarg" }, + experimental = true, +} + +list.toml = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-toml", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@tk-shirasaka" }, +} + +list.tsv = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-csv", + files = { "src/parser.c" }, + location = "tsv", + }, + maintainers = { "@amaanq" }, +} + +list.tsx = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-typescript", + files = { "src/parser.c", "src/scanner.c" }, + location = "tsx", + generate_requires_npm = true, + }, + filetype = "typescriptreact", + maintainers = { "@steelsojka" }, +} + +list.turtle = { + install_info = { + url = "https://github.com/BonaBeavis/tree-sitter-turtle", + files = { "src/parser.c" }, + }, + maintainers = { "@BonaBeavis" }, +} + +list.twig = { + install_info = { + url = "https://github.com/gbprod/tree-sitter-twig", + files = { "src/parser.c" }, + }, + maintainers = { "@gbprod" }, +} + +list.typescript = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-typescript", + files = { "src/parser.c", "src/scanner.c" }, + location = "typescript", + generate_requires_npm = true, + }, + maintainers = { "@steelsojka" }, +} + +list.typespec = { + install_info = { + url = "https://github.com/happenslol/tree-sitter-typespec", + files = { "src/parser.c" }, + }, + maintainers = { "@happenslol" }, +} + +list.typoscript = { + install_info = { + url = "https://github.com/Teddytrombone/tree-sitter-typoscript", + files = { "src/parser.c" }, + }, + maintainers = { "@Teddytrombone" }, +} + +list.typst = { + install_info = { + url = "https://github.com/uben0/tree-sitter-typst", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@uben0", "@RaafatTurki" }, +} + +list.udev = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-udev", + files = { "src/parser.c" }, + }, + filetype = "udevrules", + maintainers = { "@ObserverOfTime" }, +} + +list.ungrammar = { + install_info = { + url = "https://github.com/Philipp-M/tree-sitter-ungrammar", + files = { "src/parser.c" }, + }, + maintainers = { "@Philipp-M", "@amaanq" }, +} + +list.unison = { + install_info = { + url = "https://github.com/kylegoetz/tree-sitter-unison", + files = { "src/parser.c", "src/scanner.c" }, + requires_generate_from_grammar = true, + }, + maintainers = { "@tapegram" }, +} + +list.usd = { + install_info = { + url = "https://github.com/ColinKennedy/tree-sitter-usd", + files = { "src/parser.c" }, + }, + maintainers = { "@ColinKennedy" }, +} + +list.uxntal = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-uxntal", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "tal", + maintainers = { "@amaanq" }, + readme_name = "uxn tal", +} + +list.v = { + install_info = { + url = "https://github.com/vlang/v-analyzer", + files = { "src/parser.c" }, + location = "tree_sitter_v", + }, + filetype = "vlang", + maintainers = { "@kkharji", "@amaanq" }, +} + +list.vala = { + install_info = { + url = "https://github.com/vala-lang/tree-sitter-vala", + files = { "src/parser.c" }, + }, + maintainers = { "@Prince781" }, +} + +list.vento = { + install_info = { + url = "https://github.com/ventojs/tree-sitter-vento", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "vto", + maintainers = { "@wrapperup", "@oscarotero" }, +} + +list.verilog = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-verilog", + files = { "src/parser.c" }, + }, + maintainers = { "@zegervdv" }, +} + +list.vhs = { + install_info = { + url = "https://github.com/charmbracelet/tree-sitter-vhs", + files = { "src/parser.c" }, + }, + filetype = "tape", + maintainers = { "@caarlos0" }, +} + +list.vim = { + install_info = { + url = "https://github.com/neovim/tree-sitter-vim", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@clason" }, +} + +list.vimdoc = { + install_info = { + url = "https://github.com/neovim/tree-sitter-vimdoc", + files = { "src/parser.c" }, + }, + filetype = "help", + maintainers = { "@clason" }, +} + +list.vue = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-vue", + files = { "src/parser.c", "src/scanner.c" }, + branch = "main", + }, + maintainers = { "@WhyNotHugo", "@lucario387" }, +} + +list.wgsl = { + install_info = { + url = "https://github.com/szebniok/tree-sitter-wgsl", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@szebniok" }, +} + +list.wgsl_bevy = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-wgsl-bevy", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@theHamsta" }, +} + +list.wing = { + install_info = { + url = "https://github.com/winglang/tree-sitter-wing", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@gshpychka", "@MarkMcCulloh" }, +} + +list.wit = { + install_info = { + url = "https://github.com/liamwh/tree-sitter-wit", + files = { "src/parser.c" }, + }, + maintainers = { "@liamwh" }, +} + +list.xcompose = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-xcompose", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.xml = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-xml", + files = { "src/parser.c", "src/scanner.c" }, + location = "xml", + }, + maintainers = { "@ObserverOfTime" }, +} + +list.yaml = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-yaml", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.yang = { + install_info = { + url = "https://github.com/Hubro/tree-sitter-yang", + files = { "src/parser.c" }, + }, + maintainers = { "@Hubro" }, +} + +list.yuck = { + install_info = { + url = "https://github.com/Philipp-M/tree-sitter-yuck", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@Philipp-M", "@amaanq" }, +} + +list.zathurarc = { + install_info = { + url = "https://github.com/Freed-Wu/tree-sitter-zathurarc", + files = { "src/parser.c" }, + }, + maintainers = { "@Freed-Wu" }, +} + +list.zig = { + install_info = { + url = "https://github.com/maxxnino/tree-sitter-zig", + files = { "src/parser.c" }, + }, + maintainers = { "@maxxnino" }, +} + +list.templ = { + install_info = { + url = "https://github.com/vrischmann/tree-sitter-templ", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@vrischmann" }, +} + +local M = { + list = list, + filetype_to_parsername = filetype_to_parsername, +} + +local function get_lang(ft) + if new_lang_api then + return ts.language.get_lang(ft) + end + return filetype_to_parsername[ft] +end + +function M.ft_to_lang(ft) + local result = get_lang(ft) + if result then + return result + else + ft = vim.split(ft, ".", { plain = true })[1] + return get_lang(ft) or ft + end +end + +-- Get a list of all available parsers +---@return string[] +function M.available_parsers() + local parsers = vim.tbl_keys(M.list) + table.sort(parsers) + if vim.fn.executable "tree-sitter" == 1 and vim.fn.executable "node" == 1 then + return parsers + else + return vim.tbl_filter(function(p) ---@param p string + return not M.list[p].install_info.requires_generate_from_grammar + end, parsers) + end +end + +function M.get_parser_configs() + return M.list +end + +local parser_files + +function M.reset_cache() + parser_files = setmetatable({}, { + __index = function(tbl, key) + rawset(tbl, key, api.nvim_get_runtime_file("parser/" .. key .. ".*", false)) + return rawget(tbl, key) + end, + }) +end + +M.reset_cache() + +function M.has_parser(lang) + lang = lang or M.get_buf_lang(api.nvim_get_current_buf()) + + if not lang or #lang == 0 then + return false + end + -- HACK: nvim internal API + if vim._ts_has_language(lang) then + return true + end + return #parser_files[lang] > 0 +end + +function M.get_parser(bufnr, lang) + bufnr = bufnr or api.nvim_get_current_buf() + lang = lang or M.get_buf_lang(bufnr) + + if M.has_parser(lang) then + return ts.get_parser(bufnr, lang) + end +end + +-- @deprecated This is only kept for legacy purposes. +-- All root nodes should be accounted for. +function M.get_tree_root(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + return M.get_parser(bufnr):parse()[1]:root() +end + +-- Gets the language of a given buffer +---@param bufnr number? or current buffer +---@return string +function M.get_buf_lang(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + return M.ft_to_lang(api.nvim_buf_get_option(bufnr, "ft")) +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/query.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/query.lua new file mode 100644 index 00000000..51310545 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/query.lua @@ -0,0 +1,453 @@ +local api = vim.api +local ts = require "nvim-treesitter.compat" +local tsrange = require "nvim-treesitter.tsrange" +local utils = require "nvim-treesitter.utils" +local parsers = require "nvim-treesitter.parsers" +local caching = require "nvim-treesitter.caching" + +local M = {} + +local EMPTY_ITER = function() end + +M.built_in_query_groups = { "highlights", "locals", "folds", "indents", "injections" } + +-- Creates a function that checks whether a given query exists +-- for a specific language. +---@param query string +---@return fun(string): boolean +local function get_query_guard(query) + return function(lang) + return M.has_query_files(lang, query) + end +end + +for _, query in ipairs(M.built_in_query_groups) do + M["has_" .. query] = get_query_guard(query) +end + +---@return string[] +function M.available_query_groups() + local query_files = api.nvim_get_runtime_file("queries/*/*.scm", true) + local groups = {} + for _, f in ipairs(query_files) do + groups[vim.fn.fnamemodify(f, ":t:r")] = true + end + local list = {} + for k, _ in pairs(groups) do + table.insert(list, k) + end + return list +end + +do + local query_cache = caching.create_buffer_cache() + + local function update_cached_matches(bufnr, changed_tick, query_group) + query_cache.set(query_group, bufnr, { + tick = changed_tick, + cache = M.collect_group_results(bufnr, query_group) or {}, + }) + end + + ---@param bufnr integer + ---@param query_group string + ---@return any + function M.get_matches(bufnr, query_group) + bufnr = bufnr or api.nvim_get_current_buf() + local cached_local = query_cache.get(query_group, bufnr) + if not cached_local or api.nvim_buf_get_changedtick(bufnr) > cached_local.tick then + update_cached_matches(bufnr, api.nvim_buf_get_changedtick(bufnr), query_group) + end + + return query_cache.get(query_group, bufnr).cache + end +end + +---@param lang string +---@param query_name string +---@return string[] +local function runtime_queries(lang, query_name) + return api.nvim_get_runtime_file(string.format("queries/%s/%s.scm", lang, query_name), true) or {} +end + +---@type table> +local query_files_cache = {} + +---@param lang string +---@param query_name string +---@return boolean +function M.has_query_files(lang, query_name) + if not query_files_cache[lang] then + query_files_cache[lang] = {} + end + if query_files_cache[lang][query_name] == nil then + local files = runtime_queries(lang, query_name) + query_files_cache[lang][query_name] = files and #files > 0 + end + return query_files_cache[lang][query_name] +end + +do + local mt = {} + mt.__index = function(tbl, key) + if rawget(tbl, key) == nil then + rawset(tbl, key, {}) + end + return rawget(tbl, key) + end + + -- cache will auto set the table for each lang if it is nil + ---@type table> + local cache = setmetatable({}, mt) + + -- Same as `vim.treesitter.query` except will return cached values + ---@param lang string + ---@param query_name string + function M.get_query(lang, query_name) + if cache[lang][query_name] == nil then + cache[lang][query_name] = ts.get_query(lang, query_name) + end + + return cache[lang][query_name] + end + + -- Invalidates the query file cache. + -- + -- If lang and query_name is both present, will reload for only the lang and query_name. + -- If only lang is present, will reload all query_names for that lang + -- If none are present, will reload everything + ---@param lang? string + ---@param query_name? string + function M.invalidate_query_cache(lang, query_name) + if lang and query_name then + cache[lang][query_name] = nil + if query_files_cache[lang] then + query_files_cache[lang][query_name] = nil + end + elseif lang and not query_name then + query_files_cache[lang] = nil + for query_name0, _ in pairs(cache[lang]) do + M.invalidate_query_cache(lang, query_name0) + end + elseif not lang and not query_name then + query_files_cache = {} + for lang0, _ in pairs(cache) do + for query_name0, _ in pairs(cache[lang0]) do + M.invalidate_query_cache(lang0, query_name0) + end + end + else + error "Cannot have query_name by itself!" + end + end +end + +-- This function is meant for an autocommand and not to be used. Only use if file is a query file. +---@param fname string +function M.invalidate_query_file(fname) + local fnamemodify = vim.fn.fnamemodify + M.invalidate_query_cache(fnamemodify(fname, ":p:h:t"), fnamemodify(fname, ":t:r")) +end + +---@class QueryInfo +---@field root TSNode +---@field source integer +---@field start integer +---@field stop integer + +---@param bufnr integer +---@param query_name string +---@param root TSNode +---@param root_lang string|nil +---@return Query|nil, QueryInfo|nil +local function prepare_query(bufnr, query_name, root, root_lang) + local buf_lang = parsers.get_buf_lang(bufnr) + + if not buf_lang then + return + end + + local parser = parsers.get_parser(bufnr, buf_lang) + if not parser then + return + end + + if not root then + local first_tree = parser:trees()[1] + + if first_tree then + root = first_tree:root() + end + end + + if not root then + return + end + + local range = { root:range() } + + if not root_lang then + local lang_tree = parser:language_for_range(range) + + if lang_tree then + root_lang = lang_tree:lang() + end + end + + if not root_lang then + return + end + + local query = M.get_query(root_lang, query_name) + if not query then + return + end + + return query, + { + root = root, + source = bufnr, + start = range[1], + -- The end row is exclusive so we need to add 1 to it. + stop = range[3] + 1, + } +end + +-- Given a path (i.e. a List(String)) this functions inserts value at path +---@param object any +---@param path string[] +---@param value any +function M.insert_to_path(object, path, value) + local curr_obj = object + + for index = 1, (#path - 1) do + if curr_obj[path[index]] == nil then + curr_obj[path[index]] = {} + end + + curr_obj = curr_obj[path[index]] + end + + curr_obj[path[#path]] = value +end + +---@param query Query +---@param bufnr integer +---@param start_row integer +---@param end_row integer +function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row) + -- A function that splits a string on '.' + ---@param to_split string + ---@return string[] + local function split(to_split) + local t = {} + for str in string.gmatch(to_split, "([^.]+)") do + table.insert(t, str) + end + + return t + end + + local matches = query:iter_matches(qnode, bufnr, start_row, end_row) + + local function iterator() + local pattern, match, metadata = matches() + if pattern ~= nil then + local prepared_match = {} + + -- Extract capture names from each match + for id, node in pairs(match) do + local name = query.captures[id] -- name of the capture in the query + if name ~= nil then + local path = split(name .. ".node") + M.insert_to_path(prepared_match, path, node) + local metadata_path = split(name .. ".metadata") + M.insert_to_path(prepared_match, metadata_path, metadata[id]) + end + end + + -- Add some predicates for testing + ---@type string[][] ( TODO: make pred type so this can be pred[]) + local preds = query.info.patterns[pattern] + if preds then + for _, pred in pairs(preds) do + -- functions + if pred[1] == "set!" and type(pred[2]) == "string" then + M.insert_to_path(prepared_match, split(pred[2]), pred[3]) + end + if pred[1] == "make-range!" and type(pred[2]) == "string" and #pred == 4 then + M.insert_to_path( + prepared_match, + split(pred[2] .. ".node"), + tsrange.TSRange.from_nodes(bufnr, match[pred[3]], match[pred[4]]) + ) + end + end + end + + return prepared_match + end + end + return iterator +end + +-- Return all nodes corresponding to a specific capture path (like @definition.var, @reference.type) +-- Works like M.get_references or M.get_scopes except you can choose the capture +-- Can also be a nested capture like @definition.function to get all nodes defining a function. +-- +---@param bufnr integer the buffer +---@param captures string|string[] +---@param query_group string the name of query group (highlights or injections for example) +---@param root TSNode|nil node from where to start the search +---@param lang string|nil the language from where to get the captures. +--- Root nodes can have several languages. +---@return table|nil +function M.get_capture_matches(bufnr, captures, query_group, root, lang) + if type(captures) == "string" then + captures = { captures } + end + local strip_captures = {} ---@type string[] + for i, capture in ipairs(captures) do + if capture:sub(1, 1) ~= "@" then + error 'Captures must start with "@"' + return + end + -- Remove leading "@". + strip_captures[i] = capture:sub(2) + end + + local matches = {} + for match in M.iter_group_results(bufnr, query_group, root, lang) do + for _, capture in ipairs(strip_captures) do + local insert = utils.get_at_path(match, capture) + if insert then + table.insert(matches, insert) + end + end + end + return matches +end + +function M.iter_captures(bufnr, query_name, root, lang) + local query, params = prepare_query(bufnr, query_name, root, lang) + if not query then + return EMPTY_ITER + end + assert(params) + + local iter = query:iter_captures(params.root, params.source, params.start, params.stop) + + local function wrapped_iter() + local id, node, metadata = iter() + if not id then + return + end + + local name = query.captures[id] + if string.sub(name, 1, 1) == "_" then + return wrapped_iter() + end + + return name, node, metadata + end + + return wrapped_iter +end + +---@param bufnr integer +---@param capture_string string +---@param query_group string +---@param filter_predicate fun(match: table): boolean +---@param scoring_function fun(match: table): number +---@param root TSNode +---@return table|unknown +function M.find_best_match(bufnr, capture_string, query_group, filter_predicate, scoring_function, root) + if string.sub(capture_string, 1, 1) == "@" then + --remove leading "@" + capture_string = string.sub(capture_string, 2) + end + + local best ---@type table|nil + local best_score ---@type number + + for maybe_match in M.iter_group_results(bufnr, query_group, root) do + local match = utils.get_at_path(maybe_match, capture_string) + + if match and filter_predicate(match) then + local current_score = scoring_function(match) + if not best then + best = match + best_score = current_score + end + if current_score > best_score then + best = match + best_score = current_score + end + end + end + return best +end + +---Iterates matches from a query file. +---@param bufnr integer the buffer +---@param query_group string the query file to use +---@param root TSNode the root node +---@param root_lang? string the root node lang, if known +function M.iter_group_results(bufnr, query_group, root, root_lang) + local query, params = prepare_query(bufnr, query_group, root, root_lang) + if not query then + return EMPTY_ITER + end + assert(params) + + return M.iter_prepared_matches(query, params.root, params.source, params.start, params.stop) +end + +function M.collect_group_results(bufnr, query_group, root, lang) + local matches = {} + + for prepared_match in M.iter_group_results(bufnr, query_group, root, lang) do + table.insert(matches, prepared_match) + end + + return matches +end + +---@alias CaptureResFn function(string, LanguageTree, LanguageTree): string, string + +-- Same as get_capture_matches except this will recursively get matches for every language in the tree. +---@param bufnr integer The buffer +---@param capture_or_fn string|CaptureResFn The capture to get. If a function is provided then that +--- function will be used to resolve both the capture and query argument. +--- The function can return `nil` to ignore that tree. +---@param query_type string? The query to get the capture from. This is ignored if a function is provided +--- for the capture argument. +---@return table[] +function M.get_capture_matches_recursively(bufnr, capture_or_fn, query_type) + ---@type CaptureResFn + local type_fn + if type(capture_or_fn) == "function" then + type_fn = capture_or_fn + else + type_fn = function(_, _, _) + return capture_or_fn, query_type + end + end + local parser = parsers.get_parser(bufnr) + local matches = {} + + if parser then + parser:for_each_tree(function(tree, lang_tree) + local lang = lang_tree:lang() + local capture, type_ = type_fn(lang, tree, lang_tree) + + if capture then + vim.list_extend(matches, M.get_capture_matches(bufnr, capture, type_, tree:root(), lang) or {}) + end + end) + end + + return matches +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/query_predicates.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/query_predicates.lua new file mode 100644 index 00000000..61400e47 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/query_predicates.lua @@ -0,0 +1,238 @@ +local query = require "vim.treesitter.query" + +local html_script_type_languages = { + ["importmap"] = "json", + ["module"] = "javascript", + ["application/ecmascript"] = "javascript", + ["text/ecmascript"] = "javascript", +} + +local non_filetype_match_injection_language_aliases = { + ex = "elixir", + pl = "perl", + sh = "bash", + uxn = "uxntal", + ts = "typescript", +} + +local function get_parser_from_markdown_info_string(injection_alias) + local match = vim.filetype.match { filename = "a." .. injection_alias } + return match or non_filetype_match_injection_language_aliases[injection_alias] or injection_alias +end + +local function error(str) + vim.api.nvim_err_writeln(str) +end + +local function valid_args(name, pred, count, strict_count) + local arg_count = #pred - 1 + + if strict_count then + if arg_count ~= count then + error(string.format("%s must have exactly %d arguments", name, count)) + return false + end + elseif arg_count < count then + error(string.format("%s must have at least %d arguments", name, count)) + return false + end + + return true +end + +---@param match (TSNode|nil)[] +---@param _pattern string +---@param _bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_predicate("nth?", function(match, _pattern, _bufnr, pred) + if not valid_args("nth?", pred, 2, true) then + return + end + + local node = match[pred[2]] ---@type TSNode + local n = tonumber(pred[3]) + if node and node:parent() and node:parent():named_child_count() > n then + return node:parent():named_child(n) == node + end + + return false +end, true) + +---@param match (TSNode|nil)[] +---@param _pattern string +---@param _bufnr integer +---@param pred string[] +---@return boolean|nil +local function has_ancestor(match, _pattern, _bufnr, pred) + if not valid_args(pred[1], pred, 2) then + return + end + + local node = match[pred[2]] + local ancestor_types = { unpack(pred, 3) } + if not node then + return true + end + + local just_direct_parent = pred[1]:find("has-parent", 1, true) + + node = node:parent() + while node do + if vim.tbl_contains(ancestor_types, node:type()) then + return true + end + if just_direct_parent then + node = nil + else + node = node:parent() + end + end + return false +end + +query.add_predicate("has-ancestor?", has_ancestor, true) + +query.add_predicate("has-parent?", has_ancestor, true) + +---@param match (TSNode|nil)[] +---@param _pattern string +---@param bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_predicate("is?", function(match, _pattern, bufnr, pred) + if not valid_args("is?", pred, 2) then + return + end + + -- Avoid circular dependencies + local locals = require "nvim-treesitter.locals" + local node = match[pred[2]] + local types = { unpack(pred, 3) } + + if not node then + return true + end + + local _, _, kind = locals.find_definition(node, bufnr) + + return vim.tbl_contains(types, kind) +end, true) + +---@param match (TSNode|nil)[] +---@param _pattern string +---@param _bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_predicate("has-type?", function(match, _pattern, _bufnr, pred) + if not valid_args(pred[1], pred, 2) then + return + end + + local node = match[pred[2]] + local types = { unpack(pred, 3) } + + if not node then + return true + end + + return vim.tbl_contains(types, node:type()) +end, true) + +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_directive("set-lang-from-mimetype!", function(match, _, bufnr, pred, metadata) + local capture_id = pred[2] + local node = match[capture_id] + if not node then + return + end + local type_attr_value = vim.treesitter.get_node_text(node, bufnr) + local configured = html_script_type_languages[type_attr_value] + if configured then + metadata["injection.language"] = configured + else + local parts = vim.split(type_attr_value, "/", {}) + metadata["injection.language"] = parts[#parts] + end +end, true) + +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_directive("set-lang-from-info-string!", function(match, _, bufnr, pred, metadata) + local capture_id = pred[2] + local node = match[capture_id] + if not node then + return + end + local injection_alias = vim.treesitter.get_node_text(node, bufnr):lower() + metadata["injection.language"] = get_parser_from_markdown_info_string(injection_alias) +end, true) + +-- Just avoid some annoying warnings for this directive +query.add_directive("make-range!", function() end, true) + +--- transform node text to lower case (e.g., to make @injection.language case insensitive) +--- +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_directive("downcase!", function(match, _, bufnr, pred, metadata) + local id = pred[2] + local node = match[id] + if not node then + return + end + + local text = vim.treesitter.get_node_text(node, bufnr, { metadata = metadata[id] }) or "" + if not metadata[id] then + metadata[id] = {} + end + metadata[id].text = string.lower(text) +end, true) + +-- Trim blank lines from end of the region +-- Arguments are the captures to trim. +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@param metadata table +query.add_directive("trim!", function(match, _, bufnr, pred, metadata) + for _, id in ipairs { select(2, unpack(pred)) } do + local node = match[id] + local start_row, start_col, end_row, end_col = node:range() + + -- Don't trim if region ends in middle of a line + if end_col ~= 0 then + return + end + + while true do + -- As we only care when end_col == 0, always inspect one line above end_row. + local end_line = vim.api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1] + + if end_line ~= "" then + break + end + + end_row = end_row - 1 + end + + -- If this produces an invalid range, we just skip it. + if start_row < end_row or (start_row == end_row and start_col <= end_col) then + if not metadata[id] then + metadata[id] = {} + end + metadata[id].range = { start_row, start_col, end_row, end_col } + end + end +end, true) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/shell_command_selectors.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/shell_command_selectors.lua new file mode 100644 index 00000000..f1d557a7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/shell_command_selectors.lua @@ -0,0 +1,337 @@ +local fn = vim.fn +local utils = require "nvim-treesitter.utils" + +-- Convert path for cmd.exe on Windows. +-- This is needed when vim.opt.shellslash is in use. +---@param p string +---@return string +local function cmdpath(p) + if vim.opt.shellslash:get() then + local r = p:gsub("/", "\\") + return r + else + return p + end +end + +local M = {} + +-- Returns the mkdir command based on the OS +---@param directory string +---@param cwd string +---@param info_msg string +---@return table +function M.select_mkdir_cmd(directory, cwd, info_msg) + if fn.has "win32" == 1 then + return { + cmd = "cmd", + opts = { + args = { "/C", "mkdir", cmdpath(directory) }, + cwd = cwd, + }, + info = info_msg, + err = "Could not create " .. directory, + } + else + return { + cmd = "mkdir", + opts = { + args = { directory }, + cwd = cwd, + }, + info = info_msg, + err = "Could not create " .. directory, + } + end +end + +-- Returns the remove command based on the OS +---@param file string +---@param info_msg string +---@return table +function M.select_rm_file_cmd(file, info_msg) + if fn.has "win32" == 1 then + return { + cmd = "cmd", + opts = { + args = { "/C", "if", "exist", cmdpath(file), "del", cmdpath(file) }, + }, + info = info_msg, + err = "Could not delete " .. file, + } + else + return { + cmd = "rm", + opts = { + args = { file }, + }, + info = info_msg, + err = "Could not delete " .. file, + } + end +end + +---@param executables string[] +---@return string|nil +function M.select_executable(executables) + return vim.tbl_filter(function(c) ---@param c string + return c ~= vim.NIL and fn.executable(c) == 1 + end, executables)[1] +end + +-- Returns the compiler arguments based on the compiler and OS +---@param repo InstallInfo +---@param compiler string +---@return string[] +function M.select_compiler_args(repo, compiler) + if string.match(compiler, "cl$") or string.match(compiler, "cl.exe$") then + return { + "/Fe:", + "parser.so", + "/Isrc", + repo.files, + "-Os", + "/utf-8", + "/LD", + } + elseif string.match(compiler, "zig$") or string.match(compiler, "zig.exe$") then + return { + "c++", + "-o", + "parser.so", + repo.files, + "-lc", + "-Isrc", + "-shared", + "-Os", + } + else + local args = { + "-o", + "parser.so", + "-I./src", + repo.files, + "-Os", + } + if fn.has "mac" == 1 then + table.insert(args, "-bundle") + else + table.insert(args, "-shared") + end + if + #vim.tbl_filter(function(file) ---@param file string + local ext = vim.fn.fnamemodify(file, ":e") + return ext == "cc" or ext == "cpp" or ext == "cxx" + end, repo.files) > 0 + then + table.insert(args, "-lstdc++") + end + if fn.has "win32" == 0 then + table.insert(args, "-fPIC") + end + return args + end +end + +-- Returns the compile command based on the OS and user options +---@param repo InstallInfo +---@param cc string +---@param compile_location string +---@return Command +function M.select_compile_command(repo, cc, compile_location) + local make = M.select_executable { "gmake", "make" } + if + string.match(cc, "cl$") + or string.match(cc, "cl.exe$") + or not repo.use_makefile + or fn.has "win32" == 1 + or not make + then + return { + cmd = cc, + info = "Compiling...", + err = "Error during compilation", + opts = { + args = require("nvim-treesitter.compat").flatten(M.select_compiler_args(repo, cc)), + cwd = compile_location, + }, + } + else + return { + cmd = make, + info = "Compiling...", + err = "Error during compilation", + opts = { + args = { + "--makefile=" .. utils.join_path(utils.get_package_path(), "scripts", "compile_parsers.makefile"), + "CC=" .. cc, + "CXX_STANDARD=" .. (repo.cxx_standard or "c++14"), + }, + cwd = compile_location, + }, + } + end +end + +-- Returns the remove command based on the OS +---@param cache_folder string +---@param project_name string +---@return Command +function M.select_install_rm_cmd(cache_folder, project_name) + if fn.has "win32" == 1 then + local dir = cache_folder .. "\\" .. project_name + return { + cmd = "cmd", + opts = { + args = { "/C", "if", "exist", cmdpath(dir), "rmdir", "/s", "/q", cmdpath(dir) }, + }, + } + else + return { + cmd = "rm", + opts = { + args = { "-rf", cache_folder .. "/" .. project_name }, + }, + } + end +end + +-- Returns the move command based on the OS +---@param from string +---@param to string +---@param cwd string +---@return Command +function M.select_mv_cmd(from, to, cwd) + if fn.has "win32" == 1 then + return { + cmd = "cmd", + opts = { + args = { "/C", "move", "/Y", cmdpath(from), cmdpath(to) }, + cwd = cwd, + }, + } + else + return { + cmd = "mv", + opts = { + args = { "-f", from, to }, + cwd = cwd, + }, + } + end +end + +---@param repo InstallInfo +---@param project_name string +---@param cache_folder string +---@param revision string|nil +---@param prefer_git boolean +---@return table +function M.select_download_commands(repo, project_name, cache_folder, revision, prefer_git) + local can_use_tar = vim.fn.executable "tar" == 1 and vim.fn.executable "curl" == 1 + local is_github = repo.url:find("github.com", 1, true) + local is_gitlab = repo.url:find("gitlab.com", 1, true) + + revision = revision or repo.branch or "master" + + if can_use_tar and (is_github or is_gitlab) and not prefer_git then + local path_sep = utils.get_path_sep() + local url = repo.url:gsub(".git$", "") + + local folder_rev = revision + if is_github and revision:match "^v%d" then + folder_rev = revision:sub(2) + end + + return { + M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"), + { + cmd = "curl", + info = "Downloading " .. project_name .. "...", + err = "Error during download, please verify your internet connection", + opts = { + args = { + "--silent", + "-L", -- follow redirects + is_github and url .. "/archive/" .. revision .. ".tar.gz" + or url .. "/-/archive/" .. revision .. "/" .. project_name .. "-" .. revision .. ".tar.gz", + "--output", + project_name .. ".tar.gz", + }, + cwd = cache_folder, + }, + }, + M.select_mkdir_cmd(project_name .. "-tmp", cache_folder, "Creating temporary directory"), + { + cmd = "tar", + info = "Extracting " .. project_name .. "...", + err = "Error during tarball extraction.", + opts = { + args = { + "-xvzf", + project_name .. ".tar.gz", + "-C", + project_name .. "-tmp", + }, + cwd = cache_folder, + }, + }, + M.select_rm_file_cmd(cache_folder .. path_sep .. project_name .. ".tar.gz"), + M.select_mv_cmd( + utils.join_path(project_name .. "-tmp", url:match "[^/]-$" .. "-" .. folder_rev), + project_name, + cache_folder + ), + M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"), + } + else + local git_folder = utils.join_path(cache_folder, project_name) + local clone_error = "Error during download, please verify your internet connection" + + return { + { + cmd = "git", + info = "Downloading " .. project_name .. "...", + err = clone_error, + opts = { + args = { + "clone", + repo.url, + project_name, + "--filter=blob:none", + }, + cwd = cache_folder, + }, + }, + { + cmd = "git", + info = "Checking out locked revision", + err = "Error while checking out revision", + opts = { + args = { + "checkout", + revision, + }, + cwd = git_folder, + }, + }, + } + end +end + +---@param dir string +---@param command string +---@return string command +function M.make_directory_change_for_command(dir, command) + if fn.has "win32" == 1 then + if string.find(vim.o.shell, "cmd") ~= nil then + return string.format("pushd %s & %s & popd", cmdpath(dir), command) + else + return string.format("pushd %s ; %s ; popd", cmdpath(dir), command) + end + else + return string.format("cd %s;\n%s", dir, command) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/statusline.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/statusline.lua new file mode 100644 index 00000000..68ba41ac --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/statusline.lua @@ -0,0 +1,53 @@ +local parsers = require "nvim-treesitter.parsers" +local ts_utils = require "nvim-treesitter.ts_utils" + +local M = {} + +-- Trim spaces and opening brackets from end +local transform_line = function(line) + return line:gsub("%s*[%[%(%{]*%s*$", "") +end + +function M.statusline(opts) + if not parsers.has_parser() then + return + end + local options = opts or {} + if type(opts) == "number" then + options = { indicator_size = opts } + end + local bufnr = options.bufnr or 0 + local indicator_size = options.indicator_size or 100 + local type_patterns = options.type_patterns or { "class", "function", "method" } + local transform_fn = options.transform_fn or transform_line + local separator = options.separator or " -> " + local allow_duplicates = options.allow_duplicates or false + + local current_node = ts_utils.get_node_at_cursor() + if not current_node then + return "" + end + + local lines = {} + local expr = current_node + + while expr do + local line = ts_utils._get_line_for_node(expr, type_patterns, transform_fn, bufnr) + if line ~= "" then + if allow_duplicates or not vim.tbl_contains(lines, line) then + table.insert(lines, 1, line) + end + end + expr = expr:parent() + end + + local text = table.concat(lines, separator) + local text_len = #text + if text_len > indicator_size then + return "..." .. text:sub(text_len - indicator_size, text_len) + end + + return text +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/ts_utils.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/ts_utils.lua new file mode 100644 index 00000000..655c6288 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/ts_utils.lua @@ -0,0 +1,467 @@ +local api = vim.api + +local parsers = require "nvim-treesitter.parsers" +local utils = require "nvim-treesitter.utils" +local ts = vim.treesitter + +local M = {} + +local function get_node_text(node, bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + if not node then + return {} + end + + -- We have to remember that end_col is end-exclusive + local start_row, start_col, end_row, end_col = ts.get_node_range(node) + + if start_row ~= end_row then + local lines = api.nvim_buf_get_lines(bufnr, start_row, end_row + 1, false) + if next(lines) == nil then + return {} + end + lines[1] = string.sub(lines[1], start_col + 1) + -- end_row might be just after the last line. In this case the last line is not truncated. + if #lines == end_row - start_row + 1 then + lines[#lines] = string.sub(lines[#lines], 1, end_col) + end + return lines + else + local line = api.nvim_buf_get_lines(bufnr, start_row, start_row + 1, false)[1] + -- If line is nil then the line is empty + return line and { string.sub(line, start_col + 1, end_col) } or {} + end +end + +---@private +---@param node TSNode +---@param type_patterns string[] +---@param transform_fn fun(line: string): string +---@param bufnr integer +---@return string +function M._get_line_for_node(node, type_patterns, transform_fn, bufnr) + local node_type = node:type() + local is_valid = false + for _, rgx in ipairs(type_patterns) do + if node_type:find(rgx) then + is_valid = true + break + end + end + if not is_valid then + return "" + end + local line = transform_fn(vim.trim(get_node_text(node, bufnr)[1] or ""), node) + -- Escape % to avoid statusline to evaluate content as expression + return line:gsub("%%", "%%%%") +end + +-- Gets the actual text content of a node +-- @deprecated Use vim.treesitter.query.get_node_text +-- @param node the node to get the text from +-- @param bufnr the buffer containing the node +-- @return list of lines of text of the node +function M.get_node_text(node, bufnr) + vim.notify_once( + "nvim-treesitter.ts_utils.get_node_text is deprecated: use vim.treesitter.query.get_node_text", + vim.log.levels.WARN + ) + return get_node_text(node, bufnr) +end + +-- Determines whether a node is the parent of another +-- @param dest the possible parent +-- @param source the possible child node +function M.is_parent(dest, source) + if not (dest and source) then + return false + end + + local current = source + while current ~= nil do + if current == dest then + return true + end + + current = current:parent() + end + + return false +end + +-- Get next node with same parent +---@param node TSNode +---@param allow_switch_parents? boolean allow switching parents if last node +---@param allow_next_parent? boolean allow next parent if last node and next parent without children +function M.get_next_node(node, allow_switch_parents, allow_next_parent) + local destination_node ---@type TSNode + local parent = node:parent() + + if not parent then + return + end + local found_pos = 0 + for i = 0, parent:named_child_count() - 1, 1 do + if parent:named_child(i) == node then + found_pos = i + break + end + end + if parent:named_child_count() > found_pos + 1 then + destination_node = parent:named_child(found_pos + 1) + elseif allow_switch_parents then + local next_node = M.get_next_node(node:parent()) + if next_node and next_node:named_child_count() > 0 then + destination_node = next_node:named_child(0) + elseif next_node and allow_next_parent then + destination_node = next_node + end + end + + return destination_node +end + +-- Get previous node with same parent +---@param node TSNode +---@param allow_switch_parents? boolean allow switching parents if first node +---@param allow_previous_parent? boolean allow previous parent if first node and previous parent without children +function M.get_previous_node(node, allow_switch_parents, allow_previous_parent) + local destination_node ---@type TSNode + local parent = node:parent() + if not parent then + return + end + + local found_pos = 0 + for i = 0, parent:named_child_count() - 1, 1 do + if parent:named_child(i) == node then + found_pos = i + break + end + end + if 0 < found_pos then + destination_node = parent:named_child(found_pos - 1) + elseif allow_switch_parents then + local previous_node = M.get_previous_node(node:parent()) + if previous_node and previous_node:named_child_count() > 0 then + destination_node = previous_node:named_child(previous_node:named_child_count() - 1) + elseif previous_node and allow_previous_parent then + destination_node = previous_node + end + end + return destination_node +end + +function M.get_named_children(node) + local nodes = {} ---@type TSNode[] + for i = 0, node:named_child_count() - 1, 1 do + nodes[i + 1] = node:named_child(i) + end + return nodes +end + +function M.get_node_at_cursor(winnr, ignore_injected_langs) + winnr = winnr or 0 + local cursor = api.nvim_win_get_cursor(winnr) + local cursor_range = { cursor[1] - 1, cursor[2] } + + local buf = vim.api.nvim_win_get_buf(winnr) + local root_lang_tree = parsers.get_parser(buf) + if not root_lang_tree then + return + end + + local root ---@type TSNode|nil + if ignore_injected_langs then + for _, tree in pairs(root_lang_tree:trees()) do + local tree_root = tree:root() + if tree_root and ts.is_in_node_range(tree_root, cursor_range[1], cursor_range[2]) then + root = tree_root + break + end + end + else + root = M.get_root_for_position(cursor_range[1], cursor_range[2], root_lang_tree) + end + + if not root then + return + end + + return root:named_descendant_for_range(cursor_range[1], cursor_range[2], cursor_range[1], cursor_range[2]) +end + +function M.get_root_for_position(line, col, root_lang_tree) + if not root_lang_tree then + if not parsers.has_parser() then + return + end + + root_lang_tree = parsers.get_parser() + end + + local lang_tree = root_lang_tree:language_for_range { line, col, line, col } + + for _, tree in pairs(lang_tree:trees()) do + local root = tree:root() + + if root and ts.is_in_node_range(root, line, col) then + return root, tree, lang_tree + end + end + + -- This isn't a likely scenario, since the position must belong to a tree somewhere. + return nil, nil, lang_tree +end + +---comment +---@param node TSNode +---@return TSNode result +function M.get_root_for_node(node) + local parent = node + local result = node + + while parent ~= nil do + result = parent + parent = result:parent() + end + + return result +end + +function M.highlight_node(node, buf, hl_namespace, hl_group) + if not node then + return + end + M.highlight_range({ node:range() }, buf, hl_namespace, hl_group) +end + +-- Get a compatible vim range (1 index based) from a TS node range. +-- +-- TS nodes start with 0 and the end col is ending exclusive. +-- They also treat a EOF/EOL char as a char ending in the first +-- col of the next row. +---comment +---@param range integer[] +---@param buf integer|nil +---@return integer, integer, integer, integer +function M.get_vim_range(range, buf) + ---@type integer, integer, integer, integer + local srow, scol, erow, ecol = unpack(range) + srow = srow + 1 + scol = scol + 1 + erow = erow + 1 + + if ecol == 0 then + -- Use the value of the last col of the previous row instead. + erow = erow - 1 + if not buf or buf == 0 then + ecol = vim.fn.col { erow, "$" } - 1 + else + ecol = #api.nvim_buf_get_lines(buf, erow - 1, erow, false)[1] + end + ecol = math.max(ecol, 1) + end + return srow, scol, erow, ecol +end + +function M.highlight_range(range, buf, hl_namespace, hl_group) + ---@type integer, integer, integer, integer + local start_row, start_col, end_row, end_col = unpack(range) + ---@diagnostic disable-next-line: missing-parameter + vim.highlight.range(buf, hl_namespace, hl_group, { start_row, start_col }, { end_row, end_col }) +end + +-- Set visual selection to node +-- @param selection_mode One of "charwise" (default) or "v", "linewise" or "V", +-- "blockwise" or "" (as a string with 5 characters or a single character) +function M.update_selection(buf, node, selection_mode) + local start_row, start_col, end_row, end_col = M.get_vim_range({ ts.get_node_range(node) }, buf) + + local v_table = { charwise = "v", linewise = "V", blockwise = "" } + selection_mode = selection_mode or "charwise" + + -- Normalise selection_mode + if vim.tbl_contains(vim.tbl_keys(v_table), selection_mode) then + selection_mode = v_table[selection_mode] + end + + -- enter visual mode if normal or operator-pending (no) mode + -- Why? According to https://learnvimscriptthehardway.stevelosh.com/chapters/15.html + -- If your operator-pending mapping ends with some text visually selected, Vim will operate on that text. + -- Otherwise, Vim will operate on the text between the original cursor position and the new position. + local mode = api.nvim_get_mode() + if mode.mode ~= selection_mode then + -- Call to `nvim_replace_termcodes()` is needed for sending appropriate command to enter blockwise mode + selection_mode = vim.api.nvim_replace_termcodes(selection_mode, true, true, true) + api.nvim_cmd({ cmd = "normal", bang = true, args = { selection_mode } }, {}) + end + + api.nvim_win_set_cursor(0, { start_row, start_col - 1 }) + vim.cmd "normal! o" + api.nvim_win_set_cursor(0, { end_row, end_col - 1 }) +end + +-- Byte length of node range +---@param node TSNode +---@return number +function M.node_length(node) + local _, _, start_byte = node:start() + local _, _, end_byte = node:end_() + return end_byte - start_byte +end + +---@deprecated Use `vim.treesitter.is_in_node_range()` instead +function M.is_in_node_range(node, line, col) + vim.notify_once( + "nvim-treesitter.ts_utils.is_in_node_range is deprecated: use vim.treesitter.is_in_node_range", + vim.log.levels.WARN + ) + return ts.is_in_node_range(node, line, col) +end + +---@deprecated Use `vim.treesitter.get_node_range()` instead +function M.get_node_range(node_or_range) + vim.notify_once( + "nvim-treesitter.ts_utils.get_node_range is deprecated: use vim.treesitter.get_node_range", + vim.log.levels.WARN + ) + return ts.get_node_range(node_or_range) +end + +---@param node TSNode +---@return table +function M.node_to_lsp_range(node) + local start_line, start_col, end_line, end_col = ts.get_node_range(node) + local rtn = {} + rtn.start = { line = start_line, character = start_col } + rtn["end"] = { line = end_line, character = end_col } + return rtn +end + +-- Memoizes a function based on the buffer tick of the provided bufnr. +-- The cache entry is cleared when the buffer is detached to avoid memory leaks. +-- The options argument is a table with two optional values: +-- - bufnr: extracts a bufnr from the given arguments. +-- - key: extracts the cache key from the given arguments. +---@param fn function the fn to memoize, taking the buffer as first argument +---@param options? {bufnr: integer?, key: string|fun(...): string?} the memoization options +---@return function: a memoized function +function M.memoize_by_buf_tick(fn, options) + options = options or {} + + ---@type table + local cache = setmetatable({}, { __mode = "kv" }) + local bufnr_fn = utils.to_func(options.bufnr or utils.identity) + local key_fn = utils.to_func(options.key or utils.identity) + + return function(...) + local bufnr = bufnr_fn(...) + local key = key_fn(...) + local tick = api.nvim_buf_get_changedtick(bufnr) + + if cache[key] then + if cache[key].last_tick == tick then + return cache[key].result + end + else + local function detach_handler() + cache[key] = nil + end + + -- Clean up logic only! + api.nvim_buf_attach(bufnr, false, { + on_detach = detach_handler, + on_reload = detach_handler, + }) + end + + cache[key] = { + result = fn(...), + last_tick = tick, + } + + return cache[key].result + end +end + +function M.swap_nodes(node_or_range1, node_or_range2, bufnr, cursor_to_second) + if not node_or_range1 or not node_or_range2 then + return + end + local range1 = M.node_to_lsp_range(node_or_range1) + local range2 = M.node_to_lsp_range(node_or_range2) + + local text1 = get_node_text(node_or_range1, bufnr) + local text2 = get_node_text(node_or_range2, bufnr) + + local edit1 = { range = range1, newText = table.concat(text2, "\n") } + local edit2 = { range = range2, newText = table.concat(text1, "\n") } + vim.lsp.util.apply_text_edits({ edit1, edit2 }, bufnr, "utf-8") + + if cursor_to_second then + utils.set_jump() + + local char_delta = 0 + local line_delta = 0 + if + range1["end"].line < range2.start.line + or (range1["end"].line == range2.start.line and range1["end"].character <= range2.start.character) + then + line_delta = #text2 - #text1 + end + + if range1["end"].line == range2.start.line and range1["end"].character <= range2.start.character then + if line_delta ~= 0 then + --- why? + --correction_after_line_change = -range2.start.character + --text_now_before_range2 = #(text2[#text2]) + --space_between_ranges = range2.start.character - range1["end"].character + --char_delta = correction_after_line_change + text_now_before_range2 + space_between_ranges + --- Equivalent to: + char_delta = #text2[#text2] - range1["end"].character + + -- add range1.start.character if last line of range1 (now text2) does not start at 0 + if range1.start.line == range2.start.line + line_delta then + char_delta = char_delta + range1.start.character + end + else + char_delta = #text2[#text2] - #text1[#text1] + end + end + + api.nvim_win_set_cursor( + api.nvim_get_current_win(), + { range2.start.line + 1 + line_delta, range2.start.character + char_delta } + ) + end +end + +function M.goto_node(node, goto_end, avoid_set_jump) + if not node then + return + end + if not avoid_set_jump then + utils.set_jump() + end + local range = { M.get_vim_range { node:range() } } + ---@type table + local position + if not goto_end then + position = { range[1], range[2] } + else + position = { range[3], range[4] } + end + + -- Enter visual mode if we are in operator pending mode + -- If we don't do this, it will miss the last character. + local mode = vim.api.nvim_get_mode() + if mode.mode == "no" then + vim.cmd "normal! v" + end + + -- Position is 1, 0 indexed. + api.nvim_win_set_cursor(0, { position[1], position[2] - 1 }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/tsrange.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/tsrange.lua new file mode 100644 index 00000000..d41585c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/tsrange.lua @@ -0,0 +1,154 @@ +local M = {} +local TSRange = {} +TSRange.__index = TSRange + +local api = vim.api +local ts_utils = require "nvim-treesitter.ts_utils" +local parsers = require "nvim-treesitter.parsers" + +local function get_byte_offset(buf, row, col) + return api.nvim_buf_get_offset(buf, row) + vim.fn.byteidx(api.nvim_buf_get_lines(buf, row, row + 1, false)[1], col) +end + +function TSRange.new(buf, start_row, start_col, end_row, end_col) + return setmetatable({ + start_pos = { start_row, start_col, get_byte_offset(buf, start_row, start_col) }, + end_pos = { end_row, end_col, get_byte_offset(buf, end_row, end_col) }, + buf = buf, + [1] = start_row, + [2] = start_col, + [3] = end_row, + [4] = end_col, + }, TSRange) +end + +function TSRange.from_nodes(buf, start_node, end_node) + TSRange.__index = TSRange + local start_pos = start_node and { start_node:start() } or { end_node:start() } + local end_pos = end_node and { end_node:end_() } or { start_node:end_() } + return setmetatable({ + start_pos = { start_pos[1], start_pos[2], start_pos[3] }, + end_pos = { end_pos[1], end_pos[2], end_pos[3] }, + buf = buf, + [1] = start_pos[1], + [2] = start_pos[2], + [3] = end_pos[1], + [4] = end_pos[2], + }, TSRange) +end + +function TSRange.from_table(buf, range) + return setmetatable({ + start_pos = { range[1], range[2], get_byte_offset(buf, range[1], range[2]) }, + end_pos = { range[3], range[4], get_byte_offset(buf, range[3], range[4]) }, + buf = buf, + [1] = range[1], + [2] = range[2], + [3] = range[3], + [4] = range[4], + }, TSRange) +end + +function TSRange:parent() + local root_lang_tree = parsers.get_parser(self.buf) + local root = ts_utils.get_root_for_position(self[1], self[2], root_lang_tree) + + return root + and root:named_descendant_for_range(self.start_pos[1], self.start_pos[2], self.end_pos[1], self.end_pos[2]) + or nil +end + +function TSRange:field() end + +function TSRange:child_count() + return #self:collect_children() +end + +function TSRange:named_child_count() + return #self:collect_children(function(c) + return c:named() + end) +end + +function TSRange:iter_children() + local raw_iterator = self:parent().iter_children() + return function() + while true do + local node = raw_iterator() + if not node then + return + end + local _, _, start_byte = node:start() + local _, _, end_byte = node:end_() + if start_byte >= self.start_pos[3] and end_byte <= self.end_pos[3] then + return node + end + end + end +end + +function TSRange:collect_children(filter_fun) + local children = {} + for _, c in self:iter_children() do + if not filter_fun or filter_fun(c) then + table.insert(children, c) + end + end + return children +end + +function TSRange:child(index) + return self:collect_children()[index + 1] +end + +function TSRange:named_child(index) + return self:collect_children(function(c) + return c.named() + end)[index + 1] +end + +function TSRange:start() + return unpack(self.start_pos) +end + +function TSRange:end_() + return unpack(self.end_pos) +end + +function TSRange:range() + return self.start_pos[1], self.start_pos[2], self.end_pos[1], self.end_pos[2] +end + +function TSRange:type() + return "nvim-treesitter-range" +end + +function TSRange:symbol() + return -1 +end + +function TSRange:named() + return false +end + +function TSRange:missing() + return false +end + +function TSRange:has_error() + return #self:collect_children(function(c) + return c:has_error() + end) > 0 and true or false +end + +function TSRange:sexpr() + return table.concat( + vim.tbl_map(function(c) + return c:sexpr() + end, self:collect_children()), + " " + ) +end + +M.TSRange = TSRange +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/utils.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/utils.lua new file mode 100644 index 00000000..d920f4a6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/lua/nvim-treesitter/utils.lua @@ -0,0 +1,237 @@ +local api = vim.api +local fn = vim.fn +local luv = vim.loop + +local M = {} + +-- Wrapper around vim.notify with common options set. +---@param msg string +---@param log_level number|nil +---@param opts table|nil +function M.notify(msg, log_level, opts) + local default_opts = { title = "nvim-treesitter" } + vim.notify(msg, log_level, vim.tbl_extend("force", default_opts, opts or {})) +end + +-- Returns the system-specific path separator. +---@return string +function M.get_path_sep() + return (fn.has "win32" == 1 and not vim.opt.shellslash:get()) and "\\" or "/" +end + +-- Returns a function that joins the given arguments with separator. Arguments +-- can't be nil. Example: +-- +--[[ + print(M.generate_join(" ")("foo", "bar")) +--]] +--prints "foo bar" +---@param separator string +---@return fun(...: string): string +function M.generate_join(separator) + return function(...) + return table.concat({ ... }, separator) + end +end + +M.join_path = M.generate_join(M.get_path_sep()) + +M.join_space = M.generate_join " " + +---@class Command +---@field run function +---@field f_args string +---@field args string + +-- Define user defined vim command which calls nvim-treesitter module function +-- - If module name is 'mod', it should be defined in hierarchy 'nvim-treesitter.mod' +-- - A table with name 'commands' should be defined in 'mod' which needs to be passed as +-- the commands param of this function +-- +---@param mod string Name of the module that resides in the hierarchy - nvim-treesitter.module +---@param commands table Command list for the module +--- - {command_name} Name of the vim user defined command, Keys: +--- - {run}: (function) callback function that needs to be executed +--- - {f_args}: (string, default ) +--- - type of arguments that needs to be passed to the vim command +--- - {args}: (string, optional) +--- - vim command attributes +--- +---* @example +--- If module is nvim-treesitter.custom_mod +---
+---  M.commands = {
+---      custom_command = {
+---          run = M.module_function,
+---          f_args = "",
+---          args = {
+---              "-range"
+---          }
+---      }
+---  }
+---
+---  utils.setup_commands("custom_mod", require("nvim-treesitter.custom_mod").commands)
+---  
+--- +--- Will generate command : +---
+---  command! -range custom_command \
+---      lua require'nvim-treesitter.custom_mod'.commands.custom_command['run']()
+---  
+function M.setup_commands(mod, commands) + for command_name, def in pairs(commands) do + local f_args = def.f_args or "" + local call_fn = + string.format("lua require'nvim-treesitter.%s'.commands.%s['run'](%s)", mod, command_name, f_args) + local parts = require("nvim-treesitter.compat").flatten { + "command!", + "-bar", + def.args, + command_name, + call_fn, + } + api.nvim_command(table.concat(parts, " ")) + end +end + +---@param dir string +---@param create_err string +---@param writeable_err string +---@return string|nil, string|nil +function M.create_or_reuse_writable_dir(dir, create_err, writeable_err) + create_err = create_err or M.join_space("Could not create dir '", dir, "': ") + writeable_err = writeable_err or M.join_space("Invalid rights, '", dir, "' should be read/write") + -- Try creating and using parser_dir if it doesn't exist + if not luv.fs_stat(dir) then + local ok, error = pcall(vim.fn.mkdir, dir, "p", "0755") + if not ok then + return nil, M.join_space(create_err, error) + end + + return dir + end + + -- parser_dir exists, use it if it's read/write + if luv.fs_access(dir, "RW") then + return dir + end + + -- parser_dir exists but isn't read/write, give up + return nil, M.join_space(writeable_err, dir, "'") +end + +function M.get_package_path() + -- Path to this source file, removing the leading '@' + local source = string.sub(debug.getinfo(1, "S").source, 2) + + -- Path to the package root + return fn.fnamemodify(source, ":p:h:h:h") +end + +function M.get_cache_dir() + local cache_dir = fn.stdpath "data" + + if luv.fs_access(cache_dir, "RW") then + return cache_dir + elseif luv.fs_access("/tmp", "RW") then + return "/tmp" + end + + return nil, M.join_space("Invalid cache rights,", fn.stdpath "data", "or /tmp should be read/write") +end + +-- Returns $XDG_DATA_HOME/nvim/site, but could use any directory that is in +-- runtimepath +function M.get_site_dir() + return M.join_path(fn.stdpath "data", "site") +end + +-- Gets a property at path +---@param tbl table the table to access +---@param path string the '.' separated path +---@return table|nil result the value at path or nil +function M.get_at_path(tbl, path) + if path == "" then + return tbl + end + + local segments = vim.split(path, ".", true) + ---@type table[]|table + local result = tbl + + for _, segment in ipairs(segments) do + if type(result) == "table" then + ---@type table + -- TODO: figure out the actual type of tbl + result = result[segment] + end + end + + return result +end + +function M.set_jump() + vim.cmd "normal! m'" +end + +-- Filters a list based on the given predicate +---@param tbl any[] The list to filter +---@param predicate fun(v:any, i:number):boolean The predicate to filter with +function M.filter(tbl, predicate) + local result = {} + + for i, v in ipairs(tbl) do + if predicate(v, i) then + table.insert(result, v) + end + end + + return result +end + +-- Returns a list of all values from the first list +-- that are not present in the second list. +---@param tbl1 any[] The first table +---@param tbl2 any[] The second table +---@return table +function M.difference(tbl1, tbl2) + return M.filter(tbl1, function(v) + return not vim.tbl_contains(tbl2, v) + end) +end + +function M.identity(a) + return a +end + +-- Returns a function returning the given value +---@param a any +---@return fun():any +function M.constant(a) + return function() + return a + end +end + +-- Returns a function that returns the given value if it is a function, +-- otherwise returns a function that returns the given value. +---@param a any +---@return fun(...):any +function M.to_func(a) + return type(a) == "function" and a or M.constant(a) +end + +---@return string|nil +function M.ts_cli_version() + if fn.executable "tree-sitter" == 1 then + local handle = io.popen "tree-sitter -V" + if not handle then + return + end + local result = handle:read "*a" + handle:close() + return vim.split(result, "\n")[1]:match "[^tree%psitter ].*" + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/parser-info/.gitignore b/config/neovim/store/lazy-plugins/nvim-treesitter/parser-info/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/parser-info/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/plugin/nvim-treesitter.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/plugin/nvim-treesitter.lua new file mode 100644 index 00000000..4ea3925f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/plugin/nvim-treesitter.lua @@ -0,0 +1,34 @@ +-- Last Change: 2022 Apr 16 + +if vim.g.loaded_nvim_treesitter then + return +end +vim.g.loaded_nvim_treesitter = true + +-- setup modules +require("nvim-treesitter").setup() + +local api = vim.api + +-- define autocommands +local augroup = api.nvim_create_augroup("NvimTreesitter", {}) + +api.nvim_create_autocmd("Filetype", { + pattern = "query", + group = augroup, + callback = function() + api.nvim_clear_autocmds { + group = augroup, + event = "BufWritePost", + } + api.nvim_create_autocmd("BufWritePost", { + group = augroup, + buffer = 0, + callback = function(opts) + require("nvim-treesitter.query").invalidate_query_file(opts.file) + end, + desc = "Invalidate query file", + }) + end, + desc = "Reload query", +}) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ada/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ada/folds.scm new file mode 100644 index 00000000..8e3defac --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ada/folds.scm @@ -0,0 +1,13 @@ +; Support for folding in Ada +; za toggles folding a package, subprogram, if statement or loop +[ + (package_declaration) + (generic_package_declaration) + (package_body) + (subprogram_body) + (block_statement) + (if_statement) + (loop_statement) + (gnatprep_declarative_if_statement) + (gnatprep_if_statement) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ada/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ada/highlights.scm new file mode 100644 index 00000000..0d42b70f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ada/highlights.scm @@ -0,0 +1,286 @@ +; highlight queries. +; See the syntax at https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries +; See also https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md#parser-configurations +; for a list of recommended @ tags, though not all of them have matching +; highlights in neovim. +[ + "abort" + "abs" + "abstract" + "accept" + "access" + "all" + "array" + "at" + "begin" + "body" + "declare" + "delay" + "delta" + "digits" + "do" + "end" + "entry" + "exit" + "generic" + "interface" + "is" + "limited" + "mod" + "new" + "null" + "of" + "others" + "out" + "overriding" + "package" + "pragma" + "private" + "protected" + "range" + "separate" + "subtype" + "synchronized" + "tagged" + "task" + "terminate" + "type" + "until" + "when" +] @keyword + +"record" @keyword.type + +[ + "aliased" + "constant" + "renames" +] @keyword.modifier + +[ + "with" + "use" +] @keyword.import + +[ + "function" + "procedure" +] @keyword.function + +[ + "and" + "in" + "not" + "or" + "xor" +] @keyword.operator + +[ + "while" + "loop" + "for" + "parallel" + "reverse" + "some" +] @keyword.repeat + +"return" @keyword.return + +[ + "case" + "if" + "else" + "then" + "elsif" + "select" +] @keyword.conditional + +[ + "exception" + "raise" +] @keyword.exception + +(comment) @comment @spell + +(string_literal) @string + +(character_literal) @string + +(numeric_literal) @number + +; Highlight the name of subprograms +(procedure_specification + name: (_) @function) + +(function_specification + name: (_) @function) + +(package_declaration + name: (_) @function) + +(package_body + name: (_) @function) + +(generic_instantiation + name: (_) @function) + +(entry_declaration + . + (identifier) @function) + +; Some keywords should take different categories depending on the context +(use_clause + "use" @keyword.import + "type" @keyword.import) + +(with_clause + "private" @keyword.import) + +(with_clause + "limited" @keyword.import) + +(use_clause + (_) @module) + +(with_clause + (_) @module) + +(loop_statement + "end" @keyword.repeat) + +(if_statement + "end" @keyword.conditional) + +(loop_parameter_specification + "in" @keyword.repeat) + +(loop_parameter_specification + "in" @keyword.repeat) + +(iterator_specification + [ + "in" + "of" + ] @keyword.repeat) + +(range_attribute_designator + "range" @keyword.repeat) + +(raise_statement + "with" @keyword.exception) + +(gnatprep_declarative_if_statement) @keyword.directive + +(gnatprep_if_statement) @keyword.directive + +(gnatprep_identifier) @keyword.directive + +(subprogram_declaration + "is" @keyword.function + "abstract" @keyword.function) + +(aspect_specification + "with" @keyword.function) + +(full_type_declaration + "is" @keyword.type) + +(subtype_declaration + "is" @keyword.type) + +(record_definition + "end" @keyword.type) + +(full_type_declaration + (_ + "access" @keyword.type)) + +(array_type_definition + "array" @keyword.type + "of" @keyword.type) + +(access_to_object_definition + "access" @keyword.type) + +(access_to_object_definition + "access" @keyword.type + [ + (general_access_modifier + "constant" @keyword.type) + (general_access_modifier + "all" @keyword.type) + ]) + +(range_constraint + "range" @keyword.type) + +(signed_integer_type_definition + "range" @keyword.type) + +(index_subtype_definition + "range" @keyword.type) + +(record_type_definition + "abstract" @keyword.type) + +(record_type_definition + "tagged" @keyword.type) + +(record_type_definition + "limited" @keyword.type) + +(record_type_definition + (record_definition + "null" @keyword.type)) + +(private_type_declaration + "is" @keyword.type + "private" @keyword.type) + +(private_type_declaration + "tagged" @keyword.type) + +(private_type_declaration + "limited" @keyword.type) + +(task_type_declaration + "task" @keyword.type + "is" @keyword.type) + +; Gray the body of expression functions +(expression_function_declaration + (function_specification) + "is" + (_) @attribute) + +(subprogram_declaration + (aspect_specification) @attribute) + +; Highlight full subprogram specifications +;(subprogram_body +; [ +; (procedure_specification) +; (function_specification) +; ] @function.spec +;) +((comment) @comment.documentation + . + [ + (entry_declaration) + (subprogram_declaration) + (parameter_specification) + ]) + +(compilation_unit + . + (comment) @comment.documentation) + +(component_list + (component_declaration) + . + (comment) @comment.documentation) + +(enumeration_type_definition + (identifier) + . + (comment) @comment.documentation) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ada/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ada/locals.scm new file mode 100644 index 00000000..bdfc38be --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ada/locals.scm @@ -0,0 +1,91 @@ +; Better highlighting by referencing to the definition, for variable +; references. However, this is not yet supported by neovim +; See https://tree-sitter.github.io/tree-sitter/syntax-highlighting#local-variables +(compilation) @local.scope + +(package_declaration) @local.scope + +(package_body) @local.scope + +(subprogram_declaration) @local.scope + +(subprogram_body) @local.scope + +(block_statement) @local.scope + +(with_clause + (identifier) @local.definition.import) + +(procedure_specification + name: (_) @local.definition.function) + +(function_specification + name: (_) @local.definition.function) + +(package_declaration + name: (_) @local.definition.var) + +(package_body + name: (_) @local.definition.var) + +(generic_instantiation + . + name: (_) @local.definition.var) + +(component_declaration + . + (identifier) @local.definition.var) + +(exception_declaration + . + (identifier) @local.definition.var) + +(formal_object_declaration + . + (identifier) @local.definition.var) + +(object_declaration + . + (identifier) @local.definition.var) + +(parameter_specification + . + (identifier) @local.definition.var) + +(full_type_declaration + . + (identifier) @local.definition.type) + +(private_type_declaration + . + (identifier) @local.definition.type) + +(private_extension_declaration + . + (identifier) @local.definition.type) + +(incomplete_type_declaration + . + (identifier) @local.definition.type) + +(protected_type_declaration + . + (identifier) @local.definition.type) + +(formal_complete_type_declaration + . + (identifier) @local.definition.type) + +(formal_incomplete_type_declaration + . + (identifier) @local.definition.type) + +(task_type_declaration + . + (identifier) @local.definition.type) + +(subtype_declaration + . + (identifier) @local.definition.type) + +(identifier) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/agda/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/agda/folds.scm new file mode 100644 index 00000000..5e1051f8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/agda/folds.scm @@ -0,0 +1,4 @@ +[ + (record) + (module) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/agda/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/agda/highlights.scm new file mode 100644 index 00000000..4626a8c1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/agda/highlights.scm @@ -0,0 +1,87 @@ +; Constants +(integer) @number + +; Variables and Symbols +(typed_binding + (atom + (qid) @variable)) + +(untyped_binding) @variable + +(typed_binding + (expr) @type) + +(id) @function + +(bid) @function + +(function_name + (atom + (qid) @function)) + +(field_name) @function + +[ + (data_name) + (record_name) +] @constructor + +; Set +(SetN) @type.builtin + +(expr + . + (atom) @function) + +((atom) @boolean + (#any-of? @boolean "true" "false" "True" "False")) + +; Imports and Module Declarations +"import" @keyword.import + +(module_name) @module + +; Pragmas and comments +(pragma) @keyword.directive + +(comment) @comment @spell + +; Keywords +[ + "where" + "data" + "rewrite" + "postulate" + "public" + "private" + "tactic" + "Prop" + "quote" + "renaming" + "open" + "in" + "hiding" + "constructor" + "abstract" + "let" + "field" + "mutual" + "module" + "infix" + "infixl" + "infixr" +] @keyword + +"record" @keyword.type + +;(expr +; f_name: (atom) @function) +; Brackets +[ + "(" + ")" + "{" + "}" +] @punctuation.bracket + +"=" @operator diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/folds.scm new file mode 100644 index 00000000..1f2129cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/folds.scm @@ -0,0 +1 @@ +; inherits: html diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/highlights.scm new file mode 100644 index 00000000..09e3abfd --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/highlights.scm @@ -0,0 +1,150 @@ +; inherits: html_tags + +(identifier) @variable + +(pipe_operator) @operator + +[ + (string) + (static_member_expression) +] @string + +(number) @number + +(pipe_call + name: (identifier) @function) + +(pipe_call + arguments: (pipe_arguments + (identifier) @variable.parameter)) + +(structural_directive + "*" @keyword + (identifier) @keyword) + +(attribute + (attribute_name) @variable.member + (#lua-match? @variable.member "#.*")) + +(binding_name + (identifier) @keyword) + +(event_binding + (binding_name + (identifier) @keyword)) + +(event_binding + "\"" @punctuation.delimiter) + +(property_binding + "\"" @punctuation.delimiter) + +(structural_assignment + operator: (identifier) @keyword) + +(member_expression + property: (identifier) @property) + +(call_expression + function: (identifier) @function) + +(call_expression + function: ((identifier) @function.builtin + (#eq? @function.builtin "$any"))) + +(pair + key: ((identifier) @variable.builtin + (#eq? @variable.builtin "$implicit"))) + +((control_keyword) @keyword.repeat + (#any-of? @keyword.repeat "for" "empty")) + +((control_keyword) @keyword.conditional + (#any-of? @keyword.conditional "if" "else" "switch" "case" "default")) + +((control_keyword) @keyword.coroutine + (#any-of? @keyword.coroutine "defer" "placeholder" "loading")) + +((control_keyword) @keyword.exception + (#eq? @keyword.exception "error")) + +(special_keyword) @keyword + +((identifier) @boolean + (#any-of? @boolean "true" "false")) + +((identifier) @variable.builtin + (#any-of? @variable.builtin "this" "$event")) + +((identifier) @constant.builtin + (#eq? @constant.builtin "null")) + +[ + (ternary_operator) + (conditional_operator) +] @keyword.conditional.ternary + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "@" + "} @" + (if_end_expression) + (for_end_expression) + (switch_end_expression) + (case_end_expression) + (default_end_expression) + (defer_end_expression) +] @punctuation.bracket + +(two_way_binding + [ + "[(" + ")]" + ] @punctuation.bracket) + +[ + "{{" + "}}" +] @punctuation.special + +[ + ";" + "." + "," + "?." +] @punctuation.delimiter + +(nullish_coalescing_expression + (coalescing_operator) @operator) + +(concatenation_expression + "+" @operator) + +(icu_clause) @keyword.operator + +(icu_category) @keyword.conditional + +(binary_expression + [ + "-" + "&&" + "+" + "<" + "<=" + "=" + "==" + "===" + "!=" + "!==" + ">" + ">=" + "*" + "/" + "||" + "%" + ] @operator) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/indents.scm new file mode 100644 index 00000000..448e9427 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/indents.scm @@ -0,0 +1 @@ +; inherits: html_tags diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/injections.scm new file mode 100644 index 00000000..448e9427 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/injections.scm @@ -0,0 +1 @@ +; inherits: html_tags diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/locals.scm new file mode 100644 index 00000000..1f2129cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/angular/locals.scm @@ -0,0 +1 @@ +; inherits: html diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/apex/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/apex/folds.scm new file mode 100644 index 00000000..fdfc2a1e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/apex/folds.scm @@ -0,0 +1,6 @@ +[ + (class_body) + (constructor_declaration) + (argument_list) + (annotation_argument_list) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/apex/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/apex/highlights.scm new file mode 100644 index 00000000..85453b22 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/apex/highlights.scm @@ -0,0 +1,260 @@ +; inherits: soql + +; Apex + SOQL +[ + "[" + "]" + "{" + "}" + "(" + ")" +] @punctuation.bracket + +[ + "," + "." + ":" + "?" + ";" +] @punctuation.delimiter + +; Default general color definition +(identifier) @variable + +(type_identifier) @type + +; Methods +(method_declaration + name: (identifier) @function.method) + +(method_invocation + name: (identifier) @function.method.call) + +(super) @function.builtin + +; Annotations +(annotation + name: (identifier) @attribute) + +; Types +(interface_declaration + name: (identifier) @type) + +(class_declaration + name: (identifier) @type) + +(class_declaration + (superclass) @type) + +(enum_declaration + name: (identifier) @type) + +(enum_constant + name: (identifier) @constant) + +(type_arguments + "<" @punctuation.delimiter) + +(type_arguments + ">" @punctuation.delimiter) + +(field_access + object: (identifier) @type) + +(field_access + field: (identifier) @property) + +((scoped_identifier + scope: (identifier) @type) + (#match? @type "^[A-Z]")) + +((method_invocation + object: (identifier) @type) + (#match? @type "^[A-Z]")) + +(method_declaration + (formal_parameters + (formal_parameter + name: (identifier) @variable.parameter))) + +(constructor_declaration + name: (identifier) @constructor) + +(dml_type) @function.builtin + +(assignment_operator) @operator + +(update_expression + [ + "++" + "--" + ] @operator) + +(trigger_declaration + name: (identifier) @type + object: (identifier) @type + (trigger_event) @keyword + ("," + (trigger_event) @keyword)*) + +[ + "@" + "=" + "!=" + "<=" + ">=" +] @operator + +(binary_expression + operator: [ + ">" + "<" + "==" + "===" + "!==" + "&&" + "||" + "+" + "-" + "*" + "/" + "&" + "|" + "^" + "%" + "<<" + ">>" + ">>>" + ] @operator) + +(unary_expression + operator: [ + "+" + "-" + "!" + "~" + ]) @operator + +(map_initializer + "=>" @operator) + +[ + (boolean_type) + (void_type) +] @type.builtin + +; Fields +(field_declaration + declarator: (variable_declarator + name: (identifier) @variable.member)) + +(field_access + field: (identifier) @variable.member) + +; Variables +(field_declaration + (modifiers + (modifier + [ + "final" + "static" + ]) + (modifier + [ + "final" + "static" + ])) + (variable_declarator + name: (identifier) @constant)) + +(variable_declarator + (identifier) @property) + +((identifier) @constant + (#lua-match? @constant "^[A-Z][A-Z0-9_]+$")) ; SCREAM SNAKE CASE + +(this) @variable.builtin + +; Literals +[ + (int) + (decimal) + (currency_literal) +] @number + +(string_literal) @string + +[ + (line_comment) + (block_comment) +] @comment + +(null_literal) @constant.builtin + +; ;; Keywords +[ + "abstract" + "final" + "private" + "protected" + "public" + "static" +] @keyword.modifier + +[ + "if" + "else" + "switch" +] @keyword.conditional + +[ + "for" + "while" + "do" + "break" +] @keyword.repeat + +"return" @keyword.return + +[ + "throw" + "finally" + "try" + "catch" +] @keyword.exception + +"new" @keyword.operator + +[ + "abstract" + "continue" + "default" + "extends" + "final" + "get" + "global" + "implements" + "instanceof" + "on" + "private" + "protected" + "public" + "set" + "static" + "testMethod" + "transient" + "trigger" + "virtual" + "when" + "with_sharing" + "without_sharing" + "inherited_sharing" +] @keyword + +[ + "interface" + "class" + "enum" +] @keyword.type + +"System.runAs" @function.builtin diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/apex/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/apex/locals.scm new file mode 100644 index 00000000..2457752f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/apex/locals.scm @@ -0,0 +1,70 @@ +; declarations +(class_declaration) @local.scope + +(method_declaration) @local.scope + +(constructor_declaration) @local.scope + +(enum_declaration) @local.scope + +(enhanced_for_statement) @local.scope + +; if/else +(if_statement) @local.scope + +(if_statement + consequence: (_) @local.scope) ; if body in case there are no braces + +(if_statement + alternative: (_) @local.scope) ; else body in case there are no braces + +; try/catch +(try_statement) @local.scope ; covers try+catch, individual try and catch are covered by (block) + +(catch_clause) @local.scope ; needed because `Exception` variable + +; loops +(for_statement) @local.scope + +(for_statement ; "for" body in case there are no braces + body: (_) @local.scope) + +(do_statement + body: (_) @local.scope) + +(while_statement + body: (_) @local.scope) + +; Functions +(constructor_declaration) @local.scope + +(method_declaration) @local.scope + +; definitions +(enum_declaration + name: (identifier) @local.definition.enum) + +(method_declaration + name: (identifier) @local.definition.method) + +(local_variable_declaration + declarator: (variable_declarator + name: (identifier) @local.definition.var)) + +(enhanced_for_statement + name: (identifier) @local.definition.var) + +(formal_parameter + name: (identifier) @local.definition.parameter) + +(catch_formal_parameter + name: (identifier) @local.definition.parameter) + +(field_declaration + declarator: (variable_declarator + name: (identifier) @local.definition.field)) + +; REFERENCES +(identifier) @local.reference + +(type_identifier) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/folds.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/folds.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/highlights.scm new file mode 100644 index 00000000..e6bf1478 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/highlights.scm @@ -0,0 +1,53 @@ +; inherits: cpp + +((identifier) @function.builtin + (#any-of? @function.builtin + ; Digital I/O + "digitalRead" "digitalWrite" "pinMode" + ; Analog I/O + "analogRead" "analogReference" "analogWrite" + ; Zero, Due & MKR Family + "analogReadResolution" "analogWriteResolution" + ; Advanced I/O + "noTone" "pulseIn" "pulseInLong" "shiftIn" "shiftOut" "tone" + ; Time + "delay" "delayMicroseconds" "micros" "millis" + ; Math + "abs" "constrain" "map" "max" "min" "pow" "sq" "sqrt" + ; Trigonometry + "cos" "sin" "tan" + ; Characters + "isAlpha" "isAlphaNumeric" "isAscii" "isControl" "isDigit" "isGraph" "isHexadecimalDigit" + "isLowerCase" "isPrintable" "isPunct" "isSpace" "isUpperCase" "isWhitespace" + ; Random Numbers + "random" "randomSeed" + ; Bits and Bytes + "bit" "bitClear" "bitRead" "bitSet" "bitWrite" "highByte" "lowByte" + ; External Interrupts + "attachInterrupt" "detachInterrupt" + ; Interrupts + "interrupts" "noInterrupts")) + +((identifier) @type.builtin + (#any-of? @type.builtin "Serial" "SPI" "Stream" "Wire" "Keyboard" "Mouse" "String")) + +((identifier) @constant.builtin + (#any-of? @constant.builtin "HIGH" "LOW" "INPUT" "OUTPUT" "INPUT_PULLUP" "LED_BUILTIN")) + +(function_definition + (function_declarator + declarator: (identifier) @function.builtin) + (#any-of? @function.builtin "loop" "setup")) + +(call_expression + function: (primitive_type) @function.builtin) + +(call_expression + function: (identifier) @constructor + (#any-of? @constructor "SPISettings" "String")) + +(declaration + (type_identifier) @type.builtin + (function_declarator + declarator: (identifier) @constructor) + (#eq? @type.builtin "SPISettings")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/indents.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/indents.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/injections.scm new file mode 100644 index 00000000..b637d9b2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/injections.scm @@ -0,0 +1,5 @@ +((preproc_arg) @injection.content + (#set! injection.language "arduino")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/locals.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/arduino/locals.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/asm/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/asm/highlights.scm new file mode 100644 index 00000000..eccf9c99 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/asm/highlights.scm @@ -0,0 +1,66 @@ +; General +(label + [ + (ident) + (word) + ] @label) + +(reg) @variable.builtin + +(meta + kind: (_) @function.builtin) + +(instruction + kind: (_) @function.builtin) + +(const + name: (word) @constant) + +; Comments +[ + (line_comment) + (block_comment) +] @comment @spell + +; Literals +(int) @number + +(float) @number.float + +(string) @string + +; Keywords +[ + "byte" + "word" + "dword" + "qword" + "ptr" + "rel" + "label" + "const" +] @keyword + +; Operators & Punctuation +[ + "+" + "-" + "*" + "/" + "%" + "|" + "^" + "&" +] @operator + +[ + "(" + ")" + "[" + "]" +] @punctuation.bracket + +[ + "," + ":" +] @punctuation.delimiter diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/asm/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/asm/injections.scm new file mode 100644 index 00000000..3cd6aac8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/asm/injections.scm @@ -0,0 +1,5 @@ +([ + (line_comment) + (block_comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/folds.scm new file mode 100644 index 00000000..1f2129cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/folds.scm @@ -0,0 +1 @@ +; inherits: html diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/highlights.scm new file mode 100644 index 00000000..bdaf88e3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/highlights.scm @@ -0,0 +1,21 @@ +; inherits: html + +"---" @punctuation.delimiter + +[ + "{" + "}" +] @punctuation.special + +; custom components get `@type` highlighting +((start_tag + (tag_name) @type) + (#lua-match? @type "^[A-Z]")) + +((end_tag + (tag_name) @type) + (#lua-match? @type "^[A-Z]")) + +((erroneous_end_tag + (erroneous_end_tag_name) @type) + (#lua-match? @type "^[A-Z]")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/indents.scm new file mode 100644 index 00000000..1f2129cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/indents.scm @@ -0,0 +1 @@ +; inherits: html diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/injections.scm new file mode 100644 index 00000000..d4f15ba5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/injections.scm @@ -0,0 +1,32 @@ +; inherits: html_tags + +(frontmatter + (frontmatter_js_block) @injection.content + (#set! injection.language "typescript")) + +(attribute_interpolation + (attribute_js_expr) @injection.content + (#set! injection.language "typescript")) + +(attribute + (attribute_backtick_string) @injection.content + (#set! injection.language "typescript")) + +(html_interpolation + (permissible_text) @injection.content + (#set! injection.language "typescript")) + +(script_element + (raw_text) @injection.content + (#set! injection.language "typescript")) + +(style_element + (start_tag + (attribute + (attribute_name) @_lang_attr + (quoted_attribute_value + (attribute_value) @_lang_value))) + (raw_text) @injection.content + (#eq? @_lang_attr "lang") + (#eq? @_lang_value "scss") + (#set! injection.language "scss")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/locals.scm new file mode 100644 index 00000000..1f2129cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/astro/locals.scm @@ -0,0 +1 @@ +; inherits: html diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/authzed/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/authzed/highlights.scm new file mode 100644 index 00000000..fb946caa --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/authzed/highlights.scm @@ -0,0 +1,60 @@ +(identifier) @function + +(block + (relation + (relation_literal) @function.builtin + (identifier) @constant)) + +(block + (permission + (permission_literal) @variable.builtin + (identifier) @type)) + +; relations +(rel_expression + (identifier) @property) + +(relation + (rel_expression + (hash_literal) + . + (identifier) @constant)) + +; permissions +(perm_expression + (identifier) @property) + +(call_expression + function: (selector_expression + operand: (identifier) @constant + field: (field_identifier) @function.method)) + +(perm_expression + (stabby) @operator + . + (identifier) @function) + +; misc +[ + (plus_literal) + (minus_literal) + (amp_literal) + (pipe_literal) +] @operator + +[ + (true) + (false) +] @boolean + +(nil) @constant.builtin + +[ + (caveat_literal) + (definition_literal) +] @keyword + +[ + (hash_literal) + (comment) +] @comment diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/authzed/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/authzed/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/authzed/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/awk/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/awk/highlights.scm new file mode 100644 index 00000000..904cf2a9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/awk/highlights.scm @@ -0,0 +1,199 @@ +; adapted from https://github.com/Beaglefoot/tree-sitter-awk +[ + (identifier) + (field_ref) +] @variable + +(field_ref + (_) @variable) + +; https://www.gnu.org/software/gawk/manual/html_node/Auto_002dset.html +((identifier) @constant.builtin + (#any-of? @constant.builtin + "ARGC" "ARGV" "ARGIND" "ENVIRON" "ERRNO" "FILENAME" "FNR" "NF" "FUNCTAB" "NR" "PROCINFO" + "RLENGTH" "RSTART" "RT" "SYMTAB")) + +; https://www.gnu.org/software/gawk/manual/html_node/User_002dmodified.html +((identifier) @variable.builtin + (#any-of? @variable.builtin + "BINMODE" "CONVFMT" "FIELDWIDTHS" "FPAT" "FS" "IGNORECASE" "LINT" "OFMT" "OFS" "ORS" "PREC" + "ROUNDMODE" "RS" "SUBSEP" "TEXTDOMAIN")) + +(number) @number + +(string) @string + +(regex) @string.regexp + +(escape_sequence) @string.escape + +(comment) @comment @spell + +((program + . + (comment) @keyword.directive @nospell) + (#lua-match? @keyword.directive "^#!/")) + +(ns_qualified_name + (namespace) @module) + +(ns_qualified_name + "::" @punctuation.delimiter) + +(func_def + name: (_ + (identifier) @function) @function) + +(func_call + name: (_ + (identifier) @function) @function) + +(func_def + (param_list + (identifier) @variable.parameter)) + +[ + "print" + "printf" + "getline" +] @function.builtin + +[ + (delete_statement) + (break_statement) + (continue_statement) + (next_statement) + (nextfile_statement) +] @keyword + +[ + "func" + "function" +] @keyword.function + +[ + "return" + "exit" +] @keyword.return + +[ + "do" + "while" + "for" + "in" +] @keyword.repeat + +[ + "if" + "else" + "switch" + "case" + "default" +] @keyword.conditional + +[ + "@include" + "@load" +] @keyword.import + +"@namespace" @keyword.directive + +[ + "BEGIN" + "END" + "BEGINFILE" + "ENDFILE" +] @label + +(binary_exp + [ + "^" + "**" + "*" + "/" + "%" + "+" + "-" + "<" + ">" + "<=" + ">=" + "==" + "!=" + "~" + "!~" + "in" + "&&" + "||" + ] @operator) + +(unary_exp + [ + "!" + "+" + "-" + ] @operator) + +(assignment_exp + [ + "=" + "+=" + "-=" + "*=" + "/=" + "%=" + "^=" + ] @operator) + +(ternary_exp + [ + "?" + ":" + ] @keyword.conditional.ternary) + +(update_exp + [ + "++" + "--" + ] @operator) + +(redirected_io_statement + [ + ">" + ">>" + ] @operator) + +(piped_io_statement + [ + "|" + "|&" + ] @operator) + +(piped_io_exp + [ + "|" + "|&" + ] @operator) + +(field_ref + "$" @punctuation.delimiter) + +(regex + "/" @punctuation.delimiter) + +(regex_constant + "@" @punctuation.delimiter) + +[ + ";" + "," +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/awk/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/awk/injections.scm new file mode 100644 index 00000000..3e67da24 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/awk/injections.scm @@ -0,0 +1,17 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((regex) @injection.content + (#set! injection.language "regex")) + +((print_statement + (exp_list + . + (string) @injection.content)) + (#set! injection.language "printf")) + +((printf_statement + (exp_list + . + (string) @injection.content)) + (#set! injection.language "printf")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/folds.scm new file mode 100644 index 00000000..766dbe59 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/folds.scm @@ -0,0 +1,9 @@ +[ + (function_definition) + (if_statement) + (case_statement) + (for_statement) + (while_statement) + (c_style_for_statement) + (heredoc_redirect) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/highlights.scm new file mode 100644 index 00000000..0e192bbc --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/highlights.scm @@ -0,0 +1,239 @@ +[ + "(" + ")" + "{" + "}" + "[" + "]" + "[[" + "]]" + "((" + "))" +] @punctuation.bracket + +[ + ";" + ";;" + ";&" + ";;&" + "&" +] @punctuation.delimiter + +[ + ">" + ">>" + "<" + "<<" + "&&" + "|" + "|&" + "||" + "=" + "+=" + "=~" + "==" + "!=" + "&>" + "&>>" + "<&" + ">&" + ">|" + "<&-" + ">&-" + "<<-" + "<<<" + ".." + "!" +] @operator + +; Do *not* spell check strings since they typically have some sort of +; interpolation in them, or, are typically used for things like filenames, URLs, +; flags and file content. +[ + (string) + (raw_string) + (ansi_c_string) + (heredoc_body) +] @string + +[ + (heredoc_start) + (heredoc_end) +] @label + +(variable_assignment + (word) @string) + +(command + argument: "$" @string) ; bare dollar + +(concatenation + (word) @string) + +[ + "if" + "then" + "else" + "elif" + "fi" + "case" + "in" + "esac" +] @keyword.conditional + +[ + "for" + "do" + "done" + "select" + "until" + "while" +] @keyword.repeat + +[ + "declare" + "typeset" + "readonly" + "local" + "unset" + "unsetenv" +] @keyword + +"export" @keyword.import + +"function" @keyword.function + +(special_variable_name) @constant + +; trap -l +((word) @constant.builtin + (#any-of? @constant.builtin + "SIGHUP" "SIGINT" "SIGQUIT" "SIGILL" "SIGTRAP" "SIGABRT" "SIGBUS" "SIGFPE" "SIGKILL" "SIGUSR1" + "SIGSEGV" "SIGUSR2" "SIGPIPE" "SIGALRM" "SIGTERM" "SIGSTKFLT" "SIGCHLD" "SIGCONT" "SIGSTOP" + "SIGTSTP" "SIGTTIN" "SIGTTOU" "SIGURG" "SIGXCPU" "SIGXFSZ" "SIGVTALRM" "SIGPROF" "SIGWINCH" + "SIGIO" "SIGPWR" "SIGSYS" "SIGRTMIN" "SIGRTMIN+1" "SIGRTMIN+2" "SIGRTMIN+3" "SIGRTMIN+4" + "SIGRTMIN+5" "SIGRTMIN+6" "SIGRTMIN+7" "SIGRTMIN+8" "SIGRTMIN+9" "SIGRTMIN+10" "SIGRTMIN+11" + "SIGRTMIN+12" "SIGRTMIN+13" "SIGRTMIN+14" "SIGRTMIN+15" "SIGRTMAX-14" "SIGRTMAX-13" + "SIGRTMAX-12" "SIGRTMAX-11" "SIGRTMAX-10" "SIGRTMAX-9" "SIGRTMAX-8" "SIGRTMAX-7" "SIGRTMAX-6" + "SIGRTMAX-5" "SIGRTMAX-4" "SIGRTMAX-3" "SIGRTMAX-2" "SIGRTMAX-1" "SIGRTMAX")) + +((word) @boolean + (#any-of? @boolean "true" "false")) + +(comment) @comment @spell + +(test_operator) @operator + +(command_substitution + "$(" @punctuation.special + ")" @punctuation.special) + +(process_substitution + [ + "<(" + ">(" + ] @punctuation.special + ")" @punctuation.special) + +(arithmetic_expansion + [ + "$((" + "((" + ] @punctuation.special + "))" @punctuation.special) + +(arithmetic_expansion + "," @punctuation.delimiter) + +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +(binary_expression + operator: _ @operator) + +(unary_expression + operator: _ @operator) + +(postfix_expression + operator: _ @operator) + +(function_definition + name: (word) @function) + +(command_name + (word) @function.call) + +(command_name + (word) @function.builtin + (#any-of? @function.builtin + "alias" "bg" "bind" "break" "builtin" "caller" "cd" "command" "compgen" "complete" "compopt" + "continue" "coproc" "dirs" "disown" "echo" "enable" "eval" "exec" "exit" "fc" "fg" "getopts" + "hash" "help" "history" "jobs" "kill" "let" "logout" "mapfile" "popd" "printf" "pushd" "pwd" + "read" "readarray" "return" "set" "shift" "shopt" "source" "suspend" "test" "time" "times" + "trap" "type" "typeset" "ulimit" "umask" "unalias" "wait")) + +(command + argument: [ + (word) @variable.parameter + (concatenation + (word) @variable.parameter) + ]) + +(declaration_command + (word) @variable.parameter) + +(unset_command + (word) @variable.parameter) + +(number) @number + +((word) @number + (#lua-match? @number "^[0-9]+$")) + +(file_redirect + destination: (word) @variable.parameter) + +(file_descriptor) @operator + +(simple_expansion + "$" @punctuation.special) @none + +(expansion + "${" @punctuation.special + "}" @punctuation.special) @none + +(expansion + operator: _ @punctuation.special) + +(expansion + "@" + . + operator: _ @character.special) + +((expansion + (subscript + index: (word) @character.special)) + (#any-of? @character.special "@" "*")) + +"``" @punctuation.special + +(variable_name) @variable + +((variable_name) @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) + +(case_item + value: (word) @variable.parameter) + +[ + (regex) + (extglob_pattern) +] @string.regexp + +((program + . + (comment) @keyword.directive @nospell) + (#lua-match? @keyword.directive "^#!/")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/injections.scm new file mode 100644 index 00000000..2f385bfd --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/injections.scm @@ -0,0 +1,67 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((regex) @injection.content + (#set! injection.language "regex")) + +((heredoc_redirect + (heredoc_body) @injection.content + (heredoc_end) @injection.language) + (#downcase! @injection.language)) + +; printf 'format' +((command + name: (command_name) @_command + . + argument: [ + (string + (string_content) @injection.content) + (concatenation + (string + (string_content) @injection.content)) + (raw_string) @injection.content + (concatenation + (raw_string) @injection.content) + ]) + (#eq? @_command "printf") + (#set! injection.language "printf")) + +; printf -v var 'format' +((command + name: (command_name) @_command + argument: (word) @_arg + . + (_) + . + argument: [ + (string + (string_content) @injection.content) + (concatenation + (string + (string_content) @injection.content)) + (raw_string) @injection.content + (concatenation + (raw_string) @injection.content) + ]) + (#eq? @_command "printf") + (#eq? @_arg "-v") + (#set! injection.language "printf")) + +; printf -- 'format' +((command + name: (command_name) @_command + argument: (word) @_arg + . + argument: [ + (string + (string_content) @injection.content) + (concatenation + (string + (string_content) @injection.content)) + (raw_string) @injection.content + (concatenation + (raw_string) @injection.content) + ]) + (#eq? @_command "printf") + (#eq? @_arg "--") + (#set! injection.language "printf")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/locals.scm new file mode 100644 index 00000000..347f51fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bash/locals.scm @@ -0,0 +1,14 @@ +; Scopes +(function_definition) @local.scope + +; Definitions +(variable_assignment + name: (variable_name) @local.definition.var) + +(function_definition + name: (word) @local.definition.function) + +; References +(variable_name) @local.reference + +(word) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/folds.scm new file mode 100644 index 00000000..d99e0c1a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/folds.scm @@ -0,0 +1,5 @@ +[ + (list) + (scope) + (cons) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/highlights.scm new file mode 100644 index 00000000..f84993af --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/highlights.scm @@ -0,0 +1,126 @@ +; Variables +(list + (symbol) @variable) + +(cons + (symbol) @variable) + +(scope + (symbol) @variable) + +(symbind + (symbol) @variable) + +; Constants +((symbol) @constant + (#lua-match? @constant "^_*[A-Z][A-Z0-9_]*$")) + +; Functions +(list + . + (symbol) @function) + +; Namespaces +(symbind + (symbol) @module + . + (keyword)) + +; Includes +((symbol) @keyword.import + (#any-of? @keyword.import "use" "import" "load")) + +; Keywords +((symbol) @keyword + (#any-of? @keyword "do" "doc")) + +; Special Functions +; Keywords construct a symbol +(keyword) @constructor + +((list + . + (symbol) @keyword.function + . + (symbol) @function + (symbol)? @variable.parameter) + (#any-of? @keyword.function "def" "defop" "defn" "fn")) + +((cons + . + (symbol) @keyword.function + . + (symbol) @function + (symbol)? @variable.parameter) + (#any-of? @keyword.function "def" "defop" "defn" "fn")) + +((symbol) @function.builtin + (#any-of? @function.builtin + "dump" "mkfs" "json" "log" "error" "now" "cons" "wrap" "unwrap" "eval" "make-scope" "bind" + "meta" "with-meta" "null?" "ignore?" "boolean?" "number?" "string?" "symbol?" "scope?" "sink?" + "source?" "list?" "pair?" "applicative?" "operative?" "combiner?" "path?" "empty?" "thunk?" "+" + "*" "quot" "-" "max" "min" "=" ">" ">=" "<" "<=" "list->source" "across" "emit" "next" + "reduce-kv" "assoc" "symbol->string" "string->symbol" "str" "substring" "trim" "scope->list" + "string->fs-path" "string->cmd-path" "string->dir" "subpath" "path-name" "path-stem" + "with-image" "with-dir" "with-args" "with-cmd" "with-stdin" "with-env" "with-insecure" + "with-label" "with-port" "with-tls" "with-mount" "thunk-cmd" "thunk-args" "resolve" "start" + "addr" "wait" "read" "cache-dir" "binds?" "recall-memo" "store-memo" "mask" "list" "list*" + "first" "rest" "length" "second" "third" "map" "map-pairs" "foldr" "foldl" "append" "filter" + "conj" "list->scope" "merge" "apply" "id" "always" "vals" "keys" "memo" "succeeds?" "run" "last" + "take" "take-all" "insecure!" "from" "cd" "wrap-cmd" "mkfile" "path-base" "not")) + +((symbol) @function.macro + (#any-of? @function.macro + "op" "current-scope" "quote" "let" "provide" "module" "or" "and" "curryfn" "for" "$" "linux")) + +; Conditionals +((symbol) @keyword.conditional + (#any-of? @keyword.conditional "if" "case" "cond" "when")) + +; Repeats +((symbol) @keyword.repeat + (#any-of? @keyword.repeat "each")) + +; Operators +((symbol) @operator + (#any-of? @operator "&" "*" "+" "-" "<" "<=" "=" ">" ">=")) + +; Punctuation +[ + "(" + ")" +] @punctuation.bracket + +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +((symbol) @punctuation.delimiter + (#eq? @punctuation.delimiter "->")) + +; Literals +(string) @string + +(escape_sequence) @string.escape + +(path) @string.special.url + +(number) @number + +(boolean) @boolean + +[ + (ignore) + (null) +] @constant.builtin + +"^" @character.special + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/indents.scm new file mode 100644 index 00000000..27b976f2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/indents.scm @@ -0,0 +1,31 @@ +[ + (list) + (scope) + (cons) +] @indent.begin + +[ + ")" + "}" + "]" +] @indent.end + +[ + "(" + ")" +] @indent.branch + +[ + "{" + "}" +] @indent.branch + +[ + "[" + "]" +] @indent.branch + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/locals.scm new file mode 100644 index 00000000..daed7e5e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bass/locals.scm @@ -0,0 +1,26 @@ +; Scopes +[ + (list) + (scope) + (cons) +] @local.scope + +; References +(symbol) @local.reference + +; Definitions +((list + . + (symbol) @_fnkw + . + (symbol) @local.definition.function + (symbol)? @local.definition.parameter) + (#any-of? @_fnkw "def" "defop" "defn" "fn")) + +((cons + . + (symbol) @_fnkw + . + (symbol) @local.definition.function + (symbol)? @local.definition.parameter) + (#any-of? @_fnkw "def" "defop" "defn" "fn")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/beancount/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/beancount/folds.scm new file mode 100644 index 00000000..9f1b6cbe --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/beancount/folds.scm @@ -0,0 +1,4 @@ +[ + (transaction) + (section) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/beancount/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/beancount/highlights.scm new file mode 100644 index 00000000..1e23d28f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/beancount/highlights.scm @@ -0,0 +1,57 @@ +(date) @variable.member + +(txn) @attribute + +(account) @type + +(amount) @number + +(incomplete_amount) @number + +(compound_amount) @number + +(amount_tolerance) @number + +(currency) @property + +(key) @label + +(string) @string + +(narration) @string @spell + +(payee) @string @spell + +(tag) @constant + +(link) @constant + +[ + (minus) + (plus) + (slash) + (asterisk) +] @operator + +(comment) @comment @spell + +[ + (balance) + (open) + (close) + (commodity) + (pad) + (event) + (price) + (note) + (document) + (query) + (custom) + (pushtag) + (poptag) + (pushmeta) + (popmeta) + (option) + (include) + (plugin) +] @keyword diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/beancount/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/beancount/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/beancount/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bibtex/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bibtex/folds.scm new file mode 100644 index 00000000..321a045c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bibtex/folds.scm @@ -0,0 +1 @@ +(entry) @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bibtex/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bibtex/highlights.scm new file mode 100644 index 00000000..a82b371a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bibtex/highlights.scm @@ -0,0 +1,50 @@ +; CREDITS @pfoerster (adapted from https://github.com/latex-lsp/tree-sitter-bibtex) +[ + (string_type) + (preamble_type) + (entry_type) +] @keyword + +[ + (junk) + (comment) +] @comment + +(comment) @spell + +[ + "=" + "#" +] @operator + +(command) @function.builtin + +(number) @number + +(field + name: (identifier) @variable.member) + +(token + (identifier) @variable.parameter) + +[ + (brace_word) + (quote_word) +] @string + +[ + (key_brace) + (key_paren) +] @string.special.symbol + +(string + name: (identifier) @constant) + +[ + "{" + "}" + "(" + ")" +] @punctuation.bracket + +"," @punctuation.delimiter diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bibtex/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bibtex/indents.scm new file mode 100644 index 00000000..764172a7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bibtex/indents.scm @@ -0,0 +1,8 @@ +(entry) @indent.begin + +[ + "{" + "}" +] @indent.branch + +(comment) @indent.ignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/folds.scm new file mode 100644 index 00000000..217a86d6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/folds.scm @@ -0,0 +1,19 @@ +[ + (module_declaration) + (metadata_declaration) + (output_declaration) + (parameter_declaration) + (resource_declaration) + (type_declaration) + (variable_declaration) + (parenthesized_expression) + (decorators) + (array) + (object) + (if_statement) + (for_statement) + (subscript_expression) + (ternary_expression) + (string) + (comment) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/highlights.scm new file mode 100644 index 00000000..35c9d6c1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/highlights.scm @@ -0,0 +1,234 @@ +; Includes +[ + "import" + "provider" + "with" + "as" + "from" +] @keyword.import + +; Namespaces +(module_declaration + (identifier) @module) + +; Builtins +(primitive_type) @type.builtin + +((member_expression + object: (identifier) @type.builtin) + (#eq? @type.builtin "sys")) + +; Functions +(call_expression + function: (identifier) @function.call) + +(user_defined_function + name: (identifier) @function) + +; Properties +(object_property + (identifier) @property + ":" @punctuation.delimiter + (_)) + +(object_property + (compatible_identifier) @property + ":" @punctuation.delimiter + (_)) + +(property_identifier) @property + +; Attributes +(decorator + "@" @attribute) + +(decorator + (call_expression + (identifier) @attribute)) + +(decorator + (call_expression + (member_expression + object: (identifier) @attribute + property: (property_identifier) @attribute))) + +; Types +(type_declaration + (identifier) @type) + +(type_declaration + (identifier) + "=" + (identifier) @type) + +(type + (identifier) @type) + +(resource_declaration + (identifier) @type) + +(resource_expression + (identifier) @type) + +; Parameters +(parameter_declaration + (identifier) @variable.parameter + (_)) + +(call_expression + function: (_) + (arguments + (identifier) @variable.parameter)) + +(call_expression + function: (_) + (arguments + (member_expression + object: (identifier) @variable.parameter))) + +(parameter + . + (identifier) @variable.parameter) + +; Variables +(variable_declaration + (identifier) @variable + (_)) + +(metadata_declaration + (identifier) @variable + (_)) + +(output_declaration + (identifier) @variable + (_)) + +(object_property + (_) + ":" + (identifier) @variable) + +(for_statement + "for" + (for_loop_parameters + (loop_variable) @variable + (loop_enumerator) @variable)) + +; Conditionals +"if" @keyword.conditional + +(ternary_expression + "?" @keyword.conditional.ternary + ":" @keyword.conditional.ternary) + +; Loops +(for_statement + "for" @keyword.repeat + "in" + ":" @punctuation.delimiter) + +; Keywords +[ + "module" + "metadata" + "output" + "param" + "resource" + "existing" + "targetScope" + "type" + "var" + "using" + "test" +] @keyword + +"func" @keyword.function + +"assert" @keyword.exception + +; Operators +[ + "+" + "-" + "*" + "/" + "%" + "||" + "&&" + "|" + "==" + "!=" + "=~" + "!~" + ">" + ">=" + "<=" + "<" + "??" + "=" + "!" + ".?" +] @operator + +(subscript_expression + "?" @operator) + +(nullable_type + "?" @operator) + +"in" @keyword.operator + +; Literals +(string) @string + +(escape_sequence) @string.escape + +(number) @number + +(boolean) @boolean + +(null) @constant.builtin + +; Misc +(compatible_identifier + "?" @punctuation.special) + +(nullable_return_type) @punctuation.special + +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "." + ":" + "::" + "=>" +] @punctuation.delimiter + +; Interpolation +(interpolation) @none + +(interpolation + "${" @punctuation.special + "}" @punctuation.special) + +(interpolation + (identifier) @variable) + +; Comments +[ + (comment) + (diagnostic_comment) +] @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/indents.scm new file mode 100644 index 00000000..055e51b2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/indents.scm @@ -0,0 +1,27 @@ +[ + (array) + (object) +] @indent.begin + +"}" @indent.end + +[ + "{" + "}" +] @indent.branch + +[ + "[" + "]" +] @indent.branch + +[ + "(" + ")" +] @indent.branch + +[ + (ERROR) + (comment) + (diagnostic_comment) +] @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/injections.scm new file mode 100644 index 00000000..5c2d4a57 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/injections.scm @@ -0,0 +1,5 @@ +([ + (comment) + (diagnostic_comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/locals.scm new file mode 100644 index 00000000..cc9c3c2c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bicep/locals.scm @@ -0,0 +1,73 @@ +; Scopes +[ + (infrastructure) + (call_expression) + (lambda_expression) + (subscript_expression) + (if_statement) + (for_statement) + (array) + (object) + (interpolation) +] @local.scope + +; References +(property_identifier) @local.reference + +(call_expression + (identifier) @local.reference) + +(object_property + (_) + ":" + (identifier) @local.reference) + +(resource_expression + (identifier) @local.reference) + +; Definitions +(type) @local.definition.associated + +(object_property + (identifier) @local.definition.field + (_)) + +(object_property + (compatible_identifier) @local.definition.field + (_)) + +(user_defined_function + name: (identifier) @local.definition.function) + +(module_declaration + (identifier) @local.definition.namespace) + +(parameter_declaration + (identifier) @local.definition.parameter + (_)) + +(parameter + . + (identifier) @local.definition.parameter) + +(type_declaration + (identifier) @local.definition.type + (_)) + +(variable_declaration + (identifier) @local.definition.var + (_)) + +(metadata_declaration + (identifier) @local.definition.var + (_)) + +(output_declaration + (identifier) @local.definition.var + (_)) + +(for_statement + "for" + (for_loop_parameters + (loop_variable) @local.definition.var + (loop_enumerator) @local.definition.var)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/folds.scm new file mode 100644 index 00000000..85d22634 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/folds.scm @@ -0,0 +1,24 @@ +[ + (function_definition) + (anonymous_python_function) + (python_function_definition) + (while_statement) + (for_statement) + (if_statement) + (with_statement) + (try_statement) + (import_from_statement) + (parameters) + (argument_list) + (parenthesized_expression) + (generator_expression) + (list_comprehension) + (set_comprehension) + (dictionary_comprehension) + (tuple) + (list) + (set) + (dictionary) + (string) + (python_string) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/highlights.scm new file mode 100644 index 00000000..4b03fbc2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/highlights.scm @@ -0,0 +1,407 @@ +; Includes +[ + "inherit" + "include" + "require" + "export" + "import" +] @keyword.import + +; Keywords +[ + "unset" + "EXPORT_FUNCTIONS" + "python" + "assert" + "exec" + "global" + "nonlocal" + "pass" + "print" + "with" + "as" +] @keyword + +[ + "async" + "await" +] @keyword.coroutine + +[ + "return" + "yield" +] @keyword.return + +(yield + "from" @keyword.return) + +(future_import_statement + "from" @keyword.import + "__future__" @constant.builtin) + +(import_from_statement + "from" @keyword.import) + +"import" @keyword.import + +(aliased_import + "as" @keyword.import) + +[ + "if" + "elif" + "else" +] @keyword.conditional + +[ + "for" + "while" + "break" + "continue" +] @keyword.repeat + +[ + "try" + "except" + "except*" + "raise" + "finally" +] @keyword.exception + +(raise_statement + "from" @keyword.exception) + +(try_statement + (else_clause + "else" @keyword.exception)) + +[ + "addtask" + "deltask" + "addhandler" + "def" + "lambda" +] @keyword.function + +[ + "before" + "after" +] @keyword.modifier + +[ + "append" + "prepend" + "remove" +] @keyword.modifier + +; Variables +[ + (identifier) + (python_identifier) +] @variable + +[ + "noexec" + "INHERIT" + "OVERRIDES" + "$BB_ENV_PASSTHROUGH" + "$BB_ENV_PASSTHROUGH_ADDITIONS" +] @variable.builtin + +; Reset highlighting in f-string interpolations +(interpolation) @none + +; Identifier naming conventions +((python_identifier) @type + (#lua-match? @type "^[A-Z].*[a-z]")) + +([ + (identifier) + (python_identifier) +] @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) + +((python_identifier) @constant.builtin + (#lua-match? @constant.builtin "^__[a-zA-Z0-9_]*__$")) + +((python_identifier) @constant.builtin + (#any-of? @constant.builtin + ; https://docs.python.org/3/library/constants.html + "NotImplemented" "Ellipsis" "quit" "exit" "copyright" "credits" "license")) + +((assignment + left: (python_identifier) @type.definition + (type + (python_identifier) @_annotation)) + (#eq? @_annotation "TypeAlias")) + +((assignment + left: (python_identifier) @type.definition + right: (call + function: (python_identifier) @_func)) + (#any-of? @_func "TypeVar" "NewType")) + +; Fields +(flag) @variable.member + +((attribute + attribute: (python_identifier) @variable.member) + (#lua-match? @variable.member "^[%l_].*$")) + +; Functions +(call + function: (python_identifier) @function.call) + +(call + function: (attribute + attribute: (python_identifier) @function.method.call)) + +((call + function: (python_identifier) @constructor) + (#lua-match? @constructor "^%u")) + +((call + function: (attribute + attribute: (python_identifier) @constructor)) + (#lua-match? @constructor "^%u")) + +((call + function: (python_identifier) @function.builtin) + (#any-of? @function.builtin + "abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray" "bytes" "callable" "chr" + "classmethod" "compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec" + "filter" "float" "format" "frozenset" "getattr" "globals" "hasattr" "hash" "help" "hex" "id" + "input" "int" "isinstance" "issubclass" "iter" "len" "list" "locals" "map" "max" "memoryview" + "min" "next" "object" "oct" "open" "ord" "pow" "print" "property" "range" "repr" "reversed" + "round" "set" "setattr" "slice" "sorted" "staticmethod" "str" "sum" "super" "tuple" "type" + "vars" "zip" "__import__")) + +(python_function_definition + name: (python_identifier) @function) + +(type + (python_identifier) @type) + +(type + (subscript + (python_identifier) @type)) ; type subscript: Tuple[int] + +((call + function: (python_identifier) @_isinstance + arguments: (argument_list + (_) + (python_identifier) @type)) + (#eq? @_isinstance "isinstance")) + +(anonymous_python_function + (identifier) @function) + +(function_definition + (identifier) @function) + +(addtask_statement + (identifier) @function) + +(deltask_statement + (identifier) @function) + +(export_functions_statement + (identifier) @function) + +(addhandler_statement + (identifier) @function) + +(python_function_definition + body: (block + . + (expression_statement + (python_string) @string.documentation @spell))) + +; Namespace +(inherit_path) @module + +; Normal parameters +(parameters + (python_identifier) @variable.parameter) + +; Lambda parameters +(lambda_parameters + (python_identifier) @variable.parameter) + +(lambda_parameters + (tuple_pattern + (python_identifier) @variable.parameter)) + +; Default parameters +(keyword_argument + name: (python_identifier) @variable.parameter) + +; Naming parameters on call-site +(default_parameter + name: (python_identifier) @variable.parameter) + +(typed_parameter + (python_identifier) @variable.parameter) + +(typed_default_parameter + (python_identifier) @variable.parameter) + +; Variadic parameters *args, **kwargs +(parameters + (list_splat_pattern + ; *args + (python_identifier) @variable.parameter)) + +(parameters + (dictionary_splat_pattern + ; **kwargs + (python_identifier) @variable.parameter)) + +; Literals +(none) @constant.builtin + +[ + (true) + (false) +] @boolean + +((python_identifier) @variable.builtin + (#any-of? @variable.builtin "self" "cls")) + +(integer) @number + +(float) @number.float + +; Operators +[ + "?=" + "??=" + ":=" + "=+" + ".=" + "=." + "-" + "-=" + ":=" + "!=" + "*" + "**" + "**=" + "*=" + "/" + "//" + "//=" + "/=" + "&" + "&=" + "%" + "%=" + "^" + "^=" + "+" + "+=" + "<" + "<<" + "<<=" + "<=" + "<>" + "=" + "==" + ">" + ">=" + ">>" + ">>=" + "@" + "@=" + "|" + "|=" + "~" + "->" +] @operator + +[ + "and" + "in" + "is" + "not" + "or" + "is not" + "not in" + "del" +] @keyword.operator + +; Literals +[ + (string) + (python_string) + "\"" +] @string + +(include_path) @string.special.path + +[ + (escape_sequence) + (escape_interpolation) +] @string.escape + +; Punctuation +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + ":" + "->" + ";" + "." + "," + (ellipsis) +] @punctuation.delimiter + +(variable_expansion + [ + "${" + "}" + ] @punctuation.special) + +(inline_python + [ + "${@" + "}" + ] @punctuation.special) + +(interpolation + "{" @punctuation.special + "}" @punctuation.special) + +(type_conversion) @function.macro + +([ + (identifier) + (python_identifier) +] @type.builtin + (#any-of? @type.builtin + ; https://docs.python.org/3/library/exceptions.html + "BaseException" "Exception" "ArithmeticError" "BufferError" "LookupError" "AssertionError" + "AttributeError" "EOFError" "FloatingPointError" "GeneratorExit" "ImportError" + "ModuleNotFoundError" "IndexError" "KeyError" "KeyboardInterrupt" "MemoryError" "NameError" + "NotImplementedError" "OSError" "OverflowError" "RecursionError" "ReferenceError" "RuntimeError" + "StopIteration" "StopAsyncIteration" "SyntaxError" "IndentationError" "TabError" "SystemError" + "SystemExit" "TypeError" "UnboundLocalError" "UnicodeError" "UnicodeEncodeError" + "UnicodeDecodeError" "UnicodeTranslateError" "ValueError" "ZeroDivisionError" "EnvironmentError" + "IOError" "WindowsError" "BlockingIOError" "ChildProcessError" "ConnectionError" + "BrokenPipeError" "ConnectionAbortedError" "ConnectionRefusedError" "ConnectionResetError" + "FileExistsError" "FileNotFoundError" "InterruptedError" "IsADirectoryError" + "NotADirectoryError" "PermissionError" "ProcessLookupError" "TimeoutError" "Warning" + "UserWarning" "DeprecationWarning" "PendingDeprecationWarning" "SyntaxWarning" "RuntimeWarning" + "FutureWarning" "ImportWarning" "UnicodeWarning" "BytesWarning" "ResourceWarning" + ; https://docs.python.org/3/library/stdtypes.html + "bool" "int" "float" "complex" "list" "tuple" "range" "str" "bytes" "bytearray" "memoryview" + "set" "frozenset" "dict" "type" "object")) + +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/indents.scm new file mode 100644 index 00000000..5f208186 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/indents.scm @@ -0,0 +1,172 @@ +[ + (import_from_statement) + (parenthesized_expression) + (generator_expression) + (list_comprehension) + (set_comprehension) + (dictionary_comprehension) + (tuple_pattern) + (list_pattern) + (binary_operator) + (lambda) + (concatenated_string) +] @indent.begin + +((list) @indent.align + (#set! indent.open_delimiter "[") + (#set! indent.close_delimiter "]")) + +((dictionary) @indent.align + (#set! indent.open_delimiter "{") + (#set! indent.close_delimiter "}")) + +((set) @indent.align + (#set! indent.open_delimiter "{") + (#set! indent.close_delimiter "}")) + +((for_statement) @indent.begin + (#set! indent.immediate 1)) + +((if_statement) @indent.begin + (#set! indent.immediate 1)) + +((while_statement) @indent.begin + (#set! indent.immediate 1)) + +((try_statement) @indent.begin + (#set! indent.immediate 1)) + +(ERROR + "try" + ":" @indent.begin + (#set! indent.immediate 1)) + +((python_function_definition) @indent.begin + (#set! indent.immediate 1)) + +(function_definition) @indent.begin + +(anonymous_python_function) @indent.begin + +((with_statement) @indent.begin + (#set! indent.immediate 1)) + +(if_statement + condition: (parenthesized_expression) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")") + (#set! indent.avoid_last_matching_next 1)) + +(while_statement + condition: (parenthesized_expression) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")") + (#set! indent.avoid_last_matching_next 1)) + +(ERROR + "(" @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")") + . + (_)) + +((argument_list) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) + +((parameters) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")") + (#set! indent.avoid_last_matching_next 1)) + +((tuple) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) + +(ERROR + "[" @indent.align + (#set! indent.open_delimiter "[") + (#set! indent.close_delimiter "]") + . + (_)) + +(ERROR + "{" @indent.align + (#set! indent.open_delimiter "{") + (#set! indent.close_delimiter "}") + . + (_)) + +[ + (break_statement) + (continue_statement) +] @indent.dedent + +(ERROR + (_) @indent.branch + ":" + . + (#lua-match? @indent.branch "^else")) + +(ERROR + (_) @indent.branch @indent.dedent + ":" + . + (#lua-match? @indent.branch "^elif")) + +(parenthesized_expression + ")" @indent.end) + +(generator_expression + ")" @indent.end) + +(list_comprehension + "]" @indent.end) + +(set_comprehension + "}" @indent.end) + +(dictionary_comprehension + "}" @indent.end) + +(tuple_pattern + ")" @indent.end) + +(list_pattern + "]" @indent.end) + +(function_definition + "}" @indent.end) + +(anonymous_python_function + "}" @indent.end) + +(return_statement + [ + (_) @indent.end + (_ + [ + (_) + ")" + "}" + "]" + ] @indent.end .) + (attribute + attribute: (_) @indent.end) + (call + arguments: (_ + ")" @indent.end)) + "return" @indent.end + ] .) + +[ + ")" + "]" + "}" + (elif_clause) + (else_clause) + (except_clause) + (finally_clause) +] @indent.branch + +(string) @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/injections.scm new file mode 100644 index 00000000..35c984a5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/injections.scm @@ -0,0 +1,15 @@ +(call + function: (attribute + object: (python_identifier) @_re) + arguments: (argument_list + (python_string + (string_content) @injection.content) @_string) + (#eq? @_re "re") + (#lua-match? @_string "^r.*") + (#set! injection.language "regex")) + +((shell_content) @injection.content + (#set! injection.language "bash")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/locals.scm new file mode 100644 index 00000000..e4726ec5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bitbake/locals.scm @@ -0,0 +1,117 @@ +; References +[ + (python_identifier) + (identifier) +] @local.reference + +; Imports +(aliased_import + alias: (python_identifier) @local.definition.import) + +(import_statement + name: (dotted_name + (python_identifier) @local.definition.import)) + +(import_from_statement + name: (dotted_name + (python_identifier) @local.definition.import)) + +; Function with parameters, defines parameters +(parameters + (python_identifier) @local.definition.parameter) + +(default_parameter + (python_identifier) @local.definition.parameter) + +(typed_parameter + (python_identifier) @local.definition.parameter) + +(typed_default_parameter + (python_identifier) @local.definition.parameter) + +; *args parameter +(parameters + (list_splat_pattern + (python_identifier) @local.definition.parameter)) + +; **kwargs parameter +(parameters + (dictionary_splat_pattern + (python_identifier) @local.definition.parameter)) + +; Function defines function and scope +((python_function_definition + name: (python_identifier) @local.definition.function) @local.scope + (#set! definition.function.scope "parent")) + +(function_definition + (identifier) @local.definition.function) + +(anonymous_python_function + (identifier) @local.definition.function) + +; Loops +; not a scope! +(for_statement + left: (pattern_list + (python_identifier) @local.definition.var)) + +(for_statement + left: (tuple_pattern + (python_identifier) @local.definition.var)) + +(for_statement + left: (python_identifier) @local.definition.var) + +; not a scope! +;(while_statement) @local.scope +; for in list comprehension +(for_in_clause + left: (python_identifier) @local.definition.var) + +(for_in_clause + left: (tuple_pattern + (python_identifier) @local.definition.var)) + +(for_in_clause + left: (pattern_list + (python_identifier) @local.definition.var)) + +(dictionary_comprehension) @local.scope + +(list_comprehension) @local.scope + +(set_comprehension) @local.scope + +; Assignments +(assignment + left: (python_identifier) @local.definition.var) + +(assignment + left: (pattern_list + (python_identifier) @local.definition.var)) + +(assignment + left: (tuple_pattern + (python_identifier) @local.definition.var)) + +(assignment + left: (attribute + (python_identifier) + (python_identifier) @local.definition.field)) + +(variable_assignment + (identifier) + operator: [ + "=" + "?=" + "??=" + ":=" + ] @local.definition.var) + +; Walrus operator x := 1 +(named_expression + (python_identifier) @local.definition.var) + +(as_pattern + alias: (as_pattern_target) @local.definition.var) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/blueprint/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/blueprint/highlights.scm new file mode 100644 index 00000000..f3c39f23 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/blueprint/highlights.scm @@ -0,0 +1,75 @@ +(object_id) @variable + +(string) @string + +(escape_sequence) @string.escape + +(comment) @comment @spell + +(constant) @constant.builtin + +(boolean) @boolean + +(using) @keyword.import + +(template) @keyword + +(decorator) @attribute + +(property_definition + (property_name) @property) + +(object) @type + +(signal_binding + (signal_name) @function.builtin) + +(signal_binding + (function + (identifier)) @function) + +(signal_binding + "swapped" @keyword) + +(styles_list + "styles" @function.macro) + +(layout_definition + "layout" @function.macro) + +(gettext_string + "_" @function.builtin) + +(menu_definition + "menu" @keyword) + +(menu_section + "section" @keyword) + +(menu_item + "item" @function.macro) + +(import_statement + (gobject_library) @module) + +(import_statement + (version_number) @number.float) + +(float) @number.float + +(number) @number + +[ + ";" + "." + "," +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/folds.scm new file mode 100644 index 00000000..c40ea3df --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/folds.scm @@ -0,0 +1,6 @@ +[ + (list_expression) + (map_expression) + (module) + (select_expression) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/highlights.scm new file mode 100644 index 00000000..d18e6eea --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/highlights.scm @@ -0,0 +1,55 @@ +(comment) @comment @spell + +(operator) @operator + +(integer_literal + "-" @operator) + +[ + "," + ":" +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(boolean_literal) @boolean + +(integer_literal) @number + +[ + (raw_string_literal) + (interpreted_string_literal) +] @string + +(escape_sequence) @string.escape + +(identifier) @variable + +(module + type: (identifier) @function.call) + +(module + (property + field: (identifier) @variable.parameter)) + +[ + (unset) + (default) +] @variable.builtin + +(condition + name: (identifier) @function.builtin) + +(map_expression + (property + field: (identifier) @property)) + +(select_expression + "select" @keyword.conditional) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/indents.scm new file mode 100644 index 00000000..8cf8adc8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/indents.scm @@ -0,0 +1,38 @@ +(list_expression) @indent.begin + +(list_expression + "]" @indent.branch) + +(map_expression) @indent.begin + +(map_expression + "}" @indent.branch) + +(select_expression) @indent.begin + +(select_expression + ")" @indent.branch) + +(select_value) @indent.begin + +(select_value + ")" @indent.branch) + +(select_pattern + "(" @indent.begin) + +(select_pattern + ")" @indent.branch) + +(select_cases) @indent.begin + +(select_cases + "}" @indent.branch) + +(module) @indent.begin + +(module + ")" @indent.branch) + +(module + "}" @indent.branch) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/locals.scm new file mode 100644 index 00000000..66b444fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/bp/locals.scm @@ -0,0 +1,12 @@ +(module + (property + field: (identifier) @local.definition.parameter)) + +(map_expression + (property + field: (identifier) @local.definition.field)) + +(assignment + left: (identifier) @local.definition.var) + +(identifier) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/folds.scm new file mode 100644 index 00000000..bb26a62e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/folds.scm @@ -0,0 +1,23 @@ +[ + (for_statement) + (if_statement) + (while_statement) + (do_statement) + (switch_statement) + (case_statement) + (function_definition) + (struct_specifier) + (enum_specifier) + (comment) + (preproc_if) + (preproc_elif) + (preproc_else) + (preproc_ifdef) + (preproc_function_def) + (initializer_list) + (gnu_asm_expression) + (preproc_include)+ +] @fold + +(compound_statement + (compound_statement) @fold) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/highlights.scm new file mode 100644 index 00000000..170937c8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/highlights.scm @@ -0,0 +1,332 @@ +; Lower priority to prefer @variable.parameter when identifier appears in parameter_declaration. +((identifier) @variable + (#set! "priority" 95)) + +(preproc_def + (preproc_arg) @variable) + +[ + "default" + "goto" + "asm" + "__asm__" +] @keyword + +[ + "enum" + "struct" + "union" + "typedef" +] @keyword.type + +[ + "sizeof" + "offsetof" +] @keyword.operator + +(alignof_expression + . + _ @keyword.operator) + +"return" @keyword.return + +[ + "while" + "for" + "do" + "continue" + "break" +] @keyword.repeat + +[ + "if" + "else" + "case" + "switch" +] @keyword.conditional + +[ + "#if" + "#ifdef" + "#ifndef" + "#else" + "#elif" + "#endif" + "#elifdef" + "#elifndef" + (preproc_directive) +] @keyword.directive + +"#define" @keyword.directive.define + +"#include" @keyword.import + +[ + ";" + ":" + "," + "::" +] @punctuation.delimiter + +"..." @punctuation.special + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + "=" + "-" + "*" + "/" + "+" + "%" + "~" + "|" + "&" + "^" + "<<" + ">>" + "->" + "." + "<" + "<=" + ">=" + ">" + "==" + "!=" + "!" + "&&" + "||" + "-=" + "+=" + "*=" + "/=" + "%=" + "|=" + "&=" + "^=" + ">>=" + "<<=" + "--" + "++" +] @operator + +; Make sure the comma operator is given a highlight group after the comma +; punctuator so the operator is highlighted properly. +(comma_expression + "," @operator) + +[ + (true) + (false) +] @boolean + +(conditional_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +(string_literal) @string + +(system_lib_string) @string + +(escape_sequence) @string.escape + +(null) @constant.builtin + +(number_literal) @number + +(char_literal) @character + +(preproc_defined) @function.macro + +((field_expression + (field_identifier) @property) @_parent + (#not-has-parent? @_parent template_method function_declarator call_expression)) + +(field_designator) @property + +((field_identifier) @property + (#has-ancestor? @property field_declaration) + (#not-has-ancestor? @property function_declarator)) + +(statement_identifier) @label + +(declaration + type: (type_identifier) @_type + declarator: (identifier) @label + (#eq? @_type "__label__")) + +[ + (type_identifier) + (type_descriptor) +] @type + +(storage_class_specifier) @keyword.modifier + +[ + (type_qualifier) + (gnu_asm_qualifier) + "__extension__" +] @keyword.modifier + +(linkage_specification + "extern" @keyword.modifier) + +(type_definition + declarator: (type_identifier) @type.definition) + +(primitive_type) @type.builtin + +(sized_type_specifier + _ @type.builtin + type: _?) + +((identifier) @constant + (#lua-match? @constant "^[A-Z][A-Z0-9_]+$")) + +(preproc_def + (preproc_arg) @constant + (#lua-match? @constant "^[A-Z][A-Z0-9_]+$")) + +(enumerator + name: (identifier) @constant) + +(case_statement + value: (identifier) @constant) + +((identifier) @constant.builtin + ; format-ignore + (#any-of? @constant.builtin + "stderr" "stdin" "stdout" + "__FILE__" "__LINE__" "__DATE__" "__TIME__" + "__STDC__" "__STDC_VERSION__" "__STDC_HOSTED__" + "__cplusplus" "__OBJC__" "__ASSEMBLER__" + "__BASE_FILE__" "__FILE_NAME__" "__INCLUDE_LEVEL__" + "__TIMESTAMP__" "__clang__" "__clang_major__" + "__clang_minor__" "__clang_patchlevel__" + "__clang_version__" "__clang_literal_encoding__" + "__clang_wide_literal_encoding__" + "__FUNCTION__" "__func__" "__PRETTY_FUNCTION__" + "__VA_ARGS__" "__VA_OPT__")) + +(preproc_def + (preproc_arg) @constant.builtin + ; format-ignore + (#any-of? @constant.builtin + "stderr" "stdin" "stdout" + "__FILE__" "__LINE__" "__DATE__" "__TIME__" + "__STDC__" "__STDC_VERSION__" "__STDC_HOSTED__" + "__cplusplus" "__OBJC__" "__ASSEMBLER__" + "__BASE_FILE__" "__FILE_NAME__" "__INCLUDE_LEVEL__" + "__TIMESTAMP__" "__clang__" "__clang_major__" + "__clang_minor__" "__clang_patchlevel__" + "__clang_version__" "__clang_literal_encoding__" + "__clang_wide_literal_encoding__" + "__FUNCTION__" "__func__" "__PRETTY_FUNCTION__" + "__VA_ARGS__" "__VA_OPT__")) + +(attribute_specifier + (argument_list + (identifier) @variable.builtin)) + +(attribute_specifier + (argument_list + (call_expression + function: (identifier) @variable.builtin))) + +((call_expression + function: (identifier) @function.builtin) + (#lua-match? @function.builtin "^__builtin_")) + +((call_expression + function: (identifier) @function.builtin) + (#has-ancestor? @function.builtin attribute_specifier)) + +; Preproc def / undef +(preproc_def + name: (_) @constant) + +(preproc_call + directive: (preproc_directive) @_u + argument: (_) @constant + (#eq? @_u "#undef")) + +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (field_expression + field: (field_identifier) @function.call)) + +(function_declarator + declarator: (identifier) @function) + +(function_declarator + declarator: (parenthesized_declarator + (pointer_declarator + declarator: (field_identifier) @function))) + +(preproc_function_def + name: (identifier) @function.macro) + +(comment) @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$")) + +; Parameters +(parameter_declaration + declarator: (identifier) @variable.parameter) + +(parameter_declaration + declarator: (array_declarator) @variable.parameter) + +(parameter_declaration + declarator: (pointer_declarator) @variable.parameter) + +; K&R functions +; To enable support for K&R functions, +; add the following lines to your own query config and uncomment them. +; They are commented out as they'll conflict with C++ +; Note that you'll need to have `; extends` at the top of your query file. +; +; (parameter_list (identifier) @variable.parameter) +; +; (function_definition +; declarator: _ +; (declaration +; declarator: (identifier) @variable.parameter)) +; +; (function_definition +; declarator: _ +; (declaration +; declarator: (array_declarator) @variable.parameter)) +; +; (function_definition +; declarator: _ +; (declaration +; declarator: (pointer_declarator) @variable.parameter)) +(preproc_params + (identifier) @variable.parameter) + +[ + "__attribute__" + "__declspec" + "__based" + "__cdecl" + "__clrcall" + "__stdcall" + "__fastcall" + "__thiscall" + "__vectorcall" + (ms_pointer_modifier) + (attribute_declaration) +] @attribute diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/indents.scm new file mode 100644 index 00000000..372a26c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/indents.scm @@ -0,0 +1,99 @@ +[ + (compound_statement) + (field_declaration_list) + (case_statement) + (enumerator_list) + (compound_literal_expression) + (initializer_list) + (init_declarator) +] @indent.begin + +; With current indent logic, if we capture expression_statement with @indent.begin +; It will be affected by _parent_ node with error subnodes deep down the tree +; So narrow indent capture to check for error inside expression statement only, +(expression_statement + (_) @indent.begin + ";" @indent.end) + +(ERROR + "for" + "(" @indent.begin + ";" + ";" + ")" @indent.end) + +((for_statement + body: (_) @_body) @indent.begin + (#not-has-type? @_body compound_statement)) + +(while_statement + condition: (_) @indent.begin) + +((while_statement + body: (_) @_body) @indent.begin + (#not-has-type? @_body compound_statement)) + +((if_statement) + . + (ERROR + "else" @indent.begin)) + +(if_statement + condition: (_) @indent.begin) + +; Supports if without braces (but not both if-else without braces) +(if_statement + consequence: (_ + ";" @indent.end) @_consequence + (#not-has-type? @_consequence compound_statement) + alternative: (else_clause + "else" @indent.branch + [ + (if_statement + (compound_statement) @indent.dedent)? @indent.dedent + (compound_statement)? @indent.dedent + (_)? @indent.dedent + ])?) @indent.begin + +(else_clause + (_ + . + "{" @indent.branch)) + +(compound_statement + "}" @indent.end) + +[ + ")" + "}" + (statement_identifier) +] @indent.branch + +[ + "#define" + "#ifdef" + "#ifndef" + "#elif" + "#if" + "#else" + "#endif" +] @indent.zero + +[ + (preproc_arg) + (string_literal) +] @indent.ignore + +((ERROR + (parameter_declaration)) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) + +([ + (argument_list) + (parameter_list) +] @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) + +(comment) @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/injections.scm new file mode 100644 index 00000000..77b4d7a8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c/injections.scm @@ -0,0 +1,128 @@ +((preproc_arg) @injection.content + (#set! injection.language "c")) + +((comment) @injection.content + (#set! injection.language "comment")) + +((comment) @injection.content + (#match? @injection.content "/\\*!([a-zA-Z]+:)?re2c") + (#set! injection.language "re2c")) + +((comment) @injection.content + (#lua-match? @injection.content "/[*\/][!*\/]" + ">=" + "=" + "-=" + "+=" + "*=" + "/=" + "%=" + "^" + "^=" + "&=" + "|=" + "~" + ">>" + ">>>" + "<<" + "<<=" + ">>=" + ">>>=" + "=>" + "??" + "??=" + ".." +] @operator + +[ + ";" + "." + "," + ":" +] @punctuation.delimiter + +(conditional_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +[ + "[" + "]" + "{" + "}" + "(" + ")" +] @punctuation.bracket + +(interpolation_brace) @punctuation.special + +(type_argument_list + [ + "<" + ">" + ] @punctuation.bracket) + +[ + "using" + "as" +] @keyword.import + +(alias_qualified_name + (identifier + "global") @keyword.import) + +[ + "with" + "new" + "typeof" + "sizeof" + "is" + "and" + "or" + "not" + "stackalloc" + "__makeref" + "__reftype" + "__refvalue" + "in" + "out" + "ref" +] @keyword.operator + +[ + "lock" + "params" + "operator" + "default" + "implicit" + "explicit" + "override" + "get" + "set" + "init" + "where" + "add" + "remove" + "checked" + "unchecked" + "fixed" + "alias" + "file" + "unsafe" +] @keyword + +(attribute_target_specifier + . + _ @keyword) + +[ + "enum" + "record" + "class" + "struct" + "interface" + "namespace" + "event" + "delegate" +] @keyword.type + +[ + "async" + "await" +] @keyword.coroutine + +[ + "const" + "extern" + "readonly" + "static" + "volatile" + "required" + "managed" + "unmanaged" + "notnull" + "abstract" + "private" + "protected" + "internal" + "public" + "partial" + "sealed" + "virtual" + "global" +] @keyword.modifier + +(scoped_type + "scoped" @keyword.modifier) + +(query_expression + (_ + [ + "from" + "orderby" + "select" + "group" + "by" + "ascending" + "descending" + "equals" + "let" + ] @keyword)) + +[ + "return" + "yield" +] @keyword.return diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c_sharp/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c_sharp/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c_sharp/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c_sharp/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c_sharp/locals.scm new file mode 100644 index 00000000..bef09400 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/c_sharp/locals.scm @@ -0,0 +1,42 @@ +; Definitions +(variable_declarator + . + (identifier) @local.definition.var) + +(variable_declarator + (tuple_pattern + (identifier) @local.definition.var)) + +(declaration_expression + name: (identifier) @local.definition.var) + +(foreach_statement + left: (identifier) @local.definition.var) + +(foreach_statement + left: (tuple_pattern + (identifier) @local.definition.var)) + +(parameter + (identifier) @local.definition.parameter) + +(method_declaration + name: (identifier) @local.definition.method) + +(local_function_statement + name: (identifier) @local.definition.method) + +(property_declaration + name: (identifier) @local.definition) + +(type_parameter + (identifier) @local.definition.type) + +(class_declaration + name: (identifier) @local.definition) + +; References +(identifier) @local.reference + +; Scope +(block) @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/folds.scm new file mode 100644 index 00000000..9937da6f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/folds.scm @@ -0,0 +1,26 @@ +[ + (mod_item) + (struct_item) + (trait_item) + (enum_item) + (impl_item) + (type_item) + (use_declaration) + (let_declaration) + (namespace_definition) + (arguments) + (implicit_arguments) + (tuple_type) + (import_statement) + (attribute_statement) + (with_statement) + (if_statement) + (function_definition) + (struct_definition) + (loop_expression) + (if_expression) + (match_expression) + (call_expression) + (tuple_expression) + (attribute_item) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/highlights.scm new file mode 100644 index 00000000..1ea6245d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/highlights.scm @@ -0,0 +1,414 @@ +; Preproc +[ + "%builtins" + "%lang" +] @keyword.directive + +; Includes +(import_statement + [ + "from" + "import" + ] @keyword.import + module_name: (dotted_name + (identifier) @module .)) + +[ + "as" + "use" + "mod" +] @keyword.import + +; Variables +(identifier) @variable + +; Namespaces +(namespace_definition + (identifier) @module) + +(mod_item + name: (identifier) @module) + +(use_list + (self) @module) + +(scoped_use_list + (self) @module) + +(scoped_identifier + path: (identifier) @module) + +(scoped_identifier + (scoped_identifier + name: (identifier) @module)) + +(scoped_type_identifier + path: (identifier) @module) + +((scoped_identifier + path: (identifier) @type) + (#lua-match? @type "^[A-Z]")) + +((scoped_identifier + name: (identifier) @type) + (#lua-match? @type "^[A-Z]")) + +((scoped_identifier + name: (identifier) @constant) + (#lua-match? @constant "^[A-Z][A-Z%d_]*$")) + +((scoped_identifier + path: (identifier) @type + name: (identifier) @constant) + (#lua-match? @type "^[A-Z]") + (#lua-match? @constant "^[A-Z]")) + +((scoped_type_identifier + path: (identifier) @type + name: (type_identifier) @constant) + (#lua-match? @type "^[A-Z]") + (#lua-match? @constant "^[A-Z]")) + +(scoped_use_list + path: (identifier) @module) + +(scoped_use_list + path: (scoped_identifier + (identifier) @module)) + +(use_list + (scoped_identifier + (identifier) @module + . + (_))) + +(use_list + (identifier) @type + (#lua-match? @type "^[A-Z]")) + +(use_as_clause + alias: (identifier) @type + (#lua-match? @type "^[A-Z]")) + +; Keywords +[ + ; 0.x + "using" + "let" + "const" + "local" + "rel" + "abs" + "dw" + "alloc_locals" + (inst_ret) + "with_attr" + "with" + "call" + "nondet" + ; 1.0 + "impl" + "implicits" + "of" + "ref" + "mut" +] @keyword + +[ + "struct" + "enum" + "namespace" + "type" + "trait" +] @keyword.type + +[ + "func" + "fn" + "end" +] @keyword.function + +"return" @keyword.return + +[ + "cast" + "new" + "and" +] @keyword.operator + +[ + "tempvar" + "extern" +] @keyword.modifier + +[ + "if" + "else" + "match" +] @keyword.conditional + +"loop" @keyword.repeat + +[ + "assert" + "static_assert" + "nopanic" +] @keyword.exception + +; Fields +(implicit_arguments + (typed_identifier + (identifier) @variable.member)) + +(member_expression + "." + (identifier) @variable.member) + +(call_expression + (assignment_expression + left: (identifier) @variable.member)) + +(tuple_expression + (assignment_expression + left: (identifier) @variable.member)) + +(field_identifier) @variable.member + +(shorthand_field_initializer + (identifier) @variable.member) + +; Parameters +(arguments + (typed_identifier + (identifier) @variable.parameter)) + +(call_expression + (tuple_expression + (assignment_expression + left: (identifier) @variable.parameter))) + +(return_type + (tuple_type + (named_type + . + (identifier) @variable.parameter))) + +(parameter + (identifier) @variable.parameter) + +; Builtins +(builtin_directive + (identifier) @variable.builtin) + +(lang_directive + (identifier) @variable.builtin) + +[ + "ap" + "fp" + (self) +] @variable.builtin + +; Functions +(function_definition + "func" + (identifier) @function) + +(function_definition + "fn" + (identifier) @function) + +(function_signature + "fn" + (identifier) @function) + +(extern_function_statement + (identifier) @function) + +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (scoped_identifier + (identifier) @function.call .)) + +(call_expression + function: (field_expression + field: (field_identifier) @function.call)) + +"jmp" @function.builtin + +; Types +(struct_definition + . + (identifier) @type + (typed_identifier + (identifier) @variable.member)?) + +(named_type + (identifier) @type .) + +[ + (builtin_type) + (primitive_type) +] @type.builtin + +((identifier) @type + (#lua-match? @type "^[A-Z][a-zA-Z0-9_]*$")) + +(type_identifier) @type + +; Constants +((identifier) @constant + (#lua-match? @constant "^[A-Z_][A-Z0-9_]*$")) + +(enum_variant + name: (identifier) @constant) + +(call_expression + function: (scoped_identifier + "::" + name: (identifier) @constant) + (#lua-match? @constant "^[A-Z]")) + +((match_arm + pattern: (match_pattern + (identifier) @constant)) + (#lua-match? @constant "^[A-Z]")) + +((match_arm + pattern: (match_pattern + (scoped_identifier + name: (identifier) @constant))) + (#lua-match? @constant "^[A-Z]")) + +((identifier) @constant.builtin + (#any-of? @constant.builtin "Some" "None" "Ok" "Err")) + +; Constructors +(unary_expression + "new" + (call_expression + . + (identifier) @constructor)) + +((call_expression + . + (identifier) @constructor) + (#lua-match? @constructor "^%u")) + +; Attributes +(decorator + "@" @attribute + (identifier) @attribute) + +(attribute_item + (identifier) @function.macro) + +(attribute_item + (scoped_identifier + (identifier) @function.macro .)) + +; Labels +(label + . + (identifier) @label) + +(inst_jmp_to_label + "jmp" + . + (identifier) @label) + +(inst_jnz_to_label + "jmp" + . + (identifier) @label) + +; Operators +[ + "+" + "-" + "*" + "/" + "**" + "==" + "!=" + "&" + "=" + "++" + "+=" + "@" + "!" + "~" + ".." + "&&" + "||" + "^" + "<" + "<=" + ">" + ">=" + "<<" + ">>" + "%" + "-=" + "*=" + "/=" + "%=" + "&=" + "|=" + "^=" + "<<=" + ">>=" + "?" +] @operator + +; Literals +(number) @number + +(boolean) @boolean + +[ + (string) + (short_string) +] @string + +; Punctuation +(attribute_item + "#" @punctuation.special) + +[ + "." + "," + ":" + ";" + "->" + "=>" + "::" +] @punctuation.delimiter + +[ + "{" + "}" + "(" + ")" + "[" + "]" + "%{" + "%}" +] @punctuation.bracket + +(type_parameters + [ + "<" + ">" + ] @punctuation.bracket) + +(type_arguments + [ + "<" + ">" + ] @punctuation.bracket) + +; Comment +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/indents.scm new file mode 100644 index 00000000..f63ef36f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/indents.scm @@ -0,0 +1,57 @@ +[ + (mod_item) + (struct_item) + (enum_item) + (impl_item) + (struct_expression) + (match_expression) + (tuple_expression) + (match_arm) + (match_block) + (call_expression) + (assignment_expression) + (arguments) + (block) + (use_list) + (field_declaration_list) + (enum_variant_list) + (tuple_pattern) +] @indent.begin + +(import_statement + "(") @indent.begin + +(block + "}" @indent.end) + +(enum_item + body: (enum_variant_list + "}" @indent.end)) + +(match_expression + body: (match_block + "}" @indent.end)) + +(mod_item + body: (declaration_list + "}" @indent.end)) + +(struct_item + body: (field_declaration_list + "}" @indent.end)) + +(trait_item + body: (declaration_list + "}" @indent.end)) + +[ + ")" + "]" + "}" +] @indent.branch + +[ + (comment) + (string) + (short_string) +] @indent.ignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/injections.scm new file mode 100644 index 00000000..fbb66be3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/injections.scm @@ -0,0 +1,5 @@ +((python_code) @injection.content + (#set! injection.language "python")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/locals.scm new file mode 100644 index 00000000..0573cf6d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cairo/locals.scm @@ -0,0 +1,66 @@ +; References +(identifier) @local.reference + +((type_identifier) @local.reference + (#set! reference.kind "type")) + +((field_identifier) @local.reference + (#set! reference.kind "field")) + +; Scopes +[ + (program) + (block) + (function_definition) + (loop_expression) + (if_expression) + (match_expression) + (match_arm) + (struct_item) + (enum_item) + (impl_item) +] @local.scope + +(use_declaration + argument: (scoped_identifier + name: (identifier) @local.definition.import)) + +(use_as_clause + alias: (identifier) @local.definition.import) + +(use_list + (identifier) @local.definition.import) ; use std::process::{Child, Command, Stdio}; + +; Functions +(function_definition + (identifier) @local.definition.function) + +(function_definition + (identifier) @local.definition.method + (parameter + (self))) + +; Function with parameters, defines parameters +(parameter + [ + (identifier) + (self) + ] @local.definition.parameter) + +; Types +(struct_item + name: (type_identifier) @local.definition.type) + +(constrained_type_parameter + left: (type_identifier) @local.definition.type) ; the P in remove_file>(path: P) + +(enum_item + name: (type_identifier) @local.definition.type) + +; Module +(mod_item + name: (identifier) @local.definition.namespace) + +; Variables +(assignment_expression + left: (identifier) @local.definition.var) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/folds.scm new file mode 100644 index 00000000..6e3f9c18 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/folds.scm @@ -0,0 +1,14 @@ +[ + (annotation_targets) + (const_list) + (enum) + (interface) + (implicit_generics) + (generics) + (group) + (method_parameters) + (named_return_types) + (struct) + (struct_shorthand) + (union) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/highlights.scm new file mode 100644 index 00000000..a48c007e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/highlights.scm @@ -0,0 +1,141 @@ +; Preproc +[ + (unique_id) + (top_level_annotation_body) +] @keyword.directive + +; Includes +[ + "import" + "$import" + "embed" + "using" +] @keyword.import + +(import_path) @string.special.path + +; Keywords +"extends" @keyword + +[ + "struct" + "interface" + "union" + "enum" + "annotation" + "group" + "namespace" +] @keyword.type + +; Builtins +"const" @keyword.modifier + +[ + (primitive_type) + "List" +] @type.builtin + +; Typedefs +(type_definition) @type.definition + +; Labels (@number, @number!) +(field_version) @label + +; Methods +[ + (annotation_definition_identifier) + (method_identifier) +] @function.method + +; Fields +(field_identifier) @variable.member + +; Properties +(property) @property + +; Parameters +[ + (param_identifier) + (return_identifier) +] @variable.parameter + +(annotation_target) @variable.parameter.builtin + +; Constants +[ + (const_identifier) + (local_const) + (enum_member) +] @constant + +(void) @constant.builtin + +; Types +[ + (enum_identifier) + (extend_type) + (type_identifier) +] @type + +; Attributes +[ + (annotation_identifier) + (attribute) +] @attribute + +; Operators +"=" @operator + +; Literals +[ + (string) + (concatenated_string) + (block_text) + (namespace) +] @string + +(namespace) @string.special + +(escape_sequence) @string.escape + +(data_string) @string.special + +(number) @number + +(float) @number.float + +(boolean) @boolean + +(data_hex) @string.special.symbol + +; Punctuation +[ + "*" + "$" + ":" +] @punctuation.special + +[ + "{" + "}" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "." + "," + ";" + "->" +] @punctuation.delimiter + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/indents.scm new file mode 100644 index 00000000..cc2f4d75 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/indents.scm @@ -0,0 +1,40 @@ +[ + (annotation_targets) + (const) + (enum) + (interface) + (implicit_generics) + (generics) + (group) + (method_parameters) + (named_return_types) + (struct) + (union) + (field) +] @indent.begin + +((struct_shorthand + (property)) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) + +((method + (field_version)) @indent.align + (#set! indent.open_delimiter field_version)) + +((const_list + (const_value)) @indent.align + (#set! indent.open_delimiter "[") + (#set! indent.close_delimiter "]")) + +(concatenated_string) @indent.align + +[ + "}" + ")" +] @indent.end @indent.branch + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/locals.scm new file mode 100644 index 00000000..d1f0ccac --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/capnp/locals.scm @@ -0,0 +1,97 @@ +[ + (message) + (annotation_targets) + (const_list) + (enum) + (interface) + (implicit_generics) + (generics) + (group) + (method_parameters) + (named_return_types) + (struct) + (struct_shorthand) + (union) +] @local.scope + +[ + (extend_type) + (field_type) +] @local.reference + +(custom_type + (type_identifier) @local.reference) + +(custom_type + (generics + (generic_parameters + (generic_identifier) @local.reference))) + +(annotation_definition_identifier) @local.definition + +(const_identifier) @local.definition.constant + +(enum + (enum_identifier) @local.definition.enum) + +[ + (enum_member) + (field_identifier) +] @local.definition.field + +(method_identifier) @local.definition.method + +(namespace) @local.definition.namespace + +[ + (param_identifier) + (return_identifier) +] @local.definition.parameter + +(group + (type_identifier) @local.definition.type) + +(struct + (type_identifier) @local.definition.type) + +(union + (type_identifier) @local.definition.type) + +(interface + (type_identifier) @local.definition.type) + +; Generics Related (don't know how to combine these) +(struct + (generics + (generic_parameters + (generic_identifier) @local.definition.parameter))) + +(interface + (generics + (generic_parameters + (generic_identifier) @local.definition.parameter))) + +(method + (implicit_generics + (implicit_generic_parameters + (generic_identifier) @local.definition.parameter))) + +(method + (generics + (generic_parameters + (generic_identifier) @local.definition.parameter))) + +(annotation + (generics + (generic_parameters + (generic_identifier) @local.definition.type))) + +(replace_using + (generics + (generic_parameters + (generic_identifier) @local.definition.type))) + +(return_type + (generics + (generic_parameters + (generic_identifier) @local.definition.type))) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/folds.scm new file mode 100644 index 00000000..052dd206 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/folds.scm @@ -0,0 +1,5 @@ +[ + (intent_def) + (slot_def) + (alias_def) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/highlights.scm new file mode 100644 index 00000000..47113f2c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/highlights.scm @@ -0,0 +1,54 @@ +; Punctuation +[ + "%[" + "@[" + "~[" + "*[" + "]" + "(" + ")" +] @punctuation.bracket + +"," @punctuation.delimiter + +(eq) @operator + +([ + "\"" + "'" +] @punctuation.special + (#set! conceal "")) + +[ + "%" + "?" + "#" +] @character.special + +; Entities +(intent) @module + +(slot) @type + +(variation) @attribute + +(alias) @keyword.directive + +(number) @number + +(argument + key: (string) @property + value: (string) @string) + +(escape) @string.escape + +; Import +"import" @keyword.import + +(file) @string.special.path + +; Text +(word) @spell + +; Comment +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/indents.scm new file mode 100644 index 00000000..dc9e13d7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/indents.scm @@ -0,0 +1,8 @@ +[ + (intent_def) + (slot_def) + (alias_def) +] @indent.begin + +(ERROR + "]") @indent.begin diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/locals.scm new file mode 100644 index 00000000..827447f5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/chatito/locals.scm @@ -0,0 +1,16 @@ +; Definitions +(intent_def + (intent) @local.definition) + +(slot_def + (slot) @local.definition) + +(alias_def + (alias) @local.definition) + +; References +(slot_ref + (slot) @local.reference) + +(alias_ref + (alias) @local.reference) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/folds.scm new file mode 100644 index 00000000..eceb6971 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/folds.scm @@ -0,0 +1,2 @@ +(source + (list_lit) @fold) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/highlights.scm new file mode 100644 index 00000000..62e59ae5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/highlights.scm @@ -0,0 +1,347 @@ +; >> Explanation +; Parsers for lisps are a bit weird in that they just return the raw forms. +; This means we have to do a bit of extra work in the queries to get things +; highlighted as they should be. +; +; For the most part this means that some things have to be assigned multiple +; groups. +; By doing this we can add a basic capture and then later refine it with more +; specialized captures. +; This can mean that sometimes things are highlighted weirdly because they +; have multiple highlight groups applied to them. +; >> Literals +((dis_expr) @comment + (#set! "priority" 105) + ; Higher priority to mark the whole sexpr as a comment + ) + +(kwd_lit) @string.special.symbol + +(str_lit) @string + +(num_lit) @number + +(char_lit) @character + +(bool_lit) @boolean + +(nil_lit) @constant.builtin + +(comment) @comment @spell + +(regex_lit) @string.regexp + +[ + "'" + "`" +] @string.escape + +[ + "~" + "~@" + "#" +] @punctuation.special + +[ + "{" + "}" + "[" + "]" + "(" + ")" +] @punctuation.bracket + +; >> Symbols +; General symbol highlighting +(sym_lit) @variable + +; General function calls +(list_lit + . + (sym_lit) @function.call) + +(anon_fn_lit + . + (sym_lit) @function.call) + +; Quoted symbols +(quoting_lit + (sym_lit) @string.special.symbol) + +(syn_quoting_lit + (sym_lit) @string.special.symbol) + +; Used in destructure pattern +((sym_lit) @variable.parameter + (#lua-match? @variable.parameter "^[&]")) + +; Inline function variables +((sym_lit) @variable.builtin + (#lua-match? @variable.builtin "^%%%d*$")) + +((sym_lit) @variable.builtin + (#eq? @variable.builtin "%&")) + +; Constructor +((sym_lit) @constructor + (#lua-match? @constructor "^-%>[^>].*")) + +; Builtin dynamic variables +((sym_lit) @variable.builtin + (#any-of? @variable.builtin + "*agent*" "*allow-unresolved-vars*" "*assert*" "*clojure-version*" "*command-line-args*" + "*compile-files*" "*compile-path*" "*compiler-options*" "*data-readers*" + "*default-data-reader-fn*" "*err*" "*file*" "*flush-on-newline*" "*fn-loader*" "*in*" + "*math-context*" "*ns*" "*out*" "*print-dup*" "*print-length*" "*print-level*" "*print-meta*" + "*print-namespace-maps*" "*print-readably*" "*read-eval*" "*reader-resolver*" "*source-path*" + "*suppress-read*" "*unchecked-math*" "*use-context-classloader*" "*verbose-defrecords*" + "*warn-on-reflection*")) + +; Builtin repl variables +((sym_lit) @variable.builtin + (#any-of? @variable.builtin "*1" "*2" "*3" "*e")) + +; Types +(sym_lit + name: (sym_name) @_name + (#lua-match? @_name "^[%u][^/%s]*$")) @type + +; Symbols with `.` but not `/` +(sym_lit + !namespace + name: (sym_name) @_name + (#lua-match? @_name "^[^.]+[.]")) @type + +; Interop +; (.instanceMember instance args*) +; (.instanceMember Classname args*) +((sym_lit + name: (sym_name) @_name) @function.method + (#lua-match? @_name "^%.[^-]")) + +; (.-instanceField instance) +((sym_name) @variable.member + (#lua-match? @variable.member "^%.%-%S*")) + +; Classname/staticField +(sym_lit + namespace: (sym_ns) @_namespace + (#lua-match? @_namespace "^[%u]%S*$")) @variable.member + +; (Classname/staticMethod args*) +(list_lit + . + (sym_lit + namespace: (sym_ns) @_namespace + (#lua-match? @_namespace "^%u")) @function.method) + +; TODO: Special casing for the `.` macro +; Operators +((sym_lit) @operator + (#any-of? @operator "*" "*'" "+" "+'" "-" "-'" "/" "<" "<=" ">" ">=" "=" "==")) + +((sym_lit) @keyword.operator + (#any-of? @keyword.operator "not" "not=" "and" "or")) + +; Definition functions +((sym_lit) @keyword + (#any-of? @keyword + "def" "defonce" "defrecord" "defmacro" "definline" "definterface" "defmulti" "defmethod" + "defstruct" "defprotocol" "deftype")) + +((sym_lit) @keyword + (#eq? @keyword "declare")) + +((sym_name) @keyword.coroutine + (#any-of? @keyword.coroutine + "alts!" "alts!!" "await" "await-for" "await1" "chan" "close!" "future" "go" "sync" "thread" + "timeout" "!" ">!!")) + +((sym_lit + name: (sym_name) @_keyword.function.name) @keyword.function + (#any-of? @_keyword.function.name "defn" "defn-" "fn" "fn*")) + +; Comment +((sym_lit) @comment + (#eq? @comment "comment")) + +; Conditionals +((sym_lit) @keyword.conditional + (#any-of? @keyword.conditional "case" "cond" "cond->" "cond->>" "condp")) + +((sym_lit) @keyword.conditional + (#any-of? @keyword.conditional "if" "if-let" "if-not" "if-some")) + +((sym_lit) @keyword.conditional + (#any-of? @keyword.conditional "when" "when-first" "when-let" "when-not" "when-some")) + +; Repeats +((sym_lit) @keyword.repeat + (#any-of? @keyword.repeat "doseq" "dotimes" "for" "loop" "recur" "while")) + +; Exception +((sym_lit) @keyword.exception + (#any-of? @keyword.exception "throw" "try" "catch" "finally")) + +; Includes +((sym_lit) @keyword.import + (#any-of? @keyword.import "ns" "import" "require" "use")) + +; Builtin macros +; TODO: Do all these items belong here? +((sym_lit + name: (sym_name) @function.macro) + (#any-of? @function.macro + "." ".." "->" "->>" "amap" "areduce" "as->" "assert" "binding" "bound-fn" "delay" "do" "dosync" + "doto" "extend-protocol" "extend-type" "gen-class" "gen-interface" "io!" "lazy-cat" "lazy-seq" + "let" "letfn" "locking" "memfn" "monitor-enter" "monitor-exit" "proxy" "proxy-super" "pvalues" + "refer-clojure" "reify" "set!" "some->" "some->>" "time" "unquote" "unquote-splicing" "var" + "vswap!" "with-bindings" "with-in-str" "with-loading-context" "with-local-vars" "with-open" + "with-out-str" "with-precision" "with-redefs")) + +; All builtin functions +; (->> (ns-publics *ns*) +; (keep (fn [[s v]] (when-not (:macro (meta v)) s))) +; sort +; clojure.pprint/pprint)) +; ...and then lots of manual filtering... +((sym_lit + name: (sym_name) @function.builtin) + (#any-of? @function.builtin + "->ArrayChunk" "->Eduction" "->Vec" "->VecNode" "->VecSeq" "-cache-protocol-fn" "-reset-methods" + "PrintWriter-on" "StackTraceElement->vec" "Throwable->map" "accessor" "aclone" "add-classpath" + "add-tap" "add-watch" "agent" "agent-error" "agent-errors" "aget" "alength" "alias" "all-ns" + "alter" "alter-meta!" "alter-var-root" "ancestors" "any?" "apply" "array-map" "aset" + "aset-boolean" "aset-byte" "aset-char" "aset-double" "aset-float" "aset-int" "aset-long" + "aset-short" "assoc" "assoc!" "assoc-in" "associative?" "atom" "bases" "bean" "bigdec" "bigint" + "biginteger" "bit-and" "bit-and-not" "bit-clear" "bit-flip" "bit-not" "bit-or" "bit-set" + "bit-shift-left" "bit-shift-right" "bit-test" "bit-xor" "boolean" "boolean-array" "boolean?" + "booleans" "bound-fn*" "bound?" "bounded-count" "butlast" "byte" "byte-array" "bytes" "bytes?" + "cast" "cat" "char" "char-array" "char-escape-string" "char-name-string" "char?" "chars" "chunk" + "chunk-append" "chunk-buffer" "chunk-cons" "chunk-first" "chunk-next" "chunk-rest" + "chunked-seq?" "class" "class?" "clear-agent-errors" "clojure-version" "coll?" "commute" "comp" + "comparator" "compare" "compare-and-set!" "compile" "complement" "completing" "concat" "conj" + "conj!" "cons" "constantly" "construct-proxy" "contains?" "count" "counted?" "create-ns" + "create-struct" "cycle" "dec" "dec'" "decimal?" "dedupe" "default-data-readers" "delay?" + "deliver" "denominator" "deref" "derive" "descendants" "destructure" "disj" "disj!" "dissoc" + "dissoc!" "distinct" "distinct?" "doall" "dorun" "double" "double-array" "eduction" "empty" + "empty?" "ensure" "ensure-reduced" "enumeration-seq" "error-handler" "error-mode" "eval" "even?" + "every-pred" "every?" "extend" "extenders" "extends?" "false?" "ffirst" "file-seq" "filter" + "filterv" "find" "find-keyword" "find-ns" "find-protocol-impl" "find-protocol-method" "find-var" + "first" "flatten" "float" "float-array" "float?" "floats" "flush" "fn?" "fnext" "fnil" "force" + "format" "frequencies" "future-call" "future-cancel" "future-cancelled?" "future-done?" + "future?" "gensym" "get" "get-in" "get-method" "get-proxy-class" "get-thread-bindings" + "get-validator" "group-by" "halt-when" "hash" "hash-combine" "hash-map" "hash-ordered-coll" + "hash-set" "hash-unordered-coll" "ident?" "identical?" "identity" "ifn?" "in-ns" "inc" "inc'" + "indexed?" "init-proxy" "inst-ms" "inst-ms*" "inst?" "instance?" "int" "int-array" "int?" + "integer?" "interleave" "intern" "interpose" "into" "into-array" "ints" "isa?" "iterate" + "iterator-seq" "juxt" "keep" "keep-indexed" "key" "keys" "keyword" "keyword?" "last" "line-seq" + "list" "list*" "list?" "load" "load-file" "load-reader" "load-string" "loaded-libs" "long" + "long-array" "longs" "macroexpand" "macroexpand-1" "make-array" "make-hierarchy" "map" + "map-entry?" "map-indexed" "map?" "mapcat" "mapv" "max" "max-key" "memoize" "merge" "merge-with" + "meta" "method-sig" "methods" "min" "min-key" "mix-collection-hash" "mod" "munge" "name" + "namespace" "namespace-munge" "nat-int?" "neg-int?" "neg?" "newline" "next" "nfirst" "nil?" + "nnext" "not-any?" "not-empty" "not-every?" "ns-aliases" "ns-imports" "ns-interns" "ns-map" + "ns-name" "ns-publics" "ns-refers" "ns-resolve" "ns-unalias" "ns-unmap" "nth" "nthnext" + "nthrest" "num" "number?" "numerator" "object-array" "odd?" "parents" "partial" "partition" + "partition-all" "partition-by" "pcalls" "peek" "persistent!" "pmap" "pop" "pop!" + "pop-thread-bindings" "pos-int?" "pos?" "pr" "pr-str" "prefer-method" "prefers" + "primitives-classnames" "print" "print-ctor" "print-dup" "print-method" "print-simple" + "print-str" "printf" "println" "println-str" "prn" "prn-str" "promise" "proxy-call-with-super" + "proxy-mappings" "proxy-name" "push-thread-bindings" "qualified-ident?" "qualified-keyword?" + "qualified-symbol?" "quot" "rand" "rand-int" "rand-nth" "random-sample" "range" "ratio?" + "rational?" "rationalize" "re-find" "re-groups" "re-matcher" "re-matches" "re-pattern" "re-seq" + "read" "read+string" "read-line" "read-string" "reader-conditional" "reader-conditional?" + "realized?" "record?" "reduce" "reduce-kv" "reduced" "reduced?" "reductions" "ref" + "ref-history-count" "ref-max-history" "ref-min-history" "ref-set" "refer" + "release-pending-sends" "rem" "remove" "remove-all-methods" "remove-method" "remove-ns" + "remove-tap" "remove-watch" "repeat" "repeatedly" "replace" "replicate" "requiring-resolve" + "reset!" "reset-meta!" "reset-vals!" "resolve" "rest" "restart-agent" "resultset-seq" "reverse" + "reversible?" "rseq" "rsubseq" "run!" "satisfies?" "second" "select-keys" "send" "send-off" + "send-via" "seq" "seq?" "seqable?" "seque" "sequence" "sequential?" "set" + "set-agent-send-executor!" "set-agent-send-off-executor!" "set-error-handler!" "set-error-mode!" + "set-validator!" "set?" "short" "short-array" "shorts" "shuffle" "shutdown-agents" + "simple-ident?" "simple-keyword?" "simple-symbol?" "slurp" "some" "some-fn" "some?" "sort" + "sort-by" "sorted-map" "sorted-map-by" "sorted-set" "sorted-set-by" "sorted?" "special-symbol?" + "spit" "split-at" "split-with" "str" "string?" "struct" "struct-map" "subs" "subseq" "subvec" + "supers" "swap!" "swap-vals!" "symbol" "symbol?" "tagged-literal" "tagged-literal?" "take" + "take-last" "take-nth" "take-while" "tap>" "test" "the-ns" "thread-bound?" "to-array" + "to-array-2d" "trampoline" "transduce" "transient" "tree-seq" "true?" "type" "unchecked-add" + "unchecked-add-int" "unchecked-byte" "unchecked-char" "unchecked-dec" "unchecked-dec-int" + "unchecked-divide-int" "unchecked-double" "unchecked-float" "unchecked-inc" "unchecked-inc-int" + "unchecked-int" "unchecked-long" "unchecked-multiply" "unchecked-multiply-int" + "unchecked-negate" "unchecked-negate-int" "unchecked-remainder-int" "unchecked-short" + "unchecked-subtract" "unchecked-subtract-int" "underive" "unquote" "unquote-splicing" + "unreduced" "unsigned-bit-shift-right" "update" "update-in" "update-proxy" "uri?" "uuid?" "val" + "vals" "var-get" "var-set" "var?" "vary-meta" "vec" "vector" "vector-of" "vector?" "volatile!" + "volatile?" "vreset!" "with-bindings*" "with-meta" "with-redefs-fn" "xml-seq" "zero?" "zipmap" + ; earlier + "drop" "drop-last" "drop-while" "double?" "doubles" "ex-data" "ex-info" + ; 1.10 + "ex-cause" "ex-message" + ; 1.11 + "NaN?" "abs" "infinite?" "iteration" "random-uuid" "parse-boolean" "parse-double" "parse-long" + "parse-uuid" "seq-to-map-for-destructuring" "update-keys" "update-vals" + ; 1.12 + "partitionv" "partitionv-all" "splitv-at")) + +; >> Context based highlighting +; def-likes +; Correctly highlight docstrings +;(list_lit +;. +;(sym_lit) @_keyword ; Don't really want to highlight twice +;(#any-of? @_keyword +;"def" "defonce" "defrecord" "defmacro" "definline" +;"defmulti" "defmethod" "defstruct" "defprotocol" +;"deftype") +;. +;(sym_lit) +;. +; TODO: Add @comment highlight +;(str_lit)? +;. +;(_)) +; Function definitions +(list_lit + . + ((sym_lit + name: (sym_name) @_keyword.function.name) + (#any-of? @_keyword.function.name "defn" "defn-" "fn" "fn*")) + . + (sym_lit)? @function + . + ; TODO: Add @comment highlight + (str_lit)?) + +; TODO: Fix parameter highlighting +; I think there's a bug here in nvim-treesitter +; TODO: Reproduce bug and file ticket +;. +;[(vec_lit +; (sym_lit)* @variable.parameter) +; (list_lit +; (vec_lit +; (sym_lit)* @variable.parameter))]) +;[((list_lit +; (vec_lit +; (sym_lit) @variable.parameter) +; (_) +; + +; ((vec_lit +; (sym_lit) @variable.parameter) +; (_))) +; Meta punctuation +; NOTE: When the above `Function definitions` query captures the +; the @function it also captures the child meta_lit +; We capture the meta_lit symbol (^) after so that the later +; highlighting overrides the former +"^" @punctuation.special + +; namespaces +(list_lit + . + (sym_lit) @_include + (#eq? @_include "ns") + . + (sym_lit) @module) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/locals.scm new file mode 100644 index 00000000..e47adce4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/clojure/locals.scm @@ -0,0 +1 @@ +; placeholder file to get incremental selection to work diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cmake/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cmake/folds.scm new file mode 100644 index 00000000..ef153b91 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cmake/folds.scm @@ -0,0 +1,8 @@ +[ + (if_condition) + (foreach_loop) + (while_loop) + (function_def) + (macro_def) + (block_def) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cmake/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cmake/highlights.scm new file mode 100644 index 00000000..d6e0f243 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cmake/highlights.scm @@ -0,0 +1,223 @@ +(normal_command + (identifier) + (argument_list + (argument + (unquoted_argument)) @constant) + (#lua-match? @constant "^[%u@][%u%d_]+$")) + +[ + (quoted_argument) + (bracket_argument) +] @string + +(variable_ref) @none + +(variable) @variable + +[ + (bracket_comment) + (line_comment) +] @comment @spell + +(normal_command + (identifier) @function) + +[ + "ENV" + "CACHE" +] @module + +[ + "$" + "{" + "}" + "<" + ">" +] @punctuation.special + +[ + "(" + ")" +] @punctuation.bracket + +[ + (function) + (endfunction) + (macro) + (endmacro) +] @keyword.function + +[ + (if) + (elseif) + (else) + (endif) +] @keyword.conditional + +[ + (foreach) + (endforeach) + (while) + (endwhile) +] @keyword.repeat + +(normal_command + (identifier) @keyword.repeat + (#match? @keyword.repeat "\\c^(continue|break)$")) + +(normal_command + (identifier) @keyword.return + (#match? @keyword.return "\\c^return$")) + +(function_command + (function) + (argument_list + . + (argument) @function + (argument)* @variable.parameter)) + +(macro_command + (macro) + (argument_list + . + (argument) @function.macro + (argument)* @variable.parameter)) + +(block_def + (block_command + (block) @function.builtin + (argument_list + (argument + (unquoted_argument) @constant)) + (#any-of? @constant "SCOPE_FOR" "POLICIES" "VARIABLES" "PROPAGATE")) + (endblock_command + (endblock) @function.builtin)) + +; +((argument) @boolean + (#match? @boolean "\\c^(1|on|yes|true|y|0|off|no|false|n|ignore|notfound|.*-notfound)$")) + +; +(if_command + (if) + (argument_list + (argument) @keyword.operator) + (#any-of? @keyword.operator + "NOT" "AND" "OR" "COMMAND" "POLICY" "TARGET" "TEST" "DEFINED" "IN_LIST" "EXISTS" "IS_NEWER_THAN" + "IS_DIRECTORY" "IS_SYMLINK" "IS_ABSOLUTE" "MATCHES" "LESS" "GREATER" "EQUAL" "LESS_EQUAL" + "GREATER_EQUAL" "STRLESS" "STRGREATER" "STREQUAL" "STRLESS_EQUAL" "STRGREATER_EQUAL" + "VERSION_LESS" "VERSION_GREATER" "VERSION_EQUAL" "VERSION_LESS_EQUAL" "VERSION_GREATER_EQUAL")) + +(elseif_command + (elseif) + (argument_list + (argument) @keyword.operator) + (#any-of? @keyword.operator + "NOT" "AND" "OR" "COMMAND" "POLICY" "TARGET" "TEST" "DEFINED" "IN_LIST" "EXISTS" "IS_NEWER_THAN" + "IS_DIRECTORY" "IS_SYMLINK" "IS_ABSOLUTE" "MATCHES" "LESS" "GREATER" "EQUAL" "LESS_EQUAL" + "GREATER_EQUAL" "STRLESS" "STRGREATER" "STREQUAL" "STRLESS_EQUAL" "STRGREATER_EQUAL" + "VERSION_LESS" "VERSION_GREATER" "VERSION_EQUAL" "VERSION_LESS_EQUAL" "VERSION_GREATER_EQUAL")) + +(normal_command + (identifier) @function.builtin + (#match? @function.builtin + "\\c^(cmake_host_system_information|cmake_language|cmake_minimum_required|cmake_parse_arguments|cmake_path|cmake_policy|configure_file|execute_process|file|find_file|find_library|find_package|find_path|find_program|foreach|get_cmake_property|get_directory_property|get_filename_component|get_property|include|include_guard|list|macro|mark_as_advanced|math|message|option|separate_arguments|set|set_directory_properties|set_property|site_name|string|unset|variable_watch|add_compile_definitions|add_compile_options|add_custom_command|add_custom_target|add_definitions|add_dependencies|add_executable|add_library|add_link_options|add_subdirectory|add_test|aux_source_directory|build_command|create_test_sourcelist|define_property|enable_language|enable_testing|export|fltk_wrap_ui|get_source_file_property|get_target_property|get_test_property|include_directories|include_external_msproject|include_regular_expression|install|link_directories|link_libraries|load_cache|project|remove_definitions|set_source_files_properties|set_target_properties|set_tests_properties|source_group|target_compile_definitions|target_compile_features|target_compile_options|target_include_directories|target_link_directories|target_link_libraries|target_link_options|target_precompile_headers|target_sources|try_compile|try_run|ctest_build|ctest_configure|ctest_coverage|ctest_empty_binary_directory|ctest_memcheck|ctest_read_custom_files|ctest_run_script|ctest_sleep|ctest_start|ctest_submit|ctest_test|ctest_update|ctest_upload)$")) + +(normal_command + (identifier) @_function + (argument_list + . + (argument) @variable) + (#match? @_function "\\c^set$")) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^set$") + (argument_list + . + (argument) + ((argument) @_cache @keyword.modifier + . + (argument) @_type @type + (#any-of? @_cache "CACHE") + (#any-of? @_type "BOOL" "FILEPATH" "PATH" "STRING" "INTERNAL")))) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^unset$") + (argument_list + . + (argument) + (argument) @keyword.modifier + (#any-of? @keyword.modifier "CACHE" "PARENT_SCOPE"))) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^list$") + (argument_list + . + (argument) @constant + (#any-of? @constant "LENGTH" "GET" "JOIN" "SUBLIST" "FIND") + . + (argument) @variable + (argument) @variable .)) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^list$") + (argument_list + . + (argument) @constant + . + (argument) @variable + (#any-of? @constant + "APPEND" "FILTER" "INSERT" "POP_BACK" "POP_FRONT" "PREPEND" "REMOVE_ITEM" "REMOVE_AT" + "REMOVE_DUPLICATES" "REVERSE" "SORT"))) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^list$") + (argument_list + . + (argument) @_transform @constant + . + (argument) @variable + . + (argument) @_action @constant + (#eq? @_transform "TRANSFORM") + (#any-of? @_action "APPEND" "PREPEND" "TOUPPER" "TOLOWER" "STRIP" "GENEX_STRIP" "REPLACE"))) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^list$") + (argument_list + . + (argument) @_transform @constant + . + (argument) @variable + . + (argument) @_action @constant + . + (argument)? @_selector @constant + (#eq? @_transform "TRANSFORM") + (#any-of? @_action "APPEND" "PREPEND" "TOUPPER" "TOLOWER" "STRIP" "GENEX_STRIP" "REPLACE") + (#any-of? @_selector "AT" "FOR" "REGEX"))) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^list$") + (argument_list + . + (argument) @_transform @constant + (argument) @constant + . + (argument) @variable + (#eq? @_transform "TRANSFORM") + (#eq? @constant "OUTPUT_VARIABLE"))) + +(escape_sequence) @string.escape + +((source_file + . + (line_comment) @keyword.directive @nospell) + (#lua-match? @keyword.directive "^#!/")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cmake/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cmake/indents.scm new file mode 100644 index 00000000..cbd976c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cmake/indents.scm @@ -0,0 +1,26 @@ +[ + (normal_command) + (if_condition) + (foreach_loop) + (while_loop) + (function_def) + (macro_def) + (block_def) +] @indent.begin + +[ + (elseif_command) + (else_command) + (endif_command) + (endforeach_command) + (endwhile_command) + (endfunction_command) + (endmacro_command) + (endblock_command) +] @indent.branch + +")" @indent.branch + +")" @indent.end + +(argument_list) @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/comment/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/comment/highlights.scm new file mode 100644 index 00000000..5d18b790 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/comment/highlights.scm @@ -0,0 +1,49 @@ +((tag + (name) @comment.todo @nospell + ("(" @punctuation.bracket + (user) @constant + ")" @punctuation.bracket)? + ":" @punctuation.delimiter) + (#any-of? @comment.todo "TODO" "WIP")) + +("text" @comment.todo @nospell + (#any-of? @comment.todo "TODO" "WIP")) + +((tag + (name) @comment.note @nospell + ("(" @punctuation.bracket + (user) @constant + ")" @punctuation.bracket)? + ":" @punctuation.delimiter) + (#any-of? @comment.note "NOTE" "XXX" "INFO" "DOCS" "PERF" "TEST")) + +("text" @comment.note @nospell + (#any-of? @comment.note "NOTE" "XXX" "INFO" "DOCS" "PERF" "TEST")) + +((tag + (name) @comment.warning @nospell + ("(" @punctuation.bracket + (user) @constant + ")" @punctuation.bracket)? + ":" @punctuation.delimiter) + (#any-of? @comment.warning "HACK" "WARNING" "WARN" "FIX")) + +("text" @comment.warning @nospell + (#any-of? @comment.warning "HACK" "WARNING" "WARN" "FIX")) + +((tag + (name) @comment.error @nospell + ("(" @punctuation.bracket + (user) @constant + ")" @punctuation.bracket)? + ":" @punctuation.delimiter) + (#any-of? @comment.error "FIXME" "BUG" "ERROR")) + +("text" @comment.error @nospell + (#any-of? @comment.error "FIXME" "BUG" "ERROR")) + +; Issue number (#123) +("text" @number + (#lua-match? @number "^#[0-9]+$")) + +(uri) @string.special.url @nospell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/commonlisp/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/commonlisp/folds.scm new file mode 100644 index 00000000..eceb6971 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/commonlisp/folds.scm @@ -0,0 +1,2 @@ +(source + (list_lit) @fold) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/commonlisp/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/commonlisp/highlights.scm new file mode 100644 index 00000000..84f132e4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/commonlisp/highlights.scm @@ -0,0 +1,320 @@ +(sym_lit) @variable + +; A highlighting for functions/macros in th cl namespace is available in theHamsta/nvim-treesitter-commonlisp +;(list_lit . (sym_lit) @function.builtin (#cl-standard-function? @function.builtin)) +;(list_lit . (sym_lit) @function.builtin (#cl-standard-macro? @function.macro)) +(dis_expr) @comment + +(defun_keyword) @function.macro + +(defun_header + function_name: (_) @function) + +(defun_header + lambda_list: (list_lit + (sym_lit) @variable.parameter)) + +(defun_header + keyword: (defun_keyword + "defmethod") + lambda_list: (list_lit + (list_lit + . + (sym_lit) + . + (sym_lit) @string.special.symbol))) + +(defun_header + lambda_list: (list_lit + (list_lit + . + (sym_lit) @variable.parameter + . + (_)))) + +(defun_header + specifier: (sym_lit) @string.special.symbol) + +[ + ":" + "::" + "." +] @punctuation.special + +[ + (accumulation_verb) + (for_clause_word) + "for" + "and" + "finally" + "thereis" + "always" + "when" + "if" + "unless" + "else" + "do" + "loop" + "below" + "in" + "from" + "across" + "repeat" + "being" + "into" + "with" + "as" + "while" + "until" + "return" + "initially" +] @function.macro + +"=" @operator + +(include_reader_macro) @string.special.symbol + +[ + "#C" + "#c" +] @number + +[ + (kwd_lit) + (self_referential_reader_macro) +] @string.special.symbol + +(package_lit + package: (_) @module) + +"cl" @module + +(str_lit) @string + +(num_lit) @number + +((sym_lit) @boolean + (#any-of? @boolean "t" "T")) + +(nil_lit) @constant.builtin + +(comment) @comment @spell + +; dynamic variables +((sym_lit) @variable.builtin + (#lua-match? @variable.builtin "^[*].+[*]$")) + +; quote +"'" @string.escape + +(format_specifier) @string.escape + +(quoting_lit) @string.escape + +; syntax quote +"`" @string.escape + +"," @string.escape + +",@" @string.escape + +(syn_quoting_lit) @string.escape + +(unquoting_lit) @none + +(unquote_splicing_lit) @none + +[ + "(" + ")" +] @punctuation.bracket + +(block_comment) @comment @spell + +(with_clause + type: (_) @type) + +(for_clause + type: (_) @type) + +; defun-like things +(list_lit + . + (sym_lit) @function.macro + . + (sym_lit) @function + (#eq? @function.macro "deftest")) + +; Macros and Special Operators +(list_lit + . + (sym_lit) @function.macro + ; Generated via https://github.com/theHamsta/nvim-treesitter-commonlisp/blob/22fdc9fd6ed594176cc7299cc6f68dd21c94c63b/scripts/generate-symbols.lisp#L1-L21 + (#any-of? @function.macro + "do*" "step" "handler-bind" "decf" "prog1" "destructuring-bind" "defconstant" "do" "lambda" + "with-standard-io-syntax" "case" "restart-bind" "ignore-errors" "with-slots" "prog2" "defclass" + "define-condition" "print-unreadable-object" "defvar" "when" "with-open-file" "prog" "incf" + "declaim" "and" "loop-finish" "multiple-value-bind" "pop" "psetf" "defmacro" "with-open-stream" + "define-modify-macro" "defsetf" "formatter" "call-method" "handler-case" "pushnew" "or" + "with-hash-table-iterator" "ecase" "cond" "defun" "remf" "ccase" "define-compiler-macro" + "dotimes" "multiple-value-list" "assert" "deftype" "with-accessors" "trace" + "with-simple-restart" "do-symbols" "nth-value" "define-symbol-macro" "psetq" "rotatef" "dolist" + "check-type" "multiple-value-setq" "push" "pprint-pop" "loop" "define-setf-expander" + "pprint-exit-if-list-exhausted" "with-condition-restarts" "defstruct" "with-input-from-string" + "with-compilation-unit" "defgeneric" "with-output-to-string" "untrace" "defparameter" + "ctypecase" "do-external-symbols" "etypecase" "do-all-symbols" "with-package-iterator" "unless" + "defmethod" "in-package" "defpackage" "return" "typecase" "shiftf" "setf" "pprint-logical-block" + "time" "restart-case" "prog*" "define-method-combination" "optimize")) + +; constant +((sym_lit) @constant + (#lua-match? @constant "^[+].+[+]$")) + +(var_quoting_lit + marker: "#'" @string.special.symbol + value: (_) @string.special.symbol) + +[ + "#" + "#p" + "#P" +] @string.special.symbol + +(list_lit + . + (sym_lit) @function.builtin + ; Generated via https://github.com/theHamsta/nvim-treesitter-commonlisp/blob/22fdc9fd6ed594176cc7299cc6f68dd21c94c63b/scripts/generate-symbols.lisp#L1-L21 + (#any-of? @function.builtin + "apropos-list" "subst" "substitute" "pprint-linear" "file-namestring" "write-char" "do*" + "slot-exists-p" "file-author" "macro-function" "rassoc" "make-echo-stream" + "arithmetic-error-operation" "position-if-not" "list" "cdadr" "lisp-implementation-type" + "vector-push" "let" "length" "string-upcase" "adjoin" "digit-char" "step" "member-if" + "handler-bind" "lognot" "apply" "gcd" "slot-unbound" "stringp" "values-list" "stable-sort" + "decode-float" "make-list" "rplaca" "isqrt" "export" "synonym-stream-symbol" "function-keywords" + "replace" "tanh" "maphash" "code-char" "decf" "array-displacement" "string-not-lessp" + "slot-value" "remove-if" "cell-error-name" "vectorp" "cdddar" "two-way-stream-output-stream" + "parse-integer" "get-internal-real-time" "fourth" "make-string" "slot-missing" "byte-size" + "string-trim" "nstring-downcase" "cdaddr" "<" "labels" "interactive-stream-p" "fifth" "max" + "logxor" "pathname-name" "function" "realp" "eql" "logand" "short-site-name" "prog1" + "user-homedir-pathname" "list-all-packages" "exp" "cadar" "read-char-no-hang" + "package-error-package" "stream-external-format" "bit-andc2" "nsubstitute-if" "mapcar" + "complement" "load-logical-pathname-translations" "pprint-newline" "oddp" "caaar" + "destructuring-bind" "copy-alist" "acos" "go" "bit-nor" "defconstant" "fceiling" "tenth" + "nreverse" "=" "nunion" "slot-boundp" "string>" "count-if" "atom" "char=" "random-state-p" + "row-major-aref" "bit-andc1" "translate-pathname" "simple-vector-p" "coerce" "substitute-if-not" + "zerop" "invalid-method-error" "compile" "realpart" "remove-if-not" "pprint-tab" + "hash-table-rehash-threshold" "invoke-restart" "if" "count" "/=" "do" "initialize-instance" + "abs" "schar" "simple-condition-format-control" "delete-package" "subst-if" "lambda" + "hash-table-count" "array-has-fill-pointer-p" "bit" "with-standard-io-syntax" "parse-namestring" + "proclaim" "array-in-bounds-p" "multiple-value-call" "rplacd" "some" "graphic-char-p" + "read-from-string" "consp" "cadaar" "acons" "every" "make-pathname" "mask-field" "case" + "set-macro-character" "bit-and" "restart-bind" "echo-stream-input-stream" "compile-file" + "fill-pointer" "numberp" "acosh" "array-dimensions" "documentation" "minusp" "inspect" + "copy-structure" "integer-length" "ensure-generic-function" "char>=" "quote" "lognor" + "make-two-way-stream" "ignore-errors" "tailp" "with-slots" "fboundp" + "logical-pathname-translations" "equal" "float-sign" "shadow" "sleep" "numerator" "prog2" "getf" + "ldb-test" "round" "locally" "echo-stream-output-stream" "log" "get-macro-character" + "alphanumericp" "find-method" "nintersection" "defclass" "define-condition" + "print-unreadable-object" "defvar" "broadcast-stream-streams" "floatp" "subst-if-not" "integerp" + "translate-logical-pathname" "subsetp" "when" "write-string" "with-open-file" "clrhash" + "apropos" "intern" "min" "string-greaterp" "import" "nset-difference" "prog" "incf" + "both-case-p" "multiple-value-prog1" "characterp" "streamp" "digit-char-p" "random" + "string-lessp" "make-string-input-stream" "copy-symbol" "read-sequence" "logcount" "bit-not" + "boundp" "encode-universal-time" "third" "declaim" "map" "cons" "set-syntax-from-char" "and" + "cis" "symbol-plist" "loop-finish" "standard-char-p" "multiple-value-bind" "asin" "string" "pop" + "complex" "fdefinition" "psetf" "type-error-datum" "output-stream-p" "floor" "write-line" "<=" + "defmacro" "rational" "hash-table-test" "with-open-stream" "read-char" "string-capitalize" + "get-properties" "y-or-n-p" "use-package" "remove" "compiler-macro-function" "read" + "package-nicknames" "remove-duplicates" "make-load-form-saving-slots" "dribble" + "define-modify-macro" "make-dispatch-macro-character" "close" "cosh" "open" "finish-output" + "string-downcase" "car" "nstring-capitalize" "software-type" "read-preserving-whitespace" "cadr" + "fround" "nsublis" "defsetf" "find-all-symbols" "char>" "no-applicable-method" + "compute-restarts" "pathname" "bit-orc2" "write-sequence" "pprint-tabular" "symbol-value" + "char-name" "get-decoded-time" "formatter" "bit-vector-p" "intersection" "pathname-type" + "clear-input" "call-method" "princ-to-string" "symbolp" "make-load-form" "nsubst" + "pprint-dispatch" "handler-case" "method-combination-error" "probe-file" "atan" "string<" + "type-error-expected-type" "pushnew" "unread-char" "print" "or" "with-hash-table-iterator" + "make-sequence" "ecase" "unwind-protect" "require" "sixth" "get-dispatch-macro-character" + "char-not-lessp" "read-byte" "tagbody" "file-error-pathname" "catch" "rationalp" "char-downcase" + "char-int" "array-rank" "cond" "last" "make-string-output-stream" "array-dimension" + "host-namestring" "input-stream-p" "decode-universal-time" "defun" "eval-when" "char-code" + "pathname-directory" "evenp" "subseq" "pprint" "ftruncate" "make-instance" "pathname-host" + "logbitp" "remf" "1+" "copy-pprint-dispatch" "char-upcase" "error" "read-line" "second" + "make-package" "directory" "special-operator-p" "open-stream-p" "rassoc-if-not" "ccase" "equalp" + "substitute-if" "*" "char/=" "cdr" "sqrt" "lcm" "logical-pathname" "eval" + "define-compiler-macro" "nsubstitute-if-not" "mapcon" "imagpart" "set-exclusive-or" + "simple-condition-format-arguments" "expt" "concatenate" "file-position" "macrolet" "keywordp" + "hash-table-rehash-size" "+" "eighth" "use-value" "char-equal" "bit-xor" "format" "byte" + "dotimes" "namestring" "char-not-equal" "multiple-value-list" "assert" "append" "notany" "typep" + "delete-file" "makunbound" "cdaar" "file-write-date" ">" "cdddr" "write-to-string" "funcall" + "member-if-not" "deftype" "readtable-case" "with-accessors" "truename" "constantp" "rassoc-if" + "caaadr" "tree-equal" "nset-exclusive-or" "nsubstitute" "make-instances-obsolete" + "package-use-list" "invoke-debugger" "provide" "count-if-not" "trace" "logandc1" "nthcdr" + "char<=" "functionp" "with-simple-restart" "set-dispatch-macro-character" "logorc2" "unexport" + "rest" "unbound-slot-instance" "make-hash-table" "hash-table-p" "reinitialize-instance" "nth" + "do-symbols" "nreconc" "macroexpand" "store-value" "float-precision" "remprop" "nth-value" + "define-symbol-macro" "update-instance-for-redefined-class" "identity" "progv" "progn" + "return-from" "readtablep" "rem" "symbol-name" "psetq" "wild-pathname-p" "char" "list*" "char<" + "plusp" "pairlis" "cddar" "pprint-indent" "union" "compiled-function-p" "rotatef" "abort" + "machine-type" "concatenated-stream-streams" "string-right-trim" "enough-namestring" + "arithmetic-error-operands" "ceiling" "dolist" "delete" "make-condition" "string-left-trim" + "integer-decode-float" "check-type" "notevery" "function-lambda-expression" "-" + "multiple-value-setq" "name-char" "push" "pprint-pop" "compile-file-pathname" "list-length" + "nstring-upcase" "eq" "find-if" "method-qualifiers" "caadr" "cddr" "string=" "let*" + "remove-method" "pathname-match-p" "find-package" "truncate" "caaddr" "get-setf-expansion" + "loop" "define-setf-expander" "caddr" "package-shadowing-symbols" "force-output" + "slot-makunbound" "string-not-greaterp" "cdadar" "cdaadr" "logandc2" "make-array" + "merge-pathnames" "sin" "1-" "machine-version" "ffloor" "packagep" "set-pprint-dispatch" "flet" + "gensym" "pprint-exit-if-list-exhausted" "cos" "get" "mapl" "delete-if" + "with-condition-restarts" "atanh" "copy-list" "fill" "char-not-greaterp" "bit-orc1" "mod" + "package-used-by-list" "warn" "add-method" "simple-string-p" "find-restart" "describe" + "pathname-version" "peek-char" "yes-or-no-p" "complexp" "aref" "not" "position-if" "string>=" + "defstruct" "float-radix" "ninth" "caadar" "subtypep" "set" "butlast" "allocate-instance" + "with-input-from-string" "assoc" "write" "make-random-state" "bit-eqv" "float-digits" + "long-site-name" "with-compilation-unit" "delete-duplicates" "make-symbol" "room" "cdar" + "pprint-fill" "defgeneric" "macroexpand-1" "scale-float" "cdaaar" + "update-instance-for-different-class" "array-row-major-index" "ed" "file-string-length" + "ensure-directories-exist" "copy-readtable" "string<=" "seventh" "with-output-to-string" + "signum" "elt" "untrace" "null" "defparameter" "block" "prin1" "revappend" "gentemp" "ctypecase" + "ash" "sxhash" "listp" "do-external-symbols" "bit-ior" "etypecase" "sort" "change-class" + "find-class" "alpha-char-p" "map-into" "terpri" "do-all-symbols" "ldb" "logorc1" "search" + "fmakunbound" "load" "character" "string-not-equal" "pathnamep" "make-broadcast-stream" "arrayp" + "mapcan" "cerror" "invoke-restart-interactively" "assoc-if-not" "with-package-iterator" + "get-internal-run-time" "read-delimited-list" "unless" "lower-case-p" "restart-name" "/" "boole" + "defmethod" "float" "software-version" "vector-pop" "vector-push-extend" "caar" "ldiff" "member" + "find-symbol" "reduce" "svref" "describe-object" "logior" "string-equal" "type-of" "position" + "cddadr" "pathname-device" "get-output-stream-string" "symbol-package" "tan" + "compute-applicable-methods" "cddddr" "nsubst-if-not" "sublis" "set-difference" + "two-way-stream-input-stream" "adjustable-array-p" "machine-instance" "signal" "conjugate" + "caaaar" "endp" "lisp-implementation-version" "cddaar" "package-name" "adjust-array" "bit-nand" + "gethash" "in-package" "symbol-function" "make-concatenated-stream" "defpackage" "class-of" + "no-next-method" "logeqv" "deposit-field" "disassemble" "unuse-package" "copy-tree" "find" + "asinh" "class-name" "rename-file" "values" "print-not-readable-object" "mismatch" "cadadr" + "shadowing-import" "delete-if-not" "maplist" "listen" "return" "stream-element-type" "unintern" + "merge" "make-synonym-stream" "prin1-to-string" "nsubst-if" "byte-position" "phase" + "muffle-warning" "remhash" "continue" "load-time-value" "hash-table-size" + "upgraded-complex-part-type" "char-lessp" "sbit" "upgraded-array-element-type" "file-length" + "typecase" "cadddr" "first" "rationalize" "logtest" "find-if-not" "dpb" "mapc" "sinh" + "char-greaterp" "shiftf" "denominator" "get-universal-time" "nconc" "setf" "lognand" + "rename-package" "pprint-logical-block" "break" "symbol-macrolet" "the" "fresh-line" + "clear-output" "assoc-if" "string/=" "princ" "directory-namestring" "stream-error-stream" + "array-element-type" "setq" "copy-seq" "time" "restart-case" "prog*" "shared-initialize" + "array-total-size" "simple-bit-vector-p" "define-method-combination" "write-byte" "constantly" + "caddar" "print-object" "vector" "throw" "reverse" ">=" "upper-case-p" "nbutlast") + ) + +(list_lit + . + (sym_lit) @operator + (#match? @operator "^([+*-+=<>]|<=|>=|/=)$")) + +((sym_lit) @string.special.symbol + (#lua-match? @string.special.symbol "^[&]")) + +[ + (array_dimension) + "#0A" + "#0a" +] @number + +(char_lit) @character diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/commonlisp/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/commonlisp/locals.scm new file mode 100644 index 00000000..98036d32 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/commonlisp/locals.scm @@ -0,0 +1,109 @@ +(defun_header + function_name: (sym_lit) @local.definition.function + (#set! definition.function.scope "parent")) + +(defun_header + lambda_list: (list_lit + (sym_lit) @local.definition.parameter)) + +(defun_header + keyword: (defun_keyword + "defmethod") + lambda_list: (list_lit + (list_lit + . + (sym_lit) + . + (sym_lit) @local.definition.type))) + +(defun_header + lambda_list: (list_lit + (list_lit + . + (sym_lit) @local.definition.parameter + . + (_)))) + +(sym_lit) @local.reference + +(defun) @local.scope + +((list_lit + . + (sym_lit) @_defvar + . + (sym_lit) @local.definition.var) + (#match? @_defvar "^(cl:)?(defvar|defparameter)$")) + +(list_lit + . + (sym_lit) @_deftest + . + (sym_lit) @local.definition.function + (#eq? @_deftest "deftest")) @local.scope + +(list_lit + . + (sym_lit) @_deftest + . + (sym_lit) @local.definition.function + (#eq? @_deftest "deftest")) @local.scope + +(for_clause + . + (sym_lit) @local.definition.var) + +(with_clause + . + (sym_lit) @local.definition.var) + +(loop_macro) @local.scope + +(list_lit + . + (sym_lit) @_let + (#match? @_let "(cl:|cffi:)?(with-accessors|with-foreign-objects|let[*]?)") + . + (list_lit + (list_lit + . + (sym_lit) @local.definition.var))) @local.scope + +(list_lit + . + (sym_lit) @_let + (#match? @_let "(cl:|alexandria:)?(with-gensyms|dotimes|with-foreign-object)") + . + (list_lit + . + (sym_lit) @local.definition.var)) @local.scope + +(list_lit + . + (kwd_lit) @_import_from + (#eq? @_import_from ":import-from") + . + (_) + (kwd_lit + (kwd_symbol) @local.definition.import)) + +(list_lit + . + (kwd_lit) @_import_from + (#eq? @_import_from ":import-from") + . + (_) + (sym_lit) @local.definition.import) + +(list_lit + . + (kwd_lit) @_use + (#eq? @_use ":use") + (kwd_lit + (kwd_symbol) @local.definition.import)) + +(list_lit + . + (kwd_lit) @_use + (#eq? @_use ":use") + (sym_lit) @local.definition.import) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cooklang/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cooklang/highlights.scm new file mode 100644 index 00000000..ca870dd4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cooklang/highlights.scm @@ -0,0 +1,22 @@ +(metadata) @comment + +(ingredient + "@" @punctuation.delimiter + (name)? @string.special.symbol + (amount + (quantity)? @number + (units)? @constant)?) + +(timer + "~" @punctuation.delimiter + (name)? @string.special.symbol + (amount + (quantity)? @number + (units)? @constant)?) + +(cookware + "#" @punctuation.delimiter + (name)? @string.special.symbol + (amount + (quantity)? @number + (units)? @constant)?) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/folds.scm new file mode 100644 index 00000000..2ce5ddb3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/folds.scm @@ -0,0 +1,5 @@ +[ + (object) + (array) + (assign_block) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/highlights.scm new file mode 100644 index 00000000..02929146 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/highlights.scm @@ -0,0 +1,26 @@ +"let" @keyword + +"in" @keyword + +[ + "{" + "}" + "[" + "]" +] @punctuation.bracket + +"." @punctuation.delimiter + +(input) @constant + +(comment) @comment @spell + +(string) @string + +(integer) @number + +(float) @number.float + +(boolean) @boolean + +(null) @keyword diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/indents.scm new file mode 100644 index 00000000..f1f5e04d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/indents.scm @@ -0,0 +1,24 @@ +[ + (assign_block + "{") + (object) + (array) +] @indent.begin + +(assign_block + "}" @indent.branch) + +(assign_block + "}" @indent.end) + +(object + "}" @indent.branch) + +(object + "}" @indent.end) + +(array + "]" @indent.branch) + +(array + "]" @indent.end) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/locals.scm new file mode 100644 index 00000000..7e78c4d2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/corn/locals.scm @@ -0,0 +1,13 @@ +; scopes +[ + (object) + (array) +] @local.scope + +; definitions +(assign_block + (assignment + (input) @local.definition.constant)) + +(value + (input) @local.reference) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/folds.scm new file mode 100644 index 00000000..02feec4e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/folds.scm @@ -0,0 +1,5 @@ +[ + (meta_map) + (map) + (array) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/highlights.scm new file mode 100644 index 00000000..9cc438ea --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/highlights.scm @@ -0,0 +1,54 @@ +; Literals +(string) @string + +(escape_sequence) @string.escape + +(hex_blob + "x" @character.special + (_) @string) + +(esc_blob + "b" @character.special + (_) @string) + +(datetime + "d" @character.special + (_) @string.special) + +(_ + key: (_) @property) + +(number) @number + +(float) @number.float + +(boolean) @boolean + +(null) @constant.builtin + +; Punctuation +[ + "," + ":" +] @punctuation.delimiter + +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "<" + ">" +] @punctuation.bracket + +("\"" @string + (#set! conceal "")) + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/indents.scm new file mode 100644 index 00000000..8ec2ff57 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/indents.scm @@ -0,0 +1,17 @@ +[ + (meta_map) + (map) + (imap) + (array) +] @indent.begin + +[ + "]" + "}" + ">" +] @indent.end @indent.branch + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/locals.scm new file mode 100644 index 00000000..2a4ba471 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpon/locals.scm @@ -0,0 +1,6 @@ +[ + (document) + (meta_map) + (map) + (array) +] @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/folds.scm new file mode 100644 index 00000000..f5f56648 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/folds.scm @@ -0,0 +1,14 @@ +; inherits: c + +[ + (for_range_loop) + (class_specifier) + (field_declaration + type: (enum_specifier) + default_value: (initializer_list)) + (template_declaration) + (namespace_definition) + (try_statement) + (catch_clause) + (lambda_expression) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/highlights.scm new file mode 100644 index 00000000..1779d59e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/highlights.scm @@ -0,0 +1,268 @@ +; inherits: c + +((identifier) @variable.member + (#lua-match? @variable.member "^m_.*$")) + +(parameter_declaration + declarator: (reference_declarator) @variable.parameter) + +; function(Foo ...foo) +(variadic_parameter_declaration + declarator: (variadic_declarator + (_) @variable.parameter)) + +; int foo = 0 +(optional_parameter_declaration + declarator: (_) @variable.parameter) + +;(field_expression) @variable.parameter ;; How to highlight this? +((field_expression + (field_identifier) @function.method) @_parent + (#has-parent? @_parent template_method function_declarator)) + +(field_declaration + (field_identifier) @variable.member) + +(field_initializer + (field_identifier) @property) + +(function_declarator + declarator: (field_identifier) @function.method) + +(concept_definition + name: (identifier) @type.definition) + +(alias_declaration + name: (type_identifier) @type.definition) + +(auto) @type.builtin + +(namespace_identifier) @module + +((namespace_identifier) @type + (#lua-match? @type "^[%u]")) + +(case_statement + value: (qualified_identifier + (identifier) @constant)) + +(using_declaration + . + "using" + . + "namespace" + . + [ + (qualified_identifier) + (identifier) + ] @module) + +(destructor_name + (identifier) @function.method) + +; functions +(function_declarator + (qualified_identifier + (identifier) @function)) + +(function_declarator + (qualified_identifier + (qualified_identifier + (identifier) @function))) + +(function_declarator + (qualified_identifier + (qualified_identifier + (qualified_identifier + (identifier) @function)))) + +((qualified_identifier + (qualified_identifier + (qualified_identifier + (qualified_identifier + (identifier) @function)))) @_parent + (#has-ancestor? @_parent function_declarator)) + +(function_declarator + (template_function + (identifier) @function)) + +(operator_name) @function + +"operator" @function + +"static_assert" @function.builtin + +(call_expression + (qualified_identifier + (identifier) @function.call)) + +(call_expression + (qualified_identifier + (qualified_identifier + (identifier) @function.call))) + +(call_expression + (qualified_identifier + (qualified_identifier + (qualified_identifier + (identifier) @function.call)))) + +((qualified_identifier + (qualified_identifier + (qualified_identifier + (qualified_identifier + (identifier) @function.call)))) @_parent + (#has-ancestor? @_parent call_expression)) + +(call_expression + (template_function + (identifier) @function.call)) + +(call_expression + (qualified_identifier + (template_function + (identifier) @function.call))) + +(call_expression + (qualified_identifier + (qualified_identifier + (template_function + (identifier) @function.call)))) + +(call_expression + (qualified_identifier + (qualified_identifier + (qualified_identifier + (template_function + (identifier) @function.call))))) + +((qualified_identifier + (qualified_identifier + (qualified_identifier + (qualified_identifier + (template_function + (identifier) @function.call))))) @_parent + (#has-ancestor? @_parent call_expression)) + +; methods +(function_declarator + (template_method + (field_identifier) @function.method)) + +(call_expression + (field_expression + (field_identifier) @function.method.call)) + +; constructors +((function_declarator + (qualified_identifier + (identifier) @constructor)) + (#lua-match? @constructor "^%u")) + +((call_expression + function: (identifier) @constructor) + (#lua-match? @constructor "^%u")) + +((call_expression + function: (qualified_identifier + name: (identifier) @constructor)) + (#lua-match? @constructor "^%u")) + +((call_expression + function: (field_expression + field: (field_identifier) @constructor)) + (#lua-match? @constructor "^%u")) + +; constructing a type in an initializer list: Constructor (): **SuperType (1)** +((field_initializer + (field_identifier) @constructor + (argument_list)) + (#lua-match? @constructor "^%u")) + +; Constants +(this) @variable.builtin + +(null + "nullptr" @constant.builtin) + +(true) @boolean + +(false) @boolean + +; Literals +(raw_string_literal) @string + +; Keywords +[ + "try" + "catch" + "noexcept" + "throw" +] @keyword.exception + +[ + "decltype" + "explicit" + "friend" + "override" + "using" + "requires" + "constexpr" +] @keyword + +[ + "class" + "namespace" + "template" + "typename" + "concept" +] @keyword.type + +[ + "co_await" + "co_yield" + "co_return" +] @keyword.coroutine + +[ + "public" + "private" + "protected" + "virtual" + "final" +] @keyword.modifier + +[ + "new" + "delete" + "xor" + "bitand" + "bitor" + "compl" + "not" + "xor_eq" + "and_eq" + "or_eq" + "not_eq" + "and" + "or" +] @keyword.operator + +"<=>" @operator + +"::" @punctuation.delimiter + +(template_argument_list + [ + "<" + ">" + ] @punctuation.bracket) + +(template_parameter_list + [ + "<" + ">" + ] @punctuation.bracket) + +(literal_suffix) @operator diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/indents.scm new file mode 100644 index 00000000..0782d226 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/indents.scm @@ -0,0 +1,8 @@ +; inherits: c + +(condition_clause) @indent.begin + +((field_initializer_list) @indent.begin + (#set! indent.start_at_same_line 1)) + +(access_specifier) @indent.branch diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/injections.scm new file mode 100644 index 00000000..6e165722 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cpp/injections.scm @@ -0,0 +1,13 @@ +((preproc_arg) @injection.content + (#set! injection.language "cpp")) + +((comment) @injection.content + (#set! injection.language "comment")) + +((comment) @injection.content + (#lua-match? @injection.content "/[*\/][!*\/] +(type_parameter_declaration + (type_identifier) @local.definition.type) + +(template_declaration) @local.scope + +; Namespaces +(namespace_definition + name: (namespace_identifier) @local.definition.namespace + body: (_) @local.scope) + +(namespace_definition + name: (nested_namespace_specifier) @local.definition.namespace + body: (_) @local.scope) + +((namespace_identifier) @local.reference + (#set! reference.kind "namespace")) + +; Function definitions +(template_function + name: (identifier) @local.definition.function) @local.scope + +(template_method + name: (field_identifier) @local.definition.method) @local.scope + +(function_declarator + declarator: (qualified_identifier + name: (identifier) @local.definition.function)) @local.scope + +(field_declaration + declarator: (function_declarator + (field_identifier) @local.definition.method)) + +(lambda_expression) @local.scope + +; Control structures +(try_statement + body: (_) @local.scope) + +(catch_clause) @local.scope + +(requires_expression) @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/folds.scm new file mode 100644 index 00000000..dc3c14df --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/folds.scm @@ -0,0 +1,4 @@ +[ + (rule_set) + (import_statement)+ +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/highlights.scm new file mode 100644 index 00000000..acc2b638 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/highlights.scm @@ -0,0 +1,107 @@ +[ + "@media" + "@charset" + "@namespace" + "@supports" + "@keyframes" + (at_keyword) +] @keyword.directive + +"@import" @keyword.import + +[ + (to) + (from) +] @keyword + +(comment) @comment @spell + +(tag_name) @tag + +(class_name) @type + +(id_name) @constant + +[ + (property_name) + (feature_name) +] @property + +[ + (nesting_selector) + (universal_selector) +] @character.special + +(function_name) @function + +[ + "~" + ">" + "+" + "-" + "*" + "/" + "=" + "^=" + "|=" + "~=" + "$=" + "*=" +] @operator + +[ + "and" + "or" + "not" + "only" +] @keyword.operator + +(important) @keyword.modifier + +(attribute_selector + (plain_value) @string) + +(pseudo_element_selector + "::" + (tag_name) @attribute) + +(pseudo_class_selector + (class_name) @attribute) + +(attribute_name) @tag.attribute + +(namespace_name) @module + +((property_name) @variable + (#lua-match? @variable "^[-][-]")) + +((plain_value) @variable + (#lua-match? @variable "^[-][-]")) + +[ + (string_value) + (color_value) + (unit) +] @string + +(integer_value) @number + +(float_value) @number.float + +[ + "#" + "," + "." + ":" + "::" + ";" +] @punctuation.delimiter + +[ + "{" + ")" + "(" + "}" + "[" + "]" +] @punctuation.bracket diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/indents.scm new file mode 100644 index 00000000..75e4a63a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/indents.scm @@ -0,0 +1,11 @@ +[ + (block) + (declaration) +] @indent.begin + +(block + "}" @indent.branch) + +"}" @indent.dedent + +(comment) @indent.ignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/css/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/csv/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/csv/highlights.scm new file mode 100644 index 00000000..de2213aa --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/csv/highlights.scm @@ -0,0 +1,3 @@ +; inherits: tsv + +"," @punctuation.delimiter diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/folds.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/folds.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/highlights.scm new file mode 100644 index 00000000..6605c5a7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/highlights.scm @@ -0,0 +1,17 @@ +; inherits: cpp + +[ + "<<<" + ">>>" +] @punctuation.bracket + +[ + "__host__" + "__device__" + "__global__" + "__managed__" + "__forceinline__" + "__noinline__" +] @keyword.modifier + +"__launch_bounds__" @keyword.modifier diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/indents.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/indents.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/injections.scm new file mode 100644 index 00000000..0259958c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/injections.scm @@ -0,0 +1,5 @@ +((preproc_arg) @injection.content + (#set! injection.language "cuda")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/locals.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cuda/locals.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/folds.scm new file mode 100644 index 00000000..934b59e6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/folds.scm @@ -0,0 +1,5 @@ +[ + (import_spec_list) + (field) + (string) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/highlights.scm new file mode 100644 index 00000000..27d4dadd --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/highlights.scm @@ -0,0 +1,164 @@ +; Includes +[ + "package" + "import" +] @keyword.import + +; Namespaces +(package_identifier) @module + +(import_spec + [ + "." + "_" + ] @punctuation.special) + +[ + (attr_path) + (package_path) +] @string.special.url ; In attributes + +; Attributes +(attribute) @attribute + +; Conditionals +"if" @keyword.conditional + +; Repeats +"for" @keyword.repeat + +(for_clause + "_" @punctuation.special) + +; Keywords +"let" @keyword + +"in" @keyword.operator + +; Operators +[ + "+" + "-" + "*" + "/" + "|" + "&" + "||" + "&&" + "==" + "!=" + "<" + "<=" + ">" + ">=" + "=~" + "!~" + "!" + "=" +] @operator + +; Fields & Properties +(field + (label + (identifier) @variable.member)) + +(selector_expression + (_) + (identifier) @property) + +; Functions +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (selector_expression + (_) + (identifier) @function.call)) + +(call_expression + function: (builtin_function) @function.call) + +(builtin_function) @function.builtin + +; Variables +(identifier) @variable + +; Types +(primitive_type) @type.builtin + +((identifier) @type + (#lua-match? @type "^_?#")) + +[ + (slice_type) + (pointer_type) +] @type ; In attributes + +; Punctuation +[ + "," + ":" +] @punctuation.delimiter + +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "<" + ">" +] @punctuation.bracket + +[ + (ellipsis) + "?" +] @punctuation.special + +; Literals +(string) @string + +[ + (escape_char) + (escape_unicode) +] @string.escape + +(number) @number + +(float) @number.float + +(si_unit + (float) + (_) @string.special.symbol) + +(boolean) @boolean + +[ + (null) + (top) + (bottom) +] @constant.builtin + +; Interpolations +(interpolation + "\\(" @punctuation.special + (_) + ")" @punctuation.special) @none + +(interpolation + "\\(" + (identifier) @variable + ")") + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/indents.scm new file mode 100644 index 00000000..cef2345c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/indents.scm @@ -0,0 +1,30 @@ +[ + (import_spec_list) + (field) +] @indent.begin + +[ + "}" + "]" + ")" +] @indent.end + +[ + "{" + "}" +] @indent.branch + +[ + "[" + "]" +] @indent.branch + +[ + "(" + ")" +] @indent.branch + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/locals.scm new file mode 100644 index 00000000..b2a8972f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/cue/locals.scm @@ -0,0 +1,31 @@ +; Scopes +[ + (source_file) + (field) + (for_clause) +] @local.scope + +; References +(identifier) @local.reference + +; Definitions +(import_spec + path: (string) @local.definition.import) + +(field + (label + (identifier) @local.definition.field)) + +(package_identifier) @local.definition.namespace + +(for_clause + (identifier) @local.definition.var + (expression)) + +(for_clause + (identifier) + (identifier) @local.definition.var + (expression)) + +(let_clause + (identifier) @local.definition.var) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/folds.scm new file mode 100644 index 00000000..49d6256f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/folds.scm @@ -0,0 +1,4 @@ +[ + (block_statement) + (aggregate_body) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/highlights.scm new file mode 100644 index 00000000..11d08a1b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/highlights.scm @@ -0,0 +1,374 @@ +; Keywords +[ + (directive) + (shebang) +] @keyword.directive + +[ + (import) + (module) +] @keyword.import + +[ + (alias) + (asm) + (class) + (delegate) + (delete) + (enum) + (interface) + (invariant) + (mixin) + (pragma) + (struct) + (template) + (union) + (unittest) + (version) + (with) + (traits) + (vector) + (parameters_) + (default) + (goto) +] @keyword + +(function) @keyword.function + +(synchronized) @keyword.coroutine + +[ + (if) + (else) + (switch) + (case) + (break) +] @keyword.conditional + +[ + (do) + (for) + (foreach) + (foreach_reverse) + (while) + (continue) +] @keyword.repeat + +(return) @keyword.return + +[ + (abstract) + (deprecated) + (private) + (protected) + (public) + (package) + (immutable) + (final) + (const) + (override) + (static) +] @keyword.modifier + +[ + (assert) + (try) + (catch) + (finally) + (throw) + (nothrow) +] @keyword.exception + +[ + (cast) + (new) + (in) + (is) + (not_in) + (not_is) + (typeid) + (typeof) +] @keyword.operator + +[ + (lazy) + (align) + (extern) + (scope) + (ref) + (pure) + (export) + (shared) + (gshared) + (out) + (inout) +] @keyword.modifier + +(parameter_attribute + (return) @keyword.modifier) + +(parameter_attribute + (in) @keyword.modifier) + +(parameter_attribute + (out) @keyword.modifier) + +(debug) @keyword.debug + +; Operators +[ + "/=" + "/" + ".." + "&" + "&=" + "&&" + "|" + "|=" + "||" + "-" + "-=" + "--" + "+" + "+=" + "++" + "<" + "<=" + "<<" + "<<=" + ">" + ">=" + ">>=" + ">>>=" + ">>" + ">>>" + "!" + "!=" + "$" + "=" + "==" + "*" + "*=" + "%" + "%=" + "^" + "^=" + "^^" + "^^=" + "~" + "~=" + "@" +] @operator + +; Variables +(identifier) @variable + +[ + "exit" + "success" + "failure" + (this) + (super) +] @variable.builtin + +(linkage_attribute + "(" + _ @variable.builtin + ")") + +; Modules +(module_fqn + (identifier) @module) + +; Attributes +(at_attribute + (identifier) @attribute) + +; Constants +(enum_member + (identifier) @constant) + +(manifest_declarator + . + (identifier) @constant) + +; Members +(aggregate_body + (variable_declaration + (declarator + (identifier) @variable.member))) + +(property_expression + "." + (identifier) @variable.member) + +(type + "." + (identifier) @variable.member) + +; Types +(class_declaration + (class) + . + (identifier) @type) + +(struct_declaration + (struct) + . + (identifier) @type) + +(union_declaration + (union) + . + (identifier) @type) + +(enum_declaration + (enum) + . + (identifier) @type) + +(alias_declaration + (alias) + . + (identifier) @type) + +((identifier) @type + (#lua-match? @type "^[A-Z].*")) + +(type + . + (identifier) @type .) + +[ + (auto) + (void) + (bool) + (byte) + (ubyte) + (char) + (short) + (ushort) + (wchar) + (dchar) + (int) + (uint) + (long) + (ulong) + (real) + (double) + (float) + (cent) + (ucent) + (ireal) + (idouble) + (ifloat) + (creal) + (double) + (cfloat) +] @type.builtin + +; Functions +(function_declaration + (identifier) @function) + +(call_expression + (identifier) @function) + +(call_expression + (type + (identifier) @function .)) + +(call_expression + (property_expression + (call_expression) + (identifier) @function .)) + +; Parameters +(parameter + (_) + (identifier) @variable.parameter) + +(function_literal + "(" + (type + (identifier) @variable.parameter)) + +; Constructors +(constructor + (this) @constructor) + +(destructor + (this) @constructor) + +(postblit + . + (this) @constructor) + +; Punctuation +[ + ";" + "." + ":" + "," + "=>" +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +"..." @punctuation.special + +; Ternaries +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +; Labels +(label + (identifier) @label) + +(goto_statement + (identifier) @label) + +; Literals +(string_literal) @string + +[ + (int_literal) + (float_literal) +] @number + +(char_literal) @character + +[ + (true) + (false) +] @boolean + +[ + (null) + (special_keyword) +] @constant.builtin + +; Comments +(comment) @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^///[^/]")) + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^///$")) + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$")) + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^/[+][+][^+].*[+]/$")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/indents.scm new file mode 100644 index 00000000..c89b4e91 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/indents.scm @@ -0,0 +1,24 @@ +[ + (parameters) + (template_parameters) + (expression_statement) + (aggregate_body) + (function_body) + (scope_statement) + (block_statement) + (case_statement) +] @indent.begin + +(comment) @indent.auto + +[ + (case) + (default) + "}" + "]" +] @indent.branch + +[ + (directive) + (shebang) +] @indent.zero diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/injections.scm new file mode 100644 index 00000000..16334810 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/injections.scm @@ -0,0 +1,18 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((call_expression + (type) @_printf + (arguments + "(" + . + (expression + (string_literal) @injection.content))) + (#eq? @_printf "printf") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "printf")) + +; TODO: uncomment when asm is added +; ((asm_inline) @injection.content +; (#set! injection.language "asm") +; (#set! injection.combined)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/locals.scm new file mode 100644 index 00000000..1d7d617d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/d/locals.scm @@ -0,0 +1,79 @@ +; Scopes +[ + (source_file) + (block_statement) + (aggregate_body) +] @local.scope + +; References +(identifier) @local.reference + +; Definitions +(module_def + (module_declaration + (module_fqn) @local.definition.namespace) + (#set! "definition.namespace.scope" "global")) + +(enum_declaration + (enum_member + . + (identifier) @local.definition.enum)) + +(class_declaration + (class) + . + (identifier) @local.definition.type) + +(struct_declaration + (struct) + . + (identifier) @local.definition.type) + +(union_declaration + (union) + . + (identifier) @local.definition.type) + +(enum_declaration + (enum) + . + (identifier) @local.definition.type) + +(alias_declaration + (alias_initializer + . + (identifier) @local.definition.type)) + +(constructor + (this) @local.definition.method) + +(destructor + (this) @local.definition.method) + +(postblit + (this) @local.definition.method) + +(aggregate_body + (function_declaration + (identifier) @local.definition.method)) + +(manifest_declarator + . + (identifier) @local.definition.constant) + +(anonymous_enum_declaration + (enum_member + . + (identifier) @local.definition.constant)) + +(variable_declaration + (declarator + (identifier) @local.definition.var)) + +(aggregate_body + (variable_declaration + (declarator + (identifier) @local.definition.field))) + +(function_declaration + (identifier) @local.definition.function) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/folds.scm new file mode 100644 index 00000000..8dc4e781 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/folds.scm @@ -0,0 +1,12 @@ +[ + (class_definition) + (enum_declaration) + (extension_declaration) + (arguments) + (function_body) + (block) + (switch_block) + (list_literal) + (set_or_map_literal) + (string_literal) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/highlights.scm new file mode 100644 index 00000000..142c6e19 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/highlights.scm @@ -0,0 +1,279 @@ +(dotted_identifier_list) @string + +; Methods +; -------------------- +(super) @function + +; TODO: add method/call_expression to grammar and +; distinguish method call from variable access +(function_expression_body + (identifier) @function) + +; ((identifier)(selector (argument_part)) @function) +; NOTE: This query is a bit of a work around for the fact that the dart grammar doesn't +; specifically identify a node as a function call +(((identifier) @function + (#lua-match? @function "^_?[%l]")) + . + (selector + . + (argument_part))) @function + +; Annotations +; -------------------- +(annotation + name: (identifier) @attribute) + +; Operators and Tokens +; -------------------- +(template_substitution + "$" @punctuation.special + "{" @punctuation.special + "}" @punctuation.special) @none + +(template_substitution + "$" @punctuation.special + (identifier_dollar_escaped) @variable) @none + +(escape_sequence) @string.escape + +[ + "@" + "=>" + ".." + "??" + "==" + "?" + ":" + "&&" + "%" + "<" + ">" + "=" + ">=" + "<=" + "||" + (multiplicative_operator) + (increment_operator) + (is_operator) + (prefix_operator) + (equality_operator) + (additive_operator) +] @operator + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +; Delimiters +; -------------------- +[ + ";" + "." + "," +] @punctuation.delimiter + +; Types +; -------------------- +(class_definition + name: (identifier) @type) + +(constructor_signature + name: (identifier) @type) + +(scoped_identifier + scope: (identifier) @type) + +(function_signature + name: (identifier) @function.method) + +(getter_signature + (identifier) @function.method) + +(setter_signature + name: (identifier) @function.method) + +(enum_declaration + name: (identifier) @type) + +(enum_constant + name: (identifier) @type) + +(void_type) @type + +((scoped_identifier + scope: (identifier) @type + name: (identifier) @type) + (#lua-match? @type "^[%u%l]")) + +(type_identifier) @type + +(type_alias + (type_identifier) @type.definition) + +; Variables +; -------------------- +; var keyword +(inferred_type) @keyword + +((identifier) @type + (#lua-match? @type "^_?[%u].*[%l]")) ; catch Classes or IClasses not CLASSES + +"Function" @type + +; properties +(unconditional_assignable_selector + (identifier) @property) + +(conditional_assignable_selector + (identifier) @property) + +; assignments +(assignment_expression + left: (assignable_expression) @variable) + +(this) @variable.builtin + +; Parameters +; -------------------- +(formal_parameter + name: (identifier) @variable.parameter) + +(named_argument + (label + (identifier) @variable.parameter)) + +; Literals +; -------------------- +[ + (hex_integer_literal) + (decimal_integer_literal) + (decimal_floating_point_literal) + ; TODO: inaccessible nodes + ; (octal_integer_literal) + ; (hex_floating_point_literal) +] @number + +(symbol_literal) @string.special.symbol + +(string_literal) @string + +(true) @boolean + +(false) @boolean + +(null_literal) @constant.builtin + +(comment) @comment @spell + +(documentation_comment) @comment.documentation @spell + +; Keywords +; -------------------- +[ + "import" + "library" + "export" + "as" + "show" + "hide" +] @keyword.import + +; Reserved words (cannot be used as identifiers) +[ + ; TODO: + ; "rethrow" cannot be targeted at all and seems to be an invisible node + ; TODO: + ; the assert keyword cannot be specifically targeted + ; because the grammar selects the whole node or the content + ; of the assertion not just the keyword + ; assert + (case_builtin) + "late" + "required" + "on" + "extends" + "in" + "is" + "new" + "super" + "with" +] @keyword + +[ + "class" + "enum" + "extension" +] @keyword.type + +"return" @keyword.return + +; Built in identifiers: +; alone these are marked as keywords +[ + "deferred" + "factory" + "get" + "implements" + "interface" + "library" + "operator" + "mixin" + "part" + "set" + "typedef" +] @keyword + +[ + "async" + "async*" + "sync*" + "await" + "yield" +] @keyword.coroutine + +[ + (const_builtin) + (final_builtin) + "abstract" + "covariant" + "dynamic" + "external" + "static" + "final" + "base" + "sealed" +] @keyword.modifier + +; when used as an identifier: +((identifier) @variable.builtin + (#any-of? @variable.builtin + "abstract" "as" "covariant" "deferred" "dynamic" "export" "external" "factory" "Function" "get" + "implements" "import" "interface" "library" "operator" "mixin" "part" "set" "static" "typedef")) + +[ + "if" + "else" + "switch" + "default" +] @keyword.conditional + +[ + "try" + "throw" + "catch" + "finally" + (break_statement) +] @keyword.exception + +[ + "do" + "while" + "continue" + "for" +] @keyword.repeat diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/indents.scm new file mode 100644 index 00000000..03d9464b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/indents.scm @@ -0,0 +1,49 @@ +[ + (class_body) + (function_body) + (function_expression_body) + (declaration + (initializers)) + (switch_block) + (formal_parameter_list) + (formal_parameter) + (list_literal) + (return_statement) + (arguments) + (try_statement) +] @indent.begin + +(switch_block + (_) @indent.begin + (#set! indent.immediate 1) + (#set! indent.start_at_same_line 1)) + +[ + (switch_statement_case) + (switch_statement_default) +] @indent.branch + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @indent.branch + +"}" @indent.end + +(return_statement + ";" @indent.end) + +(break_statement + ";" @indent.end) + +(comment) @indent.ignore + +; dedenting the else block is painfully slow; replace with simpler strategy +; (if_statement) @indent.begin +; (if_statement +; (block) @indent.branch) +(if_statement) @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/locals.scm new file mode 100644 index 00000000..3e3beb58 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dart/locals.scm @@ -0,0 +1,32 @@ +; Definitions +(function_signature + name: (identifier) @local.definition.function) + +(formal_parameter + name: (identifier) @local.definition.parameter) + +(initialized_variable_definition + name: (identifier) @local.definition.var) + +(initialized_identifier + (identifier) @local.definition.var) + +(static_final_declaration + (identifier) @local.definition.var) + +; References +(identifier) @local.reference + +; Scopes +(class_definition + body: (_) @local.scope) + +[ + (block) + (if_statement) + (for_statement) + (while_statement) + (try_statement) + (catch_clause) + (finally_clause) +] @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/folds.scm new file mode 100644 index 00000000..206c4bed --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/folds.scm @@ -0,0 +1 @@ +(node) @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/highlights.scm new file mode 100644 index 00000000..74ec89af --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/highlights.scm @@ -0,0 +1,61 @@ +(comment) @comment @spell + +[ + (preproc_include) + (dtsi_include) +] @keyword.import + +(preproc_def) @constant.macro + +(preproc_function_def) @function.macro + +[ + (memory_reservation) + (file_version) +] @attribute + +[ + (string_literal) + (byte_string_literal) + (system_lib_string) +] @string + +(integer_literal) @number + +(identifier) @variable + +(node + (identifier) @module) + +(property + (identifier) @property) + +(node + label: (_) @label) + +(call_expression + (identifier) @function.macro) + +(reference) @label ; referencing labeled_item.identifier + +(unit_address) @constant + +"=" @operator + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "<" + ">" +] @punctuation.bracket + +[ + ";" + ":" + "," + "@" +] @punctuation.delimiter diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/indents.scm new file mode 100644 index 00000000..9740060c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/indents.scm @@ -0,0 +1,12 @@ +[ + (node) + (property) + (integer_cells) +] @indent.begin + +[ + "}" + ">" +] @indent.branch + +(comment) @indent.ignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/locals.scm new file mode 100644 index 00000000..e33a81df --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/devicetree/locals.scm @@ -0,0 +1,4 @@ +[ + (node) + (integer_cells) +] @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dhall/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dhall/folds.scm new file mode 100644 index 00000000..bc92797b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dhall/folds.scm @@ -0,0 +1,10 @@ +[ + (let_binding) + (application_expression) + (lambda_expression) + (record_type) + (union_type) + (list_literal) + (record_literal) + (block_comment) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dhall/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dhall/highlights.scm new file mode 100644 index 00000000..efd7fedf --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dhall/highlights.scm @@ -0,0 +1,193 @@ +; Text +; Imports +(missing_import) @keyword.import + +(local_import) @string.special.path + +(http_import) @string.special.url + +[ + (env_variable) + (import_hash) +] @string.special + +[ + (import_as_location) + (import_as_text) +] @type + +; Types +([ + (let_binding + (label) @type) + (union_type_entry + (label) @type) +] + (#lua-match? @type "^%u")) + +((primitive_expression + (identifier + (label) @type) + (selector + (label) @type)) @variable + (#lua-match? @variable "^[A-Z][^.]*$")) + +; Parameters +(lambda_expression + label: (label) @variable.parameter) + +; Variables +(label) @variable + +(identifier + [ + (label) @variable + (de_bruijn_index) @operator + ]) + +(let_binding + label: (label) @variable) + +; Fields +(record_literal_entry + (label) @variable.member) + +(record_type_entry + (label) @variable.member) + +(selector + (selector_dot) + (_) @variable.member) + +; Keywords +(env_import) @keyword + +[ + "let" + "in" + "assert" +] @keyword + +[ + "using" + "as" + "with" +] @keyword.operator + +; Operators +[ + (type_operator) + (assign_operator) + (lambda_operator) + (arrow_operator) + (infix_operator) + (completion_operator) + (assert_operator) + (forall_operator) + (empty_record_literal) +] @operator + +; Builtins +(builtin_function) @function.builtin + +(builtin + [ + "Natural" + "Natural/build" + "Natural/fold" + "Natural/isZero" + "Natural/even" + "Natural/odd" + "Natural/subtract" + "Natural/toInteger" + "Natural/show" + "Integer" + "Integer/toDouble" + "Integer/show" + "Integer/negate" + "Integer/clamp" + "Double" + "Double/show" + "List" + "List/build" + "List/fold" + "List/length" + "List/head" + "List/last" + "List/indexed" + "List/reverse" + "Text" + "Text/show" + "Text/replace" + "Optional" + "Date" + "Time" + "TimeZone" + "Type" + "Kind" + "Sort" + ] @type.builtin) + +; Punctuation +[ + "," + "|" +] @punctuation.delimiter + +(selector_dot) @punctuation.delimiter + +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "<" + ">" +] @punctuation.bracket + +; Conditionals +[ + "if" + "then" + "else" +] @keyword.conditional + +; Literals +(text_literal) @string + +(interpolation + "}" @string) + +[ + (double_quote_escaped) + (single_quote_escaped) +] @string.escape + +[ + (integer_literal) + (natural_literal) +] @number + +(double_literal) @number.float + +(boolean_literal) @boolean + +(builtin + "None") @constant.builtin + +; Comments +[ + (line_comment) + (block_comment) +] @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dhall/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dhall/injections.scm new file mode 100644 index 00000000..3cd6aac8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dhall/injections.scm @@ -0,0 +1,5 @@ +([ + (line_comment) + (block_comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/diff/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/diff/highlights.scm new file mode 100644 index 00000000..44506c05 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/diff/highlights.scm @@ -0,0 +1,46 @@ +[ + (addition) + (new_file) +] @diff.plus + +[ + (deletion) + (old_file) +] @diff.minus + +(commit) @constant + +(location) @attribute + +(command + "diff" @function + (argument) @variable.parameter) + +(filename) @string.special.path + +(mode) @number + +[ + ".." + "+" + "++" + "+++" + "++++" + "-" + "--" + "---" + "----" +] @punctuation.special + +[ + (binary_change) + (similarity) + (file_change) +] @label + +(index + "index" @keyword) + +(similarity + (score) @number + "%" @number) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/disassembly/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/disassembly/highlights.scm new file mode 100644 index 00000000..b1ece9ab --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/disassembly/highlights.scm @@ -0,0 +1,30 @@ +(byte) @constant + +[ + (address) + (hexadecimal) + (integer) +] @number + +(identifier) @variable + +(bad_instruction) @comment.warning + +(code_location + (identifier) @function.call) + +(comment) @comment + +(instruction) @function + +(memory_dump) @string + +[ + "<" + ">" +] @punctuation.special + +[ + "+" + ":" +] @punctuation.delimiter diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/disassembly/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/disassembly/injections.scm new file mode 100644 index 00000000..9fb52daf --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/disassembly/injections.scm @@ -0,0 +1,6 @@ +; TODO: https://github.com/nvim-treesitter/nvim-treesitter/pull/5548#issuecomment-1773707396 +; +; To be added once a compatible Assembly parser is merged into nvim-treesitter +; +; ((instruction) @injection.content +; (#set! injection.language "asm")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/folds.scm new file mode 100644 index 00000000..94f3724e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/folds.scm @@ -0,0 +1,7 @@ +[ + (section) + (code_block) + (raw_block) + (list) + (div) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/highlights.scm new file mode 100644 index 00000000..e788891e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/highlights.scm @@ -0,0 +1,396 @@ +(heading1) @markup.heading.1 + +(heading2) @markup.heading.2 + +(heading3) @markup.heading.3 + +(heading4) @markup.heading.4 + +(heading5) @markup.heading.5 + +(heading6) @markup.heading.6 + +(thematic_break) @string.special + +[ + (div_marker_begin) + (div_marker_end) +] @punctuation.delimiter + +([ + (code_block) + (raw_block) + (frontmatter) +] @markup.raw.block + (#set! "priority" 90)) + +; Remove @markup.raw for code with a language spec +(code_block + . + (code_block_marker_begin) + (language) + (code) @none + (#set! "priority" 90)) + +[ + (code_block_marker_begin) + (code_block_marker_end) + (raw_block_marker_begin) + (raw_block_marker_end) +] @punctuation.delimiter + +(language) @attribute + +(inline_attribute + _ @conceal + (#set! conceal "")) + +((language_marker) @punctuation.delimiter + (#set! conceal "")) + +(block_quote) @markup.quote + +(block_quote_marker) @punctuation.special + +(table_header) @markup.heading + +(table_header + "|" @punctuation.special) + +(table_row + "|" @punctuation.special) + +(table_separator) @punctuation.special + +(table_caption + (marker) @punctuation.special) + +(table_caption) @markup.italic + +[ + (list_marker_dash) + (list_marker_plus) + (list_marker_star) + (list_marker_definition) + (list_marker_decimal_period) + (list_marker_decimal_paren) + (list_marker_decimal_parens) + (list_marker_lower_alpha_period) + (list_marker_lower_alpha_paren) + (list_marker_lower_alpha_parens) + (list_marker_upper_alpha_period) + (list_marker_upper_alpha_paren) + (list_marker_upper_alpha_parens) + (list_marker_lower_roman_period) + (list_marker_lower_roman_paren) + (list_marker_lower_roman_parens) + (list_marker_upper_roman_period) + (list_marker_upper_roman_paren) + (list_marker_upper_roman_parens) +] @markup.list + +(list_marker_task + (unchecked)) @markup.list.unchecked + +(list_marker_task + (checked)) @markup.list.checked + +; Colorize `x` in `[x]` +((checked) @constant.builtin + (#offset! @constant.builtin 0 1 0 -1)) + +[ + (ellipsis) + (en_dash) + (em_dash) + (quotation_marks) +] @string.special + +(list_item + (term) @type.definition) + +; Conceal { and } but leave " and ' +((quotation_marks) @string.special + (#any-of? @string.special "\"}" "'}") + (#offset! @string.special 0 1 0 0) + (#set! conceal "")) + +((quotation_marks) @string.special + (#any-of? @string.special "\\\"" "\\'" "{'" "{\"") + (#offset! @string.special 0 0 0 -1) + (#set! conceal "")) + +[ + (hard_line_break) + (backslash_escape) +] @string.escape + +; Only conceal \ but leave escaped character. +((backslash_escape) @string.escape + (#offset! @string.escape 0 0 0 -1) + (#set! conceal "")) + +(frontmatter_marker) @punctuation.delimiter + +(emphasis) @markup.italic + +(strong) @markup.strong + +(symbol) @string.special.symbol + +(insert) @markup.underline + +(delete) @markup.strikethrough + +[ + (highlighted) + (superscript) + (subscript) +] @string.special + +; We need to target tokens specifically because `{=` etc can exist as fallback symbols in +; regular text, which we don't want to highlight or conceal. +(highlighted + [ + "{=" + "=}" + ] @punctuation.delimiter + (#set! conceal "")) + +(insert + [ + "{+" + "+}" + ] @punctuation.delimiter + (#set! conceal "")) + +(delete + [ + "{-" + "-}" + ] @punctuation.delimiter + (#set! conceal "")) + +(superscript + [ + "^" + "{^" + "^}" + ] @punctuation.delimiter + (#set! conceal "")) + +(subscript + [ + "~" + "{~" + "~}" + ] @punctuation.delimiter + (#set! conceal "")) + +([ + (emphasis_begin) + (emphasis_end) + (strong_begin) + (strong_end) + (verbatim_marker_begin) + (verbatim_marker_end) + (math_marker) + (math_marker_begin) + (math_marker_end) + (raw_inline_attribute) + (raw_inline_marker_begin) + (raw_inline_marker_end) +] @punctuation.delimiter + (#set! conceal "")) + +((math) @markup.math + (#set! "priority" 90)) + +(verbatim) @markup.raw + +((raw_inline) @markup.raw + (#set! "priority" 90)) + +(comment + "%" @comment + (#set! conceal "")) + +(span + [ + "[" + "]" + ] @punctuation.bracket) + +(inline_attribute + [ + "{" + "}" + ] @punctuation.bracket) + +(block_attribute + [ + "{" + "}" + ] @punctuation.bracket) + +[ + (class) + (class_name) +] @type + +(identifier) @tag + +(key_value + "=" @operator) + +(key_value + (key) @property) + +(key_value + (value) @string) + +(link_text + [ + "[" + "]" + ] @punctuation.bracket + (#set! conceal "")) + +(autolink + [ + "<" + ">" + ] @punctuation.bracket + (#set! conceal "")) + +(inline_link + (inline_link_destination) @markup.link.url + (#set! conceal "")) + +(link_reference_definition + ":" @punctuation.special) + +(full_reference_link + (link_text) @markup.link) + +(full_reference_link + (link_label) @markup.link.label + (#set! conceal "")) + +(collapsed_reference_link + "[]" @punctuation.bracket + (#set! conceal "")) + +(full_reference_link + [ + "[" + "]" + ] @punctuation.bracket + (#set! conceal "")) + +(collapsed_reference_link + (link_text) @markup.link) + +(collapsed_reference_link + (link_text) @markup.link.label) + +(inline_link + (link_text) @markup.link) + +(full_reference_image + (link_label) @markup.link.label) + +(full_reference_image + [ + "![" + "[" + "]" + ] @punctuation.bracket) + +(collapsed_reference_image + [ + "![" + "]" + ] @punctuation.bracket) + +(inline_image + [ + "![" + "]" + ] @punctuation.bracket) + +(image_description) @markup.italic + +(image_description + [ + "[" + "]" + ] @punctuation.bracket) + +(link_reference_definition + [ + "[" + "]" + ] @punctuation.bracket) + +(link_reference_definition + (link_label) @markup.link.label) + +(inline_link_destination + [ + "(" + ")" + ] @punctuation.bracket) + +[ + (autolink) + (inline_link_destination) + (link_destination) + (link_reference_definition) +] @markup.link.url + +(footnote + (reference_label) @markup.link.label) + +(footnote_reference + (reference_label) @markup.link.label) + +[ + (footnote_marker_begin) + (footnote_marker_end) +] @punctuation.bracket + +(todo) @comment.todo + +(note) @comment.note + +(fixme) @comment.error + +[ + (paragraph) + (comment) + (table_cell) +] @spell + +[ + (autolink) + (inline_link_destination) + (link_destination) + (code_block) + (raw_block) + (math) + (raw_inline) + (verbatim) + (reference_label) + (class) + (class_name) + (identifier) + (key_value) + (frontmatter) +] @nospell + +(full_reference_link + (link_label) @nospell) + +(full_reference_image + (link_label) @nospell) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/indents.scm new file mode 100644 index 00000000..3b1a56eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/indents.scm @@ -0,0 +1,10 @@ +; The intention here is to rely on Neovims `autoindent` setting. +; This allows us to not indent after just a single list item +; so we can create narrow lists quickly, but indent blocks inside list items +; to the previous paragraph. +(list_item_content) @indent.auto + +(footnote_content) @indent.align + +((table_caption) @indent.begin + (#set! indent.immediate 1)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/injections.scm new file mode 100644 index 00000000..b590e084 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/injections.scm @@ -0,0 +1,17 @@ +(code_block + (language) @injection.language + (code) @injection.content) + +(raw_block + (raw_block_info + (language) @injection.language) + (content) @injection.content) + +(raw_inline + (content) @injection.content + (raw_inline_attribute + (language) @injection.language)) + +(frontmatter + (language) @injection.language + (frontmatter_content) @injection.content) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/locals.scm new file mode 100644 index 00000000..1ac27529 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/djot/locals.scm @@ -0,0 +1,17 @@ +(link_reference_definition + (link_label) @local.definition) + +(footnote + (reference_label) @local.definition) + +(collapsed_reference_link + (link_text) @local.reference) + +(full_reference_link + (link_label) @local.reference) + +(full_reference_image + (link_label) @local.reference) + +(footnote_reference + (reference_label) @local.reference) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dockerfile/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dockerfile/highlights.scm new file mode 100644 index 00000000..5a8f0c7c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dockerfile/highlights.scm @@ -0,0 +1,68 @@ +[ + "FROM" + "AS" + "RUN" + "CMD" + "LABEL" + "EXPOSE" + "ENV" + "ADD" + "COPY" + "ENTRYPOINT" + "VOLUME" + "USER" + "WORKDIR" + "ARG" + "ONBUILD" + "STOPSIGNAL" + "HEALTHCHECK" + "SHELL" + "MAINTAINER" + "CROSS_BUILD" +] @keyword + +[ + ":" + "@" +] @operator + +(comment) @comment @spell + +(image_spec + (image_tag + ":" @punctuation.special) + (image_digest + "@" @punctuation.special)) + +(double_quoted_string) @string + +[ + (heredoc_marker) + (heredoc_end) +] @label + +((heredoc_block + (heredoc_line) @string) + (#set! "priority" 90)) + +(expansion + [ + "$" + "{" + "}" + ] @punctuation.special) + +((variable) @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) + +(arg_instruction + . + (unquoted_string) @property) + +(env_instruction + (env_pair + . + (unquoted_string) @property)) + +(expose_instruction + (expose_port) @number) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dockerfile/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dockerfile/injections.scm new file mode 100644 index 00000000..5d3bbffb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dockerfile/injections.scm @@ -0,0 +1,12 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((shell_command + (shell_fragment) @injection.content) + (#set! injection.language "bash") + (#set! injection.combined)) + +((run_instruction + (heredoc_block) @injection.content) + (#set! injection.language "bash") + (#set! injection.include-children)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dot/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dot/highlights.scm new file mode 100644 index 00000000..75ad9227 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dot/highlights.scm @@ -0,0 +1,49 @@ +(identifier) @type + +[ + "strict" + "graph" + "digraph" + "subgraph" + "node" + "edge" +] @keyword + +(string_literal) @string + +(number_literal) @number + +[ + (edgeop) + (operator) +] @operator + +[ + "," + ";" +] @punctuation.delimiter + +[ + "{" + "}" + "[" + "]" + "<" + ">" +] @punctuation.bracket + +(subgraph + id: (id + (identifier) @module)) + +(attribute + name: (id + (identifier) @variable.member)) + +(attribute + value: (id + (identifier) @constant)) + +(comment) @comment @spell + +(preproc) @keyword.directive diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dot/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dot/indents.scm new file mode 100644 index 00000000..a951e551 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dot/indents.scm @@ -0,0 +1,9 @@ +[ + (block) + (attr_list) +] @indent.begin + +[ + "}" + "]" +] @indent.branch @indent.end diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dot/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dot/injections.scm new file mode 100644 index 00000000..4fe39a8b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dot/injections.scm @@ -0,0 +1,5 @@ +((html_internal) @injection.content + (#set! injection.language "html")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/doxygen/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/doxygen/highlights.scm new file mode 100644 index 00000000..da39bdd8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/doxygen/highlights.scm @@ -0,0 +1,61 @@ +((tag_name) @keyword + (#set! "priority" 105)) + +[ + "@code" + "@endcode" +] @keyword + +(identifier) @variable + +((tag + (tag_name) @_param + (identifier) @variable.parameter) + (#any-of? @_param "@param" "\\param")) + +(function + (identifier) @function) + +(function_link) @function + +(emphasis) @markup.italic + +[ + "\\a" + "\\c" +] @tag + +(code_block_language) @label + +[ + "in" + "out" + "inout" +] @keyword.modifier + +"~" @operator + +[ + "" + "" +] @tag + +[ + "." + "," + "::" + (code_block_start) + (code_block_end) +] @punctuation.delimiter + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +(code_block_content) @none diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/doxygen/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/doxygen/indents.scm new file mode 100644 index 00000000..ef30f1e7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/doxygen/indents.scm @@ -0,0 +1 @@ +(document) @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/doxygen/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/doxygen/injections.scm new file mode 100644 index 00000000..994f535a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/doxygen/injections.scm @@ -0,0 +1,15 @@ +((type) @injection.content + (#set! injection.parent)) + +([ + (function_link) + (code) +] @injection.content + (#set! injection.parent)) + +((link) @injection.content + (#set! injection.language "html")) + +(code_block + (code_block_language) @injection.language + (code_block_content) @injection.content) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/folds.scm new file mode 100644 index 00000000..b1bce4ff --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/folds.scm @@ -0,0 +1,4 @@ +[ + (conditionalSect) + (Comment) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/highlights.scm new file mode 100644 index 00000000..7bc7a7ed --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/highlights.scm @@ -0,0 +1,153 @@ +; XML declaration +(XMLDecl + "xml" @keyword.directive) + +(XMLDecl + [ + "version" + "encoding" + ] @tag.attribute) + +(XMLDecl + (EncName) @string.special) + +(XMLDecl + (VersionNum) @number) + +; Processing instructions +(PI) @keyword.directive + +; Element declaration +(elementdecl + "ELEMENT" @keyword.directive.define + (Name) @tag) + +(contentspec + (_ + (Name) @tag.attribute)) + +"#PCDATA" @type.builtin + +[ + "EMPTY" + "ANY" +] @keyword.modifier + +[ + "*" + "?" + "+" +] @character.special + +; Entity declaration +(GEDecl + "ENTITY" @keyword.directive.define + (Name) @constant) + +(GEDecl + (EntityValue) @string) + +(NDataDecl + "NDATA" @keyword + (Name) @label) + +; Parsed entity declaration +(PEDecl + "ENTITY" @keyword.directive.define + "%" @operator + (Name) @function.macro) + +(PEDecl + (EntityValue) @string) + +; Notation declaration +(NotationDecl + "NOTATION" @keyword.directive + (Name) @label) + +(NotationDecl + (ExternalID + (SystemLiteral + (URI) @string.special.url)) + (#set! "priority" 105)) + +; Attlist declaration +(AttlistDecl + "ATTLIST" @keyword.directive.define + (Name) @tag) + +(AttDef + (Name) @tag.attribute) + +(AttDef + (Enumeration + (Nmtoken) @string)) + +[ + (StringType) + (TokenizedType) +] @type.builtin + +(NotationType + "NOTATION" @type.builtin) + +[ + "#REQUIRED" + "#IMPLIED" + "#FIXED" +] @attribute + +; Entities +(EntityRef) @constant + +((EntityRef) @constant.builtin + (#any-of? @constant.builtin "&" "<" ">" """ "'")) + +(CharRef) @character + +(PEReference) @function.macro + +; External references +[ + "PUBLIC" + "SYSTEM" +] @keyword + +(PubidLiteral) @string.special + +(SystemLiteral + (URI) @string.special.url) + +; Delimiters & punctuation +[ + "" + "" + "]]>" +] @tag.delimiter + +[ + "(" + ")" + "[" +] @punctuation.bracket + +[ + "\"" + "'" +] @punctuation.delimiter + +[ + "," + "|" + "=" +] @operator + +; Misc +[ + "INCLUDE" + "IGNORE" +] @keyword.import + +(Comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/injections.scm new file mode 100644 index 00000000..ed5557a0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/injections.scm @@ -0,0 +1 @@ +(Comment) @comment diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/locals.scm new file mode 100644 index 00000000..88246c04 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/dtd/locals.scm @@ -0,0 +1,11 @@ +(elementdecl + (Name) @local.definition.type) + +(elementdecl + (contentspec + (children + (Name) @local.reference))) + +(AttlistDecl + . + (Name) @local.reference) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/earthfile/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/earthfile/highlights.scm new file mode 100644 index 00000000..cc7dce29 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/earthfile/highlights.scm @@ -0,0 +1,129 @@ +(string_array + "," @punctuation.delimiter) + +(string_array + [ + "[" + "]" + ] @punctuation.bracket) + +[ + "ARG" + "AS LOCAL" + "BUILD" + "CACHE" + "CMD" + "COPY" + "DO" + "ENTRYPOINT" + "ENV" + "EXPOSE" + "FROM DOCKERFILE" + "FROM" + "FUNCTION" + "GIT CLONE" + "HOST" + "IMPORT" + "LABEL" + "LET" + "PROJECT" + "RUN" + "SAVE ARTIFACT" + "SAVE IMAGE" + "SET" + "USER" + "VERSION" + "VOLUME" + "WORKDIR" +] @keyword + +(for_command + [ + "FOR" + "IN" + "END" + ] @keyword.repeat) + +(if_command + [ + "IF" + "END" + ] @keyword.conditional) + +(elif_block + "ELSE IF" @keyword.conditional) + +(else_block + "ELSE" @keyword.conditional) + +(import_command + [ + "IMPORT" + "AS" + ] @keyword.import) + +(try_command + [ + "TRY" + "FINALLY" + "END" + ] @keyword.exception) + +(wait_command + [ + "WAIT" + "END" + ] @keyword) + +(with_docker_command + [ + "WITH DOCKER" + "END" + ] @keyword) + +[ + (comment) + (line_continuation_comment) +] @comment @spell + +[ + (target_ref) + (target_artifact) + (function_ref) +] @function + +(target + (identifier) @function) + +[ + (double_quoted_string) + (single_quoted_string) +] @string + +(unquoted_string) @string.special + +(escape_sequence) @string.escape + +(variable) @variable + +(expansion + [ + "$" + "{" + "}" + "(" + ")" + ] @punctuation.special) + +(build_arg + [ + "--" + (variable) + ] @variable.parameter) + +(options + (_) @property) + +"=" @operator + +(line_continuation) @operator diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/earthfile/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/earthfile/injections.scm new file mode 100644 index 00000000..7435a400 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/earthfile/injections.scm @@ -0,0 +1,9 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((line_continuation_comment) @injection.content + (#set! injection.language "comment")) + +((shell_fragment) @injection.content + (#set! injection.language "bash") + (#set! injection.include-children)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ebnf/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ebnf/highlights.scm new file mode 100644 index 00000000..4254d04d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ebnf/highlights.scm @@ -0,0 +1,42 @@ +; Simple tokens ;;;; +(terminal) @string + +(special_sequence) @string.special + +(integer) @number + +(comment) @comment @spell + +; Identifiers ;;;; +; Allow different highlighting for specific casings +((identifier) @type + (#lua-match? @type "^%u")) + +((identifier) @string.special.symbol + (#lua-match? @string.special.symbol "^%l")) + +((identifier) @constant + (#lua-match? @constant "^%u[%u%d_]+$")) + +; Punctuation ;;;; +[ + ";" + "," +] @punctuation.delimiter + +[ + "|" + "*" + "-" +] @operator + +"=" @keyword.operator + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/folds.scm new file mode 100644 index 00000000..a348f344 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/folds.scm @@ -0,0 +1,24 @@ +[ + (arguments) + (for_in_statement) + (for_statement) + (while_statement) + (arrow_function) + (function_expression) + (function_declaration) + (class_declaration) + (method_definition) + (do_statement) + (with_statement) + (switch_statement) + (switch_case) + (switch_default) + (import_statement)+ + (if_statement) + (try_statement) + (catch_clause) + (array) + (object) + (generator_function) + (generator_function_declaration) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/highlights.scm new file mode 100644 index 00000000..f6040627 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/highlights.scm @@ -0,0 +1,376 @@ +; Types +; Javascript +; Variables +;----------- +(identifier) @variable + +; Properties +;----------- +(property_identifier) @variable.member + +(shorthand_property_identifier) @variable.member + +(private_property_identifier) @variable.member + +(object_pattern + (shorthand_property_identifier_pattern) @variable) + +(object_pattern + (object_assignment_pattern + (shorthand_property_identifier_pattern) @variable)) + +; Special identifiers +;-------------------- +((identifier) @type + (#lua-match? @type "^[A-Z]")) + +((identifier) @constant + (#lua-match? @constant "^_*[A-Z][A-Z%d_]*$")) + +((shorthand_property_identifier) @constant + (#lua-match? @constant "^_*[A-Z][A-Z%d_]*$")) + +((identifier) @variable.builtin + (#any-of? @variable.builtin "arguments" "module" "console" "window" "document")) + +((identifier) @type.builtin + (#any-of? @type.builtin + "Object" "Function" "Boolean" "Symbol" "Number" "Math" "Date" "String" "RegExp" "Map" "Set" + "WeakMap" "WeakSet" "Promise" "Array" "Int8Array" "Uint8Array" "Uint8ClampedArray" "Int16Array" + "Uint16Array" "Int32Array" "Uint32Array" "Float32Array" "Float64Array" "ArrayBuffer" "DataView" + "Error" "EvalError" "InternalError" "RangeError" "ReferenceError" "SyntaxError" "TypeError" + "URIError")) + +(statement_identifier) @label + +(glimmer_opening_tag) @tag.builtin + +(glimmer_closing_tag) @tag.builtin + +; Function and method definitions +;-------------------------------- +(function_expression + name: (identifier) @function) + +(function_declaration + name: (identifier) @function) + +(generator_function + name: (identifier) @function) + +(generator_function_declaration + name: (identifier) @function) + +(method_definition + name: [ + (property_identifier) + (private_property_identifier) + ] @function.method) + +(method_definition + name: (property_identifier) @constructor + (#eq? @constructor "constructor")) + +(pair + key: (property_identifier) @function.method + value: (function_expression)) + +(pair + key: (property_identifier) @function.method + value: (arrow_function)) + +(assignment_expression + left: (member_expression + property: (property_identifier) @function.method) + right: (arrow_function)) + +(assignment_expression + left: (member_expression + property: (property_identifier) @function.method) + right: (function_expression)) + +(variable_declarator + name: (identifier) @function + value: (arrow_function)) + +(variable_declarator + name: (identifier) @function + value: (function_expression)) + +(assignment_expression + left: (identifier) @function + right: (arrow_function)) + +(assignment_expression + left: (identifier) @function + right: (function_expression)) + +; Function and method calls +;-------------------------- +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (member_expression + property: [ + (property_identifier) + (private_property_identifier) + ] @function.method.call)) + +; Builtins +;--------- +((identifier) @module.builtin + (#eq? @module.builtin "Intl")) + +((identifier) @function.builtin + (#any-of? @function.builtin + "eval" "isFinite" "isNaN" "parseFloat" "parseInt" "decodeURI" "decodeURIComponent" "encodeURI" + "encodeURIComponent" "require")) + +; Constructor +;------------ +(new_expression + constructor: (identifier) @constructor) + +; Variables +;---------- +(namespace_import + (identifier) @module) + +; Decorators +;---------- +(decorator + "@" @attribute + (identifier) @attribute) + +(decorator + "@" @attribute + (call_expression + (identifier) @attribute)) + +(decorator + "@" @attribute + (member_expression + (property_identifier) @attribute)) + +(decorator + "@" @attribute + (call_expression + (member_expression + (property_identifier) @attribute))) + +; Literals +;--------- +[ + (this) + (super) +] @variable.builtin + +((identifier) @variable.builtin + (#eq? @variable.builtin "self")) + +[ + (true) + (false) +] @boolean + +[ + (null) + (undefined) +] @constant.builtin + +[ + (comment) + (html_comment) +] @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$")) + +(hash_bang_line) @keyword.directive + +((string_fragment) @keyword.directive + (#eq? @keyword.directive "use strict")) + +(string) @string + +(template_string) @string + +(escape_sequence) @string.escape + +(regex_pattern) @string.regexp + +(regex_flags) @character.special + +(regex + "/" @punctuation.bracket) ; Regex delimiters + +(number) @number + +((identifier) @number + (#any-of? @number "NaN" "Infinity")) + +; Punctuation +;------------ +[ + ";" + "." + "," + ":" +] @punctuation.delimiter + +[ + "--" + "-" + "-=" + "&&" + "+" + "++" + "+=" + "&=" + "/=" + "**=" + "<<=" + "<" + "<=" + "<<" + "=" + "==" + "===" + "!=" + "!==" + "=>" + ">" + ">=" + ">>" + "||" + "%" + "%=" + "*" + "**" + ">>>" + "&" + "|" + "^" + "??" + "*=" + ">>=" + ">>>=" + "^=" + "|=" + "&&=" + "||=" + "??=" + "..." +] @operator + +(binary_expression + "/" @operator) + +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +(unary_expression + [ + "!" + "~" + "-" + "+" + ] @operator) + +(unary_expression + [ + "delete" + "void" + ] @keyword.operator) + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(template_substitution + [ + "${" + "}" + ] @punctuation.special) @none + +; Keywords +;---------- +[ + "if" + "else" + "switch" + "case" +] @keyword.conditional + +[ + "import" + "from" + "as" + "export" +] @keyword.import + +[ + "for" + "of" + "do" + "while" + "continue" +] @keyword.repeat + +[ + "break" + "const" + "debugger" + "extends" + "get" + "let" + "set" + "static" + "target" + "var" + "with" +] @keyword + +"class" @keyword.type + +[ + "async" + "await" +] @keyword.coroutine + +[ + "return" + "yield" +] @keyword.return + +"function" @keyword.function + +[ + "new" + "delete" + "in" + "instanceof" + "typeof" +] @keyword.operator + +[ + "throw" + "try" + "catch" + "finally" +] @keyword.exception + +(export_statement + "default" @keyword) + +(switch_default + "default" @keyword.conditional) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/indents.scm new file mode 100644 index 00000000..03b10c60 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/indents.scm @@ -0,0 +1,77 @@ +[ + (arguments) + (array) + (binary_expression) + (class_body) + (export_clause) + (formal_parameters) + (named_imports) + (object) + (object_pattern) + (parenthesized_expression) + (return_statement) + (statement_block) + (switch_case) + (switch_default) + (switch_statement) + (template_substitution) + (ternary_expression) +] @indent.begin + +(arguments + (call_expression) @indent.begin) + +(binary_expression + (call_expression) @indent.begin) + +(expression_statement + (call_expression) @indent.begin) + +(arrow_function + body: (_) @_body + (#not-has-type? @_body statement_block)) @indent.begin + +(assignment_expression + right: (_) @_right + (#not-has-type? @_right arrow_function function)) @indent.begin + +(variable_declarator + value: (_) @_value + (#not-has-type? @_value arrow_function call_expression function)) @indent.begin + +(arguments + ")" @indent.end) + +(object + "}" @indent.end) + +(statement_block + "}" @indent.end) + +[ + (arguments + (object)) + ")" + "}" + "]" +] @indent.branch + +(statement_block + "{" @indent.branch) + +(parenthesized_expression + ("(" + (_) + ")" @indent.end)) + +[ + "}" + "]" +] @indent.end + +(template_string) @indent.ignore + +[ + (comment) + (ERROR) +] @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/injections.scm new file mode 100644 index 00000000..85e0383c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/injections.scm @@ -0,0 +1,216 @@ +(((comment) @_jsdoc_comment + (#lua-match? @_jsdoc_comment "^/[*][*][^*].*[*]/$")) @injection.content + (#set! injection.language "jsdoc")) + +((comment) @injection.content + (#set! injection.language "comment")) + +; html(`...`), html`...`, sql(`...`), etc. +(call_expression + function: [ + (await_expression + (identifier) @injection.language) + (identifier) @injection.language + ] + arguments: [ + (arguments + (template_string) @injection.content) + (template_string) @injection.content + ] + (#lua-match? @injection.language "^[a-zA-Z][a-zA-Z0-9]*$") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + ; Languages excluded from auto-injection due to special rules + ; - svg uses the html parser + ; - css uses the styled parser + (#not-any-of? @injection.language "svg" "css")) + +; svg`...` or svg(`...`) +(call_expression + function: [ + (await_expression + (identifier) @_name + (#eq? @_name "svg")) + ((identifier) @_name + (#eq? @_name "svg")) + ] + arguments: [ + (arguments + (template_string) @injection.content) + (template_string) @injection.content + ] + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "html")) + +(call_expression + function: [ + (await_expression + (identifier) @_name + (#eq? @_name "gql")) + ((identifier) @_name + (#eq? @_name "gql")) + ] + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "graphql"))) + +(call_expression + function: [ + (await_expression + (identifier) @_name + (#eq? @_name "hbs")) + ((identifier) @_name + (#eq? @_name "hbs")) + ] + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "glimmer"))) + +((glimmer_template) @injection.content + (#set! injection.language "glimmer")) + +; css``, keyframes`` +(call_expression + function: [ + (await_expression + (identifier) @_name + (#any-of? @_name "css" "keyframes")) + ((identifier) @_name + (#any-of? @_name "css" "keyframes")) + ] + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "styled"))) + +; styled.div`` +(call_expression + function: (member_expression + object: (identifier) @_name + (#eq? @_name "styled")) + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "styled"))) + +; styled(Component)`` +(call_expression + function: (call_expression + function: (identifier) @_name + (#eq? @_name "styled")) + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "styled"))) + +; styled.div.attrs({ prop: "foo" })`` +(call_expression + function: (call_expression + function: (member_expression + object: (member_expression + object: (identifier) @_name + (#eq? @_name "styled")))) + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "styled"))) + +; styled(Component).attrs({ prop: "foo" })`` +(call_expression + function: (call_expression + function: (member_expression + object: (call_expression + function: (identifier) @_name + (#eq? @_name "styled")))) + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "styled"))) + +((regex_pattern) @injection.content + (#set! injection.language "regex")) + +; ((comment) @_gql_comment +; (#eq? @_gql_comment "/* GraphQL */") +; (template_string) @injection.content +; (#set! injection.language "graphql")) +((template_string) @injection.content + (#lua-match? @injection.content "^`#graphql") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "graphql")) + +; el.innerHTML = `` +(assignment_expression + left: (member_expression + property: (property_identifier) @_prop + (#any-of? @_prop "outerHTML" "innerHTML")) + right: (template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "html")) + +; el.innerHTML = '' +(assignment_expression + left: (member_expression + property: (property_identifier) @_prop + (#any-of? @_prop "outerHTML" "innerHTML")) + right: (string + (string_fragment) @injection.content) + (#set! injection.language "html")) + +;---- Angular injections ----- +; @Component({ +; template: `` +; }) +(decorator + (call_expression + function: ((identifier) @_name + (#eq? @_name "Component")) + arguments: (arguments + (object + (pair + key: ((property_identifier) @_prop + (#eq? @_prop "template")) + value: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "angular"))))))) + +; @Component({ +; styles: [``] +; }) +(decorator + (call_expression + function: ((identifier) @_name + (#eq? @_name "Component")) + arguments: (arguments + (object + (pair + key: ((property_identifier) @_prop + (#eq? @_prop "styles")) + value: (array + ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "css")))))))) + +; @Component({ +; styles: `` +; }) +(decorator + (call_expression + function: ((identifier) @_name + (#eq? @_name "Component")) + arguments: (arguments + (object + (pair + key: ((property_identifier) @_prop + (#eq? @_prop "styles")) + value: ((template_string) @injection.content + (#set! injection.include-children) + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "css"))))))) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/locals.scm new file mode 100644 index 00000000..24ea7c0a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/ecma/locals.scm @@ -0,0 +1,42 @@ +; Scopes +;------- +(statement_block) @local.scope + +(function_expression) @local.scope + +(arrow_function) @local.scope + +(function_declaration) @local.scope + +(method_definition) @local.scope + +(for_statement) @local.scope + +(for_in_statement) @local.scope + +(catch_clause) @local.scope + +; Definitions +;------------ +(variable_declarator + name: (identifier) @local.definition.var) + +(import_specifier + (identifier) @local.definition.import) + +(namespace_import + (identifier) @local.definition.import) + +(function_declaration + (identifier) @local.definition.function + (#set! definition.var.scope parent)) + +(method_definition + (property_identifier) @local.definition.function + (#set! definition.var.scope parent)) + +; References +;------------ +(identifier) @local.reference + +(shorthand_property_identifier) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eds/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eds/folds.scm new file mode 100644 index 00000000..911798f5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eds/folds.scm @@ -0,0 +1 @@ +(section) @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eds/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eds/highlights.scm new file mode 100644 index 00000000..0e008c97 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eds/highlights.scm @@ -0,0 +1,45 @@ +"=" @punctuation.delimiter + +[ + "[" + "]" +] @punctuation.bracket + +((section_name) @variable.builtin + (#match? @variable.builtin + "\\c^(FileInfo|DeviceInfo|DummyUsage|MandatoryObjects|OptionalObjects)$")) + +((section_name) @variable.builtin + (#lua-match? @variable.builtin "^1")) + +(section + (section_name) @_name + (#match? @_name "\\c^Comments$")) @comment + +(section + (section_name) @_name + (statement + (key) @_key) @string + (#match? @_key "\\c^ParameterName$") + (#not-match? @_name "\\c^Comments$")) + +(section + (section_name) @_name + (statement + (key) @_key) @type + (#match? @_key "\\c^(ObjectType|DataType|AccessType)$") + (#not-match? @_name "\\c^Comments$")) + +(section + (section_name) @_name + (statement + (key) @_key) @attribute + (#match? @_key "\\c^PDOMapping$") + (#not-match? @_name "\\c^Comments$")) + +(section + (section_name) @_name + (statement + (key) @_key) @number + (#match? @_key "\\c^(DefaultValue|LowLimit|HighLimit|SubNumber)$") + (#not-match? @_name "\\c^Comments$")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eex/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eex/highlights.scm new file mode 100644 index 00000000..d032a748 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eex/highlights.scm @@ -0,0 +1,12 @@ +[ + "%>" + "--%>" + "<%!--" + "<%" + "<%#" + "<%%=" + "<%=" +] @tag.delimiter + +; EEx comments are highlighted as such +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eex/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eex/injections.scm new file mode 100644 index 00000000..f13d3c14 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/eex/injections.scm @@ -0,0 +1,8 @@ +; EEx expressions are Elixir +((expression) @injection.content + (#set! injection.language "elixir")) + +; EEx expressions can span multiple interpolated lines +((partial_expression) @injection.content + (#set! injection.language "elixir") + (#set! injection.combined)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/folds.scm new file mode 100644 index 00000000..7abfe679 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/folds.scm @@ -0,0 +1,10 @@ +[ + (anonymous_function) + (stab_clause) + (arguments) + (block) + (do_block) + (list) + (map) + (tuple) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/highlights.scm new file mode 100644 index 00000000..cbdb40de --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/highlights.scm @@ -0,0 +1,217 @@ +; Punctuation +[ + "," + ";" +] @punctuation.delimiter + +[ + "(" + ")" + "<<" + ">>" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +"%" @punctuation.special + +; Identifiers +(identifier) @variable + +; Unused Identifiers +((identifier) @comment + (#lua-match? @comment "^_")) + +; Comments +(comment) @comment @spell + +; Strings +(string) @string + +; Modules +(alias) @module + +; Atoms & Keywords +[ + (atom) + (quoted_atom) + (keyword) + (quoted_keyword) +] @string.special.symbol + +; Interpolation +(interpolation + [ + "#{" + "}" + ] @string.special) + +; Escape sequences +(escape_sequence) @string.escape + +; Integers +(integer) @number + +; Floats +(float) @number.float + +; Characters +[ + (char) + (charlist) +] @character + +; Booleans +(boolean) @boolean + +; Nil +(nil) @constant.builtin + +; Operators +(operator_identifier) @operator + +(unary_operator + operator: _ @operator) + +(binary_operator + operator: _ @operator) + +; Pipe Operator +(binary_operator + operator: "|>" + right: (identifier) @function) + +(dot + operator: _ @operator) + +(stab_clause + operator: _ @operator) + +; Local Function Calls +(call + target: (identifier) @function.call) + +; Remote Function Calls +(call + target: (dot + left: [ + (atom) @type + (_) + ] + right: (identifier) @function.call) + (arguments)) + +; Definition Function Calls +(call + target: ((identifier) @keyword.function + (#any-of? @keyword.function + "def" "defdelegate" "defexception" "defguard" "defguardp" "defimpl" "defmacro" "defmacrop" + "defmodule" "defn" "defnp" "defoverridable" "defp" "defprotocol" "defstruct")) + (arguments + [ + (call + (identifier) @function) + (identifier) @function + (binary_operator + left: (call + target: (identifier) @function) + operator: "when") + ])?) + +; Kernel Keywords & Special Forms +(call + target: ((identifier) @keyword + (#any-of? @keyword + "alias" "case" "catch" "cond" "else" "for" "if" "import" "quote" "raise" "receive" "require" + "reraise" "super" "throw" "try" "unless" "unquote" "unquote_splicing" "use" "with"))) + +; Special Constants +((identifier) @constant.builtin + (#any-of? @constant.builtin "__CALLER__" "__DIR__" "__ENV__" "__MODULE__" "__STACKTRACE__")) + +; Reserved Keywords +[ + "after" + "catch" + "do" + "end" + "fn" + "rescue" + "when" + "else" +] @keyword + +; Operator Keywords +[ + "and" + "in" + "not in" + "not" + "or" +] @keyword.operator + +; Capture Operator +(unary_operator + operator: "&" + operand: [ + (integer) @operator + (binary_operator + left: [ + (call + target: (dot + left: (_) + right: (identifier) @function)) + (identifier) @function + ] + operator: "/" + right: (integer) @operator) + ]) + +; Non-String Sigils +(sigil + "~" @string.special + (sigil_name) @string.special @_sigil_name + quoted_start: _ @string.special + quoted_end: _ @string.special + ((sigil_modifiers) @string.special)? + (#not-any-of? @_sigil_name "s" "S")) + +; String Sigils +(sigil + "~" @string + (sigil_name) @string @_sigil_name + quoted_start: _ @string + (quoted_content) @string + quoted_end: _ @string + ((sigil_modifiers) @string)? + (#any-of? @_sigil_name "s" "S")) + +; Module attributes +(unary_operator + operator: "@" + operand: [ + (identifier) + (call + target: (identifier)) + ] @constant) @constant + +; Documentation +(unary_operator + operator: "@" + operand: (call + target: ((identifier) @_identifier + (#any-of? @_identifier "moduledoc" "typedoc" "shortdoc" "doc")) @comment.documentation + (arguments + [ + (string) + (boolean) + (charlist) + (sigil + "~" @comment.documentation + (sigil_name) @comment.documentation + quoted_start: _ @comment.documentation + (quoted_content) @comment.documentation + quoted_end: _ @comment.documentation) + ] @comment.documentation))) @comment.documentation diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/indents.scm new file mode 100644 index 00000000..5470b642 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/indents.scm @@ -0,0 +1,25 @@ +[ + (block) + (do_block) + (list) + (map) + (stab_clause) + (tuple) + (arguments) +] @indent.begin + +[ + ")" + "]" + "after" + "catch" + "else" + "rescue" + "}" + "end" +] @indent.end @indent.branch + +; Elixir pipelines are not indented, but other binary operator chains are +((binary_operator + operator: _ @_operator) @indent.begin + (#not-eq? @_operator "|>")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/injections.scm new file mode 100644 index 00000000..cc74f792 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/injections.scm @@ -0,0 +1,59 @@ +; Comments +((comment) @injection.content + (#set! injection.language "comment")) + +; Documentation +(unary_operator + operator: "@" + operand: (call + target: ((identifier) @_identifier + (#any-of? @_identifier "moduledoc" "typedoc" "shortdoc" "doc")) + (arguments + [ + (string + (quoted_content) @injection.content) + (sigil + (quoted_content) @injection.content) + ]) + (#set! injection.language "markdown"))) + +; HEEx +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#eq? @_sigil_name "H") + (#set! injection.language "heex")) + +; Surface +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#eq? @_sigil_name "F") + (#set! injection.language "surface")) + +; Zigler +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#any-of? @_sigil_name "E" "L") + (#set! injection.language "eex")) + +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#any-of? @_sigil_name "z" "Z") + (#set! injection.language "zig")) + +; Regex +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#any-of? @_sigil_name "r" "R") + (#set! injection.language "regex")) + +; Json +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#any-of? @_sigil_name "j" "J") + (#set! injection.language "json")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/locals.scm new file mode 100644 index 00000000..ac9d86e3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elixir/locals.scm @@ -0,0 +1,200 @@ +; References +(identifier) @local.reference + +(alias) @local.reference + +; Module Definitions +(call + target: ((identifier) @_identifier + (#eq? @_identifier "defmodule")) + (arguments + (alias) @local.definition.type)) + +; Pattern Match Definitions +(binary_operator + ; format-ignore + left: + [ + (identifier) @local.definition.var + (_ (identifier) @local.definition.var) + (_ (_ (identifier) @local.definition.var)) + (_ (_ (_ (identifier) @local.definition.var))) + (_ (_ (_ (_ (identifier) @local.definition.var)))) + (_ (_ (_ (_ (_ (identifier) @local.definition.var))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))) + (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))))) + ] + operator: "=") + +; Stab Clause Definitions +; format-ignore +(stab_clause + left: + [ + (arguments + [ + (identifier) @local.definition.var + (_ (identifier) @local.definition.var) + (_ (_ (identifier) @local.definition.var)) + (_ (_ (_ (identifier) @local.definition.var))) + (_ (_ (_ (_ (identifier) @local.definition.var)))) + (_ (_ (_ (_ (_ (identifier) @local.definition.var))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))) + (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))))) + ]) + + (binary_operator + left: + (arguments + ; format-ignore + [ + (identifier) @local.definition.var + (_ (identifier) @local.definition.var) + (_ (_ (identifier) @local.definition.var)) + (_ (_ (_ (identifier) @local.definition.var))) + (_ (_ (_ (_ (identifier) @local.definition.var)))) + (_ (_ (_ (_ (_ (identifier) @local.definition.var))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))) + (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))))) + ]) + operator: "when") + ]) + +; Aliases +; format-ignore +(call + target: + ((identifier) @_identifier + (#any-of? @_identifier "require" "alias" "use" "import")) + (arguments + [ + (alias) @local.definition.import + (_ (alias) @local.definition.import) + (_ (_ (alias) @local.definition.import)) + (_ (_ (_ (alias) @local.definition.import))) + (_ (_ (_ (_ (alias) @local.definition.import)))) + ])) + +; Local Function Definitions & Scopes +; format-ignore +(call + target: + ((identifier) @_identifier + (#any-of? @_identifier "def" "defp" "defmacro" "defmacrop" "defguard" "defguardp" "defn" "defnp" "for")) + (arguments + [ + (identifier) @local.definition.function + (binary_operator + left: (identifier) @local.definition.function + operator: "when") + (binary_operator + (identifier) @local.definition.parameter) + (call + target: (identifier) @local.definition.function + (arguments + [ + (identifier) @local.definition.parameter + (_ (identifier) @local.definition.parameter) + (_ (_ (identifier) @local.definition.parameter)) + (_ (_ (_ (identifier) @local.definition.parameter))) + (_ (_ (_ (_ (identifier) @local.definition.parameter)))) + (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))) + (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))))))) + ])) + ]?) + (#set! definition.function.scope parent)(do_block)?) @local.scope + +; ExUnit Test Definitions & Scopes +; format-ignore +(call + target: + ((identifier) @_identifier + (#eq? @_identifier "test")) + (arguments + [ + (string) + ((string) + . + "," + . + [ + (identifier) @local.definition.parameter + (_ (identifier) @local.definition.parameter) + (_ (_ (identifier) @local.definition.parameter)) + (_ (_ (_ (identifier) @local.definition.parameter))) + (_ (_ (_ (_ (identifier) @local.definition.parameter)))) + (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))) + (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))))))) + ]) + ]) + (do_block)?) @local.scope + +; Stab Clause Scopes +(stab_clause) @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elm/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elm/highlights.scm new file mode 100644 index 00000000..fc18566c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elm/highlights.scm @@ -0,0 +1,229 @@ +[ + (line_comment) + (block_comment) +] @comment @spell + +((block_comment) @comment.documentation + (#lua-match? @comment.documentation "^{[-]|[^|]")) + +; Keywords +;--------- +[ + "if" + "then" + "else" + (case) + (of) +] @keyword.conditional + +[ + "let" + "in" + (as) + (port) + (alias) + (infix) + (module) + (type) +] @keyword + +[ + (import) + (exposing) +] @keyword.import + +; Punctuation +;------------ +(double_dot) @punctuation.special + +[ + "," + "|" + (dot) +] @punctuation.delimiter + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +; Variables +;---------- +(value_qid + (lower_case_identifier) @variable) + +(value_declaration + (function_declaration_left + (lower_case_identifier) @variable)) + +(type_annotation + (lower_case_identifier) @variable) + +(port_annotation + (lower_case_identifier) @variable) + +(anything_pattern + (underscore) @variable) + +(record_base_identifier + (lower_case_identifier) @variable) + +(lower_pattern + (lower_case_identifier) @variable) + +(exposed_value + (lower_case_identifier) @variable) + +(value_qid + ((dot) + (lower_case_identifier) @variable.member)) + +(field_access_expr + ((dot) + (lower_case_identifier) @variable.member)) + +(function_declaration_left + (anything_pattern + (underscore) @variable.parameter)) + +(function_declaration_left + (lower_pattern + (lower_case_identifier) @variable.parameter)) + +; Functions +;---------- +(value_declaration + functionDeclarationLeft: (function_declaration_left + (lower_case_identifier) @function + (pattern))) + +(value_declaration + functionDeclarationLeft: (function_declaration_left + (lower_case_identifier) @function + pattern: (_))) + +(value_declaration + functionDeclarationLeft: (function_declaration_left + (lower_case_identifier) @function) + body: (anonymous_function_expr)) + +(type_annotation + name: (lower_case_identifier) @function + typeExpression: (type_expression + (arrow))) + +(port_annotation + name: (lower_case_identifier) @function + typeExpression: (type_expression + (arrow))) + +(function_call_expr + target: (value_expr + (value_qid + (lower_case_identifier) @function.call))) + +; Operators +;---------- +[ + (operator_identifier) + (eq) + (colon) + (arrow) + (backslash) + "::" +] @operator + +; Modules +;-------- +(module_declaration + (upper_case_qid + (upper_case_identifier) @module)) + +(import_clause + (upper_case_qid + (upper_case_identifier) @module)) + +(as_clause + (upper_case_identifier) @module) + +(value_expr + (value_qid + (upper_case_identifier) @module)) + +; Types +;------ +(type_declaration + (upper_case_identifier) @type) + +(type_ref + (upper_case_qid + (upper_case_identifier) @type)) + +(type_variable + (lower_case_identifier) @type) + +(lower_type_name + (lower_case_identifier) @type) + +(exposed_type + (upper_case_identifier) @type) + +(type_alias_declaration + (upper_case_identifier) @type.definition) + +(field_type + name: (lower_case_identifier) @property) + +(field + name: (lower_case_identifier) @property) + +(type_declaration + (union_variant + (upper_case_identifier) @constructor)) + +(nullary_constructor_argument_pattern + (upper_case_qid + (upper_case_identifier) @constructor)) + +(union_pattern + (upper_case_qid + (upper_case_identifier) @constructor)) + +(value_expr + (upper_case_qid + (upper_case_identifier)) @constructor) + +; Literals +;--------- +(number_constant_expr + (number_literal) @number) + +(upper_case_qid + ((upper_case_identifier) @boolean + (#any-of? @boolean "True" "False"))) + +[ + (open_quote) + (close_quote) +] @string + +(string_constant_expr + (string_escape) @string) + +(string_constant_expr + (regular_string_part) @string) + +[ + (open_char) + (close_char) +] @character + +(char_constant_expr + (string_escape) @character) + +(char_constant_expr + (regular_string_part) @character) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elm/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elm/injections.scm new file mode 100644 index 00000000..7ee6c7f0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elm/injections.scm @@ -0,0 +1,8 @@ +([ + (line_comment) + (block_comment) +] @injection.content + (#set! injection.language "comment")) + +((glsl_content) @injection.content + (#set! injection.language "glsl")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/folds.scm new file mode 100644 index 00000000..afdfec30 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/folds.scm @@ -0,0 +1 @@ +(reduction) @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/highlights.scm new file mode 100644 index 00000000..1a974bda --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/highlights.scm @@ -0,0 +1,36 @@ +; Keywords +[ + "eval" + "let" +] @keyword + +; Function +(function) @function + +; Method +(method) @function.method + +; Parameter +(parameter) @variable.parameter + +; Variables +(identifier) @variable + +; Operators +[ + "\\" + "->" + "=" + (step) +] @operator + +; Punctuation +[ + "(" + ")" +] @punctuation.bracket + +":" @punctuation.delimiter + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/indents.scm new file mode 100644 index 00000000..6ddd1aa6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/indents.scm @@ -0,0 +1,6 @@ +(reduction) @indent.begin + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/locals.scm new file mode 100644 index 00000000..3e8197ae --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elsa/locals.scm @@ -0,0 +1,12 @@ +[ + (source_file) + (reduction) +] @local.scope + +(identifier) @local.reference + +(function) @local.definition.function + +(method) @local.definition.method + +(parameter) @local.definition.parameter diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elvish/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elvish/highlights.scm new file mode 100644 index 00000000..9836a6c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elvish/highlights.scm @@ -0,0 +1,157 @@ +(comment) @comment @spell + +[ + "if" + "elif" +] @keyword.conditional + +(if + (else + "else" @keyword.conditional)) + +[ + "while" + "for" +] @keyword.repeat + +(while + (else + "else" @keyword.repeat)) + +(for + (else + "else" @keyword.repeat)) + +[ + "try" + "catch" + "finally" +] @keyword.exception + +(try + (else + "else" @keyword.exception)) + +"use" @keyword.import + +(import + (bareword) @string.special.path) + +(wildcard + [ + "*" + "**" + "?" + ] @character.special) + +(command + argument: (bareword) @variable.parameter) + +(command + head: (identifier) @function.call) + +((command + head: (identifier) @keyword.return) + (#eq? @keyword.return "return")) + +((command + (identifier) @keyword.operator) + (#any-of? @keyword.operator "and" "or" "coalesce")) + +[ + "+" + "-" + "*" + "/" + "%" + "<" + "<=" + "==" + "!=" + ">" + ">=" + "s" + ">=s" +] @function.builtin + +[ + ">" + "<" + ">>" + "<>" + "|" +] @operator + +(io_port) @number + +(function_definition + "fn" @keyword.function + (identifier) @function) + +(parameter_list) @variable.parameter + +(parameter_list + "|" @punctuation.bracket) + +[ + "var" + "set" + "tmp" + "del" +] @keyword + +(variable_declaration + (lhs + (identifier) @variable)) + +(variable_assignment + (lhs + (identifier) @variable)) + +(temporary_assignment + (lhs + (identifier) @variable)) + +(variable_deletion + (identifier) @variable) + +(number) @number + +(string) @string + +(variable + (identifier) @variable) + +((variable + (identifier) @function) + (#lua-match? @function ".+[~]$")) + +((variable + (identifier) @boolean) + (#any-of? @boolean "true" "false")) + +((variable + (identifier) @constant.builtin) + (#any-of? @constant.builtin + "_" "after-chdir" "args" "before-chdir" "buildinfo" "nil" "notify-bg-job-success" "num-bg-jobs" + "ok" "paths" "pid" "pwd" "value-out-indicator" "version")) + +[ + "$" + "@" +] @punctuation.special + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +";" @punctuation.delimiter diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elvish/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elvish/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/elvish/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/embedded_template/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/embedded_template/highlights.scm new file mode 100644 index 00000000..410983d6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/embedded_template/highlights.scm @@ -0,0 +1,12 @@ +(comment_directive) @comment @spell + +[ + "<%#" + "<%" + "<%=" + "<%_" + "<%-" + "%>" + "-%>" + "_%>" +] @keyword diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/embedded_template/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/embedded_template/injections.scm new file mode 100644 index 00000000..cdeb2cd4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/embedded_template/injections.scm @@ -0,0 +1,7 @@ +((content) @injection.content + (#set! injection.language "html") + (#set! injection.combined)) + +((code) @injection.content + (#set! injection.language "ruby") + (#set! injection.combined)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/erlang/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/erlang/folds.scm new file mode 100644 index 00000000..65c2d8ed --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/erlang/folds.scm @@ -0,0 +1,9 @@ +[ + (fun_decl) + (anonymous_fun) + (case_expr) + (maybe_expr) + (map_expr) + (export_attribute) + (export_type_attribute) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/erlang/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/erlang/highlights.scm new file mode 100644 index 00000000..1ce04668 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/erlang/highlights.scm @@ -0,0 +1,184 @@ +((atom) @constant + (#set! "priority" "90")) + +(var) @variable + +(char) @character + +(integer) @number + +(float) @number.float + +(comment) @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^[%%][%%]")) + +; keyword +[ + "fun" + "div" +] @keyword + +; bracket +[ + "(" + ")" + "{" + "}" + "[" + "]" + "#" +] @punctuation.bracket + +; Comparisons +[ + "==" + "=:=" + "=/=" + "=<" + ">=" + "<" + ">" +] @operator ; .comparison + +; operator +[ + ":" + ":=" + "!" + ; "-" + "+" + "=" + "->" + "=>" + "|" + "?=" +] @operator + +[ + "," + "." + ";" +] @punctuation.delimiter + +; conditional +[ + "receive" + "if" + "case" + "of" + "when" + "after" + "begin" + "end" + "maybe" + "else" +] @keyword.conditional + +[ + "catch" + "try" +] @keyword.exception + +((atom) @boolean + (#any-of? @boolean "true" "false")) + +; Macros +((macro_call_expr) @constant.macro + (#set! "priority" 101)) + +; Preprocessor +(pp_define + lhs: _ @constant.macro + (#set! "priority" 101)) + +(_preprocessor_directive) @keyword.directive +(#set! "priority" 99) + +; Attributes +(pp_include) @keyword.import + +(pp_include_lib) @keyword.import + +(export_attribute) @keyword.import + +(export_type_attribute) @type.definition + +(export_type_attribute + types: (fa + fun: _ @type + (#set! "priority" 101))) + +(behaviour_attribute) @keyword.import + +(module_attribute + (atom) @module) @keyword.import + +(wild_attribute + name: (attr_name + name: _ @attribute)) @attribute + +; Records +(record_expr) @type + +(record_field_expr + _ @variable.member) @type + +(record_field_name + _ @variable.member) @type + +(record_name + "#" @type + name: _ @type) @type + +(record_decl + name: _ @type) @type.definition + +(record_field + name: _ @variable.member) + +(record_field + name: _ @variable.member + ty: _ @type) + +; Type alias +(type_alias + name: _ @type) @type.definition + +(spec) @type.definition + +[ + (string) + (binary) +] @string + +; expr_function_call +(call + expr: [ + (atom) + (remote) + (var) + ] @function) + +(call + (atom) @keyword.exception + (#any-of? @keyword.exception "error" "throw" "exit")) + +; Parenthesized expression: (SomeFunc)(), only highlight the parens +(call + expr: (paren_expr + "(" @function.call + ")" @function.call)) + +; function +(external_fun) @function.call + +(internal_fun + fun: (atom) @function.call) + +(function_clause + name: (atom) @function) + +(fa + fun: (atom) @function) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/folds.scm new file mode 100644 index 00000000..7d8bafc8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/folds.scm @@ -0,0 +1,6 @@ +[ + (service) + (method) + (dto) + (enum) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/highlights.scm new file mode 100644 index 00000000..6816b480 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/highlights.scm @@ -0,0 +1,87 @@ +[ + ";" + "." + "," +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(comment) @comment @spell + +(doc_comment) @comment.documentation @spell + +[ + "service" + "errors" +] @keyword + +"method" @keyword.function + +[ + "enum" + "data" +] @keyword.type + +"extern" @keyword.modifier + +(type) @type.builtin + +(service + service_name: (identifier) @type) + +(error_set + (identifier) @variable.member) + +(error_set + name: (identifier) @type) + +(dto + name: (identifier) @type) + +(external_dto + name: (identifier) @type) + +(enum + (values_block + (identifier) @constant)) + +(enum + name: (identifier) @type) + +(external_enum + name: (identifier) @type) + +(type + name: (identifier) @type) + +[ + "map" + "nullable" + "result" + "required" + "http" + "csharp" + "js" + "info" + "obsolete" +] @attribute.builtin + +(parameter + name: (identifier) @variable.parameter) + +(field + name: (identifier) @variable.member) + +(method + name: (identifier) @function.method) + +(number_literal) @number + +(string_literal) @string diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/indents.scm new file mode 100644 index 00000000..247949ba --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/indents.scm @@ -0,0 +1,7 @@ +[ + (service_block) + (values_block) + (field_list) +] @indent.begin + +"}" @indent.branch diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/injections.scm new file mode 100644 index 00000000..5d9b7836 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/facility/injections.scm @@ -0,0 +1,8 @@ +((remarks) @injection.content + (#set! injection.language "markdown")) + +([ + (comment) + (doc_comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/faust/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/faust/highlights.scm new file mode 100644 index 00000000..6e7ef1d9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/faust/highlights.scm @@ -0,0 +1,219 @@ +; Identifiers +(identifier) @variable + +[ + "process" + "effect" +] @variable.builtin + +(parameters + (identifier)) @variable.parameter + +(access + definition: (identifier) @variable.member) + +(global_metadata + key: (identifier) @variable.member) + +(function_metadata + function_name: (identifier) @variable.member) + +; Literals +(_ + filename: (string)) @string.special.path + +(documentation) @string.documentation @spell + +[ + (string) + (fstring) +] @string + +(int) @number + +(real) @number.float + +; Types +(_ + type: [ + (int_type) + (float_type) + (any_type) + ]) @type.builtin + +[ + (single_precision) + (double_precision) + (quad_precision) + (fixed_point_precision) +] @attribute + +; Functions +(function_definition + name: (identifier) @function) + +(function_names) @function + +(function_call + (identifier) @function.call) + +(function_call + (access + definition: (identifier) @function.call)) + +[ + "exp" + "log" + "log10" + "sqrt" + "abs" + "floor" + "ceil" + "rint" + "round" + "acos" + "asin" + "atan" + "cos" + "sin" + "tan" + "atan2" + "int" + "float" + "pow" + "min" + "max" + "fmod" + "remainder" + "prefix" + "attach" + "enable" + "control" + "rdtable" + "rwtable" + "select2" + "select3" + "lowest" + "highest" + "assertbounds" + (par) + (seq) + (sum) + (prod) + (component) + (library) + (vslider_type) + (hslider_type) + (nentry_type) + (vbargraph_type) + (hbargraph_type) + (vgroup_type) + (hgroup_type) + (tgroup_type) + "button" + "checkbox" + "soundfile" + "inputs" + "outputs" + "route" +] @function.builtin + +; xor is a @keyword.operator +[ + (add) + (sub) + (mult) + (div) + (mod) + (pow) + (or) + (and) + (lshift) + (rshift) + (lt) + (le) + (gt) + (ge) + (eq) + (neq) + (delay) + (one_sample_delay) + "=" + "=>" + "->" +] @operator + +(recursive + "~" @operator) + +(sequential + ":" @operator) + +(split + "<:" @operator) + +(merge + ":>" @operator) + +(parallel + "," @operator) + +; Keywords +[ + (par) + (seq) + (sum) + (prod) +] @keyword.repeat + +(file_import + "import" @keyword.import) + +[ + (wire) + (cut) + (mem) + "declare" + "with" + "environment" + "case" + "ffunction" + "fconstant" + "fvariable" +] @keyword + +(xor) @keyword.operator + +; Punctuation +[ + "," + ";" + "." +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +; Comments +(comment) @comment @spell + +; Tags +[ + "" + "" + "" + "" + "" + "" + "" + "" + "" +] @tag diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/faust/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/faust/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/faust/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/folds.scm new file mode 100644 index 00000000..0862e592 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/folds.scm @@ -0,0 +1,51 @@ +; compounds +[ + (list) + (table) + (sequence) +] @fold + +; sub-forms / special compounds +[ + (list_binding) + (table_binding) + (sequence_binding) + (table_metadata) + (sequence_arguments) + (let_vars) + (case_guard_or_special) + (case_guard) + (case_catch) +] @fold + +; forms +[ + (quote_form) + (unquote_form) + (local_form) + (var_form) + (set_form) + (global_form) + (let_form) + (fn_form) + (lambda_form) + (hashfn_form) + (each_form) + (collect_form) + (icollect_form) + (accumulate_form) + (for_form) + (fcollect_form) + (faccumulate_form) + (case_form) + (match_form) + (case_try_form) + (match_try_form) +] @fold + +; reader macros +(quote_reader_macro + expression: (_) @fold) + +(quasi_quote_reader_macro + expression: (_) @fold) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/highlights.scm new file mode 100644 index 00000000..2f0b5f73 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/highlights.scm @@ -0,0 +1,193 @@ +; Most primitive nodes +(shebang) @keyword.directive + +(comment) @comment @spell + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + (nil) + (nil_binding) +] @constant.builtin + +[ + (boolean) + (boolean_binding) +] @boolean + +[ + (number) + (number_binding) +] @number + +[ + (string) + (string_binding) +] @string + +[ + (symbol) + (symbol_binding) +] @variable + +(symbol_option) @keyword.directive + +(escape_sequence) @string.escape + +(multi_symbol + "." @punctuation.delimiter + member: (symbol_fragment) @variable.member) + +(list + call: (symbol) @function.call) + +(list + call: (multi_symbol + member: (symbol_fragment) @function.call .)) + +(multi_symbol_method + ":" @punctuation.delimiter + method: (symbol_fragment) @function.method.call) + +(quasi_quote_reader_macro + macro: _ @punctuation.special) + +(quote_reader_macro + macro: _ @punctuation.special) + +(unquote_reader_macro + macro: _ @punctuation.special) + +(hashfn_reader_macro + macro: _ @keyword.function) + +(sequence_arguments + (symbol_binding) @variable.parameter) + +(sequence_arguments + (rest_binding + rhs: (symbol_binding) @variable.parameter)) + +(docstring) @string.documentation + +(fn_form + name: [ + (symbol) @function + (multi_symbol + member: (symbol_fragment) @function .) + ]) + +(lambda_form + name: [ + (symbol) @function + (multi_symbol + member: (symbol_fragment) @function .) + ]) + +; NOTE: The macro name is highlighted as @variable because it +; gives a nicer contrast instead of everything being the same +; color. Rust queries use this workaround too for `macro_rules!`. +(macro_form + name: [ + (symbol) @variable + (multi_symbol + member: (symbol_fragment) @variable .) + ]) + +((symbol) @variable.parameter + (#any-of? @variable.parameter "$" "$...")) + +((symbol) @variable.parameter + (#lua-match? @variable.parameter "^%$[1-9]$")) + +((symbol) @operator + (#any-of? @operator + ; arithmetic + "+" "-" "*" "/" "//" "%" "^" + ; comparison + ">" "<" ">=" "<=" "=" "~=" + ; other + "#" "." "?." "..")) + +((symbol) @keyword.operator + (#any-of? @keyword.operator + ; comparison + "not=" + ; boolean + "and" "or" "not" + ; bitwise + "lshift" "rshift" "band" "bor" "bxor" "bnot" + ; other + "length")) + +(case_guard + call: (_) @keyword.conditional) + +(case_guard_or_special + call: (_) @keyword.conditional) + +((symbol) @keyword.function + (#any-of? @keyword.function "fn" "lambda" "λ" "hashfn")) + +((symbol) @keyword.repeat + (#any-of? @keyword.repeat "for" "each" "while")) + +((symbol) @keyword.conditional + (#any-of? @keyword.conditional "if" "when" "match" "case" "match-try" "case-try")) + +((symbol) @keyword + (#any-of? @keyword + "global" "local" "let" "set" "var" "comment" "do" "doc" "eval-compiler" "lua" "macros" "unquote" + "quote" "tset" "values" "tail!")) + +((symbol) @keyword.import + (#any-of? @keyword.import "require-macros" "import-macros" "include")) + +((symbol) @function.macro + (#any-of? @function.macro + "collect" "icollect" "fcollect" "accumulate" "faccumulate" "->" "->>" "-?>" "-?>>" "?." "doto" + "macro" "macrodebug" "partial" "pick-args" "pick-values" "with-open")) + +(case_catch + call: (symbol) @keyword) + +(import_macros_form + imports: (table_binding + (table_binding_pair + value: (symbol_binding) @function.macro))) + +; TODO: Highlight builtin methods (`table.unpack`, etc) as @function.builtin +([ + (symbol) @module.builtin + (multi_symbol + base: (symbol_fragment) @module.builtin) +] + (#any-of? @module.builtin + "vim" "_G" "debug" "io" "jit" "math" "os" "package" "string" "table" "utf8")) + +([ + (symbol) @variable.builtin + (multi_symbol + . + (symbol_fragment) @variable.builtin) +] + (#eq? @variable.builtin "arg")) + +((symbol) @variable.builtin + (#eq? @variable.builtin "...")) + +((symbol) @constant.builtin + (#eq? @constant.builtin "_VERSION")) + +((symbol) @function.builtin + (#any-of? @function.builtin + "assert" "collectgarbage" "dofile" "error" "getmetatable" "ipairs" "load" "loadfile" "next" + "pairs" "pcall" "print" "rawequal" "rawget" "rawlen" "rawset" "require" "select" "setmetatable" + "tonumber" "tostring" "type" "warn" "xpcall" "module" "setfenv" "loadstring" "unpack")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/injections.scm new file mode 100644 index 00000000..f6724d32 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/injections.scm @@ -0,0 +1,134 @@ +((comment_body) @injection.content + (#set! injection.language "comment")) + +(list + call: (multi_symbol) @_vimcmd_identifier + (#any-of? @_vimcmd_identifier "vim.cmd" "vim.api.nvim_command" "vim.api.nvim_exec2") + . + item: (string + (string_content) @injection.content + (#set! injection.language "vim"))) + +; NOTE: Matches *exactly* `ffi.cdef` +(list + call: (multi_symbol) @_cdef_identifier + (#eq? @_cdef_identifier "ffi.cdef") + . + item: (string + (string_content) @injection.content + (#set! injection.language "c"))) + +(list + call: (multi_symbol) @_ts_query_identifier + (#any-of? @_ts_query_identifier "vim.treesitter.query.set" "vim.treesitter.query.parse") + . + item: (_) + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "query"))) + +(list + call: (multi_symbol) @_vimcmd_identifier + (#eq? @_vimcmd_identifier "vim.api.nvim_create_autocmd") + . + item: (_) + . + item: (table + (table_pair + key: (string + (string_content) @_command + (#eq? @_command "command")) + value: (string + (string_content) @injection.content + (#set! injection.language "vim"))))) + +(list + call: (multi_symbol) @_user_cmd + (#eq? @_user_cmd "vim.api.nvim_create_user_command") + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "vim"))) + +(list + call: (multi_symbol) @_user_cmd + (#eq? @_user_cmd "vim.api.nvim_buf_create_user_command") + . + item: (_) + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "vim"))) + +(list + call: (multi_symbol) @_map + (#any-of? @_map "vim.api.nvim_set_keymap" "vim.keymap.set") + . + item: (_) + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "vim"))) + +(list + call: (multi_symbol) @_map + (#eq? @_map "vim.api.nvim_buf_set_keymap") + . + item: (_) + . + item: (_) + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "vim"))) + +; highlight string as query if starts with `; query` +(string + (string_content) @injection.content + (#lua-match? @injection.content "^%s*;+%s?query") + (#set! injection.language "query")) + +; (string.match "123" "%d+") +(list + call: (multi_symbol + member: (symbol_fragment) @_func + . + (#any-of? @_func "find" "match" "gmatch" "gsub")) + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "luap") + (#set! injection.include-children))) + +; (my-string:match "%d+") +(list + call: (multi_symbol_method + method: (symbol_fragment) @_method + (#any-of? @_method "find" "match" "gmatch" "gsub")) + . + item: (string + (string_content) @injection.content + (#set! injection.language "luap") + (#set! injection.include-children))) + +; (string.format "pi = %.2f" 3.14159) +(list + call: (multi_symbol) @_func + (#eq? @_func "string.format") + . + item: (string + (string_content) @injection.content + (#set! injection.language "printf"))) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/locals.scm new file mode 100644 index 00000000..be63e728 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fennel/locals.scm @@ -0,0 +1,56 @@ +(program) @local.scope + +(symbol) @local.reference + +[ + (let_form) + (fn_form) + (lambda_form) + (each_form) + (for_form) + (collect_form) + (icollect_form) + (accumulate_form) + (for_form) + (fcollect_form) + (faccumulate_form) + (case_form) + (match_form) + (case_try_form) + (match_try_form) + (if_form) +] @local.scope + +(list + call: (symbol) @_call + (#any-of? @_call "while" "when" "do")) @local.scope + +(fn_form + name: [ + (symbol) @local.definition.function + (multi_symbol + member: (symbol_fragment) @local.definition.function .) + ] + args: (sequence_arguments + (symbol_binding) @local.definition.parameter) + (#set! definition.function.scope "parent")) + +(lambda_form + name: [ + (symbol) @local.definition.function + (multi_symbol + member: (symbol_fragment) @local.definition.function .) + ] + args: (sequence_arguments + (symbol_binding) @local.definition.parameter) + (#set! definition.function.scope "parent")) + +(macro_form + name: [ + (symbol) @local.definition.function + (multi_symbol + member: (symbol_fragment) @local.definition.function .) + ] + args: (sequence_arguments + (symbol_binding) @local.definition.parameter) + (#set! definition.function.scope "parent")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fidl/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fidl/folds.scm new file mode 100644 index 00000000..f524c455 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fidl/folds.scm @@ -0,0 +1,6 @@ +[ + (layout_declaration) + (protocol_declaration) + (resource_declaration) + (service_declaration) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fidl/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fidl/highlights.scm new file mode 100644 index 00000000..f1960c61 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fidl/highlights.scm @@ -0,0 +1,67 @@ +[ + "ajar" + "alias" + "as" + "bits" + "closed" + "compose" + "const" + "error" + "flexible" + "library" + "open" + ; "optional" we did not specify a node for optional yet + "overlay" + "protocol" + "reserved" + "strict" + "using" +] @keyword + +[ + "enum" + "struct" + "table" + "union" + "resource" + "service" + "type" +] @keyword.type + +(primitives_type) @type.builtin + +(builtin_complex_type) @type.builtin + +(const_declaration + (identifier) @constant) + +[ + "=" + "|" + "&" + "->" +] @operator + +(attribute + "@" @attribute + (identifier) @attribute) + +(string_literal) @string + +(numeric_literal) @number + +[ + (true) + (false) +] @boolean + +(comment) @comment + +[ + "(" + ")" + "<" + ">" + "{" + "}" +] @punctuation.bracket diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fidl/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fidl/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fidl/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/folds.scm new file mode 100644 index 00000000..4c64e644 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/folds.scm @@ -0,0 +1,6 @@ +[ + (circuit) + (module) + (when) + (else) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/highlights.scm new file mode 100644 index 00000000..b2cfd08a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/highlights.scm @@ -0,0 +1,198 @@ +; Namespaces +(circuit + (identifier) @module) + +(module + (identifier) @module) + +; Types +((identifier) @type + (#lua-match? @type "^[A-Z][A-Za-z0-9_$]*$")) + +; Keywords +[ + "circuit" + "module" + "extmodule" + "flip" + "parameter" + "reset" + "wire" + "cmem" + "smem" + "mem" + "reg" + "with" + "mport" + "inst" + "of" + "node" + "is" + "invalid" + "skip" + "infer" + "read" + "write" + "rdwr" + "defname" +] @keyword + +; Qualifiers +(qualifier) @keyword.modifier + +; Storageclasses +[ + "input" + "output" +] @keyword.modifier + +; Conditionals +[ + "when" + "else" +] @keyword.conditional + +; Annotations +(info) @attribute + +; Builtins +[ + "stop" + "printf" + "assert" + "assume" + "cover" + "attach" + "mux" + "validif" +] @function.builtin + +[ + "UInt" + "SInt" + "Analog" + "Fixed" + "Clock" + "AsyncReset" + "Reset" +] @type.builtin + +; Fields +[ + "data-type" + "depth" + "read-latency" + "write-latency" + "read-under-write" + "reader" + "writer" + "readwriter" +] @variable.member + +((field_id) @variable.member + (#set! "priority" 105)) + +(port + (identifier) @variable.member) + +(wire + (identifier) @variable.member) + +(cmem + (identifier) @variable.member) + +(smem + (identifier) @variable.member) + +(memory + (identifier) @variable.member) + +(register + (identifier) @variable.member) + +; Parameters +(primitive_operation + (identifier) @variable.parameter) + +(mux + (identifier) @variable.parameter) + +(printf + (identifier) @variable.parameter) + +(reset + (identifier) @variable.parameter) + +(stop + (identifier) @variable.parameter) + +; Variables +(identifier) @variable + +; Operators +(primop) @keyword.operator + +[ + "+" + "-" + "=" + "=>" + "<=" + "<-" +] @operator + +; Literals +[ + (uint) + (number) +] @number + +(number_str) @string.special + +(double) @number.float + +(string) @string + +(escape_sequence) @string.escape + +[ + "old" + "new" + "undefined" +] @constant.builtin + +; Punctuation +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "<" + ">" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "," + "." + ":" +] @punctuation.delimiter + +; Comments +(comment) @comment @spell + +[ + "=>" + "<=" + "=" +] @operator diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/indents.scm new file mode 100644 index 00000000..e172e1e8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/indents.scm @@ -0,0 +1,12 @@ +[ + (circuit) + (module) + (memory) + (when) + (else) +] @indent.begin + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/locals.scm new file mode 100644 index 00000000..97b7931b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/firrtl/locals.scm @@ -0,0 +1,45 @@ +; Scopes +[ + (source_file) + (circuit) + (module) + (else) + (when) +] @local.scope + +; References +(identifier) @local.reference + +; Definitions +(port + (identifier) @local.definition.field) + +(wire + (identifier) @local.definition.field) + +(cmem + (identifier) @local.definition.field) + +(smem + (identifier) @local.definition.field) + +(memory + (identifier) @local.definition.field) + +(register + (identifier) @local.definition.field) + +(circuit + (identifier) @local.definition.namespace) + +(module + (identifier) @local.definition.namespace) + +(parameter + (identifier) @local.definition.parameter) + +(rdwr + (identifier) @local.definition.var) + +(node + (identifier) @local.definition.var) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/folds.scm new file mode 100644 index 00000000..06363e15 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/folds.scm @@ -0,0 +1,8 @@ +[ + (function_definition) + (if_statement) + (switch_statement) + (for_statement) + (while_statement) + (begin_statement) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/highlights.scm new file mode 100644 index 00000000..10ed533d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/highlights.scm @@ -0,0 +1,176 @@ +; Fish highlighting +; Operators +[ + "&&" + "||" + "|" + "&|" + "2>|" + "&" + ".." + "!" + (direction) + (stream_redirect) +] @operator + +; match operators of test command +(command + name: (word) @function.builtin + (#eq? @function.builtin "test") + argument: (word) @operator + (#match? @operator "^(!?\\=|-[a-zA-Z]+)$")) + +; match operators of [ command +(command + name: (word) @punctuation.bracket + (#eq? @punctuation.bracket "[") + argument: (word) @operator + (#match? @operator "^(!?\\=|-[a-zA-Z]+)$")) + +[ + "not" + "and" + "or" +] @keyword.operator + +; Conditionals +(if_statement + [ + "if" + "end" + ] @keyword.conditional) + +(switch_statement + [ + "switch" + "end" + ] @keyword.conditional) + +(case_clause + "case" @keyword.conditional) + +(else_clause + "else" @keyword.conditional) + +(else_if_clause + [ + "else" + "if" + ] @keyword.conditional) + +; Loops/Blocks +(while_statement + [ + "while" + "end" + ] @keyword.repeat) + +(for_statement + [ + "for" + "end" + ] @keyword.repeat) + +(begin_statement + [ + "begin" + "end" + ] @keyword.repeat) + +; Keywords +[ + "in" + (break) + (continue) +] @keyword + +"return" @keyword.return + +; Punctuation +[ + "[" + "]" + "{" + "}" + "(" + ")" +] @punctuation.bracket + +"," @punctuation.delimiter + +; Commands +(command + argument: [ + (word) @variable.parameter + (#lua-match? @variable.parameter "^[-]") + ]) + +(command_substitution + "$" @punctuation.bracket) + +; non-builtin command names +(command + name: (word) @function.call) + +; derived from builtin -n (fish 3.2.2) +(command + name: [ + (word) @function.builtin + (#any-of? @function.builtin + "." ":" "_" "alias" "argparse" "bg" "bind" "block" "breakpoint" "builtin" "cd" "command" + "commandline" "complete" "contains" "count" "disown" "echo" "emit" "eval" "exec" "exit" "fg" + "functions" "history" "isatty" "jobs" "math" "printf" "pwd" "random" "read" "realpath" "set" + "set_color" "source" "status" "string" "test" "time" "type" "ulimit" "wait") + ]) + +; Functions +(function_definition + [ + "function" + "end" + ] @keyword.function) + +(function_definition + name: [ + (word) + (concatenation) + ] @function) + +(function_definition + option: [ + (word) + (concatenation + (word)) + ] @variable.parameter + (#lua-match? @variable.parameter "^[-]")) + +; Strings +[ + (double_quote_string) + (single_quote_string) +] @string + +(escape_sequence) @string.escape + +; Variables +(variable_name) @variable + +(variable_expansion) @constant + +; Nodes +[ + (integer) + (float) +] @number + +(comment) @comment + +(comment) @spell + +((word) @boolean + (#any-of? @boolean "true" "false")) + +((program + . + (comment) @keyword.directive @nospell) + (#lua-match? @keyword.directive "^#!/")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/indents.scm new file mode 100644 index 00000000..4984c4cb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/indents.scm @@ -0,0 +1,18 @@ +[ + (function_definition) + (while_statement) + (for_statement) + (if_statement) + (begin_statement) + (switch_statement) +] @indent.begin + +[ + "else" ; else and else if must both start the line with "else", so tag the string directly + "case" + "end" +] @indent.branch + +"end" @indent.end + +(comment) @indent.ignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/locals.scm new file mode 100644 index 00000000..904d568f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fish/locals.scm @@ -0,0 +1,19 @@ +; Scopes +[ + (command) + (function_definition) + (if_statement) + (for_statement) + (begin_statement) + (while_statement) + (switch_statement) +] @local.scope + +; Definitions +(function_definition + name: (word) @local.definition.function) + +; References +(variable_name) @local.reference + +(word) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/folds.scm new file mode 100644 index 00000000..e05d0dba --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/folds.scm @@ -0,0 +1,8 @@ +[ + (comment) + (list) + (dict_core) +] @fold + +(code + (code_body)* @fold) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/highlights.scm new file mode 100644 index 00000000..9c96f196 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/highlights.scm @@ -0,0 +1,64 @@ +; Comments +(comment) @comment @spell + +; Generic Key-value pairs and dictionary keywords +(key_value + keyword: (identifier) @function) + +(dict + key: (identifier) @type) + +; Macros +(macro + "$" @keyword.conditional + (prev_scope)* @keyword.conditional + (identifier)* @module) + +; Directives +"#" @keyword.conditional + +(preproc_call + directive: (identifier)* @keyword.conditional + argument: (identifier)* @module) + +((preproc_call + argument: (identifier)* @module) @keyword.conditional + (#eq? @keyword.conditional "ifeq")) + +((preproc_call) @keyword.conditional + (#any-of? @keyword.conditional "else" "endif")) + +; Literals +(number_literal) @number.float + +(string_literal) @string + +(escape_sequence) @string.escape + +(boolean) @boolean + +; Treat [m^2 s^-2] the same as if it was put in numbers format +(dimensions + dimension: (identifier) @number.float) + +; Punctuation +[ + "(" + ")" + "[" + "]" + "{" + "}" + "#{" + "#}" + "|-" + "-|" + "" + "$$" +] @punctuation.bracket + +";" @punctuation.delimiter + +((identifier) @constant.builtin + (#any-of? @constant.builtin "uniform" "non-uniform" "and" "or")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/indents.scm new file mode 100644 index 00000000..be02b80e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/indents.scm @@ -0,0 +1,11 @@ +[ + "{" + "}" +] @indent.branch + +[ + (dict) + (key_value) +] @indent.begin + +(comment) @indent.ignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/injections.scm new file mode 100644 index 00000000..e1c223bd --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/injections.scm @@ -0,0 +1,20 @@ +; Pass code blocks to Cpp highlighter +(code + (code_body) @injection.content + (#set! injection.language "cpp")) + +; Pass identifiers to Go highlighter (Cheating I know) +; ((identifier) @injection.content +; (#set! injection.language "lua") +; Highlight regex syntax inside literal strings +((string_literal) @injection.content + (#set! injection.language "regex")) + +; Highlight PyFoam syntax as Python statements +(pyfoam_variable + code_body: (_) @injection.content + (#set! injection.language "python")) + +(pyfoam_expression + code_body: (_) @injection.content + (#set! injection.language "python")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/locals.scm new file mode 100644 index 00000000..f3f68908 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/foam/locals.scm @@ -0,0 +1,11 @@ +(dict) @local.scope + +(dict + key: (_) @local.definition.type) + +(key_value + keyword: (_) @local.definition.parameter) + +(key_value + value: (macro + (identifier)*)* @local.reference) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/folds.scm new file mode 100644 index 00000000..443abb30 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/folds.scm @@ -0,0 +1 @@ +(word_definition) @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/highlights.scm new file mode 100644 index 00000000..1e720759 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/highlights.scm @@ -0,0 +1,19 @@ +(core) @function + +(operator) @operator + +(word) @variable + +((word) @constant + (#lua-match? @constant "^[A-Z_]+$")) + +(number) @number + +(string) @string + +[ + (start_definition) + (end_definition) +] @punctuation.delimiter + +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/indents.scm new file mode 100644 index 00000000..06775543 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/indents.scm @@ -0,0 +1,3 @@ +(word_definition) @indent.begin + +(end_definition) @indent.end @indent.branch diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/locals.scm new file mode 100644 index 00000000..d91d3aa9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/forth/locals.scm @@ -0,0 +1,3 @@ +(word) @local.reference + +(word_definition) @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fortran/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fortran/folds.scm new file mode 100644 index 00000000..cedbdb63 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fortran/folds.scm @@ -0,0 +1,11 @@ +; by @oponkork +[ + (if_statement) + (where_statement) + (enum_statement) + (do_loop_statement) + (derived_type_definition) + (function) + (subroutine) + (interface) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fortran/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fortran/highlights.scm new file mode 100644 index 00000000..34b612c8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fortran/highlights.scm @@ -0,0 +1,319 @@ +; Preprocs +(preproc_file_line) @keyword.directive + +; Namespaces +(program_statement + (name) @module) + +(end_program_statement + (name) @module) + +(module_statement + (name) @module) + +(end_module_statement + (name) @module) + +(submodule_statement + (name) @module) + +(end_submodule_statement + (name) @module) + +; Includes +[ + "import" + "include" + "use" +] @keyword.import + +(import_statement + "," + [ + "all" + "none" + ] @keyword) + +; Attributes +[ + (none) + "implicit" + "intent" +] @attribute + +(implicit_statement + "type" @attribute) + +; Keywords +[ + "attributes" + "associate" + "block" + "classis" + "contains" + "default" + "dimension" + "endassociate" + "endselect" + "enumerator" + "equivalence" + "extends" + "goto" + "intrinsic" + "non_intrinsic" + "namelist" + "parameter" + "quiet" + "rank" + "save" + "selectcase" + "selectrank" + "selecttype" + "sequence" + "stop" + "target" + "typeis" +] @keyword + +[ + "class" + "enum" + "endenum" + "type" + "endtype" + "module" + "endmodule" + "submodule" + "endsubmodule" + "interface" + "endinterface" +] @keyword.type + +(default) @keyword + +; Types +(type_name) @type + +(intrinsic_type) @type.builtin + +; Qualifiers +[ + "abstract" + "allocatable" + "automatic" + "constant" + "contiguous" + "data" + "deferred" + "device" + "external" + "family" + "final" + "generic" + "global" + "grid_global" + "host" + "initial" + "local" + "local_init" + "managed" + "nopass" + "non_overridable" + "optional" + "pass" + "pinned" + "pointer" + "private" + "property" + "protected" + "public" + "shared" + "static" + "texture" + "value" + "volatile" + (procedure_qualifier) +] @keyword.modifier + +[ + "common" + "in" + "inout" + "out" +] @keyword.modifier + +; Labels +[ + (statement_label) + (statement_label_reference) +] @label + +[ + "call" + "endfunction" + "endprogram" + "endprocedure" + "endsubroutine" + "function" + "procedure" + "program" + "subroutine" +] @keyword.function + +[ + "result" + "return" +] @keyword.return + +; Functions +(function_statement + (name) @function) + +(end_function_statement + (name) @function) + +(subroutine_statement + (name) @function) + +(end_subroutine_statement + (name) @function) + +(module_procedure_statement + (name) @function) + +(end_module_procedure_statement + (name) @function) + +(subroutine_call + (identifier) @function.call) + +[ + "character" + "close" + "bind" + "format" + "open" + "print" + "read" + "write" +] @function.builtin + +; Exceptions +"error" @keyword.exception + +; Conditionals +[ + "else" + "elseif" + "elsewhere" + "endif" + "endwhere" + "if" + "then" + "where" +] @keyword.conditional + +; Repeats +[ + "do" + "concurrent" + "enddo" + "endforall" + "forall" + "while" + "continue" + "cycle" + "exit" +] @keyword.repeat + +; Variables +(identifier) @variable + +; Parameters +(keyword_argument + name: (identifier) @variable.parameter) + +(parameters + (identifier) @variable.parameter) + +; Properties +(derived_type_member_expression + (type_member) @variable.member) + +; Operators +[ + "+" + "-" + "*" + "**" + "/" + "=" + "<" + ">" + "<=" + ">=" + "==" + "/=" + "//" + (assumed_rank) +] @operator + +[ + "\\.and\\." + "\\.or\\." + "\\.eqv\\." + "\\.neqv\\." + "\\.lt\\." + "\\.gt\\." + "\\.le\\." + "\\.ge\\." + "\\.eq\\." + "\\.ne\\." + "\\.not\\." +] @keyword.operator + +; Punctuation +[ + "[" + "]" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "<<<" + ">>>" +] @punctuation.bracket + +(array_literal + [ + "(/" + "/)" + ] @punctuation.bracket) + +[ + ":" + "," + "/" + "%" + "::" + "=>" +] @punctuation.delimiter + +; Literals +(string_literal) @string + +(number_literal) @number + +(boolean_literal) @boolean + +(null_literal) @constant.builtin + +; Comments +(comment) @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^!>")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fortran/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fortran/indents.scm new file mode 100644 index 00000000..86704c4f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fortran/indents.scm @@ -0,0 +1,27 @@ +[ + (module) + (program) + (subroutine) + (function) + ; (interface) + (if_statement) + (do_loop_statement) + (where_statement) + (derived_type_definition) + (enum) +] @indent.begin + +[ + (end_module_statement) + (end_program_statement) + (end_subroutine_statement) + (end_function_statement) + ; (end_interface_statement) + (end_if_statement) + (end_do_loop_statement) + (else_clause) + (elseif_clause) + (end_type_statement) + (end_enum_statement) + (end_where_statement) +] @indent.branch diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fsh/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fsh/highlights.scm new file mode 100644 index 00000000..2354a203 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fsh/highlights.scm @@ -0,0 +1,97 @@ +[ + "(" + ")" +] @punctuation.bracket + +[ + "^" + "=" + ":" +] @operator + +[ + "#" + ".." + "*" + "->" +] @punctuation.special + +; Entities +[ + "Profile" + "Alias" + "Extension" + "Invariant" + "Instance" + "ValueSet" + "CodeSystem" + "Mapping" + "Logical" + "Resource" + "RuleSet" +] @keyword + +; Metadata Keywords +[ + "Parent" + "Title" + "Description" + "Id" + "Severity" + "InstanceOf" + "Usage" + "Source" + "XPath" + "Target" +] @keyword + +; Rule Keywords +[ + "contentReference" + "insert" + "and" + "or" + "contains" + "named" + "only" + "obeys" + "valueset" + "codes" + "from" + "include" + "exclude" + "where" + "system" + "exactly" +] @keyword.operator + +; Types +[ + "Reference" + "Canonical" +] @type.builtin + +(sd_metadata + (parent + (name))) @type + +(target_type + (name)) @type + +; Strings +(string) @string + +(multiline_string) @string + +; Constants +(strength_value) @constant + +(bool) @boolean + +(flag) @constant + +; Special Params +(code_value) @variable.parameter + +; Extras +(fsh_comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/func/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/func/highlights.scm new file mode 100644 index 00000000..9fd6dd82 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/func/highlights.scm @@ -0,0 +1,167 @@ +; Include +"#include" @keyword.import + +(include_path) @string + +; Preproc +"#pragma" @keyword.directive + +(pragma_directive + [ + "version" + "not-version" + "test-version-set" + ] @keyword.directive) + +; Keywords +[ + "asm" + "impure" + "inline" + "inline_ref" + "method_id" + "type" +] @keyword + +"return" @keyword.return + +; Conditionals +[ + "if" + "ifnot" + "else" + "elseif" + "elseifnot" + "until" +] @keyword.conditional + +; Exceptions +[ + "try" + "catch" +] @keyword.exception + +; Repeats +[ + "do" + "forall" + "repeat" + "while" +] @keyword.repeat + +; Qualifiers +[ + "const" + "global" + (var) +] @keyword.modifier + +; Variables +(identifier) @variable + +; Constants +(const_var_declarations + name: (identifier) @constant) + +; Functions/Methods +(function_definition + name: (function_name) @function) + +(function_application + function: (identifier) @function) + +(method_call + method_name: (identifier) @function.method.call) + +; Parameters +(parameter) @variable.parameter + +; Types +(type_identifier) @type + +(primitive_type) @type.builtin + +; Operators +[ + "=" + "+=" + "-=" + "*=" + "/=" + "~/=" + "^/=" + "%=" + "~%=" + "^%=" + "<<=" + ">>=" + "~>>=" + "^>>=" + "&=" + "|=" + "^=" + "==" + "<" + ">" + "<=" + ">=" + "!=" + "<=>" + "<<" + ">>" + "~>>" + "^>>" + "-" + "+" + "|" + "^" + "*" + "/" + "%" + "~/" + "^/" + "~%" + "^%" + "/%" + "&" + "~" +] @operator + +; Literals +[ + (string) + (asm_instruction) +] @string + +[ + (string_type) + (underscore) +] @character.special + +(number) @number + +; Punctuation +[ + "{" + "}" +] @punctuation.bracket + +[ + "(" + ")" + "()" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + ";" + "," + "->" +] @punctuation.delimiter + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/folds.scm new file mode 100644 index 00000000..179fc160 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/folds.scm @@ -0,0 +1,6 @@ +[ + (comment) + (block) + (afx_comment) + (afx_element) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/highlights.scm new file mode 100644 index 00000000..7108e570 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/highlights.scm @@ -0,0 +1,132 @@ +(comment) @comment @spell + +(afx_comment) @comment @spell + +; identifiers afx +(afx_opening_element + (afx_identifier) @tag) + +(afx_closing_element + (afx_identifier) @tag) + +(afx_element_self_closing + (afx_identifier) @tag) + +(afx_attribute + (afx_property_identifier) @tag.attribute) + +(afx_text) @spell + +; identifiers eel +(eel_object_path + (eel_path_identifier) @variable.builtin + (#any-of? @variable.builtin "this" "props")) + +(eel_object_path + (eel_path_identifier) @variable) + +(eel_object_pair + key: (eel_property_name) @property) + +(eel_method_name) @function + +(eel_parameter) @variable + +; identifiers fusion +; ----------- +(path_part) @property + +(meta_property) @attribute + +(prototype_signature + "prototype" @keyword) + +(include_statement + "include" @keyword.import + (source_file) @string.special.url) + +(namespace_declaration + "namespace" @keyword.type + (alias_namespace) @module) + +(type + name: (type_name) @type) + +; tokens +; ------ +(afx_opening_element + [ + "<" + ">" + ] @punctuation.bracket) + +(afx_closing_element + [ + "<" + ">" + "/" + ] @punctuation.bracket) + +(afx_element_self_closing + [ + "<" + "/>" + ] @punctuation.bracket) + +[ + (package_name) + (alias_namespace) +] @module + +(namespace_declaration + "=" @operator) + +(assignment + "=" @operator) + +(copy + "<" @operator) + +(deletion) @operator + +(eel_binary_expression + operator: _ @operator) + +(eel_not_expression + [ + "!" + "not" + ] @operator) + +(string) @string + +(number) @number + +(boolean) @boolean + +(null) @constant.builtin + +(value_expression + start: _ @punctuation.special + end: _ @punctuation.special) + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + ":" + "." + "?" +] @punctuation.delimiter + +(eel_ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/indents.scm new file mode 100644 index 00000000..0ba6cf75 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/indents.scm @@ -0,0 +1,24 @@ +[ + (block) + (value_dsl) + (afx_element) + (afx_element_self_closing) + (eel_array) + (eel_object) +] @indent.begin + +(block + end: _ @indent.branch) + +(value_dsl + end: _ @indent.branch) + +(eel_array + end: _ @indent.branch) + +(eel_object + end: _ @indent.branch) + +(afx_closing_element) @indent.branch + +(comment) @indent.ignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/locals.scm new file mode 100644 index 00000000..d23e0ab4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/fusion/locals.scm @@ -0,0 +1,23 @@ +; Fusion base +(block) @local.scope + +(namespace_declaration + (alias_namespace) @local.definition.namespace) + +(property + (path + (path_part) @local.definition.field)) + +(type + namespace: (package_name)? @local.definition.namespace + name: (type_name) @local.definition.type) + +; Eel Expressions +(eel_arrow_function) @local.scope + +(eel_object) @local.scope + +(eel_parameter) @local.definition.parameter + +(eel_object_pair + key: (eel_property_name) @local.definition.field) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/folds.scm new file mode 100644 index 00000000..cda70907 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/folds.scm @@ -0,0 +1,26 @@ +[ + ; Body fold will "join" the next adjacent fold into a SUPER fold. + ; This is an issue with the grammar. + ; (body) + (if_statement) + (elif_clause) + (else_clause) + (for_statement) + (while_statement) + (class_definition) + (enum_definition) + (match_statement) + (pattern_section) + (function_definition) + (lambda) + (constructor_definition) +] @fold + +; It's nice to be able to fold the if/elif/else clauses and the entire +; if_statement. +(if_statement + (body) @fold) + +; Fold strings that are probably doc strings. +(expression_statement + (string) @fold) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/highlights.scm new file mode 100644 index 00000000..9f122e47 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/highlights.scm @@ -0,0 +1,421 @@ +; Basic +(identifier) @variable + +(name) @variable + +(type + (identifier) @type) + +(comment) @comment @spell + +(string_name) @string + +(string) @string + +(float) @number.float + +(integer) @number + +(null) @constant + +(setter) @function + +(getter) @function + +(set_body + "set" @keyword.function) + +(get_body + "get" @keyword.function) + +(static_keyword) @keyword.modifier + +(tool_statement) @keyword + +(breakpoint_statement) @keyword.debug + +(inferred_type) @operator + +[ + (true) + (false) +] @boolean + +[ + (get_node) + (node_path) +] @string.special.url + +(class_name_statement + (name) @type) @keyword + +(const_statement + "const" @keyword.modifier + (name) @constant) + +(expression_statement + (string) @comment @spell) + +; Functions +(constructor_definition + "_init" @constructor) + +(function_definition + (name) @function) + +(parameters + (identifier) @variable.parameter) + +(typed_parameter + (identifier) @variable.parameter) + +(default_parameter + (identifier) @variable.parameter) + +(typed_default_parameter + (identifier) @variable.parameter) + +(call + (identifier) @function.call) + +(call + (identifier) @keyword.import + (#any-of? @keyword.import "preload" "load")) + +; Properties and Methods +; We'll use @property since that's the term Godot uses. +; But, should (source (variable_statement (name))) be @property, too? Since a +; script file is a class in gdscript. +(class_definition + (body + (variable_statement + (name) @property))) + +; Same question but for methods? +(class_definition + (body + (function_definition + (name) @function.method))) + +(attribute_call + (identifier) @function.method.call) + +(attribute_subscript + (identifier) @property) + +(attribute + (_) + (identifier) @property) + +; Identifier naming conventions +; - Make sure the following query is below the attribute queries so that it +; takes precedence on a `(type (attribute (identifier)))` +((identifier) @type + (#lua-match? @type "^[A-Z]")) + +((identifier) @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) + +; Enums +(enumerator + left: (identifier) @constant) + +; Special Builtins +((identifier) @variable.builtin + (#any-of? @variable.builtin "self" "super")) + +(attribute_call + (identifier) @keyword.operator + (#eq? @keyword.operator "new")) + +; Match Pattern +(underscore) @constant ; The "_" pattern. + +(pattern_open_ending) @operator ; The ".." pattern. + +; Alternations +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + "," + "." + ":" +] @punctuation.delimiter + +[ + "if" + "elif" + "else" + "match" +] @keyword.conditional + +(pattern_guard + "when" @keyword.conditional) + +[ + "for" + "while" + "break" + "continue" +] @keyword.repeat + +[ + "~" + "-" + "*" + "/" + "%" + "+" + "-" + "<<" + ">>" + "&" + "^" + "|" + "<" + ">" + "==" + "!=" + ">=" + "<=" + "!" + "&&" + "||" + "=" + "+=" + "-=" + "*=" + "/=" + "%=" + "&=" + "|=" + "->" +] @operator + +[ + "and" + "as" + "in" + "is" + "not" + "or" +] @keyword.operator + +[ + "pass" + "class_name" + "extends" + "signal" + "var" + "onready" + "setget" + "remote" + "master" + "puppet" + "remotesync" + "mastersync" + "puppetsync" +] @keyword + +"export" @keyword.import + +[ + "enum" + "class" +] @keyword.type + +"func" @keyword.function + +"return" @keyword.return + +"await" @keyword.coroutine + +(call + (identifier) @keyword.coroutine + (#eq? @keyword.coroutine "yield")) + +; Builtins +; generated from +; - godot commit: fb10e67fef +; - https://github.com/godotengine/godot/blob/fb10e67fef/doc/classes +; - https://github.com/godotengine/godot/blob/fb10e67fef/doc/classes/@GlobalScope.xml +; - https://github.com/godotengine/godot/blob/fb10e67fef/modules/gdscript/doc_classes/@GDScript.xml +; Built-in Annotations +((annotation + "@" @attribute + (identifier) @attribute) + (#any-of? @attribute + ; from modules/gdscript/doc_classes/@GDScript.xml + "export" "export_category" "export_color_no_alpha" "export_dir" "export_enum" + "export_exp_easing" "export_file" "export_flags" "export_flags_2d_navigation" + "export_flags_2d_physics" "export_flags_2d_render" "export_flags_3d_navigation" + "export_flags_3d_physics" "export_flags_3d_render" "export_flags_avoidance" "export_global_dir" + "export_global_file" "export_group" "export_multiline" "export_node_path" "export_placeholder" + "export_range" "export_subgroup" "icon" "onready" "rpc" "static_unload" "tool" "warning_ignore")) + +; Builtin Types +((identifier) @type.builtin + (#any-of? @type.builtin + ; from doc/classes/*.xml + "AABB" "Array" "Basis" "Callable" "Color" "Dictionary" "NodePath" "PackedByteArray" + "PackedColorArray" "PackedFloat32Array" "PackedFloat64Array" "PackedInt32Array" + "PackedInt64Array" "PackedStringArray" "PackedVector2Array" "PackedVector3Array" "Plane" + "Projection" "Quaternion" "RID" "Rect2" "Rect2i" "Signal" "String" "StringName" "Transform2D" + "Transform3D" "Vector2" "Vector2i" "Vector3" "Vector3i" "Vector4" "Vector4i" "bool" "float" + "int" + ; from doc/classes/@GlobalScope.xml + "AudioServer" "CameraServer" "ClassDB" "DisplayServer" "EditorInterface" "Engine" + "EngineDebugger" "GDExtensionManager" "Geometry2D" "Geometry3D" "GodotSharp" "IP" "Input" + "InputMap" "JavaClassWrapper" "JavaScriptBridge" "Marshalls" "NavigationMeshGenerator" + "NavigationServer2D" "NavigationServer3D" "OS" "Performance" "PhysicsServer2D" + "PhysicsServer2DManager" "PhysicsServer3D" "PhysicsServer3DManager" "ProjectSettings" + "RenderingServer" "ResourceLoader" "ResourceSaver" "ResourceUID" "TextServerManager" "ThemeDB" + "Time" "TranslationServer" "WorkerThreadPool" "XRServer")) + +; Builtin Funcs +(call + (identifier) @function.builtin + (#any-of? @function.builtin + ; from doc/classes/@GlobalScope.xml + "abs" "absf" "absi" "acos" "acosh" "angle_difference" "asin" "asinh" "atan" "atan2" "atanh" + "bezier_derivative" "bezier_interpolate" "bytes_to_var" "bytes_to_var_with_objects" "ceil" + "ceilf" "ceili" "clamp" "clampf" "clampi" "cos" "cosh" "cubic_interpolate" + "cubic_interpolate_angle" "cubic_interpolate_angle_in_time" "cubic_interpolate_in_time" + "db_to_linear" "deg_to_rad" "ease" "error_string" "exp" "floor" "floorf" "floori" "fmod" + "fposmod" "hash" "instance_from_id" "inverse_lerp" "is_equal_approx" "is_finite" "is_inf" + "is_instance_id_valid" "is_instance_valid" "is_nan" "is_same" "is_zero_approx" "lerp" + "lerp_angle" "lerpf" "linear_to_db" "log" "max" "maxf" "maxi" "min" "minf" "mini" "move_toward" + "nearest_po2" "pingpong" "posmod" "pow" "print" "print_rich" "print_verbose" "printerr" + "printraw" "prints" "printt" "push_error" "push_warning" "rad_to_deg" "rand_from_seed" "randf" + "randf_range" "randfn" "randi" "randi_range" "randomize" "remap" "rid_allocate_id" + "rid_from_int64" "rotate_toward" "round" "roundf" "roundi" "seed" "sign" "signf" "signi" "sin" + "sinh" "smoothstep" "snapped" "snappedf" "snappedi" "sqrt" "step_decimals" "str" "str_to_var" + "tan" "tanh" "type_convert" "type_string" "typeof" "var_to_bytes" "var_to_bytes_with_objects" + "var_to_str" "weakref" "wrap" "wrapf" "wrapi" + ; from modules/gdscript/doc_classes/@GDScript.xml + "Color8" "assert" "char" "convert" "dict_to_inst" "get_stack" "inst_to_dict" "is_instance_of" + "len" "load" "preload" "print_debug" "print_stack" "range" "type_exists") + ) + +; Builtin Constants +((identifier) @constant.builtin + (#any-of? @constant.builtin + ; from modules/gdscript/doc_classes/@GDScript.xml + "INF" "NAN" "PI" "TAU" + ; from doc/classes/@GlobalScope.xml + "CLOCKWISE" "CORNER_BOTTOM_LEFT" "CORNER_BOTTOM_RIGHT" "CORNER_TOP_LEFT" "CORNER_TOP_RIGHT" + "COUNTERCLOCKWISE" "ERR_ALREADY_EXISTS" "ERR_ALREADY_IN_USE" "ERR_BUG" "ERR_BUSY" + "ERR_CANT_ACQUIRE_RESOURCE" "ERR_CANT_CONNECT" "ERR_CANT_CREATE" "ERR_CANT_FORK" "ERR_CANT_OPEN" + "ERR_CANT_RESOLVE" "ERR_COMPILATION_FAILED" "ERR_CONNECTION_ERROR" "ERR_CYCLIC_LINK" + "ERR_DATABASE_CANT_READ" "ERR_DATABASE_CANT_WRITE" "ERR_DOES_NOT_EXIST" "ERR_DUPLICATE_SYMBOL" + "ERR_FILE_ALREADY_IN_USE" "ERR_FILE_BAD_DRIVE" "ERR_FILE_BAD_PATH" "ERR_FILE_CANT_OPEN" + "ERR_FILE_CANT_READ" "ERR_FILE_CANT_WRITE" "ERR_FILE_CORRUPT" "ERR_FILE_EOF" + "ERR_FILE_MISSING_DEPENDENCIES" "ERR_FILE_NOT_FOUND" "ERR_FILE_NO_PERMISSION" + "ERR_FILE_UNRECOGNIZED" "ERR_HELP" "ERR_INVALID_DATA" "ERR_INVALID_DECLARATION" + "ERR_INVALID_PARAMETER" "ERR_LINK_FAILED" "ERR_LOCKED" "ERR_METHOD_NOT_FOUND" + "ERR_OUT_OF_MEMORY" "ERR_PARAMETER_RANGE_ERROR" "ERR_PARSE_ERROR" "ERR_PRINTER_ON_FIRE" + "ERR_QUERY_FAILED" "ERR_SCRIPT_FAILED" "ERR_SKIP" "ERR_TIMEOUT" "ERR_UNAUTHORIZED" + "ERR_UNAVAILABLE" "ERR_UNCONFIGURED" "EULER_ORDER_XYZ" "EULER_ORDER_XZY" "EULER_ORDER_YXZ" + "EULER_ORDER_YZX" "EULER_ORDER_ZXY" "EULER_ORDER_ZYX" "FAILED" "HORIZONTAL" + "HORIZONTAL_ALIGNMENT_CENTER" "HORIZONTAL_ALIGNMENT_FILL" "HORIZONTAL_ALIGNMENT_LEFT" + "HORIZONTAL_ALIGNMENT_RIGHT" "INLINE_ALIGNMENT_BASELINE_TO" "INLINE_ALIGNMENT_BOTTOM" + "INLINE_ALIGNMENT_BOTTOM_TO" "INLINE_ALIGNMENT_CENTER" "INLINE_ALIGNMENT_CENTER_TO" + "INLINE_ALIGNMENT_IMAGE_MASK" "INLINE_ALIGNMENT_TEXT_MASK" "INLINE_ALIGNMENT_TOP" + "INLINE_ALIGNMENT_TOP_TO" "INLINE_ALIGNMENT_TO_BASELINE" "INLINE_ALIGNMENT_TO_BOTTOM" + "INLINE_ALIGNMENT_TO_CENTER" "INLINE_ALIGNMENT_TO_TOP" "JOY_AXIS_INVALID" "JOY_AXIS_LEFT_X" + "JOY_AXIS_LEFT_Y" "JOY_AXIS_MAX" "JOY_AXIS_RIGHT_X" "JOY_AXIS_RIGHT_Y" "JOY_AXIS_SDL_MAX" + "JOY_AXIS_TRIGGER_LEFT" "JOY_AXIS_TRIGGER_RIGHT" "JOY_BUTTON_A" "JOY_BUTTON_B" "JOY_BUTTON_BACK" + "JOY_BUTTON_DPAD_DOWN" "JOY_BUTTON_DPAD_LEFT" "JOY_BUTTON_DPAD_RIGHT" "JOY_BUTTON_DPAD_UP" + "JOY_BUTTON_GUIDE" "JOY_BUTTON_INVALID" "JOY_BUTTON_LEFT_SHOULDER" "JOY_BUTTON_LEFT_STICK" + "JOY_BUTTON_MAX" "JOY_BUTTON_MISC1" "JOY_BUTTON_PADDLE1" "JOY_BUTTON_PADDLE2" + "JOY_BUTTON_PADDLE3" "JOY_BUTTON_PADDLE4" "JOY_BUTTON_RIGHT_SHOULDER" "JOY_BUTTON_RIGHT_STICK" + "JOY_BUTTON_SDL_MAX" "JOY_BUTTON_START" "JOY_BUTTON_TOUCHPAD" "JOY_BUTTON_X" "JOY_BUTTON_Y" + "KEY_0" "KEY_1" "KEY_2" "KEY_3" "KEY_4" "KEY_5" "KEY_6" "KEY_7" "KEY_8" "KEY_9" "KEY_A" + "KEY_ALT" "KEY_AMPERSAND" "KEY_APOSTROPHE" "KEY_ASCIICIRCUM" "KEY_ASCIITILDE" "KEY_ASTERISK" + "KEY_AT" "KEY_B" "KEY_BACK" "KEY_BACKSLASH" "KEY_BACKSPACE" "KEY_BACKTAB" "KEY_BAR" + "KEY_BRACELEFT" "KEY_BRACERIGHT" "KEY_BRACKETLEFT" "KEY_BRACKETRIGHT" "KEY_C" "KEY_CAPSLOCK" + "KEY_CLEAR" "KEY_CODE_MASK" "KEY_COLON" "KEY_COMMA" "KEY_CTRL" "KEY_D" "KEY_DELETE" "KEY_DOLLAR" + "KEY_DOWN" "KEY_E" "KEY_END" "KEY_ENTER" "KEY_EQUAL" "KEY_ESCAPE" "KEY_EXCLAM" "KEY_F" "KEY_F1" + "KEY_F10" "KEY_F11" "KEY_F12" "KEY_F13" "KEY_F14" "KEY_F15" "KEY_F16" "KEY_F17" "KEY_F18" + "KEY_F19" "KEY_F2" "KEY_F20" "KEY_F21" "KEY_F22" "KEY_F23" "KEY_F24" "KEY_F25" "KEY_F26" + "KEY_F27" "KEY_F28" "KEY_F29" "KEY_F3" "KEY_F30" "KEY_F31" "KEY_F32" "KEY_F33" "KEY_F34" + "KEY_F35" "KEY_F4" "KEY_F5" "KEY_F6" "KEY_F7" "KEY_F8" "KEY_F9" "KEY_FAVORITES" "KEY_FORWARD" + "KEY_G" "KEY_GLOBE" "KEY_GREATER" "KEY_H" "KEY_HELP" "KEY_HOME" "KEY_HOMEPAGE" "KEY_HYPER" + "KEY_I" "KEY_INSERT" "KEY_J" "KEY_JIS_EISU" "KEY_JIS_KANA" "KEY_K" "KEY_KEYBOARD" "KEY_KP_0" + "KEY_KP_1" "KEY_KP_2" "KEY_KP_3" "KEY_KP_4" "KEY_KP_5" "KEY_KP_6" "KEY_KP_7" "KEY_KP_8" + "KEY_KP_9" "KEY_KP_ADD" "KEY_KP_DIVIDE" "KEY_KP_ENTER" "KEY_KP_MULTIPLY" "KEY_KP_PERIOD" + "KEY_KP_SUBTRACT" "KEY_L" "KEY_LAUNCH0" "KEY_LAUNCH1" "KEY_LAUNCH2" "KEY_LAUNCH3" "KEY_LAUNCH4" + "KEY_LAUNCH5" "KEY_LAUNCH6" "KEY_LAUNCH7" "KEY_LAUNCH8" "KEY_LAUNCH9" "KEY_LAUNCHA" + "KEY_LAUNCHB" "KEY_LAUNCHC" "KEY_LAUNCHD" "KEY_LAUNCHE" "KEY_LAUNCHF" "KEY_LAUNCHMAIL" + "KEY_LAUNCHMEDIA" "KEY_LEFT" "KEY_LESS" "KEY_LOCATION_LEFT" "KEY_LOCATION_RIGHT" + "KEY_LOCATION_UNSPECIFIED" "KEY_M" "KEY_MASK_ALT" "KEY_MASK_CMD_OR_CTRL" "KEY_MASK_CTRL" + "KEY_MASK_GROUP_SWITCH" "KEY_MASK_KPAD" "KEY_MASK_META" "KEY_MASK_SHIFT" "KEY_MEDIANEXT" + "KEY_MEDIAPLAY" "KEY_MEDIAPREVIOUS" "KEY_MEDIARECORD" "KEY_MEDIASTOP" "KEY_MENU" "KEY_META" + "KEY_MINUS" "KEY_MODIFIER_MASK" "KEY_N" "KEY_NONE" "KEY_NUMBERSIGN" "KEY_NUMLOCK" "KEY_O" + "KEY_OPENURL" "KEY_P" "KEY_PAGEDOWN" "KEY_PAGEUP" "KEY_PARENLEFT" "KEY_PARENRIGHT" "KEY_PAUSE" + "KEY_PERCENT" "KEY_PERIOD" "KEY_PLUS" "KEY_PRINT" "KEY_Q" "KEY_QUESTION" "KEY_QUOTEDBL" + "KEY_QUOTELEFT" "KEY_R" "KEY_REFRESH" "KEY_RIGHT" "KEY_S" "KEY_SCROLLLOCK" "KEY_SEARCH" + "KEY_SECTION" "KEY_SEMICOLON" "KEY_SHIFT" "KEY_SLASH" "KEY_SPACE" "KEY_SPECIAL" "KEY_STANDBY" + "KEY_STOP" "KEY_SYSREQ" "KEY_T" "KEY_TAB" "KEY_U" "KEY_UNDERSCORE" "KEY_UNKNOWN" "KEY_UP" + "KEY_V" "KEY_VOLUMEDOWN" "KEY_VOLUMEMUTE" "KEY_VOLUMEUP" "KEY_W" "KEY_X" "KEY_Y" "KEY_YEN" + "KEY_Z" "METHOD_FLAGS_DEFAULT" "METHOD_FLAG_CONST" "METHOD_FLAG_EDITOR" "METHOD_FLAG_NORMAL" + "METHOD_FLAG_OBJECT_CORE" "METHOD_FLAG_STATIC" "METHOD_FLAG_VARARG" "METHOD_FLAG_VIRTUAL" + "MIDI_MESSAGE_ACTIVE_SENSING" "MIDI_MESSAGE_AFTERTOUCH" "MIDI_MESSAGE_CHANNEL_PRESSURE" + "MIDI_MESSAGE_CONTINUE" "MIDI_MESSAGE_CONTROL_CHANGE" "MIDI_MESSAGE_NONE" + "MIDI_MESSAGE_NOTE_OFF" "MIDI_MESSAGE_NOTE_ON" "MIDI_MESSAGE_PITCH_BEND" + "MIDI_MESSAGE_PROGRAM_CHANGE" "MIDI_MESSAGE_QUARTER_FRAME" "MIDI_MESSAGE_SONG_POSITION_POINTER" + "MIDI_MESSAGE_SONG_SELECT" "MIDI_MESSAGE_START" "MIDI_MESSAGE_STOP" + "MIDI_MESSAGE_SYSTEM_EXCLUSIVE" "MIDI_MESSAGE_SYSTEM_RESET" "MIDI_MESSAGE_TIMING_CLOCK" + "MIDI_MESSAGE_TUNE_REQUEST" "MOUSE_BUTTON_LEFT" "MOUSE_BUTTON_MASK_LEFT" + "MOUSE_BUTTON_MASK_MB_XBUTTON1" "MOUSE_BUTTON_MASK_MB_XBUTTON2" "MOUSE_BUTTON_MASK_MIDDLE" + "MOUSE_BUTTON_MASK_RIGHT" "MOUSE_BUTTON_MIDDLE" "MOUSE_BUTTON_NONE" "MOUSE_BUTTON_RIGHT" + "MOUSE_BUTTON_WHEEL_DOWN" "MOUSE_BUTTON_WHEEL_LEFT" "MOUSE_BUTTON_WHEEL_RIGHT" + "MOUSE_BUTTON_WHEEL_UP" "MOUSE_BUTTON_XBUTTON1" "MOUSE_BUTTON_XBUTTON2" "OK" "OP_ADD" "OP_AND" + "OP_BIT_AND" "OP_BIT_NEGATE" "OP_BIT_OR" "OP_BIT_XOR" "OP_DIVIDE" "OP_EQUAL" "OP_GREATER" + "OP_GREATER_EQUAL" "OP_IN" "OP_LESS" "OP_LESS_EQUAL" "OP_MAX" "OP_MODULE" "OP_MULTIPLY" + "OP_NEGATE" "OP_NOT" "OP_NOT_EQUAL" "OP_OR" "OP_POSITIVE" "OP_POWER" "OP_SHIFT_LEFT" + "OP_SHIFT_RIGHT" "OP_SUBTRACT" "OP_XOR" "PROPERTY_HINT_ARRAY_TYPE" + "PROPERTY_HINT_COLOR_NO_ALPHA" "PROPERTY_HINT_DIR" "PROPERTY_HINT_ENUM" + "PROPERTY_HINT_ENUM_SUGGESTION" "PROPERTY_HINT_EXPRESSION" "PROPERTY_HINT_EXP_EASING" + "PROPERTY_HINT_FILE" "PROPERTY_HINT_FLAGS" "PROPERTY_HINT_GLOBAL_DIR" + "PROPERTY_HINT_GLOBAL_FILE" "PROPERTY_HINT_GLOBAL_SAVE_FILE" + "PROPERTY_HINT_HIDE_QUATERNION_EDIT" "PROPERTY_HINT_INT_IS_OBJECTID" + "PROPERTY_HINT_INT_IS_POINTER" "PROPERTY_HINT_LAYERS_2D_NAVIGATION" + "PROPERTY_HINT_LAYERS_2D_PHYSICS" "PROPERTY_HINT_LAYERS_2D_RENDER" + "PROPERTY_HINT_LAYERS_3D_NAVIGATION" "PROPERTY_HINT_LAYERS_3D_PHYSICS" + "PROPERTY_HINT_LAYERS_3D_RENDER" "PROPERTY_HINT_LAYERS_AVOIDANCE" "PROPERTY_HINT_LINK" + "PROPERTY_HINT_LOCALE_ID" "PROPERTY_HINT_LOCALIZABLE_STRING" "PROPERTY_HINT_MAX" + "PROPERTY_HINT_MULTILINE_TEXT" "PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" + "PROPERTY_HINT_NODE_PATH_VALID_TYPES" "PROPERTY_HINT_NODE_TYPE" "PROPERTY_HINT_NONE" + "PROPERTY_HINT_OBJECT_ID" "PROPERTY_HINT_OBJECT_TOO_BIG" "PROPERTY_HINT_PASSWORD" + "PROPERTY_HINT_PLACEHOLDER_TEXT" "PROPERTY_HINT_RANGE" "PROPERTY_HINT_RESOURCE_TYPE" + "PROPERTY_HINT_SAVE_FILE" "PROPERTY_HINT_TYPE_STRING" "PROPERTY_USAGE_ALWAYS_DUPLICATE" + "PROPERTY_USAGE_ARRAY" "PROPERTY_USAGE_CATEGORY" "PROPERTY_USAGE_CHECKABLE" + "PROPERTY_USAGE_CHECKED" "PROPERTY_USAGE_CLASS_IS_BITFIELD" "PROPERTY_USAGE_CLASS_IS_ENUM" + "PROPERTY_USAGE_DEFAULT" "PROPERTY_USAGE_DEFERRED_SET_RESOURCE" "PROPERTY_USAGE_EDITOR" + "PROPERTY_USAGE_EDITOR_BASIC_SETTING" "PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT" + "PROPERTY_USAGE_GROUP" "PROPERTY_USAGE_HIGH_END_GFX" "PROPERTY_USAGE_INTERNAL" + "PROPERTY_USAGE_KEYING_INCREMENTS" "PROPERTY_USAGE_NEVER_DUPLICATE" + "PROPERTY_USAGE_NIL_IS_VARIANT" "PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT" "PROPERTY_USAGE_NONE" + "PROPERTY_USAGE_NO_EDITOR" "PROPERTY_USAGE_NO_INSTANCE_STATE" "PROPERTY_USAGE_READ_ONLY" + "PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT" "PROPERTY_USAGE_RESTART_IF_CHANGED" + "PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE" "PROPERTY_USAGE_SCRIPT_VARIABLE" "PROPERTY_USAGE_SECRET" + "PROPERTY_USAGE_STORAGE" "PROPERTY_USAGE_STORE_IF_NULL" "PROPERTY_USAGE_SUBGROUP" + "PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED" "SIDE_BOTTOM" "SIDE_LEFT" "SIDE_RIGHT" "SIDE_TOP" + "TYPE_AABB" "TYPE_ARRAY" "TYPE_BASIS" "TYPE_BOOL" "TYPE_CALLABLE" "TYPE_COLOR" "TYPE_DICTIONARY" + "TYPE_FLOAT" "TYPE_INT" "TYPE_MAX" "TYPE_NIL" "TYPE_NODE_PATH" "TYPE_OBJECT" + "TYPE_PACKED_BYTE_ARRAY" "TYPE_PACKED_COLOR_ARRAY" "TYPE_PACKED_FLOAT32_ARRAY" + "TYPE_PACKED_FLOAT64_ARRAY" "TYPE_PACKED_INT32_ARRAY" "TYPE_PACKED_INT64_ARRAY" + "TYPE_PACKED_STRING_ARRAY" "TYPE_PACKED_VECTOR2_ARRAY" "TYPE_PACKED_VECTOR3_ARRAY" "TYPE_PLANE" + "TYPE_PROJECTION" "TYPE_QUATERNION" "TYPE_RECT2" "TYPE_RECT2I" "TYPE_RID" "TYPE_SIGNAL" + "TYPE_STRING" "TYPE_STRING_NAME" "TYPE_TRANSFORM2D" "TYPE_TRANSFORM3D" "TYPE_VECTOR2" + "TYPE_VECTOR2I" "TYPE_VECTOR3" "TYPE_VECTOR3I" "TYPE_VECTOR4" "TYPE_VECTOR4I" "VERTICAL" + "VERTICAL_ALIGNMENT_BOTTOM" "VERTICAL_ALIGNMENT_CENTER" "VERTICAL_ALIGNMENT_FILL" + "VERTICAL_ALIGNMENT_TOP")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/indents.scm new file mode 100644 index 00000000..36b989f9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/indents.scm @@ -0,0 +1,78 @@ +[ + (lambda) + (function_definition) + (constructor_definition) + (for_statement) + (while_statement) + (if_statement) + (class_definition) + (match_statement) + (pattern_section) + (setget) + (match_body) + (set_body) + (get_body) +] @indent.begin + +[ + (elif_clause) + (else_clause) +] @indent.branch + +[ + (string) + (comment) + (array) + (dictionary) + (parenthesized_expression) + (ERROR) +] @indent.auto + +[ + (pass_statement) + (continue_statement) + (break_statement) + (return_statement) +] @indent.dedent + +[ + (ERROR + "[") + (ERROR + "(") + (ERROR + "{") +] @indent.begin + +; This only works with expanded tabs. +; ((parameters) @indent.align (#set! indent.open_delimiter "(") (#set! indent.close_delimiter ")")) +; ((arguments) @indent.align (#set! indent.open_delimiter "(") (#set! indent.close_delimiter ")")) +; The following queries either do not agree with the current body parsing or are +; attempted workarounds. Specifically as the last statement of a body. Opening +; a new line in between statements works well. +; +; The overall experience is poor, so I've opted for @indent.auto. +; +; The gdscript parser will need to be patched to accommodate more interactive +; edits. As far as I can tell the parser greedily consumes whitespace +; as a zero-width token which causes trouble when inserting indents. +; This indents correctly with tabs. +; (arguments) @indent.begin +; (parameters) @indent.begin +; (array) @indent.begin +; (dictionary) @indent.begin +; (parenthesized_expression) @indent.begin +; Partial workaround for when the cursor is on the bracket character and a newline +; is created with . Without this the newline is opened with extra +; indentation. +; (body (_ (array "]" @indent.end) ) _) +; Problematic behaviors occur at the last statement of a body. +; with @dedent: +; - [ | ] i will dedent ] to 0. +; - [ +; ]| o will open new line at correct indentation. +; with @auto: +; - [ | ] i same +; - [ +; ]| o will open new line with extra indent. +;(body (_ (array "]" @indent.auto) ) .) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/locals.scm new file mode 100644 index 00000000..796ec187 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdscript/locals.scm @@ -0,0 +1,121 @@ +; Scopes +[ + (if_statement) + (elif_clause) + (else_clause) + (for_statement) + (while_statement) + (function_definition) + (constructor_definition) + (class_definition) + (match_statement) + (pattern_section) + (lambda) + (get_body) + (set_body) +] @local.scope + +; Parameters +(parameters + (identifier) @local.definition.parameter) + +(default_parameter + (identifier) @local.definition.parameter) + +(typed_parameter + (identifier) @local.definition.parameter) + +(typed_default_parameter + (identifier) @local.definition.parameter) + +; Signals +; Can gdscript 2 signals be considered fields? +(signal_statement + (name) @local.definition.field) + +; Variable Definitions +(const_statement + (name) @local.definition.constant) + +; onready and export variations are only properties. +(variable_statement + (name) @local.definition.var) + +(setter) @local.reference + +(getter) @local.reference + +; Function Definition +((function_definition + (name) @local.definition.function) + (#set! "definition.function.scope" "parent")) + +; Lambda +; lambda names are not accessible and are only for debugging. +(lambda + (name) @local.definition.function) + +; Source +(class_name_statement + (name) @local.definition.type) + +(source + (variable_statement + (name) @local.definition.field)) + +(source + (onready_variable_statement + (name) @local.definition.field)) + +(source + (export_variable_statement + (name) @local.definition.field)) + +; Class +((class_definition + (name) @local.definition.type) + (#set! "definition.type.scope" "parent")) + +(class_definition + (body + (variable_statement + (name) @local.definition.field))) + +(class_definition + (body + (onready_variable_statement + (name) @local.definition.field))) + +(class_definition + (body + (export_variable_statement + (name) @local.definition.field))) + +(class_definition + (body + (signal_statement + (name) @local.definition.field))) + +; Although a script is also a class, let's only define functions in an inner class as +; methods. +((class_definition + (body + (function_definition + (name) @local.definition.method))) + (#set! "definition.method.scope" "parent")) + +; Enum +(enum_definition + (name) @local.definition.enum) + +; Repeat +(for_statement + . + (identifier) @local.definition.var) + +; Match Statement +(pattern_binding + (identifier) @local.definition.var) + +; References +(identifier) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdshader/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdshader/highlights.scm new file mode 100644 index 00000000..c93fd472 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdshader/highlights.scm @@ -0,0 +1,142 @@ +[ + "render_mode" + "shader_type" + "group_uniforms" + "global" + "instance" + "const" + "varying" + "uniform" +] @keyword + +"struct" @keyword.type + +[ + (precision_qualifier) + (interpolation_qualifier) +] @keyword.modifier + +[ + "in" + "out" + "inout" +] @keyword.modifier + +[ + "while" + "for" +] @keyword.repeat + +[ + "continue" + "break" + "return" +] @keyword.return + +[ + "if" + "else" + "switch" + "case" + "default" +] @keyword.conditional + +[ + "#" + "include" +] @keyword.directive + +(string) @string + +[ + "=" + "+=" + "-=" + "!" + "~" + "+" + "-" + "*" + "/" + "%" + "||" + "&&" + "|" + "^" + "&" + "==" + "!=" + ">" + ">=" + "<=" + "<" + "<<" + ">>" + "++" + "--" +] @operator + +(boolean) @boolean + +(integer) @number + +(float) @number.float + +[ + "." + "," + ";" +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(builtin_type) @type.builtin + +(ident_type) @type.definition + +[ + (shader_type) + (render_mode) + (hint_name) +] @attribute + +(builtin_variable) @constant.builtin + +(builtin_function) @function.builtin + +(group_uniforms_declaration + group_name: (ident) @property + subgroup_name: (ident) @property) + +(struct_declaration + name: (ident) @type) + +(struct_member + name: (ident) @property) + +(function_declaration + name: (ident) @function) + +(parameter + name: (ident) @variable.parameter) + +(member_expr + member: (ident) @property) + +(call_expr + function: [ + (ident) + (builtin_type) + ] @function) + +(call_expr + function: (builtin_type) @function.call) + +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdshader/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdshader/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gdshader/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_config/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_config/folds.scm new file mode 100644 index 00000000..cb376d5b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_config/folds.scm @@ -0,0 +1,2 @@ +((section) @fold + (#trim! @fold)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_config/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_config/highlights.scm new file mode 100644 index 00000000..3423e1b0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_config/highlights.scm @@ -0,0 +1,59 @@ +; Sections +(section_name) @type + +((section_name) @keyword.import + (#eq? @keyword.import "include")) + +((section_header + (section_name) @keyword.import + (subsection_name)) + (#eq? @keyword.import "includeIf")) + +(variable + (name) @property) + +; Operators +"=" @operator + +; Literals +(integer) @number + +[ + (true) + (false) +] @boolean + +(string) @string + +(escape_sequence) @string.escape + +((string) @string.special.path + (#lua-match? @string.special.path "^[.]?[.]?[/]")) + +((string) @string.special.path + (#lua-match? @string.special.path "^[~]")) + +(section_header + [ + "\"" + (subsection_name) + ] @string.special) + +((section_header + (section_name) @_name + (subsection_name) @string.special.url) + (#any-of? @_name "credential" "url")) + +((variable + (name) @_name + value: (string) @string.special.url) + (#eq? @_name "insteadOf")) + +; Punctuation +[ + "[" + "]" +] @punctuation.bracket + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_config/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_config/injections.scm new file mode 100644 index 00000000..7bda6979 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_config/injections.scm @@ -0,0 +1,69 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((variable + (name) @_name + value: (string) @injection.content) + (#any-of? @_name "cmd" "command" "textconv" "sendmailCmd") + (#set! injection.language "bash")) + +(section + (variable + (name) @_name + value: (string) @injection.content) + (#eq? @_name "tool") + (#set! injection.language "bash")) + +(section + (section_header + (section_name) @_pager) + (variable + value: (string) @injection.content) + (#eq? @_pager "pager") + (#set! injection.language "bash")) + +(section + (section_header + (section_name) @_interactive) + (variable + (name) @_name + value: (string) @injection.content) + (#eq? @_interactive "interactive") + (#eq? @_name "diffFilter") + (#set! injection.language "bash")) + +; https://github.com/git-lfs/git-lfs +; git lfs install +(section + (section_header + (section_name) @_filter + (subsection_name) @_lfs) + (variable + (name) @_name + value: (string) @injection.content) + (#eq? @_filter "filter") + (#eq? @_lfs "lfs") + (#any-of? @_name "smudge" "process" "clean") + (#set! injection.language "bash")) + +(section + (section_header + (section_name) @_alias) + (variable + value: (string) @injection.content) + (#eq? @_alias "alias") + (#lua-match? @injection.content "^!") + (#offset! @injection.content 0 1 0 0) + (#set! injection.language "bash")) + +(section + (section_header + (section_name) @_alias) + (variable + value: (string + "\"" + "\"") @injection.content) + (#eq? @_alias "alias") + (#lua-match? @injection.content "^\"!") + (#offset! @injection.content 0 2 0 -1) + (#set! injection.language "bash")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_rebase/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_rebase/highlights.scm new file mode 100644 index 00000000..248366e2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_rebase/highlights.scm @@ -0,0 +1,7 @@ +((command) @keyword + (label)? @constant + (message)? @none @spell) + +(option) @operator + +(comment) @comment diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_rebase/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_rebase/injections.scm new file mode 100644 index 00000000..da262866 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/git_rebase/injections.scm @@ -0,0 +1,5 @@ +((operation + (command) @_command + (message) @injection.content) + (#set! injection.language "bash") + (#any-of? @_command "exec" "x")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitattributes/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitattributes/highlights.scm new file mode 100644 index 00000000..aec7750f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitattributes/highlights.scm @@ -0,0 +1,55 @@ +(dir_sep) @punctuation.delimiter + +(quoted_pattern + "\"" @punctuation.special) + +(range_notation) @string.special + +(range_notation + [ + "[" + "]" + ] @punctuation.bracket) + +(wildcard) @character.special + +(range_negation) @operator + +(character_class) @constant + +(class_range + "-" @operator) + +[ + (ansi_c_escape) + (escaped_char) +] @string.escape + +(attribute + (attr_name) @variable.parameter) + +(attribute + (builtin_attr) @variable.builtin) + +[ + (attr_reset) + (attr_unset) + (attr_set) +] @operator + +(boolean_value) @boolean + +(string_value) @string + +(macro_tag) @keyword.directive + +(macro_def + macro_name: (_) @property) + +; we do not lint syntax errors +; [ +; (pattern_negation) +; (redundant_escape) +; (trailing_slash) +; ] @error +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitattributes/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitattributes/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitattributes/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitattributes/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitattributes/locals.scm new file mode 100644 index 00000000..2471b8bc --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitattributes/locals.scm @@ -0,0 +1,8 @@ +(macro_def + (attr_name) @local.definition.macro) + +(attribute + (attr_name) @local.reference) + +(attribute + (builtin_attr) @local.reference) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitcommit/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitcommit/highlights.scm new file mode 100644 index 00000000..4542413c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitcommit/highlights.scm @@ -0,0 +1,51 @@ +(comment) @comment + +(generated_comment) @comment + +(title) @markup.heading + +; (text) @none +(branch) @markup.link + +(change) @keyword + +(filepath) @string.special.url + +(arrow) @punctuation.delimiter + +(subject) @markup.heading @spell + +(overflow) @comment.warning @spell + +(subject + (subject_prefix) @function @nospell) + +(prefix + (type) @keyword @nospell) + +(prefix + (scope) @variable.parameter @nospell) + +(prefix + [ + "(" + ")" + ":" + ] @punctuation.delimiter) + +(prefix + "!" @punctuation.special) + +(message) @spell + +(trailer + (token) @label) + +; (trailer (value) @none) +(breaking_change + (token) @comment.error) + +(breaking_change + (value) @none @spell) + +(scissor) @comment diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitcommit/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitcommit/injections.scm new file mode 100644 index 00000000..5613d7e8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitcommit/injections.scm @@ -0,0 +1,5 @@ +((diff) @injection.content + (#set! injection.language "diff")) + +((rebase_command) @injection.content + (#set! injection.language "git_rebase")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitignore/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitignore/highlights.scm new file mode 100644 index 00000000..10ae3f30 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gitignore/highlights.scm @@ -0,0 +1,33 @@ +(comment) @comment @spell + +[ + (directory_separator) + (directory_separator_escaped) +] @punctuation.delimiter + +[ + (wildcard_char_single) + (wildcard_chars) + (wildcard_chars_allow_slash) + (bracket_negation) +] @operator + +(negation) @punctuation.special + +[ + (pattern_char_escaped) + (bracket_char_escaped) +] @string.escape + +; bracket expressions +[ + "[" + "]" +] @punctuation.bracket + +(bracket_char) @constant + +(bracket_range + "-" @operator) + +(bracket_char_class) @constant.builtin diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/folds.scm new file mode 100644 index 00000000..b4cd225e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/folds.scm @@ -0,0 +1,7 @@ +; Folds +[ + (case) + (function) + (anonymous_function) + (type_definition) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/highlights.scm new file mode 100644 index 00000000..8754f3ed --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/highlights.scm @@ -0,0 +1,201 @@ +; Keywords +[ + "as" + "let" + "panic" + "todo" + "type" + "use" +] @keyword + +; Function Keywords +"fn" @keyword.function + +; Imports +"import" @keyword.import + +; Conditionals +[ + "case" + "if" +] @keyword.conditional + +; Exceptions +"assert" @keyword.exception + +; Punctuation +[ + "(" + ")" + "<<" + ">>" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + "," + "." + ":" + "->" +] @punctuation.delimiter + +"#" @punctuation.special + +; Operators +[ + "%" + "&&" + "*" + "*." + "+" + "+." + "-" + "-." + ".." + "/" + "/." + "<" + "<." + "<=" + "<=." + "=" + "==" + ">" + ">." + ">=" + ">=." + "|>" + "||" +] @operator + +; Identifiers +(identifier) @variable + +; Comments +(comment) @comment @spell + +[ + (module_comment) + (statement_comment) +] @comment.documentation @spell + +; Unused Identifiers +[ + (discard) + (hole) +] @comment + +; Modules & Imports +(module) @module + +(import + alias: ((identifier) @module)?) + +(remote_type_identifier + module: (identifier) @module) + +(unqualified_import + name: (identifier) @function) + +; Strings +(string) @string + +; Bit Strings +(bit_string_segment) @string.special + +; Numbers +(integer) @number + +(float) @number.float + +; Function Parameter Labels +(function_call + arguments: (arguments + (argument + label: (label) @label))) + +(function_parameter + label: (label)? @label + name: (identifier) @variable.parameter) + +; Records +(record + arguments: (arguments + (argument + label: (label) @variable.member)?)) + +(record_pattern_argument + label: (label) @variable.member) + +(record_update_argument + label: (label) @variable.member) + +(field_access + record: (identifier) @variable + field: (label) @variable.member) + +(data_constructor_argument + (label) @variable.member) + +; Types +[ + (type_identifier) + (type_parameter) + (type_var) +] @type + +((type_identifier) @type.builtin + (#any-of? @type.builtin "Int" "Float" "String" "List")) + +; Type Qualifiers +[ + "const" + "external" + (opacity_modifier) + (visibility_modifier) +] @keyword.modifier + +; Tuples +(tuple_access + index: (integer) @operator) + +; Functions +(function + name: (identifier) @function) + +(function_call + function: (identifier) @function.call) + +(function_call + function: (field_access + field: (label) @function.call)) + +; External Functions +(external_function + name: (identifier) @function) + +(external_function_body + (string) @module + . + (string) @function) + +; Constructors +(constructor_name) @type @constructor + +([ + (type_identifier) + (constructor_name) +] @constant.builtin + (#any-of? @constant.builtin "Ok" "Error")) + +; Booleans +((constructor_name) @boolean + (#any-of? @boolean "True" "False")) + +; Pipe Operator +(binary_expression + operator: "|>" + right: (identifier) @function) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/indents.scm new file mode 100644 index 00000000..c7985450 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/indents.scm @@ -0,0 +1,30 @@ +; Gleam indents similar to Rust and JavaScript +[ + (anonymous_function) + (assert) + (case) + (case_clause) + (constant) + (external_function) + (function) + (import) + (let) + (list) + (constant) + (function) + (type_definition) + (type_alias) + (todo) + (tuple) +] @indent.begin + +[ + ")" + "]" + "}" +] @indent.end @indent.branch + +; Gleam pipelines are not indented, but other binary expression chains are +((binary_expression + operator: _ @_operator) @indent.begin + (#not-eq? @_operator "|>")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/injections.scm new file mode 100644 index 00000000..11d4f5d5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/injections.scm @@ -0,0 +1,7 @@ +; Comments +([ + (module_comment) + (statement_comment) + (comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/locals.scm new file mode 100644 index 00000000..ba0632c0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gleam/locals.scm @@ -0,0 +1,31 @@ +; Let Binding Definition +(let + pattern: (identifier) @local.definition) + +; List Pattern Definitions +(list_pattern + (identifier) @local.definition) + +(list_pattern + assign: (identifier) @local.definition) + +; Tuple Pattern Definition +(tuple_pattern + (identifier) @local.definition) + +; Record Pattern Definition +(record_pattern_argument + pattern: (identifier) @local.definition) + +; Function Parameter Definition +(function_parameter + name: (identifier) @local.definition) + +; References +(identifier) @local.reference + +; Function Body Scope +(function_body) @local.scope + +; Case Scope +(case_clause) @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/glimmer/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/glimmer/folds.scm new file mode 100644 index 00000000..6502455d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/glimmer/folds.scm @@ -0,0 +1,5 @@ +[ + (element_node + (element_node_start)) + (block_statement) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/glimmer/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/glimmer/highlights.scm new file mode 100644 index 00000000..9f11468d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/glimmer/highlights.scm @@ -0,0 +1,117 @@ +; === Tag Names === +; Tags that start with a lower case letter are HTML tags +; We'll also use this highlighting for named blocks (which start with `:`) +((tag_name) @tag + (#lua-match? @tag "^:?[%l]")) + +; Tags that start with a capital letter are Glimmer components +((tag_name) @constructor + (#lua-match? @constructor "^%u")) + +(attribute_name) @attribute + +(string_literal) @string + +(number_literal) @number + +(boolean_literal) @boolean + +(concat_statement) @string + +; === Block Statements === +; Highlight the brackets +(block_statement_start) @tag.delimiter + +(block_statement_end) @tag.delimiter + +; Highlight `if`/`each`/`let` +(block_statement_start + path: (identifier) @keyword.conditional) + +(block_statement_end + path: (identifier) @keyword.conditional) + +((mustache_statement + (identifier) @keyword.conditional) + (#lua-match? @keyword.conditional "else")) + +; == Mustache Statements === +; Highlight the whole statement, to color brackets and separators +(mustache_statement) @tag.delimiter + +; An identifier in a mustache expression is a variable +((mustache_statement + [ + (path_expression + (identifier) @variable) + (identifier) @variable + ]) + (#not-any-of? @variable "yield" "outlet" "this" "else")) + +; As are arguments in a block statement +(block_statement_start + argument: [ + (path_expression + (identifier) @variable) + (identifier) @variable + ]) + +; As is an identifier in a block param +(block_params + (identifier) @variable) + +; As are helper arguments +((helper_invocation + argument: [ + (path_expression + (identifier) @variable) + (identifier) @variable + ]) + (#not-eq? @variable "this")) + +; `this` should be highlighted as a built-in variable +((identifier) @variable.builtin + (#eq? @variable.builtin "this")) + +; If the identifier is just "yield" or "outlet", it's a keyword +((mustache_statement + (identifier) @keyword) + (#any-of? @keyword "yield" "outlet")) + +; Helpers are functions +((helper_invocation + helper: [ + (path_expression + (identifier) @function) + (identifier) @function + ]) + (#not-any-of? @function "if" "yield")) + +((helper_invocation + helper: (identifier) @keyword.conditional) + (#eq? @keyword.conditional "if")) + +((helper_invocation + helper: (identifier) @keyword) + (#eq? @keyword "yield")) + +(hash_pair + key: (identifier) @property) + +(comment_statement) @comment @spell + +(attribute_node + "=" @operator) + +(block_params + "as" @keyword) + +(block_params + "|" @operator) + +[ + "<" + ">" + "" +] @tag.delimiter diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/glimmer/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/glimmer/indents.scm new file mode 100644 index 00000000..c1ef130c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/glimmer/indents.scm @@ -0,0 +1,34 @@ +[ + (element_node + (element_node_start)) + (element_node_void) + (block_statement + (block_statement_start)) + (mustache_statement) +] @indent.begin + +(element_node + (element_node_end + ">" @indent.end)) + +(element_node_void + "/>" @indent.end) + +[ + ">" + "/>" + "" + ">=" + "==" + "!=" + "&&" + "||" +] @operator + +; Variables +(identifier) @variable + +; Functions +(call_expression + function: (identifier) @function.call) + +; Fields +(scope_access + field: (identifier) @variable.member) + +; Literals +(string) @string + +(escape_sequence) @string.escape + +(expansion) @none + +(integer) @number + +(hex) @string.special + +(boolean) @boolean + +; Punctuation +[ + "{" + "}" + "[" + "]" + "(" + ")" +] @punctuation.bracket + +[ + "." + "," +] @punctuation.delimiter + +(expansion + [ + "$" + "${" + "}" + ] @punctuation.special) + +; Comments +(comment) @comment diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gn/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gn/indents.scm new file mode 100644 index 00000000..82f44711 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gn/indents.scm @@ -0,0 +1,12 @@ +[ + (block) + (parenthesized_expression) +] @indent.begin + +[ + "}" + ")" + "]" +] @indent.end @indent.branch + +(comment) @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gn/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gn/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gn/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gn/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gn/locals.scm new file mode 100644 index 00000000..eecb3426 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gn/locals.scm @@ -0,0 +1,6 @@ +[ + (source_file) + (block) +] @local.scope + +(identifier) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gnuplot/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gnuplot/highlights.scm new file mode 100644 index 00000000..884c7263 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gnuplot/highlights.scm @@ -0,0 +1,648 @@ +; highlights.scm +(comment) @comment @spell + +(identifier) @variable + +[ + "-" + "+" + "~" + "!" + "$" + "|" + "**" + "*" + "/" + "%" + "==" + "!=" + "<" + "<=" + ">" + ">=" + "<<" + ">>" + "&" + "^" + "&&" + "||" + "=" + "," + "." +] @operator + +[ + "eq" + "ne" +] @keyword.operator + +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +"sum" @function.builtin + +[ + "for" + "in" + "do" + "while" +] @keyword.repeat + +[ + (c_break) + (c_cd) + (c_clear) + "evaluate" + "fit" + "help" + "load" + "lower" + "print" + (c_replot) + (c_reread) + "reset" + "splot" + "cmd" + "test" + "undefine" + "vfill" +] @keyword + +(c_pause + "pause" @keyword + "mouse" @variable.member + _? @attribute + ("," + _ @attribute)?) + +(c_plot + "plot" @keyword) + +(c_show + "show" @keyword + "plot"? @attribute) + +(c_stats + "stats" @keyword + ("name" + (_))? @variable.member) + +[ + "via" + "inverse" + "sample" +] @keyword.function + +[ + "if" + "else" +] @keyword.conditional + +(plot_element + "axes"? @variable.member) + +(cntrparam + "auto"? @variable.member) + +(colorbox + "origin"? @attribute) + +(contourfill + "auto"? @variable.member) + +(format + _? @attribute + (_) + _? @attribute) + +(key + "auto"? @variable.member) + +(style ; TODO: complete + [ + "arrow" + "boxplot" + ("data" + [ + (_) + "spiderplot" @attribute + ]) + "fs" + "function" + "line" + "circle" + "rectangle" + "ellipse" + "parallelaxis" + ; (spiderplot) ; TODO: complete + "textbox" + ("watchpoint" + "labels" @attribute + (_)?) + ] @variable.member) + +(terminal + "name" @variable.member) + +; TODO: complete terminals in grammar and then simplify its options here +(t_cairolatex + [ + "eps" + "pdf" + "png" + "standalone" + "input" + "blacktext" + "colortext" + "colourtext" + ("header" + (_)) + "mono" + "color" + "background" + "rounded" + "butt" + ]* @attribute) + +; (t_canvas) +; (t_cgm) +; (t_context) +; (t_domterm) +; (t_dumb) +; (t_dxf) +; (t_emf) +; (t_epscairo) +; (t_epslatex) +; (t_fig) +; (t_gif) +; (t_hpgl) +; (t_jpeg) +; (t_lua) +; (t_pc15) +; (t_pdfcairo) +; (t_png) +; (t_pngcairo) +; (t_postscript) +; (t_pslatex) +; (t_pstricks) +; (t_qt) +; (t_sixelgd) +; (t_svg [(font_spec)]* @attribute) +; (t_tek4xxx) +; (t_texdraw) +; (t_tikz) +; (t_tkcanvas) +(plot_style + [ + "lines" + "points" + "lp" + "financebars" + "dots" + "impulses" + "labels" + "surface" + "steps" + "fsteps" + "histeps" + "arrows" + "vectors" + "sectors" + "contourfill" + "errorbar" + "errorlines" + "parallelaxes" + "boxes" + "boxerrorbars" + "boxxyerror" + "isosurface" + "boxplot" + "candlesticks" + "circles" + "zerrorfill" + "ellipses" + "filledcurves" + "fillsteps" + "histograms" + "image" + "spiderplot" + "pm3d" + "rgbalpha" + "rgbimage" + "polygons" + "table" + "mask" + ] @attribute) + +[ + "tc" + "fc" + "fs" + "lc" + "ls" + "lw" + "lt" + "pt" + "ps" + "pi" + "pn" + "dt" + "as" + "start" + "cycles" + "saturation" + "interval" + "format" + "keywidth" + "samplen" + "columns" + "title" + "notitle" + "every" + "index" + "using" + "with" + "frac" + "cb" + "arg" + "prefix" + "output" + "primary" + "specular" + "spec2" + "firstlinetype" + "width" + "height" + "expand" + "array" + "dx" + "dy" + "dz" + "filetype" + "center" + "record" +] @variable.member + +; Workaround because formatter cannot handle 300 list nodes +[ + (angles) + (clip) + (colorsequence) + (contour) + (encoding) + (mapping) + (xdata) + (theta) + "wall" + "on" + "off" + "opaque" + "inside" + "outside" + "margin" + "cen" + "lef" + "rig" + "top" + "bot" + "lr" + "a" + "maxcols" + "maxrows" + "autojustify" + "overlap" + "spread" + "wrap" + "swarm" + "range" + "label" + "mixed" + "triangles" + "insidecolor" + "noinsidecolor" + "cycle" + "tics" + "ztics" + "cbtics" + "user" + "front" + "back" + "bdefault" + "time" + "palette" + "terminal" + "onecolor" + "invert" + "reverse" + "writeback" + "extend" + "restore" + "linear" + "cubicspline" + "bspline" + "points" + "order" + "levels" + "sorted" + "autofreq" + "add" + "inout" + "axis" + "mirror" + "type" + "rowsfirst" + "columnsfirst" + "downwards" + "upwards" + "prevnext" + "gray" + "color" + "gamma" + "defined" + "cubehelix" + "model" + "maxcolors" + "file" + "colormap" + "rgbformulae" + "viridis" + "positive" + "negative" + "nops_allcF" + "ps_allcF" + "quiet" + "full" + "trip" + "numbers" + "small" + "large" + "fullwidth" + "append" + "bind" + "errors" + "session" + "behind" + "polar" + "layerdefault" + "locale" + "axes" + "fix" + "keepfix" + "noextend" + "head" + "fixed" + "filled" + "nofilled" + "absolute" + "at" + "relative" + "enhanced" + "border" + "noborder" + "rgbcolor" + "empty" + "black" + "bgnd" + "nodraw" + "size" + "new" + "first" + "second" + "screen" + "graph" + "character" + "trianglepattern" + "undefined" + "noundefined" + "altdiagonal" + "bentover" + "vertical" + "horizontal" + "square" + "ratio" + "noratio" + "solid" + "transparent" + "pattern" + "from" + "to_rto" + "length" + "angle" + "columnheaders" + "fortran" + "nofpe_trap" + "missing" + "separator" + "commentschars" + "log" + "rangelimited" + "offset" + "nooffset" + "scale" + "font" + "point" + "nopoint" + "boxed" + "noboxed" + "hypertext" + "defaults" + "keyentry" + "splines" + "qnorm" + "gauss" + "cauchy" + "exp" + "box" + "hann" + "implicit" + "explicit" + "rotate" + "by" + "parallel" + "norotate" + "map" + "projection" + "equal" + "azimuth" + "nohidden3d" + "nocontours" + "nosurface" + "colornames" + "functions" + "variables" + "version" + "nologfile" + "logfile" + "fit_out" + "errorvariables" + "covariancevariables" + "errorscaling" + "prescale" + "maxiter" + "limit" + "limit_abs" + "start-lambda" + "lambda-factor" + "script" + "clip" + "fontscale" + "lighting" + "depthorder" + "interpolate" + "corners2color" + "flush" + "scanorder" + "hidden3d" + "clipcb" + "layout" + "margins" + "spacing" + "smooth" + "binary" + "skip" + "bins" + "binrange" + "binwidth" + "binvalue" + "mask" + "convexhull" + "concavehull" + "volatile" + "zsort" + "nonuniform" + "sparse" + "matrix" +] @attribute + +[ + "x1" + "x2" + "y1" + "y2" + "y" + "r" + "z" + "xy" + "xz" + "yz" + "xyz" + "x1y1" + "x2y2" + "x1y2" + "x2y1" + "columnheader" + "seconds" + "minutes" + "hours" + "days" + "weeks" + "months" + "years" + "cm" + "in" + "discrete" + "incremental" + "default" + "long" + "nogrid" + "unique" + "frequency" + "fnormal" + "cumulative" + "cnormal" + "csplines" + "acsplines" + "mcsplines" + "path" + "bezier" + "sbezier" + "unwrap" + "kdensity" + "closed" + "between" + "above" + "below" + "variable" + "pixels" + "RGB" + "CMY" + "HSV" + "base" + "begin" + "center" + "end" + "ftriangles" + "clip1in" + "clip4in" + "c2c" + "retrace" + "whitespace" + "tab" + "comma" + "push" + "pop" + "flipx" + "flipy" + "flipz" +] @variable.member + +(colorspec + "palette" @attribute) + +(datafile_modifiers + "origin"? @variable.member) + +((datafile_modifiers + filetype: (identifier) @variable.member) + (#any-of? @variable.member + "avs" "bin" "edf" "ehf" "gif" "gpbin" "jpeg" "jpg" "png" "raw" "rgb" "auto")) + +(macro) @function.macro + +(datablock) @function.macro + +(function + name: (identifier) @function) + +((function + name: (identifier) @function.builtin) + (#any-of? @function.builtin + "abs" "acos" "acosh" "airy" "arg" "asin" "asinh" "atan" "atan2" "atanh" "besj0" "besj1" "besjn" + "besy0" "besy1" "besyn" "besi0" "besi1" "besin" "cbrt" "ceil" "conj" "cos" "cosh" "EllipticK" + "EllipticE" "EllipticPi" "erf" "erfc" "exp" "expint" "floor" "gamma" "ibeta" "inverf" "igamma" + "imag" "int" "invnorm" "invibeta" "invigamma" "LambertW" "lambertw" "lgamma" "lnGamma" "log" + "log10" "norm" "rand" "real" "round" "sgn" "sin" "sinh" "sqrt" "SynchrotronF" "tan" "tanh" + "uigamma" "voigt" "zeta" "cerf" "cdawson" "faddeva" "erfi" "FresnelC" "FresnelS" "VP" "VP_fwhm" + "Ai" "Bi" "BesselH1" "BesselH2" "BesselJ" "BesselY" "BesselI" "BesselK" "gprintf" "sprintf" + "strlen" "strstrt" "substr" "strptime" "srtftime" "system" "trim" "word" "words" "time" + "timecolumn" "tm_hour" "tm_mday" "tm_min" "tm_mon" "tm_sec" "tm_wday" "tm_week" "tm_yday" + "tm_year" "weekday_iso" "weekday_cdc" "column" "columnhead" "exists" "hsv2rgb" "index" "palette" + "rgbcolor" "stringcolumn" "valid" "value" "voxel")) + +((identifier) @variable.builtin + (#match? @variable.builtin + "^\\w+_(records|headers|outofrange|invalid|blank|blocks|columns|column_header|index_(min|max)(_x|_y)?|(min|max)(_x|_y)?|mean(_err)?(_x|_y)?|stddev(_err)?(_x|_y)?)$")) + +((identifier) @variable.builtin + (#match? @variable.builtin + "^\\w+_(sdd(_x|_y)?|(lo|up)_quartile(_x|_y)?|median(_x|_y)?|sum(sq)?(_x|_y)?|skewness(_err)?(_x|_y)?)$")) + +((identifier) @variable.builtin + (#match? @variable.builtin + "^\\w+_(kurtosis(_err)?(_x|_y)?|adev(_x|_y)?|correlation|slope(_err)?|intercept(_err)?|sumxy|pos(_min|_max)_y|size(_x|_y))$")) + +((identifier) @variable.builtin + (#match? @variable.builtin "^((GPVAL|MOUSE|FIT)_\\w+|GNUTERM|NaN|VoxelDistance|GridDistance|pi)$")) + +(array_def + "array" @keyword.function) + +(array + (identifier) @function) + +(number) @number + +(string_literal) @string diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gnuplot/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gnuplot/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gnuplot/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/folds.scm new file mode 100644 index 00000000..44b452de --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/folds.scm @@ -0,0 +1,19 @@ +[ + (const_declaration) + (expression_switch_statement) + (expression_case) + (default_case) + (type_switch_statement) + (type_case) + (for_statement) + (func_literal) + (function_declaration) + (if_statement) + (import_declaration) + (method_declaration) + (type_declaration) + (var_declaration) + (composite_literal) + (literal_element) + (block) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/highlights.scm new file mode 100644 index 00000000..62497b0c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/highlights.scm @@ -0,0 +1,239 @@ +; Forked from tree-sitter-go +; Copyright (c) 2014 Max Brunsfeld (The MIT License) +; +; Identifiers +(type_identifier) @type + +(type_spec + name: (type_identifier) @type.definition) + +(field_identifier) @property + +(identifier) @variable + +(package_identifier) @module + +(parameter_declaration + (identifier) @variable.parameter) + +(variadic_parameter_declaration + (identifier) @variable.parameter) + +(label_name) @label + +(const_spec + name: (identifier) @constant) + +; Function calls +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (selector_expression + field: (field_identifier) @function.method.call)) + +; Function definitions +(function_declaration + name: (identifier) @function) + +(method_declaration + name: (field_identifier) @function.method) + +(method_elem + name: (field_identifier) @function.method) + +; Constructors +((call_expression + (identifier) @constructor) + (#lua-match? @constructor "^[nN]ew.+$")) + +((call_expression + (identifier) @constructor) + (#lua-match? @constructor "^[mM]ake.+$")) + +; Operators +[ + "--" + "-" + "-=" + ":=" + "!" + "!=" + "..." + "*" + "*" + "*=" + "/" + "/=" + "&" + "&&" + "&=" + "&^" + "&^=" + "%" + "%=" + "^" + "^=" + "+" + "++" + "+=" + "<-" + "<" + "<<" + "<<=" + "<=" + "=" + "==" + ">" + ">=" + ">>" + ">>=" + "|" + "|=" + "||" + "~" +] @operator + +; Keywords +[ + "break" + "const" + "continue" + "default" + "defer" + "goto" + "range" + "select" + "var" + "fallthrough" +] @keyword + +[ + "type" + "struct" + "interface" +] @keyword.type + +"func" @keyword.function + +"return" @keyword.return + +"go" @keyword.coroutine + +"for" @keyword.repeat + +[ + "import" + "package" +] @keyword.import + +[ + "else" + "case" + "switch" + "if" +] @keyword.conditional + +; Builtin types +[ + "chan" + "map" +] @type.builtin + +((type_identifier) @type.builtin + (#any-of? @type.builtin + "any" "bool" "byte" "comparable" "complex128" "complex64" "error" "float32" "float64" "int" + "int16" "int32" "int64" "int8" "rune" "string" "uint" "uint16" "uint32" "uint64" "uint8" + "uintptr")) + +; Builtin functions +((identifier) @function.builtin + (#any-of? @function.builtin + "append" "cap" "clear" "close" "complex" "copy" "delete" "imag" "len" "make" "max" "min" "new" + "panic" "print" "println" "real" "recover")) + +; Delimiters +"." @punctuation.delimiter + +"," @punctuation.delimiter + +":" @punctuation.delimiter + +";" @punctuation.delimiter + +"(" @punctuation.bracket + +")" @punctuation.bracket + +"{" @punctuation.bracket + +"}" @punctuation.bracket + +"[" @punctuation.bracket + +"]" @punctuation.bracket + +; Literals +(interpreted_string_literal) @string + +(raw_string_literal) @string + +(rune_literal) @string + +(escape_sequence) @string.escape + +(int_literal) @number + +(float_literal) @number.float + +(imaginary_literal) @number + +[ + (true) + (false) +] @boolean + +[ + (nil) + (iota) +] @constant.builtin + +(keyed_element + . + (literal_element + (identifier) @variable.member)) + +(field_declaration + name: (field_identifier) @variable.member) + +; Comments +(comment) @comment @spell + +; Doc Comments +(source_file + . + (comment)+ @comment.documentation) + +(source_file + (comment)+ @comment.documentation + . + (const_declaration)) + +(source_file + (comment)+ @comment.documentation + . + (function_declaration)) + +(source_file + (comment)+ @comment.documentation + . + (type_declaration)) + +(source_file + (comment)+ @comment.documentation + . + (var_declaration)) + +; Spell +((interpreted_string_literal) @spell + (#not-has-parent? @spell import_spec)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/indents.scm new file mode 100644 index 00000000..b26811c8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/indents.scm @@ -0,0 +1,37 @@ +[ + (import_declaration) + (const_declaration) + (var_declaration) + (type_declaration) + (func_literal) + (literal_value) + (expression_case) + (communication_case) + (type_case) + (default_case) + (block) + (call_expression) + (parameter_list) + (struct_type) +] @indent.begin + +"}" @indent.branch + +(const_declaration + ")" @indent.branch) + +(import_spec_list + ")" @indent.branch) + +(var_declaration + ")" @indent.branch) + +[ + "}" + ")" +] @indent.end + +(parameter_list + ")" @indent.branch) + +(comment) @indent.ignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/injections.scm new file mode 100644 index 00000000..38ba3e11 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/injections.scm @@ -0,0 +1,39 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +(call_expression + (selector_expression) @_function + (#any-of? @_function + "regexp.Match" "regexp.MatchReader" "regexp.MatchString" "regexp.Compile" "regexp.CompilePOSIX" + "regexp.MustCompile" "regexp.MustCompilePOSIX") + (argument_list + . + [ + (raw_string_literal) + (interpreted_string_literal) + ] @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "regex"))) + +((comment) @injection.content + (#match? @injection.content "/\\*!([a-zA-Z]+:)?re2c") + (#set! injection.language "re2c")) + +((call_expression + function: (selector_expression + field: (field_identifier) @_method) + arguments: (argument_list + . + (interpreted_string_literal) @injection.content)) + (#any-of? @_method "Printf" "Sprintf" "Fatalf" "Scanf" "Errorf" "Skipf" "Logf") + (#set! injection.language "printf")) + +((call_expression + function: (selector_expression + field: (field_identifier) @_method) + arguments: (argument_list + (_) + . + (interpreted_string_literal) @injection.content)) + (#any-of? @_method "Fprintf" "Fscanf" "Appendf" "Sscanf") + (#set! injection.language "printf")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/locals.scm new file mode 100644 index 00000000..608c4582 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/go/locals.scm @@ -0,0 +1,88 @@ +((function_declaration + name: (identifier) @local.definition.function) ; @function + ) + +((method_declaration + name: (field_identifier) @local.definition.method) ; @function.method + ) + +(short_var_declaration + left: (expression_list + (identifier) @local.definition.var)) + +(var_spec + name: (identifier) @local.definition.var) + +(parameter_declaration + (identifier) @local.definition.var) + +(variadic_parameter_declaration + (identifier) @local.definition.var) + +(for_statement + (range_clause + left: (expression_list + (identifier) @local.definition.var))) + +(const_declaration + (const_spec + name: (identifier) @local.definition.var)) + +(type_declaration + (type_spec + name: (type_identifier) @local.definition.type)) + +; reference +(identifier) @local.reference + +(type_identifier) @local.reference + +(field_identifier) @local.reference + +((package_identifier) @local.reference + (#set! reference.kind "namespace")) + +(package_clause + (package_identifier) @local.definition.namespace) + +(import_spec_list + (import_spec + name: (package_identifier) @local.definition.namespace)) + +; Call references +((call_expression + function: (identifier) @local.reference) + (#set! reference.kind "call")) + +((call_expression + function: (selector_expression + field: (field_identifier) @local.reference)) + (#set! reference.kind "call")) + +((call_expression + function: (parenthesized_expression + (identifier) @local.reference)) + (#set! reference.kind "call")) + +((call_expression + function: (parenthesized_expression + (selector_expression + field: (field_identifier) @local.reference))) + (#set! reference.kind "call")) + +; Scopes +(func_literal) @local.scope + +(source_file) @local.scope + +(function_declaration) @local.scope + +(if_statement) @local.scope + +(block) @local.scope + +(expression_switch_statement) @local.scope + +(for_statement) @local.scope + +(method_declaration) @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/folds.scm new file mode 100644 index 00000000..911798f5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/folds.scm @@ -0,0 +1 @@ +(section) @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/highlights.scm new file mode 100644 index 00000000..15cbadd1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/highlights.scm @@ -0,0 +1,49 @@ +(identifier) @variable + +(section + (identifier) @tag) + +(section + [ + "[" + "]" + ] @tag.delimiter) + +(attribute + (identifier) @tag.attribute) + +(property + (path) @property) + +(constructor + (identifier) @constructor) + +(string) @string + +(integer) @number + +(float) @number.float + +[ + (true) + (false) +] @boolean + +(null) @constant.builtin + +(array + [ + "[" + "]" + ] @punctuation.bracket) + +[ + "(" + ")" + "{" + "}" +] @punctuation.bracket + +"=" @operator + +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/locals.scm new file mode 100644 index 00000000..b946a5ee --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/godot_resource/locals.scm @@ -0,0 +1 @@ +(section) @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gomod/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gomod/highlights.scm new file mode 100644 index 00000000..29cbe657 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gomod/highlights.scm @@ -0,0 +1,20 @@ +[ + "require" + "replace" + "go" + "toolchain" + "exclude" + "retract" + "module" +] @keyword + +"=>" @operator + +(comment) @comment @spell + +(module_path) @string.special.url + +[ + (version) + (go_version) +] @string diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gomod/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gomod/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gomod/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gosum/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gosum/highlights.scm new file mode 100644 index 00000000..f20c5f17 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gosum/highlights.scm @@ -0,0 +1,32 @@ +[ + "alpha" + "beta" + "dev" + "pre" + "rc" + "+incompatible" +] @keyword + +(module_path) @string.special.url + +(module_version) @string.special + +(hash_version) @attribute + +(hash) @string.special.symbol + +[ + (number) + (number_with_decimal) + (hex_number) +] @number + +(checksum + "go.mod" @string) + +[ + ":" + "." + "-" + "/" +] @punctuation.delimiter diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gotmpl/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gotmpl/highlights.scm new file mode 100644 index 00000000..ba69dc07 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gotmpl/highlights.scm @@ -0,0 +1,107 @@ +; Identifiers +[ + (field) + (field_identifier) +] @variable.member + +(variable) @variable + +; Function calls +(function_call + function: (identifier) @function) + +(method_call + method: (selector_expression + field: (field_identifier) @function)) + +; Builtin functions +(function_call + function: (identifier) @function.builtin + (#any-of? @function.builtin + "and" "call" "html" "index" "slice" "js" "len" "not" "or" "print" "printf" "println" "urlquery" + "eq" "ne" "lt" "ge" "gt" "ge")) + +; Operators +[ + "|" + ":=" +] @operator + +; Delimiters +[ + "." + "," +] @punctuation.delimiter + +[ + "{{" + "}}" + "{{-" + "-}}" + ")" + "(" +] @punctuation.bracket + +; Actions +(if_action + [ + "if" + "else" + "else if" + "end" + ] @keyword.conditional) + +(range_action + [ + "range" + "else" + "end" + ] @keyword.repeat) + +(template_action + "template" @function.builtin) + +(block_action + [ + "block" + "end" + ] @keyword.directive) + +(define_action + [ + "define" + "end" + ] @keyword.directive.define) + +(with_action + [ + "with" + "else" + "end" + ] @keyword.conditional) + +; Literals +[ + (interpreted_string_literal) + (raw_string_literal) +] @string + +(rune_literal) @string.special.symbol + +(escape_sequence) @string.escape + +[ + (int_literal) + (imaginary_literal) +] @number + +(float_literal) @number.float + +[ + (true) + (false) +] @boolean + +(nil) @constant.builtin + +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gotmpl/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gotmpl/injections.scm new file mode 100644 index 00000000..3cfc2636 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gotmpl/injections.scm @@ -0,0 +1,31 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +; {{"put" | printf "%s%s" "out" | printf "%q"}} +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#eq? @_function "printf") + (#set! injection.language "printf")) + +; {{ js "var a = 1 + 1" }} +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#eq? @_function "js") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "javascript")) + +; {{ html "

hello

" }} +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#eq? @_function "html") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "html")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gowork/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gowork/highlights.scm new file mode 100644 index 00000000..bca9a5f8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gowork/highlights.scm @@ -0,0 +1,14 @@ +[ + "replace" + "go" + "use" +] @keyword + +"=>" @operator + +(comment) @comment @spell + +[ + (version) + (go_version) +] @string diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gowork/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gowork/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gowork/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gpg/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gpg/highlights.scm new file mode 100644 index 00000000..f0283442 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gpg/highlights.scm @@ -0,0 +1,54 @@ +(option + . + _ @keyword) + +(option + ("no-" @variable.parameter)? + (name) @variable.parameter) + +(string + (content) @string) + +[ + (value) + "clear" +] @string.special + +(url) @string.special.url + +(key) @constant + +[ + (number) + (expire_time) + (iso_time) +] @number + +(format) @character.special + +"sensitive:" @keyword.modifier + +(filter_name) @variable.parameter + +(filter_scope) @module + +(filter_property) @property + +(filter_value) @string + +[ + (filter_op0) + (filter_op1) + (filter_lc) + "=" +] @operator + +"!" @punctuation.special + +[ + "\"" + "'" + "," +] @punctuation.delimiter + +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gpg/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gpg/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gpg/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/graphql/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/graphql/highlights.scm new file mode 100644 index 00000000..c1ee501c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/graphql/highlights.scm @@ -0,0 +1,163 @@ +; Types +;------ +(scalar_type_definition + (name) @type) + +(object_type_definition + (name) @type) + +(interface_type_definition + (name) @type) + +(union_type_definition + (name) @type) + +(enum_type_definition + (name) @type) + +(input_object_type_definition + (name) @type) + +(scalar_type_extension + (name) @type) + +(object_type_extension + (name) @type) + +(interface_type_extension + (name) @type) + +(union_type_extension + (name) @type) + +(enum_type_extension + (name) @type) + +(input_object_type_extension + (name) @type) + +(named_type + (name) @type) + +; Directives +;----------- +(directive_definition + "@" @attribute + (name) @attribute) + +(directive) @attribute + +; Properties +;----------- +(field + (name) @property) + +(field + (alias + (name) @property)) + +(field_definition + (name) @property) + +(object_value + (object_field + (name) @property)) + +(enum_value + (name) @property) + +; Variable Definitions and Arguments +;----------------------------------- +(operation_definition + (name) @variable) + +(fragment_name + (name) @variable) + +(input_fields_definition + (input_value_definition + (name) @variable.parameter)) + +(argument + (name) @variable.parameter) + +(arguments_definition + (input_value_definition + (name) @variable.parameter)) + +(variable_definition + (variable) @variable.parameter) + +(argument + (value + (variable) @variable)) + +; Constants +;---------- +(string_value) @string + +(int_value) @number + +(float_value) @number.float + +(boolean_value) @boolean + +; Literals +;--------- +(description + (string_value) @string.documentation @spell) + +(comment) @comment @spell + +(directive_location + (executable_directive_location) @type.builtin) + +(directive_location + (type_system_directive_location) @type.builtin) + +; Keywords +;---------- +[ + "query" + "mutation" + "subscription" + "fragment" + "scalar" + "input" + "extend" + "directive" + "schema" + "on" + "repeatable" + "implements" +] @keyword + +[ + "enum" + "union" + "type" + "interface" +] @keyword.type + +; Punctuation +;------------ +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +"=" @operator + +"|" @punctuation.delimiter + +"&" @punctuation.delimiter + +":" @punctuation.delimiter + +"..." @punctuation.special + +"!" @punctuation.special diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/graphql/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/graphql/indents.scm new file mode 100644 index 00000000..fcffeb82 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/graphql/indents.scm @@ -0,0 +1,11 @@ +[ + (definition) + (selection) +] @indent.begin + +[ + "{" + "}" +] @indent.branch + +"}" @indent.end diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/graphql/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/graphql/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/graphql/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/folds.scm new file mode 100644 index 00000000..354861a6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/folds.scm @@ -0,0 +1,6 @@ +[ + (argument_list) + (closure) + (list) + (map) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/highlights.scm new file mode 100644 index 00000000..de62bbb4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/highlights.scm @@ -0,0 +1,267 @@ +[ + "!instanceof" + "assert" + "extends" + "instanceof" + "package" +] @keyword + +"class" @keyword.type + +[ + "!in" + "as" + "in" +] @keyword.operator + +[ + "case" + "default" + "else" + "if" + "switch" +] @keyword.conditional + +[ + "catch" + "finally" + "try" +] @keyword.exception + +"def" @keyword.function + +"import" @keyword.import + +[ + "for" + "while" + (break) + (continue) +] @keyword.repeat + +"return" @keyword.return + +[ + "true" + "false" +] @boolean + +(null) @constant.builtin + +"this" @variable.builtin + +[ + "int" + "char" + "short" + "long" + "boolean" + "float" + "double" + "void" +] @type.builtin + +[ + "final" + "private" + "protected" + "public" + "static" + "synchronized" +] @keyword.modifier + +(comment) @comment @spell + +(shebang) @keyword.directive + +(string) @string + +(string + (escape_sequence) @string.escape) + +(string + (interpolation + "$" @punctuation.special)) + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + ":" + "," + "." +] @punctuation.delimiter + +(number_literal) @number + +(identifier) @variable + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z_]+")) + +[ + "%" + "*" + "/" + "+" + "-" + "<<" + ">>" + ">>>" + ".." + "..<" + "<..<" + "<.." + "<" + "<=" + ">" + ">=" + "==" + "!=" + "<=>" + "===" + "!==" + "=~" + "==~" + "&" + "^" + "|" + "&&" + "||" + "?:" + "+" + "*" + ".&" + ".@" + "?." + "*." + "*" + "*:" + "++" + "--" + "!" +] @operator + +(string + "/" @string) + +(ternary_op + ([ + "?" + ":" + ]) @keyword.conditional.ternary) + +(map + (map_item + key: (identifier) @variable.parameter)) + +(parameter + type: (identifier) @type + name: (identifier) @variable.parameter) + +(generic_param + name: (identifier) @variable.parameter) + +(declaration + type: (identifier) @type) + +(function_definition + type: (identifier) @type) + +(function_declaration + type: (identifier) @type) + +(class_definition + name: (identifier) @type) + +(class_definition + superclass: (identifier) @type) + +(generic_param + superclass: (identifier) @type) + +(type_with_generics + (identifier) @type) + +(type_with_generics + (generics + (identifier) @type)) + +(generics + [ + "<" + ">" + ] @punctuation.bracket) + +(generic_parameters + [ + "<" + ">" + ] @punctuation.bracket) + +; TODO: Class literals with PascalCase +(declaration + "=" @operator) + +(assignment + "=" @operator) + +(function_call + function: (identifier) @function) + +(function_call + function: (dotted_identifier + (identifier) @function .)) + +(function_call + (argument_list + (map_item + key: (identifier) @variable.parameter))) + +(juxt_function_call + function: (identifier) @function) + +(juxt_function_call + function: (dotted_identifier + (identifier) @function .)) + +(juxt_function_call + (argument_list + (map_item + key: (identifier) @variable.parameter))) + +(function_definition + function: (identifier) @function) + +(function_declaration + function: (identifier) @function) + +(annotation) @function.macro + +(annotation + (identifier) @function.macro) + +"@interface" @function.macro + +(groovy_doc) @comment.documentation @spell + +(groovy_doc + [ + (groovy_doc_param) + (groovy_doc_throws) + (groovy_doc_tag) + ] @string.special @nospell) + +(groovy_doc + (groovy_doc_param + (identifier) @variable.parameter) @nospell) + +(groovy_doc + (groovy_doc_throws + (identifier) @type @nospell)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/indents.scm new file mode 100644 index 00000000..888d5010 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/indents.scm @@ -0,0 +1,35 @@ +[ + (closure) + (map) + (list) + (argument_list) + (parameter_list) + (for_parameters) +] @indent.begin + +; (function_definition "(" @indent.begin) +(closure + "}" @indent.end) + +(argument_list + ")" @indent.end) + +(for_parameters + ")" @indent.end) + +((for_loop + body: (_) @_body) @indent.begin + (#not-has-type? @_body closure)) + +; TODO: while, try +(list + "]" @indent.end) + +(map + "]" @indent.end) + +[ + "}" + ")" + "]" +] @indent.branch diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/injections.scm new file mode 100644 index 00000000..1c04c65f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/injections.scm @@ -0,0 +1,5 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((groovy_doc) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/locals.scm new file mode 100644 index 00000000..23cb5f0c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/groovy/locals.scm @@ -0,0 +1,6 @@ +(function_definition) @local.scope + +(parameter + name: (identifier) @local.definition.parameter) + +(identifier) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gstlaunch/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gstlaunch/highlights.scm new file mode 100644 index 00000000..90729f87 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/gstlaunch/highlights.scm @@ -0,0 +1,35 @@ +[ + "!" + "=" +] @operator + +[ + "," + "." + ";" + "/" +] @punctuation.delimiter + +[ + "(" + ")" +] @punctuation.bracket + +(property + key: (identifier) @variable.member) + +(value) @string + +(string_literal) @string + +(cap + . + (identifier) @string + . + (identifier) @string) + +(simple_element + type: (_) @type) + +(bin + type: (_) @type) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hack/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hack/highlights.scm new file mode 100644 index 00000000..bb9d2a55 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hack/highlights.scm @@ -0,0 +1,366 @@ +(variable) @variable + +(identifier) @variable + +((variable) @variable.builtin + (#eq? @variable.builtin "$this")) + +(braced_expression) @none + +(scoped_identifier + (qualified_identifier + (identifier) @type)) + +[ + (comment) + (heredoc) +] @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$")) + +"function" @keyword.function + +[ + "implements" + "using" + "attribute" + "const" + "extends" + "insteadof" +] @keyword + +[ + "class" + "type" + "interface" + "namespace" +] @keyword.type + +[ + "async" + "await" +] @keyword.coroutine + +[ + "use" + "include" + "include_once" + "require" + "require_once" +] @keyword.import + +[ + "new" + "print" + "echo" + "newtype" + "clone" + "as" +] @keyword.operator + +"return" @keyword.return + +[ + (abstract_modifier) + (final_modifier) + (static_modifier) + (visibility_modifier) + (xhp_modifier) +] @keyword.modifier + +[ + "shape" + "tuple" + (array_type) + "bool" + "float" + "int" + "string" + "arraykey" + "void" + "nonnull" + "mixed" + "dynamic" + "noreturn" +] @type.builtin + +(null) @constant.builtin + +[ + (true) + (false) +] @boolean + +(type_specifier) @type + +(new_expression + (_) @type) + +(alias_declaration + "newtype" + . + (_) @type) + +(alias_declaration + "type" + . + (_) @type) + +(class_declaration + name: (identifier) @type) + +(type_parameter + name: (identifier) @type) + +(collection + (qualified_identifier + (identifier) @type .)) + +[ + "@required" + "@lateinit" + (attribute_modifier) +] @attribute + +[ + "=" + "??=" + ".=" + "|=" + "^=" + "&=" + "<<=" + ">>=" + "+=" + "-=" + "*=" + "/=" + "%=" + "**=" + "==>" + "|>" + "??" + "||" + "&&" + "|" + "^" + "&" + "==" + "!=" + "===" + "!==" + "<" + ">" + "<=" + ">=" + "<=>" + "<<" + ">>" + "->" + "+" + "-" + "." + "*" + "/" + "%" + "**" + "++" + "--" + "!" + "?:" + "=" + "??=" + ".=" + "|=" + "^=" + "&=" + "<<=" + ">>=" + "+=" + "-=" + "*=" + "/=" + "%=" + "**=" + "=>" + ; type modifiers + "@" + "?" + "~" +] @operator + +(integer) @number + +(float) @number.float + +(parameter + (variable) @variable.parameter) + +(call_expression + function: (qualified_identifier + (identifier) @function.call .)) + +(call_expression + function: (scoped_identifier + (identifier) @function.call .)) + +(call_expression + function: (selection_expression + (qualified_identifier + (identifier) @function.method.call .))) + +(qualified_identifier + (_) @module + . + (_)) + +(use_statement + (qualified_identifier + (_) @module .) + (use_clause)) + +(use_statement + (use_type + "namespace") + (use_clause + (qualified_identifier + (identifier) @module .) + alias: (identifier)? @module)) + +(use_statement + (use_type + "const") + (use_clause + (qualified_identifier + (identifier) @constant .) + alias: (identifier)? @constant)) + +(use_statement + (use_type + "function") + (use_clause + (qualified_identifier + (identifier) @function .) + alias: (identifier)? @function)) + +(use_statement + (use_type + "type") + (use_clause + (qualified_identifier + (identifier) @type .) + alias: (identifier)? @type)) + +(use_clause + (use_type + "namespace") + (qualified_identifier + (_) @module .) + alias: (identifier)? @module) + +(use_clause + (use_type + "function") + (qualified_identifier + (_) @function .) + alias: (identifier)? @function) + +(use_clause + (use_type + "const") + (qualified_identifier + (_) @constant .) + alias: (identifier)? @constant) + +(use_clause + (use_type + "type") + (qualified_identifier + (_) @type .) + alias: (identifier)? @type) + +(function_declaration + name: (identifier) @function) + +(method_declaration + name: (identifier) @function.method) + +(type_arguments + [ + "<" + ">" + ] @punctuation.bracket) + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "<<" + ">>" +] @punctuation.bracket + +(xhp_open + [ + "<" + ">" + ] @tag.delimiter) + +(xhp_close + [ + "" + ] @tag.delimiter) + +[ + "." + ";" + "::" + ":" + "," +] @punctuation.delimiter + +(qualified_identifier + "\\" @punctuation.delimiter) + +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +[ + "if" + "else" + "elseif" + "switch" + "case" +] @keyword.conditional + +[ + "try" + "catch" + "finally" +] @keyword.exception + +[ + "for" + "while" + "foreach" + "do" + "continue" + "break" +] @keyword.repeat + +[ + (string) + (xhp_string) +] @string + +[ + (xhp_open) + (xhp_close) +] @tag diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/folds.scm new file mode 100644 index 00000000..58b10bfd --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/folds.scm @@ -0,0 +1,18 @@ +[ + (imports) + (function_declaration) + (enum_type) + (struct_type) + (tuple_type) + (union_type) + (block) + (if_statement) + (for_statement) + (call_expression) + (switch_expression) + (match_expression) + (case) + (array_literal) + (struct_literal) + (tuple_literal) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/highlights.scm new file mode 100644 index 00000000..68a3e188 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/highlights.scm @@ -0,0 +1,272 @@ +; Variables +(identifier) @variable + +; Types +(type) @type + +(scoped_type_identifier + (identifier) + . + (identifier) @type) + +(struct_literal + . + (identifier) @type) + +(builtin_type) @type.builtin + +; Constants +((identifier) @constant + (#lua-match? @constant "^[A-Z_]+$")) + +; Includes +[ + "use" + "export" +] @keyword.import + +(use_statement + (scoped_type_identifier + (identifier) @module)) + +(use_statement + (identifier) @module + "{") + +(use_statement + . + (identifier) @module .) + +((scoped_type_identifier + path: (_) @module) + (#set! "priority" 105)) + +; Keywords +[ + "def" + "let" +] @keyword + +[ + "enum" + "struct" + "union" + "type" +] @keyword.type + +"fn" @keyword.function + +[ + "defer" + "yield" + "return" +] @keyword.return + +[ + "as" + "is" +] @keyword.operator + +; Typedefs +(type_declaration + "type" + (identifier) @type.definition + . + "=") + +; Qualifiers +[ + "const" + "static" + "nullable" +] @keyword.modifier + +; Attributes +[ + "@fini" + "@init" + "@test" + "@noreturn" + "@packed" + (declaration_attribute) +] @attribute + +; Labels +((label) @label + (#set! "priority" 105)) + +; Functions +(function_declaration + "fn" + . + (identifier) @function) + +(call_expression + . + (identifier) @function.call) + +(call_expression + . + (scoped_type_identifier + . + (identifier) + . + "::" + . + (identifier) @function.method.call)) + +((call_expression + . + (identifier) @function.builtin) + (#any-of? @function.builtin "align" "assert" "free" "len" "offset" "size")) + +(size_expression + "size" @function.builtin) + +((function_declaration + "fn" + . + (identifier) @constructor) + (#eq? @constructor "init")) + +((call_expression + . + (identifier) @constructor) + (#eq? @constructor "init")) + +; Parameters +(parameter + (_) @variable.parameter + . + ":") + +; Fields +((member_expression + "." + (_) @variable.member) + (#set! "priority" 105)) + +(field + . + (identifier) @variable.member) + +(field_assignment + . + (identifier) @variable.member) + +; Repeats +"for" @keyword.repeat + +; Conditionals +[ + "if" + "else" + "break" + "switch" + "match" + "case" +] @keyword.conditional + +; Operators +[ + "+" + "-" + "*" + "/" + "%" + "||" + "&&" + "^^" + "|" + "&" + "^" + "==" + "!=" + ">" + ">=" + "<=" + "<" + "<<" + ">>" + "~" + "!" + "+=" + "-=" + "*=" + "/=" + "%=" + "<<=" + ">>=" + "|=" + "&=" + "^=" + "||=" + "&&=" + "^^=" + "=" + "?" +] @operator + +; Punctuation +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + ".." + "..." + "_" +] @punctuation.special + +(pointer_type + "*" @punctuation.special) + +(slice_type + "*" @punctuation.special) + +(error_type + "!" @punctuation.special) + +[ + "," + "." + ":" + ";" + "::" + "=>" +] @punctuation.delimiter + +; Literals +[ + (string) + (raw_string) +] @string + +(rune) @character + +(escape_sequence) @string.escape + +(number) @number + +(float) @number.float + +(boolean) @boolean + +[ + (void) + (null) +] @constant.builtin + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/indents.scm new file mode 100644 index 00000000..d729663e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/indents.scm @@ -0,0 +1,44 @@ +[ + (enum_type) + (struct_type) + (tuple_type) + (union_type) + (block) + (for_statement) + (call_expression) + (case) + (array_literal) + (struct_literal) + (tuple_literal) +] @indent.begin + +(if_statement + ("(" + condition: (_)")") @indent.begin) + +[ + "}" + "]" + ")" +] @indent.end + +[ + "{" + "}" +] @indent.branch + +[ + "[" + "]" +] @indent.branch + +[ + "(" + ")" +] @indent.branch + +[ + (ERROR) + (comment) + (concatenated_string) +] @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/injections.scm new file mode 100644 index 00000000..88a3f1cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/injections.scm @@ -0,0 +1,18 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((call_expression + . + (_) @_fnname + . + "(" + . + (_ + [ + (string_content) + (raw_string_content) + ] @injection.content) + . + ")") + (#any-of? @_fnname "compile" "regex::compile") + (#set! injection.language "regex")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/locals.scm new file mode 100644 index 00000000..12a214bf --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hare/locals.scm @@ -0,0 +1,65 @@ +; Scopes +[ + (module) + (function_declaration) + (if_statement) + (for_statement) + (match_expression) + (switch_expression) +] @local.scope + +; References +[ + (identifier) + (scoped_type_identifier) +] @local.reference + +; Definitions +(global_binding + (identifier) @local.definition.constant + . + ":" + (_)) + +(const_declaration + "const" + (identifier) @local.definition.constant + . + "=") + +(field + . + (identifier) @local.definition.field) + +(field_assignment + . + (identifier) @local.definition.field) + +(function_declaration + "fn" + . + (identifier) @local.definition.function) + +(parameter + (_) @local.definition.parameter + . + ":") + +(type_declaration + "type" + (identifier) @local.definition.type + . + "=") + +(type_declaration + "type" + (identifier) @local.definition.enum + . + "=" + (enum_type)) + +(let_declaration + "let" + . + (identifier) @local.definition.var + ","?) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/folds.scm new file mode 100644 index 00000000..d7b820c9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/folds.scm @@ -0,0 +1,6 @@ +[ + (apply) + (do) + (function) + (import)+ +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/highlights.scm new file mode 100644 index 00000000..975a8c18 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/highlights.scm @@ -0,0 +1,466 @@ +; ---------------------------------------------------------------------------- +; Parameters and variables +; NOTE: These are at the top, so that they have low priority, +; and don't override destructured parameters +(variable) @variable + +(pattern/wildcard) @variable + +(decl/function + patterns: (patterns + (_) @variable.parameter)) + +(expression/lambda + (_)+ @variable.parameter + "->") + +(decl/function + (infix + (pattern) @variable.parameter)) + +; ---------------------------------------------------------------------------- +; Literals and comments +(integer) @number + +(negation) @number + +(expression/literal + (float)) @number.float + +(char) @character + +(string) @string + +(comment) @comment + +(haddock) @comment.documentation + +; ---------------------------------------------------------------------------- +; Punctuation +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + "," + ";" +] @punctuation.delimiter + +; ---------------------------------------------------------------------------- +; Keywords, operators, includes +[ + "forall" + ; "∀" ; utf-8 is not cross-platform safe +] @keyword.repeat + +(pragma) @keyword.directive + +[ + "if" + "then" + "else" + "case" + "of" +] @keyword.conditional + +[ + "import" + "qualified" + "module" +] @keyword.import + +[ + (operator) + (constructor_operator) + (all_names) + (wildcard) + "." + ".." + "=" + "|" + "::" + "=>" + "->" + "<-" + "\\" + "`" + "@" +] @operator + +(module + (module_id) @module) + +[ + "where" + "let" + "in" + "class" + "instance" + "pattern" + "data" + "newtype" + "family" + "type" + "as" + "hiding" + "deriving" + "via" + "stock" + "anyclass" + "do" + "mdo" + "rec" + "infix" + "infixl" + "infixr" +] @keyword + +; ---------------------------------------------------------------------------- +; Functions and variables +(decl + [ + name: (variable) @function + names: (binding_list + (variable) @function) + ]) + +(decl/bind + name: (variable) @variable) + +; Consider signatures (and accompanying functions) +; with only one value on the rhs as variables +(decl/signature + name: (variable) @variable + type: (type)) + +((decl/signature + name: (variable) @_name + type: (type)) + . + (decl + name: (variable) @variable) + match: (_)(#eq? @_name @variable)) + +; but consider a type that involves 'IO' a decl/function +(decl/signature + name: (variable) @function + type: (type/apply + constructor: (name) @_type) + (#eq? @_type "IO")) + +((decl/signature + name: (variable) @_name + type: (type/apply + constructor: (name) @_type) + (#eq? @_type "IO")) + . + (decl + name: (variable) @function) + match: (_)(#eq? @_name @function)) + +((decl/signature) @function + . + (decl/function + name: (variable) @function)) + +(decl/bind + name: (variable) @function + (match + expression: (expression/lambda))) + +; view patterns +(view_pattern + [ + (expression/variable) @function.call + (expression/qualified + (variable) @function.call) + ]) + +; consider infix functions as operators +(infix_id + [ + (variable) @operator + (qualified + (variable) @operator) + ]) + +; decl/function calls with an infix operator +; e.g. func <$> a <*> b +(infix + left_operand: [ + (variable) @function.call + (qualified + ((module) @module + (variable) @function.call)) + ]) + +; infix operators applied to variables +((expression/variable) @variable + . + (operator)) + +((operator) + . + [ + (expression/variable) @variable + (expression/qualified + (variable) @variable) + ]) + +; infix operator function definitions +(function + (infix + left_operand: [ + (variable) @variable + (qualified + ((module) @module + (variable) @variable)) + ]) + match: (match)) + +; decl/function calls with infix operators +([ + (expression/variable) @function.call + (expression/qualified + (variable) @function.call) +] + . + (operator) @_op + (#any-of? @_op "$" "<$>" ">>=" "=<<")) + +; right hand side of infix operator +((infix + [ + (operator) + (infix_id + (variable)) + ] ; infix or `func` + . + [ + (variable) @function.call + (qualified + (variable) @function.call) + ]) + . + (operator) @_op + (#any-of? @_op "$" "<$>" "=<<")) + +; decl/function composition, arrows, monadic composition (lhs) +([ + (expression/variable) @function + (expression/qualified + (variable) @function) +] + . + (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; right hand side of infix operator +((infix + [ + (operator) + (infix_id + (variable)) + ] ; infix or `func` + . + [ + (variable) @function + (qualified + (variable) @function) + ]) + . + (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; function composition, arrows, monadic composition (rhs) +((operator) @_op + . + [ + (expression/variable) @function + (expression/qualified + (variable) @function) + ] + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; function defined in terms of a function composition +(decl/function + name: (variable) @function + (match + expression: (infix + operator: (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")))) + +(apply + [ + (expression/variable) @function.call + (expression/qualified + (variable) @function.call) + ]) + +; function compositions, in parentheses, applied +; lhs +(apply + . + (expression/parens + (infix + [ + (variable) @function.call + (qualified + (variable) @function.call) + ] + . + (operator)))) + +; rhs +(apply + . + (expression/parens + (infix + (operator) + . + [ + (variable) @function.call + (qualified + (variable) @function.call) + ]))) + +; variables being passed to a function call +(apply + (_) + . + [ + (expression/variable) @variable + (expression/qualified + (variable) @variable) + ]) + +; main is always a function +; (this prevents `main = undefined` from being highlighted as a variable) +(decl/bind + name: (variable) @function + (#eq? @function "main")) + +; scoped function types (func :: a -> b) +(signature + pattern: (pattern/variable) @function + type: (function)) + +; signatures that have a function type +; + binds that follow them +(decl/signature + name: (variable) @function + type: (function)) + +((decl/signature + name: (variable) @_name + type: (quantified_type)) + . + (decl/bind + (variable) @function) + (#eq? @function @_name)) + +; Treat constructor assignments (smart constructors) as functions, e.g. mkJust = Just +(bind + name: (variable) @function + match: (match + expression: (constructor))) + +; Function composition +(bind + name: (variable) @function + match: (match + expression: (infix + operator: (operator) @_op + (#eq? @_op ".")))) + +; ---------------------------------------------------------------------------- +; Types +(name) @type + +(type/unit) @type + +(type/unit + [ + "(" + ")" + ] @type) + +(type/list + [ + "[" + "]" + ] @type) + +(type/star) @type + +(constructor) @constructor + +; True or False +((constructor) @boolean + (#any-of? @boolean "True" "False")) + +; otherwise (= True) +((variable) @boolean + (#eq? @boolean "otherwise")) + +; ---------------------------------------------------------------------------- +; Quasi-quotes +(quoter) @function.call + +(quasiquote + quoter: [ + (quoter) @_name + (quoter + (qualified + id: (variable) @_name)) + ] + (#eq? @_name "qq") + body: (quasiquote_body) @string) + +; namespaced quasi-quoter +(quoter + [ + (variable) @function.call + (_ + (module) @module + . + (variable) @function.call) + ]) + +; Highlighting of quasiquote_body for other languages is handled by injections.scm +; ---------------------------------------------------------------------------- +; Exceptions/error handling +((variable) @keyword.exception + (#any-of? @keyword.exception + "error" "undefined" "try" "tryJust" "tryAny" "catch" "catches" "catchJust" "handle" "handleJust" + "throw" "throwIO" "throwTo" "throwError" "ioError" "mask" "mask_" "uninterruptibleMask" + "uninterruptibleMask_" "bracket" "bracket_" "bracketOnErrorSource" "finally" "fail" + "onException" "expectationFailure")) + +; ---------------------------------------------------------------------------- +; Debugging +((variable) @keyword.debug + (#any-of? @keyword.debug + "trace" "traceId" "traceShow" "traceShowId" "traceWith" "traceShowWith" "traceStack" "traceIO" + "traceM" "traceShowM" "traceEvent" "traceEventWith" "traceEventIO" "flushEventLog" "traceMarker" + "traceMarkerIO")) + +; ---------------------------------------------------------------------------- +; Fields +(field_name + (variable) @variable.member) + +(import_name + (name) + . + (children + (variable) @variable.member)) + +; ---------------------------------------------------------------------------- +; Spell checking +(comment) @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/injections.scm new file mode 100644 index 00000000..59fe7335 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/injections.scm @@ -0,0 +1,76 @@ +; ----------------------------------------------------------------------------- +; General language injection +(quasiquote + (quoter) @injection.language + (quasiquote_body) @injection.content) + +((comment) @injection.content + (#set! injection.language "comment")) + +; ----------------------------------------------------------------------------- +; shakespeare library +; NOTE: doesn't support templating +; TODO: add once CoffeeScript parser is added +; ; CoffeeScript: Text.Coffee +; (quasiquote +; (quoter) @_name +; (#eq? @_name "coffee") +; ((quasiquote_body) @injection.content +; (#set! injection.language "coffeescript"))) +; CSS: Text.Cassius, Text.Lucius +(quasiquote + (quoter) @_name + (#any-of? @_name "cassius" "lucius") + (quasiquote_body) @injection.content + (#set! injection.language "css")) + +; HTML: Text.Hamlet +(quasiquote + (quoter) @_name + (#any-of? @_name "shamlet" "xshamlet" "hamlet" "xhamlet" "ihamlet") + (quasiquote_body) @injection.content + (#set! injection.language "html")) + +; JS: Text.Julius +(quasiquote + (quoter) @_name + (#any-of? @_name "js" "julius") + (quasiquote_body) @injection.content + (#set! injection.language "javascript")) + +; TS: Text.TypeScript +(quasiquote + (quoter) @_name + (#any-of? @_name "tsc" "tscJSX") + (quasiquote_body) @injection.content + (#set! injection.language "typescript")) + +; ----------------------------------------------------------------------------- +; HSX +(quasiquote + (quoter) @_name + (#eq? @_name "hsx") + (quasiquote_body) @injection.content + (#set! injection.language "html")) + +; ----------------------------------------------------------------------------- +; Inline JSON from aeson +(quasiquote + (quoter) @_name + (#eq? @_name "aesonQQ") + (quasiquote_body) @injection.content + (#set! injection.language "json")) + +; ----------------------------------------------------------------------------- +; SQL +; postgresql-simple +(quasiquote + (quoter) @injection.language + (#eq? @injection.language "sql") + (quasiquote_body) @injection.content) + +(quasiquote + (quoter) @_name + (#any-of? @_name "persistUpperCase" "persistLowerCase" "persistWith") + (quasiquote_body) @injection.content + (#set! injection.language "haskell_persistent")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/locals.scm new file mode 100644 index 00000000..7177f9de --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell/locals.scm @@ -0,0 +1,9 @@ +(signature + name: (variable)) @local.definition + +(function + name: (variable)) @local.definition + +(pattern/variable) @local.definition + +(expression/variable) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell_persistent/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell_persistent/folds.scm new file mode 100644 index 00000000..dd34d9f1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell_persistent/folds.scm @@ -0,0 +1 @@ +(entity_definition) @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell_persistent/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell_persistent/highlights.scm new file mode 100644 index 00000000..22cbf5cb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/haskell_persistent/highlights.scm @@ -0,0 +1,38 @@ +; ---------------------------------------------------------------------------- +; Literals and comments +(integer) @number + +(float) @number.float + +(char) @character + +(string) @string + +(attribute_name) @attribute + +(attribute_exclamation_mark) @attribute + +(con_unit) @string.special.symbol ; unit, as in () + +(comment) @comment @spell + +; ---------------------------------------------------------------------------- +; Keywords, operators, includes +[ + "Id" + "Primary" + "Foreign" + "deriving" +] @keyword + +"=" @operator + +; ---------------------------------------------------------------------------- +; Functions and variables +(variable) @variable + +; ---------------------------------------------------------------------------- +; Types +(type) @type + +(constructor) @constructor diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/folds.scm new file mode 100644 index 00000000..e0c5313a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/folds.scm @@ -0,0 +1,6 @@ +[ + (comment) + (block) + (heredoc_template) + (object) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/highlights.scm new file mode 100644 index 00000000..4fb7d1fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/highlights.scm @@ -0,0 +1,118 @@ +; highlights.scm +[ + "!" + "\*" + "/" + "%" + "\+" + "-" + ">" + ">=" + "<" + "<=" + "==" + "!=" + "&&" + "||" +] @operator + +[ + "{" + "}" + "[" + "]" + "(" + ")" +] @punctuation.bracket + +[ + "." + ".*" + "," + "[*]" +] @punctuation.delimiter + +[ + (ellipsis) + "\?" + "=>" +] @punctuation.special + +[ + ":" + "=" +] @none + +[ + "for" + "endfor" + "in" +] @keyword.repeat + +[ + "if" + "else" + "endif" +] @keyword.conditional + +[ + (quoted_template_start) ; " + (quoted_template_end) ; " + (template_literal) ; non-interpolation/directive content +] @string + +[ + (heredoc_identifier) ; END + (heredoc_start) ; << or <<- +] @punctuation.delimiter + +[ + (template_interpolation_start) ; ${ + (template_interpolation_end) ; } + (template_directive_start) ; %{ + (template_directive_end) ; } + (strip_marker) ; ~ +] @punctuation.special + +(numeric_lit) @number + +(bool_lit) @boolean + +(null_lit) @constant + +(comment) @comment @spell + +(identifier) @variable + +(body + (block + (identifier) @keyword)) + +(body + (block + (body + (block + (identifier) @type)))) + +(function_call + (identifier) @function) + +(attribute + (identifier) @variable.member) + +; { key: val } +; +; highlight identifier keys as though they were block attributes +(object_elem + key: (expression + (variable_expr + (identifier) @variable.member))) + +; var.foo, data.bar +; +; first element in get_attr is a variable.builtin or a reference to a variable.builtin +(expression + (variable_expr + (identifier) @variable.builtin) + (get_attr + (identifier) @variable.member)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/indents.scm new file mode 100644 index 00000000..1d7dc49d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/indents.scm @@ -0,0 +1,16 @@ +[ + (block) + (object) + (tuple) + (function_call) +] @indent.begin + +[ + "]" + ")" + "}" +] @indent.branch @indent.end + +(comment) @indent.auto + +(ERROR) @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/injections.scm new file mode 100644 index 00000000..b68f4cae --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hcl/injections.scm @@ -0,0 +1,7 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +(heredoc_template + (template_literal) @injection.content + (heredoc_identifier) @injection.language + (#downcase! @injection.language)) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/folds.scm new file mode 100644 index 00000000..88d4f17f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/folds.scm @@ -0,0 +1,6 @@ +; HEEx tags, components, and slots fold similar to HTML +[ + (component) + (tag) + (slot) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/highlights.scm new file mode 100644 index 00000000..e2fb0188 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/highlights.scm @@ -0,0 +1,54 @@ +; HEEx delimiters +[ + "%>" + "--%>" + "-->" + "/>" + "" + "{" + "}" +] @tag.delimiter + +; HEEx operators are highlighted as such +"=" @operator + +; HEEx inherits the DOCTYPE tag from HTML +(doctype) @constant + +; HEEx comments are highlighted as such +(comment) @comment @spell + +; HEEx text content is treated as markup +; (text) @none +; HEEx tags and slots are highlighted as HTML +[ + (tag_name) + (slot_name) +] @tag + +; HEEx attributes are highlighted as HTML attributes +(attribute_name) @tag.attribute + +[ + (attribute_value) + (quoted_attribute_value) +] @string + +; HEEx components are highlighted as modules and function calls +(component_name + [ + (module) @type + (function) @function + "." @punctuation.delimiter + ]) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/indents.scm new file mode 100644 index 00000000..82a2f891 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/indents.scm @@ -0,0 +1,20 @@ +; HEEx tags, components, and slots indent like HTML +[ + (component) + (slot) + (tag) +] @indent.begin + +; Dedent at the end of each tag, component, and slot +[ + (end_component) + (end_slot) + (end_tag) +] @indent.branch @indent.dedent + +; Self-closing tags and components should not change +; indentation level of sibling nodes +[ + (self_closing_component) + (self_closing_tag) +] @indent.auto diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/injections.scm new file mode 100644 index 00000000..4f179ee8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/injections.scm @@ -0,0 +1,30 @@ +; directives are standalone tags like '<%= @x %>' +; +; partial_expression_values are elixir code that is part of an expression that +; spans multiple directive nodes, so they must be combined. For example: +; <%= if true do %> +;

hello, tree-sitter!

+; <% end %> +(directive + [ + (partial_expression_value) + (ending_expression_value) + ] @injection.content + (#set! injection.language "elixir") + (#set! injection.include-children) + (#set! injection.combined)) + +; Regular expression_values do not need to be combined +((directive + (expression_value) @injection.content) + (#set! injection.language "elixir")) + +; expressions live within HTML tags, and do not need to be combined +; +(expression + (expression_value) @injection.content + (#set! injection.language "elixir")) + +; HEEx comments +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/locals.scm new file mode 100644 index 00000000..cfa239e5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/heex/locals.scm @@ -0,0 +1,13 @@ +; HEEx tags, components, and slots are references +[ + (component_name) + (slot_name) + (tag_name) +] @local.reference + +; Create a new scope within each HEEx tag, component, and slot +[ + (component) + (slot) + (tag) +] @local.scope diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/helm/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/helm/highlights.scm new file mode 100644 index 00000000..05028ba9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/helm/highlights.scm @@ -0,0 +1,45 @@ +; inherits: gotmpl + +; Builtin functions +(function_call + function: (identifier) @function.builtin + (#any-of? @function.builtin + "and" "or" "not" "eq" "ne" "lt" "le" "gt" "ge" "default" "required" "empty" "fail" "coalesce" + "ternary" "print" "println" "printf" "trim" "trimAll" "trimPrefix" "trimSuffix" "lower" "upper" + "title" "untitle" "repeat" "substr" "nospace" "trunc" "abbrev" "abbrevboth" "initials" + "randAlphaNum" "randAlpha" "randNumeric" "randAscii" "wrap" "wrapWith" "contains" "hasPrefix" + "hasSuffix" "quote" "squote" "cat" "indent" "nindent" "replace" "plural" "snakecase" "camelcase" + "kebabcase" "swapcase" "shuffle" "toStrings" "toDecimal" "toJson" "mustToJson" "toPrettyJson" + "mustToPrettyJson" "toRawJson" "mustToRawJson" "fromYaml" "fromJson" "fromJsonArray" + "fromYamlArray" "toYaml" "regexMatch" "mustRegexMatch" "regexFindAll" "mustRegexFinDall" + "regexFind" "mustRegexFind" "regexReplaceAll" "mustRegexReplaceAll" "regexReplaceAllLiteral" + "mustRegexReplaceAllLiteral" "regexSplit" "mustRegexSplit" "sha1sum" "sha256sum" "adler32sum" + "htpasswd" "derivePassword" "genPrivateKey" "buildCustomCert" "genCA" "genSelfSignedCert" + "genSignedCert" "encryptAES" "decryptAES" "now" "ago" "date" "dateInZone" "duration" + "durationRound" "unixEpoch" "dateModify" "mustDateModify" "htmlDate" "htmlDateInZone" "toDate" + "mustToDate" "dict" "get" "set" "unset" "hasKey" "pluck" "dig" "merge" "mustMerge" + "mergeOverwrite" "mustMergeOverwrite" "keys" "pick" "omit" "values" "deepCopy" "mustDeepCopy" + "b64enc" "b64dec" "b32enc" "b32dec" "list" "first" "mustFirst" "rest" "mustRest" "last" + "mustLast" "initial" "mustInitial" "append" "mustAppend" "prepend" "mustPrepend" "concat" + "reverse" "mustReverse" "uniq" "mustUniq" "without" "mustWithout" "has" "mustHas" "compact" + "mustCompact" "index" "slice" "mustSlice" "until" "untilStep" "seq" "add" "add1" "sub" "div" + "mod" "mul" "max" "min" "len" "addf" "add1f" "subf" "divf" "mulf" "maxf" "minf" "floor" "ceil" + "round" "getHostByName" "base" "dir" "clean" "ext" "isAbs" "kindOf" "kindIs" "typeOf" "typeIs" + "typeIsLike" "deepequal" "semver" "semverCompare" "urlParse" "urlJoin" "urlquery" "lookup" + "include") + ) + +; {{ .Values.test }} +(selector_expression + operand: (field + name: (identifier) @constant.builtin + (#any-of? @constant.builtin + "Values" "Chart" "Release" "Capabilities" "Files" "Subcharts" "Template")) + (field_identifier)) + +; {{ $.Values.test }} +(selector_expression + operand: (variable) + field: (field_identifier) @constant.builtin + (#any-of? @constant.builtin + "Values" "Chart" "Release" "Capabilities" "Files" "Subcharts" "Template")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/helm/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/helm/injections.scm new file mode 100644 index 00000000..2adf8743 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/helm/injections.scm @@ -0,0 +1,36 @@ +; inherits: gotmpl + +((text) @injection.content + (#set! injection.language "yaml") + (#set! injection.combined)) + +; {{ regexFind "[a-zA-Z][1-9]" "abcd1234" }} +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#any-of? @_function + "regexMatch" "mustRegexMatch" "regexFindAll" "mustRegexFinDall" "regexFind" "mustRegexFind" + "regexReplaceAll" "mustRegexReplaceAll" "regexReplaceAllLiteral" "mustRegexReplaceAllLiteral" + "regexSplit" "mustRegexSplit") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "regex")) + +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#any-of? @_function "fromYaml" "fromYamlArray") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "yaml")) + +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#any-of? @_function "fromJson" "fromJsonArray") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "json")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/folds.scm new file mode 100644 index 00000000..41269219 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/folds.scm @@ -0,0 +1 @@ +; inherits: json diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/highlights.scm new file mode 100644 index 00000000..8333d8fe --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/highlights.scm @@ -0,0 +1,28 @@ +(true) @boolean + +(false) @boolean + +(null) @constant.builtin + +(number) @number + +(pair + key: (string) @label) + +(pair + value: (string) @string) + +(array + (string) @string) + +; (string_content (escape_sequence) @string.escape) +; "," @punctuation.delimiter +"[" @punctuation.bracket + +"]" @punctuation.bracket + +"{" @punctuation.bracket + +"}" @punctuation.bracket + +(comment) @comment @spell diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/indents.scm new file mode 100644 index 00000000..3b01ca99 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/indents.scm @@ -0,0 +1,3 @@ +; inherits: json + +(comment) @indent.ignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/locals.scm new file mode 100644 index 00000000..41269219 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hjson/locals.scm @@ -0,0 +1 @@ +; inherits: json diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/folds.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/folds.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/highlights.scm new file mode 100644 index 00000000..839d8d79 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/highlights.scm @@ -0,0 +1,38 @@ +; inherits: cpp + +[ + "in" + "out" + "inout" + "uniform" + "shared" + "groupshared" + "discard" + "cbuffer" + "row_major" + "column_major" + "globallycoherent" + "centroid" + "noperspective" + "nointerpolation" + "sample" + "linear" + "snorm" + "unorm" + "point" + "line" + "triangleadj" + "lineadj" + "triangle" +] @keyword.modifier + +((identifier) @variable.builtin + (#lua-match? @variable.builtin "^SV_")) + +(hlsl_attribute) @attribute + +(hlsl_attribute + [ + "[" + "]" + ] @attribute) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/indents.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/indents.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/injections.scm new file mode 100644 index 00000000..c2fca712 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/injections.scm @@ -0,0 +1,5 @@ +((preproc_arg) @injection.content + (#set! injection.language "hlsl")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/locals.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsl/locals.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsplaylist/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsplaylist/highlights.scm new file mode 100644 index 00000000..9852aed6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsplaylist/highlights.scm @@ -0,0 +1,41 @@ +; Comments +(comment) @comment @spell + +; General +(uri) @string.special.url + +(tag_name) @keyword + +(attribute_name) @attribute + +[ + (dec) + (hex) + (resolution) + (range) +] @number + +(float) @number.float + +(string) @string + +[ + (enum) + (date_time_msec) +] @string.special + +(title) @markup.heading + +; Literals +[ + "=" + "x" + "@" +] @operator + +[ + ":" + "," +] @punctuation.delimiter + +"#" @punctuation.special diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsplaylist/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsplaylist/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hlsplaylist/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hocon/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hocon/folds.scm new file mode 100644 index 00000000..cc8a231a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hocon/folds.scm @@ -0,0 +1,4 @@ +[ + (object) + (array) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hocon/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hocon/highlights.scm new file mode 100644 index 00000000..0eb94e6d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hocon/highlights.scm @@ -0,0 +1,65 @@ +(comment) @comment @spell + +(null) @constant.builtin + +[ + (true) + (false) +] @boolean + +(number) @number + +(unit) @keyword + +(string) @string + +(multiline_string) @string + +(string + (escape_sequence) @string.escape) + +(unquoted_string) @string + +[ + "url" + "file" + "classpath" + "required" +] @keyword + +(include + "include" @keyword.import) + +(substitution + [ + "${" + "${?" + "}" + ] @punctuation.special) + +(substitution + (_) @variable.member) + +(path + (_) @variable.member) + +(value + [ + ":" + "=" + "+=" + ] @operator) + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +"," @punctuation.delimiter + +(unquoted_path + "." @punctuation.delimiter) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hocon/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hocon/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hocon/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hoon/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hoon/folds.scm new file mode 100644 index 00000000..cfb4b6d0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hoon/folds.scm @@ -0,0 +1,6 @@ +[ + (bartisTall) + (luslusTall) + (lusbucTall) + (barcenTall) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hoon/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hoon/highlights.scm new file mode 100644 index 00000000..d98ee44e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hoon/highlights.scm @@ -0,0 +1,35 @@ +(number) @number + +(string) @string + +[ + "(" + ")" + "[" + "]" +] @punctuation.bracket + +[ + (coreTerminator) + (seriesTerminator) +] @punctuation.delimiter + +(rune) @operator + +(term) @constant + +(aura) @constant.builtin + +(Gap) @comment + +(boolean) @constant.builtin + +(date) @string.special + +(mold) @string.special.symbol + +(specialIndex) @number + +(lark) @operator + +(fullContext) @string.special.symbol diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hoon/locals.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hoon/locals.scm new file mode 100644 index 00000000..fd7cc026 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/hoon/locals.scm @@ -0,0 +1,4 @@ +(tisfasTall + name: (name) @local.definition.var) + +(name) @local.reference diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html/folds.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html/folds.scm new file mode 100644 index 00000000..69b57eac --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html/folds.scm @@ -0,0 +1,5 @@ +[ + (element) + (style_element) + (script_element) +] @fold diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html/highlights.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html/highlights.scm new file mode 100644 index 00000000..4e2371d6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html/highlights.scm @@ -0,0 +1,7 @@ +; inherits: html_tags + +(doctype) @constant + +"" + "" +] @tag.delimiter + +"=" @operator diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html_tags/indents.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html_tags/indents.scm new file mode 100644 index 00000000..6fe984fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html_tags/indents.scm @@ -0,0 +1,38 @@ +[ + ((element + (start_tag + (tag_name) @_not_void_element)) + (#not-any-of? @_not_void_element + "area" "base" "basefont" "bgsound" "br" "col" "command" "embed" "frame" "hr" "image" "img" + "input" "isindex" "keygen" "link" "menuitem" "meta" "nextid" "param" "source" "track" "wbr")) + (element + (self_closing_tag)) +] @indent.begin + +((start_tag + (tag_name) @_void_element) + (#any-of? @_void_element + "area" "base" "basefont" "bgsound" "br" "col" "command" "embed" "frame" "hr" "image" "img" + "input" "isindex" "keygen" "link" "menuitem" "meta" "nextid" "param" "source" "track" "wbr")) @indent.begin + +; These are the nodes that will be captured when we do `normal o` +; But last element has already been ended, so capturing this +; to mark end of last element +(element + (end_tag + ">" @indent.end)) + +(element + (self_closing_tag + "/>" @indent.end)) + +; Script/style elements aren't indented, so only branch the end tag of other elements +(element + (end_tag) @indent.branch) + +[ + ">" + "/>" +] @indent.branch + +(comment) @indent.ignore diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html_tags/injections.scm b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html_tags/injections.scm new file mode 100644 index 00000000..5b78e37b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/queries/html_tags/injections.scm @@ -0,0 +1,91 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +; +; +; Add "lang" to predicate check so that vue/svelte can inherit this +; without having this element being captured twice +((style_element + (start_tag) @_no_type_lang + (raw_text) @injection.content) + (#not-lua-match? @_no_type_lang "%slang%s*=") + (#not-lua-match? @_no_type_lang "%stype%s*=") + (#set! injection.language "css")) + +((style_element + (start_tag + (attribute + (attribute_name) @_type + (quoted_attribute_value + (attribute_value) @_css))) + (raw_text) @injection.content) + (#eq? @_type "type") + (#eq? @_css "text/css") + (#set! injection.language "css")) + +; +; +((script_element + (start_tag) @_no_type_lang + (raw_text) @injection.content) + (#not-lua-match? @_no_type_lang "%slang%s*=") + (#not-lua-match? @_no_type_lang "%stype%s*=") + (#set! injection.language "javascript")) + +; + diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/tests/indent/html/self_closing_tag.html b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/indent/html/self_closing_tag.html new file mode 100644 index 00000000..79376b83 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/indent/html/self_closing_tag.html @@ -0,0 +1,14 @@ + + + + + + + + + +
+ + + + diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/vue/negative-assertions.vue b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/vue/negative-assertions.vue new file mode 100644 index 00000000..000702a1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/vue/negative-assertions.vue @@ -0,0 +1,4 @@ + + + + diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/vue/test-vue-injections.vue b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/vue/test-vue-injections.vue new file mode 100644 index 00000000..4966e6ac --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/vue/test-vue-injections.vue @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + // const file = files[0]; diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/yaml/bash-on-github-actions.yml b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/yaml/bash-on-github-actions.yml new file mode 100644 index 00000000..5c732b09 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/yaml/bash-on-github-actions.yml @@ -0,0 +1,32 @@ +name: CI +on: + push: + branches: [master] + pull_request: + branches: [master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: '16' + - name: Install dependencies + run: npm ci + # ^ @bash + - name: Run tests + run: npm test + # ^ @bash + - name: Parse Petalisp + run: | + git submodule init + git submodule update + if (( $(node_modules/tree-sitter-cli/tree-sitter parse test/Petalisp/**/*.lisp -q | wc -l) > 2 )); then # There are 2 known failures (strings that are not format strings but use ~X syntax) + exit 1 + else + echo "Successfully parsed Petalisp" + fi + # ^ @bash + - name: Run tests + run: npm test diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/yaml/promql-on-prometheus-rules.yaml b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/yaml/promql-on-prometheus-rules.yaml new file mode 100644 index 00000000..f064da37 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/query/injections/yaml/promql-on-prometheus-rules.yaml @@ -0,0 +1,19 @@ +groups: +- name: Hardware alerts + rules: + - alert: Node down + expr: up{job="node_exporter"} == 0 + # ^ @promql + for: 3m + labels: + severity: warning + annotations: + title: Node {{ $labels.instance }} is down + description: Failed to scrape {{ $labels.job }} on {{ $labels.instance }} for more than 3 minutes. Node seems down. + - alert: Node down + expr: | + up{job="node_exporter"} == 0 + # ^ @promql + for: 3m + labels: + severity: warning diff --git a/config/neovim/store/lazy-plugins/nvim-treesitter/tests/unit/ts_utils_spec.lua b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/unit/ts_utils_spec.lua new file mode 100644 index 00000000..397fa7ea --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-treesitter/tests/unit/ts_utils_spec.lua @@ -0,0 +1,114 @@ +local tsutils = require "nvim-treesitter.ts_utils" + +describe("update_selection", function() + local function get_updated_selection(case) + vim.api.nvim_buf_set_lines(0, 0, -1, false, case.lines) + tsutils.update_selection(0, case.node, case.selection_mode) + vim.cmd "normal! y" + return vim.fn.getreg '"' + end + + it("charwise1", function() + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 0, 2, 1 }, + selection_mode = "v", + }, + "foo\n\nb" + ) + it("charwise2", function() end) + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 1, 2, 1 }, + selection_mode = "v", + }, + "oo\n\nb" + ) + it("charwise3", function() end) + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 2, 2, 1 }, + selection_mode = "v", + }, + "o\n\nb" + ) + it("charwise4", function() end) + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 3, 2, 1 }, + selection_mode = "v", + }, + "\n\nb" + ) + end) + it("linewise", function() + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 3, 2, 1 }, + selection_mode = "V", + }, + "foo\n\nbar\n" + ) + end) + it("blockwise", function() + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 3, 2, 1 }, + selection_mode = "", + }, + "foo\n\nbar" + ) + end) +end) + +describe("swap_nodes", function() + local function swap(case) + vim.api.nvim_buf_set_lines(0, 0, -1, false, case.lines) + vim.opt.filetype = case.filetype + local a = vim.treesitter.get_node { + bufnr = 0, + pos = { case.a[1], case.a[2] }, + } + local b = vim.treesitter.get_node { + bufnr = 0, + pos = { case.b[1], case.b[2] }, + } + tsutils.swap_nodes(a, b, 0, true) + end + + it("works on adjacent nodes", function() + swap { + filetype = "python", + lines = { "print(1)" }, + a = { 0, 0 }, + b = { 0, 5 }, + } + + it("swaps text", function() end) + assert.same(vim.api.nvim_buf_get_lines(0, 0, -1, false), { "(1)print" }) + + it("moves the cursor", function() end) + assert.same(vim.api.nvim_win_get_cursor(0), { 1, 3 }) + end) + + it("works with multiline nodes", function() + swap { + filetype = "lua", + lines = { "x = { [[", "]], [[", ".....]]}" }, + a = { 0, 6 }, + b = { 1, 4 }, + } + + it("swaps text", function() end) + assert.same(vim.api.nvim_buf_get_lines(0, 0, -1, false), { "x = { [[", ".....]], [[", "]]}" }) + + it("moves the cursor", function() end) + assert.same(vim.api.nvim_win_get_cursor(0), { 2, 9 }) + end) +end) diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/.github/ISSUE_TEMPLATE/bug_report.yml b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..847f75db --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,53 @@ +name: Bug Report +description: Report a problem in nvim-ts-context-commentstring +labels: [bug] + +body: + - type: textarea + attributes: + label: "Minimal reproducible full config" + description: | + Please provide a working config based on [this](https://github.com/JoosepAlviste/nvim-ts-context-commentstring/blob/main/utils/minimal_init.lua). + + 1. Clone this plugin somewhere. E.g. `git clone https://github.com/JoosepAlviste/nvim-ts-context-commentstring ~/nvim-repro/nvim-ts-context-commentstring` + 2. Run `utils/run_minimal.sh` from the cloned repository + 3. Make the necessary changes to the config file to reproduce your issue + value: | + ```lua + -- Your configuration here + ``` + validations: + required: true + + - type: textarea + attributes: + label: "Description" + description: "Describe in detail what happens" + validations: + required: true + + - type: textarea + attributes: + label: "Steps to reproduce" + description: "Full reproduction steps. Please include a sample file if your issue relates to a specific filetype." + validations: + required: true + + - type: textarea + attributes: + label: "Expected behavior" + description: "A description of the behavior you expected." + validations: + required: true + + - type: textarea + attributes: + label: "Actual behavior" + description: "A description of the actual behavior." + validations: + required: true + + - type: textarea + attributes: + label: "Additional context" + description: "Any other relevant information" diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/.github/workflows/default.yml b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/.github/workflows/default.yml new file mode 100644 index 00000000..19f554bd --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/.github/workflows/default.yml @@ -0,0 +1,16 @@ +name: default + +on: [pull_request] + +jobs: + lint: + name: Lint Lua code + runs-on: ubuntu-20.04 + steps: + - name: Check out repo + uses: actions/checkout@v2 + - name: Stylua check + uses: JohnnyMorganz/stylua-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --color always --check . diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/.gitignore b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/.gitignore new file mode 100644 index 00000000..c0c5c43f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/.gitignore @@ -0,0 +1 @@ +utils/.testenv diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/LICENSE b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/LICENSE new file mode 100644 index 00000000..db0967de --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Joosep Alviste + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/README.md b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/README.md new file mode 100644 index 00000000..81086655 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/README.md @@ -0,0 +1,107 @@ +# `nvim-ts-context-commentstring` + +A Neovim plugin for setting the `commentstring` option based on the cursor +location in the file. The location is checked via treesitter queries. + +This is useful when there are embedded languages in certain types of files. For +example, Vue files can have many different sections, each of which can have a +different style for comments. + +Note that this plugin *only* changes the `commentstring` setting. It does not +add any mappings for commenting. It is recommended to use a commenting plugin +like [`Comment.nvim`](https://github.com/numToStr/Comment.nvim) alongside this +plugin. + +![Demo gif](https://user-images.githubusercontent.com/9450943/185669080-a5f05064-c247-47f5-9b63-d34a9871186e.gif) + + + +## Getting started + +**Requirements:** + +- [Neovim version 0.9.4](https://github.com/neovim/neovim/releases/tag/v0.9.4) +- Tree-sitter parsers (e.g. installed with [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter)) + +**Installation:** + +Use your favorite plugin manager. For example, here's how it would look like +with [lazy.nvim](https://github.com/folke/lazy.nvim): + +```lua +require('lazy').setup { + 'JoosepAlviste/nvim-ts-context-commentstring', +} +``` + +**Setup:** + +Defaults work out of the box. +Set `vim.g.skip_ts_context_commentstring_module = true` somewhere in your configuration to skip backwards compatibility routines and speed up loading. + +If you want to change the configuration, call the `setup` function of this plugin, e.g.: + +```lua +require('ts_context_commentstring').setup { + enable_autocmd = false, +} +``` + +> **Note** +> +> There is a minimal configuration file available at +> [`utils/minimal_init.lua`](https://github.com/JoosepAlviste/nvim-ts-context-commentstring/blob/main/utils/minimal_init.lua) for reference. + +> **Note** +> +> Don't forget to use `:h lua-heredoc` if you're using `init.vim`. + + +## Configuration + +It is recommended to use a commenting plugin that has an integration available +with this plugin. Then, the `commentstring` calculation can be triggered only +when commenting. The available integrations are listed in the +[wiki](https://github.com/JoosepAlviste/nvim-ts-context-commentstring/wiki/Integrations). +The following plugins have an integration available: + +- [`b3nj5m1n/kommentary`](https://github.com/JoosepAlviste/nvim-ts-context-commentstring/wiki/Integrations#kommentary) +- [`terrortylor/nvim-comment`](https://github.com/JoosepAlviste/nvim-ts-context-commentstring/wiki/Integrations#nvim-comment) +- [`numToStr/Comment.nvim`](https://github.com/JoosepAlviste/nvim-ts-context-commentstring/wiki/Integrations#commentnvim) +- [`echasnovski/mini.nvim/mini-comment`](https://github.com/JoosepAlviste/nvim-ts-context-commentstring/wiki/Integrations#minicomment) +- [`tpope/vim-commentary`](https://github.com/JoosepAlviste/nvim-ts-context-commentstring/wiki/Integrations#vim-commentary) + +However, if an integration is not set up, then the default behavior is to +calculate the `commentstring` on the `CursorHold` autocmd, meaning that the +`:h updatetime` should be set to a smaller value than the default of 4s: + +```lua +vim.opt.updatetime = 100 +``` + +> **Note** +> +> For more advanced configuration options, see `:h ts-context-commentstring`. + + +## More demos + +**React:** + +![React demo gif](https://user-images.githubusercontent.com/9450943/185669182-d523c328-251e-41b0-a76e-d867c401a040.gif) + +**Svelte:** + +![Svelte demo gif](https://user-images.githubusercontent.com/9450943/185669229-ad10848e-ba13-45e0-8447-a3a1f03eb85e.gif) + +**HTML:** + +![html](https://user-images.githubusercontent.com/9450943/185669275-cdfa7fa4-092e-439b-822e-330559a7d4d7.gif) + +**Nesting:** + +I injected HTML into JavaScript strings and created multiple levels of nesting +with language tree. This sort of nesting of languages works without any extra +configuration in the plugin. + +![nested](https://user-images.githubusercontent.com/9450943/185669303-e6958706-f5b7-439c-98f7-2393e6325107.gif) diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/doc/nvim-ts-context-commentstring.txt b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/doc/nvim-ts-context-commentstring.txt new file mode 100644 index 00000000..9a4b4d24 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/doc/nvim-ts-context-commentstring.txt @@ -0,0 +1,155 @@ +*ts-context-commentstring.txt* + +============================================================================== +1. nvim-ts-context-commentstring *ts-context-commentstring-intro* + +*ts-context-commentstring* is a Neovim plugin that uses Treesitter to set the +'commentstring' option based on the cursor location in a file. + +Please see the basic configuration in the `README.md` file. This file only +includes more advanced documentation. + +============================================================================== +2. Supported languages *ts-context-commentstring-languages* + +Currently, the following languages are supported when they are injected with +language tree (see `lua/ts_context_commentstring/internal.lua`): + +- `astro` +- `c` +- `css` +- `cue` +- `gleam` +- `glimmer` +- `graphql` +- `handlebars` +- `html` +- `javascript` +- `lua` +- `nix` +- `php` +- `python` +- `rescript` +- `roc` +- `scss` +- `shell` +- `sql` +- `solidity` +- `svelte` +- `tsx` +- `twig` +- `typescript` +- `vim` +- `vue` +- `zsh` + +This means that in any filetype, if the given languages are injected, this +plugin should detect them and correctly set the 'commentstring'. For example, +Vue files can be injected with `css` or `javascript`. Even though we don't +configure anything for Vue explicitly, the 'commentstring' updating logic +should still work. + + +============================================================================== +3. Commentstring configuration *ts-context-commentstring-commentstring-configuration* + +This plugin initializes using default **plugin** system. + +If you need to disable auto-initialization, set +`g:loaded_ts_context_commentstring` to non-zero value. + +Custom configuration can be supplied using `setup` function: +>lua + require('ts_context_commentstring').setup { + -- ... configuration here + } +< + +Support for more languages can be added quite easily by passing a `languages` (formerly the now deprecated `config`) table +when configuring the plugin: +>lua + require('ts_context_commentstring').setup { + languages = { + css = '// %s', + }, + } +< + +Additionally, some languages are not injected with language tree, but have +multiple commenting styles in the same language. One such example is +JavaScript with JSX. The JSX section is not an injected language, but a part +of the tree generated by the `javascript` Treesitter parser. + +In this more complex case, this plugin supports adding queries for specific +Treesitter nodes. Each node can have its own unique commenting style. For +example, here's how the default configuration for `javascript` would look +like: +>lua + require('ts_context_commentstring').setup { + languages = { + javascript = { + __default = '// %s', + jsx_element = '{/* %s */}', + jsx_fragment = '{/* %s */}', + jsx_attribute = '// %s', + comment = '// %s', + }, + }, + } +< + +The `__default` value is used when none of the other node types are seen. The +rest of the keys refer to the type of the Treesitter node. In this example, if +your cursor is inside a `jsx_element`, then the `{/* %s */}` 'commentstring' +will be set. + +Note that the language refers to the |treesitter| language, not the filetype +or the file extension. + +Additionally, it is possible to have each 'commentstring' configuration be a +table with custom keys. This can be used to configure separate single and +multi-line comment styles (useful when integrating with a commenting plugin): +>lua + require('ts_context_commentstring').setup { + languages = { + typescript = { __default = '// %s', __multiline = '/* %s */' }, + }, + } +< + +Then, the custom key can be passed to `update_commentstring`: +>lua + require('ts_context_commentstring').update_commentstring { + key = '__multiline', + } +< + +Finally, it is possible to customize the tree traversal start location when +calling `update_commentstring`, this is useful in commenting plugin +integrations. There are some useful helper functions exported from +`ts_context_commentstring.utils`: +>lua + require('ts_context_commentstring').calculate_commentstring { + location = require('ts_context_commentstring.utils').get_cursor_location(), + } +< + +If you want to calculate your own 'commentstring' you are able to do so with +the `custom_calculation` option: +>lua + require('ts_context_commentstring').setup { + custom_calculation = function(node, language_tree) + -- ... + end, + } +< + +This is a function that takes in the current node and the language tree which +could be used for context like figuring out which language you should use a +'commentstring' for. You can also for example figure out which type the current +node is. You need to return a 'commentstring' in the `custom_calculation` if you +want it to be set. + + +============================================================================== +vim:tw=78:ts=8:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/doc/tags b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/doc/tags new file mode 100644 index 00000000..19d8e140 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/doc/tags @@ -0,0 +1,5 @@ +ts-context-commentstring nvim-ts-context-commentstring.txt /*ts-context-commentstring* +ts-context-commentstring-commentstring-configuration nvim-ts-context-commentstring.txt /*ts-context-commentstring-commentstring-configuration* +ts-context-commentstring-intro nvim-ts-context-commentstring.txt /*ts-context-commentstring-intro* +ts-context-commentstring-languages nvim-ts-context-commentstring.txt /*ts-context-commentstring-languages* +ts-context-commentstring.txt nvim-ts-context-commentstring.txt /*ts-context-commentstring.txt* diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring.lua b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring.lua new file mode 100644 index 00000000..5835a223 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring.lua @@ -0,0 +1,27 @@ +local M = {} + +---Set up non-default configuration +---@param config ts_context_commentstring.Config +function M.setup(config) + require('ts_context_commentstring.config').update(config) +end + +---Calculate the commentstring based on the current location of the cursor. +--- +---@param args? ts_context_commentstring.Args +--- +---@return string | nil commentstring If found, otherwise `nil` +function M.calculate_commentstring(args) + return require('ts_context_commentstring.internal').calculate_commentstring(args) +end + +---Update the `commentstring` setting based on the current location of the +---cursor. If no `commentstring` can be calculated, will revert to the ofiginal +---`commentstring` for the current file. +--- +---@param args? ts_context_commentstring.Args +function M.update_commentstring(args) + return require('ts_context_commentstring.internal').update_commentstring(args) +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/config.lua b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/config.lua new file mode 100644 index 00000000..71350b53 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/config.lua @@ -0,0 +1,137 @@ +local M = {} + +---A commentstring configuration that includes both single and multi-line +---comments. The fields can be anything and they will be retrievable with the +---`key` option to `update_commentstring`. +---@class ts_context_commentstring.CommentConfigMultiple +---@field __default string Single-line commentstring +---@field __multiline string Multi-line commentstring + +---Commentstring configuration can either be a string (a single commenting +---style) or a table specifying multiple styles. +---@alias ts_context_commentstring.CommentConfig string | ts_context_commentstring.CommentConfigMultiple + +---The comment configuration for a language. +---@alias ts_context_commentstring.LanguageConfig ts_context_commentstring.CommentConfig | table + +---Configuration of the languages to commentstring configs. +--- +---The configuration object keys should be **treesitter** languages, NOT +---filetypes or file extensions. +--- +---You can get the treesitter language for the current file by running this +---command: +---`:=vim.treesitter.get_parser():lang()` +--- +---Or the injected language for a specific location: +---`:=vim.treesitter.get_parser():language_for_range({ line, col, line, col }):lang())` +--- +---@alias ts_context_commentstring.LanguagesConfig table + +---@class ts_context_commentstring.CommentaryConfig +---@field Commentary string | false | nil +---@field CommentaryLine string | false | nil +---@field ChangeCommentary string | false | nil +---@field CommentaryUndo string | false | nil + +---@class ts_context_commentstring.Config +---@field enable_autocmd boolean +---@field custom_calculation? fun(node: TSNode, language_tree: LanguageTree): string +---@field languages ts_context_commentstring.LanguagesConfig +---@field config ts_context_commentstring.LanguagesConfig +---@field commentary_integration ts_context_commentstring.CommentaryConfig + +---@type ts_context_commentstring.Config +M.config = { + -- Whether to update the `commentstring` on the `CursorHold` autocmd + enable_autocmd = true, + + -- Custom logic for calculating the commentstring. + custom_calculation = nil, + + -- Keybindings to use for the commentary.nvim integration + commentary_integration = { + Commentary = 'gc', + CommentaryLine = 'gcc', + ChangeCommentary = 'cgc', + CommentaryUndo = 'gcu', + }, + + languages = { + -- Languages that have a single comment style + astro = '', + c = { __default = '// %s', __multiline = '/* %s */' }, + cpp = { __default = '// %s', __multiline = '/* %s */' }, + css = '/* %s */', + cue = '// %s', + gleam = '// %s', + glimmer = '{{! %s }}', + go = { __default = '// %s', __multiline = '/* %s */' }, + graphql = '# %s', + haskell = '-- %s', + handlebars = '{{! %s }}', + hcl = { __default = '# %s', __multiline = '/* %s */' }, + html = '', + ini = '; %s', + lua = { __default = '-- %s', __multiline = '--[[ %s ]]' }, + nix = { __default = '# %s', __multiline = '/* %s */' }, + php = { __default = '// %s', __multiline = '/* %s */' }, + python = { __default = '# %s', __multiline = '""" %s """' }, + rego = '# %s', + rescript = { __default = '// %s', __multiline = '/* %s */' }, + scss = { __default = '// %s', __multiline = '/* %s */' }, + sh = '# %s', + bash = '# %s', + solidity = { __default = '// %s', __multiline = '/* %s */' }, + sql = '-- %s', + svelte = '', + terraform = { __default = '# %s', __multiline = '/* %s */' }, + twig = '{# %s #}', + typescript = { __default = '// %s', __multiline = '/* %s */' }, + vim = '" %s', + vue = '', + zsh = '# %s', + kotlin = { __default = '// %s', __multiline = '/* %s */' }, + roc = '# %s', + + -- Languages that can have multiple types of comments + tsx = { + __default = '// %s', + __multiline = '/* %s */', + jsx_element = '{/* %s */}', + jsx_fragment = '{/* %s */}', + jsx_attribute = { __default = '// %s', __multiline = '/* %s */' }, + comment = { __default = '// %s', __multiline = '/* %s */' }, + call_expression = { __default = '// %s', __multiline = '/* %s */' }, + statement_block = { __default = '// %s', __multiline = '/* %s */' }, + spread_element = { __default = '// %s', __multiline = '/* %s */' }, + }, + }, + + ---@deprecated Use the languages configuration instead! + config = {}, +} + +M.config.languages.javascript = M.config.languages.tsx + +---@param config? ts_context_commentstring.Config +function M.update(config) + M.config = vim.tbl_deep_extend('force', M.config, config or {}) +end + +---@return boolean +function M.is_autocmd_enabled() + if vim.g.loaded_commentary == 1 then + return false + end + + local enable_autocmd = M.config.enable_autocmd + return enable_autocmd == nil and true or enable_autocmd +end + +---@return ts_context_commentstring.LanguagesConfig +function M.get_languages_config() + return vim.tbl_deep_extend('force', M.config.languages, M.config.config) +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/integrations/comment_nvim.lua b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/integrations/comment_nvim.lua new file mode 100644 index 00000000..2db6c313 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/integrations/comment_nvim.lua @@ -0,0 +1,48 @@ +local M = {} + +---Use this function to get a pre_hook function that can be used when +---configuring Comment.nvim. +---https://github.com/numToStr/Comment.nvim/ +--- +---Example usage: +---```lua +---require('Comment').setup { +--- pre_hook = require('ts_context_commentstring.integrations.comment_nvim').create_pre_hook(), +---} +---``` +--- +---Feel free to copy this function into your own configuration if you need to +---make any changes (or contribute the improvements back into this plugin). +--- +---This is a higher order function in case we want to add any extra +---configuration for the hook in the future. +--- +---@return fun(ctx: CommentCtx): string|nil +function M.create_pre_hook() + ---@param ctx CommentCtx + ---@return string|nil + return function(ctx) + local U = require 'Comment.utils' + + -- Determine whether to use linewise or blockwise commentstring + local type = ctx.ctype == U.ctype.linewise and '__default' or '__multiline' + + -- Determine the location where to calculate commentstring from + local location = nil + if ctx.ctype == U.ctype.blockwise then + location = { + ctx.range.srow - 1, + ctx.range.scol, + } + elseif ctx.cmotion == U.cmotion.v or ctx.cmotion == U.cmotion.V then + location = require('ts_context_commentstring.utils').get_visual_start_location() + end + + return require('ts_context_commentstring').calculate_commentstring { + key = type, + location = location, + } + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/integrations/vim_commentary.lua b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/integrations/vim_commentary.lua new file mode 100644 index 00000000..f39e1fc0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/integrations/vim_commentary.lua @@ -0,0 +1,47 @@ +local gmap = vim.api.nvim_set_keymap +local bmap = vim.api.nvim_buf_set_keymap + +for _, mode in ipairs { 'n', 'x', 'o' } do + gmap( + mode, + 'ContextCommentary', + [[v:lua.context_commentstring.update_commentstring_and_run('Commentary')]], + { expr = true } + ) +end +gmap( + 'n', + 'ContextCommentaryLine', + [[v:lua.context_commentstring.update_commentstring_and_run('CommentaryLine')]], + { expr = true } +) +gmap( + 'n', + 'ContextChangeCommentary', + [[v:lua.context_commentstring.update_commentstring_and_run('ChangeCommentary')]], + { expr = true } +) + +local M = {} + +---Set up vim-commentary mappings to first update the commentstring, and then +---run vim-commentary +---@param maps ts_context_commentstring.CommentaryConfig +function M.set_up_maps(maps) + if maps.Commentary then + for _, mode in ipairs { 'n', 'x', 'o' } do + bmap(0, mode, maps.Commentary, 'ContextCommentary', {}) + end + end + if maps.CommentaryLine then + bmap(0, 'n', maps.CommentaryLine, 'ContextCommentaryLine', {}) + end + if maps.ChangeCommentary then + bmap(0, 'n', maps.ChangeCommentary, 'ContextChangeCommentary', {}) + end + if maps.CommentaryUndo then + bmap(0, 'n', maps.CommentaryUndo, 'ContextCommentaryCommentary', {}) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/internal.lua b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/internal.lua new file mode 100644 index 00000000..da274b52 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/internal.lua @@ -0,0 +1,163 @@ +local api = vim.api + +local utils = require 'ts_context_commentstring.utils' +local config = require 'ts_context_commentstring.config' + +local M = {} + +---Initialize the plugin in the buffer +---@param bufnr number +function M.setup_buffer(bufnr) + if not utils.is_treesitter_active(bufnr) then + return + end + + -- Save the original commentstring so that it can be restored later if there + -- is no match + api.nvim_buf_set_var(bufnr, 'ts_original_commentstring', api.nvim_buf_get_option(bufnr, 'commentstring')) + + local enable_autocmd = config.is_autocmd_enabled() + + -- If vim-commentary is installed, set up mappings for it + if vim.g.loaded_commentary == 1 then + require('ts_context_commentstring.integrations.vim_commentary').set_up_maps(config.config.commentary_integration) + end + + if enable_autocmd then + local group = api.nvim_create_augroup('context_commentstring_ft', { clear = true }) + api.nvim_create_autocmd('CursorHold', { + buffer = bufnr, + group = group, + desc = 'Change the commentstring on cursor hold using Treesitter', + callback = function() + require('ts_context_commentstring').update_commentstring() + end, + }) + end +end + +---@class ts_context_commentstring.Args +---@field key string Key to prefer to be returned from ts_context_commentstring.CommentConfigMultiple +---@field location ts_context_commentstring.Location + +---Calculate the commentstring based on the current location of the cursor. +--- +---**Note:** We should treat this function like a public API, try not to break +---it! +--- +---@param args? ts_context_commentstring.Args +--- +---@return string | nil commentstring If found, otherwise `nil` +function M.calculate_commentstring(args) + args = args or {} + local key = args.key or '__default' + local location = args.location or nil + + local node, language_tree = + utils.get_node_at_cursor_start_of_line(vim.tbl_keys(config.get_languages_config()), location) + + if not node and not language_tree then + return nil + end + + local custom_calculation = config.config.custom_calculation + if custom_calculation then + local commentstring = custom_calculation(node, language_tree) + if commentstring then + return commentstring + end + end + + local language = language_tree:lang() + local language_config = config.get_languages_config()[language] + + return M.check_node(node, language_config, key) +end + +---Update the `commentstring` setting based on the current location of the +---cursor. If no `commentstring` can be calculated, will revert to the ofiginal +---`commentstring` for the current file. +--- +---**Note:** We should treat this function like a public API, try not to break +---it! +--- +---@param args? ts_context_commentstring.Args +function M.update_commentstring(args) + local found_commentstring = M.calculate_commentstring(args) + + if found_commentstring then + api.nvim_buf_set_option(0, 'commentstring', found_commentstring) + else + -- No commentstring was found, default to the default for this buffer + local original_commentstring = vim.b.ts_original_commentstring + if original_commentstring then + api.nvim_buf_set_option(0, 'commentstring', vim.b.ts_original_commentstring) + end + end +end + +---Check if the given node matches any of the given types. If not, recursively +---check its parent node. +--- +---@param node table +---@param language_config ts_context_commentstring.LanguageConfig +---@param commentstring_key string +--- +---@return string | nil +function M.check_node(node, language_config, commentstring_key) + commentstring_key = commentstring_key or '__default' + + -- There is no commentstring configuration for this language, use the + -- `ts_original_commentstring` + if not language_config then + return nil + end + + -- There is no node, we have reached the top-most node, use the default + -- commentstring from language config + if not node then + return language_config[commentstring_key] or language_config.__default or language_config + end + + local node_type = node:type() + local match = language_config[node_type] + + if match then + return match[commentstring_key] or match.__default or match + end + + -- Recursively check the parent node + return M.check_node(node:parent(), language_config, commentstring_key) +end + +---@deprecated +function M.attach() + vim.deprecate( + 'context_commentstring nvim-treesitter module', + "require('ts_context_commentstring').setup {} and set vim.g.skip_ts_context_commentstring_module = true to speed up loading", + 'in the future (see https://github.com/JoosepAlviste/nvim-ts-context-commentstring/issues/82 for more info)', + 'ts_context_commentstring' + ) + config.update(require('nvim-treesitter.configs').get_module 'context_commentstring') +end + +---@deprecated +function M.detach() end + +_G.context_commentstring = {} + +---Trigger re-calculation of the `commentstring` and trigger the given +---mapping right after that. +--- +---This is in the global scope because +---`v:lua.require('ts_context_commentstring')` does not work for some reason. +--- +---@param mapping string The Plug mapping to execute +--- +---@return string +function _G.context_commentstring.update_commentstring_and_run(mapping) + M.update_commentstring() + return api.nvim_replace_termcodes('' .. mapping, true, true, true) +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/utils.lua b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/utils.lua new file mode 100644 index 00000000..748a44ae --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/lua/ts_context_commentstring/utils.lua @@ -0,0 +1,111 @@ +local api = vim.api +local fn = vim.fn + +---Coordinates for a location. Both the line and the column are 0-indexed (e.g., +---line nr 10 is line 9, the first column is 0). +---@alias ts_context_commentstring.Location number[] 2-tuple of (line, column) + +local M = {} + +---Get the location of the cursor to be used to get the treesitter node +---function. +--- +---@return ts_context_commentstring.Location +function M.get_cursor_location() + local cursor = vim.api.nvim_win_get_cursor(0) + return { cursor[1] - 1, cursor[2] } +end + +---Get the location of the cursor line first non-blank character. +--- +---@return ts_context_commentstring.Location +function M.get_cursor_line_non_whitespace_col_location() + local cursor = api.nvim_win_get_cursor(0) + local first_non_whitespace_col = fn.match(fn.getline '.', '\\S') + + return { + cursor[1] - 1, + first_non_whitespace_col, + } +end + +---Get the location of the visual selection start. +--- +---@return ts_context_commentstring.Location +function M.get_visual_start_location() + local first_non_whitespace_col = fn.match(fn.getline '.', '\\S') + + return { + vim.fn.getpos("'<")[2] - 1, + first_non_whitespace_col, + } +end + +---Get the location of the visual selection end. +--- +---@return ts_context_commentstring.Location +function M.get_visual_end_location() + return { + vim.fn.getpos("'>")[2] - 1, + vim.fn.getpos("'>")[3] - 1, + } +end + +---@return boolean +---@param bufnr? number +function M.is_treesitter_active(bufnr) + bufnr = bufnr or 0 + + -- get_parser will throw an error if Treesitter is not set up for the buffer + local ok, _ = pcall(vim.treesitter.get_parser, bufnr) + + return ok +end + +---Get the node that is on the given location (default first non-whitespace +---character of the cursor line). This also handles injected languages via +---language tree. +--- +---For example, if the cursor is at "|": +--- |
+--- +---then will return the
node, even though it isn't at the cursor position +--- +---Returns the node at the cursor's line and the language tree for that +---injection. +--- +---@param only_languages string[] List of languages to filter for, all +--- other languages will be ignored. +---@param location? ts_context_commentstring.Location location Line, column +--- where to start traversing the tree. Defaults to cursor start of line. +--- This usually makes the most sense when commenting the whole line. +--- +---@return table|nil node, table|nil language_tree Node and language tree for the +--- location +function M.get_node_at_cursor_start_of_line(only_languages, location) + if not M.is_treesitter_active() then + return + end + + location = location or M.get_cursor_line_non_whitespace_col_location() + local range = { + location[1], + location[2], + location[1], + location[2], + } + + -- default to top level language tree + local language_tree = vim.treesitter.get_parser() + -- Get the smallest supported language's tree with nodes inside the given range + language_tree:for_each_tree(function(_, ltree) + if ltree:contains(range) and vim.tbl_contains(only_languages, ltree:lang()) then + language_tree = ltree + end + end) + + local node = language_tree:named_node_for_range(range) + return node, language_tree +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/plugin/ts_context_commentstring.lua b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/plugin/ts_context_commentstring.lua new file mode 100644 index 00000000..d2513b62 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/plugin/ts_context_commentstring.lua @@ -0,0 +1,30 @@ +if vim.g.loaded_ts_context_commentstring and vim.g.loaded_ts_context_commentstring ~= 0 then + return +end + +vim.g.loaded_ts_context_commentstring = 1 + +local group = vim.api.nvim_create_augroup('ts_context_commentstring', { clear = true }) +vim.api.nvim_create_autocmd('FileType', { + group = group, + desc = 'Set up nvim-ts-context-commentstring for each buffer that has Treesitter active', + callback = function(args) + require('ts_context_commentstring.internal').setup_buffer(args.buf) + end, +}) + +if not vim.g.skip_ts_context_commentstring_module or vim.g.skip_ts_context_commentstring_module == 0 then + local nvim_ts_ok, nvim_ts = pcall(require, 'nvim-treesitter') + if nvim_ts_ok then + if not nvim_ts.define_modules then + -- Running nvim-treesitter >= 1.0, modules are no longer a thing + return + end + + nvim_ts.define_modules { + context_commentstring = { + module_path = 'ts_context_commentstring.internal', + }, + } + end +end diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/stylua.toml b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/stylua.toml new file mode 100644 index 00000000..2cf0ca64 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/stylua.toml @@ -0,0 +1,4 @@ +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferSingle" +no_call_parentheses = true diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/utils/minimal_init.lua b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/utils/minimal_init.lua new file mode 100644 index 00000000..cc5c14df --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/utils/minimal_init.lua @@ -0,0 +1,48 @@ +-- Install lazy.nvim automatically +local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim' +if not vim.loop.fs_stat(lazypath) then + vim.fn.system { + 'git', + 'clone', + '--filter=blob:none', + 'https://github.com/folke/lazy.nvim.git', + '--branch=stable', -- latest stable release + lazypath, + } +end +vim.opt.rtp:prepend(lazypath) + +-- Or some other small value (Vim default is 4000) +vim.opt.updatetime = 100 + +require('lazy').setup { + 'JoosepAlviste/nvim-ts-context-commentstring', + { + 'nvim-treesitter/nvim-treesitter', + build = ':TSUpdate', + config = function() + require('nvim-treesitter.configs').setup { + ensure_installed = { 'vim', 'lua' }, + highlight = { + enable = true, + }, + } + end, + }, + { + 'numToStr/Comment.nvim', + config = function() + require('Comment').setup { + pre_hook = function() + return vim.bo.commentstring + end, + } + end, + }, +} + +-- Try commenting the following vimscript in and out with `gcc`, it should be +-- commented with a double quote character +vim.cmd [[ +echo 'Hello World!' +]] diff --git a/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/utils/run_minimal.sh b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/utils/run_minimal.sh new file mode 100755 index 00000000..47876637 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ts-context-commentstring/utils/run_minimal.sh @@ -0,0 +1,17 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/bash + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +export XDG_CONFIG_HOME="${SCRIPT_DIR}/.testenv/config" +export XDG_DATA_HOME="${SCRIPT_DIR}/.testenv/data" +export XDG_STATE_HOME="${SCRIPT_DIR}/.testenv/state" +export XDG_RUNTIME_DIR="${SCRIPT_DIR}/.testenv/run" +export XDG_CACHE_HOME="${SCRIPT_DIR}/.testenv/cache" + +mkdir -p "${XDG_CONFIG_HOME}/nvim" +mkdir -p "${XDG_DATA_HOME}/nvim" +mkdir -p "${XDG_STATE_HOME}/nvim" +mkdir -p "${XDG_RUNTIME_DIR}/nvim" +mkdir -p "${XDG_CACHE_HOME}/nvim" + +nvim -u "${SCRIPT_DIR}/minimal_init.lua" "${SCRIPT_DIR}/minimal_init.lua" diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/.editorconfig b/config/neovim/store/lazy-plugins/nvim-ufo/.editorconfig new file mode 100644 index 00000000..1666f0d3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/.editorconfig @@ -0,0 +1,22 @@ +[*] +indent_style = space +indent_size = 4 +tab_width = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true + +[*.lua] +align_continuous_assign_statement = false +space_around_table_field_list = false +align_call_args = false +quote_style = single + +[*.json,*.jsonc] +indent_style = tab + +[*.js] +indent_style = tab + +[{Makefile,**.mk}] +indent_style = tab diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/.github/ISSUE_TEMPLATE/bug_report.yml b/config/neovim/store/lazy-plugins/nvim-ufo/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..26b7a78b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,65 @@ +name: Bug Report +description: File a bug report +labels: [bug] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: input + attributes: + label: 'Neovim version (nvim -v | head -n1)' + placeholder: 'NVIM v0.7.0' + validations: + required: true + - type: input + attributes: + label: 'Operating system/version' + placeholder: 'macOS 11.5' + validations: + required: true + - type: textarea + attributes: + label: 'How to reproduce the issue' + description: | + How do you trigger this bug? Please walk us through it step by step. + Log path: `~/.cache/nvim/ufo.log` + value: | + `cat mini.lua` + ```lua + -- Use Vim packages install the plugin, also work with some plugins manager such as packer.nvim + vim.o.packpath = '~/.local/share/nvim/site' + vim.cmd('packadd promise-async') + vim.cmd('packadd nvim-ufo') + + -- Setting + vim.o.foldcolumn = '1' + vim.o.foldlevel = 99 + vim.o.foldlevelstart = -1 + vim.o.foldenable = true + + local ufo = require('ufo') + ufo.setup() + vim.keymap.set('n', 'zR', ufo.openAllFolds) + vim.keymap.set('n', 'zM', ufo.closeAllFolds) + ``` + `nvim --clean +'so mini.lua'` + + 1. + 2. + 3. + ... + validations: + required: true + + - type: textarea + attributes: + label: 'Expected behavior' + description: 'Describe the behavior you expect. May include logs, images, or videos.' + validations: + required: true + - type: textarea + attributes: + label: 'Actual behavior' + validations: + required: true diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/.github/ISSUE_TEMPLATE/config.yml b/config/neovim/store/lazy-plugins/nvim-ufo/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..3ba13e0c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/.github/ISSUE_TEMPLATE/feature_request.yml b/config/neovim/store/lazy-plugins/nvim-ufo/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..210fa2a0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,23 @@ +name: Feature request +description: Request an enhancement for nvim-ufo +labels: [enhancement] +body: + - type: markdown + attributes: + value: | + Before requesting: search [existing issues](https://github.com/kevinhwang91/nvim-ufo/labels/enhancement). + - type: textarea + attributes: + label: "Feature description" + validations: + required: true + - type: textarea + attributes: + label: "Describe the solution you'd like" + validations: + required: true + - type: textarea + attributes: + label: "Additional context" + validations: + required: false diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/.github/workflows/luarocks.yml b/config/neovim/store/lazy-plugins/nvim-ufo/.github/workflows/luarocks.yml new file mode 100644 index 00000000..2e7597a9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/.github/workflows/luarocks.yml @@ -0,0 +1,31 @@ +--- +name: Push to Luarocks + +on: + push: + tags: + - '*' + release: + types: + - created + tags: + - '*' + workflow_dispatch: + +jobs: + luarocks-upload: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Required to count the commits + - name: Get Version + run: echo "LUAROCKS_VERSION=$(git describe --abbrev=0 --tags)" >> $GITHUB_ENV + - name: LuaRocks Upload + uses: nvim-neorocks/luarocks-tag-release@v5 + env: + LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }} + with: + version: ${{ env.LUAROCKS_VERSION }} + dependencies: | + promise-async diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/.gitignore b/config/neovim/store/lazy-plugins/nvim-ufo/.gitignore new file mode 100644 index 00000000..aa643854 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/.gitignore @@ -0,0 +1,2 @@ +doc/tags +build diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/.luarc.jsonc b/config/neovim/store/lazy-plugins/nvim-ufo/.luarc.jsonc new file mode 100644 index 00000000..3d35ee88 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/.luarc.jsonc @@ -0,0 +1,29 @@ +{ + "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", + "completion.callSnippet": "Replace", + "completion.displayContext": 50, + "completion.keywordSnippet": "Disable", + "completion.postfix": ".", + "diagnostics.libraryFiles": "Disable", + "diagnostics.disable": [ + "different-requires", + "param-type-mismatch", + "assign-type-mismatch" + ], + "diagnostics.globals": [ + "jit", + "it", + "describe", + "before_each", + "after_each", + "setup", + "teardown" + ], + "runtime.version": "LuaJIT", + "type.castNumberToInteger": true, + "type.weakUnionCheck": true, + "workspace.library": [ + "$VIM/runtime/lua", + "$VIM/site/pack/packer/start/promise-async/typings" + ] +} diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/CHANGELOG.md b/config/neovim/store/lazy-plugins/nvim-ufo/CHANGELOG.md new file mode 100644 index 00000000..465548f6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/CHANGELOG.md @@ -0,0 +1,180 @@ +# Changelog + +## [1.4.0] - 2024-04-03 + +### 🚀 Features + +- *(preview)* Add `jumpTop` and `jumpBot` keymap actions (#109) +- *(highlight)* Add `UfoCursorFoldedLine` (#103) +- *(render)* Support inlay (#155) +- *(render)* Add support for concealed characters (#153) (#156) +- *(api)* Add cursor range and kind information for `UfoInspect` +- *(config)* [**breaking**] Use `close_fold_kinds_for_ft` instead `close_fold_kinds` +- *(decorator)* Export fold kind in `fold_virt_text_handler` (#207) +- *(build)* Luarocks support (#211) + +### 🐛 Bug Fixes + +- *(preview)* Respect `tabstop` and `shiftwidth` opts +- *(provider)* Respect 'tabstop' and 'shiftwidth' for indent +- *(decorator)* Reset winhl after detach +- *(decorator)* Keep last winid field +- *(driver)* Respect `foldminlines` (#108) +- *(decorator)* Buffer may be changed in a window +- *(decorator)* `setl winhl` erase hl of `nvim_win_set_hl_ns` (#111) +- *(preview)* Dispose preview window even if buffer is wiped out +- *(buffer)* Quickfix buftype can't detect line changed +- *(decorator)* Open fold should redraw at once (#132) +- *(treesitter)* Support `#make-range!` (#139) +- *(preview)* Window height should more than zero +- *(fold)* Refresh fb table in closure function +- Throw UfoFallbackException on RequestFailed (#159) +- *(render)* Join text for default hlgroup (#163) +- *(render)* Skip error return by `synID` +- *(fold)* Sync extmarks with foldedLines (#167) +- *(treesitter)* Use metadata.range prefer (#169) +- *(window)* Clear win highlight if buf changed +- *(decorator)* Ignore redraw request for closing fold (#176) +- *(decorator)* Ignore redundant redraw (#180,#181) +- *(fold)* Scan win folds if one buffer in multiple window +- *(decorator)* Correct bufnrSet logic +- *(window)* Don't clear winhl during first render (#183) +- *(render)* Replace `Normal` highlight with `UfoFoldedFg` +- *(action)* Check endLnum to avoid infinite loop (#184) +- *(decorator)* Highlight open fold for multiple windows correctly (#187) +- *(decorator)* Erase extmark even in multiple windows +- *(decorator)* Narrow the fold range for stale +- *(treesitter)* Fix errors when getting hlId on nvim 0.10.x (#188) +- *(model)* Use private field to avoid inherit (#186) +- *(fold)* Don't make scan flag if manual invoke (#192) +- *(window)* Upstream bug, `set winhl` change curswant (#194) +- *(preview)* Nightly change `nvim_win_get_config` return val +- *(wffi)* `changed_window_setting` signature changed +- *(decorator)* Keep silent for `Keyboard interrupt` error (#202) +- *(decorator)* Correct capture condition +- *(fold)* Return correct winid + +### ⚡ Performance + +- *(decorator)* Skip rendering of horizontal movement +- *(decorator)* `set winhl` will redraw all lines + +## [1.3.0] - 2023-01-05 + +### Features + +#### Provider + +- Use fallback if `buftype == 'nofile'` +- Inspect current fold kinds + +### Bug Fixes + +#### Preview + +- Respect target buffer opts +- Stick to top left corner while scrolling in normal window +- Fix wrong row for upward display + +#### Fold + +- Window maybe changed before set opts +- Improve leaving diff mode behavior + +#### Miscellaneous + +- Substitute NUL byte for VimScript func +- Catch coc.nvim `Plugin not ready` error and resolve + +### Documentation + +- Explain `fold_virt_text_handler` (#98) +- Make capabilities for all available lsp servers & remove "other_fields" (#100) + +## [1.2.0] - 2022-10-09 + +### Features + +#### Fold + +- Add `close_fold_kinds` option +- Make the window display upward if `kind == 'comment'` (#73) + +#### API + +- Add `applyFolds` +- Add `openFoldsExceptKinds` (#64) + +#### Preview + +- Support highlighting with `:match` +- Show virtual winbar if preview is scrolled and export `UfoPreviewWinbar` highlight group +- Highlight cursor line for preview and export `UfoPreviewCursorLine` highlight group + +#### Decorator + +- Hint error for users' virtTextHandler (#79) +- Add `enable_get_fold_virt_text` option to get virt texts of all folded lines (#74) + +### !Breaking + +- `enable_fold_end_virt_text` option is deprecated, use `enable_get_fold_virt_text` instead +- The signature of `peekFoldedLinesUnderCursor` API is changed + +### Bug Fixes + +#### Fold + +- Handle multiple windows with same buffers +- `set foldenable` forecdly after leaving diff mode +- Restore topline after first applying folds to keep eyes comfortable +- EndLnum may exceed buffer line count because of the asynchronization + +#### API + +- Action should work after detach (#75) + +#### Preview + +- Dispose previous resources before a new attach +- Scroll bar reaches the bottom until the end of the line is visible + +#### Provider + +- Need more time to wait for the server +- Better bypass strategy, must reach the timeout and a certain number of requests +- Lsp provider always returns Promise object +- Validate buffer after requesting folds +- Dispose all providers properly + +#### Decorator + +- Stop highlighting after opening folds during incsearch +- Keep refreshing even if nofoldenable + +#### Render + +- Limit the end of range +- Treesitter extmarks may be overlapped, filter invalid extmarks out + +## [1.1.0] - 2022-08-13 + +### Bug Fixes + +- Reset foldlines if extmark range is backward +- Unexpected fired `on_lines` at nvim_buf_attach +- Fix `winsaveview()` for scanning fold ranges +- Always open folds if text content in range (#60) +- Scroll bar shouldn't be filled fully if it's scrollable +- Drop coc.nvim cancellation +- Filter out last same ranges +- Assert `provider_selector` return value (#61) + +### Features + +- Add `closeFoldsWith` API (#62) +- Truncate top border for preview if possible + +## [1.0.0] - 2022-07-24 + +First release with 1.0.0 version. diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/LICENSE b/config/neovim/store/lazy-plugins/nvim-ufo/LICENSE new file mode 100644 index 00000000..c3c56e57 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2022-2023, kevinhwang91 +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/Makefile b/config/neovim/store/lazy-plugins/nvim-ufo/Makefile new file mode 100644 index 00000000..0b9c2042 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/Makefile @@ -0,0 +1,18 @@ +SHELL := /bin/bash +DEPS ?= build + +LUA_LS ?= $(DEPS)/lua-language-server +LINT_LEVEL ?= Information + +all: + +lint: + @rm -rf $(LUA_LS) + @mkdir -p $(LUA_LS) + @VIM=$(HOME)/.local/share/nvim lua-language-server --check $(PWD) --checklevel=$(LINT_LEVEL) --logpath=$(LUA_LS) + @[[ -f $(LUA_LS)/check.json ]] && { cat $(LUA_LS)/check.json 2>/dev/null; exit 1; } || true + +clean: + rm -rf $(DEPS) + +.PHONY: all deps clean lint diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/README.md b/config/neovim/store/lazy-plugins/nvim-ufo/README.md new file mode 100644 index 00000000..1fff7f1a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/README.md @@ -0,0 +1,385 @@ +# nvim-ufo + +The goal of nvim-ufo is to make Neovim's fold look modern and keep high performance. + + + +> [setup foldcolumn like demo](https://github.com/kevinhwang91/nvim-ufo/issues/4) + +--- + +## Table of contents + +- [Table of contents](#table-of-contents) +- [Features](#features) +- [Quickstart](#quickstart) + - [Requirements](#requirements) + - [Installation](#installation) + - [Minimal configuration](#minimal-configuration) + - [Usage](#usage) +- [Documentation](#documentation) + - [How does nvim-ufo get the folds?](#how-does-nvim-ufo-get-the-folds) + - [Setup and description](#setup-and-description) + - [Preview function table](#preview-function-table) + - [Commands](#commands) + - [API](#api) + - [Highlight groups](#highlight-groups) +- [Advanced configuration](#advanced-configuration) + - [Customize configuration](#customize-configuration) + - [Customize fold text](#customize-fold-text) +- [Feedback](#feedback) +- [License](#license) + +## Features + +- Penetrate color for folded lines like other modern editors/IDEs +- Never block Neovim +- Adding folds high accuracy with Folding Range in LSP +- Support fallback and customize strategy for fold provider +- Peek folded line and jump the desired location with less redraw + +## Quickstart + +### Requirements + +- [Neovim](https://github.com/neovim/neovim) 0.7.2 or later +- [coc.nvim](https://github.com/neoclide/coc.nvim) (optional) + +### Installation + +Install with [Packer.nvim](https://github.com/wbthomason/packer.nvim): + +```lua +use {'kevinhwang91/nvim-ufo', requires = 'kevinhwang91/promise-async'} +``` + +### Minimal configuration + +```lua +use {'kevinhwang91/nvim-ufo', requires = 'kevinhwang91/promise-async'} + +vim.o.foldcolumn = '1' -- '0' is not bad +vim.o.foldlevel = 99 -- Using ufo provider need a large value, feel free to decrease the value +vim.o.foldlevelstart = 99 +vim.o.foldenable = true + +-- Using ufo provider need remap `zR` and `zM`. If Neovim is 0.6.1, remap yourself +vim.keymap.set('n', 'zR', require('ufo').openAllFolds) +vim.keymap.set('n', 'zM', require('ufo').closeAllFolds) + +-- Option 1: coc.nvim as LSP client +use {'neoclide/coc.nvim', branch = 'master', run = 'yarn install --frozen-lockfile'} +require('ufo').setup() +-- + +-- Option 2: nvim lsp as LSP client +-- Tell the server the capability of foldingRange, +-- Neovim hasn't added foldingRange to default capabilities, users must add it manually +local capabilities = vim.lsp.protocol.make_client_capabilities() +capabilities.textDocument.foldingRange = { + dynamicRegistration = false, + lineFoldingOnly = true +} +local language_servers = require("lspconfig").util.available_servers() -- or list servers manually like {'gopls', 'clangd'} +for _, ls in ipairs(language_servers) do + require('lspconfig')[ls].setup({ + capabilities = capabilities + -- you can add other fields for setting up lsp server in this table + }) +end +require('ufo').setup() +-- + +-- Option 3: treesitter as a main provider instead +-- (Note: the `nvim-treesitter` plugin is *not* needed.) +-- ufo uses the same query files for folding (queries//folds.scm) +-- performance and stability are better than `foldmethod=nvim_treesitter#foldexpr()` +require('ufo').setup({ + provider_selector = function(bufnr, filetype, buftype) + return {'treesitter', 'indent'} + end +}) +-- + +-- Option 4: disable all providers for all buffers +-- Not recommend, AFAIK, the ufo's providers are the best performance in Neovim +require('ufo').setup({ + provider_selector = function(bufnr, filetype, buftype) + return '' + end +}) +``` + +### Usage + +Use fold as usual. + +Using a provider of ufo, must set a large value for `foldlevel`, this is the limitation of +`foldmethod=manual`. A small value may close fold automatically if the fold ranges updated. + +After running `zR` and `zM` normal commands will change the `foldlevel`, ufo provide the APIs +`openAllFolds`/`closeAllFolds` to open/close all folds but keep `foldlevel` value, need to remap +them. + +Like `zR` and `zM`, if you used `zr` and `zm` before, please use `closeFoldsWith` API to close folds +like `set foldlevel=n` but keep `foldlevel` value. + +## Documentation + +### How does nvim-ufo get the folds? + +If ufo detect `foldmethod` option is not `diff` or `marker`, it will request the providers to get +the folds, the request strategy is formed by the main and the fallback. The default value of main is +`lsp` and the default value of fallback is `indent` which implemented by ufo. + +For example, Changing the text in a buffer will request the providers for folds. + +> `foldmethod` option will finally become `manual` if ufo is working. + +### Setup and description + +```lua +{ + open_fold_hl_timeout = { + description = [[Time in millisecond between the range to be highlgihted and to be cleared + while opening the folded line, `0` value will disable the highlight]], + default = 400 + }, + provider_selector = { + description = [[A function as a selector for fold providers. For now, there are + 'lsp' and 'treesitter' as main provider, 'indent' as fallback provider]], + default = nil + }, + close_fold_kinds_for_ft = { + description = [[After the buffer is displayed (opened for the first time), close the + folds whose range with `kind` field is included in this option. For now, + 'lsp' provider's standardized kinds are 'comment', 'imports' and 'region'. + This option is a table with filetype as key and fold kinds as value. Use a + default value if value of filetype is absent. + Run `UfoInspect` for details if your provider has extended the kinds.]], + default = {default = {}} + }, + fold_virt_text_handler = { + description = [[A function customize fold virt text, see ### Customize fold text]], + default = nil + }, + enable_get_fold_virt_text = { + description = [[Enable a function with `lnum` as a parameter to capture the virtual text + for the folded lines and export the function to `get_fold_virt_text` field of + ctx table as 6th parameter in `fold_virt_text_handler`]], + default = false + }, + preview = { + description = [[Configure the options for preview window and remap the keys for current + buffer and preview buffer if the preview window is displayed. + Never worry about the users's keymaps are overridden by ufo, ufo will save + them and restore them if preview window is closed.]], + win_config = { + border = { + description = [[The border for preview window, + `:h nvim_open_win() | call search('border:')`]], + default = 'rounded', + }, + winblend = { + description = [[The winblend for preview window, `:h winblend`]], + default = 12, + }, + winhighlight = { + description = [[The winhighlight for preview window, `:h winhighlight`]], + default = 'Normal:Normal', + }, + maxheight = { + description = [[The max height of preview window]], + default = 20, + } + }, + mappings = { + description = [[The table for {function = key}]], + default = [[see ###Preview function table for detail]], + } + } +} +``` + +`:h ufo` may help you to get the all default configuration. + +### Preview function table + + + +| Function | Action | Def Key | +| -------- | ---------------------------------------------------------------------------------------------- | ------- | +| scrollB | Type `CTRL-B` in preview window | | +| scrollF | Type `CTRL-F` in preview window | | +| scrollU | Type `CTRL-U` in preview window | | +| scrollD | Type `CTRL-D` in preview window | | +| scrollE | Type `CTRL-E` in preview window | `` | +| scrollY | Type `CTRL-Y` in preview window | `` | +| jumpTop | Jump to top region in preview window | | +| jumpBot | Jump to bottom region in preview window | | +| close | In normal window: Close preview window
In preview window: Close preview window | `q` | +| switch | In normal window: Go to preview window
In preview window: Go to normal window | `` | +| trace | In normal window: Trace code based on topline
In preview window: Trace code based on cursor | `` | + + + +Additional mouse supported: + +1. `` and ``: Scroll preview window. +2. `<2-LeftMouse>`: Same as `trace` action in preview window. + +> `trace` action will open all fold for the folded lines + +### Commands + +| Command | Description | +| -------------- | -------------------------------------------------------------- | +| UfoEnable | Enable ufo | +| UfoDisable | Disable ufo | +| UfoInspect | Inspect current buffer information | +| UfoAttach | Attach current buffer to enable all features | +| UfoDetach | Detach current buffer to disable all features | +| UfoEnableFold | Enable to get folds and update them at once for current buffer | +| UfoDisableFold | Disable to get folds for current buffer | + +### API + +[ufo.lua](./lua/ufo.lua) + +### Highlight groups + +```vim +" hi default UfoFoldedFg guifg=Normal.foreground +" hi default UfoFoldedBg guibg=Folded.background +hi default link UfoPreviewSbar PmenuSbar +hi default link UfoPreviewThumb PmenuThumb +hi default link UfoPreviewWinBar UfoFoldedBg +hi default link UfoPreviewCursorLine Visual +hi default link UfoFoldedEllipsis Comment +hi default link UfoCursorFoldedLine CursorLine +``` + +- `UfoFoldedFg`: Foreground for raw text of folded line. +- `UfoFoldedBg`: Background of folded line. +- `UfoPreviewSbar`: Scroll bar of preview window, only take effect if the border is missing right + horizontal line, like `border = 'none'`. +- `UfoPreviewCursorLine`: Highlight current line in preview window if it isn't the start of folded + lines. +- `UfoPreviewWinBar`: Virtual winBar of preview window. +- `UfoPreviewThumb`: Thumb of preview window. +- `UfoFoldedEllipsis`: Ellipsis at the end of folded line, invalid if `fold_virt_text_handler` is + set. +- `UfoCursorFoldedLine`: Highlight the folded line under the cursor + +## Advanced configuration + +Configuration can be found at [example.lua](./doc/example.lua) + +### Customize configuration + +```lua +local ftMap = { + vim = 'indent', + python = {'indent'}, + git = '' +} +require('ufo').setup({ + open_fold_hl_timeout = 150, + close_fold_kinds_for_ft = { + default = {'imports', 'comment'}, + json = {'array'}, + c = {'comment', 'region'} + }, + preview = { + win_config = { + border = {'', '─', '', '', '', '─', '', ''}, + winhighlight = 'Normal:Folded', + winblend = 0 + }, + mappings = { + scrollU = '', + scrollD = '', + jumpTop = '[', + jumpBot = ']' + } + }, + provider_selector = function(bufnr, filetype, buftype) + -- if you prefer treesitter provider rather than lsp, + -- return ftMap[filetype] or {'treesitter', 'indent'} + return ftMap[filetype] + + -- refer to ./doc/example.lua for detail + end +}) +vim.keymap.set('n', 'zR', require('ufo').openAllFolds) +vim.keymap.set('n', 'zM', require('ufo').closeAllFolds) +vim.keymap.set('n', 'zr', require('ufo').openFoldsExceptKinds) +vim.keymap.set('n', 'zm', require('ufo').closeFoldsWith) -- closeAllFolds == closeFoldsWith(0) +vim.keymap.set('n', 'K', function() + local winid = require('ufo').peekFoldedLinesUnderCursor() + if not winid then + -- choose one of coc.nvim and nvim lsp + vim.fn.CocActionAsync('definitionHover') -- coc.nvim + vim.lsp.buf.hover() + end +end) +``` + +### Customize fold text + +Adding number suffix of folded lines instead of the default ellipsis, here is the example: + +

+ +

+ +```lua +local handler = function(virtText, lnum, endLnum, width, truncate) + local newVirtText = {} + local suffix = (' 󰁂 %d '):format(endLnum - lnum) + local sufWidth = vim.fn.strdisplaywidth(suffix) + local targetWidth = width - sufWidth + local curWidth = 0 + for _, chunk in ipairs(virtText) do + local chunkText = chunk[1] + local chunkWidth = vim.fn.strdisplaywidth(chunkText) + if targetWidth > curWidth + chunkWidth then + table.insert(newVirtText, chunk) + else + chunkText = truncate(chunkText, targetWidth - curWidth) + local hlGroup = chunk[2] + table.insert(newVirtText, {chunkText, hlGroup}) + chunkWidth = vim.fn.strdisplaywidth(chunkText) + -- str width returned from truncate() may less than 2nd argument, need padding + if curWidth + chunkWidth < targetWidth then + suffix = suffix .. (' '):rep(targetWidth - curWidth - chunkWidth) + end + break + end + curWidth = curWidth + chunkWidth + end + table.insert(newVirtText, {suffix, 'MoreMsg'}) + return newVirtText +end + +-- global handler +-- `handler` is the 2nd parameter of `setFoldVirtTextHandler`, +-- check out `./lua/ufo.lua` and search `setFoldVirtTextHandler` for detail. +require('ufo').setup({ + fold_virt_text_handler = handler +}) + +-- buffer scope handler +-- will override global handler if it is existed +-- local bufnr = vim.api.nvim_get_current_buf() +-- require('ufo').setFoldVirtTextHandler(bufnr, handler) +``` + +## Feedback + +- If you get an issue or come up with an awesome idea, don't hesitate to open an issue in github. +- If you think this plugin is useful or cool, consider rewarding it a star. + +## License + +The project is licensed under a BSD-3-clause license. See [LICENSE](./LICENSE) file for details. diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/coc-extension/index.js b/config/neovim/store/lazy-plugins/nvim-ufo/coc-extension/index.js new file mode 100644 index 00000000..88ae8c5f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/coc-extension/index.js @@ -0,0 +1,48 @@ +const { languages, commands, workspace, wait, CancellationTokenSource, Disposable } = require('coc.nvim') + + +exports.activate = async context => { + let { logger, subscriptions } = context + let nvim = workspace.nvim + subscriptions.push(commands.registerCommand('ufo.foldingRange', async (bufnr, kind) => { + let doc = workspace.getDocument(bufnr) + if (!doc || !doc.attached) { + await wait(50) + doc = workspace.getDocument(bufnr) + if (!doc || !doc.attached) { + return + } + } + let { textDocument } = doc + + // TODO + // no way to check whether server supports `textDocument/foldingRange` + // or server will call `client/registerCapability` + if (!languages.hasProvider('foldingRange', textDocument)) { + await wait(500) + if (!languages.hasProvider('foldingRange', textDocument)) { + throw 'UfoFallbackException' + } + } + await doc.synchronize() + let tokenSource = new CancellationTokenSource() + let { token } = tokenSource + let ranges = await languages.provideFoldingRanges(textDocument, {}, token) + if (!ranges || !ranges.length) { + return [] + } + ranges = ranges.filter(o => (!kind || kind == o.kind) && o.startLine < o.endLine) + .sort((a, b) => { + if (b.startLine == a.startLine) { + return a.endLine - b.endLine + } else { + return b.startLine - a.startLine + } + }) + return ranges + })) + subscriptions.push(Disposable.create(async () => { + await nvim.lua(`require('ufo.provider.lsp.coc').handleDisposeNotify(...)`, []) + })) + await nvim.lua(`require('ufo.provider.lsp.coc').handleInitNotify(...)`, []) +} diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/doc/example.lua b/config/neovim/store/lazy-plugins/nvim-ufo/doc/example.lua new file mode 100644 index 00000000..c2153a30 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/doc/example.lua @@ -0,0 +1,186 @@ +---@diagnostic disable: unused-local, unused-function, undefined-field + +-----------------------------------------providerSelector------------------------------------------- +local function selectProviderWithFt() + local ftMap = { + vim = 'indent', + python = {'indent'}, + git = '' + } + require('ufo').setup({ + provider_selector = function(bufnr, filetype, buftype) + -- return a table with string elements: 1st is name of main provider, 2nd is fallback + -- return a string type: use ufo inner providers + -- return a string in a table: like a string type above + -- return empty string '': disable any providers + -- return `nil`: use default value {'lsp', 'indent'} + -- return a function: it will be involved and expected return `UfoFoldingRange[]|Promise` + + -- if you prefer treesitter provider rather than lsp, + -- return ftMap[filetype] or {'treesitter', 'indent'} + return ftMap[filetype] + end + }) +end + +-- lsp->treesitter->indent +local function selectProviderWithChainByDefault() + local ftMap = { + vim = 'indent', + python = {'indent'}, + git = '' + } + + ---@param bufnr number + ---@return Promise + local function customizeSelector(bufnr) + local function handleFallbackException(err, providerName) + if type(err) == 'string' and err:match('UfoFallbackException') then + return require('ufo').getFolds(bufnr, providerName) + else + return require('promise').reject(err) + end + end + + return require('ufo').getFolds(bufnr, 'lsp'):catch(function(err) + return handleFallbackException(err, 'treesitter') + end):catch(function(err) + return handleFallbackException(err, 'indent') + end) + end + + require('ufo').setup({ + provider_selector = function(bufnr, filetype, buftype) + return ftMap[filetype] or customizeSelector + end + }) +end + +local function selectProviderWithFunction() + ---@param bufnr number + ---@return UfoFoldingRange[] + local function customizeSelector(bufnr) + local res = {} + table.insert(res, {startLine = 1, endLine = 3}) + table.insert(res, {startLine = 5, endLine = 10}) + return res + end + + local ftMap = { + vim = 'indent', + python = {'indent'}, + git = customizeSelector + } + + require('ufo').setup({ + provider_selector = function(bufnr, filetype, buftype) + return ftMap[filetype] + end + }) +end + +-----------------------------------------providerSelector------------------------------------------- + +------------------------------------------enhanceAction--------------------------------------------- +local function peekOrHover() + local winid = require('ufo').peekFoldedLinesUnderCursor() + if winid then + local bufnr = vim.api.nvim_win_get_buf(winid) + local keys = {'a', 'i', 'o', 'A', 'I', 'O', 'gd', 'gr'} + for _, k in ipairs(keys) do + -- Add a prefix key to fire `trace` action, + -- if Neovim is 0.8.0 before, remap yourself + vim.keymap.set('n', k, '' .. k, {noremap = false, buffer = bufnr}) + end + else + -- coc.nvim + vim.fn.CocActionAsync('definitionHover') + -- nvimlsp + vim.lsp.buf.hover() + end +end + +local function goPreviousClosedAndPeek() + require('ufo').goPreviousClosedFold() + require('ufo').peekFoldedLinesUnderCursor() +end + +local function goNextClosedAndPeek() + require('ufo').goNextClosedFold() + require('ufo').peekFoldedLinesUnderCursor() +end + +local function applyFoldsAndThenCloseAllFolds(providerName) + require('async')(function() + local bufnr = vim.api.nvim_get_current_buf() + -- make sure buffer is attached + require('ufo').attach(bufnr) + -- getFolds return Promise if providerName == 'lsp' + local ranges = await(require('ufo').getFolds(bufnr, providerName)) + if not vim.tbl_isempty(ranges) then + local ok = require('ufo').applyFolds(bufnr, ranges) + if ok then + require('ufo').closeAllFolds() + end + end + end) +end + +------------------------------------------enhanceAction--------------------------------------------- + +---------------------------------------setFoldVirtTextHandler--------------------------------------- +local handler = function(virtText, lnum, endLnum, width, truncate) + local newVirtText = {} + local suffix = (' 󰁂 %d '):format(endLnum - lnum) + local sufWidth = vim.fn.strdisplaywidth(suffix) + local targetWidth = width - sufWidth + local curWidth = 0 + for _, chunk in ipairs(virtText) do + local chunkText = chunk[1] + local chunkWidth = vim.fn.strdisplaywidth(chunkText) + if targetWidth > curWidth + chunkWidth then + table.insert(newVirtText, chunk) + else + chunkText = truncate(chunkText, targetWidth - curWidth) + local hlGroup = chunk[2] + table.insert(newVirtText, {chunkText, hlGroup}) + chunkWidth = vim.fn.strdisplaywidth(chunkText) + -- str width returned from truncate() may less than 2nd argument, need padding + if curWidth + chunkWidth < targetWidth then + suffix = suffix .. (' '):rep(targetWidth - curWidth - chunkWidth) + end + break + end + curWidth = curWidth + chunkWidth + end + table.insert(newVirtText, {suffix, 'MoreMsg'}) + return newVirtText +end + +local function customizeFoldText() + -- global handler + require('ufo').setup({ + fold_virt_text_handler = handler + }) +end + +local function customizeBufFoldText() + -- buffer scope handler + -- will override global handler if it is existed + local bufnr = vim.api.nvim_get_current_buf() + require('ufo').setFoldVirtTextHandler(bufnr, handler) +end + +local function inspectVirtTextForFoldedLines() + require('ufo').setup({ + enable_get_fold_virt_text = true, + fold_virt_text_handler = function(virtText, lnum, endLnum, width, truncate, ctx) + for i = lnum, endLnum do + print('lnum: ', i, ', virtText: ', vim.inspect(ctx.get_fold_virt_text(i))) + end + return virtText + end + }) +end + +---------------------------------------setFoldVirtTextHandler--------------------------------------- diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/doc/ufo.txt b/config/neovim/store/lazy-plugins/nvim-ufo/doc/ufo.txt new file mode 100644 index 00000000..ff619f68 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/doc/ufo.txt @@ -0,0 +1,17 @@ +*ufo.txt* +*nvimufo.txt* +*nvim-ufo.txt* + +type `gf` or `CTRL-W gf` under the PATH value :) + +`URL`: https://github.com/kevinhwang91/nvim-ufo + +`PATH`: ../README.md + +`PATH`: ../lua/ufo/config.lua |nvim-ufo-default-config| + +`PATH`: ../lua/ufo.lua |nvim-ufo-api| + +`PATH`: ../doc/example.lua |nvim-ufo-example| + +vim:ft=help diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo.lua new file mode 100644 index 00000000..c153f8f6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo.lua @@ -0,0 +1,196 @@ +local api = vim.api + +---Export methods to the users, `require('ufo').method(...)` +---@class Ufo +local M = {} + +---Peek the folded line under cursor, any motions in the normal window will close the floating window. +---@param enter? boolean enter the floating window, default value is false +---@param nextLineIncluded? boolean include the next line of last line of closed fold, default is true +---@return number? winid return the winid if successful, otherwise return nil +function M.peekFoldedLinesUnderCursor(enter, nextLineIncluded) + return require('ufo.preview'):peekFoldedLinesUnderCursor(enter, nextLineIncluded) +end + +---Go to previous start fold. Neovim can't go to previous start fold directly, it's an extra motion. +function M.goPreviousStartFold() + require('ufo.action').goPreviousStartFold() +end + +---Go to previous closed fold. It's an extra motion. +function M.goPreviousClosedFold() + require('ufo.action').goPreviousClosedFold() +end + +---Go to next closed fold. It's an extra motion. +function M.goNextClosedFold() + return require('ufo.action').goNextClosedFold() +end + +---Close all folds but keep foldlevel +function M.closeAllFolds() + return M.closeFoldsWith(0) +end + +---Open all folds but keep foldlevel +function M.openAllFolds() + return require('ufo.action').openFoldsExceptKinds() +end + +---Close the folds with a higher level, +---Like execute `set foldlevel=level` but keep foldlevel +---@param level? number fold level, `v:count` by default +function M.closeFoldsWith(level) + return require('ufo.action').closeFolds(level or vim.v.count) +end + +---Open folds except specified kinds +---@param kinds? UfoFoldingRangeKind[] kind in ranges, get default kinds from `config.close_fold_kinds_for_ft` +function M.openFoldsExceptKinds(kinds) + if not kinds then + local c = require('ufo.config') + if c.close_fold_kinds and not vim.tbl_isempty(c.close_fold_kinds) then + kinds = c.close_fold_kinds + else + kinds = c.close_fold_kinds_for_ft[vim.bo.ft] or c.close_fold_kinds_for_ft.default + end + end + return require('ufo.action').openFoldsExceptKinds(kinds) +end + +---Inspect ufo information by bufnr +---@param bufnr? number buffer number, current buffer by default +function M.inspect(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + local msg = require('ufo.main').inspectBuf(bufnr) + if not msg then + vim.notify(('Buffer %d has not been attached.'):format(bufnr), vim.log.levels.ERROR) + else + vim.notify(table.concat(msg, '\n'), vim.log.levels.INFO) + end +end + +---Enable ufo +function M.enable() + require('ufo.main').enable() +end + +---Disable ufo +function M.disable() + require('ufo.main').disable() +end + +---Check whether the buffer has been attached +---@param bufnr? number buffer number, current buffer by default +---@return boolean +function M.hasAttached(bufnr) + return require('ufo.main').hasAttached(bufnr) +end + +---Attach bufnr to enable all features +---@param bufnr? number buffer number, current buffer by default +function M.attach(bufnr) + require('ufo.main').attach(bufnr) +end + +---Detach bufnr to disable all features +---@param bufnr? number buffer number, current buffer by default +function M.detach(bufnr) + require('ufo.main').detach(bufnr) +end + +---Enable to get folds and update them at once +---@param bufnr? number buffer number, current buffer by default +---@return string|'start'|'pending'|'stop' status +function M.enableFold(bufnr) + return require('ufo.main').enableFold(bufnr) +end + +---Disable to get folds +---@param bufnr? number buffer number, current buffer by default +---@return string|'start'|'pending'|'stop' status +function M.disableFold(bufnr) + return require('ufo.main').disableFold(bufnr) +end + +---Get foldingRange from the ufo internal providers by name +---@param bufnr number +---@param providerName string|'lsp'|'treesitter'|'indent' +---@return UfoFoldingRange[]|Promise +function M.getFolds(bufnr, providerName) + if type(bufnr) == 'string' and type(providerName) == 'number' then + --TODO signature is changed (swap parameters), notify deprecated in next released + ---@deprecated + ---@diagnostic disable-next-line: cast-local-type + bufnr, providerName = providerName, bufnr + end + local func = require('ufo.provider'):getFunction(providerName) + return func(bufnr) +end + +---Apply foldingRange at once. +---ufo always apply folds asynchronously, this function can apply folds synchronously. +---Note: Get ranges from 'lsp' provider is asynchronous. +---@param bufnr number +---@param ranges UfoFoldingRange[] +---@return number winid return the winid if successful, otherwise return -1 +function M.applyFolds(bufnr, ranges) + vim.validate({bufnr = {bufnr, 'number', true}, ranges = {ranges, 'table'}}) + return require('ufo.fold').apply(bufnr, ranges, true) +end + +---Setup configuration and enable ufo +---@param opts? UfoConfig +function M.setup(opts) + opts = opts or {} + M._config = opts + M.enable() +end + +---------------------------------------setFoldVirtTextHandler--------------------------------------- + +---@class UfoFoldVirtTextHandlerContext +---@field bufnr number buffer for closed fold +---@field winid number window for closed fold +---@field text string text for the first line of closed fold +---@field get_fold_virt_text fun(lnum: number): UfoExtmarkVirtTextChunk[] a function to get virtual text by lnum + +---@class UfoExtmarkVirtTextChunk +---@field [1] string text +---@field [2] string|number highlight + +---Set a fold virtual text handler for a buffer, will override global handler if it's existed. +---Ufo actually uses a virtual text with \`nvim_buf_set_extmark\` to overlap the first line of closed fold +---run \`:h nvim_buf_set_extmark | call search('virt_text')\` for detail. +---Return `{}` will not render folded line but only keep a extmark for providers. +---@diagnostic disable: undefined-doc-param +---Detail for handler function: +---@param virtText UfoExtmarkVirtTextChunk[] contained text and highlight captured by Ufo, export to caller +---@param lnum number first line of closed fold, like \`v:foldstart\` in foldtext() +---@param endLnum number last line of closed fold, like \`v:foldend\` in foldtext() +---@param width number text area width, exclude foldcolumn, signcolumn and numberwidth +---@param truncate fun(str: string, width: number): string truncate the str to become specific width, +---return width of string is equal or less than width (2nd argument). +---For example: '1': 1 cell, '你': 2 cells, '2': 1 cell, '好': 2 cells +---truncate('1你2好', 1) return '1' +---truncate('1你2好', 2) return '1' +---truncate('1你2好', 3) return '1你' +---truncate('1你2好', 4) return '1你2' +---truncate('1你2好', 5) return '1你2' +---truncate('1你2好', 6) return '1你2好' +---truncate('1你2好', 7) return '1你2好' +---@param ctx UfoFoldVirtTextHandlerContext the context used by ufo, export to caller + +---@alias UfoFoldVirtTextHandler fun(virtText: UfoExtmarkVirtTextChunk[], lnum: number, endLnum: number, width: number, truncate: fun(str: string, width: number), ctx: UfoFoldVirtTextHandlerContext): UfoExtmarkVirtTextChunk[] + +---@param bufnr number +---@param handler UfoFoldVirtTextHandler +function M.setFoldVirtTextHandler(bufnr, handler) + vim.validate({bufnr = {bufnr, 'number', true}, handler = {handler, 'function'}}) + require('ufo.decorator'):setVirtTextHandler(bufnr, handler) +end + +---@diagnostic disable: undefined-doc-param +---------------------------------------setFoldVirtTextHandler--------------------------------------- + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/action.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/action.lua new file mode 100644 index 00000000..393f4514 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/action.lua @@ -0,0 +1,196 @@ +local api = vim.api +local cmd = vim.cmd +local fn = vim.fn + +local utils = require('ufo.utils') +local fold = require('ufo.fold') + +local M = {} + +function M.goPreviousStartFold() + local function getCurLnum() + return api.nvim_win_get_cursor(0)[1] + end + + local cnt = vim.v.count1 + local view = utils.saveView(0) + local curLnum = getCurLnum() + cmd('norm! m`') + local previousLnum + local previousLnumList = {} + while cnt > 0 do + cmd([[keepj norm! zk]]) + local tLnum = getCurLnum() + cmd([[keepj norm! [z]]) + if tLnum == getCurLnum() then + local foldStartLnum = utils.foldClosed(0, tLnum) + if foldStartLnum > 0 then + cmd(('keepj norm! %dgg'):format(foldStartLnum)) + end + end + local nextLnum = getCurLnum() + while curLnum > nextLnum do + tLnum = nextLnum + table.insert(previousLnumList, nextLnum) + cmd([[keepj norm! zj]]) + nextLnum = getCurLnum() + if nextLnum == tLnum then + break + end + end + if #previousLnumList == 0 then + break + end + if #previousLnumList < cnt then + cnt = cnt - #previousLnumList + curLnum = previousLnumList[1] + previousLnum = curLnum + cmd(('keepj norm! %dgg'):format(curLnum)) + previousLnumList = {} + else + while cnt > 0 do + previousLnum = table.remove(previousLnumList) + cnt = cnt - 1 + end + end + end + utils.restView(0, view) + if previousLnum then + cmd(('norm! %dgg_'):format(previousLnum)) + end +end + +function M.goPreviousClosedFold() + local count = vim.v.count1 + local curLnum = api.nvim_win_get_cursor(0)[1] + local cnt = 0 + local lnum + for i = curLnum - 1, 1, -1 do + if utils.foldClosed(0, i) == i then + cnt = cnt + 1 + lnum = i + if cnt == count then + break + end + end + end + if lnum then + cmd('norm! m`') + api.nvim_win_set_cursor(0, {lnum, 0}) + end +end + +function M.goNextClosedFold() + local count = vim.v.count1 + local curLnum = api.nvim_win_get_cursor(0)[1] + local lineCount = api.nvim_buf_line_count(0) + local cnt = 0 + local lnum + for i = curLnum + 1, lineCount do + if utils.foldClosed(0, i) == i then + cnt = cnt + 1 + lnum = i + if cnt == count then + break + end + end + end + if lnum then + cmd('norm! m`') + api.nvim_win_set_cursor(0, {lnum, 0}) + end +end + +function M.closeFolds(level) + cmd('silent! %foldclose!') + local curBufnr = api.nvim_get_current_buf() + local fb = fold.get(curBufnr) + if not fb then + return + end + for _, range in ipairs(fb.foldRanges) do + fb:closeFold(range.startLine + 1, range.endLine + 1) + end + if level == 0 then + return + end + + local lineCount = fb:lineCount() + local stack = {} + local lastLevel = 0 + local lastEndLnum = -1 + local lnum = 1 + while lnum <= lineCount do + local l = fn.foldlevel(lnum) + if lastLevel < l or l > 0 and lnum == lastEndLnum + 1 then + local endLnum = utils.foldClosedEnd(0, lnum) + table.insert(stack, {endLnum, false}) + if l <= level then + local cmds = {} + for i = #stack, 1, -1 do + local opened = stack[i][2] + if opened then + break + end + stack[i][2] = true + table.insert(cmds, lnum .. 'foldopen') + fb:openFold(lnum) + end + if #cmds > 0 then + cmd(table.concat(cmds, '|')) + -- A line may contain multiple folds, make sure lnum is opened. + while lnum == utils.foldClosed(0, lnum) do + cmd(lnum .. 'foldopen') + end + end + else + --TODO + -- (#184) + --`%foldclose!` doesn't close all folds for window + --endLnum may return -1, look like upstream issue. + if lnum < endLnum then + lnum = endLnum + end + end + end + lastLevel = l + lnum = lnum + 1 + while #stack > 0 do + local endLnum = stack[#stack][1] + if lnum <= endLnum then + break + end + table.remove(stack) + lastEndLnum = math.max(lastEndLnum, endLnum) + end + end +end + +function M.openFoldsExceptKinds(kinds) + cmd('silent! %foldopen!') + local curBufnr = api.nvim_get_current_buf() + local fb = fold.get(curBufnr) + if not fb or type(kinds) ~= 'table' or #kinds == 0 then + return + end + local res = {} + for _, range in ipairs(fb.foldRanges) do + if range.kind and vim.tbl_contains(kinds, range.kind) then + local startLnum, endLnum = range.startLine + 1, range.endLine + 1 + fb:closeFold(startLnum, endLnum) + table.insert(res, {startLnum, endLnum}) + end + end + table.sort(res, function(a, b) + return a[1] == b[1] and a[2] < b[2] or a[1] > b[1] + end) + local cmds = {} + for _, range in ipairs(res) do + table.insert(cmds, range[1] .. 'foldclose') + end + if #cmds > 0 then + cmd(table.concat(cmds, '|')) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/bufmanager.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/bufmanager.lua new file mode 100644 index 00000000..f11f4b5c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/bufmanager.lua @@ -0,0 +1,113 @@ +local api = vim.api + +local buffer = require('ufo.model.buffer') +local event = require('ufo.lib.event') +local disposable = require('ufo.lib.disposable') +local promise = require('promise') +local utils = require('ufo.utils') + +---@class UfoBufferManager +---@field initialized boolean +---@field buffers UfoBuffer[] +---@field disposables UfoDisposable[] +local BufferManager = {} + +local function attach(self, bufnr) + if not self.buffers[bufnr] and not self.bufDetachSet[bufnr] then + local buf = buffer:new(bufnr) + self.buffers[bufnr] = buf + if not buf:attach() then + self.buffers[bufnr] = nil + end + end +end + +function BufferManager:initialize() + if self.initialized then + return self + end + self.initialized = true + self.buffers = {} + self.bufDetachSet = {} + self.disposables = {} + table.insert(self.disposables, disposable:create(function() + for _, b in pairs(self.buffers) do + b:dispose() + end + self.initialized = false + self.buffers = {} + self.bufDetachSet = {} + end)) + ---@diagnostic disable-next-line: unused-local + event:on('BufWinEnter', function(bufnr, winid) + attach(self, bufnr or api.nvim_get_current_buf()) + end, self.disposables) + event:on('BufDetach', function(bufnr) + local b = self.buffers[bufnr] + if b then + b:dispose() + self.buffers[bufnr] = nil + end + end, self.disposables) + event:on('BufTypeChanged', function(bufnr, new, old) + local b = self.buffers[bufnr] + if b and old ~= new then + if new == 'terminal' or new == 'prompt' then + event:emit('BufDetach', bufnr) + else + b.bt = new + end + end + end, self.disposables) + event:on('FileTypeChanged', function(bufnr, new, old) + local b = self.buffers[bufnr] + if b and old ~= new then + b.ft = new + end + end, self.disposables) + event:on('SyntaxChanged', function(bufnr, new, old) + local b = self.buffers[bufnr] + if b and old ~= new then + b._syntax = new + end + end, self.disposables) + + for _, winid in ipairs(api.nvim_tabpage_list_wins(0)) do + local bufnr = api.nvim_win_get_buf(winid) + if utils.isBufLoaded(bufnr) then + attach(self, bufnr) + else + -- the first buffer is unloaded while firing `BufWinEnter` + promise.resolve():thenCall(function() + if utils.isBufLoaded(bufnr) then + attach(self, bufnr) + end + end) + end + end + return self +end + +--- +---@param bufnr number +---@return UfoBuffer +function BufferManager:get(bufnr) + return self.buffers[bufnr] +end + +function BufferManager:dispose() + disposable.disposeAll(self.disposables) + self.disposables = {} +end + +function BufferManager:attach(bufnr) + self.bufDetachSet[bufnr] = nil + attach(self, bufnr) +end + +function BufferManager:detach(bufnr) + self.bufDetachSet[bufnr] = true + event:emit('BufDetach', bufnr) +end + +return BufferManager diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/config.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/config.lua new file mode 100644 index 00000000..9d0602fe --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/config.lua @@ -0,0 +1,88 @@ +---@class UfoConfig +---@field provider_selector? function +---@field open_fold_hl_timeout number +---@field close_fold_kinds_for_ft table +---@field fold_virt_text_handler? UfoFoldVirtTextHandler A global virtual text handler, reference to `ufo.setFoldVirtTextHandler` +---@field enable_get_fold_virt_text boolean +---@field preview table +local def = { + open_fold_hl_timeout = 400, + provider_selector = nil, + close_fold_kinds_for_ft = {default = {}}, + fold_virt_text_handler = nil, + enable_get_fold_virt_text = false, + preview = { + win_config = { + border = 'rounded', + winblend = 12, + winhighlight = 'Normal:Normal', + maxheight = 20 + }, + mappings = { + scrollB = '', + scrollF = '', + scrollU = '', + scrollD = '', + scrollE = '', + scrollY = '', + jumpTop = '', + jumpBot = '', + close = 'q', + switch = '', + trace = '' + } + } +} + +---@type UfoConfig +local Config = {} + + +---@alias UfoProviderEnum +---| 'lsp' +---| 'treesitter' +---| 'indent' + +--- +---@param bufnr number +---@param filetype string file type +---@param buftype string buffer type +---@return UfoProviderEnum|string[]|function|nil +---return a string type use ufo providers +---return a string in a table like a string type +---return empty string '' will disable any providers +---return `nil` will use default value {'lsp', 'indent'} +---@diagnostic disable-next-line: unused-function, unused-local +function Config.provider_selector(bufnr, filetype, buftype) end + +local function init() + local ufo = require('ufo') + ---@type UfoConfig + Config = vim.tbl_deep_extend('keep', ufo._config or {}, def) + vim.validate({ + open_fold_hl_timeout = {Config.open_fold_hl_timeout, 'number'}, + provider_selector = {Config.provider_selector, 'function', true}, + close_fold_kinds_for_ft = {Config.close_fold_kinds_for_ft, 'table'}, + fold_virt_text_handler = {Config.fold_virt_text_handler, 'function', true}, + preview_mappings = {Config.preview.mappings, 'table'} + }) + + local preview = Config.preview + for msg, key in pairs(preview.mappings) do + if key == '' then + preview.mappings[msg] = nil + end + end + if Config.close_fold_kinds and not vim.tbl_isempty(Config.close_fold_kinds) then + vim.notify('Option `close_fold_kinds` in `nvim-ufo` is deprecated, use `close_fold_kinds_for_ft` instead.', + vim.log.levels.WARN) + if not Config.close_fold_kinds_for_ft.default then + Config.close_fold_kinds_for_ft.default = Config.close_fold_kinds + end + end + ufo._config = nil +end + +init() + +return Config diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/decorator.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/decorator.lua new file mode 100644 index 00000000..9984d13a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/decorator.lua @@ -0,0 +1,390 @@ +local api = vim.api +local fn = vim.fn +local cmd = vim.cmd + +local utils = require('ufo.utils') +local config = require('ufo.config') +local log = require('ufo.lib.log') +local disposable = require('ufo.lib.disposable') +local event = require('ufo.lib.event') + +local window = require('ufo.model.window') +local fold = require('ufo.fold') +local render = require('ufo.render') + +---@class UfoDecorator +---@field initialized boolean +---@field ns number +---@field hlNs number +---@field virtTextHandler? UfoFoldVirtTextHandler[] +---@field enableFoldEndVirtText boolean +---@field openFoldHlTimeout number +---@field openFoldHlEnabled boolean +---@field curWinid number +---@field lastWinid number +---@field virtTextHandlers table +---@field winSessions table +---@field disposables UfoDisposable[] +local Decorator = {} + +local collection +local bufnrSet +local namespaces +local handlerErrorMsg + +---@diagnostic disable-next-line: unused-local +local function onStart(name, tick) + collection = {} + bufnrSet = {} + namespaces = {} +end + +---@diagnostic disable-next-line: unused-local +local function onWin(name, winid, bufnr, topRow, botRow) + local fb = fold.get(bufnr) + if bufnrSet[bufnr] or not fb or fb.foldedLineCount == 0 and not vim.wo[winid].foldenable then + collection[winid] = nil + return false + end + local self = Decorator + local wses = self.winSessions[winid] + wses:onWin(bufnr, fb) + collection[winid] = { + winid = winid, + bufnr = bufnr, + rows = {} + } + bufnrSet[bufnr] = winid +end + +---@diagnostic disable-next-line: unused-local +local function onLine(name, winid, bufnr, row) + table.insert(collection[winid].rows, row) +end + +---@diagnostic disable-next-line: unused-local +local function onEnd(name, tick) + local needRedraw = false + local self = Decorator + self.curWinid = api.nvim_get_current_win() + for winid, data in pairs(collection or {}) do + if #data.rows > 0 then + local wses = self.winSessions[winid] + local fb = wses.foldbuffer + local foldedPairs = wses.foldedPairs + if self.curWinid == winid and not next(foldedPairs) then + foldedPairs = self:computeFoldedPairs(data.rows) + end + local shared + for _, row in ipairs(data.rows) do + local lnum = row + 1 + if not foldedPairs[lnum] and fb:lineIsClosed(lnum) then + if shared == nil then + local _, winids = utils.getWinByBuf(fb.bufnr) + shared = winids ~= nil + end + self:highlightOpenFold(fb, winid, lnum, shared) + local didOpen = fb:openFold(lnum) + if not shared then + needRedraw = didOpen or needRedraw + end + end + end + local cursor = wses:cursor() + local curLnum = cursor[1] + if needRedraw then + fb:syncFoldedLines(winid) + end + local curFoldStart, curFoldEnd = 0, 0 + for fs, fe in pairs(foldedPairs) do + local _, didClose = self:getVirtTextAndCloseFold(winid, fs, fe) + if not utils.has10() then + needRedraw = needRedraw or didClose + end + if curFoldStart == 0 and fs <= curLnum and curLnum <= fe then + curFoldStart, curFoldEnd = fs, fe + end + end + if not needRedraw then + local lastCurLnum = wses.lastCurLnum + local lastCurFoldStart, lastCurFoldEnd = wses.lastCurFoldStart, wses.lastCurFoldEnd + if lastCurFoldStart ~= curFoldStart and + lastCurFoldStart < lastCurLnum and lastCurLnum <= lastCurFoldEnd and + lastCurFoldStart < curLnum and curLnum <= lastCurFoldEnd then + log.debug('Curosr under the stale fold range, should open fold.') + needRedraw = fb:openFold(lastCurFoldStart) or needRedraw + end + end + local didHighlight = false + if curLnum == curFoldStart then + didHighlight = wses:setCursorFoldedLineHighlight() + else + didHighlight = wses:clearCursorFoldedLineHighlight() + end + needRedraw = needRedraw or didHighlight + wses.lastCurFoldStart, wses.lastCurFoldEnd = curFoldStart, curFoldEnd + wses.lastCurLnum = curLnum + end + end + if needRedraw then + log.debug('Need redraw.') + cmd('redraw') + end + self.lastWinid = self.curWinid +end + +local function silentOnEnd(...) + local ok, msg = pcall(onEnd, ...) + if not ok and (type(msg) ~= 'string' or not msg:match('Keyboard interrupt\n')) then + error(msg, 0) + end +end + +function Decorator:resetCurosrFoldedLineHighlightByBuf(bufnr) + -- TODO + -- exit cmd window will throw E315: ml_get: invalid lnum: 1 + if api.nvim_buf_line_count(bufnr) == 0 then + return + end + local id, winids = utils.getWinByBuf(bufnr) + if id == -1 then + return + end + for _, winid in ipairs(winids or {id}) do + local wses = self.winSessions[winid] + wses:clearCursorFoldedLineHighlight() + end +end + +function Decorator:highlightOpenFold(fb, winid, lnum, shared) + if self.openFoldHlEnabled and winid == self.lastWinid and api.nvim_get_mode().mode ~= 'c' then + local endLnum + if not shared then + local fl = fb:foldedLine(lnum) + local _ + _, endLnum = fl:range() + if endLnum == 0 then + return + end + else + endLnum = utils.foldClosedEnd(winid, lnum) + if endLnum < 0 then + return + end + end + render.highlightLinesWithTimeout(shared and winid or fb.bufnr, 'UfoFoldedBg', self.hlNs, + lnum, endLnum, self.openFoldHlTimeout, shared) + end +end + +function Decorator:computeFoldedPairs(rows) + local lastRow = rows[1] + local res = {} + for i = 2, #rows do + local lnum = lastRow + 1 + local curRow = rows[i] + if curRow > lnum and utils.foldClosed(0, lnum) == lnum then + res[lnum] = curRow + end + lastRow = curRow + end + + local lnum = lastRow + 1 + if utils.foldClosed(0, lnum) == lnum then + res[lnum] = utils.foldClosedEnd(0, lnum) + end + return res +end + +function Decorator:getVirtTextAndCloseFold(winid, lnum, endLnum, doRender) + local didClose = false + local wses = self.winSessions[winid] + if not wses then + return {}, didClose + end + local bufnr, fb = wses.bufnr, wses.foldbuffer + if endLnum then + wses.foldedPairs[lnum] = endLnum + end + local width = wses:textWidth() + local ok, res = true, wses.foldedTextMaps[lnum] + local fl = fb:foldedLine(lnum) + local rendered = false + if fl then + if not res and not fl:widthChanged(width) then + res = fl.virtText + end + rendered = fl:hasRendered() + end + if not res or not rendered then + if not endLnum then + endLnum = wses:foldEndLnum(lnum) + end + local text = fb:lines(lnum)[1] + if not res then + local handler = self:getVirtTextHandler(bufnr) + local virtText + local syntax = fb:syntax() ~= '' + local concealLevel = wses:concealLevel() + if not next(namespaces) then + for _, ns in pairs(api.nvim_get_namespaces()) do + if self.ns ~= ns then + table.insert(namespaces, ns) + end + end + end + virtText = render.captureVirtText(bufnr, text, lnum, syntax, namespaces, concealLevel) + local getFoldVirtText + if self.enableGetFoldVirtText then + getFoldVirtText = function(l) + local t = type(l) + assert(t == 'number', 'expected a number, got ' .. t) + assert(lnum <= l and l <= endLnum, + ('expected lnum range from %d to %d, got %d'):format(lnum, endLnum, l)) + local line = fb:lines(l)[1] + return render.captureVirtText(bufnr, line, l, syntax, namespaces, concealLevel) + end + end + ok, res = pcall(handler, virtText, lnum, endLnum, width, utils.truncateStrByWidth, { + bufnr = bufnr, + winid = winid, + text = text, + get_fold_kind = function(l) + l = l == nil and lnum or l + local t = type(l) + assert(t == 'number', 'expected a number, got ' .. t) + return fb:lineKind(winid, l) + end, + get_fold_virt_text = getFoldVirtText + }) + wses.foldedTextMaps[lnum] = res + end + if doRender == nil then + doRender = true + end + if ok then + if bufnrSet[bufnr] == winid then + if doRender then + log.debug('Window:', winid, 'need add/update folded lnum:', lnum) + didClose = true + else + log.debug('Window:', winid, 'will add/update folded lnum:', lnum) + end + fb:closeFold(lnum, endLnum, text, res, width, doRender) + end + else + fb:closeFold(lnum, endLnum, text, {{handlerErrorMsg, 'Error'}}, width, doRender) + log.error(res) + end + end + return res, didClose +end + +---@diagnostic disable-next-line: unused-local +function Decorator.defaultVirtTextHandler(virtText, lnum, endLnum, width, truncate, ctx) + local newVirtText = {} + local suffix = ' ⋯ ' + local sufWidth = fn.strdisplaywidth(suffix) + local targetWidth = width - sufWidth + local curWidth = 0 + for _, chunk in ipairs(virtText) do + local chunkText = chunk[1] + local chunkWidth = fn.strdisplaywidth(chunkText) + if targetWidth > curWidth + chunkWidth then + table.insert(newVirtText, chunk) + else + chunkText = truncate(chunkText, targetWidth - curWidth) + local hlGroup = chunk[2] + table.insert(newVirtText, {chunkText, hlGroup}) + chunkWidth = fn.strdisplaywidth(chunkText) + -- str width returned from truncate() may less than 2nd argument, need padding + if curWidth + chunkWidth < targetWidth then + suffix = suffix .. (' '):rep(targetWidth - curWidth - chunkWidth) + end + break + end + curWidth = curWidth + chunkWidth + end + table.insert(newVirtText, {suffix, 'UfoFoldedEllipsis'}) + return newVirtText +end + +function Decorator:setVirtTextHandler(bufnr, handler) + bufnr = bufnr or api.nvim_get_current_buf() + self.virtTextHandlers[bufnr] = handler +end + +--- +---@param bufnr number +---@return UfoFoldVirtTextHandler +function Decorator:getVirtTextHandler(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + return self.virtTextHandlers[bufnr] +end + +--- +---@param namespace number +---@return UfoDecorator +function Decorator:initialize(namespace) + if self.initialized then + return self + end + self.initialized = true + api.nvim_set_decoration_provider(namespace, { + on_start = onStart, + on_win = onWin, + on_line = onLine, + on_end = silentOnEnd + }) + self.ns = namespace + self.hlNs = self.hlNs or api.nvim_create_namespace('') + self.disposables = {} + table.insert(self.disposables, disposable:create(function() + self.initialized = false + api.nvim_set_decoration_provider(namespace, {}) + for bufnr in ipairs(fold.buffers()) do + self:resetCurosrFoldedLineHighlightByBuf(bufnr) + end + end)) + self.enableGetFoldVirtText = config.enable_get_fold_virt_text + self.openFoldHlTimeout = config.open_fold_hl_timeout + self.openFoldHlEnabled = self.openFoldHlTimeout > 0 + event:on('setOpenFoldHl', function(val) + if type(val) == 'boolean' then + self.openFoldHlEnabled = val + else + self.openFoldHlEnabled = self.openFoldHlTimeout > 0 + end + end, self.disposables) + + local virtTextHandler = config.fold_virt_text_handler or self.defaultVirtTextHandler + self.virtTextHandlers = setmetatable({}, { + __index = function(tbl, bufnr) + rawset(tbl, bufnr, virtTextHandler) + return virtTextHandler + end + }) + handlerErrorMsg = ([[!Error in user's handler, check out `%s`]]):format(log.path) + self.winSessions = setmetatable({}, { + __index = function(tbl, winid) + local o = window:new(winid) + rawset(tbl, winid, o) + return o + end + }) + event:on('WinClosed', function(winid) + self.winSessions[winid] = nil + end, self.disposables) + event:on('BufDetach', function(bufnr) + self:resetCurosrFoldedLineHighlightByBuf(bufnr) + self.virtTextHandlers[bufnr] = nil + end, self.disposables) + return self +end + +function Decorator:dispose() + disposable.disposeAll(self.disposables) + self.disposables = {} +end + +return Decorator diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/fold/driver.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/fold/driver.lua new file mode 100644 index 00000000..b7669cb9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/fold/driver.lua @@ -0,0 +1,107 @@ +local cmd = vim.cmd + +local utils = require('ufo.utils') + +local function convertToFoldRanges(ranges, rowPairs) + -- just check the last range only to filter out same ranges + local lastStartLine, lastEndLine + local foldRanges = {} + local minLines = vim.wo.foldminlines + for _, r in ipairs(ranges) do + local startLine, endLine = r.startLine, r.endLine + if not rowPairs[startLine] and endLine - startLine >= minLines and + (lastStartLine ~= startLine or lastEndLine ~= endLine) then + lastStartLine, lastEndLine = startLine, endLine + table.insert(foldRanges, {startLine + 1, endLine + 1}) + end + end + return foldRanges +end + +---@type UfoFoldDriverNonFFI|UfoFoldDriverFFI +local FoldDriver + +---@class UfoFoldDriverFFI +local FoldDriverFFI = {} + +function FoldDriverFFI:new(wffi) + local o = setmetatable({}, self) + self.__index = self + self._wffi = wffi + return o +end + +--- +---@param winid number +---@param ranges UfoFoldingRange +---@param rowPairs table +function FoldDriverFFI:createFolds(winid, ranges, rowPairs) + utils.winCall(winid, function() + local wo = vim.wo + local level = wo.foldlevel + self._wffi.clearFolds(winid) + local foldRanges = convertToFoldRanges(ranges, rowPairs) + self._wffi.createFolds(winid, foldRanges) + wo.foldmethod = 'manual' + wo.foldenable = true + wo.foldlevel = level + foldRanges = {} + for row, endRow in pairs(rowPairs) do + table.insert(foldRanges, {row + 1, endRow + 1}) + end + self._wffi.createFolds(winid, foldRanges) + end) +end + +---@class UfoFoldDriverNonFFI +local FoldDriverNonFFI = {} + +function FoldDriverNonFFI:new() + local o = setmetatable({}, self) + self.__index = self + return o +end + +--- +---@param winid number +---@param ranges UfoFoldingRange +---@param rowPairs table +function FoldDriverNonFFI:createFolds(winid, ranges, rowPairs) + utils.winCall(winid, function() + local level = vim.wo.foldlevel + local cmds = {} + local foldRanges = convertToFoldRanges(ranges, rowPairs) + for _, r in ipairs(foldRanges) do + table.insert(cmds, ('%d,%d:fold'):format(r[1], r[2])) + end + local view = utils.saveView(0) + cmd('norm! zE') + utils.restView(0, view) + table.insert(cmds, 'setl foldmethod=manual') + table.insert(cmds, 'setl foldenable') + table.insert(cmds, 'setl foldlevel=' .. level) + foldRanges = {} + for row, endRow in pairs(rowPairs) do + table.insert(foldRanges, {row + 1, endRow + 1}) + end + table.sort(foldRanges, function(a, b) + return a[1] == b[1] and a[2] < b[2] or a[1] > b[1] + end) + for _, r in ipairs(foldRanges) do + table.insert(cmds, ('%d,%dfold'):format(r[1], r[2])) + end + cmd(table.concat(cmds, '|')) + end) +end + +local function init() + if jit ~= nil then + FoldDriver = FoldDriverFFI:new(require('ufo.wffi')) + else + FoldDriver = FoldDriverNonFFI:new() + end +end + +init() + +return FoldDriver diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/fold/init.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/fold/init.lua new file mode 100644 index 00000000..e97d1352 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/fold/init.lua @@ -0,0 +1,229 @@ +local api = vim.api +local cmd = vim.cmd + +local config = require('ufo.config') +local promise = require('promise') +local async = require('async') +local utils = require('ufo.utils') +local provider = require('ufo.provider') +local log = require('ufo.lib.log') +local event = require('ufo.lib.event') +local manager = require('ufo.fold.manager') +local disposable = require('ufo.lib.disposable') + +---@class UfoFold +---@field initialized boolean +---@field disposables UfoDisposable[] +local Fold = {} + +local updateFoldDebounced + +---@param bufnr number +---@return Promise +local function tryUpdateFold(bufnr) + return async(function() + local winid = utils.getWinByBuf(bufnr) + if not utils.isWinValid(winid) then + return + end + -- some plugins may change foldmethod to diff + await(utils.wait(50)) + if not utils.isWinValid(winid) or utils.isDiffOrMarkerFold(winid) then + return + end + await(Fold.update(bufnr)) + end) +end + +local function setFoldText(bufnr) + api.nvim_buf_call(bufnr, function() + cmd([[ + setl foldtext=v:lua.require'ufo.main'.foldtext() + setl fillchars+=fold:\ ]]) + end) +end + +function Fold.update(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + local fb = manager:get(bufnr) + if not fb then + return promise.resolve() + end + if manager:isFoldMethodsDisabled(fb) then + if not pcall(fb.getRangesFromExtmarks, fb) then + fb:resetFoldedLines(true) + end + return promise.resolve() + end + if fb.status == 'pending' and manager:applyFoldRanges(bufnr) ~= -1 then + return promise.resolve() + end + + local changedtick, ft, bt = fb:changedtick(), fb:filetype(), fb:buftype() + fb:acquireRequest() + + local function dispose(resolved) + ---@diagnostic disable-next-line: redefined-local + local fb = manager:get(bufnr) + if not fb then + return false + end + fb:releaseRequest() + local ok = ft == fb:filetype() and bt == fb:buftype() + if ok then + if resolved then + ok = changedtick == fb:changedtick() + end + end + local requested = fb:requested() + if not ok and not requested then + log.debug('update fold again for bufnr:', bufnr) + updateFoldDebounced(bufnr) + end + return ok and not requested + end + + log.info('providers:', fb.providers) + return provider:requestFoldingRange(fb.providers, bufnr):thenCall(function(res) + if not dispose(true) then + return + end + local selected, ranges = res[1], res[2] + fb.selectedProvider = type(selected) == 'string' and selected or 'external' + log.info('selected provider:', fb.selectedProvider) + if not ranges or #ranges == 0 or not utils.isBufLoaded(bufnr) then + return + end + fb.status = manager:applyFoldRanges(bufnr, ranges) == -1 and 'pending' or 'start' + end, function(err) + if not dispose(false) then + return + end + return promise.reject(err) + end) +end + +--- +---@param bufnr number +function Fold.get(bufnr) + return manager:get(bufnr) +end + +function Fold.buffers() + return manager.buffers +end + +function Fold.apply(bufnr, ranges, manual) + return manager:applyFoldRanges(bufnr, ranges, manual) +end + +function Fold.attach(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + if not manager:attach(bufnr) then + return + end + log.debug('attach bufnr:', bufnr) + setFoldText(bufnr) + tryUpdateFold(bufnr) +end + +function Fold.setStatus(bufnr, status) + local fb = manager:get(bufnr) + local old = '' + if fb then + old = fb.status + fb.status = status + end + return old +end + +updateFoldDebounced = (function() + local lastBufnr + local debounced = require('ufo.lib.debounce')(Fold.update, 300) + return function(bufnr, flush, onlyPending) + bufnr = bufnr or api.nvim_get_current_buf() + local fb = manager:get(bufnr) + if not fb or utils.mode() ~= 'n' or + onlyPending and fb.status ~= 'pending' or fb.status == 'stop' then + return + end + if lastBufnr ~= bufnr then + debounced:flush() + end + lastBufnr = bufnr + debounced(bufnr) + if flush then + debounced:flush() + end + end +end)() + +local function handleDiffMode(winid, new, old) + if old ~= new and new == 0 then + local bufnr = api.nvim_win_get_buf(winid) + local fb = manager:get(bufnr) + + if fb then + fb:resetFoldedLines(true) + + -- TODO + -- buffer go back normal mode from diff mode will disable `foldenable` if the foldmethod was + -- `manual` before entering diff mode. Unfortunately, foldmethod will always be `manual` if + -- enable ufo, `foldenable` will be disabled. + + -- `set foldenable` forcedly, feel free to open an issue if ufo is evil. + promise.resolve():thenCall(function() + if utils.isWinValid(winid) and vim.wo[winid].foldmethod == 'manual' then + utils.winCall(winid, function() + cmd('silent! %foldopen!') + end) + vim.wo[winid].foldenable = true + end + end) + tryUpdateFold(bufnr) + end + end +end + +--- +---@param ns number +---@return UfoFold +function Fold:initialize(ns) + if self.initialized then + return self + end + self.initialized = true + self.disposables = {} + table.insert(self.disposables, disposable:create(function() + self.initialized = false + end)) + event:on('BufWinEnter', function(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + local fb = manager:get(bufnr) + if not fb then + return + end + setFoldText(bufnr) + updateFoldDebounced(bufnr, true, true) + end, self.disposables) + event:on('BufWritePost', function(bufnr) + updateFoldDebounced(bufnr, true) + end, self.disposables) + event:on('TextChanged', updateFoldDebounced, self.disposables) + event:on('ModeChangedToNormal', function(bufnr, oldMode) + local onlyPending = oldMode ~= 'i' and oldMode ~= 't' + updateFoldDebounced(bufnr, true, onlyPending) + end, self.disposables) + event:on('BufAttach', Fold.attach, self.disposables) + event:on('DiffModeChanged', handleDiffMode, self.disposables) + table.insert(self.disposables, manager:initialize(ns, config.provider_selector, + config.close_fold_kinds_for_ft)) + return self +end + +function Fold:dispose() + disposable.disposeAll(self.disposables) + self.disposables = {} +end + +return Fold diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/fold/manager.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/fold/manager.lua new file mode 100644 index 00000000..524a8348 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/fold/manager.lua @@ -0,0 +1,211 @@ +local buffer = require('ufo.model.foldbuffer') +local event = require('ufo.lib.event') +local disposable = require('ufo.lib.disposable') +local bufmanager = require('ufo.bufmanager') +local utils = require('ufo.utils') +local driver = require('ufo.fold.driver') +local log = require('ufo.lib.log') + +---@class UfoFoldBufferManager +---@field initialized boolean +---@field buffers UfoFoldBuffer[] +---@field providerSelector function +---@field closeKindsMap table +---@field disposables UfoDisposable[] +local FoldBufferManager = {} + +--- +---@param namespace number +---@param selector function +---@param closeKindsMap +---@return UfoFoldBufferManager +function FoldBufferManager:initialize(namespace, selector, closeKindsMap) + if self.initialized then + return self + end + self.ns = namespace + self.providerSelector = selector + self.closeKindsMap = closeKindsMap + self.buffers = {} + self.initialized = true + self.disposables = {} + table.insert(self.disposables, disposable:create(function() + for _, fb in pairs(self.buffers) do + fb:dispose() + end + self.buffers = {} + self.initialized = false + end)) + event:on('BufDetach', function(bufnr) + local fb = self:get(bufnr) + if fb then + fb:dispose() + end + self.buffers[bufnr] = nil + end, self.disposables) + event:on('BufReload', function(bufnr) + local fb = self:get(bufnr) + if fb then + fb:dispose() + end + end, self.disposables) + + local function optChanged(bufnr, new, old) + if old ~= new then + local fb = self:get(bufnr) + if fb then + fb.providers = nil + end + end + end + + event:on('BufTypeChanged', optChanged, self.disposables) + event:on('FileTypeChanged', optChanged, self.disposables) + event:on('BufLinesChanged', function(bufnr, _, firstLine, lastLine, lastLineUpdated) + local fb = self:get(bufnr) + if fb then + fb:handleFoldedLinesChanged(firstLine, lastLine, lastLineUpdated) + end + end, self.disposables) + self.providerSelector = selector + return self +end + +--- +---@param bufnr number +---@return boolean +function FoldBufferManager:attach(bufnr) + local fb = self:get(bufnr) + if not fb then + self.buffers[bufnr] = buffer:new(bufmanager:get(bufnr), self.ns) + end + return not fb +end + +--- +---@param bufnr number +---@return UfoFoldBuffer +function FoldBufferManager:get(bufnr) + return self.buffers[bufnr] +end + +function FoldBufferManager:dispose() + disposable.disposeAll(self.disposables) + self.disposables = {} +end + +function FoldBufferManager:parseBufferProviders(fb, selector) + if not utils.isBufLoaded(fb.bufnr) then + return + end + if not selector then + fb.providers = {'lsp', 'indent'} + return + end + local res + local providers = selector(fb.bufnr, fb:filetype(), fb:buftype()) + local t = type(providers) + if t == 'nil' then + res = {'lsp', 'indent'} + elseif t == 'string' or t == 'function' then + res = {providers} + elseif t == 'table' then + res = {} + for _, m in ipairs(providers) do + if #res == 2 then + error('Return value of `provider_selector` only supports {`main`, `fallback`} ' .. + [[combo, don't add providers more than two into return table.]]) + end + table.insert(res, m) + end + else + res = {''} + end + fb.providers = res +end + +function FoldBufferManager:isFoldMethodsDisabled(fb) + if not fb.providers then + self:parseBufferProviders(fb, self.providerSelector) + end + return not fb.providers or fb.providers[1] == '' +end + +function FoldBufferManager:getRowPairsByScanning(fb, winid) + local rowPairs = {} + for _, range in ipairs(fb:scanFoldedRanges(winid)) do + local row, endRow = range[1], range[2] + rowPairs[row] = endRow + end + return rowPairs +end + +--- +---@param bufnr number +---@param ranges? UfoFoldingRange[] +---@param manual? boolean +---@return number +function FoldBufferManager:applyFoldRanges(bufnr, ranges, manual) + local fb = self:get(bufnr) + if not fb then + return -1 + end + local changedtick = fb:changedtick() + if ranges then + fb.foldRanges = ranges + fb.version = changedtick + elseif changedtick ~= fb.version then + return -1 + end + local winid, windows = utils.getWinByBuf(bufnr) + if winid == -1 or not utils.isWinValid(winid) then + return -1 + elseif not vim.wo[winid].foldenable or utils.isDiffOrMarkerFold(winid) then + return -1 + elseif utils.mode() ~= 'n' then + return -1 + end + local rowPairs = {} + local isFirstApply = not fb.scanned + if not manual and not fb.scanned or windows then + rowPairs = self:getRowPairsByScanning(fb, winid) + local kinds = self.closeKindsMap[fb:filetype()] or self.closeKindsMap.default + for _, range in ipairs(fb.foldRanges) do + if range.kind and vim.tbl_contains(kinds, range.kind) then + local startLine, endLine = range.startLine, range.endLine + rowPairs[startLine] = endLine + fb:closeFold(startLine + 1, endLine + 1) + end + end + fb.scanned = true + else + local ok, res = pcall(function() + for _, range in ipairs(fb:getRangesFromExtmarks()) do + local row, endRow = range[1], range[2] + rowPairs[row] = endRow + end + end) + if not ok then + log.info(res) + fb:resetFoldedLines(true) + rowPairs = self:getRowPairsByScanning(fb, winid) + end + end + + local view, wrow + -- topline may changed after applying folds, restore topline to save our eyes + if isFirstApply and not vim.tbl_isempty(rowPairs) then + view = utils.saveView(winid) + wrow = utils.wrow(winid) + end + log.info('apply fold ranges:', fb.foldRanges) + log.info('apply fold rowPairs:', rowPairs) + driver:createFolds(winid, fb.foldRanges, rowPairs) + if view and utils.wrow(winid) ~= wrow then + view.topline, view.topfill = utils.evaluateTopline(winid, view.lnum, wrow) + utils.restView(winid, view) + end + return winid +end + +return FoldBufferManager diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/highlight.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/highlight.lua new file mode 100644 index 00000000..3f8dfc8b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/highlight.lua @@ -0,0 +1,116 @@ +local cmd = vim.cmd +local api = vim.api +local fn = vim.fn + +local event = require('ufo.lib.event') +local disposable = require('ufo.lib.disposable') + +---@class UfoHighlight +local Highlight = {} +local initialized + +---@type table +local hlGroups + +---@type table +local signNames + +local function resetHighlightGroup() + local termguicolors = vim.o.termguicolors + hlGroups = setmetatable({}, { + __index = function(tbl, k) + local ok, hl + if type(k) == 'number' then + ok, hl = pcall(api.nvim_get_hl_by_id, k, termguicolors) + else + ok, hl = pcall(api.nvim_get_hl_by_name, k, termguicolors) + end + if not ok or hl[vim.type_idx] == vim.types.dictionary then + hl = {} + end + rawset(tbl, k, hl) + return hl + end + }) + local ok, hl = pcall(api.nvim_get_hl_by_name, 'Folded', termguicolors) + if ok and hl.background then + if termguicolors then + cmd(('hi default UfoFoldedBg guibg=#%x'):format(hl.background)) + else + cmd(('hi default UfoFoldedBg ctermbg=%d'):format(hl.background)) + end + else + cmd('hi default link UfoFoldedBg Visual') + end + ok, hl = pcall(api.nvim_get_hl_by_name, 'Normal', termguicolors) + if ok and hl.foreground then + if termguicolors then + cmd(('hi default UfoFoldedFg guifg=#%x'):format(hl.foreground)) + else + cmd(('hi default UfoFoldedFg ctermfg=%d'):format(hl.foreground)) + end + else + cmd('hi default UfoFoldedFg ctermfg=None guifg=None') + end + + cmd([[ + hi default link UfoPreviewSbar PmenuSbar + hi default link UfoPreviewThumb PmenuThumb + hi default link UfoPreviewWinBar UfoFoldedBg + hi default link UfoPreviewCursorLine Visual + hi default link UfoFoldedEllipsis Comment + hi default link UfoCursorFoldedLine CursorLine + ]]) +end + +local function resetSignGroup() + signNames = setmetatable({}, { + __index = function(tbl, k) + assert(fn.sign_define(k, {linehl = k}) == 0, + 'Define sign name ' .. k .. 'failed') + rawset(tbl, k, k) + return k + end + }) + return disposable:create(function() + for _, name in pairs(signNames) do + pcall(fn.sign_undefine, name) + end + end) +end + +function Highlight.hlGroups() + if not initialized then + Highlight:initialize() + end + return hlGroups +end + +function Highlight.signNames() + if not initialized then + Highlight:initialize() + end + return signNames +end + +--- +---@return UfoHighlight +function Highlight:initialize() + if initialized then + return self + end + self.disposables = {} + event:on('ColorScheme', resetHighlightGroup, self.disposables) + resetHighlightGroup() + table.insert(self.disposables, resetSignGroup()) + initialized = true + return self +end + +function Highlight:dispose() + disposable.disposeAll(self.disposables) + self.disposables = {} + initialized = false +end + +return Highlight diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/debounce.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/debounce.lua new file mode 100644 index 00000000..d609a7b7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/debounce.lua @@ -0,0 +1,78 @@ +local uv = vim.loop + +---@class UfoDebounce +---@field timer userdata +---@field fn function +---@field args table +---@field wait number +---@field leading? boolean +---@overload fun(fn: function, wait: number, leading?: boolean): UfoDebounce +local Debounce = {} + +--- +---@param fn function +---@param wait number +---@param leading? boolean +---@return UfoDebounce +function Debounce:new(fn, wait, leading) + vim.validate({ + fn = {fn, 'function'}, + wait = {wait, 'number'}, + leading = {leading, 'boolean', true} + }) + local o = setmetatable({}, self) + o.timer = nil + o.fn = vim.schedule_wrap(fn) + o.args = nil + o.wait = wait + o.leading = leading + return o +end + +function Debounce:call(...) + local timer = self.timer + self.args = {...} + if not timer then + ---@type userdata + timer = uv.new_timer() + self.timer = timer + local wait = self.wait + timer:start(wait, wait, self.leading and function() + self:cancel() + end or function() + self:flush() + end) + if self.leading then + self.fn(...) + end + else + timer:again() + end +end + +function Debounce:cancel() + local timer = self.timer + if timer then + if timer:has_ref() then + timer:stop() + if not timer:is_closing() then + timer:close() + end + end + self.timer = nil + end +end + +function Debounce:flush() + if self.timer then + self:cancel() + self.fn(unpack(self.args)) + end +end + +Debounce.__index = Debounce +Debounce.__call = Debounce.call + +return setmetatable(Debounce, { + __call = Debounce.new +}) diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/disposable.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/disposable.lua new file mode 100644 index 00000000..ce1d7624 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/disposable.lua @@ -0,0 +1,36 @@ +---@class UfoDisposable +---@field func fun() +local Disposable = {} + +--- +---@param disposables UfoDisposable[] +function Disposable.disposeAll(disposables) + for _, item in ipairs(disposables) do + if item.dispose then + item:dispose() + end + end +end + +--- +---@param func fun() +---@return UfoDisposable +function Disposable:new(func) + local o = setmetatable({}, self) + self.__index = self + o.func = func + return o +end + +--- +---@param func fun() +---@return UfoDisposable +function Disposable:create(func) + return self:new(func) +end + +function Disposable:dispose() + self.func() +end + +return Disposable diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/event.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/event.lua new file mode 100644 index 00000000..5eb7f279 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/event.lua @@ -0,0 +1,58 @@ +local disposable = require('ufo.lib.disposable') +local log = require('ufo.lib.log') + +---@class UfoEvent +local Event = { + _collection = {} +} + +---@param name string +---@param listener function +function Event:off(name, listener) + local listeners = self._collection[name] + if not listeners then + return + end + for i = 1, #listeners do + if listeners[i] == listener then + table.remove(listeners, i) + break + end + end + if #listeners == 0 then + self._collection[name] = nil + end +end + +---@param name string +---@param listener function +---@param disposables? UfoDisposable[] +---@return UfoDisposable +function Event:on(name, listener, disposables) + if not self._collection[name] then + self._collection[name] = {} + end + table.insert(self._collection[name], listener) + local d = disposable:create(function() + self:off(name, listener) + end) + if type(disposables) == 'table' then + table.insert(disposables, d) + end + return d +end + +---@param name string +---@vararg any +function Event:emit(name, ...) + local listeners = self._collection[name] + if not listeners then + return + end + log.trace('event:', name, 'listeners:', listeners, 'args:', ...) + for _, listener in ipairs(listeners) do + listener(...) + end +end + +return Event diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/log.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/log.lua new file mode 100644 index 00000000..63ec531a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/lib/log.lua @@ -0,0 +1,106 @@ +--- Singleton +---@class UfoLog +---@field trace fun(...) +---@field debug fun(...) +---@field info fun(...) +---@field warn fun(...) +---@field error fun(...) +---@field path string +local Log = {} +local fn = vim.fn +local uv = vim.loop + +---@type table +local levelMap +local levelNr +local defaultLevel + +local function getLevelNr(level) + local nr + if type(level) == 'number' then + nr = level + elseif type(level) == 'string' then + nr = levelMap[level:upper()] + else + nr = defaultLevel + end + return nr +end + +--- +---@param l number|string +function Log.setLevel(l) + levelNr = getLevelNr(l) +end + +--- +---@param l number|string +---@return boolean +function Log.isEnabled(l) + return getLevelNr(l) >= levelNr +end + +--- +---@return string|'trace'|'debug'|'info'|'warn'|'error' +function Log.level() + for l, nr in pairs(levelMap) do + if nr == levelNr then + return l + end + end + return 'UNDEFINED' +end + +local function inspect(v) + local s + local t = type(v) + if t == 'nil' then + s = 'nil' + elseif t ~= 'string' then + s = vim.inspect(v) + else + s = tostring(v) + end + return s +end + +local function pathSep() + return uv.os_uname().sysname == 'Windows_NT' and [[\]] or '/' +end + +local function init() + local logDir = fn.stdpath('cache') + Log.path = table.concat({logDir, 'ufo.log'}, pathSep()) + local logDateFmt = '%y-%m-%d %T' + + fn.mkdir(logDir, 'p') + levelMap = {TRACE = 0, DEBUG = 1, INFO = 2, WARN = 3, ERROR = 4} + defaultLevel = 3 + Log.setLevel(vim.env.UFO_LOG) + + for l in pairs(levelMap) do + Log[l:lower()] = function(...) + local argc = select('#', ...) + if argc == 0 or levelMap[l] < levelNr then + return + end + local msgTbl = {} + for i = 1, argc do + local arg = select(i, ...) + table.insert(msgTbl, inspect(arg)) + end + local msg = table.concat(msgTbl, ' ') + local info = debug.getinfo(2, 'Sl') + local linfo = info.short_src:match('[^/]*$') .. ':' .. info.currentline + + local fp = assert(io.open(Log.path, 'a+')) + local str = string.format('[%s] [%s] %s : %s\n', os.date(logDateFmt), l, linfo, msg) + fp:write(str) + fp:close() + end + end +end + +init() + +return Log diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/main.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/main.lua new file mode 100644 index 00000000..1f3b53cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/main.lua @@ -0,0 +1,204 @@ +local M = {} +local cmd = vim.cmd +local api = vim.api + +local event = require('ufo.lib.event') +local utils = require('ufo.utils') +local provider = require('ufo.provider') +local fold = require('ufo.fold') +local decorator = require('ufo.decorator') +local highlight = require('ufo.highlight') +local preview = require('ufo.preview') +local disposable = require('ufo.lib.disposable') +local bufmanager = require('ufo.bufmanager') + +local enabled + +---@type UfoDisposable[] +local disposables = {} + +local function createEvents() + local gid = api.nvim_create_augroup('Ufo', {}) + api.nvim_create_autocmd({'BufWinEnter', 'TextChanged', 'BufWritePost'}, { + group = gid, + callback = function(ev) + event:emit(ev.event, ev.buf) + end + }) + api.nvim_create_autocmd('WinClosed', { + group = gid, + callback = function(ev) + event:emit(ev.event, tonumber(ev.file)) + end + }) + api.nvim_create_autocmd('ModeChanged', { + group = gid, + pattern = '*:n', + callback = function(ev) + local previousMode = ev.match:match('(%a):') + event:emit('ModeChangedToNormal', ev.buf, previousMode) + end + }) + api.nvim_create_autocmd('ColorScheme', { + group = gid, + callback = function(ev) + event:emit(ev.event) + end + }) + api.nvim_create_autocmd('OptionSet', { + group = gid, + pattern = {'buftype', 'filetype', 'syntax', 'diff'}, + callback = function(ev) + local bufnr = api.nvim_get_current_buf() + local match = ev.match + local e + if match == 'buftype' then + e = 'BufTypeChanged' + elseif match == 'filetype' then + e = 'FileTypeChanged' + elseif match == 'syntax' then + e = 'SyntaxChanged' + elseif match == 'diff' then + event:emit('DiffModeChanged', api.nvim_get_current_win(), vim.v.option_new, vim.v.option_old) + return + else + error([[Didn't match any events!]]) + end + event:emit(e, bufnr, vim.v.option_new, vim.v.option_old) + end + }) + return disposable:create(function() + api.nvim_del_augroup_by_id(gid) + end) +end + +local function createCommand() + cmd([[ + com! UfoEnable lua require('ufo').enable() + com! UfoDisable lua require('ufo').disable() + com! UfoInspect lua require('ufo').inspect() + com! UfoAttach lua require('ufo').attach() + com! UfoDetach lua require('ufo').detach() + com! UfoEnableFold lua require('ufo').enableFold() + com! UfoDisableFold lua require('ufo').disableFold() + ]]) +end + +function M.enable() + if enabled then + return false + end + local ns = api.nvim_create_namespace('ufo') + createCommand() + disposables = {} + table.insert(disposables, createEvents()) + table.insert(disposables, highlight:initialize()) + table.insert(disposables, provider:initialize()) + table.insert(disposables, decorator:initialize(ns)) + table.insert(disposables, fold:initialize(ns)) + table.insert(disposables, preview:initialize(ns)) + table.insert(disposables, bufmanager:initialize()) + enabled = true + return true +end + +function M.disable() + if not enabled then + return false + end + disposable.disposeAll(disposables) + enabled = false + return true +end + +function M.inspectBuf(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + local fb = fold.get(bufnr) + if not fb then + return + end + local msg = {} + table.insert(msg, 'Buffer: ' .. bufnr) + table.insert(msg, 'Fold Status: ' .. fb.status) + local main = fb.providers[1] + table.insert(msg, 'Main provider: ' .. (type(main) == 'function' and 'external' or main)) + if fb.providers[2] then + table.insert(msg, 'Fallback provider: ' .. fb.providers[2]) + end + table.insert(msg, 'Selected provider: ' .. (fb.selectedProvider or 'nil')) + local winid = utils.getWinByBuf(bufnr) + local curKind + local curStartLine, curEndLine = 0, 0 + local kindSet = {} + local lnum = api.nvim_win_get_cursor(winid)[1] + for _, range in ipairs(fb.foldRanges) do + local sl, el = range.startLine, range.endLine + if curStartLine < sl and sl < lnum and lnum <= el + 1 then + curStartLine, curEndLine = sl, el + curKind = range.kind + end + if range.kind then + kindSet[range.kind] = true + end + end + local kinds = {} + for kind in pairs(kindSet) do + table.insert(kinds, kind) + end + table.insert(msg, 'Fold kinds: ' .. table.concat(kinds, ', ')) + if curStartLine ~= 0 or curEndLine ~= 0 then + table.insert(msg, ('Cursor range: [%d, %d]'):format(curStartLine + 1, curEndLine + 1)) + end + if curKind then + table.insert(msg, 'Cursor kind: ' .. curKind) + end + return msg +end + +function M.hasAttached(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + local buf = bufmanager:get(bufnr) + return buf and buf.attached +end + +function M.attach(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + bufmanager:attach(bufnr) +end + +function M.detach(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + bufmanager:detach(bufnr) +end + +function M.enableFold(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + local old = fold.setStatus(bufnr, 'start') + fold.update(bufnr) + return old +end + +function M.disableFold(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + return fold.setStatus(bufnr, 'stop') +end + +function M.foldtext() + local fs, fe = vim.v.foldstart, vim.v.foldend + local winid = api.nvim_get_current_win() + local virtText = decorator:getVirtTextAndCloseFold(winid, fs, fe, false) + if utils.has10() then + return virtText + end + local text + if next(virtText) then + text = '' + for _, chunk in ipairs(virtText) do + text = text .. chunk[1] + end + text = utils.expandTab(text, vim.bo.ts) + end + return text or utils.expandTab(api.nvim_buf_get_lines(0, fs - 1, fs, true)[1], vim.bo.ts) +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/buffer.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/buffer.lua new file mode 100644 index 00000000..47245fe0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/buffer.lua @@ -0,0 +1,246 @@ +local event = require('ufo.lib.event') + +local api = vim.api + +---@class UfoBuffer +---@field bufnr number +---@field attached boolean +---@field bt string +---@field ft string +---@field _syntax string +---@field _lines table A list of string or boolean +local Buffer = {} + +function Buffer:new(bufnr) + local o = setmetatable({}, self) + self.__index = self + o.bufnr = bufnr + o:reload() + return o +end + +function Buffer:reload() + self._changedtick = api.nvim_buf_get_changedtick(self.bufnr) + self._lines = {} + for _ = 1, api.nvim_buf_line_count(self.bufnr) do + table.insert(self._lines, false) + end +end + +function Buffer:dispose() + self.attached = false + self.bt = nil + self.ft = nil +end + +function Buffer:attach() + local bt = self:buftype() + if bt == 'terminal' or bt == 'prompt' then + return false + end + ---@diagnostic disable: redefined-local, unused-local + self.attached = api.nvim_buf_attach(self.bufnr, false, { + on_lines = function(name, bufnr, changedtick, firstLine, lastLine, + lastLineUpdated, byteCount) + if not self.attached then + return true + end + if firstLine == lastLine and lastLine == lastLineUpdated and byteCount == 0 then + return + end + -- TODO upstream bug + -- set foldmethod=expr && change lines from floating window will fire `on_lines`, + -- skip if changedtick is unchanged + if self._changedtick == changedtick then + return + end + self._changedtick = changedtick + self._lines = self:handleLinesChanged(self._lines, firstLine, lastLine, lastLineUpdated) + event:emit('BufLinesChanged', bufnr, changedtick, firstLine, lastLine, + lastLineUpdated, byteCount) + end, + on_changedtick = function(name, bufnr, changedtick) + self._changedtick = changedtick + end, + on_detach = function(name, bufnr) + event:emit('BufDetach', bufnr) + end, + on_reload = function(name, bufnr) + self:reload() + event:emit('BufReload', bufnr) + end + }) + ---@diagnostic enable: redefined-local, unused-local + if self.attached then + event:emit('BufAttach', self.bufnr) + end + return self.attached +end + +---lower is less than or equal to lnum +---@param lnum number +---@param endLnum number +---@return table[] +function Buffer:buildMissingHunk(lnum, endLnum) + local hunks = {} + local s, e + local cnt = 0 + for i = lnum, endLnum do + if not self._lines[i] then + cnt = cnt + 1 + if not s then + s = i + end + e = i + elseif e then + table.insert(hunks, {s, e}) + s, e = nil, nil + end + end + if e then + table.insert(hunks, {s, e}) + end + -- scan backward + if #hunks > 0 then + local firstHunk = hunks[1] + local fhLnum = firstHunk[1] + if fhLnum == lnum then + local i = lnum - 1 + while i > 0 do + if self._lines[i] then + break + end + i = i - 1 + end + fhLnum = i + 1 + cnt = cnt + lnum - fhLnum + firstHunk[1] = fhLnum + lnum = fhLnum + end + end + if cnt > (endLnum - lnum) / 4 and #hunks > 2 then + hunks = {{lnum, endLnum}} + end + return hunks +end + +function Buffer:sliceLines(lines, firstLine, lastLine, lastLineUpdated) + local newLines = {} + for i = 1, firstLine do + table.insert(newLines, lines[i]) + end + for _ = firstLine + 1, lastLineUpdated do + table.insert(newLines, false) + end + for i = lastLine + 1, #lines do + table.insert(newLines, lines[i]) + end + return newLines +end + +--- +---@param lines any[] +---@param firstLine number +---@param lastLine number +---@param lastLineUpdated number +---@return any[] +function Buffer:handleLinesChanged(lines, firstLine, lastLine, lastLineUpdated) + local delta = lastLineUpdated - lastLine + if delta == 0 then + for i = firstLine + 1, lastLine do + lines[i] = false + end + elseif delta > 0 then + if #lines > 800 and delta > 10 then + lines = self:sliceLines(lines, firstLine, lastLine, lastLineUpdated) + else + for i = firstLine + 1, lastLine do + lines[i] = false + end + for i = firstLine + 1, firstLine + delta do + table.insert(lines, i, false) + end + end + else + if #lines > 800 and -delta > 10 then + lines = self:sliceLines(lines, firstLine, lastLine, lastLineUpdated) + else + for i = lastLine, lastLineUpdated + 1, -1 do + table.remove(lines, i) + end + for i = firstLine + 1, lastLineUpdated do + lines[i] = false + end + end + if #lines == 0 then + lines = {false} + end + end + return lines +end + +--- +---@return number +function Buffer:changedtick() + return self._changedtick +end + +--- +---@return string +function Buffer:filetype() + if self.attached and not self.ft then + self.ft = vim.bo[self.bufnr].ft + end + return self.ft +end + +--- +---@return string +function Buffer:buftype() + if self.attached and not self.bt then + self.bt = vim.bo[self.bufnr].bt + end + return self.bt +end + +function Buffer:syntax() + if self.attached and not self.syntax then + self._syntax = vim.bo[self.bufnr].syntax + end + return self._syntax +end + +--- +---@return number +function Buffer:lineCount() + if self:buftype() == 'quickfix' then + self:reload() + end + return #self._lines +end + +---@param lnum number +---@param endLnum? number +---@return string[] +function Buffer:lines(lnum, endLnum) + local lineCount = self:lineCount() + assert(lineCount >= lnum, 'index out of bounds') + local res = {} + endLnum = endLnum and endLnum or lnum + if endLnum < 0 then + endLnum = lineCount + endLnum + 1 + end + for _, hunk in ipairs(self:buildMissingHunk(lnum, endLnum)) do + local hs, he = hunk[1], hunk[2] + local lines = api.nvim_buf_get_lines(self.bufnr, hs - 1, he, true) + for i = hs, he do + self._lines[i] = lines[i - hs + 1] + end + end + for i = lnum, endLnum do + table.insert(res, self._lines[i]) + end + return res +end + +return Buffer diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/foldbuffer.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/foldbuffer.lua new file mode 100644 index 00000000..e2bdb958 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/foldbuffer.lua @@ -0,0 +1,260 @@ +local api = vim.api +local cmd = vim.cmd + +local utils = require('ufo.utils') +local buffer = require('ufo.model.buffer') +local foldedline = require('ufo.model.foldedline') + +---@class UfoFoldBuffer +---@field bufnr number +---@field buf UfoBuffer +---@field ns number +---@field status string|'start'|'pending'|'stop' +---@field version number +---@field requestCount number +---@field foldRanges UfoFoldingRange[] +---@field foldedLines table A list of UfoFoldedLine or boolean +---@field foldedLineCount number +---@field providers table +---@field scanned boolean +---@field selectedProvider string +local FoldBuffer = setmetatable({}, buffer) +FoldBuffer.__index = FoldBuffer + +---@param buf UfoBuffer +---@return UfoFoldBuffer +function FoldBuffer:new(buf, ns) + local o = setmetatable({}, self) + self.__index = self + o.bufnr = buf.bufnr + o.buf = buf + o.ns = ns + o:reset() + return o +end + +function FoldBuffer:dispose() + self:resetFoldedLines(true) + self:reset() +end + +function FoldBuffer:changedtick() + return self.buf:changedtick() +end + +function FoldBuffer:filetype() + return self.buf:filetype() +end + +function FoldBuffer:buftype() + return self.buf:buftype() +end + +function FoldBuffer:syntax() + return self.buf:syntax() +end + +function FoldBuffer:lineCount() + return self.buf:lineCount() +end + +--- +---@param lnum number +---@param endLnum? number +---@return string[] +function FoldBuffer:lines(lnum, endLnum) + return self.buf:lines(lnum, endLnum) +end + +function FoldBuffer:reset() + self.status = 'start' + self.providers = nil + self.selectedProvider = nil + self.version = 0 + self.requestCount = 0 + self.foldRanges = {} + self:resetFoldedLines() + self.scanned = false +end + +function FoldBuffer:resetFoldedLines(clear) + self.foldedLines = {} + self.foldedLineCount = 0 + for _ = 1, self:lineCount() do + table.insert(self.foldedLines, false) + end + if clear then + pcall(api.nvim_buf_clear_namespace, self.bufnr, self.ns, 0, -1) + end +end + +function FoldBuffer:foldedLine(lnum) + local fl = self.foldedLines[lnum] + if not fl then + return + end + return fl +end + +--- +---@param winid number +---@param lnum number 1-index +---@return UfoFoldingRangeKind|'' +function FoldBuffer:lineKind(winid, lnum) + if utils.isDiffOrMarkerFold(winid) then + return '' + end + local row = lnum - 1 + for _, range in ipairs(self.foldRanges) do + if row >= range.startLine and row <= range.endLine then + return range.kind + end + end + return '' +end + +function FoldBuffer:handleFoldedLinesChanged(first, last, lastUpdated) + if self.foldedLineCount == 0 then + return + end + local didOpen = false + for i = first + 1, last do + didOpen = self:openFold(i) or didOpen + end + if didOpen and lastUpdated > first then + local winid = utils.getWinByBuf(self.bufnr) + if winid ~= -1 then + utils.winCall(winid, function() + cmd(('sil! %d,%dfoldopen!'):format(first + 1, lastUpdated)) + end) + end + end + self.foldedLines = self.buf:handleLinesChanged(self.foldedLines, first, last, lastUpdated) +end + +function FoldBuffer:acquireRequest() + self.requestCount = self.requestCount + 1 +end + +function FoldBuffer:releaseRequest() + if self.requestCount > 0 then + self.requestCount = self.requestCount - 1 + end +end + +function FoldBuffer:requested() + return self.requestCount > 0 +end + +--- +---@param lnum number +---@return boolean +function FoldBuffer:lineIsClosed(lnum) + return self:foldedLine(lnum) ~= nil +end + +--- +---@param winid number +function FoldBuffer:syncFoldedLines(winid) + for lnum, fl in ipairs(self.foldedLines) do + if fl and utils.foldClosed(winid, lnum) == -1 then + self:openFold(lnum) + end + end +end + +function FoldBuffer:getRangesFromExtmarks() + local res = {} + if self.foldedLineCount == 0 then + return res + end + local marks = api.nvim_buf_get_extmarks(self.bufnr, self.ns, 0, -1, {details = true}) + for _, m in ipairs(marks) do + local row, endRow = m[2], m[4].end_row + -- extmark may give backward range + if row > endRow then + error(('expected forward range, got row: %d, endRow: %d'):format(row, endRow)) + end + table.insert(res, {row, endRow}) + end + return res +end + +--- +---@param lnum number +---@return boolean +function FoldBuffer:openFold(lnum) + local folded = false + local fl = self.foldedLines[lnum] + if fl then + folded = self.foldedLines[lnum] ~= nil + fl:deleteExtmark() + self.foldedLineCount = self.foldedLineCount - 1 + self.foldedLines[lnum] = false + end + return folded +end + +--- +---@param lnum number +---@param endLnum number +---@param text? string +---@param virtText? string +---@param width? number +---@param doRender? boolean +---@return boolean +function FoldBuffer:closeFold(lnum, endLnum, text, virtText, width, doRender) + local lineCount = self:lineCount() + endLnum = math.min(endLnum, lineCount) + if endLnum < lnum then + return false + end + local fl = self.foldedLines[lnum] + if fl then + if width and fl:widthChanged(width) then + fl.width = width + end + if text and fl:textChanged(text) then + fl.text = text + end + if not width and not text then + return false + end + else + if self.foldedLineCount == 0 and lineCount ~= #self.foldedLines then + self:resetFoldedLines() + end + fl = foldedline:new(self.bufnr, self.ns, text, width) + self.foldedLineCount = self.foldedLineCount + 1 + self.foldedLines[lnum] = fl + end + fl:updateVirtText(lnum, endLnum, virtText, doRender) + return true +end + +function FoldBuffer:scanFoldedRanges(winid, s, e) + local res = {} + local stack = {} + s, e = s or 1, e or self:lineCount() + utils.winCall(winid, function() + for i = s, e do + local skip = false + while #stack > 0 and i >= stack[#stack] do + local endLnum = table.remove(stack) + cmd(endLnum .. 'foldclose') + skip = true + end + if not skip then + local endLnum = utils.foldClosedEnd(winid, i) + if endLnum ~= -1 then + table.insert(stack, endLnum) + table.insert(res, {i - 1, endLnum - 1}) + cmd(i .. 'foldopen') + end + end + end + end) + return res +end + +return FoldBuffer diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/foldedline.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/foldedline.lua new file mode 100644 index 00000000..2b22cc7d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/foldedline.lua @@ -0,0 +1,77 @@ +local utils = require('ufo.utils') +local api = vim.api + +---@class UfoFoldedLine +---@field id number +---@field bufnr number +---@field ns number +---@field rendered boolean +---@field text? string +---@field width? number +---@field virtText? UfoExtmarkVirtTextChunk[] +local FoldedLine = {} + +function FoldedLine:new(bufnr, ns, text, width) + local o = setmetatable({}, self) + self.__index = self + o.id = nil + o.bufnr = bufnr + o.ns = ns + o.text = text + o.width = width + o.rendered = false + o.virtText = nil + return o +end + +--- +---@param width number +---@return boolean +function FoldedLine:widthChanged(width) + return self.width ~= width +end + +function FoldedLine:textChanged(text) + return self.text ~= text +end + +function FoldedLine:hasRendered() + return self.rendered == true +end + +function FoldedLine:deleteExtmark() + if self.id then + api.nvim_buf_del_extmark(self.bufnr, self.ns, self.id) + end +end + +function FoldedLine:updateVirtText(lnum, endLnum, virtText, doRender) + if doRender then + local opts = { + id = self.id, + end_row = endLnum - 1, + end_col = 0, + priority = 10, + hl_mode = 'combine' + } + if not utils.has10() then + opts.virt_text = virtText + opts.virt_text_win_col = 0 + end + self.id = api.nvim_buf_set_extmark(self.bufnr, self.ns, lnum - 1, 0, opts) + end + self.rendered = doRender + self.virtText = virtText +end + +function FoldedLine:range() + if not self.id then + return 0, 0 + end + local mark = api.nvim_buf_get_extmark_by_id(self.bufnr, self.ns, self.id, {details = true}) + local row, details = mark[1], mark[3] + local endRow = details.end_row + return row + 1, endRow + 1 +end + +return FoldedLine diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/foldingrange.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/foldingrange.lua new file mode 100644 index 00000000..e436434f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/foldingrange.lua @@ -0,0 +1,35 @@ +---@alias UfoFoldingRangeKind +---| 'comment' +---| 'imports' +---| 'region' + +---@class UfoFoldingRange +---@field startLine number +---@field startCharacter? number +---@field endLine number +---@field endCharacter? number +---@field kind? UfoFoldingRangeKind +local FoldingRange = {} + +function FoldingRange.new(startLine, endLine, startCharacter, endCharacter, kind) + local o = {} + o.startLine = startLine + o.endLine = endLine + o.startCharacter = startCharacter + o.endCharacter = endCharacter + o.kind = kind + return o +end + +--- +---@param ranges UfoFoldingRange +function FoldingRange.sortRanges(ranges) + if jit then + return + end + table.sort(ranges, function(a, b) + return a.startLine == b.startLine and a.endLine < b.endLine or a.startLine > b.startLine + end) +end + +return FoldingRange diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/linesize.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/linesize.lua new file mode 100644 index 00000000..2f2af97f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/linesize.lua @@ -0,0 +1,119 @@ +local api = vim.api +local fn = vim.fn +local utils = require('ufo.utils') + +local LSize + +--- +---@class UfoLineSizeBase +---@field winid number +---@field foldenable boolean +---@field foldClosePairs table +---@field sizes table +local LBase = {} + +--- +---@param sizes table +---@return UfoLineSizeBase +function LBase:new(winid, sizes) + local o = setmetatable({}, self) + self.__index = self + o.winid = winid + o.foldenable = vim.wo[winid].foldenable + o.foldClosePairs = {} + o.sizes = sizes + return o +end + +--- +---@param lnum number +---@return number +function LBase:size(lnum) + return self.sizes[lnum] +end + +--- +---@class UfoLineSizeFFI : UfoLineSizeBase +---@field private _wffi UfoWffi +local LFFI = setmetatable({}, {__index = LBase}) + +--- +---@return UfoLineSizeFFI +function LFFI:new(winid) + local super = LBase:new(winid, setmetatable({}, { + __index = function(t, i) + local v = self._wffi.plinesWin(winid, i) + rawset(t, i, v) + return v + end + })) + local o = setmetatable(super, self) + self.__index = self + return o +end + +--- +---@param lnum number +---@param winheight boolean +---@return number +function LFFI:nofillSize(lnum, winheight) + winheight = winheight or true + return self._wffi.plinesWinNofill(self.winid, lnum, winheight) +end + +--- +---@param lnum number +---@return number +function LFFI:fillSize(lnum) + return self:size(lnum) - self:nofillSize(lnum, true) +end + +--- +---@class UfoLineSizeNonFFI : UfoLineSizeBase +---@field perLineWidth number +local LNonFFI = setmetatable({}, {__index = LBase}) + +--- +---@return UfoLineSizeNonFFI +function LNonFFI:new(winid) + local wrap = vim.wo[winid].wrap + local perLineWidth = api.nvim_win_get_width(winid) - utils.textoff(winid) + local super = LBase:new(winid, setmetatable({}, { + __index = function(t, i) + local v + if wrap then + v = math.ceil(math.max(fn.virtcol({i, '$'}) - 1, 1) / perLineWidth) + else + v = 1 + end + rawset(t, i, v) + return v + end + })) + local o = setmetatable(super, self) + o.perLineWidth = perLineWidth + self.__index = self + return o +end + +LNonFFI.nofillSize = LNonFFI.size + +--- +---@param _ any +---@return number +function LNonFFI.fillSize(_) + return 0 +end + +local function init() + if jit ~= nil then + LFFI._wffi = require('ufo.wffi') + LSize = LFFI + else + LSize = LNonFFI + end +end + +init() + +return LSize diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/window.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/window.lua new file mode 100644 index 00000000..82ebd74e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/model/window.lua @@ -0,0 +1,111 @@ +local utils = require('ufo.utils') + +local api = vim.api +local cmd = vim.cmd + +---@class UfoWindow +---@field winid number +---@field bufnr number +---@field lastBufnr number +---@field foldbuffer UfoFoldBuffer +---@field lastCurLnum number +---@field lastCurFoldStart number +---@field lastCurFoldEnd number +---@field isCurFoldHighlighted boolean +---@field foldedPairs table +---@field foldedTextMaps table +---@field _cursor number[] +---@field _width number +---@field _concealLevel boolean +local Window = {} +Window.__index = Window + +function Window:new(winid) + local o = self == Window and setmetatable({}, self) or self + o.winid = winid + o.bufnr = 0 + o.lastCurLnum = -1 + o.lastCurFoldStart = 0 + o.lastCurFoldEnd = 0 + o.isCurFoldHighlighted = false + return o +end + +--- Must invoke in on_win cycle +---@param bufnr number +---@param fb UfoFoldBuffer +function Window:onWin(bufnr, fb) + self.lastBufnr = self.bufnr + self.bufnr = bufnr + self.foldbuffer = fb + self.foldedPairs = {} + self.foldedTextMaps = {} + self._cursor = nil + self._width = nil + self._concealLevel = nil +end + +function Window:cursor() + if not self._cursor then + self._cursor = api.nvim_win_get_cursor(self.winid) + end + return self._cursor +end + +function Window:textWidth() + if not self._width then + local textoff = utils.textoff(self.winid) + self._width = api.nvim_win_get_width(self.winid) - textoff + end + return self._width +end + +function Window:concealLevel() + if not self._concealLevel then + self._concealLevel = vim.wo[self.winid].conceallevel + end + return self._concealLevel +end + +function Window:foldEndLnum(fs) + local fe = self.foldedPairs[fs] + if not fe then + fe = utils.foldClosedEnd(self.winid, fs) + self.foldedPairs[fs] = fe + end + return fe +end + +function Window:setCursorFoldedLineHighlight() + local res = false + if not self.isCurFoldHighlighted then + -- TODO + -- Upstream bug: Error in decoration provider (UNKNOWN PLUGIN).end + require('promise').resolve():thenCall(function() + utils.winCall(self.winid, function() + -- TODO + -- Upstream bug: `setl winhl` change curswant + local view = utils.saveView(0) + cmd('setl winhl+=CursorLine:UfoCursorFoldedLine') + utils.restView(0, view) + end) + end) + self.isCurFoldHighlighted = true + res = true + end + return res +end + +function Window:clearCursorFoldedLineHighlight() + local res = false + if self.isCurFoldHighlighted or self.lastBufnr ~= 0 and self.lastBufnr ~= self.bufnr then + utils.winCall(self.winid, function() + cmd('setl winhl-=CursorLine:UfoCursorFoldedLine') + end) + self.isCurFoldHighlighted = false + res = true + end + return res +end + +return Window diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/floatwin.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/floatwin.lua new file mode 100644 index 00000000..bab55f36 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/floatwin.lua @@ -0,0 +1,247 @@ +local api = vim.api +local fn = vim.fn +local cmd = vim.cmd + +local utils = require('ufo.utils') + +--- Singleton +---@class UfoPreviewFloatWin +---@field config table +---@field ns number +---@field winid number +---@field bufnr number +---@field bufferName string +---@field width number +---@field height number +---@field anchor string|'SW'|'NW' +---@field winblend number +---@field border string|'none'|'single'|'double'|'rounded'|'solid'|'shadow'|string[] +---@field lineCount number +---@field showScrollBar boolean +---@field topline number +---@field virtText UfoExtmarkVirtTextChunk[] +local FloatWin = {} + +local defaultBorder = { + none = {'', '', '', '', '', '', '', ''}, + single = {'┌', '─', '┐', '│', '┘', '─', '└', '│'}, + double = {'╔', '═', '╗', '║', '╝', '═', '╚', '║'}, + rounded = {'╭', '─', '╮', '│', '╯', '─', '╰', '│'}, + solid = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, + shadow = {'', '', {' ', 'FloatShadowThrough'}, {' ', 'FloatShadow'}, + {' ', 'FloatShadow'}, {' ', 'FloatShadow'}, {' ', 'FloatShadowThrough'}, ''}, +} + +local function borderHasLine(border, index) + local s = border[index] + if type(s) == 'string' then + return s ~= '' + else + return s[1] ~= '' + end +end + +function FloatWin:borderHasUpLine() + return borderHasLine(self.border, 2) +end + +function FloatWin:borderHasRightLine() + return borderHasLine(self.border, 4) +end + +function FloatWin:borderHasBottomLine() + return borderHasLine(self.border, 6) +end + +function FloatWin:borderHasLeftLine() + return borderHasLine(self.border, 8) +end + +function FloatWin:build(winid, height, border, isAbove) + local winfo = utils.getWinInfo(winid) + local aboveLine = utils.winCall(winid, fn.winline) - 1 + local belowLine = winfo.height - aboveLine + border = type(border) == 'string' and defaultBorder[border] or border + self.border = vim.deepcopy(border) + if fn.screencol() == 1 then + self.border[1], self.border[7], self.border[8] = '', '', '' + end + local row, col = 0, 0 + if isAbove then + if aboveLine < height and belowLine > aboveLine then + self.height = math.min(height, belowLine) + row = self.height - aboveLine + if self:borderHasBottomLine() then + row = row + 1 + end + if self:borderHasUpLine() then + row = row + 1 + end + else + self.height = math.min(height, aboveLine) + row = 1 + end + else + if belowLine < height and belowLine < aboveLine then + self.height = math.min(height, aboveLine) + row = belowLine - self.height + else + if self:borderHasUpLine() and fn.screenrow() == 1 and aboveLine == 0 then + self.border[1], self.border[2], self.border[3] = '', '', '' + end + self.height = math.min(height, belowLine) + row = 0 + end + if self:borderHasUpLine() then + row = row - 1 + end + end + self.width = winfo.width - winfo.textoff + if self:borderHasLeftLine() then + col = col - 1 + end + if self:borderHasRightLine() then + self.width = self.width - 1 + end + local anchor = isAbove and 'SW' or 'NW' + return { + border = self.border, + relative = 'cursor', + focusable = true, + width = self.width, + height = self.height, + anchor = anchor, + row = row, + col = col, + noautocmd = true, + zindex = 51 + } +end + +function FloatWin:validate() + return utils.isWinValid(rawget(self, 'winid')) +end + +function FloatWin.getConfig() + local config = api.nvim_win_get_config(FloatWin.winid) + local row, col = config.row, config.col + -- row and col are a table value converted from the floating-point + if type(row) == 'table' then + ---@diagnostic disable-next-line: need-check-nil, inject-field + config.row, config.col = tonumber(row[vim.val_idx]), tonumber(col[vim.val_idx]) + end + return config +end + +function FloatWin:open(wopts, enter) + if enter == nil then + enter = false + end + self.winid = api.nvim_open_win(self:getBufnr(), enter, wopts) + return self.winid +end + +function FloatWin:close() + if self:validate() then + api.nvim_win_close(self.winid, true) + end + rawset(self, 'winid', nil) +end + +function FloatWin:call(executor) + utils.winCall(self.winid, executor) +end + +function FloatWin:getBufnr() + if utils.isBufLoaded(rawget(self, 'bufnr')) then + return self.bufnr + end + local bufnr = fn.bufnr('^' .. self.bufferName .. '$') + if bufnr > 0 then + self.bufnr = bufnr + else + self.bufnr = api.nvim_create_buf(false, true) + api.nvim_buf_set_name(self.bufnr, self.bufferName) + vim.bo[self.bufnr].bufhidden = 'hide' + end + return self.bufnr +end + +function FloatWin:setContent(text) + vim.bo[self.bufnr].modifiable = true + api.nvim_buf_set_lines(self.bufnr, 0, -1, true, text) + vim.bo[self.bufnr].modifiable = false + self.lineCount = #text + self.showScrollBar = self.lineCount > self.height + api.nvim_win_set_cursor(self.winid, {1, 0}) + cmd('norm! ze') +end + +--- +---@param winid number +---@param targetHeight number +---@param enter boolean +---@param isAbove boolean +---@param postHandle? fun() +---@return number +function FloatWin:display(winid, targetHeight, enter, isAbove, postHandle) + local height = math.min(self.config.maxheight, targetHeight) + local wopts = self:build(winid, height, self.config.border, isAbove) + if self:validate() then + wopts.noautocmd = nil + api.nvim_win_set_config(self.winid, wopts) + if enter == true then + api.nvim_set_current_win(self.winid) + end + else + self:open(wopts, enter) + self.winblend = self.config.winblend + local wo = vim.wo[self.winid] + wo.wrap = false + wo.spell, wo.list = false, true + wo.nu, wo.rnu = false, false + wo.fen, wo.fdm, wo.fdc = false, 'manual', '0' + wo.cursorline = enter == true + wo.signcolumn, wo.colorcolumn = 'no', '' + if wo.so == 0 then + wo.so = 1 + end + wo.winhl = self.config.winhighlight + wo.winblend = self.winblend + end + if type(postHandle) == 'function' then + postHandle() + end + return self.winid +end + +function FloatWin:refreshTopline() + self.topline = fn.line('w0', self.winid) +end + +function FloatWin:initialize(ns, config) + self.ns = ns + local border = config.border + local tBorder = type(border) + if tBorder == 'string' then + if not defaultBorder[border] then + error(([[border string must be one of {%s}]]) + :format(table.concat(vim.tbl_keys(defaultBorder), ','))) + end + elseif tBorder == 'table' then + assert(#border == 8, 'only support 8 chars for the border') + else + error('error border config') + end + self.bufferName = 'UfoPreviewFloatWin' + self.config = config + return self +end + +function FloatWin:dispose() + self:close() + pcall(api.nvim_buf_delete, self.bufnr, {force = true}) + self.bufnr = nil +end + +return FloatWin diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/init.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/init.lua new file mode 100644 index 00000000..1c2e8c7f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/init.lua @@ -0,0 +1,364 @@ +local api = vim.api +local cmd = vim.cmd +local fn = vim.fn + +local promise = require('promise') +local render = require('ufo.render') +local utils = require('ufo.utils') +local floatwin = require('ufo.preview.floatwin') +local scrollbar = require('ufo.preview.scrollbar') +local winbar = require('ufo.preview.winbar') +local keymap = require('ufo.preview.keymap') +local event = require('ufo.lib.event') +local disposable = require('ufo.lib.disposable') +local config = require('ufo.config') +local fold = require('ufo.fold') +local highlight = require('ufo.highlight') + +---@class UfoPreview +---@field initialized boolean +---@field disposables UfoDisposable[] +---@field detachDisposables UfoDisposable[] +---@field ns number +---@field winid number +---@field bufnr number +---@field lnum number +---@field col number +---@field topline number +---@field foldedLnum number +---@field foldedEndLnum number +---@field isAbove boolean +---@field cursorSignName string +---@field cursorSignId number +---@field keyMessages table +local Preview = {} + +function Preview:trace(bufnr) + local fb = fold.get(self.bufnr) + if not fb then + return + end + local fWinConfig = floatwin.getConfig() + local wrow = fWinConfig.row + if fWinConfig.anchor == 'SW' then + wrow = wrow - fWinConfig.height + if wrow < 0 then + wrow = floatwin:borderHasUpLine() and 1 or 0 + else + if floatwin:borderHasBottomLine() then + wrow = wrow - 1 + end + end + else + if floatwin:borderHasUpLine() then + wrow = wrow + 1 + end + end + local fLnum, fWrow, col + if bufnr == self.bufnr then + fLnum, fWrow = floatwin.topline, 0 + -- did scroll, do trace base on 2nd line + if fLnum > 1 then + fLnum = fLnum + 1 + fWrow = 1 + end + else + local floatCursor = api.nvim_win_get_cursor(floatwin.winid) + fLnum = floatCursor[1] + fWrow = fLnum - floatwin.topline + col = floatCursor[2] + end + local cursor = api.nvim_win_get_cursor(self.winid) + api.nvim_set_current_win(self.winid) + local lnum = utils.foldClosed(0, cursor[1]) + fLnum - 1 + local lineSize = fWrow + wrow + cmd('norm! m`zO') + fb:syncFoldedLines(self.winid) + if bufnr == self.bufnr then + local s + s, col = fb:lines(lnum)[1]:find('^%s+%S') + col = s and col - 1 or 0 + end + local topline, topfill = utils.evaluateTopline(self.winid, lnum, lineSize) + utils.restView(0, { + lnum = lnum, + col = col, + topline = topline, + topfill = topfill, + curswant = utils.curswant(self.bufnr, lnum, col + 1) + }) +end + +function Preview:winCall(executor) + local res = false + if self.validate() then + floatwin:call(executor) + res = true + end + return res +end + +function Preview:scroll(char, toTopLeft) + if self:winCall(function() + local ctrlTbl = {B = 0x02, D = 0x04, E = 0x05, F = 0x06, U = 0x15, Y = 0x19} + cmd(('norm! %c%s'):format(ctrlTbl[char], toTopLeft and 'H_' or '')) + end) then + self:viewChanged() + end +end + +function Preview:jumpView(toBottom) + if self:winCall(function() + cmd(('norm! %s'):format(toBottom and 'GH_' or 'gg')) + end) then + self:viewChanged() + end +end + +function Preview:toggleCursor() + local bufnr = api.nvim_get_current_buf() + local floatBufnr = floatwin:getBufnr() + if self.bufnr == bufnr and self.lnum - self.foldedLnum > 0 then + self.cursorSignId = fn.sign_place(self.cursorSignId or 0, 'UfoPreview', + self.cursorSignName, floatBufnr, {lnum = self.lnum - self.foldedLnum + 1, priority = 1}) + elseif self.cursorSignId then + pcall(fn.sign_unplace, 'UfoPreview', {buffer = floatBufnr}) + self.cursorSignId = nil + end +end + +local function onBufRemap(bufnr, str) + local self = Preview + local isNormalBuf = bufnr == self.bufnr + if str == 'switch' then + if isNormalBuf then + api.nvim_set_current_win(floatwin.winid) + vim.wo.cul = true + else + vim.wo.cul = false + api.nvim_set_current_win(self.winid) + end + self:toggleCursor() + elseif str == 'trace' or str == '2click' then + self:trace(bufnr) + elseif str == 'close' then + self:close() + elseif str == 'jumpTop' then + self:jumpView(false) + elseif str == 'jumpBot' then + self:jumpView(true) + elseif str == 'scrollB' then + self:scroll('B', isNormalBuf) + elseif str == 'scrollF' then + self:scroll('F', isNormalBuf) + elseif str == 'scrollU' then + self:scroll('U', isNormalBuf) + elseif str == 'scrollD' then + self:scroll('D', isNormalBuf) + elseif str == 'scrollE' then + self:scroll('E', isNormalBuf) + elseif str == 'scrollY' then + self:scroll('Y', isNormalBuf) + elseif str == 'wheelUp' or str == 'wheelDown' then + promise.resolve():thenCall(function() + self:viewChanged() + end) + elseif str == 'onKey' then + promise.resolve():thenCall(function() + Preview:afterKey() + end) + end +end + +function Preview:attach(bufnr, winid, foldedLnum, foldedEndLnum, isAbove) + self:detach() + local disposables = {} + event:on('WinClosed', function() + promise.resolve():thenCall(function() + if not self.validate() then + self:detach() + self.close() + end + end) + end, disposables) + event:on('onBufRemap', onBufRemap, disposables) + event:emit('setOpenFoldHl', false) + table.insert(disposables, disposable:create(function() + event:emit('setOpenFoldHl') + end)) + + local view = utils.saveView(winid) + self.winid = winid + self.bufnr = bufnr + self.lnum = view.lnum + self.col = view.col + self.topline = view.topline + self.foldedLnum = foldedLnum + self.foldedEndLnum = foldedEndLnum + self.isAbove = isAbove + local floatBufnr = floatwin:getBufnr() + vim.bo[floatBufnr].iskeyword = vim.bo[bufnr].iskeyword + vim.bo[floatBufnr].tabstop = vim.bo[bufnr].tabstop + vim.bo[floatBufnr].shiftwidth = vim.bo[bufnr].shiftwidth + table.insert(disposables, disposable:create(function() + self.winid = nil + self.bufnr = nil + self.lnum = nil + self.col = nil + self.topline = nil + self.foldedLnum = nil + self.foldedEndLnum = nil + self.isAbove = nil + self.cursorSignId = nil + self.detachDisposables = nil + if utils.isBufLoaded(floatBufnr) then + api.nvim_buf_clear_namespace(floatBufnr, self.ns, 0, -1) + pcall(fn.sign_unplace, 'UfoPreview', {buffer = floatBufnr}) + if floatwin:validate() then + fn.clearmatches(floatwin.winid) + end + pcall(api.nvim_buf_call, floatBufnr, function() + cmd('setl iskeyword<') + cmd('setl topstop<') + cmd('setl shiftwidth<') + end) + end + end)) + table.insert(disposables, keymap:attach(bufnr, floatBufnr, self.ns, self.keyMessages, { + trace = self.keyMessages.trace, + switch = self.keyMessages.switch, + close = self.keyMessages.close, + ['2click'] = '<2-LeftMouse>' + })) + self.detachDisposables = disposables +end + +function Preview:detach() + if self.detachDisposables then + disposable.disposeAll(self.detachDisposables) + end +end + +function Preview:viewChanged() + floatwin:refreshTopline() + scrollbar:update() + winbar:update() +end + +function Preview:display(enter, handler) + local height = self.foldedEndLnum - self.foldedLnum + 1 + floatwin:display(self.winid, height, enter, self.isAbove, handler) + scrollbar:display() + winbar:display() +end + +--- +---@param enter? boolean +---@param nextLineIncluded? boolean +---@return number? floatWinId +function Preview:peekFoldedLinesUnderCursor(enter, nextLineIncluded) + local bufnr = api.nvim_get_current_buf() + local fb = fold.get(bufnr) + if not fb then + -- buffer is detached + return + end + local oLnum, oCol = unpack(api.nvim_win_get_cursor(0)) + local lnum = utils.foldClosed(0, oLnum) + local fl = fb.foldedLines[lnum] + if lnum == -1 or not fl then + return + end + local winid = api.nvim_get_current_win() + local endLnum = utils.foldClosedEnd(0, lnum) + local kind = fb:lineKind(winid, lnum) + local isAbove = kind == 'comment' + if not isAbove and nextLineIncluded ~= false then + endLnum = fb:lineCount() == endLnum and endLnum or (endLnum + 1) + end + self:attach(bufnr, winid, lnum, endLnum, isAbove) + floatwin.virtText = fl.virtText + local text = fb:lines(lnum, endLnum) + self:display(enter, function() + floatwin:setContent(text) + api.nvim_win_set_cursor(floatwin.winid, {oLnum - lnum + 1, oCol}) + if oLnum > lnum then + floatwin:call(utils.zz) + end + floatwin:refreshTopline() + end) + self:toggleCursor() + render.mapHighlightLimitByRange(bufnr, floatwin:getBufnr(), + {lnum - 1, 0}, {endLnum - 1, #text[endLnum - lnum + 1]}, text, self.ns) + render.mapMatchByLnum(winid, floatwin.winid, lnum, endLnum) + vim.wo[floatwin.winid].listchars = vim.wo[winid].listchars + return floatwin.winid +end + +function Preview.validate() + local res = floatwin:validate() + if floatwin.showScrollBar then + res = res and scrollbar:validate() + end + return res +end + +function Preview.close() + floatwin:close() + scrollbar:close() + winbar:close() +end + +function Preview.floatWinid() + return floatwin.winid +end + +function Preview:afterKey() + local winid = api.nvim_get_current_win() + if floatwin.winid == winid then + self:viewChanged() + return + end + if winid == self.winid then + local view = utils.saveView(winid) + if self.lnum ~= view.lnum or + self.col ~= view.col then + self.close() + elseif self.foldedLnum ~= utils.foldClosed(self.winid, self.foldedLnum) then + self.close() + elseif self.topline ~= view.topline then + if floatwin:validate() then + self:display(false) + self.topline = view.topline + end + end + else + self.close() + end +end + +function Preview:initialize(namespace) + if self.initialized then + return + end + self.initialized = true + local conf = vim.deepcopy(config.preview) + self.keyMessages = conf.mappings + self.disposables = {} + table.insert(self.disposables, disposable:create(function() + self.initialized = false + end)) + table.insert(self.disposables, floatwin:initialize(namespace, conf.win_config)) + table.insert(self.disposables, scrollbar:initialize()) + table.insert(self.disposables, winbar:initialize()) + self.ns = namespace + self.cursorSignName = highlight.signNames()['UfoPreviewCursorLine'] + return self +end + +function Preview:dispose() + disposable.disposeAll(self.disposables) + self.disposables = {} +end + +return Preview diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/keymap.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/keymap.lua new file mode 100644 index 00000000..c285e81f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/keymap.lua @@ -0,0 +1,97 @@ +local event = require('ufo.lib.event') +local utils = require('ufo.utils') + +local api = vim.api + +---@class UfoPreviewKeymap +---@field ns number +---@field bufnr number +---@field keyMessages table +---@field keyMapsBackup table +local Keymap = { + keyBackup = {} +} + +local function setKeymaps(bufnr, keyMessages) + local opts = {noremap = true, nowait = true} + local rhsFmt = [[lua require('ufo.lib.event'):emit('onBufRemap', %d, %q)]] + for msg, key in pairs(keyMessages) do + local lhs = key + local rhs = rhsFmt:format(bufnr, msg) + api.nvim_buf_set_keymap(bufnr, 'n', lhs, rhs, opts) + end +end + +function Keymap:setKeymaps() + setKeymaps(self.bufnr, self.keyMessages) +end + +function Keymap:restoreKeymaps() + if utils.isBufLoaded(self.bufnr) then + for _, key in pairs(self.keyMessages) do + pcall(api.nvim_buf_del_keymap, self.bufnr, 'n', key) + end + for _, k in ipairs(self.keyBackup) do + api.nvim_buf_set_keymap(self.bufnr, 'n', k.lhs, k.rhs, k.opts) + end + end + self.keyBackup = {} +end + +function Keymap:saveKeymaps() + local keys = {} + for _, v in pairs(self.keyMessages) do + if v:match('^<.*>$') then + v = v:upper() + end + keys[v] = true + end + for _, k in ipairs(api.nvim_buf_get_keymap(self.bufnr, 'n')) do + if keys[k.lhs] then + local opts = { + callback = k.callback, + expr = k.expr == 1, + noremap = k.noremap == 1, + nowait = k.nowait == 1, + silent = k.silent == 1 + } + table.insert(self.keyBackup, {lhs = k.lhs, rhs = k.rhs or '', opts = opts}) + end + end +end + +--- +---@param bufnr number +---@param namespace number +---@param keyMessages table +---@param floatKeyMessages table +---@return UfoPreviewKeymap +function Keymap:attach(bufnr, floatBufnr, namespace, keyMessages, floatKeyMessages) + self.bufnr = bufnr + self.ns = namespace + self.keyMessages = keyMessages + self:saveKeymaps() + self:setKeymaps() + setKeymaps(floatBufnr, floatKeyMessages) + vim.on_key(function(char) + local b1, b2, b3 = char:byte(1, -1) + -- 0x80, 0xfd, 0x4b + -- 0x80, 0xfd, 0x4c + if b1 == 0x80 and b2 == 0xfd then + if b3 == 0x4b then + event:emit('onBufRemap', bufnr, 'wheelUp') + elseif b3 == 0x4c then + event:emit('onBufRemap', bufnr, 'wheelDown') + end + end + event:emit('onBufRemap', bufnr, 'onKey') + end, namespace) + return self +end + +function Keymap:dispose() + vim.on_key(nil, self.ns) + self:restoreKeymaps() +end + +return Keymap diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/scrollbar.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/scrollbar.lua new file mode 100644 index 00000000..76e1f2cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/scrollbar.lua @@ -0,0 +1,108 @@ +local api = vim.api + +local extmark = require('ufo.render.extmark') +local FloatWin = require('ufo.preview.floatwin') + +--- Singleton +---@class UfoPreviewScrollBar : UfoPreviewFloatWin +---@field winid number +---@field bufnr number +---@field bufferName string +local ScrollBar = setmetatable({}, {__index = FloatWin}) + +function ScrollBar:build() + local config = FloatWin.getConfig() + local row, col, height = config.row, config.col + config.width, config.height + local anchor, zindex = config.anchor, config.zindex + if anchor == 'NW' then + row = self:borderHasUpLine() and row + 1 or row + else + row = (self:borderHasBottomLine() and row - 1 or row) - height + row = math.max(row, self:borderHasUpLine() and 1 or 0) + end + return vim.tbl_extend('force', config, { + anchor = 'NW', + width = 1, + row = row, + col = self:borderHasLeftLine() and col + 1 or col, + style = 'minimal', + noautocmd = true, + focusable = false, + border = 'none', + zindex = zindex + 2 + }) +end + +function ScrollBar:floatWinid() + return FloatWin.winid +end + +function ScrollBar:update() + if not self.showScrollBar then + self.winid = nil + return + end + local barSize = math.ceil(self.height * self.height / self.lineCount) + if barSize == self.height and barSize < self.lineCount then + barSize = self.height - 1 + end + + local barPos = math.ceil(self.height * self.topline / self.lineCount) + local size = barPos + barSize - 1 + if size == self.height then + if self.topline + self.height - 1 < self.lineCount then + barPos = barPos - 1 + end + elseif size > self.height then + barPos = self.height - barSize + 1 + end + + if self:borderHasRightLine() then + local wopts = self:build() + wopts.height = math.max(1, barSize) + wopts.row = wopts.row + barPos - 1 + wopts.noautocmd = nil + api.nvim_win_set_config(self.winid, wopts) + vim.wo[self.winid].winhl = 'Normal:UfoPreviewThumb' + else + api.nvim_buf_clear_namespace(self.bufnr, self.ns, 0, -1) + for i = 1, self.height do + if i >= barPos and i < barPos + barSize then + extmark.setHighlight(self.bufnr, self.ns, i - 1, 0, i - 1, 1, 'UfoPreviewThumb') + end + end + end +end + +function ScrollBar:display() + if not self.showScrollBar then + self:close() + return + end + local wopts = self:build() + if self:validate() then + wopts.noautocmd = nil + api.nvim_win_set_config(self.winid, wopts) + else + ScrollBar:open(wopts) + local wo = vim.wo[self.winid] + wo.winhl = 'Normal:UfoPreviewSbar' + wo.winblend = self.winblend + end + local lines = {} + for _ = 1, self.height do + table.insert(lines, ' ') + end + vim.bo[self.bufnr].modifiable = true + api.nvim_buf_set_lines(self.bufnr, 0, -1, true, lines) + vim.bo[self.bufnr].modifiable = false + self:update() + return self.winid +end + +function ScrollBar:initialize() + self.bufferName = 'UfoPreviewScrollBar' + return self +end + +return ScrollBar diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/winbar.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/winbar.lua new file mode 100644 index 00000000..3b5af107 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/preview/winbar.lua @@ -0,0 +1,80 @@ +local api = vim.api + +local render = require('ufo.render') +local FloatWin = require('ufo.preview.floatwin') + +--- Singleton +---@class UfoPreviewWinBar : UfoPreviewFloatWin +---@field winid number +---@field bufnr number +---@field bufferName string +---@field virtTextId number +---@field virtText UfoExtmarkVirtTextChunk[] +local WinBar = setmetatable({}, {__index = FloatWin}) + +function WinBar:build() + local config = FloatWin.getConfig() + local row, col, height = config.row, config.col, config.height + local anchor, zindex = config.anchor, config.zindex + if anchor == 'NW' then + row = self:borderHasUpLine() and row + 1 or row + else + row = (self:borderHasBottomLine() and row - 1 or row) - height + row = math.max(row, self:borderHasUpLine() and 1 or 0) + end + return vim.tbl_extend('force', config, { + anchor = 'NW', + height = 1, + row = row, + col = self:borderHasLeftLine() and col + 1 or col, + style = 'minimal', + noautocmd = true, + focusable = false, + border = 'none', + zindex = zindex + 1 + }) +end + +function WinBar:floatWinid() + return FloatWin.winid +end + +function WinBar:update() + if self.topline == 1 then + self:close() + return + end + if not self:validate() then + self:display() + end + self.virtTextId = render.setVirtText(self.bufnr, self.ns, 0, 0, self.virtText, { + id = self.virtTextId + }) +end + +function WinBar:display() + if self.topline == 1 then + self:close() + return + end + local wopts = self:build() + if self:validate() then + wopts.noautocmd = nil + api.nvim_win_set_config(self.winid, wopts) + else + WinBar:open(wopts) + local wo = vim.wo[self.winid] + wo.winhl = 'Normal:UfoPreviewWinBar' + wo.winblend = self.winblend + end + self:update() + return self.winid +end + +function WinBar:initialize() + self.bufferName = 'UfoPreviewWinBar' + self.virtTextId = nil + return self +end + +return WinBar diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/indent.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/indent.lua new file mode 100644 index 00000000..5dd00cdc --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/indent.lua @@ -0,0 +1,69 @@ +local foldingrange = require('ufo.model.foldingrange') +local bufmanager = require('ufo.bufmanager') + +local Indent = {} + +function Indent.getFolds(bufnr) + local buf = bufmanager:get(bufnr) + if not buf then + return + end + local lines = buf:lines(1, -1) + local ts = vim.bo[bufnr].ts + local sw = vim.bo[bufnr].sw + sw = sw == 0 and ts or sw + local levels = {} + for _, line in ipairs(lines) do + local level = -1 + local n = 0 + for col = 1, #line do + -- compare byte is slightly faster than a char in the string + local b = line:byte(col, col) + if b == 0x20 then + -- ' ' + n = n + 1 + elseif b == 0x09 then + -- '\t' + n = n + (ts - (n % ts)) + else + level = math.ceil(n / sw) + break + end + end + table.insert(levels, level) + end + + local ranges = {} + local stack = {} + + local function pop(curLevel, lastLnum) + while #stack > 0 do + local data = stack[#stack] + local level, lnum = data[1], data[2] + if level >= curLevel then + table.insert(ranges, foldingrange.new(lnum - 1, lastLnum - 1)) + table.remove(stack) + else + break + end + end + end + + local lastLnum = 1 + local lastLevel = levels[1] + for i, level in ipairs(levels) do + if level >= 0 then + if level > 0 and level > lastLevel then + table.insert(stack, {lastLevel, lastLnum}) + elseif level < lastLevel then + pop(level, lastLnum) + end + lastLevel = level + lastLnum = i + end + end + pop(0, lastLnum) + return ranges +end + +return Indent diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/init.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/init.lua new file mode 100644 index 00000000..f5302ae8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/init.lua @@ -0,0 +1,82 @@ +local uv = vim.loop + +local promise = require('promise') +local log = require('ufo.lib.log') + +---@class Provider UfoProvider +---@field modulePathPrefix string +---@field innerProviders string[] +---@field modules table +local Provider = { + modulePathPrefix = 'ufo.provider.', + innerProviders = {'lsp', 'treesitter', 'indent'} +} + +local function needFallback(reason) + return type(reason) == 'string' and reason:match('UfoFallbackException') +end + +function Provider:getFunction(m) + return type(m) == 'string' and self.modules[m].getFolds or m +end + +--- +---@param providers table +---@param bufnr number +---@return Promise +function Provider:requestFoldingRange(providers, bufnr) + local main, fallback = providers[1], providers[2] + local mainFunc = self:getFunction(main) + + local s + if log.isEnabled('debug') then + s = uv.hrtime() + end + local p = promise(function(resolve) + resolve(mainFunc(bufnr)) + end):thenCall(function(value) + return {main, value} + end, function(reason) + if needFallback(reason) then + local fallbackFunc = self:getFunction(fallback) + if fallbackFunc then + return {fallback, fallbackFunc(bufnr)} + else + return {main, nil} + end + else + return promise.reject(reason) + end + end) + if log.isEnabled('debug') then + p = p:finally(function() + log.debug(('requestFoldingRange(%s, %d) has elapsed: %dms') + :format(vim.inspect(providers, {indent = '', newline = ' '}), + bufnr, (uv.hrtime() - s) / 1e6)) + end) + end + return p +end + +function Provider:initialize() + self.modules = setmetatable({}, { + __index = function(t, k) + local ok, res = pcall(require, self.modulePathPrefix .. k) + assert(ok, ([[Can't find a module in `%s%s`]]):format(self.modulePathPrefix, k)) + rawset(t, k, res) + return res + end + }) + return self +end + +function Provider:dispose() + for _, name in ipairs(self.innerProviders) do + local module = _G.package.loaded[self.modulePathPrefix .. name] + if module and module.dispose then + module:dispose() + end + end +end + +return Provider diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/coc.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/coc.lua new file mode 100644 index 00000000..ff6a2809 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/coc.lua @@ -0,0 +1,68 @@ +local fn = vim.fn + +local promise = require('promise') + +---@class UfoLspCocClient +---@field initialized boolean +---@field enabled boolean +local CocClient = { + initialized = false, + enabled = false, +} + +---@param action string +---@vararg any +---@return Promise +function CocClient.action(action, ...) + local args = {...} + return promise(function(resolve, reject) + table.insert(args, function(err, res) + if err ~= vim.NIL then + if type(err) == 'string' and + (err:match('service not started') or err:match('Plugin not ready')) then + resolve() + else + reject(err) + end + else + if res == vim.NIL then + res = nil + end + resolve(res) + end + end) + fn.CocActionAsync(action, unpack(args)) + end) +end + +---@param name string +---@vararg any +---@return Promise +function CocClient.runCommand(name, ...) + return CocClient.action('runCommand', name, ...) +end + +--- +---@param bufnr number +---@param kind? string|'comment'|'imports'|'region' +---@return Promise +function CocClient.requestFoldingRange(bufnr, kind) + if not CocClient.initialized or not CocClient.enabled then + return promise.reject('UfoFallbackException') + end + return CocClient.runCommand('ufo.foldingRange', bufnr, kind) +end + +function CocClient.handleInitNotify() + if not CocClient.initialized then + CocClient.initialized = true + end + CocClient.enabled = true +end + +function CocClient.handleDisposeNotify() + CocClient.initialized = false + CocClient.enabled = false +end + +return CocClient diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/fastfailure.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/fastfailure.lua new file mode 100644 index 00000000..8518ef9d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/fastfailure.lua @@ -0,0 +1,18 @@ +local promise = require('promise') + +---@class UfoLspFastFailure +---@field initialized boolean +local FastFailure = { + initialized = false +} + +--- +---@param bufnr number +---@param kind? string|'comment'|'imports'|'region' +---@return Promise +---@diagnostic disable-next-line: unused-local +function FastFailure.requestFoldingRange(bufnr, kind) + return promise.reject('UfoFallbackException') +end + +return FastFailure diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/init.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/init.lua new file mode 100644 index 00000000..4e2dba88 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/init.lua @@ -0,0 +1,114 @@ +local uv = vim.loop + +local promise = require('promise') +local utils = require('ufo.utils') +local log = require('ufo.lib.log') +local bufmanager = require('ufo.bufmanager') + +---@class UfoLSPProviderContext +---@field timestamp number +---@field count number + +---@class UfoLSPProvider +---@field provider table +---@field hasProviders table +---@field providerContext table +local LSP = { + hasProviders = {}, + providerContext = {} +} + + +function LSP:hasInitialized() + return self.provider and self.provider.initialized +end + +function LSP:initialize() + return utils.wait(1500):thenCall(function() + local cocInitlized = vim.g.coc_service_initialized + local module + if _G.package.loaded['vim.lsp'] and (not cocInitlized or cocInitlized ~= 1) then + module = 'nvim' + elseif cocInitlized and cocInitlized == 1 then + module = 'coc' + else + module = 'fastfailure' + end + log.debug(('using %s as a lsp provider'):format(module)) + self.provider = require('ufo.provider.lsp.' .. module) + end) +end + +function LSP:request(bufnr) + local buf = bufmanager:get(bufnr) + if not buf then + return promise.resolve() + end + local bt = buf:buftype() + if bt ~= '' and bt ~= 'acwrite' then + return bt == 'nofile' and promise.reject('UfoFallbackException') or promise.resolve() + end + local ft = buf:filetype() + local hasProvider = self.hasProviders[ft] + local firstCheckFt = false + if hasProvider == nil then + local context = self.providerContext[ft] + if not context then + firstCheckFt = true + self.providerContext[ft] = {timestamp = uv.hrtime(), count = 0} + else + -- after 120 seconds and count is equal or greater than 5 + if uv.hrtime() - context.timestamp > 1.2e11 and context.count >= 5 then + self.hasProviders[ft] = false + hasProvider = false + self.providerContext[ft] = nil + end + end + end + local provider = self.provider + if provider.initialized and hasProvider ~= false then + local p + if firstCheckFt then + -- wait for the server, is 500ms enough? + p = utils.wait(500):thenCall(function() + return provider.requestFoldingRange(bufnr) + end) + else + p = provider.requestFoldingRange(bufnr) + end + if hasProvider == nil then + p = p:thenCall(function(value) + self.hasProviders[ft] = true + self.providerContext[ft] = nil + return value + end, function(reason) + local context = self.providerContext[ft] + if context then + self.providerContext[ft].count = context.count + 1 + end + return promise.reject(reason) + end) + end + return p + else + return promise.reject('UfoFallbackException') + end +end + +function LSP.getFolds(bufnr) + local self = LSP + if not self:hasInitialized() then + return self:initialize():thenCall(function() + return self:request(bufnr) + end) + end + return self:request(bufnr) +end + +function LSP:dispose() + self.provider = nil + self.hasProviders = {} + self.providerContext = {} +end + +return LSP diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/nvim.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/nvim.lua new file mode 100644 index 00000000..0a2ecf15 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/lsp/nvim.lua @@ -0,0 +1,95 @@ +local util = require('vim.lsp.util') +local promise = require('promise') +local utils = require('ufo.utils') +local async = require('async') +local log = require('ufo.lib.log') +local foldingrange = require('ufo.model.foldingrange') + +---@class UfoLspNvimClient +---@field initialized boolean +local NvimClient = { + initialized = true +} + +local errorCodes = { + -- Defined by JSON RPC + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, + serverErrorStart = -32099, + serverErrorEnd = -32000, + ServerNotInitialized = -32002, + UnknownErrorCode = -32001, + -- Defined by the protocol. + RequestCancelled = -32800, + RequestFailed = -32803, + ContentModified = -32801, +} + +local vimLspGetClients = vim.lsp.get_clients and vim.lsp.get_clients or vim.lsp.get_active_clients + +function NvimClient.request(client, method, params, bufnr) + return promise(function(resolve, reject) + client.request(method, params, function(err, res) + if err then + log.error('Received error in callback', err) + log.error('Client:', client) + log.error('All clients:', vimLspGetClients({bufnr = bufnr})) + local code = err.code + if code == errorCodes.RequestCancelled or code == errorCodes.ContentModified or code == errorCodes.RequestFailed then + reject('UfoFallbackException') + else + reject(err) + end + else + resolve(res) + end + end, bufnr) + end) +end + +local function getClients(bufnr) + local clients = vimLspGetClients({bufnr = bufnr}) + return vim.tbl_filter(function(client) + if vim.tbl_get(client.server_capabilities, 'foldingRangeProvider') then + return true + else + return false + end + end, clients) +end + +function NvimClient.requestFoldingRange(bufnr, kind) + return async(function() + if not utils.isBufLoaded(bufnr) then + return + end + local clients = getClients(bufnr) + if #clients == 0 then + await(utils.wait(500)) + clients = getClients(bufnr) + end + -- TODO + -- How to get the highest priority for the client? + local client = clients[1] + if not client then + error('UfoFallbackException') + end + local params = {textDocument = util.make_text_document_params(bufnr)} + return NvimClient.request(client, 'textDocument/foldingRange', params, bufnr) + :thenCall(function(ranges) + if not ranges then + return {} + end + ranges = vim.tbl_filter(function(o) + return (not kind or kind == o.kind) and o.startLine < o.endLine + end, ranges) + foldingrange.sortRanges(ranges) + return ranges + end) + end) +end + +return NvimClient diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/treesitter.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/treesitter.lua new file mode 100644 index 00000000..ae9094c0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/provider/treesitter.lua @@ -0,0 +1,193 @@ +local bufmanager = require('ufo.bufmanager') +local foldingrange = require('ufo.model.foldingrange') + +---@class UfoTreesitterProvider +---@field hasProviders table +local Treesitter = { + hasProviders = {} +} + +---@diagnostic disable: deprecated +---@return vim.treesitter.LanguageTree|nil parser for the buffer, or nil if parser is not available +local function getParser(bufnr, lang) + local ok, parser = pcall(vim.treesitter.get_parser, bufnr, lang) + if not ok then + return nil + end + return parser +end +local get_query = assert(vim.treesitter.query.get or vim.treesitter.query.get_query) +local get_query_files = assert(vim.treesitter.query.get_files or vim.treesitter.query.get_query_files) +---@diagnostic enable: deprecated + + +-- Backward compatibility for the dummy directive (#make-range!), +-- which no longer exists in nvim-treesitter v1.0+ +if not vim.tbl_contains(vim.treesitter.query.list_directives(), "make-range!") then + vim.treesitter.query.add_directive("make-range!", function() end, {}) +end + +local MetaNode = {} +MetaNode.__index = MetaNode + +function MetaNode:new(range) + local o = self == MetaNode and setmetatable({}, self) or self + o.value = range + return o +end + +function MetaNode:range() + local range = self.value + return range[1], range[2], range[3], range[4] +end + +--- Return a meta node that represents a range between two nodes, i.e., (#make-range!), +--- that is similar to the legacy TSRange.from_node() from nvim-treesitter. +function MetaNode.from_nodes(start_node, end_node) + local start_pos = { start_node:start() } + local end_pos = { end_node:end_() } + return MetaNode:new({ + [1] = start_pos[1], + [2] = start_pos[2], + [3] = end_pos[1], + [4] = end_pos[2], + }) +end + +local function prepareQuery(bufnr, parser, root, rootLang, queryName) + if not root then + local firstTree = parser:trees()[1] + if firstTree then + root = firstTree:root() + else + return + end + end + + local range = {root:range()} + + if not rootLang then + local langTree = parser:language_for_range(range) + if langTree then + rootLang = langTree:lang() + else + return + end + end + + return get_query(rootLang, queryName), { + root = root, + source = bufnr, + start = range[1], + -- The end row is exclusive so we need to add 1 to it. + stop = range[3] + 1, + } +end + +local function iterFoldMatches(bufnr, parser, root, rootLang) + local q, p = prepareQuery(bufnr, parser, root, rootLang, 'folds') + if not q then + return function() end + end + ---@diagnostic disable-next-line: need-check-nil + local iter = q:iter_matches(p.root, p.source, p.start, p.stop) + return function() + local pattern, match, metadata = iter() + local matches = {} + if pattern == nil then + return pattern + end + + -- Extract capture names from each match + for id, node in pairs(match) do + local m = metadata[id] + if m and m.range then + node = MetaNode:new(m.range) + end + table.insert(matches, node) + end + + -- Add some predicates for testing + local preds = q.info.patterns[pattern] + if preds then + for _, pred in pairs(preds) do + if pred[1] == 'make-range!' and type(pred[2]) == 'string' and #pred == 4 then + local node = MetaNode.from_nodes(match[pred[3]], match[pred[4]]) + table.insert(matches, node) + end + end + end + return matches + end +end + +local function getFoldMatches(res, bufnr, parser, root, lang) + for matches in iterFoldMatches(bufnr, parser, root, lang) do + for _, node in ipairs(matches) do + table.insert(res, node) + end + end + return res +end + +local function getCpatureMatchesRecursively(bufnr, parser) + local noQuery = true + local res = {} + parser:for_each_tree(function(tree, langTree) + local lang = langTree:lang() + local has_folds = #get_query_files(lang, 'folds', nil) > 0 + if has_folds then + noQuery = false + getFoldMatches(res, bufnr, parser, tree:root(), lang) + end + end) + return not noQuery, res +end + +function Treesitter.getFolds(bufnr) + local buf = bufmanager:get(bufnr) + if not buf then + return + end + local bt = buf:buftype() + if bt ~= '' and bt ~= 'acwrite' then + if bt == 'nofile' then + error('UfoFallbackException') + end + return + end + local self = Treesitter + local ft = buf:filetype() + if self.hasProviders[ft] == false then + error('UfoFallbackException') + end + local parser = getParser(bufnr) + if not parser then + self.hasProviders[ft] = false + error('UfoFallbackException') + end + + local ranges = {} + local ok, matches = getCpatureMatchesRecursively(bufnr, parser) + if not ok then + self.hasProviders[ft] = false + error('UfoFallbackException') + end + for _, node in ipairs(matches) do + local start, _, stop, stop_col = node:range() + if stop_col == 0 then + stop = stop - 1 + end + if stop > start then + table.insert(ranges, foldingrange.new(start, stop)) + end + end + foldingrange.sortRanges(ranges) + return ranges +end + +function Treesitter:dispose() + self.hasProviders = {} +end + +return Treesitter diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/extmark.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/extmark.lua new file mode 100644 index 00000000..1ea70f95 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/extmark.lua @@ -0,0 +1,63 @@ +local api = vim.api + +local M = {} + +--- +---@param bufnr number +---@param startRange number[] +---@param endRange number[] +---@param namespaces number[] +---@return table, table +function M.getHighlightsAndInlayByRange(bufnr, startRange, endRange, namespaces) + local hlRes, inlayRes = {}, {} + local endRow, endCol = endRange[1], endRange[2] + for _, ns in pairs(namespaces) do + local marks = api.nvim_buf_get_extmarks(bufnr, ns, startRange, endRange, {details = true}) + for _, m in ipairs(marks) do + local sr, sc, details = m[2], m[3], m[4] + local er = details.end_row or sr + local ec = details.end_col or (sc + 1) + local hlGroup = details.hl_group + local priority = details.priority + local conceal = details.conceal + local virtTextPos = details.virt_text_pos + if hlGroup then + if er > endRow then + er, ec = endRow, endCol + elseif er == endRow and ec > endCol then + er = endCol + end + table.insert(hlRes, {sr, sc, er, ec, hlGroup, priority, conceal}) + end + if virtTextPos == 'inline' then + table.insert(inlayRes, {sr, sc, details.virt_text, priority}) + end + end + end + return hlRes, inlayRes +end + +function M.setHighlight(bufnr, ns, row, col, endRow, endCol, hlGroup, priority) + return api.nvim_buf_set_extmark(bufnr, ns, row, col, { + end_row = endRow, + end_col = endCol, + hl_group = hlGroup, + priority = priority + }) +end + +function M.setVirtText(bufnr, ns, row, col, virtText, opts) + opts = opts or {} + local textPos = opts.virt_text_pos + local winCol = not textPos and col == 0 and 0 or nil + return api.nvim_buf_set_extmark(bufnr, ns, row, col, { + id = opts.id, + virt_text = virtText, + virt_text_win_col = winCol, + virt_text_pos = textPos or 'eol', + priority = opts.priority or 10, + hl_mode = opts.hl_mode or 'combine' + }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/init.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/init.lua new file mode 100644 index 00000000..497956e8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/init.lua @@ -0,0 +1,297 @@ +local api = vim.api +local fn = vim.fn + +local highlight = require('ufo.highlight') +local extmark = require('ufo.render.extmark') +local treesitter = require('ufo.render.treesitter') +local match = require('ufo.render.match') +local utils = require('ufo.utils') + +local M = {} + +local function fillSlots(marks, len, concealLevel) + local res = {} + local prioritySlots = {} + local hlGroups = highlight.hlGroups() + local concealEabnled = concealLevel > 0 + for _, m in ipairs(marks) do + ---@type 'string'|'number' + local hlGroup = m[5] + local cchar = m[7] + local isConcealSlot = concealEabnled and cchar + if isConcealSlot or hlGroup and hlGroups[hlGroup].foreground then + local col, endCol, priority = m[2], m[4], m[6] + if endCol == -1 then + endCol = len + end + if isConcealSlot and concealLevel == 3 then + cchar = '' + end + local e = isConcealSlot and {col + 1, cchar, hlGroup} or hlGroup + for i = col + 1, endCol do + local oPriority = prioritySlots[i] + local oType = type(res[i]) + if oType == 'nil' then + res[i], prioritySlots[i] = e, priority + elseif oType == 'string' or oType == 'number' then + if isConcealSlot or oPriority <= priority then + res[i], prioritySlots[i] = e, priority + end + else + if isConcealSlot and oPriority <= priority then + res[i], prioritySlots[i] = e, priority + end + end + end + end + end + return res +end + +local function handleSyntaxSlot(slotData, slotLen, bufnr, lnum, syntax, concealEnabled) + if not syntax and not concealEnabled then + return + end + api.nvim_buf_call(bufnr, function() + local lastConcealId = -1 + local lastConcealCol = 0 + for i = 1, slotLen do + if concealEnabled then + local concealed = fn.synconcealed(lnum, i) + if concealed[1] == 1 then + local cchar, concealId = concealed[2], concealed[3] + if concealId ~= lastConcealId then + if type(slotData[i]) ~= 'table' or slotData[i][1] ~= i then + slotData[i] = {i, cchar, 'Conceal'} + end + lastConcealCol = i + lastConcealId = concealId + else + slotData[i] = {lastConcealCol, cchar, 'Conceal'} + end + end + end + if syntax and not slotData[i] then + local hlGroupId = fn.synID(lnum, i, true) + if hlGroupId ~= 0 then + slotData[i] = hlGroupId + end + end + end + end) +end + +-- 1-indexed +local function syntaxToRowHighlightRange(res, lnum, startCol, endCol) + local lastIndex = 1 + local lastHlId + for c = startCol, endCol do + local hlId = fn.synID(lnum, c, true) + if lastHlId and lastHlId ~= hlId then + table.insert(res, {lnum, lastIndex, c - 1, lastHlId}) + lastIndex = c + end + lastHlId = hlId + end + table.insert(res, {lnum, lastIndex, endCol, lastHlId}) +end + +local function mapHighlightMarkers(bufnr, startRow, marks, hlGroups, ns) + for _, m in ipairs(marks) do + local hlGroup = m[5] + if next(hlGroups[hlGroup]) then + local sr, sc = m[1] - startRow, m[2] + local er, ec = m[3] - startRow, m[4] + extmark.setHighlight(bufnr, ns, sr, sc, er, ec, hlGroup, m[6]) + end + end +end + +local function mapInlayMarkers(bufnr, startRow, marks, ns) + for _, m in ipairs(marks) do + local sr, sc = m[1] - startRow, m[2] + extmark.setVirtText(bufnr, ns, sr, sc, m[3], { + priority = m[4], + virt_text_pos = 'inline' + }) + end +end + +function M.mapHighlightLimitByRange(srcBufnr, dstBufnr, startRange, endRange, text, ns) + local startRow, startCol = startRange[1], startRange[1] + local endRow, endCol = endRange[1], endRange[2] + local nss = {} + for _, namespace in pairs(api.nvim_get_namespaces()) do + if ns ~= namespace then + table.insert(nss, namespace) + end + end + local hlGroups = highlight.hlGroups() + local hlMarks, inlayMarks = extmark.getHighlightsAndInlayByRange(srcBufnr, startRange, endRange, nss) + mapHighlightMarkers(dstBufnr, startRow, hlMarks, hlGroups, ns) + hlMarks = treesitter.getHighlightsByRange(srcBufnr, startRange, endRange, hlGroups) + mapHighlightMarkers(dstBufnr, startRow, hlMarks, hlGroups, ns) + if vim.bo[srcBufnr].syntax ~= '' then + api.nvim_buf_call(srcBufnr, function() + local res = {} + local lnum, endLnum = startRow + 1, endRow + 1 + if lnum == endLnum then + syntaxToRowHighlightRange(res, lnum, startCol + 1, endCol) + else + for l = lnum, endLnum - 1 do + syntaxToRowHighlightRange(res, l, 1, #text[l - lnum + 1]) + end + syntaxToRowHighlightRange(res, endLnum, 1, endCol) + end + for _, r in ipairs(res) do + local row = r[1] - lnum + extmark.setHighlight(dstBufnr, ns, row, r[2] - 1, row, r[3], r[4], 1) + end + end) + end + mapInlayMarkers(dstBufnr, startRow, inlayMarks, ns) +end + +function M.mapMatchByLnum(srcWinid, dstWinid, lnum, endLnum) + local res = match.mapHighlightsByLnum(srcWinid, lnum, endLnum) + if not vim.tbl_isempty(res) then + fn.setmatches(res, dstWinid) + end +end + +function M.setVirtText(bufnr, ns, row, col, virtText, opts) + return extmark.setVirtText(bufnr, ns, row, col, virtText, opts) +end + +function M.captureVirtText(bufnr, text, lnum, syntax, namespaces, concealLevel) + local len = #text + if len == 0 then + return {{'', 'UfoFoldedFg'}} + end + + local extMarks, inlayMarks = extmark.getHighlightsAndInlayByRange(bufnr, {lnum - 1, 0}, {lnum - 1, len}, namespaces) + local tsMarks = treesitter.getHighlightsByRange(bufnr, {lnum - 1, 0}, {lnum - 1, len}) + + local marks = {} + for _, m in ipairs(extMarks) do + table.insert(marks, m) + end + for _, m in ipairs(tsMarks) do + table.insert(marks, m) + end + + table.sort(inlayMarks, function(a, b) + local aCol, bCol, aPriority, bPriority = a[2], b[2], a[4], b[4] + if aCol == bCol then + return aPriority > bPriority + else + return aCol > bCol + end + end) + + local sData = fillSlots(marks, len, concealLevel) + handleSyntaxSlot(sData, len, bufnr, lnum, syntax, concealLevel > 0) + local virtText = {} + local inlayMark = table.remove(inlayMarks) + local shouldInsertChunk = true + for i = 1, len do + local e = sData[i] or 'UfoFoldedFg' + local eType = type(e) + if eType == 'table' then + local startCol, cchar, hlGroup = e[1], e[2], e[3] + if startCol == i then + table.insert(virtText, {cchar, hlGroup}) + end + shouldInsertChunk = true + else + local lastChunk = virtText[#virtText] or {} + if shouldInsertChunk or e ~= lastChunk[2] then + table.insert(virtText, {{i, i}, e}) + shouldInsertChunk = false + else + lastChunk[1][2] = i + end + end + + -- insert inlay hints + while inlayMark and inlayMark[2] == i do + for _, chunk in ipairs(inlayMark[3]) do + table.insert(virtText, chunk) + end + inlayMark = table.remove(inlayMarks) + shouldInsertChunk = true + end + end + + for _, chunk in ipairs(virtText) do + local e1, e2 = chunk[1], chunk[2] + if type(e1) == 'table' then + local sc, ec = e1[1], e1[2] + chunk[1] = text:sub(sc, ec) + end + if e2 == 'Normal' then + chunk[2] = 'UfoFoldedFg' + end + end + return virtText +end + +---Prefer use nvim_buf_set_extmark rather than matchaddpos, only use matchaddpos if buffer is shared +---with multiple windows in current tabpage. +---Check out https://github.com/neovim/neovim/issues/20208 for detail. +---@param handle number +---@param hlGroup string +---@param ns number +---@param start number +---@param finish number +---@param delay? number +---@param shared? boolean +---@return Promise +function M.highlightLinesWithTimeout(handle, hlGroup, ns, start, finish, delay, shared) + vim.validate({ + handle = {handle, 'number'}, + hlGroup = {hlGroup, 'string'}, + ns = {ns, 'number'}, + start = {start, 'number'}, + finish = {finish, 'number'}, + delay = {delay, 'number', true}, + shared = {shared, 'boolean', true}, + }) + local ids = {} + local onFulfilled + if shared then + local prior = 10 + local l = {} + for i = start, finish do + table.insert(l, {i}) + if i % 8 == 0 then + table.insert(ids, fn.matchaddpos(hlGroup, l, prior)) + l = {} + end + end + if #l > 0 then + table.insert(ids, fn.matchaddpos(hlGroup, l, prior)) + end + onFulfilled = function() + for _, id in ipairs(ids) do + pcall(fn.matchdelete, id, handle) + end + end + else + local o = {hl_group = hlGroup} + for i = start, finish do + local row, col = i - 1, 0 + o.end_row = i + o.end_col = 0 + table.insert(ids, api.nvim_buf_set_extmark(handle, ns, row, col, o)) + end + onFulfilled = function() + for _, id in ipairs(ids) do + pcall(api.nvim_buf_del_extmark, handle, ns, id) + end + end + end + return utils.wait(delay or 300):thenCall(onFulfilled) +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/match.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/match.lua new file mode 100644 index 00000000..59a1d431 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/match.lua @@ -0,0 +1,49 @@ +local fn = vim.fn + +local M = {} + +--- +---@param winid number +---@param lnum number +---@param endLnum number +---@return table +function M.mapHighlightsByLnum(winid, lnum, endLnum) + local res = {} + for _, m in pairs(fn.getmatches(winid)) do + if m.pattern then + table.insert(res, m) + else + local added = false + local function add(match) + if not added then + table.insert(res, match) + added = true + end + end + + for i = 1, 8 do + local k = 'pos' .. i + local p = m[k] + local pType = type(p) + if pType == 'nil' then + break + end + if pType == 'number' then + if p >= lnum and p <= endLnum then + m[k] = p - lnum + 1 + add(m) + end + else + local l = p[1] + if l >= lnum and l <= endLnum then + m[k][1] = l - lnum + 1 + add(m) + end + end + end + end + end + return res +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/treesitter.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/treesitter.lua new file mode 100644 index 00000000..f99d56fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/render/treesitter.lua @@ -0,0 +1,77 @@ +local highlighter = require('vim.treesitter.highlighter') + +local M = {} + +--- +---@param bufnr number +---@param startRange number +---@param endRange number +---@param hlGroups? table +---@return table +function M.getHighlightsByRange(bufnr, startRange, endRange, hlGroups) + local data = highlighter.active[bufnr] + if not data then + return {} + end + local res = {} + local row, col = startRange[1], startRange[2] + local endRow, endCol = endRange[1], endRange[2] + data.tree:for_each_tree(function(tstree, tree) + if not tstree then + return + end + local root = tstree:root() + local rootStartRow, _, rootEndRow, _ = root:range() + if rootEndRow < row or rootStartRow > endRow then + return + end + local query = data:get_query(tree:lang()) + -- Some injected languages may not have highlight queries. + if not query:query() then + return + end + local iter = query:query():iter_captures(root, data.bufnr, row, endRow + 1) + -- Record the last range and priority + local lsr, lsc, ler, lec, lpriority, last + for capture, node, metadata in iter do + if not capture then + break + end + local hlId = assert((function() + if query.get_hl_from_capture then -- nvim 0.10+ #26675 + return query:get_hl_from_capture(capture) + else + return query.hl_cache[capture] + end + end)()) + local priority = tonumber(metadata.priority) or 100 + local conceal = metadata.conceal + local sr, sc, er, ec = node:range() + if row <= er and endRow >= sr then + if sr < row or sr == row and sc < col then + sr, sc = row, col + end + if er > endRow or er == endRow and ec > endCol then + er, ec = endRow, endCol + end + if hlGroups then + -- Overlap highlighting if range is equal to last's + if lsr == sr and lsc == sc and ler == er and lec == ec then + if hlGroups[hlId].foreground and lpriority <= priority then + last[5], last[6], last[7] = hlId, priority, conceal + end + else + last = {sr, sc, er, ec, hlId, priority, conceal} + table.insert(res, last) + end + lsr, lsc, ler, lec, lpriority = sr, sc, er, ec, priority + else + table.insert(res, {sr, sc, er, ec, hlId, priority, conceal}) + end + end + end + end) + return res +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/utils.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/utils.lua new file mode 100644 index 00000000..b2c211a5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/utils.lua @@ -0,0 +1,344 @@ +---@class UfoUtils +local M = {} +local api = vim.api +local fn = vim.fn +local cmd = vim.cmd +local uv = vim.loop + +--- +---@return fun(): boolean +M.has10 = (function() + local has10 + return function() + if has10 == nil then + has10 = fn.has('nvim-0.10') == 1 + end + return has10 + end +end)() + +--- +---@return fun(): boolean +M.has08 = (function() + local has08 + return function() + if has08 == nil then + has08 = fn.has('nvim-0.8') == 1 + end + return has08 + end +end)() + +---@return fun(): boolean +M.isWindows = (function() + local isWin + return function() + if isWin == nil then + isWin = uv.os_uname().sysname == 'Windows_NT' + end + return isWin + end +end)() + +--- +---@return string +function M.mode() + return api.nvim_get_mode().mode +end + +--- +---@param bufnr number +---@return number, number[]? +function M.getWinByBuf(bufnr) + local curBufnr + if not bufnr then + curBufnr = api.nvim_get_current_buf() + bufnr = curBufnr + end + local winids = {} + for _, winid in ipairs(api.nvim_list_wins()) do + if bufnr == api.nvim_win_get_buf(winid) then + table.insert(winids, winid) + end + end + if #winids == 0 then + return -1 + elseif #winids == 1 then + return winids[1] + else + if not curBufnr then + curBufnr = api.nvim_get_current_buf() + end + local winid = curBufnr == bufnr and api.nvim_get_current_win() or winids[1] + return winid, winids + end +end + +--- +---@param winid number +---@param f fun(): any +---@return any +function M.winCall(winid, f) + if winid == 0 or winid == api.nvim_get_current_win() then + return f() + else + return api.nvim_win_call(winid, f) + end +end + +--- +---@param winid number +---@param lnum number +---@return number +function M.foldClosed(winid, lnum) + return M.winCall(winid, function() + return fn.foldclosed(lnum) + end) +end + +--- +---@param winid number +---@param lnum number +---@return number +function M.foldClosedEnd(winid, lnum) + return M.winCall(winid, function() + return fn.foldclosedend(lnum) + end) +end + +--- +---@param str string +---@param ts number +---@param start? number +---@return string +function M.expandTab(str, ts, start) + start = start or 1 + local new = str:sub(1, start - 1) + local pad = ' ' + local ti = start - 1 + local i = start + while true do + i = str:find('\t', i, true) + if not i then + if ti == 0 then + new = str + else + new = new .. str:sub(ti + 1) + end + break + end + if ti + 1 == i then + new = new .. pad:rep(ts) + else + local append = str:sub(ti + 1, i - 1) + new = new .. append .. pad:rep(ts - api.nvim_strwidth(append) % ts) + end + ti = i + i = i + 1 + end + return new +end + +---@param ms number +---@return Promise +function M.wait(ms) + return require('promise')(function(resolve) + local timer = uv.new_timer() + timer:start(ms, 0, function() + timer:close() + resolve() + end) + end) +end + +--- +---@param callback function +---@param ms number +---@return userdata +function M.setTimeout(callback, ms) + ---@type userdata + local timer = uv.new_timer() + timer:start(ms, 0, function() + timer:close() + callback() + end) + return timer +end + +function M.zz() + local lnum1, lcount = api.nvim_win_get_cursor(0)[1], api.nvim_buf_line_count(0) + local zb = 'keepj norm! %dzb' + if lnum1 == lcount then + cmd(zb:format(lnum1)) + return + end + cmd('norm! zvzz') + lnum1 = api.nvim_win_get_cursor(0)[1] + cmd('norm! L') + local lnum2 = api.nvim_win_get_cursor(0)[1] + if lnum2 + fn.getwinvar(0, '&scrolloff') >= lcount then + cmd(zb:format(lnum2)) + end + if lnum1 ~= lnum2 then + cmd('keepj norm! ``') + end +end + +--- +---@param bufnr number +---@param name? string +---@param off? number +---@return boolean +function M.isUnNameBuf(bufnr, name, off) + name = name or api.nvim_buf_get_name(bufnr) + off = off or api.nvim_buf_get_offset(bufnr, 1) + return name == '' and off <= 0 +end + +--- +---@param winid number +---@return boolean +function M.isDiffOrMarkerFold(winid) + local method = vim.wo[winid].foldmethod + return method == 'diff' or method == 'marker' +end + +--- +---@param winid number +---@return table +function M.getWinInfo(winid) + local winfos = fn.getwininfo(winid) + assert(type(winfos) == 'table' and #winfos == 1, + '`getwininfo` expected 1 table with single element.') + return winfos[1] +end + +---@param str string +---@param targetWidth number +---@return string +function M.truncateStrByWidth(str, targetWidth) + -- str in `strdisplaywidth` need to be converted from Lua to VimScript + -- If a Lua string contains a NUL byte, it will be converted to a |Blob|. + str = str:gsub('%z', '^@') + if fn.strdisplaywidth(str) <= targetWidth then + return str + end + local width = 0 + local byteOff = 0 + while true do + local part = fn.strpart(str, byteOff, 1, true) + width = width + fn.strdisplaywidth(part) + if width > targetWidth then + break + end + byteOff = byteOff + #part + end + return str:sub(1, byteOff) +end + +--- +---@param winid number +---@return number +function M.textoff(winid) + return M.getWinInfo(winid).textoff +end + +--- +---@param bufnr number +---@param lnum number 1-indexed +---@param col number 1-indexed +---@return number 0-indexed +function M.curswant(bufnr, lnum, col) + if col == 0 then + return 0 + end + local text = api.nvim_buf_get_lines(bufnr, lnum - 1, lnum, true)[1] + text = M.expandTab(text:sub(1, col), vim.bo[bufnr].ts) + return #text - 1 +end + +--- +---@param winid number +---@return boolean +function M.isWinValid(winid) + return type(winid) == 'number' and winid > 0 and api.nvim_win_is_valid(winid) +end + +--- +---@param bufnr number +---@return boolean +function M.isBufLoaded(bufnr) + return type(bufnr) == 'number' and bufnr > 0 and api.nvim_buf_is_loaded(bufnr) +end + +--- +---@param winid number +---@param line number +---@param lsizes number +---@return number, number +function M.evaluateTopline(winid, line, lsizes) + local log = require('ufo.lib.log') + local topline + local iStart = M.foldClosed(winid, line) + iStart = iStart == -1 and line or iStart + local lsizeSum = 0 + local i = iStart - 1 + local lsizeObj = require('ufo.model.linesize'):new(winid) + local len = lsizes - lsizeObj:fillSize(line) + log.info('winid:', winid, 'line:', line, 'lsizes:', lsizes, 'len:', len) + local size + while lsizeSum < len and i > 0 do + local lnum = M.foldClosed(winid, i) + log.info('lnum:', lnum, 'i:', i) + if lnum == -1 then + size = lsizeObj:size(i) + else + size = 1 + i = lnum + end + lsizeSum = lsizeSum + size + log.info('size:', size, 'lsizeSum:', lsizeSum) + topline = i + i = i - 1 + end + if not topline then + topline = iStart + end + -- extraOff lines is need to be showed near the topline + local topfill = lsizeObj:fillSize(topline) + local extraOff = lsizeSum - len + if extraOff > 0 then + if topfill < extraOff then + topline = topline + 1 + else + topfill = topfill - extraOff + end + end + log.info('topline:', topline, 'topfill:', topfill) + return topline, topfill +end + +--- +---@param winid number +---@return table +function M.saveView(winid) + return M.winCall(winid, fn.winsaveview) +end + +--- +---@param winid number +---@param view table +function M.restView(winid, view) + M.winCall(winid, function() + fn.winrestview(view) + end) +end + +--- +---@param winid number +---@return number +function M.wrow(winid) + return M.winCall(winid, fn.winline) - 1 +end + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/wffi.lua b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/wffi.lua new file mode 100644 index 00000000..591edad2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/lua/ufo/wffi.lua @@ -0,0 +1,95 @@ +---@diagnostic disable: undefined-field +---@class UfoWffi +local M = {} + +local utils +local C +local ffi + +local CPos_T + +local function findWin(winid) + local err = ffi.new('Error') + return C.find_window_by_handle(winid, err) +end + +--- +---@param winid number +---@param lnum number +---@return number +function M.plinesWin(winid, lnum) + local wp = findWin(winid) + return C.plines_win(wp, lnum, true) +end + +--- +---@param winid number +---@param lnum number +---@param winheight boolean +---@return number +function M.plinesWinNofill(winid, lnum, winheight) + local wp = findWin(winid) + return C.plines_win_nofill(wp, lnum, winheight) +end + +--- +---@param winid number +function M.clearFolds(winid) + local wp = findWin(winid) + C.clearFolding(wp) + C.changed_window_setting(wp) +end + +--- +---@param winid number +---@param ranges table list of line range +function M.createFolds(winid, ranges) + local wp = findWin(winid) + local s, e = CPos_T(), CPos_T() + for _, p in ipairs(ranges) do + s.lnum = p[1] + e.lnum = p[2] + C.foldCreate(wp, s, e) + end +end + +local function init() + ffi = require('ffi') + setmetatable(M, {__index = ffi}) + C = ffi.C + utils = require('ufo.utils') + if utils.has08() then + ffi.cdef([[ + typedef int32_t linenr_T; + ]]) + else + ffi.cdef([[ + typedef long linenr_T; + ]]) + end + ffi.cdef([[ + typedef struct window_S win_T; + typedef int colnr_T; + + typedef struct {} Error; + win_T *find_window_by_handle(int window, Error *err); + + typedef struct { + linenr_T lnum; + colnr_T col; + colnr_T coladd; + } pos_T; + + void clearFolding(win_T *win); + void changed_window_setting(win_T *wp); + void foldCreate(win_T *wp, pos_T start, pos_T end); + + int plines_win(win_T *wp, linenr_T lnum, bool winheight); + int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight); + ]]) + CPos_T = ffi.typeof('pos_T') +end + +init() + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-ufo/package.json b/config/neovim/store/lazy-plugins/nvim-ufo/package.json new file mode 100644 index 00000000..efc06e10 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-ufo/package.json @@ -0,0 +1,16 @@ +{ + "name": "coc-ufo", + "version": "0.0.1", + "description": "handle folding ranges in coc.nvim", + "author": "kevinhwang91 ", + "license": "BSD 3-Clause", + "main": "coc-extension/index.js", + "type": "commonjs", + "engines": { + "coc": "^0.0.82" + }, + "activationEvents": [ + "*" + ], + "contributes": {} +} diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/.editorconfig b/config/neovim/store/lazy-plugins/nvim-web-devicons/.editorconfig new file mode 100644 index 00000000..0514a27e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +insert_final_newline = true +end_of_line = lf + +[*.lua] +indent_style = space +indent_size = 2 diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/ci.yml new file mode 100644 index 00000000..f9299e9e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +name: CI + +on: + pull_request: + branches: + - '*' + push: + branches: + - master + +permissions: + contents: read + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: leafo/gh-actions-lua@v10 + with: + luaVersion: "5.1" + + - uses: leafo/gh-actions-luarocks@v4 + + - name: luacheck + run: | + luarocks install luacheck 1.1.1 + make lint + + style: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: stylua + uses: JohnnyMorganz/stylua-action@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: "0.19" + args: --check lua scripts + + colors: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: rhysd/action-setup-vim@v1 + with: + neovim: true + + - name: make colors-check + run: make colors-check + diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/pre-commit-autoupdate.yml b/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/pre-commit-autoupdate.yml new file mode 100644 index 00000000..3e91de1c --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/pre-commit-autoupdate.yml @@ -0,0 +1,23 @@ +name: Pre-commit autoupdate +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: +permissions: + contents: write + pull-requests: write +jobs: + auto-update: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4.5.0 + - run: pip install pre-commit + - run: pre-commit autoupdate + - uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: update/pre-commit-hooks + title: "chore: update pre-commit hooks" + commit-message: "chore: update pre-commit hooks" + body: Update versions of pre-commit hooks to latest version. diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/release.yml b/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/release.yml new file mode 100644 index 00000000..39bb179b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/release.yml @@ -0,0 +1,28 @@ +name: Release + +on: + push: + tags: + - 'v*' + + workflow_dispatch: + +jobs: + luarocks-upload: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: LuaRocks Upload + uses: nvim-neorocks/luarocks-tag-release@v5 + env: + LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }} + with: + summary: Nerd Font icons for neovim + detailed_description: | + Coloured Nerd Font file icons for neovim. + + Dark and light background variants. + + https://www.nerdfonts.com/ + license: MIT + labels: neovim diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/semantic-pr-subject.yml b/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/semantic-pr-subject.yml new file mode 100644 index 00000000..5b9554f5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/.github/workflows/semantic-pr-subject.yml @@ -0,0 +1,15 @@ +name: Semantic Pull Request Subject +on: + pull_request: + types: + - opened + - reopened + - edited + - synchronize +jobs: + semantic-pr-subject: + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v4.5.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/.gitignore b/config/neovim/store/lazy-plugins/nvim-web-devicons/.gitignore new file mode 100644 index 00000000..df266259 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/.gitignore @@ -0,0 +1,3 @@ +.lua +.luarocks +/vim-colortemplate/ diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/.luacheckrc b/config/neovim/store/lazy-plugins/nvim-web-devicons/.luacheckrc new file mode 100644 index 00000000..eeebf4a8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/.luacheckrc @@ -0,0 +1,7 @@ +max_line_length = 120 + +globals = { + "vim", + "jit", + "bit", +} diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/.luarc.json b/config/neovim/store/lazy-plugins/nvim-web-devicons/.luarc.json new file mode 100644 index 00000000..bcb6b47a --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/.luarc.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", + "runtime.version" : "Lua 5.1", + "diagnostics": { + "globals": [ + "vim", + "jit", + "bit" + ] + } +} + diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/.pre-commit-config.yaml b/config/neovim/store/lazy-plugins/nvim-web-devicons/.pre-commit-config.yaml new file mode 100644 index 00000000..17124d7d --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +repos: + - repo: https://github.com/johnnymorganz/stylua + rev: v0.20.0 + hooks: + - id: stylua-github + fail_fast: true + verbose: true + types: [lua] + - repo: local + hooks: + - id: luacheck + name: Luacheck + description: Lints Lua files using Luacheck. + entry: luacheck -- + language: system + fail_fast: true + verbose: true + types: [lua] + - repo: local + hooks: + - id: colors + name: colors + description: Ensures Light Color Scheme version has been generated. + entry: make colors + language: system + require_serial: true + pass_filenames: false + verbose: true + diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/.stylua.toml b/config/neovim/store/lazy-plugins/nvim-web-devicons/.stylua.toml new file mode 100644 index 00000000..ecb6dca5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/.stylua.toml @@ -0,0 +1,6 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferDouble" +call_parentheses = "None" diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/CONTRIBUTING.md b/config/neovim/store/lazy-plugins/nvim-web-devicons/CONTRIBUTING.md new file mode 100644 index 00000000..a9cf5c9b --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/CONTRIBUTING.md @@ -0,0 +1,136 @@ +# Contributing to `nvim-web-devicons` + +Thank you for your contribution! + +## Order + +Please ensure `icons_by_filename`, `icons_by_file_extension` and `filetypes` are ordered alphabetically, to prevent merge conflicts. + +## Prerequisites + +Code is formatted using stylua and linted using luacheck. + +You can install these with: +```sh +cargo install stylua +luarocks install luacheck +``` + +or via your OS package manager e.g. Arch linux: +```sh +pacman -S stylua +pacman -S luacheck +``` + +## Building + +Following your changes, please run: + +```sh +make +``` + +This will: +1. `git clone --depth 1 https://github.com/lifepillar/vim-colortemplate.git vim-colortemplate` if necessary +1. Generate cterm colors +2. Generate light color variants +3. Check style +4. Lint + +You can automatically fix any style issues via: +```sh +make style-fix +``` + +## Generate Colors + +Add or update icons in `lua/nvim-web-devicons/icons-default.lua` + +There are two tables where icons can be added: +1. icons_by_filename +2. icons_by_file_extension + +Add the icon in table 1. if the icon is for a file that is always named that +way, for example `.gitconfig`. Add to table 2. if the icon is for all files +with an extension. + +Each icon must have the following (this is an example): +```lua +[".gitconfig"] = { + icon = "", + color = "#41535b", + cterm_color = "0", + name = "GitConfig", +}, +``` +___Key/value pairs must appear in the same exact order!___ + +- `color` must contain a color in the html notation +- `cterm_color` must be below `color`, and it must contain a number (any number) +- the correct value for `cterm_color` will be generated by the script +- `name` must only contain alphanumeric characters (don't use `/`, `-`, `_`) + +Ensure your current working directory is the repo root. +Run `make`. This will: +- Update `cterm_color` based on `color` +- Generate `lua/nvim-web-devicons/icons-light.lua` + +Please commit both `lua/nvim-web-devicons/icons-default.lua` and `lua/nvim-web-devicons/icons-light.lua` + +## Test + +Run `:NvimWebDeviconsHiTest` to view the icons and their highlighting. + +Start neovim with `TERM=xterm-256color nvim ...` to test cterm. + +Check with `&background` `dark` and `light` + +## Documentation + +When modifying or adding API, please update [Usage](README.md#Usage) + +# Pull Request + +Please reference any issues in the description e.g. "resolves #1234", which will be closed upon merge. + +Please check "allow edits by maintainers" to allow nvim-web-devicons maintainers to make small changes such as documentation tweaks. + +## Subject + +The merge commit message will be the subject of the PR. + +A [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) subject will be validated by the Semantic Pull Request Subject CI job. Reference the issue to be used in the release notes e.g. + +``` +feat: add gradle icons +fix: update rust icon +feat(#192): :NvimWebDeviconsHiTest +``` + +Available types: +* feat: A new feature +* fix: A bug fix +* docs: Documentation only changes +* style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) +* refactor: A code change that neither fixes a bug nor adds a feature +* perf: A code change that improves performance +* test: Adding missing tests or correcting existing tests +* build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) +* ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs) +* chore: Other changes that don't modify src or test files +* revert: Reverts a previous commit + +If in doubt, look at previous commits. + +See also [The Conventional Commits ultimate cheatsheet](https://gist.github.com/gabrielecanepa/fa6cca1a8ae96f77896fe70ddee65527) + +## Browser Font + +It is useful to see the actual glyphs in the pull request. That can be done by setting the browser font to your nerd font. + +Using firefox: + +* Settings -> General +* Fonts -> Advanced +* Change Monospace to "Hack Nerd Font Mono" or similar +* Uncheck "Allow pages to choose their own fonts, instead of your selections above" diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/LICENSE b/config/neovim/store/lazy-plugins/nvim-web-devicons/LICENSE new file mode 100644 index 00000000..df4391dd --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 nvim-tree + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/Makefile b/config/neovim/store/lazy-plugins/nvim-web-devicons/Makefile new file mode 100644 index 00000000..5f3344b0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/Makefile @@ -0,0 +1,32 @@ +VIM_COLORTEMPLATE_VERSION = 2.2.3 + +all: colors style-check lint + +colors: vim-colortemplate + nvim \ + --clean \ + --headless \ + --cmd "set rtp^=vim-colortemplate" \ + -c 'source scripts/generate_colors.lua' \ + -c 'qall' + +colors-check: colors + git diff --exit-code lua/nvim-web-devicons/icons-light.lua + +vim-colortemplate: + mkdir -p vim-colortemplate + curl -L https://github.com/lifepillar/vim-colortemplate/archive/refs/tags/v$(VIM_COLORTEMPLATE_VERSION).tar.gz | tar zx --directory vim-colortemplate --strip-components=1 + +style-check: + stylua . --check + +style-fix: + stylua . + +lint: + luacheck lua scripts + +clean: + rm -rf vim-colortemplate + +.PHONY: all colors style-check style-fix lint diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/README.md b/config/neovim/store/lazy-plugins/nvim-web-devicons/README.md new file mode 100644 index 00000000..8014e319 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/README.md @@ -0,0 +1,235 @@ +# Notice +Nerd fonts moved some symbols with version 3.0. Version 2.3 is meant for transition, supporting both version 2 and version 3 icons. + +Nvim-web-devicons requires version 2.3 or above to work properly. If you are unable to update please use your plugin manager to pin version of nvim-web-dev icons to `nerd-v2-compat` tag. + +# Nvim-web-devicons + +A `lua` fork of [vim-devicons](https://github.com/ryanoasis/vim-devicons). This plugin provides the same icons _as well as_ colors for each icon. + +Light and dark color variants are provided. + +## Requirements + +- [neovim >=0.7.0](https://github.com/neovim/neovim/wiki/Installing-Neovim) +- [A patched font](https://www.nerdfonts.com/) + +## Installation + +```vim +Plug 'nvim-tree/nvim-web-devicons' +``` + +or with [packer.nvim](https://github.com/wbthomason/packer.nvim) + +``` +use 'nvim-tree/nvim-web-devicons' +``` + +[![LuaRocks](https://img.shields.io/luarocks/v/nvim-tree/nvim-web-devicons?logo=lua&color=purple)](https://luarocks.org/modules/nvim-tree/nvim-web-devicons) + +## Usage + +### Viewing + +Run `:NvimWebDeviconsHiTest` to see all icons and their highlighting. + +### Variants + +Light or dark color variants of the icons depend on `&background`. + +The variant is updated: +- on `OptionSet` event for `background`, or +- after explicitly calling `require("nvim-web-devicons").refresh()`. + +However, be advised that the plugin using nvim-web-devicons may have cached the icons. + +### Setup + +This adds all the highlight groups for the devicons +i.e. it calls `vim.api.nvim_set_hl` for all icons +this might need to be re-called in a `Colorscheme` to re-apply cleared highlights +if the color scheme changes + +```lua +require'nvim-web-devicons'.setup { + -- your personnal icons can go here (to override) + -- you can specify color or cterm_color instead of specifying both of them + -- DevIcon will be appended to `name` + override = { + zsh = { + icon = "", + color = "#428850", + cterm_color = "65", + name = "Zsh" + } + }; + -- globally enable different highlight colors per icon (default to true) + -- if set to false all icons will have the default icon's color + color_icons = true; + -- globally enable default icons (default to false) + -- will get overriden by `get_icons` option + default = true; + -- globally enable "strict" selection of icons - icon will be looked up in + -- different tables, first by filename, and if not found by extension; this + -- prevents cases when file doesn't have any extension but still gets some icon + -- because its name happened to match some extension (default to false) + strict = true; + -- same as `override` but specifically for overrides by filename + -- takes effect when `strict` is true + override_by_filename = { + [".gitignore"] = { + icon = "", + color = "#f1502f", + name = "Gitignore" + } + }; + -- same as `override` but specifically for overrides by extension + -- takes effect when `strict` is true + override_by_extension = { + ["log"] = { + icon = "", + color = "#81e043", + name = "Log" + } + }; + -- same as `override` but specifically for operating system + -- takes effect when `strict` is true + override_by_operating_system = { + ["apple"] = { + icon = "", + color = "#A2AAAD", + cterm_color = "248", + name = "Apple", + }, + }; +} +``` + +### Get Icon + +Get the icon for a given file by passing in the `name`, the `extension` and an _optional_ options `table`. +The name is passed in to check for an exact match e.g. `.bashrc` if there is no exact name match the extension +is used. Calls `.setup()` if it hasn't already ran. + +```lua +require'nvim-web-devicons'.get_icon(filename, extension, options) +``` + +The optional `options` argument can used to change how the plugin works the keys include +`default = ` and `strict = `. If the default key is set to true this +function will return a default if there is no matching icon. If the strict key is set +to true this function will lookup icon specifically by filename, and if not found then +specifically by extension, and fallback to default icon if default key is set to true. +e.g. + +```lua +require'nvim-web-devicons'.get_icon(filename, extension, { default = true }) +``` + +You can check if the setup function was already called with: +```lua +require'nvim-web-devicons'.has_loaded() +``` + +### Get icon and color code + +`get_icon_color` differs from `get_icon` only in the second return value. +`get_icon_cterm_color` returns cterm color instead of gui color +`get_icon` returns icon and highlight name. +If you want to get color code, you can use this function. +```lua +local icon, color = require'nvim-web-devicons'.get_icon_color("init.lua", "lua") +assert(icon == "") +assert(color == "#51a0cf") +``` + +### Get all icons + +It is possible to get all of the registered icons with the `get_icons()` function: + +```lua +require'nvim-web-devicons'.get_icons() +``` + +This can be useful for debugging purposes or for creating custom highlights for each icon. + +Mapped categories can be fetched via: + +```lua +require'nvim-web-devicons'.get_icons_by_filename() +require'nvim-web-devicons'.get_icons_by_extension() +require'nvim-web-devicons'.get_icons_by_operating_system() +require'nvim-web-devicons'.get_icons_by_desktop_environment() +require'nvim-web-devicons'.get_icons_by_window_manager() +``` + +### Set an icon + +You can override individual icons with the `set_icon({...})` function: + +```lua +require("nvim-web-devicons").set_icon { + zsh = { + icon = "", + color = "#428850", + cterm_color = "65", + name = "Zsh" + } +} +``` + +You can override the default icon with the `set_default_icon(icon, color, cterm_color)` function: + +```lua +require("nvim-web-devicons").set_default_icon('', '#6d8086', 65) +``` + +### Getting and setting icons by filetype + +You can get the icon and colors associated with a filetype using the `by_filetype` functions: + +```lua +require("nvim-web-devicons").get_icon_by_filetype(filetype, opts) +require("nvim-web-devicons").get_icon_colors_by_filetype(filetype, opts) +require("nvim-web-devicons").get_icon_color_by_filetype(filetype, opts) +require("nvim-web-devicons").get_icon_cterm_color_by_filetype(filetype, opts) +``` + +or set the icon to use for a filetype with: + +```lua +require("nvim-web-devicons").set_icon_by_filetype { cpp = "c", pandoc = "md", } +``` + +These functions are the same as their counterparts without the `_by_filetype` suffix, but they take a filetype instead of a name/extension. + +You can also use `get_icon_name_by_filetype(filetype)` to get the icon name associated with the filetype. + +## Known Issues + +### My `setup` Overrides Are Not Applied + +*Cause:* A plugin may be calling nvim-web-devicons `setup` before you do. Your `setup` call will be ignored. + +*Workaround:* Call nvim-web-devicons `setup` before the plugin's own `setup`. + +### Windows and WSL not rendering icons properly on some terminals + +On Windows and WSL, it is possible that the icons are not rendered properly when +using a terminal that relies on Windows' default system libraries. An example +of this is Alacritty ([#271](https://github.com/nvim-tree/nvim-web-devicons/issues/271#issuecomment-2081280928)). +Other terminals (e.g. Windows Terminal, and WezTerm) do no have this issue, as +they ship newer versions of these libraries. More precisely, they use newer +versions of `conpty.dll` and `OpenConsole.exe`. So, as a workaround to the +rendering issue, you need to make your terminal use these newer files. Whether +this is possible depends on the terminal you are using. Please refer to the +terminal's documentation for this. + +In the specific case of Alacritty, you need to place up-to-date `conpty.dll` and +`OpenConsole.exe` files in your `PATH`. Microsoft does not provide these files +directly, but you can get them from other terminal emulators that ship them. + +## Contributing + +PRs are always welcome! Please see [CONTRIBUTING](CONTRIBUTING.md) diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons.lua b/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons.lua new file mode 100644 index 00000000..32ce9124 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons.lua @@ -0,0 +1,594 @@ +local M = {} + +-- NOTE: When adding new icons, remember to add an entry to the `filetypes` table, if applicable. +local icons, icons_by_filename, icons_by_file_extension, icons_by_operating_system +local icons_by_desktop_environment, icons_by_window_manager + +local default_icon = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Default", +} + +function M.get_icons() + return icons +end + +function M.get_icons_by_filename() + return icons_by_filename +end + +function M.get_icons_by_extension() + return icons_by_file_extension +end + +function M.get_icons_by_operating_system() + return icons_by_operating_system +end + +function M.get_icons_by_desktop_environment() + return icons_by_desktop_environment +end + +function M.get_icons_by_window_manager() + return icons_by_window_manager +end + +local global_opts = { + override = {}, + strict = false, + default = false, + color_icons = true, +} + +-- Set the current icons tables, depending on the 'background' option. +local function refresh_icons() + local theme + if vim.o.background == "light" then + theme = require "nvim-web-devicons.icons-light" + else + theme = require "nvim-web-devicons.icons-default" + end + + icons_by_filename = theme.icons_by_filename + icons_by_file_extension = theme.icons_by_file_extension + icons_by_operating_system = theme.icons_by_operating_system + icons_by_desktop_environment = theme.icons_by_desktop_environment + icons_by_window_manager = theme.icons_by_window_manager + icons = vim.tbl_extend( + "keep", + {}, + icons_by_filename, + icons_by_file_extension, + icons_by_operating_system, + icons_by_desktop_environment, + icons_by_window_manager + ) + icons = vim.tbl_extend("force", icons, global_opts.override) + icons[1] = default_icon +end + +-- Map of filetypes -> icon names +local filetypes = { + ["avif"] = "avif", + ["bash"] = "bash", + ["bib"] = "bib", + ["bicep"] = "bicep", + ["bicepparam"] = "bicepparam", + ["bzl"] = "bzl", + ["brewfile"] = "brewfile", + ["blueprint"] = "blp", + ["checkhealth"] = "checkhealth", + ["commit"] = "commit_editmsg", + ["copying"] = "copying", + ["gemfile"] = "gemfile$", + ["lesser"] = "copying.lesser", + ["vagrantfile"] = "vagrantfile$", + ["awk"] = "awk", + ["bmp"] = "bmp", + ["c"] = "c", + ["cfg"] = "cfg", + ["clojure"] = "clj", + ["cmake"] = "cmake", + ["cobol"] = "cobol", + ["coffee"] = "coffee", + ["conf"] = "conf", + ["cp"] = "cp", + ["cpp"] = "cpp", + ["cr"] = "cr", + ["cs"] = "cs", + ["csh"] = "csh", + ["cson"] = "cson", + ["css"] = "css", + ["csv"] = "csv", + ["cuda"] = "cu", + ["d"] = "d", + ["dart"] = "dart", + ["desktop"] = "desktop", + ["diff"] = "diff", + ["doc"] = "doc", + ["docx"] = "docx", + ["dockerfile"] = "dockerfile", + ["dosbatch"] = "bat", + ["dosini"] = "ini", + ["dot"] = "dot", + ["drools"] = "drl", + ["dropbox"] = "dropbox", + ["dump"] = "dump", + ["eex"] = "eex", + ["ejs"] = "ejs", + ["elixir"] = "ex", + ["elm"] = "elm", + ["epuppet"] = "epp", + ["erlang"] = "erl", + ["eruby"] = "erb", + ["fennel"] = "fnl", + ["fish"] = "fish", + ["forth"] = "fs", + ["fortran"] = "f90", + ["fsharp"] = "f#", + ["fsi"] = "fsi", + ["fsscript"] = "fsscript", + ["fsx"] = "fsx", + ["gd"] = "gd", + ["gif"] = "gif", + ["git"] = "git", + ["gitconfig"] = ".gitconfig", + ["gitcommit"] = "commit_editmsg", + ["gitignore"] = ".gitignore", + ["gitattributes"] = ".gitattributes", + ["glb"] = "glb", + ["go"] = "go", + ["godot"] = "godot", + ["graphql"] = "graphql", + ["groovy"] = "groovy", + ["gql"] = "gql", + ["gruntfile"] = "gruntfile", + ["gtkrc"] = "gtkrc", + ["gulpfile"] = "gulpfile", + ["haml"] = "haml", + ["haxe"] = "hx", + ["haskell"] = "hs", + ["hbs"] = "hbs", + ["heex"] = "heex", + ["hex"] = "hex", + ["html"] = "html", + ["ico"] = "ico", + ["idlang"] = "pro", + ["ino"] = "ino", + ["import"] = "import", + ["ipynb"] = "ipynb", + ["java"] = "java", + ["javascript"] = "js", + ["javascript.jsx"] = "jsx", + ["javascriptreact"] = "jsx", + ["jpeg"] = "jpeg", + ["jpg"] = "jpg", + ["json"] = "json", + ["jsonc"] = "jsonc", + ["json5"] = "json5", + ["julia"] = "jl", + ["kotlin"] = "kt", + ["leex"] = "leex", + ["less"] = "less", + ["liquid"] = "liquid", + ["lhaskell"] = "lhs", + ["license"] = "license", + ["unlicense"] = "unlicense", + ["log"] = "log", + ["lock"] = "lock", + ["lprolog"] = "sig", + ["lua"] = "lua", + ["make"] = "makefile", + ["markdown"] = "markdown", + ["material"] = "material", + ["mdx"] = "mdx", + ["mint"] = "mint", + ["motoko"] = "mo", + ["mustache"] = "mustache", + ["nim"] = "nim", + ["nix"] = "nix", + ["nu"] = "nu", + ["node"] = "node_modules", + ["obj"] = "obj", + ["ocaml"] = "ml", + ["openscad"] = "scad", + ["opus"] = "opus", + ["otf"] = "otf", + ["pck"] = "pck", + ["pdf"] = "pdf", + ["perl"] = "pl", + ["php"] = "php", + ["plaintex"] = "tex", + ["png"] = "png", + ["po"] = "po", + ["postscr"] = "ai", + ["ppt"] = "ppt", + ["prisma"] = "prisma", + ["procfile"] = "procfile", + ["prolog"] = "pro", + ["ps1"] = "ps1", + ["psd1"] = "psd1", + ["psm1"] = "psm1", + ["psb"] = "psb", + ["psd"] = "psd", + ["puppet"] = "pp", + ["pyc"] = "pyc", + ["pyd"] = "pyd", + ["pyo"] = "pyo", + ["python"] = "py", + ["query"] = "query", + ["r"] = "r", + ["res"] = "rescript", + ["resi"] = "rescript", + ["rlib"] = "rlib", + ["rmd"] = "rmd", + ["rproj"] = "rproj", + ["ruby"] = "rb", + ["rust"] = "rs", + ["sass"] = "sass", + ["sbt"] = "sbt", + ["scala"] = "scala", + ["scheme"] = "scm", + ["scss"] = "scss", + ["sh"] = "sh", + ["slim"] = "slim", + ["sln"] = "sln", + ["sml"] = "sml", + ["solidity"] = "sol", + ["sql"] = "sql", + ["sqlite"] = "sqlite", + ["sqlite3"] = "sqlite3", + ["srt"] = "srt", + ["ssa"] = "ssa", + ["styl"] = "styl", + ["sublime"] = "sublime", + ["suo"] = "suo", + ["svelte"] = "svelte", + ["svg"] = "svg", + ["swift"] = "swift", + ["systemverilog"] = "sv", + ["tads"] = "t", + ["tcl"] = "tcl", + ["templ"] = "templ", + ["terminal"] = "terminal", + ["tex"] = "tex", + ["toml"] = "toml", + ["tres"] = "tres", + ["tscn"] = "tscn", + ["twig"] = "twig", + ["txt"] = "txt", + ["typescript"] = "ts", + ["typescriptreact"] = "tsx", + ["vala"] = "vala", + ["verilog"] = "v", + ["vhdl"] = "vhd", + ["vim"] = "vim", + ["vue"] = "vue", + ["wasm"] = "wasm", + ["webm"] = "webm", + ["webp"] = "webp", + ["webpack"] = "webpack", + ["xcplayground"] = "xcplayground", + ["xls"] = "xls", + ["xlsx"] = "xlsx", + ["xml"] = "xml", + ["yaml"] = "yaml", + ["zig"] = "zig", + ["zsh"] = "zsh", +} + +local function get_highlight_name(data) + if not global_opts.color_icons then + data = default_icon + end + + return data.name and "DevIcon" .. data.name +end + +local nvim_set_hl = vim.api.nvim_set_hl +local function set_up_highlight(icon_data) + if not global_opts.color_icons then + icon_data = default_icon + end + + local hl_group = get_highlight_name(icon_data) + if hl_group and (icon_data.color or icon_data.cterm_color) then + nvim_set_hl(0, get_highlight_name(icon_data), { + fg = icon_data.color, + ctermfg = tonumber(icon_data.cterm_color), + }) + end +end + +local nvim_get_hl_by_name = vim.api.nvim_get_hl_by_name +local function highlight_exists(group) + if not group then + return + end + + local ok, hl = pcall(nvim_get_hl_by_name, group, true) + return ok and not (hl or {})[true] +end + +function M.set_up_highlights(allow_override) + if not global_opts.color_icons then + set_up_highlight(default_icon) + return + end + + for _, icon_data in pairs(icons) do + local has_color = icon_data.color or icon_data.cterm_color + local name_valid = icon_data.name + local defined_before = highlight_exists(get_highlight_name(icon_data)) + if has_color and name_valid and (allow_override or not defined_before) then + set_up_highlight(icon_data) + end + end +end + +local function get_highlight_foreground(icon_data) + if not global_opts.color_icons then + icon_data = default_icon + end + + return string.format("#%06x", nvim_get_hl_by_name(get_highlight_name(icon_data), true).foreground) +end + +local function get_highlight_ctermfg(icon_data) + if not global_opts.color_icons then + icon_data = default_icon + end + + return nvim_get_hl_by_name(get_highlight_name(icon_data), false).foreground +end + +local loaded = false + +function M.has_loaded() + return loaded +end + +local if_nil = vim.F.if_nil +function M.setup(opts) + if loaded then + return + end + + loaded = true + + local user_icons = opts or {} + + if user_icons.default then + global_opts.default = true + end + + if user_icons.strict then + global_opts.strict = true + end + + global_opts.color_icons = if_nil(user_icons.color_icons, global_opts.color_icons) + + if user_icons.override and user_icons.override.default_icon then + default_icon = user_icons.override.default_icon + end + + local user_filename_icons = user_icons.override_by_filename + local user_file_ext_icons = user_icons.override_by_extension + local user_operating_system_icons = user_icons.override_by_operating_system + local user_desktop_environment_icons = user_icons.override_by_desktop_environment + local user_window_manager_icons = user_icons.override_by_window_manager + + icons = vim.tbl_extend( + "force", + icons, + user_icons.override or {}, + user_filename_icons or {}, + user_file_ext_icons or {}, + user_operating_system_icons or {}, + user_desktop_environment_icons or {}, + user_window_manager_icons or {} + ) + global_opts.override = vim.tbl_extend( + "force", + global_opts.override, + user_icons.override or {}, + user_filename_icons or {}, + user_file_ext_icons or {}, + user_operating_system_icons or {}, + user_desktop_environment_icons or {}, + user_window_manager_icons or {} + ) + + if user_filename_icons then + icons_by_filename = vim.tbl_extend("force", icons_by_filename, user_filename_icons) + end + if user_file_ext_icons then + icons_by_file_extension = vim.tbl_extend("force", icons_by_file_extension, user_file_ext_icons) + end + if user_operating_system_icons then + icons_by_operating_system = vim.tbl_extend("force", icons_by_operating_system, user_operating_system_icons) + end + if user_desktop_environment_icons then + icons_by_desktop_environment = vim.tbl_extend("force", icons_by_desktop_environment, user_desktop_environment_icons) + end + if user_window_manager_icons then + icons_by_window_manager = vim.tbl_extend("force", icons_by_window_manager, user_window_manager_icons) + end + + icons[1] = default_icon + + M.set_up_highlights() + + vim.api.nvim_create_autocmd("ColorScheme", { + desc = "Re-apply icon colors after changing colorschemes", + group = vim.api.nvim_create_augroup("NvimWebDevicons", { clear = true }), + callback = M.set_up_highlights, + }) + + -- highlight test command + vim.api.nvim_create_user_command("NvimWebDeviconsHiTest", function() + require "nvim-web-devicons.hi-test"( + default_icon, + global_opts.override, + icons_by_filename, + icons_by_file_extension, + icons_by_operating_system, + icons_by_desktop_environment, + icons_by_window_manager + ) + end, { + desc = "nvim-web-devicons: highlight test", + }) +end + +function M.get_default_icon() + return default_icon +end + +-- recursively iterate over each segment separated by '.' to parse extension with multiple dots in filename +local function iterate_multi_dotted_extension(name, icon_table) + if name == nil then + return nil + end + + local compound_ext = name:match "%.(.*)" + local icon = icon_table[compound_ext] + if icon then + return icon + end + + return iterate_multi_dotted_extension(compound_ext, icon_table) +end + +local function get_icon_by_extension(name, ext, opts) + local is_strict = if_nil(opts and opts.strict, global_opts.strict) + local icon_table = is_strict and icons_by_file_extension or icons + + if ext ~= nil then + return icon_table[ext] + end + + return iterate_multi_dotted_extension(name, icon_table) +end + +local function get_icon_data(name, ext, opts) + if type(name) == "string" then + name = name:lower() + end + + if not loaded then + M.setup() + end + + local has_default = if_nil(opts and opts.default, global_opts.default) + local is_strict = if_nil(opts and opts.strict, global_opts.strict) + local icon_data + if is_strict then + icon_data = icons_by_filename[name] or get_icon_by_extension(name, ext, opts) or (has_default and default_icon) + else + icon_data = icons[name] or get_icon_by_extension(name, ext, opts) or (has_default and default_icon) + end + + return icon_data +end + +function M.get_icon(name, ext, opts) + local icon_data = get_icon_data(name, ext, opts) + + if icon_data then + return icon_data.icon, get_highlight_name(icon_data) + end +end + +function M.get_icon_name_by_filetype(ft) + return filetypes[ft] +end + +function M.get_icon_by_filetype(ft, opts) + local name = M.get_icon_name_by_filetype(ft) + opts = opts or {} + opts.strict = false + return M.get_icon(name or "", nil, opts) +end + +function M.get_icon_colors(name, ext, opts) + local icon_data = get_icon_data(name, ext, opts) + + if icon_data then + local color = icon_data.color + local cterm_color = icon_data.cterm_color + if icon_data.name and highlight_exists(get_highlight_name(icon_data)) then + color = get_highlight_foreground(icon_data) or color + cterm_color = get_highlight_ctermfg(icon_data) or cterm_color + end + return icon_data.icon, color, cterm_color + end +end + +function M.get_icon_colors_by_filetype(ft, opts) + local name = M.get_icon_name_by_filetype(ft) + return M.get_icon_colors(name or "", nil, opts) +end + +function M.get_icon_color(name, ext, opts) + local data = { M.get_icon_colors(name, ext, opts) } + return data[1], data[2] +end + +function M.get_icon_color_by_filetype(ft, opts) + local name = M.get_icon_name_by_filetype(ft) + opts = opts or {} + opts.strict = false + return M.get_icon_color(name or "", nil, opts) +end + +function M.get_icon_cterm_color(name, ext, opts) + local data = { M.get_icon_colors(name, ext, opts) } + return data[1], data[3] +end + +function M.get_icon_cterm_color_by_filetype(ft, opts) + local name = M.get_icon_name_by_filetype(ft) + return M.get_icon_cterm_color(name or "", nil, opts) +end + +function M.set_icon(user_icons) + icons = vim.tbl_extend("force", icons, user_icons or {}) + global_opts.override = vim.tbl_extend("force", global_opts.override, user_icons or {}) + if not global_opts.color_icons then + return + end + + for _, icon_data in pairs(user_icons) do + set_up_highlight(icon_data) + end +end + +function M.set_icon_by_filetype(user_filetypes) + filetypes = vim.tbl_extend("force", filetypes, user_filetypes or {}) +end + +function M.set_default_icon(icon, color, cterm_color) + default_icon.icon = icon + default_icon.color = color + default_icon.cterm_color = cterm_color + set_up_highlight(default_icon) +end + +-- Load the icons already, the loaded tables depend on the 'background' setting. +refresh_icons() + +function M.refresh() + refresh_icons() + M.set_up_highlights(true) +end + +-- Change icon set on background change +vim.api.nvim_create_autocmd("OptionSet", { + pattern = "background", + callback = M.refresh, +}) + +return M diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons/hi-test.lua b/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons/hi-test.lua new file mode 100644 index 00000000..6fa17a2e --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons/hi-test.lua @@ -0,0 +1,136 @@ +---Run a test similar to :so $VIMRUNTIME/syntax/hitest.vim +---Display all icons and their group highlighted, followed by the concrete definition +-- +---@class IconDisplay for :NvimTreeHiTest +---@field tag string filename, os or extension +---@field name string name without prefix +---@field icon string icon itself +---@field group string|nil :hi group name +---@field def string|nil :hi concrete definition +local IconDisplay = {} + +---@param o IconDisplay +---@return IconDisplay|nil +function IconDisplay:new(o) + if type(o.tag) ~= "string" or type(o.name) ~= "string" or type(o.icon) ~= "string" then + return nil + end + + setmetatable(o, self) + self.__index = self + + o.group = "DevIcon" .. o.name + o.tag = o.tag or "" + + -- concrete definition + local ok, res = pcall(vim.api.nvim_cmd, { cmd = "highlight", args = { o.group } }, { output = true }) + if ok and type(res) == "string" then + o.def = res:gsub(".*xxx *", "") + else + o.def = "" + end + + return o +end + +---Write the line with highlighting +---@param bufnr number buffer number +---@param max_tag_len number longest tag length +---@param max_group_len number longest group length +---@param l number line number +---@return number l incremented +function IconDisplay:render(bufnr, max_tag_len, max_group_len, l) + local fmt = string.format("%%s %%-%d.%ds %%-%d.%ds %%s", max_tag_len, max_tag_len, max_group_len, max_group_len) + local text = string.format(fmt, self.icon, self.tag, self.group, self.def) + + vim.api.nvim_buf_set_lines(bufnr, l, -1, true, { text }) + vim.api.nvim_buf_add_highlight(bufnr, -1, self.group, l, 0, -1) + + return l + 1 +end + +---Render a single line of text +---@param bufnr number +---@param l number line number +---@return number l incremented +local function render_line(bufnr, l, text) + vim.api.nvim_buf_set_lines(bufnr, l, -1, true, { text }) + return l + 1 +end + +---Render all icons sorted by tag +---@param bufnr number +---@param l number line number +---@param icons table +---@param header string +---@return number l incremented +local function render_icons(bufnr, l, icons, header) + local max_tag_len = 0 + local max_group_len = 0 + + local displays = {} + ---@cast displays IconDisplay[] + + -- build all icon displays + for tag, icon in pairs(icons) do + local display = IconDisplay:new { tag = tag, name = icon.name, icon = icon.icon } + if display then + table.insert(displays, display) + max_tag_len = math.max(max_tag_len, #display.tag) + max_group_len = math.max(max_group_len, #display.group) + end + end + + -- sort by name + table.sort(displays, function(a, b) + return a.name < b.name + end) + + l = render_line(bufnr, l, header) + l = render_line(bufnr, l, header:gsub(".", "-")) + for _, display in ipairs(displays) do + l = display:render(bufnr, max_tag_len, max_group_len, l) + end + l = render_line(bufnr, l, "") + + return l +end + +---Create a buffer similar to :ru syntax/hitest.vim displaying each set icons +---Icon, name, , concrete highlight definition +---tag and header follows param +---@param default_icon table no tag "Default" +---@param global_override table[] all global overrides "Overrides" +---@param icons_by_filename table[] filename "By File Name" +---@param icons_by_file_extension table[] extension "By File Extension" +---@param icons_by_operating_system table[] os "By Operating System" +---@param icons_by_desktop_environment table[] os "By Desktop Environment" +---@param icons_by_window_manager table[] os "By Window Manager" +return function( + default_icon, + global_override, + icons_by_filename, + icons_by_file_extension, + icons_by_operating_system, + icons_by_desktop_environment, + icons_by_window_manager +) + -- create a buffer + local bufnr = vim.api.nvim_create_buf(false, true) + + -- render and highlight each section + local l = 0 + l = render_icons(bufnr, l, { [""] = default_icon }, "Default") + if global_override and next(global_override) then + l = render_icons(bufnr, l, global_override, "Overrides") + end + l = render_icons(bufnr, l, icons_by_filename, "By File Name") + l = render_icons(bufnr, l, icons_by_file_extension, "By File Extension") + l = render_icons(bufnr, l, icons_by_operating_system, "By Operating System") + l = render_icons(bufnr, l, icons_by_desktop_environment, "By Desktop Environment") + render_icons(bufnr, l, icons_by_window_manager, "By Window Manager") + + -- finalise and focus the buffer + vim.api.nvim_buf_set_option(bufnr, "modifiable", false) + vim.cmd.buffer(bufnr) +end diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons/icons-default.lua b/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons/icons-default.lua new file mode 100644 index 00000000..7189e9d4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons/icons-default.lua @@ -0,0 +1,3760 @@ +local icons_by_filename = { + ["build.gradle"] = { + icon = "", + color = "#005f87", + cterm_color = "24", + name = "GradleBuildScript", + }, + ["settings.gradle"] = { + icon = "", + color = "#005f87", + cterm_color = "24", + name = "GradleSettings", + }, + [".babelrc"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Babelrc", + }, + [".bash_profile"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "BashProfile", + }, + [".bashrc"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "Bashrc", + }, + [".dockerignore"] = { + icon = "󰡨", + color = "#458ee6", + cterm_color = "68", + name = "Dockerfile", + }, + [".ds_store"] = { + icon = "", + color = "#41535b", + cterm_color = "239", + name = "DsStore", + }, + [".editorconfig"] = { + icon = "", + color = "#fff2f2", + cterm_color = "255", + name = "EditorConfig", + }, + [".env"] = { + icon = "", + color = "#faf743", + cterm_color = "227", + name = "Env", + }, + [".eslintrc"] = { + icon = "", + color = "#4b32c3", + cterm_color = "56", + name = "Eslintrc", + }, + [".eslintignore"] = { + icon = "", + color = "#4b32c3", + cterm_color = "56", + name = "EslintIgnore", + }, + [".gitattributes"] = { + icon = "", + color = "#f54d27", + cterm_color = "196", + name = "GitAttributes", + }, + [".gitconfig"] = { + icon = "", + color = "#f54d27", + cterm_color = "196", + name = "GitConfig", + }, + [".gitignore"] = { + icon = "", + color = "#f54d27", + cterm_color = "196", + name = "GitIgnore", + }, + [".gitlab-ci.yml"] = { + icon = "", + color = "#e24329", + cterm_color = "196", + name = "GitlabCI", + }, + [".gitmodules"] = { + icon = "", + color = "#f54d27", + cterm_color = "196", + name = "GitModules", + }, + [".gtkrc-2.0"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "GTK", + }, + [".gvimrc"] = { + icon = "", + color = "#019833", + cterm_color = "28", + name = "Gvimrc", + }, + [".luaurc"] = { + icon = "", + color = "#00a2ff", + cterm_color = "75", + name = "Luaurc", + }, + [".mailmap"] = { + icon = "󰊢", + color = "#41535b", + cterm_color = "239", + name = "Mailmap", + }, + [".npmignore"] = { + icon = "", + color = "#E8274B", + cterm_color = "197", + name = "NPMIgnore", + }, + [".npmrc"] = { + icon = "", + color = "#E8274B", + cterm_color = "197", + name = "NPMrc", + }, + [".prettierrc"] = { + icon = "", + color = "#4285F4", + cterm_color = "33", + name = "PrettierConfig", + }, + [".settings.json"] = { + icon = "", + color = "#854CC7", + cterm_color = "98", + name = "SettingsJson", + }, + [".SRCINFO"] = { + icon = "󰣇", + color = "#0f94d2", + cterm_color = "67", + name = "SRCINFO", + }, + [".vimrc"] = { + icon = "", + color = "#019833", + cterm_color = "28", + name = "Vimrc", + }, + [".Xauthority"] = { + icon = "", + color = "#e54d18", + cterm_color = "196", + name = "Xauthority", + }, + [".xinitrc"] = { + icon = "", + color = "#e54d18", + cterm_color = "196", + name = "XInitrc", + }, + [".Xresources"] = { + icon = "", + color = "#e54d18", + cterm_color = "196", + name = "Xresources", + }, + [".xsession"] = { + icon = "", + color = "#e54d18", + cterm_color = "196", + name = "Xsession", + }, + [".zprofile"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "Zshprofile", + }, + [".zshenv"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "Zshenv", + }, + [".zshrc"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "Zshrc", + }, + ["_gvimrc"] = { + icon = "", + color = "#019833", + cterm_color = "28", + name = "Gvimrc", + }, + ["_vimrc"] = { + icon = "", + color = "#019833", + cterm_color = "28", + name = "Vimrc", + }, + ["R"] = { + icon = "󰟔", + color = "#2266ba", + cterm_color = "25", + name = "R", + }, + ["avif"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Avif", + }, + ["brewfile"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Brewfile", + }, + ["bspwmrc"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "BSPWM", + }, + ["build"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "BazelBuild", + }, + ["checkhealth"] = { + icon = "󰓙", + color = "#75B4FB", + cterm_color = "75", + name = "Checkhealth", + }, + ["cmakelists.txt"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "CMakeLists", + }, + ["commit_editmsg"] = { + icon = "", + color = "#f54d27", + cterm_color = "196", + name = "GitCommit", + }, + ["compose.yaml"] = { + icon = "󰡨", + color = "#458ee6", + cterm_color = "68", + name = "Dockerfile", + }, + ["compose.yml"] = { + icon = "󰡨", + color = "#458ee6", + cterm_color = "68", + name = "Dockerfile", + }, + ["config"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Config", + }, + ["containerfile"] = { + icon = "󰡨", + color = "#458ee6", + cterm_color = "68", + name = "Dockerfile", + }, + ["copying"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "License", + }, + ["copying.lesser"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "License", + }, + ["docker-compose.yaml"] = { + icon = "󰡨", + color = "#458ee6", + cterm_color = "68", + name = "Dockerfile", + }, + ["docker-compose.yml"] = { + icon = "󰡨", + color = "#458ee6", + cterm_color = "68", + name = "Dockerfile", + }, + ["dockerfile"] = { + icon = "󰡨", + color = "#458ee6", + cterm_color = "68", + name = "Dockerfile", + }, + ["ext_typoscript_setup.txt"] = { + icon = "", + color = "#FF8700", + cterm_color = "208", + name = "TypoScriptSetup", + }, + ["favicon.ico"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Favicon", + }, + ["fp-info-cache"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCadCache", + }, + ["fp-lib-table"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCadFootprintTable", + }, + ["FreeCAD.conf"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCADConfig", + }, + ["gemfile$"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Gemfile", + }, + ["gnumakefile"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Makefile", + }, + ["gradlew"] = { + icon = "", + color = "#005f87", + cterm_color = "24", + name = "GradleWrapperScript", + }, + ["gradle.properties"] = { + icon = "", + color = "#005f87", + cterm_color = "24", + name = "GradleProperties", + }, + ["gradle-wrapper.properties"] = { + icon = "", + color = "#005f87", + cterm_color = "24", + name = "GradleWrapperProperties", + }, + ["groovy"] = { + icon = "", + color = "#4a687c", + cterm_color = "24", + name = "Groovy", + }, + ["gruntfile.babel.js"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "Gruntfile", + }, + ["gruntfile.coffee"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "Gruntfile", + }, + ["gruntfile.js"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "Gruntfile", + }, + ["gruntfile.ts"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "Gruntfile", + }, + ["gtkrc"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "GTK", + }, + ["gulpfile.babel.js"] = { + icon = "", + color = "#cc3e44", + cterm_color = "167", + name = "Gulpfile", + }, + ["gulpfile.coffee"] = { + icon = "", + color = "#cc3e44", + cterm_color = "167", + name = "Gulpfile", + }, + ["gulpfile.js"] = { + icon = "", + color = "#cc3e44", + cterm_color = "167", + name = "Gulpfile", + }, + ["gulpfile.ts"] = { + icon = "", + color = "#cc3e44", + cterm_color = "167", + name = "Gulpfile", + }, + ["hyprland.conf"] = { + icon = "", + color = "#00aaae", + cterm_color = "37", + name = "Hyprland", + }, + ["i3blocks.conf"] = { + icon = "", + color = "#e8ebee", + cterm_color = "255", + name = "i3", + }, + ["i3status.conf"] = { + icon = "", + color = "#e8ebee", + cterm_color = "255", + name = "i3", + }, + ["cantorrc"] = { + icon = "", + color = "#1c99f3", + cterm_color = "32", + name = "Cantorrc", + }, + ["kalgebrarc"] = { + icon = "", + color = "#1c99f3", + cterm_color = "32", + name = "Kalgebrarc", + }, + ["kdeglobals"] = { + icon = "", + color = "#1c99f3", + cterm_color = "32", + name = "KDEglobals", + }, + ["kdenlive-layoutsrc"] = { + icon = "", + color = "#83b8f2", + cterm_color = "110", + name = "KdenliveLayoutsrc", + }, + ["kdenliverc"] = { + icon = "", + color = "#83b8f2", + cterm_color = "110", + name = "Kdenliverc", + }, + ["kritadisplayrc"] = { + icon = "", + color = "#f245fb", + cterm_color = "201", + name = "Kritadisplayrc", + }, + ["kritarc"] = { + icon = "", + color = "#f245fb", + cterm_color = "201", + name = "Kritarc", + }, + ["license"] = { + icon = "", + color = "#d0bf41", + cterm_color = "185", + name = "License", + }, + ["lxde-rc.xml"] = { + icon = "", + color = "#909090", + cterm_color = "246", + name = "LXDEConfigFile", + }, + ["lxqt.conf"] = { + icon = "", + color = "#0192d3", + cterm_color = "32", + name = "LXQtConfigFile", + }, + ["makefile"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Makefile", + }, + ["mix.lock"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "MixLock", + }, + ["mpv.conf"] = { + icon = "", + color = "#3b1342", + cterm_color = "53", + name = "Mpv", + }, + ["node_modules"] = { + icon = "", + color = "#E8274B", + cterm_color = "197", + name = "NodeModules", + }, + ["package.json"] = { + icon = "", + color = "#e8274b", + cterm_color = "197", + name = "PackageJson", + }, + ["package-lock.json"] = { + icon = "", + color = "#7a0d21", + cterm_color = "52", + name = "PackageLockJson", + }, + ["PKGBUILD"] = { + icon = "", + color = "#0f94d2", + cterm_color = "67", + name = "PKGBUILD", + }, + ["platformio.ini"] = { + icon = "", + color = "#f6822b", + cterm_color = "208", + name = "Platformio", + }, + ["pom.xml"] = { + icon = "", + color = "#7a0d21", + cterm_color = "52", + name = "Maven", + }, + ["procfile"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Procfile", + }, + ["PrusaSlicer.ini"] = { + icon = "", + color = "#ec6b23", + cterm_color = "202", + name = "PrusaSlicer", + }, + ["PrusaSlicerGcodeViewer.ini"] = { + icon = "", + color = "#ec6b23", + cterm_color = "202", + name = "PrusaSlicer", + }, + ["py.typed"] = { + icon = "", + color = "#ffbc03", + cterm_color = "214", + name = "Py.typed", + }, + ["QtProject.conf"] = { + icon = "", + color = "#40cd52", + cterm_color = "77", + name = "Qt", + }, + ["r"] = { + icon = "󰟔", + color = "#2266ba", + cterm_color = "25", + name = "R", + }, + ["rakefile"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Rakefile", + }, + ["rmd"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Rmd", + }, + ["svelte.config.js"] = { + icon = "", + color = "#ff3e00", + cterm_color = "196", + name = "SvelteConfig", + }, + ["sxhkdrc"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "BSPWM", + }, + ["sym-lib-table"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCadSymbolTable", + }, + ["tailwind.config.js"] = { + icon = "󱏿", + color = "#20c2e3", + cterm_color = "45", + name = "TailwindConfig", + }, + ["tailwind.config.mjs"] = { + icon = "󱏿", + color = "#20c2e3", + cterm_color = "45", + name = "TailwindConfig", + }, + ["tailwind.config.ts"] = { + icon = "󱏿", + color = "#20c2e3", + cterm_color = "45", + name = "TailwindConfig", + }, + ["tmux.conf"] = { + icon = "", + color = "#14ba19", + cterm_color = "34", + name = "Tmux", + }, + ["tmux.conf.local"] = { + icon = "", + color = "#14ba19", + cterm_color = "34", + name = "Tmux", + }, + ["tsconfig.json"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "TSConfig", + }, + ["unlicense"] = { + icon = "", + color = "#d0bf41", + cterm_color = "185", + name = "License", + }, + ["vagrantfile$"] = { + icon = "", + color = "#1563FF", + cterm_color = "27", + name = "Vagrantfile", + }, + ["vlcrc"] = { + icon = "󰕼", + color = "#ee7a00", + cterm_color = "208", + name = "VLC", + }, + ["webpack"] = { + icon = "󰜫", + color = "#519aba", + cterm_color = "74", + name = "Webpack", + }, + ["weston.ini"] = { + icon = "", + color = "#ffbb01", + cterm_color = "214", + name = "Weston", + }, + ["workspace"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "BazelWorkspace", + }, + ["xmobarrc"] = { + icon = "", + color = "#fd4d5d", + cterm_color = "203", + name = "xmonad", + }, + ["xmobarrc.hs"] = { + icon = "", + color = "#fd4d5d", + cterm_color = "203", + name = "xmonad", + }, + ["xmonad.hs"] = { + icon = "", + color = "#fd4d5d", + cterm_color = "203", + name = "xmonad", + }, + ["xorg.conf"] = { + icon = "", + color = "#e54d18", + cterm_color = "196", + name = "XorgConf", + }, + ["xsettingsd.conf"] = { + icon = "", + color = "#e54d18", + cterm_color = "196", + name = "XSettingsdConf", + }, + ["build.zig.zon"] = { + icon = "", + color = "#f69a1b", + cterm_color = "172", + name = "ZigObjectNotation", + }, +} + +local icons_by_file_extension = { + ["3gp"] = { + icon = "", + color = "#FD971F", + cterm_color = "208", + name = "3gp", + }, + ["3mf"] = { + icon = "󰆧", + color = "#888888", + cterm_color = "102", + name = "3DObjectFile", + }, + ["7z"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "7z", + }, + ["a"] = { + icon = "", + color = "#dcddd6", + cterm_color = "253", + name = "StaticLibraryArchive", + }, + ["aac"] = { + icon = "", + color = "#00afff", + cterm_color = "39", + name = "AdvancedAudioCoding", + }, + ["aif"] = { + icon = "", + color = "#00afff", + cterm_color = "39", + name = "AudioInterchangeFileFormat", + }, + ["aiff"] = { + icon = "", + color = "#00afff", + cterm_color = "39", + name = "AudioInterchangeFileFormat", + }, + ["ape"] = { + icon = "", + color = "#00afff", + cterm_color = "39", + name = "MonkeysAudio", + }, + ["ai"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Ai", + }, + ["android"] = { + icon = "", + color = "#34a853", + cterm_color = "35", + name = "Android", + }, + ["apk"] = { + icon = "", + color = "#34a853", + cterm_color = "35", + name = "apk", + }, + ["app"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "App", + }, + ["applescript"] = { + icon = "", + color = "#6d8085", + cterm_color = "66", + name = "AppleScript", + }, + ["asc"] = { + icon = "󰦝", + color = "#576d7f", + cterm_color = "242", + name = "Asc", + }, + ["ass"] = { + icon = "󰨖", + color = "#ffb713", + cterm_color = "214", + name = "Ass", + }, + ["astro"] = { + icon = "", + color = "#e23f67", + cterm_color = "197", + name = "Astro", + }, + ["awk"] = { + icon = "", + color = "#4d5a5e", + cterm_color = "240", + name = "Awk", + }, + ["azcli"] = { + icon = "", + color = "#0078d4", + cterm_color = "32", + name = "AzureCli", + }, + ["bak"] = { + icon = "󰁯", + color = "#6d8086", + cterm_color = "66", + name = "Backup", + }, + ["bash"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "Bash", + }, + ["bat"] = { + icon = "", + color = "#C1F12E", + cterm_color = "191", + name = "Bat", + }, + ["bazel"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "Bazel", + }, + ["bib"] = { + icon = "󱉟", + color = "#cbcb41", + cterm_color = "185", + name = "BibTeX", + }, + ["bicep"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Bicep", + }, + ["bicepparam"] = { + icon = "", + color = "#9f74b3", + cterm_color = "133", + name = "BicepParameters", + }, + ["bin"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "Bin", + }, + ["blade.php"] = { + icon = "", + color = "#f05340", + cterm_color = "203", + name = "Blade", + }, + ["blend"] = { + icon = "󰂫", + color = "#ea7600", + cterm_color = "208", + name = "Blender", + }, + ["bmp"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Bmp", + }, + ["blp"] = { + icon = "󰺾", + color = "#5796E2", + cterm_color = "68", + name = "Blueprint", + }, + ["brep"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "BoundaryRepresentation", + }, + ["bz"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "Bz", + }, + ["bz2"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "Bz2", + }, + ["bz3"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "Bz3", + }, + ["bzl"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "Bzl", + }, + ["c"] = { + icon = "", + color = "#599eff", + cterm_color = "111", + name = "C", + }, + ["c++"] = { + icon = "", + color = "#f34b7d", + cterm_color = "204", + name = "CPlusPlus", + }, + ["cache"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "Cache", + }, + ["cast"] = { + icon = "", + color = "#FD971F", + cterm_color = "208", + name = "Asciinema", + }, + ["cbl"] = { + icon = "⚙", + color = "#005ca5", + cterm_color = "25", + name = "Cobol", + }, + ["cc"] = { + icon = "", + color = "#f34b7d", + cterm_color = "204", + name = "CPlusPlus", + }, + ["ccm"] = { + icon = "", + color = "#f34b7d", + cterm_color = "204", + name = "CPlusPlusModule", + }, + ["cfg"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Configuration", + }, + ["cjs"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Cjs", + }, + ["clj"] = { + icon = "", + color = "#8dc149", + cterm_color = "113", + name = "Clojure", + }, + ["cljc"] = { + icon = "", + color = "#8dc149", + cterm_color = "113", + name = "ClojureC", + }, + ["cljs"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "ClojureJS", + }, + ["cljd"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "ClojureDart", + }, + ["cmake"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "CMake", + }, + ["cob"] = { + icon = "⚙", + color = "#005ca5", + cterm_color = "25", + name = "Cobol", + }, + ["cobol"] = { + icon = "⚙", + color = "#005ca5", + cterm_color = "25", + name = "Cobol", + }, + ["coffee"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Coffee", + }, + ["conf"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Conf", + }, + ["config.ru"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "ConfigRu", + }, + ["cp"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Cp", + }, + ["cpp"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Cpp", + }, + ["cppm"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Cppm", + }, + ["cpy"] = { + icon = "⚙", + color = "#005ca5", + cterm_color = "25", + name = "Cobol", + }, + ["cr"] = { + icon = "", + color = "#c8c8c8", + cterm_color = "251", + name = "Crystal", + }, + ["crdownload"] = { + icon = "", + color = "#44cda8", + cterm_color = "43", + name = "Crdownload", + }, + ["cs"] = { + icon = "󰌛", + color = "#596706", + cterm_color = "58", + name = "Cs", + }, + ["csh"] = { + icon = "", + color = "#4d5a5e", + cterm_color = "240", + name = "Csh", + }, + ["cshtml"] = { + icon = "󱦗", + color = "#512bd4", + cterm_color = "56", + name = "RazorPage", + }, + ["cson"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Cson", + }, + ["csproj"] = { + icon = "󰪮", + color = "#512bd4", + cterm_color = "56", + name = "CSharpProject", + }, + ["css"] = { + icon = "", + color = "#42a5f5", + cterm_color = "75", + name = "Css", + }, + ["csv"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "Csv", + }, + ["cts"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Cts", + }, + ["cu"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "cuda", + }, + ["cue"] = { + icon = "󰲹", + color = "#ed95ae", + cterm_color = "211", + name = "Cue", + }, + ["cuh"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "cudah", + }, + ["cxx"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Cxx", + }, + ["cxxm"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Cxxm", + }, + ["d"] = { + icon = "", + color = "#427819", + cterm_color = "28", + name = "D", + }, + ["d.ts"] = { + icon = "", + color = "#d59855", + cterm_color = "172", + name = "TypeScriptDeclaration", + }, + ["dart"] = { + icon = "", + color = "#03589C", + cterm_color = "25", + name = "Dart", + }, + ["db"] = { + icon = "", + color = "#dad8d8", + cterm_color = "188", + name = "Db", + }, + ["dconf"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "Dconf", + }, + ["desktop"] = { + icon = "", + color = "#563d7c", + cterm_color = "54", + name = "DesktopEntry", + }, + ["diff"] = { + icon = "", + color = "#41535b", + cterm_color = "239", + name = "Diff", + }, + ["dll"] = { + icon = "", + color = "#4d2c0b", + cterm_color = "52", + name = "Dll", + }, + ["doc"] = { + icon = "󰈬", + color = "#185abd", + cterm_color = "26", + name = "Doc", + }, + ["Dockerfile"] = { + icon = "󰡨", + color = "#458ee6", + cterm_color = "68", + name = "Dockerfile", + }, + ["docx"] = { + icon = "󰈬", + color = "#185abd", + cterm_color = "26", + name = "Docx", + }, + ["dot"] = { + icon = "󱁉", + color = "#30638e", + cterm_color = "24", + name = "Dot", + }, + ["download"] = { + icon = "", + color = "#44cda8", + cterm_color = "43", + name = "Download", + }, + ["drl"] = { + icon = "", + color = "#ffafaf", + cterm_color = "217", + name = "Drools", + }, + ["dropbox"] = { + icon = "", + color = "#0061FE", + cterm_color = "27", + name = "Dropbox", + }, + ["dump"] = { + icon = "", + color = "#dad8d8", + cterm_color = "188", + name = "Dump", + }, + ["dwg"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "AutoCADDwg", + }, + ["dxf"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "AutoCADDxf", + }, + ["ebook"] = { + icon = "", + color = "#eab16d", + cterm_color = "215", + name = "Ebook", + }, + ["edn"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Edn", + }, + ["eex"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Eex", + }, + ["ejs"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Ejs", + }, + ["elf"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "Elf", + }, + ["el"] = { + icon = "", + color = "#8172be", + cterm_color = "97", + name = "Elisp", + }, + ["elc"] = { + icon = "", + color = "#8172be", + cterm_color = "97", + name = "Elisp", + }, + ["elm"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Elm", + }, + ["eln"] = { + icon = "", + color = "#8172be", + cterm_color = "97", + name = "Elisp", + }, + ["env"] = { + icon = "", + color = "#faf743", + cterm_color = "227", + name = "Env", + }, + ["eot"] = { + icon = "", + color = "#ECECEC", + cterm_color = "255", + name = "EmbeddedOpenTypeFont", + }, + ["epp"] = { + icon = "", + color = "#FFA61A", + cterm_color = "214", + name = "Epp", + }, + ["epub"] = { + icon = "", + color = "#eab16d", + cterm_color = "215", + name = "Epub", + }, + ["erb"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Erb", + }, + ["erl"] = { + icon = "", + color = "#B83998", + cterm_color = "163", + name = "Erl", + }, + ["ex"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Ex", + }, + ["exe"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "Exe", + }, + ["exs"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Exs", + }, + ["f#"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Fsharp", + }, + ["f3d"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "Fusion360", + }, + ["f90"] = { + icon = "󱈚", + color = "#734f96", + cterm_color = "97", + name = "Fortran", + }, + ["fbx"] = { + icon = "󰆧", + color = "#888888", + cterm_color = "102", + name = "3DObjectFile", + }, + ["fcbak"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcmacro"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcmat"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcparam"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcscript"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcstd"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcstd1"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fctb"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fctl"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fdmdownload"] = { + icon = "", + color = "#44cda8", + cterm_color = "43", + name = "Fdmdownload", + }, + ["flac"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "FreeLosslessAudioCodec", + }, + ["flc"] = { + icon = "", + color = "#ECECEC", + cterm_color = "255", + name = "FIGletFontControl", + }, + ["flf"] = { + icon = "", + color = "#ECECEC", + cterm_color = "255", + name = "FIGletFontFormat", + }, + ["fnl"] = { + icon = "", + color = "#fff3d7", + cterm_color = "230", + name = "Fennel", + }, + ["fish"] = { + icon = "", + color = "#4d5a5e", + cterm_color = "240", + name = "Fish", + }, + ["fs"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Fs", + }, + ["fsi"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Fsi", + }, + ["fsscript"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Fsscript", + }, + ["fsx"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Fsx", + }, + ["gcode"] = { + icon = "󰐫", + color = "#1471ad", + cterm_color = "32", + name = "GCode", + }, + ["gd"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "GDScript", + }, + ["gemspec"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Gemspec", + }, + ["gif"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Gif", + }, + ["git"] = { + icon = "", + color = "#F14C28", + cterm_color = "196", + name = "GitLogo", + }, + ["glb"] = { + icon = "", + color = "#FFB13B", + cterm_color = "214", + name = "BinaryGLTF", + }, + ["gnumakefile"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Makefile", + }, + ["go"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Go", + }, + ["godot"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "GodotProject", + }, + ["gql"] = { + icon = "", + color = "#e535ab", + cterm_color = "199", + name = "GraphQL", + }, + ["graphql"] = { + icon = "", + color = "#e535ab", + cterm_color = "199", + name = "GraphQL", + }, + ["gresource"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "GTK", + }, + ["gv"] = { + icon = "󱁉", + color = "#30638e", + cterm_color = "24", + name = "Gv", + }, + ["gz"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "Gz", + }, + ["h"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "H", + }, + ["haml"] = { + icon = "", + color = "#eaeae1", + cterm_color = "255", + name = "Haml", + }, + ["hx"] = { + icon = "", + color = "#ea8220", + cterm_color = "208", + name = "Haxe", + }, + ["hbs"] = { + icon = "", + color = "#f0772b", + cterm_color = "202", + name = "Hbs", + }, + ["hex"] = { + icon = "", + color = "#2e63ff", + cterm_color = "27", + name = "Hexadecimal", + }, + ["heex"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Heex", + }, + ["hh"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Hh", + }, + ["hpp"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Hpp", + }, + ["hrl"] = { + icon = "", + color = "#B83998", + cterm_color = "163", + name = "Hrl", + }, + ["hs"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Hs", + }, + ["htm"] = { + icon = "", + color = "#e34c26", + cterm_color = "196", + name = "Htm", + }, + ["html"] = { + icon = "", + color = "#e44d26", + cterm_color = "196", + name = "Html", + }, + ["huff"] = { + icon = "󰡘", + color = "#4242c7", + cterm_color = "56", + name = "Huff", + }, + ["hurl"] = { + icon = "", + color = "#ff0288", + cterm_color = "198", + name = "Hurl", + }, + ["hxx"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Hxx", + }, + ["ixx"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Ixx", + }, + ["ico"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Ico", + }, + ["ical"] = { + icon = "", + color = "#2B2e83", + cterm_color = "18", + name = "Ical", + }, + ["icalendar"] = { + icon = "", + color = "#2B2e83", + cterm_color = "18", + name = "Icalendar", + }, + ["ics"] = { + icon = "", + color = "#2B2e83", + cterm_color = "18", + name = "Ics", + }, + ["ifb"] = { + icon = "", + color = "#2B2e83", + cterm_color = "18", + name = "Ifb", + }, + ["ifc"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "Ifc", + }, + ["ige"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "Ige", + }, + ["iges"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "Iges", + }, + ["igs"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "Igs", + }, + ["image"] = { + icon = "", + color = "#d0bec8", + cterm_color = "181", + name = "Image", + }, + ["img"] = { + icon = "", + color = "#d0bec8", + cterm_color = "181", + name = "Img", + }, + ["import"] = { + icon = "", + color = "#ECECEC", + cterm_color = "255", + name = "ImportConfiguration", + }, + ["info"] = { + icon = "", + color = "#ffffcd", + cterm_color = "230", + name = "Info", + }, + ["ini"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Ini", + }, + ["ino"] = { + icon = "", + color = "#56b6c2", + cterm_color = "73", + name = "Arduino", + }, + ["iso"] = { + icon = "", + color = "#d0bec8", + cterm_color = "181", + name = "Iso", + }, + ["ipynb"] = { + icon = "", + color = "#51a0cf", + cterm_color = "74", + name = "Notebook", + }, + ["java"] = { + icon = "", + color = "#cc3e44", + cterm_color = "167", + name = "Java", + }, + ["jl"] = { + icon = "", + color = "#a270ba", + cterm_color = "133", + name = "Jl", + }, + ["jwmrc"] = { + icon = "", + color = "#0078cd", + cterm_color = "32", + name = "JWM", + }, + ["jpeg"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Jpeg", + }, + ["jpg"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Jpg", + }, + ["js"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Js", + }, + ["json"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Json", + }, + ["json5"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Json5", + }, + ["jsonc"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "Jsonc", + }, + ["jsx"] = { + icon = "", + color = "#20c2e3", + cterm_color = "45", + name = "Jsx", + }, + ["jxl"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "JpegXl", + }, + ["kbx"] = { + icon = "󰯄", + color = "#737672", + cterm_color = "243", + name = "Kbx", + }, + ["kdb"] = { + icon = "", + color = "#529b34", + cterm_color = "71", + name = "Kdb", + }, + ["kdbx"] = { + icon = "", + color = "#529b34", + cterm_color = "71", + name = "Kdbx", + }, + ["kdenlive"] = { + icon = "", + color = "#83b8f2", + cterm_color = "110", + name = "Kdenlive", + }, + ["kdenlivetitle"] = { + icon = "", + color = "#83b8f2", + cterm_color = "110", + name = "Kdenlive", + }, + ["kicad_dru"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCad", + }, + ["kicad_mod"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCad", + }, + ["kicad_pcb"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCad", + }, + ["kicad_prl"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCad", + }, + ["kicad_pro"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCad", + }, + ["kicad_sch"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCad", + }, + ["kicad_sym"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCad", + }, + ["kicad_wks"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "KiCad", + }, + ["ko"] = { + icon = "", + color = "#dcddd6", + cterm_color = "253", + name = "LinuxKernelObject", + }, + ["kpp"] = { + icon = "", + color = "#f245fb", + cterm_color = "201", + name = "Krita", + }, + ["kra"] = { + icon = "", + color = "#f245fb", + cterm_color = "201", + name = "Krita", + }, + ["krz"] = { + icon = "", + color = "#f245fb", + cterm_color = "201", + name = "Krita", + }, + ["ksh"] = { + icon = "", + color = "#4d5a5e", + cterm_color = "240", + name = "Ksh", + }, + ["kt"] = { + icon = "", + color = "#7F52FF", + cterm_color = "99", + name = "Kotlin", + }, + ["kts"] = { + icon = "", + color = "#7F52FF", + cterm_color = "99", + name = "KotlinScript", + }, + ["lck"] = { + icon = "", + color = "#bbbbbb", + cterm_color = "250", + name = "Lock", + }, + ["leex"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Leex", + }, + ["less"] = { + icon = "", + color = "#563d7c", + cterm_color = "54", + name = "Less", + }, + ["lff"] = { + icon = "", + color = "#ECECEC", + cterm_color = "255", + name = "LibrecadFontFile", + }, + ["lhs"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Lhs", + }, + ["lib"] = { + icon = "", + color = "#4d2c0b", + cterm_color = "52", + name = "Lib", + }, + ["license"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "License", + }, + ["liquid"] = { + icon = "", + color = "#95BF47", + cterm_color = "106", + name = "Liquid", + }, + ["lock"] = { + icon = "", + color = "#bbbbbb", + cterm_color = "250", + name = "Lock", + }, + ["log"] = { + icon = "󰌱", + color = "#dddddd", + cterm_color = "253", + name = "Log", + }, + ["lrc"] = { + icon = "󰨖", + color = "#ffb713", + cterm_color = "214", + name = "Lrc", + }, + ["lua"] = { + icon = "", + color = "#51a0cf", + cterm_color = "74", + name = "Lua", + }, + ["luac"] = { + icon = "", + color = "#51a0cf", + cterm_color = "74", + name = "Lua", + }, + ["luau"] = { + icon = "", + color = "#00a2ff", + cterm_color = "75", + name = "Luau", + }, + ["m3u"] = { + icon = "󰲹", + color = "#ed95ae", + cterm_color = "211", + name = "M3u", + }, + ["m3u8"] = { + icon = "󰲹", + color = "#ed95ae", + cterm_color = "211", + name = "M3u8", + }, + ["m4a"] = { + icon = "", + color = "#00afff", + cterm_color = "39", + name = "MPEG4", + }, + ["m4v"] = { + icon = "", + color = "#FD971F", + cterm_color = "208", + name = "M4V", + }, + ["magnet"] = { + icon = "", + color = "#a51b16", + cterm_color = "124", + name = "Magnet", + }, + ["makefile"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Makefile", + }, + ["markdown"] = { + icon = "", + color = "#dddddd", + cterm_color = "253", + name = "Markdown", + }, + ["material"] = { + icon = "󰔉", + color = "#B83998", + cterm_color = "163", + name = "Material", + }, + ["md"] = { + icon = "", + color = "#dddddd", + cterm_color = "253", + name = "Md", + }, + ["md5"] = { + icon = "󰕥", + color = "#8c86af", + cterm_color = "103", + name = "Md5", + }, + ["mdx"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Mdx", + }, + ["mint"] = { + icon = "󰌪", + color = "#87c095", + cterm_color = "108", + name = "Mint", + }, + ["mjs"] = { + icon = "", + color = "#f1e05a", + cterm_color = "185", + name = "Mjs", + }, + ["mk"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Makefile", + }, + ["mkv"] = { + icon = "", + color = "#FD971F", + cterm_color = "208", + name = "Mkv", + }, + ["ml"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "Ml", + }, + ["mli"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "Mli", + }, + ["m"] = { + icon = "", + color = "#599eff", + cterm_color = "111", + name = "ObjectiveC", + }, + ["mm"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "ObjectiveCPlusPlus", + }, + ["mo"] = { + icon = "∞", + color = "#9772FB", + cterm_color = "135", + name = "Motoko", + }, + ["mobi"] = { + icon = "", + color = "#eab16d", + cterm_color = "215", + name = "Mobi", + }, + ["mov"] = { + icon = "", + color = "#FD971F", + cterm_color = "208", + name = "MOV", + }, + ["mp3"] = { + icon = "", + color = "#00afff", + cterm_color = "39", + name = "MPEGAudioLayerIII", + }, + ["mp4"] = { + icon = "", + color = "#FD971F", + cterm_color = "208", + name = "Mp4", + }, + ["mpp"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Mpp", + }, + ["msf"] = { + icon = "", + color = "#137be1", + cterm_color = "33", + name = "Thunderbird", + }, + ["mts"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Mts", + }, + ["mustache"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "Mustache", + }, + ["nfo"] = { + icon = "", + color = "#ffffcd", + cterm_color = "230", + name = "Nfo", + }, + ["nim"] = { + icon = "", + color = "#f3d400", + cterm_color = "220", + name = "Nim", + }, + ["nix"] = { + icon = "", + color = "#7ebae4", + cterm_color = "110", + name = "Nix", + }, + ["nswag"] = { + icon = "", + color = "#85ea2d", + cterm_color = "112", + name = "Nswag", + }, + ["nu"] = { + icon = ">", + color = "#3aa675", + cterm_color = "36", + name = "Nushell", + }, + ["o"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "ObjectFile", + }, + ["obj"] = { + icon = "󰆧", + color = "#888888", + cterm_color = "102", + name = "3DObjectFile", + }, + ["ogg"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "OggVorbis", + }, + ["opus"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "OpusAudioFile", + }, + ["org"] = { + icon = "", + color = "#77AA99", + cterm_color = "73", + name = "OrgMode", + }, + ["otf"] = { + icon = "", + color = "#ECECEC", + cterm_color = "255", + name = "OpenTypeFont", + }, + ["out"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "Out", + }, + ["part"] = { + icon = "", + color = "#44cda8", + cterm_color = "43", + name = "Part", + }, + ["patch"] = { + icon = "", + color = "#41535b", + cterm_color = "239", + name = "Patch", + }, + ["pck"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "PackedResource", + }, + ["pcm"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "PulseCodeModulation", + }, + ["pdf"] = { + icon = "", + color = "#b30b00", + cterm_color = "124", + name = "Pdf", + }, + ["php"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Php", + }, + ["pl"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Pl", + }, + ["pls"] = { + icon = "󰲹", + color = "#ed95ae", + cterm_color = "211", + name = "Pls", + }, + ["ply"] = { + icon = "󰆧", + color = "#888888", + cterm_color = "102", + name = "3DObjectFile", + }, + ["pm"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Pm", + }, + ["png"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Png", + }, + ["po"] = { + icon = "", + color = "#2596be", + cterm_color = "31", + name = "Localization", + }, + ["pot"] = { + icon = "", + color = "#2596be", + cterm_color = "31", + name = "Localization", + }, + ["pp"] = { + icon = "", + color = "#FFA61A", + cterm_color = "214", + name = "Pp", + }, + ["ppt"] = { + icon = "󰈧", + color = "#cb4a32", + cterm_color = "160", + name = "Ppt", + }, + ["prisma"] = { + icon = "", + color = "#5a67d8", + cterm_color = "62", + name = "Prisma", + }, + ["pro"] = { + icon = "", + color = "#e4b854", + cterm_color = "179", + name = "Prolog", + }, + ["ps1"] = { + icon = "󰨊", + color = "#4273ca", + cterm_color = "68", + name = "PsScriptfile", + }, + ["psd1"] = { + icon = "󰨊", + color = "#6975c4", + cterm_color = "68", + name = "PsManifestfile", + }, + ["psm1"] = { + icon = "󰨊", + color = "#6975c4", + cterm_color = "68", + name = "PsScriptModulefile", + }, + ["psb"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Psb", + }, + ["psd"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Psd", + }, + ["pub"] = { + icon = "󰷖", + color = "#e3c58e", + cterm_color = "222", + name = "Pub", + }, + ["pxd"] = { + icon = "", + color = "#5aa7e4", + cterm_color = "39", + name = "Pxd", + }, + ["pxi"] = { + icon = "", + color = "#5aa7e4", + cterm_color = "39", + name = "Pxi", + }, + ["py"] = { + icon = "", + color = "#ffbc03", + cterm_color = "214", + name = "Py", + }, + ["pyc"] = { + icon = "", + color = "#ffe291", + cterm_color = "222", + name = "Pyc", + }, + ["pyd"] = { + icon = "", + color = "#ffe291", + cterm_color = "222", + name = "Pyd", + }, + ["pyi"] = { + icon = "", + color = "#ffbc03", + cterm_color = "214", + name = "Pyi", + }, + ["pyo"] = { + icon = "", + color = "#ffe291", + cterm_color = "222", + name = "Pyo", + }, + ["pyx"] = { + icon = "", + color = "#5aa7e4", + cterm_color = "39", + name = "Pyx", + }, + ["qm"] = { + icon = "", + color = "#2596be", + cterm_color = "31", + name = "Localization", + }, + ["qml"] = { + icon = "", + color = "#40cd52", + cterm_color = "77", + name = "Qt", + }, + ["qrc"] = { + icon = "", + color = "#40cd52", + cterm_color = "77", + name = "Qt", + }, + ["qss"] = { + icon = "", + color = "#40cd52", + cterm_color = "77", + name = "Qt", + }, + ["query"] = { + icon = "", + color = "#90a850", + cterm_color = "107", + name = "Query", + }, + ["r"] = { + icon = "󰟔", + color = "#2266ba", + cterm_color = "25", + name = "R", + }, + ["rake"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Rake", + }, + ["rar"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "Rar", + }, + ["razor"] = { + icon = "󱦘", + color = "#512bd4", + cterm_color = "56", + name = "RazorPage", + }, + ["rb"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Rb", + }, + ["res"] = { + icon = "", + color = "#cc3e44", + cterm_color = "167", + name = "ReScript", + }, + ["resi"] = { + icon = "", + color = "#f55385", + cterm_color = "204", + name = "ReScriptInterface", + }, + ["rlib"] = { + icon = "", + color = "#dea584", + cterm_color = "216", + name = "Rlib", + }, + ["rmd"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Rmd", + }, + ["rproj"] = { + icon = "󰗆", + color = "#358a5b", + cterm_color = "29", + name = "Rproj", + }, + ["rs"] = { + icon = "", + color = "#dea584", + cterm_color = "216", + name = "Rs", + }, + ["rss"] = { + icon = "", + color = "#FB9D3B", + cterm_color = "215", + name = "Rss", + }, + ["sass"] = { + icon = "", + color = "#f55385", + cterm_color = "204", + name = "Sass", + }, + ["sbt"] = { + icon = "", + color = "#cc3e44", + cterm_color = "167", + name = "sbt", + }, + ["scad"] = { + icon = "", + color = "#f9d72c", + cterm_color = "220", + name = "OpenSCAD", + }, + ["scala"] = { + icon = "", + color = "#cc3e44", + cterm_color = "167", + name = "Scala", + }, + ["sc"] = { + icon = "", + color = "#cc3e44", + cterm_color = "167", + name = "ScalaScript", + }, + ["scm"] = { + icon = "󰘧", + color = "#eeeeee", + cterm_color = "255", + name = "Scheme", + }, + ["scss"] = { + icon = "", + color = "#f55385", + cterm_color = "204", + name = "Scss", + }, + ["sh"] = { + icon = "", + color = "#4d5a5e", + cterm_color = "240", + name = "Sh", + }, + ["sha1"] = { + icon = "󰕥", + color = "#8c86af", + cterm_color = "103", + name = "Sha1", + }, + ["sha224"] = { + icon = "󰕥", + color = "#8c86af", + cterm_color = "103", + name = "Sha224", + }, + ["sha256"] = { + icon = "󰕥", + color = "#8c86af", + cterm_color = "103", + name = "Sha256", + }, + ["sha384"] = { + icon = "󰕥", + color = "#8c86af", + cterm_color = "103", + name = "Sha384", + }, + ["sha512"] = { + icon = "󰕥", + color = "#8c86af", + cterm_color = "103", + name = "Sha512", + }, + ["sig"] = { + icon = "λ", + color = "#e37933", + cterm_color = "166", + name = "Sig", + }, + ["signature"] = { + icon = "λ", + color = "#e37933", + cterm_color = "166", + name = "Signature", + }, + ["skp"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "SketchUp", + }, + ["sldasm"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "SolidWorksAsm", + }, + ["sldprt"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "SolidWorksPrt", + }, + ["slim"] = { + icon = "", + color = "#e34c26", + cterm_color = "196", + name = "Slim", + }, + ["sln"] = { + icon = "", + color = "#854CC7", + cterm_color = "98", + name = "Sln", + }, + ["slvs"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "SolveSpace", + }, + ["sml"] = { + icon = "λ", + color = "#e37933", + cterm_color = "166", + name = "Sml", + }, + ["so"] = { + icon = "", + color = "#dcddd6", + cterm_color = "253", + name = "SharedObject", + }, + ["sol"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Solidity", + }, + ["spec.js"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "SpecJs", + }, + ["spec.jsx"] = { + icon = "", + color = "#20c2e3", + cterm_color = "45", + name = "JavaScriptReactSpec", + }, + ["spec.ts"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "SpecTs", + }, + ["spec.tsx"] = { + icon = "", + color = "#1354bf", + cterm_color = "26", + name = "TypeScriptReactSpec", + }, + ["sql"] = { + icon = "", + color = "#dad8d8", + cterm_color = "188", + name = "Sql", + }, + ["sqlite"] = { + icon = "", + color = "#dad8d8", + cterm_color = "188", + name = "Sql", + }, + ["sqlite3"] = { + icon = "", + color = "#dad8d8", + cterm_color = "188", + name = "Sql", + }, + ["srt"] = { + icon = "󰨖", + color = "#ffb713", + cterm_color = "214", + name = "Srt", + }, + ["ssa"] = { + icon = "󰨖", + color = "#ffb713", + cterm_color = "214", + name = "Ssa", + }, + ["stl"] = { + icon = "󰆧", + color = "#888888", + cterm_color = "102", + name = "3DObjectFile", + }, + ["strings"] = { + icon = "", + color = "#2596be", + cterm_color = "31", + name = "Localization", + }, + ["ste"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "Ste", + }, + ["step"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "Step", + }, + ["stp"] = { + icon = "󰻫", + color = "#839463", + cterm_color = "101", + name = "Stp", + }, + ["styl"] = { + icon = "", + color = "#8dc149", + cterm_color = "113", + name = "Styl", + }, + ["sub"] = { + icon = "󰨖", + color = "#ffb713", + cterm_color = "214", + name = "Sub", + }, + ["sublime"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "Sublime", + }, + ["suo"] = { + icon = "", + color = "#854CC7", + cterm_color = "98", + name = "Suo", + }, + ["sv"] = { + icon = "󰍛", + color = "#019833", + cterm_color = "28", + name = "SystemVerilog", + }, + ["svelte"] = { + icon = "", + color = "#ff3e00", + cterm_color = "196", + name = "Svelte", + }, + ["svh"] = { + icon = "󰍛", + color = "#019833", + cterm_color = "28", + name = "SystemVerilog", + }, + ["svg"] = { + icon = "󰜡", + color = "#FFB13B", + cterm_color = "214", + name = "Svg", + }, + ["swift"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "Swift", + }, + ["t"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Tor", + }, + ["tbc"] = { + icon = "󰛓", + color = "#1e5cb3", + cterm_color = "25", + name = "Tcl", + }, + ["tcl"] = { + icon = "󰛓", + color = "#1e5cb3", + cterm_color = "25", + name = "Tcl", + }, + ["templ"] = { + icon = "", + color = "#dbbd30", + cterm_color = "178", + name = "Templ", + }, + ["terminal"] = { + icon = "", + color = "#31B53E", + cterm_color = "34", + name = "Terminal", + }, + ["test.js"] = { + icon = "", + color = "#cbcb41", + cterm_color = "185", + name = "TestJs", + }, + ["test.jsx"] = { + icon = "", + color = "#20c2e3", + cterm_color = "45", + name = "JavaScriptReactTest", + }, + ["test.ts"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "TestTs", + }, + ["test.tsx"] = { + icon = "", + color = "#1354bf", + cterm_color = "26", + name = "TypeScriptReactTest", + }, + ["tex"] = { + icon = "", + color = "#3D6117", + cterm_color = "22", + name = "Tex", + }, + ["tf"] = { + icon = "", + color = "#5F43E9", + cterm_color = "93", + name = "Terraform", + }, + ["tfvars"] = { + icon = "", + color = "#5F43E9", + cterm_color = "93", + name = "TFVars", + }, + ["tgz"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "Tgz", + }, + ["tmux"] = { + icon = "", + color = "#14ba19", + cterm_color = "34", + name = "Tmux", + }, + ["toml"] = { + icon = "", + color = "#9c4221", + cterm_color = "124", + name = "Toml", + }, + ["torrent"] = { + icon = "", + color = "#44cda8", + cterm_color = "43", + name = "Torrent", + }, + ["tres"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "GodotTextResource", + }, + ["ts"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "TypeScript", + }, + ["tscn"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "GodotTextScene", + }, + ["tsconfig"] = { + icon = "", + color = "#FF8700", + cterm_color = "208", + name = "TypoScriptConfig", + }, + ["tsx"] = { + icon = "", + color = "#1354bf", + cterm_color = "26", + name = "Tsx", + }, + ["ttf"] = { + icon = "", + color = "#ECECEC", + cterm_color = "255", + name = "TrueTypeFont", + }, + ["twig"] = { + icon = "", + color = "#8dc149", + cterm_color = "113", + name = "Twig", + }, + ["txz"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "Txz", + }, + ["typoscript"] = { + icon = "", + color = "#FF8700", + cterm_color = "208", + name = "TypoScript", + }, + ["txt"] = { + icon = "󰈙", + color = "#89e051", + cterm_color = "113", + name = "Txt", + }, + ["ui"] = { + icon = "", + color = "#0c306e", + cterm_color = "17", + name = "UI", + }, + ["v"] = { + icon = "󰍛", + color = "#019833", + cterm_color = "28", + name = "Verilog", + }, + ["vala"] = { + icon = "", + color = "#7239b3", + cterm_color = "91", + name = "Vala", + }, + ["vh"] = { + icon = "󰍛", + color = "#019833", + cterm_color = "28", + name = "Verilog", + }, + ["vhd"] = { + icon = "󰍛", + color = "#019833", + cterm_color = "28", + name = "VHDL", + }, + ["vhdl"] = { + icon = "󰍛", + color = "#019833", + cterm_color = "28", + name = "VHDL", + }, + ["vim"] = { + icon = "", + color = "#019833", + cterm_color = "28", + name = "Vim", + }, + ["vsh"] = { + icon = "", + color = "#5d87bf", + cterm_color = "67", + name = "Vlang", + }, + ["vsix"] = { + icon = "", + color = "#854CC7", + cterm_color = "98", + name = "Vsix", + }, + ["vue"] = { + icon = "", + color = "#8dc149", + cterm_color = "113", + name = "Vue", + }, + ["wasm"] = { + icon = "", + color = "#5c4cdb", + cterm_color = "62", + name = "Wasm", + }, + ["wav"] = { + icon = "", + color = "#00afff", + cterm_color = "39", + name = "WaveformAudioFile", + }, + ["webm"] = { + icon = "", + color = "#FD971F", + cterm_color = "208", + name = "Webm", + }, + ["webmanifest"] = { + icon = "", + color = "#f1e05a", + cterm_color = "185", + name = "Webmanifest", + }, + ["webp"] = { + icon = "", + color = "#a074c4", + cterm_color = "140", + name = "Webp", + }, + ["webpack"] = { + icon = "󰜫", + color = "#519aba", + cterm_color = "74", + name = "Webpack", + }, + ["wma"] = { + icon = "", + color = "#00afff", + cterm_color = "39", + name = "WindowsMediaAudio", + }, + ["woff"] = { + icon = "", + color = "#ECECEC", + cterm_color = "255", + name = "WebOpenFontFormat", + }, + ["woff2"] = { + icon = "", + color = "#ECECEC", + cterm_color = "255", + name = "WebOpenFontFormat", + }, + ["wrl"] = { + icon = "󰆧", + color = "#888888", + cterm_color = "102", + name = "VRML", + }, + ["wrz"] = { + icon = "󰆧", + color = "#888888", + cterm_color = "102", + name = "VRML", + }, + ["x"] = { + icon = "", + color = "#599eff", + cterm_color = "111", + name = "Logos", + }, + ["xm"] = { + icon = "", + color = "#519aba", + cterm_color = "74", + name = "Logos", + }, + ["xaml"] = { + icon = "󰙳", + color = "#512bd4", + cterm_color = "56", + name = "Xaml", + }, + ["xcf"] = { + icon = "", + color = "#635b46", + cterm_color = "240", + name = "GIMP", + }, + ["xcplayground"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "XcPlayground", + }, + ["xcstrings"] = { + icon = "", + color = "#2596be", + cterm_color = "31", + name = "XcLocalization", + }, + ["xls"] = { + icon = "󰈛", + color = "#207245", + cterm_color = "29", + name = "Xls", + }, + ["xlsx"] = { + icon = "󰈛", + color = "#207245", + cterm_color = "29", + name = "Xlsx", + }, + ["xml"] = { + icon = "󰗀", + color = "#e37933", + cterm_color = "166", + name = "Xml", + }, + ["xpi"] = { + icon = "", + color = "#ff1b01", + cterm_color = "196", + name = "Xpi", + }, + ["xul"] = { + icon = "", + color = "#e37933", + cterm_color = "166", + name = "Xul", + }, + ["xz"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "Xz", + }, + ["yaml"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Yaml", + }, + ["yml"] = { + icon = "", + color = "#6d8086", + cterm_color = "66", + name = "Yml", + }, + ["zig"] = { + icon = "", + color = "#f69a1b", + cterm_color = "172", + name = "Zig", + }, + ["zip"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "Zip", + }, + ["zsh"] = { + icon = "", + color = "#89e051", + cterm_color = "113", + name = "Zsh", + }, + ["zst"] = { + icon = "", + color = "#eca517", + cterm_color = "214", + name = "Zst", + }, +} + +local icons_by_operating_system = { + ["apple"] = { + icon = "", + color = "#A2AAAD", + cterm_color = "248", + name = "Apple", + }, + ["windows"] = { + icon = "", + color = "#00A4EF", + cterm_color = "39", + name = "Windows", + }, + ["linux"] = { + icon = "", + color = "#fdfdfb", + cterm_color = "231", + name = "Linux", + }, + ["alma"] = { + icon = "", + color = "#ff4649", + cterm_color = "203", + name = "Almalinux", + }, + ["alpine"] = { + icon = "", + color = "#0d597f", + cterm_color = "24", + name = "Alpine", + }, + ["aosc"] = { + icon = "", + color = "#c00000", + cterm_color = "124", + name = "AOSC", + }, + ["arch"] = { + icon = "󰣇", + color = "#0f94d2", + cterm_color = "67", + name = "Arch", + }, + ["archcraft"] = { + icon = "", + color = "#86bba3", + cterm_color = "108", + name = "Archcraft", + }, + ["archlabs"] = { + icon = "", + color = "#503f42", + cterm_color = "238", + name = "Archlabs", + }, + ["arcolinux"] = { + icon = "", + color = "#6690eb", + cterm_color = "68", + name = "ArcoLinux", + }, + ["artix"] = { + icon = "", + color = "#41b4d7", + cterm_color = "38", + name = "Artix", + }, + ["biglinux"] = { + icon = "", + color = "#189fc8", + cterm_color = "38", + name = "BigLinux", + }, + ["centos"] = { + icon = "", + color = "#a2518d", + cterm_color = "132", + name = "Centos", + }, + ["crystallinux"] = { + icon = "", + color = "#a900ff", + cterm_color = "129", + name = "CrystalLinux", + }, + ["debian"] = { + icon = "", + color = "#a80030", + cterm_color = "88", + name = "Debian", + }, + ["deepin"] = { + icon = "", + color = "#2ca7f8", + cterm_color = "39", + name = "Deepin", + }, + ["devuan"] = { + icon = "", + color = "#404a52", + cterm_color = "238", + name = "Devuan", + }, + ["elementary"] = { + icon = "", + color = "#5890c2", + cterm_color = "67", + name = "Elementary", + }, + ["endeavour"] = { + icon = "", + color = "#7b3db9", + cterm_color = "91", + name = "Endeavour", + }, + ["fedora"] = { + icon = "", + color = "#072a5e", + cterm_color = "17", + name = "Fedora", + }, + ["freebsd"] = { + icon = "", + color = "#c90f02", + cterm_color = "160", + name = "FreeBSD", + }, + ["garuda"] = { + icon = "", + color = "#2974e1", + cterm_color = "33", + name = "GarudaLinux", + }, + ["gentoo"] = { + icon = "󰣨", + color = "#b1abce", + cterm_color = "146", + name = "Gentoo", + }, + ["guix"] = { + icon = "", + color = "#ffcc00", + cterm_color = "220", + name = "Guix", + }, + ["hyperbola"] = { + icon = "", + color = "#c0c0c0", + cterm_color = "250", + name = "HyperbolaGNULinuxLibre", + }, + ["illumos"] = { + icon = "", + color = "#ff430f", + cterm_color = "196", + name = "Illumos", + }, + ["kali"] = { + icon = "", + color = "#2777ff", + cterm_color = "69", + name = "Kali", + }, + ["kdeneon"] = { + icon = "", + color = "#20a6a4", + cterm_color = "37", + name = "KDEneon", + }, + ["kubuntu"] = { + icon = "", + color = "#007ac2", + cterm_color = "32", + name = "Kubuntu", + }, + ["locos"] = { + icon = "", + color = "#fab402", + cterm_color = "214", + name = "LocOS", + }, + ["lxle"] = { + icon = "", + color = "#474747", + cterm_color = "238", + name = "LXLE", + }, + ["mint"] = { + icon = "󰣭", + color = "#66af3d", + cterm_color = "70", + name = "Mint", + }, + ["mageia"] = { + icon = "", + color = "#2397d4", + cterm_color = "67", + name = "Mageia", + }, + ["manjaro"] = { + icon = "", + color = "#33b959", + cterm_color = "35", + name = "Manjaro", + }, + ["mxlinux"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "MXLinux", + }, + ["nixos"] = { + icon = "", + color = "#7ab1db", + cterm_color = "110", + name = "NixOS", + }, + ["openbsd"] = { + icon = "", + color = "#f2ca30", + cterm_color = "220", + name = "OpenBSD", + }, + ["opensuse"] = { + icon = "", + color = "#6fb424", + cterm_color = "70", + name = "openSUSE", + }, + ["parabola"] = { + icon = "", + color = "#797dac", + cterm_color = "103", + name = "ParabolaGNULinuxLibre", + }, + ["parrot"] = { + icon = "", + color = "#54deff", + cterm_color = "45", + name = "Parrot", + }, + ["pop_os"] = { + icon = "", + color = "#48b9c7", + cterm_color = "73", + name = "Pop_OS", + }, + ["postmarketos"] = { + icon = "", + color = "#009900", + cterm_color = "28", + name = "postmarketOS", + }, + ["puppylinux"] = { + icon = "", + color = "#a2aeb9", + cterm_color = "145", + name = "PuppyLinux", + }, + ["qubesos"] = { + icon = "", + color = "#3774d8", + cterm_color = "33", + name = "QubesOS", + }, + ["raspberry_pi"] = { + icon = "", + color = "#be1848", + cterm_color = "161", + name = "RaspberryPiOS", + }, + ["redhat"] = { + icon = "󱄛", + color = "#EE0000", + cterm_color = "196", + name = "Redhat", + }, + ["rocky"] = { + icon = "", + color = "#0fb37d", + cterm_color = "36", + name = "RockyLinux", + }, + ["sabayon"] = { + icon = "", + color = "#c6c6c6", + cterm_color = "251", + name = "Sabayon", + }, + ["slackware"] = { + icon = "", + color = "#475fa9", + cterm_color = "61", + name = "Slackware", + }, + ["solus"] = { + icon = "", + color = "#4b5163", + cterm_color = "239", + name = "Solus", + }, + ["tails"] = { + icon = "", + color = "#56347c", + cterm_color = "54", + name = "Tails", + }, + ["trisquel"] = { + icon = "", + color = "#0f58b6", + cterm_color = "25", + name = "TrisquelGNULinux", + }, + ["ubuntu"] = { + icon = "", + color = "#dd4814", + cterm_color = "196", + name = "Ubuntu", + }, + ["vanillaos"] = { + icon = "", + color = "#fabd4d", + cterm_color = "214", + name = "VanillaOS", + }, + ["void"] = { + icon = "", + color = "#295340", + cterm_color = "23", + name = "Void", + }, + ["xerolinux"] = { + icon = "", + color = "#888fe2", + cterm_color = "104", + name = "XeroLinux", + }, + ["zorin"] = { + icon = "", + color = "#14a1e8", + cterm_color = "39", + name = "Zorin", + }, +} + +local icons_by_desktop_environment = { + ["budgie"] = { + icon = "", + color = "#4e5361", + cterm_color = "240", + name = "Budgie", + }, + ["cinnamon"] = { + icon = "", + color = "#dc682e", + cterm_color = "166", + name = "Cinnamon", + }, + ["gnome"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "GNOME", + }, + ["lxde"] = { + icon = "", + color = "#a4a4a4", + cterm_color = "248", + name = "LXDE", + }, + ["lxqt"] = { + icon = "", + color = "#0191d2", + cterm_color = "32", + name = "LXQt", + }, + ["mate"] = { + icon = "", + color = "#9bda5c", + cterm_color = "113", + name = "MATE", + }, + ["plasma"] = { + icon = "", + color = "#1b89f4", + cterm_color = "33", + name = "KDEPlasma", + }, + ["xfce"] = { + icon = "", + color = "#00aadf", + cterm_color = "74", + name = "Xfce", + }, +} + +local icons_by_window_manager = { + ["awesomewm"] = { + icon = "", + color = "#535d6c", + cterm_color = "59", + name = "awesome", + }, + ["bspwm"] = { + icon = "", + color = "#4f4f4f", + cterm_color = "239", + name = "BSPWM", + }, + ["dwm"] = { + icon = "", + color = "#1177aa", + cterm_color = "31", + name = "dwm", + }, + ["enlightenment"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "Enlightenment", + }, + ["fluxbox"] = { + icon = "", + color = "#555555", + cterm_color = "240", + name = "Fluxbox", + }, + ["hyprland"] = { + icon = "", + color = "#00aaae", + cterm_color = "37", + name = "Hyprland", + }, + ["i3"] = { + icon = "", + color = "#e8ebee", + cterm_color = "255", + name = "i3", + }, + ["jwm"] = { + icon = "", + color = "#0078cd", + cterm_color = "32", + name = "JWM", + }, + ["qtile"] = { + icon = "", + color = "#ffffff", + cterm_color = "231", + name = "Qtile", + }, + ["sway"] = { + icon = "", + color = "#68751c", + cterm_color = "64", + name = "Sway", + }, + ["xmonad"] = { + icon = "", + color = "#fd4d5d", + cterm_color = "203", + name = "xmonad", + }, +} + +return { + icons_by_filename = icons_by_filename, + icons_by_file_extension = icons_by_file_extension, + icons_by_operating_system = icons_by_operating_system, + icons_by_desktop_environment = icons_by_desktop_environment, + icons_by_window_manager = icons_by_window_manager, +} diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons/icons-light.lua b/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons/icons-light.lua new file mode 100644 index 00000000..a78e150f --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/lua/nvim-web-devicons/icons-light.lua @@ -0,0 +1,3760 @@ +local icons_by_filename = { + ["build.gradle"] = { + icon = "", + color = "#005f87", + cterm_color = "24", + name = "GradleBuildScript", + }, + ["settings.gradle"] = { + icon = "", + color = "#005f87", + cterm_color = "24", + name = "GradleSettings", + }, + [".babelrc"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Babelrc", + }, + [".bash_profile"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "BashProfile", + }, + [".bashrc"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "Bashrc", + }, + [".dockerignore"] = { + icon = "󰡨", + color = "#2e5f99", + cterm_color = "25", + name = "Dockerfile", + }, + [".ds_store"] = { + icon = "", + color = "#41535b", + cterm_color = "239", + name = "DsStore", + }, + [".editorconfig"] = { + icon = "", + color = "#333030", + cterm_color = "236", + name = "EditorConfig", + }, + [".env"] = { + icon = "", + color = "#32310d", + cterm_color = "236", + name = "Env", + }, + [".eslintrc"] = { + icon = "", + color = "#4b32c3", + cterm_color = "56", + name = "Eslintrc", + }, + [".eslintignore"] = { + icon = "", + color = "#4b32c3", + cterm_color = "56", + name = "EslintIgnore", + }, + [".gitattributes"] = { + icon = "", + color = "#b83a1d", + cterm_color = "160", + name = "GitAttributes", + }, + [".gitconfig"] = { + icon = "", + color = "#b83a1d", + cterm_color = "160", + name = "GitConfig", + }, + [".gitignore"] = { + icon = "", + color = "#b83a1d", + cterm_color = "160", + name = "GitIgnore", + }, + [".gitlab-ci.yml"] = { + icon = "", + color = "#aa321f", + cterm_color = "124", + name = "GitlabCI", + }, + [".gitmodules"] = { + icon = "", + color = "#b83a1d", + cterm_color = "160", + name = "GitModules", + }, + [".gtkrc-2.0"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "GTK", + }, + [".gvimrc"] = { + icon = "", + color = "#017226", + cterm_color = "22", + name = "Gvimrc", + }, + [".luaurc"] = { + icon = "", + color = "#007abf", + cterm_color = "32", + name = "Luaurc", + }, + [".mailmap"] = { + icon = "󰊢", + color = "#41535b", + cterm_color = "239", + name = "Mailmap", + }, + [".npmignore"] = { + icon = "", + color = "#ae1d38", + cterm_color = "161", + name = "NPMIgnore", + }, + [".npmrc"] = { + icon = "", + color = "#ae1d38", + cterm_color = "161", + name = "NPMrc", + }, + [".prettierrc"] = { + icon = "", + color = "#3264b7", + cterm_color = "25", + name = "PrettierConfig", + }, + [".settings.json"] = { + icon = "", + color = "#643995", + cterm_color = "91", + name = "SettingsJson", + }, + [".SRCINFO"] = { + icon = "󰣇", + color = "#0b6f9e", + cterm_color = "24", + name = "SRCINFO", + }, + [".vimrc"] = { + icon = "", + color = "#017226", + cterm_color = "22", + name = "Vimrc", + }, + [".Xauthority"] = { + icon = "", + color = "#ac3a12", + cterm_color = "124", + name = "Xauthority", + }, + [".xinitrc"] = { + icon = "", + color = "#ac3a12", + cterm_color = "124", + name = "XInitrc", + }, + [".Xresources"] = { + icon = "", + color = "#ac3a12", + cterm_color = "124", + name = "Xresources", + }, + [".xsession"] = { + icon = "", + color = "#ac3a12", + cterm_color = "124", + name = "Xsession", + }, + [".zprofile"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "Zshprofile", + }, + [".zshenv"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "Zshenv", + }, + [".zshrc"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "Zshrc", + }, + ["_gvimrc"] = { + icon = "", + color = "#017226", + cterm_color = "22", + name = "Gvimrc", + }, + ["_vimrc"] = { + icon = "", + color = "#017226", + cterm_color = "22", + name = "Vimrc", + }, + ["R"] = { + icon = "󰟔", + color = "#1a4c8c", + cterm_color = "25", + name = "R", + }, + ["avif"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Avif", + }, + ["brewfile"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Brewfile", + }, + ["bspwmrc"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "BSPWM", + }, + ["build"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "BazelBuild", + }, + ["checkhealth"] = { + icon = "󰓙", + color = "#3a5a7e", + cterm_color = "24", + name = "Checkhealth", + }, + ["cmakelists.txt"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "CMakeLists", + }, + ["commit_editmsg"] = { + icon = "", + color = "#b83a1d", + cterm_color = "160", + name = "GitCommit", + }, + ["compose.yaml"] = { + icon = "󰡨", + color = "#2e5f99", + cterm_color = "25", + name = "Dockerfile", + }, + ["compose.yml"] = { + icon = "󰡨", + color = "#2e5f99", + cterm_color = "25", + name = "Dockerfile", + }, + ["config"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Config", + }, + ["containerfile"] = { + icon = "󰡨", + color = "#2e5f99", + cterm_color = "25", + name = "Dockerfile", + }, + ["copying"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "License", + }, + ["copying.lesser"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "License", + }, + ["docker-compose.yaml"] = { + icon = "󰡨", + color = "#2e5f99", + cterm_color = "25", + name = "Dockerfile", + }, + ["docker-compose.yml"] = { + icon = "󰡨", + color = "#2e5f99", + cterm_color = "25", + name = "Dockerfile", + }, + ["dockerfile"] = { + icon = "󰡨", + color = "#2e5f99", + cterm_color = "25", + name = "Dockerfile", + }, + ["ext_typoscript_setup.txt"] = { + icon = "", + color = "#aa5a00", + cterm_color = "130", + name = "TypoScriptSetup", + }, + ["favicon.ico"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Favicon", + }, + ["fp-info-cache"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCadCache", + }, + ["fp-lib-table"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCadFootprintTable", + }, + ["FreeCAD.conf"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCADConfig", + }, + ["gemfile$"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Gemfile", + }, + ["gnumakefile"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Makefile", + }, + ["gradlew"] = { + icon = "", + color = "#005f87", + cterm_color = "24", + name = "GradleWrapperScript", + }, + ["gradle.properties"] = { + icon = "", + color = "#005f87", + cterm_color = "24", + name = "GradleProperties", + }, + ["gradle-wrapper.properties"] = { + icon = "", + color = "#005f87", + cterm_color = "24", + name = "GradleWrapperProperties", + }, + ["groovy"] = { + icon = "", + color = "#384e5d", + cterm_color = "239", + name = "Groovy", + }, + ["gruntfile.babel.js"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "Gruntfile", + }, + ["gruntfile.coffee"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "Gruntfile", + }, + ["gruntfile.js"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "Gruntfile", + }, + ["gruntfile.ts"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "Gruntfile", + }, + ["gtkrc"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "GTK", + }, + ["gulpfile.babel.js"] = { + icon = "", + color = "#992e33", + cterm_color = "88", + name = "Gulpfile", + }, + ["gulpfile.coffee"] = { + icon = "", + color = "#992e33", + cterm_color = "88", + name = "Gulpfile", + }, + ["gulpfile.js"] = { + icon = "", + color = "#992e33", + cterm_color = "88", + name = "Gulpfile", + }, + ["gulpfile.ts"] = { + icon = "", + color = "#992e33", + cterm_color = "88", + name = "Gulpfile", + }, + ["hyprland.conf"] = { + icon = "", + color = "#008082", + cterm_color = "30", + name = "Hyprland", + }, + ["i3blocks.conf"] = { + icon = "", + color = "#2e2f30", + cterm_color = "236", + name = "i3", + }, + ["i3status.conf"] = { + icon = "", + color = "#2e2f30", + cterm_color = "236", + name = "i3", + }, + ["cantorrc"] = { + icon = "", + color = "#1573b6", + cterm_color = "32", + name = "Cantorrc", + }, + ["kalgebrarc"] = { + icon = "", + color = "#1573b6", + cterm_color = "32", + name = "Kalgebrarc", + }, + ["kdeglobals"] = { + icon = "", + color = "#1573b6", + cterm_color = "32", + name = "KDEglobals", + }, + ["kdenlive-layoutsrc"] = { + icon = "", + color = "#425c79", + cterm_color = "24", + name = "KdenliveLayoutsrc", + }, + ["kdenliverc"] = { + icon = "", + color = "#425c79", + cterm_color = "24", + name = "Kdenliverc", + }, + ["kritadisplayrc"] = { + icon = "", + color = "#a12ea7", + cterm_color = "127", + name = "Kritadisplayrc", + }, + ["kritarc"] = { + icon = "", + color = "#a12ea7", + cterm_color = "127", + name = "Kritarc", + }, + ["license"] = { + icon = "", + color = "#686020", + cterm_color = "58", + name = "License", + }, + ["lxde-rc.xml"] = { + icon = "", + color = "#606060", + cterm_color = "59", + name = "LXDEConfigFile", + }, + ["lxqt.conf"] = { + icon = "", + color = "#016e9e", + cterm_color = "24", + name = "LXQtConfigFile", + }, + ["makefile"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Makefile", + }, + ["mix.lock"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "MixLock", + }, + ["mpv.conf"] = { + icon = "", + color = "#3b1342", + cterm_color = "53", + name = "Mpv", + }, + ["node_modules"] = { + icon = "", + color = "#ae1d38", + cterm_color = "161", + name = "NodeModules", + }, + ["package.json"] = { + icon = "", + color = "#ae1d38", + cterm_color = "161", + name = "PackageJson", + }, + ["package-lock.json"] = { + icon = "", + color = "#7a0d21", + cterm_color = "52", + name = "PackageLockJson", + }, + ["PKGBUILD"] = { + icon = "", + color = "#0b6f9e", + cterm_color = "24", + name = "PKGBUILD", + }, + ["platformio.ini"] = { + icon = "", + color = "#a4571d", + cterm_color = "130", + name = "Platformio", + }, + ["pom.xml"] = { + icon = "", + color = "#7a0d21", + cterm_color = "52", + name = "Maven", + }, + ["procfile"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Procfile", + }, + ["PrusaSlicer.ini"] = { + icon = "", + color = "#9d4717", + cterm_color = "130", + name = "PrusaSlicer", + }, + ["PrusaSlicerGcodeViewer.ini"] = { + icon = "", + color = "#9d4717", + cterm_color = "130", + name = "PrusaSlicer", + }, + ["py.typed"] = { + icon = "", + color = "#805e02", + cterm_color = "94", + name = "Py.typed", + }, + ["QtProject.conf"] = { + icon = "", + color = "#2b8937", + cterm_color = "28", + name = "Qt", + }, + ["r"] = { + icon = "󰟔", + color = "#1a4c8c", + cterm_color = "25", + name = "R", + }, + ["rakefile"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Rakefile", + }, + ["rmd"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Rmd", + }, + ["svelte.config.js"] = { + icon = "", + color = "#bf2e00", + cterm_color = "160", + name = "SvelteConfig", + }, + ["sxhkdrc"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "BSPWM", + }, + ["sym-lib-table"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCadSymbolTable", + }, + ["tailwind.config.js"] = { + icon = "󱏿", + color = "#158197", + cterm_color = "31", + name = "TailwindConfig", + }, + ["tailwind.config.mjs"] = { + icon = "󱏿", + color = "#158197", + cterm_color = "31", + name = "TailwindConfig", + }, + ["tailwind.config.ts"] = { + icon = "󱏿", + color = "#158197", + cterm_color = "31", + name = "TailwindConfig", + }, + ["tmux.conf"] = { + icon = "", + color = "#0f8c13", + cterm_color = "28", + name = "Tmux", + }, + ["tmux.conf.local"] = { + icon = "", + color = "#0f8c13", + cterm_color = "28", + name = "Tmux", + }, + ["tsconfig.json"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "TSConfig", + }, + ["unlicense"] = { + icon = "", + color = "#686020", + cterm_color = "58", + name = "License", + }, + ["vagrantfile$"] = { + icon = "", + color = "#104abf", + cterm_color = "26", + name = "Vagrantfile", + }, + ["vlcrc"] = { + icon = "󰕼", + color = "#9f5100", + cterm_color = "130", + name = "VLC", + }, + ["webpack"] = { + icon = "󰜫", + color = "#36677c", + cterm_color = "24", + name = "Webpack", + }, + ["weston.ini"] = { + icon = "", + color = "#805e00", + cterm_color = "94", + name = "Weston", + }, + ["workspace"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "BazelWorkspace", + }, + ["xmobarrc"] = { + icon = "", + color = "#a9333e", + cterm_color = "131", + name = "xmonad", + }, + ["xmobarrc.hs"] = { + icon = "", + color = "#a9333e", + cterm_color = "131", + name = "xmonad", + }, + ["xmonad.hs"] = { + icon = "", + color = "#a9333e", + cterm_color = "131", + name = "xmonad", + }, + ["xorg.conf"] = { + icon = "", + color = "#ac3a12", + cterm_color = "124", + name = "XorgConf", + }, + ["xsettingsd.conf"] = { + icon = "", + color = "#ac3a12", + cterm_color = "124", + name = "XSettingsdConf", + }, + ["build.zig.zon"] = { + icon = "", + color = "#7b4d0e", + cterm_color = "94", + name = "ZigObjectNotation", + }, +} + +local icons_by_file_extension = { + ["3gp"] = { + icon = "", + color = "#7e4c10", + cterm_color = "94", + name = "3gp", + }, + ["3mf"] = { + icon = "󰆧", + color = "#5b5b5b", + cterm_color = "240", + name = "3DObjectFile", + }, + ["7z"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "7z", + }, + ["a"] = { + icon = "", + color = "#494a47", + cterm_color = "239", + name = "StaticLibraryArchive", + }, + ["aac"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "AdvancedAudioCoding", + }, + ["aif"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "AudioInterchangeFileFormat", + }, + ["aiff"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "AudioInterchangeFileFormat", + }, + ["ape"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "MonkeysAudio", + }, + ["ai"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Ai", + }, + ["android"] = { + icon = "", + color = "#277e3e", + cterm_color = "29", + name = "Android", + }, + ["apk"] = { + icon = "", + color = "#277e3e", + cterm_color = "29", + name = "apk", + }, + ["app"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "App", + }, + ["applescript"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "AppleScript", + }, + ["asc"] = { + icon = "󰦝", + color = "#41525f", + cterm_color = "239", + name = "Asc", + }, + ["ass"] = { + icon = "󰨖", + color = "#805c0a", + cterm_color = "94", + name = "Ass", + }, + ["astro"] = { + icon = "", + color = "#aa2f4d", + cterm_color = "125", + name = "Astro", + }, + ["awk"] = { + icon = "", + color = "#3a4446", + cterm_color = "238", + name = "Awk", + }, + ["azcli"] = { + icon = "", + color = "#005a9f", + cterm_color = "25", + name = "AzureCli", + }, + ["bak"] = { + icon = "󰁯", + color = "#526064", + cterm_color = "59", + name = "Backup", + }, + ["bash"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "Bash", + }, + ["bat"] = { + icon = "", + color = "#40500f", + cterm_color = "58", + name = "Bat", + }, + ["bazel"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "Bazel", + }, + ["bib"] = { + icon = "󱉟", + color = "#666620", + cterm_color = "58", + name = "BibTeX", + }, + ["bicep"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Bicep", + }, + ["bicepparam"] = { + icon = "", + color = "#6a4d77", + cterm_color = "96", + name = "BicepParameters", + }, + ["bin"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "Bin", + }, + ["blade.php"] = { + icon = "", + color = "#a0372b", + cterm_color = "124", + name = "Blade", + }, + ["blend"] = { + icon = "󰂫", + color = "#9c4f00", + cterm_color = "130", + name = "Blender", + }, + ["bmp"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Bmp", + }, + ["blp"] = { + icon = "󰺾", + color = "#3a6497", + cterm_color = "25", + name = "Blueprint", + }, + ["brep"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "BoundaryRepresentation", + }, + ["bz"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "Bz", + }, + ["bz2"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "Bz2", + }, + ["bz3"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "Bz3", + }, + ["bzl"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "Bzl", + }, + ["c"] = { + icon = "", + color = "#3b69aa", + cterm_color = "25", + name = "C", + }, + ["c++"] = { + icon = "", + color = "#a23253", + cterm_color = "125", + name = "CPlusPlus", + }, + ["cache"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "Cache", + }, + ["cast"] = { + icon = "", + color = "#7e4c10", + cterm_color = "94", + name = "Asciinema", + }, + ["cbl"] = { + icon = "⚙", + color = "#005ca5", + cterm_color = "25", + name = "Cobol", + }, + ["cc"] = { + icon = "", + color = "#a23253", + cterm_color = "125", + name = "CPlusPlus", + }, + ["ccm"] = { + icon = "", + color = "#a23253", + cterm_color = "125", + name = "CPlusPlusModule", + }, + ["cfg"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Configuration", + }, + ["cjs"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Cjs", + }, + ["clj"] = { + icon = "", + color = "#466024", + cterm_color = "22", + name = "Clojure", + }, + ["cljc"] = { + icon = "", + color = "#466024", + cterm_color = "22", + name = "ClojureC", + }, + ["cljs"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "ClojureJS", + }, + ["cljd"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "ClojureDart", + }, + ["cmake"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "CMake", + }, + ["cob"] = { + icon = "⚙", + color = "#005ca5", + cterm_color = "25", + name = "Cobol", + }, + ["cobol"] = { + icon = "⚙", + color = "#005ca5", + cterm_color = "25", + name = "Cobol", + }, + ["coffee"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Coffee", + }, + ["conf"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Conf", + }, + ["config.ru"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "ConfigRu", + }, + ["cp"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Cp", + }, + ["cpp"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Cpp", + }, + ["cppm"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Cppm", + }, + ["cpy"] = { + icon = "⚙", + color = "#005ca5", + cterm_color = "25", + name = "Cobol", + }, + ["cr"] = { + icon = "", + color = "#434343", + cterm_color = "238", + name = "Crystal", + }, + ["crdownload"] = { + icon = "", + color = "#226654", + cterm_color = "23", + name = "Crdownload", + }, + ["cs"] = { + icon = "󰌛", + color = "#434d04", + cterm_color = "58", + name = "Cs", + }, + ["csh"] = { + icon = "", + color = "#3a4446", + cterm_color = "238", + name = "Csh", + }, + ["cshtml"] = { + icon = "󱦗", + color = "#512bd4", + cterm_color = "56", + name = "RazorPage", + }, + ["cson"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Cson", + }, + ["csproj"] = { + icon = "󰪮", + color = "#512bd4", + cterm_color = "56", + name = "CSharpProject", + }, + ["css"] = { + icon = "", + color = "#2c6ea3", + cterm_color = "24", + name = "Css", + }, + ["csv"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "Csv", + }, + ["cts"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Cts", + }, + ["cu"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "cuda", + }, + ["cue"] = { + icon = "󰲹", + color = "#764a57", + cterm_color = "95", + name = "Cue", + }, + ["cuh"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "cudah", + }, + ["cxx"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Cxx", + }, + ["cxxm"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Cxxm", + }, + ["d"] = { + icon = "", + color = "#325a13", + cterm_color = "22", + name = "D", + }, + ["d.ts"] = { + icon = "", + color = "#6a4c2a", + cterm_color = "94", + name = "TypeScriptDeclaration", + }, + ["dart"] = { + icon = "", + color = "#03589C", + cterm_color = "25", + name = "Dart", + }, + ["db"] = { + icon = "", + color = "#494848", + cterm_color = "238", + name = "Db", + }, + ["dconf"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "Dconf", + }, + ["desktop"] = { + icon = "", + color = "#563d7c", + cterm_color = "54", + name = "DesktopEntry", + }, + ["diff"] = { + icon = "", + color = "#41535b", + cterm_color = "239", + name = "Diff", + }, + ["dll"] = { + icon = "", + color = "#4d2c0b", + cterm_color = "52", + name = "Dll", + }, + ["doc"] = { + icon = "󰈬", + color = "#185abd", + cterm_color = "26", + name = "Doc", + }, + ["Dockerfile"] = { + icon = "󰡨", + color = "#2e5f99", + cterm_color = "25", + name = "Dockerfile", + }, + ["docx"] = { + icon = "󰈬", + color = "#185abd", + cterm_color = "26", + name = "Docx", + }, + ["dot"] = { + icon = "󱁉", + color = "#244a6a", + cterm_color = "24", + name = "Dot", + }, + ["download"] = { + icon = "", + color = "#226654", + cterm_color = "23", + name = "Download", + }, + ["drl"] = { + icon = "", + color = "#553a3a", + cterm_color = "238", + name = "Drools", + }, + ["dropbox"] = { + icon = "", + color = "#0049be", + cterm_color = "26", + name = "Dropbox", + }, + ["dump"] = { + icon = "", + color = "#494848", + cterm_color = "238", + name = "Dump", + }, + ["dwg"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "AutoCADDwg", + }, + ["dxf"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "AutoCADDxf", + }, + ["ebook"] = { + icon = "", + color = "#755836", + cterm_color = "94", + name = "Ebook", + }, + ["edn"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Edn", + }, + ["eex"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Eex", + }, + ["ejs"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Ejs", + }, + ["elf"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "Elf", + }, + ["el"] = { + icon = "", + color = "#61568e", + cterm_color = "60", + name = "Elisp", + }, + ["elc"] = { + icon = "", + color = "#61568e", + cterm_color = "60", + name = "Elisp", + }, + ["elm"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Elm", + }, + ["eln"] = { + icon = "", + color = "#61568e", + cterm_color = "60", + name = "Elisp", + }, + ["env"] = { + icon = "", + color = "#32310d", + cterm_color = "236", + name = "Env", + }, + ["eot"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "EmbeddedOpenTypeFont", + }, + ["epp"] = { + icon = "", + color = "#80530d", + cterm_color = "94", + name = "Epp", + }, + ["epub"] = { + icon = "", + color = "#755836", + cterm_color = "94", + name = "Epub", + }, + ["erb"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Erb", + }, + ["erl"] = { + icon = "", + color = "#8a2b72", + cterm_color = "89", + name = "Erl", + }, + ["ex"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Ex", + }, + ["exe"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "Exe", + }, + ["exs"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Exs", + }, + ["f#"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Fsharp", + }, + ["f3d"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "Fusion360", + }, + ["f90"] = { + icon = "󱈚", + color = "#563b70", + cterm_color = "53", + name = "Fortran", + }, + ["fbx"] = { + icon = "󰆧", + color = "#5b5b5b", + cterm_color = "240", + name = "3DObjectFile", + }, + ["fcbak"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcmacro"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcmat"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcparam"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcscript"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcstd"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fcstd1"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fctb"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fctl"] = { + icon = "", + color = "#cb0d0d", + cterm_color = "160", + name = "FreeCAD", + }, + ["fdmdownload"] = { + icon = "", + color = "#226654", + cterm_color = "23", + name = "Fdmdownload", + }, + ["flac"] = { + icon = "", + color = "#005880", + cterm_color = "24", + name = "FreeLosslessAudioCodec", + }, + ["flc"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "FIGletFontControl", + }, + ["flf"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "FIGletFontFormat", + }, + ["fnl"] = { + icon = "", + color = "#33312b", + cterm_color = "236", + name = "Fennel", + }, + ["fish"] = { + icon = "", + color = "#3a4446", + cterm_color = "238", + name = "Fish", + }, + ["fs"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Fs", + }, + ["fsi"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Fsi", + }, + ["fsscript"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Fsscript", + }, + ["fsx"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Fsx", + }, + ["gcode"] = { + icon = "󰐫", + color = "#0f5582", + cterm_color = "24", + name = "GCode", + }, + ["gd"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "GDScript", + }, + ["gemspec"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Gemspec", + }, + ["gif"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Gif", + }, + ["git"] = { + icon = "", + color = "#b5391e", + cterm_color = "160", + name = "GitLogo", + }, + ["glb"] = { + icon = "", + color = "#80581e", + cterm_color = "94", + name = "BinaryGLTF", + }, + ["gnumakefile"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Makefile", + }, + ["go"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Go", + }, + ["godot"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "GodotProject", + }, + ["gql"] = { + icon = "", + color = "#ac2880", + cterm_color = "126", + name = "GraphQL", + }, + ["graphql"] = { + icon = "", + color = "#ac2880", + cterm_color = "126", + name = "GraphQL", + }, + ["gresource"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "GTK", + }, + ["gv"] = { + icon = "󱁉", + color = "#244a6a", + cterm_color = "24", + name = "Gv", + }, + ["gz"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "Gz", + }, + ["h"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "H", + }, + ["haml"] = { + icon = "", + color = "#2f2f2d", + cterm_color = "236", + name = "Haml", + }, + ["hx"] = { + icon = "", + color = "#9c5715", + cterm_color = "130", + name = "Haxe", + }, + ["hbs"] = { + icon = "", + color = "#a04f1d", + cterm_color = "130", + name = "Hbs", + }, + ["hex"] = { + icon = "", + color = "#224abf", + cterm_color = "26", + name = "Hexadecimal", + }, + ["heex"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Heex", + }, + ["hh"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Hh", + }, + ["hpp"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Hpp", + }, + ["hrl"] = { + icon = "", + color = "#8a2b72", + cterm_color = "89", + name = "Hrl", + }, + ["hs"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Hs", + }, + ["htm"] = { + icon = "", + color = "#aa391c", + cterm_color = "124", + name = "Htm", + }, + ["html"] = { + icon = "", + color = "#ab3a1c", + cterm_color = "124", + name = "Html", + }, + ["huff"] = { + icon = "󰡘", + color = "#4242c7", + cterm_color = "56", + name = "Huff", + }, + ["hurl"] = { + icon = "", + color = "#bf0266", + cterm_color = "125", + name = "Hurl", + }, + ["hxx"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Hxx", + }, + ["ixx"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Ixx", + }, + ["ico"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Ico", + }, + ["ical"] = { + icon = "", + color = "#2B2e83", + cterm_color = "18", + name = "Ical", + }, + ["icalendar"] = { + icon = "", + color = "#2B2e83", + cterm_color = "18", + name = "Icalendar", + }, + ["ics"] = { + icon = "", + color = "#2B2e83", + cterm_color = "18", + name = "Ics", + }, + ["ifb"] = { + icon = "", + color = "#2B2e83", + cterm_color = "18", + name = "Ifb", + }, + ["ifc"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "Ifc", + }, + ["ige"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "Ige", + }, + ["iges"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "Iges", + }, + ["igs"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "Igs", + }, + ["image"] = { + icon = "", + color = "#453f43", + cterm_color = "238", + name = "Image", + }, + ["img"] = { + icon = "", + color = "#453f43", + cterm_color = "238", + name = "Img", + }, + ["import"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "ImportConfiguration", + }, + ["info"] = { + icon = "", + color = "#333329", + cterm_color = "236", + name = "Info", + }, + ["ini"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Ini", + }, + ["ino"] = { + icon = "", + color = "#397981", + cterm_color = "30", + name = "Arduino", + }, + ["iso"] = { + icon = "", + color = "#453f43", + cterm_color = "238", + name = "Iso", + }, + ["ipynb"] = { + icon = "", + color = "#366b8a", + cterm_color = "24", + name = "Notebook", + }, + ["java"] = { + icon = "", + color = "#992e33", + cterm_color = "88", + name = "Java", + }, + ["jl"] = { + icon = "", + color = "#6c4b7c", + cterm_color = "96", + name = "Jl", + }, + ["jwmrc"] = { + icon = "", + color = "#005a9a", + cterm_color = "25", + name = "JWM", + }, + ["jpeg"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Jpeg", + }, + ["jpg"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Jpg", + }, + ["js"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Js", + }, + ["json"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Json", + }, + ["json5"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Json5", + }, + ["jsonc"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "Jsonc", + }, + ["jsx"] = { + icon = "", + color = "#158197", + cterm_color = "31", + name = "Jsx", + }, + ["jxl"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "JpegXl", + }, + ["kbx"] = { + icon = "󰯄", + color = "#565856", + cterm_color = "240", + name = "Kbx", + }, + ["kdb"] = { + icon = "", + color = "#3e7427", + cterm_color = "28", + name = "Kdb", + }, + ["kdbx"] = { + icon = "", + color = "#3e7427", + cterm_color = "28", + name = "Kdbx", + }, + ["kdenlive"] = { + icon = "", + color = "#425c79", + cterm_color = "24", + name = "Kdenlive", + }, + ["kdenlivetitle"] = { + icon = "", + color = "#425c79", + cterm_color = "24", + name = "Kdenlive", + }, + ["kicad_dru"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCad", + }, + ["kicad_mod"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCad", + }, + ["kicad_pcb"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCad", + }, + ["kicad_prl"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCad", + }, + ["kicad_pro"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCad", + }, + ["kicad_sch"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCad", + }, + ["kicad_sym"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCad", + }, + ["kicad_wks"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "KiCad", + }, + ["ko"] = { + icon = "", + color = "#494a47", + cterm_color = "239", + name = "LinuxKernelObject", + }, + ["kpp"] = { + icon = "", + color = "#a12ea7", + cterm_color = "127", + name = "Krita", + }, + ["kra"] = { + icon = "", + color = "#a12ea7", + cterm_color = "127", + name = "Krita", + }, + ["krz"] = { + icon = "", + color = "#a12ea7", + cterm_color = "127", + name = "Krita", + }, + ["ksh"] = { + icon = "", + color = "#3a4446", + cterm_color = "238", + name = "Ksh", + }, + ["kt"] = { + icon = "", + color = "#5f3ebf", + cterm_color = "92", + name = "Kotlin", + }, + ["kts"] = { + icon = "", + color = "#5f3ebf", + cterm_color = "92", + name = "KotlinScript", + }, + ["lck"] = { + icon = "", + color = "#5e5e5e", + cterm_color = "59", + name = "Lock", + }, + ["leex"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Leex", + }, + ["less"] = { + icon = "", + color = "#563d7c", + cterm_color = "54", + name = "Less", + }, + ["lff"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "LibrecadFontFile", + }, + ["lhs"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Lhs", + }, + ["lib"] = { + icon = "", + color = "#4d2c0b", + cterm_color = "52", + name = "Lib", + }, + ["license"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "License", + }, + ["liquid"] = { + icon = "", + color = "#4a6024", + cterm_color = "58", + name = "Liquid", + }, + ["lock"] = { + icon = "", + color = "#5e5e5e", + cterm_color = "59", + name = "Lock", + }, + ["log"] = { + icon = "󰌱", + color = "#4a4a4a", + cterm_color = "239", + name = "Log", + }, + ["lrc"] = { + icon = "󰨖", + color = "#805c0a", + cterm_color = "94", + name = "Lrc", + }, + ["lua"] = { + icon = "", + color = "#366b8a", + cterm_color = "24", + name = "Lua", + }, + ["luac"] = { + icon = "", + color = "#366b8a", + cterm_color = "24", + name = "Lua", + }, + ["luau"] = { + icon = "", + color = "#007abf", + cterm_color = "32", + name = "Luau", + }, + ["m3u"] = { + icon = "󰲹", + color = "#764a57", + cterm_color = "95", + name = "M3u", + }, + ["m3u8"] = { + icon = "󰲹", + color = "#764a57", + cterm_color = "95", + name = "M3u8", + }, + ["m4a"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "MPEG4", + }, + ["m4v"] = { + icon = "", + color = "#7e4c10", + cterm_color = "94", + name = "M4V", + }, + ["magnet"] = { + icon = "", + color = "#a51b16", + cterm_color = "124", + name = "Magnet", + }, + ["makefile"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Makefile", + }, + ["markdown"] = { + icon = "", + color = "#4a4a4a", + cterm_color = "239", + name = "Markdown", + }, + ["material"] = { + icon = "󰔉", + color = "#8a2b72", + cterm_color = "89", + name = "Material", + }, + ["md"] = { + icon = "", + color = "#4a4a4a", + cterm_color = "239", + name = "Md", + }, + ["md5"] = { + icon = "󰕥", + color = "#5d5975", + cterm_color = "60", + name = "Md5", + }, + ["mdx"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Mdx", + }, + ["mint"] = { + icon = "󰌪", + color = "#44604a", + cterm_color = "23", + name = "Mint", + }, + ["mjs"] = { + icon = "", + color = "#504b1e", + cterm_color = "58", + name = "Mjs", + }, + ["mk"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Makefile", + }, + ["mkv"] = { + icon = "", + color = "#7e4c10", + cterm_color = "94", + name = "Mkv", + }, + ["ml"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "Ml", + }, + ["mli"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "Mli", + }, + ["m"] = { + icon = "", + color = "#3b69aa", + cterm_color = "25", + name = "ObjectiveC", + }, + ["mm"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "ObjectiveCPlusPlus", + }, + ["mo"] = { + icon = "∞", + color = "#654ca7", + cterm_color = "61", + name = "Motoko", + }, + ["mobi"] = { + icon = "", + color = "#755836", + cterm_color = "94", + name = "Mobi", + }, + ["mov"] = { + icon = "", + color = "#7e4c10", + cterm_color = "94", + name = "MOV", + }, + ["mp3"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "MPEGAudioLayerIII", + }, + ["mp4"] = { + icon = "", + color = "#7e4c10", + cterm_color = "94", + name = "Mp4", + }, + ["mpp"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Mpp", + }, + ["msf"] = { + icon = "", + color = "#0e5ca9", + cterm_color = "25", + name = "Thunderbird", + }, + ["mts"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Mts", + }, + ["mustache"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "Mustache", + }, + ["nfo"] = { + icon = "", + color = "#333329", + cterm_color = "236", + name = "Nfo", + }, + ["nim"] = { + icon = "", + color = "#514700", + cterm_color = "58", + name = "Nim", + }, + ["nix"] = { + icon = "", + color = "#3f5d72", + cterm_color = "24", + name = "Nix", + }, + ["nswag"] = { + icon = "", + color = "#427516", + cterm_color = "28", + name = "Nswag", + }, + ["nu"] = { + icon = ">", + color = "#276f4e", + cterm_color = "29", + name = "Nushell", + }, + ["o"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "ObjectFile", + }, + ["obj"] = { + icon = "󰆧", + color = "#5b5b5b", + cterm_color = "240", + name = "3DObjectFile", + }, + ["ogg"] = { + icon = "", + color = "#005880", + cterm_color = "24", + name = "OggVorbis", + }, + ["opus"] = { + icon = "", + color = "#005880", + cterm_color = "24", + name = "OpusAudioFile", + }, + ["org"] = { + icon = "", + color = "#4f7166", + cterm_color = "66", + name = "OrgMode", + }, + ["otf"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "OpenTypeFont", + }, + ["out"] = { + icon = "", + color = "#9F0500", + cterm_color = "124", + name = "Out", + }, + ["part"] = { + icon = "", + color = "#226654", + cterm_color = "23", + name = "Part", + }, + ["patch"] = { + icon = "", + color = "#41535b", + cterm_color = "239", + name = "Patch", + }, + ["pck"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "PackedResource", + }, + ["pcm"] = { + icon = "", + color = "#005880", + cterm_color = "24", + name = "PulseCodeModulation", + }, + ["pdf"] = { + icon = "", + color = "#b30b00", + cterm_color = "124", + name = "Pdf", + }, + ["php"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Php", + }, + ["pl"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Pl", + }, + ["pls"] = { + icon = "󰲹", + color = "#764a57", + cterm_color = "95", + name = "Pls", + }, + ["ply"] = { + icon = "󰆧", + color = "#5b5b5b", + cterm_color = "240", + name = "3DObjectFile", + }, + ["pm"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Pm", + }, + ["png"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Png", + }, + ["po"] = { + icon = "", + color = "#1c708e", + cterm_color = "24", + name = "Localization", + }, + ["pot"] = { + icon = "", + color = "#1c708e", + cterm_color = "24", + name = "Localization", + }, + ["pp"] = { + icon = "", + color = "#80530d", + cterm_color = "94", + name = "Pp", + }, + ["ppt"] = { + icon = "󰈧", + color = "#983826", + cterm_color = "124", + name = "Ppt", + }, + ["prisma"] = { + icon = "", + color = "#444da2", + cterm_color = "61", + name = "Prisma", + }, + ["pro"] = { + icon = "", + color = "#725c2a", + cterm_color = "94", + name = "Prolog", + }, + ["ps1"] = { + icon = "󰨊", + color = "#325698", + cterm_color = "25", + name = "PsScriptfile", + }, + ["psd1"] = { + icon = "󰨊", + color = "#4f5893", + cterm_color = "60", + name = "PsManifestfile", + }, + ["psm1"] = { + icon = "󰨊", + color = "#4f5893", + cterm_color = "60", + name = "PsScriptModulefile", + }, + ["psb"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Psb", + }, + ["psd"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Psd", + }, + ["pub"] = { + icon = "󰷖", + color = "#4c422f", + cterm_color = "238", + name = "Pub", + }, + ["pxd"] = { + icon = "", + color = "#3c6f98", + cterm_color = "24", + name = "Pxd", + }, + ["pxi"] = { + icon = "", + color = "#3c6f98", + cterm_color = "24", + name = "Pxi", + }, + ["py"] = { + icon = "", + color = "#805e02", + cterm_color = "94", + name = "Py", + }, + ["pyc"] = { + icon = "", + color = "#332d1d", + cterm_color = "236", + name = "Pyc", + }, + ["pyd"] = { + icon = "", + color = "#332d1d", + cterm_color = "236", + name = "Pyd", + }, + ["pyi"] = { + icon = "", + color = "#805e02", + cterm_color = "94", + name = "Pyi", + }, + ["pyo"] = { + icon = "", + color = "#332d1d", + cterm_color = "236", + name = "Pyo", + }, + ["pyx"] = { + icon = "", + color = "#3c6f98", + cterm_color = "24", + name = "Pyx", + }, + ["qm"] = { + icon = "", + color = "#1c708e", + cterm_color = "24", + name = "Localization", + }, + ["qml"] = { + icon = "", + color = "#2b8937", + cterm_color = "28", + name = "Qt", + }, + ["qrc"] = { + icon = "", + color = "#2b8937", + cterm_color = "28", + name = "Qt", + }, + ["qss"] = { + icon = "", + color = "#2b8937", + cterm_color = "28", + name = "Qt", + }, + ["query"] = { + icon = "", + color = "#607035", + cterm_color = "58", + name = "Query", + }, + ["r"] = { + icon = "󰟔", + color = "#1a4c8c", + cterm_color = "25", + name = "R", + }, + ["rake"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Rake", + }, + ["rar"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "Rar", + }, + ["razor"] = { + icon = "󱦘", + color = "#512bd4", + cterm_color = "56", + name = "RazorPage", + }, + ["rb"] = { + icon = "", + color = "#701516", + cterm_color = "52", + name = "Rb", + }, + ["res"] = { + icon = "", + color = "#992e33", + cterm_color = "88", + name = "ReScript", + }, + ["resi"] = { + icon = "", + color = "#a33759", + cterm_color = "125", + name = "ReScriptInterface", + }, + ["rlib"] = { + icon = "", + color = "#6f5242", + cterm_color = "95", + name = "Rlib", + }, + ["rmd"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Rmd", + }, + ["rproj"] = { + icon = "󰗆", + color = "#286844", + cterm_color = "29", + name = "Rproj", + }, + ["rs"] = { + icon = "", + color = "#6f5242", + cterm_color = "95", + name = "Rs", + }, + ["rss"] = { + icon = "", + color = "#7e4e1e", + cterm_color = "94", + name = "Rss", + }, + ["sass"] = { + icon = "", + color = "#a33759", + cterm_color = "125", + name = "Sass", + }, + ["sbt"] = { + icon = "", + color = "#992e33", + cterm_color = "88", + name = "sbt", + }, + ["scad"] = { + icon = "", + color = "#53480f", + cterm_color = "58", + name = "OpenSCAD", + }, + ["scala"] = { + icon = "", + color = "#992e33", + cterm_color = "88", + name = "Scala", + }, + ["sc"] = { + icon = "", + color = "#992e33", + cterm_color = "88", + name = "ScalaScript", + }, + ["scm"] = { + icon = "󰘧", + color = "#303030", + cterm_color = "236", + name = "Scheme", + }, + ["scss"] = { + icon = "", + color = "#a33759", + cterm_color = "125", + name = "Scss", + }, + ["sh"] = { + icon = "", + color = "#3a4446", + cterm_color = "238", + name = "Sh", + }, + ["sha1"] = { + icon = "󰕥", + color = "#5d5975", + cterm_color = "60", + name = "Sha1", + }, + ["sha224"] = { + icon = "󰕥", + color = "#5d5975", + cterm_color = "60", + name = "Sha224", + }, + ["sha256"] = { + icon = "󰕥", + color = "#5d5975", + cterm_color = "60", + name = "Sha256", + }, + ["sha384"] = { + icon = "󰕥", + color = "#5d5975", + cterm_color = "60", + name = "Sha384", + }, + ["sha512"] = { + icon = "󰕥", + color = "#5d5975", + cterm_color = "60", + name = "Sha512", + }, + ["sig"] = { + icon = "λ", + color = "#975122", + cterm_color = "130", + name = "Sig", + }, + ["signature"] = { + icon = "λ", + color = "#975122", + cterm_color = "130", + name = "Signature", + }, + ["skp"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "SketchUp", + }, + ["sldasm"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "SolidWorksAsm", + }, + ["sldprt"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "SolidWorksPrt", + }, + ["slim"] = { + icon = "", + color = "#aa391c", + cterm_color = "124", + name = "Slim", + }, + ["sln"] = { + icon = "", + color = "#643995", + cterm_color = "91", + name = "Sln", + }, + ["slvs"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "SolveSpace", + }, + ["sml"] = { + icon = "λ", + color = "#975122", + cterm_color = "130", + name = "Sml", + }, + ["so"] = { + icon = "", + color = "#494a47", + cterm_color = "239", + name = "SharedObject", + }, + ["sol"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Solidity", + }, + ["spec.js"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "SpecJs", + }, + ["spec.jsx"] = { + icon = "", + color = "#158197", + cterm_color = "31", + name = "JavaScriptReactSpec", + }, + ["spec.ts"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "SpecTs", + }, + ["spec.tsx"] = { + icon = "", + color = "#1354bf", + cterm_color = "26", + name = "TypeScriptReactSpec", + }, + ["sql"] = { + icon = "", + color = "#494848", + cterm_color = "238", + name = "Sql", + }, + ["sqlite"] = { + icon = "", + color = "#494848", + cterm_color = "238", + name = "Sql", + }, + ["sqlite3"] = { + icon = "", + color = "#494848", + cterm_color = "238", + name = "Sql", + }, + ["srt"] = { + icon = "󰨖", + color = "#805c0a", + cterm_color = "94", + name = "Srt", + }, + ["ssa"] = { + icon = "󰨖", + color = "#805c0a", + cterm_color = "94", + name = "Ssa", + }, + ["stl"] = { + icon = "󰆧", + color = "#5b5b5b", + cterm_color = "240", + name = "3DObjectFile", + }, + ["strings"] = { + icon = "", + color = "#1c708e", + cterm_color = "24", + name = "Localization", + }, + ["ste"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "Ste", + }, + ["step"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "Step", + }, + ["stp"] = { + icon = "󰻫", + color = "#576342", + cterm_color = "58", + name = "Stp", + }, + ["styl"] = { + icon = "", + color = "#466024", + cterm_color = "22", + name = "Styl", + }, + ["sub"] = { + icon = "󰨖", + color = "#805c0a", + cterm_color = "94", + name = "Sub", + }, + ["sublime"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "Sublime", + }, + ["suo"] = { + icon = "", + color = "#643995", + cterm_color = "91", + name = "Suo", + }, + ["sv"] = { + icon = "󰍛", + color = "#017226", + cterm_color = "22", + name = "SystemVerilog", + }, + ["svelte"] = { + icon = "", + color = "#bf2e00", + cterm_color = "160", + name = "Svelte", + }, + ["svh"] = { + icon = "󰍛", + color = "#017226", + cterm_color = "22", + name = "SystemVerilog", + }, + ["svg"] = { + icon = "󰜡", + color = "#80581e", + cterm_color = "94", + name = "Svg", + }, + ["swift"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "Swift", + }, + ["t"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Tor", + }, + ["tbc"] = { + icon = "󰛓", + color = "#1e5cb3", + cterm_color = "25", + name = "Tcl", + }, + ["tcl"] = { + icon = "󰛓", + color = "#1e5cb3", + cterm_color = "25", + name = "Tcl", + }, + ["templ"] = { + icon = "", + color = "#6e5e18", + cterm_color = "58", + name = "Templ", + }, + ["terminal"] = { + icon = "", + color = "#217929", + cterm_color = "28", + name = "Terminal", + }, + ["test.js"] = { + icon = "", + color = "#666620", + cterm_color = "58", + name = "TestJs", + }, + ["test.jsx"] = { + icon = "", + color = "#158197", + cterm_color = "31", + name = "JavaScriptReactTest", + }, + ["test.ts"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "TestTs", + }, + ["test.tsx"] = { + icon = "", + color = "#1354bf", + cterm_color = "26", + name = "TypeScriptReactTest", + }, + ["tex"] = { + icon = "", + color = "#3D6117", + cterm_color = "22", + name = "Tex", + }, + ["tf"] = { + icon = "", + color = "#4732af", + cterm_color = "55", + name = "Terraform", + }, + ["tfvars"] = { + icon = "", + color = "#4732af", + cterm_color = "55", + name = "TFVars", + }, + ["tgz"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "Tgz", + }, + ["tmux"] = { + icon = "", + color = "#0f8c13", + cterm_color = "28", + name = "Tmux", + }, + ["toml"] = { + icon = "", + color = "#753219", + cterm_color = "88", + name = "Toml", + }, + ["torrent"] = { + icon = "", + color = "#226654", + cterm_color = "23", + name = "Torrent", + }, + ["tres"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "GodotTextResource", + }, + ["ts"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "TypeScript", + }, + ["tscn"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "GodotTextScene", + }, + ["tsconfig"] = { + icon = "", + color = "#aa5a00", + cterm_color = "130", + name = "TypoScriptConfig", + }, + ["tsx"] = { + icon = "", + color = "#1354bf", + cterm_color = "26", + name = "Tsx", + }, + ["ttf"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "TrueTypeFont", + }, + ["twig"] = { + icon = "", + color = "#466024", + cterm_color = "22", + name = "Twig", + }, + ["txz"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "Txz", + }, + ["typoscript"] = { + icon = "", + color = "#aa5a00", + cterm_color = "130", + name = "TypoScript", + }, + ["txt"] = { + icon = "󰈙", + color = "#447028", + cterm_color = "22", + name = "Txt", + }, + ["ui"] = { + icon = "", + color = "#0c306e", + cterm_color = "17", + name = "UI", + }, + ["v"] = { + icon = "󰍛", + color = "#017226", + cterm_color = "22", + name = "Verilog", + }, + ["vala"] = { + icon = "", + color = "#562b86", + cterm_color = "54", + name = "Vala", + }, + ["vh"] = { + icon = "󰍛", + color = "#017226", + cterm_color = "22", + name = "Verilog", + }, + ["vhd"] = { + icon = "󰍛", + color = "#017226", + cterm_color = "22", + name = "VHDL", + }, + ["vhdl"] = { + icon = "󰍛", + color = "#017226", + cterm_color = "22", + name = "VHDL", + }, + ["vim"] = { + icon = "", + color = "#017226", + cterm_color = "22", + name = "Vim", + }, + ["vsh"] = { + icon = "", + color = "#3e5a7f", + cterm_color = "24", + name = "Vlang", + }, + ["vsix"] = { + icon = "", + color = "#643995", + cterm_color = "91", + name = "Vsix", + }, + ["vue"] = { + icon = "", + color = "#466024", + cterm_color = "22", + name = "Vue", + }, + ["wasm"] = { + icon = "", + color = "#4539a4", + cterm_color = "55", + name = "Wasm", + }, + ["wav"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "WaveformAudioFile", + }, + ["webm"] = { + icon = "", + color = "#7e4c10", + cterm_color = "94", + name = "Webm", + }, + ["webmanifest"] = { + icon = "", + color = "#504b1e", + cterm_color = "58", + name = "Webmanifest", + }, + ["webp"] = { + icon = "", + color = "#6b4d83", + cterm_color = "96", + name = "Webp", + }, + ["webpack"] = { + icon = "󰜫", + color = "#36677c", + cterm_color = "24", + name = "Webpack", + }, + ["wma"] = { + icon = "", + color = "#0075aa", + cterm_color = "24", + name = "WindowsMediaAudio", + }, + ["woff"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "WebOpenFontFormat", + }, + ["woff2"] = { + icon = "", + color = "#2f2f2f", + cterm_color = "236", + name = "WebOpenFontFormat", + }, + ["wrl"] = { + icon = "󰆧", + color = "#5b5b5b", + cterm_color = "240", + name = "VRML", + }, + ["wrz"] = { + icon = "󰆧", + color = "#5b5b5b", + cterm_color = "240", + name = "VRML", + }, + ["x"] = { + icon = "", + color = "#3b69aa", + cterm_color = "25", + name = "Logos", + }, + ["xm"] = { + icon = "", + color = "#36677c", + cterm_color = "24", + name = "Logos", + }, + ["xaml"] = { + icon = "󰙳", + color = "#512bd4", + cterm_color = "56", + name = "Xaml", + }, + ["xcf"] = { + icon = "", + color = "#4a4434", + cterm_color = "238", + name = "GIMP", + }, + ["xcplayground"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "XcPlayground", + }, + ["xcstrings"] = { + icon = "", + color = "#1c708e", + cterm_color = "24", + name = "XcLocalization", + }, + ["xls"] = { + icon = "󰈛", + color = "#207245", + cterm_color = "29", + name = "Xls", + }, + ["xlsx"] = { + icon = "󰈛", + color = "#207245", + cterm_color = "29", + name = "Xlsx", + }, + ["xml"] = { + icon = "󰗀", + color = "#975122", + cterm_color = "130", + name = "Xml", + }, + ["xpi"] = { + icon = "", + color = "#bf1401", + cterm_color = "124", + name = "Xpi", + }, + ["xul"] = { + icon = "", + color = "#975122", + cterm_color = "130", + name = "Xul", + }, + ["xz"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "Xz", + }, + ["yaml"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Yaml", + }, + ["yml"] = { + icon = "", + color = "#526064", + cterm_color = "59", + name = "Yml", + }, + ["zig"] = { + icon = "", + color = "#7b4d0e", + cterm_color = "94", + name = "Zig", + }, + ["zip"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "Zip", + }, + ["zsh"] = { + icon = "", + color = "#447028", + cterm_color = "22", + name = "Zsh", + }, + ["zst"] = { + icon = "", + color = "#76520c", + cterm_color = "94", + name = "Zst", + }, +} + +local icons_by_operating_system = { + ["apple"] = { + icon = "", + color = "#515556", + cterm_color = "240", + name = "Apple", + }, + ["windows"] = { + icon = "", + color = "#007bb3", + cterm_color = "67", + name = "Windows", + }, + ["linux"] = { + icon = "", + color = "#333332", + cterm_color = "236", + name = "Linux", + }, + ["alma"] = { + icon = "", + color = "#bf3437", + cterm_color = "160", + name = "Almalinux", + }, + ["alpine"] = { + icon = "", + color = "#0d597f", + cterm_color = "24", + name = "Alpine", + }, + ["aosc"] = { + icon = "", + color = "#c00000", + cterm_color = "124", + name = "AOSC", + }, + ["arch"] = { + icon = "󰣇", + color = "#0b6f9e", + cterm_color = "24", + name = "Arch", + }, + ["archcraft"] = { + icon = "", + color = "#435e52", + cterm_color = "23", + name = "Archcraft", + }, + ["archlabs"] = { + icon = "", + color = "#503f42", + cterm_color = "238", + name = "Archlabs", + }, + ["arcolinux"] = { + icon = "", + color = "#44609d", + cterm_color = "25", + name = "ArcoLinux", + }, + ["artix"] = { + icon = "", + color = "#2b788f", + cterm_color = "31", + name = "Artix", + }, + ["biglinux"] = { + icon = "", + color = "#127796", + cterm_color = "31", + name = "BigLinux", + }, + ["centos"] = { + icon = "", + color = "#7a3d6a", + cterm_color = "89", + name = "Centos", + }, + ["crystallinux"] = { + icon = "", + color = "#a900ff", + cterm_color = "129", + name = "CrystalLinux", + }, + ["debian"] = { + icon = "", + color = "#a80030", + cterm_color = "88", + name = "Debian", + }, + ["deepin"] = { + icon = "", + color = "#1d6fa5", + cterm_color = "24", + name = "Deepin", + }, + ["devuan"] = { + icon = "", + color = "#404a52", + cterm_color = "238", + name = "Devuan", + }, + ["elementary"] = { + icon = "", + color = "#3b6081", + cterm_color = "24", + name = "Elementary", + }, + ["endeavour"] = { + icon = "", + color = "#5c2e8b", + cterm_color = "54", + name = "Endeavour", + }, + ["fedora"] = { + icon = "", + color = "#072a5e", + cterm_color = "17", + name = "Fedora", + }, + ["freebsd"] = { + icon = "", + color = "#c90f02", + cterm_color = "160", + name = "FreeBSD", + }, + ["garuda"] = { + icon = "", + color = "#1f57a9", + cterm_color = "25", + name = "GarudaLinux", + }, + ["gentoo"] = { + icon = "󰣨", + color = "#585667", + cterm_color = "60", + name = "Gentoo", + }, + ["guix"] = { + icon = "", + color = "#554400", + cterm_color = "58", + name = "Guix", + }, + ["hyperbola"] = { + icon = "", + color = "#404040", + cterm_color = "238", + name = "HyperbolaGNULinuxLibre", + }, + ["illumos"] = { + icon = "", + color = "#bf320b", + cterm_color = "160", + name = "Illumos", + }, + ["kali"] = { + icon = "", + color = "#1d59bf", + cterm_color = "26", + name = "Kali", + }, + ["kdeneon"] = { + icon = "", + color = "#187c7b", + cterm_color = "30", + name = "KDEneon", + }, + ["kubuntu"] = { + icon = "", + color = "#005c92", + cterm_color = "24", + name = "Kubuntu", + }, + ["locos"] = { + icon = "", + color = "#7d5a01", + cterm_color = "94", + name = "LocOS", + }, + ["lxle"] = { + icon = "", + color = "#474747", + cterm_color = "238", + name = "LXLE", + }, + ["mint"] = { + icon = "󰣭", + color = "#447529", + cterm_color = "28", + name = "Mint", + }, + ["mageia"] = { + icon = "", + color = "#1a719f", + cterm_color = "24", + name = "Mageia", + }, + ["manjaro"] = { + icon = "", + color = "#227b3b", + cterm_color = "29", + name = "Manjaro", + }, + ["mxlinux"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "MXLinux", + }, + ["nixos"] = { + icon = "", + color = "#3d586e", + cterm_color = "24", + name = "NixOS", + }, + ["openbsd"] = { + icon = "", + color = "#514310", + cterm_color = "58", + name = "OpenBSD", + }, + ["opensuse"] = { + icon = "", + color = "#4a7818", + cterm_color = "64", + name = "openSUSE", + }, + ["parabola"] = { + icon = "", + color = "#515373", + cterm_color = "60", + name = "ParabolaGNULinuxLibre", + }, + ["parrot"] = { + icon = "", + color = "#2a6f80", + cterm_color = "23", + name = "Parrot", + }, + ["pop_os"] = { + icon = "", + color = "#307b85", + cterm_color = "30", + name = "Pop_OS", + }, + ["postmarketos"] = { + icon = "", + color = "#007300", + cterm_color = "22", + name = "postmarketOS", + }, + ["puppylinux"] = { + icon = "", + color = "#51575c", + cterm_color = "240", + name = "PuppyLinux", + }, + ["qubesos"] = { + icon = "", + color = "#2957a2", + cterm_color = "25", + name = "QubesOS", + }, + ["raspberry_pi"] = { + icon = "", + color = "#be1848", + cterm_color = "161", + name = "RaspberryPiOS", + }, + ["redhat"] = { + icon = "󱄛", + color = "#EE0000", + cterm_color = "196", + name = "Redhat", + }, + ["rocky"] = { + icon = "", + color = "#0b865e", + cterm_color = "29", + name = "RockyLinux", + }, + ["sabayon"] = { + icon = "", + color = "#424242", + cterm_color = "238", + name = "Sabayon", + }, + ["slackware"] = { + icon = "", + color = "#35477f", + cterm_color = "25", + name = "Slackware", + }, + ["solus"] = { + icon = "", + color = "#4b5163", + cterm_color = "239", + name = "Solus", + }, + ["tails"] = { + icon = "", + color = "#56347c", + cterm_color = "54", + name = "Tails", + }, + ["trisquel"] = { + icon = "", + color = "#0f58b6", + cterm_color = "25", + name = "TrisquelGNULinux", + }, + ["ubuntu"] = { + icon = "", + color = "#a6360f", + cterm_color = "124", + name = "Ubuntu", + }, + ["vanillaos"] = { + icon = "", + color = "#533f1a", + cterm_color = "58", + name = "VanillaOS", + }, + ["void"] = { + icon = "", + color = "#295340", + cterm_color = "23", + name = "Void", + }, + ["xerolinux"] = { + icon = "", + color = "#5b5f97", + cterm_color = "60", + name = "XeroLinux", + }, + ["zorin"] = { + icon = "", + color = "#0f79ae", + cterm_color = "67", + name = "Zorin", + }, +} + +local icons_by_desktop_environment = { + ["budgie"] = { + icon = "", + color = "#4e5361", + cterm_color = "240", + name = "Budgie", + }, + ["cinnamon"] = { + icon = "", + color = "#93451f", + cterm_color = "124", + name = "Cinnamon", + }, + ["gnome"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "GNOME", + }, + ["lxde"] = { + icon = "", + color = "#525252", + cterm_color = "239", + name = "LXDE", + }, + ["lxqt"] = { + icon = "", + color = "#016d9e", + cterm_color = "24", + name = "LXQt", + }, + ["mate"] = { + icon = "", + color = "#4e6d2e", + cterm_color = "22", + name = "MATE", + }, + ["plasma"] = { + icon = "", + color = "#1467b7", + cterm_color = "25", + name = "KDEPlasma", + }, + ["xfce"] = { + icon = "", + color = "#0080a7", + cterm_color = "31", + name = "Xfce", + }, +} + +local icons_by_window_manager = { + ["awesomewm"] = { + icon = "", + color = "#3e4651", + cterm_color = "238", + name = "awesome", + }, + ["bspwm"] = { + icon = "", + color = "#4f4f4f", + cterm_color = "239", + name = "BSPWM", + }, + ["dwm"] = { + icon = "", + color = "#0d5980", + cterm_color = "24", + name = "dwm", + }, + ["enlightenment"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "Enlightenment", + }, + ["fluxbox"] = { + icon = "", + color = "#404040", + cterm_color = "238", + name = "Fluxbox", + }, + ["hyprland"] = { + icon = "", + color = "#008082", + cterm_color = "30", + name = "Hyprland", + }, + ["i3"] = { + icon = "", + color = "#2e2f30", + cterm_color = "236", + name = "i3", + }, + ["jwm"] = { + icon = "", + color = "#005a9a", + cterm_color = "25", + name = "JWM", + }, + ["qtile"] = { + icon = "", + color = "#333333", + cterm_color = "236", + name = "Qtile", + }, + ["sway"] = { + icon = "", + color = "#4e5815", + cterm_color = "58", + name = "Sway", + }, + ["xmonad"] = { + icon = "", + color = "#a9333e", + cterm_color = "131", + name = "xmonad", + }, +} + +return { + icons_by_filename = icons_by_filename, + icons_by_file_extension = icons_by_file_extension, + icons_by_operating_system = icons_by_operating_system, + icons_by_desktop_environment = icons_by_desktop_environment, + icons_by_window_manager = icons_by_window_manager, +} diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/plugin/nvim-web-devicons.vim b/config/neovim/store/lazy-plugins/nvim-web-devicons/plugin/nvim-web-devicons.vim new file mode 100644 index 00000000..beccf9ef --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/plugin/nvim-web-devicons.vim @@ -0,0 +1,12 @@ +if exists('g:loaded_devicons') | finish | endif + +let s:save_cpo = &cpo +set cpo&vim + +" TODO change so its easier to get +let g:nvim_web_devicons = 1 + +let &cpo = s:save_cpo +unlet s:save_cpo + +let g:loaded_devicons = 1 diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/scripts/filetype-generator.sh b/config/neovim/store/lazy-plugins/nvim-web-devicons/scripts/filetype-generator.sh new file mode 100755 index 00000000..cab17966 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/scripts/filetype-generator.sh @@ -0,0 +1,68 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/sh + +## Credit: @B0o (Maddison Hellstrom) + +set -euo pipefail + +function get_icon_names() { + curl 'https://raw.githubusercontent.com/kyazdani42/nvim-web-devicons/master/lua/nvim-web-devicons.lua' | + lua -e ' + pats={} + for name in pairs(dofile().get_icons()) do + table.insert(pats, name) + end + table.sort(pats) + print(table.concat(pats, "\n")) + ' +} + +function main() { + local tmp + tmp="$(mktemp -d)" + # shellcheck disable=2064 + trap "rmdir '$tmp'" EXIT + cd "$tmp" + local file + local -A filetypes=() + local -a missing=() + while read -r pat; do + echo "$pat" >&2 + if [[ "$pat" =~ ^\. ]]; then + file="$pat" + else + file="test.$pat" + fi + touch "./$file" + local ft + ft="$(/usr/bin/nvim -u NORC --noplugin --headless "./$file" +'lua + local ok, err = pcall(vim.fn.writefile, { vim.bo.filetype }, "/dev/stdout") + if not ok then + print(err .. "\n") + vim.cmd "cquit" + end + vim.cmd "quit" + ')" + rm "./$file" + if [[ -n "$ft" ]]; then + if [[ -v filetypes["$ft"] ]]; then + filetypes["$ft"]="${filetypes["$ft"]}, '$pat'" + else + filetypes["$ft"]="'$pat'" + fi + else + missing+=("$pat") + fi + done < <(get_icon_names) + + echo "local filetypes = {" + for ft in "${!filetypes[@]}"; do + echo " ['$ft'] = { ${filetypes[$ft]} }," + done + echo "}" + echo + echo "local missing = {" + printf " '%s',\n" "${missing[@]}" + echo "}" +} + +main "$@" diff --git a/config/neovim/store/lazy-plugins/nvim-web-devicons/scripts/generate_colors.lua b/config/neovim/store/lazy-plugins/nvim-web-devicons/scripts/generate_colors.lua new file mode 100644 index 00000000..f7ed98d6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/nvim-web-devicons/scripts/generate_colors.lua @@ -0,0 +1,206 @@ +-- Plugin lifepillar/vim-colortemplate must be available on &runtimepath +-- +-- The current working directory must be set to the repo root + +-- IMPORTANT: the `cterm_color` key must always be below the `color` key of the +-- same icon. Currently they are. + +-- This file should be run from the shell with `make colors` + +local fn = vim.fn + +--- Exit vim +--- @param msg string +--- @param rc number +local function error_exit(msg, rc) + print(msg .. "\n") + vim.cmd("cq " .. rc) +end + +if not jit then + error_exit("Neovim must be LuaJIT-enabled to source this script", 1) +end + +if fn.filereadable "lua/nvim-web-devicons.lua" == 0 then + error_exit("lua/nvim-web-devicons.lua not found", 1) +end + +local rc, err = pcall(vim.fn["colortemplate#colorspace#approx"], "#000000") +if not rc then + error_exit(err .. "\nlifepillar/vim-colortemplate not present in &runtimepath '" .. vim.o.runtimepath .. "'", 1) +end + +-- Needed in order to have the correct indentation on line insertion +vim.o.autoindent = true + +-------------------------------------------------------------------------------- +-- Local functions +-------------------------------------------------------------------------------- + +local light78 = 255 * 7 / 8 +local light68 = 255 * 6 / 8 +local light58 = 255 * 5 / 8 +local light12 = 255 / 2 +local light13 = 255 / 3 + +local function darken_color(rrggbb) + local r, g, b = rrggbb:match "%#(%x%x)(%x%x)(%x%x)" + r, g, b = tonumber("0x" .. r), tonumber("0x" .. g), tonumber("0x" .. b) + -- luminance formula: see https://stackoverflow.com/a/596243 + local lum = 0.299 * r + 0.587 * g + 0.114 * b + if lum < light13 then -------------------- darkest tertile + return rrggbb + elseif lum < light12 then ---------------- second darkest quartile + r = bit.tohex(r / 4 * 3):sub(-2) + g = bit.tohex(g / 4 * 3):sub(-2) + b = bit.tohex(b / 4 * 3):sub(-2) + elseif lum < light58 then ---------------- lightest octiles: first + r = bit.tohex(r / 3 * 2):sub(-2) + g = bit.tohex(g / 3 * 2):sub(-2) + b = bit.tohex(b / 3 * 2):sub(-2) + elseif lum < light68 then ---------------- lightest octiles: second + r = bit.tohex(r / 2):sub(-2) + g = bit.tohex(g / 2):sub(-2) + b = bit.tohex(b / 2):sub(-2) + elseif lum < light78 then ---------------- lightest octiles: third + r = bit.tohex(r / 3):sub(-2) + g = bit.tohex(g / 3):sub(-2) + b = bit.tohex(b / 3):sub(-2) + else ------------------------------------- lightest octile + r = bit.tohex(r / 5):sub(-2) + g = bit.tohex(g / 5):sub(-2) + b = bit.tohex(b / 5):sub(-2) + end + return string.format("#%s%s%s", r, g, b) +end + +local function update_cterm_colors() + -- move to first line + vim.cmd ":1" + local last = 0 + + while true do + local cur = fn.search "^\\s*color =" + if cur < last then + break + end + last = cur + local color = vim.api.nvim_get_current_line():match "%#......" + local cterm_color = fn["colortemplate#colorspace#approx"](color).index + if fn.search "^\\s*cterm_color" == cur + 1 then + vim.cmd(string.format("s/=.*/= %q,", cterm_color)) + else + vim.cmd(tostring(cur)) + vim.cmd.normal(string.format("octerm_color = %q,", cterm_color)) + end + end +end + +local function generate_lines() + local start = fn.line "." - 1 + fn.search "^}" + local finish = fn.line "." + local lines = vim.api.nvim_buf_get_lines(fn.bufnr(), start, finish, true) + for i = 1, #lines do + if lines[i]:find "^%s*color =" then + local rrggbb = lines[i]:match '"(#%x%x%x%x%x%x)"' + if not rrggbb then + error_exit(string.format("invalid color at line %s: '%s'", i, lines[i]), 1) + end + lines[i] = lines[i]:gsub(rrggbb, darken_color) + end + end + table.insert(lines, "") + return lines +end + +-------------------------------------------------------------------------------- +-- Generate file with icons for light backgrounds +-------------------------------------------------------------------------------- + +if fn.filereadable "lua/nvim-web-devicons/icons-default.lua" == 0 then + error_exit("lua/nvim-web-devicons/icons-default.lua not found", 1) +end + +vim.cmd "noswapfile drop lua/nvim-web-devicons/icons-default.lua" + +print "Generating file with icons for light backgrounds..." + +-- move to first line +vim.cmd ":1" + +-- first table +if fn.search("^local icons_by_filename", "c") == 0 then + error_exit("Table 'icons_by_filename' not found in lua/nvim-web-devicons/icons-default.lua", 1) +end +local lines = generate_lines() + +-- second table +if fn.search("^local icons_by_file_extension", "c") == 0 then + error_exit("Table 'icons_by_file_extension' not found in lua/nvim-web-devicons/icons-default.lua", 1) +end +local lines2 = generate_lines() + +-- third table +if fn.search("^local icons_by_operating_system", "c") == 0 then + error_exit("Table 'icons_by_operating_system' not found in lua/nvim-web-devicons/icons-default.lua", 1) +end +local lines3 = generate_lines() + +-- fourth table +if fn.search("^local icons_by_desktop_environment", "c") == 0 then + error_exit("Table 'icons_by_desktop_environment' not found in lua/nvim-web-devicons/icons-default.lua", 1) +end +local lines4 = generate_lines() + +-- fifth table +if fn.search("^local icons_by_window_manager", "c") == 0 then + error_exit("Table 'icons_by_window_manager' not found in lua/nvim-web-devicons/icons-default.lua", 1) +end +local lines5 = generate_lines() + +table.insert(lines5, "return {") +table.insert(lines5, " icons_by_filename = icons_by_filename,") +table.insert(lines5, " icons_by_file_extension = icons_by_file_extension,") +table.insert(lines5, " icons_by_operating_system = icons_by_operating_system,") +table.insert(lines5, " icons_by_desktop_environment = icons_by_desktop_environment,") +table.insert(lines5, " icons_by_window_manager = icons_by_window_manager,") +table.insert(lines5, "}") + +-- write both tables to file +fn.writefile(lines, "lua/nvim-web-devicons/icons-light.lua") +fn.writefile(lines2, "lua/nvim-web-devicons/icons-light.lua", "a") +fn.writefile(lines3, "lua/nvim-web-devicons/icons-light.lua", "a") +fn.writefile(lines4, "lua/nvim-web-devicons/icons-light.lua", "a") +fn.writefile(lines5, "lua/nvim-web-devicons/icons-light.lua", "a") + +print "Finished creating new file!" + +-------------------------------------------------------------------------------- +-- Update cterm colors for dark background +-------------------------------------------------------------------------------- + +print "Generating cterm colors for dark background...\n" + +update_cterm_colors() + +vim.cmd "wall!" +print "Finished!" + +-------------------------------------------------------------------------------- +-- Generate cterm colors for light background +-------------------------------------------------------------------------------- + +if fn.filereadable "lua/nvim-web-devicons/icons-light.lua" == 0 then + error_exit("lua/nvim-web-devicons/icons-light.lua not found", 1) +end + +vim.cmd "noswapfile drop lua/nvim-web-devicons/icons-light.lua" + +print "Generating cterm colors for light background...\n" + +update_cterm_colors() + +vim.cmd ":1" +vim.cmd "wall!" +print "Finished!\n" diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/data/plenary/filetypes/base.lua b/config/neovim/store/lazy-plugins/plenary.nvim/data/plenary/filetypes/base.lua new file mode 100644 index 00000000..8b76c8c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/data/plenary/filetypes/base.lua @@ -0,0 +1,885 @@ +return { + extension = { + ['ncl'] = [[text]], + ['ph'] = [[perl]], + ['al'] = [[perl]], + ['cl'] = [[lisp]], + ['vhi'] = [[vhdl]], + ['sublime-snippet'] = [[xml]], + ['tcl'] = [[tcl]], + ['pp'] = [[pascal]], + ['builds'] = [[xml]], + ['lua'] = [[lua]], + ['pkb'] = [[plsql]], + ['wl'] = [[mma]], + ['6'] = [[groff]], + ['scm'] = [[scheme]], + ['ml'] = [[ocaml]], + ['filters'] = [[xml]], + ['st'] = [[html]], + ['ksy'] = [[yaml]], + ['mt'] = [[mma]], + ['ada'] = [[ada]], + ['vho'] = [[vhdl]], + ['nawk'] = [[awk]], + ['3pm'] = [[groff]], + ['maxhelp'] = [[json]], + ['ct'] = [[xml]], + ['ipp'] = [[cpp]], + ['a51'] = [[asm]], + ['meta'] = [[yaml]], + ['fsproj'] = [[xml]], + ['ccxml'] = [[xml]], + ['ado'] = [[stata]], + ['mli'] = [[ocaml]], + ['r2'] = [[rebol]], + ['csl'] = [[xml]], + ['bbx'] = [[tex]], + ['xsjslib'] = [[javascript]], + ['e'] = [[eiffel]], + ['tac'] = [[python]], + ['mustache'] = [[smarty]], + ['zcml'] = [[xml]], + ['glade'] = [[xml]], + ['cuh'] = [[cuda]], + ['cxx'] = [[cpp]], + ['urdf'] = [[xml]], + ['ditaval'] = [[xml]], + ['cscfg'] = [[xml]], + ['j2'] = [[django]], + ['pkgproj'] = [[xml]], + ['ma'] = [[mma]], + ['cljs.hl'] = [[clojure]], + ['brd'] = [[xml]], + ['asn'] = [[asn]], + ['xspec'] = [[xml]], + ['db2'] = [[sql]], + ['sjs'] = [[javascript]], + ['m'] = [[matlab]], + ['gdbinit'] = [[gdb]], + ['re'] = [[cpp]], + ['adoc'] = [[asciidoc]], + ['pyde'] = [[python]], + ['mjml'] = [[xml]], + ['workbook'] = [[markdown]], + ['ssjs'] = [[javascript]], + ['dircolors'] = [[dircolors]], + ['ui'] = [[xml]], + ['ant'] = [[xml]], + ['wast'] = [[wast]], + ['_js'] = [[javascript]], + ['7'] = [[groff]], + ['dylan'] = [[dylan]], + ['jsproj'] = [[xml]], + ['jsb'] = [[javascript]], + ['xml'] = [[xml]], + ['tcsh'] = [[tcsh]], + ['xpy'] = [[python]], + ['wisp'] = [[clojure]], + ['tm'] = [[tcl]], + ['plot'] = [[gnuplot]], + ['mjs'] = [[javascript]], + ['cjs'] = [[javascript]], + ['env'] = [[sh]], + ['jsfl'] = [[javascript]], + ['eye'] = [[ruby]], + ['jinja2'] = [[django]], + ['lookml'] = [[yaml]], + ['mcmeta'] = [[json]], + ['workflow'] = [[xml]], + ['eq'] = [[cs]], + ['storyboard'] = [[xml]], + ['nbp'] = [[mma]], + ['maxpat'] = [[json]], + ['yy'] = [[yacc]], + ['h++'] = [[cpp]], + ['asc'] = [[asciidoc]], + ['phps'] = [[php]], + ['cabal'] = [[cabal]], + ['xacro'] = [[xml]], + ['dhall'] = [[haskell]], + ['vba'] = [[vim]], + ['feature'] = [[cucumber]], + ['psc1'] = [[xml]], + ['plb'] = [[plsql]], + ['logtalk'] = [[logtalk]], + ['pascal'] = [[pascal]], + ['smk'] = [[python]], + ['rst.txt'] = [[rst]], + ['svh'] = [[systemverilog]], + ['sthlp'] = [[stata]], + ['nb'] = [[text]], + ['hrl'] = [[erlang]], + ['f'] = [[forth]], + ['rb'] = [[ruby]], + ['xsd'] = [[xml]], + ['cfg'] = [[dosini]], + ['1in'] = [[groff]], + ['mkiv'] = [[tex]], + ['mask'] = [[yaml]], + ['cproject'] = [[xml]], + ['gnuplot'] = [[gnuplot]], + ['sagews'] = [[python]], + ['nsh'] = [[nsis]], + ['njk'] = [[django]], + ['pic'] = [[pic]], + ['man'] = [[groff]], + ['owl'] = [[xml]], + ['ino'] = [[cpp]], + ['dtx'] = [[tex]], + ['volt'] = [[d]], + ['ltx'] = [[tex]], + ['rtf'] = [[rtf]], + ['jscad'] = [[javascript]], + ['8'] = [[groff]], + ['3qt'] = [[groff]], + ['n'] = [[groff]], + ['lisp'] = [[lisp]], + ['forth'] = [[forth]], + ['dll.config'] = [[xml]], + ['prefab'] = [[yaml]], + ['ads'] = [[ada]], + ['jake'] = [[javascript]], + ['zsh'] = [[sh]], + ['v'] = [[verilog]], + ['ini'] = [[dosini]], + ['command'] = [[sh]], + ['fr'] = [[forth]], + ['yacc'] = [[yacc]], + ['ccproj'] = [[xml]], + ['xqy'] = [[xquery]], + ['rabl'] = [[ruby]], + ['nr'] = [[groff]], + ['wsgi'] = [[python]], + ['model.lkml'] = [[yaml]], + ['lsl'] = [[lsl]], + ['yyp'] = [[json]], + ['yaml-tmlanguage'] = [[yaml]], + ['rbi'] = [[ruby]], + ['srt'] = [[lisp]], + ['unity'] = [[yaml]], + ['vhs'] = [[vhdl]], + ['yap'] = [[prolog]], + ['php5'] = [[php]], + ['pas'] = [[pascal]], + ['gyp'] = [[python]], + ['jsonl'] = [[json]], + ['cc'] = [[cpp]], + ['grt'] = [[groovy]], + ['txi'] = [[texinfo]], + ['asm'] = [[asm]], + ['plx'] = [[perl]], + ['prefs'] = [[dosini]], + ['mly'] = [[ocaml]], + ['xsl'] = [[xslt]], + ['osm'] = [[xml]], + ['sc'] = [[scala]], + ['au3'] = [[autoit]], + ['obj'] = [[obj]], + ['uc'] = [[java]], + ['lhs'] = [[lhaskell]], + ['1'] = [[groff]], + ['resx'] = [[xml]], + ['geojson'] = [[json]], + ['tmux'] = [[sh]], + ['rg'] = [[clojure]], + ['tcc'] = [[cpp]], + ['cbl'] = [[cobol]], + ['wat'] = [[wast]], + ['com'] = [[dcl]], + ['podspec'] = [[ruby]], + ['no'] = [[text]], + ['spec'] = [[python]], + ['vhw'] = [[vhdl]], + ['maxproj'] = [[json]], + ['xbm'] = [[c]], + ['wxl'] = [[xml]], + ['mk'] = [[make]], + ['epj'] = [[json]], + ['do'] = [[stata]], + ['rexx'] = [[rexx]], + ['ms'] = [[groff]], + ['w'] = [[cweb]], + ['ss'] = [[scheme]], + ['desktop.in'] = [[desktop]], + ['po'] = [[po]], + ['mdpolicy'] = [[xml]], + ['mathematica'] = [[mma]], + ['vw'] = [[plsql]], + ['rss'] = [[xml]], + ['cs'] = [[cs]], + ['mkdown'] = [[markdown]], + ['gs'] = [[javascript]], + ['doh'] = [[stata]], + ['vbproj'] = [[xml]], + ['sfproj'] = [[xml]], + ['mat'] = [[yaml]], + ['cmake.in'] = [[cmake]], + ['rest.txt'] = [[rst]], + ['sls'] = [[scheme]], + ['fxml'] = [[xml]], + ['jss'] = [[javascript]], + ['avsc'] = [[json]], + ['mbox'] = [[basic]], + ['cake'] = [[cs]], + ['gst'] = [[xml]], + ['p6l'] = [[perl6]], + ['cdf'] = [[mma]], + ['pyi'] = [[python]], + ['ahk'] = [[autohotkey]], + ['tmac'] = [[groff]], + ['xaml'] = [[xml]], + ['texi'] = [[texinfo]], + ['udf'] = [[sql]], + ['rebol'] = [[rebol]], + ['gvy'] = [[groovy]], + ['h'] = [[c]], + ['pm6'] = [[perl6]], + ['ivy'] = [[xml]], + ['properties'] = [[dosini]], + ['eliomi'] = [[ocaml]], + ['nanorc'] = [[nanorc]], + ['sps'] = [[scheme]], + ['nproj'] = [[xml]], + ['ch'] = [[clipper]], + ['odd'] = [[xml]], + ['fun'] = [[sml]], + ['mir'] = [[yaml]], + ['nuspec'] = [[xml]], + ['pck'] = [[plsql]], + ['2'] = [[groff]], + ['nl'] = [[lisp]], + ['fth'] = [[forth]], + ['cps'] = [[pascal]], + ['sh'] = [[sh]], + ['rbw'] = [[ruby]], + ['dlm'] = [[idl]], + ['pod'] = [[pod]], + ['clixml'] = [[xml]], + ['duby'] = [[ruby]], + ['gdb'] = [[gdb]], + ['boot'] = [[clojure]], + ['adb'] = [[ada]], + ['tex'] = [[tex]], + ['csx'] = [[cs]], + ['sbt'] = [[scala]], + ['csdef'] = [[xml]], + ['dpr'] = [[pascal]], + ['rex'] = [[rexx]], + ['srdf'] = [[xml]], + ['pl'] = [[perl]], + ['markdown'] = [[markdown]], + ['cgi'] = [[perl]], + ['cp'] = [[cpp]], + ['jinja'] = [[django]], + ['gp'] = [[gnuplot]], + ['x'] = [[rpcgen]], + ['pt'] = [[xml]], + ['tpp'] = [[cpp]], + ['intr'] = [[dylan]], + ['JSON-tmLanguage'] = [[json]], + ['ux'] = [[xml]], + ['fpp'] = [[fortran]], + ['phpt'] = [[php]], + ['4th'] = [[forth]], + ['dita'] = [[xml]], + ['viw'] = [[sql]], + ['vsixmanifest'] = [[xml]], + ['clj'] = [[clojure]], + ['yml.mysql'] = [[yaml]], + ['hpp'] = [[cpp]], + ['xsp.metadata'] = [[xml]], + ['sexp'] = [[lisp]], + ['csproj'] = [[xml]], + ['tab'] = [[sql]], + ['cql'] = [[sql]], + ['abap'] = [[abap]], + ['wlua'] = [[lua]], + ['lex'] = [[lex]], + ['java'] = [[java]], + ['edn'] = [[clojure]], + ['ditamap'] = [[xml]], + ['3p'] = [[groff]], + ['xul'] = [[xml]], + ['cmake'] = [[cmake]], + ['kit'] = [[basic]], + ['rs.in'] = [[rust]], + ['php4'] = [[php]], + ['hxx'] = [[cpp]], + ['cljscm'] = [[clojure]], + ['vcxproj'] = [[xml]], + ['php3'] = [[php]], + ['xml.dist'] = [[xml]], + ['ged'] = [[gedcom]], + ['i'] = [[asm]], + ['xproc'] = [[xml]], + ['ndproj'] = [[xml]], + ['less'] = [[less]], + ['lgt'] = [[logtalk]], + ['sql'] = [[plsql]], + ['cls'] = [[tex]], + ['targets'] = [[xml]], + ['me'] = [[groff]], + ['3x'] = [[groff]], + ['gtpl'] = [[groovy]], + ['prg'] = [[clipper]], + ['natvis'] = [[xml]], + ['3'] = [[groff]], + ['di'] = [[d]], + ['1x'] = [[groff]], + ['mdoc'] = [[groff]], + ['xsjs'] = [[javascript]], + ['iml'] = [[xml]], + ['ctp'] = [[php]], + ['kml'] = [[xml]], + ['eml'] = [[basic]], + ['raml'] = [[raml]], + ['gml'] = [[xml]], + ['yml'] = [[yaml]], + ['xq'] = [[xquery]], + ['sml'] = [[sml]], + ['sublime-syntax'] = [[yaml]], + ['mm'] = [[xml]], + ['y'] = [[yacc]], + ['mu'] = [[mupad]], + ['sld'] = [[scheme]], + ['asd'] = [[lisp]], + ['pgsql'] = [[sql]], + ['nqp'] = [[perl6]], + ['anim'] = [[yaml]], + ['vhf'] = [[vhdl]], + ['cu'] = [[cuda]], + ['make'] = [[make]], + ['pyx'] = [[pyrex]], + ['c++'] = [[cpp]], + ['lslp'] = [[lsl]], + ['rake'] = [[ruby]], + ['webmanifest'] = [[json]], + ['ijs'] = [[j]], + ['hsc'] = [[haskell]], + ['pl6'] = [[perl6]], + ['p6m'] = [[perl6]], + ['ny'] = [[lisp]], + ['mak'] = [[make]], + ['p6'] = [[perl6]], + ['6pm'] = [[perl6]], + ['lfe'] = [[lisp]], + ['6pl'] = [[perl6]], + ['toc'] = [[tex]], + ['yrl'] = [[erlang]], + ['vssettings'] = [[xml]], + ['sty'] = [[tex]], + ['ipynb'] = [[json]], + ['mkvi'] = [[tex]], + ['p'] = [[gnuplot]], + ['lmi'] = [[python]], + ['rockspec'] = [[lua]], + ['vhd'] = [[vhdl]], + ['sieve'] = [[sieve]], + ['lbx'] = [[tex]], + ['1m'] = [[groff]], + ['jelly'] = [[xml]], + ['3m'] = [[groff]], + ['ins'] = [[tex]], + ['xib'] = [[xml]], + ['cbx'] = [[tex]], + ['pd_lua'] = [[lua]], + ['dae'] = [[xml]], + ['emacs.desktop'] = [[lisp]], + ['bison'] = [[yacc]], + ['matlab'] = [[matlab]], + ['emacs'] = [[lisp]], + ['pot'] = [[po]], + ['el'] = [[lisp]], + ['podsl'] = [[lisp]], + ['pac'] = [[javascript]], + ['gitignore'] = [[gitignore]], + ['vue'] = [[vue]], + ['html.hl'] = [[html]], + ['trg'] = [[plsql]], + ['tps'] = [[plsql]], + ['hs-boot'] = [[haskell]], + ['csh'] = [[tcsh]], + ['mawk'] = [[awk]], + ['fan'] = [[fan]], + ['pike'] = [[pike]], + ['story'] = [[cucumber]], + ['plsql'] = [[plsql]], + ['app.src'] = [[erlang]], + ['mkd'] = [[markdown]], + ['ksh'] = [[sh]], + ['inl'] = [[cpp]], + ['ml4'] = [[ocaml]], + ['f77'] = [[fortran]], + ['pls'] = [[plsql]], + ['opencl'] = [[c]], + ['proj'] = [[xml]], + ['4'] = [[groff]], + ['wsf'] = [[xml]], + ['ninja'] = [[ninja]], + ['rest'] = [[rst]], + ['tpb'] = [[plsql]], + ['xquery'] = [[xquery]], + ['gitconfig'] = [[gitconfig]], + ['r3'] = [[rebol]], + ['reb'] = [[rebol]], + ['gawk'] = [[awk]], + ['groovy'] = [[groovy]], + ['auk'] = [[awk]], + ['r'] = [[rebol]], + ['cmd'] = [[dosbatch]], + ['awk'] = [[awk]], + ['xslt'] = [[xslt]], + ['ps1xml'] = [[xml]], + ['spc'] = [[plsql]], + ['wixproj'] = [[xml]], + ['upc'] = [[c]], + ['hic'] = [[clojure]], + ['cljx'] = [[clojure]], + ['cljs'] = [[clojure]], + ['json'] = [[json]], + ['cljc'] = [[clojure]], + ['pfa'] = [[postscr]], + ['vhdl'] = [[vhdl]], + ['cl2'] = [[clojure]], + ['nsi'] = [[nsis]], + ['g4'] = [[antlr]], + ['sage'] = [[python]], + ['haml.deface'] = [[haml]], + ['haml'] = [[haml]], + ['rno'] = [[groff]], + ['xrl'] = [[erlang]], + ['escript'] = [[erlang]], + ['pks'] = [[plsql]], + ['grxml'] = [[xml]], + ['erl'] = [[erlang]], + ['chem'] = [[pic]], + ['eb'] = [[python]], + ['patch'] = [[diff]], + ['diff'] = [[diff]], + ['mspec'] = [[ruby]], + ['xsp-config'] = [[xml]], + ['gv'] = [[dot]], + ['adp'] = [[tcl]], + ['webapp'] = [[json]], + ['epsi'] = [[postscr]], + ['dot'] = [[dot]], + ['phtml'] = [[php]], + ['lid'] = [[dylan]], + ['cpy'] = [[cobol]], + ['mkdn'] = [[markdown]], + ['ccp'] = [[cobol]], + ['cob'] = [[cobol]], + ['glf'] = [[tcl]], + ['sass'] = [[sass]], + ['gnu'] = [[gnuplot]], + ['pyp'] = [[python]], + ['gsp'] = [[gsp]], + ['asn1'] = [[asn]], + ['sig'] = [[sml]], + ['t'] = [[perl]], + ['xlf'] = [[xml]], + ['perl'] = [[perl]], + ['rpy'] = [[python]], + ['jbuilder'] = [[ruby]], + ['mdwn'] = [[markdown]], + ['dfm'] = [[pascal]], + ['c'] = [[c]], + ['pyw'] = [[python]], + ['mkfile'] = [[make]], + ['py3'] = [[python]], + ['gypi'] = [[python]], + ['py'] = [[python]], + ['watchr'] = [[ruby]], + ['thor'] = [[ruby]], + ['ruby'] = [[ruby]], + ['ru'] = [[ruby]], + ['gltf'] = [[json]], + ['rbx'] = [[ruby]], + ['rbuild'] = [[ruby]], + ['m4'] = [[m4]], + ['god'] = [[ruby]], + ['har'] = [[json]], + ['sas'] = [[sas]], + ['mkii'] = [[tex]], + ['frt'] = [[forth]], + ['icl'] = [[clean]], + ['mirah'] = [[ruby]], + ['wxi'] = [[xml]], + ['lektorproject'] = [[dosini]], + ['nasm'] = [[asm]], + ['njs'] = [[javascript]], + ['vstemplate'] = [[xml]], + ['jsm'] = [[javascript]], + ['html'] = [[html]], + ['xpm'] = [[xpm]], + ['tool'] = [[sh]], + ['rdf'] = [[xml]], + ['rd'] = [[r]], + ['dyl'] = [[dylan]], + ['p8'] = [[lua]], + ['prw'] = [[clipper]], + ['es6'] = [[javascript]], + ['gmx'] = [[xml]], + ['pluginspec'] = [[ruby]], + ['gemspec'] = [[ruby]], + ['aux'] = [[tex]], + ['lvlib'] = [[xml]], + ['cats'] = [[c]], + ['fcgi'] = [[perl]], + ['for'] = [[forth]], + ['scxml'] = [[xml]], + ['mdown'] = [[markdown]], + ['scala'] = [[scala]], + ['pxd'] = [[pyrex]], + ['mxml'] = [[xml]], + ['chs'] = [[haskell]], + ['syntax'] = [[yaml]], + ['5'] = [[groff]], + ['xliff'] = [[xml]], + ['pyt'] = [[python]], + ['view.lkml'] = [[yaml]], + ['pat'] = [[json]], + ['bash'] = [[sh]], + ['tfstate'] = [[json]], + ['vmb'] = [[vim]], + ['prolog'] = [[prolog]], + ['asciidoc'] = [[asciidoc]], + ['asset'] = [[yaml]], + ['mxt'] = [[json]], + ['rviz'] = [[yaml]], + ['yaml.sed'] = [[yaml]], + ['inc'] = [[php]], + ['props'] = [[xml]], + ['psgi'] = [[perl]], + ['axml'] = [[xml]], + ['pxi'] = [[pyrex]], + ['admx'] = [[xml]], + ['nse'] = [[lua]], + ['wxs'] = [[xml]], + ['ice'] = [[json]], + ['builder'] = [[ruby]], + ['jsp'] = [[jsp]], + ['eliom'] = [[ocaml]], + ['go'] = [[go]], + ['ihlp'] = [[stata]], + ['scss'] = [[scss]], + ['sce'] = [[scilab]], + ['lsp'] = [[lisp]], + ['x3d'] = [[xml]], + ['rs'] = [[rust]], + ['php'] = [[php]], + ['htm'] = [[html]], + ['pprx'] = [[rexx]], + ['es'] = [[erlang]], + ['js'] = [[javascript]], + ['ts'] = [[typescript]], + ['uno'] = [[cs]], + ['sch'] = [[scheme]], + ['lvproj'] = [[xml]], + ['xs'] = [[xs]], + ['pro'] = [[idl]], + ['dotsettings'] = [[xml]], + ['res'] = [[xml]], + ['xmi'] = [[xml]], + ['ahkl'] = [[autohotkey]], + ['plt'] = [[gnuplot]], + ['wlt'] = [[mma]], + ['lpr'] = [[pascal]], + ['dof'] = [[dosini]], + ['fs'] = [[forth]], + ['sh.in'] = [[sh]], + ['bat'] = [[dosbatch]], + ['roff'] = [[groff]], + ['depproj'] = [[xml]], + ['mdx'] = [[markdown]], + ['hs'] = [[haskell]], + ['frag'] = [[javascript]], + ['ps'] = [[postscr]], + ['9'] = [[groff]], + ['eps'] = [[postscr]], + ['ck'] = [[java]], + ['rst'] = [[rst]], + ['css'] = [[css]], + ['aw'] = [[php]], + ['veo'] = [[verilog]], + ['adml'] = [[xml]], + ['mll'] = [[ocaml]], + ['tst'] = [[scilab]], + ['svg'] = [[svg]], + ['bdy'] = [[plsql]], + ['xql'] = [[xquery]], + ['xqm'] = [[xquery]], + ['pm'] = [[perl]], + ['texinfo'] = [[texinfo]], + ['prc'] = [[plsql]], + ['3in'] = [[groff]], + ['rbxs'] = [[lua]], + ['xproj'] = [[xml]], + ['bdf'] = [[bdf]], + ['ampl'] = [[ampl]], + ['d'] = [[d]], + ['fnc'] = [[plsql]], + ['cobol'] = [[cobol]], + ['txt'] = [[text]], + ['vim'] = [[vim]], + ['mata'] = [[stata]], + ['linq'] = [[cs]], + ['launch'] = [[xml]], + ['sv'] = [[systemverilog]], + ['nlogo'] = [[lisp]], + ['sci'] = [[scilab]], + ['dockerfile'] = [[dockerfile]], + ['vht'] = [[vhdl]], + ['sed'] = [[sed]], + ['xht'] = [[html]], + ['liquid'] = [[liquid]], + ['latte'] = [[latte]], + ['vhost'] = [[apache]], + ['matah'] = [[stata]], + ['ronn'] = [[markdown]], + ['tpl'] = [[smarty]], + ['xhtml'] = [[html]], + ['bones'] = [[javascript]], + ['bats'] = [[sh]], + ['xpl'] = [[xml]], + ['shproj'] = [[xml]], + ['tfstate.backup'] = [[json]], + ['bzl'] = [[bzl]], + ['cpp'] = [[cpp]], + ['mod'] = [[ampl]], + ['idc'] = [[c]], + ['hh'] = [[cpp]], + ['druby'] = [[ruby]], + ['apacheconf'] = [[apache]], + ['l'] = [[lex]], + ['md'] = [[markdown]], + ['vxml'] = [[xml]], + ['pmod'] = [[pike]], + ['wsdl'] = [[xml]], + ['mtml'] = [[basic]], + ['vh'] = [[systemverilog]], + ['ddl'] = [[plsql]], + ['topojson'] = [[json]], + ['mysql'] = [[sql]], + ['kojo'] = [[scala]], + ['rsx'] = [[r]], + ['tml'] = [[xml]], + ['dcl'] = [[clean]], + ['reek'] = [[yaml]], + ['yaml'] = [[yaml]], + ['desktop'] = [[desktop]], + ['tsx'] = [[xml]], + }, + file_name = { + ['.classpath'] = [[xml]], + ['bsdmakefile'] = [[make]], + ['delete.me'] = [[text]], + ['packages.config'] = [[xml]], + ['jenkinsfile'] = [[groovy]], + ['ant.xml'] = [[ant]], + ['makefile.frag'] = [[make]], + ['puppetfile'] = [[ruby]], + ['.inputrc'] = [[readline]], + ['.zshrc'] = [[sh]], + ['inputrc'] = [[readline]], + ['gnumakefile'] = [[make]], + ['makefile.sco'] = [[make]], + ['pkgbuild'] = [[sh]], + ['.babelignore'] = [[gitignore]], + ['.bash_logout'] = [[sh]], + ['.nanorc'] = [[nanorc]], + ['berksfile'] = [[ruby]], + ['lexer.x'] = [[lex]], + ['sconscript'] = [[python]], + ['makefile.boot'] = [[make]], + ['starfield'] = [[tcl]], + ['.login'] = [[sh]], + ['composer.lock'] = [[json]], + ['dir_colors'] = [[dircolors]], + ['.nvimrc'] = [[vim]], + ['.project'] = [[xml]], + ['web.config'] = [[xml]], + ['makefile.in'] = [[make]], + ['readme.me'] = [[text]], + ['.cproject'] = [[xml]], + ['nuget.config'] = [[xml]], + ['zlogin'] = [[sh]], + ['phakefile'] = [[php]], + ['brewfile'] = [[ruby]], + ['podfile'] = [[ruby]], + ['use.stable.mask'] = [[text]], + ['.gemrc'] = [[yaml]], + ['emakefile'] = [[erlang]], + ['notebook'] = [[json]], + ['read.me'] = [[text]], + ['.dircolors'] = [[dircolors]], + ['.gitmodules'] = [[gitconfig]], + ['owh'] = [[tcl]], + ['zlogout'] = [[sh]], + ['package.use.stable.mask'] = [[text]], + ['httpd.conf'] = [[apache]], + ['buildozer.spec'] = [[dosini]], + ['.zshenv'] = [[sh]], + ['workspace'] = [[bzl]], + ['readme.1st'] = [[text]], + ['.simplecov'] = [[ruby]], + ['.vimrc'] = [[vim]], + ['.htmlhintrc'] = [[json]], + ['mmn'] = [[groff]], + ['rexfile'] = [[perl]], + ['m3overrides'] = [[quake]], + ['.arcconfig'] = [[json]], + ['.htaccess'] = [[apache]], + ['.php_cs'] = [[php]], + ['tiltfile'] = [[bzl]], + ['wscript'] = [[python]], + ['.stylelintignore'] = [[gitignore]], + ['gvimrc'] = [[vim]], + ['.tern-config'] = [[json]], + ['_dir_colors'] = [[dircolors]], + ['app.config'] = [[xml]], + ['abbrev_defs'] = [[lisp]], + ['_emacs'] = [[lisp]], + ['buck'] = [[bzl]], + ['dockerfile'] = [[dockerfile]], + ['project.ede'] = [[lisp]], + ['thorfile'] = [[ruby]], + ['rakefile'] = [[ruby]], + ['.viper'] = [[lisp]], + ['.spacemacs'] = [[lisp]], + ['.gnus'] = [[lisp]], + ['snapfile'] = [[ruby]], + ['eqnrc'] = [[groff]], + ['_dircolors'] = [[dircolors]], + ['configure.ac'] = [[m4]], + ['cabal.project'] = [[cabal]], + ['.env'] = [[sh]], + ['.luacheckrc'] = [[lua]], + ['9fs'] = [[sh]], + ['cabal.config'] = [[cabal]], + ['.bash_aliases'] = [[sh]], + ['install.mysql'] = [[text]], + ['yarn.lock'] = [[yaml]], + ['gitignore_global'] = [[gitignore]], + ['.cshrc'] = [[sh]], + ['.bash_history'] = [[sh]], + ['.env.example'] = [[sh]], + ['.npmignore'] = [[gitignore]], + ['gitignore-global'] = [[gitignore]], + ['build.bazel'] = [[bzl]], + ['.flaskenv'] = [[sh]], + ['license'] = [[text]], + ['mavenfile'] = [[ruby]], + ['.vscodeignore'] = [[gitignore]], + ['.abbrev_defs'] = [[lisp]], + ['gemfile.lock'] = [[ruby]], + ['go.mod'] = [[text]], + ['.gvimrc'] = [[vim]], + ['.nodemonignore'] = [[gitignore]], + ['bashrc'] = [[sh]], + ['man'] = [[sh]], + ['.dockerignore'] = [[gitignore]], + ['makefile.am'] = [[make]], + ['.zlogin'] = [[sh]], + ['.cvsignore'] = [[gitignore]], + ['.coffeelintignore'] = [[gitignore]], + ['.php'] = [[php]], + ['expr-dist'] = [[r]], + ['zshrc'] = [[sh]], + ['.clang-format'] = [[yaml]], + ['bash_aliases'] = [[sh]], + ['.exrc'] = [[vim]], + ['web.release.config'] = [[xml]], + ['.atomignore'] = [[gitignore]], + ['makefile.wat'] = [[make]], + ['troffrc-end'] = [[groff]], + ['vimrc'] = [[vim]], + ['cmakelists.txt'] = [[cmake]], + ['.gitconfig'] = [[gitconfig]], + ['riemann.config'] = [[clojure]], + ['zprofile'] = [[sh]], + ['rebar.lock'] = [[erlang]], + ['news'] = [[text]], + ['rebar.config'] = [[erlang]], + ['mcmod.info'] = [[json]], + ['cpanfile'] = [[perl]], + ['readme.mysql'] = [[text]], + ['makefile.pl'] = [[perl]], + ['dangerfile'] = [[ruby]], + ['snakefile'] = [[python]], + ['contents.lr'] = [[markdown]], + ['sconstruct'] = [[python]], + ['glide.lock'] = [[yaml]], + ['deps'] = [[python]], + ['.zprofile'] = [[sh]], + ['.gclient'] = [[python]], + ['keep.me'] = [[text]], + ['troffrc'] = [[groff]], + ['m3makefile'] = [[quake]], + ['cshrc'] = [[sh]], + ['.emacs.desktop'] = [[lisp]], + ['.zlogout'] = [[sh]], + ['.rprofile'] = [[r]], + ['cask'] = [[lisp]], + ['jarfile'] = [[ruby]], + ['deliverfile'] = [[ruby]], + ['copyright.regex'] = [[text]], + ['.prettierignore'] = [[gitignore]], + ['gemfile'] = [[ruby]], + ['profile'] = [[sh]], + ['fastfile'] = [[ruby]], + ['guardfile'] = [[ruby]], + ['capfile'] = [[ruby]], + ['buildfile'] = [[ruby]], + ['jakefile'] = [[javascript]], + ['appraisals'] = [[ruby]], + ['fontlog'] = [[text]], + ['package.use.mask'] = [[text]], + ['.pryrc'] = [[ruby]], + ['.emacs'] = [[lisp]], + ['.watchmanconfig'] = [[json]], + ['.irbrc'] = [[ruby]], + ['.tern-project'] = [[json]], + ['web.debug.config'] = [[xml]], + ['.eslintignore'] = [[gitignore]], + ['.gitignore'] = [[gitignore]], + ['copying.regex'] = [[text]], + ['build'] = [[bzl]], + ['nvimrc'] = [[vim]], + ['.dir_colors'] = [[dircolors]], + ['.bashrc'] = [[sh]], + ['kbuild'] = [[make]], + ['.profile'] = [[sh]], + ['.php_cs.dist'] = [[php]], + ['gradlew'] = [[sh]], + ['settings.stylecop'] = [[xml]], + ['makefile.inc'] = [[make]], + ['mkfile'] = [[make]], + ['.bzrignore'] = [[gitignore]], + ['ack'] = [[perl]], + ['license.mysql'] = [[text]], + ['makefile'] = [[make]], + ['vagrantfile'] = [[ruby]], + ['nanorc'] = [[nanorc]], + ['.bash_profile'] = [[sh]], + ['bash_logout'] = [[sh]], + ['bash_profile'] = [[sh]], + ['click.me'] = [[text]], + ['login'] = [[sh]], + ['_vimrc'] = [[vim]], + ['go.sum'] = [[text]], + ['apache2.conf'] = [[apache]], + ['use.mask'] = [[text]], + ['build.xml'] = [[ant]], + ['mmt'] = [[groff]], + ['zshenv'] = [[sh]], + ['copying'] = [[text]], + ['install'] = [[text]], + ['test.me'] = [[text]], + ['package.mask'] = [[text]], + ['.clang-tidy'] = [[yaml]], + ['readme.nss'] = [[text]], + ['rebar.config.lock'] = [[erlang]], + }, +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/data/plenary/filetypes/builtin.lua b/config/neovim/store/lazy-plugins/plenary.nvim/data/plenary/filetypes/builtin.lua new file mode 100644 index 00000000..c4b6d507 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/data/plenary/filetypes/builtin.lua @@ -0,0 +1,71 @@ +local shebang_prefixes = { '/usr/bin/', '/bin/', '/usr/bin/env ', '/bin/env ' } +local shebang_fts = { + ['fish'] = 'fish', + ['perl'] = 'perl', + ['python'] = 'python', + ['python2'] = 'python', + ['python3'] = 'python', + ['bash'] = 'sh', + ['sh'] = 'sh', + ['zsh'] = 'zsh', +} + +local shebang = {} +for _, prefix in ipairs(shebang_prefixes) do + for k, v in pairs(shebang_fts) do + shebang[prefix .. k] = v + end +end + +return { + extension = { + ['_coffee'] = 'coffee', + ['astro'] = 'astro', + ['cts'] = 'typescript', + ['cljd'] = 'clojure', + ['coffee'] = 'coffee', + ['dart'] = 'dart', + ['erb'] = 'eruby', + ['ex'] = 'elixir', + ['exs'] = 'elixir', + ['fish'] = 'fish', + ['fnl'] = 'fennel', + ['gd'] = 'gdscript', + ['gql'] = 'graphql', + ['gradle'] = 'groovy', + ['graphql'] = 'graphql', + ['hbs'] = 'handlebars', + ['hdbs'] = 'handlebars', + ['hlsl'] = 'hlsl', + ['jai'] = 'jai', + ['janet'] = 'janet', + ['jl'] = 'julia', + ['jsx'] = 'javascriptreact', + ['kt'] = 'kotlin', + ['mts'] = 'typescript', + ['nix'] = 'nix', + ['plist'] = 'xml', + ['purs'] = 'purescript', + ['r'] = 'r', + ['res'] = 'rescript', + ['resi'] = 'rescript', + ['rkt'] = 'racket', + ['svelte'] = 'svelte', + ['tres'] = 'gdresource', + ['tscn'] = 'gdresource', + ['tsx'] = 'typescriptreact', + ['smithy'] = [[smithy]], + ['sol'] = 'solidity', + ['dtsi'] = 'dts', + }, + file_name = { + ['cakefile'] = 'coffee', + ['.babelrc'] = 'json', + ['.clangd'] = 'yaml', + ['.eslintrc'] = 'json', + ['.firebaserc'] = 'json', + ['.prettierrc'] = 'json', + ['.stylelintrc'] = 'json', + }, + shebang = shebang +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/array.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/array.lua new file mode 100644 index 00000000..f9cb6557 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/array.lua @@ -0,0 +1,70 @@ +local assert = require('luassert.assert') +local say = require('say') + +-- Example usage: +-- local arr = { "one", "two", "three" } +-- +-- assert.array(arr).has.no.holes() -- checks the array to not contain holes --> passes +-- assert.array(arr).has.no.holes(4) -- sets explicit length to 4 --> fails +-- +-- local first_hole = assert.array(arr).has.holes(4) -- check array of size 4 to contain holes --> passes +-- assert.equal(4, first_hole) -- passes, as the index of the first hole is returned + + +-- Unique key to store the object we operate on in the state object +-- key must be unique, to make sure we do not have name collissions in the shared state object +local ARRAY_STATE_KEY = "__array_state" + +-- The modifier, to store the object in our state +local function array(state, args, level) + assert(args.n > 0, "No array provided to the array-modifier") + assert(rawget(state, ARRAY_STATE_KEY) == nil, "Array already set") + rawset(state, ARRAY_STATE_KEY, args[1]) + return state +end + +-- The actual assertion that operates on our object, stored via the modifier +local function holes(state, args, level) + local length = args[1] + local arr = rawget(state, ARRAY_STATE_KEY) -- retrieve previously set object + -- only check against nil, metatable types are allowed + assert(arr ~= nil, "No array set, please use the array modifier to set the array to validate") + if length == nil then + length = 0 + for i in pairs(arr) do + if type(i) == "number" and + i > length and + math.floor(i) == i then + length = i + end + end + end + assert(type(length) == "number", "expected array length to be of type 'number', got: "..tostring(length)) + -- let's do the actual assertion + local missing + for i = 1, length do + if arr[i] == nil then + missing = i + break + end + end + -- format arguments for output strings; + args[1] = missing + args.n = missing and 1 or 0 + return missing ~= nil, { missing } -- assert result + first missing index as return value +end + +-- Register the proper assertion messages +say:set("assertion.array_holes.positive", [[ +Expected array to have holes, but none was found. +]]) +say:set("assertion.array_holes.negative", [[ +Expected array to not have holes, hole found at position: %s +]]) + +-- Register the assertion, and the modifier +assert:register("assertion", "holes", holes, + "assertion.array_holes.positive", + "assertion.array_holes.negative") + +assert:register("modifier", "array", array) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/assert.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/assert.lua new file mode 100644 index 00000000..7fe7569f --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/assert.lua @@ -0,0 +1,180 @@ +local s = require 'say' +local astate = require 'luassert.state' +local util = require 'luassert.util' +local unpack = util.unpack +local obj -- the returned module table +local level_mt = {} + +-- list of namespaces +local namespace = require 'luassert.namespaces' + +local function geterror(assertion_message, failure_message, args) + if util.hastostring(failure_message) then + failure_message = tostring(failure_message) + elseif failure_message ~= nil then + failure_message = astate.format_argument(failure_message) + end + local message = s(assertion_message, obj:format(args)) + if message and failure_message then + message = failure_message .. "\n" .. message + end + return message or failure_message +end + +local __state_meta = { + + __call = function(self, ...) + local keys = util.extract_keys("assertion", self.tokens) + + local assertion + + for _, key in ipairs(keys) do + assertion = namespace.assertion[key] or assertion + end + + if assertion then + for _, key in ipairs(keys) do + if namespace.modifier[key] then + namespace.modifier[key].callback(self) + end + end + + local arguments = util.make_arglist(...) + local val, retargs = assertion.callback(self, arguments, util.errorlevel()) + + if (not val) == self.mod then + local message = assertion.positive_message + if not self.mod then + message = assertion.negative_message + end + local err = geterror(message, rawget(self,"failure_message"), arguments) + error(err or "assertion failed!", util.errorlevel()) + end + + if retargs then + return unpack(retargs) + end + return ... + else + local arguments = util.make_arglist(...) + self.tokens = {} + + for _, key in ipairs(keys) do + if namespace.modifier[key] then + namespace.modifier[key].callback(self, arguments, util.errorlevel()) + end + end + end + + return self + end, + + __index = function(self, key) + for token in key:lower():gmatch('[^_]+') do + table.insert(self.tokens, token) + end + + return self + end +} + +obj = { + state = function() return setmetatable({mod=true, tokens={}}, __state_meta) end, + + -- registers a function in namespace + register = function(self, nspace, name, callback, positive_message, negative_message) + local lowername = name:lower() + if not namespace[nspace] then + namespace[nspace] = {} + end + namespace[nspace][lowername] = { + callback = callback, + name = lowername, + positive_message=positive_message, + negative_message=negative_message + } + end, + + -- unregisters a function in a namespace + unregister = function(self, nspace, name) + local lowername = name:lower() + if not namespace[nspace] then + namespace[nspace] = {} + end + namespace[nspace][lowername] = nil + end, + + -- registers a formatter + -- a formatter takes a single argument, and converts it to a string, or returns nil if it cannot format the argument + add_formatter = function(self, callback) + astate.add_formatter(callback) + end, + + -- unregisters a formatter + remove_formatter = function(self, fmtr) + astate.remove_formatter(fmtr) + end, + + format = function(self, args) + -- args.n specifies the number of arguments in case of 'trailing nil' arguments which get lost + local nofmt = args.nofmt or {} -- arguments in this list should not be formatted + local fmtargs = args.fmtargs or {} -- additional arguments to be passed to formatter + for i = 1, (args.n or #args) do -- cannot use pairs because table might have nils + if not nofmt[i] then + local val = args[i] + local valfmt = astate.format_argument(val, nil, fmtargs[i]) + if valfmt == nil then valfmt = tostring(val) end -- no formatter found + args[i] = valfmt + end + end + return args + end, + + set_parameter = function(self, name, value) + astate.set_parameter(name, value) + end, + + get_parameter = function(self, name) + return astate.get_parameter(name) + end, + + add_spy = function(self, spy) + astate.add_spy(spy) + end, + + snapshot = function(self) + return astate.snapshot() + end, + + level = function(self, level) + return setmetatable({ + level = level + }, level_mt) + end, + + -- returns the level if a level-value, otherwise nil + get_level = function(self, level) + if getmetatable(level) ~= level_mt then + return nil -- not a valid error-level + end + return level.level + end, +} + +local __meta = { + + __call = function(self, bool, message, level, ...) + if not bool then + local err_level = (self:get_level(level) or 1) + 1 + error(message or "assertion failed!", err_level) + end + return bool , message , level , ... + end, + + __index = function(self, key) + return rawget(self, key) or self.state()[key] + end, + +} + +return setmetatable(obj, __meta) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/assertions.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/assertions.lua new file mode 100644 index 00000000..e5083c3d --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/assertions.lua @@ -0,0 +1,334 @@ +-- module will not return anything, only register assertions with the main assert engine + +-- assertions take 2 parameters; +-- 1) state +-- 2) arguments list. The list has a member 'n' with the argument count to check for trailing nils +-- 3) level The level of the error position relative to the called function +-- returns; boolean; whether assertion passed + +local assert = require('luassert.assert') +local astate = require ('luassert.state') +local util = require ('luassert.util') +local s = require('say') + +local function format(val) + return astate.format_argument(val) or tostring(val) +end + +local function set_failure_message(state, message) + if message ~= nil then + state.failure_message = message + end +end + +local function unique(state, arguments, level) + local list = arguments[1] + local deep + local argcnt = arguments.n + if type(arguments[2]) == "boolean" or (arguments[2] == nil and argcnt > 2) then + deep = arguments[2] + set_failure_message(state, arguments[3]) + else + if type(arguments[3]) == "boolean" then + deep = arguments[3] + end + set_failure_message(state, arguments[2]) + end + for k,v in pairs(list) do + for k2, v2 in pairs(list) do + if k ~= k2 then + if deep and util.deepcompare(v, v2, true) then + return false + else + if v == v2 then + return false + end + end + end + end + end + return true +end + +local function near(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 2, s("assertion.internal.argtolittle", { "near", 3, tostring(argcnt) }), level) + local expected = tonumber(arguments[1]) + local actual = tonumber(arguments[2]) + local tolerance = tonumber(arguments[3]) + local numbertype = "number or object convertible to a number" + assert(expected, s("assertion.internal.badargtype", { 1, "near", numbertype, format(arguments[1]) }), level) + assert(actual, s("assertion.internal.badargtype", { 2, "near", numbertype, format(arguments[2]) }), level) + assert(tolerance, s("assertion.internal.badargtype", { 3, "near", numbertype, format(arguments[3]) }), level) + -- switch arguments for proper output message + util.tinsert(arguments, 1, util.tremove(arguments, 2)) + arguments[3] = tolerance + arguments.nofmt = arguments.nofmt or {} + arguments.nofmt[3] = true + set_failure_message(state, arguments[4]) + return (actual >= expected - tolerance and actual <= expected + tolerance) +end + +local function matches(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 1, s("assertion.internal.argtolittle", { "matches", 2, tostring(argcnt) }), level) + local pattern = arguments[1] + local actual = nil + if util.hastostring(arguments[2]) or type(arguments[2]) == "number" then + actual = tostring(arguments[2]) + end + local err_message + local init_arg_num = 3 + for i=3,argcnt,1 do + if arguments[i] and type(arguments[i]) ~= "boolean" and not tonumber(arguments[i]) then + if i == 3 then init_arg_num = init_arg_num + 1 end + err_message = util.tremove(arguments, i) + break + end + end + local init = arguments[3] + local plain = arguments[4] + local stringtype = "string or object convertible to a string" + assert(type(pattern) == "string", s("assertion.internal.badargtype", { 1, "matches", "string", type(arguments[1]) }), level) + assert(actual, s("assertion.internal.badargtype", { 2, "matches", stringtype, format(arguments[2]) }), level) + assert(init == nil or tonumber(init), s("assertion.internal.badargtype", { init_arg_num, "matches", "number", type(arguments[3]) }), level) + -- switch arguments for proper output message + util.tinsert(arguments, 1, util.tremove(arguments, 2)) + set_failure_message(state, err_message) + local retargs + local ok + if plain then + ok = (actual:find(pattern, init, plain) ~= nil) + retargs = ok and { pattern } or {} + else + retargs = { actual:match(pattern, init) } + ok = (retargs[1] ~= nil) + end + return ok, retargs +end + +local function equals(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 1, s("assertion.internal.argtolittle", { "equals", 2, tostring(argcnt) }), level) + local result = arguments[1] == arguments[2] + -- switch arguments for proper output message + util.tinsert(arguments, 1, util.tremove(arguments, 2)) + set_failure_message(state, arguments[3]) + return result +end + +local function same(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 1, s("assertion.internal.argtolittle", { "same", 2, tostring(argcnt) }), level) + if type(arguments[1]) == 'table' and type(arguments[2]) == 'table' then + local result, crumbs = util.deepcompare(arguments[1], arguments[2], true) + -- switch arguments for proper output message + util.tinsert(arguments, 1, util.tremove(arguments, 2)) + arguments.fmtargs = arguments.fmtargs or {} + arguments.fmtargs[1] = { crumbs = crumbs } + arguments.fmtargs[2] = { crumbs = crumbs } + set_failure_message(state, arguments[3]) + return result + end + local result = arguments[1] == arguments[2] + -- switch arguments for proper output message + util.tinsert(arguments, 1, util.tremove(arguments, 2)) + set_failure_message(state, arguments[3]) + return result +end + +local function truthy(state, arguments, level) + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "truthy", 1, tostring(argcnt) }), level) + set_failure_message(state, arguments[2]) + return arguments[1] ~= false and arguments[1] ~= nil +end + +local function falsy(state, arguments, level) + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "falsy", 1, tostring(argcnt) }), level) + return not truthy(state, arguments, level) +end + +local function has_error(state, arguments, level) + local level = (level or 1) + 1 + local retargs = util.shallowcopy(arguments) + local func = arguments[1] + local err_expected = arguments[2] + local failure_message = arguments[3] + assert(util.callable(func), s("assertion.internal.badargtype", { 1, "error", "function or callable object", type(func) }), level) + local ok, err_actual = pcall(func) + if type(err_actual) == 'string' then + -- remove 'path/to/file:line: ' from string + err_actual = err_actual:gsub('^.-:%d+: ', '', 1) + end + retargs[1] = err_actual + arguments.nofmt = {} + arguments.n = 2 + arguments[1] = (ok and '(no error)' or err_actual) + arguments[2] = (err_expected == nil and '(error)' or err_expected) + arguments.nofmt[1] = ok + arguments.nofmt[2] = (err_expected == nil) + set_failure_message(state, failure_message) + + if ok or err_expected == nil then + return not ok, retargs + end + if type(err_expected) == 'string' then + -- err_actual must be (convertible to) a string + if util.hastostring(err_actual) then + err_actual = tostring(err_actual) + retargs[1] = err_actual + end + if type(err_actual) == 'string' then + return err_expected == err_actual, retargs + end + elseif type(err_expected) == 'number' then + if type(err_actual) == 'string' then + return tostring(err_expected) == tostring(tonumber(err_actual)), retargs + end + end + return same(state, {err_expected, err_actual, ["n"] = 2}), retargs +end + +local function error_matches(state, arguments, level) + local level = (level or 1) + 1 + local retargs = util.shallowcopy(arguments) + local argcnt = arguments.n + local func = arguments[1] + local pattern = arguments[2] + assert(argcnt > 1, s("assertion.internal.argtolittle", { "error_matches", 2, tostring(argcnt) }), level) + assert(util.callable(func), s("assertion.internal.badargtype", { 1, "error_matches", "function or callable object", type(func) }), level) + assert(pattern == nil or type(pattern) == "string", s("assertion.internal.badargtype", { 2, "error", "string", type(pattern) }), level) + + local failure_message + local init_arg_num = 3 + for i=3,argcnt,1 do + if arguments[i] and type(arguments[i]) ~= "boolean" and not tonumber(arguments[i]) then + if i == 3 then init_arg_num = init_arg_num + 1 end + failure_message = util.tremove(arguments, i) + break + end + end + local init = arguments[3] + local plain = arguments[4] + assert(init == nil or tonumber(init), s("assertion.internal.badargtype", { init_arg_num, "matches", "number", type(arguments[3]) }), level) + + local ok, err_actual = pcall(func) + if type(err_actual) == 'string' then + -- remove 'path/to/file:line: ' from string + err_actual = err_actual:gsub('^.-:%d+: ', '', 1) + end + retargs[1] = err_actual + arguments.nofmt = {} + arguments.n = 2 + arguments[1] = (ok and '(no error)' or err_actual) + arguments[2] = pattern + arguments.nofmt[1] = ok + arguments.nofmt[2] = false + set_failure_message(state, failure_message) + + if ok then return not ok, retargs end + if err_actual == nil and pattern == nil then + return true, {} + end + + -- err_actual must be (convertible to) a string + if util.hastostring(err_actual) or + type(err_actual) == "number" or + type(err_actual) == "boolean" then + err_actual = tostring(err_actual) + retargs[1] = err_actual + end + if type(err_actual) == 'string' then + local ok + local retargs_ok + if plain then + retargs_ok = { pattern } + ok = (err_actual:find(pattern, init, plain) ~= nil) + else + retargs_ok = { err_actual:match(pattern, init) } + ok = (retargs_ok[1] ~= nil) + end + if ok then retargs = retargs_ok end + return ok, retargs + end + + return false, retargs +end + +local function is_true(state, arguments, level) + util.tinsert(arguments, 2, true) + set_failure_message(state, arguments[3]) + return arguments[1] == arguments[2] +end + +local function is_false(state, arguments, level) + util.tinsert(arguments, 2, false) + set_failure_message(state, arguments[3]) + return arguments[1] == arguments[2] +end + +local function is_type(state, arguments, level, etype) + util.tinsert(arguments, 2, "type " .. etype) + arguments.nofmt = arguments.nofmt or {} + arguments.nofmt[2] = true + set_failure_message(state, arguments[3]) + return arguments.n > 1 and type(arguments[1]) == etype +end + +local function returned_arguments(state, arguments, level) + arguments[1] = tostring(arguments[1]) + arguments[2] = tostring(arguments.n - 1) + arguments.nofmt = arguments.nofmt or {} + arguments.nofmt[1] = true + arguments.nofmt[2] = true + if arguments.n < 2 then arguments.n = 2 end + return arguments[1] == arguments[2] +end + +local function set_message(state, arguments, level) + state.failure_message = arguments[1] +end + +local function is_boolean(state, arguments, level) return is_type(state, arguments, level, "boolean") end +local function is_number(state, arguments, level) return is_type(state, arguments, level, "number") end +local function is_string(state, arguments, level) return is_type(state, arguments, level, "string") end +local function is_table(state, arguments, level) return is_type(state, arguments, level, "table") end +local function is_nil(state, arguments, level) return is_type(state, arguments, level, "nil") end +local function is_userdata(state, arguments, level) return is_type(state, arguments, level, "userdata") end +local function is_function(state, arguments, level) return is_type(state, arguments, level, "function") end +local function is_thread(state, arguments, level) return is_type(state, arguments, level, "thread") end + +assert:register("modifier", "message", set_message) +assert:register("assertion", "true", is_true, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "false", is_false, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "boolean", is_boolean, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "number", is_number, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "string", is_string, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "table", is_table, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "nil", is_nil, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "userdata", is_userdata, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "function", is_function, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "thread", is_thread, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "returned_arguments", returned_arguments, "assertion.returned_arguments.positive", "assertion.returned_arguments.negative") + +assert:register("assertion", "same", same, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "matches", matches, "assertion.matches.positive", "assertion.matches.negative") +assert:register("assertion", "match", matches, "assertion.matches.positive", "assertion.matches.negative") +assert:register("assertion", "near", near, "assertion.near.positive", "assertion.near.negative") +assert:register("assertion", "equals", equals, "assertion.equals.positive", "assertion.equals.negative") +assert:register("assertion", "equal", equals, "assertion.equals.positive", "assertion.equals.negative") +assert:register("assertion", "unique", unique, "assertion.unique.positive", "assertion.unique.negative") +assert:register("assertion", "error", has_error, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "errors", has_error, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "error_matches", error_matches, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "error_match", error_matches, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "matches_error", error_matches, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "match_error", error_matches, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "truthy", truthy, "assertion.truthy.positive", "assertion.truthy.negative") +assert:register("assertion", "falsy", falsy, "assertion.falsy.positive", "assertion.falsy.negative") diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/compatibility.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/compatibility.lua new file mode 100644 index 00000000..88290ad8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/compatibility.lua @@ -0,0 +1,9 @@ +-- no longer needed, only for backward compatibility +local unpack = require ("luassert.util").unpack + +return { + unpack = function(...) + print(debug.traceback("WARN: calling deprecated function 'luassert.compatibility.unpack' use 'luassert.util.unpack' instead")) + return unpack(...) + end +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/formatters/binarystring.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/formatters/binarystring.lua new file mode 100644 index 00000000..02c05ea2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/formatters/binarystring.lua @@ -0,0 +1,28 @@ +local format = function (str) + if type(str) ~= "string" then return nil end + local result = "Binary string length; " .. tostring(#str) .. " bytes\n" + local i = 1 + local hex = "" + local chr = "" + while i <= #str do + local byte = str:byte(i) + hex = string.format("%s%2x ", hex, byte) + if byte < 32 then byte = string.byte(".") end + chr = chr .. string.char(byte) + if math.floor(i/16) == i/16 or i == #str then + -- reached end of line + hex = hex .. string.rep(" ", 16 * 3 - #hex) + chr = chr .. string.rep(" ", 16 - #chr) + + result = result .. hex:sub(1, 8 * 3) .. " " .. hex:sub(8*3+1, -1) .. " " .. chr:sub(1,8) .. " " .. chr:sub(9,-1) .. "\n" + + hex = "" + chr = "" + end + i = i + 1 + end + return result +end + +return format + diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/formatters/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/formatters/init.lua new file mode 100644 index 00000000..0ff67c97 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/formatters/init.lua @@ -0,0 +1,255 @@ +-- module will not return anything, only register formatters with the main assert engine +local assert = require('luassert.assert') +local match = require('luassert.match') +local util = require('luassert.util') + +local isatty, colors do + local ok, term = pcall(require, 'term') + isatty = io.type(io.stdout) == 'file' and ok and term.isatty(io.stdout) + if not isatty then + local isWindows = package.config:sub(1,1) == '\\' + if isWindows and os.getenv("ANSICON") then + isatty = true + end + end + + colors = setmetatable({ + none = function(c) return c end + },{ __index = function(self, key) + return function(c) + for token in key:gmatch("[^%.]+") do + c = term.colors[token](c) + end + return c + end + end + }) +end + +local function fmt_string(arg) + if type(arg) == "string" then + return string.format("(string) '%s'", arg) + end +end + +-- A version of tostring which formats numbers more precisely. +local function tostr(arg) + if type(arg) ~= "number" then + return tostring(arg) + end + + if arg ~= arg then + return "NaN" + elseif arg == 1/0 then + return "Inf" + elseif arg == -1/0 then + return "-Inf" + end + + local str = string.format("%.20g", arg) + + if math.type and math.type(arg) == "float" and not str:find("[%.,]") then + -- Number is a float but looks like an integer. + -- Insert ".0" after first run of digits. + str = str:gsub("%d+", "%0.0", 1) + end + + return str +end + +local function fmt_number(arg) + if type(arg) == "number" then + return string.format("(number) %s", tostr(arg)) + end +end + +local function fmt_boolean(arg) + if type(arg) == "boolean" then + return string.format("(boolean) %s", tostring(arg)) + end +end + +local function fmt_nil(arg) + if type(arg) == "nil" then + return "(nil)" + end +end + +local type_priorities = { + number = 1, + boolean = 2, + string = 3, + table = 4, + ["function"] = 5, + userdata = 6, + thread = 7 +} + +local function is_in_array_part(key, length) + return type(key) == "number" and 1 <= key and key <= length and math.floor(key) == key +end + +local function get_sorted_keys(t) + local keys = {} + local nkeys = 0 + + for key in pairs(t) do + nkeys = nkeys + 1 + keys[nkeys] = key + end + + local length = #t + + local function key_comparator(key1, key2) + local type1, type2 = type(key1), type(key2) + local priority1 = is_in_array_part(key1, length) and 0 or type_priorities[type1] or 8 + local priority2 = is_in_array_part(key2, length) and 0 or type_priorities[type2] or 8 + + if priority1 == priority2 then + if type1 == "string" or type1 == "number" then + return key1 < key2 + elseif type1 == "boolean" then + return key1 -- put true before false + end + else + return priority1 < priority2 + end + end + + table.sort(keys, key_comparator) + return keys, nkeys +end + +local function fmt_table(arg, fmtargs) + if type(arg) ~= "table" then + return + end + + local tmax = assert:get_parameter("TableFormatLevel") + local showrec = assert:get_parameter("TableFormatShowRecursion") + local errchar = assert:get_parameter("TableErrorHighlightCharacter") or "" + local errcolor = assert:get_parameter("TableErrorHighlightColor") + local crumbs = fmtargs and fmtargs.crumbs or {} + local cache = {} + local type_desc + + if getmetatable(arg) == nil then + type_desc = "(" .. tostring(arg) .. ") " + elseif not pcall(setmetatable, arg, getmetatable(arg)) then + -- cannot set same metatable, so it is protected, skip id + type_desc = "(table) " + else + -- unprotected metatable, temporary remove the mt + local mt = getmetatable(arg) + setmetatable(arg, nil) + type_desc = "(" .. tostring(arg) .. ") " + setmetatable(arg, mt) + end + + local function ft(t, l, with_crumbs) + if showrec and cache[t] and cache[t] > 0 then + return "{ ... recursive }" + end + + if next(t) == nil then + return "{ }" + end + + if l > tmax and tmax >= 0 then + return "{ ... more }" + end + + local result = "{" + local keys, nkeys = get_sorted_keys(t) + + cache[t] = (cache[t] or 0) + 1 + local crumb = crumbs[#crumbs - l + 1] + + for i = 1, nkeys do + local k = keys[i] + local v = t[k] + local use_crumbs = with_crumbs and k == crumb + + if type(v) == "table" then + v = ft(v, l + 1, use_crumbs) + elseif type(v) == "string" then + v = "'"..v.."'" + end + + local ch = use_crumbs and errchar or "" + local indent = string.rep(" ",l * 2 - ch:len()) + local mark = (ch:len() == 0 and "" or colors[errcolor](ch)) + result = result .. string.format("\n%s%s[%s] = %s", indent, mark, tostr(k), tostr(v)) + end + + cache[t] = cache[t] - 1 + + return result .. " }" + end + + return type_desc .. ft(arg, 1, true) +end + +local function fmt_function(arg) + if type(arg) == "function" then + local debug_info = debug.getinfo(arg) + return string.format("%s @ line %s in %s", tostring(arg), tostring(debug_info.linedefined), tostring(debug_info.source)) + end +end + +local function fmt_userdata(arg) + if type(arg) == "userdata" then + return string.format("(userdata) '%s'", tostring(arg)) + end +end + +local function fmt_thread(arg) + if type(arg) == "thread" then + return string.format("(thread) '%s'", tostring(arg)) + end +end + +local function fmt_matcher(arg) + if not match.is_matcher(arg) then + return + end + local not_inverted = { + [true] = "is.", + [false] = "no.", + } + local args = {} + for idx = 1, arg.arguments.n do + table.insert(args, assert:format({ arg.arguments[idx], n = 1, })[1]) + end + return string.format("(matcher) %s%s(%s)", + not_inverted[arg.mod], + tostring(arg.name), + table.concat(args, ", ")) +end + +local function fmt_arglist(arglist) + if not util.is_arglist(arglist) then + return + end + local formatted_vals = {} + for idx = 1, arglist.n do + table.insert(formatted_vals, assert:format({ arglist[idx], n = 1, })[1]) + end + return "(values list) (" .. table.concat(formatted_vals, ", ") .. ")" +end + +assert:add_formatter(fmt_string) +assert:add_formatter(fmt_number) +assert:add_formatter(fmt_boolean) +assert:add_formatter(fmt_nil) +assert:add_formatter(fmt_table) +assert:add_formatter(fmt_function) +assert:add_formatter(fmt_userdata) +assert:add_formatter(fmt_thread) +assert:add_formatter(fmt_matcher) +assert:add_formatter(fmt_arglist) +-- Set default table display depth for table formatter +assert:set_parameter("TableFormatLevel", 3) +assert:set_parameter("TableFormatShowRecursion", false) +assert:set_parameter("TableErrorHighlightCharacter", "*") +assert:set_parameter("TableErrorHighlightColor", isatty and "red" or "none") diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/init.lua new file mode 100644 index 00000000..4ecf8b4a --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/init.lua @@ -0,0 +1,17 @@ +local assert = require('luassert.assert') + +assert._COPYRIGHT = "Copyright (c) 2018 Olivine Labs, LLC." +assert._DESCRIPTION = "Extends Lua's built-in assertions to provide additional tests and the ability to create your own." +assert._VERSION = "Luassert 1.8.0" + +-- load basic asserts +require('luassert.assertions') +require('luassert.modifiers') +require('luassert.array') +require('luassert.matchers') +require('luassert.formatters') + +-- load default language +require('luassert.languages.en') + +return assert diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/languages/en.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/languages/en.lua new file mode 100644 index 00000000..0d84b6d4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/languages/en.lua @@ -0,0 +1,48 @@ +local s = require('say') + +s:set_namespace('en') + +s:set("assertion.same.positive", "Expected objects to be the same.\nPassed in:\n%s\nExpected:\n%s") +s:set("assertion.same.negative", "Expected objects to not be the same.\nPassed in:\n%s\nDid not expect:\n%s") + +s:set("assertion.equals.positive", "Expected objects to be equal.\nPassed in:\n%s\nExpected:\n%s") +s:set("assertion.equals.negative", "Expected objects to not be equal.\nPassed in:\n%s\nDid not expect:\n%s") + +s:set("assertion.near.positive", "Expected values to be near.\nPassed in:\n%s\nExpected:\n%s +/- %s") +s:set("assertion.near.negative", "Expected values to not be near.\nPassed in:\n%s\nDid not expect:\n%s +/- %s") + +s:set("assertion.matches.positive", "Expected strings to match.\nPassed in:\n%s\nExpected:\n%s") +s:set("assertion.matches.negative", "Expected strings not to match.\nPassed in:\n%s\nDid not expect:\n%s") + +s:set("assertion.unique.positive", "Expected object to be unique:\n%s") +s:set("assertion.unique.negative", "Expected object to not be unique:\n%s") + +s:set("assertion.error.positive", "Expected a different error.\nCaught:\n%s\nExpected:\n%s") +s:set("assertion.error.negative", "Expected no error, but caught:\n%s") + +s:set("assertion.truthy.positive", "Expected to be truthy, but value was:\n%s") +s:set("assertion.truthy.negative", "Expected to not be truthy, but value was:\n%s") + +s:set("assertion.falsy.positive", "Expected to be falsy, but value was:\n%s") +s:set("assertion.falsy.negative", "Expected to not be falsy, but value was:\n%s") + +s:set("assertion.called.positive", "Expected to be called %s time(s), but was called %s time(s)") +s:set("assertion.called.negative", "Expected not to be called exactly %s time(s), but it was.") + +s:set("assertion.called_at_least.positive", "Expected to be called at least %s time(s), but was called %s time(s)") +s:set("assertion.called_at_most.positive", "Expected to be called at most %s time(s), but was called %s time(s)") +s:set("assertion.called_more_than.positive", "Expected to be called more than %s time(s), but was called %s time(s)") +s:set("assertion.called_less_than.positive", "Expected to be called less than %s time(s), but was called %s time(s)") + +s:set("assertion.called_with.positive", "Function was never called with matching arguments.\nCalled with (last call if any):\n%s\nExpected:\n%s") +s:set("assertion.called_with.negative", "Function was called with matching arguments at least once.\nCalled with (last matching call):\n%s\nDid not expect:\n%s") + +s:set("assertion.returned_with.positive", "Function never returned matching arguments.\nReturned (last call if any):\n%s\nExpected:\n%s") +s:set("assertion.returned_with.negative", "Function returned matching arguments at least once.\nReturned (last matching call):\n%s\nDid not expect:\n%s") + +s:set("assertion.returned_arguments.positive", "Expected to be called with %s argument(s), but was called with %s") +s:set("assertion.returned_arguments.negative", "Expected not to be called with %s argument(s), but was called with %s") + +-- errors +s:set("assertion.internal.argtolittle", "the '%s' function requires a minimum of %s arguments, got: %s") +s:set("assertion.internal.badargtype", "bad argument #%s to '%s' (%s expected, got %s)") diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/match.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/match.lua new file mode 100644 index 00000000..671c82fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/match.lua @@ -0,0 +1,79 @@ +local namespace = require 'luassert.namespaces' +local util = require 'luassert.util' + +local matcher_mt = { + __call = function(self, value) + return self.callback(value) == self.mod + end, +} + +local state_mt = { + __call = function(self, ...) + local keys = util.extract_keys("matcher", self.tokens) + self.tokens = {} + + local matcher + + for _, key in ipairs(keys) do + matcher = namespace.matcher[key] or matcher + end + + if matcher then + for _, key in ipairs(keys) do + if namespace.modifier[key] then + namespace.modifier[key].callback(self) + end + end + + local arguments = util.make_arglist(...) + local matches = matcher.callback(self, arguments, util.errorlevel()) + return setmetatable({ + name = matcher.name, + mod = self.mod, + callback = matches, + arguments = arguments, + }, matcher_mt) + else + local arguments = util.make_arglist(...) + + for _, key in ipairs(keys) do + if namespace.modifier[key] then + namespace.modifier[key].callback(self, arguments, util.errorlevel()) + end + end + end + + return self + end, + + __index = function(self, key) + for token in key:lower():gmatch('[^_]+') do + table.insert(self.tokens, token) + end + + return self + end +} + +local match = { + _ = setmetatable({mod=true, callback=function() return true end}, matcher_mt), + + state = function() return setmetatable({mod=true, tokens={}}, state_mt) end, + + is_matcher = function(object) + return type(object) == "table" and getmetatable(object) == matcher_mt + end, + + is_ref_matcher = function(object) + local ismatcher = (type(object) == "table" and getmetatable(object) == matcher_mt) + return ismatcher and object.name == "ref" + end, +} + +local mt = { + __index = function(self, key) + return rawget(self, key) or self.state()[key] + end, +} + +return setmetatable(match, mt) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/matchers/composite.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/matchers/composite.lua new file mode 100644 index 00000000..e4775e0a --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/matchers/composite.lua @@ -0,0 +1,61 @@ +local assert = require('luassert.assert') +local match = require ('luassert.match') +local s = require('say') + +local function none(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "none", 1, tostring(argcnt) }), level) + for i = 1, argcnt do + assert(match.is_matcher(arguments[i]), s("assertion.internal.badargtype", { 1, "none", "matcher", type(arguments[i]) }), level) + end + + return function(value) + for _, matcher in ipairs(arguments) do + if matcher(value) then + return false + end + end + return true + end +end + +local function any(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "any", 1, tostring(argcnt) }), level) + for i = 1, argcnt do + assert(match.is_matcher(arguments[i]), s("assertion.internal.badargtype", { 1, "any", "matcher", type(arguments[i]) }), level) + end + + return function(value) + for _, matcher in ipairs(arguments) do + if matcher(value) then + return true + end + end + return false + end +end + +local function all(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "all", 1, tostring(argcnt) }), level) + for i = 1, argcnt do + assert(match.is_matcher(arguments[i]), s("assertion.internal.badargtype", { 1, "all", "matcher", type(arguments[i]) }), level) + end + + return function(value) + for _, matcher in ipairs(arguments) do + if not matcher(value) then + return false + end + end + return true + end +end + +assert:register("matcher", "none_of", none) +assert:register("matcher", "any_of", any) +assert:register("matcher", "all_of", all) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/matchers/core.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/matchers/core.lua new file mode 100644 index 00000000..4335baf1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/matchers/core.lua @@ -0,0 +1,173 @@ +-- module will return the list of matchers, and registers matchers with the main assert engine + +-- matchers take 1 parameters; +-- 1) state +-- 2) arguments list. The list has a member 'n' with the argument count to check for trailing nils +-- 3) level The level of the error position relative to the called function +-- returns; function (or callable object); a function that, given an argument, returns a boolean + +local assert = require('luassert.assert') +local astate = require('luassert.state') +local util = require('luassert.util') +local s = require('say') + +local function format(val) + return astate.format_argument(val) or tostring(val) +end + +local function unique(state, arguments, level) + local deep = arguments[1] + return function(value) + local list = value + for k,v in pairs(list) do + for k2, v2 in pairs(list) do + if k ~= k2 then + if deep and util.deepcompare(v, v2, true) then + return false + else + if v == v2 then + return false + end + end + end + end + end + return true + end +end + +local function near(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 1, s("assertion.internal.argtolittle", { "near", 2, tostring(argcnt) }), level) + local expected = tonumber(arguments[1]) + local tolerance = tonumber(arguments[2]) + local numbertype = "number or object convertible to a number" + assert(expected, s("assertion.internal.badargtype", { 1, "near", numbertype, format(arguments[1]) }), level) + assert(tolerance, s("assertion.internal.badargtype", { 2, "near", numbertype, format(arguments[2]) }), level) + + return function(value) + local actual = tonumber(value) + if not actual then return false end + return (actual >= expected - tolerance and actual <= expected + tolerance) + end +end + +local function matches(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "matches", 1, tostring(argcnt) }), level) + local pattern = arguments[1] + local init = arguments[2] + local plain = arguments[3] + assert(type(pattern) == "string", s("assertion.internal.badargtype", { 1, "matches", "string", type(arguments[1]) }), level) + assert(init == nil or tonumber(init), s("assertion.internal.badargtype", { 2, "matches", "number", type(arguments[2]) }), level) + + return function(value) + local actualtype = type(value) + local actual = nil + if actualtype == "string" or actualtype == "number" or + actualtype == "table" and (getmetatable(value) or {}).__tostring then + actual = tostring(value) + end + if not actual then return false end + return (actual:find(pattern, init, plain) ~= nil) + end +end + +local function equals(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "equals", 1, tostring(argcnt) }), level) + return function(value) + return value == arguments[1] + end +end + +local function same(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "same", 1, tostring(argcnt) }), level) + return function(value) + if type(value) == 'table' and type(arguments[1]) == 'table' then + local result = util.deepcompare(value, arguments[1], true) + return result + end + return value == arguments[1] + end +end + +local function ref(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + local argtype = type(arguments[1]) + local isobject = (argtype == "table" or argtype == "function" or argtype == "thread" or argtype == "userdata") + assert(argcnt > 0, s("assertion.internal.argtolittle", { "ref", 1, tostring(argcnt) }), level) + assert(isobject, s("assertion.internal.badargtype", { 1, "ref", "object", argtype }), level) + return function(value) + return value == arguments[1] + end +end + +local function is_true(state, arguments, level) + return function(value) + return value == true + end +end + +local function is_false(state, arguments, level) + return function(value) + return value == false + end +end + +local function truthy(state, arguments, level) + return function(value) + return value ~= false and value ~= nil + end +end + +local function falsy(state, arguments, level) + local is_truthy = truthy(state, arguments, level) + return function(value) + return not is_truthy(value) + end +end + +local function is_type(state, arguments, level, etype) + return function(value) + return type(value) == etype + end +end + +local function is_nil(state, arguments, level) return is_type(state, arguments, level, "nil") end +local function is_boolean(state, arguments, level) return is_type(state, arguments, level, "boolean") end +local function is_number(state, arguments, level) return is_type(state, arguments, level, "number") end +local function is_string(state, arguments, level) return is_type(state, arguments, level, "string") end +local function is_table(state, arguments, level) return is_type(state, arguments, level, "table") end +local function is_function(state, arguments, level) return is_type(state, arguments, level, "function") end +local function is_userdata(state, arguments, level) return is_type(state, arguments, level, "userdata") end +local function is_thread(state, arguments, level) return is_type(state, arguments, level, "thread") end + +assert:register("matcher", "true", is_true) +assert:register("matcher", "false", is_false) + +assert:register("matcher", "nil", is_nil) +assert:register("matcher", "boolean", is_boolean) +assert:register("matcher", "number", is_number) +assert:register("matcher", "string", is_string) +assert:register("matcher", "table", is_table) +assert:register("matcher", "function", is_function) +assert:register("matcher", "userdata", is_userdata) +assert:register("matcher", "thread", is_thread) + +assert:register("matcher", "ref", ref) +assert:register("matcher", "same", same) +assert:register("matcher", "matches", matches) +assert:register("matcher", "match", matches) +assert:register("matcher", "near", near) +assert:register("matcher", "equals", equals) +assert:register("matcher", "equal", equals) +assert:register("matcher", "unique", unique) +assert:register("matcher", "truthy", truthy) +assert:register("matcher", "falsy", falsy) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/matchers/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/matchers/init.lua new file mode 100644 index 00000000..c0ad62b1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/matchers/init.lua @@ -0,0 +1,3 @@ +-- load basic machers +require('luassert.matchers.core') +require('luassert.matchers.composite') diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/mock.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/mock.lua new file mode 100644 index 00000000..0a3bf3d1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/mock.lua @@ -0,0 +1,61 @@ +-- module will return a mock module table, and will not register any assertions +local spy = require 'luassert.spy' +local stub = require 'luassert.stub' + +local function mock_apply(object, action) + if type(object) ~= "table" then return end + if spy.is_spy(object) then + return object[action](object) + end + for k,v in pairs(object) do + mock_apply(v, action) + end + return object +end + +local mock +mock = { + new = function(object, dostub, func, self, key) + local visited = {} + local function do_mock(object, self, key) + local mock_handlers = { + ["table"] = function() + if spy.is_spy(object) or visited[object] then return end + visited[object] = true + for k,v in pairs(object) do + object[k] = do_mock(v, object, k) + end + return object + end, + ["function"] = function() + if dostub then + return stub(self, key, func) + elseif self==nil then + return spy.new(object) + else + return spy.on(self, key) + end + end + } + local handler = mock_handlers[type(object)] + return handler and handler() or object + end + return do_mock(object, self, key) + end, + + clear = function(object) + return mock_apply(object, "clear") + end, + + revert = function(object) + return mock_apply(object, "revert") + end +} + +return setmetatable(mock, { + __call = function(self, ...) + -- mock originally was a function only. Now that it is a module table + -- the __call method is required for backward compatibility + return mock.new(...) + end +}) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/modifiers.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/modifiers.lua new file mode 100644 index 00000000..9493228e --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/modifiers.lua @@ -0,0 +1,19 @@ +-- module will not return anything, only register assertions/modifiers with the main assert engine +local assert = require('luassert.assert') + +local function is(state) + return state +end + +local function is_not(state) + state.mod = not state.mod + return state +end + +assert:register("modifier", "is", is) +assert:register("modifier", "are", is) +assert:register("modifier", "was", is) +assert:register("modifier", "has", is) +assert:register("modifier", "does", is) +assert:register("modifier", "not", is_not) +assert:register("modifier", "no", is_not) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/namespaces.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/namespaces.lua new file mode 100644 index 00000000..0790fce8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/namespaces.lua @@ -0,0 +1,2 @@ +-- stores the list of namespaces +return {} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/spy.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/spy.lua new file mode 100644 index 00000000..eb7fc062 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/spy.lua @@ -0,0 +1,195 @@ +-- module will return spy table, and register its assertions with the main assert engine +local assert = require('luassert.assert') +local util = require('luassert.util') + +-- Spy metatable +local spy_mt = { + __call = function(self, ...) + local arguments = util.make_arglist(...) + table.insert(self.calls, util.copyargs(arguments)) + local function get_returns(...) + local returnvals = util.make_arglist(...) + table.insert(self.returnvals, util.copyargs(returnvals)) + return ... + end + return get_returns(self.callback(...)) + end +} + +local spy -- must make local before defining table, because table contents refers to the table (recursion) +spy = { + new = function(callback) + callback = callback or function() end + if not util.callable(callback) then + error("Cannot spy on type '" .. type(callback) .. "', only on functions or callable elements", util.errorlevel()) + end + local s = setmetatable({ + calls = {}, + returnvals = {}, + callback = callback, + + target_table = nil, -- these will be set when using 'spy.on' + target_key = nil, + + revert = function(self) + if not self.reverted then + if self.target_table and self.target_key then + self.target_table[self.target_key] = self.callback + end + self.reverted = true + end + return self.callback + end, + + clear = function(self) + self.calls = {} + self.returnvals = {} + return self + end, + + called = function(self, times, compare) + if times or compare then + local compare = compare or function(count, expected) return count == expected end + return compare(#self.calls, times), #self.calls + end + + return (#self.calls > 0), #self.calls + end, + + called_with = function(self, args) + local last_arglist = nil + if #self.calls > 0 then + last_arglist = self.calls[#self.calls].vals + end + local matching_arglists = util.matchargs(self.calls, args) + if matching_arglists ~= nil then + return true, matching_arglists.vals + end + return false, last_arglist + end, + + returned_with = function(self, args) + local last_returnvallist = nil + if #self.returnvals > 0 then + last_returnvallist = self.returnvals[#self.returnvals].vals + end + local matching_returnvallists = util.matchargs(self.returnvals, args) + if matching_returnvallists ~= nil then + return true, matching_returnvallists.vals + end + return false, last_returnvallist + end + }, spy_mt) + assert:add_spy(s) -- register with the current state + return s + end, + + is_spy = function(object) + return type(object) == "table" and getmetatable(object) == spy_mt + end, + + on = function(target_table, target_key) + local s = spy.new(target_table[target_key]) + target_table[target_key] = s + -- store original data + s.target_table = target_table + s.target_key = target_key + + return s + end +} + +local function set_spy(state, arguments, level) + state.payload = arguments[1] + if arguments[2] ~= nil then + state.failure_message = arguments[2] + end +end + +local function returned_with(state, arguments, level) + local level = (level or 1) + 1 + local payload = rawget(state, "payload") + if payload and payload.returned_with then + local assertion_holds, matching_or_last_returnvallist = state.payload:returned_with(arguments) + local expected_returnvallist = util.shallowcopy(arguments) + util.cleararglist(arguments) + util.tinsert(arguments, 1, matching_or_last_returnvallist) + util.tinsert(arguments, 2, expected_returnvallist) + return assertion_holds + else + error("'returned_with' must be chained after 'spy(aspy)'", level) + end +end + +local function called_with(state, arguments, level) + local level = (level or 1) + 1 + local payload = rawget(state, "payload") + if payload and payload.called_with then + local assertion_holds, matching_or_last_arglist = state.payload:called_with(arguments) + local expected_arglist = util.shallowcopy(arguments) + util.cleararglist(arguments) + util.tinsert(arguments, 1, matching_or_last_arglist) + util.tinsert(arguments, 2, expected_arglist) + return assertion_holds + else + error("'called_with' must be chained after 'spy(aspy)'", level) + end +end + +local function called(state, arguments, level, compare) + local level = (level or 1) + 1 + local num_times = arguments[1] + if not num_times and not state.mod then + state.mod = true + num_times = 0 + end + local payload = rawget(state, "payload") + if payload and type(payload) == "table" and payload.called then + local result, count = state.payload:called(num_times, compare) + arguments[1] = tostring(num_times or ">0") + util.tinsert(arguments, 2, tostring(count)) + arguments.nofmt = arguments.nofmt or {} + arguments.nofmt[1] = true + arguments.nofmt[2] = true + return result + elseif payload and type(payload) == "function" then + error("When calling 'spy(aspy)', 'aspy' must not be the original function, but the spy function replacing the original", level) + else + error("'called' must be chained after 'spy(aspy)'", level) + end +end + +local function called_at_least(state, arguments, level) + local level = (level or 1) + 1 + return called(state, arguments, level, function(count, expected) return count >= expected end) +end + +local function called_at_most(state, arguments, level) + local level = (level or 1) + 1 + return called(state, arguments, level, function(count, expected) return count <= expected end) +end + +local function called_more_than(state, arguments, level) + local level = (level or 1) + 1 + return called(state, arguments, level, function(count, expected) return count > expected end) +end + +local function called_less_than(state, arguments, level) + local level = (level or 1) + 1 + return called(state, arguments, level, function(count, expected) return count < expected end) +end + +assert:register("modifier", "spy", set_spy) +assert:register("assertion", "returned_with", returned_with, "assertion.returned_with.positive", "assertion.returned_with.negative") +assert:register("assertion", "called_with", called_with, "assertion.called_with.positive", "assertion.called_with.negative") +assert:register("assertion", "called", called, "assertion.called.positive", "assertion.called.negative") +assert:register("assertion", "called_at_least", called_at_least, "assertion.called_at_least.positive", "assertion.called_less_than.positive") +assert:register("assertion", "called_at_most", called_at_most, "assertion.called_at_most.positive", "assertion.called_more_than.positive") +assert:register("assertion", "called_more_than", called_more_than, "assertion.called_more_than.positive", "assertion.called_at_most.positive") +assert:register("assertion", "called_less_than", called_less_than, "assertion.called_less_than.positive", "assertion.called_at_least.positive") + +return setmetatable(spy, { + __call = function(self, ...) + return spy.new(...) + end +}) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/state.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/state.lua new file mode 100644 index 00000000..6de0efe1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/state.lua @@ -0,0 +1,127 @@ +-- maintains a state of the assert engine in a linked-list fashion +-- records; formatters, parameters, spies and stubs + +local state_mt = { + __call = function(self) + self:revert() + end +} + +local spies_mt = { __mode = "kv" } + +local nilvalue = {} -- unique ID to refer to nil values for parameters + +-- will hold the current state +local current + +-- exported module table +local state = {} + +------------------------------------------------------ +-- Reverts to a (specific) snapshot. +-- @param self (optional) the snapshot to revert to. If not provided, it will revert to the last snapshot. +state.revert = function(self) + if not self then + -- no snapshot given, so move 1 up + self = current + if not self.previous then + -- top of list, no previous one, nothing to do + return + end + end + if getmetatable(self) ~= state_mt then error("Value provided is not a valid snapshot", 2) end + + if self.next then + self.next:revert() + end + -- revert formatters in 'last' + self.formatters = {} + -- revert parameters in 'last' + self.parameters = {} + -- revert spies/stubs in 'last' + for s,_ in pairs(self.spies) do + self.spies[s] = nil + s:revert() + end + setmetatable(self, nil) -- invalidate as a snapshot + current = self.previous + current.next = nil +end + +------------------------------------------------------ +-- Creates a new snapshot. +-- @return snapshot table +state.snapshot = function() + local new = setmetatable ({ + formatters = {}, + parameters = {}, + spies = setmetatable({}, spies_mt), + previous = current, + revert = state.revert, + }, state_mt) + if current then current.next = new end + current = new + return current +end + + +-- FORMATTERS +state.add_formatter = function(callback) + table.insert(current.formatters, 1, callback) +end + +state.remove_formatter = function(callback, s) + s = s or current + for i, v in ipairs(s.formatters) do + if v == callback then + table.remove(s.formatters, i) + break + end + end + -- wasn't found, so traverse up 1 state + if s.previous then + state.remove_formatter(callback, s.previous) + end +end + +state.format_argument = function(val, s, fmtargs) + s = s or current + for _, fmt in ipairs(s.formatters) do + local valfmt = fmt(val, fmtargs) + if valfmt ~= nil then return valfmt end + end + -- nothing found, check snapshot 1 up in list + if s.previous then + return state.format_argument(val, s.previous, fmtargs) + end + return nil -- end of list, couldn't format +end + + +-- PARAMETERS +state.set_parameter = function(name, value) + if value == nil then value = nilvalue end + current.parameters[name] = value +end + +state.get_parameter = function(name, s) + s = s or current + local val = s.parameters[name] + if val == nil and s.previous then + -- not found, so check 1 up in list + return state.get_parameter(name, s.previous) + end + if val ~= nilvalue then + return val + end + return nil +end + +-- SPIES / STUBS +state.add_spy = function(spy) + current.spies[spy] = true +end + +state.snapshot() -- create initial state + +return state diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/stub.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/stub.lua new file mode 100644 index 00000000..91ae6e05 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/stub.lua @@ -0,0 +1,107 @@ +-- module will return a stub module table +local assert = require 'luassert.assert' +local spy = require 'luassert.spy' +local util = require 'luassert.util' +local unpack = util.unpack +local pack = util.pack + +local stub = {} + +function stub.new(object, key, ...) + if object == nil and key == nil then + -- called without arguments, create a 'blank' stub + object = {} + key = "" + end + local return_values = pack(...) + assert(type(object) == "table" and key ~= nil, "stub.new(): Can only create stub on a table key, call with 2 params; table, key", util.errorlevel()) + assert(object[key] == nil or util.callable(object[key]), "stub.new(): The element for which to create a stub must either be callable, or be nil", util.errorlevel()) + local old_elem = object[key] -- keep existing element (might be nil!) + + local fn = (return_values.n == 1 and util.callable(return_values[1]) and return_values[1]) + local defaultfunc = fn or function() + return unpack(return_values) + end + local oncalls = {} + local callbacks = {} + local stubfunc = function(...) + local args = util.make_arglist(...) + local match = util.matchoncalls(oncalls, args) + if match then + return callbacks[match](...) + end + return defaultfunc(...) + end + + object[key] = stubfunc -- set the stubfunction + local s = spy.on(object, key) -- create a spy on top of the stub function + local spy_revert = s.revert -- keep created revert function + + s.revert = function(self) -- wrap revert function to restore original element + if not self.reverted then + spy_revert(self) + object[key] = old_elem + self.reverted = true + end + return old_elem + end + + s.returns = function(...) + local return_args = pack(...) + defaultfunc = function() + return unpack(return_args) + end + return s + end + + s.invokes = function(func) + defaultfunc = function(...) + return func(...) + end + return s + end + + s.by_default = { + returns = s.returns, + invokes = s.invokes, + } + + s.on_call_with = function(...) + local match_args = util.make_arglist(...) + match_args = util.copyargs(match_args) + return { + returns = function(...) + local return_args = pack(...) + table.insert(oncalls, match_args) + callbacks[match_args] = function() + return unpack(return_args) + end + return s + end, + invokes = function(func) + table.insert(oncalls, match_args) + callbacks[match_args] = function(...) + return func(...) + end + return s + end + } + end + + return s +end + +local function set_stub(state, arguments) + state.payload = arguments[1] + state.failure_message = arguments[2] +end + +assert:register("modifier", "stub", set_stub) + +return setmetatable(stub, { + __call = function(self, ...) + -- stub originally was a function only. Now that it is a module table + -- the __call method is required for backward compatibility + return stub.new(...) + end +}) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/util.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/util.lua new file mode 100644 index 00000000..da2f2474 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/luassert/util.lua @@ -0,0 +1,362 @@ +local util = {} +local arglist_mt = {} + +-- have pack/unpack both respect the 'n' field +local _unpack = table.unpack or unpack +local unpack = function(t, i, j) return _unpack(t, i or 1, j or t.n or #t) end +local pack = function(...) return { n = select("#", ...), ... } end +util.pack = pack +util.unpack = unpack + + +function util.deepcompare(t1,t2,ignore_mt,cycles,thresh1,thresh2) + local ty1 = type(t1) + local ty2 = type(t2) + -- non-table types can be directly compared + if ty1 ~= 'table' or ty2 ~= 'table' then return t1 == t2 end + local mt1 = debug.getmetatable(t1) + local mt2 = debug.getmetatable(t2) + -- would equality be determined by metatable __eq? + if mt1 and mt1 == mt2 and mt1.__eq then + -- then use that unless asked not to + if not ignore_mt then return t1 == t2 end + else -- we can skip the deep comparison below if t1 and t2 share identity + if rawequal(t1, t2) then return true end + end + + -- handle recursive tables + cycles = cycles or {{},{}} + thresh1, thresh2 = (thresh1 or 1), (thresh2 or 1) + cycles[1][t1] = (cycles[1][t1] or 0) + cycles[2][t2] = (cycles[2][t2] or 0) + if cycles[1][t1] == 1 or cycles[2][t2] == 1 then + thresh1 = cycles[1][t1] + 1 + thresh2 = cycles[2][t2] + 1 + end + if cycles[1][t1] > thresh1 and cycles[2][t2] > thresh2 then + return true + end + + cycles[1][t1] = cycles[1][t1] + 1 + cycles[2][t2] = cycles[2][t2] + 1 + + for k1,v1 in next, t1 do + local v2 = t2[k1] + if v2 == nil then + return false, {k1} + end + + local same, crumbs = util.deepcompare(v1,v2,nil,cycles,thresh1,thresh2) + if not same then + crumbs = crumbs or {} + table.insert(crumbs, k1) + return false, crumbs + end + end + for k2,_ in next, t2 do + -- only check whether each element has a t1 counterpart, actual comparison + -- has been done in first loop above + if t1[k2] == nil then return false, {k2} end + end + + cycles[1][t1] = cycles[1][t1] - 1 + cycles[2][t2] = cycles[2][t2] - 1 + + return true +end + +function util.shallowcopy(t) + if type(t) ~= "table" then return t end + local copy = {} + setmetatable(copy, getmetatable(t)) + for k,v in next, t do + copy[k] = v + end + return copy +end + +function util.deepcopy(t, deepmt, cache) + local spy = require 'luassert.spy' + if type(t) ~= "table" then return t end + local copy = {} + + -- handle recursive tables + local cache = cache or {} + if cache[t] then return cache[t] end + cache[t] = copy + + for k,v in next, t do + copy[k] = (spy.is_spy(v) and v or util.deepcopy(v, deepmt, cache)) + end + if deepmt then + debug.setmetatable(copy, util.deepcopy(debug.getmetatable(t), false, cache)) + else + debug.setmetatable(copy, debug.getmetatable(t)) + end + return copy +end + +----------------------------------------------- +-- Copies arguments as a list of arguments +-- @param args the arguments of which to copy +-- @return the copy of the arguments +function util.copyargs(args) + local copy = {} + setmetatable(copy, getmetatable(args)) + local match = require 'luassert.match' + local spy = require 'luassert.spy' + for k,v in pairs(args) do + copy[k] = ((match.is_matcher(v) or spy.is_spy(v)) and v or util.deepcopy(v)) + end + return { vals = copy, refs = util.shallowcopy(args) } +end + +----------------------------------------------- +-- Clear an arguments or return values list from a table +-- @param arglist the table to clear of arguments or return values and their count +-- @return No return values +function util.cleararglist(arglist) + for idx = arglist.n, 1, -1 do + util.tremove(arglist, idx) + end + arglist.n = nil +end + +----------------------------------------------- +-- Test specs against an arglist in deepcopy and refs flavours. +-- @param args deepcopy arglist +-- @param argsrefs refs arglist +-- @param specs arguments/return values to match against args/argsrefs +-- @return true if specs match args/argsrefs, false otherwise +local function matcharg(args, argrefs, specs) + local match = require 'luassert.match' + for idx, argval in pairs(args) do + local spec = specs[idx] + if match.is_matcher(spec) then + if match.is_ref_matcher(spec) then + argval = argrefs[idx] + end + if not spec(argval) then + return false + end + elseif (spec == nil or not util.deepcompare(argval, spec)) then + return false + end + end + + for idx, spec in pairs(specs) do + -- only check whether each element has an args counterpart, + -- actual comparison has been done in first loop above + local argval = args[idx] + if argval == nil then + -- no args counterpart, so try to compare using matcher + if match.is_matcher(spec) then + if not spec(argval) then + return false + end + else + return false + end + end + end + return true +end + +----------------------------------------------- +-- Find matching arguments/return values in a saved list of +-- arguments/returned values. +-- @param invocations_list list of arguments/returned values to search (list of lists) +-- @param specs arguments/return values to match against argslist +-- @return the last matching arguments/returned values if a match is found, otherwise nil +function util.matchargs(invocations_list, specs) + -- Search the arguments/returned values last to first to give the + -- most helpful answer possible. In the cases where you can place + -- your assertions between calls to check this gives you the best + -- information if no calls match. In the cases where you can't do + -- that there is no good way to predict what would work best. + assert(not util.is_arglist(invocations_list), "expected a list of arglist-object, got an arglist") + for ii = #invocations_list, 1, -1 do + local val = invocations_list[ii] + if matcharg(val.vals, val.refs, specs) then + return val + end + end + return nil +end + +----------------------------------------------- +-- Find matching oncall for an actual call. +-- @param oncalls list of oncalls to search +-- @param args actual call argslist to match against +-- @return the first matching oncall if a match is found, otherwise nil +function util.matchoncalls(oncalls, args) + for _, callspecs in ipairs(oncalls) do + -- This lookup is done immediately on *args* passing into the stub + -- so pass *args* as both *args* and *argsref* without copying + -- either. + if matcharg(args, args, callspecs.vals) then + return callspecs + end + end + return nil +end + +----------------------------------------------- +-- table.insert() replacement that respects nil values. +-- The function will use table field 'n' as indicator of the +-- table length, if not set, it will be added. +-- @param t table into which to insert +-- @param pos (optional) position in table where to insert. NOTE: not optional if you want to insert a nil-value! +-- @param val value to insert +-- @return No return values +function util.tinsert(...) + -- check optional POS value + local args = {...} + local c = select('#',...) + local t = args[1] + local pos = args[2] + local val = args[3] + if c < 3 then + val = pos + pos = nil + end + -- set length indicator n if not present (+1) + t.n = (t.n or #t) + 1 + if not pos then + pos = t.n + elseif pos > t.n then + -- out of our range + t[pos] = val + t.n = pos + end + -- shift everything up 1 pos + for i = t.n, pos + 1, -1 do + t[i]=t[i-1] + end + -- add element to be inserted + t[pos] = val +end +----------------------------------------------- +-- table.remove() replacement that respects nil values. +-- The function will use table field 'n' as indicator of the +-- table length, if not set, it will be added. +-- @param t table from which to remove +-- @param pos (optional) position in table to remove +-- @return No return values +function util.tremove(t, pos) + -- set length indicator n if not present (+1) + t.n = t.n or #t + if not pos then + pos = t.n + elseif pos > t.n then + local removed = t[pos] + -- out of our range + t[pos] = nil + return removed + end + local removed = t[pos] + -- shift everything up 1 pos + for i = pos, t.n do + t[i]=t[i+1] + end + -- set size, clean last + t[t.n] = nil + t.n = t.n - 1 + return removed +end + +----------------------------------------------- +-- Checks an element to be callable. +-- The type must either be a function or have a metatable +-- containing an '__call' function. +-- @param object element to inspect on being callable or not +-- @return boolean, true if the object is callable +function util.callable(object) + return type(object) == "function" or type((debug.getmetatable(object) or {}).__call) == "function" +end + +----------------------------------------------- +-- Checks an element has tostring. +-- The type must either be a string or have a metatable +-- containing an '__tostring' function. +-- @param object element to inspect on having tostring or not +-- @return boolean, true if the object has tostring +function util.hastostring(object) + return type(object) == "string" or type((debug.getmetatable(object) or {}).__tostring) == "function" +end + +----------------------------------------------- +-- Find the first level, not defined in the same file as the caller's +-- code file to properly report an error. +-- @param level the level to use as the caller's source file +-- @return number, the level of which to report an error +function util.errorlevel(level) + local level = (level or 1) + 1 -- add one to get level of the caller + local info = debug.getinfo(level) + local source = (info or {}).source + local file = source + while file and (file == source or source == "=(tail call)") do + level = level + 1 + info = debug.getinfo(level) + source = (info or {}).source + end + if level > 1 then level = level - 1 end -- deduct call to errorlevel() itself + return level +end + +----------------------------------------------- +-- Extract modifier and namespace keys from list of tokens. +-- @param nspace the namespace from which to match tokens +-- @param tokens list of tokens to search for keys +-- @return table, list of keys that were extracted +function util.extract_keys(nspace, tokens) + local namespace = require 'luassert.namespaces' + + -- find valid keys by coalescing tokens as needed, starting from the end + local keys = {} + local key = nil + local i = #tokens + while i > 0 do + local token = tokens[i] + key = key and (token .. '_' .. key) or token + + -- find longest matching key in the given namespace + local longkey = i > 1 and (tokens[i-1] .. '_' .. key) or nil + while i > 1 and longkey and namespace[nspace][longkey] do + key = longkey + i = i - 1 + token = tokens[i] + longkey = (token .. '_' .. key) + end + + if namespace.modifier[key] or namespace[nspace][key] then + table.insert(keys, 1, key) + key = nil + end + i = i - 1 + end + + -- if there's anything left we didn't recognize it + if key then + error("luassert: unknown modifier/" .. nspace .. ": '" .. key .."'", util.errorlevel(2)) + end + + return keys +end + +----------------------------------------------- +-- store argument list for return values of a function in a table. +-- The table will get a metatable to identify it as an arglist +function util.make_arglist(...) + local arglist = { ... } + arglist.n = select('#', ...) -- add values count for trailing nils + return setmetatable(arglist, arglist_mt) +end + +----------------------------------------------- +-- check a table to be an arglist type. +function util.is_arglist(object) + return getmetatable(object) == arglist_mt +end + +return util diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/_meta/_luassert.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/_meta/_luassert.lua new file mode 100644 index 00000000..c5899705 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/_meta/_luassert.lua @@ -0,0 +1,289 @@ +---@meta +---This file is autogenerated, DO NOT EDIT +error "Cannot require a meta file" + +---@generic T:any +---@alias LuassertFunction fun(value:T, message?:string):T +---@alias LuassertFunctionTwoArgs fun(expected:T, actual:T, message?:string):T +---@alias LuassertFunctionMultiArgs fun(...:T):T + +---@class Luassert +---@field are_boolean LuassertFunction +---@field are_equal LuassertFunctionTwoArgs +---@field are_equals LuassertFunctionTwoArgs +---@field are_error LuassertFunction +---@field are_error_match LuassertFunctionTwoArgs +---@field are_error_matches LuassertFunctionTwoArgs +---@field are_errors LuassertFunction +---@field are_false LuassertFunction +---@field are_falsy LuassertFunction +---@field are_function LuassertFunction +---@field are_holes LuassertFunction +---@field are_match LuassertFunctionTwoArgs +---@field are_match_error LuassertFunctionTwoArgs +---@field are_matches LuassertFunctionTwoArgs +---@field are_matches_error LuassertFunctionTwoArgs +---@field are_near LuassertFunctionMultiArgs +---@field are_nil LuassertFunction +---@field are_number LuassertFunction +---@field are_returned_arguments LuassertFunction +---@field are_same LuassertFunctionTwoArgs +---@field are_string LuassertFunction +---@field are_table LuassertFunction +---@field are_thread LuassertFunction +---@field are_true LuassertFunction +---@field are_truthy LuassertFunction +---@field are_unique LuassertFunction +---@field are_userdata LuassertFunction +---@field array_boolean LuassertFunction +---@field array_equal LuassertFunctionTwoArgs +---@field array_equals LuassertFunctionTwoArgs +---@field array_error LuassertFunction +---@field array_error_match LuassertFunctionTwoArgs +---@field array_error_matches LuassertFunctionTwoArgs +---@field array_errors LuassertFunction +---@field array_false LuassertFunction +---@field array_falsy LuassertFunction +---@field array_function LuassertFunction +---@field array_holes LuassertFunction +---@field array_match LuassertFunctionTwoArgs +---@field array_match_error LuassertFunctionTwoArgs +---@field array_matches LuassertFunctionTwoArgs +---@field array_matches_error LuassertFunctionTwoArgs +---@field array_near LuassertFunctionMultiArgs +---@field array_nil LuassertFunction +---@field array_number LuassertFunction +---@field array_returned_arguments LuassertFunction +---@field array_same LuassertFunctionTwoArgs +---@field array_string LuassertFunction +---@field array_table LuassertFunction +---@field array_thread LuassertFunction +---@field array_true LuassertFunction +---@field array_truthy LuassertFunction +---@field array_unique LuassertFunction +---@field array_userdata LuassertFunction +---@field does_boolean LuassertFunction +---@field does_equal LuassertFunctionTwoArgs +---@field does_equals LuassertFunctionTwoArgs +---@field does_error LuassertFunction +---@field does_error_match LuassertFunctionTwoArgs +---@field does_error_matches LuassertFunctionTwoArgs +---@field does_errors LuassertFunction +---@field does_false LuassertFunction +---@field does_falsy LuassertFunction +---@field does_function LuassertFunction +---@field does_holes LuassertFunction +---@field does_match LuassertFunctionTwoArgs +---@field does_match_error LuassertFunctionTwoArgs +---@field does_matches LuassertFunctionTwoArgs +---@field does_matches_error LuassertFunctionTwoArgs +---@field does_near LuassertFunctionMultiArgs +---@field does_nil LuassertFunction +---@field does_number LuassertFunction +---@field does_returned_arguments LuassertFunction +---@field does_same LuassertFunctionTwoArgs +---@field does_string LuassertFunction +---@field does_table LuassertFunction +---@field does_thread LuassertFunction +---@field does_true LuassertFunction +---@field does_truthy LuassertFunction +---@field does_unique LuassertFunction +---@field does_userdata LuassertFunction +---@field has_boolean LuassertFunction +---@field has_equal LuassertFunctionTwoArgs +---@field has_equals LuassertFunctionTwoArgs +---@field has_error LuassertFunction +---@field has_error_match LuassertFunctionTwoArgs +---@field has_error_matches LuassertFunctionTwoArgs +---@field has_errors LuassertFunction +---@field has_false LuassertFunction +---@field has_falsy LuassertFunction +---@field has_function LuassertFunction +---@field has_holes LuassertFunction +---@field has_match LuassertFunctionTwoArgs +---@field has_match_error LuassertFunctionTwoArgs +---@field has_matches LuassertFunctionTwoArgs +---@field has_matches_error LuassertFunctionTwoArgs +---@field has_near LuassertFunctionMultiArgs +---@field has_nil LuassertFunction +---@field has_number LuassertFunction +---@field has_returned_arguments LuassertFunction +---@field has_same LuassertFunctionTwoArgs +---@field has_string LuassertFunction +---@field has_table LuassertFunction +---@field has_thread LuassertFunction +---@field has_true LuassertFunction +---@field has_truthy LuassertFunction +---@field has_unique LuassertFunction +---@field has_userdata LuassertFunction +---@field is_boolean LuassertFunction +---@field is_equal LuassertFunctionTwoArgs +---@field is_equals LuassertFunctionTwoArgs +---@field is_error LuassertFunction +---@field is_error_match LuassertFunctionTwoArgs +---@field is_error_matches LuassertFunctionTwoArgs +---@field is_errors LuassertFunction +---@field is_false LuassertFunction +---@field is_falsy LuassertFunction +---@field is_function LuassertFunction +---@field is_holes LuassertFunction +---@field is_match LuassertFunctionTwoArgs +---@field is_match_error LuassertFunctionTwoArgs +---@field is_matches LuassertFunctionTwoArgs +---@field is_matches_error LuassertFunctionTwoArgs +---@field is_near LuassertFunctionMultiArgs +---@field is_nil LuassertFunction +---@field is_number LuassertFunction +---@field is_returned_arguments LuassertFunction +---@field is_same LuassertFunctionTwoArgs +---@field is_string LuassertFunction +---@field is_table LuassertFunction +---@field is_thread LuassertFunction +---@field is_true LuassertFunction +---@field is_truthy LuassertFunction +---@field is_unique LuassertFunction +---@field is_userdata LuassertFunction +---@field message_boolean LuassertFunction +---@field message_equal LuassertFunctionTwoArgs +---@field message_equals LuassertFunctionTwoArgs +---@field message_error LuassertFunction +---@field message_error_match LuassertFunctionTwoArgs +---@field message_error_matches LuassertFunctionTwoArgs +---@field message_errors LuassertFunction +---@field message_false LuassertFunction +---@field message_falsy LuassertFunction +---@field message_function LuassertFunction +---@field message_holes LuassertFunction +---@field message_match LuassertFunctionTwoArgs +---@field message_match_error LuassertFunctionTwoArgs +---@field message_matches LuassertFunctionTwoArgs +---@field message_matches_error LuassertFunctionTwoArgs +---@field message_near LuassertFunctionMultiArgs +---@field message_nil LuassertFunction +---@field message_number LuassertFunction +---@field message_returned_arguments LuassertFunction +---@field message_same LuassertFunctionTwoArgs +---@field message_string LuassertFunction +---@field message_table LuassertFunction +---@field message_thread LuassertFunction +---@field message_true LuassertFunction +---@field message_truthy LuassertFunction +---@field message_unique LuassertFunction +---@field message_userdata LuassertFunction +---@field no_boolean LuassertFunction +---@field no_equal LuassertFunctionTwoArgs +---@field no_equals LuassertFunctionTwoArgs +---@field no_error LuassertFunction +---@field no_error_match LuassertFunctionTwoArgs +---@field no_error_matches LuassertFunctionTwoArgs +---@field no_errors LuassertFunction +---@field no_false LuassertFunction +---@field no_falsy LuassertFunction +---@field no_function LuassertFunction +---@field no_holes LuassertFunction +---@field no_match LuassertFunctionTwoArgs +---@field no_match_error LuassertFunctionTwoArgs +---@field no_matches LuassertFunctionTwoArgs +---@field no_matches_error LuassertFunctionTwoArgs +---@field no_near LuassertFunctionMultiArgs +---@field no_nil LuassertFunction +---@field no_number LuassertFunction +---@field no_returned_arguments LuassertFunction +---@field no_same LuassertFunctionTwoArgs +---@field no_string LuassertFunction +---@field no_table LuassertFunction +---@field no_thread LuassertFunction +---@field no_true LuassertFunction +---@field no_truthy LuassertFunction +---@field no_unique LuassertFunction +---@field no_userdata LuassertFunction +---@field not_boolean LuassertFunction +---@field not_equal LuassertFunctionTwoArgs +---@field not_equals LuassertFunctionTwoArgs +---@field not_error LuassertFunction +---@field not_error_match LuassertFunctionTwoArgs +---@field not_error_matches LuassertFunctionTwoArgs +---@field not_errors LuassertFunction +---@field not_false LuassertFunction +---@field not_falsy LuassertFunction +---@field not_function LuassertFunction +---@field not_holes LuassertFunction +---@field not_match LuassertFunctionTwoArgs +---@field not_match_error LuassertFunctionTwoArgs +---@field not_matches LuassertFunctionTwoArgs +---@field not_matches_error LuassertFunctionTwoArgs +---@field not_near LuassertFunctionMultiArgs +---@field not_nil LuassertFunction +---@field not_number LuassertFunction +---@field not_returned_arguments LuassertFunction +---@field not_same LuassertFunctionTwoArgs +---@field not_string LuassertFunction +---@field not_table LuassertFunction +---@field not_thread LuassertFunction +---@field not_true LuassertFunction +---@field not_truthy LuassertFunction +---@field not_unique LuassertFunction +---@field not_userdata LuassertFunction +---@field was_boolean LuassertFunction +---@field was_equal LuassertFunctionTwoArgs +---@field was_equals LuassertFunctionTwoArgs +---@field was_error LuassertFunction +---@field was_error_match LuassertFunctionTwoArgs +---@field was_error_matches LuassertFunctionTwoArgs +---@field was_errors LuassertFunction +---@field was_false LuassertFunction +---@field was_falsy LuassertFunction +---@field was_function LuassertFunction +---@field was_holes LuassertFunction +---@field was_match LuassertFunctionTwoArgs +---@field was_match_error LuassertFunctionTwoArgs +---@field was_matches LuassertFunctionTwoArgs +---@field was_matches_error LuassertFunctionTwoArgs +---@field was_near LuassertFunctionMultiArgs +---@field was_nil LuassertFunction +---@field was_number LuassertFunction +---@field was_returned_arguments LuassertFunction +---@field was_same LuassertFunctionTwoArgs +---@field was_string LuassertFunction +---@field was_table LuassertFunction +---@field was_thread LuassertFunction +---@field was_true LuassertFunction +---@field was_truthy LuassertFunction +---@field was_unique LuassertFunction +---@field was_userdata LuassertFunction +---@field boolean LuassertFunction +---@field equal LuassertFunctionTwoArgs +---@field equals LuassertFunctionTwoArgs +---@field error LuassertFunction +---@field error_match LuassertFunctionTwoArgs +---@field error_matches LuassertFunctionTwoArgs +---@field errors LuassertFunction +---@field False LuassertFunction +---@field falsy LuassertFunction +---@field Function LuassertFunction +---@field holes LuassertFunction +---@field match LuassertFunctionTwoArgs +---@field match_error LuassertFunctionTwoArgs +---@field matches LuassertFunctionTwoArgs +---@field matches_error LuassertFunctionTwoArgs +---@field near LuassertFunctionMultiArgs +---@field Nil LuassertFunction +---@field number LuassertFunction +---@field returned_arguments LuassertFunction +---@field same LuassertFunctionTwoArgs +---@field string LuassertFunction +---@field table LuassertFunction +---@field thread LuassertFunction +---@field True LuassertFunction +---@field truthy LuassertFunction +---@field unique LuassertFunction +---@field userdata LuassertFunction +---@field are Luassert +---@field array Luassert +---@field does Luassert +---@field has Luassert +---@field is Luassert +---@field message Luassert +---@field no Luassert +---@field Not Luassert +---@field was Luassert \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/api.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/api.lua new file mode 100644 index 00000000..2a4e9c0c --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/api.lua @@ -0,0 +1,14 @@ +local util = require "plenary.async.util" + +return setmetatable({}, { + __index = function(t, k) + return function(...) + -- if we are in a fast event await the scheduler + if vim.in_fast_event() then + util.scheduler() + end + + return vim.api[k](...) + end + end, +}) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/async.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/async.lua new file mode 100644 index 00000000..ff49288d --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/async.lua @@ -0,0 +1,122 @@ +local co = coroutine +local vararg = require "plenary.vararg" +local errors = require "plenary.errors" +local traceback_error = errors.traceback_error +local f = require "plenary.functional" + +local M = {} + +local function is_callable(fn) + return type(fn) == "function" or (type(fn) == "table" and type(getmetatable(fn)["__call"]) == "function") +end + +---because we can't store varargs +local function callback_or_next(step, thread, callback, ...) + local stat = f.first(...) + + if not stat then + error(string.format("The coroutine failed with this message: %s", f.second(...))) + end + + if co.status(thread) == "dead" then + if callback == nil then + return + end + callback(select(2, ...)) + else + local returned_function = f.second(...) + local nargs = f.third(...) + + assert(is_callable(returned_function), "type error :: expected func") + returned_function(vararg.rotate(nargs, step, select(4, ...))) + end +end + +---Executes a future with a callback when it is done +---@param async_function Future: the future to execute +---@param callback function: the callback to call when done +local execute = function(async_function, callback, ...) + assert(is_callable(async_function), "type error :: expected func") + + local thread = co.create(async_function) + + local step + step = function(...) + callback_or_next(step, thread, callback, co.resume(thread, ...)) + end + + step(...) +end + +local add_leaf_function +do + ---A table to store all leaf async functions + _PlenaryLeafTable = setmetatable({}, { + __mode = "k", + }) + + add_leaf_function = function(async_func, argc) + assert(_PlenaryLeafTable[async_func] == nil, "Async function should not already be in the table") + _PlenaryLeafTable[async_func] = argc + end + + function M.is_leaf_function(async_func) + return _PlenaryLeafTable[async_func] ~= nil + end + + function M.get_leaf_function_argc(async_func) + return _PlenaryLeafTable[async_func] + end +end + +---Creates an async function with a callback style function. +---@param func function: A callback style function to be converted. The last argument must be the callback. +---@param argc number: The number of arguments of func. Must be included. +---@return function: Returns an async function +M.wrap = function(func, argc) + if not is_callable(func) then + traceback_error("type error :: expected func, got " .. type(func)) + end + + if type(argc) ~= "number" then + traceback_error("type error :: expected number, got " .. type(argc)) + end + + local function leaf(...) + local nargs = select("#", ...) + + if nargs == argc then + return func(...) + else + return co.yield(func, argc, ...) + end + end + + add_leaf_function(leaf, argc) + + return leaf +end + +---Use this to either run a future concurrently and then do something else +---or use it to run a future with a callback in a non async context +---@param async_function function +---@param callback function +M.run = function(async_function, callback) + if M.is_leaf_function(async_function) then + async_function(callback) + else + execute(async_function, callback) + end +end + +---Use this to create a function which executes in an async context but +---called from a non-async context. Inherently this cannot return anything +---since it is non-blocking +---@param func function +M.void = function(func) + return function(...) + execute(func, nil, ...) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/control.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/control.lua new file mode 100644 index 00000000..3a8a8ed6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/control.lua @@ -0,0 +1,229 @@ +local a = require "plenary.async.async" +local Deque = require("plenary.async.structs").Deque +local tbl = require "plenary.tbl" + +local M = {} + +local Condvar = {} +Condvar.__index = Condvar + +---@class Condvar +---@return Condvar +function Condvar.new() + return setmetatable({ handles = {} }, Condvar) +end + +---`blocks` the thread until a notification is received +Condvar.wait = a.wrap(function(self, callback) + -- not calling the callback will block the coroutine + table.insert(self.handles, callback) +end, 2) + +---notify everyone that is waiting on this Condvar +function Condvar:notify_all() + local len = #self.handles + for i, callback in ipairs(self.handles) do + if i > len then + -- this means that more handles were added while we were notifying + -- if we don't break we can get starvation notifying as soon as new handles are added + break + end + + callback() + end + + for _ = 1, len do + -- table.remove will ensure that indexes are correct and make "ipairs" safe, + -- which is not the case for "self.handles[i] = nil" + table.remove(self.handles) + end +end + +---notify randomly one person that is waiting on this Condvar +function Condvar:notify_one() + if #self.handles == 0 then + return + end + + local idx = math.random(#self.handles) + self.handles[idx]() + table.remove(self.handles, idx) +end + +M.Condvar = Condvar + +local Semaphore = {} +Semaphore.__index = Semaphore + +---@class Semaphore +---@param initial_permits number: the number of permits that it can give out +---@return Semaphore +function Semaphore.new(initial_permits) + vim.validate { + initial_permits = { + initial_permits, + function(n) + return n > 0 + end, + "number greater than 0", + }, + } + + return setmetatable({ permits = initial_permits, handles = {} }, Semaphore) +end + +---async function, blocks until a permit can be acquired +---example: +---local semaphore = Semaphore.new(1024) +---local permit = semaphore:acquire() +---permit:forget() +---when a permit can be acquired returns it +---call permit:forget() to forget the permit +Semaphore.acquire = a.wrap(function(self, callback) + if self.permits > 0 then + self.permits = self.permits - 1 + else + table.insert(self.handles, callback) + return + end + + local permit = {} + + permit.forget = function(self_permit) + self.permits = self.permits + 1 + + if self.permits > 0 and #self.handles > 0 then + self.permits = self.permits - 1 + table.remove(self.handles)(self_permit) + end + end + + callback(permit) +end, 2) + +M.Semaphore = Semaphore + +M.channel = {} + +---Creates a oneshot channel +---returns a sender and receiver function +---the sender is not async while the receiver is +---@return function, function +M.channel.oneshot = function() + local val = nil + local saved_callback = nil + local sent = false + local received = false + local is_single = false + + --- sender is not async + --- sends a value which can be nil + local sender = function(...) + assert(not sent, "Oneshot channel can only send once") + sent = true + + if saved_callback ~= nil then + saved_callback(...) + return + end + + -- optimise for when there is only one or zero argument, no need to pack + local nargs = select("#", ...) + if nargs == 1 or nargs == 0 then + val = ... + is_single = true + else + val = tbl.pack(...) + end + end + + --- receiver is async + --- blocks until a value is received + local receiver = a.wrap(function(callback) + assert(not received, "Oneshot channel can only receive one value!") + + if sent then + received = true + if is_single then + return callback(val) + else + return callback(tbl.unpack(val)) + end + else + saved_callback = callback + end + end, 1) + + return sender, receiver +end + +---A counter channel. +---Basically a channel that you want to use only to notify and not to send any actual values. +---@return function: sender +---@return function: receiver +M.channel.counter = function() + local counter = 0 + local condvar = Condvar.new() + + local Sender = {} + + function Sender:send() + counter = counter + 1 + condvar:notify_all() + end + + local Receiver = {} + + Receiver.recv = function() + if counter == 0 then + condvar:wait() + end + counter = counter - 1 + end + + Receiver.last = function() + if counter == 0 then + condvar:wait() + end + counter = 0 + end + + return Sender, Receiver +end + +---A multiple producer single consumer channel +---@return table +---@return table +M.channel.mpsc = function() + local deque = Deque.new() + local condvar = Condvar.new() + + local Sender = {} + + function Sender.send(...) + deque:pushleft { ... } + condvar:notify_all() + end + + local Receiver = {} + + Receiver.recv = function() + if deque:is_empty() then + condvar:wait() + end + return unpack(deque:popright()) + end + + Receiver.last = function() + if deque:is_empty() then + condvar:wait() + end + local val = deque:popleft() + deque:clear() + return unpack(val or {}) + end + + return Sender, Receiver +end + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/init.lua new file mode 100644 index 00000000..027614ad --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/init.lua @@ -0,0 +1,57 @@ +---@brief [[ +--- NOTE: This API is still under construction. +--- It may change in the future :) +---@brief ]] + +local lookups = { + uv = "plenary.async.uv_async", + util = "plenary.async.util", + lsp = "plenary.async.lsp", + api = "plenary.async.api", + tests = "plenary.async.tests", + control = "plenary.async.control", +} + +local exports = setmetatable(require "plenary.async.async", { + __index = function(t, k) + local require_path = lookups[k] + if not require_path then + return + end + + local mod = require(require_path) + t[k] = mod + + return mod + end, +}) + +exports.tests.add_globals = function() + a = exports + + -- must prefix with a or stack overflow, plenary.test harness already added it + a.describe = exports.tests.describe + -- must prefix with a or stack overflow + a.it = exports.tests.it + a.pending = exports.tests.pending + a.before_each = exports.tests.before_each + a.after_each = exports.tests.after_each +end + +exports.tests.add_to_env = function() + local env = getfenv(2) + + env.a = exports + + -- must prefix with a or stack overflow, plenary.test harness already added it + env.a.describe = exports.tests.describe + -- must prefix with a or stack overflow + env.a.it = exports.tests.it + env.a.pending = exports.tests.pending + env.a.before_each = exports.tests.before_each + env.a.after_each = exports.tests.after_each + + setfenv(2, env) +end + +return exports diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/lsp.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/lsp.lua new file mode 100644 index 00000000..e76e4c4f --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/lsp.lua @@ -0,0 +1,12 @@ +local a = require "plenary.async.async" + +local M = {} + +---This will be deprecated because the callback can be called multiple times. +---This will give a coroutine error because the coroutine will be resumed multiple times. +---Please use buf_request_all instead. +M.buf_request = a.wrap(vim.lsp.buf_request, 4) + +M.buf_request_all = a.wrap(vim.lsp.buf_request_all, 4) + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/structs.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/structs.lua new file mode 100644 index 00000000..c133a216 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/structs.lua @@ -0,0 +1,116 @@ +local M = {} + +local Deque = {} +Deque.__index = Deque + +---@class Deque +---A double ended queue +--- +---@return Deque +function Deque.new() + -- the indexes are created with an offset so that the indices are consequtive + -- otherwise, when both pushleft and pushright are used, the indices will have a 1 length hole in the middle + return setmetatable({ first = 0, last = -1 }, Deque) +end + +---push to the left of the deque +---@param value any +function Deque:pushleft(value) + local first = self.first - 1 + self.first = first + self[first] = value +end + +---push to the right of the deque +---@param value any +function Deque:pushright(value) + local last = self.last + 1 + self.last = last + self[last] = value +end + +---pop from the left of the deque +---@return any +function Deque:popleft() + local first = self.first + if first > self.last then + return nil + end + local value = self[first] + self[first] = nil -- to allow garbage collection + self.first = first + 1 + return value +end + +---pops from the right of the deque +---@return any +function Deque:popright() + local last = self.last + if self.first > last then + return nil + end + local value = self[last] + self[last] = nil -- to allow garbage collection + self.last = last - 1 + return value +end + +---checks if the deque is empty +---@return boolean +function Deque:is_empty() + return self:len() == 0 +end + +---returns the number of elements of the deque +---@return number +function Deque:len() + return self.last - self.first + 1 +end + +---returns and iterator of the indices and values starting from the left +---@return function +function Deque:ipairs_left() + local i = self.first + + return function() + local res = self[i] + local idx = i + + if res then + i = i + 1 + + return idx, res + end + end +end + +---returns and iterator of the indices and values starting from the right +---@return function +function Deque:ipairs_right() + local i = self.last + + return function() + local res = self[i] + local idx = i + + if res then + i = i - 1 -- advance the iterator before we return + + return idx, res + end + end +end + +---removes all values from the deque +---@return nil +function Deque:clear() + for i, _ in self:ipairs_left() do + self[i] = nil + end + self.first = 0 + self.last = -1 +end + +M.Deque = Deque + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/tests.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/tests.lua new file mode 100644 index 00000000..d10db487 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/tests.lua @@ -0,0 +1,25 @@ +local util = require "plenary.async.util" + +local M = {} + +M.describe = function(s, async_func) + describe(s, async_func) +end + +M.it = function(s, async_func) + it(s, util.will_block(async_func, tonumber(vim.env.PLENARY_TEST_TIMEOUT))) +end + +M.pending = function(async_func) + pending(async_func) +end + +M.before_each = function(async_func) + before_each(util.will_block(async_func)) +end + +M.after_each = function(async_func) + after_each(util.will_block(async_func)) +end + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/util.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/util.lua new file mode 100644 index 00000000..c689ba33 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/util.lua @@ -0,0 +1,141 @@ +local a = require "plenary.async.async" +local vararg = require "plenary.vararg" +-- local control = a.control +local control = require "plenary.async.control" +local channel = control.channel + +local M = {} + +local defer_swapped = function(timeout, callback) + vim.defer_fn(callback, timeout) +end + +---Sleep for milliseconds +---@param ms number +M.sleep = a.wrap(defer_swapped, 2) + +---This will COMPLETELY block neovim +---please just use a.run unless you have a very special usecase +---for example, in plenary test_harness you must use this +---@param async_function Future +---@param timeout number: Stop blocking if the timeout was surpassed. Default 2000. +M.block_on = function(async_function, timeout) + async_function = M.protected(async_function) + + local stat + local ret = {} + + a.run(async_function, function(stat_, ...) + stat = stat_ + ret = { ... } + end) + + vim.wait(timeout or 2000, function() + return stat ~= nil + end, 20, false) + + if stat == false then + error(string.format("Blocking on future timed out or was interrupted.\n%s", unpack(ret))) + end + + return unpack(ret) +end + +---@see M.block_on +---@param async_function Future +---@param timeout number +M.will_block = function(async_function, timeout) + return function() + M.block_on(async_function, timeout) + end +end + +M.join = function(async_fns) + local len = #async_fns + local results = {} + local done = 0 + + local tx, rx = channel.oneshot() + + for i, async_fn in ipairs(async_fns) do + assert(type(async_fn) == "function", "type error :: future must be function") + + local cb = function(...) + results[i] = { ... } + done = done + 1 + if done == len then + tx() + end + end + + a.run(async_fn, cb) + end + + rx() + + return results +end + +---Returns a result from the future that finishes at the first +---@param async_functions table: The futures that you want to select +---@return ... +M.run_first = a.wrap(function(async_functions, step) + local ran = false + + for _, async_function in ipairs(async_functions) do + assert(type(async_function) == "function", "type error :: future must be function") + + local callback = function(...) + if not ran then + ran = true + step(...) + end + end + + async_function(callback) + end +end, 2) + +---Returns a result from the functions that finishes at the first +---@param funcs table: The async functions that you want to select +---@return ... +M.race = function(funcs) + local async_functions = vim.tbl_map(function(func) + return function(callback) + a.run(func, callback) + end + end, funcs) + return M.run_first(async_functions) +end + +M.run_all = function(async_fns, callback) + a.run(function() + M.join(async_fns) + end, callback) +end + +function M.apcall(async_fn, ...) + local nargs = a.get_leaf_function_argc(async_fn) + if nargs then + local tx, rx = channel.oneshot() + local stat, ret = pcall(async_fn, vararg.rotate(nargs, tx, ...)) + if not stat then + return stat, ret + else + return stat, rx() + end + else + return pcall(async_fn, ...) + end +end + +function M.protected(async_fn) + return function() + return M.apcall(async_fn) + end +end + +---An async function that when called will yield to the neovim scheduler to be able to call the api. +M.scheduler = a.wrap(vim.schedule, 1) + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/uv_async.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/uv_async.lua new file mode 100644 index 00000000..4ef0ebad --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async/uv_async.lua @@ -0,0 +1,82 @@ +local a = require "plenary.async.async" +local uv = vim.loop + +local M = {} + +local function add(name, argc) + local success, ret = pcall(a.wrap, uv[name], argc) + + if not success then + error("Failed to add function with name " .. name) + end + + M[name] = ret +end + +add("close", 4) -- close a handle + +-- filesystem operations +add("fs_open", 4) +add("fs_read", 4) +add("fs_close", 2) +add("fs_unlink", 2) +add("fs_write", 4) +add("fs_mkdir", 3) +add("fs_mkdtemp", 2) +-- 'fs_mkstemp', +add("fs_rmdir", 2) +add("fs_scandir", 2) +add("fs_stat", 2) +add("fs_fstat", 2) +add("fs_lstat", 2) +add("fs_rename", 3) +add("fs_fsync", 2) +add("fs_fdatasync", 2) +add("fs_ftruncate", 3) +add("fs_sendfile", 5) +add("fs_access", 3) +add("fs_chmod", 3) +add("fs_fchmod", 3) +add("fs_utime", 4) +add("fs_futime", 4) +-- 'fs_lutime', +add("fs_link", 3) +add("fs_symlink", 4) +add("fs_readlink", 2) +add("fs_realpath", 2) +add("fs_chown", 4) +add("fs_fchown", 4) +-- 'fs_lchown', +add("fs_copyfile", 4) +-- add('fs_opendir', 3) -- TODO: fix this one +add("fs_readdir", 2) +add("fs_closedir", 2) +-- 'fs_statfs', + +-- stream +add("shutdown", 2) +add("listen", 3) +-- add('read_start', 2) -- do not do this one, the callback is made multiple times +add("write", 3) +add("write2", 4) +add("shutdown", 2) + +-- tcp +add("tcp_connect", 4) +-- 'tcp_close_reset', + +-- pipe +add("pipe_connect", 3) + +-- udp +add("udp_send", 5) +add("udp_recv_start", 2) + +-- fs event (wip make into async await event) +-- fs poll event (wip make into async await event) + +-- dns +add("getaddrinfo", 4) +add("getnameinfo", 2) + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/api.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/api.lua new file mode 100644 index 00000000..baaf80c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/api.lua @@ -0,0 +1,15 @@ +local a = require "plenary.async_lib.async" +local async, await = a.async, a.await + +return setmetatable({}, { + __index = function(t, k) + return async(function(...) + -- if we are in a fast event await the scheduler + if vim.in_fast_event() then + await(a.scheduler()) + end + + vim.api[k](...) + end) + end, +}) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/async.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/async.lua new file mode 100644 index 00000000..84ca70dd --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/async.lua @@ -0,0 +1,213 @@ +local co = coroutine +local errors = require "plenary.errors" +local traceback_error = errors.traceback_error +local f = require "plenary.functional" +local tbl = require "plenary.tbl" + +local M = {} + +---because we can't store varargs +local function callback_or_next(step, thread, callback, ...) + local stat = f.first(...) + + if not stat then + error(string.format("The coroutine failed with this message: %s", f.second(...))) + end + + if co.status(thread) == "dead" then + (callback or function() end)(select(2, ...)) + else + assert(select("#", select(2, ...)) == 1, "expected a single return value") + local returned_future = f.second(...) + assert(type(returned_future) == "function", "type error :: expected func") + returned_future(step) + end +end + +---@class Future +---Something that will give a value when run + +---Executes a future with a callback when it is done +---@param future Future: the future to execute +---@param callback function: the callback to call when done +local execute = function(future, callback) + assert(type(future) == "function", "type error :: expected func") + local thread = co.create(future) + + local step + step = function(...) + callback_or_next(step, thread, callback, co.resume(thread, ...)) + end + + step() +end + +---Creates an async function with a callback style function. +---@param func function: A callback style function to be converted. The last argument must be the callback. +---@param argc number: The number of arguments of func. Must be included. +---@return function: Returns an async function +M.wrap = function(func, argc) + if type(func) ~= "function" then + traceback_error("type error :: expected func, got " .. type(func)) + end + + if type(argc) ~= "number" and argc ~= "vararg" then + traceback_error "expected argc to be a number or string literal 'vararg'" + end + + return function(...) + local params = tbl.pack(...) + + local function future(step) + if step then + if type(argc) == "number" then + params[argc] = step + params.n = argc + else + table.insert(params, step) -- change once not optional + params.n = params.n + 1 + end + + return func(tbl.unpack(params)) + else + return co.yield(future) + end + end + return future + end +end + +---Return a new future that when run will run all futures concurrently. +---@param futures table: the futures that you want to join +---@return Future: returns a future +M.join = M.wrap(function(futures, step) + local len = #futures + local results = {} + local done = 0 + + if len == 0 then + return step(results) + end + + for i, future in ipairs(futures) do + assert(type(future) == "function", "type error :: future must be function") + + local callback = function(...) + results[i] = { ... } + done = done + 1 + if done == len then + step(results) + end + end + + future(callback) + end +end, 2) + +---Returns a future that when run will select the first future that finishes +---@param futures table: The future that you want to select +---@return Future +M.select = M.wrap(function(futures, step) + local selected = false + + for _, future in ipairs(futures) do + assert(type(future) == "function", "type error :: future must be function") + + local callback = function(...) + if not selected then + selected = true + step(...) + end + end + + future(callback) + end +end, 2) + +---Use this to either run a future concurrently and then do something else +---or use it to run a future with a callback in a non async context +---@param future Future +---@param callback function +M.run = function(future, callback) + future(callback or function() end) +end + +---Same as run but runs multiple futures +---@param futures table +---@param callback function +M.run_all = function(futures, callback) + M.run(M.join(futures), callback) +end + +---Await a future, yielding the current function +---@param future Future +---@return any: returns the result of the future when it is done +M.await = function(future) + assert(type(future) == "function", "type error :: expected function to await") + return future(nil) +end + +---Same as await but can await multiple futures. +---If the futures have libuv leaf futures they will be run concurrently +---@param futures table +---@return table: returns a table of results that each future returned. Note that if the future returns multiple values they will be packed into a table. +M.await_all = function(futures) + assert(type(futures) == "table", "type error :: expected table") + return M.await(M.join(futures)) +end + +---suspend a coroutine +M.suspend = co.yield + +---create a async scope +M.scope = function(func) + M.run(M.future(func)) +end + +--- Future a :: a -> (a -> ()) +--- turns this signature +--- ... -> Future a +--- into this signature +--- ... -> () +M.void = function(async_func) + return function(...) + async_func(...)(function() end) + end +end + +M.async_void = function(func) + return M.void(M.async(func)) +end + +---creates an async function +---@param func function +---@return function: returns an async function +M.async = function(func) + if type(func) ~= "function" then + traceback_error("type error :: expected func, got " .. type(func)) + end + + return function(...) + local args = tbl.pack(...) + local function future(step) + if step == nil then + return func(tbl.unpack(args)) + else + execute(future, step) + end + end + return future + end +end + +---creates a future +---@param func function +---@return Future +M.future = function(func) + return M.async(func)() +end + +---An async function that when awaited will await the scheduler to be able to call the api. +M.scheduler = M.wrap(vim.schedule, 1) + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/init.lua new file mode 100644 index 00000000..4f1ce554 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/init.lua @@ -0,0 +1,41 @@ +---@brief [[ +--- NOTE: This API is still under construction. +--- It may change in the future :) +---@brief ]] + +local exports = require "plenary.async_lib.async" +exports.uv = require "plenary.async_lib.uv_async" +exports.util = require "plenary.async_lib.util" +exports.lsp = require "plenary.async_lib.lsp" +exports.api = require "plenary.async_lib.api" +exports.tests = require "plenary.async_lib.tests" + +exports.tests.add_globals = function() + a = exports + async = exports.async + await = exports.await + await_all = exports.await_all + + -- must prefix with a or stack overflow, plenary.test harness already added it + a.describe = exports.tests.describe + -- must prefix with a or stack overflow + a.it = exports.tests.it +end + +exports.tests.add_to_env = function() + local env = getfenv(2) + + env.a = exports + env.async = exports.async + env.await = exports.await + env.await_all = exports.await_all + + -- must prefix with a or stack overflow, plenary.test harness already added it + env.a.describe = exports.tests.describe + -- must prefix with a or stack overflow + env.a.it = exports.tests.it + + setfenv(2, env) +end + +return exports diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/lsp.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/lsp.lua new file mode 100644 index 00000000..924a7631 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/lsp.lua @@ -0,0 +1,15 @@ +local a = require "plenary.async_lib.async" + +local M = {} + +---This will be deprecated because the callback can be called multiple times. +---This will give a coroutine error because the coroutine will be resumed multiple times. +---Please use buf_request_all instead. +M.buf_request = a.wrap(vim.lsp.buf_request, 4) + +--This was recently merged into master so we just check if it is there +if vim.lsp.buf_request_all ~= nil then + M.buf_request_all = a.wrap(vim.lsp.buf_request_all, 4) +end + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/structs.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/structs.lua new file mode 100644 index 00000000..73252e40 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/structs.lua @@ -0,0 +1,116 @@ +local M = {} + +Deque = {} +Deque.__index = Deque + +---@class Deque +---A double ended queue +--- +---@return Deque +function Deque.new() + -- the indexes are created with an offset so that the indices are consequtive + -- otherwise, when both pushleft and pushright are used, the indices will have a 1 length hole in the middle + return setmetatable({ first = 0, last = -1 }, Deque) +end + +---push to the left of the deque +---@param value any +function Deque:pushleft(value) + local first = self.first - 1 + self.first = first + self[first] = value +end + +---push to the right of the deque +---@param value any +function Deque:pushright(value) + local last = self.last + 1 + self.last = last + self[last] = value +end + +---pop from the left of the deque +---@return any +function Deque:popleft() + local first = self.first + if first > self.last then + return nil + end + local value = self[first] + self[first] = nil -- to allow garbage collection + self.first = first + 1 + return value +end + +---pops from the right of the deque +---@return any +function Deque:popright() + local last = self.last + if self.first > last then + return nil + end + local value = self[last] + self[last] = nil -- to allow garbage collection + self.last = last - 1 + return value +end + +---checks if the deque is empty +---@return boolean +function Deque:is_empty() + return self:len() == 0 +end + +---returns the number of elements of the deque +---@return number +function Deque:len() + return self.last - self.first + 1 +end + +---returns and iterator of the indices and values starting from the left +---@return function +function Deque:ipairs_left() + local i = self.first + + return function() + local res = self[i] + local idx = i + + if res then + i = i + 1 + + return idx, res + end + end +end + +---returns and iterator of the indices and values starting from the right +---@return function +function Deque:ipairs_right() + local i = self.last + + return function() + local res = self[i] + local idx = i + + if res then + i = i - 1 -- advance the iterator before we return + + return idx, res + end + end +end + +---removes all values from the deque +---@return nil +function Deque:clear() + for i, _ in self:ipairs_left() do + self[i] = nil + end + self.first = 0 + self.last = -1 +end + +M.Deque = Deque + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/tests.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/tests.lua new file mode 100644 index 00000000..8dff05b3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/tests.lua @@ -0,0 +1,14 @@ +local a = require "plenary.async_lib.async" +local util = require "plenary.async_lib.util" + +local M = {} + +M.describe = function(s, func) + describe(s, util.will_block(a.future(func))) +end + +M.it = function(s, func) + it(s, util.will_block(a.future(func))) +end + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/util.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/util.lua new file mode 100644 index 00000000..1de65fe4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/util.lua @@ -0,0 +1,339 @@ +local a = require "plenary.async_lib.async" +local await = a.await +local async = a.async +local co = coroutine +local Deque = require("plenary.async_lib.structs").Deque +local uv = vim.loop + +local M = {} + +---Sleep for milliseconds +---@param ms number +M.sleep = a.wrap(function(ms, callback) + local timer = uv.new_timer() + uv.timer_start(timer, ms, 0, function() + uv.timer_stop(timer) + uv.close(timer) + callback() + end) +end, 2) + +---Takes a future and a millisecond as the timeout. +---If the time is reached and the future hasn't completed yet, it will short circuit the future +---NOTE: the future will still be running in libuv, we are just not waiting for it to complete +---thats why you should call this on a leaf future only to avoid unexpected results +---@param future Future +---@param ms number +M.timeout = a.wrap(function(future, ms, callback) + -- make sure that the callback isn't called twice, or else the coroutine can be dead + local done = false + + local timeout_callback = function(...) + if not done then + done = true + callback(false, ...) -- false because it has run normally + end + end + + vim.defer_fn(function() + if not done then + done = true + callback(true) -- true because it has timed out + end + end, ms) + + a.run(future, timeout_callback) +end, 3) + +---create an async function timer +---@param ms number +M.timer = function(ms) + return async(function() + await(M.sleep(ms)) + end) +end + +---id function that can be awaited +---@param nil ... +---@return ... +M.id = async(function(...) + return ... +end) + +---Running this function will yield now and do nothing else +M.yield_now = async(function() + await(M.id()) +end) + +local Condvar = {} +Condvar.__index = Condvar + +---@class Condvar +---@return Condvar +function Condvar.new() + return setmetatable({ handles = {} }, Condvar) +end + +---`blocks` the thread until a notification is received +Condvar.wait = a.wrap(function(self, callback) + -- not calling the callback will block the coroutine + table.insert(self.handles, callback) +end, 2) + +---notify everyone that is waiting on this Condvar +function Condvar:notify_all() + if #self.handles == 0 then + return + end + + for i, callback in ipairs(self.handles) do + callback() + self.handles[i] = nil + end +end + +---notify randomly one person that is waiting on this Condvar +function Condvar:notify_one() + if #self.handles == 0 then + return + end + + local idx = math.random(#self.handles) + self.handles[idx]() + table.remove(self.handles, idx) +end + +M.Condvar = Condvar + +local Semaphore = {} +Semaphore.__index = Semaphore + +---@class Semaphore +---@param initial_permits number: the number of permits that it can give out +---@return Semaphore +function Semaphore.new(initial_permits) + vim.validate { + initial_permits = { + initial_permits, + function(n) + return n > 0 + end, + "number greater than 0", + }, + } + + return setmetatable({ permits = initial_permits, handles = {} }, Semaphore) +end + +---async function, blocks until a permit can be acquired +---example: +---local semaphore = Semaphore.new(1024) +---local permit = await(semaphore:acquire()) +---permit:forget() +---when a permit can be acquired returns it +---call permit:forget() to forget the permit +Semaphore.acquire = a.wrap(function(self, callback) + if self.permits > 0 then + self.permits = self.permits - 1 + else + table.insert(self.handles, callback) + return + end + + local permit = {} + + permit.forget = function(self_permit) + self.permits = self.permits + 1 + + if self.permits > 0 and #self.handles > 0 then + self.permits = self.permits - 1 + local callback = table.remove(self.handles) + callback(self_permit) + end + end + + callback(permit) +end, 2) + +M.Semaphore = Semaphore + +M.channel = {} + +---Creates a oneshot channel +---returns a sender and receiver function +---the sender is not async while the receiver is +---@return function, function +M.channel.oneshot = function() + local val = nil + local saved_callback = nil + local sent = false + local received = false + + --- sender is not async + --- sends a value + local sender = function(...) + if sent then + error "Oneshot channel can only send once" + end + + sent = true + + local args = { ... } + + if saved_callback then + saved_callback(unpack(val or args)) + else + val = args + end + end + + --- receiver is async + --- blocks until a value is received + local receiver = a.wrap(function(callback) + if received then + error "Oneshot channel can only send one value!" + end + + if val then + received = true + callback(unpack(val)) + else + saved_callback = callback + end + end, 1) + + return sender, receiver +end + +---A counter channel. +---Basically a channel that you want to use only to notify and not to send any actual values. +---@return function: sender +---@return function: receiver +M.channel.counter = function() + local counter = 0 + local condvar = Condvar.new() + + local Sender = {} + + function Sender:send() + counter = counter + 1 + condvar:notify_all() + end + + local Receiver = {} + + Receiver.recv = async(function() + if counter == 0 then + await(condvar:wait()) + end + counter = counter - 1 + end) + + Receiver.last = async(function() + if counter == 0 then + await(condvar:wait()) + end + counter = 0 + end) + + return Sender, Receiver +end + +---A multiple producer single consumer channel +---@return table +---@return table +M.channel.mpsc = function() + local deque = Deque.new() + local condvar = Condvar.new() + + local Sender = {} + + function Sender.send(...) + deque:pushleft { ... } + condvar:notify_all() + end + + local Receiver = {} + + Receiver.recv = async(function() + if deque:is_empty() then + await(condvar:wait()) + end + return unpack(deque:popright()) + end) + + Receiver.last = async(function() + if deque:is_empty() then + await(condvar:wait()) + end + local val = deque:popright() + deque:clear() + return unpack(val) + end) + + return Sender, Receiver +end + +local pcall_wrap = function(func) + return function(...) + return pcall(func, ...) + end +end + +---Makes a future protected. It is like pcall but for futures. +---Only works for non-leaf futures +M.protected_non_leaf = async(function(future) + return await(pcall_wrap(future)) +end) + +---Makes a future protected. It is like pcall but for futures. +---@param future Future +---@return Future +M.protected = async(function(future) + local tx, rx = M.channel.oneshot() + + stat, ret = pcall(future, tx) + + if stat == true then + return stat, await(rx()) + else + return stat, ret + end +end) + +---This will COMPLETELY block neovim +---please just use a.run unless you have a very special usecase +---for example, in plenary test_harness you must use this +---@param future Future +---@param timeout number: Stop blocking if the timeout was surpassed. Default 2000. +M.block_on = function(future, timeout) + future = M.protected(future) + + local stat, ret + a.run(future, function(_stat, ...) + stat = _stat + ret = { ... } + end) + + local function check() + if stat == false then + error("Blocking on future failed " .. unpack(ret)) + end + return stat == true + end + + if not vim.wait(timeout or 2000, check, 20, false) then + error "Blocking on future timed out or was interrupted" + end + + return unpack(ret) +end + +---Returns a new future that WILL BLOCK +---@param future Future +---@return Future +M.will_block = async(function(future) + return M.block_on(future) +end) + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/uv_async.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/uv_async.lua new file mode 100644 index 00000000..4ff4ef6d --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/async_lib/uv_async.lua @@ -0,0 +1,82 @@ +local a = require "plenary.async_lib.async" +local uv = vim.loop + +local M = {} + +local function add(name, argc) + local success, ret = pcall(a.wrap, uv[name], argc) + + if not success then + error("Failed to add function with name " .. name) + end + + M[name] = ret +end + +add("close", 4) -- close a handle + +-- filesystem operations +add("fs_open", 4) +add("fs_read", 4) +add("fs_close", 2) +add("fs_unlink", 2) +add("fs_write", 4) +add("fs_mkdir", 3) +add("fs_mkdtemp", 2) +-- 'fs_mkstemp', +add("fs_rmdir", 2) +add("fs_scandir", 2) +add("fs_stat", 2) +add("fs_fstat", 2) +add("fs_lstat", 2) +add("fs_rename", 3) +add("fs_fsync", 2) +add("fs_fdatasync", 2) +add("fs_ftruncate", 3) +add("fs_sendfile", 5) +add("fs_access", 3) +add("fs_chmod", 3) +add("fs_fchmod", 3) +add("fs_utime", 4) +add("fs_futime", 4) +-- 'fs_lutime', +add("fs_link", 3) +add("fs_symlink", 4) +add("fs_readlink", 2) +add("fs_realpath", 2) +add("fs_chown", 4) +add("fs_fchown", 4) +-- 'fs_lchown', +add("fs_copyfile", 4) +-- add('fs_opendir', 3) -- TODO: fix this one +add("fs_readdir", 2) +add("fs_closedir", 2) +-- 'fs_statfs', + +-- stream +add("shutdown", 2) +add("listen", 3) +-- add('read_start', 2) -- do not do this one, the callback is made multiple times +add("write", 3) +add("write2", 4) +add("shutdown", 2) + +-- tcp +add("tcp_connect", 4) +-- 'tcp_close_reset', + +-- pipe +add("pipe_connect", 3) + +-- udp +add("udp_send", 5) +add("udp_recv_start", 2) + +-- fs event (wip make into async await event) +-- fs poll event (wip make into async await event) + +-- dns +add("getaddrinfo", 4) +add("getnameinfo", 2) + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/benchmark/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/benchmark/init.lua new file mode 100644 index 00000000..ec329404 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/benchmark/init.lua @@ -0,0 +1,125 @@ +local stat = require "plenary.benchmark.stat" + +local get_stats = function(results) + local ret = {} + + ret.max, ret.min = stat.maxmin(results) + ret.mean = stat.mean(results) + ret.median = stat.median(results) + ret.std = stat.std_dev(results) + + return ret +end + +local get_output = function(index, res, runs) + -- divine with a sutable one / 1e3, 1e6, 1e9 + local time_types = { "ns", "μs", "ms" } + + local get_leading = function(time) + time = math.floor(time) + local count = 0 + repeat + time = math.floor(time / 10) + count = count + 1 + until time <= 0 + return count + end + + local get_best_fmt = function(time) + for _, v in ipairs(time_types) do + if math.abs(time) < 1000.0 then + return string.format("%s%3.1f %s", string.rep(" ", 3 - get_leading(time)), time, v) + end + time = time / 1000.0 + end + return string.format("%.1f %s", time, "s") + end + + return string.format( + "Benchmark #%d: '%s'\n Time(mean ± σ): %s ± %s\n Range(min … max): %s … %s %d runs\n", + index, + res.name, + get_best_fmt(res.stats.mean), + get_best_fmt(res.stats.std), + get_best_fmt(res.stats.min), + get_best_fmt(res.stats.max), + runs + ) +end + +local get_summary = function(res) + if #res == 1 then + return "" + end + + local fastest_mean = math.huge + local fastest_index = 1 + for i, benchmark in ipairs(res) do + if benchmark.stats.mean < fastest_mean then + fastest_mean = benchmark.stats.mean + fastest_index = i + end + end + + if fastest_mean == math.huge then + return "" + end + + local output = {} + local fastest = res[fastest_index].stats + for i, benchmark in ipairs(res) do + if i ~= fastest_index then + local result = benchmark.stats + local ratio = result.mean / fastest.mean + + -- // https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulas + -- // Covariance asssumed to be 0, i.e. variables are assumed to be independent + local ratio_std = ratio + * math.sqrt(math.pow(result.std / result.mean, 2) + math.pow(fastest.std / fastest.mean, 2)) + + table.insert(output, string.format(" %.1f ± %.1f times faster than '%s'\n", ratio, ratio_std, benchmark.name)) + end + end + + return string.format("Summary\n '%s' ran\n%s", res[fastest_index].name, table.concat(output, "")) +end + +---@class benchmark_run_opts +---@field warmup number @number of initial runs before starting to track time. +---@field runs number @number of runs to make +---@field fun table> @functions to execute + +---Benchmark a function +---@param name string @benchmark name +---@param opts benchmark_run_opts +local bench = function(name, opts) + vim.validate { + opts = { opts, "table" }, + fun = { opts.fun, "table" }, + } + opts.warmup = vim.F.if_nil(opts.warmup, 3) + opts.runs = vim.F.if_nil(opts.runs, 5) + + opts.fun = type(opts.fun) == "function" and { opts.fun } or opts.fun + local output = { string.format("Benchmark Group: '%s' -----------------------\n", name) } + local res = {} + for i, fun in ipairs(opts.fun) do + res[i] = { name = fun[1], results = {} } + for _ = 1, opts.warmup do + fun[2]() + end + for j = 1, opts.runs do + local start = vim.loop.hrtime() + fun[2]() + res[i].results[j] = vim.loop.hrtime() - start + end + res[i].stats = get_stats(res[i].results) + table.insert(output, get_output(i, res[i], opts.runs)) + end + + print(string.format("%s\n%s", table.concat(output, ""), get_summary(res))) + + return res +end + +return bench diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/benchmark/stat.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/benchmark/stat.lua new file mode 100644 index 00000000..63c1f6ba --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/benchmark/stat.lua @@ -0,0 +1,86 @@ +local stat = {} + +---Calculate mean +---@param t number[] @double +---@return number @double +stat.mean = function(t) + local sum = 0 + local count = 0 + + for _, v in pairs(t) do + if type(v) == "number" then + sum = sum + v + count = count + 1 + end + end + + return (sum / count) +end + +-- Get the median of a table. +---@param t number[] +---@return number +stat.median = function(t) + local temp = {} + + -- deep copy table so that when we sort it, the original is unchanged + -- also weed out any non numbers + for _, v in pairs(t) do + if type(v) == "number" then + table.insert(temp, v) + end + end + + table.sort(temp) + + -- If we have an even number of table elements or odd. + if math.fmod(#temp, 2) == 0 then + -- return mean value of middle two elements + return (temp[#temp / 2] + temp[(#temp / 2) + 1]) / 2 + else + -- return middle element + return temp[math.ceil(#temp / 2)] + end +end + +--- Get the standard deviation of a table +---@param t number[] +stat.std_dev = function(t) + local m, vm, result + local sum = 0 + local count = 0 + + m = stat.mean(t) + + for _, v in pairs(t) do + if type(v) == "number" then + vm = v - m + sum = sum + (vm * vm) + count = count + 1 + end + end + + result = math.sqrt(sum / (count - 1)) + + return result +end + +---Get the max and min for a table +---@param t number[] +---@return number +---@return number +stat.maxmin = function(t) + local max = -math.huge + local min = math.huge + + for _, v in pairs(t) do + if type(v) == "number" then + max = math.max(max, v) + min = math.min(min, v) + end + end + + return max, min +end + +return stat diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/bit.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/bit.lua new file mode 100644 index 00000000..68a6091b --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/bit.lua @@ -0,0 +1,339 @@ +-- Shortcircuit to returning bit if it already exists +if bit then return bit end + +--[[ + +Credit: https://github.com/davidm/lua-bit-numberlua/blob/master/lmod/bit/numberlua.lua + +LUA MODULE + + bit.numberlua - Bitwise operations implemented in pure Lua as numbers, + with Lua 5.2 'bit32' and (LuaJIT) LuaBitOp 'bit' compatibility interfaces. + +SYNOPSIS + + local bit = require 'bit.numberlua' + print(bit.band(0xff00ff00, 0x00ff00ff)) --> 0xffffffff + + -- Interface providing strong (LuaJIT) LuaBitOp 'bit' compatibility + local bit = require 'plenary.bit' + assert(bit.tobit(0xffffffff) == -1) + + REMOVED! + -- Interface providing strong Lua 5.2 'bit32' compatibility + local bit32 = require 'bit.numberlua'.bit32 + assert(bit32.band(-1) == 0xffffffff) + + +DESCRIPTION + + This library implements bitwise operations entirely in Lua. + This module is typically intended if for some reasons you don't want + to or cannot install a popular C based bit library like BitOp 'bit' [1] + (which comes pre-installed with LuaJIT) or 'bit32' (which comes + pre-installed with Lua 5.2) but want a similar interface. + + This modules represents bit arrays as non-negative Lua numbers. [1] + It can represent 32-bit bit arrays when Lua is compiled + with lua_Number as double-precision IEEE 754 floating point. + + The module is nearly the most efficient it can be but may be a few times + slower than the C based bit libraries and is orders or magnitude + slower than LuaJIT bit operations, which compile to native code. Therefore, + this library is inferior in performane to the other modules. + + The `xor` function in this module is based partly on Roberto Ierusalimschy's + post in http://lua-users.org/lists/lua-l/2002-09/msg00134.html . + + The included BIT.bit32 and BIT.bit sublibraries aims to provide 100% + compatibility with the Lua 5.2 "bit32" and (LuaJIT) LuaBitOp "bit" library. + This compatbility is at the cost of some efficiency since inputted + numbers are normalized and more general forms (e.g. multi-argument + bitwise operators) are supported. + +STATUS + + WARNING: Not all corner cases have been tested and documented. + Some attempt was made to make these similar to the Lua 5.2 [2] + and LuaJit BitOp [3] libraries, but this is not fully tested and there + are currently some differences. Addressing these differences may + be improved in the future but it is not yet fully determined how to + resolve these differences. + + The BIT.bit32 library passes the Lua 5.2 test suite (bitwise.lua) + http://www.lua.org/tests/5.2/ . The BIT.bit library passes the LuaBitOp + test suite (bittest.lua). However, these have not been tested on + platforms with Lua compiled with 32-bit integer numbers. + +API + + Module's return + + This table contains functions that aim to provide 100% compatibility + with the LuaBitOp "bit" library (from LuaJIT). + + bit.tobit(x) --> y + bit.tohex(x [,n]) --> y + bit.bnot(x) --> y + bit.bor(x1 [,x2...]) --> y + bit.band(x1 [,x2...]) --> y + bit.bxor(x1 [,x2...]) --> y + bit.lshift(x, n) --> y + bit.rshift(x, n) --> y + bit.arshift(x, n) --> y + bit.rol(x, n) --> y + bit.ror(x, n) --> y + bit.bswap(x) --> y + +DEPENDENCIES + + None (other than Lua 5.1 or 5.2). + +REFERENCES + + [1] http://lua-users.org/wiki/FloatingPoint + [2] http://www.lua.org/manual/5.2/ + [3] http://bitop.luajit.org/ + +LICENSE + + (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + (end license) + + Some modifications by plenary team. + +--]] + +local M = {_TYPE='module', _NAME='bit.numberlua', _VERSION='0.3.1.20120131'} + +local floor = math.floor + +local MOD = 2^32 +local MODM = MOD-1 + +local function memoize(f) + local mt = {} + local t = setmetatable({}, mt) + function mt:__index(k) + local v = f(k); t[k] = v + return v + end + return t +end + +local function make_bitop_uncached(t, m) + local function bitop(a, b) + local res,p = 0,1 + while a ~= 0 and b ~= 0 do + local am, bm = a%m, b%m + res = res + t[am][bm]*p + a = (a - am) / m + b = (b - bm) / m + p = p*m + end + res = res + (a+b)*p + return res + end + return bitop +end + +local function make_bitop(t) + local op1 = make_bitop_uncached(t,2^1) + local op2 = memoize(function(a) + return memoize(function(b) + return op1(a, b) + end) + end) + return make_bitop_uncached(op2, 2^(t.n or 1)) +end + +-- ok? probably not if running on a 32-bit int Lua number type platform +function M.tobit(x) + return x % 2^32 +end + +M.bxor = make_bitop {[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0}, n=4} +local bxor = M.bxor + +function M.bnot(a) return MODM - a end +local bnot = M.bnot + +function M.band(a,b) return ((a+b) - bxor(a,b))/2 end +local band = M.band + +function M.bor(a,b) return MODM - band(MODM - a, MODM - b) end +local bor = M.bor + +local lshift, rshift -- forward declare + +function M.rshift(a,disp) -- Lua5.2 insipred + if disp < 0 then return lshift(a,-disp) end + return floor(a % 2^32 / 2^disp) +end +rshift = M.rshift + +function M.lshift(a,disp) -- Lua5.2 inspired + if disp < 0 then return rshift(a,-disp) end + return (a * 2^disp) % 2^32 +end +lshift = M.lshift + +function M.tohex(x, n) -- BitOp style + n = n or 8 + local up + if n <= 0 then + if n == 0 then return '' end + up = true + n = - n + end + x = band(x, 16^n-1) + return ('%0'..n..(up and 'X' or 'x')):format(x) +end +local tohex = M.tohex + +function M.extract(n, field, width) -- Lua5.2 inspired + width = width or 1 + return band(rshift(n, field), 2^width-1) +end + +function M.replace(n, v, field, width) -- Lua5.2 inspired + width = width or 1 + local mask1 = 2^width-1 + v = band(v, mask1) -- required by spec? + local mask = bnot(lshift(mask1, field)) + return band(n, mask) + lshift(v, field) +end + +function M.bswap(x) -- BitOp style + local a = band(x, 0xff); x = rshift(x, 8) + local b = band(x, 0xff); x = rshift(x, 8) + local c = band(x, 0xff); x = rshift(x, 8) + local d = band(x, 0xff) + return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d +end +local bswap = M.bswap + +function M.rrotate(x, disp) -- Lua5.2 inspired + disp = disp % 32 + local low = band(x, 2^disp-1) + return rshift(x, disp) + lshift(low, 32-disp) +end +local rrotate = M.rrotate + +function M.lrotate(x, disp) -- Lua5.2 inspired + return rrotate(x, -disp) +end +local lrotate = M.lrotate + +M.rol = M.lrotate -- LuaOp inspired +M.ror = M.rrotate -- LuaOp insipred + + +function M.arshift(x, disp) -- Lua5.2 inspired + local z = rshift(x, disp) + if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end + return z +end +local arshift = M.arshift + +function M.btest(x, y) -- Lua5.2 inspired + return band(x, y) ~= 0 +end + +-- +-- Start LuaBitOp "bit" compat section. +-- + +M.bit = {} -- LuaBitOp "bit" compatibility + +function M.bit.tobit(x) + x = x % MOD + if x >= 0x80000000 then x = x - MOD end + return x +end +local bit_tobit = M.bit.tobit + +function M.bit.tohex(x, ...) + return tohex(x % MOD, ...) +end + +function M.bit.bnot(x) + return bit_tobit(bnot(x % MOD)) +end + +local function bit_bor(a, b, c, ...) + if c then + return bit_bor(bit_bor(a, b), c, ...) + elseif b then + return bit_tobit(bor(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.bor = bit_bor + +local function bit_band(a, b, c, ...) + if c then + return bit_band(bit_band(a, b), c, ...) + elseif b then + return bit_tobit(band(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.band = bit_band + +local function bit_bxor(a, b, c, ...) + if c then + return bit_bxor(bit_bxor(a, b), c, ...) + elseif b then + return bit_tobit(bxor(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.bxor = bit_bxor + +function M.bit.lshift(x, n) + return bit_tobit(lshift(x % MOD, n % 32)) +end + +function M.bit.rshift(x, n) + return bit_tobit(rshift(x % MOD, n % 32)) +end + +function M.bit.arshift(x, n) + return bit_tobit(arshift(x % MOD, n % 32)) +end + +function M.bit.rol(x, n) + return bit_tobit(lrotate(x % MOD, n % 32)) +end + +function M.bit.ror(x, n) + return bit_tobit(rrotate(x % MOD, n % 32)) +end + +function M.bit.bswap(x) + return bit_tobit(bswap(x % MOD)) +end + +return M.bit diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/busted.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/busted.lua new file mode 100644 index 00000000..ed1b68d5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/busted.lua @@ -0,0 +1,271 @@ +local dirname = function(p) + return vim.fn.fnamemodify(p, ":h") +end + +local function get_trace(element, level, msg) + local function trimTrace(info) + local index = info.traceback:find "\n%s*%[C]" + info.traceback = info.traceback:sub(1, index) + return info + end + level = level or 3 + + local thisdir = dirname(debug.getinfo(1, "Sl").source, ":h") + local info = debug.getinfo(level, "Sl") + while + info.what == "C" + or info.short_src:match "luassert[/\\].*%.lua$" + or (info.source:sub(1, 1) == "@" and thisdir == dirname(info.source)) + do + level = level + 1 + info = debug.getinfo(level, "Sl") + end + + info.traceback = debug.traceback("", level) + info.message = msg + + -- local file = busted.getFile(element) + local file = false + return file and file.getTrace(file.name, info) or trimTrace(info) +end + +local is_headless = require("plenary.nvim_meta").is_headless + +-- We are shadowing print so people can reliably print messages +print = function(...) + for _, v in ipairs { ... } do + io.stdout:write(tostring(v)) + io.stdout:write "\t" + end + + io.stdout:write "\r\n" +end + +local mod = {} + +local results = {} +local current_description = {} +local current_before_each = {} +local current_after_each = {} + +local add_description = function(desc) + table.insert(current_description, desc) + + return vim.deepcopy(current_description) +end + +local pop_description = function() + current_description[#current_description] = nil +end + +local add_new_each = function() + current_before_each[#current_description] = {} + current_after_each[#current_description] = {} +end + +local clear_last_each = function() + current_before_each[#current_description] = nil + current_after_each[#current_description] = nil +end + +local call_inner = function(desc, func) + local desc_stack = add_description(desc) + add_new_each() + local ok, msg = xpcall(func, function(msg) + -- debug.traceback + -- return vim.inspect(get_trace(nil, 3, msg)) + local trace = get_trace(nil, 3, msg) + return trace.message .. "\n" .. trace.traceback + end) + clear_last_each() + pop_description() + + return ok, msg, desc_stack +end + +local color_table = { + yellow = 33, + green = 32, + red = 31, +} + +local color_string = function(color, str) + if not is_headless then + return str + end + + return string.format("%s[%sm%s%s[%sm", string.char(27), color_table[color] or 0, str, string.char(27), 0) +end + +local SUCCESS = color_string("green", "Success") +local FAIL = color_string("red", "Fail") +local PENDING = color_string("yellow", "Pending") + +local HEADER = string.rep("=", 40) + +mod.format_results = function(res) + print "" + print(color_string("green", "Success: "), #res.pass) + print(color_string("red", "Failed : "), #res.fail) + print(color_string("red", "Errors : "), #res.errs) + print(HEADER) +end + +mod.describe = function(desc, func) + results.pass = results.pass or {} + results.fail = results.fail or {} + results.errs = results.errs or {} + + describe = mod.inner_describe + local ok, msg, desc_stack = call_inner(desc, func) + describe = mod.describe + + if not ok then + table.insert(results.errs, { + descriptions = desc_stack, + msg = msg, + }) + end +end + +mod.inner_describe = function(desc, func) + local ok, msg, desc_stack = call_inner(desc, func) + + if not ok then + table.insert(results.errs, { + descriptions = desc_stack, + msg = msg, + }) + end +end + +mod.before_each = function(fn) + table.insert(current_before_each[#current_description], fn) +end + +mod.after_each = function(fn) + table.insert(current_after_each[#current_description], fn) +end + +mod.clear = function() + vim.api.nvim_buf_set_lines(0, 0, -1, false, {}) +end + +local indent = function(msg, spaces) + if spaces == nil then + spaces = 4 + end + + local prefix = string.rep(" ", spaces) + return prefix .. msg:gsub("\n", "\n" .. prefix) +end + +local run_each = function(tbl) + for _, v in ipairs(tbl) do + for _, w in ipairs(v) do + if type(w) == "function" then + w() + end + end + end +end + +mod.it = function(desc, func) + run_each(current_before_each) + local ok, msg, desc_stack = call_inner(desc, func) + run_each(current_after_each) + + local test_result = { + descriptions = desc_stack, + msg = nil, + } + + -- TODO: We should figure out how to determine whether + -- and assert failed or whether it was an error... + + local to_insert + if not ok then + to_insert = results.fail + test_result.msg = msg + + print(FAIL, "||", table.concat(test_result.descriptions, " ")) + print(indent(msg, 12)) + else + to_insert = results.pass + print(SUCCESS, "||", table.concat(test_result.descriptions, " ")) + end + + table.insert(to_insert, test_result) +end + +mod.pending = function(desc, func) + local curr_stack = vim.deepcopy(current_description) + table.insert(curr_stack, desc) + print(PENDING, "||", table.concat(curr_stack, " ")) +end + +_PlenaryBustedOldAssert = _PlenaryBustedOldAssert or assert + +describe = mod.describe +it = mod.it +pending = mod.pending +before_each = mod.before_each +after_each = mod.after_each +clear = mod.clear +---@type Luassert +assert = require "luassert" + +mod.run = function(file) + file = file:gsub("\\", "/") + + print("\n" .. HEADER) + print("Testing: ", file) + + local loaded, msg = loadfile(file) + + if not loaded then + print(HEADER) + print "FAILED TO LOAD FILE" + print(color_string("red", msg)) + print(HEADER) + if is_headless then + return vim.cmd "2cq" + else + return + end + end + + coroutine.wrap(function() + loaded() + + -- If nothing runs (empty file without top level describe) + if not results.pass then + if is_headless then + return vim.cmd "0cq" + else + return + end + end + + mod.format_results(results) + + if #results.errs ~= 0 then + print("We had an unexpected error: ", vim.inspect(results.errs), vim.inspect(results)) + if is_headless then + return vim.cmd "2cq" + end + elseif #results.fail > 0 then + print "Tests Failed. Exit: 1" + + if is_headless then + return vim.cmd "1cq" + end + else + if is_headless then + return vim.cmd "0cq" + end + end + end)() +end + +return mod diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/class.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/class.lua new file mode 100644 index 00000000..e82f073c --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/class.lua @@ -0,0 +1,80 @@ +---@brief [[ +---classic +--- +---Copyright (c) 2014, rxi +---@brief ]] + +---@class Object +local Object = {} +Object.__index = Object + +---Does nothing. +---You have to implement this yourself for extra functionality when initializing +---@param self Object +function Object:new() end + +---Create a new class/object by extending the base Object class. +---The extended object will have a field called `super` that will access the super class. +---@param self Object +---@return Object +function Object:extend() + local cls = {} + for k, v in pairs(self) do + if k:find "__" == 1 then + cls[k] = v + end + end + cls.__index = cls + cls.super = self + setmetatable(cls, self) + return cls +end + +---Implement a mixin onto this Object. +---@param self Object +---@param nil ... +function Object:implement(...) + for _, cls in pairs { ... } do + for k, v in pairs(cls) do + if self[k] == nil and type(v) == "function" then + self[k] = v + end + end + end +end + +---Checks if the object is an instance +---This will start with the lowest class and loop over all the superclasses. +---@param self Object +---@param T Object +---@return boolean +function Object:is(T) + local mt = getmetatable(self) + while mt do + if mt == T then + return true + end + mt = getmetatable(mt) + end + return false +end + +---The default tostring implementation for an object. +---You can override this to provide a different tostring. +---@param self Object +---@return string +function Object:__tostring() + return "Object" +end + +---You can call the class the initialize it without using `Object:new`. +---@param self Object +---@param nil ... +---@return Object +function Object:__call(...) + local obj = setmetatable({}, self) + obj:new(...) + return obj +end + +return Object diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/collections/py_list.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/collections/py_list.lua new file mode 100644 index 00000000..25df4b03 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/collections/py_list.lua @@ -0,0 +1,393 @@ +---@brief [[ +--- This module implements python-like lists. It can be used like so: +---
+---     local List = require 'plenary.collections.py_list'
+---     local l = List{3, 20, 44}
+---     print(l)  -- [3, 20, 44]
+--- 
+---@brief ]] +local List = {} + +---@class List @The base class for all list objects + +---List constructor. Can be used in higher order functions +---@param tbl table: A list-like table containing the initial elements of the list +---@return List: A new list object +function List.new(tbl) + if type(tbl) == "table" then + local len = #tbl + local obj = setmetatable(tbl, List) + obj._len = len + return obj + end + error "List constructor must be called with table argument" +end + +--- Checks whether the argument is a List object +--- @param tbl table: The object to test +--- @return boolean: Whether tbl is an instance of List +function List.is_list(tbl) + local meta = getmetatable(tbl) or {} + return meta == List +end + +function List:__index(key) + if self ~= List then + local field = List[key] + if field then + return field + end + end +end + +-- TODO: Similar to python, use [...] if the table references itself -- +function List:__tostring() + local elements = self:join ", " + return "[" .. elements .. "]" +end + +function List:__eq(other) + if #self ~= #other then + return false + end + for i = 1, #self do + if self[i] ~= other[i] then + return false + end + end + return true +end + +function List:__mul(other) + local result = List.new {} + for i = 1, other do + result[i] = self + end + return result +end + +function List:__len() + return self._len +end + +function List:__concat(other) + return self:concat(other) +end + +--- Pushes the element to the end of the list +--- @param other any: The object to append +--- @see List.pop +function List:push(other) + self[#self + 1] = other + self._len = self._len + 1 +end + +--- Pops the last element off the list and returns it +--- @return any: The (previously) last element from the list +--- @see List.push +function List:pop() + local result = table.remove(self) + self._len = self._len - 1 + return result +end + +--- Inserts other into the specified idx +--- @param idx number: The index that other will be inserted to +--- @param other any: The element to insert +--- @see List.remove +function List:insert(idx, other) + table.insert(self, idx, other) + self._len = self._len + 1 +end + +--- Removes the element at index idx and returns it +--- @param idx number: The index of the element to remove +--- @return any: The element previously at index idx +--- @see List.insert +function List:remove(idx) + self._len = self._len - 1 + return table.remove(self, idx) +end + +--- Can be used to compare elements with any list-like table. It only checks for +--- shallow equality +--- @param other any: The element to test for +--- @return boolean: True if other is a list object and all it's elements are equal +--- @see List.deep_equal +function List:equal(other) + return self:__eq(other) +end + +--- Checks for deep equality between lists. This uses vim.deep_equal for testing +--- @param other any: The element to test for +--- @return boolean: True if all elements and their children are equal +--- @see List.equal +--- @see vim.deep_equal +function List:deep_equal(other) + return vim.deep_equal(self, other) +end + +--- Returns a copy of the list with elements between a and b, inclusive +---
+---     local list = List{1, 2, 3, 4}
+---     local slice = list:slice(2, 3)
+---     print(slice) -- [2, 3]
+--- 
+--- @param a number: The low end of the slice +--- @param b number: The high end of the slice +--- @return List: A list with elements between a and b +function List:slice(a, b) + return List.new(vim.list_slice(self, a, b)) +end + +--- Similar to slice, but with every element. It only makes a shallow copy +--- @return List: A slice from 1 to #self, i.e., a complete copy of the list +--- @see List.deep_copy +function List:copy() + return self:slice(1, #self) +end + +--- Similar to copy, but makes a deep copy instead +--- @return List: A deep copy of the object +--- @see List.copy +--- @see vim.deep_copy +function List:deep_copy() + return vim.deep_copy(self) +end + +--- Reverses the list in place. If you don't want this, you could do something +--- like this +---
+---     local list = List{1, 2, 3, 4}
+---     local reversed = list:copy():reverse()
+--- 
+--- @return List: The list itself, so you can chain method calls +--- @see List.copy +--- @see List.deep_copy +function List:reverse() + local n = #self + local i = 1 + while i < n do + self[i], self[n] = self[n], self[i] + i = i + 1 + n = n - 1 + end + return self +end + +--- Concatenates the elements whithin the list separated by the given string +---
+---     local list = List{1, 2, 3, 4}
+---     print(list:join('-'))  -- 1-2-3-4
+--- 
+--- @param sep string: The separator to place between the elements. Default '' +--- @return string: The elements in the list separated by sep +function List:join(sep) + sep = sep or "" + local result = "" + for i, v in self:iter() do + result = result .. tostring(v) + if i ~= #self then + result = result .. sep + end + end + return result +end + +--- Returns a list with the elements of self concatenated with those in the +--- given arguments +--- @vararg table|List: The sequences to concatenate to this one +--- @return List +function List:concat(...) + local result = self:copy() + local others = { ... } + for _, other in ipairs(others) do + for _, v in ipairs(other) do + result:push(v) + end + end + return result +end + +--- Moves the elements between from and from+len in self, to positions between +--- to and to+len in other, like so +---
+---     other[to], other[to+1]... other[to+len] = self[from], self[from+1]... self[from+len]
+--- 
+--- @param from number: The first index of the origin slice +--- @param len number: The length of the slices +--- @param to number: The first index of the destination slice +--- @param other table|List: The destination list. Defaults to self +--- @see table.move +function List:move(from, len, to, other) + return table.move(self, from, len, to, other) +end + +--- Packs the given elements into a list. Similar to lua 5.3's table.pack +--- @vararg any: The elements to pack +--- @return List: a list containing all the given elements +--- @see table.pack +function List.pack(...) + return List.new { ... } +end + +--- Unpacks the elements from this list and returns them +--- @return ...any: All the elements from self[1] to self[#self] +function List:unpack() + return unpack(self, 1, #self) +end + +-- Iterator stuff + +local Iter = require "plenary.iterators" + +local itermetatable = getmetatable(Iter:wrap()) + +local function forward_list_gen(param, state) + state = state + 1 + local v = param[state] + if v ~= nil then + return state, v + end +end + +local function backward_list_gen(param, state) + state = state - 1 + local v = param[state] + if v ~= nil then + return state, v + end +end + +--- Run the given predicate through all the elements pointed by this iterator, +--- and classify them into two lists. The first one holds the elements for which +--- predicate returned a truthy value, and the second holds the rest. For +--- example: +--- +---
+---     local list = List{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+---     local evens, odds = list:iter():partition(function(e)
+---         return e % 2 == 0
+---     end)
+---     print(evens, odds)
+--- 
+--- +--- Would print +--- +---
+---     [0, 2, 4, 6, 8] [1, 3, 5, 7, 9]
+--- 
+---@param predicate function: The predicate to classify the elements +---@return List,List +local function partition(self, predicate) + local list1, list2 = List.new {}, List.new {} + for _, v in self do + if predicate(v) then + list1:push(v) + else + list2:push(v) + end + end + return list1, list2 +end + +local function wrap_iter(f, l, n) + local iter = Iter.wrap(f, l, n) + iter.partition = partition + return iter +end + +--- Counts the occurrences of e inside the list +--- @param e any: The element to test for +--- @return number: The number of occurrences of e +function List:count(e) + local count = 0 + for _, v in self:iter() do + if e == v then + count = count + 1 + end + end + return count +end + +--- Appends the elements in the given iterator to the list +--- @param other table: An iterator object +function List:extend(other) + if type(other) == "table" and getmetatable(other) == itermetatable then + for _, v in other do + self:push(v) + end + else + error "Argument must be an iterator" + end +end + +--- Checks whether there is an occurence of the given element in the list +--- @param e any: The object to test for +--- @return boolean: True if e is present +function List:contains(e) + for _, v in self:iter() do + if v == e then + return true + end + end + return false +end + +--- Creates an iterator for the list. For example: +---
+---     local list = List{8, 4, 7, 9}
+---     for i, v in list:iter() do
+---         print(i, v)
+---     end
+--- 
+--- Would print: +---
+---     1    8
+---     2    4
+---     3    7
+---     4    9
+--- 
+--- @return table: An iterator object +function List:iter() + return wrap_iter(forward_list_gen, self, 0) +end + +--- Creates a reverse iterator for the list. For example: +---
+---     local list = List{8, 4, 7, 9}
+---     for i, v in list:riter() do
+---         print(i, v)
+---     end
+--- 
+--- Would print: +---
+---     4    9
+---     3    7
+---     2    4
+---     1    8
+--- 
+--- @return table: An iterator object +function List:riter() + return wrap_iter(backward_list_gen, self, #self + 1) +end + +-- Miscellaneous + +--- Create a list from the elements pointed at by the given iterator. +--- @param iter table: An iterator object +--- @return List +function List.from_iter(iter) + local result = List.new {} + for _, v in iter do + result:push(v) + end + return result +end + +return setmetatable({}, { + __call = function(_, tbl) + return List.new(tbl) + end, + __index = List, +}) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/compat.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/compat.lua new file mode 100644 index 00000000..5e7e24d2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/compat.lua @@ -0,0 +1,17 @@ +local m = {} + +m.flatten = (function() + if vim.fn.has "nvim-0.11" == 1 then + return function(t) + return vim.iter(t):flatten():totable() + end + else + return function(t) + return vim.tbl_flatten(t) + end + end +end)() + +m.islist = vim.islist or vim.tbl_islist + +return m diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/context_manager.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/context_manager.lua new file mode 100644 index 00000000..7d617da2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/context_manager.lua @@ -0,0 +1,56 @@ +--- I like context managers for Python +--- I want them in Lua. + +local context_manager = {} + +function context_manager.with(obj, callable) + -- Wrap functions for people since we're nice + if type(obj) == "function" then + obj = coroutine.create(obj) + end + + if type(obj) == "thread" then + local ok, context = coroutine.resume(obj) + assert(ok, "Should have yielded in coroutine.") + + local succeeded, result = pcall(callable, context) + + local done, _ = coroutine.resume(obj) + assert(done, "Should be done") + + local no_other = not coroutine.resume(obj) + assert(no_other, "Should not yield anymore, otherwise that would make things complicated") + + assert(succeeded, result) + return result + else + assert(obj.enter) + assert(obj.exit) + + -- TODO: Callable can be string for vimL function or a lua callable + local context = obj:enter() + local succeeded, result = pcall(callable, context) + obj:exit() + + assert(succeeded, result) + return result + end +end + +--- @param filename string|table -- If string, used as io.open(filename) +--- Else, should be a table with `filename` as an attribute +function context_manager.open(filename, mode) + if type(filename) == "table" and filename.filename then + filename = filename.filename + end + + local file_io = assert(io.open(filename, mode)) + + return coroutine.create(function() + coroutine.yield(file_io) + + file_io:close() + end) +end + +return context_manager diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/curl.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/curl.lua new file mode 100644 index 00000000..b7fc9974 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/curl.lua @@ -0,0 +1,349 @@ +--[[ +Curl Wrapper + +all curl methods accepts + + url = "The url to make the request to.", (string) + query = "url query, append after the url", (table) + body = "The request body" (string/filepath/table) + auth = "Basic request auth, 'user:pass', or {"user", "pass"}" (string/array) + form = "request form" (table) + raw = "any additonal curl args, it must be an array/list." (array) + dry_run = "whether to return the args to be ran through curl." (boolean) + output = "where to download something." (filepath) + timeout = "request timeout in mseconds" (number) + http_version = "HTTP version to use: 'HTTP/0.9', 'HTTP/1.0', 'HTTP/1.1', 'HTTP/2', or 'HTTP/3'" (string) + proxy = "[protocol://]host[:port] Use this proxy" (string) + insecure = "Allow insecure server connections" (boolean) + +and returns table: + + exit = "The shell process exit code." (number) + status = "The https response status." (number) + headers = "The https response headers." (array) + body = "The http response body." (string) + +see test/plenary/curl_spec.lua for examples. + +author = github.com/tami5 +]] +-- + +local util, parse = {}, {} + +-- Helpers -------------------------------------------------- +------------------------------------------------------------- +local F = require "plenary.functional" +local J = require "plenary.job" +local P = require "plenary.path" +local compat = require "plenary.compat" + +-- Utils ---------------------------------------------------- +------------------------------------------------------------- + +util.url_encode = function(str) + if type(str) ~= "number" then + str = str:gsub("\r?\n", "\r\n") + str = str:gsub("([^%w%-%.%_%~ ])", function(c) + return string.format("%%%02X", c:byte()) + end) + str = str:gsub(" ", "+") + return str + else + return str + end +end + +util.kv_to_list = function(kv, prefix, sep) + return compat.flatten(F.kv_map(function(kvp) + return { prefix, kvp[1] .. sep .. kvp[2] } + end, kv)) +end + +util.kv_to_str = function(kv, sep, kvsep) + return F.join( + F.kv_map(function(kvp) + return kvp[1] .. kvsep .. util.url_encode(kvp[2]) + end, kv), + sep + ) +end + +util.gen_dump_path = function() + local path + local id = string.gsub("xxxx4xxx", "[xy]", function(l) + local v = (l == "x") and math.random(0, 0xf) or math.random(0, 0xb) + return string.format("%x", v) + end) + if P.path.sep == "\\" then + path = string.format("%s\\AppData\\Local\\Temp\\plenary_curl_%s.headers", os.getenv "USERPROFILE", id) + else + local temp_dir = os.getenv "XDG_RUNTIME_DIR" or "/tmp" + path = temp_dir .. "/plenary_curl_" .. id .. ".headers" + end + return { "-D", path } +end + +-- Parsers ---------------------------------------------------- +--------------------------------------------------------------- + +parse.headers = function(t) + if not t then + return + end + local upper = function(str) + return string.gsub(" " .. str, "%W%l", string.upper):sub(2) + end + return util.kv_to_list( + (function() + local normilzed = {} + for k, v in pairs(t) do + normilzed[upper(k:gsub("_", "%-"))] = v + end + return normilzed + end)(), + "-H", + ": " + ) +end + +parse.data_body = function(t) + if not t then + return + end + return util.kv_to_list(t, "-d", "=") +end + +parse.raw_body = function(xs) + if not xs then + return + end + if type(xs) == "table" then + return parse.data_body(xs) + else + return { "--data-raw", xs } + end +end + +parse.form = function(t) + if not t then + return + end + return util.kv_to_list(t, "-F", "=") +end + +parse.curl_query = function(t) + if not t then + return + end + return util.kv_to_str(t, "&", "=") +end + +parse.method = function(s) + if not s then + return + end + if s ~= "head" then + return { "-X", string.upper(s) } + else + return { "-I" } + end +end + +parse.file = function(p) + if not p then + return + end + return { "-d", "@" .. P.expand(P.new(p)) } +end + +parse.auth = function(xs) + if not xs then + return + end + return { "-u", type(xs) == "table" and util.kv_to_str(xs, nil, ":") or xs } +end + +parse.url = function(xs, q) + if not xs then + return + end + q = parse.curl_query(q) + if type(xs) == "string" then + return q and xs .. "?" .. q or xs + elseif type(xs) == "table" then + error "Low level URL definition is not supported." + end +end + +parse.accept_header = function(s) + if not s then + return + end + return { "-H", "Accept: " .. s } +end + +parse.http_version = function(s) + if not s then + return + end + if s == "HTTP/0.9" or s == "HTTP/1.0" or s == "HTTP/1.1" or s == "HTTP/2" or s == "HTTP/3" then + s = s:lower() + s = s:gsub("/", "") + return { "--" .. s } + else + error "Unknown HTTP version." + end +end + +-- Parse Request ------------------------------------------- +------------------------------------------------------------ +parse.request = function(opts) + if opts.body then + local b = opts.body + local silent_is_file = function() + local status, result = pcall(P.is_file, P.new(b)) + return status and result + end + opts.body = nil + if type(b) == "table" then + opts.data = b + elseif silent_is_file() then + opts.in_file = b + elseif type(b) == "string" then + opts.raw_body = b + end + end + local result = { "-sSL", opts.dump } + local append = function(v) + if v then + table.insert(result, v) + end + end + + if opts.insecure then + table.insert(result, "--insecure") + end + if opts.proxy then + table.insert(result, { "--proxy", opts.proxy }) + end + if opts.compressed then + table.insert(result, "--compressed") + end + append(parse.method(opts.method)) + append(parse.headers(opts.headers)) + append(parse.accept_header(opts.accept)) + append(parse.raw_body(opts.raw_body)) + append(parse.data_body(opts.data)) + append(parse.form(opts.form)) + append(parse.file(opts.in_file)) + append(parse.auth(opts.auth)) + append(parse.http_version(opts.http_version)) + append(opts.raw) + if opts.output then + table.insert(result, { "-o", opts.output }) + end + table.insert(result, parse.url(opts.url, opts.query)) + return compat.flatten(result), opts +end + +-- Parse response ------------------------------------------ +------------------------------------------------------------ +parse.response = function(lines, dump_path, code) + local headers = P.readlines(dump_path) + local status = tonumber(string.match(headers[1], "([%w+]%d+)")) + local body = F.join(lines, "\n") + + vim.loop.fs_unlink(dump_path) + table.remove(headers, 1) + + return { + status = status, + headers = headers, + body = body, + exit = code, + } +end + +local request = function(specs) + local response = {} + local args, opts = parse.request(vim.tbl_extend("force", { + compressed = package.config:sub(1, 1) ~= "\\", + dry_run = false, + dump = util.gen_dump_path(), + }, specs)) + + if opts.dry_run then + return args + end + + local job_opts = { + command = vim.g.plenary_curl_bin_path or "curl", + args = args, + } + + if opts.stream then + job_opts.on_stdout = opts.stream + end + + job_opts.on_exit = function(j, code) + if code ~= 0 then + local stderr = vim.inspect(j:stderr_result()) + local message = string.format("%s %s - curl error exit_code=%s stderr=%s", opts.method, opts.url, code, stderr) + if opts.on_error then + return opts.on_error { + message = message, + stderr = stderr, + exit = code, + } + else + error(message) + end + end + local output = parse.response(j:result(), opts.dump[2], code) + if opts.callback then + return opts.callback(output) + else + response = output + end + end + local job = J:new(job_opts) + + if opts.callback or opts.stream then + job:start() + return job + else + local timeout = opts.timeout or 10000 + job:sync(timeout) + return response + end +end + +-- Main ---------------------------------------------------- +------------------------------------------------------------ +return (function() + local spec = {} + local partial = function(method) + return function(url, opts) + opts = opts or {} + if type(url) == "table" then + opts = url + spec.method = method + else + spec.url = url + spec.method = method + end + opts = method == "request" and opts or (vim.tbl_extend("keep", opts, spec)) + return request(opts) + end + end + return { + get = partial "get", + post = partial "post", + put = partial "put", + head = partial "head", + patch = partial "patch", + delete = partial "delete", + request = partial "request", + } +end)() diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/debug_utils.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/debug_utils.lua new file mode 100644 index 00000000..c4caf65d --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/debug_utils.lua @@ -0,0 +1,13 @@ +local debug_utils = {} + +function debug_utils.sourced_filepath() + local str = debug.getinfo(2, "S").source:sub(2) + return str +end + +function debug_utils.sourced_filename() + local str = debug_utils.sourced_filepath() + return str:match "^.*/(.*).lua$" or str +end + +return debug_utils diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/enum.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/enum.lua new file mode 100644 index 00000000..22d44107 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/enum.lua @@ -0,0 +1,162 @@ +---@brief [[ +--- This module defines an idiomatic way to create enum classes, similar to +--- those in java or kotlin. There are two ways to create an enum, one is with +--- the exported `make_enum` function, or calling the module directly with the +--- enum spec. +--- +--- The enum spec consists of a list-like table whose members can be either a +--- string or a tuple of the form {string, number}. In the former case, the enum +--- member will take the next available value, while in the latter, the member +--- will take the string as it's name and the number as it's value. In both +--- cases, the name must start with a capital letter. +--- +--- Here is an example: +--- +---
+--- local Enum = require 'plenary.enum'
+--- local myEnum = Enum {
+---     'Foo',          -- Takes value 1
+---     'Bar',          -- Takes value 2
+---     {'Qux', 10},    -- Takes value 10
+---     'Baz',          -- Takes value 11
+--- }
+--- 
+--- +--- In case of name or value clashing, the call will fail. For this reason, it's +--- best if you define the members in ascending order. +---@brief ]] +local Enum = {} + +---@class Enum + +---@class Variant + +local function validate_member_name(name) + if #name > 0 and name:sub(1, 1):match "%u" then + return name + end + error('"' .. name .. '" should start with a capital letter') +end + +--- Creates an enum from the given list-like table, like so: +---
+--- local enum = Enum.make_enum{
+---     'Foo',
+---     'Bar',
+---     {'Qux', 10}
+--- }
+--- 
+--- @return Enum: A new enum +local function make_enum(tbl) + local enum = {} + + local Variant = {} + Variant.__index = Variant + + local function newVariant(i) + return setmetatable({ value = i }, Variant) + end + + -- we don't need __eq because the __eq metamethod will only ever be + -- invoked when they both have the same metatable + + function Variant:__lt(o) + return self.value < o.value + end + + function Variant:__gt(o) + return self.value > o.value + end + + function Variant:__tostring() + return tostring(self.value) + end + + local function find_next_idx(e, i) + local newI = i + 1 + if not e[newI] then + return newI + end + error("Overlapping index: " .. tostring(newI)) + end + + local i = 0 + + for _, v in ipairs(tbl) do + if type(v) == "string" then + local name = validate_member_name(v) + local idx = find_next_idx(enum, i) + enum[idx] = name + if enum[name] then + error("Duplicate enum member name: " .. name) + end + enum[name] = newVariant(idx) + i = idx + elseif type(v) == "table" and type(v[1]) == "string" and type(v[2]) == "number" then + local name = validate_member_name(v[1]) + local idx = v[2] + if enum[idx] then + error("Overlapping index: " .. tostring(idx)) + end + enum[idx] = name + if enum[name] then + error("Duplicate name: " .. name) + end + enum[name] = newVariant(idx) + i = idx + else + error "Invalid way to specify an enum variant" + end + end + + return require("plenary.tbl").freeze(setmetatable(enum, Enum)) +end + +Enum.__index = function(_, key) + if Enum[key] then + return Enum[key] + end + error("Invalid enum key: " .. tostring(key)) +end + +--- Checks whether the enum has a member with the given name +--- @param key string: The element to check for +--- @return boolean: True if key is present +function Enum:has_key(key) + if rawget(getmetatable(self).__index, key) then + return true + end + return false +end + +--- If there is a member named 'key', return it, otherwise return nil +--- @param key string: The element to check for +--- @return Variant: The element named by key, or nil if not present +function Enum:from_str(key) + if self:has_key(key) then + return self[key] + end +end + +--- If there is a member of value 'num', return it, otherwise return nil +--- @param num number: The value of the element to check for +--- @return Variant: The element whose value is num +function Enum:from_num(num) + local key = self[num] + if key then + return self[key] + end +end + +--- Checks whether the given object corresponds to an instance of Enum +--- @param tbl table: The object to be checked +--- @return boolean: True if tbl is an Enum +local function is_enum(tbl) + return getmetatable(getmetatable(tbl).__index) == Enum +end + +return setmetatable({ is_enum = is_enum, make_enum = make_enum }, { + __call = function(_, tbl) + return make_enum(tbl) + end, +}) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/errors.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/errors.lua new file mode 100644 index 00000000..64849f61 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/errors.lua @@ -0,0 +1,15 @@ +local M = {} + +M.traceback_error = function(s, level) + local traceback = debug.traceback() + traceback = traceback .. "\n" .. s + error(traceback, (level or 1) + 1) +end + +M.info_error = function(s, func_info, level) + local info = debug.getinfo(func_info) + info = info .. "\n" .. s + error(info, (level or 1) + 1) +end + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/filetype.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/filetype.lua new file mode 100644 index 00000000..60cd3078 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/filetype.lua @@ -0,0 +1,196 @@ +local Path = require "plenary.path" + +local os_sep = Path.path.sep + +local filetype = {} + +local filetype_table = { + extension = {}, + file_name = {}, + shebang = {}, +} + +filetype.add_table = function(new_filetypes) + local valid_keys = { "extension", "file_name", "shebang" } + local new_keys = {} + + -- Validate keys + for k, _ in pairs(new_filetypes) do + new_keys[k] = true + end + for _, k in ipairs(valid_keys) do + new_keys[k] = nil + end + + for k, v in pairs(new_keys) do + error(debug.traceback("Invalid key / value:" .. tostring(k) .. " / " .. tostring(v))) + end + + if new_filetypes.extension then + filetype_table.extension = vim.tbl_extend("force", filetype_table.extension, new_filetypes.extension) + end + + if new_filetypes.file_name then + filetype_table.file_name = vim.tbl_extend("force", filetype_table.file_name, new_filetypes.file_name) + end + + if new_filetypes.shebang then + filetype_table.shebang = vim.tbl_extend("force", filetype_table.shebang, new_filetypes.shebang) + end +end + +filetype.add_file = function(filename) + local filetype_files = vim.api.nvim_get_runtime_file(string.format("data/plenary/filetypes/%s.lua", filename), true) + + for _, file in ipairs(filetype_files) do + local ok, msg = pcall(filetype.add_table, dofile(file)) + if not ok then + error("Unable to add file " .. file .. ":\n" .. msg) + end + end +end + +local filename_regex = "[^" .. os_sep .. "].*" +filetype._get_extension_parts = function(filename) + local current_match = filename:match(filename_regex) + local possibilities = {} + while current_match do + current_match = current_match:match "[^.]%.(.*)" + if current_match then + table.insert(possibilities, current_match:lower()) + else + return possibilities + end + end + return possibilities +end + +filetype._parse_modeline = function(tail) + if tail:find "vim:" then + return tail:match ".*:ft=([^: ]*):.*$" or "" + end + return "" +end + +filetype._parse_shebang = function(head) + if head:sub(1, 2) == "#!" then + local match = filetype_table.shebang[head:sub(3, #head)] + if match then + return match + end + end + return "" +end + +local done_adding = false +local extend_tbl_with_ext_eq_ft_entries = function() + if not done_adding then + if vim.in_fast_event() then + return + end + local all_valid_filetypes = vim.fn.getcompletion("", "filetype") + for _, v in ipairs(all_valid_filetypes) do + if not filetype_table.extension[v] then + filetype_table.extension[v] = v + end + end + done_adding = true + return true + end +end + +filetype.detect_from_extension = function(filepath) + local exts = filetype._get_extension_parts(filepath) + for _, ext in ipairs(exts) do + local match = ext and filetype_table.extension[ext] + if match then + return match + end + end + if extend_tbl_with_ext_eq_ft_entries() then + for _, ext in ipairs(exts) do + local match = ext and filetype_table.extension[ext] + if match then + return match + end + end + end + return "" +end + +filetype.detect_from_name = function(filepath) + if filepath then + filepath = filepath:lower() + local split_path = vim.split(filepath, os_sep, true) + local fname = split_path[#split_path] + local match = filetype_table.file_name[fname] + if match then + return match + end + end + return "" +end + +filetype.detect_from_modeline = function(filepath) + local tail = Path:new(filepath):readbyterange(-256, 256) + if not tail then + return "" + end + local lines = vim.split(tail, "\n") + local idx = lines[#lines] ~= "" and #lines or #lines - 1 + if idx >= 1 then + return filetype._parse_modeline(lines[idx]) + end +end + +filetype.detect_from_shebang = function(filepath) + local head = Path:new(filepath):readbyterange(0, 256) + if not head then + return "" + end + local lines = vim.split(head, "\n") + return filetype._parse_shebang(lines[1]) +end + +--- Detect a filetype from a path. +--- +---@param opts table: Table with optional keys +--- - fs_access (bool, default=true): Should check a file if it exists +filetype.detect = function(filepath, opts) + opts = opts or {} + opts.fs_access = opts.fs_access or true + + if type(filepath) ~= string then + filepath = tostring(filepath) + end + + local match = filetype.detect_from_name(filepath) + if match ~= "" then + return match + end + + match = filetype.detect_from_extension(filepath) + + if opts.fs_access and Path:new(filepath):exists() then + if match == "" then + match = filetype.detect_from_shebang(filepath) + if match ~= "" then + return match + end + end + + if match == "text" or match == "" then + match = filetype.detect_from_modeline(filepath) + if match ~= "" then + return match + end + end + end + + return match +end + +filetype.add_file "base" +filetype.add_file "builtin" + +return filetype diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/fun.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/fun.lua new file mode 100644 index 00000000..e70367f9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/fun.lua @@ -0,0 +1,23 @@ +local M = {} + +M.bind = require("plenary.functional").partial + +function M.arify(fn, argc) + return function(...) + if select("#", ...) ~= argc then + error(("Expected %s number of arguments"):format(argc)) + end + + fn(...) + end +end + +function M.create_wrapper(map) + return function(to_wrap) + return function(...) + return map(to_wrap(...)) + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/functional.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/functional.lua new file mode 100644 index 00000000..6a5b3673 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/functional.lua @@ -0,0 +1,77 @@ +local f = {} + +function f.kv_pairs(t) + local results = {} + for k, v in pairs(t) do + table.insert(results, { k, v }) + end + return results +end + +function f.kv_map(fun, t) + return vim.tbl_map(fun, f.kv_pairs(t)) +end + +function f.join(array, sep) + return table.concat(vim.tbl_map(tostring, array), sep) +end + +local function bind_n(fn, n, a, ...) + if n == 0 then + return fn + end + return bind_n(function(...) + return fn(a, ...) + end, n - 1, ...) +end + +function f.partial(fun, ...) + return bind_n(fun, select("#", ...), ...) +end + +function f.any(fun, iterable) + for k, v in pairs(iterable) do + if fun(k, v) then + return true + end + end + + return false +end + +function f.all(fun, iterable) + for k, v in pairs(iterable) do + if not fun(k, v) then + return false + end + end + + return true +end + +function f.if_nil(val, was_nil, was_not_nil) + if val == nil then + return was_nil + else + return was_not_nil + end +end + +function f.select_only(n) + return function(...) + local x = select(n, ...) + return x + end +end + +f.first = f.select_only(1) +f.second = f.select_only(2) +f.third = f.select_only(3) + +function f.last(...) + local length = select("#", ...) + local x = select(length, ...) + return x +end + +return f diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/init.lua new file mode 100644 index 00000000..4805a4f6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/init.lua @@ -0,0 +1,14 @@ +-- Lazy load everything into plenary. +local plenary = setmetatable({}, { + __index = function(t, k) + local ok, val = pcall(require, string.format("plenary.%s", k)) + + if ok then + rawset(t, k, val) + end + + return val + end, +}) + +return plenary diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/iterators.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/iterators.lua new file mode 100644 index 00000000..2aac0d20 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/iterators.lua @@ -0,0 +1,695 @@ +---@brief [[ +---An adaptation of luafun for neovim. +---This library will use neovim specific functions. +---Some documentation is the same as from luafun. +---Some extra functions are present that are not in luafun +---@brief ]] + +local co = coroutine +local f = require "plenary.functional" +local compat = require "plenary.compat" + +-------------------------------------------------------------------------------- +-- Tools +-------------------------------------------------------------------------------- + +local exports = {} + +---@class Iterator +---@field gen function +---@field param any +---@field state any +local Iterator = {} +Iterator.__index = Iterator + +---Makes a for loop work +---If not called without param or state, will just generate with the starting state +---This is useful because the original luafun will also return param and state in addition to the iterator as a multival +---This can cause problems because when using iterators as expressions the multivals can bleed +---For example i.iter { 1, 2, i.iter { 3, 4 } } will not work because the inner iterator returns a multival thus +---polluting the list with internal values. +---So instead we do not return param and state as multivals when doing wrap +---This causes the first loop iteration to call param and state with nil because we didn't return them as multivals +---We have to use or to check for nil and default to interal starting state and param +function Iterator:__call(param, state) + return self.gen(param or self.param, state or self.state) +end + +function Iterator:__tostring() + return "" +end + +-- A special hack for zip/chain to skip last two state, if a wrapped iterator +-- has been passed +local numargs = function(...) + local n = select("#", ...) + if n >= 3 then + -- Fix last argument + local it = select(n - 2, ...) + if + type(it) == "table" + and getmetatable(it) == Iterator + and it.param == select(n - 1, ...) + and it.state == select(n, ...) + then + return n - 2 + end + end + return n +end + +local return_if_not_empty = function(state_x, ...) + if state_x == nil then + return nil + end + return ... +end + +local call_if_not_empty = function(fun, state_x, ...) + if state_x == nil then + return nil + end + return state_x, fun(...) +end + +-------------------------------------------------------------------------------- +-- Basic Functions +-------------------------------------------------------------------------------- +local nil_gen = function(_param, _state) + return nil +end + +local pairs_gen = pairs {} + +local map_gen = function(map, key) + local value + key, value = pairs_gen(map, key) + return key, key, value +end + +local string_gen = function(param, state) + state = state + 1 + if state > #param then + return nil + end + local r = string.sub(param, state, state) + return state, r +end + +local rawiter = function(obj, param, state) + assert(obj ~= nil, "invalid iterator") + + if type(obj) == "table" then + local mt = getmetatable(obj) + + if mt ~= nil then + if mt == Iterator then + return obj.gen, obj.param, obj.state + end + end + + if compat.islist(obj) then + return ipairs(obj) + else + -- hash + return map_gen, obj, nil + end + elseif type(obj) == "function" then + return obj, param, state + elseif type(obj) == "string" then + if #obj == 0 then + return nil_gen, nil, nil + end + + return string_gen, obj, 0 + end + + error(string.format('object %s of type "%s" is not iterable', obj, type(obj))) +end + +---Wraps the iterator triplet into a table to allow metamethods and calling with method form +---Important! We do not return param and state as multivals like the original luafun +---See the __call metamethod for more information +---@param gen any +---@param param any +---@param state any +---@return Iterator +local function wrap(gen, param, state) + return setmetatable({ + gen = gen, + param = param, + state = state, + }, Iterator) +end + +---Unwrap an iterator metatable into the iterator triplet +---@param self Iterator +---@return any +---@return any +---@return any +local unwrap = function(self) + return self.gen, self.param, self.state +end + +---Create an iterator from an object +---@param obj any +---@param param any (optional) +---@param state any (optional) +---@return Iterator +local iter = function(obj, param, state) + return wrap(rawiter(obj, param, state)) +end + +exports.iter = iter +exports.wrap = wrap +exports.unwrap = unwrap + +function Iterator:for_each(fn) + local param, state = self.param, self.state + repeat + state = call_if_not_empty(fn, self.gen(param, state)) + until state == nil +end + +function Iterator:stateful() + return wrap( + co.wrap(function() + self:for_each(function(...) + co.yield(f.first(...), ...) + end) + + -- too make sure that we always return nil if there are no more + while true do + co.yield() + end + end), + nil, + nil + ) +end + +-- function Iterator:stateful() +-- local gen, param, state = self.gen, self.param, self.state + +-- local function return_and_set_state(state_x, ...) +-- state = state_x +-- if state == nil then return end +-- return state_x, ... +-- end + +-- local stateful_gen = function() +-- return return_and_set_state(gen(param, state)) +-- end + +-- return wrap(stateful_gen, false, false) +-- end + +-------------------------------------------------------------------------------- +-- Generators +-------------------------------------------------------------------------------- +local range_gen = function(param, state) + local stop, step = param[1], param[2] + state = state + step + if state > stop then + return nil + end + return state, state +end + +local range_rev_gen = function(param, state) + local stop, step = param[1], param[2] + state = state + step + if state < stop then + return nil + end + return state, state +end + +---Creates a range iterator +---@param start number +---@param stop number +---@param step number +---@return Iterator +local range = function(start, stop, step) + if step == nil then + if stop == nil then + if start == 0 then + return nil_gen, nil, nil + end + stop = start + start = stop > 0 and 1 or -1 + end + step = start <= stop and 1 or -1 + end + + assert(type(start) == "number", "start must be a number") + assert(type(stop) == "number", "stop must be a number") + assert(type(step) == "number", "step must be a number") + assert(step ~= 0, "step must not be zero") + + if step > 0 then + return wrap(range_gen, { stop, step }, start - step) + elseif step < 0 then + return wrap(range_rev_gen, { stop, step }, start - step) + end +end +exports.range = range + +local duplicate_table_gen = function(param_x, state_x) + return state_x + 1, unpack(param_x) +end + +local duplicate_fun_gen = function(param_x, state_x) + return state_x + 1, param_x(state_x) +end + +local duplicate_gen = function(param_x, state_x) + return state_x + 1, param_x +end + +---Creates an infinite iterator that will yield the arguments +---If multiple arguments are passed, the args will be packed and unpacked +---@param ...: the arguments to duplicate +---@return Iterator +local duplicate = function(...) + if select("#", ...) <= 1 then + return wrap(duplicate_gen, select(1, ...), 0) + else + return wrap(duplicate_table_gen, { ... }, 0) + end +end +exports.duplicate = duplicate + +---Creates an iterator from a function +---NOTE: if the function is a closure and modifies state, the resulting iterator will not be stateless +---@param fun function +---@return Iterator +local from_fun = function(fun) + assert(type(fun) == "function") + return wrap(duplicate_fun_gen, fun, 0) +end +exports.from_fun = from_fun + +---Creates an infinite iterator that will yield zeros. +---This is an alias to calling duplicate(0) +---@return Iterator +local zeros = function() + return wrap(duplicate_gen, 0, 0) +end +exports.zeros = zeros + +---Creates an infinite iterator that will yield ones. +---This is an alias to calling duplicate(1) +---@return Iterator +local ones = function() + return wrap(duplicate_gen, 1, 0) +end +exports.ones = ones + +local rands_gen = function(param_x, _state_x) + return 0, math.random(param_x[1], param_x[2]) +end + +local rands_nil_gen = function(_param_x, _state_x) + return 0, math.random() +end + +---Creates an infinite iterator that will yield random values. +---@param n number +---@param m number +---@return Iterator +local rands = function(n, m) + if n == nil and m == nil then + return wrap(rands_nil_gen, 0, 0) + end + assert(type(n) == "number", "invalid first arg to rands") + if m == nil then + m = n + n = 0 + else + assert(type(m) == "number", "invalid second arg to rands") + end + assert(n < m, "empty interval") + return wrap(rands_gen, { n, m - 1 }, 0) +end +exports.rands = rands + +local split_gen = function(param, state) + local input, sep = param[1], param[2] + local input_len = #input + + if state > input_len + 1 then + return + end + + local start, finish = string.find(input, sep, state, true) + if not start then + start = input_len + 1 + finish = input_len + 1 + end + + local sub_str = input:sub(state, start - 1) + + return finish + 1, sub_str +end + +---Return an iterator of substrings separated by a string +---@param input string: the string to split +---@param sep string: the separator to find and split based on +---@return Iterator +local split = function(input, sep) + return wrap(split_gen, { input, sep }, 1) +end +exports.split = split + +---Splits a string based on a single space +---An alias for split(input, " ") +---@param input any +---@return any +local words = function(input) + return split(input, " ") +end +exports.words = words + +local lines = function(input) + -- TODO: platform specific linebreaks + return split(input, "\n") +end +exports.lines = lines + +-------------------------------------------------------------------------------- +-- Transformations +-------------------------------------------------------------------------------- +local map_gen2 = function(param, state) + local gen_x, param_x, fun = param[1], param[2], param[3] + return call_if_not_empty(fun, gen_x(param_x, state)) +end + +---Iterator adapter that maps the previous iterator with a function +---@param fun function: The function to map with. Will be called on each element +---@return Iterator +function Iterator:map(fun) + return wrap(map_gen2, { self.gen, self.param, fun }, self.state) +end + +local flatten_gen1 +do + local it = function(new_iter, state_x, ...) + if state_x == nil then + return nil + end + return { new_iter.gen, new_iter.param, state_x }, ... + end + + flatten_gen1 = function(state, state_x, ...) + if state_x == nil then + return nil + end + + local first_arg = f.first(...) + + -- experimental part + if getmetatable(first_arg) == Iterator then + -- attach the iterator to the rest + local new_iter = (first_arg .. wrap(state[1], state[2], state_x)):flatten() + -- advance the iterator by one + return it(new_iter, new_iter.gen(new_iter.param, new_iter.state)) + end + + return { state[1], state[2], state_x }, ... + end +end + +local flatten_gen = function(_, state) + if state == nil then + return + end + local gen_x, param_x, state_x = state[1], state[2], state[3] + return flatten_gen1(state, gen_x(param_x, state_x)) +end + +---Iterator adapter that will recursivley flatten nested iterator structure +---@return Iterator +function Iterator:flatten() + return wrap(flatten_gen, false, { self.gen, self.param, self.state }) +end + +-------------------------------------------------------------------------------- +-- Filtering +-------------------------------------------------------------------------------- +local filter1_gen = function(fun, gen_x, param_x, state_x, a) + while true do + if state_x == nil or fun(a) then + break + end + state_x, a = gen_x(param_x, state_x) + end + return state_x, a +end + +-- call each other +-- because we can't assign a vararg mutably in a while loop like filter1_gen +-- so we have to use recursion in calling both of these functions +local filterm_gen +local filterm_gen_shrink = function(fun, gen_x, param_x, state_x) + return filterm_gen(fun, gen_x, param_x, gen_x(param_x, state_x)) +end + +filterm_gen = function(fun, gen_x, param_x, state_x, ...) + if state_x == nil then + return nil + end + + if fun(...) then + return state_x, ... + end + + return filterm_gen_shrink(fun, gen_x, param_x, state_x) +end + +local filter_detect = function(fun, gen_x, param_x, state_x, ...) + if select("#", ...) < 2 then + return filter1_gen(fun, gen_x, param_x, state_x, ...) + else + return filterm_gen(fun, gen_x, param_x, state_x, ...) + end +end + +local filter_gen = function(param, state_x) + local gen_x, param_x, fun = param[1], param[2], param[3] + return filter_detect(fun, gen_x, param_x, gen_x(param_x, state_x)) +end + +---Iterator adapter that will filter values +---@param fun function: The function to filter values with. If the function returns true, the value will be kept. +---@return Iterator +function Iterator:filter(fun) + return wrap(filter_gen, { self.gen, self.param, fun }, self.state) +end + +---Iterator adapter that will provide numbers from 1 to n as the first multival +---@return Iterator +function Iterator:enumerate() + local i = 0 + return self:map(function(...) + i = i + 1 + return i, ... + end) +end + +-------------------------------------------------------------------------------- +-- Reducing +-------------------------------------------------------------------------------- + +---Returns true if any of the values in the iterator satisfy a predicate +---@param fun function +---@return boolean +function Iterator:any(fun) + local r + local state, param, gen = self.state, self.param, self.gen + repeat + state, r = call_if_not_empty(fun, gen(param, state)) + until state == nil or r + return r +end + +---Returns true if all of the values in the iterator satisfy a predicate +---@param fun function +---@return boolean +function Iterator:all(fun) + local r + local state, param, gen = self.state, self.param, self.gen + repeat + state, r = call_if_not_empty(fun, gen(param, state)) + until state == nil or not r + return state == nil +end + +---Finds a value that is equal to the provided value of satisfies a predicate. +---@param val_or_fn any +---@return any +function Iterator:find(val_or_fn) + local gen, param, state = self.gen, self.param, self.state + if type(val_or_fn) == "function" then + return return_if_not_empty(filter_detect(val_or_fn, gen, param, gen(param, state))) + else + for _, r in gen, param, state do + if r == val_or_fn then + return r + end + end + return nil + end +end + +---Folds an iterator into a single value using a function. +---@param init any +---@param fun fun(acc: any, val: any): any +---@return any +function Iterator:fold(init, fun) + local acc = init + local gen, param, state = self.gen, self.param, self.state + for _, r in gen, param, state do + acc = fun(acc, r) + end + return acc +end + +---Turns an iterator into a list. +---If the iterator yields multivals only the first multival will be used. +---@return table +function Iterator:tolist() + local list = {} + self:for_each(function(a) + table.insert(list, a) + end) + return list +end + +---Turns an iterator into a list. +---If the iterator yields multivals all multivals will be used and packed into a table. +---@return table +function Iterator:tolistn() + local list = {} + self:for_each(function(...) + table.insert(list, { ... }) + end) + return list +end + +---Turns an iterator into a map. +---The first multival that the iterator yields will be the key. +---The second multival that the iterator yields will be the value. +---@return table +function Iterator:tomap() + local map = {} + self:for_each(function(key, value) + map[key] = value + end) + return map +end + +-------------------------------------------------------------------------------- +-- Compositions +-------------------------------------------------------------------------------- +-- call each other +local chain_gen_r1 +local chain_gen_r2 = function(param, state, state_x, ...) + if state_x == nil then + local i = state[1] + 1 + if param[3 * i - 1] == nil then + return nil + end + state_x = param[3 * i] + return chain_gen_r1(param, { i, state_x }) + end + return { state[1], state_x }, ... +end + +chain_gen_r1 = function(param, state) + local i, state_x = state[1], state[2] + local gen_x, param_x = param[3 * i - 2], param[3 * i - 1] + return chain_gen_r2(param, state, gen_x(param_x, state_x)) +end + +---Make an iterator that returns elements from the first iterator until it is exhausted, +---then proceeds to the next iterator, +---until all of the iterators are exhausted. +---Used for treating consecutive iterators as a single iterator. +---Infinity iterators are supported, but are not recommended. +---@param ...: the iterators to chain +---@return Iterator +local chain = function(...) + local n = numargs(...) + + if n == 0 then + return wrap(nil_gen, nil, nil) + end + + local param = { [3 * n] = 0 } + + local gen_x, param_x, state_x + for i = 1, n, 1 do + local elem = select(i, ...) + gen_x, param_x, state_x = unwrap(elem) + param[3 * i - 2] = gen_x + param[3 * i - 1] = param_x + param[3 * i] = state_x + end + + return wrap(chain_gen_r1, param, { 1, param[3] }) +end + +Iterator.chain = chain +Iterator.__concat = chain +exports.chain = chain + +local function zip_gen_r(param, state, state_new, ...) + if #state_new == #param / 2 then + return state_new, ... + end + + local i = #state_new + 1 + local gen_x, param_x = param[2 * i - 1], param[2 * i] + local state_x, r = gen_x(param_x, state[i]) + if state_x == nil then + return nil + end + table.insert(state_new, state_x) + return zip_gen_r(param, state, state_new, r, ...) +end + +local zip_gen = function(param, state) + return zip_gen_r(param, state, {}) +end + +---Return a new iterator where i-th return value contains the i-th element from each of the iterators. +---The returned iterator is truncated in length to the length of the shortest iterator. +---For multi-return iterators only the first variable is used. +---@param ...: the iterators to zip +---@return Iterator +local zip = function(...) + local n = numargs(...) + if n == 0 then + return wrap(nil_gen, nil, nil) + end + local param = { [2 * n] = 0 } + local state = { [n] = 0 } + + local gen_x, param_x, state_x + for i = 1, n, 1 do + local it = select(n - i + 1, ...) + gen_x, param_x, state_x = rawiter(it) + param[2 * i - 1] = gen_x + param[2 * i] = param_x + state[i] = state_x + end + + return wrap(zip_gen, param, state) +end + +Iterator.zip = zip +Iterator.__div = zip +exports.zip = zip + +return exports diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/job.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/job.lua new file mode 100644 index 00000000..c095c4c1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/job.lua @@ -0,0 +1,678 @@ +local vim = vim +local uv = vim.loop +local compat = require "plenary.compat" + +local F = require "plenary.functional" + +---@class Job +---@field command string Command to run +---@field args? string[] List of arguments to pass +---@field cwd? string Working directory for job +---@field env? table|string[] Environment looking like: { ['VAR'] = 'VALUE' } or { 'VAR=VALUE' } +---@field interactive? boolean +---@field detached? boolean Spawn the child in a detached state making it a process group leader +---@field skip_validation? boolean Skip validating the arguments +---@field enable_handlers? boolean If set to false, disables all callbacks associated with output (default: true) +---@field enabled_recording? boolean +---@field on_start? fun() +---@field on_stdout? fun(error: string, data: string, self?: Job) +---@field on_stderr? fun(error: string, data: string, self?: Job) +---@field on_exit? fun(self: Job, code: number, signal: number) +---@field maximum_results? number Stop processing results after this number +---@field writer? Job|table|string Job that writes to stdin of this job. +local Job = {} +Job.__index = Job + +local function close_safely(j, key) + local handle = j[key] + + if not handle then + return + end + + if not handle:is_closing() then + handle:close() + end +end + +local start_shutdown_check = function(child, options, code, signal) + uv.check_start(child._shutdown_check, function() + if not child:_pipes_are_closed(options) then + return + end + + -- Wait until all the pipes are closing. + uv.check_stop(child._shutdown_check) + child._shutdown_check = nil + + child:_shutdown(code, signal) + + -- Remove left over references + child = nil + end) +end + +local shutdown_factory = function(child, options) + return function(code, signal) + if uv.is_closing(child._shutdown_check) then + return child:shutdown(code, signal) + else + start_shutdown_check(child, options, code, signal) + end + end +end + +local function expand(path) + if vim.in_fast_event() then + return assert(uv.fs_realpath(path), string.format("Path must be valid: %s", path)) + else + -- TODO: Probably want to check that this is valid here... otherwise that's weird. + return vim.fn.expand(vim.fn.escape(path, "[]$"), true) + end +end + +---@class Array +--- Numeric table + +---@class Map +--- Map-like table + +---Create a new job +---@param o Job +---@return Job +function Job:new(o) + if not o then + error(debug.traceback "Options are required for Job:new") + end + + local command = o.command + if not command then + if o[1] then + command = o[1] + else + error(debug.traceback "'command' is required for Job:new") + end + elseif o[1] then + error(debug.traceback "Cannot pass both 'command' and array args") + end + + local args = o.args + if not args then + if #o > 1 then + args = { select(2, unpack(o)) } + end + end + + local ok, is_exe = pcall(vim.fn.executable, command) + if not o.skip_validation and ok and 1 ~= is_exe then + error(debug.traceback(command .. ": Executable not found")) + end + + local obj = {} + + obj.command = command + obj.args = args + obj._raw_cwd = o.cwd + if o.env then + if type(o.env) ~= "table" then + error "[plenary.job] env has to be a table" + end + + local transform = {} + for k, v in pairs(o.env) do + if type(k) == "number" then + table.insert(transform, v) + elseif type(k) == "string" then + table.insert(transform, k .. "=" .. tostring(v)) + end + end + obj.env = transform + end + if o.interactive == nil then + obj.interactive = true + else + obj.interactive = o.interactive + end + + if o.detached then + obj.detached = true + end + + -- enable_handlers: Do you want to do ANYTHING with the stdout/stderr of the proc + obj.enable_handlers = F.if_nil(o.enable_handlers, true, o.enable_handlers) + + -- enable_recording: Do you want to record stdout/stderr into a table. + -- Since it cannot be enabled when enable_handlers is false, + -- we try and make sure they are associated correctly. + obj.enable_recording = + F.if_nil(F.if_nil(o.enable_recording, o.enable_handlers, o.enable_recording), true, o.enable_recording) + + if not obj.enable_handlers and obj.enable_recording then + error "[plenary.job] Cannot record items but disable handlers" + end + + obj._user_on_start = o.on_start + obj._user_on_stdout = o.on_stdout + obj._user_on_stderr = o.on_stderr + obj._user_on_exit = o.on_exit + + obj._additional_on_exit_callbacks = {} + + obj._maximum_results = o.maximum_results + + obj.user_data = {} + + obj.writer = o.writer + + self._reset(obj) + + return setmetatable(obj, self) +end + +function Job:_reset() + self.is_shutdown = nil + + if self._shutdown_check and uv.is_active(self._shutdown_check) and not uv.is_closing(self._shutdown_check) then + vim.api.nvim_err_writeln(debug.traceback "We may be memory leaking here. Please report to TJ.") + end + self._shutdown_check = uv.new_check() + + self.stdin = nil + self.stdout = nil + self.stderr = nil + + self._stdout_reader = nil + self._stderr_reader = nil + + if self.enable_recording then + self._stdout_results = {} + self._stderr_results = {} + else + self._stdout_results = nil + self._stderr_results = nil + end +end + +--- Stop a job and close all handles +function Job:_stop() + close_safely(self, "stdin") + close_safely(self, "stderr") + close_safely(self, "stdout") + close_safely(self, "handle") +end + +function Job:_pipes_are_closed(options) + for _, pipe in ipairs { options.stdin, options.stdout, options.stderr } do + if pipe and not uv.is_closing(pipe) then + return false + end + end + + return true +end + +--- Shutdown a job. +function Job:shutdown(code, signal) + if self._shutdown_check and uv.is_active(self._shutdown_check) then + -- shutdown has already started + return + end + + self:_shutdown(code, signal) +end + +function Job:_shutdown(code, signal) + if self.is_shutdown then + return + end + + self.code = code + self.signal = signal + + if self._stdout_reader then + pcall(self._stdout_reader, nil, nil, true) + end + + if self._stderr_reader then + pcall(self._stderr_reader, nil, nil, true) + end + + if self._user_on_exit then + self:_user_on_exit(code, signal) + end + + for _, v in ipairs(self._additional_on_exit_callbacks) do + v(self, code, signal) + end + + if self.stdout then + self.stdout:read_stop() + end + + if self.stderr then + self.stderr:read_stop() + end + + self:_stop() + + self.is_shutdown = true + + self._stdout_reader = nil + self._stderr_reader = nil +end + +function Job:_create_uv_options() + local options = {} + + options.command = self.command + options.args = self.args + options.stdio = { self.stdin, self.stdout, self.stderr } + + if self._raw_cwd then + options.cwd = expand(self._raw_cwd) + end + if self.env then + options.env = self.env + end + + if self.detached then + options.detached = true + end + + return options +end + +local on_output = function(self, result_key, cb) + return coroutine.wrap(function(err, data, is_complete) + local result_index = 1 + + local line, start, result_line, found_newline + + -- We repeat forever as a coroutine so that we can keep calling this. + while true do + if data then + data = data:gsub("\r", "") + + local processed_index = 1 + local data_length = #data + 1 + + repeat + start = string.find(data, "\n", processed_index, true) or data_length + line = string.sub(data, processed_index, start - 1) + found_newline = start ~= data_length + + -- Concat to last line if there was something there already. + -- This happens when "data" is broken into chunks and sometimes + -- the content is sent without any newlines. + if result_line then + -- results[result_index] = results[result_index] .. line + result_line = result_line .. line + + -- Only put in a new line when we actually have new data to split. + -- This is generally only false when we do end with a new line. + -- It prevents putting in a "" to the end of the results. + elseif start ~= processed_index or found_newline then + -- results[result_index] = line + result_line = line + + -- Otherwise, we don't need to do anything. + end + + if found_newline then + if not result_line then + return vim.api.nvim_err_writeln( + "Broken data thing due to: " .. tostring(result_line) .. " " .. tostring(data) + ) + end + + if self.enable_recording then + self[result_key][result_index] = result_line + end + + if cb then + cb(err, result_line, self) + end + + -- Stop processing if we've surpassed the maximum. + if self._maximum_results and result_index > self._maximum_results then + -- Shutdown once we get the chance. + -- Can't call it here, because we'll just keep calling ourselves. + vim.schedule(function() + self:shutdown() + end) + + return + end + + result_index = result_index + 1 + result_line = nil + end + + processed_index = start + 1 + until not found_newline + end + + if self.enable_recording then + self[result_key][result_index] = result_line + end + + -- If we didn't get a newline on the last execute, send the final results. + if cb and is_complete and not found_newline then + cb(err, result_line, self) + end + + if data == nil or is_complete then + return + end + + err, data, is_complete = coroutine.yield() + end + end) +end + +--- Stop previous execution and add new pipes. +--- Also regenerates pipes of writer. +function Job:_prepare_pipes() + self:_stop() + + if self.writer then + if Job.is_job(self.writer) then + self.writer:_prepare_pipes() + self.stdin = self.writer.stdout + elseif self.writer.write then + self.stdin = self.writer + end + end + + if not self.stdin then + self.stdin = self.interactive and uv.new_pipe(false) or nil + end + + self.stdout = uv.new_pipe(false) + self.stderr = uv.new_pipe(false) +end + +--- Execute job. Should be called only after preprocessing is done. +function Job:_execute() + local options = self:_create_uv_options() + + if self._user_on_start then + self:_user_on_start() + end + + self.handle, self.pid = uv.spawn(options.command, options, shutdown_factory(self, options)) + + if not self.handle then + error(debug.traceback("Failed to spawn process: " .. vim.inspect(self))) + end + + if self.enable_handlers then + self._stdout_reader = on_output(self, "_stdout_results", self._user_on_stdout) + self.stdout:read_start(self._stdout_reader) + + self._stderr_reader = on_output(self, "_stderr_results", self._user_on_stderr) + self.stderr:read_start(self._stderr_reader) + end + + if self.writer then + if Job.is_job(self.writer) then + self.writer:_execute() + elseif type(self.writer) == "table" and compat.islist(self.writer) then + local writer_len = #self.writer + for i, v in ipairs(self.writer) do + self.stdin:write(v) + if i ~= writer_len then + self.stdin:write "\n" + else + self.stdin:write("\n", function() + pcall(self.stdin.close, self.stdin) + end) + end + end + elseif type(self.writer) == "string" then + self.stdin:write(self.writer, function() + self.stdin:close() + end) + elseif self.writer.write then + self.stdin = self.writer + else + error("Unknown self.writer: " .. vim.inspect(self.writer)) + end + end + + return self +end + +function Job:start() + self:_reset() + self:_prepare_pipes() + self:_execute() +end + +function Job:sync(timeout, wait_interval) + self:start() + self:wait(timeout, wait_interval) + + return self.enable_recording and self:result() or nil, self.code +end + +function Job:result() + assert(self.enable_recording, "'enabled_recording' is not enabled for this job.") + return self._stdout_results +end + +function Job:stderr_result() + assert(self.enable_recording, "'enabled_recording' is not enabled for this job.") + return self._stderr_results +end + +function Job:pid() + return self.pid +end + +function Job:wait(timeout, wait_interval, should_redraw) + timeout = timeout or 5000 + wait_interval = wait_interval or 10 + + if self.handle == nil then + local msg = vim.inspect(self) + vim.schedule(function() + vim.api.nvim_err_writeln(msg) + end) + + return + end + + -- Wait five seconds, or until timeout. + local wait_result = vim.wait(timeout, function() + if should_redraw then + vim.cmd [[redraw!]] + end + + if self.is_shutdown then + assert(not self.handle or self.handle:is_closing(), "Job must be shutdown if it's closing") + end + + return self.is_shutdown + end, wait_interval, not should_redraw) + + if not wait_result then + error( + string.format( + "'%s %s' was unable to complete in %s ms", + self.command, + table.concat(self.args or {}, " "), + timeout + ) + ) + end + + return self +end + +function Job:co_wait(wait_time) + wait_time = wait_time or 5 + + if self.handle == nil then + vim.api.nvim_err_writeln(vim.inspect(self)) + return + end + + while not vim.wait(wait_time, function() + return self.is_shutdown + end) do + coroutine.yield() + end + + return self +end + +--- Wait for all jobs to complete +function Job.join(...) + local jobs_to_wait = { ... } + local num_jobs = table.getn(jobs_to_wait) + + -- last entry can be timeout + local timeout + if type(jobs_to_wait[num_jobs]) == "number" then + timeout = table.remove(jobs_to_wait, num_jobs) + num_jobs = num_jobs - 1 + end + + local completed = 0 + + return vim.wait(timeout or 10000, function() + for index, current_job in pairs(jobs_to_wait) do + if current_job.is_shutdown then + jobs_to_wait[index] = nil + completed = completed + 1 + end + end + + return num_jobs == completed + end) +end + +local _request_id = 0 +local _request_status = {} + +function Job:and_then(next_job) + self:add_on_exit_callback(function() + next_job:start() + end) +end + +function Job:and_then_wrap(next_job) + self:add_on_exit_callback(vim.schedule_wrap(function() + next_job:start() + end)) +end + +function Job:after(fn) + self:add_on_exit_callback(fn) + return self +end + +function Job:and_then_on_success(next_job) + self:add_on_exit_callback(function(_, code) + if code == 0 then + next_job:start() + end + end) +end + +function Job:and_then_on_success_wrap(next_job) + self:add_on_exit_callback(vim.schedule_wrap(function(_, code) + if code == 0 then + next_job:start() + end + end)) +end + +function Job:after_success(fn) + self:add_on_exit_callback(function(j, code, signal) + if code == 0 then + fn(j, code, signal) + end + end) +end + +function Job:and_then_on_failure(next_job) + self:add_on_exit_callback(function(_, code) + if code ~= 0 then + next_job:start() + end + end) +end + +function Job:and_then_on_failure_wrap(next_job) + self:add_on_exit_callback(vim.schedule_wrap(function(_, code) + if code ~= 0 then + next_job:start() + end + end)) +end + +function Job:after_failure(fn) + self:add_on_exit_callback(function(j, code, signal) + if code ~= 0 then + fn(j, code, signal) + end + end) +end + +function Job.chain(...) + _request_id = _request_id + 1 + _request_status[_request_id] = false + + local jobs = { ... } + + for index = 2, #jobs do + local prev_job = jobs[index - 1] + local job = jobs[index] + + prev_job:add_on_exit_callback(vim.schedule_wrap(function() + job:start() + end)) + end + + local last_on_exit = jobs[#jobs]._user_on_exit + jobs[#jobs]._user_on_exit = function(self, err, data) + if last_on_exit then + last_on_exit(self, err, data) + end + + _request_status[_request_id] = true + end + + jobs[1]:start() + + return _request_id +end + +function Job.chain_status(id) + return _request_status[id] +end + +function Job.is_job(item) + if type(item) ~= "table" then + return false + end + + return getmetatable(item) == Job +end + +function Job:add_on_exit_callback(cb) + table.insert(self._additional_on_exit_callbacks, cb) +end + +--- Send data to a job. +function Job:send(data) + if not self.stdin then + error "job has no 'stdin'. Have you run `job:start()` yet?" + end + + self.stdin:write(data) +end + +return Job diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/json.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/json.lua new file mode 100644 index 00000000..cf4358dc --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/json.lua @@ -0,0 +1,100 @@ +-- based on https://github.com/sindresorhus/strip-json-comments + +local singleComment = "singleComment" +local multiComment = "multiComment" +local stripWithoutWhitespace = function() + return "" +end + +local function slice(str, from, to) + from = from or 1 + to = to or #str + return str:sub(from, to) +end + +local stripWithWhitespace = function(str, from, to) + return slice(str, from, to):gsub("%S", " ") +end + +local isEscaped = function(jsonString, quotePosition) + local index = quotePosition - 1 + local backslashCount = 0 + + while jsonString:sub(index, index) == "\\" do + index = index - 1 + backslashCount = backslashCount + 1 + end + return backslashCount % 2 == 1 and true or false +end + +local M = {} + +-- Strips any json comments from a json string. +-- The resulting string can then be used by `vim.fn.json_decode` +-- +---@param jsonString string +---@param options table +--- * whitespace: +--- - defaults to true +--- - when true, comments will be replaced by whitespace +--- - when false, comments will be stripped +function M.json_strip_comments(jsonString, options) + options = options or {} + local strip = options.whitespace == false and stripWithoutWhitespace or stripWithWhitespace + + local insideString = false + local insideComment = false + local offset = 1 + local result = "" + local skip = false + + for i = 1, #jsonString, 1 do + if skip then + skip = false + else + local currentCharacter = jsonString:sub(i, i) + local nextCharacter = jsonString:sub(i + 1, i + 1) + + if not insideComment and currentCharacter == '"' then + local escaped = isEscaped(jsonString, i) + if not escaped then + insideString = not insideString + end + end + + if not insideString then + if not insideComment and currentCharacter .. nextCharacter == "//" then + result = result .. slice(jsonString, offset, i - 1) + offset = i + insideComment = singleComment + skip = true + elseif insideComment == singleComment and currentCharacter .. nextCharacter == "\r\n" then + i = i + 1 + skip = true + insideComment = false + result = result .. strip(jsonString, offset, i - 1) + offset = i + elseif insideComment == singleComment and currentCharacter == "\n" then + insideComment = false + result = result .. strip(jsonString, offset, i - 1) + offset = i + elseif not insideComment and currentCharacter .. nextCharacter == "/*" then + result = result .. slice(jsonString, offset, i - 1) + offset = i + insideComment = multiComment + skip = true + elseif insideComment == multiComment and currentCharacter .. nextCharacter == "*/" then + i = i + 1 + skip = true + insideComment = false + result = result .. strip(jsonString, offset, i) + offset = i + 1 + end + end + end + end + + return result .. (insideComment and strip(slice(jsonString, offset)) or slice(jsonString, offset)) +end + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/log.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/log.lua new file mode 100644 index 00000000..1bf091bc --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/log.lua @@ -0,0 +1,235 @@ +-- log.lua +-- Does only support logging source files. +-- +-- Inspired by rxi/log.lua +-- Modified by tjdevries and can be found at github.com/tjdevries/vlog.nvim +-- +-- This library is free software; you can redistribute it and/or modify it +-- under the terms of the MIT license. See LICENSE for details. + +local Path = require "plenary.path" + +local p_debug = vim.fn.getenv "DEBUG_PLENARY" +if p_debug == vim.NIL then + p_debug = false +end + +-- User configuration section +local default_config = { + -- Name of the plugin. Prepended to log messages. + plugin = "plenary", + + -- Should print the output to neovim while running. + -- values: 'sync','async',false + use_console = "async", + + -- Should highlighting be used in console (using echohl). + highlights = true, + + -- Should write to a file. + -- Default output for logging file is `stdpath("cache")/plugin`. + use_file = true, + + -- Output file has precedence over plugin, if not nil. + -- Used for the logging file, if not nil and use_file == true. + outfile = nil, + + -- Should write to the quickfix list. + use_quickfix = false, + + -- Any messages above this level will be logged. + level = p_debug and "debug" or "info", + + -- Level configuration. + modes = { + { name = "trace", hl = "Comment" }, + { name = "debug", hl = "Comment" }, + { name = "info", hl = "None" }, + { name = "warn", hl = "WarningMsg" }, + { name = "error", hl = "ErrorMsg" }, + { name = "fatal", hl = "ErrorMsg" }, + }, + + -- Can limit the number of decimals displayed for floats. + float_precision = 0.01, + + -- Adjust content as needed, but must keep function parameters to be filled + -- by library code. + ---@param is_console boolean If output is for console or log file. + ---@param mode_name string Level configuration 'modes' field 'name' + ---@param src_path string Path to source file given by debug.info.source + ---@param src_line integer Line into source file given by debug.info.currentline + ---@param msg string Message, which is later on escaped, if needed. + fmt_msg = function(is_console, mode_name, src_path, src_line, msg) + local nameupper = mode_name:upper() + local lineinfo = src_path .. ":" .. src_line + if is_console then + return string.format("[%-6s%s] %s: %s", nameupper, os.date "%H:%M:%S", lineinfo, msg) + else + return string.format("[%-6s%s] %s: %s\n", nameupper, os.date(), lineinfo, msg) + end + end, +} + +-- {{{ NO NEED TO CHANGE +local log = {} + +local unpack = unpack or table.unpack + +log.new = function(config, standalone) + config = vim.tbl_deep_extend("force", default_config, config) + + local outfile = vim.F.if_nil( + config.outfile, + Path:new(vim.api.nvim_call_function("stdpath", { "cache" }), config.plugin .. ".log").filename + ) + + local obj + if standalone then + obj = log + else + obj = config + end + + local levels = {} + for i, v in ipairs(config.modes) do + levels[v.name] = i + end + + local round = function(x, increment) + if x == 0 then + return x + end + increment = increment or 1 + x = x / increment + return (x > 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)) * increment + end + + local make_string = function(...) + local t = {} + for i = 1, select("#", ...) do + local x = select(i, ...) + + if type(x) == "number" and config.float_precision then + x = tostring(round(x, config.float_precision)) + elseif type(x) == "table" then + x = vim.inspect(x) + else + x = tostring(x) + end + + t[#t + 1] = x + end + return table.concat(t, " ") + end + + local log_at_level = function(level, level_config, message_maker, ...) + -- Return early if we're below the config.level + if level < levels[config.level] then + return + end + local msg = message_maker(...) + local info = debug.getinfo(config.info_level or 2, "Sl") + local src_path = info.source:sub(2) + local src_line = info.currentline + -- Output to console + if config.use_console then + local log_to_console = function() + local console_string = config.fmt_msg(true, level_config.name, src_path, src_line, msg) + + if config.highlights and level_config.hl then + vim.cmd(string.format("echohl %s", level_config.hl)) + end + + local split_console = vim.split(console_string, "\n") + for _, v in ipairs(split_console) do + local formatted_msg = string.format("[%s] %s", config.plugin, vim.fn.escape(v, [["\]])) + + local ok = pcall(vim.cmd, string.format([[echom "%s"]], formatted_msg)) + if not ok then + vim.api.nvim_out_write(msg .. "\n") + end + end + + if config.highlights and level_config.hl then + vim.cmd "echohl NONE" + end + end + if config.use_console == "sync" and not vim.in_fast_event() then + log_to_console() + else + vim.schedule(log_to_console) + end + end + + -- Output to log file + if config.use_file then + local outfile_parent_path = Path:new(outfile):parent() + if not outfile_parent_path:exists() then + outfile_parent_path:mkdir { parents = true } + end + local fp = assert(io.open(outfile, "a")) + local str = config.fmt_msg(false, level_config.name, src_path, src_line, msg) + fp:write(str) + fp:close() + end + + -- Output to quickfix + if config.use_quickfix then + local nameupper = level_config.name:upper() + local formatted_msg = string.format("[%s] %s", nameupper, msg) + local qf_entry = { + -- remove the @ getinfo adds to the file path + filename = info.source:sub(2), + lnum = info.currentline, + col = 1, + text = formatted_msg, + } + vim.fn.setqflist({ qf_entry }, "a") + end + end + + for i, x in ipairs(config.modes) do + -- log.info("these", "are", "separated") + obj[x.name] = function(...) + return log_at_level(i, x, make_string, ...) + end + + -- log.fmt_info("These are %s strings", "formatted") + obj[("fmt_%s"):format(x.name)] = function(...) + return log_at_level(i, x, function(...) + local passed = { ... } + local fmt = table.remove(passed, 1) + local inspected = {} + for _, v in ipairs(passed) do + table.insert(inspected, vim.inspect(v)) + end + return string.format(fmt, unpack(inspected)) + end, ...) + end + + -- log.lazy_info(expensive_to_calculate) + obj[("lazy_%s"):format(x.name)] = function() + return log_at_level(i, x, function(f) + return f() + end) + end + + -- log.file_info("do not print") + obj[("file_%s"):format(x.name)] = function(vals, override) + local original_console = config.use_console + config.use_console = false + config.info_level = override.info_level + log_at_level(i, x, make_string, unpack(vals)) + config.use_console = original_console + config.info_level = nil + end + end + + return obj +end + +log.new(default_config, true) +-- }}} + +return log diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/lsp/override.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/lsp/override.lua new file mode 100644 index 00000000..76bf02a4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/lsp/override.lua @@ -0,0 +1,30 @@ +local vim = vim + +local M = {} + +M._original_functions = {} + +--- Override an lsp method default callback +--- @param method string +--- @param new_function function +function M.override(method, new_function) + if M._original_functions[method] == nil then + M._original_functions[method] = vim.lsp.callbacks[method] + end + + vim.lsp.callbacks[method] = new_function +end + +--- Get the original method callback +--- useful if you only want to override in some circumstances +--- +--- @param method string +function M.get_original_function(method) + if M._original_functions[method] == nil then + M._original_functions[method] = vim.lsp.callbacks[method] + end + + return M._original_functions[method] +end + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/neorocks/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/neorocks/init.lua new file mode 100644 index 00000000..df95ac30 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/neorocks/init.lua @@ -0,0 +1 @@ +error "neorocks is no longer supported. Please use packer.nvim or other project for neorocks usage." diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/nvim_meta.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/nvim_meta.lua new file mode 100644 index 00000000..12d9e812 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/nvim_meta.lua @@ -0,0 +1,18 @@ +local get_lua_version = function() + if jit then + return { + lua = string.gsub(_VERSION, "Lua ", ""), + jit = not not string.find(jit.version, "LuaJIT"), + version = string.gsub(jit.version, "LuaJIT ", ""), + } + end + + error("NEOROCKS: Unsupported Lua Versions", _VERSION) +end + +return { + -- Is run in `--headless` mode. + is_headless = (#vim.api.nvim_list_uis() == 0), + + lua_jit = get_lua_version(), +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/operators.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/operators.lua new file mode 100644 index 00000000..ccc00c9f --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/operators.lua @@ -0,0 +1,100 @@ +---@brief [[ +---Operators that are functions. +---This is useful when you want to pass operators to higher order functions. +---Lua has no currying so we have to make a function for each operator. +---@brief ]] + +return { + ---------------------------------------------------------------------------- + -- Comparison operators + ---------------------------------------------------------------------------- + lt = function(a, b) + return a < b + end, + le = function(a, b) + return a <= b + end, + eq = function(a, b) + return a == b + end, + ne = function(a, b) + return a ~= b + end, + ge = function(a, b) + return a >= b + end, + gt = function(a, b) + return a > b + end, + + ---------------------------------------------------------------------------- + -- Arithmetic operators + ---------------------------------------------------------------------------- + add = function(a, b) + return a + b + end, + div = function(a, b) + return a / b + end, + floordiv = function(a, b) + return math.floor(a / b) + end, + intdiv = function(a, b) + local q = a / b + if a >= 0 then + return math.floor(q) + else + return math.ceil(q) + end + end, + mod = function(a, b) + return a % b + end, + mul = function(a, b) + return a * b + end, + neq = function(a) + return -a + end, + unm = function(a) + return -a + end, -- an alias + pow = function(a, b) + return a ^ b + end, + sub = function(a, b) + return a - b + end, + truediv = function(a, b) + return a / b + end, + + ---------------------------------------------------------------------------- + -- String operators + ---------------------------------------------------------------------------- + concat = function(a, b) + return a .. b + end, + len = function(a) + return #a + end, + length = function(a) + return #a + end, -- an alias + + ---------------------------------------------------------------------------- + -- Logical operators + ---------------------------------------------------------------------------- + land = function(a, b) + return a and b + end, + lor = function(a, b) + return a or b + end, + lnot = function(a) + return not a + end, + truth = function(a) + return not not a + end, +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/path.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/path.lua new file mode 100644 index 00000000..51229f18 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/path.lua @@ -0,0 +1,944 @@ +--- Path.lua +--- +--- Goal: Create objects that are extremely similar to Python's `Path` Objects. +--- Reference: https://docs.python.org/3/library/pathlib.html + +local bit = require "plenary.bit" +local uv = vim.loop + +local F = require "plenary.functional" + +local S_IF = { + -- S_IFDIR = 0o040000 # directory + DIR = 0x4000, + -- S_IFREG = 0o100000 # regular file + REG = 0x8000, +} + +local path = {} +path.home = vim.loop.os_homedir() + +path.sep = (function() + if jit then + local os = string.lower(jit.os) + if os ~= "windows" then + return "/" + else + return "\\" + end + else + return package.config:sub(1, 1) + end +end)() + +path.root = (function() + if path.sep == "/" then + return function() + return "/" + end + else + return function(base) + base = base or vim.loop.cwd() + return base:sub(1, 1) .. ":\\" + end + end +end)() + +path.S_IF = S_IF + +local band = function(reg, value) + return bit.band(reg, value) == reg +end + +local concat_paths = function(...) + return table.concat({ ... }, path.sep) +end + +local function is_root(pathname) + if path.sep == "\\" then + return string.match(pathname, "^[A-Z]:\\?$") + end + return pathname == "/" +end + +local _split_by_separator = (function() + local formatted = string.format("([^%s]+)", path.sep) + return function(filepath) + local t = {} + for str in string.gmatch(filepath, formatted) do + table.insert(t, str) + end + return t + end +end)() + +local is_uri = function(filename) + return string.match(filename, "^%a[%w+-.]*://") ~= nil +end + +local is_absolute = function(filename, sep) + if sep == "\\" then + return string.match(filename, "^[%a]:[\\/].*$") ~= nil + end + return string.sub(filename, 1, 1) == sep +end + +local function _normalize_path(filename, cwd) + if is_uri(filename) then + return filename + end + + -- handles redundant `./` in the middle + local redundant = path.sep .. "%." .. path.sep + if filename:match(redundant) then + filename = filename:gsub(redundant, path.sep) + end + + local out_file = filename + + local has = string.find(filename, path.sep .. "..", 1, true) or string.find(filename, ".." .. path.sep, 1, true) + + if has then + local is_abs = is_absolute(filename, path.sep) + local split_without_disk_name = function(filename_local) + local parts = _split_by_separator(filename_local) + -- Remove disk name part on Windows + if path.sep == "\\" and is_abs then + table.remove(parts, 1) + end + return parts + end + + local parts = split_without_disk_name(filename) + local idx = 1 + local initial_up_count = 0 + + repeat + if parts[idx] == ".." then + if idx == 1 then + initial_up_count = initial_up_count + 1 + end + table.remove(parts, idx) + table.remove(parts, idx - 1) + if idx > 1 then + idx = idx - 2 + else + idx = idx - 1 + end + end + idx = idx + 1 + until idx > #parts + + local prefix = "" + if is_abs or #split_without_disk_name(cwd) == initial_up_count then + prefix = path.root(filename) + end + + out_file = prefix .. table.concat(parts, path.sep) + end + + return out_file +end + +local clean = function(pathname) + if is_uri(pathname) then + return pathname + end + + -- Remove double path seps, it's annoying + pathname = pathname:gsub(path.sep .. path.sep, path.sep) + + -- Remove trailing path sep if not root + if not is_root(pathname) and pathname:sub(-1) == path.sep then + return pathname:sub(1, -2) + end + return pathname +end + +-- S_IFCHR = 0o020000 # character device +-- S_IFBLK = 0o060000 # block device +-- S_IFIFO = 0o010000 # fifo (named pipe) +-- S_IFLNK = 0o120000 # symbolic link +-- S_IFSOCK = 0o140000 # socket file + +---@class Path +local Path = { + path = path, +} + +local check_self = function(self) + if type(self) == "string" then + return Path:new(self) + end + + return self +end + +Path.__index = function(t, k) + local raw = rawget(Path, k) + if raw then + return raw + end + + if k == "_cwd" then + local cwd = uv.fs_realpath "." + t._cwd = cwd + return cwd + end + + if k == "_absolute" then + local absolute = uv.fs_realpath(t.filename) + t._absolute = absolute + return absolute + end +end + +-- TODO: Could use this to not have to call new... not sure +-- Path.__call = Path:new + +Path.__div = function(self, other) + assert(Path.is_path(self)) + assert(Path.is_path(other) or type(other) == "string") + + return self:joinpath(other) +end + +Path.__tostring = function(self) + return clean(self.filename) +end + +-- TODO: See where we concat the table, and maybe we could make this work. +Path.__concat = function(self, other) + return self.filename .. other +end + +Path.is_path = function(a) + return getmetatable(a) == Path +end + +function Path:new(...) + local args = { ... } + + if type(self) == "string" then + table.insert(args, 1, self) + self = Path -- luacheck: ignore + end + + local path_input + if #args == 1 then + path_input = args[1] + else + path_input = args + end + + -- If we already have a Path, it's fine. + -- Just return it + if Path.is_path(path_input) then + return path_input + end + + -- TODO: Should probably remove and dumb stuff like double seps, periods in the middle, etc. + local sep = path.sep + if type(path_input) == "table" then + sep = path_input.sep or path.sep + path_input.sep = nil + end + + local path_string + if type(path_input) == "table" then + -- TODO: It's possible this could be done more elegantly with __concat + -- But I'm unsure of what we'd do to make that happen + local path_objs = {} + for _, v in ipairs(path_input) do + if Path.is_path(v) then + table.insert(path_objs, v.filename) + else + assert(type(v) == "string") + table.insert(path_objs, v) + end + end + + path_string = table.concat(path_objs, sep) + else + assert(type(path_input) == "string", vim.inspect(path_input)) + path_string = path_input + end + + local obj = { + filename = path_string, + + _sep = sep, + } + + setmetatable(obj, Path) + + return obj +end + +function Path:_fs_filename() + return self:absolute() or self.filename +end + +function Path:_stat() + return uv.fs_stat(self:_fs_filename()) or {} + -- local stat = uv.fs_stat(self:absolute()) + -- if not self._absolute then return {} end + + -- if not self._stat_result then + -- self._stat_result = + -- end + + -- return self._stat_result +end + +function Path:_st_mode() + return self:_stat().mode or 0 +end + +function Path:joinpath(...) + return Path:new(self.filename, ...) +end + +function Path:absolute() + if self:is_absolute() then + return _normalize_path(self.filename, self._cwd) + else + return _normalize_path(self._absolute or table.concat({ self._cwd, self.filename }, self._sep), self._cwd) + end +end + +function Path:exists() + return not vim.tbl_isempty(self:_stat()) +end + +function Path:expand() + if is_uri(self.filename) then + return self.filename + end + + -- TODO support windows + local expanded + if string.find(self.filename, "~") then + expanded = string.gsub(self.filename, "^~", vim.loop.os_homedir()) + elseif string.find(self.filename, "^%.") then + expanded = vim.loop.fs_realpath(self.filename) + if expanded == nil then + expanded = vim.fn.fnamemodify(self.filename, ":p") + end + elseif string.find(self.filename, "%$") then + local rep = string.match(self.filename, "([^%$][^/]*)") + local val = os.getenv(rep) + if val then + expanded = string.gsub(string.gsub(self.filename, rep, val), "%$", "") + else + expanded = nil + end + else + expanded = self.filename + end + return expanded and expanded or error "Path not valid" +end + +function Path:make_relative(cwd) + if is_uri(self.filename) then + return self.filename + end + + self.filename = clean(self.filename) + cwd = clean(F.if_nil(cwd, self._cwd, cwd)) + if self.filename == cwd then + self.filename = "." + else + if cwd:sub(#cwd, #cwd) ~= path.sep then + cwd = cwd .. path.sep + end + + if self.filename:sub(1, #cwd) == cwd then + self.filename = self.filename:sub(#cwd + 1, -1) + end + end + + return self.filename +end + +function Path:normalize(cwd) + if is_uri(self.filename) then + return self.filename + end + + self:make_relative(cwd) + + -- Substitute home directory w/ "~" + -- string.gsub is not useful here because usernames with dashes at the end + -- will be seen as a regexp pattern rather than a raw string + local home = path.home + if string.sub(path.home, -1) ~= path.sep then + home = home .. path.sep + end + local start, finish = string.find(self.filename, home, 1, true) + if start == 1 then + self.filename = "~" .. path.sep .. string.sub(self.filename, (finish + 1), -1) + end + + return _normalize_path(clean(self.filename), self._cwd) +end + +local function shorten_len(filename, len, exclude) + len = len or 1 + exclude = exclude or { -1 } + local exc = {} + + -- get parts in a table + local parts = {} + local empty_pos = {} + for m in (filename .. path.sep):gmatch("(.-)" .. path.sep) do + if m ~= "" then + parts[#parts + 1] = m + else + table.insert(empty_pos, #parts + 1) + end + end + + for _, v in pairs(exclude) do + if v < 0 then + exc[v + #parts + 1] = true + else + exc[v] = true + end + end + + local final_path_components = {} + local count = 1 + for _, match in ipairs(parts) do + if not exc[count] and #match > len then + table.insert(final_path_components, string.sub(match, 1, len)) + else + table.insert(final_path_components, match) + end + table.insert(final_path_components, path.sep) + count = count + 1 + end + + local l = #final_path_components -- so that we don't need to keep calculating length + table.remove(final_path_components, l) -- remove final slash + + -- add back empty positions + for i = #empty_pos, 1, -1 do + table.insert(final_path_components, empty_pos[i], path.sep) + end + + return table.concat(final_path_components) +end + +local shorten = (function() + local fallback = function(filename) + return shorten_len(filename, 1) + end + + if jit and path.sep ~= "\\" then + local ffi = require "ffi" + ffi.cdef [[ + typedef unsigned char char_u; + void shorten_dir(char_u *str); + ]] + local ffi_func = function(filename) + if not filename or is_uri(filename) then + return filename + end + + local c_str = ffi.new("char[?]", #filename + 1) + ffi.copy(c_str, filename) + ffi.C.shorten_dir(c_str) + return ffi.string(c_str) + end + local ok = pcall(ffi_func, "/tmp/path/file.lua") + if ok then + return ffi_func + else + return fallback + end + end + return fallback +end)() + +function Path:shorten(len, exclude) + assert(len ~= 0, "len must be at least 1") + if (len and len > 1) or exclude ~= nil then + return shorten_len(self.filename, len, exclude) + end + return shorten(self.filename) +end + +function Path:mkdir(opts) + opts = opts or {} + + local mode = opts.mode or 448 -- 0700 -> decimal + local parents = F.if_nil(opts.parents, false, opts.parents) + local exists_ok = F.if_nil(opts.exists_ok, true, opts.exists_ok) + + local exists = self:exists() + if not exists_ok and exists then + error("FileExistsError:" .. self:absolute()) + end + + -- fs_mkdir returns nil if folder exists + if not uv.fs_mkdir(self:_fs_filename(), mode) and not exists then + if parents then + local dirs = self:_split() + local processed = "" + for _, dir in ipairs(dirs) do + if dir ~= "" then + local joined = concat_paths(processed, dir) + if processed == "" and self._sep == "\\" then + joined = dir + end + local stat = uv.fs_stat(joined) or {} + local file_mode = stat.mode or 0 + if band(S_IF.REG, file_mode) then + error(string.format("%s is a regular file so we can't mkdir it", joined)) + elseif band(S_IF.DIR, file_mode) then + processed = joined + else + if uv.fs_mkdir(joined, mode) then + processed = joined + else + error("We couldn't mkdir: " .. joined) + end + end + end + end + else + error "FileNotFoundError" + end + end + + return true +end + +function Path:rmdir() + if not self:exists() then + return + end + + uv.fs_rmdir(self:absolute()) +end + +function Path:rename(opts) + opts = opts or {} + if not opts.new_name or opts.new_name == "" then + error "Please provide the new name!" + end + + -- handles `.`, `..`, `./`, and `../` + if opts.new_name:match "^%.%.?/?\\?.+" then + opts.new_name = { + uv.fs_realpath(opts.new_name:sub(1, 3)), + opts.new_name:sub(4, #opts.new_name), + } + end + + local new_path = Path:new(opts.new_name) + + if new_path:exists() then + error "File or directory already exists!" + end + + local status = uv.fs_rename(self:absolute(), new_path:absolute()) + self.filename = new_path.filename + + return status +end + +--- Copy files or folders with defaults akin to GNU's `cp`. +---@param opts table: options to pass to toggling registered actions +---@field destination string|Path: target file path to copy to +---@field recursive bool: whether to copy folders recursively (default: false) +---@field override bool: whether to override files (default: true) +---@field interactive bool: confirm if copy would override; precedes `override` (default: false) +---@field respect_gitignore bool: skip folders ignored by all detected `gitignore`s (default: false) +---@field hidden bool: whether to add hidden files in recursively copying folders (default: true) +---@field parents bool: whether to create possibly non-existing parent dirs of `opts.destination` (default: false) +---@field exists_ok bool: whether ok if `opts.destination` exists, if so folders are merged (default: true) +---@return table {[Path of destination]: bool} indicating success of copy; nested tables constitute sub dirs +function Path:copy(opts) + opts = opts or {} + opts.recursive = F.if_nil(opts.recursive, false, opts.recursive) + opts.override = F.if_nil(opts.override, true, opts.override) + + local dest = opts.destination + -- handles `.`, `..`, `./`, and `../` + if not Path.is_path(dest) then + if type(dest) == "string" and dest:match "^%.%.?/?\\?.+" then + dest = { + uv.fs_realpath(dest:sub(1, 3)), + dest:sub(4, #dest), + } + end + dest = Path:new(dest) + end + -- success is true in case file is copied, false otherwise + local success = {} + if not self:is_dir() then + if opts.interactive and dest:exists() then + vim.ui.select( + { "Yes", "No" }, + { prompt = string.format("Overwrite existing %s?", dest:absolute()) }, + function(_, idx) + success[dest] = uv.fs_copyfile(self:absolute(), dest:absolute(), { excl = idx ~= 1 }) or false + end + ) + else + -- nil: not overriden if `override = false` + success[dest] = uv.fs_copyfile(self:absolute(), dest:absolute(), { excl = not opts.override }) or false + end + return success + end + -- dir + if opts.recursive then + dest:mkdir { + parents = F.if_nil(opts.parents, false, opts.parents), + exists_ok = F.if_nil(opts.exists_ok, true, opts.exists_ok), + } + local scan = require "plenary.scandir" + local data = scan.scan_dir(self.filename, { + respect_gitignore = F.if_nil(opts.respect_gitignore, false, opts.respect_gitignore), + hidden = F.if_nil(opts.hidden, true, opts.hidden), + depth = 1, + add_dirs = true, + }) + for _, entry in ipairs(data) do + local entry_path = Path:new(entry) + local suffix = table.remove(entry_path:_split()) + local new_dest = dest:joinpath(suffix) + -- clear destination as it might be Path table otherwise failing w/ extend + opts.destination = nil + local new_opts = vim.tbl_deep_extend("force", opts, { destination = new_dest }) + -- nil: not overriden if `override = false` + success[new_dest] = entry_path:copy(new_opts) or false + end + return success + else + error(string.format("Warning: %s was not copied as `recursive=false`", self:absolute())) + end +end + +function Path:touch(opts) + opts = opts or {} + + local mode = opts.mode or 420 + local parents = F.if_nil(opts.parents, false, opts.parents) + + if self:exists() then + local new_time = os.time() + uv.fs_utime(self:_fs_filename(), new_time, new_time) + return + end + + if parents then + Path:new(self:parent()):mkdir { parents = true } + end + + local fd = uv.fs_open(self:_fs_filename(), "w", mode) + if not fd then + error("Could not create file: " .. self:_fs_filename()) + end + uv.fs_close(fd) + + return true +end + +function Path:rm(opts) + opts = opts or {} + + local recursive = F.if_nil(opts.recursive, false, opts.recursive) + if recursive then + local scan = require "plenary.scandir" + local abs = self:absolute() + + -- first unlink all files + scan.scan_dir(abs, { + hidden = true, + on_insert = function(file) + uv.fs_unlink(file) + end, + }) + + local dirs = scan.scan_dir(abs, { add_dirs = true, hidden = true }) + -- iterate backwards to clean up remaining dirs + for i = #dirs, 1, -1 do + uv.fs_rmdir(dirs[i]) + end + + -- now only abs is missing + uv.fs_rmdir(abs) + else + uv.fs_unlink(self:absolute()) + end +end + +-- Path:is_* {{{ +function Path:is_dir() + -- TODO: I wonder when this would be better, if ever. + -- return self:_stat().type == 'directory' + + return band(S_IF.DIR, self:_st_mode()) +end + +function Path:is_absolute() + return is_absolute(self.filename, self._sep) +end +-- }}} + +function Path:_split() + return vim.split(self:absolute(), self._sep) +end + +local _get_parent = (function() + local formatted = string.format("^(.+)%s[^%s]+", path.sep, path.sep) + return function(abs_path) + local parent = abs_path:match(formatted) + if parent ~= nil and not parent:find(path.sep) then + return parent .. path.sep + end + return parent + end +end)() + +function Path:parent() + return Path:new(_get_parent(self:absolute()) or path.root(self:absolute())) +end + +function Path:parents() + local results = {} + local cur = self:absolute() + repeat + cur = _get_parent(cur) + table.insert(results, cur) + until not cur + table.insert(results, path.root(self:absolute())) + return results +end + +function Path:is_file() + return self:_stat().type == "file" and true or nil +end + +-- TODO: +-- Maybe I can use libuv for this? +function Path:open() end + +function Path:close() end + +function Path:write(txt, flag, mode) + assert(flag, [[Path:write_text requires a flag! For example: 'w' or 'a']]) + + mode = mode or 438 + + local fd = assert(uv.fs_open(self:_fs_filename(), flag, mode)) + assert(uv.fs_write(fd, txt, -1)) + assert(uv.fs_close(fd)) +end + +-- TODO: Asyncify this and use vim.wait in the meantime. +-- This will allow other events to happen while we're waiting! +function Path:_read() + self = check_self(self) + + local fd = assert(uv.fs_open(self:_fs_filename(), "r", 438)) -- for some reason test won't pass with absolute + local stat = assert(uv.fs_fstat(fd)) + local data = assert(uv.fs_read(fd, stat.size, 0)) + assert(uv.fs_close(fd)) + + return data +end + +function Path:_read_async(callback) + vim.loop.fs_open(self.filename, "r", 438, function(err_open, fd) + if err_open then + print("We tried to open this file but couldn't. We failed with following error message: " .. err_open) + return + end + vim.loop.fs_fstat(fd, function(err_fstat, stat) + assert(not err_fstat, err_fstat) + if stat.type ~= "file" then + return callback "" + end + vim.loop.fs_read(fd, stat.size, 0, function(err_read, data) + assert(not err_read, err_read) + vim.loop.fs_close(fd, function(err_close) + assert(not err_close, err_close) + return callback(data) + end) + end) + end) + end) +end + +function Path:read(callback) + if callback then + return self:_read_async(callback) + end + return self:_read() +end + +function Path:head(lines) + lines = lines or 10 + self = check_self(self) + local chunk_size = 256 + + local fd = uv.fs_open(self:_fs_filename(), "r", 438) + if not fd then + return + end + local stat = assert(uv.fs_fstat(fd)) + if stat.type ~= "file" then + uv.fs_close(fd) + return nil + end + + local data = "" + local index, count = 0, 0 + while count < lines and index < stat.size do + local read_chunk = assert(uv.fs_read(fd, chunk_size, index)) + + local i = 0 + for char in read_chunk:gmatch "." do + if char == "\n" then + count = count + 1 + if count >= lines then + break + end + end + index = index + 1 + i = i + 1 + end + data = data .. read_chunk:sub(1, i) + end + assert(uv.fs_close(fd)) + + -- Remove potential newline at end of file + if data:sub(-1) == "\n" then + data = data:sub(1, -2) + end + + return data +end + +function Path:tail(lines) + lines = lines or 10 + self = check_self(self) + local chunk_size = 256 + + local fd = uv.fs_open(self:_fs_filename(), "r", 438) + if not fd then + return + end + local stat = assert(uv.fs_fstat(fd)) + if stat.type ~= "file" then + uv.fs_close(fd) + return nil + end + + local data = "" + local index, count = stat.size - 1, 0 + while count < lines and index > 0 do + local real_index = index - chunk_size + if real_index < 0 then + chunk_size = chunk_size + real_index + real_index = 0 + end + + local read_chunk = assert(uv.fs_read(fd, chunk_size, real_index)) + + local i = #read_chunk + while i > 0 do + local char = read_chunk:sub(i, i) + if char == "\n" then + count = count + 1 + if count >= lines then + break + end + end + index = index - 1 + i = i - 1 + end + data = read_chunk:sub(i + 1, #read_chunk) .. data + end + assert(uv.fs_close(fd)) + + return data +end + +function Path:readlines() + self = check_self(self) + + local data = self:read() + + data = data:gsub("\r", "") + return vim.split(data, "\n") +end + +function Path:iter() + local data = self:readlines() + local i = 0 + local n = #data + return function() + i = i + 1 + if i <= n then + return data[i] + end + end +end + +function Path:readbyterange(offset, length) + self = check_self(self) + + local fd = uv.fs_open(self:_fs_filename(), "r", 438) + if not fd then + return + end + local stat = assert(uv.fs_fstat(fd)) + if stat.type ~= "file" then + uv.fs_close(fd) + return nil + end + + if offset < 0 then + offset = stat.size + offset + -- Windows fails if offset is < 0 even though offset is defined as signed + -- http://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_read + if offset < 0 then + offset = 0 + end + end + + local data = "" + while #data < length do + local read_chunk = assert(uv.fs_read(fd, length - #data, offset)) + if #read_chunk == 0 then + break + end + data = data .. read_chunk + offset = offset + #read_chunk + end + + assert(uv.fs_close(fd)) + + return data +end + +function Path:find_upwards(filename) + local folder = Path:new(self) + local root = path.root(folder) + + while folder:absolute() ~= root do + local p = folder:joinpath(filename) + if p:exists() then + return p + end + folder = folder:parent() + end + return "" +end + +return Path diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/popup/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/popup/init.lua new file mode 100644 index 00000000..1c50c065 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/popup/init.lua @@ -0,0 +1,489 @@ +--- popup.lua +--- +--- Wrapper to make the popup api from vim in neovim. +--- Hope to get this part merged in at some point in the future. +--- +--- Please make sure to update "POPUP.md" with any changes and/or notes. + +local Border = require "plenary.window.border" +local Window = require "plenary.window" +local utils = require "plenary.popup.utils" + +local if_nil = vim.F.if_nil + +local popup = {} + +popup._pos_map = { + topleft = "NW", + topright = "NE", + botleft = "SW", + botright = "SE", +} + +-- Keep track of hidden popups, so we can load them with popup.show() +popup._hidden = {} + +-- Keep track of popup borders, so we don't have to pass them between functions +popup._borders = {} + +local function dict_default(options, key, default) + if options[key] == nil then + return default[key] + else + return options[key] + end +end + +-- Callbacks to be called later by popup.execute_callback +popup._callbacks = {} + +-- Convert the positional {vim_options} to compatible neovim options and add them to {win_opts} +-- If an option is not given in {vim_options}, fall back to {default_opts} +local function add_position_config(win_opts, vim_options, default_opts) + default_opts = default_opts or {} + + local cursor_relative_pos = function(pos_str, dim) + assert(string.find(pos_str, "^cursor"), "Invalid value for " .. dim) + win_opts.relative = "cursor" + local line = 0 + if (pos_str):match "cursor%+(%d+)" then + line = line + tonumber((pos_str):match "cursor%+(%d+)") + elseif (pos_str):match "cursor%-(%d+)" then + line = line - tonumber((pos_str):match "cursor%-(%d+)") + end + return line + end + + -- Feels like maxheight, minheight, maxwidth, minwidth will all be related + -- + -- maxheight Maximum height of the contents, excluding border and padding. + -- minheight Minimum height of the contents, excluding border and padding. + -- maxwidth Maximum width of the contents, excluding border, padding and scrollbar. + -- minwidth Minimum width of the contents, excluding border, padding and scrollbar. + local width = if_nil(vim_options.width, default_opts.width) + local height = if_nil(vim_options.height, default_opts.height) + win_opts.width = utils.bounded(width, vim_options.minwidth, vim_options.maxwidth) + win_opts.height = utils.bounded(height, vim_options.minheight, vim_options.maxheight) + + if vim_options.line and vim_options.line ~= 0 then + if type(vim_options.line) == "string" then + win_opts.row = cursor_relative_pos(vim_options.line, "row") + else + win_opts.row = vim_options.line - 1 + end + else + win_opts.row = math.floor((vim.o.lines - win_opts.height) / 2) + end + + if vim_options.col and vim_options.col ~= 0 then + if type(vim_options.col) == "string" then + win_opts.col = cursor_relative_pos(vim_options.col, "col") + else + win_opts.col = vim_options.col - 1 + end + else + win_opts.col = math.floor((vim.o.columns - win_opts.width) / 2) + end + + -- pos + -- + -- Using "topleft", "topright", "botleft", "botright" defines what corner of the popup "line" + -- and "col" are used for. When not set "topleft" behaviour is used. + -- Alternatively "center" can be used to position the popup in the center of the Neovim window, + -- in which case "line" and "col" are ignored. + if vim_options.pos then + if vim_options.pos == "center" then + vim_options.line = 0 + vim_options.col = 0 + win_opts.anchor = "NW" + else + win_opts.anchor = popup._pos_map[vim_options.pos] + end + else + win_opts.anchor = "NW" -- This is the default, but makes `posinvert` easier to implement + end + + -- , fixed When FALSE (the default), and: + -- , - "pos" is "botleft" or "topleft", and + -- , - "wrap" is off, and + -- , - the popup would be truncated at the right edge of + -- , the screen, then + -- , the popup is moved to the left so as to fit the + -- , contents on the screen. Set to TRUE to disable this. +end + +function popup.create(what, vim_options) + vim_options = vim.deepcopy(vim_options) + + local bufnr + if type(what) == "number" then + bufnr = what + else + bufnr = vim.api.nvim_create_buf(false, true) + assert(bufnr, "Failed to create buffer") + + vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe") + vim.api.nvim_buf_set_option(bufnr, "modifiable", true) + + -- TODO: Handle list of lines + if type(what) == "string" then + what = { what } + else + assert(type(what) == "table", '"what" must be a table') + end + + -- padding List with numbers, defining the padding + -- above/right/below/left of the popup (similar to CSS). + -- An empty list uses a padding of 1 all around. The + -- padding goes around the text, inside any border. + -- Padding uses the 'wincolor' highlight. + -- Example: [1, 2, 1, 3] has 1 line of padding above, 2 + -- columns on the right, 1 line below and 3 columns on + -- the left. + if vim_options.padding then + local pad_top, pad_right, pad_below, pad_left + if vim.tbl_isempty(vim_options.padding) then + pad_top = 1 + pad_right = 1 + pad_below = 1 + pad_left = 1 + else + local padding = vim_options.padding + pad_top = padding[1] or 0 + pad_right = padding[2] or 0 + pad_below = padding[3] or 0 + pad_left = padding[4] or 0 + end + + local left_padding = string.rep(" ", pad_left) + local right_padding = string.rep(" ", pad_right) + for index = 1, #what do + what[index] = string.format("%s%s%s", left_padding, what[index], right_padding) + end + + for _ = 1, pad_top do + table.insert(what, 1, "") + end + + for _ = 1, pad_below do + table.insert(what, "") + end + end + + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, what) + end + + local option_defaults = { + posinvert = true, + zindex = 50, + } + + vim_options.width = if_nil(vim_options.width, 1) + if type(what) == "number" then + vim_options.height = vim.api.nvim_buf_line_count(what) + else + for _, v in ipairs(what) do + vim_options.width = math.max(vim_options.width, #v) + end + vim_options.height = #what + end + + local win_opts = {} + win_opts.relative = "editor" + win_opts.style = "minimal" + + -- Add positional and sizing config to win_opts + add_position_config(win_opts, vim_options, { width = 1, height = 1 }) + + -- posinvert, When FALSE the value of "pos" is always used. When + -- , TRUE (the default) and the popup does not fit + -- , vertically and there is more space on the other side + -- , then the popup is placed on the other side of the + -- , position indicated by "line". + if dict_default(vim_options, "posinvert", option_defaults) then + if win_opts.anchor == "NW" or win_opts.anchor == "NE" then + if win_opts.row + win_opts.height > vim.o.lines and win_opts.row * 2 > vim.o.lines then + -- Don't know why, but this is how vim adjusts it + win_opts.row = win_opts.row - win_opts.height - 2 + end + elseif win_opts.anchor == "SW" or win_opts.anchor == "SE" then + if win_opts.row - win_opts.height < 0 and win_opts.row * 2 < vim.o.lines then + -- Don't know why, but this is how vim adjusts it + win_opts.row = win_opts.row + win_opts.height + 2 + end + end + end + + -- textprop, When present the popup is positioned next to a text + -- , property with this name and will move when the text + -- , property moves. Use an empty string to remove. See + -- , |popup-textprop-pos|. + -- related: + -- textpropwin + -- textpropid + + -- zindex, Priority for the popup, default 50. Minimum value is + -- , 1, maximum value is 32000. + local zindex = dict_default(vim_options, "zindex", option_defaults) + win_opts.zindex = utils.bounded(zindex, 1, 32000) + + -- noautocmd, undocumented vim default per https://github.com/vim/vim/issues/5737 + win_opts.noautocmd = if_nil(vim_options.noautocmd, true) + + -- focusable, + -- vim popups are not focusable windows + win_opts.focusable = if_nil(vim_options.focusable, false) + + local win_id + if vim_options.hidden then + assert(false, "I have not implemented this yet and don't know how") + else + win_id = vim.api.nvim_open_win(bufnr, false, win_opts) + end + + -- Moved, handled after since we need the window ID + if vim_options.moved then + if vim_options.moved == "any" then + vim.lsp.util.close_preview_autocmd({ "CursorMoved", "CursorMovedI" }, win_id) + -- elseif vim_options.moved == "word" then + -- TODO: Handle word, WORD, expr, and the range functions... which seem hard? + end + else + local silent = false + vim.cmd( + string.format( + "autocmd BufDelete %s ++once ++nested :lua require('plenary.window').try_close(%s, true)", + (silent and "") or "", + bufnr, + win_id + ) + ) + end + + if vim_options.time then + local timer = vim.loop.new_timer() + timer:start( + vim_options.time, + 0, + vim.schedule_wrap(function() + Window.try_close(win_id, false) + end) + ) + end + + -- Buffer Options + if vim_options.cursorline then + vim.api.nvim_win_set_option(win_id, "cursorline", true) + end + + if vim_options.wrap ~= nil then + -- set_option wrap should/will trigger autocmd, see https://github.com/neovim/neovim/pull/13247 + if vim_options.noautocmd then + vim.cmd(string.format("noautocmd lua vim.api.nvim_set_option(%s, wrap, %s)", win_id, vim_options.wrap)) + else + vim.api.nvim_win_set_option(win_id, "wrap", vim_options.wrap) + end + end + + -- ===== Not Implemented Options ===== + -- flip: not implemented at the time of writing + -- Mouse: + -- mousemoved: no idea how to do the things with the mouse, so it's an exercise for the reader. + -- drag: mouses are hard + -- resize: mouses are hard + -- close: mouses are hard + -- + -- scrollbar + -- scrollbarhighlight + -- thumbhighlight + + -- tabpage: seems useless + + -- Create border + + local should_show_border = nil + local border_options = {} + + -- border List with numbers, defining the border thickness + -- above/right/below/left of the popup (similar to CSS). + -- Only values of zero and non-zero are recognized. + -- An empty list uses a border all around. + if vim_options.border then + should_show_border = true + + if type(vim_options.border) == "boolean" or vim.tbl_isempty(vim_options.border) then + border_options.border_thickness = Border._default_thickness + elseif #vim_options.border == 4 then + border_options.border_thickness = { + top = utils.bounded(vim_options.border[1], 0, 1), + right = utils.bounded(vim_options.border[2], 0, 1), + bot = utils.bounded(vim_options.border[3], 0, 1), + left = utils.bounded(vim_options.border[4], 0, 1), + } + else + error(string.format("Invalid configuration for border: %s", vim.inspect(vim_options.border))) + end + elseif vim_options.border == false then + should_show_border = false + end + + if (should_show_border == nil or should_show_border) and vim_options.borderchars then + should_show_border = true + + -- borderchars List with characters, defining the character to use + -- for the top/right/bottom/left border. Optionally + -- followed by the character to use for the + -- topleft/topright/botright/botleft corner. + -- Example: ['-', '|', '-', '|', '┌', '┐', '┘', '└'] + -- When the list has one character it is used for all. + -- When the list has two characters the first is used for + -- the border lines, the second for the corners. + -- By default a double line is used all around when + -- 'encoding' is "utf-8" and 'ambiwidth' is "single", + -- otherwise ASCII characters are used. + + local b_top, b_right, b_bot, b_left, b_topleft, b_topright, b_botright, b_botleft + if vim_options.borderchars == nil then + b_top, b_right, b_bot, b_left, b_topleft, b_topright, b_botright, b_botleft = + "═", "║", "═", "║", "╔", "╗", "╝", "╚" + elseif #vim_options.borderchars == 1 then + local b_char = vim_options.borderchars[1] + b_top, b_right, b_bot, b_left, b_topleft, b_topright, b_botright, b_botleft = + b_char, b_char, b_char, b_char, b_char, b_char, b_char, b_char + elseif #vim_options.borderchars == 2 then + local b_char = vim_options.borderchars[1] + local c_char = vim_options.borderchars[2] + b_top, b_right, b_bot, b_left, b_topleft, b_topright, b_botright, b_botleft = + b_char, b_char, b_char, b_char, c_char, c_char, c_char, c_char + elseif #vim_options.borderchars == 8 then + b_top, b_right, b_bot, b_left, b_topleft, b_topright, b_botright, b_botleft = unpack(vim_options.borderchars) + else + error(string.format 'Not enough arguments for "borderchars"') + end + + border_options.top = b_top + border_options.bot = b_bot + border_options.right = b_right + border_options.left = b_left + border_options.topleft = b_topleft + border_options.topright = b_topright + border_options.botright = b_botright + border_options.botleft = b_botleft + end + + -- title + if vim_options.title then + -- TODO: Check out how title works with weird border combos. + border_options.title = vim_options.title + end + + local border = nil + if should_show_border then + border_options.focusable = vim_options.border_focusable + border_options.highlight = vim_options.borderhighlight and string.format("Normal:%s", vim_options.borderhighlight) + border_options.titlehighlight = vim_options.titlehighlight + border = Border:new(bufnr, win_id, win_opts, border_options) + popup._borders[win_id] = border + end + + if vim_options.highlight then + vim.api.nvim_win_set_option( + win_id, + "winhl", + string.format("Normal:%s,EndOfBuffer:%s", vim_options.highlight, vim_options.highlight) + ) + end + + -- enter + local should_enter = vim_options.enter + if should_enter == nil then + should_enter = true + end + + if should_enter then + -- set focus after border creation so that it's properly placed (especially + -- in relative cursor layout) + if vim_options.noautocmd then + vim.cmd("noautocmd lua vim.api.nvim_set_current_win(" .. win_id .. ")") + else + vim.api.nvim_set_current_win(win_id) + end + end + + -- callback + if vim_options.callback then + popup._callbacks[bufnr] = function() + -- (jbyuki): Giving win_id is pointless here because it's closed right afterwards + -- but it might make more sense once hidden is implemented + local row, _ = unpack(vim.api.nvim_win_get_cursor(win_id)) + vim_options.callback(win_id, what[row]) + vim.api.nvim_win_close(win_id, true) + end + vim.api.nvim_buf_set_keymap( + bufnr, + "n", + "", + 'lua require"plenary.popup".execute_callback(' .. bufnr .. ")", + { noremap = true } + ) + end + + if vim_options.finalize_callback then + vim_options.finalize_callback(win_id, bufnr) + end + + -- TODO: Perhaps there's a way to return an object that looks like a window id, + -- but actually has some extra metadata about it. + -- + -- This would make `hidden` a lot easier to manage + return win_id, { + win_id = win_id, + border = border, + } +end + +-- Move popup with window id {win_id} to the position specified with {vim_options}. +-- {vim_options} may contain the following items that determine the popup position/size: +-- - line +-- - col +-- - height +-- - width +-- - maxheight/minheight +-- - maxwidth/minwidth +-- - pos +-- Unimplemented vim options here include: fixed +function popup.move(win_id, vim_options) + -- Create win_options + local win_opts = {} + win_opts.relative = "editor" + + local current_pos = vim.api.nvim_win_get_position(win_id) + local default_opts = { + width = vim.api.nvim_win_get_width(win_id), + height = vim.api.nvim_win_get_height(win_id), + row = current_pos[1], + col = current_pos[2], + } + + -- Add positional and sizing config to win_opts + add_position_config(win_opts, vim_options, default_opts) + + -- Update content window + vim.api.nvim_win_set_config(win_id, win_opts) + + -- Update border window (if present) + local border = popup._borders[win_id] + if border ~= nil then + border:move(win_opts, border._border_win_options) + end +end + +function popup.execute_callback(bufnr) + if popup._callbacks[bufnr] then + local wrapper = popup._callbacks[bufnr] + wrapper() + popup._callbacks[bufnr] = nil + end +end + +return popup diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/popup/utils.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/popup/utils.lua new file mode 100644 index 00000000..e665e150 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/popup/utils.lua @@ -0,0 +1,33 @@ +local utils = {} + +utils.bounded = function(value, min, max) + min = min or 0 + max = max or math.huge + + if min then + value = math.max(value, min) + end + if max then + value = math.min(value, max) + end + + return value +end + +utils.apply_defaults = function(original, defaults) + if original == nil then + original = {} + end + + original = vim.deepcopy(original) + + for k, v in pairs(defaults) do + if original[k] == nil then + original[k] = v + end + end + + return original +end + +return utils diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile.lua new file mode 100644 index 00000000..0afc4a7e --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile.lua @@ -0,0 +1,31 @@ +local profile = {} + +-- bundled version of upstream jit.p until LuaJIT is updated to include +-- https://github.com/LuaJIT/LuaJIT/commit/95140c50010c0557af66dac944403a1a65dd312c +local p = require'plenary.profile.p' + +---start profiling using LuaJIT profiler +---@param out name and path of log file +---@param opts table of options +--- flame (bool, default false) write log in flamegraph format +-- (see https://github.com/jonhoo/inferno) +function profile.start(out, opts) + out = out or "profile.log" + opts = opts or {} + local popts = "10,i1,s,m0" + if opts.flame then popts = popts .. ",G" end + p.start(popts, out) +end + +---stop profiling +profile.stop = p.stop + +function profile.benchmark(iterations, f, ...) + local start_time = vim.loop.hrtime() + for _ = 1, iterations do + f(...) + end + return (vim.loop.hrtime() - start_time) / 1E9 +end + +return profile diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile/lua_profiler.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile/lua_profiler.lua new file mode 100644 index 00000000..e29e06f4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile/lua_profiler.lua @@ -0,0 +1,252 @@ +--[[ Copyright (c) 2018-2020, Charles Mallah ]] +-- Released with MIT License +-- +-- Originally link: +-- https://github.com/charlesmallah/lua-profiler +-- +-- Hopefully will add some better neovim stuff in the future. +-- Shoutout to @clason for finding this. + + +---------------------------------------| +--- Configuration +-- +---------------------------------------| + +local PROFILER_FILENAME = "lua/telescope/profile/lua_profiler.lua" -- Location and name of profiler (to remove itself from reports); +-- e.g. if this is in a 'tool' folder, rename this as: "tool/profiler.lua" + +local EMPTY_TIME = "0.0000" -- Detect empty time, replace with tag below +local emptyToThis = "~" + +local fileWidth = 75 +local funcWidth = 22 +local lineWidth = 6 +local timeWidth = 7 +local relaWidth = 6 +local callWidth = 4 + +local reportSaved = " > Report saved to" +local formatOutputHeader = "| %-"..fileWidth.."s: %-"..funcWidth.."s: %-"..lineWidth.."s: %-"..timeWidth.."s: %-"..relaWidth.."s: %-"..callWidth.."s|\n" +local formatOutputTitle = "%-"..fileWidth.."."..fileWidth.."s: %-"..funcWidth.."."..funcWidth.."s: %-"..lineWidth.."s" -- File / Function / Line count +local formatOutput = "| %s: %-"..timeWidth.."s: %-"..relaWidth.."s: %-"..callWidth.."s|\n" -- Time / Relative / Called +local formatTotalTime = "TOTAL TIME = %f s\n" +local formatFunLine = "%"..(lineWidth - 2).."i" +local formatFunTime = "%04.4f" +local formatFunRelative = "%03.1f" +local formatFunCount = "%"..(callWidth - 1).."i" +local formatHeader = string.format(formatOutputHeader, "FILE", "FUNCTION", "LINE", "TIME", "%", "#") + + +---------------------------------------| +--- Locals +-- +---------------------------------------| + +local module = {} + +local getTime = os.clock +local string = string +local debug = debug +local table = table + +local TABL_REPORT_CACHE = {} +local TABL_REPORTS = {} +local reportCount = 0 +local startTime = 0 +local stopTime = 0 + +local printFun = nil +local verbosePrint = false + +local function functionReport(information) + local src = information.short_src + if src == nil then + src = "" + elseif string.sub(src, #src - 3, #src) == ".lua" then + src = string.sub(src, 1, #src - 4) + end + + local name = information.name + if name == nil then + name = "Anon" + elseif string.sub(name, #name - 1, #name) == "_l" then + name = string.sub(name, 1, #name - 2) + end + + local title = string.format(formatOutputTitle, + src, name, + string.format(formatFunLine, information.linedefined or 0)) + + local funcReport = TABL_REPORT_CACHE[title] + if not funcReport then + funcReport = { + title = string.format(formatOutputTitle, + src, name, + string.format(formatFunLine, information.linedefined or 0)), + count = 0, + timer = 0, + } + TABL_REPORT_CACHE[title] = funcReport + reportCount = reportCount + 1 + TABL_REPORTS[reportCount] = funcReport + end + + return funcReport +end + +local onDebugHook = function(hookType) + local information = debug.getinfo(2, "nS") + if hookType == "call" then + local funcReport = functionReport(information) + funcReport.callTime = getTime() + funcReport.count = funcReport.count + 1 + elseif hookType == "return" then + local funcReport = functionReport(information) + if funcReport.callTime and funcReport.count > 0 then + funcReport.timer = funcReport.timer + (getTime() - funcReport.callTime) + end + end +end + +local function charRepetition(n, character) + local s = "" + character = character or " " + for _ = 1, n do + s = s..character + end + return s +end + +local function singleSearchReturn(str, search) + for _ in string.gmatch(str, search) do + do return true end + end + return false +end + +local divider = charRepetition(#formatHeader - 1, "-").."\n" + + +---------------------------------------| +--- Functions +-- +---------------------------------------| + +--- Attach a print function to the profiler, to receive a single string parameter +-- +function module.attachPrintFunction(fn, verbose) + printFun = fn + if verbose ~= nil then + verbosePrint = verbose + end +end + +--- +-- +function module.start() + TABL_REPORT_CACHE = {} + TABL_REPORTS = {} + reportCount = 0 + startTime = getTime() + stopTime = nil + debug.sethook(onDebugHook, "cr", 0) +end + +--- +-- +function module.stop() + stopTime = getTime() + debug.sethook() +end + +--- Writes the profile report to file +-- +function module.report(filename) + if stopTime == nil then + module.stop() + end + + if reportCount > 0 then + filename = filename or "profiler.log" + table.sort(TABL_REPORTS, function(a, b) return a.timer > b.timer end) + local file = io.open(filename, "w+") + + if reportCount > 0 then + local divide = false + local totalTime = stopTime - startTime + local totalTimeOutput = " > "..string.format(formatTotalTime, totalTime) + + file:write(totalTimeOutput) + if printFun ~= nil then + printFun(totalTimeOutput) + end + + file:write("\n"..divider) + file:write(formatHeader) + file:write(divider) + + for i = 1, reportCount do + local funcReport = TABL_REPORTS[i] + + if funcReport.count > 0 and funcReport.timer <= totalTime then + local printThis = true + + if PROFILER_FILENAME ~= "" then + if singleSearchReturn(funcReport.title, PROFILER_FILENAME) then + printThis = false + end + end + + -- Remove line if not needed + if printThis == true then + if singleSearchReturn(funcReport.title, "[[C]]") then + printThis = false + end + end + + if printThis == true then + local count = string.format(formatFunCount, funcReport.count) + local timer = string.format(formatFunTime, funcReport.timer) + local relTime = string.format(formatFunRelative, (funcReport.timer / totalTime) * 100) + if divide == false and timer == EMPTY_TIME then + file:write(divider) + divide = true + end + + -- Replace + if timer == EMPTY_TIME then + timer = emptyToThis + relTime = emptyToThis + end + + -- Build final line + local outputLine = string.format(formatOutput, funcReport.title, timer, relTime, count) + file:write(outputLine) + + -- This is a verbose print to the printFun, however maybe make this smaller for on screen debug? + if printFun ~= nil and verbosePrint == true then + printFun(outputLine) + end + + end + end + end + + file:write(divider) + + end + + file:close() + + if printFun ~= nil then + printFun(reportSaved.."'"..filename.."'") + end + + end + +end + +--- End +-- +return module diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile/memory_profiler.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile/memory_profiler.lua new file mode 100644 index 00000000..64476eda --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile/memory_profiler.lua @@ -0,0 +1,1072 @@ +-- https://github.com/yaukeywang/LuaMemorySnapshotDump +-- Collect memory reference info. +-- +-- @filename MemoryReferenceInfo.lua +-- @author WangYaoqi +-- @date 2016-02-03 + +-- The global config of the mri. +local cConfig = +{ + m_bAllMemoryRefFileAddTime = true, + m_bSingleMemoryRefFileAddTime = true, + m_bComparedMemoryRefFileAddTime = true +} + +-- Get the format string of date time. +local function FormatDateTimeNow() + local cDateTime = os.date("*t") + local strDateTime = string.format("%04d%02d%02d-%02d%02d%02d", tostring(cDateTime.year), tostring(cDateTime.month), tostring(cDateTime.day), + tostring(cDateTime.hour), tostring(cDateTime.min), tostring(cDateTime.sec)) + return strDateTime +end + +-- Get the string result without overrided __tostring. +local function GetOriginalToStringResult(cObject) + if not cObject then + return "" + end + + local cMt = getmetatable(cObject) + if not cMt then + return tostring(cObject) + end + + -- Check tostring override. + local strName = "" + local cToString = rawget(cMt, "__tostring") + if cToString then + rawset(cMt, "__tostring", nil) + strName = tostring(cObject) + rawset(cMt, "__tostring", cToString) + else + strName = tostring(cObject) + end + + return strName +end + +-- Create a container to collect the mem ref info results. +local function CreateObjectReferenceInfoContainer() + -- Create new container. + local cContainer = {} + + -- Contain [table/function] - [reference count] info. + local cObjectReferenceCount = {} + setmetatable(cObjectReferenceCount, {__mode = "k"}) + + -- Contain [table/function] - [name] info. + local cObjectAddressToName = {} + setmetatable(cObjectAddressToName, {__mode = "k"}) + + -- Set members. + cContainer.m_cObjectReferenceCount = cObjectReferenceCount + cContainer.m_cObjectAddressToName = cObjectAddressToName + + -- For stack info. + cContainer.m_nStackLevel = -1 + cContainer.m_strShortSrc = "None" + cContainer.m_nCurrentLine = -1 + + return cContainer +end + +-- Create a container to collect the mem ref info results from a dumped file. +-- strFilePath - The file path. +local function CreateObjectReferenceInfoContainerFromFile(strFilePath) + -- Create a empty container. + local cContainer = CreateObjectReferenceInfoContainer() + cContainer.m_strShortSrc = strFilePath + + -- Cache ref info. + local cRefInfo = cContainer.m_cObjectReferenceCount + local cNameInfo = cContainer.m_cObjectAddressToName + + -- Read each line from file. + local cFile = assert(io.open(strFilePath, "rb")) + for strLine in cFile:lines() do + local strHeader = string.sub(strLine, 1, 2) + if "--" ~= strHeader then + local _, _, strAddr, strName, strRefCount= string.find(strLine, "(.+)\t(.*)\t(%d+)") + if strAddr then + cRefInfo[strAddr] = strRefCount + cNameInfo[strAddr] = strName + end + end + end + + -- Close and clear file handler. + io.close(cFile) + cFile = nil + + return cContainer +end + +-- Create a container to collect the mem ref info results from a dumped file. +-- strObjectName - The object name you need to collect info. +-- cObject - The object you need to collect info. +local function CreateSingleObjectReferenceInfoContainer(strObjectName, cObject) + -- Create new container. + local cContainer = {} + + -- Contain [address] - [true] info. + local cObjectExistTag = {} + setmetatable(cObjectExistTag, {__mode = "k"}) + + -- Contain [name] - [true] info. + local cObjectAliasName = {} + + -- Contain [access] - [true] info. + local cObjectAccessTag = {} + setmetatable(cObjectAccessTag, {__mode = "k"}) + + -- Set members. + cContainer.m_cObjectExistTag = cObjectExistTag + cContainer.m_cObjectAliasName = cObjectAliasName + cContainer.m_cObjectAccessTag = cObjectAccessTag + + -- For stack info. + cContainer.m_nStackLevel = -1 + cContainer.m_strShortSrc = "None" + cContainer.m_nCurrentLine = -1 + + -- Init with object values. + cContainer.m_strObjectName = strObjectName + cContainer.m_strAddressName = (("string" == type(cObject)) and ("\"" .. tostring(cObject) .. "\"")) or GetOriginalToStringResult(cObject) + cContainer.m_cObjectExistTag[cObject] = true + + return cContainer +end + +-- Collect memory reference info from a root table or function. +-- strName - The root object name that start to search, default is "_G" if leave this to nil. +-- cObject - The root object that start to search, default is _G if leave this to nil. +-- cDumpInfoContainer - The container of the dump result info. +local function CollectObjectReferenceInMemory(strName, cObject, cDumpInfoContainer) + if not cObject then + return + end + + if not strName then + strName = "" + end + + -- Check container. + if (not cDumpInfoContainer) then + cDumpInfoContainer = CreateObjectReferenceInfoContainer() + end + + -- Check stack. + if cDumpInfoContainer.m_nStackLevel > 0 then + local cStackInfo = debug.getinfo(cDumpInfoContainer.m_nStackLevel, "Sl") + if cStackInfo then + cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src + cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline + end + + cDumpInfoContainer.m_nStackLevel = -1 + end + + -- Get ref and name info. + local cRefInfoContainer = cDumpInfoContainer.m_cObjectReferenceCount + local cNameInfoContainer = cDumpInfoContainer.m_cObjectAddressToName + + local strType = type(cObject) + if "table" == strType then + -- Check table with class name. + if rawget(cObject, "__cname") then + if "string" == type(cObject.__cname) then + strName = strName .. "[class:" .. cObject.__cname .. "]" + end + elseif rawget(cObject, "class") then + if "string" == type(cObject.class) then + strName = strName .. "[class:" .. cObject.class .. "]" + end + elseif rawget(cObject, "_className") then + if "string" == type(cObject._className) then + strName = strName .. "[class:" .. cObject._className .. "]" + end + end + + -- Check if table is _G. + if cObject == _G then + strName = strName .. "[_G]" + end + + -- Get metatable. + local bWeakK = false + local bWeakV = false + local cMt = getmetatable(cObject) + if cMt then + -- Check mode. + local strMode = rawget(cMt, "__mode") + if strMode then + if "k" == strMode then + bWeakK = true + elseif "v" == strMode then + bWeakV = true + elseif "kv" == strMode then + bWeakK = true + bWeakV = true + end + end + end + + -- Add reference and name. + cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + if cNameInfoContainer[cObject] then + return + end + + -- Set name. + cNameInfoContainer[cObject] = strName + + -- Dump table key and value. + for k, v in pairs(cObject) do + -- Check key type. + local strKeyType = type(k) + if "table" == strKeyType then + if not bWeakK then + CollectObjectReferenceInMemory(strName .. ".[table:key.table]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "function" == strKeyType then + if not bWeakK then + CollectObjectReferenceInMemory(strName .. ".[table:key.function]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "thread" == strKeyType then + if not bWeakK then + CollectObjectReferenceInMemory(strName .. ".[table:key.thread]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "userdata" == strKeyType then + if not bWeakK then + CollectObjectReferenceInMemory(strName .. ".[table:key.userdata]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + else + CollectObjectReferenceInMemory(strName .. "." .. k, v, cDumpInfoContainer) + end + end + + -- Dump metatable. + if cMt then + CollectObjectReferenceInMemory(strName ..".[metatable]", cMt, cDumpInfoContainer) + end + elseif "function" == strType then + -- Get function info. + local cDInfo = debug.getinfo(cObject, "Su") + + -- Write this info. + cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + if cNameInfoContainer[cObject] then + return + end + + -- Set name. + cNameInfoContainer[cObject] = strName .. "[line:" .. tostring(cDInfo.linedefined) .. "@file:" .. cDInfo.short_src .. "]" + + -- Get upvalues. + local nUpsNum = cDInfo.nups + for i = 1, nUpsNum do + local strUpName, cUpValue = debug.getupvalue(cObject, i) + local strUpValueType = type(cUpValue) + --print(strUpName, cUpValue) + if "table" == strUpValueType then + CollectObjectReferenceInMemory(strName .. ".[ups:table:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "function" == strUpValueType then + CollectObjectReferenceInMemory(strName .. ".[ups:function:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "thread" == strUpValueType then + CollectObjectReferenceInMemory(strName .. ".[ups:thread:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "userdata" == strUpValueType then + CollectObjectReferenceInMemory(strName .. ".[ups:userdata:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + end + end + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectObjectReferenceInMemory(strName ..".[function:environment]", cEnv, cDumpInfoContainer) + end + end + elseif "thread" == strType then + -- Add reference and name. + cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + if cNameInfoContainer[cObject] then + return + end + + -- Set name. + cNameInfoContainer[cObject] = strName + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectObjectReferenceInMemory(strName ..".[thread:environment]", cEnv, cDumpInfoContainer) + end + end + + -- Dump metatable. + local cMt = getmetatable(cObject) + if cMt then + CollectObjectReferenceInMemory(strName ..".[thread:metatable]", cMt, cDumpInfoContainer) + end + elseif "userdata" == strType then + -- Add reference and name. + cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + if cNameInfoContainer[cObject] then + return + end + + -- Set name. + cNameInfoContainer[cObject] = strName + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectObjectReferenceInMemory(strName ..".[userdata:environment]", cEnv, cDumpInfoContainer) + end + end + + -- Dump metatable. + local cMt = getmetatable(cObject) + if cMt then + CollectObjectReferenceInMemory(strName ..".[userdata:metatable]", cMt, cDumpInfoContainer) + end + elseif "string" == strType then + -- Add reference and name. + cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + if cNameInfoContainer[cObject] then + return + end + + -- Set name. + cNameInfoContainer[cObject] = strName .. "[" .. strType .. "]" + else + -- For "number" and "boolean". (If you want to dump them, uncomment the followed lines.) + + -- -- Add reference and name. + -- cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + -- if cNameInfoContainer[cObject] then + -- return + -- end + + -- -- Set name. + -- cNameInfoContainer[cObject] = strName .. "[" .. strType .. ":" .. tostring(cObject) .. "]" + end +end + +-- Collect memory reference info of a single object from a root table or function. +-- strName - The root object name that start to search, can not be nil. +-- cObject - The root object that start to search, can not be nil. +-- cDumpInfoContainer - The container of the dump result info. +local function CollectSingleObjectReferenceInMemory(strName, cObject, cDumpInfoContainer) + if not cObject then + return + end + + if not strName then + strName = "" + end + + -- Check container. + if (not cDumpInfoContainer) then + cDumpInfoContainer = CreateObjectReferenceInfoContainer() + end + + -- Check stack. + if cDumpInfoContainer.m_nStackLevel > 0 then + local cStackInfo = debug.getinfo(cDumpInfoContainer.m_nStackLevel, "Sl") + if cStackInfo then + cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src + cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline + end + + cDumpInfoContainer.m_nStackLevel = -1 + end + + local cExistTag = cDumpInfoContainer.m_cObjectExistTag + local cNameAllAlias = cDumpInfoContainer.m_cObjectAliasName + local cAccessTag = cDumpInfoContainer.m_cObjectAccessTag + + local strType = type(cObject) + if "table" == strType then + -- Check table with class name. + if rawget(cObject, "__cname") then + if "string" == type(cObject.__cname) then + strName = strName .. "[class:" .. cObject.__cname .. "]" + end + elseif rawget(cObject, "class") then + if "string" == type(cObject.class) then + strName = strName .. "[class:" .. cObject.class .. "]" + end + elseif rawget(cObject, "_className") then + if "string" == type(cObject._className) then + strName = strName .. "[class:" .. cObject._className .. "]" + end + end + + -- Check if table is _G. + if cObject == _G then + strName = strName .. "[_G]" + end + + -- Get metatable. + local bWeakK = false + local bWeakV = false + local cMt = getmetatable(cObject) + if cMt then + -- Check mode. + local strMode = rawget(cMt, "__mode") + if strMode then + if "k" == strMode then + bWeakK = true + elseif "v" == strMode then + bWeakV = true + elseif "kv" == strMode then + bWeakK = true + bWeakV = true + end + end + end + + -- Check if the specified object. + if cExistTag[cObject] and (not cNameAllAlias[strName]) then + cNameAllAlias[strName] = true + end + + -- Add reference and name. + if cAccessTag[cObject] then + return + end + + -- Get this name. + cAccessTag[cObject] = true + + -- Dump table key and value. + for k, v in pairs(cObject) do + -- Check key type. + local strKeyType = type(k) + if "table" == strKeyType then + if not bWeakK then + CollectSingleObjectReferenceInMemory(strName .. ".[table:key.table]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "function" == strKeyType then + if not bWeakK then + CollectSingleObjectReferenceInMemory(strName .. ".[table:key.function]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "thread" == strKeyType then + if not bWeakK then + CollectSingleObjectReferenceInMemory(strName .. ".[table:key.thread]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "userdata" == strKeyType then + if not bWeakK then + CollectSingleObjectReferenceInMemory(strName .. ".[table:key.userdata]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + else + CollectSingleObjectReferenceInMemory(strName .. "." .. k, v, cDumpInfoContainer) + end + end + + -- Dump metatable. + if cMt then + CollectSingleObjectReferenceInMemory(strName ..".[metatable]", cMt, cDumpInfoContainer) + end + elseif "function" == strType then + -- Get function info. + local cDInfo = debug.getinfo(cObject, "Su") + local cCombinedName = strName .. "[line:" .. tostring(cDInfo.linedefined) .. "@file:" .. cDInfo.short_src .. "]" + + -- Check if the specified object. + if cExistTag[cObject] and (not cNameAllAlias[cCombinedName]) then + cNameAllAlias[cCombinedName] = true + end + + -- Write this info. + if cAccessTag[cObject] then + return + end + + -- Set name. + cAccessTag[cObject] = true + + -- Get upvalues. + local nUpsNum = cDInfo.nups + for i = 1, nUpsNum do + local strUpName, cUpValue = debug.getupvalue(cObject, i) + local strUpValueType = type(cUpValue) + --print(strUpName, cUpValue) + if "table" == strUpValueType then + CollectSingleObjectReferenceInMemory(strName .. ".[ups:table:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "function" == strUpValueType then + CollectSingleObjectReferenceInMemory(strName .. ".[ups:function:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "thread" == strUpValueType then + CollectSingleObjectReferenceInMemory(strName .. ".[ups:thread:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "userdata" == strUpValueType then + CollectSingleObjectReferenceInMemory(strName .. ".[ups:userdata:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + end + end + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectSingleObjectReferenceInMemory(strName ..".[function:environment]", cEnv, cDumpInfoContainer) + end + end + elseif "thread" == strType then + -- Check if the specified object. + if cExistTag[cObject] and (not cNameAllAlias[strName]) then + cNameAllAlias[strName] = true + end + + -- Add reference and name. + if cAccessTag[cObject] then + return + end + + -- Get this name. + cAccessTag[cObject] = true + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectSingleObjectReferenceInMemory(strName ..".[thread:environment]", cEnv, cDumpInfoContainer) + end + end + + -- Dump metatable. + local cMt = getmetatable(cObject) + if cMt then + CollectSingleObjectReferenceInMemory(strName ..".[thread:metatable]", cMt, cDumpInfoContainer) + end + elseif "userdata" == strType then + -- Check if the specified object. + if cExistTag[cObject] and (not cNameAllAlias[strName]) then + cNameAllAlias[strName] = true + end + + -- Add reference and name. + if cAccessTag[cObject] then + return + end + + -- Get this name. + cAccessTag[cObject] = true + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectSingleObjectReferenceInMemory(strName ..".[userdata:environment]", cEnv, cDumpInfoContainer) + end + end + + -- Dump metatable. + local cMt = getmetatable(cObject) + if cMt then + CollectSingleObjectReferenceInMemory(strName ..".[userdata:metatable]", cMt, cDumpInfoContainer) + end + elseif "string" == strType then + -- Check if the specified object. + if cExistTag[cObject] and (not cNameAllAlias[strName]) then + cNameAllAlias[strName] = true + end + + -- Add reference and name. + if cAccessTag[cObject] then + return + end + + -- Get this name. + cAccessTag[cObject] = true + else + -- For "number" and "boolean" type, they are not object type, skip. + end +end + +-- The base method to dump a mem ref info result into a file. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- strRootObjectName - The header info to show the root object name, can be nil. +-- cRootObject - The header info to show the root object address, can be nil. +-- cDumpInfoResultsBase - The base dumped mem info result, nil means no compare and only output cDumpInfoResults, otherwise to compare with cDumpInfoResults. +-- cDumpInfoResults - The compared dumped mem info result, dump itself only if cDumpInfoResultsBase is nil, otherwise dump compared results with cDumpInfoResultsBase. +local function OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, strRootObjectName, cRootObject, cDumpInfoResultsBase, cDumpInfoResults) + -- Check results. + if not cDumpInfoResults then + return + end + + -- Get time format string. + local strDateTime = FormatDateTimeNow() + + -- Collect memory info. + local cRefInfoBase = (cDumpInfoResultsBase and cDumpInfoResultsBase.m_cObjectReferenceCount) or nil + local cNameInfoBase = (cDumpInfoResultsBase and cDumpInfoResultsBase.m_cObjectAddressToName) or nil + local cRefInfo = cDumpInfoResults.m_cObjectReferenceCount + local cNameInfo = cDumpInfoResults.m_cObjectAddressToName + + -- Create a cache result to sort by ref count. + local cRes = {} + local nIdx = 0 + for k in pairs(cRefInfo) do + nIdx = nIdx + 1 + cRes[nIdx] = k + end + + -- Sort result. + table.sort(cRes, function (l, r) + return cRefInfo[l] > cRefInfo[r] + end) + + -- Save result to file. + local bOutputFile = strSavePath and (string.len(strSavePath) > 0) + local cOutputHandle = nil + local cOutputEntry = print + + if bOutputFile then + -- Check save path affix. + local strAffix = string.sub(strSavePath, -1) + if ("/" ~= strAffix) and ("\\" ~= strAffix) then + strSavePath = strSavePath .. "/" + end + + -- Combine file name. + local strFileName = strSavePath .. "LuaMemRefInfo-All" + if (not strExtraFileName) or (0 == string.len(strExtraFileName)) then + if cDumpInfoResultsBase then + if cConfig.m_bComparedMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "].txt" + else + strFileName = strFileName .. ".txt" + end + else + if cConfig.m_bAllMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "].txt" + else + strFileName = strFileName .. ".txt" + end + end + else + if cDumpInfoResultsBase then + if cConfig.m_bComparedMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "]-[" .. strExtraFileName .. "].txt" + else + strFileName = strFileName .. "-[" .. strExtraFileName .. "].txt" + end + else + if cConfig.m_bAllMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "]-[" .. strExtraFileName .. "].txt" + else + strFileName = strFileName .. "-[" .. strExtraFileName .. "].txt" + end + end + end + + local cFile = assert(io.open(strFileName, "w")) + cOutputHandle = cFile + cOutputEntry = cFile.write + end + + local cOutputer = function (strContent) + if cOutputHandle then + cOutputEntry(cOutputHandle, strContent) + else + cOutputEntry(strContent) + end + end + + -- Write table header. + if cDumpInfoResultsBase then + cOutputer("--------------------------------------------------------\n") + cOutputer("-- This is compared memory information.\n") + + cOutputer("--------------------------------------------------------\n") + cOutputer("-- Collect base memory reference at line:" .. tostring(cDumpInfoResultsBase.m_nCurrentLine) .. "@file:" .. cDumpInfoResultsBase.m_strShortSrc .. "\n") + cOutputer("-- Collect compared memory reference at line:" .. tostring(cDumpInfoResults.m_nCurrentLine) .. "@file:" .. cDumpInfoResults.m_strShortSrc .. "\n") + else + cOutputer("--------------------------------------------------------\n") + cOutputer("-- Collect memory reference at line:" .. tostring(cDumpInfoResults.m_nCurrentLine) .. "@file:" .. cDumpInfoResults.m_strShortSrc .. "\n") + end + + cOutputer("--------------------------------------------------------\n") + cOutputer("-- [Table/Function/String Address/Name]\t[Reference Path]\t[Reference Count]\n") + cOutputer("--------------------------------------------------------\n") + + if strRootObjectName and cRootObject then + if "string" == type(cRootObject) then + cOutputer("-- From Root Object: \"" .. tostring(cRootObject) .. "\" (" .. strRootObjectName .. ")\n") + else + cOutputer("-- From Root Object: " .. GetOriginalToStringResult(cRootObject) .. " (" .. strRootObjectName .. ")\n") + end + end + + -- Save each info. + for i, v in ipairs(cRes) do + if (not cDumpInfoResultsBase) or (not cRefInfoBase[v]) then + if (nMaxRescords > 0) then + if (i <= nMaxRescords) then + if "string" == type(v) then + local strOrgString = tostring(v) + local nPattenBegin, nPattenEnd = string.find(strOrgString, "string: \".*\"") + if ((not cDumpInfoResultsBase) and ((nil == nPattenBegin) or (nil == nPattenEnd))) then + local strRepString = string.gsub(strOrgString, "([\n\r])", "\\n") + cOutputer("string: \"" .. strRepString .. "\"\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + else + cOutputer(tostring(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + end + else + cOutputer(GetOriginalToStringResult(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + end + end + else + if "string" == type(v) then + local strOrgString = tostring(v) + local nPattenBegin, nPattenEnd = string.find(strOrgString, "string: \".*\"") + if ((not cDumpInfoResultsBase) and ((nil == nPattenBegin) or (nil == nPattenEnd))) then + local strRepString = string.gsub(strOrgString, "([\n\r])", "\\n") + cOutputer("string: \"" .. strRepString .. "\"\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + else + cOutputer(tostring(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + end + else + cOutputer(GetOriginalToStringResult(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + end + end + end + end + + if bOutputFile then + io.close(cOutputHandle) + cOutputHandle = nil + end +end + +-- The base method to dump a mem ref info result of a single object into a file. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- cDumpInfoResults - The dumped results. +local function OutputMemorySnapshotSingleObject(strSavePath, strExtraFileName, nMaxRescords, cDumpInfoResults) + -- Check results. + if not cDumpInfoResults then + return + end + + -- Get time format string. + local strDateTime = FormatDateTimeNow() + + -- Collect memory info. + local cObjectAliasName = cDumpInfoResults.m_cObjectAliasName + + -- Save result to file. + local bOutputFile = strSavePath and (string.len(strSavePath) > 0) + local cOutputHandle = nil + local cOutputEntry = print + + if bOutputFile then + -- Check save path affix. + local strAffix = string.sub(strSavePath, -1) + if ("/" ~= strAffix) and ("\\" ~= strAffix) then + strSavePath = strSavePath .. "/" + end + + -- Combine file name. + local strFileName = strSavePath .. "LuaMemRefInfo-Single" + if (not strExtraFileName) or (0 == string.len(strExtraFileName)) then + if cConfig.m_bSingleMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "].txt" + else + strFileName = strFileName .. ".txt" + end + else + if cConfig.m_bSingleMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "]-[" .. strExtraFileName .. "].txt" + else + strFileName = strFileName .. "-[" .. strExtraFileName .. "].txt" + end + end + + local cFile = assert(io.open(strFileName, "w")) + cOutputHandle = cFile + cOutputEntry = cFile.write + end + + local cOutputer = function (strContent) + if cOutputHandle then + cOutputEntry(cOutputHandle, strContent) + else + cOutputEntry(strContent) + end + end + + -- Write table header. + cOutputer("--------------------------------------------------------\n") + cOutputer("-- Collect single object memory reference at line:" .. tostring(cDumpInfoResults.m_nCurrentLine) .. "@file:" .. cDumpInfoResults.m_strShortSrc .. "\n") + cOutputer("--------------------------------------------------------\n") + + -- Calculate reference count. + local nCount = 0 + for k in pairs(cObjectAliasName) do + nCount = nCount + 1 + end + + -- Output reference count. + cOutputer("-- For Object: " .. cDumpInfoResults.m_strAddressName .. " (" .. cDumpInfoResults.m_strObjectName .. "), have " .. tostring(nCount) .. " reference in total.\n") + cOutputer("--------------------------------------------------------\n") + + -- Save each info. + for k in pairs(cObjectAliasName) do + if (nMaxRescords > 0) then + if (i <= nMaxRescords) then + cOutputer(k .. "\n") + end + else + cOutputer(k .. "\n") + end + end + + if bOutputFile then + io.close(cOutputHandle) + cOutputHandle = nil + end +end + +-- Fileter an existing result file and output it. +-- strFilePath - The existing result file. +-- strFilter - The filter string. +-- bIncludeFilter - Include(true) or exclude(false) the filter. +-- bOutputFile - Output to file(true) or console(false). +local function OutputFilteredResult(strFilePath, strFilter, bIncludeFilter, bOutputFile) + if (not strFilePath) or (0 == string.len(strFilePath)) then + print("You need to specify a file path.") + return + end + + if (not strFilter) or (0 == string.len(strFilter)) then + print("You need to specify a filter string.") + return + end + + -- Read file. + local cFilteredResult = {} + local cReadFile = assert(io.open(strFilePath, "rb")) + for strLine in cReadFile:lines() do + local nBegin, nEnd = string.find(strLine, strFilter) + if nBegin and nEnd then + if bIncludeFilter then + nBegin, nEnd = string.find(strLine, "[\r\n]") + if nBegin and nEnd and (string.len(strLine) == nEnd) then + table.insert(cFilteredResult, string.sub(strLine, 1, nBegin - 1)) + else + table.insert(cFilteredResult, strLine) + end + end + else + if not bIncludeFilter then + nBegin, nEnd = string.find(strLine, "[\r\n]") + if nBegin and nEnd and (string.len(strLine) == nEnd) then + table.insert(cFilteredResult, string.sub(strLine, 1, nBegin - 1)) + else + table.insert(cFilteredResult, strLine) + end + end + end + end + + -- Close and clear read file handle. + io.close(cReadFile) + cReadFile = nil + + -- Write filtered result. + local cOutputHandle = nil + local cOutputEntry = print + + if bOutputFile then + -- Combine file name. + local _, _, strResFileName = string.find(strFilePath, "(.*)%.txt") + strResFileName = strResFileName .. "-Filter-" .. ((bIncludeFilter and "I") or "E") .. "-[" .. strFilter .. "].txt" + + local cFile = assert(io.open(strResFileName, "w")) + cOutputHandle = cFile + cOutputEntry = cFile.write + end + + local cOutputer = function (strContent) + if cOutputHandle then + cOutputEntry(cOutputHandle, strContent) + else + cOutputEntry(strContent) + end + end + + -- Output result. + for i, v in ipairs(cFilteredResult) do + cOutputer(v .. "\n") + end + + if bOutputFile then + io.close(cOutputHandle) + cOutputHandle = nil + end +end + +-- Dump memory reference at current time. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- strRootObjectName - The root object name that start to search, default is "_G" if leave this to nil. +-- cRootObject - The root object that start to search, default is _G if leave this to nil. +local function DumpMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, strRootObjectName, cRootObject) + -- Get time format string. + local strDateTime = FormatDateTimeNow() + + -- Check root object. + if cRootObject then + if (not strRootObjectName) or (0 == string.len(strRootObjectName)) then + strRootObjectName = tostring(cRootObject) + end + else + cRootObject = debug.getregistry() + strRootObjectName = "registry" + end + + -- Create container. + local cDumpInfoContainer = CreateObjectReferenceInfoContainer() + local cStackInfo = debug.getinfo(2, "Sl") + if cStackInfo then + cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src + cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline + end + + -- Collect memory info. + CollectObjectReferenceInMemory(strRootObjectName, cRootObject, cDumpInfoContainer) + + -- Dump the result. + OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, strRootObjectName, cRootObject, nil, cDumpInfoContainer) +end + +-- Dump compared memory reference results generated by DumpMemorySnapshot. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- cResultBefore - The base dumped results. +-- cResultAfter - The compared dumped results. +local function DumpMemorySnapshotCompared(strSavePath, strExtraFileName, nMaxRescords, cResultBefore, cResultAfter) + -- Dump the result. + OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, nil, nil, cResultBefore, cResultAfter) +end + +-- Dump compared memory reference file results generated by DumpMemorySnapshot. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- strResultFilePathBefore - The base dumped results file. +-- strResultFilePathAfter - The compared dumped results file. +local function DumpMemorySnapshotComparedFile(strSavePath, strExtraFileName, nMaxRescords, strResultFilePathBefore, strResultFilePathAfter) + -- Read results from file. + local cResultBefore = CreateObjectReferenceInfoContainerFromFile(strResultFilePathBefore) + local cResultAfter = CreateObjectReferenceInfoContainerFromFile(strResultFilePathAfter) + + -- Dump the result. + OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, nil, nil, cResultBefore, cResultAfter) +end + +-- Dump memory reference of a single object at current time. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- strObjectName - The object name reference you want to dump. +-- cObject - The object reference you want to dump. +local function DumpMemorySnapshotSingleObject(strSavePath, strExtraFileName, nMaxRescords, strObjectName, cObject) + -- Check object. + if not cObject then + return + end + + if (not strObjectName) or (0 == string.len(strObjectName)) then + strObjectName = GetOriginalToStringResult(cObject) + end + + -- Get time format string. + local strDateTime = FormatDateTimeNow() + + -- Create container. + local cDumpInfoContainer = CreateSingleObjectReferenceInfoContainer(strObjectName, cObject) + local cStackInfo = debug.getinfo(2, "Sl") + if cStackInfo then + cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src + cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline + end + + -- Collect memory info. + CollectSingleObjectReferenceInMemory("registry", debug.getregistry(), cDumpInfoContainer) + + -- Dump the result. + OutputMemorySnapshotSingleObject(strSavePath, strExtraFileName, nMaxRescords, cDumpInfoContainer) +end + +-- Return methods. +local cPublications = {m_cConfig = nil, m_cMethods = {}, m_cHelpers = {}, m_cBases = {}} + +cPublications.m_cConfig = cConfig + +cPublications.m_cMethods.DumpMemorySnapshot = DumpMemorySnapshot +cPublications.m_cMethods.DumpMemorySnapshotCompared = DumpMemorySnapshotCompared +cPublications.m_cMethods.DumpMemorySnapshotComparedFile = DumpMemorySnapshotComparedFile +cPublications.m_cMethods.DumpMemorySnapshotSingleObject = DumpMemorySnapshotSingleObject + +cPublications.m_cHelpers.FormatDateTimeNow = FormatDateTimeNow +cPublications.m_cHelpers.GetOriginalToStringResult = GetOriginalToStringResult + +cPublications.m_cBases.CreateObjectReferenceInfoContainer = CreateObjectReferenceInfoContainer +cPublications.m_cBases.CreateObjectReferenceInfoContainerFromFile = CreateObjectReferenceInfoContainerFromFile +cPublications.m_cBases.CreateSingleObjectReferenceInfoContainer = CreateSingleObjectReferenceInfoContainer +cPublications.m_cBases.CollectObjectReferenceInMemory = CollectObjectReferenceInMemory +cPublications.m_cBases.CollectSingleObjectReferenceInMemory = CollectSingleObjectReferenceInMemory +cPublications.m_cBases.OutputMemorySnapshot = OutputMemorySnapshot +cPublications.m_cBases.OutputMemorySnapshotSingleObject = OutputMemorySnapshotSingleObject +cPublications.m_cBases.OutputFilteredResult = OutputFilteredResult + +return cPublications diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile/p.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile/p.lua new file mode 100644 index 00000000..9eb6f162 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/profile/p.lua @@ -0,0 +1,312 @@ +---------------------------------------------------------------------------- +-- LuaJIT profiler. +-- +-- Copyright (C) 2005-2021 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- +-- This module is a simple command line interface to the built-in +-- low-overhead profiler of LuaJIT. +-- +-- The lower-level API of the profiler is accessible via the "jit.profile" +-- module or the luaJIT_profile_* C API. +-- +-- Example usage: +-- +-- luajit -jp myapp.lua +-- luajit -jp=s myapp.lua +-- luajit -jp=-s myapp.lua +-- luajit -jp=vl myapp.lua +-- luajit -jp=G,profile.txt myapp.lua +-- +-- The following dump features are available: +-- +-- f Stack dump: function name, otherwise module:line. Default mode. +-- F Stack dump: ditto, but always prepend module. +-- l Stack dump: module:line. +-- stack dump depth (callee < caller). Default: 1. +-- - Inverse stack dump depth (caller > callee). +-- s Split stack dump after first stack level. Implies abs(depth) >= 2. +-- p Show full path for module names. +-- v Show VM states. Can be combined with stack dumps, e.g. vf or fv. +-- z Show zones. Can be combined with stack dumps, e.g. zf or fz. +-- r Show raw sample counts. Default: show percentages. +-- a Annotate excerpts from source code files. +-- A Annotate complete source code files. +-- G Produce raw output suitable for graphical tools (e.g. flame graphs). +-- m Minimum sample percentage to be shown. Default: 3. +-- i Sampling interval in milliseconds. Default: 10. +-- +---------------------------------------------------------------------------- + +-- Cache some library functions and objects. +local jit = require("jit") +assert(20100 <= jit.version_num and jit.version_num <= 20199, "LuaJIT core/library version mismatch: " .. jit.version) +local profile = require("jit.profile") +local vmdef = require("jit.vmdef") +local math = math +local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor +local sort, format = table.sort, string.format +local stdout = io.stdout +local zone -- Load jit.zone module on demand. + +-- Output file handle. +local out + +------------------------------------------------------------------------------ + +local prof_ud +local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth +local prof_ann, prof_count1, prof_count2, prof_samples + +local map_vmmode = { + N = "Compiled", + I = "Interpreted", + C = "C code", + G = "Garbage Collector", + J = "JIT Compiler", +} + +-- Profiler callback. +local function prof_cb(th, samples, vmmode) + prof_samples = prof_samples + samples + local key_stack, key_stack2, key_state + -- Collect keys for sample. + if prof_states then + if prof_states == "v" then + key_state = map_vmmode[vmmode] or vmmode + else + key_state = zone:get() or "(none)" + end + end + if prof_fmt then + key_stack = profile.dumpstack(th, prof_fmt, prof_depth) + key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x) + return vmdef.ffnames[tonumber(x)] + end) + if prof_split == 2 then + local k1, k2 = key_stack:match("(.-) [<>] (.*)") + if k2 then key_stack, key_stack2 = k1, k2 end + elseif prof_split == 3 then + key_stack2 = profile.dumpstack(th, "l", 1) + end + end + -- Order keys. + local k1, k2 + if prof_split == 1 then + if key_state then + k1 = key_state + if key_stack then k2 = key_stack end + end + elseif key_stack then + k1 = key_stack + if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end + end + -- Coalesce samples in one or two levels. + if k1 then + local t1 = prof_count1 + t1[k1] = (t1[k1] or 0) + samples + if k2 then + local t2 = prof_count2 + local t3 = t2[k1] + if not t3 then t3 = {}; t2[k1] = t3 end + t3[k2] = (t3[k2] or 0) + samples + end + end +end + +------------------------------------------------------------------------------ + +-- Show top N list. +local function prof_top(count1, count2, samples, indent) + local t, n = {}, 0 + for k in pairs(count1) do + n = n + 1 + t[n] = k + end + sort(t, function(a, b) return count1[a] > count1[b] end) + for i=1,n do + local k = t[i] + local v = count1[k] + local pct = floor(v*100/samples + 0.5) + if pct < prof_min then break end + if not prof_raw then + out:write(format("%s%2d%% %s\n", indent, pct, k)) + elseif prof_raw == "r" then + out:write(format("%s%5d %s\n", indent, v, k)) + else + out:write(format("%s %d\n", k, v)) + end + if count2 then + local r = count2[k] + if r then + prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or + (prof_depth < 0 and " -> " or " <- ")) + end + end + end +end + +-- Annotate source code +local function prof_annotate(count1, samples) + local files = {} + local ms = 0 + for k, v in pairs(count1) do + local pct = floor(v*100/samples + 0.5) + ms = math.max(ms, v) + if pct >= prof_min then + local file, line = k:match("^(.*):(%d+)$") + if not file then file = k; line = 0 end + local fl = files[file] + if not fl then fl = {}; files[file] = fl; files[#files+1] = file end + line = tonumber(line) + fl[line] = prof_raw and v or pct + end + end + sort(files) + local fmtv, fmtn = " %3d%% | %s\n", " | %s\n" + if prof_raw then + local n = math.max(5, math.ceil(math.log10(ms))) + fmtv = "%"..n.."d | %s\n" + fmtn = (" "):rep(n).." | %s\n" + end + local ann = prof_ann + for _, file in ipairs(files) do + local f0 = file:byte() + if f0 == 40 or f0 == 91 then + out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file)) + break + end + local fp, err = io.open(file) + if not fp then + out:write(format("====== ERROR: %s: %s\n", file, err)) + break + end + out:write(format("\n====== %s ======\n", file)) + local fl = files[file] + local n, show = 1, false + if ann ~= 0 then + for i=1,ann do + if fl[i] then show = true; out:write("@@ 1 @@\n"); break end + end + end + for line in fp:lines() do + if line:byte() == 27 then + out:write("[Cannot annotate bytecode file]\n") + break + end + local v = fl[n] + if ann ~= 0 then + local v2 = fl[n+ann] + if show then + if v2 then show = n+ann elseif v then show = n + elseif show+ann < n then show = false end + elseif v2 then + show = n+ann + out:write(format("@@ %d @@\n", n)) + end + if not show then goto next end + end + if v then + out:write(format(fmtv, v, line)) + else + out:write(format(fmtn, line)) + end + ::next:: + n = n + 1 + end + fp:close() + end +end + +------------------------------------------------------------------------------ + +-- Finish profiling and dump result. +local function prof_finish() + if prof_ud then + profile.stop() + local samples = prof_samples + if samples == 0 then + if prof_raw ~= true then out:write("[No samples collected]\n") end + return + end + if prof_ann then + prof_annotate(prof_count1, samples) + else + prof_top(prof_count1, prof_count2, samples, "") + end + prof_count1 = nil + prof_count2 = nil + prof_ud = nil + if out ~= stdout then out:close() end + end +end + +-- Start profiling. +local function prof_start(mode) + local interval = "" + mode = mode:gsub("i%d*", function(s) interval = s; return "" end) + prof_min = 3 + mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end) + prof_depth = 1 + mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end) + local m = {} + for c in mode:gmatch(".") do m[c] = c end + prof_states = m.z or m.v + if prof_states == "z" then zone = require("jit.zone") end + local scope = m.l or m.f or m.F or (prof_states and "" or "f") + local flags = (m.p or "") + prof_raw = m.r + if m.s then + prof_split = 2 + if prof_depth == -1 or m["-"] then prof_depth = -2 + elseif prof_depth == 1 then prof_depth = 2 end + elseif mode:find("[fF].*l") then + scope = "l" + prof_split = 3 + else + prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0 + end + prof_ann = m.A and 0 or (m.a and 3) + if prof_ann then + scope = "l" + prof_fmt = "pl" + prof_split = 0 + prof_depth = 1 + elseif m.G and scope ~= "" then + prof_fmt = flags..scope.."Z;" + prof_depth = -100 + prof_raw = true + prof_min = 0 + elseif scope == "" then + prof_fmt = false + else + local sc = prof_split == 3 and m.f or m.F or scope + prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ") + end + prof_count1 = {} + prof_count2 = {} + prof_samples = 0 + profile.start(scope:lower()..interval, prof_cb) + prof_ud = newproxy(true) + getmetatable(prof_ud).__gc = prof_finish +end + +------------------------------------------------------------------------------ + +local function start(mode, outfile) + if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end + if outfile then + out = outfile == "-" and stdout or assert(io.open(outfile, "w")) + else + out = stdout + end + prof_start(mode or "f") +end + +-- Public module functions. +return { + start = start, -- For -j command line option. + stop = prof_finish +} + diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/reload.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/reload.lua new file mode 100644 index 00000000..61895220 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/reload.lua @@ -0,0 +1,36 @@ +local reload = {} + +reload.reload_module = function(module_name, starts_with_only) + -- Default to starts with only + if starts_with_only == nil then + starts_with_only = true + end + + -- TODO: Might need to handle cpath / compiled lua packages? Not sure. + local matcher + if not starts_with_only then + matcher = function(pack) + return string.find(pack, module_name, 1, true) + end + else + local module_name_pattern = vim.pesc(module_name) + matcher = function(pack) + return string.find(pack, "^" .. module_name_pattern) + end + end + + -- Handle impatient.nvim automatically. + local luacache = (_G.__luacache or {}).cache + + for pack, _ in pairs(package.loaded) do + if matcher(pack) then + package.loaded[pack] = nil + + if luacache then + luacache[pack] = nil + end + end + end +end + +return reload diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/run.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/run.lua new file mode 100644 index 00000000..0661fc62 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/run.lua @@ -0,0 +1,28 @@ +local floatwin = require "plenary.window.float" + +local run = {} + +run.with_displayed_output = function(title_text, cmd, opts) + local views = floatwin.centered_with_top_win(title_text) + + local job_id = vim.fn.termopen(cmd) + + local count = 0 + while not vim.wait(1000, function() + return vim.fn.jobwait({ job_id }, 0)[1] == -1 + end) do + vim.cmd [[normal! G]] + count = count + 1 + + if count == 10 then + break + end + end + + vim.fn.win_gotoid(views.win_id) + vim.cmd [[startinsert]] + + return views.bufnr, views.win_id +end + +return run diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/scandir.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/scandir.lua new file mode 100644 index 00000000..d5b60ee4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/scandir.lua @@ -0,0 +1,620 @@ +local Path = require "plenary.path" +local os_sep = Path.path.sep +local F = require "plenary.functional" +local compat = require "plenary.compat" + +local uv = vim.loop + +local m = {} + +local make_gitignore = function(basepath) + local patterns = {} + local valid = false + for _, v in ipairs(basepath) do + local p = Path:new(v .. os_sep .. ".gitignore") + if p:exists() then + valid = true + patterns[v] = { ignored = {}, negated = {} } + for l in p:iter() do + local prefix = l:sub(1, 1) + local negated = prefix == "!" + if negated then + l = l:sub(2) + prefix = l:sub(1, 1) + end + if prefix == "/" then + l = v .. l + end + if not (prefix == "" or prefix == "#") then + local el = vim.trim(l) + el = el:gsub("%-", "%%-") + el = el:gsub("%.", "%%.") + el = el:gsub("/%*%*/", "/%%w+/") + el = el:gsub("%*%*", "") + el = el:gsub("%*", "%%w+") + el = el:gsub("%?", "%%w") + if el ~= "" then + table.insert(negated and patterns[v].negated or patterns[v].ignored, el) + end + end + end + end + end + if not valid then + return nil + end + return function(bp, entry) + for _, v in ipairs(bp) do + if entry:find(v, 1, true) then + local negated = false + for _, w in ipairs(patterns[v].ignored) do + if not negated and entry:match(w) then + for _, inverse in ipairs(patterns[v].negated) do + if not negated and entry:match(inverse) then + negated = true + end + end + if not negated then + return false + end + end + end + end + end + return true + end +end +-- exposed for testing +m.__make_gitignore = make_gitignore + +local handle_depth = function(base_paths, entry, depth) + for _, v in ipairs(base_paths) do + if entry:find(v, 1, true) then + local cut = entry:sub(#v + 1, -1) + cut = cut:sub(1, 1) == os_sep and cut:sub(2, -1) or cut + local _, count = cut:gsub(os_sep, "") + if depth <= (count + 1) then + return nil + end + end + end + return entry +end + +local gen_search_pat = function(pattern) + if type(pattern) == "string" then + return function(entry) + return entry:match(pattern) + end + elseif type(pattern) == "table" then + return function(entry) + for _, v in ipairs(pattern) do + if entry:match(v) then + return true + end + end + return false + end + elseif type(pattern) == "function" then + return pattern + end +end + +local process_item = function(opts, name, typ, current_dir, next_dir, bp, data, giti, msp) + if opts.hidden or name:sub(1, 1) ~= "." then + if typ == "directory" then + local entry = current_dir .. os_sep .. name + if opts.depth then + table.insert(next_dir, handle_depth(bp, entry, opts.depth)) + else + table.insert(next_dir, entry) + end + if opts.add_dirs or opts.only_dirs then + if not giti or giti(bp, entry .. "/") then + if not msp or msp(entry) then + table.insert(data, entry) + if opts.on_insert then + opts.on_insert(entry, typ) + end + end + end + end + elseif not opts.only_dirs then + local entry = current_dir .. os_sep .. name + if not giti or giti(bp, entry) then + if not msp or msp(entry) then + table.insert(data, entry) + if opts.on_insert then + opts.on_insert(entry, typ) + end + end + end + end + end +end + +--- m.scan_dir +-- Search directory recursive and syncronous +-- @param path: string or table +-- string has to be a valid path +-- table has to be a array of valid paths +-- @param opts: table to change behavior +-- opts.hidden (bool): if true hidden files will be added +-- opts.add_dirs (bool): if true dirs will also be added to the results +-- opts.only_dirs (bool): if true only dirs will be added to the results +-- opts.respect_gitignore (bool): if true will only add files that are not ignored by the git +-- opts.depth (int): depth on how deep the search should go +-- opts.search_pattern (regex): regex for which files will be added, string, table of strings, or fn(e) -> bool +-- opts.on_insert(entry): Will be called for each element +-- opts.silent (bool): if true will not echo messages that are not accessible +-- @return array with files +m.scan_dir = function(path, opts) + opts = opts or {} + + local data = {} + local base_paths = compat.flatten { path } + local next_dir = compat.flatten { path } + + local gitignore = opts.respect_gitignore and make_gitignore(base_paths) or nil + local match_search_pat = opts.search_pattern and gen_search_pat(opts.search_pattern) or nil + + for i = #base_paths, 1, -1 do + if uv.fs_access(base_paths[i], "X") == false then + if not F.if_nil(opts.silent, false, opts.silent) then + print(string.format("%s is not accessible by the current user!", base_paths[i])) + end + table.remove(base_paths, i) + end + end + if #base_paths == 0 then + return {} + end + + repeat + local current_dir = table.remove(next_dir, 1) + local fd = uv.fs_scandir(current_dir) + if fd then + while true do + local name, typ = uv.fs_scandir_next(fd) + if name == nil then + break + end + process_item(opts, name, typ, current_dir, next_dir, base_paths, data, gitignore, match_search_pat) + end + end + until #next_dir == 0 + return data +end + +--- m.scan_dir_async +-- Search directory recursive and asyncronous +-- @param path: string or table +-- string has to be a valid path +-- table has to be a array of valid paths +-- @param opts: table to change behavior +-- opts.hidden (bool): if true hidden files will be added +-- opts.add_dirs (bool): if true dirs will also be added to the results +-- opts.only_dirs (bool): if true only dirs will be added to the results +-- opts.respect_gitignore (bool): if true will only add files that are not ignored by git +-- opts.depth (int): depth on how deep the search should go +-- opts.search_pattern (regex): regex for which files will be added, string, table of strings, or fn(e) -> bool +-- opts.on_insert function(entry): will be called for each element +-- opts.on_exit function(results): will be called at the end +-- opts.silent (bool): if true will not echo messages that are not accessible +m.scan_dir_async = function(path, opts) + opts = opts or {} + + local data = {} + local base_paths = compat.flatten { path } + local next_dir = compat.flatten { path } + local current_dir = table.remove(next_dir, 1) + + -- TODO(conni2461): get gitignore is not async + local gitignore = opts.respect_gitignore and make_gitignore(base_paths) or nil + local match_search_pat = opts.search_pattern and gen_search_pat(opts.search_pattern) or nil + + -- TODO(conni2461): is not async. Shouldn't be that big of a problem but still + -- Maybe obers async pr can take me out of callback hell + for i = #base_paths, 1, -1 do + if uv.fs_access(base_paths[i], "X") == false then + if not F.if_nil(opts.silent, false, opts.silent) then + print(string.format("%s is not accessible by the current user!", base_paths[i])) + end + table.remove(base_paths, i) + end + end + if #base_paths == 0 then + return {} + end + + local read_dir + read_dir = function(err, fd) + if not err then + while true do + local name, typ = uv.fs_scandir_next(fd) + if name == nil then + break + end + process_item(opts, name, typ, current_dir, next_dir, base_paths, data, gitignore, match_search_pat) + end + if #next_dir == 0 then + if opts.on_exit then + opts.on_exit(data) + end + else + current_dir = table.remove(next_dir, 1) + uv.fs_scandir(current_dir, read_dir) + end + end + end + uv.fs_scandir(current_dir, read_dir) +end + +local gen_permissions = (function() + local conv_to_octal = function(nr) + local octal, i = 0, 1 + + while nr ~= 0 do + octal = octal + (nr % 8) * i + nr = math.floor(nr / 8) + i = i * 10 + end + + return octal + end + + local type_tbl = { [1] = "p", [2] = "c", [4] = "d", [6] = "b", [10] = ".", [12] = "l", [14] = "s" } + local permissions_tbl = { [0] = "---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx" } + local bit_tbl = { 4, 2, 1 } + + return function(cache, mode) + if cache[mode] then + return cache[mode] + end + + local octal = string.format("%6d", conv_to_octal(mode)) + local l4 = octal:sub(#octal - 3, -1) + local bit = tonumber(l4:sub(1, 1)) + + local result = type_tbl[tonumber(octal:sub(1, 2))] or "-" + for i = 2, #l4 do + result = result .. permissions_tbl[tonumber(l4:sub(i, i))] + if bit - bit_tbl[i - 1] >= 0 then + result = result:sub(1, -2) .. (bit_tbl[i - 1] == 1 and "T" or "S") + bit = bit - bit_tbl[i - 1] + end + end + + cache[mode] = result + return result + end +end)() + +local gen_size = (function() + local size_types = { "", "K", "M", "G", "T", "P", "E", "Z" } + + return function(size) + -- TODO(conni2461): If type directory we could just return 4.0K + for _, v in ipairs(size_types) do + if math.abs(size) < 1024.0 then + if math.abs(size) > 9 then + return string.format("%3d%s", size, v) + else + return string.format("%3.1f%s", size, v) + end + end + size = size / 1024.0 + end + return string.format("%.1f%s", size, "Y") + end +end)() + +local gen_date = (function() + local current_year = os.date "%Y" + return function(mtime) + if current_year ~= os.date("%Y", mtime) then + return os.date("%b %d %Y", mtime) + end + return os.date("%b %d %H:%M", mtime) + end +end)() + +local get_username = (function() + local fallback = function(tbl, id) + if not tbl then + return id + end + if tbl[id] then + return tbl[id] + end + tbl[id] = tostring(id) + return id + end + + if jit and os_sep ~= "\\" then + local ffi = require "ffi" + ffi.cdef [[ + typedef unsigned int __uid_t; + typedef __uid_t uid_t; + typedef unsigned int __gid_t; + typedef __gid_t gid_t; + + typedef struct { + char *pw_name; + char *pw_passwd; + __uid_t pw_uid; + __gid_t pw_gid; + char *pw_gecos; + char *pw_dir; + char *pw_shell; + } passwd; + + passwd *getpwuid(uid_t uid); + ]] + + local ffi_func = function(tbl, id) + if tbl[id] then + return tbl[id] + end + local struct = ffi.C.getpwuid(id) + local name + if struct == nil then + name = tostring(id) + else + name = ffi.string(struct.pw_name) + end + tbl[id] = name + return name + end + + local ok = pcall(ffi_func, {}, 1000) + if ok then + return ffi_func + else + return fallback + end + else + return fallback + end +end)() + +local get_groupname = (function() + local fallback = function(tbl, id) + if not tbl then + return id + end + if tbl[id] then + return tbl[id] + end + tbl[id] = tostring(id) + return id + end + + if jit and os_sep ~= "\\" then + local ffi = require "ffi" + ffi.cdef [[ + typedef unsigned int __gid_t; + typedef __gid_t gid_t; + + typedef struct { + char *gr_name; + char *gr_passwd; + __gid_t gr_gid; + char **gr_mem; + } group; + group *getgrgid(gid_t gid); + ]] + + local ffi_func = function(tbl, id) + if tbl[id] then + return tbl[id] + end + local struct = ffi.C.getgrgid(id) + local name + if struct == nil then + name = tostring(id) + else + name = ffi.string(struct.gr_name) + end + tbl[id] = name + return name + end + local ok = pcall(ffi_func, {}, 1000) + if ok then + return ffi_func + else + return fallback + end + else + return fallback + end +end)() + +local get_max_len = function(tbl) + if not tbl then + return 0 + end + local max_len = 0 + for _, v in pairs(tbl) do + if #v > max_len then + max_len = #v + end + end + return max_len +end + +local gen_ls = function(data, path, opts) + if not data or #data == 0 then + return {}, {} + end + + local check_link = function(per, file) + if per:sub(1, 1) == "l" then + local resolved = uv.fs_realpath(path .. os_sep .. file) + if not resolved then + return file + end + if resolved:sub(1, #path) == path then + resolved = resolved:sub(#path + 2, -1) + end + return string.format("%s -> %s", file, resolved) + end + return file + end + + local results, sections = {}, {} + + local users_tbl = os_sep ~= "\\" and {} or nil + local groups_tbl = os_sep ~= "\\" and {} or nil + + local stats, permissions_cache = {}, {} + for _, v in ipairs(data) do + local stat = uv.fs_lstat(v) + if stat then + stats[v] = stat + get_username(users_tbl, stat.uid) + get_groupname(groups_tbl, stat.gid) + end + end + + local insert_in_results = (function() + if not users_tbl and not groups_tbl then + local section_spacing_tbl = { [5] = 2, [6] = 0 } + + return function(...) + local args = { ... } + local section = { + { start_index = 01, end_index = 11 }, -- permissions, hardcoded indexes + { start_index = 12, end_index = 17 }, -- size, hardcoded indexes + } + local cur_index = 19 + for k = 5, 6 do + local v = section_spacing_tbl[k] + local end_index = cur_index + #args[k] + table.insert(section, { start_index = cur_index, end_index = end_index }) + cur_index = end_index + v + end + table.insert(sections, section) + table.insert( + results, + string.format("%10s %5s %s %s", args[1], args[2], args[5], check_link(args[1], args[6])) + ) + end + else + local max_user_len = get_max_len(users_tbl) + local max_group_len = get_max_len(groups_tbl) + + local section_spacing_tbl = { + [3] = { max = max_user_len, add = 1 }, + [4] = { max = max_group_len, add = 2 }, + [5] = { add = 2 }, + [6] = { add = 0 }, + } + local fmt_str = "%10s %5s %-" .. max_user_len .. "s %-" .. max_group_len .. "s %s %s" + + return function(...) + local args = { ... } + local section = { + { start_index = 01, end_index = 11 }, -- permissions, hardcoded indexes + { start_index = 12, end_index = 17 }, -- size, hardcoded indexes + } + local cur_index = 18 + for k = 3, 6 do + local v = section_spacing_tbl[k] + local end_index = cur_index + #args[k] + table.insert(section, { start_index = cur_index, end_index = end_index }) + if v.max then + cur_index = cur_index + v.max + v.add + else + cur_index = end_index + v.add + end + end + table.insert(sections, section) + table.insert( + results, + string.format(fmt_str, args[1], args[2], args[3], args[4], args[5], check_link(args[1], args[6])) + ) + end + end + end)() + + for name, stat in pairs(stats) do + insert_in_results( + gen_permissions(permissions_cache, stat.mode), + gen_size(stat.size), + get_username(users_tbl, stat.uid), + get_groupname(groups_tbl, stat.gid), + gen_date(stat.mtime.sec), + name:sub(#path + 2, -1) + ) + end + + if opts and opts.group_directories_first then + local sorted_results = {} + local sorted_sections = {} + for k, v in ipairs(results) do + if v:sub(1, 1) == "d" then + table.insert(sorted_results, v) + table.insert(sorted_sections, sections[k]) + end + end + for k, v in ipairs(results) do + if v:sub(1, 1) ~= "d" then + table.insert(sorted_results, v) + table.insert(sorted_sections, sections[k]) + end + end + return sorted_results, sorted_sections + else + return results, sections + end +end + +--- m.ls +-- List directory contents. Will always apply --long option. Use scan_dir for without --long +-- @param path: string +-- string has to be a valid path +-- @param opts: table to change behavior +-- opts.hidden (bool): if true hidden files will be added +-- opts.add_dirs (bool): if true dirs will also be added to the results, default: true +-- opts.respect_gitignore (bool): if true will only add files that are not ignored by git +-- opts.depth (int): depth on how deep the search should go, default: 1 +-- opts.group_directories_first (bool): same as real ls +-- @return array with formatted output +m.ls = function(path, opts) + opts = opts or {} + opts.depth = opts.depth or 1 + opts.add_dirs = opts.add_dirs or true + local data = m.scan_dir(path, opts) + + return gen_ls(data, path, opts) +end + +--- m.ls_async +-- List directory contents. Will always apply --long option. Use scan_dir for without --long +-- @param path: string +-- string has to be a valid path +-- @param opts: table to change behavior +-- opts.hidden (bool): if true hidden files will be added +-- opts.add_dirs (bool): if true dirs will also be added to the results, default: true +-- opts.respect_gitignore (bool): if true will only add files that are not ignored by git +-- opts.depth (int): depth on how deep the search should go, default: 1 +-- opts.group_directories_first (bool): same as real ls +-- opts.on_exit function(results): will be called at the end (required) +m.ls_async = function(path, opts) + opts = opts or {} + opts.depth = opts.depth or 1 + opts.add_dirs = opts.add_dirs or true + + local opts_copy = vim.deepcopy(opts) + + opts_copy.on_exit = function(data) + if opts.on_exit then + opts.on_exit(gen_ls(data, path, opts_copy)) + end + end + + m.scan_dir_async(path, opts_copy) +end + +return m diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/strings.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/strings.lua new file mode 100644 index 00000000..3e8e5bc1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/strings.lua @@ -0,0 +1,204 @@ +local path = require("plenary.path").path + +local M = {} + +M.strdisplaywidth = (function() + local fallback = function(str, col) + str = tostring(str) + if vim.in_fast_event() then + return #str - (col or 0) + end + return vim.fn.strdisplaywidth(str, col) + end + + if jit and path.sep ~= [[\]] then + local ffi = require "ffi" + ffi.cdef [[ + typedef unsigned char char_u; + int linetabsize_col(int startcol, char_u *s); + ]] + + local ffi_func = function(str, col) + str = tostring(str) + local startcol = col or 0 + local s = ffi.new("char[?]", #str + 1) + ffi.copy(s, str) + return ffi.C.linetabsize_col(startcol, s) - startcol + end + + local ok = pcall(ffi_func, "hello") + if ok then + return ffi_func + else + return fallback + end + else + return fallback + end +end)() + +M.strcharpart = (function() + local fallback = function(str, nchar, charlen) + if vim.in_fast_event() then + return str:sub(nchar + 1, charlen) + end + return vim.fn.strcharpart(str, nchar, charlen) + end + + if jit and path.sep ~= [[\]] then + local ffi = require "ffi" + ffi.cdef [[ + typedef unsigned char char_u; + int utf_ptr2len(const char_u *const p); + ]] + + local function utf_ptr2len(str) + local c_str = ffi.new("char[?]", #str + 1) + ffi.copy(c_str, str) + return ffi.C.utf_ptr2len(c_str) + end + + local ok = pcall(utf_ptr2len, "🔭") + if not ok then + return fallback + end + + return function(str, nchar, charlen) + local nbyte = 0 + if nchar > 0 then + while nchar > 0 and nbyte < #str do + nbyte = nbyte + utf_ptr2len(str:sub(nbyte + 1)) + nchar = nchar - 1 + end + else + nbyte = nchar + end + + local len = 0 + if charlen then + while charlen > 0 and nbyte + len < #str do + local off = nbyte + len + if off < 0 then + len = len + 1 + else + len = len + utf_ptr2len(str:sub(off + 1)) + end + charlen = charlen - 1 + end + else + len = #str - nbyte + end + + if nbyte < 0 then + len = len + nbyte + nbyte = 0 + elseif nbyte > #str then + nbyte = #str + end + if len < 0 then + len = 0 + elseif nbyte + len > #str then + len = #str - nbyte + end + + return str:sub(nbyte + 1, nbyte + len) + end + else + return fallback + end +end)() + +local truncate = function(str, len, dots, direction) + if M.strdisplaywidth(str) <= len then + return str + end + local start = direction > 0 and 0 or str:len() + local current = 0 + local result = "" + local len_of_dots = M.strdisplaywidth(dots) + local concat = function(a, b, dir) + if dir > 0 then + return a .. b + else + return b .. a + end + end + while true do + local part = M.strcharpart(str, start, 1) + current = current + M.strdisplaywidth(part) + if (current + len_of_dots) > len then + result = concat(result, dots, direction) + break + end + result = concat(result, part, direction) + start = start + direction + end + return result +end + +M.truncate = function(str, len, dots, direction) + str = tostring(str) -- We need to make sure its an actually a string and not a number + dots = dots or "…" + direction = direction or 1 + if direction ~= 0 then + return truncate(str, len, dots, direction) + else + if M.strdisplaywidth(str) <= len then + return str + end + local len1 = math.floor((len + M.strdisplaywidth(dots)) / 2) + local s1 = truncate(str, len1, dots, 1) + local len2 = len - M.strdisplaywidth(s1) + M.strdisplaywidth(dots) + local s2 = truncate(str, len2, dots, -1) + return s1 .. s2:sub(dots:len() + 1) + end +end + +M.align_str = function(string, width, right_justify) + local str_len = M.strdisplaywidth(string) + return right_justify and string.rep(" ", width - str_len) .. string or string .. string.rep(" ", width - str_len) +end + +M.dedent = function(str, leave_indent) + -- Check each line and detect the minimum indent. + local indent + local info = {} + for line in str:gmatch "[^\n]*\n?" do + -- It matches '' for the last line. + if line ~= "" then + local chars, width + local line_indent = line:match "^[ \t]+" + if line_indent then + chars = #line_indent + width = M.strdisplaywidth(line_indent) + if not indent or width < indent then + indent = width + end + -- Ignore empty lines + elseif line ~= "\n" then + indent = 0 + end + table.insert(info, { line = line, chars = chars, width = width }) + end + end + + -- Build up the result + leave_indent = leave_indent or 0 + local result = {} + for _, i in ipairs(info) do + local line + if i.chars then + local content = i.line:sub(i.chars + 1) + local indent_width = i.width - indent + leave_indent + line = (" "):rep(indent_width) .. content + elseif i.line == "\n" then + line = "\n" + else + line = (" "):rep(leave_indent) .. i.line + end + table.insert(result, line) + end + return table.concat(result) +end + +return M diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/tbl.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/tbl.lua new file mode 100644 index 00000000..276e9abe --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/tbl.lua @@ -0,0 +1,40 @@ +local tbl = {} + +function tbl.apply_defaults(original, defaults) + if original == nil then + original = {} + end + + original = vim.deepcopy(original) + + for k, v in pairs(defaults) do + if original[k] == nil then + original[k] = v + end + end + + return original +end + +function tbl.pack(...) + return { n = select("#", ...), ... } +end + +function tbl.unpack(t, i, j) + return unpack(t, i or 1, j or t.n or #t) +end + +---Freeze a table. A frozen table is not able to be modified. +---http://lua-users.org/wiki/ReadOnlyTables +---@param t table +---@return table +function tbl.freeze(t) + return setmetatable({}, { + __index = t, + __newindex = function() + error "Attempt to modify frozen table" + end, + }) +end + +return tbl diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/test_harness.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/test_harness.lua new file mode 100644 index 00000000..a548db71 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/test_harness.lua @@ -0,0 +1,246 @@ +local Path = require "plenary.path" +local Job = require "plenary.job" + +local f = require "plenary.functional" +local log = require "plenary.log" +local win_float = require "plenary.window.float" + +local headless = require("plenary.nvim_meta").is_headless + +local plenary_dir = vim.fn.fnamemodify(debug.getinfo(1).source:match "@?(.*[/\\])", ":p:h:h:h") + +local harness = {} + +local print_output = vim.schedule_wrap(function(_, ...) + for _, v in ipairs { ... } do + io.stdout:write(tostring(v)) + io.stdout:write "\n" + end + + vim.cmd [[mode]] +end) + +local get_nvim_output = function(job_id) + return vim.schedule_wrap(function(bufnr, ...) + if not vim.api.nvim_buf_is_valid(bufnr) then + return + end + for _, v in ipairs { ... } do + vim.api.nvim_chan_send(job_id, v .. "\r\n") + end + end) +end + +function harness.test_directory_command(command) + local split_string = vim.split(command, " ") + local directory = vim.fn.expand(table.remove(split_string, 1)) + + local opts = assert(loadstring("return " .. table.concat(split_string, " ")))() + + return harness.test_directory(directory, opts) +end + +local function test_paths(paths, opts) + local minimal = not opts or not opts.init or opts.minimal or opts.minimal_init + + opts = vim.tbl_deep_extend("force", { + nvim_cmd = vim.v.progpath, + winopts = { winblend = 3 }, + sequential = false, + keep_going = true, + timeout = 50000, + }, opts or {}) + + vim.env.PLENARY_TEST_TIMEOUT = opts.timeout + + local res = {} + if not headless then + res = win_float.percentage_range_window(0.95, 0.70, opts.winopts) + + res.job_id = vim.api.nvim_open_term(res.bufnr, {}) + vim.api.nvim_buf_set_keymap(res.bufnr, "n", "q", ":q", {}) + + vim.api.nvim_win_set_option(res.win_id, "winhl", "Normal:Normal") + vim.api.nvim_win_set_option(res.win_id, "conceallevel", 3) + vim.api.nvim_win_set_option(res.win_id, "concealcursor", "n") + + if res.border_win_id then + vim.api.nvim_win_set_option(res.border_win_id, "winhl", "Normal:Normal") + end + + if res.bufnr then + vim.api.nvim_buf_set_option(res.bufnr, "filetype", "PlenaryTestPopup") + end + vim.cmd "mode" + end + + local outputter = headless and print_output or get_nvim_output(res.job_id) + + local path_len = #paths + local failure = false + + local jobs = vim.tbl_map(function(p) + local args = { + "--headless", + "-c", + "set rtp+=.," .. vim.fn.escape(plenary_dir, " ") .. " | runtime plugin/plenary.vim", + } + + if minimal then + table.insert(args, "--noplugin") + if opts.minimal_init then + table.insert(args, "-u") + table.insert(args, opts.minimal_init) + end + elseif opts.init ~= nil then + table.insert(args, "-u") + table.insert(args, opts.init) + end + + table.insert(args, "-c") + table.insert(args, string.format('lua require("plenary.busted").run("%s")', p:absolute():gsub("\\", "\\\\"))) + + local job = Job:new { + command = opts.nvim_cmd, + args = args, + + -- Can be turned on to debug + on_stdout = function(_, data) + if path_len == 1 then + outputter(res.bufnr, data) + end + end, + + on_stderr = function(_, data) + if path_len == 1 then + outputter(res.bufnr, data) + end + end, + + on_exit = vim.schedule_wrap(function(j_self, _, _) + if path_len ~= 1 then + outputter(res.bufnr, unpack(j_self:stderr_result())) + outputter(res.bufnr, unpack(j_self:result())) + end + + vim.cmd "mode" + end), + } + job.nvim_busted_path = p.filename + return job + end, paths) + + log.debug "Running..." + for i, j in ipairs(jobs) do + outputter(res.bufnr, "Scheduling: " .. j.nvim_busted_path) + j:start() + if opts.sequential then + log.debug("... Sequential wait for job number", i) + if not Job.join(j, opts.timeout) then + log.debug("... Timed out job number", i) + failure = true + pcall(function() + j.handle:kill(15) -- SIGTERM + end) + else + log.debug("... Completed job number", i, j.code, j.signal) + failure = failure or j.code ~= 0 or j.signal ~= 0 + end + if failure and not opts.keep_going then + break + end + end + end + + -- TODO: Probably want to let people know when we've completed everything. + if not headless then + return + end + + if not opts.sequential then + table.insert(jobs, opts.timeout) + log.debug "... Parallel wait" + Job.join(unpack(jobs)) + log.debug "... Completed jobs" + table.remove(jobs, table.getn(jobs)) + failure = f.any(function(_, v) + return v.code ~= 0 + end, jobs) + end + vim.wait(100) + + if headless then + if failure then + return vim.cmd "1cq" + end + + return vim.cmd "0cq" + end +end + +function harness.test_directory(directory, opts) + print "Starting..." + directory = directory:gsub("\\", "/") + local paths = harness._find_files_to_run(directory) + + -- Paths work strangely on Windows, so lets have abs paths + if vim.fn.has "win32" == 1 or vim.fn.has "win64" == 1 then + paths = vim.tbl_map(function(p) + return Path:new(directory, p.filename) + end, paths) + end + + test_paths(paths, opts) +end + +function harness.test_file(filepath) + test_paths { Path:new(filepath) } +end + +function harness._find_files_to_run(directory) + local finder + if vim.fn.has "win32" == 1 or vim.fn.has "win64" == 1 then + -- On windows use powershell Get-ChildItem instead + local cmd = vim.fn.executable "pwsh.exe" == 1 and "pwsh" or "powershell" + finder = Job:new { + command = cmd, + args = { "-NoProfile", "-Command", [[Get-ChildItem -Recurse -n -Filter "*_spec.lua"]] }, + cwd = directory, + } + else + -- everywhere else use find + finder = Job:new { + command = "find", + args = { directory, "-type", "f", "-name", "*_spec.lua" }, + } + end + + return vim.tbl_map(Path.new, finder:sync(vim.env.PLENARY_TEST_TIMEOUT)) +end + +function harness._run_path(test_type, directory) + local paths = harness._find_files_to_run(directory) + + local bufnr = 0 + local win_id = 0 + + for _, p in pairs(paths) do + print " " + print("Loading Tests For: ", p:absolute(), "\n") + + local ok, _ = pcall(function() + dofile(p:absolute()) + end) + + if not ok then + print "Failed to load file" + end + end + + harness:run(test_type, bufnr, win_id) + vim.cmd "qa!" + + return paths +end + +return harness diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/vararg/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/vararg/init.lua new file mode 100644 index 00000000..1398d60b --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/vararg/init.lua @@ -0,0 +1,3 @@ +return { + rotate = require "plenary.vararg.rotate", +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/vararg/rotate.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/vararg/rotate.lua new file mode 100644 index 00000000..6b71c632 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/vararg/rotate.lua @@ -0,0 +1,83 @@ +---@brief [[ +---Do not edit this file, it was generated! +---Provides a function to rotate a lua vararg +---@brief ]] +local tbl = require "plenary.tbl" + +local rotate_lookup = {} + +rotate_lookup[1] = function(A0) + return A0 +end + +rotate_lookup[2] = function(A0, A1) + return A1, A0 +end + +rotate_lookup[3] = function(A0, A1, A2) + return A1, A2, A0 +end + +rotate_lookup[4] = function(A0, A1, A2, A3) + return A1, A2, A3, A0 +end + +rotate_lookup[5] = function(A0, A1, A2, A3, A4) + return A1, A2, A3, A4, A0 +end + +rotate_lookup[6] = function(A0, A1, A2, A3, A4, A5) + return A1, A2, A3, A4, A5, A0 +end + +rotate_lookup[7] = function(A0, A1, A2, A3, A4, A5, A6) + return A1, A2, A3, A4, A5, A6, A0 +end + +rotate_lookup[8] = function(A0, A1, A2, A3, A4, A5, A6, A7) + return A1, A2, A3, A4, A5, A6, A7, A0 +end + +rotate_lookup[9] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8) + return A1, A2, A3, A4, A5, A6, A7, A8, A0 +end + +rotate_lookup[10] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A0 +end + +rotate_lookup[11] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A0 +end + +rotate_lookup[12] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A0 +end + +rotate_lookup[13] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A0 +end + +rotate_lookup[14] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A0 +end + +rotate_lookup[15] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A0 +end + +local function rotate_n(first, ...) + local n = select("#", ...) + 1 + local args = tbl.pack(...) + args[n] = first + return tbl.unpack(args, 1, n) +end + +local function rotate(nargs, ...) + if nargs == nil or nargs < 1 then + return + end + return (rotate_lookup[nargs] or rotate_n)(...) +end + +return rotate diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/window/border.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/window/border.lua new file mode 100644 index 00000000..a454b7f9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/window/border.lua @@ -0,0 +1,295 @@ +local strings = require "plenary.strings" + +local Border = {} + +Border.__index = Border + +Border._default_thickness = { + top = 1, + right = 1, + bot = 1, + left = 1, +} + +local calc_left_start = function(title_pos, title_len, total_width) + if string.find(title_pos, "W") then + return 0 + elseif string.find(title_pos, "E") then + return total_width - title_len + else + return math.floor((total_width - title_len) / 2) + end +end + +local create_horizontal_line = function(title, pos, width, left_char, mid_char, right_char) + local title_len + if title == "" then + title_len = 0 + else + local len = strings.strdisplaywidth(title) + local max_title_width = width - 2 + if len > max_title_width then + title = strings.truncate(title, max_title_width) + len = strings.strdisplaywidth(title) + end + title = string.format(" %s ", title) + title_len = len + 2 + end + + local left_start = calc_left_start(pos, title_len, width) + + local horizontal_line = string.format( + "%s%s%s%s%s", + left_char, + string.rep(mid_char, left_start), + title, + string.rep(mid_char, width - title_len - left_start), + right_char + ) + local ranges = {} + if title_len ~= 0 then + -- Need to calculate again due to multi-byte characters + local r_start = string.len(left_char) + math.max(left_start, 0) * string.len(mid_char) + ranges = { { r_start, r_start + string.len(title) } } + end + return horizontal_line, ranges +end + +function Border._create_lines(content_win_id, content_win_options, border_win_options) + local content_pos = vim.api.nvim_win_get_position(content_win_id) + local content_height = vim.api.nvim_win_get_height(content_win_id) + local content_width = vim.api.nvim_win_get_width(content_win_id) + + -- TODO: Handle border width, which I haven't right here. + local thickness = border_win_options.border_thickness + + local top_enabled = thickness.top == 1 + local right_enabled = thickness.right == 1 and content_pos[2] + content_width < vim.o.columns + local bot_enabled = thickness.bot == 1 + local left_enabled = thickness.left == 1 and content_pos[2] > 0 + + border_win_options.border_thickness.left = left_enabled and 1 or 0 + border_win_options.border_thickness.right = right_enabled and 1 or 0 + + local border_lines = {} + local ranges = {} + + -- border_win_options.title should have be a list with entries of the + -- form: { pos = foo, text = bar }. + -- pos can take values in { "NW", "N", "NE", "SW", "S", "SE" } + local titles = type(border_win_options.title) == "string" and { { pos = "N", text = border_win_options.title } } + or border_win_options.title + or {} + + local topline = nil + local topleft = (left_enabled and border_win_options.topleft) or "" + local topright = (right_enabled and border_win_options.topright) or "" + -- Only calculate the topline if there is space above the first content row (relative to the editor) + if content_pos[1] > 0 then + for _, title in ipairs(titles) do + if string.find(title.pos, "N") then + local top_ranges + topline, top_ranges = create_horizontal_line( + title.text, + title.pos, + content_win_options.width, + topleft, + border_win_options.top or "", + topright + ) + for _, r in pairs(top_ranges) do + table.insert(ranges, { 0, r[1], r[2] }) + end + break + end + end + if topline == nil then + if top_enabled then + topline = topleft .. string.rep(border_win_options.top, content_win_options.width) .. topright + end + end + else + border_win_options.border_thickness.top = 0 + end + + if topline then + table.insert(border_lines, topline) + end + + local middle_line = string.format( + "%s%s%s", + (left_enabled and border_win_options.left) or "", + string.rep(" ", content_win_options.width), + (right_enabled and border_win_options.right) or "" + ) + + for _ = 1, content_win_options.height do + table.insert(border_lines, middle_line) + end + + local botline = nil + local botleft = (left_enabled and border_win_options.botleft) or "" + local botright = (right_enabled and border_win_options.botright) or "" + if content_pos[1] + content_height < vim.o.lines then + for _, title in ipairs(titles) do + if string.find(title.pos, "S") then + local bot_ranges + botline, bot_ranges = create_horizontal_line( + title.text, + title.pos, + content_win_options.width, + botleft, + border_win_options.bot or "", + botright + ) + for _, r in pairs(bot_ranges) do + table.insert(ranges, { content_win_options.height + thickness.top, r[1], r[2] }) + end + break + end + end + if botline == nil then + if bot_enabled then + botline = botleft .. string.rep(border_win_options.bot, content_win_options.width) .. botright + end + end + else + border_win_options.border_thickness.bot = 0 + end + + if botline then + table.insert(border_lines, botline) + end + + return border_lines, ranges +end + +local set_title_highlights = function(bufnr, ranges, hl) + -- Check if both `hl` and `ranges` are provided, and `ranges` is not the empty table. + if hl and ranges and next(ranges) then + for _, r in pairs(ranges) do + vim.api.nvim_buf_add_highlight(bufnr, -1, hl, r[1], r[2], r[3]) + end + end +end + +function Border:change_title(new_title, pos) + if self._border_win_options.title == new_title then + return + end + + pos = pos + or (self._border_win_options.title and self._border_win_options.title[1] and self._border_win_options.title[1].pos) + if pos == nil then + self._border_win_options.title = new_title + else + self._border_win_options.title = { { text = new_title, pos = pos } } + end + + self.contents, self.title_ranges = + Border._create_lines(self.content_win_id, self.content_win_options, self._border_win_options) + vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, self.contents) + + set_title_highlights(self.bufnr, self.title_ranges, self._border_win_options.titlehighlight) +end + +-- Updates characters for border lines, and returns nvim_win_config +-- (generally used in conjunction with `move` or `new`) +function Border:__align_calc_config(content_win_options, border_win_options) + border_win_options = vim.tbl_deep_extend("keep", border_win_options, { + border_thickness = Border._default_thickness, + + -- Border options, could be passed as a list? + topleft = "╔", + topright = "╗", + top = "═", + left = "║", + right = "║", + botleft = "╚", + botright = "╝", + bot = "═", + }) + + -- Ensure the relevant contents and border win_options are set + self._border_win_options = border_win_options + self.content_win_options = content_win_options + -- Update border characters and title_ranges + self.contents, self.title_ranges = Border._create_lines(self.content_win_id, content_win_options, border_win_options) + + vim.api.nvim_buf_set_option(self.bufnr, "modifiable", true) + vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, self.contents) + + local thickness = border_win_options.border_thickness + local nvim_win_config = { + anchor = content_win_options.anchor, + relative = content_win_options.relative, + style = "minimal", + row = content_win_options.row - thickness.top, + col = content_win_options.col - thickness.left, + width = content_win_options.width + thickness.left + thickness.right, + height = content_win_options.height + thickness.top + thickness.bot, + zindex = content_win_options.zindex or 50, + noautocmd = content_win_options.noautocmd, + focusable = vim.F.if_nil(border_win_options.focusable, false), + } + + return nvim_win_config +end + +-- Sets the size and position of the given Border. +-- Can be used to create a new window (with `create_window = true`) +-- or change an existing one +function Border:move(content_win_options, border_win_options) + -- Update lines in border buffer, and get config for border window + local nvim_win_config = self:__align_calc_config(content_win_options, border_win_options) + + -- Set config for border window + vim.api.nvim_win_set_config(self.win_id, nvim_win_config) + + set_title_highlights(self.bufnr, self.title_ranges, self._border_win_options.titlehighlight) +end + +function Border:new(content_bufnr, content_win_id, content_win_options, border_win_options) + assert(type(content_win_id) == "number", "Must supply a valid win_id. It's possible you forgot to call with ':'") + + local obj = {} + + obj.content_win_id = content_win_id + + obj.bufnr = vim.api.nvim_create_buf(false, true) + assert(obj.bufnr, "Failed to create border buffer") + vim.api.nvim_buf_set_option(obj.bufnr, "bufhidden", "wipe") + + -- Create a border window and buffer, with border characters around the edge + local nvim_win_config = Border.__align_calc_config(obj, content_win_options, border_win_options) + obj.win_id = vim.api.nvim_open_win(obj.bufnr, false, nvim_win_config) + + if border_win_options.highlight then + vim.api.nvim_win_set_option(obj.win_id, "winhl", border_win_options.highlight) + end + + set_title_highlights(obj.bufnr, obj.title_ranges, obj._border_win_options.titlehighlight) + + vim.cmd( + string.format( + "autocmd BufDelete ++nested ++once :lua require('plenary.window').close_related_win(%s, %s)", + content_bufnr, + content_win_id, + obj.win_id + ) + ) + + vim.cmd( + string.format( + "autocmd WinClosed ++nested ++once :lua require('plenary.window').try_close(%s, true)", + content_bufnr, + obj.win_id + ) + ) + + setmetatable(obj, Border) + + return obj +end + +return Border diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/window/float.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/window/float.lua new file mode 100644 index 00000000..86e5c046 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/window/float.lua @@ -0,0 +1,212 @@ +local Border = require "plenary.window.border" +local tbl = require "plenary.tbl" + +_AssociatedBufs = {} + +local clear_buf_on_leave = function(bufnr) + vim.cmd( + string.format( + "autocmd WinLeave,BufLeave,BufDelete ++once ++nested lua require('plenary.window.float').clear(%s)", + bufnr, + bufnr + ) + ) +end + +local win_float = {} + +win_float.default_options = { + winblend = 15, + percentage = 0.9, +} + +function win_float.default_opts(options) + options = tbl.apply_defaults(options, win_float.default_options) + + local width = math.floor(vim.o.columns * options.percentage) + local height = math.floor(vim.o.lines * options.percentage) + + local top = math.floor(((vim.o.lines - height) / 2) - 1) + local left = math.floor((vim.o.columns - width) / 2) + + local opts = { + relative = "editor", + row = top, + col = left, + width = width, + height = height, + style = "minimal", + } + + return opts +end + +function win_float.centered(options) + options = tbl.apply_defaults(options, win_float.default_options) + + local win_opts = win_float.default_opts(options) + + local bufnr = options.bufnr or vim.api.nvim_create_buf(false, true) + local win_id = vim.api.nvim_open_win(bufnr, true, win_opts) + + vim.cmd "setlocal nocursorcolumn" + vim.api.nvim_win_set_option(win_id, "winblend", options.winblend) + + vim.cmd(string.format("autocmd WinLeave silent! execute 'bdelete! %s'", bufnr)) + + return { + bufnr = bufnr, + win_id = win_id, + } +end + +function win_float.centered_with_top_win(top_text, options) + options = tbl.apply_defaults(options, win_float.default_options) + + table.insert(top_text, 1, string.rep("=", 80)) + table.insert(top_text, string.rep("=", 80)) + + local primary_win_opts = win_float.default_opts(nil, nil, options) + local minor_win_opts = vim.deepcopy(primary_win_opts) + + primary_win_opts.height = primary_win_opts.height - #top_text - 1 + primary_win_opts.row = primary_win_opts.row + #top_text + 1 + + minor_win_opts.height = #top_text + + local minor_bufnr = vim.api.nvim_create_buf(false, true) + local minor_win_id = vim.api.nvim_open_win(minor_bufnr, true, minor_win_opts) + + vim.cmd "setlocal nocursorcolumn" + vim.api.nvim_win_set_option(minor_win_id, "winblend", options.winblend) + + vim.api.nvim_buf_set_lines(minor_bufnr, 0, -1, false, top_text) + + local primary_bufnr = vim.api.nvim_create_buf(false, true) + local primary_win_id = vim.api.nvim_open_win(primary_bufnr, true, primary_win_opts) + + vim.cmd "setlocal nocursorcolumn" + vim.api.nvim_win_set_option(primary_win_id, "winblend", options.winblend) + + -- vim.cmd( + -- string.format( + -- "autocmd WinLeave,BufDelete,BufLeave ++once ++nested silent! execute 'bdelete! %s'", + -- primary_buf, + -- minor_buf + -- ) + -- ) + + -- vim.cmd( + -- string.format( + -- "autocmd WinLeave,BufDelete,BufLeave ++once ++nested silent! execute 'bdelete! %s'", + -- primary_buf + -- ) + -- ) + + local primary_border = Border:new(primary_bufnr, primary_win_id, primary_win_opts, {}) + local minor_border = Border:new(minor_bufnr, minor_win_id, minor_win_opts, {}) + + _AssociatedBufs[primary_bufnr] = { + primary_win_id, + minor_win_id, + primary_border.win_id, + minor_border.win_id, + } + + clear_buf_on_leave(primary_bufnr) + + return { + bufnr = primary_bufnr, + win_id = primary_win_id, + + minor_bufnr = minor_bufnr, + minor_win_id = minor_win_id, + } +end + +--- Create window that takes up certain percentags of the current screen. +--- +--- Works regardless of current buffers, tabs, splits, etc. +--@param col_range number | Table: +-- If number, then center the window taking up this percentage of the screen. +-- If table, first index should be start, second_index should be end +--@param row_range number | Table: +-- If number, then center the window taking up this percentage of the screen. +-- If table, first index should be start, second_index should be end +--@param win_opts Table +--@param border_opts Table +function win_float.percentage_range_window(col_range, row_range, win_opts, border_opts) + win_opts = tbl.apply_defaults(win_opts, win_float.default_options) + + local default_win_opts = win_float.default_opts(win_opts) + default_win_opts.relative = "editor" + + local height_percentage, row_start_percentage + if type(row_range) == "number" then + assert(row_range <= 1) + assert(row_range > 0) + height_percentage = row_range + row_start_percentage = (1 - height_percentage) / 2 + elseif type(row_range) == "table" then + height_percentage = row_range[2] - row_range[1] + row_start_percentage = row_range[1] + else + error(string.format("Invalid type for 'row_range': %p", row_range)) + end + + default_win_opts.height = math.ceil(vim.o.lines * height_percentage) + default_win_opts.row = math.ceil(vim.o.lines * row_start_percentage) + + local width_percentage, col_start_percentage + if type(col_range) == "number" then + assert(col_range <= 1) + assert(col_range > 0) + width_percentage = col_range + col_start_percentage = (1 - width_percentage) / 2 + elseif type(col_range) == "table" then + width_percentage = col_range[2] - col_range[1] + col_start_percentage = col_range[1] + else + error(string.format("Invalid type for 'col_range': %p", col_range)) + end + + default_win_opts.col = math.floor(vim.o.columns * col_start_percentage) + default_win_opts.width = math.floor(vim.o.columns * width_percentage) + + local bufnr = win_opts.bufnr or vim.api.nvim_create_buf(false, true) + local win_id = vim.api.nvim_open_win(bufnr, true, default_win_opts) + vim.api.nvim_win_set_buf(win_id, bufnr) + + vim.cmd "setlocal nocursorcolumn" + vim.api.nvim_win_set_option(win_id, "winblend", win_opts.winblend) + + local border = Border:new(bufnr, win_id, default_win_opts, border_opts or {}) + + _AssociatedBufs[bufnr] = { win_id, border.win_id } + + clear_buf_on_leave(bufnr) + + return { + bufnr = bufnr, + win_id = win_id, + + border_bufnr = border.bufnr, + border_win_id = border.win_id, + } +end + +function win_float.clear(bufnr) + if _AssociatedBufs[bufnr] == nil then + return + end + + for _, win_id in ipairs(_AssociatedBufs[bufnr]) do + if vim.api.nvim_win_is_valid(win_id) then + vim.api.nvim_win_close(win_id, true) + end + end + + _AssociatedBufs[bufnr] = nil +end + +return win_float diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/window/init.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/window/init.lua new file mode 100644 index 00000000..e5b85e15 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/plenary/window/init.lua @@ -0,0 +1,16 @@ +local window = {} + +window.try_close = function(win_id, force) + if force == nil then + force = true + end + + pcall(vim.api.nvim_win_close, win_id, force) +end + +window.close_related_win = function(parent_win_id, child_win_id) + window.try_close(parent_win_id, true) + window.try_close(child_win_id, true) +end + +return window diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/lua/say.lua b/config/neovim/store/lazy-plugins/plenary.nvim/lua/say.lua new file mode 100644 index 00000000..f1d7e54e --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/lua/say.lua @@ -0,0 +1,61 @@ +local unpack = table.unpack or unpack + +local registry = { } +local current_namespace +local fallback_namespace + +local s = { + + _COPYRIGHT = "Copyright (c) 2012 Olivine Labs, LLC.", + _DESCRIPTION = "A simple string key/value store for i18n or any other case where you want namespaced strings.", + _VERSION = "Say 1.2", + + set_namespace = function(self, namespace) + current_namespace = namespace + if not registry[current_namespace] then + registry[current_namespace] = {} + end + end, + + set_fallback = function(self, namespace) + fallback_namespace = namespace + if not registry[fallback_namespace] then + registry[fallback_namespace] = {} + end + end, + + set = function(self, key, value) + registry[current_namespace][key] = value + end +} + +local __meta = { + __call = function(self, key, vars) + vars = vars or {} + + local str = registry[current_namespace][key] or registry[fallback_namespace][key] + + if str == nil then + return nil + end + str = tostring(str) + local strings = {} + + for i,v in ipairs(vars) do + table.insert(strings, tostring(v)) + end + + return #strings > 0 and str:format(unpack(strings)) or str + end, + + __index = function(self, key) + return registry[key] + end +} + +s:set_fallback('en') +s:set_namespace('en') + +s._registry = registry + +return setmetatable(s, __meta) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/nix-support/propagated-build-inputs b/config/neovim/store/lazy-plugins/plenary.nvim/nix-support/propagated-build-inputs new file mode 100644 index 00000000..7e59845c --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/nix-support/propagated-build-inputs @@ -0,0 +1 @@ +/nix/store/3mv7k6zp05ahmvg2pnvd6y59pnb1qd87-lua5.1-luassert-1.9.0-1 /nix/store/mqbhz05llkddfb5wni0m48kw22ixxps4-lua-5.1.5 \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/manifest b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/manifest new file mode 100644 index 00000000..e1e80029 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/manifest @@ -0,0 +1,364 @@ +commands = {} +dependencies = { + luassert = {}, + ["plenary.nvim"] = { + ["scm-1"] = { + { + constraints = { + { + op = ">=", + version = { + 5, 1, string = "5.1" + } + }, + { + op = "<", + version = { + 5, 4, string = "5.4" + } + } + }, + name = "lua" + }, + { + constraints = {}, + name = "luassert" + } + } + } +} +modules = { + ["luassert.array"] = { + "plenary.nvim/scm-1" + }, + ["luassert.assert"] = { + "plenary.nvim/scm-1" + }, + ["luassert.assertions"] = { + "plenary.nvim/scm-1" + }, + ["luassert.compatibility"] = { + "plenary.nvim/scm-1" + }, + ["luassert.formatters.binarystring"] = { + "plenary.nvim/scm-1" + }, + ["luassert.formatters.init"] = { + "plenary.nvim/scm-1" + }, + ["luassert.init"] = { + "plenary.nvim/scm-1" + }, + ["luassert.languages.en"] = { + "plenary.nvim/scm-1" + }, + ["luassert.match"] = { + "plenary.nvim/scm-1" + }, + ["luassert.matchers.composite"] = { + "plenary.nvim/scm-1" + }, + ["luassert.matchers.core"] = { + "plenary.nvim/scm-1" + }, + ["luassert.matchers.init"] = { + "plenary.nvim/scm-1" + }, + ["luassert.mock"] = { + "plenary.nvim/scm-1" + }, + ["luassert.modifiers"] = { + "plenary.nvim/scm-1" + }, + ["luassert.namespaces"] = { + "plenary.nvim/scm-1" + }, + ["luassert.spy"] = { + "plenary.nvim/scm-1" + }, + ["luassert.state"] = { + "plenary.nvim/scm-1" + }, + ["luassert.stub"] = { + "plenary.nvim/scm-1" + }, + ["luassert.util"] = { + "plenary.nvim/scm-1" + }, + ["plenary._meta._luassert"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async.api"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async.async"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async.control"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async.init"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async.lsp"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async.structs"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async.tests"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async.util"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async.uv_async"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async_lib.api"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async_lib.async"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async_lib.init"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async_lib.lsp"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async_lib.structs"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async_lib.tests"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async_lib.util"] = { + "plenary.nvim/scm-1" + }, + ["plenary.async_lib.uv_async"] = { + "plenary.nvim/scm-1" + }, + ["plenary.benchmark.init"] = { + "plenary.nvim/scm-1" + }, + ["plenary.benchmark.stat"] = { + "plenary.nvim/scm-1" + }, + ["plenary.bit"] = { + "plenary.nvim/scm-1" + }, + ["plenary.busted"] = { + "plenary.nvim/scm-1" + }, + ["plenary.class"] = { + "plenary.nvim/scm-1" + }, + ["plenary.collections.py_list"] = { + "plenary.nvim/scm-1" + }, + ["plenary.compat"] = { + "plenary.nvim/scm-1" + }, + ["plenary.context_manager"] = { + "plenary.nvim/scm-1" + }, + ["plenary.curl"] = { + "plenary.nvim/scm-1" + }, + ["plenary.debug_utils"] = { + "plenary.nvim/scm-1" + }, + ["plenary.enum"] = { + "plenary.nvim/scm-1" + }, + ["plenary.errors"] = { + "plenary.nvim/scm-1" + }, + ["plenary.filetype"] = { + "plenary.nvim/scm-1" + }, + ["plenary.fun"] = { + "plenary.nvim/scm-1" + }, + ["plenary.functional"] = { + "plenary.nvim/scm-1" + }, + ["plenary.init"] = { + "plenary.nvim/scm-1" + }, + ["plenary.iterators"] = { + "plenary.nvim/scm-1" + }, + ["plenary.job"] = { + "plenary.nvim/scm-1" + }, + ["plenary.json"] = { + "plenary.nvim/scm-1" + }, + ["plenary.log"] = { + "plenary.nvim/scm-1" + }, + ["plenary.lsp.override"] = { + "plenary.nvim/scm-1" + }, + ["plenary.neorocks.init"] = { + "plenary.nvim/scm-1" + }, + ["plenary.nvim_meta"] = { + "plenary.nvim/scm-1" + }, + ["plenary.operators"] = { + "plenary.nvim/scm-1" + }, + ["plenary.path"] = { + "plenary.nvim/scm-1" + }, + ["plenary.popup.init"] = { + "plenary.nvim/scm-1" + }, + ["plenary.popup.utils"] = { + "plenary.nvim/scm-1" + }, + ["plenary.profile"] = { + "plenary.nvim/scm-1" + }, + ["plenary.profile.lua_profiler"] = { + "plenary.nvim/scm-1" + }, + ["plenary.profile.memory_profiler"] = { + "plenary.nvim/scm-1" + }, + ["plenary.profile.p"] = { + "plenary.nvim/scm-1" + }, + ["plenary.reload"] = { + "plenary.nvim/scm-1" + }, + ["plenary.run"] = { + "plenary.nvim/scm-1" + }, + ["plenary.scandir"] = { + "plenary.nvim/scm-1" + }, + ["plenary.strings"] = { + "plenary.nvim/scm-1" + }, + ["plenary.tbl"] = { + "plenary.nvim/scm-1" + }, + ["plenary.test_harness"] = { + "plenary.nvim/scm-1" + }, + ["plenary.vararg.init"] = { + "plenary.nvim/scm-1" + }, + ["plenary.vararg.rotate"] = { + "plenary.nvim/scm-1" + }, + ["plenary.window.border"] = { + "plenary.nvim/scm-1" + }, + ["plenary.window.float"] = { + "plenary.nvim/scm-1" + }, + ["plenary.window.init"] = { + "plenary.nvim/scm-1" + }, + say = { + "plenary.nvim/scm-1" + } +} +repository = { + ["plenary.nvim"] = { + ["scm-1"] = { + { + arch = "installed", + commands = {}, + dependencies = {}, + modules = { + ["luassert.array"] = "luassert/array.lua", + ["luassert.assert"] = "luassert/assert.lua", + ["luassert.assertions"] = "luassert/assertions.lua", + ["luassert.compatibility"] = "luassert/compatibility.lua", + ["luassert.formatters.binarystring"] = "luassert/formatters/binarystring.lua", + ["luassert.formatters.init"] = "luassert/formatters/init.lua", + ["luassert.init"] = "luassert/init.lua", + ["luassert.languages.en"] = "luassert/languages/en.lua", + ["luassert.match"] = "luassert/match.lua", + ["luassert.matchers.composite"] = "luassert/matchers/composite.lua", + ["luassert.matchers.core"] = "luassert/matchers/core.lua", + ["luassert.matchers.init"] = "luassert/matchers/init.lua", + ["luassert.mock"] = "luassert/mock.lua", + ["luassert.modifiers"] = "luassert/modifiers.lua", + ["luassert.namespaces"] = "luassert/namespaces.lua", + ["luassert.spy"] = "luassert/spy.lua", + ["luassert.state"] = "luassert/state.lua", + ["luassert.stub"] = "luassert/stub.lua", + ["luassert.util"] = "luassert/util.lua", + ["plenary._meta._luassert"] = "plenary/_meta/_luassert.lua", + ["plenary.async.api"] = "plenary/async/api.lua", + ["plenary.async.async"] = "plenary/async/async.lua", + ["plenary.async.control"] = "plenary/async/control.lua", + ["plenary.async.init"] = "plenary/async/init.lua", + ["plenary.async.lsp"] = "plenary/async/lsp.lua", + ["plenary.async.structs"] = "plenary/async/structs.lua", + ["plenary.async.tests"] = "plenary/async/tests.lua", + ["plenary.async.util"] = "plenary/async/util.lua", + ["plenary.async.uv_async"] = "plenary/async/uv_async.lua", + ["plenary.async_lib.api"] = "plenary/async_lib/api.lua", + ["plenary.async_lib.async"] = "plenary/async_lib/async.lua", + ["plenary.async_lib.init"] = "plenary/async_lib/init.lua", + ["plenary.async_lib.lsp"] = "plenary/async_lib/lsp.lua", + ["plenary.async_lib.structs"] = "plenary/async_lib/structs.lua", + ["plenary.async_lib.tests"] = "plenary/async_lib/tests.lua", + ["plenary.async_lib.util"] = "plenary/async_lib/util.lua", + ["plenary.async_lib.uv_async"] = "plenary/async_lib/uv_async.lua", + ["plenary.benchmark.init"] = "plenary/benchmark/init.lua", + ["plenary.benchmark.stat"] = "plenary/benchmark/stat.lua", + ["plenary.bit"] = "plenary/bit.lua", + ["plenary.busted"] = "plenary/busted.lua", + ["plenary.class"] = "plenary/class.lua", + ["plenary.collections.py_list"] = "plenary/collections/py_list.lua", + ["plenary.compat"] = "plenary/compat.lua", + ["plenary.context_manager"] = "plenary/context_manager.lua", + ["plenary.curl"] = "plenary/curl.lua", + ["plenary.debug_utils"] = "plenary/debug_utils.lua", + ["plenary.enum"] = "plenary/enum.lua", + ["plenary.errors"] = "plenary/errors.lua", + ["plenary.filetype"] = "plenary/filetype.lua", + ["plenary.fun"] = "plenary/fun.lua", + ["plenary.functional"] = "plenary/functional.lua", + ["plenary.init"] = "plenary/init.lua", + ["plenary.iterators"] = "plenary/iterators.lua", + ["plenary.job"] = "plenary/job.lua", + ["plenary.json"] = "plenary/json.lua", + ["plenary.log"] = "plenary/log.lua", + ["plenary.lsp.override"] = "plenary/lsp/override.lua", + ["plenary.neorocks.init"] = "plenary/neorocks/init.lua", + ["plenary.nvim_meta"] = "plenary/nvim_meta.lua", + ["plenary.operators"] = "plenary/operators.lua", + ["plenary.path"] = "plenary/path.lua", + ["plenary.popup.init"] = "plenary/popup/init.lua", + ["plenary.popup.utils"] = "plenary/popup/utils.lua", + ["plenary.profile"] = "plenary/profile.lua", + ["plenary.profile.lua_profiler"] = "plenary/profile/lua_profiler.lua", + ["plenary.profile.memory_profiler"] = "plenary/profile/memory_profiler.lua", + ["plenary.profile.p"] = "plenary/profile/p.lua", + ["plenary.reload"] = "plenary/reload.lua", + ["plenary.run"] = "plenary/run.lua", + ["plenary.scandir"] = "plenary/scandir.lua", + ["plenary.strings"] = "plenary/strings.lua", + ["plenary.tbl"] = "plenary/tbl.lua", + ["plenary.test_harness"] = "plenary/test_harness.lua", + ["plenary.vararg.init"] = "plenary/vararg/init.lua", + ["plenary.vararg.rotate"] = "plenary/vararg/rotate.lua", + ["plenary.window.border"] = "plenary/window/border.lua", + ["plenary.window.float"] = "plenary/window/float.lua", + ["plenary.window.init"] = "plenary/window/init.lua", + say = "say.lua" + } + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/data/plenary/filetypes/base.lua b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/data/plenary/filetypes/base.lua new file mode 100644 index 00000000..8b76c8c7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/data/plenary/filetypes/base.lua @@ -0,0 +1,885 @@ +return { + extension = { + ['ncl'] = [[text]], + ['ph'] = [[perl]], + ['al'] = [[perl]], + ['cl'] = [[lisp]], + ['vhi'] = [[vhdl]], + ['sublime-snippet'] = [[xml]], + ['tcl'] = [[tcl]], + ['pp'] = [[pascal]], + ['builds'] = [[xml]], + ['lua'] = [[lua]], + ['pkb'] = [[plsql]], + ['wl'] = [[mma]], + ['6'] = [[groff]], + ['scm'] = [[scheme]], + ['ml'] = [[ocaml]], + ['filters'] = [[xml]], + ['st'] = [[html]], + ['ksy'] = [[yaml]], + ['mt'] = [[mma]], + ['ada'] = [[ada]], + ['vho'] = [[vhdl]], + ['nawk'] = [[awk]], + ['3pm'] = [[groff]], + ['maxhelp'] = [[json]], + ['ct'] = [[xml]], + ['ipp'] = [[cpp]], + ['a51'] = [[asm]], + ['meta'] = [[yaml]], + ['fsproj'] = [[xml]], + ['ccxml'] = [[xml]], + ['ado'] = [[stata]], + ['mli'] = [[ocaml]], + ['r2'] = [[rebol]], + ['csl'] = [[xml]], + ['bbx'] = [[tex]], + ['xsjslib'] = [[javascript]], + ['e'] = [[eiffel]], + ['tac'] = [[python]], + ['mustache'] = [[smarty]], + ['zcml'] = [[xml]], + ['glade'] = [[xml]], + ['cuh'] = [[cuda]], + ['cxx'] = [[cpp]], + ['urdf'] = [[xml]], + ['ditaval'] = [[xml]], + ['cscfg'] = [[xml]], + ['j2'] = [[django]], + ['pkgproj'] = [[xml]], + ['ma'] = [[mma]], + ['cljs.hl'] = [[clojure]], + ['brd'] = [[xml]], + ['asn'] = [[asn]], + ['xspec'] = [[xml]], + ['db2'] = [[sql]], + ['sjs'] = [[javascript]], + ['m'] = [[matlab]], + ['gdbinit'] = [[gdb]], + ['re'] = [[cpp]], + ['adoc'] = [[asciidoc]], + ['pyde'] = [[python]], + ['mjml'] = [[xml]], + ['workbook'] = [[markdown]], + ['ssjs'] = [[javascript]], + ['dircolors'] = [[dircolors]], + ['ui'] = [[xml]], + ['ant'] = [[xml]], + ['wast'] = [[wast]], + ['_js'] = [[javascript]], + ['7'] = [[groff]], + ['dylan'] = [[dylan]], + ['jsproj'] = [[xml]], + ['jsb'] = [[javascript]], + ['xml'] = [[xml]], + ['tcsh'] = [[tcsh]], + ['xpy'] = [[python]], + ['wisp'] = [[clojure]], + ['tm'] = [[tcl]], + ['plot'] = [[gnuplot]], + ['mjs'] = [[javascript]], + ['cjs'] = [[javascript]], + ['env'] = [[sh]], + ['jsfl'] = [[javascript]], + ['eye'] = [[ruby]], + ['jinja2'] = [[django]], + ['lookml'] = [[yaml]], + ['mcmeta'] = [[json]], + ['workflow'] = [[xml]], + ['eq'] = [[cs]], + ['storyboard'] = [[xml]], + ['nbp'] = [[mma]], + ['maxpat'] = [[json]], + ['yy'] = [[yacc]], + ['h++'] = [[cpp]], + ['asc'] = [[asciidoc]], + ['phps'] = [[php]], + ['cabal'] = [[cabal]], + ['xacro'] = [[xml]], + ['dhall'] = [[haskell]], + ['vba'] = [[vim]], + ['feature'] = [[cucumber]], + ['psc1'] = [[xml]], + ['plb'] = [[plsql]], + ['logtalk'] = [[logtalk]], + ['pascal'] = [[pascal]], + ['smk'] = [[python]], + ['rst.txt'] = [[rst]], + ['svh'] = [[systemverilog]], + ['sthlp'] = [[stata]], + ['nb'] = [[text]], + ['hrl'] = [[erlang]], + ['f'] = [[forth]], + ['rb'] = [[ruby]], + ['xsd'] = [[xml]], + ['cfg'] = [[dosini]], + ['1in'] = [[groff]], + ['mkiv'] = [[tex]], + ['mask'] = [[yaml]], + ['cproject'] = [[xml]], + ['gnuplot'] = [[gnuplot]], + ['sagews'] = [[python]], + ['nsh'] = [[nsis]], + ['njk'] = [[django]], + ['pic'] = [[pic]], + ['man'] = [[groff]], + ['owl'] = [[xml]], + ['ino'] = [[cpp]], + ['dtx'] = [[tex]], + ['volt'] = [[d]], + ['ltx'] = [[tex]], + ['rtf'] = [[rtf]], + ['jscad'] = [[javascript]], + ['8'] = [[groff]], + ['3qt'] = [[groff]], + ['n'] = [[groff]], + ['lisp'] = [[lisp]], + ['forth'] = [[forth]], + ['dll.config'] = [[xml]], + ['prefab'] = [[yaml]], + ['ads'] = [[ada]], + ['jake'] = [[javascript]], + ['zsh'] = [[sh]], + ['v'] = [[verilog]], + ['ini'] = [[dosini]], + ['command'] = [[sh]], + ['fr'] = [[forth]], + ['yacc'] = [[yacc]], + ['ccproj'] = [[xml]], + ['xqy'] = [[xquery]], + ['rabl'] = [[ruby]], + ['nr'] = [[groff]], + ['wsgi'] = [[python]], + ['model.lkml'] = [[yaml]], + ['lsl'] = [[lsl]], + ['yyp'] = [[json]], + ['yaml-tmlanguage'] = [[yaml]], + ['rbi'] = [[ruby]], + ['srt'] = [[lisp]], + ['unity'] = [[yaml]], + ['vhs'] = [[vhdl]], + ['yap'] = [[prolog]], + ['php5'] = [[php]], + ['pas'] = [[pascal]], + ['gyp'] = [[python]], + ['jsonl'] = [[json]], + ['cc'] = [[cpp]], + ['grt'] = [[groovy]], + ['txi'] = [[texinfo]], + ['asm'] = [[asm]], + ['plx'] = [[perl]], + ['prefs'] = [[dosini]], + ['mly'] = [[ocaml]], + ['xsl'] = [[xslt]], + ['osm'] = [[xml]], + ['sc'] = [[scala]], + ['au3'] = [[autoit]], + ['obj'] = [[obj]], + ['uc'] = [[java]], + ['lhs'] = [[lhaskell]], + ['1'] = [[groff]], + ['resx'] = [[xml]], + ['geojson'] = [[json]], + ['tmux'] = [[sh]], + ['rg'] = [[clojure]], + ['tcc'] = [[cpp]], + ['cbl'] = [[cobol]], + ['wat'] = [[wast]], + ['com'] = [[dcl]], + ['podspec'] = [[ruby]], + ['no'] = [[text]], + ['spec'] = [[python]], + ['vhw'] = [[vhdl]], + ['maxproj'] = [[json]], + ['xbm'] = [[c]], + ['wxl'] = [[xml]], + ['mk'] = [[make]], + ['epj'] = [[json]], + ['do'] = [[stata]], + ['rexx'] = [[rexx]], + ['ms'] = [[groff]], + ['w'] = [[cweb]], + ['ss'] = [[scheme]], + ['desktop.in'] = [[desktop]], + ['po'] = [[po]], + ['mdpolicy'] = [[xml]], + ['mathematica'] = [[mma]], + ['vw'] = [[plsql]], + ['rss'] = [[xml]], + ['cs'] = [[cs]], + ['mkdown'] = [[markdown]], + ['gs'] = [[javascript]], + ['doh'] = [[stata]], + ['vbproj'] = [[xml]], + ['sfproj'] = [[xml]], + ['mat'] = [[yaml]], + ['cmake.in'] = [[cmake]], + ['rest.txt'] = [[rst]], + ['sls'] = [[scheme]], + ['fxml'] = [[xml]], + ['jss'] = [[javascript]], + ['avsc'] = [[json]], + ['mbox'] = [[basic]], + ['cake'] = [[cs]], + ['gst'] = [[xml]], + ['p6l'] = [[perl6]], + ['cdf'] = [[mma]], + ['pyi'] = [[python]], + ['ahk'] = [[autohotkey]], + ['tmac'] = [[groff]], + ['xaml'] = [[xml]], + ['texi'] = [[texinfo]], + ['udf'] = [[sql]], + ['rebol'] = [[rebol]], + ['gvy'] = [[groovy]], + ['h'] = [[c]], + ['pm6'] = [[perl6]], + ['ivy'] = [[xml]], + ['properties'] = [[dosini]], + ['eliomi'] = [[ocaml]], + ['nanorc'] = [[nanorc]], + ['sps'] = [[scheme]], + ['nproj'] = [[xml]], + ['ch'] = [[clipper]], + ['odd'] = [[xml]], + ['fun'] = [[sml]], + ['mir'] = [[yaml]], + ['nuspec'] = [[xml]], + ['pck'] = [[plsql]], + ['2'] = [[groff]], + ['nl'] = [[lisp]], + ['fth'] = [[forth]], + ['cps'] = [[pascal]], + ['sh'] = [[sh]], + ['rbw'] = [[ruby]], + ['dlm'] = [[idl]], + ['pod'] = [[pod]], + ['clixml'] = [[xml]], + ['duby'] = [[ruby]], + ['gdb'] = [[gdb]], + ['boot'] = [[clojure]], + ['adb'] = [[ada]], + ['tex'] = [[tex]], + ['csx'] = [[cs]], + ['sbt'] = [[scala]], + ['csdef'] = [[xml]], + ['dpr'] = [[pascal]], + ['rex'] = [[rexx]], + ['srdf'] = [[xml]], + ['pl'] = [[perl]], + ['markdown'] = [[markdown]], + ['cgi'] = [[perl]], + ['cp'] = [[cpp]], + ['jinja'] = [[django]], + ['gp'] = [[gnuplot]], + ['x'] = [[rpcgen]], + ['pt'] = [[xml]], + ['tpp'] = [[cpp]], + ['intr'] = [[dylan]], + ['JSON-tmLanguage'] = [[json]], + ['ux'] = [[xml]], + ['fpp'] = [[fortran]], + ['phpt'] = [[php]], + ['4th'] = [[forth]], + ['dita'] = [[xml]], + ['viw'] = [[sql]], + ['vsixmanifest'] = [[xml]], + ['clj'] = [[clojure]], + ['yml.mysql'] = [[yaml]], + ['hpp'] = [[cpp]], + ['xsp.metadata'] = [[xml]], + ['sexp'] = [[lisp]], + ['csproj'] = [[xml]], + ['tab'] = [[sql]], + ['cql'] = [[sql]], + ['abap'] = [[abap]], + ['wlua'] = [[lua]], + ['lex'] = [[lex]], + ['java'] = [[java]], + ['edn'] = [[clojure]], + ['ditamap'] = [[xml]], + ['3p'] = [[groff]], + ['xul'] = [[xml]], + ['cmake'] = [[cmake]], + ['kit'] = [[basic]], + ['rs.in'] = [[rust]], + ['php4'] = [[php]], + ['hxx'] = [[cpp]], + ['cljscm'] = [[clojure]], + ['vcxproj'] = [[xml]], + ['php3'] = [[php]], + ['xml.dist'] = [[xml]], + ['ged'] = [[gedcom]], + ['i'] = [[asm]], + ['xproc'] = [[xml]], + ['ndproj'] = [[xml]], + ['less'] = [[less]], + ['lgt'] = [[logtalk]], + ['sql'] = [[plsql]], + ['cls'] = [[tex]], + ['targets'] = [[xml]], + ['me'] = [[groff]], + ['3x'] = [[groff]], + ['gtpl'] = [[groovy]], + ['prg'] = [[clipper]], + ['natvis'] = [[xml]], + ['3'] = [[groff]], + ['di'] = [[d]], + ['1x'] = [[groff]], + ['mdoc'] = [[groff]], + ['xsjs'] = [[javascript]], + ['iml'] = [[xml]], + ['ctp'] = [[php]], + ['kml'] = [[xml]], + ['eml'] = [[basic]], + ['raml'] = [[raml]], + ['gml'] = [[xml]], + ['yml'] = [[yaml]], + ['xq'] = [[xquery]], + ['sml'] = [[sml]], + ['sublime-syntax'] = [[yaml]], + ['mm'] = [[xml]], + ['y'] = [[yacc]], + ['mu'] = [[mupad]], + ['sld'] = [[scheme]], + ['asd'] = [[lisp]], + ['pgsql'] = [[sql]], + ['nqp'] = [[perl6]], + ['anim'] = [[yaml]], + ['vhf'] = [[vhdl]], + ['cu'] = [[cuda]], + ['make'] = [[make]], + ['pyx'] = [[pyrex]], + ['c++'] = [[cpp]], + ['lslp'] = [[lsl]], + ['rake'] = [[ruby]], + ['webmanifest'] = [[json]], + ['ijs'] = [[j]], + ['hsc'] = [[haskell]], + ['pl6'] = [[perl6]], + ['p6m'] = [[perl6]], + ['ny'] = [[lisp]], + ['mak'] = [[make]], + ['p6'] = [[perl6]], + ['6pm'] = [[perl6]], + ['lfe'] = [[lisp]], + ['6pl'] = [[perl6]], + ['toc'] = [[tex]], + ['yrl'] = [[erlang]], + ['vssettings'] = [[xml]], + ['sty'] = [[tex]], + ['ipynb'] = [[json]], + ['mkvi'] = [[tex]], + ['p'] = [[gnuplot]], + ['lmi'] = [[python]], + ['rockspec'] = [[lua]], + ['vhd'] = [[vhdl]], + ['sieve'] = [[sieve]], + ['lbx'] = [[tex]], + ['1m'] = [[groff]], + ['jelly'] = [[xml]], + ['3m'] = [[groff]], + ['ins'] = [[tex]], + ['xib'] = [[xml]], + ['cbx'] = [[tex]], + ['pd_lua'] = [[lua]], + ['dae'] = [[xml]], + ['emacs.desktop'] = [[lisp]], + ['bison'] = [[yacc]], + ['matlab'] = [[matlab]], + ['emacs'] = [[lisp]], + ['pot'] = [[po]], + ['el'] = [[lisp]], + ['podsl'] = [[lisp]], + ['pac'] = [[javascript]], + ['gitignore'] = [[gitignore]], + ['vue'] = [[vue]], + ['html.hl'] = [[html]], + ['trg'] = [[plsql]], + ['tps'] = [[plsql]], + ['hs-boot'] = [[haskell]], + ['csh'] = [[tcsh]], + ['mawk'] = [[awk]], + ['fan'] = [[fan]], + ['pike'] = [[pike]], + ['story'] = [[cucumber]], + ['plsql'] = [[plsql]], + ['app.src'] = [[erlang]], + ['mkd'] = [[markdown]], + ['ksh'] = [[sh]], + ['inl'] = [[cpp]], + ['ml4'] = [[ocaml]], + ['f77'] = [[fortran]], + ['pls'] = [[plsql]], + ['opencl'] = [[c]], + ['proj'] = [[xml]], + ['4'] = [[groff]], + ['wsf'] = [[xml]], + ['ninja'] = [[ninja]], + ['rest'] = [[rst]], + ['tpb'] = [[plsql]], + ['xquery'] = [[xquery]], + ['gitconfig'] = [[gitconfig]], + ['r3'] = [[rebol]], + ['reb'] = [[rebol]], + ['gawk'] = [[awk]], + ['groovy'] = [[groovy]], + ['auk'] = [[awk]], + ['r'] = [[rebol]], + ['cmd'] = [[dosbatch]], + ['awk'] = [[awk]], + ['xslt'] = [[xslt]], + ['ps1xml'] = [[xml]], + ['spc'] = [[plsql]], + ['wixproj'] = [[xml]], + ['upc'] = [[c]], + ['hic'] = [[clojure]], + ['cljx'] = [[clojure]], + ['cljs'] = [[clojure]], + ['json'] = [[json]], + ['cljc'] = [[clojure]], + ['pfa'] = [[postscr]], + ['vhdl'] = [[vhdl]], + ['cl2'] = [[clojure]], + ['nsi'] = [[nsis]], + ['g4'] = [[antlr]], + ['sage'] = [[python]], + ['haml.deface'] = [[haml]], + ['haml'] = [[haml]], + ['rno'] = [[groff]], + ['xrl'] = [[erlang]], + ['escript'] = [[erlang]], + ['pks'] = [[plsql]], + ['grxml'] = [[xml]], + ['erl'] = [[erlang]], + ['chem'] = [[pic]], + ['eb'] = [[python]], + ['patch'] = [[diff]], + ['diff'] = [[diff]], + ['mspec'] = [[ruby]], + ['xsp-config'] = [[xml]], + ['gv'] = [[dot]], + ['adp'] = [[tcl]], + ['webapp'] = [[json]], + ['epsi'] = [[postscr]], + ['dot'] = [[dot]], + ['phtml'] = [[php]], + ['lid'] = [[dylan]], + ['cpy'] = [[cobol]], + ['mkdn'] = [[markdown]], + ['ccp'] = [[cobol]], + ['cob'] = [[cobol]], + ['glf'] = [[tcl]], + ['sass'] = [[sass]], + ['gnu'] = [[gnuplot]], + ['pyp'] = [[python]], + ['gsp'] = [[gsp]], + ['asn1'] = [[asn]], + ['sig'] = [[sml]], + ['t'] = [[perl]], + ['xlf'] = [[xml]], + ['perl'] = [[perl]], + ['rpy'] = [[python]], + ['jbuilder'] = [[ruby]], + ['mdwn'] = [[markdown]], + ['dfm'] = [[pascal]], + ['c'] = [[c]], + ['pyw'] = [[python]], + ['mkfile'] = [[make]], + ['py3'] = [[python]], + ['gypi'] = [[python]], + ['py'] = [[python]], + ['watchr'] = [[ruby]], + ['thor'] = [[ruby]], + ['ruby'] = [[ruby]], + ['ru'] = [[ruby]], + ['gltf'] = [[json]], + ['rbx'] = [[ruby]], + ['rbuild'] = [[ruby]], + ['m4'] = [[m4]], + ['god'] = [[ruby]], + ['har'] = [[json]], + ['sas'] = [[sas]], + ['mkii'] = [[tex]], + ['frt'] = [[forth]], + ['icl'] = [[clean]], + ['mirah'] = [[ruby]], + ['wxi'] = [[xml]], + ['lektorproject'] = [[dosini]], + ['nasm'] = [[asm]], + ['njs'] = [[javascript]], + ['vstemplate'] = [[xml]], + ['jsm'] = [[javascript]], + ['html'] = [[html]], + ['xpm'] = [[xpm]], + ['tool'] = [[sh]], + ['rdf'] = [[xml]], + ['rd'] = [[r]], + ['dyl'] = [[dylan]], + ['p8'] = [[lua]], + ['prw'] = [[clipper]], + ['es6'] = [[javascript]], + ['gmx'] = [[xml]], + ['pluginspec'] = [[ruby]], + ['gemspec'] = [[ruby]], + ['aux'] = [[tex]], + ['lvlib'] = [[xml]], + ['cats'] = [[c]], + ['fcgi'] = [[perl]], + ['for'] = [[forth]], + ['scxml'] = [[xml]], + ['mdown'] = [[markdown]], + ['scala'] = [[scala]], + ['pxd'] = [[pyrex]], + ['mxml'] = [[xml]], + ['chs'] = [[haskell]], + ['syntax'] = [[yaml]], + ['5'] = [[groff]], + ['xliff'] = [[xml]], + ['pyt'] = [[python]], + ['view.lkml'] = [[yaml]], + ['pat'] = [[json]], + ['bash'] = [[sh]], + ['tfstate'] = [[json]], + ['vmb'] = [[vim]], + ['prolog'] = [[prolog]], + ['asciidoc'] = [[asciidoc]], + ['asset'] = [[yaml]], + ['mxt'] = [[json]], + ['rviz'] = [[yaml]], + ['yaml.sed'] = [[yaml]], + ['inc'] = [[php]], + ['props'] = [[xml]], + ['psgi'] = [[perl]], + ['axml'] = [[xml]], + ['pxi'] = [[pyrex]], + ['admx'] = [[xml]], + ['nse'] = [[lua]], + ['wxs'] = [[xml]], + ['ice'] = [[json]], + ['builder'] = [[ruby]], + ['jsp'] = [[jsp]], + ['eliom'] = [[ocaml]], + ['go'] = [[go]], + ['ihlp'] = [[stata]], + ['scss'] = [[scss]], + ['sce'] = [[scilab]], + ['lsp'] = [[lisp]], + ['x3d'] = [[xml]], + ['rs'] = [[rust]], + ['php'] = [[php]], + ['htm'] = [[html]], + ['pprx'] = [[rexx]], + ['es'] = [[erlang]], + ['js'] = [[javascript]], + ['ts'] = [[typescript]], + ['uno'] = [[cs]], + ['sch'] = [[scheme]], + ['lvproj'] = [[xml]], + ['xs'] = [[xs]], + ['pro'] = [[idl]], + ['dotsettings'] = [[xml]], + ['res'] = [[xml]], + ['xmi'] = [[xml]], + ['ahkl'] = [[autohotkey]], + ['plt'] = [[gnuplot]], + ['wlt'] = [[mma]], + ['lpr'] = [[pascal]], + ['dof'] = [[dosini]], + ['fs'] = [[forth]], + ['sh.in'] = [[sh]], + ['bat'] = [[dosbatch]], + ['roff'] = [[groff]], + ['depproj'] = [[xml]], + ['mdx'] = [[markdown]], + ['hs'] = [[haskell]], + ['frag'] = [[javascript]], + ['ps'] = [[postscr]], + ['9'] = [[groff]], + ['eps'] = [[postscr]], + ['ck'] = [[java]], + ['rst'] = [[rst]], + ['css'] = [[css]], + ['aw'] = [[php]], + ['veo'] = [[verilog]], + ['adml'] = [[xml]], + ['mll'] = [[ocaml]], + ['tst'] = [[scilab]], + ['svg'] = [[svg]], + ['bdy'] = [[plsql]], + ['xql'] = [[xquery]], + ['xqm'] = [[xquery]], + ['pm'] = [[perl]], + ['texinfo'] = [[texinfo]], + ['prc'] = [[plsql]], + ['3in'] = [[groff]], + ['rbxs'] = [[lua]], + ['xproj'] = [[xml]], + ['bdf'] = [[bdf]], + ['ampl'] = [[ampl]], + ['d'] = [[d]], + ['fnc'] = [[plsql]], + ['cobol'] = [[cobol]], + ['txt'] = [[text]], + ['vim'] = [[vim]], + ['mata'] = [[stata]], + ['linq'] = [[cs]], + ['launch'] = [[xml]], + ['sv'] = [[systemverilog]], + ['nlogo'] = [[lisp]], + ['sci'] = [[scilab]], + ['dockerfile'] = [[dockerfile]], + ['vht'] = [[vhdl]], + ['sed'] = [[sed]], + ['xht'] = [[html]], + ['liquid'] = [[liquid]], + ['latte'] = [[latte]], + ['vhost'] = [[apache]], + ['matah'] = [[stata]], + ['ronn'] = [[markdown]], + ['tpl'] = [[smarty]], + ['xhtml'] = [[html]], + ['bones'] = [[javascript]], + ['bats'] = [[sh]], + ['xpl'] = [[xml]], + ['shproj'] = [[xml]], + ['tfstate.backup'] = [[json]], + ['bzl'] = [[bzl]], + ['cpp'] = [[cpp]], + ['mod'] = [[ampl]], + ['idc'] = [[c]], + ['hh'] = [[cpp]], + ['druby'] = [[ruby]], + ['apacheconf'] = [[apache]], + ['l'] = [[lex]], + ['md'] = [[markdown]], + ['vxml'] = [[xml]], + ['pmod'] = [[pike]], + ['wsdl'] = [[xml]], + ['mtml'] = [[basic]], + ['vh'] = [[systemverilog]], + ['ddl'] = [[plsql]], + ['topojson'] = [[json]], + ['mysql'] = [[sql]], + ['kojo'] = [[scala]], + ['rsx'] = [[r]], + ['tml'] = [[xml]], + ['dcl'] = [[clean]], + ['reek'] = [[yaml]], + ['yaml'] = [[yaml]], + ['desktop'] = [[desktop]], + ['tsx'] = [[xml]], + }, + file_name = { + ['.classpath'] = [[xml]], + ['bsdmakefile'] = [[make]], + ['delete.me'] = [[text]], + ['packages.config'] = [[xml]], + ['jenkinsfile'] = [[groovy]], + ['ant.xml'] = [[ant]], + ['makefile.frag'] = [[make]], + ['puppetfile'] = [[ruby]], + ['.inputrc'] = [[readline]], + ['.zshrc'] = [[sh]], + ['inputrc'] = [[readline]], + ['gnumakefile'] = [[make]], + ['makefile.sco'] = [[make]], + ['pkgbuild'] = [[sh]], + ['.babelignore'] = [[gitignore]], + ['.bash_logout'] = [[sh]], + ['.nanorc'] = [[nanorc]], + ['berksfile'] = [[ruby]], + ['lexer.x'] = [[lex]], + ['sconscript'] = [[python]], + ['makefile.boot'] = [[make]], + ['starfield'] = [[tcl]], + ['.login'] = [[sh]], + ['composer.lock'] = [[json]], + ['dir_colors'] = [[dircolors]], + ['.nvimrc'] = [[vim]], + ['.project'] = [[xml]], + ['web.config'] = [[xml]], + ['makefile.in'] = [[make]], + ['readme.me'] = [[text]], + ['.cproject'] = [[xml]], + ['nuget.config'] = [[xml]], + ['zlogin'] = [[sh]], + ['phakefile'] = [[php]], + ['brewfile'] = [[ruby]], + ['podfile'] = [[ruby]], + ['use.stable.mask'] = [[text]], + ['.gemrc'] = [[yaml]], + ['emakefile'] = [[erlang]], + ['notebook'] = [[json]], + ['read.me'] = [[text]], + ['.dircolors'] = [[dircolors]], + ['.gitmodules'] = [[gitconfig]], + ['owh'] = [[tcl]], + ['zlogout'] = [[sh]], + ['package.use.stable.mask'] = [[text]], + ['httpd.conf'] = [[apache]], + ['buildozer.spec'] = [[dosini]], + ['.zshenv'] = [[sh]], + ['workspace'] = [[bzl]], + ['readme.1st'] = [[text]], + ['.simplecov'] = [[ruby]], + ['.vimrc'] = [[vim]], + ['.htmlhintrc'] = [[json]], + ['mmn'] = [[groff]], + ['rexfile'] = [[perl]], + ['m3overrides'] = [[quake]], + ['.arcconfig'] = [[json]], + ['.htaccess'] = [[apache]], + ['.php_cs'] = [[php]], + ['tiltfile'] = [[bzl]], + ['wscript'] = [[python]], + ['.stylelintignore'] = [[gitignore]], + ['gvimrc'] = [[vim]], + ['.tern-config'] = [[json]], + ['_dir_colors'] = [[dircolors]], + ['app.config'] = [[xml]], + ['abbrev_defs'] = [[lisp]], + ['_emacs'] = [[lisp]], + ['buck'] = [[bzl]], + ['dockerfile'] = [[dockerfile]], + ['project.ede'] = [[lisp]], + ['thorfile'] = [[ruby]], + ['rakefile'] = [[ruby]], + ['.viper'] = [[lisp]], + ['.spacemacs'] = [[lisp]], + ['.gnus'] = [[lisp]], + ['snapfile'] = [[ruby]], + ['eqnrc'] = [[groff]], + ['_dircolors'] = [[dircolors]], + ['configure.ac'] = [[m4]], + ['cabal.project'] = [[cabal]], + ['.env'] = [[sh]], + ['.luacheckrc'] = [[lua]], + ['9fs'] = [[sh]], + ['cabal.config'] = [[cabal]], + ['.bash_aliases'] = [[sh]], + ['install.mysql'] = [[text]], + ['yarn.lock'] = [[yaml]], + ['gitignore_global'] = [[gitignore]], + ['.cshrc'] = [[sh]], + ['.bash_history'] = [[sh]], + ['.env.example'] = [[sh]], + ['.npmignore'] = [[gitignore]], + ['gitignore-global'] = [[gitignore]], + ['build.bazel'] = [[bzl]], + ['.flaskenv'] = [[sh]], + ['license'] = [[text]], + ['mavenfile'] = [[ruby]], + ['.vscodeignore'] = [[gitignore]], + ['.abbrev_defs'] = [[lisp]], + ['gemfile.lock'] = [[ruby]], + ['go.mod'] = [[text]], + ['.gvimrc'] = [[vim]], + ['.nodemonignore'] = [[gitignore]], + ['bashrc'] = [[sh]], + ['man'] = [[sh]], + ['.dockerignore'] = [[gitignore]], + ['makefile.am'] = [[make]], + ['.zlogin'] = [[sh]], + ['.cvsignore'] = [[gitignore]], + ['.coffeelintignore'] = [[gitignore]], + ['.php'] = [[php]], + ['expr-dist'] = [[r]], + ['zshrc'] = [[sh]], + ['.clang-format'] = [[yaml]], + ['bash_aliases'] = [[sh]], + ['.exrc'] = [[vim]], + ['web.release.config'] = [[xml]], + ['.atomignore'] = [[gitignore]], + ['makefile.wat'] = [[make]], + ['troffrc-end'] = [[groff]], + ['vimrc'] = [[vim]], + ['cmakelists.txt'] = [[cmake]], + ['.gitconfig'] = [[gitconfig]], + ['riemann.config'] = [[clojure]], + ['zprofile'] = [[sh]], + ['rebar.lock'] = [[erlang]], + ['news'] = [[text]], + ['rebar.config'] = [[erlang]], + ['mcmod.info'] = [[json]], + ['cpanfile'] = [[perl]], + ['readme.mysql'] = [[text]], + ['makefile.pl'] = [[perl]], + ['dangerfile'] = [[ruby]], + ['snakefile'] = [[python]], + ['contents.lr'] = [[markdown]], + ['sconstruct'] = [[python]], + ['glide.lock'] = [[yaml]], + ['deps'] = [[python]], + ['.zprofile'] = [[sh]], + ['.gclient'] = [[python]], + ['keep.me'] = [[text]], + ['troffrc'] = [[groff]], + ['m3makefile'] = [[quake]], + ['cshrc'] = [[sh]], + ['.emacs.desktop'] = [[lisp]], + ['.zlogout'] = [[sh]], + ['.rprofile'] = [[r]], + ['cask'] = [[lisp]], + ['jarfile'] = [[ruby]], + ['deliverfile'] = [[ruby]], + ['copyright.regex'] = [[text]], + ['.prettierignore'] = [[gitignore]], + ['gemfile'] = [[ruby]], + ['profile'] = [[sh]], + ['fastfile'] = [[ruby]], + ['guardfile'] = [[ruby]], + ['capfile'] = [[ruby]], + ['buildfile'] = [[ruby]], + ['jakefile'] = [[javascript]], + ['appraisals'] = [[ruby]], + ['fontlog'] = [[text]], + ['package.use.mask'] = [[text]], + ['.pryrc'] = [[ruby]], + ['.emacs'] = [[lisp]], + ['.watchmanconfig'] = [[json]], + ['.irbrc'] = [[ruby]], + ['.tern-project'] = [[json]], + ['web.debug.config'] = [[xml]], + ['.eslintignore'] = [[gitignore]], + ['.gitignore'] = [[gitignore]], + ['copying.regex'] = [[text]], + ['build'] = [[bzl]], + ['nvimrc'] = [[vim]], + ['.dir_colors'] = [[dircolors]], + ['.bashrc'] = [[sh]], + ['kbuild'] = [[make]], + ['.profile'] = [[sh]], + ['.php_cs.dist'] = [[php]], + ['gradlew'] = [[sh]], + ['settings.stylecop'] = [[xml]], + ['makefile.inc'] = [[make]], + ['mkfile'] = [[make]], + ['.bzrignore'] = [[gitignore]], + ['ack'] = [[perl]], + ['license.mysql'] = [[text]], + ['makefile'] = [[make]], + ['vagrantfile'] = [[ruby]], + ['nanorc'] = [[nanorc]], + ['.bash_profile'] = [[sh]], + ['bash_logout'] = [[sh]], + ['bash_profile'] = [[sh]], + ['click.me'] = [[text]], + ['login'] = [[sh]], + ['_vimrc'] = [[vim]], + ['go.sum'] = [[text]], + ['apache2.conf'] = [[apache]], + ['use.mask'] = [[text]], + ['build.xml'] = [[ant]], + ['mmt'] = [[groff]], + ['zshenv'] = [[sh]], + ['copying'] = [[text]], + ['install'] = [[text]], + ['test.me'] = [[text]], + ['package.mask'] = [[text]], + ['.clang-tidy'] = [[yaml]], + ['readme.nss'] = [[text]], + ['rebar.config.lock'] = [[erlang]], + }, +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/data/plenary/filetypes/builtin.lua b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/data/plenary/filetypes/builtin.lua new file mode 100644 index 00000000..c4b6d507 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/data/plenary/filetypes/builtin.lua @@ -0,0 +1,71 @@ +local shebang_prefixes = { '/usr/bin/', '/bin/', '/usr/bin/env ', '/bin/env ' } +local shebang_fts = { + ['fish'] = 'fish', + ['perl'] = 'perl', + ['python'] = 'python', + ['python2'] = 'python', + ['python3'] = 'python', + ['bash'] = 'sh', + ['sh'] = 'sh', + ['zsh'] = 'zsh', +} + +local shebang = {} +for _, prefix in ipairs(shebang_prefixes) do + for k, v in pairs(shebang_fts) do + shebang[prefix .. k] = v + end +end + +return { + extension = { + ['_coffee'] = 'coffee', + ['astro'] = 'astro', + ['cts'] = 'typescript', + ['cljd'] = 'clojure', + ['coffee'] = 'coffee', + ['dart'] = 'dart', + ['erb'] = 'eruby', + ['ex'] = 'elixir', + ['exs'] = 'elixir', + ['fish'] = 'fish', + ['fnl'] = 'fennel', + ['gd'] = 'gdscript', + ['gql'] = 'graphql', + ['gradle'] = 'groovy', + ['graphql'] = 'graphql', + ['hbs'] = 'handlebars', + ['hdbs'] = 'handlebars', + ['hlsl'] = 'hlsl', + ['jai'] = 'jai', + ['janet'] = 'janet', + ['jl'] = 'julia', + ['jsx'] = 'javascriptreact', + ['kt'] = 'kotlin', + ['mts'] = 'typescript', + ['nix'] = 'nix', + ['plist'] = 'xml', + ['purs'] = 'purescript', + ['r'] = 'r', + ['res'] = 'rescript', + ['resi'] = 'rescript', + ['rkt'] = 'racket', + ['svelte'] = 'svelte', + ['tres'] = 'gdresource', + ['tscn'] = 'gdresource', + ['tsx'] = 'typescriptreact', + ['smithy'] = [[smithy]], + ['sol'] = 'solidity', + ['dtsi'] = 'dts', + }, + file_name = { + ['cakefile'] = 'coffee', + ['.babelrc'] = 'json', + ['.clangd'] = 'yaml', + ['.eslintrc'] = 'json', + ['.firebaserc'] = 'json', + ['.prettierrc'] = 'json', + ['.stylelintrc'] = 'json', + }, + shebang = shebang +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/plenary.nvim-scm-1.rockspec b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/plenary.nvim-scm-1.rockspec new file mode 100644 index 00000000..73a0f198 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/plenary.nvim-scm-1.rockspec @@ -0,0 +1,35 @@ +local _MODREV, _SPECREV = 'scm', '-1' +rockspec_format = "3.0" +package = 'plenary.nvim' +version = _MODREV .. _SPECREV + +description = { + summary = 'lua functions you don\'t want to write ', + labels = { "neovim" }, + detailed = [[ + plenary: full; complete; entire; absolute; unqualified. All the lua functions I don't want to write twice. + ]], + homepage = 'http://github.com/nvim-lua/plenary.nvim', + license = 'MIT/X11', +} + +dependencies = { + 'lua >= 5.1, < 5.4', + 'luassert' +} + +source = { + url = 'git://github.com/nvim-lua/plenary.nvim', +} + +build = { + type = 'builtin', + copy_directories = { + 'data', + 'plugin' + } +} +test = { + type = "command", + command = "make test" +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/plugin/plenary.vim b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/plugin/plenary.vim new file mode 100644 index 00000000..3fb7bc83 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/plugin/plenary.vim @@ -0,0 +1,9 @@ + +" Create command for running busted +command! -nargs=1 -complete=file PlenaryBustedFile + \ lua require('plenary.test_harness').test_file([[]]) + +command! -nargs=+ -complete=file PlenaryBustedDirectory + \ lua require('plenary.test_harness').test_directory_command([[]]) + +nnoremap PlenaryTestFile :lua require('plenary.test_harness').test_file(vim.fn.expand("%:p")) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/rock_manifest b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/rock_manifest new file mode 100644 index 00000000..c37bfc59 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1-unstable-2024-05-20-rocks/plenary.nvim/scm-1/rock_manifest @@ -0,0 +1,128 @@ +rock_manifest = { + data = { + plenary = { + filetypes = { + ["base.lua"] = "fecf92b2b2a11bb27fbeeaeb131bf0da", + ["builtin.lua"] = "97e828c424574ea64aca2e35bf0e9f9a" + } + } + }, + lua = { + luassert = { + ["array.lua"] = "a3597420b35f43e02bd7721ceffa52de", + ["assert.lua"] = "4c3a746b57d71a6315c724aa9e20384c", + ["assertions.lua"] = "527931b0cd7646261d0738edf8d748d6", + ["compatibility.lua"] = "90c60281b15729c51082548903f941c7", + formatters = { + ["binarystring.lua"] = "1cf3fba0573869e42fe1374f3919b2db", + ["init.lua"] = "9842c79929b51698ff5f925cbf511e37" + }, + ["init.lua"] = "38edb8efb69fa44c235f9cc0884198c0", + languages = { + ["en.lua"] = "14268101fcddfbf9e22b5d9683815a63" + }, + ["match.lua"] = "9fcd14b357f2cb6802baf654b3b9e2fa", + matchers = { + ["composite.lua"] = "9bd6f059229ceabfd56de2a572a628ff", + ["core.lua"] = "1e1fb1b85a73acb2b8d7bfa238b01e04", + ["init.lua"] = "9ab33a60d7d377bfe0f4f1444ef5d600" + }, + ["mock.lua"] = "f8310817f772468c7ce2e3bbd2d18643", + ["modifiers.lua"] = "ef6c106c065ba04c5c664439db82e427", + ["namespaces.lua"] = "8dde7ef12282e586914454ac27cbec60", + ["spy.lua"] = "05651cdf35da46d7a2d726009cb90795", + ["state.lua"] = "1a6406c5e1c5fbb9419d0f07e9cf3303", + ["stub.lua"] = "fe69240533b4c18ad2005c2d894b7557", + ["util.lua"] = "d7080aebbbe64e6b444ddfec3214a449" + }, + plenary = { + _meta = { + ["_luassert.lua"] = "5a2ee66dd1a74016484a08b33d085b00" + }, + async = { + ["api.lua"] = "37d4c241eabadcea0c526c483899d863", + ["async.lua"] = "52a321d6ee2496aad0d3db8025f692da", + ["control.lua"] = "026788ddd2daa31db523b20186c7cdf1", + ["init.lua"] = "dfbe40648509c29e51b1bb8813fc8cc8", + ["lsp.lua"] = "d35d20ec07bf07737f60f74f2628ff20", + ["structs.lua"] = "812595922286653a4f68db51ffc6b8e7", + ["tests.lua"] = "fc7eac55ecf464f809c248bbfc1db78e", + ["util.lua"] = "640d8fa66bd1eea60d2fac0e4a9502e6", + ["uv_async.lua"] = "aebc4d8ef1c8829701102fd28b50d6a8" + }, + async_lib = { + ["api.lua"] = "3192d51dd33ee70098279d1f9f7867e3", + ["async.lua"] = "744a2f0155404e8fc519ab8832c849dc", + ["init.lua"] = "babd671bd31fa668aa0bb94b4c9e5513", + ["lsp.lua"] = "1496e6b8929cf24ae8d416e6b8597f75", + ["structs.lua"] = "a7a06f7fb2c6a89305a48ef123f4c87a", + ["tests.lua"] = "466252dcd6597d263b4eceb0dc688ec5", + ["util.lua"] = "97815a67faa9bd0594a4e7c585adf67f", + ["uv_async.lua"] = "d26c79ffe4f7b3cb6c623aede7bc161b" + }, + benchmark = { + ["init.lua"] = "b0c7321606a5b77a0f915de452320e56", + ["stat.lua"] = "9dfddd26f874e79bdee0773c57b08810" + }, + ["bit.lua"] = "c1242872a488007048a2fbe769906861", + ["busted.lua"] = "da38e9e5500907c0101450bf1aa37f11", + ["class.lua"] = "d0b258402d5839996aaeb75c4b507b0b", + collections = { + ["py_list.lua"] = "eb8403d14eef118e78f1c2c2e5ce3d3e" + }, + ["compat.lua"] = "0977cac3f6d6c5de404913e14d384d2a", + ["context_manager.lua"] = "3a59b45a12936d697355ab1638a14fe9", + ["curl.lua"] = "8ce22b22934008460cad0245f1b9f9c4", + ["debug_utils.lua"] = "9362a78d7f4d1b8bc836640de283353a", + ["enum.lua"] = "bfb8f89915f80658695f8dbada67dada", + ["errors.lua"] = "a39cf5fc1c6642bd3cf631ea5ec2a2d9", + ["filetype.lua"] = "be47ab2aa7acd41bea9be80323214fc0", + ["fun.lua"] = "713058172801d2427e9d670cbd8b0dca", + ["functional.lua"] = "be8ed5a614e1ad59540753774dea8ec8", + ["init.lua"] = "3814c65db80f62c61d854797b9c25691", + ["iterators.lua"] = "60c39e027360ba1f082c322f6f9a0289", + ["job.lua"] = "9cfe9a2101c620b6b0d2878445da3b63", + ["json.lua"] = "1989dffd7975806a8fd3cb1b3bb5243f", + ["log.lua"] = "2d2fccbc6ec8cfd60ba4d69d1926aad0", + lsp = { + ["override.lua"] = "424a207aeeeeb54d858e480b793d912e" + }, + neorocks = { + ["init.lua"] = "1117e199d3980495cebc90f6c1b5c1ac" + }, + ["nvim_meta.lua"] = "9292adc81b35a4188635d1bbdba1af16", + ["operators.lua"] = "dd0d8d59d77b2b406e32eb23d1d52b93", + ["path.lua"] = "67269bef8bc21dc1207346c4b527a9e8", + popup = { + ["init.lua"] = "a13e46b0000d419ab885611319c4dd90", + ["utils.lua"] = "ba82fd273233615598a5934c59ade438" + }, + profile = { + ["lua_profiler.lua"] = "7982caaef0ccc0393f9b41b3b5352de6", + ["memory_profiler.lua"] = "c4547eac8da2e3f0d1c8df479c2900d2", + ["p.lua"] = "b49065c5a37f70f1a09338f916b7b328" + }, + ["profile.lua"] = "6dc3e55c6ff395ef5b7b9045240101bb", + ["reload.lua"] = "aabfbb2dfae39e2f8aba43cbf90b082f", + ["run.lua"] = "d2f3478da4fb1ab8ab5179429b649e53", + ["scandir.lua"] = "150a9941d213ddff29429d7f843d1483", + ["strings.lua"] = "678bcda7a93920cf14456dc3ccd77d2f", + ["tbl.lua"] = "2947dc8678233f52c4345e212f002f9d", + ["test_harness.lua"] = "26374151c7e6c37ee481fa266fbf4242", + vararg = { + ["init.lua"] = "238160856408eee3a90b966f79c57780", + ["rotate.lua"] = "e591d58b1060f99e54867e74c13f9193" + }, + window = { + ["border.lua"] = "94370d22429202792f1c7c85b4d2aa6f", + ["float.lua"] = "29b0df43b37bb1ad2fe101f7b31af657", + ["init.lua"] = "21d83fd38843c078eadfe9d122610fd2" + } + }, + ["say.lua"] = "dd352934a1656fe97ae9c0422d2628a3" + }, + ["plenary.nvim-scm-1.rockspec"] = "dbec2339a4a299bfc46704015fec397f", + plugin = { + ["plenary.vim"] = "aa44419a41d4a055ec1d5287bad626f2" + } +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1.rockspec b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1.rockspec new file mode 100644 index 00000000..73a0f198 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/plenary.nvim-scm-1.rockspec @@ -0,0 +1,35 @@ +local _MODREV, _SPECREV = 'scm', '-1' +rockspec_format = "3.0" +package = 'plenary.nvim' +version = _MODREV .. _SPECREV + +description = { + summary = 'lua functions you don\'t want to write ', + labels = { "neovim" }, + detailed = [[ + plenary: full; complete; entire; absolute; unqualified. All the lua functions I don't want to write twice. + ]], + homepage = 'http://github.com/nvim-lua/plenary.nvim', + license = 'MIT/X11', +} + +dependencies = { + 'lua >= 5.1, < 5.4', + 'luassert' +} + +source = { + url = 'git://github.com/nvim-lua/plenary.nvim', +} + +build = { + type = 'builtin', + copy_directories = { + 'data', + 'plugin' + } +} +test = { + type = "command", + command = "make test" +} diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/plugin/plenary.vim b/config/neovim/store/lazy-plugins/plenary.nvim/plugin/plenary.vim new file mode 100644 index 00000000..3fb7bc83 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/plugin/plenary.vim @@ -0,0 +1,9 @@ + +" Create command for running busted +command! -nargs=1 -complete=file PlenaryBustedFile + \ lua require('plenary.test_harness').test_file([[]]) + +command! -nargs=+ -complete=file PlenaryBustedDirectory + \ lua require('plenary.test_harness').test_directory_command([[]]) + +nnoremap PlenaryTestFile :lua require('plenary.test_harness').test_file(vim.fn.expand("%:p")) diff --git a/config/neovim/store/lazy-plugins/plenary.nvim/rock_manifest b/config/neovim/store/lazy-plugins/plenary.nvim/rock_manifest new file mode 100644 index 00000000..c37bfc59 --- /dev/null +++ b/config/neovim/store/lazy-plugins/plenary.nvim/rock_manifest @@ -0,0 +1,128 @@ +rock_manifest = { + data = { + plenary = { + filetypes = { + ["base.lua"] = "fecf92b2b2a11bb27fbeeaeb131bf0da", + ["builtin.lua"] = "97e828c424574ea64aca2e35bf0e9f9a" + } + } + }, + lua = { + luassert = { + ["array.lua"] = "a3597420b35f43e02bd7721ceffa52de", + ["assert.lua"] = "4c3a746b57d71a6315c724aa9e20384c", + ["assertions.lua"] = "527931b0cd7646261d0738edf8d748d6", + ["compatibility.lua"] = "90c60281b15729c51082548903f941c7", + formatters = { + ["binarystring.lua"] = "1cf3fba0573869e42fe1374f3919b2db", + ["init.lua"] = "9842c79929b51698ff5f925cbf511e37" + }, + ["init.lua"] = "38edb8efb69fa44c235f9cc0884198c0", + languages = { + ["en.lua"] = "14268101fcddfbf9e22b5d9683815a63" + }, + ["match.lua"] = "9fcd14b357f2cb6802baf654b3b9e2fa", + matchers = { + ["composite.lua"] = "9bd6f059229ceabfd56de2a572a628ff", + ["core.lua"] = "1e1fb1b85a73acb2b8d7bfa238b01e04", + ["init.lua"] = "9ab33a60d7d377bfe0f4f1444ef5d600" + }, + ["mock.lua"] = "f8310817f772468c7ce2e3bbd2d18643", + ["modifiers.lua"] = "ef6c106c065ba04c5c664439db82e427", + ["namespaces.lua"] = "8dde7ef12282e586914454ac27cbec60", + ["spy.lua"] = "05651cdf35da46d7a2d726009cb90795", + ["state.lua"] = "1a6406c5e1c5fbb9419d0f07e9cf3303", + ["stub.lua"] = "fe69240533b4c18ad2005c2d894b7557", + ["util.lua"] = "d7080aebbbe64e6b444ddfec3214a449" + }, + plenary = { + _meta = { + ["_luassert.lua"] = "5a2ee66dd1a74016484a08b33d085b00" + }, + async = { + ["api.lua"] = "37d4c241eabadcea0c526c483899d863", + ["async.lua"] = "52a321d6ee2496aad0d3db8025f692da", + ["control.lua"] = "026788ddd2daa31db523b20186c7cdf1", + ["init.lua"] = "dfbe40648509c29e51b1bb8813fc8cc8", + ["lsp.lua"] = "d35d20ec07bf07737f60f74f2628ff20", + ["structs.lua"] = "812595922286653a4f68db51ffc6b8e7", + ["tests.lua"] = "fc7eac55ecf464f809c248bbfc1db78e", + ["util.lua"] = "640d8fa66bd1eea60d2fac0e4a9502e6", + ["uv_async.lua"] = "aebc4d8ef1c8829701102fd28b50d6a8" + }, + async_lib = { + ["api.lua"] = "3192d51dd33ee70098279d1f9f7867e3", + ["async.lua"] = "744a2f0155404e8fc519ab8832c849dc", + ["init.lua"] = "babd671bd31fa668aa0bb94b4c9e5513", + ["lsp.lua"] = "1496e6b8929cf24ae8d416e6b8597f75", + ["structs.lua"] = "a7a06f7fb2c6a89305a48ef123f4c87a", + ["tests.lua"] = "466252dcd6597d263b4eceb0dc688ec5", + ["util.lua"] = "97815a67faa9bd0594a4e7c585adf67f", + ["uv_async.lua"] = "d26c79ffe4f7b3cb6c623aede7bc161b" + }, + benchmark = { + ["init.lua"] = "b0c7321606a5b77a0f915de452320e56", + ["stat.lua"] = "9dfddd26f874e79bdee0773c57b08810" + }, + ["bit.lua"] = "c1242872a488007048a2fbe769906861", + ["busted.lua"] = "da38e9e5500907c0101450bf1aa37f11", + ["class.lua"] = "d0b258402d5839996aaeb75c4b507b0b", + collections = { + ["py_list.lua"] = "eb8403d14eef118e78f1c2c2e5ce3d3e" + }, + ["compat.lua"] = "0977cac3f6d6c5de404913e14d384d2a", + ["context_manager.lua"] = "3a59b45a12936d697355ab1638a14fe9", + ["curl.lua"] = "8ce22b22934008460cad0245f1b9f9c4", + ["debug_utils.lua"] = "9362a78d7f4d1b8bc836640de283353a", + ["enum.lua"] = "bfb8f89915f80658695f8dbada67dada", + ["errors.lua"] = "a39cf5fc1c6642bd3cf631ea5ec2a2d9", + ["filetype.lua"] = "be47ab2aa7acd41bea9be80323214fc0", + ["fun.lua"] = "713058172801d2427e9d670cbd8b0dca", + ["functional.lua"] = "be8ed5a614e1ad59540753774dea8ec8", + ["init.lua"] = "3814c65db80f62c61d854797b9c25691", + ["iterators.lua"] = "60c39e027360ba1f082c322f6f9a0289", + ["job.lua"] = "9cfe9a2101c620b6b0d2878445da3b63", + ["json.lua"] = "1989dffd7975806a8fd3cb1b3bb5243f", + ["log.lua"] = "2d2fccbc6ec8cfd60ba4d69d1926aad0", + lsp = { + ["override.lua"] = "424a207aeeeeb54d858e480b793d912e" + }, + neorocks = { + ["init.lua"] = "1117e199d3980495cebc90f6c1b5c1ac" + }, + ["nvim_meta.lua"] = "9292adc81b35a4188635d1bbdba1af16", + ["operators.lua"] = "dd0d8d59d77b2b406e32eb23d1d52b93", + ["path.lua"] = "67269bef8bc21dc1207346c4b527a9e8", + popup = { + ["init.lua"] = "a13e46b0000d419ab885611319c4dd90", + ["utils.lua"] = "ba82fd273233615598a5934c59ade438" + }, + profile = { + ["lua_profiler.lua"] = "7982caaef0ccc0393f9b41b3b5352de6", + ["memory_profiler.lua"] = "c4547eac8da2e3f0d1c8df479c2900d2", + ["p.lua"] = "b49065c5a37f70f1a09338f916b7b328" + }, + ["profile.lua"] = "6dc3e55c6ff395ef5b7b9045240101bb", + ["reload.lua"] = "aabfbb2dfae39e2f8aba43cbf90b082f", + ["run.lua"] = "d2f3478da4fb1ab8ab5179429b649e53", + ["scandir.lua"] = "150a9941d213ddff29429d7f843d1483", + ["strings.lua"] = "678bcda7a93920cf14456dc3ccd77d2f", + ["tbl.lua"] = "2947dc8678233f52c4345e212f002f9d", + ["test_harness.lua"] = "26374151c7e6c37ee481fa266fbf4242", + vararg = { + ["init.lua"] = "238160856408eee3a90b966f79c57780", + ["rotate.lua"] = "e591d58b1060f99e54867e74c13f9193" + }, + window = { + ["border.lua"] = "94370d22429202792f1c7c85b4d2aa6f", + ["float.lua"] = "29b0df43b37bb1ad2fe101f7b31af657", + ["init.lua"] = "21d83fd38843c078eadfe9d122610fd2" + } + }, + ["say.lua"] = "dd352934a1656fe97ae9c0422d2628a3" + }, + ["plenary.nvim-scm-1.rockspec"] = "dbec2339a4a299bfc46704015fec397f", + plugin = { + ["plenary.vim"] = "aa44419a41d4a055ec1d5287bad626f2" + } +} diff --git a/config/neovim/store/lazy-plugins/promise-async/.editorconfig b/config/neovim/store/lazy-plugins/promise-async/.editorconfig new file mode 100644 index 00000000..2e6221df --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/.editorconfig @@ -0,0 +1,23 @@ +[*] +indent_style = space +indent_size = 4 +tab_width = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true + +[*.lua] +align_continuous_assign_statement = false +space_around_table_field_list = false +align_call_args = false +quote_style = single + +[*.json,*.jsonc] +indent_style = tab + +[*.{yaml,yml}] +indent_style = space +indent_size = 2 + +[{Makefile,**.mk}] +indent_style = tab diff --git a/config/neovim/store/lazy-plugins/promise-async/.github/ISSUE_TEMPLATE/bug_report.yml b/config/neovim/store/lazy-plugins/promise-async/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..1d8ab7a2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,43 @@ +name: Bug Report +description: File a bug report +labels: [bug] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: input + attributes: + label: 'Version (lua -v) or (nvim -v | head -n1)' + placeholder: 'LuaJIT 2.1.0-beta3' + validations: + required: true + - type: input + attributes: + label: 'Operating system/version' + placeholder: 'ArchLinux' + validations: + required: true + - type: textarea + attributes: + label: 'How to reproduce the issue' + description: 'How do you trigger this bug? Please walk us through it step by step.' + value: | + 1. + 2. + 3. + ... + validations: + required: true + + - type: textarea + attributes: + label: 'Expected behavior' + description: 'Describe the behavior you expect. May include logs, images, or videos.' + validations: + required: true + - type: textarea + attributes: + label: 'Actual behavior' + validations: + required: true diff --git a/config/neovim/store/lazy-plugins/promise-async/.github/ISSUE_TEMPLATE/config.yml b/config/neovim/store/lazy-plugins/promise-async/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..3ba13e0c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/config/neovim/store/lazy-plugins/promise-async/.github/ISSUE_TEMPLATE/feature_request.yml b/config/neovim/store/lazy-plugins/promise-async/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..4cbdf42c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,23 @@ +name: Feature request +description: Request an enhancement for promise-async +labels: [enhancement] +body: + - type: markdown + attributes: + value: | + Before requesting: search [existing issues](https://github.com/kevinhwang91/promise-async/labels/enhancement). + - type: textarea + attributes: + label: "Feature description" + validations: + required: true + - type: textarea + attributes: + label: "Describe the solution you'd like" + validations: + required: true + - type: textarea + attributes: + label: "Additional context" + validations: + required: false diff --git a/config/neovim/store/lazy-plugins/promise-async/.github/workflows/lint.yml b/config/neovim/store/lazy-plugins/promise-async/.github/workflows/lint.yml new file mode 100644 index 00000000..64c1a14f --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/.github/workflows/lint.yml @@ -0,0 +1,30 @@ +name: Lint + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install lua-language-server + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd + gh release download -R sumneko/lua-language-server -p '*-linux-x64.tar.gz' -D lua-language-server + tar xzf lua-language-server/* -C lua-language-server + echo "${PWD}/lua-language-server/bin" >> $GITHUB_PATH + export PATH="${PWD}/lua-language-server/bin:${PATH}" + lua-language-server --version + + - name: Run Lint + run: make lint diff --git a/config/neovim/store/lazy-plugins/promise-async/.github/workflows/test.yml b/config/neovim/store/lazy-plugins/promise-async/.github/workflows/test.yml new file mode 100644 index 00000000..3b893ec0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/.github/workflows/test.yml @@ -0,0 +1,60 @@ +name: Test + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + strategy: + matrix: + include: + - { os: ubuntu-latest, target: nvim, version: stable } + - { os: ubuntu-latest, target: nvim, version: nightly } + - { os: macos-latest, target: nvim, version: stable } + - { os: macos-latest, target: nvim, version: nightly } + - { os: ubuntu-latest, target: lua, version: lua 5.1 } + - { os: ubuntu-latest, target: lua, version: lua 5.2 } + - { os: ubuntu-latest, target: lua, version: lua 5.3 } + - { os: ubuntu-latest, target: lua, version: lua 5.4 } + - { os: ubuntu-latest, target: lua, version: luajit 2.1.0-beta3 } + - { os: macos-latest, target: lua, version: lua 5.1 } + - { os: macos-latest, target: lua, version: lua 5.2 } + - { os: macos-latest, target: lua, version: lua 5.3 } + - { os: macos-latest, target: lua, version: lua 5.4 } + - { os: macos-latest, target: lua, version: luajit 2.1.0-beta3 } + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + + - if: matrix.target == 'nvim' && matrix.os == 'ubuntu-latest' + name: Install Neovim on Ubuntu + run: | + cd + curl -LO https://github.com/neovim/neovim/releases/download/${{ matrix.version }}/nvim-linux64.tar.gz + tar xzf nvim-linux64.tar.gz + echo "${PWD}/nvim-linux64/bin" >> $GITHUB_PATH + export PATH="${PWD}/nvim-linux64/bin:${PATH}" + nvim -v + - if: matrix.target == 'nvim' && matrix.os == 'macos-latest' + name: Install Neovim on Macos + run: | + cd + curl -LO https://github.com/neovim/neovim/releases/download/${{ matrix.version }}/nvim-macos.tar.gz + tar xzf nvim-macos.tar.gz + echo "${PWD}/nvim-macos/bin" >> $GITHUB_PATH + export PATH="${PWD}/nvim-macos/bin:${PATH}" + nvim -v + + - name: Run Test + run: | + if [[ ${{ matrix.target }} == lua ]]; then + export LUA_VERSION="${{ matrix.version }}" + fi + make test_${{ matrix.target }} diff --git a/config/neovim/store/lazy-plugins/promise-async/.gitignore b/config/neovim/store/lazy-plugins/promise-async/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/.gitignore @@ -0,0 +1 @@ +/build diff --git a/config/neovim/store/lazy-plugins/promise-async/.luarc.json b/config/neovim/store/lazy-plugins/promise-async/.luarc.json new file mode 100644 index 00000000..8243bfdc --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/.luarc.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", + "diagnostics.disable": [ + "duplicate-set-field" + ], + "diagnostics.globals": [ + "vim", + "jit", + "it", + "describe", + "before_each", + "after_each", + "spy", + "setup", + "teardown", + "done", + "wait" + ], + "diagnostics.severity": { + "undefined-field": "Hint" + }, + "runtime.version": "LuaJIT", + "type.castNumberToInteger": true, + "type.weakUnionCheck": true +} diff --git a/config/neovim/store/lazy-plugins/promise-async/LICENSE b/config/neovim/store/lazy-plugins/promise-async/LICENSE new file mode 100644 index 00000000..c3c56e57 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2022-2023, kevinhwang91 +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/config/neovim/store/lazy-plugins/promise-async/Makefile b/config/neovim/store/lazy-plugins/promise-async/Makefile new file mode 100644 index 00000000..6ea0a0fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/Makefile @@ -0,0 +1,74 @@ +SHELL := /bin/bash +DEPS ?= build + +LUA_VERSION ?= luajit 2.1.0-beta3 +NVIM_BIN ?= nvim +NVIM_LUA_VERSION := $(shell $(NVIM_BIN) -v 2>/dev/null | grep -E '^Lua(JIT)?' | tr A-Z a-z) +ifdef NVIM_LUA_VERSION +LUA_VERSION ?= $(NVIM_LUA_VERSION) +endif +LUA_NUMBER := $(word 2,$(LUA_VERSION)) + +TARGET_DIR := $(DEPS)/$(LUA_NUMBER) + +HEREROCKS ?= $(DEPS)/hererocks.py +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) +HEREROCKS_ENV ?= MACOSX_DEPLOYMENT_TARGET=10.15 +endif +HEREROCKS_URL ?= https://raw.githubusercontent.com/luarocks/hererocks/master/hererocks.py +HEREROCKS_ACTIVE := source $(TARGET_DIR)/bin/activate + +LUAROCKS ?= $(TARGET_DIR)/bin/luarocks + +BUSTED ?= $(TARGET_DIR)/bin/busted +BUSTED_HELPER ?= $(PWD)/spec/fixtures.lua + +LUV ?= $(TARGET_DIR)/lib/lua/$(LUA_NUMBER)/luv.so + +LUA_LS ?= $(DEPS)/lua-language-server +LINT_LEVEL ?= Information + +all: deps + +deps: | $(HEREROCKS) $(BUSTED) + +test: test_lua test_nvim + +test_lua: $(BUSTED) $(LUV) + @echo Test with $(LUA_VERSION) ...... + @$(HEREROCKS_ACTIVE) && eval $$(luarocks path) && \ + lua spec/init.lua --helper=$(BUSTED_HELPER) $(BUSTED_ARGS) + +ifdef NVIM_LUA_VERSION +test_nvim: $(BUSTED) + @echo Test with Neovim ...... + @$(HEREROCKS_ACTIVE) && eval $$(luarocks path) && \ + $(NVIM_BIN) --clean -n --headless -u spec/init.lua -- \ + --helper=$(BUSTED_HELPER) $(BUSTED_ARGS) +endif + +$(HEREROCKS): + mkdir -p $(DEPS) + curl $(HEREROCKS_URL) -o $@ + +$(LUAROCKS): $(HEREROCKS) + $(HEREROCKS_ENV) python $< $(TARGET_DIR) --$(LUA_VERSION) -r latest + +$(BUSTED): $(LUAROCKS) + $(HEREROCKS_ACTIVE) && luarocks install busted + +$(LUV): $(LUAROCKS) + @$(HEREROCKS_ACTIVE) && [[ ! $$(luarocks which luv) ]] && \ + luarocks install luv || true + +lint: + @rm -rf $(LUA_LS) + @mkdir -p $(LUA_LS) + @lua-language-server --check $(PWD) --checklevel=$(LINT_LEVEL) --logpath=$(LUA_LS) + @[[ -f $(LUA_LS)/check.json ]] && { cat $(LUA_LS)/check.json 2>/dev/null; exit 1; } || true + +clean: + rm -rf $(DEPS) + +.PHONY: all deps clean lint test test_nvim test_lua diff --git a/config/neovim/store/lazy-plugins/promise-async/README.md b/config/neovim/store/lazy-plugins/promise-async/README.md new file mode 100644 index 00000000..1069f328 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/README.md @@ -0,0 +1,191 @@ +# promise-async + +![GitHub Test](https://github.com/kevinhwang91/promise-async/workflows/Test/badge.svg) +![GitHub Lint](https://github.com/kevinhwang91/promise-async/workflows/Lint/badge.svg) + +The goal of promise-async is to port [Promise][promise] & [Async][async] from JavaScript to Lua. + +> A value returned by async function in JavaScript is actually a Promise Object. It's incomplete and +> inflexible for using an async function wrapped by bare coroutine without Promise in almost Lua +> implementation. + +- [Features](#features) +- [Demonstration](#demonstration) + - [Script](#script) + - [demo.lua](#demo.lua) + - [demo.js](#demo.js) +- [Quickstart](#quickstart) + - [Requirements](#requirements) + - [Installation](#installation) + - [As a plugin for Neovim platform](#as-a-plugin-for-neovim-platform) + - [As a library from Luarocks](#as-a-library-from-luarocks) +- [Documentation](#documentation) + - [Summary](#summary) + - [async](#async) +- [Development](#development) + - [Neovim tips](#neovim-tips) + - [Run tests](#run-tests) + - [Improve completion experience](#improve-completion-experience) + - [Customize EventLoop](#customize-eventloop) +- [Credit](#credit) +- [Feedback](#feedback) +- [License](#license) + +## Features + +- API is similar to JavaScript's +- Customize EventLoop in any platforms +- Support Lua 5.1-5.4 and LuaJIT with an EventLoop module +- Support Neovim platform + +## Demonstration + + + +### Script + +#### demo.lua + + + +#### demo.js + + + +## Quickstart + +### Requirements + +- Lua 5.1 or latter +- [Luv](https://github.com/luvit/luv) + +> Luv is a default EventLoop for promise-async. It doesn't mean promise-async must require it. In +> fact, promise-async require a general EventLoop module which Luv like. + +### Installation + +#### As a plugin for Neovim platform + +Install with [Packer.nvim](https://github.com/wbthomason/packer.nvim): + +- As a normal plugin + +```lua +use {'kevinhwang91/promise-async'} +``` + +or + +- As a Luarocks plugin + +```lua +use_rocks {'promise-async'} +``` + +#### As a library from Luarocks + +1. `luarocks install promise-async` +2. `luarocks install luv` or implement an EventLoop + [interface](https://github.com/kevinhwang91/promise-async/blob/main/typings/loop.lua) to adapt + your platform + +## Documentation + +promise-async's API is based on [MDN-Promise][promise]. [typings/promise.lua](typings/promise.lua) +is the typings with documentation of Promise class. + +### Summary + +Summary up the API different from JavaScript. + + + +| JavaScript | Lua | +| --------------------------------------------------- | ----------------------------------------------- | +| `new Promise` | `Promise:new`/`Promise` | +| `Promise.then` | `Promise:thenCall`, `then` is language keyword | +| `Promise.catch` | `Promise:catch` | +| `Promise.finally` | `Promise:finally` | +| `Promise.resolve` | `Promise.resolve` | +| `Promise.reject` | `Promise.reject` | +| `Promise.all`: `Symbol.iterator` as iterator | `Promise.all`: `pairs` as iterator | +| `Promise.allSettled`: `Symbol.iterator` as iterator | `Promise.allSettled`: `pairs` as iterator | +| `Promise.any`: `Symbol.iterator` as iterator | `Promise.any`: `pairs` as iterator | +| `Promise.race`: `Symbol.iterator` as iterator | `Promise.race`: `pairs` as iterator | +| `async`: as keyword at the start of a function | `Async`/`Async.sync`: as a surrounding function | +| `await`: as keyword | `await`/`Async.wait` as a function | + + + +### async + +The environment in `Async.sync` function have been injected some new functions for compatibility or +enhancement: + +1. `await`: A reference of `Async.wait` function; +2. `pcall`: Be compatible with LuaJIT; +3. `xpcall`: Be compatible with LuaJIT; + +`async` in JavaScript return Promise object only with single result, but may carry multiple results +in Lua. The resolved result of Promise object return by `async` function will be packed into a table +via `{...}`. However, the result handled by `await` will be unpacked and return multiple values. + +```lua +local async = require('async') + +local function f() + return 1, 2, 3 +end + +-- multiple results are packed into resolved result in Promise +async(f):thenCall(function(v) + print(v[1], v[2], v[3]) -- output: 1 2 3 +end) + +-- results returned by `await` +async(function() + local v1, v2, v3 = await(async(f)) + print(v1, v2, v3) -- output: 1 2 3 +end) + +uv.run() +``` + +## Development + +### Neovim tips + +- `Promise.resolve():thenCall(cb)` is almost equivalent to `vim.schedule(cb)`. + +### Run tests + +`make test` + +### Improve completion experience + +Following [typings/README.md](./typings/README.md) + +### Customize EventLoop + +TODO, refer to [loop.lua](./lua/promise-async/loop.lua) + +## Credit + +- [Promise][promise] +- [Async][async] +- [promises-tests](https://github.com/promises-aplus/promises-tests) +- [then/promise](https://github.com/then/promise) +- [promisejs.org](https://www.promisejs.org) +- [event-loop-timers-and-nexttick](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick) + +[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise +[async]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function + +## Feedback + +- If you get an issue or come up with an awesome idea, don't hesitate to open an issue in github. +- If you think this plugin is useful or cool, consider rewarding it a star. + +## License + +The project is licensed under a BSD-3-clause license. See [LICENSE](./LICENSE) file for details. diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/README.md b/config/neovim/store/lazy-plugins/promise-async/examples/README.md new file mode 100644 index 00000000..e7ec0abf --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/README.md @@ -0,0 +1,6 @@ +# Examples + +Make sure that modules can be found under the `package.path` and `package.cpath`. + +If can't find modules, please run `export LUA_PATH="$(dirname $PWD)/lua/?.lua;;"` under CWD in shell +or install the modules except for this project. diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/coc_nvim.lua b/config/neovim/store/lazy-plugins/promise-async/examples/coc_nvim.lua new file mode 100644 index 00000000..398de75f --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/coc_nvim.lua @@ -0,0 +1,29 @@ +local promise = require('promise') +local M = {} +local fn = vim.fn + +function M.action(action, ...) + local args = {...} + return promise(function(resolve, reject) + table.insert(args, function(err, res) + if err ~= vim.NIL then + reject(err) + else + if res == vim.NIL then + res = nil + end + resolve(res) + end + end) + fn.CocActionAsync(action, unpack(args)) + end) +end + +function M.runCommand(name, ...) + return M.action('runCommand', name, ...) +end + +-- +-- M.action('showOutline', true) + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/demo.js b/config/neovim/store/lazy-plugins/promise-async/examples/demo.js new file mode 100644 index 00000000..10bb0121 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/demo.js @@ -0,0 +1,58 @@ +async function defuse(ms) { + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve(ms) + }, ms) + }) +} + +async function bomb(ms) { + return new Promise((resolve, reject) => { + setTimeout(() => { + reject(ms) + }, ms) + }) +} + +async function race() { + return Promise.race([ + defuse(500 + Math.ceil(Math.random() * 500)), + bomb(800 + Math.ceil(Math.random() * 200)), + ]) +} + +async function play() { + console.info('Game start!') + let cnt = 0 + try { + while (true) { + let ms = await race() + cnt = cnt + ms + console.info(`Defuse after ${ms}ms~`) + } + } catch (msErr) { + cnt = cnt + msErr + console.info(`Bomb after ${msErr}ms~`) + } + + console.info(`Game end after ${cnt}ms!`) + + await { + then: function(resolve, reject) { + setTimeout(() => { + reject(this.message) + }, 1000) + }, + message: 'try to throw an error :)' + } +} + +Promise.resolve().then((value) => { + console.info('In next tick') +}) + +console.info('In main') + +play().finally(() => { + console.info('Before throwing UnhandledPromiseRejection on finally!') +}) diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/demo.lua b/config/neovim/store/lazy-plugins/promise-async/examples/demo.lua new file mode 100644 index 00000000..2a7db3c5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/demo.lua @@ -0,0 +1,87 @@ +---@diagnostic disable: unused-local +local uv = require('luv') +local async = require('async') +local promise = require('promise') + +math.randomseed(math.ceil(uv.uptime())) + +local function setTimeout(callback, ms) + local timer = uv.new_timer() + timer:start(ms, 0, function() + timer:close() + callback() + end) + return timer +end + +local function defuse(ms) + return promise:new(function(resolve, reject) + setTimeout(function() + resolve(ms) + end, ms) + end) +end + +local function bomb(ms) + -- getmetatable(promise).__call = promise.new + return promise(function(resolve, reject) + setTimeout(function() + reject(ms) + end, ms) + end) +end + +local function race() + return async(function() + return promise.race({ + defuse(math.random(500, 1000)), + bomb(math.random(800, 1000)) + }) + end) +end + +local notify = vim and vim.notify or print + +local function play() + return async(function() + -- We are not in the next tick until first `await` is called. + notify('Game start!') + local cnt = 0 + xpcall(function() + while true do + local ms = await(race()) + cnt = cnt + ms + notify(('Defuse after %dms~'):format(ms)) + end + end, function(msErr) + cnt = cnt + msErr + notify(('Bomb after %dms~'):format(msErr)) + end) + + notify(('Game end after %dms!'):format(cnt)) + + await { + thenCall = function(self, resolve, reject) + setTimeout(function() + reject(self.message) + end, 1000) + end, + message = 'try to throw an error :)' + } + end) +end + +promise.resolve():thenCall(function(value) + notify('In next tick') +end) + +notify('In main') + +play():finally(function() + print('Before throwing UnhandledPromiseRejection on finally!') +end) + +-- uv.run will be called automatically under Neovim main loop +if not vim then + uv.run() +end diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/multiple_threads.lua b/config/neovim/store/lazy-plugins/promise-async/examples/multiple_threads.lua new file mode 100644 index 00000000..6e087081 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/multiple_threads.lua @@ -0,0 +1,47 @@ +---@diagnostic disable: redefined-local +local uv = require('luv') +local promise = require('promise') +local mpack = require('mpack') + +local asyncHandle +local thread +promise(function(resolve, reject) + asyncHandle = uv.new_async(function(err, data) + asyncHandle:close() + if err then + reject((mpack.unpack or mpack.decode)(err)) + else + resolve((mpack.unpack or mpack.decode)(data)) + end + end) +end):thenCall(function(value) + print(('Getting resolved value: %s from %s'):format(value[1], thread)) +end, function(reason) + print(('Getting rejected reason: %s from %s'):format(reason[1], thread)) +end) + +thread = uv.new_thread(function(delay, asyn) + local uv = require('luv') + local mpack = require('mpack') + local promise = require('promise') + math.randomseed(math.ceil(uv.uptime())) + promise(function(resolve, reject) + print(tostring(uv.thread_self()) .. ' is running.') + promise.loop.setTimeout(function() + if math.random(1, 2) == 1 then + resolve({'succeeded'}) + else + reject({'failed'}) + end + end, delay) + end):thenCall(function(value) + uv.async_send(asyn, nil, (mpack.pack or mpack.encode)(value)) + end):catch(function(reason) + uv.async_send(asyn, (mpack.pack or mpack.encode)(reason)) + end) + uv.run() +end, 1000, asyncHandle) + +if not vim then + uv.run() +end diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/read_file.lua b/config/neovim/store/lazy-plugins/promise-async/examples/read_file.lua new file mode 100644 index 00000000..5bf8c82e --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/read_file.lua @@ -0,0 +1,23 @@ +local uv = require('luv') +local uva = require('uva') +local async = require('async') + +local function readFile(path) + return async(function() + local fd = await(uva.open(path, 'r', 438)) + local stat = await(uva.fstat(fd)) + local data = await(uva.read(fd, stat.size, 0)) + await(uva.close(fd)) + return data + end) +end + +local currentPath = debug.getinfo(1, 'S').source:sub(2) +print('Reading ' .. currentPath .. '......\n') +readFile(currentPath):thenCall(function(value) + print(value) +end) + +if not vim then + uv.run() +end diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/uva.lua b/config/neovim/store/lazy-plugins/promise-async/examples/uva.lua new file mode 100644 index 00000000..569f4fa6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/uva.lua @@ -0,0 +1,74 @@ +---@class UvFS +local M = {} + +local uv = require('luv') +local promise = require('promise') +local compat = require('promise-async.compat') + +local function wrap(name, argc) + return function(...) + local argv = {...} + return promise(function(resolve, reject) + argv[argc] = function(err, data) + if err then + reject(err) + else + resolve(data) + end + end + uv[name](compat.unpack(argv)) + end) + end +end + +M.close = wrap('fs_close', 2) +M.open = wrap('fs_open', 4) +M.read = wrap('fs_read', 4) +M.unlink = wrap('fs_unlink', 2) +M.write = wrap('fs_write', 4) +M.mkdir = wrap('fs_mkdir', 3) +M.mkdtemp = wrap('fs_mkdtemp', 2) +M.mkstemp = wrap('fs_mkstemp', 2) +M.rmdir = wrap('fs_rmdir', 2) +M.scandir = wrap('fs_scandir', 2) +M.stat = wrap('fs_stat', 2) +M.fstat = wrap('fs_fstat', 2) +M.lstat = wrap('fs_lstat', 2) +M.rename = wrap('fs_rename', 3) +M.fsync = wrap('fs_fsync', 2) +M.fdatasync = wrap('fs_fdatasync', 2) +M.ftruncate = wrap('fs_ftruncate', 3) +M.sendfile = wrap('fs_sendfile', 5) +M.access = wrap('fs_access', 3) +M.chmod = wrap('fs_chmod', 3) +M.fchmod = wrap('fs_fchmod', 3) +M.utime = wrap('fs_utime', 4) +M.futime = wrap('fs_futime', 4) +M.lutime = wrap('fs_lutime', 4) +M.link = wrap('fs_link', 3) +M.symlink = wrap('fs_symlink', 4) +M.readlink = wrap('fs_readlink', 2) +M.realpath = wrap('fs_realpath', 2) +M.chown = wrap('fs_chown', 4) +M.fchown = wrap('fs_fchown', 4) +M.lchown = wrap('fs_lchown', 4) +M.copyfile = wrap('fs_copyfile', 4) + +-- TODO +M.opendir = function(path, entries) + return promise(function(resolve, reject) + uv.fs_opendir(path, function(err, data) + if err then + reject(err) + else + resolve(data) + end + end, entries) + end) +end + +M.readdir = wrap('fs_readdir', 2) +M.closedir = wrap('fs_closedir', 2) +M.statfs = wrap('fs_statfs', 2) + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/examples/write_file.lua b/config/neovim/store/lazy-plugins/promise-async/examples/write_file.lua new file mode 100644 index 00000000..52edc7d9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/examples/write_file.lua @@ -0,0 +1,21 @@ +local uv = require('luv') +local uva = require('uva') +local async = require('async') + +local function writeFile(path, data) + return async(function() + local path_ = path .. '_' + local fd = await(uva.open(path_, 'w', 438)) + await(uva.write(fd, data, -1)) + await(uva.close(fd)) + pcall(await, uva.rename(path_, path)) + end) +end + +local path = debug.getinfo(1, 'S').source:sub(2) .. '__' +print('Writing ' .. path .. '......\n') +writeFile(path, 'write some texts :)\n') + +if not vim then + uv.run() +end diff --git a/config/neovim/store/lazy-plugins/promise-async/lua/async.lua b/config/neovim/store/lazy-plugins/promise-async/lua/async.lua new file mode 100644 index 00000000..82d0eea8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/lua/async.lua @@ -0,0 +1,98 @@ +local promise = require('promise') +local utils = require('promise-async.utils') +local compat = require('promise-async.compat') + +local asyncId = {'promise-async'} + +---@class Async +local Async = setmetatable({_id = asyncId}, { + __call = function(self, executor) + return self.sync(executor) + end +}) + +local packedId = {} + +local Packed = {_id = packedId} +Packed.__index = Packed + +local function wrapPacked(packed) + return setmetatable(packed, Packed) +end + +local function hasPacked(o) + return type(o) == 'table' and o._id == packedId +end + +local function injectENV(fn) + compat.setfenv(fn, setmetatable({ + await = Async.wait, + pcall = compat.pcall, + xpcall = compat.xpcall + }, { + __index = compat.getfenv(fn) + })) +end + +function Async.sync(executor) + local typ = type(executor) + local isCallable, fn = utils.getCallable(executor, typ) + assert(isCallable, 'a callable table or function expected, got ' .. typ) + injectENV(fn) + return promise:new(function(resolve, reject) + local co = coroutine.create(typ == 'function' and executor or function() + return executor() + end) + + local function afterResume(status, ...) + if not status then + local reason = select(1, ...) + reject(debug.traceback(co, reason)) + return + elseif coroutine.status(co) == 'dead' then + local value + local n = select('#', ...) + if n == 1 then + value = select(1, ...) + elseif n > 1 then + value = wrapPacked({...}) + end + resolve(value) + return + end + local p = select(1, ...) + return p + end + + local function next(err, res) + local p = afterResume(coroutine.resume(co, err, res)) + if p then + p:thenCall(function(value) + next(false, value) + end, function(reason) + next(true, reason) + end) + end + end + + next() + end) +end + +---Export wait function to someone needs, wait function actually have injected as `await` into +---the executor of async function +---@param p Promise|any +---@return ... +function Async.wait(p) + p = promise.resolve(p) + local err, res = coroutine.yield(p) + if err then + error(res, 0) + elseif hasPacked(res) then + return compat.unpack(res) + else + return res + end +end + +return Async diff --git a/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/compat.lua b/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/compat.lua new file mode 100644 index 00000000..01c9d549 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/compat.lua @@ -0,0 +1,131 @@ +---Functions are compatible with LuaJIT's. +---@class PromiseAsyncCompat +local M = {} + +---@return boolean +function M.is51() + return _G._VERSION:sub(-3) == '5.1' and not jit +end + +if table.pack then + M.pack = table.pack +else + M.pack = function(...) + return {n = select('#', ...), ...} + end +end + +if table.unpack then + M.unpack = table.unpack +else + M.unpack = unpack +end +---@diagnostic enable: deprecated + +if M.is51() then + local _pcall, _xpcall = pcall, xpcall + local utils = require('promise-async.utils') + + local function yieldInCoroutine(thread, co, success, ...) + if coroutine.status(co) == 'suspended' then + return yieldInCoroutine(thread, co, coroutine.resume(co, coroutine.yield(...))) + end + return success, ... + end + + local function doPcall(thread, f, ...) + local typ = type(f) + local ok, fn = utils.getCallable(f, typ) + if not ok then + return false, ('attempt to call a %s value'):format(typ) + end + local co = coroutine.create(function(...) + return fn(...) + end) + return yieldInCoroutine(thread, co, coroutine.resume(co, ...)) + end + + M.pcall = function(f, ...) + local thread = coroutine.running() + if not thread then + return _pcall(f, ...) + end + return doPcall(thread, f, ...) + end + + local function xpcallCatch(msgh, success, ...) + if success then + return true, ... + end + local ok, result = _pcall(msgh, ...) + return false, ok and result or 'error in error handling' + end + + M.xpcall = function(f, msgh, ...) + local thread = coroutine.running() + if not thread then + return _xpcall(f, msgh, ...) + end + return xpcallCatch(msgh, doPcall(thread, f, ...)) + end +else + M.pcall = pcall + M.xpcall = xpcall +end + +if setfenv then + M.setfenv = setfenv + M.getfenv = getfenv +else + local function findENV(f) + local name = '' + local value + local up = 1 + while name do + name, value = debug.getupvalue(f, up) + if name == '_ENV' then + return up, value + end + up = up + 1; + end + return 0 + end + + local function envHelper(f, name) + if type(f) == 'number' then + if f < 0 then + error(([[bad argument #1 to '%s' (level must be non-negative)]]):format(name), 3) + end + local ok, dInfo = pcall(debug.getinfo, f + 2, 'f') + if not ok or not dInfo then + error(([[bad argument #1 to '%s' (invalid level)]]):format(name), 3) + end + f = dInfo.func + elseif type(f) ~= 'function' then + error(([[bad argument #1 to '%s' (number expected, got %s)]]):format(name, type(f)), 3) + end + return f + end + + function M.setfenv(f, table) + f = envHelper(f, 'setfenv') + local up = findENV(f) + if up > 0 then + debug.upvaluejoin(f, up, function() + return table + end, 1) + end + return f + end + + function M.getfenv(f) + if f == 0 or f == nil then + return _G + end + f = envHelper(f, 'getfenv') + local up, value = findENV(f) + return up > 0 and value or _G + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/error.lua b/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/error.lua new file mode 100644 index 00000000..d2c463e3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/error.lua @@ -0,0 +1,115 @@ +local errorId = {} + +---@class PromiseAsyncError +---@field err any +---@field queue string[] +---@field index number +local Error = {_id = errorId} +Error.__index = Error + +local function dump(o, limit) + local s + if type(o) ~= 'table' then + s = tostring(o) + else + local meta = getmetatable(o) + if meta and meta.__tostring then + s = tostring(o) + else + if limit > 0 then + local fmt = '%s [%s] = %s,' + s = '{' + for k, v in pairs(o) do + if type(k) ~= 'number' then + k = '"' .. k .. '"' + end + s = fmt:format(s, k, dump(v, limit - 1)) + end + s = s:sub(1, #s - 1) .. ' }' + else + s = '{...}' + end + end + end + return s +end + +function Error.isInstance(o) + return type(o) == 'table' and o._id == errorId +end + +---@param thread? thread +---@param level number +---@param skipShortSrc? string +---@return string? +function Error.format(thread, level, skipShortSrc) + local res + local dInfo = thread and debug.getinfo(thread, level, 'nSl') or debug.getinfo(level, 'nSl') + if dInfo then + local name, shortSrc, currentline = dInfo.name, dInfo.short_src, dInfo.currentline + if skipShortSrc == shortSrc then + return + end + local detail + if not name or name == '' then + detail = ('in function '):format(dInfo.linedefined) + else + detail = ([[in function '%s']]):format(name) + end + res = (' %s:%d: %s'):format(shortSrc, currentline, detail) + end + return res +end + +---@param err any +---@return PromiseAsyncError +function Error.new(err) + local o = setmetatable({}, Error) + o.err = err + o.queue = {} + o.index = 0 + return o +end + +function Error:__tostring() + local errMsg = dump(self.err, 1) + if #self.queue == 0 then + return errMsg + end + local t = {} + for i = 1, self.index do + table.insert(t, self.queue[i]) + end + table.insert(t, errMsg) + if self.index < #self.queue then + table.insert(t, 'stack traceback:') + end + for i = self.index + 1, #self.queue do + table.insert(t, self.queue[i]) + end + return table.concat(t, '\n') +end + +---@param value string +function Error:unshift(value) + if value then + self.index = self.index + 1 + table.insert(self.queue, 1, value) + end + return #self.queue +end + +---@param value? string +function Error:push(value) + if value then + table.insert(self.queue, value) + end + return #self.queue +end + +---@return any +function Error:peek() + return self.err +end + +return Error diff --git a/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/loop.lua b/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/loop.lua new file mode 100644 index 00000000..6201995b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/loop.lua @@ -0,0 +1,85 @@ +local uv = require('luv') + +---@class PromiseAsyncLoop +---@field tick userdata +---@field tickCallbacks function[] +---@field tickStarted boolean +---@field idle userdata +---@field idleCallbacks function[] +---@field idleStarted boolean +local EventLoop = { + tick = uv.new_timer(), + tickCallbacks = {}, + tickStarted = false, + idle = uv.new_idle(), + idleCallbacks = {}, + idleStarted = false +} + +function EventLoop.setTimeout(callback, ms) + local timer = uv.new_timer() + timer:start(ms, 0, function() + timer:close() + EventLoop.callWrapper(callback) + end) + return timer +end + +local function runTick() + EventLoop.tickStarted = true + local callbacks = EventLoop.tickCallbacks + EventLoop.tickCallbacks = {} + for _, cb in ipairs(callbacks) do + EventLoop.callWrapper(cb) + end + if #EventLoop.tickCallbacks > 0 then + EventLoop.tick:start(0, 0, runTick) + else + EventLoop.tickStarted = false + end +end + +function EventLoop.nextTick(callback) + table.insert(EventLoop.tickCallbacks, callback) + if not EventLoop.tickStarted then + EventLoop.tick:start(0, 0, runTick) + end +end + +local function runIdle() + EventLoop.idleStarted = true + local callbacks = EventLoop.idleCallbacks + EventLoop.idleCallbacks = {} + for _, cb in ipairs(callbacks) do + EventLoop.callWrapper(cb) + end + if #EventLoop.idleCallbacks > 0 then + EventLoop.idle:start(runIdle) + else + EventLoop.idle:stop() + EventLoop.idleStarted = false + end +end + +function EventLoop.nextIdle(callback) + EventLoop.nextTick(function() + table.insert(EventLoop.idleCallbacks, callback) + if not EventLoop.idleStarted then + EventLoop.idle:start(runIdle) + end + end) +end + +if vim and type(vim.schedule) == 'function' then + EventLoop.callWrapper = vim.schedule +else + function EventLoop.callWrapper(fn) + local ok, res = pcall(fn) + if not ok then + -- luv can't handle object with __tostring filed + error(tostring(res)) + end + end +end + +return EventLoop diff --git a/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/utils.lua b/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/utils.lua new file mode 100644 index 00000000..2a378bbf --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/lua/promise-async/utils.lua @@ -0,0 +1,33 @@ +---@class PromiseAsyncUtils +local M = {} + +---@param o any +---@param expectedType string +function M.assertType(o, expectedType) + local gotType = type(o) + local fmt = '%s expected, got %s' + return assert(gotType == expectedType, fmt:format(expectedType, gotType)) +end + +---@param o any +---@param typ? string +---@return boolean, function|table|any +function M.getCallable(o, typ) + local ok + local f + local t = typ or type(o) + if t == 'function' then + ok, f = true, o + elseif t ~= 'table' then + ok, f = false, o + else + local meta = getmetatable(o) + ok = meta and type(meta.__call) == 'function' + if ok then + f = meta.__call + end + end + return ok, f +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/lua/promise.lua b/config/neovim/store/lazy-plugins/promise-async/lua/promise.lua new file mode 100644 index 00000000..531503e7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/lua/promise.lua @@ -0,0 +1,410 @@ +local utils = require('promise-async.utils') +local promiseId = {'promise-async'} +local errFactory = require('promise-async.error') +local shortSrc = debug.getinfo(1, 'S').short_src + +---@alias PromiseState +---| 1 #PENDING +---| 2 #FULFILLED +---| 3 #REJECTED +local PENDING = 1 +local FULFILLED = 2 +local REJECTED = 3 + +-- +---@class Promise +---@field state PromiseState +---@field result any +---@field queue table +---@field needHandleRejection? boolean +---@field err? PromiseAsyncError +local Promise = setmetatable({_id = promiseId}, { + __call = function(self, executor) + return self:new(executor) + end +}) +Promise.__index = Promise + +local function loadEventLoop() + local success, res = pcall(require, 'promise-async.loop') + assert(success, 'Promise need an EventLoop, ' .. + 'luv module or a customized EventLoop module is expected.') + return res +end + +if vim then + -- `require` in Neovim is hacked by its get_runtime API, may throw an error while calling + -- `require` in libuv, require at once as a workaround. + Promise.loop = require('promise-async.loop') +else + Promise.loop = setmetatable({}, { + __index = function(_, key) + local loop = loadEventLoop() + rawset(Promise, 'loop', loop) + return loop[key] + end, + __newindex = function(_, key, value) + local loop = loadEventLoop() + rawset(Promise, 'loop', loop) + Promise.loop[key] = value + end + }) +end + + +function Promise:__tostring() + local state = self.state + if state == PENDING then + return 'Promise { }' + elseif state == REJECTED then + return ('Promise { %s }'):format(tostring(self.result)) + else + return ('Promise { %s }'):format(tostring(self.result)) + end +end + +local function noop() end + +---@param o any +---@param typ? string +---@return boolean +function Promise.isInstance(o, typ) + return (typ or type(o)) == 'table' and o._id == promiseId +end + +---must get `thenCall` field from `o` at one time, can't call repeatedly. +---@param o any +---@param typ? type +---@return function? +local function getThenable(o, typ) + local thenCall + if (typ or type(o)) == 'table' then + thenCall = o.thenCall + if type(thenCall) ~= 'function' then + thenCall = nil + end + end + return thenCall +end + +---@param err any +---@return PromiseAsyncError +local function buildError(err) + local o = errFactory.new(err) + local level = 4 + local value + local thread = coroutine.running() + repeat + value = errFactory.format(thread, level, shortSrc) + level = level + 1 + o:push(value) + until not value + table.remove(o.queue) + return o +end + +local resolvePromise, rejectPromise + +---@param promise Promise +local function handleQueue(promise) + local queue = promise.queue + if #queue == 0 then + return + end + if promise.needHandleRejection and #queue > 0 then + promise.needHandleRejection = nil + end + promise.queue = {} + + Promise.loop.nextTick(function() + local state, result = promise.state, promise.result + for _, q in ipairs(queue) do + local newPromise, onFulfilled, onRejected = q[1], q[2], q[3] + local func + if state == FULFILLED then + if utils.getCallable(onFulfilled) then + func = onFulfilled + else + resolvePromise(newPromise, result) + end + elseif state == REJECTED then + if utils.getCallable(onRejected) then + func = onRejected + else + rejectPromise(newPromise, result) + end + end + if func then + local ok, res = xpcall(function() + return func(result) + end, function(errmsg) + if type(errmsg) == 'string' then + newPromise.err = buildError(errmsg) + end + return errmsg + end) + if ok then + resolvePromise(newPromise, res) + else + rejectPromise(newPromise, res) + end + end + end + end) +end + +---@param promise Promise +---@param result any +---@param state PromiseState +local function transition(promise, result, state) + if promise.state ~= PENDING then + return + end + promise.result = result + promise.state = state + handleQueue(promise) +end + +---@param promise Promise +---@param executor PromiseExecutor +---@param self? table +local function wrapExecutor(promise, executor, self) + local called = false + local resolve = function(value) + if called then + return + end + resolvePromise(promise, value) + called = true + end + local reject = function(reason) + if called then + return + end + rejectPromise(promise, reason) + called = true + end + + local ok, res = xpcall(function() + if self then + ---@diagnostic disable-next-line: redundant-parameter, param-type-mismatch + return executor(self, resolve, reject) + else + return executor(resolve, reject) + end + end, function(errmsg) + if type(errmsg) == 'string' then + promise.err = buildError(errmsg) + end + return errmsg + end) + if not ok and not called then + reject(res) + end +end + +---@param promise Promise +local function handleRejection(promise) + promise.needHandleRejection = true + + Promise.loop.nextIdle(function() + if promise.needHandleRejection then + promise.needHandleRejection = nil + local err = promise.err + if not err then + err = errFactory.new(promise.result) + end + err:unshift('UnhandledPromiseRejection with the reason:') + error(err) + end + end) +end + +---@param promise Promise +---@param reason any +rejectPromise = function(promise, reason) + handleRejection(promise) + transition(promise, reason, REJECTED) +end + +---@param promise Promise +---@param value any +resolvePromise = function(promise, value) + if promise == value then + local reason = debug.traceback('TypeError: Chaining cycle detected for promise') + rejectPromise(promise, reason) + return + end + + local valueType = type(value) + if Promise.isInstance(value, valueType) then + value:thenCall(function(val) + resolvePromise(promise, val) + end, function(reason) + rejectPromise(promise, reason) + end) + else + local thenCall = getThenable(value, valueType) + if thenCall then + wrapExecutor(promise, thenCall, value) + else + transition(promise, value, FULFILLED) + end + end +end + +function Promise:new(executor) + utils.assertType(executor, 'function') + local o = self == Promise and setmetatable({}, self) or self + o.state = PENDING + o.result = nil + o.queue = {} + o.needHandleRejection = nil + + if executor ~= noop then + wrapExecutor(o, executor) + end + return o +end + +function Promise:thenCall(onFulfilled, onRejected) + local o = self.new(Promise, noop) + table.insert(self.queue, {o, onFulfilled, onRejected}) + if self.state ~= PENDING then + handleQueue(self) + end + return o +end + +function Promise:catch(onRejected) + return self:thenCall(nil, onRejected) +end + +function Promise:finally(onFinally) + local function wrapFinally() + if utils.getCallable(onFinally) then + ---@diagnostic disable-next-line: need-check-nil + onFinally() + end + end + + return self:thenCall(function(value) + wrapFinally() + return value + end, function(reason) + wrapFinally() + return Promise.reject(reason) + end) +end + +function Promise.resolve(value) + local typ = type(value) + if Promise.isInstance(value, typ) then + return value + else + local o = Promise:new(noop) + local thenCall = getThenable(value, typ) + if thenCall then + wrapExecutor(o, thenCall, value) + else + o.state = FULFILLED + o.result = value + end + return o + end +end + +function Promise.reject(reason) + local o = Promise:new(noop) + o.state = REJECTED + o.result = reason + handleRejection(o) + return o +end + +function Promise.all(values) + utils.assertType(values, 'table') + return Promise:new(function(resolve, reject) + local res = {} + local cnt = 0 + for k, v in pairs(values) do + cnt = cnt + 1 + Promise.resolve(v):thenCall(function(value) + res[k] = value + cnt = cnt - 1 + if cnt == 0 then + resolve(res) + end + end, function(reason) + reject(reason) + end) + end + if cnt == 0 then + resolve(res) + end + end) +end + +function Promise.allSettled(values) + utils.assertType(values, 'table') + return Promise:new(function(resolve, reject) + local res = {} + local cnt = 0 + local _ = reject + for k, v in pairs(values) do + cnt = cnt + 1 + Promise.resolve(v):thenCall(function(value) + res[k] = {status = 'fulfilled', value = value} + end, function(reason) + res[k] = {status = 'rejected', reason = reason} + end):finally(function() + cnt = cnt - 1 + if cnt == 0 then + resolve(res) + end + end) + end + if cnt == 0 then + resolve(res) + end + end) +end + +function Promise.any(values) + utils.assertType(values, 'table') + return Promise:new(function(resolve, reject) + local cnt = 0 + local function rejectAggregateError() + if cnt == 0 then + reject('AggregateError: All promises were rejected') + end + end + + for _, p in pairs(values) do + cnt = cnt + 1 + Promise.resolve(p):thenCall(function(value) + resolve(value) + end, function() + end):finally(function() + cnt = cnt - 1 + rejectAggregateError() + end) + end + rejectAggregateError() + end) +end + +function Promise.race(values) + utils.assertType(values, 'table') + return Promise:new(function(resolve, reject) + for _, p in pairs(values) do + Promise.resolve(p):thenCall(function(value) + resolve(value) + end, function(reason) + reject(reason) + end) + end + end) +end + +return Promise diff --git a/config/neovim/store/lazy-plugins/promise-async/rockspec/promise-async-1.0-0.rockspec b/config/neovim/store/lazy-plugins/promise-async/rockspec/promise-async-1.0-0.rockspec new file mode 100644 index 00000000..07985d62 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/rockspec/promise-async-1.0-0.rockspec @@ -0,0 +1,31 @@ +package = 'promise-async' +version = '1.0-0' +source = { + url = 'git+https://github.com/kevinhwang91/promise-async.git', + tag = 'v1.0.0' +} +description = { + summary = 'Promise & Async in Lua', + detailed = 'The goal of promise-async is to port Promise & Async from JavaScript to Lua.', + homepage = 'https://github.com/kevinhwang91/promise-async', + license = ' BSD-3-Clause' +} + +dependencies = { + 'lua >= 5.1, <= 5.4' +} + +build = { + type = 'builtin', + modules = { + async = 'lua/async.lua', + promise = 'lua/promise.lua', + ['promise-async.compat'] = 'lua/promise-async/compat.lua', + ['promise-async.error'] = 'lua/promise-async/error.lua', + ['promise-async.loop'] = 'lua/promise-async/loop.lua', + ['promise-async.utils'] = 'lua/promise-async/utils.lua' + }, + copy_directories = { + 'typings' + } +} diff --git a/config/neovim/store/lazy-plugins/promise-async/rockspec/promise-async-scm-0.rockspec b/config/neovim/store/lazy-plugins/promise-async/rockspec/promise-async-scm-0.rockspec new file mode 100644 index 00000000..0eb23b26 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/rockspec/promise-async-scm-0.rockspec @@ -0,0 +1,30 @@ +package = 'promise-async' +version = 'scm-0' +source = { + url = 'git+https://github.com/kevinhwang91/promise-async.git' +} +description = { + summary = 'Promise & Async in Lua', + detailed = 'The goal of promise-async is to port Promise & Async from JavaScript to Lua.', + homepage = 'https://github.com/kevinhwang91/promise-async', + license = ' BSD-3-Clause' +} + +dependencies = { + 'lua >= 5.1, <= 5.4' +} + +build = { + type = 'builtin', + modules = { + async = 'lua/async.lua', + promise = 'lua/promise.lua', + ['promise-async.compat'] = 'lua/promise-async/compat.lua', + ['promise-async.error'] = 'lua/promise-async/error.lua', + ['promise-async.loop'] = 'lua/promise-async/loop.lua', + ['promise-async.utils'] = 'lua/promise-async/utils.lua' + }, + copy_directories = { + 'typings' + } +} diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/async_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/async_spec.lua new file mode 100644 index 00000000..80512039 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/async_spec.lua @@ -0,0 +1,406 @@ +local promise = require('promise') +local async = require('async') +local helpers = require('spec.helpers.init') +local compat = require('promise-async.compat') +local deferredPromise = helpers.deferredPromise +local setTimeout = helpers.setTimeout +local basics = require('spec.helpers.basics') +local dummy = {dummy = 'dummy'} +local sentinel = {sentinel = 'sentinel'} +local sentinel2 = {sentinel = 'sentinel2'} +local sentinel3 = {sentinel = 'sentinel3'} +local other = {other = 'other'} + +describe('async await module.', function() + describe('async return a Promise.', function() + it('return value is a Promise', function() + local f = async(function() end) + assert.True(promise.isInstance(f)) + end) + + it('async without return statement', function() + async(function() end) + :thenCall(function(value) + assert.equal(nil, value) + done() + end) + assert.True(wait()) + end) + + it('async return a single', function() + async(function() + return dummy + end):thenCall(function(value) + assert.equal(dummy, value) + done() + end) + assert.True(wait()) + end) + + it('async return multiple values, which are packed into resolved result in Promise', function() + async(function() + return sentinel, sentinel2, sentinel3 + end):thenCall(function(value) + assert.same({sentinel, sentinel2, sentinel3}, value) + done() + end) + assert.True(wait()) + end) + + it('async throw error', function() + async(function() + error(dummy) + return other + end):thenCall(nil, function(reason) + assert.equal(dummy, reason) + done() + end) + assert.True(wait()) + end) + end) + + describe('executor inside async.', function() + describe('must be either a function or a callable table,', function() + it('other values', function() + local errorValues = {nil, 0, '0', true} + for _, v in pairs(errorValues) do + assert.error(function() + async(v) + end) + end + end) + + it('a function', function() + async(function() + setTimeout(function() + done() + end, 10) + end) + assert.True(wait()) + end) + + it('a callable table', function() + async(setmetatable({}, { + __call = function() + setTimeout(function() + done() + end, 10) + end + })) + assert.True(wait()) + end) + end) + + it('should run immediately', function() + local executor = spy.new(function() end) + async(executor) + assert.spy(executor).was_called() + end) + + it('until the await is called, executor should run immediately even if in nested function', function() + local value + local executor = spy.new(function() end) + async(function() + value = async.wait(async(function() + return async(function() + executor() + return dummy + end) + end)) + done() + end) + assert.spy(executor).was_called() + assert.True(wait()) + assert.equal(dummy, value) + end) + end) + + describe([[await inside async's executor.]], function() + local function testBasicAwait(expectedValue, stringRepresentation) + it('should wait for the promise with resolved value: ' .. stringRepresentation, function() + local value + local p, resolve = deferredPromise() + async(function() + value = await(p) + done() + end) + assert.False(wait(10)) + resolve(expectedValue) + assert.True(wait()) + assert.equal(expectedValue, value) + end) + end + + for valueStr, basicFn in pairs(basics) do + testBasicAwait(basicFn(), valueStr) + end + end) + + describe('`pcall` and `xpcall` surround statement or function.', function() + it('call `pcall` to get the value from a single await', function() + local ok, value + local p, resolve = deferredPromise() + async(function() + ok, value = pcall(await, p) + done() + end) + assert.False(wait(10)) + resolve(dummy) + assert.True(wait()) + assert.True(ok) + assert.equal(dummy, value) + end) + + it('call `xpcall` to get the value from a single await', function() + local ok, value + local p, resolve = deferredPromise() + async(function() + ok, value = xpcall(await, function() end, p) + done() + end) + assert.False(wait(10)) + resolve(dummy) + assert.True(wait()) + assert.True(ok) + assert.equal(dummy, value) + end) + + it('call `pcall` to catch the reason from a single await', function() + local ok, reason + local p, _, reject = deferredPromise() + async(function() + ok, reason = pcall(await, p) + done() + end) + assert.False(wait(10)) + reject(dummy) + assert.True(wait()) + assert.False(ok) + assert.equal(dummy, reason) + end) + + it('call `xpcall` to catch the reason from a single await', function() + local ok, reason + local p, _, reject = deferredPromise() + async(function() + ok = xpcall(await, function(e) + reason = e + end, p) + done() + end) + assert.False(wait(10)) + reject(dummy) + assert.True(wait()) + assert.False(ok) + assert.equal(dummy, reason) + end) + + describe('call `pcall` to catch the reason from a function,', function() + it('throw error after the result return by await', function() + local ok, value, reason + local p1, resolve = deferredPromise() + local p2, _, reject = deferredPromise() + async(function() + ok, reason = pcall(function() + value = await(p1) + await(p2) + end) + done() + end) + p1:thenCall(function() + reject(dummy) + end) + assert.False(wait(10)) + setTimeout(function() + resolve(other) + end, 20) + assert.True(wait()) + assert.False(ok) + assert.equal(other, value) + assert.equal(dummy, reason) + end) + + it('throw error before the result return by await', function() + local ok, value, reason + local p1, _, reject = deferredPromise() + local p2, resolve = deferredPromise() + async(function() + ok, reason = pcall(function() + await(p1) + value = await(p2) + end) + done() + end) + resolve(other) + assert.False(wait(10)) + setTimeout(function() + reject(dummy) + end, 20) + assert.True(wait()) + assert.False(ok) + assert.equal(nil, value) + assert.equal(dummy, reason) + end) + end) + + describe('call `xpcall` to catch the reason from a function,', function() + it('throw error after the result return by await', function() + local ok, value, reason + local p1, resolve = deferredPromise() + local p2, _, reject = deferredPromise() + async(function() + ok = xpcall(function() + value = await(p1) + await(p2) + end, function(e) + reason = e + end) + done() + end) + p1:thenCall(function() + reject(dummy) + end) + assert.False(wait(10)) + setTimeout(function() + resolve(other) + end, 20) + assert.True(wait()) + assert.False(ok) + assert.equal(other, value) + assert.equal(dummy, reason) + end) + + it('throw error before the result return by await', function() + local ok, value, reason + local p1, _, reject = deferredPromise() + local p2, resolve = deferredPromise() + async(function() + ok = xpcall(function() + await(p1) + value = await(p2) + end, function(e) + reason = e + end) + done() + end) + resolve(other) + assert.False(wait(10)) + setTimeout(function() + reject(dummy) + end, 20) + assert.True(wait()) + assert.False(ok) + assert.equal(nil, value) + assert.equal(dummy, reason) + end) + end) + end) + + describe('nested async functions.', function() + it('simple call chain', function() + local value + async(function() + value = await(async(function() + return async(function() + return dummy + end) + end)) + done() + end) + assert.True(wait()) + assert.equal(dummy, value) + end) + + it('deferred call', function() + local value + setTimeout(function() + async(function() + value = await(async(function() + return async(function() + return sentinel + end) + end)) + done() + end) + end, 10) + assert.True(wait()) + assert.equal(sentinel, value) + end) + + it('return multiple values', function() + local value1, value2, value3, value4 + async(function() + value1, value2, value3, value4 = await(async(function() + return sentinel, sentinel2, sentinel3 + end)) + done() + end) + assert.True(wait()) + assert.equal(sentinel, value1) + assert.equal(sentinel2, value2) + assert.equal(sentinel3, value3) + assert.equal(nil, value4) + end) + + it('should catch error from the deepest callee', function() + local ok, value, reason + async(function() + ok = xpcall(function() + value = await(async(function() + return async(function() + error(dummy) + return other + end) + end)) + end, function(e) + reason = e + end) + done() + end) + assert.True(wait()) + assert.False(ok) + assert.equal(nil, value) + assert.equal(dummy, reason) + end) + + it('should wait for the deepest callee', function() + local value + local p, resolve = deferredPromise() + async(function() + value = await(async(function() + return await(async(function() + return await(p) + end)) + end)) + done() + end) + resolve(dummy) + assert.True(wait()) + assert.equal(dummy, value) + end) + + it('should wait for all callees', function() + local value + local p1, resolve1 = deferredPromise() + local p2, resolve2 = deferredPromise() + local p3, resolve3 = deferredPromise() + + async(function() + value = compat.pack(await(async(function() + return await(p1), await(async(function() + return await(p2), await(p3) + end)) + end))) + done() + end) + assert.False(wait(10)) + resolve3(sentinel3) + assert.False(wait(10)) + resolve2(sentinel2) + assert.False(wait(10)) + resolve1(sentinel) + assert.True(wait()) + assert.same({sentinel, sentinel2, sentinel3, n = 3}, value) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/fixtures.lua b/config/neovim/store/lazy-plugins/promise-async/spec/fixtures.lua new file mode 100644 index 00000000..fa3ce204 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/fixtures.lua @@ -0,0 +1,50 @@ +local busted = require('busted') + +local _done +local co = _G.co + +busted.subscribe({'test', 'start'}, function() + _done = false +end) + +local function getDone() + return _done +end + +function _G.done() + _done = true + return _done +end + +--------------------------------------------------------------------- +---Need to implement _G.wait to pass tests + +local defaultTimeout = 1000 + +---Should override this function to customize EventLoop to pass tests +---@param ms? number +---@return boolean +function _G.wait(ms) + if getDone() then + return true + end + local interval = 5 + local timer = require('luv').new_timer() + local cnt = 0 + ms = ms or defaultTimeout + timer:start(interval, interval, function() + cnt = cnt + interval + local d = getDone() + if cnt >= ms or d then + timer:stop() + timer:close() + local thread = coroutine.running() + if thread ~= co then + require('spec.helpers.init').setTimeout(function() + coroutine.resume(co, d) + end, 0) + end + end + end) + return coroutine.yield() +end diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/helpers/basics.lua b/config/neovim/store/lazy-plugins/promise-async/spec/helpers/basics.lua new file mode 100644 index 00000000..33adb910 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/helpers/basics.lua @@ -0,0 +1,30 @@ +local Basic = {} + +local co = coroutine.create(function() end) +coroutine.resume(co) + +Basic['`nil`'] = function() + return nil +end + +Basic['`false`'] = function() + return false +end + +Basic['`0`'] = function() + return 0 +end + +Basic['`string`'] = function() + return 'string' +end + +Basic['a metatable'] = function() + return setmetatable({}, {}) +end + +Basic['a thread'] = function() + return co +end + +return Basic diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/helpers/init.lua b/config/neovim/store/lazy-plugins/promise-async/spec/helpers/init.lua new file mode 100644 index 00000000..69d6e520 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/helpers/init.lua @@ -0,0 +1,58 @@ +local M = {} +local promise = require('promise') + +M.setTimeout = promise.loop.setTimeout + +function M.deferredPromise() + local resolve, reject + local p = promise(function(resolve0, reject0) + resolve, reject = resolve0, reject0 + end) + return p, resolve, reject +end + +function M.testFulfilled(it, assert, value, test) + it('already-fulfilled', function() + test(promise.resolve(value)) + assert.True(wait()) + end) + + it('immediately-fulfilled', function() + local p, resolve = M.deferredPromise() + test(p) + resolve(value) + assert.True(wait()) + end) + + it('eventually-fulfilled', function() + local p, resolve = M.deferredPromise() + test(p) + wait(10) + resolve(value) + assert.True(wait()) + end) +end + +function M.testRejected(it, assert, reason, test) + it('already-rejected', function() + test(promise.reject(reason)) + assert.True(wait()) + end) + + it('immediately-rejected', function() + local p, _, reject = M.deferredPromise() + test(p) + reject(reason) + assert.True(wait()) + end) + + it('eventually-fulfilled', function() + local p, _, reject = M.deferredPromise() + test(p) + wait(10) + reject(reason) + assert.True(wait()) + end) +end + +return M diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/helpers/outputHandler.lua b/config/neovim/store/lazy-plugins/promise-async/spec/helpers/outputHandler.lua new file mode 100644 index 00000000..d6111643 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/helpers/outputHandler.lua @@ -0,0 +1,29 @@ +return function(options) + local busted = require('busted') + local handler = require('busted.outputHandlers.utfTerminal')(options) + + local promiseUnhandledError = {} + + busted.subscribe({'test', 'end'}, function(element, parent) + while #promiseUnhandledError > 0 do + local res = table.remove(promiseUnhandledError, 1) + handler.successesCount = handler.successesCount - 1 + handler.failuresCount = handler.failuresCount + 1 + busted.publish({'failure', element.descriptor}, element, parent, tostring(res)) + end + end) + + require('promise').loop.callWrapper = function(callback) + local ok, res = pcall(callback) + if ok then + return + end + -- Some tests never handle the rejected promises, We should ignore them. + local msg = tostring(res) + if msg:match('^UnhandledPromiseRejection') then + return + end + table.insert(promiseUnhandledError, msg) + end + return handler +end diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/helpers/reasons.lua b/config/neovim/store/lazy-plugins/promise-async/spec/helpers/reasons.lua new file mode 100644 index 00000000..e4939026 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/helpers/reasons.lua @@ -0,0 +1,37 @@ +local promise = require('promise') +local Reasons = {} +local dummy = {dummy = 'dummy'} + +Reasons['`nil`'] = function() + return nil +end + +Reasons['`false`'] = function() + return false +end + +-- Lua before 5.3 versions will transfer number to string after pcall. +-- Pure string will carry some extra information after pcall, no need to test +-- Reasons['`0`'] = function() +-- return 0 +-- end + +Reasons['a metatable'] = function() + return setmetatable({}, {}) +end + +Reasons['an always-pending thenable'] = function() + return { + thenCall = function() end + } +end + +Reasons['a fulfilled promise'] = function() + return promise.resolve(dummy) +end + +Reasons['a rejected promise'] = function() + return promise.reject(dummy) +end + +return Reasons diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/helpers/thenables.lua b/config/neovim/store/lazy-plugins/promise-async/spec/helpers/thenables.lua new file mode 100644 index 00000000..24c46064 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/helpers/thenables.lua @@ -0,0 +1,137 @@ +local helpers = require('spec.helpers.init') +local setTimeout = helpers.setTimeout +local deferredPromise = helpers.deferredPromise +local promise = require('promise') +local other = {other = 'other'} + +local Thenables = { + fulfilled = { + ['a synchronously-fulfilled custom thenable'] = function(value) + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(value) + end + } + end, + ['an asynchronously-fulfilled custom thenable'] = function(value) + return { + thenCall = function(self, resolvePromise) + local _ = self + setTimeout(function() + resolvePromise(value) + end, 0) + end + } + end, + ['a synchronously-fulfilled one-time thenable'] = function(value) + local numberOfTimesThenRetrieved = 0; + return setmetatable({}, { + __index = function(_, k) + if numberOfTimesThenRetrieved == 0 and k == 'thenCall' then + numberOfTimesThenRetrieved = numberOfTimesThenRetrieved + 1 + return function(self, resolvePromise) + local _ = self + resolvePromise(value) + end + end + return nil + end + }) + end, + ['a thenable that tries to fulfill twice'] = function(value) + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(value) + resolvePromise(other) + end + } + end, + ['a thenable that fulfills but then throws'] = function(value) + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(value) + error(other) + end + } + end, + ['an already-fulfilled promise'] = function(value) + return promise.resolve(value) + end, + ['an eventually-fulfilled promise'] = function(value) + local p, resolve = deferredPromise() + setTimeout(function() + resolve(value) + end, 10) + return p + end + }, + rejected = { + ['a synchronously-rejected custom thenable'] = function(reason) + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + rejectPromise(reason) + end + } + end, + ['an asynchronously-rejected custom thenable'] = function(reason) + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + setTimeout(function() + rejectPromise(reason) + end, 0) + end + } + end, + ['a synchronously-rejected one-time thenable'] = function(reason) + local numberOfTimesThenRetrieved = 0; + return setmetatable({}, { + __index = function(_, k) + if numberOfTimesThenRetrieved == 0 and k == 'thenCall' then + numberOfTimesThenRetrieved = numberOfTimesThenRetrieved + 1 + return function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + rejectPromise(reason) + end + end + return nil + end + }) + end, + ['a thenable that immediately throws in `thenCall`'] = function(reason) + return { + thenCall = function() + error(reason) + end + } + end, + ['an table with a throwing `thenCall` metatable'] = function(reason) + return setmetatable({}, { + __index = function(_, k) + if k == 'thenCall' then + return function() + error(reason) + end + end + return nil + end + }) + end, + ['an already-rejected promise'] = function(reason) + return promise.reject(reason) + end, + ['an eventually-rejected promise'] = function(reason) + local p, _, reject = deferredPromise() + setTimeout(function() + reject(reason) + end, 10) + return p + end + } +} + +return Thenables diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/init.lua b/config/neovim/store/lazy-plugins/promise-async/spec/init.lua new file mode 100644 index 00000000..5d1d043b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/init.lua @@ -0,0 +1,54 @@ +package.path = os.getenv('PWD') .. '/lua/?.lua;' .. package.path +local compat = require('promise-async.compat') + +if compat.is51() then + _G.pcall = compat.pcall + _G.xpcall = compat.xpcall +end + +local uv = require('luv') + +local co = coroutine.create(function() + require('busted.runner')({standalone = false, output = 'spec.helpers.outputHandler'}) + -- no errors for nvim + if vim then + vim.schedule(function() + vim.cmd('cq 0') + end) + end +end) + +_G.co = co + +local compatibility = require('busted.compatibility') + +if vim then + compatibility.exit = function(code) + vim.schedule(function() + vim.cmd(('cq %d'):format(code)) + end) + end + _G.arg = vim.fn.argv() + _G.print = function(...) + local argv = {...} + for i = 1, #argv do + argv[i] = tostring(argv[i]) + end + table.insert(argv, '\n') + io.write(unpack(argv)) + end + coroutine.resume(co) +else + local c = 0 + -- https://github.com/luvit/luv/issues/599 + compatibility.exit = function(code) + c = code + end + local idle = uv.new_idle() + idle:start(function() + idle:stop() + coroutine.resume(co) + end) + uv.run() + os.exit(c) +end diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/loop_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/loop_spec.lua new file mode 100644 index 00000000..cff67015 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/loop_spec.lua @@ -0,0 +1,84 @@ +local loop = require('promise').loop + +local function testAsynchronousFunction(name) + it(name .. 'is asynchronous', function() + local called = spy.new(function() end) + loop[name](function() + called() + done() + end, 0) + assert.spy(called).was_not_called() + assert.True(wait()) + assert.spy(called).was_called() + end) +end + +describe('EventLoop for Promise.', function() + testAsynchronousFunction('setTimeout') + testAsynchronousFunction('nextTick') + testAsynchronousFunction('nextIdle') + + describe('fire `nextIdle` is later than `nextTick`,', function() + it('call `nextTick` first', function() + local queue = {} + local tick = {'tick'} + local idle = {'idle'} + loop.nextTick(function() + table.insert(queue, tick) + end) + loop.nextIdle(function() + table.insert(queue, idle) + end) + loop.setTimeout(done, 50) + assert.True(wait()) + assert.same(tick, queue[1]) + assert.same(idle, queue[2]) + end) + + it('call `nextIdle` first', function() + local queue = {} + local tick = {'tick'} + local idle = {'idle'} + loop.nextIdle(function() + table.insert(queue, idle) + end) + loop.nextTick(function() + table.insert(queue, tick) + end) + loop.setTimeout(done, 50) + assert.True(wait()) + assert.same(tick, queue[1]) + assert.same(idle, queue[2]) + end) + end) + + it('call `nextTick` in `nextTick` event', function() + local onTick = spy.new(function() end) + local onNextTick = spy.new(function() end) + loop.nextTick(function() + onTick() + loop.nextTick(function() + onNextTick() + assert.spy(onNextTick).was_called() + done() + end) + assert.spy(onTick).was_called() + assert.spy(onNextTick).was_not_called() + end) + + assert.True(wait()) + end) + + it('override callWrapper method', function() + local rawCallWrapper = loop.callWrapper + local callback = function() end + loop.callWrapper = function(fn) + loop.callWrapper = rawCallWrapper + assert.same(callback, fn) + done() + end + loop.nextTick(callback) + assert.True(wait()) + loop.callWrapper = rawCallWrapper + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.1.2_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.1.2_spec.lua new file mode 100644 index 00000000..081fab2c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.1.2_spec.lua @@ -0,0 +1,70 @@ +local helpers = require('spec.helpers.init') +local setTimeout = helpers.setTimeout +local testFulfilled = helpers.testFulfilled +local deferredPromise = helpers.deferredPromise +local dummy = {dummy = 'dummy'} + +describe('2.1.2.1: When fulfilled, a promise: must not transition to any other state.', function() + local onFulfilled, onRejected = spy.new(function() end), spy.new(function() end) + + before_each(function() + onFulfilled:clear() + onRejected:clear() + end) + + testFulfilled(it, assert, dummy, function(p) + local onFulfilledCalled = false + p:thenCall(function() + onFulfilledCalled = true + end, function() + assert.False(onFulfilledCalled) + done() + end) + + setTimeout(function() + done() + end, 50) + end) + + it('trying to fulfill then immediately reject', function() + local p, resolve, reject = deferredPromise() + p:thenCall(onFulfilled, onRejected) + resolve(dummy) + reject(dummy) + + setTimeout(function() + done() + end, 50) + assert.True(wait()) + assert.spy(onFulfilled).was_called() + assert.spy(onRejected).was_not_called() + end) + + it('trying to fulfill then reject, delayed', function() + local p, resolve, reject = deferredPromise() + p:thenCall(onFulfilled, onRejected) + resolve(dummy) + + setTimeout(function() + reject(dummy) + done() + end, 50) + assert.True(wait()) + assert.spy(onFulfilled).was_called() + assert.spy(onRejected).was_not_called() + end) + + it('trying to fulfill immediately then reject, delayed', function() + local p, resolve, reject = deferredPromise() + p:thenCall(onFulfilled, onRejected) + + setTimeout(function() + resolve(dummy) + reject(dummy) + done() + end, 50) + assert.True(wait()) + assert.spy(onFulfilled).was_called() + assert.spy(onRejected).was_not_called() + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.1.3_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.1.3_spec.lua new file mode 100644 index 00000000..c3292983 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.1.3_spec.lua @@ -0,0 +1,70 @@ +local helpers = require('spec.helpers.init') +local testRejected = helpers.testRejected +local deferredPromise = helpers.deferredPromise +local setTimeout = helpers.setTimeout +local dummy = {dummy = 'dummy'} + +describe('2.1.3.1: When rejected, a promise: must not transition to any other state.', function() + local onFulfilled, onRejected = spy.new(function() end), spy.new(function() end) + + before_each(function() + onFulfilled:clear() + onRejected:clear() + end) + + testRejected(it, assert, dummy, function(p) + local onRejectedCalled = false + p:thenCall(function() + assert.False(onRejectedCalled) + done() + end, function() + onRejectedCalled = true + end) + + setTimeout(function() + done() + end, 50) + end) + + it('trying to reject then immediately fulfill', function() + local p, resolve, reject = deferredPromise() + p:thenCall(onFulfilled, onRejected) + reject(dummy) + resolve(dummy) + + setTimeout(function() + done() + end, 50) + assert.True(wait()) + assert.spy(onFulfilled).was_not_called() + assert.spy(onRejected).was_called() + end) + + it('trying to reject then fulfill, delayed', function() + local p, resolve, reject = deferredPromise() + p:thenCall(onFulfilled, onRejected) + reject(dummy) + + setTimeout(function() + resolve(dummy) + done() + end, 50) + assert.True(wait()) + assert.spy(onFulfilled).was_not_called() + assert.spy(onRejected).was_called() + end) + + it('trying to reject immediately then fulfill, delayed', function() + local p, resolve, reject = deferredPromise() + p:thenCall(onFulfilled, onRejected) + + setTimeout(function() + reject(dummy) + resolve(dummy) + done() + end, 50) + assert.True(wait()) + assert.spy(onFulfilled).was_not_called() + assert.spy(onRejected).was_called() + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.1_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.1_spec.lua new file mode 100644 index 00000000..66f38d5d --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.1_spec.lua @@ -0,0 +1,78 @@ +local promise = require('promise') +local dummy = {dummy = 'dummy'} + +describe('2.2.1: Both `onFulfilled` and `onRejected` are optional arguments.', function() + describe('2.2.1.1: If `onFulfilled` is not a function, it must be ignored.', function() + describe('applied to a directly-rejected promise', function() + local function testNonFunction(nonFunction, stringRepresentation) + it('`onFulfilled` is ' .. stringRepresentation, function() + promise.reject(dummy) + :thenCall(nonFunction, function() + done() + end) + assert.True(wait()) + end) + end + + testNonFunction(nil, '`nil`') + testNonFunction(false, '`false`') + testNonFunction(5, '`5`') + testNonFunction({}, '`a table`') + end) + + describe('applied to a promise rejected and then chained off of', function() + local function testNonFunction(nonFunction, stringRepresentation) + it('`onFulfilled` is ' .. stringRepresentation, function() + promise.reject(dummy) + :thenCall(function() end, nil) + :thenCall(nonFunction, function() + done() + end) + assert.True(wait()) + end) + end + + testNonFunction(nil, '`nil`') + testNonFunction(false, '`false`') + testNonFunction(5, '`5`') + testNonFunction({}, '`a table`') + end) + end) + + describe('2.2.1.2: If `onRejected` is not a function, it must be ignored.', function() + describe('applied to a directly-fulfilled promise', function() + local function testNonFunction(nonFunction, stringRepresentation) + it('`onRejected` is ' .. stringRepresentation, function() + promise.resolve(dummy) + :thenCall(function() + done() + end, nonFunction) + assert.True(wait()) + end) + end + + testNonFunction(nil, '`nil`') + testNonFunction(false, '`false`') + testNonFunction(5, '`5`') + testNonFunction({}, '`a table`') + end) + + describe('applied to a promise fulfilled and then chained off of', function() + local function testNonFunction(nonFunction, stringRepresentation) + it('`onRejected` is ' .. stringRepresentation, function() + promise.resolve(dummy) + :thenCall(nil, function() end) + :thenCall(function() + done() + end, nonFunction) + assert.True(wait()) + end) + end + + testNonFunction(nil, '`nil`') + testNonFunction(false, '`false`') + testNonFunction(5, '`5`') + testNonFunction({}, '`a table`') + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.2_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.2_spec.lua new file mode 100644 index 00000000..86c22519 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.2_spec.lua @@ -0,0 +1,122 @@ +local helpers = require('spec.helpers.init') +local testFulfilled = helpers.testFulfilled +local setTimeout = helpers.setTimeout +local deferredPromise = helpers.deferredPromise +local promise = require('promise') +local dummy = {dummy = 'dummy'} +local sentinel = {sentinel = 'sentinel'} + +describe('2.2.2: If `onFulfilled` is a function,', function() + describe('2.2.2.1: it must be called after `promise` is fulfilled, ' .. + 'with `promise`’s fulfillment value as its first argument.', function() + testFulfilled(it, assert, sentinel, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('2.2.2.2: it must not be called before `promise` is fulfilled', function() + it('fulfilled after a delay', function() + local onFulfilled = spy.new(done) + local p, resolve = deferredPromise() + p:thenCall(onFulfilled) + + setTimeout(function() + resolve(dummy) + end, 10) + assert.True(wait()) + assert.spy(onFulfilled).was_called(1) + end) + + it('never fulfilled', function() + local onFulfilled = spy.new(done) + local p = deferredPromise() + p:thenCall(onFulfilled) + assert.False(wait(30)) + assert.spy(onFulfilled).was_not_called() + end) + end) + + describe('2.2.2.3: it must not be called more than once.', function() + it('already-fulfilled', function() + local onFulfilled = spy.new(done) + promise.resolve(dummy):thenCall(onFulfilled) + assert.spy(onFulfilled).was_not_called() + assert.True(wait()) + assert.spy(onFulfilled).was_called(1) + end) + + it('trying to fulfill a pending promise more than once, immediately', function() + local onFulfilled = spy.new(done) + local p, resolve = deferredPromise() + p:thenCall(onFulfilled) + resolve(dummy) + resolve(dummy) + assert.True(wait()) + assert.spy(onFulfilled).was_called(1) + end) + + it('trying to fulfill a pending promise more than once, delayed', function() + local onFulfilled = spy.new(done) + local p, resolve = deferredPromise() + p:thenCall(onFulfilled) + setTimeout(function() + resolve(dummy) + resolve(dummy) + end, 10) + assert.True(wait()) + assert.spy(onFulfilled).was_called(1) + end) + + it('trying to fulfill a pending promise more than once, immediately then delayed', function() + local onFulfilled = spy.new(done) + local p, resolve = deferredPromise() + p:thenCall(onFulfilled) + resolve(dummy) + setTimeout(function() + resolve(dummy) + end, 10) + assert.True(wait()) + assert.spy(onFulfilled).was_called(1) + end) + + it('when multiple `thenCall` calls are made, spaced apart in time', function() + local onFulfilled1 = spy.new(function() end) + local onFulfilled2 = spy.new(function() end) + local onFulfilled3 = spy.new(function() end) + local p, resolve = deferredPromise() + p:thenCall(onFulfilled1) + setTimeout(function() + p:thenCall(onFulfilled2) + end, 10) + setTimeout(function() + p:thenCall(onFulfilled3) + end, 20) + setTimeout(function() + resolve(dummy) + done() + end, 30) + assert.True(wait()) + assert.spy(onFulfilled1).was_called(1) + assert.spy(onFulfilled2).was_called(1) + assert.spy(onFulfilled3).was_called(1) + end) + + it('when `thenCall` is interleaved with fulfillment', function() + local onFulfilled1 = spy.new(function() end) + local onFulfilled2 = spy.new(function() end) + local p, resolve = deferredPromise() + p:thenCall(onFulfilled1) + resolve(dummy) + setTimeout(function() + p:thenCall(onFulfilled2) + done() + end, 10) + assert.True(wait()) + assert.spy(onFulfilled1).was_called(1) + assert.spy(onFulfilled2).was_called(1) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.3_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.3_spec.lua new file mode 100644 index 00000000..d8d18c67 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.3_spec.lua @@ -0,0 +1,122 @@ +local helpers = require('spec.helpers.init') +local testRejected = helpers.testRejected +local setTimeout = helpers.setTimeout +local deferredPromise = helpers.deferredPromise +local promise = require('promise') +local dummy = {dummy = 'dummy'} +local sentinel = {sentinel = 'sentinel'} + +describe('2.2.3: If `onRejected` is a function,', function() + describe('2.2.3.1: it must be called after `promise` is rejected, ' .. + 'with `promise`’s rejection reason as its first argument.', function() + testRejected(it, assert, sentinel, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('2.2.3.2: it must not be called before `promise` is rejected', function() + it('rejected after a delay', function() + local onRejected = spy.new(done) + local p, _, reject = deferredPromise() + p:thenCall(nil, onRejected) + + setTimeout(function() + reject(dummy) + end, 10) + assert.True(wait()) + assert.spy(onRejected).was_called(1) + end) + + it('never rejected', function() + local onRejected = spy.new(done) + local p = deferredPromise() + p:thenCall(nil, onRejected) + assert.False(wait(30)) + assert.spy(onRejected).was_not_called() + end) + end) + + describe('2.2.3.3: it must not be called more than once.', function() + it('already-rejected', function() + local onRejected = spy.new(done) + promise.reject(dummy):thenCall(nil, onRejected) + assert.spy(onRejected).was_not_called() + assert.True(wait()) + assert.spy(onRejected).was_called(1) + end) + + it('trying to reject a pending promise more than once, immediately', function() + local onRejected = spy.new(done) + local p, _, reject = deferredPromise() + p:thenCall(nil, onRejected) + reject(dummy) + reject(dummy) + assert.True(wait()) + assert.spy(onRejected).was_called(1) + end) + + it('trying to reject a pending promise more than once, delayed', function() + local onRejected = spy.new(done) + local p, _, reject = deferredPromise() + p:thenCall(nil, onRejected) + setTimeout(function() + reject(dummy) + reject(dummy) + end, 10) + assert.True(wait()) + assert.spy(onRejected).was_called(1) + end) + + it('trying to reject a pending promise more than once, immediately then delayed', function() + local onRejected = spy.new(done) + local p, _, reject = deferredPromise() + p:thenCall(nil, onRejected) + reject(dummy) + setTimeout(function() + reject(dummy) + end, 10) + assert.True(wait()) + assert.spy(onRejected).was_called(1) + end) + + it('when multiple `thenCall` calls are made, spaced apart in time', function() + local onRejected1 = spy.new(function() end) + local onRejected2 = spy.new(function() end) + local onRejected3 = spy.new(function() end) + local p, _, reject = deferredPromise() + p:thenCall(nil, onRejected1) + setTimeout(function() + p:thenCall(nil, onRejected2) + end, 15) + setTimeout(function() + p:thenCall(nil, onRejected3) + end, 25) + setTimeout(function() + reject(dummy) + done() + end, 35) + assert.True(wait()) + assert.spy(onRejected1).was_called(1) + assert.spy(onRejected2).was_called(1) + assert.spy(onRejected3).was_called(1) + end) + + it('when `thenCall` is interleaved with rejection', function() + local onRejected1 = spy.new(function() end) + local onRejected2 = spy.new(function() end) + local p, _, reject = deferredPromise() + p:thenCall(nil, onRejected1) + reject(dummy) + setTimeout(function() + p:thenCall(nil, onRejected2) + done() + end, 10) + assert.True(wait()) + assert.spy(onRejected1).was_called(1) + assert.spy(onRejected2).was_called(1) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.4_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.4_spec.lua new file mode 100644 index 00000000..722dcc26 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.4_spec.lua @@ -0,0 +1,128 @@ +local helpers = require('spec.helpers.init') +local testFulfilled = helpers.testFulfilled +local testRejected = helpers.testRejected +local setTimeout = helpers.setTimeout +local deferredPromise = helpers.deferredPromise +local promise = require('promise') +local dummy = {dummy = 'dummy'} + +describe('2.2.4: `onFulfilled` or `onRejected` must not be called until ' .. + 'the execution context stack contains only platform code.', function() + describe('`thenCall` returns before the promise becomes fulfilled or rejected', function() + testFulfilled(it, assert, dummy, function(p) + local onFulfilled = spy.new(done) + p:thenCall(onFulfilled) + end) + + testRejected(it, assert, dummy, function(p) + local onRejected = spy.new(done) + p:thenCall(nil, onRejected) + end) + end) + + describe('Clean-stack execution ordering tests (fulfillment case)', function() + local onFulfilled = spy.new(done) + + before_each(function() + onFulfilled:clear() + end) + + it('when `onFulfilled` is added immediately before the promise is fulfilled', function() + local p, resolve = deferredPromise() + p:thenCall(onFulfilled) + resolve(dummy) + assert.True(wait()) + assert.spy(onFulfilled).was_called(1) + end) + + it('when `onFulfilled` is added immediately after the promise is fulfilled', function() + local p, resolve = deferredPromise() + resolve(dummy) + p:thenCall(onFulfilled) + assert.True(wait()) + assert.spy(onFulfilled).was_called(1) + end) + + it('when one `onFulfilled` is added inside another `onFulfilled`', function() + local p = promise.resolve() + p:thenCall(function() + p:thenCall(onFulfilled) + end) + assert.True(wait()) + assert.spy(onFulfilled).was_called(1) + end) + + it('when `onFulfilled` is added inside an `onRejected`', function() + local p1 = promise.reject() + local p2 = promise.resolve() + p1:thenCall(nil, function() + p2:thenCall(onFulfilled) + end) + assert.True(wait()) + assert.spy(onFulfilled).was_called(1) + end) + + it('when the promise is fulfilled asynchronously', function() + local p, resolve = deferredPromise() + setTimeout(function() + resolve(dummy) + end, 0) + p:thenCall(onFulfilled) + assert.True(wait()) + assert.spy(onFulfilled).was_called(1) + end) + end) + + describe('Clean-stack execution ordering tests (rejection case)', function() + local onRejected = spy.new(done) + + before_each(function() + onRejected:clear() + end) + + it('when `onRejected` is added immediately before the promise is rejected', function() + local p, _, reject = deferredPromise() + p:thenCall(nil, onRejected) + reject(dummy) + assert.True(wait()) + assert.spy(onRejected).was_called(1) + end) + + it('when `onRejected` is added immediately after the promise is rejected', function() + local p, _, reject = deferredPromise() + reject(dummy) + p:thenCall(nil, onRejected) + assert.True(wait()) + assert.spy(onRejected).was_called(1) + end) + + it('when `onRejected` is added inside an `onFulfilled`', function() + local p1 = promise.resolve() + local p2 = promise.reject() + p1:thenCall(function() + p2:thenCall(nil, onRejected) + end) + assert.True(wait()) + assert.spy(onRejected).was_called(1) + end) + + it('when one `onRejected` is added inside another `onRejected`', function() + local p = promise.reject() + p:thenCall(nil, function() + p:thenCall(nil, onRejected) + end) + assert.True(wait()) + assert.spy(onRejected).was_called(1) + end) + + it('when the promise is rejected asynchronously', function() + local p, _, reject = deferredPromise() + setTimeout(function() + reject(dummy) + end, 0) + p:thenCall(nil, onRejected) + assert.True(wait()) + assert.spy(onRejected).was_called(1) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.6_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.6_spec.lua new file mode 100644 index 00000000..669542d9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.6_spec.lua @@ -0,0 +1,295 @@ +local helpers = require('spec.helpers.init') +local testFulfilled = helpers.testFulfilled +local testRejected = helpers.testRejected +local setTimeout = helpers.setTimeout +local dummy = {dummy = 'dummy'} +local sentinel = {sentinel = 'sentinel'} +local sentinel2 = {sentinel = 'sentinel2'} +local sentinel3 = {sentinel = 'sentinel3'} + +describe('2.2.6: `thenCall` may be called multiple times on the same promise.', function() + local function callbackAggregator(times, ultimateCallback) + local soFar = 0 + return function() + soFar = soFar + 1 + if soFar == times then + ultimateCallback() + end + end + end + + describe('2.2.6.1: If/when `promise` is fulfilled, all respective `onFulfilled` callbacks ' .. + 'must execute in the order of their originating calls to `thenCall`.', function() + describe('multiple boring fulfillment handlers', function() + testFulfilled(it, assert, sentinel, function(p) + local onFulfilled1 = spy.new(function() end) + local onFulfilled2 = spy.new(function() end) + local onFulfilled3 = spy.new(function() end) + local onRejected = spy.new(function() end) + p:thenCall(onFulfilled1, onRejected) + p:thenCall(onFulfilled2, onRejected) + p:thenCall(onFulfilled3, onRejected) + p:thenCall(function(value) + assert.equal(sentinel, value) + + assert.spy(onFulfilled1).was_called_with(sentinel) + assert.spy(onFulfilled2).was_called_with(sentinel) + assert.spy(onFulfilled3).was_called_with(sentinel) + assert.spy(onRejected).was_not_called() + done() + end) + end) + end) + + describe('multiple fulfillment handlers, one of which throws', function() + testFulfilled(it, assert, sentinel, function(p) + local onFulfilled1 = spy.new(function() end) + local onFulfilled2 = spy.new(function() + error() + end) + local onFulfilled3 = spy.new(function() end) + local onRejected = spy.new(function() end) + p:thenCall(onFulfilled1, onRejected) + p:thenCall(onFulfilled2, onRejected):catch(function() end) + p:thenCall(onFulfilled3, onRejected) + p:thenCall(function(value) + assert.equal(sentinel, value) + assert.spy(onFulfilled1).was_called_with(sentinel) + assert.spy(onFulfilled2).was_called_with(sentinel) + assert.spy(onFulfilled3).was_called_with(sentinel) + assert.spy(onRejected).was_not_called() + done() + end) + end) + end) + + describe('results in multiple branching chains with their own fulfillment values', function() + testFulfilled(it, assert, dummy, function(p) + local semiDone = callbackAggregator(3, function() + done() + end) + + p:thenCall(function() + return sentinel + end):thenCall(function(value) + assert.equal(sentinel, value) + semiDone() + end) + + p:thenCall(function() + error(sentinel2) + end):thenCall(nil, function(reason) + assert.equal(sentinel2, reason) + semiDone() + end) + + p:thenCall(function() + return sentinel3 + end):thenCall(function(value) + assert.equal(sentinel3, value) + semiDone() + end) + end) + end) + + describe('`onFulfilled` handlers are called in the original order', function() + local queue = {} + local function enQueue(value) + table.insert(queue, value) + end + + before_each(function() + queue = {} + end) + + testFulfilled(it, assert, dummy, function(p) + local function onFulfilled1() + enQueue(1) + end + + local function onFulfilled2() + enQueue(2) + end + + local function onFulfilled3() + enQueue(3) + end + + p:thenCall(onFulfilled1) + p:thenCall(onFulfilled2) + p:thenCall(onFulfilled3) + + p:thenCall(function() + assert.same({1, 2, 3}, queue) + done() + end) + end) + + describe('even when one handler is added inside another handler', function() + testFulfilled(it, assert, dummy, function(p) + local function onFulfilled1() + enQueue(1) + end + + local function onFulfilled2() + enQueue(2) + end + + local function onFulfilled3() + enQueue(3) + end + + p:thenCall(function() + onFulfilled1() + p:thenCall(onFulfilled3) + end) + p:thenCall(onFulfilled2) + + p:thenCall(function() + setTimeout(function() + assert.same({1, 2, 3}, queue) + done() + end, 10) + end) + end) + end) + end) + end) + + describe('2.2.6.2: If/when `promise` is rejected, all respective `onRejected` callbacks ' .. + 'must execute in the order of their originating calls to `thenCall`.', function() + describe('multiple boring rejection handlers', function() + testRejected(it, assert, sentinel, function(p) + local onFulfilled = spy.new(function() end) + local onRejected1 = spy.new(function() end) + local onRejected2 = spy.new(function() end) + local onRejected3 = spy.new(function() end) + p:thenCall(onFulfilled, onRejected1) + p:thenCall(onFulfilled, onRejected2) + p:thenCall(onFulfilled, onRejected3) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + + assert.spy(onRejected1).was_called_with(sentinel) + assert.spy(onRejected2).was_called_with(sentinel) + assert.spy(onRejected3).was_called_with(sentinel) + assert.spy(onFulfilled).was_not_called() + done() + end) + end) + end) + + describe('multiple rejection handlers, one of which throws', function() + testRejected(it, assert, sentinel, function(p) + local onFulfilled = spy.new(function() end) + local onRejected1 = spy.new(function() end) + local onRejected2 = spy.new(function() + error() + end) + local onRejected3 = spy.new(function() end) + p:thenCall(onFulfilled, onRejected1) + p:thenCall(onFulfilled, onRejected2):catch(function() end) + p:thenCall(onFulfilled, onRejected3) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + + assert.spy(onRejected1).was_called_with(sentinel) + assert.spy(onRejected2).was_called_with(sentinel) + assert.spy(onRejected3).was_called_with(sentinel) + assert.spy(onFulfilled).was_not_called() + done() + end) + end) + end) + + describe('results in multiple branching chains with their own rejection values', function() + testRejected(it, assert, dummy, function(p) + local semiDone = callbackAggregator(3, function() + done() + end) + + p:thenCall(nil, function() + return sentinel + end):thenCall(function(value) + assert.equal(sentinel, value) + semiDone() + end) + + p:thenCall(nil, function() + error(sentinel2) + end):thenCall(nil, function(reason) + assert.equal(sentinel2, reason) + semiDone() + end) + + p:thenCall(nil, function() + return sentinel3 + end):thenCall(function(value) + assert.equal(sentinel3, value) + semiDone() + end) + end) + end) + + describe('`onRejected` handlers are called in the original order', function() + local queue = {} + local function enQueue(value) + table.insert(queue, value) + end + + before_each(function() + queue = {} + end) + + testRejected(it, assert, dummy, function(p) + local function onRejected1() + enQueue(1) + end + + local function onRejected2() + enQueue(2) + end + + local function onRejected3() + enQueue(3) + end + + p:thenCall(nil, onRejected1) + p:thenCall(nil, onRejected2) + p:thenCall(nil, onRejected3) + p:thenCall(nil, function() + assert.same({1, 2, 3}, queue) + done() + end) + end) + + describe('even when one handler is added inside another handler', function() + testRejected(it, assert, dummy, function(p) + local function onRejected1() + enQueue(1) + end + + local function onRejected2() + enQueue(2) + end + + local function onRejected3() + enQueue(3) + end + + p:thenCall(nil, function() + onRejected1() + p:thenCall(nil, onRejected3) + end) + p:thenCall(nil, onRejected2) + p:thenCall(nil, function() + setTimeout(function() + assert.same({1, 2, 3}, queue) + done() + end, 15) + end) + end) + end) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.7_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.7_spec.lua new file mode 100644 index 00000000..543c1e42 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.2.7_spec.lua @@ -0,0 +1,98 @@ +local helpers = require('spec.helpers.init') +local testFulfilled = helpers.testFulfilled +local testRejected = helpers.testRejected +local deferredPromise = helpers.deferredPromise +local dummy = {dummy = 'dummy'} +local sentinel = {sentinel = 'sentinel'} +local other = {other = 'other'} +local reasons = require('spec.helpers.reasons') + +describe('2.2.7: `thenCall` must return a promise: ' .. + '`promise2 = promise1.thenCall(onFulfilled, onRejected)', function() + it('is a promise', function() + local p1 = deferredPromise() + local p2 = p1:thenCall() + + assert.True(type(p2) == 'table' or type(p2) == 'function') + assert.is.not_equal(p2, nil) + assert.equal('function', type(p2.thenCall)) + end) + + describe('2.2.7.1: If either `onFulfilled` or `onRejected` returns a value `x`, ' .. + 'run the Promise Resolution Procedure `[[Resolve]](promise2, x)`', function() + it('see separate 3.3 tests', function() + end) + end) + + describe('2.2.7.2: If either `onFulfilled` or `onRejected` throws an exception `e`, ' .. + '`promise2` must be rejected with `e` as the reason.', function() + local function testReason(expectedReason, stringRepresentation) + describe('The reason is ' .. stringRepresentation, function() + testFulfilled(it, assert, dummy, function(p1) + local p2 = p1:thenCall(function() + error(expectedReason) + end) + p2:thenCall(nil, function(actualReason) + assert.equal(expectedReason, actualReason) + done() + end) + end) + testRejected(it, assert, dummy, function(p1) + local p2 = p1:thenCall(nil, function() + error(expectedReason) + end) + p2:thenCall(nil, function(actualReason) + assert.equal(expectedReason, actualReason) + done() + end) + end) + end) + end + + for reasonStr, reason in pairs(reasons) do + testReason(reason(), reasonStr) + end + end) + + describe('2.2.7.3: If `onFulfilled` is not a function and `promise1` is fulfilled, ' .. + '`promise2` must be fulfilled with the same value', function() + local function testNonFunction(nonFunction, stringRepresentation) + describe('`onFulfilled` is' .. stringRepresentation, function() + testFulfilled(it, assert, sentinel, function(p1) + local p2 = p1:thenCall(nonFunction) + p2:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + end + + testNonFunction(nil, '`nil`') + testNonFunction(false, '`false`') + testNonFunction(5, '`5`') + testNonFunction(setmetatable({}, {}), 'a metatable') + testNonFunction({function() return other end}, 'an table containing a function') + end) + + describe('2.2.7.4: If `onRejected` is not a function and `promise1` is rejected, ' .. + '`promise2` must be rejected with the same reason', function() + local function testNonFunction(nonFunction, stringRepresentation) + describe('`onRejected` is' .. stringRepresentation, function() + testRejected(it, assert, sentinel, function(p1) + local p2 = p1:thenCall(nonFunction) + p2:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + end + + testNonFunction(nil, '`nil`') + testNonFunction(false, '`false`') + testNonFunction(5, '`5`') + testNonFunction(setmetatable({}, {}), 'a metatable') + testNonFunction({function() return other end}, 'an table containing a function') + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.1_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.1_spec.lua new file mode 100644 index 00000000..6b95577b --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.1_spec.lua @@ -0,0 +1,31 @@ +local promise = require('promise') +local dummy = {dummy = 'dummy'} + +describe('2.3.1: If `promise` and `x` refer to the same object, reject `promise` with a ' .. + '`TypeError` as the reason.', function() + it('via return from a fulfilled promise', function() + local p + p = promise.resolve(dummy):thenCall(function() + return p + end) + + p:thenCall(nil, function(reason) + assert.truthy(reason:match('^TypeError')) + done() + end) + assert.True(wait()) + end) + + it('via return from a rejected promise', function() + local p + p = promise.reject(dummy):thenCall(nil, function() + return p + end) + + p:thenCall(nil, function(reason) + assert.truthy(reason:match('^TypeError')) + done() + end) + assert.True(wait()) + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.2_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.2_spec.lua new file mode 100644 index 00000000..55dafb8d --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.2_spec.lua @@ -0,0 +1,97 @@ +local helpers = require('spec.helpers.init') +local setTimeout = helpers.setTimeout +local deferredPromise = helpers.deferredPromise +local promise = require('promise') +local dummy = {dummy = 'dummy'} +local sentinel = {sentinel = 'sentinel'} + +local function testPromiseResolution(xFactory, test) + it('via return from a fulfilled promise', function() + local p = promise.resolve(dummy):thenCall(function() + return xFactory() + end) + test(p) + assert.True(wait()) + end) + + it('via return from a rejected promise', function() + local p = promise.reject(dummy):thenCall(nil, function() + return xFactory() + end) + test(p) + assert.True(wait()) + end) +end + +describe('2.3.2: If `x` is a promise, adopt its state', function() + describe('2.3.2.1: If `x` is pending, `promise` must remain pending until `x` is ' .. + 'fulfilled or rejected.', function() + testPromiseResolution(function() + return deferredPromise() + end, function(p) + local onFulfilled = spy.new(function() end) + local onRejected = spy.new(function() end) + p:thenCall(onFulfilled, onRejected) + + assert.spy(onFulfilled).was_not_called() + assert.spy(onRejected).was_not_called() + done() + end) + end) + + describe('2.3.2.2: If/when `x` is fulfilled, fulfill `promise` with the same value.', function() + describe('`x` is already-fulfilled', function() + testPromiseResolution(function() + return promise.resolve(sentinel) + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('`x` is eventually-fulfilled', function() + testPromiseResolution(function() + local p, resolve = deferredPromise() + setTimeout(function() + resolve(sentinel) + end, 10) + return p + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + end) + + describe('2.3.2.3: If/when `x` is rejected, reject `promise` with the same reason.', function() + describe('`x` is already-rejected', function() + testPromiseResolution(function() + return promise.reject(sentinel) + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('`x` is eventually-rejected', function() + testPromiseResolution(function() + local p, _, reject = deferredPromise() + setTimeout(function() + reject(sentinel) + end, 10) + return p + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.3_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.3_spec.lua new file mode 100644 index 00000000..460d48fd --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.3_spec.lua @@ -0,0 +1,800 @@ +local helpers = require('spec.helpers.init') +local deferredPromise = helpers.deferredPromise +local promise = require('promise') +local setTimeout = helpers.setTimeout +local reasons = require('spec.helpers.reasons') +local dummy = {dummy = 'dummy'} +local sentinel = {sentinel = 'sentinel'} +local other = {other = 'other'} +local thenables = require('spec.helpers.thenables') + +local function testPromiseResolution(xFactory, test) + it('via return from a fulfilled promise', function() + local p = promise.resolve(dummy):thenCall(function() + return xFactory() + end) + test(p) + assert.True(wait()) + end) + + it('via return from a rejected promise', function() + local p = promise.reject(dummy):thenCall(nil, function() + return xFactory() + end) + test(p) + assert.True(wait()) + end) +end + +describe('2.3.3: Otherwise, if `x` is a table or function,', function() + describe('2.3.3.1: Let `thenCall` be `x.thenCall`', function() + describe('`x` is a table', function() + local thenCallRetrieved = spy.new(function() end) + + before_each(function() + thenCallRetrieved:clear() + end) + + testPromiseResolution(function() + local x = {} + setmetatable(x, { + __index = function(_, k) + if k == 'thenCall' then + thenCallRetrieved() + return function(_, resolvePromise) + resolvePromise() + end + end + end + }) + return x + end, function(p) + p:thenCall(function() + assert.spy(thenCallRetrieved).was_called(1) + done() + end) + end) + end) + + describe('2.3.3.2: If retrieving the property `x.thenCall` results in a thrown exception ' .. + '`e`, reject `promise` with `e` as the reason.', function() + local function testRejectionViaThrowingGetter(e, stringRepresentation) + describe('`e` is ' .. stringRepresentation, function() + testPromiseResolution(function() + return { + thenCall = function() + error(e) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(e, reason) + done() + end) + end) + end) + end + + for reasonStr, reason in pairs(reasons) do + testRejectionViaThrowingGetter(reason(), reasonStr) + end + end) + + describe('2.3.3.3: If `thenCall` is a function, call it with `x` as `self`, first ' .. + 'argument `resolvePromise`, and second argument `rejectPromise`', function() + testPromiseResolution(function() + local x + x = { + thenCall = function(self, resolvePromise, rejectPromise) + assert.equal(x, self) + assert.True(type(resolvePromise) == 'function') + assert.True(type(rejectPromise) == 'function') + resolvePromise() + end + } + return x + end, function(p) + p:thenCall(function() + done() + end) + end) + end) + + describe('2.3.3.3.1: If/when `resolvePromise` is called with value `y`, ' .. + 'run `[[Resolve]](promise, y)`', function() + local function testCallingResolvePromise(yFactory, stringRepresentation, test) + describe('`y` is ' .. stringRepresentation, function() + describe('`thenCall` calls `resolvePromise` synchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(yFactory()) + end + } + end, test) + end) + + describe('`thenCall` calls `resolvePromise` asynchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise) + local _ = self + setTimeout(function() + resolvePromise(yFactory()) + end, 0) + end + } + end, test) + end) + end) + end + + local function testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, + fulfillmentValue) + testCallingResolvePromise(yFactory, stringRepresentation, function(p) + p:thenCall(function(value) + assert.equal(fulfillmentValue, value) + done() + end) + end) + end + + local function testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, + rejectionReason) + testCallingResolvePromise(yFactory, stringRepresentation, function(p) + p:thenCall(nil, function(reason) + assert.equal(rejectionReason, reason) + done() + end) + end) + end + + describe('`y` is not a thenable', function() + testCallingResolvePromiseFulfillsWith(function() + return nil + end, '`null`', nil) + testCallingResolvePromiseFulfillsWith(function() + return false + end, '`false`', false) + testCallingResolvePromiseFulfillsWith(function() + return 5 + end, '`5`', 5) + testCallingResolvePromiseFulfillsWith(function() + return sentinel + end, '`an table`', sentinel) + end) + + describe('`y` is a thenable', function() + for stringRepresentation, factory in pairs(thenables.fulfilled) do + testCallingResolvePromiseFulfillsWith(function() + return factory(sentinel) + end, stringRepresentation, sentinel) + end + for stringRepresentation, factory in pairs(thenables.rejected) do + testCallingResolvePromiseRejectsWith(function() + return factory(sentinel) + end, stringRepresentation, sentinel) + end + end) + + describe('`y` is a thenable for a thenable', function() + for outerString, outerFactory in pairs(thenables.fulfilled) do + for innerString, factory in pairs(thenables.fulfilled) do + local stringRepresentation = outerString .. ' for ' .. innerString + testCallingResolvePromiseFulfillsWith(function() + return outerFactory(factory(sentinel)) + end, stringRepresentation, sentinel) + end + for innerString, factory in pairs(thenables.rejected) do + local stringRepresentation = outerString .. ' for ' .. innerString + testCallingResolvePromiseRejectsWith(function() + return outerFactory(factory(sentinel)) + end, stringRepresentation, sentinel) + end + end + end) + end) + end) + + describe('2.3.3.3.2: If/when `rejectPromise` is called with reason `r`, reject `promise` with `r`', function() + local function testCallingRejectPromise(r, stringRepresentation, test) + describe('`r` is ' .. stringRepresentation, function() + describe('`thenCall` calls `rejectPromise` synchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + rejectPromise(r) + end + } + end, test) + end) + + describe('`thenCall` calls `rejectPromise` asynchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + setTimeout(function() + rejectPromise(r) + end, 0) + end + } + end, test) + end) + end) + end + + local function testCallingRejectPromiseRejectsWith(rejectionReason, stringRepresentation) + testCallingRejectPromise(rejectionReason, stringRepresentation, function(p) + p:thenCall(nil, function(reason) + assert.equal(rejectionReason, reason) + done() + end) + end) + end + + for reasonStr, reason in pairs(reasons) do + testCallingRejectPromiseRejectsWith(reason(), reasonStr) + end + end) + + describe('2.3.3.3.3: If both `resolvePromise` and `rejectPromise` are called, or multiple ' .. + 'calls to the same argument are made, the first call takes precedence, and any further ' .. + 'calls are ignored.', function() + describe('calling `resolvePromise` then `rejectPromise`, both synchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _ = self + resolvePromise(sentinel) + rejectPromise(other) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('calling `resolvePromise` synchronously then `rejectPromise` asynchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _ = self + resolvePromise(sentinel) + setTimeout(function() + rejectPromise(other) + end, 0) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('calling `resolvePromise` then `rejectPromise`, both asynchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _ = self + setTimeout(function() + resolvePromise(sentinel) + end, 0) + setTimeout(function() + rejectPromise(other) + end, 0) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('calling `resolvePromise` with an asynchronously-fulfilled promise, then calling ' .. + '`rejectPromise`, both synchronously', function() + testPromiseResolution(function() + local p, resolve = deferredPromise() + setTimeout(function() + resolve(sentinel) + end, 10) + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _ = self + resolvePromise(p) + rejectPromise(other) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('calling `resolvePromise` with an asynchronously-rejected promise, then calling ' .. + '`rejectPromise`, both synchronously', function() + testPromiseResolution(function() + local p, _, reject = deferredPromise() + setTimeout(function() + reject(sentinel) + end, 10) + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _ = self + resolvePromise(p) + rejectPromise(other) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('calling `rejectPromise` then `resolvePromise`, both synchronously', function() + testPromiseResolution(function() + local p, resolve = deferredPromise() + setTimeout(function() + resolve(sentinel) + end, 10) + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _ = self + resolvePromise(p) + rejectPromise(other) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('calling `rejectPromise` synchronously then `resolvePromise` asynchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + rejectPromise(sentinel) + setTimeout(function() + resolvePromise(other) + end, 0) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('calling `rejectPromise` then `resolvePromise`, both asynchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + setTimeout(function() + rejectPromise(sentinel) + end, 0) + setTimeout(function() + resolvePromise(other) + end, 0) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('calling `resolvePromise` twice synchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(sentinel) + resolvePromise(other) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('calling `resolvePromise` twice, first synchronously then asynchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(sentinel) + setTimeout(function() + resolvePromise(other) + end, 0) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('calling `resolvePromise` twice, both times asynchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise) + local _ = self + setTimeout(function() + resolvePromise(sentinel) + end, 0) + setTimeout(function() + resolvePromise(other) + end, 0) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('calling `resolvePromise` with an asynchronously-fulfilled promise, ' .. + 'then calling it again, both times synchronously', function() + testPromiseResolution(function() + local p, resolve = deferredPromise() + setTimeout(function() + resolve(sentinel) + end, 10) + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(p) + resolvePromise(other) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('calling `resolvePromise` with an asynchronously-rejected promise, ' .. + 'then calling it again, both times synchronously', function() + testPromiseResolution(function() + local p, _, reject = deferredPromise() + setTimeout(function() + reject(sentinel) + end, 10) + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(p) + resolvePromise(other) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('calling `rejectPromise` twice synchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + rejectPromise(sentinel) + rejectPromise(other) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('calling `resolvePromise` twice, first synchronously then asynchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + rejectPromise(sentinel) + setTimeout(function() + rejectPromise(other) + end, 0) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('calling `rejectPromise` twice, both times asynchronously', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + setTimeout(function() + rejectPromise(sentinel) + end, 0) + setTimeout(function() + rejectPromise(other) + end, 0) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('saving and abusing `resolvePromise` and `rejectPromise`', function() + local savedResolvePromise, savedRejectPromise + + before_each(function() + savedResolvePromise, savedRejectPromise = nil, nil + end) + + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _ = self + savedResolvePromise, savedRejectPromise = resolvePromise, rejectPromise + end + } + end, function(p) + local onFulfilled, onRejected = spy.new(function() end), spy.new(function() end) + p:thenCall(onFulfilled, onRejected) + if savedResolvePromise and savedRejectPromise then + savedResolvePromise(dummy) + savedResolvePromise(dummy) + savedRejectPromise(dummy) + savedRejectPromise(dummy) + end + + setTimeout(function() + savedResolvePromise(dummy) + savedResolvePromise(dummy) + savedRejectPromise(dummy) + savedRejectPromise(dummy) + end, 10) + + setTimeout(function() + assert.spy(onFulfilled).was_called(1) + assert.spy(onRejected).was_not_called() + done() + end, 50) + end) + end) + + describe('2.3.3.3.4: If calling `thenCall` throws an exception `e`,', function() + describe('2.3.3.3.4.1: If `resolvePromise` or `rejectPromise` have been called, ignore it.', function() + describe('`resolvePromise` was called with a non-thenable', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(sentinel) + error(other) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('`resolvePromise` was called with an asynchronously-fulfilled promise', function() + testPromiseResolution(function() + local p, resolve = deferredPromise() + setTimeout(function() + resolve(sentinel) + end, 10) + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(p) + error(other) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('`resolvePromise` was called with an asynchronously-rejected promise', function() + testPromiseResolution(function() + local p, _, reject = deferredPromise() + setTimeout(function() + reject(sentinel) + end, 10) + return { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(p) + error(other) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('`rejectPromise` was called', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + rejectPromise(sentinel) + error(other) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('`resolvePromise` then `rejectPromise` were called', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _ = self + resolvePromise(sentinel) + rejectPromise(other) + end + } + end, function(p) + p:thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + end) + end) + + describe('`rejectPromise` then `resolvePromise` were called', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _ = self + rejectPromise(sentinel) + resolvePromise(other) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + end) + + describe('2.3.3.3.4.2: Otherwise, reject `promise` with `e` as the reason.', function() + describe('straightforward case', function() + testPromiseResolution(function() + return { + thenCall = function() + error(sentinel) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + end) + + describe('`resolvePromise` is called asynchronously before the `throw`', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise) + local _ = self + setTimeout(function() + resolvePromise(other) + end, 0) + error(sentinel) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + + describe('`rejectPromise` is called asynchronously before the `throw`', function() + testPromiseResolution(function() + return { + thenCall = function(self, resolvePromise, rejectPromise) + local _, _ = self, resolvePromise + setTimeout(function() + rejectPromise(other) + end, 0) + error(sentinel) + end + } + end, function(p) + p:thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + end) + end) + end) + end) + + describe('2.3.3.4: If `thenCall` is not a function, fulfill promise with `x`', function() + local function testFulfillViaNonFunction(thenCall, stringRepresentation) + local x = nil + + before_each(function() + x = {thenCall = thenCall} + end) + + describe('thenCall is ' .. stringRepresentation, function() + testPromiseResolution(function() + return x + end, function(p) + p:thenCall(function(value) + assert.equal(x, value) + done() + end) + end) + end) + end + + testFulfillViaNonFunction(5, '`5`') + testFulfillViaNonFunction({}, 'a table') + testFulfillViaNonFunction({function() end}, 'a table containing a function') + testFulfillViaNonFunction(setmetatable({}, {}), 'a metatable') + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.4_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.4_spec.lua new file mode 100644 index 00000000..8d5781c4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promiseA+/2.3.4_spec.lua @@ -0,0 +1,35 @@ +local helpers = require('spec.helpers.init') +local testFulfilled = helpers.testFulfilled +local testRejected = helpers.testRejected +local dummy = {dummy = 'dummy'} + +describe('2.3.4: If `x` is not an object or function, fulfill `promise` with `x`', function() + local function testValue(expectedValue, stringRepresentation) + describe('The value is ' .. stringRepresentation, function() + testFulfilled(it, assert, dummy, function(p1) + local p2 = p1:thenCall(function() + return expectedValue + end) + p2:thenCall(function(actualValue) + assert.equal(expectedValue, actualValue) + done() + end) + end) + + testRejected(it, assert, dummy, function(p1) + local p2 = p1:thenCall(nil, function() + return expectedValue + end) + p2:thenCall(function(actualValue) + assert.equal(expectedValue, actualValue) + done() + end) + end) + end) + end + + testValue(nil, '`nil`') + testValue(false, '`false`') + testValue(true, '`true`') + testValue(0, '`0`') +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/spec/promise_spec.lua b/config/neovim/store/lazy-plugins/promise-async/spec/promise_spec.lua new file mode 100644 index 00000000..6a390610 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/spec/promise_spec.lua @@ -0,0 +1,418 @@ +local promise = require('promise') +local helpers = require('spec.helpers.init') +local basics = require('spec.helpers.basics') +local reasons = require('spec.helpers.reasons') +local setTimeout = helpers.setTimeout +local dummy = {dummy = 'dummy'} +local sentinel = {sentinel = 'sentinel'} +local sentinel2 = {sentinel = 'sentinel2'} +local sentinel3 = {sentinel = 'sentinel3'} +local other = {other = 'other'} + +describe('Extend Promise A+.', function() + describe('Promise.resolve', function() + describe('Resolving basic values.', function() + local function testBasicResolve(expectedValue, stringRepresentation) + it('The value is ' .. stringRepresentation .. + ', and the state of Promise become fulfilled at once.', function() + local p = promise.resolve(expectedValue) + assert.truthy(tostring(p):match('')) + p:thenCall(function(value) + assert.equal(expectedValue, value) + done() + end) + assert.True(wait()) + end) + end + + for valueStr, basicFn in pairs(basics) do + testBasicResolve(basicFn(), valueStr) + end + end) + + it('resolve another resolved Promise', function() + local p1 = promise.resolve(dummy) + local p2 = promise.resolve(p1) + p2:thenCall(function(value) + assert.equal(dummy, value) + done() + end) + assert.True(wait()) + assert.equal(p1, p2) + end) + + it('resolve another rejected Promise', function() + local p1 = promise.reject(dummy) + local p2 = promise.resolve(p1) + p2:thenCall(nil, function(reason) + assert.equal(dummy, reason) + done() + end) + assert.True(wait()) + assert.equal(p1, p2) + end) + + it('resolve thenables and throwing Errors', function() + local p1 = promise.resolve({ + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(dummy) + end + }) + assert.True(promise.isInstance(p1)) + + local onFulfilled1 = spy.new(function(value) + assert.equal(dummy, value) + end) + p1:thenCall(onFulfilled1) + + local thenable = { + thenCall = function(self, resolvePromise) + local _ = self + error(dummy) + resolvePromise(other) + end + } + local onRejected = spy.new(function(reason) + assert.equal(dummy, reason) + end) + local p2 = promise.resolve(thenable) + p2:thenCall(nil, onRejected) + + thenable = { + thenCall = function(self, resolvePromise) + local _ = self + resolvePromise(dummy) + error(other) + end + } + local onFulfilled2 = spy.new(function(value) + assert.equal(dummy, value) + end) + local p3 = promise.resolve(thenable) + p3:thenCall(onFulfilled2) + + assert.False(wait(30)) + assert.spy(onFulfilled1).was_called() + assert.spy(onRejected).was_called() + assert.spy(onFulfilled2).was_called() + end) + end) + + describe('Promise.rejected.', function() + describe('Rejecting reasons', function() + local function testBasicReject(expectedReason, stringRepresentation) + it('The reason is ' .. stringRepresentation .. + ', and the state of Promise become rejected at once.', function() + local p = promise.reject(expectedReason) + assert.truthy(tostring(p):match('')) + p:thenCall(nil, function(value) + assert.equal(expectedReason, value) + done() + end) + assert.True(wait()) + end) + end + + for reasonStr, reason in pairs(reasons) do + testBasicReject(reason(), reasonStr) + end + end) + end) + + describe('Promise.catch method.', function() + it('throw errors', function() + local onRejected1 = spy.new(function(reason) + assert.equal(dummy, reason) + end) + promise(function() + error(dummy) + end):catch(onRejected1) + + local onRejected2 = spy.new(function() end) + promise(function(resolve) + resolve() + error(dummy) + end):catch(onRejected2) + + assert.False(wait(30)) + assert.spy(onRejected1).was_called() + assert.spy(onRejected2).was_not_called() + end) + + it('is resolved', function() + local onRejected1 = spy.new(function() end) + local onFulfilled = spy.new(function() end) + local onRejected2 = spy.new(function() end) + promise.resolve(dummy) + :catch(onRejected1) + :thenCall(onFulfilled) + :catch(onRejected2) + + assert.False(wait(30)) + assert.spy(onRejected1).was_not_called() + assert.spy(onFulfilled).was_called() + assert.spy(onRejected2).was_not_called() + end) + end) + + describe('Promise.finally method.', function() + it('is pending', function() + local onFinally = spy.new(done) + promise(function() end):finally(onFinally) + assert.False(wait(30)) + assert.spy(onFinally).was_not_called() + end) + + it('is fulfilled, next Promise is fulfilled with previous value', function() + local onFinally = spy.new(function() end) + local onFulfilled = spy.new(function(value) + assert.equal(dummy, value) + done() + end) + promise.resolve(dummy):finally(onFinally):thenCall(onFulfilled) + assert.True(wait()) + assert.spy(onFinally).was_called() + end) + + it('is rejected, next Promise is rejected with previous reason', function() + local onFinally = spy.new(function() end) + local onRejected = spy.new(function(reason) + assert.equal(dummy, reason) + done() + end) + promise.reject(dummy):finally(onFinally):thenCall(nil, onRejected) + assert.True(wait()) + assert.spy(onFinally).was_called() + end) + + it('throw error on finally', function() + local onRejected = spy.new(function(reason) + assert.equal(dummy, reason) + done() + end) + promise.resolve():finally(function() + error(dummy) + end):thenCall(nil, onRejected) + assert.True(wait()) + assert.spy(onRejected).was_called() + end) + end) + + describe('Promise.all method.', function() + it('should be fulfilled immediately if element is empty', function() + promise.all({}):thenCall(function(value) + assert.same({}, value) + done() + end) + assert.True(wait()) + end) + + describe('wait for fulfillments,', function() + it('use index table as elements', function() + local p1 = promise.resolve(sentinel) + local p2 = sentinel2 + local p3 = promise(function(resolve) + setTimeout(function() + resolve(sentinel3) + end, 10) + end) + + promise.all({p1, p2, p3}):thenCall(function(value) + assert.same({sentinel, sentinel2, sentinel3}, value) + done() + end) + assert.True(wait()) + end) + + it('use key-value table as elements, different from JavaScript', function() + local p1 = promise.resolve(sentinel) + local p2 = sentinel2 + local p3 = promise(function(resolve) + setTimeout(function() + resolve(sentinel3) + end, 10) + end) + + promise.all({p1 = p1, p2 = p2, p3 = p3}):thenCall(function(value) + assert.same({p1 = sentinel, p2 = sentinel2, p3 = sentinel3}, value) + done() + end) + assert.True(wait()) + end) + end) + + it('is rejected if any of the elements are rejected', function() + local p1 = promise.resolve(sentinel) + local p2 = sentinel2 + local p3 = promise(function(_, reject) + setTimeout(function() + reject(sentinel3) + end, 10) + end) + promise.all({p1, p2, p3}):thenCall(nil, function(reason) + assert.equal(sentinel3, reason) + done() + end) + assert.True(wait()) + end) + end) + + describe('Promise.allSettled method.', function() + it('should be fulfilled immediately if element is empty', function() + promise.allSettled({}):thenCall(function(value) + assert.same({}, value) + done() + end) + assert.True(wait()) + end) + + describe('wait for fulfillments,', function() + it('use index table as elements', function() + local p1 = promise.resolve(sentinel) + local p2 = sentinel2 + local p3 = promise(function(resolve) + setTimeout(function() + resolve(sentinel3) + end, 10) + end) + + promise.allSettled({p1, p2, p3}):thenCall(function(value) + assert.same({ + {status = 'fulfilled', value = sentinel}, + {status = 'fulfilled', value = sentinel2}, + {status = 'fulfilled', value = sentinel3} + }, value) + done() + end) + assert.True(wait()) + end) + + it('use key-value table as elements, different from JavaScript', function() + local p1 = promise.resolve(sentinel) + local p2 = sentinel2 + local p3 = promise(function(resolve) + setTimeout(function() + resolve(sentinel3) + end, 10) + end) + + promise.allSettled({p1 = p1, p2 = p2, p3 = p3}):thenCall(function(value) + assert.same({ + p1 = {status = 'fulfilled', value = sentinel}, + p2 = {status = 'fulfilled', value = sentinel2}, + p3 = {status = 'fulfilled', value = sentinel3} + }, value) + done() + end) + assert.True(wait()) + end) + end) + + + it('is always resolved even if any of the elements are rejected', function() + local p1 = promise.resolve(sentinel) + local p2 = sentinel2 + local p3 = promise(function(_, reject) + setTimeout(function() + reject(sentinel3) + end, 10) + end) + promise.allSettled({p1, p2, p3}):thenCall(function(value) + assert.same({ + {status = 'fulfilled', value = sentinel}, + {status = 'fulfilled', value = sentinel2}, + {status = 'rejected', reason = sentinel3} + }, value) + done() + end) + assert.True(wait()) + end) + end) + + describe('Promise.any method.', function() + it('should be rejected immediately if element is empty', function() + promise.any({}):thenCall(nil, function(reason) + assert.truthy(reason:match('^AggregateError')) + done() + end) + assert.True(wait()) + end) + + it('resolve with the first promise to fulfill, even if a promise rejects first', function() + local p1 = promise.reject(sentinel) + local p2 = promise(function(resolve) + setTimeout(function() + resolve(sentinel2) + end, 30) + end) + local p3 = promise(function(resolve) + setTimeout(function() + resolve(sentinel3) + end, 10) + end) + promise.any({p1, p2, p3}):thenCall(function(value) + assert.equal(sentinel3, value) + done() + end) + assert.True(wait()) + end) + + it('reject with `AggregateError` if no promise fulfills', function() + promise.any({promise.reject(dummy)}):thenCall(nil, function(reason) + assert.not_equal(dummy, reason) + assert.truthy(reason:match('^AggregateError')) + done() + end) + assert.True(wait()) + end) + end) + + describe('Promise.race method.', function() + it('should be pending forever if element is empty', function() + local onFinally = spy.new(done) + promise.race({}):finally(onFinally) + assert.spy(onFinally).was_not_called() + assert.False(wait(30)) + assert.spy(onFinally).was_not_called() + end) + + describe('resolves or rejects with the first promise to settle,', function() + it('resolve Promise is earlier than reject', function() + local p1 = promise(function(resolve) + setTimeout(function() + resolve(sentinel) + end, 10) + end) + local p2 = promise(function(_, reject) + setTimeout(function() + reject(sentinel2) + end, 20) + end) + promise.race({p1, p2}):thenCall(function(value) + assert.equal(sentinel, value) + done() + end) + assert.True(wait()) + end) + + it('reject Promise is earlier than resolve', function() + local p1 = promise(function(_, reject) + setTimeout(function() + reject(sentinel) + end, 10) + end) + local p2 = promise(function(resolve) + setTimeout(function() + resolve(sentinel2) + end, 20) + end) + promise.race({p1, p2}):thenCall(nil, function(reason) + assert.equal(sentinel, reason) + done() + end) + assert.True(wait()) + end) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/promise-async/typings/README.md b/config/neovim/store/lazy-plugins/promise-async/typings/README.md new file mode 100644 index 00000000..d78aeede --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/typings/README.md @@ -0,0 +1,22 @@ +# Typings of promise-async only + +Development library for the completion and documentation of promise-async. + +## Installation + +### lua-language-server + +Append this directory path to `Lua.workspace.library` field in configuration file. + +Use `.luarc.json` as an example: + +```json +{ + "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", + "workspace.library": ["your_path/typings"] +} +``` + +## Credits + +[lua-language-server/wiki/Setting](https://github.com/sumneko/lua-language-server/wiki/Setting) diff --git a/config/neovim/store/lazy-plugins/promise-async/typings/async.lua b/config/neovim/store/lazy-plugins/promise-async/typings/async.lua new file mode 100644 index 00000000..be28b7a8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/typings/async.lua @@ -0,0 +1,14 @@ +---@diagnostic disable: unused-local, missing-return + +---An async function is a function like the async keyword in JavaScript +---@class Async +---@overload fun(executor: table|fun(): ...): Promise +local Async = {} + +---Await expressions make promise returning functions behave as though they're synchronous by +---suspending execution until the returned promise is fulfilled or rejected. +---@param promise PromiseLike|Promise|any +---@return ... result The resolved value of the promise. +function _G.await(promise) end + +return Async diff --git a/config/neovim/store/lazy-plugins/promise-async/typings/loop.lua b/config/neovim/store/lazy-plugins/promise-async/typings/loop.lua new file mode 100644 index 00000000..5836d11a --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/typings/loop.lua @@ -0,0 +1,27 @@ +---@diagnostic disable: unused-local, missing-return + +---Singleton table, can't be as metatable. Two ways to extend the event loop. +---1. Create a new table and implement all methods, assign the new one to `Promise.loop` . +---2. Assign the targeted method to the `Promise.loop` field to override method. +---@class PromiseAsyncEventLoop +local EventLoop = {} + +---Sets a timer which executes a function once the timer expires. +---@param callback fun() A callback function, to be executed after the timer expires. +---@param delay number The time, in milliseconds that the timer should wait. +---@return userdata timer The timer handle created by EventLoop. +function EventLoop.setTimeout(callback, delay) end + +---The callback function will be executed in the next tick to continue the event loop. +---@param callback fun() A callback function, will be wrapped by EventLoop.callWrapper. +function EventLoop.nextTick(callback) end + +---The callback function will be executed after all next tick events are handled. +---@param callback fun() A callback function, will be wrapped by EventLoop.callWrapper. +function EventLoop.nextIdle(callback) end + +---Wrap the callback function from `setTimeout`, `nextTick` and `nextIdle`. +---@param callback fun() A callback function, executed by asynchronous methods. +function EventLoop.callWrapper(callback) end + +return EventLoop diff --git a/config/neovim/store/lazy-plugins/promise-async/typings/promise.lua b/config/neovim/store/lazy-plugins/promise-async/typings/promise.lua new file mode 100644 index 00000000..1843eb58 --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/typings/promise.lua @@ -0,0 +1,69 @@ +---@diagnostic disable: unused-local, missing-return + +---@alias PromiseExecutor fun(resolve: fun(value: any), reject: fun(reason?: any)) + +---@class Promise +---@field loop PromiseAsyncEventLoop +---@overload fun(executor: PromiseExecutor): Promise +local Promise = {} + +---Creates a new Promise. +---@param executor PromiseExecutor A callback used to initialize the promise. This callback is passed two arguments: +---a resolve callback used to resolve the promise with a value or the result of another promise, +---and a reject callback used to reject the promise with a provided reason or error. +---@return Promise promise A new Promise. +function Promise:new(executor) end + +---Attaches callbacks for the resolution and/or rejection of the Promise. +---@param onFulfilled? fun(value: any): any The callback to execute when the Promise is resolved. +---@param onRejected? fun(reason: any): any The callback to execute when the Promise is rejected. +---@return Promise promise A Promise for the completion of which ever callback is executed. +function Promise:thenCall(onFulfilled, onRejected) end + +---Attaches a callback for only the rejection of the Promise. +---@param onRejected? fun(reason: any): any The callback to execute when the Promise is rejected. +---@return Promise promise A Promise for the completion of the callback. +function Promise:catch(onRejected) end + +---Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). +---The resolved value cannot be modified from the callback. +---@param onFinally? fun() The callback to execute when the Promise is settled (fulfilled or rejected). +---@return Promise promise A new Promise. +function Promise:finally(onFinally) end + +---Creates a new resolved promise for the provided value. +---@param value? any A value, or the promise passed as value +---@return Promise promise A resolved promise. +function Promise.resolve(value) end + +---Creates a new rejected promise for the provided reason. +---@param reason? any The reason the Promise was rejected. +---@return Promise promise A new rejected Promise. +function Promise.reject(reason) end + +---Creates a Promise that is resolved with a table of results when all of the provided +---Promises resolve, or rejected when any Promise is rejected. +---@param values table A table of Promises. +---@return Promise promise A new Promise. +function Promise.all(values) end + +---Creates a Promise that is resolved with a table of results when all of the provided +---Promises resolve or reject. +---@param values table A table of Promises. +---@return Promise promise A new Promise. +function Promise.allSettled(values) end + +---The any function returns a Promise that is fulfilled by the first given Promise to be fulfilled, +---or rejected with an AggregateError containing an table of rejection reasons if all of the +---given Promises are rejected. It resolves all elements of the passed table to Promises as it runs this algorithm. +---@param values table +---@return Promise promise A new Promise +function Promise.any(values) end + +---Creates a Promise that is resolved or rejected when any of the provided Promises are resolved +---or rejected. +---@param values table A table of Promises. +---@return Promise promise A new Promise. +function Promise.race(values) end + +return Promise diff --git a/config/neovim/store/lazy-plugins/promise-async/typings/promiselike.lua b/config/neovim/store/lazy-plugins/promise-async/typings/promiselike.lua new file mode 100644 index 00000000..d184f74c --- /dev/null +++ b/config/neovim/store/lazy-plugins/promise-async/typings/promiselike.lua @@ -0,0 +1,12 @@ +---@diagnostic disable: unused-local, missing-return + +---@class PromiseLike +local PromiseLike = {} + +---Attaches callbacks for the resolution and/or rejection of the Promise. +---@param onFulfilled? fun(value: any): any The callback to execute when the Promise is resolved. +---@param onRejected? fun(reason: any): any The callback to execute when the Promise is rejected. +---@return Promise promise A Promise for the completion of which ever callback is executed. +function PromiseLike:thenCall(onFulfilled, onRejected) end + +return PromiseLike diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.busted b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.busted new file mode 100644 index 00000000..db2c6c22 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.busted @@ -0,0 +1,43 @@ +-- SPDX-License-Identifier: Unlicense + +-- This is free and unencumbered software released into the public domain. +-- +-- Anyone is free to copy, modify, publish, use, compile, sell, or distribute +-- this software, either in source code form or as a compiled binary, for any +-- purpose, commercial or non-commercial, and by any means. +-- +-- In jurisdictions that recognize copyright laws, the author or authors of +-- this software dedicate any and all copyright interest in the software to +-- the public domain. We make this dedication for the benefit of the public +-- at large and to the detriment of our heirs and successors. We intend this +-- dedication to be an overt act of relinquishment in perpetuity of all +-- present and future rights to this software under copyright law. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +-- AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +-- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-- +-- For more information, please refer to + + +return { + _all = { + coverage = false, + lua = './test/nvim-shim' + }, + default = { + verbose = true, + }, + unit = { + ROOT = {'./test/unit/'}, + }, + e2e = { + ROOT = {'./test/e2e/'}, + pattern = '', -- No fancy names for E2E tests + }, +} + +-- vim:ft=lua diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.editorconfig b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.editorconfig new file mode 100644 index 00000000..f7d89a6a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +indent_size = tab +tab_width = 4 +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.lua] +indent_style = tab + +[*.scm] +indent_style = space +indent_size = 2 diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.github/ISSUE_TEMPLATE/bug-report.yml b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 00000000..ab2d2e45 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,34 @@ +name: Bug Report +description: File a bug report +title: "[Bug]: " +labels: ["bug"] +assignees: + - "HiPhish" +body: + - type: 'input' + attributes: + label: 'Neovim version' + description: 'Version of your Neovim' + placeholder: '0.0.0' + - type: 'input' + attributes: + label: 'Language affected' + description: 'Leave empty if you experience the issue with any file type.' + placeholder: 'Lua' + - type: 'input' + attributes: + label: 'Query' + description: 'Leave empty if the issue is not tied to a specific query.' + placeholder: 'rainbow-delimiters' + - type: 'input' + attributes: + label: 'Strategy' + description: 'Leave empty if the issue is not tied to a specific strategy.' + placeholder: 'global' + - type: 'textarea' + attributes: + label: 'Description' + description: | + Please describe the bug in detail here. If possible provide a minimal + code sample which replicates the issue + placeholder: 'print((("Hello world!")))' diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.github/ISSUE_TEMPLATE/config.yml b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..2b483cdc --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Main repoo + url: 'https://gitlab.com/HiPhish/nvim-ts-rainbow2' + about: 'Upstream repository; you can also report bugs here on GitHub.' diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.gitignore b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.gitignore new file mode 100644 index 00000000..e3df9c5b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.gitignore @@ -0,0 +1,7 @@ +# Tag file created by Vim +doc/tags + +# Fake user directory structures +test/xdg/local/share/nvim/ +test/xdg/local/state/nvim/ +!test/xdg/local/share/nvim/site/pack/testing/start/rainbow-delimiters diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.gitlab/issue_templates/bug-report.md b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.gitlab/issue_templates/bug-report.md new file mode 100644 index 00000000..5816fcbc --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.gitlab/issue_templates/bug-report.md @@ -0,0 +1,7 @@ +- Neovim version: +- Language affected: +- Query used: +- Strategy used: + +Please describe the bug in detail here. If possible provide a minimal code +sample which replicates the issue. diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.gitmodules b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.gitmodules new file mode 100644 index 00000000..61001f8c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.gitmodules @@ -0,0 +1,6 @@ +[submodule "test/xdg/local/share/nvim/site/pack/testing/start/nvim-treesitter"] + path = test/xdg/local/share/nvim/site/pack/testing/start/nvim-treesitter + url = https://github.com/nvim-treesitter/nvim-treesitter +[submodule "test/xdg/local/share/nvim/site/pack/testing/start/yo-dawg"] + path = test/xdg/local/share/nvim/site/pack/testing/start/yo-dawg + url = https://gitlab.com/HiPhish/yo-dawg.nvim.git diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.luarc.json b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.luarc.json new file mode 100644 index 00000000..3533cf01 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.luarc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", + "workspace.ignoreDir": ["test/xdg"] +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.nvimrc b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.nvimrc new file mode 100644 index 00000000..c7a30dc2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/.nvimrc @@ -0,0 +1,2 @@ +" Use the custom shim as the busted binary for testing +let g:bustedprg='./test/busted' diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/CHANGELOG.rst b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/CHANGELOG.rst new file mode 100644 index 00000000..732758f9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/CHANGELOG.rst @@ -0,0 +1,309 @@ +.. default-role:: code + +########### + Changelog +########### + +All notable changes to this project will be documented in this file. The format +is based on `Keep a Changelog`_ and this project adheres to `Semantic +Versioning`_. + + +Unreleased +########## + +Added +===== + +- Missing patterns for C++: + + - `condition_clause` + - `for_statement` + - `cast_expression` + - `array_declarator` + +- Missing patterns for Common Lisp: + + - `loop_macro` + +- Missing patterns for Javascript: + + - `array_pattern` + - `for_in_statement` + - `for_statement` + +- Missing patterns for Typescript: + + - `enum_body` + - `interface_body` + +- Missing patterns for Haskell: + + - `children` + - `fields` + - `list` + - `parens` + - `prefix_id` + - `record` + - `tuple` + - `unit` + +Changed +======= + +- Renamed C# patterns: + + - `for_each_statement` to `foreach_statement` + - `type_of_expression` to `typeof_expression` + - `size_of_expression` to `sizeof_expression` + - `implicit_stack_alloc_array_creation_expression` to `implicit_stackalloc_expression` + +Removed +======= + +- Deprecated patterns for C#: + + - `interpolation` + +- Deprecated patterns for Java: + + - `condition` + +- Deprecated patterns for Haskell: + + - `con_list` + - `context` + - `deriving` + - `exp_arithmetic_sequence` + - `exp_lambda` + - `exp_list_comprehension` + - `exp_list` + - `exp_name` + - `exp_record` + - `exp_section_right` + - `pat_fields` + - `pat_list` + - `pat_parens` + - `pat_tuple` + - `record_fields` + - `type_list` + - `type_parens` + - `type_tuple` + - `type_tuple` + + +[0.4.0] - 2024-05-07 +#################### + +Added +===== + +- Public API function `is_enabled` +- Rasi support +- Svelte support +- Teal support +- Typst support +- XML support +- Missing patterns for Java: + + - `array_initializer` + - `annotation_argument_list` + - `catch_clause` + - `condition` + - `constructor_body` + - `dimensions_expr` + - `enhanced_for_statement` + - `for_statement` + - `inferred_parameters` + - `parenthesized_expression` + - `resource_specification` + - `cast_expression` + +- Missing patterns for Go: + + - `type_assertion_expression` + +- Missing patterns for Julia: + + - `curly_expression` + - `tuple_expression` + +- Missing patterns for Lua: + + - `field` + +- Missing patterns for Luadoc: + + - `indexed_field` + - `tuple_type` + +- Missing patterns for Python: + + - `dict_pattern` + - `import_from_statement` + - `interpolation` (literal string interpolation) + - `list_pattern` + - `tuple_pattern` + +- Missing patterns for R: + + - `for` + - `while` + - `switch` + - `function_definition` + +- Missing patterns for Rust: + + - `array_type` + +- Missing patterns for Starlark: + + - `tuple_pattern` + +Fixed +===== + +- Default configuration settings override custom settings if the configuration + value was used before setting the custom value +- Switched Fennel queries to new upstream grammar (`#6132`_) +- Deleted obsolete Julia pattern `parameter_list` + +.. _#6132: https://github.com/nvim-treesitter/nvim-treesitter/pull/6132 + + +[0.3.0] 2023-12-24 +################## + +This release brings a plethora of missing patterns to existing queries and lets +you specify priorities and queries dynamically at runtime. This means that it +is possible to set different queries for the same language depending on +external conditions, such as whether a buffer is read-only. + +And as a little extra given the date of this release, there is a new Christmas +strategy module. This will let you decorate your syntax tree in an especially +festive mood. The module is just a joke, so it will not be loaded by default +and you will have to figure out yourself how to set it up. + +Added +===== + +- Starlark support +- Missing patterns for Bash: + + - `array` + - `function_definition` + - `arithmetic_expansion` + - `compound_statement` + - `subscript` + +- Missing patterns for C: + + - `enumerator_list` + - `macro_type_specifier` + - `preproc_params` + - `compound_literal_expression` + - `parenthesized_declarator` + +- Missing patterns for Elixir: + + - `access_call` + +- Missing patterns for Fennel: + + - `table_binding` + +- New query for language `query`: + + - `rainbow-blocks` + +- New query for language `javascript`: + + - `rainbow-tags-react` + +- New query for language `tsx`: + + - `rainbow-tags-react` + +- New Christmas strategy module `rainbow-delimiters.strategy.christmas` (not + loaded by default) + +Fixed +===== + +- Query can be a function in configuration +- Priority can be a function in configuration +- Functions in configuration take buffer number as argument +- Updated Nim queries + + +[0.2.0] - 2023-11-26 +#################### + +Added +===== + +- Ability to set highlight priority +- Cue support +- Luadoc support +- Nim support +- Kotlin support +- templ support +- Terraform support +- TOML support + +Fixed +===== + +- Type error in local strategy +- Log error in local strategy (Neovim <0.10 only) +- Missing patterns for CSS + + - `feature_query` + - `arguments` + - `attribute_selector` + +- Missing patterns for Go + + - `array_type` + - `slice_expression` + +- Missing patterns for HCL + + - `for_tuple_expr` + - `new_index` + - `expression` + - `binary_operation` + - `for_object_expr` + - `template_interpolation` + - `unary_operation` + +- Missing pattern for Javascript and Typescript + + - `switch_body` + +- Missing patterns for Nix + + - `rec_attrset_expression` + - `inherit_from` + +- Missing pattern for SCSS + + - `parameters` + +Changed +======= + +- Default highlight priority is 110 instead of 210, which is between + Tree-sitter and LSP semantic tokens + + +[0.1.0] - 2023-11-12 +#################### + +Initial release + + + +.. ---------------------------------------------------------------------------- +.. _Keep a Changelog: https://keepachangelog.com/en/1.0.0/, +.. _Semantic Versioning: https://semver.org/spec/v2.0.0.html diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/CONTRIBUTING.rst b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/CONTRIBUTING.rst new file mode 100644 index 00000000..4781d54f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/CONTRIBUTING.rst @@ -0,0 +1,38 @@ +.. default-role:: code + +##################### + Contributor's guide +##################### + + +Adding support for a new language +################################# + +Languages are supported through language-specific queries. Each language needs +at lease one query which matches the most common delimiters, usually `(`, `)`, +`[`, `]`, `{`, `}`, `<` and `>`. Read `:h rb-delimiters-custom-query` in the +manual first to understand how to write queries. Your query should meet the +following criteria: + +- Named `rainbow-delimiters` +- Few `@delimiter` capture groups per `@container`; we do not want the default + query to be too vibrant +- Write one or more files in the language under `test/highlight/` (where + `` is the language); the standard file name is `regular.` +- The test code must have at least one instance of each pattern in the query +- The test code must not have parsing errors +- The test code should ideally have multiple levels of nesting +- The test code should compile and have no linter errors if that is feasible + (this is not a hard rule though) + +If there are many test cases or if the code becomes too verbose feel free to +create multiple test files. + +In addition to the queries and test file(s), please consider adding the type +annotations in `lua/rainbow-delimiters.types.lua` if you are adding queries +for a new language. You will need to update: + +- `@class rainbow_delimiters.config.strategies` +- `@class rainbow_delimiters.config.queries` +- `@class rainbow_delimiters.config.priorities` +- `@alias rainbow_delimiters.language` diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/HACKING.rst b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/HACKING.rst new file mode 100644 index 00000000..90009038 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/HACKING.rst @@ -0,0 +1,335 @@ +.. default-role:: code + +################################# + Hacking on Rainbow Delimiters 2 +################################# + + +Testing +####### + + +A test setup must meet the following criteria: + +- Test definitions must be run by with Neovim as the Lua interpreter to get + access to all Neovim APIs +- Tests must not be affected by the user's own plugins and configuration +- Each test which mutates editor state must run in its own Neovim process + +The first two points are achieved through a small command-line interface +adapter script (a shim). The shim exposes the command-line interface of a Lua +interpreter, and internally it sets up environment variable to point Neovim at +a prepared blank directory structure. Neovim is then called with the `-l` +flag. + +We do have to use some plugins though: + +- This plugin itself +- nvim-treesitter_ to install parsers for some languages + +Both plugins are stored under the `$XDG_DATA_HOME` directory, the former as a +symlink and the latter as a Git submodule. + +As for process isolation, this is achieved inside the tests. We start a +headless embedded Neovim instance which we control through MsgPack RPC from +inside the test. We can control and probe this process only indirectly, which +is awkward, but this is the best solution I could find. + + +Unit testing +============ + +We use busted_ for unit testing. A unit is a self-contained module which can +be used on its own independent of the editor. Execute `make unit-test` to run +unit tests. The `busted` binary must be available on the system `$PATH`. + +End to end testing +================== + +End-to-end tests run in a separate Neovim instance which we control via RPC. +These are tests which mutate the state of the editor, such as adding +highlighting on changes. Execute `make e2e-test` to run all end to end tests. + +Running tests with Neotest-busted +================================= + +To run tests the `g:bustedprg` variable must be set to `'./test/busted'`, which +is the path to the shim script. If the `exrc` option is set the variable will +be set automatically. + + + +Design decisions +################ + +Tables over strings for configuration +===================================== + +Strategies are given as a complex table, but a string identifier would have +been much more pleasant on the eye. Which of these two is easier to read and +write? + +.. code:: lua + + -- This? + settings = { + strategy = { + 'global' + html = 'local' + } + } + + -- Or this? + settings = { + strategy = { + require 'ts-rainbow.strategy.global' + html = require 'ts-rainbow.strategy.local' + } + } + +Using strings might seem like the more elegant choice, but it it makes the code +more complicated to maintain and less flexible for the user. With tables a +user can create a new custom strategy and assign it directly without the need +to "register" them first under some name. + +More importantly though, we have unlimited freedom where that table is coming +from. Suppose we wanted to add settings to a strategy. With string +identifiers we now need much more machinery to connect a string identifier and +its settings. On the other hand, we can just call a function with the settings +are arguments which returns the strategy table. + +.. code:: lua + + settings = { + strategy = { + require 'ts-rainbow.strategy.global', + -- Function call evaluates to a strategy table + latext = my_custom_strategy { + option_1 = true, + option_2 = 'test' + } + } + } + + +Strategies +########## + +On container nodes +================== + +Every query has to define a `container` capture in addition to `opening` and +`closing` captures. As humans we understand the code at an abstract level, but +Tree-sitter works on a more concrete level. To a human the HTML tag `
` is +one atomic object, but to Tree-sitter it is actually a container with further +elements. + +Consider the following HTML snippet: + +.. code:: html + +
+ Hello +
+ +The tree looks like this (showing anonymous nodes): + +.. code:: + + element [0, 0] - [2, 6] + start_tag [0, 0] - [0, 5] + "<" [0, 0] - [0, 1] + tag_name [0, 1] - [0, 4] + ">" [0, 4] - [0, 5] + text [1, 1] - [1, 6] + end_tag [2, 0] - [2, 6] + "" [2, 5] - [2, 6] + +We want to highlight the lower-level nodes like `tag_name` or `start_tag` and +`end_tag`, but we want to base our logic on the higher-level nodes like +`element`. The `@container` node will not be highlighted, we use it to +determine the nesting level or the relationship to other container nodes. + + +Determining the level of container node +======================================= + +In order to correctly highlight containers we need to know the nesting level of +each container relative to the other containers in the document. We can use +the order in which matches are returned by the `iter_matches` method of a +query. The iterator traverses the document tree in a depth-first manner +according to the visitor patter, but matches are created upon exiting a node. + +Let us look at a practical example. Here is a hypothetical tree: + +.. code:: + + A + ├─B + │ └─C + │ └─D + └─E + ├─F + └─G + +The nodes are returned in the following order: + +#) D +#) C +#) B +#) F +#) G +#) E +#) A + +We can only know how deeply nodes are nested relative to one another. We need +to build the entire tree structure to know the absolute nesting levels. Here +is an algorithm which can build up the tree, it uses the fact that the order of +nodes never skips over an ancestor. + +Start with an empty stack `s = []`. For each match `m` do the following: + +#) Keep popping matches off `s` up until we find a match `m'` whose + `@container` node is not a descendant of the container node of `m`. Collect + the popped matches (excluding `m'`) onto a new stack `s_m` (order does not + matter) +#) Set `s_m` as the child match stack of `m` +#) Add `m` to `s` + +Eventually `s` will only contain root-level matches, i.e. matches of nesting +level one. To apply the highlighting we can then traverse the match tree, +incrementing the highlighting level by one each time we descend a level. + +The order of matches among siblings in the tree does not matter. The above +algorithm uses a stack when collecting children, but any unordered +one-dimensional sequence will do. The stack `s` is important for determining +the relationship between nodes: since we know that no ancestors will be skipped +we can be certain that we can stop checking the stack for descendants of `m` +once we encounter the first non-descendant match. Otherwise we would have to +compare each match with each other match, which would tank the performance. + + +The local highlight strategy +============================ + +Consider the following bit of contrived HTML code: + +.. code:: html + +
+
+
+
+
+
+
+
+ +Supposed the cursor was inside the angle brackets of `Bravo`, which tags +should we highlight? From eyeballing the obvious answer is `Alpha`, `Bravo` +and `Charlie`. Obviously `Alpha` and `Bravo` both contain the cursor within +the range, but how do we know that we need to highlight `Charlie`? `Charlie` +is contained inside `Bravo`, which contains the cursor, but on the other hand +`Delta` is contained inside `Alpha`, which also contains the cursor. We cannot +simply check whether the parent contains the cursor. + +When working with the Tree-sitter API and iterating through matches and +captures we have no way of knowing that any of the captures within `Charlie` +are contained within `Bravo`. However, due to the order of traversal we do +know that `Bravo` is the lowest node to still contain the cursor. + +Therefore we that the first match which contains the cursor is the lowest one. +If a match does not contain the cursor we can check whether it is a +descendant of the cursor container match. + + +The problem with nested languages +################################# + +The language tree of a buffer is a tree of parsers. Some languages like +Markdown can contain other languages, which complicates things. + + +Foreign extmarks +================ + +Extmarks move along with the text they belong to. This is generally a good +thing, but it can become a problem if we move text from one language to +another. Consider the following Markdown code: + +.. code:: markdown + + Hello world + + ```lua + print {{{{}}}} + print {{{{}}}} + ``` + +We can move the cursor to line 4 and move that line out of the Lua block by +executing `:move 1` to move it to the second line. However, this will preserve +the extmarks and we will end up with Lua delimiter highlighting inside +Markdown. + +My solution is on every change to delete all rainbow delimiter extmarks which +do not belong to the current language. + + +Overwritten extmarks +==================== + +Take the following Markdown code: + +.. code:: markdown + + Hello world + + ```c + puts("This is an injected language") + { + { + { + { + { + return ((((((2)))))) + ((((3)))) + } + } + } + } + } + ``` + +If we put the cursor on the line with the `puts` statement and move it up one +line (`:move -2`) we get the following changes: + +- Markdown + - `{ 2, 0, 3, 0 }` + +This means lines 3 and 4 of the Markdown tree have changed; we have changed the +contents of the fifth line and added one more line. This is all as expected. +However, let us now move the line back down by executing `:move +1`. We get +the following changes: + +- Markdown + - `{ 3, 0, 15, 0 }` +- C + - `{ 3, 0, 4, 0 }` + +The changes to the C tree are what we expect. However, the changes to the +Markdown tree span the code block as well. This is a problem when we start +deleting foreign extmarks (see above). If we work from the outside we wipe out +all non-Markdown extmarks in the range, which includes the C extmarks. Then we +apply the C extmarks inside the C block, but the C change does not span the +entire C tree. Thus we will only apply highlighting to the changed C line, but +not the remainder of the C block. + +The solution at the moment is to overwrite the changes of nested languages. If +the changes belong to a language tree with parent language we replace all the +changes with a range that spans the entire tree for that language. + + + +.. _busted: https://lunarmodules.github.io/busted/#defining-tests +.. _nvim-treesitter: https://github.com/nvim-treesitter/nvim-treesitter diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/LICENSE b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/README.rst b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/README.rst new file mode 100644 index 00000000..4cf0a048 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/README.rst @@ -0,0 +1,229 @@ +.. default-role:: code + + +############################### + Rainbow delimiters for Neovim +############################### + +This Neovim plugin provides alternating syntax highlighting (“rainbow +parentheses”) for Neovim, powered by `Tree-sitter`_. The goal is to have a +hackable plugin which allows for different configuration of queries and +strategies, both globally and per file type. Users can override and extend the +built-in defaults through their own configuration. + +This is a fork of `nvim-ts-rainbow2`_, which was implemented as a module for +`nvim-treessiter`_. However, since nvim-treesitter has deprecated the module +system I had to create this standalone plugin. + + +Installation and setup +###################### + +Installation +============ + +Install it like any other Neovim plugin. You will need a Tree-sitter parser +for each language you want to use rainbow delimiters with. + +Setup +===== + +No configuration is needed to get started, this plugin has reasonable defaults +which you can override. Configuration is done by setting entries in the Vim +script dictionary `g:rainbow_delimiters`. Here is an example configuration: + +.. code:: vim + + let g:rainbow_delimiters = { + \ 'strategy': { + \ '': rainbow_delimiters#strategy.global, + \ 'vim': rainbow_delimiters#strategy.local, + \ }, + \ 'query': { + \ '': 'rainbow-delimiters', + \ 'lua': 'rainbow-blocks', + \ }, + \ 'priority': { + \ '': 110, + \ 'lua': 210, + \ }, + \ 'highlight': [ + \ 'RainbowDelimiterRed', + \ 'RainbowDelimiterYellow', + \ 'RainbowDelimiterBlue', + \ 'RainbowDelimiterOrange', + \ 'RainbowDelimiterGreen', + \ 'RainbowDelimiterViolet', + \ 'RainbowDelimiterCyan', + \ ], + \ } + +The equivalent code in Lua: + +.. code:: lua + + -- This module contains a number of default definitions + local rainbow_delimiters = require 'rainbow-delimiters' + + ---@type rainbow_delimiters.config + vim.g.rainbow_delimiters = { + strategy = { + [''] = rainbow_delimiters.strategy['global'], + vim = rainbow_delimiters.strategy['local'], + }, + query = { + [''] = 'rainbow-delimiters', + lua = 'rainbow-blocks', + }, + priority = { + [''] = 110, + lua = 210, + }, + highlight = { + 'RainbowDelimiterRed', + 'RainbowDelimiterYellow', + 'RainbowDelimiterBlue', + 'RainbowDelimiterOrange', + 'RainbowDelimiterGreen', + 'RainbowDelimiterViolet', + 'RainbowDelimiterCyan', + }, + } + +Please refer to the `manual`_ for more details. For those who prefer a `setup` +function there is the module `rainbow-delimiters.setup` that accepts all the +same parameters as `g:rainbow-delimiters`. + +.. code:: lua + + require('rainbow-delimiters.setup').setup { + strategy = { + -- ... + }, + query = { + -- ... + }, + highlight = { + -- ... + }, + } + + +Help wanted +########### + +There are only so many languages which I understand to the point that I can +write queries for them. If you want support for a new language please consider +contributing code. See the CONTRIBUTING_ for details. + + +Status of the plugin +#################### + +Tree-sitter support in Neovim is still experimental. This plugin and its API +should be considered stable insofar as breaking changes will only happen if +changes to Neovim necessitates them. + + +License +####### + +Licensed under the Apache-2.0 license. Please see the `LICENSE`_ file for +details. + + +Migrating from nvim-ts-rainbow2 +############################### + +Rainbow-Delimiters uses different settings than nvim-ts-rainbow2, but +converting the configuration is straight-forward. The biggest change is where +the settings are stored. + +- Settings are stored in the global variable `g:rainbow-delimiters`, which has + the same keys as the old settings +- The default strategy and query have index `''` (empty string) instead of `1` +- Default highlight groups have the prefix `RainbowDelimiter` instead of + `TSRainbow`, e.g. `RainbowDelimiterRed` instead of `TSRainbowRed` +- The default query is now called `rainbow-delimiters` instead of + `rainbow-parens` +- The public Lua module is called `rainbow-delimiters` instead of `ts-rainbow` + +The name of the default query is now `rainbow-delimiters` because for some +languages like HTML the notion of "parentheses" does not make any sense. In +HTML the only meaningful delimiter is the tag. Hence the generic notion of a +"delimiter". + + +Attribution +########### + +This is a fork of a previous Neovim plugin, the original repository is +available under https://sr.ht/~p00f/nvim-ts-rainbow/. + +Attributions from the original author +===================================== + +Huge thanks to @vigoux, @theHamsta, @sogaiu, @bfredl and @sunjon and +@steelsojka for all their help + + +.. _Tree-sitter: https://tree-sitter.github.io/tree-sitter/ +.. _nvim-treesitter: https://github.com/nvim-treesitter/nvim-treesitter +.. _CONTRIBUTING: CONTRIBUTING.rst +.. _LICENSE: LICENSE +.. _manual: doc/rainbow-delimiters.txt +.. _neovim/neovim#17099: https://github.com/neovim/neovim/pull/17099 +.. _nvim-ts-rainbow2: https://gitlab.com/HiPhish/nvim-ts-rainbow2 +.. _nvim-treessiter: https://github.com/nvim-treesitter/nvim-treesitter + + +Screenshots +########### + +Bash +==== + +.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/514ed2a2-68a4-427e-aef6-7ac3a02a2ba0 + :alt: Screenshot of a Bash script with alternating coloured delimiters + +C += + +.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/45f8e727-d507-43df-b112-a269e7262533 + :alt: Screenshot of a C program with alternating coloured delimiters + +Common Lisp +=========== + +.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/5e7e15bb-a4e3-41e5-b3fc-3c4150ffd252 + :alt: Screenshot of a Common Lisp program with alternating coloured delimiters + +HTML +==== + +.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/371d310c-d5a7-490d-bb55-d3fe4bd8b1a8 + :alt: Screenshot of an HTML document with alternating coloured delimiters + +Java +==== + +.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/bb372051-ec5f-4c0b-a9b9-3cd37edafa4f + :alt: Screenshot of a Java program with alternating coloured delimiters + +LaTeX +===== + +Using the `rainbow-blocks` query to highlight the entire `\begin` and `\end` +instructions. + +.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/0176cc0d-b729-417e-8f85-c31da70d49f5 + :alt: Screenshot of a LaTeX document with alternating coloured delimiters + +Lua +=== + +Using the `rainbow-blocks` query to highlight the entire keywords like +`function`, `if`, `else` and `end`. + +.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/a915f7e0-b1c9-4af2-ae1d-f2f48aa325e5 + :alt: Screenshot of a Lua script with alternating coloured delimiters diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/TODO.rst b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/TODO.rst new file mode 100644 index 00000000..3fba3a70 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/TODO.rst @@ -0,0 +1,36 @@ +.. default-role:: code + +####################### + Plans for this plugin +####################### + + +Built-in queries +################ + +As of version 0.8.3 Neovim can only match one node per query. This is a +limitation of Neovim and there is nothing that can be done on this end. + +The global query does not updated the highlighting of injected languages. + + +Queries which I cannot port +=========================== + +I do not know enough about the following languages in order to write good +queries. Contributions are welcome. + +- devicetree +- elm +- gdscript +- graphql +- kotlin +- meson +- ocaml +- ocaml_interface +- scala +- solidity +- sparql +- supercollider +- svelte +- turtle diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/autoload/rainbow_delimiters.vim b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/autoload/rainbow_delimiters.vim new file mode 100644 index 00000000..27c1adbb --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/autoload/rainbow_delimiters.vim @@ -0,0 +1,50 @@ +" Copyright 2023 Alejandro "HiPhish" Sanchez +" +" Licensed under the Apache License, Version 2.0 (the "License"); +" you may not use this file except in compliance with the License. +" You may obtain a copy of the License at +" +" http://www.apache.org/licenses/LICENSE-2.0 +" +" Unless required by applicable law or agreed to in writing, software +" distributed under the License is distributed on an "AS IS" BASIS, +" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +" See the License for the specific language governing permissions and +" limitations under the License. + +let g:rainbow_delimiters#strategy = { + \ 'global': luaeval("require 'rainbow-delimiters.strategy.global'"), + \ 'local': luaeval("require 'rainbow-delimiters.strategy.local'"), + \ 'noop': luaeval("require 'rainbow-delimiters.strategy.no-op'"), +\ } + +" Get the appropriate highlight group for the given level of nesting. +function! rainbow_delimiters#hlgroup_at(i) + return luaeval("require('rainbow-delimiters').hlgroup_at(_A)", a:i) +endfunction + +" Disable highlighting for the given buffer. Buffer number zero means current +" buffer. +function! rainbow_delimiters#disable(bufnr) + call luaeval("require('rainbow-delimiters').disable(_A)", a:bufnr) +endfunction + +" Enable highlighting for the given buffer. Buffer number zero means current +" buffer. +function! rainbow_delimiters#enable(bufnr) + call luaeval("require('rainbow-delimiters').enable(_A)", a:bufnr) +endfunction + +" Toggle highlighting for the given buffer. Buffer number zero means current +" buffer. +function! rainbow_delimiters#toggle(bufnr) + call luaeval("require('rainbow-delimiters').toggle(_A)", a:bufnr) +endfunction + +" Check if highlighting is enabled for the given buffer. Buffer number zero +" means current buffer. +function! rainbow_delimiters#is_enabled(bufnr) + return luaeval("require('rainbow-delimiters').is_enabled(_A)", a:bufnr) +endfunction + +" vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/doc/rainbow-delimiters.txt b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/doc/rainbow-delimiters.txt new file mode 100644 index 00000000..1ae6c4b2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/doc/rainbow-delimiters.txt @@ -0,0 +1,721 @@ +*rainbow-delimiters.txt* Alternating highlight for delimiters in code + + *rainbow-delimiters* + *rb-delimiters* + +Author: Alejandro "HiPhish" Sanchez +License: Apache-2.0 +Version: 0.4.0 + + +============================================================================== +TABLE OF CONTENTS *rb-delimiters-contents* + +1. Introduction .............................. |rb-delimiters-intro| +2. Setup and configuration ................... |rb-delimiters-setup| + 2.1 Highlight colors ...................... |rb-delimiters-colors| + 2.2 Strategies ............................ |rb-delimiters-strategy| + 2.3 Queries ............................... |rb-delimiters-query| + 2.4 Logging ............................... |rb-delimiters-logging| +3. Extending ................................. |rb-delimiters-extending| + 3.1 The library ........................... |rb-delimiters-api| + 3.2 Custom queries ........................ |rb-delimiters-custom-query| + 3.3 Custom strategies ..................... |rb-delimiters-custom-strategy| + 3.4 Adding new languages .................. |rb-delimiters-custom-lang| +4. Acknowledgements .......................... |rb-delimiters-credit| +5. Further reading ........................... |rb-delimiters-reading| + + +============================================================================== +INTRODUCTION *rb-delimiters-intro* + +This plugin provides alternating highlighting for delimiters in Neovim, also +known as "rainbow parentheses". Thanks to the built-in |treesitter| support +we are not limited to just parentheses. We can match any part of the document +tree, such as HTML tags or `do` / `end` pairs in Lua. We can define new +patterns for existing languages, add support for new languages and even change +the strategy used for highlighting. + + +============================================================================== +SETUP AND CONFIGURATION *rb-delimiters-setup* + +Install Rainbow-Delimiters like any other Neovim plugin. You also need a +Tree-sitter parser for each language to want to support. + + *g:rainbow_delimiters* +Configuration is done through the variable `g:rainbow_delimiters`. It is a +dictionary which can be defined both in Vim script and in Lua. The following +keys are recognized: + +`strategy` + Dictionary mapping Tree-sitter language names to strategies. The empty + string is the key for the default strategy. See |rb-delimiters-strategy| + for more information about strategies. + +`query` + Dictionary mapping Tree-sitter language names to queries. The empty string + is the key for the default query. See |rb-delimiters-query| for more + information about queries. + +`priority` + Dictionary mapping Tree-sitter language names to highlight priority values. + The empty string is the key for the default priority. See + |vim.highlight.priorities| and |treesitter-highlight-priority| for more + information on priorities. + +`highlight` + List of names of the highlight groups to use for highlighting, for more + information see |rb-delimiters-colors|. + +`whitelist` + List of Tree-sitter languages for which to enabled rainbow delimiters. + Rainbow delimiters will be disabled for all other languages. + +`blacklist` + List of Tree-sitter languages for which to disabled rainbow delimiters. + Rainbow delimiters will be enabled for all other languages. + +`log` + Settings for logging information. This is a dictionary which contains + further settings. See |rb-delimiters-logging| for details. + +If neither the white- nor the blacklist are set rainbow delimiters will be +enabled for all languages. If both lists are set it is undefined which will +take precedence. + + +Here is an example configuration: +>vim + let g:rainbow_delimiters = { + \ 'strategy': { + \ '': rainbow_delimiters#strategy.global, + \ 'vim': rainbow_delimiters#strategy.local, + \ }, + \ 'query': { + \ '': 'rainbow-delimiters', + \ 'lua': 'rainbow-blocks', + \ }, + \ 'priority': { + \ '': 110, + \ 'lua': 210, + \ }, + \ 'highlight': [ + \ 'RainbowDelimiterRed', + \ 'RainbowDelimiterYellow', + \ 'RainbowDelimiterBlue', + \ 'RainbowDelimiterOrange', + \ 'RainbowDelimiterGreen', + \ 'RainbowDelimiterViolet', + \ 'RainbowDelimiterCyan', + \ ], + \ 'blacklist': ['c', 'cpp'], + \ } +< + +Alternatively, the same configuration in Lua: +>lua + -- This module contains a number of default definitions + local rainbow_delimiters = require 'rainbow-delimiters' + + ---@type rainbow_delimiters.config + vim.g.rainbow_delimiters = { + strategy = { + [''] = rainbow_delimiters.strategy['global'], + commonlisp = rainbow_delimiters.strategy['local'], + }, + query = { + [''] = 'rainbow-delimiters', + lua = 'rainbow-blocks', + }, + priority = { + [''] = 110, + lua = 210, + }, + highlight = { + 'RainbowDelimiterRed', + 'RainbowDelimiterYellow', + 'RainbowDelimiterBlue', + 'RainbowDelimiterOrange', + 'RainbowDelimiterGreen', + 'RainbowDelimiterViolet', + 'RainbowDelimiterCyan', + }, + blacklist = {'c', 'cpp'}, + } +< + + *rainbow-delimiters.setup* +'rainbow-delimiters.setup'.setup({config}) + +Some people prefer to call a Lua `setup` function, so a setup function is +available as part of a Lua module. +>lua + require 'rainbow-delimiters.setup'.setup { + strategy = { + [''] = rainbow_delimiters.strategy['global'], + commonlisp = rainbow_delimiters.strategy['local'], + }, + query = { + [''] = 'rainbow-delimiters', + latex = 'rainbow-blocks', + }, + highlight = { + 'RainbowDelimiterRed', + 'RainbowDelimiterYellow', + 'RainbowDelimiterBlue', + 'RainbowDelimiterOrange', + 'RainbowDelimiterGreen', + 'RainbowDelimiterViolet', + 'RainbowDelimiterCyan', + }, + blacklist = {'c', 'cpp'}, + } +< +The keys are exactly the same as for |g:rainbow_delimiters|. In fact, this +function does the same as setting the variable directly. + +As an aside, this is a bad practice carried over from a time when Lua support +in Neovim still had issues with Vim script interoperability, but it has +persisted through cargo-culting. You are better off not using this function. + +------------------------------------------------------------------------------ +HIGHLIGHT COLORS *rb-delimiters-colors* + +The `highlight` setting controls which highlight group to apply. It is a list +of any number of highlight group names as strings. The default values are in +this order: + +- `RainbowDelimiterRed` +- `RainbowDelimiterYellow` +- `RainbowDelimiterBlue` +- `RainbowDelimiterOrange` +- `RainbowDelimiterGreen` +- `RainbowDelimiterViolet` +- `RainbowDelimiterCyan` + +These are non-standard highlight groups and I have tried to find reasonable +default values for most uses. Nevertheless, you probably want to redefine +them for your colour scheme or link them to some existing group. + +The colors are intentionally not in the order of the rainbow to help make the +contrast between adjacent delimiters more noticeable. Re-order the groups in +your settings if you prefer a different order. + +Example highlight group definitions: +>vim + " Link to an existing highlight group + highlight link RainbowDelimiterRed WarningMsg + + " Define the highlight from scratch + highlight RainbowDelimiterOrange guifg=#d65d0e ctermfg=White +< +You will probably want to have different colours per theme. Since most themes +will lack definitions for the above groups you will need to hook in somehow. +A simple solution is to use an autocommand. +>vim + au ColorSchemePre MyTheme highlight link RainbowDelimiter MyThemeRed + au ColorSchemePre MyTheme highlight link RainbowDelimiter MyThemeYellow + " and so on... +< + + +------------------------------------------------------------------------------ +STRATEGIES *rb-delimiters-strategy* + +A strategy defines how to perform the highlighting of delimiters. For +example, the included global strategy highlights every delimiter in a buffer +and updates the highlights when the document tree changes. On the other hand, +the included local strategy highlights only the sub-tree of the document which +contains the cursor and is updated whenever the cursor moves. + +The strategy is set globally with per-language overrides. The setting is a +dictionary where the empty string is the key for the default strategy and the +overrides use the name of the language as keys. + +Each value can be either a strategy or a function which evaluates to a +strategy. A function can be used to defer the decision to a later point in +time. + +The function has the following signature: + + Parameters: ~ + • {bufnr} Number of the buffer to highlight + + Return: ~ + Either a strategy or `nil` (or |v:null|). If `nil`, rainbow delimiters + will be disabled for that buffer. + +NOTE +Functions can only be used from Lua. + +>lua + local rainbow = require 'rainbow-delimiters' + + strategy = { + -- Use global strategy by default + [''] = rainbow.strategy['global'], + -- Use local for HTML + html = rainbow.strategy['local'], + -- Pick the strategy for LaTeX dynamically based on the buffer size + latex = function(bufnr) + -- Disabled for very large files, global strategy for large files, + -- local strategy otherwise + local line_count = vim.api.nvim_buf_line_count(bufnr) + if line_count > 10000 then + return nil + elseif line_count > 1000 then + return rainbow.strategy['global'] + end + return rainbow.strategy['local'] + end + } +< + +A strategy is a table which must contain specific fields. It is possible to +define your own strategy, see |rb-delimiters-custom-strategy|. The following +strategies are included: + + *rb-delimiters.strategy.global* +Global~ +'rainbow-delimiters'.strategy['global'] + +The default strategy, highlights the entire buffer. Has very simple logic. + + + *rb-delimiters.strategy.local* +Local~ +'rainbow-delimiters'.strategy['local'] + +Based on the cursor position highlights only the sub-tree which contains the +cursor. Updated every time the cursor moves and uses more complex logic than +the global strategy to figure out which nodes exactly to highlight. + + + *rb-delimiters.strategy.noop* +No-op~ +'rainbow-delimiters'.strategy['noop'] + +A dummy strategy which does nothing. This is only useful in testing or if you +really want an empty strategy. + + +------------------------------------------------------------------------------ +QUERIES *rb-delimiters-query* + +A query defines what to match. Every language needs its own custom query. +The query setting is a table where each entry maps a language name to a query +name. The empty string is the key for the default query. + +Each value in the table can be either the name of a query file or a function +which evaluates to the name of a query file. A function can be used to defer +the decision to a later point in time. + +The function has the following signature: + + Parameters: ~ + • {bufnr} Number of the buffer to highlight + + Return: ~ + The name of the query as a string. + + +NOTE +Functions can only be used from Lua. + +>lua + query = { + -- Use parentheses by default + [''] = 'rainbow-delimiters', + -- Use blocks for Lua + lua = 'rainbow-blocks', + -- Determine the query dynamically + query = function(bufnr) + -- Use blocks for read-only buffers like in `:InspectTree` + local is_nofile = vim.bo[bufnr].buftype == 'nofile' + return is_nofile and 'rainbow-blocks' or 'rainbow-delimiters' + end + } +< +If you wish to define your own custom query or add support for a new language, +consult |rb-delimiters-custom-query| for details. + +For every language the query `rainbow-delimiters` is defined, which matches a +reasonable set of parentheses and similar delimiters for each language. In +addition there are the following extra queries for certain languages: + +- `latex` + - `rainbow-blocks` Matches `\begin` and `\end` instructions +- `lua` + - `rainbow-blocks` Matches keyword delimiters like like `function` and + `end`, in addition to parentheses +- `javascript` + - `rainbow-delimiters-react` Includes React support, set by default for + Javascript files + - `rainbow-parens` Only parentheses without React tags + - `rainbow-tags-react` Only React tags without parentheses +- `query` + - `rainbow-blocks` Highlight named nodes and identifiers in addition to + parentheses (useful for |:InspectTree|) +- `tsx` + - `rainbow-parens` Just Typescript highlighting without React tags + - `rainbow-tags-react` Only React tags without Typescript highlighting +- `typescript` + - `rainbow-parens` Just Typescript highlighting without React tags +- `verilog` + - `rainbow-blocks` Matches keyword delimiters like `begin` and `end`, in + addition to parentheses + + +------------------------------------------------------------------------------ +LOGGING *rb-delimiters-logging* + +By default only errors are logged. You can adjust what and how to log by +adjusting the values of the `log` entry in the settings. For information how +to change settings. see |rb-delimiters-setup|. + +The following settings are supported: + +`file` + Path to the log file, default is `rainbow-delimiters.log` in your standard + log path (see |standard-path|). + +`level` + Only messages equal to or above this value will be logged. The default is + to log warnings or above. See |log_levels| for possible values. + +The log file format is a CSV file which uses the `TAB` character (ASCII +`0x09`) as the field separator and a `NL` (ASCII `0x0A`) as the record +separator. + +The fields are in this order: + +- Time stamp of when the message was logged in ISO 8601 format with time zone +- Log level as string +- Lua module from which the message was logged, or the empty string if outside + a module +- The logged message + + + +============================================================================== +EXTENDING RAINBOW DELIMITERS + +Rainbow delimiters are hackable, you can add your own strategies, queries for +existing languages or even queries for new languages. Strategies and queries +are split up to be independent and can be mixed arbitrarily, but there are +some rules which need to be followed. + + +------------------------------------------------------------------------------ +THE LIBRARY *rb-delimiters-api* + +There is a utility library provided for people writing their own strategies. +It is available as a table under the Lua module `'rainbow-delimiters'`. + + + *rb-delimiters.enable* + *rb_delimiters#enable* +'rainbow-delimiters'.enable({bufnr}) + Re-enable rainbow delimiters for the buffer {bufnr} (or the current buffer + if {bufnr} is `0`) after it has been disabled. + +rainbow_delimiters#enable({bufnr}) + Vim script binding for the above function. + + + *rb-delimiters.disable* + *rb_delimiters#disable* +'rainbow-delimiters'.disable({bufnr}) + Disable rainbow delimiters for the buffer {bufnr} (or the current buffer + if {bufnr} is `0`). + +rainbow_delimiters#disable({bufnr}) + Vim script binding for the above function. + + + *rb-delimiters.toggle* + *rb_delimiters#toggle* +'rainbow-delimiters'.toggle({bufnr}) + Toggle rainbow delimiters for the buffer {bufnr} (or the current buffer + if {bufnr} is `0`). + +rainbow_delimiters#toggle({bufnr}) + Vim script binding for the above function. + + + *rb-delimiters.is_enabled* + *rb_delimiters#is_enabled* +'rainbow-delimiters'.is_enabled({bufnr}) + Check if rainbow delimiters are enabled for the buffer {bufnr} (or the + current buffer if {bufnr} is `0`). + +rainbow_delimiters#is_enabled({bufnr}) + Vim script binding for the above function. + + + *rb-delimiters.hlgroup_at* + *rainbow-delimiters#hlgroup_at* +'rainbow-delimiters'.hlgroup_at({nesting_level}) + Gets the name of the highlight group set up at the given nesting level. + This function will properly roll over, meaning that if there are seven + highlight groups defined and the {nesting_level} is nine, you will get the + second highlight group. + +rainbow-delimiters#hlgroup_at({nesting_level}) + Vim script binding for the above function. + + + *rb-delimiters.strategy* + *g:rainbow_delimiters#strategy* +'rainbow-delimiters'.strategy + Table of included strategies. For more information about strategies see + |rb-delimiters-strategy|. The included ones are: + + - `global` |rb-delimiters.strategy.global| + - `local` |rb-delimiters.strategy.local| + - `noop` |rb-delimiters.strategy.noop| + + Do not add your own strategies to this table. + +g:rainbow_delimiters#strategy + Vim script dictionary, equivalent of the above table with the same keys. + + +------------------------------------------------------------------------------ +CUSTOM STRATEGIES *rb-delimiters-custom-strategy* + +A strategy is a table which must contain a certain set of fields. In +object-oriented terminology we would say that a strategy table must implement +the strategy protocol. +> + strategy = { + on_attach = function(bufnr: integer, settings: table), + on_detach = function(bufnr: integer), + on_reset = function(bufnr: integer, settings: table), + } +< + +------------------------------------------------------------------------------ +on_attach({bufnr}, {settings}) + +This function takes two arguments: the number of the buffer and the table of +settings used by the buffer. This function is generally used to set up +autocommands or other callbacks for events when the highlighting needs to be +updated. + +The settings table contains the following entries: + + - `strategy` Strategy in use + - `parser` Reference to the buffer parser (|treesitter-languagetree|) + - `lang` Language of the current parser + +A strategy should pick the settings it needs and either cache them in an +internal table, or construct closures (e.g. for callback functions) around +them. + +------------------------------------------------------------------------------ +on_detach({bufnr}) + +This function takes one argument: the number of the buffer. This function is +generally used to clean up any custom state, autocommands and callbacks set up +in the `on_attach` function. + +------------------------------------------------------------------------------ +on_reset({bufnr}, {settings}) + +Similar to `on_attach` with the same signature, except that this function is +called when the buffer has been reset in some way, for example if the +underlying file has been modified by a code formatter. Usually the strategy +should highlight the entire buffer from scratch again because we cannot rely +on Tree-sitter to tell us what has changed. + +As a rule of thumb, `on_reset` should do the work of `on_attach`, minus all +the initial setup. + +------------------------------------------------------------------------------ +The logic within the strategy can vary wildly between strategies. Usually you +will want to install some callback in the `on_attach` function. That callback +can then use the Tree-sitter API and the utility library (see +|rb-delimiters-api|) to select which nodes to highlight and what highlight +group to apply. + +See |rb-delimiters-custom-query| for the standard capture groups used. +Selecting standard capture groups allows your strategy to work with any of the +built-in queries as well as user-specified custom queries. + + +------------------------------------------------------------------------------ +CUSTOM QUERIES *rb-delimiters-custom-query* + +A query defines what exactly needs to be highlighted. Different languages +have different document trees, so you need a separate query for each language. +The queries need to define the following capture groups: + +- `@container` + The entire delimited node. +- `@delimiter` + Any delimiter you want to highlight in the current `@container`. +- `@sentinel` + A marker used to signal that you are done with the `@container`. This + should almost always be put right after the last `@delimiter` in the given + `@container`. +- `@_` + Delimiters starting with `_` (underscore) are ignored for highlighting + purposes, but you can use them for treesitter predicates like + `#eq?`, `#any-eq?`, etc. (These are very rarely needed.) + +`@container` and `@sentinel` are mandatory, and `@delimiter` will always be +present as well since `@delimiter` is what is highlighted. The captures +starting with underscore will be rarely used, since you only need them for +predicates in a few special cases. + + +Let's look at an example first. Here is a snippet of HTML code: +>html + + Example
link +
+< + +The corresponding document tree including anonymous nodes is as follows: +>query + (element ; [0, 0] - [2, 4] + (start_tag ; [0, 0] - [0, 30] + "<" ; [0, 0] - [0, 1] + (tag_name) ; [0, 1] - [0, 2] + (attribute ; [0, 3] - [0, 29] + (attribute_name) ; [0, 3] - [0, 7] + "=" ; [0, 7] - [0, 8] + (quoted_attribute_value ; [0, 8] - [0, 29] + "\"" ; [0, 8] - [0, 9] + (attribute_value) ; [0, 9] - [0, 28] + "\"")) ; [0, 28] - [0, 29] + ">") ; [0, 29] - [0, 30] + (text) ; [1, 4] - [1, 11] + (element ; [1, 11] - [1, 16] + (self_closing_tag ; [1, 11] - [1, 16] + "<" ; [1, 11] - [1, 12] + (tag_name) ; [1, 12] - [1, 14] + "/>")) ; [1, 14] - [1, 16] + (text) ; [1, 16] - [1, 20] + (end_tag ; [2, 0] - [2, 4] + "")) ; [2, 3] - [2, 4] +< + +As a human I immediately perceive the entire link as one object with two +delimiters: the opening `` tag and the closing `` tag. Perhaps the +self-closing `
` tag can be seen as an intermediate delimiter because it +does not open a new scope. On the other hand, it is part of the content of +the entire link, not one of its delimiters. + +As you can see, it is up to interpretation as to what exactly constitutes a +delimiter. In this example for the sake of exhaustiveness we will consider +the `
` tag a delimiter. The corresponding query is as follows: +>query + (element + (start_tag) @delimiter + (element + (self_closing_tag) @delimiter)? ; Optional! + (end_tag) @delimiter @sentinel) @container +< +Highlighting the entire tag might be too vibrant though. What if we want to +highlight only the opening and closing angle brackets? The query gets +slightly more complex because we have to descend deeper into the document +tree. +>query + (element + ((start_tag + ["<" ">"] @delimiter) + (element + (self_closing_tag + ["<" "/>"] @delimiter))? ;Optional! + (end_tag + "" @delimiter @sentinel))) @container +< +Note that we don't want to put the `@sentinel` on the second to last `@delimiter` +`"query + (element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container +< +Here both opening and closing tag have three delimiters each. + +In HTML the terminating slash in a self-closing tag is optional. Instead of +`
` we can write `
`. A naïve query would look like this: +>query + (element + (start_tag + "<" @delimiter + (tag_name) @delimiter @_tag_name + ">" @delimiter @sentinel)) @container +< +However, this query also matches the opening tag of regular tags like `
`. +This is where the `@_tag_name` capture comes in. The set of self-closing tags +is finite, so we can list them explicitly. This way a regular opening tag +will not match this particular pattern. +>query + (element + (start_tag + "<" @delimiter + (tag_name) @delimiter @_tag_name + ">" @delimiter @sentinel) + ;; List abridged for brevity + (#any-of? @_tag_name "br" "hr" "input")) @container +< +We need the `@_tag_name` capture so that it can be used with the `#any-of?` +predicate (|treesitter-predicate-any-of?|), but the capture itself is not used +for highlighting. + + +------------------------------------------------------------------------------ +ADDING SUPPORT FOR NEW LANGUAGES *rb-delimiters-custom-lang* + +Supporting a new new language requires creating one or more queries for the +language. If the query is mature enough please consider upstreaming it so +everyone can benefit. + + +============================================================================== +ACKNOWLEDGMENTS *rb-delimiters-credit* + +The original version of nvim-ts-rainbow was written by Chinmay "p00f" Dalal, +and discontinued in January of 2023. The original repositories can be found +under these URLs: + +- https://sr.ht/~p00f/nvim-ts-rainbow/ +- https://github.com/p00f/nvim-ts-rainbow + + +============================================================================== +FURTHER READING *rb-delimiters-reading* + +- nvim-treesitter plugin https://github.com/nvim-treesitter/nvim-treesitter +- Official Tree-sitter website https://tree-sitter.github.io/tree-sitter/ +- Neovim Tree-sitter documentation: |treesitter.txt| + + +============================================================================== + vim:tw=78:ts=8:sw=4:et:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters.lua new file mode 100644 index 00000000..815da3c8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters.lua @@ -0,0 +1,75 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + Copyright 2020-2022 Chinmay Dalal + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +local lib = require 'rainbow-delimiters.lib' + +---Disable rainbow delimiters for a given buffer. +---@param bufnr integer Buffer number, zero for current buffer. +local function disable(bufnr) + if not bufnr or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end + lib.detach(bufnr) + lib.buffers[bufnr] = false +end + +---Enable rainbow delimiters for a given buffer. +---@param bufnr integer Buffer number, zero for current buffer. +local function enable(bufnr) + if not bufnr or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end + lib.buffers[bufnr] = nil + lib.attach(bufnr) +end + +---Toggle rainbow delimiters for a given buffer. +---@param bufnr integer Buffer number, zero for current buffer. +local function toggle(bufnr) + if not bufnr or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end + if lib.buffers[bufnr] then + disable(bufnr) + else + enable(bufnr) + end +end + +---Check if rainbow delimiters are enabled for a given buffer. +---@param bufnr integer Buffer number, zero for current buffer. +---@return boolean # Whether or not rainbow delimiters is enabled +local function is_enabled(bufnr) + if not bufnr or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end + return lib.buffers[bufnr] ~= nil and lib.buffers[bufnr] ~= false +end + +---Public API for use in writing strategies or other custom code. +local M = { + hlgroup_at = lib.hlgroup_at, + ---Available default highlight strategies + strategy = { + ---Global highlighting strategy + ['global'] = require 'rainbow-delimiters.strategy.global', + ---Local highlighting strategy + ['local'] = require 'rainbow-delimiters.strategy.local', + ---Empty highlighting strategy for testing + ['noop'] = require 'rainbow-delimiters.strategy.no-op', + }, + enable = enable, + disable = disable, + toggle = toggle, + is_enabled = is_enabled, +} + + +return M +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters.types.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters.types.lua new file mode 100644 index 00000000..80c730a5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters.types.lua @@ -0,0 +1,311 @@ +---@meta + + +--# Utility Library #-- + +---Strategy to use for highlighting with rainbow-delimiters +---Must implement `on_attach`, `on_detach` and `on_reset` +---@class rainbow_delimiters.strategy +---`on_attach`: setup the highlighting on attach +---@field on_attach fun(bufnr: integer, settings: rainbow_delimiters.buffer_settings) +---`on_detach`: remove any unneccesary remaining setup on detach +---@field on_detach fun(bufnr: integer) +---`on_reset`: update the highlighting on reset +---@field on_reset fun(bufnr: integer, settings: rainbow_delimiters.buffer_settings) + +---@class (exact) rainbow_delimiters.buffer_settings +---@field strategy rainbow_delimiters.strategy +---@field parser LanguageTree +---@field lang string + + +--# Config #-- + +---Configuration table for rainbow-delimiters +---@class (exact) rainbow_delimiters.config +---Strategy to use for highlighting +---@field strategy rainbow_delimiters.config.strategies? +---Query to use for highlighting +---@field query rainbow_delimiters.config.queries? +---Highlight priority of rainbow delimiters +---@field priority rainbow_delimiters.config.priorities? +---Highlight colors +---@field highlight string[]? +---Whitelist for languages to highlight +---@field whitelist rainbow_delimiters.language[]? +---Blacklist for languages not to highlight +---@field blacklist rainbow_delimiters.language[]? +---Logging with log file and log level +---@field log rainbow_delimiters.logging? + +---@class rainbow_delimiters.config.strategies +---@field [''] (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field astro (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field bash (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field c (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field c_sharp (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field clojure (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field commonlisp (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field cpp (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field css (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field cuda (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field cue (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field dart (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field elixir (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field elm (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field fennel (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field fish (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field go (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field haskell (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field hcl (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field html (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field janet_simple (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field java (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field javascript (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field json (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field json5 (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field jsonc (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field jsonnet (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field julia (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field kotlin (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field latex (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field lua (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field luadoc (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field make (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field markdown (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field nim (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field nix (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field perl (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field php (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field python (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field query (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field r (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field racket (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field rasi (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field regex (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field rst (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field ruby (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field rust (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field scheme (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field scss (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field sql (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field starlark (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field templ (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field terraform (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field toml (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field tsx (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field typescript (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field typst (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field verilog (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field vim (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field vimdoc (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field vue (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field yaml (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---@field zig (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? +---User defined language, not part of rainbow_delimiters support +---@field [string] (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)? + +---@class rainbow_delimiters.config.queries +---@field [''] (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field astro (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field bash (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field c (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field c_sharp (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field clojure (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field commonlisp (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field cpp (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field css (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field cuda (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field cue (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field dart (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field elixir (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field elm (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field fennel (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field fish (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field go (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field haskell (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field hcl (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field html (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field janet_simple (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field java (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field javascript (('rainbow-delimiters' | 'rainbow-parens' | 'rainbow-delimiters-react' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-parens' | 'rainbow-delimiters-react' | string))? +---@field json (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field json5 (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field jsonc (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field jsonnet (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field julia (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field kotlin (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field latex (('rainbow-delimiters' | 'rainbow-blocks' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-blocks' | string))? +---@field lua (('rainbow-delimiters' | 'rainbow-blocks' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-blocks' | string))? +---@field luadoc (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field make (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field markdown (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field nim (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field nix (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field perl (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field php (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field python (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field query (('rainbow-delimiters' | 'rainbow-blocks' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-blocks' | string))? +---@field r (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field racket (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field rasi (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field regex (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field rst (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field ruby (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field rust (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field scheme (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field scss (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field sql (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field starlark (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field templ (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field terraform (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field toml (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field tsx (('rainbow-delimiters' | 'rainbow-parens' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-parens' | string))? +---@field typescript (('rainbow-delimiters' | 'rainbow-parens' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-parens' | string))? +---@field typst (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field verilog (('rainbow-delimiters' | 'rainbow-blocks' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-blocks' | string))? +---@field vim (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field vimdoc (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field vue (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field yaml (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---@field zig (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))? +---User defined language, not part of rainbow_delimiters support +---@field [string] (string | fun(bufnr: integer): string)? + +---@class rainbow_delimiters.config.priorities +---@field [''] (integer | fun(bufnr: integer): integer)? +---@field astro (integer | fun(bufnr: integer): integer)? +---@field bash (integer | fun(bufnr: integer): integer)? +---@field c (integer | fun(bufnr: integer): integer)? +---@field c_sharp (integer | fun(bufnr: integer): integer)? +---@field clojure (integer | fun(bufnr: integer): integer)? +---@field commonlisp (integer | fun(bufnr: integer): integer)? +---@field cpp (integer | fun(bufnr: integer): integer)? +---@field css (integer | fun(bufnr: integer): integer)? +---@field cuda (integer | fun(bufnr: integer): integer)? +---@field cue (integer | fun(bufnr: integer): integer)? +---@field dart (integer | fun(bufnr: integer): integer)? +---@field elixir (integer | fun(bufnr: integer): integer)? +---@field elm (integer | fun(bufnr: integer): integer)? +---@field fennel (integer | fun(bufnr: integer): integer)? +---@field fish (integer | fun(bufnr: integer): integer)? +---@field go (integer | fun(bufnr: integer): integer)? +---@field haskell (integer | fun(bufnr: integer): integer)? +---@field hcl (integer | fun(bufnr: integer): integer)? +---@field html (integer | fun(bufnr: integer): integer)? +---@field janet_simple (integer | fun(bufnr: integer): integer)? +---@field java (integer | fun(bufnr: integer): integer)? +---@field javascript (integer | fun(bufnr: integer): integer)? +---@field json (integer | fun(bufnr: integer): integer)? +---@field json5 (integer | fun(bufnr: integer): integer)? +---@field jsonc (integer | fun(bufnr: integer): integer)? +---@field jsonnet (integer | fun(bufnr: integer): integer)? +---@field julia (integer | fun(bufnr: integer): integer)? +---@field kotlin (integer | fun(bufnr: integer): integer)? +---@field latex (integer | fun(bufnr: integer): integer)? +---@field lua (integer | fun(bufnr: integer): integer)? +---@field luadoc (integer | fun(bufnr: integer): integer)? +---@field make (integer | fun(bufnr: integer): integer)? +---@field markdown (integer | fun(bufnr: integer): integer)? +---@field nim (integer | fun(bufnr: integer): integer)? +---@field nix (integer | fun(bufnr: integer): integer)? +---@field perl (integer | fun(bufnr: integer): integer)? +---@field php (integer | fun(bufnr: integer): integer)? +---@field python (integer | fun(bufnr: integer): integer)? +---@field query (integer | fun(bufnr: integer): integer)? +---@field r (integer | fun(bufnr: integer): integer)? +---@field racket (integer | fun(bufnr: integer): integer)? +---@field rasi (integer | fun(bufnr: integer): integer)? +---@field regex (integer | fun(bufnr: integer): integer)? +---@field rst (integer | fun(bufnr: integer): integer)? +---@field ruby (integer | fun(bufnr: integer): integer)? +---@field rust (integer | fun(bufnr: integer): integer)? +---@field scheme (integer | fun(bufnr: integer): integer)? +---@field scss (integer | fun(bufnr: integer): integer)? +---@field sql (integer | fun(bufnr: integer): integer)? +---@field starlark (integer | fun(bufnr: integer): integer)? +---@field templ (integer | fun(bufnr: integer): integer)? +---@field terraform (integer | fun(bufnr: integer): integer)? +---@field toml (integer | fun(bufnr: integer): integer)? +---@field tsx (integer | fun(bufnr: integer): integer)? +---@field typescript (integer | fun(bufnr: integer): integer)? +---@field typst (integer | fun(bufnr: integer): integer)? +---@field verilog (integer | fun(bufnr: integer): integer)? +---@field vim (integer | fun(bufnr: integer): integer)? +---@field vimdoc (integer | fun(bufnr: integer): integer)? +---@field vue (integer | fun(bufnr: integer): integer)? +---@field yaml (integer | fun(bufnr: integer): integer)? +---@field zig (integer | fun(bufnr: integer): integer)? +---User defined language, not part of rainbow_delimiters support +---@field [string] (integer | fun(bufnr: integer): integer)? + + +---@alias rainbow_delimiters.language +---| 'astro' +---| 'bash' +---| 'c' +---| 'c_sharp' +---| 'clojure' +---| 'commonlisp' +---| 'cpp' +---| 'css' +---| 'cuda' +---| 'cue' +---| 'dart' +---| 'elixir' +---| 'elm' +---| 'fennel' +---| 'fish' +---| 'go' +---| 'haskell' +---| 'hcl' +---| 'html' +---| 'janet_simple' +---| 'java' +---| 'javascript' +---| 'json' +---| 'json5' +---| 'jsonc' +---| 'jsonnet' +---| 'julia' +---| 'kotlin' +---| 'latex' +---| 'lua' +---| 'luadoc' +---| 'make' +---| 'markdown' +---| 'nim' +---| 'nix' +---| 'perl' +---| 'php' +---| 'python' +---| 'query' +---| 'r' +---| 'racket' +---| 'rasi' +---| 'regex' +---| 'rst' +---| 'ruby' +---| 'rust' +---| 'scheme' +---| 'scss' +---| 'sql' +---| 'starlark' +---| 'templ' +---| 'terraform' +---| 'toml' +---| 'tsx' +---| 'typescript' +---| 'typst' +---| 'verilog' +---| 'vim' +---| 'vimdoc' +---| 'vue' +---| 'yaml' +---| 'zig' +---User defined language, not part of rainbow_delimiters support +---| string + +---@class (exact) rainbow_delimiters.logging +---@field file ('rainbow_delimiters.log' | string)? +---@field level integer diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/config.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/config.lua new file mode 100644 index 00000000..857ae530 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/config.lua @@ -0,0 +1,107 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +local function get_nested(table, index, key) + local result + + -- 1. User setting for file type + if vim.g.rainbow_delimiters and vim.g.rainbow_delimiters[index] then + result = rawget(vim.g.rainbow_delimiters[index], key) + end + if result ~= nil then return result end + + -- 2. User setting for fallback + if vim.g.rainbow_delimiters and vim.g.rainbow_delimiters[index] then + result = rawget(vim.g.rainbow_delimiters[index], '') + end + if result ~= nil then return result end + + -- 3. Default setting + result = rawget(table, key) + if result ~= nil then return result end + + result = require('rainbow-delimiters.default')[index][key] + return result +end + +---Plugin settings lookup table. This table is only used for looking up +---values. Set `g:rainbow_delimiters` to change the values. +local M = { + query = setmetatable({}, { + __index = function(table, key) + return get_nested(table, 'query', key) + end + }), + strategy = setmetatable({}, { + __index = function(table, key) + return get_nested(table, 'strategy', key) + end + }), + priority = setmetatable({}, { + __index = function(table, key) + return get_nested(table, 'priority', key) + end + }), + log = setmetatable({}, { + __index = function(table, key) + return get_nested(table, 'log', key) + end + }), + enabled_for = function(lang) + local conf = vim.g.rainbow_delimiters + if not conf then return true end + + local whitelist = conf.whitelist + local blacklist = conf.blacklist + + if whitelist then + for _, v in ipairs(whitelist) do + if v == lang then return true end + end + return false + end + + if blacklist then + for _, v in ipairs(blacklist) do + if v == lang then return false end + end + return true + end + + return true + end +} + +setmetatable(M, { + __index = function(table, key) + if key == 'highlight' then + local highlight + + if vim.g.rainbow_delimiters then + highlight = rawget(vim.g.rainbow_delimiters, 'highlight') + end + if highlight and #highlight > 0 then return highlight end + + highlight = require('rainbow-delimiters.default').highlight + return highlight + end + return rawget(table, key) + end, +}) + +return M + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/default.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/default.lua new file mode 100644 index 00000000..7606dbff --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/default.lua @@ -0,0 +1,74 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +---Default plugin configuration. +---@type rainbow_delimiters.config +local M = { + ---Query names by file type + query = { + [''] = 'rainbow-delimiters', + javascript = 'rainbow-delimiters-react' + }, + ---Highlight strategies by file type + strategy = { + [''] = require 'rainbow-delimiters.strategy.global', + }, + priority = { + [''] = 110, + }, + ---Event logging settings + log = { + ---Log level of the module, see `:h log_levels`. + level = vim.log.levels.WARN, + ---File name of the log file + file = vim.fn.stdpath('log') .. '/rainbow-delimiters.log', + }, + -- Highlight groups in order of display + highlight = { + -- The colours are intentionally not in the usual order to make + -- the contrast between them stronger + 'RainbowDelimiterRed', + 'RainbowDelimiterYellow', + 'RainbowDelimiterBlue', + 'RainbowDelimiterOrange', + 'RainbowDelimiterGreen', + 'RainbowDelimiterViolet', + 'RainbowDelimiterCyan', + } +} + +---If the key does not exist in the table fall back on the empty string as +---key. +local function get_with_fallback(table, key) + return rawget(table, key) or rawget(table, '') +end + +setmetatable(M.query, { + __index = get_with_fallback, +}) + +setmetatable(M.strategy, { + __index = get_with_fallback, +}) + +setmetatable(M.priority, { + __index = get_with_fallback, +}) + + +return M + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/health.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/health.lua new file mode 100644 index 00000000..5a4cb3c8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/health.lua @@ -0,0 +1,284 @@ +---Health check module. +local M = {} + +-- In Neovim 0.10 the following functions have been renamed +local start = vim.health.start or vim.health.report_start +local ok = vim.health.ok or vim.health.report_ok +local info = vim.health.info or vim.health.report_info +local warn = vim.health.warn or vim.health.report_warn +local error = vim.health.error or vim.health.report_error + +local filewritable = vim.fn.filewritable +local fnamemodify = vim.fn.fnamemodify + + +local STRATEGY_ADVICE = "See :h rb-delimiters-strategy for the strategy protocol" +local QUERY_ADVICE = "See :h rb-delimiters-query for included standard queries." +local HLGROUP_ADVICE = "Consecutive highlight groups make delimiter levels indistinguishable, use another highlight group." +local SCHEMA_ADVICE = "This might be a typo, see :h g:rainbow_delimiters for valid entries." + +---Specification of valid options. The key is the name of an option, the value +---is either true (no further validation) or a table containing the nested +---schema for the option +local schema = { + strategy = true, + query = true, + highlight = true, + priority = true, + blacklist = true, + whitelist = true, + log = {level = true, file = true}, +} + + +---Check whether there is a parser installed for the given language. +---@param lang string +---@return boolean +local function check_parser_installed(lang) + local success = pcall(vim.treesitter.language.inspect, lang) + return success +end + +---Check whether the strategy is a valid strategy. +--- +---This is not a 100% reliable check; we only test the type of the argument and +---whether the table has the correct fields, but not what the callback +---functions actually do. +---@param strategy rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy? +---@return boolean +local function check_strategy(strategy) + if type(strategy) == 'function' then + local finfo = debug.getinfo(strategy) + return finfo.nparams == 0 or finfo.nparams == 1 + end + if type(strategy) == 'table' then + if type(strategy.on_attach) ~= 'function' then + return false + end + if type(strategy.on_detach) ~= 'function' then + return false + end + if type(strategy.on_reset) ~= 'function' then + return false + end + return true + end + return false +end + +---Check whether the given query is defined for the given language. +---@param lang string +---@param name string | fun(bufnr: integer): string +---@return boolean +local function check_query(lang, name) + if type(name) == 'function' then + local finfo = debug.getinfo(name) + return finfo.nparams == 0 or finfo.nparams == 1 + end + if type(name) == 'string' then + local query = vim.treesitter.query.get(lang, name) + return query ~= nil + end + return false +end + +---Check whether the given priority is defined for the given language. +---@param priority integer | fun(bufnr: integer): integer +---@return boolean +local function check_priority(priority) + if type(priority) == 'function' then + local finfo = debug.getinfo(priority) + return finfo.nparams == 0 or finfo.nparams == 1 + end + if type(priority) == 'number' then + return true + end + return false +end + +---@param settings rainbow_delimiters.logging +local function check_logging(settings) + local level, file = settings.level, settings.file + if level then + -- Note: although the log level is an integer, Lua 5.1 only has the + -- number type + if type(level) ~= 'number' then + error('The log level must be a number', 'See :h vim.log.levels for valid log levels.') + else + ok('Valid log level.') + end + end + if file then + if type(file) ~= 'string' then + error('The log file path must be a string') + elseif filewritable(file) == 0 then + if filewritable(fnamemodify(file, ':h')) == 2 then + ok('Valid location for log file.') + else + local msg = string.format("Cannot write to file '%s'", file) + error(msg) + end + else + ok('Valid log file.') + end + end + + local advice = "This might be a typo, see :h rb-delimiters-logging for valid entries." + for option in pairs(settings) do + if not schema.log[option] then + local msg = string.format("Unknown logging option '%s' in settings", option) + warn(msg, advice) + end + end +end + + +function M.check() + local settings = vim.g.rainbow_delimiters --[[@as rainbow_delimiters.config]] + if not settings then + return + info("No custom configuration; see :h rb-delimiters-setup for information.") + end + + local whitelist = settings.whitelist + if whitelist then + start 'Parsers for whitelisted languages' + for _, lang in ipairs(whitelist) do + local success = check_parser_installed(lang) + if success then + local msg = string.format("Parser installed for '%s'", lang) + ok(msg) + else + local msg = string.format("No parser installed for '%s'", lang) + warn(msg) + end + end + end + + local strategies = settings.strategy + if strategies then + start 'Custom strategies' + for lang, strategy in pairs(strategies) do + local has_strategy = check_strategy(strategy) + if lang == '' then + if has_strategy then + local msg = 'Valid custom default strategy.' + ok(msg) + else + local msg = 'Invalid custom default strategy.' + error(msg, STRATEGY_ADVICE) + end + else + local has_parser = check_parser_installed(lang) + if not has_parser then + local msg = string.format("No parser installed for '%s'", lang) + error(msg) + end + if not has_strategy then + local msg = string.format("Invalid custom strategy for '%s'", lang) + error(msg, STRATEGY_ADVICE) + end + if has_parser and has_strategy then + local msg = string.format("Valid custom strategy for '%s'.", lang) + ok(msg) + end + end + end + end + + local queries = settings.query + if queries then + start 'Custom queries' + for lang, query in pairs(queries) do + if lang == '' then + if query ~= 'rainbow-delimiters' then + local msg = string.format( + "User-defined default query '%s'\ + If you meant 'rainbow-delimiters' check for typos", + query + ) + ok(msg) + else + local msg = "Valid custom default query" + ok(msg) + end + else + local has_lang = check_parser_installed(lang) + local has_query = check_query(lang, query) + if not has_lang then + local msg = string.format("No parser installed for '%s'.", lang) + warn(msg) + end + if not has_query then + local msg = string.format("No query named '%s' for '%s' found.", query, lang) + warn(msg, QUERY_ADVICE) + end + if has_lang and has_query then + local msg = string.format("Valid custom query for '%s'", lang) + ok(msg) + end + end + end + end + + local priorities = settings.priority + if priorities then + start 'Custom priorities' + for lang, priority in pairs(priorities) do + local is_valid_prirority = check_priority(priority) + if lang == '' then + if is_valid_prirority then + local msg = "Valid custom default priority" + ok(msg) + else + local msg = "Invalid custom default priority" + error(msg) + end + else + if is_valid_prirority then + local msg = string.format("Valid custom priority for '%s'", lang) + ok(msg) + else + local msg = string.format("Invalid custom priority for '%s'", lang) + error(msg) + end + end + end + end + + local hlgroups = settings.highlight + if hlgroups then + start 'Custom highlight groups' + local previous + for _, hlgroup in ipairs(hlgroups) do + local has_hlgroup = vim.fn.hlID(hlgroup) ~= 0 + if has_hlgroup then + ok(string.format("Highlight group '%s' defined.", hlgroup)) + else + error(string.format("Highlight group '%s' not defined.", hlgroup)) + end + if previous and hlgroup == previous then + local msg = string.format("Consecutive highlight group '%s'", hlgroup) + warn(msg, HLGROUP_ADVICE) + end + previous = hlgroup + end + end + + local logging = settings.log + if logging then + start 'Logging settings' + check_logging(logging) + end + + for option in pairs(settings) do + if not schema[option] then + local msg = string.format("Unknown option '%s' in settings", option) + warn(msg, SCHEMA_ADVICE) + end + end +end + +return M + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/lib.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/lib.lua new file mode 100644 index 00000000..76227a99 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/lib.lua @@ -0,0 +1,248 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + Copyright 2020-2022 Chinmay Dalal + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +local api = vim.api +local get_query = vim.treesitter.query.get +local get_parser = vim.treesitter.get_parser +local log = require 'rainbow-delimiters.log' +local config = require 'rainbow-delimiters.config' +local util = require 'rainbow-delimiters.util' + + +---[ Internal ]---------------------------------------------------------------- +-- The following symbols should only be used internally. In particular, they +-- should not be used by strategies, or else our strategies are using +-- undocumented APIs. + +---Private library of shared internal functions and variables. +local M = {} + +M.enabled_for = config.enabled_for + +---Per-language namespaces. This table instantiates namespaces on demand, i.e. +---a namespace won't exist until we first try to get it from the table. +M.nsids = setmetatable({}, { + __index = function(t, k) + local result = rawget(t, k) + if result == nil then + result = vim.api.nvim_create_namespace('') + rawset(t, k, result) + end + return result + end, + -- Note: this will only catch new indices, not assignment to an already + -- existing key + __newindex = function(_, _, _) + error('Table is immutable') + end +}) + + +---Keeps track of attached buffers. The key is the buffer number and the value +---is a table of information about that buffer (e.g. language, strategy, +---query). This also makes sure we keep track of all parsers in active use to +---prevent them from being garbage-collected. +---@type table +M.buffers = {} + + +---[ This stuff needs to be re-exported ]-------------------------------------- +-- The following entries can be used in the public API as well. + +---Fetches the query object for the given language from the settings. If a +---buffer number is given it will be used as the current buffer, otherwise the +---actual current buffer is used. +--- +---@param lang string Name of the language to get the query for +---@param bufnr integer Use this buffer as the current buffer +---@return Query? query The query object +function M.get_query(lang, bufnr) + local name = config['query'][lang] + if type(name) == "function" then + name = name(bufnr) + end + local query = get_query(lang, name) + + if not query then + log.debug('Query %s not found for %s', name, lang) + else + log.trace('Query %s found for %s', name, lang) + end + return query +end + +---Apply highlighting to a single node. +---@param bufnr integer Buffer which contains the node +---@param lang string Language of the node (to group HL into namespaces) +---@param node table Node to highlight +---@param hlgroup string Name of the highlight group to apply. +function M.highlight(bufnr, lang, node, hlgroup) + -- range of the capture, zero-indexed + local startRow, startCol, endRow, endCol = node:range() + + local start, finish = {startRow, startCol}, {endRow, endCol - 1} + local priority = config.priority[lang] + if type(priority) == "function" then + priority = priority(bufnr) + end + local opts = { + regtype = 'c', + inclusive = true, + priority = priority, + } + + local nsid = M.nsids[lang] + + if vim.api.nvim_buf_is_loaded(bufnr) then + vim.highlight.range(bufnr, nsid, hlgroup, start, finish, opts) + end +end + + +---Get the appropriate highlight group for the given level of nesting. +---@param i integer One-based index into the highlight groups +---@return string hlgroup Name of the highlight groups +function M.hlgroup_at(i) + local hlgroups = config.highlight + return hlgroups[(i - 1) % #hlgroups + 1] +end + + +---Clears the reserved Rainbow namespace. +--- +---@param bufnr integer Number of the buffer for which to clear the namespace +---@param lang string +---@param line_start integer? +---@param line_end integer? +function M.clear_namespace(bufnr, lang, line_start, line_end) + local nsid = M.nsids[lang] + if vim.api.nvim_buf_is_valid(bufnr) then + vim.api.nvim_buf_clear_namespace(bufnr, nsid, line_start or 0, line_end or -1) + end +end + +---Start rainbow highlighting for the given buffer +---@param bufnr integer +function M.attach(bufnr) + -- Rainbow delimiters was explicitly disabled for this buffer + if M.buffers[bufnr] == false then return end + + local lang = vim.treesitter.language.get_lang(vim.bo[bufnr].ft) + if not lang then + log.trace('Cannot attach to buffer %d, no parser for %s', bufnr, lang) + return + end + log.trace('Attaching to buffer %d with language %s.', bufnr, lang) + + local settings = M.buffers[bufnr] + if settings then + -- if M.buffers[bufnr].lang == lang then return end + -- TODO: If the language is the same reload the parser + if settings.lang == lang then + local parser = get_parser(bufnr, lang) + local strategy = settings.strategy + parser:invalidate(true) + parser:parse() + strategy.on_reset(bufnr, settings) + return + end + -- The file type of the buffer has changed, so we need to detach first + -- before we re-attach + M.detach(bufnr) + end + + local parser + do + local success + success, parser = pcall(get_parser, bufnr, lang) + if not success then return end + end + + local strategy + do + strategy = config.strategy[lang] + if type(strategy) == 'function' then + strategy = strategy(bufnr) + end + end + + -- Intentionally abort; the user has explicitly disabled rainbow delimiters + -- for this buffer, usually by setting a strategy- or query function which + -- returned nil. + if not strategy then + log.warn('No strategy defined for %s', lang) + end + if not strategy or strategy == vim.NIL then return end + + parser:register_cbs { + ---@param bnr integer + on_detach = function(bnr) + if not M.buffers[bnr] then return end + M.detach(bufnr) + end, + ---@param child LanguageTree + on_child_removed = function(child) + M.clear_namespace(bufnr, child:lang()) + end, + } + + settings = { + strategy = strategy, + parser = parser, + lang = lang + } + M.buffers[bufnr] = settings + + -- For now we silently discard errors, but in the future we should log + -- them. + local success, error = pcall(strategy.on_attach, bufnr, settings) + if not success then + log.error('Error attaching strategy to buffer %d: %s', bufnr, error) + M.buffers[bufnr] = nil + end +end + +---Start rainbow highlighting for the given buffer +---@param bufnr integer +function M.detach(bufnr) + log.trace('Detaching from buffer %d.', bufnr) + if not M.buffers[bufnr] then + return + end + + local strategy = M.buffers[bufnr].strategy + local parser = M.buffers[bufnr].parser + + -- Clear all the namespaces for each language + util.for_each_child(nil, parser:lang(), parser, function(_, lang) + M.clear_namespace(bufnr, lang) + end) + -- Finally release all resources the parser is holding on to + parser:destroy() + + -- For now we silently discard errors, but in the future we should log + -- them. + local success, error = pcall(strategy.on_detach, bufnr) + if not success then + log.error('Error detaching strategy from buffer %d: %s', bufnr, error) + end + M.buffers[bufnr] = nil +end + +return M + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/log.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/log.lua new file mode 100644 index 00000000..7a4e1570 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/log.lua @@ -0,0 +1,103 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +---Logger module for rainbow delimiters. Logs any message whose log level is +---equal to or greater than the log level of the module. +local M = {} + +local date = os.date +local levels = vim.log.levels +local config = require 'rainbow-delimiters.config' + +---Reverse lookup table; maps a log level to its text label +local level_str = {} +for key, value in pairs(levels) do + level_str[value] = key +end + +---Dynamically determines the module from which the log function was called. +---If it was called from somewhere else return the name of the plugin. +---@return string +local function get_module() + local module = debug.getinfo(4, 'S').source:match('^.+rainbow%-delimiters/(.+).lua$') + if not module then + return '' + end + return module:gsub('/', '.') +end + +---@param file file* +---@param level integer +---@param module string +---@param message any +---@param ... any +local function write_log(file, level, module, message, ...) + local msg + local timestamp = date('%FT%H:%M%z') + if type(message) == 'function' then + msg = message() + else + msg = string.format(message, ...) + end + + file:write(string.format('%s %s %s %s\n', timestamp, level, module, msg)) +end + +---@param level integer +---@param message any +---@param ... any +local function log(level, message, ...) + if level < config.log.level then return end + + local file = io.open(config.log.file, 'a+') + -- Intentional: Silently discard the log if the log file cannot be opened + if not file then return end + + -- Wrap inside a pcall to make sure the file gets closed even if an error + -- occurs + pcall(write_log, file, level_str[level], get_module(), message, ...) + file:close() + -- Should I also print the message? +end + +---Log an error message +function M.error(message, ...) + log(levels.ERROR, message, ...) +end + +---Log a warning message +function M.warn(message, ...) + log(levels.WARN, message, ...) +end + +---Log a tracing message +function M.debug(message, ...) + log(levels.DEBUG, message, ...) +end + +---Log a tracing message +function M.trace(message, ...) + log(levels.TRACE, message, ...) +end + +---Log an info message +function M.info(message, ...) + log(levels.INFO, message, ...) +end + +return M + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/setup.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/setup.lua new file mode 100644 index 00000000..ba1058ea --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/setup.lua @@ -0,0 +1,17 @@ +local M = {} + +---Apply the given configuration to the rainbow-delimiter settings. Will +---overwrite existing settings. +--- +---@param opts rainbow_delimiters.config Settings, same format as `vim.g.rainbow_delimiters` +function M.setup(opts) + vim.g.rainbow_delimiters = opts +end + + +-- Make it possible to call the module directly; for backwards compatibility +-- with a previous version of this module. +setmetatable(M, {__call = function(_t, opts) M.setup(opts) end}) +return M + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/stack.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/stack.lua new file mode 100644 index 00000000..068bcc9d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/stack.lua @@ -0,0 +1,124 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +---Helper library for stack-like tables. +local M = {} + +---@class (exact) Stack +---@field public size fun(self: Stack): integer +---@field public peek fun(self: Stack): any +---@field public push fun(self: Stack, item: any): Stack +---@field public pop fun(self: Stack): any +---@field public iter fun(self: Stack): ((fun(i: integer, item: any): integer?, any), Stack, integer) +---@field package content any[] + +---The stack metatable. +local mt = {} + +---The actual iterator implementation, hidden behind the iter-method. +---@param stack Stack +---@param i integer +---@return integer? +---@return any +local function iter_stack(stack, i) + if i <= 1 then return end + return i - 1, stack.content[i - 1] +end + +---@param stack Stack +---@return string +local function stack_tostring(stack) + local items = {} + for _, item in ipairs(stack.content) do + items[#items + 1] = tostring(item) + end + return string.format('[%s]', table.concat(items, ', ')) +end + + +---[ Methods ]----------------------------------------------------------------- + +---Returns the current number of items in the stack. +---@param self Stack +---@return integer size Current size of the stack +local function size(self) + return #self.content +end + +---Iterate through the content of the stack from top to bottom. Each iteration +---returns the current index (one-based, counting from the bottom) and the +---current item. +---@param self Stack The stack instance +---@return fun(i: integer, stack: Stack): integer?, any +---@return Stack +---@return integer +local function iter(self) + return iter_stack, self, self:size() + 1 +end + +---Add a new item to the top of the stack. Modifies the stack in-place. +---@param item any The item to push onto the stack +---@return Stack stack The stack. +local function push(self, item) + self.content[self:size() + 1] = item + return self +end + +---Returns the topmost item of the stack without altering the stack. +---@return any top The top-most item. +local function peek(self) + local result = self.content[self:size()] + return result +end + +---Returns the topmost item of the stack and removes it from the stack. +---@return any top The top-most item. +local function pop(self) + local n = self:size() + local result = self.content[n] + self.content[n] = nil + return result +end + + +---[ Public module interface ]------------------------------------------------- + +---Instantiates a new stack containing the given items, or the empty stack if +---the argument is `nil`. +---@param items any[]? Array of items in order from bottom to top +---@return Stack stack The new stack instance +function M.new(items) + ---@type Stack + local result = { + content = {}, + size = size, + iter = iter, + push = push, + pop = pop, + peek = peek, + } + setmetatable(result, mt) + for _, item in ipairs(items or {}) do result:push(item) end + return result +end + +---[ Metamethods ]------------------------------------------------------------- +mt.__tostring = stack_tostring + +return M + + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/christmas.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/christmas.lua new file mode 100644 index 00000000..26d1f58e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/christmas.lua @@ -0,0 +1,75 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +---Strategy decorator which makes your delimiters change colours like Christmas +---lights. This module is meant as a joke and will not be loaded by default +---with the rest of the plugin. +local M = {} + +local uv = vim.loop +local lib = require 'rainbow-delimiters.lib' +local original_hlgroup_at = lib.hlgroup_at + + +local counter = 0 + +---Wrapper around the original function which applies some offset to the index. +---@param i integer +---@return string hlgroup +local function patched_hlgroup_at(i) + return original_hlgroup_at(counter + i) +end + +---Wraps the given strategy with a new strategy that switches colours like a +---chain of Christmas lights. +---@param strategy rainbow_delimiters.strategy The original strategy +---@param delay integer? Time between switches in milliseconds (default 500) +---@return rainbow_delimiters.strategy christmas_lights A new strategy object +function M.lights(strategy, delay) + delay = delay or 500 + local timer = uv.new_timer() + + ---@param bufnr integer + ---@param settings rainbow_delimiters.buffer_settings + local function on_attach(bufnr, settings) + local function blink() + counter = counter + 1 + local function callback() + lib.hlgroup_at = patched_hlgroup_at + strategy.on_reset(bufnr, lib.buffers[bufnr]) + lib.hlgroup_at = original_hlgroup_at + end + vim.schedule(callback) + end + timer:start(0, delay, blink) + strategy.on_attach(bufnr, settings) + end + + ---@param bufnr integer + local function on_detach(bufnr) + timer:stop() + strategy.on_detach(bufnr) + end + + return { + strategy = strategy, + on_attach = on_attach, + on_detach = on_detach, + on_reset = strategy.on_reset, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/global.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/global.lua new file mode 100644 index 00000000..4f8f9ebb --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/global.lua @@ -0,0 +1,300 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + Copyright 2020-2022 Chinmay Dalal + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +local Stack = require 'rainbow-delimiters.stack' +local lib = require 'rainbow-delimiters.lib' +local util = require 'rainbow-delimiters.util' +local log = require 'rainbow-delimiters.log' + + +---Strategy which highlights the entire buffer. +local M = {} + +---Changes are range objects and come in two variants: one with four entries and +---one with six entries. We only want the four-entry variant. See +---`:h TSNode:range()` +---@param change integer[] +---@return integer[] +local function normalize_change(change) + local result + if #change == 4 then + result = change + elseif #change == 6 then + result = {change[1], change[2], change[4], change[5]} + else + result = {} + end + return result +end + +---@param bufnr integer +---@param lang string +---@param matches Stack +---@param level integer +local function highlight_matches(bufnr, lang, matches, level) + local hlgroup = lib.hlgroup_at(level) + for _, match in matches:iter() do + for _, delimiter in match.delimiter:iter() do lib.highlight(bufnr, lang, delimiter, hlgroup) end + highlight_matches(bufnr, lang, match.children, level + 1) + end +end + + +---Create a new empty match_record +---@return table +local function new_match_record() + return { + delimiter = Stack.new(), + children = Stack.new(), + } +end + +---Update highlights for a range. Called every time text is changed. +---@param bufnr integer Buffer number +---@param changes table List of node ranges in which the changes occurred +---@param tree TSTree TS tree +---@param lang string Language +local function update_range(bufnr, changes, tree, lang) + log.debug('Updated range with changes %s', vim.inspect(changes)) + + if not lib.enabled_for(lang) then return end + if vim.fn.pumvisible() ~= 0 or not lang then return end + + local query = lib.get_query(lang, bufnr) + if not query then return end + + local matches = Stack.new() + + for _, change in ipairs(changes) do + -- This is the match record, it lists all the relevant nodes from + -- each match. + ---@type table? + local match_record + local root_node = tree:root() + local start_row, end_row = change[1], change[3] + 1 + lib.clear_namespace(bufnr, lang, start_row, end_row) + + for qid, node, _ in query:iter_captures(root_node, bufnr, start_row, end_row) do + local name = query.captures[qid] + -- check for 'delimiter' first, since that should be the most + -- common name + if name == 'delimiter' and match_record then + match_record.delimiter:push(node) + elseif name == 'container' and not match_record then + match_record = new_match_record() + elseif name == 'container' then + -- temporarily push the match_record to matches to be retrieved + -- later, since we haven't closed it yet + matches:push(match_record) + match_record = new_match_record() + -- since we didn't close the previous match_record, it must + -- mean that the current match_record has it as an ancestor + match_record.has_ancestor = true + elseif name == 'sentinel' and match_record then + -- if we see the sentinel, then we are done with the current + -- container + if match_record.has_ancestor then + local prev_match_record = matches:pop() + if prev_match_record then + -- since we have an ancestor, it has to be the last + -- element of the stack + prev_match_record.children:push(match_record) + match_record = prev_match_record + else + -- since match_record.has_ancestor was true, we shouldn't + -- be able to get to here unless something went wrong + -- with the queries or treesitter itself + log.error([[You are missing a @container, + which should be impossible! + Please double check the queries.]]) + end + else + -- if match_record doesn't have an ancestor, the sentinel + -- means that we are done with it + matches:push(match_record) + match_record = nil + end + elseif (name == 'delimiter' or name == 'sentinel') and not match_record then + log.error([[You query got the capture name %s. + But it didn't come with a container, which should be impossible! + Please double check your queries.]], name) + end -- do nothing with other capture names + end + if match_record then + -- we might have a dangling match_record, so we push it back into + -- matches + -- this should only happen when the query is on a proper subset + -- of the full tree (usually just one line) + matches:push(match_record) + end + end + + -- when we capture on a row and not the full tree, we get the previous + -- containers (on earlier rows) included in the above, but not the + -- delimiters and sentinels from them, so we push them up as long as + -- we know they have an ancestor + local last_match = matches:pop() + while last_match and last_match.has_ancestor do + local prev_match = matches:pop() + + if prev_match then + prev_match.children:push(last_match) + else + log.error('You are in what should be an unreachable position.') + end + last_match = prev_match + end + matches:push(last_match) + + highlight_matches(bufnr, lang, matches, 1) +end + +---Update highlights for every tree in given buffer. +---@param bufnr integer # Buffer number +---@param parser LanguageTree +local function full_update(bufnr, parser) + log.debug('Performing full updated on buffer %d', bufnr) + local function callback(tree, sub_parser) + local changes = {{tree:root():range()}} + update_range(bufnr, changes, tree, sub_parser:lang()) + end + + parser:for_each_tree(callback) +end + + +---Sets up all the callbacks and performs an initial highlighting +---@param bufnr integer # Buffer number +---@param parser LanguageTree +---@param start_parent_lang string? # Parent language or nil +local function setup_parser(bufnr, parser, start_parent_lang) + log.debug('Setting up parser for buffer %d', bufnr) + + util.for_each_child(start_parent_lang, parser:lang(), parser, function(p, lang, parent_lang) + log.debug("Setting up parser for '%s' in buffer %d", lang, bufnr) + -- Skip languages which are not supported, otherwise we get a + -- nil-reference error + if not lib.get_query(lang, bufnr) then return end + + p:register_cbs { + ---@param changes table + ---@param tree TSTree + on_changedtree = function(changes, tree) + log.trace('Changed tree in buffer %d with languages %s', bufnr, lang) + -- HACK: As of Neovim v0.9.1 there is no way of unregistering a + -- callback, so we use this check to abort + if not lib.buffers[bufnr] then return end + + -- HACK: changes can accidentally overwrite highlighting in injected code + -- blocks. + if not parent_lang then + -- If we have no parent language, then we use changes, otherwise we use the + -- whole tree's range. + -- Normalize the changes object if we have no parent language (the one we + -- get from on_changedtree) + changes = vim.tbl_map(normalize_change, changes) + elseif parent_lang ~= lang and changes[1] then + -- We have a parent language, so we are in an injected language code + -- block, thus we update all of the current code block + changes = {{tree:root():range()}} + else + -- some languages (like rust) use injections of the language itself for + -- certain functionality (e.g., macros in rust). For these the + -- highlighting will be updated by the non-injected language part of the + -- code. + changes = {} + end + + -- If a line has been moved from another region it will still carry with it + -- the extmarks from the old region. We need to clear all extmarks which + -- do not belong to the current language + for _, change in ipairs(changes) do + for key, nsid in pairs(lib.nsids) do + if key ~= lang then + -- HACK: changes in the main language sometimes need to overwrite + -- highlighting on one more line + local line_end = change[3] + (parent_lang and 0 or 1) + vim.api.nvim_buf_clear_namespace(bufnr, nsid, change[1], line_end) + end + end + end + + -- only update highlighting if we have changes + if changes[1] then + update_range(bufnr, changes, tree, lang) + end + + -- HACK: Since we update the whole tree when we have a parent + -- language, we need to make sure to then update all children + -- too, even if there is no change in them. This shouldn't + -- affect performance, since it only affects code nested at + -- least 2 injection languages deep. + if parent_lang then + local children = p:children() + for child_lang, child in pairs(children) do + if lang == child_lang then return end + child:for_each_tree(function(child_tree, child_p) + local child_changes = {{child_tree:root():range()}} + + -- we don't need to remove old extmarks, since + -- the above code will handle that correctly + -- already, but we might have accidentally + -- removed extmarks that we need to set again + update_range(bufnr, child_changes, child_tree, child_p:lang()) + end) + end + end + end, + -- New languages can be added into the text at some later time, e.g. + -- code snippets in Markdown + ---@param child LanguageTree + on_child_added = function(child) + setup_parser(bufnr, child, lang) + end, + } + log.trace("Done with setting up parser for '%s' in buffer %d", lang, bufnr) + end) + + full_update(bufnr, parser) +end + + +---on_attach implementation for the global strategy +---@param bufnr integer +---@param settings rainbow_delimiters.buffer_settings +function M.on_attach(bufnr, settings) + log.trace('global strategy on_attach') + local parser = settings.parser + setup_parser(bufnr, parser, nil) +end + +---on_detach implementation for the global strategy +---@param _bufnr integer +function M.on_detach(_bufnr) +end + +---on_reset implementation for the global strategy +---@param bufnr integer +---@param settings rainbow_delimiters.buffer_settings +function M.on_reset(bufnr, settings) + log.trace('global strategy on_reset') + full_update(bufnr, settings.parser) +end + +return M --[[@as rainbow_delimiters.strategy]] + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/local.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/local.lua new file mode 100644 index 00000000..532bfe48 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/local.lua @@ -0,0 +1,338 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +local Stack = require 'rainbow-delimiters.stack' +local lib = require 'rainbow-delimiters.lib' +local log = require 'rainbow-delimiters.log' +local util = require 'rainbow-delimiters.util' +local api = vim.api +local ts = vim.treesitter + +---Highlight strategy which highlights the sub-tree of the buffer which +---contains the cursor. Re-computes -highlights when the buffer contents change +---or when the cursor is moved. +local M = {} + +-- Implementation note: This strategy uses a two-step process: on every change +-- to the document tree we compute the match tree and cache it, then when the +-- cursor moves we use the cached match tree and the current cursor position to +-- decide which matches to highlight. +-- +-- The document tree changes rarely, so there is no need to re-compute the +-- match tree every time the cursor moves. + + +---Cache of match trees, maps a buffer number to its match tree. We compute +---the match tree on every change, so that when the cursor moves without +---changing the tree we don't need to re-compute it. +--- +---Each match tree maps a language and TS Tree to the corresponding match tree. +---We need TS Tree because there might be multiple trees per buffer, such as a +---Markdown buffer which contains multiple code blocks. +local match_trees = {} + +---Reusable autogroup for events in this strategy. +---@type integer +local augroup = api.nvim_create_augroup('TSRainbowLocalCursor', {}) + + +---Highlights a single match with the given highlight group +---@param bufnr integer +---@param lang string +---@param match table +---@param hlgroup string +local function highlight_match(bufnr, lang, match, hlgroup) + for _, delimiter in match.delimiter:iter() do lib.highlight(bufnr, lang, delimiter, hlgroup) end +end + +---Highlights all matches and their children on the stack of matches. All +---matches must be on the same level of the match tree. +--- +---@param bufnr integer Number of the buffer +---@param matches Stack Stack of matches +---@param level integer Level of the matches +local function highlight_matches(bufnr, lang, matches, level) + local hlgroup = lib.hlgroup_at(level) + for _, match in matches:iter() do + highlight_match(bufnr, lang, match, hlgroup) + highlight_matches(bufnr, lang, match.children, level + 1) + end +end + +---Finds a match (and its level) in the match tree whose container node is the +---given container node. +---@param matches Stack +---@param container TSNode +---@param level integer +---@return table +---@return integer +---If no match is found, return nil. +---@overload fun(matches: Stack, container: TSNode, level: integer) +local function find_container(matches, container, level) + for _, match in matches:iter() do + if match.container == container then return match, level end + local result, final_level = find_container(match.children, container, level + 1) + if result then return result, final_level end + end +end + +--- Create a new empty match_record with an optionally set container +---@param container TSNode +---@return table +local function new_match_record(container) + return { + container = container, + delimiter = Stack.new(), + children = Stack.new(), + } +end + +---Assembles the match tree, usually called after the document tree has +---changed. +---@param bufnr integer Buffer number +---@param changes table List of node ranges in which the changes occurred +---@param tree TSTree TS tree +---@param lang string Language +---@return Stack? +local function build_match_tree(bufnr, changes, tree, lang) + if not lib.enabled_for(lang) then return end + + local query = lib.get_query(lang, bufnr) + if not query then return end + + local matches = Stack.new() + + for _, change in ipairs(changes) do + -- This is the match record, it lists all the relevant nodes from + -- each match. + ---@type table? + local match_record + local root_node = tree:root() + local start_row, end_row = change[1], change[3] + 1 + lib.clear_namespace(bufnr, lang, start_row, end_row) + for qid, node, _ in query:iter_captures(root_node, bufnr, start_row, end_row) do + local name = query.captures[qid] + -- check for 'delimiter' first, since that should be the most + -- common name + if name == 'delimiter' and match_record then + match_record.delimiter:push(node) + elseif name == 'container' and not match_record then + match_record = new_match_record(node) + elseif name == 'container' then + local prev_match_record = match_record + -- temporarily push the match_record to matches to be retrieved + -- later, since we haven't closed it yet + matches:push(match_record) + match_record = new_match_record(node) + -- since we didn't close the previous match_record, it must + -- mean that the current match_record has it as an ancestor + match_record.ancestor = prev_match_record + elseif name == 'sentinel' and match_record then + -- if we see the sentinel, then we are done with the current + -- container + if match_record.ancestor then + local prev_match_record = matches:pop() + if prev_match_record then + -- since we have an ancestor, it has to be the last + -- element of the stack + prev_match_record.children:push(match_record) + match_record = prev_match_record + else + -- since match_record.has_ancestor was true, we shouldn't + -- be able to get to here unless something went wrong + -- with the queries or treesitter itself + log.error([[You are missing a @container, + which should be impossible! + Please double check the queries.]]) + end + else + -- if match_record doesn't have an ancestor, the sentinel + -- means that we are done with it + matches:push(match_record) + match_record = nil + end + elseif (name == 'delimiter' or name == 'sentinel') and not match_record then + log.error([[You query got the capture name: %s. + But it didn't come with a container, which should be impossible! + Please double check your queries.]], name) + end -- do nothing with other capture names + end + end + + return matches +end + +---@param bufnr integer +---@param tree TSTree +---@param lang string +local function update_local(bufnr, tree, lang) + if not lib.enabled_for(lang) then return end + local query = lib.get_query(lang, bufnr) + if not query then return end + + -- Find the lowest container node which contains the cursor + local cursor_container + do + local curpos = api.nvim_win_get_cursor(0) + -- The order of traversal guarantees that the first match which + -- contains the cursor is also the lowest one. + for _, match in query:iter_matches(tree:root(), bufnr, 0, -1) do + if cursor_container then break end + for id, node in pairs(match) do + local name = query.captures[id] + if name == 'container' and ts.is_in_node_range(node, curpos[1] - 1, curpos[2]) then + cursor_container = node + break + end + end + end + end + if not cursor_container then return end + + local matches_lang = match_trees[bufnr][lang] + if not matches_lang then + log.debug("Did not build any matches Stack for language '%s'", lang) + return + end + local matches = matches_lang[tree] + if not matches then + -- Note: vim.inspect(tree:root():range()) errors, so we need + -- to make it into a table instead of a list of numbers + log.debug("Did not build any matches Stack for tree '%s'", vim.inspect({tree:root():range()})) + return + end + + -- Find the correct container in the match tree + local cursor_match, level = find_container(matches, cursor_container, 1) + if not cursor_match then return end + + -- Highlight the container match and everything below + highlight_matches(bufnr, lang, Stack.new {cursor_match}, level) + + -- Starting with the cursor match travel up and highlight every ancestor as + -- well + local ancestor = cursor_match.ancestor + level = level - 1 + while ancestor do + highlight_match(bufnr, lang, ancestor, lib.hlgroup_at(level)) + ancestor, level = ancestor.ancestor, level - 1 + end +end + +---Callback function to re-highlight the buffer according to the current cursor +---position. +---@param bufnr integer +---@param parser LanguageTree +local function local_rainbow(bufnr, parser) + parser:for_each_tree(function(tree, sub_parser) + update_local(bufnr, tree, sub_parser:lang()) + end) +end + +---Sets up all the callbacks and performs an initial highlighting +---@param bufnr integer # Buffer number +---@param parser LanguageTree +local function setup_parser(bufnr, parser) + log.debug('Setting up parser for buffer %d', bufnr) + util.for_each_child(nil, parser:lang(), parser, function(p, lang, _parent_lang) + log.debug("Setting up parser for '%s' in buffer %d", lang, bufnr) + -- Skip languages which are not supported, otherwise we get a + -- nil-reference error + if not lib.get_query(lang, bufnr) then return end + p:register_cbs { + ---@param _changes table + ---@param tree TSTree + on_changedtree = function(_changes, tree) + -- HACK: As of Neovim v0.9.1 there is no way of unregistering a + -- callback, so we use this check to abort + if not lib.buffers[bufnr] then return end + + if vim.fn.pumvisible() ~= 0 then return end + -- Ideally we would only rebuild the parts of the tree that have changed, + -- but this doesn't work, so we will rebuild the entire tree + -- instead. + local fake_changes = { + {tree:root():range()} + } + match_trees[bufnr][lang] = match_trees[bufnr][lang] or {} + match_trees[bufnr][lang][tree] = build_match_tree(bufnr, fake_changes, tree, lang) + -- Re-highlight after the change + local_rainbow(bufnr, p) + end, + -- New languages can be added into the text at some later time, e.g. + -- code snippets in Markdown + ---@param child LanguageTree + on_child_added = function(child) + setup_parser(bufnr, child) + end, + } + log.trace("Done with setting up parser for '%s' in buffer %d", lang, bufnr) + end) +end + +---on_attach implementation for the local strategy +---@param bufnr integer +---@param settings rainbow_delimiters.buffer_settings +function M.on_attach(bufnr, settings) + local parser = settings.parser + setup_parser(bufnr, parser) + + api.nvim_create_autocmd('CursorMoved', { + group = augroup, + buffer = bufnr, + callback = function(args) + util.for_each_child(nil, parser:lang(), parser, function(_, lang, _) + lib.clear_namespace(bufnr, lang) + end) + local_rainbow(args.buf, parser) + end + }) + + -- Build up the initial match tree + match_trees[bufnr] = {} + parser:for_each_tree(function(tree, sub_parser) + local sub_lang = sub_parser:lang() + local changes = { + {tree:root():range()} + } + match_trees[bufnr][sub_lang] = match_trees[bufnr][sub_lang] or {} + match_trees[bufnr][sub_lang][tree] = build_match_tree(bufnr, changes, tree, sub_lang) + end) + local_rainbow(bufnr, parser) +end + +---on_detach implementation for the local strategy +---@param bufnr integer +function M.on_detach(bufnr) + -- Uninstall autocommand and delete cached match tree + api.nvim_clear_autocmds { + buffer = bufnr, + group = augroup, + } + match_trees[bufnr] = nil +end + +---on_reset implementation for the local strategy +---@param bufnr integer +---@param settings rainbow_delimiters.buffer_settings +function M.on_reset(bufnr, settings) + local parser = settings.parser + local_rainbow(bufnr, parser) +end + +return M --[[@as rainbow_delimiters.strategy]] + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/no-op.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/no-op.lua new file mode 100644 index 00000000..a3e84f6d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/no-op.lua @@ -0,0 +1,39 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +---A dummy strategy which does nothing; can be used in testing. +local M = {} + +---on_attach implementation for the noop strategy +---@param _bufnr integer +---@param _settings rainbow_delimiters.buffer_settings +M.on_attach = function(_bufnr, _settings) +end + +---on_detach implementation for the noop strategy +---@param _bufnr integer +M.on_detach = function(_bufnr) +end + +---on_reset implementation for the noop strategy +---@param _bufnr integer +---@param _settings rainbow_delimiters.buffer_settings +M.on_reset = function(_bufnr, _settings) +end + +return M --[[@as rainbow_delimiters.strategy]] + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/track.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/track.lua new file mode 100644 index 00000000..4a27db8e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/strategy/track.lua @@ -0,0 +1,51 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +---A strategy decorator; the decorated strategy keeps track of all currently +---attached buffers. Thew new strategy has the following fields: +--- +--- - strategy The wrapped strategy object +--- - buffers Table mapping of buffer number to buffer settings +--- - attachments Number of currently active attachments +--- +---@param strategy table +local function track(strategy) + local buffers = {} + local attachments = {0} -- Table because I want to pass it by reference + + return { + strategy = strategy, + buffers = buffers, + attachments = attachments, + on_attach = function(bufnr, settings, ...) + buffers[bufnr] = settings + attachments[1] = attachments[1] + 1 + strategy.on_attach(bufnr, settings, ...) + end, + on_detach = function(bufnr, ...) + buffers[bufnr] = nil + attachments[1] = attachments[1] - 1 + strategy.on_detach(bufnr, ...) + end, + on_reset = function(...) + strategy.on_reset(...) + end, + } +end + +return track + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/util.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/util.lua new file mode 100644 index 00000000..f0e0f7e4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/lua/rainbow-delimiters/util.lua @@ -0,0 +1,42 @@ +--[[ + Copyright 2023 Alejandro "HiPhish" Sanchez + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +---Internal helper functions. This module will probably be removed when I no +---longer need the helpers. +local M = {} + + +---Similar to the function `LanguageTree:for_each_child` which has been +---deprecated. Applies the thunk to the language tree and each of its +---descendants recursively. +--- +---See also https://github.com/neovim/neovim/pull/25154 for a better +---replacement. +---@param parent_lang string? # Parent language or nil +---@param lang string +---@param language_tree LanguageTree +---@param thunk fun(p: LanguageTree, lang: string, parent_lang: string?) +function M.for_each_child(parent_lang, lang, language_tree, thunk) + thunk(language_tree, lang, parent_lang) + local children = language_tree:children() + for child_lang, child in pairs(children) do + M.for_each_child(lang, child_lang, child, thunk) + end +end + +return M + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/makefile b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/makefile new file mode 100644 index 00000000..b5182c0d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/makefile @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: Unlicense + +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or distribute +# this software, either in source code form or as a compiled binary, for any +# purpose, commercial or non-commercial, and by any means. +# +# In jurisdictions that recognize copyright laws, the author or authors of +# this software dedicate any and all copyright interest in the software to +# the public domain. We make this dedication for the benefit of the public +# at large and to the detriment of our heirs and successors. We intend this +# dedication to be an overt act of relinquishment in perpetuity of all +# present and future rights to this software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to + + +.PHONY: check unit-test e2e-test clean + +check: unit-test e2e-test + +unit-test: + @./test/busted --run unit + +e2e-test: + @./test/busted --run e2e + +clean: + @rm -rf test/xdg/local/state/nvim/* + @rm -rf test/xdg/local/share/nvim/site/pack/testing/start/nvim-treesitter/parser/* + @# The symlink might have been left over from a failed test run + @rm -rf test/xdg/local/share/nvim/site/pack/self-* diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/plugin/rainbow-delimiters.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/plugin/rainbow-delimiters.lua new file mode 100644 index 00000000..100689fe --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/plugin/rainbow-delimiters.lua @@ -0,0 +1,75 @@ +-- Copyright 2023 Alejandro "HiPhish" Sanchez +-- Copyright 2020-2022 Chinmay Dalal +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +if vim.g.loaded_rainbow then + return +end + +local api = vim.api +local set_hl = api.nvim_set_hl +local create_augroup = api.nvim_create_augroup +local create_autocmd = api.nvim_create_autocmd +local get_lang = vim.treesitter.language.get_lang +local config = require 'rainbow-delimiters.config' +local log = require 'rainbow-delimiters.log' +local lib = require 'rainbow-delimiters.lib' + + +--- [ DEFINE HIGHLIGHT GROUPS ]------------------------------------------------ +local function define_hlgroups() + log.trace 'Define highlight groups' + + set_hl(0, 'RainbowDelimiterRed' , {default = true, fg = '#cc241d', ctermfg= 'Red' }) + set_hl(0, 'RainbowDelimiterOrange', {default = true, fg = '#d65d0e', ctermfg= 'White' }) + set_hl(0, 'RainbowDelimiterYellow', {default = true, fg = '#d79921', ctermfg= 'Yellow' }) + set_hl(0, 'RainbowDelimiterGreen' , {default = true, fg = '#689d6a', ctermfg= 'Green' }) + set_hl(0, 'RainbowDelimiterCyan' , {default = true, fg = '#a89984', ctermfg= 'Cyan' }) + set_hl(0, 'RainbowDelimiterBlue' , {default = true, fg = '#458588', ctermfg= 'Blue' }) + set_hl(0, 'RainbowDelimiterViolet', {default = true, fg = '#b16286', ctermfg= 'Magenta'}) +end + +define_hlgroups() + + +--- [ SET UP AUTOCOMMANDS ]---------------------------------------------------- +local hl_augroup = create_augroup('TSRainbowHighlight', {}) +local rb_augroup = create_augroup('TSRainbowDelimits', {}) + +create_autocmd('ColorScheme', { + desc = 'Re-apply highlight group definitions when the colour scheme changes', + group = hl_augroup, + callback = define_hlgroups +}) + +create_autocmd('FileType', { + desc = 'Attach to a new buffer', + group = rb_augroup, + callback = function(args) + local lang = get_lang(args.match) + if not config.enabled_for(lang) then return end + + lib.attach(args.buf) + end, +}) + +create_autocmd('BufUnload', { + desc = 'Detach from the current buffer', + group = rb_augroup, + callback = function(args) lib.detach(args.buf) end +}) + +vim.g.loaded_rainbow = true + +-- vim:tw=79:ts=4:sw=4:noet: diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/astro/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/astro/rainbow-delimiters.scm new file mode 100644 index 00000000..9ed95ce1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/astro/rainbow-delimiters.scm @@ -0,0 +1,13 @@ +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(interpolation + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/bash/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/bash/rainbow-delimiters.scm new file mode 100644 index 00000000..b9473ccb --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/bash/rainbow-delimiters.scm @@ -0,0 +1,37 @@ +(command_substitution + "$(" @delimiter + ")" @delimiter @sentinel) @container + +(expansion + "${" @delimiter + (":-" @delimiter)? + "}" @delimiter @sentinel) @container + +;;; The double-bracket variant is a bashism +(test_command + ["[[" "["] @delimiter + ["]]" "]"] @delimiter @sentinel) @container + +(subshell + "(" @delimiter + ")" @delimiter @sentinel) @container + +(array + "(" @delimiter + ")" @delimiter @sentinel) @container + +(function_definition + "(" @delimiter + ")" @delimiter @sentinel) @container + +(arithmetic_expansion + "$((" @delimiter + "))" @delimiter @sentinel) @container + +(compound_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(subscript + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/c/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/c/rainbow-delimiters.scm new file mode 100644 index 00000000..4da1892b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/c/rainbow-delimiters.scm @@ -0,0 +1,67 @@ +(parameter_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(compound_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(initializer_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +; This highlights the nested levels in an array differently +; although they are the same level in terms of the nesting +; of delimiters +(subscript_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(field_declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_declarator + "[" @delimiter + "]" @delimiter @sentinel) @container + +(sizeof_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +; Comment out the following to not highlight type casts +(cast_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(enumerator_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(macro_type_specifier + "(" @delimiter + ")" @delimiter @sentinel) @container + +(preproc_params + "(" @delimiter + ")" @delimiter @sentinel) @container + +(compound_literal_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_declarator + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/c_sharp/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/c_sharp/rainbow-delimiters.scm new file mode 100644 index 00000000..87e2175b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/c_sharp/rainbow-delimiters.scm @@ -0,0 +1,143 @@ +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameter_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(if_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(foreach_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(while_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(do_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(attribute_argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(attribute_list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(switch_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(switch_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(switch_expression + "{" @delimiter + "}" @delimiter @sentinel) @container + +(default_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(catch_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container + +(catch_filter_clause + "(" @delimiter + ")" @delimiter @sentinel) @container + +(using_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(lock_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cast_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(typeof_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(sizeof_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(checked_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(accessor_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(anonymous_object_creation_expression + "{" @delimiter + "}" @delimiter @sentinel) @container + +(enum_member_declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_parameter_list + "<" @delimiter + ">" @delimiter @sentinel) @container + +(type_argument_list + "<" @delimiter + ">" @delimiter @sentinel) @container + +(initializer_expression + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_rank_specifier + "[" @delimiter + "]" @delimiter @sentinel) @container + +(bracketed_argument_list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(implicit_array_creation_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(implicit_stackalloc_expression + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/clojure/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/clojure/rainbow-delimiters.scm new file mode 100644 index 00000000..a4b6a953 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/clojure/rainbow-delimiters.scm @@ -0,0 +1,19 @@ +(list_lit + "(" @delimiter + ")" @delimiter @sentinel) @container + +(vec_lit + "[" @delimiter + "]" @delimiter @sentinel) @container + +(map_lit + "{" @delimiter + "}" @delimiter @sentinel) @container + +(set_lit + "{" @delimiter + "}" @delimiter @sentinel) @container + +(anon_fn_lit + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/commonlisp/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/commonlisp/rainbow-delimiters.scm new file mode 100644 index 00000000..3b216d0d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/commonlisp/rainbow-delimiters.scm @@ -0,0 +1,13 @@ +(list_lit + "(" @delimiter + _* + ")" @delimiter @sentinel) @container + +(defun + "(" @delimiter + _* + ")" @delimiter @sentinel) @container + +(loop_macro + "(" @delimiter + ")" @delimiter @sentinel ) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/cpp/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/cpp/rainbow-delimiters.scm new file mode 100644 index 00000000..81648921 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/cpp/rainbow-delimiters.scm @@ -0,0 +1,61 @@ +;;; NOTE: The C and C++ grammar have diverged, so I cannot include the C query. + +(parameter_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(condition_clause + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(compound_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cast_expression + "(" @delimiter + ")" @delimiter @sentinel) + +(initializer_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_declarator + "[" @delimiter + "]" @delimiter @sentinel) @container + +(subscript_argument_list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(field_declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(template_parameter_list + "<" @delimiter + ">" @delimiter @sentinel) @container + +(initializer_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(template_argument_list + "<" @delimiter + ">" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/css/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/css/rainbow-delimiters.scm new file mode 100644 index 00000000..cef27cbe --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/css/rainbow-delimiters.scm @@ -0,0 +1,19 @@ +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(parenthesized_query + "(" @delimiter + ")" @delimiter @sentinel) @container + +(feature_query + "(" @delimiter + ")" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(attribute_selector + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/cuda/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/cuda/rainbow-delimiters.scm new file mode 100644 index 00000000..b910498c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/cuda/rainbow-delimiters.scm @@ -0,0 +1,5 @@ +; inherits: cpp + +(kernel_call_syntax + "<<<" @delimiter + ">>>" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/cue/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/cue/rainbow-delimiters.scm new file mode 100644 index 00000000..17fd570e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/cue/rainbow-delimiters.scm @@ -0,0 +1,31 @@ +(struct_lit + "{" @delimiter + "}" @delimiter @sentinel) @container + +(list_lit + "[" @delimiter + "]" @delimiter @sentinel) @container + +(label + "[" @delimiter + "]" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(attribute + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(import_spec_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(interpolation + "\\(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/rainbow-delimiters.scm new file mode 100644 index 00000000..8871365f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/dart/rainbow-delimiters.scm @@ -0,0 +1,31 @@ +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(formal_parameter_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(optional_formal_parameters + "{" @delimiter + "}" @delimiter @sentinel) @container + +(list_literal + "[" @delimiter + "]" @delimiter @sentinel) @container + +(set_or_map_literal + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/elixir/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/elixir/rainbow-delimiters.scm new file mode 100644 index 00000000..8e84fc9c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/elixir/rainbow-delimiters.scm @@ -0,0 +1,34 @@ +(call + (arguments + "(" @delimiter + ")" @delimiter @sentinel) @container) + +(block + "(" @delimiter + ")" @delimiter @sentinel) @container + +(string + (interpolation + "#{" @delimiter + "}" @delimiter @sentinel) @container) + +(tuple + "{" @delimiter + "}" @delimiter @sentinel) @container + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(access_call + "[" @delimiter + "]" @delimiter @sentinel) @container + +(bitstring + "<<" @delimiter + ">>" @delimiter @sentinel) @container + +(map + "%" @delimiter + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/elm/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/elm/rainbow-delimiters.scm new file mode 100644 index 00000000..220de9d8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/elm/rainbow-delimiters.scm @@ -0,0 +1,63 @@ +(exposing_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(exposed_operator + "(" @delimiter + ")" @delimiter @sentinel) @container + +(exposed_union_constructors + "(" @delimiter + ")" @delimiter @sentinel) @container + +(_ + "(" @delimiter + . + (type_expression) + . + ")" @delimiter @sentinel +) @container + +(_ +"(" @delimiter + . + [(pattern) (union_pattern)] + . +")" @delimiter @sentinel +) @container + +(record_expr + "{" @delimiter + "}" @delimiter @sentinel) @container + +(record_type + "{" @delimiter + "}" @delimiter @sentinel) @container + +(record_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(tuple_expr + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expr + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list_expr + "[" @delimiter + "]" @delimiter @sentinel) @container + +(list_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/rainbow-delimiters.scm new file mode 100644 index 00000000..520ddb84 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fennel/rainbow-delimiters.scm @@ -0,0 +1,3 @@ +(_ + open: ["(" "[" "{"] @delimiter + close: [")" "]" "}"] @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fish/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fish/rainbow-delimiters.scm new file mode 100644 index 00000000..c1b238ce --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/fish/rainbow-delimiters.scm @@ -0,0 +1,15 @@ +(command_substitution + "(" @delimiter + ")" @delimiter @sentinel) @container + +(concatenation + "[" @delimiter + "]" @delimiter @sentinel) @container + +(list_element_access + "[" @delimiter + "]" @delimiter @sentinel) @container + +(brace_expansion + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/go/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/go/rainbow-delimiters.scm new file mode 100644 index 00000000..9a4a575a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/go/rainbow-delimiters.scm @@ -0,0 +1,79 @@ +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(import_spec_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(var_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container + +(const_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container + +(type_assertion_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(field_declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameter_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(expression_switch_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_switch_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(literal_value + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(slice_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(map_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(interface_type + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_parameter_list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_arguments + "[" @delimiter + "]" @delimiter @sentinel) @container + +(index_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(slice_expression + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/haskell/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/haskell/rainbow-delimiters.scm new file mode 100644 index 00000000..97cf24ed --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/haskell/rainbow-delimiters.scm @@ -0,0 +1,39 @@ +(parens + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple + "(" @delimiter + ")" @delimiter @sentinel) @container + +(unit + "(" @delimiter + ")" @delimiter @sentinel) @container + +(exports + "(" @delimiter + ")" @delimiter @sentinel) @container + +(children + "(" @delimiter + ")" @delimiter @sentinel) @container + +(import_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(prefix_id + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(fields + "{" @delimiter + "}" @delimiter @sentinel) @container + +(record + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/hcl/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/hcl/rainbow-delimiters.scm new file mode 100644 index 00000000..7693e4f6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/hcl/rainbow-delimiters.scm @@ -0,0 +1,47 @@ +(tuple + (tuple_start "[") @delimiter + (tuple_end "]") @delimiter @sentinel) @container + +(for_tuple_expr + (tuple_start "[") @delimiter + (tuple_end "]") @delimiter @sentinel) @container + +(new_index + "[" @delimiter + "]" @delimiter @sentinel) @container + +(function_call + "(" @delimiter + ")" @delimiter @sentinel) @container + +(expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(binary_operation + "(" @delimiter + ")" @delimiter @sentinel) @container + +(unary_operation + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block + (block_start "{") @delimiter + (block_end "}") @delimiter @sentinel) @container + +(object + (object_start "{") @delimiter + (object_end "}") @delimiter @sentinel) @container + +(for_object_expr + (object_start "{") @delimiter + (object_end "}") @delimiter @sentinel) @container + +(template_interpolation + (template_interpolation_start) @delimiter + (template_interpolation_end) @delimiter @sentinel) @container + +(_ + (template_directive_start) @delimiter + (template_directive_end) @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/html/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/html/rainbow-delimiters.scm new file mode 100644 index 00000000..24e63db6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/html/rainbow-delimiters.scm @@ -0,0 +1,69 @@ +;;; A pair of delimiter tags with any content in-between. +;;; Last tag should be a sentinel. + +;;; If instead you want rainbow-delimiters to only highlight +;;; the tag names without any of "<", "" or "/>", then +;;; you can make your own query file, e.g., +;;; 'rainbow-tag-names' +;;; and use the following with +;;; x @delimiter +;;; deleted for x equal to any of "<", "" or "/>". + +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(element + (self_closing_tag + "<" @delimiter + (tag_name) @delimiter + "/>" @delimiter @sentinel)) @container + +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter @_tag_name + ">" @delimiter @sentinel) + (#any-of? @_tag_name + "area" + "base" + "br" + "col" + "embed" + "hr" + "img" + "input" + "link" + "meta" + "param" + "source" + "track" + "wbr") +) @container + +(style_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (element (self_closing_tag) @delimiter)* + (end_tag + "" @delimiter @sentinel)) @container + +(script_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/janet_simple/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/janet_simple/rainbow-delimiters.scm new file mode 100644 index 00000000..02947931 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/janet_simple/rainbow-delimiters.scm @@ -0,0 +1,23 @@ +(par_tup_lit + "(" @delimiter + ")" @delimiter @sentinel) @container + +(par_arr_lit + "@(" @delimiter + ")" @delimiter @sentinel) @container + +(sqr_tup_lit + "[" @delimiter + "]" @delimiter @sentinel) @container + +(sqr_arr_lit + "@[" @delimiter + "]" @delimiter @sentinel) @container + +(struct_lit + "{" @delimiter + "}" @delimiter @sentinel) @container + +(tbl_lit + "@{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/java/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/java/rainbow-delimiters.scm new file mode 100644 index 00000000..bca00206 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/java/rainbow-delimiters.scm @@ -0,0 +1,70 @@ +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_initializer + "{" @delimiter + "}" @delimiter @sentinel) @container + +(formal_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(resource_specification + "(" @delimiter + ")" @delimiter @sentinel) @container + +(catch_clause + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cast_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(inferred_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(annotation_argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(enhanced_for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(constructor_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +;; Treat it as a single delimiter because it will always have the same color +(dimensions) @container @delimiter @sentinel + +(dimensions_expr + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_access + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-delimiters-react.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-delimiters-react.scm new file mode 100644 index 00000000..229898a2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-delimiters-react.scm @@ -0,0 +1,102 @@ +;;; This query includes React support as well. + +;; String interpolation inside template strings +(template_substitution + "${" @delimiter + "}" @delimiter @sentinel) @container + +(object + "{" @delimiter + "}" @delimiter @sentinel) @container + +(statement_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(switch_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(formal_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_in_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subscript_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(named_imports + "{" @delimiter + "}" @delimiter @sentinel) @container + +(export_clause + "{" @delimiter + "}" @delimiter @sentinel) @container + +(object_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container + + +;;; React.js support +(jsx_element + open_tag: (jsx_opening_element + "<" @delimiter + name: (identifier) @delimiter + ">" @delimiter) + close_tag: (jsx_closing_element + "" @delimiter @sentinel)) @container + +(jsx_element + open_tag: (jsx_opening_element + "<" @delimiter + name: (member_expression) @delimiter + ">" @delimiter) + close_tag: (jsx_closing_element + "" @delimiter @sentinel)) @container + +(jsx_self_closing_element + "<" @delimiter + name: (identifier) @delimiter + "/>" @delimiter @sentinel) @container + +(jsx_self_closing_element + "<" @delimiter + name: (member_expression) @delimiter + "/>" @delimiter @sentinel) @container + +(jsx_expression + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-delimiters.scm new file mode 100644 index 00000000..766a017b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-delimiters.scm @@ -0,0 +1,68 @@ +;;; This query only covers Javascript without any React support. It exists so +;;; that Typescript query can inherit it. + +;; String interpolation inside template strings +(template_substitution + "${" @delimiter + "}" @delimiter @sentinel) @container + +(object + "{" @delimiter + "}" @delimiter @sentinel) @container + +(statement_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(switch_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(formal_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_in_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subscript_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(named_imports + "{" @delimiter + "}" @delimiter @sentinel) @container + +(export_clause + "{" @delimiter + "}" @delimiter @sentinel) @container + +(object_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-parens.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-parens.scm new file mode 100644 index 00000000..766a017b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-parens.scm @@ -0,0 +1,68 @@ +;;; This query only covers Javascript without any React support. It exists so +;;; that Typescript query can inherit it. + +;; String interpolation inside template strings +(template_substitution + "${" @delimiter + "}" @delimiter @sentinel) @container + +(object + "{" @delimiter + "}" @delimiter @sentinel) @container + +(statement_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(switch_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(formal_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_in_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subscript_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(named_imports + "{" @delimiter + "}" @delimiter @sentinel) @container + +(export_clause + "{" @delimiter + "}" @delimiter @sentinel) @container + +(object_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-tags-react.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-tags-react.scm new file mode 100644 index 00000000..a9c2fe81 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/javascript/rainbow-tags-react.scm @@ -0,0 +1,29 @@ +(jsx_element + open_tag: (jsx_opening_element + "<" @delimiter + name: (identifier) @delimiter + ">" @delimiter) + close_tag: (jsx_closing_element + "" @delimiter @sentinel)) @container + +(jsx_element + open_tag: (jsx_opening_element + "<" @delimiter + name: (member_expression) @delimiter + ">" @delimiter) + close_tag: (jsx_closing_element + "" @delimiter @sentinel)) @container + +(jsx_self_closing_element + "<" @delimiter + name: (identifier) @delimiter + "/>" @delimiter @sentinel) @container + +(jsx_self_closing_element + "<" @delimiter + name: (member_expression) @delimiter + "/>" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/json/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/json/rainbow-delimiters.scm new file mode 100644 index 00000000..8ff0d788 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/json/rainbow-delimiters.scm @@ -0,0 +1,7 @@ +(object + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/json5/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/json5/rainbow-delimiters.scm new file mode 100644 index 00000000..41269219 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/json5/rainbow-delimiters.scm @@ -0,0 +1 @@ +; inherits: json diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/jsonc/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/jsonc/rainbow-delimiters.scm new file mode 100644 index 00000000..41269219 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/jsonc/rainbow-delimiters.scm @@ -0,0 +1 @@ +; inherits: json diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/jsonnet/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/jsonnet/rainbow-delimiters.scm new file mode 100644 index 00000000..aacaad47 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/jsonnet/rainbow-delimiters.scm @@ -0,0 +1,39 @@ +(anonymous_function + "(" @delimiter + ")" @delimiter @sentinel) @container + +(functioncall + "(" @delimiter + ")" @delimiter @sentinel) @container + +(bind + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesis + "(" @delimiter + ")" @delimiter @sentinel) @container + +(field + "(" @delimiter + ")" @delimiter @sentinel) @container + +(fieldname + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(forloop + "[" @delimiter + "]" @delimiter @sentinel) @container + +(indexing + "[" @delimiter + "]" @delimiter @sentinel) @container + +(object + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/julia/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/julia/rainbow-delimiters.scm new file mode 100644 index 00000000..e45fec43 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/julia/rainbow-delimiters.scm @@ -0,0 +1,27 @@ +(vector_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(matrix_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(comprehension_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(tuple_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(curly_expression + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/kotlin/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/kotlin/rainbow-delimiters.scm new file mode 100644 index 00000000..7001e259 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/kotlin/rainbow-delimiters.scm @@ -0,0 +1,55 @@ +(class_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(function_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(control_structure_body + "{" @delimiter + "}" @delimiter @sentinel) @container + + +(lambda_literal + "{" @delimiter + "}" @delimiter @sentinel) @container + +(primary_constructor + "(" @delimiter + ")" @delimiter @sentinel) @container + +(function_value_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(value_arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(multi_variable_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(when_expression + (when_subject + "(" @delimiter + ")" @delimiter)? + "{" @delimiter + "}" @delimiter @sentinel) @container + +(indexing_suffix + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_parameters + "<" @delimiter + ">" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/latex/rainbow-blocks.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/latex/rainbow-blocks.scm new file mode 100644 index 00000000..ba71cd16 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/latex/rainbow-blocks.scm @@ -0,0 +1,11 @@ +(inline_formula + "$" @delimiter + "$" @delimiter @sentinel) @container + +(generic_environment + (begin) @delimiter + (end) @delimiter @sentinel) @container + +(math_environment + (begin) @delimiter + (end) @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/latex/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/latex/rainbow-delimiters.scm new file mode 100644 index 00000000..6189163e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/latex/rainbow-delimiters.scm @@ -0,0 +1,15 @@ +(curly_group + "{" @delimiter + "}" @delimiter @sentinel) @container + +(curly_group_text + "{" @delimiter + "}" @delimiter @sentinel) @container + +(curly_group_text_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(inline_formula + "$" @delimiter + "$" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/lua/rainbow-blocks.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/lua/rainbow-blocks.scm new file mode 100644 index 00000000..5ac26c5c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/lua/rainbow-blocks.scm @@ -0,0 +1,69 @@ +;;; Note: Some patterns are commented out because currently Neovim can capture +;;; only one node at a time. Once it becomes possible to capture multiple +;;; nodes they should be commented back in. + + +(function_declaration + "function" @delimiter + "end" @delimiter @sentinel) @container + +(function_definition + "function" @delimiter + "end" @delimiter @sentinel) @container + +(if_statement + "if" @delimiter + "then" @delimiter + (elseif_statement + "elseif" @delimiter + "then" @delimiter)* + (else_statement + "else" @delimiter)? + "end" @delimiter @sentinel) @container + +(while_statement + "while" @delimiter + "do" @delimiter + "end" @delimiter @sentinel) @container + +(repeat_statement + "repeat" @delimiter + "until" @delimiter @sentinel) @container + +(for_statement + "for" @delimiter + (for_generic_clause + "in" @delimiter)? + "do" @delimiter + "end" @delimiter @sentinel) @container + +(do_statement + "do" @delimiter + "end" @delimiter @sentinel) @container + + +;;; Copied over from rainbow-parens + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(table_constructor + "{" @delimiter + "}" @delimiter @sentinel) @container + +(bracket_index_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(field + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/lua/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/lua/rainbow-delimiters.scm new file mode 100644 index 00000000..bb2a046d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/lua/rainbow-delimiters.scm @@ -0,0 +1,23 @@ +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(table_constructor + "{" @delimiter + "}" @delimiter @sentinel) @container + +(bracket_index_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(field + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/luadoc/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/luadoc/rainbow-delimiters.scm new file mode 100644 index 00000000..e7cdb20f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/luadoc/rainbow-delimiters.scm @@ -0,0 +1,40 @@ +(function_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +;;; I wanted to use something like +; (union_type +; "|" @delimiter @sentinel +; ) @container +;;; too, but it doesn't fully work with the current parser + +(array_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(table_type + "<" @delimiter + ">" @delimiter @sentinel) @container + +(table_literal_type + "{" @delimiter + "}" @delimiter @sentinel) @container + +(indexed_field + "[" @delimiter + "]" @delimiter @sentinel) @container + +(tuple_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +(_ + "[" @delimiter + . + field: (_) + . + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/make/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/make/rainbow-delimiters.scm new file mode 100644 index 00000000..4378129b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/make/rainbow-delimiters.scm @@ -0,0 +1 @@ +;;; Nothing I can think of, everything gets taken from the injected bash query. diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/markdown/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/markdown/rainbow-delimiters.scm new file mode 100644 index 00000000..701e470b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/markdown/rainbow-delimiters.scm @@ -0,0 +1,3 @@ +;;; Intentionally empty. Markdown can have other languages embedded and we +;;; want to let those grammars handle the highlighting. This query only exists +;;; to satisfy the requirements of this plugin. diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/nim/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/nim/rainbow-delimiters.scm new file mode 100644 index 00000000..e98aba49 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/nim/rainbow-delimiters.scm @@ -0,0 +1,47 @@ +(array_construction + "[" @delimiter + "]" @delimiter @sentinel) @container +(tuple_construction + "(" @delimiter + ")" @delimiter @sentinel) @container +(tuple_deconstruct_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container +(curly_construction + "{" @delimiter + ":"? @delimiter + "}" @delimiter @sentinel) @container + +(parenthesized + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container +(parameter_declaration_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(bracket_expression + "[" @delimiter + "]" @delimiter @sentinel) @container +(field_declaration_list + "[" @delimiter + "]" @delimiter @sentinel) @container +(generic_parameter_list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(cast + "[" @delimiter + "]" @delimiter + "(" @delimiter + ")" @delimiter @sentinel) @container + +(term_rewriting_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container +(curly_expression + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/nix/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/nix/rainbow-delimiters.scm new file mode 100644 index 00000000..cb347941 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/nix/rainbow-delimiters.scm @@ -0,0 +1,27 @@ +(attrset_expression + ("{" @delimiter) + ("}" @delimiter @sentinel)) @container + +(rec_attrset_expression + ("{" @delimiter) + ("}" @delimiter @sentinel)) @container + +(formals + ("{" @delimiter) + ("}" @delimiter @sentinel)) @container + +(list_expression + ("[" @delimiter) + ("]" @delimiter @sentinel)) @container + +(parenthesized_expression + ("(" @delimiter) + (")" @delimiter @sentinel)) @container + +(interpolation + ("${" @delimiter) + ("}" @delimiter @sentinel)) @container + +(inherit_from + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/perl/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/perl/rainbow-delimiters.scm new file mode 100644 index 00000000..eb532196 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/perl/rainbow-delimiters.scm @@ -0,0 +1,86 @@ +(argument + "(" @delimiter + ")" @delimiter @sentinel) @container + +(array + "(" @delimiter + ")" @delimiter @sentinel) @container + +(array_dereference + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_ref + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_ref + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(hash_access_variable + "->{" @delimiter + "}" @delimiter @sentinel) @container + +(hash_access_variable + "{" @delimiter + "}" @delimiter @sentinel) @container + +(hash_ref + "+" @delimiter + "{" @delimiter + "}" @delimiter @sentinel) @container + +(multi_var_declaration + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(standalone_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(parenthesized_argument + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(word_list_qw + (start_delimiter_qw) @delimiter + (end_delimiter_qw) @delimiter @sentinel) @container + +(regex_pattern_qr + (start_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container + +(command_qx_quoted + (start_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container + +(string_qq_quoted + (start_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container + +(patter_matcher_m + (start_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container + +(substitution_pattern_s + (start_delimiter) @delimiter + (separator_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container + +(transliteration_tr_or_y + (start_delimiter) @delimiter + (separator_delimiter) @delimiter + (end_delimiter) @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/php/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/php/rainbow-delimiters.scm new file mode 100644 index 00000000..41031450 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/php/rainbow-delimiters.scm @@ -0,0 +1,31 @@ +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(formal_parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(compound_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(encapsed_string + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array_creation_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(subscript_expression + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/python/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/python/rainbow-delimiters.scm new file mode 100644 index 00000000..dca135fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/python/rainbow-delimiters.scm @@ -0,0 +1,75 @@ +;; NOTE: When updating this file update the Starlark test file as well if +;; applicable. + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(list_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container + +(list_comprehension + "[" @delimiter + "]" @delimiter @sentinel) @container + +(dictionary + "{" @delimiter + "}" @delimiter @sentinel) @container + +(dict_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(dictionary_comprehension + "{" @delimiter + "}" @delimiter @sentinel) @container + +(set + "{" @delimiter + "}" @delimiter @sentinel) @container + +(set_comprehension + "{" @delimiter + "}" @delimiter @sentinel) @container + +(tuple + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(generator_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subscript + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_parameter + "[" @delimiter + "]" @delimiter @sentinel) @container + +(import_from_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(string + (interpolation + "{" @delimiter + "}" @delimiter @sentinel) @container) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/query/rainbow-blocks.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/query/rainbow-blocks.scm new file mode 100644 index 00000000..9c249729 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/query/rainbow-blocks.scm @@ -0,0 +1,28 @@ +;; Note: These queries are very useful when looking at a large +;; tree of queries like in `InspectTree` +(named_node + "(" @delimiter + (identifier) @delimiter + ")" @delimiter @sentinel) @container + +(grouping + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(predicate + "(" @delimiter + ")" @delimiter @sentinel) @container + +(field_definition + (identifier) @delimiter @sentinel) @container + +;; For more highlighting the following can be added too: +; (parameters +; (identifier) @delimiter @sentinel) @container +; +; (negated_field +; (identifier) @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/query/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/query/rainbow-delimiters.scm new file mode 100644 index 00000000..fff8c317 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/query/rainbow-delimiters.scm @@ -0,0 +1,15 @@ +(named_node + "(" @delimiter + ")" @delimiter @sentinel) @container + +(grouping + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(predicate + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/r/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/r/rainbow-delimiters.scm new file mode 100644 index 00000000..f76b3ac3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/r/rainbow-delimiters.scm @@ -0,0 +1,36 @@ +(call + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subset + "[" @delimiter + "]" @delimiter @sentinel) @container + +(subset2 + "[[" @delimiter + "]]" @delimiter @sentinel) @container + +(if + "(" @delimiter + ")" @delimiter @sentinel) @container + +(for + "(" @delimiter + ")" @delimiter @sentinel) @container + +(while + "(" @delimiter + ")" @delimiter @sentinel) @container + +(switch + "(" @delimiter + ")" @delimiter @sentinel) @container + +(function_definition + (formal_parameters + "(" @delimiter + ")" @delimiter @sentinel)) @container + +(brace_list + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/racket/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/racket/rainbow-delimiters.scm new file mode 100644 index 00000000..4ff8c877 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/racket/rainbow-delimiters.scm @@ -0,0 +1,14 @@ +(list + "(" @delimiter + (dot)? @delimiter + ")" @delimiter @sentinel) @container + +(list + "[" @delimiter + (dot)? @delimiter + "]" @delimiter @sentinel) @container + +(list + "{" @delimiter + (dot)? @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/rasi/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/rasi/rainbow-delimiters.scm new file mode 100644 index 00000000..2295469c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/rasi/rainbow-delimiters.scm @@ -0,0 +1,52 @@ +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(environ_value + "$" @delimiter + "{" @delimiter + "}" @delimiter @sentinel) @container + +(environ_value + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list_value + "[" @delimiter + "]" @delimiter @sentinel) @container + +(distance_calc + "(" @delimiter + ")" @delimiter @sentinel) @container + +(feature_query + "(" @delimiter + ")" @delimiter @sentinel) @container + +(reference_value + "(" @delimiter + ")" @delimiter @sentinel) @container + +(rgb_color + "(" @delimiter + ")" @delimiter @sentinel) @container + +(hsl_color + "(" @delimiter + ")" @delimiter @sentinel) @container + +(hwb_color + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cmyk_color + "(" @delimiter + ")" @delimiter @sentinel) @container + +(url_image + "(" @delimiter + ")" @delimiter @sentinel) @container + +(gradient_image + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/regex/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/regex/rainbow-delimiters.scm new file mode 100644 index 00000000..9d117932 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/regex/rainbow-delimiters.scm @@ -0,0 +1,17 @@ +(anonymous_capturing_group + "(" @delimiter + ")" @delimiter @sentinel) @container + +;;; The inversion `^` should be an opening node as well +(character_class + "[" @delimiter + "]" @delimiter @sentinel) @container + +(count_quantifier + "{" @delimiter + "}" @delimiter @sentinel) @container + +;;; We should probably include the character after `?` like `=` as well +(lookaround_assertion + "(?" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/rst/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/rst/rainbow-delimiters.scm new file mode 100644 index 00000000..5da7b9e5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/rst/rainbow-delimiters.scm @@ -0,0 +1,3 @@ +;;; Intentionally empty. ReStructuredText can have other languages embedded +;;; and we want to let those grammars handle the highlighting. This query only +;;; exists to satisfy the requirements of this plugin. diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/ruby/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/ruby/rainbow-delimiters.scm new file mode 100644 index 00000000..48b1a664 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/ruby/rainbow-delimiters.scm @@ -0,0 +1,19 @@ +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(hash + "{" @delimiter + "}" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(parenthesized_statements + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block_parameters + "|" @delimiter + "|" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/rust/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/rust/rainbow-delimiters.scm new file mode 100644 index 00000000..b42420c9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/rust/rainbow-delimiters.scm @@ -0,0 +1,133 @@ +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(field_declaration_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(ordered_field_declaration_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(enum_variant_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(use_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(field_initializer_list + "{" @delimiter + "}" @delimiter @sentinel) @container + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(match_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(tuple_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_type + "(" @delimiter + ")" @delimiter @sentinel) @container + +(token_tree + "{" @delimiter + "}" @delimiter @sentinel) @container + +(token_tree + "(" @delimiter + ")" @delimiter @sentinel) @container + +(token_tree + "[" @delimiter + "]" @delimiter @sentinel) @container + +(token_tree_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(token_repetition_pattern + "$" @delimiter + "(" @delimiter + ")" @delimiter @sentinel) @container + +(token_repetition + "$" @delimiter + "(" @delimiter + ")" @delimiter @sentinel) @container + +(attribute_item + "[" @delimiter + "]" @delimiter @sentinel) @container + +(inner_attribute_item + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container + +(type_parameters + "<" @delimiter + ">" @delimiter @sentinel) @container + +(closure_parameters + "|" @delimiter + "|" @delimiter @sentinel) @container + +(array_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(index_expression + "[" @delimiter + "]" @delimiter @sentinel) @container + +(tuple_struct_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(struct_pattern + "{" @delimiter + "}" @delimiter @sentinel) @container + +(slice_pattern + "[" @delimiter + "]" @delimiter @sentinel) @container + +(macro_definition + "{" @delimiter + "}" @delimiter @sentinel) @container + +(visibility_modifier + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/scheme/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/scheme/rainbow-delimiters.scm new file mode 100644 index 00000000..41bbada2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/scheme/rainbow-delimiters.scm @@ -0,0 +1,7 @@ +(list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/scss/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/scss/rainbow-delimiters.scm new file mode 100644 index 00000000..af30cd70 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/scss/rainbow-delimiters.scm @@ -0,0 +1,5 @@ +; inherits: css + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/sql/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/sql/rainbow-delimiters.scm new file mode 100644 index 00000000..e6c8e03e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/sql/rainbow-delimiters.scm @@ -0,0 +1,28 @@ +(subquery + "(" @delimiter + ")" @delimiter @sentinel) @container + +(invocation + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(where + "(" @delimiter + ")" @delimiter @sentinel) @container + +(binary_expression + "(" @delimiter + ")" @delimiter @sentinel + ) @container + +; The following can cause problems with (((()))) +(term + "(" @delimiter + ; ("(" ")")* ; to fix _some_ problems, this can be uncommented + ")" @delimiter @sentinel + ) @container + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/starlark/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/starlark/rainbow-delimiters.scm new file mode 100644 index 00000000..2f28ba57 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/starlark/rainbow-delimiters.scm @@ -0,0 +1,56 @@ +;; This is mostly identical to Python, without `generator_expression`. +;; NOTE: if you update queries for Python, please consider adding the changes +;; to this file as well, given that the tree-sitter's node types exist. See +;; https://github.com/amaanq/tree-sitter-starlark/blob/master/src/node-types.json + +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(list_comprehension + "[" @delimiter + "]" @delimiter @sentinel) @container + +(dictionary + "{" @delimiter + "}" @delimiter @sentinel) @container + +(dictionary_comprehension + "{" @delimiter + "}" @delimiter @sentinel) @container + +(set + "{" @delimiter + "}" @delimiter @sentinel) @container + +(set_comprehension + "{" @delimiter + "}" @delimiter @sentinel) @container + +(tuple + "(" @delimiter + ")" @delimiter @sentinel) @container + +(tuple_pattern + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameters + "(" @delimiter + ")" @delimiter @sentinel) @container + +(argument_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parenthesized_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(subscript + "[" @delimiter + "]" @delimiter @sentinel) @container + +(type_parameter + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/svelte/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/svelte/rainbow-delimiters.scm new file mode 100644 index 00000000..0d483a5c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/svelte/rainbow-delimiters.scm @@ -0,0 +1,35 @@ +(script_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(style_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(element + (self_closing_tag + "<" @delimiter + (tag_name) @delimiter + "/>" @delimiter @sentinel)) @container + +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/svelte/rainbow-delimiters.txt b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/svelte/rainbow-delimiters.txt new file mode 100644 index 00000000..38e926b1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/svelte/rainbow-delimiters.txt @@ -0,0 +1,53 @@ +(script_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(style_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(element + (self_closing_tag + "<" @delimiter + (tag_name) @delimiter + "/>" @delimiter @sentinel)) @container + +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(const_expr + "{" @delimiter + "@" @delimiter + (special_block_keyword) @delimiter + "}" @delimiter @sentinel) @container + +(each_statement + (each_start_expr + "{" @delimiter + "#" @delimiter + (special_block_keyword) @delimiter + "}" @delimiter) @container + (each_end_expr + "{" @delimiter + "/" @delimiter + (special_block_keyword) @delimiter + "}" @delimiter @sentinel)) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/teal/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/teal/rainbow-delimiters.scm new file mode 100644 index 00000000..a8a0967d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/teal/rainbow-delimiters.scm @@ -0,0 +1,19 @@ +(arguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(table_constructor + "{" @delimiter + "}" @delimiter @sentinel) @container + +(table_entry + "[" @delimiter + "]" @delimiter @sentinel) @container + +(index + "[" @delimiter + "]" @delimiter @sentinel) @container + +(field + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/templ/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/templ/rainbow-delimiters.scm new file mode 100644 index 00000000..a28443e3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/templ/rainbow-delimiters.scm @@ -0,0 +1,61 @@ +; inherits: go + +;; HTML elements + +(element + (tag_start + "<" @delimiter + (element_identifier) @delimiter + ">" @delimiter) + (tag_end + "" @delimiter @sentinel)) @container + +(element + (self_closing_tag + "<" @delimiter + (element_identifier) @delimiter + "/>" @delimiter @sentinel)) @container + +(style_element + ("<" @delimiter + "style" @delimiter + ">" @delimiter) + ("" @delimiter @sentinel)) @container + +(script_element + ("<" @delimiter + "script" @delimiter + ">" @delimiter) + ("" @delimiter @sentinel)) @container + +;; Brackets + +(component_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(script_block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(css_declaration + "{" @delimiter + "}" @delimiter @sentinel) @container + +(component_switch_statement + "{" @delimiter + "}" @delimiter @sentinel) @container + +(component_children_expression + "{" @delimiter + "}" @delimiter @sentinel) @container + +(expression + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/terraform/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/terraform/rainbow-delimiters.scm new file mode 100644 index 00000000..0e5ffc2d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/terraform/rainbow-delimiters.scm @@ -0,0 +1 @@ +; inherits: hcl diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/toml/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/toml/rainbow-delimiters.scm new file mode 100644 index 00000000..46170e6c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/toml/rainbow-delimiters.scm @@ -0,0 +1,15 @@ +(table + "[" @delimiter + "]" @delimiter @sentinel) @container + +(array + "[" @delimiter + "]" @delimiter @sentinel) @container + +(inline_table + "{" @delimiter + "}" @delimiter @sentinel) @container + +(table_array_element + "[[" @delimiter + "]]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/rainbow-delimiters.scm new file mode 100644 index 00000000..0e30ed34 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/rainbow-delimiters.scm @@ -0,0 +1,36 @@ +; inherits: typescript + +(jsx_element + open_tag: (jsx_opening_element + "<" @delimiter + name: (identifier) @delimiter + ">" @delimiter) + close_tag: (jsx_closing_element + "" @delimiter @sentinel)) @container + +(jsx_element + open_tag: (jsx_opening_element + "<" @delimiter + name: (member_expression) @delimiter + ">" @delimiter) + close_tag: (jsx_closing_element + "" @delimiter @sentinel)) @container + +(jsx_self_closing_element + "<" @delimiter + name: (identifier) @delimiter + "/>" @delimiter @sentinel) @container + + +(jsx_self_closing_element + "<" @delimiter + name: (member_expression) @delimiter + "/>" @delimiter @sentinel) @container + +(jsx_expression + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/rainbow-parens.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/rainbow-parens.scm new file mode 100644 index 00000000..4dbf4aad --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/rainbow-parens.scm @@ -0,0 +1,4 @@ +; inherits: typescript + +;;; This query exists for people who only want to highlight parentheses without +;;; tags. diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/rainbow-tags-react.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/rainbow-tags-react.scm new file mode 100644 index 00000000..19453238 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/tsx/rainbow-tags-react.scm @@ -0,0 +1,4 @@ +; inherits: javascript + +;;; This query exists for people who only want to highlight tags without +;;; parentheses. diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/typescript/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/typescript/rainbow-delimiters.scm new file mode 100644 index 00000000..9818cb7e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/typescript/rainbow-delimiters.scm @@ -0,0 +1,25 @@ +; inherits: javascript + +(interface_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(enum_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container + +(type_parameters + "<" @delimiter + ">" @delimiter @sentinel) @container + +(lookup_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(object_type + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/typescript/rainbow-parens.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/typescript/rainbow-parens.scm new file mode 100644 index 00000000..9818cb7e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/typescript/rainbow-parens.scm @@ -0,0 +1,25 @@ +; inherits: javascript + +(interface_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(enum_body + "{" @delimiter + "}" @delimiter @sentinel) @container + +(type_arguments + "<" @delimiter + ">" @delimiter @sentinel) @container + +(type_parameters + "<" @delimiter + ">" @delimiter @sentinel) @container + +(lookup_type + "[" @delimiter + "]" @delimiter @sentinel) @container + +(object_type + "{" @delimiter + "}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/typst/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/typst/rainbow-delimiters.scm new file mode 100644 index 00000000..e68621d7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/typst/rainbow-delimiters.scm @@ -0,0 +1,19 @@ +(group + "(" @delimiter + ")" @delimiter @sentinel) @container + +(block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(content + "[" @delimiter + "]" @delimiter @sentinel) @container + +(math + "$" @delimiter + "$" @delimiter @sentinel) @container + +(call + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/verilog/rainbow-blocks.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/verilog/rainbow-blocks.scm new file mode 100644 index 00000000..e14b56cf --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/verilog/rainbow-blocks.scm @@ -0,0 +1,83 @@ +; match blocks + +(seq_block + "begin" @delimiter + "end" @delimiter @sentinel) @container + +; match parentheses + +(packed_dimension + "[" @delimiter + "]" @delimiter @sentinel) @container + +(data_type + "{" @delimiter + "}" @delimiter @sentinel) @container + +(parameter_port_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(named_port_connection + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list_of_port_declarations + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameter_value_assignment + "(" @delimiter + ")" @delimiter @sentinel) @container + +(named_parameter_assignment + "(" @delimiter + ")" @delimiter @sentinel) @container + +(hierarchical_instance + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cast + "(" @delimiter + ")" @delimiter @sentinel) @container + +(conditional_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(event_control + "(" @delimiter + ")" @delimiter @sentinel) @container + +(primary + "(" @delimiter + ")" @delimiter @sentinel) @container + +(constant_primary + "(" @delimiter + ")" @delimiter @sentinel) @container + +(concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(constant_concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(multiple_concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(constant_select1 + "[" @delimiter + "]" @delimiter @sentinel) @container + +(bit_select1 + "[" @delimiter + "]" @delimiter @sentinel) @container + +(select1 + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/verilog/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/verilog/rainbow-delimiters.scm new file mode 100644 index 00000000..3743925c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/verilog/rainbow-delimiters.scm @@ -0,0 +1,75 @@ +(packed_dimension + "[" @delimiter + "]" @delimiter @sentinel) @container + +(data_type + "{" @delimiter + "}" @delimiter @sentinel) @container + +(parameter_port_list + "(" @delimiter + ")" @delimiter @sentinel) @container + +(named_port_connection + "(" @delimiter + ")" @delimiter @sentinel) @container + +(list_of_port_declarations + "(" @delimiter + ")" @delimiter @sentinel) @container + +(parameter_value_assignment + "(" @delimiter + ")" @delimiter @sentinel) @container + +(named_parameter_assignment + "(" @delimiter + ")" @delimiter @sentinel) @container + +(hierarchical_instance + "(" @delimiter + ")" @delimiter @sentinel) @container + +(cast + "(" @delimiter + ")" @delimiter @sentinel) @container + +(conditional_statement + "(" @delimiter + ")" @delimiter @sentinel) @container + +(event_control + "(" @delimiter + ")" @delimiter @sentinel) @container + +(primary + "(" @delimiter + ")" @delimiter @sentinel) @container + +(constant_primary + "(" @delimiter + ")" @delimiter @sentinel) @container + +(concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(constant_concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(multiple_concatenation + "{" @delimiter + "}" @delimiter @sentinel) @container + +(constant_select1 + "[" @delimiter + "]" @delimiter @sentinel) @container + +(bit_select1 + "[" @delimiter + "]" @delimiter @sentinel) @container + +(select1 + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/vim/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/vim/rainbow-delimiters.scm new file mode 100644 index 00000000..f606d228 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/vim/rainbow-delimiters.scm @@ -0,0 +1,29 @@ +;;; Note: The Vim grammar places all parentheses on the same level. This means +;;; an expression like (((3))) does not have three levels of nesting, but only +;;; one. All the parentheses and the integer literal are on the same level. +;;; This makes it impossible to apply alternating highlights. +(list + "[" @delimiter + "]" @delimiter @sentinel) @container + +(dictionnary ;; this is no typo, "dictionary" is misspelled in the parser + "{" @delimiter + (dictionnary_entry + ":" @delimiter) + "}" @delimiter @sentinel) @container + +(call_expression + "(" @delimiter + ")" @delimiter @sentinel) @container + +(unary_operation + "(" @delimiter + ")" @delimiter @sentinel) @container + +(binary_operation + "(" @delimiter + ")" @delimiter @sentinel) @container + +(ternary_expression + "(" @delimiter + ")" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/vimdoc/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/vimdoc/rainbow-delimiters.scm new file mode 100644 index 00000000..d53527c8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/vimdoc/rainbow-delimiters.scm @@ -0,0 +1,3 @@ +;;; Intentionally empty. Vim help files can have other languages embedded and +;;; we want to let those grammars handle the highlighting. This query only +;;; exists to satisfy the requirements of this plugin. diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/vue/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/vue/rainbow-delimiters.scm new file mode 100644 index 00000000..fb7bb619 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/vue/rainbow-delimiters.scm @@ -0,0 +1,52 @@ +;;; A Vue file is made up of top-level tags which contain code written in other +;;; languages + +(element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(element + (self_closing_tag + "<" @delimiter + (tag_name) @delimiter + "/>" @delimiter @sentinel)) @container + +(template_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(script_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(style_element + (start_tag + "<" @delimiter + (tag_name) @delimiter + ">" @delimiter) + (end_tag + "" @delimiter @sentinel)) @container + +(interpolation + "{{" @delimiter + "}}" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/xml/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/xml/rainbow-delimiters.scm new file mode 100644 index 00000000..fbe7b0a7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/xml/rainbow-delimiters.scm @@ -0,0 +1,15 @@ +(element + (STag + "<" @delimiter + (Name) @delimiter + ">" @delimiter) + (ETag + "" @delimiter @sentinel))@container + +(element + (EmptyElemTag + "<" @delimiter + (Name) @delimiter + "/>" @delimiter @sentinel)) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/yaml/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/yaml/rainbow-delimiters.scm new file mode 100644 index 00000000..5d70a3e4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/yaml/rainbow-delimiters.scm @@ -0,0 +1,7 @@ +(flow_mapping + "{" @delimiter + "}" @delimiter @sentinel) @container + +(flow_sequence + "[" @delimiter + "]" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/zig/rainbow-delimiters.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/zig/rainbow-delimiters.scm new file mode 100644 index 00000000..0bddd1e8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/queries/zig/rainbow-delimiters.scm @@ -0,0 +1,97 @@ +(ParamDeclList + "(" @delimiter + ")" @delimiter @sentinel) @container + +(FnCallArguments + "(" @delimiter + ")" @delimiter @sentinel) @container + +(IfPrefix + "(" @delimiter + ")" @delimiter @sentinel) @container + +(ForPrefix + "(" @delimiter + ")" @delimiter @sentinel) @container + +(WhilePrefix + "(" @delimiter + ")" @delimiter @sentinel) @container + +(LinkSection + "(" @delimiter + ")" @delimiter @sentinel) @container + +(CallConv + "(" @delimiter + ")" @delimiter @sentinel) @container + +(AsmExpr + "(" @delimiter + ")" @delimiter @sentinel) @container + +(ContainerDeclType + "(" @delimiter + ")" @delimiter @sentinel) @container + +(AsmInputItem + "[" @delimiter + "]" @delimiter + "(" @delimiter + ")" @delimiter @sentinel) @container + +(AsmOutputItem + "[" @delimiter + "]" @delimiter + "(" @delimiter + ")" @delimiter @sentinel) @container + +(SwitchExpr + "(" @delimiter + ")" @delimiter + "{" @delimiter + "}" @delimiter @sentinel) @container + +(ArrayTypeStart + "[" @delimiter + "]" @delimiter @sentinel) @container + +(SliceTypeStart + "[" @delimiter + "]" @delimiter @sentinel) @container + +(PtrTypeStart + "[" @delimiter + "]" @delimiter @sentinel) @container + +(SuffixOp + "[" @delimiter + "]" @delimiter @sentinel) @container + +(Block + "{" @delimiter + "}" @delimiter @sentinel) @container + +(ContainerDecl + "{" @delimiter + "}" @delimiter @sentinel) @container + +(InitList + "{" @delimiter + "}" @delimiter @sentinel) @container + +(FormatSequence + "{" @delimiter + "}" @delimiter @sentinel) @container + +(Payload + "|" @delimiter + "|" @delimiter @sentinel) @container + +(PtrListPayload + "|" @delimiter + "|" @delimiter @sentinel) @container + +(PtrIndexPayload + "|" @delimiter + "|" @delimiter @sentinel) @container diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/busted b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/busted new file mode 100755 index 00000000..872473a3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/busted @@ -0,0 +1,32 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/sh +# SPDX-License-Identifier: Unlicense + +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or distribute +# this software, either in source code form or as a compiled binary, for any +# purpose, commercial or non-commercial, and by any means. +# +# In jurisdictions that recognize copyright laws, the author or authors of +# this software dedicate any and all copyright interest in the software to +# the public domain. We make this dedication for the benefit of the public +# at large and to the detriment of our heirs and successors. We intend this +# dedication to be an overt act of relinquishment in perpetuity of all +# present and future rights to this software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to + + +# A shim which acts as a command-line interface adapter for the busted test +# framework. If busted is installed using LuaRocks we cannot invoke it +# directly, but some tools might want to do so. This thin adapter can be used +# as a drop-in replacement for the busted executable. + +eval $(luarocks path --lua-version 5.1 --bin) && busted $@ diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/attaching.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/attaching.lua new file mode 100644 index 00000000..5ffbbb7b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/attaching.lua @@ -0,0 +1,86 @@ +local yd = require 'yo-dawg' + +describe('Attaching a strategy to a buffer', function() + local nvim + + before_each(function() + nvim = yd.start() + + -- Set up a tracking strategy + nvim:exec_lua([[ + TSEnsure('lua', 'vim') + do + local track = require('rainbow-delimiters.strategy.track') + local noop = require('rainbow-delimiters.strategy.no-op') + the_strategy = track(noop) + end + vim.g.rainbow_delimiters = { + strategy = { + [''] = the_strategy + } + }]], {}) + end) + + after_each(function() + yd.stop(nvim) + end) + + it('Does not attach a second time if the buffer is already attached', function() + -- Write buffer to a file + local tempfile = nvim:call_function('tempname', {}) + nvim:call_function('writefile', {{'print((((("Hello, world!")))))', '-- vim:ft=lua'}, tempfile}) + + -- Edit the buffer multiple times, this will trigger attachment + for _ = 1, 3 do + nvim:cmd({cmd = 'edit', args = {tempfile}}, {}) + nvim:cmd({cmd = 'filetype', args = {'detect'}}, {}) + end + + local count = nvim:exec_lua('return the_strategy.attachments[1]', {}) + assert.is.equal(1, count, 'Buffer attached multiple times') + end) + + it('Performs cleanup after a buffer is deleted', function() + local is_attached + + nvim:buf_set_lines(0, 0, -1, true, {'print((((("Hello, world!")))))', '-- vim:ft=lua'}) + nvim:cmd({cmd = 'filetype', args = {'detect'}}, {}) + + is_attached = nvim:exec_lua('return the_strategy.buffers[vim.fn.bufnr()] ~= nil', {}) + assert.is_true(is_attached, 'Strategy must be attach to buffer') + + -- Delete the buffer + nvim:cmd({cmd = 'bdelete', bang = true}, {}) + is_attached = nvim:exec_lua('return the_strategy.buffers[vim.fn.bufnr()] ~= nil', {}) + assert.is_false(is_attached, 'Strategy must not be attach to buffer') + end) + + it('Detaches from the buffer and re-attached with the new language', function() + -- Switching the file type preserves the number of attachments, but + -- changes the language + for _, expected in ipairs({'lua', 'vim'}) do + nvim:buf_set_option(0, 'filetype', expected) + + local lang = nvim:exec_lua('return the_strategy.buffers[vim.fn.bufnr()].lang', {}) + local attachments = nvim:exec_lua('return the_strategy.attachments[1]', {}) + + assert.is.equal(1, attachments) + assert.is.equal(lang, expected) + end + end) + + it('Unloads a buffer without raising errors', function() + -- Create two windows with different buffers, but with same file type + nvim:buf_set_option(0, 'filetype', 'lua') + nvim:buf_set_lines(0, 0, -1, true, {'print(((("Hello world"))))', '-- vim:ft=lua'}) + nvim:cmd({cmd = 'new'}, {}) + nvim:buf_set_option(0, 'filetype', 'lua') + nvim:buf_set_lines(0, 0, -1, true, {'print(((("Goodbye world"))))', '-- vim:ft=lua'}) + + local secondbuf = nvim:call_function('bufnr', {}) + nvim:cmd({cmd = 'bdelete', args = {secondbuf}, bang = true}, {}) + local errmsg = nvim:get_vvar('errmsg') + + assert.is.equal('', errmsg) + end) +end) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/buffer-manipulation.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/buffer-manipulation.lua new file mode 100644 index 00000000..c2fb7cf9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/buffer-manipulation.lua @@ -0,0 +1,73 @@ +local yd = require 'yo-dawg' + +---Markdown document with Lua code inside a code block +local markdown_with_injected_lua = [[This is some Markdown + +```lua +print(((('Hello world')))) +``` + +This is more markdown.]] + +---Markdown document with Lua code outside a code block +local markdown_without_injected_lua = [[This is some Markdown + +```lua +``` +print(((('Hello world')))) + +This is more markdown.]] + + +describe('Buffer Manipulation', function() + local nvim + + before_each(function() + nvim = yd.start() + nvim:exec_lua('TSEnsure(...)', {'lua', 'vim', 'markdown'}) + nvim:exec_lua([[ + local rb = require 'rainbow-delimiters' + local global = rb.strategy.global + assert(nil ~= global) + vim.g.rainbow_delimiters = { + strategy = { + [''] = global + }, + } + ]], {}) + end) + + after_each(function() + yd.stop(nvim) + end) + + it('Clears extmarks when moving line out of injected langauge', function() + nvim:exec_lua('TSEnsure(...)', {'lua', 'markdown'}) + nvim:buf_set_lines(0, 0, -2, true, vim.fn.split(markdown_with_injected_lua, '\n')) + nvim:buf_set_option(0, 'filetype', 'markdown') + assert.nvim(nvim).has_extmarks_at(3, 5, 'lua') + + -- Move Lua line out of code block + nvim:cmd({cmd = 'move', range = {4}, args = {5}}, {}) + + local given = vim.fn.join(nvim:buf_get_lines(0, 0, -2, true), '\n') + assert.is.equal(markdown_without_injected_lua, given) + + assert.nvim(nvim).Not.has_extmarks_at(4, 5, 'lua') + end) + + it('Adds extmarks when moving line into injected langauge', function() + nvim:exec_lua('TSEnsure(...)', {'lua', 'markdown'}) + nvim:buf_set_lines(0, 0, -2, true, vim.fn.split(markdown_without_injected_lua, '\n')) + nvim:buf_set_option(0, 'filetype', 'markdown') + assert.nvim(nvim).Not.has_extmarks_at(4, 5, 'lua') + + -- Move Lua line out of code block + nvim:cmd({cmd = 'move', range = {5}, args = {3}}, {}) + + local given = vim.fn.join(nvim:buf_get_lines(0, 0, -2, true), '\n') + assert.is.equal(markdown_with_injected_lua, given) + + assert.nvim(nvim).has_extmarks_at(3, 5, 'lua') + end) +end) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/config.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/config.lua new file mode 100644 index 00000000..af45b7d7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/config.lua @@ -0,0 +1,171 @@ +local yd = require 'yo-dawg' + +describe('User settings are respected', function() + local nvim + + before_each(function() + nvim = yd.start() + end) + + after_each(function() + yd.stop(nvim) + end) + + describe('Strategy settings', function() + it('Applies the default strategy to all languages', function() + local strategy = 'default strategy' + nvim:exec2('let g:rainbow_delimiters = {"strategy": {"": "default strategy"}}', {}) + local lua_strategy = nvim:exec_lua('return require("rainbow-delimiters.config").strategy.lua', {}) + local c_strategy = nvim:exec_lua('return require("rainbow-delimiters.config").strategy.c', {}) + assert.is.equal(strategy, lua_strategy) + assert.is.equal(strategy, c_strategy) + end) + + it('Overrides the strategy for individual languages', function() + -- I had to use a trick here because we cannot compare dictionaries or + -- functions for identity between Vim script and Lua. Instead I + -- set a string as the strategy and compare for that equality. + nvim:exec_lua('require("rainbow-delimiters.default").strategy[""] = "default strategy"', {}) + + -- Override the strategy for Vim only + nvim:set_var('rainbow_delimiters', {strategy = {vim = 'vim strategy'}}) + + local lua_strategy = nvim:exec_lua('return require("rainbow-delimiters.config").strategy.lua', {}) + local vim_strategy = nvim:exec_lua('return require("rainbow-delimiters.config").strategy.vim', {}) + + assert.is.equal('vim strategy', vim_strategy, 'Wrong strategy found for Vim') + assert.is.equal('default strategy', lua_strategy, 'Wrong strategy found for Lua') + end) + + describe('Strategies can be thunks', function() + before_each(function() + -- Store strategies in global variables for later reference + nvim:exec_lua('noop = require("rainbow-delimiters").strategy.noop', {}) + nvim:exec_lua('the_strategy = require("rainbow-delimiters.strategy.track")(noop)', {}) + -- Set a thunk as the strategy + nvim:exec_lua([[ + vim.g.rainbow_delimiters = { + strategy = { + [""] = function() return the_strategy end, + vim = function() return nil end + } + }]], {}) + end) + + it('Uses the strategy returned by the thunk', function() + nvim:exec_lua('TSEnsure(...)', {'lua'}) + nvim:buf_set_lines(0, 0, -1, true, {'print "Hello world"', '-- vim:ft=lua'}) + nvim:command('filetype detect') + local attachments = nvim:exec_lua('return the_strategy.attachments[1]', {}) + assert.is.equal(1, attachments, 'The strategy should be attached to the Lua buffer') + end) + + it('Does nothing if the thunk returns nil', function() + nvim:exec_lua('TSEnsure(...)', {'vim'}) + nvim:buf_set_lines(0, 0, -1, true, {'echo "Hello world"', '" vim:ft=vim'}) + nvim:command('filetype detect') + local attachments = nvim:exec_lua('return the_strategy.attachments[1]', {}) + assert.is.equal(0, attachments, 'The strategy should not be attached to the Vim buffer') + end) + end) + end) + + it('Overrides the query for an individual language', function() + -- Override the query for one language only + nvim:set_var('rainbow_delimiters', {query = {c = 'other-query'}}) + + local c_query = nvim:exec_lua('return require("rainbow-delimiters.config").query.c', {}) + local lua_query = nvim:exec_lua('return require("rainbow-delimiters.config").query.lua', {}) + + assert.is.equal('other-query', c_query) + assert.is.equal('rainbow-delimiters', lua_query) + end) + + it('Falls back to default highlighting if the highlight table is empty', function() + ---The expected highlight groups in order + local hlgroups = { + 'RainbowDelimiterRed', + 'RainbowDelimiterYellow', + 'RainbowDelimiterBlue', + 'RainbowDelimiterOrange', + 'RainbowDelimiterGreen', + 'RainbowDelimiterViolet', + 'RainbowDelimiterCyan', + } + + -- Set highlight to empty list + nvim:set_var('rainbow_delimiters', {highlight = {}}) + + for i, expected in ipairs(hlgroups) do + local given = nvim:exec_lua('return require("rainbow-delimiters.config").highlight[...]', {i}) + assert.is.equal(expected, given, string.format('Wrong highlight group at index %d', i)) + end + end) + + describe('White- and blacklist individual languages', function() + it('Has all languages enabled without configuration', function() + nvim:exec_lua('rbc = require("rainbow-delimiters.config")', {}) + local lua_enabled = nvim:exec_lua('return rbc.enabled_for("lua")', {}) + local vim_enabled = nvim:exec_lua('return rbc.enabled_for("vim")', {}) + + assert.is_true(lua_enabled, 'Lua should be enabled') + assert.is_true(vim_enabled, 'Vim script should be enabled') + end) + + it('Has all languages enabled in blank configuration', function() + nvim:set_var('rainbow_delimiters', {}) + nvim:exec_lua('rbc = require("rainbow-delimiters.config")', {}) + local lua_enabled = nvim:exec_lua('return rbc.enabled_for("lua")', {}) + local vim_enabled = nvim:exec_lua('return rbc.enabled_for("vim")', {}) + + assert.is_true(lua_enabled, 'Lua should be enabled') + assert.is_true(vim_enabled, 'Vim script should be enabled') + end) + + it('Can whitelist individual file types by adding them to our configuration', function() + nvim:set_var('rainbow_delimiters', {whitelist = {'lua'}}) + nvim:exec_lua('rbc = require("rainbow-delimiters.config")', {}) + local lua_enabled = nvim:exec_lua('return rbc.enabled_for("lua")', {}) + local vim_enabled = nvim:exec_lua('return rbc.enabled_for("vim")', {}) + + assert.is_true( lua_enabled, 'Lua should be enabled') + assert.is_false(vim_enabled, 'Vim script should be disabled') + end) + + it('Can blacklist individual file types by adding them to our configuration', function() + nvim:set_var('rainbow_delimiters', {blacklist = {'vim'}}) + nvim:exec_lua('rbc = require("rainbow-delimiters.config")', {}) + local lua_enabled = nvim:exec_lua('return rbc.enabled_for("lua")', {}) + local vim_enabled = nvim:exec_lua('return rbc.enabled_for("vim")', {}) + + assert.is_true( lua_enabled, 'Lua should be enabled') + assert.is_false(vim_enabled, 'Vim script should be disabled') + end) + end) + + describe('The setup function sets configuration indirectly', function() + it('Can call the setup function', function() + nvim:exec_lua([[ + require('rainbow-delimiters.setup').setup { + query = { + lua = 'rainbow-blocks' + } + } + ]], {}) + local lua_query = nvim:eval('g:rainbow_delimiters.query.lua') + assert.is.equal('rainbow-blocks', lua_query) + end) + + it('Can call the table itset', function() + nvim:exec_lua([[ + require('rainbow-delimiters.setup') { + query = { + lua = 'rainbow-blocks' + } + } + ]], {}) + local lua_query = nvim:eval('g:rainbow_delimiters.query.lua') + assert.is.equal('rainbow-blocks', lua_query) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/public-api.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/public-api.lua new file mode 100644 index 00000000..ddde1296 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/public-api.lua @@ -0,0 +1,182 @@ +local yd = require 'yo-dawg' + +describe('The Rainbow Delimiters public API', function() + local nvim + + before_each(function() + nvim = yd.start() + + -- Set up a tracking strategy + nvim:exec_lua([[ + TSEnsure('markdown', 'lua', 'vim') + rb = require 'rainbow-delimiters' + vim.g.rainbow_delimiters = { + strategy = { + [''] = rb.strategy.global, + }, + }]], {}) + end) + + after_each(function() + yd.stop(nvim) + end) + + describe('Whether RB is enabled for a buffer at startup', function() + it('Is disabled for a buffer without file type', function() + assert.is.False(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Is enabled for a supported language', function() + nvim:buf_set_option(0, 'filetype', 'lua') + assert.is.True(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + describe('Blacklist', function() + before_each(function() + nvim:command('let g:rainbow_delimiters.blacklist = ["markdown"]') + end) + + it('Is enabled for a not blacklisted language', function() + nvim:buf_set_option(0, 'filetype', 'lua') + assert.is.True(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Is disabled for a blacklisted language', function() + nvim:buf_set_option(0, 'filetype', 'markdown') + assert.is.False(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Is disabled for a blacklisted language with injected whitelisted language', function() + nvim:buf_set_lines(0, 0, -1, true, { + 'This is Markdown', + '', + '```lua', + 'print(((("This is Lua"))))', + '```', + '', + 'More Markdown', + }) + nvim:buf_set_option(0, 'filetype', 'markdown') + assert.is.False(nvim:exec_lua('return rb.is_enabled()', {})) + end) + end) + + describe('Whitelist', function() + before_each(function() + nvim:command('let g:rainbow_delimiters.whitelist = ["lua"]') + end) + + it('Is disabled for a not whitelisted language', function() + nvim:buf_set_option(0, 'filetype', 'markdown') + assert.is.False(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Is enabled for a whitelisted language', function() + nvim:buf_set_option(0, 'filetype', 'lua') + assert.is.True(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Is enabled for whitelisted language with other language injected', function() + nvim:buf_set_lines(0, 0, -1, true, { + 'print "This is Lua"', + 'vim.cmd [[echo "This is Vim"]]', + }) + nvim:buf_set_option(0, 'filetype', 'lua') + assert.is.True(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Is disabled for not whitelisted language with injected whitelisted language', function() + nvim:buf_set_lines(0, 0, -1, true, { + 'This is Markdown', + '', + '```lua', + 'print(((("This is Lua"))))', + '```', + '', + 'More Markdown', + }) + nvim:buf_set_option(0, 'filetype', 'markdown') + assert.is.False(nvim:exec_lua('return rb.is_enabled()', {})) + end) + end) + end) + + describe('Manual toggling', function() + it('Can be disabled for a buffer', function() + nvim:buf_set_option(0, 'filetype', 'lua') + nvim:exec_lua('rb.disable(0)', {}) + assert.is.False(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Can be turned back on', function() + nvim:buf_set_option(0, 'filetype', 'lua') + nvim:exec_lua('rb.disable(0)', {}) + nvim:exec_lua('rb.enable(0)', {}) + assert.is.True(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Can be toggled off', function() + nvim:buf_set_option(0, 'filetype', 'lua') + nvim:exec_lua('rb.toggle(0)', {}) + assert.is.False(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Can be toggled on', function() + nvim:buf_set_option(0, 'filetype', 'lua') + nvim:exec_lua('rb.toggle(0)', {}) + nvim:exec_lua('rb.toggle(0)', {}) + assert.is.True(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Gets disabled idempotently', function() + nvim:buf_set_option(0, 'filetype', 'lua') + nvim:exec_lua('rb.disable(0)', {}) + nvim:exec_lua('rb.disable(0)', {}) + assert.is.False(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Gets enabled idempotently', function() + nvim:buf_set_option(0, 'filetype', 'lua') + nvim:exec_lua('rb.disable(0)', {}) + nvim:exec_lua('rb.enable(0)', {}) + nvim:exec_lua('rb.enable(0)', {}) + assert.is.True(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + describe('Blacklist', function() + before_each(function() + nvim:command('let g:rainbow_delimiters.blacklist = ["markdown"]') + end) + + it('Can be enabled for a blacklisted language', function() + nvim:buf_set_option(0, 'filetype', 'markdown') + nvim:exec_lua('rb.enable(0)', {}) + assert.is.True(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Can be toggled for a blacklisted language', function() + nvim:buf_set_option(0, 'filetype', 'markdown') + nvim:exec_lua('rb.toggle(0)', {}) + assert.is.True(nvim:exec_lua('return rb.is_enabled()', {})) + end) + end) + + describe('Whitelist', function() + before_each(function() + nvim:command('let g:rainbow_delimiters.whitelist = ["lua"]') + end) + + it('Can be disabled for a whitelisted language', function() + nvim:buf_set_option(0, 'filetype', 'lua') + nvim:exec_lua('rb.disable(0)', {}) + assert.is.False(nvim:exec_lua('return rb.is_enabled()', {})) + end) + + it('Can be toggled for a whitelisted language', function() + nvim:buf_set_option(0, 'filetype', 'lua') + nvim:exec_lua('rb.toggle(0)', {}) + assert.is.False(nvim:exec_lua('return rb.is_enabled()', {})) + end) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/selective-activation.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/selective-activation.lua new file mode 100644 index 00000000..040b9b12 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/selective-activation.lua @@ -0,0 +1,31 @@ +local yd = require 'yo-dawg' + +describe('We can disable rainbow delimiters for certain languages', function() + local nvim + + before_each(function() + nvim = yd.start() + end) + + after_each(function() + yd.stop(nvim) + end) + + it('Does not run for a blacklisted language', function() + nvim:exec_lua('the_strategy = require("rainbow-delimiters.strategy.track")(require("rainbow-delimiters.strategy.no-op"))', {}) + nvim:exec_lua('vim.g.rainbow_delimiters = {blacklist = {"lua"}, strategy = {[""] = the_strategy}}', {}) + nvim:buf_set_lines(0, 0, -1, true, {'print "Hello world"', '-- vim:ft=lua'}) + nvim:command('filetype detect') + local attachments = nvim:exec_lua('return the_strategy.attachments[1]', {}) + assert.is.equal(0, attachments) + end) + + it('Runs for a whitelisted language', function() + nvim:exec_lua('the_strategy = require("rainbow-delimiters.strategy.track")(require("rainbow-delimiters.strategy.no-op"))', {}) + nvim:exec_lua('vim.g.rainbow_delimiters = {whitelist = {"lua"}, strategy = {[""] = the_strategy}}', {}) + nvim:buf_set_lines(0, 0, -1, true, {'print "Hello world"', '-- vim:ft=lua'}) + nvim:command('filetype detect') + local attachments = nvim:exec_lua('return the_strategy.attachments[1]', {}) + assert.is.equal(1, attachments) + end) +end) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/strategy/global.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/strategy/global.lua new file mode 100644 index 00000000..a1b67ee1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/strategy/global.lua @@ -0,0 +1,99 @@ +local yd = require 'yo-dawg' + +describe('The global strategy', function() + local nvim + + before_each(function() + nvim = yd.start() + nvim:exec_lua('TSEnsure(...)', {'lua', 'vim'}) + nvim:exec_lua([[ + local rb = require 'rainbow-delimiters' + local track = require('rainbow-delimiters.strategy.track') + local global = rb.strategy.global + assert(nil ~= global) + the_strategy = track(global) + vim.g.rainbow_delimiters = { + strategy = { + [''] = the_strategy + }, query = { + }, + } + ]], {}) + end) + + after_each(function() + yd.stop(nvim) + end) + + it('Does not reactivate when making changes', function() + nvim:buf_set_lines(0, 0, -1, true, {'print({{{{{}}}}})', '-- vim:ft=lua'}) + nvim:buf_set_option(0, 'filetype', 'lua') + assert.nvim(nvim).has_extmarks_at(0, 5, 'lua') + + nvim:call_function('rainbow_delimiters#disable', {0}) + assert.nvim(nvim).Not.has_extmarks_at(0, 5, 'lua') + + -- Add a new pair of curly braces + -- (jump to first column, find the first closing brace, insert new pair) + local keys = vim.api.nvim_replace_termcodes('gg0f}i{}', true, false, true) + nvim:feedkeys(keys, 'n', false) + assert.is.same({'print({{{{{{}}}}}})'}, nvim:buf_get_lines(0, 0, 1, true)) + + assert.nvim(nvim).Not.has_extmarks_at(0, 5, 'lua') + assert.is.equal(0, nvim:exec_lua('return the_strategy.attachments[1]', {})) + end) + + it('Ignores blacklisted injected languages', function() + nvim:exec_lua('vim.g.rainbow_delimiters.blacklist = {...}', {'vim'}) + nvim:buf_set_lines(0, 0, -1, true, { + 'print {{{{{}}}}}', + 'vim.cmd [[', + ' echo string(1 + (2 + (3 + 4)))', + ']]', + '-- vim:ft=lua' + }) + nvim:buf_set_option(0, 'filetype', 'lua') + + -- The Lua code is highlighted, the Vim code not + assert.nvim(nvim).has_extmarks_at(0, 6, 'lua') + assert.nvim(nvim).Not.has_extmarks_at(2, 13, 'vim') + end) + + it('Ignores non-whitelisted injected languages', function() + nvim:exec_lua('vim.g.rainbow_delimiters.whitelist = {...}', {'lua'}) + nvim:buf_set_lines(0, 0, -1, true, { + 'print {{{{{}}}}}', + 'vim.cmd [[', + ' echo string(1 + (2 + (3 + 4)))', + ']]', + '-- vim:ft=lua' + }) + nvim:buf_set_option(0, 'filetype', 'lua') + + -- The Lua code is highlighted, the Vim code not + assert.nvim(nvim).has_extmarks_at(0, 6, 'lua') + assert.nvim(nvim).Not.has_extmarks_at(2, 13, 'vim') + end) + + it('Applies highlighting to nested code', function() + -- See also https://github.com/HiPhish/rainbow-delimiters.nvim/pull/92 + local content = [[local function foo() + return { + a = print('a'), + } +end + +return foo]] + + nvim:exec_lua('vim.g.rainbow_delimiters.query.lua = "rainbow-blocks"', {}) + nvim:buf_set_lines(0, 0, -1, true, vim.fn.split(content, '\n')) + nvim:buf_set_option(0, 'filetype', 'lua') + -- Insert the line " b = print('b')," + nvim:win_set_cursor(0, {3, 0}) + local keys = vim.api.nvim_replace_termcodes("ob = print('b'),", true, false, true) + nvim:feedkeys(keys, '', false) + + assert.nvim(nvim).has_extmarks_at(2, 11, 'lua') + assert.nvim(nvim).has_extmarks_at(3, 11, 'lua') + end) +end) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/strategy/local.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/strategy/local.lua new file mode 100644 index 00000000..5b5cf042 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/strategy/local.lua @@ -0,0 +1,46 @@ +local yd = require 'yo-dawg' + +describe('The local strategy', function() + local nvim + + before_each(function() + nvim = yd.start() + nvim:exec_lua('TSEnsure(...)', {'lua', 'vim'}) + nvim:exec_lua([[ + local rb = require 'rainbow-delimiters' + local track = require('rainbow-delimiters.strategy.track') + local strategy = rb.strategy['local'] + assert(nil ~= strategy) + the_strategy = track(strategy) + vim.g.rainbow_delimiters = { + strategy = { + [''] = the_strategy + } + } + ]], {}) + end) + + after_each(function() + yd.stop(nvim) + end) + + it('Does not reactivate when making changes', function() + nvim:buf_set_lines(0, 0, -1, true, {'print({{{{{}}}}})', '-- vim:ft=lua'}) + nvim:win_set_cursor(0, {1, 5}) + nvim:buf_set_option(0, 'filetype', 'lua') + assert.nvim(nvim).has_extmarks_at(0, 5, 'lua') + + nvim:call_function('rainbow_delimiters#disable', {0}) + assert.nvim(nvim).Not.has_extmarks_at(0, 5, 'lua') + + -- Add a new pair of curly braces + -- (jump to first column, find the first closing brace, insert new pair) + local keys = vim.api.nvim_replace_termcodes('gg0f}i{}', true, false, true) + nvim:feedkeys(keys, 'n', false) + assert.is.same({'print({{{{{{}}}}}})'}, nvim:buf_get_lines(0, 0, 1, true)) + + assert.nvim(nvim).Not.has_extmarks_at(0, 5, 'lua') + assert.is.equal(0, nvim:exec_lua('return the_strategy.attachments[1]', {})) + end) +end) + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/toggle.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/toggle.lua new file mode 100644 index 00000000..185b7fe9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/e2e/toggle.lua @@ -0,0 +1,64 @@ +local yd = require 'yo-dawg' + +describe('We can use functions to turn rainbow delimiters off and on again.', function() + local nvim + + before_each(function() + nvim = yd.start() + nvim:exec_lua('the_strategy = require("rainbow-delimiters.strategy.global")', {}) + nvim:exec_lua('TSEnsure(...)', {'lua'}) + nvim:buf_set_lines(0, 0, -1, true, {'print((((("Hello, world!")))))'}) + nvim:buf_set_option(0, 'filetype', 'lua') + end) + + after_each(function() + yd.stop(nvim) + end) + + it('Does highlighting initially', function() + assert.nvim(nvim).has_extmarks_at(0, 5, 'lua') + end) + + it('Disables rainbow delimiters', function() + nvim:call_function('rainbow_delimiters#disable', {0}) + assert.nvim(nvim).Not.has_extmarks_at(0, 5, 'lua') + end) + + it('Remains disabled when disabling twice', function() + nvim:call_function('rainbow_delimiters#disable', {0}) + nvim:call_function('rainbow_delimiters#disable', {0}) + + assert.nvim(nvim).Not.has_extmarks_at(0, 5, 'lua') + end) + + it('Turns rainbow delimiters back on', function() + nvim:call_function('rainbow_delimiters#disable', {0}) + nvim:call_function('rainbow_delimiters#enable', {0}) + + assert.nvim(nvim).has_extmarks_at(0, 5, 'lua') + end) + + it('Remains enabled when enabling twice', function() + nvim:call_function('rainbow_delimiters#disable', {0}) + nvim:call_function('rainbow_delimiters#enable', {0}) + nvim:call_function('rainbow_delimiters#enable', {0}) + + assert.nvim(nvim).has_extmarks_at(0, 5, 'lua') + end) + + it('Can be disabled after being enabled', function() + nvim:call_function('rainbow_delimiters#disable', {0}) + nvim:call_function('rainbow_delimiters#enable', {0}) + nvim:call_function('rainbow_delimiters#disable', {0}) + + assert.nvim(nvim).Not.has_extmarks_at(0, 5, 'lua') + end) + + it('Can be enabled after being disabled twice', function() + nvim:call_function('rainbow_delimiters#disable', {0}) + nvim:call_function('rainbow_delimiters#disable', {0}) + nvim:call_function('rainbow_delimiters#enable', {0}) + + assert.nvim(nvim).has_extmarks_at(0, 5, 'lua') + end) +end) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/astro/regular.astro b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/astro/regular.astro new file mode 100644 index 00000000..c1bb3615 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/astro/regular.astro @@ -0,0 +1,33 @@ +--- +// component import +import MainLayout from "../../layouts/MainLayout.astro"; +import PostCard from "../../components/PostCard.astro"; + +// utils imports +import { formatBlogPosts } from "../../js/utils"; + +const allPosts = await Astro.glob("./*.md"); +const formattedPosts = formatBlogPosts(allPosts, {}); +--- + + +
+

New Blog Posts

+
+ { + formattedPosts.map((post) => ( + + )) + } +
+
+ +
diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/bash/regular.sh b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/bash/regular.sh new file mode 100755 index 00000000..0810c978 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/bash/regular.sh @@ -0,0 +1,30 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/bash + +# Command substitution +echo $(basedir $(pwd)) + +# Variable expansion +echo ${FOO:-${BAR:-${BAZ}}} + +# Test expression (using the `test` command) +if [ -d "herp/derp/" ]; then + echo "Yay" +fi + +# Test expression (bashism) +if [[ -d "herp/derp/" ]]; then + echo "Yay" +fi + +# Sub-shells +(true; false; (true; true; (false; true))) + +person() { + array=( + [Alice]="$((2 ^ 10))" + [Bob]=2048 + ) + echo "${array[$1]}" +} + +person "Alice" diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c/regular.c b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c/regular.c new file mode 100644 index 00000000..243efe33 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c/regular.c @@ -0,0 +1,82 @@ +#include + + +#define PI 3.14 +/* These aren't highlight correctly. A problem with the parser? */ +#define TESTMACRO (-1) +#define min(X,Y) ((X) < (Y) ? (X) : (Y)) + + +/* Declaration with parentheses, a function pointer */ +static void (*callback)(int); +int c_init() { return 1; } + +/* Macro type specifier */ +#define Map int Foo +static Map(char *c_str) {return 4;} + +typedef enum { + E1, + E2, + E3 + // comment +} Myenum; + +/* A function declaration */ +int add(int, int); + +struct Point2D { + int x; + int y; +}; + +/* Compound literal expression */ +struct Point2D v = (struct Point2D){ 0, 0 }; + +/* A function definition */ +int add(int x, int y) { + if (!y) { + if (1) { + if (1) { + if (1) { + return x; + } + } + } + } + + while (0) { + while (0) { + while (0) { + ; + } + } + } + + for (int i = 0; i < 0; i++) { + for (int j = 0; j < 0; j++) { + for (int k = 0; k < 0; k++) { + ; + } + } + } + + return add(x + 1, y - 1); +} + +float int2float(int i) { + return (float)i; +} + +int main(int argc, char *argv[]) { + int a = 10, b = 5; + int result = add(a, b); + printf("The sum of %d and %d is %d", ((((a)))), b, result); + int indices[] = {0, }; + int i = indices[indices[indices[indices[indices[indices[0]]]]]]; + #if 0 + /* A language server may mark this block semantically as a comment */ + printf("The sum of %d and %d is %d", ((((a)))), b, result); + #endif + return 0; +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/HelloWorld.cs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/HelloWorld.cs new file mode 100644 index 00000000..c3c26d8c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/HelloWorld.cs @@ -0,0 +1,8 @@ +using System; + +// A version of the classic "Hello World" program +class Program { + static void Main() { + Console.WriteLine("Hello, world!"); + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/array.cs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/array.cs new file mode 100644 index 00000000..4245bc66 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/array.cs @@ -0,0 +1,17 @@ +using System; + +// Arrays and nested arrays +class Program { + static void Main() { + int[,,] array3D = new int[,,] { + { {1}, {2} }, + { {3}, {4} }, + { {5}, {6} }, + { {7}, {8} }, + }; + int[] indices = new int[] {0}; + int i = array3D[0, 0, 0]; + var implicitArray = new[] { "" }; + int j = indices[indices[indices[indices[0]]]]; + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/attributes.cs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/attributes.cs new file mode 100644 index 00000000..bfd71552 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/attributes.cs @@ -0,0 +1,8 @@ +internal class TestAttribute : Attribute { } + +[Test()] +public class Person +{ + [Test()] + public string? Name { get; set; } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/generics.cs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/generics.cs new file mode 100644 index 00000000..58dd08ba --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/generics.cs @@ -0,0 +1,14 @@ +using System; + +public class A { } + +public struct B { } + +public interface C : A> { } + +// Nested generic parameters +class Program { + static void Main(List>> l) { + } +} + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/loop.cs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/loop.cs new file mode 100644 index 00000000..37369cb1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/loop.cs @@ -0,0 +1,21 @@ +using System; + + +// Nested loops +class Program { + static int[] integers = {0, 1, 2, 3}; + + static void Main() { + foreach (int i in integers) { + foreach (int i in integers) { + foreach (int i in integers) { + foreach (int i in integers) { + while (false) { + Console.WriteLine("Hello, world!"); + } + } + } + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/misc.cs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/misc.cs new file mode 100644 index 00000000..bbd4f278 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/misc.cs @@ -0,0 +1,39 @@ +public class TestClass +{ + public string? Name { get; set; } + public int[][]? MultiDimArray { get; set; } + + private string MergeLines(IEnumerable> sections) + { + return string.Join(",", sections.SelectMany(t => t)); + } + + private void LoopTest() + { + foreach (var item in new string[0]) { } + + for (int i = 0; i < 0; i++) { } + + while (false) { } + + do { } while (false); + } + + private void Interpolation() + { + var passTitle = "123"; + if (true) { + System.Console.WriteLine($"== {passTitle} =="); + } + } + + private void AnonymousObject() + { + var a = new { Test = 123, }; + } + + private (int a, float b, (int c, float d)) TupleExpressions() + { + return (1, 2, (3, 4)); + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/parens.cs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/parens.cs new file mode 100644 index 00000000..58d5e251 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/parens.cs @@ -0,0 +1,8 @@ +using System; + +// Nested parenthesized expressions +class Program { + static void Main() { + var i = (((((1 + 2))) + ((((3)))))); + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/switch.cs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/switch.cs new file mode 100644 index 00000000..48cca4d1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/c_sharp/switch.cs @@ -0,0 +1,55 @@ +public static class SwitchTest +{ + private static string GenericFirstCharProcessing( + this string input, + Func firstCharProcessor + ) => + input switch + { + null => throw new ArgumentNullException(nameof(input)), + "" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)), + _ => firstCharProcessor(input[0].ToString()) + input.Substring(1) + }; + + private static void T() + { + var defaultInt = default(int); + + try { } + catch (Exception e) when (true) { } + finally { } + + using (var stream = new Stream()) { } + + lock (new string()) { } + + var name = "test"; + switch (name) + { + case "aab": + { + break; + } + case var o when (o?.Trim().Length ?? 0) == 0: + case "test": + break; + default: + break; + } + + int c = (int)b; // explicit conversion from long to int + + Type[] t = { typeof(int), }; + sizeof(int); + int AllBits = unchecked((int)0xFFFFFFFF); + int AllBits = checked((int)0xFFFFFFFF); + Span span5 = stackalloc[] { 11, 12, 13 }; + } + + enum Color + { + Red, + Blue, + Green + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/clojure/regular.clj b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/clojure/regular.clj new file mode 100644 index 00000000..ff583d4a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/clojure/regular.clj @@ -0,0 +1,9 @@ +(defn fn-name "docs" [a0 a1 & xz] + [ + "some _text_ with parens #() #{} {} [] (#())" + '(#(identity "")) + [[[], [[]]], #(:k {}), #{{}, ""}, '((())), `({})] + ] + ) + +(fn-name 1 2 3 4) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/commonlisp/regular.lisp b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/commonlisp/regular.lisp new file mode 100644 index 00000000..0cce82d4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/commonlisp/regular.lisp @@ -0,0 +1,16 @@ +(defun add (x y) + "A silly way to add two numbers recursively." + (if (zerop y) + x + (add (incf x) + (decf y)))) + +(defmacro foo (a &rest rest) + `(format t "~A~%" (list ,a ,@rest))) + +;;; The LOOP macro has its own node type +(loop repeat 3 + do (print "Hello world")) + +'(((a . b))) +'((((a b . c)))) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/cpp/regular.cpp b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/cpp/regular.cpp new file mode 100644 index 00000000..8310d7c0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/cpp/regular.cpp @@ -0,0 +1,79 @@ +#include +#include + +namespace herp { + const int derpiness = 9000; + int get_derpiness() { + return derpiness; + } +} + +/* A function declaration */ +int add(int, int); + +// Structure and class definitions +struct Point2D { +public: + int x; + int y; +}; + +class Point3D { +public: + int x; + int y; + int z; +}; + +/* A function definition */ +int add(int x, int y) { + if (!y) { + if (1) { + if (1) { + if (1) { + return x; + } + } + } + } + + while (0) { + while (0) { + while (0) { + ; + } + } + } + + for (int i = 0; i < 0; i++) { + for (int j = 0; j < 0; j++) { + for (int k = 0; k < 0; k++) { + ; + } + } + } + + return add(x + 1, y - 1); +} + +template T myMax(T x, T y) { + return (x > y) ? x : y; +} + +float int2float(int i) { + return (float)i; +} + +void do_nothing_with_vector(std::vector>> v) { + return; +} + +int main(int argc, char *argv[]) { + auto a {10}; + auto b (5); + auto result = add(a, b); + printf("The sum of %d and %d is %d", ((((a)))), b, result); + int indices[] = {0, }; + auto i = indices[indices[indices[indices[indices[indices[0]]]]]]; + return 0; +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/css/regular.css b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/css/regular.css new file mode 100644 index 00000000..4b71f46a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/css/regular.css @@ -0,0 +1,22 @@ +:root { + @media (prefers-color-scheme: dark) { + --color-bg: #3b4252; + --color-fg: #eceff4; + --color-gray: #434c5e; + --color-blue: #81a1c1; + } +} + +li:has(input[type="checkbox"]) { + list-style-type: none; +} + +.foo { + color: #ffffff; +} + +@media (not (color)) { + .foo { + color: #ffffff; + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/cuda/regular.cu b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/cuda/regular.cu new file mode 100644 index 00000000..6e320859 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/cuda/regular.cu @@ -0,0 +1,87 @@ +#include +#include +#include + +/* A function declaration */ +int add(int, int); + +// Structure and class definitions +struct Point2D { +public: + int x; + int y; +}; + +class Point3D { +public: + int x; + int y; + int z; +}; + +/* A function definition */ +int add(int x, int y) { + if (!y) { + if (1) { + if (1) { + if (1) { + return x; + } + } + } + } + + while (0) { + while (0) { + while (0) { + ; + } + } + } + + for (int i = 0; i < 0; i++) { + for (int j = 0; j < 0; j++) { + for (int k = 0; k < 0; k++) { + ; + } + } + } + + return add(x + 1, y - 1); +} + +template T myMax(T x, T y) { + return (x > y) ? x : y; +} + +float int2float(int i) { + return (float)i; +} + +void do_nothing_with_vector(std::vector>> v) { + return; +} + +__global__ void add_array(int *a, int size) { + int i = threadIdx.x + blockIdx.x * blockDim.x; + if (i < size) { + a[i] += 1; + } +} + +void call_device() { + int *dev_a; + cudaMalloc(&dev_a, 10 * sizeof(int)); + add_array<<<1, 10, 1>>>(dev_a, 10); + cudaFree(dev_a); +} + +int main(int argc, char *argv[]) { + auto a {10}; + auto b (5); + auto result = add(a, b); + printf("The sum of %d and %d is %d", ((((a)))), b, result); + int indices[] = {0, }; + auto i = indices[indices[indices[indices[indices[indices[0]]]]]]; + return 0; +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/cue/regular.cue b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/cue/regular.cue new file mode 100644 index 00000000..2c153101 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/cue/regular.cue @@ -0,0 +1,25 @@ +package main + +import ( + "strings" +) + +HumanA: { + name: "Bob" + description: "A human named \(strings.ToUpper(name))" +} + +_#ComplexType: (int | string) | bool + +ok: _#ComplexType & 13 + +numList: [...int] & [ 1, 2, 3, 4 ] + +elems: [Name=_]: {name: Name} +elems: { + one: {} + two: {} +} + +_env: string | *"dev" @tag(env,type=string) +host: "\(_env).example.com" diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/dart/regular.dart b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/dart/regular.dart new file mode 100644 index 00000000..5fb04df0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/dart/regular.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; + +class ExampleWidget extends StatelessWidget { + final String title; + final String subtitle; + final String image; + final String id; + + const WidgetItem({ + super.key, + required this.id, + required this.title, + required this.subtitle, + required this.image, + }); + + @override + Widget build(BuildContext context) { + final data = {['field'] = ""}; + final theme = Theme.of(context); + return GestureDetector( + onTap: () => context.go('/example/$id'), + child: Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + const Expanded( + child: Image( + image: AssetImage('assets/image.jpg'), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Text( + title, + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + decoration: TextDecoration.underline, + ), + ), + const SizedBox(height: 8), + Text(subtitle), + ], + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/elixir/regular.exs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/elixir/regular.exs new file mode 100644 index 00000000..126bb8cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/elixir/regular.exs @@ -0,0 +1,35 @@ +defmodule Regular do + @moduledoc """ + A dummy test module. + """ + + def first_plus_five([head | tail]) do + IO.puts "The first value is #{head}" + head + (((1 + (2 + 3)))) + end + + def first_plus_five({a, b}) do + IO.puts "The first value is #{a}" + a + (((1 + (2 + 3)))) + end + + def first_plus_five(<>) do + IO.puts "The first value is #{r}" + r + (((1 + (2 + 3)))) + end + + def first_plus_five(%{head => _}) do + IO.puts "The first value is #{head}" + head + (((1 + (2 + 3)))) + end + + defp accessLookup(map, x) do + map[map[map[map[x]]]] + end +end + +# Keyword list syntactic sugar +IO.puts inspect([a: 1, b: [c: 3, d: [e: 5, f: []]]]) + +# Map syntactic sugar +IO.puts inspect(%{a => 1, b => %{c => 3, d => %{e => 5, f => %{}}}}) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/elm/Regular.elm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/elm/Regular.elm new file mode 100644 index 00000000..6102c614 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/elm/Regular.elm @@ -0,0 +1,51 @@ +module Regular exposing (CustomType(..)) + +import Browser exposing (UrlRequest(..)) +import Url.Parser exposing ((), ()) + + +type CustomType a + = CustomType a + + +type alias NestedRecordOfCustomType a = + { a : ( Int, List (Maybe ( Int, CustomType a )) ) + , b : ( Int, { c : CustomType a } ) + , d : { f : { g : String } } + } + + +nestedTypeExpr : Int -> (Int -> Int) -> (Int -> (Int -> Int)) +nestedTypeExpr x y = + \z -> y + + +nestedListPatternFunction : List (List ( Int, List ( Int, String ) )) -> List ( String, Int ) +nestedListPatternFunction list = + List.concatMap (\( _, strings ) -> List.map (\( a, b ) -> ( b, a )) strings) (List.concat list) + + +unwrapCustomType : { b | c : Int } -> CustomType (CustomType { a : Int }) -> Int +unwrapCustomType { c } (CustomType (CustomType ({ a } as b))) = + (a + (c * 1)) * (a - (a + (b.a * 1))) + + +patternMatchNestedListOfRecords : List (List (NestedRecordOfCustomType Int)) -> Maybe (List (List (NestedRecordOfCustomType Int))) +patternMatchNestedListOfRecords list = + case [ list ] of + [ [ [ { a, b } ] ] ] -> + case ( a, b ) of + ( ( 1, [ Just ( 1, ct ) ] ), ( 2, { c } ) ) -> + Just + [ [ { a = ( 1, [ Just ( 1, c ) ] ) + , b = ( 2, { c = ct } ) + , d = { f = { g = "test" } } + } + ] + ] + + _ -> + Nothing + + _ -> + Nothing diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/fennel/regular.fnl b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/fennel/regular.fnl new file mode 100644 index 00000000..57e1b07f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/fennel/regular.fnl @@ -0,0 +1,96 @@ +(print (.. "foo" "bar")) + +(local abcd { :a { :b { :c { :d {}}}}}) + +(let [one 1 two 2 tbl { : one : two}] + tbl) + +;;; Destructuring a table binding +(let [{:a {:b {:c {:d d}}}} abcd] + (print d)) + +[0 [1 [2 [3 []]]]] + +;; NOTE: the single ":" on the second line could also be a delimiter +{:a :b + : abcd} + +;;; Get AST root +(fn get-root [bufnr] + ;;; Get current buffer + (local bufnr (or bufnr + (vim.api.nvim_get_current_buf))) + + ;;; Early return if not in a Nix file + (when (not= (. vim :bo bufnr :filetype) + :nix) + (vim.notify_once "This is meant to be used with Nix files") + (lua "return nil")) + + (let [parser (vim.treesitter.get_parser bufnr :nix {}) + [tree] (parser:parse)] + (tree:root))) + +(macro -m?> [val ...] + "Thread (maybe) a value through a list of method calls" + (assert-compile + val + "There should be an input value to the pipeline") + (var res# (gensym)) + (var res `(do (var ,res# ,val))) + (each [_ [f & args] (ipairs [...])] + (table.insert + res + `(when (and (not= nil ,res#) + (not= nil (. ,res# ,f))) + (set ,res# (: ,res# ,f ,(unpack args)))))) + res) + +(fn add-partial [x] + (fn [y] + (fn [z] (+ x y z)))) + +(λ sub-partial [x] + (λ [y] + (λ [z] (- x y z)))) + +(let [a 1] + (let [b 2] + (let [c 3] + (+ a b c)))) + +(let [t {:a 4 :b 8}] + (set t.a 2) t) + +(let [(a b c) (values 1 2 3)] + (+ a b c)) + +(match (add-partial 5 6 7) + [1 [2] 3] (print "osuhow") + 12 :dont + x x) + +(each [key value (pairs {"a" 1 "b" 2})] + (print key value)) + +(for [i 1 10] + (print i)) + +(var numbers [1 2 3 4 5 6]) + +(collect [_ x (ipairs numbers)] + (values x true)) + +(icollect [_ x (ipairs numbers)] + (+ x 1)) + +(fcollect [i 0 10 2] + (if (> i 2) (* i i))) + +(accumulate [acc 0 _ x (ipairs numbers)] + (+ acc x)) + +(faccumulate [n 0 i 1 5] (+ n i)) ; => 15 + +(#(faccumulate [n 1 i 1 $] (* n i)) 5) ; => 120 (factorial!) +((hashfn (faccumulate [n 1 i 1 $] (* n i))) 5) ; => 120 (factorial!) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/fish/regular.fish b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/fish/regular.fish new file mode 100644 index 00000000..d439c24d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/fish/regular.fish @@ -0,0 +1,7 @@ +set -l shells "$SHELL" /bin/{zsh,bash,sh} (which nu) /usr/bin/xonsh + +echo "Your first few shells is $shells[1..3]" + +if set -q shells[10] + echo "You defined at least 10 shells" +end diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/go/regular.go b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/go/regular.go new file mode 100644 index 00000000..ea187cf1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/go/regular.go @@ -0,0 +1,108 @@ +package main + +import ( + "fmt" + "sort" + "regexp" +) + +const ( + TOOEXPENSIVE = 200 +) + +type Wine struct { + Name string + Produced int + Price float32 + InStock bool +} + +func (w Wine) String() string { + return fmt.Sprintf("Name: %s, Produced: %d, Cost: %0.2f", w.Name, w.Produced, w.Price) +} + +type ByProduced []Wine + +func (a ByProduced) Len() int { return len(a) } +func (a ByProduced) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByProduced) Less(i, j int) bool { return a[i].Produced < a[j].Produced } + +func isFloat32(i interface{}) bool { + switch v := i.(type) { + case float32: + fmt.Printf("%v is a float32", i.(float32)) + return true + default: + fmt.Printf("%v is not a float32", v) + return false + } +} + +func SumUp[K comparable, V float32](v1 V, v2 V) V { + return v1 + (((v2))) +} + +func main() { + var re = regexp.MustCompile(`x`) + + wines := []Wine{ + {"Cabernet Sauvignon", 1991, 200.0, true}, + {"Merlot", 1939, 500.0, true}, + {"Zinfandel", 1982, 120.0, false}, + } + + fmt.Println(len(wines[:2])) + stringArr := [4]string{"a", "b", "c", "d"} + + addons := map[string]struct { + Item string + Price float32 + }{ + "Zinfandel": {Item: "chocolate", Price: 10.0}, + "Cabernet Sauvignon": {Item: "cake", Price: 12.0}, + } + + var ( + nonexpensive []Wine + ) + +LABEL: + for { + for { + for { + for { + for { + sort.Sort(ByProduced(wines)) + for _, wine := range wines { + switch wprice := wine.Price; { + case wprice > TOOEXPENSIVE: + // Too expensive + default: + nonexpensive = append(nonexpensive, wine) + } + } + + break LABEL + } + } + } + } + } + + for _, wine := range nonexpensive { + if wine.InStock { + if wine.InStock { + if wine.InStock { + if wine.InStock { + if isFloat32(wine.Price) { + fmt.Println("I can sell you", wine) + if val, ok := addons[wine.Name]; ok { + fmt.Println("And I have a bundle with ", val.Item, " if you would like ? you can get it for ", SumUp[float32](wine.Price, val.Price)) + } + } + } + } + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/haskell/regular.hs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/haskell/regular.hs new file mode 100644 index 00000000..5a7ff096 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/haskell/regular.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE RecordWildCards #-} + +module ExampleModule + ( ExampleRecord (..) + , someRecord + , mkRec + , mkRec2 + , mkRec3 + , mkRec4 + ) where + +import Data.Maybe hiding (fromJust) +import Data.Functor ((<$>)) + +data ExampleRecord + = ExampleRecord + { name :: String + , mmUnit :: Maybe (Maybe ()) + } + deriving (Eq, Show) + +getName :: ExampleRecord -> String +getName ExampleRecord {..} = name + +someRecord :: ExampleRecord +someRecord = anotherRecord { name = "xyz" } + where anotherRecord = mkRec "" Nothing + +mkRec :: String -> Maybe (Maybe a) -> ExampleRecord +mkRec name (Just (Just _)) = ExampleRecord {..} + where mmUnit = Just $ Just () +mkRec name (Just _) = ExampleRecord {..} + where mmUnit = Just Nothing +mkRec name _ = ExampleRecord {..} + where mmUnit = Nothing + +mkRec2 :: String -> String -> ExampleRecord +mkRec2 first last = mkRec (first <> " " <> last) Nothing + +mkRec3 :: [Char] -> ExampleRecord +mkRec3 (a:b:c:_) = mkRec [a, b, c] Nothing +mkRec3 _ = mkRec "" Nothing + +mkRec4 :: (String, String) -> ExampleRecord +mkRec4 (a, b) = mkRec (a <> b) Nothing diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/hcl/basic.tf b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/hcl/basic.tf new file mode 100644 index 00000000..bef06343 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/hcl/basic.tf @@ -0,0 +1,72 @@ +terraform { + required_providers { + provider1 = { + source = "provider1" + version = "0.1.3" + } + } +} + +data "terraform_remote_state" "test_remotestate" { + backend = "test" + + config = { + storage_account_name = "abc" + container_name = "terraform-state" + key = "prod.terraform.tfstate" + } +} + +resource "provider_role_grants" "admins_role_grants" { + provider = provider.security_admin + role_name = provider_role.admins_role.name + users = [provider_user.user1.name] + roles = [provider_role.role2.name] +} + +resource "provider_grant" "usage_grants" { + for_each = toset(["USAGE", "TEST"]) + privilege = each.key + roles = [provider_role.role2.name] +} + +resource "example" "binary_expressions" { + cond1 = (0*1) ? 1 : "foobar" + bin1 = ((!(1)+2)%3)*4 +} + +resource "example" "for_expressions" { + for1 = { for i, v in ["a", "a", "b"] : v => i... } + for2 = [ for k, v in x : "${k}-${v}" ] +} + +variable "timestamp" { + type = string + + validation { + # formatdate fails if the second argument is not a valid timestamp + condition = can(formatdate("", var.timestamp)) + error_message = "Hello, %{ if var.name != "" }${var.name}%{ else }unnamed%{ endif }!" + } +} + +block { + sample = <<-EOT + %{ for ip in aws_instance.example[*].private_ip } + server ${ip} + %{ endfor } + EOT +} + +resource "terraform_data" "cluster" { + # Replacement of any instance of the cluster requires re-provisioning + triggers_replace = aws_instance.cluster.[*].id + + # Bootstrap script can run on any instance of the cluster + # So we just choose the first in this case + connection { + host = aws_instance.cluster.[0].public_ip + } +} + +# vim:ft=hcl diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/highlight_spec.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/highlight_spec.lua new file mode 100644 index 00000000..b920f181 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/highlight_spec.lua @@ -0,0 +1,264 @@ +---@class ExpectedRainbowHighlight +---@field line number 0-based index of the line to be highlighted +---@field column number 0-based index of the character to be highlighted +---@field rainbow_level string The expected level of the rainbow to use in the highlight + +---@param source_code_lines string[] +---@return ExpectedRainbowHighlight[] +local function get_expected_highlights(source_code_lines) + ---@type ExpectedRainbowHighlight[] + local expected_highlights = {} + + for line_index, line in ipairs(source_code_lines) do + local matches = string.find(line, "hl[%s%d]*") + if matches ~= nil then + for char_index = 1, #line do + local char = line:sub(char_index, char_index) + if string.match(char, "%d") then + table.insert(expected_highlights, { + -- NOTE: subtract 1 to get the index to be 0-based, + -- and another 1 because the hl-line describes the + -- expected highlights for the line above the hl-line + line = line_index - 2, + column = char_index - 1, + rainbow_level = char, + }) + end + end + end + end + + return expected_highlights +end + +---Returns the name of the rainbow highlight group for a given level +---@param level string +---@return string +local function get_rainbow_highlight_group_name(level) + return "rainbowcol" .. level +end + +---Prepares a line with the invalid column marked by ^. +---Used in error reporting. +---@param invalid_column number 0-based +---@return string +local function get_error_marker_line(invalid_column) + return string.rep(" ", invalid_column) .. "^" +end + +---Prints the line with an error marker line that points to some character. +---@param line string The line to print +---@param line_number number The line number to be printed +---@param error_marker_column number 0-based column number to place the error marker +local function print_line_with_error_marker(line, line_number, error_marker_column) + local line_number_width = 3 + + print(string.format("%" .. line_number_width .. "d: %s", line_number, line)) + print( + string.format( + "%" .. line_number_width .. "s %s\n", + "", + get_error_marker_line(error_marker_column) + ) + ) +end + +---@class HighlightedSymbol +---@field line number 0-based +---@field column number 0-based +---@field hl_group string + +---@param extmarks table[] Extmark returned from nvim_buf_get_extmarks +---@return HighlightedSymbol[] +local function get_highlighted_symbols_from_extmarks(extmarks) + ---@type HighlightedSymbol[] + local highlighted_symbols = {} + + for _, extmark in ipairs(extmarks) do + -- TODO: support multi-line extmarks + local line = extmark[2] + local start_col = extmark[3] + local details = extmark[4] + ---Extmark is end_col-exclusive + local end_col = details.end_col + + for col = start_col, end_col - 1 do + table.insert(highlighted_symbols, { + line = line, + column = col, + hl_group = details.hl_group, + }) + end + end + + return highlighted_symbols +end + +---Prunes duplicate highlighted symbols ensuring that each symbol is highlighted +---with a single highlight group. +---nvim-ts-rainbow sometimes sets duplicated extmarks to highlight symbols. +---Not pruning duplicates would mean errors would be reported multiple times +--(once for each duplicate extmark). +---@param highlighted_symbols HighlightedSymbol[] The table of highlighted symbols. it will be modified in place. +---@param source_code_lines string[] Source code lines used for error reporting +local function remove_duplicate_highlighted_symbols(highlighted_symbols, source_code_lines) + local multiple_highlights_for_symbols = false + + -- NOTE: manual index tracking because one loop iteration can remove + -- multiple elements. Using iterators could iterate over removed indices + local index = 1 + while index <= #highlighted_symbols do + local symbol = highlighted_symbols[index] + + -- The body of the loop tries to prune duplicates in the range of + -- index+1..#highlighted_symbols + + -- NOTE: loop from the end to allow removing elements in the loop while + -- preserving indices that will be looped over in the future + for other_symbol_index = #highlighted_symbols, index + 1, -1 do + local other_symbol = highlighted_symbols[other_symbol_index] + if symbol.line == other_symbol.line and symbol.column == other_symbol.column then + if symbol.hl_group == other_symbol.hl_group then + table.remove(highlighted_symbols, other_symbol_index) + else + print("Symbol has multiple different highlight groups assigned to it.") + print( + string.format( + "Found highlight groups: %s %s", + symbol.hl_group, + other_symbol.hl_group + ) + ) + + print_line_with_error_marker( + source_code_lines[symbol.line + 1], + symbol.line + 1, + symbol.column + ) + multiple_highlights_for_symbols = true + end + end + end + + index = index + 1 + end + + assert(not multiple_highlights_for_symbols, "There are multiple highlights for some symbols") +end + +local function verify_highlights_in_file(filename) + local extended_mode = string.find(filename, "extended") ~= nil + local rainbow_module = require("nvim-treesitter.configs").get_module("rainbow") + rainbow_module.extended_mode = extended_mode + + vim.cmd.edit(filename) + + local source_code_lines = vim.api.nvim_buf_get_lines(0, 0, -1, 1) + + vim.api.nvim_buf_set_lines(0, 0, -1, true, source_code_lines) + + local rainbow_ns_id = vim.api.nvim_get_namespaces().rainbow_ns + assert.not_equal(nil, rainbow_ns_id, "rainbow namespace not found") + + local parser = require("nvim-treesitter.parsers").get_parser(0) + assert.truthy(parser, "Parser not found") + parser:parse() + + -- NOTE: nvim_buf_get_extmarks does not return extmarks that contain + -- some range. It only returns extmarks within the given range. + -- We cannot use it to look for extmarks for a given symbol, because sometimes + -- the extmarks are for a range (e.g. when highlighting a JSX tag, the + -- whole range "div" is a single extmark and asking for an extmark for + -- the position of "i" returns nothing). + -- Thus, we must filter through all extmarks set on the buffer and + -- check each symbol. + local extmarks = vim.api.nvim_buf_get_extmarks(0, rainbow_ns_id, 0, -1, { details = true }) + local highlighted_symbols = get_highlighted_symbols_from_extmarks(extmarks) + + remove_duplicate_highlighted_symbols(highlighted_symbols, source_code_lines) + + local some_symbol_not_highlighted = false + local invalid_highlight = false + for _, expected_highlight in ipairs(get_expected_highlights(source_code_lines)) do + local symbol_highlighted = false + + -- NOTE: loop from the end to allow removing elements inside of the loop + -- without changing the indices that will be looped over + for i = #highlighted_symbols, 1, -1 do + local highlighted_symbol = highlighted_symbols[i] + if + highlighted_symbol.line == expected_highlight.line + and highlighted_symbol.column == expected_highlight.column + then + symbol_highlighted = true + + local expected_highlight_group = + get_rainbow_highlight_group_name(expected_highlight.rainbow_level) + if expected_highlight_group ~= highlighted_symbol.hl_group then + invalid_highlight = true + print( + string.format( + 'Invalid rainbow highlight group. Expected "%s", found "%s"', + expected_highlight_group, + highlighted_symbol.hl_group + ) + ) + print_line_with_error_marker( + source_code_lines[expected_highlight.line + 1], + expected_highlight.line + 1, + expected_highlight.column + ) + end + + -- NOTE: remove the matched highlighted symbol to later + -- check that all highlighted symbols matched some expected + -- highlight + table.remove(highlighted_symbols, i) + end + end + + if not symbol_highlighted then + print( + string.format( + 'No highlight groups detected. Expected "%s".', + get_rainbow_highlight_group_name(expected_highlight.rainbow_level) + ) + ) + print_line_with_error_marker( + source_code_lines[expected_highlight.line + 1], + expected_highlight.line + 1, + expected_highlight.column + ) + some_symbol_not_highlighted = true + end + end + + for _, symbol in ipairs(highlighted_symbols) do + print( + string.format( + 'Symbol was extraneously highlighted with highlight group "%s"', + symbol.hl_group + ) + ) + print_line_with_error_marker( + source_code_lines[symbol.line + 1], + symbol.line + 1, + symbol.column + ) + end + assert(not invalid_highlight, "Some symbol was incorrectly highlighted") + assert(not some_symbol_not_highlighted, "Some symbol was not highlighted") + assert(#highlighted_symbols == 0, "Extraneous highlights") +end + +describe("Highlighting integration tests", function() + local files = vim.fn.glob("test/highlight/**/*.*", nil, true) + + for _, filename in ipairs(files) do + if not string.match(filename, "highlight_spec.lua") then + it(filename, function() + verify_highlights_in_file(filename) + end) + end + end +end) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/html/regular.html b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/html/regular.html new file mode 100644 index 00000000..f01eefeb --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/html/regular.html @@ -0,0 +1,31 @@ + + + + + + + + Test page + + + + +

+ This is an Example link. +

+
+

+ This is an Example
link
with
line
break. +

+
+
+

Goodbye.

+
+ + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/janet_simple/regular.janet b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/janet_simple/regular.janet new file mode 100644 index 00000000..8d3fb615 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/janet_simple/regular.janet @@ -0,0 +1,251 @@ +@(:ant :bee + :cat :dog + :elephant :fox + :giraffe :heron + :iguana :janet) + +@["Archimedes" "Bohm" + "Cantor" "Deming" + "Erdos" "Feynman" + "Gauss" "Houdini" + "Ishikawa" "Janet"] + +{"Ada" + {:file-extensions [".adb" ".ads"] + :people ["Jean Ichbiah"] + :year 1983} + + "Bash" + {:file-extensions [".sh"] + :people ["Brian Fox" + "Chet Ramey"] + :year 1989} + + "C" + {:file-extensions [".c" ".h"] + :people ["Dennis Ritchie"] + :year 1972} + + "Dart" + {:file-extensions [".dart"] + :people ["Lars Bak" + "Kasper Lund"] + :year 2011} + + "Emacs Lisp" + {:file-extensions [".el" ".elc" ".eln"] + :people ["Richard Stallman" + "Guy L. Steele, Jr."] + :year 1985} + + "Forth" + {:file-extensions [".fs" ".fth" ".4th" ".f" ".forth"] + :people ["Charles H. Moore"] + :year 1970} + + "Go" + {:file-extensions [".go"] + :people ["Robert Griesemer" + "Rob Pike" + "Ken Thompson"] + :year 2009} + + "Haskell" + {:file-extensions [".hs" ".lhs"] + :people ["Lennart Augustsson" + "Dave Barton" + "Brian Boutel" + "Warren Burton" + "Joseph Fasel" + "Kevin Hammond" + "Ralf Hinze" + "Paul Hudak" + "John Hughes" + "Thomas Johnsson" + "Mark Jones" + "Simon Peyton Jones" + "John Launchbury" + "Erik Meijer" + "John Peterson" + "Alastair Reid" + "Colin Runciman" + "Philip Wadler"] + :year 1990} + + "Idris" + {:file-extensions [".idr" ".lidr"] + :people ["Edwin Brady"] + :year 2007} + + "Janet" + {:file-extensions [".cgen" ".janet" ".jdn"] + :people ["Calvin Rose"] + :year 2017}} + +~@{:main + (some :input) + # + :input + (choice :non-form + :form) + # + :non-form + (choice :whitespace + :comment) + # + :whitespace + (choice (some (set " \0\f\t\v")) + (choice "\r\n" + "\r" + "\n")) + # + :comment + (sequence "#" + (any (if-not (set "\r\n") 1))) + # + :form + (choice :reader-macro + :collection + :literal) + # + :reader-macro + (choice :fn + :quasiquote + :quote + :splice + :unquote) + # + :fn + (sequence "|" + (any :non-form) + :form) + # + :quasiquote + (sequence "~" + (any :non-form) + :form) + # + :quote + (sequence "'" + (any :non-form) + :form) + # + :splice + (sequence ";" + (any :non-form) + :form) + # + :unquote + (sequence "," + (any :non-form) + :form) + # + :literal + (choice :number + :constant + :buffer + :string + :long-buffer + :long-string + :keyword + :symbol) + # + :collection + (choice :array + :bracket-array + :tuple + :bracket-tuple + :table + :struct) + # + :number + (drop (cmt + (capture (some :name-char)) + ,scan-number)) + # + :name-char + (choice (range "09" "AZ" "az" "\x80\xFF") + (set "!$%&*+-./:@^_")) + # + :constant + (sequence (choice "false" "nil" "true") + (not :name-char)) + # + :buffer + (sequence "@\"" + (any (choice :escape + (if-not "\"" 1))) + "\"") + # + :escape + (sequence "\\" + (choice (set `"'0?\abefnrtvz`) + (sequence "x" [2 :h]) + (sequence "u" [4 :h]) + (sequence "U" [6 :h]) + (error (constant "bad escape")))) + # + :string + (sequence "\"" + (any (choice :escape + (if-not "\"" 1))) + "\"") + # + :long-string :long-bytes + # + :long-bytes + {:main (drop (sequence :open + (any (if-not :close 1)) + :close)) + :open (capture :delim :n) + :delim (some "`") + :close (cmt (sequence (not (look -1 "`")) + (backref :n) + (capture (backmatch :n))) + ,=)} + # + :long-buffer + (sequence "@" + :long-bytes) + # + :keyword + (sequence ":" + (any :name-char)) + # + :symbol (some :name-char) + # + :array + (sequence "@(" + (any :input) + (choice ")" + (error (constant "missing )")))) + # + :tuple + (sequence "(" + (any :input) + (choice ")" + (error (constant "missing )")))) + # + :bracket-array + (sequence "@[" + (any :input) + (choice "]" + (error (constant "missing ]")))) + # + :bracket-tuple + (sequence "[" + (any :input) + (choice "]" + (error (constant "missing ]")))) + :table + (sequence "@{" + (any :input) + (choice "}" + (error (constant "missing }")))) + # + :struct + (sequence "{" + (any :input) + (choice "}" + (error (constant "missing }")))) + } diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/java/HelloWorld.java b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/java/HelloWorld.java new file mode 100644 index 00000000..076f9d24 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/java/HelloWorld.java @@ -0,0 +1,66 @@ +@Author(name = "John Doe") +public class HelloWorld { + // Constructor body + public HelloWorld() { + } + + // Method with formal parameters + public static void main(String[] args) { + System.out.println("Hello, world!"); + System.out.println(args[0]); + } + + public static void printList(List>> l) { + // Array initializer + String[] names = {"Alice", "Bob", "Carol", "Dan"}; + // Multi-dimensional dimensions and a dimensions expression + Integer[][] inputArrays = new Integer[3][]; + + // Enhanced for statement (for-each) + for (var name: names) { + var msg = String.format("Hello, %s.", name); + System.out.println(msg); + } + + // Regular for-statement + for (var i = 0; i < 3; ++i) { + System.out.print(i); + } + + // Parentheses around condition + if (false) { + System.err.println("This will never print"); + } + + // Parentheses around catch clause + try { + // A parenthesized expression + System.out.print(((3/0))); + } catch(ArithmeticException e) { + System.err.print(e); + } + + + // Nested bodies + for (var item1: l) { + for (var item2: item1) { + for (var item3: item2) { + System.out.format("%d", item3) + } + } + } + + // Try resource specification + try (FileWriter fw = new FileWriter("test"); + BufferedWriter bw = new BufferedWriter(fw)) { + bw.close(); + } catch (IOException e) { + System.out.println(e); + } + + double d = 13.37; + int i = (int) d; // cast expression + } +} + +// vim:noexpandtab diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/java/LambdaTest.java b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/java/LambdaTest.java new file mode 100644 index 00000000..8dda82b2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/java/LambdaTest.java @@ -0,0 +1,9 @@ +class LambdaTest { + void singleton() { + version -> create; + // Inferred parameters + (record, b) -> record + b; + } +} + +// vim:noexpandtab diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/javascript/regular.js b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/javascript/regular.js new file mode 100644 index 00000000..3ab85acc --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/javascript/regular.js @@ -0,0 +1,67 @@ +// Named imports +import { useState } from 'react' + +// Template strings +const who = 'world'; +console.log(`Hello, ${who}`); + +// Function with nested function +function add(x, y) { + function iter(i, acc) { + if (i == 0) { + return acc; + } + return iter(i - 1, acc + 1); + } + return iter(y, x) +} + +// Loops +function iterate() { + for (let i = 0; i <= 2; i++) { + break; + } + + let list = [] + for (let element of list) { + console.log(element); + } +} + +// Arrow function definition +const multiply = (x, y) => x * y; + +// Nested object and array +let some_object = { + a: { + b: { + c: {}, + }, + d: [[1, 2, 3]] + } +}; + +// object pattern +const destructuredFunction = ({ value }) => { + return {} +} + +// Subscript expressions +const zeroes = [0]; +console.log(zeroes[zeroes[zeroes[0]]]) + +// Destructuring assignment +const [x, y] = array; + +// Parenthesized expressions +console.log(1 + (2 + (3 + 4))) + +let a = 1 + +switch(a) { + case 1: + break; +} + +// export clause +export { zeroes } diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/json/regular.json b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/json/regular.json new file mode 100644 index 00000000..893326ed --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/json/regular.json @@ -0,0 +1,10 @@ +{ + "foo": "bar", + "bar": { + "baz": [ + [ + [] + ] + ] + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/json5/regular.json5 b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/json5/regular.json5 new file mode 100644 index 00000000..4f5ebdb6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/json5/regular.json5 @@ -0,0 +1,13 @@ +{ + // comments + unquoted: 'and you can quote me on that', + singleQuotes: 'I can use "double quotes" here', + lineBreaks: "Look, Mom! +No \\n's!", + hexadecimal: 0xdecaf, + object: {a: {b: {c: {}}}}, + leadingDecimalPoint: .8675309, andTrailing: 8675309., + positiveSign: +1, + trailingComma: 'in objects', andIn: [[['arrays',]]], + "backwardsCompatible": "with JSON", +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonc/regular.jsonc b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonc/regular.jsonc new file mode 100644 index 00000000..f66d3f0f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonc/regular.jsonc @@ -0,0 +1,13 @@ +// This is a comment +{ + "foo": "bar", + /* This is a multi-line comment + */ + "bar": { + "baz": [ + [ + [] + ] + ] + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/arithmetic.jsonnet b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/arithmetic.jsonnet new file mode 100644 index 00000000..3130fe28 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/arithmetic.jsonnet @@ -0,0 +1,34 @@ +{ + concat_array: [1, 2, 3] + [4], + concat_string: '123' + 4, + equality1: 1 == '1', + equality2: [{}, { x: 3 - 1 }] + == [{}, { x: 2 }], + ex1: 1 + 2 * 3 / (4 + 5), + // Bitwise operations first cast to int. + ex2: self.ex1 | 3, + // Modulo operator. + ex3: self.ex1 % 2, + // Boolean logic + ex4: (4 > 3) && (1 <= 3) || false, + // Mixing objects together + obj: { a: 1, b: 2 } + { b: 3, c: 4 }, + // Test if a field is in an object + obj_member: 'foo' in { foo: 1 }, + // String formatting + str1: 'The value of self.ex2 is ' + + self.ex2 + '.', + str2: 'The value of self.ex2 is %g.' + % self.ex2, + str3: 'ex1=%0.2f, ex2=%0.2f' + % [self.ex1, self.ex2], + // By passing self, we allow ex1 and ex2 to + // be extracted internally. + str4: 'ex1=%(ex1)0.2f, ex2=%(ex2)0.2f' + % self, + // Do textual templating of entire files: + str5: ||| + ex1=%(ex1)0.2f + ex2=%(ex2)0.2f + ||| % self, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/cocktail-comprehensions.jsonnet b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/cocktail-comprehensions.jsonnet new file mode 100644 index 00000000..6be13dc1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/cocktail-comprehensions.jsonnet @@ -0,0 +1,31 @@ +{ + cocktails: { + "Bee's Knees": { + // Construct the ingredients by using + // 4/3 oz of each element in the given + // list. + ingredients: [ // Array comprehension. + { kind: kind, qty: 4 / 3 } + for kind in [ + 'Honey Syrup', + 'Lemon Juice', + 'Farmers Gin', + ] + ], + garnish: 'Lemon Twist', + served: 'Straight Up', + }, + } + { // Object comprehension. + [sd.name + 'Screwdriver']: { + ingredients: [ + { kind: 'Vodka', qty: 1.5 }, + { kind: sd.fruit, qty: 3 }, + ], + served: 'On The Rocks', + } + for sd in [ + { name: 'Yellow ', fruit: 'Lemonade' }, + { name: '', fruit: 'Orange Juice' }, + ] + }, +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/computed-fields.jsonnet b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/computed-fields.jsonnet new file mode 100644 index 00000000..197da375 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/computed-fields.jsonnet @@ -0,0 +1,12 @@ +local Margarita(salted) = { + ingredients: [ + { kind: 'Tequila Blanco', qty: 2 }, + { kind: 'Lime', qty: 1 }, + { kind: 'Cointreau', qty: 1 }, + ], + [if salted then 'garnish']: 'Salt', +}; +{ + Margarita: Margarita(true), + 'Margarita Unsalted': Margarita(false), +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/function.jsonnet b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/function.jsonnet new file mode 100644 index 00000000..fb89f226 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsonnet/function.jsonnet @@ -0,0 +1,44 @@ +// Define a local function. +// Default arguments are like Python: +local my_function(x, y=10) = x + y; + +// Define a local multiline function. +local multiline_function(x) = + // One can nest locals. + local temp = x * 2; + // Every local ends with a semi-colon. + [temp, temp + 1]; + +local object = { + // A method + my_method(x): x * x, +}; + +{ + // Functions are first class citizens. + call_inline_function: + (function(x) x * x)(5), + + call_multiline_function: multiline_function(4), + + // Using the variable fetches the function, + // the parens call the function. + call: my_function(2), + + // Like python, parameters can be named at + // call time. + named_params: my_function(x=2), + // This allows changing their order + named_params2: my_function(y=3, x=2), + + // object.my_method returns the function, + // which is then called like any other. + call_method1: object.my_method(3), + + standard_lib: + std.join(' ', std.split('foo/bar', '/')), + len: [ + std.length('hello'), + std.length([1, 2, 3]), + ], +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsx/regular.jsx b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsx/regular.jsx new file mode 100644 index 00000000..99ff730d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/jsx/regular.jsx @@ -0,0 +1,46 @@ +// Template strings +const who = 'world'; +console.log(`Hello, ${who}`); + +// Nested object +let some_object = { + a: { + b: { + c: {}, + } + } +}; + +// Subscript expressions +const zeroes = [0]; +console.log(zeroes[zeroes[zeroes[0]]]) + +// Parenthesized expressions +console.log(1 + (2 + (3 + 4))) + +function hello() { + console.log('Hello, world!'); +} + +function app() { + const [x, y] = array; + + return ( +
+

+ This is an Example link. +

+

+ This is an Example
link
with
line
break. +

+ + {someFunction().map((x) =>
)} +
+ + {someFunction().map((x) =>
)} +
+ + +
+ ) +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/julia/regular.jl b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/julia/regular.jl new file mode 100644 index 00000000..28b0b12b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/julia/regular.jl @@ -0,0 +1,9 @@ +a = Vector{Int}([1, 2, 3, 4, 5, 6]); +A = [ + 28 32 + 11 70 +]; + +f(x) = abs((x-4)*(x+2)) +b = [f(x) for x ∈ A] +x = (1, 2) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/kotlin/Test.kt b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/kotlin/Test.kt new file mode 100644 index 00000000..c8754d10 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/kotlin/Test.kt @@ -0,0 +1,113 @@ +// Define a simple class with a primary constructor +class Person(private val name: String, private val age: Int, private val t: T) { + // Secondary constructor + constructor(name: String) : this(name, 0) + + class Hello { + class Goodbye { + } + } + + init { + println("New person created with name $name") + } + + // Member function + fun greet() { + println("Hello, my name is $name and I am $age years old.") + } +} + +// Extension function +fun String.exclaim() = "$this!" + +// Top-level function +fun calculateFactorial(n: Int): Int { + return if (n == 1) n else n * calculateFactorial(n - 1) +} + +// Main function - entry point of the program +fun main() { + val person = Person>("Alice", 30, emptyMap()) + person.greet() + + // Using the extension function + println("Wow".exclaim()) + + // Conditional + val number = 5 + if (number % 2 == 0) { + println("$number is even") + } else { + println("$number is odd") + } + + // Loop + for (i in 1..5) { + println("Factorial of $i is: ${calculateFactorial(i)}") + } + + // Using a map + val map = mapOf("a" to 1, "b" to 2, "c" to 3) + for ((key, value) in map) { + println("Key: $key, Value: $value") + } + + // Lambda expression + val numbers = listOf(1, 2, 3, 4, 5) + val doubled = numbers.map { it * 2 } + println("Doubled numbers: $doubled") +} + +val list = listOf(1, 2, 3) +list.forEach { item -> + println(item) +} + +fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int { + return operation(a, b) +} + +val sum = operateOnNumbers(2, 3) { x, y -> x + y } +println("Sum: $sum") + + +val multiply = fun(x: Int, y: Int): Int { + return x * y +} + +println("Product: ${multiply(2, 3)}") + +val x = 2 +when (x) { + 1 -> println("x == 1") + 2 -> println("x == 2") + else -> println("x is neither 1 nor 2") +} + +when { + 1 == 1 -> print("1") + else -> print("not") +} + + +val rows = 2 +val cols = 3 +val matrix = Array(rows) { IntArray(cols) } + +// Fill the array +for (i in matrix.indices) { + for (j in matrix[i].indices) { + matrix[i][j] = i + j + } + matrix[matrix[i][i]] +} + +// Print the 2D array +for (row in matrix) { + for (col in row) { + print("$col ") + } + println() +} + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/latex/regular.tex b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/latex/regular.tex new file mode 100644 index 00000000..426c7bdc --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/latex/regular.tex @@ -0,0 +1,31 @@ +\documentclass{article} % Starts an article +\usepackage{amsmath} % Imports amsmath + +\title{\LaTeX} % Title + +\begin{document} % Begins a document + \maketitle + \LaTeX{} is a document preparation system for + the \TeX{} typesetting program. It offers + programmable desktop publishing features and + extensive facilities for automating most + aspects of typesetting and desktop publishing, + including numbering and cross-referencing, + tables and figures, page layout, + bibliographies, and much more. \LaTeX{} was + originally written in 1984 by Leslie Lamport + and has become the dominant method for using + \TeX; few people write in plain \TeX{} anymore. + The current version is \LaTeXe. + + % This is a comment, not shown in final output. + % The following shows typesetting power of LaTeX: + \begin{align} + E_0 &= mc^2 \\ + E &= \frac{mc^2}{\sqrt{1-\frac{v^2}{c^2}}} + \end{align} + + This is testing {nesting {of {text} with a reference\ref{section:some_sec}}}. +\end{document} + +% vim:ft=latex diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/lua/regular.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/lua/regular.lua new file mode 100644 index 00000000..ac363c39 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/lua/regular.lua @@ -0,0 +1,66 @@ +-- This is a comment + +local function f1(a, b) + local function f2(a2, b2) + return a2, b2 + end + return f2(a, b) +end + +function GlobalFunction() + print 'This is a global function' +end + +if true then + print 'True condition' +elseif false then + print 'Alternative condition' +elseif false then + print 'Alternative condition' +else + print 'Alternative' +end + +while false do + print 'A while-loop' +end + +repeat + print 'This will repeat only once' +until true + +do + print 'A block' +end + +for i, v in ipairs({'a', 'b', 'c'}) do + print(string.format("%d = %s", i, v)) +end + +for i = 1, 5, 1 do + print(string.format("Number %d", i)) +end + +print(f1('a', 'b')) +print((((('Hello, world!'))))) + +print { + { + { + 'Hello, world!' + } + } +} + +local one = {1} + +print(one[one[one[1]]]) + +-- Embedded Vim script +vim.cmd [[ + echo a(b(c(d(e(f()))))) +]] + +local tbl = { + ["highlight me"] = {} +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/luadoc/regular.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/luadoc/regular.lua new file mode 100644 index 00000000..f9344771 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/luadoc/regular.lua @@ -0,0 +1,30 @@ +---@type fun(x: (fun(): integer), y: fun(z: fun())) + +---@type { key1: { key2: { [string]: table }, [integer]: integer } } + +---@type table>> + +---@class test +---@field a boolean +---@field b (((boolean))) +---@field x number | string | { key: number | string | boolean } | boolean +---@field [string] boolean + +---@type string[] +local _str_tbl = { 'a', 'b', 'c' } + +---@param f fun(i: integer): (integer, integer) +---@return integer, integer +local function _test_fun(f) + return f(1) +end + +-- Note: The parser nests union types, which can mess +-- with rainbow-delimiters highlighting, so we don't +-- highlight the '|' here: +---@type number | integer[] | string | number[] | string[] | boolean | boolean[] +local _x = 1 +---@type boolean[] | integer[] +local _t = { true, false } or { 0, 1 } +---@type (boolean | integer)[] +local _arr = { true, 0 } diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/make/makefile b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/make/makefile new file mode 100644 index 00000000..73487eca --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/make/makefile @@ -0,0 +1,23 @@ +.PHONY: all + +all: herp derp + +herp: a.txt + # Command substitution + echo $(basedir $(pwd)) + + # Variable expansion + echo ${FOO${BAR${BAZ}}} + + # Test expression (using the `test` command) + if [ -d "herp/derp/" ]; then + echo "Yay" + fi + + # Test expression (bashism) + if [[ -d "herp/derp/" ]]; then + echo "Yay" + fi + + # Sub-shells + (true; false; (true; true; (false; true))) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/markdown/extra.md b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/markdown/extra.md new file mode 100644 index 00000000..d28b0576 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/markdown/extra.md @@ -0,0 +1,9 @@ +Some markdown. +```markdown +~~~lua +print({{{{}}}}) +print({{{{}}}}) +vim.cmd[[echo str([])]] +~~~ +``` +More markdown diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/markdown/regular.md b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/markdown/regular.md new file mode 100644 index 00000000..0963363c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/markdown/regular.md @@ -0,0 +1,72 @@ +# A Markdown example + +## Some nonsense text +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu +fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in +culpa qui officia deserunt mollit anim id est laborum. + +## Injected languages + +Here we test highlighting of an injected + + +### Lua + +Lua is a good candidate + +```lua +-- This is a comment + +local function f1(a, b) + local function f2(a2, b2) + return a2, b2 + end + return f2(a, b) +end + +print(f1('a', 'b')) +print((((('Hello, world!'))))) + +print { + { + { + 'Hello, wold!' + } + } +} + +local one = {1} + +print(one[one[one[1]]]) + +-- Embedded Vim script +vim.cmd [[ + echo a(b(c(d(e(f()))))) +]] +-- Embedded Vim script on one line +vim.cmd[[echo a(b(c(d())))]] +``` + +### Vim script + +Let's try another embedded language + +```vim +let g:my_list = [[[1]]] +let g:my_dict = { + \'a': { + \'b': { + \'c': {}, + \} + \} +\ } + +echo string(1 + (2 + (3 + 4))) +echo string(-(3)) +echo string((5)-(3)) +echo string((1) ? (2) : (3)) +echo ((('Hello, world!'))) +```` diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/nim/regular.nim b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/nim/regular.nim new file mode 100644 index 00000000..226ab487 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/nim/regular.nim @@ -0,0 +1,26 @@ +# parameter_declaration_list and generic_parameter_list +proc p[T: seq[int]](a: seq[seq[int]]): int = + result = 1 + +let + # array + a = [[[1], [1]], [[1], [1]]] + # tuple and tuple_deconstruct_declaration + (((q), (_)), ((p), (_))) = (((1, ), (1, )), ((1, ), (1, ))) + # set + c = {'a'} + # table + d = {1: {1: {: }, 2: {: }}, 2: {1: {: }, 2: {: }}} + # parenthesized + e = ((( + discard; + discard; + 1))) + # call and bracket_expression + f = p[seq[int]](@[@[p[seq[int]](@[@[1]])]]) + +# cast and field_declaration_list +cast[tuple[a: seq[int], ]](p[seq[int]](@[@[1]])) + +# term_rewriting_pattern and curly_expression +template t{(0|1|2){x}}(x: untyped): untyped = x + 1 diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/nix/regular.nix b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/nix/regular.nix new file mode 100644 index 00000000..4ba44150 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/nix/regular.nix @@ -0,0 +1,56 @@ +{ + description = "Test flake for rainbow-delimiters.nvim"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + pre-commit-hooks = { + url = "github:cachix/pre-commit-hooks.nix"; + }; + + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + pre-commit-hooks, + ... + }: let + supportedSystems = [ + "x86_64-linux" + ]; + in + flake-utils.lib.eachSystem supportedSystems (system: let + inherit (nixpkgs) lib; + pkgs = nixpkgs.legacyPackages.${system}; + + formatting = pre-commit-hooks.lib.${system}.run { + src = lib.cleanSource self; + hooks = { + alejandra.enable = true; + markdownlint.enable = true; + }; + settings = { + markdownlint.config = { + MD004 = false; + }; + }; + }; + in { + apps = rec { + hi = with pkgs; flake-utils.lib.mkApp { + drv = writeShellScriptBin "hi" '' + echo "Hello, world" + ''; + }; + default = hi; + }; + packages = { + }; + checks = { + inherit formatting; + }; + }); +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/perl/regular.pl b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/perl/regular.pl new file mode 100644 index 00000000..b6da9cd8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/perl/regular.pl @@ -0,0 +1,47 @@ +#!/usr/bin/perl +use v5.20; +use feature qw(signatures); +no warnings qw(experimental::signatures); + +sub foobar($foo, $bar) { + my %h = ( + foo => +{ foo => 'bar' }, + ($foo ? ( + bar => hoge(foo => $bar)->id, + ) : ()), + ); + say $h{foo}{foo}; + my $h_ref = \%h; + say $h_ref->{foo}{foo}; +} + +sub barfoo { + my ($foo, $bar) = @_; + say $_[0]; + foobar([ $foo, $bar ]); + my ($ary) = ([ ($foo, $bar) ]); + $ary = [ $foo, $bar ]; + my $result = ($foo ? $bar : $foo); + { + say @$ary[0]; + say @{$ary}[0]; + say $ary->[1]; + } + if ($foo) { + # TODO: tree-sitter-perl cannot detect string interpolation yet. + # say "${foo} $bar"; + } + my $sub = sub { + say $foo; + }; + map { +{} } @ary; + qw(a b c d e); + qr(a b c d e); + qx(a b c d e); + qq(a b c d e); + q(a b c d e); + m(a b c d e); + s(a b c)(d e); + tr(a b c)(d e); + y(a b c)(d e); +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/php/regular.php b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/php/regular.php new file mode 100644 index 00000000..cbb9c87f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/php/regular.php @@ -0,0 +1,32 @@ +innerValue; + } + +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/python/regular.py b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/python/regular.py new file mode 100644 index 00000000..d371384f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/python/regular.py @@ -0,0 +1,49 @@ +# NOTE: When updating this file update the Starlark test file as well if +# applicable. + +from typing import ( + Dict, + List, +) + + +def sum_list(lst: List[Dict[int, int]]) -> int: + result = 0 + for inner in lst: + for i in inner: + result += i + return result + + +my_list = [[['Hello, world!']]] +my_dict = {'x': {'x': {'x': 'Hello, wold!'}}} +my_set = {{{{'Hello, wold!'}}}} +my_tuple = (((('Hello, wold!'),),),) + +list_comp = [i for i in [j for j in range(5)] if i % 2 == 0] +dict_comp = {k: v for k, v in {k: v for k, v in {'k': 'v'}.items()} + if k == 'k'} +set_comp = {i for i in {j for j in range(5)} if i % 2 == 0} +gen_comp = (i for i in (j for j in range(5)) if i % 2 == 0) + +match my_dict: + case {'x': {'x': {'x': message}}}: + print(message) + case [[[message]]]: + print(message) + case (((message))): + print(message) + +zero = [0] + +(a,b) = (1,2) +[c,d] = [3,4] + +print(zero[zero[zero[0]]]) + + +print(2 + ((((3))))) +print(len(my_list)) + +# Format-string with embedded delimiters +print(f'The sum of 2 and 3 is {2 + (1 + 2)}') diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/query/regular.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/query/regular.scm new file mode 100644 index 00000000..7d37c044 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/query/regular.scm @@ -0,0 +1,7 @@ +(foo (bar (baz)) @bar) @foo + +[foo [bar [baz]] @bar] @foo + +(foo [bar (baz)] @bar) @foo + +;;; vim:ft=query diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/r/regular.r b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/r/regular.r new file mode 100644 index 00000000..993d8a26 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/r/regular.r @@ -0,0 +1,35 @@ +# call +suppressWarnings(library(datasets)) + +# outer subset, inner subset2 +mtcars[mtcars[[1, 2]], ] + +# if ladder, inner brace_list +var <- 10 +if (var > 5) { + print(paste(var, "is greater than 5")) + if (var < 10) { + print(paste(var, "is less than 10")) + } +} + +foobar <- function(num) { + for (i in 1:5) { + print(i) + } + + while (TRUE) { + break + } + + x <- "a" + v <- switch(x, "a"="apple", "b"="banana", "c"="cherry") + + if (num > 0) { + return("Positive") + } else if (num < 0) { + return("Negative") + } else { + return("Zero") + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/racket/regular.rkt b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/racket/regular.rkt new file mode 100644 index 00000000..446f941c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/racket/regular.rkt @@ -0,0 +1,56 @@ +#lang racket + +(define [add x y] + "A silly way to add two numbers recursively." + (if (zero? y) + x + (add (add1 x) + (sub1 x)))) + +(define-syntax foo +  (syntax-rules () +    ((_ a ...) +     (printf "~a\n" (list a ...))))) + +{+ 2 {+ 3 {+ 4 5}}} + +'(((a . b))) +'((((a b . c)))) + +'[[[a . b]]] +'[[[a b . c]]] + +'{{{a . b}}} +' +'([{a . b}]) +'([{a b . c}]) + +;;; Vector literals +#(#(#(a))) +#[#[#[a]]] +#{#{#{a}}} + + +;;; Inexact number vector literals +#fl(#fl(#fl(a))) +#fl[#fl[#fl[a]]] +#fl{#fl{#fl{a}}} + +#Fl(#Fl(#Fl(a))) +#Fl[#Fl[#Fl[a]]] +#Fl{#Fl{#Fl{a}}} + + +;;; Fixnum vector literals +#fx(#fx(#fx(a))) +#fx[#fx[#fx[a]]] +#fx{#fx{#fx{a}}} + +#Fx(#Fx(#Fx(a))) +#Fx[#Fx[#Fx[a]]] +#Fx{#Fx{#Fx{a}}} + + +;;; Structures +(struct prefab-point (x y) #:prefab) +'#s(prefab-point 1 2) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/rasi/regular.rasi b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/rasi/regular.rasi new file mode 100644 index 00000000..55e392f4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/rasi/regular.rasi @@ -0,0 +1,20 @@ +// Ref: https://github.com/davatorium/rofi/blob/next/doc/rofi-theme.5.markdown + +* { + background-image: url("a.jpg", width); + background-color: env(ROFI_BACKGROUND_COLOR, transparent); + text-color: rgba(256, 256, 256, 0.9); + text-color-2: var(text-color, hsl(20, 1, 1)); + text-color-3: hwb(20, 1, 10); + text-color-4: cmyk(20, 15, 10, 5); + width: 1024px +} + +@media (monitor-id: ${ROFI_MAIN_MONITOR}) { + width: calc(120% * 1024px); +} + +mainbox { + background-image: linear-gradient(to bottom, darkgreen/50%, black/70%); + children: [inputbar, listview]; +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/regex/regular.txt b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/regex/regular.txt new file mode 100644 index 00000000..8fd7ce32 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/regex/regular.txt @@ -0,0 +1,9 @@ +a(b(c(d))) +a(b(c(d)?)) +a(b(c[def])) +a(b(c[^def])) +a(b(c{1})) +a(b(c{1,4})) +a(b(c(?=d))) + + vim:ft=regex diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/rst/regular.rst b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/rst/regular.rst new file mode 100644 index 00000000..ecf2b5e8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/rst/regular.rst @@ -0,0 +1,74 @@ +.. default-role:: code + +############################ + A reStructuredText example +############################ + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu +fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in +culpa qui officia deserunt mollit anim id est laborum. + + +Injected languages +################## + +Here we test highlighting of an injected + + +Lua +=== + +Lua is a good candidate + +.. code:: lua + + -- This is a comment + + local function f1(a, b) + local function f2(a2, b2) + return a2, b2 + end + return f2(a, b) + end + + print(f1('a', 'b')) + print((((('Hello, world!'))))) + + print { + { + { + 'Hello, world!' + } + } + } + + local one = {1} + + print(one[one[one[1]]]) + + -- Embedded Vim script + vim.cmd [[ + echo a(b(c(d(e(f()))))) + ]] + +Let's try another embedded language + +.. code:: vim + + let g:my_list = [[[1]]] + let g:my_dict = { + \'a': { + \'b': { + \'c': {}, + \} + \} + \ } + + echo string(1 + (2 + (3 + 4))) + echo string(-(3)) + echo string((5)-(3)) + echo string((1) ? (2) : (3)) + echo ((('Hello, world!'))) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/ruby/regular.rb b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/ruby/regular.rb new file mode 100644 index 00000000..810b3099 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/ruby/regular.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +an_array = [[['Hello', 'world!']]] # rubocop:disable Lint/UselessAssignment +a_hash = { 'x' => { 'x' => { 'x' => 'Hello, world!' } } } # rubocop:disable Lint/UselessAssignment + +def greeting(name, age) + age_in_seconds = (((((age * 365))) * 24) * 60) * 60 + + puts "Hello, #{name}! You are #{age} years old, which is #{age_in_seconds} in seconds!" +end + +puts greeting('Fry', 25) + +[[1], [2], [3]].each { |nums| nums.each { |num| puts num } } diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/rust/regular.rs b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/rust/regular.rs new file mode 100644 index 00000000..5803125e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/rust/regular.rs @@ -0,0 +1,171 @@ +#![crate_type = "lib"] +struct NestedStruct { + value: u32, + inner: Inner, +} + +struct Inner { + value: u32, +} + +extern "C" { + fn extern_block(); +} + +union TestUnion { + val_1: f32, + val_2: u32, +} + +#[derive(Default, Debug)] +struct TupleStruct(u32); + +#[cfg_attr(all(target_os = "linux", feature = "multithreaded"), derive(Default))] +enum EnumTest { + TupleVariant(u32), + TupleVariantTupleStruct(TupleStruct), + StructVariant { value: u32 }, + NestedStructVariant { inner: Inner }, +} + +fn test_type_param() -> usize { + std::mem::size_of::() +} + +fn test_param(a: u32, b: u32) -> u32 { + a * b +} + +fn tuple_param(a: (u32, u32)) -> u32 { + let (a, b) = a; + + a * a +} + +macro_rules! inefficient_vec { + ( $( $x:expr ),* ) => { + { + let mut temp_vec = Vec::new(); + $( + temp_vec.push($x); + )* + temp_vec + } + }; +} + +pub(crate) struct VisibilityModifier; + + +pub const NAMES: &'static [(&'static str, u32)] = &[ + ("TEST NAME 1", 1), + ("TEST NAME 2", 2), +]; + +fn main() { + let nested_vec: Vec>>>>> = Vec::<_>::new(); + let arr_arr_arr = [[[0; 4]; 4]; 4]; + let constructed_struct = Inner { value: 0 }; + + let nested_constructed = NestedStruct { + value: 0, + inner: Inner { value: 0 }, + }; + + let tuple_struct = TupleStruct(0); + let nested_tuple = ( + ((1, 2, 3, 4), (5, 6, 7, 8)), + ((9, 10, 11, 12), (13, 14, 15, 16)), + ); + + let enums = vec![ + EnumTest::TupleVariant(0), + EnumTest::TupleVariantTupleStruct(TupleStruct(0)), + EnumTest::StructVariant { value: 0 }, + EnumTest::NestedStructVariant { + inner: Inner { value: 0 }, + }, + ]; + + let closure = |long_parameter_name: u8, + long_parameter_name_two: u8, + very_long_parameter_name: u8, + extra_long_name: u8| { + let nested_closure = || {}; + nested_closure(); + }; + + let async_block = async { 0 }; + + let labelled_block = 'a: { 0 }; + + let boolean_expr = (((3 * 4) + 5) > 1 || false) && (true || true); + + let num = 5; + + let match_expr = match num { + _ => match boolean_expr { + _ => {} + }, + }; + + let fancy_match_expr = match enums[0] { + EnumTest::TupleVariant(v) => {} + EnumTest::TupleVariantTupleStruct(TupleStruct(v)) => {} + EnumTest::StructVariant { value } => {} + EnumTest::NestedStructVariant { + inner: Inner { value }, + } => {} + }; + + let array = [1, 2, 3, 4]; + + let array_match = match array { + [a, b, c, d] => {} + }; + + let nested_macro = vec![vec![vec![vec![vec![0]]]]]; + + test_param(3, 4); + + let test_tuple: (u32, u32) = (0, 1); + tuple_param(test_tuple); +} + +use level_1::{ + level_2::{ + level_3::{ + level_4::{ + level_5::{A, B}, + C, D, + }, + E, F, + }, + G, H, + }, + I, J, +}; + +mod level_1 { + pub mod level_2 { + pub mod level_3 { + pub mod level_4 { + pub mod level_5 { + pub struct A; + pub struct B; + } + pub struct C; + pub struct D; + } + + pub struct E; + pub struct F; + } + + pub struct G; + pub struct H; + } + + pub struct I; + pub struct J; +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/scheme/regular.scm b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/scheme/regular.scm new file mode 100644 index 00000000..09390a08 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/scheme/regular.scm @@ -0,0 +1,22 @@ +(define (add x y) + "A silly way to add two numbers recursively." + (if (zero? y) + x + (add (add1 x) + (sub1 y)))) + +;; R6RS allows square brackets as well +(define [mult x y] + "A silly way of multiplying to numbers recursively" + (if [= 1 y] + x + (add (add x x) + (sub1 y)))) + +(define-syntax foo +  (syntax-rules () +    ((_ a ...) +     (printf "~a\n" (list a ...))))) + +'(((a . b))) +'((((a b . c)))) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/scss/regular.scss b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/scss/regular.scss new file mode 100644 index 00000000..44c13efd --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/scss/regular.scss @@ -0,0 +1,19 @@ +@mixin admon($fg, $sign, $title) { + border-top: 2.25rem solid $fg; + background: color-mix(in srgb, $fg 50%, var(--color-bg)); + + &::before { + position: absolute; + content: $sign; + top: 0.4rem; + left: 0.75rem; + } + + &::after { + position: absolute; + content: $title; + font-weight: bold; + top: 0.5rem; + left: 2.25rem; + } +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/sql/regular.sql b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/sql/regular.sql new file mode 100644 index 00000000..8812dd78 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/sql/regular.sql @@ -0,0 +1,34 @@ +SELECT (1 + (2 + (3 + (4 + (5 + (6 + (7 + (8 + ((9) + (0)))))))))); + +SELECT + (1 + ((2)((())) - 3)) AS "expression", + (()) AS "list", + -- ((((())))) AS "list" -- this will cause problems with the highlighting! + "users"."id" AS "user_id", + SUM("orders"."sum_prices") AS "user_orders_amount" +FROM "users" +JOIN ( + SELECT + "orders"."id", + "orders"."user_id", + SUM("orders"."amount") AS "sum_prices" + FROM "orders" + GROUP BY + "orders"."id", + "orders"."user_id" +) AS "orders" ON "orders"."user_id" = "users"."id" +WHERE "users"."age" = (2 + (3 * 4)) AND (4 - (5 * 0)) = (1 * (2 + 2 + (5))) + AND "users"."id" IN (1, 2, 3, 4) +GROUP BY + "users"."id"; + +SELECT * +FROM products +where ( + Product_Category = 'Fit' + AND Product_number IN (1234, 1235, 1236, 1237, 1238) +) +or + (Product_Category IN ('Tight', 'Wide') AND Product_number = 1324); + +SELECT 10 FROM generate_series(1, 10) WHERE (TRUE); diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/starlark/regular.star b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/starlark/regular.star new file mode 100644 index 00000000..1f004c3c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/starlark/regular.star @@ -0,0 +1,43 @@ +# This is mostly identical to Python, without the generator comprehension +# NOTE: if you update queries for Python, please consider adding the changes +# to this file as well + +def sum_list(lst: List[Dict[int, int]]) -> int: + result = 0 + for inner in lst: + for i in inner: + result += i + return result + + +my_list = [[['Hello, world!']]] +my_dict = {'x': {'x': {'x': 'Hello, wold!'}}} +my_set = {{{{'Hello, wold!'}}}} +my_tuple = (((('Hello, wold!'),),),) +(a,b) = (1,2) + +list_comp = [i for i in [j for j in range(5)] if i % 2 == 0] +dict_comp = {k: v for k, v in {k: v for k, v in {'k': 'v'}.items()} + if k == 'k'} +set_comp = {i for i in {j for j in range(5)} if i % 2 == 0} +gen_comp = (i for i in (j for j in range(5)) if i % 2 == 0) + +match my_dict: + case {'x': {'x': {'x': message}}}: + print(message) + case [[[message]]]: + print(message) + case (((message))): + print(message) + + +zero = [0] + +(a,b) = (1,2) +[c,d] = [3,4] + +print(zero[zero[zero[0]]]) + + +print(2 + ((((3))))) +print(len(my_list)) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/svelte/regular.svelte b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/svelte/regular.svelte new file mode 100644 index 00000000..044a408f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/svelte/regular.svelte @@ -0,0 +1,31 @@ + + + + + + Test page + + + +

A test page for Svelte

+ +
+

This is a paragraph

+
+
    + {#each ["foo", "bar", "baz"] as x} +
  • { x }
  • + {/each} +
+ {# if True} +

Some text

+ {/if} + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/teal/regular.tl b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/teal/regular.tl new file mode 100644 index 00000000..dea3a4d6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/teal/regular.tl @@ -0,0 +1,62 @@ +-- This is a comment + +local function add(x: integer, y: integer): integer + if y == 0 then + return x + end + return add((x + (1)), (y - (1))) +end + +if true then + print 'True condition' +elseif false then + print 'Alternative condition' +elseif false then + print 'Alternative condition' +else + print 'Alternative' +end + +while false do + print 'A while-loop' +end + +repeat + print 'This will repeat only once' +until true + +do + print 'A block' +end + +for i, v in ipairs({'a', 'b', 'c'}) do + print(string.format("%d = %s", i, v)) +end + +for i = 1, 5, 1 do + print(string.format("Number %d", i)) +end + +print(f1('a', 'b')) +print((((('Hello, world!'))))) + +print { + { + { + 'Hello, world!' + } + } +} + +local one = {1} + +print(one[one[one[1]]]) + +-- Embedded Vim script +vim.cmd [[ + echo a(b(c(d(e(f()))))) +]] + +local tbl = { + ["highlight me"] = {} +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/templ/regular.templ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/templ/regular.templ new file mode 100644 index 00000000..597c57f2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/templ/regular.templ @@ -0,0 +1,86 @@ +package main + +import "fmt" +import "time" + +templ headerTemplate(name string) { +
+ switch name { + case "Alice", "Bob": +

{ name }

+ default: +

{ "Unknown" }

+ } + + +
+} + +templ footerTemplate() { +
+
© { fmt.Sprintf("%d", time.Now().Year()) }
+
+} + +templ layout(name string) { + + { name } + + @headerTemplate(name) + @navTemplate() +
+ { children... } +

+ Hello
world! +

+
+ + @footerTemplate() + +} + +templ navTemplate() { +
+} + +templ posts(posts []Post) { + @layout("Posts") { + @postsTemplate(posts) + if len(posts) > 0 { +
{ "Not empty" }
+ } else { +
{ "Empty" }
+ } + } +} + +templ postsTemplate(posts []Post) { +
+ for _, p := range posts { +
+
{ p.Name }
+
{ p.Author }
+
+ } +
+} + +script withParameters(a string, b string, c int) { + console.log(a, b, c); +} + +css red() { + background-color: #ff0000; + font-family: "Iosevka"; +} + +// vim:ft=templ diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/toml/regular.toml b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/toml/regular.toml new file mode 100644 index 00000000..033b16cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/toml/regular.toml @@ -0,0 +1,20 @@ +[table] +[table.sub] +people.names = [ "John", "Joe" ] + +inline-table = { x = 0.1, y = 2 } + +array-of-table = [ + { name = "Alice", level = 2 }, + { name = "Bob", level = 1 }, +] + +[[array-of-table2]] +z = 3 + +[[array-of-table2.t]] +desc = "Index 1" + +[[array-of-table2]] +z = 30 +t.desc = "Index 2" diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/tsx/regular.tsx b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/tsx/regular.tsx new file mode 100644 index 00000000..e0bc15f0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/tsx/regular.tsx @@ -0,0 +1,79 @@ +// Function with nested function +function add(x: number, y: number): number { + function iter(i: number, acc: number) { + if (i == 0) { + return acc; + } + return iter(i - 1, acc + 1); + } + return iter(y, x) +} + +// Function with generic type parameter +function id(x: T): T { + return x; +} + +// Class with members +class Person { + private name: string; + private age: number; + private salary: number; + + constructor(name: string, age: number, salary: number) { + this.name = name; + this.age = age; + this.salary = salary; + } + + toString(): string { + return `${this.name} (${this.age}) (${this.salary})`; // As of version 1.4 + } +} + +// Template strings +const who = 'world'; +console.log(`Hello, ${who}`); + +// Nested object +let some_object = { + a: { + b: { + c: {}, + } + } +}; + +// Subscript expressions +const zeroes = [0]; +console.log(zeroes[zeroes[zeroes[0]]]) + +// Parenthesized expressions +console.log(1 + (2 + (3 + (4 + (5 + 6))))) + + +function hello() { + console.log('Hello, world!'); +} + +function app() { + return ( +
+

+ This is an Example link. +

+

+ This is an Example
link
with
line
break. +

+ + + {someFunction().map((x) =>
)} +
+ + {someFunction().map((x) =>
)} +
+ + +
+ ) +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/typescript/regular.d.ts b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/typescript/regular.d.ts new file mode 100644 index 00000000..dba267f3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/typescript/regular.d.ts @@ -0,0 +1,5 @@ +// Declarations +declare namespace arithmetics { + add(x: number, y: number): number; +} + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/typescript/regular.ts b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/typescript/regular.ts new file mode 100644 index 00000000..c1f27095 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/typescript/regular.ts @@ -0,0 +1,71 @@ +// Function with nested function +function add(x: number, y: number): number { + function iter(i: number, acc: number) { + if (i == 0) { + return accu; + } + return iter(i - 1, acc + 1); + } + return iter(y, x) +} + +// Function with generic type parameter +function id(x: T): T { + return x; +} + +// Class with members +class Person { + private name: string; + private age: number; + private salary: number; + + constructor(name: string, age: number, salary: number) { + this.name = name; + this.age = age; + this.salary = salary; + } + + toString(): string { + return `${this.name} (${this.age}) (${this.salary})`; // As of version 1.4 + } + + async method(): Promise>> { + return [] + } +} + +interface Request { + body: RequestProp['body']; +} + +enum A { + Foo = "Bar", +} + +// Template strings +const who = 'world'; +console.log(`Hello, ${who}`); + +// Nested object +let some_object = { + a: { + b: { + c: {}, + } + } +}; + +// Subscript expressions +const zeroes = [0]; +console.log(zeroes[zeroes[zeroes[0]]]) + +let a = 1 + +switch(a) { + case 1: + break; +} + +// Parenthesized expressions +console.log(1 + (2 + (3 + (4 + (5 + 6))))) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/typst/regular.typ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/typst/regular.typ new file mode 100644 index 00000000..f080e390 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/typst/regular.typ @@ -0,0 +1,29 @@ +#let template(doc) = { + set page(paper: "a4", margin: (x: 2cm, y: 3cm)) + set heading(numbering: "1.1") + set par(justify: true, leading: 0.55em) + + show heading: it => { + set block(above: 1.6em, below: 1em) + it + } + + doc +} + +#show: template + += Typst + +Typst is a markup language for typesetting documents. It is designed to be an +alternative to LaTeX and other document processing tools. + +This is a #[nested section #[of text with #[multiple layers of nesting.]]] + +Maths can either be typeset inline: $A = pi r^2$; or as a separate block: +$ frac(a^(2x), (5x + (3))) $ + +We can also put #[maths inside other content blocks: $V = 4/3 (pi r^3)$]. + + +// vim:ft=typst diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/verilog/systemverilog.sv b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/verilog/systemverilog.sv new file mode 100644 index 00000000..54fbb354 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/verilog/systemverilog.sv @@ -0,0 +1,140 @@ +// Comment +`timescale 1ns/1ns +`default_nettype none +`include "filename.svh" + +typedef enum { + A, + B, + C +} Enum_t; + +module test #( + parameter int PARAM = 1 +) ( + input logic[15:0] a, + input logic[15:0] b, + + output logic[15:0] c +); + logic[15:0][8:0] packed_data; + logic[8:0] to_be_casted; + + divu #( + .WIDTH(24), + .FBITS(15) + ) divider ( + .clk(i_clk), + .rst(div_rst_actual), + .start(div_start), + .valid(o_avg_rdy), + .done(), + .dbz(), + .ovf(), + .busy(div_busy), + .a(div_dividend), + .b(div_divisor), + .val(div_result_q15_9) + ); + + always_comb begin + if (a > b) begin + c = 15'd1; + end else if ((a == b) || (a == c)) begin + c = 15'd2; + to_be_casted = 32'(c); + + if (c == 15'd2) begin + c = 15'd3; + packed_data[2][3] = 8'd4; + end + end + end + + always_ff @(posedge a) begin + c <= ((a + b) + b); + end +endmodule + +// This module implements an unsigned fixed point divider. +// Source: https://projectf.io/posts/division-in-verilog/ +module divu #( + parameter WIDTH=32, // width of numbers in bits (integer and fractional) + parameter FBITS=16 // fractional bits within WIDTH + ) ( + input wire logic clk, // clock + input wire logic rst, // reset + input wire logic start, // start calculation + output logic busy, // calculation in progress + output logic done, // calculation is complete (high for one tick) + output logic valid, // result is valid + output logic dbz, // divide by zero + output logic ovf, // overflow + input wire logic [WIDTH-1:0] a, // dividend (numerator) + input wire logic [WIDTH-1:0] b, // divisor (denominator) + output logic [WIDTH-1:0] val // result value: quotient + ); + + localparam FBITSW = (FBITS == 0) ? 1 : FBITS; // avoid negative vector width when FBITS=0 + + logic [WIDTH-1:0] b1; // copy of divisor + logic [WIDTH-1:0] quo, quo_next; // intermediate quotient + logic [WIDTH:0] acc, acc_next; // accumulator (1 bit wider) + + localparam ITER = WIDTH + FBITS; // iteration count: unsigned input width + fractional bits + logic [$clog2(ITER)-1:0] i; // iteration counter + + // division algorithm iteration + always_comb begin + if (acc >= {1'b0, b1}) begin + acc_next = acc - b1; + {acc_next, quo_next} = {acc_next[WIDTH-1:0], quo, 1'b1}; + end else begin + {acc_next, quo_next} = {acc, quo} << 1; + end + end + + // calculation control + always_ff @(posedge clk) begin + done <= 0; + if (start) begin + valid <= 0; + ovf <= 0; + i <= 0; + if (b == 0) begin // catch divide by zero + busy <= 0; + done <= 1; + dbz <= 1; + end else begin + busy <= 1; + dbz <= 0; + b1 <= b; + {acc, quo} <= {{WIDTH{1'b0}}, a, 1'b0}; // initialize calculation + end + end else if (busy) begin + if (i == ITER-1) begin // done + busy <= 0; + done <= 1; + valid <= 1; + val <= quo_next; + end else if (i == WIDTH-1 && quo_next[WIDTH-1:WIDTH-FBITSW] != 0) begin // overflow? + busy <= 0; + done <= 1; + ovf <= 1; + val <= 0; + end else begin // next iteration + i <= i + 1; + acc <= acc_next; + quo <= quo_next; + end + end + if (rst) begin + busy <= 0; + done <= 0; + valid <= 0; + dbz <= 0; + ovf <= 0; + val <= 0; + end + end +endmodule diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/verilog/verilog.v b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/verilog/verilog.v new file mode 100644 index 00000000..bc956ecc --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/verilog/verilog.v @@ -0,0 +1,52 @@ +// Comment +`timescale 1ns/1ns +`default_nettype none +`include "filename.vh" + +module test #( + parameter PARAM = 1 +) ( + input reg[15:0] a, + input reg[15:0] b, + + output reg[15:0] c +); + logic[15:0][8:0] packed_data; + + always @* begin + if (a > b) begin + c = 15'd1; + end else if ((a == b) || (a == c)) begin + c = 15'd2; + + if (c == 15'd2) begin + c = 15'd3; + packed_data[2][3] = 8'd4; + end + end + end +endmodule + +module test2 #( + parameter PARAM = 1 +) ( + input reg[15:0] a, + input reg[15:0] b, + + output reg[15:0] c +); + logic[15:0][8:0] packed_data; + + always @* begin + if (a > b) begin + c = 15'd1; + end else if ((a == b) || (a == c)) begin + c = 15'd2; + + if (c == 15'd2) begin + c = 15'd3; + packed_data[2][3] = 8'd4; + end + end + end +endmodule diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vim/regular.vim b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vim/regular.vim new file mode 100644 index 00000000..d50e3e31 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vim/regular.vim @@ -0,0 +1,14 @@ +let g:my_list = [[[1]]] +let g:my_dict = { + \'a': { + \'b': { + \'c': {}, + \} + \} +\ } + +echo string(1 + (2 + (3 + 4))) +echo string(-(3)) +echo string((5)-(3)) +echo string((1) ? (2) : (3)) +echo ((('Hello, world!'))) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/pug-template.vue b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/pug-template.vue new file mode 100644 index 00000000..5e731d29 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/pug-template.vue @@ -0,0 +1,10 @@ + + + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/regular.vue b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/regular.vue new file mode 100644 index 00000000..8d06c940 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/regular.vue @@ -0,0 +1,39 @@ + + + + + + + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/scss-style.vue b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/scss-style.vue new file mode 100644 index 00000000..d0d8cfd1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/scss-style.vue @@ -0,0 +1,18 @@ + + + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/typescript-script.vue b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/typescript-script.vue new file mode 100644 index 00000000..6906d9a8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/vue/typescript-script.vue @@ -0,0 +1,14 @@ + + + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/xml/regular.xml b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/xml/regular.xml new file mode 100644 index 00000000..f907a900 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/xml/regular.xml @@ -0,0 +1,24 @@ + + + + + Example of MathML embedded in an XHTML file + + + +

Example of MathML embedded in an XHTML file

+

+ The area of a circle is + + π + + + r + 2 + + + . +

+ + diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/yaml/regular.yaml b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/yaml/regular.yaml new file mode 100644 index 00000000..e4ef650e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/yaml/regular.yaml @@ -0,0 +1,25 @@ +json_compatibility: + { + "foo": "bar", + "bar": { + "baz": [ + [ + [[['Hello, world!']]] + ] + ] + }, + "key": { + "key": { + "key": { + "key": { + "key": "value" + } + } + } + } + } + +list_of_objects: + - { key1: value, key2: value, key3: value } + - { key1: value, key2: value, key3: value } # A comment + - { key1: { key2: { key3: value, key4: value, key5: value } } } # Nested map diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/zig/regular.zig b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/zig/regular.zig new file mode 100644 index 00000000..361ba54b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/highlight/zig/regular.zig @@ -0,0 +1,86 @@ +const std = @import("std"); + +const Void = struct {}; + +const MyBool = enum { + my_true, + my_false, +}; + +const SomeUnion = union(enum) { + top: u0, + kek: u1, +}; + +comptime { + const a: anyerror!SomeUnion = SomeUnion{.top = 0}; + + const b = switch (a catch |err| err) { + SomeUnion.top => |val| val, + SomeUnion.kek => |val| val, + else => undefined, + }; + + _ = b; +} + +pub fn main() !void { + const some_type: type = *[][:8][*][*:.{ .mqu = false }]u123; + _ = some_type; + + const stoqn = [_]u8{ 'k', 'o', 'l', 'e', 'v' }; + + std.debug.print("My last {s} is {s} and it's first letter is {s}\n", .{ "name", stoqn, [_]u8{ stoqn[0] } }); + + // stdout is for the actual output of your application, for example if you + // are implementing gzip, then only the compressed bytes should be sent to + // stdout, not any debugging messages. + const stdout_file = blk: { + break :blk std.io.getStdOut().writer(); + }; + var bw = std.io.bufferedWriter(stdout_file); + const stdout = bw.writer(); + + switch (6) { + 5 => undefined, + _ => { + try stdout.print("Run `zig build test` to run the tests.\n", .{}); + }, + } + + if (false) { + const k = undefined; + const a = undefined; + { + asm volatile ("" + : [_] "=r,m" (k) + : [_] "r,m" (a) + : "" + ); + } + } else if (true) { + for ("proba", 0..) |c, i| { + _ = c; + _ = i; + } + } else { + while (false) { + // ... + } + } + + try bw.flush(); // don't forget to flush! +} + +const truth linksection("lambda") = "calculus"; + +fn foo(a: *opaque {}) callconv(.C) void { + _ = a; +} + +test "simple test" { + var list = std.ArrayList(i32).init(std.testing.allocator); + defer list.deinit(); // try commenting this out and see if zig detects the memory leak! + try list.append(42); + try std.testing.expectEqual(@as(i32, 42), list.pop()); +} diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/nvim-shim b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/nvim-shim new file mode 100755 index 00000000..ce9fbaad --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/nvim-shim @@ -0,0 +1,71 @@ +#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/sh +# SPDX-License-Identifier: Unlicense + +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or distribute +# this software, either in source code form or as a compiled binary, for any +# purpose, commercial or non-commercial, and by any means. +# +# In jurisdictions that recognize copyright laws, the author or authors of +# this software dedicate any and all copyright interest in the software to +# the public domain. We make this dedication for the benefit of the public +# at large and to the detriment of our heirs and successors. We intend this +# dedication to be an overt act of relinquishment in perpetuity of all +# present and future rights to this software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to + + +# A shim which acts as a command-line interface adapter to use Neovim as a Lua +# interpreter. + +# Set custom XDG base directory paths to isolate the test Neovim from the +# user's own configuration and data files. +export XDG_CONFIG_HOME='test/xdg/config/' +export XDG_STATE_HOME='test/xdg/local/state/' +export XDG_DATA_HOME='test/xdg/local/share/' + +# Handle Lua command-line arguments; not all options are supported +while getopts 'ilEve:' opt; do + case $opt in + e) lua_expr=$OPTARG;; + v) nvim --version; exit;; + i | l | E) echo "Option '$opt' not supported by shim"; exit 1;; + esac +done + + +# We need to add this plugin to the custom configuration. The easiest way is +# to create a symlink. Why not always have a symlink in the project? The Lua +# language server will search for Lua files in every directory, so if it enters +# the symlink it will be trapped in a cycle. What we do instead is create the +# symlink only for the duration of a test session and remove it again +# afterwards. + +# We need separate symlinks if we want to run different tasks in parallel. +# Otherwise the one the finishes first would delete the symlink from underneath +# the one that is still running. +uuid=$(uuidgen) +mkdir -p ${XDG_DATA_HOME}/nvim/site/pack/self-${uuid}/start/ +ln -fs $(pwd) ${XDG_DATA_HOME}/nvim/site/pack/self-${uuid}/start/ + +if [ -n "$lua_expr" ]; then + nvim --headless -c "lua $lua_expr" -c 'quitall!' +else + # We have to explicitly enable plugins, see ':h -l' + nvim --cmd 'set loadplugins' -l $@ +fi + +exit_code=$? + +rm -rf ${XDG_DATA_HOME}/nvim/site/pack/self-${uuid}/ + +exit $exit_code diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/stress/markdown/lorem-ipsum.md b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/stress/markdown/lorem-ipsum.md new file mode 100644 index 00000000..416fb14b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/stress/markdown/lorem-ipsum.md @@ -0,0 +1,177 @@ +# Lorem Ipsum stress test + +This is a large Markdown file with other languages injected. + +```lua +print 'This is an injected language' +print({{{{{{{}}}}}}}) +``` + +```markdown +Injected markdown. +~~~lua +print 'Injected Lua' +print({{{{}}}}) +vim.cmd[[echo str([])]] +vim.cmd[[ +echo 'Injected vim in injected Lua' +echo str([]) +]] +~~~ +More injected markdown. +``` + +## Cupit Phoce sonus + +Lorem markdownum acernas. **Ignis ore amplius** dixit, supremumque miserere +vinoque peti tendit plebe ergo aguntur gerere expulsa, post loca sinebat. Nostra +minor comas saevit; quos rerum de ipsa, est bis [semine](http://caede.com/) +Saturnia saepius flet, sim. Antro remis patet genitam pariterque ipsum dum socia +vicit, tu nocens sororum? Hippomenes momentaque solusque Minyis +[Deucalion](http://anus.org/nec-tellus) aevo poena, voces instant consorte +fraternos occiderat tangit posita solo vellera me sanguis! + +```vim +echo 'This is an injected language' +echo str([[[[[[[[[]]]]]]]]]) +``` + +Fata quae: **falsi**: pares dea [agmine +hospite](http://www.quo-retro.net/sublimis) catenas? [Morte](http://corpus.net/) +ad putes. Dicentem Chariclo vidit! + +```c +puts("This is an injected language") +{ + { + { + { + { + return ((((((2)))))) + ((((3)))) + } + } + } + } +} +``` + +```c +puts("This is a second c code block") +``` + +```c +puts("This is a third c code block") +``` + +> Delius irascere cuncti Argolis, femineis **ubi est** relatis Tityos supple +> alebat excusat animalia. Qua licet gentem laniaverat vidit Chromin: ut Thracum +> Lacedaemoniumque parantur tribuitque. Nec Acoete rogat satis gramine: mollibus +> regis, sermonibus deus, lumina at. + +```python +print('This is an injected language') +print([[[[[[[[]]]]]]]]) +``` + +## Silentia foret + +Requievit eventuque sociis ordine ebur referam iussam accessit temperie in +fugant nymphas te ramos puellas, per. Posse per ait pressa dammas. Quam proximus +scopulum sonanti accensus ab *auras* nostra ambo forte medicina repulsa et quae +occuluere et. + +```bash +echo 'This is an injected language' +echo $(echo $(echo $(echo $(echo 'test')))) +```` + +Aliquam sem et tortor consequat id porta. Lectus urna duis convallis convallis +tellus id interdum velit. Diam volutpat commodo sed egestas egestas fringilla +phasellus. Amet commodo nulla facilisi nullam vehicula ipsum a arcu cursus. Ac +turpis egestas maecenas pharetra convallis posuere morbi. Facilisis volutpat +est velit egestas dui id ornare arcu. Lacus sed turpis tincidunt id aliquet. Et +netus et malesuada fames ac turpis egestas sed tempus. Cursus in hac habitasse +platea. Lectus proin nibh nisl condimentum id. + + +```json +[[[[[[[[["This is an injected language"]]]]]]]]] +``` + +Pictos excipiunt saxea. Equorum esse fac iactatis resolvit fumantis tota faveas +ortu imago insanis. Nil Typhoea ramis timido teneris nunc septem, vale furor. + +```html +
+
+
+
+
+

This is an injected language

+
+
+
+
+
+``` + +1. Cadmeides condi Pelagonaque manibus petit moderator sua +2. Et absumere dextera rediit +3. Suis priorum dixit flendo +4. Sic contingit mihi +5. Nec tenet adsiduis tua vidit invitaque caeli + +```css +@media (not (color)) { + .foo { + color: #ffffff; + } +} +``` + +Sternentem raptamque meam requiret retentis et natum et, quoque, at tamen +peccasse *manet*. Tamen cuius, increpat solvunt meritum tibi veniente +semihomines ligo corpus de arma fata! + +```javascript +console.log('This is an injected language') +console.log(1 + (2 + (3 + (4 + (5 + (6 + (7 + (8 + 9)))))))) +``` + +## Aliquam sub nulla tandem movere oscula pulcherrima + +In Cybeleia mearum illos est, superest viscera fugienti fabula Thisbes agros. +Cum nepotem quae aurum qua abest harundine, caput et noverca! + +Caelum fore domus nox noxque bello Tempe erat ruent perstant te prope. Bis illis +ea maritum, pro est ferrum mihi ictu Cyllenius. Rogat ad et sed taedae Iphis, et +ad sinus afueram? Huc umbras: **asper iuro postquam**: ordine dum qualia diro +equorum nisi atque! + + ntfs_party_memory.graphicPortalMask(drive_tween, infringementCompression); + brouterHard = thinFios; + arp.pppEpsUnmount -= refresh.wavelength(compression(wiki, 5), serverIpv) + + room.paper(kindleLeopardBarcraft, gigo) + serial; + +Et opem primitias sine agrestes, me puto illo cum puer. Loca aptabat sed munera +crede, quem **veris volubilibus maximus** Desierat +[lanificae](http://natacognatas.net/ut-plus.php) inponit *pars* paratas est +anser. Tremoribus iste [furit](http://ira.net/)! Alis potiere praedelassat +increvit Cytherea, mediis in modo mitissima ad feto amavit. [Moenia +illo](http://www.estygius.net/nisi.php) ille, ars ramis alis tamen mihi iste +Naupliades et diu molles ultro agendo ire nullo. + +> Vicimus locuturo, quod [pars](http://www.idomeneus.org/unus) iam hunc +> lacusque, toto. Partes **per** nec latuisse nullus isto dolore iungimus nervi. +> Illic vult cucurri, capit digitis longus nymphaeque plagis viribus virgae, +> ascendere sederunt urbs, aspergine trunca. Frigus [Leucothoen +> adsiduae](http://non.org/) duos; quae dari est Amorque domuisse proles +> quaerenti **haec**. + +Ac rebus merentem, portas, eandem dea versos [laborum ab +posse](http://lignumsuccessibus.io/postquam) in mirata ad. Chirona terras; +aether morer proelia accedere, praesagaque avido clavae vestra, tamen. Vocas +Quirini sanguine insignia, aemula mea nomen [credere](http://pennas.io/); non +possit primoque bisque. Omni dea vultus, huc prodierat auras de per vel despice +retro aut. Aut generum timore, nam succedit, in sui Hector ramos loquuntur, +naias, traiecit. diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/unit/stack_spec.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/unit/stack_spec.lua new file mode 100644 index 00000000..1484bcc4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/unit/stack_spec.lua @@ -0,0 +1,85 @@ +local Stack = require 'rainbow-delimiters.stack' + +describe('The stack data structure #stack', function() + describe('The empty stack', function() + local stack + + before_each(function() stack = Stack.new() end) + + it('Can instantiate an empty stack', function() + assert.is_not._nil(stack) + end) + + it('Is empty', function() + assert.is.equal(0, stack:size()) + end) + + it('Can push items onto the stack', function () + stack:push('a') + stack:push('b') + assert.is.equal(2, stack:size()) + end) + end) + + describe('Stack with contents', function() + local stack, items + + before_each(function() + items = {'a', 'b', 'c', 'd'} + stack = Stack.new(items) + end) + + it('Can instantiate stack with contents', function() + assert.is_not._nil(stack) + end) + + it('Holds the correct amount of items', function() + assert.is.equal(4, stack:size()) + end) + + it('Can inspect the topmost element', function () + local top = stack:peek() + assert.is.equal('d', top) + end) + + it('Can pop items off the stack in reverse order', function() + for i = 3, 0, -1 do + local val = stack:pop() + assert.is.equal(items[i + 1], val) + assert.is.equal(i, stack:size()) + end + end) + + it('Can push an item onto the stack', function() + local val = 'e' + stack:push(val) + assert.is.equal(5, stack:size()) + assert.is.equal(val, stack:pop()) + end) + end) + + describe('Stack traversal', function() + it('Traverses the stack from top to bottom', function() + local counter = 1 + local expected_indices = {4, 3, 2, 1} + local expected_values = {'d', 'c', 'b', 'a'} + + local stack = Stack.new {'a', 'b', 'c', 'd'} + for i, v in stack:iter() do + local index = expected_indices[counter] + local value = expected_values[ counter] + assert.is.equal(index, i) + assert.is.equal(value, v) + counter = counter + 1 + end + end) + + it('Does nothing for an empty stack', function() + local stack = Stack.new() + for _i, _v in stack:iter() do + -- This must never run because the stack is empty + assert.is_true(false) + end + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/README.rst b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/README.rst new file mode 100644 index 00000000..cf8d81a9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/README.rst @@ -0,0 +1,6 @@ +.. default-role:: code + + +This directory exists so that we can point Neovim to its sub-directories +instead of the default XDG base directories. This isolates the Neovim instance +used during testing from the user's own configuration and data. diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/config/nvim/init.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/config/nvim/init.lua new file mode 100644 index 00000000..ebe75940 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/config/nvim/init.lua @@ -0,0 +1,11 @@ +-- Tree-sitter highlighting needs to be running, otherwise rainbow highlighting +-- won't get updated on tree changes. The following autocommand enables it on +-- every file type change. + + +local function on_file_type(_args) + vim.treesitter.start() +end + + +vim.api.nvim_create_autocmd('FileType', {pattern = '*', callback = on_file_type}) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/config/nvim/plugin/busted.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/config/nvim/plugin/busted.lua new file mode 100644 index 00000000..78d6f34d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/config/nvim/plugin/busted.lua @@ -0,0 +1,35 @@ +-- Custom configuration for Busted + +local say = require 'say' +local assert = require 'luassert' +local filter = vim.fn.filter + + +local NVIM_STATE_KEY = {} + +local function nvim(state, args, level) + assert(args.n > 0, "No Neovim channel provided to the modifier") + assert(rawget(state, NVIM_STATE_KEY) == nil, "Neovim already set") + rawset(state, NVIM_STATE_KEY, args[1]) + return state +end + +---Asserts that there are Rainbow Delimiters extmarks at the given position +---@param arguments integer[] Row and column, both zero-based +local function has_extmarks_at(_state, arguments, lang) + local nvim = rawget(_state, NVIM_STATE_KEY) + assert(nvim ~= nil, 'No Neovim channel set, use the nvim modifier to set the channel') + local row, column = arguments[1], arguments[2] + local nsid = nvim:exec_lua('return require("rainbow-delimiters.lib").nsids[...]', {lang}) + local extmarks = nvim:exec_lua('return vim.inspect_pos(...).extmarks', {0, row, column}) + filter(extmarks, function(_, v) return v.ns_id == nsid end) + return #extmarks > 0 +end + +say:set('assertion.extmarks_at.positive', 'Expected extmarks at (%s, %s)') +say:set('assertion.extmarks_at.negative', 'Expected no extmarks at (%s, %s)') + +assert:register( + 'assertion', 'has_extmarks_at', has_extmarks_at, + 'assertion.has_extmarks_at.positive', 'assertion.has_extmarks_at.negative') +assert:register('modifier', 'nvim', nvim) diff --git a/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/config/nvim/plugin/ts-ensure.lua b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/config/nvim/plugin/ts-ensure.lua new file mode 100644 index 00000000..8bd783e8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rainbow-delimiters.nvim/test/xdg/config/nvim/plugin/ts-ensure.lua @@ -0,0 +1,14 @@ +local get_runtime_file = vim.api.nvim_get_runtime_file +local parser_pattern = 'parser/%s.*' + +---Wrapper around the `:TSinstall` command which will only install a parser if +---it is not installed yet +---@param lang string Language to install +function TSEnsure(lang, ...) + for _, l in ipairs({lang, ...}) do + local parsers = get_runtime_file(parser_pattern:format(l), true) + if #parsers == 0 then + vim.cmd {cmd = 'TSInstallSync', args = {l}} + end + end +end diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/doc/mason.txt b/config/neovim/store/lazy-plugins/rustaceanvim/doc/mason.txt new file mode 100644 index 00000000..a30f1510 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/doc/mason.txt @@ -0,0 +1,54 @@ +============================================================================== +mason-lspconfig troubleshooting *rustaceanvim.mason* + +This plugin supports automatically detecting mason.nvim codelldb installations, +but not rust-analyzer. +The main reason for this choice is that it mason.nvim installations of rust-analyzer +will most likely have been built with a different toolchain than your project, +leading to inconsistencies and possibly subtle bugs. + +If you want to use a mason.nvim installation anyway, you can do so by specifying +the `server.cmd` setting (see |rustaceanvim.config| and |RustaceanLspClientOpts|): +>lua + vim.g.rustaceanvim = { + server = { + cmd = function() + local mason_registry = require('mason-registry') + local ra_binary = mason_registry.is_installed('rust-analyzer') + -- This may need to be tweaked, depending on the operating system. + and mason_registry.get_package('rust-analyzer'):get_install_path() .. "/rust-analyzer" + or "rust-analyzer" + return { ra_binary } -- You can add args to the list, such as '--log-file' + end, + }, + } +< +Note that mason-lspconfig.nvim, when configured to ensure rust-analyzer is installed, +assumes you are using the `nvim-lspconfig.rust_analyzer` client. +Some Neovim distributions will automatically call the client's `setup` +function, resulting in a conflict with this plugin. + +General approach to prevent mason-lspconfig from setting up +`lspconfig.rust_analyzer`: + +>lua + require('mason-lspconfig').setup_handlers { + ['rust_analyzer'] = function() end, + } +< + +Using LazyVim: + +>lua + { + 'neovim/nvim-lspconfig', + opts = { + setup = { + rust_analyzer = function() + return true + end, + }, + }, + } +< +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/doc/rustaceanvim.txt b/config/neovim/store/lazy-plugins/rustaceanvim/doc/rustaceanvim.txt new file mode 100644 index 00000000..9da43f53 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/doc/rustaceanvim.txt @@ -0,0 +1,409 @@ +============================================================================== +Table of Contents *rustaceanvim.contents* + +Introduction ··························································· |intro| + ································································ |rustaceanvim| +plugin configuration ····································· |rustaceanvim.config| +LSP configuration utility ························· |rustaceanvim.config.server| + ························································ |rustaceanvim.neotest| + +============================================================================== +Introduction *intro* + +This plugin automatically configures the `rust-analyzer` builtin LSP client +and integrates with other rust tools. + +============================================================================== + *rustaceanvim* + + +Commands: + + ':RustAnalyzer start' - Start the LSP client. + ':RustAnalyzer stop' - Stop the LSP client. + ':RustAnalyzer restart' - Restart the LSP client. + ':RustAnalyzer reloadSettings' - Reload settings for the LSP client. + +The ':RustLsp[!]' command is available after the LSP client has initialized. +It accepts the following subcommands: + + 'runnables {args[]}?' - Run tests, executables, etc. + ':RustLsp!' means run the last runnable (ignores any args). + `args[]` allows you to override the executable's arguments. + 'run {args[]}?' - Like 'runnables', but runs the target at the current cursor position. + 'debuggables {args[]}?' - Debug tests, executables, etc. (requires |nvim-dap|). + ':RustLsp!' means run the last debuggable (ignores any args). + `args[]` allows you to override the executable's arguments. + 'debug {args[]}?' - Like 'debuggables', but debugs the target at the current cursor position. + 'testables {args[]}?' - Run tests + ':RustLsp!' means run the last testable (ignores any args). + `args[]` allows you to override the executable's arguments. + 'expandMacro' - Expand macros recursively. + 'moveItem {up|down}' - Move items up or down. + 'hover {actions|range}' - Hover actions, or hover over visually selected range. + 'explainError' - Display a hover window with explanations form the Rust error index. + Like |vim.diagnostic.goto_next|, |explainError| will cycle diagnostics, + starting at the cursor position, until it can find a diagnostic with + an error code. + 'renderDiagnostic' - Display a hover window with the rendered diagnostic, + as displayed during `cargo build`. + Like |vim.diagnostic.goto_next|, |renderDiagnostic| will cycle diagnostics, + starting at the cursor position, until it can find a diagnostic with + rendered data. + 'openCargo' - Open the Cargo.toml file for the current package. + 'openDocs' - Open docs.rs documentation for the symbol under the cursor. + 'parentModule' - Open the current module's parent module. + 'workspaceSymbol {onlyTypes?|allSymbols?} {query?}' + Filtered workspace symbol search. + When run with a bang (`:RustLsp! workspaceSymbol ...`), + rust-analyzer will include dependencies in the search. + You can also configure rust-analyzer so that |vim.lsp.buf.workspace_symbol| + supports filtering (with a # suffix to the query) or searching dependencies. + 'joinLines' - Join adjacent lines. + 'ssr {query}' - Structural search and replace. + Searches the entire buffer in normal mode. + Searches the selected region in visual mode. + 'crateGraph {backend}' - Create and view a crate graph with graphviz. + 'syntaxTree' - View the syntax tree. + 'view {mir|hir}' - View MIR or HIR. + 'flyCheck' {run?|clear?|cancel?} + - Run `cargo check` or another compatible command (f.x. `clippy`) + in a background thread and provide LSP diagnostics based on + the output of the command. + Useful in large projects where running `cargo check` on each save + can be costly. + Defaults to `flyCheck run` if called without an argument. + 'logFile' - Open the rust-analyzer log file. + +The ':Rustc' command can be used to interact with rustc. +It accepts the following subcommands: + + 'unpretty {args[]}' - Opens a buffer with a textual representation of the MIR or others things, + of the function closest to the cursor. + Achieves an experience similar to Rust Playground. + NOTE: This currently requires a tree-sitter parser for Rust, + and a nightly compiler toolchain. + +============================================================================== +plugin configuration *rustaceanvim.config* + + +rustaceanvim is a filetype plugin, and does not need +a `setup` function to work. + +To configure rustaceanvim, set the variable `vim.g.rustaceanvim`, +which is a `RustaceanOpts` table, in your neovim configuration. + +Example: + +>lua +---@type RustaceanOpts +vim.g.rustaceanvim = { + ---@type RustaceanToolsOpts + tools = { + -- ... + }, + ---@type RustaceanLspClientOpts + server = { + on_attach = function(client, bufnr) + -- Set keybindings, etc. here. + end, + default_settings = { + -- rust-analyzer language server configuration + ['rust-analyzer'] = { + }, + }, + -- ... + }, + ---@type RustaceanDapOpts + dap = { + -- ... + }, + } +< + +Notes: + + - `vim.g.rustaceanvim` can also be a function that returns a `RustaceanOpts` table. + - `server.settings`, by default, is a function that looks for a `rust-analyzer.json` file + in the project root, to load settings from it. It falls back to an empty table. + + +RustaceanOpts *RustaceanOpts* + + Fields: ~ + {tools?} (RustaceanToolsOpts) Plugin options + {server?} (RustaceanLspClientOpts) Language server client options + {dap?} (RustaceanDapOpts) Debug adapter options + + +RustaceanToolsOpts *RustaceanToolsOpts* + + Fields: ~ + {executor?} (RustaceanExecutor|executor_alias) The executor to use for runnables/debuggables + {test_executor?} (RustaceanExecutor|test_executor_alias) The executor to use for runnables that are tests / testables + {crate_test_executor?} (RustaceanExecutor|test_executor_alias) The executor to use for runnables that are crate test suites (--all-targets) + {cargo_override?} (string) Set this to override the 'cargo' command for runnables, debuggables (etc., e.g. to 'cross'). If set, this takes precedence over 'enable_nextest'. + {enable_nextest?} (boolean) Whether to enable nextest. If enabled, `cargo test` commands will be transformed to `cargo nextest run` commands. Defaults to `true` if cargo-nextest is detected. Ignored if `cargo_override` is set. + {enable_clippy?} (boolean) Whether to enable clippy checks on save if a clippy installation is detected. Default: `true` + {on_initialized?} (fun(health:RustAnalyzerInitializedStatus)) Function that is invoked when the LSP server has finished initializing + {reload_workspace_from_cargo_toml?} (boolean) Automatically call `RustReloadWorkspace` when writing to a Cargo.toml file + {hover_actions?} (RustaceanHoverActionsOpts) Options for hover actions + {code_actions?} (RustaceanCodeActionOpts) Options for code actions + {float_win_config?} (FloatWinConfig) Options applied to floating windows. See |api-win_config|. + {create_graph?} (RustaceanCrateGraphConfig) Options for showing the crate graph based on graphviz and the dot + {open_url?} (fun(url:string):nil) If set, overrides how to open URLs + {rustc?} (RustcOpts) Options for `rustc` + + +RustaceanExecutor *RustaceanExecutor* + + Fields: ~ + {execute_command} (fun(cmd:string,args:string[],cwd:string|nil,opts?:RustaceanExecutorOpts)) + + +RustaceanExecutorOpts *RustaceanExecutorOpts* + + Fields: ~ + {bufnr?} (integer) The buffer from which the executor was invoked. + + +FloatWinConfig *FloatWinConfig* + + Fields: ~ + {auto_focus?} (boolean) + {open_split?} ("horizontal"|"vertical") + + See: ~ + |vim.lsp.util.open_floating_preview.Opts| + |vim.api.nvim_open_win| + + +executor_alias *executor_alias* + + Type: ~ + "termopen"|"quickfix"|"toggleterm"|"vimux"|"neotest" + + +test_executor_alias *test_executor_alias* + + Type: ~ + executor_alias|"background" + + +RustaceanHoverActionsOpts *RustaceanHoverActionsOpts* + + Fields: ~ + {replace_builtin_hover?} (boolean) Whether to replace Neovim's built-in `vim.lsp.buf.hover` with hover actions. Default: `true` + + +RustaceanCodeActionOpts *RustaceanCodeActionOpts* + + Fields: ~ + {group_icon?} (string) Text appended to a group action + {ui_select_fallback?} (boolean) Whether to fall back to `vim.ui.select` if there are no grouped code actions. Default: `false` + + +lsp_server_health_status *lsp_server_health_status* + + Type: ~ + "ok"|"warning"|"error" + + +RustAnalyzerInitializedStatus *RustAnalyzerInitializedStatus* + + Fields: ~ + {health} (lsp_server_health_status) + + +RustaceanCrateGraphConfig *RustaceanCrateGraphConfig* + + Fields: ~ + {backend?} (string) Backend used for displaying the graph. See: https://graphviz.org/docs/outputs/ Defaults to `"x11"` if unset. + {output?} (string) Where to store the output. No output if unset. Relative path from `cwd`. + {enabled_graphviz_backends?} (string[]) Override the enabled graphviz backends list, used for input validation and autocompletion. + {pipe?} (string) Overide the pipe symbol in the shell command. Useful if using a shell that is not supported by this plugin. + + +RustcOpts *RustcOpts* + + Fields: ~ + {edition} (string) The edition to use. See https://rustc-dev-guide.rust-lang.org/guides/editions.html. Default '2021'. + + +RustaceanLspClientOpts *RustaceanLspClientOpts* + + Fields: ~ + {auto_attach?} (boolean|fun(bufnr:integer):boolean) Whether to automatically attach the LSP client. Defaults to `true` if the `rust-analyzer` executable is found. + {cmd?} (string[]|fun():string[]) Command and arguments for starting rust-analyzer + {settings?} (table|fun(project_root:string|nil,default_settings:table):table) Setting passed to rust-analyzer. Defaults to a function that looks for a `rust-analyzer.json` file or returns an empty table. See https://rust-analyzer.github.io/manual.html#configuration. + {standalone?} (boolean) Standalone file support (enabled by default). Disabling it may improve rust-analyzer's startup time. + {logfile?} (string) The path to the rust-analyzer log file. + {load_vscode_settings?} (boolean) Whether to search (upward from the buffer) for rust-analyzer settings in .vscode/settings json. If found, loaded settings will override configured options. Default: false + + +RustaceanDapOpts *RustaceanDapOpts* + + Fields: ~ + {adapter?} (DapExecutableConfig|DapServerConfig|disable|fun():DapExecutableConfig|DapServerConfig|disable) @field autoload_configurations boolean Whether to autoload nvim-dap configurations when rust-analyzer has attached? Default: `true`. + + +disable *disable* + + Type: ~ + false + + +DapCommand *DapCommand* + + Type: ~ + string + + +DapExecutableConfig *DapExecutableConfig* + + Fields: ~ + {type} (dap_adapter_type_executable) The type of debug adapter. + {command} (string) Default: `"lldb-vscode"`. + {args?} (string) Default: unset. + {name?} (string) Default: `"lldb"`. + + +DapServerConfig *DapServerConfig* + + Fields: ~ + {type} (dap_adapter_type_server) The type of debug adapter. + {host?} (string) The host to connect to. + {port} (string) The port to connect to. + {executable} (DapExecutable) The executable to run + {name?} (string) + + +DapExecutable *DapExecutable* + + Fields: ~ + {command} (string) The executable. + {args} (string[]) Its arguments. + + +dap_adapter_type_executable *dap_adapter_type_executable* + + Type: ~ + + + +dap_adapter_type_server *dap_adapter_type_server* + + Type: ~ + + + +DapClientConfig : Configuration *DapClientConfig* + + Fields: ~ + {type} (string) The dap adapter to use + {name} (string) + {request} (dap_config_request_launch|dap_config_request_attach|dap_config_request_custom) The type of dap session + {cwd?} (string) Current working directory + {program?} (string) Path to executable for most DAP clients + {args?} (string[]) Optional args to DAP client, not valid for all client types + {env?} (EnvironmentMap) Environmental variables + {initCommands?} (string[]) Initial commands to run, `lldb` clients only + {coreConfigs?} (table) Essential config values for `probe-rs` client, see https://probe.rs/docs/tools/debugger/ + + +EnvironmentMap *EnvironmentMap* + + Type: ~ + table + + +dap_config_request_launch *dap_config_request_launch* + + Type: ~ + + + +dap_config_request_attach *dap_config_request_attach* + + Type: ~ + + + +dap_config_request_custom *dap_config_request_custom* + + Type: ~ + + + + *M.get_codelldb_adapter* +M.get_codelldb_adapter({codelldb_path}, {liblldb_path}) + For the heroes who want to use it. + + Parameters: ~ + {codelldb_path} (string) Path to the codelldb executable + {liblldb_path} (string) Path to the liblldb dynamic library + + Returns: ~ + (DapServerConfig) + + +============================================================================== +LSP configuration utility *rustaceanvim.config.server* + +LoadRASettingsOpts *LoadRASettingsOpts* + + Fields: ~ + {settings_file_pattern} (string|nil) File name or pattern to search for. Defaults to 'rust-analyzer.json' + {default_settings} (table|nil) Default settings to merge the loaded settings into + + + *server.load_rust_analyzer_settings* +server.load_rust_analyzer_settings({project_root}, {opts}) + Load rust-analyzer settings from a JSON file, + falling back to the default settings if none is found or if it cannot be decoded. + + Parameters: ~ + {project_root} (string|nil) The project root + {opts} (LoadRASettingsOpts|nil) + + Returns: ~ + (table) server_settings + + See: ~ + |https://rust-analyzer.github.io/manual.html#configuration| + + +server.create_client_capabilities() *server.create_client_capabilities* + + Returns: ~ + (lsp.ClientCapabilities) + + +============================================================================== + *rustaceanvim.neotest* + + +A |neotest| adapter for rust, powered by rustaceanvim. + +If you add this to neotest: + +> +require('neotest').setup { + -- ..., + adapters = { + -- ..., + require('rustaceanvim.neotest') + }, +} +< + +this plugin will configure itself to use |neotest| +as a test executor, and |neotest| will use rust-analyzer +for test discovery and command construction. + +Note: If you use this adapter, do not add the neotest-rust adapter +(another plugin). + + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/doc/tags b/config/neovim/store/lazy-plugins/rustaceanvim/doc/tags new file mode 100644 index 00000000..2a7bfc5e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/doc/tags @@ -0,0 +1,38 @@ +DapClientConfig rustaceanvim.txt /*DapClientConfig* +DapCommand rustaceanvim.txt /*DapCommand* +DapExecutable rustaceanvim.txt /*DapExecutable* +DapExecutableConfig rustaceanvim.txt /*DapExecutableConfig* +DapServerConfig rustaceanvim.txt /*DapServerConfig* +EnvironmentMap rustaceanvim.txt /*EnvironmentMap* +FloatWinConfig rustaceanvim.txt /*FloatWinConfig* +LoadRASettingsOpts rustaceanvim.txt /*LoadRASettingsOpts* +M.get_codelldb_adapter rustaceanvim.txt /*M.get_codelldb_adapter* +RustAnalyzerInitializedStatus rustaceanvim.txt /*RustAnalyzerInitializedStatus* +RustaceanCodeActionOpts rustaceanvim.txt /*RustaceanCodeActionOpts* +RustaceanCrateGraphConfig rustaceanvim.txt /*RustaceanCrateGraphConfig* +RustaceanDapOpts rustaceanvim.txt /*RustaceanDapOpts* +RustaceanExecutor rustaceanvim.txt /*RustaceanExecutor* +RustaceanExecutorOpts rustaceanvim.txt /*RustaceanExecutorOpts* +RustaceanHoverActionsOpts rustaceanvim.txt /*RustaceanHoverActionsOpts* +RustaceanLspClientOpts rustaceanvim.txt /*RustaceanLspClientOpts* +RustaceanOpts rustaceanvim.txt /*RustaceanOpts* +RustaceanToolsOpts rustaceanvim.txt /*RustaceanToolsOpts* +RustcOpts rustaceanvim.txt /*RustcOpts* +dap_adapter_type_executable rustaceanvim.txt /*dap_adapter_type_executable* +dap_adapter_type_server rustaceanvim.txt /*dap_adapter_type_server* +dap_config_request_attach rustaceanvim.txt /*dap_config_request_attach* +dap_config_request_custom rustaceanvim.txt /*dap_config_request_custom* +dap_config_request_launch rustaceanvim.txt /*dap_config_request_launch* +disable rustaceanvim.txt /*disable* +executor_alias rustaceanvim.txt /*executor_alias* +intro rustaceanvim.txt /*intro* +lsp_server_health_status rustaceanvim.txt /*lsp_server_health_status* +rustaceanvim rustaceanvim.txt /*rustaceanvim* +rustaceanvim.config rustaceanvim.txt /*rustaceanvim.config* +rustaceanvim.config.server rustaceanvim.txt /*rustaceanvim.config.server* +rustaceanvim.contents rustaceanvim.txt /*rustaceanvim.contents* +rustaceanvim.mason mason.txt /*rustaceanvim.mason* +rustaceanvim.neotest rustaceanvim.txt /*rustaceanvim.neotest* +server.create_client_capabilities rustaceanvim.txt /*server.create_client_capabilities* +server.load_rust_analyzer_settings rustaceanvim.txt /*server.load_rust_analyzer_settings* +test_executor_alias rustaceanvim.txt /*test_executor_alias* diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/ftplugin/rust.lua b/config/neovim/store/lazy-plugins/rustaceanvim/ftplugin/rust.lua new file mode 100644 index 00000000..786e1309 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/ftplugin/rust.lua @@ -0,0 +1,64 @@ +---@type RustaceanConfig +local config = require('rustaceanvim.config.internal') + +if not vim.g.loaded_rustaceanvim then + require('rustaceanvim.config.check').check_for_lspconfig_conflict(vim.schedule_wrap(function(warn) + vim.notify_once(warn, vim.log.levels.WARN) + end)) + vim.lsp.commands['rust-analyzer.runSingle'] = function(command) + local runnables = require('rustaceanvim.runnables') + local cached_commands = require('rustaceanvim.cached_commands') + ---@type RARunnable[] + local ra_runnables = command.arguments + local runnable = ra_runnables[1] + local cargo_args = runnable.args.cargoArgs + if #cargo_args > 0 and vim.startswith(cargo_args[1], 'test') then + cached_commands.set_last_testable(1, ra_runnables) + end + cached_commands.set_last_runnable(1, ra_runnables) + runnables.run_command(1, ra_runnables) + end + + vim.lsp.commands['rust-analyzer.gotoLocation'] = function(command, ctx) + local client = vim.lsp.get_client_by_id(ctx.client_id) + if client then + vim.lsp.util.jump_to_location(command.arguments[1], client.offset_encoding) + end + end + + vim.lsp.commands['rust-analyzer.showReferences'] = function(_) + vim.lsp.buf.implementation() + end + + vim.lsp.commands['rust-analyzer.debugSingle'] = function(command) + local overrides = require('rustaceanvim.overrides') + local args = command.arguments[1].args + overrides.sanitize_command_for_debugging(args.cargoArgs) + local cached_commands = require('rustaceanvim.cached_commands') + cached_commands.set_last_debuggable(args) + local rt_dap = require('rustaceanvim.dap') + rt_dap.start(args) + end + + local commands = require('rustaceanvim.commands') + commands.create_rustc_command() +end + +vim.g.loaded_rustaceanvim = true + +local bufnr = vim.api.nvim_get_current_buf() +local auto_attach = config.server.auto_attach +if type(auto_attach) == 'function' then + auto_attach = auto_attach(bufnr) +end + +if auto_attach then + -- Defer for a smoother experience on low-end devices + vim.api.nvim_create_autocmd('BufEnter', { + buffer = bufnr, + group = vim.api.nvim_create_augroup('RustaceanvimAttach', { clear = true }), + callback = function() + require('rustaceanvim.lsp').start() + end, + }) +end diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/ftplugin/rust.vim b/config/neovim/store/lazy-plugins/rustaceanvim/ftplugin/rust.vim new file mode 100644 index 00000000..e0466566 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/ftplugin/rust.vim @@ -0,0 +1,26 @@ +" Copied from built-in compiler/{rustc,cargo}.vim +setlocal errorformat= + \%f:%l:%c:\ %t%*[^:]:\ %m, + \%f:%l:%c:\ %*\\d:%*\\d\ %t%*[^:]:\ %m, + \%-G%f:%l\ %s, + \%-G%*[\ ]^, + \%-G%*[\ ]^%*[~], + \%-G%*[\ ]... + +" New errorformat (after nightly 2016/08/10) +setlocal errorformat+= + \%-G, + \%-Gerror:\ aborting\ %.%#, + \%-Gerror:\ Could\ not\ compile\ %.%#, + \%Eerror:\ %m, + \%Eerror[E%n]:\ %m, + \%Wwarning:\ %m, + \%Inote:\ %m, + \%C\ %#-->\ %f:%l:%c + +setlocal errorformat+= + \%-G%\\s%#Downloading%.%#, + \%-G%\\s%#Compiling%.%#, + \%-G%\\s%#Finished%.%#, + \%-G%\\s%#error:\ Could\ not\ compile\ %.%#, + \%-G%\\s%#To\ learn\ more\\,%.%# diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/ftplugin/toml.lua b/config/neovim/store/lazy-plugins/rustaceanvim/ftplugin/toml.lua new file mode 100644 index 00000000..b465d3c4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/ftplugin/toml.lua @@ -0,0 +1,24 @@ +local fname = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':t') +if fname ~= 'Cargo.toml' then + return +end + +local config = require('rustaceanvim.config.internal') +local ra = require('rustaceanvim.rust_analyzer') +if config.tools.reload_workspace_from_cargo_toml then + local group = vim.api.nvim_create_augroup('RustaceanCargoReloadWorkspace', { clear = false }) + local bufnr = vim.api.nvim_get_current_buf() + vim.api.nvim_clear_autocmds { + buffer = bufnr, + group = group, + } + vim.api.nvim_create_autocmd('BufWritePost', { + buffer = vim.api.nvim_get_current_buf(), + group = group, + callback = function() + if #ra.get_active_rustaceanvim_clients(nil) > 0 then + vim.cmd.RustLsp { 'reloadWorkspace', mods = { silent = true } } + end + end, + }) +end diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/cached_commands.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/cached_commands.lua new file mode 100644 index 00000000..faa0123e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/cached_commands.lua @@ -0,0 +1,84 @@ +local M = {} + +---@alias RARunnablesChoice { choice: integer, runnables: RARunnable[] } + +---@class CommandCache +local cache = { + ---@type RARunnableArgs | nil + last_debuggable = nil, + ---@type RARunnablesChoice + last_runnable = nil, + ---@type RARunnablesChoice + last_testable = nil, +} + +---@param choice integer +---@param runnables RARunnable[] +M.set_last_runnable = function(choice, runnables) + cache.last_runnable = { + choice = choice, + runnables = runnables, + } +end + +---@param choice integer +---@param runnables RARunnable[] +M.set_last_testable = function(choice, runnables) + cache.last_testable = { + choice = choice, + runnables = runnables, + } +end + +---@param args RARunnableArgs +M.set_last_debuggable = function(args) + cache.last_debuggable = args +end + +---@param executableArgsOverride? string[] +M.execute_last_debuggable = function(executableArgsOverride) + local args = cache.last_debuggable + if args then + if type(executableArgsOverride) == 'table' and #executableArgsOverride > 0 then + args.executableArgs = executableArgsOverride + end + local rt_dap = require('rustaceanvim.dap') + rt_dap.start(args) + else + local debuggables = require('rustaceanvim.commands.debuggables') + debuggables.debuggables(executableArgsOverride) + end +end + +---@param choice RARunnablesChoice +---@param executableArgsOverride? string[] +local function override_executable_args_if_set(choice, executableArgsOverride) + if type(executableArgsOverride) == 'table' and #executableArgsOverride > 0 then + choice.runnables[choice.choice].args.executableArgs = executableArgsOverride + end +end + +M.execute_last_testable = function(executableArgsOverride) + local action = cache.last_testable + local runnables = require('rustaceanvim.runnables') + if action then + override_executable_args_if_set(action, executableArgsOverride) + runnables.run_command(action.choice, action.runnables) + else + runnables.runnables(executableArgsOverride, { tests_only = true }) + end +end + +---@param executableArgsOverride? string[] +M.execute_last_runnable = function(executableArgsOverride) + local action = cache.last_runnable + local runnables = require('rustaceanvim.runnables') + if action then + override_executable_args_if_set(action, executableArgsOverride) + runnables.run_command(action.choice, action.runnables) + else + runnables.runnables(executableArgsOverride) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/cargo.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/cargo.lua new file mode 100644 index 00000000..f8ef7130 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/cargo.lua @@ -0,0 +1,80 @@ +local compat = require('rustaceanvim.compat') +local rust_analyzer = require('rustaceanvim.rust_analyzer') +local os = require('rustaceanvim.os') +local joinpath = compat.joinpath + +local cargo = {} + +---Checks if there is an active client for file_name and returns its root directory if found. +---@param file_name string +---@return string | nil root_dir The root directory of the active client for file_name (if there is one) +local function get_mb_active_client_root(file_name) + ---@diagnostic disable-next-line: missing-parameter + local cargo_home = compat.uv.os_getenv('CARGO_HOME') or joinpath(vim.env.HOME, '.cargo') + local registry = joinpath(cargo_home, 'registry', 'src') + local checkouts = joinpath(cargo_home, 'git', 'checkouts') + + ---@diagnostic disable-next-line: missing-parameter + local rustup_home = compat.uv.os_getenv('RUSTUP_HOME') or joinpath(vim.env.HOME, '.rustup') + local toolchains = joinpath(rustup_home, 'toolchains') + + for _, item in ipairs { toolchains, registry, checkouts } do + item = os.normalize_path_on_windows(item) + if file_name:sub(1, #item) == item then + local clients = rust_analyzer.get_active_rustaceanvim_clients() + return clients and #clients > 0 and clients[#clients].config.root_dir or nil + end + end +end + +---@param file_name string +---@return string | nil root_dir +function cargo.get_root_dir(file_name) + local reuse_active = get_mb_active_client_root(file_name) + if reuse_active then + return reuse_active + end + local path = file_name:find('%.rs$') and vim.fs.dirname(file_name) or file_name + if not path then + return nil + end + ---@diagnostic disable-next-line: missing-fields + local cargo_crate_dir = vim.fs.dirname(vim.fs.find({ 'Cargo.toml' }, { + upward = true, + path = path, + })[1]) + local cargo_workspace_dir = nil + if vim.fn.executable('cargo') == 1 then + local cmd = { 'cargo', 'metadata', '--no-deps', '--format-version', '1' } + if cargo_crate_dir ~= nil then + cmd[#cmd + 1] = '--manifest-path' + cmd[#cmd + 1] = joinpath(cargo_crate_dir, 'Cargo.toml') + end + local cargo_metadata = '' + local cm = vim.fn.jobstart(cmd, { + on_stdout = function(_, d, _) + cargo_metadata = table.concat(d, '\n') + end, + stdout_buffered = true, + cwd = compat.uv.fs_stat(path) and path or cargo_crate_dir or vim.fn.getcwd(), + }) + if cm > 0 then + cm = vim.fn.jobwait({ cm })[1] + else + cm = -1 + end + if cm == 0 then + cargo_workspace_dir = vim.fn.json_decode(cargo_metadata)['workspace_root'] + ---@cast cargo_workspace_dir string + end + end + return cargo_workspace_dir + or cargo_crate_dir + ---@diagnostic disable-next-line: missing-fields + or vim.fs.dirname(vim.fs.find({ 'rust-project.json' }, { + upward = true, + path = path, + })[1]) +end + +return cargo diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/code_action_group.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/code_action_group.lua new file mode 100644 index 00000000..ab6a28fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/code_action_group.lua @@ -0,0 +1,388 @@ +local ui = require('rustaceanvim.ui') +local config = require('rustaceanvim.config.internal') +local M = {} + +---@class RACodeAction +---@field kind string +---@field group? string +---@field edit? table +---@field command? { command: string } | string + +---@class RACommand +---@field title string +---@field group? string +---@field command string +---@field arguments? any[] + +---@param action RACodeAction | RACommand +---@param client lsp.Client +---@param ctx table +function M.apply_action(action, client, ctx) + if action.edit then + vim.lsp.util.apply_workspace_edit(action.edit, client.offset_encoding) + end + if action.command then + local command = type(action.command) == 'table' and action.command or action + local fn = vim.lsp.commands[command.command] + if fn then + fn(command, ctx) + else + M.execute_command(command) + end + end +end + +---@alias action_tuple { [1]: number, [2]: RACodeAction|RACommand } + +---@param action_tuple action_tuple | nil +---@param ctx table +function M.on_user_choice(action_tuple, ctx) + if not action_tuple then + return + end + local client = vim.lsp.get_client_by_id(action_tuple[1]) + local action = action_tuple[2] + local code_action_provider = client and client.server_capabilities.codeActionProvider + if not client then + return + end + if not action.edit and type(code_action_provider) == 'table' and code_action_provider.resolveProvider then + client.request('codeAction/resolve', action, function(err, resolved_action) + ---@cast resolved_action RACodeAction|RACommand + if err then + vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) + return + end + M.apply_action(resolved_action, client, ctx) + end, 0) + else + M.apply_action(action, client, ctx) + end +end + +---@class CodeActionWindowGeometry +---@field width integer + +---@param action_tuples action_tuple[] +---@param is_group boolean +---@return CodeActionWindowGeometry +local function compute_width(action_tuples, is_group) + local width = 0 + + for _, value in pairs(action_tuples) do + local action = value[2] + local text = action.title + + if is_group and action.group then + text = action.group .. config.tools.code_actions.group_icon + end + local len = string.len(text) + if len > width then + width = len + end + end + + return { width = width + 5 } +end + +local function on_primary_enter_press() + if M.state.secondary.winnr then + vim.api.nvim_set_current_win(M.state.secondary.winnr) + return + end + + local line = vim.api.nvim_win_get_cursor(M.state.secondary.winnr or 0)[1] + + for _, value in ipairs(M.state.actions.ungrouped) do + if value[2].idx == line then + M.on_user_choice(value, M.state.ctx) + end + end + + M.cleanup() +end + +local function on_primary_quit() + M.cleanup() +end + +---@class RACodeActionResult +---@field result? RACodeAction[] | RACommand[] + +---@param results { [number]: RACodeActionResult } +---@param ctx table +local function on_code_action_results(results, ctx) + local cur_win = vim.api.nvim_get_current_win() + M.state.ctx = ctx + + ---@type action_tuple[] + local action_tuples = {} + for client_id, result in pairs(results) do + for _, action in pairs(result.result or {}) do + table.insert(action_tuples, { client_id, action }) + end + end + if #action_tuples == 0 then + vim.notify('No code actions available', vim.log.levels.INFO) + return + end + + M.state.primary.geometry = compute_width(action_tuples, true) + ---@alias grouped_actions_tbl { actions: action_tuple[], idx: integer | nil } + ---@class PartitionedActions + M.state.actions = { + grouped = {}, + ungrouped = {}, + } + + for _, value in ipairs(action_tuples) do + local action = value[2] + -- Some clippy lints may have newlines in them + action.title = string.gsub(action.title, '[\n\r]+', ' ') + if action.group then + if not M.state.actions.grouped[action.group] then + M.state.actions.grouped[action.group] = { actions = {}, idx = nil } + end + table.insert(M.state.actions.grouped[action.group].actions, value) + else + table.insert(M.state.actions.ungrouped, value) + end + end + + if #M.state.actions.grouped == 0 and config.tools.code_actions.ui_select_fallback then + ---@param item action_tuple + local function format_item(item) + local title = item[2].title:gsub('\r\n', '\\r\\n') + return title:gsub('\n', '\\n') + end + local select_opts = { + prompt = 'Code actions:', + kind = 'codeaction', + format_item = format_item, + } + vim.ui.select(M.state.actions.ungrouped, select_opts, M.on_user_choice) + return + end + + M.state.primary.bufnr = vim.api.nvim_create_buf(false, true) + local primary_winnr = vim.api.nvim_open_win(M.state.primary.bufnr, true, { + relative = 'cursor', + width = M.state.primary.geometry.width, + height = vim.tbl_count(M.state.actions.grouped) + vim.tbl_count(M.state.actions.ungrouped), + focusable = true, + border = config.tools.float_win_config.border, + row = 1, + col = 0, + }) + vim.wo[primary_winnr].signcolumn = 'no' + M.state.primary.winnr = primary_winnr + + local idx = 1 + for key, value in pairs(M.state.actions.grouped) do + value.idx = idx + vim.api.nvim_buf_set_lines(M.state.primary.bufnr, -1, -1, false, { key .. config.tools.code_actions.group_icon }) + idx = idx + 1 + end + + for _, value in pairs(M.state.actions.ungrouped) do + local action = value[2] + value[2].idx = idx + vim.api.nvim_buf_set_lines(M.state.primary.bufnr, -1, -1, false, { action.title }) + idx = idx + 1 + end + + vim.api.nvim_buf_set_lines(M.state.primary.bufnr, 0, 1, false, {}) + + vim.keymap.set('n', '', on_primary_enter_press, { buffer = M.state.primary.bufnr, noremap = true, silent = true }) + + vim.keymap.set('n', 'q', on_primary_quit, { buffer = M.state.primary.bufnr, noremap = true, silent = true }) + vim.keymap.set('n', '', on_primary_quit, { buffer = M.state.primary.bufnr, noremap = true, silent = true }) + + M.codeactionify_window_buffer(M.state.primary.winnr, M.state.primary.bufnr) + + vim.api.nvim_buf_attach(M.state.primary.bufnr, false, { + on_detach = function(_, _) + M.state.primary.clear() + vim.schedule(function() + M.cleanup() + pcall(vim.api.nvim_set_current_win, cur_win) + end) + end, + }) + + vim.api.nvim_create_autocmd('CursorMoved', { + buffer = M.state.primary.bufnr, + callback = M.on_cursor_move, + }) + + vim.cmd.redraw() +end + +function M.codeactionify_window_buffer(winnr, bufnr) + vim.bo[bufnr].modifiable = false + vim.bo[bufnr].bufhidden = 'delete' + vim.bo[bufnr].buftype = 'nofile' + vim.bo[bufnr].ft = 'markdown' + + vim.wo[winnr].nu = true + vim.wo[winnr].rnu = false + vim.wo[winnr].cul = true +end + +local function on_secondary_enter_press() + local line = vim.api.nvim_win_get_cursor(M.state.secondary.winnr)[1] + local active_group = nil + + for _, value in pairs(M.state.actions.grouped) do + if value.idx == M.state.active_group_index then + active_group = value + break + end + end + + if active_group then + for _, value in pairs(active_group.actions) do + if value[2].idx == line then + M.on_user_choice(value, M.state.ctx) + end + end + end + + M.cleanup() +end + +local function on_secondary_quit() + local winnr = M.state.secondary.winnr + -- we clear first because if we close the window first, the cursor moved + -- autocmd of the first buffer gets called which then sees that + -- M.state.secondary.winnr exists (when it shouldnt because it is closed) + -- and errors out + M.state.secondary.clear() + + ui.close_win(winnr) +end + +function M.cleanup() + if M.state.primary.winnr then + ui.close_win(M.state.primary.winnr) + M.state.primary.clear() + end + + if M.state.secondary.winnr then + ui.close_win(M.state.secondary.winnr) + M.state.secondary.clear() + end + + M.state.actions = {} + M.state.active_group_index = nil + M.state.ctx = {} +end + +function M.on_cursor_move() + local line = vim.api.nvim_win_get_cursor(M.state.primary.winnr)[1] + + for _, value in pairs(M.state.actions.grouped) do + if value.idx == line then + M.state.active_group_index = line + + if M.state.secondary.winnr then + ui.close_win(M.state.secondary.winnr) + M.state.secondary.clear() + end + + M.state.secondary.geometry = compute_width(value.actions, false) + + M.state.secondary.bufnr = vim.api.nvim_create_buf(false, true) + local secondary_winnr = vim.api.nvim_open_win(M.state.secondary.bufnr, false, { + relative = 'win', + win = M.state.primary.winnr, + width = M.state.secondary.geometry.width, + height = #value.actions, + focusable = true, + border = config.tools.float_win_config.border, + row = line - 2, + col = M.state.primary.geometry.width + 1, + }) + M.state.secondary.winnr = secondary_winnr + vim.wo[secondary_winnr].signcolumn = 'no' + + local idx = 1 + for _, inner_value in pairs(value.actions) do + local action = inner_value[2] + action.idx = idx + vim.api.nvim_buf_set_lines(M.state.secondary.bufnr, -1, -1, false, { action.title }) + idx = idx + 1 + end + + vim.api.nvim_buf_set_lines(M.state.secondary.bufnr, 0, 1, false, {}) + + M.codeactionify_window_buffer(M.state.secondary.winnr, M.state.secondary.bufnr) + + vim.keymap.set('n', '', on_secondary_enter_press, { buffer = M.state.secondary.bufnr }) + + vim.keymap.set('n', 'q', on_secondary_quit, { buffer = M.state.secondary.bufnr }) + + return + end + + if M.state.secondary.winnr then + ui.close_win(M.state.secondary.winnr) + M.state.secondary.clear() + end + end +end + +---@class CodeActionWindowState +---@field bufnr integer | nil +---@field winnr integer | nil +---@field geometry CodeActionWindowGeometry | nil +---@field clear fun() + +---@class CodeActionInternalState +M.state = { + ctx = {}, + ---@type PartitionedActions + actions = { + ---@type grouped_actions_tbl[] + grouped = {}, + ---@type action_tuple[] + ungrouped = {}, + }, + ---@type number | nil + active_group_index = nil, + ---@type CodeActionWindowState + primary = { + bufnr = nil, + winnr = nil, + geometry = nil, + clear = function() + M.state.primary.geometry = nil + M.state.primary.bufnr = nil + M.state.primary.winnr = nil + end, + }, + ---@type CodeActionWindowState + secondary = { + bufnr = nil, + winnr = nil, + geometry = nil, + clear = function() + M.state.secondary.geometry = nil + M.state.secondary.bufnr = nil + M.state.secondary.winnr = nil + end, + }, +} + +M.code_action_group = function() + local context = {} + context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() + local params = vim.lsp.util.make_range_params() + params.context = context + + vim.lsp.buf_request_all(0, 'textDocument/codeAction', params, function(results) + on_code_action_results(results, { bufnr = 0, method = 'textDocument/codeAction', params = params }) + end) +end + +return M.code_action_group diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/crate_graph.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/crate_graph.lua new file mode 100644 index 00000000..86d784ef --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/crate_graph.lua @@ -0,0 +1,77 @@ +local config = require('rustaceanvim.config.internal') +local compat = require('rustaceanvim.compat') + +local M = {} + +---@return { full: boolean} +local function get_opts() + return { full = config.tools.crate_graph.full } +end + +--- Creation of the correct handler depending on the initial call of the command +--- and give the option to override global settings +---@param backend string | nil +---@param output string | nil +---@param pipe string | nil +---@return fun(err: string, graph: string) +local function handler_factory(backend, output, pipe) + backend = backend or config.tools.crate_graph.backend + output = output or config.tools.crate_graph.output + pipe = pipe or config.tools.crate_graph.pipe + + -- Graph is a representation of the crate graph following the graphviz format + -- The handler processes and pipes the graph to the dot command that will + -- visualize with the given backend + return function(err, graph) + if err ~= nil then + vim.notify('Could not execute request to server ' .. (err or ''), vim.log.levels.ERROR) + return + end + + -- Validating backend + if not backend then + vim.notify('no crate graph backend specified.', vim.log.levels.ERROR) + return + end + if not compat.list_contains(config.tools.crate_graph.enabled_graphviz_backends, backend) then + vim.notify('crate graph backend not recognized as valid: ' .. vim.inspect(backend), vim.log.levels.ERROR) + return + end + + graph = string.gsub(graph, '\n', '') + vim.notify('rustaceanvim: Processing crate graph. This may take a while...') + + local cmd = 'dot -T' .. backend + if pipe ~= nil then -- optionally pipe to `pipe` + cmd = cmd .. ' | ' .. pipe + end + if output ~= nil then -- optionally redirect to `output` + cmd = cmd .. ' > ' .. output + end + + -- Execute dot command to generate the output graph + -- Needs to be handled with care to prevent security problems + local handle, err_ = io.popen(cmd, 'w') + if not handle then + vim.notify('Could not create crate graph ' .. (err_ or ''), vim.log.levels.ERROR) + return + end + handle:write(graph) + + -- needs to be here otherwise dot may take a long time before it gets + -- any input + cleaning up (not waiting for garbage collection) + handle:flush() + handle:close() + end +end + +local rl = require('rustaceanvim.rust_analyzer') + +---@param backend string | nil +---@param output string | nil +---@param pipe string | nil +function M.view_crate_graph(backend, output, pipe) + rl.buf_request(0, 'rust-analyzer/viewCrateGraph', get_opts(), handler_factory(backend, output, pipe)) +end + +return M.view_crate_graph diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/debuggables.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/debuggables.lua new file mode 100644 index 00000000..f5dc8052 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/debuggables.lua @@ -0,0 +1,202 @@ +local M = {} + +local compat = require('rustaceanvim.compat') +local ra_runnables = require('rustaceanvim.runnables') +local config = require('rustaceanvim.config.internal') + +---@return { textDocument: lsp_text_document, position: nil } +local function get_params() + return { + textDocument = vim.lsp.util.make_text_document_params(), + position = nil, -- get em all + } +end + +---@type { [string]: boolean? } Used to prevent this plugin from adding the same configuration twice +local _dap_configuration_added = {} + +---@param args RARunnableArgs +---@return string +local function build_label(args) + local ret = '' + for _, value in ipairs(args.cargoArgs) do + ret = ret .. value .. ' ' + end + + for _, value in ipairs(args.cargoExtraArgs) do + ret = ret .. value .. ' ' + end + + if not vim.tbl_isempty(args.executableArgs) then + ret = ret .. '-- ' + for _, value in ipairs(args.executableArgs) do + ret = ret .. value .. ' ' + end + end + return ret +end + +---@param result RARunnable[] +---@return string[] option_strings +local function get_options(result) + ---@type string[] + local option_strings = {} + + for _, debuggable in ipairs(result) do + local label = build_label(debuggable.args) + local str = label + if config.tools.cargo_override then + str = str:gsub('^cargo', config.tools.cargo_override) + end + table.insert(option_strings, str) + end + + return option_strings +end + +---@param args RARunnableArgs +---@return boolean +local function is_valid_test(args) + local is_not_cargo_check = args.cargoArgs[1] ~= 'check' + return is_not_cargo_check +end + +-- rust-analyzer doesn't actually support giving a list of debuggable targets, +-- so work around that by manually removing non debuggable targets (only cargo +-- check for now). +-- This function also makes it so that the debuggable commands are more +-- debugging friendly. For example, we move cargo run to cargo build, and cargo +-- test to cargo test --no-run. +---@param result RARunnable[] +local function sanitize_results_for_debugging(result) + ---@type RARunnable[] + local ret = vim.tbl_filter(function(value) + ---@cast value RARunnable + return is_valid_test(value.args) + end, result or {}) + + local overrides = require('rustaceanvim.overrides') + for _, value in ipairs(ret) do + overrides.sanitize_command_for_debugging(value.args.cargoArgs) + end + + return ret +end + +local function dap_run(args) + local rt_dap = require('rustaceanvim.dap') + local ok, dap = pcall(require, 'dap') + if ok then + rt_dap.start(args, true, dap.run) + local cached_commands = require('rustaceanvim.cached_commands') + cached_commands.set_last_debuggable(args) + else + vim.notify('nvim-dap is required for debugging', vim.log.levels.ERROR) + return + end +end + +---@param debuggables RARunnable[] +---@param executableArgsOverride? string[] +local function ui_select_debuggable(debuggables, executableArgsOverride) + debuggables = ra_runnables.apply_exec_args_override(executableArgsOverride, debuggables) + local options = get_options(debuggables) + if #options == 0 then + return + end + vim.ui.select(options, { prompt = 'Debuggables', kind = 'rust-tools/debuggables' }, function(_, choice) + if choice == nil then + return + end + local args = debuggables[choice].args + dap_run(args) + end) +end + +---@param debuggables RARunnable[] +local function add_debuggables_to_nvim_dap(debuggables) + local ok, dap = pcall(require, 'dap') + if not ok then + return + end + local rt_dap = require('rustaceanvim.dap') + dap.configurations.rust = dap.configurations.rust or {} + for _, debuggable in pairs(debuggables) do + rt_dap.start(debuggable.args, false, function(configuration) + local name = 'Cargo: ' .. build_label(debuggable.args) + if not _dap_configuration_added[name] then + configuration.name = name + table.insert(dap.configurations.rust, configuration) + _dap_configuration_added[name] = true + end + end) + end +end + +---@param debuggables RARunnable[] +---@param executableArgsOverride? string[] +local function debug_at_cursor_position(debuggables, executableArgsOverride) + if debuggables == nil then + return + end + debuggables = ra_runnables.apply_exec_args_override(executableArgsOverride, debuggables) + local choice = ra_runnables.get_runnable_at_cursor_position(debuggables) + if not choice then + vim.notify('No debuggable targets found for the current position.', vim.log.levels.ERROR) + return + end + local args = debuggables[choice].args + dap_run(args) +end + +---@param callback fun(result:RARunnable[]) +local function mk_handler(callback) + return function(_, result, _, _) + ---@cast result RARunnable[] + if result == nil then + return + end + result = sanitize_results_for_debugging(result) + callback(result) + end +end + +local rl = require('rustaceanvim.rust_analyzer') + +---@param handler? lsp.Handler See |lsp-handler| +local function runnables_request(handler) + rl.buf_request(0, 'experimental/runnables', get_params(), handler) +end + +---Sends the request to rust-analyzer to get the debuggables and handles them +---@param executableArgsOverride? string[] +function M.debuggables(executableArgsOverride) + runnables_request(mk_handler(function(debuggables) + ui_select_debuggable(debuggables, executableArgsOverride) + end)) +end + +---Runs the debuggable under the cursor, if present +---@param executableArgsOverride? string[] +function M.debug(executableArgsOverride) + runnables_request(mk_handler(function(debuggables) + debug_at_cursor_position(debuggables, executableArgsOverride) + end)) +end + +--- Sends the request to rust-analyzer to get the debuggables and adds them to nvim-dap's +--- configurations +function M.add_dap_debuggables() + -- Defer, because rust-analyzer may not be ready yet + runnables_request(mk_handler(add_debuggables_to_nvim_dap)) + local timer = compat.uv.new_timer() + timer:start( + 2000, + 0, + vim.schedule_wrap(function() + runnables_request(mk_handler(add_debuggables_to_nvim_dap)) + end) + ) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/diagnostic.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/diagnostic.lua new file mode 100644 index 00000000..d5b5a39e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/diagnostic.lua @@ -0,0 +1,252 @@ +local M = {} + +local config = require('rustaceanvim.config.internal') +local compat = require('rustaceanvim.compat') +local ui = require('rustaceanvim.ui') + +local rustc = 'rustc' + +---@class DiagnosticWindowState +local _window_state = { + ---@type integer | nil + float_winnr = nil, + ---@type integer | nil + latest_scratch_buf_id = nil, +} + +---@param bufnr integer +---@param winnr integer +---@param lines string[] +local function set_open_split_keymap(bufnr, winnr, lines) + local function open_split() + -- check if a buffer with the latest id is already open, if it is then + -- delete it and continue + ui.delete_buf(_window_state.latest_scratch_buf_id) + + -- create a new buffer + _window_state.latest_scratch_buf_id = vim.api.nvim_create_buf(false, true) -- not listed and scratch + + -- split the window to create a new buffer and set it to our window + local vsplit = config.tools.float_win_config.open_split == 'vertical' + ui.split(vsplit, _window_state.latest_scratch_buf_id) + + -- set filetype to rust for syntax highlighting + vim.bo[_window_state.latest_scratch_buf_id].filetype = 'rust' + -- write the expansion content to the buffer + vim.api.nvim_buf_set_lines(_window_state.latest_scratch_buf_id, 0, 0, false, lines) + end + vim.keymap.set('n', '', function() + local line = vim.api.nvim_win_get_cursor(winnr)[1] + if line > 1 then + return + end + open_split() + end, { buffer = bufnr, noremap = true, silent = true }) +end + +---@return nil +local function close_hover() + local winnr = _window_state.float_winnr + if winnr ~= nil and vim.api.nvim_win_is_valid(winnr) then + vim.api.nvim_win_close(winnr, true) + _window_state.float_winnr = nil + end +end + +---@param bufnr integer +local function set_close_keymaps(bufnr) + vim.keymap.set('n', 'q', close_hover, { buffer = bufnr, noremap = true, silent = true }) + vim.keymap.set('n', '', close_hover, { buffer = bufnr, noremap = true, silent = true }) +end + +function M.explain_error() + if vim.fn.executable(rustc) ~= 1 then + vim.notify('rustc is needed to explain errors.', vim.log.levels.ERROR) + return + end + + local diagnostics = vim.tbl_filter(function(diagnostic) + return diagnostic.code ~= nil + and diagnostic.source == 'rustc' + and diagnostic.severity == vim.diagnostic.severity.ERROR + end, vim.diagnostic.get(0, {})) + if #diagnostics == 0 then + vim.notify('No explainable errors found.', vim.log.levels.INFO) + return + end + local win_id = vim.api.nvim_get_current_win() + local opts = { + cursor_position = vim.api.nvim_win_get_cursor(win_id), + severity = vim.diagnostic.severity.ERROR, + wrap = true, + } + local found = false + local diagnostic + local pos_map = {} + ---@type string + local pos_id = '0' + repeat + diagnostic = vim.diagnostic.get_next(opts) + pos_map[pos_id] = diagnostic + if diagnostic == nil then + break + end + found = diagnostic.code ~= nil and diagnostic.source == 'rustc' + local pos = { diagnostic.lnum, diagnostic.col } + -- check if there is an explainable error at the same location + if not found then + local cursor_diagnostics = vim.tbl_filter(function(diag) + return pos[1] == diag.lnum and pos[2] == diag.col + end, diagnostics) + if #cursor_diagnostics ~= 0 then + diagnostic = cursor_diagnostics[1] + found = true + break + end + end + pos_id = vim.inspect(pos) + -- diagnostics are (0,0)-indexed but cursors are (1,0)-indexed + opts.cursor_position = { pos[1] + 1, pos[2] } + local searched_all = pos_map[pos_id] ~= nil + until diagnostic == nil or found or searched_all + if not found then + -- Fall back to first diagnostic + diagnostic = diagnostics[1] + local pos = { diagnostic.lnum, diagnostic.col } + opts.cursor_position = pos + return + end + + ---@param sc vim.SystemCompleted + local function handler(sc) + if sc.code ~= 0 or not sc.stdout then + vim.notify('Error calling rustc --explain' .. (sc.stderr and ': ' .. sc.stderr or ''), vim.log.levels.ERROR) + return + end + local output = sc.stdout:gsub('```', '```rust', 1) + local markdown_lines = vim.lsp.util.convert_input_to_markdown_lines(output, {}) + local float_preview_lines = vim.deepcopy(markdown_lines) + table.insert(float_preview_lines, 1, '---') + table.insert(float_preview_lines, 1, '1. Open in split') + vim.schedule(function() + close_hover() + local bufnr, winnr = vim.lsp.util.open_floating_preview( + float_preview_lines, + 'markdown', + vim.tbl_extend('keep', config.tools.float_win_config, { + focus = false, + focusable = true, + focus_id = 'rustc-explain-error', + close_events = { 'CursorMoved', 'BufHidden', 'InsertCharPre' }, + }) + ) + _window_state.float_winnr = winnr + set_close_keymaps(bufnr) + set_open_split_keymap(bufnr, winnr, markdown_lines) + + if config.tools.float_win_config.auto_focus then + vim.api.nvim_set_current_win(winnr) + end + end) + end + + -- Save position in the window's jumplist + vim.cmd("normal! m'") + vim.api.nvim_win_set_cursor(win_id, { diagnostic.lnum + 1, diagnostic.col }) + -- Open folds under the cursor + vim.cmd('normal! zv') + compat.system({ rustc, '--explain', tostring(diagnostic.code) }, nil, vim.schedule_wrap(handler)) +end + +---@param diagnostic table +---@return string | nil +local function get_rendered_diagnostic(diagnostic) + local result = vim.tbl_get(diagnostic, 'user_data', 'lsp', 'data', 'rendered') + if type(result) == 'string' then + ---@diagnostic disable-next-line: cast-type-mismatch + ---@cast result string + return result + end +end + +function M.render_diagnostic() + local diagnostics = vim.tbl_filter(function(diagnostic) + return get_rendered_diagnostic(diagnostic) ~= nil + end, vim.diagnostic.get(0, {})) + if #diagnostics == 0 then + vim.notify('No renderable diagnostics found.', vim.log.levels.INFO) + return + end + local win_id = vim.api.nvim_get_current_win() + local opts = { + cursor_position = vim.api.nvim_win_get_cursor(win_id), + wrap = true, + } + local rendered_diagnostic + local diagnostic + local pos_map = {} + ---@type string + local pos_id = '0' + repeat + diagnostic = vim.diagnostic.get_next(opts) + pos_map[pos_id] = diagnostic + if diagnostic == nil then + break + end + rendered_diagnostic = get_rendered_diagnostic(diagnostic) + local pos = { diagnostic.lnum, diagnostic.col } + -- check if there is a rendered diagnostic at the same location + if rendered_diagnostic == nil then + local cursor_diagnostics = vim.tbl_filter(function(diag) + return pos[1] == diag.lnum and pos[2] == diag.col + end, diagnostics) + if #cursor_diagnostics ~= 0 then + diagnostic = cursor_diagnostics[1] + rendered_diagnostic = get_rendered_diagnostic(diagnostic) + break + end + end + pos_id = vim.inspect(pos) + -- diagnostics are (0,0)-indexed but cursors are (1,0)-indexed + opts.cursor_position = { pos[1] + 1, pos[2] } + local searched_all = pos_map[pos_id] ~= nil + until diagnostic == nil or rendered_diagnostic ~= nil or searched_all + if not rendered_diagnostic then + -- No diagnostics found. Fall back to first result from filter, + diagnostic = diagnostics[1] + rendered_diagnostic = get_rendered_diagnostic(diagnostic) + ---@cast rendered_diagnostic string + end + + -- Save position in the window's jumplist + vim.cmd("normal! m'") + vim.api.nvim_win_set_cursor(win_id, { diagnostic.lnum + 1, diagnostic.col }) + -- Open folds under the cursor + vim.cmd('normal! zv') + + local lines = vim.split(rendered_diagnostic, '\n') + local float_preview_lines = vim.deepcopy(lines) + table.insert(float_preview_lines, 1, '---') + table.insert(float_preview_lines, 1, '1. Open in split') + vim.schedule(function() + close_hover() + local bufnr, winnr = vim.lsp.util.open_floating_preview( + float_preview_lines, + '', + vim.tbl_extend('keep', config.tools.float_win_config, { + focus = false, + focusable = true, + focus_id = 'ra-render-diagnostic', + close_events = { 'CursorMoved', 'BufHidden', 'InsertCharPre' }, + }) + ) + _window_state.float_winnr = winnr + set_close_keymaps(bufnr) + set_open_split_keymap(bufnr, winnr, lines) + if config.tools.float_win_config.auto_focus then + vim.api.nvim_set_current_win(winnr) + end + end) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/expand_macro.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/expand_macro.lua new file mode 100644 index 00000000..a05415e8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/expand_macro.lua @@ -0,0 +1,80 @@ +local ui = require('rustaceanvim.ui') + +local M = {} + +---@return lsp_position_params +local function get_params() + return vim.lsp.util.make_position_params() +end + +---@type integer | nil +local latest_buf_id = nil + +---@class RAMacroExpansionResult +---@field name string +---@field expansion string + +-- parse the lines from result to get a list of the desirable output +-- Example: +-- // Recursive expansion of the eprintln macro +-- // ============================================ + +-- { +-- $crate::io::_eprint(std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(err),std::fmt::Display::fmt),])); +-- } +---@param result RAMacroExpansionResult +---@return string[] +local function parse_lines(result) + local ret = {} + + local name = result.name + local text = '// Recursive expansion of the ' .. name .. ' macro' + table.insert(ret, '// ' .. string.rep('=', string.len(text) - 3)) + table.insert(ret, text) + table.insert(ret, '// ' .. string.rep('=', string.len(text) - 3)) + table.insert(ret, '') + + local expansion = result.expansion + for string in string.gmatch(expansion, '([^\n]+)') do + table.insert(ret, string) + end + + return ret +end + +---@param result? RAMacroExpansionResult +local function handler(_, result) + -- echo a message when result is nil (meaning no macro under cursor) and + -- exit + if result == nil then + vim.notify('No macro under cursor!', vim.log.levels.INFO) + return + end + + -- check if a buffer with the latest id is already open, if it is then + -- delete it and continue + ui.delete_buf(latest_buf_id) + + -- create a new buffer + latest_buf_id = vim.api.nvim_create_buf(false, true) -- not listed and scratch + + -- split the window to create a new buffer and set it to our window + ui.split(true, latest_buf_id) + + -- set filetype to rust for syntax highlighting + vim.bo[latest_buf_id].filetype = 'rust' + -- write the expansion content to the buffer + vim.api.nvim_buf_set_lines(latest_buf_id, 0, 0, false, parse_lines(result)) + + -- make the new buffer smaller + ui.resize(true, '-25') +end + +local rl = require('rustaceanvim.rust_analyzer') + +--- Sends the request to rust-analyzer to expand the macro under the cursor +function M.expand_macro() + rl.buf_request(0, 'rust-analyzer/expandMacro', get_params(), handler) +end + +return M.expand_macro diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/external_docs.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/external_docs.lua new file mode 100644 index 00000000..0f644a61 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/external_docs.lua @@ -0,0 +1,14 @@ +local M = {} + +local rl = require('rustaceanvim.rust_analyzer') + +function M.open_external_docs() + rl.buf_request(0, 'experimental/externalDocs', vim.lsp.util.make_position_params(), function(_, url) + if url then + local config = require('rustaceanvim.config.internal') + config.tools.open_url(url) + end + end) +end + +return M.open_external_docs diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/fly_check.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/fly_check.lua new file mode 100644 index 00000000..79f0f757 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/fly_check.lua @@ -0,0 +1,13 @@ +local M = {} + +local rl = require('rustaceanvim.rust_analyzer') + +---@alias flyCheckCommand 'run' | 'clear' | 'cancel' + +---@param cmd flyCheckCommand +function M.fly_check(cmd) + local params = cmd == 'run' and vim.lsp.util.make_text_document_params() or {} + rl.notify('rust-analyzer/' .. cmd .. 'Flycheck', params) +end + +return M.fly_check diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/hover_range.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/hover_range.lua new file mode 100644 index 00000000..75d8a876 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/hover_range.lua @@ -0,0 +1,59 @@ +local M = {} + +-- Converts a tuple of range coordinates into LSP's position argument +---@param row1 integer +---@param col1 integer +---@param row2 integer +---@param col2 integer +---@return lsp_range +local function make_lsp_position(row1, col1, row2, col2) + -- Note: vim's lines are 1-indexed, but LSP's are 0-indexed + return { + ['start'] = { + line = row1 - 1, + character = col1, + }, + ['end'] = { + line = row2 - 1, + character = col2, + }, + } +end + +---@return lsp_range | nil +local function get_visual_selected_range() + -- Taken from https://github.com/neovim/neovim/pull/13896#issuecomment-774680224 + local p1 = vim.fn.getpos('v') + if not p1 then + return nil + end + local row1 = p1[2] + local col1 = p1[3] + local p2 = vim.api.nvim_win_get_cursor(0) + local row2 = p2[1] + local col2 = p2[2] + + if row1 < row2 then + return make_lsp_position(row1, col1, row2, col2) + elseif row2 < row1 then + return make_lsp_position(row2, col2, row1, col1) + end + + return make_lsp_position(row1, math.min(col1, col2), row1, math.max(col1, col2)) +end + +---@return lsp_range_params +local function get_opts() + local params = vim.lsp.util.make_range_params() + params.position = get_visual_selected_range() + params.range = nil + return params +end + +local rl = require('rustaceanvim.rust_analyzer') + +function M.hover_range() + rl.buf_request(0, 'textDocument/hover', get_opts()) +end + +return M.hover_range diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/init.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/init.lua new file mode 100644 index 00000000..00247ff9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/init.lua @@ -0,0 +1,382 @@ +---@mod rustaceanvim.commands + +local config = require('rustaceanvim.config.internal') + +---@class RustaceanCommands +local M = {} + +local rust_lsp_cmd_name = 'RustLsp' +local rustc_cmd_name = 'Rustc' + +---@class command_tbl +---@field impl fun(args: string[], opts: vim.api.keyset.user_command) The command implementation +---@field complete? fun(subcmd_arg_lead: string): string[] Command completions callback, taking the lead of the subcommand's arguments +---@field bang? boolean Whether this command supports a bang! + +---@type command_tbl[] +local rustlsp_command_tbl = { + codeAction = { + impl = function(_) + require('rustaceanvim.commands.code_action_group')() + end, + }, + crateGraph = { + impl = function(args) + require('rustaceanvim.commands.crate_graph')(unpack(args)) + end, + complete = function(subcmd_arg_lead) + return vim.tbl_filter(function(backend) + return backend:find(subcmd_arg_lead) ~= nil + end, config.tools.crate_graph.enabled_graphviz_backends or {}) + end, + }, + debuggables = { + ---@param args string[] + ---@param opts vim.api.keyset.user_command + impl = function(args, opts) + if opts.bang then + require('rustaceanvim.cached_commands').execute_last_debuggable(args) + else + require('rustaceanvim.commands.debuggables').debuggables(args) + end + end, + bang = true, + }, + debug = { + ---@param args string[] + ---@param opts vim.api.keyset.user_command + impl = function(args, opts) + if opts.bang then + require('rustaceanvim.cached_commands').execute_last_debuggable(args) + else + require('rustaceanvim.commands.debuggables').debug(args) + end + end, + bang = true, + }, + expandMacro = { + impl = function(_) + require('rustaceanvim.commands.expand_macro')() + end, + }, + explainError = { + impl = function(_) + require('rustaceanvim.commands.diagnostic').explain_error() + end, + }, + renderDiagnostic = { + impl = function(_) + require('rustaceanvim.commands.diagnostic').render_diagnostic() + end, + }, + rebuildProcMacros = { + impl = function() + require('rustaceanvim.commands.rebuild_proc_macros')() + end, + }, + externalDocs = { + impl = function(_) + require('rustaceanvim.commands.external_docs')() + end, + }, + hover = { + impl = function(args) + if #args == 0 then + vim.notify("hover: called without 'actions' or 'range'", vim.log.levels.ERROR) + return + end + local subcmd = args[1] + if subcmd == 'actions' then + require('rustaceanvim.hover_actions').hover_actions() + elseif subcmd == 'range' then + require('rustaceanvim.commands.hover_range')() + else + vim.notify('hover: unknown subcommand: ' .. subcmd .. " expected 'actions' or 'range'", vim.log.levels.ERROR) + end + end, + complete = function() + return { 'actions', 'range' } + end, + }, + runnables = { + ---@param opts vim.api.keyset.user_command + impl = function(args, opts) + if opts.bang then + require('rustaceanvim.cached_commands').execute_last_runnable(args) + else + require('rustaceanvim.runnables').runnables(args) + end + end, + bang = true, + }, + run = { + ---@param opts vim.api.keyset.user_command + impl = function(args, opts) + if opts.bang then + require('rustaceanvim.cached_commands').execute_last_runnable(args) + else + require('rustaceanvim.runnables').run(args) + end + end, + bang = true, + }, + testables = { + ---@param opts vim.api.keyset.user_command + impl = function(args, opts) + if opts.bang then + require('rustaceanvim.cached_commands').execute_last_testable() + else + require('rustaceanvim.runnables').runnables(args, { tests_only = true }) + end + end, + bang = true, + }, + joinLines = { + impl = function(_, opts) + ---@cast opts vim.api.keyset.user_command + local visual_mode = opts.range and opts.range ~= 0 or false + require('rustaceanvim.commands.join_lines')(visual_mode) + end, + }, + moveItem = { + impl = function(args) + if #args == 0 then + vim.notify("moveItem: called without 'up' or 'down'", vim.log.levels.ERROR) + return + end + if args[1] == 'down' then + require('rustaceanvim.commands.move_item')() + elseif args[1] == 'up' then + require('rustaceanvim.commands.move_item')(true) + else + vim.notify( + 'moveItem: unexpected argument: ' .. vim.inspect(args) .. " expected 'up' or 'down'", + vim.log.levels.ERROR + ) + end + end, + complete = function() + return { 'up', 'down' } + end, + }, + openCargo = { + impl = function(_) + require('rustaceanvim.commands.open_cargo_toml')() + end, + }, + openDocs = { + impl = function(_) + require('rustaceanvim.commands.external_docs')() + end, + }, + parentModule = { + impl = function(_) + require('rustaceanvim.commands.parent_module')() + end, + }, + ssr = { + impl = function(args, opts) + ---@cast opts vim.api.keyset.user_command + local visual_mode = opts.range and opts.range > 0 or false + local query = args and #args > 0 and table.concat(args, ' ') or nil + require('rustaceanvim.commands.ssr')(query, visual_mode) + end, + }, + reloadWorkspace = { + impl = function() + require('rustaceanvim.commands.workspace_refresh')() + end, + }, + workspaceSymbol = { + ---@param opts vim.api.keyset.user_command + impl = function(args, opts) + local c = require('rustaceanvim.commands.workspace_symbol') + ---@type WorkspaceSymbolSearchScope + local searchScope = opts.bang and c.WorkspaceSymbolSearchScope.workspaceAndDependencies + or c.WorkspaceSymbolSearchScope.workspace + c.workspace_symbol(searchScope, args) + end, + complete = function(subcmd_arg_lead) + local c = require('rustaceanvim.commands.workspace_symbol') + return vim.tbl_filter(function(arg) + return arg:find(subcmd_arg_lead) ~= nil + end, vim.tbl_values(c.WorkspaceSymbolSearchKind)) + -- + end, + bang = true, + }, + syntaxTree = { + impl = function() + require('rustaceanvim.commands.syntax_tree')() + end, + }, + flyCheck = { + impl = function(args) + local cmd = args[1] or 'run' + require('rustaceanvim.commands.fly_check')(cmd) + end, + complete = function(subcmd_arg_lead) + return vim.tbl_filter(function(arg) + return arg:find(subcmd_arg_lead) ~= nil + end, { 'run', 'clear', 'cancel' }) + end, + }, + view = { + impl = function(args) + if not args or #args == 0 then + vim.notify("Expected argument: 'mir' or 'hir'", vim.log.levels.ERROR) + return + end + local level + local arg = args[1]:lower() + if arg == 'mir' then + level = 'Mir' + elseif arg == 'hir' then + level = 'Hir' + else + vim.notify('Unexpected argument: ' .. arg .. " Expected: 'mir' or 'hir'", vim.log.levels.ERROR) + return + end + require('rustaceanvim.commands.view_ir')(level) + end, + complete = function(subcmd_arg_lead) + return vim.tbl_filter(function(arg) + return arg:find(subcmd_arg_lead) ~= nil + end, { 'mir', 'hir' }) + end, + }, + logFile = { + impl = function() + vim.cmd.e(config.server.logfile) + end, + }, +} + +---@type command_tbl[] +local rustc_command_tbl = { + unpretty = { + impl = function(args) + local err_msg = table.concat(require('rustaceanvim.commands.rustc_unpretty').available_unpretty, ' | ') + if not args or #args == 0 then + vim.notify('Expected argument list: ' .. err_msg, vim.log.levels.ERROR) + return + end + local arg = args[1]:lower() + local available = false + for _, value in ipairs(require('rustaceanvim.commands.rustc_unpretty').available_unpretty) do + if value == arg then + available = true + break + end + end + if not available then + vim.notify('Expected argument list: ' .. err_msg, vim.log.levels.ERROR) + return + end + require('rustaceanvim.commands.rustc_unpretty').rustc_unpretty(arg) + end, + complete = function(subcmd_arg_lead) + return vim.tbl_filter(function(arg) + return arg:find(subcmd_arg_lead) ~= nil + end, require('rustaceanvim.commands.rustc_unpretty').available_unpretty) + end, + }, +} + +---@param command_tbl command_tbl +---@param opts table +---@see vim.api.nvim_create_user_command +local function run_command(command_tbl, cmd_name, opts) + local fargs = opts.fargs + local cmd = fargs[1] + local args = #fargs > 1 and vim.list_slice(fargs, 2, #fargs) or {} + local command = command_tbl[cmd] + if type(command) ~= 'table' or type(command.impl) ~= 'function' then + vim.notify(cmd_name .. ': Unknown subcommand: ' .. cmd, vim.log.levels.ERROR) + return + end + command.impl(args, opts) +end + +---@param opts table +---@see vim.api.nvim_create_user_command +local function rust_lsp(opts) + run_command(rustlsp_command_tbl, rust_lsp_cmd_name, opts) +end + +---@param opts table +---@see vim.api.nvim_create_user_command +local function rustc(opts) + run_command(rustc_command_tbl, rustc_cmd_name, opts) +end + +---@generic K, V +---@param predicate fun(V):boolean +---@param tbl table +---@return K[] +local function tbl_keys_by_value_filter(predicate, tbl) + local ret = {} + for k, v in pairs(tbl) do + if predicate(v) then + ret[k] = v + end + end + return vim.tbl_keys(ret) +end + +---Create the `:RustLsp` command +function M.create_rust_lsp_command() + vim.api.nvim_create_user_command(rust_lsp_cmd_name, rust_lsp, { + nargs = '+', + range = true, + bang = true, + desc = 'Interacts with the rust-analyzer LSP client', + complete = function(arg_lead, cmdline, _) + local commands = cmdline:match('^' .. rust_lsp_cmd_name .. '!') ~= nil + -- bang! + and tbl_keys_by_value_filter(function(command) + return command.bang == true + end, rustlsp_command_tbl) + or vim.tbl_keys(rustlsp_command_tbl) + local subcmd, subcmd_arg_lead = cmdline:match('^' .. rust_lsp_cmd_name .. '[!]*%s(%S+)%s(.*)$') + if subcmd and subcmd_arg_lead and rustlsp_command_tbl[subcmd] and rustlsp_command_tbl[subcmd].complete then + return rustlsp_command_tbl[subcmd].complete(subcmd_arg_lead) + end + if cmdline:match('^' .. rust_lsp_cmd_name .. '[!]*%s+%w*$') then + return vim.tbl_filter(function(command) + return command:find(arg_lead) ~= nil + end, commands) + end + end, + }) +end + +--- Delete the `:RustLsp` command +function M.delete_rust_lsp_command() + if vim.cmd[rust_lsp_cmd_name] then + pcall(vim.api.nvim_del_user_command, rust_lsp_cmd_name) + end +end + +---Create the `:Rustc` command +function M.create_rustc_command() + vim.api.nvim_create_user_command(rustc_cmd_name, rustc, { + nargs = '+', + range = true, + desc = 'Interacts with rustc', + complete = function(arg_lead, cmdline, _) + local commands = vim.tbl_keys(rustc_command_tbl) + local subcmd, subcmd_arg_lead = cmdline:match('^' .. rustc_cmd_name .. '[!]*%s(%S+)%s(.*)$') + if subcmd and subcmd_arg_lead and rustc_command_tbl[subcmd] and rustc_command_tbl[subcmd].complete then + return rustc_command_tbl[subcmd].complete(subcmd_arg_lead) + end + if cmdline:match('^' .. rustc_cmd_name .. '[!]*%s+%w*$') then + return vim.tbl_filter(function(command) + return command:find(arg_lead) ~= nil + end, commands) + end + end, + }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/join_lines.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/join_lines.lua new file mode 100644 index 00000000..89f42181 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/join_lines.lua @@ -0,0 +1,30 @@ +local M = {} + +---@alias lsp_join_lines_params { textDocument: lsp_text_document, ranges: lsp_range[] } + +---@param visual_mode boolean +---@return lsp_join_lines_params +local function get_params(visual_mode) + local params = visual_mode and vim.lsp.util.make_given_range_params() or vim.lsp.util.make_range_params() + local range = params.range + + params.range = nil + params.ranges = { range } + + return params +end + +local function handler(_, result, ctx) + vim.lsp.util.apply_text_edits(result, ctx.bufnr, vim.lsp.get_client_by_id(ctx.client_id).offset_encoding) +end + +local rl = require('rustaceanvim.rust_analyzer') + +--- Sends the request to rust-analyzer to get the TextEdits to join the lines +--- under the cursor and applies them +---@param visual_mode boolean +function M.join_lines(visual_mode) + rl.buf_request(0, 'experimental/joinLines', get_params(visual_mode), handler) +end + +return M.join_lines diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/move_item.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/move_item.lua new file mode 100644 index 00000000..524ef8e5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/move_item.lua @@ -0,0 +1,32 @@ +local M = {} + +---@alias lsp_move_items_params { textDocument: lsp_text_document, range: lsp_range, direction: 'Up' | 'Down' } + +---@param up boolean +---@return lsp_move_items_params +local function get_params(up) + local direction = up and 'Up' or 'Down' + local params = vim.lsp.util.make_range_params() + params.direction = direction + + return params +end + +-- move it baby +local function handler(_, result, ctx) + if result == nil or #result == 0 then + return + end + local overrides = require('rustaceanvim.overrides') + overrides.snippet_text_edits_to_text_edits(result) + vim.lsp.util.apply_text_edits(result, ctx.bufnr, vim.lsp.get_client_by_id(ctx.client_id).offset_encoding) +end + +local rl = require('rustaceanvim.rust_analyzer') + +-- Sends the request to rust-analyzer to move the item and handle the response +function M.move_item(up) + rl.buf_request(0, 'experimental/moveItem', get_params(up or false), handler) +end + +return M.move_item diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/open_cargo_toml.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/open_cargo_toml.lua new file mode 100644 index 00000000..20b0cff2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/open_cargo_toml.lua @@ -0,0 +1,27 @@ +local M = {} + +local function get_params() + return { + textDocument = vim.lsp.util.make_text_document_params(0), + } +end + +local function handler(_, result, ctx) + if result == nil then + return + end + + local client = vim.lsp.get_client_by_id(ctx.client_id) + if client then + vim.lsp.util.jump_to_location(result, client.offset_encoding) + end +end + +local rl = require('rustaceanvim.rust_analyzer') + +--- Sends the request to rust-analyzer to get cargo.toml's location and open it +function M.open_cargo_toml() + rl.buf_request(0, 'experimental/openCargoToml', get_params(), handler) +end + +return M.open_cargo_toml diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/parent_module.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/parent_module.lua new file mode 100644 index 00000000..cbe039c1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/parent_module.lua @@ -0,0 +1,33 @@ +local M = {} + +local rl = require('rustaceanvim.rust_analyzer') +local compat = require('rustaceanvim.compat') + +local function get_params() + return vim.lsp.util.make_position_params(0, nil) +end + +local function handler(_, result, ctx) + if result == nil or vim.tbl_isempty(result) then + vim.api.nvim_out_write("Can't find parent module\n") + return + end + + local location = result + + if compat.islist(result) then + location = result[1] + end + + local client = vim.lsp.get_client_by_id(ctx.client_id) + if client then + vim.lsp.util.jump_to_location(location, client.offset_encoding) + end +end + +--- Sends the request to rust-analyzer to get the parent modules location and open it +function M.parent_module() + rl.buf_request(0, 'experimental/parentModule', get_params(), handler) +end + +return M.parent_module diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/rebuild_proc_macros.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/rebuild_proc_macros.lua new file mode 100644 index 00000000..cd79d2b0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/rebuild_proc_macros.lua @@ -0,0 +1,18 @@ +local M = {} + +---@param err string | nil +local function handler(err, _, _) + if err then + vim.notify('Error rebuilding proc macros: ' .. err) + return + end +end + +local rl = require('rustaceanvim.rust_analyzer') + +--- Sends the request to rust-analyzer rebuild proc macros +function M.rebuild_macros() + rl.any_buf_request('rust-analyzer/rebuildProcMacros', nil, handler) +end + +return M.rebuild_macros diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/rustc_unpretty.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/rustc_unpretty.lua new file mode 100644 index 00000000..f81d516c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/rustc_unpretty.lua @@ -0,0 +1,146 @@ +local M = {} + +local config = require('rustaceanvim.config.internal') +local compat = require('rustaceanvim.compat') +local ui = require('rustaceanvim.ui') +local api = vim.api +local ts = vim.treesitter + +local rustc = 'rustc' + +-- TODO: See if these can be queried from rustc? +M.available_unpretty = { + 'normal', + 'identified', + 'expanded', + 'expanded,identified', + 'expanded,hygiene', + 'ast-tree', + 'ast-tree,expanded', + 'hir', + 'hir,identified', + 'hir,typed', + 'hir-tree', + 'thir-tree', + 'thir-flat', + 'mir', + 'stable-mir', + 'mir-cfg', +} +---@alias rustcir_level 'normal'| 'identified'| 'expanded'| 'expanded,identified'| 'expanded,hygiene'| 'ast-tree'| 'ast-tree,expanded'| 'hir'| 'hir,identified'| 'hir,typed'| 'hir-tree'| 'thir-tree'| 'thir-flat'| 'mir'| 'stable-mir'| 'mir-cfg' + +---@type integer | nil +local latest_buf_id = nil + +---Get a compatible vim range (1 index based) from a TS node range. +--- +---TS nodes start with 0 and the end col is ending exclusive. +---They also treat a EOF/EOL char as a char ending in the first +---col of the next row. +---comment +---@param range integer[] +---@param buf integer|nil +---@return integer, integer, integer, integer +local function get_vim_range(range, buf) + ---@type integer, integer, integer, integer + local srow, scol, erow, ecol = unpack(range) + srow = srow + 1 + scol = scol + 1 + erow = erow + 1 + + if ecol == 0 then + -- Use the value of the last col of the previous row instead. + erow = erow - 1 + if not buf or buf == 0 then + ecol = vim.fn.col { erow, '$' } - 1 + else + ecol = #vim.api.nvim_buf_get_lines(buf, erow - 1, erow, false)[1] + end + ecol = math.max(ecol, 1) + end + + return srow, scol, erow, ecol +end + +---@param node TSNode +local function get_rows(node) + local start_row, _, end_row, _ = get_vim_range({ ts.get_node_range(node) }, 0) + return vim.api.nvim_buf_get_lines(0, start_row - 1, end_row, true) +end + +---@param sc vim.SystemCompleted +local function handler(sc) + if sc.code ~= 0 then + vim.notify('rustc unpretty failed' .. sc.stderr, vim.log.levels.ERROR) + return + end + + -- check if a buffer with the latest id is already open, if it is then + -- delete it and continue + ui.delete_buf(latest_buf_id) + + -- create a new buffer + latest_buf_id = vim.api.nvim_create_buf(false, true) -- not listed and scratch + + -- split the window to create a new buffer and set it to our window + ui.split(true, latest_buf_id) + + local lines = vim.split(sc.stdout, '\n') + + -- set filetype to rust for syntax highlighting + vim.bo[latest_buf_id].filetype = 'rust' + -- write the expansion content to the buffer + vim.api.nvim_buf_set_lines(latest_buf_id, 0, 0, false, lines) +end + +---@param level rustcir_level +function M.rustc_unpretty(level) + if #api.nvim_get_runtime_file('parser/rust.so', true) == 0 then + vim.notify("a treesitter parser for Rust is required for 'rustc unpretty'", vim.log.levels.ERROR) + return + end + if vim.fn.executable(rustc) ~= 1 then + vim.notify('rustc is needed to rustc unpretty.', vim.log.levels.ERROR) + return + end + + local text + + local cursor = api.nvim_win_get_cursor(0) + local pos = { math.max(cursor[1] - 1, 0), cursor[2] } + + local cline = api.nvim_get_current_line() + if not string.find(cline, 'fn%s+') then + local temp = vim.fn.searchpos('fn ', 'bcn', vim.fn.line('w0')) + pos = { math.max(temp[1] - 1, 0), temp[2] } + end + + local node = ts.get_node { pos = pos } + + if node == nil or node:type() ~= 'function_item' then + vim.notify('no function found or function is incomplete', vim.log.levels.ERROR) + return + end + + local b = get_rows(node) + if b == nil then + vim.notify('get code text failed', vim.log.levels.ERROR) + return + end + text = table.concat(b, '\n') + + compat.system({ + rustc, + '--crate-type', + 'lib', + '--edition', + config.tools.rustc.edition, + '-Z', + 'unstable-options', + '-Z', + 'unpretty=' .. level, + '-', + }, { stdin = text }, vim.schedule_wrap(handler)) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/ssr.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/ssr.lua new file mode 100644 index 00000000..66b7026e --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/ssr.lua @@ -0,0 +1,41 @@ +local M = {} + +---@param query string +---@param visual_mode boolean +local function get_opts(query, visual_mode) + local opts = vim.lsp.util.make_position_params() + local range = (visual_mode and vim.lsp.util.make_given_range_params() or vim.lsp.util.make_range_params()).range + opts.query = query + opts.parseOnly = false + opts.selections = { range } + return opts +end + +local function handler(err, result, ctx) + if err then + error('Could not execute request to server: ' .. err.message) + return + end + + local client = vim.lsp.get_client_by_id(ctx.client_id) + if client then + vim.lsp.util.apply_workspace_edit(result, client.offset_encoding) + end +end + +local rl = require('rustaceanvim.rust_analyzer') + +---@param query string | nil +---@param visual_mode boolean +function M.ssr(query, visual_mode) + if not query then + vim.ui.input({ prompt = 'Enter query: ' }, function(input) + query = input + end) + end + if query then + rl.buf_request(0, 'experimental/ssr', get_opts(query, visual_mode), handler) + end +end + +return M.ssr diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/syntax_tree.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/syntax_tree.lua new file mode 100644 index 00000000..ba89594d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/syntax_tree.lua @@ -0,0 +1,37 @@ +local ui = require('rustaceanvim.ui') + +local M = {} + +---@return lsp_range_params +local function get_params() + return vim.lsp.util.make_range_params() +end + +---@type integer | nil +local latest_buf_id = nil + +local function parse_lines(result) + local ret = {} + + for line in string.gmatch(result, '([^\n]+)') do + table.insert(ret, line) + end + + return ret +end + +local function handler(_, result) + ui.delete_buf(latest_buf_id) + latest_buf_id = vim.api.nvim_create_buf(false, true) + ui.split(true, latest_buf_id) + vim.api.nvim_buf_set_name(latest_buf_id, 'syntax.rust') + vim.api.nvim_buf_set_text(latest_buf_id, 0, 0, 0, 0, parse_lines(result)) + ui.resize(true, '-25') +end + +local rl = require('rustaceanvim.rust_analyzer') +function M.syntax_tree() + rl.buf_request(0, 'rust-analyzer/syntaxTree', get_params(), handler) +end + +return M.syntax_tree diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/view_ir.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/view_ir.lua new file mode 100644 index 00000000..8e0c65b7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/view_ir.lua @@ -0,0 +1,50 @@ +local M = {} + +local rl = require('rustaceanvim.rust_analyzer') +local ui = require('rustaceanvim.ui') + +---@type integer | nil +local latest_buf_id = nil + +---@alias ir_level 'Hir' | 'Mir' + +local function handler(level, err, result) + local requestType = 'view' .. level + if err then + vim.notify(requestType .. ' failed' .. (result and ': ' .. result or vim.inspect(err)), vim.log.levels.ERROR) + return + end + if result and result:match('Not inside a function body') then + vim.notify(requestType .. ' failed: Not inside a function body', vim.log.levels.ERROR) + return + elseif type(result) ~= 'string' then + vim.notify(requestType .. ' failed: ' .. vim.inspect(result), vim.log.levels.ERROR) + end + + -- check if a buffer with the latest id is already open, if it is then + -- delete it and continue + ui.delete_buf(latest_buf_id) + + -- create a new buffer + latest_buf_id = vim.api.nvim_create_buf(false, true) -- not listed and scratch + + -- split the window to create a new buffer and set it to our window + ui.split(true, latest_buf_id) + + local lines = vim.split(result, '\n') + + -- set filetype to rust for syntax highlighting + vim.bo[latest_buf_id].filetype = 'rust' + -- write the expansion content to the buffer + vim.api.nvim_buf_set_lines(latest_buf_id, 0, 0, false, lines) +end + +---@param level ir_level +function M.viewIR(level) + local position_params = vim.lsp.util.make_position_params(0, nil) + rl.buf_request(0, 'rust-analyzer/view' .. level, position_params, function(...) + return handler(level, ...) + end) +end + +return M.viewIR diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/workspace_refresh.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/workspace_refresh.lua new file mode 100644 index 00000000..b0f1be57 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/workspace_refresh.lua @@ -0,0 +1,18 @@ +local M = {} + +local function handler(err) + if err then + vim.notify(tostring(err), vim.log.levels.ERROR) + return + end + vim.notify('Cargo workspace reloaded') +end + +local rl = require('rustaceanvim.rust_analyzer') + +function M.reload_workspace() + vim.notify('Reloading Cargo Workspace') + rl.any_buf_request('rust-analyzer/reloadWorkspace', nil, handler) +end + +return M.reload_workspace diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/workspace_symbol.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/workspace_symbol.lua new file mode 100644 index 00000000..286c535d --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/commands/workspace_symbol.lua @@ -0,0 +1,63 @@ +local rl = require('rustaceanvim.rust_analyzer') + +local M = {} + +---@enum WorkspaceSymbolSearchScope +M.WorkspaceSymbolSearchScope = { + workspace = 'workspace', + workspaceAndDependencies = 'workspaceAndDependencies', +} + +---@enum WorkspaceSymbolSearchKind +M.WorkspaceSymbolSearchKind = { + onlyTypes = 'onlyTypes', + allSymbols = 'allSymbols', +} + +---@type WorkspaceSymbolSearchKind +local default_search_kind = M.WorkspaceSymbolSearchKind.allSymbols + +---@param searchScope WorkspaceSymbolSearchScope +---@param searchKind WorkspaceSymbolSearchKind +---@param query string +local function get_params(searchScope, searchKind, query) + return { + query = query, + searchScope = searchScope, + searchKind = searchKind, + } +end + +---@return string | nil +local function query_from_input() + return vim.F.npcall(vim.fn.input, 'Query: ') +end + +---@param searchScope WorkspaceSymbolSearchScope +---@param args? unknown[] +function M.workspace_symbol(searchScope, args) + local searchKind = default_search_kind + local query + if not args or #args == 0 then + query = query_from_input() + if query == nil then + return + end + args = {} + end + if #args > 0 and M.WorkspaceSymbolSearchKind[args[1]] then + searchKind = args[1] + table.remove(args, 1) + end + if #args == 0 then + query = query_from_input() + if not query then + return + end + else + query = args[1] + end + rl.any_buf_request('workspace/symbol', get_params(searchScope, searchKind, query)) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/compat.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/compat.lua new file mode 100644 index 00000000..bedf3f2c --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/compat.lua @@ -0,0 +1,116 @@ +---@diagnostic disable: deprecated, duplicate-doc-field, duplicate-doc-alias +---@mod rustaceanvim.compat Functions for backward compatibility with older Neovim versions +--- and with compatibility type annotations to make the type checker +--- happy for both stable and nightly neovim versions. + +local M = {} + +M.joinpath = vim.fs.joinpath or function(...) + return (table.concat({ ... }, '/'):gsub('//+', '/')) +end + +---@class vim.lsp.get_clients.Filter +---@field id integer|nil Match clients by id +---@field bufnr integer|nil match clients attached to the given buffer +---@field name string|nil match clients by name +---@field method string|nil match client by supported method name + +---@alias vim.lsp.get_active_clients.filter vim.lsp.get_clients.Filter +---@alias lsp.Client vim.lsp.Client +---@alias lsp.ClientConfig vim.lsp.ClientConfig + +M.get_clients = vim.lsp.get_clients or vim.lsp.get_active_clients + +M.uv = vim.uv or vim.loop + +--- @enum vim.diagnostic.Severity +M.severity = { + ERROR = 1, + WARN = 2, + INFO = 3, + HINT = 4, + [1] = 'ERROR', + [2] = 'WARN', + [3] = 'INFO', + [4] = 'HINT', +} + +--- @class vim.Diagnostic +--- @field bufnr? integer +--- @field lnum integer 0-indexed +--- @field end_lnum? integer 0-indexed +--- @field col integer 0-indexed +--- @field end_col? integer 0-indexed +--- @field severity? vim.diagnostic.Severity +--- @field message string +--- @field source? string +--- @field code? string +--- @field _tags? { deprecated: boolean, unnecessary: boolean} +--- @field user_data? any arbitrary data plugins can add +--- @field namespace? integer + +--- @class vim.api.keyset.user_command +--- @field addr? any +--- @field bang? boolean +--- @field bar? boolean +--- @field complete? any +--- @field count? any +--- @field desc? any +--- @field force? boolean +--- @field keepscript? boolean +--- @field nargs? any +--- @field preview? any +--- @field range? any +--- @field register? boolean + +--- @class vim.SystemCompleted +--- @field code integer +--- @field signal integer +--- @field stdout? string +--- @field stderr? string + +M.system = vim.system + -- wrapper around vim.fn.system to give it a similar API to vim.system + or function(cmd, opts, on_exit) + ---@cast cmd string[] + ---@cast opts vim.SystemOpts | nil + ---@cast on_exit fun(sc: vim.SystemCompleted) | nil + ---@diagnostic disable-next-line: undefined-field + if opts and opts.cwd then + local shell = require('rustaceanvim.shell') + ---@diagnostic disable-next-line: undefined-field + cmd = shell.chain_commands { shell.make_cd_command(opts.cwd), table.concat(cmd, ' ') } + ---@cast cmd string + end + + local output = vim.fn.system(cmd) + local ok = vim.v.shell_error + ---@type vim.SystemCompleted + local systemObj = { + signal = 0, + stdout = ok and (output or '') or nil, + stderr = not ok and (output or '') or nil, + code = vim.v.shell_error, + } + if on_exit then + on_exit(systemObj) + end + return systemObj + end + +M.list_contains = vim.list_contains + or function(t, value) + vim.validate { t = { t, 't' } } + for _, v in ipairs(t) do + if v == value then + return true + end + end + return false + end + +---@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext, config?: table): ...any + +M.islist = vim.islist or vim.tbl_islist + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/check.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/check.lua new file mode 100644 index 00000000..41910d83 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/check.lua @@ -0,0 +1,158 @@ +---@mod rustaceanvim.config.check rustaceanvim configuration check + +local types = require('rustaceanvim.types.internal') + +local M = {} + +---@param path string +---@param msg string|nil +---@return string +local function mk_error_msg(path, msg) + return msg and path .. '.' .. msg or path +end + +---@param path string The config path +---@param tbl table The table to validate +---@see vim.validate +---@return boolean is_valid +---@return string|nil error_message +local function validate(path, tbl) + local prefix = 'Invalid config: ' + local ok, err = pcall(vim.validate, tbl) + return ok or false, prefix .. mk_error_msg(path, err) +end + +---Validates the config. +---@param cfg RustaceanConfig +---@return boolean is_valid +---@return string|nil error_message +function M.validate(cfg) + local ok, err + ok, err = validate('rustaceanvim', { + tools = { cfg.tools, 'table' }, + server = { cfg.server, 'table' }, + dap = { cfg.dap, 'table' }, + }) + if not ok then + return false, err + end + local tools = cfg.tools + local crate_graph = tools.crate_graph + ok, err = validate('tools.crate_graph', { + backend = { crate_graph.backend, 'string', true }, + enabled_graphviz_backends = { crate_graph.enabled_graphviz_backends, 'table', true }, + full = { crate_graph.full, 'boolean' }, + output = { crate_graph.output, 'string', true }, + pipe = { crate_graph.pipe, 'string', true }, + }) + if not ok then + return false, err + end + local hover_actions = tools.hover_actions + ok, err = validate('tools.hover_actions', { + replace_builtin_hover = { hover_actions.replace_builtin_hover, 'boolean' }, + }) + if not ok then + return false, err + end + local float_win_config = tools.float_win_config + ok, err = validate('tools.float_win_config', { + auto_focus = { float_win_config.auto_focus, 'boolean' }, + open_split = { float_win_config.open_split, 'string' }, + }) + if not ok then + return false, err + end + local rustc = tools.rustc + ok, err = validate('tools.rustc', { + edition = { rustc.edition, 'string' }, + }) + if not ok then + return false, err + end + ok, err = validate('tools', { + executor = { tools.executor, { 'table', 'string' } }, + test_executor = { tools.test_executor, { 'table', 'string' } }, + crate_test_executor = { tools.crate_test_executor, { 'table', 'string' } }, + cargo_override = { tools.cargo_override, 'string', true }, + enable_nextest = { tools.enable_nextest, 'boolean' }, + enable_clippy = { tools.enable_clippy, 'boolean' }, + on_initialized = { tools.on_initialized, 'function', true }, + reload_workspace_from_cargo_toml = { tools.reload_workspace_from_cargo_toml, 'boolean' }, + open_url = { tools.open_url, 'function' }, + }) + if not ok then + return false, err + end + local server = cfg.server + ok, err = validate('server', { + cmd = { server.cmd, { 'function', 'table' } }, + standalone = { server.standalone, 'boolean' }, + settings = { server.settings, { 'function', 'table' }, true }, + }) + if not ok then + return false, err + end + if type(server.settings) == 'table' then + ok, err = validate('server.settings', { + ['rust-analyzer'] = { server.settings['rust-analyzer'], 'table', true }, + }) + if not ok then + return false, err + end + end + local dap = cfg.dap + local adapter = types.evaluate(dap.adapter) + if adapter == false then + ok = true + elseif adapter.type == 'executable' then + ---@cast adapter DapExecutableConfig + ok, err = validate('dap.adapter', { + command = { adapter.command, 'string' }, + name = { adapter.name, 'string', true }, + args = { adapter.args, 'table', true }, + }) + elseif adapter.type == 'server' then + ---@cast adapter DapServerConfig + ok, err = validate('dap.adapter', { + command = { adapter.executable, 'table' }, + name = { adapter.name, 'string', true }, + host = { adapter.host, 'string', true }, + port = { adapter.port, 'string' }, + }) + if ok then + ok, err = validate('dap.adapter.executable', { + command = { adapter.executable.command, 'string' }, + args = { adapter.executable.args, 'table', true }, + }) + end + else + ok = false + err = 'dap.adapter: Expected DapExecutableConfig, DapServerConfig or false' + end + if not ok then + return false, err + end + return true +end + +---@param callback fun(msg: string) +function M.check_for_lspconfig_conflict(callback) + for _, autocmd in ipairs(vim.api.nvim_get_autocmds { event = 'FileType', pattern = 'rust' }) do + if + autocmd.group_name + and autocmd.group_name == 'lspconfig' + and autocmd.desc + and autocmd.desc:match('rust_analyzer') + then + callback([[ +nvim-lspconfig.rust_analyzer has been setup. +This will likely lead to conflicts with the rustaceanvim LSP client. +See ':h rustaceanvim.mason' +]]) + return + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/init.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/init.lua new file mode 100644 index 00000000..124a404a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/init.lua @@ -0,0 +1,183 @@ +---@mod rustaceanvim.config plugin configuration +--- +---@brief [[ +--- +---rustaceanvim is a filetype plugin, and does not need +---a `setup` function to work. +--- +---To configure rustaceanvim, set the variable `vim.g.rustaceanvim`, +---which is a `RustaceanOpts` table, in your neovim configuration. +--- +---Example: +--- +--->lua +------@type RustaceanOpts +---vim.g.rustaceanvim = { +--- ---@type RustaceanToolsOpts +--- tools = { +--- -- ... +--- }, +--- ---@type RustaceanLspClientOpts +--- server = { +--- on_attach = function(client, bufnr) +--- -- Set keybindings, etc. here. +--- end, +--- default_settings = { +--- -- rust-analyzer language server configuration +--- ['rust-analyzer'] = { +--- }, +--- }, +--- -- ... +--- }, +--- ---@type RustaceanDapOpts +--- dap = { +--- -- ... +--- }, +--- } +---< +--- +---Notes: +--- +--- - `vim.g.rustaceanvim` can also be a function that returns a `RustaceanOpts` table. +--- - `server.settings`, by default, is a function that looks for a `rust-analyzer.json` file +--- in the project root, to load settings from it. It falls back to an empty table. +--- +---@brief ]] + +local M = {} + +---@type RustaceanOpts | fun():RustaceanOpts | nil +vim.g.rustaceanvim = vim.g.rustaceanvim + +---@class RustaceanOpts +---@field tools? RustaceanToolsOpts Plugin options +---@field server? RustaceanLspClientOpts Language server client options +---@field dap? RustaceanDapOpts Debug adapter options + +---@class RustaceanToolsOpts +---@field executor? RustaceanExecutor | executor_alias The executor to use for runnables/debuggables +---@field test_executor? RustaceanExecutor | test_executor_alias The executor to use for runnables that are tests / testables +---@field crate_test_executor? RustaceanExecutor | test_executor_alias The executor to use for runnables that are crate test suites (--all-targets) +---@field cargo_override? string Set this to override the 'cargo' command for runnables, debuggables (etc., e.g. to 'cross'). If set, this takes precedence over 'enable_nextest'. +---@field enable_nextest? boolean Whether to enable nextest. If enabled, `cargo test` commands will be transformed to `cargo nextest run` commands. Defaults to `true` if cargo-nextest is detected. Ignored if `cargo_override` is set. +---@field enable_clippy? boolean Whether to enable clippy checks on save if a clippy installation is detected. Default: `true` +---@field on_initialized? fun(health:RustAnalyzerInitializedStatus) Function that is invoked when the LSP server has finished initializing +---@field reload_workspace_from_cargo_toml? boolean Automatically call `RustReloadWorkspace` when writing to a Cargo.toml file +---@field hover_actions? RustaceanHoverActionsOpts Options for hover actions +---@field code_actions? RustaceanCodeActionOpts Options for code actions +---@field float_win_config? FloatWinConfig Options applied to floating windows. See |api-win_config|. +---@field create_graph? RustaceanCrateGraphConfig Options for showing the crate graph based on graphviz and the dot +---@field open_url? fun(url:string):nil If set, overrides how to open URLs +---@field rustc? RustcOpts Options for `rustc` + +---@class RustaceanExecutor +---@field execute_command fun(cmd:string, args:string[], cwd:string|nil, opts?: RustaceanExecutorOpts) + +---@class RustaceanExecutorOpts +---@field bufnr? integer The buffer from which the executor was invoked. + +---@class FloatWinConfig +---@field auto_focus? boolean +---@field open_split? 'horizontal' | 'vertical' +---@see vim.lsp.util.open_floating_preview.Opts +---@see vim.api.nvim_open_win + +---@alias executor_alias 'termopen' | 'quickfix' | 'toggleterm' | 'vimux' | 'neotest' + +---@alias test_executor_alias executor_alias | 'background' + +---@class RustaceanHoverActionsOpts +---@field replace_builtin_hover? boolean Whether to replace Neovim's built-in `vim.lsp.buf.hover` with hover actions. Default: `true` + +---@class RustaceanCodeActionOpts +---@field group_icon? string Text appended to a group action +---@field ui_select_fallback? boolean Whether to fall back to `vim.ui.select` if there are no grouped code actions. Default: `false` + +---@alias lsp_server_health_status 'ok' | 'warning' | 'error' + +---@class RustAnalyzerInitializedStatus +---@field health lsp_server_health_status + +---@class RustaceanCrateGraphConfig +---@field backend? string Backend used for displaying the graph. See: https://graphviz.org/docs/outputs/ Defaults to `"x11"` if unset. +---@field output? string Where to store the output. No output if unset. Relative path from `cwd`. +---@field enabled_graphviz_backends? string[] Override the enabled graphviz backends list, used for input validation and autocompletion. +---@field pipe? string Overide the pipe symbol in the shell command. Useful if using a shell that is not supported by this plugin. + +---@class RustcOpts +---@field edition string The edition to use. See https://rustc-dev-guide.rust-lang.org/guides/editions.html. Default '2021'. + +---@class RustaceanLspClientOpts +---@field auto_attach? boolean | fun(bufnr: integer):boolean Whether to automatically attach the LSP client. Defaults to `true` if the `rust-analyzer` executable is found. +---@field cmd? string[] | fun():string[] Command and arguments for starting rust-analyzer +---@field settings? table | fun(project_root:string|nil, default_settings: table):table Setting passed to rust-analyzer. Defaults to a function that looks for a `rust-analyzer.json` file or returns an empty table. See https://rust-analyzer.github.io/manual.html#configuration. +---@field standalone? boolean Standalone file support (enabled by default). Disabling it may improve rust-analyzer's startup time. +---@field logfile? string The path to the rust-analyzer log file. +---@field load_vscode_settings? boolean Whether to search (upward from the buffer) for rust-analyzer settings in .vscode/settings json. If found, loaded settings will override configured options. Default: false + +---@class RustaceanDapOpts +--- @field autoload_configurations boolean Whether to autoload nvim-dap configurations when rust-analyzer has attached? Default: `true`. +---@field adapter? DapExecutableConfig | DapServerConfig | disable | fun():(DapExecutableConfig | DapServerConfig | disable) Defaults to creating the `rt_lldb` adapter, which is a `DapServerConfig` if `codelldb` is detected, and a `DapExecutableConfig` if `lldb` is detected. Set to `false` to disable. +---@field configuration? DapClientConfig | disable | fun():(DapClientConfig | disable) Dap client configuration. Defaults to a function that looks for a `launch.json` file or returns a `DapExecutableConfig` that launches the `rt_lldb` adapter. Set to `false` to disable. +---@field add_dynamic_library_paths? boolean | fun():boolean Accommodate dynamically-linked targets by passing library paths to lldb. Default: `true`. +---@field auto_generate_source_map? fun():boolean | boolean Whether to auto-generate a source map for the standard library. +---@field load_rust_types? fun():boolean | boolean Whether to get Rust types via initCommands (rustlib/etc/lldb_commands, lldb only). Default: `true`. + +---@alias disable false + +---@alias DapCommand string + +---@class DapExecutableConfig +---@field type dap_adapter_type_executable The type of debug adapter. +---@field command string Default: `"lldb-vscode"`. +---@field args? string Default: unset. +---@field name? string Default: `"lldb"`. + +---@class DapServerConfig +---@field type dap_adapter_type_server The type of debug adapter. +---@field host? string The host to connect to. +---@field port string The port to connect to. +---@field executable DapExecutable The executable to run +---@field name? string + +---@class DapExecutable +---@field command string The executable. +---@field args string[] Its arguments. + +---@alias dap_adapter_type_executable "executable" +---@alias dap_adapter_type_server "server" + +---@class DapClientConfig: Configuration +---@field type string The dap adapter to use +---@field name string +---@field request dap_config_request_launch | dap_config_request_attach | dap_config_request_custom The type of dap session +---@field cwd? string Current working directory +---@field program? string Path to executable for most DAP clients +---@field args? string[] Optional args to DAP client, not valid for all client types +---@field env? EnvironmentMap Environmental variables +---@field initCommands? string[] Initial commands to run, `lldb` clients only +---@field coreConfigs? table Essential config values for `probe-rs` client, see https://probe.rs/docs/tools/debugger/ + +---@alias EnvironmentMap table + +---@alias dap_config_request_launch "launch" +---@alias dap_config_request_attach "attach" +---@alias dap_config_request_custom "custom" + +---For the heroes who want to use it. +---@param codelldb_path string Path to the codelldb executable +---@param liblldb_path string Path to the liblldb dynamic library +---@return DapServerConfig +function M.get_codelldb_adapter(codelldb_path, liblldb_path) + return { + type = 'server', + port = '${port}', + host = '127.0.0.1', + executable = { + command = codelldb_path, + args = { '--liblldb', liblldb_path, '--port', '${port}' }, + }, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/internal.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/internal.lua new file mode 100644 index 00000000..6f9cf45a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/internal.lua @@ -0,0 +1,425 @@ +local types = require('rustaceanvim.types.internal') +local compat = require('rustaceanvim.compat') +local config = require('rustaceanvim.config') +local executors = require('rustaceanvim.executors') +local os = require('rustaceanvim.os') +local server_config = require('rustaceanvim.config.server') + +local RustaceanConfig + +---@class RustAnalyzerInitializedStatusInternal : RustAnalyzerInitializedStatus +---@field health lsp_server_health_status +---@field quiescent boolean inactive? +--- +---@param dap_adapter DapExecutableConfig | DapServerConfig | disable +---@return boolean +local function should_enable_dap_config_value(dap_adapter) + local adapter = types.evaluate(dap_adapter) + if adapter == false then + return false + end + return vim.fn.executable('rustc') == 1 +end + +---@param adapter DapServerConfig | DapExecutableConfig +local function is_codelldb_adapter(adapter) + return adapter.type == 'server' +end + +---@param adapter DapServerConfig | DapExecutableConfig +local function is_lldb_adapter(adapter) + return adapter.type == 'executable' +end + +---@param type string +---@return DapClientConfig +local function load_dap_configuration(type) + -- default + ---@type DapClientConfig + local dap_config = { + name = 'Rust debug client', + type = type, + request = 'launch', + stopOnEntry = false, + } + ---@diagnostic disable-next-line: different-requires + local dap = require('dap') + -- Load configurations from a `launch.json`. + -- It is necessary to check for changes in the `dap.configurations` table, as + -- `load_launchjs` does not return anything, it loads directly into `dap.configurations`. + local pre_launch = vim.deepcopy(dap.configurations) or {} + require('dap.ext.vscode').load_launchjs(nil, { lldb = { 'rust' }, codelldb = { 'rust' } }) + for name, configuration_entries in pairs(dap.configurations) do + if pre_launch[name] == nil or not vim.deep_equal(pre_launch[name], configuration_entries) then + -- `configurations` are tables of `configuration` entries + -- use the first `configuration` that matches + for _, entry in pairs(configuration_entries) do + ---@cast entry DapClientConfig + if entry.type == type then + dap_config = entry + break + end + end + end + end + return dap_config +end + +---@return RustaceanExecutor +local function get_crate_test_executor() + if vim.fn.has('nvim-0.10.0') == 1 then + return executors.background + else + return executors.termopen + end +end + +---@return RustaceanExecutor +local function get_test_executor() + if package.loaded['rustaceanvim.neotest'] ~= nil then + -- neotest has been set up with rustaceanvim as an adapter + return executors.neotest + end + return get_crate_test_executor() +end + +---@class RustaceanConfig +local RustaceanDefaultConfig = { + ---@class RustaceanToolsConfig + tools = { + + --- how to execute terminal commands + --- options right now: termopen / quickfix / toggleterm / vimux + ---@type RustaceanExecutor + executor = executors.termopen, + + ---@type RustaceanExecutor + test_executor = get_test_executor(), + + ---@type RustaceanExecutor + crate_test_executor = get_crate_test_executor(), + + ---@type string | nil + cargo_override = nil, + + ---@type boolean + enable_nextest = true, + + ---@type boolean + enable_clippy = true, + + --- callback to execute once rust-analyzer is done initializing the workspace + --- The callback receives one parameter indicating the `health` of the server: "ok" | "warning" | "error" + ---@type fun(health:RustAnalyzerInitializedStatus) | nil + on_initialized = nil, + + --- automatically call RustReloadWorkspace when writing to a Cargo.toml file. + ---@type boolean + reload_workspace_from_cargo_toml = true, + + --- options same as lsp hover + ---@see vim.lsp.util.open_floating_preview + ---@class RustaceanHoverActionsConfig + hover_actions = { + + --- whether to replace Neovim's built-in `vim.lsp.buf.hover`. + ---@type boolean + replace_builtin_hover = true, + }, + + code_actions = { + --- text appended to a group action + ---@type string + group_icon = ' ▶', + + --- whether to fall back to `vim.ui.select` if there are no grouped code actions + ---@type boolean + ui_select_fallback = false, + }, + + --- options same as lsp hover + ---@see vim.lsp.util.open_floating_preview + ---@see vim.api.nvim_open_win + ---@type table Options applied to floating windows. + float_win_config = { + --- whether the window gets automatically focused + --- default: false + ---@type boolean + auto_focus = false, + + --- whether splits opened from floating preview are vertical + --- default: false + ---@type 'horizontal' | 'vertical' + open_split = 'horizontal', + }, + + --- settings for showing the crate graph based on graphviz and the dot + --- command + ---@class RustaceanCrateGraphConfig + crate_graph = { + -- backend used for displaying the graph + -- see: https://graphviz.org/docs/outputs/ + -- default: x11 + ---@type string + backend = 'x11', + -- where to store the output, nil for no output stored (relative + -- path from pwd) + -- default: nil + ---@type string | nil + output = nil, + -- true for all crates.io and external crates, false only the local + -- crates + -- default: true + ---@type boolean + full = true, + + -- List of backends found on: https://graphviz.org/docs/outputs/ + -- Is used for input validation and autocompletion + -- Last updated: 2021-08-26 + ---@type string[] + enabled_graphviz_backends = { + 'bmp', + 'cgimage', + 'canon', + 'dot', + 'gv', + 'xdot', + 'xdot1.2', + 'xdot1.4', + 'eps', + 'exr', + 'fig', + 'gd', + 'gd2', + 'gif', + 'gtk', + 'ico', + 'cmap', + 'ismap', + 'imap', + 'cmapx', + 'imap_np', + 'cmapx_np', + 'jpg', + 'jpeg', + 'jpe', + 'jp2', + 'json', + 'json0', + 'dot_json', + 'xdot_json', + 'pdf', + 'pic', + 'pct', + 'pict', + 'plain', + 'plain-ext', + 'png', + 'pov', + 'ps', + 'ps2', + 'psd', + 'sgi', + 'svg', + 'svgz', + 'tga', + 'tiff', + 'tif', + 'tk', + 'vml', + 'vmlz', + 'wbmp', + 'webp', + 'xlib', + 'x11', + }, + ---@type string | nil + pipe = nil, + }, + + ---@type fun(url:string):nil + open_url = function(url) + require('rustaceanvim.os').open_url(url) + end, + ---settings for rustc + ---@class RustaceanRustcConfig + rustc = { + ---@type string + edition = '2021', + }, + }, + + --- all the opts to send to the LSP client + --- these override the defaults set by rust-tools.nvim + ---@diagnostic disable-next-line: undefined-doc-class + ---@class RustaceanLspClientConfig: vim.lsp.ClientConfig + server = { + ---@type lsp.ClientCapabilities + capabilities = server_config.create_client_capabilities(), + ---@type boolean | fun(bufnr: integer):boolean Whether to automatically attach the LSP client. + ---Defaults to `true` if the `rust-analyzer` executable is found. + auto_attach = function(bufnr) + if #vim.bo[bufnr].buftype > 0 then + return false + end + local path = vim.api.nvim_buf_get_name(bufnr) + if not os.is_valid_file_path(path) then + return false + end + local cmd = types.evaluate(RustaceanConfig.server.cmd) + ---@cast cmd string[] + local rs_bin = cmd[1] + return vim.fn.executable(rs_bin) == 1 + end, + ---@type string[] | fun():string[] + cmd = function() + return { 'rust-analyzer', '--log-file', RustaceanConfig.server.logfile } + end, + --- standalone file support + --- setting it to false may improve startup time + ---@type boolean + standalone = true, + + ---@type string The path to the rust-analyzer log file. + logfile = vim.fn.tempname() .. '-rust-analyzer.log', + + ---@type table | (fun(project_root:string|nil, default_settings: table|nil):table) -- The rust-analyzer settings or a function that creates them. + settings = function(project_root, default_settings) + return server_config.load_rust_analyzer_settings(project_root, { default_settings = default_settings }) + end, + + --- @type table + default_settings = { + --- options to send to rust-analyzer + --- See: https://rust-analyzer.github.io/manual.html#configuration + --- @type table + ['rust-analyzer'] = {}, + }, + ---@type boolean Whether to search (upward from the buffer) for rust-analyzer settings in .vscode/settings json. + load_vscode_settings = false, + }, + + --- debugging stuff + --- @class RustaceanDapConfig + dap = { + --- @type boolean Whether to autoload nvim-dap configurations when rust-analyzer has attached? + autoload_configurations = vim.fn.has('nvim-0.10.0') == 1, -- Compiling the debug build cannot be run asynchronously on Neovim < 0.10 + --- @type DapExecutableConfig | DapServerConfig | disable | fun():(DapExecutableConfig | DapServerConfig | disable) + adapter = function() + --- @type DapExecutableConfig | DapServerConfig | disable + local result = false + local has_mason, mason_registry = pcall(require, 'mason-registry') + if has_mason and mason_registry.is_installed('codelldb') then + local codelldb_package = mason_registry.get_package('codelldb') + local mason_codelldb_path = compat.joinpath(codelldb_package:get_install_path(), 'extension') + local codelldb_path = compat.joinpath(mason_codelldb_path, 'adapter', 'codelldb') + local liblldb_path = compat.joinpath(mason_codelldb_path, 'lldb', 'lib', 'liblldb') + local shell = require('rustaceanvim.shell') + if shell.is_windows() then + codelldb_path = codelldb_path .. '.exe' + liblldb_path = compat.joinpath(mason_codelldb_path, 'lldb', 'bin', 'liblldb.dll') + else + liblldb_path = liblldb_path .. (shell.is_macos() and '.dylib' or '.so') + end + result = config.get_codelldb_adapter(codelldb_path, liblldb_path) + elseif vim.fn.executable('codelldb') == 1 then + ---@cast result DapServerConfig + result = { + type = 'server', + host = '127.0.0.1', + port = '${port}', + executable = { + command = 'codelldb', + args = { '--port', '${port}' }, + }, + } + else + local has_lldb_dap = vim.fn.executable('lldb-dap') == 1 + local has_lldb_vscode = vim.fn.executable('lldb-vscode') == 1 + if not has_lldb_dap and not has_lldb_vscode then + return result + end + local command = has_lldb_dap and 'lldb-dap' or 'lldb-vscode' + ---@cast result DapExecutableConfig + result = { + type = 'executable', + command = command, + name = 'lldb', + } + end + return result + end, + --- Accommodate dynamically-linked targets by passing library paths to lldb. + ---@type boolean | fun():boolean + add_dynamic_library_paths = function() + return should_enable_dap_config_value(RustaceanConfig.dap.adapter) + end, + --- Auto-generate a source map for the standard library. + ---@type boolean | fun():boolean + auto_generate_source_map = function() + return should_enable_dap_config_value(RustaceanConfig.dap.adapter) + end, + --- Get Rust types via initCommands (rustlib/etc/lldb_commands). + ---@type boolean | fun():boolean + load_rust_types = function() + if not should_enable_dap_config_value(RustaceanConfig.dap.adapter) then + return false + end + local adapter = types.evaluate(RustaceanConfig.dap.adapter) + ---@cast adapter DapExecutableConfig | DapServerConfig | disable + return adapter ~= false and is_lldb_adapter(adapter) + end, + --- @type DapClientConfig | disable | fun():(DapClientConfig | disable) + configuration = function() + local ok, _ = pcall(require, 'dap') + if not ok then + return false + end + local adapter = types.evaluate(RustaceanConfig.dap.adapter) + ---@cast adapter DapExecutableConfig | DapServerConfig | disable + if adapter == false then + return false + end + ---@cast adapter DapExecutableConfig | DapServerConfig + local type = is_codelldb_adapter(adapter) and 'codelldb' or 'lldb' + return load_dap_configuration(type) + end, + }, + -- debug info + was_g_rustaceanvim_sourced = vim.g.rustaceanvim ~= nil, +} +local rustaceanvim = vim.g.rustaceanvim or {} +local opts = type(rustaceanvim) == 'function' and rustaceanvim() or rustaceanvim +for _, executor in pairs { 'executor', 'test_executor', 'crate_test_executor' } do + if opts.tools and opts.tools[executor] and type(opts.tools[executor]) == 'string' then + opts.tools[executor] = assert(executors[opts.tools[executor]], 'Unknown RustaceanExecutor') + end +end + +---@type RustaceanConfig +RustaceanConfig = vim.tbl_deep_extend('force', {}, RustaceanDefaultConfig, opts) + +-- Override user dap.adapter config in a backward compatible way +if opts.dap and opts.dap.adapter then + local user_adapter = opts.dap.adapter + local default_adapter = types.evaluate(RustaceanConfig.dap.adapter) + if + type(user_adapter) == 'table' + and type(default_adapter) == 'table' + and user_adapter.type == default_adapter.type + then + ---@diagnostic disable-next-line: inject-field + RustaceanConfig.dap.adapter = vim.tbl_deep_extend('force', default_adapter, user_adapter) + elseif user_adapter ~= nil then + ---@diagnostic disable-next-line: inject-field + RustaceanConfig.dap.adapter = user_adapter + end +end + +local check = require('rustaceanvim.config.check') +local ok, err = check.validate(RustaceanConfig) +if not ok then + vim.notify('rustaceanvim: ' .. err, vim.log.levels.ERROR) +end + +return RustaceanConfig diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/json.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/json.lua new file mode 100644 index 00000000..351e409f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/json.lua @@ -0,0 +1,50 @@ +local M = {} + +local function tbl_set(tbl, keys, value) + local next = table.remove(keys, 1) + if #keys > 0 then + tbl[next] = tbl[next] or {} + tbl_set(tbl[next], keys, value) + else + tbl[next] = value + end +end + +---@param tbl table +---@param json_key string e.g. "rust-analyzer.check.overrideCommand" +---@param json_value unknown +local function override_tbl_values(tbl, json_key, json_value) + local keys = vim.split(json_key, '%.') + tbl_set(tbl, keys, json_value) +end + +---@param json_content string +---@return table +function M.silent_decode(json_content) + local ok, json_tbl = pcall(vim.json.decode, json_content) + if not ok or type(json_tbl) ~= 'table' then + return {} + end + return json_tbl +end + +---@param tbl table +---@param json_tbl { [string]: unknown } +---@param key_predicate? fun(string): boolean +function M.override_with_json_keys(tbl, json_tbl, key_predicate) + for json_key, value in pairs(json_tbl) do + if not key_predicate or key_predicate(json_key) then + override_tbl_values(tbl, json_key, value) + end + end +end + +---@param tbl table +---@param json_tbl { [string]: unknown } +function M.override_with_rust_analyzer_json_keys(tbl, json_tbl) + M.override_with_json_keys(tbl, json_tbl, function(key) + return vim.startswith(key, 'rust-analyzer') + end) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/server.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/server.lua new file mode 100644 index 00000000..fee92dba --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/config/server.lua @@ -0,0 +1,159 @@ +---@mod rustaceanvim.config.server LSP configuration utility + +local server = {} + +---@class LoadRASettingsOpts +---@field settings_file_pattern string|nil File name or pattern to search for. Defaults to 'rust-analyzer.json' +---@field default_settings table|nil Default settings to merge the loaded settings into + +--- Load rust-analyzer settings from a JSON file, +--- falling back to the default settings if none is found or if it cannot be decoded. +---@param project_root string|nil The project root +---@param opts LoadRASettingsOpts|nil +---@return table server_settings +---@see https://rust-analyzer.github.io/manual.html#configuration +function server.load_rust_analyzer_settings(project_root, opts) + local config = require('rustaceanvim.config.internal') + local compat = require('rustaceanvim.compat') + local os = require('rustaceanvim.os') + + local default_opts = { settings_file_pattern = 'rust-analyzer.json' } + opts = vim.tbl_deep_extend('force', {}, default_opts, opts or {}) + local default_settings = opts.default_settings or config.server.default_settings + local use_clippy = config.tools.enable_clippy and vim.fn.executable('cargo-clippy') == 1 + ---@diagnostic disable-next-line: undefined-field + if + default_settings['rust-analyzer'].check == nil + and use_clippy + and type(default_settings['rust-analyzer'].checkOnSave) ~= 'table' + then + ---@diagnostic disable-next-line: inject-field + default_settings['rust-analyzer'].check = { + allFeatures = true, + command = 'clippy', + extraArgs = { '--no-deps' }, + } + if type(default_settings['rust-analyzer'].checkOnSave) ~= 'boolean' then + ---@diagnostic disable-next-line: inject-field + default_settings['rust-analyzer'].checkOnSave = true + end + end + if not project_root then + return default_settings + end + local results = vim.fn.glob(compat.joinpath(project_root, opts.settings_file_pattern), true, true) + if #results == 0 then + return default_settings + end + local config_json = results[1] + local content = os.read_file(config_json) + if not content then + vim.notify('Could not read ' .. config_json, vim.log.levels.WARNING) + return default_settings + end + local json = require('rustaceanvim.config.json') + local rust_analyzer_settings = json.silent_decode(content) + local ra_key = 'rust-analyzer' + local has_ra_key = false + for key, _ in pairs(rust_analyzer_settings) do + if key:find(ra_key) ~= nil then + has_ra_key = true + break + end + end + if has_ra_key then + -- Settings json with "rust-analyzer" key + json.override_with_rust_analyzer_json_keys(default_settings, rust_analyzer_settings) + else + -- "rust-analyzer" settings are top level + json.override_with_json_keys(default_settings, rust_analyzer_settings) + end + return default_settings +end + +---@return lsp.ClientCapabilities +local function make_rustaceanvim_capabilities() + local capabilities = vim.lsp.protocol.make_client_capabilities() + + if vim.fn.has('nvim-0.10.0') == 1 then + -- snippets + -- This will also be added if cmp_nvim_lsp is detected. + capabilities.textDocument.completion.completionItem.snippetSupport = true + end + + -- send actions with hover request + capabilities.experimental = { + hoverActions = true, + hoverRange = true, + serverStatusNotification = true, + snippetTextEdit = true, + codeActionGroup = true, + ssr = true, + } + + -- enable auto-import + capabilities.textDocument.completion.completionItem.resolveSupport = { + properties = { 'documentation', 'detail', 'additionalTextEdits' }, + } + + -- rust analyzer goodies + local experimental_commands = { + 'rust-analyzer.runSingle', + 'rust-analyzer.showReferences', + 'rust-analyzer.gotoLocation', + 'editor.action.triggerParameterHints', + } + if package.loaded['dap'] ~= nil then + table.insert(experimental_commands, 'rust-analyzer.debugSingle') + end + + capabilities.experimental.commands = { + commands = experimental_commands, + } + + return capabilities +end + +---@param mod_name string +---@param callback fun(mod: table): lsp.ClientCapabilities +---@return lsp.ClientCapabilities +local function mk_capabilities_if_available(mod_name, callback) + local available, mod = pcall(require, mod_name) + if available and type(mod) == 'table' then + local ok, capabilities = pcall(callback, mod) + if ok then + return capabilities + end + end + return {} +end + +---@return lsp.ClientCapabilities +function server.create_client_capabilities() + local rs_capabilities = make_rustaceanvim_capabilities() + local cmp_capabilities = mk_capabilities_if_available('cmp_nvim_lsp', function(cmp_nvim_lsp) + return cmp_nvim_lsp.default_capabilities() + end) + local selection_range_capabilities = mk_capabilities_if_available('lsp-selection-range', function(lsp_selection_range) + return lsp_selection_range.update_capabilities {} + end) + local folding_range_capabilities = mk_capabilities_if_available('ufo', function(_) + return { + textDocument = { + foldingRange = { + dynamicRegistration = false, + lineFoldingOnly = true, + }, + }, + } + end) + return vim.tbl_deep_extend( + 'keep', + rs_capabilities, + cmp_capabilities, + selection_range_capabilities, + folding_range_capabilities + ) +end + +return server diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dap.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dap.lua new file mode 100644 index 00000000..c70641a1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/dap.lua @@ -0,0 +1,390 @@ +local config = require('rustaceanvim.config.internal') +local compat = require('rustaceanvim.compat') +local shell = require('rustaceanvim.shell') +local types = require('rustaceanvim.types.internal') + +---@param err string +local function scheduled_error(err) + vim.schedule(function() + vim.notify(err, vim.log.levels.ERROR) + end) +end + +local ok, _ = pcall(require, 'dap') +if not ok then + return { + ---@param on_error fun(err:string) + start = function(_, _, _, on_error) + on_error = on_error or scheduled_error + on_error('nvim-dap not found.') + end, + } +end +local dap = require('dap') + +local M = {} + +---@deprecated Use require('rustaceanvim.config').get_codelldb_adapter +function M.get_codelldb_adapter(...) + vim.deprecate( + "require('rustaceanvim.dap').get_codelldb_adapter", + "require('rustaceanvim.config').get_codelldb_adapter", + '4.0.0', + 'rustaceanvim' + ) + return require('rustaceanvim.config').get_codelldb_adapter(...) +end + +local function get_cargo_args_from_runnables_args(runnable_args) + local cargo_args = runnable_args.cargoArgs + + local message_json = '--message-format=json' + if not compat.list_contains(cargo_args, message_json) then + table.insert(cargo_args, message_json) + end + + for _, value in ipairs(runnable_args.cargoExtraArgs) do + if not compat.list_contains(cargo_args, value) then + table.insert(cargo_args, value) + end + end + + return cargo_args +end + +---@param callback fun(commit_hash:string) +local function get_rustc_commit_hash(callback) + compat.system({ 'rustc', '--version', '--verbose' }, nil, function(sc) + ---@cast sc vim.SystemCompleted + local result = sc.stdout + if sc.code ~= 0 or result == nil then + return + end + local commit_hash = result:match('commit%-hash:%s+([^\n]+)') + if not commit_hash then + return + end + callback(commit_hash) + end) +end + +local function get_rustc_sysroot(callback) + compat.system({ 'rustc', '--print', 'sysroot' }, nil, function(sc) + ---@cast sc vim.SystemCompleted + local result = sc.stdout + if sc.code ~= 0 or result == nil then + return + end + callback((result:gsub('\n$', ''))) + end) +end + +---@alias DapSourceMap {[string]: string} + +---@param tbl { [string]: string } +---@return string[][] +local function tbl_to_tuple_list(tbl) + ---@type string[][] + local result = {} + for k, v in pairs(tbl) do + ---@type string[] + local tuple = { k, v } + table.insert(result, tuple) + end + return result +end + +---codelldb expects a map, +-- while lldb expects a list of tuples. +---@param adapter DapExecutableConfig | DapServerConfig | boolean +---@param tbl { [string]: string } +---@return string[][] | { [string]: string } +local function format_source_map(adapter, tbl) + if adapter.type == 'server' then + return tbl + end + return tbl_to_tuple_list(tbl) +end + +---@type {[string]: DapSourceMap} +local source_maps = {} + +---See https://github.com/vadimcn/codelldb/issues/204 +---@param workspace_root? string +local function generate_source_map(workspace_root) + if not workspace_root or source_maps[workspace_root] then + return + end + get_rustc_commit_hash(function(commit_hash) + get_rustc_sysroot(function(rustc_sysroot) + local src_path + for _, src_dir in pairs { 'src', 'rustc-src' } do + src_path = compat.joinpath(rustc_sysroot, 'lib', 'rustlib', src_dir, 'rust') + if compat.uv.fs_stat(src_path) then + break + end + src_path = nil + end + if not src_path then + return + end + ---@type DapSourceMap + source_maps[workspace_root] = { + [compat.joinpath('/rustc', commit_hash)] = src_path, + } + end) + end) +end + +---@type {[string]: string[]} +local init_commands = {} + +---@param workspace_root? string +local function get_lldb_commands(workspace_root) + if not workspace_root or init_commands[workspace_root] then + return + end + get_rustc_sysroot(function(rustc_sysroot) + local script = compat.joinpath(rustc_sysroot, 'lib', 'rustlib', 'etc', 'lldb_lookup.py') + if not compat.uv.fs_stat(script) then + return + end + local script_import = 'command script import "' .. script .. '"' + local commands_file = compat.joinpath(rustc_sysroot, 'lib', 'rustlib', 'etc', 'lldb_commands') + local file = io.open(commands_file, 'r') + local workspace_root_cmds = {} + if file then + for line in file:lines() do + table.insert(workspace_root_cmds, line) + end + file:close() + end + table.insert(workspace_root_cmds, 1, script_import) + init_commands[workspace_root] = workspace_root_cmds + end) +end + +---map for codelldb, list of strings for lldb-dap +---@param adapter DapExecutableConfig | DapServerConfig +---@param key string +---@param segments string[] +---@param sep string +---@return {[string]: string} | string[] +local function format_environment_variable(adapter, key, segments, sep) + ---@diagnostic disable-next-line: missing-parameter + local existing = compat.uv.os_getenv(key) + existing = existing and sep .. existing or '' + local value = table.concat(segments, sep) .. existing + return adapter.type == 'server' and { [key] = value } or { key .. '=' .. value } +end + +---@type {[string]: EnvironmentMap} +local environments = {} + +-- Most succinct description: https://github.com/bevyengine/bevy/issues/2589#issuecomment-1753413600 +---@param adapter DapExecutableConfig | DapServerConfig +---@param workspace_root string | nil +local function add_dynamic_library_paths(adapter, workspace_root) + if not workspace_root or environments[workspace_root] then + return + end + compat.system({ 'rustc', '--print', 'target-libdir' }, nil, function(sc) + ---@cast sc vim.SystemCompleted + local result = sc.stdout + if sc.code ~= 0 or result == nil then + return + end + local rustc_target_path = (result:gsub('\n$', '')) + local target_path = compat.joinpath(workspace_root, 'target', 'debug', 'deps') + if shell.is_windows() then + environments[workspace_root] = environments[workspace_root] + or format_environment_variable(adapter, 'PATH', { rustc_target_path, target_path }, ';') + elseif shell.is_macos() then + ---@diagnostic disable-next-line: missing-parameter + environments[workspace_root] = environments[workspace_root] + or format_environment_variable(adapter, 'DKLD_LIBRARY_PATH', { rustc_target_path, target_path }, ':') + else + ---@diagnostic disable-next-line: missing-parameter + environments[workspace_root] = environments[workspace_root] + or format_environment_variable(adapter, 'LD_LIBRARY_PATH', { rustc_target_path, target_path }, ':') + end + end) +end + +---@param action fun() Action to perform +---@param desc? string Description of the action or nil to suppress warning +local function pall_with_warn(action, desc) + local success, err = pcall(action) + if not success and desc then + vim.schedule(function() + vim.notify(desc .. ' failed: ' .. err, vim.log.levels.WARN) + end) + end +end + +---@param adapter DapExecutableConfig | DapServerConfig +---@param args RARunnableArgs +---@param verbose? boolean +local function handle_configured_options(adapter, args, verbose) + local is_generate_source_map_enabled = types.evaluate(config.dap.auto_generate_source_map) + ---@cast is_generate_source_map_enabled boolean + if is_generate_source_map_enabled then + pall_with_warn(function() + generate_source_map(args.workspaceRoot) + end, verbose and 'Generating source map' or nil) + end + + local is_load_rust_types_enabled = types.evaluate(config.dap.load_rust_types) + ---@cast is_load_rust_types_enabled boolean + if is_load_rust_types_enabled then + pall_with_warn(function() + get_lldb_commands(args.workspaceRoot) + end, verbose and 'Getting LLDB commands' or nil) + end + + local is_add_dynamic_library_paths_enabled = types.evaluate(config.dap.add_dynamic_library_paths) + ---@cast is_add_dynamic_library_paths_enabled boolean + if is_add_dynamic_library_paths_enabled then + pall_with_warn(function() + add_dynamic_library_paths(adapter, args.workspaceRoot) + end, verbose and 'Adding library paths' or nil) + end +end + +---@param args RARunnableArgs +---@param verbose? boolean +---@param callback? fun(config: DapClientConfig) +---@param on_error? fun(err: string) +function M.start(args, verbose, callback, on_error) + if verbose then + on_error = on_error or scheduled_error + else + on_error = on_error or function() end + end + if type(callback) ~= 'function' then + callback = dap.run + end + local adapter = types.evaluate(config.dap.adapter) + --- @cast adapter DapExecutableConfig | DapServerConfig | disable + if adapter == false then + on_error('Debug adapter is disabled.') + return + end + + handle_configured_options(adapter, args, verbose) + + local cargo_args = get_cargo_args_from_runnables_args(args) + local cmd = vim.list_extend({ config.tools.cargo_override or 'cargo' }, cargo_args) + if verbose then + vim.notify('Compiling a debug build for debugging. This might take some time...') + end + compat.system(cmd, { cwd = args.workspaceRoot }, function(sc) + ---@cast sc vim.SystemCompleted + local output = sc.stdout + if sc.code ~= 0 or output == nil then + on_error( + 'An error occurred while compiling. Please fix all compilation issues and try again.' + .. '\nCommand: ' + .. table.concat(cmd, ' ') + .. (sc.stderr and '\nstderr: \n' .. sc.stderr or '') + .. (output and '\nstdout: ' .. output or '') + ) + return + end + vim.schedule(function() + local executables = {} + for value in output:gmatch('([^\n]*)\n?') do + local is_json, artifact = pcall(vim.fn.json_decode, value) + if not is_json then + goto loop_end + end + + -- only process artifact if it's valid json object and it is a compiler artifact + if type(artifact) ~= 'table' or artifact.reason ~= 'compiler-artifact' then + goto loop_end + end + + local is_binary = compat.list_contains(artifact.target.crate_types, 'bin') + local is_build_script = compat.list_contains(artifact.target.kind, 'custom-build') + local is_test = ((artifact.profile.test == true) and (artifact.executable ~= nil)) + or compat.list_contains(artifact.target.kind, 'test') + -- only add executable to the list if we want a binary debug and it is a binary + -- or if we want a test debug and it is a test + if + (cargo_args[1] == 'build' and is_binary and not is_build_script) + or (cargo_args[1] == 'test' and is_test) + then + table.insert(executables, artifact.executable) + end + + ::loop_end:: + end + -- only 1 executable is allowed for debugging - error out if zero or many were found + if #executables <= 0 then + on_error('No compilation artifacts found.') + return + end + if #executables > 1 then + on_error('Multiple compilation artifacts are not supported.') + return + end + + -- If the adapter is not defined elsewhere, use the adapter + -- defined in `config.dap.adapter` + local is_codelldb = adapter.type == 'server' + local adapter_key = is_codelldb and 'codelldb' or 'lldb' + if dap.adapters[adapter_key] == nil then + ---@TODO: Add nvim-dap to lua-ls lint + ---@diagnostic disable-next-line: assign-type-mismatch + dap.adapters[adapter_key] = adapter + end + + -- Use the first configuration, if it exists + local _, dap_config = next(dap.configurations.rust or {}) + + local local_config = types.evaluate(config.dap.configuration) + --- @cast local_config DapClientConfig | boolean + + ---@diagnostic disable-next-line: param-type-mismatch + local final_config = local_config ~= false and vim.deepcopy(local_config) or vim.deepcopy(dap_config) + --- @cast final_config DapClientConfig + + if dap.adapters[final_config.type] == nil then + on_error('No adapter exists named "' .. final_config.type .. '". See ":h dap-adapter" for more information') + return + end + + -- common entries + -- `program` and `args` aren't supported in probe-rs but are safely ignored + final_config.cwd = args.workspaceRoot + final_config.program = executables[1] + final_config.args = args.executableArgs or {} + local environment = args.workspaceRoot and environments[args.workspaceRoot] + final_config = next(environment or {}) ~= nil + and vim.tbl_deep_extend('force', final_config, { env = environment }) + or final_config + + if string.find(final_config.type, 'lldb') ~= nil then + -- lldb specific entries + final_config = args.workspaceRoot + and next(init_commands or {}) ~= nil + and vim.tbl_deep_extend('force', final_config, { initCommands = init_commands[args.workspaceRoot] }) + or final_config + + local source_map = args.workspaceRoot and source_maps[args.workspaceRoot] + final_config = source_map + and next(source_map or {}) ~= nil + and vim.tbl_deep_extend('force', final_config, { sourceMap = format_source_map(adapter, source_map) }) + or final_config + elseif string.find(final_config.type, 'probe%-rs') ~= nil then + -- probe-rs specific entries + final_config.coreConfigs[1].programBinary = final_config.program + end + + -- start debugging + callback(final_config) + end) + end) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/background.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/background.lua new file mode 100644 index 00000000..c4ae7b92 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/background.lua @@ -0,0 +1,52 @@ +local diag_namespace = vim.api.nvim_create_namespace('rustaceanvim') + +---@param output string +---@return string | nil +local function get_test_summary(output) + return output:match('(test result:.*)') +end + +---@type RustaceanExecutor +---@diagnostic disable-next-line: missing-fields +local M = {} + +---@class rustaceanvim.Diagnostic: vim.Diagnostic +---@field test_id string + +M.execute_command = function(command, args, cwd, opts) + ---@type RustaceanExecutorOpts + opts = vim.tbl_deep_extend('force', { bufnr = 0 }, opts or {}) + if vim.fn.has('nvim-0.10.0') ~= 1 then + vim.schedule(function() + vim.notify_once("the 'background' executor is not recommended for Neovim < 0.10.", vim.log.levels.WARN) + end) + return + end + + vim.diagnostic.reset(diag_namespace, opts.bufnr) + local is_single_test = args[1] == 'test' + local notify_prefix = (is_single_test and 'test ' or 'tests ') + local compat = require('rustaceanvim.compat') + local cmd = vim.list_extend({ command }, args) + local fname = vim.api.nvim_buf_get_name(opts.bufnr) + compat.system(cmd, { cwd = cwd }, function(sc) + ---@cast sc vim.SystemCompleted + if sc.code == 0 then + local summary = get_test_summary(sc.stdout or '') + vim.schedule(function() + vim.notify(summary and summary or (notify_prefix .. 'passed!'), vim.log.levels.INFO) + end) + return + end + local output = (sc.stderr or '') .. '\n' .. (sc.stdout or '') + local diagnostics = require('rustaceanvim.test').parse_diagnostics(fname, output) + local summary = get_test_summary(sc.stdout or '') + vim.schedule(function() + vim.diagnostic.set(diag_namespace, opts.bufnr, diagnostics) + vim.cmd.redraw() + vim.notify(summary and summary or (notify_prefix .. 'failed!'), vim.log.levels.ERROR) + end) + end) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/init.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/init.lua new file mode 100644 index 00000000..85b834a1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/init.lua @@ -0,0 +1,20 @@ +---@mod rustaceanvim.executors + +local termopen = require('rustaceanvim.executors.termopen') +local quickfix = require('rustaceanvim.executors.quickfix') +local toggleterm = require('rustaceanvim.executors.toggleterm') +local vimux = require('rustaceanvim.executors.vimux') +local background = require('rustaceanvim.executors.background') +local neotest = require('rustaceanvim.executors.neotest') + +---@type { [test_executor_alias]: RustaceanExecutor } +local M = {} + +M.termopen = termopen +M.quickfix = quickfix +M.toggleterm = toggleterm +M.vimux = vimux +M.background = background +M.neotest = neotest + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/meta.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/meta.lua new file mode 100644 index 00000000..da4a4bd1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/meta.lua @@ -0,0 +1,7 @@ +error('Cannot import a meta module') + +---@class RustaceanTestExecutor: RustaceanExecutor +---@field execute_command fun(cmd:string, args:string[], cwd:string|nil, opts?: RustaceanExecutorOpts) + +---@class RustaceanTestExecutorOpts: RustaceanExecutorOpts +---@field runnable? RARunnable diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/neotest.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/neotest.lua new file mode 100644 index 00000000..b721d1a7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/neotest.lua @@ -0,0 +1,20 @@ +local trans = require('rustaceanvim.neotest.trans') + +---@type RustaceanTestExecutor +---@diagnostic disable-next-line: missing-fields +local M = {} + +---@param opts RustaceanTestExecutorOpts +M.execute_command = function(_, _, _, opts) + ---@type RustaceanTestExecutorOpts + opts = vim.tbl_deep_extend('force', { bufnr = 0 }, opts or {}) + if type(opts.runnable) ~= 'table' then + vim.notify('rustaceanvim neotest executor called without a runnable. This is a bug!', vim.log.levels.ERROR) + end + local file = vim.api.nvim_buf_get_name(opts.bufnr) + local pos_id = trans.get_position_id(file, opts.runnable) + ---@diagnostic disable-next-line: undefined-field + require('neotest').run.run(pos_id) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/quickfix.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/quickfix.lua new file mode 100644 index 00000000..c7740182 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/quickfix.lua @@ -0,0 +1,46 @@ +local compat = require('rustaceanvim.compat') + +local function clear_qf() + vim.fn.setqflist({}, ' ', { title = 'cargo' }) +end + +local function scroll_qf() + if vim.bo.buftype ~= 'quickfix' then + vim.api.nvim_command('cbottom') + end +end + +local function append_qf(line) + vim.fn.setqflist({}, 'a', { lines = { line } }) + scroll_qf() +end + +local function copen() + vim.cmd('copen') +end + +---@type RustaceanExecutor +local M = { + execute_command = function(command, args, cwd, _) + -- open quickfix + copen() + -- go back to the previous window + vim.cmd.wincmd('p') + -- clear the quickfix + clear_qf() + + -- start compiling + local cmd = vim.list_extend({ command }, args) + compat.system( + cmd, + cwd and { cwd = cwd } or {}, + vim.schedule_wrap(function(sc) + ---@cast sc vim.SystemCompleted + local data = sc.stdout or sc.stderr + append_qf(data) + end) + ) + end, +} + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/termopen.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/termopen.lua new file mode 100644 index 00000000..63df8a3b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/termopen.lua @@ -0,0 +1,44 @@ +---@type integer | nil +local latest_buf_id = nil + +---@type RustaceanExecutor +local M = { + execute_command = function(command, args, cwd, _) + local shell = require('rustaceanvim.shell') + local ui = require('rustaceanvim.ui') + local commands = {} + if cwd then + table.insert(commands, shell.make_cd_command(cwd)) + end + table.insert(commands, shell.make_command_from_args(command, args)) + local full_command = shell.chain_commands(commands) + + -- check if a buffer with the latest id is already open, if it is then + -- delete it and continue + ui.delete_buf(latest_buf_id) + + -- create the new buffer + latest_buf_id = vim.api.nvim_create_buf(false, true) + + -- split the window to create a new buffer and set it to our window + ui.split(false, latest_buf_id) + + -- make the new buffer smaller + ui.resize(false, '-5') + + -- close the buffer when escape is pressed :) + vim.keymap.set('n', '', 'q', { buffer = latest_buf_id, noremap = true }) + + -- run the command + vim.fn.termopen(full_command) + + -- when the buffer is closed, set the latest buf id to nil else there are + -- some edge cases with the id being sit but a buffer not being open + local function onDetach(_, _) + latest_buf_id = nil + end + vim.api.nvim_buf_attach(latest_buf_id, false, { on_detach = onDetach }) + end, +} + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/toggleterm.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/toggleterm.lua new file mode 100644 index 00000000..b12c0401 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/toggleterm.lua @@ -0,0 +1,30 @@ +---@type RustaceanExecutor +local M = { + execute_command = function(command, args, cwd, _) + local ok, term = pcall(require, 'toggleterm.terminal') + if not ok then + vim.schedule(function() + vim.notify('toggleterm not found.', vim.log.levels.ERROR) + end) + return + end + + local shell = require('rustaceanvim.shell') + term.Terminal + :new({ + dir = cwd, + cmd = shell.make_command_from_args(command, args), + close_on_exit = false, + on_open = function(t) + -- enter normal mode + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes([[]], true, true, true), '', true) + + -- set close keymap + vim.keymap.set('n', 'q', 'close', { buffer = t.bufnr, noremap = true }) + end, + }) + :toggle() + end, +} + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/vimux.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/vimux.lua new file mode 100644 index 00000000..91797518 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/executors/vimux.lua @@ -0,0 +1,16 @@ +local shell = require('rustaceanvim.shell') + +---@type RustaceanExecutor +local M = { + execute_command = function(command, args, cwd, _) + local commands = {} + if cwd then + table.insert(commands, shell.make_cd_command(cwd)) + end + table.insert(commands, shell.make_command_from_args(command, args)) + local full_command = shell.chain_commands(commands) + vim.fn.VimuxRunCommand(full_command) + end, +} + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/health.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/health.lua new file mode 100644 index 00000000..0d77a7db --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/health.lua @@ -0,0 +1,281 @@ +---@mod rustaceanvim.health Health checks + +local health = {} + +local h = vim.health or require('health') +---@diagnostic disable-next-line: deprecated +local start = h.start or h.report_start +---@diagnostic disable-next-line: deprecated +local ok = h.ok or h.report_ok +---@diagnostic disable-next-line: deprecated +local error = h.error or h.report_error +---@diagnostic disable-next-line: deprecated +local warn = h.warn or h.report_warn + +---@class LuaDependency +---@field module string The name of a module +---@field optional fun():boolean Function that returns whether the dependency is optional +---@field url string URL (markdown) +---@field info string Additional information + +---@type LuaDependency[] +local lua_dependencies = { + { + module = 'dap', + optional = function() + return true + end, + url = '[mfussenegger/nvim-dap](https://github.com/mfussenegger/nvim-dap)', + info = 'Needed for debugging features', + }, +} + +---@class ExternalDependency +---@field name string Name of the dependency +---@field get_binaries fun():string[] Function that returns the binaries to check for +---@field is_installed? fun(bin: string):boolean Default: `vim.fn.executable(bin) == 1` +---@field optional fun():boolean Function that returns whether the dependency is optional +---@field url string URL (markdown) +---@field info string Additional information +---@field extra_checks_if_installed? fun(bin: string) Optional extra checks to perform if the dependency is installed +---@field extra_checks_if_not_installed? fun() Optional extra checks to perform if the dependency is not installed + +---@param dep LuaDependency +local function check_lua_dependency(dep) + if pcall(require, dep.module) then + ok(dep.url .. ' installed.') + return + end + if dep.optional() then + warn(('%s not installed. %s %s'):format(dep.module, dep.info, dep.url)) + else + error(('Lua dependency %s not found: %s'):format(dep.module, dep.url)) + end +end + +---@param dep ExternalDependency +---@return boolean is_installed +---@return string binary +---@return string version +local check_installed = function(dep) + local binaries = dep.get_binaries() + for _, binary in ipairs(binaries) do + local is_executable = dep.is_installed or function(bin) + return vim.fn.executable(bin) == 1 + end + if is_executable(binary) then + local handle = io.popen(binary .. ' --version') + if handle then + local binary_version, error_msg = handle:read('*a') + handle:close() + if error_msg then + return false, binary, error_msg + end + return true, binary, binary_version + end + return false, binary, 'Unable to determine version.' + end + end + return false, binaries[1], 'Could not find an executable binary.' +end + +---@param dep ExternalDependency +local function check_external_dependency(dep) + local is_installed, binary, version_or_err = check_installed(dep) + if is_installed then + ---@cast binary string + local mb_version_newline_idx = version_or_err and version_or_err:find('\n') + local mb_version_len = version_or_err + and (mb_version_newline_idx and mb_version_newline_idx - 1 or version_or_err:len()) + version_or_err = version_or_err and version_or_err:sub(0, mb_version_len) or '(unknown version)' + ok(('%s: found %s'):format(dep.name, version_or_err)) + if dep.extra_checks_if_installed then + dep.extra_checks_if_installed(binary) + end + return + end + if dep.optional() then + warn(([[ + %s: not found. + Install %s for extended capabilities. + %s + ]]):format(dep.name, dep.url, dep.info)) + else + error(([[ + %s: not found: %s + rustaceanvim requires %s. + %s + ]]):format(dep.name, version_or_err, dep.url, dep.info)) + end + if dep.extra_checks_if_not_installed then + dep.extra_checks_if_not_installed() + end +end + +---@param config RustaceanConfig +local function check_config(config) + start('Checking config') + if vim.g.rustaceanvim and not config.was_g_rustaceanvim_sourced then + error('vim.g.rustaceanvim is set, but it was sourced after rustaceanvim was initialized.') + end + local valid, err = require('rustaceanvim.config.check').validate(config) + if valid then + ok('No errors found in config.') + else + error(err or '' .. vim.g.rustaceanvim and '' or ' This looks like a plugin bug!') + end +end + +local function check_for_conflicts() + start('Checking for conflicting plugins') + require('rustaceanvim.config.check').check_for_lspconfig_conflict(error) + if package.loaded['rustaceanvim.neotest'] ~= nil and package.loaded['neotest-rust'] ~= nil then + error('rustaceanvim.neotest and neotest-rust are both loaded. This is likely a conflict.') + return + end + ok('No conflicting plugins detected.') +end + +local function check_tree_sitter() + start('Checking for tree-sitter parser') + local has_tree_sitter_rust_parser = #vim.api.nvim_get_runtime_file('parser/rust.so', true) > 0 + if has_tree_sitter_rust_parser then + ok('tree-sitter parser for Rust detected.') + else + warn("No tree-sitter parser for Rust detected. Required by 'Rustc unpretty' command.") + end +end + +function health.check() + local types = require('rustaceanvim.types.internal') + local config = require('rustaceanvim.config.internal') + + start('Checking for Lua dependencies') + for _, dep in ipairs(lua_dependencies) do + check_lua_dependency(dep) + end + + start('Checking external dependencies') + + local adapter = types.evaluate(config.dap.adapter) + ---@cast adapter DapExecutableConfig | DapServerConfig | boolean + + ---@return string + local function get_rust_analyzer_binary() + local default = 'rust-analyzer' + if not config then + return default + end + local cmd = types.evaluate(config.server.cmd) + if not cmd or #cmd == 0 then + return default + end + return cmd[1] + end + + ---@type ExternalDependency[] + local external_dependencies = { + { + name = 'rust-analyzer', + get_binaries = function() + return { get_rust_analyzer_binary() } + end, + is_installed = function(bin) + if type(vim.system) == 'function' then + local success = pcall(function() + vim.system { bin, '--version' } + end) + return success + end + return vim.fn.executable(bin) == 1 + end, + optional = function() + return false + end, + url = '[rust-analyzer](https://rust-analyzer.github.io/)', + info = 'Required by the LSP client.', + extra_checks_if_not_installed = function() + local bin = get_rust_analyzer_binary() + if vim.fn.executable(bin) == 1 then + warn("rust-analyzer wrapper detected. Run 'rustup component add rust-analyzer' to install rust-analyzer.") + end + end, + }, + { + name = 'Cargo', + get_binaries = function() + return { 'cargo' } + end, + optional = function() + return true + end, + url = '[Cargo](https://doc.rust-lang.org/cargo/)', + info = [[ + The Rust package manager. + Required by rust-analyzer for non-standalone files, and for debugging features. + Not required in standalone files. + ]], + }, + { + name = 'rustc', + get_binaries = function() + return { 'rustc' } + end, + optional = function() + return true + end, + url = '[rustc](https://doc.rust-lang.org/rustc/what-is-rustc.html)', + info = [[ + The Rust compiler. + Called by `:RustLsp explainError`. + ]], + }, + } + + if config.tools.cargo_override then + table.insert(external_dependencies, { + name = 'Cargo override: ' .. config.tools.cargo_override, + get_binaries = function() + return { config.tools.cargo_override } + end, + optional = function() + return true + end, + url = '', + info = [[ + Set in the config to override the 'cargo' command for debugging and testing. + ]], + }) + end + + if adapter ~= false then + table.insert(external_dependencies, { + name = adapter.name or 'debug adapter', + get_binaries = function() + if adapter.type == 'executable' then + ---@cast adapter DapExecutableConfig + return { 'lldb', adapter.command } + else + ---@cast adapter DapServerConfig + return { 'codelldb', adapter.executable.command } + end + end, + optional = function() + return true + end, + url = '[lldb](https://lldb.llvm.org/)', + info = [[ + A debug adapter (defaults to: LLDB). + Required for debugging features. + ]], + }) + end + for _, dep in ipairs(external_dependencies) do + check_external_dependency(dep) + end + check_config(config) + check_for_conflicts() + check_tree_sitter() +end + +return health diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/hover_actions.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/hover_actions.lua new file mode 100644 index 00000000..3194d08b --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/hover_actions.lua @@ -0,0 +1,146 @@ +local config = require('rustaceanvim.config.internal') +local lsp_util = vim.lsp.util + +local M = {} + +local function get_params() + return lsp_util.make_position_params(0, nil) +end + +---@class HoverActionsState +local _state = { + ---@type integer + winnr = nil, + ---@type unknown + commands = nil, +} + +local function close_hover() + local ui = require('rustaceanvim.ui') + ui.close_win(_state.winnr) +end + +local function execute_rust_analyzer_command(action, ctx) + local fn = vim.lsp.commands[action.command] + if fn then + fn(action, ctx) + end +end + +-- run the command under the cursor, if the thing under the cursor is not the +-- command then do nothing +---@param ctx table +local function run_command(ctx) + local winnr = vim.api.nvim_get_current_win() + local line = vim.api.nvim_win_get_cursor(winnr)[1] + + if line > #_state.commands then + return + end + + local action = _state.commands[line] + + close_hover() + execute_rust_analyzer_command(action, ctx) +end + +---@return string[] +local function parse_commands() + local prompt = {} + + for i, value in ipairs(_state.commands) do + if value.command == 'rust-analyzer.gotoLocation' then + table.insert(prompt, string.format('%d. Go to %s (%s)', i, value.title, value.tooltip)) + elseif value.command == 'rust-analyzer.showReferences' then + table.insert(prompt, string.format('%d. %s', i, 'Go to ' .. value.title)) + else + table.insert(prompt, string.format('%d. %s', i, value.title)) + end + end + + return prompt +end + +function M.handler(_, result, ctx) + if not (result and result.contents) then + -- return { 'No information available' } + return + end + + local markdown_lines = lsp_util.convert_input_to_markdown_lines(result.contents, {}) + if result.actions then + _state.commands = result.actions[1].commands + local prompt = parse_commands() + local l = {} + + for _, value in ipairs(prompt) do + table.insert(l, value) + end + table.insert(l, '---') + + markdown_lines = vim.list_extend(l, markdown_lines) + end + + if vim.tbl_isempty(markdown_lines) then + -- return { 'No information available' } + return + end + + -- NOTE: This is for backward compatibility + local win_opt = vim.tbl_deep_extend('force', config.tools.float_win_config, config.tools.hover_actions) + + local bufnr, winnr = lsp_util.open_floating_preview( + markdown_lines, + 'markdown', + vim.tbl_extend('keep', win_opt, { + focusable = true, + focus_id = 'rust-analyzer-hover-actions', + close_events = { 'CursorMoved', 'BufHidden', 'InsertCharPre' }, + }) + ) + + if win_opt.auto_focus then + vim.api.nvim_set_current_win(winnr) + end + + if _state.winnr ~= nil then + return + end + + -- update the window number here so that we can map escape to close even + -- when there are no actions, update the rest of the state later + _state.winnr = winnr + vim.keymap.set('n', 'q', close_hover, { buffer = bufnr, noremap = true, silent = true }) + vim.keymap.set('n', '', close_hover, { buffer = bufnr, noremap = true, silent = true }) + + vim.api.nvim_buf_attach(bufnr, false, { + on_detach = function() + _state.winnr = nil + end, + }) + + --- stop here if there are no possible actions + if result.actions == nil then + return + end + + -- makes more sense in a dropdown-ish ui + vim.wo[winnr].cursorline = true + + -- explicitly disable signcolumn + vim.wo[winnr].signcolumn = 'no' + + -- run the command under the cursor + vim.keymap.set('n', '', function() + run_command(ctx) + end, { buffer = bufnr, noremap = true, silent = true }) +end + +local rl = require('rustaceanvim.rust_analyzer') + +--- Sends the request to rust-analyzer to get hover actions and handle it +function M.hover_actions() + rl.buf_request(0, 'textDocument/hover', get_params(), M.handler) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/init.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/init.lua new file mode 100644 index 00000000..51d9ead7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/init.lua @@ -0,0 +1,82 @@ +---@toc rustaceanvim.contents + +---@mod intro Introduction +---@brief [[ +---This plugin automatically configures the `rust-analyzer` builtin LSP client +---and integrates with other rust tools. +---@brief ]] +--- +---@mod rustaceanvim +--- +---@brief [[ +--- +---Commands: +--- +--- ':RustAnalyzer start' - Start the LSP client. +--- ':RustAnalyzer stop' - Stop the LSP client. +--- ':RustAnalyzer restart' - Restart the LSP client. +--- ':RustAnalyzer reloadSettings' - Reload settings for the LSP client. +--- +---The ':RustLsp[!]' command is available after the LSP client has initialized. +---It accepts the following subcommands: +--- +--- 'runnables {args[]}?' - Run tests, executables, etc. +--- ':RustLsp!' means run the last runnable (ignores any args). +--- `args[]` allows you to override the executable's arguments. +--- 'run {args[]}?' - Like 'runnables', but runs the target at the current cursor position. +--- 'debuggables {args[]}?' - Debug tests, executables, etc. (requires |nvim-dap|). +--- ':RustLsp!' means run the last debuggable (ignores any args). +--- `args[]` allows you to override the executable's arguments. +--- 'debug {args[]}?' - Like 'debuggables', but debugs the target at the current cursor position. +--- 'testables {args[]}?' - Run tests +--- ':RustLsp!' means run the last testable (ignores any args). +--- `args[]` allows you to override the executable's arguments. +--- 'expandMacro' - Expand macros recursively. +--- 'moveItem {up|down}' - Move items up or down. +--- 'hover {actions|range}' - Hover actions, or hover over visually selected range. +--- 'explainError' - Display a hover window with explanations form the Rust error index. +--- Like |vim.diagnostic.goto_next|, |explainError| will cycle diagnostics, +--- starting at the cursor position, until it can find a diagnostic with +--- an error code. +--- 'renderDiagnostic' - Display a hover window with the rendered diagnostic, +--- as displayed during `cargo build`. +--- Like |vim.diagnostic.goto_next|, |renderDiagnostic| will cycle diagnostics, +--- starting at the cursor position, until it can find a diagnostic with +--- rendered data. +--- 'openCargo' - Open the Cargo.toml file for the current package. +--- 'openDocs' - Open docs.rs documentation for the symbol under the cursor. +--- 'parentModule' - Open the current module's parent module. +--- 'workspaceSymbol {onlyTypes?|allSymbols?} {query?}' +--- Filtered workspace symbol search. +--- When run with a bang (`:RustLsp! workspaceSymbol ...`), +--- rust-analyzer will include dependencies in the search. +--- You can also configure rust-analyzer so that |vim.lsp.buf.workspace_symbol| +--- supports filtering (with a # suffix to the query) or searching dependencies. +--- 'joinLines' - Join adjacent lines. +--- 'ssr {query}' - Structural search and replace. +--- Searches the entire buffer in normal mode. +--- Searches the selected region in visual mode. +--- 'crateGraph {backend}' - Create and view a crate graph with graphviz. +--- 'syntaxTree' - View the syntax tree. +--- 'view {mir|hir}' - View MIR or HIR. +--- 'flyCheck' {run?|clear?|cancel?} +--- - Run `cargo check` or another compatible command (f.x. `clippy`) +--- in a background thread and provide LSP diagnostics based on +--- the output of the command. +--- Useful in large projects where running `cargo check` on each save +--- can be costly. +--- Defaults to `flyCheck run` if called without an argument. +--- 'logFile' - Open the rust-analyzer log file. +--- +---The ':Rustc' command can be used to interact with rustc. +---It accepts the following subcommands: +--- +--- 'unpretty {args[]}' - Opens a buffer with a textual representation of the MIR or others things, +--- of the function closest to the cursor. +--- Achieves an experience similar to Rust Playground. +--- NOTE: This currently requires a tree-sitter parser for Rust, +--- and a nightly compiler toolchain. +---@brief ]] + +local M = {} +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/lsp.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/lsp.lua new file mode 100644 index 00000000..1efa5daa --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/lsp.lua @@ -0,0 +1,290 @@ +local M = {} +---@type RustaceanConfig +local config = require('rustaceanvim.config.internal') +local compat = require('rustaceanvim.compat') +local types = require('rustaceanvim.types.internal') +local rust_analyzer = require('rustaceanvim.rust_analyzer') +local server_status = require('rustaceanvim.server_status') +local cargo = require('rustaceanvim.cargo') +local os = require('rustaceanvim.os') + +local function override_apply_text_edits() + local old_func = vim.lsp.util.apply_text_edits + ---@diagnostic disable-next-line + vim.lsp.util.apply_text_edits = function(edits, bufnr, offset_encoding) + local overrides = require('rustaceanvim.overrides') + overrides.snippet_text_edits_to_text_edits(edits) + old_func(edits, bufnr, offset_encoding) + end +end + +---@param client lsp.Client +---@param root_dir string +---@return boolean +local function is_in_workspace(client, root_dir) + if not client.workspace_folders then + return false + end + + for _, dir in ipairs(client.workspace_folders) do + if (root_dir .. '/'):sub(1, #dir.name + 1) == dir.name .. '/' then + return true + end + end + + return false +end + +---Searches upward for a .vscode/settings.json that contains rust-analyzer +---settings and returns them. +---@param bufname string +---@return table server_settings or an empty table if no settings were found +local function find_vscode_settings(bufname) + local settings = {} + local found_dirs = vim.fs.find({ '.vscode' }, { upward = true, path = vim.fs.dirname(bufname), type = 'directory' }) + if vim.tbl_isempty(found_dirs) then + return settings + end + local vscode_dir = found_dirs[1] + local results = vim.fn.glob(compat.joinpath(vscode_dir, 'settings.json'), true, true) + if vim.tbl_isempty(results) then + return settings + end + local content = os.read_file(results[1]) + return content and require('rustaceanvim.config.json').silent_decode(content) or {} +end + +---Generate the settings from config and vscode settings if found. +---settings and returns them. +---@param bufname string +---@param root_dir string | nil +---@param client_config table +---@return table server_settings or an empty table if no settings were found +local function get_start_settings(bufname, root_dir, client_config) + local settings = client_config.settings + local evaluated_settings = type(settings) == 'function' and settings(root_dir, client_config.default_settings) + or settings + + if config.server.load_vscode_settings then + local json_settings = find_vscode_settings(bufname) + require('rustaceanvim.config.json').override_with_rust_analyzer_json_keys(evaluated_settings, json_settings) + end + + return evaluated_settings +end + +---@class LspStartConfig: RustaceanLspClientConfig +---@field root_dir string | nil +---@field init_options? table +---@field settings table +---@field cmd string[] +---@field name string +---@field filetypes string[] +---@field capabilities table +---@field handlers lsp.Handler[] +---@field on_init function +---@field on_attach function +---@field on_exit function + +--- Start or attach the LSP client +---@param bufnr? number The buffer number (optional), defaults to the current buffer +---@return integer|nil client_id The LSP client ID +M.start = function(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + local bufname = vim.api.nvim_buf_get_name(bufnr) + local client_config = config.server + ---@type LspStartConfig + local lsp_start_config = vim.tbl_deep_extend('force', {}, client_config) + local root_dir = cargo.get_root_dir(bufname) + if not root_dir then + --- No project root found. Start in detached/standalone mode. + root_dir = vim.fs.dirname(bufname) + lsp_start_config.init_options = { detachedFiles = { bufname } } + end + root_dir = os.normalize_path_on_windows(root_dir) + lsp_start_config.root_dir = root_dir + + lsp_start_config.settings = get_start_settings(bufname, root_dir, client_config) + + -- Check if a client is already running and add the workspace folder if necessary. + for _, client in pairs(rust_analyzer.get_active_rustaceanvim_clients()) do + if root_dir and not is_in_workspace(client, root_dir) then + local workspace_folder = { uri = vim.uri_from_fname(root_dir), name = root_dir } + local params = { + event = { + added = { workspace_folder }, + removed = {}, + }, + } + client.rpc.notify('workspace/didChangeWorkspaceFolders', params) + if not client.workspace_folders then + client.workspace_folders = {} + end + table.insert(client.workspace_folders, workspace_folder) + vim.lsp.buf_attach_client(bufnr, client.id) + return + end + end + + local rust_analyzer_cmd = types.evaluate(client_config.cmd) + if #rust_analyzer_cmd == 0 or vim.fn.executable(rust_analyzer_cmd[1]) ~= 1 then + vim.notify('rust-analyzer binary not found.', vim.log.levels.ERROR) + return + end + ---@cast rust_analyzer_cmd string[] + lsp_start_config.cmd = rust_analyzer_cmd + lsp_start_config.name = 'rust-analyzer' + lsp_start_config.filetypes = { 'rust' } + + local custom_handlers = {} + custom_handlers['experimental/serverStatus'] = server_status.handler + + if config.tools.hover_actions.replace_builtin_hover then + custom_handlers['textDocument/hover'] = require('rustaceanvim.hover_actions').handler + end + + lsp_start_config.handlers = vim.tbl_deep_extend('force', custom_handlers, lsp_start_config.handlers or {}) + + local commands = require('rustaceanvim.commands') + local old_on_init = lsp_start_config.on_init + lsp_start_config.on_init = function(...) + override_apply_text_edits() + commands.create_rust_lsp_command() + if type(old_on_init) == 'function' then + old_on_init(...) + end + end + + local old_on_attach = lsp_start_config.on_attach + lsp_start_config.on_attach = function(...) + if type(old_on_attach) == 'function' then + old_on_attach(...) + end + end + + local old_on_exit = lsp_start_config.on_exit + lsp_start_config.on_exit = function(...) + override_apply_text_edits() + -- on_exit runs in_fast_event + vim.schedule(function() + commands.delete_rust_lsp_command() + end) + if type(old_on_exit) == 'function' then + old_on_exit(...) + end + end + + return vim.lsp.start(lsp_start_config) +end + +---Stop the LSP client. +---@param bufnr? number The buffer number, defaults to the current buffer +---@return table[] clients A list of clients that will be stopped +M.stop = function(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + local clients = rust_analyzer.get_active_rustaceanvim_clients(bufnr) + vim.lsp.stop_client(clients) + if type(clients) == 'table' then + ---@cast clients lsp.Client[] + for _, client in ipairs(clients) do + server_status.reset_client_state(client.id) + end + else + ---@cast clients lsp.Client + server_status.reset_client_state(clients.id) + end + return clients +end + +---Reload settings for the LSP client. +---@param bufnr? number The buffer number, defaults to the current buffer +---@return table[] clients A list of clients that will be have their settings reloaded +M.reload_settings = function(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + local clients = rust_analyzer.get_active_rustaceanvim_clients(bufnr) + ---@cast clients lsp.Client[] + for _, client in ipairs(clients) do + local settings = get_start_settings(vim.api.nvim_buf_get_name(bufnr), client.config.root_dir, config.server) + ---@diagnostic disable-next-line: inject-field + client.settings = settings + client.notify('workspace/didChangeConfiguration', { + settings = client.settings, + }) + end + return clients +end + +---Restart the LSP client. +---Fails silently if the buffer's filetype is not one of the filetypes specified in the config. +---@param bufnr? number The buffer number (optional), defaults to the current buffer +---@return number|nil client_id The LSP client ID after restart +M.restart = function(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + local clients = M.stop(bufnr) + local timer, _, _ = compat.uv.new_timer() + if not timer then + -- TODO: Log error when logging is implemented + return + end + local attempts_to_live = 50 + local stopped_client_count = 0 + timer:start(200, 100, function() + for _, client in ipairs(clients) do + if client:is_stopped() then + stopped_client_count = stopped_client_count + 1 + vim.schedule(function() + M.start(bufnr) + end) + end + end + if stopped_client_count >= #clients then + timer:stop() + attempts_to_live = 0 + elseif attempts_to_live <= 0 then + vim.notify('rustaceanvim.lsp: Could not restart all LSP clients.', vim.log.levels.ERROR) + timer:stop() + attempts_to_live = 0 + end + attempts_to_live = attempts_to_live - 1 + end) +end + +---@enum RustAnalyzerCmd +local RustAnalyzerCmd = { + start = 'start', + stop = 'stop', + restart = 'restart', + reload_settings = 'reloadSettings', +} + +local function rust_analyzer_cmd(opts) + local fargs = opts.fargs + local cmd = fargs[1] + ---@cast cmd RustAnalyzerCmd + if cmd == RustAnalyzerCmd.start then + M.start() + elseif cmd == RustAnalyzerCmd.stop then + M.stop() + elseif cmd == RustAnalyzerCmd.restart then + M.restart() + elseif cmd == RustAnalyzerCmd.reload_settings then + M.reload_settings() + end +end + +vim.api.nvim_create_user_command('RustAnalyzer', rust_analyzer_cmd, { + nargs = '+', + desc = 'Starts or stops the rust-analyzer LSP client', + complete = function(arg_lead, cmdline, _) + local clients = rust_analyzer.get_active_rustaceanvim_clients() + ---@type RustAnalyzerCmd[] + local commands = #clients == 0 and { 'start' } or { 'stop', 'restart', 'reloadSettings' } + if cmdline:match('^RustAnalyzer%s+%w*$') then + return vim.tbl_filter(function(command) + return command:find(arg_lead) ~= nil + end, commands) + end + end, +}) + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/neotest/init.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/neotest/init.lua new file mode 100644 index 00000000..9eb7cbae --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/neotest/init.lua @@ -0,0 +1,395 @@ +---@mod rustaceanvim.neotest +--- +---@brief [[ +--- +---A |neotest| adapter for rust, powered by rustaceanvim. +--- +---If you add this to neotest: +--- +---> +---require('neotest').setup { +--- -- ..., +--- adapters = { +--- -- ..., +--- require('rustaceanvim.neotest') +--- }, +---} +---< +--- +---this plugin will configure itself to use |neotest| +---as a test executor, and |neotest| will use rust-analyzer +---for test discovery and command construction. +--- +---Note: If you use this adapter, do not add the neotest-rust adapter +---(another plugin). +--- +---@brief ]] + +---@diagnostic disable: duplicate-set-field + +local lib = require('neotest.lib') +local nio = require('nio') +local trans = require('rustaceanvim.neotest.trans') +local cargo = require('rustaceanvim.cargo') +local overrides = require('rustaceanvim.overrides') +local compat = require('rustaceanvim.compat') + +---@package +---@type neotest.Adapter +local NeotestAdapter = { name = 'rustaceanvim' } + +---@package +---@param file_name string +---@return string | nil +NeotestAdapter.root = function(file_name) + return cargo.get_root_dir(file_name) +end + +---@package +---@param rel_path string Path to directory, relative to root +---@return boolean +NeotestAdapter.filter_dir = function(_, rel_path, _) + return rel_path ~= 'target' +end + +---@package +---@param file_path string +---@return boolean +NeotestAdapter.is_test_file = function(file_path) + return vim.endswith(file_path, '.rs') +end + +---@package +---@class rustaceanvim.neotest.Position: neotest.Position +---@field runnable? RARunnable + +----@param name string +----@return integer +local function find_buffer_by_name(name) + for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do + local buf_name = vim.api.nvim_buf_get_name(bufnr) + if buf_name == name then + return bufnr + end + end + return 0 +end + +---@package +---@class nio.rustaceanvim.Client: nio.lsp.Client +---@field request nio.rustaceanvim.RequestClient Interface to all requests that can be sent by the client +---@field config vim.lsp.ClientConfig + +---@package +---@class nio.rustaceanvim.RequestClient: nio.lsp.RequestClient +---@field experimental_runnables fun(args: nio.lsp.types.ImplementationParams, bufnr: integer?, opts: nio.lsp.RequestOpts): nio.lsp.types.ResponseError|nil, RARunnable[]|nil + +---@package +---@param file_path string +---@return neotest.Tree +NeotestAdapter.discover_positions = function(file_path) + ---@type rustaceanvim.neotest.Position[] + local positions = {} + + local lsp_client = require('rustaceanvim.rust_analyzer').get_client_for_file(file_path, 'experimental/runnables') + if not lsp_client then + ---@diagnostic disable-next-line: missing-parameter + return lib.positions.parse_tree(positions) + end + local nio_client = nio.lsp.get_client_by_id(lsp_client.id) + ---@cast nio_client nio.rustaceanvim.Client + local bufnr = find_buffer_by_name(file_path) + local params = { + textDocument = { + uri = vim.uri_from_fname(file_path), + }, + position = nil, + } + local err, runnables = nio_client.request.experimental_runnables(params, bufnr, { + timeout = 100000, + }) + + if err or type(runnables) ~= 'table' or #runnables == 0 then + ---@diagnostic disable-next-line: missing-parameter + return lib.positions.parse_tree(positions) + end + + local max_end_row = 0 + for _, runnable in pairs(runnables) do + local pos = trans.runnable_to_position(file_path, runnable) + if pos then + max_end_row = math.max(max_end_row, pos.range[3]) + if pos.type ~= 'dir' then + table.insert(positions, pos) + end + end + end + ---@diagnostic disable-next-line: cast-type-mismatch + ---@cast runnables RARunnable[] + + ---@type { [string]: neotest.Position } + local tests_by_name = {} + ---@type rustaceanvim.neotest.Position[] + local namespaces = {} + for _, pos in pairs(positions) do + if pos.type == 'test' then + tests_by_name[pos.name] = pos + elseif pos.type == 'namespace' then + table.insert(namespaces, pos) + end + end + + -- sort namespaces by name from longest to shortest + table.sort(namespaces, function(a, b) + return #a.name > #b.name + end) + + ---@type { [string]: rustaceanvim.neotest.Position[] } + local positions_by_namespace = {} + -- group tests by their longest matching namespace + for _, namespace in ipairs(namespaces) do + if namespace.name ~= '' then + ---@type string[] + local child_keys = vim.tbl_filter(function(name) + return vim.startswith(name, namespace.name .. '::') + end, vim.tbl_keys(tests_by_name)) + local children = { namespace } + for _, key in ipairs(child_keys) do + local child_pos = tests_by_name[key] + tests_by_name[key] = nil + --- strip the namespace and "::" from the name + child_pos.name = child_pos.name:sub(#namespace.name + 3, #child_pos.name) + table.insert(children, child_pos) + end + positions_by_namespace[namespace.name] = children + end + end + + -- nest child namespaces in their parent namespace + for i, namespace in ipairs(namespaces) do + ---@type rustaceanvim.neotest.Position? + local parent = nil + -- search remaning namespaces for the longest matching parent namespace + for _, other_namespace in ipairs { unpack(namespaces, i + 1) } do + if vim.startswith(namespace.name, other_namespace.name .. '::') then + parent = other_namespace + break + end + end + if parent ~= nil then + local namespace_name = namespace.name + local children = positions_by_namespace[namespace_name] + -- strip parent namespace + "::" + children[1].name = children[1].name:sub(#parent.name + 3, #namespace_name) + table.insert(positions_by_namespace[parent.name], children) + positions_by_namespace[namespace_name] = nil + end + end + + local sorted_positions = {} + for _, namespace_positions in pairs(positions_by_namespace) do + table.insert(sorted_positions, namespace_positions) + end + -- any remaning tests had no parent namespace + vim.list_extend(sorted_positions, vim.tbl_values(tests_by_name)) + + -- sort positions by their start range + local function sort_positions(to_sort) + for _, item in ipairs(to_sort) do + if compat.islist(item) then + sort_positions(item) + end + end + + -- pop header from the list before sorting since it's used to sort in its parent's context + local header = table.remove(to_sort, 1) + table.sort(to_sort, function(a, b) + local a_item = compat.islist(a) and a[1] or a + local b_item = compat.islist(b) and b[1] or b + if a_item.range[1] == b_item.range[1] then + return a_item.name < b_item.name + else + return a_item.range[1] < b_item.range[1] + end + end) + table.insert(to_sort, 1, header) + end + sort_positions(sorted_positions) + + local file_pos = { + id = file_path, + name = vim.fn.fnamemodify(file_path, ':t'), + type = 'file', + path = file_path, + range = { 0, 0, max_end_row, 0 }, + -- use the shortest namespace for the file runnable + runnable = #namespaces > 0 and namespaces[#namespaces].runnable or nil, + } + table.insert(sorted_positions, 1, file_pos) + + return require('neotest.types.tree').from_list(sorted_positions, function(x) + return x.name + end) +end + +---@package +---@class rustaceanvim.neotest.RunSpec: neotest.RunSpec +---@field context rustaceanvim.neotest.RunContext + +---@package +---@class rustaceanvim.neotest.RunContext +---@field file string +---@field pos_id string +---@field type neotest.PositionType +---@field tree neotest.Tree + +---@package +---@param run_args neotest.RunArgs +---@return neotest.RunSpec|nil +---@private +function NeotestAdapter.build_spec(run_args) + local supported_types = { 'test', 'namespace', 'file', 'dir' } + local tree = run_args and run_args.tree + if not tree then + return + end + local pos = tree:data() + ---@cast pos rustaceanvim.neotest.Position + if not vim.tbl_contains(supported_types, pos.type) then + return + end + local runnable = pos.runnable + if not runnable then + return + end + local context = { + file = pos.path, + pos_id = pos.id, + type = pos.type, + tree = tree, + } + local exe, args, cwd = require('rustaceanvim.runnables').get_command(runnable) + if run_args.strategy == 'dap' then + local dap = require('rustaceanvim.dap') + overrides.sanitize_command_for_debugging(runnable.args.cargoArgs) + local future = nio.control.future() + dap.start(runnable.args, false, function(strategy) + future.set(strategy) + end, function(err) + future.set_error(err) + end) + local ok, strategy = pcall(future.wait) + if not ok then + ---@cast strategy string + lib.notify(strategy, vim.log.levels.ERROR) + end + ---@cast strategy DapClientConfig + ---@type rustaceanvim.neotest.RunSpec + local run_spec = { + cwd = cwd, + context = context, + strategy = strategy, + } + return run_spec + else + overrides.undo_debug_sanitize(runnable.args.cargoArgs) + end + ---@type rustaceanvim.neotest.RunSpec + ---@diagnostic disable-next-line: missing-fields + local run_spec = { + command = vim.list_extend({ exe }, args), + cwd = cwd, + context = context, + } + return run_spec +end + +---@package +---Get the file root from a test tree. +---@param tree neotest.Tree The test tree. +---@return neotest.Tree file_root The file root position. +local function get_file_root(tree) + for _, node in tree:iter_parents() do + local data = node and node:data() + if data and not vim.tbl_contains({ 'test', 'namespace' }, data.type) then + return node + end + end + return tree +end + +---@package +---@param spec neotest.RunSpec +---@param strategy_result neotest.StrategyResult +---@return table results +function NeotestAdapter.results(spec, strategy_result) + ---@type table + local results = {} + ---@type rustaceanvim.neotest.RunContext + local context = spec.context + local ctx_pos_id = context.pos_id + ---@type string + local output_content = lib.files.read(strategy_result.output) + if strategy_result.code == 0 then + results[ctx_pos_id] = { + status = 'passed', + output = strategy_result.output, + } + return results + end + ---@type table + local errors_by_test_id = {} + output_content = output_content:gsub('\r\n', '\n') + local diagnostics = require('rustaceanvim.test').parse_diagnostics(context.file, output_content) + for _, diagnostic in pairs(diagnostics) do + ---@type neotest.Error + local err = { + line = diagnostic.lnum, + message = diagnostic.message, + } + errors_by_test_id[diagnostic.test_id] = errors_by_test_id[diagnostic.test_id] or {} + table.insert(errors_by_test_id[diagnostic.test_id], err) + end + if not vim.tbl_contains({ 'file', 'test', 'namespace' }, context.type) then + return results + end + results[ctx_pos_id] = { + status = 'failed', + output = strategy_result.output, + } + local has_failures = not vim.tbl_isempty(diagnostics) + for _, node in get_file_root(context.tree):iter_nodes() do + local data = node:data() + for test_id, errors in pairs(errors_by_test_id) do + if vim.endswith(data.id, test_id) then + results[data.id] = { + status = 'failed', + errors = errors, + short = output_content, + } + elseif has_failures and data.type == 'test' then + -- Initialise as skipped. Passed positions will be parsed and set later. + results[data.id] = { + status = 'skipped', + } + end + end + end + if has_failures then + local pass_positions = output_content:gmatch('test%s(%S+)%s...%sok') + for pos in pass_positions do + results[trans.get_position_id(context.file, pos)] = { + status = 'passed', + } + end + end + return results +end + +setmetatable(NeotestAdapter, { + __call = function() + return NeotestAdapter + end, +}) + +return NeotestAdapter diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/neotest/trans.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/neotest/trans.lua new file mode 100644 index 00000000..10f03459 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/neotest/trans.lua @@ -0,0 +1,68 @@ +local M = {} + +---@param runnable RARunnable +---@return string | nil +local function get_test_path(runnable) + local executableArgs = runnable.args and runnable.args.executableArgs or {} + return #executableArgs > 0 and executableArgs[1] or nil +end + +---@overload fun(file_path: string, test_path: string | nil) +---@overload fun(file_path: string, runnable: RARunnable) +---@return string +function M.get_position_id(file_path, runnable) + local test_path = runnable + if type(runnable) == 'table' then + test_path = get_test_path(runnable) + end + ---@cast test_path string | nil + return test_path and table.concat(vim.list_extend({ file_path }, { test_path }), '::') or file_path +end + +---@param file_path string +---@param runnable RARunnable +---@return rustaceanvim.neotest.Position | nil +function M.runnable_to_position(file_path, runnable) + local cargoArgs = runnable.args and runnable.args.cargoArgs or {} + if #cargoArgs > 0 and vim.startswith(cargoArgs[1], 'test') then + ---@type neotest.PositionType + local type + if vim.startswith(runnable.label, 'cargo test -p') then + type = 'dir' + elseif vim.startswith(runnable.label, 'test-mod') then + type = 'namespace' + elseif vim.startswith(runnable.label, 'test') or vim.startswith(runnable.label, 'doctest') then + type = 'test' + else + return + end + local location = runnable.location + local start_row, start_col, end_row, end_col = 0, 0, 0, 0 + if location then + start_row = location.targetRange.start.line + 1 + start_col = location.targetRange.start.character + end_row = location.targetRange['end'].line + 1 + end_col = location.targetRange['end'].character + end + local test_path = get_test_path(runnable) + -- strip the file module prefix from the name + if test_path then + local mod_name = vim.fn.fnamemodify(file_path, ':t:r') + if vim.startswith(test_path, mod_name .. '::') then + test_path = test_path:sub(#mod_name + 3, #test_path) + end + end + ---@type rustaceanvim.neotest.Position + local pos = { + id = M.get_position_id(file_path, runnable), + name = test_path or runnable.label, + type = type, + path = file_path, + range = { start_row, start_col, end_row, end_col }, + runnable = runnable, + } + return pos + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/os.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/os.lua new file mode 100644 index 00000000..a84a3a66 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/os.lua @@ -0,0 +1,76 @@ +---@mod rustaceanvim.os Utilities for interacting with the operating system + +local os = {} + +local compat = require('rustaceanvim.compat') +local shell = require('rustaceanvim.shell') + +---@param url string +function os.open_url(url) + ---@param obj table + local function on_exit(obj) + if obj.code ~= 0 then + vim.schedule(function() + vim.notify('Could not open URL: ' .. url, vim.log.levels.ERROR) + end) + end + end + + if vim.fn.has('mac') == 1 then + compat.system({ 'open', url }, nil, on_exit) + return + end + if vim.fn.executable('sensible-browser') == 1 then + compat.system({ 'sensible-browser', url }, nil, on_exit) + return + end + if vim.fn.executable('xdg-open') == 1 then + compat.system({ 'xdg-open', url }, nil, on_exit) + return + end + local ok, err = pcall(vim.fn['netrw#BrowseX'], url, 0) + if not ok then + vim.notify('Could not open external docs. Neither xdg-open, nor netrw found: ' .. err, vim.log.levels.ERROR) + end +end + +---@param path string +---@return boolean +local function starts_with_windows_drive_letter(path) + return path:match('^%a:') ~= nil +end + +---Normalize path for Windows, which is case insensitive +---@param path string +---@return string normalized_path +function os.normalize_path_on_windows(path) + if shell.is_windows() and starts_with_windows_drive_letter(path) then + return path:sub(1, 1):lower() .. path:sub(2):gsub('/+', '\\') + end + return path +end + +---@param path string +---@return boolean +function os.is_valid_file_path(path) + local normalized_path = vim.fs.normalize(path, { expand_env = false }) + if shell.is_windows() then + return starts_with_windows_drive_letter(normalized_path) + end + return vim.startswith(normalized_path, '/') +end + +---Read the content of a file +---@param filename string +---@return string|nil content +function os.read_file(filename) + local content + local f = io.open(filename, 'r') + if f then + content = f:read('*a') + f:close() + end + return content +end + +return os diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/overrides.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/overrides.lua new file mode 100644 index 00000000..17521a74 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/overrides.lua @@ -0,0 +1,96 @@ +local M = {} + +local compat = require('rustaceanvim.compat') + +---@param input string unparsed snippet +---@return string parsed snippet +local function parse_snippet_fallback(input) + local output = input + -- $0 -> Nothing + :gsub('%$%d', '') + -- ${0:_} -> _ + :gsub('%${%d:(.-)}', '%1') + :gsub([[\}]], '}') + return output +end + +---@param input string unparsed snippet +---@return string parsed snippet +local function parse_snippet(input) + local ok, parsed = pcall(function() + return vim.lsp._snippet_grammar.parse(input) + end) + return ok and tostring(parsed) or parse_snippet_fallback(input) +end + +---@param spe? table +function M.snippet_text_edits_to_text_edits(spe) + if type(spe) ~= 'table' then + return + end + for _, value in ipairs(spe) do + if value.newText and value.insertTextFormat then + value.newText = parse_snippet(value.newText) + end + end +end + +---Transforms the args to cargo-nextest args if it is detected. +---Mutates command! +---@param args string[] +function M.try_nextest_transform(args) + if vim.fn.executable('cargo-nextest') ~= 1 then + return args + end + if args[1] == 'test' then + args[1] = 'run' + table.insert(args, 1, 'nextest') + end + if args[#args] == '--nocapture' then + table.insert(args, 3, '--nocapture') + table.remove(args, #args) + end + local nextest_unsupported_flags = { + '--exact', + '--show-output', + } + local indexes_to_remove_reverse_order = {} + for i, arg in ipairs(args) do + if compat.list_contains(nextest_unsupported_flags, arg) then + table.insert(indexes_to_remove_reverse_order, 1, i) + end + end + for _, i in pairs(indexes_to_remove_reverse_order) do + table.remove(args, i) + end + return args +end + +-- sanitize_command_for_debugging substitutes the command arguments so it can be used to run a +-- debugger. +-- +-- @param command should be a table like: { "run", "--package", "", "--bin", "" } +-- For some reason the endpoint textDocument/hover from rust-analyzer returns +-- cargoArgs = { "run", "--package", "", "--bin", "" } for Debug entry. +-- It doesn't make any sense to run a program before debugging. Even more the debugging won't run if +-- the program waits some input. Take a look at rust-analyzer/editors/code/src/toolchain.ts. +---@param command string[] +function M.sanitize_command_for_debugging(command) + if command[1] == 'run' then + command[1] = 'build' + elseif command[1] == 'test' and not compat.list_contains(command, '--no-run') then + table.insert(command, 2, '--no-run') + end +end + +---Undo sanitize_command_for_debugging. +---@param command string[] +function M.undo_debug_sanitize(command) + if command[1] == 'build' then + command[1] = 'run' + elseif command[1] == 'test' and command[2] == '--no-run' then + table.remove(command, 2) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/runnables.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/runnables.lua new file mode 100644 index 00000000..63752bca --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/runnables.lua @@ -0,0 +1,245 @@ +local config = require('rustaceanvim.config.internal') +local overrides = require('rustaceanvim.overrides') + +local M = {} + +---@return { textDocument: lsp_text_document, position: nil } +local function get_params() + return { + textDocument = vim.lsp.util.make_text_document_params(0), + position = nil, -- get em all + } +end + +---@class RARunnable +---@field args RARunnableArgs +---@field label string +---@field location? RARunnableLocation + +---@class RARunnableLocation +---@field targetRange lsp.Range +---@field targetSelectionRange lsp.Range + +---@class RARunnableArgs +---@field workspaceRoot string +---@field cargoArgs string[] +---@field cargoExtraArgs string[] +---@field executableArgs string[] + +---@param option string +---@return string +local function prettify_test_option(option) + for _, prefix in pairs { 'test-mod ', 'test ', 'cargo test -p ' } do + if vim.startswith(option, prefix) then + return option:sub(prefix:len() + 1, option:len()):gsub('%-%-all%-targets', '(all targets)') or option + end + end + return option:gsub('%-%-all%-targets', '(all targets)') or option +end + +---@param result RARunnable[] +---@param executableArgsOverride? string[] +---@param opts RunnablesOpts +---@return string[] +local function get_options(result, executableArgsOverride, opts) + local option_strings = {} + + for _, runnable in ipairs(result) do + local str = runnable.label + .. ( + executableArgsOverride and #executableArgsOverride > 0 and ' -- ' .. table.concat(executableArgsOverride, ' ') + or '' + ) + if opts.tests_only then + str = prettify_test_option(str) + end + if config.tools.cargo_override then + str = str:gsub('^cargo', config.tools.cargo_override) + end + table.insert(option_strings, str) + end + + return option_strings +end + +---@alias CargoCmd 'cargo' + +---@param runnable RARunnable +---@return string executable +---@return string[] args +---@return string | nil dir +function M.get_command(runnable) + local args = runnable.args + + local dir = args.workspaceRoot + + local ret = vim.list_extend({}, args.cargoArgs or {}) + ret = vim.list_extend(ret, args.cargoExtraArgs or {}) + table.insert(ret, '--') + ret = vim.list_extend(ret, args.executableArgs or {}) + if + config.tools.enable_nextest + and not config.tools.cargo_override + and not vim.startswith(runnable.label, 'doctest') + then + ret = overrides.try_nextest_transform(ret) + end + + return config.tools.cargo_override or 'cargo', ret, dir +end + +---@param choice integer +---@param runnables RARunnable[] +---@return CargoCmd command build command +---@return string[] args +---@return string|nil dir +local function getCommand(choice, runnables) + return M.get_command(runnables[choice]) +end + +---@param choice integer +---@param runnables RARunnable[] +function M.run_command(choice, runnables) + -- do nothing if choice is too high or too low + if not choice or choice < 1 or choice > #runnables then + return + end + + local opts = config.tools + + local command, args, cwd = getCommand(choice, runnables) + if not cwd then + return + end + + if #args > 0 and (vim.startswith(args[1], 'test') or vim.startswith(args[1], 'nextest')) then + local test_executor = vim.tbl_contains(args, '--all-targets') and opts.crate_test_executor or opts.test_executor + test_executor.execute_command(command, args, cwd, { + bufnr = vim.api.nvim_get_current_buf(), + runnable = runnables[choice], + }) + else + opts.executor.execute_command(command, args, cwd) + end +end + +---@param runnable RARunnable +---@return boolean +local function is_testable(runnable) + ---@cast runnable RARunnable + local cargoArgs = runnable.args and runnable.args.cargoArgs or {} + return #cargoArgs > 0 and vim.startswith(cargoArgs[1], 'test') +end + +---@param executableArgsOverride? string[] +---@param runnables RARunnable[] +---@return RARunnable[] +function M.apply_exec_args_override(executableArgsOverride, runnables) + if type(executableArgsOverride) == 'table' and #executableArgsOverride > 0 then + local unique_runnables = {} + for _, runnable in pairs(runnables) do + runnable.args.executableArgs = executableArgsOverride + unique_runnables[vim.inspect(runnable)] = runnable + end + runnables = vim.tbl_values(unique_runnables) + end + return runnables +end + +---@param executableArgsOverride? string[] +---@param opts RunnablesOpts +---@return fun(_, result: RARunnable[]) +local function mk_handler(executableArgsOverride, opts) + ---@param runnables RARunnable[] + return function(_, runnables) + if runnables == nil then + return + end + runnables = M.apply_exec_args_override(executableArgsOverride, runnables) + if opts.tests_only then + runnables = vim.tbl_filter(is_testable, runnables) + end + + -- get the choice from the user + local options = get_options(runnables, executableArgsOverride, opts) + vim.ui.select(options, { prompt = 'Runnables', kind = 'rust-tools/runnables' }, function(_, choice) + ---@cast choice integer + M.run_command(choice, runnables) + + local cached_commands = require('rustaceanvim.cached_commands') + if opts.tests_only then + cached_commands.set_last_testable(choice, runnables) + else + cached_commands.set_last_runnable(choice, runnables) + end + end) + end +end + +---@param position lsp.Position +---@param targetRange lsp.Range +local function is_within_range(position, targetRange) + return targetRange.start.line <= position.line and targetRange['end'].line >= position.line +end + +---@param runnables RARunnable +---@return integer | nil choice +function M.get_runnable_at_cursor_position(runnables) + ---@type lsp.Position + local position = vim.lsp.util.make_position_params().position + ---@type integer|nil, integer|nil + local choice, fallback + for idx, runnable in ipairs(runnables) do + if runnable.location then + local range = runnable.location.targetRange + if is_within_range(position, range) then + if vim.startswith(runnable.label, 'test-mod') then + fallback = idx + else + choice = idx + break + end + end + end + end + return choice or fallback +end + +local function mk_cursor_position_handler(executableArgsOverride) + ---@param runnables RARunnable[] + return function(_, runnables) + if runnables == nil then + return + end + runnables = M.apply_exec_args_override(executableArgsOverride, runnables) + local choice = M.get_runnable_at_cursor_position(runnables) + if not choice then + vim.notify('No runnable targets found for the current position.', vim.log.levels.ERROR) + return + end + M.run_command(choice, runnables) + local cached_commands = require('rustaceanvim.cached_commands') + if is_testable(runnables[choice]) then + cached_commands.set_last_testable(choice, runnables) + end + cached_commands.set_last_runnable(choice, runnables) + end +end + +---@class RunnablesOpts +---@field tests_only? boolean + +---Sends the request to rust-analyzer to get the runnables and handles them +---@param executableArgsOverride? string[] +---@param opts? RunnablesOpts +function M.runnables(executableArgsOverride, opts) + ---@type RunnablesOpts + opts = vim.tbl_deep_extend('force', { tests_only = false }, opts or {}) + vim.lsp.buf_request(0, 'experimental/runnables', get_params(), mk_handler(executableArgsOverride, opts)) +end + +function M.run(executableArgsOverride) + vim.lsp.buf_request(0, 'experimental/runnables', get_params(), mk_cursor_position_handler(executableArgsOverride)) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/rust_analyzer.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/rust_analyzer.lua new file mode 100644 index 00000000..f385fb5a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/rust_analyzer.lua @@ -0,0 +1,82 @@ +---@mod rustaceanvim.rust_analyzer Functions for interacting with rust-analyzer + +local compat = require('rustaceanvim.compat') +local os = require('rustaceanvim.os') + +---@class RustAnalyzerClientAdapter +local M = {} + +---@param bufnr number | nil 0 for the current buffer, `nil` for no buffer filter +---@param filter? vim.lsp.get_clients.Filter +---@return lsp.Client[] +M.get_active_rustaceanvim_clients = function(bufnr, filter) + ---@type vim.lsp.get_clients.Filter + filter = vim.tbl_deep_extend('force', filter or {}, { + name = 'rust-analyzer', + }) + if bufnr then + filter.bufnr = bufnr + end + return compat.get_clients(filter) +end + +---@param method string LSP method name +---@param params table|nil Parameters to send to the server +---@param handler? lsp.Handler See |lsp-handler| +--- If nil, follows resolution strategy defined in |lsp-handler-configuration| +M.any_buf_request = function(method, params, handler) + local bufnr = vim.api.nvim_get_current_buf() + local client_found = M.buf_request(bufnr, method, params, handler) + if client_found then + return + end + -- No buffer found. Try any client. + for _, client in ipairs(M.get_active_rustaceanvim_clients(nil, { method = method })) do + client.request(method, params, handler, 0) + end +end + +---@param bufnr integer Buffer handle, or 0 for current. +---@param method string LSP method name +---@param params table|nil Parameters to send to the server +---@param handler? lsp.Handler See |lsp-handler| +--- If nil, follows resolution strategy defined in |lsp-handler-configuration| +---@return boolean client_found +M.buf_request = function(bufnr, method, params, handler) + if bufnr == nil or bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local client_found = false + for _, client in ipairs(M.get_active_rustaceanvim_clients(bufnr, { method = method })) do + client.request(method, params, handler, bufnr) + client_found = true + end + return client_found +end + +---@param file_path string Search for clients with a root_dir matching this file path +---@param method string LSP method name +---@return lsp.Client|nil +M.get_client_for_file = function(file_path, method) + for _, client in ipairs(M.get_active_rustaceanvim_clients(nil, { method = method })) do + local root_dir = client.config.root_dir + if root_dir and vim.startswith(os.normalize_path_on_windows(file_path), root_dir) then + return client + end + end +end + +---@param method string LSP method name +---@param params table|nil Parameters to send to the server +M.notify = function(method, params) + local client_found = false + for _, client in ipairs(M.get_active_rustaceanvim_clients(0, { method = method })) do + client.notify(method, params) + client_found = true + end + if not client_found then + vim.notify('No rust-analyzer client found for method: ' .. method, vim.log.levels.ERROR) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/server_status.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/server_status.lua new file mode 100644 index 00000000..35cb7954 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/server_status.lua @@ -0,0 +1,42 @@ +local config = require('rustaceanvim.config.internal') + +local M = {} + +---@type { [integer]: boolean } +local _ran_once = {} + +---@param result RustAnalyzerInitializedStatusInternal +function M.handler(_, result, ctx, _) + -- quiescent means the full set of results is ready. + if not result.quiescent or _ran_once[ctx.client_id] then + return + end + -- rust-analyzer may provide incomplete/empty inlay hints by the time Neovim + -- calls the `on_attach` callback. + -- [https://github.com/neovim/neovim/issues/26511] + -- This workaround forces Neovim to redraw inlay hints if they are enabled, + -- as soon as rust-analyzer has fully initialized. + if type(vim.lsp.inlay_hint) == 'table' then + for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do + if vim.lsp.inlay_hint.is_enabled { bufnr = bufnr } then + vim.lsp.inlay_hint.enable(false, { bufnr = bufnr }) + vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) + end + end + end + -- Load user on_initialized + if config.tools.on_initialized then + config.tools.on_initialized(result) + end + if config.dap.autoload_configurations then + require('rustaceanvim.commands.debuggables').add_dap_debuggables() + end + _ran_once[ctx.client_id] = true +end + +---@param client_id integer +function M.reset_client_state(client_id) + _ran_once[client_id] = false +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/shell.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/shell.lua new file mode 100644 index 00000000..c8b64095 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/shell.lua @@ -0,0 +1,65 @@ +local M = {} + +local compat = require('rustaceanvim.compat') + +---@return boolean +function M.is_windows() + local sysname = compat.uv.os_uname().sysname + return sysname == 'Windows' or sysname == 'Windows_NT' +end + +---@return boolean +function M.is_macos() + return compat.uv.os_uname().sysname == 'Darwin' +end + +---@return boolean +local function is_nushell() + ---@diagnostic disable-next-line: missing-parameter + local shell = compat.uv.os_getenv('SHELL') + local nu = 'nu' + -- Check if $SHELL ends in "nu" + return shell ~= nil and shell:sub(-string.len(nu)) == nu +end + +---Get a new command which is a chain of all the old commands +---Note that a space is not added at the end of the returned command string +---@param commands string[] +---@return string +function M.chain_commands(commands) + local separator = M.is_windows() and ' | ' or is_nushell() and ';' or ' && ' + local ret = '' + + for i, value in ipairs(commands) do + local is_last = i == #commands + ret = ret .. value + + if not is_last then + ret = ret .. separator + end + end + + return ret +end + +---Create a `cd` command for the path +---@param path string +---@return string +function M.make_cd_command(path) + return ('cd "%s"'):format(path) +end + +---@param command string +---@param args string[] +---@return string command +function M.make_command_from_args(command, args) + local ret = command .. ' ' + + for _, value in ipairs(args) do + ret = ret .. value .. ' ' + end + + return ret +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/test.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/test.lua new file mode 100644 index 00000000..f0c01a39 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/test.lua @@ -0,0 +1,55 @@ +local M = {} + +---@param file_name string +---@param output string +---@return rustaceanvim.Diagnostic[] +---@diagnostic disable-next-line: inject-field +M.parse_diagnostics = function(file_name, output) + output = output:gsub('\r\n', '\n') + local lines = vim.split(output, '\n') + ---@type rustaceanvim.Diagnostic[] + local diagnostics = {} + for i, line in ipairs(lines) do + local message = '' + local test_id, file, lnum, col = line:match("thread '([^']+)' panicked at ([^:]+):(%d+):(%d+):") + if lnum and col and message and vim.endswith(file_name, file) then + local next_i = i + 1 + while #lines >= next_i and lines[next_i] ~= '' do + message = message .. lines[next_i] .. '\n' + next_i = next_i + 1 + end + ---@type rustaceanvim.Diagnostic + local diagnostic = { + test_id = test_id, + lnum = tonumber(lnum) - 1, + col = tonumber(col) or 0, + message = message, + source = 'rustaceanvim', + severity = vim.diagnostic.severity.ERROR, + } + table.insert(diagnostics, diagnostic) + end + end + if #diagnostics == 0 then + --- Fall back to old format + for test_id, message, file, lnum, col in + output:gmatch("thread '([^']+)' panicked at '([^']+)', ([^:]+):(%d+):(%d+)") + do + if vim.endswith(file_name, file) then + ---@type rustaceanvim.Diagnostic + local diagnostic = { + test_id = test_id, + lnum = tonumber(lnum) - 1, + col = tonumber(col) or 0, + message = message, + source = 'rustaceanvim', + severity = vim.diagnostic.severity.ERROR, + } + table.insert(diagnostics, diagnostic) + end + end + end + return diagnostics +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/types/internal.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/types/internal.lua new file mode 100644 index 00000000..34aff36a --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/types/internal.lua @@ -0,0 +1,21 @@ +---@alias lsp_position { character: integer, line: integer } +---@alias lsp_range { start: lsp_position, end: lsp_position } +---@alias lsp_text_document { uri: string } +---@alias lsp_range_params { textDocument: lsp_text_document, range: lsp_range } +---@alias lsp_position_params { textDocument: lsp_text_document, position: lsp_position } + +local M = {} + +---Evaluate a value that may be a function +---or an evaluated value +---@generic T +---@param value (fun():T)|T +---@return T +M.evaluate = function(value) + if type(value) == 'function' then + return value() + end + return value +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/ui.lua b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/ui.lua new file mode 100644 index 00000000..56943785 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/lua/rustaceanvim/ui.lua @@ -0,0 +1,36 @@ +local M = {} + +---@param bufnr integer | nil +function M.delete_buf(bufnr) + if bufnr ~= nil and vim.api.nvim_buf_is_valid(bufnr) then + vim.api.nvim_buf_delete(bufnr, { force = true }) + end +end + +---@param winnr integer | nil +function M.close_win(winnr) + if winnr ~= nil and vim.api.nvim_win_is_valid(winnr) then + vim.api.nvim_win_close(winnr, true) + end +end + +---@param vertical boolean +---@param bufnr integer +function M.split(vertical, bufnr) + local cmd = vertical and 'vsplit' or 'split' + + vim.cmd(cmd) + local win = vim.api.nvim_get_current_win() + vim.api.nvim_win_set_buf(win, bufnr) +end + +---@param vertical boolean +---@param amount string +function M.resize(vertical, amount) + local cmd = vertical and 'vertical resize ' or 'resize' + cmd = cmd .. amount + + vim.cmd(cmd) +end + +return M diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/nix-support/propagated-build-inputs b/config/neovim/store/lazy-plugins/rustaceanvim/nix-support/propagated-build-inputs new file mode 100644 index 00000000..b3b8c91f --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/nix-support/propagated-build-inputs @@ -0,0 +1 @@ +/nix/store/mqbhz05llkddfb5wni0m48kw22ixxps4-lua-5.1.5 \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/rock_manifest b/config/neovim/store/lazy-plugins/rustaceanvim/rock_manifest new file mode 100644 index 00000000..e04f0495 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/rock_manifest @@ -0,0 +1,78 @@ +rock_manifest = { + doc = { + ["mason.txt"] = "858adffa88b1649b91187f49f191e376", + ["rustaceanvim.txt"] = "0bb03192be9a68d7b6c73e8414b01625" + }, + ftplugin = { + ["rust.lua"] = "40f5f67ffb8f441a636ff231b7878ff8", + ["rust.vim"] = "3121ffa3b7f1aa9c3efbfe32e438dbeb", + ["toml.lua"] = "a8807dcfb5f6687ac543f4f8da3a9554" + }, + lua = { + rustaceanvim = { + ["cached_commands.lua"] = "6e23c8231f44a3edc76be54766fca334", + ["cargo.lua"] = "39415eb623c1376d5f2d5c717b8c0eb0", + commands = { + ["code_action_group.lua"] = "55bf0c83a007e5d64924eafe2e2a96eb", + ["crate_graph.lua"] = "30d01abc92dff63ace83a5d1697d2073", + ["debuggables.lua"] = "6548c5c600f14c9dc3d10e87983f6fb4", + ["diagnostic.lua"] = "55160b50ac49bcab96e15176fc5c7dde", + ["expand_macro.lua"] = "0a5991437c93aa39ace91d31dfd5d041", + ["external_docs.lua"] = "1d2fa5d88ea197ad9976c23b0ff70763", + ["fly_check.lua"] = "3f7b89700bd8f98fd5ab0ec02cce1cdd", + ["hover_range.lua"] = "68828cfd0c465bbe0915996795ff7b92", + ["init.lua"] = "6d7415da1c6e12b7b7914ab4e0339fae", + ["join_lines.lua"] = "f31a68f1ddac01b926d58ccd249bc03d", + ["move_item.lua"] = "f05118ba148e66c1e92882141e68343f", + ["open_cargo_toml.lua"] = "2368251db4ce52659693acc8eee15b74", + ["parent_module.lua"] = "c274b7357e35825f75802dc96e609992", + ["rebuild_proc_macros.lua"] = "6113336c3b82042f60c54446e6c5a887", + ["rustc_unpretty.lua"] = "fafde14d3d482667cecce2981444cad2", + ["ssr.lua"] = "1c936709630f09b2a81d22452d3e3fa2", + ["syntax_tree.lua"] = "c28fd8b22608e3f7bd3bdbb2e10c1617", + ["view_ir.lua"] = "12eadc00a2ca1005bcbeda4fa8fc80a0", + ["workspace_refresh.lua"] = "083b2d70533d0f417b65d2b7866f531b", + ["workspace_symbol.lua"] = "69bdc6d1255f7eb586ea8e350b840b85" + }, + ["compat.lua"] = "fedaef1aeaceaf086116ed62b423d0a3", + config = { + ["check.lua"] = "8a68a6e78ea97ab59ea0be83bff7ffc7", + ["init.lua"] = "f2ef97efc1f563c06932d51006e5b7cc", + ["internal.lua"] = "319f08833432f1ec7b04d4432737a1f0", + ["json.lua"] = "a8c7caf443ad9ce1193726aec053cc8d", + ["server.lua"] = "d642a65f31cb4f12b812840c1171821f" + }, + ["dap.lua"] = "757ecf822f6c30ebf9e462f7009ce99a", + executors = { + ["background.lua"] = "ae1c76a5c2f73e9c58456f580b7552b7", + ["init.lua"] = "a443891fc3b974db6787502179d85c19", + ["meta.lua"] = "99cae6c6b77725710ec2fc7d86bff665", + ["neotest.lua"] = "64a17e71f15a1741f0e7f835472f26e3", + ["quickfix.lua"] = "b1ea55a261b8623e73c2fd6aed278fd3", + ["termopen.lua"] = "4a225919ab4e1ad67fc591a9947d86e9", + ["toggleterm.lua"] = "221616f53cc50124694fcc1b0f1c92a2", + ["vimux.lua"] = "24d3f692083ca86f0926e7aa4722c0fe" + }, + ["health.lua"] = "126952700269c9ab976f29f59604c5da", + ["hover_actions.lua"] = "d0842822fa99c55b4cc5a65eae968ff2", + ["init.lua"] = "a48de2e7e937208adc6781a5ea35ec88", + ["lsp.lua"] = "f26e69489b384870c2774703599bf1bb", + neotest = { + ["init.lua"] = "10bfd8f06b7ad2de8d706c2e26505ae7", + ["trans.lua"] = "1f79320bca0c81d91673543dd7ad4984" + }, + ["os.lua"] = "4c8caca1afb6a8e7862300b4dcc29f09", + ["overrides.lua"] = "177b171bfb8c16c19326d6402b94a535", + ["runnables.lua"] = "d458cb318b2cd900e178930d3b14f224", + ["rust_analyzer.lua"] = "aa3bce32849886001dc2cb38a4ff951e", + ["server_status.lua"] = "25017ef9b15fadd713574c897e242970", + ["shell.lua"] = "561e4be6d5ba43f93a00ad5e6169e0eb", + ["test.lua"] = "e436ae539217729f46a1d556974e1100", + types = { + ["internal.lua"] = "95d9ff5763390a3efae83cc27dbef452" + }, + ["ui.lua"] = "185cbb4ef9b15b4feb6699fcbd5d4435" + } + }, + ["rustaceanvim-4.22.8-1.rockspec"] = "1d483e8dffa0275323da043a1e8c9cf3" +} diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/manifest b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/manifest new file mode 100644 index 00000000..756ee0f7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/manifest @@ -0,0 +1,241 @@ +commands = {} +dependencies = { + rustaceanvim = { + ["4.22.8-1"] = { + { + constraints = { + { + op = ">=", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + } + } + } +} +modules = { + ["rustaceanvim.cached_commands"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.cargo"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.code_action_group"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.crate_graph"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.debuggables"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.diagnostic"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.expand_macro"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.external_docs"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.fly_check"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.hover_range"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.init"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.join_lines"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.move_item"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.open_cargo_toml"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.parent_module"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.rebuild_proc_macros"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.rustc_unpretty"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.ssr"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.syntax_tree"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.view_ir"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.workspace_refresh"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.commands.workspace_symbol"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.compat"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.config.check"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.config.init"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.config.internal"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.config.json"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.config.server"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.dap"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.executors.background"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.executors.init"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.executors.meta"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.executors.neotest"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.executors.quickfix"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.executors.termopen"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.executors.toggleterm"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.executors.vimux"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.health"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.hover_actions"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.init"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.lsp"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.neotest.init"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.neotest.trans"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.os"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.overrides"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.runnables"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.rust_analyzer"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.server_status"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.shell"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.test"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.types.internal"] = { + "rustaceanvim/4.22.8-1" + }, + ["rustaceanvim.ui"] = { + "rustaceanvim/4.22.8-1" + } +} +repository = { + rustaceanvim = { + ["4.22.8-1"] = { + { + arch = "installed", + commands = {}, + dependencies = {}, + modules = { + ["rustaceanvim.cached_commands"] = "rustaceanvim/cached_commands.lua", + ["rustaceanvim.cargo"] = "rustaceanvim/cargo.lua", + ["rustaceanvim.commands.code_action_group"] = "rustaceanvim/commands/code_action_group.lua", + ["rustaceanvim.commands.crate_graph"] = "rustaceanvim/commands/crate_graph.lua", + ["rustaceanvim.commands.debuggables"] = "rustaceanvim/commands/debuggables.lua", + ["rustaceanvim.commands.diagnostic"] = "rustaceanvim/commands/diagnostic.lua", + ["rustaceanvim.commands.expand_macro"] = "rustaceanvim/commands/expand_macro.lua", + ["rustaceanvim.commands.external_docs"] = "rustaceanvim/commands/external_docs.lua", + ["rustaceanvim.commands.fly_check"] = "rustaceanvim/commands/fly_check.lua", + ["rustaceanvim.commands.hover_range"] = "rustaceanvim/commands/hover_range.lua", + ["rustaceanvim.commands.init"] = "rustaceanvim/commands/init.lua", + ["rustaceanvim.commands.join_lines"] = "rustaceanvim/commands/join_lines.lua", + ["rustaceanvim.commands.move_item"] = "rustaceanvim/commands/move_item.lua", + ["rustaceanvim.commands.open_cargo_toml"] = "rustaceanvim/commands/open_cargo_toml.lua", + ["rustaceanvim.commands.parent_module"] = "rustaceanvim/commands/parent_module.lua", + ["rustaceanvim.commands.rebuild_proc_macros"] = "rustaceanvim/commands/rebuild_proc_macros.lua", + ["rustaceanvim.commands.rustc_unpretty"] = "rustaceanvim/commands/rustc_unpretty.lua", + ["rustaceanvim.commands.ssr"] = "rustaceanvim/commands/ssr.lua", + ["rustaceanvim.commands.syntax_tree"] = "rustaceanvim/commands/syntax_tree.lua", + ["rustaceanvim.commands.view_ir"] = "rustaceanvim/commands/view_ir.lua", + ["rustaceanvim.commands.workspace_refresh"] = "rustaceanvim/commands/workspace_refresh.lua", + ["rustaceanvim.commands.workspace_symbol"] = "rustaceanvim/commands/workspace_symbol.lua", + ["rustaceanvim.compat"] = "rustaceanvim/compat.lua", + ["rustaceanvim.config.check"] = "rustaceanvim/config/check.lua", + ["rustaceanvim.config.init"] = "rustaceanvim/config/init.lua", + ["rustaceanvim.config.internal"] = "rustaceanvim/config/internal.lua", + ["rustaceanvim.config.json"] = "rustaceanvim/config/json.lua", + ["rustaceanvim.config.server"] = "rustaceanvim/config/server.lua", + ["rustaceanvim.dap"] = "rustaceanvim/dap.lua", + ["rustaceanvim.executors.background"] = "rustaceanvim/executors/background.lua", + ["rustaceanvim.executors.init"] = "rustaceanvim/executors/init.lua", + ["rustaceanvim.executors.meta"] = "rustaceanvim/executors/meta.lua", + ["rustaceanvim.executors.neotest"] = "rustaceanvim/executors/neotest.lua", + ["rustaceanvim.executors.quickfix"] = "rustaceanvim/executors/quickfix.lua", + ["rustaceanvim.executors.termopen"] = "rustaceanvim/executors/termopen.lua", + ["rustaceanvim.executors.toggleterm"] = "rustaceanvim/executors/toggleterm.lua", + ["rustaceanvim.executors.vimux"] = "rustaceanvim/executors/vimux.lua", + ["rustaceanvim.health"] = "rustaceanvim/health.lua", + ["rustaceanvim.hover_actions"] = "rustaceanvim/hover_actions.lua", + ["rustaceanvim.init"] = "rustaceanvim/init.lua", + ["rustaceanvim.lsp"] = "rustaceanvim/lsp.lua", + ["rustaceanvim.neotest.init"] = "rustaceanvim/neotest/init.lua", + ["rustaceanvim.neotest.trans"] = "rustaceanvim/neotest/trans.lua", + ["rustaceanvim.os"] = "rustaceanvim/os.lua", + ["rustaceanvim.overrides"] = "rustaceanvim/overrides.lua", + ["rustaceanvim.runnables"] = "rustaceanvim/runnables.lua", + ["rustaceanvim.rust_analyzer"] = "rustaceanvim/rust_analyzer.lua", + ["rustaceanvim.server_status"] = "rustaceanvim/server_status.lua", + ["rustaceanvim.shell"] = "rustaceanvim/shell.lua", + ["rustaceanvim.test"] = "rustaceanvim/test.lua", + ["rustaceanvim.types.internal"] = "rustaceanvim/types/internal.lua", + ["rustaceanvim.ui"] = "rustaceanvim/ui.lua" + } + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/doc/mason.txt b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/doc/mason.txt new file mode 100644 index 00000000..a30f1510 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/doc/mason.txt @@ -0,0 +1,54 @@ +============================================================================== +mason-lspconfig troubleshooting *rustaceanvim.mason* + +This plugin supports automatically detecting mason.nvim codelldb installations, +but not rust-analyzer. +The main reason for this choice is that it mason.nvim installations of rust-analyzer +will most likely have been built with a different toolchain than your project, +leading to inconsistencies and possibly subtle bugs. + +If you want to use a mason.nvim installation anyway, you can do so by specifying +the `server.cmd` setting (see |rustaceanvim.config| and |RustaceanLspClientOpts|): +>lua + vim.g.rustaceanvim = { + server = { + cmd = function() + local mason_registry = require('mason-registry') + local ra_binary = mason_registry.is_installed('rust-analyzer') + -- This may need to be tweaked, depending on the operating system. + and mason_registry.get_package('rust-analyzer'):get_install_path() .. "/rust-analyzer" + or "rust-analyzer" + return { ra_binary } -- You can add args to the list, such as '--log-file' + end, + }, + } +< +Note that mason-lspconfig.nvim, when configured to ensure rust-analyzer is installed, +assumes you are using the `nvim-lspconfig.rust_analyzer` client. +Some Neovim distributions will automatically call the client's `setup` +function, resulting in a conflict with this plugin. + +General approach to prevent mason-lspconfig from setting up +`lspconfig.rust_analyzer`: + +>lua + require('mason-lspconfig').setup_handlers { + ['rust_analyzer'] = function() end, + } +< + +Using LazyVim: + +>lua + { + 'neovim/nvim-lspconfig', + opts = { + setup = { + rust_analyzer = function() + return true + end, + }, + }, + } +< +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/doc/rustaceanvim.txt b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/doc/rustaceanvim.txt new file mode 100644 index 00000000..9da43f53 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/doc/rustaceanvim.txt @@ -0,0 +1,409 @@ +============================================================================== +Table of Contents *rustaceanvim.contents* + +Introduction ··························································· |intro| + ································································ |rustaceanvim| +plugin configuration ····································· |rustaceanvim.config| +LSP configuration utility ························· |rustaceanvim.config.server| + ························································ |rustaceanvim.neotest| + +============================================================================== +Introduction *intro* + +This plugin automatically configures the `rust-analyzer` builtin LSP client +and integrates with other rust tools. + +============================================================================== + *rustaceanvim* + + +Commands: + + ':RustAnalyzer start' - Start the LSP client. + ':RustAnalyzer stop' - Stop the LSP client. + ':RustAnalyzer restart' - Restart the LSP client. + ':RustAnalyzer reloadSettings' - Reload settings for the LSP client. + +The ':RustLsp[!]' command is available after the LSP client has initialized. +It accepts the following subcommands: + + 'runnables {args[]}?' - Run tests, executables, etc. + ':RustLsp!' means run the last runnable (ignores any args). + `args[]` allows you to override the executable's arguments. + 'run {args[]}?' - Like 'runnables', but runs the target at the current cursor position. + 'debuggables {args[]}?' - Debug tests, executables, etc. (requires |nvim-dap|). + ':RustLsp!' means run the last debuggable (ignores any args). + `args[]` allows you to override the executable's arguments. + 'debug {args[]}?' - Like 'debuggables', but debugs the target at the current cursor position. + 'testables {args[]}?' - Run tests + ':RustLsp!' means run the last testable (ignores any args). + `args[]` allows you to override the executable's arguments. + 'expandMacro' - Expand macros recursively. + 'moveItem {up|down}' - Move items up or down. + 'hover {actions|range}' - Hover actions, or hover over visually selected range. + 'explainError' - Display a hover window with explanations form the Rust error index. + Like |vim.diagnostic.goto_next|, |explainError| will cycle diagnostics, + starting at the cursor position, until it can find a diagnostic with + an error code. + 'renderDiagnostic' - Display a hover window with the rendered diagnostic, + as displayed during `cargo build`. + Like |vim.diagnostic.goto_next|, |renderDiagnostic| will cycle diagnostics, + starting at the cursor position, until it can find a diagnostic with + rendered data. + 'openCargo' - Open the Cargo.toml file for the current package. + 'openDocs' - Open docs.rs documentation for the symbol under the cursor. + 'parentModule' - Open the current module's parent module. + 'workspaceSymbol {onlyTypes?|allSymbols?} {query?}' + Filtered workspace symbol search. + When run with a bang (`:RustLsp! workspaceSymbol ...`), + rust-analyzer will include dependencies in the search. + You can also configure rust-analyzer so that |vim.lsp.buf.workspace_symbol| + supports filtering (with a # suffix to the query) or searching dependencies. + 'joinLines' - Join adjacent lines. + 'ssr {query}' - Structural search and replace. + Searches the entire buffer in normal mode. + Searches the selected region in visual mode. + 'crateGraph {backend}' - Create and view a crate graph with graphviz. + 'syntaxTree' - View the syntax tree. + 'view {mir|hir}' - View MIR or HIR. + 'flyCheck' {run?|clear?|cancel?} + - Run `cargo check` or another compatible command (f.x. `clippy`) + in a background thread and provide LSP diagnostics based on + the output of the command. + Useful in large projects where running `cargo check` on each save + can be costly. + Defaults to `flyCheck run` if called without an argument. + 'logFile' - Open the rust-analyzer log file. + +The ':Rustc' command can be used to interact with rustc. +It accepts the following subcommands: + + 'unpretty {args[]}' - Opens a buffer with a textual representation of the MIR or others things, + of the function closest to the cursor. + Achieves an experience similar to Rust Playground. + NOTE: This currently requires a tree-sitter parser for Rust, + and a nightly compiler toolchain. + +============================================================================== +plugin configuration *rustaceanvim.config* + + +rustaceanvim is a filetype plugin, and does not need +a `setup` function to work. + +To configure rustaceanvim, set the variable `vim.g.rustaceanvim`, +which is a `RustaceanOpts` table, in your neovim configuration. + +Example: + +>lua +---@type RustaceanOpts +vim.g.rustaceanvim = { + ---@type RustaceanToolsOpts + tools = { + -- ... + }, + ---@type RustaceanLspClientOpts + server = { + on_attach = function(client, bufnr) + -- Set keybindings, etc. here. + end, + default_settings = { + -- rust-analyzer language server configuration + ['rust-analyzer'] = { + }, + }, + -- ... + }, + ---@type RustaceanDapOpts + dap = { + -- ... + }, + } +< + +Notes: + + - `vim.g.rustaceanvim` can also be a function that returns a `RustaceanOpts` table. + - `server.settings`, by default, is a function that looks for a `rust-analyzer.json` file + in the project root, to load settings from it. It falls back to an empty table. + + +RustaceanOpts *RustaceanOpts* + + Fields: ~ + {tools?} (RustaceanToolsOpts) Plugin options + {server?} (RustaceanLspClientOpts) Language server client options + {dap?} (RustaceanDapOpts) Debug adapter options + + +RustaceanToolsOpts *RustaceanToolsOpts* + + Fields: ~ + {executor?} (RustaceanExecutor|executor_alias) The executor to use for runnables/debuggables + {test_executor?} (RustaceanExecutor|test_executor_alias) The executor to use for runnables that are tests / testables + {crate_test_executor?} (RustaceanExecutor|test_executor_alias) The executor to use for runnables that are crate test suites (--all-targets) + {cargo_override?} (string) Set this to override the 'cargo' command for runnables, debuggables (etc., e.g. to 'cross'). If set, this takes precedence over 'enable_nextest'. + {enable_nextest?} (boolean) Whether to enable nextest. If enabled, `cargo test` commands will be transformed to `cargo nextest run` commands. Defaults to `true` if cargo-nextest is detected. Ignored if `cargo_override` is set. + {enable_clippy?} (boolean) Whether to enable clippy checks on save if a clippy installation is detected. Default: `true` + {on_initialized?} (fun(health:RustAnalyzerInitializedStatus)) Function that is invoked when the LSP server has finished initializing + {reload_workspace_from_cargo_toml?} (boolean) Automatically call `RustReloadWorkspace` when writing to a Cargo.toml file + {hover_actions?} (RustaceanHoverActionsOpts) Options for hover actions + {code_actions?} (RustaceanCodeActionOpts) Options for code actions + {float_win_config?} (FloatWinConfig) Options applied to floating windows. See |api-win_config|. + {create_graph?} (RustaceanCrateGraphConfig) Options for showing the crate graph based on graphviz and the dot + {open_url?} (fun(url:string):nil) If set, overrides how to open URLs + {rustc?} (RustcOpts) Options for `rustc` + + +RustaceanExecutor *RustaceanExecutor* + + Fields: ~ + {execute_command} (fun(cmd:string,args:string[],cwd:string|nil,opts?:RustaceanExecutorOpts)) + + +RustaceanExecutorOpts *RustaceanExecutorOpts* + + Fields: ~ + {bufnr?} (integer) The buffer from which the executor was invoked. + + +FloatWinConfig *FloatWinConfig* + + Fields: ~ + {auto_focus?} (boolean) + {open_split?} ("horizontal"|"vertical") + + See: ~ + |vim.lsp.util.open_floating_preview.Opts| + |vim.api.nvim_open_win| + + +executor_alias *executor_alias* + + Type: ~ + "termopen"|"quickfix"|"toggleterm"|"vimux"|"neotest" + + +test_executor_alias *test_executor_alias* + + Type: ~ + executor_alias|"background" + + +RustaceanHoverActionsOpts *RustaceanHoverActionsOpts* + + Fields: ~ + {replace_builtin_hover?} (boolean) Whether to replace Neovim's built-in `vim.lsp.buf.hover` with hover actions. Default: `true` + + +RustaceanCodeActionOpts *RustaceanCodeActionOpts* + + Fields: ~ + {group_icon?} (string) Text appended to a group action + {ui_select_fallback?} (boolean) Whether to fall back to `vim.ui.select` if there are no grouped code actions. Default: `false` + + +lsp_server_health_status *lsp_server_health_status* + + Type: ~ + "ok"|"warning"|"error" + + +RustAnalyzerInitializedStatus *RustAnalyzerInitializedStatus* + + Fields: ~ + {health} (lsp_server_health_status) + + +RustaceanCrateGraphConfig *RustaceanCrateGraphConfig* + + Fields: ~ + {backend?} (string) Backend used for displaying the graph. See: https://graphviz.org/docs/outputs/ Defaults to `"x11"` if unset. + {output?} (string) Where to store the output. No output if unset. Relative path from `cwd`. + {enabled_graphviz_backends?} (string[]) Override the enabled graphviz backends list, used for input validation and autocompletion. + {pipe?} (string) Overide the pipe symbol in the shell command. Useful if using a shell that is not supported by this plugin. + + +RustcOpts *RustcOpts* + + Fields: ~ + {edition} (string) The edition to use. See https://rustc-dev-guide.rust-lang.org/guides/editions.html. Default '2021'. + + +RustaceanLspClientOpts *RustaceanLspClientOpts* + + Fields: ~ + {auto_attach?} (boolean|fun(bufnr:integer):boolean) Whether to automatically attach the LSP client. Defaults to `true` if the `rust-analyzer` executable is found. + {cmd?} (string[]|fun():string[]) Command and arguments for starting rust-analyzer + {settings?} (table|fun(project_root:string|nil,default_settings:table):table) Setting passed to rust-analyzer. Defaults to a function that looks for a `rust-analyzer.json` file or returns an empty table. See https://rust-analyzer.github.io/manual.html#configuration. + {standalone?} (boolean) Standalone file support (enabled by default). Disabling it may improve rust-analyzer's startup time. + {logfile?} (string) The path to the rust-analyzer log file. + {load_vscode_settings?} (boolean) Whether to search (upward from the buffer) for rust-analyzer settings in .vscode/settings json. If found, loaded settings will override configured options. Default: false + + +RustaceanDapOpts *RustaceanDapOpts* + + Fields: ~ + {adapter?} (DapExecutableConfig|DapServerConfig|disable|fun():DapExecutableConfig|DapServerConfig|disable) @field autoload_configurations boolean Whether to autoload nvim-dap configurations when rust-analyzer has attached? Default: `true`. + + +disable *disable* + + Type: ~ + false + + +DapCommand *DapCommand* + + Type: ~ + string + + +DapExecutableConfig *DapExecutableConfig* + + Fields: ~ + {type} (dap_adapter_type_executable) The type of debug adapter. + {command} (string) Default: `"lldb-vscode"`. + {args?} (string) Default: unset. + {name?} (string) Default: `"lldb"`. + + +DapServerConfig *DapServerConfig* + + Fields: ~ + {type} (dap_adapter_type_server) The type of debug adapter. + {host?} (string) The host to connect to. + {port} (string) The port to connect to. + {executable} (DapExecutable) The executable to run + {name?} (string) + + +DapExecutable *DapExecutable* + + Fields: ~ + {command} (string) The executable. + {args} (string[]) Its arguments. + + +dap_adapter_type_executable *dap_adapter_type_executable* + + Type: ~ + + + +dap_adapter_type_server *dap_adapter_type_server* + + Type: ~ + + + +DapClientConfig : Configuration *DapClientConfig* + + Fields: ~ + {type} (string) The dap adapter to use + {name} (string) + {request} (dap_config_request_launch|dap_config_request_attach|dap_config_request_custom) The type of dap session + {cwd?} (string) Current working directory + {program?} (string) Path to executable for most DAP clients + {args?} (string[]) Optional args to DAP client, not valid for all client types + {env?} (EnvironmentMap) Environmental variables + {initCommands?} (string[]) Initial commands to run, `lldb` clients only + {coreConfigs?} (table) Essential config values for `probe-rs` client, see https://probe.rs/docs/tools/debugger/ + + +EnvironmentMap *EnvironmentMap* + + Type: ~ + table + + +dap_config_request_launch *dap_config_request_launch* + + Type: ~ + + + +dap_config_request_attach *dap_config_request_attach* + + Type: ~ + + + +dap_config_request_custom *dap_config_request_custom* + + Type: ~ + + + + *M.get_codelldb_adapter* +M.get_codelldb_adapter({codelldb_path}, {liblldb_path}) + For the heroes who want to use it. + + Parameters: ~ + {codelldb_path} (string) Path to the codelldb executable + {liblldb_path} (string) Path to the liblldb dynamic library + + Returns: ~ + (DapServerConfig) + + +============================================================================== +LSP configuration utility *rustaceanvim.config.server* + +LoadRASettingsOpts *LoadRASettingsOpts* + + Fields: ~ + {settings_file_pattern} (string|nil) File name or pattern to search for. Defaults to 'rust-analyzer.json' + {default_settings} (table|nil) Default settings to merge the loaded settings into + + + *server.load_rust_analyzer_settings* +server.load_rust_analyzer_settings({project_root}, {opts}) + Load rust-analyzer settings from a JSON file, + falling back to the default settings if none is found or if it cannot be decoded. + + Parameters: ~ + {project_root} (string|nil) The project root + {opts} (LoadRASettingsOpts|nil) + + Returns: ~ + (table) server_settings + + See: ~ + |https://rust-analyzer.github.io/manual.html#configuration| + + +server.create_client_capabilities() *server.create_client_capabilities* + + Returns: ~ + (lsp.ClientCapabilities) + + +============================================================================== + *rustaceanvim.neotest* + + +A |neotest| adapter for rust, powered by rustaceanvim. + +If you add this to neotest: + +> +require('neotest').setup { + -- ..., + adapters = { + -- ..., + require('rustaceanvim.neotest') + }, +} +< + +this plugin will configure itself to use |neotest| +as a test executor, and |neotest| will use rust-analyzer +for test discovery and command construction. + +Note: If you use this adapter, do not add the neotest-rust adapter +(another plugin). + + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/ftplugin/rust.lua b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/ftplugin/rust.lua new file mode 100644 index 00000000..786e1309 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/ftplugin/rust.lua @@ -0,0 +1,64 @@ +---@type RustaceanConfig +local config = require('rustaceanvim.config.internal') + +if not vim.g.loaded_rustaceanvim then + require('rustaceanvim.config.check').check_for_lspconfig_conflict(vim.schedule_wrap(function(warn) + vim.notify_once(warn, vim.log.levels.WARN) + end)) + vim.lsp.commands['rust-analyzer.runSingle'] = function(command) + local runnables = require('rustaceanvim.runnables') + local cached_commands = require('rustaceanvim.cached_commands') + ---@type RARunnable[] + local ra_runnables = command.arguments + local runnable = ra_runnables[1] + local cargo_args = runnable.args.cargoArgs + if #cargo_args > 0 and vim.startswith(cargo_args[1], 'test') then + cached_commands.set_last_testable(1, ra_runnables) + end + cached_commands.set_last_runnable(1, ra_runnables) + runnables.run_command(1, ra_runnables) + end + + vim.lsp.commands['rust-analyzer.gotoLocation'] = function(command, ctx) + local client = vim.lsp.get_client_by_id(ctx.client_id) + if client then + vim.lsp.util.jump_to_location(command.arguments[1], client.offset_encoding) + end + end + + vim.lsp.commands['rust-analyzer.showReferences'] = function(_) + vim.lsp.buf.implementation() + end + + vim.lsp.commands['rust-analyzer.debugSingle'] = function(command) + local overrides = require('rustaceanvim.overrides') + local args = command.arguments[1].args + overrides.sanitize_command_for_debugging(args.cargoArgs) + local cached_commands = require('rustaceanvim.cached_commands') + cached_commands.set_last_debuggable(args) + local rt_dap = require('rustaceanvim.dap') + rt_dap.start(args) + end + + local commands = require('rustaceanvim.commands') + commands.create_rustc_command() +end + +vim.g.loaded_rustaceanvim = true + +local bufnr = vim.api.nvim_get_current_buf() +local auto_attach = config.server.auto_attach +if type(auto_attach) == 'function' then + auto_attach = auto_attach(bufnr) +end + +if auto_attach then + -- Defer for a smoother experience on low-end devices + vim.api.nvim_create_autocmd('BufEnter', { + buffer = bufnr, + group = vim.api.nvim_create_augroup('RustaceanvimAttach', { clear = true }), + callback = function() + require('rustaceanvim.lsp').start() + end, + }) +end diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/ftplugin/rust.vim b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/ftplugin/rust.vim new file mode 100644 index 00000000..e0466566 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/ftplugin/rust.vim @@ -0,0 +1,26 @@ +" Copied from built-in compiler/{rustc,cargo}.vim +setlocal errorformat= + \%f:%l:%c:\ %t%*[^:]:\ %m, + \%f:%l:%c:\ %*\\d:%*\\d\ %t%*[^:]:\ %m, + \%-G%f:%l\ %s, + \%-G%*[\ ]^, + \%-G%*[\ ]^%*[~], + \%-G%*[\ ]... + +" New errorformat (after nightly 2016/08/10) +setlocal errorformat+= + \%-G, + \%-Gerror:\ aborting\ %.%#, + \%-Gerror:\ Could\ not\ compile\ %.%#, + \%Eerror:\ %m, + \%Eerror[E%n]:\ %m, + \%Wwarning:\ %m, + \%Inote:\ %m, + \%C\ %#-->\ %f:%l:%c + +setlocal errorformat+= + \%-G%\\s%#Downloading%.%#, + \%-G%\\s%#Compiling%.%#, + \%-G%\\s%#Finished%.%#, + \%-G%\\s%#error:\ Could\ not\ compile\ %.%#, + \%-G%\\s%#To\ learn\ more\\,%.%# diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/ftplugin/toml.lua b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/ftplugin/toml.lua new file mode 100644 index 00000000..b465d3c4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/ftplugin/toml.lua @@ -0,0 +1,24 @@ +local fname = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':t') +if fname ~= 'Cargo.toml' then + return +end + +local config = require('rustaceanvim.config.internal') +local ra = require('rustaceanvim.rust_analyzer') +if config.tools.reload_workspace_from_cargo_toml then + local group = vim.api.nvim_create_augroup('RustaceanCargoReloadWorkspace', { clear = false }) + local bufnr = vim.api.nvim_get_current_buf() + vim.api.nvim_clear_autocmds { + buffer = bufnr, + group = group, + } + vim.api.nvim_create_autocmd('BufWritePost', { + buffer = vim.api.nvim_get_current_buf(), + group = group, + callback = function() + if #ra.get_active_rustaceanvim_clients(nil) > 0 then + vim.cmd.RustLsp { 'reloadWorkspace', mods = { silent = true } } + end + end, + }) +end diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/rock_manifest b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/rock_manifest new file mode 100644 index 00000000..e04f0495 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/rock_manifest @@ -0,0 +1,78 @@ +rock_manifest = { + doc = { + ["mason.txt"] = "858adffa88b1649b91187f49f191e376", + ["rustaceanvim.txt"] = "0bb03192be9a68d7b6c73e8414b01625" + }, + ftplugin = { + ["rust.lua"] = "40f5f67ffb8f441a636ff231b7878ff8", + ["rust.vim"] = "3121ffa3b7f1aa9c3efbfe32e438dbeb", + ["toml.lua"] = "a8807dcfb5f6687ac543f4f8da3a9554" + }, + lua = { + rustaceanvim = { + ["cached_commands.lua"] = "6e23c8231f44a3edc76be54766fca334", + ["cargo.lua"] = "39415eb623c1376d5f2d5c717b8c0eb0", + commands = { + ["code_action_group.lua"] = "55bf0c83a007e5d64924eafe2e2a96eb", + ["crate_graph.lua"] = "30d01abc92dff63ace83a5d1697d2073", + ["debuggables.lua"] = "6548c5c600f14c9dc3d10e87983f6fb4", + ["diagnostic.lua"] = "55160b50ac49bcab96e15176fc5c7dde", + ["expand_macro.lua"] = "0a5991437c93aa39ace91d31dfd5d041", + ["external_docs.lua"] = "1d2fa5d88ea197ad9976c23b0ff70763", + ["fly_check.lua"] = "3f7b89700bd8f98fd5ab0ec02cce1cdd", + ["hover_range.lua"] = "68828cfd0c465bbe0915996795ff7b92", + ["init.lua"] = "6d7415da1c6e12b7b7914ab4e0339fae", + ["join_lines.lua"] = "f31a68f1ddac01b926d58ccd249bc03d", + ["move_item.lua"] = "f05118ba148e66c1e92882141e68343f", + ["open_cargo_toml.lua"] = "2368251db4ce52659693acc8eee15b74", + ["parent_module.lua"] = "c274b7357e35825f75802dc96e609992", + ["rebuild_proc_macros.lua"] = "6113336c3b82042f60c54446e6c5a887", + ["rustc_unpretty.lua"] = "fafde14d3d482667cecce2981444cad2", + ["ssr.lua"] = "1c936709630f09b2a81d22452d3e3fa2", + ["syntax_tree.lua"] = "c28fd8b22608e3f7bd3bdbb2e10c1617", + ["view_ir.lua"] = "12eadc00a2ca1005bcbeda4fa8fc80a0", + ["workspace_refresh.lua"] = "083b2d70533d0f417b65d2b7866f531b", + ["workspace_symbol.lua"] = "69bdc6d1255f7eb586ea8e350b840b85" + }, + ["compat.lua"] = "fedaef1aeaceaf086116ed62b423d0a3", + config = { + ["check.lua"] = "8a68a6e78ea97ab59ea0be83bff7ffc7", + ["init.lua"] = "f2ef97efc1f563c06932d51006e5b7cc", + ["internal.lua"] = "319f08833432f1ec7b04d4432737a1f0", + ["json.lua"] = "a8c7caf443ad9ce1193726aec053cc8d", + ["server.lua"] = "d642a65f31cb4f12b812840c1171821f" + }, + ["dap.lua"] = "757ecf822f6c30ebf9e462f7009ce99a", + executors = { + ["background.lua"] = "ae1c76a5c2f73e9c58456f580b7552b7", + ["init.lua"] = "a443891fc3b974db6787502179d85c19", + ["meta.lua"] = "99cae6c6b77725710ec2fc7d86bff665", + ["neotest.lua"] = "64a17e71f15a1741f0e7f835472f26e3", + ["quickfix.lua"] = "b1ea55a261b8623e73c2fd6aed278fd3", + ["termopen.lua"] = "4a225919ab4e1ad67fc591a9947d86e9", + ["toggleterm.lua"] = "221616f53cc50124694fcc1b0f1c92a2", + ["vimux.lua"] = "24d3f692083ca86f0926e7aa4722c0fe" + }, + ["health.lua"] = "126952700269c9ab976f29f59604c5da", + ["hover_actions.lua"] = "d0842822fa99c55b4cc5a65eae968ff2", + ["init.lua"] = "a48de2e7e937208adc6781a5ea35ec88", + ["lsp.lua"] = "f26e69489b384870c2774703599bf1bb", + neotest = { + ["init.lua"] = "10bfd8f06b7ad2de8d706c2e26505ae7", + ["trans.lua"] = "1f79320bca0c81d91673543dd7ad4984" + }, + ["os.lua"] = "4c8caca1afb6a8e7862300b4dcc29f09", + ["overrides.lua"] = "177b171bfb8c16c19326d6402b94a535", + ["runnables.lua"] = "d458cb318b2cd900e178930d3b14f224", + ["rust_analyzer.lua"] = "aa3bce32849886001dc2cb38a4ff951e", + ["server_status.lua"] = "25017ef9b15fadd713574c897e242970", + ["shell.lua"] = "561e4be6d5ba43f93a00ad5e6169e0eb", + ["test.lua"] = "e436ae539217729f46a1d556974e1100", + types = { + ["internal.lua"] = "95d9ff5763390a3efae83cc27dbef452" + }, + ["ui.lua"] = "185cbb4ef9b15b4feb6699fcbd5d4435" + } + }, + ["rustaceanvim-4.22.8-1.rockspec"] = "1d483e8dffa0275323da043a1e8c9cf3" +} diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/rustaceanvim-4.22.8-1.rockspec b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/rustaceanvim-4.22.8-1.rockspec new file mode 100644 index 00000000..0ecf8440 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1-unstable-2024-05-24-rocks/rustaceanvim/4.22.8-1/rustaceanvim-4.22.8-1.rockspec @@ -0,0 +1,40 @@ +local git_ref = '4.22.8' +local modrev = '4.22.8' +local specrev = '1' + +local repo_url = 'https://github.com/mrcjkb/rustaceanvim' + +rockspec_format = '3.0' +package = 'rustaceanvim' +version = modrev ..'-'.. specrev + +description = { + summary = 'Supercharge your Rust experience in Neovim! A heavily modified fork of rust-tools.nvim', + detailed = [[ +This plugin automatically configures the rust-analyzer builtin LSP client +and integrates with other Rust tools. See the README's #features section +for more info.]], + labels = { 'dap', 'debug-adapter-protocol', 'language-server-protocol', 'lsp', 'neovim', 'nvim', 'plugin', 'rust', 'rust-analyzer', 'rust-lang', 'rust-tools' } , + homepage = 'https://github.com/mrcjkb/rustaceanvim', + license = 'GPL-2.0' +} + +dependencies = { 'lua >= 5.1' } + +test_dependencies = { } + +source = { + url = repo_url .. '/archive/' .. git_ref .. '.zip', + dir = 'rustaceanvim-' .. '4.22.8', +} + +if modrev == 'scm' or modrev == 'dev' then + source = { + url = repo_url:gsub('https', 'git') + } +end + +build = { + type = 'builtin', + copy_directories = { 'doc', 'ftplugin' } , +} diff --git a/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1.rockspec b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1.rockspec new file mode 100644 index 00000000..0ecf8440 --- /dev/null +++ b/config/neovim/store/lazy-plugins/rustaceanvim/rustaceanvim-4.22.8-1.rockspec @@ -0,0 +1,40 @@ +local git_ref = '4.22.8' +local modrev = '4.22.8' +local specrev = '1' + +local repo_url = 'https://github.com/mrcjkb/rustaceanvim' + +rockspec_format = '3.0' +package = 'rustaceanvim' +version = modrev ..'-'.. specrev + +description = { + summary = 'Supercharge your Rust experience in Neovim! A heavily modified fork of rust-tools.nvim', + detailed = [[ +This plugin automatically configures the rust-analyzer builtin LSP client +and integrates with other Rust tools. See the README's #features section +for more info.]], + labels = { 'dap', 'debug-adapter-protocol', 'language-server-protocol', 'lsp', 'neovim', 'nvim', 'plugin', 'rust', 'rust-analyzer', 'rust-lang', 'rust-tools' } , + homepage = 'https://github.com/mrcjkb/rustaceanvim', + license = 'GPL-2.0' +} + +dependencies = { 'lua >= 5.1' } + +test_dependencies = { } + +source = { + url = repo_url .. '/archive/' .. git_ref .. '.zip', + dir = 'rustaceanvim-' .. '4.22.8', +} + +if modrev == 'scm' or modrev == 'dev' then + source = { + url = repo_url:gsub('https', 'git') + } +end + +build = { + type = 'builtin', + copy_directories = { 'doc', 'ftplugin' } , +} diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.clang-format b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.clang-format new file mode 100644 index 00000000..32799e7d --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.clang-format @@ -0,0 +1,7 @@ +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +IndentCaseLabels: false +SortIncludes: false +ColumnLimit: 80 +IndentWidth: 2 diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.github/FUNDING.yml b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.github/FUNDING.yml new file mode 100644 index 00000000..66fb523f --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.github/FUNDING.yml @@ -0,0 +1 @@ +github: Conni2461 diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.github/workflows/ci.yml new file mode 100644 index 00000000..34a0d02b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.github/workflows/ci.yml @@ -0,0 +1,94 @@ +name: CI + +on: [push, pull_request] + +jobs: + gcc: + name: c build and tests + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + compiler: gcc + - os: ubuntu-latest + compiler: clang + - os: macos-latest + compiler: gcc + - os: macos-latest + compiler: clang + steps: + - uses: actions/checkout@v3 + - name: Prepare + env: + CC: ${{ matrix.compiler }} + run: | + cc --version + git clone https://github.com/Conni2461/examiner + cd examiner + make && sudo make install + - name: Build + env: + CC: ${{ matrix.compiler }} + LD_LIBRARY_PATH: /usr/lib:/usr/local/lib + run: make + - name: Tests + env: + CC: ${{ matrix.compiler }} + LD_LIBRARY_PATH: /usr/lib:/usr/local/lib + run: make test + + windows: + name: windows + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + - uses: lukka/get-cmake@latest + - name: Build + run: | + cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release + cmake --build build --config Release + cmake --install build --prefix build + + nvim-tests: + name: nvim-tests + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + rev: nightly/nvim-linux64.tar.gz + - os: ubuntu-latest + rev: v0.9.0/nvim-linux64.tar.gz + - os: macos-latest + rev: nightly/nvim-macos.tar.gz + - os: macos-latest + rev: v0.9.0/nvim-macos.tar.gz + steps: + - uses: actions/checkout@v3 + - run: date +%F > todays-date + - name: Restore from todays cache + uses: actions/cache@v3 + with: + path: _neovim + key: ${{ runner.os }}-${{ matrix.rev }}-${{ hashFiles('todays-date') }} + + - name: Prepare + run: | + test -d _neovim || { + mkdir -p _neovim + curl -sL "https://github.com/neovim/neovim/releases/download/${{ matrix.rev }}" | tar xzf - --strip-components=1 -C "${PWD}/_neovim" + } + mkdir -p ~/.local/share/nvim/site/pack/vendor/start + git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim + ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start + - name: Build + run: make + - name: Tests + run: | + export PATH="${PWD}/_neovim/bin:${PATH}" + export VIM="${PWD}/_neovim/share/nvim/runtime" + nvim --version + make ntest diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.github/workflows/lint.yml b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.github/workflows/lint.yml new file mode 100644 index 00000000..0b996de9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.github/workflows/lint.yml @@ -0,0 +1,43 @@ +name: Linting and style checking + +on: [push, pull_request] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Prepare + run: | + sudo apt-get update + sudo apt-get install luarocks + sudo luarocks install luacheck + + - name: Lint + run: make lint + + clangformat: + name: clangformat + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Prepare clang-format + run: | + sudo apt-get update + sudo apt-get install clang-format + - name: Format + run: make format + + stylua: + name: stylua + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: JohnnyMorganz/stylua-action@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + # CLI arguments + args: --color always --check lua/ diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.gitignore b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.gitignore new file mode 100644 index 00000000..0b1aea85 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.gitignore @@ -0,0 +1,4 @@ +build/ +.cache/ + +compile_commands.json diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.luacheckrc b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.luacheckrc new file mode 100644 index 00000000..ce3221f1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.luacheckrc @@ -0,0 +1,16 @@ +-- cache false so i don't need sudo upstream +cache = false +std = luajit +codes = true +self = false + +-- Glorious list of warnings: https://luacheck.readthedocs.io/en/stable/warnings.html +ignore = { + "212", -- Unused argument, In the case of callback function, _arg_name is easier to understand than _, so this option is set to off. + "122", -- Indirectly setting a readonly global +} + +-- Global objects defined by the C code +read_globals = { + "vim", +} diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.stylua.toml b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.stylua.toml new file mode 100644 index 00000000..ecb6dca5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/.stylua.toml @@ -0,0 +1,6 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferDouble" +call_parentheses = "None" diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/CMakeLists.txt b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/CMakeLists.txt new file mode 100644 index 00000000..1e792095 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.2) +project(fzf LANGUAGES C) + +add_library(${PROJECT_NAME} SHARED "src/fzf.c") + +target_include_directories(${PROJECT_NAME} PUBLIC + $) +target_compile_options(${PROJECT_NAME} + PRIVATE + $<$:/W4> + $<$>:-Wall>) +target_compile_definitions(${PROJECT_NAME} + PRIVATE + $<$:_CRT_NONSTDC_NO_DEPRECATE> + $<$:_CRT_SECURE_NO_DEPRECATE> + $<$:_CRT_SECURE_NO_WARNINGS>) + +set_target_properties(${PROJECT_NAME} PROPERTIES + WINDOWS_EXPORT_ALL_SYMBOLS ON + C_STANDARD 99 + PREFIX lib) + +# This cannot be a generator expression in this version of CMake +if (NOT (MSVC OR MSYS OR MINGW OR WIN32)) + set_property(TARGET ${PROJECT_NAME} PROPERTY SUFFIX .so) +endif() + +install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_BINARY_DIR}) diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/CMakePresets.json b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/CMakePresets.json new file mode 100644 index 00000000..93688294 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/CMakePresets.json @@ -0,0 +1,28 @@ +{ + "version": 1, + "cmakeMinimumRequired": { + "major": 3, + "minor": 19, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "ninja", + "inherits": "base", + "generator": "Ninja" + }, + { + "name": "make", + "inherits": "base", + "generator": "Unix Makefiles" + } + ] +} diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/LICENSE b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/LICENSE new file mode 100644 index 00000000..57a10051 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Simon Hauser + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/Makefile b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/Makefile new file mode 100644 index 00000000..98427c01 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/Makefile @@ -0,0 +1,46 @@ +CFLAGS += -Wall -Werror -fpic -std=gnu99 + +ifeq ($(OS),Windows_NT) + CC = gcc + TARGET := libfzf.dll +ifeq (,$(findstring MSYS,$(MSYSTEM))) + # On Windows, but NOT msys + MKD = cmd /C mkdir + RM = cmd /C rmdir /Q /S +else + MKD = mkdir -p + RM = rm -rf +endif +else + MKD = mkdir -p + RM = rm -rf + TARGET := libfzf.so +endif + +all: build/$(TARGET) + +build/$(TARGET): src/fzf.c src/fzf.h + $(MKD) build + $(CC) -O3 $(CFLAGS) -shared src/fzf.c -o build/$(TARGET) + +build/test: build/$(TARGET) test/test.c + $(CC) -Og -ggdb3 $(CFLAGS) test/test.c -o build/test -I./src -L./build -lfzf -lexaminer + +.PHONY: lint format clangdhappy clean test ntest +lint: + luacheck lua + +format: + clang-format --style=file --dry-run -Werror src/fzf.c src/fzf.h test/test.c + +test: build/test + @LD_LIBRARY_PATH=${PWD}/build:${PWD}/examiner/build:${LD_LIBRARY_PATH} ./build/test + +ntest: + nvim --headless --noplugin -u test/minrc.vim -c "PlenaryBustedDirectory test/ { minimal_init = './test/minrc.vim' }" + +clangdhappy: + compiledb make + +clean: + $(RM) build diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/README.md b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/README.md new file mode 100644 index 00000000..ba0886f4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/README.md @@ -0,0 +1,179 @@ +# telescope-fzf-native.nvim + +**fzf-native** is a `c` port of **[fzf][fzf]**. It only covers the algorithm and +implements few functions to support calculating the score. + +This means that the [fzf syntax](https://github.com/junegunn/fzf#search-syntax) +is supported: + +| Token | Match type | Description | +| --------- | -------------------------- | ------------------------------------ | +| `sbtrkt` | fuzzy-match | Items that match `sbtrkt` | +| `'wild` | exact-match (quoted) | Items that include `wild` | +| `^music` | prefix-exact-match | Items that start with `music` | +| `.mp3$` | suffix-exact-match | Items that end with `.mp3` | +| `!fire` | inverse-exact-match | Items that do not include `fire` | +| `!^music` | inverse-prefix-exact-match | Items that do not start with `music` | +| `!.mp3$` | inverse-suffix-exact-match | Items that do not end with `.mp3` | + +A single bar character term acts as an OR operator. For example, the following +query matches entries that start with `core` and end with either `go`, `rb`, +or `py`. + +``` +^core go$ | rb$ | py$ +``` + +This is an advantage over the more simpler `fzy` algorithm, which is also +available for telescope (as native component or as lua component). + +## Installation + +To get **fzf-native** working, you need to build it with either `cmake` or `make`. As of now, we do not ship binaries. +Both install methods will be supported going forward. + +### CMake (Windows, Linux, MacOS) + +This requires: + +- CMake, and the Microsoft C++ Build Tools on Windows +- CMake, make, and GCC or Clang on Linux and MacOS + +#### vim-plug + +```viml +Plug 'nvim-telescope/telescope-fzf-native.nvim', { 'do': 'cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release && cmake --build build --config Release && cmake --install build --prefix build' } +``` + +#### packer.nvim + +```lua +use { 'nvim-telescope/telescope-fzf-native.nvim', run = 'cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release && cmake --build build --config Release && cmake --install build --prefix build' } +``` + +#### lazy.nvim + +```lua +{ 'nvim-telescope/telescope-fzf-native.nvim', build = 'cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release && cmake --build build --config Release && cmake --install build --prefix build' } +``` + +### Make (Linux, MacOS, Windows with MinGW) + +This requires `gcc` or `clang` and `make` + +#### vim-plug + +```viml +Plug 'nvim-telescope/telescope-fzf-native.nvim', { 'do': 'make' } +``` + +#### packer.nvim + +```lua +use { 'nvim-telescope/telescope-fzf-native.nvim', run = 'make' } +``` + +#### lazy.nvim + +```lua +{ 'nvim-telescope/telescope-fzf-native.nvim', build = 'make' } +``` + +## Telescope Setup and Configuration: + +```lua +-- You dont need to set any of these options. These are the default ones. Only +-- the loading is important +require('telescope').setup { + extensions = { + fzf = { + fuzzy = true, -- false will only do exact matching + override_generic_sorter = true, -- override the generic sorter + override_file_sorter = true, -- override the file sorter + case_mode = "smart_case", -- or "ignore_case" or "respect_case" + -- the default case_mode is "smart_case" + } + } +} +-- To get fzf loaded and working with telescope, you need to call +-- load_extension, somewhere after setup function: +require('telescope').load_extension('fzf') +``` + +## Developer Interface + +This section is only addressed towards developers who plan to use the library +(c or lua bindings). +This section is not addressed towards users of the telescope extension. + +### C Interface + +```c +fzf_slab_t *slab = fzf_make_default_slab(); +/* fzf_case_mode enum : CaseSmart = 0, CaseIgnore, CaseRespect + * normalize bool : always set to false because its not implemented yet. + * This is reserved for future use + * pattern char* : pattern you want to match. e.g. "src | lua !.c$ + * fuzzy bool : enable or disable fuzzy matching + */ +fzf_pattern_t *pattern = fzf_parse_pattern(CaseSmart, false, "src | lua !.c$", true); + +/* you can get the score/position for as many items as you want */ +int score = fzf_get_score(line, pattern, slab); +fzf_position_t *pos = fzf_get_positions(line, pattern, slab); + +fzf_free_positions(pos); +fzf_free_pattern(pattern); +fzf_free_slab(slab); +``` + +### Lua Interface + +```lua +local fzf = require('fzf_lib') + +local slab = fzf.allocate_slab() +-- pattern: string +-- case_mode: number with 0 = smart_case, 1 = ignore_case, 2 = respect_case +-- fuzzy: enable or disable fuzzy matching. default true +local pattern_obj = fzf.parse_pattern(pattern, case_mode, fuzzy) + +-- you can get the score/position for as many items as you want +-- line: string +-- score: number +local score = fzf.get_score(line, pattern_obj, slab) + +-- table (does not have to be freed) +local pos = fzf.get_pos(line, pattern_obj, slab) + +fzf.free_pattern(pattern_obj) +fzf.free_slab(slab) +``` + +## Disclaimer + +This projects implements **[fzf][fzf]** algorithm in c. So there might be +differences in matching. I don't guarantee completeness. + +### TODO + +Stuff still missing that is present in **[fzf][fzf]**. + +- [ ] normalize +- [ ] case for unicode (i don't think this works currently) + +## Benchmark + +Comparison with fzy-native and fzy-lua with a table containing 240201 file +strings. It calculated the score and position (if score > 0) for each of these +strings with the pattern that is listed below: + +![benchmark 1](https://raw.githubusercontent.com/wiki/nvim-telescope/telescope.nvim/imgs/bench1.png) +![benchmark 2](https://raw.githubusercontent.com/wiki/nvim-telescope/telescope.nvim/imgs/bench2.png) + +## Credit + +All credit for the algorithm goes to junegunn and his work on **[fzf][fzf]**. +This is merely a c fork distributed under MIT for telescope. + +[fzf]: https://github.com/junegunn/fzf diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/lua/fzf_lib.lua b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/lua/fzf_lib.lua new file mode 100644 index 00000000..bced3d8d --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/lua/fzf_lib.lua @@ -0,0 +1,85 @@ +local ffi = require "ffi" + +local library_path = (function() + local dirname = string.sub(debug.getinfo(1).source, 2, #"/fzf_lib.lua" * -1) + if package.config:sub(1, 1) == "\\" then + return dirname .. "../build/libfzf.dll" + else + return dirname .. "../build/libfzf.so" + end +end)() +local native = ffi.load(library_path) + +ffi.cdef [[ + typedef struct {} fzf_i16_t; + typedef struct {} fzf_i32_t; + typedef struct { + fzf_i16_t I16; + fzf_i32_t I32; + } fzf_slab_t; + + typedef struct {} fzf_term_set_t; + typedef struct { + fzf_term_set_t **ptr; + size_t size; + size_t cap; + } fzf_pattern_t; + typedef struct { + uint32_t *data; + size_t size; + size_t cap; + } fzf_position_t; + + fzf_position_t *fzf_get_positions(const char *text, fzf_pattern_t *pattern, fzf_slab_t *slab); + void fzf_free_positions(fzf_position_t *pos); + int32_t fzf_get_score(const char *text, fzf_pattern_t *pattern, fzf_slab_t *slab); + + fzf_pattern_t *fzf_parse_pattern(int32_t case_mode, bool normalize, char *pattern, bool fuzzy); + void fzf_free_pattern(fzf_pattern_t *pattern); + + fzf_slab_t *fzf_make_default_slab(void); + void fzf_free_slab(fzf_slab_t *slab); +]] + +local fzf = {} + +fzf.get_score = function(input, pattern_struct, slab) + return native.fzf_get_score(input, pattern_struct, slab) +end + +fzf.get_pos = function(input, pattern_struct, slab) + local pos = native.fzf_get_positions(input, pattern_struct, slab) + if pos == nil then + return + end + + local res = {} + for i = 1, tonumber(pos.size) do + res[i] = pos.data[i - 1] + 1 + end + native.fzf_free_positions(pos) + + return res +end + +fzf.parse_pattern = function(pattern, case_mode, fuzzy) + case_mode = case_mode == nil and 0 or case_mode + fuzzy = fuzzy == nil and true or fuzzy + local c_str = ffi.new("char[?]", #pattern + 1) + ffi.copy(c_str, pattern) + return native.fzf_parse_pattern(case_mode, false, c_str, fuzzy) +end + +fzf.free_pattern = function(p) + native.fzf_free_pattern(p) +end + +fzf.allocate_slab = function() + return native.fzf_make_default_slab() +end + +fzf.free_slab = function(s) + native.fzf_free_slab(s) +end + +return fzf diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/lua/telescope/_extensions/fzf.lua b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/lua/telescope/_extensions/fzf.lua new file mode 100644 index 00000000..09aa07a2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/lua/telescope/_extensions/fzf.lua @@ -0,0 +1,202 @@ +local fzf = require "fzf_lib" +local sorters = require "telescope.sorters" + +local case_enum = setmetatable({ + ["smart_case"] = 0, + ["ignore_case"] = 1, + ["respect_case"] = 2, +}, { + __index = function(_, k) + error(string.format("%s is not a valid case mode", k)) + end, + __newindex = function() + error "Don't set new things" + end, +}) + +local get_fzf_sorter = function(opts) + local case_mode = case_enum[opts.case_mode] + local fuzzy_mode = opts.fuzzy == nil and true or opts.fuzzy + local post_or = false + local post_inv = false + local post_escape = false + + local get_struct = function(self, prompt) + local struct = self.state.prompt_cache[prompt] + if not struct then + struct = fzf.parse_pattern(prompt, case_mode, fuzzy_mode) + self.state.prompt_cache[prompt] = struct + end + return struct + end + + local clear_filter_fun = function(self, prompt) + local filter = "^(" .. self._delimiter .. "(%S+)" .. "[" .. self._delimiter .. "%s]" .. ")" + local matched = prompt:match(filter) + + if matched == nil then + return prompt + end + return prompt:sub(#matched + 1, -1) + end + + return sorters.Sorter:new { + init = function(self) + self.state.slab = fzf.allocate_slab() + self.state.prompt_cache = {} + + if self.filter_function then + self.__highlight_prefilter = clear_filter_fun + end + end, + destroy = function(self) + for _, v in pairs(self.state.prompt_cache) do + fzf.free_pattern(v) + end + self.state.prompt_cache = {} + if self.state.slab ~= nil then + fzf.free_slab(self.state.slab) + self.state.slab = nil + end + end, + start = function(self, prompt) + local last = prompt:sub(-1, -1) + + if last == "|" then + self._discard_state.filtered = {} + post_or = true + elseif last == " " and post_or then + self._discard_state.filtered = {} + elseif post_or then + self._discard_state.filtered = {} + post_or = false + else + post_or = false + end + + if last == "\\" and not post_escape then + self._discard_state.filtered = {} + post_escape = true + else + self._discard_state.filtered = {} + post_escape = false + end + + if last == "!" and not post_inv then + post_inv = true + self._discard_state.filtered = {} + elseif post_inv then + self._discard_state.filtered = {} + elseif post_inv and " " then + post_inv = false + end + end, + discard = true, + scoring_function = function(self, prompt, line) + local obj = get_struct(self, prompt) + local score = fzf.get_score(line, obj, self.state.slab) + if score == 0 then + return -1 + else + return 1 / score + end + end, + highlighter = function(self, prompt, display) + if self.__highlight_prefilter then + prompt = self:__highlight_prefilter(prompt) + end + return fzf.get_pos(display, get_struct(self, prompt), self.state.slab) + end, + } +end + +local fast_extend = function(opts, conf) + local ret = {} + ret.case_mode = vim.F.if_nil(opts.case_mode, conf.case_mode) + ret.fuzzy = vim.F.if_nil(opts.fuzzy, conf.fuzzy) + return ret +end + +local wrap_sorter = function(conf) + return function(opts) + opts = opts or {} + return get_fzf_sorter(fast_extend(opts, conf)) + end +end + +return require("telescope").register_extension { + setup = function(ext_config, config) + local override_file = vim.F.if_nil(ext_config.override_file_sorter, true) + local override_generic = vim.F.if_nil(ext_config.override_generic_sorter, true) + + local conf = {} + conf.case_mode = vim.F.if_nil(ext_config.case_mode, "smart_case") + conf.fuzzy = vim.F.if_nil(ext_config.fuzzy, true) + + if override_file then + config.file_sorter = wrap_sorter(conf) + end + + if override_generic then + config.generic_sorter = wrap_sorter(conf) + end + end, + exports = { + native_fzf_sorter = function(opts) + return get_fzf_sorter(opts or { case_mode = "smart_case", fuzzy = true }) + end, + }, + health = function() + local health = vim.health or require "health" + local ok = health.ok or health.report_ok + local warn = health.warn or health.report_warn + local error = health.error or health.report_error + + local good = true + local eq = function(expected, actual) + if tostring(expected) ~= tostring(actual) then + good = false + end + end + + local p = fzf.parse_pattern("fzf", 0) + local slab = fzf.allocate_slab() + + eq(80, fzf.get_score("src/fzf", p, slab)) + eq(0, fzf.get_score("asdf", p, slab)) + eq(54, fzf.get_score("fasdzasdf", p, slab)) + + fzf.free_pattern(p) + fzf.free_slab(slab) + + if good then + ok "lib working as expected" + else + error "lib not working as expected, please reinstall and open an issue if this error persists" + return + end + + local has, config = pcall(require, "telescope.config") + if not has then + error "unexpected: telescope configuration couldn't be loaded" + end + + local test_sorter = function(name, sorter) + good = true + sorter:_init() + local prompt = "fzf !lua" + eq(1 / 80, sorter:scoring_function(prompt, "src/fzf")) + eq(-1, sorter:scoring_function(prompt, "lua/fzf")) + eq(-1, sorter:scoring_function(prompt, "asdf")) + sorter:_destroy() + + if good then + ok(name .. " correctly configured") + else + warn(name .. " is not configured") + end + end + test_sorter("file_sorter", config.values.file_sorter {}) + test_sorter("generic_sorter", config.values.generic_sorter {}) + end, +} diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/src/fzf.c b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/src/fzf.c new file mode 100644 index 00000000..c4620bfb --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/src/fzf.c @@ -0,0 +1,1272 @@ +#include "fzf.h" + +#include +#include +#include + +// TODO(conni2461): UNICODE HEADER +#define UNICODE_MAXASCII 0x7f + +#define SFREE(x) \ + if (x) { \ + free(x); \ + } + +/* Helpers */ +#define free_alloc(obj) \ + if ((obj).allocated) { \ + free((obj).data); \ + } + +#define gen_simple_slice(name, type) \ + typedef struct { \ + type *data; \ + size_t size; \ + } name##_slice_t; \ + static name##_slice_t slice_##name(type *input, size_t from, size_t to) { \ + return (name##_slice_t){.data = input + from, .size = to - from}; \ + } + +#define gen_slice(name, type) \ + gen_simple_slice(name, type); \ + static name##_slice_t slice_##name##_right(type *input, size_t to) { \ + return slice_##name(input, 0, to); \ + } + +gen_slice(i16, int16_t); +gen_simple_slice(i32, int32_t); +gen_slice(str, const char); +#undef gen_slice +#undef gen_simple_slice + +/* TODO(conni2461): additional types (utf8) */ +typedef int32_t char_class; +typedef char byte; + +typedef enum { + ScoreMatch = 16, + ScoreGapStart = -3, + ScoreGapExtention = -1, + BonusBoundary = ScoreMatch / 2, + BonusNonWord = ScoreMatch / 2, + BonusCamel123 = BonusBoundary + ScoreGapExtention, + BonusConsecutive = -(ScoreGapStart + ScoreGapExtention), + BonusFirstCharMultiplier = 2, +} score_t; + +typedef enum { + CharNonWord = 0, + CharLower, + CharUpper, + CharLetter, + CharNumber +} char_types; + +static int32_t index_byte(fzf_string_t *string, char b) { + for (size_t i = 0; i < string->size; i++) { + if (string->data[i] == b) { + return (int32_t)i; + } + } + return -1; +} + +static size_t leading_whitespaces(fzf_string_t *str) { + size_t whitespaces = 0; + for (size_t i = 0; i < str->size; i++) { + if (!isspace((uint8_t)str->data[i])) { + break; + } + whitespaces++; + } + return whitespaces; +} + +static size_t trailing_whitespaces(fzf_string_t *str) { + size_t whitespaces = 0; + for (size_t i = str->size - 1; i >= 0; i--) { + if (!isspace((uint8_t)str->data[i])) { + break; + } + whitespaces++; + } + return whitespaces; +} + +static void copy_runes(fzf_string_t *src, fzf_i32_t *destination) { + for (size_t i = 0; i < src->size; i++) { + destination->data[i] = (int32_t)src->data[i]; + } +} + +static void copy_into_i16(i16_slice_t *src, fzf_i16_t *dest) { + for (size_t i = 0; i < src->size; i++) { + dest->data[i] = src->data[i]; + } +} + +// char* helpers +static char *trim_whitespace_left(char *str, size_t *len) { + for (size_t i = 0; i < *len; i++) { + if (str[0] == ' ') { + (*len)--; + str++; + } else { + break; + } + } + return str; +} + +static bool has_prefix(const char *str, const char *prefix, size_t prefix_len) { + return strncmp(prefix, str, prefix_len) == 0; +} + +static bool has_suffix(const char *str, size_t len, const char *suffix, + size_t suffix_len) { + return len >= suffix_len && + strncmp(slice_str(str, len - suffix_len, len).data, suffix, + suffix_len) == 0; +} + +static char *str_replace_char(char *str, char find, char replace) { + char *current_pos = strchr(str, find); + while (current_pos) { + *current_pos = replace; + current_pos = strchr(current_pos, find); + } + return str; +} + +static char *str_replace(char *orig, char *rep, char *with) { + if (!orig || !rep || !with) { + return NULL; + } + + char *result; + char *ins; + char *tmp; + + size_t len_rep = strlen(rep); + size_t len_front = 0; + size_t len_orig = strlen(orig); + size_t len_with = strlen(with); + size_t count = 0; + + if (len_rep == 0) { + return NULL; + } + + ins = orig; + for (; (tmp = strstr(ins, rep)); ++count) { + ins = tmp + len_rep; + } + + tmp = result = (char *)malloc(len_orig + (len_with - len_rep) * count + 1); + if (!result) { + return NULL; + } + + while (count--) { + ins = strstr(orig, rep); + len_front = (size_t)(ins - orig); + tmp = strncpy(tmp, orig, len_front) + len_front; + tmp = strcpy(tmp, with) + len_with; + orig += len_front + len_rep; + len_orig -= len_front + len_rep; + } + strncpy(tmp, orig, len_orig); + tmp[len_orig] = 0; + return result; +} + +// TODO(conni2461): REFACTOR +static char *str_tolower(char *str, size_t size) { + char *lower_str = (char *)malloc((size + 1) * sizeof(char)); + for (size_t i = 0; i < size; i++) { + lower_str[i] = (char)tolower((uint8_t)str[i]); + } + lower_str[size] = '\0'; + return lower_str; +} + +static int16_t max16(int16_t a, int16_t b) { + return (a > b) ? a : b; +} + +static size_t min64u(size_t a, size_t b) { + return (a < b) ? a : b; +} + +fzf_position_t *fzf_pos_array(size_t len) { + fzf_position_t *pos = (fzf_position_t *)malloc(sizeof(fzf_position_t)); + pos->size = 0; + pos->cap = len; + if (len > 0) { + pos->data = (uint32_t *)malloc(len * sizeof(uint32_t)); + } else { + pos->data = NULL; + } + return pos; +} + +static void resize_pos(fzf_position_t *pos, size_t add_len, size_t comp) { + if (!pos) { + return; + } + if (pos->size + comp > pos->cap) { + pos->cap += add_len > 0 ? add_len : 1; + pos->data = (uint32_t *)realloc(pos->data, sizeof(uint32_t) * pos->cap); + } +} + +static void unsafe_append_pos(fzf_position_t *pos, size_t value) { + resize_pos(pos, pos->cap, 1); + pos->data[pos->size] = value; + pos->size++; +} + +static void append_pos(fzf_position_t *pos, size_t value) { + if (pos) { + unsafe_append_pos(pos, value); + } +} + +static void insert_range(fzf_position_t *pos, size_t start, size_t end) { + if (!pos) { + return; + } + + int32_t diff = ((int32_t)end - (int32_t)start); + if (diff <= 0) { + return; + } + + resize_pos(pos, end - start, end - start); + for (size_t k = start; k < end; k++) { + pos->data[pos->size] = k; + pos->size++; + } +} + +static fzf_i16_t alloc16(size_t *offset, fzf_slab_t *slab, size_t size) { + if (slab != NULL && slab->I16.cap > *offset + size) { + i16_slice_t slice = slice_i16(slab->I16.data, *offset, (*offset) + size); + *offset = *offset + size; + return (fzf_i16_t){.data = slice.data, + .size = slice.size, + .cap = slice.size, + .allocated = false}; + } + int16_t *data = (int16_t *)malloc(size * sizeof(int16_t)); + memset(data, 0, size * sizeof(int16_t)); + return (fzf_i16_t){ + .data = data, .size = size, .cap = size, .allocated = true}; +} + +static fzf_i32_t alloc32(size_t *offset, fzf_slab_t *slab, size_t size) { + if (slab != NULL && slab->I32.cap > *offset + size) { + i32_slice_t slice = slice_i32(slab->I32.data, *offset, (*offset) + size); + *offset = *offset + size; + return (fzf_i32_t){.data = slice.data, + .size = slice.size, + .cap = slice.size, + .allocated = false}; + } + int32_t *data = (int32_t *)malloc(size * sizeof(int32_t)); + memset(data, 0, size * sizeof(int32_t)); + return (fzf_i32_t){ + .data = data, .size = size, .cap = size, .allocated = true}; +} + +static char_class char_class_of_ascii(char ch) { + if (ch >= 'a' && ch <= 'z') { + return CharLower; + } + if (ch >= 'A' && ch <= 'Z') { + return CharUpper; + } + if (ch >= '0' && ch <= '9') { + return CharNumber; + } + return CharNonWord; +} + +// static char_class char_class_of_non_ascii(char ch) { +// return 0; +// } + +static char_class char_class_of(char ch) { + return char_class_of_ascii(ch); + // if (ch <= 0x7f) { + // return char_class_of_ascii(ch); + // } + // return char_class_of_non_ascii(ch); +} + +static int16_t bonus_for(char_class prev_class, char_class class) { + if (prev_class == CharNonWord && class != CharNonWord) { + return BonusBoundary; + } + if ((prev_class == CharLower && class == CharUpper) || + (prev_class != CharNumber && class == CharNumber)) { + return BonusCamel123; + } + if (class == CharNonWord) { + return BonusNonWord; + } + return 0; +} + +static int16_t bonus_at(fzf_string_t *input, size_t idx) { + if (idx == 0) { + return BonusBoundary; + } + return bonus_for(char_class_of(input->data[idx - 1]), + char_class_of(input->data[idx])); +} + +/* TODO(conni2461): maybe just not do this */ +static char normalize_rune(char r) { + // TODO(conni2461) + /* if (r < 0x00C0 || r > 0x2184) { */ + /* return r; */ + /* } */ + /* rune n = normalized[r]; */ + /* if n > 0 { */ + /* return n; */ + /* } */ + return r; +} + +static int32_t try_skip(fzf_string_t *input, bool case_sensitive, byte b, + int32_t from) { + str_slice_t slice = slice_str(input->data, (size_t)from, input->size); + fzf_string_t byte_array = {.data = slice.data, .size = slice.size}; + int32_t idx = index_byte(&byte_array, b); + if (idx == 0) { + return from; + } + + if (!case_sensitive && b >= 'a' && b <= 'z') { + if (idx > 0) { + str_slice_t tmp = slice_str_right(byte_array.data, (size_t)idx); + byte_array.data = tmp.data; + byte_array.size = tmp.size; + } + int32_t uidx = index_byte(&byte_array, b - (byte)32); + if (uidx >= 0) { + idx = uidx; + } + } + if (idx < 0) { + return -1; + } + + return from + idx; +} + +static bool is_ascii(const char *runes, size_t size) { + // TODO(conni2461): future use + /* for (size_t i = 0; i < size; i++) { */ + /* if (runes[i] >= 256) { */ + /* return false; */ + /* } */ + /* } */ + return true; +} + +static int32_t ascii_fuzzy_index(fzf_string_t *input, const char *pattern, + size_t size, bool case_sensitive) { + if (!is_ascii(pattern, size)) { + return -1; + } + + int32_t first_idx = 0; + int32_t idx = 0; + for (size_t pidx = 0; pidx < size; pidx++) { + idx = try_skip(input, case_sensitive, pattern[pidx], idx); + if (idx < 0) { + return -1; + } + if (pidx == 0 && idx > 0) { + first_idx = idx - 1; + } + idx++; + } + + return first_idx; +} + +static int32_t calculate_score(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + size_t sidx, size_t eidx, fzf_position_t *pos) { + const size_t M = pattern->size; + + size_t pidx = 0; + int32_t score = 0; + int32_t consecutive = 0; + bool in_gap = false; + int16_t first_bonus = 0; + + resize_pos(pos, M, M); + int32_t prev_class = CharNonWord; + if (sidx > 0) { + prev_class = char_class_of(text->data[sidx - 1]); + } + for (size_t idx = sidx; idx < eidx; idx++) { + char c = text->data[idx]; + int32_t class = char_class_of(c); + if (!case_sensitive) { + /* TODO(conni2461): He does some unicode stuff here, investigate */ + c = (char)tolower((uint8_t)c); + } + if (normalize) { + c = normalize_rune(c); + } + if (c == pattern->data[pidx]) { + append_pos(pos, idx); + score += ScoreMatch; + int16_t bonus = bonus_for(prev_class, class); + if (consecutive == 0) { + first_bonus = bonus; + } else { + if (bonus == BonusBoundary) { + first_bonus = bonus; + } + bonus = max16(max16(bonus, first_bonus), BonusConsecutive); + } + if (pidx == 0) { + score += (int32_t)(bonus * BonusFirstCharMultiplier); + } else { + score += (int32_t)bonus; + } + in_gap = false; + consecutive++; + pidx++; + } else { + if (in_gap) { + score += ScoreGapExtention; + } else { + score += ScoreGapStart; + } + in_gap = true; + consecutive = 0; + first_bonus = 0; + } + prev_class = class; + } + return score; +} + +fzf_result_t fzf_fuzzy_match_v1(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab) { + const size_t M = pattern->size; + const size_t N = text->size; + if (M == 0) { + return (fzf_result_t){0, 0, 0}; + } + if (ascii_fuzzy_index(text, pattern->data, M, case_sensitive) < 0) { + return (fzf_result_t){-1, -1, 0}; + } + + int32_t pidx = 0; + int32_t sidx = -1; + int32_t eidx = -1; + for (size_t idx = 0; idx < N; idx++) { + char c = text->data[idx]; + /* TODO(conni2461): Common pattern maybe a macro would be good here */ + if (!case_sensitive) { + /* TODO(conni2461): He does some unicode stuff here, investigate */ + c = (char)tolower((uint8_t)c); + } + if (normalize) { + c = normalize_rune(c); + } + if (c == pattern->data[pidx]) { + if (sidx < 0) { + sidx = (int32_t)idx; + } + pidx++; + if (pidx == M) { + eidx = (int32_t)idx + 1; + break; + } + } + } + if (sidx >= 0 && eidx >= 0) { + size_t start = (size_t)sidx; + size_t end = (size_t)eidx; + pidx--; + for (size_t idx = end - 1; idx >= start; idx--) { + char c = text->data[idx]; + if (!case_sensitive) { + /* TODO(conni2461): He does some unicode stuff here, investigate */ + c = (char)tolower((uint8_t)c); + } + if (c == pattern->data[pidx]) { + pidx--; + if (pidx < 0) { + start = idx; + break; + } + } + } + + int32_t score = calculate_score(case_sensitive, normalize, text, pattern, + start, end, pos); + return (fzf_result_t){(int32_t)start, (int32_t)end, score}; + } + return (fzf_result_t){-1, -1, 0}; +} + +fzf_result_t fzf_fuzzy_match_v2(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab) { + const size_t M = pattern->size; + const size_t N = text->size; + if (M == 0) { + return (fzf_result_t){0, 0, 0}; + } + if (slab != NULL && N * M > slab->I16.cap) { + return fzf_fuzzy_match_v1(case_sensitive, normalize, text, pattern, pos, + slab); + } + + size_t idx; + { + int32_t tmp_idx = ascii_fuzzy_index(text, pattern->data, M, case_sensitive); + if (tmp_idx < 0) { + return (fzf_result_t){-1, -1, 0}; + } + idx = (size_t)tmp_idx; + } + + size_t offset16 = 0; + size_t offset32 = 0; + + fzf_i16_t h0 = alloc16(&offset16, slab, N); + fzf_i16_t c0 = alloc16(&offset16, slab, N); + // Bonus point for each positions + fzf_i16_t bo = alloc16(&offset16, slab, N); + // The first occurrence of each character in the pattern + fzf_i32_t f = alloc32(&offset32, slab, M); + // Rune array + fzf_i32_t t = alloc32(&offset32, slab, N); + copy_runes(text, &t); // input.CopyRunes(T) + + // Phase 2. Calculate bonus for each point + int16_t max_score = 0; + size_t max_score_pos = 0; + + size_t pidx = 0; + size_t last_idx = 0; + + char pchar0 = pattern->data[0]; + char pchar = pattern->data[0]; + int16_t prev_h0 = 0; + int32_t prev_class = CharNonWord; + bool in_gap = false; + + i32_slice_t t_sub = slice_i32(t.data, idx, t.size); // T[idx:]; + i16_slice_t h0_sub = + slice_i16_right(slice_i16(h0.data, idx, h0.size).data, t_sub.size); + i16_slice_t c0_sub = + slice_i16_right(slice_i16(c0.data, idx, c0.size).data, t_sub.size); + i16_slice_t b_sub = + slice_i16_right(slice_i16(bo.data, idx, bo.size).data, t_sub.size); + + for (size_t off = 0; off < t_sub.size; off++) { + char_class class; + char c = (char)t_sub.data[off]; + class = char_class_of_ascii(c); + if (!case_sensitive && class == CharUpper) { + /* TODO(conni2461): unicode support */ + c = (char)tolower((uint8_t)c); + } + if (normalize) { + c = normalize_rune(c); + } + + t_sub.data[off] = (uint8_t)c; + int16_t bonus = bonus_for(prev_class, class); + b_sub.data[off] = bonus; + prev_class = class; + if (c == pchar) { + if (pidx < M) { + f.data[pidx] = (int32_t)(idx + off); + pidx++; + pchar = pattern->data[min64u(pidx, M - 1)]; + } + last_idx = idx + off; + } + + if (c == pchar0) { + int16_t score = ScoreMatch + bonus * BonusFirstCharMultiplier; + h0_sub.data[off] = score; + c0_sub.data[off] = 1; + if (M == 1 && (score > max_score)) { + max_score = score; + max_score_pos = idx + off; + if (bonus == BonusBoundary) { + break; + } + } + in_gap = false; + } else { + if (in_gap) { + h0_sub.data[off] = max16(prev_h0 + ScoreGapExtention, 0); + } else { + h0_sub.data[off] = max16(prev_h0 + ScoreGapStart, 0); + } + c0_sub.data[off] = 0; + in_gap = true; + } + prev_h0 = h0_sub.data[off]; + } + if (pidx != M) { + free_alloc(t); + free_alloc(f); + free_alloc(bo); + free_alloc(c0); + free_alloc(h0); + return (fzf_result_t){-1, -1, 0}; + } + if (M == 1) { + free_alloc(t); + free_alloc(f); + free_alloc(bo); + free_alloc(c0); + free_alloc(h0); + fzf_result_t res = {(int32_t)max_score_pos, (int32_t)max_score_pos + 1, + max_score}; + append_pos(pos, max_score_pos); + return res; + } + + size_t f0 = (size_t)f.data[0]; + size_t width = last_idx - f0 + 1; + fzf_i16_t h = alloc16(&offset16, slab, width * M); + { + i16_slice_t h0_tmp_slice = slice_i16(h0.data, f0, last_idx + 1); + copy_into_i16(&h0_tmp_slice, &h); + } + + fzf_i16_t c = alloc16(&offset16, slab, width * M); + { + i16_slice_t c0_tmp_slice = slice_i16(c0.data, f0, last_idx + 1); + copy_into_i16(&c0_tmp_slice, &c); + } + + i32_slice_t f_sub = slice_i32(f.data, 1, f.size); + str_slice_t p_sub = + slice_str_right(slice_str(pattern->data, 1, M).data, f_sub.size); + for (size_t off = 0; off < f_sub.size; off++) { + size_t foff = (size_t)f_sub.data[off]; + pchar = p_sub.data[off]; + pidx = off + 1; + size_t row = pidx * width; + in_gap = false; + t_sub = slice_i32(t.data, foff, last_idx + 1); + b_sub = slice_i16_right(slice_i16(bo.data, foff, bo.size).data, t_sub.size); + i16_slice_t c_sub = slice_i16_right( + slice_i16(c.data, row + foff - f0, c.size).data, t_sub.size); + i16_slice_t c_diag = slice_i16_right( + slice_i16(c.data, row + foff - f0 - 1 - width, c.size).data, + t_sub.size); + i16_slice_t h_sub = slice_i16_right( + slice_i16(h.data, row + foff - f0, h.size).data, t_sub.size); + i16_slice_t h_diag = slice_i16_right( + slice_i16(h.data, row + foff - f0 - 1 - width, h.size).data, + t_sub.size); + i16_slice_t h_left = slice_i16_right( + slice_i16(h.data, row + foff - f0 - 1, h.size).data, t_sub.size); + h_left.data[0] = 0; + for (size_t j = 0; j < t_sub.size; j++) { + char ch = (char)t_sub.data[j]; + size_t col = j + foff; + int16_t s1 = 0; + int16_t s2 = 0; + int16_t consecutive = 0; + + if (in_gap) { + s2 = h_left.data[j] + ScoreGapExtention; + } else { + s2 = h_left.data[j] + ScoreGapStart; + } + + if (pchar == ch) { + s1 = h_diag.data[j] + ScoreMatch; + int16_t b = b_sub.data[j]; + consecutive = c_diag.data[j] + 1; + if (b == BonusBoundary) { + consecutive = 1; + } else if (consecutive > 1) { + b = max16(b, max16(BonusConsecutive, + bo.data[col - ((size_t)consecutive) + 1])); + } + if (s1 + b < s2) { + s1 += b_sub.data[j]; + consecutive = 0; + } else { + s1 += b; + } + } + c_sub.data[j] = consecutive; + in_gap = s1 < s2; + int16_t score = max16(max16(s1, s2), 0); + if (pidx == M - 1 && (score > max_score)) { + max_score = score; + max_score_pos = col; + } + h_sub.data[j] = score; + } + } + + resize_pos(pos, M, M); + size_t j = max_score_pos; + if (pos) { + size_t i = M - 1; + bool prefer_match = true; + for (;;) { + size_t ii = i * width; + size_t j0 = j - f0; + int16_t s = h.data[ii + j0]; + + int16_t s1 = 0; + int16_t s2 = 0; + if (i > 0 && j >= f.data[i]) { + s1 = h.data[ii - width + j0 - 1]; + } + if (j > f.data[i]) { + s2 = h.data[ii + j0 - 1]; + } + + if (s > s1 && (s > s2 || (s == s2 && prefer_match))) { + unsafe_append_pos(pos, j); + if (i == 0) { + break; + } + i--; + } + prefer_match = c.data[ii + j0] > 1 || (ii + width + j0 + 1 < c.size && + c.data[ii + width + j0 + 1] > 0); + j--; + } + } + + free_alloc(h); + free_alloc(c); + free_alloc(t); + free_alloc(f); + free_alloc(bo); + free_alloc(c0); + free_alloc(h0); + return (fzf_result_t){(int32_t)j, (int32_t)max_score_pos + 1, + (int32_t)max_score}; +} + +fzf_result_t fzf_exact_match_naive(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab) { + const size_t M = pattern->size; + const size_t N = text->size; + + if (M == 0) { + return (fzf_result_t){0, 0, 0}; + } + if (N < M) { + return (fzf_result_t){-1, -1, 0}; + } + if (ascii_fuzzy_index(text, pattern->data, M, case_sensitive) < 0) { + return (fzf_result_t){-1, -1, 0}; + } + + size_t pidx = 0; + int32_t best_pos = -1; + int16_t bonus = 0; + int16_t best_bonus = -1; + for (size_t idx = 0; idx < N; idx++) { + char c = text->data[idx]; + if (!case_sensitive) { + /* TODO(conni2461): He does some unicode stuff here, investigate */ + c = (char)tolower((uint8_t)c); + } + if (normalize) { + c = normalize_rune(c); + } + if (c == pattern->data[pidx]) { + if (pidx == 0) { + bonus = bonus_at(text, idx); + } + pidx++; + if (pidx == M) { + if (bonus > best_bonus) { + best_pos = (int32_t)idx; + best_bonus = bonus; + } + if (bonus == BonusBoundary) { + break; + } + idx -= pidx - 1; + pidx = 0; + bonus = 0; + } + } else { + idx -= pidx; + pidx = 0; + bonus = 0; + } + } + if (best_pos >= 0) { + size_t bp = (size_t)best_pos; + size_t sidx = bp - M + 1; + size_t eidx = bp + 1; + int32_t score = calculate_score(case_sensitive, normalize, text, pattern, + sidx, eidx, NULL); + insert_range(pos, sidx, eidx); + return (fzf_result_t){(int32_t)sidx, (int32_t)eidx, score}; + } + return (fzf_result_t){-1, -1, 0}; +} + +fzf_result_t fzf_prefix_match(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab) { + const size_t M = pattern->size; + if (M == 0) { + return (fzf_result_t){0, 0, 0}; + } + size_t trimmed_len = 0; + /* TODO(conni2461): i feel this is wrong */ + if (!isspace((uint8_t)pattern->data[0])) { + trimmed_len = leading_whitespaces(text); + } + if (text->size - trimmed_len < M) { + return (fzf_result_t){-1, -1, 0}; + } + for (size_t i = 0; i < M; i++) { + char c = text->data[trimmed_len + i]; + if (!case_sensitive) { + c = (char)tolower((uint8_t)c); + } + if (normalize) { + c = normalize_rune(c); + } + if (c != pattern->data[i]) { + return (fzf_result_t){-1, -1, 0}; + } + } + size_t start = trimmed_len; + size_t end = trimmed_len + M; + int32_t score = calculate_score(case_sensitive, normalize, text, pattern, + start, end, NULL); + insert_range(pos, start, end); + return (fzf_result_t){(int32_t)start, (int32_t)end, score}; +} + +fzf_result_t fzf_suffix_match(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab) { + size_t trimmed_len = text->size; + const size_t M = pattern->size; + /* TODO(conni2461): i think this is wrong */ + if (M == 0 || !isspace((uint8_t)pattern->data[M - 1])) { + trimmed_len -= trailing_whitespaces(text); + } + if (M == 0) { + return (fzf_result_t){(int32_t)trimmed_len, (int32_t)trimmed_len, 0}; + } + size_t diff = trimmed_len - M; + if (diff < 0) { + return (fzf_result_t){-1, -1, 0}; + } + + for (size_t idx = 0; idx < M; idx++) { + char c = text->data[idx + diff]; + if (!case_sensitive) { + c = (char)tolower((uint8_t)c); + } + if (normalize) { + c = normalize_rune(c); + } + if (c != pattern->data[idx]) { + return (fzf_result_t){-1, -1, 0}; + } + } + size_t start = trimmed_len - M; + size_t end = trimmed_len; + int32_t score = calculate_score(case_sensitive, normalize, text, pattern, + start, end, NULL); + insert_range(pos, start, end); + return (fzf_result_t){(int32_t)start, (int32_t)end, score}; +} + +fzf_result_t fzf_equal_match(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab) { + const size_t M = pattern->size; + if (M == 0) { + return (fzf_result_t){-1, -1, 0}; + } + + size_t trimmed_len = leading_whitespaces(text); + size_t trimmed_end_len = trailing_whitespaces(text); + + if ((text->size - trimmed_len - trimmed_end_len) != M) { + return (fzf_result_t){-1, -1, 0}; + } + + bool match = true; + if (normalize) { + // TODO(conni2461): to rune + for (size_t idx = 0; idx < M; idx++) { + char pchar = pattern->data[idx]; + char c = text->data[trimmed_len + idx]; + if (!case_sensitive) { + c = (char)tolower((uint8_t)c); + } + if (normalize_rune(c) != normalize_rune(pchar)) { + match = false; + break; + } + } + } else { + // TODO(conni2461): to rune + for (size_t idx = 0; idx < M; idx++) { + char pchar = pattern->data[idx]; + char c = text->data[trimmed_len + idx]; + if (!case_sensitive) { + c = (char)tolower((uint8_t)c); + } + if (c != pchar) { + match = false; + break; + } + } + } + if (match) { + insert_range(pos, trimmed_len, trimmed_len + M); + return (fzf_result_t){(int32_t)trimmed_len, + ((int32_t)trimmed_len + (int32_t)M), + (ScoreMatch + BonusBoundary) * (int32_t)M + + (BonusFirstCharMultiplier - 1) * BonusBoundary}; + } + return (fzf_result_t){-1, -1, 0}; +} + +static void append_set(fzf_term_set_t *set, fzf_term_t value) { + if (set->cap == 0) { + set->cap = 1; + set->ptr = (fzf_term_t *)malloc(sizeof(fzf_term_t)); + } else if (set->size + 1 > set->cap) { + set->cap *= 2; + set->ptr = realloc(set->ptr, sizeof(fzf_term_t) * set->cap); + } + set->ptr[set->size] = value; + set->size++; +} + +static void append_pattern(fzf_pattern_t *pattern, fzf_term_set_t *value) { + if (pattern->cap == 0) { + pattern->cap = 1; + pattern->ptr = (fzf_term_set_t **)malloc(sizeof(fzf_term_set_t *)); + } else if (pattern->size + 1 > pattern->cap) { + pattern->cap *= 2; + pattern->ptr = + realloc(pattern->ptr, sizeof(fzf_term_set_t *) * pattern->cap); + } + pattern->ptr[pattern->size] = value; + pattern->size++; +} + +#define CALL_ALG(term, normalize, input, pos, slab) \ + term->fn((term)->case_sensitive, normalize, &(input), \ + (fzf_string_t *)(term)->text, pos, slab) + +// TODO(conni2461): REFACTOR +/* assumption (maybe i change that later) + * - always v2 alg + * - bool extended always true (thats the whole point of this isn't it) + */ +fzf_pattern_t *fzf_parse_pattern(fzf_case_types case_mode, bool normalize, + char *pattern, bool fuzzy) { + fzf_pattern_t *pat_obj = (fzf_pattern_t *)malloc(sizeof(fzf_pattern_t)); + memset(pat_obj, 0, sizeof(*pat_obj)); + + size_t pat_len = strlen(pattern); + if (pat_len == 0) { + return pat_obj; + } + pattern = trim_whitespace_left(pattern, &pat_len); + while (has_suffix(pattern, pat_len, " ", 1) && + !has_suffix(pattern, pat_len, "\\ ", 2)) { + pattern[pat_len - 1] = 0; + pat_len--; + } + + char *pattern_copy = str_replace(pattern, "\\ ", "\t"); + const char *delim = " "; + char *ptr = strtok(pattern_copy, delim); + + fzf_term_set_t *set = (fzf_term_set_t *)malloc(sizeof(fzf_term_set_t)); + memset(set, 0, sizeof(*set)); + + bool switch_set = false; + bool after_bar = false; + while (ptr != NULL) { + fzf_algo_t fn = fzf_fuzzy_match_v2; + bool inv = false; + + size_t len = strlen(ptr); + str_replace_char(ptr, '\t', ' '); + char *text = strdup(ptr); + + char *og_str = text; + char *lower_text = str_tolower(text, len); + bool case_sensitive = + case_mode == CaseRespect || + (case_mode == CaseSmart && strcmp(text, lower_text) != 0); + if (!case_sensitive) { + SFREE(text); + text = lower_text; + og_str = lower_text; + } else { + SFREE(lower_text); + } + if (!fuzzy) { + fn = fzf_exact_match_naive; + } + if (set->size > 0 && !after_bar && strcmp(text, "|") == 0) { + switch_set = false; + after_bar = true; + ptr = strtok(NULL, delim); + SFREE(og_str); + continue; + } + after_bar = false; + if (has_prefix(text, "!", 1)) { + inv = true; + fn = fzf_exact_match_naive; + text++; + len--; + } + + if (strcmp(text, "$") != 0 && has_suffix(text, len, "$", 1)) { + fn = fzf_suffix_match; + text[len - 1] = 0; + len--; + } + + if (has_prefix(text, "'", 1)) { + if (fuzzy && !inv) { + fn = fzf_exact_match_naive; + text++; + len--; + } else { + fn = fzf_fuzzy_match_v2; + text++; + len--; + } + } else if (has_prefix(text, "^", 1)) { + if (fn == fzf_suffix_match) { + fn = fzf_equal_match; + } else { + fn = fzf_prefix_match; + } + text++; + len--; + } + + if (len > 0) { + if (switch_set) { + append_pattern(pat_obj, set); + set = (fzf_term_set_t *)malloc(sizeof(fzf_term_set_t)); + set->cap = 0; + set->size = 0; + } + fzf_string_t *text_ptr = (fzf_string_t *)malloc(sizeof(fzf_string_t)); + text_ptr->data = text; + text_ptr->size = len; + append_set(set, (fzf_term_t){.fn = fn, + .inv = inv, + .ptr = og_str, + .text = text_ptr, + .case_sensitive = case_sensitive}); + switch_set = true; + } else { + SFREE(og_str); + } + + ptr = strtok(NULL, delim); + } + if (set->size > 0) { + append_pattern(pat_obj, set); + } else { + SFREE(set->ptr); + SFREE(set); + } + bool only = true; + for (size_t i = 0; i < pat_obj->size; i++) { + fzf_term_set_t *term_set = pat_obj->ptr[i]; + if (term_set->size > 1) { + only = false; + break; + } + if (term_set->ptr[0].inv == false) { + only = false; + break; + } + } + pat_obj->only_inv = only; + SFREE(pattern_copy); + return pat_obj; +} + +void fzf_free_pattern(fzf_pattern_t *pattern) { + if (pattern->ptr) { + for (size_t i = 0; i < pattern->size; i++) { + fzf_term_set_t *term_set = pattern->ptr[i]; + for (size_t j = 0; j < term_set->size; j++) { + fzf_term_t *term = &term_set->ptr[j]; + free(term->ptr); + free(term->text); + } + free(term_set->ptr); + free(term_set); + } + free(pattern->ptr); + } + SFREE(pattern); +} + +int32_t fzf_get_score(const char *text, fzf_pattern_t *pattern, + fzf_slab_t *slab) { + // If the pattern is an empty string then pattern->ptr will be NULL and we + // basically don't want to filter. Return 1 for telescope + if (pattern->ptr == NULL) { + return 1; + } + + fzf_string_t input = {.data = text, .size = strlen(text)}; + if (pattern->only_inv) { + int final = 0; + for (size_t i = 0; i < pattern->size; i++) { + fzf_term_set_t *term_set = pattern->ptr[i]; + fzf_term_t *term = &term_set->ptr[0]; + + final += CALL_ALG(term, false, input, NULL, slab).score; + } + return (final > 0) ? 0 : 1; + } + + int32_t total_score = 0; + for (size_t i = 0; i < pattern->size; i++) { + fzf_term_set_t *term_set = pattern->ptr[i]; + int32_t current_score = 0; + bool matched = false; + for (size_t j = 0; j < term_set->size; j++) { + fzf_term_t *term = &term_set->ptr[j]; + fzf_result_t res = CALL_ALG(term, false, input, NULL, slab); + if (res.start >= 0) { + if (term->inv) { + continue; + } + current_score = res.score; + matched = true; + break; + } + + if (term->inv) { + current_score = 0; + matched = true; + } + } + if (matched) { + total_score += current_score; + } else { + total_score = 0; + break; + } + } + + return total_score; +} + +fzf_position_t *fzf_get_positions(const char *text, fzf_pattern_t *pattern, + fzf_slab_t *slab) { + // If the pattern is an empty string then pattern->ptr will be NULL and we + // basically don't want to filter. Return 1 for telescope + if (pattern->ptr == NULL) { + return NULL; + } + + fzf_string_t input = {.data = text, .size = strlen(text)}; + fzf_position_t *all_pos = fzf_pos_array(0); + for (size_t i = 0; i < pattern->size; i++) { + fzf_term_set_t *term_set = pattern->ptr[i]; + bool matched = false; + for (size_t j = 0; j < term_set->size; j++) { + fzf_term_t *term = &term_set->ptr[j]; + if (term->inv) { + // If we have an inverse term we need to check if we have a match, but + // we are not interested in the positions (for highlights) so to speed + // this up we can pass in NULL here and don't calculate the positions + fzf_result_t res = CALL_ALG(term, false, input, NULL, slab); + if (res.start < 0) { + matched = true; + } + continue; + } + fzf_result_t res = CALL_ALG(term, false, input, all_pos, slab); + if (res.start >= 0) { + matched = true; + break; + } + } + if (!matched) { + fzf_free_positions(all_pos); + return NULL; + } + } + return all_pos; +} + +void fzf_free_positions(fzf_position_t *pos) { + if (pos) { + SFREE(pos->data); + free(pos); + } +} + +fzf_slab_t *fzf_make_slab(fzf_slab_config_t config) { + fzf_slab_t *slab = (fzf_slab_t *)malloc(sizeof(fzf_slab_t)); + memset(slab, 0, sizeof(*slab)); + + slab->I16.data = (int16_t *)malloc(config.size_16 * sizeof(int16_t)); + memset(slab->I16.data, 0, config.size_16 * sizeof(*slab->I16.data)); + slab->I16.cap = config.size_16; + slab->I16.size = 0; + slab->I16.allocated = true; + + slab->I32.data = (int32_t *)malloc(config.size_32 * sizeof(int32_t)); + memset(slab->I32.data, 0, config.size_32 * sizeof(*slab->I32.data)); + slab->I32.cap = config.size_32; + slab->I32.size = 0; + slab->I32.allocated = true; + + return slab; +} + +fzf_slab_t *fzf_make_default_slab(void) { + return fzf_make_slab((fzf_slab_config_t){(size_t)100 * 1024, 2048}); +} + +void fzf_free_slab(fzf_slab_t *slab) { + if (slab) { + free(slab->I16.data); + free(slab->I32.data); + free(slab); + } +} diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/src/fzf.h b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/src/fzf.h new file mode 100644 index 00000000..7bc16473 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/src/fzf.h @@ -0,0 +1,111 @@ +#ifndef FZF_H_ +#define FZF_H_ + +#include +#include +#include + +typedef struct { + int16_t *data; + size_t size; + size_t cap; + bool allocated; +} fzf_i16_t; + +typedef struct { + int32_t *data; + size_t size; + size_t cap; + bool allocated; +} fzf_i32_t; + +typedef struct { + uint32_t *data; + size_t size; + size_t cap; +} fzf_position_t; + +typedef struct { + int32_t start; + int32_t end; + int32_t score; +} fzf_result_t; + +typedef struct { + fzf_i16_t I16; + fzf_i32_t I32; +} fzf_slab_t; + +typedef struct { + size_t size_16; + size_t size_32; +} fzf_slab_config_t; + +typedef struct { + const char *data; + size_t size; +} fzf_string_t; + +typedef fzf_result_t (*fzf_algo_t)(bool, bool, fzf_string_t *, fzf_string_t *, + fzf_position_t *, fzf_slab_t *); + +typedef enum { CaseSmart = 0, CaseIgnore, CaseRespect } fzf_case_types; + +typedef struct { + fzf_algo_t fn; + bool inv; + char *ptr; + void *text; + bool case_sensitive; +} fzf_term_t; + +typedef struct { + fzf_term_t *ptr; + size_t size; + size_t cap; +} fzf_term_set_t; + +typedef struct { + fzf_term_set_t **ptr; + size_t size; + size_t cap; + bool only_inv; +} fzf_pattern_t; + +fzf_result_t fzf_fuzzy_match_v1(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab); +fzf_result_t fzf_fuzzy_match_v2(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab); +fzf_result_t fzf_exact_match_naive(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab); +fzf_result_t fzf_prefix_match(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab); +fzf_result_t fzf_suffix_match(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab); +fzf_result_t fzf_equal_match(bool case_sensitive, bool normalize, + fzf_string_t *text, fzf_string_t *pattern, + fzf_position_t *pos, fzf_slab_t *slab); + +/* interface */ +fzf_pattern_t *fzf_parse_pattern(fzf_case_types case_mode, bool normalize, + char *pattern, bool fuzzy); +void fzf_free_pattern(fzf_pattern_t *pattern); + +int32_t fzf_get_score(const char *text, fzf_pattern_t *pattern, + fzf_slab_t *slab); + +fzf_position_t *fzf_pos_array(size_t len); +fzf_position_t *fzf_get_positions(const char *text, fzf_pattern_t *pattern, + fzf_slab_t *slab); +void fzf_free_positions(fzf_position_t *pos); + +fzf_slab_t *fzf_make_slab(fzf_slab_config_t config); +fzf_slab_t *fzf_make_default_slab(void); +void fzf_free_slab(fzf_slab_t *slab); + +#endif // FZF_H_ diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/test/fzf_lib_spec.lua b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/test/fzf_lib_spec.lua new file mode 100644 index 00000000..f5ccdae3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/test/fzf_lib_spec.lua @@ -0,0 +1,111 @@ +local fzf = require "fzf_lib" +local eq = assert.are.same +local is_nil = assert.is_nil + +describe("fzf", function() + local slab = fzf.allocate_slab() + it("can get the score for simple pattern", function() + local p = fzf.parse_pattern("fzf", 0) + eq(80, fzf.get_score("src/fzf", p, slab)) + eq(0, fzf.get_score("asdf", p, slab)) + eq(54, fzf.get_score("fasdzasdf", p, slab)) + fzf.free_pattern(p) + end) + + it("can get the score for or pattern", function() + local p = fzf.parse_pattern("lua | src | 'doc | ^asdfasdf | file$", 0) + eq(80, fzf.get_score("src/fzf.c", p, slab)) + eq(0, fzf.get_score("build/libfzf", p, slab)) + eq(80, fzf.get_score("lua/fzf_lib.lua", p, slab)) + eq(80, fzf.get_score("doc/fzf.txt", p, slab)) + eq(0, fzf.get_score("daonc/fzf.txt", p, slab)) + eq(200, fzf.get_score("asdfasdf", p, slab)) + eq(0, fzf.get_score("noasdfasdf", p, slab)) + eq(104, fzf.get_score("not_file", p, slab)) + eq(0, fzf.get_score("not_file.txt", p, slab)) + fzf.free_pattern(p) + end) + + it("can get the score for and pattern", function() + local p = fzf.parse_pattern("fzf !lib", 0) + eq(80, fzf.get_score("src/fzf.c", p, slab)) + eq(0, fzf.get_score("lua/fzf_lib.lua", p, slab)) + eq(0, fzf.get_score("build/libfzf", p, slab)) + fzf.free_pattern(p) + + local p = fzf.parse_pattern("fzf src c", 0) + eq(192, fzf.get_score("src/fzf.c", p, slab)) + eq(0, fzf.get_score("lua/fzf_lib.lua", p, slab)) + eq(0, fzf.get_score("build/libfzf", p, slab)) + fzf.free_pattern(p) + end) + + it("can get the score for patterns with escaped space", function() + local p = fzf.parse_pattern("\\ ", 0) + eq(32, fzf.get_score("src file", p, slab)) + eq(0, fzf.get_score("src_file", p, slab)) + eq(32, fzf.get_score("another another file", p, slab)) + fzf.free_pattern(p) + end) + + it("can get the score for issue 11", function() + local p = fzf.parse_pattern("feature/1337-some-times-i-have-a-lot-of-hyphens", 0) + eq(1136, fzf.get_score("feature/1337-some-times-i-have-a-lot-of-hyphens", p, slab)) + fzf.free_pattern(p) + end) + + it("can get the pos for simple pattern", function() + local p = fzf.parse_pattern("fzf", 0) + eq({ 7, 6, 5 }, fzf.get_pos("src/fzf", p, slab)) + is_nil(fzf.get_pos("asdf", p, slab)) + eq({ 9, 5, 1 }, fzf.get_pos("fasdzasdf", p, slab)) + fzf.free_pattern(p) + end) + + it("can get the pos for or pattern", function() + local p = fzf.parse_pattern("lua | src | 'doc | ^asdfasdf | file$", 0) + eq({ 3, 2, 1 }, fzf.get_pos("src/fzf.c", p, slab)) + is_nil(fzf.get_pos("build/libfzf", p, slab)) + eq({ 3, 2, 1 }, fzf.get_pos("lua/fzf_lib.lua", p, slab)) + eq({ 1, 2, 3 }, fzf.get_pos("doc/fzf.txt", p, slab)) + is_nil(fzf.get_pos("daonc/fzf.txt", p, slab)) + eq({ 1, 2, 3, 4, 5, 6, 7, 8 }, fzf.get_pos("asdfasdf", p, slab)) + is_nil(fzf.get_pos("noasdfasdf", p, slab)) + eq({ 5, 6, 7, 8 }, fzf.get_pos("not_file", p, slab)) + is_nil(fzf.get_pos("not_file.txt", p, slab)) + fzf.free_pattern(p) + end) + + it("can get the pos for and pattern", function() + local p = fzf.parse_pattern("fzf !lib", 0) + eq({ 7, 6, 5 }, fzf.get_pos("src/fzf.c", p, slab)) + is_nil(fzf.get_pos("lua/fzf_lib.lua", p, slab)) + is_nil(fzf.get_pos("build/libfzf", p, slab)) + fzf.free_pattern(p) + + p = fzf.parse_pattern("fzf src c", 0) + eq({ 7, 6, 5, 3, 2, 1, 9 }, fzf.get_pos("src/fzf.c", p, slab)) + is_nil(fzf.get_pos("lua/fzf_lib.lua", p, slab)) + is_nil(fzf.get_pos("build/libfzf", p, slab)) + fzf.free_pattern(p) + end) + + it("can get the pos for patterns with escaped space", function() + local p = fzf.parse_pattern("\\ ", 0) + eq({ 4 }, fzf.get_pos("src file", p, slab)) + is_nil(fzf.get_pos("src_file", p, slab)) + eq({ 8 }, fzf.get_pos("another another file", p, slab)) + fzf.free_pattern(p) + end) + + it("can get the pos for issue 11", function() + local p = fzf.parse_pattern("feature/1337-some-times-i-have-a-lot-of-hyphens", 0) + local expected = {} + for i = 47, 1, -1 do + table.insert(expected, i) + end + eq(expected, fzf.get_pos("feature/1337-some-times-i-have-a-lot-of-hyphens", p, slab)) + fzf.free_pattern(p) + end) + fzf.free_slab(slab) +end) diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/test/minrc.vim b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/test/minrc.vim new file mode 100644 index 00000000..da29e2f9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/test/minrc.vim @@ -0,0 +1,4 @@ +set rtp+=. +set rtp+=../plenary.nvim/ + +runtime! plugin/plenary.vim diff --git a/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/test/test.c b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/test/test.c new file mode 100644 index 00000000..c22961a6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-fzf-native.nvim/test/test.c @@ -0,0 +1,771 @@ +#include "fzf.h" + +#include +#include +#include + +typedef enum { + ScoreMatch = 16, + ScoreGapStart = -3, + ScoreGapExtension = -1, + BonusBoundary = ScoreMatch / 2, + BonusNonWord = ScoreMatch / 2, + BonusCamel123 = BonusBoundary + ScoreGapExtension, + BonusConsecutive = -(ScoreGapStart + ScoreGapExtension), + BonusFirstCharMultiplier = 2, +} score_t; + +#define call_alg(alg, case, txt, pat, assert_block) \ + { \ + fzf_position_t *pos = fzf_pos_array(0); \ + fzf_result_t res = alg(case, false, txt, pat, pos, NULL); \ + assert_block; \ + fzf_free_positions(pos); \ + } \ + { \ + fzf_position_t *pos = fzf_pos_array(0); \ + fzf_slab_t *slab = fzf_make_default_slab(); \ + fzf_result_t res = alg(case, false, txt, pat, pos, slab); \ + assert_block; \ + fzf_free_positions(pos); \ + fzf_free_slab(slab); \ + } + +static int8_t max_i8(int8_t a, int8_t b) { + return a > b ? a : b; +} + +#define MATCH_WRAPPER(nn, og) \ + fzf_result_t nn(bool case_sensitive, bool normalize, const char *text, \ + const char *pattern, fzf_position_t *pos, \ + fzf_slab_t *slab) { \ + fzf_string_t input = {.data = text, .size = strlen(text)}; \ + fzf_string_t pattern_wrap = {.data = pattern, .size = strlen(pattern)}; \ + return og(case_sensitive, normalize, &input, &pattern_wrap, pos, slab); \ + } + +MATCH_WRAPPER(fuzzy_match_v2, fzf_fuzzy_match_v2); +MATCH_WRAPPER(fuzzy_match_v1, fzf_fuzzy_match_v1); +MATCH_WRAPPER(exact_match_naive, fzf_exact_match_naive); +MATCH_WRAPPER(prefix_match, fzf_prefix_match); +MATCH_WRAPPER(suffix_match, fzf_suffix_match); +MATCH_WRAPPER(equal_match, fzf_equal_match); + +// TODO(conni2461): Implement normalize and test it here +TEST(FuzzyMatchV2, case1) { + call_alg(fuzzy_match_v2, true, "So Danco Samba", "So", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(2, res.end); + ASSERT_EQ(56, res.score); + + ASSERT_EQ(2, pos->size); + ASSERT_EQ(1, pos->data[0]); + ASSERT_EQ(0, pos->data[1]); + }); +} + +TEST(FuzzyMatchV2, case2) { + call_alg(fuzzy_match_v2, false, "So Danco Samba", "sodc", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(7, res.end); + ASSERT_EQ(89, res.score); + + ASSERT_EQ(4, pos->size); + ASSERT_EQ(6, pos->data[0]); + ASSERT_EQ(3, pos->data[1]); + ASSERT_EQ(1, pos->data[2]); + ASSERT_EQ(0, pos->data[3]); + }); +} + +TEST(FuzzyMatchV2, case3) { + call_alg(fuzzy_match_v2, false, "Danco", "danco", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(5, res.end); + ASSERT_EQ(128, res.score); + + ASSERT_EQ(5, pos->size); + ASSERT_EQ(4, pos->data[0]); + ASSERT_EQ(3, pos->data[1]); + ASSERT_EQ(2, pos->data[2]); + ASSERT_EQ(1, pos->data[3]); + ASSERT_EQ(0, pos->data[4]); + }); +} + +TEST(FuzzyMatchV2, case4) { + call_alg(fuzzy_match_v2, false, "fooBarbaz1", "obz", { + ASSERT_EQ(2, res.start); + ASSERT_EQ(9, res.end); + int expected_score = + ScoreMatch * 3 + BonusCamel123 + ScoreGapStart + ScoreGapExtension * 3; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case5) { + call_alg(fuzzy_match_v2, false, "foo bar baz", "fbb", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(9, res.end); + int expected_score = + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + + BonusBoundary * 2 + 2 * ScoreGapStart + 4 * ScoreGapExtension; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case6) { + call_alg(fuzzy_match_v2, false, "/AutomatorDocument.icns", "rdoc", { + ASSERT_EQ(9, res.start); + ASSERT_EQ(13, res.end); + int expected_score = ScoreMatch * 4 + BonusCamel123 + BonusConsecutive * 2; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case7) { + call_alg(fuzzy_match_v2, false, "/man1/zshcompctl.1", "zshc", { + ASSERT_EQ(6, res.start); + ASSERT_EQ(10, res.end); + int expected_score = ScoreMatch * 4 + + BonusBoundary * BonusFirstCharMultiplier + + BonusBoundary * 3; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case8) { + call_alg(fuzzy_match_v2, false, "/.oh-my-zsh/cache", "zshc", { + ASSERT_EQ(8, res.start); + ASSERT_EQ(13, res.end); + int expected_score = ScoreMatch * 4 + + BonusBoundary * BonusFirstCharMultiplier + + BonusBoundary * 3 + ScoreGapStart; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case9) { + call_alg(fuzzy_match_v2, false, "ab0123 456", "12356", { + ASSERT_EQ(3, res.start); + ASSERT_EQ(10, res.end); + int expected_score = ScoreMatch * 5 + BonusConsecutive * 3 + ScoreGapStart + + ScoreGapExtension; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case10) { + call_alg(fuzzy_match_v2, false, "abc123 456", "12356", { + ASSERT_EQ(3, res.start); + ASSERT_EQ(10, res.end); + int expected_score = ScoreMatch * 5 + + BonusCamel123 * BonusFirstCharMultiplier + + BonusCamel123 * 2 + BonusConsecutive + ScoreGapStart + + ScoreGapExtension; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case11) { + call_alg(fuzzy_match_v2, false, "foo/bar/baz", "fbb", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(9, res.end); + int expected_score = + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + + BonusBoundary * 2 + 2 * ScoreGapStart + 4 * ScoreGapExtension; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case12) { + call_alg(fuzzy_match_v2, false, "fooBarBaz", "fbb", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(7, res.end); + int expected_score = + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + + BonusCamel123 * 2 + 2 * ScoreGapStart + 2 * ScoreGapExtension; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case13) { + call_alg(fuzzy_match_v2, false, "foo barbaz", "fbb", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(8, res.end); + int expected_score = + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + + BonusBoundary + ScoreGapStart * 2 + ScoreGapExtension * 3; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case14) { + call_alg(fuzzy_match_v2, false, "fooBar Baz", "foob", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(4, res.end); + int expected_score = ScoreMatch * 4 + + BonusBoundary * BonusFirstCharMultiplier + + BonusBoundary * 3; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case15) { + call_alg(fuzzy_match_v2, false, "xFoo-Bar Baz", "foo-b", { + ASSERT_EQ(1, res.start); + ASSERT_EQ(6, res.end); + int expected_score = ScoreMatch * 5 + + BonusCamel123 * BonusFirstCharMultiplier + + BonusCamel123 * 2 + BonusNonWord + BonusBoundary; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case16) { + call_alg(fuzzy_match_v2, true, "fooBarbaz", "oBz", { + ASSERT_EQ(2, res.start); + ASSERT_EQ(9, res.end); + int expected_score = + ScoreMatch * 3 + BonusCamel123 + ScoreGapStart + ScoreGapExtension * 3; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case17) { + call_alg(fuzzy_match_v2, true, "Foo/Bar/Baz", "FBB", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(9, res.end); + int expected_score = ScoreMatch * 3 + + BonusBoundary * (BonusFirstCharMultiplier + 2) + + ScoreGapStart * 2 + ScoreGapExtension * 4; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case18) { + call_alg(fuzzy_match_v2, true, "FooBarBaz", "FBB", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(7, res.end); + int expected_score = + ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + + BonusCamel123 * 2 + ScoreGapStart * 2 + ScoreGapExtension * 2; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case19) { + call_alg(fuzzy_match_v2, true, "FooBar Baz", "FooB", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(4, res.end); + int expected_score = + ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier + + BonusBoundary * 2 + max_i8(BonusCamel123, BonusBoundary); + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case20) { + call_alg(fuzzy_match_v2, true, "foo-bar", "o-ba", { + ASSERT_EQ(2, res.start); + ASSERT_EQ(6, res.end); + int expected_score = ScoreMatch * 4 + BonusBoundary * 3; + ASSERT_EQ(expected_score, res.score); + }); +} + +TEST(FuzzyMatchV2, case21) { + call_alg(fuzzy_match_v2, true, "fooBarbaz", "oBZ", { + ASSERT_EQ(-1, res.start); + ASSERT_EQ(-1, res.end); + ASSERT_EQ(0, res.score); + }); +} + +TEST(FuzzyMatchV2, case22) { + call_alg(fuzzy_match_v2, true, "Foo Bar Baz", "fbb", { + ASSERT_EQ(-1, res.start); + ASSERT_EQ(-1, res.end); + ASSERT_EQ(0, res.score); + }); +} + +TEST(FuzzyMatchV2, case23) { + call_alg(fuzzy_match_v2, true, "fooBarbaz", "fooBarbazz", { + ASSERT_EQ(-1, res.start); + ASSERT_EQ(-1, res.end); + ASSERT_EQ(0, res.score); + }); +} + +TEST(FuzzyMatchV1, case1) { + call_alg(fuzzy_match_v1, true, "So Danco Samba", "So", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(2, res.end); + ASSERT_EQ(56, res.score); + + ASSERT_EQ(2, pos->size); + ASSERT_EQ(0, pos->data[0]); + ASSERT_EQ(1, pos->data[1]); + }); +} + +TEST(FuzzyMatchV1, case2) { + call_alg(fuzzy_match_v1, false, "So Danco Samba", "sodc", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(7, res.end); + ASSERT_EQ(89, res.score); + + ASSERT_EQ(4, pos->size); + ASSERT_EQ(0, pos->data[0]); + ASSERT_EQ(1, pos->data[1]); + ASSERT_EQ(3, pos->data[2]); + ASSERT_EQ(6, pos->data[3]); + }); +} + +TEST(FuzzyMatchV1, case3) { + call_alg(fuzzy_match_v1, false, "Danco", "danco", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(5, res.end); + ASSERT_EQ(128, res.score); + + ASSERT_EQ(5, pos->size); + ASSERT_EQ(0, pos->data[0]); + ASSERT_EQ(1, pos->data[1]); + ASSERT_EQ(2, pos->data[2]); + ASSERT_EQ(3, pos->data[3]); + ASSERT_EQ(4, pos->data[4]); + }); +} + +TEST(ExactMatch, case1) { + call_alg(exact_match_naive, true, "So Danco Samba", "So", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(2, res.end); + ASSERT_EQ(56, res.score); + }); +} + +TEST(ExactMatch, case2) { + call_alg(exact_match_naive, false, "So Danco Samba", "sodc", { + ASSERT_EQ(-1, res.start); + ASSERT_EQ(-1, res.end); + ASSERT_EQ(0, res.score); + }); +} + +TEST(ExactMatch, case3) { + call_alg(exact_match_naive, false, "Danco", "danco", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(5, res.end); + ASSERT_EQ(128, res.score); + }); +} + +TEST(PrefixMatch, case1) { + call_alg(prefix_match, true, "So Danco Samba", "So", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(2, res.end); + ASSERT_EQ(56, res.score); + }); +} + +TEST(PrefixMatch, case2) { + call_alg(prefix_match, false, "So Danco Samba", "sodc", { + ASSERT_EQ(-1, res.start); + ASSERT_EQ(-1, res.end); + ASSERT_EQ(0, res.score); + }); +} + +TEST(PrefixMatch, case3) { + call_alg(prefix_match, false, "Danco", "danco", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(5, res.end); + ASSERT_EQ(128, res.score); + }); +} + +TEST(SuffixMatch, case1) { + call_alg(suffix_match, true, "So Danco Samba", "So", { + ASSERT_EQ(-1, res.start); + ASSERT_EQ(-1, res.end); + ASSERT_EQ(0, res.score); + }); +} + +TEST(SuffixMatch, case2) { + call_alg(suffix_match, false, "So Danco Samba", "sodc", { + ASSERT_EQ(-1, res.start); + ASSERT_EQ(-1, res.end); + ASSERT_EQ(0, res.score); + }); +} + +TEST(SuffixMatch, case3) { + call_alg(suffix_match, false, "Danco", "danco", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(5, res.end); + ASSERT_EQ(128, res.score); + }); +} + +TEST(EqualMatch, case1) { + call_alg(equal_match, true, "So Danco Samba", "So", { + ASSERT_EQ(-1, res.start); + ASSERT_EQ(-1, res.end); + ASSERT_EQ(0, res.score); + }); +} + +TEST(EqualMatch, case2) { + call_alg(equal_match, false, "So Danco Samba", "sodc", { + ASSERT_EQ(-1, res.start); + ASSERT_EQ(-1, res.end); + ASSERT_EQ(0, res.score); + }); +} + +TEST(EqualMatch, case3) { + call_alg(equal_match, false, "Danco", "danco", { + ASSERT_EQ(0, res.start); + ASSERT_EQ(5, res.end); + ASSERT_EQ(128, res.score); + }); +} + +TEST(PatternParsing, empty) { + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "", true); + ASSERT_EQ(0, pat->size); + ASSERT_EQ(0, pat->cap); + ASSERT_FALSE(pat->only_inv); + + fzf_free_pattern(pat); +} + +TEST(PatternParsing, simple) { + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "lua", true); + ASSERT_EQ(1, pat->size); + ASSERT_EQ(1, pat->cap); + ASSERT_FALSE(pat->only_inv); + + ASSERT_EQ(1, pat->ptr[0]->size); + ASSERT_EQ(1, pat->ptr[0]->cap); + + ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn); + ASSERT_EQ("lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); + fzf_free_pattern(pat); +} + +TEST(PatternParsing, withEscapedSpace) { + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "file\\ ", true); + ASSERT_EQ(1, pat->size); + ASSERT_EQ(1, pat->cap); + ASSERT_FALSE(pat->only_inv); + + ASSERT_EQ(1, pat->ptr[0]->size); + ASSERT_EQ(1, pat->ptr[0]->cap); + + ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn); + ASSERT_EQ("file ", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); + fzf_free_pattern(pat); +} + +TEST(PatternParsing, withComplexEscapedSpace) { + fzf_pattern_t *pat = + fzf_parse_pattern(CaseSmart, false, "file\\ with\\ space", true); + ASSERT_EQ(1, pat->size); + ASSERT_EQ(1, pat->cap); + ASSERT_FALSE(pat->only_inv); + + ASSERT_EQ(1, pat->ptr[0]->size); + ASSERT_EQ(1, pat->ptr[0]->cap); + + ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn); + ASSERT_EQ("file with space", + ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); + fzf_free_pattern(pat); +} + +TEST(PatternParsing, withEscapedSpaceAndNormalSpace) { + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "file\\ new", true); + ASSERT_EQ(2, pat->size); + ASSERT_EQ(2, pat->cap); + ASSERT_FALSE(pat->only_inv); + + ASSERT_EQ(1, pat->ptr[0]->size); + ASSERT_EQ(1, pat->ptr[0]->cap); + ASSERT_EQ(1, pat->ptr[1]->size); + ASSERT_EQ(1, pat->ptr[1]->cap); + + ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn); + ASSERT_EQ("file ", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); + + ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[1]->ptr[0].fn); + ASSERT_EQ("new", ((fzf_string_t *)(pat->ptr[1]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[1]->ptr[0].case_sensitive); + fzf_free_pattern(pat); +} + +TEST(PatternParsing, invert) { + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "!Lua", true); + ASSERT_EQ(1, pat->size); + ASSERT_EQ(1, pat->cap); + ASSERT_TRUE(pat->only_inv); + + ASSERT_EQ(1, pat->ptr[0]->size); + ASSERT_EQ(1, pat->ptr[0]->cap); + + ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[0]->ptr[0].fn); + ASSERT_EQ("Lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); + ASSERT_TRUE(pat->ptr[0]->ptr[0].case_sensitive); + ASSERT_TRUE(pat->ptr[0]->ptr[0].inv); + fzf_free_pattern(pat); +} + +TEST(PatternParsing, invertMultiple) { + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "!fzf !test", true); + ASSERT_EQ(2, pat->size); + ASSERT_EQ(2, pat->cap); + ASSERT_TRUE(pat->only_inv); + + ASSERT_EQ(1, pat->ptr[0]->size); + ASSERT_EQ(1, pat->ptr[0]->cap); + ASSERT_EQ(1, pat->ptr[1]->size); + ASSERT_EQ(1, pat->ptr[1]->cap); + + ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[0]->ptr[0].fn); + ASSERT_EQ("fzf", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); + ASSERT_TRUE(pat->ptr[0]->ptr[0].inv); + + ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[1]->ptr[0].fn); + ASSERT_EQ("test", ((fzf_string_t *)(pat->ptr[1]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[1]->ptr[0].case_sensitive); + ASSERT_TRUE(pat->ptr[1]->ptr[0].inv); + fzf_free_pattern(pat); +} + +TEST(PatternParsing, smartCase) { + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "Lua", true); + ASSERT_EQ(1, pat->size); + ASSERT_EQ(1, pat->cap); + ASSERT_FALSE(pat->only_inv); + + ASSERT_EQ(1, pat->ptr[0]->size); + ASSERT_EQ(1, pat->ptr[0]->cap); + + ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn); + ASSERT_EQ("Lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); + ASSERT_TRUE(pat->ptr[0]->ptr[0].case_sensitive); + fzf_free_pattern(pat); +} + +TEST(PatternParsing, simpleOr) { + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "'src | ^Lua", true); + ASSERT_EQ(1, pat->size); + ASSERT_EQ(1, pat->cap); + ASSERT_FALSE(pat->only_inv); + + ASSERT_EQ(2, pat->ptr[0]->size); + ASSERT_EQ(2, pat->ptr[0]->cap); + + ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[0]->ptr[0].fn); + ASSERT_EQ("src", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); + + ASSERT_EQ((void *)fzf_prefix_match, pat->ptr[0]->ptr[1].fn); + ASSERT_EQ("Lua", ((fzf_string_t *)(pat->ptr[0]->ptr[1].text))->data); + ASSERT_TRUE(pat->ptr[0]->ptr[1].case_sensitive); + fzf_free_pattern(pat); +} + +TEST(PatternParsing, complexAnd) { + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, + ".lua$ 'previewer !'term !asdf", true); + ASSERT_EQ(4, pat->size); + ASSERT_EQ(4, pat->cap); + ASSERT_FALSE(pat->only_inv); + + ASSERT_EQ(1, pat->ptr[0]->size); + ASSERT_EQ(1, pat->ptr[0]->cap); + ASSERT_EQ(1, pat->ptr[1]->size); + ASSERT_EQ(1, pat->ptr[1]->cap); + ASSERT_EQ(1, pat->ptr[2]->size); + ASSERT_EQ(1, pat->ptr[2]->cap); + ASSERT_EQ(1, pat->ptr[3]->size); + ASSERT_EQ(1, pat->ptr[3]->cap); + + ASSERT_EQ((void *)fzf_suffix_match, pat->ptr[0]->ptr[0].fn); + ASSERT_EQ(".lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); + + ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[1]->ptr[0].fn); + ASSERT_EQ("previewer", ((fzf_string_t *)(pat->ptr[1]->ptr[0].text))->data); + ASSERT_EQ(0, pat->ptr[1]->ptr[0].case_sensitive); + + ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[2]->ptr[0].fn); + ASSERT_EQ("term", ((fzf_string_t *)(pat->ptr[2]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[2]->ptr[0].case_sensitive); + ASSERT_TRUE(pat->ptr[2]->ptr[0].inv); + + ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[3]->ptr[0].fn); + ASSERT_EQ("asdf", ((fzf_string_t *)(pat->ptr[3]->ptr[0].text))->data); + ASSERT_FALSE(pat->ptr[3]->ptr[0].case_sensitive); + ASSERT_TRUE(pat->ptr[3]->ptr[0].inv); + fzf_free_pattern(pat); +} + +static void score_wrapper(char *pattern, char **input, int *expected) { + fzf_slab_t *slab = fzf_make_default_slab(); + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, pattern, true); + for (size_t i = 0; input[i] != NULL; ++i) { + ASSERT_EQ(expected[i], fzf_get_score(input[i], pat, slab)); + } + fzf_free_pattern(pat); + fzf_free_slab(slab); +} + +TEST(ScoreIntegration, simple) { + char *input[] = {"fzf", "main.c", "src/fzf", "fz/noooo", NULL}; + int expected[] = {0, 1, 0, 1}; + score_wrapper("!fzf", input, expected); +} + +TEST(ScoreIntegration, invertAnd) { + char *input[] = {"src/fzf.c", "README.md", "lua/asdf", "test/test.c", NULL}; + int expected[] = {0, 1, 1, 0}; + score_wrapper("!fzf !test", input, expected); +} + +TEST(ScoreIntegration, withEscapedSpace) { + char *input[] = {"file ", "file lua", "lua", NULL}; + int expected[] = {0, 200, 0}; + score_wrapper("file\\ lua", input, expected); +} + +TEST(ScoreIntegration, onlyEscapedSpace) { + char *input[] = {"file with space", "file lua", "lua", "src", "test", NULL}; + int expected[] = {32, 32, 0, 0, 0}; + score_wrapper("\\ ", input, expected); +} + +TEST(ScoreIntegration, simpleOr) { + char *input[] = {"src/fzf.h", "README.md", "build/fzf", + "lua/fzf_lib.lua", "Lua/fzf_lib.lua", NULL}; + int expected[] = {80, 0, 0, 0, 80}; + score_wrapper("'src | ^Lua", input, expected); +} + +TEST(ScoreIntegration, complexTerm) { + char *input[] = {"lua/random_previewer", "README.md", + "previewers/utils.lua", "previewers/buffer.lua", + "previewers/term.lua", NULL}; + int expected[] = {0, 0, 328, 328, 0}; + score_wrapper(".lua$ 'previewer !'term", input, expected); +} + +static void pos_wrapper(char *pattern, char **input, int **expected) { + fzf_slab_t *slab = fzf_make_default_slab(); + fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, pattern, true); + for (size_t i = 0; input[i] != NULL; ++i) { + fzf_position_t *pos = fzf_get_positions(input[i], pat, slab); + if (!pos) { + ASSERT_EQ((void *)pos, expected[i]); + continue; + } + + // Verify that the size is correct + if (expected[i]) { + ASSERT_EQ(-1, expected[i][pos->size]); + } else { + ASSERT_EQ(0, pos->size); + } + ASSERT_EQ_MEM(expected[i], pos->data, pos->size * sizeof(pos->data[0])); + fzf_free_positions(pos); + } + fzf_free_pattern(pat); + fzf_free_slab(slab); +} + +TEST(PosIntegration, simple) { + char *input[] = {"src/fzf.c", "src/fzf.h", + "lua/fzf_lib.lua", "lua/telescope/_extensions/fzf.lua", + "README.md", NULL}; + int match1[] = {6, 5, 4, -1}; + int match2[] = {6, 5, 4, -1}; + int match3[] = {6, 5, 4, -1}; + int match4[] = {28, 27, 26, -1}; + int *expected[] = {match1, match2, match3, match4, NULL}; + pos_wrapper("fzf", input, expected); +} + +TEST(PosIntegration, invert) { + char *input[] = {"fzf", "main.c", "src/fzf", "fz/noooo", NULL}; + int *expected[] = {NULL, NULL, NULL, NULL, NULL}; + pos_wrapper("!fzf", input, expected); +} + +TEST(PosIntegration, andWithSecondInvert) { + char *input[] = {"src/fzf.c", "lua/fzf_lib.lua", "build/libfzf", NULL}; + int match1[] = {6, 5, 4, -1}; + int *expected[] = {match1, NULL, NULL}; + pos_wrapper("fzf !lib", input, expected); +} + +TEST(PosIntegration, andAllInvert) { + char *input[] = {"src/fzf.c", "README.md", "lua/asdf", "test/test.c", NULL}; + int *expected[] = {NULL, NULL, NULL, NULL}; + pos_wrapper("!fzf !test", input, expected); +} + +TEST(PosIntegration, withEscapedSpace) { + char *input[] = {"file ", "file lua", "lua", NULL}; + int match1[] = {7, 6, 5, 4, 3, 2, 1, 0, -1}; + int *expected[] = {NULL, match1, NULL}; + pos_wrapper("file\\ lua", input, expected); +} + +TEST(PosIntegration, onlyEscapedSpace) { + char *input[] = {"file with space", "lul lua", "lua", "src", "test", NULL}; + int match1[] = {4, -1}; + int match2[] = {3, -1}; + int *expected[] = {match1, match2, NULL, NULL, NULL}; + pos_wrapper("\\ ", input, expected); +} + +TEST(PosIntegration, simpleOr) { + char *input[] = {"src/fzf.h", "README.md", "build/fzf", + "lua/fzf_lib.lua", "Lua/fzf_lib.lua", NULL}; + int match1[] = {0, 1, 2, -1}; + int match2[] = {0, 1, 2, -1}; + int *expected[] = {match1, NULL, NULL, NULL, match2}; + pos_wrapper("'src | ^Lua", input, expected); +} + +TEST(PosIntegration, orMemLeak) { + char *input[] = {"src/fzf.h", NULL}; + int match1[] = {2, 1, 0, -1}; + int *expected[] = {match1}; + pos_wrapper("src | src", input, expected); +} + +TEST(PosIntegration, complexTerm) { + char *input[] = {"lua/random_previewer", "README.md", + "previewers/utils.lua", "previewers/buffer.lua", + "previewers/term.lua", NULL}; + int match1[] = {16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1}; + int match2[] = {17, 18, 19, 20, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1}; + int *expected[] = {NULL, NULL, match1, match2, NULL}; + pos_wrapper(".lua$ 'previewer !'term", input, expected); +} + +int main(int argc, char **argv) { + exam_init(argc, argv); + return exam_run(); +} diff --git a/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/.github/workflows/lint.yml b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/.github/workflows/lint.yml new file mode 100644 index 00000000..cc1bf41c --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/.github/workflows/lint.yml @@ -0,0 +1,31 @@ +name: linting + +on: [push, pull_request] + +jobs: + luacheck: + name: Luacheck + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + + - name: Prepare + run: | + sudo apt-get update + sudo apt-get install -y luarocks + sudo luarocks install luacheck + + - name: Lint + run: sudo luacheck lua + + stylua: + name: stylua + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: JohnnyMorganz/stylua-action@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + # CLI arguments + args: --color always --check lua/ diff --git a/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/.luacheckrc b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/.luacheckrc new file mode 100644 index 00000000..a6be5c07 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/.luacheckrc @@ -0,0 +1,23 @@ +-- Rerun tests only if their modification time changed. +cache = true + +std = luajit +codes = true + +self = false + +-- Glorious list of warnings: https://luacheck.readthedocs.io/en/stable/warnings.html +ignore = { + "212", -- Unused argument, In the case of callback function, _arg_name is easier to understand than _, so this option is set to off. + "122", -- Indirectly setting a readonly global +} + +globals = { + "_", + "__TelescopeUISelectSpecificOpts", +} + +-- Global objects defined by the C code +read_globals = { + "vim", +} diff --git a/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/.stylua.toml b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/.stylua.toml new file mode 100644 index 00000000..df96b7b0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/.stylua.toml @@ -0,0 +1,6 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferDouble" +no_call_parentheses = true diff --git a/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/LICENSE b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/LICENSE new file mode 100644 index 00000000..50e03678 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 nvim-telescope + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/README.md b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/README.md new file mode 100644 index 00000000..d11c3014 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/README.md @@ -0,0 +1,52 @@ +# telescope-ui-select.nvim + +It sets `vim.ui.select` to telescope. That means for example that neovim core +stuff can fill the telescope picker. Example would be +`lua vim.lsp.buf.code_action()`. + +![screenshot](https://user-images.githubusercontent.com/66286082/154263222-ccecd75a-9b4b-410f-9843-1f300638aecf.png) + +requires latest nvim 0.7 or newer nightly version + +## Installation + +```viml +Plug 'nvim-telescope/telescope-ui-select.nvim' +``` + + +```lua +use {'nvim-telescope/telescope-ui-select.nvim' } +``` + +## Telescope Setup and Configuration: + +```lua +-- This is your opts table +require("telescope").setup { + extensions = { + ["ui-select"] = { + require("telescope.themes").get_dropdown { + -- even more opts + } + + -- pseudo code / specification for writing custom displays, like the one + -- for "codeactions" + -- specific_opts = { + -- [kind] = { + -- make_indexed = function(items) -> indexed_items, width, + -- make_displayer = function(widths) -> displayer + -- make_display = function(displayer) -> function(e) + -- make_ordinal = function(e) -> string + -- }, + -- -- for example to disable the custom builtin "codeactions" display + -- do the following + -- codeactions = false, + -- } + } + } +} +-- To get ui-select loaded and working with telescope, you need to call +-- load_extension, somewhere after setup function: +require("telescope").load_extension("ui-select") +``` diff --git a/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/lua/telescope/_extensions/ui-select.lua b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/lua/telescope/_extensions/ui-select.lua new file mode 100644 index 00000000..0ba2e733 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-ui-select.nvim/lua/telescope/_extensions/ui-select.lua @@ -0,0 +1,155 @@ +return require("telescope").register_extension { + setup = function(topts) + local specific_opts = vim.F.if_nil(topts.specific_opts, {}) + topts.specific_opts = nil + + if #topts == 1 and topts[1] ~= nil then + topts = topts[1] + end + + local pickers = require "telescope.pickers" + local finders = require "telescope.finders" + local conf = require("telescope.config").values + local actions = require "telescope.actions" + local action_state = require "telescope.actions.state" + local strings = require "plenary.strings" + local entry_display = require "telescope.pickers.entry_display" + local utils = require "telescope.utils" + + __TelescopeUISelectSpecificOpts = vim.F.if_nil( + __TelescopeUISelectSpecificOpts, + vim.tbl_extend("keep", specific_opts, { + ["codeaction"] = { + make_indexed = function(items) + local indexed_items = {} + local widths = { + idx = 0, + command_title = 0, + client_name = 0, + } + for idx, item in ipairs(items) do + local client_id, title + if vim.version and vim.version.cmp(vim.version(), vim.version.parse "0.10-dev") >= 0 then + client_id = item.ctx.client_id + title = item.action.title + else + client_id = item[1] + title = item[2].title + end + + local client = vim.lsp.get_client_by_id(client_id) + + local entry = { + idx = idx, + ["add"] = { + command_title = title:gsub("\r\n", "\\r\\n"):gsub("\n", "\\n"), + client_name = client and client.name or "", + }, + text = item, + } + table.insert(indexed_items, entry) + widths.idx = math.max(widths.idx, strings.strdisplaywidth(entry.idx)) + widths.command_title = math.max(widths.command_title, strings.strdisplaywidth(entry.add.command_title)) + widths.client_name = math.max(widths.client_name, strings.strdisplaywidth(entry.add.client_name)) + end + return indexed_items, widths + end, + make_displayer = function(widths) + return entry_display.create { + separator = " ", + items = { + { width = widths.idx + 1 }, -- +1 for ":" suffix + { width = widths.command_title }, + { width = widths.client_name }, + }, + } + end, + make_display = function(displayer) + return function(e) + return displayer { + { e.value.idx .. ":", "TelescopePromptPrefix" }, + { e.value.add.command_title }, + { e.value.add.client_name, "TelescopeResultsComment" }, + } + end + end, + make_ordinal = function(e) + return e.idx .. e.add["command_title"] + end, + }, + }) + ) + + vim.ui.select = function(items, opts, on_choice) + opts = opts or {} + local prompt = vim.F.if_nil(opts.prompt, "Select one of") + if prompt:sub(-1, -1) == ":" then + prompt = prompt:sub(1, -2) + end + opts.format_item = vim.F.if_nil(opts.format_item, function(e) + return tostring(e) + end) + + -- schedule_wrap because closing the windows is deferred + -- See https://github.com/nvim-telescope/telescope.nvim/pull/2336 + -- And we only want to dispatch the callback when we're back in the original win + on_choice = vim.schedule_wrap(on_choice) + + -- We want or here because __TelescopeUISelectSpecificOpts[x] can be either nil or even false -> {} + local sopts = __TelescopeUISelectSpecificOpts[vim.F.if_nil(opts.kind, "")] or {} + local indexed_items, widths = vim.F.if_nil(sopts.make_indexed, function(items_) + local indexed_items = {} + for idx, item in ipairs(items_) do + table.insert(indexed_items, { idx = idx, text = item }) + end + return indexed_items + end)(items) + local displayer = vim.F.if_nil(sopts.make_displayer, function() end)(widths) + local make_display = vim.F.if_nil(sopts.make_display, function(_) + return function(e) + local x, _ = opts.format_item(e.value.text) + return x + end + end)(displayer) + local make_ordinal = vim.F.if_nil(sopts.make_ordinal, function(e) + return opts.format_item(e.text) + end) + pickers + .new(topts, { + prompt_title = string.gsub(prompt, "\n", " "), + finder = finders.new_table { + results = indexed_items, + entry_maker = function(e) + return { + value = e, + display = make_display, + ordinal = make_ordinal(e), + } + end, + }, + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + local cb = on_choice + on_choice = function(_, _) end + actions.close(prompt_bufnr) + if selection == nil then + utils.__warn_no_selection "ui-select" + cb(nil, nil) + return + end + cb(selection.value.text, selection.value.idx) + end) + actions.close:enhance { + post = function() + on_choice(nil, nil) + end, + } + return true + end, + sorter = conf.generic_sorter(topts), + }) + :find() + end + end, +} diff --git a/config/neovim/store/lazy-plugins/telescope-undo.nvim/LICENSE b/config/neovim/store/lazy-plugins/telescope-undo.nvim/LICENSE new file mode 100644 index 00000000..c7ea9102 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-undo.nvim/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Daniel Nägele + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/neovim/store/lazy-plugins/telescope-undo.nvim/README.md b/config/neovim/store/lazy-plugins/telescope-undo.nvim/README.md new file mode 100644 index 00000000..4acdc2a0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-undo.nvim/README.md @@ -0,0 +1,252 @@ +# telescope-undo.nvim +Visualize your undo tree and fuzzy-search changes in it. For those days where committing early and +often doesn't work out. + +![screenshot](https://user-images.githubusercontent.com/4604331/208297854-df5a104a-2fc1-4411-9f5f-5e40454d8dac.png) + +## Usage + +After invoking `telescope-undo` you can browse the current buffer's undo tree in a text-based tree +representation by using telescope's `move_selection_next/previous` actions. These are mapped to +arrow keys or `,` by default. Inserted text is fuzzy matched against the additions and +deletions in each undo state in your undo tree and the finder will limit the results accordingly. +While this obviously breaks the tree visuals, you can freely search in your undo states. The +previewer will always show the diff of the current selection with some context according to the +config or your `scrolloff` value. + +If you have found the undo state you were looking for, you can use `` or `` to revert to +that state. If you'd rather not change your whole buffer, you can use `` to yank the additions +of this undo state into your default register (use `` or `` to yank the deletions). + +## Installation +Install with your favorite Neovim package manager. + +As part of your Telescope plugin spec for [lazy.nvim](https://github.com/folke/lazy.nvim): + +```lua +{ + "nvim-telescope/telescope.nvim", + dependencies = { + "nvim-lua/plenary.nvim", + "debugloop/telescope-undo.nvim", + }, + config = function() + require("telescope").setup({ + -- the rest of your telescope config goes here + extensions = { + undo = { + -- telescope-undo.nvim config, see below + }, + -- other extensions: + -- file_browser = { ... } + }, + }) + require("telescope").load_extension("undo") + -- optional: vim.keymap.set("n", "u", "Telescope undo") + end, +}, +``` + +If you prefer standalone Lazy plugin specs (my personal recommendation), here's how you do that with +some more options as an example: + +```lua +{ + "debugloop/telescope-undo.nvim", + dependencies = { -- note how they're inverted to above example + { + "nvim-telescope/telescope.nvim", + dependencies = { "nvim-lua/plenary.nvim" }, + }, + }, + keys = { + { -- lazy style key map + "u", + "Telescope undo", + desc = "undo history", + }, + }, + opts = { + -- don't use `defaults = { }` here, do this in the main telescope spec + extensions = { + undo = { + -- telescope-undo.nvim config, see below + }, + -- no other extensions here, they can have their own spec too + }, + }, + config = function(_, opts) + -- Calling telescope's setup from multiple specs does not hurt, it will happily merge the + -- configs for us. We won't use data, as everything is in it's own namespace (telescope + -- defaults, as well as each extension). + require("telescope").setup(opts) + require("telescope").load_extension("undo") + end, +}, +``` + +Invoke using: + +```viml +" Directly by calling it through Telescope's extension interface: + +" using lua +:lua require("telescope").extensions.undo.undo() + +" using custom options for just this call +:lua require("telescope").extensions.undo.undo({ side_by_side = true }) + +" using legacy Vim script +:Telescope undo + +" Using the optional mapping: + +" using lua +:lua vim.keymap.set("n", "u", "Telescope undo") +" using legacy Vim script +:nmap u Telescope undo +``` + + +## Configuration + +The available configuration values are: + +* `use_delta`, which controls whether [delta](https://github.com/dandavison/delta) is used for fancy +diffs in the preview section. If set to false, `telescope-undo` will not use `delta` even when +available and fall back to a plain diff with treesitter highlights. +* `use_custom_command`, which can be used to use an *unsupported* diff tool other than `delta` +* `side_by_side`, which tells `delta` to render diffs side-by-side. Thus, requires `delta` to be +used. Be aware that `delta` always uses its own configuration, so it might be that you're getting +the side-by-side view even if this is set to false. +* `diff_context_lines`, defaults to your scrolloff value. +* `entry_format`, defaults to `"state #$ID, $STAT, $TIME""`, which contains the three supported +variables. +* `time_format`, defaults to "" for a timeago-style representation. Can be set to a [Lua date format + string](https://www.lua.org/pil/22.1.html). +* `saved_only`, defaults to false, but can be used to limit shown undo states to those that have +been saved to disk. + +Further, the undo telescope should accept any of the usual telescope attributes as well as the +special `theme` key which auto-extends the telescope theme *on top* of any of your explicitly +provided config. Of course, you might also want to remap some of the default keys. + +This is what the defaults look like with some additional explanations: + +```lua +opts = { + extensions = { + undo = { + use_delta = true, + use_custom_command = nil, -- setting this implies `use_delta = false`. Accepted format is: { "bash", "-c", "echo '$DIFF' | delta" } + side_by_side = false, + diff_context_lines = vim.o.scrolloff, + entry_format = "state #$ID, $STAT, $TIME", + time_format = "", + saved_only = false, + }, + }, +}, +``` + +The full list will always be available in the code providing the defaults +[here](https://github.com/debugloop/telescope-undo.nvim/blob/main/lua/telescope/_extensions/undo.lua#L6). + +My personal recommendation is the following, which maximizes the width of the preview to enable +side-by-side diffs: + +```lua +opts = { + extensions = { + undo = { + side_by_side = true, + layout_strategy = "vertical", + layout_config = { + preview_height = 0.8, + }, + }, + }, +} +``` + +## Mappings + +By default, the following mappings are enabled. + +```lua +require("telescope").setup({ + extensions = { + undo = { + mappings = { + i = { + [""] = require("telescope-undo.actions").yank_additions, + [""] = require("telescope-undo.actions").yank_deletions, + [""] = require("telescope-undo.actions").restore, + -- alternative defaults, for users whose terminals do questionable things with modified + [""] = require("telescope-undo.actions").yank_deletions, + [""] = require("telescope-undo.actions").restore, + }, + n = { + ["y"] = require("telescope-undo.actions").yank_additions, + ["Y"] = require("telescope-undo.actions").yank_deletions, + ["u"] = require("telescope-undo.actions").restore, + }, + }, + }, + }, +}) +``` + +> [!IMPORTANT] +> Note how above example uses the call to telescope's `setup()`. This is due to the fact that +> directly requiring these actions needs `telescope-undo` to be available already, which it is not +> inside lazy's `opts` key when using above "standalone" spec. See the next example for how to do it +> inside `opts`. + +There is one more mapping available, `yank_larger`. This yanks either the additions or the deletions +based on their line count, with the additions winning in case of a tie. This is how you configure +this mapping, or remap any of the default actions for that matter: + +```lua +opts = { + extensions = { + undo = { + mappings = { + -- Wrapping the actions inside a function prevents the error due to telescope-undo being not + -- yet loaded. + i = { + [""] = function(bufnr) + return require("telescope-undo.actions").yank_larger(bufnr) + end, + }, + n = { + ["y"] = function(bufnr) + return require("telescope-undo.actions").yank_larger(bufnr) + end, + }, + }, + }, + }, +} +``` + +If you wish to disable one of the default mappings, just set it to `false`. + +```lua +opts = { + extensions = { + undo = { + mappings = { + i = { + [""] = false, + }, + }, + }, + }, +} +``` + +## Contributions + +Suggestions, issues and patches are very much welcome. There are some TODOs sprinkeled into the code +that need addressing, but could also use some input and opinions. diff --git a/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/actions.lua b/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/actions.lua new file mode 100644 index 00000000..500a4017 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/actions.lua @@ -0,0 +1,84 @@ +local actions = require("telescope.actions") +local actions_state = require("telescope.actions.state") + +local function _get_default_register() + local clipboardFlags = vim.split(vim.api.nvim_get_option("clipboard"), ",") + if vim.tbl_contains(clipboardFlags, "unnamedplus") then + return "+" + end + if vim.tbl_contains(clipboardFlags, "unnamed") then + return "*" + end + return '"' +end + +-- TODO maybe provide actions for the following: +-- * yank to arbitrary registers +-- * open state in new file +-- * rewind to last saved state +-- * goto lastest undo state +-- * something with git staging? +local myactions = {} + +-- these actions are returning named functions with the prompt_bufnr en-closured so that their metatable contains a name that telescope which-key can use +myactions.restore = function(prompt_bufnr) + local function restore() + -- makes the selected undo state the buffers current state + local entry = actions_state.get_selected_entry() + if entry ~= nil then + vim.api.nvim_buf_call(entry.value.bufnr, function() + vim.cmd("undo " .. entry.value.seq) + end) + actions.close(prompt_bufnr) + end + end + return restore +end + +myactions.yank_deletions = function(prompt_bufnr) + local function yank_deletions() + -- yanks the deletions from the currently selected undo state into the default register + local entry = actions_state.get_selected_entry() + if entry ~= nil then + vim.fn.setreg(_get_default_register(), entry.value.deletions, (#entry.value.deletions > 1) and "V" or "v") + actions.close(prompt_bufnr) + return entry.value.deletions + end + end + return yank_deletions +end + +myactions.yank_additions = function(prompt_bufnr) + local function yank_additions() + -- yanks the additions from the currently selected undo state into the default register + local entry = actions_state.get_selected_entry() + if entry ~= nil then + vim.fn.setreg(_get_default_register(), entry.value.additions, (#entry.value.additions > 1) and "V" or "v") + actions.close(prompt_bufnr) + return entry.value.additions + end + end + return yank_additions +end + +myactions.yank_larger = function(prompt_bufnr) + local function yank_larger() + -- yanks the additions from the currently selected undo state into the default register + local entry = actions_state.get_selected_entry() + if entry == nil then + return + end + if #entry.value.additions >= #entry.value.deletions then + vim.fn.setreg(_get_default_register(), entry.value.additions, (#entry.value.additions > 1) and "V" or "v") + actions.close(prompt_bufnr) + return entry.value.additions + else + vim.fn.setreg(_get_default_register(), entry.value.deletions, (#entry.value.deletions > 1) and "V" or "v") + actions.close(prompt_bufnr) + return entry.value.deletions + end + end + return yank_larger +end + +return myactions diff --git a/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/init.lua b/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/init.lua new file mode 100644 index 00000000..6e92792b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/init.lua @@ -0,0 +1,172 @@ +local finders = require("telescope.finders") +local pickers = require("telescope.pickers") +local conf = require("telescope.config").values + +require("telescope-undo.previewer") +require("telescope-undo.actions") +require("telescope-undo.lua-timeago") + +local function _traverse_undotree(opts, entries, level) + local undolist = {} + -- create diffs for each entry in our undotree + for i = #entries, 1, -1 do + if opts.saved_only ~= nil and opts.saved_only and entries[i].save == nil then + goto continue + end + -- grab the buffer as it is after this iteration's undo state + vim.cmd("silent undo " .. entries[i].seq) + local buffer_after_lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) or {} + local buffer_after = table.concat(buffer_after_lines, "\n") + + -- grab the buffer as it is after this undo state's parent + vim.cmd("silent undo") + local buffer_before_lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) or {} + local buffer_before = table.concat(buffer_before_lines, "\n") + + -- build diff header so that delta can go ahead and syntax highlight + local filename = vim.fn.expand("%") + local header = filename .. "\n--- " .. filename .. "\n+++ " .. filename .. "\n" + + -- do the diff using our internal diff function + local diff = vim.diff(buffer_before, buffer_after, { + algorithm = "patience", + ctxlen = opts.diff_context_lines or 0, + }) + + -- extract data for yanking and searching + local ordinal = "" + local additions = {} + local deletions = {} + for line in (diff .. "\n"):gmatch("(.-)\n") do + if line:sub(1, 1) == "+" then + local content = line:sub(2, -1) + table.insert(additions, content) + ordinal = ordinal .. content + elseif line:sub(1, 1) == "-" then + local content = line:sub(2, -1) + table.insert(deletions, content) + ordinal = ordinal .. content + end + end + + -- use the data we just created to feed into our finder later + table.insert(undolist, { + seq = entries[i].seq, -- save state number, used in display and to restore + alt = level, -- current level, i.e. how deep into alt branches are we, used to graph + first = i == #entries, -- whether this is the first node in this branch, used to graph + time = entries[i].time, -- save state time, used in display + ordinal = ordinal, -- a long string of all additions and deletions, used for search + diff = header .. diff, -- the proper diff, used for preview + additions = additions, -- all additions, used to yank a result + deletions = deletions, -- all deletions, used to yank a result + bufnr = vim.api.nvim_get_current_buf(), -- for which buffer this telescope was invoked, used to restore + }) + + -- descend recursively into alternate histories of undo states + if entries[i].alt ~= nil then + local alt_undolist = _traverse_undotree(opts, entries[i].alt, level + 1) + -- pretend these results are our results + for _, elem in pairs(alt_undolist) do + table.insert(undolist, elem) + end + end + ::continue:: + end + return undolist +end + +local function build_undolist(opts) + -- save our current cursor + local cursor = vim.api.nvim_win_get_cursor(0) + + -- get all diffs + local ut = vim.fn.undotree() + + -- TODO: maybe use this opportunity to limit the number of root nodes we process overall, to ensure good performance + local undolist = _traverse_undotree(opts, ut.entries, 0) + + -- restore everything after all diffs have been created + -- BUG: `gi` (last insert location) is being killed by our method, we should save that as well + vim.cmd("silent undo " .. ut.seq_cur) + vim.api.nvim_win_set_cursor(0, cursor) + + return undolist +end + +local M = {} + +M.undo = function(opts) + if not vim.api.nvim_buf_get_option(0, "modifiable") then + print("telescope-undo.nvim: Current buffer is not modifiable.") + return + end + opts = opts or {} + pickers + .new(opts, { + prompt_title = "Undo History", + finder = finders.new_table({ + results = build_undolist(opts), + entry_maker = function(undo) + local order = require("telescope.config").values.sorting_strategy + + -- TODO: show a table instead of a list + if #undo.additions + #undo.deletions == 0 then + -- skip empty changes, vim has these sometimes... + return nil + end + -- the following prefix should work out to this graph structure: + -- state #1 + -- └─state #2 + -- state #3 + -- ├─state #4 + -- └─state #5 + -- state #6 + -- ├─state #7 + -- ┆ ├─state #8 + -- ┆ └─state #9 + -- └─state #10 + local prefix = "" + if undo.alt > 0 then + prefix = string.rep("┆ ", undo.alt - 1) + if undo.first then + local corner = order == "ascending" and "┌" or "└" + prefix = prefix .. corner .. "╴" + else + prefix = prefix .. "├╴" + end + end + local diffstat = "" + if #undo.additions > 0 then + diffstat = "+" .. #undo.additions + end + if #undo.deletions > 0 then + if diffstat ~= "" then + diffstat = diffstat .. " " + end + diffstat = "-" .. #undo.deletions + end + local formatted_time = opts.time_format == "" and timeago(undo.time) or os.date(opts.time_format, undo.time) + return { + value = undo, + display = prefix + .. opts.entry_format:gsub("$ID", undo.seq):gsub("$STAT", diffstat):gsub("$TIME", formatted_time), + ordinal = undo.ordinal, + } + end, + }), + previewer = get_previewer(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr, map) + for _, mode in pairs({ "i", "n" }) do + for key, get_action in pairs(opts.mappings[mode] or {}) do + map(mode, key, get_action(prompt_bufnr)) + end + end + -- TODO: provide means to filter for time frames + return true -- include defaults as well + end, + }) + :find() +end + +return M diff --git a/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/lua-timeago.lua b/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/lua-timeago.lua new file mode 100644 index 00000000..2f63c53b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/lua-timeago.lua @@ -0,0 +1,72 @@ +-- This file is subject to LGPL-2.1 and installed from: +-- https://github.com/f-person/lua-timeago +-- +-- TODO: Understand lua require weirdness and properly include this as a git submodule + +local language = { + justnow = "just now", + minute = { singular = "a minute ago", plural = "minutes ago" }, + hour = { singular = "an hour ago", plural = "hours ago" }, + day = { singular = "a day ago", plural = "days ago" }, + week = { singular = "a week ago", plural = "weeks ago" }, + month = { singular = "a month ago", plural = "months ago" }, + year = { singular = "a year ago", plural = "years ago" }, +} + +local function round(num) + return math.floor(num + 0.5) +end + +function timeago(time) + local now = os.time() + local diff_seconds = os.difftime(now, time) + if diff_seconds < 45 then + return language.justnow + end + + local diff_minutes = diff_seconds / 60 + if diff_minutes < 1.5 then + return language.minute.singular + end + if diff_minutes < 59.5 then + return round(diff_minutes) .. " " .. language.minute.plural + end + + local diff_hours = diff_minutes / 60 + if diff_hours < 1.5 then + return language.hour.singular + end + if diff_hours < 23.5 then + return round(diff_hours) .. " " .. language.hour.plural + end + + local diff_days = diff_hours / 24 + if diff_days < 1.5 then + return language.day.singular + end + if diff_days < 7.5 then + return round(diff_days) .. " " .. language.day.plural + end + + local diff_weeks = diff_days / 7 + if diff_weeks < 1.5 then + return language.week.singular + end + if diff_weeks < 4.5 then + return round(diff_weeks) .. " " .. language.week.plural + end + + local diff_months = diff_days / 30 + if diff_months < 1.5 then + return language.month.singular + end + if diff_months < 11.5 then + return round(diff_months) .. " " .. language.month.plural + end + + local diff_years = diff_days / 365.25 + if diff_years < 1.5 then + return language.year.singular + end + return round(diff_years) .. " " .. language.year.plural +end diff --git a/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/previewer.lua b/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/previewer.lua new file mode 100644 index 00000000..8248822b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope-undo/previewer.lua @@ -0,0 +1,57 @@ +local previewers = require("telescope.previewers") +local is_wsl = (function() + local output = vim.fn.systemlist("uname -r") + return not not string.find(output[1] or "", "WSL") +end)() +function get_previewer(opts) + if opts.use_custom_command ~= nil then + return previewers.new_termopen_previewer({ + get_command = function(entry, status) + local difftext = entry.value.diff:gsub("'", [['"'"']]) + local shlexed = {} + for i, part in ipairs(opts.use_custom_command) do + shlexed[i] = part:gsub("$DIFF", difftext) + end + return shlexed + end, + }) + end + local has_powershell = vim.fn.executable("powershell") == 1 + local has_bash = vim.fn.executable("bash") == 1 + if opts.use_delta and not is_wsl and (has_powershell or has_bash) and vim.fn.executable("delta") == 1 then + return previewers.new_termopen_previewer({ + get_command = function(entry, status) + local append = "" + if opts.side_by_side == true then + append = append .. " -s" + end + if has_powershell then + return { + "powershell", + "-Command", + "echo '" .. entry.value.diff:gsub([[']], [['']]) .. "' | delta" .. append, + } + elseif has_bash then + return { + "bash", + "-c", + "echo '" .. entry.value.diff:gsub("'", [['"'"']]) .. "' | delta" .. append, + -- HACK: check out this escape method -----^ + } + end + end, + }) + else + return previewers.new_buffer_previewer({ + -- this is not the prettiest preview... + define_preview = function(self, entry, status) + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, true, vim.split(entry.value.diff, "\n")) + require("telescope.previewers.utils").highlighter( + self.state.bufnr, + "diff", + { preview = { treesitter = { enable = {} } } } + ) + end, + }) + end +end diff --git a/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope/_extensions/undo.lua b/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope/_extensions/undo.lua new file mode 100644 index 00000000..cfdd46ad --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-undo.nvim/lua/telescope/_extensions/undo.lua @@ -0,0 +1,57 @@ +local has_telescope, telescope = pcall(require, "telescope") +if not has_telescope then + error("telescope_undo.nvim requires telescope.nvim - https://github.com/nvim-telescope/telescope.nvim") +end + +-- full list of available config items and their defaults +local defaults = { + use_delta = true, + use_custom_command = nil, -- should be in this format: { "bash", "-c", "echo '$DIFF' | delta" } + side_by_side = false, + diff_context_lines = vim.o.scrolloff, + entry_format = "state #$ID, $STAT, $TIME", + time_format = "", + saved_only = false, + mappings = { + i = { + [""] = require("telescope-undo.actions").yank_additions, + [""] = require("telescope-undo.actions").yank_deletions, + [""] = require("telescope-undo.actions").restore, + -- alternative defaults, for users whose terminals do questionable things with modified + [""] = require("telescope-undo.actions").yank_deletions, + [""] = require("telescope-undo.actions").restore, + }, + n = { + ["y"] = require("telescope-undo.actions").yank_additions, + ["Y"] = require("telescope-undo.actions").yank_deletions, + ["u"] = require("telescope-undo.actions").restore, + }, + }, +} + +local M = { + exports = {}, +} + +M.exports.undo = function(config) + config = vim.tbl_deep_extend("force", M.config, config or {}) + if config.theme then + config = require("telescope.themes")["get_" .. config.theme](config) + end + require("telescope-undo").undo(config) +end + +M.setup = function(extension_config, telescope_config) + M.config = vim.tbl_deep_extend("force", defaults, extension_config) + -- Remove default keymaps that have been disabled by the user. + for _, mode in ipairs({ "i", "n" }) do + M.config.mappings[mode] = vim.tbl_map(function(val) + return val ~= false and val or nil + end, M.config.mappings[mode]) + end + if M.config["side_by_side"] and not M.config["use_delta"] then + error("telescope_undo.nvim: setting side_by_side but not use_delta will have no effect") + end +end + +return telescope.register_extension(M) diff --git a/config/neovim/store/lazy-plugins/telescope-undo.nvim/stylua.toml b/config/neovim/store/lazy-plugins/telescope-undo.nvim/stylua.toml new file mode 100644 index 00000000..0435f677 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope-undo.nvim/stylua.toml @@ -0,0 +1,2 @@ +indent_type = "Spaces" +indent_width = 2 diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/autoload/health/telescope.vim b/config/neovim/store/lazy-plugins/telescope.nvim/autoload/health/telescope.vim new file mode 100644 index 00000000..46cc9280 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/autoload/health/telescope.vim @@ -0,0 +1,3 @@ +function! health#telescope#check() + lua require 'telescope.health'.check() +endfunction diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/earth b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/earth new file mode 100644 index 00000000..aa624e17 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/earth @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓   ▓▓▓▓▓▓▓▓▓▓▓▒▒▒░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▓▓▓▓▓▒▒   ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▓▒▒░   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓ ░▓▓▓▒▒▒▒▒  ▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▒▓▓▓▓▓▓▒▒ ▒▓▒░░▒▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓░▓▓▓▒▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓░░▒▓▓▓▓▓▓▓▓▓   ▒░░  ░ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▓▓▓▒▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▒▓▓▓▓▓▓▓▓ ▒░  ░▓  ░ ░ ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▒▒▒▓▓▒░░▓▒▓▓▓▓▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓     ░▒▓      ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▒▒▓▒▒░░░▓▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░    ▓       ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▒▒▒▒▒▒░▒▓▓▓▓▒▓▒▒▓▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▒▒▓▒▒░▓▓▓░▓▓▓▓▓▒▓    ░▒▓▒▓░       ▓▓▓▓▓▓ +▓▓▓▓▓▓ ▒▒▒▒░▒▒▒░░▒▓▓▒▓░▓▓▓░▒▓▓▓▓▓▓▓▓▒▓▓▒▒▒▒▒░░░▓▓ ▓▓░▓▓▓▒▒░  ▓ ░▒▓         ▓▓▓▓▓ +▓▓▓▓▓▓▒▒▒▒░▒▒▒░▒▓▒▒▓▓▓▓▓▓▒▓▓ ▓░▓▓▓▒▒▒▓▓▓▓▒░░░░▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒         ▓▓▓▓▓ +▓▓▓▓▓ ▒▒▒▓▓▒▒▒▒▒▓▓▒▒▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▒▒ ▓▓▒▓▒▒░▒ ▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒░▓   ░░   ▒ ▓▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒▒▒▒▒▓▒▓▓▒▓▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▒▒▒▓▓▒▓▓▓▓▓▓▓▓ ░▓▓▓▓▒▒░   ▒ ░   ▒░ ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒▒▒▒▒▓▓▒▓▓▓▒▓▓▓▓▒▓▓▓▓▒▓▒░ ▓▒▓▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓  ▒ ▒░▒░░    ░ ▒░     ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▓▒▒▓▓▓▓▓▓▓▒▓▓▓▒▓▓▓▓▓▒▒▓░▓▓▓▒░▒   ░▒  ░ ░    ▒    ░▒░    ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒░░▒▓▒▒▒▒▒▒▒▓ ▓▓▓▒▓▓▓▓▓▓ ▓▒░░▓▓░▒▓▓            ░   ░ ░▒ ░░░░▒░ ░░▒ ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▓▒▒▒▒░▒░▒▓  ▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▒ ▓▓▓▓░          ▒▒▓▒░ ▒░░ ▒▓▓▓▒▒▒░▒ ▓ ▓▓▓▓ +▓▓▓▓▓ ▒░▒▓▒▒░░▒░░░▒▒▓ ░ ░▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▒ ▒▓   ▓▓  ░░░▒░▒   ▒▓▓▓▓▒░░    ▓▓▓▓▓ +▓▓▓▓▓▓▒▒░  ▒▓▒▒▒▒▓▒░ ▒ ░▒ ░▓▓▓▓▓▓▓▓▓▓▓▓░▒░░▓▓▓▓ ▓▓▓ ░▓▒░  ░ ░▒▒░▒▓▓░░░▒    ▓▓▓▓▓ +▓▓▓▓▓▓░▒▒▒▒ ▒  ▒▒░▓▒░▒ ▒░▒▓░▒▒▓▓▓▓▓▓▓▓▓▒▒░ ▒▓▓▒▓▓▓▓▓▓▓▓▓▒░▓▒ ▓▓▒▓▓▓▓▓▓▒░ ▒▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▒▒▒▒▒▒░▒▒▒░░▒▓░░▒▒░▒░▒░▒▓▓▓▒▒▒▒▓░ ░ ░       ▓▓▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▒▒░ ▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▒▒░▒▒▒▒░▒▒░▒ ▒ ▓▓▓░▒▓░▒▓▓▒░░░░▒▒  ░░▓ ▒░ ▒░░▒░▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒ ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▒░▒▒▒▒▒░▒▒▒░▒▒▒▒  ▒▒░ ▒▒▒▓░▒▒   ▒▓▓░▒      ▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒ ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒░▒▒░▒░▒▒▓░▒▓▓░▒▒░▒░▒▓▒▓▓▓▓▒▒▓░▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒░▒▒▒▒░▒░░▓▒▓▒▒▓░▓▓░▓▓ ▓▒▓ ▒  ░▓ ▒░▓░▓▓▓▓▓▓▓▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓ ▒ ▒▒▒▒▒▒▒░▒░▒░░░▒▒░░░░ ▒ ░░▓▒░▓▓▓▓▓▒▓▓▓ ▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒▓░▒▒░▒░▒▒░░▒▓▒▒▓▒▒▒▓▒░▓▒▒▓▓▓▓▓▓▓▓░░▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒░░▒▓▓▓▒▓▓▓▒▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒ ▒▒▒▒░░▒▒▒▓▒▒▓▒░▒▒▒▒░▒▒▓▓▒▒▓▓▒▓▓▓▓▓▓▓▓▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒░▒▒▒▒▒▒▒▒▒▒▓▒▒▒▓▓▒▓▒▒▒▒▓▓▒▒▓▓▓▓▓▒▓░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▓▓▒▓▓▓▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▓▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/jupiter b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/jupiter new file mode 100644 index 00000000..dac1487f --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/jupiter @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ░░░ ░    ░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▒▒▒▒▒▒▒░░ ▒ ░▒░ ░░▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░▒▓▒▒░▒▒ ▒▒▒░▒▒░▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒ ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▓▒▓▒▓▓▓▓▓▓▓▓▒▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒   ▓ ░   ░▒▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▒▒▓▓▒▓▓▓▒▒▒▒▓▒▒▒▒▒▒▓▒▓▓▒▓▒▒▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▒▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▒▒▒▒▓▒▒▓▓▓▒▒▒▒▒▒▒▒▓░░░▒▒▒▒░░░░░▒▒▒░░░░░▒▒▒░░░▒░▒▒▒▒▒▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▓▓▓▓▓▒▓▓▓▓▒▒▓▓▒▒▒▓▓▒▒▒▒▓▓▓▒▓▓▓▓▓▒▓▒▓▓▓▓▓▓▓▓ ▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▓▓▓▒░▒▓▒▒▓▓▓▒▓▓▒▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓▒░▓▒▓▓   ▒ ▓▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▒▒░▓▓░░░  ▒   ▒▒▓▓▒  ▓▓░▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▒▓▓▓▒▒▓▓░▒░ ▓▓▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓ ▓▓▓ +▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▓▓▓▓▒▓▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▒▓▓▓▓▓▒▒▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓░░▓▒▒▓▒▓ ▒▒▓▓▒▓▓▒▓▓▒▓▓▓▓▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▒▒▒▒▒▒▓▒▓▓▓▒▒▓▓▒▒    ░░▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▒▓▓▓▒▓▓▓▓▒      ░▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▓▓▓▒▓▒▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/mars b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/mars new file mode 100644 index 00000000..2eb8cfdd --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/mars @@ -0,0 +1,27 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░▒   ▓▒ ░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░  ░░░▒▒ ░ ▒░  ░░░░░░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░ ░░  ░ ░░▒▓░░░░░░░ ░░ ░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░  ░░░░░░░░ ░▒░▒░░░▒▒░░░░░░░░░ ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░ ░░░░░░░░░░░░░░░▒▒▒▓▒▒▒▒▒▒▒░░░░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░░ ░  ░░░░░░░░░░ ░░░▒▒▒▒▒▒▒▓▒▒▒▒░▒▒░░░▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░ ░░         ░  ░  ░ ░░▒▓▒▒▒▒▒▒▒▓▒▒░▒░▒▒░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░ ░░                  ░ ░░▒▒▒▒▒▓▒▒▓▒▒░░ ▒▒░ ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░                        ░░░▒▒▒▒▒▒▒▒▒░ ░░▒░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                          ░▒░░▒▒▒▒▒▒░░░░░░░░░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░░ ░░░░░▒▒░▒░▒▒▒░░░░░ ░ ░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░ ░▒▒ ░ ░▒░░░░░░░░░  ░▒░░ ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░░  ░░ ░░ ░░░░░░░ ░░░░  ░░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓        ░           ░░ ░              ░     ▒░░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                      ░░░░     ░░     ░░ ░░░░░ ▒░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                       ░░░             ░ ░░▒░ ░▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░░  ░  ░       ░░░  ░▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░                       ░ ▒        ░  ░░░▒▒░▒▒▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░        ░     ░ ░    ▒  ░░░ ░  ░░░░▒░░▓░▒░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░        ░          ░░ ▒▓▒░▒▒▒ ░░░░▒▒░▒▒ ░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░ ░   ░░░          ░▒ ▒▒▒▓▒▒░░░░░░░░░▒░▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░ ░░░░░░░░░░ ░░░░ ▓▒▒▒▒▒▒▒▒▒▒░░░░░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░░░░░░▒▒▒▒▒░▒▒▒░░▒▒▓▓▓▓▒▓▒▒▒▒▒░▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░   ░▒▒░░░░░▒▒░░▒▒▒▓▓▒▒▒▒▒▒░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░ ░ ░░░░░░░░░▒▒░░░░░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░ ░ ░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/mercury b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/mercury new file mode 100644 index 00000000..fcad37da --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/mercury @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓            ░  ░░    ░   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░░            ░░   ░░  ░░ ░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓      ░░            ░   ░ ░░ ░ ░     ░   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░░ ░          ░ ░        ░░ ░  ░░░     ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                 ░░    ░          ░░  ░  ░ ░  ░ ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓             ░░     ░░░░      ░ ░  ░ ░░  ░ ░░░░ ░░░░  ▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓             ░░  ░     ░ ░░░ ░░ ░ ░░░    ░ ░░░░░░░ ░░░░░ ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓ ░    ░   ░ ░░░  ░░░ ░░  ░░░░░░   ░░░░░  ░ ░ ░░ ░░░ ░░░░░░░ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓░   ░░ ░░  ░  ░  ░░░  ░░░ ░░ ░░  ░  ░░░░░░ ░░░ ░░░   ░░ ░░░░░░░▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓    ░░           ░░ ░   ░ ░░  ░░░░░░ ░░░░░░ ░    ░ ░  ░  ░░░░░ ░ ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓ ░   ░ ░░░    ░  ░░░░ ░ ░ ░░░  ░░░░░░░░░░░░░░░░░░░ ░ ░ ░ ░░░░░░ ░░░▓▓▓▓▓▓ +▓▓▓▓▓▓    ░ ░     ░     ░░░░      ░   ░░░   ░ ░░░░░░░░░░░ ░░  ░░░  ░  ░ ░░▓▓▓▓▓▓ +▓▓▓▓▓▓ ░   ░  ░░░ ░   ░░░░░     ░   ░░  ░░   ░░░   ░░░░░░░░  ░░░░░░░░░░░░ ░▓▓▓▓▓ +▓▓▓▓▓░ ░  ░░        ░ ░░░ ░ ░░ ░  ░░░░░░░░ ░  ░░░ ░ ░░░░░░    ░░░ ░░░░░░░░  ▓▓▓▓ +▓▓▓▓▓   ░░░ ░░░░░  ░░  ░░░░░   ░░░░ ░░  ░ ░░░ ░░░░░░ ░ ░░░░ ░  ░░░░░░   ░░ ░▓▓▓▓ +▓▓▓▓▓ ░░░░░░░ ░░░     ░░░░ ░░░░░░░░     ░░░ ░      ░   ░░░░ ░░░░░░░░░░  ░░░ ▓▓▓▓ +▓▓▓▓ ░ ░░░░░ ░░░   ░░░░░ ░ ░░░░░░░░ ░   ░  ░  ░ ░░ ░░  ░░░░░░░░░░░░░░░░░░░░░▓▓▓▓ +▓▓▓▓░░░░░░░░░░░░░░░░░   ░    ░░ ░░░░░░  ░      ░ ░░░     ░ ░░░░░░░░░░░░░░░░ ▓▓▓▓ +▓▓▓▓ ░  ░░ ░░  ░░░░░░  ░ ░░    ░ ░░░░░         ░░ ░░░░░░░░░ ░░░░░░░░░░░░░ ░ ▓▓▓▓ +▓▓▓▓▓ ░░░       ░░░░░░ ░  ░     ░  ░░░░░ ░░░ ░░░░ ░░░░░░░░ ░░░░░░░░░░░░░░ ░░▓▓▓▓ +▓▓▓▓▓ ░░░░░░ ░  ░░░ ░░ ░ ░░░  ░     ░░░░ ░ ░░░░  ░░░ ░  ░░░  ░░░░░░░░   ░░  ▓▓▓▓ +▓▓▓▓▓░░░  ░░ ░░  ░░░ ░░░░░░░░░░░   ░  ░░░ ░░ ░ ░░░░░    ░ ░░ ░░ ░░░ ░ ░░ ░░░▓▓▓▓ +▓▓▓▓▓▓░░  ░░ ░  ░░░░░░░░░░░░░░░░░░░░░░░ ░  ░░░░       ░░░  ░░░░░░░░ ░░░ ░░ ▓▓▓▓▓ +▓▓▓▓▓▓▓  ░░ ░░░░░░░ ░ ░░░░ ░░░░░ ░░░░░   ░░ ░░░    ░  ░░ ░░░ ░ ░  ░░░░  ░░▓▓▓▓▓▓ +▓▓▓▓▓▓▓  ░░░░░░░░░░░░░░░░░░░ ░   ░░░░░░ ░  ░░ ░    ░  ░     ░  ░░░░  ░░░░░▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓ ░░░░ ░░░░░      ░░░░░  ░░         ░ ░ ░                  ░ ░ ░ ░▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓ ░░░░  ░░░░░░░░░░░░░   ░           ░░             ░    ░░     ░▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓      ░░░░  ░  ░░                ░ ░ ░  ░░        ░      ░░ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓    ░░░░░░░░        ░                                 ░░ ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓   ░░░░░░░                     ░░░    ░              ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓       ░           ░         ░  ░     ░          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░                      ░  ░    ░          ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░                      ░           ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░   ░        ░              ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                   ░  ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/moon b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/moon new file mode 100644 index 00000000..2943e284 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/moon @@ -0,0 +1,35 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/neptune b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/neptune new file mode 100644 index 00000000..9c2954e0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/neptune @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓       ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░▒▒▒░░░▒▒░░▒░▒░▒░░▒▒░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░░░░░░░░░░░░░░▒░░░░▒░▒░░▒▒▒░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░░░░   ░░░░░░░░░░░░▒░▒░▒░░░▒▒░░▒░▒░░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒░░▒░▒░▒░░░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒░▒░▒░░░░▒▒▒░░░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░░░░░░░░░░░░░      ░░░░░░░░░░░░░░░░░▒░░▒▒░▒░░░░   ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▒░░░▒░▒░░░░░░                 ░ ░░░░░░░░░░░▒░▒░░░░░░░   ▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓░▒░▒░░░░░                           ░░░░░░▒░░▒▒░░░▒░░░░░  ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓ ▒▒▒░░░░░░                              ░░░░▒░▒░▒▒░▒▒░░░░░   ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓░▒▒░░░░░░                                ░ ░░░░░░▒░░▒░░░░░░░   ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓ ▒▒▒░░░░░                                   ░░░░░░▒░░▒░░▒▒░░░░   ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▒░░░░░░                                    ░░░░░▒░░░▒░░▒░▒░░░   ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓░░░▒░░░                                       ░░░░░▒░░▒░▒░▒░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▒░▒▒░░                         ░ ░░░░░░░░░     ░░░░░░░▒▒░░░▒░░░    ▓▓▓▓▓▓▓ +▓▓▓▓▓░▒░▒░░░░                          ░░░░░░░░░░░░░░░░░░░▒░▒░▒░░░░░░░░  ▓▓▓▓▓▓▓ +▓▓▓▓▓▒▒▒░░░░░                              ░ ░░░░░░░░░░░ ░▒▒░▒▒▒░░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓▒▒▒▒░░░░                                     ░░░░░░▒░▒▒▒▒▒░░░░░░    ▓▓▓▓▓▓▓ +▓▓▓▓▓░░▒░░░░░░                                   ░░░░░░  ░▒░▒░░░▒░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓░░▒░░░░░░                                   ░░░░░▒▒░░░░░▒▒░▒░▒░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓ ▒▒▒▒░░░░░░                                ░░░░░░░░░░░▒░▒░░▒░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓▓░▒░░▒░░░░░                            ░ ░░░░░░░░░░░░▒░▒░░░░░░░░   ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓░▒▒░▒▒░░░░░ ░      ░     ░   ░   ░░░░░░░░░░░░▒░░░░░░▒░░░░░░░░     ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓░▒▒░▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒░░░░░░░░░▒░░▒░░░░░░░░░   ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓ ▒▒▒░▒ ░░░░░░░░░░░░░░░░░░░▒░░░░░░░░░▒░░░▒░░░▒░▒░▒░░░░░▒░░ ░    ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░▒░░░▒░░░░░░░░░░░▒░░░░░░░░░░░░▒░▒░░░░░░░░░░    ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓ ▒░░░░░░░░░░░░░░░░░░▒░░░░░░░░░░░▒░▒░░▒░░░▒░▒░░░░░░░░░ ░░   ▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓░▒░░░░▒░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░▒░░░░░░░░░░░░░    ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓░░▒░░░▒░░░░░░▒░░░░░░░░░░▒░░░░░░░▒░░░░░▒░░░░░░ ░      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░▒░░░▒░░░░░░▒▒░░░░▒░▒░░░▒▒▒░░░░░░░░░  ░     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░▒░░░░░░░░░░░░░░░░░░░░░░▒░░░░░░░░░░      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░▒░░░░░░░░░▒░░░░░░▒░░▒░░▒░░░░░░░ ░    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░ ░░░░░░░░░░░░░░░░░░░▒░░░░░░░       ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓     ░░░░░░░░░░░░░░░░        ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/pluto b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/pluto new file mode 100644 index 00000000..cfcde6ff --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/pluto @@ -0,0 +1,39 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▒▒▒▒▒▒▒░░░░ ░░░▒░▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▒▒▒▒░░ ░░░░░░ ░░░▒▒▒░▒▒▒▒▒▒▒▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒  ░░  ░     ░░░ ░ ░▒▒▒▒▒▒▒▓▓▒▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▒▒▒░           ░        ░░▒▒▒▒▒▒▓▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒░                  ░░░▒▒░▒▒▒▒▒▓▓▓▒▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▒░░     ▒   ░       ░░░░░░▒▒▒▒▒▓▓▓▓▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▒▒▒░░░▒ ░░▒▒▒░░      ░░░░░░▒▒▒▒▒▓▓▓▓▓▓▓▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▒▒░▒▒▒▒▒▒▒▒▒▒░░░░░░▒▒░▒▒▒▒▒▒▒▒▓▓▒▓▓▓▓▓▓▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▒▓░▒▒▒▒▒▒▒▒░░▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▓▓▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▒▒▒▒▓▒▒▒▒░░▒ ░░▒▒▒▒▒▓▓▒▓▒▒▒▒▒▒▓▓▓▓▓▓▓▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▒▓▒▒▒▒▒       ░▒▒▒▓▓▒▓▓▒▒▒▒▒▓▒▒▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▓▓▓▓▓▒▓▓▒▒▒▒ ░         ░▒▓▒▒▒▒▒▒▒░▒▓▒▒▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓▒▓▓▓▓▒▒▓▓▒░             ░▒░░░▒▒▒▒▒▒▒▓▓▓▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▒▓▓▒▒▓▓▓▓▓░░░░          ░░░░ ░▒▒▒▒░▓▒▒▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░░▒ ▒▓▓▒▓▓▒░         ░░░░░░░▒▒▒▒▒▒▓▓▓▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒      ▒▒▒ ▒▒▒▒░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▓▓▓▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒          ▒▓▒▒▒░░░░░▒▒▒░▒▒▒▒▒▒▒▓▓▓▓▓▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ▒ ▒   ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓░░▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒  ▒       ▓▓▓▓▒▒▒▒▒▒▒▓▓▓▓▓▓░      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▒░▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒░▒░     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▒▒▒▒▒    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓▓▓▒▒▓▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/saturn b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/saturn new file mode 100644 index 00000000..68a7ffae --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/saturn @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒  ▒ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒░▒▒▒▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒ ▒▒▓▒▓▓▒▓░▓░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒    ░▒▒▒ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░▒▒  ▓▓  ▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░▒ ▓▓▓▓▓▓  ▒▓▓▓▓▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▒ ▒▒▓▓▒▓▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ▒▒ ▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▓▒▒ ▒▓▓▒▒▒▓▒░▓▒▓▒▓▒▓░▒▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒░▓▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒▒░▒▓▓▓▓▓▓▓▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒  ▓▒▒▒ ▒░░░░░▒▒▒▒▒▒▒▒▒▒░░▓▓▓▓▓▓▓▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░  ▒▓▒▒▒▒░▒░░░░▒▒▒▒▒▒▒▒▒▒▒░▒▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▒  ▓▓▒▒▒▒▒░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒     ▓▓▒▒▒░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▓  ░▒▒▓▒▒▒▓▒░░░░░▒▒▒▒▒▒▒▓▒▓▒▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▓▒  ▒▓▒▒▒▒▒░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓░░▒▒▓▒▒▒ ░▒▒▒▒░▒▒▒▒▒▓▒▒▒▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒░  ▓ ░▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▓▓▒▓   ▓▒▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓░▓░  ▓▓▓▓▓▒▒▒░▒▓▒▒▒▒▓▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▒ ▒▓▒▒▒▓▒▒▒▒▒▓▒▒▒▒▒▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▒▒▒▒▒▓▒▓▓▒▒▒▒░░▓▓▓▓▓ ▓▓ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒▓▓▒▓▒ ▓▓▓▓   ▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓      ▒▒░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒   ░▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▒▓▓▓▒▒░ ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░▒▒▒▒▒░▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▒▒░▒▒ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/uranus b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/uranus new file mode 100644 index 00000000..f5a8b36a --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/uranus @@ -0,0 +1,39 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓       ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░▒▓▓▓▓▓▓▒▒░     ░░▒▒▓▓▓▓░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░▒▓▓▓▓▓▓▒░              ▒▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓▓▓▓▓▓▓▓░                 ░▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▒▓▓▓▒▒▓▓▓░                   ░▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▒▒▓▒▒▒▒▒▒▒▓ ▓▓▓▓▓▓▓▓▓           ▒▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▓▓▒▒▒▒▒▒▒░░░░░  ▓▓▓▓▓▓▓▓▓▓          ▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▒▒▒░▒░░░░░░    ▓▓▓▓▓▓▓▓▓▓          ▒▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▓▓▒▒▒░░░░░░░      ▓▓▓▓▓▓▓▓▓▓▓       ▒▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓▓▒▒▒▒░░░░░░░         ▓▓▓▓▓▓▓▓▓      ▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▓▓▒▒▒░░ ░░                ▓▓▓▓    ░▒▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒▒▒░░░                    ░▒▒▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░░▒▒▒░░░░░            ░░░░░░░▒▒▒▒▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒░░  ░▒▒░░░░░░            ░░░░░░░▒▒▒▓▓▓▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒░░   ░▒▒░░░░    ░    ░  ░░░░░░▒▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░     ░▒░░░░░░░ ░░░░░░░░░░░░▒▒▒▒▓▓▒▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░      ░▒░░░░░░░░░░░░░░░░▒▒▒▒▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░       ░░▒▒▒▒░▒░▒▒▒▒▒▒▒▒▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░         ░▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒░░         ░░▒▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/venus b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/venus new file mode 100644 index 00000000..b95aff1b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/data/memes/planets/venus @@ -0,0 +1,35 @@ +                          ▓   ▓  ░░        ░░░░ ▓  ▓                             +                           ▒   ░        ░░░░░░  ░   ▒                            +                  ▓ ▓▓ ░  ░      ░░░     ░  ░░   ░ ░ ░  ▒    ▓                   +                ▓   ░░░  ░      ░     ░ ░             ░░   ░                     +                 ▓ ░░░   ░      ░  ░ ░ ░         ░ ░  ▒ ░ ░ ░░   ▓▓              +              ▓    ░  ░░ ░       ░░   ░                        ░                 +             ▓░  ░░ ░ ░ ░  ░░  ░  ░ ░░  ░    ░  ░     ░          ▒               +          ▓▓ ░   ░  ░    ░  ░ ░    ░      ░ ░ ░    ░     ░ ░░ ░                  +          ▓  ░  ░  ░░  ░ ▒  ░ ░░░░        ▒  ░░ ░  ░░  ░░   ░         ▓          +           ░ ░  ░ ░░░   ░░  ░  ▒▒    ░  ░ ░   ░             ▒ ░  ░  ░            +        ▓     ░ ░ ░    ░       ░░      ░░▒     ░░▒▒          ░ ░  ░    ▒         +        ▓░   ░    ░  ░     ░  ░   ░   ░             ░  ░░       ▒  ░   ▓         +         ░     ░ ░    ░ ░  ░            ░░      ░  ░░░░░         ░               +        ░  ░     ░  ░░    ░ ░░   ░    ░ ░     ░░  ░ ▒  ░ ░  ░ ░  ░░    ░         +    ▓                   ░   ░ ▓░     ░           ░ ░         ░     ░  ░░         +    ▓ ▓░          ░  ░ ░░    ░  ░ ░  ░░░        ░░░             ░░               +       ▒     ░░ ░ ░  ░░  ░ ░ ░░           ░        ░    ░           ░ ░  ▓       +                  ░   ░░░░ ░  ░ ░  ░               ░                ░░   ▓       +        ░ ░      ░     ░    ░           ░     ░         ░           ░░           +     ▓ ▓     ░  ░ ░         ░            ░          ░      ░ ░          ▓        +         ░        ░    ░        ░    ░   ░         ░  ░                ▒         +        ▓ ░ ░  ░░   ░  ░    ░   ░  ░░   ░       ░                      ▓         +        ▓    ░  ░          ░   ░░ ░  ░░  ░ ░   ░░ ░              ░               +              ░  ░░     ░ ░░          ░   ░   ░░                ░   ░            +             ░    ░  ░  ░ ░    ░    ░ ░ ░░░░ ░                                   +             ░ ░       ░ ░      ░ ░░  ░   ░ ░   ░       ░░        ░▓             +              ▒░░       ░           ░        ░   ░   ░          ░░  ▓            +                ░                    ░░        ░    ░░ ░       ░                 +                  ▒ ░         ░        ░   ░      ░       ░  ░                   +                    ▒  ░ ░        ░    ░  ░     ░           ▓                    +                    ▓  ░░  ░        ░░  ░     ░  ░  ░   ░                        +                     ▓▓    ▒░      ░           ░   ░░   ▓                        +                              ▓  ░░░░       ░░   ▓                               + + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/doc/secret.txt b/config/neovim/store/lazy-plugins/telescope.nvim/doc/secret.txt new file mode 100644 index 00000000..e872ca45 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/doc/secret.txt @@ -0,0 +1,32 @@ +================================================================================ + *telescope.theprimeagen* + +To The Viewers: ~ + +Oh why hello, I didn't see you there. So nice of you to join us. The Primeagen +must have sent you here. + +The places you want to look for help are: (you can do `:help ` below) + - |telescope.nvim| + - |telescope.setup| + - |telescope.builtin| + - |telescope.layout| + - |telescope.actions| + +I hope you enjoy telescope & Neovim. May your programming always be fun and +your vimming be quick. + + + +To The Primeagen: ~ + +Cyrnfr ernq guvf uryc znahny orsber pnyyvat zr ng 3 NZ jvgu gryrfpbcr +rzretrapvrf. V xabj ynfg gvzr jr fnirq gur ragver fgernzvat vaqhfgel, ohg +V unir n lbhat fba jub xrrcf zr hc ng avtug nyy ol uvzfrys. OGJ, unir lbh +pbafvqrerq fraqvat culfvpny QIQf sbe znkvzhz dhnyvgl naq rneyl npprff gb arj +pbagrag? Vg frrzf yvxr vg pbhyq or n cerggl pbby vqrn. + +#FunzryrffFrysCebzbgvba: uggcf://tvguho.pbz/fcbafbef/gwqrievrf + + + vim:tw=78:ts=8:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/doc/tags b/config/neovim/store/lazy-plugins/telescope.nvim/doc/tags new file mode 100644 index 00000000..10539210 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/doc/tags @@ -0,0 +1,282 @@ +TelescopeLayout telescope.txt /*TelescopeLayout* +TelescopeLayout.config telescope.txt /*TelescopeLayout.config* +TelescopeWindow telescope.txt /*TelescopeWindow* +TelescopeWindow.config telescope.txt /*TelescopeWindow.config* +TelescopeWindowBorder telescope.txt /*TelescopeWindowBorder* +TelescopeWindowBorder.config telescope.txt /*TelescopeWindowBorder.config* +telescope.actions telescope.txt /*telescope.actions* +telescope.actions._close() telescope.txt /*telescope.actions._close()* +telescope.actions.add_selected_to_loclist() telescope.txt /*telescope.actions.add_selected_to_loclist()* +telescope.actions.add_selected_to_qflist() telescope.txt /*telescope.actions.add_selected_to_qflist()* +telescope.actions.add_selection() telescope.txt /*telescope.actions.add_selection()* +telescope.actions.add_to_loclist() telescope.txt /*telescope.actions.add_to_loclist()* +telescope.actions.add_to_qflist() telescope.txt /*telescope.actions.add_to_qflist()* +telescope.actions.center() telescope.txt /*telescope.actions.center()* +telescope.actions.close() telescope.txt /*telescope.actions.close()* +telescope.actions.complete_tag() telescope.txt /*telescope.actions.complete_tag()* +telescope.actions.cycle_history_next() telescope.txt /*telescope.actions.cycle_history_next()* +telescope.actions.cycle_history_prev() telescope.txt /*telescope.actions.cycle_history_prev()* +telescope.actions.cycle_previewers_next() telescope.txt /*telescope.actions.cycle_previewers_next()* +telescope.actions.cycle_previewers_prev() telescope.txt /*telescope.actions.cycle_previewers_prev()* +telescope.actions.delete_buffer() telescope.txt /*telescope.actions.delete_buffer()* +telescope.actions.delete_mark() telescope.txt /*telescope.actions.delete_mark()* +telescope.actions.drop_all() telescope.txt /*telescope.actions.drop_all()* +telescope.actions.edit_command_line() telescope.txt /*telescope.actions.edit_command_line()* +telescope.actions.edit_register() telescope.txt /*telescope.actions.edit_register()* +telescope.actions.edit_search_line() telescope.txt /*telescope.actions.edit_search_line()* +telescope.actions.file_edit() telescope.txt /*telescope.actions.file_edit()* +telescope.actions.file_split() telescope.txt /*telescope.actions.file_split()* +telescope.actions.file_tab() telescope.txt /*telescope.actions.file_tab()* +telescope.actions.file_vsplit() telescope.txt /*telescope.actions.file_vsplit()* +telescope.actions.generate telescope.txt /*telescope.actions.generate* +telescope.actions.generate.which_key() telescope.txt /*telescope.actions.generate.which_key()* +telescope.actions.git_apply_stash() telescope.txt /*telescope.actions.git_apply_stash()* +telescope.actions.git_checkout() telescope.txt /*telescope.actions.git_checkout()* +telescope.actions.git_checkout_current_buffer() telescope.txt /*telescope.actions.git_checkout_current_buffer()* +telescope.actions.git_create_branch() telescope.txt /*telescope.actions.git_create_branch()* +telescope.actions.git_delete_branch() telescope.txt /*telescope.actions.git_delete_branch()* +telescope.actions.git_merge_branch() telescope.txt /*telescope.actions.git_merge_branch()* +telescope.actions.git_rebase_branch() telescope.txt /*telescope.actions.git_rebase_branch()* +telescope.actions.git_rename_branch() telescope.txt /*telescope.actions.git_rename_branch()* +telescope.actions.git_reset_hard() telescope.txt /*telescope.actions.git_reset_hard()* +telescope.actions.git_reset_mixed() telescope.txt /*telescope.actions.git_reset_mixed()* +telescope.actions.git_reset_soft() telescope.txt /*telescope.actions.git_reset_soft()* +telescope.actions.git_staging_toggle() telescope.txt /*telescope.actions.git_staging_toggle()* +telescope.actions.git_switch_branch() telescope.txt /*telescope.actions.git_switch_branch()* +telescope.actions.git_track_branch() telescope.txt /*telescope.actions.git_track_branch()* +telescope.actions.history telescope.txt /*telescope.actions.history* +telescope.actions.history.History() telescope.txt /*telescope.actions.history.History()* +telescope.actions.history.History:append() telescope.txt /*telescope.actions.history.History:append()* +telescope.actions.history.History:get_next() telescope.txt /*telescope.actions.history.History:get_next()* +telescope.actions.history.History:get_prev() telescope.txt /*telescope.actions.history.History:get_prev()* +telescope.actions.history.History:new() telescope.txt /*telescope.actions.history.History:new()* +telescope.actions.history.History:reset() telescope.txt /*telescope.actions.history.History:reset()* +telescope.actions.history.get_simple_history() telescope.txt /*telescope.actions.history.get_simple_history()* +telescope.actions.history.new() telescope.txt /*telescope.actions.history.new()* +telescope.actions.insert_original_cword() telescope.txt /*telescope.actions.insert_original_cword()* +telescope.actions.insert_symbol() telescope.txt /*telescope.actions.insert_symbol()* +telescope.actions.insert_symbol_i() telescope.txt /*telescope.actions.insert_symbol_i()* +telescope.actions.layout telescope.txt /*telescope.actions.layout* +telescope.actions.layout.cycle_layout_next() telescope.txt /*telescope.actions.layout.cycle_layout_next()* +telescope.actions.layout.cycle_layout_prev() telescope.txt /*telescope.actions.layout.cycle_layout_prev()* +telescope.actions.layout.toggle_mirror() telescope.txt /*telescope.actions.layout.toggle_mirror()* +telescope.actions.layout.toggle_preview() telescope.txt /*telescope.actions.layout.toggle_preview()* +telescope.actions.layout.toggle_prompt_position() telescope.txt /*telescope.actions.layout.toggle_prompt_position()* +telescope.actions.move_selection_better() telescope.txt /*telescope.actions.move_selection_better()* +telescope.actions.move_selection_next() telescope.txt /*telescope.actions.move_selection_next()* +telescope.actions.move_selection_previous() telescope.txt /*telescope.actions.move_selection_previous()* +telescope.actions.move_selection_worse() telescope.txt /*telescope.actions.move_selection_worse()* +telescope.actions.move_to_bottom() telescope.txt /*telescope.actions.move_to_bottom()* +telescope.actions.move_to_middle() telescope.txt /*telescope.actions.move_to_middle()* +telescope.actions.move_to_top() telescope.txt /*telescope.actions.move_to_top()* +telescope.actions.open_loclist() telescope.txt /*telescope.actions.open_loclist()* +telescope.actions.open_qflist() telescope.txt /*telescope.actions.open_qflist()* +telescope.actions.paste_register() telescope.txt /*telescope.actions.paste_register()* +telescope.actions.preview_scrolling_down() telescope.txt /*telescope.actions.preview_scrolling_down()* +telescope.actions.preview_scrolling_left() telescope.txt /*telescope.actions.preview_scrolling_left()* +telescope.actions.preview_scrolling_right() telescope.txt /*telescope.actions.preview_scrolling_right()* +telescope.actions.preview_scrolling_up() telescope.txt /*telescope.actions.preview_scrolling_up()* +telescope.actions.remove_selected_picker() telescope.txt /*telescope.actions.remove_selected_picker()* +telescope.actions.remove_selection() telescope.txt /*telescope.actions.remove_selection()* +telescope.actions.results_scrolling_down() telescope.txt /*telescope.actions.results_scrolling_down()* +telescope.actions.results_scrolling_left() telescope.txt /*telescope.actions.results_scrolling_left()* +telescope.actions.results_scrolling_right() telescope.txt /*telescope.actions.results_scrolling_right()* +telescope.actions.results_scrolling_up() telescope.txt /*telescope.actions.results_scrolling_up()* +telescope.actions.select_all() telescope.txt /*telescope.actions.select_all()* +telescope.actions.select_default() telescope.txt /*telescope.actions.select_default()* +telescope.actions.select_drop() telescope.txt /*telescope.actions.select_drop()* +telescope.actions.select_horizontal() telescope.txt /*telescope.actions.select_horizontal()* +telescope.actions.select_tab() telescope.txt /*telescope.actions.select_tab()* +telescope.actions.select_tab_drop() telescope.txt /*telescope.actions.select_tab_drop()* +telescope.actions.select_vertical() telescope.txt /*telescope.actions.select_vertical()* +telescope.actions.send_selected_to_loclist() telescope.txt /*telescope.actions.send_selected_to_loclist()* +telescope.actions.send_selected_to_qflist() telescope.txt /*telescope.actions.send_selected_to_qflist()* +telescope.actions.send_to_loclist() telescope.txt /*telescope.actions.send_to_loclist()* +telescope.actions.send_to_qflist() telescope.txt /*telescope.actions.send_to_qflist()* +telescope.actions.set telescope.txt /*telescope.actions.set* +telescope.actions.set.edit() telescope.txt /*telescope.actions.set.edit()* +telescope.actions.set.scroll_horizontal_previewer() telescope.txt /*telescope.actions.set.scroll_horizontal_previewer()* +telescope.actions.set.scroll_horizontal_results() telescope.txt /*telescope.actions.set.scroll_horizontal_results()* +telescope.actions.set.scroll_previewer() telescope.txt /*telescope.actions.set.scroll_previewer()* +telescope.actions.set.scroll_results() telescope.txt /*telescope.actions.set.scroll_results()* +telescope.actions.set.select() telescope.txt /*telescope.actions.set.select()* +telescope.actions.set.shift_selection() telescope.txt /*telescope.actions.set.shift_selection()* +telescope.actions.set_command_line() telescope.txt /*telescope.actions.set_command_line()* +telescope.actions.set_search_line() telescope.txt /*telescope.actions.set_search_line()* +telescope.actions.smart_add_to_loclist() telescope.txt /*telescope.actions.smart_add_to_loclist()* +telescope.actions.smart_add_to_qflist() telescope.txt /*telescope.actions.smart_add_to_qflist()* +telescope.actions.smart_send_to_loclist() telescope.txt /*telescope.actions.smart_send_to_loclist()* +telescope.actions.smart_send_to_qflist() telescope.txt /*telescope.actions.smart_send_to_qflist()* +telescope.actions.state telescope.txt /*telescope.actions.state* +telescope.actions.state.get_current_line() telescope.txt /*telescope.actions.state.get_current_line()* +telescope.actions.state.get_current_picker() telescope.txt /*telescope.actions.state.get_current_picker()* +telescope.actions.state.get_selected_entry() telescope.txt /*telescope.actions.state.get_selected_entry()* +telescope.actions.to_fuzzy_refine() telescope.txt /*telescope.actions.to_fuzzy_refine()* +telescope.actions.toggle_all() telescope.txt /*telescope.actions.toggle_all()* +telescope.actions.toggle_selection() telescope.txt /*telescope.actions.toggle_selection()* +telescope.actions.utils telescope.txt /*telescope.actions.utils* +telescope.actions.utils.get_registered_mappings() telescope.txt /*telescope.actions.utils.get_registered_mappings()* +telescope.actions.utils.map_entries() telescope.txt /*telescope.actions.utils.map_entries()* +telescope.actions.utils.map_selections() telescope.txt /*telescope.actions.utils.map_selections()* +telescope.actions.which_key() telescope.txt /*telescope.actions.which_key()* +telescope.builtin telescope.txt /*telescope.builtin* +telescope.builtin.autocommands() telescope.txt /*telescope.builtin.autocommands()* +telescope.builtin.buffers() telescope.txt /*telescope.builtin.buffers()* +telescope.builtin.builtin() telescope.txt /*telescope.builtin.builtin()* +telescope.builtin.colorscheme() telescope.txt /*telescope.builtin.colorscheme()* +telescope.builtin.command_history() telescope.txt /*telescope.builtin.command_history()* +telescope.builtin.commands() telescope.txt /*telescope.builtin.commands()* +telescope.builtin.current_buffer_fuzzy_find() telescope.txt /*telescope.builtin.current_buffer_fuzzy_find()* +telescope.builtin.current_buffer_tags() telescope.txt /*telescope.builtin.current_buffer_tags()* +telescope.builtin.diagnostics() telescope.txt /*telescope.builtin.diagnostics()* +telescope.builtin.fd() telescope.txt /*telescope.builtin.fd()* +telescope.builtin.filetypes() telescope.txt /*telescope.builtin.filetypes()* +telescope.builtin.find_files() telescope.txt /*telescope.builtin.find_files()* +telescope.builtin.git_bcommits() telescope.txt /*telescope.builtin.git_bcommits()* +telescope.builtin.git_bcommits_range() telescope.txt /*telescope.builtin.git_bcommits_range()* +telescope.builtin.git_branches() telescope.txt /*telescope.builtin.git_branches()* +telescope.builtin.git_commits() telescope.txt /*telescope.builtin.git_commits()* +telescope.builtin.git_files() telescope.txt /*telescope.builtin.git_files()* +telescope.builtin.git_stash() telescope.txt /*telescope.builtin.git_stash()* +telescope.builtin.git_status() telescope.txt /*telescope.builtin.git_status()* +telescope.builtin.grep_string() telescope.txt /*telescope.builtin.grep_string()* +telescope.builtin.help_tags() telescope.txt /*telescope.builtin.help_tags()* +telescope.builtin.highlights() telescope.txt /*telescope.builtin.highlights()* +telescope.builtin.jumplist() telescope.txt /*telescope.builtin.jumplist()* +telescope.builtin.keymaps() telescope.txt /*telescope.builtin.keymaps()* +telescope.builtin.live_grep() telescope.txt /*telescope.builtin.live_grep()* +telescope.builtin.loclist() telescope.txt /*telescope.builtin.loclist()* +telescope.builtin.lsp_definitions() telescope.txt /*telescope.builtin.lsp_definitions()* +telescope.builtin.lsp_document_symbols() telescope.txt /*telescope.builtin.lsp_document_symbols()* +telescope.builtin.lsp_dynamic_workspace_symbols() telescope.txt /*telescope.builtin.lsp_dynamic_workspace_symbols()* +telescope.builtin.lsp_implementations() telescope.txt /*telescope.builtin.lsp_implementations()* +telescope.builtin.lsp_incoming_calls() telescope.txt /*telescope.builtin.lsp_incoming_calls()* +telescope.builtin.lsp_outgoing_calls() telescope.txt /*telescope.builtin.lsp_outgoing_calls()* +telescope.builtin.lsp_references() telescope.txt /*telescope.builtin.lsp_references()* +telescope.builtin.lsp_type_definitions() telescope.txt /*telescope.builtin.lsp_type_definitions()* +telescope.builtin.lsp_workspace_symbols() telescope.txt /*telescope.builtin.lsp_workspace_symbols()* +telescope.builtin.man_pages() telescope.txt /*telescope.builtin.man_pages()* +telescope.builtin.marks() telescope.txt /*telescope.builtin.marks()* +telescope.builtin.oldfiles() telescope.txt /*telescope.builtin.oldfiles()* +telescope.builtin.pickers() telescope.txt /*telescope.builtin.pickers()* +telescope.builtin.planets() telescope.txt /*telescope.builtin.planets()* +telescope.builtin.quickfix() telescope.txt /*telescope.builtin.quickfix()* +telescope.builtin.quickfixhistory() telescope.txt /*telescope.builtin.quickfixhistory()* +telescope.builtin.registers() telescope.txt /*telescope.builtin.registers()* +telescope.builtin.reloader() telescope.txt /*telescope.builtin.reloader()* +telescope.builtin.resume() telescope.txt /*telescope.builtin.resume()* +telescope.builtin.search_history() telescope.txt /*telescope.builtin.search_history()* +telescope.builtin.spell_suggest() telescope.txt /*telescope.builtin.spell_suggest()* +telescope.builtin.symbols() telescope.txt /*telescope.builtin.symbols()* +telescope.builtin.tags() telescope.txt /*telescope.builtin.tags()* +telescope.builtin.tagstack() telescope.txt /*telescope.builtin.tagstack()* +telescope.builtin.treesitter() telescope.txt /*telescope.builtin.treesitter()* +telescope.builtin.vim_options() telescope.txt /*telescope.builtin.vim_options()* +telescope.changelog telescope_changelog.txt /*telescope.changelog* +telescope.changelog-1406 telescope_changelog.txt /*telescope.changelog-1406* +telescope.changelog-1549 telescope_changelog.txt /*telescope.changelog-1549* +telescope.changelog-1553 telescope_changelog.txt /*telescope.changelog-1553* +telescope.changelog-1851 telescope_changelog.txt /*telescope.changelog-1851* +telescope.changelog-1866 telescope_changelog.txt /*telescope.changelog-1866* +telescope.changelog-1945 telescope_changelog.txt /*telescope.changelog-1945* +telescope.changelog-2499 telescope_changelog.txt /*telescope.changelog-2499* +telescope.changelog-2529 telescope_changelog.txt /*telescope.changelog-2529* +telescope.changelog-473 telescope_changelog.txt /*telescope.changelog-473* +telescope.changelog-839 telescope_changelog.txt /*telescope.changelog-839* +telescope.changelog-922 telescope_changelog.txt /*telescope.changelog-922* +telescope.command telescope.txt /*telescope.command* +telescope.defaults.border telescope.txt /*telescope.defaults.border* +telescope.defaults.borderchars telescope.txt /*telescope.defaults.borderchars* +telescope.defaults.buffer_previewer_maker telescope.txt /*telescope.defaults.buffer_previewer_maker* +telescope.defaults.cache_picker telescope.txt /*telescope.defaults.cache_picker* +telescope.defaults.color_devicons telescope.txt /*telescope.defaults.color_devicons* +telescope.defaults.create_layout telescope.txt /*telescope.defaults.create_layout* +telescope.defaults.cycle_layout_list telescope.txt /*telescope.defaults.cycle_layout_list* +telescope.defaults.default_mappings telescope.txt /*telescope.defaults.default_mappings* +telescope.defaults.dynamic_preview_title telescope.txt /*telescope.defaults.dynamic_preview_title* +telescope.defaults.entry_prefix telescope.txt /*telescope.defaults.entry_prefix* +telescope.defaults.file_ignore_patterns telescope.txt /*telescope.defaults.file_ignore_patterns* +telescope.defaults.file_previewer telescope.txt /*telescope.defaults.file_previewer* +telescope.defaults.file_sorter telescope.txt /*telescope.defaults.file_sorter* +telescope.defaults.generic_sorter telescope.txt /*telescope.defaults.generic_sorter* +telescope.defaults.get_selection_window telescope.txt /*telescope.defaults.get_selection_window* +telescope.defaults.get_status_text telescope.txt /*telescope.defaults.get_status_text* +telescope.defaults.git_worktrees telescope.txt /*telescope.defaults.git_worktrees* +telescope.defaults.grep_previewer telescope.txt /*telescope.defaults.grep_previewer* +telescope.defaults.history telescope.txt /*telescope.defaults.history* +telescope.defaults.hl_result_eol telescope.txt /*telescope.defaults.hl_result_eol* +telescope.defaults.initial_mode telescope.txt /*telescope.defaults.initial_mode* +telescope.defaults.layout_config telescope.txt /*telescope.defaults.layout_config* +telescope.defaults.layout_strategy telescope.txt /*telescope.defaults.layout_strategy* +telescope.defaults.mappings telescope.txt /*telescope.defaults.mappings* +telescope.defaults.multi_icon telescope.txt /*telescope.defaults.multi_icon* +telescope.defaults.path_display telescope.txt /*telescope.defaults.path_display* +telescope.defaults.prefilter_sorter telescope.txt /*telescope.defaults.prefilter_sorter* +telescope.defaults.preview telescope.txt /*telescope.defaults.preview* +telescope.defaults.prompt_prefix telescope.txt /*telescope.defaults.prompt_prefix* +telescope.defaults.prompt_title telescope.txt /*telescope.defaults.prompt_title* +telescope.defaults.qflist_previewer telescope.txt /*telescope.defaults.qflist_previewer* +telescope.defaults.results_title telescope.txt /*telescope.defaults.results_title* +telescope.defaults.scroll_strategy telescope.txt /*telescope.defaults.scroll_strategy* +telescope.defaults.selection_caret telescope.txt /*telescope.defaults.selection_caret* +telescope.defaults.selection_strategy telescope.txt /*telescope.defaults.selection_strategy* +telescope.defaults.set_env telescope.txt /*telescope.defaults.set_env* +telescope.defaults.sorting_strategy telescope.txt /*telescope.defaults.sorting_strategy* +telescope.defaults.tiebreak telescope.txt /*telescope.defaults.tiebreak* +telescope.defaults.use_less telescope.txt /*telescope.defaults.use_less* +telescope.defaults.vimgrep_arguments telescope.txt /*telescope.defaults.vimgrep_arguments* +telescope.defaults.winblend telescope.txt /*telescope.defaults.winblend* +telescope.defaults.wrap_results telescope.txt /*telescope.defaults.wrap_results* +telescope.extensions() telescope.txt /*telescope.extensions()* +telescope.layout telescope.txt /*telescope.layout* +telescope.layout.bottom_pane() telescope.txt /*telescope.layout.bottom_pane()* +telescope.layout.center() telescope.txt /*telescope.layout.center()* +telescope.layout.cursor() telescope.txt /*telescope.layout.cursor()* +telescope.layout.flex() telescope.txt /*telescope.layout.flex()* +telescope.layout.horizontal() telescope.txt /*telescope.layout.horizontal()* +telescope.layout.vertical() telescope.txt /*telescope.layout.vertical()* +telescope.load_extension() telescope.txt /*telescope.load_extension()* +telescope.make_entry telescope.txt /*telescope.make_entry* +telescope.mappings telescope.txt /*telescope.mappings* +telescope.nvim telescope.txt /*telescope.nvim* +telescope.pickers.entry_display telescope.txt /*telescope.pickers.entry_display* +telescope.pickers.layout telescope.txt /*telescope.pickers.layout* +telescope.pickers.layout:mount() telescope.txt /*telescope.pickers.layout:mount()* +telescope.pickers.layout:unmount() telescope.txt /*telescope.pickers.layout:unmount()* +telescope.pickers.layout:update() telescope.txt /*telescope.pickers.layout:update()* +telescope.previewers telescope.txt /*telescope.previewers* +telescope.previewers.Previewer() telescope.txt /*telescope.previewers.Previewer()* +telescope.previewers.buffer_previewer_maker() telescope.txt /*telescope.previewers.buffer_previewer_maker()* +telescope.previewers.cat() telescope.txt /*telescope.previewers.cat()* +telescope.previewers.display_content() telescope.txt /*telescope.previewers.display_content()* +telescope.previewers.git_branch_log() telescope.txt /*telescope.previewers.git_branch_log()* +telescope.previewers.git_commit_diff_as_was() telescope.txt /*telescope.previewers.git_commit_diff_as_was()* +telescope.previewers.git_commit_diff_to_head() telescope.txt /*telescope.previewers.git_commit_diff_to_head()* +telescope.previewers.git_commit_diff_to_parent() telescope.txt /*telescope.previewers.git_commit_diff_to_parent()* +telescope.previewers.git_commit_message() telescope.txt /*telescope.previewers.git_commit_message()* +telescope.previewers.git_file_diff() telescope.txt /*telescope.previewers.git_file_diff()* +telescope.previewers.git_stash_diff() telescope.txt /*telescope.previewers.git_stash_diff()* +telescope.previewers.new() telescope.txt /*telescope.previewers.new()* +telescope.previewers.new_buffer_previewer() telescope.txt /*telescope.previewers.new_buffer_previewer()* +telescope.previewers.new_termopen_previewer() telescope.txt /*telescope.previewers.new_termopen_previewer()* +telescope.previewers.qflist() telescope.txt /*telescope.previewers.qflist()* +telescope.previewers.vim_buffer_cat() telescope.txt /*telescope.previewers.vim_buffer_cat()* +telescope.previewers.vim_buffer_qflist() telescope.txt /*telescope.previewers.vim_buffer_qflist()* +telescope.previewers.vim_buffer_vimgrep() telescope.txt /*telescope.previewers.vim_buffer_vimgrep()* +telescope.previewers.vimgrep() telescope.txt /*telescope.previewers.vimgrep()* +telescope.register_extension() telescope.txt /*telescope.register_extension()* +telescope.resolve telescope.txt /*telescope.resolve* +telescope.resolve.resolve_anchor_pos() telescope.txt /*telescope.resolve.resolve_anchor_pos()* +telescope.resolve.resolve_height() telescope.txt /*telescope.resolve.resolve_height()* +telescope.resolve.resolve_width() telescope.txt /*telescope.resolve.resolve_width()* +telescope.setup() telescope.txt /*telescope.setup()* +telescope.themes telescope.txt /*telescope.themes* +telescope.themes.get_cursor() telescope.txt /*telescope.themes.get_cursor()* +telescope.themes.get_dropdown() telescope.txt /*telescope.themes.get_dropdown()* +telescope.themes.get_ivy() telescope.txt /*telescope.themes.get_ivy()* +telescope.theprimeagen secret.txt /*telescope.theprimeagen* +telescope.utils telescope.txt /*telescope.utils* +telescope.utils.has_ts_parser() telescope.txt /*telescope.utils.has_ts_parser()* +telescope.utils.notify() telescope.txt /*telescope.utils.notify()* +telescope.utils.path_expand() telescope.txt /*telescope.utils.path_expand()* +telescope.utils.transform_path() telescope.txt /*telescope.utils.transform_path()* diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/doc/telescope.txt b/config/neovim/store/lazy-plugins/telescope.nvim/doc/telescope.txt new file mode 100644 index 00000000..f63162d6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/doc/telescope.txt @@ -0,0 +1,4171 @@ +================================================================================ +INTRODUCTION *telescope.nvim* + +Telescope.nvim is a plugin for fuzzy finding and neovim. It helps you search, +filter, find and pick things in Lua. + +Getting started with telescope: + 1. Run `:checkhealth telescope` to make sure everything is installed. + 2. Evaluate it is working with `:Telescope find_files` or `:lua + require("telescope.builtin").find_files()` + 3. Put a `require("telescope").setup()` call somewhere in your neovim config. + 4. Read |telescope.setup| to check what config keys are available and what + you can put inside the setup call + 5. Read |telescope.builtin| to check which builtin pickers are offered and + what options these implement + 6. Profit + +The below flow chart illustrates a simplified telescope architecture: +┌───────────────────────────────────────────────────────────┐ +│ ┌────────┐ │ +│ │ Multi │ ┌───────+ │ +│ │ Select │ ┌───────┐ │ Entry │ │ +│ └─────┬──* │ Entry │ ┌────────+ │ Maker │ │ +│ │ ┌───│Manager│────│ Sorter │┐ └───┬───* │ +│ ▼ ▼ └───────* └────────┘│ │ │ +│ 1────────┐ 2───┴──┐ │ │ +│ ┌─────│ Picker │ │Finder│◀────┘ │ +│ ▼ └───┬────┘ └──────* │ +│ ┌────────┐ │ 3────────+ ▲ │ +│ │Selected│ └───────│ Prompt │─────────┘ │ +│ │ Entry │ └───┬────┘ │ +│ └────────* ┌───┴────┐ ┌────────┐ ┌────────┐ │ +│ │ ▲ 4─────────┐│ Prompt │ │(Attach)│ │Actions │ │ +│ ▼ └──▶ │ Results ││ Buffer │◀─┤Mappings│◀─┤User Fn │ │ +│5─────────┐ └─────────┘└────────┘ └────────┘ └────────┘ │ +││Previewer│ │ +│└─────────┘ telescope.nvim architecture │ +└───────────────────────────────────────────────────────────┘ + + + The `Entry Maker` at least defines + - value: "raw" result of the finder + - ordinal: string to be sorted derived from value + - display: line representation of entry in results buffer + + * The finder, entry manager, selected entry, and multi selections + comprises `entries` constructed by the `Entry Maker` from + raw results of the finder (`value`s) + + Primary components: + 1 Picker: central UI dedicated to varying use cases + (finding files, grepping, diagnostics, etc.) + see :h telescope.builtin + 2 Finder: pipe or interactively generates results to pick over + 3 Prompt: user input that triggers the finder which sorts results + in order into the entry manager + 4 Results: listed entries scored by sorter from finder results + 5 Previewer: preview of context of selected entry + see :h telescope.previewers + +A practical introduction into telescope customization is our `developers.md` +(top-level of repo) and `:h telescope.actions` that showcase how to access +information about the state of the picker (current selection, etc.). +To find out more: +https://github.com/nvim-telescope/telescope.nvim + + :h telescope.setup + :h telescope.command + :h telescope.builtin + :h telescope.themes + :h telescope.layout + :h telescope.resolve + :h telescope.actions + :h telescope.actions.state + :h telescope.actions.set + :h telescope.actions.utils + :h telescope.actions.generate + :h telescope.actions.history + :h telescope.previewers + +telescope.setup({opts}) *telescope.setup()* + Setup function to be run by user. Configures the defaults, pickers and + extensions of telescope. + + Usage: + > + require('telescope').setup{ + defaults = { + -- Default configuration for telescope goes here: + -- config_key = value, + -- .. + }, + pickers = { + -- Default configuration for builtin pickers goes here: + -- picker_name = { + -- picker_config_key = value, + -- ... + -- } + -- Now the picker_config_key will be applied every time you call this + -- builtin picker + }, + extensions = { + -- Your extension configuration goes here: + -- extension_name = { + -- extension_config_key = value, + -- } + -- please take a look at the readme of the extension you want to configure + } + } +< + + + Valid keys for {opts.defaults} + + *telescope.defaults.sorting_strategy* + sorting_strategy: ~ + Determines the direction "better" results are sorted towards. + + Available options are: + - "descending" (default) + - "ascending" + + *telescope.defaults.selection_strategy* + selection_strategy: ~ + Determines how the cursor acts after each sort iteration. + + Available options are: + - "reset" (default) + - "follow" + - "row" + - "closest" + - "none" + + *telescope.defaults.scroll_strategy* + scroll_strategy: ~ + Determines what happens if you try to scroll past the view of the + picker. + + Available options are: + - "cycle" (default) + - "limit" + + *telescope.defaults.layout_strategy* + layout_strategy: ~ + Determines the default layout of Telescope pickers. + See |telescope.layout| for details of the available strategies. + + Default: 'horizontal' + + *telescope.defaults.create_layout* + create_layout: ~ + Configure the layout of Telescope pickers. + See |telescope.pickers.layout| for details. + + Default: 'nil' + + *telescope.defaults.layout_config* + layout_config: ~ + Determines the default configuration values for layout strategies. + See |telescope.layout| for details of the configurations options for + each strategy. + + Allows setting defaults for all strategies as top level options and + for overriding for specific options. + For example, the default values below set the default width to 80% of + the screen width for all strategies except 'center', which has width + of 50% of the screen width. + + Default: { + bottom_pane = { + height = 25, + preview_cutoff = 120, + prompt_position = "top" + }, + center = { + height = 0.4, + preview_cutoff = 40, + prompt_position = "top", + width = 0.5 + }, + cursor = { + height = 0.9, + preview_cutoff = 40, + width = 0.8 + }, + horizontal = { + height = 0.9, + preview_cutoff = 120, + prompt_position = "bottom", + width = 0.8 + }, + vertical = { + height = 0.9, + preview_cutoff = 40, + prompt_position = "bottom", + width = 0.8 + } + } + + + *telescope.defaults.cycle_layout_list* + cycle_layout_list: ~ + Determines the layouts to cycle through when using `actions.layout.cycle_layout_next` + and `actions.layout.cycle_layout_prev`. + Should be a list of "layout setups". + Each "layout setup" can take one of two forms: + 1. string + This is interpreted as the name of a `layout_strategy` + 2. table + A table with possible keys `layout_strategy`, `layout_config` and `previewer` + + Default: { "horizontal", "vertical" } + + + *telescope.defaults.winblend* + winblend: ~ + Configure winblend for telescope floating windows. See |winblend| for + more information. Type can be a number or a function returning a + number + + Default: function() return vim.o.winblend end + + *telescope.defaults.wrap_results* + wrap_results: ~ + Word wrap the search results + + Default: false + + *telescope.defaults.prompt_prefix* + prompt_prefix: ~ + The character(s) that will be shown in front of Telescope's prompt. + + Default: '> ' + + *telescope.defaults.selection_caret* + selection_caret: ~ + The character(s) that will be shown in front of the current selection. + + Default: '> ' + + *telescope.defaults.entry_prefix* + entry_prefix: ~ + Prefix in front of each result entry. Current selection not included. + + Default: ' ' + + *telescope.defaults.multi_icon* + multi_icon: ~ + Symbol to add in front of a multi-selected result entry. + Replaces final character of |telescope.defaults.selection_caret| and + |telescope.defaults.entry_prefix| as appropriate. + To have no icon, set to the empty string. + + Default: '+' + + *telescope.defaults.initial_mode* + initial_mode: ~ + Determines in which mode telescope starts. Valid Keys: + `insert` and `normal`. + + Default: "insert" + + *telescope.defaults.border* + border: ~ + Boolean defining if borders are added to Telescope windows. + + Default: true + + *telescope.defaults.path_display* + path_display: ~ + Determines how file paths are displayed. + + path_display can be set to an array with a combination of: + - "hidden" hide file names + - "tail" only display the file name, and not the path + - "absolute" display absolute paths + - "smart" remove as much from the path as possible to only show + the difference between the displayed paths. + Warning: The nature of the algorithm might have a negative + performance impact! + - "shorten" only display the first character of each directory in + the path + - "truncate" truncates the start of the path when the whole path will + not fit. To increase the gap between the path and the edge, + set truncate to number `truncate = 3` + - "filename_first" shows filenames first and then the directories + + You can also specify the number of characters of each directory name + to keep by setting `path_display.shorten = num`. + e.g. for a path like + `alpha/beta/gamma/delta.txt` + setting `path_display.shorten = 1` will give a path like: + `a/b/g/delta.txt` + Similarly, `path_display.shorten = 2` will give a path like: + `al/be/ga/delta.txt` + + You can also further customise the shortening behaviour by + setting `path_display.shorten = { len = num, exclude = list }`, + where `len` acts as above, and `exclude` is a list of positions + that are not shortened. Negative numbers in the list are considered + relative to the end of the path. + e.g. for a path like + `alpha/beta/gamma/delta.txt` + setting `path_display.shorten = { len = 1, exclude = {1, -1} }` + will give a path like: + `alpha/b/g/delta.txt` + setting `path_display.shorten = { len = 2, exclude = {2, -2} }` + will give a path like: + `al/beta/gamma/de` + + path_display can also be set to 'filename_first' to put the filename + in front. + + path_display = { + "filename_first" + }, + + The directory structure can be reversed as follows: + + path_display = { + filename_first = { + reverse_directories = true + } + }, + + path_display can also be set to 'hidden' string to hide file names + + path_display can also be set to a function for custom formatting of + the path display with the following signature + + Signature: fun(opts: table, path: string): string, table? + + The optional table is an list of positions and highlight groups to + set the highlighting of the return path string. + + Example: + + -- Format path as "file.txt (path\to\file\)" + path_display = function(opts, path) + local tail = require("telescope.utils").path_tail(path) + return string.format("%s (%s)", tail, path) + end, + + -- Format path and add custom highlighting + path_display = function(opts, path) + local tail = require("telescope.utils").path_tail(path) + path = string.format("%s (%s)", tail, path) + + local highlights = { + { + { + 0, -- highlight start position + #path, -- highlight end position + }, + "Comment", -- highlight group name + }, + } + + return path, highlights + end + + Default: {} + + *telescope.defaults.borderchars* + borderchars: ~ + Set the borderchars of telescope floating windows. It has to be a + table of 8 string values. + + Default: { "─", "│", "─", "│", "╭", "╮", "╯", "╰" } + + *telescope.defaults.get_status_text* + get_status_text: ~ + A function that determines what the virtual text looks like. + Signature: function(picker) -> str + + Default: function that shows current count / all + + *telescope.defaults.hl_result_eol* + hl_result_eol: ~ + Changes if the highlight for the selected item in the results + window is always the full width of the window + + Default: true + + *telescope.defaults.dynamic_preview_title* + dynamic_preview_title: ~ + Will change the title of the preview window dynamically, where it + is supported. For example, the preview window's title could show up as + the full filename. + + Default: false + + *telescope.defaults.results_title* + results_title: ~ + Defines the default title of the results window. A false value + can be used to hide the title altogether. + + Default: "Results" + + *telescope.defaults.prompt_title* + prompt_title: ~ + Defines the default title of the prompt window. A false value + can be used to hide the title altogether. Most of the times builtins + define a prompt_title which will be preferred over this default. + + Default: "Prompt" + + *telescope.defaults.mappings* + mappings: ~ + Your mappings to override telescope's default mappings. + + See: ~ + |telescope.mappings| + + + *telescope.defaults.default_mappings* + default_mappings: ~ + Not recommended to use except for advanced users. + + Will allow you to completely remove all of telescope's default maps + and use your own. + + Default: nil + + + *telescope.defaults.history* + history: ~ + This field handles the configuration for prompt history. + By default it is a table, with default values (more below). + To disable history, set it to false. + + Currently mappings still need to be added, Example: + mappings = { + i = { + [""] = require('telescope.actions').cycle_history_next, + [""] = require('telescope.actions').cycle_history_prev, + }, + }, + + Fields: + - path: The path to the telescope history as string. + Default: stdpath("data")/telescope_history + - limit: The amount of entries that will be written in the + history. + Warning: If limit is set to nil it will grow unbound. + Default: 100 + - handler: A lua function that implements the history. + This is meant as a developer setting for extensions to + override the history handling, e.g., + https://github.com/nvim-telescope/telescope-smart-history.nvim, + which allows context sensitive (cwd + picker) history. + + Default: + require('telescope.actions.history').get_simple_history + - cycle_wrap: Indicates whether the cycle_history_next and + cycle_history_prev functions should wrap around to the + beginning or end of the history entries on reaching + their respective ends + Default: false + + *telescope.defaults.cache_picker* + cache_picker: ~ + This field handles the configuration for picker caching. + By default it is a table, with default values (more below). + To disable caching, set it to false. + + Caching preserves all previous multi selections and results and + therefore may result in slowdown or increased RAM occupation + if too many pickers (`cache_picker.num_pickers`) or entries + ('cache_picker.limit_entries`) are cached. + + Fields: + - num_pickers: The number of pickers to be cached. + Set to -1 to preserve all pickers of your + session. If passed to a picker, the cached + pickers with indices larger than + `cache_picker.num_pickers` will be cleared. + Default: 1 + - limit_entries: The amount of entries that will be saved for + each picker. + Default: 1000 + - ignore_empty_prompt: If true, the picker will not be cached if + the prompt is empty (i.e., no text has been + typed at the time of closing the prompt). + Default: false + + + *telescope.defaults.preview* + preview: ~ + This field handles the global configuration for previewers. + By default it is a table, with default values (more below). + To disable previewing, set it to false. If you have disabled previewers + globally, but want to opt in to previewing for single pickers, you will have to + pass `preview = true` or `preview = {...}` (your config) to the `opts` of + your picker. + + Fields: + - check_mime_type: Use `file` if available to try to infer whether the + file to preview is a binary if filetype + detection fails. + Windows users get `file` from: + https://github.com/julian-r/file-windows + Set to false to attempt to preview any mime type. + Default: true for all OS excl. Windows + - filesize_limit: The maximum file size in MB attempted to be previewed. + Set to false to attempt to preview any file size. + Default: 25 + - highlight_limit: The maximum file size in MB attempted to be highlighted. + Set to false to attempt to highlight any file size. + Default: 1 + - timeout: Timeout the previewer if the preview did not + complete within `timeout` milliseconds. + Set to false to not timeout preview. + Default: 250 + - hook(s): Function(s) that takes `(filepath, bufnr, opts)`, where opts + exposes winid and ft (filetype). + Available hooks (in order of priority): + {filetype, mime, filesize, timeout}_hook + Important: the filetype_hook must return true or false + to indicate whether to continue (true) previewing or not (false), + respectively. + Two examples: + local putils = require("telescope.previewers.utils") + ... -- preview is called in telescope.setup { ... } + preview = { + -- 1) Do not show previewer for certain files + filetype_hook = function(filepath, bufnr, opts) + -- you could analogously check opts.ft for filetypes + local excluded = vim.tbl_filter(function(ending) + return filepath:match(ending) + end, { + ".*%.csv", + ".*%.toml", + }) + if not vim.tbl_isempty(excluded) then + putils.set_preview_message( + bufnr, + opts.winid, + string.format("I don't like %s files!", + excluded[1]:sub(5, -1)) + ) + return false + end + return true + end, + -- 2) Truncate lines to preview window for too large files + filesize_hook = function(filepath, bufnr, opts) + local path = require("plenary.path"):new(filepath) + -- opts exposes winid + local height = vim.api.nvim_win_get_height(opts.winid) + local lines = vim.split(path:head(height), "[\r]?\n") + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + end, + } + The configuration recipes for relevant examples. + Note: we use vim.filetype filetype detection, + so if you have troubles with files not + highlighting correctly, please read + |vim.filetype| + Default: nil + - treesitter: Determines whether the previewer performs treesitter + highlighting, which falls back to regex-based highlighting. + `true`: treesitter highlighting for all available filetypes + `false`: regex-based highlighting for all filetypes + `table`: may contain the following keys: + - enable boolean|table: if boolean, enable ts + highlighting for all supported + filetypes. + if table, ts highlighting is only + enabled for given filetypes. + - disable table: list of filetypes for which ts highlighting + is not used if `enable = true`. + Default: true + - msg_bg_fillchar: Character to fill background of unpreviewable buffers with + Default: "╱" + - hide_on_startup: Hide previewer when picker starts. Previewer can be toggled + with actions.layout.toggle_preview. + Default: false + - ls_short: Determines whether to use the `--short` flag for the `ls` + command when previewing directories. Otherwise will result + to using `--long`. + Default: false + + + *telescope.defaults.vimgrep_arguments* + vimgrep_arguments: ~ + Defines the command that will be used for `live_grep` and `grep_string` + pickers. + Hint: Make sure that color is currently set to `never` because we do + not yet interpret color codes + Hint 2: Make sure that these options are in your changes arguments: + "--no-heading", "--with-filename", "--line-number", "--column" + because we need them so the ripgrep output is in the correct format. + + Default: { + "rg", + "--color=never", + "--no-heading", + "--with-filename", + "--line-number", + "--column", + "--smart-case" + } + + *telescope.defaults.use_less* + use_less: ~ + Boolean if less should be enabled in term_previewer (deprecated and + currently no longer used in the builtin pickers). + + Default: true + + *telescope.defaults.set_env* + set_env: ~ + Set an environment for term_previewer. A table of key values: + Example: { COLORTERM = "truecolor", ... } + Hint: Empty table is not allowed. + + Default: nil + + *telescope.defaults.color_devicons* + color_devicons: ~ + Boolean if devicons should be enabled or not. If set to false, the + text highlight group is used. + Hint: Coloring only works if |termguicolors| is enabled. + + Default: true + + *telescope.defaults.file_sorter* + file_sorter: ~ + A function pointer that specifies the file_sorter. This sorter will + be used for find_files, git_files and similar. + Hint: If you load a native sorter, you don't need to change this value, + the native sorter will override it anyway. + + Default: require("telescope.sorters").get_fzy_sorter + + *telescope.defaults.generic_sorter* + generic_sorter: ~ + A function pointer to the generic sorter. The sorter that should be + used for everything that is not a file. + Hint: If you load a native sorter, you don't need to change this value, + the native sorter will override it anyway. + + Default: require("telescope.sorters").get_fzy_sorter + + *telescope.defaults.prefilter_sorter* + prefilter_sorter: ~ + This points to a wrapper sorter around the generic_sorter that is able + to do prefiltering. + It's usually used for lsp_*_symbols and lsp_*_diagnostics + + Default: require("telescope.sorters").prefilter + + *telescope.defaults.tiebreak* + tiebreak: ~ + A function that determines how to break a tie when two entries have + the same score. + Having a function that always returns false would keep the entries in + the order they are found, so existing_entry before current_entry. + Vice versa always returning true would place the current_entry + before the existing_entry. + + Signature: function(current_entry, existing_entry, prompt) -> boolean + + Default: function that breaks the tie based on the length of the + entry's ordinal + + *telescope.defaults.file_ignore_patterns* + file_ignore_patterns: ~ + A table of lua regex that define the files that should be ignored. + Example: { "^scratch/" } -- ignore all files in scratch directory + Example: { "%.npz" } -- ignore all npz files + See: https://www.lua.org/manual/5.1/manual.html#5.4.1 for more + information about lua regex + Note: `file_ignore_patterns` will be used in all pickers that have a + file associated. This might lead to the problem that lsp_ pickers + aren't displaying results because they might be ignored by + `file_ignore_patterns`. For example, setting up node_modules as ignored + will never show node_modules in any results, even if you are + interested in lsp_ results. + + If you only want `file_ignore_patterns` for `find_files` and + `grep_string`/`live_grep` it is suggested that you setup `gitignore` + and have fd and or ripgrep installed because both tools will not show + `gitignore`d files on default. + + Default: nil + + *telescope.defaults.get_selection_window* + get_selection_window: ~ + Function that takes function(picker, entry) and returns a window id. + The window ID will be used to decide what window the chosen file will + be opened in and the cursor placed in upon leaving the picker. + + Default: `function() return 0 end` + + + *telescope.defaults.git_worktrees* + git_worktrees: ~ + A table of arrays of detached working trees with keys `gitdir` and `toplevel`. + Used to pass `--git-dir` and `--work-tree` flags to git commands when telescope fails + to infer the top-level directory of a given working tree based on cwd. + Example: + git_worktrees = { + { + toplevel = vim.env.HOME, + gitdir = vim.env.HOME .. '/.cfg' + } + } + + Default: nil + + + *telescope.defaults.file_previewer* + file_previewer: ~ + Function pointer to the default file_previewer. It is mostly used + for find_files, git_files and similar. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").cat.new + + Default: require("telescope.previewers").vim_buffer_cat.new + + *telescope.defaults.grep_previewer* + grep_previewer: ~ + Function pointer to the default vim_grep previewer. It is mostly + used for live_grep, grep_string and similar. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").vimgrep.new + + Default: require("telescope.previewers").vim_buffer_vimgrep.new + + *telescope.defaults.qflist_previewer* + qflist_previewer: ~ + Function pointer to the default qflist previewer. It is mostly + used for qflist, loclist and lsp. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").qflist.new + + Default: require("telescope.previewers").vim_buffer_qflist.new + + *telescope.defaults.buffer_previewer_maker* + buffer_previewer_maker: ~ + Developer option that defines the underlining functionality + of the buffer previewer. + For interesting configuration examples take a look at + https://github.com/nvim-telescope/telescope.nvim/wiki/Configuration-Recipes + + Default: require("telescope.previewers").buffer_previewer_maker + + Parameters: ~ + {opts} (table) Configuration opts. Keys: defaults, pickers, + extensions + + +telescope.load_extension({name}) *telescope.load_extension()* + Load an extension. + - Notes: + - Loading triggers ext setup via the config passed in |telescope.setup| + + + Parameters: ~ + {name} (string) Name of the extension + + +telescope.register_extension({mod}) *telescope.register_extension()* + Register an extension. To be used by plugin authors. + + + Parameters: ~ + {mod} (table) Module + + +telescope.extensions() *telescope.extensions()* + Use telescope.extensions to reference any extensions within your + configuration. + While the docs currently generate this as a function, it's actually a + table. Sorry. + + + + +================================================================================ +COMMAND *telescope.command* + +Telescope commands can be called through two apis, the lua api and the viml +api. + +The lua api is the more direct way to interact with Telescope, as you directly +call the lua functions that Telescope defines. It can be called in a lua file +using commands like: +`require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})` +If you want to use this api from a vim file you should prepend `lua` to the +command, as below: +`lua require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})` +If you want to use this api from a neovim command line you should prepend +`:lua` to the command, as below: +`:lua require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})` + +The viml api is more indirect, as first the command must be parsed to the +relevant lua equivalent, which brings some limitations. The viml api can be +called using commands like: +`:Telescope find_files hidden=true layout_config={"prompt_position":"top"}` +This involves setting options using an `=` and using viml syntax for lists and +dictionaries when the corresponding lua function requires a table. + +One limitation of the viml api is that there can be no spaces in any of the +options. For example, if you want to use the `cwd` option for `find_files` to +specify that you only want to search within the folder `/foo bar/subfolder/` +you could not do that using the viml api, as the path name contains a space. +Similarly, you could NOT set the `prompt_position` to `"top"` using the +following command: +`:Telescope find_files layout_config={ "prompt_position" : "top" }` +as there are spaces in the option. + + + +================================================================================ +BUILTIN *telescope.builtin* + +Telescope Builtins is a collection of community maintained pickers to support +common workflows. It can be used as reference when writing PRs, Telescope +extensions, your own custom pickers, or just as a discovery tool for all of the +amazing pickers already shipped with Telescope! + +Any of these functions can just be called directly by doing: + +:lua require('telescope.builtin').$NAME_OF_PICKER() + +To use any of Telescope's default options or any picker-specific options, call +your desired picker by passing a lua table to the picker with all of the +options you want to use. Here's an example with the live_grep picker: + +> + :lua require('telescope.builtin').live_grep({ + prompt_title = 'find string in open buffers...', + grep_open_files = true + }) + -- or with dropdown theme + :lua require('telescope.builtin').find_files(require('telescope.themes').get_dropdown{ + previewer = false + }) +< + +builtin.live_grep({opts}) *telescope.builtin.live_grep()* + Search for a string and get results live as you type, respects .gitignore + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from + (default: cwd, use + utils.buffer_dir() to search + relative to open buffer) + {grep_open_files} (boolean) if true, restrict search to + open files only, mutually + exclusive with `search_dirs` + {search_dirs} (table) directory/directories/files to + search, mutually exclusive + with `grep_open_files` + {glob_pattern} (string|table) argument to be used with + `--glob`, e.g. "*.toml", can + use the opposite "!*.toml" + {type_filter} (string) argument to be used with + `--type`, e.g. "rust", see `rg + --type-list` + {additional_args} (function|table) additional arguments to be + passed on. Can be fn(opts) -> + tbl + {max_results} (number) define a upper result value + {disable_coordinates} (boolean) don't show the line & row + numbers (default: false) + {file_encoding} (string) file encoding for the entry & + previewer + + +builtin.grep_string({opts}) *telescope.builtin.grep_string()* + Searches for the string under your cursor or the visual selection in your + current working directory + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from + (default: cwd, use + utils.buffer_dir() to search + relative to open buffer) + {search} (string) the query to search + {grep_open_files} (boolean) if true, restrict search to + open files only, mutually + exclusive with `search_dirs` + {search_dirs} (table) directory/directories/files to + search, mutually exclusive + with `grep_open_files` + {use_regex} (boolean) if true, special characters + won't be escaped, allows for + using regex (default: false) + {word_match} (string) can be set to `-w` to enable + exact word matches + {additional_args} (function|table) additional arguments to be + passed on. Can be fn(opts) -> + tbl + {disable_coordinates} (boolean) don't show the line and row + numbers (default: false) + {only_sort_text} (boolean) only sort the text, not the + file, line or row (default: + false) + {file_encoding} (string) file encoding for the entry & + previewer + + +builtin.find_files({opts}) *telescope.builtin.find_files()* + Search for files (respecting .gitignore) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from + (default: cwd, use + utils.buffer_dir() to search + relative to open buffer) + {find_command} (function|table) cmd to use for the search. Can + be a fn(opts) -> tbl (default: + autodetect) + {file_entry_encoding} (string) encoding of output of + `find_command` + {follow} (boolean) if true, follows symlinks + (i.e. uses `-L` flag for the + `find` command) + {hidden} (boolean) determines whether to show + hidden files or not (default: + false) + {no_ignore} (boolean) show files ignored by + .gitignore, .ignore, etc. + (default: false) + {no_ignore_parent} (boolean) show files ignored by + .gitignore, .ignore, etc. in + parent dirs. (default: false) + {search_dirs} (table) directory/directories/files to + search + {search_file} (string) specify a filename to search + for + {file_encoding} (string) file encoding for the + previewer + + +builtin.fd() *telescope.builtin.fd()* + This is an alias for the `find_files` picker + + + +builtin.treesitter() *telescope.builtin.treesitter()* + Lists function names, variables, and other symbols from treesitter queries + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by kind of ts + node you want to see (i.e. `:var:`) + + + Options: ~ + {show_line} (boolean) if true, shows the row:column that + the result is found at (default: + true) + {bufnr} (number) specify the buffer number where + treesitter should run. (default: + current buffer) + {symbol_width} (number) defines the width of the symbol + section (default: 25) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.current_buffer_fuzzy_find({opts}) *telescope.builtin.current_buffer_fuzzy_find()* + Live fuzzy search inside of the currently open buffer + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {skip_empty_lines} (boolean) if true we don't display empty lines + (default: false) + {results_ts_highlight} (boolean) highlight result entries with + treesitter (default: true) + {file_encoding} (string) file encoding for the previewer + + +builtin.tags({opts}) *telescope.builtin.tags()* + Lists tags in current directory with tag location file preview (users are + required to run ctags -R to generate tags or update when introducing new + changes) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from (default: cwd, use + utils.buffer_dir() to search relative to + open buffer) + {ctags_file} (string) specify a particular ctags file to use + {show_line} (boolean) if true, shows the content of the line the + tag is found on in the picker (default: + true) + {only_sort_tags} (boolean) if true we will only sort tags (default: + false) + {fname_width} (number) defines the width of the filename section + (default: 30) + + +builtin.current_buffer_tags({opts}) *telescope.builtin.current_buffer_tags()* + Lists all of the tags for the currently open buffer, with a preview + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from (default: cwd, use + utils.buffer_dir() to search relative to + open buffer) + {ctags_file} (string) specify a particular ctags file to use + {show_line} (boolean) if true, shows the content of the line the + tag is found on in the picker (default: + true) + {only_sort_tags} (boolean) if true we will only sort tags (default: + false) + {fname_width} (number) defines the width of the filename section + (default: 30) + + +builtin.git_files({opts}) *telescope.builtin.git_files()* + Fuzzy search for files tracked by Git. This command lists the output of the + `git ls-files` command, respects .gitignore + - Default keymaps: + - ``: opens the currently selected file + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer + git root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or + the cwd (important for submodule) + (default: true) + {show_untracked} (boolean) if true, adds `--others` flag to + command and shows untracked files + (default: false) + {recurse_submodules} (boolean) if true, adds the + `--recurse-submodules` flag to command + (default: false) + {git_command} (table) command that will be executed. + {"git","ls-files","--exclude-standard","--cached"} + {file_encoding} (string) file encoding for the previewer + + +builtin.git_commits({opts}) *telescope.builtin.git_commits()* + Lists commits for current directory with diff preview + - Default keymaps: + - ``: checks out the currently selected commit + - `m`: resets current branch to selected commit using mixed mode + - `s`: resets current branch to selected commit using soft mode + - `h`: resets current branch to selected commit using hard mode + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {git_command} (table) command that will be executed. + {"git","log","--pretty=oneline","--abbrev-commit","--","."} + + +builtin.git_bcommits({opts}) *telescope.builtin.git_bcommits()* + Lists commits for current buffer with diff preview + - Default keymaps or your overridden `select_` keys: + - ``: checks out the currently selected commit + - ``: opens a diff in a vertical split + - ``: opens a diff in a horizontal split + - ``: opens a diff in a new tab + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {current_file} (string) specify the current file that should be + used for bcommits (default: current buffer) + {git_command} (table) command that will be executed. + {"git","log","--pretty=oneline","--abbrev-commit"} + + +builtin.git_bcommits_range({opts}) *telescope.builtin.git_bcommits_range()* + Lists commits for a range of lines in the current buffer with diff preview + In visual mode, lists commits for the selected lines With operator mode + enabled, lists commits inside the text object/motion + - Default keymaps or your overridden `select_` keys: + - ``: checks out the currently selected commit + - ``: opens a diff in a vertical split + - ``: opens a diff in a horizontal split + - ``: opens a diff in a new tab + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {current_file} (string) specify the current file that should be used + for bcommits (default: current buffer) + {git_command} (table) command that will be executed. the last + element must be "-L". + {"git","log","--pretty=oneline","--abbrev-commit","--no-patch","-L"} + {from} (number) the first line number in the range (default: + current line) + {to} (number) the last line number in the range (default: + the value of `from`) + {operator} (boolean) select lines in operator-pending mode + (default: false) + + +builtin.git_branches({opts}) *telescope.builtin.git_branches()* + List branches for current directory, with output from `git log --oneline` + shown in the preview window + - Default keymaps: + - ``: checks out the currently selected branch + - ``: tracks currently selected branch + - ``: rebases currently selected branch + - ``: creates a new branch, with confirmation prompt before creation + - ``: deletes the currently selected branch, with confirmation + prompt before deletion + - ``: merges the currently selected branch, with confirmation prompt + before deletion + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the + repo + {use_file_path} (boolean) if we should use the + current buffer git root + (default: false) + {use_git_root} (boolean) if we should use git root + as cwd or the cwd + (important for submodule) + (default: true) + {show_remote_tracking_branches} (boolean) show remote tracking + branches like origin/main + (default: true) + {pattern} (string) specify the pattern to + match all refs + + +builtin.git_status({opts}) *telescope.builtin.git_status()* + Lists git status for current directory + - Default keymaps: + - ``: stages or unstages the currently selected file + - ``: opens the currently selected file + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {git_icons} (table) string -> string. Matches name with icon + (see source code, make_entry.lua + git_icon_defaults) + {expand_dir} (boolean) pass flag `-uall` to show files in + untracked directories (default: true) + + +builtin.git_stash({opts}) *telescope.builtin.git_stash()* + Lists stash items in current repository + - Default keymaps: + - ``: runs `git apply` for currently selected stash + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {show_branch} (boolean) if we should display the branch name for + git stash entries (default: true) + + +builtin.builtin({opts}) *telescope.builtin.builtin()* + Lists all of the community maintained pickers built into Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {include_extensions} (boolean) if true will show the pickers of the + installed extensions (default: false) + {use_default_opts} (boolean) if the selected picker should use its + default options (default: false) + + +builtin.resume({opts}) *telescope.builtin.resume()* + Opens the previous picker in the identical state (incl. multi selections) + - Notes: + - Requires `cache_picker` in setup or when having invoked pickers, see + |telescope.defaults.cache_picker| + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cache_index} (number) what picker to resume, where 1 denotes most + recent (default: 1) + + +builtin.pickers({opts}) *telescope.builtin.pickers()* + Opens a picker over previously cached pickers in their preserved states + (incl. multi selections) + - Default keymaps: + - ``: delete the selected cached picker + - Notes: + - Requires `cache_picker` in setup or when having invoked pickers, see + |telescope.defaults.cache_picker| + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.planets({opts}) *telescope.builtin.planets()* + Use the telescope... + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_pluto} (boolean) we love Pluto (default: false, because its a + hidden feature) + {show_moon} (boolean) we love the Moon (default: false, because its + a hidden feature) + + +builtin.symbols({opts}) *telescope.builtin.symbols()* + Lists symbols inside of `data/telescope-sources/*.json` found in your + runtime path or found in `stdpath("data")/telescope/symbols/*.json`. The + second path can be customized. We provide a couple of default symbols which + can be found in https://github.com/nvim-telescope/telescope-symbols.nvim. + This repos README also provides more information about the format in which + the symbols have to be. + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {symbol_path} (string) specify the second path. Default: + `stdpath("data")/telescope/symbols/*.json` + {sources} (table) specify a table of sources you want to load + this time + + +builtin.commands({opts}) *telescope.builtin.commands()* + Lists available plugin/user commands and runs them on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_buf_command} (boolean) show buf local command (Default: true) + + +builtin.quickfix({opts}) *telescope.builtin.quickfix()* + Lists items in the quickfix list, jumps to location on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {nr} (number) specify the quickfix list number + + +builtin.quickfixhistory({opts}) *telescope.builtin.quickfixhistory()* + Lists all quickfix lists in your history and open them with + `builtin.quickfix`. It seems that neovim only keeps the full history for 10 + lists + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.loclist({opts}) *telescope.builtin.loclist()* + Lists items from the current window's location list, jumps to location on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + + +builtin.oldfiles({opts}) *telescope.builtin.oldfiles()* + Lists previously open files, opens on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify a working directory to filter + oldfiles by + {only_cwd} (boolean) show only files in the cwd (default: false) + {cwd_only} (boolean) alias for only_cwd + {file_encoding} (string) file encoding for the previewer + + +builtin.command_history({opts}) *telescope.builtin.command_history()* + Lists commands that were executed recently, and reruns them on `` + - Default keymaps: + - ``: open the command line with the text of the currently selected + result populated in it + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {filter_fn} (function) filter fn(cmd:string). true if the history + command should be presented. + + +builtin.search_history({opts}) *telescope.builtin.search_history()* + Lists searches that were executed recently, and reruns them on `` + - Default keymaps: + - ``: open a search window with the text of the currently selected + search result populated in it + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.vim_options({opts}) *telescope.builtin.vim_options()* + Lists vim options, allows you to edit the current value on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.help_tags({opts}) *telescope.builtin.help_tags()* + Lists available help tags and opens a new window with the relevant help + info on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {lang} (string) specify language (default: vim.o.helplang) + {fallback} (boolean) fallback to en if language isn't installed + (default: true) + + +builtin.man_pages({opts}) *telescope.builtin.man_pages()* + Lists manpage entries, opens them in a help window on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {sections} (table) a list of sections to search, use `{ "ALL" }` + to search in all sections (default: { "1" }) + {man_cmd} (function) that returns the man command. (Default: + `apropos ""` on linux, `apropos " "` on macos) + + +builtin.reloader({opts}) *telescope.builtin.reloader()* + Lists lua modules and reloads them on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {column_len} (number) define the max column len for the module name + (default: dynamic, longest module name) + + +builtin.buffers({opts}) *telescope.builtin.buffers()* + Lists open buffers in current neovim instance, opens selected buffer on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify a working directory to + filter buffers list by + {show_all_buffers} (boolean) if true, show all buffers, + including unloaded buffers + (default: true) + {ignore_current_buffer} (boolean) if true, don't show the current + buffer in the list (default: + false) + {only_cwd} (boolean) if true, only show buffers in the + current working directory + (default: false) + {cwd_only} (boolean) alias for only_cwd + {sort_lastused} (boolean) Sorts current and last buffer to + the top and selects the lastused + (default: false) + {sort_mru} (boolean) Sorts all buffers after most + recent used. Not just the current + and last one (default: false) + {bufnr_width} (number) Defines the width of the buffer + numbers in front of the filenames + (default: dynamic) + {file_encoding} (string) file encoding for the previewer + {sort_buffers} (function) sort fn(bufnr_a, bufnr_b). true if + bufnr_a should go first. Runs + after sorting by most recent (if + specified) + {select_current} (boolean) select current buffer (default: + false) + + +builtin.colorscheme({opts}) *telescope.builtin.colorscheme()* + Lists available colorschemes and applies them on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {colors} (table) a list of additional colorschemes to + explicitly make available to telescope + (default: {}) + {enable_preview} (boolean) if true, will preview the selected color + + +builtin.marks({opts}) *telescope.builtin.marks()* + Lists vim marks and their value, jumps to the mark on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {file_encoding} (string) file encoding for the previewer + {mark_type} (string) filter marks by type (default: "all", + options: "all"|"global"|"local") + + +builtin.registers({opts}) *telescope.builtin.registers()* + Lists vim registers, pastes the contents of the register on `` + - Default keymaps: + - ``: edit the contents of the currently selected register + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.keymaps({opts}) *telescope.builtin.keymaps()* + Lists normal mode keymappings, runs the selected keymap on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {modes} (table) a list of short-named keymap modes to search + (default: { "n", "i", "c", "x" }) + {show_plug} (boolean) if true, the keymaps for which the lhs + contains "" are also shown (default: + true) + {only_buf} (boolean) if true, only show the buffer-local keymaps + (default: false) + {lhs_filter} (function) filter(lhs:string) -> boolean. true for + keymap.lhs if the keymap should be shown + (optional) + {filter} (function) filter(km:keymap) -> boolean. true for the + keymap if it should be shown (optional) + + +builtin.filetypes({opts}) *telescope.builtin.filetypes()* + Lists all available filetypes, sets currently open buffer's filetype to + selected filetype in Telescope on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.highlights({opts}) *telescope.builtin.highlights()* + Lists all available highlights + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.autocommands({opts}) *telescope.builtin.autocommands()* + Lists vim autocommands and goes to their declaration on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.spell_suggest({opts}) *telescope.builtin.spell_suggest()* + Lists spelling suggestions for the current word under the cursor, replaces + word with selected suggestion on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.tagstack({opts}) *telescope.builtin.tagstack()* + Lists the tag stack for the current window, jumps to tag on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + + +builtin.jumplist({opts}) *telescope.builtin.jumplist()* + Lists items from Vim's jumplist, jumps to location on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + + +builtin.lsp_references({opts}) *telescope.builtin.lsp_references()* + Lists LSP references for word under the cursor, jumps to reference on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {include_declaration} (boolean) include symbol declaration in the + lsp references (default: true) + {include_current_line} (boolean) include current line (default: + false) + {jump_type} (string) how to goto reference if there is + only one and the definition file is + different from the current file, + values: "tab", "tab drop", "split", + "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_incoming_calls({opts}) *telescope.builtin.lsp_incoming_calls()* + Lists LSP incoming calls for word under the cursor, jumps to reference on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_outgoing_calls({opts}) *telescope.builtin.lsp_outgoing_calls()* + Lists LSP outgoing calls for word under the cursor, jumps to reference on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_definitions({opts}) *telescope.builtin.lsp_definitions()* + Goto the definition of the word under the cursor, if there's only one, + otherwise show all options in Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {jump_type} (string) how to goto definition if there is only one + and the definition file is different from + the current file, values: "tab", "tab + drop", "split", "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {reuse_win} (boolean) jump to existing window if buffer is + already opened (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_type_definitions({opts}) *telescope.builtin.lsp_type_definitions()* + Goto the definition of the type of the word under the cursor, if there's + only one, otherwise show all options in Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {jump_type} (string) how to goto definition if there is only one + and the definition file is different from + the current file, values: "tab", "tab + drop", "split", "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {reuse_win} (boolean) jump to existing window if buffer is + already opened (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_implementations({opts}) *telescope.builtin.lsp_implementations()* + Goto the implementation of the word under the cursor if there's only one, + otherwise show all options in Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {jump_type} (string) how to goto implementation if there is only + one and the definition file is different + from the current file, values: "tab", "tab + drop", "split", "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {reuse_win} (boolean) jump to existing window if buffer is + already opened (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_document_symbols({opts}) *telescope.builtin.lsp_document_symbols()* + Lists LSP document symbols in the current buffer + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by type of + symbol you want to see (i.e. `:variable:`) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {fname_width} (number) defines the width of the filename + section (default: 30) + {symbol_width} (number) defines the width of the symbol + section (default: 25) + {symbol_type_width} (number) defines the width of the symbol + type section (default: 8) + {show_line} (boolean) if true, shows the content of the + line the tag is found on (default: + false) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_workspace_symbols({opts}) *telescope.builtin.lsp_workspace_symbols()* + Lists LSP document symbols in the current workspace + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by type of + symbol you want to see (i.e. `:variable:`) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {query} (string) for what to query the workspace + (default: "") + {fname_width} (number) defines the width of the filename + section (default: 30) + {symbol_width} (number) defines the width of the symbol + section (default: 25) + {symbol_type_width} (number) defines the width of the symbol + type section (default: 8) + {show_line} (boolean) if true, shows the content of the + line the tag is found on (default: + false) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_dynamic_workspace_symbols({opts}) *telescope.builtin.lsp_dynamic_workspace_symbols()* + Dynamically lists LSP for all workspace symbols + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by type of + symbol you want to see (i.e. `:variable:`), only works after refining + to fuzzy search using + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {fname_width} (number) defines the width of the filename + section (default: 30) + {show_line} (boolean) if true, shows the content of the + line the symbol is found on + (default: false) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.diagnostics({opts}) *telescope.builtin.diagnostics()* + Lists diagnostics + - Fields: + - `All severity flags can be passed as `string` or `number` as per + `:vim.diagnostic.severity:` + - Default keymaps: + - ``: show autocompletion menu to prefilter your query with the + diagnostic you want to see (i.e. `:warning:`) + - sort_by option: + - "buffer": order by bufnr (prioritizing current bufnr), severity, lnum + - "severity": order by severity, bufnr (prioritizing current bufnr), lnum + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {bufnr} (number|nil) Buffer number to get + diagnostics from. Use 0 for + current buffer or nil for all + buffers + {severity} (string|number) filter diagnostics by severity + name (string) or id (number) + {severity_limit} (string|number) keep diagnostics equal or more + severe wrt severity name + (string) or id (number) + {severity_bound} (string|number) keep diagnostics equal or less + severe wrt severity name + (string) or id (number) + {root_dir} (string|boolean) if set to string, get + diagnostics only for buffers + under this dir otherwise cwd + {no_unlisted} (boolean) if true, get diagnostics only + for listed buffers + {no_sign} (boolean) hide DiagnosticSigns from + Results (default: false) + {line_width} (string|number) set length of diagnostic entry + text in Results. Use 'full' + for full untruncated text + {namespace} (number) limit your diagnostics to a + specific namespace + {disable_coordinates} (boolean) don't show the line & row + numbers (default: false) + {sort_by} (string) sort order of the diagnostics + results; see above notes + (default: "buffer") + + + +================================================================================ +THEMES *telescope.themes* + +Themes are ways to combine several elements of styling together. + +They are helpful for managing the several different UI aspects for telescope +and provide a simple interface for users to get a particular "style" of picker. + +themes.get_dropdown() *telescope.themes.get_dropdown()* + Dropdown style theme. + + Usage: + > + local opts = {...} -- picker options + local builtin = require('telescope.builtin') + local themes = require('telescope.themes') + builtin.find_files(themes.get_dropdown(opts)) +< + + + +themes.get_cursor() *telescope.themes.get_cursor()* + Cursor style theme. + + Usage: + > + local opts = {...} -- picker options + local builtin = require('telescope.builtin') + local themes = require('telescope.themes') + builtin.find_files(themes.get_cursor(opts)) +< + + + +themes.get_ivy() *telescope.themes.get_ivy()* + Ivy style theme. + + Usage: + > + local opts = {...} -- picker options + local builtin = require('telescope.builtin') + local themes = require('telescope.themes') + builtin.find_files(themes.get_ivy(opts)) +< + + + + +================================================================================ +MAPPINGS *telescope.mappings* + +|telescope.mappings| is used to configure the keybindings within a telescope +picker. These key binds are only local to the picker window and will be cleared +once you exit the picker. + +We provide multiple configuration options to make it easy for you to adjust +telescope's default key bindings and create your own custom key binds. + +To see many of the builtin actions that you can use as values for this table, +see |telescope.actions| + +Format is: +> + { + mode = { ..keys } + } +< + +where {mode} is the one character letter for a mode ('i' for insert, 'n' for +normal). + +For example: +> + mappings = { + i = { + [""] = require('telescope.actions').close, + }, + } +< + +To disable a keymap, put `[map] = false` +For example: +> + { + ..., + [""] = false, + ..., + } +< + +To override behavior of a key, simply set the value to be a function (either by +requiring an action or by writing your own function) +> + { + ..., + [""] = require('telescope.actions').select_default, + ..., + } +< + +If the function you want is part of `telescope.actions`, then you can simply +supply the function name as a string. For example, the previous option is +equivalent to: +> + { + ..., + [""] = "select_default", + ..., + } +< + +You can also add other mappings using tables with `type = "command"`. For +example: +> + { + ..., + ["jj"] = { "", type = "command" }, + ["kk"] = { "echo \"Hello, World!\"", type = "command" },) + ..., + } +< + +You can also add additional options for mappings of any type ("action" and +"command"). For example: +> + { + ..., + [""] = { + actions.move_selection_next, type = "action", + opts = { nowait = true, silent = true } + }, + ..., + } +< + +There are three main places you can configure |telescope.mappings|. These are +ordered from the lowest priority to the highest priority. + +1. |telescope.defaults.mappings| +2. In the |telescope.setup()| table, inside a picker with a given name, use the + `mappings` key +> + require("telescope").setup { + pickers = { + find_files = { + mappings = { + n = { + ["kj"] = "close", + }, + }, + }, + }, + } +< +3. `attach_mappings` function for a particular picker. +> + require("telescope.builtin").find_files { + attach_mappings = function(_, map) + map("i", "asdf", function(_prompt_bufnr) + print "You typed asdf" + end) + + map({"i", "n"}, "", function(_prompt_bufnr) + print "You typed " + end, { desc = "desc for which key"}) + + -- needs to return true if you want to map default_mappings and + -- false if not + return true + end, + } +< + + +================================================================================ +LAYOUT *telescope.pickers.layout* + +The telescope pickers layout can be configured using the +|telescope.defaults.create_layout| option. + +Parameters: ~ + - picker : A Picker object. + +Return: ~ + - layout : instance of `TelescopeLayout` class. + +Example: ~ +> +local Layout = require "telescope.pickers.layout" + +require("telescope").setup { + create_layout = function(picker) + local function create_window(enter, width, height, row, col, title) + local bufnr = vim.api.nvim_create_buf(false, true) + local winid = vim.api.nvim_open_win(bufnr, enter, { + style = "minimal", + relative = "editor", + width = width, + height = height, + row = row, + col = col, + border = "single", + title = title, + }) + + vim.wo[winid].winhighlight = "Normal:Normal" + + return Layout.Window { + bufnr = bufnr, + winid = winid, + } + end + + local function destory_window(window) + if window then + if vim.api.nvim_win_is_valid(window.winid) then + vim.api.nvim_win_close(window.winid, true) + end + if vim.api.nvim_buf_is_valid(window.bufnr) then + vim.api.nvim_buf_delete(window.bufnr, { force = true }) + end + end + end + + local layout = Layout { + picker = picker, + mount = function(self) + self.results = create_window(false, 40, 20, 0, 0, "Results") + self.preview = create_window(false, 40, 23, 0, 42, "Preview") + self.prompt = create_window(true, 40, 1, 22, 0, "Prompt") + end, + unmount = function(self) + destory_window(self.results) + destory_window(self.preview) + destory_window(self.prompt) + end, + update = function(self) end, + } + + return layout + end, +} +< + +TelescopeWindowBorder.config *TelescopeWindowBorder.config* + + + Fields: ~ + {bufnr} (integer) + {winid} (integer|nil) + {change_title} (nil|function) (self: TelescopeWindowBorder, title: + string, pos?: + "NW"|"N"|"NE"|"SW"|"S"|"SE"):nil + + +TelescopeWindowBorder *TelescopeWindowBorder* + + + Fields: ~ + {bufnr} (integer|nil) + {winid} (integer|nil) + + +TelescopeWindow.config *TelescopeWindow.config* + + + Fields: ~ + {bufnr} (integer) + {winid} (integer|nil) + {border} (TelescopeWindowBorder.config|nil) + + +TelescopeWindow *TelescopeWindow* + + + Fields: ~ + {border} (TelescopeWindowBorder) + {bufnr} (integer) + {winid} (integer) + + +TelescopeLayout.config *TelescopeLayout.config* + + + Fields: ~ + {mount} (function) (self: TelescopeLayout):nil + {unmount} (function) (self: TelescopeLayout):nil + {update} (function) (self: TelescopeLayout):nil + {prompt} (TelescopeWindow|nil) + {results} (TelescopeWindow|nil) + {preview} (TelescopeWindow|nil) + + +TelescopeLayout *TelescopeLayout* + + + Fields: ~ + {prompt} (TelescopeWindow) + {results} (TelescopeWindow) + {preview} (TelescopeWindow|nil) + + +Layout:mount() *telescope.pickers.layout:mount()* + Create the layout. This needs to ensure the required properties are + populated. + + + +Layout:unmount() *telescope.pickers.layout:unmount()* + Destroy the layout. This is responsible for performing clean-up, for + example: + - deleting buffers + - closing windows + - clearing autocmds + + + +Layout:update() *telescope.pickers.layout:update()* + Refresh the layout. This is called when, for example, vim is resized. + + + + +================================================================================ +LAYOUT *telescope.layout* + +The layout of telescope pickers can be adjusted using the +|telescope.defaults.layout_strategy| and |telescope.defaults.layout_config| +options. For example, the following configuration changes the default layout +strategy and the default size of the picker: +> + require('telescope').setup{ + defaults = { + layout_strategy = 'vertical', + layout_config = { height = 0.95 }, + }, + } +< + + +──────────────────────────────────────────────────────────────────────────────── + +Layout strategies are different functions to position telescope. + +All layout strategies are functions with the following signature: + +> + function(picker, columns, lines, layout_config) + -- Do some calculations here... + return { + preview = preview_configuration + results = results_configuration, + prompt = prompt_configuration, + } + end +< + + Parameters: ~ + - picker : A Picker object. (docs coming soon) + - columns : (number) Columns in the vim window + - lines : (number) Lines in the vim window + - layout_config : (table) The configuration values specific to the picker. + +This means you can create your own layout strategy if you want! Just be aware +for now that we may change some APIs or interfaces, so they may break if you +create your own. + +A good method for creating your own would be to copy one of the strategies that +most resembles what you want from +"./lua/telescope/pickers/layout_strategies.lua" in the telescope repo. + + +layout_strategies.horizontal() *telescope.layout.horizontal()* + Horizontal layout has two columns, one for the preview and one for the + prompt and results. + + ┌──────────────────────────────────────────────────┐ + │ │ + │ ┌───────────────────┐┌───────────────────┐ │ + │ │ ││ │ │ + │ │ ││ │ │ + │ │ ││ │ │ + │ │ Results ││ │ │ + │ │ ││ Preview │ │ + │ │ ││ │ │ + │ │ ││ │ │ + │ └───────────────────┘│ │ │ + │ ┌───────────────────┐│ │ │ + │ │ Prompt ││ │ │ + │ └───────────────────┘└───────────────────┘ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When columns are less than this value, the preview will be disabled + - preview_width: + - Change the width of Telescope's preview window + - See |resolver.resolve_width()| + + +layout_strategies.center() *telescope.layout.center()* + Centered layout with a combined block of the prompt and results aligned to + the middle of the screen. The preview window is then placed in the + remaining space above or below, according to `anchor` or `mirror`. + Particularly useful for creating dropdown menus (see |telescope.themes| and + |themes.get_dropdown()|). + + Note that vertical anchoring, i.e. `anchor` containing `"N"` or `"S"`, will + override `mirror` config. For `"N"` anchoring preview will be placed below + prompt/result block. For `"S"` anchoring preview will be placed above + prompt/result block. For horizontal only anchoring preview will be placed + according to `mirror` config, default is above the prompt/result block. + + ┌──────────────────────────────────────────────────┐ + │ ┌────────────────────────────────────────┐ │ + │ │ Preview │ │ + │ │ Preview │ │ + │ └────────────────────────────────────────┘ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Prompt │ │ + │ ├────────────────────────────────────────┤ │ + │ │ Result │ │ + │ │ Result │ │ + │ └────────────────────────────────────────┘ │ + │ │ + │ │ + │ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When lines are less than this value, the preview will be disabled + + +layout_strategies.cursor() *telescope.layout.cursor()* + Cursor layout dynamically positioned below the cursor if possible. If there + is no place below the cursor it will be placed above. + + ┌──────────────────────────────────────────────────┐ + │ │ + │ █ │ + │ ┌──────────────┐┌─────────────────────┐ │ + │ │ Prompt ││ Preview │ │ + │ ├──────────────┤│ Preview │ │ + │ │ Result ││ Preview │ │ + │ │ Result ││ Preview │ │ + │ └──────────────┘└─────────────────────┘ │ + │ █ │ + │ │ + │ │ + │ │ + │ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When columns are less than this value, the preview will be disabled + - preview_width: + - Change the width of Telescope's preview window + - See |resolver.resolve_width()| + + +layout_strategies.vertical() *telescope.layout.vertical()* + Vertical layout stacks the items on top of each other. Particularly useful + with thinner windows. + + ┌──────────────────────────────────────────────────┐ + │ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Preview │ │ + │ │ Preview │ │ + │ │ Preview │ │ + │ └────────────────────────────────────────┘ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Result │ │ + │ │ Result │ │ + │ └────────────────────────────────────────┘ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Prompt │ │ + │ └────────────────────────────────────────┘ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When lines are less than this value, the preview will be disabled + - preview_height: + - Change the height of Telescope's preview window + - See |resolver.resolve_height()| + + +layout_strategies.flex() *telescope.layout.flex()* + Flex layout swaps between `horizontal` and `vertical` strategies based on + the window width + - Supports |layout_strategies.vertical| or |layout_strategies.horizontal| + features + + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - flip_columns: The number of columns required to move to horizontal mode + - flip_lines: The number of lines required to move to horizontal mode + - horizontal: Options to pass when switching to horizontal layout + - vertical: Options to pass when switching to vertical layout + + +layout_strategies.bottom_pane() *telescope.layout.bottom_pane()* + Bottom pane can be used to create layouts similar to "ivy". + + For an easy ivy configuration, see |themes.get_ivy()| + + + + +================================================================================ +RESOLVE *telescope.resolve* + +Provides "resolver functions" to allow more customisable inputs for options. + +resolver.resolve_height() *telescope.resolve.resolve_height()* + Converts input to a function that returns the height. The input must take + one of five forms: + 1. 0 <= number < 1 + This means total height as a percentage. + 2. 1 <= number + This means total height as a fixed number. + 3. function + Must have signature: function(self, max_columns, max_lines): number + 4. table of the form: { val, max = ..., min = ... } + val has to be in the first form 0 <= val < 1 and only one is given, + `min` or `max` as fixed number + 5. table of the form: {padding = `foo`} + where `foo` has one of the previous three forms. + The height is then set to be the remaining space after padding. For + example, if the window has height 50, and the input is {padding = 5}, + the height returned will be `40 = 50 - 2*5` + + The returned function will have signature: function(self, max_columns, + max_lines): number + + + +resolver.resolve_width() *telescope.resolve.resolve_width()* + Converts input to a function that returns the width. The input must take + one of five forms: + 1. 0 <= number < 1 + This means total width as a percentage. + 2. 1 <= number + This means total width as a fixed number. + 3. function + Must have signature: function(self, max_columns, max_lines): number + 4. table of the form: { val, max = ..., min = ... } + val has to be in the first form 0 <= val < 1 and only one is given, + `min` or `max` as fixed number + 5. table of the form: {padding = `foo`} + where `foo` has one of the previous three forms. + The width is then set to be the remaining space after padding. For + example, if the window has width 100, and the input is {padding = 5}, + the width returned will be `90 = 100 - 2*5` + + The returned function will have signature: function(self, max_columns, + max_lines): number + + + +resolver.resolve_anchor_pos() *telescope.resolve.resolve_anchor_pos()* + Calculates the adjustment required to move the picker from the middle of + the screen to an edge or corner. + The `anchor` can be any of the following strings: + - "", "CENTER", "NW", "N", "NE", "E", "SE", "S", "SW", "W" The anchors + have the following meanings: + - "" or "CENTER": + the picker will remain in the middle of the screen. + - Compass directions: + the picker will move to the corresponding edge/corner e.g. "NW" -> "top + left corner", "E" -> "right edge", "S" -> "bottom edge" + + + + +================================================================================ +MAKE_ENTRY *telescope.make_entry* + +Each picker has a finder made up of two parts, the results which are the data +to be displayed, and the entry_maker. These entry_makers are functions returned +from make_entry functions. These will be referred to as entry_makers in the +following documentation. + +Every entry maker returns a function that accepts the data to be used for an +entry. This function will return an entry table (or nil, meaning skip this +entry) which contains the following important keys: +- value any: value key can be anything but still required +- valid bool (optional): is an optional key because it defaults to true but if + the key is set to false it will not be displayed by the picker +- ordinal string: is the text that is used for filtering +- display string|function: is either a string of the text that is being + displayed or a function receiving the entry at a later stage, when the entry + is actually being displayed. A function can be useful here if a complex + calculation has to be done. `make_entry` can also return a second value - a + highlight array which will then apply to the line. Highlight entry in this + array has the following signature `{ { start_col, end_col }, hl_group }` +- filename string (optional): will be interpreted by the default `` action + as open this file +- bufnr number (optional): will be interpreted by the default `` action as + open this buffer +- lnum number (optional): lnum value which will be interpreted by the default + `` action as a jump to this line +- col number (optional): col value which will be interpreted by the default + `` action as a jump to this column + +For more information on easier displaying, see +|telescope.pickers.entry_display| + +TODO: Document something we call `entry_index` + + +================================================================================ +ENTRY_DISPLAY *telescope.pickers.entry_display* + +Entry Display is used to format each entry shown in the result panel. + +Entry Display create() will give us a function based on the configuration of +column widths we pass into it. We then can use this function n times to return +a string based on structured input. + +Note that if you call `create()` inside `make_display` it will be called for +every single entry. So it is suggested to do this outside of `make_display` for +the best performance. + +The create function will use the column widths passed to it in +configuration.items. Each item in that table is the number of characters in the +column. It's also possible for the final column to not have a fixed width, this +will be shown in the configuration as 'remaining = true'. + +An example of this configuration is shown for the buffers picker: +> +local displayer = entry_display.create { + separator = " ", + items = { + { width = opts.bufnr_width }, + { width = 4 }, + { width = icon_width }, + { remaining = true }, + }, +} +< + +This shows 4 columns, the first is defined in the opts as the width we'll use +when display_string is the number of the buffer. The second has a fixed width +of 4 and the third column's width will be decided by the width of the icons we +use. The fourth column will use the remaining space. Finally, we have also +defined the separator between each column will be the space " ". + +An example of how the display reference will be used is shown, again for the +buffers picker: +> +return displayer { + { entry.bufnr, "TelescopeResultsNumber" }, + { entry.indicator, "TelescopeResultsComment" }, + { icon, hl_group }, + display_bufname .. ":" .. entry.lnum, +} +< + +There are two types of values each column can have. Either a simple String or a +table containing the String as well as the hl_group. + +The displayer can return values, string and an optional highlights. The string +is all the text to be displayed for this entry as a single string. If parts of +the string are to be highlighted they will be described in the highlights +table. + +For a better understanding of how create() and displayer are used it's best to +look at the code in make_entry.lua. + + +================================================================================ +UTILS *telescope.utils* + +Utilities for writing telescope pickers + +utils.path_expand({path}) *telescope.utils.path_expand()* + Hybrid of `vim.fn.expand()` and custom `vim.fs.normalize()` + + Paths starting with '%', '#' or '<' are expanded with `vim.fn.expand()`. + Otherwise avoids using `vim.fn.expand()` due to its overly aggressive + expansion behavior which can sometimes lead to errors or the creation of + non-existent paths when dealing with valid absolute paths. + + Other paths will have '~' and environment variables expanded. Unlike + `vim.fs.normalize()`, backslashes are preserved. This has better + compatibility with `plenary.path` and also avoids mangling valid Unix paths + with literal backslashes. + + Trailing slashes are trimmed. With the exception of root paths. eg. `/` on + Unix or `C:\` on Windows + + + + Parameters: ~ + {path} (string) + + Return: ~ + string + + +utils.transform_path({opts}, {path}) *telescope.utils.transform_path()* + Transform path is a util function that formats a path based on path_display + found in `opts` or the default value from config. It is meant to be used in + make_entry to have a uniform interface for builtins as well as extensions + utilizing the same user configuration Note: It is only supported inside + `make_entry`/`make_display` the use of this function outside of telescope + might yield to undefined behavior and will not be addressed by us + + + Parameters: ~ + {opts} (table) The opts the users passed into the picker. Might + contains a path_display key + {path} (string|nil) The path that should be formatted + + Return: ~ + string: path to be displayed + table: The transformed path ready to be displayed with the styling + + +utils.has_ts_parser({lang}) *telescope.utils.has_ts_parser()* + Checks if treesitter parser for language is installed + + + Parameters: ~ + {lang} (string) + + +utils.notify({funname}, {opts}) *telescope.utils.notify()* + Telescope Wrapper around vim.notify + + + Parameters: ~ + {funname} (string) name of the function that will be + {opts} (table) opts.level string, opts.msg string, opts.once bool + + + +================================================================================ +ACTIONS *telescope.actions* + +These functions are useful for people creating their own mappings. + +Actions can be either normal functions that expect the `prompt_bufnr` as first +argument (1) or they can be a custom telescope type called "action" (2). + +(1) The `prompt_bufnr` of a normal function denotes the identifier of your +picker which can be used to access the picker state. In practice, users most +commonly access from both picker and global state via the following: +> + -- for utility functions + local action_state = require "telescope.actions.state" + + local actions = {} + actions.do_stuff = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) -- picker state + local entry = action_state.get_selected_entry() + end +< + +See |telescope.actions.state| for more information. + +(2) To transform a module of functions into a module of "action"s, you need to +do the following: +> + local transform_mod = require("telescope.actions.mt").transform_mod + + local mod = {} + mod.a1 = function(prompt_bufnr) + -- your code goes here + -- You can access the picker/global state as described above in (1). + end + + mod.a2 = function(prompt_bufnr) + -- your code goes here + end + mod = transform_mod(mod) + + -- Now the following is possible. This means that actions a2 will be executed + -- after action a1. You can chain as many actions as you want. + local action = mod.a1 + mod.a2 + action(bufnr) +< + +Another interesting thing to do is that these actions now have functions you +can call. These functions include `:replace(f)`, `:replace_if(f, c)`, +`replace_map(tbl)` and `enhance(tbl)`. More information on these functions can +be found in the `developers.md` and `lua/tests/automated/action_spec.lua` file. + +actions.move_selection_next({prompt_bufnr}) *telescope.actions.move_selection_next()* + Move the selection to the next entry + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_selection_previous({prompt_bufnr}) *telescope.actions.move_selection_previous()* + Move the selection to the previous entry + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_selection_worse({prompt_bufnr}) *telescope.actions.move_selection_worse()* + Move the selection to the entry that has a worse score + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_selection_better({prompt_bufnr}) *telescope.actions.move_selection_better()* + Move the selection to the entry that has a better score + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_to_top({prompt_bufnr}) *telescope.actions.move_to_top()* + Move to the top of the picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_to_middle({prompt_bufnr}) *telescope.actions.move_to_middle()* + Move to the middle of the picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_to_bottom({prompt_bufnr}) *telescope.actions.move_to_bottom()* + Move to the bottom of the picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_selection({prompt_bufnr}) *telescope.actions.add_selection()* + Add current entry to multi select + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.remove_selection({prompt_bufnr}) *telescope.actions.remove_selection()* + Remove current entry from multi select + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.toggle_selection({prompt_bufnr}) *telescope.actions.toggle_selection()* + Toggle current entry status for multi select + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_all({prompt_bufnr}) *telescope.actions.select_all()* + Multi select all entries. + - Note: selected entries may include results not visible in the results pop + up. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.drop_all({prompt_bufnr}) *telescope.actions.drop_all()* + Drop all entries from the current multi selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.toggle_all({prompt_bufnr}) *telescope.actions.toggle_all()* + Toggle multi selection for all entries. + - Note: toggled entries may include results not visible in the results pop + up. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_up({prompt_bufnr}) *telescope.actions.preview_scrolling_up()* + Scroll the preview window up + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_down({prompt_bufnr}) *telescope.actions.preview_scrolling_down()* + Scroll the preview window down + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_left({prompt_bufnr}) *telescope.actions.preview_scrolling_left()* + Scroll the preview window to the left + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_right({prompt_bufnr}) *telescope.actions.preview_scrolling_right()* + Scroll the preview window to the right + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_up({prompt_bufnr}) *telescope.actions.results_scrolling_up()* + Scroll the results window up + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_down({prompt_bufnr}) *telescope.actions.results_scrolling_down()* + Scroll the results window down + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_left({prompt_bufnr}) *telescope.actions.results_scrolling_left()* + Scroll the results window to the left + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_right({prompt_bufnr}) *telescope.actions.results_scrolling_right()* + Scroll the results window to the right + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.center({prompt_bufnr}) *telescope.actions.center()* + Center the cursor in the window, can be used after selecting a file to edit + You can just map `actions.select_default + actions.center` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_default({prompt_bufnr}) *telescope.actions.select_default()* + Perform default action on selection, usually something like + `:edit ` + + i.e. open the selection in the current buffer + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_horizontal({prompt_bufnr}) *telescope.actions.select_horizontal()* + Perform 'horizontal' action on selection, usually something like + `:new ` + + i.e. open the selection in a new horizontal split + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_vertical({prompt_bufnr}) *telescope.actions.select_vertical()* + Perform 'vertical' action on selection, usually something like + `:vnew ` + + i.e. open the selection in a new vertical split + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_tab({prompt_bufnr}) *telescope.actions.select_tab()* + Perform 'tab' action on selection, usually something like + `:tabedit ` + + i.e. open the selection in a new tab + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_drop({prompt_bufnr}) *telescope.actions.select_drop()* + Perform 'drop' action on selection, usually something like + `:drop ` + + i.e. open the selection in a window + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_tab_drop({prompt_bufnr}) *telescope.actions.select_tab_drop()* + Perform 'tab drop' action on selection, usually something like + `:tab drop ` + + i.e. open the selection in a new tab + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_edit({prompt_bufnr}) *telescope.actions.file_edit()* + Perform file edit on selection, usually something like + `:edit ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_split({prompt_bufnr}) *telescope.actions.file_split()* + Perform file split on selection, usually something like + `:new ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_vsplit({prompt_bufnr}) *telescope.actions.file_vsplit()* + Perform file vsplit on selection, usually something like + `:vnew ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_tab({prompt_bufnr}) *telescope.actions.file_tab()* + Perform file tab on selection, usually something like + `:tabedit ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.close({prompt_bufnr}) *telescope.actions.close()* + Close the Telescope window, usually used within an action + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions._close({prompt_bufnr}) *telescope.actions._close()* + Close the Telescope window, usually used within an action + Deprecated and no longer needed, does the same as + |telescope.actions.close|. Might be removed in the future + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.edit_command_line({prompt_bufnr}) *telescope.actions.edit_command_line()* + Set a value in the command line and don't run it, making it editable. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.set_command_line({prompt_bufnr}) *telescope.actions.set_command_line()* + Set a value in the command line and run it + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.edit_search_line({prompt_bufnr}) *telescope.actions.edit_search_line()* + Set a value in the search line and don't search for it, making it editable. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.set_search_line({prompt_bufnr}) *telescope.actions.set_search_line()* + Set a value in the search line and search for it + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.edit_register({prompt_bufnr}) *telescope.actions.edit_register()* + Edit a register + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.paste_register({prompt_bufnr}) *telescope.actions.paste_register()* + Paste the selected register into the buffer + + Note: only meant to be used inside builtin.registers + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_symbol({prompt_bufnr}) *telescope.actions.insert_symbol()* + Insert a symbol into the current buffer (while switching to normal mode) + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_symbol_i({prompt_bufnr}) *telescope.actions.insert_symbol_i()* + Insert a symbol into the current buffer and keeping the insert mode. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_create_branch({prompt_bufnr}) *telescope.actions.git_create_branch()* + Create and checkout a new git branch if it doesn't already exist + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_apply_stash({prompt_bufnr}) *telescope.actions.git_apply_stash()* + Applies an existing git stash + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_checkout({prompt_bufnr}) *telescope.actions.git_checkout()* + Checkout an existing git branch + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_switch_branch({prompt_bufnr}) *telescope.actions.git_switch_branch()* + Switch to git branch. + If the branch already exists in local, switch to that. If the branch is + only in remote, create new branch tracking remote and switch to new one. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_rename_branch() *telescope.actions.git_rename_branch()* + Action to rename selected git branch + + + +actions.git_track_branch({prompt_bufnr}) *telescope.actions.git_track_branch()* + Tell git to track the currently selected remote branch in Telescope + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_delete_branch({prompt_bufnr}) *telescope.actions.git_delete_branch()* + Delete all currently selected branches + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_merge_branch({prompt_bufnr}) *telescope.actions.git_merge_branch()* + Merge the currently selected branch + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_rebase_branch({prompt_bufnr}) *telescope.actions.git_rebase_branch()* + Rebase to selected git branch + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_reset_mixed({prompt_bufnr}) *telescope.actions.git_reset_mixed()* + Reset to selected git commit using mixed mode + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_reset_soft({prompt_bufnr}) *telescope.actions.git_reset_soft()* + Reset to selected git commit using soft mode + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_reset_hard({prompt_bufnr}) *telescope.actions.git_reset_hard()* + Reset to selected git commit using hard mode + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_checkout_current_buffer({prompt_bufnr}) *telescope.actions.git_checkout_current_buffer()* + Checkout a specific file for a given sha + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_staging_toggle({prompt_bufnr}) *telescope.actions.git_staging_toggle()* + Stage/unstage selected file + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_selected_to_qflist({prompt_bufnr}) *telescope.actions.send_selected_to_qflist()* + Sends the selected entries to the quickfix list, replacing the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_selected_to_qflist({prompt_bufnr}) *telescope.actions.add_selected_to_qflist()* + Adds the selected entries to the quickfix list, keeping the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_to_qflist({prompt_bufnr}) *telescope.actions.send_to_qflist()* + Sends all entries to the quickfix list, replacing the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_to_qflist({prompt_bufnr}) *telescope.actions.add_to_qflist()* + Adds all entries to the quickfix list, keeping the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_selected_to_loclist({prompt_bufnr}) *telescope.actions.send_selected_to_loclist()* + Sends the selected entries to the location list, replacing the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_selected_to_loclist({prompt_bufnr}) *telescope.actions.add_selected_to_loclist()* + Adds the selected entries to the location list, keeping the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_to_loclist({prompt_bufnr}) *telescope.actions.send_to_loclist()* + Sends all entries to the location list, replacing the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_to_loclist({prompt_bufnr}) *telescope.actions.add_to_loclist()* + Adds all entries to the location list, keeping the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_send_to_qflist({prompt_bufnr}) *telescope.actions.smart_send_to_qflist()* + Sends the selected entries to the quickfix list, replacing the previous + entries. If no entry was selected, sends all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_add_to_qflist({prompt_bufnr}) *telescope.actions.smart_add_to_qflist()* + Adds the selected entries to the quickfix list, keeping the previous + entries. If no entry was selected, adds all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_send_to_loclist({prompt_bufnr}) *telescope.actions.smart_send_to_loclist()* + Sends the selected entries to the location list, replacing the previous + entries. If no entry was selected, sends all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_add_to_loclist({prompt_bufnr}) *telescope.actions.smart_add_to_loclist()* + Adds the selected entries to the location list, keeping the previous + entries. If no entry was selected, adds all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.complete_tag({prompt_bufnr}) *telescope.actions.complete_tag()* + Open completion menu containing the tags which can be used to filter the + results in a faster way + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_history_next({prompt_bufnr}) *telescope.actions.cycle_history_next()* + Cycle to the next search prompt in the history + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_history_prev({prompt_bufnr}) *telescope.actions.cycle_history_prev()* + Cycle to the previous search prompt in the history + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.open_qflist({prompt_bufnr}) *telescope.actions.open_qflist()* + Open the quickfix list. It makes sense to use this in combination with one + of the send_to_qflist actions `actions.smart_send_to_qflist + + actions.open_qflist` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.open_loclist({prompt_bufnr}) *telescope.actions.open_loclist()* + Open the location list. It makes sense to use this in combination with one + of the send_to_loclist actions `actions.smart_send_to_qflist + + actions.open_qflist` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.delete_buffer({prompt_bufnr}) *telescope.actions.delete_buffer()* + Delete the selected buffer or all the buffers selected using multi + selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_previewers_next({prompt_bufnr}) *telescope.actions.cycle_previewers_next()* + Cycle to the next previewer if there is one available. + This action is not mapped on default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_previewers_prev({prompt_bufnr}) *telescope.actions.cycle_previewers_prev()* + Cycle to the previous previewer if there is one available. + This action is not mapped on default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.remove_selected_picker({prompt_bufnr}) *telescope.actions.remove_selected_picker()* + Removes the selected picker in |builtin.pickers|. + This action is not mapped by default and only intended for + |builtin.pickers|. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.which_key({prompt_bufnr}) *telescope.actions.which_key()* + Display the keymaps of registered actions similar to which-key.nvim. + + - Notes: + - The defaults can be overridden via |action_generate.which_key|. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.to_fuzzy_refine({prompt_bufnr}) *telescope.actions.to_fuzzy_refine()* + Move from a none fuzzy search to a fuzzy one + This action is meant to be used in live_grep and + lsp_dynamic_workspace_symbols + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.delete_mark({prompt_bufnr}) *telescope.actions.delete_mark()* + Delete the selected mark or all the marks selected using multi selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_original_cword({prompt_bufnr}) *telescope.actions.insert_original_cword()* + Insert the word under the cursor of the original (pre-Telescope) window + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_STATE *telescope.actions.state* + +Functions to be used to determine the current state of telescope. + +Generally used from within other |telescope.actions| + +action_state.get_selected_entry() *telescope.actions.state.get_selected_entry()* + Get the current entry + + + +action_state.get_current_line() *telescope.actions.state.get_current_line()* + Gets the current line in the search prompt + + + +action_state.get_current_picker({prompt_bufnr}) *telescope.actions.state.get_current_picker()* + Gets the current picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_SET *telescope.actions.set* + +Telescope action sets are used to provide an interface for managing actions +that all primarily do the same thing, but with slight tweaks. + +For example, when editing files you may want it in the current split, a +vertical split, etc. Instead of making users have to overwrite EACH of those +every time they want to change this behavior, they can instead replace the +`set` itself and then it will work great and they're done. + +action_set.shift_selection({prompt_bufnr}, {change}) *telescope.actions.set.shift_selection()* + Move the current selection of a picker {change} rows. Handles not + overflowing / underflowing the list. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {change} (number) The amount to shift the selection by + + +action_set.select({prompt_bufnr}, {type}) *telescope.actions.set.select()* + Select the current entry. This is the action set to overwrite common + actions by the user. + + By default maps to editing a file. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {type} (string) The type of selection to make + + +action_set.edit({prompt_bufnr}, {command}) *telescope.actions.set.edit()* + Edit a file based on the current selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {command} (string) The command to use to open the file. + + +action_set.scroll_previewer({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_previewer()* + Scrolls the previewer up or down. Defaults to a half page scroll, but can + be overridden using the `scroll_speed` option in `layout_config`. See + |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + +action_set.scroll_horizontal_previewer({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_horizontal_previewer()* + Scrolls the previewer to the left or right. Defaults to a half page scroll, + but can be overridden using the `scroll_speed` option in `layout_config`. + See |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + +action_set.scroll_results({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_results()* + Scrolls the results up or down. Defaults to a half page scroll, but can be + overridden using the `scroll_speed` option in `layout_config`. See + |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + +action_set.scroll_horizontal_results({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_horizontal_results()* + Scrolls the results to the left or right. Defaults to a half page scroll, + but can be overridden using the `scroll_speed` option in `layout_config`. + See |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + + +================================================================================ +ACTIONS_LAYOUT *telescope.actions.layout* + +The layout actions are actions to be used to change the layout of a picker. + +action_layout.toggle_preview({prompt_bufnr}) *telescope.actions.layout.toggle_preview()* + Toggle preview window. + - Note: preview window can be toggled even if preview is set to false. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.toggle_prompt_position({prompt_bufnr}) *telescope.actions.layout.toggle_prompt_position()* + Toggles the `prompt_position` option between "top" and "bottom". Checks if + `prompt_position` is an option for the current layout. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.toggle_mirror({prompt_bufnr}) *telescope.actions.layout.toggle_mirror()* + Toggles the `mirror` option between `true` and `false`. Checks if `mirror` + is an option for the current layout. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.cycle_layout_next({prompt_bufnr}) *telescope.actions.layout.cycle_layout_next()* + Cycles to the next layout in `cycle_layout_list`. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.cycle_layout_prev({prompt_bufnr}) *telescope.actions.layout.cycle_layout_prev()* + Cycles to the previous layout in `cycle_layout_list`. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_UTILS *telescope.actions.utils* + +Utilities to wrap functions around picker selections and entries. + +Generally used from within other |telescope.actions| + +utils.map_entries({prompt_bufnr}, {f}) *telescope.actions.utils.map_entries()* + Apply `f` to the entries of the current picker. + - Notes: + - Mapped entries include all currently filtered results, not just the + visible ones. + - Indices are 1-indexed, whereas rows are 0-indexed. + - Warning: `map_entries` has no return value. + - The below example showcases how to collect results + + Usage: + > + local action_state = require "telescope.actions.state" + local action_utils = require "telescope.actions.utils" + function entry_value_by_row() + local prompt_bufnr = vim.api.nvim_get_current_buf() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local results = {} + action_utils.map_entries(prompt_bufnr, function(entry, index, row) + results[row] = entry.value + end) + return results + end +< + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {f} (function) Function to map onto entries of picker that + takes (entry, index, row) as viable + arguments + + +utils.map_selections({prompt_bufnr}, {f}) *telescope.actions.utils.map_selections()* + Apply `f` to the multi selections of the current picker and return a table + of mapped selections. + - Notes: + - Mapped selections may include results not visible in the results pop + up. + - Selected entries are returned in order of their selection. + - Warning: `map_selections` has no return value. + - The below example showcases how to collect results + + Usage: + > + local action_state = require "telescope.actions.state" + local action_utils = require "telescope.actions.utils" + function selection_by_index() + local prompt_bufnr = vim.api.nvim_get_current_buf() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local results = {} + action_utils.map_selections(prompt_bufnr, function(entry, index) + results[index] = entry.value + end) + return results + end +< + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {f} (function) Function to map onto selection of picker + that takes (selection) as a viable argument + + +utils.get_registered_mappings({prompt_bufnr}) *telescope.actions.utils.get_registered_mappings()* + Utility to collect mappings of prompt buffer in array of `{mode, keybind, + name}`. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_GENERATE *telescope.actions.generate* + +Module for convenience to override defaults of corresponding +|telescope.actions| at |telescope.setup()|. + +General usage: +> + require("telescope").setup { + defaults = { + mappings = { + n = { + ["?"] = action_generate.which_key { + name_width = 20, -- typically leads to smaller floats + max_height = 0.5, -- increase potential maximum height + separator = " > ", -- change sep between mode, keybind, and name + close_with_action = false, -- do not close float on action + }, + }, + }, + }, + } +< + +action_generate.which_key({opts}) *telescope.actions.generate.which_key()* + Display the keymaps of registered actions similar to which-key.nvim. + + - Floating window: + - Appears on the opposite side of the prompt. + - Resolves to minimum required number of lines to show hints with `opts` + or truncates entries at `max_height`. + - Closes automatically on action call and can be disabled with by setting + `close_with_action` to false. + + + Parameters: ~ + {opts} (table) options to pass to toggling registered actions + + Fields: ~ + {max_height} (number) % of max. height or no. of rows + for hints (default: 0.4), see + |resolver.resolve_height()| + {only_show_current_mode} (boolean) only show keymaps for the current + mode (default: true) + {mode_width} (number) fixed width of mode to be shown + (default: 1) + {keybind_width} (number) fixed width of keybind to be shown + (default: 7) + {name_width} (number) fixed width of action name to be + shown (default: 30) + {column_padding} (string) string to split; can be used for + vertical separator (default: " ") + {mode_hl} (string) hl group of mode (default: + TelescopeResultsConstant) + {keybind_hl} (string) hl group of keybind (default: + TelescopeResultsVariable) + {name_hl} (string) hl group of action name (default: + TelescopeResultsFunction) + {column_indent} (number) number of left-most spaces before + keybinds are shown (default: 4) + {line_padding} (number) row padding in top and bottom of + float (default: 1) + {separator} (string) separator string between mode, key + bindings, and action (default: " + -> ") + {close_with_action} (boolean) registered action will close + keymap float (default: true) + {normal_hl} (string) winhl of "Normal" for keymap hints + floating window (default: + "TelescopePrompt") + {border_hl} (string) winhl of "Normal" for keymap + borders (default: + "TelescopePromptBorder") + {winblend} (number) pseudo-transparency of keymap + hints floating window + {zindex} (number) z-index of keymap hints floating + window (default: 100) + + + +================================================================================ +PREVIEWERS *telescope.previewers* + +Provides a Previewer table that has to be implemented by each previewer. To +achieve this, this module also provides two wrappers that abstract most of the +work and make it really easy to create new previewers. + - `previewers.new_termopen_previewer` + - `previewers.new_buffer_previewer` + +Furthermore, there are a collection of previewers already defined which can be +used for every picker, as long as the entries of the picker provide the +necessary fields. The more important ones are + - `previewers.cat` + - `previewers.vimgrep` + - `previewers.qflist` + - `previewers.vim_buffer_cat` + - `previewers.vim_buffer_vimgrep` + - `previewers.vim_buffer_qflist` + +Previewers can be disabled for any builtin or custom picker by doing :Telescope +find_files previewer=false + +previewers.Previewer() *telescope.previewers.Previewer()* + This is the base table all previewers have to implement. It's possible to + write a wrapper for this because most previewers need to have the same keys + set. Examples of wrappers are: + - `new_buffer_previewer` + - `new_termopen_previewer` + + To create a new table do following: + - `local new_previewer = Previewer:new(opts)` + + What `:new` expects is listed below + + The interface provides the following set of functions. All of them, besides + `new`, will be handled by telescope pickers. + - `:new(opts)` + - `:preview(entry, status)` + - `:teardown()` + - `:send_input(input)` + - `:scroll_fn(direction)` + - `:scroll_horizontal_fn(direction)` + + `Previewer:new()` expects a table as input with following keys: + - `setup` function(self): Will be called the first time preview will be + called. + - `teardown` function(self): Will be called on clean up. + - `preview_fn` function(self, entry, status): Will be called each time a + new entry was selected. + - `title` function(self): Will return the static title of the previewer. + - `dynamic_title` function(self, entry): Will return the dynamic title of + the previewer. Will only be called when config value + dynamic_preview_title is true. + - `send_input` function(self, input): This is meant for + `termopen_previewer` and it can be used to send input to the terminal + application, like less. + - `scroll_fn` function(self, direction): Used to make scrolling work. + - `scroll_horizontal_fn` function(self, direction): Used to make + horizontal scrolling work. + + + +previewers.new() *telescope.previewers.new()* + A shorthand for creating a new Previewer. The provided table will be + forwarded to `Previewer:new(...)` + + + +previewers.new_termopen_previewer() *telescope.previewers.new_termopen_previewer()* + Is a wrapper around Previewer and helps with creating a new + `termopen_previewer`. + + It requires you to specify one table entry `get_command(entry, status)`. + This `get_command` function has to return the terminal command that will be + executed for each entry. Example: + > + get_command = function(entry, status) + return { 'bat', entry.path } + end +< + + Additionally you can define: + - `title` a static title for example "File Preview" + - `dyn_title(self, entry)` a dynamic title function which gets called when + config value `dynamic_preview_title = true` + + It's an easy way to get your first previewer going and it integrates well + with `bat` and `less`. Providing out of the box scrolling if the command + uses less. + + Furthermore, it will forward all `config.set_env` environment variables to + that terminal process. + + + +previewers.cat() *telescope.previewers.cat()* + Provides a `termopen_previewer` which has the ability to display files. It + will always show the top of the file and has support for `bat`(prioritized) + and `cat`. Each entry has to provide either the field `path` or `filename` + in order to make this previewer work. + + The preferred way of using this previewer is like this + `require('telescope.config').values.cat_previewer` This will respect user + configuration and will use `buffer_previewers` in case it's configured that + way. + + + +previewers.vimgrep() *telescope.previewers.vimgrep()* + Provides a `termopen_previewer` which has the ability to display files at + the provided line. It has support for `bat`(prioritized) and `cat`. Each + entry has to provide either the field `path` or `filename` and a `lnum` + field in order to make this previewer work. + + The preferred way of using this previewer is like this + `require('telescope.config').values.grep_previewer` This will respect user + configuration and will use `buffer_previewers` in case it's configured that + way. + + + +previewers.qflist() *telescope.previewers.qflist()* + Provides a `termopen_previewer` which has the ability to display files at + the provided line or range. It has support for `bat`(prioritized) and + `cat`. Each entry has to provide either the field `path` or `filename`, + `lnum` and a `start` and `finish` range in order to make this previewer + work. + + The preferred way of using this previewer is like this + `require('telescope.config').values.qflist_previewer` This will respect + user configuration and will use buffer previewers in case it's configured + that way. + + + +previewers.new_buffer_previewer() *telescope.previewers.new_buffer_previewer()* + An interface to instantiate a new `buffer_previewer`. That means that the + content actually lives inside a vim buffer which enables us more control + over the actual content. For example, we can use `vim.fn.search` to jump to + a specific line or reuse buffers/already opened files more easily. This + interface is more complex than `termopen_previewer` but offers more + flexibility over your content. It was designed to display files but was + extended to also display the output of terminal commands. + + In the following options, state table and general tips are mentioned to + make your experience with this previewer more seamless. + + + options: + - `define_preview = function(self, entry, status)` (required) Is called + for each selected entry, after each selection_move (up or down) and is + meant to handle things like reading file, jump to line or attach a + highlighter. + - `setup = function(self)` (optional) Is called once at the beginning, + before the preview for the first entry is displayed. You can return a + table of vars that will be available in `self.state` in each + `define_preview` call. + - `teardown = function(self)` (optional) Will be called at the end, when + the picker is being closed and is meant to clean up everything that was + allocated by the previewer. The `buffer_previewer` will automatically + clean up all created buffers. So you only need to handle things that + were introduced by you. + - `keep_last_buf = true` (optional) Will not delete the last selected + buffer. This would allow you to reuse that buffer in the select action. + For example, that buffer can be opened in a new split, rather than + recreating that buffer in an action. To access the last buffer number: + `require('telescope.state').get_global_key("last_preview_bufnr")` + - `get_buffer_by_name = function(self, entry)` Allows you to set a unique + name for each buffer. This is used for caching purposes. + `self.state.bufname` will be nil if the entry was never loaded or the + unique name when it was loaded once. For example, useful if you have + one file but multiple entries. This happens for grep and lsp builtins. + So to make the cache work only load content if `self.state.bufname ~= + entry.your_unique_key` + - `title` a static title for example "File Preview" + - `dyn_title(self, entry)` a dynamic title function which gets called + when config value `dynamic_preview_title = true` + + `self.state` table: + - `self.state.bufnr` Is the current buffer number, in which you have to + write the loaded content. Don't create a buffer yourself, otherwise + it's not managed by the buffer_previewer interface and you will + probably be better off writing your own interface. + - self.state.winid Current window id. Useful if you want to set the + cursor to a provided line number. + - self.state.bufname Will return the current buffer name, if + `get_buffer_by_name` is defined. nil will be returned if the entry was + never loaded or when `get_buffer_by_name` is not set. + + Tips: + - If you want to display content of a terminal job, use: + `require('telescope.previewers.utils').job_maker(cmd, bufnr, opts)` + - `cmd` table: for example { 'git', 'diff', entry.value } + - `bufnr` number: in which the content will be written + - `opts` table: with following keys + - `bufname` string: used for cache + - `value` string: used for cache + - `mode` string: either "insert" or "append". "insert" is default + - `env` table: define environment variables. Example: + - `{ ['PAGER'] = '', ['MANWIDTH'] = 50 }` + - `cwd` string: define current working directory for job + - `callback` function(bufnr, content): will be called when job is + done. Content will be nil if job is already loaded. So you can do + highlighting only the first time the previewer is created for + that entry. Use the returned `bufnr` and not `self.state.bufnr` + in callback, because state can already be changed at this point + in time. + - If you want to attach a highlighter use: + - `require('telescope.previewers.utils').highlighter(bufnr, ft)` + - This will prioritize tree sitter highlighting if available for + environment and language. + - `require('telescope.previewers.utils').regex_highlighter(bufnr, ft)` + - `require('telescope.previewers.utils').ts_highlighter(bufnr, ft)` + - If you want to use `vim.fn.search` or similar you need to run it in + that specific buffer context. Do + > + vim.api.nvim_buf_call(bufnr, function() + -- for example `search` and `matchadd` + end) +< + to achieve that. + - If you want to read a file into the buffer it's best to use + `buffer_previewer_maker`. But access this function with + `require('telescope.config').values.buffer_previewer_maker` because it + can be redefined by users. + + + +previewers.buffer_previewer_maker({filepath}, {bufnr}, {opts}) *telescope.previewers.buffer_previewer_maker()* + A universal way of reading a file into a buffer previewer. It handles async + reading, cache, highlighting, displaying directories and provides a + callback which can be used, to jump to a line in the buffer. + + + Parameters: ~ + {filepath} (string) String to the filepath, will be expanded + {bufnr} (number) Where the content will be written + {opts} (table) keys: `use_ft_detect`, `bufname` and `callback` + + +previewers.vim_buffer_cat() *telescope.previewers.vim_buffer_cat()* + A previewer that is used to display a file. It uses the `buffer_previewer` + interface and won't jump to the line. To integrate this one into your own + picker make sure that the field `path` or `filename` is set for each entry. + The preferred way of using this previewer is like this + `require('telescope.config').values.file_previewer` This will respect user + configuration and will use `termopen_previewer` in case it's configured + that way. + + + +previewers.vim_buffer_vimgrep() *telescope.previewers.vim_buffer_vimgrep()* + A previewer that is used to display a file and jump to the provided line. + It uses the `buffer_previewer` interface. To integrate this one into your + own picker make sure that the field `path` or `filename` and `lnum` is set + in each entry. If the latter is not present, it will default to the first + line. Additionally, `lnend`, `col` and `colend` can be set to highlight a + text range instead of a single line. All line/column values are 1-indexed. + The preferred way of using this previewer is like this + `require('telescope.config').values.grep_previewer` This will respect user + configuration and will use `termopen_previewer` in case it's configured + that way. + + + +previewers.vim_buffer_qflist() *telescope.previewers.vim_buffer_qflist()* + Is the same as `vim_buffer_vimgrep` and only exists for consistency with + `term_previewers`. + + The preferred way of using this previewer is like this + `require('telescope.config').values.qflist_previewer` This will respect + user configuration and will use `termopen_previewer` in case it's + configured that way. + + + +previewers.git_branch_log() *telescope.previewers.git_branch_log()* + A previewer that shows a log of a branch as graph + + + +previewers.git_stash_diff() *telescope.previewers.git_stash_diff()* + A previewer that shows a diff of a stash + + + +previewers.git_commit_diff_to_parent() *telescope.previewers.git_commit_diff_to_parent()* + A previewer that shows a diff of a commit to a parent commit. + The run command is `git --no-pager diff SHA^! -- $CURRENT_FILE` + + The current file part is optional. So is only uses it with bcommits. + + + +previewers.git_commit_diff_to_head() *telescope.previewers.git_commit_diff_to_head()* + A previewer that shows a diff of a commit to head. + The run command is `git --no-pager diff --cached $SHA -- $CURRENT_FILE` + + The current file part is optional. So is only uses it with bcommits. + + + +previewers.git_commit_diff_as_was() *telescope.previewers.git_commit_diff_as_was()* + A previewer that shows a diff of a commit as it was. + The run command is `git --no-pager show $SHA:$CURRENT_FILE` or `git + --no-pager show $SHA` + + + +previewers.git_commit_message() *telescope.previewers.git_commit_message()* + A previewer that shows the commit message of a diff. + The run command is `git --no-pager log -n 1 $SHA` + + + +previewers.git_file_diff() *telescope.previewers.git_file_diff()* + A previewer that shows the current diff of a file. Used in git_status. + The run command is `git --no-pager diff $FILE` + + + +previewers.display_content() *telescope.previewers.display_content()* + A deprecated way of displaying content more easily. Was written at a time, + where the buffer_previewer interface wasn't present. Nowadays it's easier + to just use this. We will keep it around for backwards compatibility + because some extensions use it. It doesn't use cache or some other clever + tricks. + + + + +================================================================================ +HISTORY *telescope.actions.history* + +A base implementation of a prompt history that provides a simple history and +can be replaced with a custom implementation. + +For example: We provide an extension for a smart history that uses sql.nvim to +map histories to metadata, like the calling picker or cwd. + +So you have a history for: +- find_files project_1 +- grep_string project_1 +- live_grep project_1 +- find_files project_2 +- grep_string project_2 +- live_grep project_2 +- etc + +See https://github.com/nvim-telescope/telescope-smart-history.nvim + +histories.History() *telescope.actions.history.History()* + Manages prompt history + + + Fields: ~ + {enabled} (boolean) Will indicate if History is enabled or + disabled + {path} (string) Will point to the location of the history file + {limit} (string) Will have the limit of the history. Can be + nil, if limit is disabled. + {content} (table) History table. Needs to be filled by your own + History implementation + {index} (number) Used to keep track of the next or previous + index. Default is #content + 1 + {cycle_wrap} (boolean) Controls if history will wrap on reaching + beginning or end + + +histories.History:new({opts}) *telescope.actions.history.History:new()* + Create a new History + + + Parameters: ~ + {opts} (table) Defines the behavior of History + + Fields: ~ + {init} (function) Will be called after handling configuration + (required) + {append} (function) How to append a new prompt item (required) + {reset} (function) What happens on reset. Will be called when + telescope closes (required) + {pre_get} (function) Will be called before a next or previous item + will be returned (optional) + + +histories.new() *telescope.actions.history.new()* + Shorthand to create a new history + + + +histories.History:reset() *telescope.actions.history.History:reset()* + Will reset the history index to the default initial state. Will happen + after the picker closed + + + +histories.History:append({line}, {picker}, {no_reset}) *telescope.actions.history.History:append()* + Append a new line to the history + + + Parameters: ~ + {line} (string) current line that will be appended + {picker} (table) the current picker object + {no_reset} (boolean) On default it will reset the state at the end. + If you don't want to do this set to true + + +histories.History:get_next({line}, {picker}) *telescope.actions.history.History:get_next()* + Will return the next history item. Can be nil if there are no next items + + + Parameters: ~ + {line} (string) the current line + {picker} (table) the current picker object + + Return: ~ + string: the next history item + + +histories.History:get_prev({line}, {picker}) *telescope.actions.history.History:get_prev()* + Will return the previous history item. Can be nil if there are no previous + items + + + Parameters: ~ + {line} (string) the current line + {picker} (table) the current picker object + + Return: ~ + string: the previous history item + + +histories.get_simple_history() *telescope.actions.history.get_simple_history()* + A simple implementation of history. + + It will keep one unified history across all pickers. + + + + + vim:tw=78:ts=8:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/doc/telescope_changelog.txt b/config/neovim/store/lazy-plugins/telescope.nvim/doc/telescope_changelog.txt new file mode 100644 index 00000000..08750a7b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/doc/telescope_changelog.txt @@ -0,0 +1,282 @@ +================================================================================ + *telescope.changelog* + +# Changelog + + *telescope.changelog-922* + +Date: May 17, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/922 + +This is one of our largest breaking changes thus far, so I (TJ) am adding some +information here so that you can more easily update (without having to track +down the commit, etc.). + +The goal of these breaking changes is to greatly simplify the way +configuration for layouts happen. This should make it much easier to configure +each picker, layout_strategy, and more. Please report any bugs or behavior +that is broken / confusing upstream and we can try and make the configuration +better. + +|telescope.setup()| has changed `layout_defaults` -> `layout_config`. + This makes it so that the setup and the pickers share the same key, + otherwise it is too confusing which key is for which. + + +`picker:find()` now has different values available for configuring the UI. + All configuration for the layout must be passed in the key: + `layout_config`. + + Previously, these keys were passed via `picker:find(opts)`, but should be + passed via `opts.layout_config` now. + - {height} + - {width} + - {prompt_position} + - {preview_cutoff} + + These keys are removed: + - {results_height}: This key is no longer valid. Instead, use `height` + and the corresponding `preview_*` options for the layout strategy to + get the correct results height. This simplifies the configuration + for many of the existing strategies. + + - {results_width}: This key actually never did anything. It was + leftover from some hacking that I had attempted before. Instead you + should be using something like the `preview_width` configuration + option for |layout_strategies.horizontal()| + + You should get error messages when you try and use any of the above keys now. + + *telescope.changelog-839* + +Date: July 7, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/839 + +Small breaking change regarding `shorten_path` and `hide_filename`. +This allows to configure path displays on a global level and offers a way for +extension developers to make use of the same configuration, offering a better +overall experience. + +The new way to configure to configure path displays is with: + `path_display`: It is a table and accepts multiple values: + - "hidden" hide file names + - "tail" only display the file name, and not the path + - "absolute" display absolute paths + - "shorten" only display the first character of each directory in + the path + see |telescope.defaults.path_display| + +Example would be for a global configuration: + require("telescope").setup{ + defaults = { + path_display = { + "shorten", + "absolute", + }, + } + } + +You can also still pass this to a single builtin call: + require("telescope.builtin").find_files { + path_display = { "shorten" } + } + +For extension developers there is a new util function that can be used to +display a path: + local filename = utils.transform_path(opts, entry.filename) + + *telescope.changelog-473* + +Date: July 14, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/473 + +Deprecation of telescope.path + +Extension developers need to move to plenary.path, because we will remove the +telescope.path module soon. + +Guide to switch over to plenary.path + - separator + before: require("telescope.path").separator + now: require("plenary.path").path.sep + - home + before: require("telescope.path").home + now: require("plenary.path").path.home + - make_relative + before: require("telescope.path").make_relative(filepath, cwd) + now: require("plenary.path"):new(filepath):make_relative(cwd) + - shorten + before: require("telescope.path").shorten(filepath) + now: require("plenary.path"):new(filepath):shorten() + with optional len, default is 1 + - normalize + before: require("telescope.path").normalize(filepath, cwd) + now: require("plenary.path"):new(filepath):normalize(cwd) + - read_file + before: require("telescope.path").read_file(filepath) + now: require("plenary.path"):new(filepath):read() + - read_file_async + before: require("telescope.path").read_file_async(filepath, callback) + now: require("plenary.path"):new(filepath):read(callback) + + *telescope.changelog-1406* + +Date: November 4, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1406 + +Telescope requires Neovim release 0.5.1 or a recent nightly + +Due to making use of newly implemented extmark features, Telescope now +requires users to be on Neovim 0.5.1 (the most recent stable version) or on +the LATEST version of Neovim nightly. + + + *telescope.changelog-1549* + +Date: December 10, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1549 + +Telescope requires now Neovim release 0.6.0 or a more recent nightly. +If you are running neovim nightly, you need to make sure that you are on the +LATEST version. Every other commit is not supported. So make sure you build +the newest nightly before reporting issues. + + + *telescope.changelog-1553* + +Date: December 10, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1553 + +Move from `vim.lsp.diagnostic` to `vim.diagnostic`. + +Because the newly added `vim.diagnostic` has no longer anything to do with lsp +we also decided to rename our diagnostic functions: + Telescope lsp_document_diagnostics -> Telescope diagnostics bufnr=0 + Telescope lsp_workspace_diagnostics -> Telescope diagnostics +Because of that the `lsp_*_diagnostics` inside Telescope will be deprecated +and removed soon. The new `diagnostics` works almost identical to the previous +functions. Note that there is no longer a workspace diagnostics. You can only +get all diagnostics for all open buffers. + + + *telescope.changelog-1851* + +Date: April 22, 2022 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1851 + +Telescope requires now Neovim release 0.7.0 or a more recent nightly. +If you are running Neovim nightly, you need to make sure that you are on the +LATEST version. Every other commit is not supported. So make sure you build +the newest nightly before reporting issues. +In the future, we will adopt a different release strategy. This release +strategy follows the approach that the latest telescope.nvim master will only +work with latest Neovim nightly and we will provide tags for specific Neovim +versions. You can read more about this strategy here: +https://github.com/nvim-telescope/telescope.nvim/issues/1772 + + + *telescope.changelog-1866* + +Date: April 25, 2022 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1866 + +We decided to remove both `lsp_code_actions` and `lsp_range_code_actions`. +Currently, both functions are highly duplicated code from neovim, with fewer +features, because it's out of date. So rather that we copy over the required +changes to fix some bugs or implement client side code actions, we decided to +remove both of them and suggest you use `vim.lsp.buf.code_action` and +`vim.lsp.buf.range_code_action`. The transition to it is easy thanks to +`vim.ui.select` which allows you to override the select UI. We provide a small +extension for quite some time that make it easy to use telescope for +`vim.ui.select`. You can found the code here +https://github.com/nvim-telescope/telescope-ui-select.nvim. It offers the same +displaying as the current version of `lsp_code_actions`. An alternative is +https://github.com/stevearc/dressing.nvim which has support for multiple +different backends including telescope. + + + *telescope.changelog-1945* + +Date: July 01, 2022 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1945 + +This is our dev branch which contains a lot of PRs, a lot of fixes, +refactoring and general quality of life improvements. It also contains new +features, the most noteworthy are the following (mostly developed by the +community): +- feat: none strategy & control attachment (#1867) +- feat: force buffer delete for terminal and improvements for + Picker:delete_selection (#1943) +- feat(tags): process tagfiles on the fly (#1989) +- feat(builtin.lsp): implement builtin handlers for + lsp.(incoming|outgoing)_calls (#1484) +- feat: clear previewer if no item is selected (#2004) +- feat: add min max boundary to width, height resolver (#2002) +- feat: Add entry_index for entry_makers (#1850) +- feat: refine with new_table (#1115) + +The last one is one of the most exciting new features, because it allows you +to go from live_grep into a fuzzy environment with the following mapping +``. It's a general interface we now implemented for `live_grep` and +`lsp_dynamic_workspace_symbols` but it could also be easily implemented for +other builtins, by us or the user. It's now available for extension developers. +We will add documentation in the next couple of days and improve it by adding +more options to configure it after the initial 0.1 release. + +But as with all longer development phases, there are also some breaking +changes. This is the main reason we moved development to a separate branch, for +the past two months. We can't promise that there won't be more breaking +changes, but it is the plan that this is the last set of breaking changes prior +to the 0.1 release on July, 12. We are deeply sorry for the inconvenience. The +following breaking changes are included in this PR: +- break(git_files): change `show_untracked` default to false. Can be changed + back with `:Telescope git_files show_untracked=true` +- break: deprecate `utils.get_default` `utils.if_nil`, will be removed prior + to 0.1, so if you use it in your config, please move to `vim.F.if_nil` +- break: drops `ignore_filename` option, use `path_display= { "hidden" }` + instead +- break: prefix internal interfaces with __ so + `require("telescope.builtin.files").find_files` will show a notify error but + still works for now. The error will be removed prior to 0.1! You should use + `require("telescope.builtin").find_files` because we wrap all the functions + that are exposed in this module. +- break: defaults.preview.treesitter rework that allows you to either enable a + list of languages, or enable all and disable some. Please read + `:help telescope.defaults.preview` for more information. + Something like this is now possible: + > + treesitter = { + enable = false, + -- or + enable = { "c" }, + -- disable can be set if enable isn't set + disable = { "perl", "javascript" }, + }, +< + + + *telescope.changelog-2499* + +Date: May 24, 2023 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/2499 + +We decided to bump the minimum Neovim version to 0.9.0, in order to remove a +couple of no longer required workarounds. That includes using upstream +treesitter implementation in favor of nvim-treesitter. +If you still have a requirement for Neovim 0.7 or 0.8, we also have a stable +branch 0.1.x (or version, currently 0.1.1) which will not receive this version +bump and will continue to offer support for older Neovim versions. + + + *telescope.changelog-2529* + +Date: June 09, 2023 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/2529 + +We finally removed usage of `plenary.filetype` to determine filetypes for +previewing and replaced it with `vim.filetype`. So if you have highlighting +issues you no longer have to configure `plenary`, but rather read +|vim.filetype|. + + + vim:tw=78:ts=8:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/ftplugin/TelescopePrompt.lua b/config/neovim/store/lazy-plugins/telescope.nvim/ftplugin/TelescopePrompt.lua new file mode 100644 index 00000000..8888a88f --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/ftplugin/TelescopePrompt.lua @@ -0,0 +1,12 @@ +-- Don't wrap textwidth things +vim.opt_local.formatoptions:remove "t" +vim.opt_local.formatoptions:remove "c" + +-- Don't include `showbreak` when calculating strdisplaywidth +vim.opt_local.wrap = false + +-- There's also no reason to enable textwidth here anyway +vim.opt_local.textwidth = 0 +vim.opt_local.scrollbind = false + +vim.opt_local.signcolumn = "no" diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/ftplugin/TelescopeResults.lua b/config/neovim/store/lazy-plugins/telescope.nvim/ftplugin/TelescopeResults.lua new file mode 100644 index 00000000..08e9dccf --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/ftplugin/TelescopeResults.lua @@ -0,0 +1,5 @@ +-- Don't have scrolloff, it makes things weird. +vim.opt_local.scrolloff = 0 +vim.opt_local.scrollbind = false + +vim.opt_local.signcolumn = "no" diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/_.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/_.lua new file mode 100644 index 00000000..6adc5a6e --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/_.lua @@ -0,0 +1,324 @@ +local uv = vim.loop + +local Object = require "plenary.class" +local log = require "plenary.log" + +local async = require "plenary.async" +local channel = require("plenary.async").control.channel +local utils = require "telescope.utils" + +local M = {} + +local AsyncJob = {} +AsyncJob.__index = AsyncJob + +function AsyncJob.new(opts) + local self = setmetatable({}, AsyncJob) + + self.command, self.uv_opts = M.convert_opts(opts) + + self.stdin = opts.stdin or M.NullPipe() + self.stdout = opts.stdout or M.NullPipe() + self.stderr = opts.stderr or M.NullPipe() + + if opts.cwd and opts.cwd ~= "" then + self.uv_opts.cwd = utils.path_expand(opts.cwd) + -- this is a "illegal" hack for windows. E.g. If the git command returns `/` rather than `\` as delimiter, + -- vim.fn.expand might just end up returning an empty string. Weird + -- Because empty string is not allowed in libuv the job will not spawn. Solution is we just set it to opts.cwd + if self.uv_opts.cwd == "" then + self.uv_opts.cwd = opts.cwd + end + end + + self.uv_opts.stdio = { + self.stdin.handle, + self.stdout.handle, + self.stderr.handle, + } + + return self +end + +function AsyncJob:_for_each_pipe(f, ...) + for _, pipe in ipairs { self.stdin, self.stdout, self.stderr } do + f(pipe, ...) + end +end + +function AsyncJob:close(force) + if force == nil then + force = true + end + + self:_for_each_pipe(function(p) + p:close(force) + end) + + uv.process_kill(self.handle, "sigterm") + + log.debug "[async_job] closed" +end + +M.spawn = function(opts) + local self = AsyncJob.new(opts) + self.handle, self.pid = uv.spawn( + self.command, + self.uv_opts, + async.void(function() + self:close(false) + if not self.handle:is_closing() then + self.handle:close() + end + end) + ) + + if not self.handle then + error(debug.traceback("Failed to spawn process: " .. vim.inspect(self))) + end + + return self +end + +---@class uv_pipe_t +--- A pipe handle from libuv +---@field read_start function: Start reading +---@field read_stop function: Stop reading +---@field close function: Close the handle +---@field is_closing function: Whether handle is currently closing +---@field is_active function: Whether the handle is currently reading + +---@class BasePipe +---@field super Object: Always available +---@field handle uv_pipe_t: A pipe handle +---@field extend function: Extend +local BasePipe = Object:extend() + +function BasePipe:new() + self.eof_tx, self.eof_rx = channel.oneshot() +end + +function BasePipe:close(force) + if force == nil then + force = true + end + + assert(self.handle, "Must have a pipe to close. Otherwise it's weird!") + + if self.handle:is_closing() then + return + end + + -- If we're not forcing the stop, allow waiting for eof + -- This ensures that we don't end up with weird race conditions + if not force then + self.eof_rx() + end + + self.handle:read_stop() + if not self.handle:is_closing() then + self.handle:close() + end + + self._closed = true +end + +---@class LinesPipe : BasePipe +local LinesPipe = BasePipe:extend() + +function LinesPipe:new() + LinesPipe.super.new(self) + self.handle = uv.new_pipe(false) +end + +function LinesPipe:read() + local read_tx, read_rx = channel.oneshot() + + self.handle:read_start(function(err, data) + assert(not err, err) + self.handle:read_stop() + + read_tx(data) + if data == nil then + self.eof_tx() + end + end) + + return read_rx() +end + +function LinesPipe:iter(schedule) + if schedule == nil then + schedule = true + end + + local text = nil + local index = nil + + local get_next_text = function(previous) + index = nil + + local read = self:read() + if previous == nil and read == nil then + return + end + + read = string.gsub(read or "", "\r", "") + return (previous or "") .. read + end + + local next_value = nil + next_value = function() + if schedule then + async.util.scheduler() + end + + if text == nil or (text == "" and index == nil) then + return nil + end + + local start = index + index = string.find(text, "\n", index, true) + + if index == nil then + text = get_next_text(string.sub(text, start or 1)) + return next_value() + end + + index = index + 1 + + return string.sub(text, start or 1, index - 2) + end + + text = get_next_text() + + return function() + return next_value() + end +end + +---@class NullPipe : BasePipe +local NullPipe = BasePipe:extend() + +function NullPipe:new() + NullPipe.super.new(self) + self.start = function() end + self.read_start = function() end + self.close = function() end + + -- This always has eof tx done, so can just call it now + self.eof_tx() +end + +---@class ChunkPipe : BasePipe +local ChunkPipe = BasePipe:extend() + +function ChunkPipe:new() + ChunkPipe.super.new(self) + self.handle = uv.new_pipe(false) +end + +function ChunkPipe:read() + local read_tx, read_rx = channel.oneshot() + + self.handle:read_start(function(err, data) + assert(not err, err) + self.handle:read_stop() + + read_tx(data) + if data == nil then + self.eof_tx() + end + end) + + return read_rx() +end + +function ChunkPipe:iter() + return function() + if self._closed then + return nil + end + + return self:read() + end +end + +---@class ErrorPipe : BasePipe +local ErrorPipe = BasePipe:extend() + +function ErrorPipe:new() + ErrorPipe.super.new(self) + self.handle = uv.new_pipe(false) +end + +function ErrorPipe:start() + self.handle:read_start(function(err, data) + if not err and not data then + return + end + + self.handle:read_stop() + self.handle:close() + + error(string.format("Err: %s, Data: '%s'", err, data)) + end) +end + +M.NullPipe = NullPipe +M.LinesPipe = LinesPipe +M.ChunkPipe = ChunkPipe +M.ErrorPipe = ErrorPipe + +M.convert_opts = function(o) + if not o then + error(debug.traceback "Options are required for Job:new") + end + + local command = o.command + if not command then + if o[1] then + command = o[1] + else + error(debug.traceback "'command' is required for Job:new") + end + elseif o[1] then + error(debug.traceback "Cannot pass both 'command' and array args") + end + + local args = o.args + if not args then + if #o > 1 then + args = { select(2, unpack(o)) } + end + end + + local ok, is_exe = pcall(vim.fn.executable, command) + if not o.skip_validation and ok and 1 ~= is_exe then + error(debug.traceback(command .. ": Executable not found")) + end + + local obj = {} + + obj.args = args + + if o.env then + if type(o.env) ~= "table" then + error(debug.traceback "'env' has to be a table") + end + + local transform = {} + for k, v in pairs(o.env) do + if type(k) == "number" then + table.insert(transform, v) + elseif type(k) == "string" then + table.insert(transform, k .. "=" .. tostring(v)) + end + end + obj.env = transform + end + + return command, obj +end + +return M diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/_extensions/init.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/_extensions/init.lua new file mode 100644 index 00000000..c31205a3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/_extensions/init.lua @@ -0,0 +1,73 @@ +local extensions = {} + +extensions._loaded = {} +extensions._config = {} +extensions._health = {} + +local load_extension = function(name) + local ok, ext = pcall(require, "telescope._extensions." .. name) + if not ok then + error(string.format("'%s' extension doesn't exist or isn't installed: %s", name, ext)) + end + return ext +end + +extensions.manager = setmetatable({}, { + __index = function(t, k) + local ext = load_extension(k) + t[k] = ext.exports or {} + if ext.setup then + ext.setup(extensions._config[k] or {}, require("telescope.config").values) + end + extensions._health[k] = ext.health + + return t[k] + end, +}) + +--- Register an extension module. +--- +--- Extensions have several important keys. +--- - setup: +--- function(ext_config, config) -> nil +--- +--- Called when first loading the extension. +--- The first parameter is the config passed by the user +--- in telescope setup. The second parameter is the resulting +--- config.values after applying the users setup defaults. +--- +--- It is acceptable for a plugin to override values in config, +--- as some plugins will be installed simply to manage some setup, +--- install some sorter, etc. +--- +--- - exports: +--- table +--- +--- Only the items in `exports` will be exposed on the resulting +--- module that users can access via require('telescope').extensions.foo +--- Also, any top-level key-value pairs in exports where the value is a function and the +--- key doesn't start with an underscore will be included when calling the `builtin` picker +--- with the `include_extensions` option enabled. +--- +--- Other things in the module will not be accessible. This is the public API +--- for your extension. Consider not breaking it a lot :laugh: +--- +--- TODO: +--- - actions +extensions.register = function(mod) + return mod +end + +extensions.load = function(name) + local ext = load_extension(name) + if ext.setup then + ext.setup(extensions._config[name] or {}, require("telescope.config").values) + end + return extensions.manager[name] +end + +extensions.set_config = function(extensions_config) + extensions._config = extensions_config or {} +end + +return extensions diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/generate.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/generate.lua new file mode 100644 index 00000000..a5579695 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/generate.lua @@ -0,0 +1,119 @@ +---@tag telescope.actions.generate +---@config { ["module"] = "telescope.actions.generate", ["name"] = "ACTIONS_GENERATE" } + +---@brief [[ +--- Module for convenience to override defaults of corresponding |telescope.actions| at |telescope.setup()|. +--- +--- General usage: +--- +--- require("telescope").setup { +--- defaults = { +--- mappings = { +--- n = { +--- ["?"] = action_generate.which_key { +--- name_width = 20, -- typically leads to smaller floats +--- max_height = 0.5, -- increase potential maximum height +--- separator = " > ", -- change sep between mode, keybind, and name +--- close_with_action = false, -- do not close float on action +--- }, +--- }, +--- }, +--- }, +--- } +--- +---@brief ]] + +local actions = require "telescope.actions" +local config = require "telescope.config" +local action_state = require "telescope.actions.state" +local finders = require "telescope.finders" + +local action_generate = {} + +--- Display the keymaps of registered actions similar to which-key.nvim.
+--- - Floating window: +--- - Appears on the opposite side of the prompt. +--- - Resolves to minimum required number of lines to show hints with `opts` or truncates entries at `max_height`. +--- - Closes automatically on action call and can be disabled with by setting `close_with_action` to false. +---@param opts table: options to pass to toggling registered actions +---@field max_height number: % of max. height or no. of rows for hints (default: 0.4), see |resolver.resolve_height()| +---@field only_show_current_mode boolean: only show keymaps for the current mode (default: true) +---@field mode_width number: fixed width of mode to be shown (default: 1) +---@field keybind_width number: fixed width of keybind to be shown (default: 7) +---@field name_width number: fixed width of action name to be shown (default: 30) +---@field column_padding string: string to split; can be used for vertical separator (default: " ") +---@field mode_hl string: hl group of mode (default: TelescopeResultsConstant) +---@field keybind_hl string: hl group of keybind (default: TelescopeResultsVariable) +---@field name_hl string: hl group of action name (default: TelescopeResultsFunction) +---@field column_indent number: number of left-most spaces before keybinds are shown (default: 4) +---@field line_padding number: row padding in top and bottom of float (default: 1) +---@field separator string: separator string between mode, key bindings, and action (default: " -> ") +---@field close_with_action boolean: registered action will close keymap float (default: true) +---@field normal_hl string: winhl of "Normal" for keymap hints floating window (default: "TelescopePrompt") +---@field border_hl string: winhl of "Normal" for keymap borders (default: "TelescopePromptBorder") +---@field winblend number: pseudo-transparency of keymap hints floating window +---@field zindex number: z-index of keymap hints floating window (default: 100) +action_generate.which_key = function(opts) + local which_key = function(prompt_bufnr) + actions.which_key(prompt_bufnr, opts) + end + return which_key +end + +action_generate.refine = function(prompt_bufnr, opts) + opts = opts or {} + opts.prompt_to_prefix = vim.F.if_nil(opts.prompt_to_prefix, false) + opts.prefix_hl_group = vim.F.if_nil(opts.prompt_hl_group, "TelescopePromptPrefix") + opts.prompt_prefix = vim.F.if_nil(opts.prompt_prefix, config.values.prompt_prefix) + opts.reset_multi_selection = vim.F.if_nil(opts.reset_multi_selection, false) + opts.reset_prompt = vim.F.if_nil(opts.reset_prompt, true) + opts.sorter = vim.F.if_nil(opts.sorter, config.values.generic_sorter {}) + local push_history = vim.F.if_nil(opts.push_history, true) + + local current_picker = action_state.get_current_picker(prompt_bufnr) + local current_line = action_state.get_current_line() + if push_history then + action_state.get_current_history():append(current_line, current_picker) + end + + -- title + if opts.prompt_title and current_picker.layout.prompt.border then + current_picker.layout.prompt.border:change_title(opts.prompt_title) + end + + if opts.results_title and current_picker.layout.results.border then + current_picker.layout.results.border:change_title(opts.results_title) + end + + local results = {} + for entry in current_picker.manager:iter() do + table.insert(results, entry) + end + + -- if opts.sorter == false, keep older sorter + if opts.sorter then + current_picker.sorter:_destroy() + current_picker.sorter = opts.sorter + current_picker.sorter:_init() + end + + local new_finder = finders.new_table { + results = results, + entry_maker = function(x) + return x + end, + } + + if not opts.reset_multi_selection and current_line ~= "" then + opts.multi = current_picker._multi + end + + if opts.prompt_to_prefix then + local current_prefix = current_picker.prompt_prefix + local suffix = current_prefix ~= opts.prompt_prefix and current_prefix or "" + opts.new_prefix = suffix .. current_line .. " " .. opts.prompt_prefix + end + current_picker:refresh(new_finder, opts) +end + +return action_generate diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/history.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/history.lua new file mode 100644 index 00000000..2b086896 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/history.lua @@ -0,0 +1,217 @@ +local conf = require("telescope.config").values +local Path = require "plenary.path" +local utils = require "telescope.utils" + +local uv = vim.loop + +---@tag telescope.actions.history +---@config { ["module"] = "telescope.actions.history" } + +---@brief [[ +--- A base implementation of a prompt history that provides a simple history +--- and can be replaced with a custom implementation. +--- +--- For example: We provide an extension for a smart history that uses sql.nvim +--- to map histories to metadata, like the calling picker or cwd. +--- +--- So you have a history for: +--- - find_files project_1 +--- - grep_string project_1 +--- - live_grep project_1 +--- - find_files project_2 +--- - grep_string project_2 +--- - live_grep project_2 +--- - etc +--- +--- See https://github.com/nvim-telescope/telescope-smart-history.nvim +---@brief ]] + +-- TODO(conni2461): currently not present in plenary path only sync. +-- But sync is just unnecessary here +local write_async = function(path, txt, flag) + uv.fs_open(path, flag, 438, function(open_err, fd) + assert(not open_err, open_err) + uv.fs_write(fd, txt, -1, function(write_err) + assert(not write_err, write_err) + uv.fs_close(fd, function(close_err) + assert(not close_err, close_err) + end) + end) + end) +end + +local append_async = function(path, txt) + write_async(path, txt, "a") +end + +local histories = {} + +--- Manages prompt history +---@class History @Manages prompt history +---@field enabled boolean: Will indicate if History is enabled or disabled +---@field path string: Will point to the location of the history file +---@field limit string: Will have the limit of the history. Can be nil, if limit is disabled. +---@field content table: History table. Needs to be filled by your own History implementation +---@field index number: Used to keep track of the next or previous index. Default is #content + 1 +---@field cycle_wrap boolean: Controls if history will wrap on reaching beginning or end +histories.History = {} +histories.History.__index = histories.History + +--- Create a new History +---@param opts table: Defines the behavior of History +---@field init function: Will be called after handling configuration (required) +---@field append function: How to append a new prompt item (required) +---@field reset function: What happens on reset. Will be called when telescope closes (required) +---@field pre_get function: Will be called before a next or previous item will be returned (optional) +function histories.History:new(opts) + local obj = {} + if conf.history == false or type(conf.history) ~= "table" then + obj.enabled = false + return setmetatable(obj, self) + end + obj.enabled = true + if conf.history.limit then + obj.limit = conf.history.limit + end + obj.path = utils.path_expand(conf.history.path) + obj.content = {} + obj.index = 1 + obj.cycle_wrap = conf.history.cycle_wrap + + opts.init(obj) + obj._reset = opts.reset + obj._append = opts.append + obj._pre_get = opts.pre_get + + return setmetatable(obj, self) +end + +--- Shorthand to create a new history +function histories.new(...) + return histories.History:new(...) +end + +--- Will reset the history index to the default initial state. Will happen after the picker closed +function histories.History:reset() + if not self.enabled then + return + end + self._reset(self) +end + +--- Append a new line to the history +---@param line string: current line that will be appended +---@param picker table: the current picker object +---@param no_reset boolean: On default it will reset the state at the end. If you don't want to do this set to true +function histories.History:append(line, picker, no_reset) + if not self.enabled then + return + end + self._append(self, line, picker, no_reset) +end + +--- Will return the next history item. Can be nil if there are no next items +---@param line string: the current line +---@param picker table: the current picker object +---@return string: the next history item +function histories.History:get_next(line, picker) + if not self.enabled then + utils.notify("History:get_next", { + msg = "You are cycling to next the history item but history is disabled. Read ':help telescope.defaults.history'", + level = "WARN", + }) + return false + end + if self._pre_get then + self._pre_get(self, line, picker) + end + + local next_idx = self.index + 1 + if next_idx > #self.content and self.cycle_wrap then + next_idx = 1 + end + + if next_idx <= #self.content then + self.index = next_idx + return self.content[next_idx] + end + self.index = #self.content + 1 + return nil +end + +--- Will return the previous history item. Can be nil if there are no previous items +---@param line string: the current line +---@param picker table: the current picker object +---@return string: the previous history item +function histories.History:get_prev(line, picker) + if not self.enabled then + utils.notify("History:get_prev", { + msg = "You are cycling to next the history item but history is disabled. Read ':help telescope.defaults.history'", + level = "WARN", + }) + return false + end + if self._pre_get then + self._pre_get(self, line, picker) + end + + local next_idx = self.index - 1 + if next_idx < 1 and self.cycle_wrap then + next_idx = #self.content + end + + if self.index == #self.content + 1 then + if line ~= "" then + self:append(line, picker, true) + end + end + if next_idx >= 1 then + self.index = next_idx + return self.content[next_idx] + end + return nil +end + +--- A simple implementation of history. +--- +--- It will keep one unified history across all pickers. +histories.get_simple_history = function() + return histories.new { + init = function(obj) + local p = Path:new(obj.path) + if not p:exists() then + p:touch { parents = true } + end + + obj.content = Path:new(obj.path):readlines() + obj.index = #obj.content + table.remove(obj.content, obj.index) + end, + reset = function(self) + self.index = #self.content + 1 + end, + append = function(self, line, _, no_reset) + if line ~= "" then + if self.content[#self.content] ~= line then + table.insert(self.content, line) + + local len = #self.content + if self.limit and len > self.limit then + local diff = len - self.limit + for i = diff, 1, -1 do + table.remove(self.content, i) + end + write_async(self.path, table.concat(self.content, "\n") .. "\n", "w") + else + append_async(self.path, line .. "\n") + end + end + end + if not no_reset then + self:reset() + end + end, + } +end + +return histories diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/init.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/init.lua new file mode 100644 index 00000000..c8bef1d0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/init.lua @@ -0,0 +1,1534 @@ +---@tag telescope.actions +---@config { ["module"] = "telescope.actions" } + +---@brief [[ +--- These functions are useful for people creating their own mappings. +--- +--- Actions can be either normal functions that expect the `prompt_bufnr` as +--- first argument (1) or they can be a custom telescope type called "action" (2). +--- +--- (1) The `prompt_bufnr` of a normal function denotes the identifier of your +--- picker which can be used to access the picker state. In practice, users +--- most commonly access from both picker and global state via the following: +--- +--- -- for utility functions +--- local action_state = require "telescope.actions.state" +--- +--- local actions = {} +--- actions.do_stuff = function(prompt_bufnr) +--- local current_picker = action_state.get_current_picker(prompt_bufnr) -- picker state +--- local entry = action_state.get_selected_entry() +--- end +--- +--- +--- See |telescope.actions.state| for more information. +--- +--- (2) To transform a module of functions into a module of "action"s, you need +--- to do the following: +--- +--- local transform_mod = require("telescope.actions.mt").transform_mod +--- +--- local mod = {} +--- mod.a1 = function(prompt_bufnr) +--- -- your code goes here +--- -- You can access the picker/global state as described above in (1). +--- end +--- +--- mod.a2 = function(prompt_bufnr) +--- -- your code goes here +--- end +--- mod = transform_mod(mod) +--- +--- -- Now the following is possible. This means that actions a2 will be executed +--- -- after action a1. You can chain as many actions as you want. +--- local action = mod.a1 + mod.a2 +--- action(bufnr) +--- +--- +--- Another interesting thing to do is that these actions now have functions you +--- can call. These functions include `:replace(f)`, `:replace_if(f, c)`, +--- `replace_map(tbl)` and `enhance(tbl)`. More information on these functions +--- can be found in the `developers.md` and `lua/tests/automated/action_spec.lua` +--- file. +---@brief ]] + +local a = vim.api + +local conf = require("telescope.config").values +local state = require "telescope.state" +local utils = require "telescope.utils" +local popup = require "plenary.popup" +local p_scroller = require "telescope.pickers.scroller" + +local action_state = require "telescope.actions.state" +local action_utils = require "telescope.actions.utils" +local action_set = require "telescope.actions.set" +local entry_display = require "telescope.pickers.entry_display" +local from_entry = require "telescope.from_entry" + +local transform_mod = require("telescope.actions.mt").transform_mod +local resolver = require "telescope.config.resolve" + +local actions = setmetatable({}, { + __index = function(_, k) + error("Key does not exist for 'telescope.actions': " .. tostring(k)) + end, +}) + +local append_to_history = function(prompt_bufnr) + action_state + .get_current_history() + :append(action_state.get_current_line(), action_state.get_current_picker(prompt_bufnr)) +end + +--- Move the selection to the next entry +---@param prompt_bufnr number: The prompt bufnr +actions.move_selection_next = function(prompt_bufnr) + action_set.shift_selection(prompt_bufnr, 1) +end + +--- Move the selection to the previous entry +---@param prompt_bufnr number: The prompt bufnr +actions.move_selection_previous = function(prompt_bufnr) + action_set.shift_selection(prompt_bufnr, -1) +end + +--- Move the selection to the entry that has a worse score +---@param prompt_bufnr number: The prompt bufnr +actions.move_selection_worse = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + action_set.shift_selection(prompt_bufnr, p_scroller.worse(picker.sorting_strategy)) +end + +--- Move the selection to the entry that has a better score +---@param prompt_bufnr number: The prompt bufnr +actions.move_selection_better = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + action_set.shift_selection(prompt_bufnr, p_scroller.better(picker.sorting_strategy)) +end + +--- Move to the top of the picker +---@param prompt_bufnr number: The prompt bufnr +actions.move_to_top = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_selection( + p_scroller.top(current_picker.sorting_strategy, current_picker.max_results, current_picker.manager:num_results()) + ) +end + +--- Move to the middle of the picker +---@param prompt_bufnr number: The prompt bufnr +actions.move_to_middle = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_selection( + p_scroller.middle(current_picker.sorting_strategy, current_picker.max_results, current_picker.manager:num_results()) + ) +end + +--- Move to the bottom of the picker +---@param prompt_bufnr number: The prompt bufnr +actions.move_to_bottom = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_selection( + p_scroller.bottom(current_picker.sorting_strategy, current_picker.max_results, current_picker.manager:num_results()) + ) +end + +--- Add current entry to multi select +---@param prompt_bufnr number: The prompt bufnr +actions.add_selection = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:add_selection(current_picker:get_selection_row()) +end + +--- Remove current entry from multi select +---@param prompt_bufnr number: The prompt bufnr +actions.remove_selection = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:remove_selection(current_picker:get_selection_row()) +end + +--- Toggle current entry status for multi select +---@param prompt_bufnr number: The prompt bufnr +actions.toggle_selection = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:toggle_selection(current_picker:get_selection_row()) +end + +--- Multi select all entries. +--- - Note: selected entries may include results not visible in the results pop up. +---@param prompt_bufnr number: The prompt bufnr +actions.select_all = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + action_utils.map_entries(prompt_bufnr, function(entry, _, row) + if not current_picker._multi:is_selected(entry) then + current_picker._multi:add(entry) + if current_picker:can_select_row(row) then + local caret = current_picker:update_prefix(entry, row) + if current_picker._selection_entry == entry and current_picker._selection_row == row then + current_picker.highlighter:hi_selection(row, caret:match "(.*%S)") + end + current_picker.highlighter:hi_multiselect(row, current_picker._multi:is_selected(entry)) + end + end + end) + current_picker:get_status_updater(current_picker.prompt_win, current_picker.prompt_bufnr)() +end + +--- Drop all entries from the current multi selection. +---@param prompt_bufnr number: The prompt bufnr +actions.drop_all = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + action_utils.map_entries(prompt_bufnr, function(entry, _, row) + current_picker._multi:drop(entry) + if current_picker:can_select_row(row) then + local caret = current_picker:update_prefix(entry, row) + if current_picker._selection_entry == entry and current_picker._selection_row == row then + current_picker.highlighter:hi_selection(row, caret:match "(.*%S)") + end + current_picker.highlighter:hi_multiselect(row, current_picker._multi:is_selected(entry)) + end + end) + current_picker:get_status_updater(current_picker.prompt_win, current_picker.prompt_bufnr)() +end + +--- Toggle multi selection for all entries. +--- - Note: toggled entries may include results not visible in the results pop up. +---@param prompt_bufnr number: The prompt bufnr +actions.toggle_all = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + action_utils.map_entries(prompt_bufnr, function(entry, _, row) + current_picker._multi:toggle(entry) + if current_picker:can_select_row(row) then + local caret = current_picker:update_prefix(entry, row) + if current_picker._selection_entry == entry and current_picker._selection_row == row then + current_picker.highlighter:hi_selection(row, caret:match "(.*%S)") + end + current_picker.highlighter:hi_multiselect(row, current_picker._multi:is_selected(entry)) + end + end) + current_picker:get_status_updater(current_picker.prompt_win, current_picker.prompt_bufnr)() +end + +--- Scroll the preview window up +---@param prompt_bufnr number: The prompt bufnr +actions.preview_scrolling_up = function(prompt_bufnr) + action_set.scroll_previewer(prompt_bufnr, -1) +end + +--- Scroll the preview window down +---@param prompt_bufnr number: The prompt bufnr +actions.preview_scrolling_down = function(prompt_bufnr) + action_set.scroll_previewer(prompt_bufnr, 1) +end + +--- Scroll the preview window to the left +---@param prompt_bufnr number: The prompt bufnr +actions.preview_scrolling_left = function(prompt_bufnr) + action_set.scroll_horizontal_previewer(prompt_bufnr, -1) +end + +--- Scroll the preview window to the right +---@param prompt_bufnr number: The prompt bufnr +actions.preview_scrolling_right = function(prompt_bufnr) + action_set.scroll_horizontal_previewer(prompt_bufnr, 1) +end + +--- Scroll the results window up +---@param prompt_bufnr number: The prompt bufnr +actions.results_scrolling_up = function(prompt_bufnr) + action_set.scroll_results(prompt_bufnr, -1) +end + +--- Scroll the results window down +---@param prompt_bufnr number: The prompt bufnr +actions.results_scrolling_down = function(prompt_bufnr) + action_set.scroll_results(prompt_bufnr, 1) +end + +--- Scroll the results window to the left +---@param prompt_bufnr number: The prompt bufnr +actions.results_scrolling_left = function(prompt_bufnr) + action_set.scroll_horizontal_results(prompt_bufnr, -1) +end + +--- Scroll the results window to the right +---@param prompt_bufnr number: The prompt bufnr +actions.results_scrolling_right = function(prompt_bufnr) + action_set.scroll_horizontal_results(prompt_bufnr, 1) +end + +--- Center the cursor in the window, can be used after selecting a file to edit +--- You can just map `actions.select_default + actions.center` +---@param prompt_bufnr number: The prompt bufnr +actions.center = function(prompt_bufnr) + vim.cmd ":normal! zz" +end + +--- Perform default action on selection, usually something like
+--- `:edit ` +--- +--- i.e. open the selection in the current buffer +---@param prompt_bufnr number: The prompt bufnr +actions.select_default = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "default") + end, +} + +--- Perform 'horizontal' action on selection, usually something like
+---`:new ` +--- +--- i.e. open the selection in a new horizontal split +---@param prompt_bufnr number: The prompt bufnr +actions.select_horizontal = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "horizontal") + end, +} + +--- Perform 'vertical' action on selection, usually something like
+---`:vnew ` +--- +--- i.e. open the selection in a new vertical split +---@param prompt_bufnr number: The prompt bufnr +actions.select_vertical = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "vertical") + end, +} + +--- Perform 'tab' action on selection, usually something like
+---`:tabedit ` +--- +--- i.e. open the selection in a new tab +---@param prompt_bufnr number: The prompt bufnr +actions.select_tab = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "tab") + end, +} + +--- Perform 'drop' action on selection, usually something like
+---`:drop ` +--- +--- i.e. open the selection in a window +---@param prompt_bufnr number: The prompt bufnr +actions.select_drop = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "drop") + end, +} + +--- Perform 'tab drop' action on selection, usually something like
+---`:tab drop ` +--- +--- i.e. open the selection in a new tab +---@param prompt_bufnr number: The prompt bufnr +actions.select_tab_drop = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "tab drop") + end, +} + +-- TODO: consider adding float! +-- https://github.com/nvim-telescope/telescope.nvim/issues/365 + +--- Perform file edit on selection, usually something like
+--- `:edit ` +---@param prompt_bufnr number: The prompt bufnr +actions.file_edit = function(prompt_bufnr) + return action_set.edit(prompt_bufnr, "edit") +end + +--- Perform file split on selection, usually something like
+--- `:new ` +---@param prompt_bufnr number: The prompt bufnr +actions.file_split = function(prompt_bufnr) + return action_set.edit(prompt_bufnr, "new") +end + +--- Perform file vsplit on selection, usually something like
+--- `:vnew ` +---@param prompt_bufnr number: The prompt bufnr +actions.file_vsplit = function(prompt_bufnr) + return action_set.edit(prompt_bufnr, "vnew") +end + +--- Perform file tab on selection, usually something like
+--- `:tabedit ` +---@param prompt_bufnr number: The prompt bufnr +actions.file_tab = function(prompt_bufnr) + return action_set.edit(prompt_bufnr, "tabedit") +end + +actions.close_pum = function(_) + if 0 ~= vim.fn.pumvisible() then + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, true, true), "n", true) + end +end + +--- Close the Telescope window, usually used within an action +---@param prompt_bufnr number: The prompt bufnr +actions.close = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + local original_win_id = picker.original_win_id + local cursor_valid, original_cursor = pcall(a.nvim_win_get_cursor, original_win_id) + + actions.close_pum(prompt_bufnr) + + require("telescope.pickers").on_close_prompt(prompt_bufnr) + pcall(a.nvim_set_current_win, original_win_id) + if cursor_valid and a.nvim_get_mode().mode == "i" and picker._original_mode ~= "i" then + pcall(a.nvim_win_set_cursor, original_win_id, { original_cursor[1], original_cursor[2] + 1 }) + end +end + +--- Close the Telescope window, usually used within an action
+--- Deprecated and no longer needed, does the same as |telescope.actions.close|. Might be removed in the future +---@deprecated +---@param prompt_bufnr number: The prompt bufnr +actions._close = function(prompt_bufnr) + actions.close(prompt_bufnr) +end + +local set_edit_line = function(prompt_bufnr, fname, prefix, postfix) + postfix = vim.F.if_nil(postfix, "") + postfix = a.nvim_replace_termcodes(postfix, true, false, true) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection(fname) + return + end + actions.close(prompt_bufnr) + a.nvim_feedkeys(prefix .. selection.value .. postfix, "n", true) +end + +--- Set a value in the command line and don't run it, making it editable. +---@param prompt_bufnr number: The prompt bufnr +actions.edit_command_line = function(prompt_bufnr) + set_edit_line(prompt_bufnr, "actions.edit_command_line", ":") +end + +--- Set a value in the command line and run it +---@param prompt_bufnr number: The prompt bufnr +actions.set_command_line = function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.set_command_line" + return + end + actions.close(prompt_bufnr) + vim.fn.histadd("cmd", selection.value) + vim.cmd(selection.value) +end + +--- Set a value in the search line and don't search for it, making it editable. +---@param prompt_bufnr number: The prompt bufnr +actions.edit_search_line = function(prompt_bufnr) + set_edit_line(prompt_bufnr, "actions.edit_search_line", "/") +end + +--- Set a value in the search line and search for it +---@param prompt_bufnr number: The prompt bufnr +actions.set_search_line = function(prompt_bufnr) + set_edit_line(prompt_bufnr, "actions.set_search_line", "/", "") +end + +--- Edit a register +---@param prompt_bufnr number: The prompt bufnr +actions.edit_register = function(prompt_bufnr) + local selection = action_state.get_selected_entry() + local picker = action_state.get_current_picker(prompt_bufnr) + + vim.fn.inputsave() + local updated_value = vim.fn.input("Edit [" .. selection.value .. "] ❯ ", selection.content) + vim.fn.inputrestore() + if updated_value ~= selection.content then + vim.fn.setreg(selection.value, updated_value) + selection.content = updated_value + end + + -- update entry in results table + -- TODO: find way to redraw finder content + for _, v in pairs(picker.finder.results) do + if v == selection then + v.content = updated_value + end + end + -- print(vim.inspect(picker.finder.results)) +end + +--- Paste the selected register into the buffer +--- +--- Note: only meant to be used inside builtin.registers +---@param prompt_bufnr number: The prompt bufnr +actions.paste_register = function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.paste_register" + return + end + + actions.close(prompt_bufnr) + + -- ensure that the buffer can be written to + if vim.api.nvim_buf_get_option(vim.api.nvim_get_current_buf(), "modifiable") then + vim.api.nvim_paste(selection.content, true, -1) + end +end + +--- Insert a symbol into the current buffer (while switching to normal mode) +---@param prompt_bufnr number: The prompt bufnr +actions.insert_symbol = function(prompt_bufnr) + local symbol = action_state.get_selected_entry().value[1] + actions.close(prompt_bufnr) + vim.api.nvim_put({ symbol }, "", true, true) +end + +--- Insert a symbol into the current buffer and keeping the insert mode. +---@param prompt_bufnr number: The prompt bufnr +actions.insert_symbol_i = function(prompt_bufnr) + local symbol = action_state.get_selected_entry().value[1] + actions.close(prompt_bufnr) + vim.schedule(function() + vim.cmd [[startinsert]] + vim.api.nvim_put({ symbol }, "", true, true) + end) +end + +-- TODO: Think about how to do this. +actions.insert_value = function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.insert_value" + return + end + + vim.schedule(function() + actions.close(prompt_bufnr) + end) + + return selection.value +end + +--- Ask user to confirm an action +---@param prompt string: The prompt for confirmation +---@param default_value string: The default value of user input +---@param yes_values table: List of positive user confirmations ({"y", "yes"} by default) +---@return boolean: Whether user confirmed the prompt +local function ask_to_confirm(prompt, default_value, yes_values) + yes_values = yes_values or { "y", "yes" } + default_value = default_value or "" + local confirmation = vim.fn.input(prompt, default_value) + confirmation = string.lower(confirmation) + if string.len(confirmation) == 0 then + return false + end + for _, v in pairs(yes_values) do + if v == confirmation then + return true + end + end + return false +end + +--- Create and checkout a new git branch if it doesn't already exist +---@param prompt_bufnr number: The prompt bufnr +actions.git_create_branch = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local new_branch = action_state.get_current_line() + + if new_branch == "" then + utils.notify("actions.git_create_branch", { + msg = "Missing the new branch name", + level = "ERROR", + }) + else + local confirmation = ask_to_confirm(string.format("Create new branch '%s'? [y/n]: ", new_branch)) + if not confirmation then + utils.notify("actions.git_create_branch", { + msg = string.format("branch creation canceled: '%s'", new_branch), + level = "INFO", + }) + return + end + + actions.close(prompt_bufnr) + + local _, ret, stderr = utils.get_os_command_output({ "git", "checkout", "-b", new_branch }, cwd) + if ret == 0 then + utils.notify("actions.git_create_branch", { + msg = string.format("Switched to a new branch: %s", new_branch), + level = "INFO", + }) + else + utils.notify("actions.git_create_branch", { + msg = string.format( + "Error when creating new branch: '%s' Git returned '%s'", + new_branch, + table.concat(stderr, " ") + ), + level = "INFO", + }) + end + end +end + +--- Applies an existing git stash +---@param prompt_bufnr number: The prompt bufnr +actions.git_apply_stash = function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_apply_stash" + return + end + actions.close(prompt_bufnr) + local _, ret, stderr = utils.get_os_command_output { "git", "stash", "apply", "--index", selection.value } + if ret == 0 then + utils.notify("actions.git_apply_stash", { + msg = string.format("applied: '%s' ", selection.value), + level = "INFO", + }) + else + utils.notify("actions.git_apply_stash", { + msg = string.format("Error when applying: %s. Git returned: '%s'", selection.value, table.concat(stderr, " ")), + level = "ERROR", + }) + end +end + +--- Checkout an existing git branch +---@param prompt_bufnr number: The prompt bufnr +actions.git_checkout = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_checkout" + return + end + actions.close(prompt_bufnr) + local _, ret, stderr = utils.get_os_command_output({ "git", "checkout", selection.value }, cwd) + if ret == 0 then + utils.notify("actions.git_checkout", { + msg = string.format("Checked out: %s", selection.value), + level = "INFO", + }) + vim.cmd "checktime" + else + utils.notify("actions.git_checkout", { + msg = string.format( + "Error when checking out: %s. Git returned: '%s'", + selection.value, + table.concat(stderr, " ") + ), + level = "ERROR", + }) + end +end + +--- Switch to git branch.
+--- If the branch already exists in local, switch to that. +--- If the branch is only in remote, create new branch tracking remote and switch to new one. +---@param prompt_bufnr number: The prompt bufnr +actions.git_switch_branch = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_switch_branch" + return + end + actions.close(prompt_bufnr) + local pattern = "^refs/remotes/%w+/" + local branch = selection.value + if string.match(selection.refname, pattern) then + branch = string.gsub(selection.refname, pattern, "") + end + local _, ret, stderr = utils.get_os_command_output({ "git", "switch", branch }, cwd) + if ret == 0 then + utils.notify("actions.git_switch_branch", { + msg = string.format("Switched to: '%s'", branch), + level = "INFO", + }) + else + utils.notify("actions.git_switch_branch", { + msg = string.format( + "Error when switching to: %s. Git returned: '%s'", + selection.value, + table.concat(stderr, " ") + ), + level = "ERROR", + }) + end +end + +--- Action to rename selected git branch +--- @param prompt_bufnr number: The prompt bufnr +actions.git_rename_branch = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_rename_branch" + return + end + -- Keeps the selected branch name for the input that asks for the new branch name + local new_branch = vim.fn.input("New branch name: ", selection.value) + if new_branch == "" then + utils.notify("actions.git_rename_branch", { + msg = "Missing the new branch name", + level = "ERROR", + }) + else + actions.close(prompt_bufnr) + local _, ret, stderr = utils.get_os_command_output({ "git", "branch", "-m", selection.value, new_branch }, cwd) + if ret == 0 then + utils.notify("actions.git_rename_branch", { + msg = string.format("Renamed branch: '%s'", selection.value), + level = "INFO", + }) + else + utils.notify("actions.git_rename_branch", { + msg = string.format( + "Error when renaming branch: %s. Git returned: '%s'", + selection.value, + table.concat(stderr, " ") + ), + level = "ERROR", + }) + end + end +end + +local function make_git_branch_action(opts) + return function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection(opts.action_name) + return + end + + local should_confirm = opts.should_confirm + if should_confirm then + local confirmation = ask_to_confirm(string.format(opts.confirmation_question, selection.value), "y") + if not confirmation then + utils.notify(opts.action_name, { + msg = "action canceled", + level = "INFO", + }) + return + end + end + + actions.close(prompt_bufnr) + local _, ret, stderr = utils.get_os_command_output(opts.command(selection.value), cwd) + if ret == 0 then + utils.notify(opts.action_name, { + msg = string.format(opts.success_message, selection.value), + level = "INFO", + }) + else + utils.notify(opts.action_name, { + msg = string.format(opts.error_message, selection.value, table.concat(stderr, " ")), + level = "ERROR", + }) + end + end +end + +--- Tell git to track the currently selected remote branch in Telescope +---@param prompt_bufnr number: The prompt bufnr +actions.git_track_branch = make_git_branch_action { + should_confirm = false, + action_name = "actions.git_track_branch", + success_message = "Tracking branch: %s", + error_message = "Error when tracking branch: %s. Git returned: '%s'", + command = function(branch_name) + return { "git", "checkout", "--track", branch_name } + end, +} + +--- Delete all currently selected branches +---@param prompt_bufnr number: The prompt bufnr +actions.git_delete_branch = function(prompt_bufnr) + local confirmation = ask_to_confirm("Do you really want to delete the selected branches? [Y/n] ", "y") + if not confirmation then + utils.notify("actions.git_delete_branch", { + msg = "action canceled", + level = "INFO", + }) + return + end + + local picker = action_state.get_current_picker(prompt_bufnr) + local action_name = "actions.git_delete_branch" + picker:delete_selection(function(selection) + local branch = selection.value + print("Deleting branch " .. branch) + local _, ret, stderr = utils.get_os_command_output({ "git", "branch", "-D", branch }, picker.cwd) + if ret == 0 then + utils.notify(action_name, { + msg = string.format("Deleted branch: %s", branch), + level = "INFO", + }) + else + utils.notify(action_name, { + msg = string.format("Error when deleting branch: %s. Git returned: '%s'", branch, table.concat(stderr, " ")), + level = "ERROR", + }) + end + return ret == 0 + end) +end + +--- Merge the currently selected branch +---@param prompt_bufnr number: The prompt bufnr +actions.git_merge_branch = make_git_branch_action { + should_confirm = true, + action_name = "actions.git_merge_branch", + confirmation_question = "Do you really wanna merge branch %s? [Y/n] ", + success_message = "Merged branch: %s", + error_message = "Error when merging branch: %s. Git returned: '%s'", + command = function(branch_name) + return { "git", "merge", branch_name } + end, +} + +--- Rebase to selected git branch +---@param prompt_bufnr number: The prompt bufnr +actions.git_rebase_branch = make_git_branch_action { + should_confirm = true, + action_name = "actions.git_rebase_branch", + confirmation_question = "Do you really wanna rebase branch %s? [Y/n] ", + success_message = "Rebased branch: %s", + error_message = "Error when rebasing branch: %s. Git returned: '%s'", + command = function(branch_name) + return { "git", "rebase", branch_name } + end, +} + +local git_reset_branch = function(prompt_bufnr, mode) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_reset_branch" + return + end + + local confirmation = + ask_to_confirm("Do you really wanna " .. mode .. " reset to " .. selection.value .. "? [Y/n] ", "y") + if not confirmation then + utils.notify("actions.git_reset_branch", { + msg = "action canceled", + level = "INFO", + }) + return + end + + actions.close(prompt_bufnr) + local _, ret, stderr = utils.get_os_command_output({ "git", "reset", mode, selection.value }, cwd) + if ret == 0 then + utils.notify("actions.git_rebase_branch", { + msg = string.format("Reset to: '%s'", selection.value), + level = "INFO", + }) + else + utils.notify("actions.git_rebase_branch", { + msg = string.format("Rest to: %s. Git returned: '%s'", selection.value, table.concat(stderr, " ")), + level = "ERROR", + }) + end +end + +--- Reset to selected git commit using mixed mode +---@param prompt_bufnr number: The prompt bufnr +actions.git_reset_mixed = function(prompt_bufnr) + git_reset_branch(prompt_bufnr, "--mixed") +end + +--- Reset to selected git commit using soft mode +---@param prompt_bufnr number: The prompt bufnr +actions.git_reset_soft = function(prompt_bufnr) + git_reset_branch(prompt_bufnr, "--soft") +end + +--- Reset to selected git commit using hard mode +---@param prompt_bufnr number: The prompt bufnr +actions.git_reset_hard = function(prompt_bufnr) + git_reset_branch(prompt_bufnr, "--hard") +end + +--- Checkout a specific file for a given sha +---@param prompt_bufnr number: The prompt bufnr +actions.git_checkout_current_buffer = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_checkout_current_buffer" + + return + end + actions.close(prompt_bufnr) + utils.get_os_command_output({ "git", "checkout", selection.value, "--", selection.current_file }, cwd) + vim.cmd "checktime" +end + +--- Stage/unstage selected file +---@param prompt_bufnr number: The prompt bufnr +actions.git_staging_toggle = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_staging_toggle" + return + end + if selection.status:sub(2) == " " then + utils.get_os_command_output({ "git", "restore", "--staged", selection.value }, cwd) + else + utils.get_os_command_output({ "git", "add", selection.value }, cwd) + end +end + +local entry_to_qf = function(entry) + local text = entry.text + + if not text then + if type(entry.value) == "table" then + text = entry.value.text + else + text = entry.value + end + end + + return { + bufnr = entry.bufnr, + filename = from_entry.path(entry, false, false), + lnum = vim.F.if_nil(entry.lnum, 1), + col = vim.F.if_nil(entry.col, 1), + text = text, + type = entry.qf_type, + } +end + +local send_selected_to_qf = function(prompt_bufnr, mode, target) + local picker = action_state.get_current_picker(prompt_bufnr) + + local qf_entries = {} + for _, entry in ipairs(picker:get_multi_selection()) do + table.insert(qf_entries, entry_to_qf(entry)) + end + + local prompt = picker:_get_prompt() + actions.close(prompt_bufnr) + + vim.api.nvim_exec_autocmds("QuickFixCmdPre", {}) + if target == "loclist" then + vim.fn.setloclist(picker.original_win_id, qf_entries, mode) + else + local qf_title = string.format([[%s (%s)]], picker.prompt_title, prompt) + vim.fn.setqflist(qf_entries, mode) + vim.fn.setqflist({}, "a", { title = qf_title }) + end + vim.api.nvim_exec_autocmds("QuickFixCmdPost", {}) +end + +local send_all_to_qf = function(prompt_bufnr, mode, target) + local picker = action_state.get_current_picker(prompt_bufnr) + local manager = picker.manager + + local qf_entries = {} + for entry in manager:iter() do + table.insert(qf_entries, entry_to_qf(entry)) + end + + local prompt = picker:_get_prompt() + actions.close(prompt_bufnr) + + vim.api.nvim_exec_autocmds("QuickFixCmdPre", {}) + local qf_title = string.format([[%s (%s)]], picker.prompt_title, prompt) + if target == "loclist" then + vim.fn.setloclist(picker.original_win_id, qf_entries, mode) + vim.fn.setloclist(picker.original_win_id, {}, "a", { title = qf_title }) + else + vim.fn.setqflist(qf_entries, mode) + vim.fn.setqflist({}, "a", { title = qf_title }) + end + vim.api.nvim_exec_autocmds("QuickFixCmdPost", {}) +end + +--- Sends the selected entries to the quickfix list, replacing the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.send_selected_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_selected_to_qf(prompt_bufnr, " ") + end, +} +--- Adds the selected entries to the quickfix list, keeping the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.add_selected_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_selected_to_qf(prompt_bufnr, "a") + end, +} +--- Sends all entries to the quickfix list, replacing the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.send_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_all_to_qf(prompt_bufnr, " ") + end, +} +--- Adds all entries to the quickfix list, keeping the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.add_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_all_to_qf(prompt_bufnr, "a") + end, +} +--- Sends the selected entries to the location list, replacing the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.send_selected_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_selected_to_qf(prompt_bufnr, " ", "loclist") + end, +} +--- Adds the selected entries to the location list, keeping the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.add_selected_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_selected_to_qf(prompt_bufnr, "a", "loclist") + end, +} +--- Sends all entries to the location list, replacing the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.send_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_all_to_qf(prompt_bufnr, " ", "loclist") + end, +} +--- Adds all entries to the location list, keeping the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.add_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_all_to_qf(prompt_bufnr, "a", "loclist") + end, +} + +local smart_send = function(prompt_bufnr, mode, target) + local picker = action_state.get_current_picker(prompt_bufnr) + if #picker:get_multi_selection() > 0 then + send_selected_to_qf(prompt_bufnr, mode, target) + else + send_all_to_qf(prompt_bufnr, mode, target) + end +end + +--- Sends the selected entries to the quickfix list, replacing the previous entries. +--- If no entry was selected, sends all entries. +---@param prompt_bufnr number: The prompt bufnr +actions.smart_send_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + smart_send(prompt_bufnr, " ") + end, +} +--- Adds the selected entries to the quickfix list, keeping the previous entries. +--- If no entry was selected, adds all entries. +---@param prompt_bufnr number: The prompt bufnr +actions.smart_add_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + smart_send(prompt_bufnr, "a") + end, +} +--- Sends the selected entries to the location list, replacing the previous entries. +--- If no entry was selected, sends all entries. +---@param prompt_bufnr number: The prompt bufnr +actions.smart_send_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + smart_send(prompt_bufnr, " ", "loclist") + end, +} +--- Adds the selected entries to the location list, keeping the previous entries. +--- If no entry was selected, adds all entries. +---@param prompt_bufnr number: The prompt bufnr +actions.smart_add_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + smart_send(prompt_bufnr, "a", "loclist") + end, +} +--- Open completion menu containing the tags which can be used to filter the results in a faster way +---@param prompt_bufnr number: The prompt bufnr +actions.complete_tag = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + local tags = current_picker.sorter.tags + local delimiter = current_picker.sorter._delimiter + + if not tags then + utils.notify("actions.complete_tag", { + msg = "No tag pre-filtering set for this picker", + level = "ERROR", + }) + + return + end + + -- format tags to match filter_function + local prefilter_tags = {} + for tag, _ in pairs(tags) do + table.insert(prefilter_tags, string.format("%s%s%s ", delimiter, tag:lower(), delimiter)) + end + + local line = action_state.get_current_line() + local filtered_tags = {} + -- retrigger completion with already selected tag anew + -- trim and add space since we can match [[:pattern: ]] with or without space at the end + if vim.tbl_contains(prefilter_tags, vim.trim(line) .. " ") then + filtered_tags = prefilter_tags + else + -- match tag by substring + for _, tag in pairs(prefilter_tags) do + local start, _ = tag:find(line) + if start then + table.insert(filtered_tags, tag) + end + end + end + + if vim.tbl_isempty(filtered_tags) then + utils.notify("complete_tag", { + msg = "No matches found", + level = "INFO", + }) + return + end + + -- incremental completion by substituting string starting from col - #line byte offset + local col = vim.api.nvim_win_get_cursor(0)[2] + 1 + vim.fn.complete(col - #line, filtered_tags) +end + +--- Cycle to the next search prompt in the history +---@param prompt_bufnr number: The prompt bufnr +actions.cycle_history_next = function(prompt_bufnr) + local history = action_state.get_current_history() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local line = action_state.get_current_line() + + local entry = history:get_next(line, current_picker) + if entry == false then + return + end + + current_picker:reset_prompt() + if entry ~= nil then + current_picker:set_prompt(entry) + end +end + +--- Cycle to the previous search prompt in the history +---@param prompt_bufnr number: The prompt bufnr +actions.cycle_history_prev = function(prompt_bufnr) + local history = action_state.get_current_history() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local line = action_state.get_current_line() + + local entry = history:get_prev(line, current_picker) + if entry == false then + return + end + if entry ~= nil then + current_picker:reset_prompt() + current_picker:set_prompt(entry) + end +end + +--- Open the quickfix list. It makes sense to use this in combination with one of the send_to_qflist actions +--- `actions.smart_send_to_qflist + actions.open_qflist` +---@param prompt_bufnr number: The prompt bufnr +actions.open_qflist = function(prompt_bufnr) + vim.cmd [[botright copen]] +end + +--- Open the location list. It makes sense to use this in combination with one of the send_to_loclist actions +--- `actions.smart_send_to_qflist + actions.open_qflist` +---@param prompt_bufnr number: The prompt bufnr +actions.open_loclist = function(prompt_bufnr) + vim.cmd [[lopen]] +end + +--- Delete the selected buffer or all the buffers selected using multi selection. +---@param prompt_bufnr number: The prompt bufnr +actions.delete_buffer = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:delete_selection(function(selection) + local force = vim.api.nvim_buf_get_option(selection.bufnr, "buftype") == "terminal" + local ok = pcall(vim.api.nvim_buf_delete, selection.bufnr, { force = force }) + return ok + end) +end + +--- Cycle to the next previewer if there is one available.
+--- This action is not mapped on default. +---@param prompt_bufnr number: The prompt bufnr +actions.cycle_previewers_next = function(prompt_bufnr) + action_state.get_current_picker(prompt_bufnr):cycle_previewers(1) +end + +--- Cycle to the previous previewer if there is one available.
+--- This action is not mapped on default. +---@param prompt_bufnr number: The prompt bufnr +actions.cycle_previewers_prev = function(prompt_bufnr) + action_state.get_current_picker(prompt_bufnr):cycle_previewers(-1) +end + +--- Removes the selected picker in |builtin.pickers|.
+--- This action is not mapped by default and only intended for |builtin.pickers|. +---@param prompt_bufnr number: The prompt bufnr +actions.remove_selected_picker = function(prompt_bufnr) + local curr_picker = action_state.get_current_picker(prompt_bufnr) + local curr_entry = action_state.get_selected_entry() + local cached_pickers = state.get_global_key "cached_pickers" + + if not curr_entry then + return + end + + local selection_index, _ = utils.list_find(function(v) + if curr_entry.value == v.value then + return true + end + return false + end, curr_picker.finder.results) + + curr_picker:delete_selection(function() + table.remove(cached_pickers, selection_index) + end) + + if #cached_pickers == 0 then + actions.close(prompt_bufnr) + end +end + +--- Display the keymaps of registered actions similar to which-key.nvim.
+--- - Notes: +--- - The defaults can be overridden via |action_generate.which_key|. +---@param prompt_bufnr number: The prompt bufnr +actions.which_key = function(prompt_bufnr, opts) + opts = opts or {} + opts.max_height = vim.F.if_nil(opts.max_height, 0.4) + opts.only_show_current_mode = vim.F.if_nil(opts.only_show_current_mode, true) + opts.mode_width = vim.F.if_nil(opts.mode_width, 1) + opts.keybind_width = vim.F.if_nil(opts.keybind_width, 7) + opts.name_width = vim.F.if_nil(opts.name_width, 30) + opts.line_padding = vim.F.if_nil(opts.line_padding, 1) + opts.separator = vim.F.if_nil(opts.separator, " -> ") + opts.close_with_action = vim.F.if_nil(opts.close_with_action, true) + opts.normal_hl = vim.F.if_nil(opts.normal_hl, "TelescopePrompt") + opts.border_hl = vim.F.if_nil(opts.border_hl, "TelescopePromptBorder") + opts.winblend = vim.F.if_nil(opts.winblend, conf.winblend) + if type(opts.winblend) == "function" then + opts.winblend = opts.winblend() + end + opts.zindex = vim.F.if_nil(opts.zindex, 100) + opts.column_padding = vim.F.if_nil(opts.column_padding, " ") + + -- Assigning into 'opts.column_indent' would override a number with a string and + -- cause issues with subsequent calls, keep a local copy of the string instead + local column_indent = table.concat(utils.repeated_table(vim.F.if_nil(opts.column_indent, 4), " ")) + + -- close on repeated keypress + local km_bufs = (function() + local ret = {} + local bufs = a.nvim_list_bufs() + for _, buf in ipairs(bufs) do + for _, bufname in ipairs { "_TelescopeWhichKey", "_TelescopeWhichKeyBorder" } do + if string.find(a.nvim_buf_get_name(buf), bufname) then + table.insert(ret, buf) + end + end + end + return ret + end)() + if not vim.tbl_isempty(km_bufs) then + for _, buf in ipairs(km_bufs) do + utils.buf_delete(buf) + local win_ids = vim.fn.win_findbuf(buf) + for _, win_id in ipairs(win_ids) do + pcall(a.nvim_win_close, win_id, true) + end + end + return + end + + local displayer = entry_display.create { + separator = opts.separator, + items = { + { width = opts.mode_width }, + { width = opts.keybind_width }, + { width = opts.name_width }, + }, + } + + local make_display = function(mapping) + return displayer { + { mapping.mode, vim.F.if_nil(opts.mode_hl, "TelescopeResultsConstant") }, + { mapping.keybind, vim.F.if_nil(opts.keybind_hl, "TelescopeResultsVariable") }, + { mapping.name, vim.F.if_nil(opts.name_hl, "TelescopeResultsFunction") }, + } + end + + local mappings = {} + local mode = a.nvim_get_mode().mode + for _, v in pairs(action_utils.get_registered_mappings(prompt_bufnr)) do + if v.desc and v.desc ~= "which_key" and v.desc ~= "nop" then + if not opts.only_show_current_mode or mode == v.mode then + table.insert(mappings, { mode = v.mode, keybind = v.keybind, name = v.desc }) + if v.desc == "" then + utils.notify("actions.which_key", { + msg = "No name available for anonymous functions.", + level = "INFO", + once = true, + }) + end + end + end + end + + table.sort(mappings, function(x, y) + if x.name < y.name then + return true + elseif x.name == y.name then + -- show normal mode as the standard mode first + if x.mode > y.mode then + return true + else + return false + end + else + return false + end + end) + + local entry_width = #opts.column_padding + + opts.mode_width + + opts.keybind_width + + opts.name_width + + (3 * #opts.separator) + local num_total_columns = math.floor((vim.o.columns - #column_indent) / entry_width) + opts.num_rows = + math.min(math.ceil(#mappings / num_total_columns), resolver.resolve_height(opts.max_height)(_, _, vim.o.lines)) + local total_available_entries = opts.num_rows * num_total_columns + local winheight = opts.num_rows + 2 * opts.line_padding + + -- place hints at top or bottom relative to prompt + local win_central_row = function(win_nr) + return a.nvim_win_get_position(win_nr)[1] + 0.5 * a.nvim_win_get_height(win_nr) + end + -- TODO(fdschmidt93|l-kershaw): better generalization of where to put which key float + local picker = action_state.get_current_picker(prompt_bufnr) + local prompt_row = win_central_row(picker.prompt_win) + local results_row = win_central_row(picker.results_win) + local preview_row = picker.preview_win and win_central_row(picker.preview_win) or results_row + local prompt_pos = prompt_row < 0.4 * vim.o.lines + or prompt_row < 0.6 * vim.o.lines and results_row + preview_row < vim.o.lines + + local modes = { n = "Normal", i = "Insert" } + local title_mode = opts.only_show_current_mode and modes[mode] .. " Mode " or "" + local title_text = title_mode .. "Keymaps" + local popup_opts = { + relative = "editor", + enter = false, + minwidth = vim.o.columns, + maxwidth = vim.o.columns, + minheight = winheight, + maxheight = winheight, + line = prompt_pos == true and vim.o.lines - winheight + 1 or 1, + col = 0, + border = { prompt_pos and 1 or 0, 0, not prompt_pos and 1 or 0, 0 }, + borderchars = { prompt_pos and "─" or " ", "", not prompt_pos and "─" or " ", "", "", "", "", "" }, + noautocmd = true, + title = { { text = title_text, pos = prompt_pos and "N" or "S" } }, + zindex = opts.zindex, + } + local km_win_id, km_opts = popup.create("", popup_opts) + local km_buf = a.nvim_win_get_buf(km_win_id) + a.nvim_buf_set_name(km_buf, "_TelescopeWhichKey") + a.nvim_buf_set_name(km_opts.border.bufnr, "_TelescopeTelescopeWhichKeyBorder") + a.nvim_win_set_option(km_win_id, "winhl", "Normal:" .. opts.normal_hl) + a.nvim_win_set_option(km_opts.border.win_id, "winhl", "Normal:" .. opts.border_hl) + a.nvim_win_set_option(km_win_id, "winblend", opts.winblend) + a.nvim_win_set_option(km_win_id, "foldenable", false) + + vim.api.nvim_create_autocmd("BufLeave", { + buffer = km_buf, + once = true, + callback = function() + pcall(vim.api.nvim_win_close, km_win_id, true) + pcall(vim.api.nvim_win_close, km_opts.border.win_id, true) + require("telescope.utils").buf_delete(km_buf) + end, + }) + + a.nvim_buf_set_lines(km_buf, 0, -1, false, utils.repeated_table(opts.num_rows + 2 * opts.line_padding, column_indent)) + + local keymap_highlights = a.nvim_create_namespace "telescope_whichkey" + local highlights = {} + for index, mapping in ipairs(mappings) do + local row = utils.cycle(index, opts.num_rows) - 1 + opts.line_padding + local prev_line = a.nvim_buf_get_lines(km_buf, row, row + 1, false)[1] + if index == total_available_entries and total_available_entries > #mappings then + local new_line = prev_line .. "..." + a.nvim_buf_set_lines(km_buf, row, row + 1, false, { new_line }) + break + end + local display, display_hl = make_display(mapping) + local new_line = prev_line .. display .. opts.column_padding -- incl. padding + a.nvim_buf_set_lines(km_buf, row, row + 1, false, { new_line }) + table.insert(highlights, { hl = display_hl, row = row, col = #prev_line }) + end + + -- highlighting only after line setting as vim.api.nvim_buf_set_lines removes hl otherwise + for _, highlight_tbl in pairs(highlights) do + local highlight = highlight_tbl.hl + local row_ = highlight_tbl.row + local col = highlight_tbl.col + for _, hl_block in ipairs(highlight) do + a.nvim_buf_add_highlight(km_buf, keymap_highlights, hl_block[2], row_, col + hl_block[1][1], col + hl_block[1][2]) + end + end + + -- if close_with_action is true, close the which_key window when any action is triggered + -- otherwise close the window when the prompt buffer is closed + local close_event, close_pattern, close_buffer + if opts.close_with_action then + close_event, close_pattern, close_buffer = "User", "TelescopeKeymap", nil + else + close_event, close_pattern, close_buffer = "BufWinLeave", nil, prompt_bufnr + end + -- only set up autocommand after showing preview completed + vim.schedule(function() + vim.api.nvim_create_autocmd(close_event, { + pattern = close_pattern, + buffer = close_buffer, + once = true, + callback = function() + vim.schedule(function() + pcall(vim.api.nvim_win_close, km_win_id, true) + pcall(vim.api.nvim_win_close, km_opts.border.win_id, true) + utils.buf_delete(km_buf) + end) + end, + }) + end) +end + +--- Move from a none fuzzy search to a fuzzy one
+--- This action is meant to be used in live_grep and lsp_dynamic_workspace_symbols +---@param prompt_bufnr number: The prompt bufnr +actions.to_fuzzy_refine = function(prompt_bufnr) + local line = action_state.get_current_line() + local opts = (function() + local opts = { + sorter = conf.generic_sorter {}, + } + + local title = action_state.get_current_picker(prompt_bufnr).prompt_title + if title == "Live Grep" then + opts.prefix = "Find Word" + elseif title == "LSP Dynamic Workspace Symbols" then + opts.prefix = "LSP Workspace Symbols" + opts.sorter = conf.prefilter_sorter { + tag = "symbol_type", + sorter = opts.sorter, + } + else + opts.prefix = "Fuzzy over" + end + + return opts + end)() + + require("telescope.actions.generate").refine(prompt_bufnr, { + prompt_title = string.format("%s (%s)", opts.prefix, line), + sorter = opts.sorter, + }) +end + +--- Delete the selected mark or all the marks selected using multi selection. +---@param prompt_bufnr number: The prompt bufnr +actions.delete_mark = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:delete_selection(function(selection) + local bufname = selection.filename + local bufnr = vim.fn.bufnr(bufname) + local mark = selection.ordinal:sub(1, 1) + + local success + if mark:match "%u" then + success = pcall(vim.api.nvim_del_mark, mark) + else + success = pcall(vim.api.nvim_buf_del_mark, bufnr, mark) + end + return success + end) +end + +--- Insert the word under the cursor of the original (pre-Telescope) window +---@param prompt_bufnr number: The prompt bufnr +actions.insert_original_cword = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_prompt(current_picker.original_cword, false) +end + +actions.nop = function(_) end + +actions.mouse_click = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + + local pos = vim.fn.getmousepos() + if pos.winid == picker.results_win then + vim.schedule(function() + picker:set_selection(pos.line - 1) + end) + elseif pos.winid == picker.preview_win then + vim.schedule(function() + actions.select_default(prompt_bufnr) + end) + end + return "" +end + +actions.double_mouse_click = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + + local pos = vim.fn.getmousepos() + if pos.winid == picker.results_win then + vim.schedule(function() + picker:set_selection(pos.line - 1) + actions.select_default(prompt_bufnr) + end) + end + return "" +end + +-- ================================================== +-- Transforms modules and sets the correct metatables. +-- ================================================== +actions = transform_mod(actions) +return actions diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/layout.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/layout.lua new file mode 100644 index 00000000..18afb3b7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/layout.lua @@ -0,0 +1,152 @@ +---@tag telescope.actions.layout +---@config { ["module"] = "telescope.actions.layout", ["name"] = "ACTIONS_LAYOUT" } + +---@brief [[ +--- The layout actions are actions to be used to change the layout of a picker. +---@brief ]] + +local action_state = require "telescope.actions.state" +local state = require "telescope.state" +local layout_strats = require "telescope.pickers.layout_strategies" + +local transform_mod = require("telescope.actions.mt").transform_mod + +local action_layout = setmetatable({}, { + __index = function(_, k) + error("'telescope.actions.layout' does not have a value: " .. tostring(k)) + end, +}) + +--- Toggle preview window. +--- - Note: preview window can be toggled even if preview is set to false. +--- +--- This action is not mapped by default. +---@param prompt_bufnr number: The prompt bufnr +action_layout.toggle_preview = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + local status = state.get_status(picker.prompt_bufnr) + + local preview_winid = status.layout.preview and status.layout.preview.winid + if picker.previewer and preview_winid then + picker.hidden_previewer = picker.previewer + picker.previewer = nil + elseif picker.hidden_previewer and not preview_winid then + picker.previewer = picker.hidden_previewer + picker.hidden_previewer = nil + else + return + end + picker:full_layout_update() +end + +-- TODO IMPLEMENT (mentored project available, contact @l-kershaw) +action_layout.toggle_padding = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + -- if padding ~= 0 + -- 1. Save `height` and `width` of picker + -- 2. Set both to `{padding = 0}` + -- else + -- 1. Lookup previous `height` and `width` of picker + -- 2. Set both to previous values + picker:full_layout_update() +end + +--- Toggles the `prompt_position` option between "top" and "bottom". +--- Checks if `prompt_position` is an option for the current layout. +--- +--- This action is not mapped by default. +---@param prompt_bufnr number: The prompt bufnr +action_layout.toggle_prompt_position = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + picker.layout_config = picker.layout_config or {} + picker.layout_config[picker.layout_strategy] = picker.layout_config[picker.layout_strategy] or {} + -- flex layout is weird and needs handling separately + if picker.layout_strategy == "flex" then + picker.layout_config.flex.horizontal = picker.layout_config.flex.horizontal or {} + picker.layout_config.flex.vertical = picker.layout_config.flex.vertical or {} + local old_pos = vim.F.if_nil( + picker.layout_config.flex[picker.__flex_strategy].prompt_position, + picker.layout_config[picker.__flex_strategy].prompt_position + ) + local new_pos = old_pos == "top" and "bottom" or "top" + picker.layout_config[picker.__flex_strategy].prompt_position = new_pos + picker.layout_config.flex[picker.__flex_strategy].prompt_position = new_pos + picker:full_layout_update() + elseif layout_strats._configurations[picker.layout_strategy].prompt_position then + if picker.layout_config.prompt_position == "top" then + picker.layout_config.prompt_position = "bottom" + picker.layout_config[picker.layout_strategy].prompt_position = "bottom" + else + picker.layout_config.prompt_position = "top" + picker.layout_config[picker.layout_strategy].prompt_position = "top" + end + picker:full_layout_update() + end +end + +--- Toggles the `mirror` option between `true` and `false`. +--- Checks if `mirror` is an option for the current layout. +--- +--- This action is not mapped by default. +---@param prompt_bufnr number: The prompt bufnr +action_layout.toggle_mirror = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + -- flex layout is weird and needs handling separately + if picker.layout_strategy == "flex" then + picker.layout_config.flex.horizontal = picker.layout_config.flex.horizontal or {} + picker.layout_config.flex.vertical = picker.layout_config.flex.vertical or {} + local new_mirror = not picker.layout_config.flex[picker.__flex_strategy].mirror + picker.layout_config[picker.__flex_strategy].mirror = new_mirror + picker.layout_config.flex[picker.__flex_strategy].mirror = new_mirror + picker:full_layout_update() + elseif layout_strats._configurations[picker.layout_strategy].mirror then + picker.layout_config = picker.layout_config or {} + local new_mirror = not picker.layout_config.mirror + picker.layout_config.mirror = new_mirror + picker.layout_config[picker.layout_strategy] = picker.layout_config[picker.layout_strategy] or {} + picker.layout_config[picker.layout_strategy].mirror = new_mirror + picker:full_layout_update() + end +end + +-- Helper function for `cycle_layout_next` and `cycle_layout_prev`. +local get_cycle_layout = function(dir) + return function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + if picker.__layout_index then + picker.__layout_index = ((picker.__layout_index + dir - 1) % #picker.__cycle_layout_list) + 1 + else + picker.__layout_index = 1 + end + local new_layout = picker.__cycle_layout_list[picker.__layout_index] + if type(new_layout) == "string" then + picker.layout_strategy = new_layout + picker.layout_config = {} + picker.previewer = picker.all_previewers and picker.all_previewers[1] or nil + elseif type(new_layout) == "table" then + picker.layout_strategy = new_layout.layout_strategy + picker.layout_config = new_layout.layout_config or {} + picker.previewer = (new_layout.previewer == nil and picker.all_previewers[picker.current_previewer_index]) + or new_layout.previewer + else + error("Not a valid layout setup: " .. vim.inspect(new_layout) .. "\nShould be a string or a table") + end + + picker:full_layout_update() + end +end + +--- Cycles to the next layout in `cycle_layout_list`. +--- +--- This action is not mapped by default. +---@param prompt_bufnr number: The prompt bufnr +action_layout.cycle_layout_next = get_cycle_layout(1) + +--- Cycles to the previous layout in `cycle_layout_list`. +--- +--- This action is not mapped by default. +---@param prompt_bufnr number: The prompt bufnr +action_layout.cycle_layout_prev = get_cycle_layout(-1) + +action_layout = transform_mod(action_layout) +return action_layout diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/mt.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/mt.lua new file mode 100644 index 00000000..07b1e42a --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/mt.lua @@ -0,0 +1,210 @@ +local action_mt = {} + +--- Checks all replacement combinations to determine which function to run. +--- If no replacement can be found, then it will run the original function +local run_replace_or_original = function(replacements, original_func, ...) + for _, replacement_map in ipairs(replacements or {}) do + for condition, replacement in pairs(replacement_map) do + if condition == true or condition(...) then + return replacement(...) + end + end + end + + return original_func(...) +end + +local append_action_copy = function(new, v, old) + table.insert(new, v) + new._func[v] = old._func[v] + new._static_pre[v] = old._static_pre[v] + new._pre[v] = old._pre[v] + new._replacements[v] = old._replacements[v] + new._static_post[v] = old._static_post[v] + new._post[v] = old._post[v] +end + +-- TODO(conni2461): Not a fan of this solution/hack. Needs to be addressed +local all_mts = {} + +--TODO(conni2461): It gets worse. This is so bad but because we have now n mts for n actions +-- We have to check all actions for relevant mts to set replace and before, after +-- Its not bad for performance because its being called on startup when we attach mappings. +-- Its just a bad solution +local find_all_relevant_mts = function(action_name, f) + for _, mt in ipairs(all_mts) do + for fun, _ in pairs(mt._func) do + if fun == action_name then + f(mt) + end + end + end +end + +--- an action is metatable which allows replacement(prepend or append) of the function +---@class Action +---@field _func table: the original action function +---@field _static_pre table: will allways run before the function even if its replaced +---@field _pre table: the functions that will run before the action +---@field _replacements table: the function that replaces this action +---@field _static_post table: will allways run after the function even if its replaced +---@field _post table: the functions that will run after the action +action_mt.create = function() + local mt = { + __call = function(t, ...) + local values = {} + for _, action_name in ipairs(t) do + if t._static_pre[action_name] then + t._static_pre[action_name](...) + end + if vim.tbl_isempty(t._replacements) and t._pre[action_name] then + t._pre[action_name](...) + end + + local result = { + run_replace_or_original(t._replacements[action_name], t._func[action_name], ...), + } + for _, res in ipairs(result) do + table.insert(values, res) + end + + if t._static_post[action_name] then + t._static_post[action_name](...) + end + if vim.tbl_isempty(t._replacements) and t._post[action_name] then + t._post[action_name](...) + end + end + + return unpack(values) + end, + + __add = function(lhs, rhs) + local new_action = setmetatable({}, action_mt.create()) + for _, v in ipairs(lhs) do + append_action_copy(new_action, v, lhs) + end + + for _, v in ipairs(rhs) do + append_action_copy(new_action, v, rhs) + end + new_action.clear = function() + lhs.clear() + rhs.clear() + end + + return new_action + end, + + _func = {}, + _static_pre = {}, + _pre = {}, + _replacements = {}, + _static_post = {}, + _post = {}, + } + + mt.__index = mt + + mt.clear = function() + mt._pre = {} + mt._replacements = {} + mt._post = {} + end + + --- Replace the reference to the function with a new one temporarily + function mt:replace(v) + assert(#self == 1, "Cannot replace an already combined action") + + return self:replace_map { [true] = v } + end + + function mt:replace_if(condition, replacement) + assert(#self == 1, "Cannot replace an already combined action") + + return self:replace_map { [condition] = replacement } + end + + --- Replace table with + -- Example: + -- + -- actions.select:replace_map { + -- [function() return filetype == 'lua' end] = actions.file_split, + -- [function() return filetype == 'other' end] = actions.file_split_edit, + -- } + function mt:replace_map(tbl) + assert(#self == 1, "Cannot replace an already combined action") + + local action_name = self[1] + find_all_relevant_mts(action_name, function(another) + if not another._replacements[action_name] then + another._replacements[action_name] = {} + end + + table.insert(another._replacements[action_name], 1, tbl) + end) + + return self + end + + function mt:enhance(opts) + assert(#self == 1, "Cannot enhance already combined actions") + + local action_name = self[1] + find_all_relevant_mts(action_name, function(another) + if opts.pre then + another._pre[action_name] = opts.pre + end + + if opts.post then + another._post[action_name] = opts.post + end + end) + + return self + end + + table.insert(all_mts, mt) + return mt +end + +action_mt.transform = function(k, mt, _, v) + local res = setmetatable({ k }, mt) + if type(v) == "table" then + res._static_pre[k] = v.pre + res._static_post[k] = v.post + res._func[k] = v.action + else + res._func[k] = v + end + return res +end + +action_mt.transform_mod = function(mod) + -- Pass the metatable of the module if applicable. + -- This allows for custom errors, lookups, etc. + local redirect = setmetatable({}, getmetatable(mod) or {}) + + for k, v in pairs(mod) do + local mt = action_mt.create() + redirect[k] = action_mt.transform(k, mt, _, v) + end + + redirect._clear = function() + for k, v in pairs(redirect) do + if k ~= "_clear" then + pcall(v.clear) + end + end + end + + return redirect +end + +action_mt.clear_all = function() + for _, v in ipairs(all_mts) do + pcall(v.clear) + end +end + +return action_mt diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/set.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/set.lua new file mode 100644 index 00000000..c9b91f43 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/set.lua @@ -0,0 +1,316 @@ +---@tag telescope.actions.set +---@config { ["module"] = "telescope.actions.set", ["name"] = "ACTIONS_SET" } + +---@brief [[ +--- Telescope action sets are used to provide an interface for managing +--- actions that all primarily do the same thing, but with slight tweaks. +--- +--- For example, when editing files you may want it in the current split, +--- a vertical split, etc. Instead of making users have to overwrite EACH +--- of those every time they want to change this behavior, they can instead +--- replace the `set` itself and then it will work great and they're done. +---@brief ]] + +local a = vim.api + +local log = require "telescope.log" +local Path = require "plenary.path" +local state = require "telescope.state" +local utils = require "telescope.utils" + +local action_state = require "telescope.actions.state" + +local transform_mod = require("telescope.actions.mt").transform_mod + +local action_set = setmetatable({}, { + __index = function(_, k) + error("'telescope.actions.set' does not have a value: " .. tostring(k)) + end, +}) + +--- Move the current selection of a picker {change} rows. +--- Handles not overflowing / underflowing the list. +---@param prompt_bufnr number: The prompt bufnr +---@param change number: The amount to shift the selection by +action_set.shift_selection = function(prompt_bufnr, change) + local count = vim.v.count + count = count == 0 and 1 or count + count = a.nvim_get_mode().mode == "n" and count or 1 + action_state.get_current_picker(prompt_bufnr):move_selection(change * count) +end + +--- Select the current entry. This is the action set to overwrite common +--- actions by the user. +--- +--- By default maps to editing a file. +---@param prompt_bufnr number: The prompt bufnr +---@param type string: The type of selection to make +-- Valid types include: "default", "horizontal", "vertical", "tabedit" +action_set.select = function(prompt_bufnr, type) + return action_set.edit(prompt_bufnr, action_state.select_key_to_edit_key(type)) +end + +-- goal: currently we have a workaround in actions/init.lua where we do this for all files +-- action_set.select = { +-- -- Will not be called if `select_default` is replaced rather than `action_set.select` because we never get here +-- pre = function(prompt_bufnr) +-- action_state.get_current_history():append( +-- action_state.get_current_line(), +-- action_state.get_current_picker(prompt_bufnr) +-- ) +-- end, +-- action = function(prompt_bufnr, type) +-- return action_set.edit(prompt_bufnr, action_state.select_key_to_edit_key(type)) +-- end +-- } + +local edit_buffer +do + local map = { + drop = "drop", + ["tab drop"] = "tab drop", + edit = "buffer", + new = "sbuffer", + vnew = "vert sbuffer", + ["leftabove new"] = "leftabove sbuffer", + ["leftabove vnew"] = "leftabove vert sbuffer", + ["rightbelow new"] = "rightbelow sbuffer", + ["rightbelow vnew"] = "rightbelow vert sbuffer", + ["topleft new"] = "topleft sbuffer", + ["topleft vnew"] = "topleft vert sbuffer", + ["botright new"] = "botright sbuffer", + ["botright vnew"] = "botright vert sbuffer", + tabedit = "tab sb", + } + + edit_buffer = function(command, bufnr) + local buf_command = map[command] + if buf_command == nil then + local valid_commands = vim.tbl_map(function(cmd) + return string.format("%q", cmd) + end, vim.tbl_keys(map)) + table.sort(valid_commands) + error( + string.format( + "There was no associated buffer command for %q.\nValid commands are: %s.", + command, + table.concat(valid_commands, ", ") + ) + ) + end + if buf_command ~= "drop" and buf_command ~= "tab drop" then + vim.cmd(string.format("%s %d", buf_command, bufnr)) + else + vim.cmd(string.format("%s %s", buf_command, vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr)))) + end + end +end + +--- Edit a file based on the current selection. +---@param prompt_bufnr number: The prompt bufnr +---@param command string: The command to use to open the file. +-- Valid commands are: +-- - "edit" +-- - "new" +-- - "vedit" +-- - "tabedit" +-- - "drop" +-- - "tab drop" +-- - "leftabove new" +-- - "leftabove vnew" +-- - "rightbelow new" +-- - "rightbelow vnew" +-- - "topleft new" +-- - "topleft vnew" +-- - "botright new" +-- - "botright vnew" +action_set.edit = function(prompt_bufnr, command) + local entry = action_state.get_selected_entry() + + if not entry then + utils.notify("actions.set.edit", { + msg = "Nothing currently selected", + level = "WARN", + }) + return + end + + local filename, row, col + + if entry.path or entry.filename then + filename = entry.path or entry.filename + + -- TODO: Check for off-by-one + row = entry.row or entry.lnum + col = entry.col + elseif not entry.bufnr then + -- TODO: Might want to remove this and force people + -- to put stuff into `filename` + local value = entry.value + if not value then + utils.notify("actions.set.edit", { + msg = "Could not do anything with blank line...", + level = "WARN", + }) + return + end + + if type(value) == "table" then + value = entry.display + end + + local sections = vim.split(value, ":") + + filename = sections[1] + row = tonumber(sections[2]) + col = tonumber(sections[3]) + end + + local entry_bufnr = entry.bufnr + + local picker = action_state.get_current_picker(prompt_bufnr) + require("telescope.pickers").on_close_prompt(prompt_bufnr) + pcall(vim.api.nvim_set_current_win, picker.original_win_id) + local win_id = picker.get_selection_window(picker, entry) + + if picker.push_cursor_on_edit then + vim.cmd "normal! m'" + end + + if picker.push_tagstack_on_edit then + local from = { vim.fn.bufnr "%", vim.fn.line ".", vim.fn.col ".", 0 } + local items = { { tagname = vim.fn.expand "", from = from } } + vim.fn.settagstack(vim.fn.win_getid(), { items = items }, "t") + end + + if win_id ~= 0 and a.nvim_get_current_win() ~= win_id then + vim.api.nvim_set_current_win(win_id) + end + + if entry_bufnr then + if not vim.api.nvim_buf_get_option(entry_bufnr, "buflisted") then + vim.api.nvim_buf_set_option(entry_bufnr, "buflisted", true) + end + edit_buffer(command, entry_bufnr) + else + -- check if we didn't pick a different buffer + -- prevents restarting lsp server + if vim.api.nvim_buf_get_name(0) ~= filename or command ~= "edit" then + filename = Path:new(filename):normalize(vim.loop.cwd()) + pcall(vim.cmd, string.format("%s %s", command, vim.fn.fnameescape(filename))) + end + end + + -- HACK: fixes folding: https://github.com/nvim-telescope/telescope.nvim/issues/699 + if vim.wo.foldmethod == "expr" then + vim.schedule(function() + vim.opt.foldmethod = "expr" + end) + end + + local pos = vim.api.nvim_win_get_cursor(0) + if col == nil then + if row == pos[1] then + col = pos[2] + 1 + elseif row == nil then + row, col = pos[1], pos[2] + 1 + else + col = 1 + end + end + + if row and col then + local ok, err_msg = pcall(a.nvim_win_set_cursor, 0, { row, col }) + if not ok then + log.debug("Failed to move to cursor:", err_msg, row, col) + end + end +end + +---@param prompt_bufnr integer +---@return table? previewer +---@return number? speed +local __scroll_previewer = function(prompt_bufnr) + local previewer = action_state.get_current_picker(prompt_bufnr).previewer + local status = state.get_status(prompt_bufnr) + local preview_winid = status.layout.preview and status.layout.preview.winid + + -- Check if we actually have a previewer and a preview window + if type(previewer) ~= "table" or not preview_winid then + return + end + + local default_speed = vim.api.nvim_win_get_height(preview_winid) / 2 + local speed = status.picker.layout_config.scroll_speed or default_speed + return previewer, speed +end + +--- Scrolls the previewer up or down. +--- Defaults to a half page scroll, but can be overridden using the `scroll_speed` +--- option in `layout_config`. See |telescope.layout| for more details. +---@param prompt_bufnr number: The prompt bufnr +---@param direction number: The direction of the scrolling +-- Valid directions include: "1", "-1" +action_set.scroll_previewer = function(prompt_bufnr, direction) + local previewer, speed = __scroll_previewer(prompt_bufnr) + if previewer and previewer.scroll_fn then + previewer:scroll_fn(math.floor(speed * direction)) + end +end + +--- Scrolls the previewer to the left or right. +--- Defaults to a half page scroll, but can be overridden using the `scroll_speed` +--- option in `layout_config`. See |telescope.layout| for more details. +---@param prompt_bufnr number: The prompt bufnr +---@param direction number: The direction of the scrolling +-- Valid directions include: "1", "-1" +action_set.scroll_horizontal_previewer = function(prompt_bufnr, direction) + local previewer, speed = __scroll_previewer(prompt_bufnr) + if previewer and previewer.scroll_horizontal_fn then + previewer:scroll_horizontal_fn(math.floor(speed * direction)) + end +end + +--- Scrolls the results up or down. +--- Defaults to a half page scroll, but can be overridden using the `scroll_speed` +--- option in `layout_config`. See |telescope.layout| for more details. +---@param prompt_bufnr number: The prompt bufnr +---@param direction number: The direction of the scrolling +-- Valid directions include: "1", "-1" +action_set.scroll_results = function(prompt_bufnr, direction) + local status = state.get_status(prompt_bufnr) + local default_speed = vim.api.nvim_win_get_height(status.layout.results.winid) / 2 + local speed = status.picker.layout_config.scroll_speed or default_speed + + local input = direction > 0 and [[]] or [[]] + + vim.api.nvim_win_call(status.layout.results.winid, function() + vim.cmd([[normal! ]] .. math.floor(speed) .. input) + end) + + action_set.shift_selection(prompt_bufnr, math.floor(speed) * direction) +end + +--- Scrolls the results to the left or right. +--- Defaults to a half page scroll, but can be overridden using the `scroll_speed` +--- option in `layout_config`. See |telescope.layout| for more details. +---@param prompt_bufnr number: The prompt bufnr +---@param direction number: The direction of the scrolling +-- Valid directions include: "1", "-1" +action_set.scroll_horizontal_results = function(prompt_bufnr, direction) + local status = state.get_status(prompt_bufnr) + local default_speed = vim.api.nvim_win_get_height(status.results_win) / 2 + local speed = status.picker.layout_config.scroll_speed or default_speed + + local input = direction > 0 and [[zl]] or [[zh]] + + vim.api.nvim_win_call(status.results_win, function() + vim.cmd([[normal! ]] .. math.floor(speed) .. input) + end) +end + +-- ================================================== +-- Transforms modules and sets the corect metatables. +-- ================================================== +action_set = transform_mod(action_set) +return action_set diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/state.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/state.lua new file mode 100644 index 00000000..539d97c8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/state.lua @@ -0,0 +1,58 @@ +---@tag telescope.actions.state +---@config { ["module"] = "telescope.actions.state", ["name"] = "ACTIONS_STATE" } + +---@brief [[ +--- Functions to be used to determine the current state of telescope. +--- +--- Generally used from within other |telescope.actions| +---@brief ]] + +local global_state = require "telescope.state" +local conf = require("telescope.config").values + +local action_state = {} + +--- Get the current entry +function action_state.get_selected_entry() + return global_state.get_global_key "selected_entry" +end + +--- Gets the current line in the search prompt +function action_state.get_current_line() + return global_state.get_global_key "current_line" or "" +end + +--- Gets the current picker +---@param prompt_bufnr number: The prompt bufnr +function action_state.get_current_picker(prompt_bufnr) + return global_state.get_status(prompt_bufnr).picker +end + +local select_to_edit_map = { + default = "edit", + horizontal = "new", + vertical = "vnew", + tab = "tabedit", + drop = "drop", + ["tab drop"] = "tab drop", +} +function action_state.select_key_to_edit_key(type) + return select_to_edit_map[type] +end + +function action_state.get_current_history() + local history = global_state.get_global_key "history" + if not history then + if conf.history == false or type(conf.history) ~= "table" then + history = require("telescope.actions.history").get_simple_history() + global_state.set_global_key("history", history) + else + history = conf.history.handler() + global_state.set_global_key("history", history) + end + end + + return history +end + +return action_state diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/utils.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/utils.lua new file mode 100644 index 00000000..81bd870b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/actions/utils.lua @@ -0,0 +1,150 @@ +---@tag telescope.actions.utils +---@config { ["module"] = "telescope.actions.utils", ["name"] = "ACTIONS_UTILS" } + +---@brief [[ +--- Utilities to wrap functions around picker selections and entries. +--- +--- Generally used from within other |telescope.actions| +---@brief ]] + +local action_state = require "telescope.actions.state" + +local utils = {} + +--- Apply `f` to the entries of the current picker. +--- - Notes: +--- - Mapped entries include all currently filtered results, not just the visible ones. +--- - Indices are 1-indexed, whereas rows are 0-indexed. +--- - Warning: `map_entries` has no return value. +--- - The below example showcases how to collect results +--- +--- Usage: +--- +--- local action_state = require "telescope.actions.state" +--- local action_utils = require "telescope.actions.utils" +--- function entry_value_by_row() +--- local prompt_bufnr = vim.api.nvim_get_current_buf() +--- local current_picker = action_state.get_current_picker(prompt_bufnr) +--- local results = {} +--- action_utils.map_entries(prompt_bufnr, function(entry, index, row) +--- results[row] = entry.value +--- end) +--- return results +--- end +--- +---@param prompt_bufnr number: The prompt bufnr +---@param f function: Function to map onto entries of picker that takes (entry, index, row) as viable arguments +function utils.map_entries(prompt_bufnr, f) + vim.validate { + f = { f, "function" }, + } + local current_picker = action_state.get_current_picker(prompt_bufnr) + local index = 1 + -- indices are 1-indexed, rows are 0-indexed + for entry in current_picker.manager:iter() do + local row = current_picker:get_row(index) + f(entry, index, row) + index = index + 1 + end +end + +--- Apply `f` to the multi selections of the current picker and return a table of mapped selections. +--- - Notes: +--- - Mapped selections may include results not visible in the results pop up. +--- - Selected entries are returned in order of their selection. +--- - Warning: `map_selections` has no return value. +--- - The below example showcases how to collect results +--- +--- Usage: +--- +--- local action_state = require "telescope.actions.state" +--- local action_utils = require "telescope.actions.utils" +--- function selection_by_index() +--- local prompt_bufnr = vim.api.nvim_get_current_buf() +--- local current_picker = action_state.get_current_picker(prompt_bufnr) +--- local results = {} +--- action_utils.map_selections(prompt_bufnr, function(entry, index) +--- results[index] = entry.value +--- end) +--- return results +--- end +--- +---@param prompt_bufnr number: The prompt bufnr +---@param f function: Function to map onto selection of picker that takes (selection) as a viable argument +function utils.map_selections(prompt_bufnr, f) + vim.validate { + f = { f, "function" }, + } + local current_picker = action_state.get_current_picker(prompt_bufnr) + for _, selection in ipairs(current_picker:get_multi_selection()) do + f(selection) + end +end + +--- Utility to collect mappings of prompt buffer in array of `{mode, keybind, name}`. +---@param prompt_bufnr number: The prompt bufnr +function utils.get_registered_mappings(prompt_bufnr) + local ret = {} + for _, mode in ipairs { "n", "i" } do + for _, mapping in ipairs(vim.api.nvim_buf_get_keymap(prompt_bufnr, mode)) do + -- ensure only telescope mappings + if mapping.desc then + if mapping.desc:sub(1, 10) == "telescope|" then + table.insert(ret, { mode = mode, keybind = mapping.lhs, desc = mapping.desc:sub(11) }) + elseif mapping.desc:sub(1, 11) == "telescopej|" then + local fname = utils._get_anon_function_name(vim.json.decode(mapping.desc:sub(12))) + fname = fname:lower() == mapping.lhs:lower() and "" or fname + table.insert(ret, { + mode = mode, + keybind = mapping.lhs, + desc = fname, + }) + end + end + end + end + return ret +end + +-- Best effort to infer function names for actions.which_key +function utils._get_anon_function_name(info) + local Path = require "plenary.path" + local fname + -- if fn defined in string (ie loadstring) source is string + -- if fn defined in file, source is file name prefixed with a `@´ + local path = Path:new((info.source:gsub("@", ""))) + if not path:exists() then + return "" + end + for i, line in ipairs(path:readlines()) do + if i == info.linedefined then + fname = line + break + end + end + + -- test if assignment or named function, otherwise anon + if (fname:match "=" == nil) and (fname:match "function %S+%(" == nil) then + return "" + else + local patterns = { + { "function", "" }, -- remove function + { "local", "" }, -- remove local + { "[%s=]", "" }, -- remove whitespace and = + { [=[%[["']]=], "" }, -- remove left-hand bracket of table assignment + { [=[["']%]]=], "" }, -- remove right-ahnd bracket of table assignment + { "%((.+)%)", "" }, -- remove function arguments + { "(.+)%.", "" }, -- remove TABLE. prefix if available + } + for _, tbl in ipairs(patterns) do + fname = (fname:gsub(tbl[1], tbl[2])) -- make sure only string is returned + end + -- not sure if this can happen, catch all just in case + if fname == nil or fname == "" then + return "" + end + return fname + end +end + +return utils diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/algos/fzy.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/algos/fzy.lua new file mode 100644 index 00000000..bf322ab4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/algos/fzy.lua @@ -0,0 +1,197 @@ +-- The fzy matching algorithm +-- +-- by Seth Warn +-- a lua port of John Hawthorn's fzy +-- +-- > fzy tries to find the result the user intended. It does this by favouring +-- > matches on consecutive letters and starts of words. This allows matching +-- > using acronyms or different parts of the path." - J Hawthorn + +local has_path, Path = pcall(require, "plenary.path") +if not has_path then + Path = { + path = { + separator = "/", + }, + } +end + +local SCORE_GAP_LEADING = -0.005 +local SCORE_GAP_TRAILING = -0.005 +local SCORE_GAP_INNER = -0.01 +local SCORE_MATCH_CONSECUTIVE = 1.0 +local SCORE_MATCH_SLASH = 0.9 +local SCORE_MATCH_WORD = 0.8 +local SCORE_MATCH_CAPITAL = 0.7 +local SCORE_MATCH_DOT = 0.6 +local SCORE_MAX = math.huge +local SCORE_MIN = -math.huge +local MATCH_MAX_LENGTH = 1024 + +local fzy = {} + +function fzy.has_match(needle, haystack) + needle = string.lower(needle) + haystack = string.lower(haystack) + + local j = 1 + for i = 1, string.len(needle) do + j = string.find(haystack, needle:sub(i, i), j, true) + if not j then + return false + else + j = j + 1 + end + end + + return true +end + +local function is_lower(c) + return c:match "%l" +end + +local function is_upper(c) + return c:match "%u" +end + +local function precompute_bonus(haystack) + local match_bonus = {} + + local last_char = Path.path.sep + for i = 1, string.len(haystack) do + local this_char = haystack:sub(i, i) + if last_char == Path.path.sep then + match_bonus[i] = SCORE_MATCH_SLASH + elseif last_char == "-" or last_char == "_" or last_char == " " then + match_bonus[i] = SCORE_MATCH_WORD + elseif last_char == "." then + match_bonus[i] = SCORE_MATCH_DOT + elseif is_lower(last_char) and is_upper(this_char) then + match_bonus[i] = SCORE_MATCH_CAPITAL + else + match_bonus[i] = 0 + end + + last_char = this_char + end + + return match_bonus +end + +local function compute(needle, haystack, D, M) + local match_bonus = precompute_bonus(haystack) + local n = string.len(needle) + local m = string.len(haystack) + local lower_needle = string.lower(needle) + local lower_haystack = string.lower(haystack) + + -- Because lua only grants access to chars through substring extraction, + -- get all the characters from the haystack once now, to reuse below. + local haystack_chars = {} + for i = 1, m do + haystack_chars[i] = lower_haystack:sub(i, i) + end + + for i = 1, n do + D[i] = {} + M[i] = {} + + local prev_score = SCORE_MIN + local gap_score = i == n and SCORE_GAP_TRAILING or SCORE_GAP_INNER + local needle_char = lower_needle:sub(i, i) + + for j = 1, m do + if needle_char == haystack_chars[j] then + local score = SCORE_MIN + if i == 1 then + score = ((j - 1) * SCORE_GAP_LEADING) + match_bonus[j] + elseif j > 1 then + local a = M[i - 1][j - 1] + match_bonus[j] + local b = D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE + score = math.max(a, b) + end + D[i][j] = score + prev_score = math.max(score, prev_score + gap_score) + M[i][j] = prev_score + else + D[i][j] = SCORE_MIN + prev_score = prev_score + gap_score + M[i][j] = prev_score + end + end + end +end + +function fzy.score(needle, haystack) + local n = string.len(needle) + local m = string.len(haystack) + + if n == 0 or m == 0 or m > MATCH_MAX_LENGTH or n > MATCH_MAX_LENGTH then + return SCORE_MIN + elseif n == m then + return SCORE_MAX + else + local D = {} + local M = {} + compute(needle, haystack, D, M) + return M[n][m] + end +end + +function fzy.positions(needle, haystack) + local n = string.len(needle) + local m = string.len(haystack) + + if n == 0 or m == 0 or m > MATCH_MAX_LENGTH or n > MATCH_MAX_LENGTH then + return {} + elseif n == m then + local consecutive = {} + for i = 1, n do + consecutive[i] = i + end + return consecutive + end + + local D = {} + local M = {} + compute(needle, haystack, D, M) + + local positions = {} + local match_required = false + local j = m + for i = n, 1, -1 do + while j >= 1 do + if D[i][j] ~= SCORE_MIN and (match_required or D[i][j] == M[i][j]) then + match_required = (i ~= 1) and (j ~= 1) and (M[i][j] == D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE) + positions[i] = j + j = j - 1 + break + else + j = j - 1 + end + end + end + + return positions +end + +-- If strings a or b are empty or too long, `fzy.score(a, b) == fzy.get_score_min()`. +function fzy.get_score_min() + return SCORE_MIN +end + +-- For exact matches, `fzy.score(s, s) == fzy.get_score_max()`. +function fzy.get_score_max() + return SCORE_MAX +end + +-- For all strings a and b that +-- - are not covered by either `fzy.get_score_min()` or fzy.get_score_max()`, and +-- - are matched, such that `fzy.has_match(a, b) == true`, +-- then `fzy.score(a, b) > fzy.get_score_floor()` will be true. +function fzy.get_score_floor() + return (MATCH_MAX_LENGTH + 1) * SCORE_GAP_INNER +end + +return fzy diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/algos/linked_list.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/algos/linked_list.lua new file mode 100644 index 00000000..2da6a6e1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/algos/linked_list.lua @@ -0,0 +1,255 @@ +local LinkedList = {} +LinkedList.__index = LinkedList + +function LinkedList:new(opts) + opts = opts or {} + local track_at = opts.track_at + + return setmetatable({ + size = 0, + head = false, + tail = false, + + -- track_at: Track at can track a particular node + -- Use to keep a node tracked at a particular index + -- This greatly decreases looping for checking values at this location. + track_at = track_at, + _tracked_node = nil, + tracked = nil, + }, self) +end + +function LinkedList:_increment() + self.size = self.size + 1 + return self.size +end + +local create_node = function(item) + return { + item = item, + } +end + +function LinkedList:append(item) + local final_size = self:_increment() + + local node = create_node(item) + + if not self.head then + self.head = node + end + + if self.tail then + self.tail.next = node + node.prev = self.tail + end + + self.tail = node + + if self.track_at then + if final_size == self.track_at then + self.tracked = item + self._tracked_node = node + end + end +end + +function LinkedList:prepend(item) + local final_size = self:_increment() + local node = create_node(item) + + if not self.tail then + self.tail = node + end + + if self.head then + self.head.prev = node + node.next = self.head + end + + self.head = node + + if self.track_at then + if final_size == self.track_at then + self._tracked_node = self.tail + elseif final_size > self.track_at then + self._tracked_node = self._tracked_node.prev + else + return + end + + self.tracked = self._tracked_node.item + end +end + +-- [a, b, c] +-- b.prev = a +-- b.next = c +-- +-- a.next = b +-- c.prev = c +-- +-- insert d after b +-- [a, b, d, c] +-- +-- b.next = d +-- b.prev = a +-- +-- Place "item" after "node" (which is at index `index`) +function LinkedList:place_after(index, node, item) + local new_node = create_node(item) + + assert(node.prev ~= node) + assert(node.next ~= node) + local final_size = self:_increment() + + -- Update tail to be the next node. + if self.tail == node then + self.tail = new_node + end + + new_node.prev = node + new_node.next = node.next + + node.next = new_node + + if new_node.prev then + new_node.prev.next = new_node + end + + if new_node.next then + new_node.next.prev = new_node + end + + if self.track_at then + if index == self.track_at then + self._tracked_node = new_node + elseif index < self.track_at then + if final_size == self.track_at then + self._tracked_node = self.tail + elseif final_size > self.track_at then + self._tracked_node = self._tracked_node.prev + else + return + end + end + + self.tracked = self._tracked_node.item + end +end + +function LinkedList:place_before(index, node, item) + local new_node = create_node(item) + + assert(node.prev ~= node) + assert(node.next ~= node) + local final_size = self:_increment() + + -- Update head to be the node we are inserting. + if self.head == node then + self.head = new_node + end + + new_node.prev = node.prev + new_node.next = node + + node.prev = new_node + -- node.next = node.next + + if new_node.prev then + new_node.prev.next = new_node + end + + if new_node.next then + new_node.next.prev = new_node + end + + if self.track_at then + if index == self.track_at - 1 then + self._tracked_node = node + elseif index < self.track_at then + if final_size == self.track_at then + self._tracked_node = self.tail + elseif final_size > self.track_at then + self._tracked_node = self._tracked_node.prev + else + return + end + end + + self.tracked = self._tracked_node.item + end +end + +-- Do you even do this in linked lists...? +-- function LinkedList:remove(item) +-- end + +function LinkedList:iter() + local current_node = self.head + + return function() + local node = current_node + if not node then + return nil + end + + current_node = current_node.next + return node.item + end +end + +function LinkedList:ipairs() + local index = 0 + local current_node = self.head + + return function() + local node = current_node + if not node then + return nil + end + + current_node = current_node.next + index = index + 1 + return index, node.item, node + end +end + +function LinkedList:truncate(max_results) + if max_results >= self.size then + return + end + + local current_node + if max_results < self.size - max_results then + local index = 1 + current_node = self.head + while index < max_results do + local node = current_node + if not node.next then + break + end + current_node = current_node.next + index = index + 1 + end + self.size = max_results + else + current_node = self.tail + while self.size > max_results do + if current_node.prev == nil then + break + end + current_node = current_node.prev + self.size = self.size - 1 + end + end + self.tail = current_node + self.tail.next = nil + if max_results < self.track_at then + self.track_at = max_results + self.tracked = current_node.item + self._tracked_node = current_node + end +end + +return LinkedList diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/algos/string_distance.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/algos/string_distance.lua new file mode 100644 index 00000000..c2c5eade --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/algos/string_distance.lua @@ -0,0 +1,52 @@ +local function min(a, b, c) + local min_val = a + + if b < min_val then + min_val = b + end + if c < min_val then + min_val = c + end + + return min_val +end + +---------------------------------- +--- Levenshtein distance function. +-- @tparam string s1 +-- @tparam string s2 +-- @treturn number the levenshtein distance +-- @within Metrics +return function(s1, s2) + if s1 == s2 then + return 0 + end + if s1:len() == 0 then + return s2:len() + end + if s2:len() == 0 then + return s1:len() + end + if s1:len() < s2:len() then + s1, s2 = s2, s1 + end + + local t = {} + for i = 1, #s1 + 1 do + t[i] = { i - 1 } + end + + for i = 1, #s2 + 1 do + t[1][i] = i - 1 + end + + local cost + for i = 2, #s1 + 1 do + for j = 2, #s2 + 1 do + cost = (s1:sub(i - 1, i - 1) == s2:sub(j - 1, j - 1) and 0) or 1 + t[i][j] = min(t[i - 1][j] + 1, t[i][j - 1] + 1, t[i - 1][j - 1] + cost) + end + end + + return t[#s1 + 1][#s2 + 1] +end diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__diagnostics.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__diagnostics.lua new file mode 100644 index 00000000..e55a130c --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__diagnostics.lua @@ -0,0 +1,188 @@ +local conf = require("telescope.config").values +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local pickers = require "telescope.pickers" +local utils = require "telescope.utils" + +local diagnostics = {} + +local sorting_comparator = function(opts) + local current_buf = vim.api.nvim_get_current_buf() + local comparators = { + -- sort results by bufnr (prioritize cur buf), severity, lnum + buffer = function(a, b) + if a.bufnr == b.bufnr then + if a.type == b.type then + return a.lnum < b.lnum + else + return a.type < b.type + end + else + if a.bufnr == current_buf then + return true + end + if b.bufnr == current_buf then + return false + end + return a.bufnr < b.bufnr + end + end, + severity = function(a, b) + if a.type < b.type then + return true + elseif a.type > b.type then + return false + end + + if a.bufnr == b.bufnr then + return a.lnum < b.lnum + elseif a.bufnr == current_buf then + return true + elseif b.bufnr == current_buf then + return false + else + return a.bufnr < b.bufnr + end + end, + } + + local sort_by = vim.F.if_nil(opts.sort_by, "buffer") + return comparators[sort_by] +end + +local convert_diagnostic_type = function(severities, severity) + -- convert from string to int + if type(severity) == "string" then + -- make sure that e.g. error is uppercased to ERROR + return severities[severity:upper()] + end + -- otherwise keep original value, incl. nil + return severity +end + +local diagnostics_to_tbl = function(opts) + opts = vim.F.if_nil(opts, {}) + local items = {} + local severities = vim.diagnostic.severity + + opts.severity = convert_diagnostic_type(severities, opts.severity) + opts.severity_limit = convert_diagnostic_type(severities, opts.severity_limit) + opts.severity_bound = convert_diagnostic_type(severities, opts.severity_bound) + + local diagnosis_opts = { severity = {}, namespace = opts.namespace } + if opts.severity ~= nil then + if opts.severity_limit ~= nil or opts.severity_bound ~= nil then + utils.notify("builtin.diagnostics", { + msg = "Invalid severity parameters. Both a specific severity and a limit/bound is not allowed", + level = "ERROR", + }) + return {} + end + diagnosis_opts.severity = opts.severity + else + if opts.severity_limit ~= nil then + diagnosis_opts.severity["min"] = opts.severity_limit + end + if opts.severity_bound ~= nil then + diagnosis_opts.severity["max"] = opts.severity_bound + end + if vim.version().minor > 9 and vim.tbl_isempty(diagnosis_opts.severity) then + diagnosis_opts.severity = nil + end + end + + opts.root_dir = opts.root_dir == true and vim.loop.cwd() or opts.root_dir + + local bufnr_name_map = {} + local filter_diag = function(diagnostic) + if bufnr_name_map[diagnostic.bufnr] == nil then + bufnr_name_map[diagnostic.bufnr] = vim.api.nvim_buf_get_name(diagnostic.bufnr) + end + + local root_dir_test = not opts.root_dir + or string.sub(bufnr_name_map[diagnostic.bufnr], 1, #opts.root_dir) == opts.root_dir + local listed_test = not opts.no_unlisted or vim.api.nvim_buf_get_option(diagnostic.bufnr, "buflisted") + + return root_dir_test and listed_test + end + + local preprocess_diag = function(diagnostic) + return { + bufnr = diagnostic.bufnr, + filename = bufnr_name_map[diagnostic.bufnr], + lnum = diagnostic.lnum + 1, + col = diagnostic.col + 1, + text = vim.trim(diagnostic.message:gsub("[\n]", "")), + type = severities[diagnostic.severity] or severities[1], + } + end + + for _, d in ipairs(vim.diagnostic.get(opts.bufnr, diagnosis_opts)) do + if filter_diag(d) then + table.insert(items, preprocess_diag(d)) + end + end + + table.sort(items, sorting_comparator(opts)) + + return items +end + +diagnostics.get = function(opts) + if opts.bufnr ~= 0 then + opts.bufnr = nil + end + if opts.bufnr == nil then + opts.path_display = vim.F.if_nil(opts.path_display, {}) + end + if type(opts.bufnr) == "string" then + opts.bufnr = tonumber(opts.bufnr) + end + + local locations = diagnostics_to_tbl(opts) + + if vim.tbl_isempty(locations) then + utils.notify("builtin.diagnostics", { + msg = "No diagnostics found", + level = "INFO", + }) + return + end + + if type(opts.line_width) == "string" and opts.line_width ~= "full" then + utils.notify("builtin.diagnostics", { + msg = string.format("'%s' is not a valid value for line_width", opts.line_width), + level = "ERROR", + }) + return + end + + opts.path_display = vim.F.if_nil(opts.path_display, "hidden") + pickers + .new(opts, { + prompt_title = opts.bufnr == nil and "Workspace Diagnostics" or "Document Diagnostics", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_diagnostics(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.prefilter_sorter { + tag = "type", + sorter = conf.generic_sorter(opts), + }, + }) + :find() +end + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + v(opts) + end + end + + return mod +end + +return apply_checks(diagnostics) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__files.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__files.lua new file mode 100644 index 00000000..2a3f2ce1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__files.lua @@ -0,0 +1,648 @@ +local action_state = require "telescope.actions.state" +local action_set = require "telescope.actions.set" +local actions = require "telescope.actions" +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local pickers = require "telescope.pickers" +local previewers = require "telescope.previewers" +local sorters = require "telescope.sorters" +local utils = require "telescope.utils" +local conf = require("telescope.config").values +local log = require "telescope.log" + +local Path = require "plenary.path" + +local flatten = utils.flatten +local filter = vim.tbl_filter + +local files = {} + +local escape_chars = function(string) + return string.gsub(string, "[%(|%)|\\|%[|%]|%-|%{%}|%?|%+|%*|%^|%$|%.]", { + ["\\"] = "\\\\", + ["-"] = "\\-", + ["("] = "\\(", + [")"] = "\\)", + ["["] = "\\[", + ["]"] = "\\]", + ["{"] = "\\{", + ["}"] = "\\}", + ["?"] = "\\?", + ["+"] = "\\+", + ["*"] = "\\*", + ["^"] = "\\^", + ["$"] = "\\$", + ["."] = "\\.", + }) +end + +local has_rg_program = function(picker_name, program) + if vim.fn.executable(program) == 1 then + return true + end + + utils.notify(picker_name, { + msg = string.format( + "'ripgrep', or similar alternative, is a required dependency for the %s picker. " + .. "Visit https://github.com/BurntSushi/ripgrep#installation for installation instructions.", + picker_name + ), + level = "ERROR", + }) + return false +end + +local get_open_filelist = function(grep_open_files, cwd) + if not grep_open_files then + return nil + end + + local bufnrs = filter(function(b) + if 1 ~= vim.fn.buflisted(b) then + return false + end + return true + end, vim.api.nvim_list_bufs()) + if not next(bufnrs) then + return + end + + local filelist = {} + for _, bufnr in ipairs(bufnrs) do + local file = vim.api.nvim_buf_get_name(bufnr) + table.insert(filelist, Path:new(file):make_relative(cwd)) + end + return filelist +end + +local opts_contain_invert = function(args) + local invert = false + local files_with_matches = false + + for _, v in ipairs(args) do + if v == "--invert-match" then + invert = true + elseif v == "--files-with-matches" or v == "--files-without-match" then + files_with_matches = true + end + + if #v >= 2 and v:sub(1, 1) == "-" and v:sub(2, 2) ~= "-" then + local non_option = false + for i = 2, #v do + local vi = v:sub(i, i) + if vi == "=" then -- ignore option -g=xxx + break + elseif vi == "g" or vi == "f" or vi == "m" or vi == "e" or vi == "r" or vi == "t" or vi == "T" then + non_option = true + elseif non_option == false and vi == "v" then + invert = true + elseif non_option == false and vi == "l" then + files_with_matches = true + end + end + end + end + return invert, files_with_matches +end + +-- Special keys: +-- opts.search_dirs -- list of directory to search in +-- opts.grep_open_files -- boolean to restrict search to open files +files.live_grep = function(opts) + local vimgrep_arguments = opts.vimgrep_arguments or conf.vimgrep_arguments + if not has_rg_program("live_grep", vimgrep_arguments[1]) then + return + end + local search_dirs = opts.search_dirs + local grep_open_files = opts.grep_open_files + opts.cwd = opts.cwd and utils.path_expand(opts.cwd) or vim.loop.cwd() + + local filelist = get_open_filelist(grep_open_files, opts.cwd) + if search_dirs then + for i, path in ipairs(search_dirs) do + search_dirs[i] = utils.path_expand(path) + end + end + + local additional_args = {} + if opts.additional_args ~= nil then + if type(opts.additional_args) == "function" then + additional_args = opts.additional_args(opts) + elseif type(opts.additional_args) == "table" then + additional_args = opts.additional_args + end + end + + if opts.type_filter then + additional_args[#additional_args + 1] = "--type=" .. opts.type_filter + end + + if type(opts.glob_pattern) == "string" then + additional_args[#additional_args + 1] = "--glob=" .. opts.glob_pattern + elseif type(opts.glob_pattern) == "table" then + for i = 1, #opts.glob_pattern do + additional_args[#additional_args + 1] = "--glob=" .. opts.glob_pattern[i] + end + end + + if opts.file_encoding then + additional_args[#additional_args + 1] = "--encoding=" .. opts.file_encoding + end + + local args = flatten { vimgrep_arguments, additional_args } + opts.__inverted, opts.__matches = opts_contain_invert(args) + + local live_grepper = finders.new_job(function(prompt) + if not prompt or prompt == "" then + return nil + end + + local search_list = {} + + if grep_open_files then + search_list = filelist + elseif search_dirs then + search_list = search_dirs + end + + return flatten { args, "--", prompt, search_list } + end, opts.entry_maker or make_entry.gen_from_vimgrep(opts), opts.max_results, opts.cwd) + + pickers + .new(opts, { + prompt_title = "Live Grep", + finder = live_grepper, + previewer = conf.grep_previewer(opts), + -- TODO: It would be cool to use `--json` output for this + -- and then we could get the highlight positions directly. + sorter = sorters.highlighter_only(opts), + attach_mappings = function(_, map) + map("i", "", actions.to_fuzzy_refine) + return true + end, + }) + :find() +end + +files.grep_string = function(opts) + local vimgrep_arguments = vim.F.if_nil(opts.vimgrep_arguments, conf.vimgrep_arguments) + if not has_rg_program("grep_string", vimgrep_arguments[1]) then + return + end + local word + local visual = vim.fn.mode() == "v" + + if visual == true then + local saved_reg = vim.fn.getreg "v" + vim.cmd [[noautocmd sil norm! "vy]] + local sele = vim.fn.getreg "v" + vim.fn.setreg("v", saved_reg) + word = vim.F.if_nil(opts.search, sele) + else + word = vim.F.if_nil(opts.search, vim.fn.expand "") + end + local search = opts.use_regex and word or escape_chars(word) + + local additional_args = {} + if opts.additional_args ~= nil then + if type(opts.additional_args) == "function" then + additional_args = opts.additional_args(opts) + elseif type(opts.additional_args) == "table" then + additional_args = opts.additional_args + end + end + + if opts.file_encoding then + additional_args[#additional_args + 1] = "--encoding=" .. opts.file_encoding + end + + if search == "" then + search = { "-v", "--", "^[[:space:]]*$" } + else + search = { "--", search } + end + + local args + if visual == true then + args = flatten { + vimgrep_arguments, + additional_args, + search, + } + else + args = flatten { + vimgrep_arguments, + additional_args, + opts.word_match, + search, + } + end + + opts.__inverted, opts.__matches = opts_contain_invert(args) + + if opts.grep_open_files then + for _, file in ipairs(get_open_filelist(opts.grep_open_files, opts.cwd)) do + table.insert(args, file) + end + elseif opts.search_dirs then + for _, path in ipairs(opts.search_dirs) do + table.insert(args, utils.path_expand(path)) + end + end + + opts.entry_maker = opts.entry_maker or make_entry.gen_from_vimgrep(opts) + pickers + .new(opts, { + prompt_title = "Find Word (" .. word:gsub("\n", "\\n") .. ")", + finder = finders.new_oneshot_job(args, opts), + previewer = conf.grep_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() +end + +files.find_files = function(opts) + local find_command = (function() + if opts.find_command then + if type(opts.find_command) == "function" then + return opts.find_command(opts) + end + return opts.find_command + elseif 1 == vim.fn.executable "rg" then + return { "rg", "--files", "--color", "never" } + elseif 1 == vim.fn.executable "fd" then + return { "fd", "--type", "f", "--color", "never" } + elseif 1 == vim.fn.executable "fdfind" then + return { "fdfind", "--type", "f", "--color", "never" } + elseif 1 == vim.fn.executable "find" and vim.fn.has "win32" == 0 then + return { "find", ".", "-type", "f" } + elseif 1 == vim.fn.executable "where" then + return { "where", "/r", ".", "*" } + end + end)() + + if not find_command then + utils.notify("builtin.find_files", { + msg = "You need to install either find, fd, or rg", + level = "ERROR", + }) + return + end + + local command = find_command[1] + local hidden = opts.hidden + local no_ignore = opts.no_ignore + local no_ignore_parent = opts.no_ignore_parent + local follow = opts.follow + local search_dirs = opts.search_dirs + local search_file = opts.search_file + + if search_dirs then + for k, v in pairs(search_dirs) do + search_dirs[k] = utils.path_expand(v) + end + end + + if command == "fd" or command == "fdfind" or command == "rg" then + if hidden then + find_command[#find_command + 1] = "--hidden" + end + if no_ignore then + find_command[#find_command + 1] = "--no-ignore" + end + if no_ignore_parent then + find_command[#find_command + 1] = "--no-ignore-parent" + end + if follow then + find_command[#find_command + 1] = "-L" + end + if search_file then + if command == "rg" then + find_command[#find_command + 1] = "-g" + find_command[#find_command + 1] = "*" .. search_file .. "*" + else + find_command[#find_command + 1] = search_file + end + end + if search_dirs then + if command ~= "rg" and not search_file then + find_command[#find_command + 1] = "." + end + vim.list_extend(find_command, search_dirs) + end + elseif command == "find" then + if not hidden then + table.insert(find_command, { "-not", "-path", "*/.*" }) + find_command = flatten(find_command) + end + if no_ignore ~= nil then + log.warn "The `no_ignore` key is not available for the `find` command in `find_files`." + end + if no_ignore_parent ~= nil then + log.warn "The `no_ignore_parent` key is not available for the `find` command in `find_files`." + end + if follow then + table.insert(find_command, 2, "-L") + end + if search_file then + table.insert(find_command, "-name") + table.insert(find_command, "*" .. search_file .. "*") + end + if search_dirs then + table.remove(find_command, 2) + for _, v in pairs(search_dirs) do + table.insert(find_command, 2, v) + end + end + elseif command == "where" then + if hidden ~= nil then + log.warn "The `hidden` key is not available for the Windows `where` command in `find_files`." + end + if no_ignore ~= nil then + log.warn "The `no_ignore` key is not available for the Windows `where` command in `find_files`." + end + if no_ignore_parent ~= nil then + log.warn "The `no_ignore_parent` key is not available for the Windows `where` command in `find_files`." + end + if follow ~= nil then + log.warn "The `follow` key is not available for the Windows `where` command in `find_files`." + end + if search_dirs ~= nil then + log.warn "The `search_dirs` key is not available for the Windows `where` command in `find_files`." + end + if search_file ~= nil then + log.warn "The `search_file` key is not available for the Windows `where` command in `find_files`." + end + end + + if opts.cwd then + opts.cwd = utils.path_expand(opts.cwd) + end + + opts.entry_maker = opts.entry_maker or make_entry.gen_from_file(opts) + + pickers + .new(opts, { + prompt_title = "Find Files", + __locations_input = true, + finder = finders.new_oneshot_job(find_command, opts), + previewer = conf.grep_previewer(opts), + sorter = conf.file_sorter(opts), + }) + :find() +end + +local function prepare_match(entry, kind) + local entries = {} + + if entry.node then + table.insert(entries, entry) + else + for name, item in pairs(entry) do + vim.list_extend(entries, prepare_match(item, name)) + end + end + + return entries +end + +-- TODO: finish docs for opts.show_line +files.treesitter = function(opts) + opts.show_line = vim.F.if_nil(opts.show_line, true) + + local has_nvim_treesitter, _ = pcall(require, "nvim-treesitter") + if not has_nvim_treesitter then + utils.notify("builtin.treesitter", { + msg = "This picker requires nvim-treesitter", + level = "ERROR", + }) + return + end + + local parsers = require "nvim-treesitter.parsers" + if not parsers.has_parser(parsers.get_buf_lang(opts.bufnr)) then + utils.notify("builtin.treesitter", { + msg = "No parser for the current buffer", + level = "ERROR", + }) + return + end + + local ts_locals = require "nvim-treesitter.locals" + local results = {} + for _, definition in ipairs(ts_locals.get_definitions(opts.bufnr)) do + local entries = prepare_match(ts_locals.get_local_nodes(definition)) + for _, entry in ipairs(entries) do + entry.kind = vim.F.if_nil(entry.kind, "") + table.insert(results, entry) + end + end + + results = utils.filter_symbols(results, opts) + if results == nil then + -- error message already printed in `utils.filter_symbols` + return + end + + if vim.tbl_isempty(results) then + return + end + + pickers + .new(opts, { + prompt_title = "Treesitter Symbols", + finder = finders.new_table { + results = results, + entry_maker = opts.entry_maker or make_entry.gen_from_treesitter(opts), + }, + previewer = conf.grep_previewer(opts), + sorter = conf.prefilter_sorter { + tag = "kind", + sorter = conf.generic_sorter(opts), + }, + }) + :find() +end + +files.current_buffer_fuzzy_find = function(opts) + -- All actions are on the current buffer + local filename = vim.api.nvim_buf_get_name(opts.bufnr) + local filetype = vim.api.nvim_buf_get_option(opts.bufnr, "filetype") + + local lines = vim.api.nvim_buf_get_lines(opts.bufnr, 0, -1, false) + local lines_with_numbers = {} + + for lnum, line in ipairs(lines) do + table.insert(lines_with_numbers, { + lnum = lnum, + bufnr = opts.bufnr, + filename = filename, + text = line, + }) + end + + opts.results_ts_highlight = vim.F.if_nil(opts.results_ts_highlight, true) + local lang = vim.treesitter.language.get_lang(filetype) or filetype + if opts.results_ts_highlight and lang and utils.has_ts_parser(lang) then + local parser = vim.treesitter.get_parser(opts.bufnr, lang) + local query = vim.treesitter.query.get(lang, "highlights") + local root = parser:parse()[1]:root() + + local line_highlights = setmetatable({}, { + __index = function(t, k) + local obj = {} + rawset(t, k, obj) + return obj + end, + }) + + for id, node in query:iter_captures(root, opts.bufnr, 0, -1) do + local hl = "@" .. query.captures[id] + if hl and type(hl) ~= "number" then + local row1, col1, row2, col2 = node:range() + + if row1 == row2 then + local row = row1 + 1 + + for index = col1, col2 do + line_highlights[row][index] = hl + end + else + local row = row1 + 1 + for index = col1, #lines[row] do + line_highlights[row][index] = hl + end + + while row < row2 + 1 do + row = row + 1 + + for index = 0, #(lines[row] or {}) do + line_highlights[row][index] = hl + end + end + end + end + end + + opts.line_highlights = line_highlights + end + + pickers + .new(opts, { + prompt_title = "Current Buffer Fuzzy", + finder = finders.new_table { + results = lines_with_numbers, + entry_maker = opts.entry_maker or make_entry.gen_from_buffer_lines(opts), + }, + sorter = conf.generic_sorter(opts), + previewer = conf.grep_previewer(opts), + attach_mappings = function() + actions.select_default:replace(function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if not selection then + utils.__warn_no_selection "builtin.current_buffer_fuzzy_find" + return + end + local current_picker = action_state.get_current_picker(prompt_bufnr) + local searched_for = require("telescope.actions.state").get_current_line() + + ---@type number[] | {start:number, end:number?, highlight:string?}[] + local highlights = current_picker.sorter:highlighter(searched_for, selection.ordinal) or {} + highlights = vim.tbl_map(function(hl) + if type(hl) == "table" and hl.start then + return hl.start + elseif type(hl) == "number" then + return hl + end + error "Invalid higlighter fn" + end, highlights) + + local first_col = 0 + if #highlights > 0 then + first_col = math.min(unpack(highlights)) - 1 + end + + actions.close(prompt_bufnr) + vim.schedule(function() + vim.api.nvim_win_set_cursor(0, { selection.lnum, first_col }) + end) + end) + + return true + end, + push_cursor_on_edit = true, + }) + :find() +end + +files.tags = function(opts) + local tagfiles = opts.ctags_file and { opts.ctags_file } or vim.fn.tagfiles() + for i, ctags_file in ipairs(tagfiles) do + tagfiles[i] = vim.fn.expand(ctags_file, true) + end + if vim.tbl_isempty(tagfiles) then + utils.notify("builtin.tags", { + msg = "No tags file found. Create one with ctags -R", + level = "ERROR", + }) + return + end + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_ctags(opts)) + + pickers + .new(opts, { + prompt_title = "Tags", + finder = finders.new_oneshot_job(flatten { "cat", tagfiles }, opts), + previewer = previewers.ctags.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function() + action_set.select:enhance { + post = function() + local selection = action_state.get_selected_entry() + if not selection then + return + end + + if selection.scode then + -- un-escape / then escape required + -- special chars for vim.fn.search() + -- ] ~ * + local scode = selection.scode:gsub([[\/]], "/"):gsub("[%]~*]", function(x) + return "\\" .. x + end) + + vim.cmd "keepjumps norm! gg" + vim.fn.search(scode) + vim.cmd "norm! zz" + else + vim.api.nvim_win_set_cursor(0, { selection.lnum, 0 }) + end + end, + } + return true + end, + }) + :find() +end + +files.current_buffer_tags = function(opts) + return files.tags(vim.tbl_extend("force", { + prompt_title = "Current Buffer Tags", + only_current_file = true, + path_display = "hidden", + }, opts)) +end + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + v(opts) + end + end + + return mod +end + +return apply_checks(files) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__git.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__git.lua new file mode 100644 index 00000000..1b6e9ed2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__git.lua @@ -0,0 +1,505 @@ +local actions = require "telescope.actions" +local action_state = require "telescope.actions.state" +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local operators = require "telescope.operators" +local pickers = require "telescope.pickers" +local previewers = require "telescope.previewers" +local utils = require "telescope.utils" +local entry_display = require "telescope.pickers.entry_display" +local strings = require "plenary.strings" +local Path = require "plenary.path" + +local conf = require("telescope.config").values +local git_command = utils.__git_command + +local git = {} + +local get_git_command_output = function(args, opts) + return utils.get_os_command_output(git_command(args, opts), opts.cwd) +end + +git.files = function(opts) + if opts.is_bare then + utils.notify("builtin.git_files", { + msg = "This operation must be run in a work tree", + level = "ERROR", + }) + return + end + + local show_untracked = vim.F.if_nil(opts.show_untracked, false) + local recurse_submodules = vim.F.if_nil(opts.recurse_submodules, false) + if show_untracked and recurse_submodules then + utils.notify("builtin.git_files", { + msg = "Git does not support both --others and --recurse-submodules", + level = "ERROR", + }) + return + end + + -- By creating the entry maker after the cwd options, + -- we ensure the maker uses the cwd options when being created. + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_file(opts)) + opts.git_command = vim.F.if_nil( + opts.git_command, + git_command({ "-c", "core.quotepath=false", "ls-files", "--exclude-standard", "--cached" }, opts) + ) + + pickers + .new(opts, { + prompt_title = "Git Files", + __locations_input = true, + finder = finders.new_oneshot_job( + utils.flatten { + opts.git_command, + show_untracked and "--others" or nil, + recurse_submodules and "--recurse-submodules" or nil, + }, + opts + ), + previewer = conf.grep_previewer(opts), + sorter = conf.file_sorter(opts), + }) + :find() +end + +git.commits = function(opts) + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_commits(opts)) + opts.git_command = + vim.F.if_nil(opts.git_command, git_command({ "log", "--pretty=oneline", "--abbrev-commit", "--", "." }, opts)) + + pickers + .new(opts, { + prompt_title = "Git Commits", + finder = finders.new_oneshot_job(opts.git_command, opts), + previewer = { + previewers.git_commit_diff_to_parent.new(opts), + previewers.git_commit_diff_to_head.new(opts), + previewers.git_commit_diff_as_was.new(opts), + previewers.git_commit_message.new(opts), + }, + sorter = conf.file_sorter(opts), + attach_mappings = function(_, map) + actions.select_default:replace(actions.git_checkout) + map({ "i", "n" }, "m", actions.git_reset_mixed) + map({ "i", "n" }, "s", actions.git_reset_soft) + map({ "i", "n" }, "h", actions.git_reset_hard) + return true + end, + }) + :find() +end + +git.stash = function(opts) + opts.show_branch = vim.F.if_nil(opts.show_branch, true) + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_stash(opts)) + opts.git_command = vim.F.if_nil(opts.git_command, git_command({ "--no-pager", "stash", "list" }, opts)) + + pickers + .new(opts, { + prompt_title = "Git Stash", + finder = finders.new_oneshot_job(opts.git_command, opts), + previewer = previewers.git_stash_diff.new(opts), + sorter = conf.file_sorter(opts), + attach_mappings = function() + actions.select_default:replace(actions.git_apply_stash) + return true + end, + }) + :find() +end + +local get_current_buf_line = function(winnr) + local lnum = vim.api.nvim_win_get_cursor(winnr)[1] + return vim.trim(vim.api.nvim_buf_get_lines(vim.api.nvim_win_get_buf(winnr), lnum - 1, lnum, false)[1]) +end + +local bcommits_picker = function(opts, title, finder) + return pickers.new(opts, { + prompt_title = title, + finder = finder, + previewer = { + previewers.git_commit_diff_to_parent.new(opts), + previewers.git_commit_diff_to_head.new(opts), + previewers.git_commit_diff_as_was.new(opts), + previewers.git_commit_message.new(opts), + }, + sorter = conf.file_sorter(opts), + attach_mappings = function() + actions.select_default:replace(actions.git_checkout_current_buffer) + local transfrom_file = function() + return opts.current_file and Path:new(opts.current_file):make_relative(opts.cwd) or "" + end + + local get_buffer_of_orig = function(selection) + local value = selection.value .. ":" .. transfrom_file() + local content = utils.get_os_command_output({ "git", "--no-pager", "show", value }, opts.cwd) + + local bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, content) + vim.api.nvim_buf_set_name(bufnr, "Original") + return bufnr + end + + local vimdiff = function(selection, command) + local ft = vim.bo.filetype + vim.cmd "diffthis" + + local bufnr = get_buffer_of_orig(selection) + vim.cmd(string.format("%s %s", command, bufnr)) + vim.bo.filetype = ft + vim.cmd "diffthis" + + vim.api.nvim_create_autocmd("WinClosed", { + buffer = bufnr, + nested = true, + once = true, + callback = function() + vim.api.nvim_buf_delete(bufnr, { force = true }) + end, + }) + end + + actions.select_vertical:replace(function(prompt_bufnr) + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + vimdiff(selection, "leftabove vert sbuffer") + end) + + actions.select_horizontal:replace(function(prompt_bufnr) + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + vimdiff(selection, "belowright sbuffer") + end) + + actions.select_tab:replace(function(prompt_bufnr) + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + vim.cmd("tabedit " .. transfrom_file()) + vimdiff(selection, "leftabove vert sbuffer") + end) + return true + end, + }) +end + +git.bcommits = function(opts) + opts.current_line = (opts.current_file == nil) and get_current_buf_line(opts.winnr) or nil + opts.current_file = vim.F.if_nil(opts.current_file, vim.api.nvim_buf_get_name(opts.bufnr)) + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_commits(opts)) + opts.git_command = + vim.F.if_nil(opts.git_command, git_command({ "log", "--pretty=oneline", "--abbrev-commit", "--follow" }, opts)) + + local title = "Git BCommits" + local finder = finders.new_oneshot_job( + utils.flatten { + opts.git_command, + opts.current_file, + }, + opts + ) + bcommits_picker(opts, title, finder):find() +end + +git.bcommits_range = function(opts) + opts.current_line = (opts.current_file == nil) and get_current_buf_line(opts.winnr) or nil + opts.current_file = vim.F.if_nil(opts.current_file, vim.api.nvim_buf_get_name(opts.bufnr)) + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_commits(opts)) + opts.git_command = vim.F.if_nil( + opts.git_command, + git_command({ "log", "--pretty=oneline", "--abbrev-commit", "--no-patch", "-L" }, opts) + ) + local visual = string.find(vim.fn.mode(), "[vV]") ~= nil + + local line_number_first = opts.from + local line_number_last = vim.F.if_nil(opts.to, line_number_first) + if visual then + line_number_first = vim.F.if_nil(line_number_first, vim.fn.line "v") + line_number_last = vim.F.if_nil(line_number_last, vim.fn.line ".") + elseif opts.operator then + opts.operator = false + opts.operator_callback = true + operators.run_operator(git.bcommits_range, opts) + return + elseif opts.operator_callback then + line_number_first = vim.fn.line "'[" + line_number_last = vim.fn.line "']" + elseif line_number_first == nil then + line_number_first = vim.F.if_nil(line_number_first, vim.fn.line ".") + line_number_last = vim.F.if_nil(line_number_last, vim.fn.line ".") + end + local line_range = + string.format("%d,%d:%s", line_number_first, line_number_last, Path:new(opts.current_file):make_relative(opts.cwd)) + + local title = "Git BCommits in range" + local finder = finders.new_oneshot_job( + utils.flatten { + opts.git_command, + line_range, + }, + opts + ) + bcommits_picker(opts, title, finder):find() +end + +git.branches = function(opts) + local format = "%(HEAD)" + .. "%(refname)" + .. "%(authorname)" + .. "%(upstream:lstrip=2)" + .. "%(committerdate:format-local:%Y/%m/%d %H:%M:%S)" + + local output = get_git_command_output( + { "for-each-ref", "--perl", "--format", format, "--sort", "-authordate", opts.pattern }, + opts + ) + + local show_remote_tracking_branches = vim.F.if_nil(opts.show_remote_tracking_branches, true) + + local results = {} + local widths = { + name = 0, + authorname = 0, + upstream = 0, + committerdate = 0, + } + local unescape_single_quote = function(v) + return string.gsub(v, "\\([\\'])", "%1") + end + local parse_line = function(line) + local fields = vim.split(string.sub(line, 2, -2), "''") + local entry = { + head = fields[1], + refname = unescape_single_quote(fields[2]), + authorname = unescape_single_quote(fields[3]), + upstream = unescape_single_quote(fields[4]), + committerdate = fields[5], + } + local prefix + if vim.startswith(entry.refname, "refs/remotes/") then + if show_remote_tracking_branches then + prefix = "refs/remotes/" + else + return + end + elseif vim.startswith(entry.refname, "refs/heads/") then + prefix = "refs/heads/" + else + return + end + local index = 1 + if entry.head ~= "*" then + index = #results + 1 + end + + entry.name = string.sub(entry.refname, string.len(prefix) + 1) + for key, value in pairs(widths) do + widths[key] = math.max(value, strings.strdisplaywidth(entry[key] or "")) + end + if string.len(entry.upstream) > 0 then + widths.upstream_indicator = 2 + end + table.insert(results, index, entry) + end + for _, line in ipairs(output) do + parse_line(line) + end + if #results == 0 then + return + end + + local displayer = entry_display.create { + separator = " ", + items = { + { width = 1 }, + { width = widths.name }, + { width = widths.authorname }, + { width = widths.upstream_indicator }, + { width = widths.upstream }, + { width = widths.committerdate }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.head }, + { entry.name, "TelescopeResultsIdentifier" }, + { entry.authorname }, + { string.len(entry.upstream) > 0 and "=>" or "" }, + { entry.upstream, "TelescopeResultsIdentifier" }, + { entry.committerdate }, + } + end + + pickers + .new(opts, { + prompt_title = "Git Branches", + finder = finders.new_table { + results = results, + entry_maker = function(entry) + entry.value = entry.name + entry.ordinal = entry.name + entry.display = make_display + return make_entry.set_default_entry_mt(entry, opts) + end, + }, + previewer = previewers.git_branch_log.new(opts), + sorter = conf.file_sorter(opts), + attach_mappings = function(_, map) + actions.select_default:replace(actions.git_checkout) + map({ "i", "n" }, "", actions.git_track_branch) + map({ "i", "n" }, "", actions.git_rebase_branch) + map({ "i", "n" }, "", actions.git_create_branch) + map({ "i", "n" }, "", actions.git_switch_branch) + map({ "i", "n" }, "", actions.git_delete_branch) + map({ "i", "n" }, "", actions.git_merge_branch) + return true + end, + }) + :find() +end + +git.status = function(opts) + if opts.is_bare then + utils.notify("builtin.git_status", { + msg = "This operation must be run in a work tree", + level = "ERROR", + }) + return + end + + local args = { "status", "--porcelain=v1", "--", "." } + + local gen_new_finder = function() + if vim.F.if_nil(opts.expand_dir, true) then + table.insert(args, #args - 1, "-uall") + end + local git_cmd = git_command(args, opts) + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_status(opts)) + return finders.new_oneshot_job(git_cmd, opts) + end + + local initial_finder = gen_new_finder() + if not initial_finder then + return + end + + pickers + .new(opts, { + prompt_title = "Git Status", + finder = initial_finder, + previewer = previewers.git_file_diff.new(opts), + sorter = conf.file_sorter(opts), + on_complete = { + function(self) + local lines = self.manager:num_results() + local prompt = action_state.get_current_line() + if lines == 0 and prompt == "" then + utils.notify("builtin.git_status", { + msg = "No changes found", + level = "ERROR", + }) + actions.close(self.prompt_bufnr) + end + end, + }, + attach_mappings = function(prompt_bufnr, map) + actions.git_staging_toggle:enhance { + post = function() + local picker = action_state.get_current_picker(prompt_bufnr) + + -- temporarily register a callback which keeps selection on refresh + local selection = picker:get_selection_row() + local callbacks = { unpack(picker._completion_callbacks) } -- shallow copy + picker:register_completion_callback(function(self) + self:set_selection(selection) + self._completion_callbacks = callbacks + end) + + -- refresh + picker:refresh(gen_new_finder(), { reset_prompt = false }) + end, + } + + map({ "i", "n" }, "", actions.git_staging_toggle) + return true + end, + }) + :find() +end + +local try_worktrees = function(opts) + local worktrees = conf.git_worktrees + + if utils.islist(worktrees) then + for _, wt in ipairs(worktrees) do + if vim.startswith(opts.cwd, wt.toplevel) then + opts.toplevel = wt.toplevel + opts.gitdir = wt.gitdir + if opts.use_git_root then + opts.cwd = wt.toplevel + end + return + end + end + end + + error(opts.cwd .. " is not a git directory") +end + +local current_path_toplevel = function() + local gitdir = vim.fn.finddir(".git", vim.fn.expand "%:p" .. ";") + if gitdir == "" then + return nil + end + return Path:new(gitdir):parent():absolute() +end + +local set_opts_cwd = function(opts) + opts.use_git_root = vim.F.if_nil(opts.use_git_root, true) + if opts.cwd then + opts.cwd = utils.path_expand(opts.cwd) + elseif opts.use_file_path then + opts.cwd = current_path_toplevel() + if not opts.cwd then + opts.cwd = vim.fn.expand "%:p:h" + try_worktrees(opts) + return + end + else + opts.cwd = vim.loop.cwd() + end + + local toplevel, ret = utils.get_os_command_output({ "git", "rev-parse", "--show-toplevel" }, opts.cwd) + + if ret ~= 0 then + local in_worktree = utils.get_os_command_output({ "git", "rev-parse", "--is-inside-work-tree" }, opts.cwd) + local in_bare = utils.get_os_command_output({ "git", "rev-parse", "--is-bare-repository" }, opts.cwd) + + if in_worktree[1] ~= "true" and in_bare[1] ~= "true" then + try_worktrees(opts) + elseif in_worktree[1] ~= "true" and in_bare[1] == "true" then + opts.is_bare = true + end + else + if opts.use_git_root then + opts.cwd = toplevel[1] + end + end +end + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = vim.F.if_nil(opts, {}) + + set_opts_cwd(opts) + v(opts) + end + end + + return mod +end + +return apply_checks(git) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__internal.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__internal.lua new file mode 100644 index 00000000..22c9c659 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__internal.lua @@ -0,0 +1,1470 @@ +local actions = require "telescope.actions" +local action_set = require "telescope.actions.set" +local action_state = require "telescope.actions.state" +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local Path = require "plenary.path" +local pickers = require "telescope.pickers" +local previewers = require "telescope.previewers" +local p_window = require "telescope.pickers.window" +local state = require "telescope.state" +local utils = require "telescope.utils" + +local conf = require("telescope.config").values + +-- Makes sure aliased options are set correctly +local function apply_cwd_only_aliases(opts) + local has_cwd_only = opts.cwd_only ~= nil + local has_only_cwd = opts.only_cwd ~= nil + + if has_only_cwd and not has_cwd_only then + -- Internally, use cwd_only + opts.cwd_only = opts.only_cwd + opts.only_cwd = nil + end + + return opts +end + +---@return boolean +local function buf_in_cwd(bufname, cwd) + if cwd:sub(-1) ~= Path.path.sep then + cwd = cwd .. Path.path.sep + end + local bufname_prefix = bufname:sub(1, #cwd) + return bufname_prefix == cwd +end + +local internal = {} + +internal.builtin = function(opts) + opts.include_extensions = vim.F.if_nil(opts.include_extensions, false) + opts.use_default_opts = vim.F.if_nil(opts.use_default_opts, false) + + local objs = {} + + for k, v in pairs(require "telescope.builtin") do + local debug_info = debug.getinfo(v) + table.insert(objs, { + filename = string.sub(debug_info.source, 2), + text = k, + }) + end + + local title = "Telescope Builtin" + + if opts.include_extensions then + title = "Telescope Pickers" + for ext, funcs in pairs(require("telescope").extensions) do + for func_name, func_obj in pairs(funcs) do + -- Only include exported functions whose name doesn't begin with an underscore + if type(func_obj) == "function" and string.sub(func_name, 0, 1) ~= "_" then + local debug_info = debug.getinfo(func_obj) + table.insert(objs, { + filename = string.sub(debug_info.source, 2), + text = string.format("%s : %s", ext, func_name), + }) + end + end + end + end + + table.sort(objs, function(a, b) + return a.text < b.text + end) + + opts.bufnr = vim.api.nvim_get_current_buf() + opts.winnr = vim.api.nvim_get_current_win() + pickers + .new(opts, { + prompt_title = title, + finder = finders.new_table { + results = objs, + entry_maker = function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + text = entry.text, + display = entry.text, + ordinal = entry.text, + filename = entry.filename, + }, opts) + end, + }, + previewer = previewers.builtin.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(_) + actions.select_default:replace(function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if not selection then + utils.__warn_no_selection "builtin.builtin" + return + end + + -- we do this to avoid any surprises + opts.include_extensions = nil + + local picker_opts + if not opts.use_default_opts then + picker_opts = opts + end + + actions.close(prompt_bufnr) + if string.match(selection.text, " : ") then + -- Call appropriate function from extensions + local split_string = vim.split(selection.text, " : ") + local ext = split_string[1] + local func = split_string[2] + require("telescope").extensions[ext][func](picker_opts) + else + -- Call appropriate telescope builtin + require("telescope.builtin")[selection.text](picker_opts) + end + end) + return true + end, + }) + :find() +end + +internal.resume = function(opts) + opts = opts or {} + opts.cache_index = vim.F.if_nil(opts.cache_index, 1) + + local cached_pickers = state.get_global_key "cached_pickers" + if cached_pickers == nil or vim.tbl_isempty(cached_pickers) then + utils.notify("builtin.resume", { + msg = "No cached picker(s).", + level = "INFO", + }) + return + end + local picker = cached_pickers[opts.cache_index] + if picker == nil then + utils.notify("builtin.resume", { + msg = string.format("Index too large as there are only '%s' pickers cached", #cached_pickers), + level = "ERROR", + }) + return + end + -- reset layout strategy and get_window_options if default as only one is valid + -- and otherwise unclear which was actually set + if picker.layout_strategy == conf.layout_strategy then + picker.layout_strategy = nil + end + if picker.get_window_options == p_window.get_window_options then + picker.get_window_options = nil + end + picker.cache_picker.index = opts.cache_index + + -- avoid partial `opts.cache_picker` at picker creation + if opts.cache_picker ~= false then + picker.cache_picker = vim.tbl_extend("keep", opts.cache_picker or {}, picker.cache_picker) + else + picker.cache_picker.disabled = true + end + opts.cache_picker = nil + picker.previewer = picker.all_previewers + if picker.hidden_previewer then + picker.hidden_previewer = nil + opts.previewer = vim.F.if_nil(opts.previewer, false) + end + opts.resumed_picker = true + pickers.new(opts, picker):find() +end + +internal.pickers = function(opts) + local cached_pickers = state.get_global_key "cached_pickers" + if cached_pickers == nil or vim.tbl_isempty(cached_pickers) then + utils.notify("builtin.pickers", { + msg = "No cached picker(s).", + level = "INFO", + }) + return + end + + opts = opts or {} + + -- clear cache picker for immediate pickers.new and pass option to resumed picker + if opts.cache_picker ~= nil then + opts._cache_picker = opts.cache_picker + opts.cache_picker = nil + end + + pickers + .new(opts, { + prompt_title = "Pickers", + finder = finders.new_table { + results = cached_pickers, + entry_maker = make_entry.gen_from_picker(opts), + }, + previewer = previewers.pickers.new(opts), + sorter = conf.generic_sorter(opts), + cache_picker = false, + attach_mappings = function(_, map) + actions.select_default:replace(function(prompt_bufnr) + local curr_picker = action_state.get_current_picker(prompt_bufnr) + local curr_entry = action_state.get_selected_entry() + if not curr_entry then + return + end + + actions.close(prompt_bufnr) + + local selection_index, _ = utils.list_find(function(v) + if curr_entry.value == v.value then + return true + end + return false + end, curr_picker.finder.results) + + opts.cache_picker = opts._cache_picker + opts["cache_index"] = selection_index + opts["initial_mode"] = cached_pickers[selection_index].initial_mode + internal.resume(opts) + end) + map({ "i", "n" }, "", actions.remove_selected_picker) + return true + end, + }) + :find() +end + +internal.planets = function(opts) + local show_pluto = opts.show_pluto or false + local show_moon = opts.show_moon or false + + local sourced_file = require("plenary.debug_utils").sourced_filepath() + local base_directory = vim.fn.fnamemodify(sourced_file, ":h:h:h:h") + + local globbed_files = vim.fn.globpath(base_directory .. "/data/memes/planets/", "*", true, true) + local acceptable_files = {} + for _, v in ipairs(globbed_files) do + if (show_pluto or not v:find "pluto") and (show_moon or not v:find "moon") then + table.insert(acceptable_files, vim.fn.fnamemodify(v, ":t")) + end + end + + pickers + .new(opts, { + prompt_title = "Planets", + finder = finders.new_table { + results = acceptable_files, + entry_maker = function(line) + return make_entry.set_default_entry_mt({ + ordinal = line, + display = line, + filename = base_directory .. "/data/memes/planets/" .. line, + }, opts) + end, + }, + previewer = previewers.cat.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.planets" + return + end + + actions.close(prompt_bufnr) + print("Enjoy astronomy! You viewed:", selection.display) + end) + + return true + end, + }) + :find() +end + +internal.symbols = function(opts) + local initial_mode = vim.fn.mode() + local files = vim.api.nvim_get_runtime_file("data/telescope-sources/*.json", true) + local data_path = (function() + if not opts.symbol_path then + return Path:new { vim.fn.stdpath "data", "telescope", "symbols" } + else + return Path:new { opts.symbol_path } + end + end)() + if data_path:exists() then + for _, v in ipairs(require("plenary.scandir").scan_dir(data_path:absolute(), { search_pattern = "%.json$" })) do + table.insert(files, v) + end + end + + if #files == 0 then + utils.notify("builtin.symbols", { + msg = "No sources found! Check out https://github.com/nvim-telescope/telescope-symbols.nvim " + .. "for some prebuild symbols or how to create you own symbol source.", + level = "ERROR", + }) + return + end + + local sources = {} + if opts.sources then + for _, v in ipairs(files) do + for _, s in ipairs(opts.sources) do + if v:find(s) then + table.insert(sources, v) + end + end + end + else + sources = files + end + + local results = {} + for _, source in ipairs(sources) do + local data = vim.json.decode(Path:new(source):read()) + for _, entry in ipairs(data) do + table.insert(results, entry) + end + end + + pickers + .new(opts, { + prompt_title = "Symbols", + finder = finders.new_table { + results = results, + entry_maker = function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = entry[1] .. " " .. entry[2], + display = entry[1] .. " " .. entry[2], + }, opts) + end, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(_) + if initial_mode == "i" then + actions.select_default:replace(actions.insert_symbol_i) + else + actions.select_default:replace(actions.insert_symbol) + end + return true + end, + }) + :find() +end + +internal.commands = function(opts) + pickers + .new(opts, { + prompt_title = "Commands", + finder = finders.new_table { + results = (function() + local command_iter = vim.api.nvim_get_commands {} + local commands = {} + + for _, cmd in pairs(command_iter) do + table.insert(commands, cmd) + end + + local need_buf_command = vim.F.if_nil(opts.show_buf_command, true) + + if need_buf_command then + local buf_command_iter = vim.api.nvim_buf_get_commands(0, {}) + buf_command_iter[true] = nil -- remove the redundant entry + for _, cmd in pairs(buf_command_iter) do + table.insert(commands, cmd) + end + end + return commands + end)(), + + entry_maker = opts.entry_maker or make_entry.gen_from_commands(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.commands" + return + end + + actions.close(prompt_bufnr) + local val = selection.value + local cmd = string.format([[:%s ]], val.name) + + if val.nargs == "0" then + local cr = vim.api.nvim_replace_termcodes("", true, false, true) + cmd = cmd .. cr + end + vim.cmd [[stopinsert]] + vim.api.nvim_feedkeys(cmd, "nt", false) + end) + + return true + end, + }) + :find() +end + +internal.quickfix = function(opts) + local qf_identifier = opts.id or vim.F.if_nil(opts.nr, "$") + local locations = vim.fn.getqflist({ [opts.id and "id" or "nr"] = qf_identifier, items = true }).items + + if vim.tbl_isempty(locations) then + utils.notify("builtin.quickfix", { msg = "No quickfix items", level = "INFO" }) + return + end + + pickers + .new(opts, { + prompt_title = "Quickfix", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() +end + +internal.quickfixhistory = function(opts) + local qflists = {} + for i = 1, 10 do -- (n)vim keeps at most 10 quickfix lists in full + -- qf weirdness: id = 0 gets id of quickfix list nr + local qflist = vim.fn.getqflist { nr = i, id = 0, title = true, items = true } + if not vim.tbl_isempty(qflist.items) then + table.insert(qflists, qflist) + end + end + local entry_maker = opts.make_entry + or function(entry) + return make_entry.set_default_entry_mt({ + value = entry.title or "Untitled", + ordinal = entry.title or "Untitled", + display = entry.title or "Untitled", + nr = entry.nr, + id = entry.id, + items = entry.items, + }, opts) + end + local qf_entry_maker = make_entry.gen_from_quickfix(opts) + pickers + .new(opts, { + prompt_title = "Quickfix History", + finder = finders.new_table { + results = qflists, + entry_maker = entry_maker, + }, + previewer = previewers.new_buffer_previewer { + title = "Quickfix List Preview", + dyn_title = function(_, entry) + return entry.title + end, + + get_buffer_by_name = function(_, entry) + return "quickfixlist_" .. tostring(entry.nr) + end, + + define_preview = function(self, entry) + if self.state.bufname then + return + end + local entries = vim.tbl_map(function(i) + return qf_entry_maker(i):display() + end, entry.items) + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, entries) + end, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(_, map) + action_set.select:replace(function(prompt_bufnr) + local nr = action_state.get_selected_entry().nr + actions.close(prompt_bufnr) + internal.quickfix { nr = nr } + end) + + map({ "i", "n" }, "", function(prompt_bufnr) + local nr = action_state.get_selected_entry().nr + actions.close(prompt_bufnr) + vim.cmd(nr .. "chistory") + vim.cmd "botright copen" + end) + return true + end, + }) + :find() +end + +internal.loclist = function(opts) + local locations = vim.fn.getloclist(0) + local filenames = {} + for _, value in pairs(locations) do + local bufnr = value.bufnr + if filenames[bufnr] == nil then + filenames[bufnr] = vim.api.nvim_buf_get_name(bufnr) + end + value.filename = filenames[bufnr] + end + + if vim.tbl_isempty(locations) then + utils.notify("builtin.loclist", { msg = "No loclist items", level = "INFO" }) + return + end + + pickers + .new(opts, { + prompt_title = "Loclist", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() +end + +internal.oldfiles = function(opts) + opts = apply_cwd_only_aliases(opts) + opts.include_current_session = vim.F.if_nil(opts.include_current_session, true) + + local current_buffer = vim.api.nvim_get_current_buf() + local current_file = vim.api.nvim_buf_get_name(current_buffer) + local results = {} + + if opts.include_current_session then + for _, buffer in ipairs(vim.split(vim.fn.execute ":buffers! t", "\n")) do + local match = tonumber(string.match(buffer, "%s*(%d+)")) + local open_by_lsp = string.match(buffer, "line 0$") + if match and not open_by_lsp then + local file = vim.api.nvim_buf_get_name(match) + if vim.loop.fs_stat(file) and match ~= current_buffer then + table.insert(results, file) + end + end + end + end + + for _, file in ipairs(vim.v.oldfiles) do + local file_stat = vim.loop.fs_stat(file) + if file_stat and file_stat.type == "file" and not vim.tbl_contains(results, file) and file ~= current_file then + table.insert(results, file) + end + end + + if opts.cwd_only or opts.cwd then + local cwd = opts.cwd_only and vim.loop.cwd() or opts.cwd + cwd = cwd .. utils.get_separator() + results = vim.tbl_filter(function(file) + return buf_in_cwd(file, cwd) + end, results) + end + + pickers + .new(opts, { + prompt_title = "Oldfiles", + __locations_input = true, + finder = finders.new_table { + results = results, + entry_maker = opts.entry_maker or make_entry.gen_from_file(opts), + }, + sorter = conf.file_sorter(opts), + previewer = conf.grep_previewer(opts), + }) + :find() +end + +internal.command_history = function(opts) + local history_string = vim.fn.execute "history cmd" + local history_list = vim.split(history_string, "\n") + + local results = {} + local filter_fn = opts.filter_fn + + for i = #history_list, 3, -1 do + local item = history_list[i] + local _, finish = string.find(item, "%d+ +") + local cmd = string.sub(item, finish + 1) + + if filter_fn then + if filter_fn(cmd) then + table.insert(results, cmd) + end + else + table.insert(results, cmd) + end + end + + pickers + .new(opts, { + prompt_title = "Command History", + finder = finders.new_table(results), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(_, map) + actions.select_default:replace(actions.set_command_line) + map({ "i", "n" }, "", actions.edit_command_line) + + -- TODO: Find a way to insert the text... it seems hard. + -- map('i', '', actions.insert_value, { expr = true }) + + return true + end, + }) + :find() +end + +internal.search_history = function(opts) + local search_string = vim.fn.execute "history search" + local search_list = vim.split(search_string, "\n") + + local results = {} + for i = #search_list, 3, -1 do + local item = search_list[i] + local _, finish = string.find(item, "%d+ +") + table.insert(results, string.sub(item, finish + 1)) + end + + pickers + .new(opts, { + prompt_title = "Search History", + finder = finders.new_table(results), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(_, map) + actions.select_default:replace(actions.set_search_line) + map({ "i", "n" }, "", actions.edit_search_line) + + -- TODO: Find a way to insert the text... it seems hard. + -- map('i', '', actions.insert_value, { expr = true }) + + return true + end, + }) + :find() +end + +internal.vim_options = function(opts) + local res = {} + for _, v in pairs(vim.api.nvim_get_all_options_info()) do + table.insert(res, v) + end + table.sort(res, function(left, right) + return left.name < right.name + end) + + pickers + .new(opts, { + prompt_title = "options", + finder = finders.new_table { + results = res, + entry_maker = opts.entry_maker or make_entry.gen_from_vimoptions(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function() + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.vim_options" + return + end + + local esc = "" + if vim.fn.mode() == "i" then + esc = vim.api.nvim_replace_termcodes("", true, false, true) + end + + vim.api.nvim_feedkeys( + string.format("%s:set %s=%s", esc, selection.value.name, selection.value.value), + "m", + true + ) + end) + + return true + end, + }) + :find() +end + +internal.help_tags = function(opts) + opts.lang = vim.F.if_nil(opts.lang, vim.o.helplang) + opts.fallback = vim.F.if_nil(opts.fallback, true) + opts.file_ignore_patterns = {} + + local langs = vim.split(opts.lang, ",", true) + if opts.fallback and not vim.tbl_contains(langs, "en") then + table.insert(langs, "en") + end + local langs_map = {} + for _, lang in ipairs(langs) do + langs_map[lang] = true + end + + local tag_files = {} + local function add_tag_file(lang, file) + if langs_map[lang] then + if tag_files[lang] then + table.insert(tag_files[lang], file) + else + tag_files[lang] = { file } + end + end + end + + local help_files = {} + local all_files = vim.api.nvim_get_runtime_file("doc/*", true) + for _, fullpath in ipairs(all_files) do + local file = utils.path_tail(fullpath) + if file == "tags" then + add_tag_file("en", fullpath) + elseif file:match "^tags%-..$" then + local lang = file:sub(-2) + add_tag_file(lang, fullpath) + else + help_files[file] = fullpath + end + end + + local tags = {} + local tags_map = {} + local delimiter = string.char(9) + for _, lang in ipairs(langs) do + for _, file in ipairs(tag_files[lang] or {}) do + local lines = vim.split(Path:new(file):read(), "\n", true) + for _, line in ipairs(lines) do + -- TODO: also ignore tagComment starting with ';' + if not line:match "^!_TAG_" then + local fields = vim.split(line, delimiter, true) + if #fields == 3 and not tags_map[fields[1]] then + if fields[1] ~= "help-tags" or fields[2] ~= "tags" then + table.insert(tags, { + name = fields[1], + filename = help_files[fields[2]], + cmd = fields[3], + lang = lang, + }) + tags_map[fields[1]] = true + end + end + end + end + end + end + + pickers + .new(opts, { + prompt_title = "Help", + finder = finders.new_table { + results = tags, + entry_maker = function(entry) + return make_entry.set_default_entry_mt({ + value = entry.name .. "@" .. entry.lang, + display = entry.name, + ordinal = entry.name, + filename = entry.filename, + cmd = entry.cmd, + }, opts) + end, + }, + previewer = previewers.help.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + action_set.select:replace(function(_, cmd) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.help_tags" + return + end + + actions.close(prompt_bufnr) + if cmd == "default" or cmd == "horizontal" then + vim.cmd("help " .. selection.value) + elseif cmd == "vertical" then + vim.cmd("vert help " .. selection.value) + elseif cmd == "tab" then + vim.cmd("tab help " .. selection.value) + end + end) + + return true + end, + }) + :find() +end + +internal.man_pages = function(opts) + opts.sections = vim.F.if_nil(opts.sections, { "1" }) + assert(utils.islist(opts.sections), "sections should be a list") + opts.man_cmd = utils.get_lazy_default(opts.man_cmd, function() + local uname = vim.loop.os_uname() + local sysname = string.lower(uname.sysname) + if sysname == "darwin" then + local major_version = tonumber(vim.fn.matchlist(uname.release, [[^\(\d\+\)\..*]])[2]) or 0 + return major_version >= 22 and { "apropos", "." } or { "apropos", " " } + elseif sysname == "freebsd" then + return { "apropos", "." } + else + return { "apropos", "" } + end + end) + opts.entry_maker = opts.entry_maker or make_entry.gen_from_apropos(opts) + opts.env = { PATH = vim.env.PATH, MANPATH = vim.env.MANPATH } + + pickers + .new(opts, { + prompt_title = "Man", + finder = finders.new_oneshot_job(opts.man_cmd, opts), + previewer = previewers.man.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + action_set.select:replace(function(_, cmd) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.man_pages" + return + end + + local args = selection.section .. " " .. selection.value + actions.close(prompt_bufnr) + if cmd == "default" or cmd == "horizontal" then + vim.cmd("Man " .. args) + elseif cmd == "vertical" then + vim.cmd("vert Man " .. args) + elseif cmd == "tab" then + vim.cmd("tab Man " .. args) + end + end) + + return true + end, + }) + :find() +end + +internal.reloader = function(opts) + local package_list = vim.tbl_keys(package.loaded) + + -- filter out packages we don't want and track the longest package name + local column_len = 0 + for index, module_name in pairs(package_list) do + if + type(require(module_name)) ~= "table" + or module_name:sub(1, 1) == "_" + or package.searchpath(module_name, package.path) == nil + then + table.remove(package_list, index) + elseif #module_name > column_len then + column_len = #module_name + end + end + opts.column_len = vim.F.if_nil(opts.column_len, column_len) + + pickers + .new(opts, { + prompt_title = "Packages", + finder = finders.new_table { + results = package_list, + entry_maker = opts.entry_maker or make_entry.gen_from_packages(opts), + }, + -- previewer = previewers.vim_buffer.new(opts), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.reloader" + return + end + + actions.close(prompt_bufnr) + require("plenary.reload").reload_module(selection.value) + utils.notify("builtin.reloader", { + msg = string.format("[%s] - module reloaded", selection.value), + level = "INFO", + }) + end) + + return true + end, + }) + :find() +end + +internal.buffers = function(opts) + opts = apply_cwd_only_aliases(opts) + + local bufnrs = vim.tbl_filter(function(bufnr) + if 1 ~= vim.fn.buflisted(bufnr) then + return false + end + -- only hide unloaded buffers if opts.show_all_buffers is false, keep them listed if true or nil + if opts.show_all_buffers == false and not vim.api.nvim_buf_is_loaded(bufnr) then + return false + end + if opts.ignore_current_buffer and bufnr == vim.api.nvim_get_current_buf() then + return false + end + + local bufname = vim.api.nvim_buf_get_name(bufnr) + + if opts.cwd_only and not buf_in_cwd(bufname, vim.loop.cwd()) then + return false + end + if not opts.cwd_only and opts.cwd and not buf_in_cwd(bufname, opts.cwd) then + return false + end + return true + end, vim.api.nvim_list_bufs()) + + if not next(bufnrs) then + utils.notify("builtin.buffers", { msg = "No buffers found with the provided options", level = "INFO" }) + return + end + + if opts.sort_mru then + table.sort(bufnrs, function(a, b) + return vim.fn.getbufinfo(a)[1].lastused > vim.fn.getbufinfo(b)[1].lastused + end) + end + + if type(opts.sort_buffers) == "function" then + table.sort(bufnrs, opts.sort_buffers) + end + + local buffers = {} + local default_selection_idx = 1 + for _, bufnr in ipairs(bufnrs) do + local flag = bufnr == vim.fn.bufnr "" and "%" or (bufnr == vim.fn.bufnr "#" and "#" or " ") + + if opts.sort_lastused and not opts.ignore_current_buffer and flag == "#" then + default_selection_idx = 2 + end + + local element = { + bufnr = bufnr, + flag = flag, + info = vim.fn.getbufinfo(bufnr)[1], + } + + if opts.sort_lastused and (flag == "#" or flag == "%") then + local idx = ((buffers[1] ~= nil and buffers[1].flag == "%") and 2 or 1) + table.insert(buffers, idx, element) + else + if opts.select_current and flag == "%" then + default_selection_idx = bufnr + end + table.insert(buffers, element) + end + end + + if not opts.bufnr_width then + local max_bufnr = math.max(unpack(bufnrs)) + opts.bufnr_width = #tostring(max_bufnr) + end + + pickers + .new(opts, { + prompt_title = "Buffers", + finder = finders.new_table { + results = buffers, + entry_maker = opts.entry_maker or make_entry.gen_from_buffer(opts), + }, + previewer = conf.grep_previewer(opts), + sorter = conf.generic_sorter(opts), + default_selection_index = default_selection_idx, + }) + :find() +end + +internal.colorscheme = function(opts) + local before_background = vim.o.background + local before_color = vim.api.nvim_exec2("colorscheme", { output = true }).output + local need_restore = true + + local colors = opts.colors or { before_color } + if not vim.tbl_contains(colors, before_color) then + table.insert(colors, 1, before_color) + end + + colors = vim.list_extend( + colors, + vim.tbl_filter(function(color) + return not vim.tbl_contains(colors, color) + end, vim.fn.getcompletion("", "color")) + ) + + local previewer + if opts.enable_preview then + -- define previewer + local bufnr = vim.api.nvim_get_current_buf() + local p = vim.api.nvim_buf_get_name(bufnr) + + -- show current buffer content in previewer + previewer = previewers.new_buffer_previewer { + get_buffer_by_name = function() + return p + end, + define_preview = function(self) + if vim.loop.fs_stat(p) then + conf.buffer_previewer_maker(p, self.state.bufnr, { bufname = self.state.bufname }) + else + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines) + end + end, + } + end + + local picker = pickers.new(opts, { + prompt_title = "Change Colorscheme", + finder = finders.new_table { + results = colors, + }, + sorter = conf.generic_sorter(opts), + previewer = previewer, + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.colorscheme" + return + end + + actions.close(prompt_bufnr) + need_restore = false + vim.cmd.colorscheme(selection.value) + end) + action_set.shift_selection:enhance { + post = function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.colorscheme" + return + end + need_restore = true + if opts.enable_preview then + vim.cmd.colorscheme(selection.value) + end + end, + } + actions.close:enhance { + post = function() + if need_restore then + vim.cmd.colorscheme(before_color) + end + end, + } + return true + end, + on_complete = { + function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.colorscheme" + return + end + need_restore = true + vim.cmd.colorscheme(selection.value) + end, + }, + }) + + if opts.enable_preview then + -- rewrite picker.close_windows. restore color if needed + local close_windows = picker.close_windows + picker.close_windows = function(status) + close_windows(status) + if need_restore then + vim.o.background = before_background + vim.cmd.colorscheme(before_color) + end + end + end + + picker:find() +end + +internal.marks = function(opts) + local local_marks = { + items = vim.fn.getmarklist(opts.bufnr), + name_func = function(_, line) + return vim.api.nvim_buf_get_lines(opts.bufnr, line - 1, line, false)[1] + end, + } + local global_marks = { + items = vim.fn.getmarklist(), + name_func = function(mark, _) + -- get buffer name if it is opened, otherwise get file name + return vim.api.nvim_get_mark(mark, {})[4] + end, + } + local marks_table = {} + local marks_others = {} + local bufname = vim.api.nvim_buf_get_name(opts.bufnr) + local all_marks = {} + opts.mark_type = vim.F.if_nil(opts.mark_type, "all") + if opts.mark_type == "all" then + all_marks = { local_marks, global_marks } + elseif opts.mark_type == "local" then + all_marks = { local_marks } + elseif opts.mark_type == "global" then + all_marks = { global_marks } + end + + for _, cnf in ipairs(all_marks) do + for _, v in ipairs(cnf.items) do + -- strip the first single quote character + local mark = string.sub(v.mark, 2, 3) + local _, lnum, col, _ = unpack(v.pos) + local name = cnf.name_func(mark, lnum) + -- same format to :marks command + local line = string.format("%s %6d %4d %s", mark, lnum, col - 1, name) + local row = { + line = line, + lnum = lnum, + col = col, + filename = utils.path_expand(v.file or bufname), + } + -- non alphanumeric marks goes to last + if mark:match "%w" then + table.insert(marks_table, row) + else + table.insert(marks_others, row) + end + end + end + marks_table = vim.fn.extend(marks_table, marks_others) + + pickers + .new(opts, { + prompt_title = "Marks", + finder = finders.new_table { + results = marks_table, + entry_maker = opts.entry_maker or make_entry.gen_from_marks(opts), + }, + previewer = conf.grep_previewer(opts), + sorter = conf.generic_sorter(opts), + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }) + :find() +end + +internal.registers = function(opts) + local registers_table = { '"', "-", "#", "=", "/", "*", "+", ":", ".", "%" } + + -- named + for i = 0, 9 do + table.insert(registers_table, tostring(i)) + end + + -- alphabetical + for i = 65, 90 do + table.insert(registers_table, string.char(i)) + end + + pickers + .new(opts, { + prompt_title = "Registers", + finder = finders.new_table { + results = registers_table, + entry_maker = opts.entry_maker or make_entry.gen_from_registers(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(_, map) + actions.select_default:replace(actions.paste_register) + map({ "i", "n" }, "", actions.edit_register) + + return true + end, + }) + :find() +end + +internal.keymaps = function(opts) + opts.modes = vim.F.if_nil(opts.modes, { "n", "i", "c", "x" }) + opts.show_plug = vim.F.if_nil(opts.show_plug, true) + opts.only_buf = vim.F.if_nil(opts.only_buf, false) + + local keymap_encountered = {} -- used to make sure no duplicates are inserted into keymaps_table + local keymaps_table = {} + local max_len_lhs = 0 + + -- helper function to populate keymaps_table and determine max_len_lhs + local function extract_keymaps(keymaps) + for _, keymap in pairs(keymaps) do + local keymap_key = keymap.buffer .. keymap.mode .. keymap.lhs -- should be distinct for every keymap + if not keymap_encountered[keymap_key] then + keymap_encountered[keymap_key] = true + if + (opts.show_plug or not string.find(keymap.lhs, "")) + and (not opts.lhs_filter or opts.lhs_filter(keymap.lhs)) + and (not opts.filter or opts.filter(keymap)) + then + table.insert(keymaps_table, keymap) + max_len_lhs = math.max(max_len_lhs, #utils.display_termcodes(keymap.lhs)) + end + end + end + end + + for _, mode in pairs(opts.modes) do + local global = vim.api.nvim_get_keymap(mode) + local buf_local = vim.api.nvim_buf_get_keymap(0, mode) + if not opts.only_buf then + extract_keymaps(global) + end + extract_keymaps(buf_local) + end + opts.width_lhs = max_len_lhs + 1 + + pickers + .new(opts, { + prompt_title = "Key Maps", + finder = finders.new_table { + results = keymaps_table, + entry_maker = opts.entry_maker or make_entry.gen_from_keymaps(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.keymaps" + return + end + + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(selection.value.lhs, true, false, true), "t", true) + return actions.close(prompt_bufnr) + end) + return true + end, + }) + :find() +end + +internal.filetypes = function(opts) + local filetypes = vim.fn.getcompletion("", "filetype") + + pickers + .new(opts, { + prompt_title = "Filetypes", + finder = finders.new_table { + results = filetypes, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + print "[telescope] Nothing currently selected" + return + end + + actions.close(prompt_bufnr) + vim.cmd("setfiletype " .. selection[1]) + end) + return true + end, + }) + :find() +end + +internal.highlights = function(opts) + local highlights = vim.fn.getcompletion("", "highlight") + + pickers + .new(opts, { + prompt_title = "Highlights", + finder = finders.new_table { + results = highlights, + entry_maker = opts.entry_maker or make_entry.gen_from_highlights(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.highlights" + return + end + + actions.close(prompt_bufnr) + vim.cmd("hi " .. selection.value) + end) + return true + end, + previewer = previewers.highlights.new(opts), + }) + :find() +end + +internal.autocommands = function(opts) + local autocmds = vim.api.nvim_get_autocmds {} + table.sort(autocmds, function(lhs, rhs) + return lhs.event < rhs.event + end) + pickers + .new(opts, { + prompt_title = "autocommands", + finder = finders.new_table { + results = autocmds, + entry_maker = opts.entry_maker or make_entry.gen_from_autocommands(opts), + }, + previewer = previewers.autocommands.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + action_set.select:replace_if(function() + local selection = action_state.get_selected_entry() + if selection == nil then + return false + end + local val = selection.value + local cb = val.callback + if vim.is_callable(cb) then + if type(cb) ~= "string" then + local f = type(cb) == "function" and cb or rawget(getmetatable(cb), "__call") + local info = debug.getinfo(f, "S") + local file = info.source:match "^@(.+)" + local lnum = info.linedefined + if file and (lnum or 0) > 0 then + selection.filename, selection.lnum, selection.col = file, lnum, 1 + return false + end + end + end + local group_name = val.group_name ~= "" and val.group_name or "" + local output = + vim.fn.execute("verb autocmd " .. group_name .. " " .. val.event .. " " .. val.pattern, "silent") + for line in output:gmatch "[^\r\n]+" do + local source_file = line:match "Last set from (.*) line %d*$" or line:match "Last set from (.*)$" + if source_file and source_file ~= "Lua" then + selection.filename = source_file + local source_lnum = line:match "line (%d*)$" or "1" + selection.lnum = tonumber(source_lnum) + selection.col = 1 + return false + end + end + return true + end, function() + local selection = action_state.get_selected_entry() + actions.close(prompt_bufnr) + print("You selected autocmd: " .. vim.inspect(selection.value)) + end) + + return true + end, + }) + :find() +end + +internal.spell_suggest = function(opts) + local cursor_word = vim.fn.expand "" + local suggestions = vim.fn.spellsuggest(cursor_word) + + pickers + .new(opts, { + prompt_title = "Spelling Suggestions", + finder = finders.new_table { + results = suggestions, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.spell_suggest" + return + end + + action_state.get_current_picker(prompt_bufnr)._original_mode = "i" + actions.close(prompt_bufnr) + vim.cmd('normal! "_ciw' .. selection[1]) + vim.cmd "stopinsert" + end) + return true + end, + }) + :find() +end + +internal.tagstack = function(opts) + opts = opts or {} + local tagstack = vim.fn.gettagstack().items + + local tags = {} + for i = #tagstack, 1, -1 do + local tag = tagstack[i] + tag.bufnr = tag.from[1] + if vim.api.nvim_buf_is_valid(tag.bufnr) then + tags[#tags + 1] = tag + tag.filename = vim.fn.bufname(tag.bufnr) + tag.lnum = tag.from[2] + tag.col = tag.from[3] + + tag.text = vim.api.nvim_buf_get_lines(tag.bufnr, tag.lnum - 1, tag.lnum, false)[1] or "" + end + end + + if vim.tbl_isempty(tags) then + utils.notify("builtin.tagstack", { + msg = "No tagstack available", + level = "WARN", + }) + return + end + + pickers + .new(opts, { + prompt_title = "TagStack", + finder = finders.new_table { + results = tags, + entry_maker = make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() +end + +internal.jumplist = function(opts) + opts = opts or {} + local jumplist = vim.fn.getjumplist()[1] + + -- reverse the list + local sorted_jumplist = {} + for i = #jumplist, 1, -1 do + if vim.api.nvim_buf_is_valid(jumplist[i].bufnr) then + jumplist[i].text = vim.api.nvim_buf_get_lines(jumplist[i].bufnr, jumplist[i].lnum - 1, jumplist[i].lnum, false)[1] + or "" + table.insert(sorted_jumplist, jumplist[i]) + end + end + + pickers + .new(opts, { + prompt_title = "Jumplist", + finder = finders.new_table { + results = sorted_jumplist, + entry_maker = make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() +end + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + v(opts) + end + end + + return mod +end + +return apply_checks(internal) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__lsp.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__lsp.lua new file mode 100644 index 00000000..e781d240 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/__lsp.lua @@ -0,0 +1,447 @@ +local channel = require("plenary.async.control").channel +local actions = require "telescope.actions" +local sorters = require "telescope.sorters" +local conf = require("telescope.config").values +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local pickers = require "telescope.pickers" +local utils = require "telescope.utils" + +local lsp = {} + +local function call_hierarchy(opts, method, title, direction, item) + vim.lsp.buf_request(opts.bufnr, method, { item = item }, function(err, result) + if err then + vim.api.nvim_err_writeln("Error handling " .. title .. ": " .. err.message) + return + end + + if not result or vim.tbl_isempty(result) then + return + end + + local locations = {} + for _, ch_call in pairs(result) do + local ch_item = ch_call[direction] + for _, rng in pairs(ch_call.fromRanges) do + table.insert(locations, { + filename = vim.uri_to_fname(ch_item.uri), + text = ch_item.name, + lnum = rng.start.line + 1, + col = rng.start.character + 1, + }) + end + end + + pickers + .new(opts, { + prompt_title = title, + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }) + :find() + end) +end + +local function pick_call_hierarchy_item(call_hierarchy_items) + if not call_hierarchy_items then + return + end + if #call_hierarchy_items == 1 then + return call_hierarchy_items[1] + end + local items = {} + for i, item in pairs(call_hierarchy_items) do + local entry = item.detail or item.name + table.insert(items, string.format("%d. %s", i, entry)) + end + local choice = vim.fn.inputlist(items) + if choice < 1 or choice > #items then + return + end + return choice +end + +local function calls(opts, direction) + local params = vim.lsp.util.make_position_params() + vim.lsp.buf_request(opts.bufnr, "textDocument/prepareCallHierarchy", params, function(err, result) + if err then + vim.api.nvim_err_writeln("Error when preparing call hierarchy: " .. err) + return + end + + local call_hierarchy_item = pick_call_hierarchy_item(result) + if not call_hierarchy_item then + return + end + + if direction == "from" then + call_hierarchy(opts, "callHierarchy/incomingCalls", "LSP Incoming Calls", direction, call_hierarchy_item) + else + call_hierarchy(opts, "callHierarchy/outgoingCalls", "LSP Outgoing Calls", direction, call_hierarchy_item) + end + end) +end + +lsp.incoming_calls = function(opts) + calls(opts, "from") +end + +lsp.outgoing_calls = function(opts) + calls(opts, "to") +end + +---@type { [string]: fun(results: table, items: table, opts: table): table, table } +local action_handlers = { + ["textDocument/references"] = function(results, items, opts) + if not opts.include_current_line then + local retresults = {} + local retitems = {} + + for i, item in pairs(items) do + if + not ( + item.filename == vim.api.nvim_buf_get_name(opts.bufnr) + and item.lnum == vim.api.nvim_win_get_cursor(opts.winnr)[1] + ) + then + table.insert(retresults, results[i]) + table.insert(retitems, items[i]) + end + end + + return retresults, retitems + end + + return results, items + end, +} + +---@param action string +---@param locations table +---@param items table +---@param opts table +---@return table results, table items +local apply_action_handler = function(action, locations, items, opts) + local handler = action_handlers[action] + if handler then + return handler(locations, items, opts) + end + + return locations, items +end + +---@param action string +---@param title string prompt title +---@param params lsp.TextDocumentPositionParams +---@param opts table +local function list_or_jump(action, title, params, opts) + vim.lsp.buf_request(opts.bufnr, action, params, function(err, result, ctx, _) + if err then + vim.api.nvim_err_writeln("Error when executing " .. action .. " : " .. err.message) + return + end + + if result == nil then + return + end + + local locations = {} + if not utils.islist(result) then + locations = { result } + end + vim.list_extend(locations, result) + + local offset_encoding = vim.lsp.get_client_by_id(ctx.client_id).offset_encoding + local items = vim.lsp.util.locations_to_items(locations, offset_encoding) + + locations, items = apply_action_handler(action, locations, items, opts) + + if vim.tbl_isempty(locations) then + return + end + + if #locations == 1 and opts.jump_type ~= "never" then + local current_uri = params.textDocument.uri + local target_uri = locations[1].uri or locations[1].targetUri + if current_uri ~= target_uri then + local cmd + local file_path = vim.uri_to_fname(target_uri) + if opts.jump_type == "tab" then + cmd = "tabedit" + elseif opts.jump_type == "split" then + cmd = "new" + elseif opts.jump_type == "vsplit" then + cmd = "vnew" + elseif opts.jump_type == "tab drop" then + cmd = "tab drop" + end + + if cmd then + vim.cmd(string.format("%s %s", cmd, file_path)) + end + end + + vim.lsp.util.jump_to_location(locations[1], offset_encoding, opts.reuse_win) + else + pickers + .new(opts, { + prompt_title = title, + finder = finders.new_table { + results = items, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }) + :find() + end + end) +end + +lsp.references = function(opts) + local params = vim.lsp.util.make_position_params(opts.winnr) + params.context = { includeDeclaration = vim.F.if_nil(opts.include_declaration, true) } + return list_or_jump("textDocument/references", "LSP References", params, opts) +end + +lsp.definitions = function(opts) + local params = vim.lsp.util.make_position_params(opts.winnr) + return list_or_jump("textDocument/definition", "LSP Definitions", params, opts) +end + +lsp.type_definitions = function(opts) + local params = vim.lsp.util.make_position_params(opts.winnr) + return list_or_jump("textDocument/typeDefinition", "LSP Type Definitions", params, opts) +end + +lsp.implementations = function(opts) + local params = vim.lsp.util.make_position_params(opts.winnr) + return list_or_jump("textDocument/implementation", "LSP Implementations", params, opts) +end + +local symbols_sorter = function(symbols) + if vim.tbl_isempty(symbols) then + return symbols + end + + local current_buf = vim.api.nvim_get_current_buf() + + -- sort adequately for workspace symbols + local filename_to_bufnr = {} + for _, symbol in ipairs(symbols) do + if filename_to_bufnr[symbol.filename] == nil then + filename_to_bufnr[symbol.filename] = vim.uri_to_bufnr(vim.uri_from_fname(symbol.filename)) + end + symbol.bufnr = filename_to_bufnr[symbol.filename] + end + + table.sort(symbols, function(a, b) + if a.bufnr == b.bufnr then + return a.lnum < b.lnum + end + if a.bufnr == current_buf then + return true + end + if b.bufnr == current_buf then + return false + end + return a.bufnr < b.bufnr + end) + + return symbols +end + +lsp.document_symbols = function(opts) + local params = vim.lsp.util.make_position_params(opts.winnr) + vim.lsp.buf_request(opts.bufnr, "textDocument/documentSymbol", params, function(err, result, _, _) + if err then + vim.api.nvim_err_writeln("Error when finding document symbols: " .. err.message) + return + end + + if not result or vim.tbl_isempty(result) then + utils.notify("builtin.lsp_document_symbols", { + msg = "No results from textDocument/documentSymbol", + level = "INFO", + }) + return + end + + local locations = vim.lsp.util.symbols_to_items(result or {}, opts.bufnr) or {} + locations = utils.filter_symbols(locations, opts, symbols_sorter) + if locations == nil then + -- error message already printed in `utils.filter_symbols` + return + end + + if vim.tbl_isempty(locations) then + utils.notify("builtin.lsp_document_symbols", { + msg = "No document_symbol locations found", + level = "INFO", + }) + return + end + + opts.path_display = { "hidden" } + pickers + .new(opts, { + prompt_title = "LSP Document Symbols", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.prefilter_sorter { + tag = "symbol_type", + sorter = conf.generic_sorter(opts), + }, + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }) + :find() + end) +end + +lsp.workspace_symbols = function(opts) + local params = { query = opts.query or "" } + vim.lsp.buf_request(opts.bufnr, "workspace/symbol", params, function(err, server_result, _, _) + if err then + vim.api.nvim_err_writeln("Error when finding workspace symbols: " .. err.message) + return + end + + local locations = vim.lsp.util.symbols_to_items(server_result or {}, opts.bufnr) or {} + locations = utils.filter_symbols(locations, opts, symbols_sorter) + if locations == nil then + -- error message already printed in `utils.filter_symbols` + return + end + + if vim.tbl_isempty(locations) then + utils.notify("builtin.lsp_workspace_symbols", { + msg = "No results from workspace/symbol. Maybe try a different query: " + .. "'Telescope lsp_workspace_symbols query=example'", + level = "INFO", + }) + return + end + + opts.ignore_filename = vim.F.if_nil(opts.ignore_filename, false) + + pickers + .new(opts, { + prompt_title = "LSP Workspace Symbols", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.prefilter_sorter { + tag = "symbol_type", + sorter = conf.generic_sorter(opts), + }, + }) + :find() + end) +end + +local function get_workspace_symbols_requester(bufnr, opts) + local cancel = function() end + + return function(prompt) + local tx, rx = channel.oneshot() + cancel() + _, cancel = vim.lsp.buf_request(bufnr, "workspace/symbol", { query = prompt }, tx) + + -- Handle 0.5 / 0.5.1 handler situation + local err, res = rx() + assert(not err, err) + + local locations = vim.lsp.util.symbols_to_items(res or {}, bufnr) or {} + if not vim.tbl_isempty(locations) then + locations = utils.filter_symbols(locations, opts, symbols_sorter) or {} + end + return locations + end +end + +lsp.dynamic_workspace_symbols = function(opts) + pickers + .new(opts, { + prompt_title = "LSP Dynamic Workspace Symbols", + finder = finders.new_dynamic { + entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts), + fn = get_workspace_symbols_requester(opts.bufnr, opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = sorters.highlighter_only(opts), + attach_mappings = function(_, map) + map("i", "", actions.to_fuzzy_refine) + return true + end, + }) + :find() +end + +local function check_capabilities(method, bufnr) + --TODO(clason): remove when dropping support for Nvim 0.9 + local get_clients = vim.fn.has "nvim-0.10" == 1 and vim.lsp.get_clients or vim.lsp.get_active_clients + local clients = get_clients { bufnr = bufnr } + + for _, client in pairs(clients) do + if client.supports_method(method, { bufnr = bufnr }) then + return true + end + end + + if #clients == 0 then + utils.notify("builtin.lsp_*", { + msg = "no client attached", + level = "INFO", + }) + else + utils.notify("builtin.lsp_*", { + msg = "server does not support " .. method, + level = "INFO", + }) + end + return false +end + +local feature_map = { + ["document_symbols"] = "textDocument/documentSymbol", + ["references"] = "textDocument/references", + ["definitions"] = "textDocument/definition", + ["type_definitions"] = "textDocument/typeDefinition", + ["implementations"] = "textDocument/implementation", + ["workspace_symbols"] = "workspace/symbol", + ["incoming_calls"] = "callHierarchy/incomingCalls", + ["outgoing_calls"] = "callHierarchy/outgoingCalls", +} + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + local method = feature_map[k] + if method and not check_capabilities(method, opts.bufnr) then + return + end + v(opts) + end + end + + return mod +end + +return apply_checks(lsp) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/init.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/init.lua new file mode 100644 index 00000000..d46ff30a --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/builtin/init.lua @@ -0,0 +1,590 @@ +---@tag telescope.builtin + +---@config { ['field_heading'] = "Options", ["module"] = "telescope.builtin" } + +---@brief [[ +--- Telescope Builtins is a collection of community maintained pickers to support common workflows. It can be used as +--- reference when writing PRs, Telescope extensions, your own custom pickers, or just as a discovery tool for all of +--- the amazing pickers already shipped with Telescope! +--- +--- Any of these functions can just be called directly by doing: +--- +--- :lua require('telescope.builtin').$NAME_OF_PICKER() +--- +--- To use any of Telescope's default options or any picker-specific options, call your desired picker by passing a lua +--- table to the picker with all of the options you want to use. Here's an example with the live_grep picker: +--- +--- +--- :lua require('telescope.builtin').live_grep({ +--- prompt_title = 'find string in open buffers...', +--- grep_open_files = true +--- }) +--- -- or with dropdown theme +--- :lua require('telescope.builtin').find_files(require('telescope.themes').get_dropdown{ +--- previewer = false +--- }) +--- +---@brief ]] + +local builtin = {} + +-- Ref: https://github.com/tjdevries/lazy.nvim +local function require_on_exported_call(mod) + return setmetatable({}, { + __index = function(_, picker) + return function(...) + return require(mod)[picker](...) + end + end, + }) +end + +-- +-- +-- File-related Pickers +-- +-- + +--- Search for a string and get results live as you type, respects .gitignore +---@param opts table: options to pass to the picker +---@field cwd string: root dir to search from (default: cwd, use utils.buffer_dir() to search relative to open buffer) +---@field grep_open_files boolean: if true, restrict search to open files only, mutually exclusive with `search_dirs` +---@field search_dirs table: directory/directories/files to search, mutually exclusive with `grep_open_files` +---@field glob_pattern string|table: argument to be used with `--glob`, e.g. "*.toml", can use the opposite "!*.toml" +---@field type_filter string: argument to be used with `--type`, e.g. "rust", see `rg --type-list` +---@field additional_args function|table: additional arguments to be passed on. Can be fn(opts) -> tbl +---@field max_results number: define a upper result value +---@field disable_coordinates boolean: don't show the line & row numbers (default: false) +---@field file_encoding string: file encoding for the entry & previewer +builtin.live_grep = require_on_exported_call("telescope.builtin.__files").live_grep + +--- Searches for the string under your cursor or the visual selection in your current working directory +---@param opts table: options to pass to the picker +---@field cwd string: root dir to search from (default: cwd, use utils.buffer_dir() to search relative to open buffer) +---@field search string: the query to search +---@field grep_open_files boolean: if true, restrict search to open files only, mutually exclusive with `search_dirs` +---@field search_dirs table: directory/directories/files to search, mutually exclusive with `grep_open_files` +---@field use_regex boolean: if true, special characters won't be escaped, allows for using regex (default: false) +---@field word_match string: can be set to `-w` to enable exact word matches +---@field additional_args function|table: additional arguments to be passed on. Can be fn(opts) -> tbl +---@field disable_coordinates boolean: don't show the line and row numbers (default: false) +---@field only_sort_text boolean: only sort the text, not the file, line or row (default: false) +---@field file_encoding string: file encoding for the entry & previewer +builtin.grep_string = require_on_exported_call("telescope.builtin.__files").grep_string + +--- Search for files (respecting .gitignore) +---@param opts table: options to pass to the picker +---@field cwd string: root dir to search from (default: cwd, use utils.buffer_dir() to search relative to open buffer) +---@field find_command function|table: cmd to use for the search. Can be a fn(opts) -> tbl (default: autodetect) +---@field file_entry_encoding string: encoding of output of `find_command` +---@field follow boolean: if true, follows symlinks (i.e. uses `-L` flag for the `find` command) +---@field hidden boolean: determines whether to show hidden files or not (default: false) +---@field no_ignore boolean: show files ignored by .gitignore, .ignore, etc. (default: false) +---@field no_ignore_parent boolean: show files ignored by .gitignore, .ignore, etc. in parent dirs. (default: false) +---@field search_dirs table: directory/directories/files to search +---@field search_file string: specify a filename to search for +---@field file_encoding string: file encoding for the previewer +builtin.find_files = require_on_exported_call("telescope.builtin.__files").find_files + +--- This is an alias for the `find_files` picker +builtin.fd = builtin.find_files + +--- Lists function names, variables, and other symbols from treesitter queries +--- - Default keymaps: +--- - ``: show autocompletion menu to prefilter your query by kind of ts node you want to see (i.e. `:var:`) +---@field show_line boolean: if true, shows the row:column that the result is found at (default: true) +---@field bufnr number: specify the buffer number where treesitter should run. (default: current buffer) +---@field symbol_width number: defines the width of the symbol section (default: 25) +---@field symbols string|table: filter results by symbol kind(s) +---@field ignore_symbols string|table: list of symbols to ignore +---@field symbol_highlights table: string -> string. Matches symbol with hl_group +---@field file_encoding string: file encoding for the previewer +builtin.treesitter = require_on_exported_call("telescope.builtin.__files").treesitter + +--- Live fuzzy search inside of the currently open buffer +---@param opts table: options to pass to the picker +---@field skip_empty_lines boolean: if true we don't display empty lines (default: false) +---@field results_ts_highlight boolean: highlight result entries with treesitter (default: true) +---@field file_encoding string: file encoding for the previewer +builtin.current_buffer_fuzzy_find = require_on_exported_call("telescope.builtin.__files").current_buffer_fuzzy_find + +--- Lists tags in current directory with tag location file preview (users are required to run ctags -R to generate tags +--- or update when introducing new changes) +---@param opts table: options to pass to the picker +---@field cwd string: root dir to search from (default: cwd, use utils.buffer_dir() to search relative to open buffer) +---@field ctags_file string: specify a particular ctags file to use +---@field show_line boolean: if true, shows the content of the line the tag is found on in the picker (default: true) +---@field only_sort_tags boolean: if true we will only sort tags (default: false) +---@field fname_width number: defines the width of the filename section (default: 30) +builtin.tags = require_on_exported_call("telescope.builtin.__files").tags + +--- Lists all of the tags for the currently open buffer, with a preview +---@param opts table: options to pass to the picker +---@field cwd string: root dir to search from (default: cwd, use utils.buffer_dir() to search relative to open buffer) +---@field ctags_file string: specify a particular ctags file to use +---@field show_line boolean: if true, shows the content of the line the tag is found on in the picker (default: true) +---@field only_sort_tags boolean: if true we will only sort tags (default: false) +---@field fname_width number: defines the width of the filename section (default: 30) +builtin.current_buffer_tags = require_on_exported_call("telescope.builtin.__files").current_buffer_tags + +-- +-- +-- Git-related Pickers +-- +-- + +--- Fuzzy search for files tracked by Git. This command lists the output of the `git ls-files` command, +--- respects .gitignore +--- - Default keymaps: +--- - ``: opens the currently selected file +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field show_untracked boolean: if true, adds `--others` flag to command and shows untracked files (default: false) +---@field recurse_submodules boolean: if true, adds the `--recurse-submodules` flag to command (default: false) +---@field git_command table: command that will be executed. {"git","ls-files","--exclude-standard","--cached"} +---@field file_encoding string: file encoding for the previewer +builtin.git_files = require_on_exported_call("telescope.builtin.__git").files + +--- Lists commits for current directory with diff preview +--- - Default keymaps: +--- - ``: checks out the currently selected commit +--- - `m`: resets current branch to selected commit using mixed mode +--- - `s`: resets current branch to selected commit using soft mode +--- - `h`: resets current branch to selected commit using hard mode +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field git_command table: command that will be executed. {"git","log","--pretty=oneline","--abbrev-commit","--","."} +builtin.git_commits = require_on_exported_call("telescope.builtin.__git").commits + +--- Lists commits for current buffer with diff preview +--- - Default keymaps or your overridden `select_` keys: +--- - ``: checks out the currently selected commit +--- - ``: opens a diff in a vertical split +--- - ``: opens a diff in a horizontal split +--- - ``: opens a diff in a new tab +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field current_file string: specify the current file that should be used for bcommits (default: current buffer) +---@field git_command table: command that will be executed. {"git","log","--pretty=oneline","--abbrev-commit"} +builtin.git_bcommits = require_on_exported_call("telescope.builtin.__git").bcommits + +--- Lists commits for a range of lines in the current buffer with diff preview +--- In visual mode, lists commits for the selected lines +--- With operator mode enabled, lists commits inside the text object/motion +--- - Default keymaps or your overridden `select_` keys: +--- - ``: checks out the currently selected commit +--- - ``: opens a diff in a vertical split +--- - ``: opens a diff in a horizontal split +--- - ``: opens a diff in a new tab +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field current_file string: specify the current file that should be used for bcommits (default: current buffer) +---@field git_command table: command that will be executed. the last element must be "-L". {"git","log","--pretty=oneline","--abbrev-commit","--no-patch","-L"} +---@field from number: the first line number in the range (default: current line) +---@field to number: the last line number in the range (default: the value of `from`) +---@field operator boolean: select lines in operator-pending mode (default: false) +builtin.git_bcommits_range = require_on_exported_call("telescope.builtin.__git").bcommits_range + +--- List branches for current directory, with output from `git log --oneline` shown in the preview window +--- - Default keymaps: +--- - ``: checks out the currently selected branch +--- - ``: tracks currently selected branch +--- - ``: rebases currently selected branch +--- - ``: creates a new branch, with confirmation prompt before creation +--- - ``: deletes the currently selected branch, with confirmation prompt before deletion +--- - ``: merges the currently selected branch, with confirmation prompt before deletion +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field show_remote_tracking_branches boolean: show remote tracking branches like origin/main (default: true) +---@field pattern string: specify the pattern to match all refs +builtin.git_branches = require_on_exported_call("telescope.builtin.__git").branches + +--- Lists git status for current directory +--- - Default keymaps: +--- - ``: stages or unstages the currently selected file +--- - ``: opens the currently selected file +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field git_icons table: string -> string. Matches name with icon (see source code, make_entry.lua git_icon_defaults) +---@field expand_dir boolean: pass flag `-uall` to show files in untracked directories (default: true) +builtin.git_status = require_on_exported_call("telescope.builtin.__git").status + +--- Lists stash items in current repository +--- - Default keymaps: +--- - ``: runs `git apply` for currently selected stash +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field show_branch boolean: if we should display the branch name for git stash entries (default: true) +builtin.git_stash = require_on_exported_call("telescope.builtin.__git").stash + +-- +-- +-- Internal and Vim-related Pickers +-- +-- + +--- Lists all of the community maintained pickers built into Telescope +---@param opts table: options to pass to the picker +---@field include_extensions boolean: if true will show the pickers of the installed extensions (default: false) +---@field use_default_opts boolean: if the selected picker should use its default options (default: false) +builtin.builtin = require_on_exported_call("telescope.builtin.__internal").builtin + +--- Opens the previous picker in the identical state (incl. multi selections) +--- - Notes: +--- - Requires `cache_picker` in setup or when having invoked pickers, see |telescope.defaults.cache_picker| +---@param opts table: options to pass to the picker +---@field cache_index number: what picker to resume, where 1 denotes most recent (default: 1) +builtin.resume = require_on_exported_call("telescope.builtin.__internal").resume + +--- Opens a picker over previously cached pickers in their preserved states (incl. multi selections) +--- - Default keymaps: +--- - ``: delete the selected cached picker +--- - Notes: +--- - Requires `cache_picker` in setup or when having invoked pickers, see |telescope.defaults.cache_picker| +---@param opts table: options to pass to the picker +builtin.pickers = require_on_exported_call("telescope.builtin.__internal").pickers + +--- Use the telescope... +---@param opts table: options to pass to the picker +---@field show_pluto boolean: we love Pluto (default: false, because its a hidden feature) +---@field show_moon boolean: we love the Moon (default: false, because its a hidden feature) +builtin.planets = require_on_exported_call("telescope.builtin.__internal").planets + +--- Lists symbols inside of `data/telescope-sources/*.json` found in your runtime path +--- or found in `stdpath("data")/telescope/symbols/*.json`. The second path can be customized. +--- We provide a couple of default symbols which can be found in +--- https://github.com/nvim-telescope/telescope-symbols.nvim. This repos README also provides more +--- information about the format in which the symbols have to be. +---@param opts table: options to pass to the picker +---@field symbol_path string: specify the second path. Default: `stdpath("data")/telescope/symbols/*.json` +---@field sources table: specify a table of sources you want to load this time +builtin.symbols = require_on_exported_call("telescope.builtin.__internal").symbols + +--- Lists available plugin/user commands and runs them on `` +---@param opts table: options to pass to the picker +---@field show_buf_command boolean: show buf local command (Default: true) +builtin.commands = require_on_exported_call("telescope.builtin.__internal").commands + +--- Lists items in the quickfix list, jumps to location on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field nr number: specify the quickfix list number +builtin.quickfix = require_on_exported_call("telescope.builtin.__internal").quickfix + +--- Lists all quickfix lists in your history and open them with `builtin.quickfix`. It seems that neovim +--- only keeps the full history for 10 lists +---@param opts table: options to pass to the picker +builtin.quickfixhistory = require_on_exported_call("telescope.builtin.__internal").quickfixhistory + +--- Lists items from the current window's location list, jumps to location on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +builtin.loclist = require_on_exported_call("telescope.builtin.__internal").loclist + +--- Lists previously open files, opens on `` +---@param opts table: options to pass to the picker +---@field cwd string: specify a working directory to filter oldfiles by +---@field only_cwd boolean: show only files in the cwd (default: false) +---@field cwd_only boolean: alias for only_cwd +---@field file_encoding string: file encoding for the previewer +builtin.oldfiles = require_on_exported_call("telescope.builtin.__internal").oldfiles + +--- Lists commands that were executed recently, and reruns them on `` +--- - Default keymaps: +--- - ``: open the command line with the text of the currently selected result populated in it +---@param opts table: options to pass to the picker +---@field filter_fn function: filter fn(cmd:string). true if the history command should be presented. +builtin.command_history = require_on_exported_call("telescope.builtin.__internal").command_history + +--- Lists searches that were executed recently, and reruns them on `` +--- - Default keymaps: +--- - ``: open a search window with the text of the currently selected search result populated in it +---@param opts table: options to pass to the picker +builtin.search_history = require_on_exported_call("telescope.builtin.__internal").search_history + +--- Lists vim options, allows you to edit the current value on `` +---@param opts table: options to pass to the picker +builtin.vim_options = require_on_exported_call("telescope.builtin.__internal").vim_options + +--- Lists available help tags and opens a new window with the relevant help info on `` +---@param opts table: options to pass to the picker +---@field lang string: specify language (default: vim.o.helplang) +---@field fallback boolean: fallback to en if language isn't installed (default: true) +builtin.help_tags = require_on_exported_call("telescope.builtin.__internal").help_tags + +--- Lists manpage entries, opens them in a help window on `` +---@param opts table: options to pass to the picker +---@field sections table: a list of sections to search, use `{ "ALL" }` to search in all sections (default: { "1" }) +---@field man_cmd function: that returns the man command. (Default: `apropos ""` on linux, `apropos " "` on macos) +builtin.man_pages = require_on_exported_call("telescope.builtin.__internal").man_pages + +--- Lists lua modules and reloads them on `` +---@param opts table: options to pass to the picker +---@field column_len number: define the max column len for the module name (default: dynamic, longest module name) +builtin.reloader = require_on_exported_call("telescope.builtin.__internal").reloader + +--- Lists open buffers in current neovim instance, opens selected buffer on `` +---@param opts table: options to pass to the picker +---@field cwd string: specify a working directory to filter buffers list by +---@field show_all_buffers boolean: if true, show all buffers, including unloaded buffers (default: true) +---@field ignore_current_buffer boolean: if true, don't show the current buffer in the list (default: false) +---@field only_cwd boolean: if true, only show buffers in the current working directory (default: false) +---@field cwd_only boolean: alias for only_cwd +---@field sort_lastused boolean: Sorts current and last buffer to the top and selects the lastused (default: false) +---@field sort_mru boolean: Sorts all buffers after most recent used. Not just the current and last one (default: false) +---@field bufnr_width number: Defines the width of the buffer numbers in front of the filenames (default: dynamic) +---@field file_encoding string: file encoding for the previewer +---@field sort_buffers function: sort fn(bufnr_a, bufnr_b). true if bufnr_a should go first. Runs after sorting by most recent (if specified) +---@field select_current boolean: select current buffer (default: false) +builtin.buffers = require_on_exported_call("telescope.builtin.__internal").buffers + +--- Lists available colorschemes and applies them on `` +---@param opts table: options to pass to the picker +---@field colors table: a list of additional colorschemes to explicitly make available to telescope (default: {}) +---@field enable_preview boolean: if true, will preview the selected color +builtin.colorscheme = require_on_exported_call("telescope.builtin.__internal").colorscheme + +--- Lists vim marks and their value, jumps to the mark on `` +---@param opts table: options to pass to the picker +---@field file_encoding string: file encoding for the previewer +---@field mark_type string: filter marks by type (default: "all", options: "all"|"global"|"local") +builtin.marks = require_on_exported_call("telescope.builtin.__internal").marks + +--- Lists vim registers, pastes the contents of the register on `` +--- - Default keymaps: +--- - ``: edit the contents of the currently selected register +---@param opts table: options to pass to the picker +builtin.registers = require_on_exported_call("telescope.builtin.__internal").registers + +--- Lists normal mode keymappings, runs the selected keymap on `` +---@param opts table: options to pass to the picker +---@field modes table: a list of short-named keymap modes to search (default: { "n", "i", "c", "x" }) +---@field show_plug boolean: if true, the keymaps for which the lhs contains "" are also shown (default: true) +---@field only_buf boolean: if true, only show the buffer-local keymaps (default: false) +---@field lhs_filter function: filter(lhs:string) -> boolean. true for keymap.lhs if the keymap should be shown (optional) +---@field filter function: filter(km:keymap) -> boolean. true for the keymap if it should be shown (optional) +builtin.keymaps = require_on_exported_call("telescope.builtin.__internal").keymaps + +--- Lists all available filetypes, sets currently open buffer's filetype to selected filetype in Telescope on `` +---@param opts table: options to pass to the picker +builtin.filetypes = require_on_exported_call("telescope.builtin.__internal").filetypes + +--- Lists all available highlights +---@param opts table: options to pass to the picker +builtin.highlights = require_on_exported_call("telescope.builtin.__internal").highlights + +--- Lists vim autocommands and goes to their declaration on `` +---@param opts table: options to pass to the picker +builtin.autocommands = require_on_exported_call("telescope.builtin.__internal").autocommands + +--- Lists spelling suggestions for the current word under the cursor, replaces word with selected suggestion on `` +---@param opts table: options to pass to the picker +builtin.spell_suggest = require_on_exported_call("telescope.builtin.__internal").spell_suggest + +--- Lists the tag stack for the current window, jumps to tag on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +builtin.tagstack = require_on_exported_call("telescope.builtin.__internal").tagstack + +--- Lists items from Vim's jumplist, jumps to location on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +builtin.jumplist = require_on_exported_call("telescope.builtin.__internal").jumplist + +-- +-- +-- LSP-related Pickers +-- +-- + +--- Lists LSP references for word under the cursor, jumps to reference on `` +---@param opts table: options to pass to the picker +---@field include_declaration boolean: include symbol declaration in the lsp references (default: true) +---@field include_current_line boolean: include current line (default: false) +---@field jump_type string: how to goto reference if there is only one and the definition file is different from the current file, values: "tab", "tab drop", "split", "vsplit", "never" +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_references = require_on_exported_call("telescope.builtin.__lsp").references + +--- Lists LSP incoming calls for word under the cursor, jumps to reference on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_incoming_calls = require_on_exported_call("telescope.builtin.__lsp").incoming_calls + +--- Lists LSP outgoing calls for word under the cursor, jumps to reference on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_outgoing_calls = require_on_exported_call("telescope.builtin.__lsp").outgoing_calls + +--- Goto the definition of the word under the cursor, if there's only one, otherwise show all options in Telescope +---@param opts table: options to pass to the picker +---@field jump_type string: how to goto definition if there is only one and the definition file is different from the current file, values: "tab", "tab drop", "split", "vsplit", "never" +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field reuse_win boolean: jump to existing window if buffer is already opened (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_definitions = require_on_exported_call("telescope.builtin.__lsp").definitions + +--- Goto the definition of the type of the word under the cursor, if there's only one, +--- otherwise show all options in Telescope +---@param opts table: options to pass to the picker +---@field jump_type string: how to goto definition if there is only one and the definition file is different from the current file, values: "tab", "tab drop", "split", "vsplit", "never" +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field reuse_win boolean: jump to existing window if buffer is already opened (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_type_definitions = require_on_exported_call("telescope.builtin.__lsp").type_definitions + +--- Goto the implementation of the word under the cursor if there's only one, otherwise show all options in Telescope +---@param opts table: options to pass to the picker +---@field jump_type string: how to goto implementation if there is only one and the definition file is different from the current file, values: "tab", "tab drop", "split", "vsplit", "never" +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field reuse_win boolean: jump to existing window if buffer is already opened (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_implementations = require_on_exported_call("telescope.builtin.__lsp").implementations + +--- Lists LSP document symbols in the current buffer +--- - Default keymaps: +--- - ``: show autocompletion menu to prefilter your query by type of symbol you want to see (i.e. `:variable:`) +---@param opts table: options to pass to the picker +---@field fname_width number: defines the width of the filename section (default: 30) +---@field symbol_width number: defines the width of the symbol section (default: 25) +---@field symbol_type_width number: defines the width of the symbol type section (default: 8) +---@field show_line boolean: if true, shows the content of the line the tag is found on (default: false) +---@field symbols string|table: filter results by symbol kind(s) +---@field ignore_symbols string|table: list of symbols to ignore +---@field symbol_highlights table: string -> string. Matches symbol with hl_group +---@field file_encoding string: file encoding for the previewer +builtin.lsp_document_symbols = require_on_exported_call("telescope.builtin.__lsp").document_symbols + +--- Lists LSP document symbols in the current workspace +--- - Default keymaps: +--- - ``: show autocompletion menu to prefilter your query by type of symbol you want to see (i.e. `:variable:`) +---@param opts table: options to pass to the picker +---@field query string: for what to query the workspace (default: "") +---@field fname_width number: defines the width of the filename section (default: 30) +---@field symbol_width number: defines the width of the symbol section (default: 25) +---@field symbol_type_width number: defines the width of the symbol type section (default: 8) +---@field show_line boolean: if true, shows the content of the line the tag is found on (default: false) +---@field symbols string|table: filter results by symbol kind(s) +---@field ignore_symbols string|table: list of symbols to ignore +---@field symbol_highlights table: string -> string. Matches symbol with hl_group +---@field file_encoding string: file encoding for the previewer +builtin.lsp_workspace_symbols = require_on_exported_call("telescope.builtin.__lsp").workspace_symbols + +--- Dynamically lists LSP for all workspace symbols +--- - Default keymaps: +--- - ``: show autocompletion menu to prefilter your query by type of symbol you want to see (i.e. `:variable:`), only works after refining to fuzzy search using +---@param opts table: options to pass to the picker +---@field fname_width number: defines the width of the filename section (default: 30) +---@field show_line boolean: if true, shows the content of the line the symbol is found on (default: false) +---@field symbols string|table: filter results by symbol kind(s) +---@field ignore_symbols string|table: list of symbols to ignore +---@field symbol_highlights table: string -> string. Matches symbol with hl_group +---@field file_encoding string: file encoding for the previewer +builtin.lsp_dynamic_workspace_symbols = require_on_exported_call("telescope.builtin.__lsp").dynamic_workspace_symbols + +-- +-- +-- Diagnostics Pickers +-- +-- + +--- Lists diagnostics +--- - Fields: +--- - `All severity flags can be passed as `string` or `number` as per `:vim.diagnostic.severity:` +--- - Default keymaps: +--- - ``: show autocompletion menu to prefilter your query with the diagnostic you want to see (i.e. `:warning:`) +--- - sort_by option: +--- - "buffer": order by bufnr (prioritizing current bufnr), severity, lnum +--- - "severity": order by severity, bufnr (prioritizing current bufnr), lnum +---@param opts table: options to pass to the picker +---@field bufnr number|nil: Buffer number to get diagnostics from. Use 0 for current buffer or nil for all buffers +---@field severity string|number: filter diagnostics by severity name (string) or id (number) +---@field severity_limit string|number: keep diagnostics equal or more severe wrt severity name (string) or id (number) +---@field severity_bound string|number: keep diagnostics equal or less severe wrt severity name (string) or id (number) +---@field root_dir string|boolean: if set to string, get diagnostics only for buffers under this dir otherwise cwd +---@field no_unlisted boolean: if true, get diagnostics only for listed buffers +---@field no_sign boolean: hide DiagnosticSigns from Results (default: false) +---@field line_width string|number: set length of diagnostic entry text in Results. Use 'full' for full untruncated text +---@field namespace number: limit your diagnostics to a specific namespace +---@field disable_coordinates boolean: don't show the line & row numbers (default: false) +---@field sort_by string: sort order of the diagnostics results; see above notes (default: "buffer") +builtin.diagnostics = require_on_exported_call("telescope.builtin.__diagnostics").get + +local apply_config = function(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + local pickers_conf = require("telescope.config").pickers + + opts = opts or {} + opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf() + opts.winnr = opts.winnr or vim.api.nvim_get_current_win() + local pconf = pickers_conf[k] or {} + local defaults = (function() + if pconf.theme then + return require("telescope.themes")["get_" .. pconf.theme](pconf) + end + return vim.deepcopy(pconf) + end)() + + if pconf.mappings then + defaults.attach_mappings = function(_, map) + for mode, tbl in pairs(pconf.mappings) do + for key, action in pairs(tbl) do + map(mode, key, action) + end + end + return true + end + end + + if pconf.attach_mappings and opts.attach_mappings then + local opts_attach = opts.attach_mappings + opts.attach_mappings = function(prompt_bufnr, map) + pconf.attach_mappings(prompt_bufnr, map) + return opts_attach(prompt_bufnr, map) + end + end + + if defaults.attach_mappings and opts.attach_mappings then + local opts_attach = opts.attach_mappings + opts.attach_mappings = function(prompt_bufnr, map) + defaults.attach_mappings(prompt_bufnr, map) + return opts_attach(prompt_bufnr, map) + end + end + + v(vim.tbl_extend("force", defaults, opts)) + end + end + + return mod +end + +-- We can't do this in one statement because tree-sitter-lua docgen gets confused if we do +builtin = apply_config(builtin) +return builtin diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/command.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/command.lua new file mode 100644 index 00000000..0da84bac --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/command.lua @@ -0,0 +1,262 @@ +---@tag telescope.command +---@config { ["module"] = "telescope.command" } + +---@brief [[ +--- +--- Telescope commands can be called through two apis, +--- the lua api and the viml api. +--- +--- The lua api is the more direct way to interact with Telescope, as you directly call the +--- lua functions that Telescope defines. +--- It can be called in a lua file using commands like: +---
+--- `require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})`
+--- 
+--- If you want to use this api from a vim file you should prepend `lua` to the command, as below: +---
+--- `lua require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})`
+--- 
+--- If you want to use this api from a neovim command line you should prepend `:lua` to +--- the command, as below: +---
+--- `:lua require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})`
+--- 
+--- +--- The viml api is more indirect, as first the command must be parsed to the relevant lua +--- equivalent, which brings some limitations. +--- The viml api can be called using commands like: +---
+--- `:Telescope find_files hidden=true layout_config={"prompt_position":"top"}`
+--- 
+--- This involves setting options using an `=` and using viml syntax for lists and +--- dictionaries when the corresponding lua function requires a table. +--- +--- One limitation of the viml api is that there can be no spaces in any of the options. +--- For example, if you want to use the `cwd` option for `find_files` to specify that you +--- only want to search within the folder `/foo bar/subfolder/` you could not do that using the +--- viml api, as the path name contains a space. +--- Similarly, you could NOT set the `prompt_position` to `"top"` using the following command: +---
+--- `:Telescope find_files layout_config={ "prompt_position" : "top" }`
+--- 
+--- as there are spaces in the option. +--- +---@brief ]] +local themes = require "telescope.themes" +local builtin = require "telescope.builtin" +local extensions = require("telescope._extensions").manager +local config = require "telescope.config" +local utils = require "telescope.utils" +local command = {} + +local arg_value = { + ["nil"] = nil, + ['""'] = "", + ['"'] = "", +} + +local bool_type = { + ["false"] = false, + ["true"] = true, +} + +local split_keywords = { + ["find_command"] = true, + ["vimgrep_arguments"] = true, + ["sections"] = true, + ["search_dirs"] = true, + ["symbols"] = true, + ["ignore_symbols"] = true, +} + +-- convert command line string arguments to +-- lua number boolean type and nil value +command.convert_user_opts = function(user_opts) + local default_opts = config.values + + local _switch = { + ["boolean"] = function(key, val) + if val == "false" then + user_opts[key] = false + return + end + user_opts[key] = true + end, + ["number"] = function(key, val) + user_opts[key] = tonumber(val) + end, + ["string"] = function(key, val) + if arg_value[val] ~= nil then + user_opts[key] = arg_value[val] + return + end + + if bool_type[val] ~= nil then + user_opts[key] = bool_type[val] + end + end, + ["table"] = function(key, val) + local ok, eval = pcall(vim.fn.eval, val) + if ok then + user_opts[key] = eval + else + local err + eval, err = loadstring("return " .. val) + if err ~= nil then + -- discard invalid lua expression + user_opts[key] = nil + elseif eval ~= nil then + ok, eval = pcall(eval) + if ok and type(eval) == "table" then + -- allow if return a table only + user_opts[key] = eval + else + -- otherwise return nil (allows split check later) + user_opts[key] = nil + end + end + end + end, + } + + local _switch_metatable = { + __index = function(_, k) + utils.notify("command", { + msg = string.format("Type of '%s' does not match", k), + level = "WARN", + }) + end, + } + + setmetatable(_switch, _switch_metatable) + + for key, val in pairs(user_opts) do + if split_keywords[key] then + _switch["table"](key, val) + if user_opts[key] == nil then + user_opts[key] = vim.split(val, ",") + end + elseif default_opts[key] ~= nil then + _switch[type(default_opts[key])](key, val) + elseif tonumber(val) ~= nil then + _switch["number"](key, val) + else + _switch["string"](key, val) + end + end +end + +-- receive the viml command args +-- it should be a table value like +-- { +-- cmd = 'find_files', +-- theme = 'dropdown', +-- extension_type = 'command' +-- opts = { +-- cwd = '***', +-- } +local function run_command(args) + local user_opts = args or {} + if next(user_opts) == nil and not user_opts.cmd then + utils.notify("command", { + msg = "Command missing arguments", + level = "ERROR", + }) + return + end + + local cmd = user_opts.cmd + local opts = user_opts.opts or {} + local extension_type = user_opts.extension_type or "" + local theme = user_opts.theme or "" + + if next(opts) ~= nil then + command.convert_user_opts(opts) + end + + if string.len(theme) > 0 then + local func = themes[theme] or themes["get_" .. theme] + opts = func(opts) + end + + if string.len(extension_type) > 0 and extension_type ~= '"' then + extensions[cmd][extension_type](opts) + return + end + + if builtin[cmd] then + builtin[cmd](opts) + return + end + + if rawget(extensions, cmd) then + extensions[cmd][cmd](opts) + return + end + + local ok = pcall(require("telescope").load_extension, cmd) + if ok then + extensions[cmd][cmd](opts) + return + end + + utils.notify("run_command", { + msg = "Unknown command", + level = "ERROR", + }) +end + +-- @Summary get extensions sub command +-- register extensions dap gh etc. +-- input in command line `Telescope gh ` +-- Returns a list for each extension. +function command.get_extensions_subcommand() + local exts = require("telescope._extensions").manager + local complete_ext_table = {} + for cmd, value in pairs(exts) do + if type(value) == "table" then + local subcmds = {} + for key, _ in pairs(value) do + table.insert(subcmds, key) + end + complete_ext_table[cmd] = subcmds + end + end + return complete_ext_table +end + +function command.register_keyword(keyword) + split_keywords[keyword] = true +end + +function command.load_command(cmd, ...) + local args = { ... } + if cmd == nil then + run_command { cmd = "builtin" } + return + end + + local user_opts = { + cmd = cmd, + opts = {}, + } + + for _, arg in ipairs(args) do + if arg:find("=", 1) == nil then + user_opts["extension_type"] = arg + else + local param = vim.split(arg, "=") + local key = table.remove(param, 1) + param = table.concat(param, "=") + if key == "theme" then + user_opts["theme"] = param + else + user_opts.opts[key] = param + end + end + end + + run_command(user_opts) +end + +return command diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/config.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/config.lua new file mode 100644 index 00000000..71614166 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/config.lua @@ -0,0 +1,991 @@ +local strings = require "plenary.strings" +local deprecated = require "telescope.deprecated" +local sorters = require "telescope.sorters" +local os_sep = require("plenary.path").path.sep +local has_win = vim.fn.has "win32" == 1 + +-- Keep the values around between reloads +_TelescopeConfigurationValues = _TelescopeConfigurationValues or {} +_TelescopeConfigurationPickers = _TelescopeConfigurationPickers or {} + +local function first_non_null(...) + local n = select("#", ...) + for i = 1, n do + local value = select(i, ...) + + if value ~= nil then + return value + end + end +end + +-- A function that creates an amended copy of the `base` table, +-- by replacing keys at "level 2" that match keys in "level 1" in `priority`, +-- and then performs a deep_extend. +-- May give unexpected results if used with tables of "depth" +-- greater than 2. +local smarter_depth_2_extend = function(priority, base) + local result = {} + for key, val in pairs(base) do + if type(val) ~= "table" then + result[key] = first_non_null(priority[key], val) + else + result[key] = {} + for k, v in pairs(val) do + result[key][k] = first_non_null(priority[k], v) + end + end + end + for key, val in pairs(priority) do + if type(val) ~= "table" then + result[key] = first_non_null(val, result[key]) + else + result[key] = vim.tbl_extend("keep", val, result[key] or {}) + end + end + return result +end + +local resolve_table_opts = function(priority, base) + if priority == false or (priority == nil and base == false) then + return false + end + if priority == nil and type(base) == "table" then + return base + end + return smarter_depth_2_extend(priority, base) +end + +-- TODO: Add other major configuration points here. +-- selection_strategy + +local config = {} +config.smarter_depth_2_extend = smarter_depth_2_extend +config.resolve_table_opts = resolve_table_opts + +config.values = _TelescopeConfigurationValues +config.descriptions = {} +config.pickers = _TelescopeConfigurationPickers + +function config.set_pickers(pickers) + pickers = vim.F.if_nil(pickers, {}) + + for k, v in pairs(pickers) do + config.pickers[k] = v + end +end + +local layout_config_defaults = { + + horizontal = { + width = 0.8, + height = 0.9, + prompt_position = "bottom", + preview_cutoff = 120, + }, + + vertical = { + width = 0.8, + height = 0.9, + prompt_position = "bottom", + preview_cutoff = 40, + }, + + center = { + width = 0.5, + height = 0.4, + preview_cutoff = 40, + prompt_position = "top", + }, + + cursor = { + width = 0.8, + height = 0.9, + preview_cutoff = 40, + }, + + bottom_pane = { + height = 25, + prompt_position = "top", + preview_cutoff = 120, + }, +} + +local layout_config_description = string.format( + [[ + Determines the default configuration values for layout strategies. + See |telescope.layout| for details of the configurations options for + each strategy. + + Allows setting defaults for all strategies as top level options and + for overriding for specific options. + For example, the default values below set the default width to 80%% of + the screen width for all strategies except 'center', which has width + of 50%% of the screen width. + + Default: %s +]], + vim.inspect(layout_config_defaults, { newline = "\n ", indent = " " }) +) + +-- A table of all the usual defaults for telescope. +-- Keys will be the name of the default, +-- values will be a list where: +-- - first entry is the value +-- - second entry is the description of the option + +local telescope_defaults = {} +config.descriptions_order = {} +local append = function(name, val, doc) + telescope_defaults[name] = { val, doc } + table.insert(config.descriptions_order, name) +end + +append( + "sorting_strategy", + "descending", + [[ + Determines the direction "better" results are sorted towards. + + Available options are: + - "descending" (default) + - "ascending"]] +) + +append( + "selection_strategy", + "reset", + [[ + Determines how the cursor acts after each sort iteration. + + Available options are: + - "reset" (default) + - "follow" + - "row" + - "closest" + - "none"]] +) + +append( + "scroll_strategy", + "cycle", + [[ + Determines what happens if you try to scroll past the view of the + picker. + + Available options are: + - "cycle" (default) + - "limit"]] +) + +append( + "layout_strategy", + "horizontal", + [[ + Determines the default layout of Telescope pickers. + See |telescope.layout| for details of the available strategies. + + Default: 'horizontal']] +) + +append( + "create_layout", + nil, + [[ + Configure the layout of Telescope pickers. + See |telescope.pickers.layout| for details. + + Default: 'nil']] +) + +append("layout_config", layout_config_defaults, layout_config_description) + +append( + "cycle_layout_list", + { "horizontal", "vertical" }, + [[ + Determines the layouts to cycle through when using `actions.layout.cycle_layout_next` + and `actions.layout.cycle_layout_prev`. + Should be a list of "layout setups". + Each "layout setup" can take one of two forms: + 1. string + This is interpreted as the name of a `layout_strategy` + 2. table + A table with possible keys `layout_strategy`, `layout_config` and `previewer` + + Default: { "horizontal", "vertical" } + ]] +) + +append( + "winblend", + function() + return vim.o.winblend + end, + [[ + Configure winblend for telescope floating windows. See |winblend| for + more information. Type can be a number or a function returning a + number + + Default: function() return vim.o.winblend end]] +) + +append( + "wrap_results", + false, + [[ + Word wrap the search results + + Default: false]] +) + +append( + "prompt_prefix", + "> ", + [[ + The character(s) that will be shown in front of Telescope's prompt. + + Default: '> ']] +) + +append( + "selection_caret", + "> ", + [[ + The character(s) that will be shown in front of the current selection. + + Default: '> ']] +) + +append( + "entry_prefix", + " ", + [[ + Prefix in front of each result entry. Current selection not included. + + Default: ' ']] +) + +append( + "multi_icon", + "+", + [[ + Symbol to add in front of a multi-selected result entry. + Replaces final character of |telescope.defaults.selection_caret| and + |telescope.defaults.entry_prefix| as appropriate. + To have no icon, set to the empty string. + + Default: '+']] +) + +append( + "initial_mode", + "insert", + [[ + Determines in which mode telescope starts. Valid Keys: + `insert` and `normal`. + + Default: "insert"]] +) + +append( + "border", + true, + [[ + Boolean defining if borders are added to Telescope windows. + + Default: true]] +) + +append( + "path_display", + {}, + [[ + Determines how file paths are displayed. + + path_display can be set to an array with a combination of: + - "hidden" hide file names + - "tail" only display the file name, and not the path + - "absolute" display absolute paths + - "smart" remove as much from the path as possible to only show + the difference between the displayed paths. + Warning: The nature of the algorithm might have a negative + performance impact! + - "shorten" only display the first character of each directory in + the path + - "truncate" truncates the start of the path when the whole path will + not fit. To increase the gap between the path and the edge, + set truncate to number `truncate = 3` + - "filename_first" shows filenames first and then the directories + + You can also specify the number of characters of each directory name + to keep by setting `path_display.shorten = num`. + e.g. for a path like + `alpha/beta/gamma/delta.txt` + setting `path_display.shorten = 1` will give a path like: + `a/b/g/delta.txt` + Similarly, `path_display.shorten = 2` will give a path like: + `al/be/ga/delta.txt` + + You can also further customise the shortening behaviour by + setting `path_display.shorten = { len = num, exclude = list }`, + where `len` acts as above, and `exclude` is a list of positions + that are not shortened. Negative numbers in the list are considered + relative to the end of the path. + e.g. for a path like + `alpha/beta/gamma/delta.txt` + setting `path_display.shorten = { len = 1, exclude = {1, -1} }` + will give a path like: + `alpha/b/g/delta.txt` + setting `path_display.shorten = { len = 2, exclude = {2, -2} }` + will give a path like: + `al/beta/gamma/de` + + path_display can also be set to 'filename_first' to put the filename + in front. + + path_display = { + "filename_first" + }, + + The directory structure can be reversed as follows: + + path_display = { + filename_first = { + reverse_directories = true + } + }, + + path_display can also be set to 'hidden' string to hide file names + + path_display can also be set to a function for custom formatting of + the path display with the following signature + + Signature: fun(opts: table, path: string): string, table? + + The optional table is an list of positions and highlight groups to + set the highlighting of the return path string. + + Example: + + -- Format path as "file.txt (path\to\file\)" + path_display = function(opts, path) + local tail = require("telescope.utils").path_tail(path) + return string.format("%s (%s)", tail, path) + end, + + -- Format path and add custom highlighting + path_display = function(opts, path) + local tail = require("telescope.utils").path_tail(path) + path = string.format("%s (%s)", tail, path) + + local highlights = { + { + { + 0, -- highlight start position + #path, -- highlight end position + }, + "Comment", -- highlight group name + }, + } + + return path, highlights + end + + Default: {}]] +) + +append( + "borderchars", + { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + [[ + Set the borderchars of telescope floating windows. It has to be a + table of 8 string values. + + Default: { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }]] +) + +append( + "get_status_text", + function(self, opts) + local multi_select_cnt = #(self:get_multi_selection()) + local showing_cnt = (self.stats.processed or 0) - (self.stats.filtered or 0) + local total_cnt = self.stats.processed or 0 + + local status_icon = "" + local status_text + if opts and not opts.completed then + status_icon = "*" + end + + if showing_cnt == 0 and total_cnt == 0 then + status_text = status_icon + elseif multi_select_cnt == 0 then + status_text = string.format("%s %s / %s", status_icon, showing_cnt, total_cnt) + else + status_text = string.format("%s %s / %s / %s", status_icon, multi_select_cnt, showing_cnt, total_cnt) + end + + -- quick workaround for extmark right_align side-scrolling limitation + -- https://github.com/nvim-telescope/telescope.nvim/issues/2929 + local prompt_width = vim.api.nvim_win_get_width(self.prompt_win) + local cursor_col = vim.api.nvim_win_get_cursor(self.prompt_win)[2] + local prefix_display_width = strings.strdisplaywidth(self.prompt_prefix) --[[@as integer]] + local prefix_width = #self.prompt_prefix + local prefix_shift = 0 + if prefix_display_width ~= prefix_width then + prefix_shift = prefix_display_width + end + + local cursor_occluded = (prompt_width - cursor_col - #status_text + prefix_shift) < 0 + if cursor_occluded then + return "" + else + return status_text + end + end, + [[ + A function that determines what the virtual text looks like. + Signature: function(picker) -> str + + Default: function that shows current count / all]] +) + +append( + "hl_result_eol", + true, + [[ + Changes if the highlight for the selected item in the results + window is always the full width of the window + + Default: true]] +) + +append( + "dynamic_preview_title", + false, + [[ + Will change the title of the preview window dynamically, where it + is supported. For example, the preview window's title could show up as + the full filename. + + Default: false]] +) + +append( + "results_title", + "Results", + [[ + Defines the default title of the results window. A false value + can be used to hide the title altogether. + + Default: "Results"]] +) + +append( + "prompt_title", + "Prompt", + [[ + Defines the default title of the prompt window. A false value + can be used to hide the title altogether. Most of the times builtins + define a prompt_title which will be preferred over this default. + + Default: "Prompt"]] +) + +append( + "mappings", + {}, + [[ + Your mappings to override telescope's default mappings. + + See: ~ + |telescope.mappings| + ]] +) + +append( + "default_mappings", + nil, + [[ + Not recommended to use except for advanced users. + + Will allow you to completely remove all of telescope's default maps + and use your own. + + Default: nil + ]] +) + +append( + "history", + { + path = vim.fn.stdpath "data" .. os_sep .. "telescope_history", + limit = 100, + handler = function(...) + return require("telescope.actions.history").get_simple_history(...) + end, + cycle_wrap = false, + }, + [[ + This field handles the configuration for prompt history. + By default it is a table, with default values (more below). + To disable history, set it to false. + + Currently mappings still need to be added, Example: + mappings = { + i = { + [""] = require('telescope.actions').cycle_history_next, + [""] = require('telescope.actions').cycle_history_prev, + }, + }, + + Fields: + - path: The path to the telescope history as string. + Default: stdpath("data")/telescope_history + - limit: The amount of entries that will be written in the + history. + Warning: If limit is set to nil it will grow unbound. + Default: 100 + - handler: A lua function that implements the history. + This is meant as a developer setting for extensions to + override the history handling, e.g., + https://github.com/nvim-telescope/telescope-smart-history.nvim, + which allows context sensitive (cwd + picker) history. + + Default: + require('telescope.actions.history').get_simple_history + - cycle_wrap: Indicates whether the cycle_history_next and + cycle_history_prev functions should wrap around to the + beginning or end of the history entries on reaching + their respective ends + Default: false]] +) + +append( + "cache_picker", + { + num_pickers = 1, + limit_entries = 1000, + ignore_empty_prompt = false, + }, + [[ + This field handles the configuration for picker caching. + By default it is a table, with default values (more below). + To disable caching, set it to false. + + Caching preserves all previous multi selections and results and + therefore may result in slowdown or increased RAM occupation + if too many pickers (`cache_picker.num_pickers`) or entries + ('cache_picker.limit_entries`) are cached. + + Fields: + - num_pickers: The number of pickers to be cached. + Set to -1 to preserve all pickers of your + session. If passed to a picker, the cached + pickers with indices larger than + `cache_picker.num_pickers` will be cleared. + Default: 1 + - limit_entries: The amount of entries that will be saved for + each picker. + Default: 1000 + - ignore_empty_prompt: If true, the picker will not be cached if + the prompt is empty (i.e., no text has been + typed at the time of closing the prompt). + Default: false + ]] +) + +append( + "preview", + { + check_mime_type = not has_win, + filesize_limit = 25, + highlight_limit = 1, + timeout = 250, + treesitter = true, + msg_bg_fillchar = "╱", + hide_on_startup = false, + }, + [[ + This field handles the global configuration for previewers. + By default it is a table, with default values (more below). + To disable previewing, set it to false. If you have disabled previewers + globally, but want to opt in to previewing for single pickers, you will have to + pass `preview = true` or `preview = {...}` (your config) to the `opts` of + your picker. + + Fields: + - check_mime_type: Use `file` if available to try to infer whether the + file to preview is a binary if filetype + detection fails. + Windows users get `file` from: + https://github.com/julian-r/file-windows + Set to false to attempt to preview any mime type. + Default: true for all OS excl. Windows + - filesize_limit: The maximum file size in MB attempted to be previewed. + Set to false to attempt to preview any file size. + Default: 25 + - highlight_limit: The maximum file size in MB attempted to be highlighted. + Set to false to attempt to highlight any file size. + Default: 1 + - timeout: Timeout the previewer if the preview did not + complete within `timeout` milliseconds. + Set to false to not timeout preview. + Default: 250 + - hook(s): Function(s) that takes `(filepath, bufnr, opts)`, where opts + exposes winid and ft (filetype). + Available hooks (in order of priority): + {filetype, mime, filesize, timeout}_hook + Important: the filetype_hook must return true or false + to indicate whether to continue (true) previewing or not (false), + respectively. + Two examples: + local putils = require("telescope.previewers.utils") + ... -- preview is called in telescope.setup { ... } + preview = { + -- 1) Do not show previewer for certain files + filetype_hook = function(filepath, bufnr, opts) + -- you could analogously check opts.ft for filetypes + local excluded = vim.tbl_filter(function(ending) + return filepath:match(ending) + end, { + ".*%.csv", + ".*%.toml", + }) + if not vim.tbl_isempty(excluded) then + putils.set_preview_message( + bufnr, + opts.winid, + string.format("I don't like %s files!", + excluded[1]:sub(5, -1)) + ) + return false + end + return true + end, + -- 2) Truncate lines to preview window for too large files + filesize_hook = function(filepath, bufnr, opts) + local path = require("plenary.path"):new(filepath) + -- opts exposes winid + local height = vim.api.nvim_win_get_height(opts.winid) + local lines = vim.split(path:head(height), "[\r]?\n") + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + end, + } + The configuration recipes for relevant examples. + Note: we use vim.filetype filetype detection, + so if you have troubles with files not + highlighting correctly, please read + |vim.filetype| + Default: nil + - treesitter: Determines whether the previewer performs treesitter + highlighting, which falls back to regex-based highlighting. + `true`: treesitter highlighting for all available filetypes + `false`: regex-based highlighting for all filetypes + `table`: may contain the following keys: + - enable boolean|table: if boolean, enable ts + highlighting for all supported + filetypes. + if table, ts highlighting is only + enabled for given filetypes. + - disable table: list of filetypes for which ts highlighting + is not used if `enable = true`. + Default: true + - msg_bg_fillchar: Character to fill background of unpreviewable buffers with + Default: "╱" + - hide_on_startup: Hide previewer when picker starts. Previewer can be toggled + with actions.layout.toggle_preview. + Default: false + - ls_short: Determines whether to use the `--short` flag for the `ls` + command when previewing directories. Otherwise will result + to using `--long`. + Default: false + ]] +) + +append( + "vimgrep_arguments", + { "rg", "--color=never", "--no-heading", "--with-filename", "--line-number", "--column", "--smart-case" }, + [[ + Defines the command that will be used for `live_grep` and `grep_string` + pickers. + Hint: Make sure that color is currently set to `never` because we do + not yet interpret color codes + Hint 2: Make sure that these options are in your changes arguments: + "--no-heading", "--with-filename", "--line-number", "--column" + because we need them so the ripgrep output is in the correct format. + + Default: { + "rg", + "--color=never", + "--no-heading", + "--with-filename", + "--line-number", + "--column", + "--smart-case" + }]] +) + +append( + "use_less", + true, + [[ + Boolean if less should be enabled in term_previewer (deprecated and + currently no longer used in the builtin pickers). + + Default: true]] +) + +append( + "set_env", + nil, + [[ + Set an environment for term_previewer. A table of key values: + Example: { COLORTERM = "truecolor", ... } + Hint: Empty table is not allowed. + + Default: nil]] +) + +append( + "color_devicons", + true, + [[ + Boolean if devicons should be enabled or not. If set to false, the + text highlight group is used. + Hint: Coloring only works if |termguicolors| is enabled. + + Default: true]] +) + +append( + "file_sorter", + sorters.get_fzy_sorter, + [[ + A function pointer that specifies the file_sorter. This sorter will + be used for find_files, git_files and similar. + Hint: If you load a native sorter, you don't need to change this value, + the native sorter will override it anyway. + + Default: require("telescope.sorters").get_fzy_sorter]] +) + +append( + "generic_sorter", + sorters.get_fzy_sorter, + [[ + A function pointer to the generic sorter. The sorter that should be + used for everything that is not a file. + Hint: If you load a native sorter, you don't need to change this value, + the native sorter will override it anyway. + + Default: require("telescope.sorters").get_fzy_sorter]] +) + +--TODO(conni2461): Why is this even configurable??? +append( + "prefilter_sorter", + sorters.prefilter, + [[ + This points to a wrapper sorter around the generic_sorter that is able + to do prefiltering. + It's usually used for lsp_*_symbols and lsp_*_diagnostics + + Default: require("telescope.sorters").prefilter]] +) + +append( + "tiebreak", + function(current_entry, existing_entry, _) + return #current_entry.ordinal < #existing_entry.ordinal + end, + [[ + A function that determines how to break a tie when two entries have + the same score. + Having a function that always returns false would keep the entries in + the order they are found, so existing_entry before current_entry. + Vice versa always returning true would place the current_entry + before the existing_entry. + + Signature: function(current_entry, existing_entry, prompt) -> boolean + + Default: function that breaks the tie based on the length of the + entry's ordinal]] +) + +append( + "file_ignore_patterns", + nil, + [[ + A table of lua regex that define the files that should be ignored. + Example: { "^scratch/" } -- ignore all files in scratch directory + Example: { "%.npz" } -- ignore all npz files + See: https://www.lua.org/manual/5.1/manual.html#5.4.1 for more + information about lua regex + Note: `file_ignore_patterns` will be used in all pickers that have a + file associated. This might lead to the problem that lsp_ pickers + aren't displaying results because they might be ignored by + `file_ignore_patterns`. For example, setting up node_modules as ignored + will never show node_modules in any results, even if you are + interested in lsp_ results. + + If you only want `file_ignore_patterns` for `find_files` and + `grep_string`/`live_grep` it is suggested that you setup `gitignore` + and have fd and or ripgrep installed because both tools will not show + `gitignore`d files on default. + + Default: nil]] +) + +append( + "get_selection_window", + function() + return 0 + end, + [[ + Function that takes function(picker, entry) and returns a window id. + The window ID will be used to decide what window the chosen file will + be opened in and the cursor placed in upon leaving the picker. + + Default: `function() return 0 end` + ]] +) + +append( + "git_worktrees", + nil, + [[ + A table of arrays of detached working trees with keys `gitdir` and `toplevel`. + Used to pass `--git-dir` and `--work-tree` flags to git commands when telescope fails + to infer the top-level directory of a given working tree based on cwd. + Example: + git_worktrees = { + { + toplevel = vim.env.HOME, + gitdir = vim.env.HOME .. '/.cfg' + } + } + + Default: nil + ]] +) + +append( + "file_previewer", + function(...) + return require("telescope.previewers").vim_buffer_cat.new(...) + end, + [[ + Function pointer to the default file_previewer. It is mostly used + for find_files, git_files and similar. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").cat.new + + Default: require("telescope.previewers").vim_buffer_cat.new]] +) + +append( + "grep_previewer", + function(...) + return require("telescope.previewers").vim_buffer_vimgrep.new(...) + end, + [[ + Function pointer to the default vim_grep previewer. It is mostly + used for live_grep, grep_string and similar. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").vimgrep.new + + Default: require("telescope.previewers").vim_buffer_vimgrep.new]] +) + +append( + "qflist_previewer", + function(...) + return require("telescope.previewers").vim_buffer_qflist.new(...) + end, + [[ + Function pointer to the default qflist previewer. It is mostly + used for qflist, loclist and lsp. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").qflist.new + + Default: require("telescope.previewers").vim_buffer_qflist.new]] +) + +append( + "buffer_previewer_maker", + function(...) + return require("telescope.previewers").buffer_previewer_maker(...) + end, + [[ + Developer option that defines the underlining functionality + of the buffer previewer. + For interesting configuration examples take a look at + https://github.com/nvim-telescope/telescope.nvim/wiki/Configuration-Recipes + + Default: require("telescope.previewers").buffer_previewer_maker]] +) + +-- @param user_defaults table: a table where keys are the names of options, +-- and values are the ones the user wants +-- @param tele_defaults table: (optional) a table containing all of the defaults +-- for telescope [defaults to `telescope_defaults`] +function config.set_defaults(user_defaults, tele_defaults) + user_defaults = vim.F.if_nil(user_defaults, {}) + tele_defaults = vim.F.if_nil(tele_defaults, telescope_defaults) + + -- Check if using layout keywords outside of `layout_config` + deprecated.options(user_defaults) + + local function get(name, default_val) + if name == "layout_config" then + return smarter_depth_2_extend( + vim.F.if_nil(user_defaults[name], {}), + vim.tbl_deep_extend("keep", vim.F.if_nil(config.values[name], {}), vim.F.if_nil(default_val, {})) + ) + end + if name == "history" or name == "cache_picker" or name == "preview" then + if user_defaults[name] == false or config.values[name] == false then + return false + end + if user_defaults[name] == true then + return vim.F.if_nil(config.values[name], {}) + end + + return smarter_depth_2_extend( + vim.F.if_nil(user_defaults[name], {}), + vim.tbl_deep_extend("keep", vim.F.if_nil(config.values[name], {}), vim.F.if_nil(default_val, {})) + ) + end + return first_non_null(user_defaults[name], config.values[name], default_val) + end + + local function set(name, default_val, description) + assert(description, "Config values must always have a description") + + config.values[name] = get(name, default_val) + config.descriptions[name] = strings.dedent(description) + end + + for key, info in pairs(tele_defaults) do + set(key, info[1], info[2]) + end + + local M = {} + M.get = get + return M +end + +function config.clear_defaults() + for k, _ in pairs(config.values) do + config.values[k] = nil + end +end + +config.set_defaults() + +return config diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/config/resolve.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/config/resolve.lua new file mode 100644 index 00000000..10c9178c --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/config/resolve.lua @@ -0,0 +1,323 @@ +---@tag telescope.resolve +---@config { ["module"] = "telescope.resolve" } + +---@brief [[ +--- Provides "resolver functions" to allow more customisable inputs for options. +---@brief ]] + +--[[ + +Ultimately boils down to getting `height` and `width` for: +- prompt +- preview +- results + +No matter what you do, I will not make prompt have more than one line (atm) + +Result of `resolve` should be a table with: + +{ + preview = { + get_width = function(self, max_columns, max_lines) end + get_height = function(self, max_columns, max_lines) end + }, + + result = { + get_width = function(self, max_columns, max_lines) end + get_height = function(self, max_columns, max_lines) end + }, + + prompt = { + get_width = function(self, max_columns, max_lines) end + get_height = function(self, max_columns, max_lines) end + }, + + total ? +} + +!!NOT IMPLEMENTED YET!! + +height = + 1. 0 <= number < 1 + This means total height as a percentage + + 2. 1 <= number + This means total height as a fixed number + + 3. function(picker, columns, lines) + -> returns one of the above options + return math.min(110, max_rows * .5) + + if columns > 120 then + return 110 + else + return 0.6 + end + + 3. { + previewer = x, + results = x, + prompt = x, + }, this means I do my best guess I can for these, given your options + +width = + exactly the same, but switch to width + + +{ + height = 0.5, + width = { + previewer = 0.25, + results = 30, + } +} + +https://github.com/nvim-lua/telescope.nvim/pull/43 + +After we get layout, we should try and make top-down sorting work. +That's the next step to scrolling. + +{ + vertical = { + }, + horizontal = { + }, + + height = ... + width = ... +} + + + +--]] + +local resolver = {} +local _resolve_map = {} + +local throw_invalid_config_option = function(key, value) + error(string.format("Invalid configuration option for '%s': '%s'", key, tostring(value)), 2) +end + +-- Booleans +_resolve_map[function(val) + return val == false +end] = function(_, val) + return function(...) + return val + end +end + +-- Percentages +_resolve_map[function(val) + return type(val) == "number" and val >= 0 and val < 1 +end] = function(selector, val) + return function(...) + local selected = select(selector, ...) + return math.floor(val * selected) + end +end + +-- Numbers +_resolve_map[function(val) + return type(val) == "number" and val >= 1 +end] = function(selector, val) + return function(...) + local selected = select(selector, ...) + return math.min(val, selected) + end +end + +-- function: +-- Function must have same signature as get_window_layout +-- function(self, max_columns, max_lines): number +-- +-- Resulting number is used for this configuration value. +_resolve_map[function(val) + return type(val) == "function" +end] = function(_, val) + return val +end + +_resolve_map[function(val) + return type(val) == "table" and val["max"] ~= nil and val[1] ~= nil and val[1] >= 0 and val[1] < 1 +end] = function( + selector, + val +) + return function(...) + local selected = select(selector, ...) + return math.min(math.floor(val[1] * selected), val["max"]) + end +end + +_resolve_map[function(val) + return type(val) == "table" and val["min"] ~= nil and val[1] ~= nil and val[1] >= 0 and val[1] < 1 +end] = function( + selector, + val +) + return function(...) + local selected = select(selector, ...) + return math.max(math.floor(val[1] * selected), val["min"]) + end +end + +-- Add padding option +_resolve_map[function(val) + return type(val) == "table" and val["padding"] ~= nil +end] = function(selector, val) + local resolve_pad = function(value) + for k, v in pairs(_resolve_map) do + if k(value) then + return v(selector, value) + end + end + throw_invalid_config_option("padding", value) + end + + return function(...) + local selected = select(selector, ...) + local padding = resolve_pad(val["padding"]) + return math.floor(selected - 2 * padding(...)) + end +end + +--- Converts input to a function that returns the height. +--- The input must take one of five forms: +--- 1. 0 <= number < 1
+--- This means total height as a percentage. +--- 2. 1 <= number
+--- This means total height as a fixed number. +--- 3. function
+--- Must have signature: +--- function(self, max_columns, max_lines): number +--- 4. table of the form: { val, max = ..., min = ... }
+--- val has to be in the first form 0 <= val < 1 and only one is given, +--- `min` or `max` as fixed number +--- 5. table of the form: {padding = `foo`}
+--- where `foo` has one of the previous three forms.
+--- The height is then set to be the remaining space after padding. +--- For example, if the window has height 50, and the input is {padding = 5}, +--- the height returned will be `40 = 50 - 2*5` +--- +--- The returned function will have signature: +--- function(self, max_columns, max_lines): number +resolver.resolve_height = function(val) + for k, v in pairs(_resolve_map) do + if k(val) then + return v(3, val) + end + end + throw_invalid_config_option("height", val) +end + +--- Converts input to a function that returns the width. +--- The input must take one of five forms: +--- 1. 0 <= number < 1
+--- This means total width as a percentage. +--- 2. 1 <= number
+--- This means total width as a fixed number. +--- 3. function
+--- Must have signature: +--- function(self, max_columns, max_lines): number +--- 4. table of the form: { val, max = ..., min = ... }
+--- val has to be in the first form 0 <= val < 1 and only one is given, +--- `min` or `max` as fixed number +--- 5. table of the form: {padding = `foo`}
+--- where `foo` has one of the previous three forms.
+--- The width is then set to be the remaining space after padding. +--- For example, if the window has width 100, and the input is {padding = 5}, +--- the width returned will be `90 = 100 - 2*5` +--- +--- The returned function will have signature: +--- function(self, max_columns, max_lines): number +resolver.resolve_width = function(val) + for k, v in pairs(_resolve_map) do + if k(val) then + return v(2, val) + end + end + + throw_invalid_config_option("width", val) +end + +--- Calculates the adjustment required to move the picker from the middle of the screen to +--- an edge or corner.
+--- The `anchor` can be any of the following strings: +--- - "", "CENTER", "NW", "N", "NE", "E", "SE", "S", "SW", "W" +--- The anchors have the following meanings: +--- - "" or "CENTER":
+--- the picker will remain in the middle of the screen. +--- - Compass directions:
+--- the picker will move to the corresponding edge/corner +--- e.g. "NW" -> "top left corner", "E" -> "right edge", "S" -> "bottom edge" +resolver.resolve_anchor_pos = function(anchor, p_width, p_height, max_columns, max_lines) + anchor = anchor:upper() + local pos = { 0, 0 } + if anchor == "CENTER" then + return pos + end + if anchor:find "W" then + pos[1] = math.ceil((p_width - max_columns) / 2) + 1 + elseif anchor:find "E" then + pos[1] = math.ceil((max_columns - p_width) / 2) - 1 + end + if anchor:find "N" then + pos[2] = math.ceil((p_height - max_lines) / 2) + 1 + elseif anchor:find "S" then + pos[2] = math.ceil((max_lines - p_height) / 2) - 1 + end + return pos +end + +-- duplicate from utils.lua to keep self-contained +-- Win option always returns a table with preview, results, and prompt. +-- It handles many different ways. Some examples are as follows: +-- +-- -- Disable +-- borderchars = false +-- +-- -- All three windows share the same +-- borderchars = { '─', '│', '─', '│', '┌', '┐', '┘', '└'}, +-- +-- -- Each window gets it's own configuration +-- borderchars = { +-- preview = {...}, +-- results = {...}, +-- prompt = {...}, +-- } +-- +-- -- Default to [1] but override with specific items +-- borderchars = { +-- {...} +-- prompt = {...}, +-- } +resolver.win_option = function(val, default) + local islist = require("telescope.utils").islist + if type(val) ~= "table" or islist(val) then + if val == nil then + val = default + end + + return { + preview = val, + results = val, + prompt = val, + } + elseif type(val) == "table" then + assert(not islist(val)) + + local val_to_set = val[1] + if val_to_set == nil then + val_to_set = default + end + + return { + preview = vim.F.if_nil(val.preview, val_to_set), + results = vim.F.if_nil(val.results, val_to_set), + prompt = vim.F.if_nil(val.prompt, val_to_set), + } + end +end + +return resolver diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/debounce.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/debounce.lua new file mode 100644 index 00000000..5afbb2f0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/debounce.lua @@ -0,0 +1,171 @@ +-- Credit: https://gist.github.com/runiq/31aa5c4bf00f8e0843cd267880117201 +-- + +local M = {} + +---Validates args for `throttle()` and `debounce()`. +local function td_validate(fn, ms) + vim.validate { + fn = { fn, "f" }, + ms = { + ms, + function(v) + return type(v) == "number" and v > 0 + end, + "number > 0", + }, + } +end + +--- Throttles a function on the leading edge. Automatically `schedule_wrap()`s. +---@param fn fun(...) Function to throttle +---@param ms number Timeout in ms +---@return fun(...) wrapped_fn Throttled function +---@return uv_timer_t timer Remember to call `timer.close()` at the end or you will leak memory! +function M.throttle_leading(fn, ms) + td_validate(fn, ms) + local timer = vim.loop.new_timer() + local running = false + + local function wrapped_fn(...) + if not running then + timer:start(ms, 0, function() + running = false + end) + running = true + pcall(vim.schedule_wrap(fn), select(1, ...)) + end + end + return wrapped_fn, timer +end + +--- Throttles a function on the trailing edge. Automatically `schedule_wrap()`s. +---@param fn fun(...) Function to throttle +---@param ms number Timeout in ms +---@param last? boolean Whether to use the arguments of the last call to `fn` within the timeframe. +--- Default: Use arguments of the first call. +---@return fun(...) wrapped_fn Throttled function +---@return uv_timer_t timer Remember to call `timer.close()` at the end or you will leak memory! +function M.throttle_trailing(fn, ms, last) + td_validate(fn, ms) + local timer = vim.loop.new_timer() + local running = false + + local wrapped_fn + if not last then + function wrapped_fn(...) + if not running then + local argv = { ... } + local argc = select("#", ...) + + timer:start(ms, 0, function() + running = false + pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc)) + end) + running = true + end + end + else + local argv, argc + function wrapped_fn(...) + argv = { ... } + argc = select("#", ...) + + if not running then + timer:start(ms, 0, function() + running = false + pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc)) + end) + running = true + end + end + end + return wrapped_fn, timer +end + +--- Debounces a function on the leading edge. Automatically `schedule_wrap()`s. +---@param fn fun(...) Function to debounce +---@param ms number Timeout in ms +---@return fun(...) wrapped_fn Debounced function +---@return uv_timer_t timer Remember to call `timer.close()` at the end or you will leak memory! +function M.debounce_leading(fn, ms) + td_validate(fn, ms) + local timer = vim.loop.new_timer() + local running = false + + local function wrapped_fn(...) + timer:start(ms, 0, function() + running = false + end) + + if not running then + running = true + pcall(vim.schedule_wrap(fn), select(1, ...)) + end + end + return wrapped_fn, timer +end + +--- Debounces a function on the trailing edge. Automatically `schedule_wrap()`s. +---@param fn fun(...) Function to debounce +---@param ms number Timeout in ms +---@param first? boolean Whether to use the arguments of the first call to `fn` within the timeframe. +--- Default: Use arguments of the last call. +---@return fun(...) wrapped_fn Debounced function +---@return uv_timer_t timer Remember to call `timer.close()` at the end or you will leak memory! +function M.debounce_trailing(fn, ms, first) + td_validate(fn, ms) + local timer = vim.loop.new_timer() + local wrapped_fn + + if not first then + function wrapped_fn(...) + local argv = { ... } + local argc = select("#", ...) + + timer:start(ms, 0, function() + pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc)) + end) + end + else + local argv, argc + function wrapped_fn(...) + argv = argv or { ... } + argc = argc or select("#", ...) + + timer:start(ms, 0, function() + pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc)) + end) + end + end + return wrapped_fn, timer +end + +--- Test deferment methods (`{throttle,debounce}_{leading,trailing}()`). +---@param bouncer string Bouncer function to test +---@param ms? number Timeout in ms, default 2000. +---@param firstlast? boolean Whether to use the 'other' fn call strategy. +function M.test_defer(bouncer, ms, firstlast) + local bouncers = { + tl = M.throttle_leading, + tt = M.throttle_trailing, + dl = M.debounce_leading, + dt = M.debounce_trailing, + } + + local timeout = ms or 2000 + + local bounced = bouncers[bouncer](function(i) + vim.cmd('echom "' .. bouncer .. ": " .. i .. '"') + end, timeout, firstlast) + + for i, _ in ipairs { 1, 2, 3, 4, 5 } do + bounced(i) + vim.schedule(function() + vim.cmd("echom " .. i) + end) + vim.fn.call("wait", { 1000, "v:false" }) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/deprecated.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/deprecated.lua new file mode 100644 index 00000000..b1cbf2d5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/deprecated.lua @@ -0,0 +1,12 @@ +local deprecated = {} + +deprecated.options = function(opts) + local messages = {} + + if #messages > 0 then + table.insert(messages, 1, "Deprecated options. Please see ':help telescope.changelog'") + vim.api.nvim_err_write(table.concat(messages, "\n \n ") .. "\n \nPress to continue\n") + end +end + +return deprecated diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/entry_manager.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/entry_manager.lua new file mode 100644 index 00000000..a8331e4c --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/entry_manager.lua @@ -0,0 +1,168 @@ +local log = require "telescope.log" + +local LinkedList = require "telescope.algos.linked_list" + +local EntryManager = {} +EntryManager.__index = EntryManager + +function EntryManager:new(max_results, set_entry, info) + log.trace "Creating entry_manager..." + + info = info or {} + info.looped = 0 + info.inserted = 0 + info.find_loop = 0 + + -- state contains list of + -- { entry, score } + -- Stored directly in a table, accessed as [1], [2] + set_entry = set_entry or function() end + + return setmetatable({ + linked_states = LinkedList:new { track_at = max_results }, + info = info, + max_results = max_results, + set_entry = set_entry, + worst_acceptable_score = math.huge, + }, self) +end + +function EntryManager:num_results() + return self.linked_states.size +end + +function EntryManager:get_container(index) + local count = 0 + for val in self.linked_states:iter() do + count = count + 1 + + if count == index then + return val + end + end + + return {} +end + +function EntryManager:get_entry(index) + return self:get_container(index)[1] +end + +function EntryManager:get_score(index) + return self:get_container(index)[2] +end + +function EntryManager:get_ordinal(index) + return self:get_entry(index).ordinal +end + +function EntryManager:find_entry(entry) + local info = self.info + + local count = 0 + for container in self.linked_states:iter() do + count = count + 1 + + if container[1] == entry then + info.find_loop = info.find_loop + count + + return count + end + end + + info.find_loop = info.find_loop + count + return nil +end + +function EntryManager:_update_score_from_tracked() + local linked = self.linked_states + + if linked.tracked then + self.worst_acceptable_score = math.min(self.worst_acceptable_score, linked.tracked[2]) + end +end + +function EntryManager:_insert_container_before(picker, index, linked_node, new_container) + self.linked_states:place_before(index, linked_node, new_container) + self.set_entry(picker, index, new_container[1], new_container[2], true) + + self:_update_score_from_tracked() +end + +function EntryManager:_insert_container_after(picker, index, linked_node, new_container) + self.linked_states:place_after(index, linked_node, new_container) + self.set_entry(picker, index, new_container[1], new_container[2], true) + + self:_update_score_from_tracked() +end + +function EntryManager:_append_container(picker, new_container, should_update) + self.linked_states:append(new_container) + self.worst_acceptable_score = math.min(self.worst_acceptable_score, new_container[2]) + + if should_update then + self.set_entry(picker, self.linked_states.size, new_container[1], new_container[2]) + end +end + +function EntryManager:add_entry(picker, score, entry, prompt) + score = score or 0 + + local max_res = self.max_results + local worst_score = self.worst_acceptable_score + local size = self.linked_states.size + + local info = self.info + info.maxed = info.maxed or 0 + + local new_container = { entry, score } + + -- Short circuit for bad scores -- they never need to be displayed. + -- Just save them and we'll deal with them later. + if score >= worst_score then + return self.linked_states:append(new_container) + end + + -- Short circuit for first entry. + if size == 0 then + self.linked_states:prepend(new_container) + self.set_entry(picker, 1, entry, score) + return + end + + for index, container, node in self.linked_states:ipairs() do + info.looped = info.looped + 1 + + if container[2] > score then + return self:_insert_container_before(picker, index, node, new_container) + end + + if score < 1 and container[2] == score and picker.tiebreak(entry, container[1], prompt) then + return self:_insert_container_before(picker, index, node, new_container) + end + + -- Don't add results that are too bad. + if index >= max_res then + info.maxed = info.maxed + 1 + return self:_append_container(picker, new_container, false) + end + end + + if self.linked_states.size >= max_res then + self.worst_acceptable_score = math.min(self.worst_acceptable_score, score) + end + + return self:_insert_container_after(picker, size + 1, self.linked_states.tail, new_container) +end + +function EntryManager:iter() + local iterator = self.linked_states:iter() + return function() + local val = iterator() + if val then + return val[1] + end + end +end + +return EntryManager diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders.lua new file mode 100644 index 00000000..20698ca8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders.lua @@ -0,0 +1,231 @@ +local Job = require "plenary.job" + +local make_entry = require "telescope.make_entry" +local log = require "telescope.log" + +local async_static_finder = require "telescope.finders.async_static_finder" +local async_oneshot_finder = require "telescope.finders.async_oneshot_finder" +local async_job_finder = require "telescope.finders.async_job_finder" + +local finders = {} + +local _callable_obj = function() + local obj = {} + + obj.__index = obj + obj.__call = function(t, ...) + return t:_find(...) + end + + obj.close = function() end + + return obj +end + +--[[ ============================================================= + + JobFinder + +Uses an external Job to get results. Processes results as they arrive. + +For more information about how Jobs are implemented, checkout 'plenary.job' + +-- ============================================================= ]] +local JobFinder = _callable_obj() + +--- Create a new finder command +--- +---@param opts table Keys: +-- fn_command function The function to call +function JobFinder:new(opts) + opts = opts or {} + + assert(not opts.results, "`results` should be used with finder.new_table") + assert(not opts.static, "`static` should be used with finder.new_oneshot_job") + + local obj = setmetatable({ + entry_maker = opts.entry_maker or make_entry.gen_from_string(opts), + fn_command = opts.fn_command, + cwd = opts.cwd, + writer = opts.writer, + + -- Maximum number of results to process. + -- Particularly useful for live updating large queries. + maximum_results = opts.maximum_results, + }, self) + + return obj +end + +function JobFinder:_find(prompt, process_result, process_complete) + log.trace "Finding..." + + if self.job and not self.job.is_shutdown then + log.debug "Shutting down old job" + self.job:shutdown() + end + + local line_num = 0 + local on_output = function(_, line, _) + line_num = line_num + 1 + if not line or line == "" then + return + end + + local entry + if self.entry_maker then + entry = self.entry_maker(line) + if entry then + entry.index = line_num + end + else + entry = line + end + + process_result(entry) + end + + local opts = self:fn_command(prompt) + if not opts then + process_complete() + return + end + + local writer = nil + if opts.writer and Job.is_job(opts.writer) then + writer = opts.writer + elseif opts.writer then + writer = Job:new(opts.writer) + end + + self.job = Job:new { + command = opts.command, + args = opts.args, + cwd = opts.cwd or self.cwd, + + maximum_results = self.maximum_results, + + writer = writer, + + enable_recording = false, + + on_stdout = on_output, + -- on_stderr = on_output, + + on_exit = function() + process_complete() + end, + } + + self.job:start() +end + +local DynamicFinder = _callable_obj() + +function DynamicFinder:new(opts) + opts = opts or {} + + assert(not opts.results, "`results` should be used with finder.new_table") + assert(not opts.static, "`static` should be used with finder.new_oneshot_job") + + local obj = setmetatable({ + curr_buf = opts.curr_buf, + fn = opts.fn, + entry_maker = opts.entry_maker or make_entry.gen_from_string(opts), + }, self) + + return obj +end + +function DynamicFinder:_find(prompt, process_result, process_complete) + local results = self.fn(prompt) + + local result_num = 0 + for _, result in ipairs(results) do + result_num = result_num + 1 + local entry = self.entry_maker(result) + if entry then + entry.index = result_num + end + if process_result(entry) then + return + end + end + + process_complete() +end + +--- Return a new Finder +-- +-- Use at your own risk. +-- This opts dictionary is likely to change, but you are welcome to use it right now. +-- I will try not to change it needlessly, but I will change it sometimes and I won't feel bad. +finders._new = function(opts) + assert(not opts.results, "finder.new is deprecated with `results`. You should use `finder.new_table`") + return JobFinder:new(opts) +end + +finders.new_async_job = function(opts) + if opts.writer then + return finders._new(opts) + end + + return async_job_finder(opts) +end + +finders.new_job = function(command_generator, entry_maker, _, cwd) + return async_job_finder { + command_generator = command_generator, + entry_maker = entry_maker, + cwd = cwd, + } +end + +--- One shot job +---@param command_list string[]: Command list to execute. +---@param opts table: stuff +-- @key entry_maker function Optional: function(line: string) => table +-- @key cwd string +finders.new_oneshot_job = function(command_list, opts) + opts = opts or {} + + assert(not opts.results, "`results` should be used with finder.new_table") + + command_list = vim.deepcopy(command_list) + local command = table.remove(command_list, 1) + + return async_oneshot_finder { + entry_maker = opts.entry_maker or make_entry.gen_from_string(opts), + + cwd = opts.cwd, + maximum_results = opts.maximum_results, + + fn_command = function() + return { + command = command, + args = command_list, + } + end, + } +end + +--- Used to create a finder for a Lua table. +-- If you only pass a table of results, then it will use that as the entries. +-- +-- If you pass a table, and then a function, it's used as: +-- results table, the results to run on +-- entry_maker function, the function to convert results to entries. +finders.new_table = function(t) + return async_static_finder(t) +end + +--- Used to create a finder from a function. +-- +---@param opts table: stuff +-- @key fn function() => list[string] +-- @key entry_maker function Optional: function(line: string) => table +finders.new_dynamic = function(opts) + return DynamicFinder:new(opts) +end + +return finders diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders/async_job_finder.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders/async_job_finder.lua new file mode 100644 index 00000000..23444e7d --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders/async_job_finder.lua @@ -0,0 +1,84 @@ +local async_job = require "telescope._" +local LinesPipe = require("telescope._").LinesPipe + +local make_entry = require "telescope.make_entry" +local log = require "telescope.log" + +return function(opts) + log.trace("Creating async_job:", opts) + local entry_maker = opts.entry_maker or make_entry.gen_from_string(opts) + + local fn_command = function(prompt) + local command_list = opts.command_generator(prompt) + if command_list == nil then + return nil + end + + local command = table.remove(command_list, 1) + + local res = { + command = command, + args = command_list, + } + + return res + end + + local job + + local callable = function(_, prompt, process_result, process_complete) + if job then + job:close(true) + end + + local job_opts = fn_command(prompt) + if not job_opts then + process_complete() + return + end + + local writer = nil + -- if job_opts.writer and Job.is_job(job_opts.writer) then + -- writer = job_opts.writer + if opts.writer then + error "async_job_finder.writer is not yet implemented" + writer = async_job.writer(opts.writer) + end + + local stdout = LinesPipe() + + job = async_job.spawn { + command = job_opts.command, + args = job_opts.args, + cwd = job_opts.cwd or opts.cwd, + env = job_opts.env or opts.env, + writer = writer, + + stdout = stdout, + } + + local line_num = 0 + for line in stdout:iter(true) do + line_num = line_num + 1 + local entry = entry_maker(line) + if entry then + entry.index = line_num + end + if process_result(entry) then + return + end + end + + process_complete() + end + + return setmetatable({ + close = function() + if job then + job:close(true) + end + end, + }, { + __call = callable, + }) +end diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders/async_oneshot_finder.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders/async_oneshot_finder.lua new file mode 100644 index 00000000..cacc0b97 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders/async_oneshot_finder.lua @@ -0,0 +1,104 @@ +local async = require "plenary.async" +local async_job = require "telescope._" +local LinesPipe = require("telescope._").LinesPipe + +local make_entry = require "telescope.make_entry" + +local await_count = 1000 + +return function(opts) + opts = opts or {} + + local entry_maker = opts.entry_maker or make_entry.gen_from_string(opts) + local cwd = opts.cwd + local env = opts.env + local fn_command = assert(opts.fn_command, "Must pass `fn_command`") + + local results = vim.F.if_nil(opts.results, {}) + local num_results = #results + + local job_started = false + local job_completed = false + local stdout = nil + + local job + + return setmetatable({ + close = function() + if job then + job:close() + end + end, + results = results, + entry_maker = entry_maker, + }, { + __call = function(_, prompt, process_result, process_complete) + if not job_started then + local job_opts = fn_command() + + -- TODO: Handle writers. + -- local writer + -- if job_opts.writer and Job.is_job(job_opts.writer) then + -- writer = job_opts.writer + -- elseif job_opts.writer then + -- writer = Job:new(job_opts.writer) + -- end + + stdout = LinesPipe() + job = async_job.spawn { + command = job_opts.command, + args = job_opts.args, + cwd = cwd, + env = env, + + stdout = stdout, + } + + job_started = true + end + + if not job_completed then + if not vim.tbl_isempty(results) then + for _, v in ipairs(results) do + process_result(v) + end + end + for line in stdout:iter(false) do + num_results = num_results + 1 + + if num_results % await_count then + async.util.scheduler() + end + + local entry = entry_maker(line) + if entry then + entry.index = num_results + end + results[num_results] = entry + process_result(entry) + end + + process_complete() + job_completed = true + + return + end + + local current_count = num_results + for index = 1, current_count do + -- TODO: Figure out scheduling... + if index % await_count then + async.util.scheduler() + end + + if process_result(results[index]) then + break + end + end + + if job_completed then + process_complete() + end + end, + }) +end diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders/async_static_finder.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders/async_static_finder.lua new file mode 100644 index 00000000..7db3e99d --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/finders/async_static_finder.lua @@ -0,0 +1,44 @@ +local scheduler = require("plenary.async").util.scheduler + +local make_entry = require "telescope.make_entry" + +return function(opts) + local input_results + if require("telescope.utils").islist(opts) then + input_results = opts + else + input_results = opts.results + end + + local entry_maker = opts.entry_maker or make_entry.gen_from_string(opts) + + local results = {} + for k, v in ipairs(input_results) do + local entry = entry_maker(v) + + if entry then + entry.index = k + table.insert(results, entry) + end + end + + return setmetatable({ + results = results, + entry_maker = entry_maker, + close = function() end, + }, { + __call = function(_, _, process_result, process_complete) + for i, v in ipairs(results) do + if process_result(v) then + break + end + + if i % 1000 == 0 then + scheduler() + end + end + + process_complete() + end, + }) +end diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/from_entry.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/from_entry.lua new file mode 100644 index 00000000..4a66d47e --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/from_entry.lua @@ -0,0 +1,45 @@ +--[[ ============================================================================= + +Get metadata from entries. + +This file is still WIP, so expect some changes if you're trying to consume these APIs. + +This will provide standard mechanism for accessing information from an entry. + +--============================================================================= ]] +local utils = require "telescope.utils" + +local from_entry = {} + +function from_entry.path(entry, validate, escape) + escape = vim.F.if_nil(escape, true) + local path = entry.path + if path == nil then + path = entry.filename + end + if path == nil then + path = entry.value + end + if path == nil then + require("telescope.log").error(string.format("Invalid Entry: '%s'", vim.inspect(entry))) + return + end + + -- only 0 if neither filereadable nor directory + if validate then + -- We need to expand for filereadable and isdirectory + -- TODO(conni2461): we are not going to return the expanded path because + -- this would lead to cache misses in the perviewer. + -- Requires overall refactoring in previewer interface + local expanded = utils.path_expand(path) + if (vim.fn.filereadable(expanded) + vim.fn.isdirectory(expanded)) == 0 then + return + end + end + if escape then + return vim.fn.fnameescape(path) + end + return path +end + +return from_entry diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/health.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/health.lua new file mode 100644 index 00000000..70e5254a --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/health.lua @@ -0,0 +1,133 @@ +local health = vim.health or require "health" +local start = health.start or health.report_start +local ok = health.ok or health.report_ok +local warn = health.warn or health.report_warn +local error = health.error or health.report_error +local info = health.info or health.report_info + +local extension_module = require "telescope._extensions" +local extension_info = require("telescope").extensions +local is_win = vim.api.nvim_call_function("has", { "win32" }) == 1 + +local optional_dependencies = { + { + finder_name = "live-grep", + package = { + { + name = "rg", + url = "[BurntSushi/ripgrep](https://github.com/BurntSushi/ripgrep)", + optional = false, + }, + }, + }, + { + finder_name = "find-files", + package = { + { + name = "fd", + binaries = { "fdfind", "fd" }, + url = "[sharkdp/fd](https://github.com/sharkdp/fd)", + optional = true, + }, + }, + }, +} + +local required_plugins = { + { lib = "plenary", optional = false }, + { + lib = "nvim-treesitter", + optional = true, + info = "(Required for `:Telescope treesitter`.)", + }, +} + +local check_binary_installed = function(package) + local binaries = package.binaries or { package.name } + for _, binary in ipairs(binaries) do + local found = vim.fn.executable(binary) == 1 + if not found and is_win then + binary = binary .. ".exe" + found = vim.fn.executable(binary) == 1 + end + if found then + local handle = io.popen(binary .. " --version") + local binary_version = handle:read "*a" + handle:close() + return true, binary_version + end + end +end + +local function lualib_installed(lib_name) + local res, _ = pcall(require, lib_name) + return res +end + +local M = {} + +M.check = function() + -- Required lua libs + start "Checking for required plugins" + for _, plugin in ipairs(required_plugins) do + if lualib_installed(plugin.lib) then + ok(plugin.lib .. " installed.") + else + local lib_not_installed = plugin.lib .. " not found." + if plugin.optional then + warn(("%s %s"):format(lib_not_installed, plugin.info)) + else + error(lib_not_installed) + end + end + end + + -- external dependencies + -- TODO: only perform checks if user has enabled dependency in their config + start "Checking external dependencies" + + for _, opt_dep in pairs(optional_dependencies) do + for _, package in ipairs(opt_dep.package) do + local installed, version = check_binary_installed(package) + if not installed then + local err_msg = ("%s: not found."):format(package.name) + if package.optional then + warn(("%s %s"):format(err_msg, ("Install %s for extended capabilities"):format(package.url))) + else + error( + ("%s %s"):format( + err_msg, + ("`%s` finder will not function without %s installed."):format(opt_dep.finder_name, package.url) + ) + ) + end + else + local eol = version:find "\n" + local ver = eol and version:sub(0, eol - 1) or "(unknown version)" + ok(("%s: found %s"):format(package.name, ver)) + end + end + end + + -- Extensions + start "===== Installed extensions =====" + + local installed = {} + for extension_name, _ in pairs(extension_info) do + installed[#installed + 1] = extension_name + end + table.sort(installed) + + for _, installed_ext in ipairs(installed) do + local extension_healthcheck = extension_module._health[installed_ext] + + start(string.format("Telescope Extension: `%s`", installed_ext)) + if extension_healthcheck then + extension_healthcheck() + else + info "No healthcheck provided" + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/init.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/init.lua new file mode 100644 index 00000000..acb56e79 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/init.lua @@ -0,0 +1,176 @@ +local _extensions = require "telescope._extensions" + +local telescope = {} + +-- TODO(conni2461): also table of contents for tree-sitter-lua +-- TODO: Add pre to the works +-- ---@pre [[ +-- ---@pre ]] + +---@brief [[ +--- Telescope.nvim is a plugin for fuzzy finding and neovim. It helps you search, +--- filter, find and pick things in Lua. +--- +--- Getting started with telescope: +--- 1. Run `:checkhealth telescope` to make sure everything is installed. +--- 2. Evaluate it is working with +--- `:Telescope find_files` or +--- `:lua require("telescope.builtin").find_files()` +--- 3. Put a `require("telescope").setup()` call somewhere in your neovim config. +--- 4. Read |telescope.setup| to check what config keys are available and what you can put inside the setup call +--- 5. Read |telescope.builtin| to check which builtin pickers are offered and what options these implement +--- 6. Profit +--- +--- The below flow chart illustrates a simplified telescope architecture: +---
+--- ┌───────────────────────────────────────────────────────────┐
+--- │      ┌────────┐                                           │
+--- │      │ Multi  │                                ┌───────+  │
+--- │      │ Select │    ┌───────┐                   │ Entry │  │
+--- │      └─────┬──*    │ Entry │    ┌────────+     │ Maker │  │
+--- │            │   ┌───│Manager│────│ Sorter │┐    └───┬───*  │
+--- │            ▼   ▼   └───────*    └────────┘│        │      │
+--- │            1────────┐                 2───┴──┐     │      │
+--- │      ┌─────│ Picker │                 │Finder│◀────┘      │
+--- │      ▼     └───┬────┘                 └──────*            │
+--- │ ┌────────┐     │       3────────+         ▲               │
+--- │ │Selected│     └───────│ Prompt │─────────┘               │
+--- │ │ Entry  │             └───┬────┘                         │
+--- │ └────────*             ┌───┴────┐  ┌────────┐  ┌────────┐ │
+--- │     │  ▲    4─────────┐│ Prompt │  │(Attach)│  │Actions │ │
+--- │     ▼  └──▶ │ Results ││ Buffer │◀─┤Mappings│◀─┤User Fn │ │
+--- │5─────────┐  └─────────┘└────────┘  └────────┘  └────────┘ │
+--- ││Previewer│                                                │
+--- │└─────────┘                   telescope.nvim architecture  │
+--- └───────────────────────────────────────────────────────────┘
+---
+---   + The `Entry Maker` at least defines
+---     - value: "raw" result of the finder
+---     - ordinal: string to be sorted derived from value
+---     - display: line representation of entry in results buffer
+---
+---   * The finder, entry manager, selected entry, and multi selections
+---     comprises `entries` constructed by the `Entry Maker` from
+---     raw results of the finder (`value`s)
+---
+---  Primary components:
+---   1 Picker: central UI dedicated to varying use cases
+---             (finding files, grepping, diagnostics, etc.)
+---             see :h telescope.builtin
+---   2 Finder: pipe or interactively generates results to pick over
+---   3 Prompt: user input that triggers the finder which sorts results
+---             in order into the entry manager
+---   4 Results: listed entries scored by sorter from finder results
+---   5 Previewer: preview of context of selected entry
+---                see :h telescope.previewers
+--- 
+--- +--- A practical introduction into telescope customization is our +--- `developers.md` (top-level of repo) and `:h telescope.actions` that +--- showcase how to access information about the state of the picker (current +--- selection, etc.). +---
+--- To find out more:
+--- https://github.com/nvim-telescope/telescope.nvim
+---
+---   :h telescope.setup
+---   :h telescope.command
+---   :h telescope.builtin
+---   :h telescope.themes
+---   :h telescope.layout
+---   :h telescope.resolve
+---   :h telescope.actions
+---   :h telescope.actions.state
+---   :h telescope.actions.set
+---   :h telescope.actions.utils
+---   :h telescope.actions.generate
+---   :h telescope.actions.history
+---   :h telescope.previewers
+--- 
+---@brief ]] + +---@tag telescope.nvim +---@config { ["name"] = "INTRODUCTION" } + +--- Setup function to be run by user. Configures the defaults, pickers and +--- extensions of telescope. +--- +--- Usage: +--- +--- require('telescope').setup{ +--- defaults = { +--- -- Default configuration for telescope goes here: +--- -- config_key = value, +--- -- .. +--- }, +--- pickers = { +--- -- Default configuration for builtin pickers goes here: +--- -- picker_name = { +--- -- picker_config_key = value, +--- -- ... +--- -- } +--- -- Now the picker_config_key will be applied every time you call this +--- -- builtin picker +--- }, +--- extensions = { +--- -- Your extension configuration goes here: +--- -- extension_name = { +--- -- extension_config_key = value, +--- -- } +--- -- please take a look at the readme of the extension you want to configure +--- } +--- } +--- +---@param opts table: Configuration opts. Keys: defaults, pickers, extensions +---@eval { ["description"] = require('telescope').__format_setup_keys() } +function telescope.setup(opts) + opts = opts or {} + + if opts.default then + error "'default' is not a valid value for setup. See 'defaults'" + end + + require("telescope.config").set_defaults(opts.defaults) + require("telescope.config").set_pickers(opts.pickers) + _extensions.set_config(opts.extensions) +end + +--- Load an extension. +--- - Notes: +--- - Loading triggers ext setup via the config passed in |telescope.setup| +---@param name string: Name of the extension +function telescope.load_extension(name) + return _extensions.load(name) +end + +--- Register an extension. To be used by plugin authors. +---@param mod table: Module +function telescope.register_extension(mod) + return _extensions.register(mod) +end + +--- Use telescope.extensions to reference any extensions within your configuration.
+--- While the docs currently generate this as a function, it's actually a table. Sorry. +telescope.extensions = require("telescope._extensions").manager + +telescope.__format_setup_keys = function() + local names = require("telescope.config").descriptions_order + local descriptions = require("telescope.config").descriptions + + local result = { "
", "", "Valid keys for {opts.defaults}" }
+  for _, name in ipairs(names) do
+    local desc = descriptions[name]
+
+    table.insert(result, "")
+    table.insert(result, string.format("%s*telescope.defaults.%s*", string.rep(" ", 70 - 20 - #name), name))
+    table.insert(result, string.format("%s: ~", name))
+    for _, line in ipairs(vim.split(desc, "\n")) do
+      table.insert(result, string.format("    %s", line))
+    end
+  end
+
+  table.insert(result, "
") + return result +end + +return telescope diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/log.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/log.lua new file mode 100644 index 00000000..c74378f0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/log.lua @@ -0,0 +1,4 @@ +return require("plenary.log").new { + plugin = "telescope", + level = "info", +} diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/make_entry.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/make_entry.lua new file mode 100644 index 00000000..cf0a1a44 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/make_entry.lua @@ -0,0 +1,1378 @@ +---@tag telescope.make_entry + +---@brief [[ +--- +--- Each picker has a finder made up of two parts, the results which are the +--- data to be displayed, and the entry_maker. These entry_makers are functions +--- returned from make_entry functions. These will be referred to as +--- entry_makers in the following documentation. +--- +--- Every entry maker returns a function that accepts the data to be used for +--- an entry. This function will return an entry table (or nil, meaning skip +--- this entry) which contains the following important keys: +--- - value any: value key can be anything but still required +--- - valid bool (optional): is an optional key because it defaults to true but if the key +--- is set to false it will not be displayed by the picker +--- - ordinal string: is the text that is used for filtering +--- - display string|function: is either a string of the text that is being +--- displayed or a function receiving the entry at a later stage, when the entry +--- is actually being displayed. A function can be useful here if a complex +--- calculation has to be done. `make_entry` can also return a second value - +--- a highlight array which will then apply to the line. Highlight entry in +--- this array has the following signature `{ { start_col, end_col }, hl_group }` +--- - filename string (optional): will be interpreted by the default `` action as +--- open this file +--- - bufnr number (optional): will be interpreted by the default `` action as open +--- this buffer +--- - lnum number (optional): lnum value which will be interpreted by the default `` +--- action as a jump to this line +--- - col number (optional): col value which will be interpreted by the default `` +--- action as a jump to this column +--- +--- For more information on easier displaying, see |telescope.pickers.entry_display| +--- +--- TODO: Document something we call `entry_index` +---@brief ]] + +local entry_display = require "telescope.pickers.entry_display" +local utils = require "telescope.utils" +local strings = require "plenary.strings" +local Path = require "plenary.path" + +local treesitter_type_highlight = { + ["associated"] = "TSConstant", + ["constant"] = "TSConstant", + ["field"] = "TSField", + ["function"] = "TSFunction", + ["method"] = "TSMethod", + ["parameter"] = "TSParameter", + ["property"] = "TSProperty", + ["struct"] = "Struct", + ["var"] = "TSVariableBuiltin", +} + +local lsp_type_highlight = { + ["Class"] = "TelescopeResultsClass", + ["Constant"] = "TelescopeResultsConstant", + ["Field"] = "TelescopeResultsField", + ["Function"] = "TelescopeResultsFunction", + ["Method"] = "TelescopeResultsMethod", + ["Property"] = "TelescopeResultsOperator", + ["Struct"] = "TelescopeResultsStruct", + ["Variable"] = "TelescopeResultsVariable", +} + +local get_filename_fn = function() + local bufnr_name_cache = {} + return function(bufnr) + bufnr = vim.F.if_nil(bufnr, 0) + local c = bufnr_name_cache[bufnr] + if c then + return c + end + + local n = vim.api.nvim_buf_get_name(bufnr) + bufnr_name_cache[bufnr] = n + return n + end +end + +local handle_entry_index = function(opts, t, k) + local override = ((opts or {}).entry_index or {})[k] + if not override then + return + end + + local val, save = override(t, opts) + if save then + rawset(t, k, val) + end + return val +end + +local make_entry = {} + +make_entry.set_default_entry_mt = function(tbl, opts) + return setmetatable({}, { + __index = function(t, k) + local override = handle_entry_index(opts, t, k) + if override then + return override + end + + -- Only hit tbl once + local val = tbl[k] + if val then + rawset(t, k, val) + end + + return val + end, + }) +end + +do + local lookup_keys = { + display = 1, + ordinal = 1, + value = 1, + } + + function make_entry.gen_from_string(opts) + local mt_string_entry = { + __index = function(t, k) + local override = handle_entry_index(opts, t, k) + if override then + return override + end + + return rawget(t, rawget(lookup_keys, k)) + end, + } + + return function(line) + return setmetatable({ + line, + }, mt_string_entry) + end + end +end + +do + local lookup_keys = { + ordinal = 1, + value = 1, + filename = 1, + cwd = 2, + } + + function make_entry.gen_from_file(opts) + opts = opts or {} + + local cwd = utils.path_expand(opts.cwd or vim.loop.cwd()) + + local disable_devicons = opts.disable_devicons + + local mt_file_entry = {} + + mt_file_entry.cwd = cwd + mt_file_entry.display = function(entry) + local hl_group, icon + local display, path_style = utils.transform_path(opts, entry.value) + + display, hl_group, icon = utils.transform_devicons(entry.value, display, disable_devicons) + + if hl_group then + local style = { { { 0, #icon + 1 }, hl_group } } + style = utils.merge_styles(style, path_style, #icon + 1) + return display, style + else + return display, path_style + end + end + + mt_file_entry.__index = function(t, k) + local override = handle_entry_index(opts, t, k) + if override then + return override + end + + local raw = rawget(mt_file_entry, k) + if raw then + return raw + end + + if k == "path" then + local retpath = Path:new({ t.cwd, t.value }):absolute() + if not vim.loop.fs_access(retpath, "R", nil) then + retpath = t.value + end + return retpath + end + + return rawget(t, rawget(lookup_keys, k)) + end + + if opts.file_entry_encoding then + return function(line) + line = vim.iconv(line, opts.file_entry_encoding, "utf8") + return setmetatable({ line }, mt_file_entry) + end + else + return function(line) + return setmetatable({ line }, mt_file_entry) + end + end + end +end + +do + local lookup_keys = { + value = 1, + ordinal = 1, + } + + -- Gets called only once to parse everything out for the vimgrep, after that looks up directly. + local parse_with_col = function(t) + local _, _, filename, lnum, col, text = string.find(t.value, [[(..-):(%d+):(%d+):(.*)]]) + + local ok + ok, lnum = pcall(tonumber, lnum) + if not ok then + lnum = nil + end + + ok, col = pcall(tonumber, col) + if not ok then + col = nil + end + + t.filename = filename + t.lnum = lnum + t.col = col + t.text = text + + return { filename, lnum, col, text } + end + + local parse_without_col = function(t) + local _, _, filename, lnum, text = string.find(t.value, [[(..-):(%d+):(.*)]]) + + local ok + ok, lnum = pcall(tonumber, lnum) + if not ok then + lnum = nil + end + + t.filename = filename + t.lnum = lnum + t.col = nil + t.text = text + + return { filename, lnum, nil, text } + end + + local parse_only_filename = function(t) + t.filename = t.value + t.lnum = nil + t.col = nil + t.text = "" + + return { t.filename, nil, nil, "" } + end + + function make_entry.gen_from_vimgrep(opts) + opts = opts or {} + + local mt_vimgrep_entry + local parse = parse_with_col + if opts.__matches == true then + parse = parse_only_filename + elseif opts.__inverted == true then + parse = parse_without_col + end + + local disable_devicons = opts.disable_devicons + local disable_coordinates = opts.disable_coordinates + local only_sort_text = opts.only_sort_text + + local execute_keys = { + path = function(t) + if Path:new(t.filename):is_absolute() then + return t.filename, false + else + return Path:new({ t.cwd, t.filename }):absolute(), false + end + end, + + filename = function(t) + return parse(t)[1], true + end, + + lnum = function(t) + return parse(t)[2], true + end, + + col = function(t) + return parse(t)[3], true + end, + + text = function(t) + return parse(t)[4], true + end, + } + + -- For text search only, the ordinal value is actually the text. + if only_sort_text then + execute_keys.ordinal = function(t) + return t.text + end + end + + local display_string = "%s%s%s" + + mt_vimgrep_entry = { + cwd = utils.path_expand(opts.cwd or vim.loop.cwd()), + + display = function(entry) + local display_filename, path_style = utils.transform_path(opts, entry.filename) + + local coordinates = ":" + if not disable_coordinates then + if entry.lnum then + if entry.col then + coordinates = string.format(":%s:%s:", entry.lnum, entry.col) + else + coordinates = string.format(":%s:", entry.lnum) + end + end + end + + local display, hl_group, icon = utils.transform_devicons( + entry.filename, + string.format(display_string, display_filename, coordinates, entry.text), + disable_devicons + ) + + if hl_group then + local style = { { { 0, #icon }, hl_group } } + style = utils.merge_styles(style, path_style, #icon + 1) + return display, style + else + return display, path_style + end + end, + + __index = function(t, k) + local override = handle_entry_index(opts, t, k) + if override then + return override + end + + local raw = rawget(mt_vimgrep_entry, k) + if raw then + return raw + end + + local executor = rawget(execute_keys, k) + if executor then + local val, save = executor(t) + if save then + rawset(t, k, val) + end + return val + end + + return rawget(t, rawget(lookup_keys, k)) + end, + } + + return function(line) + return setmetatable({ line }, mt_vimgrep_entry) + end + end +end + +function make_entry.gen_from_git_stash(opts) + local displayer = entry_display.create { + separator = " ", + items = { + { width = 10 }, + opts.show_branch and { width = 15 } or "", + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.value, "TelescopeResultsLineNr" }, + opts.show_branch and { entry.branch_name, "TelescopeResultsIdentifier" } or "", + entry.commit_info, + } + end + + return function(entry) + if entry == "" then + return nil + end + + local splitted = utils.max_split(entry, ": ", 2) + local stash_idx = splitted[1] + local _, branch_name = string.match(splitted[2], "^([WIP on|On]+) (.+)") + local commit_info = splitted[3] + + return make_entry.set_default_entry_mt({ + value = stash_idx, + ordinal = commit_info, + branch_name = branch_name, + commit_info = commit_info, + display = make_display, + }, opts) + end +end + +function make_entry.gen_from_git_commits(opts) + opts = opts or {} + + local displayer = entry_display.create { + separator = " ", + items = { + { width = 8 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.value, "TelescopeResultsIdentifier" }, + entry.msg, + } + end + + return function(entry) + if entry == "" then + return nil + end + + local sha, msg = string.match(entry, "([^ ]+) (.+)") + + if not msg then + sha = entry + msg = "" + end + + return make_entry.set_default_entry_mt({ + value = sha, + ordinal = sha .. " " .. msg, + msg = msg, + display = make_display, + current_file = opts.current_file, + }, opts) + end +end + +function make_entry.gen_from_quickfix(opts) + opts = opts or {} + local show_line = vim.F.if_nil(opts.show_line, true) + + local hidden = utils.is_path_hidden(opts) + + local make_display = function(entry) + local display_filename, path_style = utils.transform_path(opts, entry.filename) + local display_string = string.format("%s:%d:%d", display_filename, entry.lnum, entry.col) + if hidden then + display_string = string.format("%4d:%2d", entry.lnum, entry.col) + end + + if show_line then + local text = entry.text + if opts.trim_text then + text = vim.trim(text) + end + text = text:gsub(".* | ", "") + display_string = display_string .. ":" .. text + end + + return display_string, path_style + end + + local get_filename = get_filename_fn() + return function(entry) + local filename = vim.F.if_nil(entry.filename, get_filename(entry.bufnr)) + + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = (not hidden and filename or "") .. " " .. entry.text, + display = make_display, + + bufnr = entry.bufnr, + filename = filename, + lnum = entry.lnum, + col = entry.col, + text = entry.text, + start = entry.start, + finish = entry.finish, + }, opts) + end +end + +function make_entry.gen_from_lsp_symbols(opts) + opts = opts or {} + + local bufnr = opts.bufnr or vim.api.nvim_get_current_buf() + + -- Default we have two columns, symbol and type(unbound) + -- If path is not hidden then its, filepath, symbol and type(still unbound) + -- If show_line is also set, type is bound to len 8 + local display_items = { + { width = opts.symbol_width or 25 }, + { remaining = true }, + } + + local hidden = utils.is_path_hidden(opts) + if not hidden then + table.insert(display_items, 1, { width = vim.F.if_nil(opts.fname_width, 30) }) + end + + if opts.show_line then + -- bound type to len 8 or custom + table.insert(display_items, #display_items, { width = opts.symbol_type_width or 8 }) + end + + local displayer = entry_display.create { + separator = " ", + hl_chars = { ["["] = "TelescopeBorder", ["]"] = "TelescopeBorder" }, + items = display_items, + } + local type_highlight = vim.F.if_nil(opts.symbol_highlights or lsp_type_highlight) + + local make_display = function(entry) + local msg + + if opts.show_line then + msg = vim.trim(vim.F.if_nil(vim.api.nvim_buf_get_lines(bufnr, entry.lnum - 1, entry.lnum, false)[1], "")) + end + + if hidden then + return displayer { + entry.symbol_name, + { entry.symbol_type:lower(), type_highlight[entry.symbol_type] }, + msg, + } + else + return displayer { + utils.transform_path(opts, entry.filename), + entry.symbol_name, + { entry.symbol_type:lower(), type_highlight[entry.symbol_type] }, + msg, + } + end + end + + local get_filename = get_filename_fn() + return function(entry) + local filename = vim.F.if_nil(entry.filename, get_filename(entry.bufnr)) + local symbol_msg = entry.text + local symbol_type, symbol_name = symbol_msg:match "%[(.+)%]%s+(.*)" + local ordinal = "" + if not hidden and filename then + ordinal = filename .. " " + end + ordinal = ordinal .. symbol_name .. " " .. (symbol_type or "unknown") + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = ordinal, + display = make_display, + + filename = filename, + lnum = entry.lnum, + col = entry.col, + symbol_name = symbol_name, + symbol_type = symbol_type, + start = entry.start, + finish = entry.finish, + }, opts) + end +end + +function make_entry.gen_from_buffer(opts) + opts = opts or {} + + local disable_devicons = opts.disable_devicons + + local icon_width = 0 + if not disable_devicons then + local icon, _ = utils.get_devicons("fname", disable_devicons) + icon_width = strings.strdisplaywidth(icon) + end + + local displayer = entry_display.create { + separator = " ", + items = { + { width = opts.bufnr_width }, + { width = 4 }, + { width = icon_width }, + { remaining = true }, + }, + } + + local cwd = utils.path_expand(opts.cwd or vim.loop.cwd()) + + local make_display = function(entry) + -- bufnr_width + modes + icon + 3 spaces + : + lnum + opts.__prefix = opts.bufnr_width + 4 + icon_width + 3 + 1 + #tostring(entry.lnum) + local display_bufname, path_style = utils.transform_path(opts, entry.filename) + local icon, hl_group = utils.get_devicons(entry.filename, disable_devicons) + + return displayer { + { entry.bufnr, "TelescopeResultsNumber" }, + { entry.indicator, "TelescopeResultsComment" }, + { icon, hl_group }, + { + display_bufname .. ":" .. entry.lnum, + function() + return path_style + end, + }, + } + end + + return function(entry) + local filename = entry.info.name ~= "" and entry.info.name or nil + local bufname = filename and Path:new(filename):normalize(cwd) or "[No Name]" + + local hidden = entry.info.hidden == 1 and "h" or "a" + local readonly = vim.api.nvim_buf_get_option(entry.bufnr, "readonly") and "=" or " " + local changed = entry.info.changed == 1 and "+" or " " + local indicator = entry.flag .. hidden .. readonly .. changed + local lnum = 1 + + -- account for potentially stale lnum as getbufinfo might not be updated or from resuming buffers picker + if entry.info.lnum ~= 0 then + -- but make sure the buffer is loaded, otherwise line_count is 0 + if vim.api.nvim_buf_is_loaded(entry.bufnr) then + local line_count = vim.api.nvim_buf_line_count(entry.bufnr) + lnum = math.max(math.min(entry.info.lnum, line_count), 1) + else + lnum = entry.info.lnum + end + end + + return make_entry.set_default_entry_mt({ + value = bufname, + ordinal = entry.bufnr .. " : " .. bufname, + display = make_display, + bufnr = entry.bufnr, + path = filename, + filename = bufname, + lnum = lnum, + indicator = indicator, + }, opts) + end +end + +function make_entry.gen_from_treesitter(opts) + opts = opts or {} + + local bufnr = opts.bufnr or vim.api.nvim_get_current_buf() + + local display_items = { + { width = opts.symbol_width or 25 }, + { width = 10 }, + { remaining = true }, + } + + if opts.show_line then + table.insert(display_items, 2, { width = 6 }) + end + + local displayer = entry_display.create { + separator = " ", + items = display_items, + } + + local type_highlight = opts.symbol_highlights or treesitter_type_highlight + + local make_display = function(entry) + local msg = vim.api.nvim_buf_get_lines(bufnr, entry.lnum, entry.lnum, false)[1] or "" + msg = vim.trim(msg) + + local display_columns = { + entry.text, + { entry.kind, type_highlight[entry.kind], type_highlight[entry.kind] }, + msg, + } + if opts.show_line then + table.insert(display_columns, 2, { entry.lnum .. ":" .. entry.col, "TelescopeResultsLineNr" }) + end + + return displayer(display_columns) + end + + local get_filename = get_filename_fn() + return function(entry) + local start_row, start_col, end_row, _ = vim.treesitter.get_node_range(entry.node) + local node_text = vim.treesitter.get_node_text(entry.node, bufnr) + return make_entry.set_default_entry_mt({ + value = entry.node, + kind = entry.kind, + ordinal = node_text .. " " .. (entry.kind or "unknown"), + display = make_display, + + node_text = node_text, + + filename = get_filename(bufnr), + -- need to add one since the previewer substacts one + lnum = start_row + 1, + col = start_col, + text = node_text, + start = start_row, + finish = end_row, + }, opts) + end +end + +function make_entry.gen_from_packages(opts) + opts = opts or {} + + local make_display = function(module_name) + local p_path = package.searchpath(module_name, package.path) or "" + local display = string.format("%-" .. opts.column_len .. "s : %s", module_name, vim.fn.fnamemodify(p_path, ":~:.")) + + return display + end + + return function(module_name) + return make_entry.set_default_entry_mt({ + valid = module_name ~= "", + value = module_name, + ordinal = module_name, + display = make_display(module_name), + }, opts) + end +end + +function make_entry.gen_from_apropos(opts) + local sections = {} + if #opts.sections == 1 and opts.sections[1] == "ALL" then + setmetatable(sections, { + __index = function() + return true + end, + }) + else + for _, section in ipairs(opts.sections) do + sections[section] = true + end + end + + local displayer = entry_display.create { + separator = " ", + items = { + { width = 30 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.keyword, "TelescopeResultsFunction" }, + entry.description, + } + end + + return function(line) + local keyword, cmd, section, desc = line:match "^((.-)%s*%(([^)]+)%).-)%s+%-%s+(.*)$" + -- apropos might return alternatives for the cmd which are split on `,` and breaks everything else + -- for example on void linux it will return `alacritty, Alacritty` which will later result in + -- `man 1 alacritty, Alacritty`. So we just take the first one. + -- doing this outside of regex because of obvious reasons + cmd = vim.split(cmd, ",")[1] + return keyword + and sections[section] + and make_entry.set_default_entry_mt({ + value = cmd, + description = desc, + ordinal = cmd, + display = make_display, + section = section, + keyword = keyword, + }, opts) + or nil + end +end + +function make_entry.gen_from_marks(opts) + return function(item) + return make_entry.set_default_entry_mt({ + value = item.line, + ordinal = item.line, + display = item.line, + lnum = item.lnum, + col = item.col, + start = item.lnum, + filename = item.filename, + }, opts) + end +end + +function make_entry.gen_from_registers(opts) + local displayer = entry_display.create { + separator = " ", + hl_chars = { ["["] = "TelescopeBorder", ["]"] = "TelescopeBorder" }, + items = { + { width = 3 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + local content = entry.content + return displayer { + { "[" .. entry.value .. "]", "TelescopeResultsNumber" }, + type(content) == "string" and content:gsub("\n", "\\n") or content, + } + end + + return function(entry) + local contents = vim.fn.getreg(entry, 1) + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = string.format("%s %s", entry, contents), + content = contents, + display = make_display, + }, opts) + end +end + +function make_entry.gen_from_keymaps(opts) + local function get_desc(entry) + if entry.callback and not entry.desc then + return require("telescope.actions.utils")._get_anon_function_name(debug.getinfo(entry.callback)) + end + return vim.F.if_nil(entry.desc, entry.rhs):gsub("\n", "\\n") + end + + local function get_lhs(entry) + return utils.display_termcodes(entry.lhs) + end + + local function get_attr(entry) + local ret = "" + if entry.value.noremap ~= 0 then + ret = ret .. "*" + end + if entry.value.buffer ~= 0 then + ret = ret .. "@" + end + return ret + end + + local displayer = require("telescope.pickers.entry_display").create { + separator = "▏", + items = { + { width = 3 }, + { width = opts.width_lhs }, + { width = 2 }, + { remaining = true }, + }, + } + local make_display = function(entry) + return displayer { + entry.mode, + get_lhs(entry), + get_attr(entry), + get_desc(entry), + } + end + + return function(entry) + local desc = get_desc(entry) + local lhs = get_lhs(entry) + return make_entry.set_default_entry_mt({ + mode = entry.mode, + lhs = lhs, + desc = desc, + valid = entry ~= "", + value = entry, + ordinal = entry.mode .. " " .. lhs .. " " .. desc, + display = make_display, + }, opts) + end +end + +function make_entry.gen_from_highlights(opts) + local make_display = function(entry) + local display = entry.value + return display, { { { 0, #display }, display } } + end + + return function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + display = make_display, + ordinal = entry, + }, opts) + end +end + +function make_entry.gen_from_picker(opts) + local displayer = entry_display.create { + separator = " │ ", + items = { + { width = 0.5 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + entry.value.prompt_title, + entry.value.default_text, + } + end + + return function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + text = entry.prompt_title, + ordinal = string.format("%s %s", entry.prompt_title, vim.F.if_nil(entry.default_text, "")), + display = make_display, + }, opts) + end +end + +function make_entry.gen_from_buffer_lines(opts) + local displayer = entry_display.create { + separator = " │ ", + items = { + { width = 5 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.lnum, opts.lnum_highlight_group or "TelescopeResultsSpecialComment" }, + { + entry.text, + function() + if not opts.line_highlights then + return {} + end + + local line_hl = opts.line_highlights[entry.lnum] or {} + -- TODO: We could probably squash these together if the are the same... + -- But I don't think that it's worth it at the moment. + local result = {} + + for col, hl in pairs(line_hl) do + table.insert(result, { { col, col + 1 }, hl }) + end + + return result + end, + }, + } + end + + return function(entry) + if opts.skip_empty_lines and string.match(entry.text, "^$") then + return + end + + return make_entry.set_default_entry_mt({ + ordinal = entry.text, + display = make_display, + filename = entry.filename, + lnum = entry.lnum, + text = entry.text, + }, opts) + end +end + +function make_entry.gen_from_vimoptions(opts) + local displayer = entry_display.create { + separator = "", + hl_chars = { ["["] = "TelescopeBorder", ["]"] = "TelescopeBorder" }, + items = { + { width = 25 }, + { width = 12 }, + { width = 11 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.value.name, "Keyword" }, + { "[" .. entry.value.type .. "]", "Type" }, + { "[" .. entry.value.scope .. "]", "Identifier" }, + utils.display_termcodes(tostring(entry.value.value)), + } + end + + return function(o) + local entry = { + display = make_display, + value = { + name = o.name, + value = o.default, + type = o.type, + scope = o.scope, + }, + ordinal = string.format("%s %s %s", o.name, o.type, o.scope), + } + + local ok, value = pcall(vim.api.nvim_get_option, o.name) + if ok then + entry.value.value = value + entry.ordinal = entry.ordinal .. " " .. utils.display_termcodes(tostring(value)) + else + entry.ordinal = entry.ordinal .. " " .. utils.display_termcodes(tostring(o.default)) + end + + return make_entry.set_default_entry_mt(entry, opts) + end +end + +function make_entry.gen_from_ctags(opts) + opts = opts or {} + + local cwd = utils.path_expand(opts.cwd or vim.loop.cwd()) + local current_file = Path:new(vim.api.nvim_buf_get_name(opts.bufnr)):normalize(cwd) + + local display_items = { + { remaining = true }, + } + + local idx = 1 + local hidden = utils.is_path_hidden(opts) + if not hidden then + table.insert(display_items, idx, { width = vim.F.if_nil(opts.fname_width, 30) }) + idx = idx + 1 + end + + if opts.show_line then + table.insert(display_items, idx, { width = 30 }) + end + + local displayer = entry_display.create { + separator = " │ ", + items = display_items, + } + + local make_display = function(entry) + local filename = utils.transform_path(opts, entry.filename) + + local scode + if opts.show_line then + scode = entry.scode + end + + if hidden then + return displayer { + entry.tag, + scode, + } + else + return displayer { + filename, + entry.tag, + scode, + } + end + end + + local mt = {} + mt.__index = function(t, k) + local override = handle_entry_index(opts, t, k) + if override then + return override + end + + if k == "path" then + local retpath = Path:new({ t.filename }):absolute() + if not vim.loop.fs_access(retpath, "R", nil) then + retpath = t.filename + end + return retpath + end + end + + local current_file_cache = {} + return function(line) + if line == "" or line:sub(1, 1) == "!" then + return nil + end + + local tag, file, scode, lnum + -- ctags gives us: 'tags\tfile\tsource' + tag, file, scode = string.match(line, '([^\t]+)\t([^\t]+)\t/^?\t?(.*)/;"\t+.*') + if not tag then + -- hasktags gives us: 'tags\tfile\tlnum' + tag, file, lnum = string.match(line, "([^\t]+)\t([^\t]+)\t(%d+).*") + end + + if Path.path.sep == "\\" then + file = string.gsub(file, "/", "\\") + end + + if opts.only_current_file then + if current_file_cache[file] == nil then + current_file_cache[file] = Path:new(file):normalize(cwd) == current_file + end + + if current_file_cache[file] == false then + return nil + end + end + + local tag_entry = {} + if opts.only_sort_tags then + tag_entry.ordinal = tag + else + tag_entry.ordinal = file .. ": " .. tag + end + + tag_entry.display = make_display + tag_entry.scode = scode + tag_entry.tag = tag + tag_entry.filename = file + tag_entry.col = 1 + tag_entry.lnum = lnum and tonumber(lnum) or 1 + + return setmetatable(tag_entry, mt) + end +end + +function make_entry.gen_from_diagnostics(opts) + opts = opts or {} + + local type_diagnostic = vim.diagnostic.severity + local signs = (function() + if opts.no_sign then + return + end + local signs = {} + for _, severity in ipairs(type_diagnostic) do + local status, sign = pcall(function() + -- only the first char is upper all others are lowercalse + return vim.trim(vim.fn.sign_getdefined("DiagnosticSign" .. severity:lower():gsub("^%l", string.upper))[1].text) + end) + if not status then + sign = severity:sub(1, 1) + end + signs[severity] = sign + end + return signs + end)() + + local sign_width + if opts.disable_coordinates then + sign_width = signs ~= nil and 2 or 0 + else + sign_width = signs ~= nil and 10 or 8 + end + + local display_items = { + { width = sign_width }, + { remaining = true }, + } + local line_width = vim.F.if_nil(opts.line_width, 0.5) + local line_width_opts = { width = line_width } + if type(line_width) == "string" and line_width == "full" then + line_width_opts = {} + end + local hidden = utils.is_path_hidden(opts) + if not hidden then + table.insert(display_items, 2, line_width_opts) + end + local displayer = entry_display.create { + separator = "▏", + items = display_items, + } + + local make_display = function(entry) + local filename = utils.transform_path(opts, entry.filename) + + -- add styling of entries + local pos = string.format("%4d:%2d", entry.lnum, entry.col) + local line_info_text = signs and signs[entry.type] .. " " or "" + local line_info = { + opts.disable_coordinates and line_info_text or line_info_text .. pos, + "DiagnosticSign" .. entry.type, + } + + return displayer { + line_info, + entry.text, + filename, + } + end + + local errlist_type_map = { + [type_diagnostic.ERROR] = "E", + [type_diagnostic.WARN] = "W", + [type_diagnostic.INFO] = "I", + [type_diagnostic.HINT] = "N", + } + + return function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = ("%s %s"):format(not hidden and entry.filename or "", entry.text), + display = make_display, + filename = entry.filename, + type = entry.type, + lnum = entry.lnum, + col = entry.col, + text = entry.text, + qf_type = errlist_type_map[type_diagnostic[entry.type]], + }, opts) + end +end + +function make_entry.gen_from_autocommands(opts) + local displayer = entry_display.create { + separator = "▏", + items = { + { width = 14 }, + { width = 18 }, + { width = 16 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.value.event, "vimAutoEvent" }, + { entry.value.group_name, "vimAugroup" }, + { entry.value.pattern, "vimAutoCmdSfxList" }, + entry.value.command, + } + end + + return function(entry) + local group_name = vim.F.if_nil(entry.group_name, "") + local command = entry.command + if entry.desc and (entry.callback or vim.startswith(command, " +--- { +--- mode = { ..keys } +--- } +--- +--- +--- where {mode} is the one character letter for a mode ('i' for insert, 'n' for normal). +--- +--- For example: +--- +--- mappings = { +--- i = { +--- [""] = require('telescope.actions').close, +--- }, +--- } +--- +--- +--- To disable a keymap, put `[map] = false`
+--- For example: +--- +--- { +--- ..., +--- [""] = false, +--- ..., +--- } +--- +--- +--- To override behavior of a key, simply set the value +--- to be a function (either by requiring an action or by writing +--- your own function) +--- +--- { +--- ..., +--- [""] = require('telescope.actions').select_default, +--- ..., +--- } +--- +--- +--- If the function you want is part of `telescope.actions`, then you can +--- simply supply the function name as a string. +--- For example, the previous option is equivalent to: +--- +--- { +--- ..., +--- [""] = "select_default", +--- ..., +--- } +--- +--- +--- You can also add other mappings using tables with `type = "command"`. +--- For example: +--- +--- { +--- ..., +--- ["jj"] = { "", type = "command" }, +--- ["kk"] = { "echo \"Hello, World!\"", type = "command" },) +--- ..., +--- } +--- +--- +--- You can also add additional options for mappings of any type ("action" and "command"). +--- For example: +--- +--- { +--- ..., +--- [""] = { +--- actions.move_selection_next, type = "action", +--- opts = { nowait = true, silent = true } +--- }, +--- ..., +--- } +--- +--- +--- There are three main places you can configure |telescope.mappings|. These are +--- ordered from the lowest priority to the highest priority. +--- +--- 1. |telescope.defaults.mappings| +--- 2. In the |telescope.setup()| table, inside a picker with a given name, use the `mappings` key +--- +--- require("telescope").setup { +--- pickers = { +--- find_files = { +--- mappings = { +--- n = { +--- ["kj"] = "close", +--- }, +--- }, +--- }, +--- }, +--- } +--- +--- 3. `attach_mappings` function for a particular picker. +--- +--- require("telescope.builtin").find_files { +--- attach_mappings = function(_, map) +--- map("i", "asdf", function(_prompt_bufnr) +--- print "You typed asdf" +--- end) +--- +--- map({"i", "n"}, "", function(_prompt_bufnr) +--- print "You typed " +--- end, { desc = "desc for which key"}) +--- +--- -- needs to return true if you want to map default_mappings and +--- -- false if not +--- return true +--- end, +--- } +--- +---@brief ]] + +local a = vim.api + +local actions = require "telescope.actions" +local config = require "telescope.config" + +local mappings = {} + +mappings.default_mappings = config.values.default_mappings + or { + i = { + [""] = { + actions.mouse_click, + type = "action", + opts = { expr = true }, + }, + ["<2-LeftMouse>"] = { + actions.double_mouse_click, + type = "action", + opts = { expr = true }, + }, + + [""] = actions.move_selection_next, + [""] = actions.move_selection_previous, + + [""] = actions.close, + + [""] = actions.move_selection_next, + [""] = actions.move_selection_previous, + + [""] = actions.select_default, + [""] = actions.select_horizontal, + [""] = actions.select_vertical, + [""] = actions.select_tab, + + [""] = actions.preview_scrolling_up, + [""] = actions.preview_scrolling_down, + [""] = actions.preview_scrolling_left, + [""] = actions.preview_scrolling_right, + + [""] = actions.results_scrolling_up, + [""] = actions.results_scrolling_down, + [""] = actions.results_scrolling_left, + [""] = actions.results_scrolling_right, + + [""] = actions.toggle_selection + actions.move_selection_worse, + [""] = actions.toggle_selection + actions.move_selection_better, + [""] = actions.send_to_qflist + actions.open_qflist, + [""] = actions.send_selected_to_qflist + actions.open_qflist, + [""] = actions.complete_tag, + [""] = actions.which_key, + [""] = actions.which_key, -- keys from pressing + [""] = { "", type = "command" }, + [""] = actions.insert_original_cword, + + -- disable c-j because we dont want to allow new lines #2123 + [""] = actions.nop, + }, + n = { + [""] = { + actions.mouse_click, + type = "action", + opts = { expr = true }, + }, + ["<2-LeftMouse>"] = { + actions.double_mouse_click, + type = "action", + opts = { expr = true }, + }, + + [""] = actions.close, + [""] = actions.select_default, + [""] = actions.select_horizontal, + [""] = actions.select_vertical, + [""] = actions.select_tab, + + [""] = actions.toggle_selection + actions.move_selection_worse, + [""] = actions.toggle_selection + actions.move_selection_better, + [""] = actions.send_to_qflist + actions.open_qflist, + [""] = actions.send_selected_to_qflist + actions.open_qflist, + + -- TODO: This would be weird if we switch the ordering. + ["j"] = actions.move_selection_next, + ["k"] = actions.move_selection_previous, + ["H"] = actions.move_to_top, + ["M"] = actions.move_to_middle, + ["L"] = actions.move_to_bottom, + + [""] = actions.move_selection_next, + [""] = actions.move_selection_previous, + ["gg"] = actions.move_to_top, + ["G"] = actions.move_to_bottom, + + [""] = actions.preview_scrolling_up, + [""] = actions.preview_scrolling_down, + [""] = actions.preview_scrolling_left, + [""] = actions.preview_scrolling_right, + + [""] = actions.results_scrolling_up, + [""] = actions.results_scrolling_down, + [""] = actions.results_scrolling_left, + [""] = actions.results_scrolling_right, + + ["?"] = actions.which_key, + }, + } + +-- normal names are prefixed with telescope| +-- encoded objects are prefixed with telescopej| +---@param key_func table|fun() +---@param opts table +---@return string? +local get_desc_for_keyfunc = function(key_func, opts) + if opts and opts.desc then + return "telescope|" .. opts.desc + end + + if type(key_func) == "table" then + local name = "" + for _, action in ipairs(key_func) do + if type(action) == "string" then + name = name == "" and action or name .. " + " .. action + end + end + return "telescope|" .. name + elseif type(key_func) == "function" then + local info = debug.getinfo(key_func) + return "telescopej|" .. vim.json.encode { source = info.source, linedefined = info.linedefined } + end +end + +local telescope_map = function(prompt_bufnr, mode, key_bind, key_func, opts) + if not key_func then + return + end + + opts = opts or {} + if opts.noremap == nil then + opts.noremap = true + end + if opts.silent == nil then + opts.silent = true + end + + if type(key_func) == "string" then + key_func = actions[key_func] + elseif type(key_func) == "table" then + if key_func.type == "command" then + vim.keymap.set( + mode, + key_bind, + key_func[1], + vim.tbl_extend("force", opts or { + silent = true, + }, { buffer = prompt_bufnr }) + ) + return + elseif key_func.type == "action_key" then + key_func = actions[key_func[1]] + elseif key_func.type == "action" then + key_func = key_func[1] + end + end + + vim.keymap.set(mode, key_bind, function() + local ret = key_func(prompt_bufnr) + vim.api.nvim_exec_autocmds("User", { pattern = "TelescopeKeymap" }) + return ret + end, vim.tbl_extend("force", opts, { buffer = prompt_bufnr, desc = get_desc_for_keyfunc(key_func, opts) })) +end + +local extract_keymap_opts = function(key_func) + if type(key_func) == "table" and key_func.opts ~= nil then + -- we can't clear this because key_func could be a table from the config. + -- If we clear it the table ref would lose opts after the first bind + -- We need to copy it so noremap and silent won't be part of the table ref after the first bind + return vim.deepcopy(key_func.opts) + end + return {} +end + +mappings.apply_keymap = function(prompt_bufnr, attach_mappings, buffer_keymap) + local applied_mappings = { n = {}, i = {} } + + local map = function(modes, key_bind, key_func, opts) + if type(modes) == "string" then + modes = { modes } + end + + for _, mode in pairs(modes) do + mode = string.lower(mode) + local key_bind_internal = a.nvim_replace_termcodes(key_bind, true, true, true) + applied_mappings[mode][key_bind_internal] = true + + telescope_map(prompt_bufnr, mode, key_bind, key_func, opts) + end + end + + if attach_mappings then + local attach_results = attach_mappings(prompt_bufnr, map) + + if attach_results == nil then + error( + "Attach mappings must always return a value. `true` means use default mappings, " + .. "`false` means only use attached mappings" + ) + end + + if not attach_results then + return + end + end + + for mode, mode_map in pairs(buffer_keymap or {}) do + mode = string.lower(mode) + + for key_bind, key_func in pairs(mode_map) do + local key_bind_internal = a.nvim_replace_termcodes(key_bind, true, true, true) + if not applied_mappings[mode][key_bind_internal] then + applied_mappings[mode][key_bind_internal] = true + telescope_map(prompt_bufnr, mode, key_bind, key_func, extract_keymap_opts(key_func)) + end + end + end + + -- TODO: Probably should not overwrite any keymaps + for mode, mode_map in pairs(mappings.default_mappings) do + mode = string.lower(mode) + + for key_bind, key_func in pairs(mode_map) do + local key_bind_internal = a.nvim_replace_termcodes(key_bind, true, true, true) + if not applied_mappings[mode][key_bind_internal] then + applied_mappings[mode][key_bind_internal] = true + telescope_map(prompt_bufnr, mode, key_bind, key_func, extract_keymap_opts(key_func)) + end + end + end +end + +return mappings diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/operators.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/operators.lua new file mode 100644 index 00000000..e7ee8b5f --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/operators.lua @@ -0,0 +1,23 @@ +local operators = {} + +local last_operator = { callback = function(_) end, opts = {} } + +--- Execute the last saved operator callback and options +operators.operator_callback = function() + last_operator.callback(last_operator.opts) +end + +--- Enters operator-pending mode, then executes callback. +--- See `:h map-operator` +--- +---@param callback function: the function to call after exiting operator-pending +---@param opts table: options to pass to the callback +operators.run_operator = function(callback, opts) + last_operator = { callback = callback, opts = opts } + vim.o.operatorfunc = "v:lua.require'telescope.operators'.operator_callback" + -- feed g@ to enter operator-pending mode + -- 'i' required for which-key compatibility, etc. + vim.api.nvim_feedkeys("g@", "mi", false) +end + +return operators diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers.lua new file mode 100644 index 00000000..88e89cc9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers.lua @@ -0,0 +1,1728 @@ +require "telescope" + +local a = vim.api + +local async = require "plenary.async" +local await_schedule = async.util.scheduler +local channel = require("plenary.async.control").channel +local popup = require "plenary.popup" + +local actions = require "telescope.actions" +local config = require "telescope.config" +local debounce = require "telescope.debounce" +local deprecated = require "telescope.deprecated" +local log = require "telescope.log" +local mappings = require "telescope.mappings" +local state = require "telescope.state" +local utils = require "telescope.utils" + +local entry_display = require "telescope.pickers.entry_display" +local p_highlighter = require "telescope.pickers.highlights" +local p_scroller = require "telescope.pickers.scroller" +local p_window = require "telescope.pickers.window" +local Layout = require "telescope.pickers.layout" + +local EntryManager = require "telescope.entry_manager" +local MultiSelect = require "telescope.pickers.multi" + +local truncate = require("plenary.strings").truncate +local strdisplaywidth = require("plenary.strings").strdisplaywidth + +local ns_telescope_matching = a.nvim_create_namespace "telescope_matching" +local ns_telescope_prompt = a.nvim_create_namespace "telescope_prompt" +local ns_telescope_prompt_prefix = a.nvim_create_namespace "telescope_prompt_prefix" + +---@class telescope_popup_options +---@field border table<1|2|3|4, integer> +---@field borderchars table<1|2|3|4|5|6|7|8, string> +---@field borderhighlight string +---@field col integer +---@field enter boolean +---@field height integer +---@field highlight string +---@field line integer +---@field minheight integer +---@field title integer +---@field titlehighlight integer +---@field width integer + +-- Create three windows: +-- 1. Prompt window +-- 2. Options window +-- 3. Preview window +-- +---@param picker Picker +local function default_create_layout(picker) + local function make_border(border) + if not border then + return nil + end + border.winid = border.win_id + return border + end + + local layout = Layout { + picker = picker, + ---@param self TelescopeLayout + mount = function(self) + local line_count = vim.o.lines - vim.o.cmdheight + if vim.o.laststatus ~= 0 then + line_count = line_count - 1 + end + + local popup_opts = picker:get_window_options(vim.o.columns, line_count) + + -- `popup.nvim` massaging so people don't have to remember minheight shenanigans + popup_opts.results.focusable = true + popup_opts.results.minheight = popup_opts.results.height + popup_opts.results.highlight = "TelescopeResultsNormal" + popup_opts.results.borderhighlight = "TelescopeResultsBorder" + popup_opts.results.titlehighlight = "TelescopeResultsTitle" + popup_opts.prompt.minheight = popup_opts.prompt.height + popup_opts.prompt.highlight = "TelescopePromptNormal" + popup_opts.prompt.borderhighlight = "TelescopePromptBorder" + popup_opts.prompt.titlehighlight = "TelescopePromptTitle" + + if popup_opts.preview then + popup_opts.preview.focusable = true + popup_opts.preview.minheight = popup_opts.preview.height + popup_opts.preview.highlight = "TelescopePreviewNormal" + popup_opts.preview.borderhighlight = "TelescopePreviewBorder" + popup_opts.preview.titlehighlight = "TelescopePreviewTitle" + end + + local results_win, results_opts = picker:_create_window("", popup_opts.results) + local results_bufnr = a.nvim_win_get_buf(results_win) + + self.results = Layout.Window { + winid = results_win, + bufnr = results_bufnr, + border = make_border(results_opts.border), + } + + if popup_opts.preview then + local preview_win, preview_opts = picker:_create_window("", popup_opts.preview) + local preview_bufnr = a.nvim_win_get_buf(preview_win) + + self.preview = Layout.Window { + winid = preview_win, + bufnr = preview_bufnr, + border = make_border(preview_opts.border), + } + end + + local prompt_win, prompt_opts = picker:_create_window("", popup_opts.prompt) + local prompt_bufnr = a.nvim_win_get_buf(prompt_win) + + self.prompt = Layout.Window { + winid = prompt_win, + bufnr = prompt_bufnr, + border = make_border(prompt_opts.border), + } + end, + ---@param self TelescopeLayout + unmount = function(self) + utils.win_delete("results_win", self.results.winid, true, true) + if self.preview then + utils.win_delete("preview_win", self.preview.winid, true, true) + end + + utils.win_delete("prompt_border_win", self.prompt.border.winid, true, true) + utils.win_delete("results_border_win", self.results.border.winid, true, true) + if self.preview then + utils.win_delete("preview_border_win", self.preview.border.winid, true, true) + end + + -- we cant use win_delete. We first need to close and then delete the buffer + if vim.api.nvim_win_is_valid(self.prompt.winid) then + vim.api.nvim_win_close(self.prompt.winid, true) + end + vim.schedule(function() + utils.buf_delete(self.prompt.bufnr) + end) + end, + ---@param self TelescopeLayout + update = function(self) + local line_count = vim.o.lines - vim.o.cmdheight + if vim.o.laststatus ~= 0 then + line_count = line_count - 1 + end + + local popup_opts = picker:get_window_options(vim.o.columns, line_count) + -- `popup.nvim` massaging so people don't have to remember minheight shenanigans + popup_opts.results.minheight = popup_opts.results.height + popup_opts.prompt.minheight = popup_opts.prompt.height + if popup_opts.preview then + popup_opts.preview.minheight = popup_opts.preview.height + end + + local prompt_win = self.prompt.winid + local results_win = self.results.winid + local preview_win = self.preview and self.preview.winid + + local preview_opts + if popup_opts.preview then + if preview_win ~= nil then + -- Move all popups at the same time + popup.move(prompt_win, popup_opts.prompt) + popup.move(results_win, popup_opts.results) + popup.move(preview_win, popup_opts.preview) + else + popup_opts.preview.focusable = true + popup_opts.preview.highlight = "TelescopePreviewNormal" + popup_opts.preview.borderhighlight = "TelescopePreviewBorder" + popup_opts.preview.titlehighlight = "TelescopePreviewTitle" + local preview_bufnr = (self.preview and self.preview.bufnr ~= nil) + and vim.api.nvim_buf_is_valid(self.preview.bufnr) + and self.preview.bufnr + or "" + preview_win, preview_opts = picker:_create_window(preview_bufnr, popup_opts.preview) + if preview_bufnr == "" then + preview_bufnr = a.nvim_win_get_buf(preview_win) + end + self.preview = Layout.Window { + winid = preview_win, + bufnr = preview_bufnr, + border = make_border(preview_opts.border), + } + if picker.previewer and picker.previewer.state and picker.previewer.state.winid then + picker.previewer.state.winid = preview_win + end + + -- Move prompt and results after preview created + vim.defer_fn(function() + popup.move(prompt_win, popup_opts.prompt) + popup.move(results_win, popup_opts.results) + end, 0) + end + elseif preview_win ~= nil then + popup.move(prompt_win, popup_opts.prompt) + popup.move(results_win, popup_opts.results) + + -- Remove preview after the prompt and results are moved + vim.defer_fn(function() + utils.win_delete("preview_win", preview_win, true) + utils.win_delete("preview_win", self.preview.border.winid, true) + self.preview = nil + end, 0) + else + popup.move(prompt_win, popup_opts.prompt) + popup.move(results_win, popup_opts.results) + end + end, + } + + return layout +end + +local pickers = {} + +-- TODO: Add overscroll option for results buffer + +---@class Picker +--- Picker is the main UI that shows up to interact w/ your results. +-- Takes a filter & a previewer +local Picker = {} +Picker.__index = Picker + +--- Create new picker +function Picker:new(opts) + opts = opts or {} + + if opts.layout_strategy and opts.get_window_options then + error "layout_strategy and get_window_options are not compatible keys" + end + + if vim.fn.win_gettype() == "command" then + error "Can't open telescope from command-line window. See E11" + end + + deprecated.options(opts) + + -- We need to clear at the beginning not on close because after close we can still have select:post + -- etc ... + require("telescope.actions.mt").clear_all() + -- TODO(conni2461): This seems like the better solution but it won't clear actions that were never mapped + -- for _, v in ipairs(keymap_store[prompt_bufnr]) do + -- pcall(v.clear) + -- end + + local layout_strategy = vim.F.if_nil(opts.layout_strategy, config.values.layout_strategy) + local winblend = + vim.F.if_nil(opts.winblend, type(opts.window) == "table" and opts.window.winblend or config.values.winblend) + if type(winblend) == "function" then + winblend = winblend() + end + + local obj = setmetatable({ + prompt_title = vim.F.if_nil(opts.prompt_title, config.values.prompt_title), + results_title = vim.F.if_nil(opts.results_title, config.values.results_title), + -- either whats passed in by the user or whats defined by the previewer + preview_title = opts.preview_title, + + prompt_prefix = vim.F.if_nil(opts.prompt_prefix, config.values.prompt_prefix), + wrap_results = vim.F.if_nil(opts.wrap_results, config.values.wrap_results), + selection_caret = vim.F.if_nil(opts.selection_caret, config.values.selection_caret), + entry_prefix = vim.F.if_nil(opts.entry_prefix, config.values.entry_prefix), + multi_icon = vim.F.if_nil(opts.multi_icon, config.values.multi_icon), + + initial_mode = vim.F.if_nil(opts.initial_mode, config.values.initial_mode), + _original_mode = vim.api.nvim_get_mode().mode, + debounce = vim.F.if_nil(tonumber(opts.debounce), nil), + + _finder_attached = true, + default_text = opts.default_text, + get_status_text = vim.F.if_nil(opts.get_status_text, config.values.get_status_text), + _on_input_filter_cb = opts.on_input_filter_cb or function() end, + + finder = assert(opts.finder, "Finder is required."), + sorter = opts.sorter or require("telescope.sorters").empty(), + + all_previewers = opts.previewer, + current_previewer_index = opts.current_previewer_index or 1, + + default_selection_index = opts.default_selection_index, + + get_selection_window = vim.F.if_nil(opts.get_selection_window, config.values.get_selection_window), + + cwd = opts.cwd, + + _find_id = 0, + _completion_callbacks = type(opts._completion_callbacks) == "table" and opts._completion_callbacks or {}, + manager = (type(opts.manager) == "table" and getmetatable(opts.manager) == EntryManager) and opts.manager, + _multi = (type(opts._multi) == "table" and getmetatable(opts._multi) == getmetatable(MultiSelect:new())) + and opts._multi + or MultiSelect:new(), + + track = vim.F.if_nil(opts.track, false), + stats = {}, + + attach_mappings = opts.attach_mappings, + file_ignore_patterns = vim.F.if_nil(opts.file_ignore_patterns, config.values.file_ignore_patterns), + + scroll_strategy = vim.F.if_nil(opts.scroll_strategy, config.values.scroll_strategy), + sorting_strategy = vim.F.if_nil(opts.sorting_strategy, config.values.sorting_strategy), + tiebreak = vim.F.if_nil(opts.tiebreak, config.values.tiebreak), + selection_strategy = vim.F.if_nil(opts.selection_strategy, config.values.selection_strategy), + + push_cursor_on_edit = vim.F.if_nil(opts.push_cursor_on_edit, false), + push_tagstack_on_edit = vim.F.if_nil(opts.push_tagstack_on_edit, false), + + layout_strategy = layout_strategy, + layout_config = config.smarter_depth_2_extend(opts.layout_config or {}, config.values.layout_config or {}), + + __cycle_layout_list = vim.F.if_nil(opts.cycle_layout_list, config.values.cycle_layout_list), + + window = { + winblend = winblend, + border = vim.F.if_nil(opts.border, type(opts.window) == "table" and opts.window.border or config.values.border), + borderchars = vim.F.if_nil( + opts.borderchars, + type(opts.window) == "table" and opts.window.borderchars or config.values.borderchars + ), + }, + + cache_picker = config.resolve_table_opts(opts.cache_picker, vim.deepcopy(config.values.cache_picker)), + + __scrolling_limit = tonumber(vim.F.if_nil(opts.temp__scrolling_limit, 250)), + + __locations_input = vim.F.if_nil(opts.__locations_input, false), + }, self) + + obj.create_layout = opts.create_layout or config.values.create_layout or default_create_layout + obj.get_window_options = opts.get_window_options or p_window.get_window_options + + if obj.all_previewers ~= nil and obj.all_previewers ~= false then + if obj.all_previewers[1] == nil then + obj.all_previewers = { obj.all_previewers } + end + obj.previewer = obj.all_previewers[obj.current_previewer_index] + if + obj.preview_title == nil + or #obj.all_previewers > 1 + or opts.resumed_picker and opts.fix_preview_title ~= true + then + obj.preview_title = obj.previewer:title(nil, config.values.dynamic_preview_title) + else + obj.fix_preview_title = true + end + else + obj.previewer = false + end + + local __hide_previewer = opts.__hide_previewer + if __hide_previewer then + obj.hidden_previewer = obj.previewer + obj.previewer = nil + else + obj.hidden_previewer = nil + end + + -- TODO: It's annoying that this is create and everything else is "new" + obj.scroller = p_scroller.create(obj.scroll_strategy, obj.sorting_strategy) + + obj.highlighter = p_highlighter.new(obj) + + if opts.on_complete then + for _, on_complete_item in ipairs(opts.on_complete) do + obj:register_completion_callback(on_complete_item) + end + end + + return obj +end + +--- Take an index and get a row. +---@note: Rows are 0-indexed, and `index` is 1 indexed (table index) +---@param index number: the index in line_manager +---@return number: the row for the picker to display in +function Picker:get_row(index) + if self.sorting_strategy == "ascending" then + return index - 1 + else + return self.max_results - index + end +end + +--- Take a row and get an index +---@note: Rows are 0-indexed, and `index` is 1 indexed (table index) +---@param row number: The row being displayed +---@return number: The index in line_manager +function Picker:get_index(row) + if self.sorting_strategy == "ascending" then + return row + 1 + else + return self.max_results - row + end +end + +--- Get the row number of the "best" entry +---@return number: the number of the "reset" row +function Picker:get_reset_row() + if self.sorting_strategy == "ascending" then + return 0 + else + return self.max_results - 1 + end +end + +--- Check if the picker is no longer in use +---@return boolean|nil: `true` if picker is closed, `nil` otherwise +function Picker:is_done() + if not self.manager then + return true + end +end + +--- Clear rows that are after the final remaining entry +---@note: useful when number of remaining results is narrowed down +---@param results_bufnr number: the buffer number of the results buffer +function Picker:clear_extra_rows(results_bufnr) + if self:is_done() then + log.trace "Not clearing due to being already complete" + return + end + + if not vim.api.nvim_buf_is_valid(results_bufnr) then + log.debug("Invalid results_bufnr for clearing:", results_bufnr) + return + end + + local worst_line, ok, msg + if self.sorting_strategy == "ascending" then + local num_results = self.manager:num_results() + worst_line = self.max_results - num_results + + if worst_line <= 0 then + return + end + + ok, msg = pcall(vim.api.nvim_buf_set_lines, results_bufnr, num_results, -1, false, {}) + else + worst_line = self:get_row(self.manager:num_results()) + if worst_line <= 0 then + return + end + + local empty_lines = utils.repeated_table(worst_line, "") + ok, msg = pcall(vim.api.nvim_buf_set_lines, results_bufnr, 0, worst_line, false, empty_lines) + end + + if not ok then + log.debug("Failed to set lines:", msg) + end + + log.trace("Clearing:", worst_line) +end + +--- Highlight the entry corresponding to the given row +---@param results_bufnr number: the buffer number of the results buffer +---@param prompt table: table with information about the prompt buffer +---@param display string: the text corresponding to the given row +---@param row number: the number of the chosen row +function Picker:highlight_one_row(results_bufnr, prompt, display, row) + if not self.sorter.highlighter then + return + end + + local highlights = self.sorter:highlighter(prompt, display) + + if highlights then + for _, hl in ipairs(highlights) do + local highlight, start, finish + if type(hl) == "table" then + highlight = hl.highlight or "TelescopeMatching" + start = hl.start + finish = hl.finish or hl.start + elseif type(hl) == "number" then + highlight = "TelescopeMatching" + start = hl + finish = hl + else + error "Invalid higlighter fn" + end + + self:_increment "highlights" + + vim.api.nvim_buf_add_highlight(results_bufnr, ns_telescope_matching, highlight, row, start - 1, finish) + end + end + + local entry = self.manager:get_entry(self:get_index(row)) + self.highlighter:hi_multiselect(row, self:is_multi_selected(entry)) +end + +--- Check if the given row number can be selected +---@param row number: the number of the chosen row in the results buffer +---@return boolean +function Picker:can_select_row(row) + if self.sorting_strategy == "ascending" then + return row <= self.manager:num_results() and row < self.max_results + else + return row >= 0 and row <= self.max_results and row >= self.max_results - self.manager:num_results() + end +end + +--TODO: document what `find_id` is for +function Picker:_next_find_id() + local find_id = self._find_id + 1 + self._find_id = find_id + + return find_id +end + +--- A helper function for creating each of the windows in a picker +---@param bufnr number: the buffer number to be used in the window +---@param popup_opts table: options to pass to `popup.create` +function Picker:_create_window(bufnr, popup_opts) + local what = bufnr or "" + local win, opts = popup.create(what, popup_opts) + + a.nvim_win_set_option(win, "winblend", self.window.winblend) + local border_win = opts and opts.border and opts.border.win_id + if border_win then + a.nvim_win_set_option(border_win, "winblend", self.window.winblend) + end + return win, opts, border_win +end + +--- Opens the given picker for the user to interact with +---@note: this is the main function for pickers, as it actually creates the interface for users +function Picker:find() + self:close_existing_pickers() + self:reset_selection() + + self.__original_mousemoveevent = vim.o.mousemoveevent + vim.o.mousemoveevent = true + + self.original_win_id = a.nvim_get_current_win() + _, self.original_cword = pcall(vim.fn.expand, "") + + -- User autocmd run it before create Telescope window + vim.api.nvim_exec_autocmds("User", { pattern = "TelescopeFindPre" }) + + local layout = self:create_layout() + layout:mount() + + self.layout = layout + self.prompt_win, self.prompt_bufnr, self.prompt_border = + layout.prompt.winid, layout.prompt.bufnr, layout.prompt.border + self.results_win, self.results_bufnr, self.results_border = + layout.results.winid, layout.results.bufnr, layout.results.border + if layout.preview then + self.preview_win, self.preview_bufnr, self.preview_border = + layout.preview.winid, layout.preview.bufnr, layout.preview.border + else + self.preview_win, self.preview_bufnr, self.preview_border = nil, nil, nil + end + + pcall(a.nvim_buf_set_option, self.results_bufnr, "tabstop", 1) -- #1834 + pcall(a.nvim_buf_set_option, self.prompt_bufnr, "tabstop", 1) -- #1834 + a.nvim_buf_set_option(self.prompt_bufnr, "buftype", "prompt") + a.nvim_win_set_option(self.results_win, "wrap", self.wrap_results) + a.nvim_win_set_option(self.prompt_win, "wrap", true) + if self.preview_win then + a.nvim_win_set_option(self.preview_win, "wrap", true) + end + + -- Prompt prefix + local prompt_prefix = self.prompt_prefix + vim.fn.prompt_setprompt(self.prompt_bufnr, prompt_prefix) + self:_reset_prefix_color() + + -- TODO: This could be configurable in the future, but I don't know why you would + -- want to scroll through more than 10,000 items. + -- + -- This just lets us stop doing stuff after tons of things. + self.max_results = self.__scrolling_limit + + vim.api.nvim_buf_set_lines(self.results_bufnr, 0, self.max_results, false, utils.repeated_table(self.max_results, "")) + + local status_updater = self:get_status_updater(self.prompt_win, self.prompt_bufnr) + local debounced_status = debounce.throttle_leading(status_updater, 50) + + local tx, rx = channel.mpsc() + self._on_lines = tx.send + + local find_id = self:_next_find_id() + + if self.default_text then + self:set_prompt(self.default_text) + end + + if vim.tbl_contains({ "insert", "normal" }, self.initial_mode) then + local mode = vim.fn.mode() + local keys + if self.initial_mode == "normal" then + -- n: A makes sure cursor is at always at end of prompt w/o default_text + keys = mode ~= "n" and "A" or "A" + else + -- always fully retrigger insert mode: required for going from one picker to next + keys = mode ~= "n" and "A" or "A" + end + a.nvim_feedkeys(a.nvim_replace_termcodes(keys, true, false, true), "ni", true) + else + utils.notify("pickers.find", { + msg = "`initial_mode` should be one of ['normal', 'insert'] but passed " .. self.initial_mode, + level = "ERROR", + }) + end + + local main_loop = async.void(function() + self.sorter:_init() + + -- Do filetype last, so that users can register at the last second. + pcall(a.nvim_buf_set_option, self.prompt_bufnr, "filetype", "TelescopePrompt") + pcall(a.nvim_buf_set_option, self.results_bufnr, "filetype", "TelescopeResults") + + await_schedule() + + while true do + -- Wait for the next input + rx.last() + await_schedule() + + self:_reset_track() + + if not vim.api.nvim_buf_is_valid(self.prompt_bufnr) then + log.debug("ON_LINES: Invalid prompt_bufnr", self.prompt_bufnr) + return + end + + -- we kinda always wanna reset the color, because of `cc` and `dd` commands, + -- which also delete the prefix and after prefix deletion we need to reapply highlighting + self:_reset_prefix_color() + + local start_time = vim.loop.hrtime() + + local prompt = self:_get_next_filtered_prompt() + state.set_global_key("current_line", prompt) + + if self.__locations_input == true then + local filename, line_number, column_number = utils.__separate_file_path_location(prompt) + + if line_number or column_number then + state.set_global_key("prompt_location", { row = line_number, col = column_number }) + elseif state.get_global_key "prompt_location" then + state.set_global_key("prompt_location", nil) + end + + -- it is important to continue behaving as if there is no location in prompt + prompt = filename + elseif state.get_global_key "prompt_location" then + -- in case new picker that does not support locations is opened clear the location + state.set_global_key("prompt_location", nil) + end + + -- TODO: Entry manager should have a "bulk" setter. This can prevent a lot of redraws from display + if self.cache_picker == false or self.cache_picker.is_cached ~= true then + self.sorter:_start(prompt) + self.manager = EntryManager:new(self.max_results, self.entry_adder, self.stats) + + self:_reset_highlights() + local process_result = self:get_result_processor(find_id, prompt, debounced_status) + local process_complete = self:get_result_completor(self.results_bufnr, find_id, prompt, status_updater) + + local ok, msg = pcall(function() + self.finder(prompt, process_result, process_complete) + end) + + if not ok then + log.warn("Finder failed with msg: ", msg) + end + + local diff_time = (vim.loop.hrtime() - start_time) / 1e6 + if self.debounce and diff_time < self.debounce then + async.util.sleep(self.debounce - diff_time) + end + else + -- TODO(scroll): This can only happen once, I don't like where it is. + self:_resume_picker() + end + end + end) + + -- Register attach + vim.api.nvim_buf_attach(self.prompt_bufnr, false, { + on_lines = function(...) + if self._finder_attached then + find_id = self:_next_find_id() + + status_updater { completed = false } + self._on_lines(...) + end + end, + + on_detach = function() + self:_detach() + end, + }) + + vim.api.nvim_create_augroup("PickerInsert", {}) + -- TODO: Use WinLeave as well? + vim.api.nvim_create_autocmd("BufLeave", { + buffer = self.prompt_bufnr, + group = "PickerInsert", + nested = true, + once = true, + callback = function() + require("telescope.pickers").on_close_prompt(self.prompt_bufnr) + end, + }) + vim.api.nvim_create_autocmd("VimResized", { + buffer = self.prompt_bufnr, + group = "PickerInsert", + nested = true, + callback = function() + require("telescope.pickers").on_resize_window(self.prompt_bufnr) + end, + }) + + state.set_status( + self.prompt_bufnr, + setmetatable({ + layout = layout, + picker = self, + + -- compatibility + prompt_bufnr = self.prompt_bufnr, + prompt_win = self.prompt_win, + prompt_border_win = self.prompt_border.winid, + results_bufnr = self.results_bufnr, + results_win = self.results_win, + results_border_win = self.results_border.winid, + preview_bufnr = self.preview_bufnr, + preview_win = self.preview_win, + preview_border_win = self.preview_border and self.preview_border.winid, + }, { + __mode = "kv", + }) + ) + + mappings.apply_keymap(self.prompt_bufnr, self.attach_mappings, config.values.mappings) + + tx.send() + main_loop() +end + +--- A helper function to update picker windows when layout options are changed +function Picker:recalculate_layout() + local status = state.get_status(self.prompt_bufnr) + + status.layout:update() + + local layout = status.layout + self.prompt_win, self.prompt_bufnr, self.prompt_border = + layout.prompt.winid, layout.prompt.bufnr, layout.prompt.border + self.results_win, self.results_bufnr, self.results_border = + layout.results.winid, layout.results.bufnr, layout.results.border + if layout.preview then + self.preview_win, self.preview_bufnr, self.preview_border = + layout.preview.winid, layout.preview.bufnr, layout.preview.border + else + self.preview_win, self.preview_bufnr, self.preview_border = nil, nil, nil + end + + -- Temporarily disabled: Draw the screen ASAP. This makes things feel speedier. + -- vim.cmd [[redraw]] + + -- self.max_results = popup_opts.results.height +end + +local update_scroll = function(win, oldinfo, oldcursor, strategy, buf_maxline) + if strategy == "ascending" then + vim.api.nvim_win_set_cursor(win, { buf_maxline, 0 }) + vim.api.nvim_win_set_cursor(win, { oldinfo.topline, 0 }) + vim.api.nvim_win_set_cursor(win, oldcursor) + elseif strategy == "descending" then + vim.api.nvim_win_set_cursor(win, { 1, 0 }) + vim.api.nvim_win_set_cursor(win, { oldinfo.botline, 0 }) + vim.api.nvim_win_set_cursor(win, oldcursor) + else + error(debug.traceback("Unknown sorting strategy: " .. (strategy or ""))) + end +end + +--- A wrapper for `Picker:recalculate_layout()` that also handles maintaining cursor position +function Picker:full_layout_update() + local oldinfo = vim.fn.getwininfo(self.results_win)[1] + local oldcursor = vim.api.nvim_win_get_cursor(self.results_win) + self:recalculate_layout() + self:refresh_previewer() + + -- update scrolled position + local buf_maxline = #vim.api.nvim_buf_get_lines(self.results_bufnr, 0, -1, false) + update_scroll(self.results_win, oldinfo, oldcursor, self.sorting_strategy, buf_maxline) +end + +-- TODO: update multi-select with the correct tag name when available +--- A simple interface to remove an entry from the results window without +--- closing telescope. This either deletes the current selection or all the +--- selections made using multi-select. It can be used to define actions +--- such as deleting buffers or files. +--- +--- Example usage: +--- +--- actions.delete_something = function(prompt_bufnr) +--- local current_picker = action_state.get_current_picker(prompt_bufnr) +--- current_picker:delete_selection(function(selection) +--- -- delete the selection outside of telescope +--- end) +--- end +--- +--- +--- Example usage in telescope: +--- - `actions.delete_buffer()` +---@param delete_cb function: called for each selection fn(s) -> bool|nil (true|nil removes the entry from the results) +function Picker:delete_selection(delete_cb) + vim.validate { delete_cb = { delete_cb, "f" } } + local original_selection_strategy = self.selection_strategy + self.selection_strategy = "row" + + local delete_selections = self._multi:get() + local used_multi_select = true + if vim.tbl_isempty(delete_selections) then + table.insert(delete_selections, self:get_selection()) + used_multi_select = false + end + + local selection_index = {} + for result_index, result_entry in ipairs(self.finder.results) do + if vim.tbl_contains(delete_selections, result_entry) then + table.insert(selection_index, result_index) + end + end + + -- Sort in reverse order as removing an entry from the table shifts down the + -- other elements to close the hole. + table.sort(selection_index, function(x, y) + return x > y + end) + for _, index in ipairs(selection_index) do + local delete_cb_return = delete_cb(self.finder.results[index]) + if delete_cb_return == nil or delete_cb_return == true then + table.remove(self.finder.results, index) + end + end + + if used_multi_select then + self._multi = MultiSelect:new() + end + + self:refresh() + vim.defer_fn(function() + self.selection_strategy = original_selection_strategy + end, 50) +end + +---@param text string text to set as prompt +---@param reset boolean? whether to replace prompt with text entirely or just append +function Picker:set_prompt(text, reset) + reset = vim.F.if_nil(reset, true) + if not reset then + text = self:_get_prompt() .. text + end + self:reset_prompt(text) +end + +--- Closes the windows for the prompt, results and preview +---@param status table: table containing information on the picker +--- and associated windows. Generally obtained from `state.get_status` +function Picker.close_windows(status) + local prompt_bufnr = status.layout.prompt.bufnr + status.layout:unmount() + state.clear_status(prompt_bufnr) +end + +--- Get the entry table of the current selection +---@return table +function Picker:get_selection() + return self._selection_entry +end + +--- Get the row number of the current selection +---@return number +function Picker:get_selection_row() + if self._selection_row then + -- If the current row is no longer selectable than reduce it to num_results - 1, so the next selectable row. + -- This makes selection_strategy `row` work much better if the selected row is no longer part of the output. + --TODO(conni2461): Maybe this can be moved to scroller. (currently in a hotfix so not viable) + if self.selection_strategy == "row" then + local num_results = self.manager:num_results() + if self.sorting_strategy == "ascending" then + if self._selection_row >= num_results then + return num_results - 1 + end + else + local max = self.max_results - num_results + if self._selection_row < max then + return self.max_results - num_results + end + end + end + return self._selection_row + end + return self:get_reset_row() +end + +--- Move the current selection by `change` steps +---@param change number +function Picker:move_selection(change) + self:set_selection(self:get_selection_row() + change) +end + +--- Add the entry of the given row to the multi-select object +---@param row number: the number of the chosen row +function Picker:add_selection(row) + local entry = self.manager:get_entry(self:get_index(row)) + self._multi:add(entry) + + self:update_prefix(entry, row) + self:get_status_updater(self.prompt_win, self.prompt_bufnr)() + self.highlighter:hi_multiselect(row, true) +end + +--- Remove the entry of the given row to the multi-select object +---@param row number: the number of the chosen row +function Picker:remove_selection(row) + local entry = self.manager:get_entry(self:get_index(row)) + self._multi:drop(entry) + + self:update_prefix(entry, row) + self:get_status_updater(self.prompt_win, self.prompt_bufnr)() + self.highlighter:hi_multiselect(row, false) +end + +--- Check if the given row is in the multi-select object +---@param entry table: table with information about the chosen entry +---@return number: the "count" associated to the entry in the multi-select +--- object (if present), `nil` otherwise +function Picker:is_multi_selected(entry) + return self._multi:is_selected(entry) +end + +--- Get a table containing all of the currently selected entries +---@return table: an integer indexed table of selected entries +function Picker:get_multi_selection() + return self._multi:get() +end + +--- Toggle the given row in and out of the multi-select object. +--- Also updates the highlighting for the given entry +---@param row number: the number of the chosen row +function Picker:toggle_selection(row) + local entry = self.manager:get_entry(self:get_index(row)) + if entry == nil then + return + end + self._multi:toggle(entry) + + self:update_prefix(entry, row) + self:get_status_updater(self.prompt_win, self.prompt_bufnr)() + self.highlighter:hi_multiselect(row, self._multi:is_selected(entry)) +end + +--- Set the current selection to `nil` +---@note: generally used when a picker is first activated with `find()` +function Picker:reset_selection() + self._selection_entry = nil + self._selection_row = nil +end + +function Picker:_reset_prefix_color(hl_group) + self._current_prefix_hl_group = hl_group or nil + + if self.prompt_prefix ~= "" and a.nvim_buf_is_valid(self.prompt_bufnr) then + vim.api.nvim_buf_add_highlight( + self.prompt_bufnr, + ns_telescope_prompt_prefix, + self._current_prefix_hl_group or "TelescopePromptPrefix", + 0, + 0, + #self.prompt_prefix + ) + end +end + +-- TODO(conni2461): Maybe _ prefix these next two functions +-- TODO(conni2461): Next two functions only work together otherwise color doesn't work +-- Probably a issue with prompt buffers +--- Change the prefix in the prompt to be `new_prefix` and apply `hl_group` +---@param new_prefix string: the string to be used as the new prefix +---@param hl_group string: the name of the chosen highlight +function Picker:change_prompt_prefix(new_prefix, hl_group) + if not new_prefix then + return + end + + if new_prefix ~= "" then + vim.fn.prompt_setprompt(self.prompt_bufnr, new_prefix) + else + vim.api.nvim_buf_set_text(self.prompt_bufnr, 0, 0, 0, #self.prompt_prefix, {}) + vim.api.nvim_buf_set_option(self.prompt_bufnr, "buftype", "") + end + self.prompt_prefix = new_prefix + self:_reset_prefix_color(hl_group) +end + +--- Reset the prompt to the provided `text` +---@param text string +function Picker:reset_prompt(text) + local prompt_text = self.prompt_prefix .. (text or "") + vim.api.nvim_buf_set_lines(self.prompt_bufnr, 0, -1, false, { prompt_text }) + self:_reset_prefix_color(self._current_prefix_hl_group) + + if text then + vim.api.nvim_win_set_cursor(self.prompt_win, { 1, #prompt_text }) + end +end + +---@param finder finder: telescope finder (see telescope/finders.lua) +---@param opts table: options to pass when refreshing the picker +---@field new_prefix string|table: either as string or { new_string, hl_group } +---@field reset_prompt bool: whether to reset the prompt +---@field multi MultiSelect: multi-selection to persist upon renewing finder (see telescope/pickers/multi.lua) +function Picker:refresh(finder, opts) + opts = opts or {} + if opts.new_prefix then + local handle = type(opts.new_prefix) == "table" and unpack or function(x) + return x + end + self:change_prompt_prefix(handle(opts.new_prefix), opts.prefix_hl_group) + end + + if finder then + self.finder:close() + self.finder = finder + self._multi = vim.F.if_nil(opts.multi, MultiSelect:new()) + end + + -- reset already triggers finder loop + if opts.reset_prompt then + self:reset_prompt() + else + self._on_lines(nil, nil, nil, 0, 1) + end +end + +---Set the selection to the provided `row` +---@param row number +function Picker:set_selection(row) + if not self.manager then + return + end + + row = self.scroller(self.max_results, self.manager:num_results(), row) + + if not self:can_select_row(row) then + -- If the current selected row exceeds number of currently displayed + -- elements we have to reset it. Affects sorting_strategy = 'row'. + if not self:can_select_row(self:get_selection_row()) then + row = self:get_row(self.manager:num_results()) + else + log.trace("Cannot select row:", row, self.manager:num_results(), self.max_results) + return + end + end + + local results_bufnr = self.results_bufnr + if not a.nvim_buf_is_valid(results_bufnr) then + return + end + + if row > a.nvim_buf_line_count(results_bufnr) then + log.debug( + string.format("Should not be possible to get row this large %s %s", row, a.nvim_buf_line_count(results_bufnr)) + ) + + return + end + + local entry = self.manager:get_entry(self:get_index(row)) + + local prompt_location = state.get_global_key "prompt_location" + if entry and prompt_location then + entry.lnum = prompt_location.row or 0 + if prompt_location.col and prompt_location.col > 0 then + entry.col = prompt_location.col + entry.colend = prompt_location.col + 1 + else + entry.col = 1 -- we do + 1 here because previewer does -1 + entry.colend = 0 + end + end + + state.set_global_key("selected_entry", entry) + + if not entry then + -- also refresh previewer when there is no entry selected, so the preview window is cleared + self._selection_entry = entry + self:refresh_previewer() + return + end + + local old_entry + + -- TODO: Probably should figure out what the rows are that made this happen... + -- Probably something with setting a row that's too high for this? + -- Not sure. + local set_ok, set_errmsg = pcall(function() + local prompt = self:_get_prompt() + + -- Check if previous selection is still visible + if self._selection_entry and self.manager:find_entry(self._selection_entry) then + -- Find old selection, and update prefix and highlights + old_entry = self._selection_entry + local old_row = self:get_row(self.manager:find_entry(old_entry)) + + self._selection_entry = entry + + if old_row >= 0 then + self:update_prefix(old_entry, old_row) + self.highlighter:hi_multiselect(old_row, self:is_multi_selected(old_entry)) + end + else + self._selection_entry = entry + end + + local caret = self:update_prefix(entry, row) + + local display, _ = entry_display.resolve(self, entry) + display = caret .. display + + -- TODO: You should go back and redraw the highlights for this line from the sorter. + -- That's the only smart thing to do. + if not a.nvim_buf_is_valid(results_bufnr) then + log.debug "Invalid buf somehow..." + return + end + + -- don't highlight any whitespace at the end of caret + self.highlighter:hi_selection(row, caret:match "(.*%S)") + self.highlighter:hi_sorter(row, prompt, display) + + self.highlighter:hi_multiselect(row, self:is_multi_selected(entry)) + end) + + if not set_ok then + log.debug(set_errmsg) + return + end + + self:refresh_previewer() + if old_entry == entry and self._selection_row == row then + return + end + + -- TODO: Get row & text in the same obj + self._selection_entry = entry + self._selection_row = row + + vim.api.nvim_win_set_cursor(self.results_win, { row + 1, 0 }) +end + +--- Update prefix for entry on a given row +function Picker:update_prefix(entry, row) + local prefix = function(sel, multi) + local t + if sel then + t = self.selection_caret + else + t = self.entry_prefix + end + if multi and type(self.multi_icon) == "string" then + t = truncate(t, strdisplaywidth(t) - strdisplaywidth(self.multi_icon), "") .. self.multi_icon + end + return t + end + + local line = vim.api.nvim_buf_get_lines(self.results_bufnr, row, row + 1, false)[1] + if not line then + log.trace(string.format("no line found at row %d in buffer %d", row, self.results_bufnr)) + return + end + + local old_caret = string.sub(line, 0, #prefix(true)) == prefix(true) and prefix(true) + or string.sub(line, 0, #prefix(true, true)) == prefix(true, true) and prefix(true, true) + or string.sub(line, 0, #prefix(false)) == prefix(false) and prefix(false) + or string.sub(line, 0, #prefix(false, true)) == prefix(false, true) and prefix(false, true) + if old_caret == false then + log.warn(string.format("can't identify old caret in line: %s", line)) + return + end + + local pre = prefix(entry == self._selection_entry, self:is_multi_selected(entry)) + -- Only change the first couple characters, nvim_buf_set_text leaves the existing highlights + a.nvim_buf_set_text(self.results_bufnr, row, 0, row, #old_caret, { pre }) + return pre +end + +--- Refresh the previewer based on the current `status` of the picker +function Picker:refresh_previewer() + local status = state.get_status(self.prompt_bufnr) + if + self.previewer + and status.layout.preview + and status.layout.preview.winid + and a.nvim_win_is_valid(status.layout.preview.winid) + then + self:_increment "previewed" + + self.previewer:preview(self._selection_entry, status) + if self.preview_border then + if self.fix_preview_title then + return + end + + local new_title = self.previewer:title(self._selection_entry, config.values.dynamic_preview_title) + if new_title ~= nil and new_title ~= self.preview_title then + self.preview_title = new_title + self.layout.preview.border:change_title(new_title) + end + end + end +end + +function Picker:cycle_previewers(next) + local size = #self.all_previewers + if size == 1 then + return + end + + self.current_previewer_index = self.current_previewer_index + next + if self.current_previewer_index > size then + self.current_previewer_index = 1 + elseif self.current_previewer_index < 1 then + self.current_previewer_index = size + end + + if self.previewer then + self.previewer = self.all_previewers[self.current_previewer_index] + self:refresh_previewer() + elseif self.hidden_previewer then + self.hidden_previewer = self.all_previewers[self.current_previewer_index] + end +end + +--- Handler for when entries are added by `self.manager` +---@param index number: the index to add the entry at +---@param entry table: the entry that has been added to the manager +---@param insert boolean: whether the entry has been "inserted" or not +function Picker:entry_adder(index, entry, _, insert) + if not entry then + return + end + + local row = self:get_row(index) + + -- If it's less than 0, then we don't need to show it at all. + if row < 0 then + log.debug("ON_ENTRY: Weird row", row) + return + end + + local display, display_highlights = entry_display.resolve(self, entry) + if not display then + log.info("Weird entry", entry) + return + end + + -- This is the two spaces to manage the '> ' stuff. + -- Maybe someday we can use extmarks or floaty text or something to draw this and not insert here. + -- until then, insert two spaces + local prefix = self.entry_prefix + display = prefix .. display + + self:_increment "displayed" + + local offset = insert and 0 or 1 + if not vim.api.nvim_buf_is_valid(self.results_bufnr) then + log.debug "ON_ENTRY: Invalid buffer" + return + end + + -- TODO: Does this every get called? + -- local line_count = vim.api.nvim_win_get_height(self.results_win) + local line_count = vim.api.nvim_buf_line_count(self.results_bufnr) + if row > line_count then + return + end + + if insert then + if self.sorting_strategy == "descending" then + vim.api.nvim_buf_set_lines(self.results_bufnr, 0, 1, false, {}) + end + end + + local set_ok, msg = pcall(vim.api.nvim_buf_set_lines, self.results_bufnr, row, row + offset, false, { display }) + if set_ok then + if display_highlights then + self.highlighter:hi_display(row, prefix, display_highlights) + end + self:update_prefix(entry, row) + self:highlight_one_row(self.results_bufnr, self:_get_prompt(), display, row) + end + + if not set_ok then + log.debug("Failed to set lines...", msg) + end + + -- This pretty much only fails when people leave newlines in their results. + -- So we'll clean it up for them if it fails. + if not set_ok and display:find "\n" then + display = display:gsub("\n", " | ") + vim.api.nvim_buf_set_lines(self.results_bufnr, row, row + 1, false, { display }) + end +end + +--- Reset tracked information for this picker +function Picker:_reset_track() + self.stats.processed = 0 + self.stats.displayed = 0 + self.stats.display_fn = 0 + self.stats.previewed = 0 + self.stats.status = 0 + + self.stats.filtered = 0 + self.stats.highlights = 0 +end + +--- Increment the count of the tracked info at `self.stats[key]` +---@param key string +function Picker:_increment(key) + self.stats[key] = (self.stats[key] or 0) + 1 +end + +--- Decrement the count of the tracked info at `self.stats[key]` +---@param key string +function Picker:_decrement(key) + self.stats[key] = (self.stats[key] or 0) - 1 +end + +-- TODO: Decide how much we want to use this. +-- Would allow for better debugging of items. +function Picker:register_completion_callback(cb) + table.insert(self._completion_callbacks, cb) +end + +function Picker:clear_completion_callbacks() + self._completion_callbacks = {} +end + +function Picker:_on_complete() + for _, v in ipairs(self._completion_callbacks) do + pcall(v, self) + end +end + +--- Close all open Telescope pickers +function Picker:close_existing_pickers() + for _, prompt_bufnr in ipairs(state.get_existing_prompt_bufnrs()) do + pcall(actions.close, prompt_bufnr) + end +end + +--- Returns a function that sets virtual text for the count indicator +--- e.g. "10/50" as "filtered"/"processed" +---@param prompt_win number +---@param prompt_bufnr number +---@return function +function Picker:get_status_updater(prompt_win, prompt_bufnr) + return function(opts) + if self.closed or not vim.api.nvim_buf_is_valid(prompt_bufnr) then + return + end + + local current_prompt = self:_get_prompt() + if not current_prompt then + return + end + + if not vim.api.nvim_win_is_valid(prompt_win) then + return + end + + local text = self:get_status_text(opts) + vim.api.nvim_buf_clear_namespace(prompt_bufnr, ns_telescope_prompt, 0, -1) + vim.api.nvim_buf_set_extmark(prompt_bufnr, ns_telescope_prompt, 0, 0, { + virt_text = { { text, "TelescopePromptCounter" } }, + virt_text_pos = "right_align", + }) + + self:_increment "status" + end +end + +--- Returns a function that will process an element. +--- Returned function handles updating the "filtered" and "processed" counts +--- as appropriate and runs the sorters score function +---@param find_id number +---@param prompt string +---@param status_updater function +---@return function +function Picker:get_result_processor(find_id, prompt, status_updater) + local count = 0 + + local cb_add = function(score, entry) + -- may need the prompt for tiebreak + self.manager:add_entry(self, score, entry, prompt) + status_updater { completed = false } + end + + local cb_filter = function(_) + self:_increment "filtered" + end + + return function(entry) + if find_id ~= self._find_id then + return true + end + + if not entry or entry.valid == false then + return + end + + self:_increment "processed" + + count = count + 1 + + -- TODO: Probably should asyncify this / cache this / do something because this probably takes + -- a ton of time on large results. + log.trace("Processing result... ", entry) + for _, v in ipairs(self.file_ignore_patterns or {}) do + local file = vim.F.if_nil(entry.filename, type(entry.value) == "string" and entry.value) -- false if none is true + if file then + if string.find(file, v) then + log.trace("SKIPPING", entry.value, "because", v) + self:_decrement "processed" + return + end + end + end + + self.sorter:score(prompt, entry, cb_add, cb_filter) + end +end + +--- Handles updating the picker after all the entries are scored/processed. +---@param results_bufnr number +---@param find_id number +---@param prompt string +---@param status_updater function +function Picker:get_result_completor(results_bufnr, find_id, prompt, status_updater) + return vim.schedule_wrap(function() + if self.closed == true or self:is_done() then + return + end + + self:_do_selection(prompt) + + status_updater { completed = true } + + self:clear_extra_rows(results_bufnr) + self.sorter:_finish(prompt) + + if self.sorting_strategy == "descending" then + local visible_result_rows = vim.api.nvim_win_get_height(self.results_win) + vim.api.nvim_win_set_cursor(self.results_win, { self.max_results - visible_result_rows, 1 }) + vim.api.nvim_win_set_cursor(self.results_win, { self.max_results, 1 }) + else + vim.api.nvim_win_set_cursor(self.results_win, { 1, 0 }) + end + self:_on_complete() + end) +end + +function Picker:_do_selection(prompt) + local selection_strategy = self.selection_strategy or "reset" + -- TODO: Either: always leave one result or make sure we actually clean up the results when nothing matches + if selection_strategy == "row" then + if self._selection_row == nil and self.default_selection_index ~= nil then + self:set_selection(self:get_row(self.default_selection_index)) + else + self:set_selection(self:get_selection_row()) + end + elseif selection_strategy == "follow" then + if self._selection_row == nil and self.default_selection_index ~= nil then + self:set_selection(self:get_row(self.default_selection_index)) + else + local index = self.manager:find_entry(self:get_selection()) + + if index then + local follow_row = self:get_row(index) + self:set_selection(follow_row) + else + self:set_selection(self:get_reset_row()) + end + end + elseif selection_strategy == "reset" then + if self.default_selection_index ~= nil then + self:set_selection(self:get_row(self.default_selection_index)) + else + self:set_selection(self:get_reset_row()) + end + elseif selection_strategy == "closest" then + if prompt == "" and self.default_selection_index ~= nil then + self:set_selection(self:get_row(self.default_selection_index)) + else + self:set_selection(self:get_reset_row()) + end + elseif selection_strategy == "none" then + if self._selection_entry then + local old_entry, old_row = self._selection_entry, self._selection_row + self:reset_selection() -- required to reset selection before updating prefix + if old_row >= 0 then + self:update_prefix(old_entry, old_row) + self.highlighter:hi_multiselect(old_row, self:is_multi_selected(old_entry)) + end + end + return + else + error("Unknown selection strategy: " .. selection_strategy) + end +end + +--- Wrapper function for `Picker:new` that incorporates user provided `opts` +--- with the telescope `defaults` +---@param opts table +---@param defaults table +---@return Picker +pickers.new = function(opts, defaults) + opts = opts or {} + defaults = defaults or {} + local result = {} + + for k, v in pairs(opts) do + assert(type(k) == "string" or type(k) == "number", "Should be string or number, found: " .. type(k)) + result[k] = v + end + + for k, v in pairs(defaults) do + if result[k] == nil then + assert(type(k) == "string", "Should be string, defaults") + result[k] = v + else + -- For attach mappings, we want people to be able to pass in another function + -- and apply their mappings after we've applied our defaults. + if k == "attach_mappings" then + local opt_value = result[k] + result[k] = function(...) + v(...) + return opt_value(...) + end + end + end + end + + if result["previewer"] == false then + result["previewer"] = defaults["previewer"] + result["__hide_previewer"] = true + elseif result["previewer"] == true then + result["previewer"] = defaults["previewer"] + elseif type(opts["preview"]) == "table" and opts["preview"]["hide_on_startup"] then + result["__hide_previewer"] = true + end + + return Picker:new(result) +end + +--- Close the picker which has prompt with buffer number `prompt_bufnr` +---@param prompt_bufnr number +function pickers.on_close_prompt(prompt_bufnr) + local status = state.get_status(prompt_bufnr) + local picker = status.picker + require("telescope.actions.state").get_current_history():reset() + + if type(picker.cache_picker) == "table" then + local cached_pickers = state.get_global_key "cached_pickers" or {} + + if type(picker.cache_picker.index) == "number" then + if not vim.tbl_isempty(cached_pickers) then + table.remove(cached_pickers, picker.cache_picker.index) + end + end + + -- if picker was disabled post-hoc (e.g. `cache_picker = false` conclude after deletion) + if picker.cache_picker.disabled ~= true then + if picker.cache_picker.limit_entries > 0 then + -- edge case: starting in normal mode and not having run a search means having no manager instantiated + if picker.manager then + picker.manager.linked_states:truncate(picker.cache_picker.limit_entries) + else + picker.manager = EntryManager:new(picker.max_results, picker.entry_adder, picker.stats) + end + end + local curr_prompt = picker:_get_prompt() + picker.default_text = curr_prompt + picker.cache_picker.selection_row = picker._selection_row + + -- Only cache if prompt is not empty or ignore_empty_prompt is false + if not picker.cache_picker.ignore_empty_prompt or (curr_prompt and curr_prompt ~= "") then + picker.cache_picker.cached_prompt = curr_prompt + table.insert(cached_pickers, 1, picker) + picker.cache_picker.is_cached = true + end + + -- release pickers + if picker.cache_picker.num_pickers > 0 then + while #cached_pickers > picker.cache_picker.num_pickers do + table.remove(cached_pickers, #cached_pickers) + end + end + state.set_global_key("cached_pickers", cached_pickers) + end + end + + if picker.sorter then + picker.sorter:_destroy() + end + + if picker.all_previewers then + for _, v in ipairs(picker.all_previewers) do + v:teardown() + end + end + + if picker.finder then + picker.finder:close() + end + + -- so we dont call close_windows multiple times we clear that autocmd + vim.api.nvim_clear_autocmds { + group = "PickerInsert", + event = "BufLeave", + buffer = prompt_bufnr, + } + picker.close_windows(status) + + vim.o.mousemoveevent = picker.__original_mousemoveevent +end + +function pickers.on_resize_window(prompt_bufnr) + local status = state.get_status(prompt_bufnr) + local picker = status.picker + + picker:full_layout_update() +end + +--- Get the prompt text without the prompt prefix. +function Picker:_get_prompt() + local cursor_line = vim.api.nvim_win_get_cursor(self.prompt_win)[1] - 1 + return vim.api + .nvim_buf_get_lines(self.prompt_bufnr, cursor_line, cursor_line + 1, false)[1] + :sub(#self.prompt_prefix + 1) +end + +function Picker:_reset_highlights() + self.highlighter:clear_display() + vim.api.nvim_buf_clear_namespace(self.results_bufnr, ns_telescope_matching, 0, -1) +end + +-- Toggles whether finder is attached to prompt buffer input +function Picker:_toggle_finder_attach() + self._finder_attached = not self._finder_attached +end + +function Picker:_detach() + self.finder:close() + + -- TODO: Can we add a "cleanup" / "teardown" function that completely removes these. + -- self.finder = nil + -- self.previewer = nil + -- self.sorter = nil + -- self.manager = nil + + self.closed = true +end + +function Picker:_get_next_filtered_prompt() + local prompt = self:_get_prompt() + local on_input_result = self._on_input_filter_cb(prompt) or {} + + local new_prompt = on_input_result.prompt + if new_prompt then + prompt = new_prompt + end + + local new_finder = on_input_result.updated_finder + if new_finder then + self.finder:close() + self.finder = new_finder + end + + return prompt +end + +function Picker:_resume_picker() + -- resume previous picker + local index = 1 + for entry in self.manager:iter() do + self:entry_adder(index, entry, _, true) + index = index + 1 + end + self.cache_picker.is_cached = false + local on_resume_complete = function() + if vim.api.nvim_buf_is_valid(self.prompt_bufnr) then + vim.api.nvim_buf_call(self.prompt_bufnr, function() + vim.cmd "do User TelescopeResumePost" + end) + end + end + -- if text changed, required to set anew to restart finder; otherwise hl and selection + if self.cache_picker.cached_prompt ~= self.default_text then + self:set_prompt(self.default_text) + on_resume_complete() + else + -- scheduling required to apply highlighting and selection appropriately + await_schedule(function() + if self.cache_picker.selection_row ~= nil then + self:set_selection(self.cache_picker.selection_row) + end + on_resume_complete() + end) + end +end + +pickers._Picker = Picker + +return pickers diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/entry_display.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/entry_display.lua new file mode 100644 index 00000000..1353cff1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/entry_display.lua @@ -0,0 +1,169 @@ +---@tag telescope.pickers.entry_display + +---@brief [[ +--- Entry Display is used to format each entry shown in the result panel. +--- +--- Entry Display create() will give us a function based on the configuration +--- of column widths we pass into it. We then can use this function n times to +--- return a string based on structured input. +--- +--- Note that if you call `create()` inside `make_display` it will be called for +--- every single entry. So it is suggested to do this outside of `make_display` +--- for the best performance. +--- +--- The create function will use the column widths passed to it in +--- configuration.items. Each item in that table is the number of characters in +--- the column. It's also possible for the final column to not have a fixed +--- width, this will be shown in the configuration as 'remaining = true'. +--- +--- An example of this configuration is shown for the buffers picker: +--- +--- local displayer = entry_display.create { +--- separator = " ", +--- items = { +--- { width = opts.bufnr_width }, +--- { width = 4 }, +--- { width = icon_width }, +--- { remaining = true }, +--- }, +--- } +--- +--- +--- This shows 4 columns, the first is defined in the opts as the width we'll +--- use when display_string is the number of the buffer. The second has a fixed +--- width of 4 and the third column's width will be decided by the width of the +--- icons we use. The fourth column will use the remaining space. Finally, we +--- have also defined the separator between each column will be the space " ". +--- +--- An example of how the display reference will be used is shown, again for +--- the buffers picker: +--- +--- return displayer { +--- { entry.bufnr, "TelescopeResultsNumber" }, +--- { entry.indicator, "TelescopeResultsComment" }, +--- { icon, hl_group }, +--- display_bufname .. ":" .. entry.lnum, +--- } +--- +--- +--- There are two types of values each column can have. Either a simple String +--- or a table containing the String as well as the hl_group. +--- +--- The displayer can return values, string and an optional highlights. The string +--- is all the text to be displayed for this entry as a single string. If parts of +--- the string are to be highlighted they will be described in the highlights +--- table. +--- +--- For a better understanding of how create() and displayer are used it's best to look +--- at the code in make_entry.lua. +---@brief ]] + +local strings = require "plenary.strings" +local state = require "telescope.state" +local resolve = require "telescope.config.resolve" + +local entry_display = {} +entry_display.truncate = strings.truncate + +entry_display.create = function(configuration) + local generator = {} + for _, v in ipairs(configuration.items) do + if v.width then + local justify = v.right_justify + local width + table.insert(generator, function(item) + if width == nil then + local status = state.get_status(vim.F.if_nil(configuration.prompt_bufnr, vim.api.nvim_get_current_buf())) + local s = {} + s[1] = vim.api.nvim_win_get_width(status.layout.results.winid) - #status.picker.selection_caret + s[2] = vim.api.nvim_win_get_height(status.layout.results.winid) + width = resolve.resolve_width(v.width)(nil, s[1], s[2]) + end + if type(item) == "table" then + return strings.align_str(entry_display.truncate(item[1], width), width, justify), item[2] + else + return strings.align_str(entry_display.truncate(item, width), width, justify) + end + end) + else + table.insert(generator, function(item) + if type(item) == "table" then + return item[1], item[2] + else + return item + end + end) + end + end + + return function(self, picker) + local results = {} + local highlights = {} + for i = 1, #generator do + if self[i] ~= nil then + local str, hl = generator[i](self[i], picker) + if hl then + local hl_start = 0 + for j = 1, (i - 1) do + hl_start = hl_start + #results[j] + (#configuration.separator or 1) + end + local hl_end = hl_start + #str:gsub("%s*$", "") + + if type(hl) == "function" then + for _, hl_res in ipairs(hl()) do + table.insert(highlights, { { hl_res[1][1] + hl_start, hl_res[1][2] + hl_start }, hl_res[2] }) + end + else + table.insert(highlights, { { hl_start, hl_end }, hl }) + end + end + + table.insert(results, str) + end + end + + if configuration.separator_hl then + local width = #configuration.separator or 1 + + local hl_start, hl_end + for _, v in ipairs(results) do + hl_start = (hl_end or 0) + #tostring(v) + hl_end = hl_start + width + table.insert(highlights, { { hl_start, hl_end }, configuration.separator_hl }) + end + end + + local final_str = table.concat(results, configuration.separator or "│") + if configuration.hl_chars then + for i = 1, #final_str do + local c = final_str:sub(i, i) + local hl = configuration.hl_chars[c] + if hl then + table.insert(highlights, { { i - 1, i }, hl }) + end + end + end + + return final_str, highlights + end +end + +entry_display.resolve = function(self, entry) + local display, display_highlights + if type(entry.display) == "function" then + self:_increment "display_fn" + display, display_highlights = entry:display(self) + + if type(display) == "string" then + return display, display_highlights + end + else + display = entry.display + end + + if type(display) == "string" then + return display, display_highlights + end +end + +return entry_display diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/highlights.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/highlights.lua new file mode 100644 index 00000000..be693a7b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/highlights.lua @@ -0,0 +1,125 @@ +local a = vim.api +local log = require "telescope.log" +local conf = require("telescope.config").values + +local highlights = {} + +local ns_telescope_selection = a.nvim_create_namespace "telescope_selection" +local ns_telescope_multiselection = a.nvim_create_namespace "telescope_multiselection" +local ns_telescope_entry = a.nvim_create_namespace "telescope_entry" + +local Highlighter = {} +Highlighter.__index = Highlighter + +function Highlighter:new(picker) + return setmetatable({ + picker = picker, + }, self) +end + +function Highlighter:hi_display(row, prefix, display_highlights) + -- This is the bug that made my highlight fixes not work. + -- We will leave the solution commented, so the test fails. + if not display_highlights or vim.tbl_isempty(display_highlights) then + return + end + + local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr") + + a.nvim_buf_clear_namespace(results_bufnr, ns_telescope_entry, row, row + 1) + local len_prefix = #prefix + + for _, hl_block in ipairs(display_highlights) do + a.nvim_buf_add_highlight( + results_bufnr, + ns_telescope_entry, + hl_block[2], + row, + len_prefix + hl_block[1][1], + len_prefix + hl_block[1][2] + ) + end +end + +function Highlighter:clear_display() + if + not self + or not self.picker + or not self.picker.results_bufnr + or not vim.api.nvim_buf_is_valid(self.picker.results_bufnr) + then + return + end + + a.nvim_buf_clear_namespace(self.picker.results_bufnr, ns_telescope_entry, 0, -1) +end + +function Highlighter:hi_sorter(row, prompt, display) + local picker = self.picker + if not picker.sorter or not picker.sorter.highlighter then + return + end + + local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr") + picker:highlight_one_row(results_bufnr, prompt, display, row) +end + +function Highlighter:hi_selection(row, caret) + caret = vim.F.if_nil(caret, "") + local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr") + + a.nvim_buf_clear_namespace(results_bufnr, ns_telescope_selection, 0, -1) + a.nvim_buf_add_highlight(results_bufnr, ns_telescope_selection, "TelescopeSelectionCaret", row, 0, #caret) + + a.nvim_buf_set_extmark( + results_bufnr, + ns_telescope_selection, + row, + #caret, + { end_line = row + 1, hl_eol = conf.hl_result_eol, hl_group = "TelescopeSelection" } + ) +end + +function Highlighter:hi_multiselect(row, is_selected) + local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr") + + if is_selected then + vim.api.nvim_buf_add_highlight(results_bufnr, ns_telescope_multiselection, "TelescopeMultiSelection", row, 0, -1) + if self.picker.multi_icon then + local line = vim.api.nvim_buf_get_lines(results_bufnr, row, row + 1, false)[1] + local pos = line:find(self.picker.multi_icon) + if pos and pos <= math.max(#self.picker.selection_caret, #self.picker.entry_prefix) then + vim.api.nvim_buf_add_highlight( + results_bufnr, + ns_telescope_multiselection, + "TelescopeMultiIcon", + row, + pos - 1, + pos - 1 + #self.picker.multi_icon + ) + end + end + else + local existing_marks = vim.api.nvim_buf_get_extmarks( + results_bufnr, + ns_telescope_multiselection, + { row, 0 }, + { row, -1 }, + {} + ) + + -- This is still kind of weird to me, since it seems like I'm erasing stuff + -- when I shouldn't... Perhaps it's about the gravity of the extmark? + if #existing_marks > 0 then + log.trace("Clearing highlight multi select row: ", row) + + vim.api.nvim_buf_clear_namespace(results_bufnr, ns_telescope_multiselection, row, row + 1) + end + end +end + +highlights.new = function(...) + return Highlighter:new(...) +end + +return highlights diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/layout.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/layout.lua new file mode 100644 index 00000000..1dde1c44 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/layout.lua @@ -0,0 +1,193 @@ +---@tag telescope.pickers.layout +---@config { ["module"] = "telescope.pickers.layout" } + +---@brief [[ +--- The telescope pickers layout can be configured using the +--- |telescope.defaults.create_layout| option. +--- +--- Parameters: ~ +--- - picker : A Picker object. +--- +--- Return: ~ +--- - layout : instance of `TelescopeLayout` class. +--- +--- Example: ~ +--- +--- local Layout = require "telescope.pickers.layout" +--- +--- require("telescope").setup { +--- create_layout = function(picker) +--- local function create_window(enter, width, height, row, col, title) +--- local bufnr = vim.api.nvim_create_buf(false, true) +--- local winid = vim.api.nvim_open_win(bufnr, enter, { +--- style = "minimal", +--- relative = "editor", +--- width = width, +--- height = height, +--- row = row, +--- col = col, +--- border = "single", +--- title = title, +--- }) +--- +--- vim.wo[winid].winhighlight = "Normal:Normal" +--- +--- return Layout.Window { +--- bufnr = bufnr, +--- winid = winid, +--- } +--- end +--- +--- local function destory_window(window) +--- if window then +--- if vim.api.nvim_win_is_valid(window.winid) then +--- vim.api.nvim_win_close(window.winid, true) +--- end +--- if vim.api.nvim_buf_is_valid(window.bufnr) then +--- vim.api.nvim_buf_delete(window.bufnr, { force = true }) +--- end +--- end +--- end +--- +--- local layout = Layout { +--- picker = picker, +--- mount = function(self) +--- self.results = create_window(false, 40, 20, 0, 0, "Results") +--- self.preview = create_window(false, 40, 23, 0, 42, "Preview") +--- self.prompt = create_window(true, 40, 1, 22, 0, "Prompt") +--- end, +--- unmount = function(self) +--- destory_window(self.results) +--- destory_window(self.preview) +--- destory_window(self.prompt) +--- end, +--- update = function(self) end, +--- } +--- +--- return layout +--- end, +--- } +--- +---@brief ]] + +local function wrap_instance(class, instance) + local self = instance + if not getmetatable(instance) then + self = setmetatable(instance, { __index = class }) + end + return self +end + +---@class TelescopeWindowBorder.config +---@field bufnr integer +---@field winid integer|nil +---@field change_title nil|function: (self: TelescopeWindowBorder, title: string, pos?: "NW"|"N"|"NE"|"SW"|"S"|"SE"):nil + +---@param class TelescopeWindowBorder +---@param config TelescopeWindowBorder.config +---@return TelescopeWindowBorder +local function init_border(class, config) + config = config or {} + + ---@type TelescopeWindowBorder + local self = wrap_instance(class, config) + if not self.change_title then + self.change_title = class.change_title + end + + return self +end + +---@class TelescopeWindowBorder +---@field bufnr integer|nil +---@field winid integer|nil +local Border = setmetatable({}, { + __call = init_border, + __name = "TelescopeWindowBorder", +}) + +---@param title string +---@param pos "NW"|"N"|"NE"|"SW"|"S"|"SE"|nil +function Border:change_title(title, pos) end + +---@class TelescopeWindow.config +---@field bufnr integer +---@field winid integer|nil +---@field border TelescopeWindowBorder.config|nil + +---@param class TelescopeWindow +---@param config TelescopeWindow.config +---@return TelescopeWindow +local function init_window(class, config) + config = config or {} + + ---@type TelescopeWindow + local self = wrap_instance(class, config) + self.border = Border(config.border) + + return self +end + +---@class TelescopeWindow +---@field border TelescopeWindowBorder +---@field bufnr integer +---@field winid integer +local Window = setmetatable({}, { + __call = init_window, + __name = "TelescopeWindow", +}) + +---@class TelescopeLayout.config +---@field mount function: (self: TelescopeLayout):nil +---@field unmount function: (self: TelescopeLayout):nil +---@field update function: (self: TelescopeLayout):nil +---@field prompt TelescopeWindow|nil +---@field results TelescopeWindow|nil +---@field preview TelescopeWindow|nil + +---@param class TelescopeLayout +---@param config TelescopeLayout.config +---@return TelescopeLayout +local function init_layout(class, config) + config = config or {} + + ---@type TelescopeLayout + local self = wrap_instance(class, config) + + assert(config.mount, "missing layout:mount") + assert(config.unmount, "missing layout:unmount") + assert(config.update, "missing layout:update") + + return self +end + +---@class TelescopeLayout +---@field prompt TelescopeWindow +---@field results TelescopeWindow +---@field preview TelescopeWindow|nil +local Layout = setmetatable({ + Window = Window, +}, { + __call = init_layout, + __name = "TelescopeLayout", +}) + +--- Create the layout. +--- This needs to ensure the required properties are populated. +function Layout:mount() end + +--- Destroy the layout. +--- This is responsible for performing clean-up, for example: +--- - deleting buffers +--- - closing windows +--- - clearing autocmds +function Layout:unmount() end + +--- Refresh the layout. +--- This is called when, for example, vim is resized. +function Layout:update() end + +---@alias TelescopeWindow.constructor fun(config: TelescopeWindow.config): TelescopeWindow +---@alias TelescopeLayout.constructor fun(config: TelescopeLayout.config): TelescopeLayout + +return Layout --[[@as TelescopeLayout.constructor|{ Window: TelescopeWindow.constructor }]] diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/layout_strategies.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/layout_strategies.lua new file mode 100644 index 00000000..931da094 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/layout_strategies.lua @@ -0,0 +1,949 @@ +---@tag telescope.layout +---@config { ["module"] = "telescope.layout" } + +---@brief [[ +--- The layout of telescope pickers can be adjusted using the +--- |telescope.defaults.layout_strategy| and |telescope.defaults.layout_config| options. +--- For example, the following configuration changes the default layout strategy and the +--- default size of the picker: +--- +--- require('telescope').setup{ +--- defaults = { +--- layout_strategy = 'vertical', +--- layout_config = { height = 0.95 }, +--- }, +--- } +--- +--- +--- ──────────────────────────────────────────────────────────────────────────────── +--- +--- Layout strategies are different functions to position telescope. +--- +--- All layout strategies are functions with the following signature: +--- +--- +--- function(picker, columns, lines, layout_config) +--- -- Do some calculations here... +--- return { +--- preview = preview_configuration +--- results = results_configuration, +--- prompt = prompt_configuration, +--- } +--- end +--- +--- +---
+---   Parameters: ~
+---     - picker        : A Picker object. (docs coming soon)
+---     - columns       : (number) Columns in the vim window
+---     - lines         : (number) Lines in the vim window
+---     - layout_config : (table) The configuration values specific to the picker.
+--- 
+--- +--- This means you can create your own layout strategy if you want! Just be aware +--- for now that we may change some APIs or interfaces, so they may break if you create +--- your own. +--- +--- A good method for creating your own would be to copy one of the strategies that most +--- resembles what you want from "./lua/telescope/pickers/layout_strategies.lua" in the +--- telescope repo. +--- +---@brief ]] + +local resolve = require "telescope.config.resolve" +local p_window = require "telescope.pickers.window" + +local get_border_size = function(opts) + if opts.window.border == false then + return 0 + end + + return 1 +end + +local calc_tabline = function(max_lines) + local tbln = (vim.o.showtabline == 2) or (vim.o.showtabline == 1 and #vim.api.nvim_list_tabpages() > 1) + if tbln then + max_lines = max_lines - 1 + end + return max_lines, tbln +end + +-- Helper function for capping over/undersized width/height, and calculating spacing +--@param cur_size number: size to be capped +--@param max_size any: the maximum size, e.g. max_lines or max_columns +--@param bs number: the size of the border +--@param w_num number: the maximum number of windows of the picker in the given direction +--@param b_num number: the number of border rows/column in the given direction (when border enabled) +--@param s_num number: the number of gaps in the given direction (when border disabled) +local calc_size_and_spacing = function(cur_size, max_size, bs, w_num, b_num, s_num) + local spacing = s_num * (1 - bs) + b_num * bs + cur_size = math.min(cur_size, max_size) + cur_size = math.max(cur_size, w_num + spacing) + return cur_size, spacing +end + +local layout_strategies = {} +layout_strategies._configurations = {} + +--@param strategy_config table: table with keys for each option for a strategy +--@return table: table with keys for each option (for this strategy) and with keys for each layout_strategy +local get_valid_configuration_keys = function(strategy_config) + local valid_configuration_keys = { + -- TEMP: There are a few keys we should say are valid to start with. + preview_cutoff = true, + prompt_position = true, + } + + for key in pairs(strategy_config) do + valid_configuration_keys[key] = true + end + + for name in pairs(layout_strategies) do + valid_configuration_keys[name] = true + end + + return valid_configuration_keys +end + +local adjust_pos = function(pos, ...) + for _, opts in ipairs { ... } do + opts.col = opts.col and opts.col + pos[1] + opts.line = opts.line and opts.line + pos[2] + end +end + +--@param strategy_name string: the name of the layout_strategy we are validating for +--@param configuration table: table with keys for each option available +--@param values table: table containing all of the non-default options we want to set +--@param default_layout_config table: table with the default values to configure layouts +--@return table: table containing the combined options (defaults and non-defaults) +local function validate_layout_config(strategy_name, configuration, values, default_layout_config) + assert(strategy_name, "It is required to have a strategy name for validation.") + local valid_configuration_keys = get_valid_configuration_keys(configuration) + + -- If no default_layout_config provided, check Telescope's config values + default_layout_config = vim.F.if_nil(default_layout_config, require("telescope.config").values.layout_config) + + local result = {} + local get_value = function(k) + -- skip "private" items + if string.sub(k, 1, 1) == "_" then + return + end + + local val + -- Prioritise options that are specific to this strategy + if values[strategy_name] ~= nil and values[strategy_name][k] ~= nil then + val = values[strategy_name][k] + end + + -- Handle nested layout config values + if layout_strategies[k] and strategy_name ~= k and type(val) == "table" then + val = vim.tbl_deep_extend("force", default_layout_config[k], val) + end + + if val == nil and values[k] ~= nil then + val = values[k] + end + + if val == nil then + if default_layout_config[strategy_name] ~= nil and default_layout_config[strategy_name][k] ~= nil then + val = default_layout_config[strategy_name][k] + else + val = default_layout_config[k] + end + end + + return val + end + + -- Always set the values passed first. + for k in pairs(values) do + if not valid_configuration_keys[k] then + -- TODO: At some point we'll move to error here, + -- but it's a bit annoying to just straight up crash everyone's stuff. + vim.api.nvim_err_writeln( + string.format( + "Unsupported layout_config key for the %s strategy: %s\n%s", + strategy_name, + k, + vim.inspect(values) + ) + ) + end + + result[k] = get_value(k) + end + + -- And then set other valid keys via "inheritance" style extension + for k in pairs(valid_configuration_keys) do + if result[k] == nil then + result[k] = get_value(k) + end + end + + return result +end + +-- List of options that are shared by more than one layout. +local shared_options = { + width = { "How wide to make Telescope's entire layout", "See |resolver.resolve_width()|" }, + height = { "How tall to make Telescope's entire layout", "See |resolver.resolve_height()|" }, + mirror = "Flip the location of the results/prompt and preview windows", + scroll_speed = "The number of lines to scroll through the previewer", + prompt_position = { "Where to place prompt window.", "Available Values: 'bottom', 'top'" }, + anchor = { "Which edge/corner to pin the picker to", "See |resolver.resolve_anchor_pos()|" }, +} + +-- Used for generating vim help documentation. +layout_strategies._format = function(name) + local strategy_config = layout_strategies._configurations[name] + if vim.tbl_isempty(strategy_config) then + return {} + end + + local results = { "
", "`picker.layout_config` shared options:" }
+
+  local strategy_keys = vim.tbl_keys(strategy_config)
+  table.sort(strategy_keys, function(a, b)
+    return a < b
+  end)
+
+  local add_value = function(k, val)
+    if type(val) == "string" then
+      table.insert(results, string.format("  - %s: %s", k, val))
+    elseif type(val) == "table" then
+      table.insert(results, string.format("  - %s:", k))
+      for _, line in ipairs(val) do
+        table.insert(results, string.format("    - %s", line))
+      end
+    else
+      error(string.format("expected string or table but found '%s'", type(val)))
+    end
+  end
+
+  for _, k in ipairs(strategy_keys) do
+    if shared_options[k] then
+      add_value(k, strategy_config[k])
+    end
+  end
+
+  table.insert(results, "")
+  table.insert(results, "`picker.layout_config` unique options:")
+
+  for _, k in ipairs(strategy_keys) do
+    if not shared_options[k] then
+      add_value(k, strategy_config[k])
+    end
+  end
+
+  table.insert(results, "
") + return results +end + +--@param name string: the name to be assigned to the layout +--@param layout_config table: table where keys are the available options for the layout +--@param layout function: function with signature +-- function(self, max_columns, max_lines, layout_config): table +-- the returned table is the sizing and location information for the parts of the picker +--@retun function: wrapped function that inputs a validated layout_config into the `layout` function +local function make_documented_layout(name, layout_config, layout) + -- Save configuration data to be used by documentation + layout_strategies._configurations[name] = layout_config + + -- Return new function that always validates configuration + return function(self, max_columns, max_lines, override_layout) + return layout( + self, + max_columns, + max_lines, + validate_layout_config( + name, + layout_config, + vim.tbl_deep_extend("keep", vim.F.if_nil(override_layout, {}), vim.F.if_nil(self.layout_config, {})) + ) + ) + end +end + +--- Horizontal layout has two columns, one for the preview +--- and one for the prompt and results. +--- +---
+--- ┌──────────────────────────────────────────────────┐
+--- │                                                  │
+--- │    ┌───────────────────┐┌───────────────────┐    │
+--- │    │                   ││                   │    │
+--- │    │                   ││                   │    │
+--- │    │                   ││                   │    │
+--- │    │      Results      ││                   │    │
+--- │    │                   ││      Preview      │    │
+--- │    │                   ││                   │    │
+--- │    │                   ││                   │    │
+--- │    └───────────────────┘│                   │    │
+--- │    ┌───────────────────┐│                   │    │
+--- │    │      Prompt       ││                   │    │
+--- │    └───────────────────┘└───────────────────┘    │
+--- │                                                  │
+--- └──────────────────────────────────────────────────┘
+--- 
+---@eval { ["description"] = require('telescope.pickers.layout_strategies')._format("horizontal") } +--- +layout_strategies.horizontal = make_documented_layout( + "horizontal", + vim.tbl_extend("error", shared_options, { + preview_width = { "Change the width of Telescope's preview window", "See |resolver.resolve_width()|" }, + preview_cutoff = "When columns are less than this value, the preview will be disabled", + }), + function(self, max_columns, max_lines, layout_config) + local initial_options = p_window.get_initial_window_options(self) + local preview = initial_options.preview + local results = initial_options.results + local prompt = initial_options.prompt + + local tbln + max_lines, tbln = calc_tabline(max_lines) + + local width_opt = layout_config.width + local width = resolve.resolve_width(width_opt)(self, max_columns, max_lines) + + local height_opt = layout_config.height + local height = resolve.resolve_height(height_opt)(self, max_columns, max_lines) + + local bs = get_border_size(self) + + local w_space + if self.previewer and max_columns >= layout_config.preview_cutoff then + -- Cap over/undersized width (with previewer) + width, w_space = calc_size_and_spacing(width, max_columns, bs, 2, 4, 1) + + preview.width = resolve.resolve_width(vim.F.if_nil(layout_config.preview_width, function(_, cols) + if cols < 150 then + return math.floor(cols * 0.4) + elseif cols < 200 then + return 80 + else + return 120 + end + end))(self, width, max_lines) + + results.width = width - preview.width - w_space + prompt.width = results.width + else + -- Cap over/undersized width (without previewer) + width, w_space = calc_size_and_spacing(width, max_columns, bs, 1, 2, 0) + + preview.width = 0 + results.width = width - preview.width - w_space + prompt.width = results.width + end + + local h_space + -- Cap over/undersized height + height, h_space = calc_size_and_spacing(height, max_lines, bs, 2, 4, 1) + + prompt.height = 1 + results.height = height - prompt.height - h_space + + if self.previewer then + preview.height = height - 2 * bs + else + preview.height = 0 + end + + local width_padding = math.floor((max_columns - width) / 2) + -- Default value is false, to use the normal horizontal layout + if not layout_config.mirror then + results.col = width_padding + bs + 1 + prompt.col = results.col + preview.col = results.col + results.width + 1 + bs + else + preview.col = width_padding + bs + 1 + prompt.col = preview.col + preview.width + 1 + bs + results.col = preview.col + preview.width + 1 + bs + end + + preview.line = math.floor((max_lines - height) / 2) + bs + 1 + if layout_config.prompt_position == "top" then + prompt.line = preview.line + results.line = prompt.line + prompt.height + 1 + bs + elseif layout_config.prompt_position == "bottom" then + results.line = preview.line + prompt.line = results.line + results.height + 1 + bs + else + error(string.format("Unknown prompt_position: %s\n%s", self.window.prompt_position, vim.inspect(layout_config))) + end + + local anchor_pos = resolve.resolve_anchor_pos(layout_config.anchor or "", width, height, max_columns, max_lines) + adjust_pos(anchor_pos, prompt, results, preview) + + if tbln then + prompt.line = prompt.line + 1 + results.line = results.line + 1 + preview.line = preview.line + 1 + end + + return { + preview = self.previewer and preview.width > 0 and preview, + results = results, + prompt = prompt, + } + end +) + +--- Centered layout with a combined block of the prompt +--- and results aligned to the middle of the screen. +--- The preview window is then placed in the remaining +--- space above or below, according to `anchor` or `mirror`. +--- Particularly useful for creating dropdown menus +--- (see |telescope.themes| and |themes.get_dropdown()|). +--- +--- Note that vertical anchoring, i.e. `anchor` containing +--- `"N"` or `"S"`, will override `mirror` config. For `"N"` +--- anchoring preview will be placed below prompt/result +--- block. For `"S"` anchoring preview will be placed above +--- prompt/result block. For horizontal only anchoring preview +--- will be placed according to `mirror` config, default is +--- above the prompt/result block. +--- +---
+--- ┌──────────────────────────────────────────────────┐
+--- │    ┌────────────────────────────────────────┐    │
+--- │    │                 Preview                │    │
+--- │    │                 Preview                │    │
+--- │    └────────────────────────────────────────┘    │
+--- │    ┌────────────────────────────────────────┐    │
+--- │    │                 Prompt                 │    │
+--- │    ├────────────────────────────────────────┤    │
+--- │    │                 Result                 │    │
+--- │    │                 Result                 │    │
+--- │    └────────────────────────────────────────┘    │
+--- │                                                  │
+--- │                                                  │
+--- │                                                  │
+--- │                                                  │
+--- └──────────────────────────────────────────────────┘
+--- 
+---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("center") } +--- +layout_strategies.center = make_documented_layout( + "center", + vim.tbl_extend("error", shared_options, { + preview_cutoff = "When lines are less than this value, the preview will be disabled", + }), + function(self, max_columns, max_lines, layout_config) + local initial_options = p_window.get_initial_window_options(self) + local preview = initial_options.preview + local results = initial_options.results + local prompt = initial_options.prompt + + local tbln + max_lines, tbln = calc_tabline(max_lines) + + -- This sets the width for the whole layout + local width_opt = layout_config.width + local width = resolve.resolve_width(width_opt)(self, max_columns, max_lines) + + -- This sets the height for the whole layout + local height_opt = layout_config.height + local height = resolve.resolve_height(height_opt)(self, max_columns, max_lines) + + local bs = get_border_size(self) + + local w_space + -- Cap over/undersized width + width, w_space = calc_size_and_spacing(width, max_columns, bs, 1, 2, 0) + + prompt.width = width - w_space + results.width = width - w_space + preview.width = width - w_space + + local h_space + -- Cap over/undersized height + height, h_space = calc_size_and_spacing(height, max_lines, bs, 2, 3, 0) + + prompt.height = 1 + results.height = height - prompt.height - h_space + + local topline = math.floor((max_lines / 2) - ((results.height + (2 * bs)) / 2) + 1) + -- Align the prompt and results so halfway up the screen is + -- in the middle of this combined block + if layout_config.prompt_position == "top" then + prompt.line = topline + results.line = prompt.line + 1 + bs + elseif layout_config.prompt_position == "bottom" then + results.line = topline + prompt.line = results.line + results.height + bs + if type(prompt.title) == "string" then + prompt.title = { { pos = "S", text = prompt.title } } + end + else + error(string.format("Unknown prompt_position: %s\n%s", self.window.prompt_position, vim.inspect(layout_config))) + end + + local width_padding = math.floor((max_columns - width) / 2) + bs + 1 + results.col, preview.col, prompt.col = width_padding, width_padding, width_padding + + local anchor = layout_config.anchor or "" + local anchor_pos = resolve.resolve_anchor_pos(anchor, width, height, max_columns, max_lines) + adjust_pos(anchor_pos, prompt, results, preview) + + -- Vertical anchoring (S or N variations) ignores layout_config.mirror + anchor = anchor:upper() + local mirror + if anchor:find "S" then + mirror = false + elseif anchor:find "N" then + mirror = true + else + mirror = layout_config.mirror + end + + -- Set preview position + local block_line = math.min(results.line, prompt.line) + if not mirror then -- Preview at top + preview.line = 1 + bs + preview.height = block_line - (2 + 2 * bs) + else -- Preview at bottom + preview.line = block_line + results.height + 2 + 2 * bs + preview.height = max_lines - preview.line - bs + 1 + end + + if not (self.previewer and max_lines >= layout_config.preview_cutoff) then + preview.height = 0 + end + + if tbln then + prompt.line = prompt.line + 1 + results.line = results.line + 1 + preview.line = preview.line + 1 + end + + return { + preview = self.previewer and preview.height > 0 and preview, + results = results, + prompt = prompt, + } + end +) + +--- Cursor layout dynamically positioned below the cursor if possible. +--- If there is no place below the cursor it will be placed above. +--- +---
+--- ┌──────────────────────────────────────────────────┐
+--- │                                                  │
+--- │   █                                              │
+--- │   ┌──────────────┐┌─────────────────────┐        │
+--- │   │    Prompt    ││      Preview        │        │
+--- │   ├──────────────┤│      Preview        │        │
+--- │   │    Result    ││      Preview        │        │
+--- │   │    Result    ││      Preview        │        │
+--- │   └──────────────┘└─────────────────────┘        │
+--- │                                         █        │
+--- │                                                  │
+--- │                                                  │
+--- │                                                  │
+--- │                                                  │
+--- │                                                  │
+--- └──────────────────────────────────────────────────┘
+--- 
+---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("cursor") } +layout_strategies.cursor = make_documented_layout( + "cursor", + vim.tbl_extend("error", { + width = shared_options.width, + height = shared_options.height, + scroll_speed = shared_options.scroll_speed, + }, { + preview_width = { "Change the width of Telescope's preview window", "See |resolver.resolve_width()|" }, + preview_cutoff = "When columns are less than this value, the preview will be disabled", + }), + function(self, max_columns, max_lines, layout_config) + local initial_options = p_window.get_initial_window_options(self) + local preview = initial_options.preview + local results = initial_options.results + local prompt = initial_options.prompt + local winid = self.original_win_id + + local height_opt = layout_config.height + local height = resolve.resolve_height(height_opt)(self, max_columns, max_lines) + + local width_opt = layout_config.width + local width = resolve.resolve_width(width_opt)(self, max_columns, max_lines) + + local bs = get_border_size(self) + + local h_space + -- Cap over/undersized height + height, h_space = calc_size_and_spacing(height, max_lines, bs, 2, 3, 0) + + prompt.height = 1 + results.height = height - prompt.height - h_space + preview.height = height - 2 * bs + + local w_space + if self.previewer and max_columns >= layout_config.preview_cutoff then + -- Cap over/undersized width (with preview) + width, w_space = calc_size_and_spacing(width, max_columns, bs, 2, 4, 0) + + preview.width = resolve.resolve_width(vim.F.if_nil(layout_config.preview_width, 2 / 3))(self, width, max_lines) + prompt.width = width - preview.width - w_space + results.width = prompt.width + else + -- Cap over/undersized width (without preview) + width, w_space = calc_size_and_spacing(width, max_columns, bs, 1, 2, 0) + + preview.width = 0 + prompt.width = width - w_space + results.width = prompt.width + end + + local position = vim.api.nvim_win_get_position(winid) + local winbar = (function() + if vim.fn.exists "&winbar" == 1 then + return vim.wo[winid].winbar == "" and 0 or 1 + end + return 0 + end)() + local top_left = { + line = vim.api.nvim_win_call(winid, vim.fn.winline) + position[1] + bs + winbar, + col = vim.api.nvim_win_call(winid, vim.fn.wincol) + position[2], + } + local bot_right = { + line = top_left.line + height - 1, + col = top_left.col + width - 1, + } + + if bot_right.line > max_lines then + -- position above current line + top_left.line = top_left.line - height - 1 + end + if bot_right.col >= max_columns then + -- cap to the right of the screen + top_left.col = max_columns - width + end + + prompt.line = top_left.line + 1 + results.line = prompt.line + bs + 1 + preview.line = prompt.line + + prompt.col = top_left.col + 1 + results.col = prompt.col + preview.col = results.col + (bs * 2) + results.width + + return { + preview = self.previewer and preview.width > 0 and preview, + results = results, + prompt = prompt, + } + end +) + +--- Vertical layout stacks the items on top of each other. +--- Particularly useful with thinner windows. +--- +---
+--- ┌──────────────────────────────────────────────────┐
+--- │                                                  │
+--- │    ┌────────────────────────────────────────┐    │
+--- │    │                 Preview                │    │
+--- │    │                 Preview                │    │
+--- │    │                 Preview                │    │
+--- │    └────────────────────────────────────────┘    │
+--- │    ┌────────────────────────────────────────┐    │
+--- │    │                 Result                 │    │
+--- │    │                 Result                 │    │
+--- │    └────────────────────────────────────────┘    │
+--- │    ┌────────────────────────────────────────┐    │
+--- │    │                 Prompt                 │    │
+--- │    └────────────────────────────────────────┘    │
+--- │                                                  │
+--- └──────────────────────────────────────────────────┘
+--- 
+---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("vertical") } +--- +layout_strategies.vertical = make_documented_layout( + "vertical", + vim.tbl_extend("error", shared_options, { + preview_cutoff = "When lines are less than this value, the preview will be disabled", + preview_height = { "Change the height of Telescope's preview window", "See |resolver.resolve_height()|" }, + }), + function(self, max_columns, max_lines, layout_config) + local initial_options = p_window.get_initial_window_options(self) + local preview = initial_options.preview + local results = initial_options.results + local prompt = initial_options.prompt + + local tbln + max_lines, tbln = calc_tabline(max_lines) + + local width_opt = layout_config.width + local width = resolve.resolve_width(width_opt)(self, max_columns, max_lines) + + local height_opt = layout_config.height + local height = resolve.resolve_height(height_opt)(self, max_columns, max_lines) + + local bs = get_border_size(self) + + local w_space + -- Cap over/undersized width + width, w_space = calc_size_and_spacing(width, max_columns, bs, 1, 2, 0) + + prompt.width = width - w_space + results.width = prompt.width + preview.width = prompt.width + + local h_space + if self.previewer and max_lines >= layout_config.preview_cutoff then + -- Cap over/undersized height (with previewer) + height, h_space = calc_size_and_spacing(height, max_lines, bs, 3, 6, 2) + + preview.height = + resolve.resolve_height(vim.F.if_nil(layout_config.preview_height, 0.5))(self, max_columns, height) + else + -- Cap over/undersized height (without previewer) + height, h_space = calc_size_and_spacing(height, max_lines, bs, 2, 4, 1) + + preview.height = 0 + end + prompt.height = 1 + results.height = height - preview.height - prompt.height - h_space + + local width_padding = math.floor((max_columns - width) / 2) + bs + 1 + results.col, preview.col, prompt.col = width_padding, width_padding, width_padding + + local height_padding = math.floor((max_lines - height) / 2) + if not layout_config.mirror then + preview.line = height_padding + (1 + bs) + if layout_config.prompt_position == "top" then + prompt.line = (preview.height == 0) and preview.line or preview.line + preview.height + (1 + bs) + results.line = prompt.line + prompt.height + (1 + bs) + elseif layout_config.prompt_position == "bottom" then + results.line = (preview.height == 0) and preview.line or preview.line + preview.height + (1 + bs) + prompt.line = results.line + results.height + (1 + bs) + else + error(string.format("Unknown prompt_position: %s\n%s", self.window.prompt_position, vim.inspect(layout_config))) + end + else + if layout_config.prompt_position == "top" then + prompt.line = height_padding + (1 + bs) + results.line = prompt.line + prompt.height + (1 + bs) + preview.line = results.line + results.height + (1 + bs) + elseif layout_config.prompt_position == "bottom" then + results.line = height_padding + (1 + bs) + prompt.line = results.line + results.height + (1 + bs) + preview.line = prompt.line + prompt.height + (1 + bs) + else + error(string.format("Unknown prompt_position: %s\n%s", self.window.prompt_position, vim.inspect(layout_config))) + end + end + + local anchor_pos = resolve.resolve_anchor_pos(layout_config.anchor or "", width, height, max_columns, max_lines) + adjust_pos(anchor_pos, prompt, results, preview) + + if tbln then + prompt.line = prompt.line + 1 + results.line = results.line + 1 + preview.line = preview.line + 1 + end + + return { + preview = self.previewer and preview.height > 0 and preview, + results = results, + prompt = prompt, + } + end +) + +--- Flex layout swaps between `horizontal` and `vertical` strategies based on the window width +--- - Supports |layout_strategies.vertical| or |layout_strategies.horizontal| features +--- +---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("flex") } +--- +layout_strategies.flex = make_documented_layout( + "flex", + vim.tbl_extend("error", shared_options, { + flip_columns = "The number of columns required to move to horizontal mode", + flip_lines = "The number of lines required to move to horizontal mode", + vertical = "Options to pass when switching to vertical layout", + horizontal = "Options to pass when switching to horizontal layout", + }), + function(self, max_columns, max_lines, layout_config) + local flip_columns = vim.F.if_nil(layout_config.flip_columns, 100) + local flip_lines = vim.F.if_nil(layout_config.flip_lines, 20) + + if max_columns < flip_columns and max_lines > flip_lines then + self.__flex_strategy = "vertical" + self.layout_config.flip_columns = nil + self.layout_config.flip_lines = nil + return layout_strategies.vertical(self, max_columns, max_lines, layout_config.vertical) + else + self.__flex_strategy = "horizontal" + self.layout_config.flip_columns = nil + self.layout_config.flip_lines = nil + return layout_strategies.horizontal(self, max_columns, max_lines, layout_config.horizontal) + end + end +) + +layout_strategies.current_buffer = make_documented_layout("current_buffer", { + -- No custom options. + -- height, width ignored +}, function(self, _, _, _) + local initial_options = p_window.get_initial_window_options(self) + + local window_width = vim.api.nvim_win_get_width(0) + local window_height = vim.api.nvim_win_get_height(0) + + local preview = initial_options.preview + local results = initial_options.results + local prompt = initial_options.prompt + + local bs = get_border_size(self) + + -- Width + local width_padding = (1 + bs) -- TODO(l-kershaw): make this configurable + + prompt.width = window_width - 2 * width_padding + results.width = prompt.width + preview.width = prompt.width + + -- Height + local height_padding = (1 + bs) -- TODO(l-kershaw): make this configurable + + prompt.height = 1 + if self.previewer then + results.height = 10 -- TODO(l-kershaw): make this configurable + preview.height = window_height - results.height - prompt.height - 2 * (1 + bs) - 2 * height_padding + else + results.height = window_height - prompt.height - (1 + bs) - 2 * height_padding + preview.height = 0 + end + + local win_position = vim.api.nvim_win_get_position(0) + + local line = win_position[1] + if self.previewer then + preview.line = height_padding + line + 1 + results.line = preview.line + preview.height + (1 + bs) + prompt.line = results.line + results.height + (1 + bs) + else + results.line = height_padding + line + 1 + prompt.line = results.line + results.height + (1 + bs) + end + + local col = win_position[2] + width_padding + 1 + preview.col, results.col, prompt.col = col, col, col + + return { + preview = preview.height > 0 and preview, + results = results, + prompt = prompt, + } +end) + +--- Bottom pane can be used to create layouts similar to "ivy". +--- +--- For an easy ivy configuration, see |themes.get_ivy()| +layout_strategies.bottom_pane = make_documented_layout( + "bottom_pane", + vim.tbl_extend("error", shared_options, { + preview_width = { "Change the width of Telescope's preview window", "See |resolver.resolve_width()|" }, + preview_cutoff = "When columns are less than this value, the preview will be disabled", + }), + function(self, max_columns, max_lines, layout_config) + local initial_options = p_window.get_initial_window_options(self) + local results = initial_options.results + local prompt = initial_options.prompt + local preview = initial_options.preview + + local tbln + max_lines, tbln = calc_tabline(max_lines) + + local height = vim.F.if_nil(resolve.resolve_height(layout_config.height)(self, max_columns, max_lines), 25) + if type(layout_config.height) == "table" and type(layout_config.height.padding) == "number" then + -- Since bottom_pane only has padding at the top, we only need half as much padding in total + -- This doesn't match the vim help for `resolve.resolve_height`, but it matches expectations + height = math.floor((max_lines + height) / 2) + end + + local bs = get_border_size(self) + + -- Cap over/undersized height + height, _ = calc_size_and_spacing(height, max_lines, bs, 2, 3, 0) + + -- Height + prompt.height = 1 + results.height = height - prompt.height - (2 * bs) + preview.height = results.height - bs + + -- Width + prompt.width = max_columns - 2 * bs + if self.previewer and max_columns >= layout_config.preview_cutoff then + -- Cap over/undersized width (with preview) + local width, w_space = calc_size_and_spacing(max_columns, max_columns, bs, 2, 4, 0) + + preview.width = resolve.resolve_width(vim.F.if_nil(layout_config.preview_width, 0.5))(self, width, max_lines) + results.width = width - preview.width - w_space + else + results.width = prompt.width + preview.width = 0 + end + + -- Line + if layout_config.prompt_position == "top" then + prompt.line = max_lines - results.height - (1 + bs) + 1 + results.line = prompt.line + 1 + preview.line = results.line + bs + if results.border == true then + results.border = { 0, 1, 1, 1 } + end + if type(results.title) == "string" then + results.title = { { pos = "S", text = results.title } } + end + if type(preview.title) == "string" then + preview.title = { { pos = "S", text = preview.title } } + end + elseif layout_config.prompt_position == "bottom" then + results.line = max_lines - results.height - (1 + bs) + 1 + preview.line = results.line + prompt.line = max_lines - bs + if type(prompt.title) == "string" then + prompt.title = { { pos = "S", text = prompt.title } } + end + if results.border == true then + results.border = { 1, 1, 0, 1 } + end + else + error(string.format("Unknown prompt_position: %s\n%s", self.window.prompt_position, vim.inspect(layout_config))) + end + + -- Col + prompt.col = 0 -- centered + if layout_config.mirror and preview.width > 0 then + results.col = preview.width + (3 * bs) + 1 + preview.col = bs + 1 + else + results.col = bs + 1 + preview.col = results.width + (3 * bs) + 1 + end + + if tbln then + prompt.line = prompt.line + 1 + results.line = results.line + 1 + preview.line = preview.line + 1 + end + + return { + preview = self.previewer and preview.width > 0 and preview, + prompt = prompt, + results = results, + } + end +) + +layout_strategies._validate_layout_config = validate_layout_config + +return layout_strategies diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/multi.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/multi.lua new file mode 100644 index 00000000..50aa0513 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/multi.lua @@ -0,0 +1,50 @@ +local MultiSelect = {} +MultiSelect.__index = MultiSelect + +function MultiSelect:new() + return setmetatable({ + _entries = {}, + }, MultiSelect) +end + +function MultiSelect:get() + local marked_entries = {} + for entry, count in pairs(self._entries) do + table.insert(marked_entries, { count, entry }) + end + + table.sort(marked_entries, function(left, right) + return left[1] < right[1] + end) + + local selections = {} + for _, entry in ipairs(marked_entries) do + table.insert(selections, entry[2]) + end + + return selections +end + +function MultiSelect:is_selected(entry) + return self._entries[entry] +end + +local multi_select_count = 0 +function MultiSelect:add(entry) + multi_select_count = multi_select_count + 1 + self._entries[entry] = multi_select_count +end + +function MultiSelect:drop(entry) + self._entries[entry] = nil +end + +function MultiSelect:toggle(entry) + if self:is_selected(entry) then + self:drop(entry) + else + self:add(entry) + end +end + +return MultiSelect diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/scroller.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/scroller.lua new file mode 100644 index 00000000..a658f685 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/scroller.lua @@ -0,0 +1,124 @@ +local scroller = {} + +local range_calculators = { + ascending = function(max_results, num_results) + return 0, math.min(max_results, num_results) + end, + + descending = function(max_results, num_results) + return math.max(max_results - num_results, 0), max_results + end, +} + +local scroll_calculators = { + cycle = function(range_fn) + return function(max_results, num_results, row) + local start, finish = range_fn(max_results, num_results) + + if row >= finish then + return start + elseif row < start then + return (finish - 1 < 0) and finish or finish - 1 + end + + return row + end + end, + + limit = function(range_fn) + return function(max_results, num_results, row) + local start, finish = range_fn(max_results, num_results) + + if row >= finish and finish > 0 then + return finish - 1 + elseif row < start then + return start + end + + return row + end + end, +} + +scroller.create = function(scroll_strategy, sorting_strategy) + local range_fn = range_calculators[sorting_strategy] + if not range_fn then + error(debug.traceback("Unknown sorting strategy: " .. sorting_strategy)) + end + + local scroll_fn = scroll_calculators[scroll_strategy] + if not scroll_fn then + error(debug.traceback("Unknown scroll strategy: " .. (scroll_strategy or ""))) + end + + local calculator = scroll_fn(range_fn) + return function(max_results, num_results, row) + local result = calculator(max_results, num_results, row) + + if result < 0 then + error( + string.format( + "Must never return a negative row: { result = %s, args = { %s %s %s } }", + result, + max_results, + num_results, + row + ) + ) + end + + if result > max_results then + error( + string.format( + "Must never exceed max results: { result = %s, args = { %s %s %s } }", + result, + max_results, + num_results, + row + ) + ) + end + + return result + end +end + +scroller.top = function(sorting_strategy, max_results, num_results) + if sorting_strategy == "ascending" then + return 0 + end + return (num_results > max_results) and 0 or (max_results - num_results) +end + +scroller.middle = function(sorting_strategy, max_results, num_results) + local mid_pos + + if sorting_strategy == "ascending" then + mid_pos = math.floor(num_results / 2) + else + mid_pos = math.floor(max_results - num_results / 2) + end + + return (num_results < max_results) and mid_pos or math.floor(max_results / 2) +end + +scroller.bottom = function(sorting_strategy, max_results, num_results) + if sorting_strategy == "ascending" then + return math.min(max_results, num_results) - 1 + end + return max_results - 1 +end + +scroller.better = function(sorting_strategy) + if sorting_strategy == "ascending" then + return -1 + else + return 1 + end +end + +scroller.worse = function(sorting_strategy) + return -(scroller.better(sorting_strategy)) +end + +return scroller diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/window.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/window.lua new file mode 100644 index 00000000..945a7e3a --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/pickers/window.lua @@ -0,0 +1,49 @@ +local resolve = require "telescope.config.resolve" + +local p_window = {} + +function p_window.get_window_options(picker, max_columns, max_lines) + local layout_strategy = picker.layout_strategy + local getter = require("telescope.pickers.layout_strategies")[layout_strategy] + + if not getter then + error(string.format("'%s' is not a valid layout strategy", layout_strategy)) + end + + return getter(picker, max_columns, max_lines) +end + +function p_window.get_initial_window_options(picker) + local popup_border = resolve.win_option(picker.window.border) + local popup_borderchars = resolve.win_option(picker.window.borderchars) + + local preview = { + title = picker.preview_title, + border = popup_border.preview, + borderchars = popup_borderchars.preview, + enter = false, + highlight = false, + } + + local results = { + title = picker.results_title, + border = popup_border.results, + borderchars = popup_borderchars.results, + enter = false, + } + + local prompt = { + title = picker.prompt_title, + border = popup_border.prompt, + borderchars = popup_borderchars.prompt, + enter = true, + } + + return { + preview = preview, + results = results, + prompt = prompt, + } +end + +return p_window diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/buffer_previewer.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/buffer_previewer.lua new file mode 100644 index 00000000..a404c14c --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/buffer_previewer.lua @@ -0,0 +1,1236 @@ +local from_entry = require "telescope.from_entry" +local Path = require "plenary.path" +local utils = require "telescope.utils" +local putils = require "telescope.previewers.utils" +local Previewer = require "telescope.previewers.previewer" +local conf = require("telescope.config").values +local global_state = require "telescope.state" + +local pscan = require "plenary.scandir" + +local buf_delete = utils.buf_delete +local git_command = utils.__git_command + +local previewers = {} + +local ns_previewer = vim.api.nvim_create_namespace "telescope.previewers" + +local has_file = 1 == vim.fn.executable "file" + +-- TODO(fdschmidt93) switch to Job once file_maker callbacks get cleaned up with plenary async +-- avoids SIGABRT from utils.get_os_command_output due to vim.time in fs_stat cb +local function capture(cmd, raw) + local f = assert(io.popen(cmd, "r")) + local s = assert(f:read "*a") + f:close() + if raw then + return s + end + s = string.gsub(s, "^%s+", "") + s = string.gsub(s, "%s+$", "") + s = string.gsub(s, "[\n\r]+", " ") + return s +end + +local function defaulter(f, default_opts) + default_opts = default_opts or {} + return { + new = function(opts) + if conf.preview == false and not opts.preview then + return false + end + opts.preview = type(opts.preview) ~= "table" and {} or opts.preview + if type(conf.preview) == "table" then + for k, v in pairs(conf.preview) do + opts.preview[k] = vim.F.if_nil(opts.preview[k], v) + end + end + return f(opts) + end, + __call = function() + local ok, err = pcall(f(default_opts)) + if not ok then + error(debug.traceback(err)) + end + end, + } +end + +-- modified vim.split to incorporate a timer +local function split(s, sep, plain, opts) + opts = opts or {} + local t = {} + for c in vim.gsplit(s, sep, plain) do + local line = opts.file_encoding and vim.iconv(c, opts.file_encoding, "utf8") or c + table.insert(t, line) + if opts.preview.timeout then + local diff_time = (vim.loop.hrtime() - opts.start_time) / 1e6 + if diff_time > opts.preview.timeout then + return + end + end + end + return t +end +local bytes_to_megabytes = math.pow(1024, 2) + +local color_hash = { + ["p"] = "TelescopePreviewPipe", + ["c"] = "TelescopePreviewCharDev", + ["d"] = "TelescopePreviewDirectory", + ["b"] = "TelescopePreviewBlock", + ["l"] = "TelescopePreviewLink", + ["s"] = "TelescopePreviewSocket", + ["."] = "TelescopePreviewNormal", + ["r"] = "TelescopePreviewRead", + ["w"] = "TelescopePreviewWrite", + ["x"] = "TelescopePreviewExecute", + ["-"] = "TelescopePreviewHyphen", + ["T"] = "TelescopePreviewSticky", + ["S"] = "TelescopePreviewSticky", + [2] = "TelescopePreviewSize", + [3] = "TelescopePreviewUser", + [4] = "TelescopePreviewGroup", + [5] = "TelescopePreviewDate", +} +color_hash[6] = function(line) + return color_hash[line:sub(1, 1)] +end + +local colorize_ls_long = function(bufnr, data, sections) + local windows_add = Path.path.sep == "\\" and 2 or 0 + for lnum, line in ipairs(data) do + local section = sections[lnum] + for i = 1, section[1].end_index - 1 do -- Highlight permissions + local c = line:sub(i, i) + vim.api.nvim_buf_add_highlight(bufnr, ns_previewer, color_hash[c], lnum - 1, i - 1, i) + end + for i = 2, #section do -- highlights size, (user, group), date and name + local hl_group = color_hash[i + (i ~= 2 and windows_add or 0)] + vim.api.nvim_buf_add_highlight( + bufnr, + ns_previewer, + type(hl_group) == "function" and hl_group(line) or hl_group, + lnum - 1, + section[i].start_index - 1, + section[i].end_index - 1 + ) + end + end +end + +local handle_directory_preview = function(filepath, bufnr, opts) + opts.preview.ls_short = vim.F.if_nil(opts.preview.ls_short, false) + + local set_colorize_lines + if opts.preview.ls_short then + set_colorize_lines = function(data, sections) + local PATH_SECTION = Path.path.sep == "\\" and 4 or 6 + local paths = {} + for i, line in ipairs(data) do + local section = sections[i][PATH_SECTION] + local path = line:sub(section.start_index, section.end_index) + table.insert(paths, path) + end + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, paths) + for i, path in ipairs(paths) do + local hl = color_hash[6](data[i]) + vim.api.nvim_buf_add_highlight(bufnr, ns_previewer, hl, i - 1, 0, #path) + end + end + else + set_colorize_lines = function(data, sections) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, data) + colorize_ls_long(bufnr, data, sections) + end + end + + pscan.ls_async(filepath, { + hidden = true, + group_directories_first = true, + on_exit = vim.schedule_wrap(function(data, sections) + set_colorize_lines(data, sections) + if opts.callback then + opts.callback(bufnr) + end + end), + }) +end + +local handle_file_preview = function(filepath, bufnr, stat, opts) + vim.schedule(function() + opts.ft = opts.use_ft_detect and putils.filetype_detect(filepath) + local possible_binary = false + if type(opts.preview.filetype_hook) == "function" and opts.ft ~= nil and opts.ft ~= "" then + if not opts.preview.filetype_hook(filepath, bufnr, opts) then + return + end + end + if opts.preview.check_mime_type == true and has_file and (opts.ft == nil or opts.ft == "") then + -- avoid SIGABRT in buffer previewer happening with utils.get_os_command_output + local mime_type = capture(string.format([[file --mime-type -b "%s"]], filepath)) + if putils.binary_mime_type(mime_type) then + if type(opts.preview.mime_hook) == "function" then + opts.preview.mime_hook(filepath, bufnr, opts) + return + else + possible_binary = true + end + end + if mime_type[2] == "json" then + opts.ft = "json" + end + end + + local mb_filesize = stat.size / bytes_to_megabytes + if opts.preview.filesize_limit then + if mb_filesize > opts.preview.filesize_limit then + if type(opts.preview.filesize_hook) == "function" then + opts.preview.filesize_hook(filepath, bufnr, opts) + else + putils.set_preview_message(bufnr, opts.winid, "File exceeds preview size limit", opts.preview.msg_bg_fillchar) + end + return + end + end + + opts.start_time = vim.loop.hrtime() + Path:new(filepath):_read_async(vim.schedule_wrap(function(data) + if not vim.api.nvim_buf_is_valid(bufnr) then + return + end + local processed_data = split(data, "[\r]?\n", nil, opts) + + if processed_data then + local ok = pcall(vim.api.nvim_buf_set_lines, bufnr, 0, -1, false, processed_data) + if not ok then + return + end + -- last resort, if ft is still empty at this point in time, + -- we need to determine the filetype using the buffer contents + if opts.ft == nil or opts.ft == "" then + opts.ft = vim.filetype.match { filename = filepath, buf = bufnr } + end + -- we need to attempt to call filetype hook at this point "again" + -- previously only if we had a valid filetype, now every time + -- also if there will never be a filetype + if type(opts.preview.filetype_hook) == "function" then + if not opts.preview.filetype_hook(filepath, bufnr, opts) then + return + end + end + -- if we still dont have a ft we need to display the binary message + if (opts.ft == nil or opts.ft == "") and possible_binary then + putils.set_preview_message(bufnr, opts.winid, "Binary cannot be previewed", opts.preview.msg_bg_fillchar) + return + end + + if opts.callback then + opts.callback(bufnr) + end + + if not (opts.preview.highlight_limit and mb_filesize > opts.preview.highlight_limit) then + putils.highlighter(bufnr, opts.ft, opts) + end + else + if type(opts.preview.timeout_hook) == "function" then + opts.preview.timeout_hook(filepath, bufnr, opts) + else + putils.set_preview_message(bufnr, opts.winid, "Previewer timed out", opts.preview.msg_bg_fillchar) + end + return + end + end)) + end) +end + +local PREVIEW_TIMEOUT_MS = 250 +local PREVIEW_FILESIZE_MB = 25 +local PREVIEW_HIGHLIGHT_MB = 1 + +previewers.file_maker = function(filepath, bufnr, opts) + opts = vim.F.if_nil(opts, {}) + opts.preview = vim.F.if_nil(opts.preview, {}) + opts.preview.timeout = vim.F.if_nil(opts.preview.timeout, PREVIEW_TIMEOUT_MS) + opts.preview.filesize_limit = vim.F.if_nil(opts.preview.filesize_limit, PREVIEW_FILESIZE_MB) + opts.preview.highlight_limit = vim.F.if_nil(opts.preview.highlight_limit, PREVIEW_HIGHLIGHT_MB) + opts.preview.msg_bg_fillchar = vim.F.if_nil(opts.preview.msg_bg_fillchar, "╱") + opts.preview.treesitter = vim.F.if_nil(opts.preview.treesitter, true) + if opts.use_ft_detect == nil then + opts.use_ft_detect = true + end + if opts.bufname ~= filepath then + if not vim.in_fast_event() then + filepath = utils.path_expand(filepath) + end + vim.loop.fs_stat(filepath, function(_, stat) + if not stat then + return + end + if stat.type == "directory" then + handle_directory_preview(filepath, bufnr, opts) + else + handle_file_preview(filepath, bufnr, stat, opts) + end + end) + else + if opts.callback then + if vim.in_fast_event() then + vim.schedule(function() + opts.callback(bufnr) + end) + else + opts.callback(bufnr) + end + end + end +end + +local search_cb_jump = function(self, bufnr, query) + if not query then + return + end + vim.api.nvim_buf_call(bufnr, function() + pcall(vim.fn.matchdelete, self.state.hl_id, self.state.winid) + vim.cmd "keepjumps norm! gg" + vim.fn.search(query, "W") + vim.cmd "norm! zz" + + self.state.hl_id = vim.fn.matchadd("TelescopePreviewMatch", query) + end) +end + +local search_teardown = function(self) + if self.state and self.state.hl_id then + pcall(vim.fn.matchdelete, self.state.hl_id, self.state.hl_win) + self.state.hl_id = nil + end +end + +local scroll_fn = function(self, direction) + if not self.state then + return + end + + local input = direction > 0 and [[]] or [[]] + local count = math.abs(direction) + + vim.api.nvim_win_call(self.state.winid, function() + vim.cmd([[normal! ]] .. count .. input) + end) +end + +local scroll_horizontal_fn = function(self, direction) + if not self.state then + return + end + + local input = direction > 0 and [[zl]] or [[zh]] + local count = math.abs(direction) + + vim.api.nvim_win_call(self.state.winid, function() + vim.api.nvim_win_set_option(self.state.winid, "virtualedit", "all") + vim.cmd([[normal! ]] .. count .. input) + end) +end + +previewers.new_buffer_previewer = function(opts) + opts = opts or {} + + assert(opts.define_preview, "define_preview is a required function") + assert(not opts.preview_fn, "preview_fn not allowed") + + local opt_setup = opts.setup + local opt_teardown = opts.teardown + + local old_bufs = {} + local bufname_table = {} + local preview_window_id + + local function get_bufnr(self) + if not self.state then + return nil + end + return self.state.bufnr + end + + local function set_bufnr(self, value) + if self.state then + self.state.bufnr = value + table.insert(old_bufs, value) + end + end + + local function get_bufnr_by_bufname(self, value) + if not self.state then + return nil + end + return bufname_table[value] + end + + local function set_bufname(self, value) + if self.state then + self.state.bufname = value + if value then + bufname_table[value] = get_bufnr(self) + end + end + end + + function opts.setup(self) + local state = {} + if opt_setup then + vim.tbl_deep_extend("force", state, opt_setup(self)) + end + return state + end + + function opts.teardown(self) + if opt_teardown then + opt_teardown(self) + end + + local last_nr + if opts.keep_last_buf then + last_nr = global_state.get_global_key "last_preview_bufnr" + -- Push in another buffer so the last one will not be cleaned up + if preview_window_id then + local bufnr = vim.api.nvim_create_buf(false, true) + utils.win_set_buf_noautocmd(preview_window_id, bufnr) + end + end + + set_bufnr(self, nil) + set_bufname(self, nil) + + for _, bufnr in ipairs(old_bufs) do + if bufnr ~= last_nr then + buf_delete(bufnr) + end + end + -- enable resuming picker with existing previewer to avoid lookup of deleted bufs + bufname_table = {} + end + + function opts.preview_fn(self, entry, status) + local preview_winid = status.layout.preview and status.layout.preview.winid + if get_bufnr(self) == nil then + set_bufnr(self, vim.api.nvim_win_get_buf(preview_winid)) + preview_window_id = preview_winid + end + + if opts.get_buffer_by_name and get_bufnr_by_bufname(self, opts.get_buffer_by_name(self, entry)) then + self.state.bufname = opts.get_buffer_by_name(self, entry) + self.state.bufnr = get_bufnr_by_bufname(self, self.state.bufname) + utils.win_set_buf_noautocmd(preview_winid, self.state.bufnr) + else + local bufnr = vim.api.nvim_create_buf(false, true) + set_bufnr(self, bufnr) + vim.api.nvim_buf_set_option(bufnr, "modifiable", true) + + vim.schedule(function() + if vim.api.nvim_buf_is_valid(bufnr) then + utils.win_set_buf_noautocmd(preview_winid, bufnr) + end + end) + + vim.api.nvim_win_set_option(preview_winid, "winhl", "Normal:TelescopePreviewNormal") + vim.api.nvim_win_set_option(preview_winid, "signcolumn", "no") + vim.api.nvim_win_set_option(preview_winid, "foldlevel", 100) + vim.api.nvim_win_set_option(preview_winid, "wrap", false) + vim.api.nvim_win_set_option(preview_winid, "scrollbind", false) + + self.state.winid = preview_winid + self.state.bufname = nil + end + + if opts.keep_last_buf then + global_state.set_global_key("last_preview_bufnr", self.state.bufnr) + end + + opts.define_preview(self, entry, status) + + vim.schedule(function() + if not self or not self.state or not self.state.bufnr then + return + end + + if vim.api.nvim_buf_is_valid(self.state.bufnr) then + vim.api.nvim_buf_call(self.state.bufnr, function() + vim.api.nvim_exec_autocmds("User", { + pattern = "TelescopePreviewerLoaded", + data = { + title = entry.preview_title, + bufname = self.state.bufname, + filetype = putils.filetype_detect(self.state.bufname or ""), + }, + }) + end) + end + end) + + if opts.get_buffer_by_name then + set_bufname(self, opts.get_buffer_by_name(self, entry)) + end + end + + if not opts.scroll_fn then + opts.scroll_fn = scroll_fn + end + + if not opts.scroll_horizontal_fn then + opts.scroll_horizontal_fn = scroll_horizontal_fn + end + + return Previewer:new(opts) +end + +previewers.cat = defaulter(function(opts) + opts = opts or {} + local cwd = opts.cwd or vim.loop.cwd() + return previewers.new_buffer_previewer { + title = "File Preview", + dyn_title = function(_, entry) + return Path:new(from_entry.path(entry, false, false)):normalize(cwd) + end, + + get_buffer_by_name = function(_, entry) + return from_entry.path(entry, false, false) + end, + + define_preview = function(self, entry) + local p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + conf.buffer_previewer_maker(p, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + file_encoding = opts.file_encoding, + }) + end, + } +end, {}) + +previewers.vimgrep = defaulter(function(opts) + opts = opts or {} + local cwd = opts.cwd or vim.loop.cwd() + + local jump_to_line = function(self, bufnr, entry) + pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns_previewer, 0, -1) + + if entry.lnum and entry.lnum > 0 then + local lnum, lnend = entry.lnum - 1, (entry.lnend or entry.lnum) - 1 + + local col, colend = 0, -1 + -- Both col delimiters should be provided for them to take effect. + -- This is to ensure that column range highlighting was opted in, as `col` + -- is already used to determine the buffer jump position elsewhere. + if entry.col and entry.colend then + col, colend = entry.col - 1, entry.colend - 1 + end + + for i = lnum, lnend do + pcall( + vim.api.nvim_buf_add_highlight, + bufnr, + ns_previewer, + "TelescopePreviewLine", + i, + i == lnum and col or 0, + i == lnend and colend or -1 + ) + end + + local middle_ln = math.floor(lnum + (lnend - lnum) / 2) + pcall(vim.api.nvim_win_set_cursor, self.state.winid, { middle_ln + 1, 0 }) + vim.api.nvim_buf_call(bufnr, function() + vim.cmd "norm! zz" + end) + end + end + + return previewers.new_buffer_previewer { + title = "Grep Preview", + dyn_title = function(_, entry) + return Path:new(from_entry.path(entry, false, false)):normalize(cwd) + end, + + get_buffer_by_name = function(_, entry) + return from_entry.path(entry, false, false) + end, + + define_preview = function(self, entry) + -- builtin.buffers: bypass path validation for terminal buffers that don't have appropriate path + local has_buftype = entry.bufnr + and vim.api.nvim_buf_is_valid(entry.bufnr) + and vim.api.nvim_buf_get_option(entry.bufnr, "buftype") ~= "" + or false + local p + if not has_buftype then + p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + end + + -- Workaround for unnamed buffer when using builtin.buffer + if entry.bufnr and (p == "[No Name]" or has_buftype) then + local lines = vim.api.nvim_buf_get_lines(entry.bufnr, 0, -1, false) + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines) + -- schedule so that the lines are actually there and can be jumped onto when we call jump_to_line + vim.schedule(function() + jump_to_line(self, self.state.bufnr, entry) + end) + else + conf.buffer_previewer_maker(p, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + callback = function(bufnr) + jump_to_line(self, bufnr, entry) + end, + file_encoding = opts.file_encoding, + }) + end + end, + } +end, {}) + +previewers.qflist = previewers.vimgrep + +previewers.ctags = defaulter(function(opts) + local determine_jump = function(entry) + if entry.scode then + return function(self) + -- un-escape / then escape required + -- special chars for vim.fn.search() + -- ] ~ * + local scode = entry.scode:gsub([[\/]], "/"):gsub("[%]~*]", function(x) + return "\\" .. x + end) + + pcall(vim.fn.matchdelete, self.state.hl_id, self.state.winid) + vim.cmd "keepjumps norm! gg" + vim.fn.search(scode, "W") + vim.cmd "norm! zz" + + self.state.hl_id = vim.fn.matchadd("TelescopePreviewMatch", scode) + end + else + return function(self, bufnr) + if self.state.last_set_bufnr then + pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, ns_previewer, 0, -1) + end + pcall(vim.api.nvim_buf_add_highlight, bufnr, ns_previewer, "TelescopePreviewMatch", entry.lnum - 1, 0, -1) + pcall(vim.api.nvim_win_set_cursor, self.state.winid, { entry.lnum, 0 }) + self.state.last_set_bufnr = bufnr + end + end + end + + return previewers.new_buffer_previewer { + title = "Tags Preview", + teardown = function(self) + if self.state and self.state.hl_id then + pcall(vim.fn.matchdelete, self.state.hl_id, self.state.hl_win) + self.state.hl_id = nil + elseif self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then + vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, ns_previewer, 0, -1) + end + end, + + get_buffer_by_name = function(_, entry) + return entry.filename + end, + + define_preview = function(self, entry) + conf.buffer_previewer_maker(entry.filename, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + callback = function(bufnr) + pcall(vim.api.nvim_buf_call, bufnr, function() + determine_jump(entry)(self, bufnr) + end) + end, + file_encoding = opts.file_encoding, + }) + end, + } +end, {}) + +previewers.builtin = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Grep Preview", + teardown = search_teardown, + + get_buffer_by_name = function(_, entry) + return entry.filename + end, + + define_preview = function(self, entry) + local module_name = vim.fn.fnamemodify(vim.fn.fnamemodify(entry.filename, ":h"), ":t") + local text + if entry.text:sub(1, #module_name) ~= module_name then + text = module_name .. "." .. entry.text + else + text = entry.text:gsub("_", ".", 1) + end + + conf.buffer_previewer_maker(entry.filename, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + callback = function(bufnr) + search_cb_jump(self, bufnr, text) + end, + file_encoding = opts.file_encoding, + }) + end, + } +end, {}) + +previewers.help = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Help Preview", + teardown = search_teardown, + + get_buffer_by_name = function(_, entry) + return entry.filename + end, + + define_preview = function(self, entry) + local query = entry.cmd + query = query:sub(2) + query = [[\V]] .. query + + conf.buffer_previewer_maker(entry.filename, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + callback = function(bufnr) + putils.highlighter(bufnr, "help", opts) + search_cb_jump(self, bufnr, query) + end, + file_encoding = opts.file_encoding, + }) + end, + } +end, {}) + +previewers.man = defaulter(function(opts) + local pager = utils.get_lazy_default(opts.PAGER, function() + return vim.fn.executable "col" == 1 and { "col", "-bx" } or { "cat" } + end) + return previewers.new_buffer_previewer { + title = "Man Preview", + get_buffer_by_name = function(_, entry) + return entry.value .. "/" .. entry.section + end, + + define_preview = function(self, entry) + local win_width = vim.api.nvim_win_get_width(self.state.winid) + putils.job_maker(vim.deepcopy(pager), self.state.bufnr, { + writer = { "man", entry.section, entry.value }, + env = { ["MANWIDTH"] = win_width, PATH = vim.env.PATH, MANPATH = vim.env.MANPATH }, + value = entry.value .. "/" .. entry.section, + bufname = self.state.bufname, + }) + putils.highlighter(self.state.bufnr, "man", opts) + end, + } +end) + +previewers.git_branch_log = defaulter(function(opts) + local highlight_buffer = function(bufnr, content) + for i = 1, #content do + local line = content[i] + local hstart, hend = line:find "[0-9a-fA-F]+" + if hstart then + if hend < #line then + pcall( + vim.api.nvim_buf_add_highlight, + bufnr, + ns_previewer, + "TelescopeResultsIdentifier", + i - 1, + hstart - 1, + hend + ) + end + end + local _, cstart = line:find "- %(" + if cstart then + local cend = string.find(line, "%) ") + if cend then + pcall( + vim.api.nvim_buf_add_highlight, + bufnr, + ns_previewer, + "TelescopeResultsConstant", + i - 1, + cstart - 1, + cend + ) + end + end + local dstart, _ = line:find " %(%d" + if dstart then + pcall( + vim.api.nvim_buf_add_highlight, + bufnr, + ns_previewer, + "TelescopeResultsSpecialComment", + i - 1, + dstart, + #line + ) + end + end + end + + return previewers.new_buffer_previewer { + title = "Git Branch Preview", + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + local cmd = git_command({ + "--no-pager", + "log", + "--graph", + "--pretty=format:%h -%d %s (%cr)", + "--abbrev-commit", + "--date=relative", + entry.value, + }, opts) + + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr, content) + if not content then + return + end + highlight_buffer(bufnr, content) + end, + }) + end, + } +end, {}) + +previewers.git_stash_diff = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Git Stash Preview", + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry, _) + local cmd = git_command({ "--no-pager", "stash", "show", "-p", entry.value }, opts) + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + putils.highlighter(bufnr, "diff", opts) + end + end, + }) + end, + } +end, {}) + +previewers.git_commit_diff_to_parent = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Git Diff to Parent Preview", + teardown = search_teardown, + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + local cmd = git_command({ "--no-pager", "diff", entry.value .. "^!" }, opts) + if opts.current_file then + table.insert(cmd, "--") + table.insert(cmd, opts.current_file) + end + + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + search_cb_jump(self, bufnr, opts.current_line) + putils.highlighter(bufnr, "diff", opts) + end + end, + }) + end, + } +end, {}) + +previewers.git_commit_diff_to_head = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Git Diff to Head Preview", + teardown = search_teardown, + + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + local cmd = git_command({ "--no-pager", "diff", "--cached", entry.value }, opts) + if opts.current_file then + table.insert(cmd, "--") + table.insert(cmd, opts.current_file) + end + + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + search_cb_jump(self, bufnr, opts.current_line) + putils.highlighter(bufnr, "diff", opts) + end + end, + }) + end, + } +end, {}) + +previewers.git_commit_diff_as_was = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Git Show Preview", + teardown = search_teardown, + + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + local cmd = git_command({ "--no-pager", "show" }, opts) + local cf = opts.current_file and Path:new(opts.current_file):make_relative(opts.cwd) + local value = cf and (entry.value .. ":" .. cf) or entry.value + local ft = cf and putils.filetype_detect(value) or "diff" + table.insert(cmd, value) + + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + search_cb_jump(self, bufnr, opts.current_line) + putils.highlighter(bufnr, ft, opts) + end + end, + }) + end, + } +end, {}) + +previewers.git_commit_message = defaulter(function(opts) + local hl_map = { + "TelescopeResultsIdentifier", + "TelescopePreviewUser", + "TelescopePreviewDate", + } + return previewers.new_buffer_previewer { + title = "Git Message", + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + local cmd = git_command({ "--no-pager", "log", "-n 1", entry.value }, opts) + + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr, content) + if not content then + return + end + for k, v in ipairs(hl_map) do + local _, s = content[k]:find "%s" + if s then + vim.api.nvim_buf_add_highlight(bufnr, ns_previewer, v, k - 1, s, #content[k]) + end + end + end, + }) + end, + } +end, {}) + +previewers.git_file_diff = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Git File Diff Preview", + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + if entry.status and (entry.status == "??" or entry.status == "A ") then + local p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + conf.buffer_previewer_maker(p, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + file_encoding = opts.file_encoding, + }) + else + local cmd = git_command({ "--no-pager", "diff", "HEAD", "--", entry.value }, opts) + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + putils.highlighter(bufnr, "diff", opts) + end + end, + }) + end + end, + } +end, {}) + +previewers.autocommands = defaulter(function(_) + return previewers.new_buffer_previewer { + title = "Autocommands Preview", + teardown = function(self) + if self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then + pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, ns_previewer, 0, -1) + end + end, + + get_buffer_by_name = function(_, entry) + return entry.value.group_name + end, + + define_preview = function(self, entry, status) + local results = vim.tbl_filter(function(x) + return x.value.group_name == entry.value.group_name + end, status.picker.finder.results) + + if self.state.last_set_bufnr then + pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, ns_previewer, 0, -1) + end + + local preview_winid = status.layout.preview and status.layout.preview.winid + + local selected_row = 0 + if self.state.bufname ~= entry.value.group_name then + local display = {} + table.insert(display, string.format(" augroup: %s - [ %d entries ]", entry.value.group_name, #results)) + -- TODO: calculate banner width/string in setup() + -- TODO: get column characters to be the same HL group as border + table.insert(display, string.rep("─", vim.fn.getwininfo(preview_winid)[1].width)) + + for idx, item in ipairs(results) do + if item == entry then + selected_row = idx + end + table.insert( + display, + string.format(" %-14s▏%-08s %s", item.value.event, item.value.pattern, item.value.command) + ) + end + + vim.api.nvim_buf_set_option(self.state.bufnr, "filetype", "vim") + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, display) + vim.api.nvim_buf_add_highlight(self.state.bufnr, 0, "TelescopeBorder", 1, 0, -1) + else + for idx, item in ipairs(results) do + if item == entry then + selected_row = idx + break + end + end + end + + vim.api.nvim_buf_add_highlight(self.state.bufnr, ns_previewer, "TelescopePreviewLine", selected_row + 1, 0, -1) + -- set the cursor position after self.state.bufnr is connected to the + -- preview window (which is scheduled in new_buffer_previewer) + vim.schedule(function() + pcall(vim.api.nvim_win_set_cursor, preview_winid, { selected_row, 0 }) + end) + + self.state.last_set_bufnr = self.state.bufnr + end, + } +end, {}) + +previewers.highlights = defaulter(function(_) + return previewers.new_buffer_previewer { + title = "Highlights Preview", + teardown = function(self) + if self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then + vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, ns_previewer, 0, -1) + end + end, + + get_buffer_by_name = function() + return "highlights" + end, + + define_preview = function(self, entry) + if not self.state.bufname then + local output = vim.split(vim.fn.execute "highlight", "\n") + local hl_groups = {} + for _, v in ipairs(output) do + if v ~= "" then + if v:sub(1, 1) == " " then + local part_of_old = v:match "%s+(.*)" + hl_groups[#hl_groups] = hl_groups[#hl_groups] .. part_of_old + else + table.insert(hl_groups, v) + end + end + end + + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, hl_groups) + for k, v in ipairs(hl_groups) do + local startPos = string.find(v, "xxx", 1, true) - 1 + local endPos = startPos + 3 + local hlgroup = string.match(v, "([^ ]*)%s+.*") + pcall(vim.api.nvim_buf_add_highlight, self.state.bufnr, 0, hlgroup, k - 1, startPos, endPos) + end + end + + vim.schedule(function() + vim.api.nvim_buf_call(self.state.bufnr, function() + vim.cmd "keepjumps norm! gg" + vim.fn.search(entry.value .. " ") + local lnum = vim.api.nvim_win_get_cursor(self.state.winid)[1] + -- That one is actually a match but its better to use it like that then matchadd + pcall(vim.api.nvim_buf_clear_namespace, self.state.bufnr, ns_previewer, 0, -1) + vim.api.nvim_buf_add_highlight( + self.state.bufnr, + ns_previewer, + "TelescopePreviewMatch", + lnum - 1, + 0, + #entry.value + ) + -- we need to zz after the highlighting otherwise highlighting doesnt work + vim.cmd "norm! zz" + end) + end) + end, + } +end, {}) + +previewers.pickers = defaulter(function(_) + local ns_telescope_multiselection = vim.api.nvim_create_namespace "telescope_mulitselection" + local get_row = function(picker, preview_height, index) + if picker.sorting_strategy == "ascending" then + return index - 1 + else + return preview_height - index + end + end + return previewers.new_buffer_previewer { + + dyn_title = function(_, entry) + if entry.value.default_text and entry.value.default_text ~= "" then + return string.format("%s ─ %s", entry.value.prompt_title, entry.value.default_text) + end + return entry.value.prompt_title + end, + + get_buffer_by_name = function(_, entry) + return tostring(entry.value.prompt_bufnr) + end, + + teardown = function(self) + if self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then + vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, ns_telescope_multiselection, 0, -1) + end + end, + + define_preview = function(self, entry) + vim.api.nvim_buf_call(self.state.bufnr, function() + local ns_telescope_entry = vim.api.nvim_create_namespace "telescope_entry" + local preview_height = vim.api.nvim_win_get_height(self.state.winid) + + if self.state.bufname then + return + end + + local picker = entry.value + -- prefill buffer to be able to set lines individually + local placeholder = utils.repeated_table(preview_height, "") + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, placeholder) + + for index = 1, math.min(preview_height, picker.manager:num_results()) do + local row = get_row(picker, preview_height, index) + local e = picker.manager:get_entry(index) + + local display, display_highlight + -- if-clause as otherwise function return values improperly unpacked + if type(e.display) == "function" then + display, display_highlight = e:display() + else + display = e.display + end + + vim.api.nvim_buf_set_lines(self.state.bufnr, row, row + 1, false, { display }) + + if display_highlight ~= nil then + for _, hl_block in ipairs(display_highlight) do + vim.api.nvim_buf_add_highlight( + self.state.bufnr, + ns_telescope_entry, + hl_block[2], + row, + hl_block[1][1], + hl_block[1][2] + ) + end + end + if picker._multi:is_selected(e) then + vim.api.nvim_buf_add_highlight( + self.state.bufnr, + ns_telescope_multiselection, + "TelescopeMultiSelection", + row, + 0, + -1 + ) + end + end + end) + end, + } +end, {}) + +previewers.display_content = defaulter(function(_) + return previewers.new_buffer_previewer { + define_preview = function(self, entry) + assert( + type(entry.preview_command) == "function", + "entry must provide a preview_command function which will put the content into the buffer" + ) + vim.api.nvim_buf_call(self.state.bufnr, function() + entry.preview_command(entry, self.state.bufnr) + end) + end, + } +end, {}) + +return previewers diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/init.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/init.lua new file mode 100644 index 00000000..cf998e2e --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/init.lua @@ -0,0 +1,319 @@ +---@tag telescope.previewers +---@config { ["module"] = "telescope.previewers" } + +---@brief [[ +--- Provides a Previewer table that has to be implemented by each previewer. +--- To achieve this, this module also provides two wrappers that abstract most +--- of the work and make it really easy to create new previewers. +--- - `previewers.new_termopen_previewer` +--- - `previewers.new_buffer_previewer` +--- +--- Furthermore, there are a collection of previewers already defined which +--- can be used for every picker, as long as the entries of the picker provide +--- the necessary fields. The more important ones are +--- - `previewers.cat` +--- - `previewers.vimgrep` +--- - `previewers.qflist` +--- - `previewers.vim_buffer_cat` +--- - `previewers.vim_buffer_vimgrep` +--- - `previewers.vim_buffer_qflist` +--- +--- Previewers can be disabled for any builtin or custom picker by doing +--- :Telescope find_files previewer=false +---@brief ]] + +local Previewer = require "telescope.previewers.previewer" +local term_previewer = require "telescope.previewers.term_previewer" +local buffer_previewer = require "telescope.previewers.buffer_previewer" + +local previewers = {} + +--- This is the base table all previewers have to implement. It's possible to +--- write a wrapper for this because most previewers need to have the same +--- keys set. +--- Examples of wrappers are: +--- - `new_buffer_previewer` +--- - `new_termopen_previewer` +--- +--- To create a new table do following: +--- - `local new_previewer = Previewer:new(opts)` +--- +--- What `:new` expects is listed below +--- +--- The interface provides the following set of functions. All of them, besides +--- `new`, will be handled by telescope pickers. +--- - `:new(opts)` +--- - `:preview(entry, status)` +--- - `:teardown()` +--- - `:send_input(input)` +--- - `:scroll_fn(direction)` +--- - `:scroll_horizontal_fn(direction)` +--- +--- `Previewer:new()` expects a table as input with following keys: +--- - `setup` function(self): Will be called the first time preview will be +--- called. +--- - `teardown` function(self): Will be called on clean up. +--- - `preview_fn` function(self, entry, status): Will be called each time +--- a new entry was selected. +--- - `title` function(self): Will return the static title of the previewer. +--- - `dynamic_title` function(self, entry): Will return the dynamic title of +--- the previewer. Will only be called +--- when config value dynamic_preview_title +--- is true. +--- - `send_input` function(self, input): This is meant for +--- `termopen_previewer` and it can be +--- used to send input to the terminal +--- application, like less. +--- - `scroll_fn` function(self, direction): Used to make scrolling work. +--- - `scroll_horizontal_fn` function(self, direction): Used to make +--- horizontal scrolling work. +previewers.Previewer = Previewer + +--- A shorthand for creating a new Previewer. +--- The provided table will be forwarded to `Previewer:new(...)` +previewers.new = function(...) + return Previewer:new(...) +end + +--- Is a wrapper around Previewer and helps with creating a new +--- `termopen_previewer`. +--- +--- It requires you to specify one table entry `get_command(entry, status)`. +--- This `get_command` function has to return the terminal command that will be +--- executed for each entry. Example: +--- +--- get_command = function(entry, status) +--- return { 'bat', entry.path } +--- end +--- +--- +--- Additionally you can define: +--- - `title` a static title for example "File Preview" +--- - `dyn_title(self, entry)` a dynamic title function which gets called +--- when config value `dynamic_preview_title = true` +--- +--- It's an easy way to get your first previewer going and it integrates well +--- with `bat` and `less`. Providing out of the box scrolling if the command +--- uses less. +--- +--- Furthermore, it will forward all `config.set_env` environment variables to +--- that terminal process. +previewers.new_termopen_previewer = term_previewer.new_termopen_previewer + +--- Provides a `termopen_previewer` which has the ability to display files. +--- It will always show the top of the file and has support for +--- `bat`(prioritized) and `cat`. Each entry has to provide either the field +--- `path` or `filename` in order to make this previewer work. +--- +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.cat_previewer` +--- This will respect user configuration and will use `buffer_previewers` in +--- case it's configured that way. +previewers.cat = term_previewer.cat + +--- Provides a `termopen_previewer` which has the ability to display files at +--- the provided line. It has support for `bat`(prioritized) and `cat`. +--- Each entry has to provide either the field `path` or `filename` and +--- a `lnum` field in order to make this previewer work. +--- +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.grep_previewer` +--- This will respect user configuration and will use `buffer_previewers` in +--- case it's configured that way. +previewers.vimgrep = term_previewer.vimgrep + +--- Provides a `termopen_previewer` which has the ability to display files at +--- the provided line or range. It has support for `bat`(prioritized) and +--- `cat`. Each entry has to provide either the field `path` or `filename`, +--- `lnum` and a `start` and `finish` range in order to make this previewer +--- work. +--- +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.qflist_previewer` +--- This will respect user configuration and will use buffer previewers in +--- case it's configured that way. +previewers.qflist = term_previewer.qflist + +--- An interface to instantiate a new `buffer_previewer`. +--- That means that the content actually lives inside a vim buffer which +--- enables us more control over the actual content. For example, we can +--- use `vim.fn.search` to jump to a specific line or reuse buffers/already +--- opened files more easily. +--- This interface is more complex than `termopen_previewer` but offers more +--- flexibility over your content. +--- It was designed to display files but was extended to also display the +--- output of terminal commands. +--- +--- In the following options, state table and general tips are mentioned to +--- make your experience with this previewer more seamless. +--- +--- +--- options: +--- - `define_preview = function(self, entry, status)` (required) +--- Is called for each selected entry, after each selection_move +--- (up or down) and is meant to handle things like reading file, +--- jump to line or attach a highlighter. +--- - `setup = function(self)` (optional) +--- Is called once at the beginning, before the preview for the first +--- entry is displayed. You can return a table of vars that will be +--- available in `self.state` in each `define_preview` call. +--- - `teardown = function(self)` (optional) +--- Will be called at the end, when the picker is being closed and is +--- meant to clean up everything that was allocated by the previewer. +--- The `buffer_previewer` will automatically clean up all created buffers. +--- So you only need to handle things that were introduced by you. +--- - `keep_last_buf = true` (optional) +--- Will not delete the last selected buffer. This would allow you to +--- reuse that buffer in the select action. For example, that buffer can +--- be opened in a new split, rather than recreating that buffer in +--- an action. To access the last buffer number: +--- `require('telescope.state').get_global_key("last_preview_bufnr")` +--- - `get_buffer_by_name = function(self, entry)` +--- Allows you to set a unique name for each buffer. This is used for +--- caching purposes. `self.state.bufname` will be nil if the entry was +--- never loaded or the unique name when it was loaded once. For example, +--- useful if you have one file but multiple entries. This happens for grep +--- and lsp builtins. So to make the cache work only load content if +--- `self.state.bufname ~= entry.your_unique_key` +--- - `title` a static title for example "File Preview" +--- - `dyn_title(self, entry)` a dynamic title function which gets called +--- when config value `dynamic_preview_title = true` +--- +--- `self.state` table: +--- - `self.state.bufnr` +--- Is the current buffer number, in which you have to write the loaded +--- content. +--- Don't create a buffer yourself, otherwise it's not managed by the +--- buffer_previewer interface and you will probably be better off +--- writing your own interface. +--- - self.state.winid +--- Current window id. Useful if you want to set the cursor to a provided +--- line number. +--- - self.state.bufname +--- Will return the current buffer name, if `get_buffer_by_name` is +--- defined. nil will be returned if the entry was never loaded or when +--- `get_buffer_by_name` is not set. +--- +--- Tips: +--- - If you want to display content of a terminal job, use: +--- `require('telescope.previewers.utils').job_maker(cmd, bufnr, opts)` +--- - `cmd` table: for example { 'git', 'diff', entry.value } +--- - `bufnr` number: in which the content will be written +--- - `opts` table: with following keys +--- - `bufname` string: used for cache +--- - `value` string: used for cache +--- - `mode` string: either "insert" or "append". "insert" is default +--- - `env` table: define environment variables. Example: +--- - `{ ['PAGER'] = '', ['MANWIDTH'] = 50 }` +--- - `cwd` string: define current working directory for job +--- - `callback` function(bufnr, content): will be called when job +--- is done. Content will be nil if job is already loaded. +--- So you can do highlighting only the first time the previewer +--- is created for that entry. +--- Use the returned `bufnr` and not `self.state.bufnr` in callback, +--- because state can already be changed at this point in time. +--- - If you want to attach a highlighter use: +--- - `require('telescope.previewers.utils').highlighter(bufnr, ft)` +--- - This will prioritize tree sitter highlighting if available for +--- environment and language. +--- - `require('telescope.previewers.utils').regex_highlighter(bufnr, ft)` +--- - `require('telescope.previewers.utils').ts_highlighter(bufnr, ft)` +--- - If you want to use `vim.fn.search` or similar you need to run it in +--- that specific buffer context. Do +--- +--- vim.api.nvim_buf_call(bufnr, function() +--- -- for example `search` and `matchadd` +--- end) +--- +--- to achieve that. +--- - If you want to read a file into the buffer it's best to use +--- `buffer_previewer_maker`. But access this function with +--- `require('telescope.config').values.buffer_previewer_maker` +--- because it can be redefined by users. +previewers.new_buffer_previewer = buffer_previewer.new_buffer_previewer + +--- A universal way of reading a file into a buffer previewer. +--- It handles async reading, cache, highlighting, displaying directories +--- and provides a callback which can be used, to jump to a line in the buffer. +---@param filepath string: String to the filepath, will be expanded +---@param bufnr number: Where the content will be written +---@param opts table: keys: `use_ft_detect`, `bufname` and `callback` +previewers.buffer_previewer_maker = buffer_previewer.file_maker + +--- A previewer that is used to display a file. It uses the `buffer_previewer` +--- interface and won't jump to the line. To integrate this one into your +--- own picker make sure that the field `path` or `filename` is set for +--- each entry. +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.file_previewer` +--- This will respect user configuration and will use `termopen_previewer` in +--- case it's configured that way. +previewers.vim_buffer_cat = buffer_previewer.cat + +--- A previewer that is used to display a file and jump to the provided line. +--- It uses the `buffer_previewer` interface. To integrate this one into your +--- own picker make sure that the field `path` or `filename` and `lnum` is set +--- in each entry. If the latter is not present, it will default to the first +--- line. Additionally, `lnend`, `col` and `colend` can be set to highlight a +--- text range instead of a single line. All line/column values are 1-indexed. +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.grep_previewer` +--- This will respect user configuration and will use `termopen_previewer` in +--- case it's configured that way. +previewers.vim_buffer_vimgrep = buffer_previewer.vimgrep + +--- Is the same as `vim_buffer_vimgrep` and only exists for consistency with +--- `term_previewers`. +--- +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.qflist_previewer` +--- This will respect user configuration and will use `termopen_previewer` in +--- case it's configured that way. +previewers.vim_buffer_qflist = buffer_previewer.qflist + +--- A previewer that shows a log of a branch as graph +previewers.git_branch_log = buffer_previewer.git_branch_log + +--- A previewer that shows a diff of a stash +previewers.git_stash_diff = buffer_previewer.git_stash_diff + +--- A previewer that shows a diff of a commit to a parent commit.
+--- The run command is `git --no-pager diff SHA^! -- $CURRENT_FILE` +--- +--- The current file part is optional. So is only uses it with bcommits. +previewers.git_commit_diff_to_parent = buffer_previewer.git_commit_diff_to_parent + +--- A previewer that shows a diff of a commit to head.
+--- The run command is `git --no-pager diff --cached $SHA -- $CURRENT_FILE` +--- +--- The current file part is optional. So is only uses it with bcommits. +previewers.git_commit_diff_to_head = buffer_previewer.git_commit_diff_to_head + +--- A previewer that shows a diff of a commit as it was.
+--- The run command is `git --no-pager show $SHA:$CURRENT_FILE` or `git --no-pager show $SHA` +previewers.git_commit_diff_as_was = buffer_previewer.git_commit_diff_as_was + +--- A previewer that shows the commit message of a diff.
+--- The run command is `git --no-pager log -n 1 $SHA` +previewers.git_commit_message = buffer_previewer.git_commit_message + +--- A previewer that shows the current diff of a file. Used in git_status.
+--- The run command is `git --no-pager diff $FILE` +previewers.git_file_diff = buffer_previewer.git_file_diff + +previewers.ctags = buffer_previewer.ctags +previewers.builtin = buffer_previewer.builtin +previewers.help = buffer_previewer.help +previewers.man = buffer_previewer.man +previewers.autocommands = buffer_previewer.autocommands +previewers.highlights = buffer_previewer.highlights +previewers.pickers = buffer_previewer.pickers + +--- A deprecated way of displaying content more easily. Was written at a time, +--- where the buffer_previewer interface wasn't present. Nowadays it's easier +--- to just use this. We will keep it around for backwards compatibility +--- because some extensions use it. +--- It doesn't use cache or some other clever tricks. +previewers.display_content = buffer_previewer.display_content + +return previewers diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/previewer.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/previewer.lua new file mode 100644 index 00000000..a02d5bf3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/previewer.lua @@ -0,0 +1,107 @@ +local utils = require "telescope.utils" + +local Previewer = {} +Previewer.__index = Previewer + +local force_function_wrap = function(value) + if value ~= nil then + if type(value) ~= "function" then + return function() + return tostring(value) + end + else + return value + end + end +end + +function Previewer:new(opts) + opts = opts or {} + + return setmetatable({ + state = nil, + _title_fn = force_function_wrap(opts.title), + _dyn_title_fn = force_function_wrap(opts.dyn_title), + _setup_func = opts.setup, + _teardown_func = opts.teardown, + _send_input = opts.send_input, + _scroll_fn = opts.scroll_fn, + _scroll_horizontal_fn = opts.scroll_horizontal_fn, + preview_fn = opts.preview_fn, + _empty_bufnr = nil, + }, Previewer) +end + +function Previewer:preview(entry, status) + if not entry then + if not self._empty_bufnr then + self._empty_bufnr = vim.api.nvim_create_buf(false, true) + end + + if vim.api.nvim_buf_is_valid(self._empty_bufnr) then + vim.api.nvim_win_set_buf(status.layout.preview.winid, self._empty_bufnr) + end + return + end + + if not self.state then + if self._setup_func then + self.state = self:_setup_func(status) + else + self.state = {} + end + end + + return self:preview_fn(entry, status) +end + +function Previewer:title(entry, dynamic) + if dynamic == true and self._dyn_title_fn ~= nil then + if entry == nil then + if self._title_fn ~= nil then + return self:_title_fn() + else + return "" + end + end + return self:_dyn_title_fn(entry) + end + if self._title_fn ~= nil then + return self:_title_fn() + end +end + +function Previewer:teardown() + if self._empty_bufnr then + utils.buf_delete(self._empty_bufnr) + end + if self._teardown_func then + self:_teardown_func() + end +end + +function Previewer:send_input(input) + if self._send_input then + self:_send_input(input) + else + vim.api.nvim_err_writeln "send_input is not defined for this previewer" + end +end + +function Previewer:scroll_fn(direction) + if self._scroll_fn then + self:_scroll_fn(direction) + else + vim.api.nvim_err_writeln "scroll_fn is not defined for this previewer" + end +end + +function Previewer:scroll_horizontal_fn(direction) + if self._scroll_horizontal_fn then + self:_scroll_horizontal_fn(direction) + else + vim.api.nvim_err_writeln "scroll_horizontal_fn is not defined for this previewer" + end +end + +return Previewer diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/term_previewer.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/term_previewer.lua new file mode 100644 index 00000000..8b4bae65 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/term_previewer.lua @@ -0,0 +1,344 @@ +local conf = require("telescope.config").values +local utils = require "telescope.utils" +local Path = require "plenary.path" +local from_entry = require "telescope.from_entry" +local Previewer = require "telescope.previewers.previewer" +local putil = require "telescope.previewers.utils" + +local defaulter = utils.make_default_callable + +local previewers = {} + +-- TODO: Should play with these some more, ty @clason +local bat_options = { "--style=plain", "--color=always", "--paging=always" } +local has_less = (vim.fn.executable "less" == 1) and conf.use_less + +local get_file_stat = function(filename) + return vim.loop.fs_stat(utils.path_expand(filename)) or {} +end + +local list_dir = (function() + if vim.fn.has "win32" == 1 then + return function(dirname) + return { "cmd.exe", "/c", "dir", utils.path_expand(dirname) } + end + else + return function(dirname) + return { "ls", "-la", utils.path_expand(dirname) } + end + end +end)() + +local bat_maker = function(filename, lnum, start, finish) + if get_file_stat(filename).type == "directory" then + return list_dir(filename) + end + + local command = { "bat" } + + if lnum then + table.insert(command, { "--highlight-line", lnum }) + end + + if has_less then + if start then + table.insert(command, { "--pager", string.format("less -RS +%s", start) }) + else + table.insert(command, { "--pager", "less -RS" }) + end + else + if start and finish then + table.insert(command, { "-r", string.format("%s:%s", start, finish) }) + end + end + + return utils.flatten { + command, + bat_options, + "--", + utils.path_expand(filename), + } +end + +local cat_maker = function(filename, _, start, _) + if get_file_stat(filename).type == "directory" then + return list_dir(filename) + end + + if 1 == vim.fn.executable "file" then + local mime_type = utils.get_os_command_output({ "file", "--mime-type", "-b", filename })[1] + if putil.binary_mime_type(mime_type) then + return { "echo", "Binary file found. These files cannot be displayed!" } + end + end + + if has_less then + if start then + return { "less", "-RS", string.format("+%s", start), utils.path_expand(filename) } + else + return { "less", "-RS", utils.path_expand(filename) } + end + else + return { + "cat", + "--", + utils.path_expand(filename), + } + end +end + +local get_maker = function(opts) + local maker = opts.maker + if not maker and 1 == vim.fn.executable "bat" then + maker = bat_maker + elseif not maker and 1 == vim.fn.executable "cat" then + maker = cat_maker + end + + if not maker then + error "Needs maker" + end + + return maker +end + +-- TODO: We shoudl make sure that all our terminals close all the way. +-- Otherwise it could be bad if they're just sitting around, waiting to be closed. +-- I don't think that's the problem, but it could be? +previewers.new_termopen_previewer = function(opts) + opts = opts or {} + + assert(opts.get_command, "get_command is a required function") + assert(not opts.preview_fn, "preview_fn not allowed") + + local opt_setup = opts.setup + local opt_teardown = opts.teardown + + local old_bufs = {} + local bufentry_table = {} + local term_ids = {} + + local function get_term_id(self) + if self.state then + return self.state.termopen_id + end + end + + local function get_bufnr(self) + if self.state then + return self.state.termopen_bufnr + end + end + + local function set_term_id(self, value) + if self.state and term_ids[self.state.termopen_bufnr] == nil then + term_ids[self.state.termopen_bufnr] = value + self.state.termopen_id = value + end + end + + local function set_bufnr(self, value) + if get_bufnr(self) then + table.insert(old_bufs, get_bufnr(self)) + end + if self.state then + self.state.termopen_bufnr = value + end + end + + local function get_bufnr_by_bufentry(self, value) + if self.state then + return bufentry_table[value] + end + end + + local function set_bufentry(self, value) + if self.state and value then + bufentry_table[value] = get_bufnr(self) + end + end + + function opts.setup(self) + local state = {} + if opt_setup then + vim.tbl_deep_extend("force", state, opt_setup(self)) + end + return state + end + + function opts.teardown(self) + if opt_teardown then + opt_teardown(self) + end + + set_bufnr(self, nil) + set_bufentry(self, nil) + + for _, bufnr in ipairs(old_bufs) do + local term_id = term_ids[bufnr] + if term_id and utils.job_is_running(term_id) then + vim.fn.jobstop(term_id) + end + utils.buf_delete(bufnr) + end + bufentry_table = {} + end + + function opts.preview_fn(self, entry, status) + local preview_winid = status.layout.preview and status.layout.preview.winid + if get_bufnr(self) == nil then + set_bufnr(self, vim.api.nvim_win_get_buf(preview_winid)) + end + + local prev_bufnr = get_bufnr_by_bufentry(self, entry) + if prev_bufnr then + self.state.termopen_bufnr = prev_bufnr + utils.win_set_buf_noautocmd(preview_winid, self.state.termopen_bufnr) + self.state.termopen_id = term_ids[self.state.termopen_bufnr] + else + local bufnr = vim.api.nvim_create_buf(false, true) + set_bufnr(self, bufnr) + utils.win_set_buf_noautocmd(preview_winid, bufnr) + + local term_opts = { + cwd = opts.cwd or vim.loop.cwd(), + env = conf.set_env, + } + + local cmd = opts.get_command(entry, status) + if cmd then + vim.api.nvim_buf_call(bufnr, function() + set_term_id(self, vim.fn.termopen(cmd, term_opts)) + end) + end + set_bufentry(self, entry) + end + end + + if not opts.send_input then + function opts.send_input(self, input) + local termcode = vim.api.nvim_replace_termcodes(input, true, false, true) + + local term_id = get_term_id(self) + if term_id then + if not utils.job_is_running(term_id) then + return + end + + vim.fn.chansend(term_id, termcode) + end + end + end + + if not opts.scroll_fn then + function opts.scroll_fn(self, direction) + if not self.state then + return + end + + local input = direction > 0 and "d" or "u" + local count = math.abs(direction) + + self:send_input(count .. input) + end + end + + return Previewer:new(opts) +end + +previewers.cat = defaulter(function(opts) + opts = opts or {} + + local maker = get_maker(opts) + local cwd = opts.cwd or vim.loop.cwd() + + return previewers.new_termopen_previewer { + title = "File Preview", + dyn_title = function(_, entry) + return Path:new(from_entry.path(entry, false, false)):normalize(cwd) + end, + + get_command = function(entry) + local p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + + return maker(p) + end, + } +end, {}) + +previewers.vimgrep = defaulter(function(opts) + opts = opts or {} + + local maker = get_maker(opts) + local cwd = opts.cwd or vim.loop.cwd() + + return previewers.new_termopen_previewer { + title = "Grep Preview", + dyn_title = function(_, entry) + return Path:new(from_entry.path(entry, false, false)):normalize(cwd) + end, + + get_command = function(entry, status) + local win_id = status.layout.preview and status.layout.preview.winid + local height = vim.api.nvim_win_get_height(win_id) + + local p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + if entry.bufnr and (p == "[No Name]" or vim.api.nvim_buf_get_option(entry.bufnr, "buftype") ~= "") then + return + end + + local lnum = entry.lnum or 0 + + local context = math.floor(height / 2) + local start = math.max(0, lnum - context) + local finish = lnum + context + + return maker(p, lnum, start, finish) + end, + } +end, {}) + +previewers.qflist = defaulter(function(opts) + opts = opts or {} + + local maker = get_maker(opts) + local cwd = opts.cwd or vim.loop.cwd() + + return previewers.new_termopen_previewer { + title = "Grep Preview", + dyn_title = function(_, entry) + return Path:new(from_entry.path(entry, false, false)):normalize(cwd) + end, + + get_command = function(entry, status) + local win_id = status.layout.preview and status.layout.preview.winid + local height = vim.api.nvim_win_get_height(win_id) + + local p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + local lnum = entry.lnum + + local start, finish + if entry.start and entry.finish then + start = entry.start + finish = entry.finish + else + local context = math.floor(height / 2) + start = math.max(0, lnum - context) + finish = lnum + context + end + + return maker(p, lnum, start, finish) + end, + } +end, {}) + +return previewers diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/utils.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/utils.lua new file mode 100644 index 00000000..076bab43 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/previewers/utils.lua @@ -0,0 +1,242 @@ +local ts_utils = require "telescope.utils" +local strings = require "plenary.strings" +local conf = require("telescope.config").values + +local Job = require "plenary.job" +local Path = require "plenary.path" + +local utils = {} + +local detect_from_shebang = function(p) + local s = p:readbyterange(0, 256) + if s then + local lines = vim.split(s, "\n") + return vim.filetype.match { contents = lines } + end +end + +local parse_modeline = function(tail) + if tail:find "vim:" then + return tail:match ".*:ft=([^: ]*):.*$" or "" + end +end + +local detect_from_modeline = function(p) + local s = p:readbyterange(-256, 256) + if s then + local lines = vim.split(s, "\n") + local idx = lines[#lines] ~= "" and #lines or #lines - 1 + if idx >= 1 then + return parse_modeline(lines[idx]) + end + end +end + +utils.filetype_detect = function(filepath) + if type(filepath) ~= string then + filepath = tostring(filepath) + end + + local match = vim.filetype.match { filename = filepath } + if match and match ~= "" then + return match + end + + local p = Path:new(filepath) + if p and p:is_file() then + match = detect_from_shebang(p) + if match and match ~= "" then + return match + end + + match = detect_from_modeline(p) + if match and match ~= "" then + return match + end + end +end + +-- API helper functions for buffer previewer +--- Job maker for buffer previewer +utils.job_maker = function(cmd, bufnr, opts) + opts = opts or {} + opts.mode = opts.mode or "insert" + -- bufname and value are optional + -- if passed, they will be use as the cache key + -- if any of them are missing, cache will be skipped + if opts.bufname ~= opts.value or not opts.bufname or not opts.value then + local command = table.remove(cmd, 1) + local writer = (function() + if opts.writer ~= nil then + local wcommand = table.remove(opts.writer, 1) + return Job:new { + command = wcommand, + args = opts.writer, + env = opts.env, + cwd = opts.cwd, + } + end + end)() + + Job:new({ + command = command, + args = cmd, + env = opts.env, + cwd = opts.cwd, + writer = writer, + on_exit = vim.schedule_wrap(function(j) + if not vim.api.nvim_buf_is_valid(bufnr) then + return + end + if opts.mode == "append" then + vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, j:result()) + elseif opts.mode == "insert" then + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, j:result()) + end + if opts.callback then + opts.callback(bufnr, j:result()) + end + end), + }):start() + else + if opts.callback then + opts.callback(bufnr) + end + end +end + +local function has_filetype(ft) + return ft and ft ~= "" +end + +--- Attach default highlighter which will choose between regex and ts +utils.highlighter = function(bufnr, ft, opts) + opts = vim.F.if_nil(opts, {}) + opts.preview = vim.F.if_nil(opts.preview, {}) + opts.preview.treesitter = (function() + if type(opts.preview) == "table" and opts.preview.treesitter then + return opts.preview.treesitter + end + if type(conf.preview) == "table" and conf.preview.treesitter then + return conf.preview.treesitter + end + if type(conf.preview) == "boolean" then + return conf.preview + end + -- We should never get here + return false + end)() + + if type(opts.preview.treesitter) == "boolean" then + local temp = { enable = opts.preview.treesitter } + opts.preview.treesitter = temp + end + + local ts_highlighting = (function() + if type(opts.preview.treesitter.enable) == "table" then + if vim.tbl_contains(opts.preview.treesitter.enable, ft) then + return true + end + return false + end + + if vim.tbl_contains(vim.F.if_nil(opts.preview.treesitter.disable, {}), ft) then + return false + end + + return opts.preview.treesitter.enable == nil or opts.preview.treesitter.enable == true + end)() + + local ts_success + if ts_highlighting then + ts_success = utils.ts_highlighter(bufnr, ft) + end + if not ts_highlighting or ts_success == false then + utils.regex_highlighter(bufnr, ft) + end +end + +--- Attach regex highlighter +utils.regex_highlighter = function(bufnr, ft) + if has_filetype(ft) then + return pcall(vim.api.nvim_buf_set_option, bufnr, "syntax", ft) + end + return false +end + +-- Attach ts highlighter +utils.ts_highlighter = function(bufnr, ft) + if has_filetype(ft) then + local lang = vim.treesitter.language.get_lang(ft) or ft + if lang and ts_utils.has_ts_parser(lang) then + return vim.treesitter.start(bufnr, lang) + end + end + return false +end + +utils.set_preview_message = function(bufnr, winid, message, fillchar) + fillchar = vim.F.if_nil(fillchar, "╱") + local height = vim.api.nvim_win_get_height(winid) + local width = vim.api.nvim_win_get_width(winid) + vim.api.nvim_buf_set_lines( + bufnr, + 0, + -1, + false, + ts_utils.repeated_table(height, table.concat(ts_utils.repeated_table(width, fillchar), "")) + ) + local anon_ns = vim.api.nvim_create_namespace "" + local padding = table.concat(ts_utils.repeated_table(#message + 4, " "), "") + local formatted_message = " " .. message .. " " + -- Populate lines table based on height + local lines = {} + if height == 1 then + lines[1] = formatted_message + else + for i = 1, math.min(height, 3), 1 do + if i % 2 == 0 then + lines[i] = formatted_message + else + lines[i] = padding + end + end + end + vim.api.nvim_buf_set_extmark( + bufnr, + anon_ns, + 0, + 0, + { end_line = height, hl_group = "TelescopePreviewMessageFillchar" } + ) + local col = math.floor((width - strings.strdisplaywidth(formatted_message)) / 2) + for i, line in ipairs(lines) do + local line_pos = math.floor(height / 2) - 2 + i + vim.api.nvim_buf_set_extmark( + bufnr, + anon_ns, + math.max(line_pos, 0), + 0, + { virt_text = { { line, "TelescopePreviewMessage" } }, virt_text_pos = "overlay", virt_text_win_col = col } + ) + end +end + +--- Check if mime type is binary. +--- NOT an exhaustive check, may get false negatives. Ideally should check +--- filetype with `vim.filetype.match` or `filetype_detect` first for filetype +--- info. +---@param mime_type string +---@return boolean +utils.binary_mime_type = function(mime_type) + local type_, subtype = unpack(vim.split(mime_type, "/")) + if vim.tbl_contains({ "text", "inode" }, type_) then + return false + end + if vim.tbl_contains({ "json", "javascript" }, subtype) then + return false + end + return true +end + +return utils diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/sorters.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/sorters.lua new file mode 100644 index 00000000..c1b4b0f3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/sorters.lua @@ -0,0 +1,624 @@ +local log = require "telescope.log" +local util = require "telescope.utils" + +local sorters = {} + +local ngram_highlighter = function(ngram_len, prompt, display) + local highlights = {} + display = display:lower() + + for disp_index = 1, #display do + local char = display:sub(disp_index, disp_index + ngram_len - 1) + if prompt:find(char, 1, true) then + table.insert(highlights, { + start = disp_index, + finish = disp_index + ngram_len - 1, + }) + end + end + + return highlights +end + +local FILTERED = -1 + +local Sorter = {} +Sorter.__index = Sorter + +---@class Sorter +--- Sorter sorts a list of results by return a single integer for a line, +--- given a prompt +--- +--- Lower number is better (because it's like a closer match) +--- But, any number below 0 means you want that line filtered out. +---@field scoring_function function: Function that has the interface: (sorter, prompt, line): number +---@field tags table: Unique tags collected at filtering for tag completion +---@field filter_function function: Function that can filter results +---@field highlighter function: Highlights results to display them pretty +---@field discard boolean: Whether this is a discardable style sorter or not. +---@field score function: Override the score function if desired. +---@field init function: Function to run when creating sorter +---@field start function: Function to run on every new prompt +---@field finish function: Function to run after every new prompt +---@field destroy function: Functo to run when destroying sorter +function Sorter:new(opts) + opts = opts or {} + + return setmetatable({ + score = opts.score, + state = {}, + tags = opts.tags, + + -- State management + init = opts.init, + start = opts.start, + finish = opts.finish, + destroy = opts.destroy, + _status = nil, + + filter_function = opts.filter_function, + scoring_function = opts.scoring_function, + highlighter = opts.highlighter, + discard = opts.discard, + _discard_state = { + filtered = {}, + prompt = "", + }, + }, Sorter) +end + +function Sorter:_init() + self._status = "init" + if self.init then + self:init() + end +end + +function Sorter:_destroy() + self._status = "destroy" + if self.destroy then + self:destroy() + end +end + +-- TODO: We could make this a bit smarter and cache results "as we go" and where they got filtered. +-- Then when we hit backspace, we don't have to re-caculate everything. +-- Prime did a lot of the hard work already, but I don't want to copy as much memory around +-- as he did in his example. +-- Example can be found in ./scratch/prime_prompt_cache.lua +function Sorter:_start(prompt) + self._status = "start" + if self.start then + self:start(prompt) + end + + if not self.discard then + return + end + + local previous = self._discard_state.prompt + local len_previous = #previous + + if #prompt < len_previous then + log.trace "Reset discard because shorter prompt" + self._discard_state.filtered = {} + elseif string.sub(prompt, 1, len_previous) ~= previous then + log.trace "Reset discard no match" + self._discard_state.filtered = {} + end + + self._discard_state.prompt = prompt +end + +function Sorter:_finish(prompt) + self._status = "finish" + if self.finish then + self:finish(prompt) + end +end + +-- TODO: Consider doing something that makes it so we can skip the filter checks +-- if we're not discarding. Also, that means we don't have to check otherwise as well :) +function Sorter:score(prompt, entry, cb_add, cb_filter) + if not entry or not entry.ordinal then + return + end + + if self._status and self._status ~= "start" then + return + end + + local ordinal = entry.ordinal + if self:_was_discarded(prompt, ordinal) then + return cb_filter(entry) + end + + local filter_score + if self.filter_function ~= nil then + if self.tags then + self.tags:insert(entry) + end + filter_score, prompt = self:filter_function(prompt, entry, cb_add, cb_filter) + end + + if filter_score == FILTERED then + return cb_filter(entry) + end + + local score = self:scoring_function(prompt or "", ordinal, entry, cb_add, cb_filter) + if score == FILTERED then + self:_mark_discarded(prompt, ordinal) + return cb_filter(entry) + end + + if cb_add then + return cb_add(score, entry) + else + return score + end +end + +function Sorter:_was_discarded(prompt, ordinal) + return self.discard and self._discard_state.filtered[ordinal] +end + +function Sorter:_mark_discarded(prompt, ordinal) + if not self.discard then + return + end + + self._discard_state.filtered[ordinal] = true +end + +function sorters.new(...) + return Sorter:new(...) +end + +sorters.Sorter = Sorter + +local make_cached_tail = function() + local os_sep = util.get_separator() + local match_string = "[^" .. os_sep .. "]*$" + return setmetatable({}, { + __index = function(t, k) + local tail = string.match(k, match_string) + + rawset(t, k, tail) + return tail + end, + }) +end + +local make_cached_uppers = function() + return setmetatable({}, { + __index = function(t, k) + local obj = {} + for i = 1, #k do + local s_byte = k:byte(i, i) + if s_byte <= 90 and s_byte >= 65 then + obj[s_byte] = true + end + end + + rawset(t, k, obj) + return obj + end, + }) +end + +-- TODO: Match on upper case words +-- TODO: Match on last match +sorters.get_fuzzy_file = function(opts) + opts = opts or {} + + local ngram_len = opts.ngram_len or 2 + + local cached_ngrams = {} + + local function overlapping_ngrams(s, n) + if cached_ngrams[s] and cached_ngrams[s][n] then + return cached_ngrams[s][n] + end + + local R = {} + for i = 1, s:len() - n + 1 do + R[#R + 1] = s:sub(i, i + n - 1) + end + + if not cached_ngrams[s] then + cached_ngrams[s] = {} + end + + cached_ngrams[s][n] = R + + return R + end + + local cached_tails = make_cached_tail() + local cached_uppers = make_cached_uppers() + + return Sorter:new { + scoring_function = function(_, prompt, line) + local N = #prompt + + if N == 0 or N < ngram_len then + -- TODO: If the character is in the line, + -- then it should get a point or somethin. + return 1 + end + + local prompt_lower = prompt:lower() + local line_lower = line:lower() + + local prompt_lower_ngrams = overlapping_ngrams(prompt_lower, ngram_len) + + -- Contains the original string + local contains_string = line_lower:find(prompt_lower, 1, true) + + local prompt_uppers = cached_uppers[prompt] + local line_uppers = cached_uppers[line] + + local uppers_matching = 0 + for k, _ in pairs(prompt_uppers) do + if line_uppers[k] then + uppers_matching = uppers_matching + 1 + end + end + + -- TODO: Consider case senstivity + local tail = cached_tails[line_lower] + local contains_tail = tail:find(prompt, 1, true) + + local consecutive_matches = 0 + local previous_match_index = 0 + local match_count = 0 + + for i = 1, #prompt_lower_ngrams do + local match_start = line_lower:find(prompt_lower_ngrams[i], 1, true) + if match_start then + match_count = match_count + 1 + if match_start > previous_match_index then + consecutive_matches = consecutive_matches + 1 + end + + previous_match_index = match_start + end + end + + local tail_modifier = 1 + if contains_tail then + tail_modifier = 2 + end + + local denominator = ( + (10 * match_count / #prompt_lower_ngrams) + -- biases for shorter strings + + 3 * match_count * ngram_len / #line + + consecutive_matches + + N / (contains_string or (2 * #line)) + -- + 30/(c1 or 2*N) + -- TODO: It might be possible that this too strongly correlates, + -- but it's unlikely for people to type capital letters without actually + -- wanting to do something with a capital letter in it. + + uppers_matching + ) * tail_modifier + + if denominator == 0 or denominator ~= denominator then + return -1 + end + + if #prompt > 2 and denominator < 0.5 then + return -1 + end + + return 1 / denominator + end, + + highlighter = opts.highlighter or function(_, prompt, display) + return ngram_highlighter(ngram_len, prompt, display) + end, + } +end + +sorters.get_generic_fuzzy_sorter = function(opts) + opts = opts or {} + + local ngram_len = opts.ngram_len or 2 + + local cached_ngrams = {} + local function overlapping_ngrams(s, n) + if cached_ngrams[s] and cached_ngrams[s][n] then + return cached_ngrams[s][n] + end + + local R = {} + for i = 1, s:len() - n + 1 do + R[#R + 1] = s:sub(i, i + n - 1) + end + + if not cached_ngrams[s] then + cached_ngrams[s] = {} + end + + cached_ngrams[s][n] = R + + return R + end + + return Sorter:new { + -- self + -- prompt (which is the text on the line) + -- line (entry.ordinal) + -- entry (the whole entry) + scoring_function = function(_, prompt, line, _) + if prompt == 0 or #prompt < ngram_len then + return 1 + end + + local prompt_lower = prompt:lower() + local line_lower = line:lower() + + local prompt_ngrams = overlapping_ngrams(prompt_lower, ngram_len) + + local N = #prompt + + local contains_string = line_lower:find(prompt_lower, 1, true) + + local consecutive_matches = 0 + local previous_match_index = 0 + local match_count = 0 + + for i = 1, #prompt_ngrams do + local match_start = line_lower:find(prompt_ngrams[i], 1, true) + if match_start then + match_count = match_count + 1 + if match_start > previous_match_index then + consecutive_matches = consecutive_matches + 1 + end + + previous_match_index = match_start + end + end + + -- TODO: Copied from ashkan. + local denominator = ( + (10 * match_count / #prompt_ngrams) + -- biases for shorter strings + -- TODO(ashkan): this can bias towards repeated finds of the same + -- subpattern with overlapping_ngrams + + 3 * match_count * ngram_len / #line + + consecutive_matches + + N / (contains_string or (2 * #line)) -- + 30/(c1 or 2*N) + + ) + + if denominator == 0 or denominator ~= denominator then + return -1 + end + + if #prompt > 2 and denominator < 0.5 then + return -1 + end + + return 1 / denominator + end, + + highlighter = opts.highlighter or function(_, prompt, display) + return ngram_highlighter(ngram_len, prompt, display) + end, + } +end + +sorters.fuzzy_with_index_bias = function(opts) + opts = opts or {} + opts.ngram_len = 2 + + -- TODO: Probably could use a better sorter here. + local fuzzy_sorter = sorters.get_generic_fuzzy_sorter(opts) + + return Sorter:new { + scoring_function = function(_, prompt, line, entry, cb_add, cb_filter) + local base_score = fuzzy_sorter:scoring_function(prompt, line, cb_add, cb_filter) + + if base_score == FILTERED then + return FILTERED + end + + if not base_score or base_score == 0 then + return entry.index + else + return math.min(math.pow(entry.index, 0.25), 2) * base_score + end + end, + highlighter = fuzzy_sorter.highlighter, + } +end + +-- Sorter using the fzy algorithm +sorters.get_fzy_sorter = function(opts) + opts = opts or {} + local fzy = opts.fzy_mod or require "telescope.algos.fzy" + local OFFSET = -fzy.get_score_floor() + + return sorters.Sorter:new { + discard = true, + + scoring_function = function(_, prompt, line) + -- Check for actual matches before running the scoring alogrithm. + if not fzy.has_match(prompt, line) then + return -1 + end + + local fzy_score = fzy.score(prompt, line) + + -- The fzy score is -inf for empty queries and overlong strings. Since + -- this function converts all scores into the range (0, 1), we can + -- convert these to 1 as a suitable "worst score" value. + if fzy_score == fzy.get_score_min() then + return 1 + end + + -- Poor non-empty matches can also have negative values. Offset the score + -- so that all values are positive, then invert to match the + -- telescope.Sorter "smaller is better" convention. Note that for exact + -- matches, fzy returns +inf, which when inverted becomes 0. + return 1 / (fzy_score + OFFSET) + end, + + -- The fzy.positions function, which returns an array of string indices, is + -- compatible with telescope's conventions. It's moderately wasteful to + -- call call fzy.score(x,y) followed by fzy.positions(x,y): both call the + -- fzy.compute function, which does all the work. But, this doesn't affect + -- perceived performance. + highlighter = function(_, prompt, display) + return fzy.positions(prompt, display) + end, + } +end + +-- TODO: Could probably do something nice where we check their conf +-- and choose their default for this. +-- But I think `fzy` is good default for now. +sorters.highlighter_only = function(opts) + opts = opts or {} + local fzy = opts.fzy_mod or require "telescope.algos.fzy" + + return Sorter:new { + scoring_function = function() + return 1 + end, + + highlighter = function(_, prompt, display) + return fzy.positions(prompt, display) + end, + } +end + +sorters.empty = function() + return Sorter:new { + scoring_function = function() + return 1 + end, + } +end + +-- Bad & Dumb Sorter +sorters.get_levenshtein_sorter = function() + return Sorter:new { + scoring_function = function(_, prompt, line) + return require "telescope.algos.string_distance"(prompt, line) + end, + } +end + +local substr_highlighter = function(make_display) + return function(_, prompt, display) + local highlights = {} + display = make_display(prompt, display) + + local search_terms = util.max_split(prompt, "%s") + local hl_start, hl_end + + for _, word in pairs(search_terms) do + hl_start, hl_end = display:find(word, 1, true) + if hl_start then + table.insert(highlights, { start = hl_start, finish = hl_end }) + end + end + + return highlights + end +end + +sorters.get_substr_matcher = function() + local make_display = vim.o.smartcase + and function(prompt, display) + local has_upper_case = not not prompt:match "%u" + return has_upper_case and display or display:lower() + end + or function(_, display) + return display:lower() + end + + return Sorter:new { + highlighter = substr_highlighter(make_display), + scoring_function = function(_, prompt, _, entry) + if #prompt == 0 then + return 1 + end + + local display = make_display(prompt, entry.ordinal) + + local search_terms = util.max_split(prompt, "%s") + for _, word in pairs(search_terms) do + if not display:find(word, 1, true) then + return -1 + end + end + + return entry.index + end, + } +end + +local substr_matcher = function(_, prompt, line, _) + local display = line:lower() + local search_terms = util.max_split(prompt:lower(), "%s") + local matched = 0 + local total_search_terms = 0 + for _, word in pairs(search_terms) do + total_search_terms = total_search_terms + 1 + if display:find(word, 1, true) then + matched = matched + 1 + end + end + + return matched == total_search_terms and 0 or FILTERED +end + +local filter_function = function(opts) + local scoring_function = vim.F.if_nil(opts.filter_function, substr_matcher) + local tag = vim.F.if_nil(opts.tag, "ordinal") + + return function(_, prompt, entry) + local filter = "^(" .. opts.delimiter .. "(%S+)" .. "[" .. opts.delimiter .. "%s]" .. ")" + local matched = prompt:match(filter) + + if matched == nil then + return 0, prompt + end + -- clear prompt of tag + prompt = prompt:sub(#matched + 1, -1) + local query = vim.trim(matched:gsub(opts.delimiter, "")) + return scoring_function(_, query, entry[tag], _), prompt + end +end + +local function create_tag_set(tag) + tag = vim.F.if_nil(tag, "ordinal") + local set = {} + return setmetatable(set, { + __index = { + insert = function(set_, entry) + local value = entry[tag] + if not set_[value] then + set_[value] = true + end + end, + }, + }) +end + +sorters.prefilter = function(opts) + local sorter = opts.sorter + opts.delimiter = vim.F.if_nil(opts.delimiter, ":") + sorter._delimiter = opts.delimiter + sorter.tags = create_tag_set(opts.tag) + sorter.filter_function = filter_function(opts) + sorter._was_discarded = function() + return false + end + return sorter +end + +return sorters diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/state.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/state.lua new file mode 100644 index 00000000..a3e6d851 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/state.lua @@ -0,0 +1,39 @@ +local state = {} + +TelescopeGlobalState = TelescopeGlobalState or {} +TelescopeGlobalState.global = TelescopeGlobalState.global or {} + +--- Set the status for a particular prompt bufnr +function state.set_status(prompt_bufnr, status) + TelescopeGlobalState[prompt_bufnr] = status +end + +function state.set_global_key(key, value) + TelescopeGlobalState.global[key] = value +end + +function state.get_global_key(key) + return TelescopeGlobalState.global[key] +end + +function state.get_status(prompt_bufnr) + return TelescopeGlobalState[prompt_bufnr] or {} +end + +function state.clear_status(prompt_bufnr) + state.set_status(prompt_bufnr, nil) +end + +function state.get_existing_prompt_bufnrs() + local prompt_bufnrs = {} + + for key, _ in pairs(TelescopeGlobalState) do + if type(key) == "number" then + table.insert(prompt_bufnrs, key) + end + end + + return prompt_bufnrs +end + +return state diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/testharness/helpers.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/testharness/helpers.lua new file mode 100644 index 00000000..1f0b0b1d --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/testharness/helpers.lua @@ -0,0 +1,56 @@ +local test_helpers = {} + +test_helpers.get_picker = function() + local state = require "telescope.state" + return state.get_status(vim.api.nvim_get_current_buf()).picker +end + +test_helpers.get_results_bufnr = function() + local state = require "telescope.state" + return state.get_status(vim.api.nvim_get_current_buf()).layout.results.bufnr +end + +test_helpers.get_file = function() + return vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ":t") +end + +test_helpers.get_prompt = function() + return vim.api.nvim_buf_get_lines(0, 0, -1, false)[1] +end + +test_helpers.get_results = function() + return vim.api.nvim_buf_get_lines(test_helpers.get_results_bufnr(), 0, -1, false) +end + +test_helpers.get_best_result = function() + local results = test_helpers.get_results() + local picker = test_helpers.get_picker() + + if picker.sorting_strategy == "ascending" then + return results[1] + else + return results[#results] + end +end + +test_helpers.get_selection = function() + local state = require "telescope.state" + return state.get_global_key "selected_entry" +end + +test_helpers.get_selection_value = function() + return test_helpers.get_selection().value +end + +test_helpers.make_globals = function() + GetFile = test_helpers.get_file -- luacheck: globals GetFile + GetPrompt = test_helpers.get_prompt -- luacheck: globals GetPrompt + + GetResults = test_helpers.get_results -- luacheck: globals GetResults + GetBestResult = test_helpers.get_best_result -- luacheck: globals GetBestResult + + GetSelection = test_helpers.get_selection -- luacheck: globals GetSelection + GetSelectionValue = test_helpers.get_selection_value -- luacheck: globals GetSelectionValue +end + +return test_helpers diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/testharness/init.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/testharness/init.lua new file mode 100644 index 00000000..9a8e7073 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/testharness/init.lua @@ -0,0 +1,112 @@ +local assert = require "luassert" + +local Path = require "plenary.path" + +local tester = {} +tester.debug = false + +local get_results_from_contents = function(content) + local nvim = vim.fn.jobstart( + { "nvim", "--noplugin", "-u", "scripts/minimal_init.vim", "--headless", "--embed" }, + { rpc = true } + ) + + local result = vim.fn.rpcrequest(nvim, "nvim_exec_lua", content, {}) + assert.are.same(true, result[1], vim.inspect(result)) + + local count = 0 + while + vim.fn.rpcrequest(nvim, "nvim_exec_lua", "return require('telescope.testharness.runner').state.done", {}) ~= true + do + count = count + 1 + vim.wait(100) + + -- TODO: Could maybe wait longer, but it's annoying to wait if the test is going to timeout. + if count > 100 then + break + end + end + + local state = vim.fn.rpcrequest(nvim, "nvim_exec_lua", "return require('telescope.testharness.runner').state", {}) + vim.fn.jobstop(nvim) + + assert.are.same(true, state.done, vim.inspect(state)) + + local result_table = {} + for _, v in ipairs(state.results) do + table.insert(result_table, v) + end + + return result_table, state +end + +local check_results = function(results, state) + assert(state, "Must pass state") + + for _, v in ipairs(results) do + local assertion + if not v._type or v._type == "are" or v._type == "_default" then + assertion = assert.are.same + else + assertion = assert.are_not.same + end + + -- TODO: I think it would be nice to be able to see the state, + -- but it clutters up the test output so much here. + -- + -- So we would have to consider how to do that I think. + assertion(v.expected, v.actual, string.format("Test Case: %s // %s", v.location, v.case)) + end +end + +tester.run_string = function(contents) + contents = [[ + return (function() + local tester = require('telescope.testharness') + local runner = require('telescope.testharness.runner') + local helper = require('telescope.testharness.helpers') + helper.make_globals() + local ok, msg = pcall(function() + runner.log("Loading Test") + ]] .. contents .. [[ + end) + return {ok, msg or runner.state} + end)() + ]] + + check_results(get_results_from_contents(contents)) +end + +tester.run_file = function(filename) + local file = "./lua/tests/pickers/" .. filename .. ".lua" + local path = Path:new(file) + + if not path:exists() then + assert.are.same("", file) + end + + local contents = string.format( + [[ + return (function() + local runner = require('telescope.testharness.runner') + local helper = require('telescope.testharness.helpers') + helper.make_globals() + local ok, msg = pcall(function() + runner.log("Loading Test") + return loadfile("%s")() + end) + return {ok, msg or runner.state} + end)() + ]], + path:absolute() + ) + + check_results(get_results_from_contents(contents)) +end + +tester.not_ = function(val) + val._type = "are_not" + return val +end + +return tester diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/testharness/runner.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/testharness/runner.lua new file mode 100644 index 00000000..af1bf30b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/testharness/runner.lua @@ -0,0 +1,156 @@ +local builtin = require "telescope.builtin" + +local DELAY = vim.g.telescope_test_delay or 50 +local runner = {} + +-- State is test variable +runner.state = { + done = false, + results = {}, + msgs = {}, +} + +local writer = function(val) + table.insert(runner.state.results, val) +end + +local invalid_test_case = function(k) + error { case = k, expected = "", actual = k } +end + +local _VALID_KEYS = { + post_typed = true, + post_close = true, +} + +local replace_terms = function(input) + return vim.api.nvim_replace_termcodes(input, true, false, true) +end + +runner.nvim_feed = function(text, feed_opts) + feed_opts = feed_opts or "m" + + vim.api.nvim_feedkeys(text, feed_opts, true) +end + +local end_test_cases = function() + runner.state.done = true +end + +local execute_test_case = function(location, key, spec) + local ok, actual = pcall(spec[2]) + + if not ok then + writer { + location = "Error: " .. location, + case = key, + expected = "To succeed and return: " .. tostring(spec[1]), + actual = actual, + + _type = spec._type, + } + + end_test_cases() + else + writer { + location = location, + case = key, + expected = spec[1], + actual = actual, + + _type = spec._type, + } + end + + return ok +end + +runner.log = function(msg) + table.insert(runner.state.msgs, msg) +end + +runner.picker = function(picker_name, input, test_cases, opts) + opts = opts or {} + + for k, _ in pairs(test_cases) do + if not _VALID_KEYS[k] then + return invalid_test_case(k) + end + end + + opts.on_complete = { + runner.create_on_complete(input, test_cases), + } + + opts._on_error = function(self, msg) + runner.state.done = true + writer { + location = "Error while running on complete", + expected = "To Work", + actual = msg, + } + end + + runner.log "Starting picker" + builtin[picker_name](opts) + runner.log "Called picker" +end + +runner.create_on_complete = function(input, test_cases) + input = replace_terms(input) + + local actions = {} + for i = 1, #input do + local char = input:sub(i, i) + table.insert(actions, { + cb = function() + runner.log("Inserting char: " .. char) + runner.nvim_feed(char, "") + end, + char = char, + }) + end + + return function() + local action + + repeat + action = table.remove(actions, 1) + if action then + action.cb() + end + until not action or string.match(action.char, "%g") + + if #actions > 0 then + return + end + + vim.defer_fn(function() + if test_cases.post_typed then + for k, v in ipairs(test_cases.post_typed) do + if not execute_test_case("post_typed", k, v) then + return + end + end + end + + vim.defer_fn(function() + runner.nvim_feed(replace_terms "", "") + + vim.defer_fn(function() + if test_cases.post_close then + for k, v in ipairs(test_cases.post_close) do + if not execute_test_case("post_close", k, v) then + return + end + end + end + + vim.defer_fn(end_test_cases, DELAY) + end, DELAY) + end, DELAY) + end, DELAY) + end +end + +return runner diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/themes.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/themes.lua new file mode 100644 index 00000000..69d12e82 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/themes.lua @@ -0,0 +1,139 @@ +-- Prototype Theme System (WIP) +-- Currently certain designs need a number of parameters. +-- +-- local opts = themes.get_dropdown { winblend = 3 } + +---@tag telescope.themes +---@config { ["module"] = "telescope.themes" } + +---@brief [[ +--- Themes are ways to combine several elements of styling together. +--- +--- They are helpful for managing the several different UI aspects for telescope and provide +--- a simple interface for users to get a particular "style" of picker. +---@brief ]] + +local themes = {} + +--- Dropdown style theme. +--- +--- Usage: +--- +--- local opts = {...} -- picker options +--- local builtin = require('telescope.builtin') +--- local themes = require('telescope.themes') +--- builtin.find_files(themes.get_dropdown(opts)) +--- +function themes.get_dropdown(opts) + opts = opts or {} + + local theme_opts = { + theme = "dropdown", + + results_title = false, + + sorting_strategy = "ascending", + layout_strategy = "center", + layout_config = { + preview_cutoff = 1, -- Preview should always show (unless previewer = false) + + width = function(_, max_columns, _) + return math.min(max_columns, 80) + end, + + height = function(_, _, max_lines) + return math.min(max_lines, 15) + end, + }, + + border = true, + borderchars = { + prompt = { "─", "│", " ", "│", "╭", "╮", "│", "│" }, + results = { "─", "│", "─", "│", "├", "┤", "╯", "╰" }, + preview = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + }, + } + if opts.layout_config and opts.layout_config.prompt_position == "bottom" then + theme_opts.borderchars = { + prompt = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + results = { "─", "│", "─", "│", "╭", "╮", "┤", "├" }, + preview = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + } + end + + return vim.tbl_deep_extend("force", theme_opts, opts) +end + +--- Cursor style theme. +--- +--- Usage: +--- +--- local opts = {...} -- picker options +--- local builtin = require('telescope.builtin') +--- local themes = require('telescope.themes') +--- builtin.find_files(themes.get_cursor(opts)) +--- +function themes.get_cursor(opts) + opts = opts or {} + + local theme_opts = { + theme = "cursor", + + sorting_strategy = "ascending", + results_title = false, + layout_strategy = "cursor", + layout_config = { + width = 80, + height = 9, + }, + borderchars = { + prompt = { "─", "│", " ", "│", "╭", "╮", "│", "│" }, + results = { "─", "│", "─", "│", "├", "┤", "╯", "╰" }, + preview = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + }, + } + + return vim.tbl_deep_extend("force", theme_opts, opts) +end + +--- Ivy style theme. +--- +--- Usage: +--- +--- local opts = {...} -- picker options +--- local builtin = require('telescope.builtin') +--- local themes = require('telescope.themes') +--- builtin.find_files(themes.get_ivy(opts)) +--- +function themes.get_ivy(opts) + opts = opts or {} + + local theme_opts = { + theme = "ivy", + + sorting_strategy = "ascending", + + layout_strategy = "bottom_pane", + layout_config = { + height = 25, + }, + + border = true, + borderchars = { + prompt = { "─", " ", " ", " ", "─", "─", " ", " " }, + results = { " " }, + preview = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + }, + } + if opts.layout_config and opts.layout_config.prompt_position == "bottom" then + theme_opts.borderchars = { + prompt = { " ", " ", "─", " ", " ", " ", "─", "─" }, + results = { "─", " ", " ", " ", "─", "─", " ", " " }, + preview = { "─", " ", "─", "│", "┬", "─", "─", "╰" }, + } + end + + return vim.tbl_deep_extend("force", theme_opts, opts) +end + +return themes diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/utils.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/utils.lua new file mode 100644 index 00000000..c2ee27f2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/telescope/utils.lua @@ -0,0 +1,730 @@ +---@tag telescope.utils +---@config { ["module"] = "telescope.utils" } + +---@brief [[ +--- Utilities for writing telescope pickers +---@brief ]] + +local Path = require "plenary.path" +local Job = require "plenary.job" + +local log = require "telescope.log" + +local truncate = require("plenary.strings").truncate +local get_status = require("telescope.state").get_status + +local utils = {} + +utils.iswin = vim.loop.os_uname().sysname == "Windows_NT" + +--TODO(clason): Remove when dropping support for Nvim 0.9 +utils.islist = vim.fn.has "nvim-0.10" == 1 and vim.islist or vim.tbl_islist +local flatten = function(t) + return vim.iter(t):flatten():totable() +end +utils.flatten = vim.fn.has "nvim-0.11" == 1 and flatten or vim.tbl_flatten + +--- Hybrid of `vim.fn.expand()` and custom `vim.fs.normalize()` +--- +--- Paths starting with '%', '#' or '<' are expanded with `vim.fn.expand()`. +--- Otherwise avoids using `vim.fn.expand()` due to its overly aggressive +--- expansion behavior which can sometimes lead to errors or the creation of +--- non-existent paths when dealing with valid absolute paths. +--- +--- Other paths will have '~' and environment variables expanded. +--- Unlike `vim.fs.normalize()`, backslashes are preserved. This has better +--- compatibility with `plenary.path` and also avoids mangling valid Unix paths +--- with literal backslashes. +--- +--- Trailing slashes are trimmed. With the exception of root paths. +--- eg. `/` on Unix or `C:\` on Windows +--- +---@param path string +---@return string +utils.path_expand = function(path) + vim.validate { + path = { path, { "string" } }, + } + + if utils.is_uri(path) then + return path + end + + if path:match "^[%%#<]" then + path = vim.fn.expand(path) + end + + if path:sub(1, 1) == "~" then + local home = vim.loop.os_homedir() or "~" + if home:sub(-1) == "\\" or home:sub(-1) == "/" then + home = home:sub(1, -2) + end + path = home .. path:sub(2) + end + + path = path:gsub("%$([%w_]+)", vim.loop.os_getenv) + path = path:gsub("/+", "/") + if utils.iswin then + path = path:gsub("\\+", "\\") + if path:match "^%w:\\$" then + return path + else + return (path:gsub("(.)\\$", "%1")) + end + end + return (path:gsub("(.)/$", "%1")) +end + +utils.get_separator = function() + return Path.path.sep +end + +utils.cycle = function(i, n) + return i % n == 0 and n or i % n +end + +utils.get_lazy_default = function(x, defaulter, ...) + if x == nil then + return defaulter(...) + else + return x + end +end + +utils.repeated_table = function(n, val) + local empty_lines = {} + for _ = 1, n do + table.insert(empty_lines, val) + end + return empty_lines +end + +utils.filter_symbols = function(results, opts, post_filter) + local has_ignore = opts.ignore_symbols ~= nil + local has_symbols = opts.symbols ~= nil + local filtered_symbols + + if has_symbols and has_ignore then + utils.notify("filter_symbols", { + msg = "Either opts.symbols or opts.ignore_symbols, can't process opposing options at the same time!", + level = "ERROR", + }) + return + elseif not (has_ignore or has_symbols) then + return results + elseif has_ignore then + if type(opts.ignore_symbols) == "string" then + opts.ignore_symbols = { opts.ignore_symbols } + end + if type(opts.ignore_symbols) ~= "table" then + utils.notify("filter_symbols", { + msg = "Please pass ignore_symbols as either a string or a list of strings", + level = "ERROR", + }) + return + end + + opts.ignore_symbols = vim.tbl_map(string.lower, opts.ignore_symbols) + filtered_symbols = vim.tbl_filter(function(item) + return not vim.tbl_contains(opts.ignore_symbols, string.lower(item.kind)) + end, results) + elseif has_symbols then + if type(opts.symbols) == "string" then + opts.symbols = { opts.symbols } + end + if type(opts.symbols) ~= "table" then + utils.notify("filter_symbols", { + msg = "Please pass filtering symbols as either a string or a list of strings", + level = "ERROR", + }) + return + end + + opts.symbols = vim.tbl_map(string.lower, opts.symbols) + filtered_symbols = vim.tbl_filter(function(item) + return vim.tbl_contains(opts.symbols, string.lower(item.kind)) + end, results) + end + + if type(post_filter) == "function" then + filtered_symbols = post_filter(filtered_symbols) + end + + if not vim.tbl_isempty(filtered_symbols) then + return filtered_symbols + end + + -- print message that filtered_symbols is now empty + if has_symbols then + local symbols = table.concat(opts.symbols, ", ") + utils.notify("filter_symbols", { + msg = string.format("%s symbol(s) were not part of the query results", symbols), + level = "WARN", + }) + elseif has_ignore then + local symbols = table.concat(opts.ignore_symbols, ", ") + utils.notify("filter_symbols", { + msg = string.format("%s ignore_symbol(s) have removed everything from the query result", symbols), + level = "WARN", + }) + end +end + +utils.path_smart = (function() + local paths = {} + local os_sep = utils.get_separator() + return function(filepath) + local final = filepath + if #paths ~= 0 then + local dirs = vim.split(filepath, os_sep) + local max = 1 + for _, p in pairs(paths) do + if #p > 0 and p ~= filepath then + local _dirs = vim.split(p, os_sep) + for i = 1, math.min(#dirs, #_dirs) do + if (dirs[i] ~= _dirs[i]) and i > max then + max = i + break + end + end + end + end + if #dirs ~= 0 then + if max == 1 and #dirs >= 2 then + max = #dirs - 2 + end + final = "" + for k, v in pairs(dirs) do + if k >= max - 1 then + final = final .. (#final > 0 and os_sep or "") .. v + end + end + end + end + if not paths[filepath] then + paths[filepath] = "" + table.insert(paths, filepath) + end + if final and final ~= filepath then + return ".." .. os_sep .. final + else + return filepath + end + end +end)() + +utils.path_tail = (function() + local os_sep = utils.get_separator() + + return function(path) + for i = #path, 1, -1 do + if path:sub(i, i) == os_sep then + return path:sub(i + 1, -1) + end + end + return path + end +end)() + +utils.is_path_hidden = function(opts, path_display) + path_display = path_display or vim.F.if_nil(opts.path_display, require("telescope.config").values.path_display) + + return path_display == nil + or path_display == "hidden" + or type(path_display) == "table" and (vim.tbl_contains(path_display, "hidden") or path_display.hidden) +end + +utils.is_uri = function(filename) + local char = string.byte(filename, 1) or 0 + + -- is alpha? + if char < 65 or (char > 90 and char < 97) or char > 122 then + return false + end + + for i = 2, #filename do + char = string.byte(filename, i) + if char == 58 then -- `:` + return i < #filename and string.byte(filename, i + 1) ~= 92 -- `\` + elseif + not ( + (char >= 48 and char <= 57) -- 0-9 + or (char >= 65 and char <= 90) -- A-Z + or (char >= 97 and char <= 122) -- a-z + or char == 43 -- `+` + or char == 46 -- `.` + or char == 45 -- `-` + ) + then + return false + end + end + return false +end + +local calc_result_length = function(truncate_len) + local status = get_status(vim.api.nvim_get_current_buf()) + local len = vim.api.nvim_win_get_width(status.layout.results.winid) - status.picker.selection_caret:len() - 2 + return type(truncate_len) == "number" and len - truncate_len or len +end + +--- Transform path is a util function that formats a path based on path_display +--- found in `opts` or the default value from config. +--- It is meant to be used in make_entry to have a uniform interface for +--- builtins as well as extensions utilizing the same user configuration +--- Note: It is only supported inside `make_entry`/`make_display` the use of +--- this function outside of telescope might yield to undefined behavior and will +--- not be addressed by us +---@param opts table: The opts the users passed into the picker. Might contains a path_display key +---@param path string|nil: The path that should be formatted +---@return string: path to be displayed +---@return table: The transformed path ready to be displayed with the styling +utils.transform_path = function(opts, path) + if path == nil then + return "", {} + end + if utils.is_uri(path) then + return path, {} + end + + ---@type fun(opts:table, path: string): string, table? + local path_display = vim.F.if_nil(opts.path_display, require("telescope.config").values.path_display) + + local transformed_path = path + local path_style = {} + + if type(path_display) == "function" then + local custom_transformed_path, custom_path_style = path_display(opts, transformed_path) + return custom_transformed_path, custom_path_style or path_style + elseif utils.is_path_hidden(nil, path_display) then + return "", path_style + elseif type(path_display) == "table" then + if vim.tbl_contains(path_display, "tail") or path_display.tail then + transformed_path = utils.path_tail(transformed_path) + elseif vim.tbl_contains(path_display, "smart") or path_display.smart then + transformed_path = utils.path_smart(transformed_path) + else + if not vim.tbl_contains(path_display, "absolute") and not path_display.absolute then + local cwd + if opts.cwd then + cwd = opts.cwd + if not vim.in_fast_event() then + cwd = utils.path_expand(opts.cwd) + end + else + cwd = vim.loop.cwd() + end + transformed_path = Path:new(transformed_path):make_relative(cwd) + end + + if vim.tbl_contains(path_display, "shorten") or path_display["shorten"] ~= nil then + if type(path_display["shorten"]) == "table" then + local shorten = path_display["shorten"] + transformed_path = Path:new(transformed_path):shorten(shorten.len, shorten.exclude) + else + local length = type(path_display["shorten"]) == "number" and path_display["shorten"] + transformed_path = Path:new(transformed_path):shorten(length) + end + end + + if vim.tbl_contains(path_display, "truncate") or path_display.truncate then + if opts.__length == nil then + opts.__length = calc_result_length(path_display.truncate) + end + if opts.__prefix == nil then + opts.__prefix = 0 + end + transformed_path = truncate(transformed_path, opts.__length - opts.__prefix, nil, -1) + end + + -- IMPORTANT: filename_first needs to be the last option. Otherwise the + -- other options will not be displayed correctly. + if vim.tbl_contains(path_display, "filename_first") or path_display["filename_first"] ~= nil then + local reverse_directories = false + + if type(path_display["filename_first"]) == "table" then + local filename_first_opts = path_display["filename_first"] + + if filename_first_opts.reverse_directories == nil or filename_first_opts.reverse_directories == false then + reverse_directories = false + else + reverse_directories = filename_first_opts.reverse_directories + end + end + + local dirs = vim.split(transformed_path, utils.get_separator()) + local filename + + if reverse_directories then + dirs = utils.reverse_table(dirs) + filename = table.remove(dirs, 1) + else + filename = table.remove(dirs, #dirs) + end + + local tail = table.concat(dirs, utils.get_separator()) + + -- Prevents a toplevel filename to have a trailing whitespace + transformed_path = vim.trim(filename .. " " .. tail) + + path_style = { { { #filename, #transformed_path }, "TelescopeResultsComment" } } + end + end + + return transformed_path, path_style + else + log.warn("`path_display` must be either a function or a table.", "See `:help telescope.defaults.path_display.") + return transformed_path, path_style + end +end + +-- local x = utils.make_default_callable(function(opts) +-- return function() +-- print(opts.example, opts.another) +-- end +-- end, { example = 7, another = 5 }) + +-- x() +-- x.new { example = 3 }() +function utils.make_default_callable(f, default_opts) + default_opts = default_opts or {} + + return setmetatable({ + new = function(opts) + opts = vim.tbl_extend("keep", opts, default_opts) + return f(opts) + end, + }, { + __call = function() + local ok, err = pcall(f(default_opts)) + if not ok then + error(debug.traceback(err)) + end + end, + }) +end + +function utils.job_is_running(job_id) + if job_id == nil then + return false + end + return vim.fn.jobwait({ job_id }, 0)[1] == -1 +end + +function utils.buf_delete(bufnr) + if bufnr == nil then + return + end + + -- Suppress the buffer deleted message for those with &report<2 + local start_report = vim.o.report + if start_report < 2 then + vim.o.report = 2 + end + + if vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_is_loaded(bufnr) then + vim.api.nvim_buf_delete(bufnr, { force = true }) + end + + if start_report < 2 then + vim.o.report = start_report + end +end + +function utils.win_delete(name, win_id, force, bdelete) + if win_id == nil or not vim.api.nvim_win_is_valid(win_id) then + return + end + + local bufnr = vim.api.nvim_win_get_buf(win_id) + if bdelete then + utils.buf_delete(bufnr) + end + + if not vim.api.nvim_win_is_valid(win_id) then + return + end + + if not pcall(vim.api.nvim_win_close, win_id, force) then + log.trace("Unable to close window: ", name, "/", win_id) + end +end + +function utils.max_split(s, pattern, maxsplit) + pattern = pattern or " " + maxsplit = maxsplit or -1 + + local t = {} + + local curpos = 0 + while maxsplit ~= 0 and curpos < #s do + local found, final = string.find(s, pattern, curpos, false) + if found ~= nil then + local val = string.sub(s, curpos, found - 1) + + if #val > 0 then + maxsplit = maxsplit - 1 + table.insert(t, val) + end + + curpos = final + 1 + else + table.insert(t, string.sub(s, curpos)) + break + -- curpos = curpos + 1 + end + + if maxsplit == 0 then + table.insert(t, string.sub(s, curpos)) + end + end + + return t +end + +function utils.data_directory() + local sourced_file = require("plenary.debug_utils").sourced_filepath() + local base_directory = vim.fn.fnamemodify(sourced_file, ":h:h:h") + + return Path:new({ base_directory, "data" }):absolute() .. Path.path.sep +end + +function utils.buffer_dir() + return vim.fn.expand "%:p:h" +end + +function utils.display_termcodes(str) + return str:gsub(string.char(9), ""):gsub("", ""):gsub(" ", "") +end + +function utils.get_os_command_output(cmd, cwd) + if type(cmd) ~= "table" then + utils.notify("get_os_command_output", { + msg = "cmd has to be a table", + level = "ERROR", + }) + return {} + end + local command = table.remove(cmd, 1) + local stderr = {} + local stdout, ret = Job:new({ + command = command, + args = cmd, + cwd = cwd, + on_stderr = function(_, data) + table.insert(stderr, data) + end, + }):sync() + return stdout, ret, stderr +end + +function utils.win_set_buf_noautocmd(win, buf) + local save_ei = vim.o.eventignore + vim.o.eventignore = "all" + vim.api.nvim_win_set_buf(win, buf) + vim.o.eventignore = save_ei +end + +local load_once = function(f) + local resolved = nil + return function(...) + if resolved == nil then + resolved = f() + end + + return resolved(...) + end +end + +utils.file_extension = function(filename) + local parts = vim.split(filename, "%.") + -- this check enables us to get multi-part extensions, like *.test.js for example + if #parts > 2 then + return table.concat(vim.list_slice(parts, #parts - 1), ".") + else + return table.concat(vim.list_slice(parts, #parts), ".") + end +end + +utils.transform_devicons = load_once(function() + local has_devicons, devicons = pcall(require, "nvim-web-devicons") + + if has_devicons then + if not devicons.has_loaded() then + devicons.setup() + end + + return function(filename, display, disable_devicons) + local conf = require("telescope.config").values + if disable_devicons or not filename then + return display + end + + local basename = utils.path_tail(filename) + local icon, icon_highlight = devicons.get_icon(basename, utils.file_extension(basename), { default = false }) + if not icon then + icon, icon_highlight = devicons.get_icon(basename, nil, { default = true }) + icon = icon or " " + end + local icon_display = icon .. " " .. (display or "") + + if conf.color_devicons then + return icon_display, icon_highlight, icon + else + return icon_display, nil, icon + end + end + else + return function(_, display, _) + return display + end + end +end) + +utils.get_devicons = load_once(function() + local has_devicons, devicons = pcall(require, "nvim-web-devicons") + + if has_devicons then + if not devicons.has_loaded() then + devicons.setup() + end + + return function(filename, disable_devicons) + local conf = require("telescope.config").values + if disable_devicons or not filename then + return "" + end + + local basename = utils.path_tail(filename) + local icon, icon_highlight = devicons.get_icon(basename, utils.file_extension(basename), { default = false }) + if not icon then + icon, icon_highlight = devicons.get_icon(basename, nil, { default = true }) + end + if conf.color_devicons then + return icon, icon_highlight + else + return icon, nil + end + end + else + return function(_, _) + return "" + end + end +end) + +--- Checks if treesitter parser for language is installed +---@param lang string +utils.has_ts_parser = function(lang) + return pcall(vim.treesitter.language.add, lang) +end + +--- Telescope Wrapper around vim.notify +---@param funname string: name of the function that will be +---@param opts table: opts.level string, opts.msg string, opts.once bool +utils.notify = function(funname, opts) + opts.once = vim.F.if_nil(opts.once, false) + local level = vim.log.levels[opts.level] + if not level then + error("Invalid error level", 2) + end + local notify_fn = opts.once and vim.notify_once or vim.notify + notify_fn(string.format("[telescope.%s]: %s", funname, opts.msg), level, { + title = "telescope.nvim", + }) +end + +utils.__warn_no_selection = function(name) + utils.notify(name, { + msg = "Nothing currently selected", + level = "WARN", + }) +end + +--- Generate git command optionally with git env variables +---@param args string[] +---@param opts? table +---@return string[] +utils.__git_command = function(args, opts) + opts = opts or {} + + local _args = { "git" } + if opts.gitdir then + vim.list_extend(_args, { "--git-dir", opts.gitdir }) + end + if opts.toplevel then + vim.list_extend(_args, { "--work-tree", opts.toplevel }) + end + + return vim.list_extend(_args, args) +end + +utils.list_find = function(func, list) + for i, v in ipairs(list) do + if func(v, i, list) then + return i, v + end + end +end + +--- Takes the path and parses optional cursor location `$file:$line:$column` +--- If line or column not present `0` returned. +---@param path string +---@return string path +---@return integer? lnum +---@return integer? col +utils.__separate_file_path_location = function(path) + local location_numbers = {} + for i = #path, 1, -1 do + if path:sub(i, i) == ":" then + if i == #path then + path = path:sub(1, i - 1) + else + local location_value = tonumber(path:sub(i + 1)) + if location_value then + table.insert(location_numbers, location_value) + path = path:sub(1, i - 1) + + if #location_numbers == 2 then + -- There couldn't be more than 2 : separated number + break + end + end + end + end + end + + if #location_numbers == 2 then + -- because of the reverse the line number will be second + return path, location_numbers[2], location_numbers[1] + end + + if #location_numbers == 1 then + return path, location_numbers[1], 0 + end + + return path, nil, nil +end + +utils.merge_styles = function(style1, style2, offset) + local function addOffset(i, obj) + return { obj[1] + i, obj[2] + i } + end + + for _, item in ipairs(style2) do + item[1] = addOffset(offset, item[1]) + table.insert(style1, item) + end + + return style1 +end + +utils.reverse_table = function(input_table) + local temp_table = {} + for index = 0, #input_table do + temp_table[#input_table - index] = input_table[index + 1] -- Reverses the order + end + return temp_table +end + +return utils diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/action_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/action_spec.lua new file mode 100644 index 00000000..3db014f1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/action_spec.lua @@ -0,0 +1,510 @@ +local actions = require "telescope.actions" +local action_set = require "telescope.actions.set" + +local transform_mod = require("telescope.actions.mt").transform_mod + +local eq = assert.are.same + +describe("actions", function() + it("should allow creating custom actions", function() + local a = transform_mod { + x = function() + return 5 + end, + } + + eq(5, a.x()) + end) + + it("allows adding actions", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local x_plus_y = a.x + a.y + + eq({ "x", "y" }, { x_plus_y() }) + end) + + it("ignores nils from added actions", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + nil_maker = function() + return nil + end, + } + + local x_plus_y = a.x + a.nil_maker + a.y + + eq({ "x", "y" }, { x_plus_y() }) + end) + + it("allows overriding an action", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + -- actions.file_goto_selection_edit:replace(...) + a.x:replace(function() + return "foo" + end) + eq("foo", a.x()) + + a._clear() + eq("x", a.x()) + end) + + it("allows overriding an action only in specific cases with if", function() + local a = transform_mod { + x = function(e) + return e * 10 + end, + y = function() + return "y" + end, + } + + -- actions.file_goto_selection_edit:replace(...) + a.x:replace_if(function(e) + return e > 0 + end, function(e) + return (e / 10) + end) + eq(-100, a.x(-10)) + eq(10, a.x(100)) + eq(1, a.x(10)) + + a._clear() + eq(100, a.x(10)) + end) + + it("allows overriding an action only in specific cases with mod", function() + local a = transform_mod { + x = function(e) + return e * 10 + end, + y = function() + return "y" + end, + } + + -- actions.file_goto_selection_edit:replace(...) + a.x:replace_map { + [function(e) + return e > 0 + end] = function(e) + return (e / 10) + end, + [function(e) + return e == 0 + end] = function(e) + return (e + 10) + end, + } + + eq(-100, a.x(-10)) + eq(10, a.x(100)) + eq(1, a.x(10)) + eq(10, a.x(0)) + + a._clear() + eq(100, a.x(10)) + end) + + it("continuous replacement", function() + local a = transform_mod { + x = function() + return "cleared" + end, + y = function() + return "y" + end, + } + + -- Replace original, which becomes new fallback + a.x:replace(function() + return "negative" + end) + + -- actions.file_goto_selection_edit:replace(...) + a.x:replace_map { + [function(e) + return e > 0 + end] = function(e) + return "positive" + end, + [function(e) + return e == 0 + end] = function(e) + return "zero" + end, + } + + eq("positive", a.x(10)) + eq("zero", a.x(0)) + eq("negative", a.x(-10)) + + a._clear() + eq("cleared", a.x(10)) + end) + + it("enhance.pre", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_pre = false + + a.y:enhance { + pre = function() + called_pre = true + end, + } + eq("y", a.y()) + eq(true, called_pre) + end) + + it("enhance.post", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_post = false + + a.y:enhance { + post = function() + called_post = true + end, + } + eq("y", a.y()) + eq(true, called_post) + end) + + it("static_pre static_post", function() + local called_pre = false + local called_post = false + local static_post = 0 + local a = transform_mod { + x = { + pre = function() + called_pre = true + end, + action = function() + return "x" + end, + post = function() + called_post = true + end, + }, + } + + eq("x", a.x()) + eq(true, called_pre) + eq(true, called_post) + end) + + it("can call both", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_count = 0 + local count_inc = function() + called_count = called_count + 1 + end + + a.y:enhance { + pre = count_inc, + post = count_inc, + } + + eq("y", a.y()) + eq(2, called_count) + end) + + it("can call both even when combined", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_count = 0 + local count_inc = function() + called_count = called_count + 1 + end + + a.y:enhance { + pre = count_inc, + post = count_inc, + } + + a.x:enhance { + post = count_inc, + } + + local x_plus_y = a.x + a.y + x_plus_y() + + eq(3, called_count) + end) + + it( + "can call replace fn even when combined before replace registered the fn (because that happens with mappings)", + function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_count = 0 + local count_inc = function() + called_count = called_count + 1 + end + + local x_plus_y = a.x + a.y + a.x:replace(function() + count_inc() + end) + a.y:replace(function() + count_inc() + end) + + x_plus_y() + + eq(2, called_count) + end + ) + + it( + "can call enhance fn even when combined before enhance registed fns (because that happens with mappings)", + function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_count = 0 + local count_inc = function() + called_count = called_count + 1 + end + + local x_plus_y = a.x + a.y + a.y:enhance { + pre = count_inc, + post = count_inc, + } + + a.x:enhance { + post = count_inc, + } + + x_plus_y() + + eq(3, called_count) + end + ) + + it("clears enhance", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_post = false + + a.y:enhance { + post = function() + called_post = true + end, + } + + a._clear() + + eq("y", a.y()) + eq(false, called_post) + end) + + it("handles passing arguments", function() + local a = transform_mod { + x = function(bufnr) + return string.format "bufnr: %s" + end, + } + + a.x:replace(function(bufnr) + return string.format("modified: %s", bufnr) + end) + eq("modified: 5", a.x(5)) + end) + + it("handles add with two different tables", function() + local count_a = 0 + local count_b = 0 + local a = transform_mod { + x = function() + count_a = count_a + 1 + end, + } + local b = transform_mod { + y = function() + count_b = count_b + 1 + end, + } + + local called_count = 0 + local count_inc = function() + called_count = called_count + 1 + end + + a.x:enhance { + post = count_inc, + } + b.y:enhance { + post = count_inc, + } + + local x_plus_y = a.x + b.y + x_plus_y() + + eq(2, called_count) + eq(1, count_a) + eq(1, count_b) + end) + + it("handles tripple concat with static pre post", function() + local count_a = 0 + local count_b = 0 + local count_c = 0 + local static_pre = 0 + local static_post = 0 + local a = transform_mod { + x = { + pre = function() + static_pre = static_pre + 1 + end, + action = function() + count_a = count_a + 1 + end, + post = function() + static_post = static_post + 1 + end, + }, + } + local b = transform_mod { + y = { + pre = function() + static_pre = static_pre + 1 + end, + action = function() + count_b = count_b + 1 + end, + post = function() + static_post = static_post + 1 + end, + }, + } + local c = transform_mod { + z = { + pre = function() + static_pre = static_pre + 1 + end, + action = function() + count_c = count_c + 1 + end, + post = function() + static_post = static_post + 1 + end, + }, + } + + local replace_count = 0 + a.x:replace(function() + replace_count = replace_count + 1 + end) + + local x_plus_y_plus_z = a.x + b.y + c.z + x_plus_y_plus_z() + + eq(0, count_a) + eq(1, count_b) + eq(1, count_c) + eq(1, replace_count) + eq(3, static_pre) + eq(3, static_post) + end) + + describe("action_set", function() + it("can replace `action_set.edit`", function() + action_set.edit:replace(function(_, arg) + return "replaced:" .. arg + end) + eq("replaced:edit", actions.file_edit()) + eq("replaced:vnew", actions.file_vsplit()) + end) + + pending("handles backwards compat with select and edit files", function() + -- Reproduce steps: + -- In config, we have { [""] = actions.select, ... } + -- In caller, we have actions._goto:replace(...) + -- Person calls `select`, does not see update + action_set.edit:replace(function(_, arg) + return "default_to_edit:" .. arg + end) + eq("default_to_edit:edit", actions.select_default()) + + action_set.select:replace(function(_, arg) + return "override_with_select:" .. arg + end) + eq("override_with_select:default", actions.select_default()) + + -- Sometimes you might want to change the default selection... + -- but you don't want to prohibit the ability to edit the code... + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/command_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/command_spec.lua new file mode 100644 index 00000000..92128151 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/command_spec.lua @@ -0,0 +1,102 @@ +local command = require "telescope.command" + +local eq = assert.are.same + +describe("command_parser", function() + local test_parse = function(should, input, output) + it(should, function() + command.convert_user_opts(input) + eq(output, input) + end) + end + + -- Strings + test_parse("should handle cwd", { cwd = "string" }, { cwd = "string" }) + + -- Find commands + test_parse( + "should handle find_command 1", + { find_command = "rg,--ignore,--hidden,files" }, + { find_command = { "rg", "--ignore", "--hidden", "files" } } + ) + test_parse( + "should handle find_command 2", + { find_command = "fd,-t,f,-H" }, + { find_command = { "fd", "-t", "f", "-H" } } + ) + test_parse( + "should handle find_command 3", + { find_command = "fdfind,--type,f,--no-ignore" }, + { find_command = { "fdfind", "--type", "f", "--no-ignore" } } + ) + + -- Dictionaries/tables + test_parse( + "should handle layout_config viml 1", + { layout_config = "{'prompt_position':'top'}" }, + { layout_config = { prompt_position = "top" } } + ) + test_parse( + "should handle layout_config viml 2", + { layout_config = "#{prompt_position:'bottom'}" }, + { layout_config = { prompt_position = "bottom" } } + ) + test_parse( + "should handle layout_config viml 3", + { layout_config = "{'mirror':v:true}" }, + { layout_config = { mirror = true } } + ) + test_parse( + "should handle layout_config viml 4", + { layout_config = "#{mirror:v:true}" }, + { layout_config = { mirror = true } } + ) + test_parse( + "should handle layout_config lua 1", + { layout_config = "{prompt_position='bottom'}" }, + { layout_config = { prompt_position = "bottom" } } + ) + test_parse( + "should handle layout_config lua 2", + { layout_config = "{mirror=true}" }, + { layout_config = { mirror = true } } + ) + + -- Lists/tables + test_parse( + "should handle symbols commas list", + { symbols = "alpha,beta,gamma" }, + { symbols = { "alpha", "beta", "gamma" } } + ) + test_parse( + "should handle symbols viml list", + { symbols = "['alpha','beta','gamma']" }, + { symbols = { "alpha", "beta", "gamma" } } + ) + test_parse( + "should handle symbols lua list", + { symbols = "{'alpha','beta','gamma'}" }, + { symbols = { "alpha", "beta", "gamma" } } + ) + + -- Booleans + test_parse("should handle booleans 1", { hidden = "true" }, { hidden = true }) + test_parse("should handle booleans 2", { no_ignore = "false" }, { no_ignore = false }) + + -- Numbers + test_parse("should handle numbers 1", { depth = "2" }, { depth = 2 }) + test_parse("should handle numbers 2", { bufnr_width = "4" }, { bufnr_width = 4 }) + test_parse("should handle numbers 3", { severity = "27" }, { severity = 27 }) + + -- Multiple options + test_parse( + "should handle multiple options 1", + { layout_config = '{prompt_position="top"}', cwd = "/foobar", severity = "27" }, + { layout_config = { prompt_position = "top" }, cwd = "/foobar", severity = 27 } + ) + test_parse( + "should handle multiple options 2", + { symbols = "['alef','bet','gimel']", depth = "2", find_command = "rg,--ignore,files" }, + { symbols = { "alef", "bet", "gimel" }, depth = 2, find_command = { "rg", "--ignore", "files" } } + ) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/entry_display_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/entry_display_spec.lua new file mode 100644 index 00000000..a09ccae4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/entry_display_spec.lua @@ -0,0 +1,34 @@ +local entry_display = require "telescope.pickers.entry_display" + +describe("truncate", function() + for _, ambiwidth in ipairs { "single", "double" } do + for _, case in ipairs { + { args = { "abcde", 6 }, expected = { single = "abcde", double = "abcde" } }, + { args = { "abcde", 5 }, expected = { single = "abcde", double = "abcde" } }, + { args = { "abcde", 4 }, expected = { single = "abc…", double = "ab…" } }, + { args = { "アイウエオ", 11 }, expected = { single = "アイウエオ", double = "アイウエオ" } }, + { args = { "アイウエオ", 10 }, expected = { single = "アイウエオ", double = "アイウエオ" } }, + { args = { "アイウエオ", 9 }, expected = { single = "アイウエ…", double = "アイウ…" } }, + { args = { "アイウエオ", 8 }, expected = { single = "アイウ…", double = "アイウ…" } }, + { args = { "├─┤", 7 }, expected = { single = "├─┤", double = "├─┤" } }, + { args = { "├─┤", 6 }, expected = { single = "├─┤", double = "├─┤" } }, + { args = { "├─┤", 5 }, expected = { single = "├─┤", double = "├…" } }, + { args = { "├─┤", 4 }, expected = { single = "├─┤", double = "├…" } }, + { args = { "├─┤", 3 }, expected = { single = "├─┤", double = "…" } }, + { args = { "├─┤", 2 }, expected = { single = "├…", double = "…" } }, + } do + local msg = ("can truncate: ambiwidth = %s, [%s, %d] -> %s"):format( + ambiwidth, + case.args[1], + case.args[2], + case.expected[ambiwidth] + ) + it(msg, function() + local original = vim.o.ambiwidth + vim.o.ambiwidth = ambiwidth + assert.are.same(case.expected[ambiwidth], entry_display.truncate(case.args[1], case.args[2])) + vim.o.ambiwidth = original + end) + end + end +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/entry_manager_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/entry_manager_spec.lua new file mode 100644 index 00000000..6d2b5d3b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/entry_manager_spec.lua @@ -0,0 +1,189 @@ +local EntryManager = require "telescope.entry_manager" + +local eq = assert.are.same + +describe("process_result", function() + it("works with one entry", function() + local manager = EntryManager:new(5, nil) + + manager:add_entry(nil, 1, "hello", "") + + eq(1, manager:get_score(1)) + end) + + it("works with two entries", function() + local manager = EntryManager:new(5, nil) + + manager:add_entry(nil, 1, "hello", "") + manager:add_entry(nil, 2, "later", "") + + eq(2, manager.linked_states.size) + + eq("hello", manager:get_entry(1)) + eq("later", manager:get_entry(2)) + end) + + it("calls functions when inserting", function() + local called_count = 0 + local manager = EntryManager:new(5, function() + called_count = called_count + 1 + end) + + assert(called_count == 0) + manager:add_entry(nil, 1, "hello", "") + assert(called_count == 1) + end) + + it("calls functions when inserting twice", function() + local called_count = 0 + local manager = EntryManager:new(5, function() + called_count = called_count + 1 + end) + + assert(called_count == 0) + manager:add_entry(nil, 1, "hello", "") + manager:add_entry(nil, 2, "world", "") + assert(called_count == 2) + end) + + it("correctly sorts lower scores", function() + local called_count = 0 + local manager = EntryManager:new(5, function() + called_count = called_count + 1 + end) + manager:add_entry(nil, 5, "worse result", "") + manager:add_entry(nil, 2, "better result", "") + + eq("better result", manager:get_entry(1)) + eq("worse result", manager:get_entry(2)) + + eq(2, called_count) + end) + + it("respects max results", function() + local called_count = 0 + local manager = EntryManager:new(1, function() + called_count = called_count + 1 + end) + manager:add_entry(nil, 2, "better result", "") + manager:add_entry(nil, 5, "worse result", "") + + eq("better result", manager:get_entry(1)) + eq(1, called_count) + end) + + it("should allow simple entries", function() + local manager = EntryManager:new(5) + + local counts_executed = 0 + manager:add_entry( + nil, + 1, + setmetatable({}, { + __index = function(t, k) + local val = nil + if k == "ordinal" then + counts_executed = counts_executed + 1 + + -- This could be expensive, only call later + val = "wow" + end + + rawset(t, k, val) + return val + end, + }), + "" + ) + + eq("wow", manager:get_ordinal(1)) + eq("wow", manager:get_ordinal(1)) + eq("wow", manager:get_ordinal(1)) + + eq(1, counts_executed) + end) + + it("should not loop a bunch", function() + local info = {} + local manager = EntryManager:new(5, nil, info) + manager:add_entry(nil, 4, "better result", "") + manager:add_entry(nil, 3, "better result", "") + manager:add_entry(nil, 2, "better result", "") + + -- Loops once to find 3 < 4 + -- Loops again to find 2 < 3 + eq(2, info.looped) + end) + + it("should not loop a bunch, part 2", function() + local info = {} + local manager = EntryManager:new(5, nil, info) + manager:add_entry(nil, 4, "better result", "") + manager:add_entry(nil, 2, "better result", "") + manager:add_entry(nil, 3, "better result", "") + + -- Loops again to find 2 < 4 + -- Loops once to find 3 > 2 + -- but less than 4 + eq(3, info.looped) + end) + + it("should update worst score in all append case", function() + local manager = EntryManager:new(2, nil) + manager:add_entry(nil, 2, "result 2", "") + manager:add_entry(nil, 3, "result 3", "") + manager:add_entry(nil, 4, "result 4", "") + + eq(3, manager.worst_acceptable_score) + end) + + it("should update worst score in all prepend case", function() + local called_count = 0 + local manager = EntryManager:new(2, function() + called_count = called_count + 1 + end) + manager:add_entry(nil, 5, "worse result", "") + manager:add_entry(nil, 4, "less worse result", "") + manager:add_entry(nil, 2, "better result", "") + + -- Once for insert 5 + -- Once for prepend 4 + -- Once for prepend 2 + eq(3, called_count) + + eq("better result", manager:get_entry(1)) + eq(4, manager.worst_acceptable_score) + end) + + it("should call tiebreaker if score is the same, sort length", function() + local manager = EntryManager:new(5, nil) + local picker = { + tiebreak = function(curr, prev, prompt) + eq("asdf", prompt) + return #curr < #prev + end, + } + + manager:add_entry(picker, 0.5, "same same", "asdf") + manager:add_entry(picker, 0.5, "same", "asdf") + + eq("same", manager:get_entry(1)) + eq("same same", manager:get_entry(2)) + end) + + it("should call tiebreaker if score is the same, keep initial", function() + local manager = EntryManager:new(5, nil) + local picker = { + tiebreak = function(_, _, prompt) + eq("asdf", prompt) + return false + end, + } + + manager:add_entry(picker, 0.5, "same same", "asdf") + manager:add_entry(picker, 0.5, "same", "asdf") + + eq("same", manager:get_entry(2)) + eq("same same", manager:get_entry(1)) + end) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/layout_strategies_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/layout_strategies_spec.lua new file mode 100644 index 00000000..3315f635 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/layout_strategies_spec.lua @@ -0,0 +1,161 @@ +local config = require "telescope.config" +local resolve = require "telescope.config.resolve" +local layout_strats = require "telescope.pickers.layout_strategies" + +local validate_layout_config = layout_strats._validate_layout_config + +local eq = assert.are.same + +describe("layout_strategies", function() + it("should have validator", function() + assert(validate_layout_config, "Has validator") + end) + + local test_height = function(should, output, input, opts) + opts = opts or {} + + local max_columns, max_lines = opts.max_columns or 100, opts.max_lines or 100 + it(should, function() + local layout_config = validate_layout_config("horizontal", { height = true }, { height = input }) + + eq(output, resolve.resolve_height(layout_config.height)({}, max_columns, max_lines)) + end) + end + + test_height("should handle numbers", 10, 10) + + test_height("should handle percentage: 100", 10, 0.1, { max_lines = 100 }) + test_height("should handle percentage: 110", 11, 0.1, { max_lines = 110 }) + + test_height("should call functions: simple", 5, function() + return 5 + end) + test_height("should call functions: percentage", 15, function(_, _, lines) + return 0.1 * lines + end, { + max_lines = 150, + }) + + local test_defaults_key = function(should, key, strat, output, ours, theirs, override) + ours = ours or {} + theirs = theirs or {} + override = override or {} + + it(should, function() + config.clear_defaults() + config.set_defaults({ layout_config = theirs }, { layout_config = { ours, "description" } }) + local layout_config = validate_layout_config(strat, layout_strats._configurations[strat], override) + eq(output, layout_config[key]) + end) + end + + test_defaults_key( + "should use ours if theirs and override don't give the key", + "height", + "horizontal", + 50, + { height = 50 }, + { width = 100 }, + { width = 120 } + ) + + test_defaults_key( + "should use ours if theirs and override don't give the key for this strategy", + "height", + "horizontal", + 50, + { height = 50 }, + { vertical = { height = 100 } }, + { vertical = { height = 120 } } + ) + + test_defaults_key( + "should use theirs if override doesn't give the key", + "height", + "horizontal", + 100, + { height = 50 }, + { height = 100 }, + { width = 120 } + ) + + test_defaults_key( + "should use override if key given", + "height", + "horizontal", + 120, + { height = 50 }, + { height = 100 }, + { height = 120 } + ) + + test_defaults_key( + "should use override if key given for this strategy", + "height", + "horizontal", + 120, + { height = 50 }, + { height = 100 }, + { horizontal = { height = 120 } } + ) + + test_defaults_key( + "should use theirs if override doesn't give key (even if ours has strategy specific)", + "height", + "horizontal", + 100, + { horizontal = { height = 50 } }, + { height = 100 }, + { width = 120 } + ) + + test_defaults_key( + "should use override (even if ours has strategy specific)", + "height", + "horizontal", + 120, + { horizontal = { height = 50 } }, + { height = 100 }, + { height = 120 } + ) + + test_defaults_key( + "should use override (even if theirs has strategy specific)", + "height", + "horizontal", + 120, + { height = 50 }, + { horizontal = { height = 100 } }, + { height = 120 } + ) + + test_defaults_key( + "should use override (even if ours and theirs have strategy specific)", + "height", + "horizontal", + 120, + { horizontal = { height = 50 } }, + { horizontal = { height = 100 } }, + { height = 120 } + ) + + test_defaults_key( + "should handle user config overriding a table with a number", + "height", + "horizontal", + 120, + { height = { padding = 5 } }, + { height = 120 }, + {} + ) + + test_defaults_key( + "should handle user oneshot overriding a table with a number", + "height", + "horizontal", + 120, + {}, + { height = { padding = 5 } }, + { height = 120 } + ) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/linked_list_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/linked_list_spec.lua new file mode 100644 index 00000000..bc17ba19 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/linked_list_spec.lua @@ -0,0 +1,133 @@ +local LinkedList = require "telescope.algos.linked_list" + +describe("LinkedList", function() + it("can create a list", function() + local l = LinkedList:new() + + assert.are.same(0, l.size) + end) + + it("can add a single entry to the list", function() + local l = LinkedList:new() + l:append "hello" + + assert.are.same(1, l.size) + end) + + it("can iterate over one item", function() + local l = LinkedList:new() + l:append "hello" + + for val in l:iter() do + assert.are.same("hello", val) + end + end) + + it("iterates in order", function() + local l = LinkedList:new() + l:append "hello" + l:append "world" + + local x = {} + for val in l:iter() do + table.insert(x, val) + end + + assert.are.same({ "hello", "world" }, x) + end) + + it("iterates in order, for prepend", function() + local l = LinkedList:new() + l:prepend "world" + l:prepend "hello" + + local x = {} + for val in l:iter() do + table.insert(x, val) + end + + assert.are.same({ "hello", "world" }, x) + end) + + it("iterates in order, for combo", function() + local l = LinkedList:new() + l:prepend "world" + l:prepend "hello" + l:append "last" + l:prepend "first" + + local x = {} + for val in l:iter() do + table.insert(x, val) + end + + assert.are.same({ "first", "hello", "world", "last" }, x) + assert.are.same(#x, l.size) + end) + + it("has ipairs", function() + local l = LinkedList:new() + l:prepend "world" + l:prepend "hello" + l:append "last" + l:prepend "first" + + local x = {} + for v in l:iter() do + table.insert(x, v) + end + assert.are.same({ "first", "hello", "world", "last" }, x) + + local expected = {} + for i, v in ipairs(x) do + table.insert(expected, { i, v }) + end + + local actual = {} + for i, v in l:ipairs() do + table.insert(actual, { i, v }) + end + + assert.are.same(expected, actual) + end) + + describe("track_at", function() + it("should update tracked when only appending", function() + local l = LinkedList:new { track_at = 2 } + l:append "first" + l:append "second" + l:append "third" + + assert.are.same("second", l.tracked) + end) + + it("should update tracked when first some prepend and then append", function() + local l = LinkedList:new { track_at = 2 } + l:prepend "first" + l:append "second" + l:append "third" + + assert.are.same("second", l.tracked) + end) + + it("should update when only prepending", function() + local l = LinkedList:new { track_at = 2 } + l:prepend "third" + l:prepend "second" + l:prepend "first" + + assert.are.same("second", l.tracked) + end) + + it("should update when lots of prepend and append", function() + local l = LinkedList:new { track_at = 2 } + l:prepend "third" + l:prepend "second" + l:prepend "first" + l:append "fourth" + l:prepend "zeroth" + + assert.are.same("first", l.tracked) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/pickers/find_files_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/pickers/find_files_spec.lua new file mode 100644 index 00000000..5a1c460b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/pickers/find_files_spec.lua @@ -0,0 +1,143 @@ +-- Just skip on mac, it has flaky CI for some reason +if vim.fn.has "mac" == 1 or require("telescope.utils").iswin then + return +end + +local tester = require "telescope.testharness" + +local disp = function(val) + return vim.inspect(val, { newline = " ", indent = "" }) +end + +describe("builtin.find_files", function() + it("should find the readme", function() + tester.run_file "find_files__readme" + end) + + it("should handle cycling for full list", function() + tester.run_file "find_files__scrolling_descending_cycle" + end) + + for _, configuration in ipairs { + { sorting_strategy = "descending" }, + { sorting_strategy = "ascending" }, + } do + it("should not display devicons when disabled: " .. disp(configuration), function() + tester.run_string(string.format( + [[ + local max_results = 5 + + runner.picker('find_files', 'README.md', { + post_typed = { + { "> README.md", GetPrompt }, + { "> README.md", GetBestResult }, + }, + post_close = { + { 'README.md', GetFile }, + { 'README.md', GetFile }, + } + }, vim.tbl_extend("force", { + disable_devicons = true, + sorter = require('telescope.sorters').get_fzy_sorter(), + layout_strategy = 'center', + layout_config = { + height = max_results + 1, + width = 0.9, + }, + }, vim.json.decode([==[%s]==]))) + ]], + vim.json.encode(configuration) + )) + end) + + pending("use devicons, if it has it when enabled", function() + if not pcall(require, "nvim-web-devicons") then + return + end + + local md = require("nvim-web-devicons").get_icon "md" + tester.run_string(string.format( + [[ + runner.picker('find_files', 'README.md', { + post_typed = { + { "> README.md", GetPrompt }, + { "> %s README.md", GetBestResult } + }, + post_close = { + { 'README.md', GetFile }, + { 'README.md', GetFile }, + } + }, vim.tbl_extend("force", { + disable_devicons = false, + sorter = require('telescope.sorters').get_fzy_sorter(), + }, vim.json.decode([==[%s]==]))) + ]], + md, + vim.json.encode(configuration) + )) + end) + end + + it("should find the readme, using lowercase", function() + tester.run_string [[ + runner.picker('find_files', 'readme.md', { + post_close = { + { 'README.md', GetFile }, + } + }) + ]] + end) + + it("should find the pickers.lua, using lowercase", function() + tester.run_string [[ + runner.picker('find_files', 'pickers.lua', { + post_close = { + { 'pickers.lua', GetFile }, + } + }) + ]] + end) + + it("should find the pickers.lua", function() + tester.run_string [[ + runner.picker('find_files', 'pickers.lua', { + post_close = { + { 'pickers.lua', GetFile }, + { 'pickers.lua', GetFile }, + } + }) + ]] + end) + + it("should be able to c-n the items", function() + tester.run_string [[ + runner.picker('find_files', 'fixtures/find_files/file', { + post_typed = { + { + { + " lua/tests/fixtures/find_files/file_a.txt", + "> lua/tests/fixtures/find_files/file_abc.txt", + }, GetResults + }, + }, + post_close = { + { 'file_abc.txt', GetFile }, + }, + }, { + sorter = require('telescope.sorters').get_fzy_sorter(), + sorting_strategy = "ascending", + disable_devicons = true, + }) + ]] + end) + + it("should be able to get the current selection", function() + tester.run_string [[ + runner.picker('find_files', 'fixtures/find_files/file_abc', { + post_typed = { + { 'lua/tests/fixtures/find_files/file_abc.txt', GetSelectionValue }, + } + }) + ]] + end) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/resolver_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/resolver_spec.lua new file mode 100644 index 00000000..f30a3237 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/resolver_spec.lua @@ -0,0 +1,208 @@ +local eq = function(a, b) + assert.are.same(a, b) +end + +local resolve = require "telescope.config.resolve" + +describe("telescope.config.resolve", function() + describe("win_option", function() + it("should resolve for percentages", function() + local height_config = 0.8 + local opt = resolve.win_option(height_config) + + eq(height_config, opt.preview) + eq(height_config, opt.prompt) + eq(height_config, opt.results) + end) + + it("should resolve for percentages with default", function() + local height_config = 0.8 + local opt = resolve.win_option(nil, height_config) + + eq(height_config, opt.preview) + eq(height_config, opt.prompt) + eq(height_config, opt.results) + end) + + it("should resolve table values", function() + local table_val = { "a" } + local opt = resolve.win_option(nil, table_val) + + eq(table_val, opt.preview) + eq(table_val, opt.prompt) + eq(table_val, opt.results) + end) + + it("should allow overrides for different wins", function() + local prompt_override = { "a", prompt = "b" } + local opt = resolve.win_option(prompt_override) + eq("a", opt.preview) + eq("a", opt.results) + eq("b", opt.prompt) + end) + + it("should allow overrides for all wins", function() + local all_specified = { preview = "a", prompt = "b", results = "c" } + local opt = resolve.win_option(all_specified) + eq("a", opt.preview) + eq("b", opt.prompt) + eq("c", opt.results) + end) + + it("should allow some specified with a simple default", function() + local some_specified = { prompt = "b", results = "c" } + local opt = resolve.win_option(some_specified, "a") + eq("a", opt.preview) + eq("b", opt.prompt) + eq("c", opt.results) + end) + end) + + describe("resolve_height/width", function() + local test_sizes = { + { 24, 100 }, + { 35, 125 }, + { 60, 59 }, + { 100, 40 }, + } + it("should handle percentages", function() + local percentages = { 0.1, 0.33333, 0.5, 0.99 } + for _, s in ipairs(test_sizes) do + for _, p in ipairs(percentages) do + eq(math.floor(s[1] * p), resolve.resolve_width(p)(nil, unpack(s))) + eq(math.floor(s[2] * p), resolve.resolve_height(p)(nil, unpack(s))) + end + end + end) + + it("should handle percentages with min/max boundary", function() + eq(20, resolve.resolve_width { 0.1, min = 20 }(nil, 40, 120)) + eq(30, resolve.resolve_height { 0.1, min = 20 }(nil, 40, 300)) + + eq(24, resolve.resolve_width { 0.4, max = 80 }(nil, 60, 60)) + eq(80, resolve.resolve_height { 0.4, max = 80 }(nil, 60, 300)) + end) + + it("should handle fixed size", function() + local fixed = { 5, 8, 13, 21, 34 } + for _, s in ipairs(test_sizes) do + for _, f in ipairs(fixed) do + eq(math.min(f, s[1]), resolve.resolve_width(f)(nil, unpack(s))) + eq(math.min(f, s[2]), resolve.resolve_height(f)(nil, unpack(s))) + end + end + end) + + it("should handle functions", function() + local func = function(_, max_columns, max_lines) + if max_columns < 45 then + return math.min(max_columns, max_lines) + elseif max_columns < max_lines then + return max_columns * 0.8 + else + return math.min(max_columns, max_lines) * 0.5 + end + end + for _, s in ipairs(test_sizes) do + eq(func(nil, unpack(s)), resolve.resolve_height(func)(nil, unpack(s))) + end + end) + + it("should handle padding", function() + local func = function(_, max_columns, max_lines) + return math.floor(math.min(max_columns * 0.6, max_lines * 0.8)) + end + local pads = { 0.1, 5, func } + for _, s in ipairs(test_sizes) do + for _, p in ipairs(pads) do + eq(s[1] - 2 * resolve.resolve_width(p)(nil, unpack(s)), resolve.resolve_width { padding = p }(nil, unpack(s))) + eq( + s[2] - 2 * resolve.resolve_height(p)(nil, unpack(s)), + resolve.resolve_height { padding = p }(nil, unpack(s)) + ) + end + end + end) + end) + + describe("resolve_anchor_pos", function() + local test_sizes = { + { 6, 7, 8, 9 }, + { 10, 20, 30, 40 }, + { 15, 15, 16, 16 }, + { 17, 19, 23, 31 }, + { 21, 18, 26, 24 }, + { 50, 100, 150, 200 }, + } + + it([[should not adjust when "CENTER" or "" is the anchor]], function() + for _, s in ipairs(test_sizes) do + eq({ 0, 0 }, resolve.resolve_anchor_pos("", unpack(s))) + eq({ 0, 0 }, resolve.resolve_anchor_pos("center", unpack(s))) + eq({ 0, 0 }, resolve.resolve_anchor_pos("CENTER", unpack(s))) + end + end) + + it([[should end up at top when "N" in the anchor]], function() + local top_test = function(anchor, p_width, p_height, max_columns, max_lines) + local pos = resolve.resolve_anchor_pos(anchor, p_width, p_height, max_columns, max_lines) + eq(1, pos[2] + math.floor((max_lines - p_height) / 2)) + end + for _, s in ipairs(test_sizes) do + top_test("NW", unpack(s)) + top_test("N", unpack(s)) + top_test("NE", unpack(s)) + end + end) + + it([[should end up at left when "W" in the anchor]], function() + local left_test = function(anchor, p_width, p_height, max_columns, max_lines) + local pos = resolve.resolve_anchor_pos(anchor, p_width, p_height, max_columns, max_lines) + eq(1, pos[1] + math.floor((max_columns - p_width) / 2)) + end + for _, s in ipairs(test_sizes) do + left_test("NW", unpack(s)) + left_test("W", unpack(s)) + left_test("SW", unpack(s)) + end + end) + + it([[should end up at bottom when "S" in the anchor]], function() + local bot_test = function(anchor, p_width, p_height, max_columns, max_lines) + local pos = resolve.resolve_anchor_pos(anchor, p_width, p_height, max_columns, max_lines) + eq(max_lines - 1, pos[2] + p_height + math.floor((max_lines - p_height) / 2)) + end + for _, s in ipairs(test_sizes) do + bot_test("SW", unpack(s)) + bot_test("S", unpack(s)) + bot_test("SE", unpack(s)) + end + end) + + it([[should end up at right when "E" in the anchor]], function() + local right_test = function(anchor, p_width, p_height, max_columns, max_lines) + local pos = resolve.resolve_anchor_pos(anchor, p_width, p_height, max_columns, max_lines) + eq(max_columns - 1, pos[1] + p_width + math.floor((max_columns - p_width) / 2)) + end + for _, s in ipairs(test_sizes) do + right_test("NE", unpack(s)) + right_test("E", unpack(s)) + right_test("SE", unpack(s)) + end + end) + + it([[should ignore casing of the anchor]], function() + local case_test = function(a1, a2, p_width, p_height, max_columns, max_lines) + local pos1 = resolve.resolve_anchor_pos(a1, p_width, p_height, max_columns, max_lines) + local pos2 = resolve.resolve_anchor_pos(a2, p_width, p_height, max_columns, max_lines) + eq(pos1, pos2) + end + for _, s in ipairs(test_sizes) do + case_test("ne", "NE", unpack(s)) + case_test("w", "W", unpack(s)) + case_test("sW", "sw", unpack(s)) + case_test("cEnTeR", "CeNtEr", unpack(s)) + end + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/scroller_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/scroller_spec.lua new file mode 100644 index 00000000..96d64afd --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/scroller_spec.lua @@ -0,0 +1,143 @@ +local p_scroller = require "telescope.pickers.scroller" + +local log = require "telescope.log" +log.use_console = false + +local eq = assert.are.same + +describe("scroller", function() + local max_results = 10 + + describe("ascending cycle", function() + local cycle_scroller = p_scroller.create("cycle", "ascending") + + it("should return values within the max results", function() + eq(5, cycle_scroller(max_results, max_results, 5)) + end) + + it("should return 0 at 0", function() + eq(0, cycle_scroller(max_results, max_results, 0)) + end) + + it("should cycle you to the top when you go below 0", function() + eq(max_results - 1, cycle_scroller(max_results, max_results, -1)) + end) + + it("should cycle you to 0 when you go past the results", function() + eq(0, cycle_scroller(max_results, max_results, max_results + 1)) + end) + + it("should cycle when current results is less than max_results", function() + eq(0, cycle_scroller(max_results, 5, 7)) + end) + end) + + describe("ascending limit", function() + local limit_scroller = p_scroller.create("limit", "ascending") + + it("should return values within the max results", function() + eq(5, limit_scroller(max_results, max_results, 5)) + end) + + it("should return 0 at 0", function() + eq(0, limit_scroller(max_results, max_results, 0)) + end) + + it("should not cycle", function() + eq(0, limit_scroller(max_results, max_results, -1)) + end) + + it("should not cycle you to 0 when you go past the results", function() + eq(max_results - 1, limit_scroller(max_results, max_results, max_results + 1)) + end) + + it("should stay at current results when current results is less than max_results", function() + local current = 5 + eq(current - 1, limit_scroller(max_results, current, 7)) + end) + end) + + describe("descending cycle", function() + local cycle_scroller = p_scroller.create("cycle", "descending") + + it("should return values within the max results", function() + eq(5, cycle_scroller(max_results, max_results, 5)) + end) + + it("should return max_results - 1 at 0", function() + eq(0, cycle_scroller(max_results, max_results, 0)) + end) + + it("should cycle you to the bot when you go below 0", function() + eq(max_results - 1, cycle_scroller(max_results, max_results, -1)) + end) + + it("should cycle you to 0 when you go past the results", function() + eq(0, cycle_scroller(max_results, max_results, max_results + 1)) + end) + + it("should cycle when current results is less than max_results", function() + eq(9, cycle_scroller(max_results, 5, 4)) + end) + end) + + describe("descending limit", function() + local limit_scroller = p_scroller.create("limit", "descending") + + it("should return values within the max results", function() + eq(5, limit_scroller(max_results, max_results, 5)) + end) + + it("should return 0 at 0", function() + eq(0, limit_scroller(max_results, max_results, 0)) + end) + + it("should not cycle", function() + eq(0, limit_scroller(max_results, max_results, -1)) + end) + + it("should not cycle you to 0 when you go past the results", function() + eq(max_results - 1, limit_scroller(max_results, max_results, max_results + 1)) + end) + + it("should stay at current results when current results is less than max_results", function() + local current = 5 + eq(max_results - current, limit_scroller(max_results, current, 4)) + end) + end) + + describe("https://github.com/nvim-telescope/telescope.nvim/pull/293#issuecomment-751463224", function() + it("should handle having many more results than necessary", function() + local scroller = p_scroller.create("cycle", "descending") + + -- 23 112 23 + eq(0, scroller(23, 112, 23)) + end) + end) + + describe("should give top, middle and bottom index", function() + it("should handle ascending", function() + eq(0, p_scroller.top("ascending", 20, 1000)) + eq(19, p_scroller.bottom("ascending", 20, 1000)) + + eq(0, p_scroller.top("ascending", 20, 10)) + eq(9, p_scroller.bottom("ascending", 20, 10)) + + eq(5, p_scroller.middle("ascending", 11, 100)) + eq(10, p_scroller.middle("ascending", 20, 100)) + eq(12, p_scroller.middle("ascending", 25, 100)) + end) + + it("should handle descending", function() + eq(0, p_scroller.top("descending", 20, 1000)) + eq(19, p_scroller.bottom("descending", 20, 1000)) + + eq(10, p_scroller.top("descending", 20, 10)) + eq(19, p_scroller.bottom("descending", 20, 10)) + + eq(25, p_scroller.middle("descending", 30, 10)) + eq(50, p_scroller.middle("descending", 60, 20)) + eq(105, p_scroller.middle("descending", 120, 30)) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/sorters_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/sorters_spec.lua new file mode 100644 index 00000000..f3453a22 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/sorters_spec.lua @@ -0,0 +1,84 @@ +local sorters = require "telescope.sorters" + +describe("get_substr_matcher", function() + local function with_smartcase(smartcase, case) + local original = vim.o.smartcase + vim.o.smartcase = smartcase + + describe("scoring_function", function() + it(case.msg, function() + local matcher = sorters.get_substr_matcher() + assert.are.same(case.expected_score, matcher.scoring_function(_, case.prompt, _, case.entry)) + end) + end) + + describe("highlighter", function() + it("returns valid highlights", function() + local matcher = sorters.get_substr_matcher() + local highlights = matcher.highlighter(_, case.prompt, case.entry.ordinal) + table.sort(highlights, function(a, b) + return a.start < b.start + end) + assert.are.same(case.expected_highlights, highlights) + end) + end) + + vim.o.smartcase = original + end + + describe("when smartcase=OFF", function() + for _, case in ipairs { + { + msg = "doesn't match", + prompt = "abc def", + entry = { index = 3, ordinal = "abc d" }, + expected_score = -1, + expected_highlights = { { start = 1, finish = 3 } }, + }, + { + msg = "matches with lower case letters only", + prompt = "abc def", + entry = { index = 3, ordinal = "abc def ghi" }, + expected_score = 3, + expected_highlights = { { start = 1, finish = 3 }, { start = 5, finish = 7 } }, + }, + { + msg = "doesn't match with upper case letters", + prompt = "ABC def", + entry = { index = 3, ordinal = "ABC def ghi" }, + expected_score = -1, + expected_highlights = { { start = 5, finish = 7 } }, + }, + } do + with_smartcase(false, case) + end + end) + + describe("when smartcase=OFF", function() + for _, case in ipairs { + { + msg = "doesn't match", + prompt = "abc def", + entry = { index = 3, ordinal = "abc d" }, + expected_score = -1, + expected_highlights = { { start = 1, finish = 3 } }, + }, + { + msg = "matches with lower case letters only", + prompt = "abc def", + entry = { index = 3, ordinal = "abc def ghi" }, + expected_score = 3, + expected_highlights = { { start = 1, finish = 3 }, { start = 5, finish = 7 } }, + }, + { + msg = "matches with upper case letters", + prompt = "ABC def", + entry = { index = 3, ordinal = "ABC def ghi" }, + expected_score = 3, + expected_highlights = { { start = 1, finish = 3 }, { start = 5, finish = 7 } }, + }, + } do + with_smartcase(true, case) + end + end) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/telescope_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/telescope_spec.lua new file mode 100644 index 00000000..44594dd3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/telescope_spec.lua @@ -0,0 +1,218 @@ +local picker = require "telescope.pickers" +local Path = require "plenary.path" + +local eq = assert.are.same + +local function new_path(unix_path) + return Path:new(unpack(vim.split(unix_path, "/"))).filename +end + +describe("telescope", function() + describe("Picker", function() + describe("window_dimensions", function() + it("", function() + assert(true) + end) + end) + + describe("attach_mappings", function() + local new_picker = function(a, b) + a.finder = true + return picker.new(a, b) + end + + it("should allow for passing in a function", function() + local p = new_picker({}, { + attach_mappings = function() + return 1 + end, + }) + eq(1, p.attach_mappings()) + end) + + it("should override an attach mappings passed in by opts", function() + local called_order = {} + local p = new_picker({ + attach_mappings = function() + table.insert(called_order, "opts") + end, + }, { + attach_mappings = function() + table.insert(called_order, "default") + end, + }) + + p.attach_mappings() + + eq({ "default", "opts" }, called_order) + end) + end) + end) + + describe("Sorters", function() + describe("generic_fuzzy_sorter", function() + it("sort matches well", function() + local sorter = require("telescope.sorters").get_generic_fuzzy_sorter() + + local exact_match = sorter:score("hello", { ordinal = "hello" }) + local no_match = sorter:score("abcdef", { ordinal = "ghijkl" }) + local ok_match = sorter:score("abcdef", { ordinal = "ab" }) + + assert(exact_match < no_match, "exact match better than no match") + assert(exact_match < ok_match, "exact match better than ok match") + assert(ok_match < no_match, "ok match better than no match") + end) + + it("sorts multiple finds better", function() + local sorter = require("telescope.sorters").get_generic_fuzzy_sorter() + + local multi_match = sorter:score("generics", "exercises/generics/generics2.rs") + local one_match = sorter:score("abcdef", "exercises/generics/README.md") + + -- assert(multi_match < one_match) + end) + end) + + describe("fuzzy_file", function() + it("sort matches well", function() + local sorter = require("telescope.sorters").get_fuzzy_file() + + local exact_match = sorter:score("abcdef", { ordinal = "abcdef" }) + local no_match = sorter:score("abcdef", { ordinal = "ghijkl" }) + local ok_match = sorter:score("abcdef", { ordinal = "ab" }) + + assert(exact_match < no_match, string.format("Exact match better than no match: %s %s", exact_match, no_match)) + assert(exact_match < ok_match, string.format("Exact match better than OK match: %s %s", exact_match, ok_match)) + assert(ok_match < no_match, "OK match better than no match") + end) + + it("sorts matches after last os sep better", function() + local sorter = require("telescope.sorters").get_fuzzy_file() + + local better_match = sorter:score("aaa", { ordinal = new_path "bbb/aaa" }) + local worse_match = sorter:score("aaa", { ordinal = new_path "aaa/bbb" }) + + assert(better_match < worse_match, "Final match should be stronger") + end) + + pending("sorts multiple finds better", function() + local sorter = require("telescope.sorters").get_fuzzy_file() + + local multi_match = sorter:score("generics", { ordinal = "exercises/generics/generics2.rs" }) + local one_match = sorter:score("abcdef", { ordinal = "exercises/generics/README.md" }) + + assert(multi_match < one_match) + end) + end) + + describe("fzy", function() + local sorter = require("telescope.sorters").get_fzy_sorter() + local function score(prompt, line) + line = new_path(line) + return sorter:score(prompt, { ordinal = line }, function(val) + return val + end, function() + return -1 + end) + end + + describe("matches", function() + it("exact matches", function() + assert.True(score("a", "a") >= 0) + assert.True(score("a.bb", "a.bb") >= 0) + end) + it("ignore case", function() + assert.True(score("AbB", "abb") >= 0) + assert.True(score("abb", "ABB") >= 0) + end) + it("partial matches", function() + assert.True(score("a", "ab") >= 0) + assert.True(score("a", "ba") >= 0) + assert.True(score("aba", "baabbaab") >= 0) + end) + it("with delimiters between", function() + assert.True(score("abc", "a|b|c") >= 0) + end) + it("with empty query", function() + assert.True(score("", "") >= 0) + assert.True(score("", "a") >= 0) + end) + it("rejects non-matches", function() + assert.True(score("a", "") < 0) + assert.True(score("a", "b") < 0) + assert.True(score("aa", "a") < 0) + assert.True(score("ba", "a") < 0) + assert.True(score("ab", "a") < 0) + end) + end) + + describe("scoring", function() + it("prefers beginnings of words", function() + assert.True(score("amor", "app/models/order") < score("amor", "app/models/zrder")) + end) + it("prefers consecutive letters", function() + assert.True(score("amo", "app/models/foo") < score("amo", "app/m/foo")) + assert.True(score("erf", "perfect") < score("erf", "terrific")) + end) + it("prefers contiguous over letter following period", function() + assert.True(score("gemfil", "Gemfile") < score("gemfil", "Gemfile.lock")) + end) + it("prefers shorter matches", function() + assert.True(score("abce", "abcdef") < score("abce", "abc de")) + assert.True(score("abc", " a b c ") < score("abc", " a b c ")) + assert.True(score("abc", " a b c ") < score("abc", " a b c ")) + end) + it("prefers shorter candidates", function() + assert.True(score("test", "tests") < score("test", "testing")) + end) + it("prefers matches at the beginning", function() + assert.True(score("ab", "abbb") < score("ab", "babb")) + assert.True(score("test", "testing") < score("test", "/testing")) + end) + it("prefers matches at some locations", function() + assert.True(score("a", "/a") < score("a", "ba")) + assert.True(score("a", "bA") < score("a", "ba")) + assert.True(score("a", ".a") < score("a", "ba")) + end) + end) + + local function positions(prompt, line) + return sorter:highlighter(prompt, new_path(line)) + end + + describe("positioning", function() + it("favors consecutive positions", function() + assert.same({ 1, 5, 6 }, positions("amo", "app/models/foo")) + end) + it("favors word beginnings", function() + assert.same({ 1, 5, 12, 13 }, positions("amor", "app/models/order")) + end) + it("works when there are no bonuses", function() + assert.same({ 2, 4 }, positions("as", "tags")) + assert.same({ 3, 8 }, positions("as", "examples.txt")) + end) + it("favors smaller groupings of positions", function() + assert.same({ 3, 5, 7 }, positions("abc", "a/a/b/c/c")) + assert.same({ 3, 5 }, positions("ab", "caacbbc")) + end) + it("handles exact matches", function() + assert.same({ 1, 2, 3 }, positions("foo", "foo")) + end) + it("ignores empty requests", function() + assert.same({}, positions("", "")) + assert.same({}, positions("", "foo")) + assert.same({}, positions("foo", "")) + end) + end) + end) + + describe("layout_strategies", function() + describe("center", function() + it("should handle large terminals", function() + -- TODO: This could call layout_strategies.center w/ some weird edge case. + -- and then assert stuff about the dimensions. + end) + end) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/utils_spec.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/utils_spec.lua new file mode 100644 index 00000000..51c35266 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/automated/utils_spec.lua @@ -0,0 +1,309 @@ +local Path = require "plenary.path" +local utils = require "telescope.utils" + +local eq = assert.are.equal + +describe("path_expand()", function() + it("removes trailing os_sep", function() + if utils.iswin then + eq([[C:\Users\a\b]], utils.path_expand [[C:\Users\a\b\]]) + else + eq("/home/user", utils.path_expand "/home/user/") + end + end) + + it("works with root dir", function() + if utils.iswin then + eq([[C:\]], utils.path_expand [[C:\]]) + else + eq("/", utils.path_expand "/") + end + end) + + it("works with ~", function() + eq(vim.loop.os_homedir() .. "/src/foo", utils.path_expand "~/src/foo") + end) + + it("handles duplicate os_sep", function() + if utils.iswin then + eq([[C:\Users\a]], utils.path_expand [[C:\\\Users\\a]]) + else + eq("/home/user", utils.path_expand "/home///user") + end + end) + + it("preserves fake whitespace characters and whitespace", function() + local path_space = "/home/user/hello world" + eq(path_space, utils.path_expand(path_space)) + local path_newline = [[/home/user/hello\nworld]] + eq(path_newline, utils.path_expand(path_newline)) + end) + describe("early return for uri", function() + local uris = { + [[https://www.example.com/index.html]], + [[ftp://ftp.example.com/files/document.pdf]], + [[mailto:user@example.com]], + [[tel:+1234567890]], + [[file:///home/user/documents/report.docx]], + [[news:comp.lang.python]], + [[ldap://ldap.example.com:389/dc=example,dc=com]], + [[git://github.com/user/repo.git]], + [[steam://run/123456]], + [[magnet:?xt=urn:btih:6B4C3343E1C63A1BC36AEB8A3D1F52C4EDEEB096]], + } + + for _, uri in ipairs(uris) do + it(uri, function() + eq(uri, utils.path_expand(uri)) + end) + end + end) +end) + +describe("is_uri", function() + describe("detects valid uris", function() + local uris = { + [[https://www.example.com/index.html]], + [[ftp://ftp.example.com/files/document.pdf]], + [[mailto:user@example.com]], + [[tel:+1234567890]], + [[file:///home/user/documents/report.docx]], + [[news:comp.lang.python]], + [[ldap://ldap.example.com:389/dc=example,dc=com]], + [[git://github.com/user/repo.git]], + [[steam://run/123456]], + [[magnet:?xt=urn:btih:6B4C3343E1C63A1BC36AEB8A3D1F52C4EDEEB096]], + } + + for _, uri in ipairs(uris) do + it(uri, function() + assert.True(utils.is_uri(uri)) + end) + end + end) + + describe("detects invalid uris/paths", function() + local inputs = { + "hello", + "hello:", + "123", + "", + } + for _, input in ipairs(inputs) do + it(input, function() + assert.False(utils.is_uri(input)) + end) + end + end) + + describe("handles windows paths", function() + local paths = { + [[C:\Users\Usuario\Documents\archivo.txt]], + [[D:\Projects\project_folder\source_code.py]], + [[E:\Music\song.mp3]], + } + + for _, uri in ipairs(paths) do + it(uri, function() + assert.False(utils.is_uri(uri)) + end) + end + end) + + describe("handles linux paths", function() + local paths = { + [[/home/usuario/documents/archivo.txt]], + [[/var/www/html/index.html]], + [[/mnt/backup/backup_file.tar.gz]], + } + + for _, path in ipairs(paths) do + it(path, function() + assert.False(utils.is_uri(path)) + end) + end + end) + + describe("handles macos paths", function() + local paths = { + [[/Users/Usuario/Documents/archivo.txt]], + [[/Applications/App.app/Contents/MacOS/app_executable]], + [[/Volumes/ExternalDrive/Data/file.xlsx]], + } + + for _, path in ipairs(paths) do + it(path, function() + assert.False(utils.is_uri(path)) + end) + end + end) +end) + +describe("__separates_file_path_location", function() + local suites = { + { + input = "file.txt:12:4", + file = "file.txt", + row = 12, + col = 4, + }, + { + input = "file.txt:12", + file = "file.txt", + row = 12, + col = 0, + }, + { + input = "file:12:4", + file = "file", + row = 12, + col = 4, + }, + { + input = "file:12:", + file = "file", + row = 12, + col = 0, + }, + { + input = "file:", + file = "file", + }, + } + + for _, suite in ipairs(suites) do + it("separtates file path for " .. suite.input, function() + local file, row, col = utils.__separate_file_path_location(suite.input) + + eq(file, suite.file) + eq(row, suite.row) + eq(col, suite.col) + end) + end +end) + +describe("transform_path", function() + local cwd = (function() + if utils.iswin then + return [[C:\Users\user\projects\telescope.nvim]] + else + return "/home/user/projects/telescope.nvim" + end + end)() + + local function new_relpath(unix_path) + return Path:new(unpack(vim.split(unix_path, "/"))).filename + end + + local function assert_path(path_display, path, expect) + local opts = { cwd = cwd, __length = 15 } + if type(path_display) == "string" then + opts.path_display = { path_display } + eq(expect, utils.transform_path(opts, path)) + opts.path_display = { [path_display] = true } + eq(expect, utils.transform_path(opts, path)) + elseif type(path_display) == "table" then + opts.path_display = path_display + eq(expect, utils.transform_path(opts, path)) + elseif type(path_display) == "function" then + opts.path_display = path_display + eq(expect, utils.transform_path(opts, path)) + elseif path_display == nil then + eq(expect, utils.transform_path(opts, path)) + end + end + + it("handles nil path", function() + assert_path(nil, nil, "") + end) + + it("returns back uri", function() + local uri = [[https://www.example.com/index.html]] + assert_path(nil, uri, uri) + end) + + it("handles 'hidden' path_display", function() + eq("", utils.transform_path({ cwd = cwd, path_display = "hidden" }, "foobar")) + assert_path("hidden", "foobar", "") + end) + + it("returns relative path for default opts", function() + local relative = Path:new { "lua", "telescope", "init.lua" } + local absolute = Path:new { cwd, relative } + assert_path(nil, absolute.filename, relative.filename) + assert_path(nil, relative.filename, relative.filename) + end) + + it("handles 'tail' path_display", function() + local path = new_relpath "lua/telescope/init.lua" + assert_path("tail", path, "init.lua") + end) + + it("handles 'smart' path_display", function() + local path1 = new_relpath "lua/telescope/init.lua" + local path2 = new_relpath "lua/telescope/finders.lua" + local path3 = new_relpath "lua/telescope/finders/async_job_finder.lua" + local path4 = new_relpath "plugin/telescope.lua" + + assert_path("smart", path1, path1) + assert_path("smart", path2, new_relpath "../telescope/finders.lua") + assert_path("smart", path3, new_relpath "../telescope/finders/async_job_finder.lua") + assert_path("smart", path4, path4) + end) + + it("handles 'absolute' path_display", function() + local relative = Path:new { "lua", "telescope", "init.lua" } + local absolute = Path:new { cwd, relative } + + -- TODO: feels like 'absolute' should turn relative paths to absolute + -- assert_path("absolute", relative.filename, absolute.filename) + assert_path("absolute", absolute.filename, absolute.filename) + end) + + it("handles default 'shorten' path_display", function() + assert_path("shorten", new_relpath "lua/telescope/init.lua", new_relpath "l/t/init.lua") + end) + + it("handles 'shorten' with number", function() + assert_path({ shorten = 2 }, new_relpath "lua/telescope/init.lua", new_relpath "lu/te/init.lua") + end) + + it("handles 'shorten' with option table", function() + assert_path({ shorten = { len = 2 } }, new_relpath "lua/telescope/init.lua", new_relpath "lu/te/init.lua") + assert_path( + { shorten = { len = 2, exclude = { 1, 3, -1 } } }, + new_relpath "lua/telescope/builtin/init.lua", + new_relpath "lua/te/builtin/init.lua" + ) + end) + + it("handles default 'truncate' path_display", function() + assert_path({ "truncate" }, new_relpath "lua/telescope/init.lua", new_relpath "…scope/init.lua") + end) + + it("handles 'filename_first' path_display", function() + assert_path("filename_first", new_relpath "init.lua", new_relpath "init.lua") + assert_path("filename_first", new_relpath "lua/telescope/init.lua", new_relpath "init.lua lua/telescope") + end) + + it("handles 'filename_first' path_display with the option to reverse directories", function() + assert_path({ filename_first = { reverse_directories = true } }, new_relpath "init.lua", new_relpath "init.lua") + assert_path( + { filename_first = { reverse_directories = true } }, + new_relpath "lua/telescope/init.lua", + new_relpath "init.lua telescope/lua" + ) + assert_path({ filename_first = { reverse_directories = false } }, new_relpath "init.lua", new_relpath "init.lua") + assert_path( + { filename_first = { reverse_directories = false } }, + new_relpath "lua/telescope/init.lua", + new_relpath "init.lua lua/telescope" + ) + end) + + it("handles function passed to path_display", function() + assert_path(function(_, path) + return string.gsub(path, "^doc", "d") + end, new_relpath "doc/mydoc.md", new_relpath "d/mydoc.md") + end) +end) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/fixtures/find_files/file_a.txt b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/fixtures/find_files/file_a.txt new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/fixtures/find_files/file_abc.txt b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/fixtures/find_files/file_abc.txt new file mode 100644 index 00000000..e69de29b diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/helpers.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/helpers.lua new file mode 100644 index 00000000..73bd0c82 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/helpers.lua @@ -0,0 +1,87 @@ +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local previewers = require "telescope.previewers" +local pickers = require "telescope.pickers" +local sorters = require "telescope.sorters" +local utils = require "telescope.utils" + +local helpers = {} + +-- TODO: We should do something with builtins to get those easily. +helpers.auto_find_files = function(opts) + opts = opts or {} + opts.prompt_prefix = "" + + local find_command = opts.find_command + + if not find_command then + if 1 == vim.fn.executable "fd" then + find_command = { "fd", "--type", "f" } + elseif 1 == vim.fn.executable "fdfind" then + find_command = { "fdfind", "--type", "f" } + elseif 1 == vim.fn.executable "rg" then + find_command = { "rg", "--files" } + end + end + + if opts.cwd then + opts.cwd = utils.path_expand(opts.cwd) + end + + opts.entry_maker = opts.entry_maker or make_entry.gen_from_file(opts) + + local p = pickers.new(opts, { + prompt = "Find Files", + finder = finders.new_oneshot_job(find_command, opts), + previewer = previewers.cat.new(opts), + sorter = sorters.get_fuzzy_file(), + + track = true, + }) + + local count = 0 + p:register_completion_callback(function(s) + print( + count, + vim.inspect(s.stats, { + process = function(item) + if type(item) == "string" and item:sub(1, 1) == "_" then + return nil + end + + return item + end, + }) + ) + + count = count + 1 + end) + + local feed = function(text, feed_opts) + feed_opts = feed_opts or "n" + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(text, true, false, true), feed_opts, true) + end + + p:register_completion_callback(coroutine.wrap(function() + local input = opts.input + + for i = 1, #input do + feed(input:sub(i, i)) + coroutine.yield() + end + + vim.wait(300, function() end) + feed("", "") + + vim.defer_fn(function() + PASSED = opts.condition() + COMPLETED = true + end, 500) + + coroutine.yield() + end)) + + p:find() +end + +return helpers diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/pickers/find_files__readme.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/pickers/find_files__readme.lua new file mode 100644 index 00000000..1b76ad6c --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/pickers/find_files__readme.lua @@ -0,0 +1,8 @@ +local helper = require "telescope.testharness.helpers" +local runner = require "telescope.testharness.runner" + +runner.picker("find_files", "README.md", { + post_close = { + { "README.md", helper.get_file }, + }, +}) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/pickers/find_files__scrolling_descending_cycle.lua b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/pickers/find_files__scrolling_descending_cycle.lua new file mode 100644 index 00000000..6b3c0239 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/lua/tests/pickers/find_files__scrolling_descending_cycle.lua @@ -0,0 +1,12 @@ +local tester = require "telescope.testharness" +local helper = require "telescope.testharness.helpers" +local runner = require "telescope.testharness.runner" + +runner.picker("find_files", "telescope", { + post_close = { + tester.not_ { "plugin/telescope.vim", helper.get_file }, + }, +}, { + sorting_strategy = "descending", + scroll_strategy = "cycle", +}) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/nix-support/propagated-build-inputs b/config/neovim/store/lazy-plugins/telescope.nvim/nix-support/propagated-build-inputs new file mode 100644 index 00000000..54a6e25b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/nix-support/propagated-build-inputs @@ -0,0 +1 @@ +/nix/store/k36j1wm99w5xq490qzzskhs7ngzmhsg4-lua5.1-plenary.nvim-scm-1 /nix/store/mqbhz05llkddfb5wni0m48kw22ixxps4-lua-5.1.5 \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/plugin/telescope.lua b/config/neovim/store/lazy-plugins/telescope.nvim/plugin/telescope.lua new file mode 100644 index 00000000..338036cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/plugin/telescope.lua @@ -0,0 +1,156 @@ +if 1 ~= vim.fn.has "nvim-0.9.0" then + vim.api.nvim_err_writeln "Telescope.nvim requires at least nvim-0.9.0. See `:h telescope.changelog-2499`" + return +end + +if vim.g.loaded_telescope == 1 then + return +end +vim.g.loaded_telescope = 1 + +local highlights = { + -- Sets the highlight for selected items within the picker. + TelescopeSelection = { default = true, link = "Visual" }, + TelescopeSelectionCaret = { default = true, link = "TelescopeSelection" }, + TelescopeMultiSelection = { default = true, link = "Type" }, + TelescopeMultiIcon = { default = true, link = "Identifier" }, + + -- "Normal" in the floating windows created by telescope. + TelescopeNormal = { default = true, link = "Normal" }, + TelescopePreviewNormal = { default = true, link = "TelescopeNormal" }, + TelescopePromptNormal = { default = true, link = "TelescopeNormal" }, + TelescopeResultsNormal = { default = true, link = "TelescopeNormal" }, + + -- Border highlight groups. + -- Use TelescopeBorder to override the default. + -- Otherwise set them specifically + TelescopeBorder = { default = true, link = "TelescopeNormal" }, + TelescopePromptBorder = { default = true, link = "TelescopeBorder" }, + TelescopeResultsBorder = { default = true, link = "TelescopeBorder" }, + TelescopePreviewBorder = { default = true, link = "TelescopeBorder" }, + + -- Title highlight groups. + -- Use TelescopeTitle to override the default. + -- Otherwise set them specifically + TelescopeTitle = { default = true, link = "TelescopeBorder" }, + TelescopePromptTitle = { default = true, link = "TelescopeTitle" }, + TelescopeResultsTitle = { default = true, link = "TelescopeTitle" }, + TelescopePreviewTitle = { default = true, link = "TelescopeTitle" }, + + TelescopePromptCounter = { default = true, link = "NonText" }, + + -- Used for highlighting characters that you match. + TelescopeMatching = { default = true, link = "Special" }, + + -- Used for the prompt prefix + TelescopePromptPrefix = { default = true, link = "Identifier" }, + + -- Used for highlighting the matched line inside Previewer. Works only for (vim_buffer_ previewer) + TelescopePreviewLine = { default = true, link = "Visual" }, + TelescopePreviewMatch = { default = true, link = "Search" }, + + TelescopePreviewPipe = { default = true, link = "Constant" }, + TelescopePreviewCharDev = { default = true, link = "Constant" }, + TelescopePreviewDirectory = { default = true, link = "Directory" }, + TelescopePreviewBlock = { default = true, link = "Constant" }, + TelescopePreviewLink = { default = true, link = "Special" }, + TelescopePreviewSocket = { default = true, link = "Statement" }, + TelescopePreviewRead = { default = true, link = "Constant" }, + TelescopePreviewWrite = { default = true, link = "Statement" }, + TelescopePreviewExecute = { default = true, link = "String" }, + TelescopePreviewHyphen = { default = true, link = "NonText" }, + TelescopePreviewSticky = { default = true, link = "Keyword" }, + TelescopePreviewSize = { default = true, link = "String" }, + TelescopePreviewUser = { default = true, link = "Constant" }, + TelescopePreviewGroup = { default = true, link = "Constant" }, + TelescopePreviewDate = { default = true, link = "Directory" }, + TelescopePreviewMessage = { default = true, link = "TelescopePreviewNormal" }, + TelescopePreviewMessageFillchar = { default = true, link = "TelescopePreviewMessage" }, + + -- Used for Picker specific Results highlighting + TelescopeResultsClass = { default = true, link = "Function" }, + TelescopeResultsConstant = { default = true, link = "Constant" }, + TelescopeResultsField = { default = true, link = "Function" }, + TelescopeResultsFunction = { default = true, link = "Function" }, + TelescopeResultsMethod = { default = true, link = "Method" }, + TelescopeResultsOperator = { default = true, link = "Operator" }, + TelescopeResultsStruct = { default = true, link = "Struct" }, + TelescopeResultsVariable = { default = true, link = "SpecialChar" }, + + TelescopeResultsLineNr = { default = true, link = "LineNr" }, + TelescopeResultsIdentifier = { default = true, link = "Identifier" }, + TelescopeResultsNumber = { default = true, link = "Number" }, + TelescopeResultsComment = { default = true, link = "Comment" }, + TelescopeResultsSpecialComment = { default = true, link = "SpecialComment" }, + + -- Used for git status Results highlighting + TelescopeResultsDiffChange = { default = true, link = "DiffChange" }, + TelescopeResultsDiffAdd = { default = true, link = "DiffAdd" }, + TelescopeResultsDiffDelete = { default = true, link = "DiffDelete" }, + TelescopeResultsDiffUntracked = { default = true, link = "NonText" }, +} + +for k, v in pairs(highlights) do + vim.api.nvim_set_hl(0, k, v) +end + +-- This is like "" in your terminal. +-- To use it, do `cmap (TelescopeFuzzyCommandSearch) +vim.keymap.set( + "c", + "(TelescopeFuzzyCommandSearch)", + "e \"lua require('telescope.builtin').command_history " + .. '{ default_text = [=[" . escape(getcmdline(), \'"\') . "]=] }"', + { silent = true, noremap = true } +) + +vim.api.nvim_create_user_command("Telescope", function(opts) + require("telescope.command").load_command(unpack(opts.fargs)) +end, { + nargs = "*", + complete = function(_, line) + local builtin_list = vim.tbl_keys(require "telescope.builtin") + local extensions_list = vim.tbl_keys(require("telescope._extensions").manager) + + local l = vim.split(line, "%s+") + local n = #l - 2 + + if n == 0 then + local commands = { builtin_list, extensions_list } + -- TODO(clason): remove when dropping support for Nvim 0.9 + if vim.fn.has "nvim-0.11" == 1 then + commands = vim.iter(commands):flatten():totable() + else + commands = vim.tbl_flatten(commands) + end + table.sort(commands) + + return vim.tbl_filter(function(val) + return vim.startswith(val, l[2]) + end, commands) + end + + if n == 1 then + local is_extension = vim.tbl_filter(function(val) + return val == l[2] + end, extensions_list) + + if #is_extension > 0 then + local extensions_subcommand_dict = require("telescope.command").get_extensions_subcommand() + local commands = extensions_subcommand_dict[l[2]] + table.sort(commands) + + return vim.tbl_filter(function(val) + return vim.startswith(val, l[3]) + end, commands) + end + end + + local options_list = vim.tbl_keys(require("telescope.config").values) + table.sort(options_list) + + return vim.tbl_filter(function(val) + return vim.startswith(val, l[#l]) + end, options_list) + end, +}) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/rock_manifest b/config/neovim/store/lazy-plugins/telescope.nvim/rock_manifest new file mode 100644 index 00000000..52662f77 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/rock_manifest @@ -0,0 +1,147 @@ +rock_manifest = { + autoload = { + health = { + ["telescope.vim"] = "7e299db63944941eca0a9c0a857ee937" + } + }, + data = { + memes = { + planets = { + earth = "82832fcbe2e0eedeb3422285312ede07", + jupiter = "026b61e7620f516ea15d42813128a626", + mars = "552a3bb388e63202c5298b5a8759e720", + mercury = "846e54aca2ba791b745a8cc7fb3271e2", + moon = "da940686c32ef9e6d59481c418da9acc", + neptune = "49326b778a7a38ebfc7c90006967fb72", + pluto = "1a9e8e9618dbe487b7c140eec3996a4c", + saturn = "c93f370b9099a4a0727c2331d1d57dc6", + uranus = "5ff7c70e9b0b825e6793533b6560ef97", + venus = "59a266ef8a3cde831a540568ed3fa6b2" + } + } + }, + doc = { + ["secret.txt"] = "59f2d5cd8b01a044b4e7f598244869ec", + ["telescope.txt"] = "190706f7b21d86c8809b8b0d946d8aec", + ["telescope_changelog.txt"] = "5c807cb49d45799276be85c33693c08f" + }, + ftplugin = { + ["TelescopePrompt.lua"] = "207de42b635cc7de9ebf5e73128106aa", + ["TelescopeResults.lua"] = "50bc0d8c4520c7e678f4122a956d9a49" + }, + lua = { + telescope = { + ["_.lua"] = "e94b53c4fe57d51c361f0b1df9a6d64f", + _extensions = { + ["init.lua"] = "6c8e3d4e324874d3e9fef2f3b973027a" + }, + actions = { + ["generate.lua"] = "b627dbab3b7d2a572779707529331a5f", + ["history.lua"] = "6b34c6a357dfad0762f099c1ba3b426f", + ["init.lua"] = "0d6ea5eaeb361e6298663fbf0ea52206", + ["layout.lua"] = "91470edb5dd37a3025e141d5cc6691bd", + ["mt.lua"] = "9c22e387921dbfbd56afdf3fed7b84af", + ["set.lua"] = "e0b8491c7e789e09c032e650d8c858ef", + ["state.lua"] = "7c488697afa9b5a993920103661c5cc1", + ["utils.lua"] = "bc8870c00a9206ab13dcb2ab1142b5bd" + }, + algos = { + ["fzy.lua"] = "146c9ee304190d4f50d46f4b279e3182", + ["linked_list.lua"] = "9ca6315ec7c740da8198d8d62823040b", + ["string_distance.lua"] = "1d63486c008b414586ec505b36c361a2" + }, + builtin = { + ["__diagnostics.lua"] = "f2edc0b78359facb94a2269bf28fb0a7", + ["__files.lua"] = "d2c0373381ef70771adae4b6e9bb03ce", + ["__git.lua"] = "a40e030790285c1e8706d181f0b4a831", + ["__internal.lua"] = "04bf667684de70d6c6d600ba7c40ee99", + ["__lsp.lua"] = "03e5d78027aa37c04280151029744063", + ["init.lua"] = "0e0188ef0f37c07387270ea8594c0f2e" + }, + ["command.lua"] = "66d2dea0d60e1a27bfff18e55b1afb4c", + config = { + ["resolve.lua"] = "2ba48ad00f945ebcb300e211bc0280e3" + }, + ["config.lua"] = "824e9c60f4923d5e389d64ebc3b2914b", + ["debounce.lua"] = "562e0f836431d2e8e99130e657212196", + ["deprecated.lua"] = "7c1395098782b9c220a0dad7daca2fa6", + ["entry_manager.lua"] = "3d493cab0145fa8fd1405b2f160e0ec1", + finders = { + ["async_job_finder.lua"] = "7653d922e17ba0d3705a6cadcc9b544d", + ["async_oneshot_finder.lua"] = "4fd43f20022a9b72188776ef9b667497", + ["async_static_finder.lua"] = "66421d0a0cd5ce650bf107faaae87e62" + }, + ["finders.lua"] = "742e701ba623ff99035c455e231dc05a", + ["from_entry.lua"] = "7c2b68ad9af645ef0be4d7f929e021eb", + ["health.lua"] = "885a0947d640b003eba0b9aac67f5a21", + ["init.lua"] = "defe6eaf81f3596ccfafb9647afdac9f", + ["log.lua"] = "5c57992c1ac7c17c2e5879d38921f819", + ["make_entry.lua"] = "ece9c42534f9296fb9873147fe53291c", + ["mappings.lua"] = "5cfd5f1aff126928f745439d5c61b12b", + ["operators.lua"] = "e7244d086698dfaa71be2133820e57ee", + pickers = { + ["entry_display.lua"] = "27c74c2c4812244722444ebafbccd1ae", + ["highlights.lua"] = "2a1497c924141b85e57b106d0d8ec0ed", + ["layout.lua"] = "cdcf7f2019df1a715b233c4deae252e9", + ["layout_strategies.lua"] = "c11e7ff0f07d4c5e8f860bd2c4116e27", + ["multi.lua"] = "7822c69f5e6e537d7ad1d46c01097069", + ["scroller.lua"] = "54fc7267917bf9f9076a632293a73cd5", + ["window.lua"] = "fc74d7849a1381643289a65678e14aa3" + }, + ["pickers.lua"] = "b72754962d52ff38ec5af1ba74d51d34", + previewers = { + ["buffer_previewer.lua"] = "415436f46b3df7c247fbee9a466a4467", + ["init.lua"] = "fa1745a8bef70d35f713d3c4b3c70257", + ["previewer.lua"] = "3b02d1b4a9aeb5c6a79771709ca799cc", + ["term_previewer.lua"] = "6d38b050b6bc564215113e7cb42bb42a", + ["utils.lua"] = "9d4808a9af4dca92c1fb6f5993185213" + }, + ["sorters.lua"] = "bb1c9e687331d5cf0952582b2dadab7f", + ["state.lua"] = "450c0b38c2497ae8c505a882257e0bed", + testharness = { + ["helpers.lua"] = "1783be4d1f997b8ad23e9fab7680e0e3", + ["init.lua"] = "a447ceb5e7478053e98eb27acd011799", + ["runner.lua"] = "1201b1158848fc7e6a581f258776a977" + }, + ["themes.lua"] = "02e18c91826ee7834ffedc9378cbc9b9", + ["utils.lua"] = "a0d3abb98a237ada18f8a57421270af7" + }, + tests = { + automated = { + ["action_spec.lua"] = "7ffd015976efdc4bef88bf143e876a29", + ["command_spec.lua"] = "46a62833bbda1c56e29b2f183be62b9c", + ["entry_display_spec.lua"] = "b10d4d661d3ab2e1b89b412f45942061", + ["entry_manager_spec.lua"] = "708fbb4c280288a0a9c6fdd4e396c16e", + ["layout_strategies_spec.lua"] = "b146a6044e86ce63449f1463571b3ae2", + ["linked_list_spec.lua"] = "ebd7de3a8a8eaabf1e2fa6ced761374b", + pickers = { + ["find_files_spec.lua"] = "87af15abf6f94d58c667149a3e5a4e59" + }, + ["resolver_spec.lua"] = "ccfc7d99d8d230513c5e22ad5100e02e", + ["scroller_spec.lua"] = "3595abe16efe6753888274f11f1818ad", + ["sorters_spec.lua"] = "6a5c265752ac0ea6a07ef39d66518b8e", + ["telescope_spec.lua"] = "573cee7dce62d9f8cd0d3592f784aade", + ["utils_spec.lua"] = "e246ecc4ce2e9676a16c0269cf770bcc" + }, + fixtures = { + find_files = { + ["file_a.txt"] = "d41d8cd98f00b204e9800998ecf8427e", + ["file_abc.txt"] = "d41d8cd98f00b204e9800998ecf8427e" + } + }, + ["helpers.lua"] = "9d89080a25a31d5731b967731ed97c1d", + pickers = { + ["find_files__readme.lua"] = "c4a263e39c831b33a06a9d72ad383263", + ["find_files__scrolling_descending_cycle.lua"] = "8f32f279f627bdf01aa21628db699eef" + } + } + }, + plugin = { + ["telescope.lua"] = "0c9ae4aa6947d3bb124d7437d901237e" + }, + scripts = { + ["gendocs.lua"] = "bc7142cfb353b6315afec3fa2cac8b22", + ["minimal_init.vim"] = "97317fe610193d1fdad989e007a6cff6" + }, + ["telescope.nvim-scm-1.rockspec"] = "ecc6c254c09cc8213cde6d58393bc57b" +} diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/scripts/gendocs.lua b/config/neovim/store/lazy-plugins/telescope.nvim/scripts/gendocs.lua new file mode 100644 index 00000000..ec0047c9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/scripts/gendocs.lua @@ -0,0 +1,49 @@ +-- Setup telescope with defaults +if RELOAD then + RELOAD "telescope" +end +require("telescope").setup() + +local docgen = require "docgen" + +local docs = {} + +docs.test = function() + -- TODO: Fix the other files so that we can add them here. + local input_files = { + "./lua/telescope/init.lua", + "./lua/telescope/command.lua", + "./lua/telescope/builtin/init.lua", + "./lua/telescope/themes.lua", + "./lua/telescope/mappings.lua", + "./lua/telescope/pickers/layout.lua", + "./lua/telescope/pickers/layout_strategies.lua", + "./lua/telescope/config/resolve.lua", + "./lua/telescope/make_entry.lua", + "./lua/telescope/pickers/entry_display.lua", + "./lua/telescope/utils.lua", + "./lua/telescope/actions/init.lua", + "./lua/telescope/actions/state.lua", + "./lua/telescope/actions/set.lua", + "./lua/telescope/actions/layout.lua", + "./lua/telescope/actions/utils.lua", + "./lua/telescope/actions/generate.lua", + "./lua/telescope/previewers/init.lua", + "./lua/telescope/actions/history.lua", + } + + local output_file = "./doc/telescope.txt" + local output_file_handle = io.open(output_file, "w") + + for _, input_file in ipairs(input_files) do + docgen.write(input_file, output_file_handle) + end + + output_file_handle:write " vim:tw=78:ts=8:ft=help:norl:\n" + output_file_handle:close() + vim.cmd [[checktime]] +end + +docs.test() + +return docs diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/scripts/minimal_init.vim b/config/neovim/store/lazy-plugins/telescope.nvim/scripts/minimal_init.vim new file mode 100644 index 00000000..6f6731f7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/scripts/minimal_init.vim @@ -0,0 +1,8 @@ +set rtp+=. +set rtp+=../plenary.nvim/ +set rtp+=../tree-sitter-lua/ + +runtime! plugin/plenary.vim +runtime! plugin/telescope.lua + +let g:telescope_test_delay = 100 diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/manifest b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/manifest new file mode 100644 index 00000000..eadd72ee --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/manifest @@ -0,0 +1,330 @@ +commands = {} +dependencies = { + ["plenary.nvim"] = {}, + ["telescope.nvim"] = { + ["scm-1"] = { + { + constraints = { + { + op = "==", + version = { + 5, 1, string = "5.1" + } + } + }, + name = "lua" + }, + { + constraints = {}, + name = "plenary.nvim" + } + } + } +} +modules = { + ["telescope._"] = { + "telescope.nvim/scm-1" + }, + ["telescope._extensions.init"] = { + "telescope.nvim/scm-1" + }, + ["telescope.actions.generate"] = { + "telescope.nvim/scm-1" + }, + ["telescope.actions.history"] = { + "telescope.nvim/scm-1" + }, + ["telescope.actions.init"] = { + "telescope.nvim/scm-1" + }, + ["telescope.actions.layout"] = { + "telescope.nvim/scm-1" + }, + ["telescope.actions.mt"] = { + "telescope.nvim/scm-1" + }, + ["telescope.actions.set"] = { + "telescope.nvim/scm-1" + }, + ["telescope.actions.state"] = { + "telescope.nvim/scm-1" + }, + ["telescope.actions.utils"] = { + "telescope.nvim/scm-1" + }, + ["telescope.algos.fzy"] = { + "telescope.nvim/scm-1" + }, + ["telescope.algos.linked_list"] = { + "telescope.nvim/scm-1" + }, + ["telescope.algos.string_distance"] = { + "telescope.nvim/scm-1" + }, + ["telescope.builtin.__diagnostics"] = { + "telescope.nvim/scm-1" + }, + ["telescope.builtin.__files"] = { + "telescope.nvim/scm-1" + }, + ["telescope.builtin.__git"] = { + "telescope.nvim/scm-1" + }, + ["telescope.builtin.__internal"] = { + "telescope.nvim/scm-1" + }, + ["telescope.builtin.__lsp"] = { + "telescope.nvim/scm-1" + }, + ["telescope.builtin.init"] = { + "telescope.nvim/scm-1" + }, + ["telescope.command"] = { + "telescope.nvim/scm-1" + }, + ["telescope.config"] = { + "telescope.nvim/scm-1" + }, + ["telescope.config.resolve"] = { + "telescope.nvim/scm-1" + }, + ["telescope.debounce"] = { + "telescope.nvim/scm-1" + }, + ["telescope.deprecated"] = { + "telescope.nvim/scm-1" + }, + ["telescope.entry_manager"] = { + "telescope.nvim/scm-1" + }, + ["telescope.finders"] = { + "telescope.nvim/scm-1" + }, + ["telescope.finders.async_job_finder"] = { + "telescope.nvim/scm-1" + }, + ["telescope.finders.async_oneshot_finder"] = { + "telescope.nvim/scm-1" + }, + ["telescope.finders.async_static_finder"] = { + "telescope.nvim/scm-1" + }, + ["telescope.from_entry"] = { + "telescope.nvim/scm-1" + }, + ["telescope.health"] = { + "telescope.nvim/scm-1" + }, + ["telescope.init"] = { + "telescope.nvim/scm-1" + }, + ["telescope.log"] = { + "telescope.nvim/scm-1" + }, + ["telescope.make_entry"] = { + "telescope.nvim/scm-1" + }, + ["telescope.mappings"] = { + "telescope.nvim/scm-1" + }, + ["telescope.operators"] = { + "telescope.nvim/scm-1" + }, + ["telescope.pickers"] = { + "telescope.nvim/scm-1" + }, + ["telescope.pickers.entry_display"] = { + "telescope.nvim/scm-1" + }, + ["telescope.pickers.highlights"] = { + "telescope.nvim/scm-1" + }, + ["telescope.pickers.layout"] = { + "telescope.nvim/scm-1" + }, + ["telescope.pickers.layout_strategies"] = { + "telescope.nvim/scm-1" + }, + ["telescope.pickers.multi"] = { + "telescope.nvim/scm-1" + }, + ["telescope.pickers.scroller"] = { + "telescope.nvim/scm-1" + }, + ["telescope.pickers.window"] = { + "telescope.nvim/scm-1" + }, + ["telescope.previewers.buffer_previewer"] = { + "telescope.nvim/scm-1" + }, + ["telescope.previewers.init"] = { + "telescope.nvim/scm-1" + }, + ["telescope.previewers.previewer"] = { + "telescope.nvim/scm-1" + }, + ["telescope.previewers.term_previewer"] = { + "telescope.nvim/scm-1" + }, + ["telescope.previewers.utils"] = { + "telescope.nvim/scm-1" + }, + ["telescope.sorters"] = { + "telescope.nvim/scm-1" + }, + ["telescope.state"] = { + "telescope.nvim/scm-1" + }, + ["telescope.testharness.helpers"] = { + "telescope.nvim/scm-1" + }, + ["telescope.testharness.init"] = { + "telescope.nvim/scm-1" + }, + ["telescope.testharness.runner"] = { + "telescope.nvim/scm-1" + }, + ["telescope.themes"] = { + "telescope.nvim/scm-1" + }, + ["telescope.utils"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.action_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.command_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.entry_display_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.entry_manager_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.layout_strategies_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.linked_list_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.pickers.find_files_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.resolver_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.scroller_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.sorters_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.telescope_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.automated.utils_spec"] = { + "telescope.nvim/scm-1" + }, + ["tests.helpers"] = { + "telescope.nvim/scm-1" + }, + ["tests.pickers.find_files__readme"] = { + "telescope.nvim/scm-1" + }, + ["tests.pickers.find_files__scrolling_descending_cycle"] = { + "telescope.nvim/scm-1" + }, + ["tests/fixtures/find_files/file_a.txt"] = { + "telescope.nvim/scm-1" + }, + ["tests/fixtures/find_files/file_abc.txt"] = { + "telescope.nvim/scm-1" + } +} +repository = { + ["telescope.nvim"] = { + ["scm-1"] = { + { + arch = "installed", + commands = {}, + dependencies = {}, + modules = { + ["telescope._"] = "telescope/_.lua", + ["telescope._extensions.init"] = "telescope/_extensions/init.lua", + ["telescope.actions.generate"] = "telescope/actions/generate.lua", + ["telescope.actions.history"] = "telescope/actions/history.lua", + ["telescope.actions.init"] = "telescope/actions/init.lua", + ["telescope.actions.layout"] = "telescope/actions/layout.lua", + ["telescope.actions.mt"] = "telescope/actions/mt.lua", + ["telescope.actions.set"] = "telescope/actions/set.lua", + ["telescope.actions.state"] = "telescope/actions/state.lua", + ["telescope.actions.utils"] = "telescope/actions/utils.lua", + ["telescope.algos.fzy"] = "telescope/algos/fzy.lua", + ["telescope.algos.linked_list"] = "telescope/algos/linked_list.lua", + ["telescope.algos.string_distance"] = "telescope/algos/string_distance.lua", + ["telescope.builtin.__diagnostics"] = "telescope/builtin/__diagnostics.lua", + ["telescope.builtin.__files"] = "telescope/builtin/__files.lua", + ["telescope.builtin.__git"] = "telescope/builtin/__git.lua", + ["telescope.builtin.__internal"] = "telescope/builtin/__internal.lua", + ["telescope.builtin.__lsp"] = "telescope/builtin/__lsp.lua", + ["telescope.builtin.init"] = "telescope/builtin/init.lua", + ["telescope.command"] = "telescope/command.lua", + ["telescope.config"] = "telescope/config.lua", + ["telescope.config.resolve"] = "telescope/config/resolve.lua", + ["telescope.debounce"] = "telescope/debounce.lua", + ["telescope.deprecated"] = "telescope/deprecated.lua", + ["telescope.entry_manager"] = "telescope/entry_manager.lua", + ["telescope.finders"] = "telescope/finders.lua", + ["telescope.finders.async_job_finder"] = "telescope/finders/async_job_finder.lua", + ["telescope.finders.async_oneshot_finder"] = "telescope/finders/async_oneshot_finder.lua", + ["telescope.finders.async_static_finder"] = "telescope/finders/async_static_finder.lua", + ["telescope.from_entry"] = "telescope/from_entry.lua", + ["telescope.health"] = "telescope/health.lua", + ["telescope.init"] = "telescope/init.lua", + ["telescope.log"] = "telescope/log.lua", + ["telescope.make_entry"] = "telescope/make_entry.lua", + ["telescope.mappings"] = "telescope/mappings.lua", + ["telescope.operators"] = "telescope/operators.lua", + ["telescope.pickers"] = "telescope/pickers.lua", + ["telescope.pickers.entry_display"] = "telescope/pickers/entry_display.lua", + ["telescope.pickers.highlights"] = "telescope/pickers/highlights.lua", + ["telescope.pickers.layout"] = "telescope/pickers/layout.lua", + ["telescope.pickers.layout_strategies"] = "telescope/pickers/layout_strategies.lua", + ["telescope.pickers.multi"] = "telescope/pickers/multi.lua", + ["telescope.pickers.scroller"] = "telescope/pickers/scroller.lua", + ["telescope.pickers.window"] = "telescope/pickers/window.lua", + ["telescope.previewers.buffer_previewer"] = "telescope/previewers/buffer_previewer.lua", + ["telescope.previewers.init"] = "telescope/previewers/init.lua", + ["telescope.previewers.previewer"] = "telescope/previewers/previewer.lua", + ["telescope.previewers.term_previewer"] = "telescope/previewers/term_previewer.lua", + ["telescope.previewers.utils"] = "telescope/previewers/utils.lua", + ["telescope.sorters"] = "telescope/sorters.lua", + ["telescope.state"] = "telescope/state.lua", + ["telescope.testharness.helpers"] = "telescope/testharness/helpers.lua", + ["telescope.testharness.init"] = "telescope/testharness/init.lua", + ["telescope.testharness.runner"] = "telescope/testharness/runner.lua", + ["telescope.themes"] = "telescope/themes.lua", + ["telescope.utils"] = "telescope/utils.lua", + ["tests.automated.action_spec"] = "tests/automated/action_spec.lua", + ["tests.automated.command_spec"] = "tests/automated/command_spec.lua", + ["tests.automated.entry_display_spec"] = "tests/automated/entry_display_spec.lua", + ["tests.automated.entry_manager_spec"] = "tests/automated/entry_manager_spec.lua", + ["tests.automated.layout_strategies_spec"] = "tests/automated/layout_strategies_spec.lua", + ["tests.automated.linked_list_spec"] = "tests/automated/linked_list_spec.lua", + ["tests.automated.pickers.find_files_spec"] = "tests/automated/pickers/find_files_spec.lua", + ["tests.automated.resolver_spec"] = "tests/automated/resolver_spec.lua", + ["tests.automated.scroller_spec"] = "tests/automated/scroller_spec.lua", + ["tests.automated.sorters_spec"] = "tests/automated/sorters_spec.lua", + ["tests.automated.telescope_spec"] = "tests/automated/telescope_spec.lua", + ["tests.automated.utils_spec"] = "tests/automated/utils_spec.lua", + ["tests.helpers"] = "tests/helpers.lua", + ["tests.pickers.find_files__readme"] = "tests/pickers/find_files__readme.lua", + ["tests.pickers.find_files__scrolling_descending_cycle"] = "tests/pickers/find_files__scrolling_descending_cycle.lua", + ["tests/fixtures/find_files/file_a.txt"] = "tests/fixtures/find_files/file_a.txt", + ["tests/fixtures/find_files/file_abc.txt"] = "tests/fixtures/find_files/file_abc.txt" + } + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/autoload/health/telescope.vim b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/autoload/health/telescope.vim new file mode 100644 index 00000000..46cc9280 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/autoload/health/telescope.vim @@ -0,0 +1,3 @@ +function! health#telescope#check() + lua require 'telescope.health'.check() +endfunction diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/earth b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/earth new file mode 100644 index 00000000..aa624e17 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/earth @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓   ▓▓▓▓▓▓▓▓▓▓▓▒▒▒░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▓▓▓▓▓▒▒   ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▓▒▒░   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓ ░▓▓▓▒▒▒▒▒  ▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▒▓▓▓▓▓▓▒▒ ▒▓▒░░▒▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓░▓▓▓▒▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓░░▒▓▓▓▓▓▓▓▓▓   ▒░░  ░ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▓▓▓▒▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▒▓▓▓▓▓▓▓▓ ▒░  ░▓  ░ ░ ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▒▒▒▓▓▒░░▓▒▓▓▓▓▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓     ░▒▓      ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▒▒▓▒▒░░░▓▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░    ▓       ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▒▒▒▒▒▒░▒▓▓▓▓▒▓▒▒▓▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▒▒▓▒▒░▓▓▓░▓▓▓▓▓▒▓    ░▒▓▒▓░       ▓▓▓▓▓▓ +▓▓▓▓▓▓ ▒▒▒▒░▒▒▒░░▒▓▓▒▓░▓▓▓░▒▓▓▓▓▓▓▓▓▒▓▓▒▒▒▒▒░░░▓▓ ▓▓░▓▓▓▒▒░  ▓ ░▒▓         ▓▓▓▓▓ +▓▓▓▓▓▓▒▒▒▒░▒▒▒░▒▓▒▒▓▓▓▓▓▓▒▓▓ ▓░▓▓▓▒▒▒▓▓▓▓▒░░░░▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒         ▓▓▓▓▓ +▓▓▓▓▓ ▒▒▒▓▓▒▒▒▒▒▓▓▒▒▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▒▒ ▓▓▒▓▒▒░▒ ▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒░▓   ░░   ▒ ▓▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒▒▒▒▒▓▒▓▓▒▓▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▒▒▒▓▓▒▓▓▓▓▓▓▓▓ ░▓▓▓▓▒▒░   ▒ ░   ▒░ ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒▒▒▒▒▓▓▒▓▓▓▒▓▓▓▓▒▓▓▓▓▒▓▒░ ▓▒▓▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓  ▒ ▒░▒░░    ░ ▒░     ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▓▒▒▓▓▓▓▓▓▓▒▓▓▓▒▓▓▓▓▓▒▒▓░▓▓▓▒░▒   ░▒  ░ ░    ▒    ░▒░    ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒░░▒▓▒▒▒▒▒▒▒▓ ▓▓▓▒▓▓▓▓▓▓ ▓▒░░▓▓░▒▓▓            ░   ░ ░▒ ░░░░▒░ ░░▒ ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▓▒▒▒▒░▒░▒▓  ▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▒ ▓▓▓▓░          ▒▒▓▒░ ▒░░ ▒▓▓▓▒▒▒░▒ ▓ ▓▓▓▓ +▓▓▓▓▓ ▒░▒▓▒▒░░▒░░░▒▒▓ ░ ░▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▒ ▒▓   ▓▓  ░░░▒░▒   ▒▓▓▓▓▒░░    ▓▓▓▓▓ +▓▓▓▓▓▓▒▒░  ▒▓▒▒▒▒▓▒░ ▒ ░▒ ░▓▓▓▓▓▓▓▓▓▓▓▓░▒░░▓▓▓▓ ▓▓▓ ░▓▒░  ░ ░▒▒░▒▓▓░░░▒    ▓▓▓▓▓ +▓▓▓▓▓▓░▒▒▒▒ ▒  ▒▒░▓▒░▒ ▒░▒▓░▒▒▓▓▓▓▓▓▓▓▓▒▒░ ▒▓▓▒▓▓▓▓▓▓▓▓▓▒░▓▒ ▓▓▒▓▓▓▓▓▓▒░ ▒▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▒▒▒▒▒▒░▒▒▒░░▒▓░░▒▒░▒░▒░▒▓▓▓▒▒▒▒▓░ ░ ░       ▓▓▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▒▒░ ▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▒▒░▒▒▒▒░▒▒░▒ ▒ ▓▓▓░▒▓░▒▓▓▒░░░░▒▒  ░░▓ ▒░ ▒░░▒░▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒ ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▒░▒▒▒▒▒░▒▒▒░▒▒▒▒  ▒▒░ ▒▒▒▓░▒▒   ▒▓▓░▒      ▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒ ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒░▒▒░▒░▒▒▓░▒▓▓░▒▒░▒░▒▓▒▓▓▓▓▒▒▓░▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒░▒▒▒▒░▒░░▓▒▓▒▒▓░▓▓░▓▓ ▓▒▓ ▒  ░▓ ▒░▓░▓▓▓▓▓▓▓▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓ ▒ ▒▒▒▒▒▒▒░▒░▒░░░▒▒░░░░ ▒ ░░▓▒░▓▓▓▓▓▒▓▓▓ ▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒▓░▒▒░▒░▒▒░░▒▓▒▒▓▒▒▒▓▒░▓▒▒▓▓▓▓▓▓▓▓░░▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒░░▒▓▓▓▒▓▓▓▒▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒ ▒▒▒▒░░▒▒▒▓▒▒▓▒░▒▒▒▒░▒▒▓▓▒▒▓▓▒▓▓▓▓▓▓▓▓▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒░▒▒▒▒▒▒▒▒▒▒▓▒▒▒▓▓▒▓▒▒▒▒▓▓▒▒▓▓▓▓▓▒▓░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▓▓▒▓▓▓▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▓▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/jupiter b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/jupiter new file mode 100644 index 00000000..dac1487f --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/jupiter @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ░░░ ░    ░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▒▒▒▒▒▒▒░░ ▒ ░▒░ ░░▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░▒▓▒▒░▒▒ ▒▒▒░▒▒░▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒ ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▓▒▓▒▓▓▓▓▓▓▓▓▒▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒   ▓ ░   ░▒▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▒▒▓▓▒▓▓▓▒▒▒▒▓▒▒▒▒▒▒▓▒▓▓▒▓▒▒▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▒▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▒▒▒▒▓▒▒▓▓▓▒▒▒▒▒▒▒▒▓░░░▒▒▒▒░░░░░▒▒▒░░░░░▒▒▒░░░▒░▒▒▒▒▒▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▓▓▓▓▓▒▓▓▓▓▒▒▓▓▒▒▒▓▓▒▒▒▒▓▓▓▒▓▓▓▓▓▒▓▒▓▓▓▓▓▓▓▓ ▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▓▓▓▒░▒▓▒▒▓▓▓▒▓▓▒▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓▒░▓▒▓▓   ▒ ▓▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▒▒░▓▓░░░  ▒   ▒▒▓▓▒  ▓▓░▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▒▓▓▓▒▒▓▓░▒░ ▓▓▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓ ▓▓▓ +▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▓▓▓▓▒▓▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▒▓▓▓▓▓▒▒▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓░░▓▒▒▓▒▓ ▒▒▓▓▒▓▓▒▓▓▒▓▓▓▓▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▒▒▒▒▒▒▓▒▓▓▓▒▒▓▓▒▒    ░░▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▒▓▓▓▒▓▓▓▓▒      ░▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▓▓▓▒▓▒▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/mars b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/mars new file mode 100644 index 00000000..2eb8cfdd --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/mars @@ -0,0 +1,27 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░▒   ▓▒ ░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░  ░░░▒▒ ░ ▒░  ░░░░░░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░ ░░  ░ ░░▒▓░░░░░░░ ░░ ░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░  ░░░░░░░░ ░▒░▒░░░▒▒░░░░░░░░░ ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░ ░░░░░░░░░░░░░░░▒▒▒▓▒▒▒▒▒▒▒░░░░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░░ ░  ░░░░░░░░░░ ░░░▒▒▒▒▒▒▒▓▒▒▒▒░▒▒░░░▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░ ░░         ░  ░  ░ ░░▒▓▒▒▒▒▒▒▒▓▒▒░▒░▒▒░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░ ░░                  ░ ░░▒▒▒▒▒▓▒▒▓▒▒░░ ▒▒░ ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░                        ░░░▒▒▒▒▒▒▒▒▒░ ░░▒░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                          ░▒░░▒▒▒▒▒▒░░░░░░░░░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░░ ░░░░░▒▒░▒░▒▒▒░░░░░ ░ ░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░ ░▒▒ ░ ░▒░░░░░░░░░  ░▒░░ ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░░  ░░ ░░ ░░░░░░░ ░░░░  ░░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓        ░           ░░ ░              ░     ▒░░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                      ░░░░     ░░     ░░ ░░░░░ ▒░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                       ░░░             ░ ░░▒░ ░▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░░  ░  ░       ░░░  ░▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░                       ░ ▒        ░  ░░░▒▒░▒▒▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░        ░     ░ ░    ▒  ░░░ ░  ░░░░▒░░▓░▒░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░        ░          ░░ ▒▓▒░▒▒▒ ░░░░▒▒░▒▒ ░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░ ░   ░░░          ░▒ ▒▒▒▓▒▒░░░░░░░░░▒░▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░ ░░░░░░░░░░ ░░░░ ▓▒▒▒▒▒▒▒▒▒▒░░░░░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░░░░░░▒▒▒▒▒░▒▒▒░░▒▒▓▓▓▓▒▓▒▒▒▒▒░▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░   ░▒▒░░░░░▒▒░░▒▒▒▓▓▒▒▒▒▒▒░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░ ░ ░░░░░░░░░▒▒░░░░░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░ ░ ░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/mercury b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/mercury new file mode 100644 index 00000000..fcad37da --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/mercury @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓            ░  ░░    ░   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░░            ░░   ░░  ░░ ░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓      ░░            ░   ░ ░░ ░ ░     ░   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░░ ░          ░ ░        ░░ ░  ░░░     ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                 ░░    ░          ░░  ░  ░ ░  ░ ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓             ░░     ░░░░      ░ ░  ░ ░░  ░ ░░░░ ░░░░  ▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓             ░░  ░     ░ ░░░ ░░ ░ ░░░    ░ ░░░░░░░ ░░░░░ ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓ ░    ░   ░ ░░░  ░░░ ░░  ░░░░░░   ░░░░░  ░ ░ ░░ ░░░ ░░░░░░░ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓░   ░░ ░░  ░  ░  ░░░  ░░░ ░░ ░░  ░  ░░░░░░ ░░░ ░░░   ░░ ░░░░░░░▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓    ░░           ░░ ░   ░ ░░  ░░░░░░ ░░░░░░ ░    ░ ░  ░  ░░░░░ ░ ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓ ░   ░ ░░░    ░  ░░░░ ░ ░ ░░░  ░░░░░░░░░░░░░░░░░░░ ░ ░ ░ ░░░░░░ ░░░▓▓▓▓▓▓ +▓▓▓▓▓▓    ░ ░     ░     ░░░░      ░   ░░░   ░ ░░░░░░░░░░░ ░░  ░░░  ░  ░ ░░▓▓▓▓▓▓ +▓▓▓▓▓▓ ░   ░  ░░░ ░   ░░░░░     ░   ░░  ░░   ░░░   ░░░░░░░░  ░░░░░░░░░░░░ ░▓▓▓▓▓ +▓▓▓▓▓░ ░  ░░        ░ ░░░ ░ ░░ ░  ░░░░░░░░ ░  ░░░ ░ ░░░░░░    ░░░ ░░░░░░░░  ▓▓▓▓ +▓▓▓▓▓   ░░░ ░░░░░  ░░  ░░░░░   ░░░░ ░░  ░ ░░░ ░░░░░░ ░ ░░░░ ░  ░░░░░░   ░░ ░▓▓▓▓ +▓▓▓▓▓ ░░░░░░░ ░░░     ░░░░ ░░░░░░░░     ░░░ ░      ░   ░░░░ ░░░░░░░░░░  ░░░ ▓▓▓▓ +▓▓▓▓ ░ ░░░░░ ░░░   ░░░░░ ░ ░░░░░░░░ ░   ░  ░  ░ ░░ ░░  ░░░░░░░░░░░░░░░░░░░░░▓▓▓▓ +▓▓▓▓░░░░░░░░░░░░░░░░░   ░    ░░ ░░░░░░  ░      ░ ░░░     ░ ░░░░░░░░░░░░░░░░ ▓▓▓▓ +▓▓▓▓ ░  ░░ ░░  ░░░░░░  ░ ░░    ░ ░░░░░         ░░ ░░░░░░░░░ ░░░░░░░░░░░░░ ░ ▓▓▓▓ +▓▓▓▓▓ ░░░       ░░░░░░ ░  ░     ░  ░░░░░ ░░░ ░░░░ ░░░░░░░░ ░░░░░░░░░░░░░░ ░░▓▓▓▓ +▓▓▓▓▓ ░░░░░░ ░  ░░░ ░░ ░ ░░░  ░     ░░░░ ░ ░░░░  ░░░ ░  ░░░  ░░░░░░░░   ░░  ▓▓▓▓ +▓▓▓▓▓░░░  ░░ ░░  ░░░ ░░░░░░░░░░░   ░  ░░░ ░░ ░ ░░░░░    ░ ░░ ░░ ░░░ ░ ░░ ░░░▓▓▓▓ +▓▓▓▓▓▓░░  ░░ ░  ░░░░░░░░░░░░░░░░░░░░░░░ ░  ░░░░       ░░░  ░░░░░░░░ ░░░ ░░ ▓▓▓▓▓ +▓▓▓▓▓▓▓  ░░ ░░░░░░░ ░ ░░░░ ░░░░░ ░░░░░   ░░ ░░░    ░  ░░ ░░░ ░ ░  ░░░░  ░░▓▓▓▓▓▓ +▓▓▓▓▓▓▓  ░░░░░░░░░░░░░░░░░░░ ░   ░░░░░░ ░  ░░ ░    ░  ░     ░  ░░░░  ░░░░░▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓ ░░░░ ░░░░░      ░░░░░  ░░         ░ ░ ░                  ░ ░ ░ ░▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓ ░░░░  ░░░░░░░░░░░░░   ░           ░░             ░    ░░     ░▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓      ░░░░  ░  ░░                ░ ░ ░  ░░        ░      ░░ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓    ░░░░░░░░        ░                                 ░░ ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓   ░░░░░░░                     ░░░    ░              ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓       ░           ░         ░  ░     ░          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░                      ░  ░    ░          ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░                      ░           ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░   ░        ░              ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                   ░  ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/moon b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/moon new file mode 100644 index 00000000..2943e284 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/moon @@ -0,0 +1,35 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/neptune b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/neptune new file mode 100644 index 00000000..9c2954e0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/neptune @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓       ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░▒▒▒░░░▒▒░░▒░▒░▒░░▒▒░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░░░░░░░░░░░░░░▒░░░░▒░▒░░▒▒▒░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░░░░   ░░░░░░░░░░░░▒░▒░▒░░░▒▒░░▒░▒░░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒░░▒░▒░▒░░░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒░▒░▒░░░░▒▒▒░░░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░░░░░░░░░░░░░      ░░░░░░░░░░░░░░░░░▒░░▒▒░▒░░░░   ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▒░░░▒░▒░░░░░░                 ░ ░░░░░░░░░░░▒░▒░░░░░░░   ▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓░▒░▒░░░░░                           ░░░░░░▒░░▒▒░░░▒░░░░░  ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓ ▒▒▒░░░░░░                              ░░░░▒░▒░▒▒░▒▒░░░░░   ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓░▒▒░░░░░░                                ░ ░░░░░░▒░░▒░░░░░░░   ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓ ▒▒▒░░░░░                                   ░░░░░░▒░░▒░░▒▒░░░░   ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▒░░░░░░                                    ░░░░░▒░░░▒░░▒░▒░░░   ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓░░░▒░░░                                       ░░░░░▒░░▒░▒░▒░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▒░▒▒░░                         ░ ░░░░░░░░░     ░░░░░░░▒▒░░░▒░░░    ▓▓▓▓▓▓▓ +▓▓▓▓▓░▒░▒░░░░                          ░░░░░░░░░░░░░░░░░░░▒░▒░▒░░░░░░░░  ▓▓▓▓▓▓▓ +▓▓▓▓▓▒▒▒░░░░░                              ░ ░░░░░░░░░░░ ░▒▒░▒▒▒░░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓▒▒▒▒░░░░                                     ░░░░░░▒░▒▒▒▒▒░░░░░░    ▓▓▓▓▓▓▓ +▓▓▓▓▓░░▒░░░░░░                                   ░░░░░░  ░▒░▒░░░▒░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓░░▒░░░░░░                                   ░░░░░▒▒░░░░░▒▒░▒░▒░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓ ▒▒▒▒░░░░░░                                ░░░░░░░░░░░▒░▒░░▒░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓▓░▒░░▒░░░░░                            ░ ░░░░░░░░░░░░▒░▒░░░░░░░░   ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓░▒▒░▒▒░░░░░ ░      ░     ░   ░   ░░░░░░░░░░░░▒░░░░░░▒░░░░░░░░     ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓░▒▒░▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒░░░░░░░░░▒░░▒░░░░░░░░░   ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓ ▒▒▒░▒ ░░░░░░░░░░░░░░░░░░░▒░░░░░░░░░▒░░░▒░░░▒░▒░▒░░░░░▒░░ ░    ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░▒░░░▒░░░░░░░░░░░▒░░░░░░░░░░░░▒░▒░░░░░░░░░░    ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓ ▒░░░░░░░░░░░░░░░░░░▒░░░░░░░░░░░▒░▒░░▒░░░▒░▒░░░░░░░░░ ░░   ▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓░▒░░░░▒░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░▒░░░░░░░░░░░░░    ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓░░▒░░░▒░░░░░░▒░░░░░░░░░░▒░░░░░░░▒░░░░░▒░░░░░░ ░      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░▒░░░▒░░░░░░▒▒░░░░▒░▒░░░▒▒▒░░░░░░░░░  ░     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░▒░░░░░░░░░░░░░░░░░░░░░░▒░░░░░░░░░░      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░▒░░░░░░░░░▒░░░░░░▒░░▒░░▒░░░░░░░ ░    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░ ░░░░░░░░░░░░░░░░░░░▒░░░░░░░       ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓     ░░░░░░░░░░░░░░░░        ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/pluto b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/pluto new file mode 100644 index 00000000..cfcde6ff --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/pluto @@ -0,0 +1,39 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▒▒▒▒▒▒▒░░░░ ░░░▒░▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▒▒▒▒░░ ░░░░░░ ░░░▒▒▒░▒▒▒▒▒▒▒▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒  ░░  ░     ░░░ ░ ░▒▒▒▒▒▒▒▓▓▒▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▒▒▒░           ░        ░░▒▒▒▒▒▒▓▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒░                  ░░░▒▒░▒▒▒▒▒▓▓▓▒▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▒░░     ▒   ░       ░░░░░░▒▒▒▒▒▓▓▓▓▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▒▒▒░░░▒ ░░▒▒▒░░      ░░░░░░▒▒▒▒▒▓▓▓▓▓▓▓▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▒▒░▒▒▒▒▒▒▒▒▒▒░░░░░░▒▒░▒▒▒▒▒▒▒▒▓▓▒▓▓▓▓▓▓▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▒▓░▒▒▒▒▒▒▒▒░░▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▓▓▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▒▒▒▒▓▒▒▒▒░░▒ ░░▒▒▒▒▒▓▓▒▓▒▒▒▒▒▒▓▓▓▓▓▓▓▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▒▓▒▒▒▒▒       ░▒▒▒▓▓▒▓▓▒▒▒▒▒▓▒▒▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▓▓▓▓▓▒▓▓▒▒▒▒ ░         ░▒▓▒▒▒▒▒▒▒░▒▓▒▒▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓▒▓▓▓▓▒▒▓▓▒░             ░▒░░░▒▒▒▒▒▒▒▓▓▓▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▒▓▓▒▒▓▓▓▓▓░░░░          ░░░░ ░▒▒▒▒░▓▒▒▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░░▒ ▒▓▓▒▓▓▒░         ░░░░░░░▒▒▒▒▒▒▓▓▓▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒      ▒▒▒ ▒▒▒▒░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▓▓▓▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒          ▒▓▒▒▒░░░░░▒▒▒░▒▒▒▒▒▒▒▓▓▓▓▓▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ▒ ▒   ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓░░▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒  ▒       ▓▓▓▓▒▒▒▒▒▒▒▓▓▓▓▓▓░      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▒░▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒░▒░     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▒▒▒▒▒    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓▓▓▒▒▓▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/saturn b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/saturn new file mode 100644 index 00000000..68a7ffae --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/saturn @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒  ▒ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒░▒▒▒▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒ ▒▒▓▒▓▓▒▓░▓░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒    ░▒▒▒ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░▒▒  ▓▓  ▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░▒ ▓▓▓▓▓▓  ▒▓▓▓▓▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▒ ▒▒▓▓▒▓▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ▒▒ ▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▓▒▒ ▒▓▓▒▒▒▓▒░▓▒▓▒▓▒▓░▒▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒░▓▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒▒░▒▓▓▓▓▓▓▓▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒  ▓▒▒▒ ▒░░░░░▒▒▒▒▒▒▒▒▒▒░░▓▓▓▓▓▓▓▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░  ▒▓▒▒▒▒░▒░░░░▒▒▒▒▒▒▒▒▒▒▒░▒▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▒  ▓▓▒▒▒▒▒░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒     ▓▓▒▒▒░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▓  ░▒▒▓▒▒▒▓▒░░░░░▒▒▒▒▒▒▒▓▒▓▒▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▓▒  ▒▓▒▒▒▒▒░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓░░▒▒▓▒▒▒ ░▒▒▒▒░▒▒▒▒▒▓▒▒▒▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒░  ▓ ░▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▓▓▒▓   ▓▒▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓░▓░  ▓▓▓▓▓▒▒▒░▒▓▒▒▒▒▓▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▒ ▒▓▒▒▒▓▒▒▒▒▒▓▒▒▒▒▒▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▒▒▒▒▒▓▒▓▓▒▒▒▒░░▓▓▓▓▓ ▓▓ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒▓▓▒▓▒ ▓▓▓▓   ▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓      ▒▒░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒   ░▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▒▓▓▓▒▒░ ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░▒▒▒▒▒░▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▒▒░▒▒ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/uranus b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/uranus new file mode 100644 index 00000000..f5a8b36a --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/uranus @@ -0,0 +1,39 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓       ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░▒▓▓▓▓▓▓▒▒░     ░░▒▒▓▓▓▓░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░▒▓▓▓▓▓▓▒░              ▒▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓▓▓▓▓▓▓▓░                 ░▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▒▓▓▓▒▒▓▓▓░                   ░▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▒▒▓▒▒▒▒▒▒▒▓ ▓▓▓▓▓▓▓▓▓           ▒▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▓▓▒▒▒▒▒▒▒░░░░░  ▓▓▓▓▓▓▓▓▓▓          ▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▒▒▒░▒░░░░░░    ▓▓▓▓▓▓▓▓▓▓          ▒▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▓▓▒▒▒░░░░░░░      ▓▓▓▓▓▓▓▓▓▓▓       ▒▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓▓▒▒▒▒░░░░░░░         ▓▓▓▓▓▓▓▓▓      ▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▓▓▒▒▒░░ ░░                ▓▓▓▓    ░▒▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒▒▒░░░                    ░▒▒▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░░▒▒▒░░░░░            ░░░░░░░▒▒▒▒▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒░░  ░▒▒░░░░░░            ░░░░░░░▒▒▒▓▓▓▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒░░   ░▒▒░░░░    ░    ░  ░░░░░░▒▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░     ░▒░░░░░░░ ░░░░░░░░░░░░▒▒▒▒▓▓▒▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░      ░▒░░░░░░░░░░░░░░░░▒▒▒▒▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░       ░░▒▒▒▒░▒░▒▒▒▒▒▒▒▒▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░         ░▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒░░         ░░▒▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/venus b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/venus new file mode 100644 index 00000000..b95aff1b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/data/memes/planets/venus @@ -0,0 +1,35 @@ +                          ▓   ▓  ░░        ░░░░ ▓  ▓                             +                           ▒   ░        ░░░░░░  ░   ▒                            +                  ▓ ▓▓ ░  ░      ░░░     ░  ░░   ░ ░ ░  ▒    ▓                   +                ▓   ░░░  ░      ░     ░ ░             ░░   ░                     +                 ▓ ░░░   ░      ░  ░ ░ ░         ░ ░  ▒ ░ ░ ░░   ▓▓              +              ▓    ░  ░░ ░       ░░   ░                        ░                 +             ▓░  ░░ ░ ░ ░  ░░  ░  ░ ░░  ░    ░  ░     ░          ▒               +          ▓▓ ░   ░  ░    ░  ░ ░    ░      ░ ░ ░    ░     ░ ░░ ░                  +          ▓  ░  ░  ░░  ░ ▒  ░ ░░░░        ▒  ░░ ░  ░░  ░░   ░         ▓          +           ░ ░  ░ ░░░   ░░  ░  ▒▒    ░  ░ ░   ░             ▒ ░  ░  ░            +        ▓     ░ ░ ░    ░       ░░      ░░▒     ░░▒▒          ░ ░  ░    ▒         +        ▓░   ░    ░  ░     ░  ░   ░   ░             ░  ░░       ▒  ░   ▓         +         ░     ░ ░    ░ ░  ░            ░░      ░  ░░░░░         ░               +        ░  ░     ░  ░░    ░ ░░   ░    ░ ░     ░░  ░ ▒  ░ ░  ░ ░  ░░    ░         +    ▓                   ░   ░ ▓░     ░           ░ ░         ░     ░  ░░         +    ▓ ▓░          ░  ░ ░░    ░  ░ ░  ░░░        ░░░             ░░               +       ▒     ░░ ░ ░  ░░  ░ ░ ░░           ░        ░    ░           ░ ░  ▓       +                  ░   ░░░░ ░  ░ ░  ░               ░                ░░   ▓       +        ░ ░      ░     ░    ░           ░     ░         ░           ░░           +     ▓ ▓     ░  ░ ░         ░            ░          ░      ░ ░          ▓        +         ░        ░    ░        ░    ░   ░         ░  ░                ▒         +        ▓ ░ ░  ░░   ░  ░    ░   ░  ░░   ░       ░                      ▓         +        ▓    ░  ░          ░   ░░ ░  ░░  ░ ░   ░░ ░              ░               +              ░  ░░     ░ ░░          ░   ░   ░░                ░   ░            +             ░    ░  ░  ░ ░    ░    ░ ░ ░░░░ ░                                   +             ░ ░       ░ ░      ░ ░░  ░   ░ ░   ░       ░░        ░▓             +              ▒░░       ░           ░        ░   ░   ░          ░░  ▓            +                ░                    ░░        ░    ░░ ░       ░                 +                  ▒ ░         ░        ░   ░      ░       ░  ░                   +                    ▒  ░ ░        ░    ░  ░     ░           ▓                    +                    ▓  ░░  ░        ░░  ░     ░  ░  ░   ░                        +                     ▓▓    ▒░      ░           ░   ░░   ▓                        +                              ▓  ░░░░       ░░   ▓                               + + diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/doc/secret.txt b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/doc/secret.txt new file mode 100644 index 00000000..e872ca45 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/doc/secret.txt @@ -0,0 +1,32 @@ +================================================================================ + *telescope.theprimeagen* + +To The Viewers: ~ + +Oh why hello, I didn't see you there. So nice of you to join us. The Primeagen +must have sent you here. + +The places you want to look for help are: (you can do `:help ` below) + - |telescope.nvim| + - |telescope.setup| + - |telescope.builtin| + - |telescope.layout| + - |telescope.actions| + +I hope you enjoy telescope & Neovim. May your programming always be fun and +your vimming be quick. + + + +To The Primeagen: ~ + +Cyrnfr ernq guvf uryc znahny orsber pnyyvat zr ng 3 NZ jvgu gryrfpbcr +rzretrapvrf. V xabj ynfg gvzr jr fnirq gur ragver fgernzvat vaqhfgel, ohg +V unir n lbhat fba jub xrrcf zr hc ng avtug nyy ol uvzfrys. OGJ, unir lbh +pbafvqrerq fraqvat culfvpny QIQf sbe znkvzhz dhnyvgl naq rneyl npprff gb arj +pbagrag? Vg frrzf yvxr vg pbhyq or n cerggl pbby vqrn. + +#FunzryrffFrysCebzbgvba: uggcf://tvguho.pbz/fcbafbef/gwqrievrf + + + vim:tw=78:ts=8:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/doc/telescope.txt b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/doc/telescope.txt new file mode 100644 index 00000000..f63162d6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/doc/telescope.txt @@ -0,0 +1,4171 @@ +================================================================================ +INTRODUCTION *telescope.nvim* + +Telescope.nvim is a plugin for fuzzy finding and neovim. It helps you search, +filter, find and pick things in Lua. + +Getting started with telescope: + 1. Run `:checkhealth telescope` to make sure everything is installed. + 2. Evaluate it is working with `:Telescope find_files` or `:lua + require("telescope.builtin").find_files()` + 3. Put a `require("telescope").setup()` call somewhere in your neovim config. + 4. Read |telescope.setup| to check what config keys are available and what + you can put inside the setup call + 5. Read |telescope.builtin| to check which builtin pickers are offered and + what options these implement + 6. Profit + +The below flow chart illustrates a simplified telescope architecture: +┌───────────────────────────────────────────────────────────┐ +│ ┌────────┐ │ +│ │ Multi │ ┌───────+ │ +│ │ Select │ ┌───────┐ │ Entry │ │ +│ └─────┬──* │ Entry │ ┌────────+ │ Maker │ │ +│ │ ┌───│Manager│────│ Sorter │┐ └───┬───* │ +│ ▼ ▼ └───────* └────────┘│ │ │ +│ 1────────┐ 2───┴──┐ │ │ +│ ┌─────│ Picker │ │Finder│◀────┘ │ +│ ▼ └───┬────┘ └──────* │ +│ ┌────────┐ │ 3────────+ ▲ │ +│ │Selected│ └───────│ Prompt │─────────┘ │ +│ │ Entry │ └───┬────┘ │ +│ └────────* ┌───┴────┐ ┌────────┐ ┌────────┐ │ +│ │ ▲ 4─────────┐│ Prompt │ │(Attach)│ │Actions │ │ +│ ▼ └──▶ │ Results ││ Buffer │◀─┤Mappings│◀─┤User Fn │ │ +│5─────────┐ └─────────┘└────────┘ └────────┘ └────────┘ │ +││Previewer│ │ +│└─────────┘ telescope.nvim architecture │ +└───────────────────────────────────────────────────────────┘ + + + The `Entry Maker` at least defines + - value: "raw" result of the finder + - ordinal: string to be sorted derived from value + - display: line representation of entry in results buffer + + * The finder, entry manager, selected entry, and multi selections + comprises `entries` constructed by the `Entry Maker` from + raw results of the finder (`value`s) + + Primary components: + 1 Picker: central UI dedicated to varying use cases + (finding files, grepping, diagnostics, etc.) + see :h telescope.builtin + 2 Finder: pipe or interactively generates results to pick over + 3 Prompt: user input that triggers the finder which sorts results + in order into the entry manager + 4 Results: listed entries scored by sorter from finder results + 5 Previewer: preview of context of selected entry + see :h telescope.previewers + +A practical introduction into telescope customization is our `developers.md` +(top-level of repo) and `:h telescope.actions` that showcase how to access +information about the state of the picker (current selection, etc.). +To find out more: +https://github.com/nvim-telescope/telescope.nvim + + :h telescope.setup + :h telescope.command + :h telescope.builtin + :h telescope.themes + :h telescope.layout + :h telescope.resolve + :h telescope.actions + :h telescope.actions.state + :h telescope.actions.set + :h telescope.actions.utils + :h telescope.actions.generate + :h telescope.actions.history + :h telescope.previewers + +telescope.setup({opts}) *telescope.setup()* + Setup function to be run by user. Configures the defaults, pickers and + extensions of telescope. + + Usage: + > + require('telescope').setup{ + defaults = { + -- Default configuration for telescope goes here: + -- config_key = value, + -- .. + }, + pickers = { + -- Default configuration for builtin pickers goes here: + -- picker_name = { + -- picker_config_key = value, + -- ... + -- } + -- Now the picker_config_key will be applied every time you call this + -- builtin picker + }, + extensions = { + -- Your extension configuration goes here: + -- extension_name = { + -- extension_config_key = value, + -- } + -- please take a look at the readme of the extension you want to configure + } + } +< + + + Valid keys for {opts.defaults} + + *telescope.defaults.sorting_strategy* + sorting_strategy: ~ + Determines the direction "better" results are sorted towards. + + Available options are: + - "descending" (default) + - "ascending" + + *telescope.defaults.selection_strategy* + selection_strategy: ~ + Determines how the cursor acts after each sort iteration. + + Available options are: + - "reset" (default) + - "follow" + - "row" + - "closest" + - "none" + + *telescope.defaults.scroll_strategy* + scroll_strategy: ~ + Determines what happens if you try to scroll past the view of the + picker. + + Available options are: + - "cycle" (default) + - "limit" + + *telescope.defaults.layout_strategy* + layout_strategy: ~ + Determines the default layout of Telescope pickers. + See |telescope.layout| for details of the available strategies. + + Default: 'horizontal' + + *telescope.defaults.create_layout* + create_layout: ~ + Configure the layout of Telescope pickers. + See |telescope.pickers.layout| for details. + + Default: 'nil' + + *telescope.defaults.layout_config* + layout_config: ~ + Determines the default configuration values for layout strategies. + See |telescope.layout| for details of the configurations options for + each strategy. + + Allows setting defaults for all strategies as top level options and + for overriding for specific options. + For example, the default values below set the default width to 80% of + the screen width for all strategies except 'center', which has width + of 50% of the screen width. + + Default: { + bottom_pane = { + height = 25, + preview_cutoff = 120, + prompt_position = "top" + }, + center = { + height = 0.4, + preview_cutoff = 40, + prompt_position = "top", + width = 0.5 + }, + cursor = { + height = 0.9, + preview_cutoff = 40, + width = 0.8 + }, + horizontal = { + height = 0.9, + preview_cutoff = 120, + prompt_position = "bottom", + width = 0.8 + }, + vertical = { + height = 0.9, + preview_cutoff = 40, + prompt_position = "bottom", + width = 0.8 + } + } + + + *telescope.defaults.cycle_layout_list* + cycle_layout_list: ~ + Determines the layouts to cycle through when using `actions.layout.cycle_layout_next` + and `actions.layout.cycle_layout_prev`. + Should be a list of "layout setups". + Each "layout setup" can take one of two forms: + 1. string + This is interpreted as the name of a `layout_strategy` + 2. table + A table with possible keys `layout_strategy`, `layout_config` and `previewer` + + Default: { "horizontal", "vertical" } + + + *telescope.defaults.winblend* + winblend: ~ + Configure winblend for telescope floating windows. See |winblend| for + more information. Type can be a number or a function returning a + number + + Default: function() return vim.o.winblend end + + *telescope.defaults.wrap_results* + wrap_results: ~ + Word wrap the search results + + Default: false + + *telescope.defaults.prompt_prefix* + prompt_prefix: ~ + The character(s) that will be shown in front of Telescope's prompt. + + Default: '> ' + + *telescope.defaults.selection_caret* + selection_caret: ~ + The character(s) that will be shown in front of the current selection. + + Default: '> ' + + *telescope.defaults.entry_prefix* + entry_prefix: ~ + Prefix in front of each result entry. Current selection not included. + + Default: ' ' + + *telescope.defaults.multi_icon* + multi_icon: ~ + Symbol to add in front of a multi-selected result entry. + Replaces final character of |telescope.defaults.selection_caret| and + |telescope.defaults.entry_prefix| as appropriate. + To have no icon, set to the empty string. + + Default: '+' + + *telescope.defaults.initial_mode* + initial_mode: ~ + Determines in which mode telescope starts. Valid Keys: + `insert` and `normal`. + + Default: "insert" + + *telescope.defaults.border* + border: ~ + Boolean defining if borders are added to Telescope windows. + + Default: true + + *telescope.defaults.path_display* + path_display: ~ + Determines how file paths are displayed. + + path_display can be set to an array with a combination of: + - "hidden" hide file names + - "tail" only display the file name, and not the path + - "absolute" display absolute paths + - "smart" remove as much from the path as possible to only show + the difference between the displayed paths. + Warning: The nature of the algorithm might have a negative + performance impact! + - "shorten" only display the first character of each directory in + the path + - "truncate" truncates the start of the path when the whole path will + not fit. To increase the gap between the path and the edge, + set truncate to number `truncate = 3` + - "filename_first" shows filenames first and then the directories + + You can also specify the number of characters of each directory name + to keep by setting `path_display.shorten = num`. + e.g. for a path like + `alpha/beta/gamma/delta.txt` + setting `path_display.shorten = 1` will give a path like: + `a/b/g/delta.txt` + Similarly, `path_display.shorten = 2` will give a path like: + `al/be/ga/delta.txt` + + You can also further customise the shortening behaviour by + setting `path_display.shorten = { len = num, exclude = list }`, + where `len` acts as above, and `exclude` is a list of positions + that are not shortened. Negative numbers in the list are considered + relative to the end of the path. + e.g. for a path like + `alpha/beta/gamma/delta.txt` + setting `path_display.shorten = { len = 1, exclude = {1, -1} }` + will give a path like: + `alpha/b/g/delta.txt` + setting `path_display.shorten = { len = 2, exclude = {2, -2} }` + will give a path like: + `al/beta/gamma/de` + + path_display can also be set to 'filename_first' to put the filename + in front. + + path_display = { + "filename_first" + }, + + The directory structure can be reversed as follows: + + path_display = { + filename_first = { + reverse_directories = true + } + }, + + path_display can also be set to 'hidden' string to hide file names + + path_display can also be set to a function for custom formatting of + the path display with the following signature + + Signature: fun(opts: table, path: string): string, table? + + The optional table is an list of positions and highlight groups to + set the highlighting of the return path string. + + Example: + + -- Format path as "file.txt (path\to\file\)" + path_display = function(opts, path) + local tail = require("telescope.utils").path_tail(path) + return string.format("%s (%s)", tail, path) + end, + + -- Format path and add custom highlighting + path_display = function(opts, path) + local tail = require("telescope.utils").path_tail(path) + path = string.format("%s (%s)", tail, path) + + local highlights = { + { + { + 0, -- highlight start position + #path, -- highlight end position + }, + "Comment", -- highlight group name + }, + } + + return path, highlights + end + + Default: {} + + *telescope.defaults.borderchars* + borderchars: ~ + Set the borderchars of telescope floating windows. It has to be a + table of 8 string values. + + Default: { "─", "│", "─", "│", "╭", "╮", "╯", "╰" } + + *telescope.defaults.get_status_text* + get_status_text: ~ + A function that determines what the virtual text looks like. + Signature: function(picker) -> str + + Default: function that shows current count / all + + *telescope.defaults.hl_result_eol* + hl_result_eol: ~ + Changes if the highlight for the selected item in the results + window is always the full width of the window + + Default: true + + *telescope.defaults.dynamic_preview_title* + dynamic_preview_title: ~ + Will change the title of the preview window dynamically, where it + is supported. For example, the preview window's title could show up as + the full filename. + + Default: false + + *telescope.defaults.results_title* + results_title: ~ + Defines the default title of the results window. A false value + can be used to hide the title altogether. + + Default: "Results" + + *telescope.defaults.prompt_title* + prompt_title: ~ + Defines the default title of the prompt window. A false value + can be used to hide the title altogether. Most of the times builtins + define a prompt_title which will be preferred over this default. + + Default: "Prompt" + + *telescope.defaults.mappings* + mappings: ~ + Your mappings to override telescope's default mappings. + + See: ~ + |telescope.mappings| + + + *telescope.defaults.default_mappings* + default_mappings: ~ + Not recommended to use except for advanced users. + + Will allow you to completely remove all of telescope's default maps + and use your own. + + Default: nil + + + *telescope.defaults.history* + history: ~ + This field handles the configuration for prompt history. + By default it is a table, with default values (more below). + To disable history, set it to false. + + Currently mappings still need to be added, Example: + mappings = { + i = { + [""] = require('telescope.actions').cycle_history_next, + [""] = require('telescope.actions').cycle_history_prev, + }, + }, + + Fields: + - path: The path to the telescope history as string. + Default: stdpath("data")/telescope_history + - limit: The amount of entries that will be written in the + history. + Warning: If limit is set to nil it will grow unbound. + Default: 100 + - handler: A lua function that implements the history. + This is meant as a developer setting for extensions to + override the history handling, e.g., + https://github.com/nvim-telescope/telescope-smart-history.nvim, + which allows context sensitive (cwd + picker) history. + + Default: + require('telescope.actions.history').get_simple_history + - cycle_wrap: Indicates whether the cycle_history_next and + cycle_history_prev functions should wrap around to the + beginning or end of the history entries on reaching + their respective ends + Default: false + + *telescope.defaults.cache_picker* + cache_picker: ~ + This field handles the configuration for picker caching. + By default it is a table, with default values (more below). + To disable caching, set it to false. + + Caching preserves all previous multi selections and results and + therefore may result in slowdown or increased RAM occupation + if too many pickers (`cache_picker.num_pickers`) or entries + ('cache_picker.limit_entries`) are cached. + + Fields: + - num_pickers: The number of pickers to be cached. + Set to -1 to preserve all pickers of your + session. If passed to a picker, the cached + pickers with indices larger than + `cache_picker.num_pickers` will be cleared. + Default: 1 + - limit_entries: The amount of entries that will be saved for + each picker. + Default: 1000 + - ignore_empty_prompt: If true, the picker will not be cached if + the prompt is empty (i.e., no text has been + typed at the time of closing the prompt). + Default: false + + + *telescope.defaults.preview* + preview: ~ + This field handles the global configuration for previewers. + By default it is a table, with default values (more below). + To disable previewing, set it to false. If you have disabled previewers + globally, but want to opt in to previewing for single pickers, you will have to + pass `preview = true` or `preview = {...}` (your config) to the `opts` of + your picker. + + Fields: + - check_mime_type: Use `file` if available to try to infer whether the + file to preview is a binary if filetype + detection fails. + Windows users get `file` from: + https://github.com/julian-r/file-windows + Set to false to attempt to preview any mime type. + Default: true for all OS excl. Windows + - filesize_limit: The maximum file size in MB attempted to be previewed. + Set to false to attempt to preview any file size. + Default: 25 + - highlight_limit: The maximum file size in MB attempted to be highlighted. + Set to false to attempt to highlight any file size. + Default: 1 + - timeout: Timeout the previewer if the preview did not + complete within `timeout` milliseconds. + Set to false to not timeout preview. + Default: 250 + - hook(s): Function(s) that takes `(filepath, bufnr, opts)`, where opts + exposes winid and ft (filetype). + Available hooks (in order of priority): + {filetype, mime, filesize, timeout}_hook + Important: the filetype_hook must return true or false + to indicate whether to continue (true) previewing or not (false), + respectively. + Two examples: + local putils = require("telescope.previewers.utils") + ... -- preview is called in telescope.setup { ... } + preview = { + -- 1) Do not show previewer for certain files + filetype_hook = function(filepath, bufnr, opts) + -- you could analogously check opts.ft for filetypes + local excluded = vim.tbl_filter(function(ending) + return filepath:match(ending) + end, { + ".*%.csv", + ".*%.toml", + }) + if not vim.tbl_isempty(excluded) then + putils.set_preview_message( + bufnr, + opts.winid, + string.format("I don't like %s files!", + excluded[1]:sub(5, -1)) + ) + return false + end + return true + end, + -- 2) Truncate lines to preview window for too large files + filesize_hook = function(filepath, bufnr, opts) + local path = require("plenary.path"):new(filepath) + -- opts exposes winid + local height = vim.api.nvim_win_get_height(opts.winid) + local lines = vim.split(path:head(height), "[\r]?\n") + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + end, + } + The configuration recipes for relevant examples. + Note: we use vim.filetype filetype detection, + so if you have troubles with files not + highlighting correctly, please read + |vim.filetype| + Default: nil + - treesitter: Determines whether the previewer performs treesitter + highlighting, which falls back to regex-based highlighting. + `true`: treesitter highlighting for all available filetypes + `false`: regex-based highlighting for all filetypes + `table`: may contain the following keys: + - enable boolean|table: if boolean, enable ts + highlighting for all supported + filetypes. + if table, ts highlighting is only + enabled for given filetypes. + - disable table: list of filetypes for which ts highlighting + is not used if `enable = true`. + Default: true + - msg_bg_fillchar: Character to fill background of unpreviewable buffers with + Default: "╱" + - hide_on_startup: Hide previewer when picker starts. Previewer can be toggled + with actions.layout.toggle_preview. + Default: false + - ls_short: Determines whether to use the `--short` flag for the `ls` + command when previewing directories. Otherwise will result + to using `--long`. + Default: false + + + *telescope.defaults.vimgrep_arguments* + vimgrep_arguments: ~ + Defines the command that will be used for `live_grep` and `grep_string` + pickers. + Hint: Make sure that color is currently set to `never` because we do + not yet interpret color codes + Hint 2: Make sure that these options are in your changes arguments: + "--no-heading", "--with-filename", "--line-number", "--column" + because we need them so the ripgrep output is in the correct format. + + Default: { + "rg", + "--color=never", + "--no-heading", + "--with-filename", + "--line-number", + "--column", + "--smart-case" + } + + *telescope.defaults.use_less* + use_less: ~ + Boolean if less should be enabled in term_previewer (deprecated and + currently no longer used in the builtin pickers). + + Default: true + + *telescope.defaults.set_env* + set_env: ~ + Set an environment for term_previewer. A table of key values: + Example: { COLORTERM = "truecolor", ... } + Hint: Empty table is not allowed. + + Default: nil + + *telescope.defaults.color_devicons* + color_devicons: ~ + Boolean if devicons should be enabled or not. If set to false, the + text highlight group is used. + Hint: Coloring only works if |termguicolors| is enabled. + + Default: true + + *telescope.defaults.file_sorter* + file_sorter: ~ + A function pointer that specifies the file_sorter. This sorter will + be used for find_files, git_files and similar. + Hint: If you load a native sorter, you don't need to change this value, + the native sorter will override it anyway. + + Default: require("telescope.sorters").get_fzy_sorter + + *telescope.defaults.generic_sorter* + generic_sorter: ~ + A function pointer to the generic sorter. The sorter that should be + used for everything that is not a file. + Hint: If you load a native sorter, you don't need to change this value, + the native sorter will override it anyway. + + Default: require("telescope.sorters").get_fzy_sorter + + *telescope.defaults.prefilter_sorter* + prefilter_sorter: ~ + This points to a wrapper sorter around the generic_sorter that is able + to do prefiltering. + It's usually used for lsp_*_symbols and lsp_*_diagnostics + + Default: require("telescope.sorters").prefilter + + *telescope.defaults.tiebreak* + tiebreak: ~ + A function that determines how to break a tie when two entries have + the same score. + Having a function that always returns false would keep the entries in + the order they are found, so existing_entry before current_entry. + Vice versa always returning true would place the current_entry + before the existing_entry. + + Signature: function(current_entry, existing_entry, prompt) -> boolean + + Default: function that breaks the tie based on the length of the + entry's ordinal + + *telescope.defaults.file_ignore_patterns* + file_ignore_patterns: ~ + A table of lua regex that define the files that should be ignored. + Example: { "^scratch/" } -- ignore all files in scratch directory + Example: { "%.npz" } -- ignore all npz files + See: https://www.lua.org/manual/5.1/manual.html#5.4.1 for more + information about lua regex + Note: `file_ignore_patterns` will be used in all pickers that have a + file associated. This might lead to the problem that lsp_ pickers + aren't displaying results because they might be ignored by + `file_ignore_patterns`. For example, setting up node_modules as ignored + will never show node_modules in any results, even if you are + interested in lsp_ results. + + If you only want `file_ignore_patterns` for `find_files` and + `grep_string`/`live_grep` it is suggested that you setup `gitignore` + and have fd and or ripgrep installed because both tools will not show + `gitignore`d files on default. + + Default: nil + + *telescope.defaults.get_selection_window* + get_selection_window: ~ + Function that takes function(picker, entry) and returns a window id. + The window ID will be used to decide what window the chosen file will + be opened in and the cursor placed in upon leaving the picker. + + Default: `function() return 0 end` + + + *telescope.defaults.git_worktrees* + git_worktrees: ~ + A table of arrays of detached working trees with keys `gitdir` and `toplevel`. + Used to pass `--git-dir` and `--work-tree` flags to git commands when telescope fails + to infer the top-level directory of a given working tree based on cwd. + Example: + git_worktrees = { + { + toplevel = vim.env.HOME, + gitdir = vim.env.HOME .. '/.cfg' + } + } + + Default: nil + + + *telescope.defaults.file_previewer* + file_previewer: ~ + Function pointer to the default file_previewer. It is mostly used + for find_files, git_files and similar. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").cat.new + + Default: require("telescope.previewers").vim_buffer_cat.new + + *telescope.defaults.grep_previewer* + grep_previewer: ~ + Function pointer to the default vim_grep previewer. It is mostly + used for live_grep, grep_string and similar. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").vimgrep.new + + Default: require("telescope.previewers").vim_buffer_vimgrep.new + + *telescope.defaults.qflist_previewer* + qflist_previewer: ~ + Function pointer to the default qflist previewer. It is mostly + used for qflist, loclist and lsp. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").qflist.new + + Default: require("telescope.previewers").vim_buffer_qflist.new + + *telescope.defaults.buffer_previewer_maker* + buffer_previewer_maker: ~ + Developer option that defines the underlining functionality + of the buffer previewer. + For interesting configuration examples take a look at + https://github.com/nvim-telescope/telescope.nvim/wiki/Configuration-Recipes + + Default: require("telescope.previewers").buffer_previewer_maker + + Parameters: ~ + {opts} (table) Configuration opts. Keys: defaults, pickers, + extensions + + +telescope.load_extension({name}) *telescope.load_extension()* + Load an extension. + - Notes: + - Loading triggers ext setup via the config passed in |telescope.setup| + + + Parameters: ~ + {name} (string) Name of the extension + + +telescope.register_extension({mod}) *telescope.register_extension()* + Register an extension. To be used by plugin authors. + + + Parameters: ~ + {mod} (table) Module + + +telescope.extensions() *telescope.extensions()* + Use telescope.extensions to reference any extensions within your + configuration. + While the docs currently generate this as a function, it's actually a + table. Sorry. + + + + +================================================================================ +COMMAND *telescope.command* + +Telescope commands can be called through two apis, the lua api and the viml +api. + +The lua api is the more direct way to interact with Telescope, as you directly +call the lua functions that Telescope defines. It can be called in a lua file +using commands like: +`require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})` +If you want to use this api from a vim file you should prepend `lua` to the +command, as below: +`lua require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})` +If you want to use this api from a neovim command line you should prepend +`:lua` to the command, as below: +`:lua require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})` + +The viml api is more indirect, as first the command must be parsed to the +relevant lua equivalent, which brings some limitations. The viml api can be +called using commands like: +`:Telescope find_files hidden=true layout_config={"prompt_position":"top"}` +This involves setting options using an `=` and using viml syntax for lists and +dictionaries when the corresponding lua function requires a table. + +One limitation of the viml api is that there can be no spaces in any of the +options. For example, if you want to use the `cwd` option for `find_files` to +specify that you only want to search within the folder `/foo bar/subfolder/` +you could not do that using the viml api, as the path name contains a space. +Similarly, you could NOT set the `prompt_position` to `"top"` using the +following command: +`:Telescope find_files layout_config={ "prompt_position" : "top" }` +as there are spaces in the option. + + + +================================================================================ +BUILTIN *telescope.builtin* + +Telescope Builtins is a collection of community maintained pickers to support +common workflows. It can be used as reference when writing PRs, Telescope +extensions, your own custom pickers, or just as a discovery tool for all of the +amazing pickers already shipped with Telescope! + +Any of these functions can just be called directly by doing: + +:lua require('telescope.builtin').$NAME_OF_PICKER() + +To use any of Telescope's default options or any picker-specific options, call +your desired picker by passing a lua table to the picker with all of the +options you want to use. Here's an example with the live_grep picker: + +> + :lua require('telescope.builtin').live_grep({ + prompt_title = 'find string in open buffers...', + grep_open_files = true + }) + -- or with dropdown theme + :lua require('telescope.builtin').find_files(require('telescope.themes').get_dropdown{ + previewer = false + }) +< + +builtin.live_grep({opts}) *telescope.builtin.live_grep()* + Search for a string and get results live as you type, respects .gitignore + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from + (default: cwd, use + utils.buffer_dir() to search + relative to open buffer) + {grep_open_files} (boolean) if true, restrict search to + open files only, mutually + exclusive with `search_dirs` + {search_dirs} (table) directory/directories/files to + search, mutually exclusive + with `grep_open_files` + {glob_pattern} (string|table) argument to be used with + `--glob`, e.g. "*.toml", can + use the opposite "!*.toml" + {type_filter} (string) argument to be used with + `--type`, e.g. "rust", see `rg + --type-list` + {additional_args} (function|table) additional arguments to be + passed on. Can be fn(opts) -> + tbl + {max_results} (number) define a upper result value + {disable_coordinates} (boolean) don't show the line & row + numbers (default: false) + {file_encoding} (string) file encoding for the entry & + previewer + + +builtin.grep_string({opts}) *telescope.builtin.grep_string()* + Searches for the string under your cursor or the visual selection in your + current working directory + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from + (default: cwd, use + utils.buffer_dir() to search + relative to open buffer) + {search} (string) the query to search + {grep_open_files} (boolean) if true, restrict search to + open files only, mutually + exclusive with `search_dirs` + {search_dirs} (table) directory/directories/files to + search, mutually exclusive + with `grep_open_files` + {use_regex} (boolean) if true, special characters + won't be escaped, allows for + using regex (default: false) + {word_match} (string) can be set to `-w` to enable + exact word matches + {additional_args} (function|table) additional arguments to be + passed on. Can be fn(opts) -> + tbl + {disable_coordinates} (boolean) don't show the line and row + numbers (default: false) + {only_sort_text} (boolean) only sort the text, not the + file, line or row (default: + false) + {file_encoding} (string) file encoding for the entry & + previewer + + +builtin.find_files({opts}) *telescope.builtin.find_files()* + Search for files (respecting .gitignore) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from + (default: cwd, use + utils.buffer_dir() to search + relative to open buffer) + {find_command} (function|table) cmd to use for the search. Can + be a fn(opts) -> tbl (default: + autodetect) + {file_entry_encoding} (string) encoding of output of + `find_command` + {follow} (boolean) if true, follows symlinks + (i.e. uses `-L` flag for the + `find` command) + {hidden} (boolean) determines whether to show + hidden files or not (default: + false) + {no_ignore} (boolean) show files ignored by + .gitignore, .ignore, etc. + (default: false) + {no_ignore_parent} (boolean) show files ignored by + .gitignore, .ignore, etc. in + parent dirs. (default: false) + {search_dirs} (table) directory/directories/files to + search + {search_file} (string) specify a filename to search + for + {file_encoding} (string) file encoding for the + previewer + + +builtin.fd() *telescope.builtin.fd()* + This is an alias for the `find_files` picker + + + +builtin.treesitter() *telescope.builtin.treesitter()* + Lists function names, variables, and other symbols from treesitter queries + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by kind of ts + node you want to see (i.e. `:var:`) + + + Options: ~ + {show_line} (boolean) if true, shows the row:column that + the result is found at (default: + true) + {bufnr} (number) specify the buffer number where + treesitter should run. (default: + current buffer) + {symbol_width} (number) defines the width of the symbol + section (default: 25) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.current_buffer_fuzzy_find({opts}) *telescope.builtin.current_buffer_fuzzy_find()* + Live fuzzy search inside of the currently open buffer + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {skip_empty_lines} (boolean) if true we don't display empty lines + (default: false) + {results_ts_highlight} (boolean) highlight result entries with + treesitter (default: true) + {file_encoding} (string) file encoding for the previewer + + +builtin.tags({opts}) *telescope.builtin.tags()* + Lists tags in current directory with tag location file preview (users are + required to run ctags -R to generate tags or update when introducing new + changes) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from (default: cwd, use + utils.buffer_dir() to search relative to + open buffer) + {ctags_file} (string) specify a particular ctags file to use + {show_line} (boolean) if true, shows the content of the line the + tag is found on in the picker (default: + true) + {only_sort_tags} (boolean) if true we will only sort tags (default: + false) + {fname_width} (number) defines the width of the filename section + (default: 30) + + +builtin.current_buffer_tags({opts}) *telescope.builtin.current_buffer_tags()* + Lists all of the tags for the currently open buffer, with a preview + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from (default: cwd, use + utils.buffer_dir() to search relative to + open buffer) + {ctags_file} (string) specify a particular ctags file to use + {show_line} (boolean) if true, shows the content of the line the + tag is found on in the picker (default: + true) + {only_sort_tags} (boolean) if true we will only sort tags (default: + false) + {fname_width} (number) defines the width of the filename section + (default: 30) + + +builtin.git_files({opts}) *telescope.builtin.git_files()* + Fuzzy search for files tracked by Git. This command lists the output of the + `git ls-files` command, respects .gitignore + - Default keymaps: + - ``: opens the currently selected file + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer + git root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or + the cwd (important for submodule) + (default: true) + {show_untracked} (boolean) if true, adds `--others` flag to + command and shows untracked files + (default: false) + {recurse_submodules} (boolean) if true, adds the + `--recurse-submodules` flag to command + (default: false) + {git_command} (table) command that will be executed. + {"git","ls-files","--exclude-standard","--cached"} + {file_encoding} (string) file encoding for the previewer + + +builtin.git_commits({opts}) *telescope.builtin.git_commits()* + Lists commits for current directory with diff preview + - Default keymaps: + - ``: checks out the currently selected commit + - `m`: resets current branch to selected commit using mixed mode + - `s`: resets current branch to selected commit using soft mode + - `h`: resets current branch to selected commit using hard mode + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {git_command} (table) command that will be executed. + {"git","log","--pretty=oneline","--abbrev-commit","--","."} + + +builtin.git_bcommits({opts}) *telescope.builtin.git_bcommits()* + Lists commits for current buffer with diff preview + - Default keymaps or your overridden `select_` keys: + - ``: checks out the currently selected commit + - ``: opens a diff in a vertical split + - ``: opens a diff in a horizontal split + - ``: opens a diff in a new tab + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {current_file} (string) specify the current file that should be + used for bcommits (default: current buffer) + {git_command} (table) command that will be executed. + {"git","log","--pretty=oneline","--abbrev-commit"} + + +builtin.git_bcommits_range({opts}) *telescope.builtin.git_bcommits_range()* + Lists commits for a range of lines in the current buffer with diff preview + In visual mode, lists commits for the selected lines With operator mode + enabled, lists commits inside the text object/motion + - Default keymaps or your overridden `select_` keys: + - ``: checks out the currently selected commit + - ``: opens a diff in a vertical split + - ``: opens a diff in a horizontal split + - ``: opens a diff in a new tab + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {current_file} (string) specify the current file that should be used + for bcommits (default: current buffer) + {git_command} (table) command that will be executed. the last + element must be "-L". + {"git","log","--pretty=oneline","--abbrev-commit","--no-patch","-L"} + {from} (number) the first line number in the range (default: + current line) + {to} (number) the last line number in the range (default: + the value of `from`) + {operator} (boolean) select lines in operator-pending mode + (default: false) + + +builtin.git_branches({opts}) *telescope.builtin.git_branches()* + List branches for current directory, with output from `git log --oneline` + shown in the preview window + - Default keymaps: + - ``: checks out the currently selected branch + - ``: tracks currently selected branch + - ``: rebases currently selected branch + - ``: creates a new branch, with confirmation prompt before creation + - ``: deletes the currently selected branch, with confirmation + prompt before deletion + - ``: merges the currently selected branch, with confirmation prompt + before deletion + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the + repo + {use_file_path} (boolean) if we should use the + current buffer git root + (default: false) + {use_git_root} (boolean) if we should use git root + as cwd or the cwd + (important for submodule) + (default: true) + {show_remote_tracking_branches} (boolean) show remote tracking + branches like origin/main + (default: true) + {pattern} (string) specify the pattern to + match all refs + + +builtin.git_status({opts}) *telescope.builtin.git_status()* + Lists git status for current directory + - Default keymaps: + - ``: stages or unstages the currently selected file + - ``: opens the currently selected file + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {git_icons} (table) string -> string. Matches name with icon + (see source code, make_entry.lua + git_icon_defaults) + {expand_dir} (boolean) pass flag `-uall` to show files in + untracked directories (default: true) + + +builtin.git_stash({opts}) *telescope.builtin.git_stash()* + Lists stash items in current repository + - Default keymaps: + - ``: runs `git apply` for currently selected stash + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {show_branch} (boolean) if we should display the branch name for + git stash entries (default: true) + + +builtin.builtin({opts}) *telescope.builtin.builtin()* + Lists all of the community maintained pickers built into Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {include_extensions} (boolean) if true will show the pickers of the + installed extensions (default: false) + {use_default_opts} (boolean) if the selected picker should use its + default options (default: false) + + +builtin.resume({opts}) *telescope.builtin.resume()* + Opens the previous picker in the identical state (incl. multi selections) + - Notes: + - Requires `cache_picker` in setup or when having invoked pickers, see + |telescope.defaults.cache_picker| + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cache_index} (number) what picker to resume, where 1 denotes most + recent (default: 1) + + +builtin.pickers({opts}) *telescope.builtin.pickers()* + Opens a picker over previously cached pickers in their preserved states + (incl. multi selections) + - Default keymaps: + - ``: delete the selected cached picker + - Notes: + - Requires `cache_picker` in setup or when having invoked pickers, see + |telescope.defaults.cache_picker| + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.planets({opts}) *telescope.builtin.planets()* + Use the telescope... + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_pluto} (boolean) we love Pluto (default: false, because its a + hidden feature) + {show_moon} (boolean) we love the Moon (default: false, because its + a hidden feature) + + +builtin.symbols({opts}) *telescope.builtin.symbols()* + Lists symbols inside of `data/telescope-sources/*.json` found in your + runtime path or found in `stdpath("data")/telescope/symbols/*.json`. The + second path can be customized. We provide a couple of default symbols which + can be found in https://github.com/nvim-telescope/telescope-symbols.nvim. + This repos README also provides more information about the format in which + the symbols have to be. + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {symbol_path} (string) specify the second path. Default: + `stdpath("data")/telescope/symbols/*.json` + {sources} (table) specify a table of sources you want to load + this time + + +builtin.commands({opts}) *telescope.builtin.commands()* + Lists available plugin/user commands and runs them on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_buf_command} (boolean) show buf local command (Default: true) + + +builtin.quickfix({opts}) *telescope.builtin.quickfix()* + Lists items in the quickfix list, jumps to location on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {nr} (number) specify the quickfix list number + + +builtin.quickfixhistory({opts}) *telescope.builtin.quickfixhistory()* + Lists all quickfix lists in your history and open them with + `builtin.quickfix`. It seems that neovim only keeps the full history for 10 + lists + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.loclist({opts}) *telescope.builtin.loclist()* + Lists items from the current window's location list, jumps to location on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + + +builtin.oldfiles({opts}) *telescope.builtin.oldfiles()* + Lists previously open files, opens on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify a working directory to filter + oldfiles by + {only_cwd} (boolean) show only files in the cwd (default: false) + {cwd_only} (boolean) alias for only_cwd + {file_encoding} (string) file encoding for the previewer + + +builtin.command_history({opts}) *telescope.builtin.command_history()* + Lists commands that were executed recently, and reruns them on `` + - Default keymaps: + - ``: open the command line with the text of the currently selected + result populated in it + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {filter_fn} (function) filter fn(cmd:string). true if the history + command should be presented. + + +builtin.search_history({opts}) *telescope.builtin.search_history()* + Lists searches that were executed recently, and reruns them on `` + - Default keymaps: + - ``: open a search window with the text of the currently selected + search result populated in it + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.vim_options({opts}) *telescope.builtin.vim_options()* + Lists vim options, allows you to edit the current value on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.help_tags({opts}) *telescope.builtin.help_tags()* + Lists available help tags and opens a new window with the relevant help + info on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {lang} (string) specify language (default: vim.o.helplang) + {fallback} (boolean) fallback to en if language isn't installed + (default: true) + + +builtin.man_pages({opts}) *telescope.builtin.man_pages()* + Lists manpage entries, opens them in a help window on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {sections} (table) a list of sections to search, use `{ "ALL" }` + to search in all sections (default: { "1" }) + {man_cmd} (function) that returns the man command. (Default: + `apropos ""` on linux, `apropos " "` on macos) + + +builtin.reloader({opts}) *telescope.builtin.reloader()* + Lists lua modules and reloads them on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {column_len} (number) define the max column len for the module name + (default: dynamic, longest module name) + + +builtin.buffers({opts}) *telescope.builtin.buffers()* + Lists open buffers in current neovim instance, opens selected buffer on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify a working directory to + filter buffers list by + {show_all_buffers} (boolean) if true, show all buffers, + including unloaded buffers + (default: true) + {ignore_current_buffer} (boolean) if true, don't show the current + buffer in the list (default: + false) + {only_cwd} (boolean) if true, only show buffers in the + current working directory + (default: false) + {cwd_only} (boolean) alias for only_cwd + {sort_lastused} (boolean) Sorts current and last buffer to + the top and selects the lastused + (default: false) + {sort_mru} (boolean) Sorts all buffers after most + recent used. Not just the current + and last one (default: false) + {bufnr_width} (number) Defines the width of the buffer + numbers in front of the filenames + (default: dynamic) + {file_encoding} (string) file encoding for the previewer + {sort_buffers} (function) sort fn(bufnr_a, bufnr_b). true if + bufnr_a should go first. Runs + after sorting by most recent (if + specified) + {select_current} (boolean) select current buffer (default: + false) + + +builtin.colorscheme({opts}) *telescope.builtin.colorscheme()* + Lists available colorschemes and applies them on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {colors} (table) a list of additional colorschemes to + explicitly make available to telescope + (default: {}) + {enable_preview} (boolean) if true, will preview the selected color + + +builtin.marks({opts}) *telescope.builtin.marks()* + Lists vim marks and their value, jumps to the mark on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {file_encoding} (string) file encoding for the previewer + {mark_type} (string) filter marks by type (default: "all", + options: "all"|"global"|"local") + + +builtin.registers({opts}) *telescope.builtin.registers()* + Lists vim registers, pastes the contents of the register on `` + - Default keymaps: + - ``: edit the contents of the currently selected register + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.keymaps({opts}) *telescope.builtin.keymaps()* + Lists normal mode keymappings, runs the selected keymap on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {modes} (table) a list of short-named keymap modes to search + (default: { "n", "i", "c", "x" }) + {show_plug} (boolean) if true, the keymaps for which the lhs + contains "" are also shown (default: + true) + {only_buf} (boolean) if true, only show the buffer-local keymaps + (default: false) + {lhs_filter} (function) filter(lhs:string) -> boolean. true for + keymap.lhs if the keymap should be shown + (optional) + {filter} (function) filter(km:keymap) -> boolean. true for the + keymap if it should be shown (optional) + + +builtin.filetypes({opts}) *telescope.builtin.filetypes()* + Lists all available filetypes, sets currently open buffer's filetype to + selected filetype in Telescope on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.highlights({opts}) *telescope.builtin.highlights()* + Lists all available highlights + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.autocommands({opts}) *telescope.builtin.autocommands()* + Lists vim autocommands and goes to their declaration on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.spell_suggest({opts}) *telescope.builtin.spell_suggest()* + Lists spelling suggestions for the current word under the cursor, replaces + word with selected suggestion on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.tagstack({opts}) *telescope.builtin.tagstack()* + Lists the tag stack for the current window, jumps to tag on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + + +builtin.jumplist({opts}) *telescope.builtin.jumplist()* + Lists items from Vim's jumplist, jumps to location on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + + +builtin.lsp_references({opts}) *telescope.builtin.lsp_references()* + Lists LSP references for word under the cursor, jumps to reference on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {include_declaration} (boolean) include symbol declaration in the + lsp references (default: true) + {include_current_line} (boolean) include current line (default: + false) + {jump_type} (string) how to goto reference if there is + only one and the definition file is + different from the current file, + values: "tab", "tab drop", "split", + "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_incoming_calls({opts}) *telescope.builtin.lsp_incoming_calls()* + Lists LSP incoming calls for word under the cursor, jumps to reference on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_outgoing_calls({opts}) *telescope.builtin.lsp_outgoing_calls()* + Lists LSP outgoing calls for word under the cursor, jumps to reference on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_definitions({opts}) *telescope.builtin.lsp_definitions()* + Goto the definition of the word under the cursor, if there's only one, + otherwise show all options in Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {jump_type} (string) how to goto definition if there is only one + and the definition file is different from + the current file, values: "tab", "tab + drop", "split", "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {reuse_win} (boolean) jump to existing window if buffer is + already opened (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_type_definitions({opts}) *telescope.builtin.lsp_type_definitions()* + Goto the definition of the type of the word under the cursor, if there's + only one, otherwise show all options in Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {jump_type} (string) how to goto definition if there is only one + and the definition file is different from + the current file, values: "tab", "tab + drop", "split", "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {reuse_win} (boolean) jump to existing window if buffer is + already opened (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_implementations({opts}) *telescope.builtin.lsp_implementations()* + Goto the implementation of the word under the cursor if there's only one, + otherwise show all options in Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {jump_type} (string) how to goto implementation if there is only + one and the definition file is different + from the current file, values: "tab", "tab + drop", "split", "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {reuse_win} (boolean) jump to existing window if buffer is + already opened (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_document_symbols({opts}) *telescope.builtin.lsp_document_symbols()* + Lists LSP document symbols in the current buffer + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by type of + symbol you want to see (i.e. `:variable:`) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {fname_width} (number) defines the width of the filename + section (default: 30) + {symbol_width} (number) defines the width of the symbol + section (default: 25) + {symbol_type_width} (number) defines the width of the symbol + type section (default: 8) + {show_line} (boolean) if true, shows the content of the + line the tag is found on (default: + false) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_workspace_symbols({opts}) *telescope.builtin.lsp_workspace_symbols()* + Lists LSP document symbols in the current workspace + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by type of + symbol you want to see (i.e. `:variable:`) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {query} (string) for what to query the workspace + (default: "") + {fname_width} (number) defines the width of the filename + section (default: 30) + {symbol_width} (number) defines the width of the symbol + section (default: 25) + {symbol_type_width} (number) defines the width of the symbol + type section (default: 8) + {show_line} (boolean) if true, shows the content of the + line the tag is found on (default: + false) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_dynamic_workspace_symbols({opts}) *telescope.builtin.lsp_dynamic_workspace_symbols()* + Dynamically lists LSP for all workspace symbols + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by type of + symbol you want to see (i.e. `:variable:`), only works after refining + to fuzzy search using + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {fname_width} (number) defines the width of the filename + section (default: 30) + {show_line} (boolean) if true, shows the content of the + line the symbol is found on + (default: false) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.diagnostics({opts}) *telescope.builtin.diagnostics()* + Lists diagnostics + - Fields: + - `All severity flags can be passed as `string` or `number` as per + `:vim.diagnostic.severity:` + - Default keymaps: + - ``: show autocompletion menu to prefilter your query with the + diagnostic you want to see (i.e. `:warning:`) + - sort_by option: + - "buffer": order by bufnr (prioritizing current bufnr), severity, lnum + - "severity": order by severity, bufnr (prioritizing current bufnr), lnum + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {bufnr} (number|nil) Buffer number to get + diagnostics from. Use 0 for + current buffer or nil for all + buffers + {severity} (string|number) filter diagnostics by severity + name (string) or id (number) + {severity_limit} (string|number) keep diagnostics equal or more + severe wrt severity name + (string) or id (number) + {severity_bound} (string|number) keep diagnostics equal or less + severe wrt severity name + (string) or id (number) + {root_dir} (string|boolean) if set to string, get + diagnostics only for buffers + under this dir otherwise cwd + {no_unlisted} (boolean) if true, get diagnostics only + for listed buffers + {no_sign} (boolean) hide DiagnosticSigns from + Results (default: false) + {line_width} (string|number) set length of diagnostic entry + text in Results. Use 'full' + for full untruncated text + {namespace} (number) limit your diagnostics to a + specific namespace + {disable_coordinates} (boolean) don't show the line & row + numbers (default: false) + {sort_by} (string) sort order of the diagnostics + results; see above notes + (default: "buffer") + + + +================================================================================ +THEMES *telescope.themes* + +Themes are ways to combine several elements of styling together. + +They are helpful for managing the several different UI aspects for telescope +and provide a simple interface for users to get a particular "style" of picker. + +themes.get_dropdown() *telescope.themes.get_dropdown()* + Dropdown style theme. + + Usage: + > + local opts = {...} -- picker options + local builtin = require('telescope.builtin') + local themes = require('telescope.themes') + builtin.find_files(themes.get_dropdown(opts)) +< + + + +themes.get_cursor() *telescope.themes.get_cursor()* + Cursor style theme. + + Usage: + > + local opts = {...} -- picker options + local builtin = require('telescope.builtin') + local themes = require('telescope.themes') + builtin.find_files(themes.get_cursor(opts)) +< + + + +themes.get_ivy() *telescope.themes.get_ivy()* + Ivy style theme. + + Usage: + > + local opts = {...} -- picker options + local builtin = require('telescope.builtin') + local themes = require('telescope.themes') + builtin.find_files(themes.get_ivy(opts)) +< + + + + +================================================================================ +MAPPINGS *telescope.mappings* + +|telescope.mappings| is used to configure the keybindings within a telescope +picker. These key binds are only local to the picker window and will be cleared +once you exit the picker. + +We provide multiple configuration options to make it easy for you to adjust +telescope's default key bindings and create your own custom key binds. + +To see many of the builtin actions that you can use as values for this table, +see |telescope.actions| + +Format is: +> + { + mode = { ..keys } + } +< + +where {mode} is the one character letter for a mode ('i' for insert, 'n' for +normal). + +For example: +> + mappings = { + i = { + [""] = require('telescope.actions').close, + }, + } +< + +To disable a keymap, put `[map] = false` +For example: +> + { + ..., + [""] = false, + ..., + } +< + +To override behavior of a key, simply set the value to be a function (either by +requiring an action or by writing your own function) +> + { + ..., + [""] = require('telescope.actions').select_default, + ..., + } +< + +If the function you want is part of `telescope.actions`, then you can simply +supply the function name as a string. For example, the previous option is +equivalent to: +> + { + ..., + [""] = "select_default", + ..., + } +< + +You can also add other mappings using tables with `type = "command"`. For +example: +> + { + ..., + ["jj"] = { "", type = "command" }, + ["kk"] = { "echo \"Hello, World!\"", type = "command" },) + ..., + } +< + +You can also add additional options for mappings of any type ("action" and +"command"). For example: +> + { + ..., + [""] = { + actions.move_selection_next, type = "action", + opts = { nowait = true, silent = true } + }, + ..., + } +< + +There are three main places you can configure |telescope.mappings|. These are +ordered from the lowest priority to the highest priority. + +1. |telescope.defaults.mappings| +2. In the |telescope.setup()| table, inside a picker with a given name, use the + `mappings` key +> + require("telescope").setup { + pickers = { + find_files = { + mappings = { + n = { + ["kj"] = "close", + }, + }, + }, + }, + } +< +3. `attach_mappings` function for a particular picker. +> + require("telescope.builtin").find_files { + attach_mappings = function(_, map) + map("i", "asdf", function(_prompt_bufnr) + print "You typed asdf" + end) + + map({"i", "n"}, "", function(_prompt_bufnr) + print "You typed " + end, { desc = "desc for which key"}) + + -- needs to return true if you want to map default_mappings and + -- false if not + return true + end, + } +< + + +================================================================================ +LAYOUT *telescope.pickers.layout* + +The telescope pickers layout can be configured using the +|telescope.defaults.create_layout| option. + +Parameters: ~ + - picker : A Picker object. + +Return: ~ + - layout : instance of `TelescopeLayout` class. + +Example: ~ +> +local Layout = require "telescope.pickers.layout" + +require("telescope").setup { + create_layout = function(picker) + local function create_window(enter, width, height, row, col, title) + local bufnr = vim.api.nvim_create_buf(false, true) + local winid = vim.api.nvim_open_win(bufnr, enter, { + style = "minimal", + relative = "editor", + width = width, + height = height, + row = row, + col = col, + border = "single", + title = title, + }) + + vim.wo[winid].winhighlight = "Normal:Normal" + + return Layout.Window { + bufnr = bufnr, + winid = winid, + } + end + + local function destory_window(window) + if window then + if vim.api.nvim_win_is_valid(window.winid) then + vim.api.nvim_win_close(window.winid, true) + end + if vim.api.nvim_buf_is_valid(window.bufnr) then + vim.api.nvim_buf_delete(window.bufnr, { force = true }) + end + end + end + + local layout = Layout { + picker = picker, + mount = function(self) + self.results = create_window(false, 40, 20, 0, 0, "Results") + self.preview = create_window(false, 40, 23, 0, 42, "Preview") + self.prompt = create_window(true, 40, 1, 22, 0, "Prompt") + end, + unmount = function(self) + destory_window(self.results) + destory_window(self.preview) + destory_window(self.prompt) + end, + update = function(self) end, + } + + return layout + end, +} +< + +TelescopeWindowBorder.config *TelescopeWindowBorder.config* + + + Fields: ~ + {bufnr} (integer) + {winid} (integer|nil) + {change_title} (nil|function) (self: TelescopeWindowBorder, title: + string, pos?: + "NW"|"N"|"NE"|"SW"|"S"|"SE"):nil + + +TelescopeWindowBorder *TelescopeWindowBorder* + + + Fields: ~ + {bufnr} (integer|nil) + {winid} (integer|nil) + + +TelescopeWindow.config *TelescopeWindow.config* + + + Fields: ~ + {bufnr} (integer) + {winid} (integer|nil) + {border} (TelescopeWindowBorder.config|nil) + + +TelescopeWindow *TelescopeWindow* + + + Fields: ~ + {border} (TelescopeWindowBorder) + {bufnr} (integer) + {winid} (integer) + + +TelescopeLayout.config *TelescopeLayout.config* + + + Fields: ~ + {mount} (function) (self: TelescopeLayout):nil + {unmount} (function) (self: TelescopeLayout):nil + {update} (function) (self: TelescopeLayout):nil + {prompt} (TelescopeWindow|nil) + {results} (TelescopeWindow|nil) + {preview} (TelescopeWindow|nil) + + +TelescopeLayout *TelescopeLayout* + + + Fields: ~ + {prompt} (TelescopeWindow) + {results} (TelescopeWindow) + {preview} (TelescopeWindow|nil) + + +Layout:mount() *telescope.pickers.layout:mount()* + Create the layout. This needs to ensure the required properties are + populated. + + + +Layout:unmount() *telescope.pickers.layout:unmount()* + Destroy the layout. This is responsible for performing clean-up, for + example: + - deleting buffers + - closing windows + - clearing autocmds + + + +Layout:update() *telescope.pickers.layout:update()* + Refresh the layout. This is called when, for example, vim is resized. + + + + +================================================================================ +LAYOUT *telescope.layout* + +The layout of telescope pickers can be adjusted using the +|telescope.defaults.layout_strategy| and |telescope.defaults.layout_config| +options. For example, the following configuration changes the default layout +strategy and the default size of the picker: +> + require('telescope').setup{ + defaults = { + layout_strategy = 'vertical', + layout_config = { height = 0.95 }, + }, + } +< + + +──────────────────────────────────────────────────────────────────────────────── + +Layout strategies are different functions to position telescope. + +All layout strategies are functions with the following signature: + +> + function(picker, columns, lines, layout_config) + -- Do some calculations here... + return { + preview = preview_configuration + results = results_configuration, + prompt = prompt_configuration, + } + end +< + + Parameters: ~ + - picker : A Picker object. (docs coming soon) + - columns : (number) Columns in the vim window + - lines : (number) Lines in the vim window + - layout_config : (table) The configuration values specific to the picker. + +This means you can create your own layout strategy if you want! Just be aware +for now that we may change some APIs or interfaces, so they may break if you +create your own. + +A good method for creating your own would be to copy one of the strategies that +most resembles what you want from +"./lua/telescope/pickers/layout_strategies.lua" in the telescope repo. + + +layout_strategies.horizontal() *telescope.layout.horizontal()* + Horizontal layout has two columns, one for the preview and one for the + prompt and results. + + ┌──────────────────────────────────────────────────┐ + │ │ + │ ┌───────────────────┐┌───────────────────┐ │ + │ │ ││ │ │ + │ │ ││ │ │ + │ │ ││ │ │ + │ │ Results ││ │ │ + │ │ ││ Preview │ │ + │ │ ││ │ │ + │ │ ││ │ │ + │ └───────────────────┘│ │ │ + │ ┌───────────────────┐│ │ │ + │ │ Prompt ││ │ │ + │ └───────────────────┘└───────────────────┘ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When columns are less than this value, the preview will be disabled + - preview_width: + - Change the width of Telescope's preview window + - See |resolver.resolve_width()| + + +layout_strategies.center() *telescope.layout.center()* + Centered layout with a combined block of the prompt and results aligned to + the middle of the screen. The preview window is then placed in the + remaining space above or below, according to `anchor` or `mirror`. + Particularly useful for creating dropdown menus (see |telescope.themes| and + |themes.get_dropdown()|). + + Note that vertical anchoring, i.e. `anchor` containing `"N"` or `"S"`, will + override `mirror` config. For `"N"` anchoring preview will be placed below + prompt/result block. For `"S"` anchoring preview will be placed above + prompt/result block. For horizontal only anchoring preview will be placed + according to `mirror` config, default is above the prompt/result block. + + ┌──────────────────────────────────────────────────┐ + │ ┌────────────────────────────────────────┐ │ + │ │ Preview │ │ + │ │ Preview │ │ + │ └────────────────────────────────────────┘ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Prompt │ │ + │ ├────────────────────────────────────────┤ │ + │ │ Result │ │ + │ │ Result │ │ + │ └────────────────────────────────────────┘ │ + │ │ + │ │ + │ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When lines are less than this value, the preview will be disabled + + +layout_strategies.cursor() *telescope.layout.cursor()* + Cursor layout dynamically positioned below the cursor if possible. If there + is no place below the cursor it will be placed above. + + ┌──────────────────────────────────────────────────┐ + │ │ + │ █ │ + │ ┌──────────────┐┌─────────────────────┐ │ + │ │ Prompt ││ Preview │ │ + │ ├──────────────┤│ Preview │ │ + │ │ Result ││ Preview │ │ + │ │ Result ││ Preview │ │ + │ └──────────────┘└─────────────────────┘ │ + │ █ │ + │ │ + │ │ + │ │ + │ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When columns are less than this value, the preview will be disabled + - preview_width: + - Change the width of Telescope's preview window + - See |resolver.resolve_width()| + + +layout_strategies.vertical() *telescope.layout.vertical()* + Vertical layout stacks the items on top of each other. Particularly useful + with thinner windows. + + ┌──────────────────────────────────────────────────┐ + │ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Preview │ │ + │ │ Preview │ │ + │ │ Preview │ │ + │ └────────────────────────────────────────┘ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Result │ │ + │ │ Result │ │ + │ └────────────────────────────────────────┘ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Prompt │ │ + │ └────────────────────────────────────────┘ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When lines are less than this value, the preview will be disabled + - preview_height: + - Change the height of Telescope's preview window + - See |resolver.resolve_height()| + + +layout_strategies.flex() *telescope.layout.flex()* + Flex layout swaps between `horizontal` and `vertical` strategies based on + the window width + - Supports |layout_strategies.vertical| or |layout_strategies.horizontal| + features + + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - flip_columns: The number of columns required to move to horizontal mode + - flip_lines: The number of lines required to move to horizontal mode + - horizontal: Options to pass when switching to horizontal layout + - vertical: Options to pass when switching to vertical layout + + +layout_strategies.bottom_pane() *telescope.layout.bottom_pane()* + Bottom pane can be used to create layouts similar to "ivy". + + For an easy ivy configuration, see |themes.get_ivy()| + + + + +================================================================================ +RESOLVE *telescope.resolve* + +Provides "resolver functions" to allow more customisable inputs for options. + +resolver.resolve_height() *telescope.resolve.resolve_height()* + Converts input to a function that returns the height. The input must take + one of five forms: + 1. 0 <= number < 1 + This means total height as a percentage. + 2. 1 <= number + This means total height as a fixed number. + 3. function + Must have signature: function(self, max_columns, max_lines): number + 4. table of the form: { val, max = ..., min = ... } + val has to be in the first form 0 <= val < 1 and only one is given, + `min` or `max` as fixed number + 5. table of the form: {padding = `foo`} + where `foo` has one of the previous three forms. + The height is then set to be the remaining space after padding. For + example, if the window has height 50, and the input is {padding = 5}, + the height returned will be `40 = 50 - 2*5` + + The returned function will have signature: function(self, max_columns, + max_lines): number + + + +resolver.resolve_width() *telescope.resolve.resolve_width()* + Converts input to a function that returns the width. The input must take + one of five forms: + 1. 0 <= number < 1 + This means total width as a percentage. + 2. 1 <= number + This means total width as a fixed number. + 3. function + Must have signature: function(self, max_columns, max_lines): number + 4. table of the form: { val, max = ..., min = ... } + val has to be in the first form 0 <= val < 1 and only one is given, + `min` or `max` as fixed number + 5. table of the form: {padding = `foo`} + where `foo` has one of the previous three forms. + The width is then set to be the remaining space after padding. For + example, if the window has width 100, and the input is {padding = 5}, + the width returned will be `90 = 100 - 2*5` + + The returned function will have signature: function(self, max_columns, + max_lines): number + + + +resolver.resolve_anchor_pos() *telescope.resolve.resolve_anchor_pos()* + Calculates the adjustment required to move the picker from the middle of + the screen to an edge or corner. + The `anchor` can be any of the following strings: + - "", "CENTER", "NW", "N", "NE", "E", "SE", "S", "SW", "W" The anchors + have the following meanings: + - "" or "CENTER": + the picker will remain in the middle of the screen. + - Compass directions: + the picker will move to the corresponding edge/corner e.g. "NW" -> "top + left corner", "E" -> "right edge", "S" -> "bottom edge" + + + + +================================================================================ +MAKE_ENTRY *telescope.make_entry* + +Each picker has a finder made up of two parts, the results which are the data +to be displayed, and the entry_maker. These entry_makers are functions returned +from make_entry functions. These will be referred to as entry_makers in the +following documentation. + +Every entry maker returns a function that accepts the data to be used for an +entry. This function will return an entry table (or nil, meaning skip this +entry) which contains the following important keys: +- value any: value key can be anything but still required +- valid bool (optional): is an optional key because it defaults to true but if + the key is set to false it will not be displayed by the picker +- ordinal string: is the text that is used for filtering +- display string|function: is either a string of the text that is being + displayed or a function receiving the entry at a later stage, when the entry + is actually being displayed. A function can be useful here if a complex + calculation has to be done. `make_entry` can also return a second value - a + highlight array which will then apply to the line. Highlight entry in this + array has the following signature `{ { start_col, end_col }, hl_group }` +- filename string (optional): will be interpreted by the default `` action + as open this file +- bufnr number (optional): will be interpreted by the default `` action as + open this buffer +- lnum number (optional): lnum value which will be interpreted by the default + `` action as a jump to this line +- col number (optional): col value which will be interpreted by the default + `` action as a jump to this column + +For more information on easier displaying, see +|telescope.pickers.entry_display| + +TODO: Document something we call `entry_index` + + +================================================================================ +ENTRY_DISPLAY *telescope.pickers.entry_display* + +Entry Display is used to format each entry shown in the result panel. + +Entry Display create() will give us a function based on the configuration of +column widths we pass into it. We then can use this function n times to return +a string based on structured input. + +Note that if you call `create()` inside `make_display` it will be called for +every single entry. So it is suggested to do this outside of `make_display` for +the best performance. + +The create function will use the column widths passed to it in +configuration.items. Each item in that table is the number of characters in the +column. It's also possible for the final column to not have a fixed width, this +will be shown in the configuration as 'remaining = true'. + +An example of this configuration is shown for the buffers picker: +> +local displayer = entry_display.create { + separator = " ", + items = { + { width = opts.bufnr_width }, + { width = 4 }, + { width = icon_width }, + { remaining = true }, + }, +} +< + +This shows 4 columns, the first is defined in the opts as the width we'll use +when display_string is the number of the buffer. The second has a fixed width +of 4 and the third column's width will be decided by the width of the icons we +use. The fourth column will use the remaining space. Finally, we have also +defined the separator between each column will be the space " ". + +An example of how the display reference will be used is shown, again for the +buffers picker: +> +return displayer { + { entry.bufnr, "TelescopeResultsNumber" }, + { entry.indicator, "TelescopeResultsComment" }, + { icon, hl_group }, + display_bufname .. ":" .. entry.lnum, +} +< + +There are two types of values each column can have. Either a simple String or a +table containing the String as well as the hl_group. + +The displayer can return values, string and an optional highlights. The string +is all the text to be displayed for this entry as a single string. If parts of +the string are to be highlighted they will be described in the highlights +table. + +For a better understanding of how create() and displayer are used it's best to +look at the code in make_entry.lua. + + +================================================================================ +UTILS *telescope.utils* + +Utilities for writing telescope pickers + +utils.path_expand({path}) *telescope.utils.path_expand()* + Hybrid of `vim.fn.expand()` and custom `vim.fs.normalize()` + + Paths starting with '%', '#' or '<' are expanded with `vim.fn.expand()`. + Otherwise avoids using `vim.fn.expand()` due to its overly aggressive + expansion behavior which can sometimes lead to errors or the creation of + non-existent paths when dealing with valid absolute paths. + + Other paths will have '~' and environment variables expanded. Unlike + `vim.fs.normalize()`, backslashes are preserved. This has better + compatibility with `plenary.path` and also avoids mangling valid Unix paths + with literal backslashes. + + Trailing slashes are trimmed. With the exception of root paths. eg. `/` on + Unix or `C:\` on Windows + + + + Parameters: ~ + {path} (string) + + Return: ~ + string + + +utils.transform_path({opts}, {path}) *telescope.utils.transform_path()* + Transform path is a util function that formats a path based on path_display + found in `opts` or the default value from config. It is meant to be used in + make_entry to have a uniform interface for builtins as well as extensions + utilizing the same user configuration Note: It is only supported inside + `make_entry`/`make_display` the use of this function outside of telescope + might yield to undefined behavior and will not be addressed by us + + + Parameters: ~ + {opts} (table) The opts the users passed into the picker. Might + contains a path_display key + {path} (string|nil) The path that should be formatted + + Return: ~ + string: path to be displayed + table: The transformed path ready to be displayed with the styling + + +utils.has_ts_parser({lang}) *telescope.utils.has_ts_parser()* + Checks if treesitter parser for language is installed + + + Parameters: ~ + {lang} (string) + + +utils.notify({funname}, {opts}) *telescope.utils.notify()* + Telescope Wrapper around vim.notify + + + Parameters: ~ + {funname} (string) name of the function that will be + {opts} (table) opts.level string, opts.msg string, opts.once bool + + + +================================================================================ +ACTIONS *telescope.actions* + +These functions are useful for people creating their own mappings. + +Actions can be either normal functions that expect the `prompt_bufnr` as first +argument (1) or they can be a custom telescope type called "action" (2). + +(1) The `prompt_bufnr` of a normal function denotes the identifier of your +picker which can be used to access the picker state. In practice, users most +commonly access from both picker and global state via the following: +> + -- for utility functions + local action_state = require "telescope.actions.state" + + local actions = {} + actions.do_stuff = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) -- picker state + local entry = action_state.get_selected_entry() + end +< + +See |telescope.actions.state| for more information. + +(2) To transform a module of functions into a module of "action"s, you need to +do the following: +> + local transform_mod = require("telescope.actions.mt").transform_mod + + local mod = {} + mod.a1 = function(prompt_bufnr) + -- your code goes here + -- You can access the picker/global state as described above in (1). + end + + mod.a2 = function(prompt_bufnr) + -- your code goes here + end + mod = transform_mod(mod) + + -- Now the following is possible. This means that actions a2 will be executed + -- after action a1. You can chain as many actions as you want. + local action = mod.a1 + mod.a2 + action(bufnr) +< + +Another interesting thing to do is that these actions now have functions you +can call. These functions include `:replace(f)`, `:replace_if(f, c)`, +`replace_map(tbl)` and `enhance(tbl)`. More information on these functions can +be found in the `developers.md` and `lua/tests/automated/action_spec.lua` file. + +actions.move_selection_next({prompt_bufnr}) *telescope.actions.move_selection_next()* + Move the selection to the next entry + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_selection_previous({prompt_bufnr}) *telescope.actions.move_selection_previous()* + Move the selection to the previous entry + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_selection_worse({prompt_bufnr}) *telescope.actions.move_selection_worse()* + Move the selection to the entry that has a worse score + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_selection_better({prompt_bufnr}) *telescope.actions.move_selection_better()* + Move the selection to the entry that has a better score + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_to_top({prompt_bufnr}) *telescope.actions.move_to_top()* + Move to the top of the picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_to_middle({prompt_bufnr}) *telescope.actions.move_to_middle()* + Move to the middle of the picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_to_bottom({prompt_bufnr}) *telescope.actions.move_to_bottom()* + Move to the bottom of the picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_selection({prompt_bufnr}) *telescope.actions.add_selection()* + Add current entry to multi select + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.remove_selection({prompt_bufnr}) *telescope.actions.remove_selection()* + Remove current entry from multi select + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.toggle_selection({prompt_bufnr}) *telescope.actions.toggle_selection()* + Toggle current entry status for multi select + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_all({prompt_bufnr}) *telescope.actions.select_all()* + Multi select all entries. + - Note: selected entries may include results not visible in the results pop + up. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.drop_all({prompt_bufnr}) *telescope.actions.drop_all()* + Drop all entries from the current multi selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.toggle_all({prompt_bufnr}) *telescope.actions.toggle_all()* + Toggle multi selection for all entries. + - Note: toggled entries may include results not visible in the results pop + up. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_up({prompt_bufnr}) *telescope.actions.preview_scrolling_up()* + Scroll the preview window up + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_down({prompt_bufnr}) *telescope.actions.preview_scrolling_down()* + Scroll the preview window down + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_left({prompt_bufnr}) *telescope.actions.preview_scrolling_left()* + Scroll the preview window to the left + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_right({prompt_bufnr}) *telescope.actions.preview_scrolling_right()* + Scroll the preview window to the right + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_up({prompt_bufnr}) *telescope.actions.results_scrolling_up()* + Scroll the results window up + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_down({prompt_bufnr}) *telescope.actions.results_scrolling_down()* + Scroll the results window down + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_left({prompt_bufnr}) *telescope.actions.results_scrolling_left()* + Scroll the results window to the left + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_right({prompt_bufnr}) *telescope.actions.results_scrolling_right()* + Scroll the results window to the right + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.center({prompt_bufnr}) *telescope.actions.center()* + Center the cursor in the window, can be used after selecting a file to edit + You can just map `actions.select_default + actions.center` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_default({prompt_bufnr}) *telescope.actions.select_default()* + Perform default action on selection, usually something like + `:edit ` + + i.e. open the selection in the current buffer + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_horizontal({prompt_bufnr}) *telescope.actions.select_horizontal()* + Perform 'horizontal' action on selection, usually something like + `:new ` + + i.e. open the selection in a new horizontal split + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_vertical({prompt_bufnr}) *telescope.actions.select_vertical()* + Perform 'vertical' action on selection, usually something like + `:vnew ` + + i.e. open the selection in a new vertical split + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_tab({prompt_bufnr}) *telescope.actions.select_tab()* + Perform 'tab' action on selection, usually something like + `:tabedit ` + + i.e. open the selection in a new tab + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_drop({prompt_bufnr}) *telescope.actions.select_drop()* + Perform 'drop' action on selection, usually something like + `:drop ` + + i.e. open the selection in a window + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_tab_drop({prompt_bufnr}) *telescope.actions.select_tab_drop()* + Perform 'tab drop' action on selection, usually something like + `:tab drop ` + + i.e. open the selection in a new tab + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_edit({prompt_bufnr}) *telescope.actions.file_edit()* + Perform file edit on selection, usually something like + `:edit ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_split({prompt_bufnr}) *telescope.actions.file_split()* + Perform file split on selection, usually something like + `:new ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_vsplit({prompt_bufnr}) *telescope.actions.file_vsplit()* + Perform file vsplit on selection, usually something like + `:vnew ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_tab({prompt_bufnr}) *telescope.actions.file_tab()* + Perform file tab on selection, usually something like + `:tabedit ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.close({prompt_bufnr}) *telescope.actions.close()* + Close the Telescope window, usually used within an action + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions._close({prompt_bufnr}) *telescope.actions._close()* + Close the Telescope window, usually used within an action + Deprecated and no longer needed, does the same as + |telescope.actions.close|. Might be removed in the future + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.edit_command_line({prompt_bufnr}) *telescope.actions.edit_command_line()* + Set a value in the command line and don't run it, making it editable. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.set_command_line({prompt_bufnr}) *telescope.actions.set_command_line()* + Set a value in the command line and run it + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.edit_search_line({prompt_bufnr}) *telescope.actions.edit_search_line()* + Set a value in the search line and don't search for it, making it editable. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.set_search_line({prompt_bufnr}) *telescope.actions.set_search_line()* + Set a value in the search line and search for it + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.edit_register({prompt_bufnr}) *telescope.actions.edit_register()* + Edit a register + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.paste_register({prompt_bufnr}) *telescope.actions.paste_register()* + Paste the selected register into the buffer + + Note: only meant to be used inside builtin.registers + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_symbol({prompt_bufnr}) *telescope.actions.insert_symbol()* + Insert a symbol into the current buffer (while switching to normal mode) + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_symbol_i({prompt_bufnr}) *telescope.actions.insert_symbol_i()* + Insert a symbol into the current buffer and keeping the insert mode. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_create_branch({prompt_bufnr}) *telescope.actions.git_create_branch()* + Create and checkout a new git branch if it doesn't already exist + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_apply_stash({prompt_bufnr}) *telescope.actions.git_apply_stash()* + Applies an existing git stash + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_checkout({prompt_bufnr}) *telescope.actions.git_checkout()* + Checkout an existing git branch + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_switch_branch({prompt_bufnr}) *telescope.actions.git_switch_branch()* + Switch to git branch. + If the branch already exists in local, switch to that. If the branch is + only in remote, create new branch tracking remote and switch to new one. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_rename_branch() *telescope.actions.git_rename_branch()* + Action to rename selected git branch + + + +actions.git_track_branch({prompt_bufnr}) *telescope.actions.git_track_branch()* + Tell git to track the currently selected remote branch in Telescope + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_delete_branch({prompt_bufnr}) *telescope.actions.git_delete_branch()* + Delete all currently selected branches + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_merge_branch({prompt_bufnr}) *telescope.actions.git_merge_branch()* + Merge the currently selected branch + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_rebase_branch({prompt_bufnr}) *telescope.actions.git_rebase_branch()* + Rebase to selected git branch + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_reset_mixed({prompt_bufnr}) *telescope.actions.git_reset_mixed()* + Reset to selected git commit using mixed mode + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_reset_soft({prompt_bufnr}) *telescope.actions.git_reset_soft()* + Reset to selected git commit using soft mode + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_reset_hard({prompt_bufnr}) *telescope.actions.git_reset_hard()* + Reset to selected git commit using hard mode + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_checkout_current_buffer({prompt_bufnr}) *telescope.actions.git_checkout_current_buffer()* + Checkout a specific file for a given sha + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_staging_toggle({prompt_bufnr}) *telescope.actions.git_staging_toggle()* + Stage/unstage selected file + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_selected_to_qflist({prompt_bufnr}) *telescope.actions.send_selected_to_qflist()* + Sends the selected entries to the quickfix list, replacing the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_selected_to_qflist({prompt_bufnr}) *telescope.actions.add_selected_to_qflist()* + Adds the selected entries to the quickfix list, keeping the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_to_qflist({prompt_bufnr}) *telescope.actions.send_to_qflist()* + Sends all entries to the quickfix list, replacing the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_to_qflist({prompt_bufnr}) *telescope.actions.add_to_qflist()* + Adds all entries to the quickfix list, keeping the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_selected_to_loclist({prompt_bufnr}) *telescope.actions.send_selected_to_loclist()* + Sends the selected entries to the location list, replacing the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_selected_to_loclist({prompt_bufnr}) *telescope.actions.add_selected_to_loclist()* + Adds the selected entries to the location list, keeping the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_to_loclist({prompt_bufnr}) *telescope.actions.send_to_loclist()* + Sends all entries to the location list, replacing the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_to_loclist({prompt_bufnr}) *telescope.actions.add_to_loclist()* + Adds all entries to the location list, keeping the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_send_to_qflist({prompt_bufnr}) *telescope.actions.smart_send_to_qflist()* + Sends the selected entries to the quickfix list, replacing the previous + entries. If no entry was selected, sends all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_add_to_qflist({prompt_bufnr}) *telescope.actions.smart_add_to_qflist()* + Adds the selected entries to the quickfix list, keeping the previous + entries. If no entry was selected, adds all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_send_to_loclist({prompt_bufnr}) *telescope.actions.smart_send_to_loclist()* + Sends the selected entries to the location list, replacing the previous + entries. If no entry was selected, sends all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_add_to_loclist({prompt_bufnr}) *telescope.actions.smart_add_to_loclist()* + Adds the selected entries to the location list, keeping the previous + entries. If no entry was selected, adds all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.complete_tag({prompt_bufnr}) *telescope.actions.complete_tag()* + Open completion menu containing the tags which can be used to filter the + results in a faster way + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_history_next({prompt_bufnr}) *telescope.actions.cycle_history_next()* + Cycle to the next search prompt in the history + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_history_prev({prompt_bufnr}) *telescope.actions.cycle_history_prev()* + Cycle to the previous search prompt in the history + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.open_qflist({prompt_bufnr}) *telescope.actions.open_qflist()* + Open the quickfix list. It makes sense to use this in combination with one + of the send_to_qflist actions `actions.smart_send_to_qflist + + actions.open_qflist` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.open_loclist({prompt_bufnr}) *telescope.actions.open_loclist()* + Open the location list. It makes sense to use this in combination with one + of the send_to_loclist actions `actions.smart_send_to_qflist + + actions.open_qflist` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.delete_buffer({prompt_bufnr}) *telescope.actions.delete_buffer()* + Delete the selected buffer or all the buffers selected using multi + selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_previewers_next({prompt_bufnr}) *telescope.actions.cycle_previewers_next()* + Cycle to the next previewer if there is one available. + This action is not mapped on default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_previewers_prev({prompt_bufnr}) *telescope.actions.cycle_previewers_prev()* + Cycle to the previous previewer if there is one available. + This action is not mapped on default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.remove_selected_picker({prompt_bufnr}) *telescope.actions.remove_selected_picker()* + Removes the selected picker in |builtin.pickers|. + This action is not mapped by default and only intended for + |builtin.pickers|. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.which_key({prompt_bufnr}) *telescope.actions.which_key()* + Display the keymaps of registered actions similar to which-key.nvim. + + - Notes: + - The defaults can be overridden via |action_generate.which_key|. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.to_fuzzy_refine({prompt_bufnr}) *telescope.actions.to_fuzzy_refine()* + Move from a none fuzzy search to a fuzzy one + This action is meant to be used in live_grep and + lsp_dynamic_workspace_symbols + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.delete_mark({prompt_bufnr}) *telescope.actions.delete_mark()* + Delete the selected mark or all the marks selected using multi selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_original_cword({prompt_bufnr}) *telescope.actions.insert_original_cword()* + Insert the word under the cursor of the original (pre-Telescope) window + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_STATE *telescope.actions.state* + +Functions to be used to determine the current state of telescope. + +Generally used from within other |telescope.actions| + +action_state.get_selected_entry() *telescope.actions.state.get_selected_entry()* + Get the current entry + + + +action_state.get_current_line() *telescope.actions.state.get_current_line()* + Gets the current line in the search prompt + + + +action_state.get_current_picker({prompt_bufnr}) *telescope.actions.state.get_current_picker()* + Gets the current picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_SET *telescope.actions.set* + +Telescope action sets are used to provide an interface for managing actions +that all primarily do the same thing, but with slight tweaks. + +For example, when editing files you may want it in the current split, a +vertical split, etc. Instead of making users have to overwrite EACH of those +every time they want to change this behavior, they can instead replace the +`set` itself and then it will work great and they're done. + +action_set.shift_selection({prompt_bufnr}, {change}) *telescope.actions.set.shift_selection()* + Move the current selection of a picker {change} rows. Handles not + overflowing / underflowing the list. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {change} (number) The amount to shift the selection by + + +action_set.select({prompt_bufnr}, {type}) *telescope.actions.set.select()* + Select the current entry. This is the action set to overwrite common + actions by the user. + + By default maps to editing a file. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {type} (string) The type of selection to make + + +action_set.edit({prompt_bufnr}, {command}) *telescope.actions.set.edit()* + Edit a file based on the current selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {command} (string) The command to use to open the file. + + +action_set.scroll_previewer({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_previewer()* + Scrolls the previewer up or down. Defaults to a half page scroll, but can + be overridden using the `scroll_speed` option in `layout_config`. See + |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + +action_set.scroll_horizontal_previewer({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_horizontal_previewer()* + Scrolls the previewer to the left or right. Defaults to a half page scroll, + but can be overridden using the `scroll_speed` option in `layout_config`. + See |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + +action_set.scroll_results({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_results()* + Scrolls the results up or down. Defaults to a half page scroll, but can be + overridden using the `scroll_speed` option in `layout_config`. See + |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + +action_set.scroll_horizontal_results({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_horizontal_results()* + Scrolls the results to the left or right. Defaults to a half page scroll, + but can be overridden using the `scroll_speed` option in `layout_config`. + See |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + + +================================================================================ +ACTIONS_LAYOUT *telescope.actions.layout* + +The layout actions are actions to be used to change the layout of a picker. + +action_layout.toggle_preview({prompt_bufnr}) *telescope.actions.layout.toggle_preview()* + Toggle preview window. + - Note: preview window can be toggled even if preview is set to false. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.toggle_prompt_position({prompt_bufnr}) *telescope.actions.layout.toggle_prompt_position()* + Toggles the `prompt_position` option between "top" and "bottom". Checks if + `prompt_position` is an option for the current layout. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.toggle_mirror({prompt_bufnr}) *telescope.actions.layout.toggle_mirror()* + Toggles the `mirror` option between `true` and `false`. Checks if `mirror` + is an option for the current layout. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.cycle_layout_next({prompt_bufnr}) *telescope.actions.layout.cycle_layout_next()* + Cycles to the next layout in `cycle_layout_list`. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.cycle_layout_prev({prompt_bufnr}) *telescope.actions.layout.cycle_layout_prev()* + Cycles to the previous layout in `cycle_layout_list`. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_UTILS *telescope.actions.utils* + +Utilities to wrap functions around picker selections and entries. + +Generally used from within other |telescope.actions| + +utils.map_entries({prompt_bufnr}, {f}) *telescope.actions.utils.map_entries()* + Apply `f` to the entries of the current picker. + - Notes: + - Mapped entries include all currently filtered results, not just the + visible ones. + - Indices are 1-indexed, whereas rows are 0-indexed. + - Warning: `map_entries` has no return value. + - The below example showcases how to collect results + + Usage: + > + local action_state = require "telescope.actions.state" + local action_utils = require "telescope.actions.utils" + function entry_value_by_row() + local prompt_bufnr = vim.api.nvim_get_current_buf() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local results = {} + action_utils.map_entries(prompt_bufnr, function(entry, index, row) + results[row] = entry.value + end) + return results + end +< + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {f} (function) Function to map onto entries of picker that + takes (entry, index, row) as viable + arguments + + +utils.map_selections({prompt_bufnr}, {f}) *telescope.actions.utils.map_selections()* + Apply `f` to the multi selections of the current picker and return a table + of mapped selections. + - Notes: + - Mapped selections may include results not visible in the results pop + up. + - Selected entries are returned in order of their selection. + - Warning: `map_selections` has no return value. + - The below example showcases how to collect results + + Usage: + > + local action_state = require "telescope.actions.state" + local action_utils = require "telescope.actions.utils" + function selection_by_index() + local prompt_bufnr = vim.api.nvim_get_current_buf() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local results = {} + action_utils.map_selections(prompt_bufnr, function(entry, index) + results[index] = entry.value + end) + return results + end +< + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {f} (function) Function to map onto selection of picker + that takes (selection) as a viable argument + + +utils.get_registered_mappings({prompt_bufnr}) *telescope.actions.utils.get_registered_mappings()* + Utility to collect mappings of prompt buffer in array of `{mode, keybind, + name}`. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_GENERATE *telescope.actions.generate* + +Module for convenience to override defaults of corresponding +|telescope.actions| at |telescope.setup()|. + +General usage: +> + require("telescope").setup { + defaults = { + mappings = { + n = { + ["?"] = action_generate.which_key { + name_width = 20, -- typically leads to smaller floats + max_height = 0.5, -- increase potential maximum height + separator = " > ", -- change sep between mode, keybind, and name + close_with_action = false, -- do not close float on action + }, + }, + }, + }, + } +< + +action_generate.which_key({opts}) *telescope.actions.generate.which_key()* + Display the keymaps of registered actions similar to which-key.nvim. + + - Floating window: + - Appears on the opposite side of the prompt. + - Resolves to minimum required number of lines to show hints with `opts` + or truncates entries at `max_height`. + - Closes automatically on action call and can be disabled with by setting + `close_with_action` to false. + + + Parameters: ~ + {opts} (table) options to pass to toggling registered actions + + Fields: ~ + {max_height} (number) % of max. height or no. of rows + for hints (default: 0.4), see + |resolver.resolve_height()| + {only_show_current_mode} (boolean) only show keymaps for the current + mode (default: true) + {mode_width} (number) fixed width of mode to be shown + (default: 1) + {keybind_width} (number) fixed width of keybind to be shown + (default: 7) + {name_width} (number) fixed width of action name to be + shown (default: 30) + {column_padding} (string) string to split; can be used for + vertical separator (default: " ") + {mode_hl} (string) hl group of mode (default: + TelescopeResultsConstant) + {keybind_hl} (string) hl group of keybind (default: + TelescopeResultsVariable) + {name_hl} (string) hl group of action name (default: + TelescopeResultsFunction) + {column_indent} (number) number of left-most spaces before + keybinds are shown (default: 4) + {line_padding} (number) row padding in top and bottom of + float (default: 1) + {separator} (string) separator string between mode, key + bindings, and action (default: " + -> ") + {close_with_action} (boolean) registered action will close + keymap float (default: true) + {normal_hl} (string) winhl of "Normal" for keymap hints + floating window (default: + "TelescopePrompt") + {border_hl} (string) winhl of "Normal" for keymap + borders (default: + "TelescopePromptBorder") + {winblend} (number) pseudo-transparency of keymap + hints floating window + {zindex} (number) z-index of keymap hints floating + window (default: 100) + + + +================================================================================ +PREVIEWERS *telescope.previewers* + +Provides a Previewer table that has to be implemented by each previewer. To +achieve this, this module also provides two wrappers that abstract most of the +work and make it really easy to create new previewers. + - `previewers.new_termopen_previewer` + - `previewers.new_buffer_previewer` + +Furthermore, there are a collection of previewers already defined which can be +used for every picker, as long as the entries of the picker provide the +necessary fields. The more important ones are + - `previewers.cat` + - `previewers.vimgrep` + - `previewers.qflist` + - `previewers.vim_buffer_cat` + - `previewers.vim_buffer_vimgrep` + - `previewers.vim_buffer_qflist` + +Previewers can be disabled for any builtin or custom picker by doing :Telescope +find_files previewer=false + +previewers.Previewer() *telescope.previewers.Previewer()* + This is the base table all previewers have to implement. It's possible to + write a wrapper for this because most previewers need to have the same keys + set. Examples of wrappers are: + - `new_buffer_previewer` + - `new_termopen_previewer` + + To create a new table do following: + - `local new_previewer = Previewer:new(opts)` + + What `:new` expects is listed below + + The interface provides the following set of functions. All of them, besides + `new`, will be handled by telescope pickers. + - `:new(opts)` + - `:preview(entry, status)` + - `:teardown()` + - `:send_input(input)` + - `:scroll_fn(direction)` + - `:scroll_horizontal_fn(direction)` + + `Previewer:new()` expects a table as input with following keys: + - `setup` function(self): Will be called the first time preview will be + called. + - `teardown` function(self): Will be called on clean up. + - `preview_fn` function(self, entry, status): Will be called each time a + new entry was selected. + - `title` function(self): Will return the static title of the previewer. + - `dynamic_title` function(self, entry): Will return the dynamic title of + the previewer. Will only be called when config value + dynamic_preview_title is true. + - `send_input` function(self, input): This is meant for + `termopen_previewer` and it can be used to send input to the terminal + application, like less. + - `scroll_fn` function(self, direction): Used to make scrolling work. + - `scroll_horizontal_fn` function(self, direction): Used to make + horizontal scrolling work. + + + +previewers.new() *telescope.previewers.new()* + A shorthand for creating a new Previewer. The provided table will be + forwarded to `Previewer:new(...)` + + + +previewers.new_termopen_previewer() *telescope.previewers.new_termopen_previewer()* + Is a wrapper around Previewer and helps with creating a new + `termopen_previewer`. + + It requires you to specify one table entry `get_command(entry, status)`. + This `get_command` function has to return the terminal command that will be + executed for each entry. Example: + > + get_command = function(entry, status) + return { 'bat', entry.path } + end +< + + Additionally you can define: + - `title` a static title for example "File Preview" + - `dyn_title(self, entry)` a dynamic title function which gets called when + config value `dynamic_preview_title = true` + + It's an easy way to get your first previewer going and it integrates well + with `bat` and `less`. Providing out of the box scrolling if the command + uses less. + + Furthermore, it will forward all `config.set_env` environment variables to + that terminal process. + + + +previewers.cat() *telescope.previewers.cat()* + Provides a `termopen_previewer` which has the ability to display files. It + will always show the top of the file and has support for `bat`(prioritized) + and `cat`. Each entry has to provide either the field `path` or `filename` + in order to make this previewer work. + + The preferred way of using this previewer is like this + `require('telescope.config').values.cat_previewer` This will respect user + configuration and will use `buffer_previewers` in case it's configured that + way. + + + +previewers.vimgrep() *telescope.previewers.vimgrep()* + Provides a `termopen_previewer` which has the ability to display files at + the provided line. It has support for `bat`(prioritized) and `cat`. Each + entry has to provide either the field `path` or `filename` and a `lnum` + field in order to make this previewer work. + + The preferred way of using this previewer is like this + `require('telescope.config').values.grep_previewer` This will respect user + configuration and will use `buffer_previewers` in case it's configured that + way. + + + +previewers.qflist() *telescope.previewers.qflist()* + Provides a `termopen_previewer` which has the ability to display files at + the provided line or range. It has support for `bat`(prioritized) and + `cat`. Each entry has to provide either the field `path` or `filename`, + `lnum` and a `start` and `finish` range in order to make this previewer + work. + + The preferred way of using this previewer is like this + `require('telescope.config').values.qflist_previewer` This will respect + user configuration and will use buffer previewers in case it's configured + that way. + + + +previewers.new_buffer_previewer() *telescope.previewers.new_buffer_previewer()* + An interface to instantiate a new `buffer_previewer`. That means that the + content actually lives inside a vim buffer which enables us more control + over the actual content. For example, we can use `vim.fn.search` to jump to + a specific line or reuse buffers/already opened files more easily. This + interface is more complex than `termopen_previewer` but offers more + flexibility over your content. It was designed to display files but was + extended to also display the output of terminal commands. + + In the following options, state table and general tips are mentioned to + make your experience with this previewer more seamless. + + + options: + - `define_preview = function(self, entry, status)` (required) Is called + for each selected entry, after each selection_move (up or down) and is + meant to handle things like reading file, jump to line or attach a + highlighter. + - `setup = function(self)` (optional) Is called once at the beginning, + before the preview for the first entry is displayed. You can return a + table of vars that will be available in `self.state` in each + `define_preview` call. + - `teardown = function(self)` (optional) Will be called at the end, when + the picker is being closed and is meant to clean up everything that was + allocated by the previewer. The `buffer_previewer` will automatically + clean up all created buffers. So you only need to handle things that + were introduced by you. + - `keep_last_buf = true` (optional) Will not delete the last selected + buffer. This would allow you to reuse that buffer in the select action. + For example, that buffer can be opened in a new split, rather than + recreating that buffer in an action. To access the last buffer number: + `require('telescope.state').get_global_key("last_preview_bufnr")` + - `get_buffer_by_name = function(self, entry)` Allows you to set a unique + name for each buffer. This is used for caching purposes. + `self.state.bufname` will be nil if the entry was never loaded or the + unique name when it was loaded once. For example, useful if you have + one file but multiple entries. This happens for grep and lsp builtins. + So to make the cache work only load content if `self.state.bufname ~= + entry.your_unique_key` + - `title` a static title for example "File Preview" + - `dyn_title(self, entry)` a dynamic title function which gets called + when config value `dynamic_preview_title = true` + + `self.state` table: + - `self.state.bufnr` Is the current buffer number, in which you have to + write the loaded content. Don't create a buffer yourself, otherwise + it's not managed by the buffer_previewer interface and you will + probably be better off writing your own interface. + - self.state.winid Current window id. Useful if you want to set the + cursor to a provided line number. + - self.state.bufname Will return the current buffer name, if + `get_buffer_by_name` is defined. nil will be returned if the entry was + never loaded or when `get_buffer_by_name` is not set. + + Tips: + - If you want to display content of a terminal job, use: + `require('telescope.previewers.utils').job_maker(cmd, bufnr, opts)` + - `cmd` table: for example { 'git', 'diff', entry.value } + - `bufnr` number: in which the content will be written + - `opts` table: with following keys + - `bufname` string: used for cache + - `value` string: used for cache + - `mode` string: either "insert" or "append". "insert" is default + - `env` table: define environment variables. Example: + - `{ ['PAGER'] = '', ['MANWIDTH'] = 50 }` + - `cwd` string: define current working directory for job + - `callback` function(bufnr, content): will be called when job is + done. Content will be nil if job is already loaded. So you can do + highlighting only the first time the previewer is created for + that entry. Use the returned `bufnr` and not `self.state.bufnr` + in callback, because state can already be changed at this point + in time. + - If you want to attach a highlighter use: + - `require('telescope.previewers.utils').highlighter(bufnr, ft)` + - This will prioritize tree sitter highlighting if available for + environment and language. + - `require('telescope.previewers.utils').regex_highlighter(bufnr, ft)` + - `require('telescope.previewers.utils').ts_highlighter(bufnr, ft)` + - If you want to use `vim.fn.search` or similar you need to run it in + that specific buffer context. Do + > + vim.api.nvim_buf_call(bufnr, function() + -- for example `search` and `matchadd` + end) +< + to achieve that. + - If you want to read a file into the buffer it's best to use + `buffer_previewer_maker`. But access this function with + `require('telescope.config').values.buffer_previewer_maker` because it + can be redefined by users. + + + +previewers.buffer_previewer_maker({filepath}, {bufnr}, {opts}) *telescope.previewers.buffer_previewer_maker()* + A universal way of reading a file into a buffer previewer. It handles async + reading, cache, highlighting, displaying directories and provides a + callback which can be used, to jump to a line in the buffer. + + + Parameters: ~ + {filepath} (string) String to the filepath, will be expanded + {bufnr} (number) Where the content will be written + {opts} (table) keys: `use_ft_detect`, `bufname` and `callback` + + +previewers.vim_buffer_cat() *telescope.previewers.vim_buffer_cat()* + A previewer that is used to display a file. It uses the `buffer_previewer` + interface and won't jump to the line. To integrate this one into your own + picker make sure that the field `path` or `filename` is set for each entry. + The preferred way of using this previewer is like this + `require('telescope.config').values.file_previewer` This will respect user + configuration and will use `termopen_previewer` in case it's configured + that way. + + + +previewers.vim_buffer_vimgrep() *telescope.previewers.vim_buffer_vimgrep()* + A previewer that is used to display a file and jump to the provided line. + It uses the `buffer_previewer` interface. To integrate this one into your + own picker make sure that the field `path` or `filename` and `lnum` is set + in each entry. If the latter is not present, it will default to the first + line. Additionally, `lnend`, `col` and `colend` can be set to highlight a + text range instead of a single line. All line/column values are 1-indexed. + The preferred way of using this previewer is like this + `require('telescope.config').values.grep_previewer` This will respect user + configuration and will use `termopen_previewer` in case it's configured + that way. + + + +previewers.vim_buffer_qflist() *telescope.previewers.vim_buffer_qflist()* + Is the same as `vim_buffer_vimgrep` and only exists for consistency with + `term_previewers`. + + The preferred way of using this previewer is like this + `require('telescope.config').values.qflist_previewer` This will respect + user configuration and will use `termopen_previewer` in case it's + configured that way. + + + +previewers.git_branch_log() *telescope.previewers.git_branch_log()* + A previewer that shows a log of a branch as graph + + + +previewers.git_stash_diff() *telescope.previewers.git_stash_diff()* + A previewer that shows a diff of a stash + + + +previewers.git_commit_diff_to_parent() *telescope.previewers.git_commit_diff_to_parent()* + A previewer that shows a diff of a commit to a parent commit. + The run command is `git --no-pager diff SHA^! -- $CURRENT_FILE` + + The current file part is optional. So is only uses it with bcommits. + + + +previewers.git_commit_diff_to_head() *telescope.previewers.git_commit_diff_to_head()* + A previewer that shows a diff of a commit to head. + The run command is `git --no-pager diff --cached $SHA -- $CURRENT_FILE` + + The current file part is optional. So is only uses it with bcommits. + + + +previewers.git_commit_diff_as_was() *telescope.previewers.git_commit_diff_as_was()* + A previewer that shows a diff of a commit as it was. + The run command is `git --no-pager show $SHA:$CURRENT_FILE` or `git + --no-pager show $SHA` + + + +previewers.git_commit_message() *telescope.previewers.git_commit_message()* + A previewer that shows the commit message of a diff. + The run command is `git --no-pager log -n 1 $SHA` + + + +previewers.git_file_diff() *telescope.previewers.git_file_diff()* + A previewer that shows the current diff of a file. Used in git_status. + The run command is `git --no-pager diff $FILE` + + + +previewers.display_content() *telescope.previewers.display_content()* + A deprecated way of displaying content more easily. Was written at a time, + where the buffer_previewer interface wasn't present. Nowadays it's easier + to just use this. We will keep it around for backwards compatibility + because some extensions use it. It doesn't use cache or some other clever + tricks. + + + + +================================================================================ +HISTORY *telescope.actions.history* + +A base implementation of a prompt history that provides a simple history and +can be replaced with a custom implementation. + +For example: We provide an extension for a smart history that uses sql.nvim to +map histories to metadata, like the calling picker or cwd. + +So you have a history for: +- find_files project_1 +- grep_string project_1 +- live_grep project_1 +- find_files project_2 +- grep_string project_2 +- live_grep project_2 +- etc + +See https://github.com/nvim-telescope/telescope-smart-history.nvim + +histories.History() *telescope.actions.history.History()* + Manages prompt history + + + Fields: ~ + {enabled} (boolean) Will indicate if History is enabled or + disabled + {path} (string) Will point to the location of the history file + {limit} (string) Will have the limit of the history. Can be + nil, if limit is disabled. + {content} (table) History table. Needs to be filled by your own + History implementation + {index} (number) Used to keep track of the next or previous + index. Default is #content + 1 + {cycle_wrap} (boolean) Controls if history will wrap on reaching + beginning or end + + +histories.History:new({opts}) *telescope.actions.history.History:new()* + Create a new History + + + Parameters: ~ + {opts} (table) Defines the behavior of History + + Fields: ~ + {init} (function) Will be called after handling configuration + (required) + {append} (function) How to append a new prompt item (required) + {reset} (function) What happens on reset. Will be called when + telescope closes (required) + {pre_get} (function) Will be called before a next or previous item + will be returned (optional) + + +histories.new() *telescope.actions.history.new()* + Shorthand to create a new history + + + +histories.History:reset() *telescope.actions.history.History:reset()* + Will reset the history index to the default initial state. Will happen + after the picker closed + + + +histories.History:append({line}, {picker}, {no_reset}) *telescope.actions.history.History:append()* + Append a new line to the history + + + Parameters: ~ + {line} (string) current line that will be appended + {picker} (table) the current picker object + {no_reset} (boolean) On default it will reset the state at the end. + If you don't want to do this set to true + + +histories.History:get_next({line}, {picker}) *telescope.actions.history.History:get_next()* + Will return the next history item. Can be nil if there are no next items + + + Parameters: ~ + {line} (string) the current line + {picker} (table) the current picker object + + Return: ~ + string: the next history item + + +histories.History:get_prev({line}, {picker}) *telescope.actions.history.History:get_prev()* + Will return the previous history item. Can be nil if there are no previous + items + + + Parameters: ~ + {line} (string) the current line + {picker} (table) the current picker object + + Return: ~ + string: the previous history item + + +histories.get_simple_history() *telescope.actions.history.get_simple_history()* + A simple implementation of history. + + It will keep one unified history across all pickers. + + + + + vim:tw=78:ts=8:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/doc/telescope_changelog.txt b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/doc/telescope_changelog.txt new file mode 100644 index 00000000..08750a7b --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/doc/telescope_changelog.txt @@ -0,0 +1,282 @@ +================================================================================ + *telescope.changelog* + +# Changelog + + *telescope.changelog-922* + +Date: May 17, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/922 + +This is one of our largest breaking changes thus far, so I (TJ) am adding some +information here so that you can more easily update (without having to track +down the commit, etc.). + +The goal of these breaking changes is to greatly simplify the way +configuration for layouts happen. This should make it much easier to configure +each picker, layout_strategy, and more. Please report any bugs or behavior +that is broken / confusing upstream and we can try and make the configuration +better. + +|telescope.setup()| has changed `layout_defaults` -> `layout_config`. + This makes it so that the setup and the pickers share the same key, + otherwise it is too confusing which key is for which. + + +`picker:find()` now has different values available for configuring the UI. + All configuration for the layout must be passed in the key: + `layout_config`. + + Previously, these keys were passed via `picker:find(opts)`, but should be + passed via `opts.layout_config` now. + - {height} + - {width} + - {prompt_position} + - {preview_cutoff} + + These keys are removed: + - {results_height}: This key is no longer valid. Instead, use `height` + and the corresponding `preview_*` options for the layout strategy to + get the correct results height. This simplifies the configuration + for many of the existing strategies. + + - {results_width}: This key actually never did anything. It was + leftover from some hacking that I had attempted before. Instead you + should be using something like the `preview_width` configuration + option for |layout_strategies.horizontal()| + + You should get error messages when you try and use any of the above keys now. + + *telescope.changelog-839* + +Date: July 7, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/839 + +Small breaking change regarding `shorten_path` and `hide_filename`. +This allows to configure path displays on a global level and offers a way for +extension developers to make use of the same configuration, offering a better +overall experience. + +The new way to configure to configure path displays is with: + `path_display`: It is a table and accepts multiple values: + - "hidden" hide file names + - "tail" only display the file name, and not the path + - "absolute" display absolute paths + - "shorten" only display the first character of each directory in + the path + see |telescope.defaults.path_display| + +Example would be for a global configuration: + require("telescope").setup{ + defaults = { + path_display = { + "shorten", + "absolute", + }, + } + } + +You can also still pass this to a single builtin call: + require("telescope.builtin").find_files { + path_display = { "shorten" } + } + +For extension developers there is a new util function that can be used to +display a path: + local filename = utils.transform_path(opts, entry.filename) + + *telescope.changelog-473* + +Date: July 14, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/473 + +Deprecation of telescope.path + +Extension developers need to move to plenary.path, because we will remove the +telescope.path module soon. + +Guide to switch over to plenary.path + - separator + before: require("telescope.path").separator + now: require("plenary.path").path.sep + - home + before: require("telescope.path").home + now: require("plenary.path").path.home + - make_relative + before: require("telescope.path").make_relative(filepath, cwd) + now: require("plenary.path"):new(filepath):make_relative(cwd) + - shorten + before: require("telescope.path").shorten(filepath) + now: require("plenary.path"):new(filepath):shorten() + with optional len, default is 1 + - normalize + before: require("telescope.path").normalize(filepath, cwd) + now: require("plenary.path"):new(filepath):normalize(cwd) + - read_file + before: require("telescope.path").read_file(filepath) + now: require("plenary.path"):new(filepath):read() + - read_file_async + before: require("telescope.path").read_file_async(filepath, callback) + now: require("plenary.path"):new(filepath):read(callback) + + *telescope.changelog-1406* + +Date: November 4, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1406 + +Telescope requires Neovim release 0.5.1 or a recent nightly + +Due to making use of newly implemented extmark features, Telescope now +requires users to be on Neovim 0.5.1 (the most recent stable version) or on +the LATEST version of Neovim nightly. + + + *telescope.changelog-1549* + +Date: December 10, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1549 + +Telescope requires now Neovim release 0.6.0 or a more recent nightly. +If you are running neovim nightly, you need to make sure that you are on the +LATEST version. Every other commit is not supported. So make sure you build +the newest nightly before reporting issues. + + + *telescope.changelog-1553* + +Date: December 10, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1553 + +Move from `vim.lsp.diagnostic` to `vim.diagnostic`. + +Because the newly added `vim.diagnostic` has no longer anything to do with lsp +we also decided to rename our diagnostic functions: + Telescope lsp_document_diagnostics -> Telescope diagnostics bufnr=0 + Telescope lsp_workspace_diagnostics -> Telescope diagnostics +Because of that the `lsp_*_diagnostics` inside Telescope will be deprecated +and removed soon. The new `diagnostics` works almost identical to the previous +functions. Note that there is no longer a workspace diagnostics. You can only +get all diagnostics for all open buffers. + + + *telescope.changelog-1851* + +Date: April 22, 2022 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1851 + +Telescope requires now Neovim release 0.7.0 or a more recent nightly. +If you are running Neovim nightly, you need to make sure that you are on the +LATEST version. Every other commit is not supported. So make sure you build +the newest nightly before reporting issues. +In the future, we will adopt a different release strategy. This release +strategy follows the approach that the latest telescope.nvim master will only +work with latest Neovim nightly and we will provide tags for specific Neovim +versions. You can read more about this strategy here: +https://github.com/nvim-telescope/telescope.nvim/issues/1772 + + + *telescope.changelog-1866* + +Date: April 25, 2022 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1866 + +We decided to remove both `lsp_code_actions` and `lsp_range_code_actions`. +Currently, both functions are highly duplicated code from neovim, with fewer +features, because it's out of date. So rather that we copy over the required +changes to fix some bugs or implement client side code actions, we decided to +remove both of them and suggest you use `vim.lsp.buf.code_action` and +`vim.lsp.buf.range_code_action`. The transition to it is easy thanks to +`vim.ui.select` which allows you to override the select UI. We provide a small +extension for quite some time that make it easy to use telescope for +`vim.ui.select`. You can found the code here +https://github.com/nvim-telescope/telescope-ui-select.nvim. It offers the same +displaying as the current version of `lsp_code_actions`. An alternative is +https://github.com/stevearc/dressing.nvim which has support for multiple +different backends including telescope. + + + *telescope.changelog-1945* + +Date: July 01, 2022 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1945 + +This is our dev branch which contains a lot of PRs, a lot of fixes, +refactoring and general quality of life improvements. It also contains new +features, the most noteworthy are the following (mostly developed by the +community): +- feat: none strategy & control attachment (#1867) +- feat: force buffer delete for terminal and improvements for + Picker:delete_selection (#1943) +- feat(tags): process tagfiles on the fly (#1989) +- feat(builtin.lsp): implement builtin handlers for + lsp.(incoming|outgoing)_calls (#1484) +- feat: clear previewer if no item is selected (#2004) +- feat: add min max boundary to width, height resolver (#2002) +- feat: Add entry_index for entry_makers (#1850) +- feat: refine with new_table (#1115) + +The last one is one of the most exciting new features, because it allows you +to go from live_grep into a fuzzy environment with the following mapping +``. It's a general interface we now implemented for `live_grep` and +`lsp_dynamic_workspace_symbols` but it could also be easily implemented for +other builtins, by us or the user. It's now available for extension developers. +We will add documentation in the next couple of days and improve it by adding +more options to configure it after the initial 0.1 release. + +But as with all longer development phases, there are also some breaking +changes. This is the main reason we moved development to a separate branch, for +the past two months. We can't promise that there won't be more breaking +changes, but it is the plan that this is the last set of breaking changes prior +to the 0.1 release on July, 12. We are deeply sorry for the inconvenience. The +following breaking changes are included in this PR: +- break(git_files): change `show_untracked` default to false. Can be changed + back with `:Telescope git_files show_untracked=true` +- break: deprecate `utils.get_default` `utils.if_nil`, will be removed prior + to 0.1, so if you use it in your config, please move to `vim.F.if_nil` +- break: drops `ignore_filename` option, use `path_display= { "hidden" }` + instead +- break: prefix internal interfaces with __ so + `require("telescope.builtin.files").find_files` will show a notify error but + still works for now. The error will be removed prior to 0.1! You should use + `require("telescope.builtin").find_files` because we wrap all the functions + that are exposed in this module. +- break: defaults.preview.treesitter rework that allows you to either enable a + list of languages, or enable all and disable some. Please read + `:help telescope.defaults.preview` for more information. + Something like this is now possible: + > + treesitter = { + enable = false, + -- or + enable = { "c" }, + -- disable can be set if enable isn't set + disable = { "perl", "javascript" }, + }, +< + + + *telescope.changelog-2499* + +Date: May 24, 2023 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/2499 + +We decided to bump the minimum Neovim version to 0.9.0, in order to remove a +couple of no longer required workarounds. That includes using upstream +treesitter implementation in favor of nvim-treesitter. +If you still have a requirement for Neovim 0.7 or 0.8, we also have a stable +branch 0.1.x (or version, currently 0.1.1) which will not receive this version +bump and will continue to offer support for older Neovim versions. + + + *telescope.changelog-2529* + +Date: June 09, 2023 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/2529 + +We finally removed usage of `plenary.filetype` to determine filetypes for +previewing and replaced it with `vim.filetype`. So if you have highlighting +issues you no longer have to configure `plenary`, but rather read +|vim.filetype|. + + + vim:tw=78:ts=8:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/ftplugin/TelescopePrompt.lua b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/ftplugin/TelescopePrompt.lua new file mode 100644 index 00000000..8888a88f --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/ftplugin/TelescopePrompt.lua @@ -0,0 +1,12 @@ +-- Don't wrap textwidth things +vim.opt_local.formatoptions:remove "t" +vim.opt_local.formatoptions:remove "c" + +-- Don't include `showbreak` when calculating strdisplaywidth +vim.opt_local.wrap = false + +-- There's also no reason to enable textwidth here anyway +vim.opt_local.textwidth = 0 +vim.opt_local.scrollbind = false + +vim.opt_local.signcolumn = "no" diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/ftplugin/TelescopeResults.lua b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/ftplugin/TelescopeResults.lua new file mode 100644 index 00000000..08e9dccf --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/ftplugin/TelescopeResults.lua @@ -0,0 +1,5 @@ +-- Don't have scrolloff, it makes things weird. +vim.opt_local.scrolloff = 0 +vim.opt_local.scrollbind = false + +vim.opt_local.signcolumn = "no" diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/plugin/telescope.lua b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/plugin/telescope.lua new file mode 100644 index 00000000..338036cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/plugin/telescope.lua @@ -0,0 +1,156 @@ +if 1 ~= vim.fn.has "nvim-0.9.0" then + vim.api.nvim_err_writeln "Telescope.nvim requires at least nvim-0.9.0. See `:h telescope.changelog-2499`" + return +end + +if vim.g.loaded_telescope == 1 then + return +end +vim.g.loaded_telescope = 1 + +local highlights = { + -- Sets the highlight for selected items within the picker. + TelescopeSelection = { default = true, link = "Visual" }, + TelescopeSelectionCaret = { default = true, link = "TelescopeSelection" }, + TelescopeMultiSelection = { default = true, link = "Type" }, + TelescopeMultiIcon = { default = true, link = "Identifier" }, + + -- "Normal" in the floating windows created by telescope. + TelescopeNormal = { default = true, link = "Normal" }, + TelescopePreviewNormal = { default = true, link = "TelescopeNormal" }, + TelescopePromptNormal = { default = true, link = "TelescopeNormal" }, + TelescopeResultsNormal = { default = true, link = "TelescopeNormal" }, + + -- Border highlight groups. + -- Use TelescopeBorder to override the default. + -- Otherwise set them specifically + TelescopeBorder = { default = true, link = "TelescopeNormal" }, + TelescopePromptBorder = { default = true, link = "TelescopeBorder" }, + TelescopeResultsBorder = { default = true, link = "TelescopeBorder" }, + TelescopePreviewBorder = { default = true, link = "TelescopeBorder" }, + + -- Title highlight groups. + -- Use TelescopeTitle to override the default. + -- Otherwise set them specifically + TelescopeTitle = { default = true, link = "TelescopeBorder" }, + TelescopePromptTitle = { default = true, link = "TelescopeTitle" }, + TelescopeResultsTitle = { default = true, link = "TelescopeTitle" }, + TelescopePreviewTitle = { default = true, link = "TelescopeTitle" }, + + TelescopePromptCounter = { default = true, link = "NonText" }, + + -- Used for highlighting characters that you match. + TelescopeMatching = { default = true, link = "Special" }, + + -- Used for the prompt prefix + TelescopePromptPrefix = { default = true, link = "Identifier" }, + + -- Used for highlighting the matched line inside Previewer. Works only for (vim_buffer_ previewer) + TelescopePreviewLine = { default = true, link = "Visual" }, + TelescopePreviewMatch = { default = true, link = "Search" }, + + TelescopePreviewPipe = { default = true, link = "Constant" }, + TelescopePreviewCharDev = { default = true, link = "Constant" }, + TelescopePreviewDirectory = { default = true, link = "Directory" }, + TelescopePreviewBlock = { default = true, link = "Constant" }, + TelescopePreviewLink = { default = true, link = "Special" }, + TelescopePreviewSocket = { default = true, link = "Statement" }, + TelescopePreviewRead = { default = true, link = "Constant" }, + TelescopePreviewWrite = { default = true, link = "Statement" }, + TelescopePreviewExecute = { default = true, link = "String" }, + TelescopePreviewHyphen = { default = true, link = "NonText" }, + TelescopePreviewSticky = { default = true, link = "Keyword" }, + TelescopePreviewSize = { default = true, link = "String" }, + TelescopePreviewUser = { default = true, link = "Constant" }, + TelescopePreviewGroup = { default = true, link = "Constant" }, + TelescopePreviewDate = { default = true, link = "Directory" }, + TelescopePreviewMessage = { default = true, link = "TelescopePreviewNormal" }, + TelescopePreviewMessageFillchar = { default = true, link = "TelescopePreviewMessage" }, + + -- Used for Picker specific Results highlighting + TelescopeResultsClass = { default = true, link = "Function" }, + TelescopeResultsConstant = { default = true, link = "Constant" }, + TelescopeResultsField = { default = true, link = "Function" }, + TelescopeResultsFunction = { default = true, link = "Function" }, + TelescopeResultsMethod = { default = true, link = "Method" }, + TelescopeResultsOperator = { default = true, link = "Operator" }, + TelescopeResultsStruct = { default = true, link = "Struct" }, + TelescopeResultsVariable = { default = true, link = "SpecialChar" }, + + TelescopeResultsLineNr = { default = true, link = "LineNr" }, + TelescopeResultsIdentifier = { default = true, link = "Identifier" }, + TelescopeResultsNumber = { default = true, link = "Number" }, + TelescopeResultsComment = { default = true, link = "Comment" }, + TelescopeResultsSpecialComment = { default = true, link = "SpecialComment" }, + + -- Used for git status Results highlighting + TelescopeResultsDiffChange = { default = true, link = "DiffChange" }, + TelescopeResultsDiffAdd = { default = true, link = "DiffAdd" }, + TelescopeResultsDiffDelete = { default = true, link = "DiffDelete" }, + TelescopeResultsDiffUntracked = { default = true, link = "NonText" }, +} + +for k, v in pairs(highlights) do + vim.api.nvim_set_hl(0, k, v) +end + +-- This is like "" in your terminal. +-- To use it, do `cmap (TelescopeFuzzyCommandSearch) +vim.keymap.set( + "c", + "(TelescopeFuzzyCommandSearch)", + "e \"lua require('telescope.builtin').command_history " + .. '{ default_text = [=[" . escape(getcmdline(), \'"\') . "]=] }"', + { silent = true, noremap = true } +) + +vim.api.nvim_create_user_command("Telescope", function(opts) + require("telescope.command").load_command(unpack(opts.fargs)) +end, { + nargs = "*", + complete = function(_, line) + local builtin_list = vim.tbl_keys(require "telescope.builtin") + local extensions_list = vim.tbl_keys(require("telescope._extensions").manager) + + local l = vim.split(line, "%s+") + local n = #l - 2 + + if n == 0 then + local commands = { builtin_list, extensions_list } + -- TODO(clason): remove when dropping support for Nvim 0.9 + if vim.fn.has "nvim-0.11" == 1 then + commands = vim.iter(commands):flatten():totable() + else + commands = vim.tbl_flatten(commands) + end + table.sort(commands) + + return vim.tbl_filter(function(val) + return vim.startswith(val, l[2]) + end, commands) + end + + if n == 1 then + local is_extension = vim.tbl_filter(function(val) + return val == l[2] + end, extensions_list) + + if #is_extension > 0 then + local extensions_subcommand_dict = require("telescope.command").get_extensions_subcommand() + local commands = extensions_subcommand_dict[l[2]] + table.sort(commands) + + return vim.tbl_filter(function(val) + return vim.startswith(val, l[3]) + end, commands) + end + end + + local options_list = vim.tbl_keys(require("telescope.config").values) + table.sort(options_list) + + return vim.tbl_filter(function(val) + return vim.startswith(val, l[#l]) + end, options_list) + end, +}) diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/rock_manifest b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/rock_manifest new file mode 100644 index 00000000..52662f77 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/rock_manifest @@ -0,0 +1,147 @@ +rock_manifest = { + autoload = { + health = { + ["telescope.vim"] = "7e299db63944941eca0a9c0a857ee937" + } + }, + data = { + memes = { + planets = { + earth = "82832fcbe2e0eedeb3422285312ede07", + jupiter = "026b61e7620f516ea15d42813128a626", + mars = "552a3bb388e63202c5298b5a8759e720", + mercury = "846e54aca2ba791b745a8cc7fb3271e2", + moon = "da940686c32ef9e6d59481c418da9acc", + neptune = "49326b778a7a38ebfc7c90006967fb72", + pluto = "1a9e8e9618dbe487b7c140eec3996a4c", + saturn = "c93f370b9099a4a0727c2331d1d57dc6", + uranus = "5ff7c70e9b0b825e6793533b6560ef97", + venus = "59a266ef8a3cde831a540568ed3fa6b2" + } + } + }, + doc = { + ["secret.txt"] = "59f2d5cd8b01a044b4e7f598244869ec", + ["telescope.txt"] = "190706f7b21d86c8809b8b0d946d8aec", + ["telescope_changelog.txt"] = "5c807cb49d45799276be85c33693c08f" + }, + ftplugin = { + ["TelescopePrompt.lua"] = "207de42b635cc7de9ebf5e73128106aa", + ["TelescopeResults.lua"] = "50bc0d8c4520c7e678f4122a956d9a49" + }, + lua = { + telescope = { + ["_.lua"] = "e94b53c4fe57d51c361f0b1df9a6d64f", + _extensions = { + ["init.lua"] = "6c8e3d4e324874d3e9fef2f3b973027a" + }, + actions = { + ["generate.lua"] = "b627dbab3b7d2a572779707529331a5f", + ["history.lua"] = "6b34c6a357dfad0762f099c1ba3b426f", + ["init.lua"] = "0d6ea5eaeb361e6298663fbf0ea52206", + ["layout.lua"] = "91470edb5dd37a3025e141d5cc6691bd", + ["mt.lua"] = "9c22e387921dbfbd56afdf3fed7b84af", + ["set.lua"] = "e0b8491c7e789e09c032e650d8c858ef", + ["state.lua"] = "7c488697afa9b5a993920103661c5cc1", + ["utils.lua"] = "bc8870c00a9206ab13dcb2ab1142b5bd" + }, + algos = { + ["fzy.lua"] = "146c9ee304190d4f50d46f4b279e3182", + ["linked_list.lua"] = "9ca6315ec7c740da8198d8d62823040b", + ["string_distance.lua"] = "1d63486c008b414586ec505b36c361a2" + }, + builtin = { + ["__diagnostics.lua"] = "f2edc0b78359facb94a2269bf28fb0a7", + ["__files.lua"] = "d2c0373381ef70771adae4b6e9bb03ce", + ["__git.lua"] = "a40e030790285c1e8706d181f0b4a831", + ["__internal.lua"] = "04bf667684de70d6c6d600ba7c40ee99", + ["__lsp.lua"] = "03e5d78027aa37c04280151029744063", + ["init.lua"] = "0e0188ef0f37c07387270ea8594c0f2e" + }, + ["command.lua"] = "66d2dea0d60e1a27bfff18e55b1afb4c", + config = { + ["resolve.lua"] = "2ba48ad00f945ebcb300e211bc0280e3" + }, + ["config.lua"] = "824e9c60f4923d5e389d64ebc3b2914b", + ["debounce.lua"] = "562e0f836431d2e8e99130e657212196", + ["deprecated.lua"] = "7c1395098782b9c220a0dad7daca2fa6", + ["entry_manager.lua"] = "3d493cab0145fa8fd1405b2f160e0ec1", + finders = { + ["async_job_finder.lua"] = "7653d922e17ba0d3705a6cadcc9b544d", + ["async_oneshot_finder.lua"] = "4fd43f20022a9b72188776ef9b667497", + ["async_static_finder.lua"] = "66421d0a0cd5ce650bf107faaae87e62" + }, + ["finders.lua"] = "742e701ba623ff99035c455e231dc05a", + ["from_entry.lua"] = "7c2b68ad9af645ef0be4d7f929e021eb", + ["health.lua"] = "885a0947d640b003eba0b9aac67f5a21", + ["init.lua"] = "defe6eaf81f3596ccfafb9647afdac9f", + ["log.lua"] = "5c57992c1ac7c17c2e5879d38921f819", + ["make_entry.lua"] = "ece9c42534f9296fb9873147fe53291c", + ["mappings.lua"] = "5cfd5f1aff126928f745439d5c61b12b", + ["operators.lua"] = "e7244d086698dfaa71be2133820e57ee", + pickers = { + ["entry_display.lua"] = "27c74c2c4812244722444ebafbccd1ae", + ["highlights.lua"] = "2a1497c924141b85e57b106d0d8ec0ed", + ["layout.lua"] = "cdcf7f2019df1a715b233c4deae252e9", + ["layout_strategies.lua"] = "c11e7ff0f07d4c5e8f860bd2c4116e27", + ["multi.lua"] = "7822c69f5e6e537d7ad1d46c01097069", + ["scroller.lua"] = "54fc7267917bf9f9076a632293a73cd5", + ["window.lua"] = "fc74d7849a1381643289a65678e14aa3" + }, + ["pickers.lua"] = "b72754962d52ff38ec5af1ba74d51d34", + previewers = { + ["buffer_previewer.lua"] = "415436f46b3df7c247fbee9a466a4467", + ["init.lua"] = "fa1745a8bef70d35f713d3c4b3c70257", + ["previewer.lua"] = "3b02d1b4a9aeb5c6a79771709ca799cc", + ["term_previewer.lua"] = "6d38b050b6bc564215113e7cb42bb42a", + ["utils.lua"] = "9d4808a9af4dca92c1fb6f5993185213" + }, + ["sorters.lua"] = "bb1c9e687331d5cf0952582b2dadab7f", + ["state.lua"] = "450c0b38c2497ae8c505a882257e0bed", + testharness = { + ["helpers.lua"] = "1783be4d1f997b8ad23e9fab7680e0e3", + ["init.lua"] = "a447ceb5e7478053e98eb27acd011799", + ["runner.lua"] = "1201b1158848fc7e6a581f258776a977" + }, + ["themes.lua"] = "02e18c91826ee7834ffedc9378cbc9b9", + ["utils.lua"] = "a0d3abb98a237ada18f8a57421270af7" + }, + tests = { + automated = { + ["action_spec.lua"] = "7ffd015976efdc4bef88bf143e876a29", + ["command_spec.lua"] = "46a62833bbda1c56e29b2f183be62b9c", + ["entry_display_spec.lua"] = "b10d4d661d3ab2e1b89b412f45942061", + ["entry_manager_spec.lua"] = "708fbb4c280288a0a9c6fdd4e396c16e", + ["layout_strategies_spec.lua"] = "b146a6044e86ce63449f1463571b3ae2", + ["linked_list_spec.lua"] = "ebd7de3a8a8eaabf1e2fa6ced761374b", + pickers = { + ["find_files_spec.lua"] = "87af15abf6f94d58c667149a3e5a4e59" + }, + ["resolver_spec.lua"] = "ccfc7d99d8d230513c5e22ad5100e02e", + ["scroller_spec.lua"] = "3595abe16efe6753888274f11f1818ad", + ["sorters_spec.lua"] = "6a5c265752ac0ea6a07ef39d66518b8e", + ["telescope_spec.lua"] = "573cee7dce62d9f8cd0d3592f784aade", + ["utils_spec.lua"] = "e246ecc4ce2e9676a16c0269cf770bcc" + }, + fixtures = { + find_files = { + ["file_a.txt"] = "d41d8cd98f00b204e9800998ecf8427e", + ["file_abc.txt"] = "d41d8cd98f00b204e9800998ecf8427e" + } + }, + ["helpers.lua"] = "9d89080a25a31d5731b967731ed97c1d", + pickers = { + ["find_files__readme.lua"] = "c4a263e39c831b33a06a9d72ad383263", + ["find_files__scrolling_descending_cycle.lua"] = "8f32f279f627bdf01aa21628db699eef" + } + } + }, + plugin = { + ["telescope.lua"] = "0c9ae4aa6947d3bb124d7437d901237e" + }, + scripts = { + ["gendocs.lua"] = "bc7142cfb353b6315afec3fa2cac8b22", + ["minimal_init.vim"] = "97317fe610193d1fdad989e007a6cff6" + }, + ["telescope.nvim-scm-1.rockspec"] = "ecc6c254c09cc8213cde6d58393bc57b" +} diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/scripts/gendocs.lua b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/scripts/gendocs.lua new file mode 100644 index 00000000..ec0047c9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/scripts/gendocs.lua @@ -0,0 +1,49 @@ +-- Setup telescope with defaults +if RELOAD then + RELOAD "telescope" +end +require("telescope").setup() + +local docgen = require "docgen" + +local docs = {} + +docs.test = function() + -- TODO: Fix the other files so that we can add them here. + local input_files = { + "./lua/telescope/init.lua", + "./lua/telescope/command.lua", + "./lua/telescope/builtin/init.lua", + "./lua/telescope/themes.lua", + "./lua/telescope/mappings.lua", + "./lua/telescope/pickers/layout.lua", + "./lua/telescope/pickers/layout_strategies.lua", + "./lua/telescope/config/resolve.lua", + "./lua/telescope/make_entry.lua", + "./lua/telescope/pickers/entry_display.lua", + "./lua/telescope/utils.lua", + "./lua/telescope/actions/init.lua", + "./lua/telescope/actions/state.lua", + "./lua/telescope/actions/set.lua", + "./lua/telescope/actions/layout.lua", + "./lua/telescope/actions/utils.lua", + "./lua/telescope/actions/generate.lua", + "./lua/telescope/previewers/init.lua", + "./lua/telescope/actions/history.lua", + } + + local output_file = "./doc/telescope.txt" + local output_file_handle = io.open(output_file, "w") + + for _, input_file in ipairs(input_files) do + docgen.write(input_file, output_file_handle) + end + + output_file_handle:write " vim:tw=78:ts=8:ft=help:norl:\n" + output_file_handle:close() + vim.cmd [[checktime]] +end + +docs.test() + +return docs diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/scripts/minimal_init.vim b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/scripts/minimal_init.vim new file mode 100644 index 00000000..6f6731f7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/scripts/minimal_init.vim @@ -0,0 +1,8 @@ +set rtp+=. +set rtp+=../plenary.nvim/ +set rtp+=../tree-sitter-lua/ + +runtime! plugin/plenary.vim +runtime! plugin/telescope.lua + +let g:telescope_test_delay = 100 diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/telescope.nvim-scm-1.rockspec b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/telescope.nvim-scm-1.rockspec new file mode 100644 index 00000000..f717d640 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1-unstable-2024-05-25-rocks/telescope.nvim/scm-1/telescope.nvim-scm-1.rockspec @@ -0,0 +1,44 @@ +local MODREV, SPECREV = 'scm', '-1' +rockspec_format = '3.0' +package = 'telescope.nvim' +version = MODREV .. SPECREV + +description = { + summary = 'Find, Filter, Preview, Pick. All lua, all the time.', + detailed = [[ + A highly extendable fuzzy finder over lists. + Built on the latest awesome features from neovim core. + Telescope is centered around modularity, allowing for easy customization. + ]], + labels = { 'neovim', 'plugin', }, + homepage = 'https://github.com/nvim-telescope/telescope.nvim', + license = 'MIT', +} + +dependencies = { + 'lua == 5.1', + 'plenary.nvim', +} + +source = { + url = 'https://github.com/nvim-telescope/telescope.nvim/archive/refs/tags/' .. MODREV .. '.zip', + dir = 'telescope.nvim-' .. MODREV +} + +if MODREV == 'scm' then + source = { + url = 'git://github.com/nvim-telescope/telescope.nvim', + } +end + +build = { + type = 'builtin', + copy_directories = { + 'doc', + 'ftplugin', + 'plugin', + 'scripts', + 'autoload', + 'data', + } +} diff --git a/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1.rockspec b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1.rockspec new file mode 100644 index 00000000..f717d640 --- /dev/null +++ b/config/neovim/store/lazy-plugins/telescope.nvim/telescope.nvim-scm-1.rockspec @@ -0,0 +1,44 @@ +local MODREV, SPECREV = 'scm', '-1' +rockspec_format = '3.0' +package = 'telescope.nvim' +version = MODREV .. SPECREV + +description = { + summary = 'Find, Filter, Preview, Pick. All lua, all the time.', + detailed = [[ + A highly extendable fuzzy finder over lists. + Built on the latest awesome features from neovim core. + Telescope is centered around modularity, allowing for easy customization. + ]], + labels = { 'neovim', 'plugin', }, + homepage = 'https://github.com/nvim-telescope/telescope.nvim', + license = 'MIT', +} + +dependencies = { + 'lua == 5.1', + 'plenary.nvim', +} + +source = { + url = 'https://github.com/nvim-telescope/telescope.nvim/archive/refs/tags/' .. MODREV .. '.zip', + dir = 'telescope.nvim-' .. MODREV +} + +if MODREV == 'scm' then + source = { + url = 'git://github.com/nvim-telescope/telescope.nvim', + } +end + +build = { + type = 'builtin', + copy_directories = { + 'doc', + 'ftplugin', + 'plugin', + 'scripts', + 'autoload', + 'data', + } +} diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/.github/FUNDING.yml b/config/neovim/store/lazy-plugins/toggleterm.nvim/.github/FUNDING.yml new file mode 100644 index 00000000..912001b2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: akinsho +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/.github/ISSUE_TEMPLATE/bug_report.yaml b/config/neovim/store/lazy-plugins/toggleterm.nvim/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 00000000..aa413b66 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,67 @@ +name: 🐞 Bug +description: File a bug/issue +title: "[BUG] " +labels: [ Bug, Needs Triage ] +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: | + Please search to see if an issue already exists for the bug you encountered. + Do not open an issue using just a premade configuration, the most productive + way to debug an issue is using a minimal init.lua that does nothing other than + load this plugin and do the few other steps required to reproduce the issue. + + Please note if you have not tested your issue with a minimal configuration I will close + it. Many problems are caused by misconfiguration and interactions this takes up a lot of + my time to narrow down. Please always use a minimal config when reporting bugs. + options: + - label: I have searched the existing issues + required: true + - type: textarea + attributes: + label: Current Behavior + description: A concise description of what you're experiencing. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. In this environment... + 2. With this config... + 3. Run '...' + 4. See error... + validations: + required: true + - type: textarea + attributes: + label: Environment + description: | + examples: + - **OS**: Ubuntu 20.04 + - **neovim version**: 0.8.3 + - **Shell**: bash + value: | + - OS: + - neovim version: + - Shell: + render: Markdown + validations: + required: false + - type: textarea + attributes: + label: Anything else? + description: | + Links? References? Anything that will give us more context about the issue you are encountering! + + Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. + validations: + required: false diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/toggleterm.nvim/.github/workflows/ci.yml new file mode 100644 index 00000000..3d7a2538 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/.github/workflows/ci.yml @@ -0,0 +1,98 @@ +name: CI +on: + push: + pull_request: + + +jobs: + tests: + strategy: + matrix: + os: [ ubuntu-latest ] + runs-on: ${{ matrix.os }} + permissions: + # Give the default GITHUB_TOKEN write permission to commit and push the + # added or changed files to the repository. + contents: write + + steps: + - uses: actions/checkout@v3 + - name: Install Neovim + shell: bash + run: | + mkdir -p /tmp/nvim + wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage + cd /tmp/nvim + chmod a+x ./nvim.appimage + ./nvim.appimage --appimage-extract + echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + - name: Run Tests + run: | + nvim --version + [ ! -d tests ] && exit 0 + nvim --headless -u tests/minimal_init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/minimal_init.lua', sequential = true}" + + stylua: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.WORKFLOW_ACCESS_TOKEN }} + - uses: JohnnyMorganz/stylua-action@v2 + with: + version: latest + token: ${{ secrets.WORKFLOW_ACCESS_TOKEN }} + args: --config-path=stylua.toml lua/ + + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "style(format): run stylua" + branch: ${{ github.head_ref }} + + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: ${{ github.ref }} + + docs: + runs-on: ubuntu-latest + if: ${{ github.ref == 'refs/heads/main' }} + steps: + - uses: actions/checkout@v3 + - name: panvimdoc + uses: kdheepak/panvimdoc@main + with: + vimdoc: toggleterm + version: "Neovim >= 0.8.0" + demojify: true + treesitter: true + - name: Push changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + token: ${{ secrets.WORKFLOW_ACCESS_TOKEN }} + commit_message: "chore(build): auto-generate vimdoc" + commit_user_name: "github-actions[bot]" + commit_user_email: "github-actions[bot]@users.noreply.github.com" + commit_author: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>" + + release: + name: release + if: ${{ github.ref == 'refs/heads/main' }} + needs: + - docs + - tests + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v3 + id: release + with: + release-type: simple + package-name: toggleterm.nvim + - uses: actions/checkout@v3 + - name: tag stable versions + if: ${{ steps.release.outputs.release_created }} + run: | + git config user.name github-actions[bot] + git config user.email github-actions[bot]@users.noreply.github.com + git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/.gitignore b/config/neovim/store/lazy-plugins/toggleterm.nvim/.gitignore new file mode 100644 index 00000000..69b142ee --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/.gitignore @@ -0,0 +1 @@ +.tests/ diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/.luarc.json b/config/neovim/store/lazy-plugins/toggleterm.nvim/.luarc.json new file mode 100644 index 00000000..d0bfdee4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/.luarc.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", + "Lua.diagnostics.disable": [ + "redundant-parameter", + "return-type-mismatch", + "assign-type-mismatch", + "cast-local-type", + "missing-parameter" + ], + "Lua.workspace.checkThirdParty": false +} \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/CHANGELOG.md b/config/neovim/store/lazy-plugins/toggleterm.nvim/CHANGELOG.md new file mode 100644 index 00000000..51024ae2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/CHANGELOG.md @@ -0,0 +1,111 @@ +# Changelog + +## [2.11.0](https://github.com/akinsho/toggleterm.nvim/compare/v2.10.0...v2.11.0) (2024-04-22) + + +### Features + +* add string array support to `open_mapping` setting. ([#557](https://github.com/akinsho/toggleterm.nvim/issues/557)) ([5ec59c3](https://github.com/akinsho/toggleterm.nvim/commit/5ec59c3a8ae4f220e40f0d37e1732354ee3ba181)) +* support the CR for nushell ([#561](https://github.com/akinsho/toggleterm.nvim/issues/561)) ([72d2aa2](https://github.com/akinsho/toggleterm.nvim/commit/72d2aa290a8bcd3155d851b3d7a28ea20a1dc1f1)) + + +### Bug Fixes + +* autochdir for custom terminals ([#553](https://github.com/akinsho/toggleterm.nvim/issues/553)) ([dca1c80](https://github.com/akinsho/toggleterm.nvim/commit/dca1c80fb8ec41c97e7c3ef308719d8143fbbb05)) +* clear command ([#565](https://github.com/akinsho/toggleterm.nvim/issues/565)) ([fef08f3](https://github.com/akinsho/toggleterm.nvim/commit/fef08f32b9ca7d08eefc5af34dc416a3ac259bc8)) +* cmd and path now work with paths containing spaces ([#483](https://github.com/akinsho/toggleterm.nvim/issues/483)) ([f059a52](https://github.com/akinsho/toggleterm.nvim/commit/f059a52c3f8adb285cff66882462f67603c1f9ba)) +* column indexing ([#572](https://github.com/akinsho/toggleterm.nvim/issues/572)) ([9e65d60](https://github.com/akinsho/toggleterm.nvim/commit/9e65d60cfa0c33a9ddc9cc9ec77471753f1984df)) +* cursor position after motion ([#563](https://github.com/akinsho/toggleterm.nvim/issues/563)) ([75d3de9](https://github.com/akinsho/toggleterm.nvim/commit/75d3de9d261431dd4d6a68134bb46907c91c2023)) +* ensure `on_choice` operates on exact `items` element ([#566](https://github.com/akinsho/toggleterm.nvim/issues/566)) ([d3fff44](https://github.com/akinsho/toggleterm.nvim/commit/d3fff44252b57da0dc918b5eb7aeee258603a2a7)) + +## [2.10.0](https://github.com/akinsho/toggleterm.nvim/compare/v2.9.0...v2.10.0) (2024-02-12) + + +### Features + +* enable title for floating terminals ([#534](https://github.com/akinsho/toggleterm.nvim/issues/534)) ([d3aa6e8](https://github.com/akinsho/toggleterm.nvim/commit/d3aa6e88c2dcbefd240ffb77a2c77b486a19fa5f)) + + +### Bug Fixes + +* send_lines_to_terminal now honours ID variable when trim_spaces = false ([#541](https://github.com/akinsho/toggleterm.nvim/issues/541)) ([63ac4c8](https://github.com/akinsho/toggleterm.nvim/commit/63ac4c8529604ad247d9426644128de6ebb1f43a)) + +## [2.9.0](https://github.com/akinsho/toggleterm.nvim/compare/v2.8.0...v2.9.0) (2023-12-06) + + +### Features + +* allow operator mapping to send to terminal ([#507](https://github.com/akinsho/toggleterm.nvim/issues/507)) ([5b84866](https://github.com/akinsho/toggleterm.nvim/commit/5b848664989b6deb2c28dad5135c89720915675a)) + + +### Bug Fixes + +* **commands:** call ToggleTermSetName with count ([#497](https://github.com/akinsho/toggleterm.nvim/issues/497)) ([ef1bbff](https://github.com/akinsho/toggleterm.nvim/commit/ef1bbff59c9ab5b468062c33ca183541a3849547)), closes [#496](https://github.com/akinsho/toggleterm.nvim/issues/496) +* **terminal:** clear correctly on windows ([#513](https://github.com/akinsho/toggleterm.nvim/issues/513)) ([0731e99](https://github.com/akinsho/toggleterm.nvim/commit/0731e99de590fb7451eb4fee99470506e012b34d)) + +## [2.8.0](https://github.com/akinsho/toggleterm.nvim/compare/v2.7.1...v2.8.0) (2023-09-11) + + +### Features + +* add `Terminal.find` function ([#486](https://github.com/akinsho/toggleterm.nvim/issues/486)) ([01a84bc](https://github.com/akinsho/toggleterm.nvim/commit/01a84bc642484681933140537c3ff99b10b8a866)) +* add name param to ToggleTerm and TermExec ([#479](https://github.com/akinsho/toggleterm.nvim/issues/479)) ([81ea9f7](https://github.com/akinsho/toggleterm.nvim/commit/81ea9f71a3fd7621fd02b2c74861595378a3c938)) + + +### Bug Fixes + +* **#487:** avoid terminal id collisions in __add ([#490](https://github.com/akinsho/toggleterm.nvim/issues/490)) ([6bec54e](https://github.com/akinsho/toggleterm.nvim/commit/6bec54e73807919b15fc92824fb48be32fb7e8ea)) +* determine custom terminal ids on spawn ([#488](https://github.com/akinsho/toggleterm.nvim/issues/488)) ([8572917](https://github.com/akinsho/toggleterm.nvim/commit/8572917413dd039d1a53b007df5c571e2a3b8ad7)) +* TermExec cmd with config.shell as function ([#467](https://github.com/akinsho/toggleterm.nvim/issues/467)) ([83871e3](https://github.com/akinsho/toggleterm.nvim/commit/83871e3c34837117644d83f422ee6c869b61891f)) + + +### Reverts + +* determine custom terminal ids on spawn ([#488](https://github.com/akinsho/toggleterm.nvim/issues/488)) ([0e4dcb8](https://github.com/akinsho/toggleterm.nvim/commit/0e4dcb8f0914bd191f732cae826df59f174359fe)) + +## [2.7.1](https://github.com/akinsho/toggleterm.nvim/compare/v2.7.0...v2.7.1) (2023-07-10) + + +### Bug Fixes + +* handle errors when switching buffer [#453](https://github.com/akinsho/toggleterm.nvim/issues/453) ([#454](https://github.com/akinsho/toggleterm.nvim/issues/454)) ([029ad96](https://github.com/akinsho/toggleterm.nvim/commit/029ad968fd5a06ac5e29afe083d0a61be68e792b)) +* replace vim.wo with nvim_set_option_value ([#449](https://github.com/akinsho/toggleterm.nvim/issues/449)) ([7da102a](https://github.com/akinsho/toggleterm.nvim/commit/7da102a9c2fa1dd190c11faea03ee1c47af03d02)) +* **terminal:** allow resizing hidden terminals ([bacbaa7](https://github.com/akinsho/toggleterm.nvim/commit/bacbaa7480344e4cfcebdf46fdfc058b3cb04648)), closes [#459](https://github.com/akinsho/toggleterm.nvim/issues/459) + +## [2.7.0](https://github.com/akinsho/toggleterm.nvim/compare/v2.6.0...v2.7.0) (2023-05-22) + + +### Features + +* add a terminal select command ([#429](https://github.com/akinsho/toggleterm.nvim/issues/429)) ([c8574d7](https://github.com/akinsho/toggleterm.nvim/commit/c8574d7a7d2e5682de4479463ddba794390c0e40)) +* allow changing terminal dir in background ([#438](https://github.com/akinsho/toggleterm.nvim/issues/438)) ([f5cf0b1](https://github.com/akinsho/toggleterm.nvim/commit/f5cf0b1eebd95ba4edc69e2fbd13e1a289048d5d)) + + +### Bug Fixes + +* **float:** ensure sidescroll is zero ([43b75f4](https://github.com/akinsho/toggleterm.nvim/commit/43b75f43aa7590228d88945525c737f0ddc05c22)) + +## [2.6.0](https://github.com/akinsho/toggleterm.nvim/compare/v2.5.0...v2.6.0) (2023-04-09) + + +### Features + +* **config:** allow `shell` parameter to be a function ([#423](https://github.com/akinsho/toggleterm.nvim/issues/423)) ([a7857b6](https://github.com/akinsho/toggleterm.nvim/commit/a7857b6cbfdfc98df2a7b61591be16e1020c7a82)) + +## [2.5.0](https://github.com/akinsho/toggleterm.nvim/compare/2.4.0...v2.5.0) (2023-03-31) + + +### ⚠ BREAKING CHANGES + +* switch persist_mode to false ([#410](https://github.com/akinsho/toggleterm.nvim/issues/410)) + +### Features + +* support z-index option for floating windows ([#418](https://github.com/akinsho/toggleterm.nvim/issues/418)) ([0aa9364](https://github.com/akinsho/toggleterm.nvim/commit/0aa936445b895cd5d3387860f96ce424ce32b072)) +* switch persist_mode to false ([#410](https://github.com/akinsho/toggleterm.nvim/issues/410)) ([98e15df](https://github.com/akinsho/toggleterm.nvim/commit/98e15df2c838fe5c3cae1efa36fa5c255fc75aa8)) +* **terminal:** add mechanism to fetch last focused terminal ([#411](https://github.com/akinsho/toggleterm.nvim/issues/411)) ([bfb7a72](https://github.com/akinsho/toggleterm.nvim/commit/bfb7a7254b5d897a5b889484c6a5142951a18b29)) + + +### Miscellaneous Chores + +* release 2.5.0 ([f14cbfd](https://github.com/akinsho/toggleterm.nvim/commit/f14cbfd3141ce35d2738084e40bccf2176a474b2)) diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/LICENSE b/config/neovim/store/lazy-plugins/toggleterm.nvim/LICENSE new file mode 100644 index 00000000..53d1f3d0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/LICENSE @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. + diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/README.md b/config/neovim/store/lazy-plugins/toggleterm.nvim/README.md new file mode 100644 index 00000000..632d057c --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/README.md @@ -0,0 +1,553 @@ +<!-- panvimdoc-ignore-start --> + +<h1 align="center"> + toggleterm.nvim +</h1> + +<!-- panvimdoc-ignore-end --> + +<!-- panvimdoc-ignore-start --> + +<p align="center">A <i>neovim</i> plugin to persist and toggle multiple terminals during an editing session</p> + +<!-- panvimdoc-ignore-end --> + +<!-- panvimdoc-ignore-start --> + +![toggleterm in action](https://user-images.githubusercontent.com/22454918/224485816-8b0cb1b8-b0e6-4da6-9d71-a7299d39f1a0.gif) + +<!-- panvimdoc-ignore-end --> + +### Multiple orientations + +- **Float** + +<!-- panvimdoc-ignore-start --> + +![floating window](https://user-images.githubusercontent.com/22454918/115306123-42d2ac00-a15f-11eb-84fc-c4246ee82a09.png) + +<!-- panvimdoc-ignore-end --> + +- **Vertical** + +<!-- panvimdoc-ignore-start --> + +![vertical-terms](https://user-images.githubusercontent.com/22454918/224485828-cd509271-8288-46e2-afb4-4e520fa049cb.png) + +<!-- panvimdoc-ignore-end --> + +- **Tab** + +<!-- panvimdoc-ignore-start --> + +![tab orientation](https://user-images.githubusercontent.com/22454918/133490969-6a59e623-79db-4ca7-a73b-ef4b24a73b91.gif) + +<!-- panvimdoc-ignore-end --> + +### Send commands to different terminals + +<!-- panvimdoc-ignore-start --> + +![exec](https://user-images.githubusercontent.com/22454918/112119367-36d1e980-8bb5-11eb-9787-5936391127a3.gif) + +<!-- panvimdoc-ignore-end --> + +### Winbar (Experimental/Nightly ONLY) + +<!-- panvimdoc-ignore-start --> + +<img width="1728" alt="image" src="https://user-images.githubusercontent.com/22454918/179199998-75ec16cb-8271-490e-925f-6c82c50ffc5d.png"> + +<!-- panvimdoc-ignore-end --> + +## Requirements + +This plugin only works in _Neovim 0.7_ or newer. + +## Installation + +Using [packer](https://github.com/wbthomason/packer.nvim) in lua + +```lua +use {"akinsho/toggleterm.nvim", tag = '*', config = function() + require("toggleterm").setup() +end} +``` + +Using [lazy.nvim](https://github.com/folke/lazy.nvim) in lua + +```lua +{ + -- amongst your other plugins + {'akinsho/toggleterm.nvim', version = "*", config = true} + -- or + {'akinsho/toggleterm.nvim', version = "*", opts = {--[[ things you want to change go here]]}} +} +``` + +Using [vim-plug](https://github.com/junegunn/vim-plug) in vimscript + +```vim +Plug 'akinsho/toggleterm.nvim', {'tag' : '*'} + +lua require("toggleterm").setup() +``` + +You can/should specify a tag for the current major version of the plugin, to avoid breaking changes as this plugin evolves. +To use a version of this plugin compatible with nvim versions less than 0.7 please use the tag `v1.*`. + +## Notices + +- **28/07/1990** — If using `persist_mode` terminal mappings should be changed to use `wincmd` instead otherwise persist mode will not work correctly. See [here](#terminal-window-mappings) for details. + +## Why? + +Neovim's terminal is a very cool, but not super ergonomic tool to use. I find that I often want to +set a process going and leave it to continue to run in the background. I don't need to see it all the time. +I just need to be able to refer back to it at intervals. I also sometimes want to create a new terminal and run a few commands. + +Sometimes I want these side by side, and I _really_ want these terminals to be easy to access. +I also want my terminal to look different from non-terminal buffers, so I use `winhighlight` to darken them based on the `Normal` +background colour. + +This is the exact use case this was designed for. If that's your use case this might work for you. + +## Roadmap + +All I really want this plugin to be is what I described above. A wrapper around the terminal functionality. + +It basically (almost) does all that I need it to. + +I won't be turning this into a REPL plugin or doing a bunch of complex stuff. +If you find any issues, _please_ consider a _pull request_ not an issue. +I'm also going to be pretty conservative about what I add. + +### Setup + +This plugin must be explicitly enabled by using `require("toggleterm").setup{}` + +Setting the `open_mapping` key to use for toggling the terminal(s) will set up mappings for _normal_ mode. The `open_mapping` can be a key string or an array of key strings. +If you prefix the mapping with a number that particular terminal will be opened. Otherwise if a prefix is not set, then the last toggled terminal will be opened. In case there are multiple terminals opened they'll all be closed, and on the next mapping key they'll be restored. + +If you set the `insert_mappings` key to `true`, the mapping will also take effect in insert mode; similarly setting `terminal_mappings` to `true` will have the mappings take effect in the opened terminal. + +However you will not be able to use a count with the open mapping in terminal and insert modes. You can create buffer specific mappings to exit terminal mode and then use a count with the open mapping. Check _Terminal window mappings_ for an example of how to do this. + +alternatively you can do this manually (not recommended but, your prerogative) + +```vim +" set +autocmd TermEnter term://*toggleterm#* + \ tnoremap <silent><c-t> <Cmd>exe v:count1 . "ToggleTerm"<CR> + +" By applying the mappings this way you can pass a count to your +" mapping to open a specific window. +" For example: 2<C-t> will open terminal 2 +nnoremap <silent><c-t> <Cmd>exe v:count1 . "ToggleTerm"<CR> +inoremap <silent><c-t> <Esc><Cmd>exe v:count1 . "ToggleTerm"<CR> +``` + +**NOTE**: Please ensure you have set `hidden` in your neovim config, otherwise the terminals will be discarded when closed. + +**WARNING**: Please do not copy and paste this configuration! It is here to show what options are available. It is not written to be used as is. + +```lua +require("toggleterm").setup{ + -- size can be a number or function which is passed the current terminal + size = 20 | function(term) + if term.direction == "horizontal" then + return 15 + elseif term.direction == "vertical" then + return vim.o.columns * 0.4 + end + end, + open_mapping = [[<c-\>]], -- or { [[<c-\>]], [[<c-¥>]] } if you also use a Japanese keyboard. + on_create = fun(t: Terminal), -- function to run when the terminal is first created + on_open = fun(t: Terminal), -- function to run when the terminal opens + on_close = fun(t: Terminal), -- function to run when the terminal closes + on_stdout = fun(t: Terminal, job: number, data: string[], name: string) -- callback for processing output on stdout + on_stderr = fun(t: Terminal, job: number, data: string[], name: string) -- callback for processing output on stderr + on_exit = fun(t: Terminal, job: number, exit_code: number, name: string) -- function to run when terminal process exits + hide_numbers = true, -- hide the number column in toggleterm buffers + shade_filetypes = {}, + autochdir = false, -- when neovim changes it current directory the terminal will change it's own when next it's opened + highlights = { + -- highlights which map to a highlight group name and a table of it's values + -- NOTE: this is only a subset of values, any group placed here will be set for the terminal window split + Normal = { + guibg = "<VALUE-HERE>", + }, + NormalFloat = { + link = 'Normal' + }, + FloatBorder = { + guifg = "<VALUE-HERE>", + guibg = "<VALUE-HERE>", + }, + }, + shade_terminals = true, -- NOTE: this option takes priority over highlights specified so if you specify Normal highlights you should set this to false + shading_factor = '<number>', -- the percentage by which to lighten terminal background, default: -30 (gets multiplied by -3 if background is light) + start_in_insert = true, + insert_mappings = true, -- whether or not the open mapping applies in insert mode + terminal_mappings = true, -- whether or not the open mapping applies in the opened terminals + persist_size = true, + persist_mode = true, -- if set to true (default) the previous terminal mode will be remembered + direction = 'vertical' | 'horizontal' | 'tab' | 'float', + close_on_exit = true, -- close the terminal window when the process exits + -- Change the default shell. Can be a string or a function returning a string + shell = vim.o.shell, + auto_scroll = true, -- automatically scroll to the bottom on terminal output + -- This field is only relevant if direction is set to 'float' + float_opts = { + -- The border key is *almost* the same as 'nvim_open_win' + -- see :h nvim_open_win for details on borders however + -- the 'curved' border is a custom border type + -- not natively supported but implemented in this plugin. + border = 'single' | 'double' | 'shadow' | 'curved' | ... other options supported by win open + -- like `size`, width, height, row, and col can be a number or function which is passed the current terminal + width = <value>, + height = <value>, + row = <value>, + col = <value>, + winblend = 3, + zindex = <value>, + title_pos = 'left' | 'center' | 'right', position of the title of the floating window + }, + winbar = { + enabled = false, + name_formatter = function(term) -- term: Terminal + return term.name + end + }, +} +``` + +### Usage + +### `ToggleTerm` + +This is the command the mappings call under the hood. You can use it directly +and prefix it with a count to target a specific terminal. This function also takes +arguments `size`, `dir`, `direction` and `name`. e.g. + +```vim +:ToggleTerm size=40 dir=~/Desktop direction=horizontal name=desktop +``` + +If `dir` is specified on creation toggle term will open at the specified directory. +If the terminal has already been opened at a particular directory it will remain in that directory. + +The directory can also be specified as `git_dir` which toggleterm will then +use to try and derive the git repo directory. +_NOTE_: This will not work for `git-worktrees` or other more complex setups. + +If `size` is specified, and the command opens a split (horizontal/vertical) terminal, +the height/width of all terminals in the same direction will be changed to `size`. + +If `direction` is specified, and the command opens a terminal, +the terminal will be changed to the specified direction. + +If `name` is specified, the display name is set for the toggled terminal. This name will be visible +when using `TermSelect` command to indicate the specific terminal. + +`size` and `direction` are ignored if the command closes a terminal. + +#### Caveats + +- Having multiple terminals with different directions open at the same time is unsupported. + +### `ToggleTermToggleAll` + +This command allows you to open all the previously toggled terminal in one go +or close all the open terminals at once. + +### `TermExec` + +This command allows you to open a terminal with a specific action. +e.g. `2TermExec cmd="git status" dir=~/<my-repo-path>` will run git status in terminal 2. +note that the `cmd` argument **must be quoted**. + +_NOTE:_ the `dir` argument can also be _optionally_ quoted if it contains spaces. + +The `cmd` and `dir` arguments can also expand the same special keywords as `:h expand` e.g. +`TermExec cmd="echo %"` will be expanded to `TermExec cmd="echo /file/example"` + +These special keywords can be escaped using the `\` character, if you want to print character as is. + +The `size`, `direction` and `name` arguments are like the `size`, `direction` and `name` arguments of `ToggleTerm`. + +By default, focus is returned to the original window after executing the command +(except for floating terminals). Use argument `go_back=0` to disable this behaviour. + +You can send commands to a terminal without opening its window by using the `open=0` argument. + +see `:h expand()` for more details + +### TermSelect + +This command uses `vim.ui.select` to allow a user to select a terminal to open +or to focus it if it's already open. This can be useful if you have a lot of +terminals and want to open a specific one. + +### Sending lines to the terminal + +You can "send lines" to the toggled terminals with the following commands: + +- `:ToggleTermSendCurrentLine <T_ID>`: sends the whole line where you are standing with your cursor +- `:ToggleTermSendVisualLines <T_ID>`: sends all the (whole) lines in your visual selection +- `:ToggleTermSendVisualSelection <T_ID>`: sends only the visually selected text (this can be a block of text or a selection in a single line) + +(`<T_ID` is an optional terminal ID parameter, which defines where should we send the lines. +If the parameter is not provided, then the default is the `first terminal`) + +Alternatively, for more fine-grained control and use in mappings, in lua: + +```lua +local trim_spaces = true +vim.keymap.set("v", "<space>s", function() + require("toggleterm").send_lines_to_terminal("single_line", trim_spaces, { args = vim.v.count }) +end) + -- Replace with these for the other two options + -- require("toggleterm").send_lines_to_terminal("visual_lines", trim_spaces, { args = vim.v.count }) + -- require("toggleterm").send_lines_to_terminal("visual_selection", trim_spaces, { args = vim.v.count }) + +-- For use as an operator map: +-- Send motion to terminal +vim.keymap.set("n", [[<leader><c-\>]], function() + set_opfunc(function(motion_type) + require("toggleterm").send_lines_to_terminal(motion_type, false, { args = vim.v.count }) + end) + vim.api.nvim_feedkeys("g@", "n", false) +end) +-- Double the command to send line to terminal +vim.keymap.set("n", [[<leader><c-\><c-\>]], function() + set_opfunc(function(motion_type) + require("toggleterm").send_lines_to_terminal(motion_type, false, { args = vim.v.count }) + end) + vim.api.nvim_feedkeys("g@_", "n", false) +end) +-- Send whole file +vim.keymap.set("n", [[<leader><leader><c-\>]], function() + set_opfunc(function(motion_type) + require("toggleterm").send_lines_to_terminal(motion_type, false, { args = vim.v.count }) + end) + vim.api.nvim_feedkeys("ggg@G''", "n", false) +end) +``` + +Set `trim_spaces=false` for sending to REPLs for whitespace-sensitive languages like python. +(For python, you probably want to start ipython with `ipython --no-autoindent`.) + +<!-- panvimdoc-ignore-start --> + +Example: + +<!-- panvimdoc-ignore-end --> + +<!-- panvimdoc-ignore-start --> + +<video src="https://user-images.githubusercontent.com/18753533/159889865-724becab-877b-45a2-898e-820afd6a4ee1.mov" controls="controls" muted="muted" height="640px"></video> + +<!-- panvimdoc-ignore-end --> + +### ToggleTermSetName + +This function allows setting a display name for a terminal. This name is primarily used inside the winbar, and can be a more descriptive way +to remember, which terminal is for what. + +You can map this to a key and call it with a count, which will then prompt you a name for the terminal with the matching ID. +Alternatively you can call it with just the name e.g. `:ToggleTermSetName work<CR>` this will the prompt you for which terminal it should apply to. +Lastly you can call it without any arguments, and it will prompt you for which terminal it should apply to then prompt you for the name to use. + +### Set terminal shading + +This plugin automatically shades terminal filetypes to be darker than other window +you can disable this by setting `shade_terminals = false` in the setup object + +```lua +require'toggleterm'.setup { + shade_terminals = false +} +``` + +alternatively you can set, _which_ filetypes should be shaded by setting + +```lua +-- fzf is just an example +require'toggleterm'.setup { + shade_filetypes = { "none", "fzf" } +} + +``` + +setting `"none"` will allow normal terminal buffers to be highlighted. + +### Set persistent size + +By default, this plugin will persist the size of horizontal and vertical terminals. +Split terminals in the same direction always have the same size. +You can disable this behaviour by setting `persist_size = false` in the setup object. +Disabling this behaviour forces the opening terminal size to the `size` defined in the setup object. + +```lua +require'toggleterm'.setup{ + persist_size = false +} +``` + +### Terminal window mappings + +It can be helpful to add mappings to make moving in and out of a terminal easier +once toggled, whilst still keeping it open. + +```lua +function _G.set_terminal_keymaps() + local opts = {buffer = 0} + vim.keymap.set('t', '<esc>', [[<C-\><C-n>]], opts) + vim.keymap.set('t', 'jk', [[<C-\><C-n>]], opts) + vim.keymap.set('t', '<C-h>', [[<Cmd>wincmd h<CR>]], opts) + vim.keymap.set('t', '<C-j>', [[<Cmd>wincmd j<CR>]], opts) + vim.keymap.set('t', '<C-k>', [[<Cmd>wincmd k<CR>]], opts) + vim.keymap.set('t', '<C-l>', [[<Cmd>wincmd l<CR>]], opts) + vim.keymap.set('t', '<C-w>', [[<C-\><C-n><C-w>]], opts) +end + +-- if you only want these mappings for toggle term use term://*toggleterm#* instead +vim.cmd('autocmd! TermOpen term://* lua set_terminal_keymaps()') +``` + +### Custom Terminals + +![lazy git](https://user-images.githubusercontent.com/22454918/116447435-e69f1480-a84f-11eb-86dd-19fa29646aa1.png) +_using [lazygit](https://github.com/jesseduffield/lazygit)_ + +Toggleterm also exposes the `Terminal` class so that this can be used to create custom terminals +for showing terminal UIs like `lazygit`, `htop` etc. + +Each terminal can take the following arguments: + +```lua +Terminal:new { + cmd = string -- command to execute when creating the terminal e.g. 'top' + display_name = string -- the name of the terminal + direction = string -- the layout for the terminal, same as the main config options + dir = string -- the directory for the terminal + close_on_exit = bool -- close the terminal window when the process exits + highlights = table -- a table with highlights + env = table -- key:value table with environmental variables passed to jobstart() + clear_env = bool -- use only environmental variables from `env`, passed to jobstart() + on_open = fun(t: Terminal) -- function to run when the terminal opens + on_close = fun(t: Terminal) -- function to run when the terminal closes + auto_scroll = boolean -- automatically scroll to the bottom on terminal output + -- callbacks for processing the output + on_stdout = fun(t: Terminal, job: number, data: string[], name: string) -- callback for processing output on stdout + on_stderr = fun(t: Terminal, job: number, data: string[], name: string) -- callback for processing output on stderr + on_exit = fun(t: Terminal, job: number, exit_code: number, name: string) -- function to run when terminal process exits +} +``` + +If you want to spawn a custom terminal without running any command, you can omit the `cmd` option. + +#### Custom terminal usage + +```lua +local Terminal = require('toggleterm.terminal').Terminal +local lazygit = Terminal:new({ cmd = "lazygit", hidden = true }) + +function _lazygit_toggle() + lazygit:toggle() +end + +vim.api.nvim_set_keymap("n", "<leader>g", "<cmd>lua _lazygit_toggle()<CR>", {noremap = true, silent = true}) +``` + +This will create a new terminal, but the specified command is not being run immediately. +The command will run once the terminal is opened. Alternatively `term:spawn()` can be used +to start the command in a background buffer without opening a terminal window yet. If the +`hidden` key is set to true, this terminal will not be toggled by normal toggleterm commands +such as `:ToggleTerm` or the open mapping. It will only open and close by using the returned +terminal object. A mapping for toggling the terminal can be set as in the example above. + +Alternatively the terminal can be specified with a count, which is the number that can be used +to trigger this specific terminal. This can then be triggered using the current count e.g. +`:5ToggleTerm<CR>` + +```lua +local lazygit = Terminal:new({ cmd = "lazygit", count = 5 }) +``` + +You can also set a custom layout for a terminal. + +```lua +local lazygit = Terminal:new({ + cmd = "lazygit", + dir = "git_dir", + direction = "float", + float_opts = { + border = "double", + }, + -- function to run on opening the terminal + on_open = function(term) + vim.cmd("startinsert!") + vim.api.nvim_buf_set_keymap(term.bufnr, "n", "q", "<cmd>close<CR>", {noremap = true, silent = true}) + end, + -- function to run on closing the terminal + on_close = function(term) + vim.cmd("startinsert!") + end, +}) + +function _lazygit_toggle() + lazygit:toggle() +end + +vim.api.nvim_set_keymap("n", "<leader>g", "<cmd>lua _lazygit_toggle()<CR>", {noremap = true, silent = true}) +``` + +**WARNING**: do not use any of the private functionality of the terminal or other non-public parts of the API as these +can change in the future. + +### Statusline + +To tell each terminal apart you can use the terminal buffer variable `b:toggle_number` +in your statusline + +```vim +" this is pseudo code +let statusline .= '%{&ft == "toggleterm" ? "terminal (".b:toggle_number.")" : ""}' +``` + +### Custom commands + +You can create your own commands by using the lua functions this plugin provides directly + +```vim +command! -count=1 TermGitPush lua require'toggleterm'.exec("git push", <count>, 12) +command! -count=1 TermGitPushF lua require'toggleterm'.exec("git push -f", <count>, 12) +``` + +### Open multiple terminals side-by-side + +| Direction | Supported | +| ---------- | --------- | +| vertical | ✔️ | +| horizontal | ✔️ | +| tab | ✖️ | +| float | ✖️ | + +In your first terminal, you need to leave the `TERMINAL` mode using <kbd>C-\\</kbd><kbd>C-N</kbd> which can be remapped to <kbd>Esc</kbd> for ease of use. +![image](https://user-images.githubusercontent.com/31947091/133395516-22fef1e6-633d-4964-9175-f76fabf66794.png) + +Then you type on: `2<C-\>`, and the result: +![image](https://user-images.githubusercontent.com/31947091/133396789-fdf68b30-3a8c-440b-822f-6549f282c4fc.png) + +Explain: + +- `2`: this is the terminal's number (or ID), your first terminal is `1` (e.g. your 3rd terminal will be `3<C-\>`, so on). +- <kbd>C-\\</kbd>: this is the combined key mapping to the command `:ToggleTerm`. + +### FAQ + +#### How do I get this plugin to work with Powershell? + +Please check out the [Wiki section on this topic](https://github.com/akinsho/toggleterm.nvim/wiki/Tips-and-Tricks#using-toggleterm-with-powershell). diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/doc/tags b/config/neovim/store/lazy-plugins/toggleterm.nvim/doc/tags new file mode 100644 index 00000000..346bac45 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/doc/tags @@ -0,0 +1,9 @@ +almost toggleterm.txt /*almost* +toggleterm-installation toggleterm.txt /*toggleterm-installation* +toggleterm-links toggleterm.txt /*toggleterm-links* +toggleterm-notices toggleterm.txt /*toggleterm-notices* +toggleterm-requirements toggleterm.txt /*toggleterm-requirements* +toggleterm-roadmap toggleterm.txt /*toggleterm-roadmap* +toggleterm-table-of-contents toggleterm.txt /*toggleterm-table-of-contents* +toggleterm-why? toggleterm.txt /*toggleterm-why?* +toggleterm.txt toggleterm.txt /*toggleterm.txt* diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/doc/toggleterm.txt b/config/neovim/store/lazy-plugins/toggleterm.nvim/doc/toggleterm.txt new file mode 100644 index 00000000..7e08147f --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/doc/toggleterm.txt @@ -0,0 +1,575 @@ +*toggleterm.txt* For Neovim >= 0.8.0 Last change: 2024 May 19 + +============================================================================== +Table of Contents *toggleterm-table-of-contents* + + - Requirements |toggleterm-requirements| + - Installation |toggleterm-installation| + - Notices |toggleterm-notices| + - Why? |toggleterm-why?| + - Roadmap |toggleterm-roadmap| +1. Links |toggleterm-links| + +MULTIPLE ORIENTATIONS ~ + +- **Float** + +- **Vertical** + +- **Tab** + + +SEND COMMANDS TO DIFFERENT TERMINALS ~ + + +WINBAR (EXPERIMENTAL/NIGHTLY ONLY) ~ + + +REQUIREMENTS *toggleterm-requirements* + +This plugin only works in _Neovim 0.7_ or newer. + + +INSTALLATION *toggleterm-installation* + +Using packer <https://github.com/wbthomason/packer.nvim> in lua + +>lua + use {"akinsho/toggleterm.nvim", tag = '*', config = function() + require("toggleterm").setup() + end} +< + +Using lazy.nvim <https://github.com/folke/lazy.nvim> in lua + +>lua + { + -- amongst your other plugins + {'akinsho/toggleterm.nvim', version = "*", config = true} + -- or + {'akinsho/toggleterm.nvim', version = "*", opts = {--[[ things you want to change go here]]}} + } +< + +Using vim-plug <https://github.com/junegunn/vim-plug> in vimscript + +>vim + Plug 'akinsho/toggleterm.nvim', {'tag' : '*'} + + lua require("toggleterm").setup() +< + +You can/should specify a tag for the current major version of the plugin, to +avoid breaking changes as this plugin evolves. To use a version of this plugin +compatible with nvim versions less than 0.7 please use the tag `v1.*`. + + +NOTICES *toggleterm-notices* + +- **28/07/1990** — If using `persist_mode` terminal mappings should be changed to use `wincmd` instead otherwise persist mode will not work correctly. See |toggleterm-here| for details. + + +WHY? *toggleterm-why?* + +Neovim’s terminal is a very cool, but not super ergonomic tool to use. I find +that I often want to set a process going and leave it to continue to run in the +background. I don’t need to see it all the time. I just need to be able to +refer back to it at intervals. I also sometimes want to create a new terminal +and run a few commands. + +Sometimes I want these side by side, and I _really_ want these terminals to be +easy to access. I also want my terminal to look different from non-terminal +buffers, so I use `winhighlight` to darken them based on the `Normal` +background colour. + +This is the exact use case this was designed for. If that’s your use case +this might work for you. + + +ROADMAP *toggleterm-roadmap* + +All I really want this plugin to be is what I described above. A wrapper around +the terminal functionality. + +It basically (almost) does all that I need it to. + +I won’t be turning this into a REPL plugin or doing a bunch of complex stuff. +If you find any issues, _please_ consider a _pull request_ not an issue. I’m +also going to be pretty conservative about what I add. + + +SETUP ~ + +This plugin must be explicitly enabled by using `require("toggleterm").setup{}` + +Setting the `open_mapping` key to use for toggling the terminal(s) will set up +mappings for _normal_ mode. The `open_mapping` can be a key string or an array +of key strings. If you prefix the mapping with a number that particular +terminal will be opened. Otherwise if a prefix is not set, then the last +toggled terminal will be opened. In case there are multiple terminals opened +they’ll all be closed, and on the next mapping key they’ll be restored. + +If you set the `insert_mappings` key to `true`, the mapping will also take +effect in insert mode; similarly setting `terminal_mappings` to `true` will +have the mappings take effect in the opened terminal. + +However you will not be able to use a count with the open mapping in terminal +and insert modes. You can create buffer specific mappings to exit terminal mode +and then use a count with the open mapping. Check _Terminal window mappings_ +for an example of how to do this. + +alternatively you can do this manually (not recommended but, your prerogative) + +>vim + " set + autocmd TermEnter term://*toggleterm#* + \ tnoremap <silent><c-t> <Cmd>exe v:count1 . "ToggleTerm"<CR> + + " By applying the mappings this way you can pass a count to your + " mapping to open a specific window. + " For example: 2<C-t> will open terminal 2 + nnoremap <silent><c-t> <Cmd>exe v:count1 . "ToggleTerm"<CR> + inoremap <silent><c-t> <Esc><Cmd>exe v:count1 . "ToggleTerm"<CR> +< + +**NOTE**Please ensure you have set `hidden` in your neovim config, otherwise +the terminals will be discarded when closed. + +**WARNING**Please do not copy and paste this configuration! It is here to show +what options are available. It is not written to be used as is. + +>lua + require("toggleterm").setup{ + -- size can be a number or function which is passed the current terminal + size = 20 | function(term) + if term.direction == "horizontal" then + return 15 + elseif term.direction == "vertical" then + return vim.o.columns * 0.4 + end + end, + open_mapping = [[<c-\>]], -- or { [[<c-\>]], [[<c-¥>]] } if you also use a Japanese keyboard. + on_create = fun(t: Terminal), -- function to run when the terminal is first created + on_open = fun(t: Terminal), -- function to run when the terminal opens + on_close = fun(t: Terminal), -- function to run when the terminal closes + on_stdout = fun(t: Terminal, job: number, data: string[], name: string) -- callback for processing output on stdout + on_stderr = fun(t: Terminal, job: number, data: string[], name: string) -- callback for processing output on stderr + on_exit = fun(t: Terminal, job: number, exit_code: number, name: string) -- function to run when terminal process exits + hide_numbers = true, -- hide the number column in toggleterm buffers + shade_filetypes = {}, + autochdir = false, -- when neovim changes it current directory the terminal will change it's own when next it's opened + highlights = { + -- highlights which map to a highlight group name and a table of it's values + -- NOTE: this is only a subset of values, any group placed here will be set for the terminal window split + Normal = { + guibg = "<VALUE-HERE>", + }, + NormalFloat = { + link = 'Normal' + }, + FloatBorder = { + guifg = "<VALUE-HERE>", + guibg = "<VALUE-HERE>", + }, + }, + shade_terminals = true, -- NOTE: this option takes priority over highlights specified so if you specify Normal highlights you should set this to false + shading_factor = '<number>', -- the percentage by which to lighten terminal background, default: -30 (gets multiplied by -3 if background is light) + start_in_insert = true, + insert_mappings = true, -- whether or not the open mapping applies in insert mode + terminal_mappings = true, -- whether or not the open mapping applies in the opened terminals + persist_size = true, + persist_mode = true, -- if set to true (default) the previous terminal mode will be remembered + direction = 'vertical' | 'horizontal' | 'tab' | 'float', + close_on_exit = true, -- close the terminal window when the process exits + -- Change the default shell. Can be a string or a function returning a string + shell = vim.o.shell, + auto_scroll = true, -- automatically scroll to the bottom on terminal output + -- This field is only relevant if direction is set to 'float' + float_opts = { + -- The border key is *almost* the same as 'nvim_open_win' + -- see :h nvim_open_win for details on borders however + -- the 'curved' border is a custom border type + -- not natively supported but implemented in this plugin. + border = 'single' | 'double' | 'shadow' | 'curved' | ... other options supported by win open + -- like `size`, width, height, row, and col can be a number or function which is passed the current terminal + width = <value>, + height = <value>, + row = <value>, + col = <value>, + winblend = 3, + zindex = <value>, + title_pos = 'left' | 'center' | 'right', position of the title of the floating window + }, + winbar = { + enabled = false, + name_formatter = function(term) -- term: Terminal + return term.name + end + }, + } +< + + +USAGE ~ + + +TOGGLETERM ~ + +This is the command the mappings call under the hood. You can use it directly +and prefix it with a count to target a specific terminal. This function also +takes arguments `size`, `dir`, `direction` and `name`. e.g. + +>vim + :ToggleTerm size=40 dir=~/Desktop direction=horizontal name=desktop +< + +If `dir` is specified on creation toggle term will open at the specified +directory. If the terminal has already been opened at a particular directory it +will remain in that directory. + +The directory can also be specified as `git_dir` which toggleterm will then use +to try and derive the git repo directory. _NOTE_This will not work for +`git-worktrees` or other more complex setups. + +If `size` is specified, and the command opens a split (horizontal/vertical) +terminal, the height/width of all terminals in the same direction will be +changed to `size`. + +If `direction` is specified, and the command opens a terminal, the terminal +will be changed to the specified direction. + +If `name` is specified, the display name is set for the toggled terminal. This +name will be visible when using `TermSelect` command to indicate the specific +terminal. + +`size` and `direction` are ignored if the command closes a terminal. + + +CAVEATS + +- Having multiple terminals with different directions open at the same time is unsupported. + + +TOGGLETERMTOGGLEALL ~ + +This command allows you to open all the previously toggled terminal in one go +or close all the open terminals at once. + + +TERMEXEC ~ + +This command allows you to open a terminal with a specific action. +e.g. `2TermExec cmd="git status" dir=~/<my-repo-path>` will run git status in +terminal 2. note that the `cmd` argument **must be quoted**. + +_NOTE:_ the `dir` argument can also be _optionally_ quoted if it contains +spaces. + +The `cmd` and `dir` arguments can also expand the same special keywords as +|expand| e.g. `TermExec cmd="echo %"` will be expanded to `TermExec cmd="echo +/file/example"` + +These special keywords can be escaped using the `\` character, if you want to +print character as is. + +The `size`, `direction` and `name` arguments are like the `size`, `direction` +and `name` arguments of `ToggleTerm`. + +By default, focus is returned to the original window after executing the +command (except for floating terminals). Use argument `go_back=0` to disable +this behaviour. + +You can send commands to a terminal without opening its window by using the +`open=0` argument. + +see |expand()| for more details + + +TERMSELECT ~ + +This command uses `vim.ui.select` to allow a user to select a terminal to open +or to focus it if it’s already open. This can be useful if you have a lot of +terminals and want to open a specific one. + + +SENDING LINES TO THE TERMINAL ~ + +You can "send lines" to the toggled terminals with the following commands: + +- `:ToggleTermSendCurrentLine <T_ID>`sends the whole line where you are standing with your cursor +- `:ToggleTermSendVisualLines <T_ID>`sends all the (whole) lines in your visual selection +- `:ToggleTermSendVisualSelection <T_ID>`sends only the visually selected text (this can be a block of text or a selection in a single line) + +(`<T_ID` is an optional terminal ID parameter, which defines where should we +send the lines. If the parameter is not provided, then the default is the +`first terminal`) + +Alternatively, for more fine-grained control and use in mappings, in lua: + +>lua + local trim_spaces = true + vim.keymap.set("v", "<space>s", function() + require("toggleterm").send_lines_to_terminal("single_line", trim_spaces, { args = vim.v.count }) + end) + -- Replace with these for the other two options + -- require("toggleterm").send_lines_to_terminal("visual_lines", trim_spaces, { args = vim.v.count }) + -- require("toggleterm").send_lines_to_terminal("visual_selection", trim_spaces, { args = vim.v.count }) + + -- For use as an operator map: + -- Send motion to terminal + vim.keymap.set("n", [[<leader><c-\>]], function() + set_opfunc(function(motion_type) + require("toggleterm").send_lines_to_terminal(motion_type, false, { args = vim.v.count }) + end) + vim.api.nvim_feedkeys("g@", "n", false) + end) + -- Double the command to send line to terminal + vim.keymap.set("n", [[<leader><c-\><c-\>]], function() + set_opfunc(function(motion_type) + require("toggleterm").send_lines_to_terminal(motion_type, false, { args = vim.v.count }) + end) + vim.api.nvim_feedkeys("g@_", "n", false) + end) + -- Send whole file + vim.keymap.set("n", [[<leader><leader><c-\>]], function() + set_opfunc(function(motion_type) + require("toggleterm").send_lines_to_terminal(motion_type, false, { args = vim.v.count }) + end) + vim.api.nvim_feedkeys("ggg@G''", "n", false) + end) +< + +Set `trim_spaces=false` for sending to REPLs for whitespace-sensitive languages +like python. (For python, you probably want to start ipython with `ipython +--no-autoindent`.) + + +TOGGLETERMSETNAME ~ + +This function allows setting a display name for a terminal. This name is +primarily used inside the winbar, and can be a more descriptive way to +remember, which terminal is for what. + +You can map this to a key and call it with a count, which will then prompt you +a name for the terminal with the matching ID. Alternatively you can call it +with just the name e.g. `:ToggleTermSetName work<CR>` this will the prompt you +for which terminal it should apply to. Lastly you can call it without any +arguments, and it will prompt you for which terminal it should apply to then +prompt you for the name to use. + + +SET TERMINAL SHADING ~ + +This plugin automatically shades terminal filetypes to be darker than other +window you can disable this by setting `shade_terminals = false` in the setup +object + +>lua + require'toggleterm'.setup { + shade_terminals = false + } +< + +alternatively you can set, _which_ filetypes should be shaded by setting + +>lua + -- fzf is just an example + require'toggleterm'.setup { + shade_filetypes = { "none", "fzf" } + } +< + +setting `"none"` will allow normal terminal buffers to be highlighted. + + +SET PERSISTENT SIZE ~ + +By default, this plugin will persist the size of horizontal and vertical +terminals. Split terminals in the same direction always have the same size. You +can disable this behaviour by setting `persist_size = false` in the setup +object. Disabling this behaviour forces the opening terminal size to the `size` +defined in the setup object. + +>lua + require'toggleterm'.setup{ + persist_size = false + } +< + + +TERMINAL WINDOW MAPPINGS ~ + +It can be helpful to add mappings to make moving in and out of a terminal +easier once toggled, whilst still keeping it open. + +>lua + function _G.set_terminal_keymaps() + local opts = {buffer = 0} + vim.keymap.set('t', '<esc>', [[<C-\><C-n>]], opts) + vim.keymap.set('t', 'jk', [[<C-\><C-n>]], opts) + vim.keymap.set('t', '<C-h>', [[<Cmd>wincmd h<CR>]], opts) + vim.keymap.set('t', '<C-j>', [[<Cmd>wincmd j<CR>]], opts) + vim.keymap.set('t', '<C-k>', [[<Cmd>wincmd k<CR>]], opts) + vim.keymap.set('t', '<C-l>', [[<Cmd>wincmd l<CR>]], opts) + vim.keymap.set('t', '<C-w>', [[<C-\><C-n><C-w>]], opts) + end + + -- if you only want these mappings for toggle term use term://*toggleterm#* instead + vim.cmd('autocmd! TermOpen term://* lua set_terminal_keymaps()') +< + + +CUSTOM TERMINALS ~ + +_using lazygit_ + +Toggleterm also exposes the `Terminal` class so that this can be used to create +custom terminals for showing terminal UIs like `lazygit`, `htop` etc. + +Each terminal can take the following arguments: + +>lua + Terminal:new { + cmd = string -- command to execute when creating the terminal e.g. 'top' + display_name = string -- the name of the terminal + direction = string -- the layout for the terminal, same as the main config options + dir = string -- the directory for the terminal + close_on_exit = bool -- close the terminal window when the process exits + highlights = table -- a table with highlights + env = table -- key:value table with environmental variables passed to jobstart() + clear_env = bool -- use only environmental variables from `env`, passed to jobstart() + on_open = fun(t: Terminal) -- function to run when the terminal opens + on_close = fun(t: Terminal) -- function to run when the terminal closes + auto_scroll = boolean -- automatically scroll to the bottom on terminal output + -- callbacks for processing the output + on_stdout = fun(t: Terminal, job: number, data: string[], name: string) -- callback for processing output on stdout + on_stderr = fun(t: Terminal, job: number, data: string[], name: string) -- callback for processing output on stderr + on_exit = fun(t: Terminal, job: number, exit_code: number, name: string) -- function to run when terminal process exits + } +< + +If you want to spawn a custom terminal without running any command, you can +omit the `cmd` option. + + +CUSTOM TERMINAL USAGE + +>lua + local Terminal = require('toggleterm.terminal').Terminal + local lazygit = Terminal:new({ cmd = "lazygit", hidden = true }) + + function _lazygit_toggle() + lazygit:toggle() + end + + vim.api.nvim_set_keymap("n", "<leader>g", "<cmd>lua _lazygit_toggle()<CR>", {noremap = true, silent = true}) +< + +This will create a new terminal, but the specified command is not being run +immediately. The command will run once the terminal is opened. Alternatively +`term:spawn()` can be used to start the command in a background buffer without +opening a terminal window yet. If the `hidden` key is set to true, this +terminal will not be toggled by normal toggleterm commands such as +`:ToggleTerm` or the open mapping. It will only open and close by using the +returned terminal object. A mapping for toggling the terminal can be set as in +the example above. + +Alternatively the terminal can be specified with a count, which is the number +that can be used to trigger this specific terminal. This can then be triggered +using the current count e.g. `:5ToggleTerm<CR>` + +>lua + local lazygit = Terminal:new({ cmd = "lazygit", count = 5 }) +< + +You can also set a custom layout for a terminal. + +>lua + local lazygit = Terminal:new({ + cmd = "lazygit", + dir = "git_dir", + direction = "float", + float_opts = { + border = "double", + }, + -- function to run on opening the terminal + on_open = function(term) + vim.cmd("startinsert!") + vim.api.nvim_buf_set_keymap(term.bufnr, "n", "q", "<cmd>close<CR>", {noremap = true, silent = true}) + end, + -- function to run on closing the terminal + on_close = function(term) + vim.cmd("startinsert!") + end, + }) + + function _lazygit_toggle() + lazygit:toggle() + end + + vim.api.nvim_set_keymap("n", "<leader>g", "<cmd>lua _lazygit_toggle()<CR>", {noremap = true, silent = true}) +< + +**WARNING**do not use any of the private functionality of the terminal or other +non-public parts of the API as these can change in the future. + + +STATUSLINE ~ + +To tell each terminal apart you can use the terminal buffer variable +`b:toggle_number` in your statusline + +>vim + " this is pseudo code + let statusline .= '%{&ft == "toggleterm" ? "terminal (".b:toggle_number.")" : ""}' +< + + +CUSTOM COMMANDS ~ + +You can create your own commands by using the lua functions this plugin +provides directly + +>vim + command! -count=1 TermGitPush lua require'toggleterm'.exec("git push", <count>, 12) + command! -count=1 TermGitPushF lua require'toggleterm'.exec("git push -f", <count>, 12) +< + + +OPEN MULTIPLE TERMINALS SIDE-BY-SIDE ~ + + Direction Supported + ------------ ----------- + vertical + horizontal + tab + float +Inyour first terminal, you need to leave the `TERMINAL` mode using C-\C-N which +can be remapped to Esc for ease of use. + +Then you type on: `2<C-\>`, and the result: + +Explain: + +- `2`this is the terminal’s number (or ID), your first terminal is `1` (e.g. your 3rd terminal will be `3<C-\>`, so on). +- C-\this is the combined key mapping to the command `:ToggleTerm`. + + +FAQ ~ + + +HOW DO I GET THIS PLUGIN TO WORK WITH POWERSHELL? + +Please check out the Wiki section on this topic +<https://github.com/akinsho/toggleterm.nvim/wiki/Tips-and-Tricks#using-toggleterm-with-powershell>. + +============================================================================== +1. Links *toggleterm-links* + +1. *lazy git*: https://user-images.githubusercontent.com/22454918/116447435-e69f1480-a84f-11eb-86dd-19fa29646aa1.png +2. *image*: https://user-images.githubusercontent.com/31947091/133395516-22fef1e6-633d-4964-9175-f76fabf66794.png +3. *image*: https://user-images.githubusercontent.com/31947091/133396789-fdf68b30-3a8c-440b-822f-6549f282c4fc.png + +Generated by panvimdoc <https://github.com/kdheepak/panvimdoc> + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm.lua new file mode 100644 index 00000000..09310dce --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm.lua @@ -0,0 +1,475 @@ +local api = vim.api +local fn = vim.fn + +local lazy = require("toggleterm.lazy") +---@module "toggleterm.utils" +local utils = lazy.require("toggleterm.utils") +---@module "toggleterm.constants" +local constants = require("toggleterm.constants") +---@module "toggleterm.config" +local config = lazy.require("toggleterm.config") +---@module "toggleterm.ui" +local ui = lazy.require("toggleterm.ui") +---@module "toggleterm.commandline" +local commandline = lazy.require("toggleterm.commandline") + +local terms = require("toggleterm.terminal") + +local AUGROUP = "ToggleTermCommands" +----------------------------------------------------------- +-- Export +----------------------------------------------------------- +local M = {} + +--- only shade explicitly specified filetypes +local function apply_colors() + local ft = vim.bo.filetype + ft = (not ft or ft == "") and "none" or ft + local allow_list = config.shade_filetypes or {} + local is_enabled_ft = vim.tbl_contains(allow_list, ft) + if vim.bo.buftype == "terminal" and is_enabled_ft then + local _, term = terms.identify() + ui.hl_term(term) + end +end + +local function setup_global_mappings() + local mapping = config.open_mapping + -- v:count defaults the count to 0 but if a count is passed in uses that instead + if mapping then + utils.key_map("n", mapping, '<Cmd>execute v:count . "ToggleTerm"<CR>', { + desc = "Toggle Terminal", + silent = true, + }) + if config.insert_mappings then + utils.key_map("i", mapping, "<Esc><Cmd>ToggleTerm<CR>", { + desc = "Toggle Terminal", + silent = true, + }) + end + end +end + +-- Creates a new terminal if none are present or closes terminals that are +-- currently opened, or opens terminals that were previously closed. +---@param size number? +---@param dir string? +---@param direction string? +---@param name string? +local function smart_toggle(size, dir, direction, name) + local has_open, windows = ui.find_open_windows() + if not has_open then + if not ui.open_terminal_view(size, direction) then + local term_id = terms.get_toggled_id() + terms.get_or_create_term(term_id, dir, direction, name):open(size, direction) + end + else + ui.close_and_save_terminal_view(windows) + end +end + +--- @param num number +--- @param size number? +--- @param dir string? +--- @param direction string? +--- @param name string? +local function toggle_nth_term(num, size, dir, direction, name) + local term = terms.get_or_create_term(num, dir, direction, name) + ui.update_origin_window(term.window) + term:toggle(size, direction) + -- Save the terminal in view if it was last closed terminal. + if not ui.find_open_windows() then ui.save_terminal_view({ term.id }, term.id) end +end + +---Close the last window if only a terminal *split* is open +---@param term Terminal +---@return boolean +local function close_last_window(term) + local only_one_window = fn.winnr("$") == 1 + if only_one_window and vim.bo[term.bufnr].filetype == constants.FILETYPE then + if term:is_split() then + local has_next = pcall(vim.cmd, "keepalt bnext") + return has_next + end + end + return false +end + +local function handle_term_enter() + local _, term = terms.identify() + if term then + --- FIXME: we have to reset the filetype here because it is reset by other plugins + --- i.e. telescope.nvim + if vim.bo[term.bufnr] ~= constants.FILETYPE then term:__set_ft_options() end + + local closed = close_last_window(term) + if closed then return end + if config.persist_mode then + term:__restore_mode() + elseif config.start_in_insert then + term:set_mode(terms.mode.INSERT) + end + end +end + +local function handle_term_leave() + local _, term = terms.identify() + if not term then return end + if config.persist_mode then term:persist_mode() end + if term:is_float() then term:close() end +end + +local function on_term_open() + local id, term = terms.identify() + if not term then + local buf = api.nvim_get_current_buf() + terms.Terminal + :new({ + id = id, + bufnr = buf, + window = api.nvim_get_current_win(), + highlights = config.highlights, + job_id = vim.b[buf].terminal_job_id, + direction = ui.guess_direction(), + }) + :__resurrect() + end + ui.set_winbar(term) +end + +function M.exec_command(args, count) + vim.validate({ args = { args, "string" } }) + if not args:match("cmd") then + return utils.notify( + "TermExec requires a cmd specified using the syntax cmd='ls -l' e.g. TermExec cmd='ls -l'", + "error" + ) + end + local parsed = require("toggleterm.commandline").parse(args) + vim.validate({ + cmd = { parsed.cmd, "string" }, + size = { parsed.size, "number", true }, + dir = { parsed.dir, "string", true }, + direction = { parsed.direction, "string", true }, + name = { parsed.name, "string", true }, + go_back = { parsed.go_back, "boolean", true }, + open = { parsed.open, "boolean", true }, + }) + M.exec( + parsed.cmd, + count, + parsed.size, + parsed.dir, + parsed.direction, + parsed.name, + parsed.go_back, + parsed.open + ) +end + +--- @param cmd string +--- @param num number? +--- @param size number? +--- @param dir string? +--- @param direction string? +--- @param name string? +--- @param go_back boolean? whether or not to return to original window +--- @param open boolean? whether or not to open terminal window +function M.exec(cmd, num, size, dir, direction, name, go_back, open) + vim.validate({ + cmd = { cmd, "string" }, + num = { num, "number", true }, + size = { size, "number", true }, + dir = { dir, "string", true }, + direction = { direction, "string", true }, + name = { name, "string", true }, + go_back = { go_back, "boolean", true }, + open = { open, "boolean", true }, + }) + num = (num and num >= 1) and num or terms.get_toggled_id() + open = open == nil or open + local term = terms.get_or_create_term(num, dir, direction, name) + if not term:is_open() then term:open(size, direction) end + -- going back from floating window closes it + if term:is_float() then go_back = false end + if go_back == nil then go_back = true end + if not open then + term:close() + go_back = false + end + term:send(cmd, go_back) +end + +--- @param selection_type string +--- @param trim_spaces boolean +--- @param cmd_data table<string, any> +function M.send_lines_to_terminal(selection_type, trim_spaces, cmd_data) + local id = tonumber(cmd_data.args) or 1 + trim_spaces = trim_spaces == nil or trim_spaces + + vim.validate({ + selection_type = { selection_type, "string", true }, + trim_spaces = { trim_spaces, "boolean", true }, + terminal_id = { id, "number", true }, + }) + + local current_window = api.nvim_get_current_win() -- save current window + + local lines = {} + -- Beginning of the selection: line number, column number + local start_line, start_col + if selection_type == "single_line" then + start_line, start_col = unpack(api.nvim_win_get_cursor(0)) + -- nvim_win_get_cursor uses 0-based indexing for columns, while we use 1-based indexing + start_col = start_col + 1 + table.insert(lines, fn.getline(start_line)) + else + local res = nil + if string.match(selection_type, "visual") then + -- This calls vim.fn.getpos, which uses 1-based indexing for columns + res = utils.get_line_selection("visual") + else + -- This calls vim.fn.getpos, which uses 1-based indexing for columns + res = utils.get_line_selection("motion") + end + start_line, start_col = unpack(res.start_pos) + -- char, line and block are used for motion/operatorfunc. 'block' is ignored + if selection_type == "visual_lines" or selection_type == "line" then + lines = res.selected_lines + elseif selection_type == "visual_selection" or selection_type == "char" then + lines = utils.get_visual_selection(res, true) + end + end + + if not lines or not next(lines) then return end + + if not trim_spaces then + M.exec(table.concat(lines, "\n"), id) + else + for _, line in ipairs(lines) do + local l = trim_spaces and line:gsub("^%s+", ""):gsub("%s+$", "") or line + M.exec(l, id) + end + end + + -- Jump back with the cursor where we were at the beginning of the selection + api.nvim_set_current_win(current_window) + -- nvim_win_set_cursor() uses 0-based indexing for columns, while we use 1-based indexing + api.nvim_win_set_cursor(current_window, { start_line, start_col - 1 }) +end + +function M.toggle_command(args, count) + local parsed = commandline.parse(args) + vim.validate({ + size = { parsed.size, "number", true }, + dir = { parsed.dir, "string", true }, + direction = { parsed.direction, "string", true }, + name = { parsed.name, "string", true }, + }) + if parsed.size then parsed.size = tonumber(parsed.size) end + M.toggle(count, parsed.size, parsed.dir, parsed.direction, parsed.name) +end + +function _G.___toggleterm_winbar_click(id) + if id then + local term = terms.get_or_create_term(id) + if not term then return end + term:toggle() + end +end + +--- If a count is provided we operate on the specific terminal buffer +--- i.e. 2ToggleTerm => open or close Term 2 +--- if the count is 1 we use a heuristic which is as follows +--- if there is no open terminal window we toggle the first one i.e. assumed +--- to be the primary. However if several are open we close them. +--- this can be used with the count commands to allow specific operations +--- per term or mass actions +--- @param count number? +--- @param size number? +--- @param dir string? +--- @param direction string? +--- @param name string? +function M.toggle(count, size, dir, direction, name) + if count and count >= 1 then + toggle_nth_term(count, size, dir, direction, name) + else + smart_toggle(size, dir, direction, name) + end +end + +-- Toggle all terminals +-- If any terminal is open it will be closed +-- If no terminal exists it will do nothing +-- If any terminal exists but is not open it will be open +function M.toggle_all(force) + local terminals = terms.get_all() + + if force and ui.find_open_windows() then + for _, term in pairs(terminals) do + term:close() + end + else + if not ui.find_open_windows() then + for _, term in pairs(terminals) do + term:open() + end + else + for _, term in pairs(terminals) do + term:close() + end + end + end +end + +---@param _ ToggleTermConfig +local function setup_autocommands(_) + api.nvim_create_augroup(AUGROUP, { clear = true }) + local toggleterm_pattern = { "term://*#toggleterm#*", "term://*::toggleterm::*" } + + api.nvim_create_autocmd("BufEnter", { + pattern = toggleterm_pattern, + group = AUGROUP, + nested = true, -- this is necessary in case the buffer is the last + callback = handle_term_enter, + }) + + api.nvim_create_autocmd("WinLeave", { + pattern = toggleterm_pattern, + group = AUGROUP, + callback = handle_term_leave, + }) + + api.nvim_create_autocmd("TermOpen", { + pattern = toggleterm_pattern, + group = AUGROUP, + callback = on_term_open, + }) + + api.nvim_create_autocmd("ColorScheme", { + group = AUGROUP, + callback = function() + config.reset_highlights() + for _, term in pairs(terms.get_all()) do + if api.nvim_win_is_valid(term.window) then + api.nvim_win_call(term.window, function() ui.hl_term(term) end) + end + end + end, + }) + + api.nvim_create_autocmd("TermOpen", { + group = AUGROUP, + pattern = "term://*", + callback = apply_colors, + }) +end + +--------------------------------------------------------------------------------- +-- Commands +--------------------------------------------------------------------------------- + +---@param callback fun(t: Terminal?) +local function get_subject_terminal(callback) + local items = terms.get_all(true) + if #items == 0 then return utils.notify("No toggleterms are open yet") end + + vim.ui.select(items, { + prompt = "Please select a terminal to name: ", + format_item = function(term) return term.id .. ": " .. term:_display_name() end, + }, function(term) + if not term then return end + callback(term) + end) +end + +---@param name string +---@param term Terminal +local function set_term_name(name, term) term.display_name = name end + +local function request_term_name(term) + vim.ui.input({ prompt = "Please set a name for the terminal" }, function(name) + if name and #name > 0 then set_term_name(name, term) end + end) +end + +local function select_terminal(opts) + local terminals = terms.get_all(opts.bang) + if #terminals == 0 then return utils.notify("No toggleterms are open yet", "info") end + vim.ui.select(terminals, { + prompt = "Please select a terminal to open (or focus): ", + format_item = function(term) return term.id .. ": " .. term:_display_name() end, + }, function(_, idx) + local term = terminals[idx] + if not term then return end + if term:is_open() then + term:focus() + else + term:open() + end + end) +end + +local function setup_commands() + local command = api.nvim_create_user_command + command("TermSelect", select_terminal, { bang = true }) + -- Count is 0 by default + command( + "TermExec", + function(opts) M.exec_command(opts.args, opts.count) end, + { count = true, complete = commandline.term_exec_complete, nargs = "*" } + ) + + command( + "ToggleTerm", + function(opts) M.toggle_command(opts.args, opts.count) end, + { count = true, complete = commandline.toggle_term_complete, nargs = "*" } + ) + + command("ToggleTermToggleAll", function(opts) M.toggle_all(opts.bang) end, { bang = true }) + + command( + "ToggleTermSendVisualLines", + function(args) M.send_lines_to_terminal("visual_lines", true, args) end, + { range = true, nargs = "?" } + ) + + command( + "ToggleTermSendVisualSelection", + function(args) M.send_lines_to_terminal("visual_selection", true, args) end, + { range = true, nargs = "?" } + ) + + command( + "ToggleTermSendCurrentLine", + function(args) M.send_lines_to_terminal("single_line", true, args) end, + { nargs = "?" } + ) + + command("ToggleTermSetName", function(opts) + local no_count = not opts.count or opts.count < 1 + local no_name = opts.args == "" + if no_count and no_name then + get_subject_terminal(request_term_name) + elseif no_name then + local term = terms.get(opts.count) + if not term then return end + request_term_name(term) + elseif no_count then + get_subject_terminal(function(t) set_term_name(opts.args, t) end) + else + local term = terms.get(opts.count) + if not term then return end + set_term_name(opts.args, term) + end + end, { nargs = "?", count = true }) +end + +function M.setup(user_prefs) + local conf = config.set(user_prefs) + setup_global_mappings() + setup_autocommands(conf) + setup_commands() +end + +return M diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/colors.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/colors.lua new file mode 100644 index 00000000..203b09e7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/colors.lua @@ -0,0 +1,117 @@ +local api = vim.api +local bit = require("bit") +----------------------------------------------------------- +-- Export +----------------------------------------------------------- +local M = {} +----------------------------------------------------------- +-- Helpers +----------------------------------------------------------- +---Convert a hex color to an rgb color +---@param color string +---@return number +---@return number +---@return number +local function to_rgb(color) + return tonumber(color:sub(2, 3), 16), tonumber(color:sub(4, 5), 16), tonumber(color:sub(6), 16) +end + +-- SOURCE: https://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color-or-rgb-and-blend-colors +-- @see: https://stackoverflow.com/questions/37796287/convert-decimal-to-hex-in-lua-4 +--- Shade Color generate +--- @param color string hex color +--- @param percent number +--- @return string +function M.shade_color(color, percent) + local r, g, b = to_rgb(color) + -- If any of the colors are missing return "NONE" i.e. no highlight + if not r or not g or not b then return "NONE" end + r = math.floor(tonumber(r * (100 + percent) / 100) or 0) + g = math.floor(tonumber(g * (100 + percent) / 100) or 0) + b = math.floor(tonumber(b * (100 + percent) / 100) or 0) + r, g, b = r < 255 and r or 255, g < 255 and g or 255, b < 255 and b or 255 + + return "#" .. string.format("%02x%02x%02x", r, g, b) +end + +--- Determine whether to use black or white text +--- Ref: +--- 1. https://stackoverflow.com/a/1855903/837964 +--- 2. https://stackoverflow.com/a/596243 +function M.color_is_bright(hex) + if not hex then return false end + local r, g, b = to_rgb(hex) + -- If any of the colors are missing return false + if not r or not g or not b then return false end + -- Counting the perceptive luminance - human eye favors green color + local luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 + -- If luminance is > 0.5 -> Bright colors, black font else Dark colors, white font + return luminance > 0.5 +end + +--- Get hex color +---@param name string highlight group name +---@param attr string attr name 'bg', 'fg' +---@return string +function M.get_hex(name, attr) + local ok, hl = pcall(api.nvim_get_hl_by_name, name, true) + if not ok then return "NONE" end + hl.foreground = hl.foreground and "#" .. bit.tohex(hl.foreground, 6) + hl.background = hl.background and "#" .. bit.tohex(hl.background, 6) + attr = ({ bg = "background", fg = "foreground" })[attr] or attr + return hl[attr] or "NONE" +end + +--- Check if background is bright +--- @return boolean +function M.is_bright_background() + local bg_color = M.get_hex("Normal", "bg") + return M.color_is_bright(bg_color) +end + +----------------------------------------------------------- +-- Darken Terminal +----------------------------------------------------------- + +local function convert_attributes(result, key, value) + local target = result + if key == "cterm" then + result.cterm = {} + target = result.cterm + end + if value:find(",") then + for _, v in vim.split(value, ",") do + target[v] = true + end + else + target[value] = true + end +end + +local function convert_options(opts) + local keys = { + gui = true, + guifg = "foreground", + guibg = "background", + guisp = "sp", + cterm = "cterm", + ctermfg = "ctermfg", + ctermbg = "ctermbg", + link = "link", + } + local result = {} + for key, value in pairs(opts) do + if keys[key] then + if key == "gui" or key == "cterm" then + if value ~= "NONE" then convert_attributes(result, key, value) end + else + result[keys[key]] = value + end + end + end + return result +end + +function M.set_hl(name, opts) api.nvim_set_hl(0, name, convert_options(opts)) end + +return M diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/commandline.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/commandline.lua new file mode 100644 index 00000000..10111b73 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/commandline.lua @@ -0,0 +1,198 @@ +local fn = vim.fn +local u = require("toggleterm.utils") + +local M = {} + +local p = { + single = "'(.-)'", + double = '"(.-)"', +} + +local is_windows = vim.loop.os_uname().version:match("Windows") + +---@class ParsedArgs +---@field direction string? +---@field cmd string? +---@field dir string? +---@field size number? +---@field name string? +---@field go_back boolean? +---@field open boolean? + +---Take a users command arguments in the format "cmd='git commit' dir=~/dotfiles" +---and parse this into a table of arguments +---{cmd = "git commit", dir = "~/dotfiles"} +---@see https://stackoverflow.com/a/27007701 +---@param args string +---@return ParsedArgs +function M.parse(args) + local result = {} + if args then + local quotes = args:match(p.single) and p.single or args:match(p.double) and p.double or nil + if quotes then + -- 1. extract the quoted command + local pattern = "(%S+)=" .. quotes + for key, value in args:gmatch(pattern) do + -- Check if the current OS is Windows so we can determine if +shellslash + -- exists and if it exists, then determine if it is enabled. In that way, + -- we can determine if we should match the value with single or double quotes. + if is_windows then + quotes = not vim.opt.shellslash:get() and quotes or p.single + else + quotes = p.single + end + value = fn.shellescape(value) + result[vim.trim(key)] = fn.expandcmd(value:match(quotes)) + end + -- 2. then remove it from the rest of the argument string + args = args:gsub(pattern, "") + end + + for _, part in ipairs(vim.split(args, " ")) do + if #part > 1 then + local arg = vim.split(part, "=") + local key, value = arg[1], arg[2] + if key == "size" then + value = tonumber(value) + elseif key == "go_back" or key == "open" then + value = value ~= "0" + end + result[key] = value + end + end + end + return result +end + +-- Get a valid base path for a user provided path +-- and an optional search term +---@param typed_path string +---@return string|nil, string|nil +function M.get_path_parts(typed_path) + if vim.fn.isdirectory(typed_path ~= "" and typed_path or ".") == 1 then + -- The string is a valid path, we just need to drop trailing slashes to + -- ease joining the base path with the suggestions + return typed_path:gsub("/$", ""), nil + elseif typed_path:find("/", 2) ~= nil then + -- Maybe the typed path is looking for a nested directory + -- we need to make sure it has at least one slash in it, and that is not + -- from a root path + local base_path = vim.fn.fnamemodify(typed_path, ":h") + local search_term = vim.fn.fnamemodify(typed_path, ":t") + if vim.fn.isdirectory(base_path) then return base_path, search_term end + end + + return nil, nil +end + +local term_exec_options = { + --- Suggests commands + ---@param typed_cmd string|nil + cmd = function(typed_cmd) + local paths = vim.split(vim.env.PATH, ":") + local commands = {} + + for _, path in ipairs(paths) do + local glob_str + if string.match(path, "%s*") then + --path with spaces + glob_str = path:gsub(" ", "\\ ") .. "/" .. (typed_cmd or "") .. "*" + else + -- path without spaces + glob_str = path .. "/" .. (typed_cmd or "") .. "*" + end + local dir_cmds = vim.split(vim.fn.glob(glob_str), "\n") + + for _, cmd in ipairs(dir_cmds) do + if not u.str_is_empty(cmd) then table.insert(commands, vim.fn.fnamemodify(cmd, ":t")) end + end + end + + return commands + end, + --- Suggests paths in the cwd + ---@param typed_path string + dir = function(typed_path) + -- Read the typed path as the base for the directory search + local base_path, search_term = M.get_path_parts(typed_path or "") + local safe_path = base_path ~= "" and base_path or "." + + local paths = vim.fn.readdir( + safe_path, + function(entry) return vim.fn.isdirectory(safe_path .. "/" .. entry) end + ) + + if not u.str_is_empty(search_term) then + paths = vim.tbl_filter( + function(path) return path:match("^" .. search_term .. "*") ~= nil end, + paths + ) + end + + return vim.tbl_map( + function(path) return u.concat_without_empty({ base_path, path }, "/") end, + paths + ) + end, + --- Suggests directions for the term + ---@param typed_direction string + direction = function(typed_direction) + local directions = { + "float", + "horizontal", + "tab", + "vertical", + } + if u.str_is_empty(typed_direction) then return directions end + return vim.tbl_filter( + function(direction) return direction:match("^" .. typed_direction .. "*") ~= nil end, + directions + ) + end, + --- The size param takes in arbitrary numbers, we keep this function only to + --- match the signature of other options + size = function() return {} end, + --- The name param takes in arbitrary strings, we keep this function only to + --- match the signature of other options + name = function() return {} end, +} + +local toggle_term_options = { + dir = term_exec_options.dir, + direction = term_exec_options.direction, + size = term_exec_options.size, + name = term_exec_options.name, +} + +---@param options table a dictionary of key to function +---@return fun(lead: string, command: string, _: number) +local function complete(options) + ---@param lead string the leading portion of the argument currently being completed on + ---@param command string the entire command line + ---@param _ number the cursor position in it (byte index) + return function(lead, command, _) + local parts = vim.split(lead, "=") + local key = parts[1] + local value = parts[2] + if options[key] then + return vim.tbl_map(function(option) return key .. "=" .. option end, options[key](value)) + end + + local available_options = vim.tbl_filter( + function(option) return command:match(" " .. option .. "=") == nil end, + vim.tbl_keys(options) + ) + + table.sort(available_options) + + return vim.tbl_map(function(option) return option .. "=" end, available_options) + end +end + +--- See :h :command-completion-custom +M.term_exec_complete = complete(term_exec_options) + +--- See :h :command-completion-custom +M.toggle_term_complete = complete(toggle_term_options) + +return M diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/config.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/config.lua new file mode 100644 index 00000000..feebe5f5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/config.lua @@ -0,0 +1,146 @@ +local colors = require("toggleterm.colors") +local constants = require("toggleterm.constants") +local utils = require("toggleterm.utils") + +local M = {} + +local fmt = string.format + +local function shade(color, factor) return colors.shade_color(color, factor) end + +--- @alias ToggleTermHighlights table<string, table<string, string>> + +---@class WinbarOpts +---@field name_formatter fun(term: Terminal):string +---@field enabled boolean + +--- @class ToggleTermConfig +--- @field size number +--- @field shade_filetypes string[] +--- @field hide_numbers boolean +--- @field open_mapping string | string[] +--- @field shade_terminals boolean +--- @field insert_mappings boolean +--- @field terminal_mappings boolean +--- @field start_in_insert boolean +--- @field persist_size boolean +--- @field persist_mode boolean +--- @field close_on_exit boolean +--- @field direction '"horizontal"' | '"vertical"' | '"float"' +--- @field shading_factor number +--- @field shell string|fun():string +--- @field auto_scroll boolean +--- @field float_opts table<string, any> +--- @field highlights ToggleTermHighlights +--- @field winbar WinbarOpts +--- @field autochdir boolean +--- @field title_pos '"left"' | '"center"' | '"right"' + +---@type ToggleTermConfig +local config = { + size = 12, + shade_filetypes = {}, + hide_numbers = true, + shade_terminals = true, + insert_mappings = true, + terminal_mappings = true, + start_in_insert = true, + persist_size = true, + persist_mode = true, + close_on_exit = true, + direction = "horizontal", + shading_factor = constants.shading_amount, + shell = vim.o.shell, + autochdir = false, + auto_scroll = true, + winbar = { + enabled = false, + name_formatter = function(term) return fmt("%d:%s", term.id, term:_display_name()) end, + }, + float_opts = { + winblend = 0, + title_pos = "left", + }, +} + +---Derive the highlights for a toggleterm and merge these with the user's preferences +---A few caveats must be noted. Since I link the normal and float border to the Normal +---highlight this has to be done carefully as if the user has specified any Float highlights +---themselves merging will result in a mix of user highlights and the link key which is invalid +---so I check that they have not attempted to highlight these themselves. Also +---if they have chosen to shade the terminal then this takes priority over their own highlights +---since they can't have it both ways i.e. custom highlighting and shading +---@param conf ToggleTermConfig +---@return ToggleTermHighlights +local function get_highlights(conf) + local user = conf.highlights + local defaults = { + NormalFloat = vim.F.if_nil(user.NormalFloat, { link = "Normal" }), + FloatBorder = vim.F.if_nil(user.FloatBorder, { link = "Normal" }), + StatusLine = { gui = "NONE" }, + StatusLineNC = { cterm = "italic", gui = "NONE" }, + } + local overrides = {} + local nightly = utils.is_nightly() + + local comment_fg = colors.get_hex("Comment", "fg") + local dir_fg = colors.get_hex("Directory", "fg") + + local winbar_inactive_opts = { guifg = comment_fg } + local winbar_active_opts = { guifg = dir_fg, gui = "underline" } + + if conf.shade_terminals then + local is_bright = colors.is_bright_background() + local degree = is_bright and -3 or 1 + local amount = conf.shading_factor * degree + local normal_bg = colors.get_hex("Normal", "bg") + local terminal_bg = conf.shade_terminals and shade(normal_bg, amount) or normal_bg + + overrides = { + Normal = { guibg = terminal_bg }, + SignColumn = { guibg = terminal_bg }, + EndOfBuffer = { guibg = terminal_bg }, + StatusLine = { guibg = terminal_bg }, + StatusLineNC = { guibg = terminal_bg }, + } + -- TODO: Move this to the main overrides block once nvim 0.8 is stable + if nightly then + winbar_inactive_opts.guibg = terminal_bg + winbar_active_opts.guibg = terminal_bg + overrides.WinBarNC = { guibg = terminal_bg } + overrides.WinBar = { guibg = terminal_bg } + end + end + + if nightly and conf.winbar.enabled then + colors.set_hl("WinBarActive", winbar_active_opts) + colors.set_hl("WinBarInactive", winbar_inactive_opts) + end + + return vim.tbl_deep_extend("force", defaults, conf.highlights, overrides) +end + +--- get the full user config or just a specified value +---@param key string? +---@return any +function M.get(key) + if key then return config[key] end + return config +end + +function M.reset_highlights() config.highlights = get_highlights(config) end + +---@param user_conf ToggleTermConfig +---@return ToggleTermConfig +function M.set(user_conf) + user_conf = user_conf or {} + user_conf.highlights = user_conf.highlights or {} + config = vim.tbl_deep_extend("force", config, user_conf) + config.highlights = get_highlights(config) + return config +end + +---@return ToggleTermConfig +return setmetatable(M, { + __index = function(_, k) return config[k] end, +}) diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/constants.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/constants.lua new file mode 100644 index 00000000..ffd78801 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/constants.lua @@ -0,0 +1,11 @@ +local M = {} +----------------------------------------------------------- +-- Constants +----------------------------------------------------------- +M.FILETYPE = "toggleterm" +-- -30 is a magic number based on manual testing of what looks good +M.shading_amount = -30 +-- Highlight group name prefix +M.highlight_group_name_prefix = "ToggleTerm" + +return M diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/lazy.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/lazy.lua new file mode 100644 index 00000000..65a57577 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/lazy.lua @@ -0,0 +1,17 @@ +local lazy = {} + +--- Require on index. +--- +--- Will only require the module after the first index of a module. +--- Only works for modules that export a table. +---@param require_path string +---@return table +lazy.require = function(require_path) + return setmetatable({}, { + __index = function(_, key) return require(require_path)[key] end, + + __newindex = function(_, key, value) require(require_path)[key] = value end, + }) +end + +return lazy diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/terminal.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/terminal.lua new file mode 100644 index 00000000..904eb340 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/terminal.lua @@ -0,0 +1,595 @@ +local M = {} + +local lazy = require("toggleterm.lazy") +---@module "toggleterm.ui" +local ui = lazy.require("toggleterm.ui") +---@module "toggleterm.config" +local config = lazy.require("toggleterm.config") +---@module "toggleterm.utils" +local utils = lazy.require("toggleterm.utils") +---@module "toggleterm.constants" +local constants = lazy.require("toggleterm.constants") + +local api = vim.api +local fmt = string.format +local fn = vim.fn + +local mode = { + INSERT = "i", + NORMAL = "n", + UNSUPPORTED = "?", +} + +local AUGROUP = api.nvim_create_augroup("ToggleTermBuffer", { clear = true }) + +local is_windows = fn.has("win32") == 1 +local function is_cmd(shell) return shell:find("cmd") end + +local function is_pwsh(shell) return shell:find("pwsh") or shell:find("powershell") end + +local function is_nushell(shell) return shell:find("nu") end + +local function get_command_sep() return is_windows and is_cmd(vim.o.shell) and "&" or ";" end + +local function get_comment_sep() return is_windows and is_cmd(vim.o.shell) and "::" or "#" end + +local function get_newline_chr() + local shell = config.get("shell") + if type(shell) == "function" then shell = shell() end + if is_windows then + return is_pwsh(shell) and "\r" or "\r\n" + elseif is_nushell(shell) then + return "\r" + else + return "\n" + end +end + +---@alias Mode "n" | "i" | "?" + +--- @class TerminalState +--- @field mode Mode + +---@type Terminal[] +local terminals = {} + +--- @class TermCreateArgs +--- @field newline_chr? string user specified newline chararacter +--- @field cmd? string a custom command to run +--- @field direction? string the layout style for the terminal +--- @field id number? +--- @field highlights table<string, table<string, string>>? +--- @field dir string? the directory for the terminal +--- @field count number? the count that triggers that specific terminal +--- @field display_name string? +--- @field hidden boolean? whether or not to include this terminal in the terminals list +--- @field close_on_exit boolean? whether or not to close the terminal window when the process exits +--- @field auto_scroll boolean? whether or not to scroll down on terminal output +--- @field float_opts table<string, any>? +--- @field on_stdout fun(t: Terminal, job: number, data: string[]?, name: string?)? +--- @field on_stderr fun(t: Terminal, job: number, data: string[], name: string)? +--- @field on_exit fun(t: Terminal, job: number, exit_code: number?, name: string?)? +--- @field on_create fun(term:Terminal)? +--- @field on_open fun(term:Terminal)? +--- @field on_close fun(term:Terminal)? + +--- @class Terminal +--- @field newline_chr string +--- @field cmd string +--- @field direction string the layout style for the terminal +--- @field id number +--- @field bufnr number +--- @field window number +--- @field job_id number +--- @field highlights table<string, table<string, string>> +--- @field dir string the directory for the terminal +--- @field name string the name of the terminal +--- @field count number the count that triggers that specific terminal +--- @field hidden boolean whether or not to include this terminal in the terminals list +--- @field close_on_exit boolean? whether or not to close the terminal window when the process exits +--- @field auto_scroll boolean? whether or not to scroll down on terminal output +--- @field float_opts table<string, any>? +--- @field display_name string? +--- @field env table<string, string> environmental variables passed to jobstart() +--- @field clear_env boolean use clean job environment, passed to jobstart() +--- @field on_stdout fun(t: Terminal, job: number, data: string[]?, name: string?)? +--- @field on_stderr fun(t: Terminal, job: number, data: string[], name: string)? +--- @field on_exit fun(t: Terminal, job: number, exit_code: number?, name: string?)? +--- @field on_create fun(term:Terminal)? +--- @field on_open fun(term:Terminal)? +--- @field on_close fun(term:Terminal)? +--- @field _display_name fun(term: Terminal): string +--- @field __state TerminalState +local Terminal = {} + +--- Get the next available id based on the next number in the sequence that +--- hasn't already been allocated e.g. in a list of {1,2,5,6} the next id should +--- be 3 then 4 then 7 +---@return integer +local function next_id() + local all = M.get_all(true) + for index, term in pairs(all) do + if index ~= term.id then return index end + end + return #all + 1 +end + +---Get an opened (valid) toggle terminal by id, defaults to the first opened +---@param position number? +---@return number? +function M.get_toggled_id(position) + position = position or 1 + local t = M.get_all() + return t[position] and t[position].id or nil +end + +---Return currently focused terminal id. +---@return number? +function M.get_focused_id() + for _, term in pairs(terminals) do + if term:is_focused() then return term.id end + end + return nil +end + +function M.get_last_focused() + local last_focus = ui.get_terminal_view().focus_term_id + return M.get(last_focus, true) +end + +--- @param bufnr number +local function setup_buffer_mappings(bufnr) + local mapping = config.open_mapping + if mapping and config.terminal_mappings then + utils.key_map("t", mapping, "<Cmd>ToggleTerm<CR>", { buffer = bufnr, silent = true }) + end +end + +---@param id number terminal id +local function on_vim_resized(id) + local term = M.get(id, true) + if not term or not term:is_float() or not term:is_open() then return end + ui.update_float(term) +end + +--- Remove the in memory reference to the no longer open terminal +--- @param num number +local function delete(num) + if terminals[num] then terminals[num] = nil end +end + +---Terminal buffer autocommands +---@param term Terminal +local function setup_buffer_autocommands(term) + api.nvim_create_autocmd("TermClose", { + buffer = term.bufnr, + group = AUGROUP, + callback = function() delete(term.id) end, + }) + if term:is_float() then + api.nvim_create_autocmd("VimResized", { + buffer = term.bufnr, + group = AUGROUP, + callback = function() on_vim_resized(term.id) end, + }) + end + + if config.start_in_insert then + -- Avoid entering insert mode when spawning terminal in the background + if term.window == api.nvim_get_current_win() then vim.cmd("startinsert") end + end +end + +---get the directory for the terminal parsing special arguments +---@param dir string? +---@return string +local function _get_dir(dir) + if dir == "git_dir" then dir = utils.git_dir() end + if dir then + return fn.expand(dir) + else + return vim.loop.cwd() + end +end + +---Create a new terminal object +---@param term TermCreateArgs? +---@return Terminal +function Terminal:new(term) + term = term or {} + --- If we try to create a new terminal, but the id is already + --- taken, return the terminal with the containing id + local id = term.count or term.id + if id and terminals[id] then return terminals[id] end + local conf = config.get() + self.__index = self + term.newline_chr = term.newline_chr or get_newline_chr() + term.direction = term.direction or conf.direction + term.id = id or next_id() + term.display_name = term.display_name + term.float_opts = vim.tbl_deep_extend("keep", term.float_opts or {}, conf.float_opts) + term.clear_env = term.clear_env + term.auto_scroll = vim.F.if_nil(term.auto_scroll, conf.auto_scroll) + term.env = vim.F.if_nil(term.env, conf.env) + term.hidden = vim.F.if_nil(term.hidden, false) + term.on_create = vim.F.if_nil(term.on_create, conf.on_create) + term.on_open = vim.F.if_nil(term.on_open, conf.on_open) + term.on_close = vim.F.if_nil(term.on_close, conf.on_close) + term.on_stdout = vim.F.if_nil(term.on_stdout, conf.on_stdout) + term.on_stderr = vim.F.if_nil(term.on_stderr, conf.on_stderr) + term.on_exit = vim.F.if_nil(term.on_exit, conf.on_exit) + term.__state = { mode = "?" } + if term.close_on_exit == nil then term.close_on_exit = conf.close_on_exit end + -- Add the newly created terminal to the list of all terminals + ---@diagnostic disable-next-line: return-type-mismatch + return setmetatable(term, self) +end + +---@package +---Add a terminal to the list of terminals +function Terminal:__add() + if terminals[self.id] and terminals[self.id] ~= self then self.id = next_id() end + if not terminals[self.id] then terminals[self.id] = self end + return self +end + +function Terminal:is_float() return self.direction == "float" and ui.is_float(self.window) end + +function Terminal:is_split() + return (self.direction == "vertical" or self.direction == "horizontal") + and not ui.is_float(self.window) +end + +function Terminal:is_tab() return self.direction == "tab" and not ui.is_float(self.window) end + +function Terminal:resize(size) + if self:is_split() then ui.resize_split(self, size) end +end + +function Terminal:is_open() + if not self.window then return false end + local win_type = fn.win_gettype(self.window) + -- empty string window type corresponds to a normal window + local win_open = win_type == "" or win_type == "popup" + return win_open and api.nvim_win_get_buf(self.window) == self.bufnr +end + +---@package +function Terminal:__restore_mode() self:set_mode(self.__state.mode) end + +--- Set the terminal's mode +---@param m Mode +function Terminal:set_mode(m) + if m == mode.INSERT then + vim.cmd("startinsert") + elseif m == mode.NORMAL then + vim.cmd("stopinsert") + elseif m == mode.UNSUPPORTED and config.get("start_in_insert") then + vim.cmd("startinsert") + end +end + +function Terminal:persist_mode() + local raw_mode = api.nvim_get_mode().mode + local m = "?" + if raw_mode:match("nt") then -- nt is normal mode in the terminal + m = mode.NORMAL + elseif raw_mode:match("t") then -- t is insert mode in the terminal + m = mode.INSERT + end + self.__state.mode = m +end + +---@package +function Terminal:_display_name() return self.display_name or vim.split(self.name, ";")[1] end + +function Terminal:close() + if self.on_close then self:on_close() end + ui.close(self) + ui.stopinsert() + ui.update_origin_window(self.window) +end + +function Terminal:shutdown() + if self:is_open() then self:close() end + ui.delete_buf(self) + delete(self.id) +end + +---Combine arguments into strings separated by new lines +---@vararg string +---@param newline_chr string +---@return string +local function with_cr(newline_chr, ...) + local result = {} + for _, str in ipairs({ ... }) do + table.insert(result, str .. newline_chr) + end + return table.concat(result, "") +end + +function Terminal:scroll_bottom() + if not api.nvim_buf_is_loaded(self.bufnr) or not api.nvim_buf_is_valid(self.bufnr) then return end + if ui.term_has_open_win(self) then api.nvim_buf_call(self.bufnr, ui.scroll_to_bottom) end +end + +function Terminal:is_focused() return self.window == api.nvim_get_current_win() end + +function Terminal:focus() + if ui.term_has_open_win(self) then api.nvim_set_current_win(self.window) end +end + +---Send a command to a running terminal +---@param cmd string|string[] +---@param go_back boolean? whether or not to return to original window +function Terminal:send(cmd, go_back) + cmd = type(cmd) == "table" and with_cr(self.newline_chr, unpack(cmd)) + or with_cr(self.newline_chr, cmd --[[@as string]]) + fn.chansend(self.job_id, cmd) + self:scroll_bottom() + if go_back and self:is_focused() then + ui.goto_previous() + ui.stopinsert() + elseif not go_back and not self:is_focused() then + self:focus() + end +end + +--check for os type and perform os specific clear command +function Terminal:clear() + local clear = is_windows and "cls" or "clear" + self:send(clear) +end + +---Update the directory of an already opened terminal +---@param dir string +function Terminal:change_dir(dir, go_back) + dir = _get_dir(dir) + if self.dir == dir then return end + self:send({ fmt("cd %s", dir), self:clear() }, go_back) + self.dir = dir +end + +---Update the direction of an already opened terminal +---@param direction string +function Terminal:change_direction(direction) + self.direction = direction + self.window = nil +end + +--- Handle when a terminal process exits +---@param term Terminal +local function __handle_exit(term) + return function(...) + if term.on_exit then term:on_exit(...) end + if term.close_on_exit then + term:close() + if api.nvim_buf_is_loaded(term.bufnr) then + api.nvim_buf_delete(term.bufnr, { force = true }) + end + end + end +end + +---@private +---Prepare callback for terminal output handling +---If `auto_scroll` is active, will create a handler that scrolls on terminal output +---If `handler` is present, will call it passing `self` as the first parameter +---If none of the above is applicable, will not return a handler +---@param handler function? a custom callback function for output handling +function Terminal:__make_output_handler(handler) + if self.auto_scroll or handler then + return function(...) + if self.auto_scroll then self:scroll_bottom() end + if handler then handler(self, ...) end + end + end +end + +---@private +function Terminal:__spawn() + local cmd = self.cmd or config.get("shell") + if type(cmd) == "function" then cmd = cmd() end + local command_sep = get_command_sep() + local comment_sep = get_comment_sep() + cmd = table.concat({ + cmd, + command_sep, + comment_sep, + constants.FILETYPE, + comment_sep, + self.id, + }) + local dir = _get_dir(self.dir) + self.job_id = fn.termopen(cmd, { + detach = 1, + cwd = dir, + on_exit = __handle_exit(self), + on_stdout = self:__make_output_handler(self.on_stdout), + on_stderr = self:__make_output_handler(self.on_stderr), + env = self.env, + clear_env = self.clear_env, + }) + self.name = cmd + self.dir = dir +end + +---@package +---Add an orphaned terminal to the list of terminal and re-apply settings +function Terminal:__resurrect() + self:__add() + if self:is_split() then ui.resize_split(self) end + -- set the window options including fixing height or width once the window is resized + self:__set_options() + ui.hl_term(self) +end + +---@package +function Terminal:__set_ft_options() + local buf = vim.bo[self.bufnr] + buf.filetype = constants.FILETYPE + buf.buflisted = false +end + +---@package +function Terminal:__set_win_options() + if self:is_split() then + local field = self.direction == "vertical" and "winfixwidth" or "winfixheight" + utils.wo_setlocal(self.window, field, true) + end + + if config.hide_numbers then + utils.wo_setlocal(self.window, "number", false) + utils.wo_setlocal(self.window, "relativenumber", false) + end +end + +---@package +function Terminal:__set_options() + self:__set_ft_options() + self:__set_win_options() + vim.b[self.bufnr].toggle_number = self.id +end + +---Open a terminal in a type of window i.e. a split,full window or tab +---@param size number +---@param term table +local function opener(size, term) + local direction = term.direction + if term:is_split() then + ui.open_split(size, term) + elseif direction == "tab" then + ui.open_tab(term) + elseif direction == "float" then + ui.open_float(term) + else + error("Invalid terminal direction") + end +end + +---Spawn terminal background job in a buffer without a window +function Terminal:spawn() + if not self.bufnr or not api.nvim_buf_is_valid(self.bufnr) then self.bufnr = ui.create_buf() end + self:__add() + if api.nvim_get_current_buf() ~= self.bufnr then + api.nvim_buf_call(self.bufnr, function() self:__spawn() end) + else + self:__spawn() + end + setup_buffer_autocommands(self) + setup_buffer_mappings(self.bufnr) + if self.on_create then self:on_create() end +end + +---Open a terminal window +---@param size number? +---@param direction string? +function Terminal:open(size, direction) + local cwd = fn.getcwd() + self.dir = _get_dir(config.autochdir and cwd or self.dir) + ui.set_origin_window() + if direction then self:change_direction(direction) end + if not self.bufnr or not api.nvim_buf_is_valid(self.bufnr) then + local ok, err = pcall(opener, size, self) + if not ok and err then return utils.notify(err, "error") end + self:spawn() + else + local ok, err = pcall(opener, size, self) + if not ok and err then return utils.notify(err, "error") end + ui.switch_buf(self.bufnr) + if config.autochdir and self.dir ~= cwd then self:change_dir(cwd) end + end + ui.hl_term(self) + -- NOTE: it is important that this function is called at this point. i.e. the buffer has been correctly assigned + if self.on_open then self:on_open() end +end + +---Open if closed and close if opened +---@param size number? +---@param direction string? +function Terminal:toggle(size, direction) + if self:is_open() then + self:close() + else + self:open(size, direction) + end + return self +end + +--- get the toggle term number from +--- the name e.g. term://~/.dotfiles//3371887:/usr/bin/zsh;#toggleterm#1 +--- the number in this case is 1 +--- @param name string? +--- @return number? +--- @return Terminal? +function M.identify(name) + name = name or api.nvim_buf_get_name(api.nvim_get_current_buf()) + local comment_sep = get_comment_sep() + local parts = vim.split(name, comment_sep) + local id = tonumber(parts[#parts]) + return id, terminals[id] +end + +---get existing terminal or create an empty term table +---@param num number? +---@param dir string? +---@param direction string? +---@param name string? +---@return Terminal +---@return boolean +function M.get_or_create_term(num, dir, direction, name) + local term = M.get(num) + if term then return term, false end + if dir and fn.isdirectory(fn.expand(dir)) == 0 then dir = nil end + return Terminal:new({ id = num, dir = dir, direction = direction, display_name = name }), true +end + +---Get a single terminal by id, unless it is hidden +---@param id number? +---@param include_hidden boolean? whether or nor to filter out hidden +---@return Terminal? +function M.get(id, include_hidden) + local term = terminals[id] + return (term and (include_hidden == true or not term.hidden)) and term or nil +end + +---Get the first terminal that matches a predicate +---@param predicate fun(term: Terminal): boolean +---@return Terminal? +function M.find(predicate) + if type(predicate) ~= "function" then + utils.notify("terminal.find expects a function, got " .. type(predicate), "error") + return + end + for _, term in pairs(terminals) do + if predicate(term) then return term end + end + return nil +end + +---Return the potentially non contiguous map of terminals as a sorted array +---@param include_hidden boolean? whether or nor to filter out hidden +---@return Terminal[] +function M.get_all(include_hidden) + local result = {} + for _, v in pairs(terminals) do + if include_hidden or (not include_hidden and not v.hidden) then table.insert(result, v) end + end + table.sort(result, function(a, b) return a.id < b.id end) + return result +end + +if _G.IS_TEST then + function M.__reset() + for _, term in pairs(terminals) do + term:shutdown() + end + end + + M.__next_id = next_id +end + +M.Terminal = Terminal +M.mode = mode + +return M diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/ui.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/ui.lua new file mode 100644 index 00000000..be182bf0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/ui.lua @@ -0,0 +1,472 @@ +local M = {} + +local lazy = require("toggleterm.lazy") +---@module "toggleterm.constants" +local constants = lazy.require("toggleterm.constants") +---@module "toggleterm.utils" +local utils = lazy.require("toggleterm.utils") +---@module "toggleterm.colors" +local colors = lazy.require("toggleterm.colors") +---@module "toggleterm.config" +local config = lazy.require("toggleterm.config") +---@module "toggleterm.terminal" +local terms = lazy.require("toggleterm.terminal") + +local fn = vim.fn +local fmt = string.format +local api = vim.api + +local origin_window +local persistent = {} + +---@alias TerminalView {terminals: number[], focus_term_id: number} + +---@type TerminalView +local terminal_view = { + ---@type number[] + -- A list of terminal IDs that are saved from the view on smart toggle. + terminals = {}, + ---@type number + ---Last focused terminal ID in the view. + focus_term_id = nil, +} + +--- @class TerminalWindow +--- @field term_id number ID for the terminal in the window +--- @field window number window handle +-- +--- Save the size of a split window before it is hidden +--- @param direction string +--- @param window number +function M.save_window_size(direction, window) + if direction == "horizontal" then + persistent.horizontal = api.nvim_win_get_height(window) + elseif direction == "vertical" then + persistent.vertical = api.nvim_win_get_width(window) + end +end + +--- Explicitly set the persistent size of a direction +--- @param direction string +--- @param size number +function M.save_direction_size(direction, size) persistent[direction] = size end + +--- @param direction string +--- @return boolean +function M.has_saved_size(direction) return persistent[direction] ~= nil end + +--- Get the size of the split. Order of priority is as follows: +--- 1. The size argument is a valid number > 0 +--- 2. There is persistent width/height information from prev open state +--- 3. Default/base case config size +--- +--- If `config.persist_size = false` then option `2` in the +--- list is skipped. +--- @param size number? +--- @param direction string? +function M.get_size(size, direction) + local valid_size = size ~= nil and size > 0 + if not config.persist_size then return valid_size and size or config.size end + return valid_size and size or persistent[direction] or config.size +end + +local function hl(name) return "%#" .. name .. "#" end + +local hl_end = "%*" + +--- Create terminal window bar +---@param id number +---@return string +function M.winbar(id) + local terms = require("toggleterm.terminal").get_all() + local str = " " + for _, t in pairs(terms) do + local h = id == t.id and "WinBarActive" or "WinBarInactive" + str = str + .. fmt("%%%d@v:lua.___toggleterm_winbar_click@", t.id) + .. hl(h) + .. config.winbar.name_formatter(t) + .. hl_end + .. " " + end + return str +end + +---@param term Terminal? +function M.set_winbar(term) + if + not config.winbar.enabled + or not term + or term:is_float() -- TODO: make this configurable + or fn.exists("+winbar") ~= 1 + or not term.window + or not api.nvim_win_is_valid(term.window) + then + return + end + local value = fmt('%%{%%v:lua.require("toggleterm.ui").winbar(%d)%%}', term.id) + utils.wo_setlocal(term.window, "winbar", value) +end + +---apply highlights to a terminal +---if no term is passed in we use default values instead +---@param term Terminal? +function M.hl_term(term) + local hls = (term and term.highlights and not vim.tbl_isempty(term.highlights)) + and term.highlights + or config.highlights + + if not hls or vim.tbl_isempty(hls) then return end + + local window = term and term.window or api.nvim_get_current_win() + local id = term and term.id or "Default" + local is_float = M.is_float(window) + + -- If the terminal is a floating window we only want to set the background and border + -- not the statusline etc. which are not applicable to floating windows + local hl_names = vim.tbl_filter( + function(name) + return not is_float or (is_float and vim.tbl_contains({ "FloatBorder", "NormalFloat" }, name)) + end, + vim.tbl_keys(hls) + ) + + local highlights = vim.tbl_map(function(hl_group_name) + local name = constants.highlight_group_name_prefix .. id .. hl_group_name + local hi_target = fmt("%s:%s", hl_group_name, name) + local attrs = hls[hl_group_name] + attrs.default = true + colors.set_hl(name, attrs) + return hi_target + end, hl_names) + + utils.wo_setlocal(window, "winhighlight", table.concat(highlights, ",")) +end + +---Create a terminal buffer with the correct buffer/window options +---then set it to current window +---@param term Terminal +local function create_term_buf_if_needed(term) + local valid_win = term.window and api.nvim_win_is_valid(term.window) + local window = valid_win and term.window or api.nvim_get_current_win() + -- If the buffer doesn't exist create a new one + local valid_buf = term.bufnr and api.nvim_buf_is_valid(term.bufnr) + local bufnr = valid_buf and term.bufnr or api.nvim_create_buf(false, false) + -- Assign buf to window to ensure window options are set correctly + api.nvim_win_set_buf(window, bufnr) + term.window, term.bufnr = window, bufnr + term:__set_options() + api.nvim_set_current_buf(bufnr) +end + +function M.create_buf() return api.nvim_create_buf(false, false) end + +function M.delete_buf(term) + if term.bufnr and api.nvim_buf_is_valid(term.bufnr) then + api.nvim_buf_delete(term.bufnr, { force = true }) + end +end + +function M.set_origin_window() origin_window = api.nvim_get_current_win() end + +function M.get_origin_window() return origin_window end + +function M.update_origin_window(term_window) + local curr_win = api.nvim_get_current_win() + if term_window ~= curr_win then origin_window = curr_win end +end + +function M.scroll_to_bottom() + local info = vim.api.nvim_get_mode() + if info and (info.mode == "n" or info.mode == "nt") then vim.cmd("normal! G") end +end + +function M.goto_previous() vim.cmd("wincmd p") end + +function M.stopinsert() vim.cmd("stopinsert!") end + +---@param buf integer +---@return boolean +local function default_compare(buf) + return vim.bo[buf].filetype == constants.FILETYPE or vim.b[buf].toggle_number ~= nil +end + +--- Find the first open terminal window +--- by iterating all windows and matching the +--- containing buffers filetype with the passed in +--- comparator function or the default which matches +--- the filetype +--- @param comparator function? +--- @return boolean, TerminalWindow[] +function M.find_open_windows(comparator) + comparator = comparator or default_compare + local term_wins, is_open = {}, false + for _, tab in ipairs(api.nvim_list_tabpages()) do + for _, win in pairs(api.nvim_tabpage_list_wins(tab)) do + local buf = api.nvim_win_get_buf(win) + if comparator(buf) then + is_open = true + table.insert(term_wins, { window = win, term_id = vim.b[buf].toggle_number }) + end + end + end + return is_open, term_wins +end + +---Switch to the given buffer without changing the alternate +---@param buf number +function M.switch_buf(buf) + -- don't change the alternate buffer so that <c-^><c-^> does nothing in the terminal split + local cur_buf = api.nvim_get_current_buf() + if cur_buf ~= buf then vim.cmd(fmt("keepalt buffer %d", buf)) end +end + +local split_commands = { + horizontal = { + existing = "rightbelow vsplit", + new = "botright split", + resize = "resize", + }, + vertical = { + existing = "rightbelow split", + new = "botright vsplit", + resize = "vertical resize", + }, +} + +---Guess whether or not the window is a horizontal or vertical split +---this only works if either of the two are full size +---@return string? +function M.guess_direction() + -- current window is full height vertical split + -- NOTE: add one for tabline and one for status + local ui_lines = (vim.o.tabline ~= "" and 1 or 0) + (vim.o.laststatus ~= 0 and 1 or 0) + if api.nvim_win_get_height(0) + vim.o.cmdheight + ui_lines == vim.o.lines then + return "vertical" + end + -- current window is full width horizontal split + if api.nvim_win_get_width(0) == vim.o.columns then return "horizontal" end + return nil +end + +--- @private +--- @param size number|function +--- @param term Terminal? +--- @return number? +function M._resolve_size(size, term) + if not size then + return + elseif type(size) == "number" then + return size + elseif term and type(size) == "function" then + return size(term) + end + utils.notify(fmt('The input %s is not of type "number" or "function".', size), "error") +end + +local curved = { "╭", "─", "╮", "│", "╯", "─", "╰", "│" } + +--- @private +--- @param term Terminal +--- @param opening boolean +function M._get_float_config(term, opening) + local opts = term.float_opts or {} + local border = opts.border == "curved" and curved or opts.border or "single" + + local width = math.ceil(math.min(vim.o.columns, math.max(80, vim.o.columns - 20))) + local height = math.ceil(math.min(vim.o.lines, math.max(20, vim.o.lines - 10))) + + width = vim.F.if_nil(M._resolve_size(opts.width, term), width) + height = vim.F.if_nil(M._resolve_size(opts.height, term), height) + + local row = math.ceil(vim.o.lines - height) * 0.5 - 1 + local col = math.ceil(vim.o.columns - width) * 0.5 - 1 + + row = vim.F.if_nil(M._resolve_size(opts.row, term), row) + col = vim.F.if_nil(M._resolve_size(opts.col, term), col) + + local version = vim.version() + + local float_config = { + row = row, + col = col, + relative = opts.relative or "editor", + style = opening and "minimal" or nil, + width = width, + height = height, + border = opening and border or nil, + zindex = opts.zindex or nil, + } + if version.major > 0 or version.minor >= 9 then + float_config.title_pos = term.display_name and opts.title_pos or nil + float_config.title = term.display_name + end + return float_config +end + +--- @param size number +--- @param term Terminal +function M.open_split(size, term) + local has_open, windows = M.find_open_windows() + local commands = split_commands[term.direction] + + if has_open then + -- we need to be in the terminal window most recently opened + -- in order to split it + local split_win = windows[#windows] + if config.persist_size then M.save_window_size(term.direction, split_win.window) end + api.nvim_set_current_win(split_win.window) + vim.cmd(commands.existing) + else + vim.cmd(commands.new) + end + + M.resize_split(term, size) + create_term_buf_if_needed(term) +end + +--- @param term Terminal +function M.open_tab(term) + -- Open the current buffer in a tab (use tabnew due to issue #95) + vim.cmd("tabedit new") + -- tabnew creates an empty no name buffer so we set it to be wiped once it's replaced + -- by the terminal buffer + vim.bo.bufhidden = "wipe" + -- Replace the current window with a tab + create_term_buf_if_needed(term) +end + +---@param term Terminal +local function close_tab(term) + if #vim.api.nvim_list_tabpages() == 1 then + return utils.notify("You cannot close the last tab! This will exit neovim", "error") + end + api.nvim_win_close(term.window, true) +end + +---Close terminal window +---@param term Terminal +local function close_split(term) + if term.window and api.nvim_win_is_valid(term.window) then + local persist_size = require("toggleterm.config").get("persist_size") + if persist_size then M.save_window_size(term.direction, term.window) end + api.nvim_win_close(term.window, true) + end + if origin_window and api.nvim_win_is_valid(origin_window) then + api.nvim_set_current_win(origin_window) + else + origin_window = nil + end +end + +---Open a floating window +---@param term Terminal +function M.open_float(term) + local opts = term.float_opts or {} + local valid_buf = term.bufnr and api.nvim_buf_is_valid(term.bufnr) + local buf = valid_buf and term.bufnr or api.nvim_create_buf(false, false) + local win = api.nvim_open_win(buf, true, M._get_float_config(term, true)) + + term.window, term.bufnr = win, buf + -- partial fix for #391 + utils.wo_setlocal(win, "sidescrolloff", 0) + + if opts.winblend then utils.wo_setlocal(win, "winblend", opts.winblend) end + term:__set_options() +end + +---Updates the floating terminal size +---@param term Terminal +function M.update_float(term) + if not vim.api.nvim_win_is_valid(term.window) then return end + vim.api.nvim_win_set_config(term.window, M._get_float_config(term, false)) +end + +---Close given terminal's ui +---@param term Terminal +function M.close(term) + if term:is_split() then + close_split(term) + elseif term:is_tab() then + close_tab(term) + elseif term.window and api.nvim_win_is_valid(term.window) then + api.nvim_win_close(term.window, true) + end +end + +---Resize a split window +---@param term Terminal +---@param size number? +function M.resize_split(term, size) + size = M._resolve_size(M.get_size(size, term.direction), term) + if config.persist_size and size then M.save_direction_size(term.direction, size) end + vim.cmd(split_commands[term.direction].resize .. " " .. size) +end + +---Determine if a window is a float +---@param window number +function M.is_float(window) return fn.win_gettype(window) == "popup" end + +--- @param bufnr number +function M.find_windows_by_bufnr(bufnr) return fn.win_findbuf(bufnr) end + +---Return whether or not the terminal passed in has an open window +---@param term Terminal +---@return boolean +function M.term_has_open_win(term) + if not term.window then return false end + local wins = {} + for _, tab in ipairs(api.nvim_list_tabpages()) do + vim.list_extend(wins, api.nvim_tabpage_list_wins(tab)) + end + return vim.tbl_contains(wins, term.window) +end + +---Close and save terminals that are currently in view. +---@param windows TerminalWindow[] +function M.close_and_save_terminal_view(windows) + local terminals = {} + local focused_term_id = terms.get_focused_id() + -- NOTE: Use windows to close terminals in order they are being shown on + -- the view. + for _, window in pairs(windows) do + local term = terms.get(window.term_id) + if term then + table.insert(terminals, term.id) + term:close() + end + end + M.save_terminal_view(terminals, focused_term_id) +end + +---Open terminals that were saved in the last terminal view. +---@return boolean +function M.open_terminal_view(size, direction) + local opened = false + if not vim.tbl_isempty(terminal_view.terminals) then + for _, term_id in pairs(terminal_view.terminals) do + local term = terms.get(term_id) + if term then + term:open(size, direction) + opened = true + end + end + local focus_term = terms.get(terminal_view.focus_term_id) + if focus_term then focus_term:focus() end + M.save_terminal_view({}, nil) + end + return opened +end + +---Save the terminal view with the just closed terminals and the previously +--focused terminal. +---@param terminals number[] +---@param focus_term_id number? +function M.save_terminal_view(terminals, focus_term_id) + terminal_view = { terminals = terminals, focus_term_id = focus_term_id } +end + +---@return TerminalView +function M.get_terminal_view() return terminal_view end + +return M diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/utils.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/utils.lua new file mode 100644 index 00000000..46bb6379 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/lua/toggleterm/utils.lua @@ -0,0 +1,135 @@ +local M = {} + +local fn, api, opt = vim.fn, vim.api, vim.opt +local fmt = string.format +local levels = vim.log.levels + +function M.is_nightly() + local v = vim.version() + return v.minor >= 8 +end + +---@alias error_types 'error' | 'info' | 'warn' +---Inform a user about something +---@param msg string +---@param level error_types +function M.notify(msg, level) + local err = level:upper() + level = level and levels[err] or levels.INFO + vim.schedule(function() vim.notify(msg, level, { title = "Toggleterm" }) end) +end + +---@private +---Helper function to derive the current git directory path +---@return string|nil +function M.git_dir() + local gitdir = fn.system(fmt("git -C %s rev-parse --show-toplevel", fn.expand("%:p:h"))) + local isgitdir = fn.matchstr(gitdir, "^fatal:.*") == "" + if not isgitdir then return end + return vim.trim(gitdir) +end + +---@param str string|nil +---@return boolean +function M.str_is_empty(str) return str == nil or str == "" end + +---@param tbl table +---@return table +function M.tbl_filter_empty(tbl) + return vim.tbl_filter( + ---@param str string|nil + function(str) return not M.str_is_empty(str) end, + tbl + ) +end + +--- Concats a table ignoring empty entries +---@param tbl table +---@param sep string +function M.concat_without_empty(tbl, sep) return table.concat(M.tbl_filter_empty(tbl), sep) end + +-- Key mapping function +---@param mod string | string[] +---@param lhs string | string[] +---@param rhs string | function +---@param opts table? +function M.key_map(mod, lhs, rhs, opts) + if type(lhs) == "string" then + vim.keymap.set(mod, lhs, rhs, opts) + elseif type(lhs) == "table" then + for _, key in pairs(lhs) do + vim.keymap.set(mod, key, rhs, opts) + end + end +end + +---@param mode "visual" | "motion" +---@return table +function M.get_line_selection(mode) + local start_char, end_char = unpack(({ + visual = { "'<", "'>" }, + motion = { "'[", "']" }, + })[mode]) + -- '< marks are only updated when one leaves visual mode. + -- When calling lua functions directly from a mapping, need to + -- explicitly exit visual with the escape key to ensure those marks are + -- accurate. + vim.cmd("normal! ") + + -- Get the start and the end of the selection + local start_line, start_col = unpack(fn.getpos(start_char), 2, 3) + local end_line, end_col = unpack(fn.getpos(end_char), 2, 3) + local selected_lines = api.nvim_buf_get_lines(0, start_line - 1, end_line, false) + return { + start_pos = { start_line, start_col }, + end_pos = { end_line, end_col }, + selected_lines = selected_lines, + } +end + +function M.get_visual_selection(res, motion) + motion = motion or false + local mode = fn.visualmode() + if motion then mode = "v" end + + -- line-visual + -- return lines encompassed by the selection; already in res object + if mode == "V" then return res.selected_lines end + + if mode == "v" then + -- regular-visual + -- return the buffer text encompassed by the selection + local start_line, start_col = unpack(res.start_pos) + local end_line, end_col = unpack(res.end_pos) + -- exclude the last char in text if "selection" is set to "exclusive" + if opt.selection:get() == "exclusive" then end_col = end_col - 1 end + return api.nvim_buf_get_text(0, start_line - 1, start_col - 1, end_line - 1, end_col, {}) + end + + -- block-visual + -- return the lines encompassed by the selection, each truncated by the start and end columns + if mode == "\x16" then + local _, start_col = unpack(res.start_pos) + local _, end_col = unpack(res.end_pos) + -- exclude the last col of the block if "selection" is set to "exclusive" + if opt.selection:get() == "exclusive" then end_col = end_col - 1 end + -- exchange start and end columns for proper substring indexing if needed + -- e.g. instead of str:sub(10, 5), do str:sub(5, 10) + if start_col > end_col then + start_col, end_col = end_col, start_col + end + -- iterate over lines, truncating each one + return vim.tbl_map(function(line) return line:sub(start_col, end_col) end, res.selected_lines) + end +end + +--- Sets a local window option, like `:setlocal` +--- TODO: replace with double-indexing on `vim.wo` when neovim/neovim#20288 (hopefully) merges +---@param win number +---@param option string +---@param value any +function M.wo_setlocal(win, option, value) + api.nvim_set_option_value(option, value, { scope = "local", win = win }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/stylua.toml b/config/neovim/store/lazy-plugins/toggleterm.nvim/stylua.toml new file mode 100644 index 00000000..1904265d --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/stylua.toml @@ -0,0 +1,5 @@ +column_width = 100 +indent_type = "Spaces" +quote_style = "AutoPreferDouble" +indent_width = 2 +collapse_simple_statement = 'Always' diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/command-complete_spec.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/command-complete_spec.lua new file mode 100644 index 00000000..e4e08013 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/command-complete_spec.lua @@ -0,0 +1,86 @@ +describe("command-complete", function() + local command_complete = require("toggleterm.commandline") + it("should return the default options", function() + local results = command_complete.term_exec_complete("", "TermExec ", 9) + + assert.is_equal("cmd=, dir=, direction=, name=, size=", table.concat(results, ", ")) + end) + + describe("helpers", function() + it("should validate relative paths", function() + local cwd, no_search_term = command_complete.get_path_parts("") + assert.is_equal(nil, no_search_term) + assert.is_equal("", cwd) + + local partial_path, search_term = command_complete.get_path_parts(".github/work") + assert.is_equal(".github", partial_path) + assert.is_equal("work", search_term) + + local path_with_slash, _ = command_complete.get_path_parts(".github/") + assert.is_equal(".github", path_with_slash) + + local path_without_slash, _ = command_complete.get_path_parts(".github") + assert.is_equal(".github", path_without_slash) + end) + end) + + describe("cmd=", function() + it("should return all the commands in $PATH", function() + local results = command_complete.term_exec_complete("cmd=", "ToggleExec cmd=", 16) + + assert.is_not_equal(0, #results) + end) + + it("should return matching commands in $PATH", function() + local results = command_complete.term_exec_complete("cmd=m", "ToggleExec cmd=m", 16) + + assert.is_not_equal(0, #results) + assert.is_true(vim.tbl_contains(results, "cmd=mv")) + assert.is_true(vim.tbl_contains(results, "cmd=mkdir")) + end) + end) + + describe("dir=", function() + it("should return all directories in the cwd", function() + local results = command_complete.term_exec_complete("dir=", "ToggleExec dir=", 16) + + assert.is_not_equal(0, #results) + end) + + it("should return matching subdirectories", function() + local results = + command_complete.term_exec_complete("dir=.github/wor", "ToggleExec dir=.github/wor", 27) + + assert.is_equal("dir=.github/workflows", table.concat(results, ", ")) + end) + + it("should handle empty dir values", function() + local results = command_complete.term_exec_complete("dir", "ToggleExec dir", 15) + + assert.is_not_equal(0, #results) + end) + end) + + describe("directions=", function() + it("should return all directions", function() + local results = command_complete.term_exec_complete("direction=", "TermExec direction=", 19) + + assert.equal( + table.concat({ + "direction=float", + "direction=horizontal", + "direction=tab", + "direction=vertical", + }, ", "), + table.concat(results, ", ") + ) + end) + + it("should return partiall typed directions", function() + local results = + command_complete.term_exec_complete("direction=ver", "TermExec direction=ver", 22) + + assert.equal("direction=vertical", table.concat(results, ", ")) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/commandline_spec.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/commandline_spec.lua new file mode 100644 index 00000000..f07cf858 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/commandline_spec.lua @@ -0,0 +1,55 @@ +local fmt = string.format + +describe("Commandline tests:", function() + local parser = require("toggleterm.commandline") + it("should return a table containg correct arguments", function() + local file = vim.fn.tempname() .. ".txt" + vim.cmd(fmt("e %s", file)) + local result = parser.parse("cmd='echo %' dir='/test dir/file.txt'") + assert.is_truthy(result.cmd) + assert.is_truthy(result.dir) + + assert.equal(fmt("echo %s", file), result.cmd) + assert.equal("/test dir/file.txt", result.dir) + end) + + it("should handle double quotes", function() + local result = parser.parse('cmd="git status"') + assert.truthy(result.cmd) + assert.equal("git status", result.cmd) + end) + + it("should handle non-quoted arguments", function() + local result = parser.parse("direction=horizontal dir=/test/file.txt") + assert.is_truthy(result.dir) + assert.is_truthy(result.direction) + assert.equal("/test/file.txt", result.dir) + assert.equal("horizontal", result.direction) + end) + + it("should handle size args correctly", function() + local result = parser.parse("size=34") + assert.is_truthy(result.size) + assert.is_true(type(result.size) == "number") + assert.equal(34, result.size) + end) + + it("should handle name args correctly", function() + local result = parser.parse("name=sample") + assert.is_truthy(result.name) + assert.is_true(type(result.name) == "string") + assert.equal("sample", result.name) + end) + + it("should handle go_back args correctly", function() + local result = parser.parse("go_back=0") + assert.is_true(type(result.go_back) == "boolean") + assert.is_false(result.go_back) + end) + + it("should handle open args correctly", function() + local result = parser.parse("open=0") + assert.is_true(type(result.open) == "boolean") + assert.is_false(result.open) + end) +end) diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/minimal_init.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/minimal_init.lua new file mode 100644 index 00000000..d0b6f140 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/minimal_init.lua @@ -0,0 +1,39 @@ +local M = {} + +function M.root(root) + local f = debug.getinfo(1, "S").source:sub(2) + return vim.fn.fnamemodify(f, ":p:h:h") .. "/" .. (root or "") +end + +---@param plugin string +function M.load(plugin) + local name = plugin:match(".*/(.*)") + local package_root = M.root(".tests/site/pack/deps/start/") + if not vim.loop.fs_stat(package_root .. name) then + print("Installing " .. plugin) + vim.fn.mkdir(package_root, "p") + vim.fn.system({ + "git", + "clone", + "--depth=1", + "https://github.com/" .. plugin .. ".git", + package_root .. "/" .. name, + }) + end +end + +function M.setup() + vim.cmd([[set runtimepath=$VIMRUNTIME]]) + vim.opt.runtimepath:append(M.root()) + vim.opt.packpath = { M.root(".tests/site") } + M.load("nvim-lua/plenary.nvim") + vim.env.XDG_CONFIG_HOME = M.root(".tests/config") + vim.env.XDG_DATA_HOME = M.root(".tests/data") + vim.env.XDG_STATE_HOME = M.root(".tests/state") + vim.env.XDG_CACHE_HOME = M.root(".tests/cache") +end + +vim.o.swapfile = false +_G.__TEST = true + +M.setup() diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/state_spec.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/state_spec.lua new file mode 100644 index 00000000..825dd000 --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/state_spec.lua @@ -0,0 +1,43 @@ +_G.IS_TEST = true + +local t = require("toggleterm.terminal") + +local Terminal = t.Terminal + +describe("Terminal state - ", function() + local toggleterm + vim.o.hidden = true + vim.o.swapfile = false + + before_each(function() + package.loaded["toggleterm"] = nil + toggleterm = require("toggleterm") + toggleterm.setup({ start_in_insert = true }) + end) + + after_each(function() require("toggleterm.terminal").__reset() end) + + -- TODO: this test fails because (I think) the shell takes some time to start up and + -- and so the right autocommands haven't fired yet + pending("should persist the terminal state when the window is closed", function() + vim.cmd("edit test.txt") + local term = Terminal:new():toggle() + assert.is_equal(vim.bo.buftype, "terminal") + vim.api.nvim_feedkeys("ils", "x", true) + assert.is.equal("ls", vim.api.nvim_get_current_line()) + term:close() + assert.is_not_equal(vim.bo.buftype, "terminal") + assert.equal(t.mode.INSERT, term.__state.mode) + end) + + pending("should restore the terminal state when the window is re-opened", function() + local term = Terminal:new():toggle() + term:close() + term:open() + assert.equal(term.__state.mode, t.mode.UNSUPPORTED) + term:set_mode(t.mode.INSERT) + vim.cmd("wincmd p") + vim.cmd("wincmd p") + assert.equal(term.__state.mode, t.mode.INSERT) + end) +end) diff --git a/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/terminal_spec.lua b/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/terminal_spec.lua new file mode 100644 index 00000000..488c807d --- /dev/null +++ b/config/neovim/store/lazy-plugins/toggleterm.nvim/tests/terminal_spec.lua @@ -0,0 +1,474 @@ +_G.IS_TEST = true + +local api = vim.api +local fn = vim.fn +local fmt = string.format + +local spy = require("luassert.spy") +local match = require("luassert.match") + +local toggleterm = require("toggleterm") +local constants = require("toggleterm.constants") + +local ui = require("toggleterm.ui") +local t = require("toggleterm.terminal") + +---@type Terminal +local Terminal = t.Terminal +---@type fun(): Terminal[] +local get_all = t.get_all + +---Return if a terminal has windows +---@param term table +---@return boolean, TerminalWindow[] +local function term_has_windows(term) + return ui.find_open_windows(function(buf) return buf == term.bufnr end) +end + +describe("ToggleTerm tests:", function() + -- We must set hidden to use the plugin + vim.o.hidden = true + + after_each(function() require("toggleterm.terminal").__reset() end) + + describe("toggling terminals - ", function() + it("new terminals are assigned incremental ids", function() + local test1 = Terminal:new():toggle() + local test2 = Terminal:new():toggle() + local test3 = Terminal:new():toggle() + assert.are.same(test1.id, 1) + assert.are.same(test2.id, 2) + assert.are.same(test3.id, 3) + end) + + it("should assign the next id filling in any missing gaps", function() + Terminal:new({ id = 2 }):toggle() --2 + Terminal:new():toggle() --1 + Terminal:new():toggle() --3 + Terminal:new():toggle() --4 + Terminal:new({ id = 6 }):toggle() --6 + local terms = get_all() + terms[3]:shutdown() + terms[1]:shutdown() + local new1 = Terminal:new():toggle() + assert.equal(1, new1.id) + local new3 = Terminal:new():toggle() + assert.equal(3, new3.id) + local new5 = Terminal:new():toggle() + assert.equal(5, new5.id) + local new7 = Terminal:new():toggle() + assert.equal(7, new7.id) + end) + + it("should get terminals as a list", function() + Terminal:new({ id = 20 }):toggle() + Terminal:new():toggle() + local terms = get_all() + assert.equal(#terms, 2) + assert.equal(terms[#terms].id, 20) + end) + + it("should open a terminal window on toggle", function() + local test1 = Terminal:new() + test1:toggle() + assert.is_true(api.nvim_buf_is_valid(test1.bufnr)) + assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window)) + end) + + it("should close a terminal window if open", function() + local test1 = Terminal:new() + test1:toggle() + assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window)) + test1:toggle() + assert.is_not_true(vim.tbl_contains(api.nvim_list_wins(), test1.window)) + end) + + it("should toggle a specific buffer if a count is passed", function() + toggleterm.toggle(2, 15) + local terminals = get_all() + assert.equals(#terminals, 1) + local term = terminals[1] + assert.is_true(term_has_windows(term)) + end) + + it("should not list hidden terminals", function() + Terminal:new({ hidden = true }):toggle() + local terminals = get_all() + assert.equal(#terminals, 0) + Terminal:new():toggle() + terminals = get_all() + assert.equal(#terminals, 1) + end) + + -- FIXME: this test does not work despite the functionality seeming to work + -- the idea here is that if a custom terminal with hidden = true is created + -- then it shouldn't be toggled open or closed if the general toggleterm command + -- is run so I expect to still see that it's window is open + pending("should not toggle a terminal if hidden", function() + local term = Terminal:new({ cmd = "bash", hidden = true }):toggle() + assert.is_true(term_has_windows(term)) + toggleterm.toggle(1) + assert.is_true(term_has_windows(term)) + end) + + it("should not toggle a terminal if not hidden", function() + local term = Terminal:new():toggle() + assert.is_true(term_has_windows(term)) + toggleterm.toggle(1) + assert.is_false(term_has_windows(term)) + end) + + it("should create a terminal with a custom command", function() + Terminal:new({ cmd = "ls" }):toggle() + assert.truthy(vim.b.term_title:match("ls")) + end) + + -- FIXME: Fix flaky test + pending("should spawn in the background", function() + local stdout = {} + local has_spawned = function() return table.concat(stdout, ""):match("SPAWNED") ~= nil end + Terminal:new({ + cmd = [[echo SPAWNED]], + on_stdout = function(_, _, lines) vim.list_extend(stdout, lines) end, + }):spawn() + -- Wait some time if job is not ready + vim.wait(1500, has_spawned) + assert.is_true(has_spawned()) + end) + + -- FIXME: Fix flaky test + pending("should pass environmental variables", function() + local stdout = {} + local expected = "TESTVAR = 0123456789" + local find_end = function() return table.concat(stdout, ""):match(expected) end + Terminal:new({ + cmd = [[echo "TESTVAR = $TESTVAR END"]], + env = { TESTVAR = "0123456789" }, + on_stdout = function(_, _, lines) vim.list_extend(stdout, lines) end, + }):toggle() + -- Wait some time if job is not ready + vim.wait(500, find_end) + assert.are.equal(expected, table.concat(stdout, " "):match("TESTVAR = %S+")) + end) + + it("should open the correct terminal if a user specifies a count", function() + local term = Terminal:new({ count = 5 }):toggle() + term:toggle() + assert.is_false(ui.term_has_open_win(term)) + toggleterm.toggle(5) + assert.is_true(ui.term_has_open_win(term)) + end) + + it("should open the last toggled terminal", function() + -- GIVEN two opened terminals + toggleterm.toggle(1) + toggleterm.toggle(2) + -- AND then closed first terminal + toggleterm.toggle(1) + -- AND then close second terminal via smart toggle + toggleterm.toggle(0) + + -- WHEN a smart toggle is hit + toggleterm.toggle(0) + + local terms = get_all() + --- THEN only the second terminal should be opened + assert.is_true(ui.term_has_open_win(terms[2])) + assert.is_false(ui.term_has_open_win(terms[1])) + end) + + it("should open the last toggled terminal view", function() + -- GIVEN two opened terminals + toggleterm.toggle(1) + toggleterm.toggle(2) + get_all()[1]:focus() + -- AND then closed terminal view via smart toggle + toggleterm.toggle(0) + + -- WHEN a smart toggle is hit + toggleterm.toggle(0) + + --- THEN both terminals should be opened + local terms = get_all() + local term1 = terms[1] + local term2 = terms[2] + assert.is_true(term1:is_focused()) + assert.is_false(term2:is_focused()) + assert.is_true(ui.term_has_open_win(term2)) + assert.is_true(ui.term_has_open_win(term1)) + end) + + it("should open the last toggled nth terminal", function() + -- GIVEN two opened terminals + toggleterm.toggle(1) + toggleterm.toggle(2) + -- AND then closed terminal view via smart toggle + toggleterm.toggle(0) + -- AND then open and close 3rd terminal + toggleterm.toggle(3) + toggleterm.toggle(3) + + -- WHEN a smart toggle is hit + toggleterm.toggle(0) + + local terms = get_all() + --- THEN only 3rd terminal should be opened + assert.is_true(ui.term_has_open_win(terms[3])) + assert.is_false(ui.term_has_open_win(terms[2])) + assert.is_false(ui.term_has_open_win(terms[1])) + end) + + it("should open a hidden terminal and a visible one", function() + local hidden = Terminal:new({ hidden = true }):toggle() + local visible = Terminal:new():toggle() + hidden:toggle() + visible:toggle() + end) + + it("should close all open terminals using toggle all", function() + local test1 = Terminal:new():toggle() + local test2 = Terminal:new():toggle() + toggleterm.toggle_all() + + assert.is_false(ui.term_has_open_win(test1)) + assert.is_false(ui.term_has_open_win(test2)) + end) + + it("should open all open terminals using toggle all", function() + local test1 = Terminal:new():toggle() + local test2 = Terminal:new():toggle() + toggleterm.toggle_all() + + assert.is_false(ui.term_has_open_win(test1)) + assert.is_false(ui.term_has_open_win(test2)) + + toggleterm.toggle_all() + assert.is_true(ui.term_has_open_win(test1)) + assert.is_true(ui.term_has_open_win(test2)) + end) + + -- FIXME: broken in CI + -- it("should close on exit", function() + -- local term = Terminal:new():toggle() + -- assert.is_true(ui.term_has_open_win(term)) + -- term:send("exit") + -- vim.wait(1000, function() end) + -- assert.is_false(ui.term_has_open_win(term)) + -- end) + end) + + describe("terminal buffers options - ", function() + before_each( + function() + toggleterm.setup({ + open_mapping = [[<c-\>]], + shade_filetypes = { "none" }, + direction = "horizontal", + float_opts = { + height = 10, + width = 20, + }, + }) + end + ) + + it("should give each terminal a winhighlight", function() + local test1 = Terminal:new({ direction = "horizontal" }):toggle() + assert.is_true(test1:is_split()) + local winhighlight = vim.wo[test1.window].winhighlight + assert.is.truthy(winhighlight:match("Normal:ToggleTerm" .. test1.id .. "Normal")) + end) + + it("should set the correct filetype", function() + local test1 = Terminal:new():toggle() + local ft = vim.bo[test1.bufnr].filetype + assert.equals(constants.FILETYPE, ft) + end) + end) + + describe("executing commands - ", function() + it("should open a terminal to execute commands", function() + toggleterm.exec("ls", 1) + local terminals = get_all() + assert.is_true(#terminals == 1) + assert.is_true(term_has_windows(terminals[1])) + end) + + it("should change terminal's directory if specified", function() + toggleterm.exec("ls", 1, 15, fn.expand("~/")) + local terminals = get_all() + assert.is_true(#terminals == 1) + assert.is_true(term_has_windows(terminals[1])) + end) + + it("should send commands to a terminal on exec", function() + local test1 = Terminal:new():toggle() + local _ = match._ + spy.on(test1, "send") + toggleterm.exec('echo "hello world"', 1) + assert.spy(test1.send).was_called() + assert.spy(test1.send).was_called_with(_, 'echo "hello world"', true) + assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window)) + end) + + it("should send commands to a terminal without opening its window", function() + local test1 = Terminal:new():toggle() + test1:close() + spy.on(test1, "send") + toggleterm.exec_command("cmd='echo \"hello world\"' open=0", 1) + assert.spy(test1.send).was_called_with(test1, 'echo "hello world"', false) + assert.is_false(vim.tbl_contains(api.nvim_list_wins(), test1.window)) + end) + + it("should execute the same regardless whether shell is a string or a function", function() + toggleterm.setup({ shell = function() return vim.o.shell end }) + local test1 = Terminal:new():toggle() + local _ = match._ + spy.on(test1, "send") + toggleterm.exec('echo "hello world"', 1) + assert.spy(test1.send).was_called() + assert.spy(test1.send).was_called_with(_, 'echo "hello world"', true) + assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window)) + end) + + it("should expand vim wildcards", function() + local file = vim.fn.tempname() .. ".txt" + vim.cmd(fmt("e %s", file)) + local test1 = Terminal:new():toggle() + vim.cmd("wincmd w") + spy.on(test1, "send") + toggleterm.exec_command("cmd='echo %'", 1) + assert.spy(test1.send).was_called_with(test1, fmt("echo %s", file), true) + end) + + it("should handle nested quotes in cmd args", function() + local file = vim.fn.tempname() .. ".txt" + vim.cmd(fmt("e %s", file)) + local test1 = Terminal:new():toggle() + vim.cmd("wincmd w") + spy.on(test1, "send") + toggleterm.exec_command("cmd='g++ -std=c++17 % -o run'", 1) + assert.spy(test1.send).was_called_with(test1, fmt("g++ -std=c++17 %s -o run", file), true) + end) + end) + + describe("terminal mappings behaviour", function() + it("should respect terminal_mappings in terminal mode", function() + toggleterm.setup({ open_mapping = [[<space>t]], terminal_mappings = false }) + t.Terminal:new():toggle() + local result = vim.fn.mapcheck("<space>t", "t") + assert.equal("", result) + end) + + it("should map in terminal mode if terminal_mappings is true", function() + toggleterm.setup({ open_mapping = [[<space>t]], terminal_mappings = true }) + t.Terminal:new():toggle() + local result = vim.fn.mapcheck("<space>t", "t") + assert.is_true(#result > 0) + end) + end) + + describe("layout options - ", function() + before_each( + function() + toggleterm.setup({ + open_mapping = [[<c-\>]], + shade_filetypes = { "none" }, + direction = "horizontal", + float_opts = { + height = 10, + width = 20, + }, + }) + end + ) + + it("should open with the correct layout", function() + local term = Terminal:new({ direction = "float" }):toggle() + local _, wins = term_has_windows(term) + assert.equal(#wins, 1) + assert.equal("popup", fn.win_gettype(fn.win_id2win(wins[1].window))) + end) + + it("should not change numbers when resolving size", function() + local term = Terminal:new() + local size = 20 + assert.equal(size, ui._resolve_size(size)) + assert.equal(size, ui._resolve_size(size, term)) + end) + + it("should evaluate custom functions when resolving size", function() + local term = Terminal:new({ direction = "vertical" }) + local size1 = 20 + local size2 = function(_t) + if _t.direction == "vertical" then return size1 end + return 0 + end + assert.equal(ui._resolve_size(size2, term), size1) + end) + + it("should correctly merge a users highlights", function() + toggleterm.setup({ + shade_terminals = false, + highlights = { + Normal = { + guibg = "Red", + }, + }, + }) + local config = require("toggleterm.config").get("highlights") + assert.equal(config.Normal.guibg, "Red") + assert.truthy(config.StatusLine.guibg) + end) + + it("should prefer shading over a users highlights if they opt to shade terminals", function() + toggleterm.setup({ + shade_terminals = true, + highlights = { + Normal = { + guibg = "Red", + }, + }, + }) + local config = require("toggleterm.config").get("highlights") + assert.is_not_equal(config.Normal.guibg, "Red") + assert.truthy(config.StatusLine.guibg) + end) + + -- FIXME the height is passed in correctly but is returned as 15 + -- which seems to be an nvim quirk not the code + it("should open with user configuration if set", function() + local term = Terminal:new({ direction = "float" }):toggle() + local _, wins = term_has_windows(term) + ---@type table + local config = api.nvim_win_get_config(wins[1].window) + assert.equal(config.width, 20) + end) + + it("should use a user's selected highlights", function() + local normal = "#000000" + local border = "#ffffff" + + local term = Terminal:new({ + direction = "float", + highlights = { + NormalFloat = { + guibg = normal, + }, + FloatBorder = { + guifg = border, + }, + }, + float_opts = { + winblend = 12, + }, + }):toggle() + local winhighlight = vim.wo[term.window].winhighlight + local winblend = vim.wo[term.window].winblend + assert.equal(12, winblend) + + assert.is.truthy(winhighlight:match("NormalFloat:ToggleTerm" .. term.id .. "NormalFloat")) + assert.is.truthy(winhighlight:match("FloatBorder:ToggleTerm" .. term.id .. "FloatBorder")) + end) + end) +end) diff --git a/config/neovim/store/lazy-plugins/trim.nvim/.github/dependabot.yml b/config/neovim/store/lazy-plugins/trim.nvim/.github/dependabot.yml new file mode 100644 index 00000000..012385d7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every weekday + interval: "daily" diff --git a/config/neovim/store/lazy-plugins/trim.nvim/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/trim.nvim/.github/workflows/ci.yml new file mode 100644 index 00000000..45a5493c --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +name: CI +on: + push: + pull_request: + +jobs: + tests: + strategy: + matrix: + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Install Neovim + shell: bash + run: | + mkdir -p /tmp/nvim + wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage + cd /tmp/nvim + chmod a+x ./nvim.appimage + ./nvim.appimage --appimage-extract + echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + - name: Run Lints + uses: lunarmodules/luacheck@v1 + with: + args: lua/trim + - name: Run Tests + run: | + alias nvim="/tmp/nvim-linux64/bin/nvim" + nvim --version + [ ! -d tests ] && exit 0 + nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua', sequential = true}" + + docs: + runs-on: ubuntu-latest + needs: tests + if: ${{ github.ref == 'refs/heads/master' }} + steps: + - uses: actions/checkout@v4 + - name: panvimdoc + uses: kdheepak/panvimdoc@main + with: + vimdoc: trim.nvim + version: "Neovim >= 0.7.0" + demojify: true + treesitter: true + - name: Push changes + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "chore(build): auto-generate vimdoc" + commit_user_name: "github-actions[bot]" + commit_user_email: "github-actions[bot]@users.noreply.github.com" + commit_author: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>" diff --git a/config/neovim/store/lazy-plugins/trim.nvim/.github/workflows/release.yml b/config/neovim/store/lazy-plugins/trim.nvim/.github/workflows/release.yml new file mode 100644 index 00000000..05421fdb --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release +on: + push: + tags: + - "v*.*.*" +jobs: + build: + name: Create Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@master + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/config/neovim/store/lazy-plugins/trim.nvim/.gitignore b/config/neovim/store/lazy-plugins/trim.nvim/.gitignore new file mode 100644 index 00000000..93797214 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/.gitignore @@ -0,0 +1,43 @@ +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Tests +.tests diff --git a/config/neovim/store/lazy-plugins/trim.nvim/.luacheckrc b/config/neovim/store/lazy-plugins/trim.nvim/.luacheckrc new file mode 100644 index 00000000..b0350795 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/.luacheckrc @@ -0,0 +1,26 @@ +-- Rerun tests only if their modification time changed. +cache = true + +std = luajit +codes = true + +self = false + +-- Glorious list of warnings: https://luacheck.readthedocs.io/en/stable/warnings.html +ignore = { + "212", -- Unused argument, In the case of callback function, _arg_name is easier to understand than _, so this option is set to off. + "122", -- Indirectly setting a readonly global +} + +globals = { + "_", + "TelescopeGlobalState", + "_TelescopeConfigurationValues", + "_TelescopeConfigurationPickers", + "__TelescopeKeymapStore", +} + +-- Global objects defined by the C code +read_globals = { + "vim", +} diff --git a/config/neovim/store/lazy-plugins/trim.nvim/.stylua.toml b/config/neovim/store/lazy-plugins/trim.nvim/.stylua.toml new file mode 100644 index 00000000..5d12dbdf --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/.stylua.toml @@ -0,0 +1,6 @@ +column_width = 160 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferSingle" +no_call_parentheses = true diff --git a/config/neovim/store/lazy-plugins/trim.nvim/CONTRIBUTING.md b/config/neovim/store/lazy-plugins/trim.nvim/CONTRIBUTING.md new file mode 100644 index 00000000..ba50678e --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/CONTRIBUTING.md @@ -0,0 +1,7 @@ +# Contributing + +## Submitting a new pattern for trimming + +You can add patterns to [trim\.nvim/config\.lua at master · cappyzawa/trim\.nvim](https://github.com/cappyzawa/trim.nvim/blob/master/lua/trim/config.lua). + +Thanks! diff --git a/config/neovim/store/lazy-plugins/trim.nvim/LICENSE b/config/neovim/store/lazy-plugins/trim.nvim/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/config/neovim/store/lazy-plugins/trim.nvim/NOTICE.md b/config/neovim/store/lazy-plugins/trim.nvim/NOTICE.md new file mode 100644 index 00000000..b183890c --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/NOTICE.md @@ -0,0 +1,16 @@ +Copyright (c) 2020 Shu Kutsuzawa <cappyzawa@yahoo.ne.jp> + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +``` +http://www.apache.org/licenses/LICENSE-2.0 +``` + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/config/neovim/store/lazy-plugins/trim.nvim/README.md b/config/neovim/store/lazy-plugins/trim.nvim/README.md new file mode 100644 index 00000000..a013abee --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/README.md @@ -0,0 +1,78 @@ +# trim.nvim + +[![GitHub release](https://img.shields.io/github/release/cappyzawa/trim.nvim.svg)](https://github.com/cappyzawa/trim.nvim/releases) +[![GitHub](https://img.shields.io/github/license/cappyzawa/trim.nvim.svg)](./LICENSE) + +This plugin trims trailing whitespace and lines. + +## Requirements + +**Neovim v0.7.0+** + +## How to install + +### Lazy + +```lua +require("lazy").setup({ + "cappyzawa/trim.nvim", + opts = {} +}, opt) +``` + +### Packer + +```lua +use({ + "cappyzawa/trim.nvim", + config = function() + require("trim").setup({}) + end +}) +``` + +## How to setup + +```lua +-- default config +local default_config = { + ft_blocklist = {}, + patterns = {}, + trim_on_write = true, + trim_trailing = true, + trim_last_line = true, + trim_first_line = true, + highlight = false, + highlight_bg = '#ff0000', -- or 'red' + highlight_ctermbg = 'red', +} +``` + +```lua +require('trim').setup({ + -- if you want to ignore markdown file. + -- you can specify filetypes. + ft_blocklist = {"markdown"}, + + -- if you want to remove multiple blank lines + patterns = { + [[%s/\(\n\n\)\n\+/\1/]], -- replace multiple blank lines with a single line + }, + + -- if you want to disable trim on write by default + trim_on_write = false, + + -- highlight trailing spaces + highlight = true +}) +``` + +## Commands + +### `:TrimToggle` + +Toggle trim on save. + +### `:Trim` + +Trim the buffer right away. diff --git a/config/neovim/store/lazy-plugins/trim.nvim/doc/tags b/config/neovim/store/lazy-plugins/trim.nvim/doc/tags new file mode 100644 index 00000000..e91a15c1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/doc/tags @@ -0,0 +1,8 @@ +trim.nvim-links trim.nvim.txt /*trim.nvim-links* +trim.nvim-table-of-contents trim.nvim.txt /*trim.nvim-table-of-contents* +trim.nvim-trim.nvim trim.nvim.txt /*trim.nvim-trim.nvim* +trim.nvim-trim.nvim-commands trim.nvim.txt /*trim.nvim-trim.nvim-commands* +trim.nvim-trim.nvim-how-to-install trim.nvim.txt /*trim.nvim-trim.nvim-how-to-install* +trim.nvim-trim.nvim-how-to-setup trim.nvim.txt /*trim.nvim-trim.nvim-how-to-setup* +trim.nvim-trim.nvim-requirements trim.nvim.txt /*trim.nvim-trim.nvim-requirements* +trim.nvim.txt trim.nvim.txt /*trim.nvim.txt* diff --git a/config/neovim/store/lazy-plugins/trim.nvim/doc/trim.nvim.txt b/config/neovim/store/lazy-plugins/trim.nvim/doc/trim.nvim.txt new file mode 100644 index 00000000..16e62598 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/doc/trim.nvim.txt @@ -0,0 +1,107 @@ +*trim.nvim.txt* For Neovim >= 0.7.0 Last change: 2024 March 15 + +============================================================================== +Table of Contents *trim.nvim-table-of-contents* + +1. trim.nvim |trim.nvim-trim.nvim| + - Requirements |trim.nvim-trim.nvim-requirements| + - How to install |trim.nvim-trim.nvim-how-to-install| + - How to setup |trim.nvim-trim.nvim-how-to-setup| + - Commands |trim.nvim-trim.nvim-commands| + +============================================================================== +1. trim.nvim *trim.nvim-trim.nvim* + +<https://github.com/cappyzawa/trim.nvim/releases> <./LICENSE> + +This plugin trims trailing whitespace and lines. + + +REQUIREMENTS *trim.nvim-trim.nvim-requirements* + +**Neovim v0.7.0+** + + +HOW TO INSTALL *trim.nvim-trim.nvim-how-to-install* + + +LAZY ~ + +>lua + require("lazy").setup({ + "cappyzawa/trim.nvim", + opts = {} + }, opt) +< + + +PACKER ~ + +>lua + use({ + "cappyzawa/trim.nvim", + config = function() + require("trim").setup({}) + end + }) +< + + +HOW TO SETUP *trim.nvim-trim.nvim-how-to-setup* + +>lua + -- default config + local default_config = { + ft_blocklist = {}, + patterns = {}, + trim_on_write = true, + trim_trailing = true, + trim_last_line = true, + trim_first_line = true, + highlight = false, + highlight_bg = '#ff0000', -- or 'red' + highlight_ctermbg = 'red', + } +< + +>lua + require('trim').setup({ + -- if you want to ignore markdown file. + -- you can specify filetypes. + ft_blocklist = {"markdown"}, + + -- if you want to remove multiple blank lines + patterns = { + [[%s/\(\n\n\)\n\+/\1/]], -- replace multiple blank lines with a single line + }, + + -- if you want to disable trim on write by default + trim_on_write = false, + + -- highlight trailing spaces + highlight = true + }) +< + + +COMMANDS *trim.nvim-trim.nvim-commands* + + +:TRIMTOGGLE ~ + +Toggle trim on save. + + +:TRIM ~ + +Trim the buffer right away. + +============================================================================== +2. Links *trim.nvim-links* + +1. *GitHub release*: https://img.shields.io/github/release/cappyzawa/trim.nvim.svg +2. *GitHub*: https://img.shields.io/github/license/cappyzawa/trim.nvim.svg + +Generated by panvimdoc <https://github.com/kdheepak/panvimdoc> + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/trim.nvim/lua/trim/config.lua b/config/neovim/store/lazy-plugins/trim.nvim/lua/trim/config.lua new file mode 100644 index 00000000..580981e0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/lua/trim/config.lua @@ -0,0 +1,67 @@ +local M = { + config = {}, +} + +local default_config = { + ft_blocklist = {}, + patterns = {}, + trim_on_write = true, + trim_trailing = true, + trim_last_line = true, + trim_first_line = true, + highlight = false, + highlight_bg = '#ff0000', + highlight_ctermbg = 'red', +} + +function M.setup(opts) + opts = opts or {} + + -- compatability: disable -> ft_blocklist + if opts.disable and not opts.ft_blocklist then + vim.notify('`disable` is deprecated, use `ft_blocklist` instead', vim.log.levels.WARN, { title = 'trim.nvim' }) + opts.ft_blocklist = opts.disable + end + + M.config = vim.tbl_deep_extend('force', default_config, opts) + + if M.config.trim_trailing then + table.insert(M.config.patterns, [[%s/\s\+$//e]]) + end + if M.config.trim_first_line then + table.insert(M.config.patterns, [[%s/\%^\n\+//]]) + end + if M.config.trim_last_line then + table.insert(M.config.patterns, [[%s/\($\n\s*\)\+\%$//]]) + end + + if M.config.highlight then + local augroup = vim.api.nvim_create_augroup('TrimHighlight', { clear = true }) + + -- Define the autocommand for FileType events + vim.api.nvim_create_autocmd('FileType', { + group = augroup, + pattern = '*', -- This will trigger the autocmd for all file types + callback = function() + -- Check if the current file type is not in the blocklist to avoid highlighting + if not vim.tbl_contains(M.config.ft_blocklist, vim.bo.filetype) then + -- Apply highlighting for trailing whitespace + vim.api.nvim_set_hl(0, 'ExtraWhitespace', { + bg = M.config.highlight_bg, + ctermbg = M.config.highlight_ctermbg + }) + vim.api.nvim_exec('match ExtraWhitespace /\\s\\+$/', false) + else + -- Clear the highlighting for this buffer if the file type is in the blocklist + vim.api.nvim_exec('match none', false) + end + end, + }) + end +end + +function M.get() + return M.config +end + +return M diff --git a/config/neovim/store/lazy-plugins/trim.nvim/lua/trim/init.lua b/config/neovim/store/lazy-plugins/trim.nvim/lua/trim/init.lua new file mode 100644 index 00000000..96adcdde --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/lua/trim/init.lua @@ -0,0 +1,14 @@ +local M = {} + +local config = require 'trim.config' +local trimmer = require 'trim.trimmer' + +function M.setup(opt) + config.setup(opt) + local cfg = config.get() + if cfg.trim_on_write then + trimmer.enable(true) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/trim.nvim/lua/trim/trimmer.lua b/config/neovim/store/lazy-plugins/trim.nvim/lua/trim/trimmer.lua new file mode 100644 index 00000000..4132bf7d --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/lua/trim/trimmer.lua @@ -0,0 +1,59 @@ +local vim = vim +local api = vim.api + +local trimmer = {} + +function trimmer.trim() + local config = require('trim.config').get() + local save = vim.fn.winsaveview() + for _, v in ipairs(config.patterns) do + api.nvim_exec(string.format('keepjumps keeppatterns silent! %s', v), false) + end + vim.fn.winrestview(save) +end + +local has_value = function(tbl, val) + for _, v in ipairs(tbl) do + if v == val then + return true + end + end + return false +end + +function trimmer.enable(is_configured) + local opts = { pattern = '*' } + local config = require('trim.config').get() + vim.api.nvim_create_augroup('TrimNvim', { clear = true }) + vim.api.nvim_create_autocmd('BufWritePre', { + group = 'TrimNvim', + pattern = opts.pattern, + callback = function() + if not has_value(config.ft_blocklist, vim.bo.filetype) then + trimmer.trim() + end + end, + }) + if not is_configured then + vim.notify('TrimNvim enabled', vim.log.levels.INFO, { title = 'trim.nvim' }) + end +end + +function trimmer.disable() + pcall(vim.api.nvim_del_augroup_by_name, 'TrimNvim') + vim.notify('TrimNvim disabled', vim.log.levels.INFO, { title = 'trim.nvim' }) +end + +function trimmer.toggle() + local status = pcall(vim.api.nvim_get_autocmds, { + group = 'TrimNvim', + event = 'BufWritePre', + }) + if not status then + trimmer.enable(false) + else + trimmer.disable() + end +end + +return trimmer diff --git a/config/neovim/store/lazy-plugins/trim.nvim/plugin/trim.lua b/config/neovim/store/lazy-plugins/trim.nvim/plugin/trim.lua new file mode 100644 index 00000000..b322ff7f --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/plugin/trim.lua @@ -0,0 +1,23 @@ +if vim.g.load_trim_nvim then + return +end +vim.g.load_trim_nvim = true + +local trimmer = require 'trim.trimmer' + +if not vim.api.nvim_create_autocmd then + vim.notify_once('trim.nvim requires nvim 0.7.0+.', vim.log.levels.ERROR, { title = 'trim.nvim' }) + return +end + +vim.api.nvim_create_user_command('TrimToggle', function(args) + trimmer.toggle() +end, { + range = false, +}) + +vim.api.nvim_create_user_command('Trim', function(args) + trimmer.trim() +end, { + range = false, +}) diff --git a/config/neovim/store/lazy-plugins/trim.nvim/tests/core/config_spec.lua b/config/neovim/store/lazy-plugins/trim.nvim/tests/core/config_spec.lua new file mode 100644 index 00000000..b70a3448 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/tests/core/config_spec.lua @@ -0,0 +1,69 @@ +local config = require 'trim.config' + +local assert = require 'luassert' + +describe('default config', function() + config.setup() + local actual = config.get() + + it('has empty ft_blocklist', function() + assert.are.equal(#actual.ft_blocklist, 0) + end) + + it('has trim_on_write enabled', function() + assert.is_true(actual.trim_on_write) + end) + + it('has trim_trailing enabled', function() + assert.is_true(actual.trim_trailing) + end) + + it('has trim_last_line enabled', function() + assert.is_true(actual.trim_last_line) + end) + + it('has trim_first_line enabled', function() + assert.is_true(actual.trim_first_line) + end) + + it('has 3 patterns', function() + assert.are.equal(#actual.patterns, 3) + assert.are.equal(actual.patterns[1], [[%s/\s\+$//e]]) + assert.are.equal(actual.patterns[2], [[%s/\%^\n\+//]]) + assert.are.equal(actual.patterns[3], [[%s/\($\n\s*\)\+\%$//]]) + end) +end) + +describe('config', function() + config.setup { + ft_blocklist = { 'markdown' }, + trim_on_write = false, + trim_trailing = false, + trim_last_line = false, + trim_first_line = false, + patterns = { [[%s/\(\n\n\)\n\+/\1/]] }, + } + + local actual = config.get() + + it('is overwritten', function() + assert(#actual.ft_blocklist, 1) + assert(actual.ft_blocklist[1], 'markdown') + assert.is_not_true(actual.trim_on_write) + assert.is_not_true(actual.trim_trailing) + assert.is_not_true(actual.trim_last_line) + assert.is_not_true(actual.trim_first_line) + + assert.are.equal(#actual.patterns, 1) + assert.are.equal(actual.patterns[1], [[%s/\(\n\n\)\n\+/\1/]]) + end) + + it('keeps compatability', function() + config.setup { disable = { 'lua' } } + local actual = config.get() + + -- disable is deprecated, use ft_blocklist instead + assert.are.equal(#actual.ft_blocklist, 1) + assert.are.equal(actual.ft_blocklist[1], 'lua') + end) +end) diff --git a/config/neovim/store/lazy-plugins/trim.nvim/tests/core/init_spec.lua b/config/neovim/store/lazy-plugins/trim.nvim/tests/core/init_spec.lua new file mode 100644 index 00000000..7e7c7995 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/tests/core/init_spec.lua @@ -0,0 +1,7 @@ +describe('init', function() + it('has correct environment for tests', function() + for _, path in ipairs { 'config', 'data', 'cache', 'state' } do + assert(vim.fn.stdpath(path):find('.tests/' .. path)) + end + end) +end) diff --git a/config/neovim/store/lazy-plugins/trim.nvim/tests/init.lua b/config/neovim/store/lazy-plugins/trim.nvim/tests/init.lua new file mode 100644 index 00000000..7a3ca3be --- /dev/null +++ b/config/neovim/store/lazy-plugins/trim.nvim/tests/init.lua @@ -0,0 +1,36 @@ +local M = {} + +function M.root(root) + local f = debug.getinfo(1, 'S').source:sub(2) + return vim.fn.fnamemodify(f, ':p:h:h') .. '/' .. (root or '') +end + +---@param plugin string +function M.load(plugin) + local name = plugin:match '.*/(.*)' + local package_root = M.root '.tests/site/pack/deps/start/' + if not vim.loop.fs_stat(package_root .. name) then + print('Installing ' .. plugin) + vim.fn.mkdir(package_root, 'p') + vim.fn.system { + 'git', + 'clone', + '--depth=1', + 'https://github.com/' .. plugin .. '.git', + package_root .. '/' .. name, + } + end +end + +function M.setup() + vim.cmd [[set runtimepath=$VIMRUNTIME]] + vim.opt.runtimepath:append(M.root()) + vim.opt.packpath = { M.root '.tests/site' } + M.load 'nvim-lua/plenary.nvim' + vim.env.XDG_CONFIG_HOME = M.root '.tests/config' + vim.env.XDG_DATA_HOME = M.root '.tests/data' + vim.env.XDG_STATE_HOME = M.root '.tests/state' + vim.env.XDG_CACHE_HOME = M.root '.tests/cache' +end + +M.setup() diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/bug_report.yml b/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..b7cfd905 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,89 @@ +name: Bug Report +description: File a bug/issue +title: "bug: " +labels: [bug] +body: + - type: markdown + attributes: + value: | + **Before** reporting an issue, make sure to read the [documentation](https://github.com/folke/trouble.nvim) and search [existing issues](https://github.com/folke/trouble.nvim/issues). Usage questions such as ***"How do I...?"*** belong in [Discussions](https://github.com/folke/trouble.nvim/discussions) and will be closed. + - type: checkboxes + attributes: + label: Did you check docs and existing issues? + description: Make sure you checked all of the below before submitting an issue + options: + - label: I have read all the trouble.nvim docs + required: true + - label: I have searched the existing issues of trouble.nvim + required: true + - label: I have searched the existing issues of plugins related to this issue + required: true + - type: input + attributes: + label: "Neovim version (nvim -v)" + placeholder: "0.8.0 commit db1b0ee3b30f" + validations: + required: true + - type: input + attributes: + label: "Operating system/version" + placeholder: "MacOS 11.5" + validations: + required: true + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. Please include any related errors you see in Neovim. + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. + 2. + 3. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Repro + description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua` + value: | + -- DO NOT change the paths and don't remove the colorscheme + local root = vim.fn.fnamemodify("./.repro", ":p") + + -- set stdpaths to use .repro + for _, name in ipairs({ "config", "data", "state", "cache" }) do + vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name + end + + -- bootstrap lazy + local lazypath = root .. "/plugins/lazy.nvim" + if not vim.loop.fs_stat(lazypath) then + vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath, }) + end + vim.opt.runtimepath:prepend(lazypath) + + -- install plugins + local plugins = { + "folke/tokyonight.nvim", + "folke/trouble.nvim", + -- add any other plugins here + } + require("lazy").setup(plugins, { + root = root .. "/plugins", + }) + + vim.cmd.colorscheme("tokyonight") + -- add anything else here + render: Lua + validations: + required: false diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/bug_report_v3.yml b/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/bug_report_v3.yml new file mode 100644 index 00000000..20b04ab4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/bug_report_v3.yml @@ -0,0 +1,89 @@ +name: Bug Report v3 +description: File a bug/issue +title: "bug v3: " +labels: [bug v3] +body: + - type: markdown + attributes: + value: | + **Before** reporting an issue, make sure to read the [documentation](https://github.com/folke/trouble.nvim/tree/dev) and search [existing issues](https://github.com/folke/trouble.nvim/issues). Usage questions such as ***"How do I...?"*** belong in [Discussions](https://github.com/folke/trouble.nvim/discussions) and will be closed. + - type: checkboxes + attributes: + label: Did you check docs and existing issues? + description: Make sure you checked all of the below before submitting an issue + options: + - label: I have read all the trouble.nvim docs + required: true + - label: I have searched the existing issues of trouble.nvim + required: true + - label: I have searched the existing issues of plugins related to this issue + required: true + - type: input + attributes: + label: "Neovim version (nvim -v)" + placeholder: "0.8.0 commit db1b0ee3b30f" + validations: + required: true + - type: input + attributes: + label: "Operating system/version" + placeholder: "MacOS 11.5" + validations: + required: true + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. Please include any related errors you see in Neovim. + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. + 2. + 3. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Repro + description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua` + value: | + -- DO NOT change the paths and don't remove the colorscheme + local root = vim.fn.fnamemodify("./.repro", ":p") + + -- set stdpaths to use .repro + for _, name in ipairs({ "config", "data", "state", "cache" }) do + vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name + end + + -- bootstrap lazy + local lazypath = root .. "/plugins/lazy.nvim" + if not vim.loop.fs_stat(lazypath) then + vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath, }) + end + vim.opt.runtimepath:prepend(lazypath) + + -- install plugins + local plugins = { + "folke/tokyonight.nvim", + "folke/trouble.nvim", + -- add any other plugins here + } + require("lazy").setup(plugins, { + root = root .. "/plugins", + }) + + vim.cmd.colorscheme("tokyonight") + -- add anything else here + render: Lua + validations: + required: false diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/feature_request.yml b/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..47325e2a --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,36 @@ +name: Feature Request +description: Suggest a new feature +title: "feature: " +labels: [enhancement] +body: + - type: checkboxes + attributes: + label: Did you check the docs? + description: Make sure you read all the docs before submitting a feature request + options: + - label: I have read all the trouble.nvim docs + required: true + - type: textarea + validations: + required: true + attributes: + label: Is your feature request related to a problem? Please describe. + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + - type: textarea + validations: + required: true + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + - type: textarea + validations: + required: true + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + - type: textarea + validations: + required: false + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/feature_request_v3.yml b/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/feature_request_v3.yml new file mode 100644 index 00000000..2fc74ae1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/.github/ISSUE_TEMPLATE/feature_request_v3.yml @@ -0,0 +1,36 @@ +name: Feature Request v3 +description: Suggest a new feature +title: "feature: " +labels: [enhancement v3] +body: + - type: checkboxes + attributes: + label: Did you check the docs? + description: Make sure you read all the docs before submitting a feature request + options: + - label: I have read all the trouble.nvim docs + required: true + - type: textarea + validations: + required: true + attributes: + label: Is your feature request related to a problem? Please describe. + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + - type: textarea + validations: + required: true + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + - type: textarea + validations: + required: true + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + - type: textarea + validations: + required: false + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/trouble.nvim/.github/workflows/ci.yml new file mode 100644 index 00000000..0bc9fb39 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +name: CI +on: + push: + pull_request: + +jobs: + tests: + strategy: + matrix: + # os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Install Neovim + shell: bash + run: | + mkdir -p /tmp/nvim + wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage + cd /tmp/nvim + chmod a+x ./nvim.appimage + ./nvim.appimage --appimage-extract + echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + - name: Run Tests + run: | + nvim --version + [ ! -d tests ] && exit 0 + nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua', sequential = true}" + docs: + runs-on: ubuntu-latest + needs: tests + if: ${{ github.ref == 'refs/heads/main' }} + steps: + - uses: actions/checkout@v3 + - name: panvimdoc + uses: kdheepak/panvimdoc@main + with: + vimdoc: trouble.nvim + version: "Neovim >= 0.8.0" + demojify: true + treesitter: true + - name: Push changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "chore(build): auto-generate vimdoc" + commit_user_name: "github-actions[bot]" + commit_user_email: "github-actions[bot]@users.noreply.github.com" + commit_author: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>" + release: + name: release + if: ${{ github.ref == 'refs/heads/main' }} + needs: + - docs + - tests + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v3 + id: release + with: + release-type: simple + package-name: trouble.nvim + - uses: actions/checkout@v3 + - name: tag stable versions + if: ${{ steps.release.outputs.release_created }} + run: | + git config user.name github-actions[bot] + git config user.email github-actions[bot]@users.noreply.github.com + git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" + git tag -d stable || true + git push origin :stable || true + git tag -a stable -m "Last Stable Release" + git push origin stable diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/.gitignore b/config/neovim/store/lazy-plugins/trouble.nvim/.gitignore new file mode 100644 index 00000000..df756c8e --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/.gitignore @@ -0,0 +1,9 @@ +tt.* +.tests +doc/tags +debug +.repro +foo.* +*.log +data +.DS_Store diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/.lua-format b/config/neovim/store/lazy-plugins/trouble.nvim/.lua-format new file mode 100644 index 00000000..c5e029d9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/.lua-format @@ -0,0 +1,13 @@ +# https://github.com/Koihik/LuaFormatter/blob/master/docs/Style-Config.md +#column_limit: 100 +#indent_width: 4 +#continuation_indent_width: 4 +#use_tab: false +#chop_down_parameter: true +#chop_down_table: true +#chop_down_kv_table: true +#single_quote_to_double_quote: true +#spaces_inside_table_braces: true +#align_parameter: true +#keep_simple_control_block_one_line: true +#extra_sep_at_table_end: true diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/.nvim.lsp.json b/config/neovim/store/lazy-plugins/trouble.nvim/.nvim.lsp.json new file mode 100644 index 00000000..3eb67cdc --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/.nvim.lsp.json @@ -0,0 +1,3 @@ +{ + "Lua.diagnostics.disable": ["undefined-local"] +} diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/CHANGELOG.md b/config/neovim/store/lazy-plugins/trouble.nvim/CHANGELOG.md new file mode 100644 index 00000000..bb94e472 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/CHANGELOG.md @@ -0,0 +1,283 @@ +# Changelog + +## [2.10.0](https://github.com/folke/trouble.nvim/compare/v2.9.1...v2.10.0) (2023-10-18) + + +### Features + +* `open({focus=false})` now works as intended ([600fe24](https://github.com/folke/trouble.nvim/commit/600fe24ad04f130030fa54f0c70949ff084810a3)) + + +### Bug Fixes + +* **auto_open:** dont steal focus on auto open. Fixes [#344](https://github.com/folke/trouble.nvim/issues/344) ([1f00b6f](https://github.com/folke/trouble.nvim/commit/1f00b6f730c5ef6bcfeb829a5659ed3780778087)) + +## [2.9.1](https://github.com/folke/trouble.nvim/compare/v2.9.0...v2.9.1) (2023-10-09) + + +### Bug Fixes + +* **preview:** skip non-existing. Fixes [#87](https://github.com/folke/trouble.nvim/issues/87). Fixes [#188](https://github.com/folke/trouble.nvim/issues/188). Fixes [#336](https://github.com/folke/trouble.nvim/issues/336). ([#338](https://github.com/folke/trouble.nvim/issues/338)) ([5e78824](https://github.com/folke/trouble.nvim/commit/5e7882429ee2e235148ab759a6159950afd8021a)) + +## [2.9.0](https://github.com/folke/trouble.nvim/compare/v2.8.0...v2.9.0) (2023-10-07) + + +### Features + +* Make floating window configuration customizable ([#310](https://github.com/folke/trouble.nvim/issues/310)) ([ef0336a](https://github.com/folke/trouble.nvim/commit/ef0336a818e562439e25638b866cb4638a0fdc26)) + + +### Bug Fixes + +* check that view is valid before render and focus ([#319](https://github.com/folke/trouble.nvim/issues/319)) ([81e1643](https://github.com/folke/trouble.nvim/commit/81e1643a7c6b426535cf23ebdb28baec4ab7428e)) +* only filter msg if sev is hardcoded ([#328](https://github.com/folke/trouble.nvim/issues/328)) ([0ccc43d](https://github.com/folke/trouble.nvim/commit/0ccc43d61e0f9278056a8eeefbe022ce71707a85)) +* **qf:** properly deal with invalid qf entries. Fixes [#87](https://github.com/folke/trouble.nvim/issues/87). Fixes [#188](https://github.com/folke/trouble.nvim/issues/188). Fixes [#336](https://github.com/folke/trouble.nvim/issues/336) ([46b60e9](https://github.com/folke/trouble.nvim/commit/46b60e9fb942d60740c647f61fd779f05e7b9392)) + +## [2.8.0](https://github.com/folke/trouble.nvim/compare/v2.7.0...v2.8.0) (2023-07-25) + + +### Features + +* Create Configuration for IncludeDeclaration ([#312](https://github.com/folke/trouble.nvim/issues/312)) ([7691d93](https://github.com/folke/trouble.nvim/commit/7691d93131be9c4ef7788892a9c52374642beb89)) + +## [2.7.0](https://github.com/folke/trouble.nvim/compare/v2.6.0...v2.7.0) (2023-07-25) + + +### Features + +* Expose help action ([#311](https://github.com/folke/trouble.nvim/issues/311)) ([467dc20](https://github.com/folke/trouble.nvim/commit/467dc204af863a9f11bc3444b8f89af286fbf6b2)) +* Use code descriptions for opening URIs with extra information ([#309](https://github.com/folke/trouble.nvim/issues/309)) ([d2b0f1d](https://github.com/folke/trouble.nvim/commit/d2b0f1de1fe6f013d38234f7557c7935a9f97655)) + +## [2.6.0](https://github.com/folke/trouble.nvim/compare/v2.5.0...v2.6.0) (2023-07-22) + + +### Features + +* make multiline the default ([1f2eb71](https://github.com/folke/trouble.nvim/commit/1f2eb71948b8d08cd8fe0947f9dae95c441baf6d)) + +## [2.5.0](https://github.com/folke/trouble.nvim/compare/v2.4.0...v2.5.0) (2023-07-22) + + +### Features + +* add multiline diagnostic support ([#305](https://github.com/folke/trouble.nvim/issues/305)) ([7a6abd7](https://github.com/folke/trouble.nvim/commit/7a6abd7ed811def9494316d4217d1dcc80b05048)) +* Map double click to jump action ([#158](https://github.com/folke/trouble.nvim/issues/158)) ([ef53b9a](https://github.com/folke/trouble.nvim/commit/ef53b9a1401919a9a3ae5b2949068c456ce23085)) +* use markdown to render hover ([835b87d](https://github.com/folke/trouble.nvim/commit/835b87d93537a3cc403b961c084ca8c2998758cd)) +* **util:** trigger TroubleJump on jump. Closes [#248](https://github.com/folke/trouble.nvim/issues/248) ([d91f3b3](https://github.com/folke/trouble.nvim/commit/d91f3b3d588b0259060780c73dd4c93a8f158f38)) + +## [2.4.0](https://github.com/folke/trouble.nvim/compare/v2.3.0...v2.4.0) (2023-07-16) + + +### Features + +* add option to control cycling of result list ([#302](https://github.com/folke/trouble.nvim/issues/302)) ([e7805dc](https://github.com/folke/trouble.nvim/commit/e7805dc3448f28599e022dc7a0e58060dfdeeb9a)) +* rendering messages from provider ([#304](https://github.com/folke/trouble.nvim/issues/304)) ([a66a78b](https://github.com/folke/trouble.nvim/commit/a66a78b8878780e3b3154e9812ff040ec9b0f1d6)) + + +### Bug Fixes + +* Check parent window is valid before setting active ([#291](https://github.com/folke/trouble.nvim/issues/291)) ([c14786d](https://github.com/folke/trouble.nvim/commit/c14786d5e88f3e66360c70bab56694abd0e60af6)) +* move end of doc pos to last line. Fixes [#151](https://github.com/folke/trouble.nvim/issues/151) ([cb4da04](https://github.com/folke/trouble.nvim/commit/cb4da0401abe7ae6f368bf79d2ed6c2571b1e7ba)) + +## [2.3.0](https://github.com/folke/trouble.nvim/compare/v2.2.3...v2.3.0) (2023-05-25) + + +### Features + +* filter diagnostics by severity level ([#285](https://github.com/folke/trouble.nvim/issues/285)) ([b1f607f](https://github.com/folke/trouble.nvim/commit/b1f607ff0f2c107faf8b0c26d09877028b549d63)) + +## [2.2.3](https://github.com/folke/trouble.nvim/compare/v2.2.2...v2.2.3) (2023-05-22) + + +### Bug Fixes + +* set window options locally ([#282](https://github.com/folke/trouble.nvim/issues/282)) ([a5649c9](https://github.com/folke/trouble.nvim/commit/a5649c9a60d7c5aa2fed1781057af3f29b10f167)) + +## [2.2.2](https://github.com/folke/trouble.nvim/compare/v2.2.1...v2.2.2) (2023-04-17) + + +### Bug Fixes + +* **util:** auto_jump when trouble is open. Fixes [#144](https://github.com/folke/trouble.nvim/issues/144) ([e4f1623](https://github.com/folke/trouble.nvim/commit/e4f1623b51e18eb4e2835446e50886062c339f80)) +* **util:** save position in jump list before jump. Fixes [#143](https://github.com/folke/trouble.nvim/issues/143) Fixes [#235](https://github.com/folke/trouble.nvim/issues/235) ([f0477b0](https://github.com/folke/trouble.nvim/commit/f0477b0e78d9a16ff326e356235876ff3f87882d)) + +## [2.2.1](https://github.com/folke/trouble.nvim/compare/v2.2.0...v2.2.1) (2023-03-26) + + +### Bug Fixes + +* **icons:** fixed deprecated icons with nerdfix ([39db399](https://github.com/folke/trouble.nvim/commit/39db3994c8de87b0b5ca7a4d3d415926f201f1fc)) + +## [2.2.0](https://github.com/folke/trouble.nvim/compare/v2.1.1...v2.2.0) (2023-02-28) + + +### Features + +* enable looping during next/prev ([#232](https://github.com/folke/trouble.nvim/issues/232)) ([fc4c0f8](https://github.com/folke/trouble.nvim/commit/fc4c0f82c9181f3c27a4cbdd5db97c110fd78ee9)) +* expose renderer.signs. Fixes [#252](https://github.com/folke/trouble.nvim/issues/252) ([5581e73](https://github.com/folke/trouble.nvim/commit/5581e736c8afc8b227ad958ded1929c8a39f049e)) + +## [2.1.1](https://github.com/folke/trouble.nvim/compare/v2.1.0...v2.1.1) (2023-02-19) + + +### Bug Fixes + +* ensure that the diagnostic parameters are complete ([#179](https://github.com/folke/trouble.nvim/issues/179)) ([210969f](https://github.com/folke/trouble.nvim/commit/210969fce79e7d11554c61bca263d7e1ac77bde0)) +* icorrect row/line in diagnostics. Fixes [#264](https://github.com/folke/trouble.nvim/issues/264) ([32fa4ed](https://github.com/folke/trouble.nvim/commit/32fa4ed742fc91f3075c98edd3c131b716b9d782)) + +## [2.1.0](https://github.com/folke/trouble.nvim/compare/v2.0.1...v2.1.0) (2023-02-18) + + +### Features + +* expose `require("trouble").is_open()` ([2eb27b3](https://github.com/folke/trouble.nvim/commit/2eb27b34442894e903fdc6e01edea6d7c476be63)) + +## [2.0.1](https://github.com/folke/trouble.nvim/compare/v2.0.0...v2.0.1) (2023-02-16) + + +### Bug Fixes + +* **init:** version check ([73eea32](https://github.com/folke/trouble.nvim/commit/73eea32efec2056cdce7593787390fc9aadf9c0c)) + +## [2.0.0](https://github.com/folke/trouble.nvim/compare/v1.0.2...v2.0.0) (2023-02-16) + + +### ⚠ BREAKING CHANGES + +* Trouble now requires Neovim >= 0.7.2 + +### Features + +* Trouble now requires Neovim >= 0.7.2 ([ef93259](https://github.com/folke/trouble.nvim/commit/ef9325970b341d436f43c50ce876aa0a665d3cf0)) + + +### Bug Fixes + +* Focus parent before closing ([#259](https://github.com/folke/trouble.nvim/issues/259)) ([66b057b](https://github.com/folke/trouble.nvim/commit/66b057b2b07881bceb969624f4c3b5727703c2c8)) +* **preview:** properly load buffer when showing preview ([949199a](https://github.com/folke/trouble.nvim/commit/949199a9ac60ce784a417f90388b8f173ef53819)) +* **util:** properly load a buffer when jumping to it ([bf0eeea](https://github.com/folke/trouble.nvim/commit/bf0eeead88d59d51003f4da1b649b4977ed90e2b)) + + +### Performance Improvements + +* dont load buffers when processing items. Get line with luv instead ([82c9a9a](https://github.com/folke/trouble.nvim/commit/82c9a9a9cd2cd2cdb05e05a3e6538529e2473e14)) + +## [1.0.2](https://github.com/folke/trouble.nvim/compare/v1.0.1...v1.0.2) (2023-02-10) + + +### Bug Fixes + +* **telescope:** properly fix issue with relative filenames in telescope. See [#250](https://github.com/folke/trouble.nvim/issues/250) ([7da0821](https://github.com/folke/trouble.nvim/commit/7da0821d20342751a7eedecd28cf16040146cbf7)) + +## [1.0.1](https://github.com/folke/trouble.nvim/compare/v1.0.0...v1.0.1) (2023-01-23) + + +### Bug Fixes + +* ensure first line is selected when padding is false ([#233](https://github.com/folke/trouble.nvim/issues/233)) ([b2d6ac8](https://github.com/folke/trouble.nvim/commit/b2d6ac8607e1ab612a85c1ec563aaff3a60f0603)) +* **telescope:** correctly use cwd for files. Fixes [#250](https://github.com/folke/trouble.nvim/issues/250) ([3174767](https://github.com/folke/trouble.nvim/commit/3174767c61b3786e65d78f539c60c6f70d26cdbe)) + +## 1.0.0 (2023-01-04) + + +### ⚠ BREAKING CHANGES + +* renamed use_lsp_diagnostic_signs to use_diagnostic_signs +* removed deprecated commands + +### Features + +* added "hover" action that defaults to "K" to show the full multiline text [#11](https://github.com/folke/trouble.nvim/issues/11) ([9111a5e](https://github.com/folke/trouble.nvim/commit/9111a5eb7881a84cd66107077118614e218fba61)) +* added actions for opening in new tab, split and vsplit. Fixes [#36](https://github.com/folke/trouble.nvim/issues/36) ([c94cc59](https://github.com/folke/trouble.nvim/commit/c94cc599badb7086878559653ec705ed68579682)) +* added mapping for jump & close (defaults to "o") [#15](https://github.com/folke/trouble.nvim/issues/15) ([09de784](https://github.com/folke/trouble.nvim/commit/09de78495bad194b2d0d85498a1c1a7996182a71)) +* added support for vim.diagnostics and Neovim 0.7 ([735dcd5](https://github.com/folke/trouble.nvim/commit/735dcd599871179a835d1e0ebd777d4db24c2c72)) +* allow proper passing of plugin options ([79513ed](https://github.com/folke/trouble.nvim/commit/79513ed42a273a1bc80d82c7e1117d3a2e0f2c79)) +* Api to go to first and last items ([#157](https://github.com/folke/trouble.nvim/issues/157)) ([0649811](https://github.com/folke/trouble.nvim/commit/0649811e69a11dea4708a19deee9ab0b1e90313e)) +* better preview and mode ([160fa6c](https://github.com/folke/trouble.nvim/commit/160fa6cb213db6c7a421450b67adc495ae69cef0)) +* command complete ([9923b01](https://github.com/folke/trouble.nvim/commit/9923b01692a238535420d58e440b139a89c3de46)) +* comments to open/toggle workspace or ducument mode directly ([f7db1c2](https://github.com/folke/trouble.nvim/commit/f7db1c29d7eb76cb3310e0aa56a4d546420e7814)) +* config for auto_preview ([0ad97fb](https://github.com/folke/trouble.nvim/commit/0ad97fb67b21579729090214cbb3bce78fd153b7)) +* define multiple keybindings for the same action (better for defaults) ([bf8e8ee](https://github.com/folke/trouble.nvim/commit/bf8e8ee63c38103fb42de0b889810b584e378962)) +* expose items ([#41](https://github.com/folke/trouble.nvim/issues/41)) ([4f84ca4](https://github.com/folke/trouble.nvim/commit/4f84ca4530829b9448c6f13530c26df6d7020fd0)) +* indent lines ([f9e6930](https://github.com/folke/trouble.nvim/commit/f9e6930b5188593b9e6408d8937093d04198e90a)) +* inital version ([980fb07](https://github.com/folke/trouble.nvim/commit/980fb07fd33ea0f72b274e1ad3c8626bf8a14ac9)) +* Lsp implementation ([#50](https://github.com/folke/trouble.nvim/issues/50)) ([069cdae](https://github.com/folke/trouble.nvim/commit/069cdae61d58d2477b150af91692ace636000d47)) +* lsp references, loclist and quickfix lists! ([0b852c8](https://github.com/folke/trouble.nvim/commit/0b852c8418d65191983b2c9b8f90ad6d7f45ff51)) +* made it easier to integrate with trouble ([1dd72c2](https://github.com/folke/trouble.nvim/commit/1dd72c22403519c160b0c694762091971bcf191e)) +* make file grouping and padding configurable ([#66](https://github.com/folke/trouble.nvim/issues/66)) ([ff40475](https://github.com/folke/trouble.nvim/commit/ff40475143ecd40c86f13054935f3afc5653c469)) +* make position of the trouble list configurable (top, bottom, left or right) [#27](https://github.com/folke/trouble.nvim/issues/27) ([0c9ca5e](https://github.com/folke/trouble.nvim/commit/0c9ca5e10c2e5dd8d8479e864e12383b1d614273)) +* make signs configurable ([ff9fd51](https://github.com/folke/trouble.nvim/commit/ff9fd51ab05398c83c2a0b384999d49269d95572)) +* make sorting keys configurable ([#190](https://github.com/folke/trouble.nvim/issues/190)) ([68d3dc5](https://github.com/folke/trouble.nvim/commit/68d3dc52fe49375fe556af69d1e91e0a88b67935)) +* next/previous API. Implements [#44](https://github.com/folke/trouble.nvim/issues/44) ([a2a7dbf](https://github.com/folke/trouble.nvim/commit/a2a7dbfefc5ebdf1a9c1d37e9df1d26a3b13c1cd)) +* option to automatically jump when there is only one result (fixes [#57](https://github.com/folke/trouble.nvim/issues/57)) ([#79](https://github.com/folke/trouble.nvim/issues/79)) ([09fafb2](https://github.com/folke/trouble.nvim/commit/09fafb2e01fbaa4fe6ecede10a7e7a738464deba)) +* **providers.lsp:** Add definitions support ([#20](https://github.com/folke/trouble.nvim/issues/20)) ([a951198](https://github.com/folke/trouble.nvim/commit/a95119893c8dfd4b4bed42da97d601c25c7a495f)) +* sort files by current directory and prefer non-hidden ([ea9a5e3](https://github.com/folke/trouble.nvim/commit/ea9a5e331b70cf4011081c951015033f0079a0cc)) +* sort items by severity / filename / lnum / col ([4a45782](https://github.com/folke/trouble.nvim/commit/4a45782db943f95500b61ffce187bf4cada954ae)) +* sort results by row and column isntead of just row ([#118](https://github.com/folke/trouble.nvim/issues/118)) ([5897b09](https://github.com/folke/trouble.nvim/commit/5897b09933731298382e86a5cf4d1a4861630873)) +* **telescope provider:** (Smart) multiselect ([#39](https://github.com/folke/trouble.nvim/issues/39)) ([45ff198](https://github.com/folke/trouble.nvim/commit/45ff198f4d436d256f02b14db9c817024c7fc85c)) +* Telescope support ([9c81e16](https://github.com/folke/trouble.nvim/commit/9c81e16adec697ffd0b694eb86e14cfee453917d)) +* use vim.notify for logging ([293118e](https://github.com/folke/trouble.nvim/commit/293118e195639c373a6a744621b9341e5e18f6e4)) + + +### Bug Fixes + +* Add nowait option to keymap ([#30](https://github.com/folke/trouble.nvim/issues/30)) ([4375f1f](https://github.com/folke/trouble.nvim/commit/4375f1f0b2457fcbb91d32de457e6e3b3bb7eba7)) +* added additional space between message and code ([aae12e7](https://github.com/folke/trouble.nvim/commit/aae12e7b23b3a2b8337ec5b1d6b7b4317aa3929b)) +* added compatibility to retrieve signs from vim.diagnostic ([dab82ef](https://github.com/folke/trouble.nvim/commit/dab82ef0f39893f50908881fdc5e96bfb1578ba1)) +* added suport for vim.diagnostic hl groups ([d25a8e6](https://github.com/folke/trouble.nvim/commit/d25a8e6779462127fb227397fa92b07bced8a6fe)) +* added support for new handler signatures (backward compatible with 0.5) ([87cae94](https://github.com/folke/trouble.nvim/commit/87cae946aee4798bee621ea6108224c08c218d69)) +* auto_open was broken. Fixed now [#29](https://github.com/folke/trouble.nvim/issues/29) ([a2f2b92](https://github.com/folke/trouble.nvim/commit/a2f2b9248bed41522d8caa3a7e9932981c4087ec)) +* better detection of the parent window ([4c5fd8a](https://github.com/folke/trouble.nvim/commit/4c5fd8abaf6058312ebe52f662ca002bf0aa9f77)) +* default to current window in jump_to_item ([#175](https://github.com/folke/trouble.nvim/issues/175)) ([ec24219](https://github.com/folke/trouble.nvim/commit/ec242197b1f72cabe17dfd61119c896f58bda672)) +* don't "edit" en existing buffer. Use "buffer" instead. ([#5](https://github.com/folke/trouble.nvim/issues/5), [#6](https://github.com/folke/trouble.nvim/issues/6)) ([abef115](https://github.com/folke/trouble.nvim/commit/abef1158c0ff236333f67f9f091e5d9ae67d6a89)) +* don't steal focus on auto_open. Fixes [#48](https://github.com/folke/trouble.nvim/issues/48) ([36b6813](https://github.com/folke/trouble.nvim/commit/36b6813a2103d85b469a61721b030903ddd8b3b3)) +* don't try to fetch sign for "other" ([5b50990](https://github.com/folke/trouble.nvim/commit/5b509904f8865bea7d09b7a686e139077a2484c6)) +* don't use file sorter for items without a valid filename ([20469be](https://github.com/folke/trouble.nvim/commit/20469be985143d024c460d95326ebeff9971d714)) +* dont advance two items at a time. Fixes https://github.com/folke/todo-comments.nvim/issues/39 ([7de8bc4](https://github.com/folke/trouble.nvim/commit/7de8bc46164ec1f787dee34b6843b61251b1ea91)) +* files without col/row should be set to col=1 and row=1 [#22](https://github.com/folke/trouble.nvim/issues/22) ([fcd5f1f](https://github.com/folke/trouble.nvim/commit/fcd5f1fc035ee3d9832c63a307247c09f25c9cd1)) +* filetype set too early ([#230](https://github.com/folke/trouble.nvim/issues/230)) ([c4da921](https://github.com/folke/trouble.nvim/commit/c4da921ba613aa6d6659dc18edc204c37e4b8833)) +* fixed auto_open swicth_to_parent. Fixes [#7](https://github.com/folke/trouble.nvim/issues/7) ([7cf1aa1](https://github.com/folke/trouble.nvim/commit/7cf1aa1195245d3098097bc3a2510dc358c87363)) +* give focus back to correct window when closing ([#72](https://github.com/folke/trouble.nvim/issues/72)) ([a736b8d](https://github.com/folke/trouble.nvim/commit/a736b8db9f49b8b49ac96fbab7f8e396032cfa37)) +* handle normal api calls to trouble as it should [#42](https://github.com/folke/trouble.nvim/issues/42) ([52b875d](https://github.com/folke/trouble.nvim/commit/52b875d1aaf88f32e9f070a0119190c3e65b51a5)) +* if grouping is off, decrease indent ([#140](https://github.com/folke/trouble.nvim/issues/140)) ([ed65f84](https://github.com/folke/trouble.nvim/commit/ed65f84abc4a1e5d8f368d7e02601fc0357ea15e)) +* lazy include telescope when needed ([7e3d4f9](https://github.com/folke/trouble.nvim/commit/7e3d4f9efc157bbfeb3e37837f8ded9289c48f25)) +* lsp diag creates ugly buffers for unopened files in the workspace. Fixed now ([91d1139](https://github.com/folke/trouble.nvim/commit/91d1139d85407b99bd4d2f6850200a793631679b)) +* lsp diagnostics codes ([dbbd523](https://github.com/folke/trouble.nvim/commit/dbbd523d91fe51e8421909147bf069b1ec780720)) +* lsp handler error log ([#95](https://github.com/folke/trouble.nvim/issues/95)) ([063aefd](https://github.com/folke/trouble.nvim/commit/063aefd69a8146e27cde860c9ddd807891e5a119)) +* **lsp:** avoid overwriting uri of result ([#60](https://github.com/folke/trouble.nvim/issues/60)) ([655391c](https://github.com/folke/trouble.nvim/commit/655391c2f592ef61943b6325030333dfacc54757)) +* only use old hl groups when they exist (Fixes [#49](https://github.com/folke/trouble.nvim/issues/49)) ([d4ce76f](https://github.com/folke/trouble.nvim/commit/d4ce76fa82cdbd12dcf9dbfa682dae89b2a143ac)) +* possible vim.NIL on diagnostics code ([1faa347](https://github.com/folke/trouble.nvim/commit/1faa347a93748531b5e418d84276c93da21b86a7)) +* prevent segfault on closing ([756f09d](https://github.com/folke/trouble.nvim/commit/756f09de113a775ab16ba6d26c090616b40a999d)) +* properly close trouble window on close ([d10ee4b](https://github.com/folke/trouble.nvim/commit/d10ee4bc99b8e2bb842c2274316db400b197cca9)) +* properly exit when trouble is the last window. Fixes [#24](https://github.com/folke/trouble.nvim/issues/24) ([2b27b96](https://github.com/folke/trouble.nvim/commit/2b27b96c7893ac534ba0cbfc95d52c6c609a0b20)) +* remove useless "no results" notification ([#164](https://github.com/folke/trouble.nvim/issues/164)) ([da61737](https://github.com/folke/trouble.nvim/commit/da61737d860ddc12f78e638152834487eabf0ee5)), closes [#154](https://github.com/folke/trouble.nvim/issues/154) +* removed space betweend rendering of source + code ([b676029](https://github.com/folke/trouble.nvim/commit/b6760291874d078668f4ff04d78acc0670536ca9)) +* removed unused plenary require. Fixes [#1](https://github.com/folke/trouble.nvim/issues/1) ([1ff45e2](https://github.com/folke/trouble.nvim/commit/1ff45e274de32e816b891b1ca12f73f73b58a604)) +* replace possible newlines in rendered text ([08d068f](https://github.com/folke/trouble.nvim/commit/08d068fb1668b7f898af721cbc8a1ae72ddf6565)) +* restore item indentation ([7c93271](https://github.com/folke/trouble.nvim/commit/7c93271e7a6a147b8f4342f5b377fa863419846f)) +* set buftype before filetype ([#67](https://github.com/folke/trouble.nvim/issues/67)) ([169b2ec](https://github.com/folke/trouble.nvim/commit/169b2ec3a4d0cac01f22cc8f7332f1d0a11f1fa4)) +* set EndOfBuffer to LspTroubleNormal and hide ~ [#23](https://github.com/folke/trouble.nvim/issues/23) ([7d67f34](https://github.com/folke/trouble.nvim/commit/7d67f34d92b3b52ca63c84f929751d98b3f56b63)) +* set nowrap for the trouble window. Fixes [#69](https://github.com/folke/trouble.nvim/issues/69) ([51dd917](https://github.com/folke/trouble.nvim/commit/51dd9175eb506b026189c70f81823dfa77defe86)) +* set the filetype lastly so autocmd's can override options from it ([#126](https://github.com/folke/trouble.nvim/issues/126)) ([b5353dd](https://github.com/folke/trouble.nvim/commit/b5353ddcd09bd7e93d6f934149d25792d455a8fb)) +* show warning when icons=true but devicons is not installed ([7aabea5](https://github.com/folke/trouble.nvim/commit/7aabea5cca2d51ba5432c988fe84ff9d3644637a)) +* support LocationLink ([#94](https://github.com/folke/trouble.nvim/issues/94)) ([7f3761b](https://github.com/folke/trouble.nvim/commit/7f3761b6dbadd682a20bd1ff4cb588985c14c9a0)) +* typos ([#55](https://github.com/folke/trouble.nvim/issues/55)) ([059ea2b](https://github.com/folke/trouble.nvim/commit/059ea2b999171f50019291ee776dd496799fdf3a)) +* use deprecated vim.lsp.diagnostics for now ([afb300f](https://github.com/folke/trouble.nvim/commit/afb300f18c09f7b474783aa12eb680ea59785b46)) +* use new DiagnosticChanged event ([#127](https://github.com/folke/trouble.nvim/issues/127)) ([4d0a711](https://github.com/folke/trouble.nvim/commit/4d0a711e7432eed022611ce385f3a7714e81f63b)), closes [#122](https://github.com/folke/trouble.nvim/issues/122) +* use vim.diagnostic instead of vim.lsp.diagnostic when available ([a2e2e7b](https://github.com/folke/trouble.nvim/commit/a2e2e7b53f389f84477a1a11c086c9a379af702e)) +* workspace and document diagnostics were switched around ([1fa8469](https://github.com/folke/trouble.nvim/commit/1fa84691236d16a2d1c12707c1fbc54060c910f7)) + + +### Performance Improvements + +* debounce auto refresh when diagnostics get updated ([068476d](https://github.com/folke/trouble.nvim/commit/068476db8576e5b32acf20df040e7fca032cd11d)) +* much faster async preview ([2c9b319](https://github.com/folke/trouble.nvim/commit/2c9b3195a7fa8cfc19a368666c9f83fd7a20a482)) +* only fetch line when needed. Fixes [#26](https://github.com/folke/trouble.nvim/issues/26) ([52f18fd](https://github.com/folke/trouble.nvim/commit/52f18fd6bea57af54265247a3ec39f19a31adce3)) +* only update diagnostics once when window changes ([d965d22](https://github.com/folke/trouble.nvim/commit/d965d22ee37e50be0ab32f6a5987a8cd88206f10)) +* prevent nested loading of preview [#2](https://github.com/folke/trouble.nvim/issues/2) ([b20a784](https://github.com/folke/trouble.nvim/commit/b20a7844a035cf6795270db575ad8c4db2a774c9)) +* use vim.lsp.util.get_line to get line instad of bufload ([607b1d5](https://github.com/folke/trouble.nvim/commit/607b1d5bbfdbd19242659415746b5e62f5ddfb94)) + + +### Code Refactoring + +* removed deprecated commands ([dd89ad9](https://github.com/folke/trouble.nvim/commit/dd89ad9ebb63e131098ff04857f8598eb88d8d79)) +* renamed use_lsp_diagnostic_signs to use_diagnostic_signs ([9db77e1](https://github.com/folke/trouble.nvim/commit/9db77e194d848744139673aa246efa00fbcba982)) diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/LICENSE b/config/neovim/store/lazy-plugins/trouble.nvim/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/README.md b/config/neovim/store/lazy-plugins/trouble.nvim/README.md new file mode 100644 index 00000000..aa1e23db --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/README.md @@ -0,0 +1,267 @@ +# 🚦 Trouble v3 Beta! + +❗**Trouble** has been rewritten from scratch. If you'd like to try the new version, +please refer to the [beta docs](https://github.com/folke/trouble.nvim/tree/dev) + +![image](https://github.com/folke/trouble.nvim/assets/292349/481bc1f7-cb93-432d-8ab6-f54044334b96) + + +--- + +# 🚦 Trouble v2 + +A pretty list for showing diagnostics, references, telescope results, quickfix and location lists to help you solve all the trouble your code is causing. + +![LSP Trouble Screenshot](./media/shot.png) + +## ✨ Features + +- pretty list of: + - Diagnostics + - LSP references + - LSP implementations + - LSP definitions + - LSP type definitions + - quickfix list + - location list + - [Telescope](https://github.com/nvim-telescope/telescope.nvim) search results +- automatically updates on new diagnostics +- toggle **diagnostics** mode between **workspace** or **document** +- **interactive preview** in your last accessed window +- _cancel_ preview or _jump_ to the location +- configurable actions, signs, highlights,... + +## ⚡️ Requirements + +- Neovim >= 0.7.2 +- Properly configured Neovim LSP client +- [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) is optional to enable file icons +- a theme with properly configured highlight groups for Neovim Diagnostics +- or install 🌈 [lsp-colors](https://github.com/folke/lsp-colors.nvim) to automatically create the missing highlight groups +- a [patched font](https://www.nerdfonts.com/) for the default severity and fold icons + +## 📦 Installation + +Install the plugin with your preferred package manager: + +### [lazy.nvim](https://github.com/folke/lazy.nvim) + +```lua +return { + "folke/trouble.nvim", + dependencies = { "nvim-tree/nvim-web-devicons" }, + opts = { + -- your configuration comes here + -- or leave it empty to use the default settings + -- refer to the configuration section below + }, +} +``` + +## ⚙️ Configuration + +### Setup + +Trouble comes with the following defaults: + +```lua +{ + position = "bottom", -- position of the list can be: bottom, top, left, right + height = 10, -- height of the trouble list when position is top or bottom + width = 50, -- width of the list when position is left or right + icons = true, -- use devicons for filenames + mode = "workspace_diagnostics", -- "workspace_diagnostics", "document_diagnostics", "quickfix", "lsp_references", "loclist" + severity = nil, -- nil (ALL) or vim.diagnostic.severity.ERROR | WARN | INFO | HINT + fold_open = "", -- icon used for open folds + fold_closed = "", -- icon used for closed folds + group = true, -- group results by file + padding = true, -- add an extra new line on top of the list + cycle_results = true, -- cycle item list when reaching beginning or end of list + action_keys = { -- key mappings for actions in the trouble list + -- map to {} to remove a mapping, for example: + -- close = {}, + close = "q", -- close the list + cancel = "<esc>", -- cancel the preview and get back to your last window / buffer / cursor + refresh = "r", -- manually refresh + jump = { "<cr>", "<tab>", "<2-leftmouse>" }, -- jump to the diagnostic or open / close folds + open_split = { "<c-x>" }, -- open buffer in new split + open_vsplit = { "<c-v>" }, -- open buffer in new vsplit + open_tab = { "<c-t>" }, -- open buffer in new tab + jump_close = {"o"}, -- jump to the diagnostic and close the list + toggle_mode = "m", -- toggle between "workspace" and "document" diagnostics mode + switch_severity = "s", -- switch "diagnostics" severity filter level to HINT / INFO / WARN / ERROR + toggle_preview = "P", -- toggle auto_preview + hover = "K", -- opens a small popup with the full multiline message + preview = "p", -- preview the diagnostic location + open_code_href = "c", -- if present, open a URI with more information about the diagnostic error + close_folds = {"zM", "zm"}, -- close all folds + open_folds = {"zR", "zr"}, -- open all folds + toggle_fold = {"zA", "za"}, -- toggle fold of current file + previous = "k", -- previous item + next = "j" -- next item + help = "?" -- help menu + }, + multiline = true, -- render multi-line messages + indent_lines = true, -- add an indent guide below the fold icons + win_config = { border = "single" }, -- window configuration for floating windows. See |nvim_open_win()|. + auto_open = false, -- automatically open the list when you have diagnostics + auto_close = false, -- automatically close the list when you have no diagnostics + auto_preview = true, -- automatically preview the location of the diagnostic. <esc> to close preview and go back to last window + auto_fold = false, -- automatically fold a file trouble list at creation + auto_jump = {"lsp_definitions"}, -- for the given modes, automatically jump if there is only a single result + include_declaration = { "lsp_references", "lsp_implementations", "lsp_definitions" }, -- for the given modes, include the declaration of the current symbol in the results + signs = { + -- icons / text used for a diagnostic + error = "", + warning = "", + hint = "", + information = "", + other = "", + }, + use_diagnostic_signs = false -- enabling this will use the signs defined in your lsp client +} +``` + +> 💡 if you don't want to use icons or a patched font, you can use the settings below + +```lua +-- settings without a patched font or icons +{ + icons = false, + fold_open = "v", -- icon used for open folds + fold_closed = ">", -- icon used for closed folds + indent_lines = false, -- add an indent guide below the fold icons + signs = { + -- icons / text used for a diagnostic + error = "error", + warning = "warn", + hint = "hint", + information = "info" + }, + use_diagnostic_signs = false -- enabling this will use the signs defined in your lsp client +} +``` + +## 🚀 Usage + +### Commands + +Trouble comes with the following commands: + +- `Trouble [mode]`: open the list +- `TroubleClose [mode]`: close the list +- `TroubleToggle [mode]`: toggle the list +- `TroubleRefresh`: manually refresh the active list + +Modes: + +- **document_diagnostics:** document diagnostics from the builtin LSP client +- **workspace_diagnostics:** workspace diagnostics from the builtin LSP client +- **lsp_references:** references of the word under the cursor from the builtin LSP client +- **lsp_definitions:** definitions of the word under the cursor from the builtin LSP client + +* **lsp_type_definitions:** type definitions of the word under the cursor from the builtin LSP client + +- **quickfix:** [quickfix](https://neovim.io/doc/user/quickfix.html) items +- **loclist:** items from the window's [location list](https://neovim.io/doc/user/quickfix.html) + +Example keybindings: + +```vim +" Vim Script +nnoremap <leader>xx <cmd>TroubleToggle<cr> +nnoremap <leader>xw <cmd>TroubleToggle workspace_diagnostics<cr> +nnoremap <leader>xd <cmd>TroubleToggle document_diagnostics<cr> +nnoremap <leader>xq <cmd>TroubleToggle quickfix<cr> +nnoremap <leader>xl <cmd>TroubleToggle loclist<cr> +nnoremap gR <cmd>TroubleToggle lsp_references<cr> +``` + +```lua +-- Lua +vim.keymap.set("n", "<leader>xx", function() require("trouble").toggle() end) +vim.keymap.set("n", "<leader>xw", function() require("trouble").toggle("workspace_diagnostics") end) +vim.keymap.set("n", "<leader>xd", function() require("trouble").toggle("document_diagnostics") end) +vim.keymap.set("n", "<leader>xq", function() require("trouble").toggle("quickfix") end) +vim.keymap.set("n", "<leader>xl", function() require("trouble").toggle("loclist") end) +vim.keymap.set("n", "gR", function() require("trouble").toggle("lsp_references") end) +``` + +### API + +You can use the following functions in your keybindings: + +```lua +-- toggle trouble with optional mode +require("trouble").toggle(mode?) + +-- open trouble with optional mode +require("trouble").open(mode?) + +-- close trouble +require("trouble").close() + +-- jump to the next item, skipping the groups +require("trouble").next({skip_groups = true, jump = true}); + +-- jump to the previous item, skipping the groups +require("trouble").previous({skip_groups = true, jump = true}); + +-- jump to the first item, skipping the groups +require("trouble").first({skip_groups = true, jump = true}); + +-- jump to the last item, skipping the groups +require("trouble").last({skip_groups = true, jump = true}); +``` + +### Telescope + +You can easily open any search results in **Trouble**, by defining a custom action: + +```lua +local actions = require("telescope.actions") +local trouble = require("trouble.providers.telescope") + +local telescope = require("telescope") + +telescope.setup { + defaults = { + mappings = { + i = { ["<c-t>"] = trouble.open_with_trouble }, + n = { ["<c-t>"] = trouble.open_with_trouble }, + }, + }, +} +``` + +When you open telescope, you can now hit `<c-t>` to open the results in **Trouble** + +## 🎨 Colors + +The table below shows all the highlight groups defined for Trouble. + +| Highlight Group | +| ------------------------ | +| _TroubleCount_ | +| _TroubleError_ | +| _TroubleNormal_ | +| _TroubleTextInformation_ | +| _TroubleSignWarning_ | +| _TroubleLocation_ | +| _TroubleWarning_ | +| _TroublePreview_ | +| _TroubleTextError_ | +| _TroubleSignInformation_ | +| _TroubleIndent_ | +| _TroubleSource_ | +| _TroubleSignHint_ | +| _TroubleSignOther_ | +| _TroubleFoldIcon_ | +| _TroubleTextWarning_ | +| _TroubleCode_ | +| _TroubleInformation_ | +| _TroubleSignError_ | +| _TroubleFile_ | +| _TroubleHint_ | +| _TroubleTextHint_ | +| _TroubleText_ | diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/doc/trouble.nvim.txt b/config/neovim/store/lazy-plugins/trouble.nvim/doc/trouble.nvim.txt new file mode 100644 index 00000000..7689dad7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/doc/trouble.nvim.txt @@ -0,0 +1,305 @@ +*trouble.nvim.txt* For Neovim >= 0.8.0 Last change: 2024 May 19 + +============================================================================== +Table of Contents *trouble.nvim-table-of-contents* + +1. Trouble v3 Beta! |trouble.nvim-trouble-v3-beta!| +2. Trouble v2 |trouble.nvim-trouble-v2| + - Features |trouble.nvim-trouble-v2-features| + - Requirements |trouble.nvim-trouble-v2-requirements| + - Installation |trouble.nvim-trouble-v2-installation| + - Configuration |trouble.nvim-trouble-v2-configuration| + - Usage |trouble.nvim-trouble-v2-usage| + - Colors |trouble.nvim-trouble-v2-colors| +3. Links |trouble.nvim-links| + +============================================================================== +1. Trouble v3 Beta! *trouble.nvim-trouble-v3-beta!* + +**Trouble**has been rewritten from scratch. If you’d like to try the new +version, please refer to the beta docs +<https://github.com/folke/trouble.nvim/tree/dev> + +------------------------------------------------------------------------------ + +============================================================================== +2. Trouble v2 *trouble.nvim-trouble-v2* + +A pretty list for showing diagnostics, references, telescope results, quickfix +and location lists to help you solve all the trouble your code is causing. + + +FEATURES *trouble.nvim-trouble-v2-features* + +- pretty list of: + - Diagnostics + - LSP references + - LSP implementations + - LSP definitions + - LSP type definitions + - quickfix list + - location list + - Telescope <https://github.com/nvim-telescope/telescope.nvim> search results +- automatically updates on new diagnostics +- toggle **diagnostics** mode between **workspace** or **document** +- **interactive preview** in your last accessed window +- _cancel_ preview or _jump_ to the location +- configurable actions, signs, highlights,… + + +REQUIREMENTS *trouble.nvim-trouble-v2-requirements* + +- Neovim >= 0.7.2 +- Properly configured Neovim LSP client +- nvim-web-devicons <https://github.com/nvim-tree/nvim-web-devicons> is optional to enable file icons +- a theme with properly configured highlight groups for Neovim Diagnostics +- or install lsp-colors <https://github.com/folke/lsp-colors.nvim> to automatically create the missing highlight groups +- a patched font <https://www.nerdfonts.com/> for the default severity and fold icons + + +INSTALLATION *trouble.nvim-trouble-v2-installation* + +Install the plugin with your preferred package manager: + + +LAZY.NVIM ~ + +>lua + return { + "folke/trouble.nvim", + dependencies = { "nvim-tree/nvim-web-devicons" }, + opts = { + -- your configuration comes here + -- or leave it empty to use the default settings + -- refer to the configuration section below + }, + } +< + + +CONFIGURATION *trouble.nvim-trouble-v2-configuration* + + +SETUP ~ + +Trouble comes with the following defaults: + +>lua + { + position = "bottom", -- position of the list can be: bottom, top, left, right + height = 10, -- height of the trouble list when position is top or bottom + width = 50, -- width of the list when position is left or right + icons = true, -- use devicons for filenames + mode = "workspace_diagnostics", -- "workspace_diagnostics", "document_diagnostics", "quickfix", "lsp_references", "loclist" + severity = nil, -- nil (ALL) or vim.diagnostic.severity.ERROR | WARN | INFO | HINT + fold_open = "", -- icon used for open folds + fold_closed = "", -- icon used for closed folds + group = true, -- group results by file + padding = true, -- add an extra new line on top of the list + cycle_results = true, -- cycle item list when reaching beginning or end of list + action_keys = { -- key mappings for actions in the trouble list + -- map to {} to remove a mapping, for example: + -- close = {}, + close = "q", -- close the list + cancel = "<esc>", -- cancel the preview and get back to your last window / buffer / cursor + refresh = "r", -- manually refresh + jump = { "<cr>", "<tab>", "<2-leftmouse>" }, -- jump to the diagnostic or open / close folds + open_split = { "<c-x>" }, -- open buffer in new split + open_vsplit = { "<c-v>" }, -- open buffer in new vsplit + open_tab = { "<c-t>" }, -- open buffer in new tab + jump_close = {"o"}, -- jump to the diagnostic and close the list + toggle_mode = "m", -- toggle between "workspace" and "document" diagnostics mode + switch_severity = "s", -- switch "diagnostics" severity filter level to HINT / INFO / WARN / ERROR + toggle_preview = "P", -- toggle auto_preview + hover = "K", -- opens a small popup with the full multiline message + preview = "p", -- preview the diagnostic location + open_code_href = "c", -- if present, open a URI with more information about the diagnostic error + close_folds = {"zM", "zm"}, -- close all folds + open_folds = {"zR", "zr"}, -- open all folds + toggle_fold = {"zA", "za"}, -- toggle fold of current file + previous = "k", -- previous item + next = "j" -- next item + help = "?" -- help menu + }, + multiline = true, -- render multi-line messages + indent_lines = true, -- add an indent guide below the fold icons + win_config = { border = "single" }, -- window configuration for floating windows. See |nvim_open_win()|. + auto_open = false, -- automatically open the list when you have diagnostics + auto_close = false, -- automatically close the list when you have no diagnostics + auto_preview = true, -- automatically preview the location of the diagnostic. <esc> to close preview and go back to last window + auto_fold = false, -- automatically fold a file trouble list at creation + auto_jump = {"lsp_definitions"}, -- for the given modes, automatically jump if there is only a single result + include_declaration = { "lsp_references", "lsp_implementations", "lsp_definitions" }, -- for the given modes, include the declaration of the current symbol in the results + signs = { + -- icons / text used for a diagnostic + error = "", + warning = "", + hint = "", + information = "", + other = "", + }, + use_diagnostic_signs = false -- enabling this will use the signs defined in your lsp client + } +< + + + if you don’t want to use icons or a patched font, you can use the settings + below +>lua + -- settings without a patched font or icons + { + icons = false, + fold_open = "v", -- icon used for open folds + fold_closed = ">", -- icon used for closed folds + indent_lines = false, -- add an indent guide below the fold icons + signs = { + -- icons / text used for a diagnostic + error = "error", + warning = "warn", + hint = "hint", + information = "info" + }, + use_diagnostic_signs = false -- enabling this will use the signs defined in your lsp client + } +< + + +USAGE *trouble.nvim-trouble-v2-usage* + + +COMMANDS ~ + +Trouble comes with the following commands: + +- `Trouble [mode]`open the list +- `TroubleClose [mode]`close the list +- `TroubleToggle [mode]`toggle the list +- `TroubleRefresh`manually refresh the active list + +Modes: + +- **document_diagnostics:** document diagnostics from the builtin LSP client +- **workspace_diagnostics:** workspace diagnostics from the builtin LSP client +- **lsp_references:** references of the word under the cursor from the builtin + LSP client +- **lsp_definitions:** definitions of the word under the cursor from the builtin + LSP client +- **lsp_type_definitions:** type definitions of the word under the cursor from + the builtin LSP client +- **quickfix:** |quickfix| items +- **loclist:** items from the window’s |location list| + +Example keybindings: + +>vim + " Vim Script + nnoremap <leader>xx <cmd>TroubleToggle<cr> + nnoremap <leader>xw <cmd>TroubleToggle workspace_diagnostics<cr> + nnoremap <leader>xd <cmd>TroubleToggle document_diagnostics<cr> + nnoremap <leader>xq <cmd>TroubleToggle quickfix<cr> + nnoremap <leader>xl <cmd>TroubleToggle loclist<cr> + nnoremap gR <cmd>TroubleToggle lsp_references<cr> +< + +>lua + -- Lua + vim.keymap.set("n", "<leader>xx", function() require("trouble").toggle() end) + vim.keymap.set("n", "<leader>xw", function() require("trouble").toggle("workspace_diagnostics") end) + vim.keymap.set("n", "<leader>xd", function() require("trouble").toggle("document_diagnostics") end) + vim.keymap.set("n", "<leader>xq", function() require("trouble").toggle("quickfix") end) + vim.keymap.set("n", "<leader>xl", function() require("trouble").toggle("loclist") end) + vim.keymap.set("n", "gR", function() require("trouble").toggle("lsp_references") end) +< + + +API ~ + +You can use the following functions in your keybindings: + +>lua + -- toggle trouble with optional mode + require("trouble").toggle(mode?) + + -- open trouble with optional mode + require("trouble").open(mode?) + + -- close trouble + require("trouble").close() + + -- jump to the next item, skipping the groups + require("trouble").next({skip_groups = true, jump = true}); + + -- jump to the previous item, skipping the groups + require("trouble").previous({skip_groups = true, jump = true}); + + -- jump to the first item, skipping the groups + require("trouble").first({skip_groups = true, jump = true}); + + -- jump to the last item, skipping the groups + require("trouble").last({skip_groups = true, jump = true}); +< + + +TELESCOPE ~ + +You can easily open any search results in **Trouble**, by defining a custom +action: + +>lua + local actions = require("telescope.actions") + local trouble = require("trouble.providers.telescope") + + local telescope = require("telescope") + + telescope.setup { + defaults = { + mappings = { + i = { ["<c-t>"] = trouble.open_with_trouble }, + n = { ["<c-t>"] = trouble.open_with_trouble }, + }, + }, + } +< + +When you open telescope, you can now hit `<c-t>` to open the results in +**Trouble** + + +COLORS *trouble.nvim-trouble-v2-colors* + +The table below shows all the highlight groups defined for Trouble. + + Highlight Group + ------------------------ + TroubleCount + TroubleError + TroubleNormal + TroubleTextInformation + TroubleSignWarning + TroubleLocation + TroubleWarning + TroublePreview + TroubleTextError + TroubleSignInformation + TroubleIndent + TroubleSource + TroubleSignHint + TroubleSignOther + TroubleFoldIcon + TroubleTextWarning + TroubleCode + TroubleInformation + TroubleSignError + TroubleFile + TroubleHint + TroubleTextHint + TroubleText +============================================================================== +3. Links *trouble.nvim-links* + +1. *image*: https://github.com/folke/trouble.nvim/assets/292349/481bc1f7-cb93-432d-8ab6-f54044334b96 +2. *LSP Trouble Screenshot*: ./media/shot.png + +Generated by panvimdoc <https://github.com/kdheepak/panvimdoc> + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/colors.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/colors.lua new file mode 100644 index 00000000..9623fa82 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/colors.lua @@ -0,0 +1,34 @@ +local util = require("trouble.util") + +local M = {} + +local links = { + TextError = "TroubleText", + TextWarning = "TroubleText", + TextInformation = "TroubleText", + TextHint = "TroubleText", + Text = "Normal", + File = "Directory", + Source = "Comment", + Code = "Comment", + Location = "LineNr", + FoldIcon = "CursorLineNr", + Normal = "Normal", + Count = "TabLineSel", + Preview = "Search", + Indent = "LineNr", + SignOther = "TroubleSignInformation", +} + +function M.setup() + for k, v in pairs(links) do + vim.api.nvim_command("hi def link Trouble" .. k .. " " .. v) + end + + for _, severity in pairs(util.severity) do + vim.api.nvim_command("hi def link Trouble" .. severity .. " " .. util.get_severity_label(severity)) + vim.api.nvim_command("hi def link TroubleSign" .. severity .. " " .. util.get_severity_label(severity, "Sign")) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/config.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/config.lua new file mode 100644 index 00000000..6402263a --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/config.lua @@ -0,0 +1,83 @@ +local M = {} + +M.namespace = vim.api.nvim_create_namespace("Trouble") + +---@class TroubleOptions +---@field buf number|nil +---@field win number|nil +---@field severity lsp.DiagnosticSeverity|nil +-- TODO: make some options configurable per mode +-- TODO: make it possible to have multiple trouble lists open at the same time +local defaults = { + debug = false, + cmd_options = {}, + group = true, -- group results by file + padding = true, -- add an extra new line on top of the list + position = "bottom", -- position of the list can be: bottom, top, left, right + height = 10, -- height of the trouble list when position is top or bottom + width = 50, -- width of the list when position is left or right + icons = true, -- use devicons for filenames + mode = "workspace_diagnostics", -- "workspace_diagnostics", "document_diagnostics", "quickfix", "lsp_references", "loclist" + severity = nil, -- nil (ALL) or vim.diagnostic.severity.ERROR | WARN | INFO | HINT + fold_open = "", -- icon used for open folds + fold_closed = "", -- icon used for closed folds + cycle_results = true, -- cycle item list when reaching beginning or end of list + action_keys = { -- key mappings for actions in the trouble list + close = "q", -- close the list + cancel = "<esc>", -- cancel the preview and get back to your last window / buffer / cursor + refresh = "r", -- manually refresh + jump = { "<cr>", "<tab>", "<2-leftmouse>" }, -- jump to the diagnostic or open / close folds + open_split = { "<c-x>" }, -- open buffer in new split + open_vsplit = { "<c-v>" }, -- open buffer in new vsplit + open_tab = { "<c-t>" }, -- open buffer in new tab + jump_close = { "o" }, -- jump to the diagnostic and close the list + toggle_mode = "m", -- toggle between "workspace" and "document" mode + switch_severity = "s", -- switch "diagnostics" severity filter level to ALL / HINT / INFO / WARN / ERROR + toggle_preview = "P", -- toggle auto_preview + hover = "K", -- opens a small popup with the full multiline message + preview = "p", -- preview the diagnostic location + open_code_href = "c", -- if present, open a URI with more information about the diagnostic error + close_folds = { "zM", "zm" }, -- close all folds + open_folds = { "zR", "zr" }, -- open all folds + toggle_fold = { "zA", "za" }, -- toggle fold of current file + previous = "k", -- preview item + next = "j", -- next item + help = "?", -- help menu + }, + multiline = true, -- render multi-line messages + indent_lines = true, -- add an indent guide below the fold icons + win_config = { border = "single" }, -- window configuration for floating windows. See |nvim_open_win()|. + auto_open = false, -- automatically open the list when you have diagnostics + auto_close = false, -- automatically close the list when you have no diagnostics + auto_preview = true, -- automatically preview the location of the diagnostic. <esc> to close preview and go back to last window + auto_fold = false, -- automatically fold a file trouble list at creation + auto_jump = { "lsp_definitions" }, -- for the given modes, automatically jump if there is only a single result + include_declaration = { "lsp_references", "lsp_implementations", "lsp_definitions" }, -- for the given modes, include the declaration of the current symbol in the results + signs = { + -- icons / text used for a diagnostic + error = "", + warning = "", + hint = "", + information = "", + other = "", + }, + use_diagnostic_signs = false, -- enabling this will use the signs defined in your lsp client + sort_keys = { + "severity", + "filename", + "lnum", + "col", + }, +} + +---@type TroubleOptions +M.options = {} +---@return TroubleOptions + +function M.setup(options) + M.options = vim.tbl_deep_extend("force", {}, defaults, options or {}) +end + +M.setup() + +return M diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/folds.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/folds.lua new file mode 100644 index 00000000..e0c8f442 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/folds.lua @@ -0,0 +1,24 @@ +local config = require("trouble.config") + +local M = {} + +M.folded = {} + +function M.is_folded(filename) + local fold = M.folded[filename] + return (fold == nil and config.options.auto_fold == true) or (fold == true) +end + +function M.toggle(filename) + M.folded[filename] = not M.is_folded(filename) +end + +function M.close(filename) + M.folded[filename] = true +end + +function M.open(filename) + M.folded[filename] = false +end + +return M diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/init.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/init.lua new file mode 100644 index 00000000..31abfe5d --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/init.lua @@ -0,0 +1,300 @@ +local View = require("trouble.view") +local config = require("trouble.config") +local colors = require("trouble.colors") +local util = require("trouble.util") + +colors.setup() + +local Trouble = {} + +local view + +function Trouble.is_open() + return view and view:is_valid() or false +end + +function Trouble.setup(options) + if vim.fn.has("nvim-0.7.2") == 0 then + util.error("Trouble needs Neovim >= 0.7.2") + return + end + config.setup(options) + util.fix_mode(config.options) + colors.setup() +end + +function Trouble.close() + if Trouble.is_open() then + view:close() + end +end + +local function get_opts(...) + local args = { ... } + if (vim.islist or vim.tbl_islist)(args) and #args == 1 and type(args[1]) == "table" then + args = args[1] + end + local opts = {} + for key, value in pairs(args) do + if type(key) == "number" then + local k, v = value:match("^(.*)=(.*)$") + if k then + opts[k] = v + elseif opts.mode then + util.error("unknown option " .. value) + else + opts.mode = value + end + else + opts[key] = value + end + end + opts = opts or {} + util.fix_mode(opts) + config.options.cmd_options = opts + return opts +end + +function Trouble.open(...) + local opts = get_opts(...) + if opts.mode and (opts.mode ~= config.options.mode) then + config.options.mode = opts.mode + end + + if opts.severity and (opts.severity ~= config.options.severity) then + config.options.severity = opts.severity + end + + opts.focus = (opts.focus == nil and not opts.auto) and true or opts.focus + opts.on_open = true + + if Trouble.is_open() then + Trouble.refresh(opts) + elseif not opts.auto and vim.tbl_contains(config.options.auto_jump, opts.mode) then + require("trouble.providers").get(vim.api.nvim_get_current_win(), vim.api.nvim_get_current_buf(), function(results) + if #results == 1 then + util.jump_to_item(opts.win, opts.precmd, results[1]) + elseif #results > 0 then + view = View.create(opts) + end + end, config.options) + else + view = View.create(opts) + end +end + +function Trouble.toggle(...) + local opts = get_opts(...) + + if opts.mode and (opts.mode ~= config.options.mode) then + config.options.mode = opts.mode + Trouble.open(...) + return + end + + if Trouble.is_open() then + Trouble.close() + else + Trouble.open(...) + end +end + +function Trouble.help() + local lines = { "# Key Bindings" } + local height = 1 + for command, key in pairs(config.options.action_keys) do + if type(key) == "table" then + key = table.concat(key, " | ") + end + table.insert(lines, " * **" .. key .. "** " .. command:gsub("_", " ")) + height = height + 1 + end + -- help + vim.lsp.util.open_floating_preview(lines, "markdown", config.options.win_config) +end + +local updater = util.debounce(100, function() + -- buff might have been closed during the debounce + if not Trouble.is_open() then + util.debug("refresh: not open anymore") + return + end + + util.debug("refresh: auto") + view:update({ auto = true }) +end) + +function Trouble.refresh(opts) + opts = opts or {} + + -- dont do an update if this is an automated refresh from a different provider + if opts.auto then + if opts.provider == "diagnostics" and config.options.mode == "document_diagnostics" then + opts.provider = "document_diagnostics" + elseif opts.provider == "diagnostics" and config.options.mode == "workspace_diagnostics" then + opts.provider = "workspace_diagnostics" + elseif opts.provider == "qf" and config.options.mode == "quickfix" then + opts.provider = "quickfix" + elseif opts.provider == "qf" and config.options.mode == "loclist" then + opts.provider = "loclist" + end + if opts.provider ~= config.options.mode then + return + end + end + + if Trouble.is_open() then + if opts.auto then + updater() + else + util.debug("refresh") + view:update(opts) + end + elseif opts.auto and config.options.auto_open and opts.provider == config.options.mode then + require("trouble.providers").get(vim.api.nvim_get_current_win(), vim.api.nvim_get_current_buf(), function(results) + if #results > 0 then + Trouble.open(opts) + end + end, config.options) + end +end + +function Trouble.action(action) + if action == "toggle_mode" then + if config.options.mode == "document_diagnostics" then + config.options.mode = "workspace_diagnostics" + elseif config.options.mode == "workspace_diagnostics" then + config.options.mode = "document_diagnostics" + end + action = "refresh" + end + + if action == "switch_severity" then + if config.options.severity == nil then + config.options.severity = vim.diagnostic.severity.ERROR + elseif config.options.severity < 4 then + config.options.severity = config.options.severity + 1 + else + config.options.severity = nil + end + action = "refresh" + end + + if view and action == "on_win_enter" then + view:on_win_enter() + end + if not Trouble.is_open() then + return Trouble + end + if action == "hover" then + view:hover() + end + if action == "jump" then + view:jump() + elseif action == "open_split" then + view:jump({ precmd = "split" }) + elseif action == "open_vsplit" then + view:jump({ precmd = "vsplit" }) + elseif action == "open_tab" then + view:jump({ precmd = "tabe" }) + end + if action == "jump_close" then + view:jump() + Trouble.close() + end + if action == "open_folds" then + Trouble.refresh({ open_folds = true }) + end + if action == "close_folds" then + Trouble.refresh({ close_folds = true }) + end + if action == "toggle_fold" then + view:toggle_fold() + end + if action == "on_enter" then + view:on_enter() + end + if action == "on_leave" then + view:on_leave() + end + if action == "cancel" then + view:switch_to_parent() + end + if action == "next" then + view:next_item() + return Trouble + end + if action == "previous" then + view:previous_item() + return Trouble + end + if action == "first" then + view:first_item() + return Trouble + end + if action == "last" then + view:last_item() + return Trouble + end + + if action == "toggle_preview" then + config.options.auto_preview = not config.options.auto_preview + if not config.options.auto_preview then + view:close_preview() + else + action = "preview" + end + end + if action == "auto_preview" and config.options.auto_preview then + action = "preview" + end + if action == "preview" then + view:preview() + end + if action == "open_code_href" then + view:open_code_href() + end + + if Trouble[action] then + Trouble[action]() + end + return Trouble +end + +function Trouble.next(opts) + util.fix_mode(opts) + if view then + view:next_item(opts) + end +end + +function Trouble.previous(opts) + util.fix_mode(opts) + if view then + view:previous_item(opts) + end +end + +function Trouble.first(opts) + util.fix_mode(opts) + if view then + view:first_item(opts) + end +end + +function Trouble.last(opts) + util.fix_mode(opts) + if view then + view:last_item(opts) + end +end + +function Trouble.get_items() + if view ~= nil then + return view.items + else + return {} + end +end + +return Trouble diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/diagnostic.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/diagnostic.lua new file mode 100644 index 00000000..3a917d17 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/diagnostic.lua @@ -0,0 +1,59 @@ +local util = require("trouble.util") + +---@class Lsp +local M = {} + +local severity = { + [1] = "ERROR", + [2] = "WARN", + [3] = "INFO", + [4] = "HINT", +} + +---@param options TroubleOptions +---@return Item[] +function M.diagnostics(_, buf, cb, options) + if options.mode == "workspace_diagnostics" then + buf = nil + end + + local items = {} + + if vim.diagnostic then + local diags = vim.diagnostic.get(buf, { severity = options.severity }) + for _, item in ipairs(diags) do + table.insert(items, util.process_item(item)) + end + else + ---@diagnostic disable-next-line: deprecated + local diags = buf and { [buf] = vim.lsp.diagnostic.get(buf) } or vim.lsp.diagnostic.get_all() + items = util.locations_to_items(diags, 1) + end + + local messages = {} + if severity[options.severity] then + table.insert(messages, { text = "filter:", group = "Information" }) + table.insert(messages, { text = severity[options.severity], group = "Sign" .. util.severity[options.severity] }) + end + + cb(items, messages) +end + +function M.get_signs() + local signs = {} + for _, v in pairs(util.severity) do + if v ~= "Other" then + -- pcall to catch entirely unbound or cleared out sign hl group + local status, sign = pcall(function() + return vim.trim(vim.fn.sign_getdefined(util.get_severity_label(v, "Sign"))[1].text) + end) + if not status then + sign = v:sub(1, 1) + end + signs[string.lower(v)] = sign + end + end + return signs +end + +return M diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/init.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/init.lua new file mode 100644 index 00000000..88d80b61 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/init.lua @@ -0,0 +1,94 @@ +local util = require("trouble.util") +local qf = require("trouble.providers.qf") +local telescope = require("trouble.providers.telescope") +local lsp = require("trouble.providers.lsp") +local diagnostic = require("trouble.providers.diagnostic") + +local M = {} + +M.providers = { + workspace_diagnostics = diagnostic.diagnostics, + document_diagnostics = diagnostic.diagnostics, + lsp_references = lsp.references, + lsp_implementations = lsp.implementations, + lsp_definitions = lsp.definitions, + lsp_type_definitions = lsp.type_definitions, + quickfix = qf.qflist, + loclist = qf.loclist, + telescope = telescope.telescope, +} + +---@param options TroubleOptions +function M.get(win, buf, cb, options) + local name = options.mode + local provider = M.providers[name] + + if not provider then + local ok, mod = pcall(require, "trouble.providers." .. name) + if ok then + M.providers[name] = mod + provider = mod + end + end + + if not provider then + util.error(("invalid provider %q"):format(name)) + return {} + end + + local sort_keys = vim.list_extend({ + function(item) + local cwd = vim.loop.fs_realpath(vim.fn.getcwd()) + local path = vim.loop.fs_realpath(item.filename) + if not path then + return 200 + end + local ret = string.find(path, cwd, 1, true) == 1 and 10 or 100 + -- prefer non-hidden files + if string.find(path, ".") then + ret = ret + 1 + end + return ret + end, + }, options.sort_keys) + + provider(win, buf, function(items, messages) + table.sort(items, function(a, b) + for _, key in ipairs(sort_keys) do + local ak = type(key) == "string" and a[key] or key(a) + local bk = type(key) == "string" and b[key] or key(b) + if ak ~= bk then + return ak < bk + end + end + end) + cb(items, messages) + end, options) +end + +---@param items Item[] +---@return table<string, Item[]> +function M.group(items) + local keys = {} + local keyid = 0 + local groups = {} + for _, item in ipairs(items) do + if groups[item.filename] == nil then + groups[item.filename] = { filename = item.filename, items = {} } + keys[item.filename] = keyid + keyid = keyid + 1 + end + table.insert(groups[item.filename].items, item) + end + + local ret = {} + for _, group in pairs(groups) do + table.insert(ret, group) + end + table.sort(ret, function(a, b) + return keys[a.filename] < keys[b.filename] + end) + return ret +end + +return M diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/lsp.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/lsp.lua new file mode 100644 index 00000000..536299ea --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/lsp.lua @@ -0,0 +1,93 @@ +local lsp = require("vim.lsp") +local util = require("trouble.util") + +---@class Lsp +local M = {} + +local function lsp_buf_request(buf, method, params, handler) + lsp.buf_request(buf, method, params, function(err, m, result) + handler(err, method == m and result or m) + end) +end + +---@return Item[] +function M.references(win, buf, cb, options) + local method = "textDocument/references" + local params = util.make_position_params(win, buf) + params.context = { includeDeclaration = vim.tbl_contains(options.include_declaration, options.mode) } + + lsp_buf_request(buf, method, params, function(err, result) + if err then + util.error("an error happened getting references: " .. err.message) + return cb({}) + end + if result == nil or #result == 0 then + return cb({}) + end + local ret = util.locations_to_items({ result }, 0) + cb(ret) + end) +end + +---@return Item[] +function M.implementations(win, buf, cb, options) + local method = "textDocument/implementation" + local params = util.make_position_params(win, buf) + params.context = { includeDeclaration = vim.tbl_contains(options.include_declaration, options.mode) } + lsp_buf_request(buf, method, params, function(err, result) + if err then + util.error("an error happened getting implementation: " .. err.message) + return cb({}) + end + if result == nil or #result == 0 then + return cb({}) + end + local ret = util.locations_to_items({ result }, 0) + cb(ret) + end) +end + +---@return Item[] +function M.definitions(win, buf, cb, options) + local method = "textDocument/definition" + local params = util.make_position_params(win, buf) + params.context = { includeDeclaration = vim.tbl_contains(options.include_declaration, options.mode) } + lsp_buf_request(buf, method, params, function(err, result) + if err then + util.error("an error happened getting definitions: " .. err.message) + return cb({}) + end + if result == nil or #result == 0 then + return cb({}) + end + for _, value in ipairs(result) do + value.uri = value.targetUri or value.uri + value.range = value.targetSelectionRange or value.range + end + local ret = util.locations_to_items({ result }, 0) + cb(ret) + end) +end + +---@return Item[] +function M.type_definitions(win, buf, cb, _options) + local method = "textDocument/typeDefinition" + local params = util.make_position_params(win, buf) + lsp_buf_request(buf, method, params, function(err, result) + if err then + util.error("an error happened getting type definitions: " .. err.message) + return cb({}) + end + if result == nil or #result == 0 then + return cb({}) + end + for _, value in ipairs(result) do + value.uri = value.targetUri or value.uri + value.range = value.targetSelectionRange or value.range + end + local ret = util.locations_to_items({ result }, 0) + cb(ret) + end) +end + +return M diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/qf.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/qf.lua new file mode 100644 index 00000000..3e3b6235 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/qf.lua @@ -0,0 +1,47 @@ +local util = require("trouble.util") + +local M = {} + +local severities = { E = 1, W = 2, I = 3, H = 4 } + +function M.get_list(winid) + local list = winid == nil and vim.fn.getqflist({ all = true }) or vim.fn.getloclist(winid, { all = true }) + + local ret = {} + for _, item in pairs(list.items) do + local row = (item.lnum == 0 and 1 or item.lnum) - 1 + local col = (item.col == 0 and 1 or item.col) - 1 + + if item.valid == 1 then + ret[#ret + 1] = { + row = row, + col = col, + message = item.text, + severity = severities[item.type] or 0, + bufnr = item.bufnr, + range = { + start = { line = row, character = col }, + ["end"] = { line = row, character = -1 }, + }, + } + elseif #ret > 0 then + ret[#ret].message = ret[#ret].message .. "\n" .. item.text + end + end + + for i, item in ipairs(ret) do + ret[i] = util.process_item(item) + end + + return ret +end + +function M.loclist(win, _buf, cb, _options) + return cb(M.get_list(win)) +end + +function M.qflist(_win, _buf, cb, _options) + return cb(M.get_list()) +end + +return M diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/telescope.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/telescope.lua new file mode 100644 index 00000000..d6721486 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/providers/telescope.lua @@ -0,0 +1,86 @@ +local util = require("trouble.util") + +local M = {} + +M.results = {} + +--- Turns a Telescope item into a Trouble item. +local function item_to_result(item) + local row = (item.lnum or 1) - 1 + local col = (item.col or 1) - 1 + + if not item.bufnr then + local fname = vim.fn.fnamemodify(item.filename, ":p") + if vim.fn.filereadable(fname) == 0 and item.cwd then + fname = vim.fn.fnamemodify(item.cwd .. "/" .. item.filename, ":p") + end + item.bufnr = vim.fn.bufnr(fname, true) + end + + local pitem = { + row = row, + col = col, + message = item.text, + severity = 0, + range = { + start = { line = row, character = col }, + ["end"] = { line = row, character = -1 }, + }, + } + + return util.process_item(pitem, item.bufnr) +end + +--- Shows all Telescope results in Trouble. +function M.open_with_trouble(prompt_bufnr, _mode) + local action_state = require("telescope.actions.state") + local actions = require("telescope.actions") + local picker = action_state.get_current_picker(prompt_bufnr) + local manager = picker.manager + + M.results = {} + for item in manager:iter() do + table.insert(M.results, item_to_result(item)) + end + + actions.close(prompt_bufnr) + require("trouble").open("telescope") +end + +--- Shows the selected Telescope results in Trouble. +function M.open_selected_with_trouble(prompt_bufnr, _mode) + local action_state = require("telescope.actions.state") + local actions = require("telescope.actions") + local picker = action_state.get_current_picker(prompt_bufnr) + + M.results = {} + for _, item in ipairs(picker:get_multi_selection()) do + table.insert(M.results, item_to_result(item)) + end + + actions.close(prompt_bufnr) + require("trouble").open("telescope") +end + +--- Shows the selected Telescope results in Trouble. +--- If no results are currently selected, shows all of them. +function M.smart_open_with_trouble(prompt_bufnr, _mode) + local action_state = require("telescope.actions.state") + local picker = action_state.get_current_picker(prompt_bufnr) + if #picker:get_multi_selection() > 0 then + M.open_selected_with_trouble(prompt_bufnr, _mode) + else + M.open_with_trouble(prompt_bufnr, _mode) + end +end + +function M.telescope(_win, _buf, cb, _options) + if #M.results == 0 then + util.warn( + "No Telescope results found. Open Telescope and send results to Trouble first. Refer to the documentation for more info." + ) + end + cb(M.results) +end + +return M diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/renderer.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/renderer.lua new file mode 100644 index 00000000..7786bf0f --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/renderer.lua @@ -0,0 +1,173 @@ +local providers = require("trouble.providers") +local util = require("trouble.util") +local config = require("trouble.config") +local Text = require("trouble.text") +local folds = require("trouble.folds") + +---@class Renderer +local renderer = {} + +renderer.signs = {} + +local function get_icon(file) + local ok, icons = pcall(require, "nvim-web-devicons") + if not ok then + util.warn( + "'nvim-web-devicons' is not installed. Install it, or set icons=false in your configuration to disable this message" + ) + return "" + end + local fname = vim.fn.fnamemodify(file, ":t") + local ext = vim.fn.fnamemodify(file, ":e") + return icons.get_icon(fname, ext, { default = true }) +end + +local function update_signs() + renderer.signs = config.options.signs + if config.options.use_diagnostic_signs then + local lsp_signs = require("trouble.providers.diagnostic").get_signs() + renderer.signs = vim.tbl_deep_extend("force", {}, renderer.signs, lsp_signs) + end +end + +---@param view TroubleView +function renderer.render(view, opts) + opts = opts or {} + local buf = vim.api.nvim_win_get_buf(view.parent) + providers.get(view.parent, buf, function(items, messages) + local auto_jump = vim.tbl_contains(config.options.auto_jump, opts.mode) + if opts.on_open and #items == 1 and auto_jump and not opts.auto then + view:close() + util.jump_to_item(opts.win, opts.precmd, items[1]) + return + end + + local grouped = providers.group(items) + local count = util.count(grouped) + + -- check for auto close + if opts.auto and config.options.auto_close then + if count == 0 then + view:close() + return + end + end + + -- Update lsp signs + update_signs() + + local text = Text:new() + view.items = {} + + if config.options.padding then + if messages ~= nil then + for _, msg in ipairs(messages) do + text:render(" " .. msg.text, msg.group, { append = " " }) + end + end + text:nl() + end + + -- render file groups + for _, group in ipairs(grouped) do + if opts.open_folds then + folds.open(group.filename) + end + if opts.close_folds then + folds.close(group.filename) + end + renderer.render_file(view, text, group.filename, group.items) + end + + view:render(text) + if opts.focus then + view:focus() + end + end, config.options) +end + +---@param view TroubleView +---@param text Text +---@param items Item[] +---@param filename string +function renderer.render_file(view, text, filename, items) + view.items[text.lineNr + 1] = { filename = filename, is_file = true } + + if view.group == true then + local count = util.count(items) + + text:render(" ") + + if folds.is_folded(filename) then + text:render(config.options.fold_closed, "FoldIcon", " ") + else + text:render(config.options.fold_open, "FoldIcon", " ") + end + + if config.options.icons then + local icon, icon_hl = get_icon(filename) + text:render(icon, icon_hl, { exact = true, append = " " }) + end + + text:render(vim.fn.fnamemodify(filename, ":p:."), "File", " ") + text:render(" " .. count .. " ", "Count") + text:nl() + end + + if not folds.is_folded(filename) then + renderer.render_diagnostics(view, text, items) + end +end + +---@param view TroubleView +---@param text Text +---@param items Item[] +function renderer.render_diagnostics(view, text, items) + for _, diag in ipairs(items) do + view.items[text.lineNr + 1] = diag + + local sign = diag.sign or renderer.signs[string.lower(diag.type)] + if not sign then + sign = diag.type + end + + local indent = " " + if config.options.indent_lines then + indent = " │ " + elseif config.options.group == false then + indent = " " + end + + local sign_hl = diag.sign_hl or ("TroubleSign" .. diag.type) + + text:render(indent, "Indent") + text:render(sign .. " ", sign_hl, { exact = true }) + + local lines = config.options.multiline and vim.split(diag.full_text, "\n") or { diag.text } + + text:render(lines[1], "Text" .. diag.type, " ") + + if diag.source then + text:render(diag.source, "Source") + end + if diag.code and diag.code ~= vim.NIL then + text:render(" (" .. diag.code .. ")", "Code") + end + + text:render(" ") + + text:render("[" .. diag.lnum .. ", " .. diag.col .. "]", "Location") + + for l = 2, #lines do + local str = lines[l] + text:nl() + view.items[text.lineNr + 1] = diag + text:render(indent .. " ", "Indent") + text:render(str, "Text" .. diag.type, " ") + end + + text:nl() + end +end + +return renderer diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/text.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/text.lua new file mode 100644 index 00000000..d3595096 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/text.lua @@ -0,0 +1,52 @@ +---@class Text +---@field lines string[] +---@field hl Highlight[] +---@field lineNr number +---@field current string +local Text = {} +Text.__index = Text + +function Text:new() + local this = { lines = {}, hl = {}, lineNr = 0, current = "" } + setmetatable(this, self) + return this +end + +function Text:nl() + table.insert(self.lines, self.current) + self.current = "" + self.lineNr = self.lineNr + 1 +end + +function Text:render(str, group, opts) + str = str:gsub("[\n]", " ") + if type(opts) == "string" then + opts = { append = opts } + end + opts = opts or {} + + if group then + if opts.exact ~= true then + group = "Trouble" .. group + end + local from = string.len(self.current) + ---@class Highlight + local hl + hl = { + line = self.lineNr, + from = from, + to = from + string.len(str), + group = group, + } + table.insert(self.hl, hl) + end + self.current = self.current .. str + if opts.append then + self.current = self.current .. opts.append + end + if opts.nl then + self:nl() + end +end + +return Text diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/util.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/util.lua new file mode 100644 index 00000000..62fe12e4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/util.lua @@ -0,0 +1,242 @@ +local config = require("trouble.config") +local uv = vim.loop + +local M = {} + +function M.jump_to_item(win, precmd, item) + -- requiring here, as otherwise we run into a circular dependency + local View = require("trouble.view") + + -- save position in jump list + vim.cmd("normal! m'") + + View.switch_to(win) + if precmd then + vim.cmd(precmd) + end + if not vim.bo[item.bufnr].buflisted then + vim.bo[item.bufnr].buflisted = true + end + if not vim.api.nvim_buf_is_loaded(item.bufnr) then + vim.fn.bufload(item.bufnr) + end + vim.api.nvim_set_current_buf(item.bufnr) + vim.api.nvim_win_set_cursor(win or 0, { item.start.line + 1, item.start.character }) + vim.api.nvim_exec_autocmds("User", { pattern = "TroubleJump", modeline = false }) +end + +function M.fix_mode(opts) + if opts.use_lsp_diagnostic_signs then + opts.use_diagnostic_signs = opts.use_lsp_diagnostic_signs + M.warn("The Trouble option use_lsp_diagnostic_signs has been renamed to use_diagnostic_signs") + end + local replace = { + lsp_workspace_diagnostics = "workspace_diagnostics", + lsp_document_diagnostics = "document_diagnostics", + workspace = "workspace_diagnostics", + document = "document_diagnostics", + } + + for old, new in pairs(replace) do + if opts.mode == old then + opts.mode = new + M.warn("Using " .. old .. " for Trouble is deprecated. Please use " .. new .. " instead.") + end + end +end + +---@return number +function M.count(tab) + local count = 0 + for _ in pairs(tab) do + count = count + 1 + end + return count +end + +function M.warn(msg) + vim.notify(msg, vim.log.levels.WARN, { title = "Trouble" }) +end + +function M.error(msg) + vim.notify(msg, vim.log.levels.ERROR, { title = "Trouble" }) +end + +function M.debug(msg) + if config.options.debug then + vim.notify(msg, vim.log.levels.DEBUG, { title = "Trouble" }) + end +end + +function M.debounce(ms, fn) + local timer = vim.loop.new_timer() + return function(...) + local argv = { ... } + timer:start(ms, 0, function() + timer:stop() + vim.schedule_wrap(fn)(unpack(argv)) + end) + end +end + +function M.throttle(ms, fn) + local timer = vim.loop.new_timer() + local running = false + return function(...) + if not running then + local argv = { ... } + local argc = select("#", ...) + + timer:start(ms, 0, function() + running = false + pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc)) + end) + running = true + end + end +end + +M.severity = { + [0] = "Other", + [1] = "Error", + [2] = "Warning", + [3] = "Information", + [4] = "Hint", +} + +-- returns a hl or sign label for the givin severity and type +-- correctly handles new names introduced in vim.diagnostic +function M.get_severity_label(severity, type) + local label = severity + local prefix = "LspDiagnostics" .. (type or "Default") + + if vim.diagnostic then + prefix = type and ("Diagnostic" .. type) or "Diagnostic" + label = ({ + Warning = "Warn", + Information = "Info", + })[severity] or severity + end + + return prefix .. label +end + +-- based on the Telescope diagnostics code +-- see https://github.com/nvim-telescope/telescope.nvim/blob/0d6cd47990781ea760dd3db578015c140c7b9fa7/lua/telescope/utils.lua#L85 +function M.process_item(item, bufnr) + bufnr = bufnr or item.bufnr + local filename = vim.api.nvim_buf_get_name(bufnr) + local range = item.range or item.targetSelectionRange + + local start = { + line = range and vim.tbl_get(range, "start", "line") or item.lnum, + character = range and vim.tbl_get(range, "start", "character") or item.col, + } + local finish = { + line = range and vim.tbl_get(range, "end", "line") or item.end_lnum, + character = range and vim.tbl_get(range, "end", "character") or item.end_col, + } + + if start.character == nil or start.line == nil then + M.error("Found an item for Trouble without start range " .. vim.inspect(start)) + end + if finish.character == nil or finish.line == nil then + M.error("Found an item for Trouble without finish range " .. vim.inspect(finish)) + end + local row = start.line ---@type number + local col = start.character ---@type number + + if not item.message and filename then + -- check if the filename is a uri + if string.match(filename, "^%w+://") ~= nil then + if not vim.api.nvim_buf_is_loaded(bufnr) then + vim.fn.bufload(bufnr) + end + local lines = vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) + item.message = lines[1] or "" + else + local fd = assert(uv.fs_open(filename, "r", 438)) + local stat = assert(uv.fs_fstat(fd)) + local data = assert(uv.fs_read(fd, stat.size, 0)) + assert(uv.fs_close(fd)) + + item.message = vim.split(data, "\n", { plain = true })[row + 1] or "" + end + end + + ---@class Item + ---@field is_file boolean + ---@field fixed boolean + local ret = { + bufnr = bufnr, + filename = filename, + lnum = row + 1, + col = col + 1, + start = start, + finish = finish, + sign = item.sign, ---@type string? + sign_hl = item.sign_hl, ---@type string? + -- remove line break to avoid display issues + text = vim.trim(item.message:gsub("[\n]+", "")):sub(0, vim.o.columns), + full_text = vim.trim(item.message), + type = M.severity[item.severity] or M.severity[0], + code = item.code or (item.user_data and item.user_data.lsp and item.user_data.lsp.code), ---@type string? + code_href = (item.codeDescription and item.codeDescription.href) + or ( + item.user_data + and item.user_data.lsp + and item.user_data.lsp.codeDescription + and item.user_data.lsp.codeDescription.href + ), ---@type string? + source = item.source, ---@type string? + severity = item.severity or 0, + } + return ret +end + +-- takes either a table indexed by bufnr, or an lsp result with uri +---@return Item[] +function M.locations_to_items(results, default_severity) + default_severity = default_severity or 0 + local ret = {} + for bufnr, locs in pairs(results or {}) do + for _, loc in pairs(locs.result or locs) do + if not vim.tbl_isempty(loc) then + local uri = loc.uri or loc.targetUri + local buf = uri and vim.uri_to_bufnr(uri) or bufnr + loc.severity = loc.severity or default_severity + table.insert(ret, M.process_item(loc, buf)) + end + end + end + return ret +end + +-- @private +local function make_position_param(win, buf) + local row, col = unpack(vim.api.nvim_win_get_cursor(win)) + row = row - 1 + local line = vim.api.nvim_buf_get_lines(buf, row, row + 1, true)[1] + if not line then + return { line = 0, character = 0 } + end + col = vim.str_utfindex(line, col) + return { line = row, character = col } +end + +function M.make_text_document_params(buf) + return { uri = vim.uri_from_bufnr(buf) } +end + +--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. +--- +-- @returns `TextDocumentPositionParams` object +-- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams +function M.make_position_params(win, buf) + return { + textDocument = M.make_text_document_params(buf), + position = make_position_param(win, buf), + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/view.lua b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/view.lua new file mode 100644 index 00000000..9569a5ef --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/lua/trouble/view.lua @@ -0,0 +1,581 @@ +local renderer = require("trouble.renderer") +local config = require("trouble.config") +local folds = require("trouble.folds") +local util = require("trouble.util") + +local highlight = vim.api.nvim_buf_add_highlight + +---@class TroubleView +---@field buf number +---@field win number +---@field group boolean +---@field items Item[] +---@field folded table<string, boolean> +---@field parent number +---@field float number +local View = {} +View.__index = View + +-- keep track of buffers with added highlights +-- highlights are cleared on BufLeave of Trouble +local hl_bufs = {} + +local function clear_hl(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + vim.api.nvim_buf_clear_namespace(bufnr, config.namespace, 0, -1) + end +end + +---Find a rogue Trouble buffer that might have been spawned by i.e. a session. +local function find_rogue_buffer() + for _, v in ipairs(vim.api.nvim_list_bufs()) do + if vim.fn.bufname(v) == "Trouble" then + return v + end + end + return nil +end + +---Find pre-existing Trouble buffer, delete its windows then wipe it. +---@private +local function wipe_rogue_buffer() + local bn = find_rogue_buffer() + if bn then + local win_ids = vim.fn.win_findbuf(bn) + for _, id in ipairs(win_ids) do + if vim.fn.win_gettype(id) ~= "autocmd" and vim.api.nvim_win_is_valid(id) then + vim.api.nvim_win_close(id, true) + end + end + + vim.api.nvim_buf_set_name(bn, "") + vim.schedule(function() + pcall(vim.api.nvim_buf_delete, bn, {}) + end) + end +end + +---@return TroubleView +function View:new(opts) + opts = opts or {} + + local group + if opts.group ~= nil then + group = opts.group + else + group = config.options.group + end + + local this = { + buf = vim.api.nvim_get_current_buf(), + win = opts.win or vim.api.nvim_get_current_win(), + parent = opts.parent, + items = {}, + group = group, + } + setmetatable(this, self) + return this +end + +function View:set_option(name, value, win) + if win then + return vim.api.nvim_set_option_value(name, value, { win = self.win, scope = "local" }) + else + return vim.api.nvim_set_option_value(name, value, { buf = self.buf }) + end +end + +---@param text Text +function View:render(text) + if not self:is_valid() then + return + end + + self:unlock() + self:set_lines(text.lines) + self:lock() + clear_hl(self.buf) + for _, data in ipairs(text.hl) do + highlight(self.buf, config.namespace, data.group, data.line, data.from, data.to) + end +end + +function View:clear() + return vim.api.nvim_buf_set_lines(self.buf, 0, -1, false, {}) +end + +function View:unlock() + self:set_option("modifiable", true) + self:set_option("readonly", false) +end + +function View:lock() + self:set_option("readonly", true) + self:set_option("modifiable", false) +end + +function View:set_lines(lines, first, last, strict) + first = first or 0 + last = last or -1 + strict = strict or false + return vim.api.nvim_buf_set_lines(self.buf, first, last, strict, lines) +end + +function View:is_valid() + return vim.api.nvim_buf_is_valid(self.buf) and vim.api.nvim_buf_is_loaded(self.buf) +end + +function View:update(opts) + util.debug("update") + renderer.render(self, opts) +end + +function View:setup(opts) + util.debug("setup") + opts = opts or {} + vim.cmd("setlocal nonu") + vim.cmd("setlocal nornu") + if not pcall(vim.api.nvim_buf_set_name, self.buf, "Trouble") then + wipe_rogue_buffer() + vim.api.nvim_buf_set_name(self.buf, "Trouble") + end + self:set_option("bufhidden", "wipe") + self:set_option("buftype", "nofile") + self:set_option("swapfile", false) + self:set_option("cursorline", true, true) + self:set_option("buflisted", false) + self:set_option("winfixwidth", true, true) + self:set_option("wrap", false, true) + self:set_option("spell", false, true) + self:set_option("list", false, true) + self:set_option("winfixheight", true, true) + self:set_option("signcolumn", "no", true) + self:set_option("foldmethod", "manual", true) + self:set_option("foldcolumn", "0", true) + self:set_option("foldlevel", 3, true) + self:set_option("foldenable", false, true) + self:set_option("winhighlight", "Normal:TroubleNormal,EndOfBuffer:TroubleNormal,SignColumn:TroubleNormal", true) + self:set_option("fcs", "eob: ", true) + + for action, keys in pairs(config.options.action_keys) do + if type(keys) == "string" then + keys = { keys } + end + for _, key in pairs(keys) do + vim.api.nvim_buf_set_keymap( + self.buf, + "n", + key, + [[<cmd>lua require("trouble").action("]] .. action .. [[")<cr>]], + { + silent = true, + noremap = true, + nowait = true, + } + ) + end + end + + if config.options.position == "top" or config.options.position == "bottom" then + vim.api.nvim_win_set_height(self.win, config.options.height) + else + vim.api.nvim_win_set_width(self.win, config.options.width) + end + + self:set_option("filetype", "Trouble") + + vim.api.nvim_exec( + [[ + augroup TroubleHighlights + autocmd! * <buffer> + autocmd BufEnter <buffer> lua require("trouble").action("on_enter") + autocmd CursorMoved <buffer> lua require("trouble").action("auto_preview") + autocmd BufLeave <buffer> lua require("trouble").action("on_leave") + augroup END + ]], + false + ) + + if not opts.parent then + self:on_enter() + end + self:lock() + self:update(opts) +end + +function View:on_enter() + util.debug("on_enter") + + self.parent = self.parent or vim.fn.win_getid(vim.fn.winnr("#")) + + if (not self:is_valid_parent(self.parent)) or self.parent == self.win then + util.debug("not valid parent") + for _, win in pairs(vim.api.nvim_list_wins()) do + if self:is_valid_parent(win) and win ~= self.win then + self.parent = win + break + end + end + end + + if not vim.api.nvim_win_is_valid(self.parent) then + return self:close() + end + + self.parent_state = { + buf = vim.api.nvim_win_get_buf(self.parent), + cursor = vim.api.nvim_win_get_cursor(self.parent), + } +end + +function View:on_leave() + util.debug("on_leave") + self:close_preview() +end + +function View:close_preview() + -- Clear preview highlights + for buf, _ in pairs(hl_bufs) do + clear_hl(buf) + end + hl_bufs = {} + + -- Reset parent state + local valid_win = vim.api.nvim_win_is_valid(self.parent) + local valid_buf = self.parent_state and vim.api.nvim_buf_is_valid(self.parent_state.buf) + + if self.parent_state and valid_buf and valid_win then + vim.api.nvim_win_set_buf(self.parent, self.parent_state.buf) + vim.api.nvim_win_set_cursor(self.parent, self.parent_state.cursor) + end + + self.parent_state = nil +end + +function View:is_float(win) + local opts = vim.api.nvim_win_get_config(win) + return opts and opts.relative and opts.relative ~= "" +end + +function View:is_valid_parent(win) + if not vim.api.nvim_win_is_valid(win) then + return false + end + -- dont do anything for floating windows + if View:is_float(win) then + return false + end + local buf = vim.api.nvim_win_get_buf(win) + -- Skip special buffers + if vim.api.nvim_buf_get_option(buf, "buftype") ~= "" then + return false + end + + return true +end + +function View:on_win_enter() + util.debug("on_win_enter") + + local current_win = vim.api.nvim_get_current_win() + + if vim.fn.winnr("$") == 1 and current_win == self.win then + vim.cmd([[q]]) + return + end + + if not self:is_valid_parent(current_win) then + return + end + + local current_buf = vim.api.nvim_get_current_buf() + + -- update parent when needed + if current_win ~= self.parent and current_win ~= self.win then + self.parent = current_win + -- update diagnostics to match the window we are viewing + if self:is_valid() then + vim.defer_fn(function() + util.debug("update_on_win_enter") + self:update() + end, 100) + end + end + + -- check if another buffer took over our window + local parent = self.parent + if current_win == self.win and current_buf ~= self.buf then + -- open the buffer in the parent + vim.api.nvim_win_set_buf(parent, current_buf) + -- HACK: some window local settings need to be reset + vim.api.nvim_win_set_option(parent, "winhl", "") + -- close the current trouble window + vim.api.nvim_win_close(self.win, false) + -- open a new trouble window + require("trouble").open() + -- switch back to the opened window / buffer + View.switch_to(parent, current_buf) + -- util.warn("win_enter pro") + end +end + +function View:focus() + if not self:is_valid() then + return + end + + View.switch_to(self.win, self.buf) + local line = self:get_line() + if line == 1 then + self:next_item() + if config.options.padding then + self:next_item() + end + end +end + +function View.switch_to(win, buf) + if win then + vim.api.nvim_set_current_win(win) + if buf then + vim.api.nvim_win_set_buf(win, buf) + end + end +end + +function View:switch_to_parent() + -- vim.cmd("wincmd p") + View.switch_to(self.parent) +end + +function View:close() + util.debug("close") + if vim.api.nvim_win_is_valid(self.win) then + if vim.api.nvim_win_is_valid(self.parent) then + vim.api.nvim_set_current_win(self.parent) + end + vim.api.nvim_win_close(self.win, {}) + end + if vim.api.nvim_buf_is_valid(self.buf) then + vim.api.nvim_buf_delete(self.buf, {}) + end +end + +function View.create(opts) + opts = opts or {} + ---@type TroubleView + local view + vim.api.nvim_win_call(0, function() + if opts.win then + View.switch_to(opts.win) + vim.cmd("enew") + else + vim.cmd("below new") + local pos = { bottom = "J", top = "K", left = "H", right = "L" } + vim.cmd("wincmd " .. (pos[config.options.position] or "K")) + end + view = View:new(opts) + view:setup(opts) + end) + + if opts.focus == true then + view:focus() + end + + return view +end + +function View:get_cursor() + return vim.api.nvim_win_get_cursor(self.win) +end +function View:get_line() + return self:get_cursor()[1] +end +function View:get_col() + return self:get_cursor()[2] +end + +function View:current_item() + local line = self:get_line() + local item = self.items[line] + return item +end + +function View:next_item(opts) + opts = opts or { skip_groups = false } + local line = opts.first and 0 or self:get_line() + 1 + + if line > #self.items then + if config.options.cycle_results then + self:first_item(opts) + end + else + for i = line, vim.api.nvim_buf_line_count(self.buf), 1 do + if self.items[i] and not (opts.skip_groups and self.items[i].is_file) then + vim.api.nvim_win_set_cursor(self.win, { i, self:get_col() }) + if opts.jump then + self:jump() + end + return + end + end + end +end + +function View:previous_item(opts) + opts = opts or { skip_groups = false } + local line = opts.last and vim.api.nvim_buf_line_count(self.buf) or self:get_line() - 1 + + for i = 0, vim.api.nvim_buf_line_count(self.buf), 1 do + if self.items[i] then + if line < i + (opts.skip_groups and 1 or 0) then + if config.options.cycle_results then + self:last_item(opts) + end + return + end + break + end + end + + for i = line, 0, -1 do + if self.items[i] and not (opts.skip_groups and self.items[i].is_file) then + vim.api.nvim_win_set_cursor(self.win, { i, self:get_col() }) + if opts.jump then + self:jump() + end + return + end + end +end + +function View:first_item(opts) + opts = opts or {} + opts.first = true + return self:next_item(opts) +end + +function View:last_item(opts) + opts = opts or {} + opts.last = true + return self:previous_item(opts) +end + +function View:hover(opts) + opts = opts or {} + local item = opts.item or self:current_item() + if not (item and item.full_text) then + return + end + vim.lsp.util.open_floating_preview(vim.split(item.full_text, "\n"), "markdown", config.options.win_config) +end + +function View:jump(opts) + opts = opts or {} + local item = opts.item or self:current_item() + if not item then + return + end + + if item.is_file == true then + folds.toggle(item.filename) + self:update() + else + util.jump_to_item(opts.win or self.parent, opts.precmd, item) + end +end + +function View:toggle_fold() + folds.toggle(self:current_item().filename) + self:update() +end + +function View:_preview() + if not vim.api.nvim_win_is_valid(self.parent) then + return + end + if not vim.api.nvim_win_is_valid(self.win) then + return + end + + local item = self:current_item() + if not item then + return + end + if item.bufnr == 0 then + return + end + + util.debug("preview") + + if item.is_file ~= true then + vim.api.nvim_win_set_buf(self.parent, item.bufnr) + local pos = { item.start.line + 1, item.start.character } + local line_count = vim.api.nvim_buf_line_count(item.bufnr) + pos[1] = math.min(pos[1], line_count) + vim.api.nvim_win_set_cursor(self.parent, pos) + + vim.api.nvim_buf_call(item.bufnr, function() + -- Center preview line on screen and open enough folds to show it + vim.cmd("norm! zz zv") + if not vim.api.nvim_buf_is_loaded(item.bufnr) then + vim.fn.bufload(item.bufnr) + end + end) + + clear_hl(item.bufnr) + hl_bufs[item.bufnr] = true + for row = item.start.line, item.finish.line, 1 do + local col_start = 0 + local col_end = -1 + if row == item.start.line then + col_start = item.start.character + end + if row == item.finish.line then + col_end = item.finish.character + end + highlight(item.bufnr, config.namespace, "TroublePreview", row, col_start, col_end) + end + end +end + +-- View.preview = View._preview + +View.preview = util.throttle(50, View._preview) + +function View:open_code_href() + if not vim.api.nvim_win_is_valid(self.parent) then + return + end + + local item = self:current_item() + if not item then + return + end + util.debug("open code href") + + if item.is_file ~= true and item.code_href then + local cmd + if vim.fn.has("win32") == 1 then + cmd = "explorer" + elseif vim.fn.executable("xdg-open") == 1 then + cmd = "xdg-open" + elseif vim.fn.executable("wslview") == 1 then + cmd = "wslview" + else + cmd = "open" + end + + local ret = vim.fn.jobstart({ cmd, item.code_href }, { detach = true }) + if ret <= 0 then + local msg = { + "Failed to open code href", + ret, + vim.inspect(cmd), + } + vim.notify(table.concat(msg, "\n"), vim.log.levels.ERROR) + end + end +end + +return View diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/media/shot.png b/config/neovim/store/lazy-plugins/trouble.nvim/media/shot.png new file mode 100644 index 00000000..e75f1fcc Binary files /dev/null and b/config/neovim/store/lazy-plugins/trouble.nvim/media/shot.png differ diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/plugin/trouble.vim b/config/neovim/store/lazy-plugins/trouble.nvim/plugin/trouble.vim new file mode 100644 index 00000000..b0ac8ba0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/plugin/trouble.vim @@ -0,0 +1,21 @@ + +augroup Trouble + autocmd! + if has('nvim-0.6') + " Use the new diagnostic subsystem for neovim 0.6 and up + au DiagnosticChanged * lua require'trouble'.refresh({auto = true, provider = "diagnostics"}) + else + au User LspDiagnosticsChanged lua require'trouble'.refresh({auto = true, provider = "diagnostics"}) + endif + autocmd BufWinEnter,BufEnter * lua require("trouble").action("on_win_enter") +augroup end + +function! s:complete(arg,line,pos) abort + return join(sort(luaeval('vim.tbl_keys(require("trouble.providers").providers)')), "\n") +endfunction + +command! -nargs=* -complete=custom,s:complete Trouble lua require'trouble'.open(<f-args>) +command! -nargs=* -complete=custom,s:complete TroubleToggle lua require'trouble'.toggle(<f-args>) +command! TroubleClose lua require'trouble'.close() +command! TroubleRefresh lua require'trouble'.refresh() + diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/selene.toml b/config/neovim/store/lazy-plugins/trouble.nvim/selene.toml new file mode 100644 index 00000000..6540d6f9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/selene.toml @@ -0,0 +1 @@ +std="lua51+vim" diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/stylua.toml b/config/neovim/store/lazy-plugins/trouble.nvim/stylua.toml new file mode 100644 index 00000000..5d6c50dc --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/stylua.toml @@ -0,0 +1,3 @@ +indent_type = "Spaces" +indent_width = 2 +column_width = 120 \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/trouble.nvim/vim.toml b/config/neovim/store/lazy-plugins/trouble.nvim/vim.toml new file mode 100644 index 00000000..0fa5c4fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/trouble.nvim/vim.toml @@ -0,0 +1,2 @@ +[vim] +any = true diff --git a/config/neovim/store/lazy-plugins/vim-bbye/.editorconfig b/config/neovim/store/lazy-plugins/vim-bbye/.editorconfig new file mode 100644 index 00000000..a00e2a5f --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-bbye/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[Makefile] +indent_style = tab + +[*.vim] +indent_style = tab diff --git a/config/neovim/store/lazy-plugins/vim-bbye/CHANGELOG.md b/config/neovim/store/lazy-plugins/vim-bbye/CHANGELOG.md new file mode 100644 index 00000000..826b34c4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-bbye/CHANGELOG.md @@ -0,0 +1,22 @@ +## Unreleased +- Adds `:Bwipeout`. Thanks, [Juan Ibiapina](http://juanibiapina.com)! +- Fixes `:Bdelete`ing an already unlisted buffer. + That happens when you close a buffer that itself closes when switched away from. + Thanks, [Samuel Simões](http://blog.samuelsimoes.com), for debugging help! + +## 1.0.1 (Jul 23, 2013) +- Fixes `:Bdelete`ing via buffer number. Finally, perfect! + +## 1.0.0 (Jul 23, 2013) +- Hides the empty buffer from buffer explorers and tabbars. +- Handles `:Bdelete!`ing buffers which are set to auto-delete via `&bufhidden`. +- Wipes empty buffers after hiding to reduce the amount of unlisted buffers after using Bbye for a while. +- Handles buffer explorers and tabbars better that remove or add windows mid-flight. +- Improves an edge-case where the empty buffer might get listed and show up in buffer explorers. +- Perfect for v1.0.0. + +## 0.9.1 (Jul 21, 2013) +- Removes an innocent but forgotten debugging line. Now even more perfect. + +## 0.9.0 (Jul 21, 2013) +- First release. It's perfect. diff --git a/config/neovim/store/lazy-plugins/vim-bbye/LICENSE b/config/neovim/store/lazy-plugins/vim-bbye/LICENSE new file mode 100644 index 00000000..09f74d3f --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-bbye/LICENSE @@ -0,0 +1,20 @@ +Bbye +Copyright (C) 2013 Andri Möll + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or any later version. + +Additional permission under the GNU Affero GPL version 3 section 7: +If you modify this Program, or any covered work, by linking or +combining it with other code, such other code is not for that reason +alone subject to any of the requirements of the GNU Affero GPL version 3. + +In summary: +- You can use this program for no cost. +- You can use this program for both personal and commercial reasons. +- You do not have to share your own program's code which uses this program. +- You have to share modifications (e.g bug-fixes) you've made to this program. + +For the full copy of the GNU Affero General Public License see: +http://www.gnu.org/licenses. diff --git a/config/neovim/store/lazy-plugins/vim-bbye/Makefile b/config/neovim/store/lazy-plugins/vim-bbye/Makefile new file mode 100644 index 00000000..9fb6152f --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-bbye/Makefile @@ -0,0 +1,17 @@ +NAME := bbye +VERSION := 1.0.1 +ID := 4664 + +love: + @echo "Feel like makin' love." + +pack: + zip -r "$(NAME)-$(VERSION).zip" * --exclude Makefile --exclude "*.zip" + +publish: + open "http://www.vim.org/scripts/add_script_version.php?script_id=$(ID)" + +tag: + git tag "v$(VERSION)" + +.PHONY: love pack publish tag diff --git a/config/neovim/store/lazy-plugins/vim-bbye/README.md b/config/neovim/store/lazy-plugins/vim-bbye/README.md new file mode 100644 index 00000000..788e474f --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-bbye/README.md @@ -0,0 +1,87 @@ +Bbye (Buffer Bye) for Vim +========================== +Bbye allows you to do delete buffers (close files) without closing your windows or messing up your layout. + +Vim by default closes all windows that have the buffer (file) open when you do `:bdelete`. If you've just got your splits and columns perfectly tuned, having them messed up equals a punch in the face and that's no way to tango. + +Bbye gives you `:Bdelete` and `:Bwipeout` commands that behave like well designed citizens: + +- Close and remove the buffer. +- Show another file in that window. +- Show an empty file if you've got no other files open. +- Do not leave useless `[no file]` buffers if you decide to edit another file in that window. +- Work even if a file's open in multiple windows. +- Work a-okay with various buffer explorers and tabbars. + +Regain your throne as king of buffers! + + +Installing +---------- +The easiest and most modular way is to download Bbye to `~/.vim/bundle`: +``` +mkdir -p ~/.vim/bundle/bbye +``` + +Using Git: +``` +git clone https://github.com/moll/vim-bbye.git ~/.vim/bundle/bbye +``` + +Using Wget: +``` +wget https://github.com/moll/vim-bbye/archive/master.tar.gz -O- | tar -xf- --strip-components 1 -C ~/.vim/bundle/bbye +``` + +Then prepend that directory to Vim's `&runtimepath` (or use [Pathogen](https://github.com/tpope/vim-pathogen)): +``` +set runtimepath^=~/.vim/bundle/bbye +``` + + +Using +----- +Instead of `:bdelete` and `:bwipeout`, use `:Bdelete` and `:Bwipeout` respectively. Fortunately autocomplete helps by sorting `:Bdelete` before its lowercase brother. + +As it's likely you'll be using `:Bdelete` often, make a shortcut to `\q`, for example, to save time. Throw this to your `vimrc`: +``` +:nnoremap <Leader>q :Bdelete<CR> +``` + +### Buffer delete vs wipeout +Vim has two commands for closing a buffer: `:bdelete` and `:bwipeout`. The former removes the file from the buffer list, clears its options, variables and mappings. However, it remains in the jumplist, so `Ctrl-o` takes you back and reopens the file. If that's not what you want, use `:bwipeout` or Bbye's equivalent `:Bwipeout` where you would've used `:bdelete`. + +### Closing all open buffers and files +Occasionally you'll want to close all open buffers and files while leaving your pristine window setup as is. That's easy. Just do: +``` +:bufdo :Bdelete +``` + +For some variations, like closing all-but-one buffer, see [@qiushihe](https://github.com/qiushihe)'s script in https://github.com/moll/vim-bbye/pull/4. + +### Aliasing to :Bclose +If you've used any `Bclose.vim` scripts before and for some reason need the `:Bclose` command to exist, you may make an alias: +``` +command! -bang -complete=buffer -nargs=? Bclose Bdelete<bang> <args> +``` + + +License +------- +Bbye is released under a *Lesser GNU Affero General Public License*, which in summary means: + +- You **can** use this program for **no cost**. +- You **can** use this program for **both personal and commercial reasons**. +- You **do not have to share your own program's code** which uses this program. +- You **have to share modifications** (e.g bug-fixes) you've made to this program. + +For more convoluted language, see the `LICENSE` file. + + +About +----- +**[Andri Möll](http://themoll.com)** authored this in SublemacslipseMate++. +[Monday Calendar](https://mondayapp.com) supported the engineering work. +Inspired by [Bclose.vim](http://vim.wikia.com/wiki/VimTip165), but rewritten to be perfect. + +If you find Bbye needs improving or you've got a question, please don't hesitate to email me anytime at andri@dot.ee or [create an issue online](https://github.com/moll/vim-bbye/issues). diff --git a/config/neovim/store/lazy-plugins/vim-bbye/plugin/bbye.vim b/config/neovim/store/lazy-plugins/vim-bbye/plugin/bbye.vim new file mode 100644 index 00000000..82d482a9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-bbye/plugin/bbye.vim @@ -0,0 +1,91 @@ +if exists("g:loaded_bbye") || &cp | finish | endif +let g:loaded_bbye = 1 + +function! s:bdelete(action, bang, buffer_name) + let buffer = s:str2bufnr(a:buffer_name) + let w:bbye_back = 1 + + if buffer < 0 + return s:error("E516: No buffers were deleted. No match for ".a:buffer_name) + endif + + if getbufvar(buffer, "&modified") && empty(a:bang) + let error = "E89: No write since last change for buffer " + return s:error(error . buffer . " (add ! to override)") + endif + + " If the buffer is set to delete and it contains changes, we can't switch + " away from it. Hide it before eventual deleting: + if getbufvar(buffer, "&modified") && !empty(a:bang) + call setbufvar(buffer, "&bufhidden", "hide") + endif + + " For cases where adding buffers causes new windows to appear or hiding some + " causes windows to disappear and thereby decrement, loop backwards. + for window in reverse(range(1, winnr("$"))) + " For invalid window numbers, winbufnr returns -1. + if winbufnr(window) != buffer | continue | endif + execute window . "wincmd w" + + " Bprevious also wraps around the buffer list, if necessary: + try | exe bufnr("#") > 0 && buflisted(bufnr("#")) ? "buffer #" : "bprevious" + catch /^Vim([^)]*):E85:/ " E85: There is no listed buffer + endtry + + " If found a new buffer for this window, mission accomplished: + if bufnr("%") != buffer | continue | endif + + call s:new(a:bang) + endfor + + " Because tabbars and other appearing/disappearing windows change + " the window numbers, find where we were manually: + let back = filter(range(1, winnr("$")), "getwinvar(v:val, 'bbye_back')")[0] + if back | exe back . "wincmd w" | unlet w:bbye_back | endif + + " If it hasn't been already deleted by &bufhidden, end its pains now. + " Unless it previously was an unnamed buffer and :enew returned it again. + " + " Using buflisted() over bufexists() because bufhidden=delete causes the + " buffer to still _exist_ even though it won't be :bdelete-able. + if buflisted(buffer) && buffer != bufnr("%") + exe a:action . a:bang . " " . buffer + endif +endfunction + +function! s:str2bufnr(buffer) + if empty(a:buffer) + return bufnr("%") + elseif a:buffer =~# '^\d\+$' + return bufnr(str2nr(a:buffer)) + else + return bufnr(a:buffer) + endif +endfunction + +function! s:new(bang) + exe "enew" . a:bang + + setl noswapfile + " If empty and out of sight, delete it right away: + setl bufhidden=wipe + " Regular buftype warns people if they have unsaved text there. Wouldn't + " want to lose someone's data: + setl buftype= + " Hide the buffer from buffer explorers and tabbars: + setl nobuflisted +endfunction + +" Using the built-in :echoerr prints a stacktrace, which isn't that nice. +function! s:error(msg) + echohl ErrorMsg + echomsg a:msg + echohl NONE + let v:errmsg = a:msg +endfunction + +command! -bang -complete=buffer -nargs=? Bdelete + \ :call s:bdelete("bdelete", <q-bang>, <q-args>) + +command! -bang -complete=buffer -nargs=? Bwipeout + \ :call s:bdelete("bwipeout", <q-bang>, <q-args>) diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/.editorconfig b/config/neovim/store/lazy-plugins/vim-illuminate/.editorconfig new file mode 100644 index 00000000..adbdb1ba --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/.editorconfig @@ -0,0 +1,3 @@ +[*] +indent_style = space +indent_size = 4 diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/.github/ISSUE_TEMPLATE/bug_report.md b/config/neovim/store/lazy-plugins/vim-illuminate/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..cb42ef67 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,28 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior (include minimal `init.vim` or `.vimrc`): +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Note** Omitting a minimal `init.vim`/`init.lua`/`.vimrc` will likely result in the issue being closed without explanation. + +**Output from `:IlluminateDebug`** + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/.github/workflows/illuminate.yml b/config/neovim/store/lazy-plugins/vim-illuminate/.github/workflows/illuminate.yml new file mode 100644 index 00000000..4f7b5461 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/.github/workflows/illuminate.yml @@ -0,0 +1,30 @@ +name: panvimdoc + +on: + push: + branches: + - '*' + +jobs: + docs: + runs-on: ubuntu-latest + name: pandoc to vimdoc + steps: + - uses: actions/checkout@v2 + - name: illuminate + uses: kdheepak/panvimdoc@main + with: + vimdoc: illuminate + pandoc: "README.md" + toc: true + version: "NVIM v0.8.0" + - name: Check for Changes + id: changes + uses: UnicornGlobal/has-changes-action@v1.0.11 + with: + path: 'doc/' + - uses: stefanzweifel/git-auto-commit-action@v4 + if: steps.changes.outputs.has_changes == 'true' + with: + commit_message: 'Auto generate docs' + branch: ${{ github.head_ref }} diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/.gitignore b/config/neovim/store/lazy-plugins/vim-illuminate/.gitignore new file mode 100644 index 00000000..926ccaaf --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/.gitignore @@ -0,0 +1 @@ +doc/tags diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/README.md b/config/neovim/store/lazy-plugins/vim-illuminate/README.md new file mode 100644 index 00000000..f2a93bc3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/README.md @@ -0,0 +1,265 @@ +# Overview + +Neovim plugin for automatically highlighting other uses of the word under the cursor using either LSP, Tree-sitter, or regex matching. + +![gif](https://media.giphy.com/media/mSG0nwAHDt3Fl7WyoL/giphy.gif) + +# Quickstart + +Just install the plugin and things will work *just work*, no configuration needed. + +You'll also get `<a-n>` and `<a-p>` as keymaps to move between references and `<a-i>` as a textobject for the reference illuminated under the cursor. + +*Note: Vim users should refer to the [docs](https://github.com/RRethy/vim-illuminate?tab=readme-ov-file#vim-users) for the Vimscript implementation of this plugin.* + +# Configuration + +```lua +-- default configuration +require('illuminate').configure({ + -- providers: provider used to get references in the buffer, ordered by priority + providers = { + 'lsp', + 'treesitter', + 'regex', + }, + -- delay: delay in milliseconds + delay = 100, + -- filetype_overrides: filetype specific overrides. + -- The keys are strings to represent the filetype while the values are tables that + -- supports the same keys passed to .configure except for filetypes_denylist and filetypes_allowlist + filetype_overrides = {}, + -- filetypes_denylist: filetypes to not illuminate, this overrides filetypes_allowlist + filetypes_denylist = { + 'dirbuf', + 'dirvish', + 'fugitive', + }, + -- filetypes_allowlist: filetypes to illuminate, this is overridden by filetypes_denylist + -- You must set filetypes_denylist = {} to override the defaults to allow filetypes_allowlist to take effect + filetypes_allowlist = {}, + -- modes_denylist: modes to not illuminate, this overrides modes_allowlist + -- See `:help mode()` for possible values + modes_denylist = {}, + -- modes_allowlist: modes to illuminate, this is overridden by modes_denylist + -- See `:help mode()` for possible values + modes_allowlist = {}, + -- providers_regex_syntax_denylist: syntax to not illuminate, this overrides providers_regex_syntax_allowlist + -- Only applies to the 'regex' provider + -- Use :echom synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'name') + providers_regex_syntax_denylist = {}, + -- providers_regex_syntax_allowlist: syntax to illuminate, this is overridden by providers_regex_syntax_denylist + -- Only applies to the 'regex' provider + -- Use :echom synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'name') + providers_regex_syntax_allowlist = {}, + -- under_cursor: whether or not to illuminate under the cursor + under_cursor = true, + -- large_file_cutoff: number of lines at which to use large_file_config + -- The `under_cursor` option is disabled when this cutoff is hit + large_file_cutoff = nil, + -- large_file_config: config to use for large files (based on large_file_cutoff). + -- Supports the same keys passed to .configure + -- If nil, vim-illuminate will be disabled for large files. + large_file_overrides = nil, + -- min_count_to_highlight: minimum number of matches required to perform highlighting + min_count_to_highlight = 1, + -- should_enable: a callback that overrides all other settings to + -- enable/disable illumination. This will be called a lot so don't do + -- anything expensive in it. + should_enable = function(bufnr) return true end, + -- case_insensitive_regex: sets regex case sensitivity + case_insensitive_regex = false, +}) +``` + +# Highlight Groups + +#### IlluminatedWordText + +Default highlight group used for references if no kind information is available. + +```vim +hi def IlluminatedWordText gui=underline +``` + +#### IlluminatedWordRead + +Highlight group used for references of kind read. + +```vim +hi def IlluminatedWordRead gui=underline +``` + +#### IlluminatedWordWrite + +Highlight group used for references of kind write. + +```vim +hi def IlluminatedWordWrite gui=underline +``` + +# Commands + +#### :IlluminatePause + +Globally pause vim-illuminate. + +#### :IlluminateResume + +Globally resume vim-illuminate. + +#### :IlluminateToggle + +Globally toggle the pause/resume for vim-illuminate. + +#### :IlluminatePauseBuf + +Buffer-local pause of vim-illuminate. + +#### :IlluminateResumeBuf + +Buffer-local resume of vim-illuminate. + +#### :IlluminateToggleBuf + +Buffer-local toggle of the pause/resume for vim-illuminate. + +# Functions + +#### require('illuminate').configure(config) + +Override the default configuration with `config` + +#### require('illuminate').pause() + +Globally pause vim-illuminate. + +#### require('illuminate').resume() + +Globally resume vim-illuminate. + +#### require('illuminate').toggle() + +Globally toggle the pause/resume for vim-illuminate. + +#### require('illuminate').toggle_buf() + +Buffer-local toggle of the pause/resume for vim-illuminate. + +#### require('illuminate').pause_buf() + +Buffer-local pause of vim-illuminate. + +#### require('illuminate').resume_buf() + +Buffer-local resume of vim-illuminate. + +#### require('illuminate').freeze_buf() + +Freeze the illumination on the buffer, this won't clear the highlights. + +#### require('illuminate').unfreeze_buf() + +Unfreeze the illumination on the buffer. + +#### require('illuminate').toggle_freeze_buf() + +Toggle the frozen state of the buffer. + +#### require('illuminate').invisible_buf() + +Turn off the highlighting for the buffer, this won't stop the engine from running so you can still use `<c-n>` and `<c-p>`. + +#### require('illuminate').visible_buf() + +Turn on the highlighting for the buffer, this is only needed if you've previous called `require('illuminate').invisible_buf()`. + +#### require('illuminate').toggle_visibility_buf() + +Toggle the visibility of highlights in the buffer. + +#### require('illuminate').goto_next_reference(wrap) + +Move the cursor to the closest references after the cursor which it is not currently on. Wraps the buffer if on the last reference. + +Wraps the references unless `wrap` is false (defaults to **'wrapscan'**). + +#### require('illuminate').goto_prev_reference(wrap) + +Move the cursor to the closest references before the cursor which it is not currently on. Wraps the buffer if on the first reference. + +Wraps the references unless `wrap` is false (defaults to **'wrapscan'**). + +#### require('illuminate').textobj_select() + +Selects the reference the cursor is currently on for use as a text-object. + +# Vim Users + +**Note:** This section is deprecated for Neovim users, Neovim users can use the newer version of the plugin. Neovim users can force this old version of the plugin by adding `let g:Illuminate_useDeprecated = 1` to their `init.vim`. + +Illuminate will delay before highlighting, this is not lag, it is to avoid the jarring experience of things illuminating too fast. This can be controlled with `g:Illuminate_delay` (which is default to 0 milliseconds): + +**Note**: Delay only works for Vim8 and Neovim. + +```vim +" Time in milliseconds (default 0) +let g:Illuminate_delay = 0 +``` +Illuminate will by default highlight the word under the cursor to match the behaviour seen in Intellij and VSCode. However, to make it not highlight the word under the cursor, use the following: + +```vim +" Don't highlight word under cursor (default: 1) +let g:Illuminate_highlightUnderCursor = 0 +``` + +By default illuminate will highlight all words the cursor passes over, but for many languages, you will only want to highlight certain highlight-groups. +You can determine the highlight-group of a symbol under your cursor with `:echo synIDattr(synID(line("."), col("."), 1), "name")`. + +You can define which highlight groups you want the illuminating to apply to. This can be done with a dict mapping a filetype to a list of highlight-groups in your vimrc such as: +```vim +let g:Illuminate_ftHighlightGroups = { + \ 'vim': ['vimVar', 'vimString', 'vimLineComment', + \ 'vimFuncName', 'vimFunction', 'vimUserFunc', 'vimFunc'] + \ } +``` + +A blacklist of highlight groups can also be setup by adding the suffix `:blacklist` to the filetype. However, if the whitelist for that filetype already exists, it will override the blacklist. +```vim +let g:Illuminate_ftHighlightGroups = { + \ 'vim:blacklist': ['vimVar', 'vimString', 'vimLineComment', + \ 'vimFuncName', 'vimFunction', 'vimUserFunc', 'vimFunc'] + \ } +``` + +illuminate can also be disabled for various filetypes using the following: +```vim +let g:Illuminate_ftblacklist = ['nerdtree'] +``` + +Or you can enable it only for certain filetypes with: +```vim +let g:Illuminate_ftwhitelist = ['vim', 'sh', 'python'] +``` + +By default the highlighting will be done with the highlight-group `CursorLine` since that is in my opinion the nicest. It can however be overridden using the following (use standard Vim highlighting syntax): +Note: It must be in an autocmd to get around a weird Neovim behaviour. +```vim +augroup illuminate_augroup + autocmd! + autocmd VimEnter * hi link illuminatedWord CursorLine +augroup END + +augroup illuminate_augroup + autocmd! + autocmd VimEnter * hi illuminatedWord cterm=underline gui=underline +augroup END +``` + +Lastly, you can also specify a specific highlight for the word under the cursor so it differs from all other matches using the following higlight group: +```vim +augroup illuminate_augroup + autocmd! + autocmd VimEnter * hi illuminatedCurWord cterm=italic gui=italic +augroup END +``` diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/autoload/illuminate.vim b/config/neovim/store/lazy-plugins/vim-illuminate/autoload/illuminate.vim new file mode 100644 index 00000000..b0fc345b --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/autoload/illuminate.vim @@ -0,0 +1,202 @@ +" illuminate.vim - Vim plugin for selectively illuminating other uses of current word +" Maintainer: Adam P. Regasz-Rethy (RRethy) <rethy.spud@gmail.com> +" Version: 0.4 + +let s:previous_match = '' +let s:enabled = 1 + +let g:Illuminate_delay = get(g:, 'Illuminate_delay', 0) +let g:Illuminate_highlightUnderCursor = get(g:, 'Illuminate_highlightUnderCursor', 1) +let g:Illuminate_highlightPriority = get(g:, 'Illuminate_highlightPriority', -1) + +fun! illuminate#on_cursor_moved() abort + if !s:should_illuminate_file() + return + endif + + if s:previous_match !=# s:get_cur_word() + call s:remove_illumination() + elseif get(g:, 'Illuminate_highlightUnderCursor', 1) == 0 || hlexists('illuminatedCurWord') + call s:remove_illumination() + call s:illuminate() + return + else + return + endif + + " Any delay at or below 17 milliseconds gets counted as no delay + if !has('timers') || g:Illuminate_delay <= 17 + call s:illuminate() + return + endif + + if exists('s:timer_id') && s:timer_id > -1 + call timer_stop(s:timer_id) + endif + + let s:timer_id = timer_start(g:Illuminate_delay, function('s:illuminate')) +endf + +fun! illuminate#on_leaving_autocmds() abort + if s:should_illuminate_file() + call s:remove_illumination() + endif +endf + +fun! illuminate#on_cursor_moved_i() abort + if get(g:, 'Illuminate_insert_mode_highlight', 0) + call illuminate#on_cursor_moved() + endif +endf + +fun! illuminate#on_insert_entered() abort + if !get(g:, 'Illuminate_insert_mode_highlight', 0) && s:should_illuminate_file() + call s:remove_illumination() + endif +endf + +fun! illuminate#toggle_illumination(bufonly) abort + if a:bufonly + let b:illuminate_enabled = get(b:, 'illuminate_enabled', s:enabled) + if !b:illuminate_enabled + call illuminate#enable_illumination(1) + else + call illuminate#disable_illumination(1) + endif + else + if !s:enabled + call illuminate#enable_illumination(0) + else + call illuminate#disable_illumination(0) + endif + endif +endf + +fun! illuminate#disable_illumination(bufonly) abort + if a:bufonly + let b:illuminate_enabled = 0 + else + let s:enabled = 0 + endif + call s:remove_illumination() +endf + +fun! illuminate#enable_illumination(bufonly) abort + if a:bufonly + let b:illuminate_enabled = 1 + else + let s:enabled = 1 + endif + if s:should_illuminate_file() + call s:illuminate() + endif +endf + +fun! s:illuminate(...) abort + if !get(b:, 'illuminate_enabled', s:enabled) + return + endif + + call s:remove_illumination() + + if s:should_illuminate_word() + call s:match_word(s:get_cur_word()) + endif + let s:previous_match = s:get_cur_word() +endf + +fun! s:match_word(word) abort + if (a:word ==# '\<\>') + return + endif + if g:Illuminate_highlightUnderCursor + if hlexists('illuminatedCurWord') + let w:match_id = matchadd('illuminatedWord', '\V\(\k\*\%#\k\*\)\@\!\&' . a:word, g:Illuminate_highlightPriority) + let w:match_curword_id = matchadd('illuminatedCurWord', '\V\(\k\*\%#\k\*\)\&' . a:word, g:Illuminate_highlightPriority) + else + let w:match_id = matchadd('illuminatedWord', '\V' . a:word, g:Illuminate_highlightPriority) + endif + else + let w:match_id = matchadd('illuminatedWord', '\V\(\k\*\%#\k\*\)\@\!\&' . a:word, g:Illuminate_highlightPriority) + endif +endf + +fun! s:get_cur_word() abort + let line = getline('.') + let col = col('.') - 1 + let left_part = strpart(line, 0, col + 1) + let right_part = strpart(line, col, col('$')) + let word = matchstr(left_part, '\k*$') . matchstr(right_part, '^\k*')[1:] + + return '\<' . escape(word, '/\') . '\>' +endf + +fun! s:remove_illumination() abort + if has('timers') && exists('s:timer_id') && s:timer_id > -1 + call timer_stop(s:timer_id) + let s:timer_id = -1 + endif + + if exists('w:match_id') + try + call matchdelete(w:match_id) + catch /\v(E803|E802)/ + endtry + endif + + if exists('w:match_curword_id') + try + call matchdelete(w:match_curword_id) + catch /\v(E803|E802)/ + endtry + endif + + let s:previous_match = '' +endf + +fun! s:should_illuminate_file() abort + let g:Illuminate_ftblacklist = get(g:, 'Illuminate_ftblacklist', []) + let g:Illuminate_ftwhitelist = get(g:, 'Illuminate_ftwhitelist', []) + + return !s:list_contains_pat(g:Illuminate_ftblacklist, &filetype) + \ && (empty(g:Illuminate_ftwhitelist) || s:list_contains_pat(g:Illuminate_ftwhitelist, &filetype)) +endf + +fun! s:should_illuminate_word() abort + let ft_hl_groups = get(g:, 'Illuminate_ftHighlightGroups', {}) + let hl_groups_whitelist = get(ft_hl_groups, &filetype, []) + call extend(hl_groups_whitelist, get(ft_hl_groups, '*', [])) + if empty(hl_groups_whitelist) + let hl_groups_blacklist = get(ft_hl_groups, &filetype.':blacklist', []) + call extend(hl_groups_blacklist, get(ft_hl_groups, '*:blacklist', [])) + if empty(hl_groups_blacklist) + return 1 + else + return index(hl_groups_blacklist, synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'name')) < 0 + \ && index(hl_groups_blacklist, synIDattr(synID(line('.'), col('.'), 1), 'name')) < 0 + endif + endif + + return index(ft_hl_groups[&filetype], synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'name')) >= 0 + \ || index(ft_hl_groups[&filetype], synIDattr(synID(line('.'), col('.'), 1), 'name')) >= 0 +endf + +fun! s:dict_has_key_pat(d, key) abort + for [k, v] in items(a:d) + if key =~# '^'.k.'$' + return 1 + endif + endfor + return 0 +endfun + +fun! s:list_contains_pat(list, val) abort + for pat in a:list + if a:val =~# '^'.pat.'$' + return 1 + endif + endfor + return 0 +endfun + +" vim: foldlevel=1 foldmethod=expr tabstop=2 softtabstop=2 shiftwidth=2 diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/doc/illuminate.txt b/config/neovim/store/lazy-plugins/vim-illuminate/doc/illuminate.txt new file mode 100644 index 00000000..7cc26027 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/doc/illuminate.txt @@ -0,0 +1,343 @@ +*illuminate.txt* For NVIM v0.8.0 Last change: 2023 March 19 + +============================================================================== +Table of Contents *illuminate-table-of-contents* + +1. Overview |illuminate-overview| +2. Quickstart |illuminate-quickstart| +3. Configuration |illuminate-configuration| +4. Highlight Groups |illuminate-highlight-groups| +5. Commands |illuminate-commands| +6. Functions |illuminate-functions| +7. Vim Users |illuminate-vim-users| + +============================================================================== +1. Overview *illuminate-overview* + +Vim plugin for automatically highlighting other uses of the word under the +cursor using either LSP, Tree-sitter, or regex matching. + + +============================================================================== +2. Quickstart *illuminate-quickstart* + +Just install the plugin and things will work _just work_, no configuration +needed. + +You’ll also get `<a-n>` and `<a-p>` as keymaps to move between references and +`<a-i>` as a textobject for the reference illuminated under the cursor. + + +============================================================================== +3. Configuration *illuminate-configuration* + +>lua + -- default configuration + require('illuminate').configure({ + -- providers: provider used to get references in the buffer, ordered by priority + providers = { + 'lsp', + 'treesitter', + 'regex', + }, + -- delay: delay in milliseconds + delay = 100, + -- filetype_overrides: filetype specific overrides. + -- The keys are strings to represent the filetype while the values are tables that + -- supports the same keys passed to .configure except for filetypes_denylist and filetypes_allowlist + filetype_overrides = {}, + -- filetypes_denylist: filetypes to not illuminate, this overrides filetypes_allowlist + filetypes_denylist = { + 'dirvish', + 'fugitive', + }, + -- filetypes_allowlist: filetypes to illuminate, this is overriden by filetypes_denylist + filetypes_allowlist = {}, + -- modes_denylist: modes to not illuminate, this overrides modes_allowlist + -- See `:help mode()` for possible values + modes_denylist = {}, + -- modes_allowlist: modes to illuminate, this is overriden by modes_denylist + -- See `:help mode()` for possible values + modes_allowlist = {}, + -- providers_regex_syntax_denylist: syntax to not illuminate, this overrides providers_regex_syntax_allowlist + -- Only applies to the 'regex' provider + -- Use :echom synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'name') + providers_regex_syntax_denylist = {}, + -- providers_regex_syntax_allowlist: syntax to illuminate, this is overriden by providers_regex_syntax_denylist + -- Only applies to the 'regex' provider + -- Use :echom synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'name') + providers_regex_syntax_allowlist = {}, + -- under_cursor: whether or not to illuminate under the cursor + under_cursor = true, + -- large_file_cutoff: number of lines at which to use large_file_config + -- The `under_cursor` option is disabled when this cutoff is hit + large_file_cutoff = nil, + -- large_file_config: config to use for large files (based on large_file_cutoff). + -- Supports the same keys passed to .configure + -- If nil, vim-illuminate will be disabled for large files. + large_file_overrides = nil, + -- min_count_to_highlight: minimum number of matches required to perform highlighting + min_count_to_highlight = 1, + }) +< + + +============================================================================== +4. Highlight Groups *illuminate-highlight-groups* + + +ILLUMINATEDWORDTEXT + +Default highlight group used for references if no kind information is +available. + +>vim + hi def IlluminatedWordText gui=underline +< + + +ILLUMINATEDWORDREAD + +Highlight group used for references of kind read. + +>vim + hi def IlluminatedWordRead gui=underline +< + + +ILLUMINATEDWORDWRITE + +Highlight group used for references of kind write. + +>vim + hi def IlluminatedWordWrite gui=underline +< + + +============================================================================== +5. Commands *illuminate-commands* + + +:ILLUMINATEPAUSE + +Globally pause vim-illuminate. + + +:ILLUMINATERESUME + +Globally resume vim-illuminate. + + +:ILLUMINATETOGGLE + +Globally toggle the pause/resume for vim-illuminate. + + +:ILLUMINATEPAUSEBUF + +Buffer-local pause of vim-illuminate. + + +:ILLUMINATERESUMEBUF + +Buffer-local resume of vim-illuminate. + + +:ILLUMINATETOGGLEBUF + +Buffer-local toggle of the pause/resume for vim-illuminate. + + +============================================================================== +6. Functions *illuminate-functions* + + +REQUIRE(‘ILLUMINATE’).CONFIGURE(CONFIG) + +Override the default configuration with `config` + + +REQUIRE(‘ILLUMINATE’).PAUSE() + +Globally pause vim-illuminate. + + +REQUIRE(‘ILLUMINATE’).RESUME() + +Globally resume vim-illuminate. + + +REQUIRE(‘ILLUMINATE’).TOGGLE() + +Globally toggle the pause/resume for vim-illuminate. + + +REQUIRE(‘ILLUMINATE’).TOGGLE_BUF() + +Buffer-local toggle of the pause/resume for vim-illuminate. + + +REQUIRE(‘ILLUMINATE’).PAUSE_BUF() + +Buffer-local pause of vim-illuminate. + + +REQUIRE(‘ILLUMINATE’).RESUME_BUF() + +Buffer-local resume of vim-illuminate. + + +REQUIRE(‘ILLUMINATE’).FREEZE_BUF() + +Freeze the illumination on the buffer, this won’t clear the highlights. + + +REQUIRE(‘ILLUMINATE’).UNFREEZE_BUF() + +Unfreeze the illumination on the buffer. + + +REQUIRE(‘ILLUMINATE’).TOGGLE_FREEZE_BUF() + +Toggle the frozen state of the buffer. + + +REQUIRE(‘ILLUMINATE’).INVISIBLE_BUF() + +Turn off the highlighting for the buffer, this won’t stop the engine from +running so you can still use `<c-n>` and `<c-p>`. + + +REQUIRE(‘ILLUMINATE’).VISIBLE_BUF() + +Turn on the highlighting for the buffer, this is only needed if you’ve +previous called `require('illuminate').invisible_buf()`. + + +REQUIRE(‘ILLUMINATE’).TOGGLE_VISIBILITY_BUF() + +Toggle the visibility of highlights in the buffer. + + +REQUIRE(‘ILLUMINATE’).GOTO_NEXT_REFERENCE(WRAP) + +Move the cursor to the closest references after the cursor which it is not +currently on. Wraps the buffer if on the last reference. + +Wraps the references unless `wrap` is false (defaults to **‘wrapscan’**). + + +REQUIRE(‘ILLUMINATE’).GOTO_PREV_REFERENCE(WRAP) + +Move the cursor to the closest references before the cursor which it is not +currently on. Wraps the buffer if on the first reference. + +Wraps the references unless `wrap` is false (defaults to **‘wrapscan’**). + + +REQUIRE(‘ILLUMINATE’).TEXTOBJ_SELECT() + +Selects the reference the cursor is currently on for use as a text-object. + + +============================================================================== +7. Vim Users *illuminate-vim-users* + +**Note:** This section is deprecated for Neovim users, Neovim users can use the +newer version of the plugin. Neovim users can force this old version of the +plugin by adding `let g:Illuminate_useDeprecated = 1` to their `init.vim`. + +Illuminate will delay before highlighting, this is not lag, it is to avoid the +jarring experience of things illuminating too fast. This can be controlled with +`g:Illuminate_delay` (which is default to 0 milliseconds): + +**Note**: Delay only works for Vim8 and Neovim. + +>vim + " Time in milliseconds (default 0) + let g:Illuminate_delay = 0 +< + +Illuminate will by default highlight the word under the cursor to match the +behaviour seen in Intellij and VSCode. However, to make it not highlight the +word under the cursor, use the following: + +>vim + " Don't highlight word under cursor (default: 1) + let g:Illuminate_highlightUnderCursor = 0 +< + +By default illuminate will highlight all words the cursor passes over, but for +many languages, you will only want to highlight certain highlight-groups. You +can determine the highlight-group of a symbol under your cursor with `:echo +synIDattr(synID(line("."), col("."), 1), "name")`. + +You can define which highlight groups you want the illuminating to apply to. +This can be done with a dict mapping a filetype to a list of highlight-groups +in your vimrc such as: + +>vim + let g:Illuminate_ftHighlightGroups = { + \ 'vim': ['vimVar', 'vimString', 'vimLineComment', + \ 'vimFuncName', 'vimFunction', 'vimUserFunc', 'vimFunc'] + \ } +< + +A blacklist of highlight groups can also be setup by adding the suffix +`:blacklist` to the filetype. However, if the whitelist for that filetype +already exists, it will override the blacklist. + +>vim + let g:Illuminate_ftHighlightGroups = { + \ 'vim:blacklist': ['vimVar', 'vimString', 'vimLineComment', + \ 'vimFuncName', 'vimFunction', 'vimUserFunc', 'vimFunc'] + \ } +< + +illuminate can also be disabled for various filetypes using the following: + +>vim + let g:Illuminate_ftblacklist = ['nerdtree'] +< + +Or you can enable it only for certain filetypes with: + +>vim + let g:Illuminate_ftwhitelist = ['vim', 'sh', 'python'] +< + +By default the highlighting will be done with the highlight-group `CursorLine` +since that is in my opinion the nicest. It can however be overridden using the +following (use standard Vim highlighting syntax): Note: It must be in an +autocmd to get around a weird Neovim behaviour. + +>vim + augroup illuminate_augroup + autocmd! + autocmd VimEnter * hi link illuminatedWord CursorLine + augroup END + + augroup illuminate_augroup + autocmd! + autocmd VimEnter * hi illuminatedWord cterm=underline gui=underline + augroup END +< + +Lastly, you can also specify a specific highlight for the word under the cursor +so it differs from all other matches using the following higlight group: + +>vim + augroup illuminate_augroup + autocmd! + autocmd VimEnter * hi illuminatedCurWord cterm=italic gui=italic + augroup END +< + +============================================================================== +8. Links *illuminate-links* + +1. *gif*: https://media.giphy.com/media/mSG0nwAHDt3Fl7WyoL/giphy.gif + +Generated by panvimdoc <https://github.com/kdheepak/panvimdoc> + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/foo.txt b/config/neovim/store/lazy-plugins/vim-illuminate/foo.txt new file mode 100644 index 00000000..d1941c7e --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/foo.txt @@ -0,0 +1,31 @@ +hello + +hello + +hello + +hello + +hello + +hello + +hello +hello +hello +hello +hello world +hello hello +hello +hello +hello +hello +hello +hello +hello +hello +hello +hello +hello +hello +hello diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate.lua new file mode 100644 index 00000000..bc7b5d55 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate.lua @@ -0,0 +1,321 @@ +local M = {} + +local timers = {} +local references = {} +local paused_bufs = {} + +-- returns r1 < r2 based on start of range +local function before_by_start(r1, r2) + if r1['start'].line < r2['start'].line then return true end + if r2['start'].line < r1['start'].line then return false end + if r1['start'].character < r2['start'].character then return true end + return false +end + +-- returns r1 < r2 base on start and if they are disjoint +local function before_disjoint(r1, r2) + if r1['end'].line < r2['start'].line then return true end + if r2['start'].line < r1['end'].line then return false end + if r1['end'].character < r2['start'].character then return true end + return false +end + +-- check for cursor row in [start,end] +-- check for cursor col in [start,end] +-- While the end is technically exclusive based on the highlighting, we treat it as inclusive to match the server. +local function point_in_range(point, range) + if point.row == range['start']['line'] and point.col < range['start']['character'] then + return false + end + if point.row == range['end']['line'] and point.col > range['end']['character'] then + return false + end + return point.row >= range['start']['line'] and point.row <= range['end']['line'] +end + +local function cursor_in_references(bufnr) + if not references[bufnr] then + return false + end + if vim.api.nvim_win_get_buf(0) ~= bufnr then + return false + end + local crow, ccol = unpack(vim.api.nvim_win_get_cursor(0)) + crow = crow - 1 -- reference ranges are (0,0)-indexed for (row,col) + for _, reference in pairs(references[bufnr]) do + local range = reference.range + if point_in_range({ row = crow, col = ccol }, range) then + return true + end + end + return false +end + +local function handle_document_highlight(result, bufnr, client_id) + if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then return end + local btimer = timers[bufnr] + if btimer then + vim.loop.timer_stop(btimer) + -- vim.loop.close(btimer) + end + if type(result) ~= 'table' then + vim.lsp.util.buf_clear_references(bufnr) + return + end + timers[bufnr] = vim.defer_fn(function() + if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then return end + vim.lsp.util.buf_clear_references(bufnr) + if cursor_in_references(bufnr) then + local client = vim.lsp.get_client_by_id(client_id) + if client then + vim.lsp.util.buf_highlight_references(bufnr, result, client.offset_encoding) + end + end + end, vim.g.Illuminate_delay or 17) + table.sort(result, function(a, b) + return before_by_start(a.range, b.range) + end) + references[bufnr] = result +end + +local function valid(bufnr, range) + return range + and range.start.line < vim.api.nvim_buf_line_count(bufnr) + and range.start.character < #vim.fn.getline(range.start.line + 1) +end + +local function augroup(bufnr, autocmds) + vim.cmd('augroup vim_illuminate_lsp' .. bufnr) + vim.cmd('autocmd!') + if autocmds then + vim.b.illuminate_lsp_enabled = true + autocmds() + else + vim.b.illuminate_lsp_enabled = false + end + vim.cmd('augroup END') +end + +local function autocmd(bufnr) + vim.cmd(string.format('autocmd CursorMoved,CursorMovedI <buffer=%d> lua require"illuminate".on_cursor_moved(%d)', + bufnr, bufnr)) +end + +local function move_cursor(row, col) + if not paused_bufs[vim.api.nvim_get_current_buf()] then + augroup(vim.api.nvim_get_current_buf(), function() + vim.api.nvim_win_set_cursor(0, { row, col }) + autocmd(vim.api.nvim_get_current_buf()) + end) + else + vim.api.nvim_win_set_cursor(0, { row, col }) + end +end + +function M.on_attach(client) + M.stop_buf() + if client and not client.supports_method('textDocument/documentHighlight') then + return + end + pcall(vim.api.nvim_command, 'IlluminationDisable!') + augroup(vim.api.nvim_get_current_buf(), function() + autocmd(vim.api.nvim_get_current_buf()) + end) + vim.lsp.handlers['textDocument/documentHighlight'] = function(...) + if vim.fn.has('nvim-0.5.1') == 1 then + handle_document_highlight(select(2, ...), select(3, ...).bufnr, select(3, ...).client_id) + else + handle_document_highlight(select(3, ...), select(5, ...), nil) + end + end + vim.lsp.buf.document_highlight() +end + +function M.on_cursor_moved(bufnr) + if not cursor_in_references(bufnr) then + vim.lsp.util.buf_clear_references(bufnr) + end + + -- Best-effort check if any client support textDocument/documentHighlight + local supported = nil + -- For nvim 0.10+ + if vim.lsp.get_clients then + supported = false + for _, client in ipairs(vim.lsp.get_clients({bufnr = bufnr})) do + if client and client.supports_method('textDocument/documentHighlight') then + supported = true + end + end + -- For older versions + elseif vim.lsp.for_each_buffer_client then + supported = false + vim.lsp.for_each_buffer_client(bufnr, function(client) + if client.supports_method('textDocument/documentHighlight') then + supported = true + end + end) + end + + if supported == nil or supported then + vim.lsp.buf.document_highlight() + else + augroup(vim.api.nvim_get_current_buf(), function() + end) + end +end + +function M.get_document_highlights(bufnr) + return references[bufnr] +end + +function M.next_reference(opt) + opt = vim.tbl_extend('force', { reverse = false, wrap = false, range_ordering = 'start', silent = false }, opt or {}) + + local before + if opt.range_ordering == 'start' then + before = before_by_start + else + before = before_disjoint + end + local bufnr = vim.api.nvim_get_current_buf() + local refs = M.get_document_highlights(bufnr) + if not refs or #refs == 0 then return nil end + + local next = nil + local nexti = nil + local crow, ccol = unpack(vim.api.nvim_win_get_cursor(0)) + local crange = { start = { line = crow - 1, character = ccol } } + + for i, ref in ipairs(refs) do + local range = ref.range + if valid(bufnr, range) then + if opt.reverse then + if before(range, crange) and (not next or before(next, range)) then + next = range + nexti = i + end + else + if before(crange, range) and (not next or before(range, next)) then + next = range + nexti = i + end + end + end + end + if not next and opt.wrap then + nexti = opt.reverse and #refs or 1 + next = refs[nexti].range + end + if next then + move_cursor(next.start.line + 1, next.start.character) + if not opt.silent then + print('[' .. nexti .. '/' .. #refs .. ']') + end + end + return next +end + +function M.toggle_pause() + if paused_bufs[vim.api.nvim_get_current_buf()] then + paused_bufs[vim.api.nvim_get_current_buf()] = false + augroup(vim.api.nvim_get_current_buf(), function() + autocmd(vim.api.nvim_get_current_buf()) + end) + M.on_cursor_moved(vim.api.nvim_get_current_buf()) + else + paused_bufs[vim.api.nvim_get_current_buf()] = true + augroup(vim.api.nvim_get_current_buf(), nil) + end +end + +function M.configure(config) + require('illuminate.config').set(config) +end + +function M.pause() + require('illuminate.engine').pause() +end + +function M.resume() + require('illuminate.engine').resume() +end + +function M.toggle() + require('illuminate.engine').toggle() +end + +function M.toggle_buf() + require('illuminate.engine').toggle_buf() +end + +function M.pause_buf() + require('illuminate.engine').pause_buf() +end + +function M.stop_buf() + require('illuminate.engine').stop_buf() +end + +function M.resume_buf() + require('illuminate.engine').resume_buf() +end + +function M.freeze_buf() + require('illuminate.engine').freeze_buf() +end + +function M.unfreeze_buf() + require('illuminate.engine').unfreeze_buf() +end + +function M.toggle_freeze_buf() + require('illuminate.engine').toggle_freeze_buf() +end + +function M.invisible_buf() + require('illuminate.engine').invisible_buf() +end + +function M.visible_buf() + require('illuminate.engine').visible_buf() +end + +function M.toggle_visibility_buf() + require('illuminate.engine').toggle_visibility_buf() +end + +function M.goto_next_reference(wrap) + if wrap == nil then + wrap = vim.o.wrapscan + end + require('illuminate.goto').goto_next_reference(wrap) +end + +function M.goto_prev_reference(wrap) + if wrap == nil then + wrap = vim.o.wrapscan + end + require('illuminate.goto').goto_prev_reference(wrap) +end + +function M.textobj_select() + require('illuminate.textobj').select() +end + +function M.debug() + require('illuminate.engine').debug() +end + +function M.is_paused() + return require('illuminate.engine').is_paused() +end + +function M.set_highlight_defaults() + vim.cmd [[ + hi def IlluminatedWordText guifg=none guibg=none gui=underline + hi def IlluminatedWordRead guifg=none guibg=none gui=underline + hi def IlluminatedWordWrite guifg=none guibg=none gui=underline + ]] +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/config.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/config.lua new file mode 100644 index 00000000..27e3f842 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/config.lua @@ -0,0 +1,137 @@ +local M = {} + +local config = { + providers = { + 'lsp', + 'treesitter', + 'regex', + }, + delay = 100, + filetype_overrides = {}, + filetypes_denylist = { + 'dirbuf', + 'dirvish', + 'fugitive', + }, + filetypes_allowlist = {}, + modes_denylist = {}, + modes_allowlist = {}, + providers_regex_syntax_denylist = {}, + providers_regex_syntax_allowlist = {}, + under_cursor = true, + max_file_lines = nil, + large_file_cutoff = nil, + large_file_config = nil, + min_count_to_highlight = 1, + should_enable = nil, + case_insensitive_regex = false, +} + +function M.set(config_overrides) + config = vim.tbl_extend('force', config, config_overrides or {}) +end + +function M.get_raw() + return config +end + +function M.get() + return ( + M.large_file_cutoff() == nil + or vim.fn.line('$') <= M.large_file_cutoff() + or M.large_file_overrides() == nil + ) + and config + or M.large_file_overrides() +end + +function M.filetype_override(bufnr) + local ft = vim.api.nvim_buf_get_option(bufnr, 'filetype') + return M.get()['filetype_overrides'] and M.get()['filetype_overrides'][ft] or {} +end + +function M.providers(bufnr) + return M.filetype_override(bufnr)['providers'] or M.get()['providers'] +end + +function M.filetypes_denylist() + return M.get()['filetypes_denylist'] or {} +end + +function M.filetypes_allowlist() + return M.get()['filetypes_allowlist'] or {} +end + +function M.modes_denylist(bufnr) + return M.filetype_override(bufnr)['modes_denylist'] or M.get()['modes_denylist'] or {} +end + +function M.modes_allowlist(bufnr) + return M.filetype_override(bufnr)['modes_allowlist'] or M.get()['modes_allowlist'] or {} +end + +function M.provider_regex_syntax_denylist(bufnr) + return M.filetype_override(bufnr)['providers_regex_syntax_denylist'] + or M.get()['providers_regex_syntax_denylist'] + or {} +end + +function M.provider_regex_syntax_allowlist(bufnr) + return M.filetype_override(bufnr)['providers_regex_syntax_allowlist'] + or M.get()['providers_regex_syntax_allowlist'] + or {} +end + +function M.under_cursor(bufnr) + if M.filetype_override(bufnr)['under_cursor'] ~= nil then + return M.filetype_override(bufnr)['under_cursor'] ~= nil + end + return M.get()['under_cursor'] +end + +function M.delay(bufnr) + local delay = M.filetype_override(bufnr)['delay'] or M.get()['delay'] or 17 + if string.sub(vim.api.nvim_get_mode().mode, 1, 1) == 'i' then + delay = delay + 100 + end + if delay < 17 then + return 17 + end + return delay +end + +function M.max_file_lines() + return M.get()['max_file_lines'] +end + +function M.large_file_cutoff() + return config['large_file_cutoff'] +end + +function M.large_file_overrides() + if config['large_file_overrides'] ~= nil then + if config['large_file_overrides']['under_cursor'] == nil then + config['large_file_overrides']['under_cursor'] = true + end + return config['large_file_overrides'] + end + return { + filetypes_allowlist = { '_none' } + } +end + +function M.min_count_to_highlight() + return M.get()['min_count_to_highlight'] or 1 +end + +function M.should_enable() + return M.get()['should_enable'] or function(_) + return true + end +end + +function M.case_insensitive_regex() + return M.get()['case_insensitive_regex'] +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/engine.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/engine.lua new file mode 100644 index 00000000..64e8c3f2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/engine.lua @@ -0,0 +1,267 @@ +local hl = require('illuminate.highlight') +local ref = require('illuminate.reference') +local config = require('illuminate.config') +local util = require('illuminate.util') + +local M = {} + +local AUGROUP = 'vim_illuminate_v2_augroup' +local timers = {} +local paused_bufs = {} +local stopped_bufs = {} +local is_paused = false +local written = {} +local error_timestamps = {} +local frozen_bufs = {} +local invisible_bufs = {} +local started = false + +local function buf_should_illuminate(bufnr) + if is_paused or paused_bufs[bufnr] or stopped_bufs[bufnr] then + return false + end + + return config.should_enable()(bufnr) + and (config.max_file_lines() == nil or vim.fn.line('$') <= config.max_file_lines()) + and util.is_allowed( + config.modes_allowlist(bufnr), + config.modes_denylist(bufnr), + vim.api.nvim_get_mode().mode + ) and util.is_allowed( + config.filetypes_allowlist(), + config.filetypes_denylist(), + vim.api.nvim_buf_get_option(bufnr, 'filetype') + ) +end + +local function stop_timer(timer) + if vim.loop.is_active(timer) then + vim.loop.timer_stop(timer) + vim.loop.close(timer) + end +end + +function M.start() + started = true + vim.api.nvim_create_augroup(AUGROUP, { clear = true }) + vim.api.nvim_create_autocmd({ 'VimEnter', 'CursorMoved', 'CursorMovedI', 'ModeChanged', 'TextChanged' }, { + group = AUGROUP, + callback = function() + M.refresh_references() + end, + }) + -- If vim.lsp.buf.format is called, this will call vim.api.nvim_buf_set_text which messes up extmarks. + -- By using this `written` variable, we can ensure refresh_references doesn't terminate early based on + -- ref.buf_cursor_in_references being incorrect (we have references but they're not actually showing + -- as illuminated). vim.lsp.buf.format will trigger CursorMoved so we don't need to do it here. + vim.api.nvim_create_autocmd({ 'BufWritePost' }, { + group = AUGROUP, + callback = function() + written[vim.api.nvim_get_current_buf()] = true + end, + }) + vim.api.nvim_create_autocmd({ 'VimLeave' }, { + group = AUGROUP, + callback = function() + for _, timer in pairs(timers) do + stop_timer(timer) + end + end, + }) +end + +function M.stop() + started = false + vim.api.nvim_create_augroup(AUGROUP, { clear = true }) +end + +--- Get the highlighted references for the item under the cursor for +--- @bufnr and clears any old reference highlights +--- +--- @bufnr (number) +function M.refresh_references(bufnr, winid) + bufnr = bufnr or vim.api.nvim_get_current_buf() + winid = winid or vim.api.nvim_get_current_win() + + if frozen_bufs[bufnr] then + return + end + + if not buf_should_illuminate(bufnr) then + hl.buf_clear_references(bufnr) + ref.buf_set_references(bufnr, {}) + return + end + + -- We might want to optimize here by returning early if cursor is in references. + -- The downside is that LSP servers can sometimes return a different list of references + -- as you move around an existing reference (like return statements). + if written[bufnr] or not ref.buf_cursor_in_references(bufnr, util.get_cursor_pos(winid)) then + hl.buf_clear_references(bufnr) + ref.buf_set_references(bufnr, {}) + elseif config.large_file_cutoff() ~= nil and vim.fn.line('$') > config.large_file_cutoff() then + return + end + written[bufnr] = nil + + if timers[bufnr] then + stop_timer(timers[bufnr]) + end + + local provider = M.get_provider(bufnr) + if not provider then return end + pcall(provider['initiate_request'], bufnr, winid) + + local changedtick = vim.api.nvim_buf_get_changedtick(bufnr) + + local timer = vim.loop.new_timer() + timers[bufnr] = timer + timer:start(config.delay(bufnr), 17, vim.schedule_wrap(function() + local ok, err = pcall(function() + if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then + stop_timer(timer) + return + end + + hl.buf_clear_references(bufnr) + ref.buf_set_references(bufnr, {}) + + if vim.api.nvim_buf_get_changedtick(bufnr) ~= changedtick + or vim.api.nvim_get_current_win() ~= winid + or bufnr ~= vim.api.nvim_win_get_buf(0) then + stop_timer(timer) + return + end + + provider = M.get_provider(bufnr) + if not provider then + stop_timer(timer) + return + end + + local references = provider.get_references(bufnr, util.get_cursor_pos(winid)) + if references ~= nil then + ref.buf_set_references(bufnr, references) + if ref.buf_cursor_in_references(bufnr, util.get_cursor_pos(winid)) then + if not invisible_bufs[bufnr] == true then + hl.buf_highlight_references(bufnr, ref.buf_get_references(bufnr)) + end + else + ref.buf_set_references(bufnr, {}) + end + stop_timer(timer) + end + end) + + if not ok then + local time = vim.loop.hrtime() + if #error_timestamps == 5 then + vim.notify( + 'vim-illuminate: An internal error has occured: ' .. vim.inspect(ok) .. vim.inspect(err), + vim.log.levels.ERROR, + {} + ) + M.stop() + stop_timer(timer) + elseif #error_timestamps == 0 or time - error_timestamps[#error_timestamps] < 500000000 then + table.insert(error_timestamps, time) + else + error_timestamps = { time } + end + end + end)) +end + +function M.get_provider(bufnr) + for _, provider in ipairs(config.providers(bufnr) or {}) do + local ok, providerModule = pcall(require, string.format('illuminate.providers.%s', provider)) + if ok and providerModule.is_ready(bufnr) then + return providerModule, provider + end + end + return nil +end + +function M.pause() + is_paused = true + M.refresh_references() +end + +function M.resume() + is_paused = false + M.refresh_references() +end + +function M.toggle() + is_paused = not is_paused + M.refresh_references() +end + +function M.toggle_buf(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + if paused_bufs[bufnr] then + paused_bufs[bufnr] = nil + else + paused_bufs[bufnr] = true + end + M.refresh_references() +end + +function M.pause_buf(bufnr) + paused_bufs[bufnr or vim.api.nvim_get_current_buf()] = true + M.refresh_references() +end + +function M.resume_buf(bufnr) + paused_bufs[bufnr or vim.api.nvim_get_current_buf()] = nil + M.refresh_references() +end + +function M.stop_buf(bufnr) + stopped_bufs[bufnr or vim.api.nvim_get_current_buf()] = true + M.refresh_references() +end + +function M.freeze_buf(bufnr) + frozen_bufs[bufnr or vim.api.nvim_get_current_buf()] = true +end + +function M.unfreeze_buf(bufnr) + frozen_bufs[bufnr or vim.api.nvim_get_current_buf()] = nil +end + +function M.toggle_freeze_buf(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + frozen_bufs[bufnr] = not frozen_bufs[bufnr] +end + +function M.invisible_buf(bufnr) + invisible_bufs[bufnr or vim.api.nvim_get_current_buf()] = true + M.refresh_references() +end + +function M.visible_buf(bufnr) + invisible_bufs[bufnr or vim.api.nvim_get_current_buf()] = nil + M.refresh_references() +end + +function M.toggle_visibility_buf(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + invisible_bufs[bufnr] = not invisible_bufs[bufnr] + M.refresh_references() +end + +function M.debug() + local bufnr = vim.api.nvim_get_current_buf() + print('buf_should_illuminate', bufnr, buf_should_illuminate(bufnr)) + print('config', vim.inspect(config.get_raw())) + print('started', started) + print('provider', M.get_provider(bufnr)) + print('`termguicolors`', vim.opt.termguicolors:get()) +end + +function M.is_paused() + return is_paused +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/goto.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/goto.lua new file mode 100644 index 00000000..af20d383 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/goto.lua @@ -0,0 +1,63 @@ +local util = require('illuminate.util') +local ref = require('illuminate.reference') +local engine = require('illuminate.engine') + +local M = {} + +function M.goto_next_reference(wrap) + local bufnr = vim.api.nvim_get_current_buf() + local winid = vim.api.nvim_get_current_win() + local cursor_pos = util.get_cursor_pos(winid) + + if #ref.buf_get_references(bufnr) == 0 then + return + end + + local i = ref.bisect_left(ref.buf_get_references(bufnr), cursor_pos) + i = i + 1 + if i > #ref.buf_get_references(bufnr) then + if wrap then + i = 1 + else + vim.api.nvim_err_writeln("E384: vim-illuminate: goto_next_reference hit BOTTOM of the references") + return + end + end + + local pos, _ = unpack(ref.buf_get_references(bufnr)[i]) + local new_cursor_pos = { pos[1] + 1, pos[2] } + vim.cmd('normal! m`') + engine.freeze_buf(bufnr) + vim.api.nvim_win_set_cursor(winid, new_cursor_pos) + engine.unfreeze_buf(bufnr) +end + +function M.goto_prev_reference(wrap) + local bufnr = vim.api.nvim_get_current_buf() + local winid = vim.api.nvim_get_current_win() + local cursor_pos = util.get_cursor_pos(winid) + + if #ref.buf_get_references(bufnr) == 0 then + return + end + + local i = ref.bisect_left(ref.buf_get_references(bufnr), cursor_pos) + i = i - 1 + if i == 0 then + if wrap then + i = #ref.buf_get_references(bufnr) + else + vim.api.nvim_err_writeln("E384: vim-illuminate: goto_prev_reference hit TOP of the references") + return + end + end + + local pos, _ = unpack(ref.buf_get_references(bufnr)[i]) + local new_cursor_pos = { pos[1] + 1, pos[2] } + vim.cmd('normal! m`') + engine.freeze_buf(bufnr) + vim.api.nvim_win_set_cursor(winid, new_cursor_pos) + engine.unfreeze_buf(bufnr) +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/highlight.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/highlight.lua new file mode 100644 index 00000000..68fa2a0b --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/highlight.lua @@ -0,0 +1,59 @@ +local util = require('illuminate.util') +local config = require('illuminate.config') +local ref = require('illuminate.reference') + +local M = {} + +local HL_NAMESPACE = vim.api.nvim_create_namespace('illuminate.highlight') + +local function kind_to_hl_group(kind) + return kind == vim.lsp.protocol.DocumentHighlightKind.Text and 'IlluminatedWordText' + or kind == vim.lsp.protocol.DocumentHighlightKind.Read and 'IlluminatedWordRead' + or kind == vim.lsp.protocol.DocumentHighlightKind.Write and 'IlluminatedWordWrite' + or 'IlluminatedWordText' +end + +function M.buf_highlight_references(bufnr, references) + if config.min_count_to_highlight() > #references then + return + end + + local cursor_pos = util.get_cursor_pos() + for _, reference in ipairs(references) do + if config.under_cursor(bufnr) or not ref.is_pos_in_ref(cursor_pos, reference) then + M.range( + bufnr, + reference[1], + reference[2], + reference[3] + ) + end + end +end + +function M.range(bufnr, start, finish, kind) + local region = vim.region(bufnr, start, finish, 'v', false) + for linenr, cols in pairs(region) do + if linenr == -1 then + linenr = 0 + end + local end_row + if cols[2] == -1 then + end_row = linenr + 1 + cols[2] = 0 + end + vim.api.nvim_buf_set_extmark(bufnr, HL_NAMESPACE, linenr, cols[1], { + hl_group = kind_to_hl_group(kind), + end_row = end_row, + end_col = cols[2], + priority = 199, + strict = false, + }) + end +end + +function M.buf_clear_references(bufnr) + vim.api.nvim_buf_clear_namespace(bufnr, HL_NAMESPACE, 0, -1) +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/providers/lsp.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/providers/lsp.lua new file mode 100644 index 00000000..cbd341a4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/providers/lsp.lua @@ -0,0 +1,130 @@ +local M = {} + +local bufs = {} + +local function _str_byteindex_enc(line, col, encoding) + if not encoding then + encoding = 'utf-16' + end + + if encoding == 'utf-8' then + if col then + return col + else + return #line + end + elseif encoding == 'utf-16' then + return vim.str_byteindex(line, col, true) + elseif encoding == 'utf-32' then + return vim.str_byteindex(line, col) + else + return col + end +end + +local function get_line_byte_from_position(bufnr, line, col, offset_encoding) + if col == 0 then + return col + end + + local lines = vim.api.nvim_buf_get_lines(bufnr, line, line + 1, false) + if not lines or #lines == 0 then + return col + end + local ok, result = pcall(_str_byteindex_enc, lines[1], col, offset_encoding) + if ok then + return result + end + return math.min(#(lines[1]), col) +end + +function M.get_references(bufnr) + if not bufs[bufnr] or not bufs[bufnr][3] then + return nil + end + + return bufs[bufnr][3] +end + +function M.is_ready(bufnr) + local supported = false + -- For nvim 0.10+ + if vim.lsp.get_clients then + supported = false + for _, client in ipairs(vim.lsp.get_clients({bufnr = bufnr})) do + if client and client.supports_method('textDocument/documentHighlight') then + supported = true + end + end + -- For older versions + elseif vim.lsp.for_each_buffer_client then + supported = false + vim.lsp.for_each_buffer_client(bufnr, function(client) + if client and client.supports_method('textDocument/documentHighlight') then + supported = true + end + end) + end + return supported +end + +function M.initiate_request(bufnr, winid) + local id = 1 + if bufs[bufnr] then + local prev_id, cancel_fn, references = unpack(bufs[bufnr]) + if references == nil then + pcall(cancel_fn) + end + id = prev_id + 1 + end + + local cancel_fn = vim.lsp.buf_request_all( + bufnr, + 'textDocument/documentHighlight', + vim.lsp.util.make_position_params(winid), + function(client_results) + if bufs[bufnr][1] ~= id then + return + end + if not vim.api.nvim_buf_is_valid(bufnr) then + bufs[bufnr][3] = {} + return + end + + local references = {} + for client_id, results in pairs(client_results) do + local client = vim.lsp.get_client_by_id(client_id) + if client and results['result'] then + for _, res in ipairs(results['result']) do + local start_col = get_line_byte_from_position( + bufnr, + res['range']['start']['line'], + res['range']['start']['character'], + res['offset_encoding'] + ) + local end_col = get_line_byte_from_position( + bufnr, + res['range']['end']['line'], + res['range']['end']['character'], + res['offset_encoding'] + ) + table.insert(references, { + { res['range']['start']['line'], start_col }, + { res['range']['end']['line'], end_col }, + res['kind'], + }) + end + end + end + + bufs[bufnr][3] = references + end + ) + + bufs[bufnr] = { + id, + cancel_fn, + } +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/providers/regex.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/providers/regex.lua new file mode 100644 index 00000000..8af29eb7 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/providers/regex.lua @@ -0,0 +1,74 @@ +local config = require('illuminate.config') +local util = require('illuminate.util') + +local M = {} + +local START_WORD_REGEX = vim.regex([[^\k*]]) +local END_WORD_REGEX = vim.regex([[\k*$]]) + +-- foo +-- foo +-- Foo +-- fOo +local function get_cur_word(bufnr, cursor) + local line = vim.api.nvim_buf_get_lines(bufnr, cursor[1], cursor[1] + 1, false)[1] + local left_part = string.sub(line, 0, cursor[2] + 1) + local right_part = string.sub(line, cursor[2] + 1) + local start_idx, _ = END_WORD_REGEX:match_str(left_part) + local _, end_idx = START_WORD_REGEX:match_str(right_part) + local word = string.format('%s%s', string.sub(left_part, start_idx + 1), string.sub(right_part, 2, end_idx)) + local modifiers = [[\V]] + if config.case_insensitive_regex() then + modifiers = modifiers .. [[\c]] + end + local ok, escaped = pcall(vim.fn.escape, word, [[/\]]) + if ok then + return modifiers .. [[\<]] .. escaped .. [[\>]] + end +end + +function M.get_references(bufnr, cursor) + local refs = {} + local ok, re = pcall(vim.regex, get_cur_word(bufnr, cursor)) + if not ok then + return refs + end + + local line_count = vim.api.nvim_buf_line_count(bufnr) + for i = 0, line_count - 1 do + local start_byte, end_byte = 0, 0 + while true do + local start_offset, end_offset = re:match_line(bufnr, i, end_byte) + if not start_offset then break end + + start_byte = end_byte + start_offset + end_byte = end_byte + end_offset + table.insert(refs, { + { i, start_byte }, + { i, end_byte }, + vim.lsp.protocol.DocumentHighlightKind.Text, + }) + end + end + + return refs +end + +function M.is_ready(bufnr) + local name = vim.fn.synIDattr( + vim.fn.synIDtrans( + vim.fn.synID(vim.fn.line('.'), vim.fn.col('.'), 1) + ), + 'name' + ) + if util.is_allowed( + config.provider_regex_syntax_allowlist(bufnr), + config.provider_regex_syntax_denylist(bufnr), + name + ) then + return true + end + return false +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/providers/treesitter.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/providers/treesitter.lua new file mode 100644 index 00000000..d3897b02 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/providers/treesitter.lua @@ -0,0 +1,51 @@ +local ts_utils = require('nvim-treesitter.ts_utils') +local locals = require('nvim-treesitter.locals') + +local M = {} + +local buf_attached = {} + +function M.get_references(bufnr) + local node_at_point = ts_utils.get_node_at_cursor() + if not node_at_point then + return + end + + local refs = {} + + local def_node, scope = locals.find_definition(node_at_point, bufnr) + if def_node ~= node_at_point then + local range = { def_node:range() } + table.insert(refs, { + { range[1], range[2] }, + { range[3], range[4] }, + vim.lsp.protocol.DocumentHighlightKind.Write, + }) + end + + local usages = locals.find_usages(def_node, scope, bufnr) + for _, node in ipairs(usages) do + local range = { node:range() } + table.insert(refs, { + { range[1], range[2] }, + { range[3], range[4] }, + vim.lsp.protocol.DocumentHighlightKind.Read, + }) + end + + return refs +end + +function M.is_ready(bufnr) + return buf_attached[bufnr] and vim.api.nvim_buf_get_option(bufnr, 'filetype') ~= 'yaml' +end + +function M.attach(bufnr) + buf_attached[bufnr] = true +end + +function M.detach(bufnr) + buf_attached[bufnr] = nil +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/reference.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/reference.lua new file mode 100644 index 00000000..1d2a19cc --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/reference.lua @@ -0,0 +1,91 @@ +local M = {} + +-- @table pos +-- @field 1 (number) line (0-indexed) +-- @field 2 (number) col (0-indexed) +-- +-- @table ref +-- @field 1 (table) start pos +-- @field 2 (table) end pos + +local buf_references = {} + +local function get_references(bufnr) + return buf_references[bufnr] or {} +end + +local function pos_before(pos1, pos2) + if pos1[1] < pos2[1] then return true end + if pos1[1] > pos2[1] then return false end + if pos1[2] < pos2[2] then return true end + return false +end + +local function pos_equal(pos1, pos2) + return pos1[1] == pos2[1] and pos1[2] == pos2[2] +end + +local function ref_before(ref1, ref2) + return pos_before(ref1[1], ref2[1]) or pos_equal(ref1[1], ref2[1]) and pos_before(ref1[2], ref2[2]) +end + +local function buf_sort_references(bufnr) + local should_sort = false + for i, ref in ipairs(get_references(bufnr)) do + if i > 1 then + if not ref_before(get_references(bufnr)[i - 1], ref) then + should_sort = true + break + end + end + end + + if should_sort then + table.sort(get_references(bufnr), ref_before) + end +end + +function M.is_pos_in_ref(pos, ref) + return (pos_before(ref[1], pos) or pos_equal(ref[1], pos)) and (pos_before(pos, ref[2]) or pos_equal(pos, ref[2])) +end + +function M.bisect_left(references, pos) + local l, r = 1, #references + 1 + while l < r do + local m = l + math.floor((r - l) / 2) + if pos_before(references[m][2], pos) then + l = m + 1 + else + r = m + end + end + return l +end + +function M.buf_get_references(bufnr) + return get_references(bufnr) +end + +function M.buf_set_references(bufnr, references) + buf_references[bufnr] = references + buf_sort_references(bufnr) +end + +function M.buf_cursor_in_references(bufnr, cursor_pos) + if not get_references(bufnr) then + return false + end + + local i = M.bisect_left(get_references(bufnr), cursor_pos) + + if i > #get_references(bufnr) then + return false + end + if not M.is_pos_in_ref(cursor_pos, get_references(bufnr)[i]) then + return false + end + + return true +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/textobj.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/textobj.lua new file mode 100644 index 00000000..3f10dd1b --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/textobj.lua @@ -0,0 +1,42 @@ +local util = require('illuminate.util') +local ref = require('illuminate.reference') + +local M = {} + +local visual_modes = { + ['v'] = true, + ['vs'] = true, + ['V'] = true, + ['Vs'] = true, + ['CTRL-V'] = true, + ['CTRL-Vs'] = true, + ['s'] = true, + ['S'] = true, + ['CTRL-S'] = true, +} + +function M.select() + local bufnr = vim.api.nvim_get_current_buf() + local winid = vim.api.nvim_get_current_win() + local cursor_pos = util.get_cursor_pos(winid) + + if #ref.buf_get_references(bufnr) == 0 then + return + end + + local i = ref.bisect_left(ref.buf_get_references(bufnr), cursor_pos) + if i > #ref.buf_get_references(bufnr) then + return + end + + local reference = ref.buf_get_references(bufnr)[i] + vim.api.nvim_win_set_cursor(winid, { reference[1][1] + 1, reference[1][2] }) + if not visual_modes[vim.api.nvim_get_mode().mode] then + vim.cmd('normal! v') + else + vim.cmd('normal! o') + end + vim.api.nvim_win_set_cursor(winid, { reference[2][1] + 1, reference[2][2] - 1 }) +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/util.lua b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/util.lua new file mode 100644 index 00000000..183af7b5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate/util.lua @@ -0,0 +1,51 @@ +local M = {} + +function M.get_cursor_pos(winid) + winid = winid or vim.api.nvim_get_current_win() + local cursor = vim.api.nvim_win_get_cursor(winid) + cursor[1] = cursor[1] - 1 -- we always want line to be 0-indexed + return cursor +end + +function M.list_to_set(list) + if list == nil then + return nil + end + + local set = {} + for _, v in pairs(list) do + set[v] = true + end + return set +end + +function M.is_allowed(allow_list, deny_list, thing) + if #allow_list == 0 and #deny_list == 0 then + return true + end + + if #deny_list > 0 then + return not vim.tbl_contains(deny_list, thing) + end + + return vim.tbl_contains(allow_list, thing) +end + +function M.tbl_get(tbl, expected_type, ...) + local cur = tbl + for _, key in ipairs({ ... }) do + if type(cur) ~= 'table' or cur[key] == nil then + return nil + end + + cur = cur[key] + end + + return type(cur) == expected_type and cur or nil +end + +function M.has_keymap(mode, lhs) + return vim.fn.mapcheck(lhs, mode) ~= '' +end + +return M diff --git a/config/neovim/store/lazy-plugins/vim-illuminate/plugin/illuminate.vim b/config/neovim/store/lazy-plugins/vim-illuminate/plugin/illuminate.vim new file mode 100644 index 00000000..26110ad0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-illuminate/plugin/illuminate.vim @@ -0,0 +1,88 @@ +" illuminate.vim - Vim plugin for selectively illuminating other uses of current word +" Maintainer: Adam P. Regasz-Rethy (RRethy) <rethy.spud@gmail.com> +" Version: 2.0 + +if exists('g:loaded_illuminate') + finish +endif + +let g:loaded_illuminate = 1 + +if has('nvim-0.7.2') && get(g:, 'Illuminate_useDeprecated', 0) != 1 +lua << EOF + local ok, ts = pcall(require, 'nvim-treesitter') + if ok then + ts.define_modules({ + illuminate = { + module_path = 'illuminate.providers.treesitter', + enable = true, + disable = {}, + is_supported = require('nvim-treesitter.query').has_locals, + } + }) + end + require('illuminate.engine').start() + vim.api.nvim_create_user_command('IlluminatePause', require('illuminate').pause, { bang = true }) + vim.api.nvim_create_user_command('IlluminateResume', require('illuminate').resume, { bang = true }) + vim.api.nvim_create_user_command('IlluminateToggle', require('illuminate').toggle, { bang = true }) + vim.api.nvim_create_user_command('IlluminatePauseBuf', require('illuminate').pause_buf, { bang = true }) + vim.api.nvim_create_user_command('IlluminateResumeBuf', require('illuminate').resume_buf, { bang = true }) + vim.api.nvim_create_user_command('IlluminateToggleBuf', require('illuminate').toggle_buf, { bang = true }) + vim.api.nvim_create_user_command('IlluminateDebug', require('illuminate').debug, { bang = true }) + + if not require('illuminate.util').has_keymap('n', '<a-n>') then + vim.keymap.set('n', '<a-n>', require('illuminate').goto_next_reference, { desc = "Move to next reference" }) + end + if not require('illuminate.util').has_keymap('n', '<a-p>') then + vim.keymap.set('n', '<a-p>', require('illuminate').goto_prev_reference, { desc = "Move to previous reference" }) + end + if not require('illuminate.util').has_keymap('o', '<a-i>') then + vim.keymap.set('o', '<a-i>', require('illuminate').textobj_select) + end + if not require('illuminate.util').has_keymap('x', '<a-i>') then + vim.keymap.set('x', '<a-i>', require('illuminate').textobj_select) + end +EOF + +lua require('illuminate').set_highlight_defaults() +augroup vim_illuminate_autocmds + autocmd! + autocmd ColorScheme * lua require('illuminate').set_highlight_defaults() +augroup END + +finish +end + +" Highlight group(s) {{{ +if !hlexists('illuminatedWord') + " this is for backwards compatibility + if !empty(get(g:, 'Illuminate_hl_link', '')) + exe get(g:, 'Illuminate_hl_link', '') + else + hi def link illuminatedWord cursorline + endif +endif +" }}} + +" Autocommands {{{ +if has('autocmd') + augroup illuminated_autocmd + autocmd! + autocmd CursorMoved,InsertLeave * call illuminate#on_cursor_moved() + autocmd WinLeave,BufLeave * call illuminate#on_leaving_autocmds() + autocmd CursorMovedI * call illuminate#on_cursor_moved_i() + autocmd InsertEnter * call illuminate#on_insert_entered() + augroup END +else + echoerr 'Illuminate requires Vim compiled with +autocmd' + finish +endif +" }}} + +" Commands {{{ +command! -nargs=0 -bang IlluminationDisable call illuminate#disable_illumination(<bang>0) +command! -nargs=0 -bang IlluminationEnable call illuminate#enable_illumination(<bang>0) +command! -nargs=0 -bang IlluminationToggle call illuminate#toggle_illumination(<bang>0) +" }}} Commands: + +" vim: foldlevel=1 foldmethod=marker diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/.gitignore b/config/neovim/store/lazy-plugins/vim-sandwich/.gitignore new file mode 100644 index 00000000..61bcf973 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/.gitignore @@ -0,0 +1,2 @@ +doc/tags +doc/tags-ja diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/.travis.yml b/config/neovim/store/lazy-plugins/vim-sandwich/.travis.yml new file mode 100644 index 00000000..ec774802 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/.travis.yml @@ -0,0 +1,17 @@ +language: generic + +env: + - PPA=yes + +install: + - if [ x"$PPA" == "xyes" ] ; then sudo add-apt-repository ppa:pi-rho/dev -y; fi + - sudo apt-get update -q + - sudo apt-get install vim-nox + - git clone https://github.com/thinca/vim-themis + +before_script: + - vim --version + - chmod +x ./test/dot-repeat/test_dot.sh + +script: + - ./vim-themis/bin/themis && ./vim-themis/bin/themis test/surround/test-surround.vim && ./test/dot-repeat/test_dot.sh diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/README.md b/config/neovim/store/lazy-plugins/vim-sandwich/README.md new file mode 100644 index 00000000..dbea6cf5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/README.md @@ -0,0 +1,242 @@ +vim-sandwich +============ +[![Build Status](https://travis-ci.org/machakann/vim-sandwich.svg)](https://travis-ci.org/machakann/vim-sandwich) +[![Build status](https://ci.appveyor.com/api/projects/status/8hgvi5410lceq53x/branch/master?svg=true)](https://ci.appveyor.com/project/machakann/vim-sandwich/branch/master) + +`sandwich.vim` is a plugin that makes it super easy to work with stuff that comes in pairs, like brackets, quotes, and even HTML or XML tags. You can quickly get rid of them, swap them out, or slap new ones around your text. + +# Examples + +Let's dive into some quick examples. If you're inside a string with double quotes and you hit `sr"'`, you'll swap those double quotes for single quotes. + + "Hello world!" -> 'Hello world!' + +Want to turn that into an HTML tag? Easy, just type `sr'<q>` and watch it transform. + + 'Hello world!' -> <q>Hello world!</q> + +To switch it back to double quotes, you'd do `srt"`. + + <q>Hello world!</q> -> "Hello world!" + +To strip away those quotes, just press `sd"`. + + "Hello world!" -> Hello world! + +Say you want to bracket the word "Hello", move your cursor there and press `saiw]`. + + Hello world! -> [Hello] world! + +Fancy braces with some breathing room? Type `sr]{`. + + [Hello] world! -> { Hello } world! + +Wrap the whole line in parentheses with `sasb` or `sas)`. + + { Hello } world! -> ({ Hello } world!) + +To get back to where you started, just do `sd{sd)`. + + ({ Hello } world!) -> Hello world! + +Highlight "Hello" with an HTML emphasis tag by typing `saiw<em>`. + + Hello world! -> <em>Hello</em> world! + +For a bigger change, like wrapping the whole line in a paragraph tag with a class, first select the line with `V` and then apply `S<p class="important">`. + + <em>Hello</em> world! -> <p class="important"><em>Hello</em> world!</p> + +This tool is a game-changer for editing HTML and XML in Vim, which is an area that doesn't have a ton of great tools right now. With vim-sandwich, adding, changing, or removing tag pairs is super simple. + +If you are using [vim-surround](https://github.com/tpope/vim-surround), you can use a preset keymappings similar as it. See [here](https://github.com/machakann/vim-sandwich/wiki/Introduce-vim-surround-keymappings) + +# Design + +This plugin provides functions to add/delete/replace surroundings of a sandwiched text. These functions are implemented genuinely by utilizing operator/textobject framework. Thus their action can be repeated by `.` command without any dependency. It consists of two parts, **operator-sandwich** and **textobj-sandwich**. + +### operator-sandwich +A sandwiched text could be resolved into two parts, {surrounding} and {surrounded text}. + +* Add surroundings: mapped to the key sequence `sa` + * {surrounded text} ---> {surrounding}{surrounded text}{surrounding} + +* Delete surroundings: mapped to the key sequence `sd` + * {surrounding}{surrounded text}{surrounding} ---> {surrounded text} + +* Replace surroundings: mapped to the key sequence `sr` + * {surrounding}{surrounded text}{surrounding} ---> {new surrounding}{surrounded text}{new surrounding} + +### textobj-sandwich + +* Search and select a sandwiched text automatically: mapped to the key sequence `ib` and `ab` +* Search and select a sandwiched text with query: mapped to the key sequence `is` and `as` + +`ib` and `is` selects {surrounded text}. `ab` and `as` selects {surrounded text} including {surrounding}s. +``` + |<----ib,is---->| +{surrounding}{surrounded text}{surrounding} +|<-----------------ab,as----------------->| +``` + +### Configuration +The point is that it would be nice to be shared the definitions of {surrounding}s pairs in all kinds of operations. User can freely add new settings to extend the functionality. If `g:sandwich#recipes` was defined, this plugin works with the settings inside. As a first step, it would be better to copy the default settings in `g:sandwich#default_recipes`. +```vim +let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +``` +Each setting, it is called `recipe`, is a set of a definition of {surrounding}s pair and options. The key named `buns` is used for the definition of {surrounding}. +``` +let g:sandwich#recipes += [{'buns': [{surrounding}, {surrounding}], 'option-name1': {value1}, 'option-name2': {value2} ...}] + +For example: {'buns': ['(', ')']} + foo ---> (foo) +``` + +Or there is a different way, use external textobjects to define {surrounding}s from the difference of two textobjects. +``` +let g:sandwich#recipes += [{'external': [{textobj-i}, {textobj-a}], 'option-name1': {value1}, 'option-name2': {value} ...}] + +For example: {'external': ['it', 'at']} + <title>foo ---> foo +``` + +# Features + +### Unique count handling +As for the default operators, the possible key input in normal mode is like this. +``` + [count1]{operator}[count2]{textobject} +``` +Default operators do not distinguish `[count1]` and `[count2]` but **operator-sandwich** does. `[count1]` is given for `{operators}` and `[count2]` is given for `{textobject}`. + +### Linewise and blockwise operations + +Operator-sandwich works linewise with the linewise-visual selection and linewise motions. + +```vim +" press Vsa( + foo ---> ( + foo + ) +``` + +Using `command` option, user can execute vim Ex-commands after an action. For example it can be used to adjust indent automatically. + +```vim +let g:sandwich#recipes += [ + \ { + \ 'buns' : ['{', '}'], + \ 'motionwise' : ['line'], + \ 'kind' : ['add'], + \ 'linewise' : 1, + \ 'command' : ["'[+1,']-1normal! >>"], + \ }, + \ { + \ 'buns' : ['{', '}'], + \ 'motionwise' : ['line'], + \ 'kind' : ['delete'], + \ 'linewise' : 1, + \ 'command' : ["'[,']normal! <<"], + \ } + \ ] + +" press Vsa{ + foo ---> { + foo + } + +" press V2jsd + { ---> foo + foo + } +``` + +Operator-sandwich also can work blockwise with the blockwise-visual selection and blockwise motions. + +```vim +" press 2j2lsa( + foo (foo) + bar ---> (bar) + baz (baz) +``` + +There is an option to skip white space `skip_space`, it is valid in default. Empty line is ignored. + +```vim +" press 3j$sa( + fooooooo (fooooooo) + baaaar ---> (baaaar) + + baaaz (baaaz) +``` + + + +### Expression surroundings and regular expression matching + +The option `expr` enables to evaluate surroundings (`buns`) before adding/deleting/replacing surroundings. The following recipe is an simple example to wrap texts by html style tags. + +```vim +let g:sandwich#recipes += [ + \ { + \ 'buns' : ['TagInput(1)', 'TagInput(0)'], + \ 'expr' : 1, + \ 'filetype': ['html'], + \ 'kind' : ['add', 'replace'], + \ 'action' : ['add'], + \ 'input' : ['t'], + \ }, + \ ] + +function! TagInput(is_head) abort + if a:is_head + let s:TagLast = input('Tag: ') + if s:TagLast !=# '' + let tag = printf('<%s>', s:TagLast) + else + throw 'OperatorSandwichCancel' + endif + else + let tag = printf('', matchstr(s:TagLast, '^\a[^[:blank:]>/]*')) + endif + return tag +endfunction +``` + +The option `regex` is to regard surroundings (`buns`) as regular expressions to match and delete/replace. The following recipe is an simple example to delete both ends of html tag. + +```vim +let g:sandwich#recipes += [ + \ { + \ 'buns' : ['<\a[^[:blank:]>/]*.\{-}>', + \ '/]*>'], + \ 'regex' : 1, + \ 'filetype': ['html'], + \ 'nesting' : 1, + \ 'input' : ['t'], + \ }, + \ ] +``` + +However the above example is not so accurate. Instead of the example, there are excellent built-in textobjects `it` and `at`, these external textobjects also can be utilized through `external`. + +```vim +let g:sandwich#recipes += [ + \ { + \ 'external': ['it', 'at'], + \ 'noremap' : 1, + \ 'filetype': ['html'], + \ 'input' : ['t'], + \ }, + \ ] +``` + +### Demo +![sandwich.vim](http://art61.photozou.jp/pub/986/3080986/photo/225500462_org.v1437577755.gif) + +# Pioneers +* [vim-surround](https://github.com/tpope/vim-surround) +* [vim-operator-surround](https://github.com/rhysd/vim-operator-surround) +* [vim-textobj-multiblock](https://github.com/osyo-manga/vim-textobj-multiblock) +* [vim-textobj-anyblock](https://github.com/rhysd/vim-textobj-anyblock) +* [vim-textobj-between](https://github.com/thinca/vim-textobj-between) diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/initex/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/initex/sandwich.vim new file mode 100644 index 00000000..4417fb8b --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/initex/sandwich.vim @@ -0,0 +1,18 @@ +if &compatible || exists('b:did_sandwich_initex_ftplugin') || get(g:, 'sandwich_no_initex_ftplugin', 0) + finish +endif + +runtime macros/sandwich/ftplugin/tex.vim + +augroup sandwich-event-FileType + autocmd! + execute 'autocmd FileType initex source ' . fnameescape(expand('')) +augroup END + +let b:did_sandwich_initex_ftplugin = 1 +if !exists('b:undo_ftplugin') + let b:undo_ftplugin = '' +else + let b:undo_ftplugin .= ' | ' +endif +let b:undo_ftplugin .= 'unlet b:did_sandwich_initex_ftplugin | call sandwich#util#ftrevert("tex")' diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/julia/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/julia/sandwich.vim new file mode 100644 index 00000000..6eb94438 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/julia/sandwich.vim @@ -0,0 +1,21 @@ +if &compatible || exists('b:did_sandwich_julia_ftplugin') || get(g:, 'sandwich_no_julia_ftplugin', 0) + finish +endif + +" To include multibyte characters +let b:sandwich_magicchar_f_patterns = [ + \ { + \ 'header' : '\%#=2\<[[:upper:][:lower:]_]\k*!\?', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] + +let b:did_sandwich_julia_ftplugin = 1 +if !exists('b:undo_ftplugin') + let b:undo_ftplugin = '' +else + let b:undo_ftplugin .= ' | ' +endif +let b:undo_ftplugin .= 'unlet! b:did_sandwich_julia_ftplugin b:sandwich_magicchar_f_patterns' diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/plaintex/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/plaintex/sandwich.vim new file mode 100644 index 00000000..c4fe5493 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/plaintex/sandwich.vim @@ -0,0 +1,18 @@ +if &compatible || exists('b:did_sandwich_plaintex_ftplugin') || get(g:, 'sandwich_no_plaintex_ftplugin', 0) + finish +endif + +runtime macros/sandwich/ftplugin/tex.vim + +augroup sandwich-event-FileType + autocmd! + execute 'autocmd FileType plaintex source ' . fnameescape(expand('')) +augroup END + +let b:did_sandwich_plaintex_ftplugin = 1 +if !exists('b:undo_ftplugin') + let b:undo_ftplugin = '' +else + let b:undo_ftplugin .= ' | ' +endif +let b:undo_ftplugin .= 'unlet b:did_sandwich_plaintex_ftplugin | call sandwich#util#ftrevert("tex")' diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/tex/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/tex/sandwich.vim new file mode 100644 index 00000000..33954c85 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/tex/sandwich.vim @@ -0,0 +1,18 @@ +if &compatible || exists('b:did_sandwich_tex_ftplugin') || get(g:, 'sandwich_no_tex_ftplugin', 0) + finish +endif + +runtime macros/sandwich/ftplugin/tex.vim + +augroup sandwich-event-FileType + autocmd! + execute 'autocmd FileType tex source ' . fnameescape(expand('')) +augroup END + +let b:did_sandwich_tex_ftplugin = 1 +if !exists('b:undo_ftplugin') + let b:undo_ftplugin = '' +else + let b:undo_ftplugin .= ' | ' +endif +let b:undo_ftplugin .= 'unlet b:did_sandwich_tex_ftplugin | call sandwich#util#ftrevert("tex")' diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/vim/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/vim/sandwich.vim new file mode 100644 index 00000000..8846c985 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/after/ftplugin/vim/sandwich.vim @@ -0,0 +1,20 @@ +if &compatible || exists('b:did_sandwich_vim_ftplugin') || get(g:, 'sandwich_no_vim_ftplugin', 0) + finish +endif + +let b:sandwich_magicchar_f_patterns = [ + \ { + \ 'header' : '\C\<\%(\h\|[sa]:\h\|g:[A-Z]\)\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] + +let b:did_sandwich_vim_ftplugin = 1 +if !exists('b:undo_ftplugin') + let b:undo_ftplugin = '' +else + let b:undo_ftplugin .= ' | ' +endif +let b:undo_ftplugin .= 'unlet! b:did_sandwich_vim_ftplugin b:sandwich_magicchar_f_patterns' diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich.vim new file mode 100644 index 00000000..da324753 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich.vim @@ -0,0 +1,607 @@ +" operator-sandwich: wrap by buns! +" TODO Give API to get information from operator object. +" It would be helpful for users in use of 'expr_filter' and 'command' option. +" TODO Add 'at' option + +" variables "{{{ +let s:constants = function('sandwich#constants#get') + +" patchs +if v:version > 704 || (v:version == 704 && has('patch237')) + let s:has_patch_7_4_392 = has('patch-7.4.392') +else + let s:has_patch_7_4_392 = v:version == 704 && has('patch392') +endif + +" Others +" NOTE: This would be updated in each operator functions (operator#sandwich#{add/delete/replce}) +let s:is_in_cmdline_window = 0 +" Current set operator +let s:operator = '' +"}}} +" highlight {{{ +function! s:default_highlight() abort + highlight default link OperatorSandwichBuns IncSearch + highlight default link OperatorSandwichAdd DiffAdd + highlight default link OperatorSandwichDelete DiffDelete + + if hlexists('OperatorSandwichStuff') + highlight default link OperatorSandwichChange OperatorSandwichStuff + else + " obsolete + highlight default link OperatorSandwichChange DiffChange + endif +endfunction +call s:default_highlight() + +augroup sandwich-event-ColorScheme + autocmd! + autocmd ColorScheme * call s:default_highlight() +augroup END +"}}} + +""" Public functions +" Prerequisite +function! operator#sandwich#prerequisite(kind, mode, ...) abort "{{{ + " make new operator object + let g:operator#sandwich#object = operator#sandwich#operator#new() + + " prerequisite + let operator = g:operator#sandwich#object + let operator.state = 1 + let operator.kind = a:kind + let operator.count = a:mode ==# 'x' ? max([1, v:prevcount]) : v:count1 + let operator.mode = a:mode + let operator.view = winsaveview() + let operator.cursor.keepable = 1 + let operator.cursor.keep[0:3] = getpos('.')[0:3] + let operator.opt = sandwich#opt#new(a:kind, {}, get(a:000, 0, {})) + let operator.recipes.arg = get(a:000, 1, []) + let operator.recipes.arg_given = a:0 > 1 + + let [operator.extended, operator.blockwidth] = s:blockwisevisual_info(a:mode) + + let &l:operatorfunc = 'operator#sandwich#' . a:kind + let s:operator = a:kind + return +endfunction +"}}} +function! operator#sandwich#keymap(kind, mode, ...) abort "{{{ + if a:0 == 0 + call operator#sandwich#prerequisite(a:kind, a:mode) + elseif a:0 == 1 + call operator#sandwich#prerequisite(a:kind, a:mode, a:1) + else + call operator#sandwich#prerequisite(a:kind, a:mode, a:1, a:2) + endif + + let cmd = a:mode ==# 'x' ? 'gvg@' : 'g@' + call feedkeys(cmd, 'inx') + return +endfunction +"}}} +function! s:blockwisevisual_info(mode) abort "{{{ + if a:mode ==# 'x' && visualmode() ==# "\" + " The case for blockwise selections in visual mode + " NOTE: 'extended' could be recorded safely only at here. Do not move. + let registers = s:saveregisters() + let lazyredraw = &lazyredraw + set lazyredraw + let view = winsaveview() + try + normal! gv + let extended = winsaveview().curswant == s:constants('colmax') + silent noautocmd normal! ""y + let regtype = getregtype('"') + finally + call winrestview(view) + let &lazyredraw = lazyredraw + call s:restoreregisters(registers) + endtry + let blockwidth = str2nr(regtype[1:]) + else + let extended = 0 + let blockwidth = 0 + endif + return [extended, blockwidth] +endfunction +"}}} +function! s:saveregisters() abort "{{{ + let registers = {} + let registers['0'] = s:getregister('0') + let registers['"'] = s:getregister('"') + return registers +endfunction +"}}} +function! s:restoreregisters(registers) abort "{{{ + for [register, contains] in items(a:registers) + call s:setregister(register, contains) + endfor +endfunction +"}}} +function! s:getregister(register) abort "{{{ + return [getreg(a:register), getregtype(a:register)] +endfunction +"}}} +function! s:setregister(register, contains) abort "{{{ + let [value, options] = a:contains + return setreg(a:register, value, options) +endfunction +"}}} + +" Operator functions +function! operator#sandwich#add(motionwise, ...) abort "{{{ + call s:do('add', a:motionwise, 'OperatorSandwichAddPre', 'OperatorSandwichAddPost') +endfunction +"}}} +function! operator#sandwich#delete(motionwise, ...) abort "{{{ + call s:do('delete', a:motionwise, 'OperatorSandwichDeletePre', 'OperatorSandwichDeletePost') +endfunction +"}}} +function! operator#sandwich#replace(motionwise, ...) abort "{{{ + call s:do('replace', a:motionwise, 'OperatorSandwichReplacePre', 'OperatorSandwichReplacePost') +endfunction +"}}} +function! s:do(kind, motionwise, AutocmdPre, AutocmdPost) abort "{{{ + let s:operator = '' + if exists('g:operator#sandwich#object') + let operator = g:operator#sandwich#object + let messenger = sandwich#messenger#new() + let defaultopt = s:default_options(a:kind, a:motionwise) + call operator.opt.update('default', defaultopt) + call s:update_is_in_cmdline_window() + call s:doautocmd(a:AutocmdPre) + call operator.execute(a:motionwise) + call s:doautocmd(a:AutocmdPost) + call messenger.notify('operator-sandwich: ') + endif +endfunction +"}}} +" function! s:update_is_in_cmdline_window() abort "{{{ +if s:has_patch_7_4_392 + function! s:update_is_in_cmdline_window() abort + let s:is_in_cmdline_window = getcmdwintype() !=# '' + endfunction +else + function! s:update_is_in_cmdline_window() abort + let s:is_in_cmdline_window = 0 + try + execute 'tabnext ' . tabpagenr() + catch /^Vim\%((\a\+)\)\=:E11/ + let s:is_in_cmdline_window = 1 + catch + endtry + endfunction +endif +"}}} +function! s:doautocmd(name) abort "{{{ + let view = s:saveview() + try + if exists('#User#' . a:name) + execute 'doautocmd User ' . a:name + endif + catch + let errormsg = printf('operator-sandwich: An error occurred in autocmd %s. [%s]', a:name, v:exception) + echoerr errormsg + finally + call s:restview(view, a:name) + endtry +endfunction +"}}} +function! s:saveview() abort "{{{ + return [tabpagenr(), winnr(), winsaveview(), getpos("'["), getpos("']")] +endfunction +"}}} +function! s:restview(view, name) abort "{{{ + let [tabpagenr, winnr, view, modhead, modtail] = a:view + + if s:is_in_cmdline_window + " in cmdline-window + return + endif + + " tabpage + try + execute 'tabnext ' . tabpagenr + if tabpagenr() != tabpagenr + throw 'OperatorSandwichError:CouldNotRestoreTabpage' + endif + catch /^OperatorSandwichError:CouldNotRestoreTabpage/ + let errormsg = printf('operator-sandwich: Could not have restored tabpage after autocmd %s.', a:name) + echoerr errormsg + endtry + + " window + try + execute winnr . 'wincmd w' + catch /^Vim\%((\a\+)\)\=:E16/ + let errormsg = printf('operator-sandwich: Could not have restored window after autocmd %s.', a:name) + echoerr errormsg + endtry + " view + call winrestview(view) + " marks + call setpos("'[", modhead) + call setpos("']", modtail) +endfunction +"}}} + +" For the query1st series mappings +function! operator#sandwich#query1st(kind, mode, ...) abort "{{{ + if a:kind !=# 'add' && a:kind !=# 'replace' + return + endif + + " prerequisite + let arg_opt = get(a:000, 0, {}) + let arg_recipes = get(a:000, 1, []) + call operator#sandwich#prerequisite(a:kind, a:mode, arg_opt, arg_recipes) + let operator = g:operator#sandwich#object + " NOTE: force to set highlight=0 and query_once=1 + call operator.opt.update('default', {'highlight': 0, 'query_once': 1, 'expr': 0, 'listexpr': 0}) + let operator.recipes.arg_given = a:0 > 1 + + let stuff = operator#sandwich#stuff#new() + call stuff.initialize(operator.count, operator.cursor, operator.modmark) + let operator.basket = [stuff] + + " pick 'recipe' up and query prefered buns + call operator.recipes.integrate(a:kind, 'all', a:mode) + for i in range(operator.count) + let opt = operator.opt + let recipe = operator.query() + let operator.recipes.dog_ear += [recipe] + if !has_key(recipe, 'buns') || recipe.buns == [] + break + endif + + call opt.update('recipe_add', recipe) + if i == 0 && operator.count > 1 && opt.of('query_once') + call operator.recipes.fill(recipe, operator.count) + break + endif + endfor + + if filter(copy(operator.recipes.dog_ear), 'has_key(v:val, "buns")') != [] + let operator.state = 0 + let cmd = a:mode ==# 'x' + \ ? "\(operator-sandwich-gv)\(operator-sandwich-g@)" + \ : "\(operator-sandwich-g@)" + call feedkeys(cmd, 'im') + else + unlet g:operator#sandwich#object + endif + return +endfunction +"}}} + +" Supplementary keymappings +function! operator#sandwich#synchro_count() abort "{{{ + if exists('g:operator#sandwich#object') + return g:operator#sandwich#object.count + else + return '' + endif +endfunction +"}}} +function! operator#sandwich#release_count() abort "{{{ + if exists('g:operator#sandwich#object') + let l:count = g:operator#sandwich#object.count + let g:operator#sandwich#object.count = 1 + return l:count + else + return '' + endif +endfunction +"}}} +function! operator#sandwich#squash_count() abort "{{{ + if exists('g:operator#sandwich#object') + let g:operator#sandwich#object.count = 1 + endif + return '' +endfunction +"}}} +function! operator#sandwich#predot() abort "{{{ + if exists('g:operator#sandwich#object') + let operator = g:operator#sandwich#object + let operator.cursor.keepable = 1 + let operator.cursor.keep[0:3] = getpos('.')[0:3] + endif + return '' +endfunction +"}}} +function! operator#sandwich#dot() abort "{{{ + call operator#sandwich#predot() + return '.' +endfunction +"}}} + +" visualrepeat.vim (vimscript #3848) support +function! operator#sandwich#visualrepeat(kind) abort "{{{ + let operator = g:operator#sandwich#object + + let original_mode = operator.mode + let original_extended = operator.extended + let original_blockwidth = operator.blockwidth + + let operator.mode = 'x' + let [operator.extended, operator.blockwidth] = s:blockwisevisual_info('x') + try + normal! gv + let operator.cursor.keepable = 1 + let operator.cursor.keep[0:3] = getpos('.')[0:3] + + let l:count = v:count ? v:count : '' + let &l:operatorfunc = 'operator#sandwich#' . a:kind + let cmd = printf("normal! %sg@", l:count) + execute cmd + finally + let operator.mode = original_mode + let operator.extended = original_extended + let operator.blockwidth = original_blockwidth + endtry +endfunction +"}}} + +" API +function! operator#sandwich#show(...) abort "{{{ + if !exists('g:operator#sandwich#object') || !g:operator#sandwich#object.at_work + echoerr 'operator-sandwich: Not in an operator-sandwich operation!' + return 1 + endif + + let operator = g:operator#sandwich#object + let kind = operator.kind + let opt = operator.opt + let place = get(a:000, 0, '') + if kind ==# 'add' + if place ==# '' + let place = 'stuff' + endif + if place ==# 'added' + let hi_group = s:get_ifnotempty(a:000, 1, 'OperatorSandwichAdd') + else + let hi_group = opt.of('highlight') >= 2 + \ ? s:get_ifnotempty(a:000, 1, 'OperatorSandwichChange') + \ : s:get_ifnotempty(a:000, 1, 'OperatorSandwichBuns') + endif + elseif kind ==# 'delete' + if place ==# '' + let place = 'target' + endif + let hi_group = opt.of('highlight') >= 2 + \ ? s:get_ifnotempty(a:000, 1, 'OperatorSandwichDelete') + \ : s:get_ifnotempty(a:000, 1, 'OperatorSandwichBuns') + elseif kind ==# 'replace' + if place ==# '' + let place = 'target' + endif + if place ==# 'added' + let hi_group = s:get_ifnotempty(a:000, 1, 'OperatorSandwichAdd') + elseif place ==# 'target' + let hi_group = opt.of('highlight') >= 2 + \ ? s:get_ifnotempty(a:000, 1, 'OperatorSandwichDelete') + \ : s:get_ifnotempty(a:000, 1, 'OperatorSandwichBuns') + else + let hi_group = opt.of('highlight') >= 2 + \ ? s:get_ifnotempty(a:000, 1, 'OperatorSandwichChange') + \ : s:get_ifnotempty(a:000, 1, 'OperatorSandwichBuns') + endif + else + return 1 + endif + let forcibly = get(a:000, 2, 0) + return operator.show(place, hi_group, forcibly) +endfunction +"}}} +function! operator#sandwich#quench(...) abort "{{{ + if exists('g:operator#sandwich#object') + let operator = g:operator#sandwich#object + let kind = operator.kind + let place = get(a:000, 0, '') + if place ==# '' + if kind ==# 'add' + let place = 'stuff' + elseif kind ==# 'delete' + let place = 'target' + elseif kind ==# 'replace' + let place = 'target' + else + return 1 + endif + endif + return operator.quench(place) + endif +endfunction +"}}} +function! operator#sandwich#get_info(...) abort "{{{ + if !exists('g:operator#sandwich#object') || !g:operator#sandwich#object.at_work + echoerr 'operator-sandwich: Not in an operator-sandwich operation!' + return 1 + endif + + let info = get(a:000, 0, '') + if a:0 == 0 || info ==# '' + return g:operator#sandwich#object + elseif info ==# 'state' || info ==# 'kind' || info ==# 'count' || info ==# 'mode' || info ==# 'motionwise' + return g:operator#sandwich#object[info] + else + echoerr printf('operator-sandwich: Identifier "%s" is not supported.', string(info)) + return 1 + endif +endfunction +"}}} +function! operator#sandwich#kind() abort "{{{ + return exists('g:operator#sandwich#object') && g:operator#sandwich#object.at_work + \ ? g:operator#sandwich#object.kind + \ : s:operator +endfunction +"}}} +function! s:get_ifnotempty(list, idx, default) abort "{{{ + let item = get(a:list, a:idx, '') + if item ==# '' + let item = a:default + endif + return item +endfunction +"}}} + +" For internal communication +function! operator#sandwich#is_in_cmd_window() abort "{{{ + return s:is_in_cmdline_window +endfunction +"}}} +function! operator#sandwich#synchronize(kind, recipe) abort "{{{ + if exists('g:operator#sandwich#object') && !empty(a:recipe) + let g:operator#sandwich#object.recipes.synchro.on = 1 + let g:operator#sandwich#object.recipes.synchro.kind = a:kind + let g:operator#sandwich#object.recipes.synchro.recipe = [a:recipe] + endif +endfunction +"}}} + +" recipes "{{{ +function! operator#sandwich#get_recipes() abort "{{{ + if exists('b:operator_sandwich_recipes') + let recipes = b:operator_sandwich_recipes + elseif exists('g:operator#sandwich#recipes') + let recipes = g:operator#sandwich#recipes + else + let recipes = g:operator#sandwich#default_recipes + endif + return deepcopy(recipes) +endfunction +"}}} +if exists('g:operator#sandwich#default_recipes') + unlockvar! g:operator#sandwich#default_recipes +endif +let g:operator#sandwich#default_recipes = [] +lockvar! g:operator#sandwich#default_recipes +"}}} + +" options "{{{ +function! s:default_options(kind, motionwise) abort "{{{ + return get(b:, 'operator_sandwich_options', g:operator#sandwich#options)[a:kind][a:motionwise] +endfunction +"}}} +function! s:initialize_options(...) abort "{{{ + let manner = a:0 ? a:1 : 'keep' + let g:operator#sandwich#options = get(g:, 'operator#sandwich#options', {}) + for kind in ['add', 'delete', 'replace'] + if !has_key(g:operator#sandwich#options, kind) + let g:operator#sandwich#options[kind] = {} + endif + for motionwise in ['char', 'line', 'block'] + if !has_key(g:operator#sandwich#options[kind], motionwise) + let g:operator#sandwich#options[kind][motionwise] = {} + endif + call extend(g:operator#sandwich#options[kind][motionwise], + \ sandwich#opt#defaults(kind, motionwise), manner) + endfor + endfor +endfunction +call s:initialize_options() +"}}} +function! operator#sandwich#set_default() abort "{{{ + call s:initialize_options('force') +endfunction +"}}} +function! operator#sandwich#set(kind, motionwise, option, value) abort "{{{ + if s:argument_error(a:kind, a:motionwise, a:option, a:value) + return + endif + + if a:kind ==# 'all' + let kinds = ['add', 'delete', 'replace'] + else + let kinds = [a:kind] + endif + + if a:motionwise ==# 'all' + let motionwises = ['char', 'line', 'block'] + else + let motionwises = [a:motionwise] + endif + + call s:set_option_value(g:operator#sandwich#options, kinds, motionwises, a:option, a:value) +endfunction +"}}} +function! operator#sandwich#setlocal(kind, motionwise, option, value) abort "{{{ + if s:argument_error(a:kind, a:motionwise, a:option, a:value) + return + endif + + if !exists('b:operator_sandwich_options') + let b:operator_sandwich_options = deepcopy(g:operator#sandwich#options) + endif + + if a:kind ==# 'all' + let kinds = ['add', 'delete', 'replace'] + else + let kinds = [a:kind] + endif + + if a:motionwise ==# 'all' + let motionwises = ['char', 'line', 'block'] + else + let motionwises = [a:motionwise] + endif + + call s:set_option_value(b:operator_sandwich_options, kinds, motionwises, a:option, a:value) +endfunction +"}}} +function! s:argument_error(kind, motionwise, option, value) abort "{{{ + if !(a:kind ==# 'add' || a:kind ==# 'delete' || a:kind ==# 'replace' || a:kind ==# 'all') + echohl WarningMsg + echomsg 'Invalid kind "' . a:kind . '".' + echohl NONE + return 1 + endif + + if !(a:motionwise ==# 'char' || a:motionwise ==# 'line' || a:motionwise ==# 'block' || a:motionwise ==# 'all') + echohl WarningMsg + echomsg 'Invalid motion-wise "' . a:motionwise . '".' + echohl NONE + return 1 + endif + + if a:kind !=# 'all' && a:motionwise !=# 'all' + let defaults = sandwich#opt#defaults(a:kind, a:motionwise) + if filter(keys(defaults), 'v:val ==# a:option') == [] + echohl WarningMsg + echomsg 'Invalid option name "' . a:option . '".' + echohl NONE + return 1 + endif + + if a:option !~# 'indentkeys[-+]\?' && type(a:value) != type(defaults[a:option]) + echohl WarningMsg + echomsg 'Invalid type of value. ' . string(a:value) + echohl NONE + return 1 + endif + endif + return 0 +endfunction +"}}} +function! s:set_option_value(dest, kinds, motionwises, option, value) abort "{{{ + for kind in a:kinds + for motionwise in a:motionwises + let defaults = sandwich#opt#defaults(kind, motionwise) + if filter(keys(defaults), 'v:val ==# a:option') != [] + if a:option =~# 'indentkeys[-+]\?' || type(a:value) == type(defaults[a:option]) + let a:dest[kind][motionwise][a:option] = a:value + endif + endif + endfor + endfor +endfunction +"}}} +"}}} + +unlet! g:operator#sandwich#object + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/act.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/act.vim new file mode 100644 index 00000000..1ffb928b --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/act.vim @@ -0,0 +1,836 @@ +" act object - editing buffer + +let s:lib = operator#sandwich#lib#import() + +" variables "{{{ +let s:constants = function('sandwich#constants#get') +let s:TRUE = 1 +let s:FALSE = 0 +function! s:SID() abort + return matchstr(expand(''), '\zs\d\+\ze_SID$') +endfunction +let s:SNR = printf("\%s_", s:SID()) +delfunction s:SID + +nnoremap (i) i +nnoremap (o) o +nnoremap (O) O + +let s:KEY_i = printf('%s(i)', s:SNR) +let s:KEY_o = printf('%s(o)', s:SNR) +let s:KEY_O = printf('%s(O)', s:SNR) + +" null valiables +let s:null_pos = [0, 0, 0, 0] +let s:null_4pos = { + \ 'head1': copy(s:null_pos), + \ 'tail1': copy(s:null_pos), + \ 'head2': copy(s:null_pos), + \ 'tail2': copy(s:null_pos), + \ } + +" types +let s:type_str = type('') + +" features +let s:has_gui_running = has('gui_running') +"}}} + +function! operator#sandwich#act#new() abort "{{{ + return deepcopy(s:act) +endfunction +"}}} + +" s:act "{{{ +let s:act = { + \ 'cursor' : {}, + \ 'modmark': {}, + \ 'opt' : {}, + \ 'success': 0, + \ 'added' : [], + \ } +"}}} +function! s:act.initialize(cursor, modmark, added) dict abort "{{{ + let self.cursor = a:cursor + let self.modmark = a:modmark + let self.opt = {} + let self.added = a:added + let self.success = 0 +endfunction +"}}} +function! s:act.add_pair(buns, stuff, undojoin) dict abort "{{{ + let target = a:stuff.target + let edges = a:stuff.edges + let modmark = self.modmark + let opt = self.opt + let indent = [0, 0] + let is_linewise = [0, 0] + + if s:lib.is_valid_4pos(target) && s:lib.is_equal_or_ahead(target.head2, target.head1) + if target.head2[2] != col([target.head2[1], '$']) + let target.head2[0:3] = s:lib.get_right_pos(target.head2) + endif + + let indentopt = s:set_indent(opt) + let messenger = sandwich#messenger#get() + try + let pos = target.head1 + let [is_linewise[0], indent[0], head1, tail1] = s:add_former(a:buns, pos, opt, a:undojoin) + let pos = s:push1(copy(target.head2), target, a:buns, indent, is_linewise) + let [is_linewise[1], indent[1], head2, tail2] = s:add_latter(a:buns, pos, opt) + catch /^Vim\%((\a\+)\)\=:E21/ + call messenger.notice.queue(['Cannot make changes to read-only buffer.', 'WarningMsg']) + throw 'OperatorSandwichError:ReadOnly' + finally + call s:restore_indent(indentopt) + endtry + let [mod_head, mod_tail] = s:execute_command(head1, tail2, opt.of('command')) + + if opt.of('highlight', '') >= 3 + call map(self.added, 's:shift_added("s:shift_for_add", v:val, target, a:buns, indent, is_linewise)') + call add(self.added, { + \ 'head1': head1, + \ 'tail1': s:added_tail(head1, tail1, is_linewise[0]), + \ 'head2': head2, + \ 'tail2': s:added_tail(head2, tail2, is_linewise[1]), + \ 'linewise': is_linewise, + \ }) + endif + + " update modmark + if modmark.head == s:null_pos || s:lib.is_ahead(modmark.head, mod_head) + let modmark.head = mod_head + endif + if modmark.tail == s:null_pos + let modmark.tail = mod_tail + else + call s:shift_for_add(modmark.tail, target, a:buns, indent, is_linewise) + if s:lib.is_ahead(mod_tail, modmark.tail) + let modmark.tail = mod_tail + endif + endif + + " update cursor positions + call s:shift_for_add(self.cursor.inner_head, target, a:buns, indent, is_linewise) + call s:shift_for_add(self.cursor.keep, target, a:buns, indent, is_linewise) + call s:shift_for_add(self.cursor.inner_tail, target, a:buns, indent, is_linewise) + + " update next target positions + let edges.head = copy(head1) + let edges.tail = s:lib.get_left_pos(tail2) + + let self.success = 1 + endif + return self.success +endfunction +"}}} +function! s:act.delete_pair(stuff, modified) dict abort "{{{ + let target = a:stuff.target + let edges = a:stuff.edges + let modmark = self.modmark + let opt = self.opt + + if s:lib.is_valid_4pos(target) && s:lib.is_ahead(target.head2, target.tail1) + let reg = ['"', getreg('"'), getregtype('"')] + let deletion = ['', ''] + let is_linewise = [0, 0] + let messenger = sandwich#messenger#get() + try + let former_head = target.head1 + let former_tail = target.tail1 + let latter_head = target.head2 + let [deletion[0], is_linewise[0], head] = s:delete_former(former_head, former_tail, latter_head, opt) + + let latter_head = s:pull1(copy(target.head2), target, deletion, is_linewise) + let latter_tail = s:pull1(copy(target.tail2), target, deletion, is_linewise) + let [deletion[1], is_linewise[1], tail] = s:delete_latter(latter_head, latter_tail, former_head, opt) + catch /^Vim\%((\a\+)\)\=:E21/ + call messenger.notice.queue(['Cannot make changes to read-only buffer.', 'WarningMsg']) + throw 'OperatorSandwichError:ReadOnly' + finally + call call('setreg', reg) + endtry + let [mod_head, mod_tail] = s:execute_command(head, tail, opt.of('command')) + + " update modmark + if modmark.head == s:null_pos || s:lib.is_ahead(modmark.head, mod_head) + let modmark.head = mod_head + endif + " NOTE: Probably, there is no possibility to delete breakings along multiple acts. + if !a:modified + if modmark.tail == s:null_pos + let modmark.tail = mod_tail + else + call s:shift_for_delete(modmark.tail, target, deletion, is_linewise) + if mod_tail[1] >= modmark.tail[1] + let modmark.tail = mod_tail + endif + endif + endif + + " update cursor positions + call s:shift_for_delete(self.cursor.inner_head, target, deletion, is_linewise) + call s:shift_for_delete(self.cursor.keep, target, deletion, is_linewise) + call s:shift_for_delete(self.cursor.inner_tail, target, deletion, is_linewise) + + " update target positions + let edges.head = copy(head) + let edges.tail = s:lib.get_left_pos(tail) + + let self.success = 1 + endif + return self.success +endfunction +"}}} +function! s:act.replace_pair(buns, stuff, undojoin, modified) dict abort "{{{ + let target = a:stuff.target + let edges = a:stuff.edges + let modmark = self.modmark + let opt = self.opt + + if s:lib.is_valid_4pos(target) && s:lib.is_ahead(target.head2, target.tail1) + set virtualedit= + let next_head = s:lib.get_right_pos(target.tail1) + let next_tail = s:lib.get_left_pos(target.head2) + set virtualedit=onemore + + let reg = ['"', getreg('"'), getregtype('"')] + let deletion = ['', ''] + let indent = [0, 0] + let is_linewise = [0, 0] + let indentopt = s:set_indent(opt) + let messenger = sandwich#messenger#get() + try + let within_a_line = target.tail1[1] == target.head2[1] + let former_head = target.head1 + let former_tail = target.tail1 + let latter_head = copy(target.head2) + let latter_tail = copy(target.tail2) + let [deletion[0], is_linewise[0], indent[0], head1, tail1] = s:replace_former(a:buns, former_head, former_tail, within_a_line, opt, a:undojoin) + + call s:pull1(latter_head, target, deletion, is_linewise) + call s:push1(latter_head, target, a:buns, indent, is_linewise) + call s:pull1(latter_tail, target, deletion, is_linewise) + call s:push1(latter_tail, target, a:buns, indent, is_linewise) + let [deletion[1], is_linewise[1], indent[1], head2, tail2] = s:replace_latter(a:buns, latter_head, latter_tail, within_a_line, opt) + catch /^Vim\%((\a\+)\)\=:E21/ + call messenger.notice.queue(['Cannot make changes to read-only buffer.', 'WarningMsg']) + throw 'OperatorSandwichError:ReadOnly' + finally + call call('setreg', reg) + call s:restore_indent(indentopt) + endtry + let [mod_head, mod_tail] = s:execute_command(head1, tail2, opt.of('command')) + + if opt.of('highlight', '') >= 3 + call map(self.added, 's:shift_added("s:shift_for_replace", v:val, target, a:buns, deletion, indent, is_linewise)') + call add(self.added, { + \ 'head1': head1, + \ 'tail1': s:lib.get_left_pos(tail1), + \ 'head2': head2, + \ 'tail2': s:lib.get_left_pos(tail2), + \ 'linewise': is_linewise + \ }) + endif + + " update modmark + if modmark.head == s:null_pos || s:lib.is_ahead(modmark.head, mod_head) + let modmark.head = copy(mod_head) + endif + if !a:modified + if modmark.tail == s:null_pos + let modmark.tail = copy(mod_tail) + else + call s:shift_for_replace(modmark.tail, target, a:buns, deletion, indent, is_linewise) + if modmark.tail[1] < mod_tail[1] + let modmark.tail = copy(mod_tail) + endif + endif + endif + + " update cursor positions + call s:shift_for_replace(self.cursor.keep, target, a:buns, deletion, indent, is_linewise) + call s:shift_for_replace(next_head, target, a:buns, deletion, indent, is_linewise) + call s:shift_for_replace(next_tail, target, a:buns, deletion, indent, is_linewise) + if self.cursor.inner_head == s:null_pos || target.head1[1] <= self.cursor.inner_head[1] + let self.cursor.inner_head = copy(next_head) + endif + if self.cursor.inner_tail == s:null_pos + let self.cursor.inner_tail = copy(next_tail) + else + call s:shift_for_replace(self.cursor.inner_tail, target, a:buns, deletion, indent, is_linewise) + if self.cursor.inner_tail[1] <= next_tail[1] + let self.cursor.inner_tail = copy(next_tail) + endif + endif + + " update target positions + let edges.head = next_head + let edges.tail = next_tail + + let self.success = 1 + endif + return self.success +endfunction +"}}} + +" supplemental classes +" Indent class {{{ +let s:Indent = { + \ 'len': 0, + \ 'str': '', + \ 'savedstr': '', + \ 'linehead': s:FALSE + \ } +function! s:Indent(pos, opt) abort "{{{ + let line = getline(a:pos[1]) + let indent = deepcopy(s:Indent) + if a:pos[2] == 1 + let indent.linehead = s:TRUE + return indent + endif + + let indent.str = matchstr(line, '^\s*') + let indent.len = strlen(indent.str) + if a:pos[2] <= indent.len + " shorten if cursor is in indentation + let indent.str = indent.str[: a:pos[2] - 2] + let indent.len = strlen(indent.str) + let indent.linehead = s:TRUE + endif + + if a:opt.of('linewise') && a:opt.of('autoindent') == 4 + let indent.savedstr = indent.str + endif + return indent +endfunction "}}} +function! s:Indent.diff(addition) abort "{{{ + let addition = split(a:addition, '\m\%(\n\|\r\|\r\n\)', 1) + if len(addition) == 1 + let indentstr = matchstr(getline("'["), '\m^\s*') + let indentlen = strlen(indentstr) + if self.linehead + let indentlen -= strlen(matchstr(addition[0], '\m^\s*')) + endif + let diff = indentlen - self.len + else + let indentstr = matchstr(getline("']"), '\m^\s*') + let indentlen = strlen(indentstr) + let diff = indentlen - strlen(matchstr(addition[-1], '\m^\s*')) + endif + return diff +endfunction "}}} +"}}} + +" private functions +function! s:set_indent(opt) abort "{{{ + let indentopt = { + \ 'autoindent': { + \ 'restore': 0, + \ 'value' : [&l:autoindent, &l:smartindent, &l:cindent, &l:indentexpr], + \ }, + \ 'indentkeys': { + \ 'restore': 0, + \ 'name' : '', + \ 'value' : '', + \ }, + \ } + + " set autoindent options + if a:opt.of('autoindent') == 0 || a:opt.of('autoindent') == 4 + let [&l:autoindent, &l:smartindent, &l:cindent, &l:indentexpr] = [0, 0, 0, ''] + let indentopt.autoindent.restore = 1 + elseif a:opt.of('autoindent') == 1 + let [&l:autoindent, &l:smartindent, &l:cindent, &l:indentexpr] = [1, 0, 0, ''] + let indentopt.autoindent.restore = 1 + elseif a:opt.of('autoindent') == 2 + " NOTE: 'Smartindent' requires 'autoindent'. :help 'smartindent' + let [&l:autoindent, &l:smartindent, &l:cindent, &l:indentexpr] = [1, 1, 0, ''] + let indentopt.autoindent.restore = 1 + elseif a:opt.of('autoindent') == 3 + let [&l:cindent, &l:indentexpr] = [1, ''] + let indentopt.autoindent.restore = 1 + endif + + " set indentkeys + if &l:indentexpr !=# '' + let indentopt.indentkeys.name = 'indentkeys' + let indentopt.indentkeys.value = &l:indentkeys + else + let indentopt.indentkeys.name = 'cinkeys' + let indentopt.indentkeys.value = &l:cinkeys + endif + + let val = a:opt.of('indentkeys') + if type(val) == s:type_str + execute printf('setlocal %s=%s', indentopt.indentkeys.name, val) + let indentopt.indentkeys.restore = 1 + endif + + let val = a:opt.of('indentkeys+') + if type(val) == s:type_str && val !=# '' + execute printf('setlocal %s+=%s', indentopt.indentkeys.name, val) + let indentopt.indentkeys.restore = 1 + endif + + let val = a:opt.of('indentkeys-') + if type(val) == s:type_str && val !=# '' + " It looks there is no way to add ',' itself to 'indentkeys' + for item in split(val, ',') + execute printf('setlocal %s-=%s', indentopt.indentkeys.name, item) + endfor + let indentopt.indentkeys.restore = 1 + endif + return indentopt +endfunction +"}}} +function! s:restore_indent(indentopt) abort "{{{ + " restore indentkeys first + if a:indentopt.indentkeys.restore + execute printf('setlocal %s=%s', a:indentopt.indentkeys.name, escape(a:indentopt.indentkeys.value, ' \')) + endif + + " restore autoindent options + if a:indentopt.autoindent.restore + let [&l:autoindent, &l:smartindent, &l:cindent, &l:indentexpr] = a:indentopt.autoindent.value + endif +endfunction +"}}} +function! s:add_former(buns, pos, opt, ...) abort "{{{ + let undojoin_cmd = get(a:000, 0, 0) ? 'undojoin | ' : '' + let indent = s:Indent(a:pos, a:opt) + let opt_linewise = a:opt.of('linewise') + if opt_linewise + let startinsert = a:opt.of('noremap') ? 'normal! O' : 'normal ' . s:KEY_O + let insertion = indent.savedstr . a:buns[0] + else + let startinsert = a:opt.of('noremap') ? 'normal! i' : 'normal ' . s:KEY_i + let insertion = a:buns[0] + endif + call s:add_portion(insertion, a:pos, undojoin_cmd, startinsert) + return [opt_linewise, indent.diff(a:buns[0]), getpos("'["), getpos("']")] +endfunction +"}}} +function! s:add_latter(buns, pos, opt) abort "{{{ + let undojoin_cmd = '' + let indent = s:Indent(a:pos, a:opt) + let opt_linewise = a:opt.of('linewise') + if opt_linewise + let startinsert = a:opt.of('noremap') ? 'normal! o' : 'normal ' . s:KEY_o + let insertion = indent.savedstr . a:buns[1] + else + let startinsert = a:opt.of('noremap') ? 'normal! i' : 'normal ' . s:KEY_i + let insertion = a:buns[1] + endif + call s:add_portion(insertion, a:pos, undojoin_cmd, startinsert) + return [opt_linewise, indent.diff(a:buns[1]), getpos("'["), getpos("']")] +endfunction +"}}} +function! s:add_portion(bun, pos, undojoin_cmd, startinsert) abort "{{{ + call setpos('.', a:pos) + if operator#sandwich#is_in_cmd_window() + " workaround for a bug in cmdline-window + call s:paste(a:bun, a:undojoin_cmd) + else + execute a:undojoin_cmd . 'silent noautocmd ' . a:startinsert . a:bun + endif +endfunction +"}}} +function! s:delete_former(head, tail, latter_head, opt, ...) abort "{{{ + let is_linewise = 0 + let opt_linewise = a:opt.of('linewise') + let undojoin_cmd = get(a:000, 0, 0) ? 'undojoin | ' : '' + let deletion = s:delete_portion(a:head, a:tail, undojoin_cmd) + if opt_linewise == 2 || (opt_linewise == 1 && match(getline('.'), '^\s*$') > -1) + if line('.') != a:latter_head[1] + .delete + let is_linewise = 1 + endif + let head = getpos("']") + else + let head = getpos('.') + endif + return [deletion, is_linewise, head] +endfunction +"}}} +function! s:delete_latter(head, tail, former_head, opt) abort "{{{ + let is_linewise = 0 + let opt_linewise = a:opt.of('linewise') + let undojoin_cmd = '' + let deletion = s:delete_portion(a:head, a:tail, undojoin_cmd) + if opt_linewise == 2 || (opt_linewise == 1 && match(getline('.'), '^\s*$') > -1) + .delete + let is_linewise = 1 + let tail = getpos("']") + if tail[1] != 1 && tail[1] != a:former_head[1] + let prevline = line("']") - 1 + let tail = [0, prevline, col([prevline, '$']), 0] + endif + else + let tail = getpos("']") + endif + return [deletion, is_linewise, tail] +endfunction +"}}} +function! s:delete_portion(head, tail, undojoin_cmd) abort "{{{ + let cmd = "%ssilent noautocmd normal! \"\"dv:call setpos('\.', %s)\" + call setpos('.', a:head) + let @@ = '' + execute printf(cmd, a:undojoin_cmd, 'a:tail') + return @@ +endfunction +"}}} +function! s:replace_former(buns, head, tail, within_a_line, opt, ...) abort "{{{ + let is_linewise = 0 + let opt_linewise = a:opt.of('linewise') + let undojoin_cmd = get(a:000, 0, 0) ? 'undojoin | ' : '' + let deletion = s:delete_portion(a:head, a:tail, undojoin_cmd) + let indent = s:Indent(a:head, a:opt) + + if operator#sandwich#is_in_cmd_window() + " workaround for a bug in cmdline-window + call s:paste(a:buns[0]) + else + if opt_linewise == 1 && getline('.') =~# '^\s*$' + .delete + let startinsert = a:opt.of('noremap', 'recipe_add') ? 'normal! O' : 'normal ' . s:KEY_O + execute 'silent noautocmd ' . startinsert . indent.savedstr . a:buns[0] + let is_linewise = 1 + elseif opt_linewise == 2 + if !a:within_a_line + .delete + endif + let startinsert = a:opt.of('noremap', 'recipe_add') ? 'normal! O' : 'normal ' . s:KEY_O + execute 'silent noautocmd ' . startinsert . indent.savedstr . a:buns[0] + let is_linewise = 1 + else + let startinsert = a:opt.of('noremap', 'recipe_add') ? 'normal! i' : 'normal ' . s:KEY_i + execute 'silent noautocmd ' . startinsert . a:buns[0] + endif + endif + return [deletion, is_linewise, indent.diff(a:buns[0]), getpos("'["), getpos("']")] +endfunction +"}}} +function! s:replace_latter(buns, head, tail, within_a_line, opt) abort "{{{ + let is_linewise = 0 + let opt_linewise = a:opt.of('linewise') + let undojoin_cmd = '' + let deletion = s:delete_portion(a:head, a:tail, undojoin_cmd) + let indent = s:Indent(a:head, a:opt) + + if operator#sandwich#is_in_cmd_window() + " workaround for a bug in cmdline-window + call s:paste(a:buns[1]) + let head = getpos("'[") + let tail = getpos("']") + else + if opt_linewise == 1 && getline('.') =~# '^\s*$' + let startinsert = a:opt.of('noremap', 'recipe_add') ? 'normal! o' : 'normal ' . s:KEY_o + let current = line('.') + let fileend = line('$') + .delete + if current != fileend + normal! k + endif + execute 'silent noautocmd ' . startinsert . indent.savedstr . a:buns[1] + let head = getpos("'[") + let tail = getpos("']") + let is_linewise = 1 + elseif opt_linewise == 2 + let startinsert = a:opt.of('noremap', 'recipe_add') ? 'normal! o' : 'normal ' . s:KEY_o + if a:within_a_line + " exceptional behavior + let lnum = line('.') + execute 'silent noautocmd ' . startinsert . indent.savedstr . a:buns[1] + let head = getpos("'[") + let tail = getpos("']") + execute lnum . 'delete' + let head = [0, head[1]-1, head[2], 0] + let tail = [0, tail[1]-1, tail[2], 0] + else + " usual way (same as opt_linewise == 1) + let current = line('.') + let fileend = line('$') + .delete + if current != fileend + normal! k + endif + execute 'silent noautocmd ' . startinsert . indent.savedstr . a:buns[1] + let head = getpos("'[") + let tail = getpos("']") + endif + let is_linewise = 1 + else + let startinsert = a:opt.of('noremap', 'recipe_add') ? 'normal! i' : 'normal ' . s:KEY_i + execute 'silent noautocmd ' . startinsert . a:buns[1] + let head = getpos("'[") + let tail = getpos("']") + endif + endif + return [deletion, is_linewise, indent.diff(a:buns[1]), head, tail] +endfunction +"}}} +function! s:paste(bun, ...) abort "{{{ + let undojoin_cmd = a:0 > 0 ? a:1 : '' + let reg = ['"', getreg('"'), getregtype('"')] + let @@ = a:bun + if s:has_gui_running + execute undojoin_cmd . 'normal! ""P' + else + let paste = &paste + let &paste = 1 + execute undojoin_cmd . 'normal! ""P' + let &paste = paste + endif + call call('setreg', reg) +endfunction +"}}} +function! s:execute_command(head, tail, command_list) abort "{{{ + let mod_head = deepcopy(a:head) + let mod_tail = deepcopy(a:tail) + + if a:command_list != [] + let before_mod_head = getpos("'[") + let before_mod_tail = getpos("']") + call setpos("'[", a:head) + call setpos("']", a:tail) + for command in a:command_list + execute command + endfor + + let after_mod_head = getpos("'[") + let after_mod_tail = getpos("']") + if before_mod_head != after_mod_head || before_mod_tail != after_mod_tail + let mod_head = after_mod_head + let mod_tail = after_mod_tail + endif + endif + return [mod_head, mod_tail] +endfunction +"}}} +function! s:shift_for_add(shifted_pos, target, addition, indent, is_linewise) abort "{{{ + call s:push2(a:shifted_pos, a:target, a:addition, a:indent, a:is_linewise) + call s:push1(a:shifted_pos, a:target, a:addition, a:indent, a:is_linewise) + return a:shifted_pos +endfunction +"}}} +function! s:shift_for_delete(shifted_pos, target, deletion, is_linewise) abort "{{{ + call s:pull2(a:shifted_pos, a:target, a:deletion, a:is_linewise) + call s:pull1(a:shifted_pos, a:target, a:deletion, a:is_linewise) + return a:shifted_pos +endfunction +"}}} +function! s:shift_for_replace(shifted_pos, target, addition, deletion, indent, is_linewise) abort "{{{ + if s:lib.is_in_between(a:shifted_pos, a:target.head1, a:target.tail1) + let startpos = copy(a:target.head1) + let endpos = copy(startpos) + call s:push1(endpos, a:target, a:addition, a:indent, a:is_linewise) + let endpos = s:lib.get_left_pos(endpos) + + if s:lib.is_equal_or_ahead(a:shifted_pos, endpos) + let a:shifted_pos[0:3] = endpos + endif + elseif s:lib.is_in_between(a:shifted_pos, a:target.head2, a:target.tail2) + let startpos = copy(a:target.head2) + call s:pull1(startpos, a:target, a:deletion, a:is_linewise) + call s:push1(startpos, a:target, a:addition, a:indent, a:is_linewise) + let endpos = copy(startpos) + let target = copy(s:null_4pos) + let target.head2 = copy(startpos) + call s:push2(endpos, target, a:addition, a:indent, a:is_linewise) + let endpos = s:lib.get_left_pos(endpos) + + call s:pull1(a:shifted_pos, a:target, a:deletion, a:is_linewise) + call s:push1(a:shifted_pos, a:target, a:addition, a:indent, a:is_linewise) + + if s:lib.is_equal_or_ahead(a:shifted_pos, endpos) + let a:shifted_pos[0:3] = endpos + endif + else + call s:pull2(a:shifted_pos, a:target, a:deletion, a:is_linewise) + if a:is_linewise[1] + let a:target.head2[1] -= 1 + endif + call s:push2(a:shifted_pos, a:target, a:addition, a:indent, a:is_linewise) + if a:is_linewise[1] + let a:target.head2[1] += 1 + endif + call s:pull1(a:shifted_pos, a:target, a:deletion, a:is_linewise) + if a:is_linewise[0] + let a:target.head1[1] -= 1 + endif + call s:push1(a:shifted_pos, a:target, a:addition, a:indent, a:is_linewise) + if a:is_linewise[0] + let a:target.head1[1] += 1 + endif + endif + return a:shifted_pos +endfunction +"}}} +function! s:shift_added(func_name, added, ...) abort "{{{ + for pos in ['head1', 'tail1', 'head2', 'tail2'] + call call(a:func_name, [a:added[pos]] + a:000) + endfor + return a:added +endfunction +"}}} +function! s:push1(shifted_pos, target, addition, indent, is_linewise) abort "{{{ + if a:shifted_pos != s:null_pos + let shift = [0, 0, 0, 0] + let head = a:target.head1 + + if a:is_linewise[0] && a:shifted_pos[1] >= head[1] + " lnum + let shift[1] += 1 + endif + + if s:lib.is_equal_or_ahead(a:shifted_pos, head) || (a:is_linewise[0] && a:shifted_pos[1] == head[1]) + call s:push(shift, a:shifted_pos, head, a:addition[0], a:indent[0], a:is_linewise[0]) + endif + let a:shifted_pos[1:2] += shift[1:2] + endif + return a:shifted_pos +endfunction +"}}} +function! s:push2(shifted_pos, target, addition, indent, is_linewise) abort "{{{ + if a:shifted_pos != s:null_pos + let shift = [0, 0, 0, 0] + let head = a:target.head2 + + if a:is_linewise[1] && a:shifted_pos[1] > head[1] + " lnum + let shift[1] += 1 + endif + + if s:lib.is_equal_or_ahead(a:shifted_pos, head) + call s:push(shift, a:shifted_pos, head, a:addition[1], a:indent[1], a:is_linewise[1]) + endif + let a:shifted_pos[1:2] += shift[1:2] + endif + return a:shifted_pos +endfunction +"}}} +function! s:push(shift, shifted_pos, head, addition, indent, is_linewise) abort "{{{ + let addition = split(a:addition, '\%(\n\|\r\|\r\n\)', 1) + + " lnum + let a:shift[1] += len(addition) - 1 + " column + if !a:is_linewise && a:head[1] == a:shifted_pos[1] + let a:shift[2] += a:indent + strlen(addition[-1]) + if len(addition) > 1 + let a:shift[2] -= a:head[2] - 1 + endif + endif +endfunction +"}}} +function! s:pull1(shifted_pos, target, deletion, is_linewise) abort "{{{ + if a:shifted_pos != s:null_pos + let shift = [0, 0, 0, 0] + let head = a:target.head1 + let tail = a:target.tail1 + + " lnum + if a:shifted_pos[1] > head[1] + if a:shifted_pos[1] <= tail[1] + let shift[1] -= a:shifted_pos[1] - head[1] + else + let shift[1] -= tail[1] - head[1] + endif + endif + " column + if s:lib.is_ahead(a:shifted_pos, head) && a:shifted_pos[1] <= tail[1] + if s:lib.is_ahead(a:shifted_pos, tail) + let shift[2] -= strlen(split(a:deletion[0], '\%(\n\|\r\|\r\n\)', 1)[-1]) + let shift[2] += head[1] != a:shifted_pos[1] ? head[2] - 1 : 0 + else + let shift[2] -= a:shifted_pos[2] + let shift[2] += head[2] + endif + endif + + let a:shifted_pos[1] += shift[1] + + " the case for linewise action + if a:is_linewise[0] + if a:shifted_pos[1] == head[1] + " col + let a:shifted_pos[2] = 0 + endif + if a:shifted_pos[1] > head[1] + " lnum + let a:shifted_pos[1] -= 1 + endif + endif + + if a:shifted_pos[2] == 0 + let a:shifted_pos[2] = 1 + elseif a:shifted_pos[2] == s:constants('colmax') + let a:shifted_pos[2] = col([a:shifted_pos[1], '$']) - 1 + let a:shifted_pos[2] += shift[2] + else + let a:shifted_pos[2] += shift[2] + endif + endif + return a:shifted_pos +endfunction +"}}} +function! s:pull2(shifted_pos, target, deletion, is_linewise) abort "{{{ + if a:shifted_pos != s:null_pos + let shift = [0, 0, 0, 0] + let head = a:target.head2 + let tail = a:target.tail2 + + " lnum + if a:shifted_pos[1] >= head[1] + if a:shifted_pos[1] < tail[1] + let shift[1] -= a:shifted_pos[1] - head[1] + else + let shift[1] -= tail[1] - head[1] + endif + endif + " column + if s:lib.is_equal_or_ahead(a:shifted_pos, head) && a:shifted_pos[1] <= tail[1] + if s:lib.is_ahead(a:shifted_pos, tail) + let shift[2] -= strlen(split(a:deletion[1], '\%(\n\|\r\|\r\n\)', 1)[-1]) + let shift[2] += head[1] != a:shifted_pos[1] ? head[2] - 1 : 0 + else + let shift[2] -= a:shifted_pos[2] + 1 + let shift[2] += head[2] + endif + endif + let a:shifted_pos[1:2] += shift[1:2] + + " the case for linewise action + if a:is_linewise[1] + if a:shifted_pos[1] == head[1] + " col + let a:shifted_pos[2] = s:constants('colmax') + endif + if a:shifted_pos[1] >= head[1] + " lnum + let a:shifted_pos[1] -= 1 + endif + endif + endif + return a:shifted_pos +endfunction +"}}} +function! s:added_tail(head, tail, linewise) abort "{{{ + if a:tail[1] > 1 && a:tail[2] == 1 + let lnum = a:tail[1] - 1 + let col = col([lnum, '$']) + let tail = [a:tail[0], lnum, col, a:tail[3]] + elseif a:linewise + let tail = a:tail + else + let tail = s:lib.get_left_pos(a:tail) + endif + return tail +endfunction +"}}} + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/lib.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/lib.vim new file mode 100644 index 00000000..830be4bf --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/lib.vim @@ -0,0 +1,108 @@ +" common functions + +" variables "{{{ +" null valiables +let s:null_pos = [0, 0, 0, 0] +"}}} + +let s:lib = {} +function! s:get_wider_region(head_edge, tail_edge) abort "{{{ + return [s:get_left_pos(a:head_edge), s:get_right_pos(a:tail_edge)] +endfunction +"}}} +function! s:get_left_pos(pos) abort "{{{ + call setpos('.', a:pos) + normal! h + return getpos('.') +endfunction +"}}} +function! s:get_right_pos(pos) abort "{{{ + call setpos('.', a:pos) + normal! l + return getpos('.') +endfunction +"}}} +function! s:c2p(coord) abort "{{{ + return [0] + a:coord + [0] +endfunction +"}}} +function! s:is_valid_2pos(pos) abort "{{{ + " NOTE: This function do not check the geometric relationships. + " It should be checked by s:is_ahead or s:is_equal_or_ahead + " separately. + return a:pos.head != s:null_pos && a:pos.tail != s:null_pos +endfunction +"}}} +function! s:is_valid_4pos(pos) abort "{{{ + " NOTE: This function do not check the geometric relationships. + " It should be checked by s:is_ahead or s:is_equal_or_ahead + " separately. + return a:pos.head1 != s:null_pos && a:pos.tail1 != s:null_pos + \ && a:pos.head2 != s:null_pos && a:pos.tail2 != s:null_pos +endfunction +"}}} +function! s:is_ahead(pos1, pos2) abort "{{{ + return a:pos1[1] > a:pos2[1] || (a:pos1[1] == a:pos2[1] && a:pos1[2] > a:pos2[2]) +endfunction +"}}} +function! s:is_equal_or_ahead(pos1, pos2) abort "{{{ + return a:pos1[1] > a:pos2[1] || (a:pos1[1] == a:pos2[1] && a:pos1[2] >= a:pos2[2]) +endfunction +"}}} +function! s:is_in_between(pos, head, tail) abort "{{{ + return (a:pos != s:null_pos) && (a:head != s:null_pos) && (a:tail != s:null_pos) + \ && ((a:pos[1] > a:head[1]) || ((a:pos[1] == a:head[1]) && (a:pos[2] >= a:head[2]))) + \ && ((a:pos[1] < a:tail[1]) || ((a:pos[1] == a:tail[1]) && (a:pos[2] <= a:tail[2]))) +endfunction +"}}} +function! s:get_sandwich_option(name, default) abort "{{{ + if exists('g:operator#sandwich#' . a:name) + return eval('g:operator#sandwich#' . a:name) + endif + if exists('g:sandwich#' . a:name) + return eval('g:sandwich#' . a:name) + endif + return a:default +endfunction +"}}} +function! s:get_operator_option(name, default) abort "{{{ + return get(g:, 'operator#sandwich#' . a:name, a:default) +endfunction +"}}} +function! s:escape(string) abort "{{{ + return escape(a:string, '~"\.^$[]*') +endfunction +"}}} + +function! s:export(namelist) abort "{{{ + let module = {} + for name in a:namelist + let module[name] = function('s:' . name) + endfor + return module +endfunction +"}}} +let s:lib = s:export([ + \ 'get_wider_region', + \ 'get_left_pos', + \ 'get_right_pos', + \ 'c2p', + \ 'is_valid_2pos', + \ 'is_valid_4pos', + \ 'is_ahead', + \ 'is_equal_or_ahead', + \ 'is_in_between', + \ 'get_sandwich_option', + \ 'get_operator_option', + \ 'escape', + \ ]) +lockvar! s:lib + +function! operator#sandwich#lib#import() abort "{{{ + return s:lib +endfunction +"}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/operator.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/operator.vim new file mode 100644 index 00000000..ede8a0fa --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/operator.vim @@ -0,0 +1,1064 @@ +" operator object - controlling a whole operation + +let s:lib = operator#sandwich#lib#import() + +" variables "{{{ +" null valiables +let s:TRUE = 1 +let s:FALSE = 0 +let s:null_coord = [0, 0] +let s:null_pos = [0, 0, 0, 0] +let s:null_2pos = { + \ 'head': copy(s:null_pos), + \ 'tail': copy(s:null_pos), + \ } +let s:null_4pos = { + \ 'head1': copy(s:null_pos), + \ 'tail1': copy(s:null_pos), + \ 'head2': copy(s:null_pos), + \ 'tail2': copy(s:null_pos), + \ } + +" types +let s:type_num = type(0) +let s:type_str = type('') +let s:type_list = type([]) +let s:type_fref = type(function('tr')) + +" patchs +if v:version > 704 || (v:version == 704 && has('patch237')) + let s:has_patch_7_4_310 = has('patch-7.4.310') +else + let s:has_patch_7_4_310 = v:version == 704 && has('patch310') +endif + +" functions +let s:exists_reg_executing = exists('*reg_executing') + +" features +let s:has_gui_running = has('gui_running') +let s:has_timer = has('timers') + +" visualrepeat.vim (vimscript #3848) support +let s:visualrepeat_exists = -1 +"}}} + +function! operator#sandwich#operator#new() abort "{{{ + return deepcopy(s:operator) +endfunction +"}}} + +" s:operator "{{{ +let s:operator = { + \ 'state' : 0, + \ 'kind' : '', + \ 'motionwise': '', + \ 'count' : 1, + \ 'n' : 0, + \ 'mode' : 'n', + \ 'view' : {}, + \ 'blockwidth': 0, + \ 'extended' : 0, + \ 'at_work' : 0, + \ 'opt' : {}, + \ 'basket' : [], + \ 'recipes' : { + \ 'arg' : [], + \ 'integrated': [], + \ 'dog_ear' : [], + \ 'synchro': {'on': 0, 'kind': '', 'recipe': {}}, + \ }, + \ 'cursor': { + \ 'head' : copy(s:null_pos), + \ 'headend' : copy(s:null_pos), + \ 'inner_head': copy(s:null_pos), + \ 'keep' : copy(s:null_pos), + \ 'inner_tail': copy(s:null_pos), + \ 'tailstart' : copy(s:null_pos), + \ 'tail' : copy(s:null_pos), + \ 'default' : copy(s:null_pos), + \ 'keepable' : 0, + \ }, + \ 'modmark': copy(s:null_2pos), + \ 'highlight': { + \ 'target': sandwich#highlight#new(), + \ 'added': sandwich#highlight#new(), + \ 'stuff': sandwich#highlight#new(), + \ }, + \ } +"}}} +function! s:operator.execute(motionwise) dict abort "{{{ + let options = s:shift_options(self.kind, self.mode) + let messenger = sandwich#messenger#get() + try + call self.initialize(a:motionwise) + if self.state >= 0 + call self[self.kind]() + endif + catch /^OperatorSandwichError:ReadOnly/ + catch /^OperatorSandwichError:IncorrectBuns/ + unlet! g:operator#sandwich#object + catch /^OperatorSandwichCancel/ + unlet! g:operator#sandwich#object + catch /^Vim:Interrupt$/ + " cancelled by + unlet! g:operator#sandwich#object + catch + call messenger.error.set(printf('Unanticipated error. [%s] %s', v:throwpoint, v:exception)) + unlet! g:operator#sandwich#object + finally + call self.finalize() + call s:restore_options(self.kind, self.mode, options) + endtry +endfunction +"}}} +function! s:operator.initialize(motionwise) dict abort "{{{ + let self.at_work = 1 + let self.motionwise = a:motionwise + call self.recipes.integrate(self.kind, a:motionwise, self.mode) + let region = s:get_assigned_region(self.kind, a:motionwise) + let region_list = a:motionwise ==# 'block' ? self.split(region) : [region] + if region == s:null_2pos + " deactivate + let self.state = -1 + endif + + let self.n = len(region_list) " Number of lines in the target region + let self.cursor.inner_head = deepcopy(region.head) + let self.cursor.inner_tail = deepcopy(region.tail) + + if self.state + let self.basket = map(range(self.n), 'operator#sandwich#stuff#new()') + else + let self.view = winsaveview() + let self.modmark.head = copy(s:null_pos) + let self.modmark.tail = copy(s:null_pos) + call self.fill() + endif + for stuff in self.basket + call stuff.initialize(self.count, self.cursor, self.modmark) + endfor + + " set initial values + for i in range(self.n) + let stuff = self.basket[i] + let stuff.edges = region_list[i] + endfor +endfunction +"}}} +function! s:operator.split(region) dict abort "{{{ + let reg = ['"', getreg('"'), getregtype('"')] + let view = winsaveview() + let virtualedit = &virtualedit + let &virtualedit = 'all' + try + if self.blockwidth == 0 + " The case for blockwise motions in operator-pending mode + execute "normal! `[\`]" + silent noautocmd normal! ""y + let regtype = getregtype('"') + let self.blockwidth = str2nr(regtype[1:]) + endif + let disp_region = map(copy(a:region), 's:get_displaycoord(v:val)') + let lnum_top = disp_region.head[0] + let lnum_bottom = disp_region.tail[0] + let col_head = disp_region.head[1] + let col_tail = col_head + self.blockwidth - 1 + let region_list = [] + if self.extended + for lnum in reverse(range(lnum_top, lnum_bottom)) + call s:set_displaycoord([lnum, col_head]) + let head = getpos('.') + let tail = [0, lnum, col([lnum, '$']) - 1, 0] + if head[3] == 0 && s:lib.is_equal_or_ahead(tail, head) + let region_list += [{'head': head, 'tail': tail}] + endif + endfor + else + for lnum in reverse(range(lnum_top, lnum_bottom)) + call s:set_displaycoord([lnum, col_head]) + let head = getpos('.') + call s:set_displaycoord([lnum, col_tail]) + let tail = getpos('.') + let endcol = col([lnum, '$']) + if head[2] != endcol && s:lib.is_equal_or_ahead(tail, head) + if tail[2] == endcol + let tail[2] = endcol - 1 + let tail[3] = 0 + endif + let region_list += [{'head': head, 'tail': tail}] + endif + endfor + endif + finally + call call('setreg', reg) + call winrestview(view) + let &virtualedit = virtualedit + endtry + return region_list +endfunction +"}}} +function! s:operator.fill() dict abort "{{{ + let lack = self.n - len(self.basket) + if lack > 0 + let fillings = map(range(lack), 'operator#sandwich#stuff#new()') + call extend(self.basket, fillings) + endif +endfunction +"}}} +function! s:operator.add() dict abort "{{{ + let opt = self.opt + let hi_group = opt.of('highlight', '') >= 2 ? 'OperatorSandwichChange' : 'OperatorSandwichBuns' + + let modified = 0 + for i in range(self.count) + call self.set_target() + if self.state + " query preferable buns + call self.show('stuff', hi_group) + try + let recipe = self.query() + let self.recipes.dog_ear += [recipe] + finally + call self.quench('stuff') + endtry + + call opt.update('recipe_add', recipe) + if i == 0 && self.count > 1 && opt.of('query_once') + call self.recipes.fill(recipe, self.count) + let self.state = 0 + endif + else + let recipe = self.recipes.dog_ear[i] + call opt.update('recipe_add', recipe) + endif + if !has_key(recipe, 'buns') || empty(recipe.buns) + break + endif + + call self.skip_space(i) + let modified = self.add_once(i, recipe) || modified + call opt.clear('recipe_add') + endfor + + if modified + call self.highlight_added(opt) + + " visualrepeat.vim support + call s:visualrepeat_set('add', self.count) + endif +endfunction +"}}} +function! s:operator.add_once(i, recipe) dict abort "{{{ + let buns = s:get_buns(a:recipe, self.opt.of('expr'), self.opt.of('listexpr')) + let undojoin = a:i == 0 || self.state == 0 ? 0 : 1 + let modified = 0 + if buns[0] !=# '' || buns[1] !=# '' || self.opt.of('linewise') + for j in range(self.n) + let stuff = self.basket[j] + if stuff.active + let act = stuff.acts[a:i] + let act.opt = deepcopy(self.opt) + let success = act.add_pair(buns, stuff, undojoin) + let undojoin = success ? 0 : undojoin + let modified = modified || success + endif + endfor + + if modified + let self.cursor.head = copy(self.modmark.head) + let self.cursor.tail = s:lib.get_left_pos(self.modmark.tail) + endif + endif + return modified +endfunction +"}}} +function! s:operator.delete() dict abort "{{{ + let hi_exited = 0 + let opt_highlight = self.opt.of('highlight', '') + let hi_duration = self.opt.of('hi_duration', '') + let hi_group = opt_highlight >= 2 ? 'OperatorSandwichDelete' : 'OperatorSandwichBuns' + let modified = 0 + for i in range(self.count) + if !self.match(i) + break + endif + + if opt_highlight && !hi_exited && hi_duration > 0 + let hi_exited = self.blink('target', hi_group, hi_duration) + endif + + let modified = self.delete_once(i) || modified + endfor + + if modified + " visualrepeat.vim support + call s:visualrepeat_set('delete', self.count) + endif +endfunction +"}}} +function! s:operator.delete_once(i) dict abort "{{{ + let modified = 0 + for j in range(self.n) + let stuff = self.basket[j] + if stuff.active + let act = stuff.acts[a:i] + let success = act.delete_pair(stuff, modified) + let modified = modified || success + endif + endfor + + if modified + let self.cursor.head = copy(self.modmark.head) + let self.cursor.tail = s:lib.get_left_pos(self.modmark.tail) + endif + return modified +endfunction +"}}} +function! s:operator.replace() dict abort "{{{ + let opt = self.opt + let hi_group = opt.of('highlight', '') >= 2 ? 'OperatorSandwichDelete' : 'OperatorSandwichBuns' + let self.cursor.inner_head = copy(s:null_pos) + let self.cursor.inner_tail = copy(s:null_pos) + + let modified = 0 + for i in range(self.count) + if !self.match(i) + break + endif + + if self.state + " query preferable buns + call self.show('target', hi_group) + try + let recipe = self.query() + let self.recipes.dog_ear += [recipe] + finally + call self.quench('target') + endtry + + call opt.update('recipe_add', recipe) + if i == 0 && self.count > 1 && opt.of('query_once') + call self.recipes.fill(recipe, self.count) + let self.state = 0 + endif + else + let recipe = self.recipes.dog_ear[i] + call opt.update('recipe_add', recipe) + endif + if !has_key(recipe, 'buns') || empty(recipe.buns) + break + endif + + let modified = self.replace_once(i, recipe) || modified + call opt.clear('recipe_add') + endfor + + if modified + call self.highlight_added(opt) + + " visualrepeat.vim support + call s:visualrepeat_set('replace', self.count) + endif +endfunction +"}}} +function! s:operator.replace_once(i, recipe) dict abort "{{{ + let buns = s:get_buns(a:recipe, self.opt.of('expr'), self.opt.of('listexpr')) + let undojoin = a:i == 0 || self.state == 0 ? 0 : 1 + let modified = 0 + for j in range(self.n) + let stuff = self.basket[j] + if stuff.active + let act = stuff.acts[a:i] + call act.opt.update('recipe_add', a:recipe) + let success = act.replace_pair(buns, stuff, undojoin, modified) + let undojoin = success ? 0 : undojoin + let modified = modified || success + endif + endfor + + if modified + let self.cursor.head = copy(self.modmark.head) + let self.cursor.tail = s:lib.get_left_pos(self.modmark.tail) + endif + return modified +endfunction +"}}} +function! s:operator.set_target() dict abort "{{{ + for i in range(self.n) + let stuff = self.basket[i] + call stuff.set_target() + endfor +endfunction +"}}} +function! s:operator.skip_space(i) dict abort "{{{ + let opt = self.opt + if a:i == 0 && opt.of('skip_space') + " skip space only in the first count. + for j in range(self.n) + let stuff = self.basket[j] + call stuff.skip_space() + call stuff.set_target() + endfor + + " for cursor positions + if !opt.of('linewise') + let top_stuff = self.basket[self.n-1] + let bot_stuff = self.basket[0] + let self.cursor.inner_head = deepcopy(top_stuff.edges.head) + let self.cursor.inner_tail = deepcopy(bot_stuff.edges.tail) + endif + endif +endfunction +"}}} +function! s:operator.query() dict abort "{{{ + let filter = 'has_key(v:val, "buns") + \ && (!has_key(v:val, "regex") || !v:val.regex) + \ && s:has_action(v:val, "add")' + let recipes = filter(deepcopy(self.recipes.integrated), filter) + let opt = self.opt + let clock = sandwich#clock#new() + let timeout = s:lib.get_sandwich_option('timeout', &timeout) + let timeoutlen = s:lib.get_sandwich_option('timeoutlen', &timeoutlen) + let timeoutlen = max([0, timeoutlen]) + + " query phase + let input = '' + let last_compl_match = ['', []] + while 1 + let c = getchar(0) + if empty(c) + if clock.started && timeout && timeoutlen > 0 && clock.elapsed() > timeoutlen + let [input, recipes] = last_compl_match + break + else + sleep 20m + continue + endif + endif + + let c = type(c) == s:type_num ? nr2char(c) : c + + " exit loop if is pressed + if c is# "\" + let input = "\" + break + endif + + let input .= c + + " check forward match + let n_fwd = len(filter(recipes, 's:is_input_matched(v:val, input, opt, 0)')) + + " check complete match + let n_comp = len(filter(copy(recipes), 's:is_input_matched(v:val, input, opt, 1)')) + if n_comp || strchars(input) == 1 + if len(recipes) == n_comp + break + else + call clock.stop() + call clock.start() + let last_compl_match = [input, copy(recipes)] + endif + else + if clock.started && !n_fwd + let [input, recipes] = last_compl_match + break + endif + endif + + if recipes == [] | break | endif + endwhile + call clock.stop() + + " pick up and register a recipe + if filter(recipes, 's:is_input_matched(v:val, input, opt, 1)') != [] + let recipe = recipes[0] + else + if s:is_input_fallback(input) + let c = split(input, '\zs')[0] + let recipe = {'buns': [c, c], 'expr': 0} + else + let recipe = {} + endif + endif + return extend(recipe, {'evaluated': 0}) +endfunction +"}}} +function! s:operator.match(i) dict abort "{{{ + let opt = self.opt + let default_expr = opt.get('expr', '', 0) + let default_listexpr = opt.get('listexpr', '', 0) + let source = self.recipes.synchro.on && self.recipes.synchro.kind ==# 'query' + \ ? self.recipes.synchro.recipe + \ : self.recipes.integrated + let filter = 's:has_action(v:val, "delete") + \ && s:is_not_expr(v:val, default_expr, default_listexpr) + \ && s:is_appropriate_patterns(v:val)' + let recipes = filter(deepcopy(source), filter) + + " uniq recipes + call s:uniq_recipes(recipes, opt.of('regex'), opt.get('noremap', '')) + + let success = 0 + let match_edges = !self.recipes.synchro.on + for j in range(self.n) + let stuff = self.basket[j] + let act = stuff.acts[a:i] + let act.opt = deepcopy(self.opt) + let success = stuff.match(recipes, act.opt, match_edges) || success + endfor + return success +endfunction +"}}} +function! s:operator.show(place, hi_group, ...) dict abort "{{{ + let forcibly = get(a:000, 0, 0) + if !forcibly && (!self.opt.of('highlight') || s:reg_executing() isnot# '') + return 1 + endif + + let highlight = self.highlight[a:place] + call highlight.initialize() + for i in range(self.n) + let stuff = self.basket[i] + for [order, linewise] in stuff.hi_list(a:place, self.opt.of('linewise')) + call highlight.order(order, linewise) + endfor + endfor + let success = highlight.show(a:hi_group) + call winrestview(self.view) + redraw + return !success +endfunction +"}}} +function! s:operator.quench(place) dict abort "{{{ + let highlight = self.highlight[a:place] + return highlight.quench() +endfunction +"}}} +function! s:operator.quench_timer(place, duration) abort "{{{ + if a:duration <= 0 + call self.quench(a:place) + endif + let highlight = self.highlight[a:place] + call highlight.quench_timer(a:duration) +endfunction +"}}} +function! s:operator.blink(place, hi_group, duration) dict abort "{{{ + if !self.opt.of('highlight') || a:duration <= 0 + return 1 + endif + + " highlight off: limit the number of highlighting region to one explicitly + call sandwich#highlight#cancel() + + let clock = sandwich#clock#new() + let hi_exited = 0 + let linewise = get(a:000, 0, 0) + call self.show(a:place, a:hi_group) + try + let c = 0 + call clock.start() + while empty(c) + let c = getchar(0) + if clock.started && clock.elapsed() > a:duration + break + endif + sleep 20m + endwhile + + if c != 0 + let c = type(c) == s:type_num ? nr2char(c) : c + let hi_exited = 1 + call feedkeys(c, 'it') + endif + finally + call self.quench(a:place) + call clock.stop() + endtry + return hi_exited +endfunction +"}}} +function! s:operator.glow(place, hi_group, duration) dict abort "{{{ + if !self.opt.of('highlight') || a:duration <= 0 + return + endif + + " highlight off: limit the number of highlighting region to one explicitly + call sandwich#highlight#cancel() + + call self.show(a:place, a:hi_group) + call self.quench_timer(a:place, a:duration) +endfunction +"}}} +function! s:operator.highlight_added(opt) dict abort "{{{ + if a:opt.of('highlight', '') < 3 + return + endif + + let hi_duration = a:opt.of('hi_duration', '') + let hi_method = s:lib.get_operator_option('persistent_highlight', 'glow') + if hi_method ==# 'glow' && s:has_timer + call self.glow('added', 'OperatorSandwichAdd', hi_duration) + else + call self.blink('added', 'OperatorSandwichAdd', hi_duration) + endif +endfunction +"}}} +function! s:operator.finalize() dict abort "{{{ + if self.state >= 0 + " restore view + if self.view != {} + call winrestview(self.view) + let self.view = {} + endif + + let act = self.last_succeeded() + if act != {} + " set modified marks + let modmark = self.modmark + if modmark.head != s:null_pos && modmark.tail != s:null_pos + \ && s:lib.is_equal_or_ahead(modmark.tail, modmark.head) + call setpos("'[", modmark.head) + call setpos("']", modmark.tail) + endif + + " set cursor position + let cursor_opt = act.opt.of('cursor') + if self.cursor.keepable + let self.cursor.keepable = 0 + else + " In the case of dot repeat, it is impossible to keep original position + " unless self.cursor.keepable == 1. + let self.cursor.keep = copy(self.cursor.default) + endif + if cursor_opt ==# 'headend' + let self.cursor.headend = s:get_headend_cursor_pos(self.cursor.head, self.cursor.inner_head) + elseif cursor_opt ==# 'tailstart' + let self.cursor.tailstart = s:get_tailstart_cursor_pos(self.cursor.tail, self.cursor.inner_tail) + endif + let self.cursor.default = s:get_default_cursor_pos(self.cursor.inner_head) + let cursor = get(self.cursor, cursor_opt, 'default') + if cursor == s:null_pos + let cursor = self.cursor.default + endif + + if s:has_patch_7_4_310 + " set curswant explicitly + call setpos('.', cursor + [cursor[2]]) + else + call setpos('.', cursor) + endif + endif + endif + + " set state + let self.state = 0 + let self.at_work = 0 +endfunction +"}}} +function! s:operator.last_succeeded() abort "{{{ + for i in range(self.count - 1, 0, -1) + for j in range(self.n) + let stuff = self.basket[j] + let act = stuff.acts[i] + if act.success + return act + endif + endfor + endfor + return {} +endfunction +"}}} +function! s:operator.recipes.integrate(kind, motionwise, mode) dict abort "{{{ + let self.integrated = [] + if self.arg_given + let self.integrated += self.arg + else + let self.integrated += sandwich#get_recipes() + let self.integrated += operator#sandwich#get_recipes() + endif + if self.synchro.on + let self.integrated += self.synchro.recipe + endif + let filter = 's:has_filetype(v:val) + \ && s:has_kind(v:val, a:kind) + \ && s:has_motionwise(v:val, a:motionwise) + \ && s:has_mode(v:val, a:mode) + \ && s:expr_filter(v:val)' + call filter(self.integrated, filter) + call reverse(self.integrated) +endfunction +"}}} +function! s:operator.recipes.fill(recipe, count) dict abort "{{{ + while len(self.dog_ear) < a:count + call add(self.dog_ear, a:recipe) + endwhile +endfunction +"}}} + +" filters +function! s:has_filetype(candidate) abort "{{{ + if !has_key(a:candidate, 'filetype') + return 1 + else + let filetypes = split(&filetype, '\.') + if filetypes == [] + let filter = 'v:val ==# "all" || v:val ==# ""' + else + let filter = 'v:val ==# "all" || index(filetypes, v:val) > -1' + endif + return filter(copy(a:candidate['filetype']), filter) != [] + endif +endfunction +"}}} +function! s:has_kind(candidate, kind) abort "{{{ + if !has_key(a:candidate, 'kind') + return 1 + else + let filter = 'v:val ==# a:kind || v:val ==# "operator" || v:val ==# "all"' + return filter(copy(a:candidate['kind']), filter) != [] + endif +endfunction +"}}} +function! s:has_motionwise(candidate, motionwise) abort "{{{ + if !has_key(a:candidate, 'motionwise') + return 1 + else + let filter = 'v:val ==# a:motionwise || v:val ==# "all"' + return filter(copy(a:candidate['motionwise']), filter) != [] + endif +endfunction +"}}} +function! s:has_mode(candidate, mode) abort "{{{ + if !has_key(a:candidate, 'mode') + return 1 + else + return stridx(join(a:candidate['mode'], ''), a:mode) > -1 + endif +endfunction +"}}} +function! s:has_action(candidate, action) abort "{{{ + if !has_key(a:candidate, 'action') + return 1 + endif + let filter = 'v:val ==# a:action || v:val ==# "all"' + return filter(copy(a:candidate['action']), filter) != [] +endfunction +"}}} +function! s:expr_filter(candidate) abort "{{{ + if !has_key(a:candidate, 'expr_filter') + return 1 + else + for filter in a:candidate['expr_filter'] + if !eval(filter) + return 0 + endif + endfor + return 1 + endif +endfunction +"}}} + +" visualrepeat.vim (vimscript #3848) support +function! s:visualrepeat_set(kind, count) abort "{{{ + if !exists('g:operator_sandwich_no_visualrepeat') + let key = printf("\(operator-sandwich-%s-visualrepeat)", a:kind) + try + call visualrepeat#set(key, a:count) + catch /^Vim\%((\a\+)\)\=:E117:/ + endtry + endif +endfunction +"}}} + +" private functions +function! s:shift_options(kind, mode) abort "{{{ + """ save options + let options = {} + let options.virtualedit = &virtualedit + let options.whichwrap = &whichwrap + let options.cpoptions = &cpoptions + let options.formatoptions = &formatoptions + + """ tweak appearance + " hide_cursor + if s:has_gui_running + let options.cursor = &guicursor + set guicursor+=n-o:block-NONE + else + let options.cursor = &t_ve + set t_ve= + endif + + " hide cursorline highlight if in visualmode. + " FIXME: The cursorline would be shown at the top line of assigned region + " in a moment currently. This is not good. This could be avoidable + " if the tweak was done in operator#sandwich#prerequisite(). + " However, since operatorfunc is possible not to be called (when + " motion or textobj is canceled), it cannot be restored safely... + " Another way to avoid is to set lazyredraw on constantly. + if (a:kind ==# 'add' || a:kind ==# 'replace') && a:mode ==# 'x' + let options.cursorline = &l:cursorline + setlocal nocursorline + endif + + """ shift options + set virtualedit=onemore + set whichwrap=h,l + set cpoptions-=l + set cpoptions-=\ + setlocal formatoptions= + return options +endfunction +"}}} +function! s:restore_options(kind, mode, options) abort "{{{ + if (a:kind ==# 'add' || a:kind ==# 'replace') && a:mode ==# 'x' + let &l:cursorline = a:options.cursorline + endif + + if s:has_gui_running + set guicursor& + let &guicursor = a:options.cursor + else + let &t_ve = a:options.cursor + endif + + let &virtualedit = a:options.virtualedit + let &whichwrap = a:options.whichwrap + let &cpoptions = a:options.cpoptions + let &l:formatoptions = a:options.formatoptions +endfunction +"}}} +function! s:get_assigned_region(kind, motionwise) abort "{{{ + let region = {'head': getpos("'["), 'tail': getpos("']")} + + " early-quit conditions + if !s:is_valid_region(a:kind, region, a:motionwise) + return deepcopy(s:null_2pos) + endif + + if a:motionwise ==# 'line' + let region.head[2] = 1 + let region.tail[2] = col([region.tail[1], '$']) - 1 + else + if region.tail[2] >= col([region.tail[1], '$']) + let region.tail[2] -= 1 + endif + endif + if region.tail[2] < 1 + let region.tail[2] = 1 + endif + + " for multibyte characters + if region.tail[2] != col([region.tail[1], '$']) && region.tail[3] == 0 + let cursor = getpos('.') + call setpos('.', region.tail) + let letterhead = searchpos('\zs', 'bcn', line('.')) + if letterhead[1] > region.tail[2] + " try again without 'c' flag if letterhead is behind the original + " position. It may look strange but it happens with &enc ==# 'cp932' + let letterhead = searchpos('\zs', 'bn', line('.')) + endif + let region.tail = [0] + letterhead + [0] + call setpos('.', cursor) + endif + + " check validity again + if !s:is_valid_region(a:kind, region) + return deepcopy(s:null_2pos) + endif + + return region +endfunction +"}}} +function! s:is_valid_region(kind, region, ...) abort "{{{ + " If the third argument is given and it is 'line', ignore the geometric + " condition of head and tail. + return s:lib.is_valid_2pos(a:region) + \ && ( + \ (a:kind ==# 'add' && s:lib.is_equal_or_ahead(a:region.tail, a:region.head)) + \ || ((a:kind ==# 'delete' || a:kind ==# 'replace') && s:lib.is_ahead(a:region.tail, a:region.head)) + \ || (a:0 > 0 && a:1 ==# 'line') + \ ) +endfunction +"}}} +function! s:get_displaycoord(pos) abort "{{{ + let [lnum, col, offset] = a:pos[1:3] + + if [lnum, col] != s:null_coord + if col == 1 + let disp_col = 1 + else + let disp_col = strdisplaywidth(getline(lnum)[: col - 2]) + 1 + offset + endif + else + let disp_col = 0 + endif + return [lnum, disp_col] +endfunction +"}}} +function! s:set_displaycoord(disp_coord) abort "{{{ + if a:disp_coord != s:null_coord + execute 'normal! ' . a:disp_coord[0] . 'G' . a:disp_coord[1] . '|' + endif +endfunction +"}}} +function! s:uniq_recipes(recipes, opt_regex, opt_noremap) abort "{{{ + let recipes = copy(a:recipes) + call filter(a:recipes, 0) + while recipes != [] + let recipe = remove(recipes, 0) + call add(a:recipes, recipe) + if has_key(recipe, 'buns') + let ref_regex = get(recipe, 'regex', a:opt_regex) + call filter(recipes, '!s:is_duplicated_buns(v:val, recipe, ref_regex, a:opt_regex)') + elseif has_key(recipe, 'external') + call filter(recipes, '!s:is_duplicated_external(v:val, recipe, a:opt_noremap)') + endif + endwhile +endfunction +"}}} +function! s:is_duplicated_buns(item, ref, ref_regex, opt_regex) abort "{{{ + if has_key(a:item, 'buns') + \ && a:ref['buns'][0] ==# a:item['buns'][0] + \ && a:ref['buns'][1] ==# a:item['buns'][1] + \ && get(a:item, 'regex', a:opt_regex) == a:ref_regex + return 1 + endif + return 0 +endfunction +"}}} +function! s:is_duplicated_external(item, ref, opt_noremap) abort "{{{ + if has_key(a:item, 'external') + \ && a:ref['external'][0] ==# a:item['external'][0] + \ && a:ref['external'][1] ==# a:item['external'][1] + let noremap_r = get(a:ref, 'noremap', a:opt_noremap) + let noremap_i = get(a:item, 'noremap', a:opt_noremap) + + if noremap_r == noremap_i + return 1 + endif + endif + + return 0 +endfunction +"}}} +function! s:is_not_expr(item, default_expr, default_listexpr) abort "{{{ + return get(a:item, 'expr', a:default_expr) || get(a:item, 'listexpr', a:default_listexpr) ? 0 : 1 +endfunction +"}}} +function! s:is_appropriate_patterns(item) abort "{{{ + if has_key(a:item, 'buns') + return type(a:item.buns) == s:type_list && len(a:item.buns) >= 2 ? 1 : 0 + elseif has_key(a:item, 'external') + return type(a:item.external) == s:type_list && len(a:item.external) >= 2 ? 1 : 0 + else + return 0 + endif +endfunction +"}}} +function! s:is_input_matched(candidate, input, opt, flag) abort "{{{ + if !has_key(a:candidate, 'buns') + return 0 + elseif !a:flag && a:input ==# '' + return 1 + endif + + let candidate = deepcopy(a:candidate) + call a:opt.update('recipe_add', candidate) + + " 'input' is necessary for 'expr' ('listexpr') buns + if (a:opt.of('expr') || a:opt.of('listexpr')) && !has_key(candidate, 'input') + return 0 + endif + + " If a:flag == 0, check forward match. Otherwise, check complete match. + let inputs = get(candidate, 'input', candidate['buns']) + if a:flag + return filter(inputs, 'v:val ==# a:input') != [] + else + let idx = strlen(a:input) - 1 + return filter(inputs, 'v:val[: idx] ==# a:input') != [] + endif +endfunction +"}}} +function! s:get_buns(recipe, opt_expr, opt_listexpr) abort "{{{ + if a:opt_listexpr == 2 + let buns = eval(a:recipe.buns) + elseif a:opt_listexpr == 1 && !a:recipe.evaluated + let buns = eval(a:recipe.buns) + unlet a:recipe.buns + let a:recipe.buns = buns + let a:recipe.evaluated = 1 + elseif a:opt_expr == 2 + let buns = map(copy(a:recipe.buns), 'eval(v:val)') + elseif a:opt_expr == 1 && !a:recipe.evaluated + let buns = map(a:recipe.buns, 'eval(v:val)') + let a:recipe.evaluated = 1 + else + let buns = a:recipe.buns + endif + call s:check_buns(buns) + return buns +endfunction +"}}} +function! s:check_buns(buns) abort "{{{ + let messenger = sandwich#messenger#get() + let error = 'OperatorSandwichError:IncorrectBuns' + if type(a:buns) != s:type_list + call messenger.error.set('Incorrect buns. : not a list -> ' . string(a:buns)) + throw error + elseif len(a:buns) < 2 + call messenger.error.set('Incorrect buns. : list too short -> ' . string(a:buns)) + throw error + elseif !(type(a:buns[0]) == s:type_str || type(a:buns[0]) == s:type_num) + \ || !(type(a:buns[1]) == s:type_str || type(a:buns[1]) == s:type_num) + call messenger.error.set('Incorrect buns. : not string buns -> ' . string(a:buns)) + throw error + endif +endfunction +"}}} +function! s:is_input_fallback(input) abort "{{{ + if a:input ==# "\" || a:input ==# '' || a:input =~# '^[\x80]' + return s:FALSE + endif + let input_fallback = get(g:, 'sandwich#input_fallback', s:TRUE) + if !input_fallback + return s:FALSE + endif + return s:TRUE +endfunction "}}} +function! s:get_default_cursor_pos(inner_head) abort "{{{ + call setpos('.', a:inner_head) + let default = searchpos('^\s*\zs\S', 'cn', a:inner_head[1]) + return default == s:null_coord ? a:inner_head : s:lib.c2p(default) +endfunction +"}}} +function! s:get_headend_cursor_pos(head, inner_head) abort "{{{ + let headend = s:lib.get_left_pos(a:inner_head) + if headend == s:null_pos || s:lib.is_ahead(a:head, headend) + let headend = copy(a:head) + endif + return headend +endfunction +"}}} +function! s:get_tailstart_cursor_pos(tail, inner_tail) abort "{{{ + let tailstart = s:lib.get_right_pos(a:inner_tail) + if tailstart == s:null_pos || s:lib.is_ahead(tailstart, a:tail) + let tailstart = copy(a:tail) + endif + return tailstart +endfunction +"}}} +function! s:reg_executing() abort "{{{ + if s:exists_reg_executing + return reg_executing() + endif + return '' +endfunction "}}} + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/stuff.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/stuff.vim new file mode 100644 index 00000000..59109045 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/operator/sandwich/stuff.vim @@ -0,0 +1,507 @@ +" stuff object - managing a line on buffer + +let s:lib = operator#sandwich#lib#import() + +" variables "{{{ +" null valiables +let s:null_coord = [0, 0] +let s:null_pos = [0, 0, 0, 0] +let s:null_2pos = { + \ 'head': copy(s:null_pos), + \ 'tail': copy(s:null_pos), + \ } +let s:null_4pos = { + \ 'head1': copy(s:null_pos), + \ 'tail1': copy(s:null_pos), + \ 'head2': copy(s:null_pos), + \ 'tail2': copy(s:null_pos), + \ } +function! s:SID() abort + return matchstr(expand(''), '\zs\d\+\ze_SID$') +endfunction +let s:SNR = printf("\%s_", s:SID()) +delfunction s:SID + +nnoremap (v) v + +let s:KEY_v = printf('%s(v)', s:SNR) + +" types +let s:type_list = type([]) + +" patchs +if v:version > 704 || (v:version == 704 && has('patch237')) + let s:has_patch_7_4_771 = has('patch-7.4.771') + let s:has_patch_7_4_358 = has('patch-7.4.358') +else + let s:has_patch_7_4_771 = v:version == 704 && has('patch771') + let s:has_patch_7_4_358 = v:version == 704 && has('patch358') +endif +"}}} + +function! operator#sandwich#stuff#new() abort "{{{ + return deepcopy(s:stuff) +endfunction +"}}} + +" s:stuff "{{{ +let s:stuff = { + \ 'active' : 1, + \ 'edges' : copy(s:null_2pos), + \ 'target' : copy(s:null_4pos), + \ 'acts' : [], + \ 'added' : [], + \ } +"}}} +function! s:stuff.initialize(count, cursor, modmark) dict abort "{{{ + let self.active = 1 + let self.acts = map(range(a:count), 'operator#sandwich#act#new()') + let self.added = [] + for act in self.acts + call act.initialize(a:cursor, a:modmark, self.added) + endfor +endfunction +"}}} +function! s:stuff.set_target() dict abort "{{{ + if self.active + let head = copy(self.edges.head) + let tail = copy(self.edges.tail) + let self.target.head1 = head + let self.target.tail1 = head + let self.target.head2 = tail + let self.target.tail2 = tail + endif +endfunction +"}}} +function! s:stuff.match(recipes, opt, ...) dict abort "{{{ + if !self.active + return 0 + endif + + let edges = self.edges + let edges_saved = deepcopy(edges) + let match_edges = get(a:000, 0, 1) + if s:lib.is_valid_2pos(edges) && s:lib.is_ahead(edges.tail, edges.head) + let edge_chars = ['', ''] + if self._match_recipes(a:recipes, a:opt) || (match_edges && self._match_edges(a:recipes, a:opt, edge_chars)) + " found! + return 1 + else + let [head_c, tail_c] = edge_chars + if head_c =~# '\s' || tail_c =~# '\s' + call self.skip_space() + if s:lib.is_ahead(edges.head, edges.tail) + " invalid edges after skip spaces + let self.active = 0 + return 0 + else + if self._match_recipes(a:recipes, a:opt, 1) || self._match_edges(a:recipes, a:opt, edge_chars) + " found + return 1 + endif + endif + endif + endif + let edges = edges_saved + + " skip characters + let target_list = [] + for candidate in a:recipes + call a:opt.update('recipe_delete', candidate) + if a:opt.of('skip_char') && has_key(candidate, 'buns') + let head = edges.head + let tail = edges.tail + let opt_regex = a:opt.of('regex') + let patterns = s:get_patterns(candidate, opt_regex) + let target_list += [s:search_edges(head, tail, patterns)] + endif + endfor + if filter(target_list, 's:lib.is_valid_4pos(v:val)') != [] + " found + let self.target = s:shortest(target_list) + return 1 + endif + + call a:opt.clear('recipe_delete') + endif + let self.active = 0 + return 0 +endfunction +"}}} +function! s:stuff._match_recipes(recipes, opt, ...) dict abort "{{{ + let is_space_skipped = get(a:000, 0, 0) + + let found = 0 + for candidate in a:recipes + call a:opt.update('recipe_delete', candidate) + if !is_space_skipped || a:opt.of('skip_space') + if has_key(candidate, 'buns') + " search buns + let patterns = s:get_patterns(candidate, a:opt.of('regex')) + let target = s:check_edges(self.edges.head, self.edges.tail, patterns) + elseif has_key(candidate, 'external') + " get difference of external motion/textobject + let target = s:check_textobj_diff(self.edges.head, self.edges.tail, candidate, a:opt.of('noremap', 'recipe_delete')) + else + let target = deepcopy(s:null_4pos) + endif + + if s:lib.is_valid_4pos(target) + let found = 1 + let self.target = target + break + endif + endif + endfor + return found +endfunction +"}}} +function! s:stuff._match_edges(recipes, opt, edge_chars) dict abort "{{{ + let head = self.edges.head + let tail = self.edges.tail + let head_c = s:get_cursorchar(head) + let tail_c = s:get_cursorchar(tail) + + let found = 0 + if head_c ==# tail_c + " check duplicate with recipe + for candidate in a:recipes + call a:opt.update('recipe_delete', candidate) + if has_key(candidate, 'buns') && !a:opt.of('regex') + \ && candidate.buns[0] ==# head_c + \ && candidate.buns[1] ==# tail_c + " The pair has already checked by a recipe. + return 0 + endif + endfor + + " both edges are matched + call a:opt.clear('recipe_delete') + if !(a:opt.of('skip_space') == 2 && head_c =~# '\s') + let found = 1 + let self.target = { + \ 'head1': head, 'tail1': head, + \ 'head2': tail, 'tail2': tail, + \ } + endif + endif + + let a:edge_chars[0] = head_c + let a:edge_chars[1] = tail_c + return found +endfunction +"}}} +function! s:stuff.skip_space() dict abort "{{{ + if self.active + call s:skip_space(self.edges.head, self.edges.tail) + if s:lib.is_ahead(self.edges.head, self.edges.tail) + let self.active = 0 + endif + endif +endfunction +"}}} +function! s:stuff.hi_list(place, linewise) dict abort "{{{ + if !self.active + return [] + endif + + let orderlist = [] + if a:place ==# 'target' + let orderlist += [[self.target, [a:linewise, a:linewise]]] + elseif a:place ==# 'added' + for added in self.added + let orderlist += [[added, added.linewise]] + endfor + elseif a:place ==# 'stuff' + let stuff = {'head1': self.edges.head, 'tail1': self.edges.tail, 'head2': copy(s:null_pos), 'tail2': copy(s:null_pos)} + let orderlist += [[stuff, [0, 0]]] + endif + return orderlist +endfunction +"}}} + +" private functions +function! s:check_edges(head, tail, patterns) abort "{{{ + if a:patterns[0] ==# '' || a:patterns[1] ==# '' | return s:null_4pos | endif + + call setpos('.', a:head) + let head1 = searchpos(a:patterns[0], 'c', a:tail[1]) + + if head1 != a:head[1:2] | return s:null_4pos | endif + + call setpos('.', a:tail) + let tail2 = s:searchpos_bce(a:tail, a:patterns[1], a:head[1]) + + if tail2 != a:tail[1:2] | return s:null_4pos | endif + + let head2 = searchpos(a:patterns[1], 'bc', a:head[1]) + call setpos('.', a:head) + let tail1 = searchpos(a:patterns[0], 'ce', a:tail[1]) + + if head1 == s:null_coord || tail1 == s:null_coord + \ || head2 == s:null_coord || tail2 == s:null_coord + \ || s:lib.is_equal_or_ahead(s:lib.c2p(tail1), s:lib.c2p(head2)) + return s:null_4pos + endif + + let target = { + \ 'head1': head1, 'tail1': tail1, + \ 'head2': head2, 'tail2': tail2, + \ } + return map(target, 's:lib.c2p(v:val)') +endfunction +"}}} +function! s:search_edges(head, tail, patterns) abort "{{{ + if a:patterns[0] ==# '' || a:patterns[1] ==# '' | return s:null_4pos | endif + + call setpos('.', a:head) + let head1 = searchpos(a:patterns[0], 'c', a:tail[1]) + + call setpos('.', a:tail) + let tail2 = s:searchpos_bce(a:tail, a:patterns[1], a:head[1]) + + if head1 == s:null_coord || tail2 == s:null_coord + \ || s:lib.is_equal_or_ahead(s:lib.c2p(head1), s:lib.c2p(tail2)) + return s:null_4pos + endif + + let head2 = searchpos(a:patterns[1], 'bc', head1[0]) + call setpos('.', s:lib.c2p(head1)) + let tail1 = searchpos(a:patterns[0], 'ce', head2[0]) + + if tail1 == s:null_coord || head2 == s:null_coord + \ || s:lib.is_ahead(s:lib.c2p(head1), s:lib.c2p(tail1)) + \ || s:lib.is_ahead(s:lib.c2p(head2), s:lib.c2p(tail2)) + \ || s:lib.is_equal_or_ahead(s:lib.c2p(tail1), s:lib.c2p(head2)) + return s:null_4pos + endif + + let target = { + \ 'head1': head1, 'tail1': tail1, + \ 'head2': head2, 'tail2': tail2, + \ } + return map(target, 's:lib.c2p(v:val)') +endfunction +"}}} +function! s:check_textobj_diff(head, tail, candidate, opt_noremap) abort "{{{ + let target = deepcopy(s:null_4pos) + if has_key(a:candidate, 'excursus') + let coord = a:candidate.excursus.coord + let target.head1 = s:lib.c2p(coord.head) + let target.tail1 = s:lib.get_left_pos(s:lib.c2p(coord.inner_head)) + let target.head2 = s:lib.get_right_pos(s:lib.c2p(coord.inner_tail)) + let target.tail2 = s:lib.c2p(coord.tail) + + if target.head1 == a:head && target.tail2 == a:tail + \ && target.tail1 != s:null_pos && target.head2 != s:null_pos + \ && s:lib.is_equal_or_ahead(target.tail1, target.head1) + \ && s:lib.is_equal_or_ahead(target.tail2, target.head2) + return target + endif + endif + + let [textobj_i, textobj_a] = a:candidate.external + let [visual_head, visual_tail] = [getpos("'<"), getpos("'>")] + let visualmode = visualmode() + if a:opt_noremap + let cmd = 'silent! normal!' + let v = 'v' + else + let cmd = 'silent! normal' + let v = s:KEY_v + endif + + let order_list = [[1, a:head], [1, a:tail]] + if has_key(a:candidate, 'excursus') + let order_list = [[a:candidate.excursus.count, a:candidate.excursus.cursor]] + order_list + endif + + let found = 0 + for [l:count, cursor] in order_list + " get outer positions + let [target.head1, target.tail2, motionwise_a] = s:get_textobj_region(cursor, cmd, v, l:count, textobj_a) + + " get inner positions + let [target.tail1, target.head2, motionwise_i] = s:get_textobj_region(cursor, cmd, v, l:count, textobj_i) + if motionwise_i ==# "\" + normal! gv + if getpos('.')[1] == target.head2[1] + normal! O + let target.tail1 = getpos('.') + normal! o + let target.head2 = getpos('.') + else + normal! O + let target.head2 = getpos('.') + normal! o + let target.tail1 = getpos('.') + endif + execute "normal! \" + endif + + " check validity + if target.head1 == a:head && target.tail2 == a:tail + \ && target.tail1 != s:null_pos && target.head2 != s:null_pos + \ && s:lib.is_ahead(target.tail1, target.head1) + \ && s:lib.is_ahead(target.tail2, target.head2) + let [target.tail1, target.head2] = s:lib.get_wider_region(target.tail1, target.head2) + let found = 1 + break + endif + endfor + + " restore visualmode + if visualmode ==# '' + call visualmode(1) + else + execute 'normal! ' . visualmode + execute 'normal! ' . "\" + endif + " restore marks + call setpos("'<", visual_head) + call setpos("'>", visual_tail) + + if found + return target + else + return s:null_4pos + endif +endfunction +"}}} +function! s:get_textobj_region(cursor, cmd, visualmode, count, key_seq) abort "{{{ + call setpos('.', a:cursor) + execute printf('%s %s%d%s', a:cmd, a:visualmode, a:count, a:key_seq) + if mode() ==? 'v' || mode() ==# "\" + execute "normal! \" + else + return [copy(s:null_coord), copy(s:null_coord), a:visualmode] + endif + let visualmode = visualmode() + let [head, tail] = [getpos("'<"), getpos("'>")] + " NOTE: V never comes for v. Thus if head == tail == self.cursor, then + " it is failed. + if head == a:cursor && tail == a:cursor + let [head, tail] = [copy(s:null_pos), copy(s:null_pos)] + elseif visualmode ==# 'V' + let tail[2] = col([tail[1], '$']) + endif + return [head, tail, visualmode] +endfunction +"}}} +function! s:get_patterns(candidate, opt_regex) abort "{{{ + let patterns = deepcopy(a:candidate.buns) + if !a:opt_regex + let patterns = map(patterns, 's:lib.escape(v:val)') + endif + + " substitute a break "\n" to a regular expression pattern '\n' + let patterns = map(patterns, 'substitute(v:val, ''\n'', ''\\n'', ''g'')') + return patterns +endfunction +"}}} +" function! s:searchpos_bce(curpos, pattern, stopline) "{{{ +if s:has_patch_7_4_771 + function! s:searchpos_bce(curpos, pattern, stopline) abort + return searchpos(a:pattern, 'bce', a:stopline) + endfunction +else + " workaround for unicode string (because of a bug of vim) + " If the cursor is on a unicode character(uc), searchpos(uc, 'bce', stopline) always returns [0, 0], + " though searchpos(uc, 'bce') returns a correct value. + function! s:searchpos_bce(curpos, pattern, stopline) abort + if a:curpos[1] == line('$') && a:curpos[2] == col([line('$'), '$']) + silent! normal! h + return searchpos(a:pattern, 'e', a:stopline) + else + silent! normal! l + return searchpos(a:pattern, 'be', a:stopline) + endif + endfunction +endif +"}}} +function! s:get_cursorchar(pos) abort "{{{ + let reg = ['"', getreg('"'), getregtype('"')] + try + call setpos('.', a:pos) + silent noautocmd normal! yl + let c = @@ + finally + call call('setreg', reg) + endtry + return c +endfunction +"}}} +" function! s:shortest(list) abort "{{{ +if s:has_patch_7_4_358 + function! s:shortest(list) abort + call map(a:list, '[v:val, s:get_buf_length(v:val.head1, v:val.tail2)]') + call sort(a:list, 's:compare_buf_length') + return a:list[0][0] + endfunction + + function! s:compare_buf_length(i1, i2) abort + return a:i2[1] - a:i1[1] + endfunction +else + function! s:shortest(list) abort + call map(a:list, '[v:val, s:get_buf_length(v:val.head1, v:val.tail2)]') + let len = len(a:list) + let min = len - 1 + if len - 2 >= 0 + for i in range(len - 2, 0, -1) + if a:list[min][1] >= a:list[i][1] + let min = i + endif + endfor + endif + return a:list[min][0] + endfunction +endif +"}}} +function! s:get_buf_length(start, end) abort "{{{ + if a:start[1] == a:end[1] + let len = a:end[2] - a:start[2] + 1 + else + let length_list = map(getline(a:start[1], a:end[1]), 'len(v:val) + 1') + let idx = 0 + let accumm_length = 0 + let accumm_list = [0] + for length in length_list[1:] + let accumm_length = accumm_length + length_list[idx] + let accumm_list += [accumm_length] + let idx += 1 + endfor + let len = accumm_list[a:end[1] - a:start[1]] + a:end[2] - a:start[2] + 1 + endif + return len +endfunction +"}}} +function! s:skip_space(head, tail) abort "{{{ + " NOTE: This function is destructive, directly update a:head and a:tail. + call setpos('.', a:head) + if a:head[2] == col([a:head[1], '$']) + " if the cursor is on a line breaking, it should not be skipped. + let head = a:head + else + let head = s:lib.c2p(searchpos('\_S', 'c', a:tail[1])) + endif + + call setpos('.', a:tail) + if a:tail[2] == 1 + let tail = a:tail + else + let tail = s:lib.c2p(searchpos('\_S', 'bc', a:head[1])) + endif + + if head != s:null_pos && tail != s:null_pos && s:lib.is_equal_or_ahead(tail, head) + let a:head[0:3] = head[0:3] + let a:tail[0:3] = tail[0:3] + endif +endfunction +"}}} + + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich.vim new file mode 100644 index 00000000..716989f6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich.vim @@ -0,0 +1,52 @@ +" prepare recipes +function! sandwich#get_recipes() abort "{{{ + if exists('b:sandwich_recipes') + let recipes = b:sandwich_recipes + elseif exists('g:sandwich#recipes') + let recipes = g:sandwich#recipes + else + let recipes = g:sandwich#default_recipes + endif + return deepcopy(recipes) +endfunction +"}}} +if exists('g:sandwich#default_recipes') + unlockvar! g:sandwich#default_recipes +endif +let g:sandwich#default_recipes = [ + \ {'buns': ['\s\+', '\s\+'], 'regex': 1, 'kind': ['delete', 'replace', 'query'], 'input': [' ']}, + \ {'buns': ['', ''], 'action': ['add'], 'motionwise': ['line'], 'linewise': 1, 'input': ["\"]}, + \ {'buns': ['^$', '^$'], 'regex': 1, 'linewise': 1, 'input': ["\"]}, + \ {'buns': ['<', '>'], 'expand_range': 0}, + \ {'buns': ['"', '"'], 'quoteescape': 1, 'expand_range': 0, 'nesting': 0, 'linewise': 0}, + \ {'buns': ["'", "'"], 'quoteescape': 1, 'expand_range': 0, 'nesting': 0, 'linewise': 0}, + \ {'buns': ["`", "`"], 'quoteescape': 1, 'expand_range': 0, 'nesting': 0, 'linewise': 0}, + \ {'buns': ['{', '}'], 'nesting': 1, 'skip_break': 1}, + \ {'buns': ['[', ']'], 'nesting': 1}, + \ {'buns': ['(', ')'], 'nesting': 1}, + \ {'buns': 'sandwich#magicchar#t#tag()', 'listexpr': 1, 'kind': ['add'], 'action': ['add'], 'input': ['t', 'T']}, + \ {'buns': 'sandwich#magicchar#t#tag()', 'listexpr': 1, 'kind': ['replace'], 'action': ['add'], 'input': ['T']}, + \ {'buns': 'sandwich#magicchar#t#tagname()', 'listexpr': 1, 'kind': ['replace'], 'action': ['add'], 'input': ['t']}, + \ {'external': ["\(textobj-sandwich-tag-i)", "\(textobj-sandwich-tag-a)"], 'noremap' : 0, 'kind' : ['delete', 'textobj'], 'expr_filter': ['operator#sandwich#kind() !=# "replace"'], 'linewise': 1, 'input': ['t', 'T']}, + \ {'external': ["\(textobj-sandwich-tag-i)", "\(textobj-sandwich-tag-a)"], 'noremap' : 0, 'kind' : ['replace', 'query'], 'expr_filter': ['operator#sandwich#kind() ==# "replace"'], 'input': ['T']}, + \ {'external': ["\(textobj-sandwich-tagname-i)", "\(textobj-sandwich-tagname-a)"], 'noremap' : 0, 'kind' : ['replace', 'textobj'], 'expr_filter': ['operator#sandwich#kind() ==# "replace"'], 'input': ['t']}, + \ {'buns': ['sandwich#magicchar#f#fname()', '")"'], 'kind': ['add', 'replace'], 'action': ['add'], 'expr': 1, 'input': ['f']}, + \ {'external': ["\(textobj-sandwich-function-ip)", "\(textobj-sandwich-function-i)"], 'noremap': 0, 'kind': ['delete', 'replace', 'query'], 'input': ['f']}, + \ {'external': ["\(textobj-sandwich-function-ap)", "\(textobj-sandwich-function-a)"], 'noremap': 0, 'kind': ['delete', 'replace', 'query'], 'input': ['F']}, + \ {'buns': 'sandwich#magicchar#i#input("operator")', 'kind': ['add', 'replace'], 'action': ['add'], 'listexpr': 1, 'input': ['i']}, + \ {'buns': 'sandwich#magicchar#i#input("textobj", 1)', 'kind': ['delete', 'replace', 'query'], 'listexpr': 1, 'input': ['i']}, + \ {'buns': 'sandwich#magicchar#i#lastinput("operator", 1)', 'kind': ['add', 'replace'], 'action': ['add'], 'listexpr': 1, 'input': ['I']}, + \ {'buns': 'sandwich#magicchar#i#lastinput("textobj")', 'kind': ['delete', 'replace', 'query'], 'listexpr': 1, 'input': ['I']}, + \ ] +lockvar! g:sandwich#default_recipes + +let g:sandwich#jsx_filetypes = [ + \ 'javascript.jsx', + \ 'typescript.tsx', + \ 'javascriptreact', + \ 'typescriptreact' + \ ] + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/clock.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/clock.vim new file mode 100644 index 00000000..76180df6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/clock.vim @@ -0,0 +1,60 @@ +" clock object - measuring elapsed time in a operation + +" variables "{{{ +" features +let s:has_reltime_and_float = has('reltime') && has('float') +"}}} + +function! sandwich#clock#new() abort "{{{ + return deepcopy(s:clock) +endfunction +"}}} + +" s:clock "{{{ +let s:clock = { + \ 'started' : 0, + \ 'paused' : 0, + \ 'zerotime': reltime(), + \ 'stoptime': reltime(), + \ 'losstime': 0, + \ } +"}}} +function! s:clock.start() dict abort "{{{ + if self.started + if self.paused + let self.losstime += str2float(reltimestr(reltime(self.stoptime))) + let self.paused = 0 + endif + else + if s:has_reltime_and_float + let self.zerotime = reltime() + let self.started = 1 + endif + endif +endfunction +"}}} +function! s:clock.pause() dict abort "{{{ + let self.stoptime = reltime() + let self.paused = 1 +endfunction +"}}} +function! s:clock.elapsed() dict abort "{{{ + if self.started + let total = str2float(reltimestr(reltime(self.zerotime))) + return floor((total - self.losstime)*1000) + else + return 0 + endif +endfunction +"}}} +function! s:clock.stop() dict abort "{{{ + let self.started = 0 + let self.paused = 0 + let self.losstime = 0 +endfunction +"}}} + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/constants.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/constants.vim new file mode 100644 index 00000000..32e5759d --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/constants.vim @@ -0,0 +1,42 @@ +" constants - storing essential constants + +" variables "{{{ +" types +let s:type_list = type([]) +"}}} + +function! sandwich#constants#get(name) abort "{{{ + return type(a:name) == s:type_list + \ ? map(copy(a:name), 's:constants[v:val]()') + \ : s:constants[a:name]() +endfunction +"}}} + +" s:constants "{{{ +let s:constants = {} +"}}} +" The maximum number of columns "{{{ +function! s:constants.colmax() dict abort + return s:colmax_obtained ? s:colmax : s:colmax() +endfunction + +let s:colmax = 2147483647 " default value in many cases +let s:colmax_obtained = 0 +function! s:colmax() abort + let view = winsaveview() + try + normal! $ + let colmax = winsaveview().curswant + call winrestview(view) + let s:colmax_obtained = 1 + catch + let colmax = s:colmax + let s:colmax_obtained = 0 + endtry + return colmax +endfunction +"}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/filetype/tex.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/filetype/tex.vim new file mode 100644 index 00000000..04e2e61b --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/filetype/tex.vim @@ -0,0 +1,34 @@ +let g:sandwich#filetype#tex#environments = get(g:, 'sandwich#filetype#tex#environments', [ + \ 'array', 'center', 'description', 'enumerate', 'eqnarray', 'equation', + \ 'equation*', 'figure', 'flushleft', 'flushright', 'itemize', 'list', + \ 'minipage', 'picture', 'quotation', 'quote', 'tabbing', 'table', + \ 'tabular', 'tabular*', 'thebibliography', 'theorem', 'titlepage', + \ 'verbatim', 'verse' + \ ]) + +function! sandwich#filetype#tex#EnvCompl(argread, cmdline, cursorpos) abort + let n = strlen(a:argread) + if exists('b:sandwich#filetype#tex#environments') + let list = copy(b:sandwich#filetype#tex#environments) + else + let list = copy(g:sandwich#filetype#tex#environments) + endif + if n > 0 + let list = filter(list, 'v:val[: n-1] ==# a:argread') + endif + return list +endfunction + +function! sandwich#filetype#tex#EnvInput() abort + echohl MoreMsg + let env = input('Environment-name: ', '', 'customlist,sandwich#filetype#tex#EnvCompl') + echohl NONE + return [printf('\begin{%s}', env), printf('\end{%s}', env)] +endfunction + +function! sandwich#filetype#tex#CmdInput() abort + echohl MoreMsg + let cmd = input('Command: ', '') + echohl NONE + return [printf('\%s{', cmd), '}'] +endfunction diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/highlight.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/highlight.vim new file mode 100644 index 00000000..9574b38c --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/highlight.vim @@ -0,0 +1,514 @@ +" highlight object - managing highlight on a buffer + +" variables "{{{ +" null valiables +let s:null_pos = [0, 0, 0, 0] + +" types +let s:type_list = type([]) + +" patchs +if v:version > 704 || (v:version == 704 && has('patch237')) + let s:has_patch_7_4_362 = has('patch-7.4.362') + let s:has_patch_7_4_392 = has('patch-7.4.392') +else + let s:has_patch_7_4_362 = v:version == 704 && has('patch362') + let s:has_patch_7_4_392 = v:version == 704 && has('patch392') +endif + +" features +let s:has_gui_running = has('gui_running') +let s:has_window_ID = exists('*win_getid') + +" SID +function! s:SID() abort + return matchstr(expand(''), '\zs\d\+\ze_SID$') +endfunction +let s:SID = printf("\%s_", s:SID()) +delfunction s:SID +"}}} + +function! sandwich#highlight#new() abort "{{{ + return deepcopy(s:highlight) +endfunction +"}}} + +" s:highlight "{{{ +let s:highlight = { + \ 'status': 0, + \ 'group' : '', + \ 'id' : [], + \ 'order_list': [], + \ 'region': {}, + \ 'linewise': 0, + \ 'bufnr': 0, + \ 'winid': 0, + \ } +"}}} +function! s:highlight.initialize() dict abort "{{{ + call self.quench() + let self.status = 0 + let self.group = '' + let self.id = [] + let self.order_list = [] + let self.region = {} + let self.linewise = 0 + let self.bufnr = 0 + let self.winid = 0 +endfunction +"}}} +function! s:highlight.order(target, linewise) dict abort "{{{ + let order = [] + let order_list = [] + for [head, tail, linewise] in [[a:target.head1, a:target.tail1, a:linewise[0]], + \ [a:target.head2, a:target.tail2, a:linewise[1]]] + if linewise + call s:highlight_order_linewise(order_list, order, head, tail) + else + call s:highlight_order_charwise(order_list, order, head, tail) + endif + endfor + if order != [] + call add(order_list, order) + endif + let self.order_list += order_list + let self.region = deepcopy(a:target) + let self.linewise = a:linewise +endfunction +"}}} +function! s:highlight.show(...) dict abort "{{{ + if self.order_list == [] + return 0 + endif + + if a:0 < 1 + if self.group ==# '' + return 0 + else + let hi_group = self.group + endif + else + let hi_group = a:1 + endif + + if self.status + if hi_group ==# self.group + return 0 + else + call self.quench() + endif + endif + + for order in self.order_list + let self.id += s:matchaddpos(hi_group, order) + endfor + call filter(self.id, 'v:val > 0') + let self.status = 1 + let self.group = hi_group + let self.bufnr = bufnr('%') + if s:has_window_ID + let self.winid = win_getid() + endif + return 1 +endfunction +"}}} +function! s:highlight.quench() dict abort "{{{ + if !self.status + return 0 + endif + + let tabnr = tabpagenr() + let winnr = winnr() + let view = winsaveview() + if self.highlighted_window() + call s:matchdelete_all(self.id) + let self.status = 0 + return 1 + endif + + if s:is_in_cmdline_window() || s:is_in_popup_terminal_window() + let s:paused += [self] + augroup sandwich-pause-quenching + autocmd! + autocmd WinEnter * call s:got_out_of_cmdwindow() + augroup END + return 0 + endif + + if self.goto_highlighted_window() + call s:matchdelete_all(self.id) + else + call filter(self.id, 0) + endif + let self.status = 0 + call s:goto_window(winnr, tabnr, view) + return 1 +endfunction +"}}} +function! s:highlight.quench_timer(time) dict abort "{{{ + let id = timer_start(a:time, s:SID . 'quench') + let s:quench_table[string(id)] = self + " this is called when user gets control again + call timer_start(1, {-> s:set_autocmds(id)}) + return id +endfunction +"}}} +" function! s:highlight.highlighted_window() dict abort "{{{ +if s:has_window_ID + function! s:highlight.highlighted_window() dict abort + return self.winid == win_getid() + endfunction +else + function! s:highlight.highlighted_window() dict abort + if self.id == [] + return 0 + endif + + let id = self.id[0] + return filter(getmatches(), 'v:val.id == id') != [] ? 1 : 0 + endfunction +endif +"}}} +" function! s:highlight.goto_highlighted_window() dict abort "{{{ +if s:has_window_ID + function! s:highlight.goto_highlighted_window() dict abort + noautocmd let reached = win_gotoid(self.winid) + return reached + endfunction +else + function! s:highlight.goto_highlighted_window() dict abort + return s:search_highlighted_windows(self.id, tabpagenr()) != [0, 0] + endfunction +endif +"}}} + +" for delayed quenching "{{{ +let s:quench_table = {} +let s:paused = [] +function! s:quench(id) abort "{{{ + let options = s:shift_options() + let highlight = s:get(a:id) + try + if highlight != {} + call highlight.quench() + endif + catch /^Vim\%((\a\+)\)\=:E523/ + " NOTE: For "textlock" + if highlight != {} + call highlight.quench_timer(50) + endif + return 1 + finally + unlet s:quench_table[a:id] + call timer_stop(a:id) + call s:clear_autocmds() + call s:restore_options(options) + endtry +endfunction +"}}} +function! s:get(id) abort "{{{ + return get(s:quench_table, string(a:id), {}) +endfunction +"}}} +function! sandwich#highlight#cancel(...) abort "{{{ + if a:0 > 0 + let id_list = type(a:1) == s:type_list ? a:1 : a:000 + else + let id_list = map(keys(s:quench_table), 'str2nr(v:val)') + endif + + for id in id_list + call s:quench(id) + endfor +endfunction +"}}} +function! s:quench_paused(...) abort "{{{ + if s:is_in_cmdline_window() || s:is_in_popup_terminal_window() + return + endif + + augroup sandwich-pause-quenching + autocmd! + augroup END + + for highlight in s:paused + call highlight.quench() + endfor + let s:paused = [] +endfunction +"}}} +function! s:got_out_of_cmdwindow() abort "{{{ + augroup sandwich-pause-quenching + autocmd! + autocmd CursorMoved * call s:quench_paused() + augroup END +endfunction +"}}} +function! s:set_autocmds(id) abort "{{{ + augroup sandwich-highlight + autocmd! + execute printf('autocmd TextChanged,InsertEnter,BufUnload call s:cancel_highlight(%s)', a:id) + execute printf('autocmd BufEnter * call s:switch_highlight(%s)', a:id) + augroup END +endfunction +"}}} +function! s:clear_autocmds() abort "{{{ + augroup sandwich-highlight + autocmd! + augroup END +endfunction +"}}} +function! s:cancel_highlight(id) abort "{{{ + call s:quench(a:id) +endfunction +"}}} +function! s:switch_highlight(id) abort "{{{ + let highlight = s:get(a:id) + if highlight == {} || !highlight.highlighted_window() + return + endif + + if highlight.bufnr == bufnr('%') + call highlight.show() + else + call highlight.quench() + endif +endfunction +"}}} +"}}} + +" private functions +function! s:highlight_order_charwise(order_list, order, head, tail) abort "{{{ + let n = len(a:order) + if a:head != s:null_pos && a:tail != s:null_pos && s:is_equal_or_ahead(a:tail, a:head) + if a:head[1] == a:tail[1] + call add(a:order, a:head[1:2] + [a:tail[2] - a:head[2] + 1]) + let n += 1 + else + for lnum in range(a:head[1], a:tail[1]) + if lnum == a:head[1] + call add(a:order, a:head[1:2] + [col([a:head[1], '$']) - a:head[2] + 1]) + elseif lnum == a:tail[1] + call add(a:order, [a:tail[1], 1] + [a:tail[2]]) + else + call add(a:order, [lnum]) + endif + + if n == 7 + call add(a:order_list, deepcopy(a:order)) + call filter(a:order, 0) + let n = 0 + else + let n += 1 + endif + endfor + endif + endif +endfunction +"}}} +function! s:highlight_order_linewise(order_list, order, head, tail) abort "{{{ + let n = len(a:order) + if a:head != s:null_pos && a:tail != s:null_pos && a:head[1] <= a:tail[1] + for lnum in range(a:head[1], a:tail[1]) + call add(a:order, [lnum]) + if n == 7 + call add(a:order_list, deepcopy(a:order)) + call filter(a:order, 0) + let n = 0 + else + let n += 1 + endif + endfor + endif +endfunction +"}}} +" function! s:matchaddpos(group, pos) abort "{{{ +if s:has_patch_7_4_362 + function! s:matchaddpos(group, pos) abort + return [matchaddpos(a:group, a:pos)] + endfunction +else + function! s:matchaddpos(group, pos) abort + let id_list = [] + for pos in a:pos + if len(pos) == 1 + let id_list += [matchadd(a:group, printf('\%%%dl', pos[0]))] + else + let id_list += [matchadd(a:group, printf('\%%%dl\%%>%dc.*\%%<%dc', pos[0], pos[1]-1, pos[1]+pos[2]))] + endif + endfor + return id_list + endfunction +endif +"}}} +function! s:matchdelete_all(ids) abort "{{{ + if empty(a:ids) + return + endif + + let alive_ids = map(getmatches(), 'v:val.id') + " Return if another plugin called clearmatches() which clears *ALL* + " highlights including others set. + if empty(alive_ids) + return + endif + if !count(alive_ids, a:ids[0]) + return + endif + + for id in a:ids + try + call matchdelete(id) + catch + endtry + endfor + call filter(a:ids, 0) +endfunction +"}}} +function! s:is_ahead(pos1, pos2) abort "{{{ + return a:pos1[1] > a:pos2[1] || (a:pos1[1] == a:pos2[1] && a:pos1[2] > a:pos2[2]) +endfunction +"}}} +function! s:is_equal_or_ahead(pos1, pos2) abort "{{{ + return a:pos1[1] > a:pos2[1] || (a:pos1[1] == a:pos2[1] && a:pos1[2] >= a:pos2[2]) +endfunction +"}}} +" function! s:is_in_cmdline_window() abort "{{{ +if s:has_patch_7_4_392 + function! s:is_in_cmdline_window() abort + return getcmdwintype() !=# '' + endfunction +else + function! s:is_in_cmdline_window() abort + let is_in_cmdline_window = 0 + try + execute 'tabnext ' . tabpagenr() + catch /^Vim\%((\a\+)\)\=:E11/ + let is_in_cmdline_window = 1 + catch + finally + return is_in_cmdline_window + endtry + endfunction +endif +"}}} +" function! s:is_in_popup_terminal_window() abort "{{{ +if exists('*popup_list') + function! s:is_in_popup_terminal_window() abort + return &buftype is# 'terminal' && count(popup_list(), win_getid()) + endfunction +else + function! s:is_in_popup_terminal_window() abort + return 0 + endfunction +endif +" }}} +function! s:shift_options() abort "{{{ + let options = {} + + """ tweak appearance + " hide_cursor + if s:has_gui_running + let options.cursor = &guicursor + set guicursor+=a:block-NONE + else + let options.cursor = &t_ve + set t_ve= + endif + + return options +endfunction +"}}} +function! s:restore_options(options) abort "{{{ + if s:has_gui_running + set guicursor& + let &guicursor = a:options.cursor + else + let &t_ve = a:options.cursor + endif +endfunction +"}}} +function! s:search_highlighted_windows(id, ...) abort "{{{ + if a:id == [] + return 0 + endif + + let original_winnr = winnr() + let original_tabnr = tabpagenr() + let original_view = winsaveview() + let tablist = range(1, tabpagenr('$')) + if a:0 > 0 + let tabnr = a:1 + let [tabnr, winnr] = s:scan_windows(a:id, tabnr) + if tabnr != 0 + return [tabnr, winnr] + endif + call filter(tablist, 'v:val != tabnr') + endif + + for tabnr in tablist + let [tabnr, winnr] = s:scan_windows(a:id, tabnr) + if tabnr != 0 + return [tabnr, winnr] + endif + endfor + call s:goto_window(original_winnr, original_tabnr, original_view) + return [0, 0] +endfunction +"}}} +function! s:scan_windows(id, tabnr) abort "{{{ + if s:goto_tab(a:tabnr) + for winnr in range(1, winnr('$')) + if s:goto_window(winnr) && s:is_highlighted_window(a:id) + return [a:tabnr, winnr] + endif + endfor + endif + return [0, 0] +endfunction +"}}} +function! s:is_highlighted_window(id) abort "{{{ + if a:id != [] + let id = a:id[0] + if filter(getmatches(), 'v:val.id == id') != [] + return 1 + endif + endif + return 0 +endfunction +"}}} +function! s:goto_window(winnr, ...) abort "{{{ + if a:0 > 0 + if !s:goto_tab(a:1) + return 0 + endif + endif + + + try + if a:winnr != winnr() + execute printf('noautocmd %swincmd w', a:winnr) + endif + catch /^Vim\%((\a\+)\)\=:E16/ + return 0 + endtry + + if a:0 > 1 + noautocmd call winrestview(a:2) + endif + + return 1 +endfunction +"}}} +function! s:goto_tab(tabnr) abort "{{{ + if a:tabnr != tabpagenr() + execute 'noautocmd tabnext ' . a:tabnr + endif + return tabpagenr() == a:tabnr ? 1 : 0 +endfunction +"}}} + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/magicchar/f.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/magicchar/f.vim new file mode 100644 index 00000000..ccdf3730 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/magicchar/f.vim @@ -0,0 +1,438 @@ +let s:save_cpo = &cpo +set cpo&vim + +" variables "{{{ +let s:FALSE = 0 +let s:TRUE = 1 +let s:null_pos = [0, 0] + +" patchs +if v:version > 704 || (v:version == 704 && has('patch237')) + let s:has_patch_7_4_1685 = has('patch-7.4.1685') + let s:has_patch_7_4_2011 = has('patch-7.4.2011') +else + let s:has_patch_7_4_1685 = v:version == 704 && has('patch1685') + let s:has_patch_7_4_2011 = v:version == 704 && has('patch2011') +endif +"}}} + +" default patterns +let g:sandwich#magicchar#f#default_patterns = [ + \ { + \ 'header' : '\<\h\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] + +function! sandwich#magicchar#f#fname() abort "{{{ + call operator#sandwich#show() + try + echohl MoreMsg + if &filetype ==# 'vim' + let funcname = input('funcname: ', '', 'custom,sandwich#magicchar#f#fnamecompl_vim') + else + let funcname = input('funcname: ', '', 'custom,sandwich#magicchar#f#fnamecompl') + endif + " flash prompt + echo '' + finally + echohl NONE + call operator#sandwich#quench() + endtry + if funcname ==# '' + throw 'OperatorSandwichCancel' + endif + return funcname . '(' +endfunction +"}}} +function! sandwich#magicchar#f#fnamecompl(ArgLead, CmdLine, CursorPos) abort "{{{ + return join(uniq(sort(s:buffer_completion())), "\n") +endfunction +"}}} +function! sandwich#magicchar#f#fnamecompl_vim(ArgLead, CmdLine, CursorPos) abort "{{{ + if s:has_patch_7_4_2011 + let getcomp = map(filter(getcompletion(a:ArgLead, 'function'), 'v:val =~# ''\C^[a-z][a-zA-Z0-9_]*($'''), 'matchstr(v:val, ''\C^[a-z][a-zA-Z0-9_]*'')') + else + let getcomp = [] + endif + let buffer = s:buffer_completion() + return join(uniq(sort(getcomp + buffer)), "\n") +endfunction +"}}} +function! s:buffer_completion() abort "{{{ + " NOTE: This func does neither sort nor uniq. + let list = [] + let lines = getline(1, '$') + let pattern_list = s:resolve_patterns() + for func in pattern_list + let pat = printf('%s\ze%s', func.header, func.bra) + for line in lines + let list += s:extract_pattern(line, pat) + endfor + endfor + return list +endfunction +"}}} +function! s:extract_pattern(string, pat) abort "{{{ + let list = [] + let end = 0 + while 1 + let [str, start, end] = s:matchstrpos(a:string, a:pat, end) + if start < 0 + break + endif + let list += [str] + endwhile + return list +endfunction +"}}} +" function! s:matchstrpos(expr, pat, ...) abort "{{{ +if s:has_patch_7_4_1685 + let s:matchstrpos = function('matchstrpos') +else + function! s:matchstrpos(expr, pat, ...) abort + if a:0 == 0 + return [matchstr(a:expr, a:pat), match(a:expr, a:pat), matchend(a:expr, a:pat)] + elseif a:0 == 1 + return [matchstr(a:expr, a:pat, a:1), match(a:expr, a:pat, a:1), matchend(a:expr, a:pat, a:1)] + else + return [matchstr(a:expr, a:pat, a:1, a:2), match(a:expr, a:pat, a:1, a:2), matchend(a:expr, a:pat, a:1, a:2)] + endif + endfunction +endif +"}}} + + + +" textobj-functioncall (bundle version) +" NOTE: https://github.com/machakann/vim-textobj-functioncall + +function! sandwich#magicchar#f#i(mode) abort "{{{ + call sandwich#magicchar#f#textobj('i', a:mode) +endfunction +"}}} +function! sandwich#magicchar#f#a(mode) abort "{{{ + call sandwich#magicchar#f#textobj('a', a:mode) +endfunction +"}}} +function! sandwich#magicchar#f#ip(mode) abort "{{{ + call sandwich#magicchar#f#textobj('ip', a:mode) +endfunction +"}}} +function! sandwich#magicchar#f#ap(mode) abort "{{{ + call sandwich#magicchar#f#textobj('ap', a:mode) +endfunction +"}}} +function! sandwich#magicchar#f#textobj(kind, mode, ...) abort "{{{ + let l:count = v:count1 + if a:0 + let pattern_list = a:1 + else + let pattern_list = s:resolve_patterns() + endif + let searchlines = s:get('textobj_sandwich_function_searchlines' , 30) + let stopline = {} + if searchlines < 0 + let stopline.upper = 1 + let stopline.lower = line('$') + else + let stopline.upper = max([1, line('.') - searchlines]) + let stopline.lower = min([line('.') + searchlines, line('$')]) + endif + + let [start, end] = [s:null_pos, s:null_pos] + let view = winsaveview() + try + let candidates = s:gather_candidates(a:kind, a:mode, l:count, pattern_list, stopline) + let elected = s:elect(candidates, l:count) + if elected != {} + let [start, end] = s:to_range(a:kind, elected) + endif + finally + call winrestview(view) + endtry + call s:select(start, end) +endfunction +"}}} + + +function! s:Candidate(head, bra, ket, tail, pat, rank) abort + return { + \ 'head': a:head, + \ 'bra': a:bra, + \ 'ket': a:ket, + \ 'tail': a:tail, + \ 'rank': a:rank, + \ 'pattern': a:pat, + \ 'len': s:buflen(a:head, a:tail), + \ } +endfunction + + +function! s:gather_candidates(kind, mode, count, pattern_list, stopline) abort "{{{ + let curpos = getpos('.')[1:2] + let rank = 0 + let candidates = [] + for pattern in a:pattern_list + let rank += 1 + let candidates += s:search_pattern(pattern, a:kind, a:mode, a:count, rank, curpos, a:stopline) + call cursor(curpos) + endfor + return a:kind[0] is# 'a' && candidates == [] + \ ? s:gather_candidates('i', a:mode, a:count, a:pattern_list, a:stopline) + \ : candidates +endfunction +"}}} +function! s:search_pattern(pat, kind, mode, count, rank, curpos, stopline) abort "{{{ + let a:pat.head = a:pat.header . a:pat.bra + let a:pat.tail = a:pat.ket . a:pat.footer + + let brapos = s:search_key_bra(a:kind, a:curpos, a:pat, a:stopline) + if brapos == s:null_pos | return [] | endif + let is_string = s:is_string_syntax(brapos) + + let candidates = [] + while len(candidates) < a:count + let c = s:get_candidate(a:pat, a:kind, a:mode, a:rank, brapos, is_string, a:stopline) + if c != {} + call add(candidates, c) + endif + call cursor(brapos) + + " move to the next 'bra' + let brapos = searchpairpos(a:pat.bra, '', a:pat.ket, 'b', '', a:stopline.upper) + if brapos == s:null_pos | break | endif + let is_string = s:is_string_syntax(brapos) + endwhile + return candidates +endfunction +"}}} +function! s:search_key_bra(kind, curpos, pat, stopline) abort "{{{ + let brapos = s:null_pos + if a:kind[0] is# 'a' + " search for the first 'bra' + if searchpos(a:pat.tail, 'cn', a:stopline.lower) == a:curpos + let brapos = searchpairpos(a:pat.bra, '', a:pat.ket, 'b', '', a:stopline.upper) + endif + let brapos = searchpairpos(a:pat.bra, '', a:pat.ket, 'b', '', a:stopline.upper) + elseif a:kind[0] is# 'i' + let head_start = searchpos(a:pat.head, 'bc', a:stopline.upper) + let head_end = searchpos(a:pat.head, 'ce', a:stopline.lower) + call cursor(a:curpos) + let tail_start = searchpos(a:pat.tail, 'bc', a:stopline.upper) + let tail_end = searchpos(a:pat.tail, 'ce', a:stopline.lower) + + " check the initial position + if s:is_in_between(a:curpos, head_start, head_end) + " cursor is on a header + call cursor(head_end) + elseif s:is_in_between(a:curpos, tail_start, tail_end) + " cursor is on a footer + call cursor(tail_start) + if tail_start[1] == 1 + normal! k$ + else + normal! h + endif + else + " cursor is in between a bra and a ket + call cursor(a:curpos) + endif + + " move to the corresponded 'bra' + let brapos = searchpairpos(a:pat.bra, '', a:pat.ket, 'bc', '', a:stopline.upper) + endif + return brapos +endfunction +"}}} +function! s:get_candidate(pat, kind, mode, rank, brapos, is_string, stopline) abort "{{{ + " 'bra' should accompany with 'header' + let headstart = searchpos(a:pat.head, 'bc', a:stopline.upper) + let headend = searchpos(a:pat.head, 'ce', a:stopline.lower) + call cursor(a:brapos) + if !s:is_in_between(a:brapos, headstart, headend) + return {} + endif + let headpos = headstart + + " search for the paired 'ket' + let skip = 's:is_string_syntax(getpos(".")[1:2]) != a:is_string' + let ketpos = searchpairpos(a:pat.bra, '', a:pat.ket, '', skip, a:stopline.lower) + if ketpos == s:null_pos + return {} + endif + let tailpos = searchpos(a:pat.tail, 'ce', a:stopline.lower) + + if searchpos(a:pat.tail, 'bcn', a:stopline.upper) != ketpos + return {} + endif + + let c = s:Candidate(headpos, a:brapos, ketpos, tailpos, a:pat, a:rank) + if !s:is_valid_candidate(c, a:kind, a:mode, a:is_string) + return {} + endif + return c +endfunction +"}}} +function! s:elect(candidates, count) abort "{{{ + if a:candidates == [] + return {} + endif + let filter = 'v:val.head != s:null_pos && v:val.bra != s:null_pos && v:val.ket != s:null_pos && v:val.tail != s:null_pos' + call filter(a:candidates, filter) + call sort(a:candidates, 's:compare') + if len(a:candidates) < a:count + return {} + endif + return a:candidates[a:count - 1] +endfunction +"}}} +function! s:to_range(kind, candidate) abort "{{{ + if a:kind[1] is# 'p' + let [start, end] = s:parameter_region(a:candidate) + else + let start = a:candidate.head + let end = a:candidate.tail + endif + return [start, end] +endfunction +"}}} +function! s:select(start, end) abort "{{{ + if a:start == s:null_pos || a:end == s:null_pos + return + endif + normal! v + call cursor(a:start) + normal! o + call cursor(a:end) + if &selection is# 'exclusive' + normal! l + endif +endfunction +"}}} +function! s:is_in_between(pos, start, end) abort "{{{ + return (a:pos != s:null_pos) && (a:start != s:null_pos) && (a:end != s:null_pos) + \ && ((a:pos[0] > a:start[0]) || ((a:pos[0] == a:start[0]) && (a:pos[1] >= a:start[1]))) + \ && ((a:pos[0] < a:end[0]) || ((a:pos[0] == a:end[0]) && (a:pos[1] <= a:end[1]))) +endfunction +"}}} +function! s:is_string_syntax(pos) abort "{{{ + return match(map(synstack(a:pos[0], a:pos[1]), 'synIDattr(synIDtrans(v:val), "name")'), 'String') > -1 +endfunction +"}}} +function! s:is_continuous_syntax(brapos, ketpos) abort "{{{ + let start_col = a:brapos[1] + for lnum in range(a:brapos[0], a:ketpos[0]) + if lnum == a:ketpos[0] + let end_col= a:ketpos[1] + else + let end_col= col([lnum, '$']) + endif + for col in range(start_col, end_col) + if match(map(synstack(lnum, col), 'synIDattr(synIDtrans(v:val), "name")'), 'String') < 0 + return 0 + endif + endfor + let start_col = 1 + endfor + return 1 +endfunction +"}}} +function! s:is_valid_syntax(candidate, is_string) abort "{{{ + return !a:is_string || s:is_continuous_syntax(a:candidate.bra, a:candidate.ket) +endfunction +"}}} +function! s:is_same_or_adjacent(p1, p2) abort "{{{ + return a:p1 == a:p2 || (a:p1[0] == a:p2[0] && a:p1[1]+1 == a:p2[1]) +endfunction +"}}} +function! s:is_wider(candidate, start, end) abort "{{{ + return (s:is_ahead(a:start, a:candidate.head) && s:is_same_or_ahead(a:candidate.tail, a:end)) || + \ (s:is_same_or_ahead(a:start, a:candidate.head) && s:is_ahead(a:candidate.tail, a:end)) +endfunction +"}}} +function! s:is_ahead(p1, p2) abort "{{{ + return (a:p1[0] > a:p2[0]) || (a:p1[0] == a:p2[0] && a:p1[1] > a:p2[1]) +endfunction +"}}} +function! s:is_same_or_ahead(p1, p2) abort "{{{ + return (a:p1[0] > a:p2[0]) || (a:p1[0] == a:p2[0] && a:p1[1] >= a:p2[1]) +endfunction +"}}} +function! s:is_valid_candidate(c, kind, mode, is_string) abort "{{{ + if a:kind[1] is# 'p' && s:is_same_or_adjacent(a:c.bra, a:c.ket) + return s:FALSE + endif + if a:mode is# 'x' && !s:is_wider(a:c, getpos("'<")[1:2], getpos("'>")[1:2]) + return s:FALSE + endif + if !s:is_valid_syntax(a:c, a:is_string) + return s:FALSE + endif + return s:TRUE +endfunction +"}}} +function! s:resolve_patterns() abort "{{{ + return deepcopy(get(b:, 'sandwich_magicchar_f_patterns', + \ get(g:, 'sandwich#magicchar#f#patterns', + \ g:sandwich#magicchar#f#default_patterns))) +endfunction +"}}} +function! s:parameter_region(candidate) abort "{{{ + let whichwrap = &whichwrap + let &whichwrap = 'h,l' + let [visualhead, visualtail] = [getpos("'<"), getpos("'>")] + try + normal! v + call cursor(a:candidate.bra) + call search(a:candidate.pattern.bra, 'ce', a:candidate.ket[0]) + normal! l + let head = getpos('.')[1:2] + normal! o + call cursor(a:candidate.ket) + normal! h + let tail = getpos('.')[1:2] + execute "normal! \" + finally + let &whichwrap = whichwrap + call setpos("'<", visualhead) + call setpos("'>", visualtail) + endtry + return [head, tail] +endfunction +"}}} +function! s:buflen(start, end) abort "{{{ + " start, end -> [lnum, col] + if a:start[0] == a:end[0] + let len = a:end[1] - a:start[1] + 1 + else + let len = (line2byte(a:end[0]) + a:end[1]) - (line2byte(a:start[0]) + a:start[1]) + 1 + endif + return len +endfunction +"}}} +function! s:compare(i1, i2) abort "{{{ + " i1, i2 -> Candidate + if a:i1.len < a:i2.len + return -1 + elseif a:i1.len > a:i2.len + return 1 + else + return a:i2.rank - a:i1.rank + endif +endfunction +"}}} +function! s:get(name, default) abort "{{{ + return exists('b:' . a:name) ? b:[a:name] + \ : exists('g:' . a:name) ? g:[a:name] + \ : a:default +endfunction +"}}} + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/magicchar/i.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/magicchar/i.vim new file mode 100644 index 00000000..473474e1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/magicchar/i.vim @@ -0,0 +1,102 @@ +let s:last_inserted = [] +let s:last_searched = [] + +" variables "{{{ +" patchs +if v:version > 704 || (v:version == 704 && has('patch237')) + let s:has_patch_7_4_1685 = has('patch-7.4.1685') +else + let s:has_patch_7_4_1685 = v:version == 704 && has('patch1685') +endif +"}}} + +function! sandwich#magicchar#i#input(kind, ...) abort "{{{ + let storepoint = a:kind ==# 'operator' ? 'last_inserted' : 'last_searched' + let former = s:input(a:kind . '-sandwich:head: ') + let quit_if_empty = get(a:000, 0, 0) + if quit_if_empty && former ==# '' + let s:{storepoint} = [] + return ['', ''] + endif + let latter = s:input(a:kind . '-sandwich:tail: ') + let s:{storepoint} = [former, latter] + return copy(s:{storepoint}) +endfunction +"}}} +function! sandwich#magicchar#i#lastinput(kind, ...) abort "{{{ + let storepoint = a:kind ==# 'operator' ? 'last_inserted' : 'last_searched' + let cancel_if_invalid = get(a:000, 0, 0) + if cancel_if_invalid && s:{storepoint} == [] + throw 'OperatorSandwichCancel' + endif + return s:{storepoint} != [] ? copy(s:{storepoint}) : ['', ''] +endfunction +"}}} +function! sandwich#magicchar#i#compl(ArgLead, CmdLine, CursorPos) abort "{{{ + let list = [] + let lines = getline(1, '$') + if a:ArgLead ==# '' + let list += s:extract_patterns_from_lines(lines, '\<\k\{3,}\>') + else + let list += s:extract_patterns_from_lines(lines, printf('\<%s.\{-}\>', a:ArgLead)) + if list == [] + let tail = matchstr(a:ArgLead, '\<\k\+$') + if tail !=# '' && tail !=# a:ArgLead + let list += s:extract_patterns_from_lines(lines, tail . '.\{-}\>') + endif + endif + endif + return join(uniq(sort(list)), "\n") +endfunction +"}}} +function! s:input(mes) abort "{{{ + echohl MoreMsg + try + let input = input(a:mes, '', 'custom,sandwich#magicchar#i#compl') + " flash prompt + echo '' + finally + echohl NONE + endtry + return input +endfunction +"}}} +function! s:extract_patterns_from_lines(lines, pat) abort "{{{ + let list = [] + for line in a:lines + let list += s:extract_pattern(line, a:pat) + endfor + return list +endfunction +"}}} +function! s:extract_pattern(string, pat) abort "{{{ + let list = [] + let end = 0 + while 1 + let [str, start, end] = s:matchstrpos(a:string, a:pat, end) + if start < 0 + break + endif + let list += [str] + endwhile + return list +endfunction +"}}} +" function! s:matchstrpos(expr, pat, ...) abort "{{{ +if s:has_patch_7_4_1685 + let s:matchstrpos = function('matchstrpos') +else + function! s:matchstrpos(expr, pat, ...) abort + if a:0 == 0 + return [matchstr(a:expr, a:pat), match(a:expr, a:pat), matchend(a:expr, a:pat)] + elseif a:0 == 1 + return [matchstr(a:expr, a:pat, a:1), match(a:expr, a:pat, a:1), matchend(a:expr, a:pat, a:1)] + else + return [matchstr(a:expr, a:pat, a:1, a:2), match(a:expr, a:pat, a:1, a:2), matchend(a:expr, a:pat, a:1, a:2)] + endif + endfunction +endif +"}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/magicchar/t.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/magicchar/t.vim new file mode 100644 index 00000000..e30ea7da --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/magicchar/t.vim @@ -0,0 +1,446 @@ +" variables "{{{ +let s:null_coord = [0, 0] + +" types +let s:type_num = type(0) +let s:type_str = type('') +let s:type_list = type([]) +"}}} + +function! sandwich#magicchar#t#tag() abort "{{{ + call operator#sandwich#show() + try + echohl MoreMsg + let old_imsearch = &l:imsearch + let &l:imsearch = 0 + let tag = input('Input tag: ') + let &l:imsearch = old_imsearch + " flash prompt + echo '' + finally + echohl NONE + call operator#sandwich#quench() + endtry + if tag ==# '' + throw 'OperatorSandwichCancel' + endif + let [open, close] = s:expand(tag) + return [printf('<%s>', open), printf('', close)] +endfunction +"}}} +function! sandwich#magicchar#t#tagname() abort "{{{ + call operator#sandwich#show() + try + echohl MoreMsg + let old_imsearch = &l:imsearch + let &l:imsearch = 0 + let tagname = input('Input tag name: ') + let &l:imsearch = old_imsearch + " flash prompt + echo '' + finally + echohl NONE + call operator#sandwich#quench() + endtry + if tagname ==# '' + throw 'OperatorSandwichCancel' + endif + return s:expand(tagname) +endfunction +"}}} +function! s:tokenize(text) abort "{{{ + let j = 0 + let n = strlen(a:text) + let tokenlist = [] + while j >= 0 && j < n + let i = j + let j = match(a:text, '\m[[[:blank:]#.]', i) + if i < j + let tokenlist += [a:text[i : j-1]] + elseif i == j + let char = a:text[i] + if char =~# '\m\s' + " space is not allowed + throw 'SandwichMagiccharTIncorrectSyntax' + elseif char ==# '[' + " tokenize custom attributes + let tokenlist += [char] + let j += 1 + let [j, tokenlist] += s:tokenize_custom_attributes(a:text, j) + else + let tokenlist += [char] + let j += 1 + endif + endif + endwhile + + let i = j >= 0 ? j : i + if i < n + let tokenlist += [a:text[i :]] + endif + return tokenlist +endfunction +"}}} +function! s:tokenize_custom_attributes(text, j) abort "{{{ + let j = a:j + let n = strlen(a:text) + let closed = 0 + let tokenlist = [] + while j >= 0 && j < n + let i = j + let j = match(a:text, '\m[][:blank:]="'']', i) + if i < j + let string = a:text[i : j-1] + let tokenlist += [string] + elseif i == j + let char = a:text[i] + if char =~# '\m\s' + " skip space + let j = matchend(a:text, '\m\s\+', i) + if j > i + let tokenlist += [a:text[i : j-1]] + endif + elseif char =~# '\m["'']' + " skip string literal + let j = match(a:text, char, i+1) + if j > 0 + let tokenlist += [a:text[i : j]] + let j += 1 + else + " unclosed string literal + throw 'SandwichMagiccharTIncorrectSyntax' + endif + elseif char ==# ']' + " the end of custom attributes region + let tokenlist += [char] + let j += 1 + let closed = 1 + break + else + let tokenlist += [char] + let j += 1 + endif + endif + endwhile + if !closed + " unclosed braket + throw 'SandwichMagiccharTIncorrectSyntax' + endif + return [j - a:j, tokenlist] +endfunction +"}}} + + +function! s:matches_filetype(filetype, list) abort "{{{ + return index(a:list, a:filetype) != -1 +endfunction +" }}} + +function! s:parse(tokenlist) abort "{{{ + let itemdict = deepcopy(s:itemdict) + let itemlist = map(copy(a:tokenlist), '{"is_operator": v:val =~# ''^\%([][#.=]\|\s\+\)$'' ? 1 : 0, "string": v:val}') + + let i = 0 + let n = len(itemlist) + let item = itemlist[i] + if item.is_operator + call itemdict.queue_element('div') + else + call itemdict.queue_element(item.string) + let i += 1 + endif + while i < n + let item = itemlist[i] + if item.is_operator + if item.string ==# '#' + let i = s:handle_id(itemdict, itemlist, i) + elseif item.string ==# '.' + if s:matches_filetype(&filetype, g:sandwich#jsx_filetypes) + let i = s:handle_className(itemdict, itemlist, i) + else + let i = s:handle_class(itemdict, itemlist, i) + endif + elseif item.string ==# '[' + let i = s:parse_custom_attributes(itemdict, itemlist, i) + else + " unanticipated operator (should not reach here) + throw 'SandwichMagiccharTIncorrectSyntax' + endif + else + " successive operand is not allowed here (should not reach here) + throw 'SandwichMagiccharTIncorrectSyntax' + endif + endwhile + + let parsed = deepcopy(itemdict.queue) + for item in parsed + call filter(item, 'v:key =~# ''\%(name\|value\)''') + endfor + return parsed +endfunction +"}}} +function! s:parse_custom_attributes(itemdict, itemlist, i) abort "{{{ + " check ket + let i_ket = s:check_ket(a:itemlist, a:i) + if i_ket < 0 + " ket does not exist (should not reach here) + throw 'SandwichMagiccharTIncorrectSyntax' + endif + + let i = a:i + 1 + while i < i_ket + let item = a:itemlist[i] + if item.is_operator + if item.string ==# '=' + let i = s:handle_equal(a:itemdict, a:itemlist, i) + else + let i += 1 + endif + else + let i = s:handle_custom_attr_name(a:itemdict, a:itemlist, i) + endif + endwhile + call a:itemdict.queue_custom_attr() + return i_ket + 1 +endfunction +"}}} +function! s:handle_id(itemdict, itemlist, i) abort "{{{ + let i = a:i + 1 + let item = get(a:itemlist, i, {'is_operator': 1}) + if item.is_operator + call a:itemdict.queue_id() + else + call a:itemdict.queue_id(item.string) + let i += 1 + endif + return i +endfunction +"}}} +function! s:handle_class(itemdict, itemlist, i) abort "{{{ + let i = a:i + 1 + let item = get(a:itemlist, i, {'is_operator': 1}) + if item.is_operator + call a:itemdict.queue_class() + else + call a:itemdict.queue_class(item.string) + let i += 1 + endif + return i +endfunction +"}}} +function! s:handle_className(itemdict, itemlist, i) abort "{{{ + let i = a:i + 1 + let item = get(a:itemlist, i, {'is_operator': 1}) + if item.is_operator + call a:itemdict.queue_className() + else + call a:itemdict.queue_className(item.string) + let i += 1 + endif + return i +endfunction +"}}} +function! s:handle_equal(itemdict, itemlist, i, ...) abort "{{{ + let i = a:i + 1 + let item = a:itemlist[i] + let name = get(a:000, 0, '') + if item.is_operator + if item.string ==# '=' + call a:itemdict.list_custom_attr(name) + else + call a:itemdict.list_custom_attr(name) + let i += 1 + endif + else + call a:itemdict.list_custom_attr(name, item.string) + let i += 1 + endif + return i +endfunction +"}}} +function! s:handle_custom_attr_name(itemdict, itemlist, i) abort "{{{ + let item = a:itemlist[a:i] + let name = item.string + let i = a:i + 1 + let item = a:itemlist[i] + if item.is_operator + if item.string ==# '=' + let i = s:handle_equal(a:itemdict, a:itemlist, i, name) + else + call a:itemdict.list_custom_attr(name) + let i += 1 + endif + else + call a:itemdict.list_custom_attr(name) + endif + return i +endfunction +"}}} +function! s:check_ket(itemlist, i) abort "{{{ + let i = a:i + 1 + let n = len(a:itemlist) + let i_ket = -1 + while i < n + let item = a:itemlist[i] + if item.is_operator && item.string ==# ']' + let i_ket = i + break + endif + let i += 1 + endwhile + return i_ket +endfunction +"}}} +function! s:build(itemlist) abort "{{{ + let fragments = [a:itemlist[0]['value']] + if len(a:itemlist) > 1 + for item in a:itemlist[1:] + let name = item.name + let value = type(item.value) == s:type_list ? join(filter(item.value, 'v:val !=# ""')) : item.value + let value = value !~# '^\(["'']\).*\1$' ? printf('"%s"', value) : value + let fragments += [printf('%s=%s', name, value)] + endfor + endif + return join(fragments) +endfunction +"}}} +function! s:expand(text) abort "{{{ + try + let tokenlist = s:tokenize(a:text) + let itemlist = s:parse(tokenlist) + catch /^SandwichMagiccharTIncorrectSyntax$/ + return [a:text, matchstr(a:text, '^\s*\zs\a[^[:blank:]>/]*')] + endtry + let element = itemlist[0]['value'] + return [s:build(itemlist), element] +endfunction +"}}} + +function! sandwich#magicchar#t#i() abort "{{{ + call s:prototype('i') +endfunction +"}}} +function! sandwich#magicchar#t#a() abort "{{{ + call s:prototype('a') +endfunction +"}}} +function! s:prototype(kind) abort "{{{ + let view = winsaveview() + let visualhead = getpos("'<") + let visualtail = getpos("'>") + execute printf('silent! normal! v%dat', v:count1) + execute "normal! \" + if getpos("'<") != getpos("'>") + normal! gv + let pat = a:kind ==# 'i' ? '/]*' : '/]*\ze\s*>' + call search(pat, 'be', line("'<")) + normal! o + let pat = a:kind ==# 'i' ? '<\a[^[:blank:]>/]*\zs\_.' : '<\zs\a[^[:blank:]>/]*' + call search(pat, '', line("'>")) + else + call winrestview(view) + call setpos("'<", visualhead) + call setpos("'>", visualtail) + endif +endfunction +"}}} +function! sandwich#magicchar#t#it() abort "{{{ + call s:textobj('i') +endfunction +"}}} +function! sandwich#magicchar#t#at() abort "{{{ + call s:textobj('a') +endfunction +"}}} +function! s:textobj(a_or_i) abort "{{{ + let head = searchpos('<\a[^[:blank:]>/]*', 'bcn', 0, 50) + if head != s:null_coord + let tagname = matchstr(getline(head[0])[head[1]-1 :], '^<\zs\a[^[:blank:]>/]*') + if search(printf('', s:escape(tagname)), 'cn', 0, 50) + " add :silent! to suppress errorbell + if a:a_or_i ==# 'i' + silent! normal! vit + else + silent! normal! vat + endif + endif + endif +endfunction +"}}} +function! s:escape(string) abort "{{{ + return escape(a:string, '~"\.^$[]*') +endfunction +"}}} + +" itemdict object "{{{ +let s:itemdict = { + \ 'element': {'name': 'element', 'value': ''}, + \ 'id' : {'name': 'id', 'value': '', 'queued': 0}, + \ 'class' : {'name': 'class', 'value': [], 'queued': 0}, + \ 'className' : {'name': 'className', 'value': [], 'queued': 0}, + \ 'queue' : [], + \ 'customlist': [], + \ } + +function! s:itemdict.queue_element(value) dict abort "{{{ + let self.element.value = a:value + call add(self.queue, self.element) +endfunction +"}}} +function! s:itemdict.queue_id(...) dict abort "{{{ + let self.id.value = get(a:000, 0, '') + if !self.id.queued + call add(self.queue, self.id) + let self.id.queued = 1 + endif +endfunction +"}}} +function! s:itemdict.queue_class(...) dict abort "{{{ + call add(self.class.value, get(a:000, 0, '')) + if !self.class.queued + call add(self.queue, self.class) + let self.class.queued = 1 + endif +endfunction +"}}} +function! s:itemdict.queue_className(...) dict abort "{{{ + call add(self.className.value, get(a:000, 0, '')) + if !self.className.queued + call add(self.queue, self.className) + let self.className.queued = 1 + endif +endfunction +"}}} +function! s:itemdict.queue_custom_attr() dict abort "{{{ + if self.customlist != [] + call extend(self.queue, remove(self.customlist, 0, -1)) + endif +endfunction +"}}} +function! s:itemdict.list_custom_attr(name, ...) dict abort "{{{ + let value = get(a:000, 0, '') + if a:name ==# 'id' + call self.queue_id(value) + elseif a:name ==# 'class' + call self.queue_class(value) + endif + + if a:name ==# '' + let custom_attr = {'kind': 'custom', 'name': a:name, 'value': value} + call add(self.customlist, custom_attr) + else + if !has_key(self, a:name) + let self[a:name] = {'kind': 'custom', 'name': a:name, 'value': value} + call add(self.customlist, self[a:name]) + else + let self[a:name]['value'] = value + endif + endif +endfunction +"}}} +"}}} + + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/messenger.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/messenger.vim new file mode 100644 index 00000000..5f0ddecd --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/messenger.vim @@ -0,0 +1,67 @@ +" messenger object - managing messages and errors. + +function! sandwich#messenger#new() abort "{{{ + let s:messenger = deepcopy(s:messenger_prototype) + return s:messenger +endfunction +"}}} +function! sandwich#messenger#get() abort "{{{ + return s:messenger +endfunction +"}}} + +" s:messenger_prototype "{{{ +let s:messenger_prototype = { + \ 'error' : {'flag': 0, 'string': ''}, + \ 'notice': {'flag': 0, 'list': []}, + \ } +"}}} +function! s:messenger_prototype.initialize() dict abort "{{{ + let self.error.flag = 0 + let self.error.string = '' + let self.notice.flag = 0 + let self.notice.list = [] +endfunction +"}}} +function! s:messenger_prototype.notify(prefix) dict abort "{{{ + if self.error.flag + call self.error.echoerr(a:prefix) + elseif self.notice.flag + call self.notice.echo(a:prefix) + endif +endfunction +"}}} +function! s:messenger_prototype.error.echoerr(prefix) dict abort "{{{ + echoerr a:prefix . self.string +endfunction +"}}} +function! s:messenger_prototype.notice.echo(prefix) dict abort "{{{ + let queue = [] + if self.list != [] + for line in self.list + let queue += [[a:prefix, 'MoreMsg']] + let queue += [line] + let queue += [["\n", 'NONE']] + endfor + call remove(queue, -1) + endif + call sandwich#util#echo(queue) +endfunction +"}}} +function! s:messenger_prototype.error.set(errmes) dict abort "{{{ + let self.flag = 1 + let self.string = a:errmes +endfunction +"}}} +function! s:messenger_prototype.notice.queue(line) dict abort "{{{ + let self.flag = 1 + call add(self.list, a:line) +endfunction +"}}} + +call sandwich#messenger#new() + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/opt.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/opt.vim new file mode 100644 index 00000000..494613b6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/opt.vim @@ -0,0 +1,346 @@ +" opt object - managing options + +function! sandwich#opt#new(kind, defaultopt, argopt) abort "{{{ + let opt = deepcopy(s:opt) + if a:kind ==# 'auto' || a:kind ==# 'query' + let opt.recipe = {} + let opt.of = function('s:of_for_textobj') + let opt.filter = s:default_values['textobj']['filter'] + call opt.update('arg', a:argopt) + call opt.update('default', a:defaultopt) + else + let opt.recipe_add = {} + let opt.recipe_delete = {} + let opt.of = function('s:of_for_' . a:kind) + let opt.filter = s:default_values[a:kind]['filter'] + call opt.update('arg', a:argopt) + endif + return opt +endfunction +"}}} +function! sandwich#opt#defaults(kind, ...) abort "{{{ + if a:kind ==# 'auto' || a:kind ==# 'query' + return deepcopy(s:default_values['textobj'][a:kind]) + elseif a:kind ==# 'add' || a:kind ==# 'delete' || a:kind ==# 'replace' + let motionwise = get(a:000, 0, 'char') + return deepcopy(s:default_values[a:kind][motionwise]) + else + return {} + endif +endfunction +"}}} + +" opt "{{{ +let s:opt = { + \ 'default' : {}, + \ 'arg' : {}, + \ 'filter' : '', + \ } +"}}} +function! s:opt.clear(target) dict abort "{{{ + call filter(self[a:target], 0) +endfunction +"}}} +function! s:opt.update(target, dict) dict abort "{{{ + call self.clear(a:target) + call extend(self[a:target], filter(deepcopy(a:dict), self.filter), 'force') +endfunction +"}}} +function! s:opt.has(opt_name) dict abort "{{{ + return has_key(self.default, a:opt_name) +endfunction +"}}} +function! s:opt.max(opt_name, ...) dict abort "{{{ + let minimum = get(a:000, 0, 0) + let list = [] + if has_key(self['recipe_delete'], a:opt_name) + let list += [self['recipe_delete'][a:opt_name]] + endif + if has_key(self['recipe_add'], a:opt_name) + let list += [self['recipe_add'][a:opt_name]] + endif + if list == [] + let list += [self._of(a:opt_name)] + endif + return max([minimum] + list) +endfunction +"}}} +function! s:opt.get(opt_name, kind, ...) dict abort "{{{ + return self.has(a:opt_name) ? self.of(a:opt_name, a:kind) : get(a:000, 0, 0) +endfunction +"}}} +function! s:opt._of(opt_name, ...) dict abort "{{{ + let kind = get(a:000, 0, '') + if kind !=# '' && has_key(self[kind], a:opt_name) + return self[kind][a:opt_name] + elseif has_key(self['arg'], a:opt_name) + return self['arg'][a:opt_name] + else + return self['default'][a:opt_name] + endif +endfunction +"}}} + +function! s:of_for_add(opt_name, ...) dict abort "{{{ + return self._of(a:opt_name, 'recipe_add') +endfunction +"}}} +function! s:of_for_delete(opt_name, ...) dict abort "{{{ + return self._of(a:opt_name, 'recipe_delete') +endfunction +"}}} +function! s:of_for_replace(opt_name, ...) dict abort "{{{ + let kind = get(a:000, 0, '') + if kind !=# '' + return self._of(a:opt_name, kind) + else + if a:opt_name ==# 'cursor' + " recipe_add > recipe_delete > arg > default + if has_key(self.recipe_add, a:opt_name) + return self.recipe_add[a:opt_name] + else + return self._of(a:opt_name, 'recipe_delete') + endif + elseif a:opt_name ==# 'query_once' + return self._of(a:opt_name, 'recipe_add') + elseif a:opt_name ==# 'regex' + return self._of(a:opt_name, 'recipe_delete') + elseif a:opt_name ==# 'expr' + return self._of(a:opt_name, 'recipe_add') + elseif a:opt_name ==# 'listexpr' + return self._of(a:opt_name, 'recipe_add') + elseif a:opt_name ==# 'noremap' + return self._of(a:opt_name, '') + elseif a:opt_name ==# 'skip_space' + return self._of(a:opt_name, 'recipe_delete') + elseif a:opt_name ==# 'skip_char' + return self._of(a:opt_name, 'recipe_delete') + elseif a:opt_name ==# 'highlight' + return self._of(a:opt_name, '') + elseif a:opt_name ==# 'hi_duration' + return self._of(a:opt_name, '') + elseif a:opt_name ==# 'command' + let commands = [] + let commands += get(self.recipe_delete, a:opt_name, []) + let commands += get(self.recipe_add, a:opt_name, []) + if commands == [] + let commands += self._of(a:opt_name) + endif + return commands + elseif a:opt_name ==# 'linewise' + return self.max(a:opt_name) + elseif a:opt_name ==# 'autoindent' + return self._of(a:opt_name, 'recipe_add') + elseif a:opt_name ==# 'indentkeys' + return self._of(a:opt_name, 'recipe_add') + elseif a:opt_name ==# 'indentkeys+' + return self._of(a:opt_name, 'recipe_add') + elseif a:opt_name ==# 'indentkeys-' + return self._of(a:opt_name, 'recipe_add') + else + " should not reach here! + throw 'OperatorSandwichError:Replace:InvalidOption:' . a:opt_name + endif + endif +endfunction +"}}} +function! s:of_for_textobj(opt_name) dict abort "{{{ + if has_key(self['recipe'], a:opt_name) + return self['recipe'][a:opt_name] + elseif has_key(self['arg'], a:opt_name) + return self['arg'][a:opt_name] + else + return self['default'][a:opt_name] + endif +endfunction +"}}} + +" default values "{{{ +let s:default_values = {} +let s:default_values.add = {} +let s:default_values.add.char = { + \ 'cursor' : 'default', + \ 'query_once' : 0, + \ 'expr' : 0, + \ 'listexpr' : 0, + \ 'noremap' : 1, + \ 'skip_space' : 0, + \ 'highlight' : 3, + \ 'hi_duration': 200, + \ 'command' : [], + \ 'linewise' : 0, + \ 'autoindent' : -1, + \ 'indentkeys' : 0, + \ 'indentkeys+': 0, + \ 'indentkeys-': 0, + \ } +let s:default_values.add.line = { + \ 'cursor' : 'default', + \ 'query_once' : 0, + \ 'expr' : 0, + \ 'listexpr' : 0, + \ 'noremap' : 1, + \ 'skip_space' : 1, + \ 'highlight' : 3, + \ 'hi_duration': 200, + \ 'command' : [], + \ 'linewise' : 1, + \ 'autoindent' : -1, + \ 'indentkeys' : 0, + \ 'indentkeys+': 0, + \ 'indentkeys-': 0, + \ } +let s:default_values.add.block = { + \ 'cursor' : 'default', + \ 'query_once' : 0, + \ 'expr' : 0, + \ 'listexpr' : 0, + \ 'noremap' : 1, + \ 'skip_space' : 1, + \ 'highlight' : 3, + \ 'hi_duration': 200, + \ 'command' : [], + \ 'linewise' : 0, + \ 'autoindent' : -1, + \ 'indentkeys' : 0, + \ 'indentkeys+': 0, + \ 'indentkeys-': 0, + \ } +let s:default_values.add.filter = printf('v:key =~# ''\%%(%s\)''', join(keys(s:default_values['add']['char']), '\|')) + +let s:default_values.delete = {} +let s:default_values.delete.char = { + \ 'cursor' : 'default', + \ 'noremap' : 1, + \ 'regex' : 0, + \ 'skip_space' : 1, + \ 'skip_char' : 0, + \ 'highlight' : 3, + \ 'hi_duration': 200, + \ 'command' : [], + \ 'linewise' : 0, + \ } +let s:default_values.delete.line = { + \ 'cursor' : 'default', + \ 'noremap' : 1, + \ 'regex' : 0, + \ 'skip_space' : 2, + \ 'skip_char' : 0, + \ 'highlight' : 3, + \ 'hi_duration': 200, + \ 'command' : [], + \ 'linewise' : 1, + \ } +let s:default_values.delete.block = { + \ 'cursor' : 'default', + \ 'noremap' : 1, + \ 'regex' : 0, + \ 'skip_space' : 1, + \ 'skip_char' : 0, + \ 'highlight' : 3, + \ 'hi_duration': 200, + \ 'command' : [], + \ 'linewise' : 0, + \ } +let s:default_values.delete.filter = printf('v:key =~# ''\%%(%s\)''', join(keys(s:default_values['delete']['char']), '\|')) + +let s:default_values.replace = {} +let s:default_values.replace.char = { + \ 'cursor' : 'default', + \ 'query_once' : 0, + \ 'regex' : 0, + \ 'expr' : 0, + \ 'listexpr' : 0, + \ 'noremap' : 1, + \ 'skip_space' : 1, + \ 'skip_char' : 0, + \ 'highlight' : 3, + \ 'hi_duration': 200, + \ 'command' : [], + \ 'linewise' : 0, + \ 'autoindent' : -1, + \ 'indentkeys' : 0, + \ 'indentkeys+': 0, + \ 'indentkeys-': 0, + \ } +let s:default_values.replace.line = { + \ 'cursor' : 'default', + \ 'query_once' : 0, + \ 'regex' : 0, + \ 'expr' : 0, + \ 'listexpr' : 0, + \ 'noremap' : 1, + \ 'skip_space' : 2, + \ 'skip_char' : 0, + \ 'highlight' : 3, + \ 'hi_duration': 200, + \ 'command' : [], + \ 'linewise' : 0, + \ 'autoindent' : -1, + \ 'indentkeys' : 0, + \ 'indentkeys+': 0, + \ 'indentkeys-': 0, + \ } +let s:default_values.replace.block = { + \ 'cursor' : 'default', + \ 'query_once' : 0, + \ 'regex' : 0, + \ 'expr' : 0, + \ 'listexpr' : 0, + \ 'noremap' : 1, + \ 'skip_space' : 1, + \ 'skip_char' : 0, + \ 'highlight' : 3, + \ 'hi_duration': 200, + \ 'command' : [], + \ 'linewise' : 0, + \ 'autoindent' : -1, + \ 'indentkeys' : 0, + \ 'indentkeys+': 0, + \ 'indentkeys-': 0, + \ } +let s:default_values.replace.filter = printf('v:key =~# ''\%%(%s\)''', join(keys(s:default_values['replace']['char']), '\|')) + +let s:default_values.textobj = {} +let s:default_values.textobj.auto = { + \ 'expr' : 0, + \ 'listexpr' : 0, + \ 'regex' : 0, + \ 'skip_regex' : [], + \ 'skip_regex_head': [], + \ 'skip_regex_tail': [], + \ 'quoteescape' : 0, + \ 'expand_range' : -1, + \ 'nesting' : 0, + \ 'synchro' : 1, + \ 'noremap' : 1, + \ 'syntax' : [], + \ 'inner_syntax' : [], + \ 'match_syntax' : 0, + \ 'skip_break' : 0, + \ 'skip_expr' : [], + \ } +let s:default_values.textobj.query = { + \ 'expr' : 0, + \ 'listexpr' : 0, + \ 'regex' : 0, + \ 'skip_regex' : [], + \ 'skip_regex_head': [], + \ 'skip_regex_tail': [], + \ 'quoteescape' : 0, + \ 'expand_range' : -1, + \ 'nesting' : 0, + \ 'synchro' : 1, + \ 'noremap' : 1, + \ 'syntax' : [], + \ 'inner_syntax' : [], + \ 'match_syntax' : 0, + \ 'skip_break' : 0, + \ 'skip_expr' : [], + \ } +let s:default_values.textobj.filter = printf('v:key =~# ''\%%(%s\)''', join(keys(s:default_values.textobj.auto), '\|')) +"}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/util.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/util.vim new file mode 100644 index 00000000..b42470f3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/sandwich/util.vim @@ -0,0 +1,41 @@ +function! sandwich#util#echo(messages) abort "{{{ + echo '' + for [mes, hi_group] in a:messages + execute 'echohl ' . hi_group + echon mes + echohl NONE + endfor +endfunction +"}}} +function! sandwich#util#addlocal(recipes) abort "{{{ + return s:extend_local(a:recipes) +endfunction +"}}} +function! sandwich#util#insertlocal(recipes) abort "{{{ + return s:extend_local(a:recipes, 0) +endfunction +"}}} +function! sandwich#util#ftrevert(filetype) abort "{{{ + if exists('b:sandwich_recipes') + call filter(b:sandwich_recipes, 'get(v:val, "__filetype__", "") !=# a:filetype') + endif +endfunction +"}}} + + +function! s:extend_local(recipes, ...) abort "{{{ + if !exists('b:sandwich_recipes') + if exists('g:sandwich#recipes') + let b:sandwich_recipes = deepcopy(g:sandwich#recipes) + else + let b:sandwich_recipes = deepcopy(g:sandwich#default_recipes) + endif + endif + let i = get(a:000, 0, len(b:sandwich_recipes)) + call extend(b:sandwich_recipes, copy(a:recipes), i) + return b:sandwich_recipes +endfunction "}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich.vim new file mode 100644 index 00000000..aac10860 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich.vim @@ -0,0 +1,204 @@ +" textobj-sandwich: pick up a sandwich! + +function! textobj#sandwich#auto(mode, a_or_i, ...) abort "{{{ + let kind = 'auto' + let defaultopt = s:default_options(kind) + let argopt = get(a:000, 0, {}) + let opt = sandwich#opt#new(kind, defaultopt, argopt) + if a:0 >= 2 + let recipes = textobj#sandwich#recipes#new(kind, a:mode, a:2) + else + let recipes = textobj#sandwich#recipes#new(kind, a:mode) + endif + call recipes.uniq(opt) + + if recipes.integrated != [] + let g:textobj#sandwich#object = textobj#sandwich#textobj#new( + \ kind, a:a_or_i, a:mode, v:count1, recipes, opt) + let cmd = ":\call textobj#sandwich#select()\" + else + let cmd = a:mode ==# 'o' ? "\" : "\" + endif + return cmd +endfunction +"}}} +function! textobj#sandwich#query(mode, a_or_i, ...) abort "{{{ + let kind = 'query' + let defaultopt = s:default_options(kind) + let argopt = get(a:000, 0, {}) + let opt = sandwich#opt#new(kind, defaultopt, argopt) + if a:0 >= 2 + let recipes = textobj#sandwich#recipes#new(kind, a:mode, a:2) + else + let recipes = textobj#sandwich#recipes#new(kind, a:mode) + endif + let timeout = s:get_sandwich_option('timeout', &timeout) + let timeoutlen = max([0, s:get_sandwich_option('timeoutlen', &timeoutlen)]) + call recipes.query(opt, timeout, timeoutlen) + + if recipes.integrated != [] + let g:textobj#sandwich#object = textobj#sandwich#textobj#new( + \ kind, a:a_or_i, a:mode, v:count1, recipes, opt) + let cmd = ":\call textobj#sandwich#select()\" + else + let cmd = a:mode ==# 'o' ? "\" : "\" + endif + return cmd +endfunction +"}}} +function! textobj#sandwich#select() abort "{{{ + if !exists('g:textobj#sandwich#object') + return + endif + + let view = winsaveview() + let textobj = g:textobj#sandwich#object + call textobj.initialize() + let stimeoutlen = max([0, s:get_textobj_option('stimeoutlen', 500)]) + let [virtualedit, whichwrap] = [&virtualedit, &whichwrap] + let [&virtualedit, &whichwrap] = ['onemore', 'h,l'] + try + let candidates = textobj.list(stimeoutlen) + let elected = textobj.elect(candidates) + call winrestview(view) + call textobj.select(elected) + finally + let mark_latestjump = s:get_textobj_option('latest_jump', 1) + call textobj.finalize(mark_latestjump) + if !textobj.done + call winrestview(view) + endif + let [&virtualedit, &whichwrap] = [virtualedit, whichwrap] + endtry + + let g:textobj#sandwich#object = textobj " This is required in case that textobj-sandwich call textobj-sandwich itself in its recipe. +endfunction +"}}} + +function! s:get_sandwich_option(name, default) abort "{{{ + if exists('g:textobj#sandwich#' . a:name) + return eval('g:textobj#sandwich#' . a:name) + endif + if exists('g:sandwich#' . a:name) + return eval('g:sandwich#' . a:name) + endif + return a:default +endfunction +"}}} +function! s:get_textobj_option(name, default) abort "{{{ + return get(g:, 'textobj#sandwich#' . a:name, a:default) +endfunction +"}}} + +" recipe "{{{ +function! textobj#sandwich#get_recipes() abort "{{{ + if exists('b:textobj_sandwich_recipes') + let recipes = b:textobj_sandwich_recipes + elseif exists('g:textobj#sandwich#recipes') + let recipes = g:textobj#sandwich#recipes + else + let recipes = g:textobj#sandwich#default_recipes + endif + return deepcopy(recipes) +endfunction +"}}} +if exists('g:textobj#sandwich#default_recipes') + unlockvar! g:textobj#sandwich#default_recipes +endif +let g:textobj#sandwich#default_recipes = [] +lockvar! g:textobj#sandwich#default_recipes +"}}} + +" option "{{{ +function! s:default_options(kind) abort "{{{ + return get(b:, 'textobj_sandwich_options', g:textobj#sandwich#options)[a:kind] +endfunction +"}}} +function! s:initialize_options(...) abort "{{{ + let manner = a:0 ? a:1 : 'keep' + let g:textobj#sandwich#options = s:get_textobj_option('options', {}) + for kind in ['auto', 'query'] + if !has_key(g:textobj#sandwich#options, kind) + let g:textobj#sandwich#options[kind] = {} + endif + call extend(g:textobj#sandwich#options[kind], + \ sandwich#opt#defaults(kind), manner) + endfor +endfunction +call s:initialize_options() +"}}} +function! textobj#sandwich#set_default() abort "{{{ + call s:initialize_options('force') +endfunction +"}}} +function! textobj#sandwich#set(kind, option, value) abort "{{{ + if s:argument_error(a:kind, a:option, a:value) + return + endif + + if a:kind ==# 'all' + let kinds = ['auto', 'query'] + else + let kinds = [a:kind] + endif + + for kind in kinds + let g:textobj#sandwich#options[kind][a:option] = a:value + endfor +endfunction +"}}} +function! textobj#sandwich#setlocal(kind, option, value) abort "{{{ + if s:argument_error(a:kind, a:option, a:value) + return + endif + + if !exists('b:textobj_sandwich_options') + let b:textobj_sandwich_options = deepcopy(g:textobj#sandwich#options) + endif + + if a:kind ==# 'all' + let kinds = ['auto', 'query'] + else + let kinds = [a:kind] + endif + + for kind in kinds + let b:textobj_sandwich_options[kind][a:option] = a:value + endfor +endfunction +"}}} +function! s:argument_error(kind, option, value) abort "{{{ + if !(a:kind ==# 'auto' || a:kind ==# 'query' || a:kind ==# 'all') + echohl WarningMsg + echomsg 'Invalid kind "' . a:kind . '".' + echohl NONE + return 1 + endif + + " NOTE: Regaardless of a:kind, keys(defaults) is fixed since + " the two textobjects have same kinds of options. + let defaults = sandwich#opt#defaults('auto') + if filter(keys(defaults), 'v:val ==# a:option') == [] + echohl WarningMsg + echomsg 'Invalid option name "' . a:kind . '".' + echohl NONE + return 1 + endif + + if type(a:value) != type(defaults[a:option]) + echohl WarningMsg + echomsg 'Invalid type of value. ' . string(a:value) + echohl NONE + return 1 + endif + return 0 +endfunction +"}}} +"}}} + +unlet! g:textobj#sandwich#object + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/lib.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/lib.vim new file mode 100644 index 00000000..f7e15ef1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/lib.vim @@ -0,0 +1,128 @@ +" common functions + +" variables "{{{ +" null valiables +let s:null_coord = [0, 0] + +" patchs +if v:version > 704 || (v:version == 704 && has('patch237')) + let s:has_patch_7_4_358 = has('patch-7.4.358') +else + let s:has_patch_7_4_358 = v:version == 704 && has('patch358') +endif +"}}} + +function! s:c2p(coord) abort "{{{ + return [0] + a:coord + [0] +endfunction +"}}} +function! s:escape(string) abort "{{{ + return escape(a:string, '~"\.^$[]*') +endfunction +"}}} +" function! s:sort(list, func, count) abort "{{{ +if s:has_patch_7_4_358 + function! s:sort(list, func, count) abort + return sort(a:list, a:func) + endfunction +else + function! s:sort(list, func, count) abort + " NOTE: len(a:list) is always larger than count or same. + " FIXME: The number of item in a:list would not be large, but if there was + " any efficient argorithm, I would rewrite here. + let len = len(a:list) + for i in range(a:count) + if len - 2 >= i + let min = len - 1 + for j in range(len - 2, i, -1) + if call(a:func, [a:list[min], a:list[j]]) >= 0 + let min = j + endif + endfor + + if min > i + call insert(a:list, remove(a:list, min), i) + endif + endif + endfor + return a:list + endfunction +endif +"}}} +function! s:get_displaycoord(coord) abort "{{{ + let [lnum, col] = a:coord + + if [lnum, col] != s:null_coord + let disp_col = col == 1 ? 1 : strdisplaywidth(getline(lnum)[: col - 2]) + 1 + else + let disp_col = 0 + endif + return [lnum, disp_col] +endfunction +"}}} +function! s:set_displaycoord(disp_coord) abort "{{{ + if a:disp_coord != s:null_coord + execute 'normal! ' . a:disp_coord[0] . 'G' . a:disp_coord[1] . '|' + endif +endfunction +"}}} +function! s:get_displaysyntax(coord) abort "{{{ + return synIDattr(synIDtrans(synID(a:coord[0], a:coord[1], 1)), 'name') +endfunction +"}}} +function! s:is_matched_syntax(coord, syntaxID) abort "{{{ + if a:coord == s:null_coord + return 0 + elseif a:syntaxID == [] + return 1 + else + return s:get_displaysyntax(a:coord) ==? a:syntaxID[0] + endif +endfunction +"}}} +function! s:is_included_syntax(coord, syntaxID) abort "{{{ + let synstack = map(synstack(a:coord[0], a:coord[1]), + \ 'synIDattr(synIDtrans(v:val), "name")') + + if a:syntaxID == [] + return 1 + elseif synstack == [] + if a:syntaxID == [''] + return 1 + else + return 0 + endif + else + return filter(map(copy(a:syntaxID), '''\c'' . v:val'), 'match(synstack, v:val) > -1') != [] + endif +endfunction +"}}} + +function! s:export(namelist) abort "{{{ + let module = {} + for name in a:namelist + let module[name] = function('s:' . name) + endfor + return module +endfunction +"}}} +let s:lib = s:export([ + \ 'c2p', + \ 'escape', + \ 'sort', + \ 'get_displaycoord', + \ 'set_displaycoord', + \ 'get_displaysyntax', + \ 'is_matched_syntax', + \ 'is_included_syntax', + \ ]) +lockvar! s:lib + +function! textobj#sandwich#lib#import() abort "{{{ + return s:lib +endfunction +"}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/recipes.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/recipes.vim new file mode 100644 index 00000000..7bc071c0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/recipes.vim @@ -0,0 +1,278 @@ +" recipes object + +" variables "{{{ +let s:TRUE = 1 +let s:FALSE = 0 + +" types +let s:type_num = type(0) +let s:type_str = type('') +"}}} + +function! textobj#sandwich#recipes#new(kind, mode, ...) abort "{{{ + let recipes = deepcopy(s:recipes) + if a:0 > 0 + let recipes.arg = a:1 + let recipes.arg_given = 1 + endif + call recipes._integrate(a:kind, a:mode) + return recipes +endfunction +"}}} + +" s:recipes "{{{ +let s:recipes = { + \ 'arg': [], + \ 'arg_given': 0, + \ 'integrated': [], + \ } +"}}} +function! s:recipes.uniq(opt) dict abort "{{{ + let opt_regex = a:opt.of('regex') + let opt_expr = a:opt.of('expr') + let opt_noremap = a:opt.of('noremap') + let recipes = copy(self.integrated) + call filter(self.integrated, 0) + while recipes != [] + let recipe = remove(recipes, 0) + call add(self.integrated, recipe) + if has_key(recipe, 'buns') + call filter(recipes, '!s:is_duplicated_buns(v:val, recipe, opt_regex, opt_expr)') + elseif has_key(recipe, 'external') + call filter(recipes, '!s:is_duplicated_external(v:val, recipe, opt_noremap)') + endif + endwhile +endfunction +"}}} +function! s:recipes.query(opt, timeout, timeoutlen) dict abort "{{{ + let recipes = deepcopy(self.integrated) + let clock = sandwich#clock#new() + let input = '' + let last_compl_match = ['', []] + while 1 + let c = getchar(0) + if empty(c) + if clock.started && a:timeout && a:timeoutlen > 0 && clock.elapsed() > a:timeoutlen + let [input, recipes] = last_compl_match + break + else + sleep 20m + continue + endif + endif + + let c = type(c) == s:type_num ? nr2char(c) : c + + " exit loop if is pressed + if c is# "\" + let input = "\" + break + endif + + let input .= c + + " check forward match + let n_fwd = len(filter(recipes, 's:is_input_matched(v:val, input, a:opt, 0)')) + + " check complete match + let n_comp = len(filter(copy(recipes), 's:is_input_matched(v:val, input, a:opt, 1)')) + if n_comp || strchars(input) == 1 + if len(recipes) == n_comp + break + else + call clock.stop() + call clock.start() + let last_compl_match = [input, copy(recipes)] + endif + else + if clock.started && !n_fwd + let [input, recipes] = last_compl_match + " FIXME: Additional keypress, 'c', is ignored now, but it should be pressed. + " The problem is feedkeys() cannot be used for here... + break + endif + endif + if recipes == [] | break | endif + endwhile + call clock.stop() + + if filter(recipes, 's:is_input_matched(v:val, input, a:opt, 1)') != [] + let recipe = recipes[0] + if has_key(recipe, 'buns') || has_key(recipe, 'external') + let self.integrated = [recipe] + else + let self.integrated = [] + endif + else + if s:is_input_fallback(input) + let c = split(input, '\zs')[0] + let recipe = {'buns': [c, c], 'expr': 0, 'regex': 0} + let self.integrated = [recipe] + else + let self.integrated = [] + endif + endif +endfunction +"}}} +function! s:recipes._integrate(kind, mode) dict abort "{{{ + let self.integrated = [] + if self.arg_given + let self.integrated += self.arg + else + let self.integrated += sandwich#get_recipes() + let self.integrated += textobj#sandwich#get_recipes() + endif + let filter = 's:has_filetype(v:val) + \ && s:has_kind(v:val, a:kind) + \ && s:has_mode(v:val, a:mode) + \ && s:has_action(v:val) + \ && s:expr_filter(v:val)' + call filter(self.integrated, filter) + call reverse(self.integrated) +endfunction +"}}} + +" filters +function! s:has_filetype(candidate) abort "{{{ + if !has_key(a:candidate, 'filetype') + return 1 + else + let filetypes = split(&filetype, '\.') + if filetypes == [] + let filter = 'v:val ==# "all" || v:val ==# ""' + else + let filter = 'v:val ==# "all" || index(filetypes, v:val) > -1' + endif + return filter(copy(a:candidate['filetype']), filter) != [] + endif +endfunction +"}}} +function! s:has_kind(candidate, kind) abort "{{{ + if !has_key(a:candidate, 'kind') + return 1 + else + let filter = 'v:val ==# a:kind || v:val ==# "textobj" || v:val ==# "all"' + return filter(copy(a:candidate['kind']), filter) != [] + endif +endfunction +"}}} +function! s:has_mode(candidate, mode) abort "{{{ + if !has_key(a:candidate, 'mode') + return 1 + else + return stridx(join(a:candidate['mode'], ''), a:mode) > -1 + endif +endfunction +"}}} +function! s:has_action(candidate) abort "{{{ + if !has_key(a:candidate, 'action') + return 1 + endif + let filter = 'v:val ==# "delete" || v:val ==# "all"' + return filter(copy(a:candidate['action']), filter) != [] +endfunction +"}}} +function! s:expr_filter(candidate) abort "{{{ + if !has_key(a:candidate, 'expr_filter') + return 1 + else + for filter in a:candidate['expr_filter'] + if !eval(filter) + return 0 + endif + endfor + return 1 + endif +endfunction +"}}} + +function! s:is_duplicated_buns(item, ref, opt_regex, opt_expr) abort "{{{ + if has_key(a:item, 'buns') + \ && type(a:ref['buns'][0]) == s:type_str + \ && type(a:ref['buns'][1]) == s:type_str + \ && type(a:item['buns'][0]) == s:type_str + \ && type(a:item['buns'][1]) == s:type_str + \ && a:ref['buns'][0] ==# a:item['buns'][0] + \ && a:ref['buns'][1] ==# a:item['buns'][1] + let regex_r = get(a:ref, 'regex', a:opt_regex) + let regex_i = get(a:item, 'regex', a:opt_regex) + let expr_r = get(a:ref, 'expr', a:opt_expr) + let expr_i = get(a:item, 'expr', a:opt_expr) + + let expr_r = expr_r ? 1 : 0 + let expr_i = expr_i ? 1 : 0 + + if regex_r == regex_i && expr_r == expr_i + return 1 + endif + endif + return 0 +endfunction +"}}} +function! s:is_duplicated_external(item, ref, opt_noremap) abort "{{{ + if has_key(a:item, 'external') + \ && a:ref['external'][0] ==# a:item['external'][0] + \ && a:ref['external'][1] ==# a:item['external'][1] + let noremap_r = get(a:ref, 'noremap', a:opt_noremap) + let noremap_i = get(a:item, 'noremap', a:opt_noremap) + + if noremap_r == noremap_i + return 1 + endif + endif + + return 0 +endfunction +"}}} +function! s:is_input_matched(candidate, input, opt, flag) abort "{{{ + let has_buns = has_key(a:candidate, 'buns') + let has_ext = has_key(a:candidate, 'external') && has_key(a:candidate, 'input') + if !(has_buns || has_ext) + return 0 + elseif !a:flag && a:input ==# '' + return 1 + endif + + let candidate = deepcopy(a:candidate) + + if has_buns + call a:opt.update('recipe', candidate) + + " 'input' is necessary for 'expr' or 'regex' buns + if (a:opt.of('expr') || a:opt.of('regex')) && !has_key(candidate, 'input') + return 0 + endif + + let inputs = copy(get(candidate, 'input', candidate['buns'])) + elseif has_ext + " 'input' is necessary for 'external' textobjects assignment + if !has_key(candidate, 'input') + return 0 + endif + + let inputs = copy(a:candidate['input']) + endif + + " If a:flag == 0, check forward match. Otherwise, check complete match. + if a:flag + return filter(inputs, 'v:val ==# a:input') != [] + else + let idx = strlen(a:input) - 1 + return filter(inputs, 'v:val[: idx] ==# a:input') != [] + endif +endfunction +"}}} +function! s:is_input_fallback(input) abort "{{{ + if a:input ==# "\" || a:input ==# '' || a:input =~# '^[\x80]' + return s:FALSE + endif + let input_fallback = get(g:, 'sandwich#input_fallback', s:TRUE) + if !input_fallback + return s:FALSE + endif + return s:TRUE +endfunction "}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/sandwich.vim new file mode 100644 index 00000000..9ca8eb3c --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/sandwich.vim @@ -0,0 +1,344 @@ +" sandwich object - manage recipe and positions of a sandwiched text + +" variables "{{{ +" null valiables +let s:null_coord = [0, 0] +let s:null_4coord = { + \ 'head': copy(s:null_coord), + \ 'tail': copy(s:null_coord), + \ 'inner_head': copy(s:null_coord), + \ 'inner_tail': copy(s:null_coord), + \ } + +" types +let s:type_num = type(0) +let s:type_str = type('') +let s:type_list = type([]) +let s:type_fref = type(function('tr')) + +" common functions +let s:lib = textobj#sandwich#lib#import() +"}}} + +function! textobj#sandwich#sandwich#new(recipe, opt) abort "{{{ + let sandwich = deepcopy(s:sandwich) + let sandwich.opt = copy(a:opt) + let sandwich.opt.recipe = {} + call sandwich.opt.update('recipe', a:recipe) + + if has_key(a:recipe, 'buns') + let sandwich.searchby = 'buns' + unlet sandwich.dough + let sandwich.dough = deepcopy(a:recipe.buns) + elseif has_key(a:recipe, 'external') + let sandwich.searchby = 'external' + let sandwich.external = deepcopy(a:recipe.external) + else + return {} + endif + let sandwich.recipe = a:recipe + return sandwich +endfunction +"}}} + +" coord object"{{{ +let s:coord = deepcopy(s:null_4coord) +function! s:coord.initialize() dict abort "{{{ + let self.head = deepcopy(s:null_coord) + let self.tail = deepcopy(s:null_coord) +endfunction +"}}} +function! s:coord.get_inner(buns, skip_break) dict abort "{{{ + call cursor(self.head) + call search(a:buns[0], 'ce', self.tail[0]) + normal! l + if a:skip_break && col('.') == col([line('.'), '$']) + let self.inner_head = searchpos('\_S', '', self.tail[0]) + else + let self.inner_head = getpos('.')[1:2] + endif + + call cursor(self.tail) + call search(a:buns[1], 'bc', self.head[0]) + if a:skip_break && (col('.') < 2 || getline(line('.'))[: col('.')-2] =~# '^\s*$') + let self.inner_tail = searchpos('\_S', 'be', self.head[0]) + else + if getpos('.')[2] == 1 + normal! hl + else + normal! h + endif + let self.inner_tail = getpos('.')[1:2] + endif +endfunction +"}}} +function! s:coord.next() dict abort "{{{ + call cursor(self.head) + normal! h + let self.head = getpos('.')[1:2] +endfunction +"}}} +"}}} +" range object"{{{ +let s:range = { + \ 'valid' : 0, + \ 'top' : 0, + \ 'bottom' : 0, + \ 'toplim' : 0, + \ 'botlim' : 0, + \ 'stride' : &lines, + \ 'count' : 1, + \ } +function! s:range.initialize(cursor, expand_range) dict abort "{{{ + let filehead = 1 + let fileend = line('$') + let self.valid = 1 + let self.top = a:cursor[0] + let self.bottom = a:cursor[0] + if a:expand_range >= 0 + let self.toplim = max([filehead, a:cursor[0] - a:expand_range]) + let self.botlim = min([a:cursor[0] + a:expand_range, fileend]) + else + let self.toplim = filehead + let self.botlim = fileend + endif + let self.count = 1 +endfunction +"}}} +function! s:range.next() dict abort "{{{ + if (self.top == 1/0 && self.bottom < 1) + \ || (self.top <= self.toplim && self.bottom >= self.botlim) + let self.top = 1/0 + let self.bottom = 0 + let self.valid = 0 + else + if self.top > self.toplim + let self.top = self.top - self.stride + let self.top = self.top < self.toplim ? self.toplim : self.top + endif + if self.bottom < self.botlim + let self.bottom = self.bottom + self.stride + let self.bottom = self.bottom > self.botlim ? self.botlim : self.bottom + endif + endif +endfunction +"}}} +"}}} + +" s:sandwich "{{{ +let s:sandwich = { + \ 'buns' : [], + \ 'dough' : [], + \ 'external' : [], + \ 'searchby' : '', + \ 'recipe' : {}, + \ 'cursor' : copy(s:null_coord), + \ 'coord' : deepcopy(s:coord), + \ 'range' : deepcopy(s:range), + \ 'visualmode': '', + \ 'syntax_on' : 1, + \ 'syntax' : [], + \ 'opt' : {}, + \ 'escaped' : 0, + \ 'not_escaped': [], + \ } +"}}} +function! s:sandwich.initialize(cursor, is_syntax_on) dict abort "{{{ + let self.syntax_on = a:is_syntax_on + let self.cursor = a:cursor + call self.coord.initialize() + call self.range.initialize(a:cursor, self.opt.of('expand_range')) + return self +endfunction +"}}} +function! s:sandwich.bake_buns(state, clock) dict abort "{{{ + let opt_listexpr = self.opt.of('listexpr') + let opt_expr = self.opt.of('expr') + let opt_regex = self.opt.of('regex') + + call a:clock.pause() + if a:state + if opt_listexpr + let self.buns = eval(self.dough) + elseif opt_expr + let self.buns = s:evalexpr(self.dough) + else + let self.buns = self.dough + endif + else + if opt_listexpr >= 2 + let self.buns = eval(self.dough) + let self.escaped = 0 + elseif opt_expr >= 2 + let self.buns = s:evalexpr(self.dough) + let self.escaped = 0 + endif + endif + call a:clock.start() + + if !s:valid_buns(self.buns) + return ['', ''] + endif + + if !self.escaped + let self.not_escaped = copy(self.buns) + if !opt_regex + call map(self.buns, 's:lib.escape(v:val)') + let self.escaped = 1 + endif + endif + return self.buns +endfunction +"}}} +function! s:sandwich.searchpair_head(stimeoutlen) dict abort "{{{ + let tailpos = searchpos(self.buns[1], 'cn', self.range.bottom, a:stimeoutlen) + let flag = tailpos == getpos('.')[1:2] ? 'b' : 'bc' + let stopline = self.range.top + return searchpairpos(self.buns[0], '', self.buns[1], flag, 'self.skip(1)', stopline, a:stimeoutlen) +endfunction +"}}} +function! s:sandwich.searchpair_tail(stimeoutlen) dict abort "{{{ + let stopline = self.range.bottom + return searchpairpos(self.buns[0], '', self.buns[1], '', 'self.skip(0)', stopline, a:stimeoutlen) +endfunction +"}}} +function! s:sandwich.search_head(flag, stimeoutlen) dict abort "{{{ + return self._searchpos(self.buns[0], a:flag, 1, self.range.top, a:stimeoutlen) +endfunction +"}}} +function! s:sandwich.search_tail(flag, stimeoutlen) dict abort "{{{ + return self._searchpos(self.buns[1], a:flag, 0, self.range.bottom, a:stimeoutlen) +endfunction +"}}} +function! s:sandwich._searchpos(pattern, flag, is_head, stopline, stimeoutlen) dict abort "{{{ + let coord = searchpos(a:pattern, a:flag, a:stopline, a:stimeoutlen) + let flag = substitute(a:flag, '\m\Cc', '', 'g') + while coord != s:null_coord && self.skip(a:is_head, coord) + let coord = searchpos(a:pattern, flag, a:stopline, a:stimeoutlen) + endwhile + return coord +endfunction +"}}} +function! s:sandwich.skip(is_head, ...) dict abort "{{{ + " NOTE: for sandwich.searchpairpos()/sandwich.searchpos() functions. + let opt = self.opt + let checkpos = get(a:000, 0, getpos('.')[1:2]) + + if checkpos == s:null_coord + return 1 + endif + + " quoteescape option + let skip_patterns = [] + if opt.of('quoteescape') && "eescape !=# '' + for c in split("eescape, '\zs') + let c = s:lib.escape(c) + let pattern = printf('[^%s]%s\%%(%s%s\)*\zs\%%#', c, c, c, c) + let skip_patterns += [pattern] + endfor + endif + + " skip_regex option + let skip_patterns += opt.of('skip_regex') + let skip_patterns += a:is_head ? opt.of('skip_regex_head') : opt.of('skip_regex_tail') + if skip_patterns != [] + call cursor(checkpos) + for pattern in skip_patterns + let skip = searchpos(pattern, 'cn', checkpos[0]) == checkpos + if skip + return 1 + endif + endfor + endif + + " syntax, match_syntax option + if self.syntax_on + if a:is_head || !opt.of('match_syntax') + let opt_syntax = opt.of('syntax') + if opt_syntax != [] && !s:lib.is_included_syntax(checkpos, opt_syntax) + return 1 + endif + else + if !s:lib.is_matched_syntax(checkpos, self.syntax) + return 1 + endif + endif + endif + + " skip_expr option + for Expr in opt.of('skip_expr') + if s:eval(Expr, a:is_head, s:lib.c2p(checkpos)) + return 1 + endif + endfor + + return 0 +endfunction +"}}} +function! s:sandwich.check_syntax(coord) dict abort "{{{ + if !self.syntax_on || !self.opt.of('match_syntax') + return + endif + + let synitem = s:lib.get_displaysyntax(a:coord) + if synitem !=# '' + let self.syntax = [synitem] + endif +endfunction +"}}} +function! s:sandwich.export_recipe() dict abort "{{{ + " For the cooperation with operator-sandwich + " NOTE: After visual selection by a user-defined textobject, v:operator is set as ':' + " NOTE: 'synchro' option is not valid for visual mode, because there is no guarantee that g:operator#sandwich#object exists. + if self.opt.of('synchro') && exists('g:operator#sandwich#object') + \ && ((self.searchby ==# 'buns' && v:operator ==# 'g@') || (self.searchby ==# 'external' && v:operator =~# '\%(:\|g@\)')) + \ && &operatorfunc =~# '^operator#sandwich#\%(delete\|replace\)' + let recipe = self.recipe + if self.searchby ==# 'buns' + call extend(recipe, {'buns': self.not_escaped}, 'force') + call extend(recipe, {'expr': 0}, 'force') + call extend(recipe, {'listexpr': 0}, 'force') + elseif self.searchby ==# 'external' + let excursus = { + \ 'count' : self.range.count, + \ 'cursor': s:lib.c2p(self.cursor), + \ 'coord' : self.coord, + \ } + call extend(recipe, {'excursus': excursus}, 'force') + endif + call extend(recipe, {'action': ['delete']}, 'force') + else + let recipe = {} + endif + return recipe +endfunction +"}}} + +function! s:evalexpr(dough) abort "{{{ + let buns = [] + let buns += [eval(a:dough[0])] + if buns[0] !=# '' + let buns += [eval(a:dough[1])] + endif + return buns +endfunction +"}}} +function! s:valid_buns(buns) abort "{{{ + return type(a:buns) == s:type_list && len(a:buns) >= 2 && s:check_a_bun(a:buns[0]) && s:check_a_bun(a:buns[1]) +endfunction +"}}} +function! s:check_a_bun(bun) abort "{{{ + let type_bun = type(a:bun) + return (type_bun ==# s:type_str && a:bun !=# '') || type_bun ==# s:type_num +endfunction +"}}} +function! s:eval(expr, ...) abort "{{{ + return type(a:expr) == s:type_fref ? call(a:expr, a:000) : eval(a:expr) +endfunction +"}}} + + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/textobj.vim b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/textobj.vim new file mode 100644 index 00000000..a848943b --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/autoload/textobj/sandwich/textobj.vim @@ -0,0 +1,592 @@ +" textobj object - search & select a sandwiched text + +" variables "{{{ +function! s:SID() abort + return matchstr(expand(''), '\zs\d\+\ze_SID$') +endfunction +let s:SNR = printf("\%s_", s:SID()) +delfunction s:SID + +nnoremap (v) v +nnoremap (V) V +nnoremap (CTRL-v) + +let s:KEY_v = printf('%s(v)', s:SNR) +let s:KEY_V = printf('%s(V)', s:SNR) +let s:KEY_CTRL_v = printf('%s(CTRL-v)', s:SNR) + +" null valiables +let s:null_coord = [0, 0] + +" common functions +let s:lib = textobj#sandwich#lib#import() +"}}} + +function! textobj#sandwich#textobj#new(kind, a_or_i, mode, count, recipes, opt) abort "{{{ + let textobj = deepcopy(s:textobj) + let textobj.kind = a:kind + let textobj.a_or_i = a:a_or_i + let textobj.mode = a:mode + let textobj.count = a:count + let textobj.recipes = a:recipes + let textobj.opt = a:opt + + " NOTE: The cursor position should be recorded in textobj#sandwich#auto()/textobj#sandwich#query(). + " It is impossible to get it in textobj#sandwich#select() if in visual mode. + let textobj.cursor = getpos('.')[1:2] + return textobj +endfunction +"}}} + +" s:textobj "{{{ +let s:textobj = { + \ 'state' : 1, + \ 'kind' : '', + \ 'a_or_i' : 'i', + \ 'mode' : '', + \ 'count' : 0, + \ 'cursor' : copy(s:null_coord), + \ 'view' : {}, + \ 'recipes': { + \ 'arg' : [], + \ 'arg_given' : 0, + \ 'integrated': [], + \ }, + \ 'visual': { + \ 'mode': '', + \ 'head': copy(s:null_coord), + \ 'tail': copy(s:null_coord), + \ }, + \ 'basket': [], + \ 'opt' : {}, + \ 'done' : 0, + \ 'clock' : sandwich#clock#new(), + \ } +"}}} +function! s:textobj.initialize() dict abort "{{{ + let self.done = 0 + let self.count = !self.state && self.count != v:count1 ? v:count1 : self.count + if self.state + let self.basket = map(copy(self.recipes.integrated), 'textobj#sandwich#sandwich#new(v:val, self.opt)') + call filter(self.basket, 'v:val != {}') + else + let self.cursor = getpos('.')[1:2] + endif + if self.mode ==# 'x' + let self.visual.mode = visualmode() ==# "\" ? "\" : 'v' + let self.visual.head = getpos("'<")[1:2] + let self.visual.tail = getpos("'>")[1:2] + else + let self.visual.mode = 'v' + endif + let is_syntax_on = exists('g:syntax_on') || exists('g:syntax_manual') + call map(self.basket, 'v:val.initialize(self.cursor, is_syntax_on)') +endfunction +"}}} +function! s:textobj.list(stimeoutlen) dict abort "{{{ + let clock = self.clock + + " gather candidates + let candidates = [] + call clock.stop() + call clock.start() + while filter(copy(self.basket), 'v:val.range.valid') != [] + for sandwich in self.basket + let candidates += self.search(sandwich, a:stimeoutlen) + endfor + call s:uniq_candidates(candidates, self.a_or_i) + if len(candidates) >= self.count + break + endif + + " time out + if clock.started && a:stimeoutlen > 0 + let elapsed = clock.elapsed() + if elapsed > a:stimeoutlen + echo 'textobj-sandwich: Timed out.' + break + endif + endif + endwhile + call clock.stop() + return candidates +endfunction +"}}} +function! s:textobj.search(sandwich, stimeoutlen) dict abort "{{{ + if a:sandwich.searchby ==# 'buns' + if a:sandwich.opt.of('nesting') + let candidates = self._search_with_nest(a:sandwich, a:stimeoutlen) + else + let candidates = self._search_without_nest(a:sandwich, a:stimeoutlen) + endif + elseif a:sandwich.searchby ==# 'external' + let candidates = self._get_region(a:sandwich, a:stimeoutlen) + else + let candidates = [] + endif + return candidates +endfunction +"}}} +function! s:textobj._search_with_nest(sandwich, stimeoutlen) dict abort "{{{ + let buns = a:sandwich.bake_buns(self.state, self.clock) + let range = a:sandwich.range + let coord = a:sandwich.coord + let opt = a:sandwich.opt + let candidates = [] + + if buns[0] ==# '' || buns[1] ==# '' + let range.valid = 0 + endif + if !range.valid | return candidates | endif + + " check whether the cursor is on the buns[1] or not + call cursor(self.cursor) + let _head = searchpos(buns[1], 'bcW', range.top, a:stimeoutlen) + let _tail = searchpos(buns[1], 'cenW', range.bottom, a:stimeoutlen) + if _head != s:null_coord && _tail != s:null_coord && s:is_in_between(self.cursor, _head, _tail) + call cursor(_head) + else + call cursor(self.cursor) + endif + + while 1 + " search head + let head = a:sandwich.searchpair_head(a:stimeoutlen) + if head == s:null_coord | break | endif + let coord.head = head + call a:sandwich.check_syntax(head) + + " search tail + let tail = a:sandwich.searchpair_tail(a:stimeoutlen) + if tail == s:null_coord | break | endif + let tail = searchpos(buns[1], 'ce', range.bottom, a:stimeoutlen) + if tail == s:null_coord | break | endif + let coord.tail = tail + + " add to candidates + call coord.get_inner(buns, opt.of('skip_break')) + if self.is_valid_candidate(a:sandwich) + let candidate = deepcopy(a:sandwich) + let candidate.visualmode = self.visual.mode + let candidates += [candidate] + endif + + if coord.head == [1, 1] + " finish! + let range.valid = 0 + break + else + call coord.next() + endif + endwhile + call range.next() + return candidates +endfunction +"}}} +function! s:textobj._search_without_nest(sandwich, stimeoutlen) dict abort "{{{ + let buns = a:sandwich.bake_buns(self.state, self.clock) + let range = a:sandwich.range + let coord = a:sandwich.coord + let opt = a:sandwich.opt + let candidates = [] + + if buns[0] ==# '' || buns[1] ==# '' + let range.valid = 0 + endif + if !range.valid | return candidates | endif + + " search nearest head + call cursor(self.cursor) + let head = a:sandwich.search_head('bc', a:stimeoutlen) + if head == s:null_coord + call range.next() + return candidates + endif + call a:sandwich.check_syntax(head) + let _tail = searchpos(buns[0], 'ce', range.bottom, a:stimeoutlen) + + " search nearest tail + call cursor(self.cursor) + let tail = a:sandwich.search_tail('ce', a:stimeoutlen) + if tail == s:null_coord + call range.next() + return candidates + endif + + " If the cursor is right on a bun + if tail == _tail + " check whether it is head or tail + let odd = 1 + call cursor([range.top, 1]) + let pos = searchpos(buns[0], 'c', range.top, a:stimeoutlen) + while pos != head && pos != s:null_coord + let odd = !odd + let pos = searchpos(buns[0], '', range.top, a:stimeoutlen) + endwhile + if pos == s:null_coord | return candidates | endif + + if odd + " pos is head + let head = pos + call a:sandwich.check_syntax(head) + + " search tail + call search(buns[0], 'ce', range.bottom, a:stimeoutlen) + let tail = a:sandwich.search_tail('e', a:stimeoutlen) + if tail == s:null_coord + call range.next() + return candidates + endif + else + " pos is tail + call cursor(pos) + let tail = a:sandwich.search_tail('ce', a:stimeoutlen) + call a:sandwich.check_syntax(tail) + + " search head + call search(buns[1], 'bc', range.top, a:stimeoutlen) + let head = a:sandwich.search_head('b', a:stimeoutlen) + if head == s:null_coord + call range.next() + return candidates + endif + endif + endif + let coord.head = head + let coord.tail = tail + call coord.get_inner(buns, a:sandwich.opt.of('skip_break')) + + if self.is_valid_candidate(a:sandwich) + let candidate = deepcopy(a:sandwich) + let candidate.visualmode = self.visual.mode + let candidates += [candidate] + endif + let range.valid = 0 + return candidates +endfunction +"}}} +function! s:textobj._get_region(sandwich, stimeoutlen) dict abort "{{{ + " NOTE: Because of the restriction of vim, if a operator to get the assigned + " region is employed for 'external' user-defined textobjects, it makes + " impossible to repeat by dot command. Thus, 'external' is checked by + " using visual selection xmap in any case. + let range = a:sandwich.range + let coord = a:sandwich.coord + let opt = a:sandwich.opt + let candidates = [] + + if !range.valid | return candidates | endif + + if opt.of('noremap') + let cmd = 'normal!' + let v = self.visual.mode + else + let cmd = 'normal' + let v = self.visual.mode ==# 'v' ? s:KEY_v : + \ self.visual.mode ==# 'V' ? s:KEY_V : + \ s:KEY_CTRL_v + endif + + if self.mode ==# 'x' + let initpos = [self.visual.head, self.visual.tail] + else + let initpos = [self.cursor, self.cursor] + endif + + let selection = &selection + set selection=inclusive + try + while 1 + let [prev_head, prev_tail] = [coord.head, coord.tail] + let [prev_inner_head, prev_inner_tail] = [coord.inner_head, coord.inner_tail] + " get outer positions + let [head, tail, visualmode_a] = s:get_textobj_region(initpos, cmd, v, range.count, a:sandwich.external[1]) + + " get inner positions + if head != s:null_coord && tail != s:null_coord + let [inner_head, inner_tail, visualmode_i] = s:get_textobj_region(initpos, cmd, v, range.count, a:sandwich.external[0]) + else + let [inner_head, inner_tail, visualmode_i] = [copy(s:null_coord), copy(s:null_coord), 'v'] + endif + + if (self.a_or_i ==# 'i' && s:is_valid_region(inner_head, inner_tail, prev_inner_head, prev_inner_tail, range.count)) + \ || (self.a_or_i ==# 'a' && s:is_valid_region(head, tail, prev_head, prev_tail, range.count)) + if head[0] >= range.top && tail[0] <= range.bottom + let coord.head = head + let coord.tail = tail + let coord.inner_head = inner_head + let coord.inner_tail = inner_tail + + if self.is_valid_candidate(a:sandwich) + let candidate = deepcopy(a:sandwich) + let candidate.visualmode = self.a_or_i ==# 'a' ? visualmode_a : visualmode_i + let candidates += [candidate] + endif + else + call range.next() + break + endif + else + let range.valid = 0 + break + endif + + let range.count += 1 + endwhile + finally + " restore visualmode + execute 'normal! ' . self.visual.mode + execute "normal! \" + call cursor(self.cursor) + " restore marks + call setpos("'<", s:lib.c2p(self.visual.head)) + call setpos("'>", s:lib.c2p(self.visual.tail)) + " restore options + let &selection = selection + endtry + return candidates +endfunction +"}}} +function! s:textobj.is_valid_candidate(sandwich) dict abort "{{{ + let coord = a:sandwich.coord + if !s:is_in_between(self.cursor, coord.head, coord.tail) + return 0 + endif + + if self.a_or_i ==# 'i' + let [head, tail] = [coord.inner_head, coord.inner_tail] + else + let [head, tail] = [coord.head, coord.tail] + endif + if head == s:null_coord || tail == s:null_coord || s:is_ahead(head, tail) + return 0 + endif + + " specific condition in visual mode + if self.mode !=# 'x' + let visual_mode_affair = 1 + else + let visual_mode_affair = s:visual_mode_affair( + \ head, tail, self.a_or_i, self.cursor, self.visual) + endif + if !visual_mode_affair + return 0 + endif + + " specific condition for the option 'matched_syntax' and 'inner_syntax' + let opt_syntax_affair = s:opt_syntax_affair(a:sandwich) + if !opt_syntax_affair + return 0 + endif + + return 1 +endfunction +"}}} +function! s:textobj.elect(candidates) dict abort "{{{ + let elected = {} + if len(a:candidates) >= self.count + " election + let cursor = self.cursor + let map_rule = 'extend(v:val, {"len": s:representative_length(v:val.coord, cursor)})' + call map(a:candidates, map_rule) + call s:lib.sort(a:candidates, function('s:compare_buf_length'), self.count) + let elected = a:candidates[self.count - 1] + endif + return elected +endfunction +"}}} +function! s:textobj.select(sandwich) dict abort "{{{ + if a:sandwich == {} + if self.mode ==# 'x' + normal! gv + endif + let self.done = 1 + return + endif + + let head = self.a_or_i ==# 'i' ? a:sandwich.coord.inner_head : a:sandwich.coord.head + let tail = self.a_or_i ==# 'i' ? a:sandwich.coord.inner_tail : a:sandwich.coord.tail + if self.mode ==# 'x' && self.visual.mode ==# "\" + " trick for the blockwise visual mode + if self.cursor[0] == self.visual.tail[0] + let disp_coord = s:lib.get_displaycoord(head) + let disp_coord[0] = self.visual.head[0] + call s:lib.set_displaycoord(disp_coord) + let head = getpos('.')[1:2] + elseif self.cursor[0] == self.visual.head[0] + let disp_coord = s:lib.get_displaycoord(tail) + let disp_coord[0] = self.visual.tail[0] + call s:lib.set_displaycoord(disp_coord) + let tail = getpos('.')[1:2] + endif + endif + + execute 'normal! ' . a:sandwich.visualmode + call cursor(head) + normal! o + call cursor(tail) + + " counter measure for the 'selection' option being 'exclusive' + if &selection ==# 'exclusive' + normal! l + endif + + call operator#sandwich#synchronize(self.kind, a:sandwich.export_recipe()) + let self.done = 1 +endfunction +"}}} +function! s:textobj.finalize(mark_latestjump) dict abort "{{{ + if self.done && a:mark_latestjump + call setpos("''", s:lib.c2p(self.cursor)) + endif + + " flash echoing + if !self.state + echo '' + endif + + let self.state = 0 +endfunction +"}}} + +function! s:is_valid_region(head, tail, prev_head, prev_tail, count) abort "{{{ + return a:head != s:null_coord && a:tail != s:null_coord && (a:count == 1 || s:is_ahead(a:prev_head, a:head) || s:is_ahead(a:tail, a:prev_tail)) +endfunction +"}}} +function! s:get_textobj_region(initpos, cmd, visualmode, count, key_seq) abort "{{{ + call cursor(a:initpos[0]) + execute printf('silent! %s %s', a:cmd, a:visualmode) + call cursor(a:initpos[1]) + execute printf('silent! %s %d%s', a:cmd, a:count, a:key_seq) + if mode() ==? 'v' || mode() ==# "\" + execute "normal! \" + else + return [copy(s:null_coord), copy(s:null_coord), a:visualmode] + endif + let visualmode = visualmode() + let [head, tail] = [getpos("'<")[1:2], getpos("'>")[1:2]] + if head == a:initpos[0] && tail == a:initpos[1] + let [head, tail] = [copy(s:null_coord), copy(s:null_coord)] + elseif visualmode ==# 'V' + let tail[2] = col([tail[1], '$']) + endif + return [head, tail, visualmode] +endfunction +"}}} +function! s:get_buf_length(start, end) abort "{{{ + if a:start[0] == a:end[0] + let len = a:end[1] - a:start[1] + 1 + else + let len = (line2byte(a:end[0]) + a:end[1]) - (line2byte(a:start[0]) + a:start[1]) + 1 + endif + return len +endfunction +"}}} +function! s:uniq_candidates(candidates, a_or_i) abort "{{{ + let i = 0 + if a:a_or_i ==# 'i' + let filter = 'v:val.coord.inner_head != candidate.coord.inner_head || v:val.coord.inner_tail != candidate.coord.inner_tail' + else + let filter = 'v:val.coord.head == candidate.coord.head || v:val.coord.tail == candidate.coord.tail' + endif + while i+1 < len(a:candidates) + let candidate = a:candidates[i] + call filter(a:candidates[i+1 :], filter) + let i += 1 + endwhile + return a:candidates +endfunction +"}}} +function! s:visual_mode_affair(head, tail, a_or_i, cursor, visual) abort "{{{ + " a:visual.mode ==# 'V' never comes. + if a:visual.mode ==# 'v' + " character-wise + if a:a_or_i ==# 'i' + let visual_mode_affair = s:is_ahead(a:visual.head, a:head) + \ || s:is_ahead(a:tail, a:visual.tail) + else + let visual_mode_affair = (s:is_ahead(a:visual.head, a:head) && s:is_equal_or_ahead(a:tail, a:visual.tail)) + \ || (s:is_equal_or_ahead(a:visual.head, a:head) && s:is_ahead(a:tail, a:visual.tail)) + endif + else + " block-wise + let orig_pos = getpos('.') + let visual_head = s:lib.get_displaycoord(a:visual.head) + let visual_tail = s:lib.get_displaycoord(a:visual.tail) + call s:lib.set_displaycoord([a:cursor[0], visual_head[1]]) + let thr_head = getpos('.') + call s:lib.set_displaycoord([a:cursor[0], visual_tail[1]]) + let thr_tail = getpos('.') + let visual_mode_affair = s:is_ahead(thr_head, a:head) + \ || s:is_ahead(a:tail, thr_tail) + call setpos('.', orig_pos) + endif + return visual_mode_affair +endfunction +"}}} +function! s:opt_syntax_affair(sandwich) abort "{{{ + if !a:sandwich.syntax_on + return 1 + endif + + let coord = a:sandwich.coord + let opt = a:sandwich.opt + if opt.of('match_syntax') == 2 + let opt_syntax_affair = s:lib.is_included_syntax(coord.inner_head, a:sandwich.syntax) + \ && s:lib.is_included_syntax(coord.inner_tail, a:sandwich.syntax) + elseif opt.of('match_syntax') == 3 + " check inner syntax independently + if opt.of('inner_syntax') == [] + let syntax = [s:lib.get_displaysyntax(coord.inner_head)] + let opt_syntax_affair = s:lib.is_included_syntax(coord.inner_tail, syntax) + else + if s:lib.is_included_syntax(coord.inner_head, opt.of('inner_syntax')) + let syntax = [s:lib.get_displaysyntax(coord.inner_head)] + let opt_syntax_affair = s:lib.is_included_syntax(coord.inner_tail, syntax) + else + let opt_syntax_affair = 0 + endif + endif + else + if opt.of('inner_syntax') == [] + let opt_syntax_affair = 1 + else + let opt_syntax_affair = s:lib.is_included_syntax(coord.inner_head, opt.of('inner_syntax')) + \ && s:lib.is_included_syntax(coord.inner_tail, opt.of('inner_syntax')) + endif + endif + return opt_syntax_affair +endfunction +"}}} +function! s:is_in_between(coord, head, tail) abort "{{{ + return (a:coord != s:null_coord) && (a:head != s:null_coord) && (a:tail != s:null_coord) + \ && ((a:coord[0] > a:head[0]) || ((a:coord[0] == a:head[0]) && (a:coord[1] >= a:head[1]))) + \ && ((a:coord[0] < a:tail[0]) || ((a:coord[0] == a:tail[0]) && (a:coord[1] <= a:tail[1]))) +endfunction +"}}} +function! s:is_ahead(coord1, coord2) abort "{{{ + return a:coord1[0] > a:coord2[0] || (a:coord1[0] == a:coord2[0] && a:coord1[1] > a:coord2[1]) +endfunction +"}}} +function! s:is_equal_or_ahead(coord1, coord2) abort "{{{ + return a:coord1[0] > a:coord2[0] || (a:coord1[0] == a:coord2[0] && a:coord1[1] >= a:coord2[1]) +endfunction +"}}} +function! s:representative_length(coord, cursor) abort "{{{ + let inner_head = a:coord.inner_head + let inner_tail = a:coord.inner_tail + if s:is_in_between(a:cursor, inner_head, inner_tail) + return s:get_buf_length(inner_head, inner_tail) + else + return s:get_buf_length(a:coord.head, a:coord.tail) + endif +endfunction "}}} +function! s:compare_buf_length(i1, i2) abort "{{{ + return a:i1.len - a:i2.len +endfunction +"}}} + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: +" vim:set ts=2 sts=2 sw=2: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/doc/compound-sandwich b/config/neovim/store/lazy-plugins/vim-sandwich/doc/compound-sandwich new file mode 100644 index 00000000..3f9c5ab2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/doc/compound-sandwich @@ -0,0 +1,96 @@ +============================================================================= +COMPOUND RECIPES *sandwich-compound-recipes* + +A compound recipe is one that contains multiple simple recipes. A simple +recipe could be a pair of parentheses `()` or braces `{}`, while a compound +recipe could be made up of all types of brackets `(),[],{}`, or both types of +quotes `'',""`. + +sandwich.vim allows for the creation of compound recipes. This tutorial +demonstrates the creation of two compound recipes, brackets and quotes. +Users can adapt the code provided to customize their own compound recipes. + + +Compound text-objects~ + +By default, sandwich.vim can automatically search for a set of surroundings. +This functionality is provided by the `srb` and `sdb` |sandwich-keymappings|. +Under the hood, these mappings call |textobj#sandwich#auto()|. + +This function |textobj#sandwich#auto()| also accepts an optional fourth +argument. If a list of (simple) recipes is given to the fourth argument, this +list is used instead. + +We create a list of recipes for the brackets > + let g:sandwich_bracket_recipes = [ + \ {'buns': ['{', '}'], 'nesting': 1, 'skip_break': 1}, + \ {'buns': ['[', ']'], 'nesting': 1}, + \ {'buns': ['(', ')'], 'nesting': 1}, + \ ] +< +and another for the quotes > + let g:sandwich_quote_recipes = [ + \ {'buns': ['"', '"'], 'quoteescape': 1, 'expand_range': 0, 'nesting': 0, 'linewise': 0}, + \ {'buns': ["'", "'"], 'quoteescape': 1, 'expand_range': 0, 'nesting': 0, 'linewise': 0}, + \ ] +< + +Then, we pass these lists of (simple) recipes into |textobj#sandwich#auto()| +to create our text-objects. It is also convenient to access these +text-objects with key mappings. In this tutorial, we assign the mappings `ij, +aj` and `io, ao` to the brackets and quotes text-objects respectively > + + onoremap ij textobj#sandwich#auto('x', 'i', {}, g:sandwich_bracket_recipes) + onoremap aj textobj#sandwich#auto('x', 'a', {}, g:sandwich_bracket_recipes) + xnoremap ij textobj#sandwich#auto('x', 'i', {}, g:sandwich_bracket_recipes) + xnoremap aj textobj#sandwich#auto('x', 'a', {}, g:sandwich_bracket_recipes) + + onoremap io textobj#sandwich#auto('x', 'i', {}, g:sandwich_quote_recipes) + onoremap ao textobj#sandwich#auto('x', 'a', {}, g:sandwich_quote_recipes) + xnoremap io textobj#sandwich#auto('x', 'i', {}, g:sandwich_quote_recipes) + xnoremap ao textobj#sandwich#auto('x', 'a', {}, g:sandwich_quote_recipes) +< + +With these, one can visually select the nearest pair of brackets with `vaj`. +In a similar manner, `dio` deletes the text between the two nearest quotes. + +Compound recipes~ + +The next step is to add these text objects as compound recipes, and use them +with sandwich operators such as |(operator-sandwich-delete)| and +|(operator-sandwich-replace)| (default: `sd` and `sr`). + +We define these compound recipes using external requisites (see +|textobj-sandwich-configuration| or |operator-sandwich-configuration|). The +text objects defined above and are passed into the `'external'` item, as seen +below > + + let g:sandwich_compound_recipes = [ + \ { + \ 'external': ['ij', 'aj'], + \ 'kind' : ['delete', 'replace', 'query'], + \ 'noremap' : 0, + \ 'input' : ['j'], + \ }, + \ { + \ 'external': ['io', 'ao'], + \ 'kind' : ['delete', 'replace', 'query'], + \ 'noremap' : 0, + \ 'input' : ['o'], + \ }, + \ ] +< +These recipes require an `'input'`, which we specify to be `'j'` and `'o'` +for brackets and quotes respectively. + +Finally, we add these compound recipes to |g:sandwich#recipes|, +that is, the list of recipes used by sandwich.vim > + + call extend(g:sandwich#recipes, g:sandwich_compound_recipes) +< + +With these, one can delete the nearest pair of quotes simply with `sdo`. +Similarly, one can replace the nearest pair of brackets with `srj{char}`. + +============================================================================== +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/doc/operator-sandwich.jax b/config/neovim/store/lazy-plugins/vim-sandwich/doc/operator-sandwich.jax new file mode 100644 index 00000000..72c017d0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/doc/operator-sandwich.jax @@ -0,0 +1,1854 @@ +*operator-sandwich.jax* 『挟まれた』テキストを編集する。 + Last change:28-Nov-2021. + +書いた人 : machakann +ライセンス : NYSL license + 日本語 + English (Unofficial) + +必須要件: Vim 7.4 かそれ以降のバージョン + |+reltime| 機能 (任意) + |+float| 機能 (任意) + visualrepeat.vim (vimscript #3848) プラグイン (任意) + +============================================================================== +CONTENTS *operator-sandwich-contents* + +TL;DR +INTRODUCTION |operator-sandwich-introduction| +KEYMAPPINGS |operator-sandwich-keymappings| +CONFIGURATION |operator-sandwich-configuration| + REQUISITE + buns + external + FILTERS + filetype + kind + motionwise + mode + action + expr_filter + LOCAL OPTIONS + cursor + highlight + hi_duration + skip_space + noremap + linewise + command + query_once + expr + autoindent + indentkeys + indentkeys+ + indentkeys- + regex + skip_char + GLOBAL OPTIONS + timeout |g:operator#sandwich#timeout| + timeoutlen |g:operator#sandwich#timeoutlen| + highlight_duration |g:operator#sandwich#highlight_duration| +HIGHLIGHT GROUP |operator-sandwich-highlight-group| +AUTOCOMMANDS |operator-sandwich-autocommands| +API |operator-sandwich-api| +MISCELLANEOUS |operator-sandwich-miscellaneous| + 囲みを外すオペレータのハイライトはいらない + ハイライトは全部いらない + matchit.vim のハイライトがかぶって邪魔 + 文字列を囲むオペレータの文字指向の場合だけ空白をスキップしないのはなぜ? + なぜ囲みを外す/置換するオペレータのキーマッピングはデフォルトで提供されない? + ドットコマンドでもカーソル位置を保存・復元したい + Query1st 系列のキーマッピングは何のためにある? + 置換オペレータにおけるレシピに指定されたオプションの扱いについて + visualrepeat.vim プラグイン対応について + +============================================================================== +TL;DR + こちらをご覧ください。 |sandwich-quick-start|. + + + +============================================================================== +INTRODUCTION *operator-sandwich-introduction* + +*operator-sandwich* は (foo) や "bar" などの「挟まれた」テキストを編集するため +のオペレータプラグインです。 +|(operator-sandwich-add)|, |(operator-sandwich-delete)|, +|(operator-sandwich-replace)| の三つの独立したオペレータからなります。 + +|(operator-sandwich-add)| 文字列を括弧などで囲むオペレータです。デフォル +トでは sa にマップされています。例えば、 foo という文字列上にカーソルがあると +します。 +> + foo +< +saiw" とキーを押下すると次のようになります。 +> + "foo" +< +あるいは saiw( と押すと次のようになります。 +> + (foo) +< +( と ) のペアに関してはそれが一つのセットとして事前に登録されており、そのため +に正しく囲むことができます。登録されていないものについては、 " と同じように同 +じ文字が前後に挿入されます。登録されたセットをレシピ "recipe" と呼びます。詳し +くは |operator-sandwich-configuration| をご覧ください。 + +|(operator-sandwich-delete)| は囲みを外すオペレータです。デフォルトでは +ノーマルモードに単独のマッピングが準備されませんので、試したい場合には適当なキ +ーに割り当ててください。このオペレータは指定された領域の両端が同じ文字か、登録 +されたペアにマッチする場合にこれらを削除します。例えば、オペレータが sd にマッ +プされているとして、 (foo) にカーソルがあっている場合を考えます。 +> + (foo) +< +sda( とキー入力をすると両端の括弧が外されます。なぜならテキストオブジェクト +|a(| は括弧を両端に含む領域を指定するためです。 +> + foo +< +"foo" というテキストについてはキー入力 sd2i" (|iquote|) が同じように働くでしょ +う。実際には先に述べたように |(operator-sandwich-delete)| は sd にマップ +されていません。なぜなら |textobj-sandwich| と組み合わせて使うのがほぼ常に便利 +であるからです。このため、 sd はあらかじめ |textobj-sandwich| との複合マッピン +グとして用意されています。詳しくは |sandwich-keymappings| をご覧ください。 + +|(operator-sandwich-replace)| は囲みを置き換えるオペレータです。デフォル +トではノーマルモードに単独のマッピングが準備されませんので、試したい場合には適 +当なキーに割り当ててください。このオペレータは指定された領域の両端が同じ文字 +か、登録されたペアにマッチする場合にこれらを置換します。例えばオペレータが sr +にマップされているとして、 (foo) にカーソルがあっている場合を考えます。 +> + (foo) +< +sra(" とキー入力をすると両端の括弧がダブルクオーテーションに置換されます。 +> + "foo" +< +入力の順番は削除されるもの、ここでは ( が先で、そのあとに挿入されるもの、" を +入力します。削除されるもの、挿入されるものはそれぞれ +|(operator-sandwich-delete)| 及び |(operator-sandwich-add)| と同じ +ように選ばれます。実際には sd と同じ理由で sr には複合マッピングがマップされて +います。詳しくは |sandwich-keymappings| をご覧ください。 + +------------------------------------------------------------------------------ +これらすべてのオペレータは [count] を受け付けますが、その解釈方法は少し変って +います。通常のオペレータとテキストオブジェクトであれば、ありうるキー入力は次の +ようなものでしょう。 + + [count]{operator}[count]{textobject} + +例えば 3|d|iw| というキー入力は二単語と間のスペースを削除します。カウントを入 +力するタイミングは二回ありますが、これらが区別されることはありません。つまり +3diw と d3iw は原則的に同じです。もし両方のタイミングでカウントを与えれば、そ +の積がカウントとなります。つまり、 3d2iw あるいは 2d3iw は 6diw および d6iw と +全て同一のコマンドです。 + +これに対して |operator-sandwich| の提供するオペレータはそのタイミングによって +与えられるカウントを区別します。これらのオペレータは最初のタイミングで与えられ +るカウントのみをオペレータで使用し、後のタイミングで与えられるカウントのみをテ +キストオブジェクトに渡します。例えば、 2sa3iw(( というキー入力は二単語と間のス +ペースを二回括弧で囲みます。 +> + foo bar ---> ((foo bar)) +< +|.| コマンドで動作を繰り返した場合は最後に使ったカウントが使用されます。もしカ +ウントが |.| コマンドに渡された場合にはテキストオブジェクトに渡されたカウント +のみが更新されます。 + +------------------------------------------------------------------------------ +これらのオペレータは矩形ビジュアルモード |blockwise-visual| でも動作します。例 +えば次の三行三桁の領域を選択しているとします。 +> + foo + bar + baz +< +sa( と入力します +> + (foo) + (bar) + (baz) +< +次の四行四桁の短い行が含まれている場合を考えます。 +> + a + bb + ccc + dddd +< +sa( と入力します。 +> + (a) + (bb) + (ccc) + (dddd) +< +この場合、すべての行に対し行末までを範囲として処理します。選択最終行が最も長い +行でない場合は |$| コマンドで全ての行末まで選択を拡張した状態にするとよいでし +ょう。 +> + aaaa + bbb + cc + d +< +最初の行にカーソルを置き、 3j$sa( とキー入力します。 +> + (aaaa) + (bbb) + (cc) + (d) +< + + + +============================================================================== +KEYMAPPINGS *operator-sandwich-keymappings* + +このプラグインは下記のキーマッピングを提供します。 +|(operator-sandwich-add)|, |(operator-sandwich-delete)| 及び +|(operator-sandwich-replace)| は基本的にノーマルモード、ヴィジュアルモー +ド、オペレータ待機モードで動作します。 |(operator-sandwich-add)| はノー +マルモードとビジュアルモードの両方で sa にマップされています。一方、 +|(operator-sandwich-delete)| と |(operator-sandwich-replace)| はビ +ジュアルモードでのみ sd および sr に単独のマッピングとして定義されます。ノーマ +ルモードでは便利のため |textobj-sandwich| との複合マッピングとして定義されます +。詳しくは |sandwich-keymappings| をご覧ください。 + +機能 キーマッピング デフォルト +-------------------------------------------------------------------------- +囲む (operator-sandwich-add) sa + +囲みを外す (operator-sandwich-delete) sd (ビジュアルモード) + +囲みを置換 (operator-sandwich-replace) sr (ビジュアルモード) +-------------------------------------------------------------------------- + +もし上記のデフォルト設定が必要なければ、あなたの vimrc で +g:operator_sandwich_no_default_key_mappings を定義してください。 +> + let g:operator_sandwich_no_default_key_mappings = 1 +< +これでデフォルト設定は適用されません。お好みのキーに設定しなおしてください。 +> + map ys (operator-sandwich-add) +< + +NOTE: 誤操作を防ぐため以下の設定を vimrc に追加することを強く推奨します。 +> + nmap s + xmap s +< + |s| コマンドは |c|l| コマンドによって代替できます。 + +------------------------------------------------------------------------------ +キーマッピング~ +(operator-sandwich-add) *(operator-sandwich-add)* + テキストを括弧などで囲むためのオペレータコマンドです。[count] が与えら + れた場合、その数だけ囲みを追加します。 + +(operator-sandwich-delete) *(operator-sandwich-delete)* + テキストの囲みを外すためのオペレータコマンドです。 [count] が与えられ + た場合、その数だけ連続する囲みを外します。 + +(operator-sandwich-replace) *(operator-sandwich-replace)* + テキストの囲みを置き換えるためのオペレータコマンドです。 [count] が与 + えられた場合、その数だけ連続する囲みを置換します。 + + + +query-1st 系列キーマッピング~ +*(operator-sandwich-add-query1st)* + テキストを括弧などで囲むためのオペレータコマンドです。[count] が与えら + れた場合、その数だけ囲みを追加します。|(operator-sandwich-add)| + とは違い、 {motion/textobject} の入力の前に挿入する文字の決定を行いま + す。 +> + nmap sa (operator-sandwich-add-query1st) + + " Press sa(iw + " foo -> (foo) +< + NOTE: ノーマルモードへのマップでのみ意味を持ちます。|:xmap| でビジュア + ルモードにもマップできますが、ビジュアルモードでは入力順序が通常 + の |(operator-sandwich-add)| と同じです。また、別の理由か + ら使用が推奨されません。 |operator-sandwich-miscellaneous| を + ご覧ください。 + +*(operator-sandwich-replace-query1st)* + テキストの囲みを置き換えるためのオペレータコマンドです。 [count] が与 + えられた場合、その数だけ連続する囲みを置換します。 + |(operator-sandwich-replace)|とは違い、 {motion/textobject} の入 + 力の前に挿入する文字の決定を行います。 +> + nmap sa (operator-sandwich-replace-query1st) + + " Press sr(a[ + " [foo] -> (foo) +< + NOTE: ノーマルモードへのマップでのみ意味を持ちます。|:xmap| でビジュア + ルモードにもマップできますが、ビジュアルモードでは入力順序が通常 + の |(operator-sandwich-replace)| と同じです。また、別の + 理由から使用が推奨されません。 |operator-sandwich-miscellaneous| + をご覧ください。 + + + +補助キーマッピング~ + +カウントの取り扱いに関して、補助的な働きをするキーマッピングが三つ用意されてい +ます。短縮キーマッピングなどを独自に定義する際に役に立つでしょう。オペレータの +独特なカウントの扱いについては |operator-sandwich-introduction| をご覧ください +。実用例は |sandwich-keymappings| をご覧ください。 + + *(operator-sandwich-synchro-count)* +(operator-sandwich-synchro-count) + オペレータに与えられた [count] と同じカウントを続く + {motion}/{textobject} に与えるためのキーマッピングです。 +> + nmap sd( + \ (operator-sandwich-delete)(operator-sandwich-synchro-count)a( +< + このキーマッピングでは 2sd( は 2(operator-sandwich-delete)2a( に + 等しくなります。 + + + + *(operator-sandwich-release-count)* +(operator-sandwich-release-count) + 与えられたカウントをオペレータで使わずに続く {motion}/{textobject} に + 与えるためのキーマッピングです。 +> + nmap sd( + \ (operator-sandwich-delete)(operator-sandwich-release-count)a( +< + このキーマッピングでは 2sd( は (operator-sandwich-delete)2a( に + 等しくなります。 + + + + *(operator-sandwich-squash-count)* +(operator-sandwich-squash-count) + 与えられたカウントをすべて無視するためのキーマッピングです。 +> + nmap sd( + \ (operator-sandwich-delete)(operator-sandwich-squash-count)a( +< + このキーマッピングでは 2sd( は (operator-sandwich-delete)a( に + 等しくなります。 + + + +ドットコマンドを便利にする補助的なキーマッピングが二種あります。これらは local +option の "cursor" に関係しており、このオプションの選択肢 "keep" を |.| コマン +ドリピート時にも有効にするために使われます。カーソル位置を編集後にも保つための +このオプションはデフォルトでは |.| コマンドリピート時には有効にならないのです +が、以下のどちらかのキーマッピングを使用することで有効にすることができます。他 +のプラグインとキーマッピングが衝突する恐れがあるので、環境によって適切なほうを +使用してください。 |operator-sandwich-configuration| の local option 中 cursor +オプションの項もご覧ください。 +vim-event-DotCommandPre +を使用している場合は、以下のキーマッピングを使用する必要はありません。 + + *(operator-sandwich-predot)* +(operator-sandwich-predot) + このキーマッピングは local option "cursor" の "keep" を |.| コマンドで + も有効にするために使われます。このキーマッピング自体は |.| キーを送ら + ないので、 |.| キーシークエンスを送るマッピングに先駆けるようにマップ + してください。 |.| キーにマップするような他のプラグイン、例えば + `(ExampleDot)` というマッピングを |.| キーに定義するプラグインを + ご使用の場合は次のように使います。 +> + nmap . (operator-sandwich-predot)(ExampleDot) +< + repeat.vim (vimscript #2136) をご使用の場合は `(RepeatDot)` の + 定義が遅延されているので、そのキーマッピングの存在を保証するために次の + 行を vimrc へ追加します。 +> + runtime autoload/repeat.vim + nmap . (operator-sandwich-predot)(RepeatDot) +< + + *(operator-sandwich-dot)* +(operator-sandwich-dot) + このキーマッピングは local option "cursor" の "keep" を |.| コマンドで + も有効にするために使われます。このキーマッピング自体が |.| キーを送る + ので、 |.| キーにマップするようなプラグインをお使いでなければこちらを + 使用してください。次の行を vimrc に追加します。 +> + nmap . (operator-sandwich-dot) +< + + + +キーマッピング関数~ + +ユーザーは新しいキーマッピングを定義するのに以下の関数を使うこともできます。 + + *operator#sandwich#keymap()* +operator#sandwich#keymap(kind, mode[, options[, recipes]]) + この関数はオペレータのキーマッピングを定義するのにつかわれます。 +> + nnoremap sa :call operator#sandwich#keymap('add', 'n') + xnoremap sa :call operator#sandwich#keymap('add', 'x') +< + 省略可能な第三引数に空でない辞書変数が与えられた場合、辞書に含まれる + local option はそのキーマッピングで使われるオプションのデフォルト値 + を上書きします。ただし、 motionwise に依って設定することはここではでき + ません。 +> + " example 1 + nnoremap sa + \ :call operator#sandwich#keymap( + \ 'add', + \ 'n', + \ {'highlight': 0} + \ ) + + " example 2 + let g:sandwich_alt_options = {'highlight': 0} + nnoremap sa + \ :call operator#sandwich#keymap( + \ 'add', + \ 'n', + \ g:sandwich_alt_options + \ ) +< + + 省略可能な第四引数にリストが与えられた場合、そのキーマッピングは + |g:sandwich#recipes| (|g:sandwich#default_recipes|) や + |g:operator#sandwich#recipes| (|g:operator#sandwich#default_recipes|) + のかわりに引数に与えられたリストを参照します。 +> + " example 1 + nnoremap sa + \ :call operator#sandwich#keymap( + \ 'add', + \ 'n', + \ {}, + \ [{'buns': ['(', ')']}] + \ ) + + " example 2 + let g:sandwich_alt_recipes = [{'buns': ['(', ')']}] + nnoremap sa + \ :call operator#sandwich#keymap( + \ 'add', + \ 'n', + \ {}, + \ g:sandwich_alt_recipes + \ ) +< + + + + *operator#sandwich#query1st()* +operator#sandwich#query1st(kind, mode[, options[, recipes]]) + この関数は |operator#sandwich#keymap()| と同様の関数ですが、 + |(operator-sandwich-add-query1st)| や + |(operator-sandwich-replace-query1st)| などの query1st 系列の + キーマッピングを定義するのに使われます。 + + + +============================================================================== +CONFIGURATION *operator-sandwich-configuration* + +囲み文字のセットと付加情報をまとめたものをレシピ "recipe" と呼びます。一つ一つ +のレシピは |Dictionary| であり、これを集めた |list| がオペレータの動作を決めま +す。 |g:sandwich#default_recipes| はそのリストのうちの一つで、 +|operator-sandwich| と |textobj-sandwich| の両方から参照されます。これは多くの +場合、共有された方が便利であるためです。もしユーザーが |g:sandwich#recipes| を +定義した場合はかわりにそちらが使用されます。デフォルトの +|g:sandwich#default_recipes| は |:echo| コマンドで確認できます。 +> + :echo g:sandwich#default_recipes +< +この変数はロックされていますが、 |g:sandwich#recipes| をユーザーが宣言する際に +必要ならコピーすることができます。 +> + :let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +< +デフォルトでは (), [], {}, <> のセットが登録されています。より詳しくは +|g:sandwich#default_recipes| をご覧ください。 + +|g:operator#sandwich#default_recipes| はまた別のリストで、こちらは +|operator-sandwich| のみからしか参照されません。ユーザーが +|g:operator#sandwich#recipes| を定義した際にはそちらがかわりに使用されます。 +|g:operator#sandwich#default_recipes| は |g:sandwich#default_recipes| と同様に +ロックされています。 + + + +g:operator#sandwich#recipes *g:operator#sandwich#recipes* + オペレータのみから参照されるレシピのリストです。このリストが存在しない + 場合は |g:operator#sandwich#default_recipes| が使用されます。 + *b:operator_sandwich_recipes* + |b:operator_sandwich_recipes| が存在する場合は、 + |g:operator#sandwich#recipes| のかわりにそちらが使われます。これはバッ + ファについてローカルな値なので、ファイルタイプ固有な設定が増えすぎた時 + に使うと便利かもしれません。 + + + +g:operator#sandwich#default_recipes *g:operator#sandwich#default_recipes* + デフォルトで用意されるレシピのリストの一つです。オペレータのみから参照 + されます。ユーザーが |g:operator#sandwich#recipes| を定義した場合には + そちらがかわりにつかわれます。その際必要ならコピーすることができます。 +> + :let g:operator#sandwich#recipes + \ = deepcopy(g:operator#sandwich#default_recipes) +< + + + +NOTE: もしレシピが競合する場合は |g:operator#sandwich#default_recipes| か + |g:operator#sandwich#recipes| が |g:sandwich#default_recipes| か + |g:sandwich#recipes| よりも優先されます。また同じリスト内であれば、リス + ト後方のレシピほど優先されます。 + +------------------------------------------------------------------------------ +一つ一つのレシピは |Dictionary| 変数であり、 requisite, input, filter, local +optionの四種の情報を持ちえます。 requisite はすべてのレシピに必須の情報で囲み +文字を特定するためのものです。input はレシピを指定するためのユーザー入力を決め +ます。 filter は使用する状況によってレシピを選別するためにあります。 local +option はレシピによってオペレータの働きを細かく制御するために使われます。さら +にこれら以外にオペレータの基本的な動作を制御するための global option もいくつ +か存在します。 + +設定を変更する場合はまず必要なリストを宣言しましょう。 +> + let g:sandwich#recipes = [] +< +あるいはデフォルト設定を引き継ぐためにコピーしてもよいでしょう。 +> + let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +< + +Requisite~ + +requisite には buns と external の二種類あり、すべてのレシピはどちらかを持たな +ければなりません。 + +buns + テキストを囲む文字列のセットを定義するためのキーです。値に文字列を二つ + 要素に持つ |list| を持ちます。囲むまたは置換するオペレータにおける挿入 + される文字列、囲みを外すまたは置換するオペレータにおける削除される文字 + 列の両方に使われます。もし "regex" オプションが真なら正規表現として扱 + われます。またレシピが "input" キーを持たない場合、レシピを指定するた + めのキー入力にも使われます。 +> + let g:sandwich#recipes += [ + \ {'buns': ['(', ')']} + \ ] + + " Press saiw( or saiw) + " foo ---> (foo) + + let g:sandwich#recipes += [ + \ {'buns': ['ab', 'cd']} + \ ] + + " Press saiwab or saiwcd + " foo ---> abfoocd + + let g:sandwich#recipes += [ + \ {'buns': ['ab', 'cd'], 'input': ['a']} + \ ] + + " Press saiwa + " foo ---> abfoocd +< + + + +external + こちらは補助的に使われる requisite です。文字列二要素の |list| でそれ + ぞれの要素は外部のテキストオブジェクトになります。囲みを外すまたは置換 + するオペレータにおける削除される文字列を検索する場合にのみ使われ、挿入 + 文字列としては使われません。つまり、言い換えれば囲むオペレータでは使わ + れません。削除文字列は二つのテキストオブジェクトの選択する範囲の差によ + って決められ、リストの前方の要素が狭い範囲を選択するテキストオブジェク + ト、後方の要素が広い範囲を選択するテキストオブジェクトとみなされます。 + "buns" とは違い、多くの local option が無効になります。 +> + let g:sandwich#recipes += [ + \ {'external': ['it', 'at']} + \ ] + + " "it" selects the text inside tags, "at" selects including tags. + " Therefore, the deletions are the both tags. + " <---it---> + " title here + " <----------at-----------> + + " Press (operator-sandwich-delete)at + " title here ---> title here +< + "noremap" オプションは有効です。内部的にはビジュアルモード選択を利用し + て範囲を確かめているのでビジュアルモードへのマッピング (xmap, + xnoremap, vmap, vnoremap)が展開の対象になります。 +> + let g:sandwich#recipes += [ + \ {'external': ['i[', 'a['], 'noremap': 0} + \ ] + + " Press (operator-sandwich-delete)a[ + " [foo] ---> foo + + xnoremap i[ i{ + xnoremap a[ a{ + + " Press (operator-sandwich-delete)a{ + " {foo} ---> foo +< + "noremap" オプションと組み合わせることでユーザー定義テキストオブジェク + トも同様にレシピ化できます。 +> + " "noremap" option should be false. + let g:sandwich#recipes += [ + \ { + \ 'external': ["\(textobj-sandwich-auto-i)", + \ "\(textobj-sandwich-auto-a)"], + \ 'noremap': 0, + \ } + \ ] +< + NOTE: 使用するテキストオブジェクトはビジュアルモードで正しく動作するこ + とを期待されます。 + NOTE: すべてのテキストオブジェクトが動作するとは限りません。 + + + +Input~ + +input + 文字列を囲むまたは置換する際、オペレータは囲みを決定するためにユーザー + に入力を促します。この時の入力に使われるのがこのキーの値になります。値 + はリストで複数の文字列を含むことができます。このキーを持たないレシピで + は "buns" がかわりにつかわれます。以下の例では、 "input" キーを持たな + いので "buns" がそのまま使われます。 +> + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""']} + \ ] + " Press saiw""" + " foo ---> """foo""" +< + "input" キーがあればそちらがかわりにつかわれます。 +> + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'input': ['"']} + \ ] + " Press saiw" + " foo ---> """foo""" +< + + + +Filter~ + +filetype + ファイルタイプによってレシピを選別するフィルターです。値はリストでファ + イルタイプ名の文字列が要素になります。レシピが "filetype" キーを持たな + いか、要素として "all" を持つ場合はすべてのファイルタイプで有効になり + ます。 +> + " The following recipes are valid on any filetype. + let g:sandwich#recipes += [ + \ {'buns': ['(', ')']} + \ {'buns': ['[', ']'], 'filetype': ['all']} + \ ] + + " The textobj "it" and "at" is not versatile and might be heavy on + " large files, thus it would be better to activate only on specific + " filetypes. + let g:sandwich#recipes += [ + \ {'external': ['it', 'at'], 'filetype': ['html']} + \ ] +< + + + +kind + オペレータの種類によってレシピを選別するフィルターです。値はリストで要 + 素にオペレータの種類を文字列として持ちます。使用可能な種類は "add", + "delete", "replace", "operator", "all" です。 "operator" は "add", + "delete", "replace" のすべてを指定した場合と同じです。 "operator" と + "all" の違いは、 "all" は |textobj-sandwich| の種類も含むことがある点 + にあります。あわせて |textobj-sandwich-configuration| もご覧ください。 + レシピが "kind" キーを持たなければすべての種類で有効です。 +> + " The following recipe is valid only with the add operator. + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'kind': ['add']} + \ ] +< + + + +motionwise + 指定された範囲の性質によってレシピを選別するフィルターです。値はリスト + で使用可能な要素は "char", "line", "block", "all" です。範囲の種類につ + いては|characterwise|, |linewise|, |characterwise-visual|, + |linewise-visual|, |blockwise-visual| の項をご覧ください。レシピが + "motionwise" キーを持たなければすべての範囲指定に関して有効です。 +> + " The following recipe is valid only in linewise motion or linewise + " visual. + let g:sandwich#recipes += [ + \ {'buns': ['```', '```'], 'motionwise': ['line']} + \ ] +< + + + +mode + 使用されたモードによってレシピを選別するフィルターです。値はモードを表 + す文字のリストです。使用可能な文字は "n" と "x" のみです。"n" はノーマ + ルモードを、 "x" はビジュアルモードを表します。レシピが "mode" キーを + 持たなければすべてのモードで有効です。 +> + " These recipes are switch behaviors on modes with the same input. + let g:sandwich#recipes += [ + \ {'buns': ['"', '"'], 'mode': ['n']} + \ {'buns': ['"""', '"""'], 'mode': ['x'], 'input': ['"']} + \ ] +< + + + +action + 編集方法の種類によってレシピを選別するフィルターです。すべてのオペレー + タの動作は、追加 "add" と削除 "delete" の二つの編集方法に分けることが + できます。文字列を囲むオペレータは "add" のみ、囲みを外すオペレータは + "delete" のみ、置換するオペレータは "delete" のち "add" をすることによ + り置換しています。このフィルターはそれぞれの編集において使われる機会を + 制限します。使用可能な名前は "add", "delete", "all" です。レシピが + "action" キーを持たなければすべての編集操作において有効になります。 + "add" のみが指定されている場合、そのレシピは |textobj-sandwich| で無効 + になりますが、"delete" 及び "all" が含まれていれば有効になります。これ + は通常、削除においてそれらのテキストオブジェクトが内部的に使われるため + です。 + つまり、"add" は追加と置換のオペレータで有効、"delete" は削除と置換の + オペレータに加え、テキストオブジェクトで有効となります。 +> + " The recipe is valid only for add actions + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'action': ['add'], 'input': ['"']} + \ ] + + " The recipe is valid for delete actions and textobjects + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'action': ['delete'], 'input': ['"']} + \ ] +< + + + +expr_filter + ユーザーがフィルターを自作して使うことができます。リストの要素は式 + |expression| として評価され、評価値が真 (1) の場合にはそのレシピは有効 + となり、評価値が偽 (0) の場合にはレシピは無効となります。 +> + " A filter should be defined in somewhere, for example in your vimrc. + function! FilterValid() + return 1 + endfunction + + function! FilterInvalid() + return 0 + endfunction + + " This recipe is valid + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'expr_filter': ['FilterValid()']} + \ ] + + " This recipe is invalid + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'expr_filter': ['FilterInvalid()']} + \ ] +< + + + +Local option~ + +local option はレシピごとに細かく挙動を調整するために設定されます。もし指定の +ない場合はデフォルトで設定されている値が使われます。この値はオペレータの種類と +範囲指定の性質に依って違う値をもち、 |g:operator#sandwich#options| に格納され +ています。 + +例えば、文字列を囲むオペレータの文字単位範囲に対するハイライトをOn/Offするオプ +ション "highlight" のデフォルト値を変更したい場合は次のようにします。 +> + let g:operator#sandwich#options.add.char.highlight = 0 +< +あるいは関数インターフェースも用意されています。 |operator#sandwich#set()| +> + call operator#sandwich#set('add', 'char', 'highlight', 0) +< + + + +g:operator#sandwich#options *g:operator#sandwich#options* + local option のデフォルト値を格納した |Dictionary| 変数です。 +> + let operator#sandwich#options[kind][motionwise][option] = {value} + + " For example + let g:operator#sandwich#options['add']['char']['highlight'] = 0 + " or + let g:operator#sandwich#options.add.char.highlight = 0 +< + *b:operator_sandwich_options* + |b:operator_sandwich_options| が存在する場合はそちらが優先されます。 + バッファについてローカルなオプション設定を使うのに便利でしょう。 + + 使用可能なキー名を下に列挙します。 + * kind + - add + - delete + - replace + * motionwise + - char + - line + - block + * option + - cursor (for all kinds) + - highlight (for all kinds) + - skip_space (for all kinds) + - noremap (for all kinds) + - linewise (for all kinds) + - command (for all kinds) + - query_once (for add and replace) + - expr (for add and replace) + - listexpr (for add and replace) + - autoindent (for add and replace) + - indentkeys (for add and replace) + - indentkeys- (for add and replace) + - indentkeys+ (for add and replace) + - regex (for delete and replace) + - skip_char (for delete and replace) + + + + *operator#sandwich#set()* +operator#sandwich#set(kind, motionwise, option, value) + local option のデフォルト値を安全かつ簡単に変更するための関数です。引 + 数の組み合わせが不正な場合、関数はメッセージを表示します。使用可能な引 + 数は |g:operator#sandwich#options| の項に列挙されています。また、これ + に加えて、この関数では "all" を kind 及び motionwise に指定することが + できます。 + + + + *operator#sandwich#setlocal()* +operator#sandwich#setlocal(kind, motionwise, option, value) + local option のバッファについてローカルなデフォルト値を安全かつ簡単に + 変更するための関数です。引数の組み合わせが不正な場合、関数はメッセージ + を表示します。使用可能な引数は |g:operator#sandwich#options| の項に列 + 挙されています。また、これに加えて、この関数では "all" を kind 及び + motionwise に指定することができます。 + + + +operator#sandwich#set_default() *operator#sandwich#set_default()* + local option のデフォルト値を初期化します。 + + + +cursor + オペレータが編集後のカーソル位置を指定するオプションです。 + "innner_head", "keep", "inner_tail", "head", "tail" が使用可能です。 + inner_head: 囲みの内側の文字列の先頭 + keep : 処理開始時のカーソル位置をできる限り復元する + inner_tail: 囲みの内側の文字列の末尾 + head : 前方の囲みの先頭 + tail : 後方の囲みの末尾 + headend : 前方の囲みの末尾 + tailstart : 後方の囲みの先頭 + default : inner_head とほぼ同じですがカーソルが行頭に来るときは最 + 初の非空白文字まで送ります +> + # : cursor + foo ---> (foo) + + # : inner_head + (foo) + # : inner_tail + + # : keep + (foo) + + # : head + (foo) + # : tail +< + NOTE: 残念ながら "keep" オプションは |.| コマンドでの繰り返しの際に + デフォルトではカーソル位置を復元できません。自動的に + "inner_head" へフォールバックします。必要であれば補助キーマッピ + ング |(operator-sandwich-predot)| か + |(operator-sandwich-dot)| を |.| キーにマッピングすること + でこの機能を有効にすることができます。 + + デフォルト値 + * add + - char : "default" + - line : "default" + - block: "default" + * delete + - char : "default" + - line : "default" + - block: "default" + * replace + - char : "default" + - line : "default" + - block: "default" + + + +highlight + 処理対象のハイライト機能のオン/オフを切り替えます。値が真なら編集位置 + をハイライトします。値が 1 の場合は簡素な、値が 2 の場合は多色を用いた + リッチなハイライトを行います。さらに、値が 3 の場合は編集後も追加文字 + 列を一定時間ハイライトします。 0 を設定するとハイライトを停止します。 + + 囲みを外すオペレータのハイライトやリッチなハイライトを使っている場合は + 、編集後も |g:operator#sandwich#highlight_duration| に指定された時間の + 間ハイライトが残りますが、この間もユーザーの操作はブロックされていませ + ん。何らかの入力があった場合直ちにハイライトを中止されます。 + + ハイライトに使われる色はユーザーの好きなように変更できます。 + |operator-sandwich-highlight-group| の項をご覧ください。 + + NOTE: このオプションはレシピで指定しても効果がありません。 + 変更する場合は |operator#sandwich#set()| 関数を使ってください。 +> + call operator#sandwich#set('all', 'all', 'highlight', 1) +< + + デフォルト値 + * add + - char : 3 + - line : 3 + - block: 3 + * delete + - char : 3 + - line : 3 + - block: 3 + * replace + - char : 3 + - line : 3 + - block: 3 + + + +hi_duration + オペレータにおける編集後にハイライトが持続する時間をミリ秒単位で指定し + ます。デフォルト値は 200 です。 + + NOTE: このオプションはレシピで指定しても効果がありません。 + 変更する場合は |operator#sandwich#set()| 関数を使ってください。 +> + call operator#sandwich#set('all', 'all', 'hi_duration', 1) +< + デフォルト値 + * add + - char : 200 + - line : 200 + - block: 200 + * delete + - char : 200 + - line : 200 + - block: 200 + * replace + - char : 200 + - line : 200 + - block: 200 + + + +skip_space + 値が 1 なら、指定された文字列の両端のスペースをスキップします。 +> + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'skip_space': 0} + \ ] + " Press sa5l( when the cursor is on the first space. + " ?foo? ---> ( foo ) : ? is space + + let g:sandwich#recipes = [] + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'skip_space': 1} + \ ] + " ?foo? ---> ?(foo)? : ? is space +< + ただし、囲みを外す/置換するオペレータについては両端が同じ文字である場 + 合は動作条件を満たすのでこれはただちに削除/置換されます。よってこのオ + プションは指定範囲の片側のみが空白であった場合に動作します。 +> + let g:sandwich#recipes += [ + \ {'buns': ['"', '"'], 'skip_space': 1} + \ ] + " Press sda" + " ?"foo" ---> ?foo : ? is space +< + 両端が空白の場合にもこれをスキップするためには値を 2 にします。 + + デフォルト値 + * add + - char : 0 + - line : 1 + - block: 1 + * delete + - char : 1 + - line : 2 + - block: 1 + * replace + - char : 1 + - line : 2 + - block: 1 + + + +noremap + このオプションは囲みを挿入する際にキーマッピングを展開するかどうかを切 + り替えます。値が真なら展開しません。 +> + inoremap ( [ + inoremap ) ] + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'noremap': 1} + \ ] + + " Press saiw( + " foo ---> (foo) + + let g:sandwich#recipes = [] + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'noremap': 0} + \ ] + + " Press saiw( + " foo ---> [foo] +< + このオプションは requisite の "external" にも適用されます。 + + デフォルト値 + * add + - char : 1 + - line : 1 + - block: 1 + * delete + - char : 1 + - line : 1 + - block: 1 + * replace + - char : 1 + - line : 1 + - block: 1 + + + +linewise + このオプションは文字列の挿入/削除の方法を切り替えます。通常の動作は文 + 字単位指向です。 +> + call operator#sandwich#set('add', 'char', 'linewise', 0) + " Press saiw( + " foo ---> (foo) +< + このオプションが1であれば、行指向になります。 +> + call operator#sandwich#set('add', 'char', 'linewise', 1) + " Press saiw( + " foo ---> ( + foo + ) +< + 文字列を削除する場合は、文字列削除後にその行に空白文字以外に残っていな + ければ行ごと削除します。 +> + call operator#sandwich#set('delete', 'char', 'linewise', 0) + " Press (operator-sandwich-delete)a( + " ( : this line was remained + " foo ---> foo : this line was remained + " ) : this line was remained + + call operator#sandwich#set('delete', 'char', 'linewise', 1) + " Press (operator-sandwich-delete)a( + " ( ---> foo + " foo + " ) +< + 空白文字以外が残るようであれば行を保存します。 +> + call operator#sandwich#set('delete', 'char', 'linewise', 1) + " Press (operator-sandwich-delete)a( + " (foo foo + " bar ---> bar + " baz) baz +< + 値が2の場合は空白以外が残っても気にせず行を削除します。 +> + call operator#sandwich#set('delete', 'char', 'linewise', 2) + " Press (operator-sandwich-delete)a( + " (foo ---> bar + " bar + " baz) +< + 囲みを置換するオペレータの場合はこれらの組み合わせの動作になります。 + + NOTE: コマンドラインウィンドウ |cmdline-window| 内では無視され、0が指 + 定されているかのように振舞います。 + + デフォルト値 + * add + - char : 0 + - line : 1 + - block: 0 + * delete + - char : 0 + - line : 1 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +command + オペレータ処理後に実行されるコマンドのリストです。 |'[| と |']| の二つ + のマークが編集した位置の先頭と末尾へ設定されるので、これを使うと便利で + しょう。 +> + let g:sandwich#recipes += [ + \ { + \ 'buns' : ['{', '}'], + \ 'linewise': 1, + \ 'command' : ["'[+1,']-1normal! >>"] + \ }, + \ ] + " Press saiw{ + " foo ---> { + foo + } +< + デフォルト値 + * add + - char : [] + - line : [] + - block: [] + * delete + - char : [] + - line : [] + - block: [] + * replace + - char : [] + - line : [] + - block: [] + + + +query_once + このオプションは [count] が与えられた場合の挙動を変更します。もしこの + 値が偽なら、文字列を囲むオペレータと囲みを置換するオペレータは与えられ + たカウントの回数だけ挿入文字列をユーザーに尋ねます。一方、このオプショ + ンの値が真の場合、一回だけ尋ねて、以降は同じものを挿入します。 + + デフォルト値 + * add + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +expr + このオプションの値が 1 か 2 なら "buns" は式として評価されてから挿入さ + れます。値が 1 の場合は一度だけ評価され、|.| コマンドでは同じものが使 + 用されます。値が 2 の場合は |.| コマンドで繰り返される毎に評価されます + 。例えば以下のレシピはその時々の無名レジスタ |quotequote| の内容を挿入 + します。 (参考: |:let-@|) +> + let g:sandwich#recipes += [ + \ {'buns': ['@@', '@@'], 'expr': 1, 'input': ['@']} + \ ] +< + NOTE: このオプションが真の時、 "buns" はレシピを呼ぶためのキー入力とは + みなされませんので、 "input" の指定が必要になります。 + + NOTE: もし式が |getchar()| や |input()| でユーザーに入力を求める場合、 + |operator-sandwich| の囲みを外すオペレータや |textobj-sandwich| + の |(textobj-sandwich-auto-i)| や + |(textobj-sandwich-auto-a)| で不都合が出ることがあります。 + この問題を避けるために、 "kind" フィルター及び "action" フィルタ + ーを併用することが推奨されます。例えば次のレシピは html スタイル + のタグで囲むことを目的として、タグ名の入力をユーザーに求めます。 + + NOTE: |textobj-sandwich| と違い |operator-sandwich| は "buns" に空文字 + 列を許容します。もし中止したい場合は例外 + 'OperatorSandwichCancel' を |:throw| してください。 +> + let g:operator#sandwich#recipes += [ + \ { + \ 'buns' : ['TagInput(1)', 'TagInput(0)'], + \ 'expr' : 1, + \ 'filetype': ['html'], + \ 'action' : ['add'], + \ 'input' : ['t'], + \ }, + \ ] + + function! TagInput(is_head) abort + if a:is_head + let s:TagLast = input('Tag: ') + if s:TagLast !=# '' + let tag = printf('<%s>', s:TagLast) + else + throw 'OperatorSandwichCancel' + endif + else + let tag = printf('', + \ matchstr(s:TagLast, '^\a[^[:blank:]>/]*')) + endif + return tag + endfunction +< + デフォルト値 + * add + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +listexpr + このオプションは上の "expr" オプションに似ていますが、一つの文字列を + buns として渡します。この文字列の評価値が文字列二つを含むリストを返す + ことが必要です。これは html タグのように前後の囲みが文字列を共有する場 + 合を "expr" の場合よりも自然に記述できます。 +> + let g:operator#sandwich#recipes += [ + \ { + \ 'buns' : 'HTMLTagInput()', + \ 'listexpr': 1, + \ 'filetype': ['html'], + \ 'action' : ['add'], + \ 'input' : ['t'], + \ }, + \ ] + + function! HTMLTagInput() abort + let tagstring = input('Tag: ') + if tagstring ==# '' + throw 'OperatorSandwichCancel' + endif + let former = printf('<%s>', tagstring) + let latter = printf('', + \ matchstr(tagstring, '^\a[^[:blank:]>/]*')) + return [former, latter] + endfunction +< + デフォルト値 + * add + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +autoindent + このオプションはオペレーターによる編集中の自動インデント機能を指定する + のにつかわれます。値には整数を指定します。1~3の整数にそれぞれ vim の + オプション 'autoindent', 'smartindent', 'cindent' が対応しています。値 + が 0 の場合はすべての自動インデント機能を停止します。値が負数の場合は + 設定を変更せず、現行の設定をそのまま使います。もし vim の 'indentexpr' + オプションを使用したい場合には負数を指定するとよいでしょう。 + + * -n : 設定を変更しない ( 'indentexpr' が空でなければそれが使われる) + * 0 : 'noautoindent', 'nosmartindent', 'nocindent', 'indentexpr'='' + * 1 : 'autoindent' + * 2 : 'smartindent' + * 3 : 'cindent' + * 4 : 常にインデントを保持する +> + let g:sandwich#recipes += [ + \ {'buns': ['{', '}'], 'motionwise': ['line'], 'autoindent': 3}, + \ ] +< + Default values + * add + - char : -1 + - line : -1 + - block: -1 + * replace + - char : -1 + - line : -1 + - block: -1 + + + +indentkeys +indentkeys+ +indentkeys- + これらのオプションはオペレーター編集中に vim のオプション 'indentkeys' + あるいは 'cinkeys' オプションの値を指定するためのオプションです。書式 + は 'cinkeys' オプションと同様です。"indentkeys" オプションの値は + |:set| indentkeys={value} + の {value} のように設定されます。同じように "indentkeys+", + "indentkeys-" オプションの値はそれぞれ、 + |:set| indentkeys+={value} + |:set| indentkeys-={value} + {value} のように設定されます。ただし一点だけ違いがあり、 + |:set| indentkeys-={value} の場合は、次のようにその順番に処理が依存し + ます。 +> + :set indentkeys? " -> 0{,0},:,0#,!^F,o,O,e + :set indentkeys-=0},0{ + :set indentkeys? " -> 0{,0},:,0#,!^F,o,O,e + + " '0{' and '0}' は取り除かれていない、 + " なぜなら '0},0{' という文字列は 'indentkeys' の中にないからである。 + + :set indentkeys-=0} + :set indentkeys? " -> 0{,:,0#,!^F,o,O,e + :set indentkeys-=0{ + :set indentkeys? " -> :,0#,!^F,o,O,e +< + これに対し "indentkeys-" オプションの場合は順番を気にする必要はありま + せん。単純にコンマ区切りで列挙してください。 +> + let g:sandwich#recipes += [ + \ {'buns': ['[', ']'], 'filetype': ['tex'], 'indentkeys-': '[,]'} + \ ] + + " This is same as the above + "let g:sandwich#recipes += [ + " \ {'buns': ['[', ']'], 'filetype': ['tex'], 'indentkeys-': '],['} + " \ ] +< + もし、 'indentexpr' オプションが空で 'cindent' が有効なら自動的に + 'cinkeys' へ処理が切り替わります。 + + 処理される順番は "indentkeys" -> "indentkeys+" -> "indentkeys-" のよう + になっています。値が文字列以外の場合は 'indentkeys' 及び 'cinkeys' の + 値を変更しません。 + + Default values + * add + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +regex + もしこのオプションの値が真なら "buns" は正規表現だとみなされます。また + このオプションが真の場合は文字列を囲む/囲みを置換するオペレータでの挿 + 入文字列には使用されなくなります。削除する文字列の検索にのみ使われます + 。 +> + let g:sandwich#recipes += [ + \ {'buns': ['\d\+', '\d\+'], 'regex': 1} + \ ] + " Press (operator-sandwich-delete)iw + " 123foo456 ---> foo +< + デフォルト値 + * delete + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +skip_char + もしこのオプションの値が真なら登録された囲みを見つけるまで指定範囲の両 + 端を縮小します。 +> + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'skip_char': 1} + \ ] + " Press V(operator-sandwich-delete) + " foo(bar)baz ---> foobarbaz +< + デフォルト値 + * delete + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +Global option~ + +いくつか基本的な動作を制御するためのオプションが存在します。 + +g:operator#sandwich#timeout *g:operator#sandwich#timeout* + このオプションに偽値が設定されている場合、オペレータは一つのレシピを指 + 定する完全な入力がなされるまで待ちます。例えば、下記のレシピを用意しま + す +> + let g:sandwich#recipes = [ + \ {'buns': ['for {', '}'], 'nesting': 1, 'input': ['bf']} + \ {'buns': ['if {', '}'], 'nesting': 1, 'input': ['bi']} + \ {'buns': ['else {', '}'], 'nesting': 1, 'input': ['be']} + \ ] +< + このオプションが真の場合は `saiwb` とタイプして少し待つと、オペレータ + はタイムアウトのため単語を `b` で囲みます。しかし、偽の場合は一つのレ + シピを指定する入力が完成するまで待ちます。この変数が定義されていなけれ + ば |g:sandwich#timeout| が代わりに参照されます。特別な理由がなければ + |g:sandwich#timeout| を使ってください。 + 関連:|g:operator#sandwich#timeoutlen| + + + +g:operator#sandwich#timeoutlen *g:operator#sandwich#timeoutlen* + 入力に前方一致で重複するレシピが存在する場合に次のキーシーケンスを待つ + 時間をミリ秒単位で指定します。 +> + let g:sandwich#recipes = [ + \ {'buns': ['(', ')']} + \ {'buns': ['((', '))']} + \ ] +< + saiw( とキー入力するとオペレータは次に ( が入力されるかこの時間だけ待 + ちます。この間にもう一度 ( を押下すると '((' と '))' のレシピが使われ + るでしょう。キーの押下なしでこの時間が過ぎると '(' と ')' のレシピが使 + われるでしょう。この変数が存在しなければ |g:sandwich#timeoutlen| が代 + わりに参照されます。特別な理由がなければ |g:sandwich#timeoutlen| を + 使ってください。 + + タイムアウト機能(|g:operator#sandwich#timeout|, |g:sandwich#timeout|, + 'timeout')がオフの場合はこのオプションは無視されます。 + + + + *g:operator#sandwich#highlight_duration* +g:operator#sandwich#highlight_duration + オペレータにおける編集後にハイライトが持続する時間をミリ秒単位で指定し + ます。デフォルト値は 200 です。 + + NOTE: このオプションは近々廃止される予定です。代わりに local option の + "hi_duration" をご使用ください。 + + + + *g:operator#sandwich#persistent_highlight* +g:operator#sandwich#persistent_highlight + 囲みを追加/置換するオペレータの編集後のハイライトの方法を指定します。 + どちらの場合も一定時間 (|g:operator#sandwich#highlight_duration| に指 + 定された時間) の間ハイライトが持続します。このオプションに "blink" が + 指定されれば、ハイライトはユーザーの入力によりただちに中止されるでしょ + う。 "glow" が指定されれば、ユーザーがテキストを編集するまで中止されま + せん。デフォルト値は "glow" です。 + + NOTE: 囲みを外すオペレータは "blink" の挙動で固定されています。 + + NOTE: |g:operator#sandwich#highlight_duration| が小さい場合は二つの選 + 択肢にほとんど違いは見られないでしょう。 + + + +============================================================================== +HIGHLIGHT GROUP *operator-sandwich-highlight-group* + +三つのオペレータすべてが何らかのハイライト機能を持っています。そのオンオフの切 +り替えは local option の highlight によって切り替えられますが、その色について +は独自のハイライトグループによって管理されます。 + +OperatorSandwichBuns *hl-OperatorSandwichBuns* + このハイライトグループによってオペレータのハイライト色を宣言します。 + このグループは "highlight" オプションが 1 のときに使われます。 + デフォルトでは IncSearch |hl-IncSearch| グループにリンクしています。 + + +OperatorSandwichChange *hl-OperatorSandwichChange* + このハイライトグループによってオペレータのハイライト色を宣言します。 + このグループは "highlight" オプションが 2 以上のときに、囲みを追加する + オペレーターで囲まれる文字列に使われます。 + デフォルトでは DiffChange |hl-DiffChange| グループにリンクしています。 + + +OperatorSandwichDelete *hl-OperatorSandwichDelete* + このハイライトグループによってオペレータのハイライト色を宣言します。 + このグループは "highlight" オプションが 2 以上のときに、囲みを + 削除/置換するオペレーターで削除される囲み文字列に使われます。 + デフォルトでは DiffDelete |hl-DiffDelete| グループにリンクしています。 + + +OperatorSandwichAdd *hl-OperatorSandwichAdd* + このハイライトグループによってオペレータのハイライト色を宣言します。 + このグループは "highlight" オプションが 3 以上のときに、囲みを + 追加/置換するオペレーターで追加される囲み文字列に使われます。 + デフォルトでは DiffAdd |hl-DiffAdd| グループにリンクしています。 + + + ユーザーはこれらを自由に変更できます。 +> + " Example 1 + highlight link OperatorSandwichBuns Visual + + " Example 2 + highlight OperatorSandwichBuns ctermfg=White ctermbg=Red + \ guifg=White guibg=Red +< + + + +============================================================================== +AUTOCOMMAND *operator-sandwich-autocommands* + +ユーザーは |:autocmd| によってオペレータの動作前と後に処理をフックできます。そ +れぞれのオペレータは独自の |User| オートコマンドイベントを持っているのでこれを +使いオートコマンドを設定するとよいでしょう。 + +文字列を囲むオペレータ~ +OperatorSandwichAddPre *OperatorSandwichAddPre* + 文字列を囲むオペレータの処理前に実行されます。 + +OperatorSandwichAddPost *OperatorSandwichAddPost* + 文字列を囲むオペレータの処理後に実行されます。 + +囲みを外すオペレータ~ +OperatorSandwichDeletePre *OperatorSandwichDeletePre* + 囲みを外すオペレータの処理前に実行されます。 + +OperatorSandwichDeletePost *OperatorSandwichDeletePost* + 囲みを外すオペレータの処理後に実行されます。 + +囲みを置換するオペレータ~ +OperatorSandwichReplacePre *OperatorSandwichReplacePre* + 囲みを置換するオペレータの処理前に実行されます。 + +OperatorSandwichReplacePost *OperatorSandwichReplacePost* + 囲みを置換するオペレータの処理後に実行されます。 + +実用例~ +下記の例は Vim のデフォルトプラグイン matchit.vim のハイライトをオペレータ処理 +中のみ抑制します。 +> + autocmd User OperatorSandwichAddPre,OperatorSandwichReplacePre NoMatchParen + autocmd User OperatorSandwichAddPost,OperatorSandwichReplacePost DoMatchParen +< + + + +============================================================================== +API *operator-sandwich-api* + +|operator-sandwich| はいくつかの関数を API として提供します。これらの関数は +"expr" オプション付きの設定を便利にするのに役立つことでしょう。 + +operator#sandwich#get_info({info}) *operator#sandwich#get_info()* + 現在のオペレーターの情報を返します。有効な {info} は次です。 + `state`: |.| リピート時には 0 です。それ以外は 1 です。 + `kind` : オペレーターの種類です。 + "add", "delete", "replace" のどれかが返ります。 + `count`: オペレーターに与えられた [count] です。 + `mode` : オペレーターの使用されたモードを表す一文字です。ノーマル + モードなら "n" が、ビジュアルモードなら "x" です。 + `motionwise`: + 編集する領域の種類。 "char", "line", "block" または空文字 + 列 "" が返りうる。 + +operator#sandwich#kind() *operator#sandwich#kind()* + オペレーター待機モード |operator-pending-mode| において使用された場合 + に、使用される予定の |operator-sandwich| オペレーターの名前を返しま + す。つまり "add", "delete", "replace" が返ります。それ以外の場合には空 + 文字が返ります。他のオペレーターを使用した場合にも空文字を返します。 + |textobj-sandwich| との連携に便利かもしれません。 + + *operator#sandwich#show()* +operator#sandwich#show([{place}[, {group}[, {forcibly}]]]) + この関数はテキストをハイライトするのに使います。ハイライトするテキスト + は {place} によって指定され、次の値が指定可能です。 + `target`: 置換オペレーターにより置換の対象となるテキスト + `stuff` : 囲みを追加するオペレーターにより囲まれるテキスト + `added` : 囲みを追加/置換するオペレーターにより追加されたテキスト + {place} が省略された場合、 囲みを追加するオペレーターには `stuff` を、 + 削除/置換するオペレータには `target` を使います。 + + {group} が与えられた場合、テキストはこのハイライトグループ + |highlight-groups| によりハイライトされます。テキストが既にハイライト + されている場合は {group} が現在の値と違う場合のみ更新しますが、そうで + なければ何もせず終了します。ハイライトの追加あるいは更新に成功した + 場合、関数は 0 を返します。それ以外の場合は 1 を返します。 + もし、 {forcibly} が与えられ真の場合、この関数は "highlight" + オプションに関わらずテキストのハイライトを実行します。 + + + *operator#sandwich#quench()* +operator#sandwich#quench([{place}]) + |operator#sandwich#show()| 関数によるハイライトを削除します。 {place} + には |operator#sandwich#show()| に与えたものと同じ値を指定してくだ + さい。 {place} が省略された場合、 囲みを追加するオペレーターには + `stuff` を、削除/置換するオペレータには `target` を使います。 + 削除に成功すれば 0 、それ以外の場合は 1 を返します。 + +例えば、 query1st 系列のキーマッピング +|(operator-sandwich-add-query1st)| は、その囲むテキストをハイライトしま +せん。しかし、ユーザーに入力を促すような "expr" 設定を使った場合、ハイライトが +行われると見栄えがよくなる場合があります。下記の例はユーザーに関数名を問い合わ +せ、その関数で囲むような新しいキーマッピングを定義しています。 +> + nmap sf (operator-sandwich-add-query1st) + xmap sf (operator-sandwich-add) + + let g:operator#sandwich#recipes += [ + \ { + \ 'buns': ['FuncName()', '")"'], + \ 'kind': ['add'], + \ 'action': ['add'], + \ 'expr': 1, + \ 'cursor': 'inner_tail', + \ 'input': ["\"] + \ }, + \ ] + + function! FuncName() abort + call operator#sandwich#show('stuff') + let funcname = input('funcname: ') + call operator#sandwich#quench('stuff') + if funcname ==# '' + throw 'OperatorSandwichCancel' + endif + return funcname . '(' + endfunction +< +この新しいオペレータキーマッピング `sf` は `sfiwfunc` という入力により +"foo" を "func(foo)" にします。 + + + +============================================================================== +MISCELLANEOUS *operator-sandwich-miscellaneous* + +囲みを外すオペレータのハイライトはいらない~ + そうですね。次の行を vimrc へ追記しましょう。 +> + call operator#sandwich#set('delete', 'all', 'highlight', 0) +< + + + +ハイライトは全部いらない~ + かもしれませんね。次の行を vimrc へ追記しましょう。 +> + call operator#sandwich#set('all', 'all', 'highlight', 0) +< + + + +matchit.vim のハイライトがかぶって邪魔~ + |:autocmd| を使って一時的に抑制しましょう。 + |operator-sandwich-autocommands| をご参照ください。 +> + autocmd User OperatorSandwichAddPre,OperatorSandwichReplacePre NoMatchParen + autocmd User OperatorSandwichAddPost,OperatorSandwichReplacePost DoMatchParen +< + あるいは単にはっきりと違う色を使うのもいいですね。 + |operator-sandwich-highlight-group| の項をご覧ください。 +> + highlight OperatorSandwichBuns ctermfg=White ctermbg=Red + \ guifg=White guibg=Red +< + + + +文字列を囲むオペレータの文字指向の場合だけ空白をスキップしないのはなぜ?~ + 私がたまに不便に思ったからです。端的に言うとビジュアルモードなどでスペ + ース含みで囲みたい場合があったからです。お気に召さなければ次の行を + vimrc へ追加してください。 +> + call operator#sandwich#set('add', 'char', 'skip_space', 1) +< + + + +なぜ囲みを外す/置換するオペレータのキーマッピングはデフォルトで提供されない?~ + これらのオペレータの動作条件を鑑みるに、ほぼ常に |textobj-sandwich| と + 組み合わせるのが便利だからです。このため単体ではなくテキストオブジェク + トとの複合マッピングがかわりに用意されます。詳しくは + |sandwich-keymappings| の項をご覧ください。また、ビジュアルモードでは + 単体でマッピングされていますよ。もちろんノーマルモードでも動作条件を満 + たす限り、あらゆるモーションあるいはテキストオブジェクトと組み合わせる + ことができますので興味があれば試してみてください。 + |(operator-sandwich-delete)| + |(operator-sandwich-replace)| + + + +ドットコマンドでもカーソル位置を保存・復元したい~ + local option "cursor" の "keep" はデフォルトでは |.| コマンドリピート + では有効ではありません。しかし必要であれば、 + |(operator-sandwich-predot)| か |(operator-sandwich-dot)| + を |.| キーにマッピングすることでこの機能を有効にすることができます。 + もし他に |.| コマンドにマップするようなプラグインをご使用の場合は + |(operator-sandwich-predot)| を使います。例えば + `(ExampleDot)` というマッピングを |.| キーに定義するプラグインを + ご使用の場合は次のように使います。 +> + nmap . (operator-sandwich-predot)(ExampleDot) +< + repeat.vim (vimscript #2136) をご使用の場合は `(RepeatDot)` の + 定義が遅延されているので、そのキーマッピングの存在を保証するために次の + 行を vimrc へ追加します。 +> + runtime autoload/repeat.vim + nmap . (operator-sandwich-predot)(RepeatDot) +< + 上記のようなプラグインを使ってない場合は + |(operator-sandwich-dot)| を使います。 +> + nmap . (operator-sandwich-dot) +< + |(operator-sandwich-predot)| 及び + |(operator-sandwich-dot)| それぞれの詳しい説明もご覧ください。 + + + +Query1st 系列のキーマッピングは何のためにある?~ + Query1st 系列のキーマッピングは二つあります。 + |(operator-sandwich-add-query1st)| 及び + |(operator-sandwich-replace-query1st)| です。これらは通常の + |(operator-sandwich-add)| 及び + |(operator-sandwich-replace)| とはノーマルモードでの使用時のキー + 入力の順序が違います。通常のキーマッピングは +> + {operator}{motion/textobject}{addition} +< + のように {addition} の指定を最後に入力します。つまり、単語を () で囲み + たい場合 `saiw(` のように入力します。この場合、 `sa` が {operator}, + `iw` が {textobject}, `(` が {addition}の指定になります。 + + これに対し、query1st 系列のキーマッピングは{addition} の指定が + {textobj} の前にきます。 +> + {operator}{addition}{motion/textobj} +< + つまり、先の例と同等の入力は `sa(iw` になります。これは例えばより + 限定的なマッピングを作るのに便利です。 +> + nmap s( (operator-sandwich-add-query1st)( + nmap s[ (operator-sandwich-add-query1st)[ + nmap s{ (operator-sandwich-add-query1st){ + + " Press s(iw + " foo -> (foo) +< + Query1st 系列のキーマッピングは、範囲を指定する {motion} あるいは + {textobject} よりも前に {addition} を決定するため、いくつか制限があり + ます。 + + 1. motionwise フィルタは実際の範囲指定にかかわらず、すべての種類 + (char, line, block) について有効なレシピのみを残します。 + 2. highlight オプションは無視され、ハイライトは行われません。 + 3. 主に上記のような使用を想定しているため query_once オプションは 1 に + 設定されます。これは |operator#sandwich#query1st()| 関数を使って + キーマッピングを設定しなおすことで上書きできます。 +> + nmap (operator-sandwich-add-query1st) + \ :call operator#sandwich#query1st('add', 'n', {'query_once': 0}) + + nmap ssa (operator-sandwich-add-query1st) +< + ビジュアルモードでは入力順序が変わらないので query1st 系列のキーマッピ + ングを使う必要はありません。上記の制限もあるため通常のキーマッピングを + 使うことが望ましいでしょう。 +> + xmap s( (operator-sandwich-add)( + xmap s[ (operator-sandwich-add)[ + xmap s{ (operator-sandwich-add){ +< + + +置換オペレータにおけるレシピに指定されたオプションの扱いについて~ + 置換オペレータの編集には一度に二つのレシピが関係しますが、オプションの + 種類によってどちらの値が使用されるかが変わります。 + + `cursor` + どちらのレシピでも指定できますが、挿入文字列を指定したレシピが優先され + ます。 + + `query_once` + 挿入文字列を指定したレシピが使われます。 + + `regex` + 置換対象文字列を指定したレシピが使われます。 + + `expr` + 挿入文字列を指定したレシピが使われます。 + + `noremap` + 置換対象範囲の決定には置換対象文字列を指定したレシピが、文字列の挿入に + は挿入文字列を指定したレシピが使われます。 + + `skip_space` + 置換対象文字列を指定したレシピが使われます。 + + `skip_char` + 置換対象文字列を指定したレシピが使われます。 + + `command` + 両方のレシピに指定された値が有効です。挿入文字列を指定したレシピの値が + 後に実行されます。 + + `linewise` + 両方のレシピに指定された値のうち大きいほうが使われます。 + + `autoindent` + 挿入文字列を指定したレシピが使われます。 + + `indentkeys` + 挿入文字列を指定したレシピが使われます。 + + `indentkeys+` + 挿入文字列を指定したレシピが使われます。 + + `indentkeys-` + 挿入文字列を指定したレシピが使われます。 + + + +visualrepeat.vim プラグイン対応について + このプラグインは visualrepeat.vim プラグイン (vimscript #3848) に対応 + しています。このプラグインを利用するうえでユーザーが特別な設定をする + 必要はありません。上記のプラグインが正しくインストールされていれば、 + ビジュアルモードにおいても |.| コマンドが機能するようになります。 + + + +============================================================================== +vim:tw=78:ts=8:ft=help:norl:noet: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/doc/operator-sandwich.txt b/config/neovim/store/lazy-plugins/vim-sandwich/doc/operator-sandwich.txt new file mode 100644 index 00000000..3c7a6091 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/doc/operator-sandwich.txt @@ -0,0 +1,1890 @@ +*operator-sandwich.txt* The operator plugin to edit sandwiched textobjects. + Last change:28-Nov-2021. + +Author : machakann +License : NYSL license + Japanese + English (Unofficial) + + Requirement: Vim 7.4 or higher + |+reltime| feature (optional) + |+float| feature (optional) + visualrepeat.vim (vimscript #3848) plugin (optional) + +============================================================================== +CONTENTS *operator-sandwich-contents* + +TL;DR +INTRODUCTION |operator-sandwich-introduction| +KEYMAPPINGS |operator-sandwich-keymappings| +CONFIGURATION |operator-sandwich-configuration| + REQUISITE + buns + external + FILTERS + filetype + kind + motionwise + mode + action + expr_filter + LOCAL OPTIONS + cursor + highlight + hi_duration + skip_space + noremap + linewise + command + query_once + expr + autoindent + indentkeys + indentkeys+ + indentkeys- + regex + skip_char + GLOBAL OPTIONS + timeout |g:operator#sandwich#timeout| + timeoutlen |g:operator#sandwich#timeoutlen| + highlight_duration |g:operator#sandwich#highlight_duration| +HIGHLIGHT GROUP |operator-sandwich-highlight-group| +AUTOCOMMANDS |operator-sandwich-autocommands| +API |operator-sandwich-api| +MISCELLANEOUS |operator-sandwich-miscellaneous| + Highlighting in the delete operator is really irritating. + Highlighting is always irritating. + Highlighting by matchit.vim is confusing. + Why does not the add operator skip white spaces in character-wise motion? + Why the default mappings for the delete/replace operators were not defined? + I want to keep cursor position even after dot repeating. + What is special about the query1st series mappings? + About the handlings of recipe-local options in the replace operator. + About the visualrepeat.vim plugin support + +============================================================================== +TL;DR + See |sandwich-quick-start|. + + + +============================================================================== +INTRODUCTION *operator-sandwich-introduction* + +*operator-sandwich* is an operator plugin to edit sandwiched textobjects, like +(foo) or "bar". It consists of three independent operators, +|(operator-sandwich-add)|, |(operator-sandwich-delete)|, and +|(operator-sandwich-replace)|. + +|(operator-sandwich-add)| is an operator to add surroundings to a +textobject. This keymapping is mapped to `sa` in default. For example, assume +that the cursor is on the foo in a text: +> + foo +< +press `saiw"`, then you will get the following. +> + "foo" +< +Or if you press `saiw(`, then you will get the following. +> + (foo) +< +As for the case of '(' and ')', they are registered as a set of surroundings. +That's why the word is appropriately surrounded, otherwise this operator puts +same characters to the both ends like the case of '"'. The registered set of +surroundings are called "recipe", it is described in detail at +|operator-sandwich-configuration|. + +|(operator-sandwich-delete)| is an operator to delete surroundings to a +textobject. This keymapping is not mapped to any key in normal mode, if you +want to try please assign it to your preferable key. This operator delete +surroundings if the texts at the both ends of selected region are registered +in recipes or are just same characters. For example, assume that it is mapped +to `sd` and the cursor is on the (foo) in a text: +> + (foo) +< +press `sda(` deletes the parenthesis since the default textobject |a(| assigns +the region surrounded by "(" and ")" including them. +> + foo +< +It works for "foo" as same with the keypress sd2i" (|iquote|). In practice, +however, |(operator-sandwich-delete)| is not assigned to `sd` in normal +mode. Because this operator works well with |textobj-sandwich| which is +implemented to search and select a regions surrounded. Therefore, `sd` is +mapped to the key combination with the textobject in advance. See +|sandwich-keymappings|. + +|(operator-sandwich-replace)| is an operator to replace surroundings to +a textobject. This keymapping is not mapped to any key in normal mode, if you +want to try please assign it to your preferable key. This operator replace +surroundings if the texts at the both ends of selected region are registered +in recipes or are just same characters. For example, assume that it is mapped +to `sr` and the cursor is on the (foo) in a text: +> + (foo) +< +press `sra("`, then the operator replace "(" and ")" to '"'. +> + "foo" +< +The order is that the deletion, "(" and ")", is the first and then the +addition, '"', comes the next. The deleted and added text is chosen in the +same manner with |(operator-sandwich-delete)| and +|(operator-sandwich-add)|. In practice, `sr` is used by the default +keymappings in combination with |textobj-sandwich| in the same reason with +`sd`. +See |sandwich-keymappings|. + +------------------------------------------------------------------------------ +These all operators accepts [count], but its handling is not as usual. As for +the default operators and textobjects, the possible key input should be like +this. + + [count]{operator}[count]{textobject} + +For example, 3|d||iw| deletes two words and a intermediate space. There are +two points to tell the count, but they are not distinguished. That is, +`3diw` and `d3iw` is fundamentally the same. If both of two points are used, +the count is their product, `3d2iw` and `2d3iw` is identical to `6diw` or +`d6iw.` + +In contrast, as for the case of |operator-sandwich|, these three operators +distinguish the count depending on the point to be told. These operators takes +only the first count for own use and the second count is passed to the +following textobject. For example, `2sa3iw((` surrounds two words and a space +by two set of parenthesis. +> + foo bar ---> ((foo bar)) +< +When you use |.| for the single repeating, the last count is continue to be +used. If the count is given to |.| command, only the count for the textobject +would be updated. + +------------------------------------------------------------------------------ +These operators work also in |blockwise-visual| mode. For example, the +following 3 by 3 region is selected: +> + foo + bar + baz +< +press `sa(` +> + (foo) + (bar) + (baz) +< +If short lines are included, for example, the following 4 by 4 region is +selected: +> + a + bb + ccc + dddd +< +press `sa(` +> + (a) + (bb) + (ccc) + (dddd) +< +All the lines are processed until the ends of these lines. If a last line is +not the longest line, use |$| command in |blockwise-visual| mode. It extends +the selection until the each end of lines. +> + aaaa + bbb + cc + d +< +Start from a in the first line, press `3j$sa(`, then you will get: +> + (aaaa) + (bbb) + (cc) + (d) +< + + + +============================================================================== +KEYMAPPINGS *operator-sandwich-keymappings* + +This plugin prepares following keymappings. |(operator-sandwich-add)|, +|(operator-sandwich-delete)| and |(operator-sandwich-replace)| are +valid in normal, visual and operator-pending mode in principle. +|(operator-sandwich-add)| is mapped to the keysequences `sa` in default, +while |(operator-sandwich-delete)| and +|(operator-sandwich-replace)| are mappded to the key sequences `sd` and +`sr` respectively only in visual mode. `sd` and `sr` are used by the compound +mappings with |textobj-sandwich| in normal mode. See |sandwich-keymappings|. + +function keymappings default keymappings +-------------------------------------------------------------------------- +add (operator-sandwich-add) sa + +delete (operator-sandwich-delete) sd (in visual mode) + +replace (operator-sandwich-replace) sr (in visual mode) +-------------------------------------------------------------------------- + +If you do not need default keymappings, define a variable named +g:operator_sandwich_no_default_key_mappings in your vimrc. +> + let g:operator_sandwich_no_default_key_mappings = 1 +< +Then default mappings are never applied. And map them again as you like. +> + map ys (operator-sandwich-add) +< + +NOTE: To prevent unintended operation, the following setting is strongly + recommended to add to your vimrc. +> + nmap s + xmap s +< + |s| could be easily replaced by |c|l| commands. + +------------------------------------------------------------------------------ +keymappings~ +(operator-sandwich-add) *(operator-sandwich-add)* + The operator command to add surroundings to the both end of the + assigned region. If [count] is given, add surroundings [count] times. + +(operator-sandwich-delete) *(operator-sandwich-delete)* + The operator command to delete surroundings at the both end of the + assigned region. If [count] is given, delete successive sets of + surroundings [count] times. + +(operator-sandwich-replace) *(operator-sandwich-replace)* + The operator command to replace surroundings at the both end of the + assigned region. If [count] is given, replace successive sets of + surroundings [count] times. + + + +query-1st series~ + *(operator-sandwich-add-query1st)* +(operator-sandwich-add-query1st) + The operator command to add surroundings to the both end of the + assigned region. If [count] is given, add surroundings [count] times. + Different from |(operator-sandwich-add)|, this keymapping asks + the addition before {motion/textobject}. +> + nmap sa (operator-sandwich-add-query1st) + + " Press sa(iw + " foo -> (foo) +< + NOTE: Use it only in normal mode although user can define it in + visual mode by |:xmap|. The order of key sequence is same as the + usual keymapping, |(operator-sandwich-add)| in visual + mode. See also |operator-sandwich-miscellaneous|. + + *(operator-sandwich-replace-query1st)* +(operator-sandwich-replace-query1st) + The operator command to replace surroundings at the both end of the + assigned region. If [count] is given, replace successive sets of + surroundings [count] times. Different from + |(operator-sandwich-replace)|, this keymapping asks the addition + before {motion/textobject}. +> + nmap sa (operator-sandwich-replace-query1st) + + " Press sr(a[ + " [foo] -> (foo) +< + NOTE: Use it only in normal mode although user can define it in + visual mode by |:xmap|. The order of key sequence is same as the + usual keymapping, |(operator-sandwich-replace)| in visual + mode. See also |operator-sandwich-miscellaneous|. + + + +Supplementary keymappings~ + +There are three supplementary keymappings related to the count treatment. They +may be useful when you define your short-hand keymapping. About the unique +count interpretation of the operators, See |operator-sandwich-introduction|. +Practical examples are in |sandwich-keymappings|. + + *(operator-sandwich-synchro-count)* +(operator-sandwich-synchro-count) + The keymapping to pass a same count given to the operator for + following motions/textobjects. +> + nmap sd( + \ (operator-sandwich-delete)(operator-sandwich-synchro-count)a( +< + A key sequence `2sd(` is identical to + `2(operator-sandwich-delete)2a(` in this case. + + + + *(operator-sandwich-release-count)* +(operator-sandwich-release-count) + The keymapping that a count pass through the operator for following + motions/textobjects. +> + nmap sd( + \ (operator-sandwich-delete)(operator-sandwich-release-count)a( +< + A key sequence `2sd(` is identical to + `(operator-sandwich-delete)2a(` in this case. + + + + *(operator-sandwich-squash-count)* +(operator-sandwich-squash-count) + The keymapping that ignores a given count completely. +> + nmap sd( + \ (operator-sandwich-delete)(operator-sandwich-squash-count)a( +< + A key sequence `2sd(` is identical to + `(operator-sandwich-delete)a(` in this case. + + + +There are two supplementary keymappings to make dot repeating more convenient. +These keymappings relates to a local option, "cursor". There are the choice +"keep" of "cursor" option to keep cursor position after an operator action. +This feature is not valid with dot repeating in default. However if you really +need, these keymappings realize it. Note that it is possible to conflict with +other plugins, please use either one which is appropriate for your +environment. See a topic of a local option "cursor" in +|operator-sandwich-configuration|. +vim-event-DotCommandPre +could be an alternative of the following key mappings. + + *(operator-sandwich-predot)* +(operator-sandwich-predot) + The keymapping activates the choice "keep" of the "cursor" option with + dot repeating. This keymapping itself does not send key sequence of + |.|. Map this key ahead of |.| key sequence. If you are using other + plugins which make keymapping to |.| key, use this keymapping. For + example, if you are using a plugin which defines a mapping + `(ExampleDot)`: +> + nmap . (operator-sandwich-predot)(ExampleDot) +< + In case of using repeat.vim (vimscript #2136), the mapping + `(RepeatDot)` is defined retadatively. Thus, in order to ensure + the mapping exists, add the lines: +> + runtime autoload/repeat.vim + nmap . (operator-sandwich-predot)(RepeatDot) +< + + *(operator-sandwich-dot)* +(operator-sandwich-dot) + The keymapping activates the choice "keep" of the "cursor" option with + dot repeating. This keymapping itself sends key sequence of |.|. If + you are not using any other plugin which makes keymapping to |.| key, + use this keymapping. Add the following line into your vimrc. +> + nmap . (operator-sandwich-dot) +< + + + +KEY MAPPING FUNCTIONS~ + +User can make new mappings by using function interfaces natively. + + *operator#sandwich#keymap()* +operator#sandwich#keymap(kind, mode[, options[, recipes]]) + This function is used to make a operator key-mapping as following. +> + nnoremap sa :call operator#sandwich#keymap('add', 'n') + xnoremap sa :call operator#sandwich#keymap('add', 'x') +< + If a not-empty dictionary is given to the optional third argument of + this function, the local options inside the dictionary override + default option values. However, it can not be assigned to motionwises + separately. +> + " example 1 + nnoremap sa + \ :call operator#sandwich#keymap( + \ 'add', + \ 'n', + \ {'highlight': 0} + \ ) + + " example 2 + let g:sandwich_alt_options = {'highlight': 0} + nnoremap sa + \ :call operator#sandwich#keymap( + \ 'add', + \ 'n', + \ g:sandwich_alt_options + \ ) +< + + If a list of recipes is given to the optional fourth argument of this + function, the key mapping uses the list instead of + |g:sandwich#recipes| (or |g:sandwich#default_recipes|) and + |g:operator#sandwich#recipes| + (or |g:operator#sandwich#default_recipes|). +> + " example 1 + nnoremap sa + \ :call operator#sandwich#keymap( + \ 'add', + \ 'n', + \ {}, + \ [{'buns': ['(', ')']}] + \ ) + + " example 2 + let g:sandwich_alt_recipes = [{'buns': ['(', ')']}] + nnoremap sa + \ :call operator#sandwich#keymap( + \ 'add', + \ 'n', + \ {}, + \ g:sandwich_alt_recipes + \ ) +< + + + + *operator#sandwich#query1st()* +operator#sandwich#query1st(kind, mode[, options[, recipes]]) + This function is similar as |operator#sandwich#keymap()|, but it is + used for declaring new query1st series key mappings, + |(operator-sandwich-add-query1st)| and + |(operator-sandwich-replace-query1st)|. + + + +============================================================================== +CONFIGURATION *operator-sandwich-configuration* + +A set of surroundings and options for it is called "recipe". Each recipe is a +dictionary and the |list|s of recipes determines the operator's behavior. +|g:sandwich#default_recipes| is one of the |list|s of recipes. This is shared +to be used with |textobj-sandwich| since it is convenient in many cases. If +|g:sandwich#recipes| is defined by user, it is employed alternatively. The +default recipes |g:sandwich#default_recipes| can be checked by |:echo| +command. +> + :echo g:sandwich#default_recipes +< +This variable is locked usually, but it can be copied when you declare +|g:sandwich#recipes| if you need. +> + :let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +< +The set of (), [], {}, <> are registered in default. The detailed description +is in |g:sandwich#default_recipes|. + +|g:operator#sandwich#default_recipes| is another list of recipes. This is used +only by |operator-sandwich|. If |g:operator#sandwich#recipes| is defined, it +is employed alternatively. |g:operator#sandwich#default_recipes| is locked as +same as |g:sandwich#default_recipes|. + + + +g:operator#sandwich#recipes *g:operator#sandwich#recipes* + This is one of the lists of recipes which is referred only from + |operator-sandwich|. If this list does not exist, + |g:operator#sandwich#default_recipes| is used. + *b:operator_sandwich_recipes* + If |b:operator_sandwich_recipes| exists, it would be used instead of + |g:operator#sandwich#recipes|. This is buffer local, thus it might be + convenient to manage too many filetype-specific recipes. + + + +g:operator#sandwich#default_recipes *g:operator#sandwich#default_recipes* + This is one of the lists of recipes which is prepared in default. If + |g:operator#sandwich#recipes| exists, it will be used alternatively. + This variable is locked usually, but it can be copied when you declare + |g:operator#sandwich#recipes| if you need. +> + :let g:operator#sandwich#recipes + \ = deepcopy(g:operator#sandwich#default_recipes) +< + + + +NOTE: If recipes are conflicted in some reason, + |g:operator#sandwich#default_recipes| and |g:operator#sandwich#recipes| + is prior to |g:sandwich#default_recipes| and |g:sandwich#recipes|. In a + same list, a latter item is prior to a former item. + +------------------------------------------------------------------------------ +A recipe is a |Dictionary| variable and it can have four kinds of information. +Requisite, input, filters and local options. The requisite is essential for +all recipe, it defines a set of surroundings. The input is a option to assign +a recipe for an action. The filters is the option to filter recipes depending +on the situation in use. The local option is utilized to tune the behavior for +each recipe. In addition to them, several global options are employed to +control fundamental behavior of the operators. + +As a first step, define your list of recipes. +> + let g:sandwich#recipes = [] +< +Or just copy the default one if you need. +> + let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +< + +Requisite~ +There are two kinds of requisite, buns and external. All recipes should have +any one of the two. + +buns + This is the key to assign the surroundings. Its value is a list + including two strings. These are used for additions in add/replace + operators, and are searched as deletions in delete/replace operators. + If "regex" option is true, it is regarded as regular expression. If a + recipe do not have "input" option, this is used as the assignment + input. +> + let g:sandwich#recipes += [ + \ {'buns': ['(', ')']} + \ ] + + " Press saiw( or saiw) + " foo ---> (foo) + + let g:sandwich#recipes += [ + \ {'buns': ['ab', 'cd']} + \ ] + + " Press saiwab or saiwcd + " foo ---> abfoocd + + let g:sandwich#recipes += [ + \ {'buns': ['ab', 'cd'], 'input': ['a']} + \ ] + + " Press saiwa + " foo ---> abfoocd +< + + + +external + This is a supplementary requisite. This is a list including two + textobjects as strings. This is used only for determining deletions in + delete/replace operators. In other words, they can not be a candidate + of additions. The deletions are determined as the differences of the + textobjects. A narrower textobject is the first item and wider + textobject is the second item. Many local options are not valid. +> + let g:sandwich#recipes += [ + \ {'external': ['it', 'at']} + \ ] + + " "it" selects the text inside tags, "at" selects including tags. + " Therefore, the deletions are the both tags. + " <---it---> + " title here + " <----------at-----------> + + " Press (operator-sandwich-delete)at + " title here ---> title here +< + "noremap" option is applied for this. Since visual selection is + employed to check its region intrinsically, mappings in visual mode + are related to the action. +> + let g:sandwich#recipes += [ + \ {'external': ['i[', 'a['], 'noremap': 0} + \ ] + + " Press (operator-sandwich-delete)a[ + " [foo] ---> foo + + xnoremap i[ i{ + xnoremap a[ a{ + + " Press (operator-sandwich-delete)a{ + " {foo} ---> foo +< + Combined with "noremap" option, user defined textobjects can be used + in the same way. +> + " "noremap" option should be false. + let g:sandwich#recipes += [ + \ { + \ 'external': ["\(textobj-sandwich-auto-i)", + \ "\(textobj-sandwich-auto-a)"], + \ 'noremap': 0, + \ } + \ ] +< + NOTE: Registered textobjects should work correctly in visual mode. + NOTE: Not all the user defined textobjects are guaranteed to work. + + + +Input~ + +input + This is the key to assign a recipe for an action. The operators ask + user to determine addition in an action. At that moment, users are + asked an input to assign a recipe. This option makes the input. If a + recipe does not have the key, items in "buns" are used for the + assignment. +> + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""']} + \ ] + " Press saiw""" + " foo ---> """foo""" +< + If the recipe has input key, it will be used alternatively. +> + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'input': ['"']} + \ ] + " Press saiw" + " foo ---> """foo""" +< + This value should be a |list|, and multiple assignment is valid. + + + +Filter~ + +filetype + This filter filters recipes by filetypes in use. It is a list of + filetypes as strings. If a recipe does not have filetype key or has a + value "all", the recipe is valid on any filetype. +> + " The following recipes are valid on any filetype. + let g:sandwich#recipes += [ + \ {'buns': ['(', ')']} + \ {'buns': ['[', ']'], 'filetype': ['all']} + \ ] + + " The textobj "it" and "at" is not versatile and might be heavy on + " large files, thus it would be better to activate only on specific + " filetypes. + let g:sandwich#recipes += [ + \ {'external': ['it', 'at'], 'filetype': ['html']} + \ ] +< + + + +kind + This filter filters recipes by kinds of operator actions. It is a list + of names of kinds. "add", "delete", "replace", "operator" and "all" + can be used. "operator" is same as that "add", "delete" and "replace" + is specified. The difference between "operator" and "all" is that + "all" might include textobject kind in |g:sandwich#recipes|. See + |textobj-sandwich-configuration|. If a recipe does not have kind key, + the recipe is valid on any kind. +> + " The following recipe is valid only with the add operator. + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'kind': ['add']} + \ ] +< + + + +motionwise + This filter filters recipes by motionwise (visualmode in visual mode). + It is a list of motionwises. "char", "line", "block" and "all" can be + used. See |characterwise|, |linewise|, |characterwise-visual|, + |linewise-visual|, |blockwise-visual|. If a recipe does not have + motionwise key, the recipe is valid on any motionwise. +> + " The following recipe is valid only in linewise motion or linewise + " visual. + let g:sandwich#recipes += [ + \ {'buns': ['```', '```'], 'motionwise': ['line']} + \ ] +< + + + +mode + This filter filters recipes by modes. It is a list of characters + representing modes. "n" or "x" can be used. "n" represents a use in + normal mode, and "x" represents a use in visual mode. If a recipe does + not have mode key, the recipe is valid on any mode. +> + " These recipes are switch behaviors on modes with the same input. + let g:sandwich#recipes += [ + \ {'buns': ['"', '"'], 'mode': ['n']} + \ {'buns': ['"""', '"""'], 'mode': ['x'], 'input': ['"']} + \ ] +< + + + +action + This filter filters recipes by kinds of action. In principle, all the + operator's action could be split into two kinds, "add" and "delete". + The add operator has "add" action and the delete operator has "delete" + action. The replace operator has both "delete" action phase and "add" + action phase to replace strings. This filter limits the effectiveness + in each action. Available item is "add", "delete" and "all". If a + recipe does not have action key, the recipe is valid in both actions. + Note that "add" prohibits to take effect for |textobj-sandwich| while + "delete" and "all" allows it, because usually a delete action uses + those textobjects internally. + In short, a recipe with "add" affects add/replace operators while + "delete" affects delete/replace operators and textobjects. +> + " The recipe is valid only for add actions + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'action': ['add'], 'input': ['"']} + \ ] + + " The recipe is valid for delete actions and textobjects + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'action': ['delete'], 'input': ['"']} + \ ] +< + + + +expr_filter + User can define filters by oneself. The items of the list are + evaluated as |expression|, and if the value is true (1) then the + recipe is valid, if the value is false (0) then the recipe is invalid. +> + " A filter should be defined in somewhere, for example in your vimrc. + function! FilterValid() + return 1 + endfunction + + function! FilterInvalid() + return 0 + endfunction + + " This recipe is valid + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'expr_filter': ['FilterValid()']} + \ ] + + " This recipe is invalid + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'expr_filter': ['FilterInvalid()']} + \ ] +< + + + +Local option~ + +Local options are used to optimize the behavior for each recipe. If any +option is set, the default value depending on kinds and motionwises is used. +These default values are changed by |g:operator#sandwich#options|. + +If you want to change the default value of highlight option of add action in +character-wise motion: +> + let g:operator#sandwich#options.add.char.highlight = 0 +< +Or use the function |operator#sandwich#set()|. +> + call operator#sandwich#set('add', 'char', 'highlight', 0) +< + + + +g:operator#sandwich#options *g:operator#sandwich#options* + The dictionary includes default local option values. +> + let operator#sandwich#options[kind][motionwise][option] = {value} + + " For example + let g:operator#sandwich#options['add']['char']['highlight'] = 0 + " or + let g:operator#sandwich#options.add.char.highlight = 0 +< + *b:operator_sandwich_options* + If |b:operator_sandwich_options| exists, it will be used instead of + |g:operator#sandwich#options|. It would be useful when a user wants to + use buffer-local option settings. + + Available keys are listed below. + * kind + - add + - delete + - replace + * motionwise + - char + - line + - block + * option + - cursor (for all kinds) + - highlight (for all kinds) + - skip_space (for all kinds) + - noremap (for all kinds) + - linewise (for all kinds) + - command (for all kinds) + - query_once (for add and replace) + - expr (for add and replace) + - listexpr (for add and replace) + - autoindent (for add and replace) + - indentkeys (for add and replace) + - indentkeys- (for add and replace) + - indentkeys+ (for add and replace) + - regex (for delete and replace) + - skip_char (for delete and replace) + + + + *operator#sandwich#set()* +operator#sandwich#set(kind, motionwise, option, value) + The function to change default values of local options easily and + safely. If the combination of arguments is not appropriate, this + function shows error messages. The available arguments are listed in + |g:operator#sandwich#options|. In addition to that, "all" is available + for kind and motionwise. + + + + *operator#sandwich#setlocal()* +operator#sandwich#setlocal(kind, motionwise, option, value) + The function to change buffer local default values of local options + easily and safely. If the combination of arguments is not appropriate, + this function shows error messages. The available arguments are listed + in |g:operator#sandwich#options|. In addition to that, "all" is + available for kind and motionwise. + + + +operator#sandwich#set_default() *operator#sandwich#set_default()* + The function initializes all default values of local options. + + + +cursor + This is a local option which determines the cursor position after + an action. "inner_head", "keep", "inner_tail", "head" and "tail" can + be used. + inner_head: the head of a surrounded text + keep : the same position at start + inner_tail: the tail of a surrounded text + head : the head of the former surrounding + tail : the tail of the latter surrounding + headend : the end of the former surrounding + tailstart : the start of the latter surrounding + default : almost same as inner_head but bring cursor to the first + non-space character if in the head of line +> + # : cursor + foo ---> (foo) + + # : inner_head + (foo) + # : inner_tail + + # : keep + (foo) + + # : head + (foo) + # : tail +< + NOTE: Unfortunately, "keep" option cannot be used in |.| command + in default. In that case automatically falls back to + "inner_head" option. If needed, use a supplementary keymapping, + |(operator-sandwich-predot)| or + |(operator-sandwich-dot)|. + + Default values + * add + - char : "default" + - line : "default" + - block: "default" + * delete + - char : "default" + - line : "default" + - block: "default" + * replace + - char : "default" + - line : "default" + - block: "default" + + + +highlight + This option switches the highlight feature. If the value is 1, a + simple highlighting is used. If the value is 2, a rich highlighting + with multi-colors is used. In Addition, if the value is 3, highlight + the added texts in a short time. Set it 0 to turn off highlighting. + + The highlight for delete operator or in case of using the rich + highlighting, highlighted regions would be left in a short time, + specified in |g:operator#sandwich#highlight_duration|, after an + operation. The user input is not blocked also in this period. If + user presses any key in this period, the operator immediately quits + the highlighting. + + The highlight color can be changed. + See |operator-sandwich-highlight-group|. + + NOTE: This option can not be controlled by recipes. Use + |operator#sandwich#set()| function to set. +> + call operator#sandwich#set('all', 'all', 'highlight', 1) +< + Default values + * add + - char : 3 + - line : 3 + - block: 3 + * delete + - char : 3 + - line : 3 + - block: 3 + * replace + - char : 3 + - line : 3 + - block: 3 + + + +hi_duration +g:operator#sandwich#highlight_duration + This option determines the time duration to show deletions in an + operator action. Give number in milli seconds. + The default value is 200. + + NOTE: This option can not be controlled by recipes. Use + |operator#sandwich#set()| function to set. +> + call operator#sandwich#set('all', 'all', 'hi_duration', 300) +< + Default values + * add + - char : 200 + - line : 200 + - block: 200 + * delete + - char : 200 + - line : 200 + - block: 200 + * replace + - char : 200 + - line : 200 + - block: 200 + + + +skip_space + This option enables to skip spaces at both ends of a assigned region. + If the value is 1, adjust the both ends to skip white spaces. +> + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'skip_space': 0} + \ ] + " Press sa5l( when the cursor is on the first space. + " ?foo? ---> ( foo ) : ? is space + + let g:sandwich#recipes = [] + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'skip_space': 1} + \ ] + " ?foo? ---> ?(foo)? : ? is space +< + However as for the case of delete/replace operators if both ends are + white spaces then it fills up the working condition for operators and + white spaces would be deleted/replaced. Thus this option just works + when either one end of the assigned region is white space. +> + let g:sandwich#recipes += [ + \ {'buns': ['"', '"'], 'skip_space': 1} + \ ] + " Press sda" + " ?"foo" ---> ?foo : ? is space +< + If the value is 2, adjust the both ends to skip white spaces even + though the both ends are white spaces. + + Default values + * add + - char : 0 + - line : 1 + - block: 1 + * delete + - char : 2 + - line : 2 + - block: 2 + * replace + - char : 2 + - line : 2 + - block: 2 + + + +noremap + This option switches the behavior when adding buns (surroundings) to + the both ends whether the key presses are remapped to the nested + mappings. If the value is true then it would not be remapped. +> + inoremap ( [ + inoremap ) ] + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'noremap': 1} + \ ] + + " Press saiw( + " foo ---> (foo) + + let g:sandwich#recipes = [] + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'noremap': 0} + \ ] + + " Press saiw( + " foo ---> [foo] +< + This option is also referred from external textobjects in searching + deletions. See the description of "external" in requisite. + + Default values + * add + - char : 1 + - line : 1 + - block: 1 + * delete + - char : 1 + - line : 1 + - block: 1 + * replace + - char : 1 + - line : 1 + - block: 1 + + + +linewise + This option change the way to add/delete surroundings. The usual + behavior is like character-wise. +> + call operator#sandwich#set('add', 'char', 'linewise', 0) + " Press saiw( + " foo ---> (foo) +< + If this option is 1, add line-wise. +> + call operator#sandwich#set('add', 'char', 'linewise', 1) + " Press saiw( + " foo ---> ( + foo + ) +< + For the case of deleting, if there are nothing other than white spaces + and breaking after deleting, delete the lines. +> + call operator#sandwich#set('delete', 'char', 'linewise', 0) + " Press (operator-sandwich-delete)a( + " ( : this line was remained + " foo ---> foo : this line was remained + " ) : this line was remained + + call operator#sandwich#set('delete', 'char', 'linewise', 1) + " Press (operator-sandwich-delete)a( + " ( ---> foo + " foo + " ) +< + In the case that something other than white spaces are left, keep the + lines. +> + call operator#sandwich#set('delete', 'char', 'linewise', 1) + " Press (operator-sandwich-delete)a( + " (foo foo + " bar ---> bar + " baz) baz +< + If this option is 2, delete lines forcibly. +> + call operator#sandwich#set('delete', 'char', 'linewise', 2) + " Press (operator-sandwich-delete)a( + " (foo ---> bar + " bar + " baz) +< + For the case of replace operator, it behaves in the combination of + additions and deletions. + + NOTE: In |cmdline-window|, this option is ignored and behave as if it + was 0. + + Default values + * add + - char : 0 + - line : 1 + - block: 0 + * delete + - char : 0 + - line : 1 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +command + This is a list of commands which is executed after an action. After an + action, the marks |'[| and |']| would be set at the head and the tail + of the modified region in each action. +> + let g:sandwich#recipes += [ + \ { + \ 'buns' : ['{', '}'], + \ 'linewise': 1, + \ 'command' : ["'[+1,']-1normal! >>"] + \ }, + \ ] + " Press saiw{ + " foo ---> { + foo + } +< + Default values + * add + - char : [] + - line : [] + - block: [] + * delete + - char : [] + - line : [] + - block: [] + * replace + - char : [] + - line : [] + - block: [] + + + +query_once + This option switches the behavior when a [count] is given. If this + option is false, add/replace operators ask additions [count] times. + Whereas if this option is true, add/replace operators ask only once + and add it [count] times. + + Default values + * add + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +expr + If this option is 1 or 2, requisite "buns" is evaluated as expression. + If this option is 1, "buns" are evaluated once and repeat it by |.| + command. If this option is 2, "buns" are evaluated every times in |.| + repeating. For example, the following recipe adds the text in the + unnamed register. + (ref. |:let-@|, |quotequote|) +> + let g:sandwich#recipes += [ + \ {'buns': ['@@', '@@'], 'expr': 1, 'input': ['@']} + \ ] +< + NOTE: If this option is true, the recipe requires "input" to call by + key inputs. "buns" never be regarded as key inputs to trigger. + + NOTE: If the expressions query user something by |getchar()| or + |input()|, the recipe may cause problems on the + |operator-sandwich| delete operator and |textobj-sandwich| + textobjects, |(textobj-sandwich-auto-i)| and + |(textobj-sandwich-auto-a)|. To avoid this problem, it is + reccomended to set "kind" and "action" filters. For example the + following recipe query to get a tag name and surround by html + style tags. + + NOTE: Different from |textobj-sandwich|, |operator-sandwich| allows + empty string as bun. If you want to cancel the operation, + |:throw| an exception 'OperatorSandwichCancel'. +> + let g:operator#sandwich#recipes += [ + \ { + \ 'buns' : ['TagInput(1)', 'TagInput(0)'], + \ 'expr' : 1, + \ 'filetype': ['html'], + \ 'action' : ['add'], + \ 'input' : ['t'], + \ }, + \ ] + + function! TagInput(is_head) abort + if a:is_head + let s:TagLast = input('Tag: ') + if s:TagLast !=# '' + let tag = printf('<%s>', s:TagLast) + else + throw 'OperatorSandwichCancel' + endif + else + let tag = printf('', + \ matchstr(s:TagLast, '^\a[^[:blank:]>/]*')) + endif + return tag + endfunction +< + See also the next "listexpr" option. In case of html tags, it is + easier to write using "listexpr" option. + + Default values + * add + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +listexpr + This option is similar as the above "expr" option, but give a + expression string as buns. As far as user can guarantee that the + expression is evaluated to a list including two string, this option is + available. This is useful when former and latter surroundings shared a + part of string, like html tags. +> + let g:operator#sandwich#recipes += [ + \ { + \ 'buns' : 'HTMLTagInput()', + \ 'listexpr': 1, + \ 'filetype': ['html'], + \ 'action' : ['add'], + \ 'input' : ['t'], + \ }, + \ ] + + function! HTMLTagInput() abort + let tagstring = input('Tag: ') + if tagstring ==# '' + throw 'OperatorSandwichCancel' + endif + let former = printf('<%s>', tagstring) + let latter = printf('', + \ matchstr(tagstring, '^\a[^[:blank:]>/]*')) + return [former, latter] + endfunction +< + Default values + * add + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +autoindent + This option is used in order to control vim autoindent feature in an + operator action. The value is an integer and the numbers from 1 to 3 + correspond to vim built-in options 'autoindent', 'smartindent' and + 'cindent' respectively. If the value is 0, then never use autoindent + function. If the value is negative integer, then it does not change + any setting, just use the current autoindent setting. In case you + want to use 'indentexpr' option, the value would be a negative + integer. + + * -n : Current settings (if it is not empty, then 'indentexpr') + * 0 : 'noautoindent', 'nosmartindent', 'nocindent', 'indentexpr'='' + * 1 : 'autoindent' + * 2 : 'smartindent' + * 3 : 'cindent' + * 4 : Keep the indent level anyway +> + let g:sandwich#recipes += [ + \ {'buns': ['{', '}'], 'motionwise': ['line'], 'autoindent': 3}, + \ ] +< + Default values + * add + - char : -1 + - line : -1 + - block: -1 + * replace + - char : -1 + - line : -1 + - block: -1 + + + +indentkeys +indentkeys+ +indentkeys- + These options are used in order to change 'indentkeys' or 'cinkeys' + option temporary in an action. The format is identical to 'cinkeys'. + The value of "indentkeys" is set as written directly, as if the string + was assigned to the {value} of |:set| indentkeys={value}. Similar to + that, "indentkeys+" and "indentkeys-" is set as if they are assigned + to the {value} of |:set| indentkeys+={value} or + |:set| indentkeys-={value}. See |:set+=| and |:set-=|. One thing there + is a difference from |:set-=| is that user does not need to mind about + the order of items. |:set-=| depends on the order of items. +> + :set indentkeys? " -> 0{,0},:,0#,!^F,o,O,e + :set indentkeys-=0},0{ + :set indentkeys? " -> 0{,0},:,0#,!^F,o,O,e + + " '0{' and '0}' are not removed, + " because there is not the strings '0},0{' + + :set indentkeys-=0} + :set indentkeys? " -> 0{,:,0#,!^F,o,O,e + :set indentkeys-=0{ + :set indentkeys? " -> :,0#,!^F,o,O,e +< + In contrast to that, user does not need to care about the order of + items for the local option "indentkeys-". Simply list the items to be + removed. +> + let g:sandwich#recipes += [ + \ {'buns': ['[', ']'], 'filetype': ['tex'], 'indentkeys-': '[,]'} + \ ] + + " This is same as the above + "let g:sandwich#recipes += [ + " \ {'buns': ['[', ']'], 'filetype': ['tex'], 'indentkeys-': '],['} + " \ ] +< + If 'indentexpr' is empty and 'cindent' is valid, these options are + applied to 'cinkeys'. + + The applying order of these options is "indentkeys" -> "indentkeys+" + -> "indentkeys-". If the value is not a string, like 0, it does not + change 'indentkeys' or 'cinkeys'. + + Default values + * add + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +regex + If this option is true, requisite "buns" is regarded as regular + expressions. If this option is true, the recipe is not used for + adding steps in add/replace operators, is only used for deleting steps + in delete/replace operators. +> + let g:sandwich#recipes += [ + \ {'buns': ['\d\+', '\d\+'], 'regex': 1} + \ ] + " Press (operator-sandwich-delete)iw + " 123foo456 ---> foo +< + Default values + * delete + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +skip_char + If this option is true, both ends are shrunk to adjust until finding + registered surroundings. +> + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'skip_char': 1} + \ ] + " Press V(operator-sandwich-delete) + " foo(bar)baz ---> foobarbaz +< + Default values + * delete + - char : 0 + - line : 0 + - block: 0 + * replace + - char : 0 + - line : 0 + - block: 0 + + + +Global option~ + +There are several options to control fundamental behavior of the operators. + +g:operator#sandwich#timeout *g:operator#sandwich#timeout* + If this option is a falsy value, the operators will wait for + subsequent inputs until the complete key sequence has been received to + specify a recipe. For example, with the following recipes, +> + let g:sandwich#recipes = [ + \ {'buns': ['for {', '}'], 'nesting': 1, 'input': ['bf']} + \ {'buns': ['if {', '}'], 'nesting': 1, 'input': ['bi']} + \ {'buns': ['else {', '}'], 'nesting': 1, 'input': ['be']} + \ ] +< + type `saiwb` and a while later the operator eagerly wrap a word with + `b` if this option is true. The operators wait next input until a + recipe is specified if this option is false. If this has not been + defined, |g:sandwich#timeout| is used instead. Unless there is any + particular reason, use |g:sandwich#timeout|. + See |g:operator#sandwich#timeoutlen| also. + + + +g:operator#sandwich#timeoutlen *g:operator#sandwich#timeoutlen* + The time in milli seconds that waits for a key code or mapped key + sequence to complete. If there are recipes overlapped, this option is + used. Assume that the following recipes are prepared: +> + let g:sandwich#recipes = [ + \ {'buns': ['(', ')']} + \ {'buns': ['((', '))']} + \ ] +< + after pressing saiw(, the operator waits in the time. If you press one + more ( in the time, then a recipe for '((' and '))' is decided to use. + No keypress has come through the time a recipe for '(' and ')' is + settled. If this has not been defined, |g:sandwich#timeoutlen| is used + instead. Unless there is any particular reason, use + |g:sandwich#timeoutlen|. + + When the timeout option (|g:operator#sandwich#timeout|, + |g:sandwich#timeout|, 'timeout') is off, this option is ignored. + + + + *g:operator#sandwich#highlight_duration* +g:operator#sandwich#highlight_duration + This option determines the time duration to show deletions in an + operator action. Give number in milli seconds. + The default value is 200. + + NOTE: THIS OPTION BECOME OBSOLETE SOON! Use a local option + "hi_duration" instead. + + + + *g:operator#sandwich#persistent_highlight* +g:operator#sandwich#persistent_highlight + This option determines the method of highlight after an add/replace + operator action. In both cases, the highlight will be kept in a + certain time, which is determined by + |g:operator#sandwich#highlight_duration|. If this option is "blink", + the highlight will be quenched immediately by user input. If this + option is "glow", the highlight will be left until user edits a + buffer. The default value is "glow". + + NOTE: The highlight for delete operator is fixed with "blink" + behavior. + + NOTE: If |g:operator#sandwich#highlight_duration| is small, the two + options would look like same. + + + +============================================================================== +HIGHLIGHT GROUP *operator-sandwich-highlight-group* + +All the three operators have highlighting feature. On and off is controlled by +the local option "highlight", but the coloring is defined by its highlight +group. + +OperatorSandwichBuns *hl-OperatorSandwichBuns* + The highlight group to declare the highlight feature of the operators. + This group is used when "highlight" option is 1. + It is linked to a default highlight group IncSearch |hl-IncSearch| in + default. + + +OperatorSandwichChange *hl-OperatorSandwichChange* + The highlight group to declare the highlight feature of the operators. + This group is used for a text which is surrounded by add operator when + "highlight" option is 2 or larger. + It is linked to a default highlight group DiffChange |hl-DiffChange| + in default. + + +OperatorSandwichDelete *hl-OperatorSandwichDelete* + The highlight group to declare the highlight feature of the operators. + This group is used for deleted surrounding texts of delete/replace + operator when "highlight" option is 2 or larger. + It is linked to a default highlight group DiffDelete |hl-DiffDelete| + in default. + + +OperatorSandwichAdd *hl-OperatorSandwichAdd* + The highlight group to declare the highlight feature of the operators. + This group is used for added surrounding texts of add/replace operator + when "highlight" option is 3 or larger. + It is linked to a default highlight group DiffAdd |hl-DiffAdd| in + default. + + + Users can change these colorings as following. +> + " Example 1 + highlight link OperatorSandwichBuns Visual + + " Example 2 + highlight OperatorSandwichBuns ctermfg=White ctermbg=Red + \ guifg=White guibg=Red +< + + + +============================================================================== +AUTOCOMMAND *operator-sandwich-autocommands* + +Users can hook some processes for the start and the end of the operator action +by |:autocmd| command. Each operator defines its own |User| autocommand +events, users just declare autocommands if needed. + +add operator~ +OperatorSandwichAddPre *OperatorSandwichAddPre* + The autocommand event ignited right before the add operator action. + +OperatorSandwichAddPost *OperatorSandwichAddPost* + The autocommand event ignited right after the add operator action. + +delete operator~ +OperatorSandwichDeletePre *OperatorSandwichDeletePre* + The autocommand event ignited right before the delete operator action. + +OperatorSandwichDeletePost *OperatorSandwichDeletePost* + The autocommand event ignited right after the delete operator action. + +replace operator~ +OperatorSandwichReplacePre *OperatorSandwichReplacePre* + The autocommand event ignited right before the replace operator + action. + +OperatorSandwichReplacePost *OperatorSandwichReplacePost* + The autocommand event ignited right after the replace operator action. + +Example~ +The following example disables matchit.vim (default plugin) highlighting in +add/replace operators action. +> + autocmd User OperatorSandwichAddPre,OperatorSandwichReplacePre NoMatchParen + autocmd User OperatorSandwichAddPost,OperatorSandwichReplacePost DoMatchParen +< + + + +============================================================================== +API *operator-sandwich-api* + +|operator-sandwich| provides some functions as Application Programming +Interface. The functions would be helpful to make your "expr" buns more +convenient. + +operator#sandwich#get_info({info}) *operator#sandwich#get_info()* + This function returns information of the working operator. Available + {info}s are listed below. + `state`: 1 if in |.| repeating, otherwise 0. + `kind` : The kind of working operator, + "add", "delete" or "replace". + `count`: The given [count] for the operator. + `mode` : A character represents the mode working in, + "n" for normal mode, "x" for visual mode. + `motionwise`: + The type of target region. "char", "line", "block" or an + empty string "". + +operator#sandwich#kind() *operator#sandwich#kind()* + If in |operator-pending-mode|, this function returns the name of a + |operator-sandwich| operator which is set currently, that is, "add", + "delete" or "replace". Otherwise returns empty string, it is same in + case that is set another operators also. This function might be + helpful in cooperation with |textobj-sandwich|. + + *operator#sandwich#show()* +operator#sandwich#show([{place}[, {group}[, {forcibly}]]]) + This function highlight texts. The kind of highlighted text is + assigned by {place}, and {place} should be: + `target`: the text to be replaced in replace operators. + `stuff` : the text to be surrounded in add operators. + `added` : the added text in add/replace operators. + If {place} is not given, `stuff` is employed for add operator and + `target` for delete/replace operator. + If {forcibly} is true (1), this function ignore the "highlight" option + and highlight forcibly. + + If user gives {group}, the text will be highlighted with {group} which + is defined by |:highlight| command. Default highlight groups are + listed in |highlight-groups|. When |operator-sandwich| has highlighted + the text already, this function updates highlight if {group} is + different. Otherwise finish immediately. If it successfully adds or + updates highlighting, it returns 0. Otherwise returns 1. + + + *operator#sandwich#quench()* +operator#sandwich#quench([{place}]) + This function deletes highlight added by |operator#sandwich#show()|. + Give same {place} with |operator#sandwich#show()| to delete. If + {place} is not given, `stuff` is employed for add operator and + `target` for delete/replace operator. If it successfully deletes + highlighting, it returns 0. Otherwise returns 1. + + +For example, a query1st series keymapping +|(operator-sandwich-add-query1st)| does not highlight a text to be +surrounded. However if "expr" buns request user input, it would looks better +to highlight the surrounded text. The following example is to define a new +keymapping to surround text by a function, like func(), depending on an user +input of function name. +> + nmap sf (operator-sandwich-add-query1st) + xmap sf (operator-sandwich-add) + + let g:operator#sandwich#recipes += [ + \ { + \ 'buns': ['FuncName()', '")"'], + \ 'kind': ['add'], + \ 'action': ['add'], + \ 'expr': 1, + \ 'cursor': 'inner_tail', + \ 'input': ["\"] + \ }, + \ ] + + function! FuncName() abort + call operator#sandwich#show('stuff') + let funcname = input('funcname: ') + call operator#sandwich#quench('stuff') + if funcname ==# '' + throw 'OperatorSandwichCancel' + endif + return funcname . '(' + endfunction +< +This new operator keymapping `sf` makes "foo" to "func(foo)" by a key sequence +`sfiwfunc`. + + + +============================================================================== +MISCELLANEOUS *operator-sandwich-miscellaneous* + +Highlighting in the delete operator is really irritating.~ + Yes, you are right. Add the line to your vimrc. +> + call operator#sandwich#set('delete', 'all', 'highlight', 0) +< + + + +Highlighting is always irritating.~ + Ah, you might be right. Add the line to your vimrc. +> + call operator#sandwich#set('all', 'all', 'highlight', 0) +< + + + +Highlighting by matchit.vim is confusing.~ + Use |:autocmd| to disable it temporary. + See |operator-sandwich-autocommands|. +> + autocmd User OperatorSandwichAddPre,OperatorSandwichReplacePre NoMatchParen + autocmd User OperatorSandwichAddPost,OperatorSandwichReplacePost DoMatchParen +< + Or just use a clearly different coloring depending on your favorite + colorscheme. See |operator-sandwich-highlight-group|. +> + highlight OperatorSandwichBuns ctermfg=White ctermbg=Red + \ guifg=White guibg=Red +< + + + +Why does not the add operator skip white spaces in character-wise motion?~ + Because it sometimes causes unwanted results when I use visual mode + selection. That is, I sometimes want to surround a text including + space. If you don't like it, add the line to your vimrc. +> + call operator#sandwich#set('add', 'char', 'skip_space', 1) +< + + + +Why the default mappings for the delete/replace operators were not defined?~ + Because of their characteristics, it is better to use always with + |textobj-sandwich|. Thus, compound mappings are defined alternatively. + See |sandwich-keymappings|. In visual mode, you can use as same as + other operators. + However, of course, these operators |(operator-sandwich-delete)| + and |(operator-sandwich-replace)| can work with any + motions/textobjects as far as matching their working condition. If you + are interested, please map them somewhere you want and try! + + + +I want to keep cursor position even after dot repeating.~ + The item "keep" of the local option "cursor" is not valid with dot + repeating in default. However, if necessary, using either + |(operator-sandwich-predot)| or |(operator-sandwich-dot)|, + it can enable the feature. If you are using other plugins which make + keymappings to |.| key, use |(operator-sandwich-predot)|. For + example, if you are using a plugin which defines a mapping + `(ExampleDot)`: +> + nmap . (operator-sandwich-predot)(ExampleDot) +< + In case of using repeat.vim (vimscript #2136), the mapping + `(RepeatDot)` is defined retadatively. Thus, in order to ensure + the mapping exists, add the lines: +> + runtime autoload/repeat.vim + nmap . (operator-sandwich-predot)(RepeatDot) +< + Otherwise use |(operator-sandwich-dot)|. +> + nmap . (operator-sandwich-dot) +< + See each detailed description of |(operator-sandwich-predot)| + and |(operator-sandwich-dot)|. + + + +What is special about the query1st series mappings?~ + There are two query1st series keymappings, + |(operator-sandwich-add-query1st)| and + |(operator-sandwich-replace-query1st)|. These are different from + usual |(operator-sandwich-add)| and + |(operator-sandwich-replace)| on the order of key sequences in + normal mode. As for the usual keymappings, the assignment of + {addition} is input at last. +> + {operator}{motion/textobject}{addition} +< + For instance, if user wants to surround a word by (), press `saiw(`. + In this case, `sa` is {operator}, `iw` is {textobject}, `(` is the + assignment of {addition}. + + As for the query1st series mappings, on the other hand, {addition} + comes ahead of {textobject}. +> + {operator}{addition}{motion/textobj} +< + That is, the equivalent key sequence of the above instance is `sa(iw`. + These keymappings would be useful to define solid shortened mappings. +> + nmap s( (operator-sandwich-add-query1st)( + nmap s[ (operator-sandwich-add-query1st)[ + nmap s{ (operator-sandwich-add-query1st){ + + " Press s(iw + " foo -> (foo) +< + Query1st series keymappings have a few limitation since determining + {addition} before {motion} or {textobject} which assigns the processed + region. + + 1. "motionwise" filter dropped recipes other than the recipes which is + valid for all kinds of region (char, line, block) regardless of the + actual assigned region. + 2. "highlight" local option is ignored. Never be highlighted. + 3. "query_once" local option is set to 1 because the above use case is + mainly assumed. However this could be overwritten by using + |operator#sandwich#query1st()| function to re-define keymapping. +> + nmap (operator-sandwich-add-query1st) + \ :call operator#sandwich#query1st('add', 'n', {'query_once': 0}) + + nmap ssa (operator-sandwich-add-query1st) +< + In visual mode, there is nothing different on the order of key + sequences. Rather there are a few limitations listed, thus it is + recommended to use usual key mappings. +> + xmap s( (operator-sandwich-add)( + xmap s[ (operator-sandwich-add)[ + xmap s{ (operator-sandwich-add){ +< + + +About the handlings of recipe-local options in the replace operator.~ + Two recipes are participated in an action of replace operator, a + recipe for delete action and a recipe for add action. If any option is + assigned in the recipes, it is determined which recipe is used + depending on the kind of option. + + `cursor` + Both recipes are valid, but the recipe for add action is prior. + + `query_once` + The recipe for add action is used. + + `regex` + The recipe for delete action is used. + + `expr` + The recipe for add action is used. + + `noremap` + The recipe for delete action is used for target region matching, and + the recipe for add action is used for adding surroundings. + + `skip_space` + The recipe for delete action is used. + + `skip_char` + The recipe for delete action is used. + + `command` + Both recipes are valid, the recipe for add action is executed later. + + `linewise` + The bigger value in the two is used. + + `autoindent` + The recipe for add action is used. + + `indentkeys` + The recipe for add action is used. + + `indentkeys+` + The recipe for add action is used. + + `indentkeys-` + The recipe for add action is used. + + + +About the visualrepeat.vim plugin support~ + This plugin has the support for visualrepeat.vim plugin support. User + has nothing to do for use of it other than installing visualrepeat.vim + (vimscript #3848) plugin. If the plugin has been successfully + installed, |.| command works also in |Visual-mode|. + + + +============================================================================== +vim:tw=78:ts=8:ft=help:norl:noet: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/doc/sandwich.jax b/config/neovim/store/lazy-plugins/vim-sandwich/doc/sandwich.jax new file mode 100644 index 00000000..d07c387d --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/doc/sandwich.jax @@ -0,0 +1,687 @@ +*sandwich.jax* 日本語ヘルプ Last change:14-May-2022. + +“挟まれた”テキストを編集するためのオペレータとテキストオブジェクトの詰め合わせ +です。 + +書いた人 : machakann +ライセンス : NYSL license + 日本語 + English (Unofficial) + +必須要件: Vim 7.4 かそれ以降のバージョン + |+reltime| 機能 (任意) + |+float| 機能 (任意) + +============================================================================== +CONTENTS *sandwich-contents* + +QUICK START |sandwich-quick-start| +INTRODUCTION |sandwich-introduction| +KEYMAPPINGS |sandwich-keymappings| +CONFIGURATION |sandwich-configuration| +MAGICCHARACTERS |sandwich-magiccharacters| +FILETYPE RECIPES |sandwich-filetype-recipes| +FUNCTIONS |sandwich-functions| +MISCELLANEOUS |sandwich-miscellaneous| + vim-surround のキ-マッピングを使う + magicchar-f の挙動を変更する + +============================================================================== +QUICK START *sandwich-quick-start* + +*sandwich.vim* は文字列を括弧などで囲む/囲みを外す/囲みを置き換えることを目的 +としたオペレータとテキストオブジェクトのセットです。例えば、(foo) や "bar" の +ような文字列が処理の対象になります。 + +囲む~ +sa{motion/textobject}{addition} と、キー入力します。 +例えば、 foo という単語にカーソルを合わせて saiw( と入力すると (foo) となりま +す。 + +囲みを外す~ +sdb あるいは sd{deletion} とキー入力します。 +例えば、 (foo) というテキストにカーソルを合わせて sdb あるいは sd( と入力する +と foo となります。 sdb という入力は自動的に囲まれたテキストを検索します。 + +囲みを置き換える~ +srb{addition} あるいは sr{deletion}{addition} キー入力します。 +例えば、 (foo) というテキストにカーソルを合わせて srb" あるいは sr(" と入力す +ると "foo" となります。 + +これだけ読めばこのプラグインを使うのに十分でしょう。もし、さらに興味がわいた方 +、挙動を細かく変更したい方は続く記述およびオペレータとテキストオブジェクトそれ +ぞれのヘルプ |operator-sandwich| 、 |textobj-sandwich| をご覧ください。 + + + +============================================================================== +INTRODUCTION *sandwich-introduction* + +このプラグインは文字列を括弧などで囲む/囲みを外す/囲みを置き換えるための機能を +提供します。オペレータ部分 |operator-sandwich| とテキストオブジェクト部分 +|textobj-sandwich| の二つの部分からなり、これらが共働することにより機能を実現 +します。また、同時にそれぞれが独立したオペレータ/テキストオブジェクトであるの +で、ほかのあらゆるオペレータ/テキストオブジェクトと組み合わせることもできます +。これらの機能は純粋にオペレータ及びテキストオブジェクトの枠組みを使って実装さ +れているので、いかなるライブラリにも依存することなく |.| コマンドによって繰り +返すことができます。 + +|sandwich.vim| の提供するキーマッピングおよびマッピング変更のための情報につい +ては|sandwich-keymappings|を参照してください。 + +|sandwich.vim|を構成する部品としての純粋なオペレータについての情報は +|operator-sandwich|のヘルプを参照してください。囲みを編集する機能をカスタマイ +ズするための情報もこちらのヘルプに詳細があります。 + +|sandwich.vim|を構成する部品としての純粋なテキストオブジェクトについての情報は +|textobj-sandwich| のヘルプを参照してください。テキストオブジェクトの挙動をカ +スタマイズするための情報もこちらのヘルプに詳細があります。 + + + +============================================================================== +KEYMAPPINGS *sandwich-keymappings* + +このプラグインは以下のキーマッピングを提供します。 + +機能 デフォルトキーマッピング +-------------------------------------------------------------------------- +囲む sa{motion/textobject}{addition} (ノーマル、ビジュアルモード) + -> |(sandwich-add)| + +囲みを外す + sd{deletion} (ノーマルモード) + sd (ビジュアルモード) + -> |(sandwich-delete)| + + sdb (ノーマルモード) + -> |(sandwich-delete-auto)| + +囲みを置き換える + sr{deletion}{addition} (ノーマルモード) + sr{addition} (ビジュアルモード) + -> |(sandwich-replace)| + + srb{addition} (ノーマルモード) + -> |(sandwich-replace-auto)| + +テキストオブジェクト + ib (オペレータ待機、ビジュアルモード) + -> |(textobj-sandwich-auto-i)| + ab (オペレータ待機、ビジュアルモード) + -> |(textobj-sandwich-auto-a)| + + is (オペレータ待機、ビジュアルモード) + -> |(textobj-sandwich-query-i)| + as (オペレータ待機、ビジュアルモード) + -> |(textobj-sandwich-query-a)| + +-------------------------------------------------------------------------- + +NOTE: 誤操作を防ぐため以下の設定を vimrc に追加することを強く推奨します。 +> + nmap s + xmap s +< +|s| コマンドは |c|l| コマンドによって代替できます。 + +デフォルトのマッピングがお気に召さなければ*g:sandwich_no_default_key_mappings* +をあなたの vimrc で定義しておいてください。 +> + let g:sandwich_no_default_key_mappings = 1 +< +以下のコードはキーマッピングの開始キーを s から z に変える例です。 +> + let g:sandwich_no_default_key_mappings = 1 + + " add + nmap za (sandwich-add) + xmap za (sandwich-add) + omap za (sandwich-add) + + " delete + nmap zd (sandwich-delete) + xmap zd (sandwich-delete) + nmap zdb (sandwich-delete-auto) + + " replace + nmap zr (sandwich-replace) + xmap zr (sandwich-replace) + nmap zrb (sandwich-replace-auto) +< +必要ならテキストオブジェクトもマッピングしましょう。 +> + " text-objects (if you need) + omap ib (textobj-sandwich-auto-i) + xmap ib (textobj-sandwich-auto-i) + omap ab (textobj-sandwich-auto-a) + xmap ab (textobj-sandwich-auto-a) + + omap is (textobj-sandwich-query-i) + xmap is (textobj-sandwich-query-i) + omap as (textobj-sandwich-query-a) + xmap as (textobj-sandwich-query-a) +< + + + *(sandwich-add)* + [count1] (sandwich-add) [count2] {motion} {addition} + 指定されたテキストを囲みます。 + このキーマッピングは[count]を独特なルールで扱います。 + [count1] は|(sandwich-add)|に渡され [count1] 回囲みます。他方で + [count2] は通常通り {motion} に渡されます。どちらの[count]も省略可能で + す。{addition}は囲みを指定するキーで、例えば saiw( と入力すると単語を + 丸括弧()で囲みます。 + +{Visual} [count] (sandwich-add) {addition} + [count]回ヴィジュアル選択した範囲を囲みます。 + + (sandwich-add)はノーマルモード、ヴィジュアルモード、オペレータ待 + 機モードにマップ可能です。デフォルトでは sa にマップされています。 +> + nmap sa (sandwich-add) + xmap sa (sandwich-add) + omap sa (sandwich-add) +< + + + *(sandwich-delete)* +[count] (sandwich-delete) {deletion} + カーソルから最も近い{deletion}に指定される囲みを外します。例えば、 + sd( と入力するとカーソルから最も近い対応する丸括弧()を削除します。 +> + (foo) -> foo +< + [count]を指定するとカーソルから[count]番目に近い囲みを削除します。 +> + (foo(bar)baz) cursor is on "bar" + + -- sd( --> (foobarbaz) + -- 2sd( --> foo(bar)baz +< + +{Visual} [count] (sandwich-delete) + ヴィジュアル選択範囲の両端にある連続した囲みを[count]回外します。 + + (sandwich-delete)はノーマルモード、ヴィジュアルモードにマップ可 + 能です。デフォルトでは sd にマップされています。 +> + nmap sd (sandwich-delete) + xmap sd (sandwich-delete) +< + + + *(sandwich-delete-auto)* +[count] (sandwich-delete-auto) + カーソルから最も近い囲みを自動的に検索して外します。 + [count]を指定するとカーソルから[count]番目に近い囲みを削除します。 +> + [foo(bar)baz] cursor is on "bar" + + -- sdb --> [foobarbaz] + -- 2sdb --> foo(bar)baz +< + (sandwich-delete-auto)はノーマルモードにマップ可能です。 + デフォルトでは sdb にマップされています。 +> + nmap sdb (sandwich-delete-auto) +< + + + *(sandwich-replace)* +[count] (sandwich-replace) {deletion} {addition} + カーソルから最も近い{deletion}に指定される囲みを{addition}に指定される + 囲みに置換します。例えば、sr([ と入力するとカーソルから最も近い対応す + る丸括弧()を角括弧[]に置換します。 +> + (foo) -> [foo] +< + [count]を指定するとカーソルから[count]番目に近い囲みを置換します。 +> + (foo(bar)baz) cursor is on "bar" + + -- sr([ --> (foo[bar]baz) + -- 2sr([ --> [foo(bar)baz] +< + +{Visual} [count] (sandwich-replace) {addition} + ヴィジュアル選択範囲の両端にある連続した囲みを[count]回置換します。 + + (sandwich-replace)はノーマルモード、ヴィジュアルモードにマップ可 + 能です。デフォルトでは sr にマップされています。 +> + nmap sr (sandwich-replace) + xmap sr (sandwich-replace) +< + + + *(sandwich-replace-auto)* +[count] (sandwich-replace-auto) {addition} + カーソルから最も近い囲みを自動的に検索して置換します。 + [count]を指定するとカーソルから[count]番目に近い囲みを削除します。 +> + [foo(bar)baz] cursor is on "bar" + + -- srb{ --> [foo{bar}baz] + -- 2srb{ --> {foo(bar)baz} +< + (sandwich-replace-auto)はノーマルモードにマップ可能です。 + デフォルトでは srb にマップされています。 +> + nmap srb (sandwich-replace-auto) +< + + +============================================================================== +CONFIGURATION *sandwich-configuration* + +括弧などのセットとその性質に依るオプションをまとめた情報をレシピ "recipe" と呼 +びます。一つ一つのレシピは |Dictionary| で、これらを集めた |list| がオペレータ +やテキストオブジェクトの動作を決めます。 |g:sandwich#default_recipes| はその一 +つで、 |operator-sandwich| と |textobj-sandwich| の両方から参照されます。多く +の場合、この情報は共有したほうが便利であるためです。 |g:sandwich#recipes| がユ +ーザーによって定義された場合こちらがかわりに参照されます。デフォルト設定の +|g:sandwich#default_recipes| は |:echo| コマンドによって確認できます。 +> + :echo g:sandwich#default_recipes +< + +上記に加え、 |g:operator#sandwich#recipes| と |g:textobj#sandwich#recipes| も +レシピを持つことができます。これらは |operator-sandwich| と |textobj-sandwich| +のそれぞれからしか参照されません。固有のレシピをおきたい場合に使いましょう。 + +レシピの細かい仕様については、オペレータ及びテキストオブジェクトのヘルプ、 +|operator-sandwich-configuration| 及び |textobj-sandwich-configuration| をご覧 +ください。 + + + +g:sandwich#recipes *g:sandwich#recipes* + |operator-sandwich| と |textobj-sandwich| の両方から参照されるレシピの + リストです。もし存在しなければ |g:sandwich#default_recipes| がかわりに + つかわれます。 + *b:sandwich_recipes* + |b:sandwich_recipes| が存在する場合は、 |g:sandwich#recipes| のかわり + にそちらが使われます。これはバッファについてローカルな値なので、ファイ + ルタイプ固有な設定が増えすぎた時に使うと便利かもしれません。 + + + +g:sandwich#default_recipes *g:sandwich#default_recipes* + デフォルトで用意されたレシピのリストです。 |g:sandwich#recipes| が存在 + すれば、そちらがかわりにつかわれます。 + + この変数は変更を禁止されていますが、 |g:sandwich#recipes| を宣言する際 + に必要ならコピーすることができます。 +> + :let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +< + +デフォルトレシピについて~ +`(`, `)` +`[`, `]` +`{`, `}` +`<`, `>` + 開き/閉じ括弧は同様に機能します。例えば、 `saiw(` と `saiw)` は同様の + 結果を与えます。 +> + foo -> (foo) +< + `sd(` と `sd)` はこの反対のことをします。 + +------------------------------------------------------------------------------ +`'` +`"` + クオーテーションで文字列を囲みます。 +> + foo -> 'foo' +< + クオーテーションによる囲みを消す場合 `quoteescape` オプションが考慮さ + れます。また、両端が同じ行の中にある必要があります。 + +------------------------------------------------------------------------------ +`` + スペース囲みを消す場合、連続するスペースを一度に消します。 + +------------------------------------------------------------------------------ +`t`, `T` +`f`, `F` +`i`, `I` + |sandwich-magiccharacters| をご覧ください。 + + + +------------------------------------------------------------------------------ +Global options~ +g:sandwich#timeout *g:sandwich#timeout* + この変数に偽値が設定されている場合は、オペレータやテキストオブジェクト + は一つのレシピを指定する完全な入力がなされるまで待ちます。例えば、下記 + のレシピを用意します。 +> + let g:sandwich#recipes = [ + \ {'buns': ['for {', '}'], 'nesting': 1, 'input': ['bf']} + \ {'buns': ['if {', '}'], 'nesting': 1, 'input': ['bi']} + \ {'buns': ['else {', '}'], 'nesting': 1, 'input': ['be']} + \ ] +< + このオプションが真の場合は `saiwb` とタイプして少し待つと、オペレータ + はタイムアウトのため単語を `b` で囲みます。しかし、偽の場合は一つのレ + シピを指定する入力が完成するまで待ちます。この変数はオペレータとテキス + トオブジェクトの両方に効果を及ぼします。|g:operator#sandwich#timeout| + や |g:textobj#sandwich#timeout| が存在する場合はそれらが優先的に使われ + ます。この変数が定義されていなければ 'timeout' オプションが代わりに参 + 照されます。 + 関連:|g:sandwich#timeoutlen| + + + +g:sandwich#timeoutlen *g:sandwich#timeoutlen* + 入力に前方一致で重複するレシピが存在する場合に次のキーシーケンスを待つ + 時間をミリ秒単位で指定します。 +> + let g:sandwich#recipes = [ + \ {'buns': ['(', ')']} + \ {'buns': ['((', '))']} + \ ] +< + saiw( とキー入力するとオペレータは次に ( が入力されるかこの時間だけ待 + ちます。この間にもう一度 ( を押下すると '((' と '))' のレシピが使われ + るでしょう。キーの押下なしでこの時間が過ぎると '(' と ')' のレシピが使 + われるでしょう。この変数はオペレータとテキストオブジェクトの両方に効果 + を及ぼします。|g:operator#sandwich#timeoutlen| や + |g:textobj#sandwich#timeout| が存在する場合はそれらが優先的に使われま + す。この変数が定義されていなければ 'timeoutlen' が代わりに参照されま + す。 + + タイムアウト機能(|g:operator#sandwich#timeout|, + |g:textobj#sandwich#timeout|, |g:sandwich#timeout|, 'timeout')がオフの + 場合はこのオプションは無視されます。 + + + +g:sandwich#input_fallback *g:sandwich#input_fallback* + このオプションは囲みを追加・削除・置換するためのユーザーの入力に合致す + るレシピが存在しない場合の挙動を制御します。このオプションが真のとき、 + ユーザーの入力に合致するレシピがなければ、入力された文字自体を追加・削 + 除・置換の対象とします。例えば、a という入力に合致するレシピが存在しな + くても saiwa と入力すると単語を a で囲みます。 +> + foo -> afooa +< + この動作が必要ない場合はこのオプションに偽値を設定します。 +> + let g:sandwich#input_fallback = 0 +< + + +============================================================================== +MAGICCHARACTERS *sandwich-magiccharacters* + +sandwich.vim は {addition}/{deletion} を決めるためにユーザーに入力を促します。 +これは例えば `(` が `()` のペアを、 `"` が `""` のペアを指しますが、より特殊な +用途のための入力がいくつか存在します。 + +f~ +F~ + 関数で囲みます。例えば `saiwf` と入力すると `f` キーの入力の後に関数名 + の入力を求められます。関数名を入力し キーを押すと、その関数名付き + の丸括弧でテキストオブジェクトを囲みます。関数名の入力中、 キー + で現在のバッファから関数名を補完できます。 +> + arg -- saiwffunc --> func(arg) +< + 逆に `sdf` は関数囲みを削除します。 +> + func(arg) -- sdf --> arg +< + 関数がネストしている場合、 `sdf` はカーソル下の関数囲みを削除し、 + `sdF` は外側の関数囲みを削除します。 +> + cursor is on 'func2': + func1(func2(arg)) -- sdf --> func1(arg) + -- sdF --> func2(arg) +< + +i~ +I~ + その時だけの (`I`nstant) 囲みを定義します。 `saiwi` はユーザーに前方囲 + みと後方囲みの入力を求めます。例えば、 `saiwifoobar` という + 入力はカーソル下の単語を `foo` と `bar` で囲みます。入力中は + キーにより現在のバッファから簡単な単語補完を使えます。反対に `sdi` は + 任意の入力された囲みを削除します。つまり、 `sdifoobar` は + `foowordbar` という文字列を `word` にします。これは |.| コマンドによっ + て繰り返すことができるので、編集対象の文字列が多く存在する場合に便利か + もしれません。 + `sa{textobj}I` や `sdI` は最後の入力を再利用します。 + + +t~ +T~ + `t` および `T` は HTML 様のタグの編集を支援します。 `saiwt` はユーザー + にタグの要素名の入力を促し、そのタグで囲みます。 `saiwT` も同様に機能 + します。 +> + word -- saiwtp -->

word

+< + `sdt` は直近のタグ囲みを削除します。 `sdT` も同様に機能します。 +> +

word

-- sdt --> word +< + `t` と `T` は囲みを置換するときに別のふるまいをします。 + `srtt` はタグの要素名のみを置き換え、属性等は変更しません。 + `srTT` はタグ全体を置き換えます。 + + + +============================================================================== +FILETYPE RECIPES *sandwich-filetype-recipes* + +Sandwich.vim にはファイルタイプに固有のレシピもあります。これらは 'filetype' +オプションが特定の値のときのみ読み込まれ、有効になります。またこれらはユーザー +設定を上書きしないので、必要なものだけ残して使うとよいでしょう。設定の本体は +ftplugin/{filetype}/sandwich.vim に記述されています。 + +もし、それらのファイルを読み込んでほしくない場合は +`g:sandwich_no_{filetype}_ftplugin` に真値を設定しておいてください。例えば、 +tex 固有のレシピが必要ない場合、次の一行を vimrc に加えます。 +> + let g:sandwich_no_tex_ftplugin = 1 +< + +------------------------------------------------------------------------------ +plaintex~ +tex~ +> + 囲み 入力 + `{text}' u' + “{text}” u" + „{text}“ U" + ug + u, + «{text}» u< + uf + `{text}' l' + l` + ``{text}'' l" + "`{text}"' L" + ,,{text}`` l, + <<{text}>> l< + \{{text}\} \{ + \[{text}\] \[ + + \left({text}\right) m( + \left[{text}\right] m[ + \left|{text}\right| m| + \left\{{text}\right\} m{ + \left\langle {text}\right\rangle m< +< 'm' 始まりの入力で挿入される囲みはすべて `ma` という入力で削除すること + ができます。例えば `sdma` のように使います。 +> + \big({text}\big) M( + \big[{text}\big] M[ + \big|{text}\big| M| + \big\{{text}\big\} M{ + \big\langle {text}\big\rangle M< + + \begingroup{text}\endgroup gr + \gr + \toprule{text}\bottomrule tr + br + \tr + \br + + \{input}{{text}} c +< このレシピはユーザーに入力を求め、入力されたテキストで {input} を置き + 換えます。 +> + \begin{{input}}{text}\end{{input}} e +< このレシピはユーザーに入力を求め、入力されたテキストで {input} を置き + 換えます。 キーにより {input} の入力を補完することができます。 + この時、補完候補は `g:sandwich#filetype#tex#environments` + (あるいは存在すれば `b:sandwich#filetype#tex#environments`) から読み込 + まれます。 + + +============================================================================== +FUNCTIONS *sandwich-functions* + +sandwich#util#addlocal({recipes}) *sandwich#util#addlocal()* + レシピのリスト {recipes} をそのバッファにのみ有効な設定として追加しま + す。この際、既存のグローバルな設定はそのまま受け継がれます。この関数は + グローバルな設定をきれいに保ったままファイルタイプに特有な設定を追加す + る、などの用途に使えます。{recipes} はレシピのリスト |List| である点は + 注意してください。 +> + autocmd FileType python call sandwich#util#addlocal([ + \ {'buns': ['"""', '"""'], 'nesting': 0, 'input': ['3"']}, + \ ]) +< + +============================================================================== +MISCELLANEOUS *sandwich-miscellaneous* + +vim-surround のキーマッピングを使う~ + vim-surround (vim script #1697) と同じキーマッピングが使いたければ次の + 行を vimrc に追加してください。 +> + runtime macros/sandwich/keymap/surround.vim +< + NOTE: surround.vim とは違い、 `(` と `)` の入力は同じように機能しま + す。もし、オリジナルの surround.vim のように `(` の入力により、 + 空白を含んだ括弧で囲みたい場合は次のように書きます。 +> + runtime macros/sandwich/keymap/surround.vim + let g:sandwich#recipes += [ + \ {'buns': ['{ ', ' }'], 'nesting': 1, 'match_syntax': 1, + \ 'kind': ['add', 'replace'], 'action': ['add'], 'input': ['{']}, + \ + \ {'buns': ['[ ', ' ]'], 'nesting': 1, 'match_syntax': 1, + \ 'kind': ['add', 'replace'], 'action': ['add'], 'input': ['[']}, + \ + \ {'buns': ['( ', ' )'], 'nesting': 1, 'match_syntax': 1, + \ 'kind': ['add', 'replace'], 'action': ['add'], 'input': ['(']}, + \ + \ {'buns': ['{\s*', '\s*}'], 'nesting': 1, 'regex': 1, + \ 'match_syntax': 1, 'kind': ['delete', 'replace', 'textobj'], + \ 'action': ['delete'], 'input': ['{']}, + \ + \ {'buns': ['\[\s*', '\s*\]'], 'nesting': 1, 'regex': 1, + \ 'match_syntax': 1, 'kind': ['delete', 'replace', 'textobj'], + \ 'action': ['delete'], 'input': ['[']}, + \ + \ {'buns': ['(\s*', '\s*)'], 'nesting': 1, 'regex': 1, + \ 'match_syntax': 1, 'kind': ['delete', 'replace', 'textobj'], + \ 'action': ['delete'], 'input': ['(']}, + \ ] +< + + + ノーマルモードにおける `ys`, `yss`, `yS`, `ds`, `cs` 及び、ビジュアル + モードにおける `S` が使用できます。また、 vim-surround にはありません + が `dss` と `css` も使用できます。これらは `ds` と `cs` に似ていますが + 削除/置換対象となる括弧やクオーテーションを自動的に選択します。 + + さらに vim-sandwich は便利なテキストオブジェクトが独立して使用できま + す。ぜひ試してみてください。 + + * ユーザーの入力に従い、括弧や同じ文字に囲まれた領域を選択 + |(textobj-sandwich-query-i)|, |(textobj-sandwich-query-a)| +> + xmap is (textobj-sandwich-query-i) + xmap as (textobj-sandwich-query-a) + omap is (textobj-sandwich-query-i) + omap as (textobj-sandwich-query-a) +< + * 最も近い括弧に囲まれた領域を選択 + |(textobj-sandwich-auto-i)|, |(textobj-sandwich-auto-a)| +> + xmap iss (textobj-sandwich-auto-i) + xmap ass (textobj-sandwich-auto-a) + omap iss (textobj-sandwich-auto-i) + omap ass (textobj-sandwich-auto-a) +< + * ユーザーの入力に従い、同じ文字に囲まれた領域を選択 + |(textobj-sandwich-literal-query-i)|, + |(textobj-sandwich-literal-query-a)| +> + xmap im (textobj-sandwich-literal-query-i) + xmap am (textobj-sandwich-literal-query-a) + omap im (textobj-sandwich-literal-query-i) + omap am (textobj-sandwich-literal-query-a) +< + +------------------------------------------------------------------------------ +magicchar-f の挙動を変更する~ + +`magicchar-f` は単純な関数呼び出しを削除できます。 +> + func(arg) -- sdf --> arg +< +もし、もっと複雑なパターンにも対応させたい場合、例えば、 +> + obj.method(arg) -- sdf --> arg +< +このような場合に対応させるには `g:sandwich#magicchar#f#patterns` か +`b:sandwich_magicchar_f_patterns` を使います。 これらは次のようなパターンのリ +ストです。 +> + let g:sandwich#magicchar#f#patterns = [ + \ { + \ 'header' : '\<\h\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] +< +四つの文字列はすべて正規表現パターンで、開括弧の前の文字列、開括弧、閉括弧、 +閉括弧の後に続く文字列にマッチします。すなわち、先に述べたようなパターンに対応 +するには以下のような設定をします。 +> + let g:sandwich#magicchar#f#patterns = [ + \ { + \ 'header' : '\<\%(\h\k*\.\)*\h\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] +< +`b:sandwich_magicchar_f_patterns` はファイルタイプ毎の設定を定義するのに使えま +す。 +> + augroup sandwich-ft-python + autocmd Filetype python let b:sandwich_magicchar_f_patterns = [ + \ { + \ 'header' : '\<\%(\h\k*\.\)*\h\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] + augroup END +< +デフォルトの設定は `g:sandwich#magicchar#f#default_patterns` にあります。 + +============================================================================== +vim:tw=78:ts=8:ft=help:norl:noet: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/doc/sandwich.txt b/config/neovim/store/lazy-plugins/vim-sandwich/doc/sandwich.txt new file mode 100644 index 00000000..ad8735f2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/doc/sandwich.txt @@ -0,0 +1,699 @@ +*sandwich.txt* Last change:03-Jul-2022. + +The set of operator and textobject plugins to edit sandwiched textobjects. + +Author : machakann +License : NYSL license + Japanese + English (Unofficial) + +Requirement: Vim 7.4 or higher + |+reltime| feature (optional) + |+float| feature (optional) + +============================================================================== +CONTENTS *sandwich-contents* + +QUICK START |sandwich-quick-start| +INTRODUCTION |sandwich-introduction| +KEYMAPPINGS |sandwich-keymappings| +CONFIGURATION |sandwich-configuration| +MAGICCHARACTERS |sandwich-magiccharacters| +FILETYPE RECIPES |sandwich-filetype-recipes| +FUNCTIONS |sandwich-functions| +MISCELLANEOUS |sandwich-miscellaneous| + Introduce vim-surround keymappings + Customize the behavior of magicchar-f + Any way to make a recipe deletes kinds of parentheses and brackets? + +============================================================================== +QUICK START *sandwich-quick-start* + +*sandwich.vim* is the set of operator and textobject plugins to +add/delete/replace surroundings of a sandwiched textobject, like (foo), "bar". + +add~ +Press sa{motion/textobject}{addition}. +For example, saiw( makes foo to (foo). + +delete~ +Press sdb or sd{deletion}. +For example, sdb or sd( makes (foo) to foo. +sdb searchs a set of surrounding automatically. + +replace~ +Press srb{addition} or sr{deletion}{addition}. +For example, srb" or sr(" makes (foo) to "foo". + +Now you already know enough about sandwich.vim. If you want more, +read this help and each help documents of operator/textobject, +|operator-sandwich| and |textobj-sandwich|. + + + +============================================================================== +INTRODUCTION *sandwich-introduction* + +This plugin provides functions to add/delete/replace surroundings of +sandwiched texts. These functions are implemented genuinely by utilizing +operator/textobject framework. Their action can be repeated by |.| command +without any dependency. + +Refer to the |sandwich-keymappings| for the key mappings supplied by +|sandwich.vim|. It also explains how to change key mappings for your +preference. + +Refer to the |operator-sandwich| for the details of the genuine operators. +It also explains how to customize the functions to edit surroundings. + +Refer to the |textobj-sandwich| for the details of the genuine textobjects. +It also explains how to customize the behavior of textobjects. + + + +============================================================================== +KEYMAPPINGS *sandwich-keymappings* + +This plugin defines the following keymappings. + +function default keymappings +-------------------------------------------------------------------------- +add sa{motion/textobject}{addition} (normal and visual mode) + -> |(sandwich-add)| + +delete + sd{deletion} (normal mode) + sd (visual mode) + -> |(sandwich-delete)| + + sdb (normal mode) + -> |(sandwich-delete-auto)| + +replace + sr{deletion}{addition} (normal mode) + sr{addition} (visual mode) + -> |(sandwich-replace)| + + srb{addition} (normal mode) + -> |(sandwich-replace-auto)| + +textobjct + ib (operator-pending and visual mode) + -> |(textobj-sandwich-auto-i)| + ab (operator-pending and visual mode) + -> |(textobj-sandwich-auto-a)| + + is (operator-pending and visual mode) + -> |(textobj-sandwich-query-i)| + as (operator-pending and visual mode) + -> |(textobj-sandwich-query-a)| + +-------------------------------------------------------------------------- + +NOTE: To prevent unintended operation, the following setting is strongly + recommended to add to your vimrc. +> + nmap s + xmap s +< + |s| could be easily replaced by |c|l| commands. + +If you don't need the default mappings, define +*g:sandwich_no_default_key_mappings* in your vimrc. +> + let g:sandwich_no_default_key_mappings = 1 +< +The following code snippet shows how to change the trigger key from s to z. +> + let g:sandwich_no_default_key_mappings = 1 + + " add + nmap za (sandwich-add) + xmap za (sandwich-add) + omap za (sandwich-add) + + " delete + nmap zd (sandwich-delete) + xmap zd (sandwich-delete) + nmap zdb (sandwich-delete-auto) + + " replace + nmap zr (sandwich-replace) + xmap zr (sandwich-replace) + nmap zrb (sandwich-replace-auto) +< +Additionally, map textobjects if you need. +> + " text-objects (if you need) + omap ib (textobj-sandwich-auto-i) + xmap ib (textobj-sandwich-auto-i) + omap ab (textobj-sandwich-auto-a) + xmap ab (textobj-sandwich-auto-a) + + omap is (textobj-sandwich-query-i) + xmap is (textobj-sandwich-query-i) + omap as (textobj-sandwich-query-a) + xmap as (textobj-sandwich-query-a) +< + + + *(sandwich-add)* + [count1] (sandwich-add) [count2] {motion} {addition} + Wrap an assigned text on the buffer. + This key mapping handles [count] uniquely. + [count1] is given to |(sandwich-add)| and thus surround the text + [count1] times. On the other hand, [count2] is passed to {motion} as + usually. Both of those [count]s are optional. The {addition} is the + key to specify the surroundings; for example, an input saiw( wraps a + word by parentheses(). + +{Visual} [count] (sandwich-add) {addition} + Wrap the visual-selected text [count] times. + + (sandwich-add) is available in normal, visual, and + operator-pending mode. It is mapped at sa in default. +> + nmap sa (sandwich-add) + xmap sa (sandwich-add) + omap sa (sandwich-add) +< + + + *(sandwich-delete)* +[count] (sandwich-delete) {deletion} + Delete a pair of surroundings nearest to the cursor specified by + {deletion}. For example, an input sd( deletes a pair of parentheses() + nearest to the cursor. +> + (foo) -> foo +< + Delete the [count]th closest surroundings if [count] is given. +> + (foo(bar)baz) cursor is on "bar" + + -- sd( --> (foobarbaz) + -- 2sd( --> foo(bar)baz +< + +{Visual} [count] (sandwich-delete) + Delete the successive surroundings at the both ends of the visually + selected text. Delete [count] times if [count] is given. + + (sandwich-delete) is available in normal and visual mode. It is + mapped at sd in default. +> + nmap sd (sandwich-delete) + xmap sd (sandwich-delete) +< + + + *(sandwich-delete-auto)* +[count] (sandwich-delete-auto) + Delete the [count]th closest surroundings from the cursor. +> + [foo(bar)baz] cursor is on "bar" + + -- sdb --> [foobarbaz] + -- 2sdb --> foo(bar)baz +< + (sandwich-delete-auto) is available in normal mode. It is mapped + at sdb in default. +> + nmap sdb (sandwich-delete-auto) +< + + + *(sandwich-replace)* +[count] (sandwich-replace) {deletion} {addition} + Replace the closest surroundings from the cursor specified by + {deletion} to another surroundings specified by {addition}. For + example, an input sr([ replaces a pair of parentheses() to a pair of + square brackets[]. +> + (foo) -> [foo] +< + Replace the [count]th closest surroundings if [count] is given. +> + (foo(bar)baz) cursor is on "bar" + + -- sr([ --> (foo[bar]baz) + -- 2sr([ --> [foo(bar)baz] +< + +{Visual} [count] (sandwich-replace) {addition} + Replace the successive surroundings at the both ends of the visually + selected text to another surroundings specified by {addition}. + Replace [count] times if [count] is given. + + (sandwich-replace) is available in normal and visual mode. It is + mapped at sr in default. +> + nmap sr (sandwich-replace) + xmap sr (sandwich-replace) +< + + + *(sandwich-replace-auto)* +[count] (sandwich-replace-auto) {addition} + Replace the [count]th closest surroundings from the cursor to another + surroundings specified by {addition}. +> + [foo(bar)baz] cursor is on "bar" + + -- srb{ --> [foo{bar}baz] + -- 2srb{ --> {foo(bar)baz} +< + (sandwich-replace-auto) is available in normal mode. It is + mapped at srb in default. +> + nmap srb (sandwich-replace-auto) +< + + +============================================================================== +CONFIGURATION *sandwich-configuration* + +A set of surroundings and options for it is called "recipe". Each recipe is a +dictionary and the |list|s of recipes determines the operator's behavior and +textobject's behavior. |g:sandwich#default_recipes| is one of the |list|s of +recipes. This is shared to be used by |operator-sandwich| and +|textobj-sandwich| since it is convenient in many cases. If +|g:sandwich#recipes| is defined by user, it is employed alternatively. The +default recipes |g:sandwich#default_recipes| can be checked by |:echo| +command. +> + :echo g:sandwich#default_recipes +< +Besides them, |g:operator#sandwich#recipes| and |g:textobj#sandwich#recipes| +can be used. They are used only by |operator-sandwich| and |textobj-sandwich| +respectively. + +About the contents of a recipe, please see |operator-sandwich-configuration| +and |textobj-sandwich-configuration|. + + + +g:sandwich#recipes *g:sandwich#recipes* + This is one of the lists of recipes which is referred from both + |operator-sandwich| and |textobj-sandwich|. If this list does not + exist, |g:sandwich#default_recipes| is used. + *b:sandwich_recipes* + If |b:sandwich_recipes| exists, it would be used instead of + |g:sandwich#recipes|. This is buffer local, thus it might be + convenient to manage too many filetype-specific recipes. + + + +g:sandwich#default_recipes *g:sandwich#default_recipes* + This is a list of recipes which is prepared in default. If + |g:sandwich#recipes| exists, it will be used instead. + + This variable is locked usually, but it can be copied when you declare + |g:sandwich#recipes| if you need. +> + :let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +< + +Notes on default recipes~ +`(`, `)` +`[`, `]` +`{`, `}` +`<`, `>` + Both open/close braces behave as same. For example, `saiw(` and `saiw)` + result in the same text. +> + foo -> (foo) +< + `sd(` and `sd)` do the opposite. + +------------------------------------------------------------------------------ +`'` +`"` + Wrap a text by quotes. +> + foo -> 'foo' +< + When deleting a pair of quotes, 'quoteescape' option is considered. + The pair of quotes are searched only in a same line. + +------------------------------------------------------------------------------ +`` + When deleting a pair of spaces, successive spaces are deleted at a + time. + +------------------------------------------------------------------------------ +`t`, `T` +`f`, `F` +`i`, `I` + See |sandwich-magiccharacters|. + + + +------------------------------------------------------------------------------ +Global options~ +g:sandwich#timeout *g:sandwich#timeout* + If this option is a falsy value, the operators and the + query-textobject will wait for subsequent inputs until the complete + key sequence has been received to specify a recipe. For example, with + the following recipes, +> + let g:sandwich#recipes = [ + \ {'buns': ['for {', '}'], 'nesting': 1, 'input': ['bf']} + \ {'buns': ['if {', '}'], 'nesting': 1, 'input': ['bi']} + \ {'buns': ['else {', '}'], 'nesting': 1, 'input': ['be']} + \ ] +< + type `saiwb` and a while later the operator eagerly wrap a word with + `b` if this option is true. The operators wait next input until a + recipe is specified if this option is false. This option takes effect + both on the operators and the query-textobject. + |g:operator#sandwich#timeout| or |g:textobj#sandwich#timeout| takes + priority over this option if it exists. If this has not been defined, + 'timeout' option is referred. See |g:sandwich#timeoutlen| also. + + + +g:sandwich#timeoutlen *g:sandwich#timeoutlen* + The time in milli seconds that waits for a key code or mapped key + sequence to complete. If there are recipes overlapped, this option is + used. Assume that the following recipes are prepared: +> + let g:sandwich#recipes = [ + \ {'buns': ['(', ')']} + \ {'buns': ['((', '))']} + \ ] +< + after pressing saiw(, the operator waits in the time. If you press one + more ( in the time, then a recipe for '((' and '))' is decided to use. + No keypress has come through the time a recipe for '(' and ')' is + settled. This option takes effect both on the operators and the + query-textobject. |g:operator#sandwich#timeoutlen| or + |g:textobj#sandwich#timeoutlen| takes priority over this option if it + exists. If this variable has not been defined, 'timeoutlen' option is + referred. + + When the timeout option (|g:operator#sandwich#timeout|, + |g:textobj#sandwich#timeout|, |g:sandwich#timeout|, 'timeout') is off, + this option is ignored. + + + +g:sandwich#input_fallback *g:sandwich#input_fallback* + This bool option controls the behavior when no recipe matches with + user input to add/delete/replace surroundings. If this option is true + and no recipe matches with user input, the user input character itself + is used as the surroundings for add/delete/replace. For example, even + if there is no recipe associated with input `a` the key sequence + `saiwa` wraps a word with `a`. +> + foo -> afooa +< + Set falthy value to this option if this behavior is not desired. +> + let g:sandwich#input_fallback = 0 +< + + + +============================================================================== +MAGICCHARACTERS *sandwich-magiccharacters* + +Sandwich.vim requests user to input keys for determination of +{addtion}/{deletion}. Usually it is something like `(` for `()` pair or +`"` for `""` pair, but there is several functional inputs for cumbersome +editings. It might be helpful for your work, give it a try! + +f~ +F~ + Press `saiwf` to surround a word by function. After inputting `f` key, + user would be requested to input function name and press , then + the target textobject would be surrounded by parenthesis with the + function name. key completes function names from the current + buffer in input. +> + arg -- saiwffunc --> func(arg) +< + The key sequence `sdf`, conversely, deletes function surrounding. +> + func(arg) -- sdf --> arg +< + In case of nested functions, `sdf` deletes the function under the + cursor while `sdF` deletes the function surrounding. +> + cursor is on 'func2': + func1(func2(arg)) -- sdf --> func1(arg) + -- sdF --> func2(arg) +< + +i~ +I~ + It realizes to define `I`nstant surroundings. `saiwi` ask user for + inputting former and latter surroundings. For example, + `saiwifoobar` makes a word surrounded by `foo` and `bar`. + key completes words from the current buffer, just simply. + On the other hand `sdi` deletes arbitrary surroundings. For example, + `sdifoobar` makes `foowordbar` to `word`, the inputs would be + interpreted as regular expressions. This is useful when a lot of + targets are there because the action could be repeated by |.| command. + `sa{textobj}I`, `sdI` reuse the last inputs. + + +t~ +T~ + The inputs `t` and `T` support to edit HTML style tags. `saiwt` ask + user to input a name of element, then a textobject would be surrounded + by the tag. `saiwT` works as same. +> + word -- saiwtp -->

word

+< + `sdt` deletes the nearest tag surroundings. `sdT` works as same. +> +

word

-- sdt --> word +< + `t` and `T` works differently only when replacing surroundings. + `srtt` replaces only the name of element, does not touch attributes. + `srTT` replaces the whole body of tags. + + + + +============================================================================== +FILETYPE RECIPES *sandwich-filetype-recipes* + +Sandwich.vim has filetype specific settings. They will be available when +'filetype' option was set to a certain value. User settings are not +overwrittern by them, use only the recipes helpful for you. These recipes are +written in ftplugin/{filetype}/sandwich.vim. + +If you don't want vim to load these files, set +`g:sandwich_no_{filetype}_ftplugin` as true in advance. For example, add the +following line to your vimrc in case you don't need tex specific recipes. +> + let g:sandwich_no_tex_ftplugin = 1 +< + +------------------------------------------------------------------------------ +plaintex~ +tex~ +> + Surroundings Input + “{text}” u" + „{text}“ U" + ug + u, + «{text}» u< + uf + `{text}' l' + l` + ``{text}'' l" + "`{text}"' L" + ,,{text}`` l, + <<{text}>> l< + \{{text}\} \{ + \[{text}\] \[ + + \left({text}\right) m( + \left[{text}\right] m[ + \left|{text}\right| m| + \left\{{text}\right\} m{ + \left\langle {text}\right\rangle m< +< The surroundings its input starting from 'm' could be removed/replaced + by a input `ma`, for example press `sdma`. +> + \big({text}\big) M( + \big[{text}\big] M[ + \big|{text}\big| M| + \big\{{text}\big\} M{ + \big\langle {text}\big\rangle M< + + \begingroup{text}\endgroup gr + \gr + \toprule{text}\bottomrule tr + br + \tr + \br + + \{input}{{text}} c +< This recipe asks user to input a string and then {input} is + substituted by the input string. +> + \begin{{input}}{text}\end{{input}} e +< This recipe asks user to input a string and then {input} is + substituted by the input string. + Use to complete {input}, the completion items are loaded from + `g:sandwich#filetype#tex#environments` + (or `b:sandwich#filetype#tex#environments` if exists). + + +============================================================================== +FUNCTIONS *sandwich-functions* + +sandwich#util#addlocal({recipes}) *sandwich#util#addlocal()* + This function appends the list of recipes {recipes} as buffer-local + settings with inheritance of the global settings. This is useful when + one wants to add filetype specific settings with keeping global + setting |g:sandwich#recipes| clean. Note that {recipe} is a |List| of + recipes. +> + autocmd FileType python call sandwich#util#addlocal([ + \ {'buns': ['"""', '"""'], 'nesting': 0, 'input': ['3"']}, + \ ]) +< + +============================================================================== +MISCELLANEOUS *sandwich-miscellaneous* + +Introduce vim-surround keymappings~ + If you want to use with vim-surround (vim script #1697) keymappings, + add the following line to your vimrc. +> + runtime macros/sandwich/keymap/surround.vim +< + NOTE: Unlike surround.vim, the inputs `(` and `)` behave as same. + If you want the spaces inside braces with `(` input, add the + lines. +> + runtime macros/sandwich/keymap/surround.vim + let g:sandwich#recipes += [ + \ {'buns': ['{ ', ' }'], 'nesting': 1, 'match_syntax': 1, + \ 'kind': ['add', 'replace'], 'action': ['add'], 'input': ['{']}, + \ + \ {'buns': ['[ ', ' ]'], 'nesting': 1, 'match_syntax': 1, + \ 'kind': ['add', 'replace'], 'action': ['add'], 'input': ['[']}, + \ + \ {'buns': ['( ', ' )'], 'nesting': 1, 'match_syntax': 1, + \ 'kind': ['add', 'replace'], 'action': ['add'], 'input': ['(']}, + \ + \ {'buns': ['{\s*', '\s*}'], 'nesting': 1, 'regex': 1, + \ 'match_syntax': 1, 'kind': ['delete', 'replace', 'textobj'], + \ 'action': ['delete'], 'input': ['{']}, + \ + \ {'buns': ['\[\s*', '\s*\]'], 'nesting': 1, 'regex': 1, + \ 'match_syntax': 1, 'kind': ['delete', 'replace', 'textobj'], + \ 'action': ['delete'], 'input': ['[']}, + \ + \ {'buns': ['(\s*', '\s*)'], 'nesting': 1, 'regex': 1, + \ 'match_syntax': 1, 'kind': ['delete', 'replace', 'textobj'], + \ 'action': ['delete'], 'input': ['(']}, + \ ] +< + + + `ys`, `yss`, `yS`, `ds`, `cs` in normal mode and `S` in visual mode + are available. Not in vim-surround but `dss` and `css` are also + available, these are similar as `ds` and `cs` but determine + deleted/replaced texts automatically. See the file directly for + detail. + + Additionally, vim-sandwich provides several textobjects. They would + also be helpful, give it a try! + + * Textobjects to select a text surrounded by braket or same characters + user input. + |(textobj-sandwich-query-i)|, |(textobj-sandwich-query-a)| +> + xmap is (textobj-sandwich-query-i) + xmap as (textobj-sandwich-query-a) + omap is (textobj-sandwich-query-i) + omap as (textobj-sandwich-query-a) +< + * Textobjects to select the nearest surrounded text automatically. + |(textobj-sandwich-auto-i)|, |(textobj-sandwich-auto-a)|. +> + xmap iss (textobj-sandwich-auto-i) + xmap ass (textobj-sandwich-auto-a) + omap iss (textobj-sandwich-auto-i) + omap ass (textobj-sandwich-auto-a) +< + * Textobjects to select a text surrounded by same characters user + input. + |(textobj-sandwich-literal-query-i)|, + |(textobj-sandwich-literal-query-a)| +> + xmap im (textobj-sandwich-literal-query-i) + xmap am (textobj-sandwich-literal-query-a) + omap im (textobj-sandwich-literal-query-i) + omap am (textobj-sandwich-literal-query-a) +< + +------------------------------------------------------------------------------ +Customize the behavior of magicchar-f~ + +`magicchar-f` can delete simple function-call like syntax: +> + func(arg) -- sdf --> arg +< +If you want to delete more advanced patterns, for example: +> + obj.method(arg) -- sdf --> arg +< +You can use `g:sandwich#magicchar#f#patterns` or +`b:sandwich_magicchar_f_patterns` for the purpose. Each of those are a list of +patterns like: +> + let g:sandwich#magicchar#f#patterns = [ + \ { + \ 'header' : '\<\h\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] +< +Those four values are all regex patterns, which match with something before +open parenthesis, open & close parentheses, something after close parenthesis. +Therefore, you can delete a method with an object by the following setting. +> + let g:sandwich#magicchar#f#patterns = [ + \ { + \ 'header' : '\<\%(\h\k*\.\)*\h\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] +< +`b:sandwich_magicchar_f_patterns` can be used to define filetype specific +setting. +> + augroup sandwich-ft-python + autocmd Filetype python let b:sandwich_magicchar_f_patterns = [ + \ { + \ 'header' : '\<\%(\h\k*\.\)*\h\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] + augroup END +< +The default settings is in `g:sandwich#magicchar#f#default_patterns`. + + +------------------------------------------------------------------------------ +Any way to make a recipe deletes kinds of parentheses and brackets?~ + +See |sandwich-compound-recipes|. + +============================================================================== +vim:tw=78:ts=8:ft=help:norl:noet: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/doc/textobj-sandwich.jax b/config/neovim/store/lazy-plugins/vim-sandwich/doc/textobj-sandwich.jax new file mode 100644 index 00000000..67211ba3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/doc/textobj-sandwich.jax @@ -0,0 +1,1243 @@ +*textobj-sandwich.jax* 『挟まれた』テキストを選択する。 + Last change:28-Nov-2021. + +書いた人 : machakann +ライセンス : NYSL license + 日本語 + English (Unofficial) + +必須要件: Vim 7.4 かそれ以降のバージョン + |+reltime| 機能 (任意) + |+float| 機能 (任意) + +============================================================================== +CONTENTS *textobj-sandwich-contents* + +TL;DR +INTRODUCTION |textobj-sandwich-introduction| +KEYMAPPINGS |textobj-sandwich-keymappings| +CONFIGURATION |textobj-sandwich-configuration| + REQUISITE + buns + external + FILTERS + filetype + kind + mode + operator + LOCAL OPTIONS + nesting + expand_range + regex + skip_regex + skip_regex_head + skip_regex_tail + quoteescape + expr + noremap + syntax + inner_syntax + match_syntax + skip_break + skip_expr + GLOBAL OPTIONS + timeout |g:textobj#sandwich#timeout| + timeoutlen |g:textobj#sandwich#timeoutlen| + stimeoutlen |g:textobj#sandwich#stimeoutlen| + latest_jump |g:textobj#sandwich#latest_jump| +MISCELLANEOUS |textobj-sandwich-miscellaneous| + dib で欲しいのはそれじゃない + 頻繁に「 Timed out 」となる + 誤操作でカーソルが飛んでいった! + +============================================================================== +TL;DR + こちらをご覧ください。 |sandwich-quick-start|. + + + +============================================================================== +INTRODUCTION *textobj-sandwich-introduction* + +*textobj-sandwich* は (foo) や "bar" のような括弧などに挟まれたテキストを検索 +し選択するためのテキストオブジェクトです。これは次の四つの独立したテキストオブ +ジェクトからなります。 +|(textobj-sandwich-auto-i)|, |(textobj-sandwich-auto-a)|, +|(textobj-sandwich-query-i)|, |(textobj-sandwich-query-a)| + +|(textobj-sandwich-auto-i)| および |(textobj-sandwich-auto-a)| は +対象テキストを自動的に検索します。対象テキストはレシピ recipe と呼ばれる括弧の +セットが事前に用意されており、これに依って検索されます。これについては +|textobj-sandwich-configuration| に詳しく書かれています。 +|(textobj-sandwich-auto-i)| は囲みの内側のテキストを選択します。 +|(textobj-sandwich-auto-a)| は囲みを含めてテキストを選択します。これらは +デフォルトでは ib 及び ab にマップされています。 + +|(textobj-sandwich-query-i)| 及び |(textobj-sandwich-query-a)| は +対象テキストをユーザーの入力に応じて検索します。ユーザーの入力にマッチするレシ +ピが見つかればそのレシピに応じて、あるいはマッチするレシピがなければ入力された +一文字に挟まれた範囲を検索します。 |(textobj-sandwich-query-i)| は囲みの +内側のテキストを選択します。 |(textobj-sandwich-query-a)| は囲みを含めた +テキストを選択します。これらはデフォルトでは is 及び as にマップされています。 + +------------------------------------------------------------------------------ +ビジュアルモードではこれらのテキストオブジェクトは選択領域を拡張するように動き +ます。これは Vim 組み込みのテキストオブジェクト |iw| の |v|iw|iw|iw|... とした +時の動作に似ています。例えば次のようなテキストの foo の上にカーソルがあると仮 +定しましょう。 +> + {baz[bar(foo)bar]baz} +< +vib と入力します +> + <-> : selected region + {baz[bar(foo)bar]baz} +< +さらに ib +> + <---------> : selected region + {baz[bar(foo)bar]baz} +< +さらに ib +> + <-----------------> : selected region + {baz[bar(foo)bar]baz} +< +とこのように選択領域を拡張します。 + +------------------------------------------------------------------------------ +矩形ビジュアルモードではカーソル行内で対象テキストを検索し、選択の逆端はこれに +従います。例えば次の5行5桁のテキストがあるとします。 +> + (foo) + (bar) + (baz) +< +カーソルが第一行目にある時 2jibd と押下すると +> + () + () + () +< +こうなります。次にこのような場合を考えてみましょう。 +> + foooo + baaar + (baz) +< +カーソルが第一行目にある時 2jibd と押下すると +> + fo + br + () +< +このようにカーソルのあった第三行目に合わせて動作します。 + + + +============================================================================== +KEYMAPPINGS *textobj-sandwich-keymappings* + +このプラグインは下記のキーマッピングを定義します。これらはオペレータ待機モード +とビジュアルモードの両方で使用可能です。 + +キーマッピング デフォルト +----------------------------------------------------------- +(textobj-sandwich-auto-i) ib +(textobj-sandwich-auto-a) ab + +(textobj-sandwich-query-i) is +(textobj-sandwich-query-a) as +----------------------------------------------------------- + +これらのデフォルト設定が必要なければ vimrc で +g:textobj_sandwich_no_default_key_mappings を定義してください。 +> + let g:textobj_sandwich_no_default_key_mappings = 1 +< +これでデフォルトキーマッピングは定義されません。あとはお好きなようにマップしな +おしてください。 +> + omap ia (textobj-sandwich-auto-i) + xmap ia (textobj-sandwich-auto-i) + omap aa (textobj-sandwich-auto-a) + xmap aa (textobj-sandwich-auto-a) +< + +------------------------------------------------------------------------------ +キーマッピング~ + +(textobj-sandwich-auto-i) *(textobj-sandwich-auto-i)* + 括弧などに挟まれた文字列を自動的に検索するテキストオブジェクトです。こ + のキーマッピングは括弧などに囲まれた内側の文字列を選択します。デフォル + トではオペレータ待機モードとビジュアルモードの ib にマップされていま + す。また、オペレータ待機モード |:omap| 、ビジュアルモード |:xmap| に加 + えノーマルモード |:nmap| で使用することもできます。 + +(textobj-sandwich-auto-a) *(textobj-sandwich-auto-a)* + 括弧などに挟まれた文字列を自動的に検索するテキストオブジェクトです。こ + のキーマッピングは括弧などの囲みを含む文字列を選択します。デフォルトで + はオペレータ待機モードとビジュアルモードの ab にマップされています。ま + た、オペレータ待機モード |:omap| 、ビジュアルモード |:xmap| に加えノー + マルモード |:nmap| で使用することもできます。 + + +(textobj-sandwich-query-i) *(textobj-sandwich-query-i)* + 括弧などに挟まれた文字列をユーザーの入力に応じて検索するテキストオブジ + ェクトです。このキーマッピングは括弧などに囲まれた内側の文字列を選択し + ます。デフォルトではオペレータ待機モードとビジュアルモードの ib にマッ + プされています。また、オペレータ待機モード |:omap| 、ビジュアルモード + |:xmap| に加えノーマルモード |:nmap| で使用することもできます。 + + +(textobj-sandwich-query-a) *(textobj-sandwich-query-a)* + 括弧などに挟まれた文字列をユーザーの入力に応じて検索するテキストオブジ + ェクトです。このキーマッピングは括弧などの囲みを含む文字列を選択します + 。デフォルトではオペレータ待機モードとビジュアルモードの ib にマップさ + れています。また、オペレータ待機モード |:omap| 、ビジュアルモード + |:xmap| に加えノーマルモード |:nmap| で使用することもできます。 + + + *(textobj-sandwich-literal-query-i)* +(textobj-sandwich-literal-query-i) + これは |(textobj-sandwich-query-i)| に似ていますが、常にユーザー + 設定 |g:sandwich#recipes| 及び |g:textobj#sandwich#recipes| を無視しま + す。ユーザーの入力はそのままに解釈され、その文字に囲まれたテキストを選 + 択します。オペレータ待機モード |:omap| 、ビジュアルモード |:xmap| に加 + えノーマルモード |:nmap| で使用することができます。 > + omap if (textobj-sandwich-literal-query-i) + xmap if (textobj-sandwich-literal-query-i) + + " press difa to delete a text surrounded by 'a'. + " abcba -> aa +< + + *(textobj-sandwich-literal-query-a)* +(textobj-sandwich-literal-query-a) + これは |(textobj-sandwich-query-a)| に似ていますが、常にユーザー + 設定 |g:sandwich#recipes| 及び |g:textobj#sandwich#recipes| を無視しま + す。ユーザーの入力はそのままに解釈され、その文字に囲まれたテキストを選 + 択します。オペレータ待機モード |:omap| 、ビジュアルモード |:xmap| に加 + えノーマルモード |:nmap| で使用することができます。 > + omap if (textobj-sandwich-literal-query-a) + xmap if (textobj-sandwich-literal-query-a) + + " press dafb to delete a text surrounded by 'b' including 'b'. + " abcba -> aa +< + + + +キーマッピング関数~ + +ユーザーは新しいキーマッピングを定義するのに以下の関数を使うこともできます。 + + *textobj#sandwich#auto()* +textobj#sandwich#auto(mode, a_or_i[, options[, recipes]]) + この関数はテキストオブジェクトのキーマッピングを定義するのにつかわれま + す。 +> + onoremap ib textobj#sandwich#auto('o', 'i') + xnoremap ib textobj#sandwich#auto('x', 'i') + onoremap ab textobj#sandwich#auto('o', 'a') + xnoremap ab textobj#sandwich#auto('x', 'a') +< + 省略可能な第三引数に空でない辞書変数が与えられた場合、辞書に含まれる + local option はそのキーマッピングで使われるオプションのデフォルト値 + を上書きします。 +> + " example 1 + onoremap ib + \ textobj#sandwich#auto('o', 'i', {'expand_range': 0}) + + " example 2 + let g:sandwich_alt_options = {'expand_range': 0} + onoremap ib + \ textobj#sandwich#auto('o', 'i', g:sandwich_alt_options) +< + + 省略可能な第四引数にリストが与えられた場合、そのキーマッピングは + |g:sandwich#recipes| (|g:sandwich#default_recipes|) や + |g:textobj#sandwich#recipes| (|g:textobj#sandwich#default_recipes|) の + かわりに引数に与えられたリストを参照します。 +> + " example 1 + onoremap ib + \ textobj#sandwich#auto('o', 'i', {}, [{'buns': ['(', ')']}]) + + " example 2 + let g:sandwich_alt_recipes = [{'buns': ['(', ')']}] + onoremap ib + \ textobj#sandwich#auto('o', 'i', {}, g:sandwich_alt_recipes) +< + + + + *textobj#sandwich#query()* +textobj#sandwich#query(mode, a_or_i[, options[, recipes]]) + この関数は |textobj#sandwich#auto()| と同様の関数ですが、 + |(textobj-sandwich-query-i)| や + |(textobj-sandwich-query-a)| などの query 系列の + テキストオブジェクトを定義するのに使われます。 +> + onoremap is textobj#sandwich#query('o', 'i') + xnoremap is textobj#sandwich#query('x', 'i') + onoremap as textobj#sandwich#query('o', 'a') + xnoremap as textobj#sandwich#query('x', 'a') +< + + + +============================================================================== +CONFIGURATION *textobj-sandwich-configuration* + +囲み文字のセットと付加情報をまとめたものをレシピ "recipe" と呼びます。一つ一つ +のレシピは |Dictionary| であり、これを集めた |list| がテキストオブジェクトの動 +作を決めます。 |g:sandwich#default_recipes| はそのリストのうちの一つ +で、|operator-sandwich| と |textobj-sandwich| の両方から参照されます。これは多 +くの場合、共有された方が便利であるためです。もしユーザーが +|g:sandwich#recipes| を定義した場合はかわりにそちらが使用されます。デフォルト +の|g:sandwich#default_recipes| は |:echo| コマンドで確認できます。 +> + :echo g:sandwich#default_recipes +< +この変数はロックされていますが、 |g:sandwich#recipes| をユーザーが宣言する際に +必要ならコピーすることができます。 +> + :let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +< +デフォルトでは (), [], {}, <> のセットが登録されています。より詳しくは +|g:sandwich#default_recipes| をご覧ください。 + +|g:textobj#sandwich#default_recipes| はまた別のリストで、こちらは +|textobj-sandwich| のみからしか参照されません。ユーザーが +|g:textobj#sandwich#recipes| を定義した際にはそちらがかわりに使用されます。 +|g:textobj#sandwich#default_recipes| は |g:sandwich#default_recipes| と同様に +ロックされています。 + + + +g:textobj#sandwich#recipes *g:textobj#sandwich#recipes* + テキストオブジェクトのみから参照されるレシピのリストです。このリストが + 存在しない場合は |g:textobj#sandwich#default_recipes| が使用されます。 + *b:textobj_sandwich_recipes* + |b:textobj_sandwich_recipes| が存在する場合は、 + |g:textobj#sandwich#recipes| のかわりにそちらが使われます。これはバッ + ファについてローカルな値なので、ファイルタイプ固有な設定が増えすぎた時 + に使うと便利かもしれません。 + + + +g:textobj#sandwich#default_recipes *g:textobj#sandwich#default_recipes* + デフォルトで用意されるレシピのリストの一つです。テキストオブジェクトの + みから参照されます。ユーザーが |g:textobj#sandwich#recipes| を定義した + 場合にはそちらがかわりにつかわれます。その際必要ならコピーすることが + できます。 +> + :let g:textobj#sandwich#recipes + \ = deepcopy(g:textobj#sandwich#default_recipes) +< + + +NOTE: もしレシピが競合する場合は |g:textobj#sandwich#default_recipes| か + |g:textobj#sandwich#recipes| が |g:sandwich#default_recipes| か + |g:sandwich#recipes| よりも優先されます。また同じリスト内であれば、リス + ト後方のレシピほど優先されます。 + +------------------------------------------------------------------------------ +一つ一つのレシピは |Dictionary| 変数であり、 requisite, input, filter, local +optionの三種の情報を持ちえます。 requisite はすべてのレシピに必須の情報で囲み +文字を特定するためのものです。 input はレシピを指定するためのユーザー入力を決 +めます。 filter は使用する状況によってレシピを選別するためにあります。 local +option はレシピによってテキストオブジェクトの働きを細かく制御するために使われ +ます。さらにこれら以外にテキストオブジェクトの基本的な動作を制御するための +global option もいくつか存在します。 + +設定を変更する場合はまず必要なリストを宣言しましょう。 +> + let g:sandwich#recipes = [] +< +あるいはデフォルト設定を引き継ぐためにコピーしてもよいでしょう。 +> + let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +< + +Requisite~ + +requisite には buns と external の二種類あり、すべてのレシピはどちらかを持たな +ければなりません。 + +buns + テキストを囲む文字列のセットを定義するためのキーです。値に文字列を二つ + 要素に持つ |list| を持ちます。このセットに囲まれた文字列がテキストオブ + ジェクトに検索されます。 "regex" オプションが真のときは正規表現をとし + て扱われます。また、レシピが "input" キーを持たない場合、 + |(textobj-sandwich-query-i)| 及び + |(textobj-sandwich-query-a)| でレシピを指定するためのキー入力に + も使われます。 +> + let g:sandwich#recipes += [ + \ {'buns': ['(', ')']} + \ ] + + " Press dis( or dis) + " (foo) ---> () + + let g:sandwich#recipes += [ + \ {'buns': ['ab', 'cd']} + \ ] + + " Press disab or discd + " abfoocd ---> abcd + + let g:sandwich#recipes += [ + \ {'buns': ['ab', 'cd'], 'input': ['a']} + \ ] + + " Press disa + " abfoocd ---> abcd +< + + + +external + こちらは補助的に使われる requisite です。文字列二要素の |list| でそれ + ぞれの要素は外部のテキストオブジェクトになります。これを使うと外部のテ + キストオブジェクトをレシピ化することができます。 + |(textobj-sandwich-auto-i)| 及び + |(textobj-sandwich-query-i)| ではリストの前方の要素が選択する範 + 囲を、 |(textobj-sandwich-auto-a)| 及び + |(textobj-sandwich-query-a)| では後方の要素が選択する範囲を選択 + します。"buns" とは異なり、多くの local option が無効になります。 +> + let g:sandwich#recipes += [ + \ {'external': ['it', 'at']} + \ ] + + " "it" selects the text inside tags, "at" selects including tags. + " <---it---> + " title here + " <----------at-----------> + + " Press dib + " title here ---> + " Press dab + " title here ---> +< + "noremap" オプションは適用されます。内部的にはビジュアル選択がテキスト + オブジェクトの指定する範囲を調べるのに使われるので "noremap" が適用さ + れるのはビジュアルモードへのマッピングになります。 +> + let g:sandwich#recipes += [ + \ {'external': ['i[', 'a['], 'noremap': 0} + \ ] + + " Press dib + " [foo] ---> [] + + xnoremap i[ i{ + xnoremap a[ a{ + + " Press dib + " {foo} ---> {} +< + "noremap" オプションを偽にすることでユーザー定義テキストオブジェクトを + 同様にレシピ化することができます。 +> + " "noremap" option should be false. + let g:sandwich#recipes += [ + \ { + \ 'external': ["\(textobj-sandwich-auto-i)", + \ "\(textobj-sandwich-auto-a)"], + \ 'noremap': 0, + \ 'kind': ['query'], + \ 'input': ['b'] + \ } + \ ] +< + NOTE: 使用するテキストオブジェクトはビジュアルモードで正しく動作するこ + とを期待されます。 + NOTE: すべてのテキストオブジェクトが動作するとは限りません。 + + + +Input~ + +input + |(textobj-sandwich-query-i)| 及び + |(textobj-sandwich-query-a)| は囲みを決定するためにユーザーに入 + 力を促します。この時の入力に使われるのがこのキーの値になります。値はリ + ストで複数の文字列を含むことができます。このキーを持たないレシピでは + "buns" がかわりにつかわれます。以下の例では、 "input" キーを持たない + ので "buns" がそのまま使われます。 +> + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""']} + \ ] + " Press dis""" + " """foo""" ---> """""" +< + "input" キーがあればそちらがかわりにつかわれます。 +> + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'input': ['"']} + \ ] + " Press dis" + " """foo""" ---> """""" +< + + + +Filter~ + +filetype + ファイルタイプによってレシピを選別するフィルターです。値はリストでファ + イルタイプ名の文字列が要素になります。レシピが "filetype" キーを持たな + いか、要素として "all" を持つ場合はすべてのファイルタイプで有効になり + ます。 +> + " The following recipes are valid on any filetype. + let g:sandwich#recipes += [ + \ {'buns': ['(', ')']} + \ {'buns': ['[', ']'], 'filetype': ['all']} + \ ] + + " The textobj "it" and "at" is not versatile and might be heavy on + " large files, thus it would be better to activate only on specific + " filetypes. + let g:sandwich#recipes += [ + \ {'external': ['it', 'at'], 'filetype': ['html']} + \ ] +< + + + +kind + テキストオブジェクトの種類によってレシピを選別するフィルターです。値は + リストで要素にテキストオブジェクトの種類を文字列として持ちます。使用可 + 能な種類は "auto", "query", "textobj", "all" です。 "textobj" は + "auto", "query" の二つを指定した場合と同じです。 "textobj" と"all" の + 違いは、 "all" は |operator-sandwich| の種類も含むことがある点にありま + す。あわせて |operator-sandwich-configuration| もご覧ください。レシピ + が "kind" キーを持たなければすべての種類で有効です。 +> + " The following recipe is valid only with + " (textobj-sandwich-auto-i) and (textobj-sandwich-auto-a) + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'kind': ['auto']} + \ ] +< + + + +mode + 使用されたモードによってレシピを選別するフィルターです。値はモードを表 + す文字のリストです。使用可能な文字は "o" と "x" のみです。"o" はオペ + レータ待機モードを、 "x" はビジュアルモードを表します。レシピが "mode" + キーを持たなければすべてのモードで有効です。 +> + " These recipes are switch behaviors on modes with the same input. + let g:sandwich#recipes += [ + \ {'buns': ['"', '"'], 'mode': ['o']} + \ {'buns': ['"""', '"""'], 'mode': ['x'], 'input': ['"']} + \ ] +< + + + +action + 編集の種類によってレシピを選別するフィルターです。"add" 及び "delete" + が |operator-sandwich| のために用意されています。"all" は先の二つを包 + 含します。"add" のみが指定されている場合は |textobj-sandwich| では無効 + になります。しかし、"delete" か "all" が含まれた場合は有効になります。 + これは削除においてそれらのテキストオブジェクトが連動するためです。 +> + " This recipe does not take effect for textobjects + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'action': ['add'], 'input': ['"']} + \ ] + + " This recipe is valid for delete actions and textobjects + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'action': ['delete'], 'input': ['"']} + \ ] +< + + + +expr_filter + ユーザーがフィルターを自作して使うことができます。リストの要素は式 + |expression| として評価され、評価値が真 (1) の場合にはそのレシピは有効 + となり、評価値が偽 (0) の場合にはレシピは無効となります。 +> + " A filter should be defined in somewhere, for example in your vimrc. + function! FilterValid() + return 1 + endfunction + + function! FilterInvalid() + return 0 + endfunction + + " This recipe is valid + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'expr_filter': ['FilterValid()']} + \ ] + + " This recipe is invalid + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'expr_filter': ['FilterInvalid()']} + \ ] +< + 例えば、次のようなフィルターを定義すると特定のオペレーターでのみ有効な + レシピを作ることができます。 +> + function! FilterOperator(operator) + return a:operator ==# v:operator ? 1 : 0 + endfunction + + " This recipe is valid only with d operator. + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'expr_filter': ['FilterOperator("d")']} + \ ] +< + + + +Local option~ + +local option はレシピごとに細かく挙動を調整するために設定されます。もし指定の +ない場合はデフォルトで設定されている値が使われます。この値はテキストオブジェク +トの種類に依って違う値をもち、 |g:textobj#sandwich#options| に格納されていま +す。 + +|(textobj-sandwich-query-i)| と |(textobj-sandwich-query-a)| の +"skip_break" オプションのデフォルト値を変更したい場合は次のようにします。 +> + let g:textobj#sandwich#options.query.skip_break = 1 +< +あるいは関数インターフェースも用意されています。 |textobj#sandwich#set()| +> + call textobj#sandwich#set('query', 'skip_break', 1) +< + + + +g:textobj#sandwich#options *g:textobj#sandwich#options* + local option のデフォルト値を格納した |Dictionary| 変数です。 +> + " let textobj#sandwich#options[kind][option] = {value} + + " For example + let g:textobj#sandwich#options['query']['skip_break'] = 1 + " or + let g:textobj#sandwich#options.query.skip_break = 1 +< + *b:textobj_sandwich_options* + |b:textobj_sandwich_options| が存在する場合はそちらが優先されます。 + バッファについてローカルなオプション設定を使うのに便利でしょう。 + + 使用可能な引数を次に列挙します。 + * kind + - query + - auto + * option + - nesting + - expand_range + - regex + - skip_regex + - skip_regex_head + - skip_regex_tail + - quoteescape + - expr + - noremap + - syntax + - inner_syntax + - match_syntax + - skip_break + - skip_expr + + + + *textobj#sandwich#set()* +textobj#sandwich#set(kind, option, value) + local option のデフォルト値を安全かつ簡単に変更するための関数です。引 + 数の組み合わせが不正な場合、関数はメッセージを表示します。使用可能な引 + 数は |g:textobj#sandwich#options| の項に列挙されています。また、これに + 加えて、この関数では "all" を kind に指定することができます。 + + + + *textobj#sandwich#setlocal()* +textobj#sandwich#setlocal(kind, option, value) + local option のバッファについてローカルなデフォルト値を安全かつ簡単に + 変更するための関数です。引数の組み合わせが不正な場合、関数はメッセージ + を表示します。使用可能な引数は |g:textobj#sandwich#options| の項に列挙 + されています。また、これに加えて、この関数では "all" を kind に指定す + ることができます。 + + +textobj#sandwich#set_default() *textobj#sandwich#set_default()* + local option のデフォルト値を初期化します。 + + + +nesting + このオプションは対象範囲の検索アルゴリズムを切り替えます。もし、"buns" + がネスト構造を作る場合はこのオプションを真に設定してください。そうでな + ければ偽に設定してください。例を挙げれば、一般的に () はネスト構造を作 + りますが、"" は作りません。次のようなテキストではネスト構造の有無に + よって期待する範囲が異なることがわかるでしょう。 + + キー入力 yib によって文字列をヤンクします。 +> + # : cursor + case1: (foo(bar)baz) : foo(bar)baz is the expected result + case2: "foo"bar"baz" : foo is the expected result +< + このオプションは特にレシピごとに設定されることが望ましいです。 + + デフォルト値 + * query: 0 + * auto : 0 + + + +expand_range + このオプションが 0 なら検索はカーソル行内のみで行われます。正の整数を + 与えた場合、その行数だけカーソル行から上下に検索範囲を拡大します。負の + 整数を与えると、すべてのバッファを対象にして検索します。 + NOTE: 内部的には広い検索範囲を一度に対象とはせず、段階的に広げながら検 + 索します。この間経過時間を監視しながら + |g:textobj#sandwich#stimeoutlen| に設定された時間が経過してなお + [count] 個の候補が見つかっていない場合、検索をそこで諦めます。 + + おおよその場合 0 か負数がよい選択かと思われます。 + + デフォルト値 + * query: -1 + * auto : -1 + + + +regex + もしこのオプションの値が真なら "buns" は正規表現だとみなされます。 +> + let g:sandwich#recipes += [ + \ {'buns': ['\d\+', '\d\+'], 'regex': 1} + \ ] + " Press dib + " 123foo456 ---> 123456 +< + デフォルト値 + * query: 0 + * auto : 0 + + + +skip_regex +skip_regex_head +skip_regex_tail + 選択範囲候補を検索の際に、候補をスキップするための正規表現を収めた + |list| です。"buns" にマッチしたとしても同時にこのリストのどれかにも同 + 時にマッチした場合、この候補はスキップされます。正規表現は "buns" に + マッチした文字列の先頭にマッチしなければなりません。 +> + let g:sandwich#recipes += [ + \ {'buns': ['b', 'a'], 'skip_regex': ['a\zea']} + \ ] + " Press dib + " baaaaaaaar ---> bar +< + "skip_regex" の要素は前方と後方の、両方の囲みの検索で使われます。通常 + はこれで十分に便利なのですが、両方の囲みとエスケープ文字がすべて同一の + 場合などに問題になります。例えば Vim script では、シングルクオーテーシ + ョンに囲まれた領域内の連続した二つのシングルクオーテーションは一つのシ + ングルクオーテーションとみなされます。 +> + :echo 'foo''bar' " -> foo'bar +< + これをスキップするためには前方の検索か、後方の検索かを区別しなければな + りません。なので、 "skip_regex_head" 及び "skip_regex_tail" を使いまし + ょう。 +> + let g:sandwich#recipes += [ + \ { + \ 'buns': ["'", "'"], + \ 'filetype': ['vim'], + \ 'expand_range': 0, + \ 'skip_regex_head': + \ ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']'], + \ 'skip_regex_tail': + \ ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)'''], + \ 'nesting': 0 + \ 'match_syntax': 2, + \ } + \ ] +< + NOTE: 残念ながら、カーソルが両端の連続するシングルクオーテーションの上 + にある場合はこの設定でもうまく働きません。 +> + ### ### : Cursor should not be on + '''foo '' bar''' : the positions pointed by #. +< + 検索の間、内部的にカーソルを動かしているので、カーソルを表す正規表現の + \%# |/\%#| が使えます。 + + デフォルト値 + * query: [] + * auto : [] + + + +quoteescape + このオプションが真なら、'quoteescape' オプションに含まれる文字でエス + ケープされた候補をスキップします。実用的には、'', "", ``, 専用のオプ + ションです。 +> + let g:sandwich#recipes += [ + \ {'buns': ['"', '"'], 'quoteescape': 1} + \ ] + " Press dib + " "foo\"bar" ---> foo\"bar +< + デフォルト値 + * query: 0 + * auto : 0 + + + +expr + このオプションの値が 1 か 2 なら "buns" は式として評価されてから検索さ + れます。値が 1 の場合は一度だけ評価され、|.| コマンドでは同じものが使 + 用されます。値が 2 の場合は |.| コマンドで繰り返される毎に評価されます + 。例えば以下のレシピはその時々の無名レジスタ |quotequote| の内容に挟ま + れた範囲を検索します。 (参考: |:let-@|) +> + let g:sandwich#recipes += [ + \ {'buns': ['@@', '@@'], 'expr': 1, 'input': ['@']} + \ ] +< + NOTE: このオプションが真の時、 "buns" はレシピを呼ぶためのキー入力とは + みなされませんので "input" の指定が必要になります。 + + NOTE: 評価の結果 "buns" が空文字列を含むと |textobj-sandwich| は選択を + 中止します。 + + NOTE: もし式が |getchar()| や |input()| でユーザーに入力を求める場合、 + |operator-sandwich| の囲みを外すオペレータや |textobj-sandwich| + の |(textobj-sandwich-auto-i)| や + |(textobj-sandwich-auto-a)| で不都合が出ることがあります。 + この問題を避けるために、 "kind" フィルターを併用することが推奨さ + れます。例えば次のレシピは二文字の入力を求め、その二文字に囲まれ + たテキストを選択します。 +> + let g:textobj#sandwich#recipes += [ + \ { + \ 'buns': ['GetChar()', 'GetChar()'], + \ 'kind': ['query'], + \ 'expr': 1, + \ 'input': ['f'] + \ }, + \ ] + + function! GetChar() abort + let c = getchar() + let c = type(c) == type(0) ? nr2char(c) : c + return c ==# "\" || c ==# "\" ? '' : c + endfunction +< + デフォルト値 + * query: 0 + * auto : 0 + + + +noremap + このオプションは "external" を持つレシピにのみ適用されます。このオプ + ションが真なら "external" テキストオブジェクトのマッピングは展開されま + せん。このオプションが偽ならマッピングが展開されます。"external" の項 + もご覧ください。 + NOTE: 内部的にはテキストオブジェクトの選択範囲を調べるのに、ビジュアル + モード選択を使いますので、このオプションが適用されるのはビジュア + ルモードへのマッピングになります。 + + デフォルト値 + * query: 1 + * auto : 1 + + + +syntax + テキストの囲みに適用されていることを期待するシンタックスグループ名のリ + ストです。このリストが空でなければ、テキストオブジェクトは選択範囲の候 + 補検索時にシンタックスを確認し、リスト要素にマッチするものがなければス + キップします。構文ハイライトがオフになっていれば単に無視されます。 +> + " check the position pointed by # + " # # + " "foo" +< + 例えば "" は一般的にいくつかの限られたハイライトグループが適用されてい + ることが期待できます。 +> + let g:sandwich#recipes += [ + \ { + \ 'buns': ['"', '"'], + \ 'quoteescape': 1, + \ 'expand_range': 0, + \ 'nesting': 0, + \ 'match_syntax': 1, + \ 'syntax': + \ ['Constant', 'Statement', 'Special', 'String', 'Comment'], + \ 'inner_syntax': + \ ['Constant', 'PreProc', 'Special', 'String', 'Comment'], + \ } + \ ] +< + このレシピは次のような場合も文字列としての "" に囲まれた領域をよく認識 + します。 +> + key press vas" selects as following. # means cursor positions. + + <-#-> + list = ["foo", "bar"] + <------#-----> +< + しかし残念ながらこのレシピは汎用的とはいえずマークダウンのようなファイ + ルタイプでうまく働きません。なので可能ならば使用するファイルタイプごと + に設定するのが望ましいでしょう。 +> + let g:sandwich#recipes += [ + \ { + \ 'buns': ['"', '"'], + \ 'filetype': ['ruby'], + \ 'quoteescape': 1, + \ 'expand_range': 0, + \ 'nesting': 0, + \ 'match_syntax': 1, + \ 'syntax': + \ ['Constant', 'Special', 'String', 'Comment'], + \ 'inner_syntax': + \ ['Constant', 'Special', 'String', 'Comment'], + \ } + \ ] + + let g:sandwich#recipes += [ + \ { + \ 'buns': ['"', '"'], + \ 'filetype': ['sh'], + \ 'quoteescape': 1, + \ 'expand_range': 0, + \ 'nesting': 0, + \ 'match_syntax': 1, + \ 'syntax': + \ ['Constant', 'Statement', 'String', 'Comment'], + \ 'inner_syntax': + \ ['Constant', 'Special', 'PreProc', 'String', 'Comment'], + \ } + \ ] +< + よく使われるハイライトグループは多くのカラースキーマで共有されていま + す。こちらをご覧ください。 |group-name| + + NOTE: リストの要素がシンタックススタックの中に含まれていれば成功とみな + されます。シンタックススタックは次のようなキーマッピングを対象文 + 字列の上で使用することにより確認できます。 +> + nnoremap s :echo map(synstack(line('.'), col('.')), + \ 'synIDattr(synIDtrans(v:val), "name")') +< + デフォルト値 + * query: [] + * auto : [] + + + +inner_syntax + 囲まれたテキストの両端に適用されていることを期待するシンタックスグルー + プ名のリストです。このリストが空でなければ、テキストオブジェクトは選択 + 範囲の候補検索時にシンタックスを確認し、リスト要素にマッチするものがな + ければスキップします。構文ハイライトがオフになっていれば単に無視されま + す。 +> + let g:sandwich#recipes += [ + \ { + \ 'buns': ['"', '"'], + \ 'quoteescape': 1, + \ 'inner_syntax': ['Constant', 'String'] + \ } + \ ] + + " check the position pointed by # + " # # + " "foo" +< + 一つ上の local option "syntax" の項に実用的な使用例があります。こちら + もご覧ください。 + + よく使われるハイライトグループは多くのカラースキーマで共有されていま + す。こちらをご覧ください。 |group-name| + + NOTE: リストの要素がシンタックススタックの中に含まれていれば成功とみな + されます。シンタックススタックは次のようなキーマッピングを対象文 + 字列の上で使用することにより確認できます。 +> + nnoremap s :echo map(synstack(line('.'), col('.')), + \ 'synIDattr(synIDtrans(v:val), "name")') +< + デフォルト値 + * query: [] + * auto : [] + + + +match_syntax + もしこのオプションが 1 のとき、テキストオブジェクトは文字列両端の囲み + のシンタックスハイライトが一致していることを確認します。一致しなければ + その候補はスキップされるでしょう。さらにこのオプションが 2 の時には、 + これに加えて囲まれたテキストの両端も同じハイライトが適用されているかを + 確かめます。このオプションが 3 の場合は、 2 の場合とほとんど同じですが + 囲みと囲まれたテキストをそれぞれ独立に調べます。すなわち、両方の囲みの + ハイライトが同じ且つ囲まれた文字列の両端のハイライトが同じ必要がありま + すが、囲みと内側の文字列のハイライトが一致する必要はありません。 +> + AB CD + (foo) + + match_syntax is 1: A == D is required. + match_syntax is 2: A == D == B == C is required. + match_syntax is 3: A == D and B == C is required. +< + 囲み文字が一文字でない場合はそれぞれの先頭位置が確認されます。 + + 例えば '' は一般的にそれ自体を含み、内部の文字列もConstant か String + シンタックスが適用されているでしょう。 +> + let g:sandwich#recipes += [ + \ { + \ 'buns': ["'", "'"], + \ 'quoteescape' : 1, + \ 'syntax' : ['Constant', 'String'], + \ 'match_syntax': 2, + \ } + \ ] +< + Local option "syntax" の項にも使用例がありますのでご覧ください。 + + NOTE: 適用されたシンタックスアイテムは次のようなキーマッピングを対象文 + 字列の上で使用することで調べられます。 +> + nnoremap S + \ :echo synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'name') +< + 文字列両端の囲みの表示されているシンタックスは厳密に一致している + 必要があります。囲みの内側のシンタックスについては少し条件が緩和 + され、文字列囲みのシンタックスがシンタックススタックに含まれてい + れば一致とみなされます。これは例えば変換指定子 "%s" のようなもの + がしばしば String 以外の色でハイライトされることを考慮するためで + す。表示上の色は違ってもシンタックススタックに String が含まれて + いることが期待できるため、表示上の色は違っても通常の文字列リテラ + ルと同様に扱われます。シンタックススタックは次のようなキーマッピ + ングを調べたいテキスト上で使用することで確認できます。透過属性が + 指定されていない限りリストの最後の要素が表示されている実効シンタ + ックスアイテムです。 +> + nnoremap s :echo map(synstack(line('.'), col('.')), + \ 'synIDattr(synIDtrans(v:val), "name")') +< + デフォルト値 + * query: 0 + * auto : 0 + + + +skip_break + 次のようなテキストについて考えます。 +> + { + foo + } +< + {} に囲まれたテキストは "\n foo\n" なので、キー入力 dib の結果は次の + ようになります。 +> + {} +< + しかし、次のようになったほうが便利な場合もありますね。 +> + { + + } +< + このオプションが真のときテキストオブジェクトは両端の改行文字をスキップ + して後者を与えます。さらにインデントを保存するために改行に続くホワイト + スペースも一緒にスキップします。つまり "\n\\s*" というパターンにマッチ + する文字列がスキップされます。 + + このオプションは |(textobj-sandwich-query-i)| 及び + |(textobj-sandwich-auto-i)| に対してのみ有効です。 + + デフォルト値 + * query: 0 + * auto : 0 + + + +skip_expr + 式 |expression| のリストを値にとります。選択範囲を検索する際、 buns に + マッチする箇所を見つけるたびに式が評価され、もし返り値が真 (非ゼロ値) + ならそのマッチ箇所をスキップします。 +> + " skip 'a's other than head and tail of a line. + let g:sandwich#recipes += [ + \ { + \ 'buns': ['a', 'a'], + \ 'skip_expr': [ + \ 'col(".")[2] != 1 && col(".")[2] != col([line("."), "$"])-1' + \ ] + \ } + \ ] + " Press dib + " aaaaa ---> aa +< + もし、リストの要素が |Funcref| の場合は注意が必要です。その関数は二つ + の引数をとれるようにする必要があります。 +> + :function {func}(is_head, pos) +< + もし、 a:is_head が1なら前方の囲みを検索中です。もし、 a:is_head が0な + ら後方の囲みを検索中になります。 a:pos は buns にマッチした先頭位置を + 表す四要素のリストです。これは |getpos()| 関数とおなじフォーマットを持 + ちます。必ずしもこれらの引数を使う必要はありません。将来、引数が追加さ + れることがありうるので、可変長引数を指定しておくことをお勧めします。 +> + " skip 'a's other than head and tail of a line. + function! SkipIntermediates(is_head, pos, ...) + if a:is_head + return a:pos[2] != 1 + else + return a:pos[2] != col([a:pos[1], '$'])-1 + endif + endfunction + + let g:sandwich#recipes += [ + \ { + \ 'buns': ['a', 'a'], + \ 'skip_expr': [function('SkipIntermediates')] + \ } + \ ] + " Press dib + " aaaaa ---> aa +< + + デフォルト値 + * query: [] + * auto : [] + + + +Global option~ + +いくつか基本的な動作を制御するためのオプションが存在します。 + +g:textobj#sandwich#timeout *g:textobj#sandwich#timeout* + このオプションに偽値が設定されている場合、テキストオブジェクトは一つの + レシピを指定する完全な入力がなされるまで待ちます。例えば、下記のレシピ + を用意します。 +> + let g:sandwich#recipes = [ + \ {'buns': ['for {', '}'], 'nesting': 1, 'input': ['bf']} + \ {'buns': ['if {', '}'], 'nesting': 1, 'input': ['bi']} + \ {'buns': ['else {', '}'], 'nesting': 1, 'input': ['be']} + \ ] +< + このオプションが真の場合は `saiwb` とタイプして少し待つと、テキストオ + ブジェクトはタイムアウトのため `b` で囲まれたテキストを選択します。 + しかし、偽の場合は一つのレシピを指定する入力が完成するまで待ちます。 + この変数が定義されていなければ |g:sandwich#timeout| が代わりに参照され + ます。特別な理由がなければ |g:sandwich#timeout| を使ってください。 + 関連:|g:textobj#sandwich#timeoutlen| + + + +g:textobj#sandwich#timeoutlen *g:textobj#sandwich#timeoutlen* + 入力に前方一致で重複するレシピが存在する場合に次のキーシーケンスを待つ + 時間をミリ秒単位で指定します。 +> + let g:sandwich#recipes = [ + \ {'buns': ['(', ')']} + \ {'buns': ['((', '))']} + \ ] +< + vis( とキー入力するとテキストオブジェクトは次に ( が入力されるかこの時 + 間だけ待ちます。この間にもう一度 ( を押下すると '((' と '))' のレシピ + が使われるでしょう。キーの押下なしでこの時間が過ぎると '(' と ')' のレ + シピが使われるでしょう。この変数が定義されていなければ + |g:sandwich#timeoutlen| が代わりに参照されます。特別な理由がなければ + |g:sandwich#timeoutlen| を使ってください。 + + タイムアウト機能(|g:textobj#sandwich#timeout|, |g:sandwich#timeout|, + 'timeout')がオフの場合はこのオプションは無視されます。 + + + +g:textobj#sandwich#stimeoutlen *g:textobj#sandwich#stimeoutlen* + テキストオブジェクトが選択領域の検索を諦めるまでの時間をミリ秒単位で指 + 定します。カウントに指定された数の候補を見つけられずにこの時間が経過す + ると検索を諦め、何も選択せずにユーザーに操作をかえします。デフォルト値 + は 500 です。 + + + +g:textobj#sandwich#latest_jump *g:textobj#sandwich#latest_jump* + このオプションの値が真ならテキストオブジェクトは範囲選択の際にもとのカ + ーソル位置をマーク |``| に記録します。 これにより |``| コマンドでカー + ソルをその位置に戻すことができます。意図しない広い範囲を選択してしまっ + た際にカーソル位置を戻すのに便利です。デフォルト値は 1 です。 + + + +============================================================================== +MISCELLANEOUS *textobj-sandwich-miscellaneous* + +dib で欲しいのはそれじゃない~ + キー入力 dib は次のように働きます。これを +> + ( + foo + ) +< + このようにします。 +> + () +< + なぜならば、 () に囲まれたテキストは ' foo' ではなく '\n foo\n' + だからです。しかし次のような結果 +> + ( + + ) +< + を期待する場合は local option の "skip_break" が使えます。 +> + call textobj#sandwich#set('all', 'skip_break', 1) +< + + + +頻繁に「 Timed out 」となる~ + |g:textobj#sandwich#stimeoutlen| に大きめの値を設定しましょう。デフォ + ルト設定は 500 です。 + + + +誤操作でカーソルが飛んでいった!~ + 慌てないでください。編集結果は |u| コマンドで戻せるでしょう。カーソル + 位置は |``| コマンドでもとの位置に戻すことができます。こちらも合わせて + ご覧ください。|g:textobj#sandwich#latest_jump| + + + +============================================================================== +vim:tw=78:ts=8:ft=help:norl:noet: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/doc/textobj-sandwich.txt b/config/neovim/store/lazy-plugins/vim-sandwich/doc/textobj-sandwich.txt new file mode 100644 index 00000000..66943eef --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/doc/textobj-sandwich.txt @@ -0,0 +1,1256 @@ +*textobj-sandwich.txt* The textobject plugin to select sandwiched texts. + Last change:28-Nov-2021. + +Author : machakann +License : NYSL license + Japanese + English (Unofficial) + +Requirement: Vim 7.4 or higher + |+reltime| feature (optional) + |+float| feature (optional) + +============================================================================== +CONTENTS *textobj-sandwich-contents* + +TL;DR +INTRODUCTION |textobj-sandwich-introduction| +KEYMAPPINGS |textobj-sandwich-keymappings| +CONFIGURATION |textobj-sandwich-configuration| + REQUISITE + buns + external + FILTERS + filetype + kind + mode + operator + LOCAL OPTIONS + nesting + expand_range + regex + skip_regex + skip_regex_head + skip_regex_tail + quoteescape + expr + noremap + syntax + inner_syntax + match_syntax + skip_breaking + skip_expr + GLOBAL OPTIONS + timeout |g:textobj#sandwich#timeout| + timeoutlen |g:textobj#sandwich#timeoutlen| + stimeoutlen |g:textobj#sandwich#stimeoutlen| + latest_jump |g:textobj#sandwich#latest_jump| +MISCELLANEOUS |textobj-sandwich-miscellaneous| + It is not what I expect with key sequence dib. + Timed out so frequently! + My cursor moved a lot because of unintended selection! + +============================================================================== +TL;DR + See |sandwich-quick-start|. + + + +============================================================================== +INTRODUCTION *textobj-sandwich-introduction* + +*textobj-sandwich* is an textobject plugin to search and select sandwiched +text, like (foo) or "bar". It consists of four independent textobject +keymappings, +|(textobj-sandwich-auto-i)|, |(textobj-sandwich-auto-a)|, +|(textobj-sandwich-query-i)| and |(textobj-sandwich-query-a)|. + +|(textobj-sandwich-auto-i)| and |(textobj-sandwich-auto-a)| +searches a sandwiched text automatically. The sets of surroundings called +"recipe"s are listed in advance. It is described in detail in +|textobj-sandwich-configuration|. |(textobj-sandwich-auto-i)| selects +a surrounded region without surroundings. |(textobj-sandwich-auto-a)| +selects a surrounded region including surroundings. These are mapped to +key sequences `ib` and `ab` in default. + +|(textobj-sandwich-query-i)| and |(textobj-sandwich-query-a)| +searches a sandwiched text based on a user input. If the user input is matched +with a recipe registered, then it selects the text determined by the matched +recipe. Otherwise it selects the region surrounded by a same character input. +|(textobj-sandwich-query-i)| selects a surrounded region without +surroundings. |(textobj-sandwich-query-a)| selects a surrounded region +including surroundings. These are mapped to key sequences `is` and `as` in +default. + +------------------------------------------------------------------------------ +In visual mode, these textobjects expands the selection area by successive key +sequence as similar to |v|iw|iw|iw|.... +Assume that the following text and the cursor is on foo. +> + {baz[bar(foo)bar]baz} +< +Press `vib` +> + <-> : selected region + {baz[bar(foo)bar]baz} +< +Press `ib` again +> + <---------> : selected region + {baz[bar(foo)bar]baz} +< +Press `ib` again +> + <-----------------> : selected region + {baz[bar(foo)bar]baz} +< + +------------------------------------------------------------------------------ +In block-wise visual mode, a target text is searched within the cursor line +and the other end follows. For example, assume the 5 by 5 text. +> + (foo) + (bar) + (baz) +< +When the cursor is on the first line, press `2jibd` +> + () + () + () +< +Next assume that the lines like this: +> + foooo + baaar + (baz) +< +When the cursor is on the first line, press `2jibd` +> + fo + br + () +< + + + +============================================================================== +KEYMAPPINGS *textobj-sandwich-keymappings* + +This plugin defines following keymappings. They are valid in operator-pending +mode and visual mode. + +keymappings default keymappings +----------------------------------------------------------- +(textobj-sandwich-auto-i) ib +(textobj-sandwich-auto-a) ab + +(textobj-sandwich-query-i) is +(textobj-sandwich-query-a) as +----------------------------------------------------------- + +If you do not need default keymappings, define a variable named +g:textobj_sandwich_no_default_key_mappings in your vimrc. +> + let g:textobj_sandwich_no_default_key_mappings = 1 +< +Then default mappings are never applied. And map them again as you like. +> + omap ia (textobj-sandwich-auto-i) + xmap ia (textobj-sandwich-auto-i) + omap aa (textobj-sandwich-auto-a) + xmap aa (textobj-sandwich-auto-a) +< + +------------------------------------------------------------------------------ +keymappings~ + +(textobj-sandwich-auto-i) *(textobj-sandwich-auto-i)* + The textobject mapping to search and select a sandwiched text + automatically. This keymapping selects inside surroundings. The + surroundings are not included. This keymapping is mapped to `ib` of + operator-pending mode and visual mode in default. This keymapping + could be mapped to operator-pending mode |:omap|, to visual mode + |:xmap|, and also to normal mode |:nmap|. + +(textobj-sandwich-auto-a) *(textobj-sandwich-auto-a)* + The textobject mapping to search and select a sandwiched text + automatically. This keymapping selects sandwiched text including + surroundings. This keymapping is mapped to `ab` of operator-pending + mode and visual mode in default. This keymapping could be mapped to + operator-pending mode |:omap|, to visual mode |:xmap|, and also to + normal mode |:nmap|. + + +(textobj-sandwich-query-i) *(textobj-sandwich-query-i)* + The textobject mapping to search and select a sandwiched text + depending on a user input. This keymapping selects inside + surroundings. The surroundings are not included. This keymapping is + mapped to `is` of operator-pending mode and visual mode in default. + This keymapping could be mapped to operator-pending mode |:omap|, to + visual mode |:xmap|, and also to normal mode |:nmap|. + +(textobj-sandwich-query-a) *(textobj-sandwich-query-a)* + The textobject mapping to search and select a sandwiched text + depending on a user input. This keymapping selects sandwiched text + including surroundings. This keymapping is mapped to `as` of + operator-pending mode and visual mode in default. This keymapping + could be mapped to operator-pending mode |:omap|, to visual mode + |:xmap|, and also to normal mode |:nmap|. + + + *(textobj-sandwich-literal-query-i)* +(textobj-sandwich-literal-query-i) + This textobject mapping is similar as + |(textobj-sandwich-query-i)| but it always ignores user + settings, |g:sandwich#recipes| and |g:textobj#sandwich#recipes|. The + user input is interpreted as-is, thus the mapping selects a text + surrounded by the same characters. This keymapping could be mapped to + operator-pending mode |:omap|, to visual mode |:xmap|, and also to + normal mode |:nmap|. > + omap if (textobj-sandwich-literal-query-i) + xmap if (textobj-sandwich-literal-query-i) + + " press difa to delete a text surrounded by 'a'. + " abcba -> aa +< + *(textobj-sandwich-literal-query-a)* +(textobj-sandwich-literal-query-a) + This textobject mapping is similar as + |(textobj-sandwich-query-a)| but it always ignores user + settings, |g:sandwich#recipes| and |g:textobj#sandwich#recipes|. The + user input is interpreted as-is, thus the mapping selects a text + surrounded by the same characters. This keymapping could be mapped to + operator-pending mode |:omap|, to visual mode |:xmap|, and also to + normal mode |:nmap|. > + omap if (textobj-sandwich-literal-query-a) + xmap if (textobj-sandwich-literal-query-a) + + " press dafb to delete a text surrounded by 'b' including 'b'. + " abcba -> aa +< + + + +KEY MAPPING FUNCTIONS~ + +User can make new mappings by using function interfaces natively. + + *textobj#sandwich#auto()* +textobj#sandwich#auto(mode, a_or_i[, options[, recipes]]) + This function is used to make a operator key-mapping as following. +> + onoremap ib textobj#sandwich#auto('o', 'i') + xnoremap ib textobj#sandwich#auto('x', 'i') + onoremap ab textobj#sandwich#auto('o', 'a') + xnoremap ab textobj#sandwich#auto('x', 'a') +< + If a not-empty dictionary is given to the optional third argument of + this function, the local options inside the dictionary override + default option values. +> + " example 1 + onoremap ib + \ textobj#sandwich#auto('o', 'i', {'expand_range': 0}) + + " example 2 + let g:sandwich_alt_options = {'expand_range': 0} + onoremap ib + \ textobj#sandwich#auto('o', 'i', g:sandwich_alt_options) +< + + If a list of recipes is given to the optional fourth argument of this + function, the key mapping use the list instead of + |g:sandwich#recipes| (|g:sandwich#default_recipes|) and + |g:textobj#sandwich#recipes| (|g:textobj#sandwich#default_recipes|). +> + " example 1 + onoremap ib + \ textobj#sandwich#auto('o', 'i', {}, [{'buns': ['(', ')']}]) + + " example 2 + let g:sandwich_alt_recipes = [{'buns': ['(', ')']}] + onoremap ib + \ textobj#sandwich#auto('o', 'i', {}, g:sandwich_alt_recipes) +< + + + + *textobj#sandwich#query()* +textobj#sandwich#query(mode, a_or_i[, options[, recipes]]) + This function is similar as |textobj#sandwich#auto()|, but it is + used for declaring new query series textobjects. +> + onoremap is textobj#sandwich#query('o', 'i') + xnoremap is textobj#sandwich#query('x', 'i') + onoremap as textobj#sandwich#query('o', 'a') + xnoremap as textobj#sandwich#query('x', 'a') +< + + + +============================================================================== +CONFIGURATION *textobj-sandwich-configuration* + +A set of surroundings and options for it is called "recipe". Each recipe is a +dictionary and the |list|s of recipes determines the textobject's behavior. +|g:sandwich#default_recipes| is one of the |list|s of recipes. This is shared +to be used with |operator-sandwich| since it is convenient in many cases. If +|g:sandwich#recipes| is defined by user, it is employed alternatively. The +default recipes |g:sandwich#default_recipes| can be checked by |:echo| +command. +> + :echo g:sandwich#default_recipes +< +This variable is locked usually, but it can be copied when you declare +|g:sandwich#recipes| if you need. +> + :let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +< +The set of "(" and ")", "[" and "]", "{" and "}", "<" and ">" are registered +in default. The detailed description is in |g:sandwich#default_recipes|. + +|g:textobj#sandwich#default_recipes| is another list of recipes. This is used +only by |textobj-sandwich|. If |g:textobj#sandwich#recipes| is defined, it is +employed alternatively. |g:textobj#sandwich#default_recipes| is locked as same +as |g:sandwich#default_recipes|. + + + +g:textobj#sandwich#recipes *g:textobj#sandwich#recipes* + This is one of the lists of recipes which is referred only from + |textobj-sandwich|. If this list does not exist, + |g:textobj#sandwich#default_recipes| is used. + *b:textobj_sandwich_recipes* + If |b:textobj_sandwich_recipes| exists, it would be used instead of + |g:textobj#sandwich#recipes|. This is buffer local, thus it might be + convenient to manage too many filetype-specific recipes. + + + +g:textobj#sandwich#default_recipes *g:textobj#sandwich#default_recipes* + This is one of the lists of recipes which is prepared in default. If + |g:textobj#sandwich#recipes| exists, it will be used alternatively. + This variable is locked usually, but it can be copied when you declare + |g:textobj#sandwich#recipes| if you need. +> + :let g:textobj#sandwich#recipes + \ = deepcopy(g:textobj#sandwich#default_recipes) +< + + +NOTE: If recipes are conflicted in some reason, +|g:textobj#sandwich#default_recipes| and |g:textobj#sandwich#recipes| is prior +to |g:sandwich#default_recipes| and |g:sandwich#recipes|. In a same list, a +latter item is prior to a former item. + +------------------------------------------------------------------------------ +A recipe is a |Dictionary| variable and it can have four kinds of +information. Requisite, input, filters and local options. The requisite is +essential for all recipe, it defines a set of surroundings. The input is a +option to assign a recipe for an action. The filters is the option to filter +recipes depending on the situation in use. The local option is utilized to +tune the behavior for each recipe. In addition to them, several global options +are employed to control fundamental behavior of the textobjects. + +As a first step, define your list of recipes. +> + let g:sandwich#recipes = [] +< +Or just copy the default one if you need. +> + let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) +< + +Requisite~ +There are two kinds of requisite, buns and external. All recipes should have +any one of the two. + +buns + This is the key to assign the surroundings. Its value is a list + including two strings which will be the surroundings searched in + text. If "regex" option is true, it is regarded as regular expression. + If a recipe do not have "input" option, this is used as the assignment + input for |(textobj-sandwich-query-i)| and + |(textobj-sandwich-query-a)|. +> + let g:sandwich#recipes += [ + \ {'buns': ['(', ')']} + \ ] + + " Press dis( or dis) + " (foo) ---> () + + let g:sandwich#recipes += [ + \ {'buns': ['ab', 'cd']} + \ ] + + " Press disab or discd + " abfoocd ---> abcd + + let g:sandwich#recipes += [ + \ {'buns': ['ab', 'cd'], 'input': ['a']} + \ ] + + " Press disa + " abfoocd ---> abcd +< + + + +external + This is supplementary requisite. This is a list including two + textobjects as strings. A narrower textobject is the first item and + wider textobject is the second item. This is used when a user want to + use external textobjects as a recipe. Many local options are not + valid. +> + let g:sandwich#recipes += [ + \ {'external': ['it', 'at']} + \ ] + + " "it" selects the text inside tags, "at" selects including tags. + " <---it---> + " title here + " <----------at-----------> + + " Press dib + " title here ---> + " Press dab + " title here ---> +< + "noremap" option is applied for this. Since visual selection is + employed to check its region intrinsically, only the mappings in + visual mode are related to the option. +> + let g:sandwich#recipes += [ + \ {'external': ['i[', 'a['], 'noremap': 0} + \ ] + + " Press dib + " [foo] ---> [] + + xnoremap i[ i{ + xnoremap a[ a{ + + " Press dib + " {foo} ---> {} +< + Combined with "noremap" option, user defined textobjects can be used + in the same way. +> + " "noremap" option should be false. + let g:sandwich#recipes += [ + \ { + \ 'external': ["\(textobj-sandwich-auto-i)", + \ "\(textobj-sandwich-auto-a)"], + \ 'noremap': 0, + \ 'kind': ['query'], + \ 'input': ['b'] + \ } + \ ] +< + NOTE: Registered textobjects should work correctly in visual mode. + NOTE: Not all the user defined textobjects are not guaranteed to work. + + + +Input~ + +input + This is the key to assign a recipe for an searching. + |(textobj-sandwich-query-i)| and + |(textobj-sandwich-query-a)| ask + user to determine surroundings in an searching. At that moment, users + are asked an input to assign a recipe. This option makes the input. If + a recipe does not have the key, items in "buns" are used for the + assignment. +> + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""']} + \ ] + " Press dis""" + " """foo""" ---> """""" +< + If the recipe has input key, it will be used alternatively. +> + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'input': ['"']} + \ ] + " Press dis" + " """foo""" ---> """""" +< + This value should be a |list|, and multiple assignment is valid. + + + +Filter~ + +filetype + This filter filters recipes by filetypes in use. It is a list of + filetypes as strings. If a recipe does not have filetype key or has a + value "all", the recipe is valid on any filetype. +> + " The following recipes are valid on any filetype. + let g:sandwich#recipes += [ + \ {'buns': ['(', ')']} + \ {'buns': ['[', ']'], 'filetype': ['all']} + \ ] + + " The textobj "it" and "at" is not versatile and might be heavy on + " large files, thus it would be better to activate only on specific + " filetypes. + let g:sandwich#recipes += [ + \ {'external': ['it', 'at'], 'filetype': ['html']} + \ ] +< + + + +kind + This filter filters recipes by kinds of operator actions. It is a list + of names of kinds. "auto", "query", "textobj" and "all" can be used. + "textobj" is same as both "auto" and "query". The difference between + "textobj" and "all" is that "all" might include operator kinds in + |g:sandwich#recipes|. See |operator-sandwich-configuration|. If a + recipe does not have kind key, the recipe is valid on any kind. +> + " The following recipe is valid only with + " (textobj-sandwich-auto-i) and (textobj-sandwich-auto-a) + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'kind': ['auto']} + \ ] +< + + + +mode + This filter filters recipes by modes. It is a list of characters + representing modes. "o" or "x" can be used. "o" represents a use in + operator-pending mode, and "x" represents a use in visual mode. If a + recipe does not have mode key, the recipe is valid on any mode. +> + " These recipes are switch behaviors on modes with the same input. + let g:sandwich#recipes += [ + \ {'buns': ['"', '"'], 'mode': ['o']} + \ {'buns': ['"""', '"""'], 'mode': ['x'], 'input': ['"']} + \ ] +< + + + +action + This key is used to filter the recipes by the kinds of action. "add" + and "delete" is reserved for |operator-sandwich|, "all" includes the + those two kinds of action. "add" prohibits to take effect for + |textobj-sandwich| and "delete" and "all" allows it, because usually a + delete action uses those textobjects. +> + " This recipe does not take effect for textobjects + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'action': ['add'], 'input': ['"']} + \ ] + + " This recipe is valid for delete actions and textobjects + let g:sandwich#recipes += [ + \ {'buns': ['"""', '"""'], 'action': ['delete'], 'input': ['"']} + \ ] +< + + + +expr_filter + A user can define filters by oneself. The items of the list are + evaluated as |expression|, and if the value is true (1) then the + recipe is valid, if the value is false (0) then the recipe is invalid. +> + " A filter should be defined in somewhere, for example in your vimrc. + function! FilterValid() + return 1 + endfunction + + function! FilterInvalid() + return 0 + endfunction + + " This recipe is valid + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'expr_filter': ['FilterValid()']} + \ ] + + " This recipe is invalid + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'expr_filter': ['FilterInvalid()']} + \ ] +< + For example, the following filter makes recipes possible to be valid + only with a specific operator. +> + function! FilterOperator(operator) + return a:operator ==# v:operator ? 1 : 0 + endfunction + + " This recipe is valid only with d operator. + let g:sandwich#recipes += [ + \ {'buns': ['(', ')'], 'expr_filter': ['FilterOperator("d")']} + \ ] +< + + + +Local option~ + +Local options are used to optimize the behavior for each recipe. If any +option is set, the default value depending on kinds is used. These default +values are changed by |g:textobj#sandwich#options|. +If you want to change the default value of skip_break option of +|(textobj-sandwich-query-i)| and |(textobj-sandwich-query-a)| +> + let g:textobj#sandwich#options.query.skip_break = 1 +< +Or use the function |textobj#sandwich#set()|. +> + call textobj#sandwich#set('query', 'skip_break', 1) +< + + + +g:textobj#sandwich#options *g:textobj#sandwich#options* + The dictionary includes default local option values. +> + " let textobj#sandwich#options[kind][option] = {value} + + " For example + let g:textobj#sandwich#options['query']['skip_break'] = 1 + " or + let g:textobj#sandwich#options.query.skip_break = 1 +< + *b:textobj_sandwich_options* + If |b:textobj_sandwich_options| exists, it will be used instead of + |g:textobj#sandwich#options|. It would be useful when a user wants to + use buffer-local option settings. + + Available keys are listed below. + * kind + - query + - auto + * option + - nesting + - expand_range + - regex + - skip_regex + - skip_regex_head + - skip_regex_tail + - quoteescape + - expr + - noremap + - syntax + - inner_syntax + - match_syntax + - skip_break + - skip_expr + + + + *textobj#sandwich#set()* +textobj#sandwich#set(kind, option, value) + The function to change default values of local options easily and + safely. If the combination of arguments is not appropriate, this + function shows error messages. The available arguments are listed in + |g:textobj#sandwich#options|. In addition to that, "all" is available + for kind. + + + + *textobj#sandwich#setlocal()* +textobj#sandwich#setlocal(kind, option, value) + The function to change buffer-local default values of local options + easily and safely. If the combination of arguments is not appropriate, + this function shows error messages. The available arguments are listed + in |g:textobj#sandwich#options|. In addition to that, "all" is + available for kind. + + + +textobj#sandwich#set_default() *textobj#sandwich#set_default()* + The function initializes all default values of local options. + + + +nesting + This option switches the searching algorithm. If a set of "buns" makes + nested structure, this option should be 1. Otherwise it should be 0. + For example, ( and ) makes nest but " is not generally. Assume that + the cursor is on foo of the text, expected results are different + depending on whether it makes nest or not. + + Yank a part of text by a key sequence `yib`. +> + # : cursor + case1: (foo(bar)baz) : foo(bar)baz is the expected result + case2: "foo"bar"baz" : foo is the expected result +< + This option is desirable to set on every recipes. + + Default values + * query: 0 + * auto : 0 + + + +expand_range + If this option is 0, surroundings are searched within the cursor line. + If this option is a positive integer, the searched range is expanded + by the number of lines. If this option is a negative integer, whole + buffer is taken as the searched range. + NOTE: Intrinsically, it is not searched a wide range at a time. + The searching lines are expanded step-by-step with monitoring + the elapsed time and a certain time + |g:textobj#sandwich#stimeoutlen| is elapsed without finding + [count] number of candidate, the searching process would time + out. + + In many cases, 0 or a negative number would be a good choice. + + Default values + * query: -1 + * auto : -1 + + + +regex + If this option is true, requisite "buns" is regarded as regular + expressions. +> + let g:sandwich#recipes += [ + \ {'buns': ['\d\+', '\d\+'], 'regex': 1} + \ ] + " Press dib + " 123foo456 ---> 123456 +< + NOTE: If this option is true, the recipe requires "input" to use with + query-textobjects. "buns" never be regarded as key inputs to + trigger. + + Default values + * query: 0 + * auto : 0 + + + +skip_regex +skip_regex_head +skip_regex_tail + These are lists of regular expressions to skip candidate positions in + searching. When the textobjects searches a target text, even though a + text matched with "buns", if the text also matched with an item in + these lists then it will be skipped. The regular expression have to + matched with the head of a skipped text. +> + let g:sandwich#recipes += [ + \ {'buns': ['b', 'a'], 'skip_regex': ['a\zea']} + \ ] + " Press dib + " baaaaaaaar ---> bar +< + Items in "skip_regex" are checked both in searching head and tail + buns. Usually this is enough helpful, but if head, tail and escape + character are all same, it might be a problem. For instance, + successive two quotes, '', inside a quote wrapped string literal is + regarded as a quote in Vim script. +> + :echo '''foo bar''' " -> 'foo bar' +< + To skip the successive quotes, searching head and tail are required to + be distinguished. Use "skip_regex_head", "skip_regex_tail". +> + let g:sandwich#recipes += [ + \ { + \ 'buns': ["'", "'"], + \ 'filetype': ['vim'], + \ 'expand_range': 0, + \ 'skip_regex_head': + \ ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']'], + \ 'skip_regex_tail': + \ ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)'''], + \ 'nesting': 0 + \ 'match_syntax': 2, + \ } + \ ] +< + NOTE: This is still not perfect. If cursor is on ended successive + single quotes, it does not work as expected. +> + ### ### : Cursor should not be on + '''foo '' bar''' : the positions pointed by #. +< + Cursor is moving in searching positions, thus user can use the pattern + \%# |/\%#|. + + Default values + * query: [] + * auto : [] + + + + +quoteescape + If this option is true, skip candidate positions escaped by characters + included in 'quoteescape' option. Practically this option is specific + for ', " and `. +> + let g:sandwich#recipes += [ + \ {'buns': ['"', '"'], 'quoteescape': 1} + \ ] + " Press dib + " "foo\"bar" ---> foo\"bar +< + Default values + * query: 0 + * auto : 0 + + + +expr + If this option is 1 or 2, requisite "buns" is evaluated as expression. + If this option is 1, "buns" are evaluated once and repeat it by |.| + command. If this option is 2, "buns" are evaluated every times in |.| + repeating. For example, the following recipe searches the text in the + unnamed register. (ref. |:let-@|, |quotequote|) +> + let g:sandwich#recipes += [ + \ {'buns': ['@@', '@@'], 'expr': 1, 'input': ['@']} + \ ] +< + NOTE: If this option is true, the recipe requires "input" to use with + query-textobjects. "buns" never be regarded as key inputs to + trigger. + + NOTE: If "buns" include empty string resulting of the evaluation, + |textobj-sandwich| cancels the selection. + + NOTE: If the expressions query user something by |getchar()| or + |input()|, the recipe may cause problem on the + |operator-sandwich| delete operator and |textobj-sandwich| + textobjects, |(textobj-sandwich-auto-i)| and + |(textobj-sandwich-auto-a)|. To avoid this problem, it is + recommended to use "kind" filter. For example, the following + recipe asks two characters to select a text between the two + characters. +> + let g:textobj#sandwich#recipes += [ + \ { + \ 'buns': ['GetChar()', 'GetChar()'], + \ 'kind': ['query'], + \ 'expr': 1, + \ 'input': ['f'] + \ }, + \ ] + + function! GetChar() abort + let c = getchar() + let c = type(c) == type(0) ? nr2char(c) : c + return c ==# "\" || c ==# "\" ? '' : c + endfunction +< + Default values + * query: 0 + * auto : 0 + + + +noremap + This option is referred only with "external" textobjects. In case that + a "external" textobject has been remapped, if this option is true it + would not be remapped. If this option is false, the nest mapping would + be expanded. Also see the description about "external" in requisite. + NOTE: Since visual selection is employed to check its region + intrinsically, only the mappings in visual mode are related. + + Default values + * query: 1 + * auto : 1 + + + +syntax + This is a list of syntax groups expected to be applied to the + surroundings. If this list is not empty, the textobjects check the + syntax to skip candidates. If syntax highlighting is not active, this + option is simply ignored. +> + " check the position pointed by # + " # # + " "foo" +< + For example, " is expected to be highlighted by several limited + highlight groups. +> + let g:sandwich#recipes += [ + \ { + \ 'buns': ['"', '"'], + \ 'quoteescape': 1, + \ 'expand_range': 0, + \ 'nesting': 0, + \ 'match_syntax': 1, + \ 'syntax': + \ ['Constant', 'Statement', 'Special', 'String', 'Comment'], + \ 'inner_syntax': + \ ['Constant', 'PreProc', 'Special', 'String', 'Comment'], + \ } + \ ] +< + However the recipe is not useful for some filetype, for example + markdown, while it really goes well with some filetypes to recognize + strings surrounded by "" even in the following situation. +> + key press vas" selects as following. # means cursor positions. + + <-#-> + list = ["foo", "bar"] + <-----#------> +< + Therefore, if possible, it would be better to prepare recipes for each + filetypes. +> + let g:sandwich#recipes += [ + \ { + \ 'buns': ['"', '"'], + \ 'filetype': ['ruby'], + \ 'quoteescape': 1, + \ 'expand_range': 0, + \ 'nesting': 0, + \ 'match_syntax': 1, + \ 'syntax': + \ ['Constant', 'Special', 'String', 'Comment'], + \ 'inner_syntax': + \ ['Constant', 'Special', 'String', 'Comment'], + \ } + \ ] + + let g:sandwich#recipes += [ + \ { + \ 'buns': ['"', '"'], + \ 'filetype': ['sh'], + \ 'quoteescape': 1, + \ 'expand_range': 0, + \ 'nesting': 0, + \ 'match_syntax': 1, + \ 'syntax': + \ ['Constant', 'Statement', 'String', 'Comment'], + \ 'inner_syntax': + \ ['Constant', 'Special', 'PreProc', 'String', 'Comment'], + \ } + \ ] +< + + In many colorscheme, frequently used highlight group name is shared. + See |group-name|. + + NOTE: If an item in the list exists in the syntax stack at the + candidate, it is regarded as the matched. The syntax stack can + be checked by using following keymapping on a text. +> + nnoremap s :echo map(synstack(line('.'), col('.')), + \ 'synIDattr(synIDtrans(v:val), "name")') +< + Default values + * query: [] + * auto : [] + + + +inner_syntax + This is a list of syntax groups expected to be applied to the + both edges of a surrounded text. If this list is not empty, the + textobjects check the syntax to skip candidates. If syntax + highlighting is not active, this option is simply ignored. + In addition, also when "match_syntax" option is 2, this option is + ignored. +> + let g:sandwich#recipes += [ + \ { + \ 'buns': ['"', '"'], + \ 'quoteescape': 1, + \ 'inner_syntax': ['Constant', 'String'] + \ } + \ ] + + " check the position pointed by # + " # # + " "foo" +< + There are practical examples in the above, local option "syntax", + section. Please see also them. + + In many colorscheme, frequently used highlight group name is shared. + See |group-name|. + + NOTE: If an item in the list exists in the syntax stack at the + candidate, it is regarded as the matched. The syntax stack can + be checked by using following keymapping on a text. +> + nnoremap s :echo map(synstack(line('.'), col('.')), + \ 'synIDattr(synIDtrans(v:val), "name")') +< + Default values + * query: [] + * auto : [] + + + +match_syntax + If this option is 1, the textobjects check the both surroundings + whether their syntaxes are matched or not. If not, the candidate is + skipped. In addition to that, if this option is 2, the textobjects + also check the both edge of the surrounded text whether their + colorings are same as those of surroundings. If this option is 3, + almost same as that is 3 but textobjects check the inside text + independently. That is to say, both surroundings should be same + colorings and both edge of surrounded text should be same colorings, + but it is not required to be same the colorings of surroundings and + that of the surrounded text. +> + AB CD + (foo) + + match_syntax is 1: A == D is required. + match_syntax is 2: A == D == B == C is required. + match_syntax is 3: A == D and B == C is required. +< + If a surrounding is not a character, textobjects check its head only. + + In many cases, both sides of surroundings have same syntaxes. For + example, '' is frequently used for the string literal, "" itself would + be applied Constant or String syntax and the inside text also applied + the same. +> + let g:sandwich#recipes += [ + \ { + \ 'buns': ["'", "'"], + \ 'quoteescape' : 1, + \ 'syntax' : ['Constant', 'String'], + \ 'match_syntax': 2 + \ } + \ ] +< + There are also other examples in the section of local option "syntax". + + NOTE: The applied (displayed) syntax can be checked by using the + following keymapping on a text. +> + nnoremap S + \ :echo synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'name') +< + The displayed syntaxes of both surroundings should be matched to + be valid. However, as for the case of insides the surroundings + the matched condition is somehow relaxed, only have to be + included in syntax stacks. Because, for instance, output + conversion specifiers in string literal like "%s" is highlighted + with different color. Even though the fact, "String" is expect + to included in its syntax stack, if it is true it works same as + other string literals. A syntax stack can be checked by using + the following key mapping on a text. +> + nnoremap s :echo map(synstack(line('.'), col('.')), + \ 'synIDattr(synIDtrans(v:val), "name")') +< + Default values + * query: 0 + * auto : 0 + + + +skip_break + Assume that the following text. +> + { + foo + } +< + The strings surrounded by { and } is "\n foo\n", thus key sequence + dib should simply give: +> + {} +< + However sometimes it is more useful to make like this. +> + { + + } +< + If this option is true, the textobjects skip the breakings at the both + ends to make the latter situation. In addition, skip following spaces + together to keep indentation. That is to say, skipped string would be + a string matched with the pattern "\n\\s*". + + This option is valid only for |(textobj-sandwich-query-i)| and + |(textobj-sandwich-auto-i)|. + + Default values + * query: 0 + * auto : 0 + + + +skip_expr + The value is a list of |expression|s. Searching the selection area, + every time when a text matched with buns is found the expressions are + evalueated. If the expression returns true (not 0), then the candidate + is skipped and textobject searches next. +> + " skip 'a's other than head and tail of a line. + let g:sandwich#recipes += [ + \ { + \ 'buns': ['a', 'a'], + \ 'skip_expr': [ + \ 'getpos(".")[2] != 1 && getpos(".")[2] != col([getpos(".")[1], "$"])-1' + \ ] + \ } + \ ] + " Press dib + " aaaaa ---> aa +< + Note that if the item is |Funcref|, it should be possible to accept + two arguments. +> + :function {func}(is_head, pos) +< + If a:is_head is 1, the textobject is searching a head surrounding + matched with buns. Otherwise the textobject is searching a tail + surrounding matched with buns. a:pos is a list pointing the head of + matched position. It has four items which is same format with the + argument of |getpos()|. Not necessarily to use them, use if needed. + In future, the arguments are possible to append. Thus it might be + better to assign variable-length argument. +> + " skip 'a's other than head and tail of a line. + function! SkipIntermediates(is_head, pos, ...) + if a:is_head + return a:pos[2] != 1 + else + return a:pos[2] != col([a:pos[1], '$'])-1 + endif + endfunction + + let g:sandwich#recipes += [ + \ { + \ 'buns': ['a', 'a'], + \ 'skip_expr': [function('SkipIntermediates')] + \ } + \ ] + " Press dib + " aaaaa ---> aa +< + + Default values + * query: [] + * auto : [] + + + +Global option~ + +There are several options to control fundamental behavior of the operators. + +g:textobj#sandwich#timeout *g:textobj#sandwich#timeout* + If this option is a falsy value, the query-textobject will wait for + subsequent inputs until the complete key sequence has been received to + specify a recipe. For example, with the following recipes, +> + let g:sandwich#recipes = [ + \ {'buns': ['for {', '}'], 'nesting': 1, 'input': ['bf']} + \ {'buns': ['if {', '}'], 'nesting': 1, 'input': ['bi']} + \ {'buns': ['else {', '}'], 'nesting': 1, 'input': ['be']} + \ ] +< + type `visb` and a while later the textobject eagerly select a text + wrapped in `b` if this option is true. The textobject wait next input + until a recipe is specified if this option is false. If this has not + been defined, |g:sandwich#timeout| is used instead. Unless there is + any particular reason, use |g:sandwich#timeout|. + See |g:textobj#sandwich#timeoutlen| also. + + + +g:textobj#sandwich#timeoutlen *g:textobj#sandwich#timeoutlen* + The time in milli seconds that waits for a key code or mapped key + sequence to complete. If there are recipes overlapped, this option is + used. Assume that the following recipes are prepared: +> + let g:sandwich#recipes = [ + \ {'buns': ['(', ')']} + \ {'buns': ['((', '))']} + \ ] +< + after pressing vis(, the textobject waits in the time. If you press + one more ( in the time, then a recipe for '((' and '))' is decided to + use. No keypress has come through the time a recipe for '(' and ')' is + settled. If this has not been defined, |g:sandwich#timeoutlen| is used + instead. Unless there is any particular reason, use + |g:sandwich#timeoutlen|. + + When 'timeout' is off or |g:textobj#sandwich#timeout| is false, this + option is ignored. + + + +g:textobj#sandwich#stimeoutlen *g:textobj#sandwich#stimeoutlen* + This is the time in milli second to give up searching candidates. + After passing over the time without finding enough number of + candidates, then the textobjects give up searching and selection. The + default value is 500. + + + +g:textobj#sandwich#latest_jump *g:textobj#sandwich#latest_jump* + If the value is true, the textobjects record the original cursor + position to the latest jump marker |``|. If the cursor moved + unexpectedly, |``| command will restore the cursor position quickly. + The default value is 1. + + + +============================================================================== +MISCELLANEOUS *textobj-sandwich-miscellaneous* + +It is not what I expect with key sequence dib.~ + The key sequence `dib` will make this: +> + ( + foo + ) +< + to this: +> + () +< + Because the surrouded text is not ' foo' but '\n foo\n'. If you + want a result like this: +> + ( + + ) +< + then you can control by local option "skip_break". +> + call textobj#sandwich#set('all', 'skip_break', 1) +< + + + +Timed out so frequently!~ + Set the |g:textobj#sandwich#stimeoutlen| larger. The default value is + 500. + + + +My cursor moved a lot because of unintended selection!~ + Don't panic. You can use undo |u| command, and you can go back to the + original position by |``| command. See + |g:textobj#sandwich#latest_jump|. + + + +============================================================================== +vim:tw=78:ts=8:ft=help:norl:noet: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/macros/sandwich/ftplugin/tex.vim b/config/neovim/store/lazy-plugins/vim-sandwich/macros/sandwich/ftplugin/tex.vim new file mode 100644 index 00000000..24321452 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/macros/sandwich/ftplugin/tex.vim @@ -0,0 +1,176 @@ +scriptencoding utf-8 +if !exists('s:local_recipes') + let s:local_recipes = [ + \ {'__filetype__': 'tex', 'buns': ['“', '”'], 'nesting': 1, 'input': [ 'u"' ]}, + \ {'__filetype__': 'tex', 'buns': ['„', '“'], 'nesting': 1, 'input': [ 'U"', 'ug', 'u,' ]}, + \ {'__filetype__': 'tex', 'buns': ['«', '»'], 'nesting': 1, 'input': [ 'u<', 'uf' ]}, + \ {'__filetype__': 'tex', 'buns': ["`", "'"], 'nesting': 1, 'input': [ "l'", "l`" ]}, + \ {'__filetype__': 'tex', 'buns': ["``", "''"], 'nesting': 1, 'input': [ 'l"' ]}, + \ {'__filetype__': 'tex', 'buns': ['"`', "\"'"], 'nesting': 1, 'input': [ 'L"' ]}, + \ {'__filetype__': 'tex', 'buns': [",,", "``"], 'nesting': 1, 'input': [ 'l,' ]}, + \ {'__filetype__': 'tex', 'buns': ['<<', '>>'], 'nesting': 1, 'input': [ 'l<' ]}, + \ {'__filetype__': 'tex', 'buns': ['$', '$'], 'nesting': 0}, + \ {'__filetype__': 'tex', 'buns': ['\(', '\)'], 'nesting': 1, 'input': [ '\(' ], 'indentkeys-': '{,},0{,0}'}, + \ {'__filetype__': 'tex', 'buns': ['\{', '\}'], 'nesting': 1, 'input': [ '\{' ], 'indentkeys-': '{,},0{,0}'}, + \ {'__filetype__': 'tex', 'buns': ['\[', '\]'], 'nesting': 1, 'input': [ '\[' ], 'indentkeys-': '{,},0{,0}'}, + \ {'__filetype__': 'tex', 'buns': ['\left(', '\right)'], 'nesting': 1, 'input': [ 'm(' ], 'action': ['add'], 'indentkeys-': '(,)'}, + \ {'__filetype__': 'tex', 'buns': ['\left[', '\right]'], 'nesting': 1, 'input': [ 'm[' ], 'action': ['add'], 'indentkeys-': '[,]'}, + \ {'__filetype__': 'tex', 'buns': ['\left|', '\right|'], 'nesting': 1, 'input': [ 'm|' ], 'action': ['add']}, + \ {'__filetype__': 'tex', 'buns': ['\left\{', '\right\}'], 'nesting': 1, 'input': [ 'm{' ], 'action': ['add'], 'indentkeys-': '{,},0{,0}'}, + \ {'__filetype__': 'tex', 'buns': ['\left\langle ', '\right\rangle '], 'nesting': 1, 'input': [ 'm<' ], 'action': ['add']}, + \ {'__filetype__': 'tex', 'buns': ['\bigl(', '\bigr)'], 'nesting': 1, 'input': [ 'M(' ], 'action': ['add'], 'indentkeys-': '(,)'}, + \ {'__filetype__': 'tex', 'buns': ['\bigl[', '\bigr]'], 'nesting': 1, 'input': [ 'M[' ], 'action': ['add'], 'indentkeys-': '[,]'}, + \ {'__filetype__': 'tex', 'buns': ['\bigl|', '\bigr|'], 'nesting': 1, 'input': [ 'M|' ], 'action': ['add']}, + \ {'__filetype__': 'tex', 'buns': ['\bigl\{', '\bigr\}'], 'nesting': 1, 'input': [ 'M{' ], 'action': ['add'], 'indentkeys-': '{,},0{,0}'}, + \ {'__filetype__': 'tex', 'buns': ['\bigl\langle ', '\bigr\rangle '], 'nesting': 1, 'input': [ 'M<' ], 'action': ['add']}, + \ { + \ '__filetype__': 'tex', + \ 'buns' : ['\begingroup', '\endgroup'], + \ 'nesting' : 1, + \ 'input': ['gr', '\gr'], + \ 'linewise': 1, + \ }, + \ { + \ '__filetype__': 'tex', + \ 'buns' : ['\toprule', '\bottomrule'], + \ 'nesting' : 1, + \ 'input': ['tr', '\tr', 'br', '\br'], + \ 'linewise': 1, + \ }, + \ { + \ '__filetype__': 'tex', + \ 'buns' : 'sandwich#filetype#tex#CmdInput()', + \ 'kind' : ['add', 'replace'], + \ 'action' : ['add'], + \ 'listexpr': 1, + \ 'nesting' : 1, + \ 'input' : ['c'], + \ 'indentkeys-' : '{,},0{,0}', + \ }, + \ { + \ '__filetype__': 'tex', + \ 'buns' : 'sandwich#filetype#tex#EnvInput()', + \ 'kind' : ['add', 'replace'], + \ 'action' : ['add'], + \ 'listexpr': 1, + \ 'nesting' : 1, + \ 'linewise' : 1, + \ 'input' : ['e'], + \ 'indentkeys-' : '{,},0{,0}', + \ 'autoindent' : 0, + \ }, + \ { + \ '__filetype__': 'tex', + \ 'buns' : ['\\\a\+\*\?{', '}'], + \ 'kind' : ['delete', 'replace', 'auto', 'query'], + \ 'regex' : 1, + \ 'nesting' : 1, + \ 'input' : ['c'], + \ 'indentkeys-' : '{,},0{,0}', + \ }, + \ { + \ '__filetype__': 'tex', + \ 'buns' : ['\\begin{[^}]*}\%(\[.*\]\)\?', '\\end{[^}]*}'], + \ 'kind' : ['delete', 'replace', 'auto', 'query'], + \ 'regex' : 1, + \ 'nesting' : 1, + \ 'linewise' : 1, + \ 'input' : ['e'], + \ 'indentkeys-' : '{,},0{,0}', + \ 'autoindent' : 0, + \ }, + \ { + \ '__filetype__': 'tex', + \ 'external': ["\(textobj-sandwich-filetype-tex-marks-i)", "\(textobj-sandwich-filetype-tex-marks-a)"], + \ 'kind' : ['delete', 'replace', 'auto', 'query'], + \ 'noremap' : 0, + \ 'input' : ['ma'], + \ 'indentkeys': '{,},0{,0}', + \ 'autoindent': 0, + \ }, + \ ] + + xnoremap (textobj-sandwich-filetype-tex-marks-i) textobj#sandwich#auto('x', 'i', {'synchro': 0}, b:sandwich_tex_marks_recipes) + xnoremap (textobj-sandwich-filetype-tex-marks-a) textobj#sandwich#auto('x', 'a', {'synchro': 0}, b:sandwich_tex_marks_recipes) + let s:marks_recipes = [] + let s:marks_recipes += [ + \ { + \ 'buns': ['\%([[(]\|\\{\)', '\%([])]\|\\}\)'], + \ 'regex': 1, + \ 'nesting': 1, + \ }, + \ { + \ 'buns': ['|', '|'], + \ 'nesting': 0, + \ }, + \ { + \ 'buns': ['\m\C\\[Bb]igg\?l|', '\m\C\\[Bb]igg\?r|'], + \ 'regex': 1, + \ 'nesting': 1, + \ }, + \ { + \ 'buns': ['\m\C\\\%(langle\|lVert\|lvert\|lceil\|lfloor\)', '\m\C\\\%(rangle\|rVert\|rvert\|rceil\|rfloor\)'], + \ 'regex': 1, + \ 'nesting': 1, + \ }, + \ { + \ 'buns': ['\m\C\\left\%([[(|.]\|\\{\|\\langle\|\\lVert\|\\lvert\|\\lceil\|\\lfloor\)', '\m\C\\right\%([])|.]\|\\}\|\\rangle\|\\rVert\|\\rvert\|\\rceil\|\\rfloor\)'], + \ 'regex': 1, + \ 'nesting': 1, + \ }, + \ ] + " NOTE: It is not reasonable to set 'nesting' on when former and latter surrounds are same. + let s:marks_recipes += [ + \ { + \ 'buns': ['\m\C\\[Bb]igg\?|', '\m\C\\[Bb]igg\?|'], + \ 'regex': 1, + \ 'nesting': 0, + \ }, + \ ] + " NOTE: The existence of '\big.' makes the situation tricky. + " Try to search those two cases independently and adopt the nearest item. + " \big. foo \big) + " \big( foo \big. + " This roundabout enables the following: + " \big( foo \big. bar \big. baz \big) + " When the cursor is on; + " foo -> \big( and \big. + " bar -> nothing + " foo -> \big. and \big) + " were deleted by the input 'sdma'. + let s:marks_recipes += [ + \ { + \ 'buns': ['\m\C\\[Bb]igg\?l\?\%([[(]\|\\{\|\\langle\|\\lVert\|\\lvert\|\\lceil\|\\lfloor\)', '\m\C\\[Bb]igg\?r\?\%([]).]\|\\}\|\\rangle\|\\rVert\|\\rvert\|\\rceil\|\\rfloor\)'], + \ 'regex': 1, + \ 'nesting': 1, + \ }, + \ { + \ 'buns': ['\m\C\\[Bb]igg\?l\?\%([[(.]\|\\{\|\\langle\|\\lVert\|\\lvert\|\\lceil\|\\lfloor\)', '\m\C\\[Bb]igg\?r\?\%([])]\|\\}\|\\rangle\|\\rVert\|\\rvert\|\\rceil\|\\rfloor\)'], + \ 'regex': 1, + \ 'nesting': 1, + \ }, + \ { + \ 'buns': ['\m\C\\[Bb]igg\?|', '\m\C\\[Bb]igg\?.'], + \ 'regex': 1, + \ 'nesting': 0, + \ }, + \ { + \ 'buns': ['\m\C\\[Bb]igg\?.', '\m\C\\[Bb]igg\?|'], + \ 'regex': 1, + \ 'nesting': 0, + \ }, + \ { + \ 'buns': ['\m\C\\[Bb]igg\?l|', '\m\C\\[Bb]igg\?r[|.]'], + \ 'regex': 1, + \ 'nesting': 1, + \ }, + \ { + \ 'buns': ['\m\C\\[Bb]igg\?l[|.]', '\m\C\\[Bb]igg\?r|'], + \ 'regex': 1, + \ 'nesting': 1, + \ }, + \ ] +endif +call sandwich#util#insertlocal(s:local_recipes) +let b:sandwich_tex_marks_recipes = deepcopy(s:marks_recipes) + diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/macros/sandwich/keymap/surround.vim b/config/neovim/store/lazy-plugins/vim-sandwich/macros/sandwich/keymap/surround.vim new file mode 100644 index 00000000..0a24f91f --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/macros/sandwich/keymap/surround.vim @@ -0,0 +1,200 @@ +let g:sandwich_no_default_key_mappings = 1 +let g:operator_sandwich_no_default_key_mappings = 1 +let g:textobj_sandwich_no_default_key_mappings = 1 + +nmap ys (sandwich-add) +onoremap line :normal! ^vg_ +nmap yss (sandwich-add)line +onoremap gul g_ +nmap yS (sandwich-add)gul + +nmap ds (sandwich-delete) +nmap dss (sandwich-delete-auto) +nmap cs (sandwich-replace) +nmap css (sandwich-replace-auto) + +xmap S (sandwich-add) + +runtime autoload/repeat.vim +if hasmapto('(RepeatDot)') + nmap . (operator-sandwich-predot)(RepeatDot) +else + nmap . (operator-sandwich-dot) +endif + +" Default recipes +let g:sandwich#recipes = [ + \ { + \ 'buns': ['\s\+', '\s\+'], + \ 'regex': 1, + \ 'kind': ['delete', 'replace', 'query'], + \ 'input': [' '] + \ }, + \ + \ { + \ 'buns': ['', ''], + \ 'action': ['add'], + \ 'motionwise': ['line'], + \ 'linewise': 1, + \ 'input': ["\"] + \ }, + \ + \ { + \ 'buns': ['^$', '^$'], + \ 'regex': 1, + \ 'linewise': 1, + \ 'input': ["\"] + \ }, + \ + \ { + \ 'buns': ['<', '>'], + \ 'expand_range': 0, + \ 'input': ['>', 'a'], + \ }, + \ + \ { + \ 'buns': ['`', '`'], + \ 'quoteescape': 1, + \ 'expand_range': 0, + \ 'nesting': 0, + \ 'linewise': 0, + \ }, + \ + \ { + \ 'buns': ['"', '"'], + \ 'quoteescape': 1, + \ 'expand_range': 0, + \ 'nesting': 0, + \ 'linewise': 0, + \ }, + \ + \ { + \ 'buns': ["'", "'"], + \ 'quoteescape': 1, + \ 'expand_range': 0, + \ 'nesting': 0, + \ 'linewise': 0, + \ }, + \ + \ { + \ 'buns': ['{', '}'], + \ 'nesting': 1, + \ 'skip_break': 1, + \ 'input': ['{', '}', 'B'], + \ }, + \ + \ { + \ 'buns': ['[', ']'], + \ 'nesting': 1, + \ 'input': ['[', ']', 'r'], + \ }, + \ + \ { + \ 'buns': ['(', ')'], + \ 'nesting': 1, + \ 'input': ['(', ')', 'b'], + \ }, + \ + \ { + \ 'buns': 'sandwich#magicchar#t#tag()', + \ 'listexpr': 1, + \ 'kind': ['add'], + \ 'action': ['add'], + \ 'input': ['t', 'T'], + \ }, + \ + \ { + \ 'buns': 'sandwich#magicchar#t#tag()', + \ 'listexpr': 1, + \ 'kind': ['replace'], + \ 'action': ['add'], + \ 'input': ['T', '<'], + \ }, + \ + \ { + \ 'buns': 'sandwich#magicchar#t#tagname()', + \ 'listexpr': 1, + \ 'kind': ['replace'], + \ 'action': ['add'], + \ 'input': ['t'], + \ }, + \ + \ { + \ 'external': ["\(textobj-sandwich-tag-i)", "\(textobj-sandwich-tag-a)"], + \ 'noremap': 0, + \ 'kind': ['delete', 'textobj'], + \ 'expr_filter': ['operator#sandwich#kind() !=# "replace"'], + \ 'linewise': 1, + \ 'input': ['t', 'T', '<'], + \ }, + \ + \ { + \ 'external': ["\(textobj-sandwich-tag-i)", "\(textobj-sandwich-tag-a)"], + \ 'noremap': 0, + \ 'kind': ['replace', 'query'], + \ 'expr_filter': ['operator#sandwich#kind() ==# "replace"'], + \ 'input': ['T', '<'], + \ }, + \ + \ { + \ 'external': ["\(textobj-sandwich-tagname-i)", "\(textobj-sandwich-tagname-a)"], + \ 'noremap': 0, + \ 'kind': ['replace', 'textobj'], + \ 'expr_filter': ['operator#sandwich#kind() ==# "replace"'], + \ 'input': ['t'], + \ }, + \ + \ { + \ 'buns': ['sandwich#magicchar#f#fname()', '")"'], + \ 'kind': ['add', 'replace'], + \ 'action': ['add'], + \ 'expr': 1, + \ 'input': ['f'] + \ }, + \ + \ { + \ 'external': ["\(textobj-sandwich-function-ip)", "\(textobj-sandwich-function-i)"], + \ 'noremap': 0, + \ 'kind': ['delete', 'replace', 'query'], + \ 'input': ['f'] + \ }, + \ + \ { + \ 'external': ["\(textobj-sandwich-function-ap)", "\(textobj-sandwich-function-a)"], + \ 'noremap': 0, + \ 'kind': ['delete', 'replace', 'query'], + \ 'input': ['F'] + \ }, + \ + \ { + \ 'buns': 'sandwich#magicchar#i#input("operator")', + \ 'kind': ['add', 'replace'], + \ 'action': ['add'], + \ 'listexpr': 1, + \ 'input': ['i'], + \ }, + \ + \ { + \ 'buns': 'sandwich#magicchar#i#input("textobj", 1)', + \ 'kind': ['delete', 'replace', 'query'], + \ 'listexpr': 1, + \ 'regex': 1, + \ 'input': ['i'], + \ }, + \ + \ { + \ 'buns': 'sandwich#magicchar#i#lastinput("operator", 1)', + \ 'kind': ['add', 'replace'], + \ 'action': ['add'], + \ 'listexpr': 1, + \ 'input': ['I'], + \ }, + \ + \ { + \ 'buns': 'sandwich#magicchar#i#lastinput("textobj")', + \ 'kind': ['delete', 'replace', 'query'], + \ 'listexpr': 1, + \ 'regex': 1, + \ 'input': ['I'], + \ }, + \ ] diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/plugin/operator/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/plugin/operator/sandwich.vim new file mode 100644 index 00000000..cfbded44 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/plugin/operator/sandwich.vim @@ -0,0 +1,60 @@ +" The vim operator plugin to do well with 'sandwich' like structure +" Last Change: 31-Oct-2021. +" Maintainer : Masaaki Nakamura + +" License : NYSL +" Japanese +" English (Unofficial) + +if &compatible || exists("g:loaded_operator_sandwich") + finish +endif +let g:loaded_operator_sandwich = 1 + +" keymappings +nmap (operator-sandwich-add) (operator-sandwich-add-pre)(operator-sandwich-g@) +xmap (operator-sandwich-add) (operator-sandwich-add-pre)(operator-sandwich-gv)(operator-sandwich-g@) +omap (operator-sandwich-add) (operator-sandwich-g@) +nmap (operator-sandwich-delete) (operator-sandwich-delete-pre)(operator-sandwich-g@) +xmap (operator-sandwich-delete) (operator-sandwich-delete-pre)(operator-sandwich-gv)(operator-sandwich-g@) +omap (operator-sandwich-delete) (operator-sandwich-g@) +nmap (operator-sandwich-replace) (operator-sandwich-replace-pre)(operator-sandwich-g@) +xmap (operator-sandwich-replace) (operator-sandwich-replace-pre)(operator-sandwich-gv)(operator-sandwich-g@) +omap (operator-sandwich-replace) (operator-sandwich-g@) + +nnoremap (operator-sandwich-add-pre) :call operator#sandwich#prerequisite('add', 'n') +xnoremap (operator-sandwich-add-pre) :call operator#sandwich#prerequisite('add', 'x') +nnoremap (operator-sandwich-delete-pre) :call operator#sandwich#prerequisite('delete', 'n') +xnoremap (operator-sandwich-delete-pre) :call operator#sandwich#prerequisite('delete', 'x') +nnoremap (operator-sandwich-replace-pre) :call operator#sandwich#prerequisite('replace', 'n') +xnoremap (operator-sandwich-replace-pre) :call operator#sandwich#prerequisite('replace', 'x') + +nnoremap (operator-sandwich-add-query1st) :call operator#sandwich#query1st('add', 'n') +xnoremap (operator-sandwich-add-query1st) :call operator#sandwich#query1st('add', 'x') +nnoremap (operator-sandwich-replace-query1st) :call operator#sandwich#query1st('replace', 'n') +xnoremap (operator-sandwich-replace-query1st) :call operator#sandwich#query1st('replace', 'x') + +" supplementary keymappings +onoremap (operator-sandwich-synchro-count) operator#sandwich#synchro_count() +onoremap (operator-sandwich-release-count) operator#sandwich#release_count() +onoremap (operator-sandwich-squash-count) operator#sandwich#squash_count() +nnoremap (operator-sandwich-predot) operator#sandwich#predot() +nnoremap (operator-sandwich-dot) operator#sandwich#dot() + +" visualrepeat.vim (vimscript #3848) support +noremap (operator-sandwich-add-visualrepeat) :call operator#sandwich#visualrepeat('add') +noremap (operator-sandwich-delete-visualrepeat) :call operator#sandwich#visualrepeat('delete') +noremap (operator-sandwich-replace-visualrepeat) :call operator#sandwich#visualrepeat('replace') + +" intrinsic keymappings +noremap (operator-sandwich-g@) g@ +inoremap (operator-sandwich-g@) g@ +nnoremap (operator-sandwich-gv) gv +inoremap (operator-sandwich-gv) gv + +" use of vim-event-DotCommandPre +if !hasmapto('(operator-sandwich-predot)') && !hasmapto('(operator-sandwich-dot)') && (hasmapto('(event-DotCommandPre)') || hasmapto('(event-DotCommandPre+Dot)')) + augroup sandwich-predot + autocmd User DotCommandPre call operator#sandwich#predot() + augroup END +endif diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/plugin/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/plugin/sandwich.vim new file mode 100644 index 00000000..6047ab75 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/plugin/sandwich.vim @@ -0,0 +1,75 @@ +" The set of operator/textobj plugins to do well with 'sandwich' like structure +" Last Change: 18-Jan-2022. +" Maintainer : Masaaki Nakamura + +" License : NYSL +" Japanese +" English (Unofficial) + +if &compatible || exists("g:loaded_sandwich") + finish +endif +let g:loaded_sandwich = 1 + +" intrinsic keymappings +onoremap (textobj-sandwich-function-ip) :call sandwich#magicchar#f#ip('o') +onoremap (textobj-sandwich-function-i) :call sandwich#magicchar#f#i('o') +xnoremap (textobj-sandwich-function-ip) :call sandwich#magicchar#f#ip('x') +xnoremap (textobj-sandwich-function-i) :call sandwich#magicchar#f#i('x') +onoremap (textobj-sandwich-function-ap) :call sandwich#magicchar#f#ap('o') +onoremap (textobj-sandwich-function-a) :call sandwich#magicchar#f#a('o') +xnoremap (textobj-sandwich-function-ap) :call sandwich#magicchar#f#ap('x') +xnoremap (textobj-sandwich-function-a) :call sandwich#magicchar#f#a('x') +onoremap (textobj-sandwich-tagname-i) :call sandwich#magicchar#t#i() +onoremap (textobj-sandwich-tagname-a) :call sandwich#magicchar#t#a() +xnoremap (textobj-sandwich-tagname-i) :call sandwich#magicchar#t#i() +xnoremap (textobj-sandwich-tagname-a) :call sandwich#magicchar#t#a() +onoremap (textobj-sandwich-tag-i) :call sandwich#magicchar#t#it() +onoremap (textobj-sandwich-tag-a) :call sandwich#magicchar#t#at() +xnoremap (textobj-sandwich-tag-i) :call sandwich#magicchar#t#it() +xnoremap (textobj-sandwich-tag-a) :call sandwich#magicchar#t#at() + +nmap (sandwich-add) (operator-sandwich-add) +xmap (sandwich-add) (operator-sandwich-add) +omap (sandwich-add) (operator-sandwich-add) +nmap (sandwich-delete) (operator-sandwich-delete)(operator-sandwich-release-count)(textobj-sandwich-query-a) +xmap (sandwich-delete) (operator-sandwich-delete) +nmap (sandwich-replace) (operator-sandwich-replace)(operator-sandwich-release-count)(textobj-sandwich-query-a) +xmap (sandwich-replace) (operator-sandwich-replace) +nmap (sandwich-delete-auto) (operator-sandwich-delete)(operator-sandwich-release-count)(textobj-sandwich-auto-a) +nmap (sandwich-replace-auto) (operator-sandwich-replace)(operator-sandwich-release-count)(textobj-sandwich-auto-a) + +""" default keymappings +" If g:sandwich_no_default_key_mappings has been defined, then quit immediately. +if exists('g:sandwich_no_default_key_mappings') | finish | endif + +if !exists('g:operator_sandwich_no_default_key_mappings') + " add + silent! nmap sa (sandwich-add) + silent! xmap sa (sandwich-add) + silent! omap sa (sandwich-add) + + " delete + silent! nmap sd (sandwich-delete) + silent! xmap sd (sandwich-delete) + silent! nmap sdb (sandwich-delete-auto) + + " replace + silent! nmap sr (sandwich-replace) + silent! xmap sr (sandwich-replace) + silent! nmap srb (sandwich-replace-auto) +endif + +if !exists('g:textobj_sandwich_no_default_key_mappings') + " auto + silent! omap ib (textobj-sandwich-auto-i) + silent! xmap ib (textobj-sandwich-auto-i) + silent! omap ab (textobj-sandwich-auto-a) + silent! xmap ab (textobj-sandwich-auto-a) + + " query + silent! omap is (textobj-sandwich-query-i) + silent! xmap is (textobj-sandwich-query-i) + silent! omap as (textobj-sandwich-query-a) + silent! xmap as (textobj-sandwich-query-a) +endif diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/plugin/textobj/sandwich.vim b/config/neovim/store/lazy-plugins/vim-sandwich/plugin/textobj/sandwich.vim new file mode 100644 index 00000000..a11d0000 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/plugin/textobj/sandwich.vim @@ -0,0 +1,33 @@ +" The vim textobject plugin to search and select 'sandwich' like structure +" Last Change: 30-Oct-2021. +" Maintainer : Masaaki Nakamura + +" License : NYSL +" Japanese +" English (Unofficial) + +if exists("g:loaded_textobj_sandwich") + finish +endif +let g:loaded_textobj_sandwich = 1 + +nnoremap (textobj-sandwich-auto-i) textobj#sandwich#auto('n', 'i') +onoremap (textobj-sandwich-auto-i) textobj#sandwich#auto('o', 'i') +xnoremap (textobj-sandwich-auto-i) textobj#sandwich#auto('x', 'i') +nnoremap (textobj-sandwich-auto-a) textobj#sandwich#auto('n', 'a') +onoremap (textobj-sandwich-auto-a) textobj#sandwich#auto('o', 'a') +xnoremap (textobj-sandwich-auto-a) textobj#sandwich#auto('x', 'a') + +nnoremap (textobj-sandwich-query-i) textobj#sandwich#query('n', 'i') +onoremap (textobj-sandwich-query-i) textobj#sandwich#query('o', 'i') +xnoremap (textobj-sandwich-query-i) textobj#sandwich#query('x', 'i') +nnoremap (textobj-sandwich-query-a) textobj#sandwich#query('n', 'a') +onoremap (textobj-sandwich-query-a) textobj#sandwich#query('o', 'a') +xnoremap (textobj-sandwich-query-a) textobj#sandwich#query('x', 'a') + +nnoremap (textobj-sandwich-literal-query-i) textobj#sandwich#query('n', 'i', {}, []) +onoremap (textobj-sandwich-literal-query-i) textobj#sandwich#query('o', 'i', {}, []) +xnoremap (textobj-sandwich-literal-query-i) textobj#sandwich#query('x', 'i', {}, []) +nnoremap (textobj-sandwich-literal-query-a) textobj#sandwich#query('n', 'a', {}, []) +onoremap (textobj-sandwich-literal-query-a) textobj#sandwich#query('o', 'a', {}, []) +xnoremap (textobj-sandwich-literal-query-a) textobj#sandwich#query('x', 'a', {}, []) diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/.themisrc b/config/neovim/store/lazy-plugins/vim-sandwich/test/.themisrc new file mode 100644 index 00000000..bcb98ffb --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/.themisrc @@ -0,0 +1,65 @@ +set encoding=utf-8 +execute 'set runtimepath+=' . expand(':p:h:h') +runtime! plugin/*.vim +runtime! plugin/**/*.vim +set noswapfile +let g:operator_sandwich_no_visualrepeat = 1 + +let g:assert = themis#helper('assert') + +function! TextobjCoord(l1, c1, l2, c2) abort + normal! v + call cursor(a:l1, a:c1) + normal! o + call cursor(a:l2, a:c2) +endfunction + +function! TextobjFail() abort +endfunction + +function! TestIndent() abort + " always the indent of the previous nonbland line + shiftwidth() + return indent(prevnonblank(v:lnum)) + shiftwidth() +endfunction + +function! SandwichExprCancel() abort + throw 'OperatorSandwichCancel' +endfunction + +function! SandwichExprEmpty() abort + return '' +endfunction + +function! SandwichExprBuns(is_head) abort + if a:is_head + return 'head' + else + return 'tail' + endif +endfunction + +function! SandwichListexprEmpty(which) abort + if a:which ==# 'former' + return ['', 'bar'] + elseif a:which ==# 'latter' + return ['foo', ''] + else + return ['', ''] + endif +endfunction + +function! SandwichListexprBuns(cancel) abort + if a:cancel + throw 'OperatorSandwichCancel' + else + return ['foo', 'baz'] + endif +endfunction + +function! SandwichSkipIntermediate(is_head, pos, ...) abort + if a:is_head + return !(a:pos[2] == 1) + else + return !(a:pos[2] == col([a:pos[1], '$'])-1) + endif +endfunction diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/README b/config/neovim/store/lazy-plugins/vim-sandwich/test/README new file mode 100644 index 00000000..365ef961 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/README @@ -0,0 +1,6 @@ +Use themis.vim (https://github.com/thinca/vim-themis) + +1. git clone https://github.com/thinca/vim-themis.git +2. cd path/to/vim-sandwich +3. path/to/vim-themis/bin/themis (Linux) + path/to/vim-themis/bin/themis.bat (Windows) diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/dot-repeat/test_dot.bat b/config/neovim/store/lazy-plugins/vim-sandwich/test/dot-repeat/test_dot.bat new file mode 100644 index 00000000..42fc76ac --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/dot-repeat/test_dot.bat @@ -0,0 +1,11 @@ +@echo off +set VIM=vim +if defined THEMIS_VIM set VIM=%THEMIS_VIM% + +%VIM% -u NONE -i NONE -N -n -e -s -S %~dp0\test_dot.vim +if %errorlevel% neq 0 goto ERROR +echo Succeeded. +exit /b 0 + +:ERROR +exit /b 1 diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/dot-repeat/test_dot.sh b/config/neovim/store/lazy-plugins/vim-sandwich/test/dot-repeat/test_dot.sh new file mode 100644 index 00000000..8c861a0d --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/dot-repeat/test_dot.sh @@ -0,0 +1,15 @@ +#! /bin/sh + +SCRIPT_HOME=$0 +if [ -n "`readlink $SCRIPT_HOME`" ] ; then + SCRIPT_HOME="`readlink $SCRIPT_HOME`" +fi +SCRIPT_HOME="`dirname $SCRIPT_HOME`" + +VIM=vim +if [ -n "$THEMIS_VIM" ] ; then + VIM="$THEMIS_VIM" +fi + +$VIM -u NONE -i NONE -N -n -e -s -S $SCRIPT_HOME/test_dot.vim || exit 1 +echo "Succeeded." diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/dot-repeat/test_dot.vim b/config/neovim/store/lazy-plugins/vim-sandwich/test/dot-repeat/test_dot.vim new file mode 100644 index 00000000..5770cb0f --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/dot-repeat/test_dot.vim @@ -0,0 +1,751 @@ +if has('win16') || has('win32') || has('win64') || has('win95') + set shellslash +endif +execute 'set runtimepath+=' . expand(':p:h:h:h') +source :p:h:h:h/plugin/operator/sandwich.vim +source :p:h:h:h/plugin/textobj/sandwich.vim +source :p:h:h:h/plugin/sandwich.vim +nnoremap (test-dot) . +let g:operator_sandwich_no_visualrepeat = 1 + +function! s:assert(a1, a2, kind) abort + if type(a:a1) == type(a:a2) && string(a:a1) ==# string(a:a2) + return + endif + + %delete + call append(0, ['Got:', string(a:a1)]) + call append(0, [printf('Failured at "%s"', a:kind), '', 'Expect:', string(a:a2)]) + $delete + 1,$print + cquit +endfunction + +function! s:quit_by_error() abort + %delete + call append(0, [printf('Catched the following error at %s.', v:throwpoint), v:exception]) + $delete + 1,$print + cquit +endfunction + +function! Count(...) abort + let s:count += 1 + return s:count +endfunction + +function! ListCount(...) abort + let s:count += 1 + return [s:count, s:count] +endfunction + + + +try + +""" operator-add +" normal use +call setline('.', 'foo') +normal saiw( +normal . +call s:assert(getline('.'), '((foo))', 'operator-add:normal use #1') +call s:assert(getpos('.'), [0, 1, 3, 0], 'operator-add:normal use #2') + +normal saiw[ +normal . +call s:assert(getline('.'), '(([[foo]]))', 'operator-add:normal use #3') +call s:assert(getpos('.'), [0, 1, 5, 0], 'operator-add:normal use #4') + +%delete + +" blockwise-visual +call append(0, ['foo', 'bar', 'baz']) +$delete +execute "normal gg\2j2lsa(" +normal . +call s:assert(getline(1), '((foo))', 'operator-add:blockwise-visual #1') +call s:assert(getline(2), '((bar))', 'operator-add:blockwise-visual #2') +call s:assert(getline(3), '((baz))', 'operator-add:blockwise-visual #3') +call s:assert(getpos('.'), [0, 1, 3, 0], 'operator-add:blockwise-visual #4') + +normal j. +call s:assert(getline(1), '((foo))', 'operator-add:blockwise-visual #5') +call s:assert(getline(2), '(((bar)))', 'operator-add:blockwise-visual #6') +call s:assert(getline(3), '(((baz)))', 'operator-add:blockwise-visual #7') +call s:assert(getpos('.'), [0, 2, 4, 0], 'operator-add:blockwise-visual #8') + +normal j. +call s:assert(getline(1), '((foo))', 'operator-add:blockwise-visual #9') +call s:assert(getline(2), '(((bar)))', 'operator-add:blockwise-visual #10') +call s:assert(getline(3), '((((baz))))', 'operator-add:blockwise-visual #11') +call s:assert(getpos('.'), [0, 3, 5, 0], 'operator-add:blockwise-visual #12') + +%delete + +" count +call setline('.', 'foo') +normal 2saiw(( +normal . +call s:assert(getline('.'), '((((foo))))', 'operator-add:count #1') +call s:assert(getpos('.'), [0, 1, 5, 0], 'operator-add:count #2') + +call setline('.', 'foo') +normal saiw( +call setline('.', 'foo bar') +normal 3. +call s:assert(getline('.'), '(foo bar)', 'operator-add:count #3') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-add:count #4') + +%delete + +" expr +let g:sandwich#recipes = [] +let g:operator#sandwich#recipes = [{'buns': ['Count()', 'Count()'], 'expr': 1, 'input': ['c']}] +call setline('.', 'foo') +let s:count = 0 +normal saiwc +normal . +call s:assert(getline('.'), '11foo22', 'operator-add:expr #1') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-add:expr #2') + +let g:operator#sandwich#recipes = [{'buns': ['Count()', 'Count()'], 'expr': 2, 'input': ['c']}] +call setline('.', 'foo') +let s:count = 0 +normal saiwc +normal . +call s:assert(getline('.'), '31foo24', 'operator-add:expr #3') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-add:expr #4') + +unlet g:sandwich#recipes +unlet g:operator#sandwich#recipes +%delete + +" listexpr +let g:sandwich#recipes = [] +let g:operator#sandwich#recipes = [{'buns': 'ListCount()', 'listexpr': 1, 'input': ['c']}] +call setline('.', 'foo') +let s:count = 0 +normal saiwc +normal . +call s:assert(getline('.'), '11foo11', 'operator-add:listexpr #1') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-add:listexpr #2') + +let g:operator#sandwich#recipes = [{'buns': 'ListCount()', 'listexpr': 2, 'input': ['c']}] +call setline('.', 'foo') +let s:count = 0 +normal saiwc +normal . +call s:assert(getline('.'), '21foo12', 'operator-add:listexpr #3') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-add:listexpr #4') + +unlet g:sandwich#recipes +unlet g:operator#sandwich#recipes +%delete + +" cursor option 'keep' +call operator#sandwich#set('add', 'all', 'cursor', 'keep') +nmap . (operator-sandwich-dot) +call setline('.', 'foo') +normal 0lsaiw( +normal . +call s:assert(getline('.'), '((foo))', 'operator-add:cursor keep #1') +call s:assert(getpos('.'), [0, 1, 4, 0], 'operator-add:cursor keep #2') + +call setline('.', 'foo') +normal 0lsaiw( +call setline('.', 'foo bar') +normal 0l3. +call s:assert(getline('.'), '(foo bar)', 'operator-add:cursor keep #3') +call s:assert(getpos('.'), [0, 1, 3, 0], 'operator-add:cursor keep #4') + +nmap . (operator-sandwich-predot)(test-dot) +call setline('.', 'foo') +normal 0lsaiw( +normal . +call s:assert(getline('.'), '((foo))', 'operator-add:cursor keep #5') +call s:assert(getpos('.'), [0, 1, 4, 0], 'operator-add:cursor keep #6') + +call setline('.', 'foo') +normal 0lsaiw( +call setline('.', 'foo bar') +normal 0l3. +call s:assert(getline('.'), '(foo bar)', 'operator-add:cursor keep #7') +call s:assert(getpos('.'), [0, 1, 3, 0], 'operator-add:cursor keep #8') + +call operator#sandwich#set_default() +nunmap . +%delete + + + +""" operator-delete +nmap sd (operator-sandwich-delete) +xmap sd (operator-sandwich-delete) +" normal use +call setline('.', '((foo))') +normal sda( +normal . +call s:assert(getline('.'), 'foo', 'operator-delete:normal use #1') +call s:assert(getpos('.'), [0, 1, 1, 0], 'operator-delete:normal use #2') + +call setline('.', '[[foo]]') +normal sda[ +normal . +call s:assert(getline('.'), 'foo', 'operator-delete:normal use #3') +call s:assert(getpos('.'), [0, 1, 1, 0], 'operator-delete:normal use #4') + +%delete + +" blockwise-visual +call append(0, ['(((((foo)))))', '(((((bar)))))', '(((((baz)))))']) +$delete +execute "normal ggffh\2j4lsd" +normal h. +call s:assert(getline(1), '(((foo)))', 'operator-delete:blockwise-visual #1') +call s:assert(getline(2), '(((bar)))', 'operator-delete:blockwise-visual #2') +call s:assert(getline(3), '(((baz)))', 'operator-delete:blockwise-visual #3') +call s:assert(getpos('.'), [0, 1, 4, 0], 'operator-delete:blockwise-visual #4') + +normal jh. +call s:assert(getline(1), '(((foo)))', 'operator-delete:blockwise-visual #5') +call s:assert(getline(2), '((bar))', 'operator-delete:blockwise-visual #6') +call s:assert(getline(3), '((baz))', 'operator-delete:blockwise-visual #7') +call s:assert(getpos('.'), [0, 2, 3, 0], 'operator-delete:blockwise-visual #8') + +normal jh. +call s:assert(getline(1), '(((foo)))', 'operator-delete:blockwise-visual #9') +call s:assert(getline(2), '((bar))', 'operator-delete:blockwise-visual #10') +call s:assert(getline(3), '(baz)', 'operator-delete:blockwise-visual #11') +call s:assert(getpos('.'), [0, 3, 2, 0], 'operator-delete:blockwise-visual #12') + +%delete + +" count +call setline('.', '((((foo))))') +normal 02sda( +normal . +call s:assert(getline('.'), 'foo', 'operator-delete:count #1') +call s:assert(getpos('.'), [0, 1, 1, 0], 'operator-delete:count #2') + +call setline('.', '[([[foo]])]') +normal ffsda[ +normal 2. +call s:assert(getline('.'), '([foo])', 'operator-delete:count #3') +call s:assert(getpos('.'), [0, 1, 1, 0], 'operator-delete:count #4') + +%delete + +" external textobjct +let g:sandwich#recipes = [] +let g:operator#sandwich#recipes = [{'external': ['it', 'at'], 'noremap': 1}] +call append(0, ['fooo', 'bar']) +normal ggsdat +normal j. +call s:assert(getline(1), 'fooo', 'operator-delete:external textobject #1') +call s:assert(getline(2), 'bar', 'operator-delete:external textobject #2') +call s:assert(getpos('.'), [0, 2, 1, 0], 'operator-delete:external textobject #3') + +unlet g:sandwich#recipes +unlet g:operator#sandwich#recipes +%delete + +" cursor option 'keep' +call operator#sandwich#set('delete', 'all', 'cursor', 'keep') +nmap . (operator-sandwich-dot) +call setline('.', '((foo))') +normal 03lsda( +normal . +call s:assert(getline('.'), 'foo', 'operator-delete:cursor keep #1') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-delete:cursor keep #2') + +call setline('.', '(foo)') +normal 0sda( +call setline('.', '((foo) bar)') +normal 03l2. +call s:assert(getline('.'), '(foo) bar', 'operator-delete:cursor keep #3') +call s:assert(getpos('.'), [0, 1, 3, 0], 'operator-delete:cursor keep #4') + +nmap . (operator-sandwich-predot)(test-dot) +call setline('.', '((foo))') +normal 03lsda( +normal . +call s:assert(getline('.'), 'foo', 'operator-delete:cursor keep #5') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-delete:cursor keep #6') + +call setline('.', '(foo)') +normal 0sda( +call setline('.', '((foo) bar)') +normal 03l2. +call s:assert(getline('.'), '(foo) bar', 'operator-delete:cursor keep #7') +call s:assert(getpos('.'), [0, 1, 3, 0], 'operator-delete:cursor keep #8') + +call operator#sandwich#set_default() +nunmap . +%delete + + + +""" operator-replace +nmap sr (operator-sandwich-replace) +xmap sr (operator-sandwich-replace) +" normal use +call setline('.', '((foo))') +normal 0ffsra([ +normal . +call s:assert(getline('.'), '[[foo]]', 'operator-replace:normal use #1') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-replace:normal use #2') + +normal 0ffsra[( +normal . +call s:assert(getline('.'), '((foo))', 'operator-replace:normal use #3') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-replace:normal use #4') + +%delete + +" blockwise-visual +call append(0, ['(foo)', '(bar)', '(baz)']) +$delete +execute "normal gg\2j4lsr[" +call setline(1, '(foo)') +call setline(2, '(bar)') +call setline(3, '(baz)') +normal h. +call s:assert(getline(1), '[foo]', 'operator-replace:blockwise-visual #1') +call s:assert(getline(2), '[bar]', 'operator-replace:blockwise-visual #2') +call s:assert(getline(3), '[baz]', 'operator-replace:blockwise-visual #3') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-replace:blockwise-visual #4') + +call setline(1, '(foo)') +call setline(2, '(bar)') +call setline(3, '(baz)') +normal jh. +call s:assert(getline(1), '(foo)', 'operator-replace:blockwise-visual #5') +call s:assert(getline(2), '[bar]', 'operator-replace:blockwise-visual #6') +call s:assert(getline(3), '[baz]', 'operator-replace:blockwise-visual #7') +call s:assert(getpos('.'), [0, 2, 2, 0], 'operator-replace:blockwise-visual #8') + +call setline(1, '(foo)') +call setline(2, '(bar)') +call setline(3, '(baz)') +normal jh. +call s:assert(getline(1), '(foo)', 'operator-replace:blockwise-visual #9') +call s:assert(getline(2), '(bar)', 'operator-replace:blockwise-visual #10') +call s:assert(getline(3), '[baz]', 'operator-replace:blockwise-visual #11') +call s:assert(getpos('.'), [0, 3, 2, 0], 'operator-replace:blockwise-visual #12') + +%delete + +" count +call setline('.', '((((foo))))') +normal 0ff2sr2a([[ +normal . +call s:assert(getline('.'), '[[[[foo]]]]', 'operator-replace:count #1') +call s:assert(getpos('.'), [0, 1, 3, 0], 'operator-replace:count #2') + +call setline('.', '[([[foo]])]') +normal 0ffsra[( +normal 2. +call s:assert(getline('.'), '(([(foo)]))', 'operator-replace:count #3') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-replace:count #2') + +%delete + +" expr +let g:sandwich#recipes = [] +let g:operator#sandwich#recipes = [{'buns': ['Count()', 'Count()'], 'expr': 1, 'input': ['c']}, {'buns': ['(', ')']}] +call setline('.', '((foo))') +let s:count = 0 +normal ffsra(c +normal . +call s:assert(getline('.'), '11foo22', 'operator-replace:expr #1') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-replace:expr #2') + +let g:operator#sandwich#recipes = [{'buns': ['Count()', 'Count()'], 'expr': 2, 'input': ['c']}, {'buns': ['(', ')']}] +call setline('.', '((foo))') +let s:count = 0 +normal ffsra(c +normal . +call s:assert(getline('.'), '31foo24', 'operator-replace:expr #3') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-replace:expr #4') + +unlet g:sandwich#recipes +unlet g:operator#sandwich#recipes +%delete + +" listexpr +let g:sandwich#recipes = [] +let g:operator#sandwich#recipes = [{'buns': 'ListCount()', 'listexpr': 1, 'input': ['c']}, {'buns': ['(', ')']}] +call setline('.', '((foo))') +let s:count = 0 +normal ffsra(c +normal . +call s:assert(getline('.'), '11foo11', 'operator-replace:listexpr #1') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-replace:listexpr #2') + +let g:operator#sandwich#recipes = [{'buns': 'ListCount()', 'listexpr': 2, 'input': ['c']}, {'buns': ['(', ')']}] +call setline('.', '((foo))') +let s:count = 0 +normal ffsra(c +normal . +call s:assert(getline('.'), '21foo12', 'operator-replace:listexpr #3') +call s:assert(getpos('.'), [0, 1, 2, 0], 'operator-replace:listexpr #4') + +unlet g:sandwich#recipes +unlet g:operator#sandwich#recipes +%delete + +" external textobjct +let g:sandwich#recipes = [] +let g:operator#sandwich#recipes = [{'external': ['it', 'at'], 'noremap': 1}, {'buns': ['(', ')']}] +call append(0, ['fooo', 'bar']) +normal ggsrat( +normal j. +call s:assert(getline(1), '(fooo)', 'operator-replace:external textobject #1') +call s:assert(getline(2), '(bar)', 'operator-replace:external textobject #2') +call s:assert(getpos('.'), [0, 2, 2, 0], 'operator-replace:external textobject #3') + +unlet g:sandwich#recipes +unlet g:operator#sandwich#recipes +%delete + +" cursor option 'keep' +call operator#sandwich#set('replace', 'all', 'cursor', 'keep') +nmap . (operator-sandwich-dot) +call setline('.', '((foo))') +normal 03lsra([ +normal . +call s:assert(getline('.'), '[[foo]]', 'operator-delete:cursor keep #1') +call s:assert(getpos('.'), [0, 1, 4, 0], 'operator-delete:cursor keep #2') + +call setline('.', '(foo)') +normal 0sra([ +call setline('.', '((foo) bar)') +normal 03l2. +call s:assert(getline('.'), '[(foo) bar]', 'operator-delete:cursor keep #3') +call s:assert(getpos('.'), [0, 1, 4, 0], 'operator-delete:cursor keep #4') + +nmap . (operator-sandwich-predot)(test-dot) +call setline('.', '((foo))') +normal 03lsra([ +normal . +call s:assert(getline('.'), '[[foo]]', 'operator-delete:cursor keep #5') +call s:assert(getpos('.'), [0, 1, 4, 0], 'operator-delete:cursor keep #6') + +call setline('.', '(foo)') +normal 0sra([ +call setline('.', '((foo) bar)') +normal 03l2. +call s:assert(getline('.'), '[(foo) bar]', 'operator-delete:cursor keep #7') +call s:assert(getpos('.'), [0, 1, 4, 0], 'operator-delete:cursor keep #8') + +call operator#sandwich#set_default() +nunmap . +%delete + + + +""" textobj-query +" normal use +call setline('.', '(foo)') +normal dis( +call setline('.', '(foo)') +normal . +call s:assert(getline('.'), '()', 'textobj-query:normal use #1') + +call setline('.', '(foo)') +normal das( +call setline('.', '(foo)') +normal . +call s:assert(getline('.'), '', 'textobj-query:normal use #2') + +%delete + +" count +call setline('.', '((foo))((bar))') +normal 0ffdis( +normal 0fb2. +call s:assert(getline('.'), '(())()', 'textobj-query:count #1') + +call setline('.', '((foo))((bar))') +normal 0ffdas( +normal 0fb2. +call s:assert(getline('.'), '()', 'textobj-query:count #2') + +%delete + +" expr +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'buns': ['Count()', 'Count()'], 'expr': 1, 'input': ['c']}] +call setline('.', '1foo2') +let s:count = 0 +normal disc +call setline('.', '1foo2') +normal . +call s:assert(getline('.'), '12', 'textobj-query:expr #1') + +call setline('.', '1foo2') +let s:count = 0 +normal dasc +call setline('.', '1foo2') +normal . +call s:assert(getline('.'), '', 'textobj-query:expr #2') + +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'buns': ['Count()', 'Count()'], 'expr': 2, 'input': ['c']}] +call setline('.', '1foo2') +let s:count = 0 +normal disc +call setline('.', '3foo4') +normal . +call s:assert(getline('.'), '34', 'textobj-query:expr #3') + +call setline('.', '1foo2') +let s:count = 0 +normal dasc +call setline('.', '3foo4') +normal . +call s:assert(getline('.'), '', 'textobj-query:expr #4') + +unlet g:sandwich#recipes +unlet g:textobj#sandwich#recipes +%delete + +" listexpr +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'buns': 'ListCount()', 'listexpr': 1, 'input': ['c']}] +call setline('.', '1foo1') +let s:count = 0 +normal disc +call setline('.', '1foo1') +normal . +call s:assert(getline('.'), '11', 'textobj-query:listexpr #1') + +call setline('.', '1foo1') +let s:count = 0 +normal dasc +call setline('.', '1foo1') +normal . +call s:assert(getline('.'), '', 'textobj-query:listexpr #2') + +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'buns': 'ListCount()', 'listexpr': 2, 'input': ['c']}] +call setline('.', '1foo1') +let s:count = 0 +normal disc +call setline('.', '2foo2') +normal . +call s:assert(getline('.'), '22', 'textobj-query:listexpr #3') + +call setline('.', '1foo1') +let s:count = 0 +normal dasc +call setline('.', '2foo2') +normal . +call s:assert(getline('.'), '', 'textobj-query:listexpr #4') + +unlet g:sandwich#recipes +unlet g:textobj#sandwich#recipes +%delete + +" external textobjct +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'external': ['it', 'at'], 'noremap': 1, 'input': ['t']}] +call append(0, ['fooo', 'bar']) +$delete +normal ggdist +normal j. +call s:assert(getline(1), '', 'textobj-query:external textobject #1') +call s:assert(getline(2), '', 'textobj-query:external textobject #2') + +%delete + +call append(0, ['fooo', 'bar']) +$delete +normal ggdast +normal j. +call s:assert(getline(1), '', 'textobj-query:external textobject #3') +call s:assert(getline(2), '', 'textobj-query:external textobject #4') + +unlet g:sandwich#recipes +unlet g:textobj#sandwich#recipes +%delete + +" synchro option +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'buns': ['foo', 'baz'], 'synchro': 1, 'input': ['f']}] +call append(0, ['foo bar baz', ' foo baaaaar baz']) +$delete +normal ggdisf +normal jl. +call s:assert(getline(1), 'foobaz', 'textobj-query: synchro option #1') +call s:assert(getline(2), ' foobaz', 'textobj-query: synchro option #2') + +%delete + +call append(0, ['foo bar baz', ' foo baaaaar baz']) +$delete +normal ggdasf +normal jl. +call s:assert(getline(1), '', 'textobj-query: synchro option #3') +call s:assert(getline(2), ' ', 'textobj-query: synchro option #4') + +unlet g:sandwich#recipes +unlet g:textobj#sandwich#recipes +%delete + + + +""" textobj-auto +" normal use +call setline('.', '(foo)') +normal dib +call setline('.', '(foo)') +normal . +call s:assert(getline('.'), '()', 'textobj-auto:normal use #1') + +call setline('.', '(foo)') +normal dab +call setline('.', '(foo)') +normal . +call s:assert(getline('.'), '', 'textobj-auto:normal use #2') + +%delete + +" count +call setline('.', '((foo))((bar))') +normal 0ffdib +normal 0fb2. +call s:assert(getline('.'), '(())()', 'textobj-auto:count #1') + +call setline('.', '((foo))((bar))') +normal 0ffdab +normal 0fb2. +call s:assert(getline('.'), '()', 'textobj-auto:count #2') + +%delete + +" expr +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'buns': ['Count()', 'Count()'], 'expr': 1, 'input': ['c']}] +call setline('.', '1foo2') +let s:count = 0 +normal dib +call setline('.', '1foo2') +normal . +call s:assert(getline('.'), '12', 'textobj-auto:expr #1') + +call setline('.', '1foo2') +let s:count = 0 +normal dab +call setline('.', '1foo2') +normal . +call s:assert(getline('.'), '', 'textobj-auto:expr #2') + +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'buns': ['Count()', 'Count()'], 'expr': 2, 'input': ['c']}] +call setline('.', '1foo2') +let s:count = 0 +normal dib +call setline('.', '3foo4') +normal . +call s:assert(getline('.'), '34', 'textobj-auto:expr #3') + +call setline('.', '1foo2') +let s:count = 0 +normal dab +call setline('.', '3foo4') +normal . +call s:assert(getline('.'), '', 'textobj-auto:expr #4') + +unlet g:sandwich#recipes +unlet g:textobj#sandwich#recipes +%delete + +" listexpr +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'buns': 'ListCount()', 'listexpr': 1, 'input': ['c']}] +call setline('.', '1foo1') +let s:count = 0 +normal dib +call setline('.', '1foo1') +normal . +call s:assert(getline('.'), '11', 'textobj-query:listexpr #1') + +call setline('.', '1foo1') +let s:count = 0 +normal dab +call setline('.', '1foo1') +normal . +call s:assert(getline('.'), '', 'textobj-query:listexpr #2') + +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'buns': 'ListCount()', 'listexpr': 2, 'input': ['c']}] +call setline('.', '1foo1') +let s:count = 0 +normal dib +call setline('.', '2foo2') +normal . +call s:assert(getline('.'), '22', 'textobj-query:listexpr #3') + +call setline('.', '1foo1') +let s:count = 0 +normal dab +call setline('.', '2foo2') +normal . +call s:assert(getline('.'), '', 'textobj-query:listexpr #4') + +unlet g:sandwich#recipes +unlet g:textobj#sandwich#recipes +%delete + +" external textobjct +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'external': ['it', 'at'], 'noremap': 1, 'input': ['t']}] +call append(0, ['fooo', 'bar']) +$delete +normal ggdib +normal j. +call s:assert(getline(1), '', 'textobj-auto:external textobject #1') +call s:assert(getline(2), '', 'textobj-auto:external textobject #2') + +%delete + +call append(0, ['fooo', 'bar']) +$delete +normal ggdab +normal j. +call s:assert(getline(1), '', 'textobj-auto:external textobject #3') +call s:assert(getline(2), '', 'textobj-auto:external textobject #4') + +unlet g:sandwich#recipes +unlet g:textobj#sandwich#recipes +%delete + +" synchro option +let g:sandwich#recipes = [] +let g:textobj#sandwich#recipes = [{'buns': ['foo', 'baz'], 'synchro': 1, 'input': ['f']}] +call append(0, ['foo bar baz', ' foo baaaaar baz']) +$delete +normal ggdib +normal jl. +call s:assert(getline(1), 'foobaz', 'textobj-auto: synchro option #1') +call s:assert(getline(2), ' foobaz', 'textobj-auto: synchro option #2') + +%delete + +call append(0, ['foo bar baz', ' foo baaaaar baz']) +$delete +normal ggdab +normal jl. +call s:assert(getline(1), '', 'textobj-auto: synchro option #3') +call s:assert(getline(2), ' ', 'textobj-auto: synchro option #4') + +%delete + +catch + call s:quit_by_error() +endtry + + + +qall! diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/operator-add.vim b/config/neovim/store/lazy-plugins/vim-sandwich/test/operator-add.vim new file mode 100644 index 00000000..37168a83 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/operator-add.vim @@ -0,0 +1,11075 @@ +scriptencoding utf-8 + +let s:suite = themis#suite('operator-sandwich: add:') +let s:object = 'g:operator#sandwich#object' +call themis#helper('command').with(s:) + +function! s:suite.before() abort "{{{ + nmap sa (sandwich-add) + xmap sa (sandwich-add) + omap sa (sandwich-add) +endfunction +"}}} +function! s:suite.before_each() abort "{{{ + %delete + set filetype= + set whichwrap& + set ambiwidth& + set expandtab + set shiftwidth& + set softtabstop& + set autoindent& + set smartindent& + set cindent& + set indentexpr& + set cinkeys& + set indentkeys& + set formatoptions& + set textwidth& + silent! mapc! + silent! ounmap ii + silent! ounmap ssa + call operator#sandwich#set_default() + unlet! g:sandwich#recipes + unlet! g:operator#sandwich#recipes + unlet! g:sandwich#input_fallback +endfunction +"}}} +function! s:suite.after() abort "{{{ + call s:suite.before_each() + nunmap sa + xunmap sa + ounmap sa +endfunction +"}}} + +" Input +function! s:suite.input() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['(', ')']}] + + " #1 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'input': ['a', 'b']}] + + " #2 + call setline('.', 'foo') + normal 0saiwa + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + " #3 + call setline('.', 'foo') + normal 0saiwb + call g:assert.equals(getline('.'), '(foo)', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo(', 'failed at #4') + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['`', '`']}, + \ {'buns': ['``', '``']}, + \ {'buns': ['```', '```']}, + \ ] + + " #5 + call setline('.', 'foo') + normal 0saiw`h + call g:assert.equals(getline('.'), '`foo`', 'failed at #5') + + " #6 + call setline('.', 'foo') + normal 0saiw``h + call g:assert.equals(getline('.'), '``foo``', 'failed at #6') + + " #7 + call setline('.', 'foo') + normal 0saiw``` + call g:assert.equals(getline('.'), '```foo```', 'failed at #7') + + " #8 + call setline('.', 'foo') + execute "normal 0saiw`\" + call g:assert.equals(getline('.'), 'foo', 'failed at #8') + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['```', '```']}, + \ ] + + " #9 + call setline('.', 'foo') + normal 0saiw`h + call g:assert.equals(getline('.'), '`foo`', 'failed at #9') + + " #10 + call setline('.', 'foo') + normal 0saiw``h + call g:assert.equals(getline('.'), '`foo`', 'failed at #10') + + " #11 + call setline('.', 'foo') + normal 0saiw``` + call g:assert.equals(getline('.'), '```foo```', 'failed at #11') + + " #12 + call setline('.', 'foo') + execute "normal 0saiw`\" + call g:assert.equals(getline('.'), 'foo', 'failed at #12') + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['"', '"'], 'input': ['`']}, + \ {'buns': ['```', '```']}, + \ ] + + " #13 + call setline('.', 'foo') + normal 0saiw`h + call g:assert.equals(getline('.'), '"foo"', 'failed at #13') + + " #14 + call setline('.', 'foo') + normal 0saiw``h + call g:assert.equals(getline('.'), '"foo"', 'failed at #14') + + " #15 + call setline('.', 'foo') + normal 0saiw``` + call g:assert.equals(getline('.'), '```foo```', 'failed at #15') + + " #16 + call setline('.', 'foo') + execute "normal 0saiw`\" + call g:assert.equals(getline('.'), 'foo', 'failed at #16') +endfunction +"}}} + +" Filter +function! s:suite.filter_filetype() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'filetype': ['vim'], 'input': ['(', ')']}, + \ {'buns': ['{', '}'], 'filetype': ['all']}, + \ {'buns': ['<', '>'], 'filetype': ['']} + \ ] + + " #1 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0saiw{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + + " #3 + call setline('.', 'foo') + normal 0saiw< + call g:assert.equals(getline('.'), '', 'failed at #3') + + set filetype=vim + + " #4 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') + + " #5 + call setline('.', 'foo') + normal 0saiw{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #5') + + " #6 + call setline('.', 'foo') + normal 0saiw< + call g:assert.equals(getline('.'), 'iw(" + call g:assert.equals(getline('.'), '[foo]', 'failed at #3') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'motionwise': ['all'], 'input': ['(', ')']}, + \ ] + + " #4 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') + + " #5 + call setline('.', 'foo') + normal 0saViw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #5') + + " #6 + call setline('.', 'foo') + execute "normal 0sa\iw(" + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'motionwise': ['char'], 'input': ['(', ')']}, + \ ] + + " #7 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #7') + + " #8 + call setline('.', 'foo') + normal 0saViw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #8') + + " #9 + call setline('.', 'foo') + execute "normal 0sa\iw(" + call g:assert.equals(getline('.'), '(foo)', 'failed at #9') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'motionwise': ['line'], 'input': ['(', ')']}, + \ ] + + " #10 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #10') + + " #11 + call setline('.', 'foo') + normal 0saViw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #11') + + " #12 + call setline('.', 'foo') + execute "normal 0sa\iw(" + call g:assert.equals(getline('.'), '(foo)', 'failed at #12') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'motionwise': ['block'], 'input': ['(', ')']}, + \ ] + + " #13 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #13') + + " #14 + call setline('.', 'foo') + normal 0saViw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #14') + + " #15 + call setline('.', 'foo') + execute "normal 0sa\iw(" + call g:assert.equals(getline('.'), '[foo]', 'failed at #15') +endfunction +"}}} +function! s:suite.filter_mode() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'input': ['(', ')']}, + \ ] + + " #1 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0viwsa( + call g:assert.equals(getline('.'), '[foo]', 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'mode': ['n'], 'input': ['(', ')']}, + \ ] + + " #3 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0viwsa( + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'mode': ['x'], 'input': ['(', ')']}, + \ ] + + " #5 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + " #6 + call setline('.', 'foo') + normal 0viwsa( + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') +endfunction +"}}} +function! s:suite.filter_action() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'input': ['(', ')']}, + \ ] + + " #1 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'action': ['all'], 'input': ['(', ')']}, + \ ] + + " #3 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'action': ['add'], 'input': ['(', ')']}, + \ ] + + " #5 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #5') + + " #6 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'action': ['delete'], 'input': ['(', ')']}, + \ ] + + " #7 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #7') + + " #8 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #8') +endfunction +"}}} +function! s:suite.filter_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'expr_filter': ['FilterValid()']}, + \ {'buns': ['{', '}'], 'expr_filter': ['FilterInvalid()']}, + \ ] + + function! FilterValid() abort + return 1 + endfunction + + function! FilterInvalid() abort + return 0 + endfunction + + " #1 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0saiw[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #2') + + " #3 + call setline('.', 'foo') + normal 0saiw{ + call g:assert.equals(getline('.'), '{foo{', 'failed at #3') +endfunction +"}}} + +" character-wise +function! s:suite.charwise_n_default_recipes() abort "{{{ + " #1 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0saiw) + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + " #3 + call setline('.', 'foo') + normal 0saiw[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0saiw] + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #4') + + " #5 + call setline('.', 'foo') + normal 0saiw{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #5') + + " #6 + call setline('.', 'foo') + normal 0saiw} + call g:assert.equals(getline('.'), '{foo}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #6') + + " #7 + call setline('.', 'foo') + normal 0saiw< + call g:assert.equals(getline('.'), '', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #7') + + " #8 + call setline('.', 'foo') + normal 0saiw> + call g:assert.equals(getline('.'), '', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #8') +endfunction +"}}} +function! s:suite.charwise_n_not_registered() abort "{{{ + " #1 + call setline('.', 'foo') + normal 0saiwa + call g:assert.equals(getline('.'), 'afooa', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0saiw* + call g:assert.equals(getline('.'), '*foo*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_n_positioning() abort "{{{ + " #1 + call setline('.', 'foobar') + normal 0sa3l( + call g:assert.equals(getline('.'), '(foo)bar', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', 'foobar') + normal 03lsa3l( + call g:assert.equals(getline('.'), 'foo(bar)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #2') + + " #3 + call setline('.', 'foobarbaz') + normal 03lsa3l( + call g:assert.equals(getline('.'), 'foo(bar)baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #3') + + %delete + + onoremap ii :call TextobjCoord(1, 4, 1, 6) + call operator#sandwich#set('add', 'char', 'cursor', 'keep') + + " #4 + call setline('.', 'foobarbaz') + normal 0saii( + call g:assert.equals(getline('.'), 'foo(bar)baz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #4') + + " #5 + call setline('.', 'foobarbaz') + normal 02lsaii( + call g:assert.equals(getline('.'), 'foo(bar)baz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #5') + + " #6 + call setline('.', 'foobarbaz') + normal 03lsaii( + call g:assert.equals(getline('.'), 'foo(bar)baz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #6') + + " #7 + call setline('.', 'foobarbaz') + normal 05lsaii( + call g:assert.equals(getline('.'), 'foo(bar)baz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #7') + + " #8 + call setline('.', 'foobarbaz') + normal 06lsaii( + call g:assert.equals(getline('.'), 'foo(bar)baz', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #8') + + " #9 + call setline('.', 'foobarbaz') + normal 08lsaii( + call g:assert.equals(getline('.'), 'foo(bar)baz', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 11, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #9') + + ounmap ii + call operator#sandwich#set('add', 'char', 'cursor', 'inner_head') + %delete + + " #10 + set whichwrap=h,l + call append(0, ['foo', 'bar', 'baz']) + normal ggsa11l( + call g:assert.equals(getline(1), '(foo', 'failed at #10') + call g:assert.equals(getline(2), 'bar', 'failed at #10') + call g:assert.equals(getline(3), 'baz)', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #10') + set whichwrap& +endfunction +"}}} +function! s:suite.charwise_n_a_character() abort "{{{ + " #1 + call setline('.', 'a') + normal 0sal( + call g:assert.equals(getline('.'), '(a)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') +endfunction +"}}} +function! s:suite.charwise_n_breaking() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ {'buns': ["cc\n cc", "ccc\n "], 'input':['c']}, + \ ] + + " #1 + call setline('.', 'foo') + normal 0saiwa + call g:assert.equals(getline(1), 'aa', 'failed at #1') + call g:assert.equals(getline(2), 'aaafooaaa', 'failed at #1') + call g:assert.equals(getline(3), 'aa', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 0saiwb + call g:assert.equals(getline(1), 'bb', 'failed at #2') + call g:assert.equals(getline(2), 'bbb', 'failed at #2') + call g:assert.equals(getline(3), 'bbfoobb', 'failed at #2') + call g:assert.equals(getline(4), 'bbb', 'failed at #2') + call g:assert.equals(getline(5), 'bb', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #2') + + %delete + + onoremap ii :call TextobjCoord(1, 4, 1, 6) + call operator#sandwich#set('add', 'char', 'cursor', 'keep') + + " #3 + call setline('.', 'foobarbaz') + normal ggsaiia + call g:assert.equals(getline(1), 'fooaa', 'failed at #3') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #3') + call g:assert.equals(getline(3), 'aabaz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', 'foobarbaz') + normal gg2lsaiia + call g:assert.equals(getline(1), 'fooaa', 'failed at #4') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #4') + call g:assert.equals(getline(3), 'aabaz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #4') + + %delete + + " #5 + call setline('.', 'foobarbaz') + normal gg3lsaiia + call g:assert.equals(getline(1), 'fooaa', 'failed at #5') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #5') + call g:assert.equals(getline(3), 'aabaz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #5') + + %delete + + " #6 + call setline('.', 'foobarbaz') + normal gg5lsaiia + call g:assert.equals(getline(1), 'fooaa', 'failed at #6') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #6') + call g:assert.equals(getline(3), 'aabaz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 6, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #6') + + %delete + + " #7 + call setline('.', 'foobarbaz') + normal gg6lsaiia + call g:assert.equals(getline(1), 'fooaa', 'failed at #7') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #7') + call g:assert.equals(getline(3), 'aabaz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #7') + + %delete + + " #8 + call setline('.', 'foobarbaz') + normal gg$saiia + call g:assert.equals(getline(1), 'fooaa', 'failed at #8') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #8') + call g:assert.equals(getline(3), 'aabaz', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #8') + + %delete + + set autoindent + onoremap ii :call TextobjCoord(1, 8, 1, 10) + + " #9 + call setline('.', ' foobarbaz') + normal ggsaiic + call g:assert.equals(getline(1), ' foocc', 'failed at #9') + call g:assert.equals(getline(2), ' ccbarccc', 'failed at #9') + call g:assert.equals(getline(3), ' baz', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 8, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 8, 0], 'failed at #9') + + %delete + + " #10 + call setline('.', ' foobarbaz') + normal gg2lsaiic + call g:assert.equals(getline(1), ' foocc', 'failed at #10') + call g:assert.equals(getline(2), ' ccbarccc', 'failed at #10') + call g:assert.equals(getline(3), ' baz', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 8, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 8, 0], 'failed at #10') + + %delete + + " #11 + call setline('.', ' foobarbaz') + normal gg3lsaiic + call g:assert.equals(getline(1), ' foocc', 'failed at #11') + call g:assert.equals(getline(2), ' ccbarccc', 'failed at #11') + call g:assert.equals(getline(3), ' baz', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 2, 8, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 8, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 8, 0], 'failed at #11') + + %delete + + " #12 + call setline('.', ' foobarbaz') + normal gg5lsaiic + call g:assert.equals(getline(1), ' foocc', 'failed at #12') + call g:assert.equals(getline(2), ' ccbarccc', 'failed at #12') + call g:assert.equals(getline(3), ' baz', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 2, 10, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 8, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, 8, 0], 'failed at #12') + + %delete + + " #13 + call setline('.', ' foobarbaz') + normal gg6lsaiic + call g:assert.equals(getline(1), ' foocc', 'failed at #13') + call g:assert.equals(getline(2), ' ccbarccc', 'failed at #13') + call g:assert.equals(getline(3), ' baz', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 3, 8, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 8, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, 8, 0], 'failed at #13') + + %delete + + " #14 + call setline('.', ' foobarbaz') + normal gg$saiic + call g:assert.equals(getline(1), ' foocc', 'failed at #14') + call g:assert.equals(getline(2), ' ccbarccc', 'failed at #14') + call g:assert.equals(getline(3), ' baz', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 3, 10, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 8, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, 8, 0], 'failed at #14') + + ounmap ii + call operator#sandwich#set('add', 'char', 'cursor', 'inner_head') + + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.charwise_n_count() abort "{{{ + " #1 + call setline('.', 'foo') + normal 02saiw([ + call g:assert.equals(getline('.'), '[(foo)]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 8, 0], 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 03saiw([{ + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 10, 0], 'failed at #2') + + " #3 + call setline('.', 'foo bar') + normal 0sa2iw( + call g:assert.equals(getline('.'), '(foo )bar', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #3') + + " #4 + call setline('.', 'foo bar') + normal 0sa3iw( + call g:assert.equals(getline('.'), '(foo bar)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 10, 0], 'failed at #4') + + " #5 + call setline('.', 'foo bar') + normal 02sa3iw([ + call g:assert.equals(getline('.'), '[(foo bar)]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 12, 0], 'failed at #5') + + " #6 + call setline('.', 'foobarbaz') + normal 03l2sa3l([ + call g:assert.equals(getline('.'), 'foo[(bar)]baz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 11, 0], 'failed at #6') + + " #7 + call setline('.', 'foobarbaz') + normal 03l3sa3l([{ + call g:assert.equals(getline('.'), 'foo{[(bar)]}baz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 13, 0], 'failed at #7') +endfunction +"}}} +function! s:suite.charwise_n_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call setline('.', 'α') + normal 0sal( + call g:assert.equals(getline('.'), '(α)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, strlen('(α)')+1, 0], 'failed at #1') + + " #2 + call setline('.', 'aα') + normal 0sa2l( + call g:assert.equals(getline('.'), '(aα)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, strlen('(aα)')+1, 0], 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call setline('.', 'a') + normal 0sala + call g:assert.equals(getline('.'), 'αaα', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaα')+1, 0], 'failed at #3') + + " #4 + call setline('.', 'α') + normal 0sala + call g:assert.equals(getline('.'), 'ααα', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, strlen('ααα')+1, 0], 'failed at #4') + + " #5 + call setline('.', 'aα') + normal 0sa2la + call g:assert.equals(getline('.'), 'αaαα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaαα')+1, 0], 'failed at #5') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call setline('.', 'a') + normal 0sala + call g:assert.equals(getline('.'), 'aαaaα', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaaα')+1, 0], 'failed at #6') + + " #7 + call setline('.', 'α') + normal 0sala + call g:assert.equals(getline('.'), 'aααaα', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, strlen('aααaα')+1, 0], 'failed at #7') + + " #8 + call setline('.', 'aα') + normal 0sa2la + call g:assert.equals(getline('.'), 'aαaαaα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaαaα')+1, 0], 'failed at #8') + + unlet g:operator#sandwich#recipes + + " #9 + call setline('.', '“') + normal 0sal( + call g:assert.equals(getline('.'), '(“)', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, strlen('(“)')+1, 0], 'failed at #9') + + " #10 + call setline('.', 'a“') + normal 0sa2l( + call g:assert.equals(getline('.'), '(a“)', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, strlen('(a“)')+1, 0], 'failed at #10') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call setline('.', 'a') + normal 0sala + call g:assert.equals(getline('.'), '“a“', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a“')+1, 0], 'failed at #11') + + " #12 + call setline('.', '“') + normal 0sala + call g:assert.equals(getline('.'), '“““', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, strlen('“““')+1, 0], 'failed at #12') + + " #13 + call setline('.', 'a“') + normal 0sa2la + call g:assert.equals(getline('.'), '“a““', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a““')+1, 0], 'failed at #13') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call setline('.', 'a') + normal 0sala + call g:assert.equals(getline('.'), 'a“aa“', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“aa“')+1, 0], 'failed at #14') + + " #15 + call setline('.', '“') + normal 0sala + call g:assert.equals(getline('.'), 'a““a“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 1, strlen('a““a“')+1, 0], 'failed at #15') + + " #16 + call setline('.', 'a“') + normal 0sa2la + call g:assert.equals(getline('.'), 'a“a“a“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“a“a“')+1, 0], 'failed at #16') + + " #17 + call setline('.', 'a梵aa') + normal 0savl" + call g:assert.equals(getline('.'), '"a梵"aa', 'failed at #16') +endfunction +"}}} +function! s:suite.charwise_n_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', 'foo') + normal 0l2saiw() + call g:assert.equals(getline('.'), '((foo))', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + + " #2 + normal 2lsaiw( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + + " #3 + let g:operator#sandwich#recipes = [{'buns': ["(\n ", "\n)"], 'input':['a']}] + call setline('.', 'foo') + normal 0saiwa + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ')', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + %delete + unlet! g:operator#sandwich#recipes + + """ inner_head + call operator#sandwich#set('add', 'char', 'cursor', 'inner_head') + " #4 + call setline('.', 'foo') + normal 0l2saiw() + call g:assert.equals(getline('.'), '((foo))', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #4') + + " #5 + normal 2lsaiw( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #5') + + """ keep + " #6 + call operator#sandwich#set('add', 'char', 'cursor', 'keep') + call setline('.', 'foo') + normal 0l2saiw() + call g:assert.equals(getline('.'), '((foo))', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #6') + + " #7 + normal lsaiw( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #7') + + """ inner_tail + " #8 + call operator#sandwich#set('add', 'char', 'cursor', 'inner_tail') + call setline('.', 'foo') + normal 0l2saiw() + call g:assert.equals(getline('.'), '((foo))', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #8') + + " #9 + normal 2hsaiw( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #9') + + """ head + " #10 + call operator#sandwich#set('add', 'char', 'cursor', 'head') + call setline('.', 'foo') + normal 0l2saiw() + call g:assert.equals(getline('.'), '((foo))', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + + " #11 + normal 3lsaiw( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #11') + + """ tail + " #12 + call operator#sandwich#set('add', 'char', 'cursor', 'tail') + call setline('.', 'foo') + normal 0l2saiw() + call g:assert.equals(getline('.'), '((foo))', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #12') + + " #13 + normal 3hsaiw( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('add', 'char', 'cursor', 'inner_tail') + call setline('.', 'foo') + normal 0saiw1 + call g:assert.equals(getline('.'), '(foo)', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.charwise_n_option_query_once() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call setline('.', 'foo') + normal 03saiw([{ + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 03saiw1 + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + """ on + " #3 + call operator#sandwich#set('add', 'char', 'query_once', 1) + call setline('.', 'foo') + normal 03saiw( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 03saiw0[{ + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_n_option_expr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'foo') + normal 0saiwa + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0saiw1 + call g:assert.equals(getline('.'), '2foo3', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('add', 'char', 'expr', 1) + call setline('.', 'foo') + normal 0saiwa + call g:assert.equals(getline('.'), '2foo3', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0saiwb + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', 'foo') + normal 0saiwc + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', 'foo') + normal 02saiwab + call g:assert.equals(getline('.'), '2foo3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + " #7 + call setline('.', 'foo') + normal 02saiwac + call g:assert.equals(getline('.'), '2foo3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + " #8 + call setline('.', 'foo') + normal 02saiwba + call g:assert.equals(getline('.'), 'foo', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + " #9 + call setline('.', 'foo') + normal 02saiwbc + call g:assert.equals(getline('.'), 'foo', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + " #10 + call setline('.', 'foo') + normal 0saiw0 + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.charwise_n_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0saiwa + call g:assert.equals(getline('.'), 'bar', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + " #2 + call setline('.', 'bar') + normal 0saiw1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('add', 'char', 'listexpr', 1) + call setline('.', 'bar') + normal 0saiwa + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + + " #4 + call setline('.', 'bar') + normal 0saiwb + call g:assert.equals(getline('.'), 'bar', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0saiw0 + call g:assert.equals(getline('.'), 'bar', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', 'bar') + normal 0saiw1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.charwise_n_option_noremap() abort "{{{ + """"" noremap + let g:operator#sandwich#recipes = [ + \ {'buns': ['[', ']'], 'input':['[']}, + \ {'buns': ['[', ']'], 'noremap': 0, 'input':['0']}, + \ {'buns': ['[', ']'], 'noremap': 1, 'input':['1']}, + \ ] + inoremap [ { + inoremap ] } + + """ on + " #1 + call setline('.', 'foo') + normal 0saiw[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0saiw0 + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + + """ off + " #3 + call operator#sandwich#set('add', 'char', 'noremap', 0) + call setline('.', 'foo') + normal 0saiw[ + call g:assert.equals(getline('.'), '{foo}', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0saiw1 + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_n_option_skip_space() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'skip_space': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'skip_space': 1, 'input':['1']}, + \ ] + + """"" skip_space + """ off + " #1 + call setline('.', 'foo ') + normal 0sa2iw( + call g:assert.equals(getline('.'), '(foo )', 'failed at #1') + + " #2 + call setline('.', 'foo ') + normal 0sa2iw1 + call g:assert.equals(getline('.'), '(foo) ', 'failed at #2') + + """ on + " #3 + call operator#sandwich#set('add', 'char', 'skip_space', 1) + call setline('.', 'foo ') + normal 0sa2iw( + call g:assert.equals(getline('.'), '(foo) ', 'failed at #3') + + " #4 + call setline('.', 'foo ') + normal 0sa2iw0 + call g:assert.equals(getline('.'), '(foo )', 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_n_option_command() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['"', '"'], 'command': ['normal! `[d`]'], 'input':['1']}, + \ ] + + """"" command + " #1 + call operator#sandwich#set('add', 'char', 'command', ['normal! `[d`]']) + call setline('.', '"foo"') + normal 0ffsaiw( + call g:assert.equals(getline('.'), '""', 'failed at #1') + + " #2 + call operator#sandwich#set('add', 'char', 'command', []) + call setline('.', '"foo"') + normal 0ffsaiw1 + call g:assert.equals(getline('.'), '""', 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_n_option_linewise() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'linewise': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'linewise': 1, 'input':['1']}, + \ ] + + """"" linewise + """ on + " #1 + call operator#sandwich#set('add', 'char', 'linewise', 1) + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 0saiw0 + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + + %delete + + " #3 + set autoindent + call setline('.', ' foo') + normal ^saiw( + call g:assert.equals(getline(1), ' (', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ' )', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + set autoindent& + + %delete + + """ off + call operator#sandwich#set('add', 'char', 'linewise', 0) + + " #4 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline(1), '(foo)', 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo') + normal 0saiw1 + call g:assert.equals(getline(1), '(', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ')', 'failed at #5') +endfunction +"}}} +function! s:suite.charwise_n_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + + """ -1 + call operator#sandwich#set('add', 'char', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ']', 'failed at #1') + call g:assert.equals(getline(5), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), ' foo', 'failed at #2') + call g:assert.equals(getline(4), ' ]', 'failed at #2') + call g:assert.equals(getline(5), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), ' foo', 'failed at #3') + call g:assert.equals(getline(4), ' ]', 'failed at #3') + call g:assert.equals(getline(5), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), ' foo', 'failed at #4') + " call g:assert.equals(getline(4), ' ]', 'failed at #4') + call g:assert.equals(getline(5), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call setline('.', ' foo') + " normal ^saiwa + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), ' foo', 'failed at #5') + " call g:assert.equals(getline(4), ' ]', 'failed at #5') + " call g:assert.equals(getline(5), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 3, 17, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 5, 34, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('add', 'char', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ']', 'failed at #6') + call g:assert.equals(getline(5), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), 'foo', 'failed at #7') + call g:assert.equals(getline(4), ']', 'failed at #7') + call g:assert.equals(getline(5), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), 'foo', 'failed at #8') + call g:assert.equals(getline(4), ']', 'failed at #8') + call g:assert.equals(getline(5), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), 'foo', 'failed at #9') + call g:assert.equals(getline(4), ']', 'failed at #9') + call g:assert.equals(getline(5), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), 'foo', 'failed at #10') + call g:assert.equals(getline(4), ']', 'failed at #10') + call g:assert.equals(getline(5), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('add', 'char', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), ' foo', 'failed at #11') + call g:assert.equals(getline(4), ' ]', 'failed at #11') + call g:assert.equals(getline(5), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), ' foo', 'failed at #12') + call g:assert.equals(getline(4), ' ]', 'failed at #12') + call g:assert.equals(getline(5), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), ' foo', 'failed at #13') + call g:assert.equals(getline(4), ' ]', 'failed at #13') + call g:assert.equals(getline(5), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), ' foo', 'failed at #14') + call g:assert.equals(getline(4), ' ]', 'failed at #14') + call g:assert.equals(getline(5), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), ' foo', 'failed at #15') + call g:assert.equals(getline(4), ' ]', 'failed at #15') + call g:assert.equals(getline(5), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('add', 'char', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), ' foo', 'failed at #16') + call g:assert.equals(getline(4), ' ]', 'failed at #16') + call g:assert.equals(getline(5), ' }', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), ' foo', 'failed at #17') + call g:assert.equals(getline(4), ' ]', 'failed at #17') + call g:assert.equals(getline(5), ' }', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), ' foo', 'failed at #18') + call g:assert.equals(getline(4), ' ]', 'failed at #18') + call g:assert.equals(getline(5), ' }', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), ' foo', 'failed at #19') + call g:assert.equals(getline(4), ' ]', 'failed at #19') + call g:assert.equals(getline(5), ' }', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), ' foo', 'failed at #20') + call g:assert.equals(getline(4), ' ]', 'failed at #20') + call g:assert.equals(getline(5), ' }', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('add', 'char', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), ' foo', 'failed at #21') + " call g:assert.equals(getline(4), ' ]', 'failed at #21') + call g:assert.equals(getline(5), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), ' foo', 'failed at #22') + " call g:assert.equals(getline(4), ' ]', 'failed at #22') + call g:assert.equals(getline(5), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), ' foo', 'failed at #23') + " call g:assert.equals(getline(4), ' ]', 'failed at #23') + call g:assert.equals(getline(5), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), ' foo', 'failed at #24') + " call g:assert.equals(getline(4), ' ]', 'failed at #24') + call g:assert.equals(getline(5), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), ' foo', 'failed at #25') + " call g:assert.equals(getline(4), ' ]', 'failed at #25') + call g:assert.equals(getline(5), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal ^saiw0 + call g:assert.equals(getline(1), ' {', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), 'foo', 'failed at #26') + call g:assert.equals(getline(4), ']', 'failed at #26') + call g:assert.equals(getline(5), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') +endfunction +"}}} +function! s:suite.charwise_n_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n", "\n}"], 'input': ['a']}, + \ {'buns': ["{\n", "\n}"], 'indentkeys': '0{,0},0),:,0#,!^F,e', 'input': ['1']}, + \ ] + + """ cinkeys + call operator#sandwich#set('add', 'char', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', 3) + + " #2 + setlocal cinkeys=0{,0},0),:,0#,!^F,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys+', 'O,o') + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys-', 'O,o') + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + normal ^saiw1 + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('add', 'char', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0{,0},0),:,0#,!^F,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys+', 'O,o') + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys-', 'O,o') + call setline('.', ' foo') + normal ^saiwa + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + normal ^saiw1 + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +function! s:suite.charwise_x_default_recipes() abort "{{{ + " #1 + call setline('.', 'foo') + normal 0viwsa( + call g:assert.equals(getline('.'), '(foo)', 'ailed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'ailed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'ailed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'ailed at #1') + + " #2 + call setline('.', 'foo') + normal 0viwsa) + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + " #3 + call setline('.', 'foo') + normal 0viwsa[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0viwsa] + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #4') + + " #5 + call setline('.', 'foo') + normal 0viwsa{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #5') + + " #6 + call setline('.', 'foo') + normal 0viwsa} + call g:assert.equals(getline('.'), '{foo}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #6') + + " #7 + call setline('.', 'foo') + normal 0viwsa< + call g:assert.equals(getline('.'), '', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #7') + + " #8 + call setline('.', 'foo') + normal 0viwsa> + call g:assert.equals(getline('.'), '', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #8') +endfunction +"}}} +function! s:suite.charwise_x_not_registered() abort "{{{ + " #1 + call setline('.', 'foo') + normal 0viwsaa + call g:assert.equals(getline('.'), 'afooa', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0viwsa* + call g:assert.equals(getline('.'), '*foo*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_x_positioning() abort "{{{ + " #1 + call setline('.', 'foobar') + normal 0v2lsa( + call g:assert.equals(getline('.'), '(foo)bar', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', 'foobar') + normal 03lv2lsa( + call g:assert.equals(getline('.'), 'foo(bar)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #2') + + " #3 + call setline('.', 'foobarbaz') + normal 03lv2lsa( + call g:assert.equals(getline('.'), 'foo(bar)baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #3') + + " #4 + call setline('.', '') + call append(0, ['foo', 'bar', 'baz']) + normal ggv2j2lsa( + call g:assert.equals(getline(1), '(foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_x_a_character() abort "{{{ + " #1 + call setline('.', 'a') + normal 0vsa( + call g:assert.equals(getline('.'), '(a)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') +endfunction +"}}} +function! s:suite.charwise_x_breaking() abort "{{{ + " #1 + call append(0, ['', 'foo']) + normal ggvj$sa( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo)', 'failed at #1') + " call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 2, 5, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', '']) + normal ggvjsa( + call g:assert.equals(getline(1), '(foo', 'failed at #2') + call g:assert.equals(getline(2), ')', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 2, 2, 0], 'failed at #2') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #3 + call setline('.', 'foo') + normal 0viwsaa + call g:assert.equals(getline(1), 'aa', 'failed at #3') + call g:assert.equals(getline(2), 'aaafooaaa', 'failed at #3') + call g:assert.equals(getline(3), 'aa', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + normal 0viwsab + call g:assert.equals(getline(1), 'bb', 'failed at #4') + call g:assert.equals(getline(2), 'bbb', 'failed at #4') + call g:assert.equals(getline(3), 'bbfoobb', 'failed at #4') + call g:assert.equals(getline(4), 'bbb', 'failed at #4') + call g:assert.equals(getline(5), 'bb', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo') + execute "normal 0viwsa\n" + call g:assert.equals(getline(1), '', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), '', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #5') + + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.charwise_x_count() abort "{{{ + " #1 + call setline('.', 'foo') + normal 0viw2sa([ + call g:assert.equals(getline('.'), '[(foo)]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 8, 0], 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0viw3sa([{ + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 10, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_x_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call setline('.', 'α') + normal 0vsa( + call g:assert.equals(getline('.'), '(α)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, strlen('(α)')+1, 0], 'failed at #1') + + " #2 + call setline('.', 'aα') + normal 0vlsa( + call g:assert.equals(getline('.'), '(aα)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, strlen('(aα)')+1, 0], 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call setline('.', 'a') + normal 0vsaa + call g:assert.equals(getline('.'), 'αaα', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaα')+1, 0], 'failed at #3') + + " #4 + call setline('.', 'α') + normal 0vsaa + call g:assert.equals(getline('.'), 'ααα', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, strlen('ααα')+1, 0], 'failed at #4') + + " #5 + call setline('.', 'aα') + normal 0vlsaa + call g:assert.equals(getline('.'), 'αaαα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaαα')+1, 0], 'failed at #5') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call setline('.', 'a') + normal 0vsaa + call g:assert.equals(getline('.'), 'aαaaα', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaaα')+1, 0], 'failed at #6') + + " #7 + call setline('.', 'α') + normal 0vsaa + call g:assert.equals(getline('.'), 'aααaα', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, strlen('aααaα')+1, 0], 'failed at #7') + + " #8 + call setline('.', 'aα') + normal 0vlsaa + call g:assert.equals(getline('.'), 'aαaαaα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaαaα')+1, 0], 'failed at #8') + + unlet g:operator#sandwich#recipes + + " #9 + call setline('.', '“') + normal 0vsa( + call g:assert.equals(getline('.'), '(“)', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, strlen('(“)')+1, 0], 'failed at #9') + + " #10 + call setline('.', 'a“') + normal 0vlsa( + call g:assert.equals(getline('.'), '(a“)', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, strlen('(a“)')+1, 0], 'failed at #10') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call setline('.', 'a') + normal 0vsaa + call g:assert.equals(getline('.'), '“a“', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a“')+1, 0], 'failed at #11') + + " #12 + call setline('.', '“') + normal 0vsaa + call g:assert.equals(getline('.'), '“““', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, strlen('“““')+1, 0], 'failed at #12') + + " #13 + call setline('.', 'a“') + normal 0vlsaa + call g:assert.equals(getline('.'), '“a““', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a““')+1, 0], 'failed at #13') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call setline('.', 'a') + normal 0vsaa + call g:assert.equals(getline('.'), 'a“aa“', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“aa“')+1, 0], 'failed at #14') + + " #15 + call setline('.', '“') + normal 0vsaa + call g:assert.equals(getline('.'), 'a““a“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 1, strlen('a““a“')+1, 0], 'failed at #15') + + " #16 + call setline('.', 'a“') + normal 0vlsaa + call g:assert.equals(getline('.'), 'a“a“a“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“a“a“')+1, 0], 'failed at #16') +endfunction +"}}} +function! s:suite.charwise_x_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', 'foo') + normal 0viw2sa() + call g:assert.equals(getline('.'), '((foo))', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + + " #2 + normal viwsa( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + + " #3 + let g:operator#sandwich#recipes = [{'buns': ["(\n ", "\n)"], 'input':['a']}] + call setline('.', 'foo') + normal viwsaa + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ')', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + %delete + unlet! g:operator#sandwich#recipes + + """ inner_head + call operator#sandwich#set('add', 'char', 'cursor', 'inner_head') + " #4 + call setline('.', 'foo') + normal 0viw2sa() + call g:assert.equals(getline('.'), '((foo))', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #4') + + " #5 + normal viwsa( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #5') + + """ keep + " #6 + call operator#sandwich#set('add', 'char', 'cursor', 'keep') + call setline('.', 'foo') + normal 0viw2sa() + call g:assert.equals(getline('.'), '((foo))', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #6') + + " #7 + normal viwosa( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #7') + + """ inner_tail + " #8 + call operator#sandwich#set('add', 'char', 'cursor', 'inner_tail') + call setline('.', 'foo') + normal 0viwo2sa() + call g:assert.equals(getline('.'), '((foo))', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #8') + + " #9 + normal viwosa( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #9') + + """ head + " #10 + call operator#sandwich#set('add', 'char', 'cursor', 'head') + call setline('.', 'foo') + normal 0viw2sa() + call g:assert.equals(getline('.'), '((foo))', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + + " #11 + normal 3lviwsa( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #11') + + """ tail + " #12 + call operator#sandwich#set('add', 'char', 'cursor', 'tail') + call setline('.', 'foo') + normal 0viw2sa() + call g:assert.equals(getline('.'), '((foo))', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #12') + + " #13 + normal 3hviwsa( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('add', 'char', 'cursor', 'inner_tail') + call setline('.', 'foo') + normal 0viwsa1 + call g:assert.equals(getline('.'), '(foo)', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.charwise_x_option_query_once() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call setline('.', 'foo') + normal 0viw3sa([{ + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0viw3sa1 + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + """ on + " #3 + call operator#sandwich#set('add', 'char', 'query_once', 1) + call setline('.', 'foo') + normal 0viw3sa( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0viw3sa0[{ + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_x_option_expr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input':['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'foo') + normal 0viwsaa + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0viwsa1 + call g:assert.equals(getline('.'), '2foo3', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('add', 'char', 'expr', 1) + call setline('.', 'foo') + normal 0viwsaa + call g:assert.equals(getline('.'), '2foo3', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0viwsab + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', 'foo') + normal 0viwsac + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', 'foo') + normal 0viw2saab + call g:assert.equals(getline('.'), '2foo3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + " #7 + call setline('.', 'foo') + normal 0viw2saac + call g:assert.equals(getline('.'), '2foo3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + " #8 + call setline('.', 'foo') + normal 0viw2saba + call g:assert.equals(getline('.'), 'foo', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + " #9 + call setline('.', 'foo') + normal 0viw2sabc + call g:assert.equals(getline('.'), 'foo', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + " #10 + call setline('.', 'foo') + normal 0viwsa0 + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.charwise_x_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0viwsaa + call g:assert.equals(getline('.'), 'bar', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + " #2 + call setline('.', 'bar') + normal 0viwsa1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('add', 'char', 'listexpr', 1) + call setline('.', 'bar') + normal 0viwsaa + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + + " #4 + call setline('.', 'bar') + normal 0viwsab + call g:assert.equals(getline('.'), 'bar', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0viwsa0 + call g:assert.equals(getline('.'), 'bar', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', 'bar') + normal 0viwsa1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.charwise_x_option_noremap() abort "{{{ + """"" noremap + let g:operator#sandwich#recipes = [ + \ {'buns': ['[', ']'], 'input':['[']}, + \ {'buns': ['[', ']'], 'noremap': 0, 'input':['0']}, + \ {'buns': ['[', ']'], 'noremap': 1, 'input':['1']}, + \ ] + inoremap [ { + inoremap ] } + + """ on + " #1 + call setline('.', 'foo') + normal 0viwsa[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0viwsa0 + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + + """ off + " #3 + call operator#sandwich#set('add', 'char', 'noremap', 0) + call setline('.', 'foo') + normal 0viwsa[ + call g:assert.equals(getline('.'), '{foo}', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0viwsa1 + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_x_option_skip_space() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'skip_space': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'skip_space': 1, 'input':['1']}, + \ ] + + """"" skip_space + """ off + " #1 + call setline('.', 'foo ') + normal 0v2iwsa( + call g:assert.equals(getline('.'), '(foo )', 'failed at #1') + + " #2 + call setline('.', 'foo ') + normal 0v2iwsa1 + call g:assert.equals(getline('.'), '(foo) ', 'failed at #2') + + """ on + " #3 + call operator#sandwich#set('add', 'char', 'skip_space', 1) + call setline('.', 'foo ') + normal 0v2iwsa( + call g:assert.equals(getline('.'), '(foo) ', 'failed at #3') + + " #4 + call setline('.', 'foo ') + normal 0v2iwsa0 + call g:assert.equals(getline('.'), '(foo )', 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_x_option_command() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['"', '"'], 'command': ['normal! `[d`]'], 'input':['1']}, + \ ] + + """"" command + " #1 + call operator#sandwich#set('add', 'char', 'command', ["normal! `[d`]"]) + call setline('.', '"foo"') + normal 0ffviwsa( + call g:assert.equals(getline('.'), '""', 'failed at #1') + + " #2 + call operator#sandwich#set('add', 'char', 'command', []) + call setline('.', '"foo"') + normal 0ffviwsa1 + call g:assert.equals(getline('.'), '""', 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_x_option_linewise() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'linewise': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'linewise': 1, 'input':['1']}, + \ ] + + """"" linewise + """ on + " #1 + call operator#sandwich#set('add', 'char', 'linewise', 1) + call setline('.', 'foo') + normal 0viwsa( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 0viwsa0 + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + + %delete + + " #3 + set autoindent + call setline('.', ' foo') + normal ^viwsa( + call g:assert.equals(getline(1), ' (', 'failed at #-39') + call g:assert.equals(getline(2), ' foo', 'failed at #-39') + call g:assert.equals(getline(3), ' )', 'failed at #-39') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #-39') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #-39') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #-39') + set autoindent& + + %delete + + """ off + call operator#sandwich#set('add', 'char', 'linewise', 0) + + " #4 + call setline('.', 'foo') + normal 0viwsa( + call g:assert.equals(getline(1), '(foo)', 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo') + normal 0viwsa1 + call g:assert.equals(getline(1), '(', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ')', 'failed at #5') +endfunction +"}}} +function! s:suite.charwise_x_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + + """ -1 + call operator#sandwich#set('add', 'char', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ']', 'failed at #1') + call g:assert.equals(getline(5), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), ' foo', 'failed at #2') + call g:assert.equals(getline(4), ' ]', 'failed at #2') + call g:assert.equals(getline(5), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), ' foo', 'failed at #3') + call g:assert.equals(getline(4), ' ]', 'failed at #3') + call g:assert.equals(getline(5), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), ' foo', 'failed at #4') + " call g:assert.equals(getline(4), ' ]', 'failed at #4') + call g:assert.equals(getline(5), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call setline('.', ' foo') + " normal ^viwsaa + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), ' foo', 'failed at #5') + " call g:assert.equals(getline(4), ' ]', 'failed at #5') + " call g:assert.equals(getline(5), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 3, 17, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 5, 34, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('add', 'char', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ']', 'failed at #6') + call g:assert.equals(getline(5), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), 'foo', 'failed at #7') + call g:assert.equals(getline(4), ']', 'failed at #7') + call g:assert.equals(getline(5), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), 'foo', 'failed at #8') + call g:assert.equals(getline(4), ']', 'failed at #8') + call g:assert.equals(getline(5), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), 'foo', 'failed at #9') + call g:assert.equals(getline(4), ']', 'failed at #9') + call g:assert.equals(getline(5), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), 'foo', 'failed at #10') + call g:assert.equals(getline(4), ']', 'failed at #10') + call g:assert.equals(getline(5), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('add', 'char', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), ' foo', 'failed at #11') + call g:assert.equals(getline(4), ' ]', 'failed at #11') + call g:assert.equals(getline(5), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), ' foo', 'failed at #12') + call g:assert.equals(getline(4), ' ]', 'failed at #12') + call g:assert.equals(getline(5), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), ' foo', 'failed at #13') + call g:assert.equals(getline(4), ' ]', 'failed at #13') + call g:assert.equals(getline(5), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), ' foo', 'failed at #14') + call g:assert.equals(getline(4), ' ]', 'failed at #14') + call g:assert.equals(getline(5), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), ' foo', 'failed at #15') + call g:assert.equals(getline(4), ' ]', 'failed at #15') + call g:assert.equals(getline(5), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('add', 'char', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), ' foo', 'failed at #16') + call g:assert.equals(getline(4), ' ]', 'failed at #16') + call g:assert.equals(getline(5), ' }', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), ' foo', 'failed at #17') + call g:assert.equals(getline(4), ' ]', 'failed at #17') + call g:assert.equals(getline(5), ' }', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), ' foo', 'failed at #18') + call g:assert.equals(getline(4), ' ]', 'failed at #18') + call g:assert.equals(getline(5), ' }', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), ' foo', 'failed at #19') + call g:assert.equals(getline(4), ' ]', 'failed at #19') + call g:assert.equals(getline(5), ' }', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), ' foo', 'failed at #20') + call g:assert.equals(getline(4), ' ]', 'failed at #20') + call g:assert.equals(getline(5), ' }', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('add', 'char', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), ' foo', 'failed at #21') + " call g:assert.equals(getline(4), ' ]', 'failed at #21') + call g:assert.equals(getline(5), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), ' foo', 'failed at #22') + " call g:assert.equals(getline(4), ' ]', 'failed at #22') + call g:assert.equals(getline(5), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), ' foo', 'failed at #23') + " call g:assert.equals(getline(4), ' ]', 'failed at #23') + call g:assert.equals(getline(5), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), ' foo', 'failed at #24') + " call g:assert.equals(getline(4), ' ]', 'failed at #24') + call g:assert.equals(getline(5), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), ' foo', 'failed at #25') + " call g:assert.equals(getline(4), ' ]', 'failed at #25') + call g:assert.equals(getline(5), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal ^viwsa0 + call g:assert.equals(getline(1), ' {', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), 'foo', 'failed at #26') + call g:assert.equals(getline(4), ']', 'failed at #26') + call g:assert.equals(getline(5), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') +endfunction +"}}} +function! s:suite.charwise_x_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n", "\n}"], 'input': ['a']}, + \ {'buns': ["{\n", "\n}"], 'indentkeys': '0{,0},0),:,0#,!^F,e', 'input': ['1']}, + \ ] + + """ cinkeys + call operator#sandwich#set('add', 'char', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', 3) + + " #2 + setlocal cinkeys=0{,0},0),:,0#,!^F,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys+', 'O,o') + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys-', 'O,o') + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + normal ^viwsa1 + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('add', 'char', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0{,0},0),:,0#,!^F,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys+', 'O,o') + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'char', 'indentkeys-', 'O,o') + call setline('.', ' foo') + normal ^viwsaa + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'char', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + normal ^viwsa1 + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +" line-wise +function! s:suite.linewise_n_default_recipes() abort "{{{ + " #1 + call setline('.', 'foo') + normal 0saVl( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 0saVl) + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), ')', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + + %delete + + " #3 + call setline('.', 'foo') + normal 0saVl[ + call g:assert.equals(getline(1), '[', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), ']', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + normal 0saVl] + call g:assert.equals(getline(1), '[', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), ']', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo') + normal 0saVl{ + call g:assert.equals(getline(1), '{', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), '}', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #5') + + %delete + + " #6 + call setline('.', 'foo') + normal 0saVl} + call g:assert.equals(getline(1), '{', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #6') + + %delete + + " #7 + call setline('.', 'foo') + normal 0saVl< + call g:assert.equals(getline(1), '<', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), '>', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #7') + + %delete + + " #8 + call setline('.', 'foo') + normal 0saVl> + call g:assert.equals(getline(1), '<', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), '>', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_n_not_registered() abort "{{{ + " #1 + call setline('.', 'foo') + normal 0saVla + call g:assert.equals(getline(1), 'a', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), 'a', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 0saVl* + call g:assert.equals(getline(1), '*', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), '*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_n_positioning() abort "{{{ + " #1 + call append(0, ['foo', 'bar', 'baz']) + normal ggsa2j( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), 'bar', 'failed at #1') + call g:assert.equals(getline(4), 'baz', 'failed at #1') + call g:assert.equals(getline(5), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + + " #2 + call append(0, ['foo', 'bar', 'baz']) + normal ggjsaVl( + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), '(', 'failed at #2') + call g:assert.equals(getline(3), 'bar', 'failed at #2') + call g:assert.equals(getline(4), ')', 'failed at #2') + call g:assert.equals(getline(5), 'baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 4, 2, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_n_a_character() abort "{{{ + " #1 + call setline('.', 'a') + normal 0saVl( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'a', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') +endfunction +"}}} +function! s:suite.linewise_n_breaking() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #1 + call setline('.', 'foo') + normal 0saViwa + call g:assert.equals(getline(1), 'aa', 'failed at #1') + call g:assert.equals(getline(2), 'aaa', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), 'aaa', 'failed at #1') + call g:assert.equals(getline(5), 'aa', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 0saViwb + call g:assert.equals(getline(1), 'bb', 'failed at #2') + call g:assert.equals(getline(2), 'bbb', 'failed at #2') + call g:assert.equals(getline(3), 'bb', 'failed at #2') + call g:assert.equals(getline(4), 'foo', 'failed at #2') + call g:assert.equals(getline(5), 'bb', 'failed at #2') + call g:assert.equals(getline(6), 'bbb', 'failed at #2') + call g:assert.equals(getline(7), 'bb', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 4, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 7, 3, 0], 'failed at #2') + + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_n_count() abort "{{{ + " #1 + call setline('.', 'foo') + normal 02saViw([ + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), '(', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ')', 'failed at #1') + call g:assert.equals(getline(5), ']', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 03saViw([{ + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), '[', 'failed at #2') + call g:assert.equals(getline(3), '(', 'failed at #2') + call g:assert.equals(getline(4), 'foo', 'failed at #2') + call g:assert.equals(getline(5), ')', 'failed at #2') + call g:assert.equals(getline(6), ']', 'failed at #2') + call g:assert.equals(getline(7), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 4, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #2') + + %delete + + " #3 + call setline('.', 'foo bar') + normal 0saV2iw( + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), 'foo bar', 'failed at #3') + call g:assert.equals(getline(3), ')', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo bar') + normal 0saV3iw( + call g:assert.equals(getline(1), '(', 'failed at #4') + call g:assert.equals(getline(2), 'foo bar', 'failed at #4') + call g:assert.equals(getline(3), ')', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo bar') + normal 02saV3iw([ + call g:assert.equals(getline(1), '[', 'failed at #5') + call g:assert.equals(getline(2), '(', 'failed at #5') + call g:assert.equals(getline(3), 'foo bar', 'failed at #5') + call g:assert.equals(getline(4), ')', 'failed at #5') + call g:assert.equals(getline(5), ']', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['aa', 'foo', 'aa']) + normal ggj2saViw([ + call g:assert.equals(getline(1), 'aa', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), '(', 'failed at #6') + call g:assert.equals(getline(4), 'foo', 'failed at #6') + call g:assert.equals(getline(5), ')', 'failed at #6') + call g:assert.equals(getline(6), ']', 'failed at #6') + call g:assert.equals(getline(7), 'aa', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 4, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 6, 2, 0], 'failed at #6') +endfunction +"}}} +function! s:suite.linewise_n_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call setline('.', 'α') + normal 0saVl( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'α', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'aα') + normal 0saVl( + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'aα', 'failed at #2') + call g:assert.equals(getline(3), ')', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call setline('.', 'a') + normal 0saVla + call g:assert.equals(getline(1), 'α', 'failed at #3') + call g:assert.equals(getline(2), 'a', 'failed at #3') + call g:assert.equals(getline(3), 'α', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', 'α') + normal 0saVla + call g:assert.equals(getline(1), 'α', 'failed at #4') + call g:assert.equals(getline(2), 'α', 'failed at #4') + call g:assert.equals(getline(3), 'α', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #4') + + %delete + + " #5 + call setline('.', 'aα') + normal 0saVla + call g:assert.equals(getline(1), 'α', 'failed at #5') + call g:assert.equals(getline(2), 'aα', 'failed at #5') + call g:assert.equals(getline(3), 'α', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #5') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call setline('.', 'a') + normal 0saVla + call g:assert.equals(getline(1), 'aα', 'failed at #6') + call g:assert.equals(getline(2), 'a', 'failed at #6') + call g:assert.equals(getline(3), 'aα', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #6') + + %delete + + " #7 + call setline('.', 'α') + normal 0saVla + call g:assert.equals(getline(1), 'aα', 'failed at #7') + call g:assert.equals(getline(2), 'α', 'failed at #7') + call g:assert.equals(getline(3), 'aα', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #7') + + %delete + + " #8 + call setline('.', 'aα') + normal 0saVla + call g:assert.equals(getline(1), 'aα', 'failed at #8') + call g:assert.equals(getline(2), 'aα', 'failed at #8') + call g:assert.equals(getline(3), 'aα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #8') + + %delete + unlet g:operator#sandwich#recipes + + " #9 + call setline('.', '“') + normal 0saVl( + call g:assert.equals(getline(1), '(', 'failed at #9') + call g:assert.equals(getline(2), '“', 'failed at #9') + call g:assert.equals(getline(3), ')', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #9') + + %delete + + " #10 + call setline('.', 'a“') + normal 0saVl( + call g:assert.equals(getline(1), '(', 'failed at #10') + call g:assert.equals(getline(2), 'a“', 'failed at #10') + call g:assert.equals(getline(3), ')', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #10') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call setline('.', 'a') + normal 0saVla + call g:assert.equals(getline(1), '“', 'failed at #11') + call g:assert.equals(getline(2), 'a', 'failed at #11') + call g:assert.equals(getline(3), '“', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #11') + + %delete + + " #12 + call setline('.', '“') + normal 0saVla + call g:assert.equals(getline(1), '“', 'failed at #12') + call g:assert.equals(getline(2), '“', 'failed at #12') + call g:assert.equals(getline(3), '“', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #12') + + %delete + + " #13 + call setline('.', 'a“') + normal 0saVla + call g:assert.equals(getline(1), '“', 'failed at #13') + call g:assert.equals(getline(2), 'a“', 'failed at #13') + call g:assert.equals(getline(3), '“', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #13') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call setline('.', 'a') + normal 0saVla + call g:assert.equals(getline(1), 'a“', 'failed at #14') + call g:assert.equals(getline(2), 'a', 'failed at #14') + call g:assert.equals(getline(3), 'a“', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #14') + + %delete + + " #15 + call setline('.', '“') + normal 0saVla + call g:assert.equals(getline(1), 'a“', 'failed at #15') + call g:assert.equals(getline(2), '“', 'failed at #15') + call g:assert.equals(getline(3), 'a“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #15') + + %delete + + " #16 + call setline('.', 'a“') + normal 0saVla + call g:assert.equals(getline(1), 'a“', 'failed at #16') + call g:assert.equals(getline(2), 'a“', 'failed at #16') + call g:assert.equals(getline(3), 'a“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #16') +endfunction +"}}} +function! s:suite.linewise_n_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', 'foo') + normal 0l2saViw() + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), '(', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ')', 'failed at #1') + call g:assert.equals(getline(5), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + + " #2 + normal 2lsaViw( + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), '(', 'failed at #2') + call g:assert.equals(getline(3), '(', 'failed at #2') + call g:assert.equals(getline(4), 'foo', 'failed at #2') + call g:assert.equals(getline(5), ')', 'failed at #2') + call g:assert.equals(getline(6), ')', 'failed at #2') + call g:assert.equals(getline(7), ')', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 4, 1, 0], 'failed at #2') + + %delete + + " #3 + call setline('.', ' foo') + normal 0saViw( + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ')', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + + %delete + + """ inner_head + call operator#sandwich#set('add', 'line', 'cursor', 'inner_head') + " #4 + call setline('.', 'foo') + normal 0l2saViw() + call g:assert.equals(getline(1), '(', 'failed at #4') + call g:assert.equals(getline(2), '(', 'failed at #4') + call g:assert.equals(getline(3), 'foo', 'failed at #4') + call g:assert.equals(getline(4), ')', 'failed at #4') + call g:assert.equals(getline(5), ')', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #4') + + " #5 + normal 2lsaViw( + call g:assert.equals(getline(1), '(', 'failed at #5') + call g:assert.equals(getline(2), '(', 'failed at #5') + call g:assert.equals(getline(3), '(', 'failed at #5') + call g:assert.equals(getline(4), 'foo', 'failed at #5') + call g:assert.equals(getline(5), ')', 'failed at #5') + call g:assert.equals(getline(6), ')', 'failed at #5') + call g:assert.equals(getline(7), ')', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 4, 1, 0], 'failed at #5') + + %delete + + """ keep + " #6 + call operator#sandwich#set('add', 'line', 'cursor', 'keep') + call setline('.', 'foo') + normal 0l2saViw() + call g:assert.equals(getline(1), '(', 'failed at #6') + call g:assert.equals(getline(2), '(', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ')', 'failed at #6') + call g:assert.equals(getline(5), ')', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 2, 0], 'failed at #6') + + " #7 + normal saViw( + call g:assert.equals(getline(1), '(', 'failed at #7') + call g:assert.equals(getline(2), '(', 'failed at #7') + call g:assert.equals(getline(3), '(', 'failed at #7') + call g:assert.equals(getline(4), 'foo', 'failed at #7') + call g:assert.equals(getline(5), ')', 'failed at #7') + call g:assert.equals(getline(6), ')', 'failed at #7') + call g:assert.equals(getline(7), ')', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 4, 2, 0], 'failed at #7') + + %delete + + """ inner_tail + " #8 + call operator#sandwich#set('add', 'line', 'cursor', 'inner_tail') + call setline('.', 'foo') + normal 0l2saViw() + call g:assert.equals(getline(1), '(', 'failed at #8') + call g:assert.equals(getline(2), '(', 'failed at #8') + call g:assert.equals(getline(3), 'foo', 'failed at #8') + call g:assert.equals(getline(4), ')', 'failed at #8') + call g:assert.equals(getline(5), ')', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #8') + + " #9 + normal 2hsaViw( + call g:assert.equals(getline(1), '(', 'failed at #9') + call g:assert.equals(getline(2), '(', 'failed at #9') + call g:assert.equals(getline(3), '(', 'failed at #9') + call g:assert.equals(getline(4), 'foo', 'failed at #9') + call g:assert.equals(getline(5), ')', 'failed at #9') + call g:assert.equals(getline(6), ')', 'failed at #9') + call g:assert.equals(getline(7), ')', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 4, 3, 0], 'failed at #9') + + %delete + + """ head + " #10 + call operator#sandwich#set('add', 'line', 'cursor', 'head') + call setline('.', 'foo') + normal 0l2saViw() + call g:assert.equals(getline(1), '(', 'failed at #10') + call g:assert.equals(getline(2), '(', 'failed at #10') + call g:assert.equals(getline(3), 'foo', 'failed at #10') + call g:assert.equals(getline(4), ')', 'failed at #10') + call g:assert.equals(getline(5), ')', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + + " #11 + normal 2jsaViw( + call g:assert.equals(getline(1), '(', 'failed at #11') + call g:assert.equals(getline(2), '(', 'failed at #11') + call g:assert.equals(getline(3), '(', 'failed at #11') + call g:assert.equals(getline(4), 'foo', 'failed at #11') + call g:assert.equals(getline(5), ')', 'failed at #11') + call g:assert.equals(getline(6), ')', 'failed at #11') + call g:assert.equals(getline(7), ')', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #11') + + %delete + + """ tail + " #12 + call operator#sandwich#set('add', 'line', 'cursor', 'tail') + call setline('.', 'foo') + normal 0l2saViw() + call g:assert.equals(getline(1), '(', 'failed at #12') + call g:assert.equals(getline(2), '(', 'failed at #12') + call g:assert.equals(getline(3), 'foo', 'failed at #12') + call g:assert.equals(getline(4), ')', 'failed at #12') + call g:assert.equals(getline(5), ')', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 5, 1, 0], 'failed at #12') + + " #13 + normal 2ksaViw( + call g:assert.equals(getline(1), '(', 'failed at #13') + call g:assert.equals(getline(2), '(', 'failed at #13') + call g:assert.equals(getline(3), '(', 'failed at #13') + call g:assert.equals(getline(4), 'foo', 'failed at #13') + call g:assert.equals(getline(5), ')', 'failed at #13') + call g:assert.equals(getline(6), ')', 'failed at #13') + call g:assert.equals(getline(7), ')', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 5, 1, 0], 'failed at #13') + + %delete + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('add', 'line', 'cursor', 'inner_tail') + call setline('.', 'foo') + normal 0saViw1 + call g:assert.equals(getline(1), '(', 'failed at #14') + call g:assert.equals(getline(2), 'foo', 'failed at #14') + call g:assert.equals(getline(3), ')', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.linewise_n_option_query_once() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call setline('.', 'foo') + normal 03saViw([{ + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), '(', 'failed at #1') + call g:assert.equals(getline(4), 'foo', 'failed at #1') + call g:assert.equals(getline(5), ')', 'failed at #1') + call g:assert.equals(getline(6), ']', 'failed at #1') + call g:assert.equals(getline(7), '}', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 03saViw1 + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), '(', 'failed at #2') + call g:assert.equals(getline(3), '(', 'failed at #2') + call g:assert.equals(getline(4), 'foo', 'failed at #2') + call g:assert.equals(getline(5), ')', 'failed at #2') + call g:assert.equals(getline(6), ')', 'failed at #2') + call g:assert.equals(getline(7), ')', 'failed at #2') + + %delete + + """ on + " #3 + call operator#sandwich#set('add', 'line', 'query_once', 1) + call setline('.', 'foo') + normal 03saViw( + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), '(', 'failed at #3') + call g:assert.equals(getline(3), '(', 'failed at #3') + call g:assert.equals(getline(4), 'foo', 'failed at #3') + call g:assert.equals(getline(5), ')', 'failed at #3') + call g:assert.equals(getline(6), ')', 'failed at #3') + call g:assert.equals(getline(7), ')', 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + normal 03saViw0[{ + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), '[', 'failed at #4') + call g:assert.equals(getline(3), '(', 'failed at #4') + call g:assert.equals(getline(4), 'foo', 'failed at #4') + call g:assert.equals(getline(5), ')', 'failed at #4') + call g:assert.equals(getline(6), ']', 'failed at #4') + call g:assert.equals(getline(7), '}', 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_n_option_expr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input':['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'foo') + normal 0saViwa + call g:assert.equals(getline(1), '1+1', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '1+2', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 0saViw1 + call g:assert.equals(getline(1), '2', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), '3', 'failed at #2') + + %delete + + """ 1 + " #3 + call operator#sandwich#set('add', 'line', 'expr', 1) + call setline('.', 'foo') + normal 0saViwa + call g:assert.equals(getline(1), '2', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '3', 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + normal 0saViwb + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo') + normal 0saViwc + call g:assert.equals(getline(1), 'foo', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + %delete + + " #6 + call setline('.', 'foo') + normal 02saViwab + call g:assert.equals(getline(1), '2', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), '3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + %delete + + " #7 + call setline('.', 'foo') + normal 02saViwac + call g:assert.equals(getline(1), '2', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), '3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + %delete + + " #8 + call setline('.', 'foo') + normal 02saViwba + call g:assert.equals(getline(1), 'foo', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + %delete + + " #9 + call setline('.', 'foo') + normal 02saViwca + call g:assert.equals(getline(1), 'foo', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + %delete + + " #10 + call setline('.', 'foo') + normal 0saViw0 + call g:assert.equals(getline(1), '1+1', 'failed at #10') + call g:assert.equals(getline(2), 'foo', 'failed at #10') + call g:assert.equals(getline(3), '1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes only in + " dot-repeat. +endfunction +"}}} +function! s:suite.linewise_n_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0saVla + call g:assert.equals(getline(1), 'bar', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + %delete + + " #2 + call setline('.', 'bar') + normal 0saVl1 + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + + %delete + + """ 1 + " #3 + call operator#sandwich#set('add', 'line', 'listexpr', 1) + call setline('.', 'bar') + normal 0saVla + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getline(2), 'bar', 'failed at #3') + call g:assert.equals(getline(3), 'baz', 'failed at #3') + + %delete + + " #4 + call setline('.', 'bar') + normal 0saVlb + call g:assert.equals(getline(1), 'bar', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + %delete + + " #5 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0saVl0 + call g:assert.equals(getline(1), 'bar', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + %delete + + " #6 + call setline('.', 'bar') + normal 0saVl1 + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getline(2), 'bar', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.linewise_n_option_noremap() abort "{{{ + """"" noremap + let g:operator#sandwich#recipes = [ + \ {'buns': ['[', ']'], 'input':['[']}, + \ {'buns': ['[', ']'], 'noremap': 0, 'input':['0']}, + \ {'buns': ['[', ']'], 'noremap': 1, 'input':['1']}, + \ ] + inoremap [ { + inoremap ] } + + """ on + " #1 + call setline('.', 'foo') + normal 0saViw[ + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ']', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 0saViw0 + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + + %delete + + """ off + " #3 + call operator#sandwich#set('add', 'line', 'noremap', 0) + call setline('.', 'foo') + normal 0saViw[ + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '}', 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + normal 0saViw1 + call g:assert.equals(getline(1), '[', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), ']', 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_n_option_skip_space() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'skip_space': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'skip_space': 1, 'input':['1']}, + \ ] + + """"" skip_space + """ on + " #1 + call setline('.', 'foo ') + normal 0saViw( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo ', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo ') + normal 0saViw0 + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'foo ', 'failed at #2') + call g:assert.equals(getline(3), ')', 'failed at #2') + + %delete + + """ off + " #3 + call operator#sandwich#set('add', 'line', 'skip_space', 0) + call setline('.', 'foo ') + normal 0saViw( + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), 'foo ', 'failed at #3') + call g:assert.equals(getline(3), ')', 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo ') + normal 0saViw1 + call g:assert.equals(getline(1), '(', 'failed at #4') + call g:assert.equals(getline(2), 'foo ', 'failed at #4') + call g:assert.equals(getline(3), ')', 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_n_option_command() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['"', '"'], 'command': ['normal! `[d`]'], 'input':['1']}, + \ ] + + """"" command + " #1 + call operator#sandwich#set('add', 'line', 'command', ["normal! `[d`]"]) + call append(0, ['[', 'foo', ']']) + normal ggjsaViw( + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), ']', 'failed at #1') + + %delete + + " #2 + call operator#sandwich#set('add', 'line', 'command', []) + call append(0, ['[', 'foo', ']']) + normal ggjsaViw1 + call g:assert.equals(getline(1), '[', 'failed at #2') + call g:assert.equals(getline(2), ']', 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_n_option_linewise() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'linewise': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'linewise': 1, 'input':['1']}, + \ ] + + """"" linewise + """ off + " #1 + call operator#sandwich#set('add', 'line', 'linewise', 0) + call setline('.', 'foo') + normal 0saViw( + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 0saViw1 + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), ')', 'failed at #2') + + %delete + call operator#sandwich#set('add', 'line', 'linewise', 1) + + """ on + " #3 + set autoindent + call setline('.', ' foo') + normal ^saViw( + call g:assert.equals(getline(1), ' (', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ' )', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + set autoindent& + + %delete + + " #4 + call setline('.', 'foo') + normal 0saViw0 + call g:assert.equals(getline(1), '(foo)', 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_n_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + + """ -1 + call operator#sandwich#set('add', 'line', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + call g:assert.equals(getline(4), ' foo', 'failed at #1') + call g:assert.equals(getline(5), '', 'failed at #1') + call g:assert.equals(getline(6), ']', 'failed at #1') + call g:assert.equals(getline(7), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), '', 'failed at #2') + call g:assert.equals(getline(4), ' foo', 'failed at #2') + call g:assert.equals(getline(5), '', 'failed at #2') + call g:assert.equals(getline(6), ' ]', 'failed at #2') + call g:assert.equals(getline(7), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), '', 'failed at #3') + call g:assert.equals(getline(4), ' foo', 'failed at #3') + call g:assert.equals(getline(5), '', 'failed at #3') + call g:assert.equals(getline(6), ' ]', 'failed at #3') + call g:assert.equals(getline(7), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), '', 'failed at #4') + call g:assert.equals(getline(4), ' foo', 'failed at #4') + call g:assert.equals(getline(5), '', 'failed at #4') + " call g:assert.equals(getline(6), ' ]', 'failed at #4') + call g:assert.equals(getline(7), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call setline('.', ' foo') + " normal saVla + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), '', 'failed at #5') + " call g:assert.equals(getline(4), ' foo', 'failed at #5') + " call g:assert.equals(getline(5), '', 'failed at #5') + " call g:assert.equals(getline(6), ' ]', 'failed at #5') + " call g:assert.equals(getline(7), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 7, 22, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('add', 'line', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), '', 'failed at #6') + call g:assert.equals(getline(4), ' foo', 'failed at #6') + call g:assert.equals(getline(5), '', 'failed at #6') + call g:assert.equals(getline(6), ']', 'failed at #6') + call g:assert.equals(getline(7), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), '', 'failed at #7') + call g:assert.equals(getline(4), ' foo', 'failed at #7') + call g:assert.equals(getline(5), '', 'failed at #7') + call g:assert.equals(getline(6), ']', 'failed at #7') + call g:assert.equals(getline(7), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), '', 'failed at #8') + call g:assert.equals(getline(4), ' foo', 'failed at #8') + call g:assert.equals(getline(5), '', 'failed at #8') + call g:assert.equals(getline(6), ']', 'failed at #8') + call g:assert.equals(getline(7), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), '', 'failed at #9') + call g:assert.equals(getline(4), ' foo', 'failed at #9') + call g:assert.equals(getline(5), '', 'failed at #9') + call g:assert.equals(getline(6), ']', 'failed at #9') + call g:assert.equals(getline(7), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), '', 'failed at #10') + call g:assert.equals(getline(4), ' foo', 'failed at #10') + call g:assert.equals(getline(5), '', 'failed at #10') + call g:assert.equals(getline(6), ']', 'failed at #10') + call g:assert.equals(getline(7), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('add', 'line', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), '', 'failed at #11') + call g:assert.equals(getline(4), ' foo', 'failed at #11') + call g:assert.equals(getline(5), '', 'failed at #11') + call g:assert.equals(getline(6), ' ]', 'failed at #11') + call g:assert.equals(getline(7), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), '', 'failed at #12') + call g:assert.equals(getline(4), ' foo', 'failed at #12') + call g:assert.equals(getline(5), '', 'failed at #12') + call g:assert.equals(getline(6), ' ]', 'failed at #12') + call g:assert.equals(getline(7), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), '', 'failed at #13') + call g:assert.equals(getline(4), ' foo', 'failed at #13') + call g:assert.equals(getline(5), '', 'failed at #13') + call g:assert.equals(getline(6), ' ]', 'failed at #13') + call g:assert.equals(getline(7), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), '', 'failed at #14') + call g:assert.equals(getline(4), ' foo', 'failed at #14') + call g:assert.equals(getline(5), '', 'failed at #14') + call g:assert.equals(getline(6), ' ]', 'failed at #14') + call g:assert.equals(getline(7), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), '', 'failed at #15') + call g:assert.equals(getline(4), ' foo', 'failed at #15') + call g:assert.equals(getline(5), '', 'failed at #15') + call g:assert.equals(getline(6), ' ]', 'failed at #15') + call g:assert.equals(getline(7), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('add', 'line', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), '', 'failed at #16') + call g:assert.equals(getline(4), ' foo', 'failed at #16') + call g:assert.equals(getline(5), '', 'failed at #16') + call g:assert.equals(getline(6), ' ]', 'failed at #16') + call g:assert.equals(getline(7), '}', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), '', 'failed at #17') + call g:assert.equals(getline(4), ' foo', 'failed at #17') + call g:assert.equals(getline(5), '', 'failed at #17') + call g:assert.equals(getline(6), ' ]', 'failed at #17') + call g:assert.equals(getline(7), '}', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), '', 'failed at #18') + call g:assert.equals(getline(4), ' foo', 'failed at #18') + call g:assert.equals(getline(5), '', 'failed at #18') + call g:assert.equals(getline(6), ' ]', 'failed at #18') + call g:assert.equals(getline(7), '}', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), '', 'failed at #19') + call g:assert.equals(getline(4), ' foo', 'failed at #19') + call g:assert.equals(getline(5), '', 'failed at #19') + call g:assert.equals(getline(6), ' ]', 'failed at #19') + call g:assert.equals(getline(7), '}', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), '', 'failed at #20') + call g:assert.equals(getline(4), ' foo', 'failed at #20') + call g:assert.equals(getline(5), '', 'failed at #20') + call g:assert.equals(getline(6), ' ]', 'failed at #20') + call g:assert.equals(getline(7), '}', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('add', 'line', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), '', 'failed at #21') + call g:assert.equals(getline(4), ' foo', 'failed at #21') + call g:assert.equals(getline(5), '', 'failed at #21') + " call g:assert.equals(getline(6), ' ]', 'failed at #21') + call g:assert.equals(getline(7), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), '', 'failed at #22') + call g:assert.equals(getline(4), ' foo', 'failed at #22') + call g:assert.equals(getline(5), '', 'failed at #22') + " call g:assert.equals(getline(6), ' ]', 'failed at #22') + call g:assert.equals(getline(7), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), '', 'failed at #23') + call g:assert.equals(getline(4), ' foo', 'failed at #23') + call g:assert.equals(getline(5), '', 'failed at #23') + " call g:assert.equals(getline(6), ' ]', 'failed at #23') + call g:assert.equals(getline(7), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), '', 'failed at #24') + call g:assert.equals(getline(4), ' foo', 'failed at #24') + call g:assert.equals(getline(5), '', 'failed at #24') + " call g:assert.equals(getline(6), ' ]', 'failed at #24') + call g:assert.equals(getline(7), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), '', 'failed at #25') + call g:assert.equals(getline(4), ' foo', 'failed at #25') + call g:assert.equals(getline(5), '', 'failed at #25') + " call g:assert.equals(getline(6), ' ]', 'failed at #25') + call g:assert.equals(getline(7), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal saVl0 + call g:assert.equals(getline(1), '{', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), '', 'failed at #26') + call g:assert.equals(getline(4), ' foo', 'failed at #26') + call g:assert.equals(getline(5), '', 'failed at #26') + call g:assert.equals(getline(6), ']', 'failed at #26') + call g:assert.equals(getline(7), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') +endfunction +"}}} +function! s:suite.linewise_n_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{", "}"], 'input': ['a']}, + \ {'buns': ["{", "}"], 'indentkeys': '0},0),:,0#,!^F,o,e', 'input': ['1']}, + \ ] + + """ cinkeys + setlocal autoindent + call operator#sandwich#set('add', 'line', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys', '0},0),:,0#,!^F,o,e') + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), ' foo', 'failed at #1') + call g:assert.equals(getline(3), ' }', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', 3) + + " #2 + setlocal cinkeys=0},0),:,0#,!^F,o,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys+', 'O,0{') + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys-', 'O,0{') + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + normal saVl1 + call g:assert.equals(getline(1), ' {', 'failed at #4') + call g:assert.equals(getline(2), ' foo', 'failed at #4') + call g:assert.equals(getline(3), ' }', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('add', 'line', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys', '0},0),:,0#,!^F,o,e') + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), ' foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0},0),:,0#,!^F,o,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys+', 'O,0{') + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys-', 'O,0{') + call setline('.', ' foo') + normal saVla + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), ' foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + normal saVl1 + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), ' foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +function! s:suite.linewise_x_default_recipes() abort "{{{ + " #1 + call setline('.', 'foo') + normal Vsa( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal Vsa) + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), ')', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + + %delete + + " #3 + call setline('.', 'foo') + normal Vsa[ + call g:assert.equals(getline(1), '[', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), ']', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + normal Vsa] + call g:assert.equals(getline(1), '[', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), ']', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo') + normal Vsa{ + call g:assert.equals(getline(1), '{', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), '}', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #5') + + %delete + + " #6 + call setline('.', 'foo') + normal Vsa} + call g:assert.equals(getline(1), '{', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #6') + + %delete + + " #7 + call setline('.', 'foo') + normal Vsa< + call g:assert.equals(getline(1), '<', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), '>', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #7') + + %delete + + " #8 + call setline('.', 'foo') + normal Vsa> + call g:assert.equals(getline(1), '<', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), '>', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_x_not_registered() abort "{{{ + " #1 + call setline('.', 'foo') + normal Vsaa + call g:assert.equals(getline(1), 'a', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), 'a', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal Vsa* + call g:assert.equals(getline(1), '*', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), '*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_x_positioning() abort "{{{ + " #1 + call append(0, ['foo', 'bar', 'baz']) + normal ggV2jsa( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), 'bar', 'failed at #1') + call g:assert.equals(getline(4), 'baz', 'failed at #1') + call g:assert.equals(getline(5), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') +endfunction +"}}} +function! s:suite.linewise_x_a_character() abort "{{{ + " #1 + call setline('.', 'a') + normal Vsa( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'a', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') +endfunction +"}}} +function! s:suite.linewise_x_breaking() abort "{{{ + " #1 + call append(0, ['', 'foo']) + normal ggVjsa( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), '', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 4, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', '']) + normal ggVjsa( + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), '', 'failed at #2') + call g:assert.equals(getline(4), ')', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 4, 2, 0], 'failed at #2') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #3 + call setline('.', 'foo') + normal Vsaa + call g:assert.equals(getline(1), 'aa', 'failed at #3') + call g:assert.equals(getline(2), 'aaa', 'failed at #3') + call g:assert.equals(getline(3), 'foo', 'failed at #3') + call g:assert.equals(getline(4), 'aaa', 'failed at #3') + call g:assert.equals(getline(5), 'aa', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + normal Vsab + call g:assert.equals(getline(1), 'bb', 'failed at #4') + call g:assert.equals(getline(2), 'bbb', 'failed at #4') + call g:assert.equals(getline(3), 'bb', 'failed at #4') + call g:assert.equals(getline(4), 'foo', 'failed at #4') + call g:assert.equals(getline(5), 'bb', 'failed at #4') + call g:assert.equals(getline(6), 'bbb', 'failed at #4') + call g:assert.equals(getline(7), 'bb', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 4, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 7, 3, 0], 'failed at #4') + + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_x_count() abort "{{{ + " #1 + call setline('.', 'foo') + normal V2sa([ + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), '(', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ')', 'failed at #1') + call g:assert.equals(getline(5), ']', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal V3sa([{ + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), '[', 'failed at #2') + call g:assert.equals(getline(3), '(', 'failed at #2') + call g:assert.equals(getline(4), 'foo', 'failed at #2') + call g:assert.equals(getline(5), ')', 'failed at #2') + call g:assert.equals(getline(6), ']', 'failed at #2') + call g:assert.equals(getline(7), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 4, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_x_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call setline('.', 'α') + normal 0Vsa( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'α', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'aα') + normal 0Vsa( + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'aα', 'failed at #2') + call g:assert.equals(getline(3), ')', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call setline('.', 'a') + normal 0Vsaa + call g:assert.equals(getline(1), 'α', 'failed at #3') + call g:assert.equals(getline(2), 'a', 'failed at #3') + call g:assert.equals(getline(3), 'α', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', 'α') + normal 0Vsaa + call g:assert.equals(getline(1), 'α', 'failed at #4') + call g:assert.equals(getline(2), 'α', 'failed at #4') + call g:assert.equals(getline(3), 'α', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #4') + + %delete + + " #5 + call setline('.', 'aα') + normal 0Vsaa + call g:assert.equals(getline(1), 'α', 'failed at #5') + call g:assert.equals(getline(2), 'aα', 'failed at #5') + call g:assert.equals(getline(3), 'α', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #5') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call setline('.', 'a') + normal 0Vsaa + call g:assert.equals(getline(1), 'aα', 'failed at #6') + call g:assert.equals(getline(2), 'a', 'failed at #6') + call g:assert.equals(getline(3), 'aα', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #6') + + %delete + + " #7 + call setline('.', 'α') + normal 0Vsaa + call g:assert.equals(getline(1), 'aα', 'failed at #7') + call g:assert.equals(getline(2), 'α', 'failed at #7') + call g:assert.equals(getline(3), 'aα', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #7') + + %delete + + " #8 + call setline('.', 'aα') + normal 0Vsaa + call g:assert.equals(getline(1), 'aα', 'failed at #8') + call g:assert.equals(getline(2), 'aα', 'failed at #8') + call g:assert.equals(getline(3), 'aα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #8') + + %delete + unlet g:operator#sandwich#recipes + + " #9 + call setline('.', '“') + normal 0Vsa( + call g:assert.equals(getline(1), '(', 'failed at #9') + call g:assert.equals(getline(2), '“', 'failed at #9') + call g:assert.equals(getline(3), ')', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #9') + + %delete + + " #10 + call setline('.', 'a“') + normal 0Vsa( + call g:assert.equals(getline(1), '(', 'failed at #10') + call g:assert.equals(getline(2), 'a“', 'failed at #10') + call g:assert.equals(getline(3), ')', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #10') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call setline('.', 'a') + normal 0Vsaa + call g:assert.equals(getline(1), '“', 'failed at #11') + call g:assert.equals(getline(2), 'a', 'failed at #11') + call g:assert.equals(getline(3), '“', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #11') + + %delete + + " #12 + call setline('.', '“') + normal 0Vsaa + call g:assert.equals(getline(1), '“', 'failed at #12') + call g:assert.equals(getline(2), '“', 'failed at #12') + call g:assert.equals(getline(3), '“', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #12') + + %delete + + " #13 + call setline('.', 'a“') + normal 0Vsaa + call g:assert.equals(getline(1), '“', 'failed at #13') + call g:assert.equals(getline(2), 'a“', 'failed at #13') + call g:assert.equals(getline(3), '“', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #13') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call setline('.', 'a') + normal 0Vsaa + call g:assert.equals(getline(1), 'a“', 'failed at #14') + call g:assert.equals(getline(2), 'a', 'failed at #14') + call g:assert.equals(getline(3), 'a“', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #14') + + %delete + + " #15 + call setline('.', '“') + normal 0Vsaa + call g:assert.equals(getline(1), 'a“', 'failed at #15') + call g:assert.equals(getline(2), '“', 'failed at #15') + call g:assert.equals(getline(3), 'a“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #15') + + %delete + + " #16 + call setline('.', 'a“') + normal 0Vsaa + call g:assert.equals(getline(1), 'a“', 'failed at #16') + call g:assert.equals(getline(2), 'a“', 'failed at #16') + call g:assert.equals(getline(3), 'a“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #16') +endfunction +"}}} +function! s:suite.linewise_x_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', 'foo') + normal 0lV2sa() + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), '(', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ')', 'failed at #1') + call g:assert.equals(getline(5), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + + " #2 + normal 2lVsa( + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), '(', 'failed at #2') + call g:assert.equals(getline(3), '(', 'failed at #2') + call g:assert.equals(getline(4), 'foo', 'failed at #2') + call g:assert.equals(getline(5), ')', 'failed at #2') + call g:assert.equals(getline(6), ')', 'failed at #2') + call g:assert.equals(getline(7), ')', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 4, 1, 0], 'failed at #2') + + %delete + + " #3 + call setline('.', ' foo') + normal 0Vsa( + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ')', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + + %delete + + """ inner_head + call operator#sandwich#set('add', 'line', 'cursor', 'inner_head') + " #4 + call setline('.', 'foo') + normal 0lV2sa() + call g:assert.equals(getline(1), '(', 'failed at #4') + call g:assert.equals(getline(2), '(', 'failed at #4') + call g:assert.equals(getline(3), 'foo', 'failed at #4') + call g:assert.equals(getline(4), ')', 'failed at #4') + call g:assert.equals(getline(5), ')', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #4') + + " #5 + normal 2lVsa( + call g:assert.equals(getline(1), '(', 'failed at #5') + call g:assert.equals(getline(2), '(', 'failed at #5') + call g:assert.equals(getline(3), '(', 'failed at #5') + call g:assert.equals(getline(4), 'foo', 'failed at #5') + call g:assert.equals(getline(5), ')', 'failed at #5') + call g:assert.equals(getline(6), ')', 'failed at #5') + call g:assert.equals(getline(7), ')', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 4, 1, 0], 'failed at #5') + + %delete + + """ keep + " #6 + call operator#sandwich#set('add', 'line', 'cursor', 'keep') + call setline('.', 'foo') + normal 0lV2sa() + call g:assert.equals(getline(1), '(', 'failed at #6') + call g:assert.equals(getline(2), '(', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ')', 'failed at #6') + call g:assert.equals(getline(5), ')', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 2, 0], 'failed at #6') + + " #7 + normal Vsa( + call g:assert.equals(getline(1), '(', 'failed at #7') + call g:assert.equals(getline(2), '(', 'failed at #7') + call g:assert.equals(getline(3), '(', 'failed at #7') + call g:assert.equals(getline(4), 'foo', 'failed at #7') + call g:assert.equals(getline(5), ')', 'failed at #7') + call g:assert.equals(getline(6), ')', 'failed at #7') + call g:assert.equals(getline(7), ')', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 4, 2, 0], 'failed at #7') + + %delete + + """ inner_tail + " #8 + call operator#sandwich#set('add', 'line', 'cursor', 'inner_tail') + call setline('.', 'foo') + normal 0lV2sa() + call g:assert.equals(getline(1), '(', 'failed at #8') + call g:assert.equals(getline(2), '(', 'failed at #8') + call g:assert.equals(getline(3), 'foo', 'failed at #8') + call g:assert.equals(getline(4), ')', 'failed at #8') + call g:assert.equals(getline(5), ')', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #8') + + " #9 + normal 2hVsa( + call g:assert.equals(getline(1), '(', 'failed at #9') + call g:assert.equals(getline(2), '(', 'failed at #9') + call g:assert.equals(getline(3), '(', 'failed at #9') + call g:assert.equals(getline(4), 'foo', 'failed at #9') + call g:assert.equals(getline(5), ')', 'failed at #9') + call g:assert.equals(getline(6), ')', 'failed at #9') + call g:assert.equals(getline(7), ')', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 4, 3, 0], 'failed at #9') + + %delete + + """ head + " #10 + call operator#sandwich#set('add', 'line', 'cursor', 'head') + call setline('.', 'foo') + normal 0lV2sa() + call g:assert.equals(getline(1), '(', 'failed at #10') + call g:assert.equals(getline(2), '(', 'failed at #10') + call g:assert.equals(getline(3), 'foo', 'failed at #10') + call g:assert.equals(getline(4), ')', 'failed at #10') + call g:assert.equals(getline(5), ')', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + + " #11 + normal 2jVsa( + call g:assert.equals(getline(1), '(', 'failed at #11') + call g:assert.equals(getline(2), '(', 'failed at #11') + call g:assert.equals(getline(3), '(', 'failed at #11') + call g:assert.equals(getline(4), 'foo', 'failed at #11') + call g:assert.equals(getline(5), ')', 'failed at #11') + call g:assert.equals(getline(6), ')', 'failed at #11') + call g:assert.equals(getline(7), ')', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #11') + + %delete + + """ tail + " #12 + call operator#sandwich#set('add', 'line', 'cursor', 'tail') + call setline('.', 'foo') + normal 0lV2sa() + call g:assert.equals(getline(1), '(', 'failed at #12') + call g:assert.equals(getline(2), '(', 'failed at #12') + call g:assert.equals(getline(3), 'foo', 'failed at #12') + call g:assert.equals(getline(4), ')', 'failed at #12') + call g:assert.equals(getline(5), ')', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 5, 1, 0], 'failed at #12') + + " #13 + normal 2kVsa( + call g:assert.equals(getline(1), '(', 'failed at #13') + call g:assert.equals(getline(2), '(', 'failed at #13') + call g:assert.equals(getline(3), '(', 'failed at #13') + call g:assert.equals(getline(4), 'foo', 'failed at #13') + call g:assert.equals(getline(5), ')', 'failed at #13') + call g:assert.equals(getline(6), ')', 'failed at #13') + call g:assert.equals(getline(7), ')', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 5, 1, 0], 'failed at #13') + + %delete + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('add', 'line', 'cursor', 'inner_tail') + call setline('.', 'foo') + normal Vsa1 + call g:assert.equals(getline(1), '(', 'failed at #14') + call g:assert.equals(getline(2), 'foo', 'failed at #14') + call g:assert.equals(getline(3), ')', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.linewise_x_option_query_once() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call setline('.', 'foo') + normal V3sa([{ + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), '(', 'failed at #1') + call g:assert.equals(getline(4), 'foo', 'failed at #1') + call g:assert.equals(getline(5), ')', 'failed at #1') + call g:assert.equals(getline(6), ']', 'failed at #1') + call g:assert.equals(getline(7), '}', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal V3sa1 + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), '(', 'failed at #2') + call g:assert.equals(getline(3), '(', 'failed at #2') + call g:assert.equals(getline(4), 'foo', 'failed at #2') + call g:assert.equals(getline(5), ')', 'failed at #2') + call g:assert.equals(getline(6), ')', 'failed at #2') + call g:assert.equals(getline(7), ')', 'failed at #2') + + %delete + + """ on + " #3 + call operator#sandwich#set('add', 'line', 'query_once', 1) + call setline('.', 'foo') + normal V3sa( + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), '(', 'failed at #3') + call g:assert.equals(getline(3), '(', 'failed at #3') + call g:assert.equals(getline(4), 'foo', 'failed at #3') + call g:assert.equals(getline(5), ')', 'failed at #3') + call g:assert.equals(getline(6), ')', 'failed at #3') + call g:assert.equals(getline(7), ')', 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + normal V3sa0[{ + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), '[', 'failed at #4') + call g:assert.equals(getline(3), '(', 'failed at #4') + call g:assert.equals(getline(4), 'foo', 'failed at #4') + call g:assert.equals(getline(5), ')', 'failed at #4') + call g:assert.equals(getline(6), ']', 'failed at #4') + call g:assert.equals(getline(7), '}', 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_x_option_expr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input':['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'foo') + normal Vsaa + call g:assert.equals(getline(1), '1+1', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '1+2', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal 0Vsa1 + call g:assert.equals(getline(1), '2', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), '3', 'failed at #2') + + %delete + + """ 1 + " #3 + call operator#sandwich#set('add', 'line', 'expr', 1) + call setline('.', 'foo') + normal Vsaa + call g:assert.equals(getline(1), '2', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '3', 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + normal Vsab + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo') + normal Vsac + call g:assert.equals(getline(1), 'foo', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + %delete + + " #6 + call setline('.', 'foo') + normal V2saab + call g:assert.equals(getline(1), '2', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), '3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + %delete + + " #7 + call setline('.', 'foo') + normal V2saac + call g:assert.equals(getline(1), '2', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), '3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + %delete + + " #8 + call setline('.', 'foo') + normal V2saba + call g:assert.equals(getline(1), 'foo', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + %delete + + " #9 + call setline('.', 'foo') + normal V2saca + call g:assert.equals(getline(1), 'foo', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + %delete + + " #10 + call setline('.', 'foo') + normal 0Vsa0 + call g:assert.equals(getline(1), '1+1', 'failed at #10') + call g:assert.equals(getline(2), 'foo', 'failed at #10') + call g:assert.equals(getline(3), '1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes only in + " dot-repeat. +endfunction +"}}} +function! s:suite.linewise_x_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0Vsaa + call g:assert.equals(getline(1), 'bar', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + %delete + + " #2 + call setline('.', 'bar') + normal 0Vsa1 + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + + %delete + + """ 1 + " #3 + call operator#sandwich#set('add', 'line', 'listexpr', 1) + call setline('.', 'bar') + normal 0Vsaa + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getline(2), 'bar', 'failed at #3') + call g:assert.equals(getline(3), 'baz', 'failed at #3') + + %delete + + " #4 + call setline('.', 'bar') + normal 0Vsab + call g:assert.equals(getline(1), 'bar', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + %delete + + " #5 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0Vsa0 + call g:assert.equals(getline(1), 'bar', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + %delete + + " #6 + call setline('.', 'bar') + normal 0Vsa1 + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getline(2), 'bar', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.linewise_x_option_noremap() abort "{{{ + """"" noremap + let g:operator#sandwich#recipes = [ + \ {'buns': ['[', ']'], 'input':['[']}, + \ {'buns': ['[', ']'], 'noremap': 0, 'input':['0']}, + \ {'buns': ['[', ']'], 'noremap': 1, 'input':['1']}, + \ ] + inoremap [ { + inoremap ] } + + """ on + " #1 + call setline('.', 'foo') + normal Vsa[ + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ']', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal Vsa0 + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + + %delete + + """ off + " #3 + call operator#sandwich#set('add', 'line', 'noremap', 0) + call setline('.', 'foo') + normal Vsa[ + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '}', 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + normal Vsa1 + call g:assert.equals(getline(1), '[', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), ']', 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_x_option_skip_space() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'skip_space': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'skip_space': 1, 'input':['1']}, + \ ] + + """"" skip_space + """ on + " #1 + call setline('.', 'foo ') + normal Vsa( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo ', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo ') + normal Vsa0 + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'foo ', 'failed at #2') + call g:assert.equals(getline(3), ')', 'failed at #2') + + %delete + + """ off + " #3 + call operator#sandwich#set('add', 'line', 'skip_space', 0) + call setline('.', 'foo ') + normal Vsa( + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), 'foo ', 'failed at #3') + call g:assert.equals(getline(3), ')', 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo ') + normal Vsa1 + call g:assert.equals(getline(1), '(', 'failed at #4') + call g:assert.equals(getline(2), 'foo ', 'failed at #4') + call g:assert.equals(getline(3), ')', 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_x_option_command() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['"', '"'], 'command': ['normal! `[d`]'], 'input':['1']}, + \ ] + + """"" command + " #1 + call operator#sandwich#set('add', 'line', 'command', ["normal! `[d`]"]) + call append(0, ['[', 'foo', ']']) + normal ggjVsa( + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), ']', 'failed at #1') + + %delete + + " #2 + call operator#sandwich#set('add', 'line', 'command', []) + call append(0, ['[', 'foo', ']']) + normal ggjVsa1 + call g:assert.equals(getline(1), '[', 'failed at #2') + call g:assert.equals(getline(2), ']', 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_x_option_linewise() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'linewise': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'linewise': 1, 'input':['1']}, + \ ] + + """"" linewise + """ off + " #1 + call operator#sandwich#set('add', 'line', 'linewise', 0) + call setline('.', 'foo') + normal Vsa( + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + normal Vsa1 + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), ')', 'failed at #2') + + %delete + call operator#sandwich#set('add', 'line', 'linewise', 1) + + """ on + " #3 + set autoindent + call setline('.', ' foo') + normal Vsa( + call g:assert.equals(getline(1), ' (', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ' )', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + set autoindent& + + %delete + + " #4 + call setline('.', 'foo') + normal Vsa0 + call g:assert.equals(getline(1), '(foo)', 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_x_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + + """ -1 + call operator#sandwich#set('add', 'line', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + call g:assert.equals(getline(4), ' foo', 'failed at #1') + call g:assert.equals(getline(5), '', 'failed at #1') + call g:assert.equals(getline(6), ']', 'failed at #1') + call g:assert.equals(getline(7), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), '', 'failed at #2') + call g:assert.equals(getline(4), ' foo', 'failed at #2') + call g:assert.equals(getline(5), '', 'failed at #2') + call g:assert.equals(getline(6), ' ]', 'failed at #2') + call g:assert.equals(getline(7), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), '', 'failed at #3') + call g:assert.equals(getline(4), ' foo', 'failed at #3') + call g:assert.equals(getline(5), '', 'failed at #3') + call g:assert.equals(getline(6), ' ]', 'failed at #3') + call g:assert.equals(getline(7), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), '', 'failed at #4') + call g:assert.equals(getline(4), ' foo', 'failed at #4') + call g:assert.equals(getline(5), '', 'failed at #4') + " call g:assert.equals(getline(6), ' ]', 'failed at #4') + call g:assert.equals(getline(7), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call setline('.', ' foo') + " normal Vsaa + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), '', 'failed at #5') + " call g:assert.equals(getline(4), ' foo', 'failed at #5') + " call g:assert.equals(getline(5), '', 'failed at #5') + " call g:assert.equals(getline(6), ' ]', 'failed at #5') + " call g:assert.equals(getline(7), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 7, 22, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('add', 'line', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), '', 'failed at #6') + call g:assert.equals(getline(4), ' foo', 'failed at #6') + call g:assert.equals(getline(5), '', 'failed at #6') + call g:assert.equals(getline(6), ']', 'failed at #6') + call g:assert.equals(getline(7), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), '', 'failed at #7') + call g:assert.equals(getline(4), ' foo', 'failed at #7') + call g:assert.equals(getline(5), '', 'failed at #7') + call g:assert.equals(getline(6), ']', 'failed at #7') + call g:assert.equals(getline(7), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), '', 'failed at #8') + call g:assert.equals(getline(4), ' foo', 'failed at #8') + call g:assert.equals(getline(5), '', 'failed at #8') + call g:assert.equals(getline(6), ']', 'failed at #8') + call g:assert.equals(getline(7), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), '', 'failed at #9') + call g:assert.equals(getline(4), ' foo', 'failed at #9') + call g:assert.equals(getline(5), '', 'failed at #9') + call g:assert.equals(getline(6), ']', 'failed at #9') + call g:assert.equals(getline(7), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), '', 'failed at #10') + call g:assert.equals(getline(4), ' foo', 'failed at #10') + call g:assert.equals(getline(5), '', 'failed at #10') + call g:assert.equals(getline(6), ']', 'failed at #10') + call g:assert.equals(getline(7), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('add', 'line', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), '', 'failed at #11') + call g:assert.equals(getline(4), ' foo', 'failed at #11') + call g:assert.equals(getline(5), '', 'failed at #11') + call g:assert.equals(getline(6), ' ]', 'failed at #11') + call g:assert.equals(getline(7), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), '', 'failed at #12') + call g:assert.equals(getline(4), ' foo', 'failed at #12') + call g:assert.equals(getline(5), '', 'failed at #12') + call g:assert.equals(getline(6), ' ]', 'failed at #12') + call g:assert.equals(getline(7), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), '', 'failed at #13') + call g:assert.equals(getline(4), ' foo', 'failed at #13') + call g:assert.equals(getline(5), '', 'failed at #13') + call g:assert.equals(getline(6), ' ]', 'failed at #13') + call g:assert.equals(getline(7), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), '', 'failed at #14') + call g:assert.equals(getline(4), ' foo', 'failed at #14') + call g:assert.equals(getline(5), '', 'failed at #14') + call g:assert.equals(getline(6), ' ]', 'failed at #14') + call g:assert.equals(getline(7), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), '', 'failed at #15') + call g:assert.equals(getline(4), ' foo', 'failed at #15') + call g:assert.equals(getline(5), '', 'failed at #15') + call g:assert.equals(getline(6), ' ]', 'failed at #15') + call g:assert.equals(getline(7), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('add', 'line', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), '', 'failed at #16') + call g:assert.equals(getline(4), ' foo', 'failed at #16') + call g:assert.equals(getline(5), '', 'failed at #16') + call g:assert.equals(getline(6), ' ]', 'failed at #16') + call g:assert.equals(getline(7), '}', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), '', 'failed at #17') + call g:assert.equals(getline(4), ' foo', 'failed at #17') + call g:assert.equals(getline(5), '', 'failed at #17') + call g:assert.equals(getline(6), ' ]', 'failed at #17') + call g:assert.equals(getline(7), '}', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), '', 'failed at #18') + call g:assert.equals(getline(4), ' foo', 'failed at #18') + call g:assert.equals(getline(5), '', 'failed at #18') + call g:assert.equals(getline(6), ' ]', 'failed at #18') + call g:assert.equals(getline(7), '}', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), '', 'failed at #19') + call g:assert.equals(getline(4), ' foo', 'failed at #19') + call g:assert.equals(getline(5), '', 'failed at #19') + call g:assert.equals(getline(6), ' ]', 'failed at #19') + call g:assert.equals(getline(7), '}', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), '', 'failed at #20') + call g:assert.equals(getline(4), ' foo', 'failed at #20') + call g:assert.equals(getline(5), '', 'failed at #20') + call g:assert.equals(getline(6), ' ]', 'failed at #20') + call g:assert.equals(getline(7), '}', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('add', 'line', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), '', 'failed at #21') + call g:assert.equals(getline(4), ' foo', 'failed at #21') + call g:assert.equals(getline(5), '', 'failed at #21') + " call g:assert.equals(getline(6), ' ]', 'failed at #21') + call g:assert.equals(getline(7), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), '', 'failed at #22') + call g:assert.equals(getline(4), ' foo', 'failed at #22') + call g:assert.equals(getline(5), '', 'failed at #22') + " call g:assert.equals(getline(6), ' ]', 'failed at #22') + call g:assert.equals(getline(7), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), '', 'failed at #23') + call g:assert.equals(getline(4), ' foo', 'failed at #23') + call g:assert.equals(getline(5), '', 'failed at #23') + " call g:assert.equals(getline(6), ' ]', 'failed at #23') + call g:assert.equals(getline(7), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), '', 'failed at #24') + call g:assert.equals(getline(4), ' foo', 'failed at #24') + call g:assert.equals(getline(5), '', 'failed at #24') + " call g:assert.equals(getline(6), ' ]', 'failed at #24') + call g:assert.equals(getline(7), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), '', 'failed at #25') + call g:assert.equals(getline(4), ' foo', 'failed at #25') + call g:assert.equals(getline(5), '', 'failed at #25') + " call g:assert.equals(getline(6), ' ]', 'failed at #25') + call g:assert.equals(getline(7), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + normal Vsa0 + call g:assert.equals(getline(1), '{', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), '', 'failed at #26') + call g:assert.equals(getline(4), ' foo', 'failed at #26') + call g:assert.equals(getline(5), '', 'failed at #26') + call g:assert.equals(getline(6), ']', 'failed at #26') + call g:assert.equals(getline(7), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') +endfunction +"}}} +function! s:suite.linewise_x_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{", "}"], 'input': ['a']}, + \ {'buns': ["{", "}"], 'indentkeys': '0},0),:,0#,!^F,o,e', 'input': ['1']}, + \ ] + + """ cinkeys + setlocal autoindent + call operator#sandwich#set('add', 'line', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys', '0},0),:,0#,!^F,o,e') + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), ' foo', 'failed at #1') + call g:assert.equals(getline(3), ' }', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', 3) + + " #2 + setlocal cinkeys=0},0),:,0#,!^F,o,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys+', 'O,0{') + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys-', 'O,0{') + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + normal Vsa1 + call g:assert.equals(getline(1), ' {', 'failed at #4') + call g:assert.equals(getline(2), ' foo', 'failed at #4') + call g:assert.equals(getline(3), ' }', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('add', 'line', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys', '0},0),:,0#,!^F,o,e') + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), ' foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0},0),:,0#,!^F,o,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys+', 'O,0{') + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'line', 'indentkeys-', 'O,0{') + call setline('.', ' foo') + normal Vsaa + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), ' foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'line', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + normal Vsa1 + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), ' foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +" block-wise +function! s:suite.blockwise_n_default_recipes() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11l(" + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + call g:assert.equals(getline(2), '(bar)', 'failed at #1') + call g:assert.equals(getline(3), '(baz)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11l)" + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + call g:assert.equals(getline(2), '(bar)', 'failed at #2') + call g:assert.equals(getline(3), '(baz)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11l[" + call g:assert.equals(getline(1), '[foo]', 'failed at #3') + call g:assert.equals(getline(2), '[bar]', 'failed at #3') + call g:assert.equals(getline(3), '[baz]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11l]" + call g:assert.equals(getline(1), '[foo]', 'failed at #4') + call g:assert.equals(getline(2), '[bar]', 'failed at #4') + call g:assert.equals(getline(3), '[baz]', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11l{" + call g:assert.equals(getline(1), '{foo}', 'failed at #5') + call g:assert.equals(getline(2), '{bar}', 'failed at #5') + call g:assert.equals(getline(3), '{baz}', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11l}" + call g:assert.equals(getline(1), '{foo}', 'failed at #6') + call g:assert.equals(getline(2), '{bar}', 'failed at #6') + call g:assert.equals(getline(3), '{baz}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11l<" + call g:assert.equals(getline(1), '', 'failed at #7') + call g:assert.equals(getline(2), '', 'failed at #7') + call g:assert.equals(getline(3), '', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11l>" + call g:assert.equals(getline(1), '', 'failed at #8') + call g:assert.equals(getline(2), '', 'failed at #8') + call g:assert.equals(getline(3), '', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_not_registered() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11la" + call g:assert.equals(getline(1), 'afooa', 'failed at #1') + call g:assert.equals(getline(2), 'abara', 'failed at #1') + call g:assert.equals(getline(3), 'abaza', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11l*" + call g:assert.equals(getline(1), '*foo*', 'failed at #2') + call g:assert.equals(getline(2), '*bar*', 'failed at #2') + call g:assert.equals(getline(3), '*baz*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #2') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_positioning() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['foobarbaz', 'foobarbaz', 'foobarbaz']) + execute "normal ggsa\23l(" + call g:assert.equals(getline(1), '(foo)barbaz', 'failed at #1') + call g:assert.equals(getline(2), '(foo)barbaz', 'failed at #1') + call g:assert.equals(getline(3), '(foo)barbaz', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foobarbaz', 'foobarbaz', 'foobarbaz']) + execute "normal ggfbsa\23l(" + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #2') + call g:assert.equals(getline(2), 'foo(bar)baz', 'failed at #2') + call g:assert.equals(getline(3), 'foo(bar)baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 9, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['foobarbaz', 'foobarbaz', 'foobarbaz']) + execute "normal gg2fbsa\23l(" + call g:assert.equals(getline(1), 'foobar(baz)', 'failed at #3') + call g:assert.equals(getline(2), 'foobar(baz)', 'failed at #3') + call g:assert.equals(getline(3), 'foobar(baz)', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 7, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 12, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['foo', '', 'baz']) + execute "normal ggsa\8l(" + call g:assert.equals(getline(1), '(foo)', 'failed at #4') + call g:assert.equals(getline(2), '', 'failed at #4') + call g:assert.equals(getline(3), '(baz)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['foo', 'ba', 'baz']) + execute "normal ggsa\10l(" + call g:assert.equals(getline(1), '(foo)', 'failed at #5') + call g:assert.equals(getline(2), '(ba)', 'failed at #5') + call g:assert.equals(getline(3), '(baz)', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['fo', 'bar', 'baz']) + execute "normal ggsa\10l(" + call g:assert.equals(getline(1), '(fo)', 'failed at #6') + call g:assert.equals(getline(2), '(bar)', 'failed at #6') + call g:assert.equals(getline(3), '(baz)', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['foo', 'bar*', 'baz']) + execute "normal ggsa\12l(" + call g:assert.equals(getline(1), '(foo)', 'failed at #7') + call g:assert.equals(getline(2), '(bar)*', 'failed at #7') + call g:assert.equals(getline(3), '(baz)', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_a_character() abort "{{{ + set whichwrap=h,l + + " #1 + call setline('.', 'a') + execute "normal 0sa\l(" + call g:assert.equals(getline('.'), '(a)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['a', 'a', 'a']) + execute "normal ggsa\2j(" + call g:assert.equals(getline(1), '(a)', 'failed at #2') + call g:assert.equals(getline(2), '(a)', 'failed at #2') + call g:assert.equals(getline(3), '(a)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #2') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_breaking() abort "{{{ + set whichwrap=h,l + + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #1 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11la" + call g:assert.equals(getline(1), 'aa', 'failed at #1') + call g:assert.equals(getline(2), 'aaafooaaa', 'failed at #1') + call g:assert.equals(getline(3), 'aa', 'failed at #1') + call g:assert.equals(getline(4), 'aa', 'failed at #1') + call g:assert.equals(getline(5), 'aaabaraaa', 'failed at #1') + call g:assert.equals(getline(6), 'aa', 'failed at #1') + call g:assert.equals(getline(7), 'aa', 'failed at #1') + call g:assert.equals(getline(8), 'aaabazaaa', 'failed at #1') + call g:assert.equals(getline(9), 'aa', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 9, 3, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11lb" + call g:assert.equals(getline(1), 'bb', 'failed at #2') + call g:assert.equals(getline(2), 'bbb', 'failed at #2') + call g:assert.equals(getline(3), 'bbfoobb', 'failed at #2') + call g:assert.equals(getline(4), 'bbb', 'failed at #2') + call g:assert.equals(getline(5), 'bb', 'failed at #2') + call g:assert.equals(getline(6), 'bb', 'failed at #2') + call g:assert.equals(getline(7), 'bbb', 'failed at #2') + call g:assert.equals(getline(8), 'bbbarbb', 'failed at #2') + call g:assert.equals(getline(9), 'bbb', 'failed at #2') + call g:assert.equals(getline(10), 'bb', 'failed at #2') + call g:assert.equals(getline(11), 'bb', 'failed at #2') + call g:assert.equals(getline(12), 'bbb', 'failed at #2') + call g:assert.equals(getline(13), 'bbbazbb', 'failed at #2') + call g:assert.equals(getline(14), 'bbb', 'failed at #2') + call g:assert.equals(getline(15), 'bb', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 15, 3, 0], 'failed at #2') + + unlet! g:operator#sandwich#recipes + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_count() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg2sa\11l([" + call g:assert.equals(getline(1), '[(foo)]', 'failed at #1') + call g:assert.equals(getline(2), '[(bar)]', 'failed at #1') + call g:assert.equals(getline(3), '[(baz)]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 8, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg3sa\11l([{" + call g:assert.equals(getline(1), '{[(foo)]}', 'failed at #2') + call g:assert.equals(getline(2), '{[(bar)]}', 'failed at #2') + call g:assert.equals(getline(3), '{[(baz)]}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 10, 0], 'failed at #2') + + %delete + + " #3 + call setline('.', 'foo bar') + execute "normal 0sa\2iw(" + call g:assert.equals(getline('.'), '(foo) bar', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo bar') + execute "normal 0sa\3iw(" + call g:assert.equals(getline('.'), '(foo bar)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 10, 0], 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo bar') + execute "normal 02sa\3iw([" + call g:assert.equals(getline('.'), '[(foo bar)]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 12, 0], 'failed at #5') + %delete + + " #6 + call append(0, ['foobarbaz', 'foobarbaz', 'foobarbaz']) + execute "normal gg3l3sa\23l([{" + call g:assert.equals(getline(1), 'foo{[(bar)]}baz', 'failed at #6') + call g:assert.equals(getline(2), 'foo{[(bar)]}baz', 'failed at #6') + call g:assert.equals(getline(3), 'foo{[(bar)]}baz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 13, 0], 'failed at #6') +endfunction +"}}} +function! s:suite.blockwise_n_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + set whichwrap=h,l + + " #1 + call append(0, ['α', 'β', 'γ']) + execute "normal ggsa\5l(" + call g:assert.equals(getline(1), '(α)', 'failed at #1') + call g:assert.equals(getline(2), '(β)', 'failed at #1') + call g:assert.equals(getline(3), '(γ)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, strlen('(γ)')+1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['aα', 'bβ', 'cγ']) + execute "normal ggsa\8l(" + call g:assert.equals(getline(1), '(aα)', 'failed at #2') + call g:assert.equals(getline(2), '(bβ)', 'failed at #2') + call g:assert.equals(getline(3), '(cγ)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, strlen('(cγ)')+1, 0], 'failed at #2') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call append(0, ['a', 'b', 'c']) + execute "normal ggsa\5la" + call g:assert.equals(getline(1), 'αaα', 'failed at #3') + call g:assert.equals(getline(2), 'αbα', 'failed at #3') + call g:assert.equals(getline(3), 'αcα', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcα')+1, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['α', 'β', 'γ']) + execute "normal ggsa\5la" + call g:assert.equals(getline(1), 'ααα', 'failed at #4') + call g:assert.equals(getline(2), 'αβα', 'failed at #4') + call g:assert.equals(getline(3), 'αγα', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, strlen('αγα')+1, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['aα', 'bβ', 'cγ']) + execute "normal ggsa\8la" + call g:assert.equals(getline(1), 'αaαα', 'failed at #5') + call g:assert.equals(getline(2), 'αbβα', 'failed at #5') + call g:assert.equals(getline(3), 'αcγα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcγα')+1, 0], 'failed at #5') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call append(0, ['a', 'b', 'c']) + execute "normal ggsa\5la" + call g:assert.equals(getline(1), 'aαaaα', 'failed at #6') + call g:assert.equals(getline(2), 'aαbaα', 'failed at #6') + call g:assert.equals(getline(3), 'aαcaα', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcaα')+1, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['α', 'β', 'γ']) + execute "normal ggsa\5la" + call g:assert.equals(getline(1), 'aααaα', 'failed at #7') + call g:assert.equals(getline(2), 'aαβaα', 'failed at #7') + call g:assert.equals(getline(3), 'aαγaα', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαγaα')+1, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['aα', 'bβ', 'cγ']) + execute "normal ggsa\8la" + call g:assert.equals(getline(1), 'aαaαaα', 'failed at #8') + call g:assert.equals(getline(2), 'aαbβaα', 'failed at #8') + call g:assert.equals(getline(3), 'aαcγaα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcγaα')+1, 0], 'failed at #8') + + %delete + unlet g:operator#sandwich#recipes + + " #9 + call append(0, ['“', '“', '“']) + execute "normal ggsa\5l(" + call g:assert.equals(getline(1), '(“)', 'failed at #9') + call g:assert.equals(getline(2), '(“)', 'failed at #9') + call g:assert.equals(getline(3), '(“)', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, strlen('(“)')+1, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['a“', 'b“', 'c“']) + execute "normal ggsa\8l(" + call g:assert.equals(getline(1), '(a“)', 'failed at #10') + call g:assert.equals(getline(2), '(b“)', 'failed at #10') + call g:assert.equals(getline(3), '(c“)', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, strlen('(c“)')+1, 0], 'failed at #10') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call append(0, ['a', 'b', 'c']) + execute "normal ggsa\5la" + call g:assert.equals(getline(1), '“a“', 'failed at #11') + call g:assert.equals(getline(2), '“b“', 'failed at #11') + call g:assert.equals(getline(3), '“c“', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c“')+1, 0], 'failed at #11') + + %delete + + " #12 + call append(0, ['“', '“', '“']) + execute "normal ggsa\5la" + call g:assert.equals(getline(1), '“““', 'failed at #12') + call g:assert.equals(getline(2), '“““', 'failed at #12') + call g:assert.equals(getline(3), '“““', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, strlen('“““')+1, 0], 'failed at #12') + + %delete + + " #13 + call append(0, ['a“', 'b“', 'c“']) + execute "normal ggsa\8la" + call g:assert.equals(getline(1), '“a““', 'failed at #13') + call g:assert.equals(getline(2), '“b““', 'failed at #13') + call g:assert.equals(getline(3), '“c““', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c““')+1, 0], 'failed at #13') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call append(0, ['a', 'b', 'c']) + execute "normal ggsa\5la" + call g:assert.equals(getline(1), 'a“aa“', 'failed at #14') + call g:assert.equals(getline(2), 'a“ba“', 'failed at #14') + call g:assert.equals(getline(3), 'a“ca“', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“ca“')+1, 0], 'failed at #14') + + %delete + + " #15 + call append(0, ['“', '“', '“']) + execute "normal ggsa\5la" + call g:assert.equals(getline(1), 'a““a“', 'failed at #15') + call g:assert.equals(getline(2), 'a““a“', 'failed at #15') + call g:assert.equals(getline(3), 'a““a“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, strlen('a““a“')+1, 0], 'failed at #15') + + %delete + + " #16 + call append(0, ['a“', 'b“', 'c“']) + execute "normal ggsa\8la" + call g:assert.equals(getline(1), 'a“a“a“', 'failed at #16') + call g:assert.equals(getline(2), 'a“b“a“', 'failed at #16') + call g:assert.equals(getline(3), 'a“c“a“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“c“a“')+1, 0], 'failed at #16') +endfunction +"}}} +function! s:suite.blockwise_n_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', 'foo') + execute "normal 0l2sa\iw()" + call g:assert.equals(getline('.'), '((foo))', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + + " #2 + execute "normal 2lsa\iw(" + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + + " #3 + let g:operator#sandwich#recipes = [{'buns': ["(\n ", "\n)"], 'input':['a']}] + call setline('.', 'foo') + execute "normal 0sa\iwa" + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ')', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + %delete + unlet! g:operator#sandwich#recipes + + """ inner_head + call operator#sandwich#set('add', 'block', 'cursor', 'inner_head') + " #4 + call setline('.', 'foo') + execute "normal 0l2sa\iw()" + call g:assert.equals(getline('.'), '((foo))', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #4') + + " #5 + execute "normal 2lsa\iw(" + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #5') + + """ keep + " #6 + call operator#sandwich#set('add', 'block', 'cursor', 'keep') + call setline('.', 'foo') + execute "normal 0l2sa\iw()" + call g:assert.equals(getline('.'), '((foo))', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #6') + + " #7 + execute "normal lsa\iw(" + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #7') + + """ inner_tail + " #8 + call operator#sandwich#set('add', 'block', 'cursor', 'inner_tail') + call setline('.', 'foo') + execute "normal 0l2sa\iw()" + call g:assert.equals(getline('.'), '((foo))', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #8') + + " #9 + execute "normal 2hsa\iw(" + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #9') + + """ head + " #10 + call operator#sandwich#set('add', 'block', 'cursor', 'head') + call setline('.', 'foo') + execute "normal 0l2sa\iw()" + call g:assert.equals(getline('.'), '((foo))', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + + " #11 + execute "normal 3lsa\iw(" + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #11') + + """ tail + " #12 + call operator#sandwich#set('add', 'block', 'cursor', 'tail') + call setline('.', 'foo') + execute "normal 0l2sa\iw()" + call g:assert.equals(getline('.'), '((foo))', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #12') + + " #13 + execute "normal 3hsa\iw(" + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('add', 'block', 'cursor', 'inner_tail') + call setline('.', 'foo') + execute "normal 0lsa\iw1" + call g:assert.equals(getline('.'), '(foo)', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.blockwise_n_option_query_once() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call setline('.', 'foo') + execute "normal 03sa\iw([{" + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #1') + + " #2 + call setline('.', 'foo') + execute "normal 03sa\iw1" + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + """ on + " #3 + call operator#sandwich#set('add', 'block', 'query_once', 1) + call setline('.', 'foo') + execute "normal 03sa\iw(" + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #3') + + " #4 + call setline('.', 'foo') + execute "normal 03sa\iw0[{" + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #4') +endfunction +"}}} +function! s:suite.blockwise_n_option_expr() abort "{{{ + set whichwrap=h,l + + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input':['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'foo') + execute "normal 0sa\iwa" + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #1') + + " #2 + call setline('.', 'foo') + execute "normal 0sa\iw1" + call g:assert.equals(getline('.'), '2foo3', 'failed at #2') + + """ 1 + " #2 + call operator#sandwich#set('add', 'block', 'expr', 1) + call setline('.', 'foo') + execute "normal 0sa\iwa" + call g:assert.equals(getline('.'), '2foo3', 'failed at #2') + + %delete + + " #3 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11lb" + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getline(2), 'bar', 'failed at #3') + call g:assert.equals(getline(3), 'baz', 'failed at #3') + call g:assert.equals(exists(s:object), 0, 'failed at #3') + + %delete + + " #4 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsa\11lc" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + %delete + + " #5 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg2sa\11lab" + call g:assert.equals(getline(1), '2foo3', 'failed at #5') + call g:assert.equals(getline(2), '2bar3', 'failed at #5') + call g:assert.equals(getline(3), '2baz3', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + %delete + + " #6 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg2sa\11lac" + call g:assert.equals(getline(1), '2foo3', 'failed at #6') + call g:assert.equals(getline(2), '2bar3', 'failed at #6') + call g:assert.equals(getline(3), '2baz3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + %delete + + " #7 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg2sa\11lba" + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getline(2), 'bar', 'failed at #7') + call g:assert.equals(getline(3), 'baz', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + %delete + + " #8 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg2sa\11lca" + call g:assert.equals(getline(1), 'foo', 'failed at #8') + call g:assert.equals(getline(2), 'bar', 'failed at #8') + call g:assert.equals(getline(3), 'baz', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + %delete + + " #9 + call setline('.', 'foo') + execute "normal 0sa\iw0" + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #9') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.blockwise_n_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :execute "normal 0sa\iwa" + call g:assert.equals(getline('.'), 'bar', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + " #2 + call setline('.', 'bar') + execute "normal 0sa\iw1" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('add', 'block', 'listexpr', 1) + call setline('.', 'bar') + execute "normal 0sa\iwa" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + + " #4 + call setline('.', 'bar') + execute "normal 0sa\iwb" + call g:assert.equals(getline('.'), 'bar', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :execute "normal 0sa\iw0" + call g:assert.equals(getline('.'), 'bar', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', 'bar') + execute "normal 0sa\iw1" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.blockwise_n_option_noremap() abort "{{{ + """"" noremap + let g:operator#sandwich#recipes = [ + \ {'buns': ['[', ']'], 'input':['[']}, + \ {'buns': ['[', ']'], 'noremap': 0, 'input':['0']}, + \ {'buns': ['[', ']'], 'noremap': 1, 'input':['1']}, + \ ] + inoremap [ { + inoremap ] } + + """ on + " #1 + call setline('.', 'foo') + execute "normal 0sa\iw[" + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + + " #2 + call setline('.', 'foo') + execute "normal 0sa\iw0" + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + + """ off + " #3 + call operator#sandwich#set('add', 'block', 'noremap', 0) + call setline('.', 'foo') + execute "normal 0sa\iw[" + call g:assert.equals(getline('.'), '{foo}', 'failed at #3') + + " #4 + call setline('.', 'foo') + execute "normal 0sa\iw1" + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') +endfunction +"}}} +function! s:suite.blockwise_n_option_skip_space() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'skip_space': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'skip_space': 1, 'input':['1']}, + \ ] + + """"" skip_space + """ on + " #1 + call setline('.', 'foo ') + execute "normal 0sa\2iw(" + call g:assert.equals(getline('.'), '(foo) ', 'failed at #1') + + " #2 + call setline('.', 'foo ') + execute "normal 0sa\2iw0" + call g:assert.equals(getline('.'), '(foo )', 'failed at #2') + + """ off + " #3 + call operator#sandwich#set('add', 'block', 'skip_space', 0) + call setline('.', 'foo ') + execute "normal 0sa\2iw(" + call g:assert.equals(getline('.'), '(foo )', 'failed at #3') + + " #4 + call setline('.', 'foo ') + execute "normal 0sa\2iw1" + call g:assert.equals(getline('.'), '(foo) ', 'failed at #4') +endfunction +"}}} +function! s:suite.blockwise_n_option_command() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['"', '"'], 'command': ['normal! `[d`]'], 'input':['1']}, + \ ] + + """"" command + " #1 + call operator#sandwich#set('add', 'block', 'command', ['normal! `[d`]']) + call setline('.', '"foo"') + execute "normal 0ffsa\iw(" + call g:assert.equals(getline('.'), '""', 'failed at #1') + + " #2 + call operator#sandwich#set('add', 'block', 'command', []) + call setline('.', '"foo"') + execute "normal 0ffsa\iw1" + call g:assert.equals(getline('.'), '""', 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_n_option_linewise() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'linewise': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'linewise': 1, 'input':['1']}, + \ ] + + """"" linewise + """ on + " #1 + call operator#sandwich#set('add', 'block', 'linewise', 1) + call setline('.', 'foo') + execute "normal 0sa\iw(" + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + execute "normal 0sa\iw0" + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + + %delete + + " #3 + set autoindent + call setline('.', ' foo') + execute "normal ^sa\iw(" + call g:assert.equals(getline(1), ' (', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ' )', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + set autoindent& + + %delete + + """ off + call operator#sandwich#set('add', 'block', 'linewise', 0) + + " #4 + call setline('.', 'foo') + execute "normal 0sa\iw(" + call g:assert.equals(getline(1), '(foo)', 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo') + execute "normal 0sa\iw1" + call g:assert.equals(getline(1), '(', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ')', 'failed at #5') +endfunction +"}}} +function! s:suite.blockwise_n_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + + """ -1 + call operator#sandwich#set('add', 'block', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ']', 'failed at #1') + call g:assert.equals(getline(5), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), ' foo', 'failed at #2') + call g:assert.equals(getline(4), ' ]', 'failed at #2') + call g:assert.equals(getline(5), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), ' foo', 'failed at #3') + call g:assert.equals(getline(4), ' ]', 'failed at #3') + call g:assert.equals(getline(5), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), ' foo', 'failed at #4') + " call g:assert.equals(getline(4), ' ]', 'failed at #4') + call g:assert.equals(getline(5), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call setline('.', ' foo') + " execute "normal ^sa\iwa" + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), ' foo', 'failed at #5') + " call g:assert.equals(getline(4), ' ]', 'failed at #5') + " call g:assert.equals(getline(5), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 3, 17, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 5, 34, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('add', 'block', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ']', 'failed at #6') + call g:assert.equals(getline(5), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), 'foo', 'failed at #7') + call g:assert.equals(getline(4), ']', 'failed at #7') + call g:assert.equals(getline(5), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), 'foo', 'failed at #8') + call g:assert.equals(getline(4), ']', 'failed at #8') + call g:assert.equals(getline(5), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), 'foo', 'failed at #9') + call g:assert.equals(getline(4), ']', 'failed at #9') + call g:assert.equals(getline(5), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), 'foo', 'failed at #10') + call g:assert.equals(getline(4), ']', 'failed at #10') + call g:assert.equals(getline(5), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('add', 'block', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), ' foo', 'failed at #11') + call g:assert.equals(getline(4), ' ]', 'failed at #11') + call g:assert.equals(getline(5), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), ' foo', 'failed at #12') + call g:assert.equals(getline(4), ' ]', 'failed at #12') + call g:assert.equals(getline(5), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), ' foo', 'failed at #13') + call g:assert.equals(getline(4), ' ]', 'failed at #13') + call g:assert.equals(getline(5), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), ' foo', 'failed at #14') + call g:assert.equals(getline(4), ' ]', 'failed at #14') + call g:assert.equals(getline(5), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), ' foo', 'failed at #15') + call g:assert.equals(getline(4), ' ]', 'failed at #15') + call g:assert.equals(getline(5), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('add', 'block', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), ' foo', 'failed at #16') + call g:assert.equals(getline(4), ' ]', 'failed at #16') + call g:assert.equals(getline(5), ' }', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), ' foo', 'failed at #17') + call g:assert.equals(getline(4), ' ]', 'failed at #17') + call g:assert.equals(getline(5), ' }', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), ' foo', 'failed at #18') + call g:assert.equals(getline(4), ' ]', 'failed at #18') + call g:assert.equals(getline(5), ' }', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), ' foo', 'failed at #19') + call g:assert.equals(getline(4), ' ]', 'failed at #19') + call g:assert.equals(getline(5), ' }', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), ' foo', 'failed at #20') + call g:assert.equals(getline(4), ' ]', 'failed at #20') + call g:assert.equals(getline(5), ' }', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('add', 'block', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), ' foo', 'failed at #21') + " call g:assert.equals(getline(4), ' ]', 'failed at #21') + call g:assert.equals(getline(5), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), ' foo', 'failed at #22') + " call g:assert.equals(getline(4), ' ]', 'failed at #22') + call g:assert.equals(getline(5), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), ' foo', 'failed at #23') + " call g:assert.equals(getline(4), ' ]', 'failed at #23') + call g:assert.equals(getline(5), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), ' foo', 'failed at #24') + " call g:assert.equals(getline(4), ' ]', 'failed at #24') + call g:assert.equals(getline(5), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), ' foo', 'failed at #25') + " call g:assert.equals(getline(4), ' ]', 'failed at #25') + call g:assert.equals(getline(5), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + execute "normal ^sa\iw0" + call g:assert.equals(getline(1), ' {', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), 'foo', 'failed at #26') + call g:assert.equals(getline(4), ']', 'failed at #26') + call g:assert.equals(getline(5), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') +endfunction +"}}} +function! s:suite.blockwise_n_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n", "\n}"], 'input': ['a']}, + \ {'buns': ["{\n", "\n}"], 'indentkeys': '0{,0},0),:,0#,!^F,e', 'input': ['1']}, + \ ] + + """ cinkeys + call operator#sandwich#set('add', 'block', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', 3) + + " #2 + setlocal cinkeys=0{,0},0),:,0#,!^F,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys+', 'O,o') + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys-', 'O,o') + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + execute "normal ^sa\iw1" + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('add', 'block', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0{,0},0),:,0#,!^F,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys+', 'O,o') + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys-', 'O,o') + call setline('.', ' foo') + execute "normal ^sa\iwa" + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + execute "normal ^sa\iw1" + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +function! s:suite.blockwise_x_default_recipes() abort "{{{ + " #1 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsa(" + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + call g:assert.equals(getline(2), '(bar)', 'failed at #1') + call g:assert.equals(getline(3), '(baz)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + " #2 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsa)" + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + call g:assert.equals(getline(2), '(bar)', 'failed at #2') + call g:assert.equals(getline(3), '(baz)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #2') + + " #3 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsa[" + call g:assert.equals(getline(1), '[foo]', 'failed at #3') + call g:assert.equals(getline(2), '[bar]', 'failed at #3') + call g:assert.equals(getline(3), '[baz]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + + " #4 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsa]" + call g:assert.equals(getline(1), '[foo]', 'failed at #4') + call g:assert.equals(getline(2), '[bar]', 'failed at #4') + call g:assert.equals(getline(3), '[baz]', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #4') + + " #5 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsa{" + call g:assert.equals(getline(1), '{foo}', 'failed at #5') + call g:assert.equals(getline(2), '{bar}', 'failed at #5') + call g:assert.equals(getline(3), '{baz}', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + + " #6 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsa}" + call g:assert.equals(getline(1), '{foo}', 'failed at #6') + call g:assert.equals(getline(2), '{bar}', 'failed at #6') + call g:assert.equals(getline(3), '{baz}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #6') + + " #7 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsa<" + call g:assert.equals(getline(1), '', 'failed at #7') + call g:assert.equals(getline(2), '', 'failed at #7') + call g:assert.equals(getline(3), '', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + + " #8 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsa>" + call g:assert.equals(getline(1), '', 'failed at #8') + call g:assert.equals(getline(2), '', 'failed at #8') + call g:assert.equals(getline(3), '', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') +endfunction +"}}} +function! s:suite.blockwise_x_not_registered() abort "{{{ + " #1 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsaa" + call g:assert.equals(getline(1), 'afooa', 'failed at #1') + call g:assert.equals(getline(2), 'abara', 'failed at #1') + call g:assert.equals(getline(3), 'abaza', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsa*" + call g:assert.equals(getline(1), '*foo*', 'failed at #2') + call g:assert.equals(getline(2), '*bar*', 'failed at #2') + call g:assert.equals(getline(3), '*baz*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_x_positioning() abort "{{{ + " #1 + call append(0, ['foobarbaz', 'foobarbaz', 'foobarbaz']) + execute "normal gg\2j2lsa(" + call g:assert.equals(getline(1), '(foo)barbaz', 'failed at #1') + call g:assert.equals(getline(2), '(foo)barbaz', 'failed at #1') + call g:assert.equals(getline(3), '(foo)barbaz', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foobarbaz', 'foobarbaz', 'foobarbaz']) + execute "normal gg3l\2j2lsa(" + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #2') + call g:assert.equals(getline(2), 'foo(bar)baz', 'failed at #2') + call g:assert.equals(getline(3), 'foo(bar)baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 9, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['foobarbaz', 'foobarbaz', 'foobarbaz']) + execute "normal gg6l\2j2lsa(" + call g:assert.equals(getline(1), 'foobar(baz)', 'failed at #3') + call g:assert.equals(getline(2), 'foobar(baz)', 'failed at #3') + call g:assert.equals(getline(3), 'foobar(baz)', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 7, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 12, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['foo', '', 'baz']) + execute "normal gg\2j2lsa(" + call g:assert.equals(getline(1), '(foo)', 'failed at #4') + call g:assert.equals(getline(2), '', 'failed at #4') + call g:assert.equals(getline(3), '(baz)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['foo', 'ba', 'baz']) + execute "normal gg\2j2lsa(" + call g:assert.equals(getline(1), '(foo)', 'failed at #5') + call g:assert.equals(getline(2), '(ba)', 'failed at #5') + call g:assert.equals(getline(3), '(baz)', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['fo', 'bar', 'baz']) + execute "normal gg\2j2lsa(" + call g:assert.equals(getline(1), '(fo)', 'failed at #6') + call g:assert.equals(getline(2), '(bar)', 'failed at #6') + call g:assert.equals(getline(3), '(baz)', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['foo', 'bar', 'ba']) + execute "normal gg\2j2lsa(" + call g:assert.equals(getline(1), '(foo)', 'failed at #7') + call g:assert.equals(getline(2), '(bar)', 'failed at #7') + call g:assert.equals(getline(3), '(ba)', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['foo', 'bar*', 'baz']) + execute "normal gg\2j2lsa(" + call g:assert.equals(getline(1), '(foo)', 'failed at #8') + call g:assert.equals(getline(2), '(bar)*', 'failed at #8') + call g:assert.equals(getline(3), '(baz)', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') + + %delete + + """ terminal-extended block-wise visual mode + " #9 + call append(0, ['fooo', 'baaar', 'baz']) + execute "normal gg\2j$sa(" + call g:assert.equals(getline(1), '(fooo)', 'failed at #9') + call g:assert.equals(getline(2), '(baaar)', 'failed at #9') + call g:assert.equals(getline(3), '(baz)', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['foooo', 'bar', 'baaz']) + execute "normal gg\2j$sa(" + call g:assert.equals(getline(1), '(foooo)', 'failed at #10') + call g:assert.equals(getline(2), '(bar)', 'failed at #10') + call g:assert.equals(getline(3), '(baaz)', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 7, 0], 'failed at #10') + + %delete + + " #11 + call append(0, ['fooo', '', 'baz']) + execute "normal gg\2j$sa(" + call g:assert.equals(getline(1), '(fooo)', 'failed at #11') + call g:assert.equals(getline(2), '', 'failed at #11') + call g:assert.equals(getline(3), '(baz)', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #11') +endfunction +"}}} +function! s:suite.blockwise_x_a_character() abort "{{{ + " #1 + call setline('.', 'a') + execute "normal 0\sa(" + call g:assert.equals(getline('.'), '(a)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['a', 'a', 'a']) + execute "normal gg\2jsa(" + call g:assert.equals(getline(1), '(a)', 'failed at #2') + call g:assert.equals(getline(2), '(a)', 'failed at #2') + call g:assert.equals(getline(3), '(a)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_x_breaking() abort "{{{ + " #1 + call append(0, ['', 'foo']) + execute "normal gg\j$sa(" + call g:assert.equals(getline(1), '', 'failed at #1') + call g:assert.equals(getline(2), '(foo)', 'failed at #1') + " call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 2, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', '']) + execute "normal gg\j$sa(" + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + call g:assert.equals(getline(2), '', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #3 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsaa" + call g:assert.equals(getline(1), 'aa', 'failed at #3') + call g:assert.equals(getline(2), 'aaafooaaa', 'failed at #3') + call g:assert.equals(getline(3), 'aa', 'failed at #3') + call g:assert.equals(getline(4), 'aa', 'failed at #3') + call g:assert.equals(getline(5), 'aaabaraaa', 'failed at #3') + call g:assert.equals(getline(6), 'aa', 'failed at #3') + call g:assert.equals(getline(7), 'aa', 'failed at #3') + call g:assert.equals(getline(8), 'aaabazaaa', 'failed at #3') + call g:assert.equals(getline(9), 'aa', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 9, 3, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsab" + call g:assert.equals(getline(1), 'bb', 'failed at #4') + call g:assert.equals(getline(2), 'bbb', 'failed at #4') + call g:assert.equals(getline(3), 'bbfoobb', 'failed at #4') + call g:assert.equals(getline(4), 'bbb', 'failed at #4') + call g:assert.equals(getline(5), 'bb', 'failed at #4') + call g:assert.equals(getline(6), 'bb', 'failed at #4') + call g:assert.equals(getline(7), 'bbb', 'failed at #4') + call g:assert.equals(getline(8), 'bbbarbb', 'failed at #4') + call g:assert.equals(getline(9), 'bbb', 'failed at #4') + call g:assert.equals(getline(10), 'bb', 'failed at #4') + call g:assert.equals(getline(11), 'bb', 'failed at #4') + call g:assert.equals(getline(12), 'bbb', 'failed at #4') + call g:assert.equals(getline(13), 'bbbazbb', 'failed at #4') + call g:assert.equals(getline(14), 'bbb', 'failed at #4') + call g:assert.equals(getline(15), 'bb', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 15, 3, 0], 'failed at #4') + + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.blockwise_x_count() abort "{{{ + " #1 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2sa([" + call g:assert.equals(getline(1), '[(foo)]', 'failed at #1') + call g:assert.equals(getline(2), '[(bar)]', 'failed at #1') + call g:assert.equals(getline(3), '[(baz)]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 8, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l3sa([{" + call g:assert.equals(getline(1), '{[(foo)]}', 'failed at #2') + call g:assert.equals(getline(2), '{[(bar)]}', 'failed at #2') + call g:assert.equals(getline(3), '{[(baz)]}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 10, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_x_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call append(0, ['α', 'β', 'γ']) + execute "normal gg\2jsa(" + call g:assert.equals(getline(1), '(α)', 'failed at #1') + call g:assert.equals(getline(2), '(β)', 'failed at #1') + call g:assert.equals(getline(3), '(γ)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, strlen('(γ)')+1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['aα', 'bβ', 'cγ']) + execute "normal gg\l2jsa(" + call g:assert.equals(getline(1), '(aα)', 'failed at #2') + call g:assert.equals(getline(2), '(bβ)', 'failed at #2') + call g:assert.equals(getline(3), '(cγ)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, strlen('(cγ)')+1, 0], 'failed at #2') + + %delete + set ambiwidth=double + + " #3 + call append(0, ['a', 'α', 'a']) + execute "normal gg\2jsa(" + call g:assert.equals(getline(1), '(a)', 'failed at #3') + call g:assert.equals(getline(2), '(α)', 'failed at #3') + call g:assert.equals(getline(3), '(a)', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['a', 'a', 'α']) + execute "normal gg\2jsa(" + call g:assert.equals(getline(1), '(a)', 'failed at #4') + call g:assert.equals(getline(2), '(a)', 'failed at #4') + call g:assert.equals(getline(3), '(α)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, strlen('(α)')+1, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['abc', 'αβγ', 'abc']) + execute "normal ggl\2jsa(" + call g:assert.equals(getline(1), 'a(b)c', 'failed at #5') + call g:assert.equals(getline(2), '(α)βγ', 'failed at #5') + call g:assert.equals(getline(3), 'a(b)c', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 2, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #5') + + %delete + set ambiwidth=single + + " #6 + call append(0, ['abc', 'αβγ', 'abc']) + execute "normal ggl\2jsa(" + call g:assert.equals(getline(1), 'a(b)c', 'failed at #6') + call g:assert.equals(getline(2), 'α(β)γ', 'failed at #6') + call g:assert.equals(getline(3), 'a(b)c', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 2, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #6') + + set ambiwidth& + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #7 + call append(0, ['a', 'b', 'c']) + execute "normal gg\2jsaa" + call g:assert.equals(getline(1), 'αaα', 'failed at #7') + call g:assert.equals(getline(2), 'αbα', 'failed at #7') + call g:assert.equals(getline(3), 'αcα', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcα')+1, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['α', 'β', 'γ']) + execute "normal gg\2jsaa" + call g:assert.equals(getline(1), 'ααα', 'failed at #8') + call g:assert.equals(getline(2), 'αβα', 'failed at #8') + call g:assert.equals(getline(3), 'αγα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, strlen('αγα')+1, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['aα', 'bβ', 'cγ']) + execute "normal gg\l2jsaa" + call g:assert.equals(getline(1), 'αaαα', 'failed at #9') + call g:assert.equals(getline(2), 'αbβα', 'failed at #9') + call g:assert.equals(getline(3), 'αcγα', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcγα')+1, 0], 'failed at #9') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #10 + call append(0, ['a', 'b', 'c']) + execute "normal gg\2jsaa" + call g:assert.equals(getline(1), 'aαaaα', 'failed at #10') + call g:assert.equals(getline(2), 'aαbaα', 'failed at #10') + call g:assert.equals(getline(3), 'aαcaα', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcaα')+1, 0], 'failed at #10') + + %delete + + " #11 + call append(0, ['α', 'β', 'γ']) + execute "normal gg\2jsaa" + call g:assert.equals(getline(1), 'aααaα', 'failed at #11') + call g:assert.equals(getline(2), 'aαβaα', 'failed at #11') + call g:assert.equals(getline(3), 'aαγaα', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαγaα')+1, 0], 'failed at #11') + + %delete + + " #12 + call append(0, ['aα', 'bβ', 'cγ']) + execute "normal gg\l2jsaa" + call g:assert.equals(getline(1), 'aαaαaα', 'failed at #12') + call g:assert.equals(getline(2), 'aαbβaα', 'failed at #12') + call g:assert.equals(getline(3), 'aαcγaα', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcγaα')+1, 0], 'failed at #12') + + %delete + unlet g:operator#sandwich#recipes + + " #13 + call append(0, ['“', '“', '“']) + execute "normal gg\2jsa(" + call g:assert.equals(getline(1), '(“)', 'failed at #13') + call g:assert.equals(getline(2), '(“)', 'failed at #13') + call g:assert.equals(getline(3), '(“)', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, strlen('(“)')+1, 0], 'failed at #13') + + %delete + + " #14 + call append(0, ['a“', 'b“', 'c“']) + execute "normal gg\l2jsa(" + call g:assert.equals(getline(1), '(a“)', 'failed at #14') + call g:assert.equals(getline(2), '(b“)', 'failed at #14') + call g:assert.equals(getline(3), '(c“)', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, strlen('(c“)')+1, 0], 'failed at #14') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #15 + call append(0, ['a', 'b', 'c']) + execute "normal gg\2jsaa" + call g:assert.equals(getline(1), '“a“', 'failed at #15') + call g:assert.equals(getline(2), '“b“', 'failed at #15') + call g:assert.equals(getline(3), '“c“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c“')+1, 0], 'failed at #15') + + %delete + + " #16 + call append(0, ['“', '“', '“']) + execute "normal gg\2jsaa" + call g:assert.equals(getline(1), '“““', 'failed at #16') + call g:assert.equals(getline(2), '“““', 'failed at #16') + call g:assert.equals(getline(3), '“““', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, strlen('“““')+1, 0], 'failed at #16') + + %delete + + " #17 + call append(0, ['a“', 'b“', 'c“']) + execute "normal gg\l2jsaa" + call g:assert.equals(getline(1), '“a““', 'failed at #17') + call g:assert.equals(getline(2), '“b““', 'failed at #17') + call g:assert.equals(getline(3), '“c““', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c““')+1, 0], 'failed at #17') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #18 + call append(0, ['a', 'b', 'c']) + execute "normal gg\2jsaa" + call g:assert.equals(getline(1), 'a“aa“', 'failed at #18') + call g:assert.equals(getline(2), 'a“ba“', 'failed at #18') + call g:assert.equals(getline(3), 'a“ca“', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“ca“')+1, 0], 'failed at #18') + + %delete + + " #19 + call append(0, ['“', '“', '“']) + execute "normal gg\2jsaa" + call g:assert.equals(getline(1), 'a““a“', 'failed at #19') + call g:assert.equals(getline(2), 'a““a“', 'failed at #19') + call g:assert.equals(getline(3), 'a““a“', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 3, strlen('a““a“')+1, 0], 'failed at #19') + + %delete + + " #20 + call append(0, ['a“', 'b“', 'c“']) + execute "normal gg\l2jsaa" + call g:assert.equals(getline(1), 'a“a“a“', 'failed at #20') + call g:assert.equals(getline(2), 'a“b“a“', 'failed at #20') + call g:assert.equals(getline(3), 'a“c“a“', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“c“a“')+1, 0], 'failed at #20') +endfunction +"}}} +function! s:suite.blockwise_x_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2sa()" + call g:assert.equals(getline(1), '((foo))', 'failed at #1') + call g:assert.equals(getline(2), '((bar))', 'failed at #1') + call g:assert.equals(getline(3), '((baz))', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + + " #2 + execute "normal \2j2lsa(" + call g:assert.equals(getline(1), '(((foo)))', 'failed at #2') + call g:assert.equals(getline(2), '(((bar)))', 'failed at #2') + call g:assert.equals(getline(3), '(((baz)))', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + + %delete + + """ inner_head + call operator#sandwich#set('add', 'block', 'cursor', 'inner_head') + " #3 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2sa()" + call g:assert.equals(getline(1), '((foo))', 'failed at #3') + call g:assert.equals(getline(2), '((bar))', 'failed at #3') + call g:assert.equals(getline(3), '((baz))', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #3') + + " #4 + execute "normal \2j2lsa(" + call g:assert.equals(getline(1), '(((foo)))', 'failed at #4') + call g:assert.equals(getline(2), '(((bar)))', 'failed at #4') + call g:assert.equals(getline(3), '(((baz)))', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #4') + + %delete + + """ keep + " #5 + call operator#sandwich#set('add', 'block', 'cursor', 'keep') + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2sa()" + call g:assert.equals(getline(1), '((foo))', 'failed at #5') + call g:assert.equals(getline(2), '((bar))', 'failed at #5') + call g:assert.equals(getline(3), '((baz))', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #5') + + " #6 + execute "normal \2k2hsa(" + call g:assert.equals(getline(1), '(((foo)))', 'failed at #6') + call g:assert.equals(getline(2), '(((bar)))', 'failed at #6') + call g:assert.equals(getline(3), '(((baz)))', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #6') + + %delete + + """ inner_tail + " #7 + call operator#sandwich#set('add', 'block', 'cursor', 'inner_tail') + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2sa()" + call g:assert.equals(getline(1), '((foo))', 'failed at #7') + call g:assert.equals(getline(2), '((bar))', 'failed at #7') + call g:assert.equals(getline(3), '((baz))', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #7') + + " #8 + execute "normal \2k2hsa(" + call g:assert.equals(getline(1), '(((foo)))', 'failed at #8') + call g:assert.equals(getline(2), '(((bar)))', 'failed at #8') + call g:assert.equals(getline(3), '(((baz)))', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 6, 0], 'failed at #8') + + %delete + + """ head + " #9 + call operator#sandwich#set('add', 'block', 'cursor', 'head') + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2sa()" + call g:assert.equals(getline(1), '((foo))', 'failed at #9') + call g:assert.equals(getline(2), '((bar))', 'failed at #9') + call g:assert.equals(getline(3), '((baz))', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + + " #10 + execute "normal 2l\2j2lsa(" + call g:assert.equals(getline(1), '(((foo)))', 'failed at #10') + call g:assert.equals(getline(2), '(((bar)))', 'failed at #10') + call g:assert.equals(getline(3), '(((baz)))', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #10') + + %delete + + """ tail + " #11 + call operator#sandwich#set('add', 'block', 'cursor', 'tail') + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2sa()" + call g:assert.equals(getline(1), '((foo))', 'failed at #11') + call g:assert.equals(getline(2), '((bar))', 'failed at #11') + call g:assert.equals(getline(3), '((baz))', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 7, 0], 'failed at #11') + + " #12 + execute "normal 2h\2k2hsa(" + call g:assert.equals(getline(1), '(((foo)))', 'failed at #12') + call g:assert.equals(getline(2), '(((bar)))', 'failed at #12') + call g:assert.equals(getline(3), '(((baz)))', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 7, 0], 'failed at #12') + + %delete + + """"" recipe option + " #13 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('add', 'block', 'cursor', 'inner_tail') + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsa1" + call g:assert.equals(getline(1), '(foo)', 'failed at #13') + call g:assert.equals(getline(2), '(bar)', 'failed at #13') + call g:assert.equals(getline(3), '(baz)', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #13') +endfunction +"}}} +function! s:suite.blockwise_x_option_query_once() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call setline('.', 'foo') + execute "normal 0\iw3sa([{" + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #1') + + " #2 + call setline('.', 'foo') + execute "normal 0\iw3sa1" + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + """ on + " #3 + call operator#sandwich#set('add', 'block', 'query_once', 1) + call setline('.', 'foo') + execute "normal 0\iw3sa(" + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #3') + + " #4 + call setline('.', 'foo') + execute "normal 0\iw3sa0[{" + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #4') +endfunction +"}}} +function! s:suite.blockwise_x_option_expr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input':['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'foo') + execute "normal 0\iwsaa" + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #1') + + " #2 + call setline('.', 'foo') + execute "normal 0\iwsa1" + call g:assert.equals(getline('.'), '2foo3', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('add', 'block', 'expr', 1) + call setline('.', 'foo') + execute "normal 0\iwsaa" + call g:assert.equals(getline('.'), '2foo3', 'failed at #3') + + %delete + + " #4 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsab" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + %delete + + " #5 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2lsac" + call g:assert.equals(getline(1), 'foo', 'failed at #5') + call g:assert.equals(getline(2), 'bar', 'failed at #5') + call g:assert.equals(getline(3), 'baz', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + %delete + + " #6 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2saab" + call g:assert.equals(getline(1), '2foo3', 'failed at #6') + call g:assert.equals(getline(2), '2bar3', 'failed at #6') + call g:assert.equals(getline(3), '2baz3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + %delete + + " #7 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2saac" + call g:assert.equals(getline(1), '2foo3', 'failed at #7') + call g:assert.equals(getline(2), '2bar3', 'failed at #7') + call g:assert.equals(getline(3), '2baz3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + %delete + + " #8 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2saba" + call g:assert.equals(getline(1), 'foo', 'failed at #8') + call g:assert.equals(getline(2), 'bar', 'failed at #8') + call g:assert.equals(getline(3), 'baz', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + %delete + + " #9 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j2l2saca" + call g:assert.equals(getline(1), 'foo', 'failed at #9') + call g:assert.equals(getline(2), 'bar', 'failed at #9') + call g:assert.equals(getline(3), 'baz', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + %delete + + " #10 + call setline('.', 'foo') + execute "normal 0\iwsa0" + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.blockwise_x_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :execute "normal 0\iwsaa" + call g:assert.equals(getline('.'), 'bar', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + " #2 + call setline('.', 'bar') + execute "normal 0\iwsa1" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('add', 'block', 'listexpr', 1) + call setline('.', 'bar') + execute "normal 0\iwsaa" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + + " #4 + call setline('.', 'bar') + execute "normal 0\iwsab" + call g:assert.equals(getline('.'), 'bar', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', 'bar') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :execute "normal 0\iwsa0" + call g:assert.equals(getline('.'), 'bar', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', 'bar') + execute "normal 0\iwsa1" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.blockwise_x_option_noremap() abort "{{{ + """"" noremap + let g:operator#sandwich#recipes = [ + \ {'buns': ['[', ']'], 'input':['[']}, + \ {'buns': ['[', ']'], 'noremap': 0, 'input':['0']}, + \ {'buns': ['[', ']'], 'noremap': 1, 'input':['1']}, + \ ] + inoremap [ { + inoremap ] } + + """ on + " #1 + call setline('.', 'foo') + execute "normal 0\iwsa[" + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + + " #2 + call setline('.', 'foo') + execute "normal 0\iwsa0" + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + + """ off + " #3 + call operator#sandwich#set('add', 'block', 'noremap', 0) + call setline('.', 'foo') + execute "normal 0\iwsa[" + call g:assert.equals(getline('.'), '{foo}', 'failed at #3') + + " #4 + call setline('.', 'foo') + execute "normal 0\iwsa1" + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') +endfunction +"}}} +function! s:suite.blockwise_x_option_skip_space() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'skip_space': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'skip_space': 1, 'input':['1']}, + \ ] + + """"" skip_space + """ on + " #1 + call setline('.', 'foo ') + execute "normal 0\2iwsa(" + call g:assert.equals(getline('.'), '(foo) ', 'failed at #1') + + " #2 + call setline('.', 'foo ') + execute "normal 0\2iwsa0" + call g:assert.equals(getline('.'), '(foo )', 'failed at #2') + + """ off + " #3 + call operator#sandwich#set('add', 'block', 'skip_space', 0) + call setline('.', 'foo ') + execute "normal 0\2iwsa(" + call g:assert.equals(getline('.'), '(foo )', 'failed at #3') + + " #4 + call setline('.', 'foo ') + execute "normal 0\2iwsa1" + call g:assert.equals(getline('.'), '(foo) ', 'failed at #4') +endfunction +"}}} +function! s:suite.blockwise_x_option_command() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['"', '"'], 'command': ['normal! `[d`]'], 'input':['1']}, + \ ] + + """"" command + " #1 + call operator#sandwich#set('add', 'block', 'command', ['normal! `[d`]']) + call setline('.', '"foo"') + execute "normal 0ff\iwsa(" + call g:assert.equals(getline('.'), '""', 'failed at #1') + + " #2 + call operator#sandwich#set('add', 'block', 'command', []) + call setline('.', '"foo"') + execute "normal 0ff\iwsa1" + call g:assert.equals(getline('.'), '""', 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_x_option_linewise() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'linewise': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'linewise': 1, 'input':['1']}, + \ ] + + """"" linewise + """ on + " #1 + call operator#sandwich#set('add', 'block', 'linewise', 1) + call setline('.', 'foo') + execute "normal 0\iwsa(" + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo') + execute "normal 0\iwsa0" + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + + %delete + + " #3 + set autoindent + call setline('.', ' foo') + execute "normal ^\iwsa(" + call g:assert.equals(getline(1), ' (', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ' )', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + set autoindent& + + %delete + + """ off + call operator#sandwich#set('add', 'block', 'linewise', 0) + + " #4 + call setline('.', 'foo') + execute "normal 0\iwsa(" + call g:assert.equals(getline(1), '(foo)', 'failed at #4') + + %delete + + " #5 + call setline('.', 'foo') + execute "normal 0\iwsa1" + call g:assert.equals(getline(1), '(', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ')', 'failed at #5') +endfunction +"}}} +function! s:suite.blockwise_x_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + + """ -1 + call operator#sandwich#set('add', 'block', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ']', 'failed at #1') + call g:assert.equals(getline(5), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), ' foo', 'failed at #2') + call g:assert.equals(getline(4), ' ]', 'failed at #2') + call g:assert.equals(getline(5), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), ' foo', 'failed at #3') + call g:assert.equals(getline(4), ' ]', 'failed at #3') + call g:assert.equals(getline(5), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), ' foo', 'failed at #4') + " call g:assert.equals(getline(4), ' ]', 'failed at #4') + call g:assert.equals(getline(5), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call setline('.', ' foo') + " execute "normal ^\iwsaa" + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), ' foo', 'failed at #5') + " call g:assert.equals(getline(4), ' ]', 'failed at #5') + " call g:assert.equals(getline(5), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 3, 17, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 5, 34, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('add', 'block', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ']', 'failed at #6') + call g:assert.equals(getline(5), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), 'foo', 'failed at #7') + call g:assert.equals(getline(4), ']', 'failed at #7') + call g:assert.equals(getline(5), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), 'foo', 'failed at #8') + call g:assert.equals(getline(4), ']', 'failed at #8') + call g:assert.equals(getline(5), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), 'foo', 'failed at #9') + call g:assert.equals(getline(4), ']', 'failed at #9') + call g:assert.equals(getline(5), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), 'foo', 'failed at #10') + call g:assert.equals(getline(4), ']', 'failed at #10') + call g:assert.equals(getline(5), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('add', 'block', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), ' foo', 'failed at #11') + call g:assert.equals(getline(4), ' ]', 'failed at #11') + call g:assert.equals(getline(5), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), ' foo', 'failed at #12') + call g:assert.equals(getline(4), ' ]', 'failed at #12') + call g:assert.equals(getline(5), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), ' foo', 'failed at #13') + call g:assert.equals(getline(4), ' ]', 'failed at #13') + call g:assert.equals(getline(5), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), ' foo', 'failed at #14') + call g:assert.equals(getline(4), ' ]', 'failed at #14') + call g:assert.equals(getline(5), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), ' foo', 'failed at #15') + call g:assert.equals(getline(4), ' ]', 'failed at #15') + call g:assert.equals(getline(5), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('add', 'block', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), ' foo', 'failed at #16') + call g:assert.equals(getline(4), ' ]', 'failed at #16') + call g:assert.equals(getline(5), ' }', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), ' foo', 'failed at #17') + call g:assert.equals(getline(4), ' ]', 'failed at #17') + call g:assert.equals(getline(5), ' }', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), ' foo', 'failed at #18') + call g:assert.equals(getline(4), ' ]', 'failed at #18') + call g:assert.equals(getline(5), ' }', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), ' foo', 'failed at #19') + call g:assert.equals(getline(4), ' ]', 'failed at #19') + call g:assert.equals(getline(5), ' }', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), ' foo', 'failed at #20') + call g:assert.equals(getline(4), ' ]', 'failed at #20') + call g:assert.equals(getline(5), ' }', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('add', 'block', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), ' foo', 'failed at #21') + " call g:assert.equals(getline(4), ' ]', 'failed at #21') + call g:assert.equals(getline(5), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), ' foo', 'failed at #22') + " call g:assert.equals(getline(4), ' ]', 'failed at #22') + call g:assert.equals(getline(5), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), ' foo', 'failed at #23') + " call g:assert.equals(getline(4), ' ]', 'failed at #23') + call g:assert.equals(getline(5), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), ' foo', 'failed at #24') + " call g:assert.equals(getline(4), ' ]', 'failed at #24') + call g:assert.equals(getline(5), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), ' foo', 'failed at #25') + " call g:assert.equals(getline(4), ' ]', 'failed at #25') + call g:assert.equals(getline(5), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call setline('.', ' foo') + execute "normal ^\iwsa0" + call g:assert.equals(getline(1), ' {', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), 'foo', 'failed at #26') + call g:assert.equals(getline(4), ']', 'failed at #26') + call g:assert.equals(getline(5), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') +endfunction +"}}} +function! s:suite.blockwise_x_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n", "\n}"], 'input': ['a']}, + \ {'buns': ["{\n", "\n}"], 'indentkeys': '0{,0},0),:,0#,!^F,e', 'input': ['1']}, + \ ] + + """ cinkeys + call operator#sandwich#set('add', 'block', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', 3) + + " #2 + setlocal cinkeys=0{,0},0),:,0#,!^F,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys+', 'O,o') + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys-', 'O,o') + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + execute "normal ^\iwsa1" + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('add', 'block', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0{,0},0),:,0#,!^F,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys+', 'O,o') + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('add', 'block', 'indentkeys-', 'O,o') + call setline('.', ' foo') + execute "normal ^\iwsaa" + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('add', 'block', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' foo') + execute "normal ^\iwsa1" + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +" Function interface +function! s:suite.function_interface() abort "{{{ + nmap ssa :call operator#sandwich#prerequisite('add', 'n', {'cursor': 'inner_tail'}, [{'buns': ['(', ')']}])g@ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['[', ']']}, + \ ] + + " #1 + call setline('.', 'foo') + normal 0saiw( + call g:assert.equals(getline('.'), '(foo(', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + + " #2 + call setline('.', 'foo') + normal 0saiw[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + + " #3 + call setline('.', 'foo') + normal 0ssaiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0ssaiw[ + call g:assert.equals(getline('.'), '[foo[', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #4') +endfunction +"}}} + +" Undo +function! s:suite.undo() abort "{{{ + " #1 + call setline('.', 'foo') + " set undo point (see :help :undojoin) + let &undolevels = &undolevels + normal 0saiw( + normal! u + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', 'foo') + let &undolevels = &undolevels + normal 02saiw(( + normal! u + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + + " #3 + call setline('.', 'foo') + let &undolevels = &undolevels + normal 03saiw((( + normal! u + call g:assert.equals(getline('.'), 'foo', 'failed at #3') +endfunction +"}}} + +" auto-indent +function! s:suite.autoindent() abort "{{{ + " #1 + filetype indent on + set expandtab softtabstop=2 shiftwidth=2 + set indentexpr=TestIndent() + set indentkeys=> + call append(0, ['foo', 'bar']) + call cursor(2, 1) + execute "normal saiwtbaz\" + call g:assert.equals(getline('.'), ' bar', 'failed at #1') +endfunction "}}} +function! s:suite.insertspace() abort "{{{ + " #1 + call setline(1, 'foo') + execute "normal 0saiw\" + call g:assert.equals(getline('.'), ' foo ') + + " #2 + call setline(1, 'foo') + execute "normal 0lsal\" + call g:assert.equals(getline('.'), 'f o o') +endfunction "}}} + +" auto-formatting +function! s:suite.autoformat() abort "{{{ + " #1 + setlocal formatoptions+=t textwidth=10 + call setline(1, 'ab cd ef gh ij kl mn op') + call cursor(1, 17) + execute "normal saiw(" + call g:assert.equals(getline('.'), 'ab cd ef gh ij (kl) mn op', 'failed at #1') +endfunction "}}} + +" inappropriate input +function! s:suite.inappropriate_input() abort "{{{ + " #1 + call setline(1, 'abc') + call cursor(1, 1) + execute "normal saiw\l" + call g:assert.equals(getline('.'), 'abc', 'failed at #1') +endfunction "}}} + +" input_fallback +function! s:suite.input_fallback() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [] + + let g:sandwich#input_fallback = 1 + call setline('.', 'foo') + normal 0saiwa + call g:assert.equals(getline('.'), 'afooa', 'failed at #1') + + let g:sandwich#input_fallback = 0 + call setline('.', 'foo') + normal 0saiwa + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + + unlet! g:sandwich#input_fallback + call setline('.', 'foo') + normal 0saiwa + call g:assert.equals(getline('.'), 'afooa', 'failed at #3') +endfunction "}}} + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/operator-delete.vim b/config/neovim/store/lazy-plugins/vim-sandwich/test/operator-delete.vim new file mode 100644 index 00000000..bb32d1d0 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/operator-delete.vim @@ -0,0 +1,6690 @@ +scriptencoding utf-8 + +let s:suite = themis#suite('operator-sandwich: delete:') + +function! s:suite.before() abort "{{{ + nmap sd (operator-sandwich-delete) + xmap sd (operator-sandwich-delete) +endfunction +"}}} +function! s:suite.before_each() abort "{{{ + %delete + set filetype= + set whichwrap& + set autoindent& + set virtualedit& + silent! mapc! + silent! ounmap ii + silent! xunmap ii + silent! ounmap ssd + silent! xunmap i{ + silent! xunmap a{ + unlet! g:sandwich#recipes + unlet! g:operator#sandwich#recipes + unlet! g:sandwich#input_fallback + call operator#sandwich#set_default() + call visualmode(1) +endfunction +"}}} +function! s:suite.after() abort "{{{ + call s:suite.before_each() + nmap sd (operator-sandwich-delete)(operator-sandwich-release-count)(textobj-sandwich-query-a) + xmap sd (operator-sandwich-delete) +endfunction +"}}} + +" Filter +function! s:suite.filter_filetype() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'filetype': ['vim']}, + \ {'buns': ['{', '}'], 'filetype': ['all']}, + \ {'buns': ['<', '>'], 'filetype': ['']} + \ ] + + " #1 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0sda[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal 0sda{ + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + + " #4 + call setline('.', '') + normal 0sda< + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + set filetype=vim + + " #5 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + + " #6 + call setline('.', '[foo]') + normal 0sda[ + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + " #7 + call setline('.', '{foo}') + normal 0sda{ + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + + " #8 + call setline('.', '') + normal 0sda< + call g:assert.equals(getline('.'), '', 'failed at #8') +endfunction +"}}} +function! s:suite.filter_kind() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ ] + + " #1 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'kind': ['add']}, + \ ] + + " #2 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'kind': ['delete']}, + \ ] + + " #3 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'kind': ['replace']}, + \ ] + + " #4 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'kind': ['operator']}, + \ ] + + " #5 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'kind': ['all']}, + \ ] + + " #6 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #6') +endfunction +"}}} +function! s:suite.filter_motionwise() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ ] + call operator#sandwich#set('add', 'line', 'linewise', 0) + + " #1 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + + " #3 + call setline('.', '(foo)') + execute "normal 0sd\a(" + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'motionwise': ['all']}, + \ ] + + " #4 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + " #5 + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + + " #6 + call setline('.', '(foo)') + execute "normal 0sd\a(" + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'motionwise': ['char']}, + \ ] + + " #7 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + + " #8 + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #8') + + " #9 + call setline('.', '(foo)') + execute "normal 0sd\a(" + call g:assert.equals(getline('.'), '(foo)', 'failed at #9') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'motionwise': ['line']}, + \ ] + + " #10 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), '(foo)', 'failed at #10') + + " #11 + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #11') + + " #12 + call setline('.', '(foo)') + execute "normal 0sd\a(" + call g:assert.equals(getline('.'), '(foo)', 'failed at #12') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'motionwise': ['block']}, + \ ] + + " #13 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), '(foo)', 'failed at #13') + + " #14 + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #14') + + " #15 + call setline('.', '(foo)') + execute "normal 0sd\a(" + call g:assert.equals(getline('.'), 'foo', 'failed at #15') +endfunction +"}}} +function! s:suite.filter_mode() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ ] + + " #1 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal 0va(sd + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'mode': ['n']}, + \ ] + + " #3 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + + " #4 + call setline('.', '(foo)') + normal 0va(sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'mode': ['x']}, + \ ] + + " #5 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + " #6 + call setline('.', '(foo)') + normal 0va(sd + call g:assert.equals(getline('.'), 'foo', 'failed at #6') +endfunction +"}}} +function! s:suite.filter_action() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ ] + + " #1 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'action': ['all']}, + \ ] + + " #2 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'action': ['add']}, + \ ] + + " #3 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), '(foo)', 'failed at #3') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'action': ['delete']}, + \ ] + + " #4 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #4') +endfunction +"}}} +function! s:suite.filter_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'expr_filter': ['FilterValid()']}, + \ {'buns': ['{', '}'], 'expr_filter': ['FilterInvalid()']}, + \ ] + + function! FilterValid() abort + return 1 + endfunction + + function! FilterInvalid() abort + return 0 + endfunction + + " #1 + call setline('.', '(foo)') + normal 0sd5l + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0sd5l + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal 0sd5l + call g:assert.equals(getline('.'), '{foo}', 'failed at #3') +endfunction +"}}} + +" character-wise +function! s:suite.charwise_n_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0sda[ + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal 0sda{ + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + " #4 + call setline('.', '') + normal 0sda< + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_n_not_registered() abort "{{{ + " #1 + call setline('.', 'afooa') + normal 0sdiw + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '*foo*') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_n_positioning() abort "{{{ + " #1 + call setline('.', '(foo)bar') + normal 0sda( + call g:assert.equals(getline('.'), 'foobar', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', 'foo(bar)') + normal 0fbsda( + call g:assert.equals(getline('.'), 'foobar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #2') + + " #3 + call setline('.', 'foo(bar)baz') + normal 0fbsda( + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #3') + + %delete + + onoremap ii :call TextobjCoord(1, 4, 1, 10) + call operator#sandwich#set('delete', 'char', 'cursor', 'keep') + let g:operator#sandwich#recipes = [{'buns': ['((', '))']}] + + " #4 + call setline('.', 'foo((bar))baz') + normal 0sdii + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #4') + + " #5 + call setline('.', 'foo((bar))baz') + normal 02lsdii + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #5') + + " #6 + call setline('.', 'foo((bar))baz') + normal 03lsdii + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #6') + + " #7 + call setline('.', 'foo((bar))baz') + normal 04lsdii + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #7') + + " #8 + call setline('.', 'foo((bar))baz') + normal 05lsdii + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #8') + + " #9 + call setline('.', 'foo((bar))baz') + normal 07lsdii + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #9') + + " #10 + call setline('.', 'foo((bar))baz') + normal 08lsdii + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #10') + + " #11 + call setline('.', 'foo((bar))baz') + normal 09lsdii + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #11') + + " #12 + call setline('.', 'foo((bar))baz') + normal 010lsdii + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #12') + + " #13 + call setline('.', 'foo((bar))baz') + normal 012lsdii + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #13') + + ounmap ii + call operator#sandwich#set('delete', 'char', 'cursor', 'inner_head') + unlet g:operator#sandwich#recipes + + " #14 + set whichwrap=h,l + call append(0, ['(foo', 'bar', 'baz)']) + normal ggsd13l + call g:assert.equals(getline(1), 'foo', 'failed at #14') + call g:assert.equals(getline(2), 'bar', 'failed at #14') + call g:assert.equals(getline(3), 'baz', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #14') + set whichwrap& +endfunction +"}}} +function! s:suite.charwise_n_a_character() abort "{{{ + " #1 + call setline('.', '(a)') + normal 0sda( + call g:assert.equals(getline('.'), 'a', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(', 'a', ')']) + normal ggsda( + call g:assert.equals(getline(1), '', 'failed at #2') + call g:assert.equals(getline(2), 'a', 'failed at #2') + call g:assert.equals(getline(3), '', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(a', ')']) + normal ggsda( + call g:assert.equals(getline(1), 'a', 'failed at #3') + call g:assert.equals(getline(2), '', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 2, 1, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(', 'a)']) + normal ggsda( + call g:assert.equals(getline(1), '', 'failed at #4') + call g:assert.equals(getline(2), 'a', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 2, 2, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_n_nothing_inside() abort "{{{ + " #1 + call setline('.', '()') + normal 0sd2l + call g:assert.equals(getline('.'), '', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 1, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo()bar') + normal 03lsd2l + call g:assert.equals(getline('.'), 'foobar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + %delete + set whichwrap=h,l + + " #3 + call append(0, ['(', ')']) + normal ggsd3l + call g:assert.equals(getline(1), '', 'failed at #3') + call g:assert.equals(getline(2), '', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 2, 1, 0], 'failed at #3') + + set whichwrap& +endfunction +"}}} +function! s:suite.charwise_n_breaking() abort "{{{ + set whichwrap=h,l + + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #1 + call append(0, ['aa', 'aaafooaaa', 'aa']) + normal ggsd15l + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['bb', 'bbb', 'bbfoobb', 'bbb', 'bb']) + normal ggsd21l + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + %delete + + onoremap ii :call TextobjCoord(1, 4, 3, 2) + call operator#sandwich#set('delete', 'char', 'cursor', 'keep') + + " #3 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2lsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg3lsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg4lsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggjsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj2lsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj3lsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj5lsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #10') + + %delete + + " #11 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj6lsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #11') + + %delete + + " #12 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj8lsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #12') + + %delete + + " #13 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2jsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #13') + + %delete + + " #14 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2jlsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #14') + + %delete + + " #15 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2j2lsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #15') + + %delete + + " #16 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2j4lsdii + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #16') + + ounmap ii + call operator#sandwich#set('delete', 'char', 'cursor', 'inner_head') + unlet! g:operator#sandwich#recipes + set whichwrap& +endfunction +"}}} +function! s:suite.charwise_n_count() abort "{{{ + " #1 + call setline('.', '((foo))') + normal 02sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '{[(foo)]}') + normal 03sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + " #3 + call setline('.', '(foo)') + normal 0sd5l + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + " #4 + call setline('.', '[(foo bar)]') + normal 02sd11l + call g:assert.equals(getline('.'), 'foo bar', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 8, 0], 'failed at #4') + + " #5 + call setline('.', 'foo{[(bar)]}baz') + normal 03l3sd9l + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #5') +endfunction +"}}} +function! s:suite.charwise_n_external_textobj() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call setline('.', '{[(foo)]}') + normal 02lsd5l + call g:assert.equals(getline('.'), '{[foo]}', 'failed at #1') + + " #2 + call setline('.', '{[(foo)]}') + normal 0lsd7l + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #2') + + " #3 + call setline('.', '{[(foo)]}') + normal 0sd9l + call g:assert.equals(getline('.'), '[(foo)]', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call setline('.', '(foo)') + normal 0sd5l + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.charwise_n_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call setline('.', '(α)') + normal 0sd3l + call g:assert.equals(getline('.'), 'α', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #1') + + " #2 + call setline('.', '(aα)') + normal 0sd4l + call g:assert.equals(getline('.'), 'aα', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call setline('.', 'αaα') + normal 0sd3l + call g:assert.equals(getline('.'), 'a', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #3') + + " #4 + call setline('.', 'ααα') + normal 0sd3l + call g:assert.equals(getline('.'), 'α', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #4') + + " #5 + call setline('.', 'αaαα') + normal 0sd4l + call g:assert.equals(getline('.'), 'aα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #5') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call setline('.', 'aαaaα') + normal 0sd5l + call g:assert.equals(getline('.'), 'a', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #6') + + " #7 + call setline('.', 'aααaα') + normal 0sd5l + call g:assert.equals(getline('.'), 'α', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #7') + + " #8 + call setline('.', 'aαaαaα') + normal 0sd6l + call g:assert.equals(getline('.'), 'aα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #8') + + unlet g:operator#sandwich#recipes + + " #9 + call setline('.', '(“)') + normal 0sd3l + call g:assert.equals(getline('.'), '“', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #9') + + " #10 + call setline('.', '(a“)') + normal 0sd4l + call g:assert.equals(getline('.'), 'a“', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #10') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call setline('.', '“a“') + normal 0sd3l + call g:assert.equals(getline('.'), 'a', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #11') + + " #12 + call setline('.', '“““') + normal 0sd3l + call g:assert.equals(getline('.'), '“', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #12') + + " #13 + call setline('.', '“a““') + normal 0sd4l + call g:assert.equals(getline('.'), 'a“', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #13') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call setline('.', 'a“aa“') + normal 0sd5l + call g:assert.equals(getline('.'), 'a', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #14') + + " #15 + call setline('.', 'a““a“') + normal 0sd5l + call g:assert.equals(getline('.'), '“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #15') + + " #16 + call setline('.', 'a“a“a“') + normal 0sd6l + call g:assert.equals(getline('.'), 'a“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #16') +endfunction +"}}} +function! s:suite.charwise_n_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', '(((foo)))') + normal 0l2sd% + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + + " #2 + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + + " #3 + call setline('.', '( foo)') + normal 0sda( + call g:assert.equals(getline('.'), ' foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + + """ inner_head + " #4 + call operator#sandwich#set('delete', 'char', 'cursor', 'inner_head') + call setline('.', '(((foo)))') + normal 0l2sd% + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + + " #5 + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + + """ keep + " #6 + call operator#sandwich#set('delete', 'char', 'cursor', 'keep') + call setline('.', '(((foo)))') + normal 0l2sd% + call g:assert.equals(getline('.'), '(foo)', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #6') + + " #7 + normal 2lsda( + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #7') + + """ inner_tail + " #8 + call operator#sandwich#set('delete', 'char', 'cursor', 'inner_tail') + call setline('.', '(((foo)))') + normal 0l2sd% + call g:assert.equals(getline('.'), '(foo)', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #8') + + " #9 + normal hsda( + call g:assert.equals(getline('.'), 'foo', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #9') + + """ head + " #10 + call operator#sandwich#set('delete', 'char', 'cursor', 'head') + call setline('.', '(((foo)))') + normal 0l2sd% + call g:assert.equals(getline('.'), '(foo)', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + + " #11 + normal 3lsda( + call g:assert.equals(getline('.'), 'foo', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + + """ tail + " #12 + call operator#sandwich#set('delete', 'char', 'cursor', 'tail') + call setline('.', '(((foo)))') + normal 0l2sd% + call g:assert.equals(getline('.'), '(foo)', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #12') + + " #13 + normal 3hsda( + call g:assert.equals(getline('.'), 'foo', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head'}] + call operator#sandwich#set('delete', 'char', 'cursor', 'inner_tail') + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), 'foo', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.charwise_n_option_noremap() abort "{{{ + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '{(foo)}') + normal 0sd7l + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', '{(foo)}') + normal 0lsd5l + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #2') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call setline('.', '{(foo)}') + normal 0sd7l + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #3') + + " #4 + call setline('.', '{(foo)}') + normal 0lsd5l + call g:assert.equals(getline('.'), '{foo}', 'failed at #4') + + """ off + call operator#sandwich#set('delete', 'char', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '{(foo)}') + normal 0sd7l + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #5') + + " #6 + call setline('.', '{(foo)}') + normal 0lsd5l + call g:assert.equals(getline('.'), '{foo}', 'failed at #6') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call setline('.', '{(foo)}') + normal 0sd7l + call g:assert.equals(getline('.'), '(foo)', 'failed at #7') + + " #8 + call setline('.', '{(foo)}') + normal 0lsd5l + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #8') +endfunction +"}}} +function! s:suite.charwise_n_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+foo\d\+') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', '888foo888') + normal 0sd$ + call g:assert.equals(getline('.'), '88foo88', 'failed at #2') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call setline('.', '\d\+foo\d\+') + normal 0sd$ + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #3') + + " #4 + call setline('.', '888foo888') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + """ on + call operator#sandwich#set('delete', 'char', 'regex', 1) + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #5 + call setline('.', '\d\+foo\d\+') + normal 0sd$ + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #5') + + " #6 + call setline('.', '888foo888') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call setline('.', '\d\+foo\d\+') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + + " #8 + call setline('.', '888foo888') + normal 0sd$ + call g:assert.equals(getline('.'), '88foo88', 'failed at #8') +endfunction +"}}} +function! s:suite.charwise_n_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + + """ 1 + " #1 + call setline('.', '"foo"') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', ' "foo"') + normal 0sd$ + call g:assert.equals(getline('.'), ' foo', 'failed at #2') + + " #3 + call setline('.', '"foo" ') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo ', 'failed at #3') + + " #4 + " do not skip! + call setline('.', ' "foo" ') + normal 0sd$ + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call setline('.', ' "foo"') + normal 0sd$ + call g:assert.equals(getline('.'), ' "foo"', 'failed at #5') + + """ 2 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'char', 'skip_space', 2) + + " #6 + call setline('.', '"foo"') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + " #7 + call setline('.', ' "foo"') + normal 0sd$ + call g:assert.equals(getline('.'), ' foo', 'failed at #7') + + " #8 + call setline('.', '"foo" ') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo ', 'failed at #8') + + " #9 + call setline('.', ' "foo" ') + normal 0sd$ + call g:assert.equals(getline('.'), ' foo ', 'failed at #9') + + " " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call setline('.', ' "foo"') + normal 0sd$ + call g:assert.equals(getline('.'), ' "foo"', 'failed at #10') + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'char', 'skip_space', 0) + + " #11 + call setline('.', '"foo"') + normal 0sd$ + call g:assert.equals(getline('.'), 'foo', 'failed at #11') + + " #12 + call setline('.', ' "foo"') + normal 0sd$ + call g:assert.equals(getline('.'), ' "foo"', 'failed at #12') + + " #13 + call setline('.', '"foo" ') + normal 0sd$ + call g:assert.equals(getline('.'), '"foo" ', 'failed at #13') + + " #14 + " do not skip! + call setline('.', ' "foo" ') + normal 0sd$ + call g:assert.equals(getline('.'), '"foo"', 'failed at #14') + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}] + call setline('.', ' "foo"') + normal 0sd$ + call g:assert.equals(getline('.'), ' foo', 'failed at #15') +endfunction +"}}} +function! s:suite.charwise_n_option_skip_char() abort "{{{ + """ off + " #1 + call setline('.', 'aa(foo)bb') + normal 0sd$ + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #1') + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call setline('.', 'aa(foo)bb') + normal 0sd$ + call g:assert.equals(getline('.'), 'aafoobb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + """ on + call operator#sandwich#set('delete', 'char', 'skip_char', 1) + + " #3 + call setline('.', 'aa(foo)bb') + normal 0sd$ + call g:assert.equals(getline('.'), 'aafoobb', 'failed at #3') + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call setline('.', 'aa(foo)bb') + normal 0sd$ + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.charwise_n_option_command() abort "{{{ + " #1 + call operator#sandwich#set('delete', 'char', 'command', ['normal! `[d`]']) + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), '', 'failed at #1') + + " #2 + call operator#sandwich#set('delete', 'char', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), '', 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_n_option_linewise() abort "{{{ + set whichwrap=h,l + call operator#sandwich#set('delete', 'char', 'linewise', 1) + + """ 1 + " #1 + call append(0, ['(', 'foo', ')']) + normal ggsd7l + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['( ', 'foo', ' )']) + normal ggsd11l + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggsd11l + call g:assert.equals(getline(1), 'aa', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), 'aa', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa', 'foo', ')']) + normal ggsd9l + call g:assert.equals(getline(1), 'aa', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 2, 4, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', 'aa)']) + normal ggsd9l + call g:assert.equals(getline(1), 'foo', 'failed at #5') + call g:assert.equals(getline(2), 'aa', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 2, 3, 0], 'failed at #5') + + %delete + + " #6 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggsd9l + call g:assert.equals(getline(1), '', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), '', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #6') + unlet! g:operator#sandwich#recipes + + %delete + + """ 2 + call operator#sandwich#set('delete', 'char', 'linewise', 2) + + " #7 + call append(0, ['(', 'foo', ')']) + normal ggsd7l + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['( ', 'foo', ' )']) + normal ggsd11l + call g:assert.equals(getline(1), 'foo', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggsd11l + call g:assert.equals(getline(1), 'foo', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['aa', '(foo)', 'bb']) + normal ggjsd5l + call g:assert.equals(getline(1), 'aa', 'failed at #10') + call g:assert.equals(getline(2), 'bb', 'failed at #10') + call g:assert.equals(getline(3), '', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 2, 1, 0], 'failed at #10') + + %delete + + " #11 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggsd9l + call g:assert.equals(getline(1), '', 'failed at #11') + call g:assert.equals(getline(2), 'foo', 'failed at #11') + call g:assert.equals(getline(3), '', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #11') + unlet! g:operator#sandwich#recipes + + set whichwrap& +endfunction +"}}} + +function! s:suite.charwise_x_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + normal 0v4lsd + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0v4lsd + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal 0v4lsd + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + " #4 + call setline('.', '') + normal 0v4lsd + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_x_not_registered() abort "{{{ + " #1 + call setline('.', 'afooa') + normal 0v4lsd + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '*foo*') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_x_positioning() abort "{{{ + " #1 + call setline('.', '(foo)bar') + normal 0v4lsd + call g:assert.equals(getline('.'), 'foobar', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', 'foo(bar)') + normal 03lv4lsd + call g:assert.equals(getline('.'), 'foobar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #2') + + " #3 + call setline('.', 'foo(bar)baz') + normal 03lv4lsd + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(foo', 'bar', 'baz)']) + normal ggv2j3lsd + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_x_a_character() abort "{{{ + " #1 + call setline('.', '(a)') + normal 0v2lsd + call g:assert.equals(getline('.'), 'a', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #1') +endfunction +"}}} +function! s:suite.charwise_x_nothing_inside() abort "{{{ + " #1 + call setline('.', '()') + normal 0vlsd + call g:assert.equals(getline('.'), '', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 1, 0], 'failed at #1') + + %delete + + " #2 + call setline('.', 'foo()bar') + normal 03lvlsd + call g:assert.equals(getline('.'), 'foobar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(', ')']) + normal ggvjsd + call g:assert.equals(getline(1), '', 'failed at #3') + call g:assert.equals(getline(2), '', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 2, 1, 0], 'failed at #3') +endfunction +"}}} +function! s:suite.charwise_x_breaking() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #1 + call append(0, ['aa', 'aaafooaaa', 'aa']) + normal ggv2jlsd + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['bb', 'bbb', 'bbfoobb', 'bbb', 'bb']) + normal ggv4jlsd + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_x_count() abort "{{{ + " #1 + call setline('.', '((foo))') + normal 0v$2sd + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '{[(foo)]}') + normal 0v$3sd + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + " #3 + call setline('.', 'foo{[(bar)]}baz') + normal 03lv8l3sd + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #3') +endfunction +"}}} +function! s:suite.charwise_x_external_textobj() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call setline('.', '{[(foo)]}') + normal 02lv4lsd + call g:assert.equals(getline('.'), '{[foo]}', 'failed at #1') + + " #2 + call setline('.', '{[(foo)]}') + normal 0lv6lsd + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #2') + + " #3 + call setline('.', '{[(foo)]}') + normal 0v8lsd + call g:assert.equals(getline('.'), '[(foo)]', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call setline('.', '(foo)') + normal 0v$sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.charwise_x_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call setline('.', '(α)') + normal 0v2lsd + call g:assert.equals(getline('.'), 'α', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #1') + + " #2 + call setline('.', '(aα)') + normal 0v3lsd + call g:assert.equals(getline('.'), 'aα', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call setline('.', 'αaα') + normal 0v2lsd + call g:assert.equals(getline('.'), 'a', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #3') + + " #4 + call setline('.', 'ααα') + normal 0v2lsd + call g:assert.equals(getline('.'), 'α', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #4') + + " #5 + call setline('.', 'αaαα') + normal 0v3lsd + call g:assert.equals(getline('.'), 'aα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #5') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call setline('.', 'aαaaα') + normal 0v4lsd + call g:assert.equals(getline('.'), 'a', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #6') + + " #7 + call setline('.', 'aααaα') + normal 0v4lsd + call g:assert.equals(getline('.'), 'α', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #7') + + " #8 + call setline('.', 'aαaαaα') + normal 0v5lsd + call g:assert.equals(getline('.'), 'aα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #8') + + unlet g:operator#sandwich#recipes + + " #9 + call setline('.', '(“)') + normal 0v2lsd + call g:assert.equals(getline('.'), '“', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #9') + + " #10 + call setline('.', '(a“)') + normal 0v3lsd + call g:assert.equals(getline('.'), 'a“', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #10') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call setline('.', '“a“') + normal 0v2lsd + call g:assert.equals(getline('.'), 'a', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #11') + + " #12 + call setline('.', '“““') + normal 0v2lsd + call g:assert.equals(getline('.'), '“', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #12') + + " #13 + call setline('.', '“a““') + normal 0v3lsd + call g:assert.equals(getline('.'), 'a“', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #13') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call setline('.', 'a“aa“') + normal 0v4lsd + call g:assert.equals(getline('.'), 'a', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #14') + + " #15 + call setline('.', 'a““a“') + normal 0v4lsd + call g:assert.equals(getline('.'), '“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #15') + + " #16 + call setline('.', 'a“a“a“') + normal 0v5lsd + call g:assert.equals(getline('.'), 'a“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #16') +endfunction +"}}} +function! s:suite.charwise_x_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', '(((foo)))') + normal 0lv%2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + + " #2 + normal 0va(sd + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + + " #3 + call setline('.', '( foo)') + normal 0v$sd + call g:assert.equals(getline('.'), ' foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + + """ inner_head + " #4 + call operator#sandwich#set('delete', 'char', 'cursor', 'inner_head') + call setline('.', '(((foo)))') + normal 0lv%2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + + " #5 + normal 0va(sd + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + + """ keep + " #6 + call operator#sandwich#set('delete', 'char', 'cursor', 'keep') + call setline('.', '(((foo)))') + normal 0lv%2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #6') + + " #7 + normal va(osd + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + + """ inner_tail + " #8 + call operator#sandwich#set('delete', 'char', 'cursor', 'inner_tail') + call setline('.', '(((foo)))') + normal 0lv%o2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #8') + + " #9 + normal va(osd + call g:assert.equals(getline('.'), 'foo', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #9') + + """ head + " #10 + call operator#sandwich#set('delete', 'char', 'cursor', 'head') + call setline('.', '(((foo)))') + normal 0lv%2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + + " #11 + normal va(sd + call g:assert.equals(getline('.'), 'foo', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + + """ tail + " #12 + call operator#sandwich#set('delete', 'char', 'cursor', 'tail') + call setline('.', '(((foo)))') + normal 0lv%o2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #12') + + " #13 + normal va(osd + call g:assert.equals(getline('.'), 'foo', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head'}] + call operator#sandwich#set('delete', 'char', 'cursor', 'inner_tail') + call setline('.', '(foo)') + normal 0va(sd + call g:assert.equals(getline('.'), 'foo', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.charwise_x_option_noremap() abort "{{{ + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '{(foo)}') + normal 0v6lsd + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', '{(foo)}') + normal 0lv4lsd + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #2') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call setline('.', '{(foo)}') + normal 0v6lsd + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #3') + + " #4 + call setline('.', '{(foo)}') + normal 0lv4lsd + call g:assert.equals(getline('.'), '{foo}', 'failed at #4') + + """ off + call operator#sandwich#set('delete', 'char', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '{(foo)}') + normal 0v6lsd + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #5') + + " #6 + call setline('.', '{(foo)}') + normal 0lv4lsd + call g:assert.equals(getline('.'), '{foo}', 'failed at #6') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call setline('.', '{(foo)}') + normal 0v6lsd + call g:assert.equals(getline('.'), '(foo)', 'failed at #7') + + " #8 + call setline('.', '{(foo)}') + normal 0lv4lsd + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #8') +endfunction +"}}} +function! s:suite.charwise_x_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+foo\d\+') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', '888foo888') + normal 0v$sd + call g:assert.equals(getline('.'), '88foo88', 'failed at #2') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call setline('.', '\d\+foo\d\+') + normal 0v$sd + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #3') + + " #4 + call setline('.', '888foo888') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + """ on + call operator#sandwich#set('delete', 'char', 'regex', 1) + " #5 + call setline('.', '\d\+foo\d\+') + normal 0v$sd + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #5') + + " #6 + call setline('.', '888foo888') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call setline('.', '\d\+foo\d\+') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + + " #8 + call setline('.', '888foo888') + normal 0v$sd + call g:assert.equals(getline('.'), '88foo88', 'failed at #8') +endfunction +"}}} +function! s:suite.charwise_x_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + + """ 1 + " #1 + call setline('.', '"foo"') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', ' "foo"') + normal 0v$sd + call g:assert.equals(getline('.'), ' foo', 'failed at #2') + + " #3 + call setline('.', '"foo" ') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo ', 'failed at #3') + + " #4 + " do not skip! + call setline('.', ' "foo" ') + normal 0v$sd + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call setline('.', ' "foo"') + normal 0sd$ + call g:assert.equals(getline('.'), ' "foo"', 'failed at #5') + + """ 2 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'char', 'skip_space', 2) + + " #6 + call setline('.', '"foo"') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + " #7 + call setline('.', ' "foo"') + normal 0v$sd + call g:assert.equals(getline('.'), ' foo', 'failed at #7') + + " #8 + call setline('.', '"foo" ') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo ', 'failed at #8') + + " #9 + call setline('.', ' "foo" ') + normal 0v$sd + call g:assert.equals(getline('.'), ' foo ', 'failed at #9') + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call setline('.', ' "foo"') + normal 0sd$ + call g:assert.equals(getline('.'), ' "foo"', 'failed at #10') + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'char', 'skip_space', 0) + + " #11 + call setline('.', '"foo"') + normal 0v$sd + call g:assert.equals(getline('.'), 'foo', 'failed at #11') + + " #12 + call setline('.', ' "foo"') + normal 0v$sd + call g:assert.equals(getline('.'), ' "foo"', 'failed at #12') + + " #13 + call setline('.', '"foo" ') + normal 0v$sd + call g:assert.equals(getline('.'), '"foo" ', 'failed at #13') + + " #14 + " do not skip! + call setline('.', ' "foo" ') + normal 0v$sd + call g:assert.equals(getline('.'), '"foo"', 'failed at #14') + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}] + call setline('.', ' "foo"') + normal 0sd$ + call g:assert.equals(getline('.'), ' foo', 'failed at #15') +endfunction +"}}} +function! s:suite.charwise_x_option_skip_char() abort "{{{ + """ off + " #1 + call setline('.', 'aa(foo)bb') + normal 0v$sd + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #1') + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call setline('.', 'aa(foo)bb') + normal 0v$sd + call g:assert.equals(getline('.'), 'aafoobb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + """ on + call operator#sandwich#set('delete', 'char', 'skip_char', 1) + + " #3 + call setline('.', 'aa(foo)bb') + normal 0v$sd + call g:assert.equals(getline('.'), 'aafoobb', 'failed at #3') + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call setline('.', 'aa(foo)bb') + normal 0sd$ + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.charwise_x_option_command() abort "{{{ + " #1 + call operator#sandwich#set('delete', 'char', 'command', ['normal! `[d`]']) + call setline('.', '(foo)') + normal 0va(sd + call g:assert.equals(getline('.'), '', 'failed at #1') + + " #2 + call operator#sandwich#set('delete', 'char', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call setline('.', '(foo)') + normal 0va(sd + call g:assert.equals(getline('.'), '', 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_x_option_linewise() abort "{{{ + set whichwrap=h,l + call operator#sandwich#set('delete', 'char', 'linewise', 1) + + """ 1 + " #1 + call append(0, ['(', 'foo', ')']) + normal ggv2jsd + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['( ', 'foo', ' )']) + normal ggv2j2lsd + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggv2j2lsd + call g:assert.equals(getline(1), 'aa', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), 'aa', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa', 'foo', ')']) + normal ggv2jsd + call g:assert.equals(getline(1), 'aa', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 2, 4, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', 'aa)']) + normal ggv2j2lsd + call g:assert.equals(getline(1), 'foo', 'failed at #5') + call g:assert.equals(getline(2), 'aa', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 2, 3, 0], 'failed at #5') + + %delete + + " #6 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggv2jsd + call g:assert.equals(getline(1), '', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), '', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #6') + unlet! g:operator#sandwich#recipes + + %delete + + call operator#sandwich#set('delete', 'char', 'linewise', 2) + + """ 2 + " #7 + call append(0, ['(', 'foo', ')']) + normal ggv2jsd + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['( ', 'foo', ' )']) + normal ggv2j2lsd + call g:assert.equals(getline(1), 'foo', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggv2j2lsd + call g:assert.equals(getline(1), 'foo', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['aa', '(foo)', 'bb']) + normal ggjv4lsd + call g:assert.equals(getline(1), 'aa', 'failed at #10') + call g:assert.equals(getline(2), 'bb', 'failed at #10') + call g:assert.equals(getline(3), '', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 2, 1, 0], 'failed at #10') + + %delete + + " #11 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggv2jsd + call g:assert.equals(getline(1), '', 'failed at #11') + call g:assert.equals(getline(2), 'foo', 'failed at #11') + call g:assert.equals(getline(3), '', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #11') + unlet! g:operator#sandwich#recipes + + set whichwrap& +endfunction +"}}} + +" line-wise +function! s:suite.linewise_n_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + " #4 + call setline('.', '') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', ')']) + normal ggsdVa( + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['[', 'foo', ']']) + normal ggsdVa[ + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['{', 'foo', '}']) + normal ggsdVa{ + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['<', 'foo', '>']) + normal ggsdVa< + call g:assert.equals(getline('.'), 'foo', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_n_not_registered() abort "{{{ + " #1 + call setline('.', 'afooa') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '*foo*') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['a', 'foo', 'a']) + normal ggsd2j + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['*', 'foo', '*']) + normal ggsd2j + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_n_positioning() abort "{{{ + " #1 + call append(0, ['(', 'foo', 'bar', 'baz', ')']) + normal ggsdVa( + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getline(2), 'bar', 'failed at #1') + call g:assert.equals(getline(3), 'baz', 'failed at #1') + call g:assert.equals(getline(4), '', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', '(', 'bar', ')', 'baz']) + normal gg2jsdVa( + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + call g:assert.equals(getline(4), '', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 2, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(foo', 'bar', 'baz)']) + normal ggsdVa( + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getline(2), 'bar', 'failed at #3') + call g:assert.equals(getline(3), 'baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #3') +endfunction +"}}} +function! s:suite.linewise_n_a_character() abort "{{{ + " #1 + call setline('.', '(a)') + normal 0sdVa( + call g:assert.equals(getline('.'), 'a', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(', 'a', ')']) + normal ggsdVa( + call g:assert.equals(getline('.'), 'a', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(a', ')']) + normal ggsdVa( + call g:assert.equals(getline('.'), 'a', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(', 'a)']) + normal ggsdVa( + call g:assert.equals(getline('.'), 'a', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_n_nothing_inside() abort "{{{ + " #1 + call setline('.', '()') + normal 0sdVl + call g:assert.equals(getline('.'), '', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(', ')']) + normal ggsdj + call g:assert.equals(getline(1), '', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 1, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_n_breaking() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #1 + call append(0, ['aa', 'aaafooaaa', 'aa']) + normal ggsd2j + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['bb', 'bbb', 'bbfoobb', 'bbb', 'bb']) + normal ggsd4j + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['aa', 'aaa', 'aa', 'aaafooaaa', 'aa', 'aaa', 'aa']) + normal gg2sd6j + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['bb', 'bbb', 'bb', 'bb', 'bbb', 'bbfoobb', 'bbb', 'bb', 'bb', 'bbb', 'bb']) + normal gg2sd10j + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_n_count() abort "{{{ + " #1 + call setline('.', '((foo))') + normal 02sdV$ + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '{[(foo)]}') + normal 03sdV$ + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + " #3 + call setline('.', '(foo)') + normal 0sdV5l + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + " #4 + call setline('.', '[(foo bar)]') + normal 02sdV11l + call g:assert.equals(getline('.'), 'foo bar', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 8, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['foo', '{', '[', '(', 'bar', ')', ']', '}', 'baz']) + normal ggj3sdV6j + call g:assert.equals(getline(1), 'foo', 'failed at #5') + call g:assert.equals(getline(2), 'bar', 'failed at #5') + call g:assert.equals(getline(3), 'baz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 2, 4, 0], 'failed at #5') +endfunction +"}}} +function! s:suite.linewise_n_external_textobj() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call setline('.', '{[(foo)]}') + normal 0sdVl + call g:assert.equals(getline('.'), '[(foo)]', 'failed at #1') + + " #2 + normal 0sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + " #3 + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0sdV$ + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_n_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call append(0, ['(', 'α', ')']) + normal ggsd2j + call g:assert.equals(getline(1), 'α', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(', 'aα', ')']) + normal ggsd2j + call g:assert.equals(getline(1), 'aα', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #2') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call append(0, ['α', 'a', 'α']) + normal ggsd2j + call g:assert.equals(getline(1), 'a', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['α', 'α', 'α']) + normal ggsd2j + call g:assert.equals(getline(1), 'α', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['α', 'aα', 'α']) + normal ggsd2j + call g:assert.equals(getline(1), 'aα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #5') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call append(0, ['aα', 'a', 'aα']) + normal ggsd2j + call g:assert.equals(getline(1), 'a', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['aα', 'α', 'aα']) + normal ggsd2j + call g:assert.equals(getline(1), 'α', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['aα', 'aα', 'aα']) + normal ggsd2j + call g:assert.equals(getline(1), 'aα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #8') + + %delete + unlet g:operator#sandwich#recipes + + " #9 + call append(0, ['(', '“', ')']) + normal ggsd2j + call g:assert.equals(getline(1), '“', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['(', 'a“', ')']) + normal ggsd2j + call g:assert.equals(getline(1), 'a“', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #10') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call append(0, ['“', 'a', '“']) + normal ggsd2j + call g:assert.equals(getline(1), 'a', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #11') + + %delete + + " #12 + call append(0, ['“', '“', '“']) + normal ggsd2j + call g:assert.equals(getline(1), '“', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #12') + + %delete + + " #13 + call append(0, ['“', 'a“', '“']) + normal ggsd2j + call g:assert.equals(getline(1), 'a“', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #13') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call append(0, ['a“', 'a', 'a“']) + normal ggsd2j + call g:assert.equals(getline(1), 'a', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #14') + + %delete + + " #15 + call append(0, ['a“', '“', 'a“']) + normal ggsd2j + call g:assert.equals(getline(1), '“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #15') + + %delete + + " #16 + call append(0, ['a“', 'a“', 'a“']) + normal ggsd2j + call g:assert.equals(getline(1), 'a“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #16') +endfunction +"}}} +function! s:suite.linewise_n_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', '(((foo)))') + normal 0l2sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + + " #2 + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + + " #3 + %delete + call append(0, ['(', ' foo', ')']) + normal ggsd2j + call g:assert.equals(getline(1), ' foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + %delete + + """ inner_head + " #4 + call operator#sandwich#set('delete', 'line', 'cursor', 'inner_head') + call setline('.', '(((foo)))') + normal 0l2sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + + " #5 + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + + """ keep + " #6 + call operator#sandwich#set('delete', 'line', 'cursor', 'keep') + call setline('.', '(((foo)))') + normal 03l2sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #6') + + " #7 + normal lsdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + + """ inner_tail + " #8 + call operator#sandwich#set('delete', 'line', 'cursor', 'inner_tail') + call setline('.', '(((foo)))') + normal 02sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #8') + + " #9 + normal 2hsdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #9') + + """ head + " #10 + call operator#sandwich#set('delete', 'line', 'cursor', 'head') + call setline('.', '(((foo)))') + normal 02sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + + " #11 + normal 3lsdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + + """ tail + " #12 + call operator#sandwich#set('delete', 'line', 'cursor', 'tail') + call setline('.', '(((foo)))') + normal 02sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #12') + + " #13 + normal 3hsdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head'}] + call operator#sandwich#set('delete', 'line', 'cursor', 'inner_tail') + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.linewise_n_option_noremap() abort "{{{ + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '{(foo)}') + normal 0sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', '({foo})') + normal 0sdVl + call g:assert.equals(getline('.'), '({foo})', 'failed at #2') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call setline('.', '{(foo)}') + normal 0sdVl + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #3') + + " #4 + call setline('.', '({foo})') + normal 0sdVl + call g:assert.equals(getline('.'), '{foo}', 'failed at #4') + + """ off + call operator#sandwich#set('delete', 'line', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '{(foo)}') + normal 0sdVl + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #5') + + " #6 + call setline('.', '({foo})') + normal 0sdVl + call g:assert.equals(getline('.'), '{foo}', 'failed at #6') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call setline('.', '{(foo)}') + normal 0sdVl + call g:assert.equals(getline('.'), '(foo)', 'failed at #7') + + " #8 + call setline('.', '({foo})') + normal 0sdVl + call g:assert.equals(getline('.'), '({foo})', 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_n_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+foo\d\+') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', '888foo888') + normal 0sdVl + call g:assert.equals(getline('.'), '88foo88', 'failed at #2') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call setline('.', '\d\+foo\d\+') + normal 0sdVl + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #3') + + " #4 + call setline('.', '888foo888') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + """ on + call operator#sandwich#set('delete', 'line', 'regex', 1) + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #5 + call setline('.', '\d\+foo\d\+') + normal 0sdVl + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #5') + + " #6 + call setline('.', '888foo888') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call setline('.', '\d\+foo\d\+') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + + " #8 + call setline('.', '888foo888') + normal 0sdVl + call g:assert.equals(getline('.'), '88foo88', 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_n_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + + """ 2 + " #1 + call setline('.', '"foo"') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', ' "foo"') + normal 0sdVl + call g:assert.equals(getline('.'), ' foo', 'failed at #2') + + " #3 + call setline('.', '"foo" ') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo ', 'failed at #3') + + " #4 + call setline('.', ' "foo" ') + normal 0sdVl + call g:assert.equals(getline('.'), ' foo ', 'failed at #4') + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call setline('.', ' "foo"') + normal 0sdVl + call g:assert.equals(getline('.'), ' "foo"', 'failed at #5') + + """ 1 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'line', 'skip_space', 1) + + " #6 + call setline('.', '"foo"') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + " #7 + call setline('.', ' "foo"') + normal 0sdVl + call g:assert.equals(getline('.'), ' foo', 'failed at #7') + + " #8 + call setline('.', '"foo" ') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo ', 'failed at #8') + + " #9 + " do not skip! + call setline('.', ' "foo" ') + normal 0sdVl + call g:assert.equals(getline('.'), '"foo"', 'failed at #9') + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call setline('.', ' "foo"') + normal 0sdVl + call g:assert.equals(getline('.'), ' "foo"', 'failed at #10') + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'line', 'skip_space', 0) + + " #11 + call setline('.', '"foo"') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #11') + + " #12 + call setline('.', ' "foo"') + normal 0sdVl + call g:assert.equals(getline('.'), ' "foo"', 'failed at #12') + + " #13 + call setline('.', '"foo" ') + normal 0sdVl + call g:assert.equals(getline('.'), '"foo" ', 'failed at #13') + + " #14 + " do not skip! + call setline('.', ' "foo" ') + normal 0sdVl + call g:assert.equals(getline('.'), '"foo"', 'failed at #14') + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}] + call setline('.', ' "foo"') + normal 0sdVl + call g:assert.equals(getline('.'), ' foo', 'failed at #15') +endfunction +"}}} +function! s:suite.linewise_n_option_skip_char() abort "{{{ + """ off + " #1 + call setline('.', 'aa(foo)bb') + normal 0sdVl + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #1') + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call setline('.', 'aa(foo)bb') + normal 0sdVl + call g:assert.equals(getline('.'), 'aafoobb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + """ on + call operator#sandwich#set('delete', 'line', 'skip_char', 1) + + " #3 + call setline('.', 'aa(foo)bb') + normal 0sdVl + call g:assert.equals(getline('.'), 'aafoobb', 'failed at #3') + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call setline('.', 'aa(foo)bb') + normal 0sdVl + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_n_option_command() abort "{{{ + call operator#sandwich#set('delete', 'line', 'command', ['normal! `[d`]']) + + " #1 + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), '', 'failed at #1') + + " #2 + call operator#sandwich#set('delete', 'line', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), '', 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_n_option_linewise() abort "{{{ + call operator#sandwich#set('delete', 'line', 'linewise', 0) + + """ 0 + " #1 + call append(0, ['(', 'foo', ')']) + normal ggsd2j + call g:assert.equals(getline(1), '', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['( ', 'foo', ' )']) + normal ggsd2j + call g:assert.equals(getline(1), ' ', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), ' ', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggsd2j + call g:assert.equals(getline(1), 'aa', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), 'aa', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa', 'foo', ')']) + normal ggsd2j + call g:assert.equals(getline(1), 'aa', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '', 'failed at #4') + call g:assert.equals(getline(4), '', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', 'aa)']) + normal ggsd2j + call g:assert.equals(getline(1), '', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), 'aa', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #5') + + %delete + + " #6 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 1}] + call append(0, ['(', 'foo', ')']) + normal ggsd2j + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #6') + unlet! g:operator#sandwich#recipes + + %delete + + """ 2 + call operator#sandwich#set('delete', 'line', 'linewise', 2) + + " #7 + call append(0, ['(', 'foo', ')']) + normal ggsd2j + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['( ', 'foo', ' )']) + normal ggsd2j + call g:assert.equals(getline(1), 'foo', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggsd2j + call g:assert.equals(getline(1), 'foo', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['aa', '(foo)', 'bb']) + normal ggjsdVl + call g:assert.equals(getline(1), 'aa', 'failed at #10') + call g:assert.equals(getline(2), 'bb', 'failed at #10') + call g:assert.equals(getline(3), '', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 2, 1, 0], 'failed at #10') + + %delete + + " #11 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggsd2j + call g:assert.equals(getline(1), '', 'failed at #11') + call g:assert.equals(getline(2), 'foo', 'failed at #11') + call g:assert.equals(getline(3), '', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #11') + unlet! g:operator#sandwich#recipes +endfunction +"}}} + +function! s:suite.linewise_x_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + " #4 + call setline('.', '') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', ')']) + normal ggV2jsd + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['[', 'foo', ']']) + normal ggV2jsd + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['{', 'foo', '}']) + normal ggV2jsd + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['<', 'foo', '>']) + normal ggV2jsd + call g:assert.equals(getline('.'), 'foo', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_x_not_registered() abort "{{{ + " #1 + call setline('.', 'afooa') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '*foo*') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['a', 'foo', 'a']) + normal ggV2jsd + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['*', 'foo', '*']) + normal ggV2jsd + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_x_positioning() abort "{{{ + " #1 + call append(0, ['(', 'foo', 'bar', 'baz', ')']) + normal ggV4jsd + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getline(2), 'bar', 'failed at #1') + call g:assert.equals(getline(3), 'baz', 'failed at #1') + call g:assert.equals(getline(4), '', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', '(', 'bar', ')', 'baz']) + normal ggjV2jsd + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + call g:assert.equals(getline(4), '', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 2, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(foo', 'bar', 'baz)']) + normal ggV2jsd + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getline(2), 'bar', 'failed at #3') + call g:assert.equals(getline(3), 'baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #3') +endfunction +"}}} +function! s:suite.linewise_x_a_character() abort "{{{ + " #1 + call setline('.', '(a)') + normal 0Vsd + call g:assert.equals(getline('.'), 'a', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(', 'a', ')']) + normal ggV2jsd + call g:assert.equals(getline('.'), 'a', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(a', ')']) + normal ggVjsd + call g:assert.equals(getline('.'), 'a', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(', 'a)']) + normal ggVjsd + call g:assert.equals(getline('.'), 'a', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_x_nothing_inside() abort "{{{ + " #1 + call setline('.', '()') + normal 0Vsd + call g:assert.equals(getline('.'), '', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(', ')']) + normal ggVjsd + call g:assert.equals(getline(1), '', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 1, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_x_breaking() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #1 + call append(0, ['aa', 'aaafooaaa', 'aa']) + normal ggsdV2j + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['bb', 'bbb', 'bbfoobb', 'bbb', 'bb']) + normal ggsdV4j + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['aa', 'aaa', 'aa', 'aaafooaaa', 'aa', 'aaa', 'aa']) + normal gg2sdV6j + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['bb', 'bbb', 'bb', 'bb', 'bbb', 'bbfoobb', 'bbb', 'bb', 'bb', 'bbb', 'bb']) + normal gg2sdV10j + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_x_count() abort "{{{ + " #1 + call setline('.', '((foo))') + normal 0V2sd + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '{[(foo)]}') + normal 0V3sd + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + " #3 + call setline('.', '(foo)') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + " #4 + call setline('.', '[(foo bar)]') + normal 0V2sd + call g:assert.equals(getline('.'), 'foo bar', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 8, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['foo', '{', '[', '(', 'bar', ')', ']', '}', 'baz']) + normal ggjV6j3sd + call g:assert.equals(getline(1), 'foo', 'failed at #5') + call g:assert.equals(getline(2), 'bar', 'failed at #5') + call g:assert.equals(getline(3), 'baz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 2, 4, 0], 'failed at #5') +endfunction +"}}} +function! s:suite.linewise_x_external_textobj() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call setline('.', '{[(foo)]}') + normal 0Vsd + call g:assert.equals(getline('.'), '[(foo)]', 'failed at #1') + + " #2 + normal 0Vsd + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + " #3 + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call setline('.', '(foo)') + normal 0Vsd + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_x_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call append(0, ['(', 'α', ')']) + normal ggV2jsd + call g:assert.equals(getline(1), 'α', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(', 'aα', ')']) + normal ggV2jsd + call g:assert.equals(getline(1), 'aα', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #2') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call append(0, ['α', 'a', 'α']) + normal ggV2jsd + call g:assert.equals(getline(1), 'a', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['α', 'α', 'α']) + normal ggV2jsd + call g:assert.equals(getline(1), 'α', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['α', 'aα', 'α']) + normal ggV2jsd + call g:assert.equals(getline(1), 'aα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #5') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call append(0, ['aα', 'a', 'aα']) + normal ggV2jsd + call g:assert.equals(getline(1), 'a', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['aα', 'α', 'aα']) + normal ggV2jsd + call g:assert.equals(getline(1), 'α', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, strlen('α')+1, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['aα', 'aα', 'aα']) + normal ggV2jsd + call g:assert.equals(getline(1), 'aα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, strlen('aα')+1, 0], 'failed at #8') + + %delete + unlet g:operator#sandwich#recipes + + " #9 + call append(0, ['(', '“', ')']) + normal ggV2jsd + call g:assert.equals(getline(1), '“', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['(', 'a“', ')']) + normal ggV2jsd + call g:assert.equals(getline(1), 'a“', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #10') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call append(0, ['“', 'a', '“']) + normal ggV2jsd + call g:assert.equals(getline(1), 'a', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #11') + + %delete + + " #12 + call append(0, ['“', '“', '“']) + normal ggV2jsd + call g:assert.equals(getline(1), '“', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #12') + + %delete + + " #13 + call append(0, ['“', 'a“', '“']) + normal ggV2jsd + call g:assert.equals(getline(1), 'a“', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #13') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call append(0, ['a“', 'a', 'a“']) + normal ggV2jsd + call g:assert.equals(getline(1), 'a', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #14') + + %delete + + " #15 + call append(0, ['a“', '“', 'a“']) + normal ggV2jsd + call g:assert.equals(getline(1), '“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 1, strlen('“')+1, 0], 'failed at #15') + + %delete + + " #16 + call append(0, ['a“', 'a“', 'a“']) + normal ggV2jsd + call g:assert.equals(getline(1), 'a“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“')+1, 0], 'failed at #16') +endfunction +"}}} +function! s:suite.linewise_x_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', '(((foo)))') + normal 0V2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + + " #2 + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + + " #3 + %delete + call append(0, ['(', ' foo', ')']) + normal ggV2jsd + call g:assert.equals(getline(1), ' foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + %delete + + """ inner_head + " #4 + call operator#sandwich#set('delete', 'line', 'cursor', 'inner_head') + call setline('.', '(((foo)))') + normal 0lV2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + + " #5 + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + + """ keep + " #6 + call operator#sandwich#set('delete', 'line', 'cursor', 'keep') + call setline('.', '(((foo)))') + normal 03lV2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #6') + + " #7 + normal lVsd + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + + """ inner_tail + " #8 + call operator#sandwich#set('delete', 'line', 'cursor', 'inner_tail') + call setline('.', '(((foo)))') + normal 0V2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #8') + + " #9 + normal 2hVsd + call g:assert.equals(getline('.'), 'foo', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #9') + + """ head + " #10 + call operator#sandwich#set('delete', 'line', 'cursor', 'head') + call setline('.', '(((foo)))') + normal 0V2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + + " #11 + normal 3lVsd + call g:assert.equals(getline('.'), 'foo', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + + """ tail + " #12 + call operator#sandwich#set('delete', 'line', 'cursor', 'tail') + call setline('.', '(((foo)))') + normal 0V2sd + call g:assert.equals(getline('.'), '(foo)', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #12') + + " #13 + normal 3hVsd + call g:assert.equals(getline('.'), 'foo', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head'}] + call operator#sandwich#set('delete', 'line', 'cursor', 'inner_tail') + call setline('.', '(foo)') + normal 0sdVl + call g:assert.equals(getline('.'), 'foo', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.linewise_x_option_noremap() abort "{{{ + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '{(foo)}') + normal 0Vsd + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', '({foo})') + normal 0Vsd + call g:assert.equals(getline('.'), '({foo})', 'failed at #2') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call setline('.', '{(foo)}') + normal 0Vsd + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #3') + + " #4 + call setline('.', '({foo})') + normal 0Vsd + call g:assert.equals(getline('.'), '{foo}', 'failed at #4') + + """ off + call operator#sandwich#set('delete', 'line', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '{(foo)}') + normal 0Vsd + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #5') + + " #6 + call setline('.', '({foo})') + normal 0Vsd + call g:assert.equals(getline('.'), '{foo}', 'failed at #6') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call setline('.', '{(foo)}') + normal 0Vsd + call g:assert.equals(getline('.'), '(foo)', 'failed at #7') + + " #8 + call setline('.', '({foo})') + normal 0Vsd + call g:assert.equals(getline('.'), '({foo})', 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_x_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+foo\d\+') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', '888foo888') + normal 0Vsd + call g:assert.equals(getline('.'), '88foo88', 'failed at #2') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call setline('.', '\d\+foo\d\+') + normal 0Vsd + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #3') + + " #4 + call setline('.', '888foo888') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + """ on + call operator#sandwich#set('delete', 'line', 'regex', 1) + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #5 + call setline('.', '\d\+foo\d\+') + normal 0Vsd + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #5') + + " #6 + call setline('.', '888foo888') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call setline('.', '\d\+foo\d\+') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + + " #8 + call setline('.', '888foo888') + normal 0Vsd + call g:assert.equals(getline('.'), '88foo88', 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_x_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + + """ 2 + " #1 + call setline('.', '"foo"') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', ' "foo"') + normal 0Vsd + call g:assert.equals(getline('.'), ' foo', 'failed at #2') + + " #3 + call setline('.', '"foo" ') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo ', 'failed at #3') + + " #4 + call setline('.', ' "foo" ') + normal 0Vsd + call g:assert.equals(getline('.'), ' foo ', 'failed at #4') + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call setline('.', ' "foo"') + normal 0Vsd + call g:assert.equals(getline('.'), ' "foo"', 'failed at #5') + + """ 1 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'line', 'skip_space', 1) + + " #6 + call setline('.', '"foo"') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + " #7 + call setline('.', ' "foo"') + normal 0Vsd + call g:assert.equals(getline('.'), ' foo', 'failed at #7') + + " #8 + call setline('.', '"foo" ') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo ', 'failed at #8') + + " #9 + " do not skip! + call setline('.', ' "foo" ') + normal 0Vsd + call g:assert.equals(getline('.'), '"foo"', 'failed at #9') + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call setline('.', ' "foo"') + normal 0Vsd + call g:assert.equals(getline('.'), ' "foo"', 'failed at #10') + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'line', 'skip_space', 0) + + " #11 + call setline('.', '"foo"') + normal 0Vsd + call g:assert.equals(getline('.'), 'foo', 'failed at #11') + + " #12 + call setline('.', ' "foo"') + normal 0Vsd + call g:assert.equals(getline('.'), ' "foo"', 'failed at #12') + + " #13 + call setline('.', '"foo" ') + normal 0Vsd + call g:assert.equals(getline('.'), '"foo" ', 'failed at #13') + + " #14 + " do not skip! + call setline('.', ' "foo" ') + normal 0Vsd + call g:assert.equals(getline('.'), '"foo"', 'failed at #14') + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}] + call setline('.', ' "foo"') + normal 0Vsd + call g:assert.equals(getline('.'), ' foo', 'failed at #15') +endfunction +"}}} +function! s:suite.linewise_x_option_skip_char() abort "{{{ + """ off + " #1 + call setline('.', 'aa(foo)bb') + normal 0Vsd + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #1') + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call setline('.', 'aa(foo)bb') + normal 0Vsd + call g:assert.equals(getline('.'), 'aafoobb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + """ on + call operator#sandwich#set('delete', 'line', 'skip_char', 1) + + " #3 + call setline('.', 'aa(foo)bb') + normal 0Vsd + call g:assert.equals(getline('.'), 'aafoobb', 'failed at #3') + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call setline('.', 'aa(foo)bb') + normal 0Vsd + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_x_option_command() abort "{{{ + call operator#sandwich#set('delete', 'line', 'command', ['normal! `[d`]']) + + " #1 + call setline('.', '(foo)') + normal 0Vsd + call g:assert.equals(getline('.'), '', 'failed at #1') + + " #2 + call operator#sandwich#set('delete', 'line', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call setline('.', '(foo)') + normal 0Vsd + call g:assert.equals(getline('.'), '', 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_x_option_linewise() abort "{{{ + call operator#sandwich#set('delete', 'line', 'linewise', 0) + + """ 0 + " #1 + call append(0, ['(', 'foo', ')']) + normal ggV2jsd + call g:assert.equals(getline(1), '', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['( ', 'foo', ' )']) + normal ggV2jsd + call g:assert.equals(getline(1), ' ', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), ' ', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggV2jsd + call g:assert.equals(getline(1), 'aa', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), 'aa', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa', 'foo', ')']) + normal ggV2jsd + call g:assert.equals(getline(1), 'aa', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '', 'failed at #4') + call g:assert.equals(getline(4), '', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', 'aa)']) + normal ggV2jsd + call g:assert.equals(getline(1), '', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), 'aa', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #5') + + %delete + + " #6 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 1}] + call append(0, ['(', 'foo', ')']) + normal ggV2jsd + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #6') + unlet! g:operator#sandwich#recipes + + %delete + + call operator#sandwich#set('delete', 'line', 'linewise', 2) + + """ 2 + " #7 + call append(0, ['(', 'foo', ')']) + normal ggV2jsd + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['( ', 'foo', ' )']) + normal ggV2jsd + call g:assert.equals(getline(1), 'foo', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggV2jsd + call g:assert.equals(getline(1), 'foo', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['aa', '(foo)', 'bb']) + normal ggjVsd + call g:assert.equals(getline(1), 'aa', 'failed at #10') + call g:assert.equals(getline(2), 'bb', 'failed at #10') + call g:assert.equals(getline(3), '', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 2, 1, 0], 'failed at #10') + + %delete + + " #11 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggV2jsd + call g:assert.equals(getline(1), '', 'failed at #11') + call g:assert.equals(getline(2), 'foo', 'failed at #11') + call g:assert.equals(getline(3), '', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #11') + unlet! g:operator#sandwich#recipes +endfunction +"}}} + +" block-wise +function! s:suite.blockwise_n_default_recipes() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getline(2), 'bar', 'failed at #1') + call g:assert.equals(getline(3), 'baz', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['[foo]', '[bar]', '[baz]']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getline(2), 'bar', 'failed at #3') + call g:assert.equals(getline(3), 'baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['', '', '']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #4') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_not_registered() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['afooa', 'abara', 'abaza']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getline(2), 'bar', 'failed at #1') + call g:assert.equals(getline(3), 'baz', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #1') + + " #2 + call append(0, ['*foo*', '*bar*', '*baz*']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #2') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_positioning() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['(foo)bar', '(foo)bar', '(foo)bar']) + execute "normal ggsd\23l" + call g:assert.equals(getline(1), 'foobar', 'failed at #1') + call g:assert.equals(getline(2), 'foobar', 'failed at #1') + call g:assert.equals(getline(3), 'foobar', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo(bar)', 'foo(bar)', 'foo(bar)']) + execute "normal gg3lsd\23l" + call g:assert.equals(getline(1), 'foobar', 'failed at #2') + call g:assert.equals(getline(2), 'foobar', 'failed at #2') + call g:assert.equals(getline(3), 'foobar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 7, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['foo(bar)baz', 'foo(bar)baz', 'foo(bar)baz']) + execute "normal gg3lsd\29l" + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #3') + call g:assert.equals(getline(2), 'foobarbaz', 'failed at #3') + call g:assert.equals(getline(3), 'foobarbaz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 7, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(foo)', '(bar)', 'bazbaz']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'bazbaz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 2, 4, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(foo)', 'barbar', '(baz)']) + execute "normal ggsd\18l" + call g:assert.equals(getline(1), 'foo', 'failed at #5') + call g:assert.equals(getline(2), 'barbar', 'failed at #5') + call g:assert.equals(getline(3), 'baz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['foofoo', '(bar)', '(baz)']) + execute "normal ggsd\18l" + call g:assert.equals(getline(1), 'foofoo', 'failed at #6') + call g:assert.equals(getline(2), 'bar', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['(foo)', '(baar)', '(baaz)']) + execute "normal ggsd\20l" + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getline(2), 'baar', 'failed at #7') + call g:assert.equals(getline(3), 'baaz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['(fooo)', '(bar)', '(baaz)']) + execute "normal ggsd\20l" + call g:assert.equals(getline(1), 'fooo', 'failed at #8') + call g:assert.equals(getline(2), 'bar', 'failed at #8') + call g:assert.equals(getline(3), 'baaz', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #8') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_a_character() abort "{{{ + " #1 + call setline('.', '(a)') + execute "normal 0sd\a(" + call g:assert.equals(getline('.'), 'a', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #1') +endfunction +"}}} +function! s:suite.blockwise_n_nothing_inside() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['()', '()', '()']) + execute "normal ggsd\9l" + call g:assert.equals(getline(1), '', 'failed at #1') + call g:assert.equals(getline(2), '', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo()bar', 'foo()bar', 'foo()bar']) + execute "normal gg3lsd\20l" + call g:assert.equals(getline(1), 'foobar', 'failed at #2') + call g:assert.equals(getline(2), 'foobar', 'failed at #2') + call g:assert.equals(getline(3), 'foobar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #2') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_count() abort "{{{ + set whichwrap=h,l + + " #1 + call setline('.', '((foo))') + execute "normal 02sd\7l" + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '{[(foo)]}') + execute "normal 03sd\9l" + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + " #3 + call setline('.', '(foo)') + execute "normal 0sd\5l" + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #3') + + " #4 + call setline('.', '[(foo bar)]') + execute "normal 02sd\11l" + call g:assert.equals(getline('.'), 'foo bar', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 8, 0], 'failed at #4') + + " #5 + call setline('.', 'foo{[(bar)]}baz') + execute "normal 03l3sd\9l" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg3sd\29l" + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getline(2), 'bar', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['{[afoob]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg3sd\29l" + call g:assert.equals(getline(1), 'afoob', 'failed at #7') + call g:assert.equals(getline(2), 'bar', 'failed at #7') + call g:assert.equals(getline(3), 'baz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['{[(foo)]}', '{[abarb]}', '{[(baz)]}']) + execute "normal gg3sd\29l" + call g:assert.equals(getline(1), 'foo', 'failed at #8') + call g:assert.equals(getline(2), 'abarb', 'failed at #8') + call g:assert.equals(getline(3), 'baz', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[abazb]}']) + execute "normal gg3sd\29l" + call g:assert.equals(getline(1), 'foo', 'failed at #9') + call g:assert.equals(getline(2), 'bar', 'failed at #9') + call g:assert.equals(getline(3), 'abazb', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #9') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_external_textobj() abort "{{{ + set whichwrap=h,l + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg2lsd\25l" + call g:assert.equals(getline(1), '{[foo]}', 'failed at #1') + call g:assert.equals(getline(2), '{[bar]}', 'failed at #1') + call g:assert.equals(getline(3), '{[baz]}', 'failed at #1') + + %delete + + " #2 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gglsd\27l" + call g:assert.equals(getline(1), '{(foo)}', 'failed at #2') + call g:assert.equals(getline(2), '{(bar)}', 'failed at #2') + call g:assert.equals(getline(3), '{(baz)}', 'failed at #2') + + %delete + + " #3 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggsd\29l" + call g:assert.equals(getline(1), '[(foo)]', 'failed at #3') + call g:assert.equals(getline(2), '[(bar)]', 'failed at #3') + call g:assert.equals(getline(3), '[(baz)]', 'failed at #3') + + %delete + + " #4 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsd\56l" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + + %delete + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call append(0, ['(foo)', '(bar)', '(baz)']) + normal ggsd17l + call g:assert.equals(getline(1), '(foo)', 'failed at #5') + call g:assert.equals(getline(2), '(bar)', 'failed at #5') + call g:assert.equals(getline(3), '(baz)', 'failed at #5') + + set whichwrap& + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.blockwise_n_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + set whichwrap=h,l + + " #1 + call append(0, ['(α)', '(β)', '(γ)']) + execute "normal ggsd\11l" + call g:assert.equals(getline(1), 'α', 'failed at #1') + call g:assert.equals(getline(2), 'β', 'failed at #1') + call g:assert.equals(getline(3), 'γ', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, strlen('γ')+1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(aα)', '(bβ)', '(cγ)']) + execute "normal ggsd\14l" + call g:assert.equals(getline(1), 'aα', 'failed at #2') + call g:assert.equals(getline(2), 'bβ', 'failed at #2') + call g:assert.equals(getline(3), 'cγ', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, strlen('cγ')+1, 0], 'failed at #2') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call append(0, ['αaα', 'αbα', 'αcα']) + execute "normal ggsd\11l" + call g:assert.equals(getline(1), 'a', 'failed at #3') + call g:assert.equals(getline(2), 'b', 'failed at #3') + call g:assert.equals(getline(3), 'c', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['ααα', 'αβα', 'αγα']) + execute "normal ggsd\11l" + call g:assert.equals(getline(1), 'α', 'failed at #4') + call g:assert.equals(getline(2), 'β', 'failed at #4') + call g:assert.equals(getline(3), 'γ', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, strlen('γ')+1, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['αaαα', 'αbβα', 'αcγα']) + execute "normal ggsd\14l" + call g:assert.equals(getline(1), 'aα', 'failed at #5') + call g:assert.equals(getline(2), 'bβ', 'failed at #5') + call g:assert.equals(getline(3), 'cγ', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, strlen('cγ')+1, 0], 'failed at #5') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call append(0, ['aαaaα', 'aαbaα', 'aαcaα']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'a', 'failed at #6') + call g:assert.equals(getline(2), 'b', 'failed at #6') + call g:assert.equals(getline(3), 'c', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['aααaα', 'aαβaα', 'aαγaα']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'α', 'failed at #7') + call g:assert.equals(getline(2), 'β', 'failed at #7') + call g:assert.equals(getline(3), 'γ', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, strlen('γ')+1, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['aαaαaα', 'aαbβaα', 'aαcγaα']) + execute "normal ggsd\20l" + call g:assert.equals(getline(1), 'aα', 'failed at #8') + call g:assert.equals(getline(2), 'bβ', 'failed at #8') + call g:assert.equals(getline(3), 'cγ', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, strlen('cγ')+1, 0], 'failed at #8') + + %delete + unlet g:operator#sandwich#recipes + + " #9 + call append(0, ['(“)', '(“)', '(“)']) + execute "normal ggsd\11l" + call g:assert.equals(getline(1), '“', 'failed at #9') + call g:assert.equals(getline(2), '“', 'failed at #9') + call g:assert.equals(getline(3), '“', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['(a“)', '(b“)', '(c“)']) + execute "normal ggsd\14l" + call g:assert.equals(getline(1), 'a“', 'failed at #10') + call g:assert.equals(getline(2), 'b“', 'failed at #10') + call g:assert.equals(getline(3), 'c“', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, strlen('c“')+1, 0], 'failed at #10') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call append(0, ['“a“', '“b“', '“c“']) + execute "normal ggsd\11l" + call g:assert.equals(getline(1), 'a', 'failed at #11') + call g:assert.equals(getline(2), 'b', 'failed at #11') + call g:assert.equals(getline(3), 'c', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #11') + + %delete + + " #12 + call append(0, ['“““', '“““', '“““']) + execute "normal ggsd\11l" + call g:assert.equals(getline(1), '“', 'failed at #12') + call g:assert.equals(getline(2), '“', 'failed at #12') + call g:assert.equals(getline(3), '“', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #12') + + %delete + + " #13 + call append(0, ['“a““', '“b““', '“c““']) + execute "normal ggsd\14l" + call g:assert.equals(getline(1), 'a“', 'failed at #13') + call g:assert.equals(getline(2), 'b“', 'failed at #13') + call g:assert.equals(getline(3), 'c“', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, strlen('c“')+1, 0], 'failed at #13') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call append(0, ['a“aa“', 'a“ba“', 'a“ca“']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'a', 'failed at #14') + call g:assert.equals(getline(2), 'b', 'failed at #14') + call g:assert.equals(getline(3), 'c', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #14') + + %delete + + " #15 + call append(0, ['a““a“', 'a““a“', 'a““a“']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), '“', 'failed at #15') + call g:assert.equals(getline(2), '“', 'failed at #15') + call g:assert.equals(getline(3), '“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #15') + + %delete + + " #16 + call append(0, ['a“a“a“', 'a“b“a“', 'a“c“a“']) + execute "normal ggsd\20l" + call g:assert.equals(getline(1), 'a“', 'failed at #16') + call g:assert.equals(getline(2), 'b“', 'failed at #16') + call g:assert.equals(getline(3), 'c“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, strlen('c“')+1, 0], 'failed at #16') +endfunction +"}}} +function! s:suite.blockwise_n_option_cursor() abort "{{{ + set whichwrap=h,l + + """"" cursor + """ default + " #1 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl2sd\27l" + call g:assert.equals(getline(1), '{foo}', 'failed at #1') + call g:assert.equals(getline(2), '{bar}', 'failed at #1') + call g:assert.equals(getline(3), '{baz}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + + " #2 + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['( foo)', '( bar)', '( baz)']) + execute "normal ggsd\29l" + call g:assert.equals(getline(1), ' foo', 'failed at #3') + call g:assert.equals(getline(2), ' bar', 'failed at #3') + call g:assert.equals(getline(3), ' baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + + %delete + + """ inner_head + " #4 + call operator#sandwich#set('delete', 'block', 'cursor', 'inner_head') + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl2sd\27l" + call g:assert.equals(getline(1), '{foo}', 'failed at #4') + call g:assert.equals(getline(2), '{bar}', 'failed at #4') + call g:assert.equals(getline(3), '{baz}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + + " #5 + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #5') + call g:assert.equals(getline(2), 'bar', 'failed at #5') + call g:assert.equals(getline(3), 'baz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + + %delete + + """ keep + " #6 + call operator#sandwich#set('delete', 'block', 'cursor', 'keep') + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl2sd\27l" + call g:assert.equals(getline(1), '{foo}', 'failed at #6') + call g:assert.equals(getline(2), '{bar}', 'failed at #6') + call g:assert.equals(getline(3), '{baz}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #6') + + " #7 + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getline(2), 'bar', 'failed at #7') + call g:assert.equals(getline(3), 'baz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + + %delete + + """ inner_tail + " #8 + call operator#sandwich#set('delete', 'block', 'cursor', 'inner_tail') + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl2sd\27l" + call g:assert.equals(getline(1), '{foo}', 'failed at #8') + call g:assert.equals(getline(2), '{bar}', 'failed at #8') + call g:assert.equals(getline(3), '{baz}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 4, 0], 'failed at #8') + + " #9 + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #9') + call g:assert.equals(getline(2), 'bar', 'failed at #9') + call g:assert.equals(getline(3), 'baz', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #9') + + %delete + + """ head + " #10 + call operator#sandwich#set('delete', 'block', 'cursor', 'head') + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl2sd\27l" + call g:assert.equals(getline(1), '{foo}', 'failed at #10') + call g:assert.equals(getline(2), '{bar}', 'failed at #10') + call g:assert.equals(getline(3), '{baz}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + + " #11 + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #11') + call g:assert.equals(getline(2), 'bar', 'failed at #11') + call g:assert.equals(getline(3), 'baz', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + + %delete + + """ tail + " #12 + call operator#sandwich#set('delete', 'block', 'cursor', 'tail') + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl2sd\27l" + call g:assert.equals(getline(1), '{foo}', 'failed at #12') + call g:assert.equals(getline(2), '{bar}', 'failed at #12') + call g:assert.equals(getline(3), '{baz}', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 4, 0], 'failed at #12') + + " #13 + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #13') + call g:assert.equals(getline(2), 'bar', 'failed at #13') + call g:assert.equals(getline(3), 'baz', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head'}] + call operator#sandwich#set('delete', 'line', 'cursor', 'inner_tail') + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #14') + call g:assert.equals(getline(2), 'bar', 'failed at #14') + call g:assert.equals(getline(3), 'baz', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_option_noremap() abort "{{{ + set whichwrap=h,l + + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal ggsd\23l" + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + call g:assert.equals(getline(2), '(bar)', 'failed at #1') + call g:assert.equals(getline(3), '(baz)', 'failed at #1') + + %delete + + " #2 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal gglsd\21l" + call g:assert.equals(getline(1), '{(foo)}', 'failed at #2') + call g:assert.equals(getline(2), '{(bar)}', 'failed at #2') + call g:assert.equals(getline(3), '{(baz)}', 'failed at #2') + + %delete + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal ggsd\23l" + call g:assert.equals(getline(1), '{(foo)}', 'failed at #3') + call g:assert.equals(getline(2), '{(bar)}', 'failed at #3') + call g:assert.equals(getline(3), '{(baz)}', 'failed at #3') + + %delete + + " #4 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal gglsd\21l" + call g:assert.equals(getline(1), '{foo}', 'failed at #4') + call g:assert.equals(getline(2), '{bar}', 'failed at #4') + call g:assert.equals(getline(3), '{baz}', 'failed at #4') + + %delete + + """ off + call operator#sandwich#set('delete', 'block', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal ggsd\23l" + call g:assert.equals(getline(1), '{(foo)}', 'failed at #5') + call g:assert.equals(getline(2), '{(bar)}', 'failed at #5') + call g:assert.equals(getline(3), '{(baz)}', 'failed at #5') + + %delete + + " #6 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal gglsd\21l" + call g:assert.equals(getline(1), '{foo}', 'failed at #6') + call g:assert.equals(getline(2), '{bar}', 'failed at #6') + call g:assert.equals(getline(3), '{baz}', 'failed at #6') + + %delete + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal ggsd\23l" + call g:assert.equals(getline(1), '(foo)', 'failed at #7') + call g:assert.equals(getline(2), '(bar)', 'failed at #7') + call g:assert.equals(getline(3), '(baz)', 'failed at #7') + + %delete + + " #8 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal gglsd\21l" + call g:assert.equals(getline(1), '{(foo)}', 'failed at #8') + call g:assert.equals(getline(2), '{(bar)}', 'failed at #8') + call g:assert.equals(getline(3), '{(baz)}', 'failed at #8') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_option_regex() abort "{{{ + set whichwrap=h,l + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal ggsd\36l" + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getline(2), 'bar', 'failed at #1') + call g:assert.equals(getline(3), 'baz', 'failed at #1') + + %delete + + " #2 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal ggsd\29l" + call g:assert.equals(getline(1), '88foo88', 'failed at #2') + call g:assert.equals(getline(2), '88bar88', 'failed at #2') + call g:assert.equals(getline(3), '88baz88', 'failed at #2') + + %delete + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal ggsd\36l" + call g:assert.equals(getline(1), '\d\+foo\d\+', 'failed at #3') + call g:assert.equals(getline(2), '\d\+bar\d\+', 'failed at #3') + call g:assert.equals(getline(3), '\d\+baz\d\+', 'failed at #3') + + %delete + + " #4 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal ggsd\29l" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + + %delete + + """ on + call operator#sandwich#set('delete', 'block', 'regex', 1) + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #5 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal ggsd\36l" + call g:assert.equals(getline(1), '\d\+foo\d\+', 'failed at #5') + call g:assert.equals(getline(2), '\d\+bar\d\+', 'failed at #5') + call g:assert.equals(getline(3), '\d\+baz\d\+', 'failed at #5') + + %delete + + " #6 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal ggsd\29l" + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getline(2), 'bar', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + + %delete + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal ggsd\36l" + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getline(2), 'bar', 'failed at #7') + call g:assert.equals(getline(3), 'baz', 'failed at #7') + + %delete + + " #8 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal ggsd\29l" + call g:assert.equals(getline(1), '88foo88', 'failed at #8') + call g:assert.equals(getline(2), '88bar88', 'failed at #8') + call g:assert.equals(getline(3), '88baz88', 'failed at #8') +endfunction +"}}} +function! s:suite.blockwise_n_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + set whichwrap=h,l + + """ 1 + " #1 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0sd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getline(2), 'bar', 'failed at #1') + call g:assert.equals(getline(3), 'baz', 'failed at #1') + + %delete + + " #2 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sd\20l" + call g:assert.equals(getline(1), ' foo', 'failed at #2') + call g:assert.equals(getline(2), ' bar', 'failed at #2') + call g:assert.equals(getline(3), ' baz', 'failed at #2') + + %delete + + " #3 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0sd\20l" + call g:assert.equals(getline(1), 'foo ', 'failed at #3') + call g:assert.equals(getline(2), 'bar ', 'failed at #3') + call g:assert.equals(getline(3), 'baz ', 'failed at #3') + + %delete + + " #4 + " do not skip! + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0sd\23l" + call g:assert.equals(getline(1), '"foo"', 'failed at #4') + call g:assert.equals(getline(2), '"bar"', 'failed at #4') + call g:assert.equals(getline(3), '"baz"', 'failed at #4') + + %delete + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sd\20l" + call g:assert.equals(getline(1), ' "foo"', 'failed at #5') + call g:assert.equals(getline(2), ' "bar"', 'failed at #5') + call g:assert.equals(getline(3), ' "baz"', 'failed at #5') + + %delete + + """ 2 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'block', 'skip_space', 2) + + " #6 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0sd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getline(2), 'bar', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + + %delete + + " #7 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sd\20l" + call g:assert.equals(getline(1), ' foo', 'failed at #7') + call g:assert.equals(getline(2), ' bar', 'failed at #7') + call g:assert.equals(getline(3), ' baz', 'failed at #7') + + %delete + + " #8 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0sd\20l" + call g:assert.equals(getline(1), 'foo ', 'failed at #8') + call g:assert.equals(getline(2), 'bar ', 'failed at #8') + call g:assert.equals(getline(3), 'baz ', 'failed at #8') + + %delete + + " #9 + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0sd\23l" + call g:assert.equals(getline(1), ' foo ', 'failed at #9') + call g:assert.equals(getline(2), ' bar ', 'failed at #9') + call g:assert.equals(getline(3), ' baz ', 'failed at #9') + + %delete + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sd\20l" + call g:assert.equals(getline(1), ' "foo"', 'failed at #10') + call g:assert.equals(getline(2), ' "bar"', 'failed at #10') + call g:assert.equals(getline(3), ' "baz"', 'failed at #10') + + %delete + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'block', 'skip_space', 0) + + " #11 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0sd\17l" + call g:assert.equals(getline(1), 'foo', 'failed at #11') + call g:assert.equals(getline(2), 'bar', 'failed at #11') + call g:assert.equals(getline(3), 'baz', 'failed at #11') + + %delete + + " #12 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sd\20l" + call g:assert.equals(getline(1), ' "foo"', 'failed at #12') + call g:assert.equals(getline(2), ' "bar"', 'failed at #12') + call g:assert.equals(getline(3), ' "baz"', 'failed at #12') + + %delete + + " #13 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0sd\20l" + call g:assert.equals(getline(1), '"foo" ', 'failed at #13') + call g:assert.equals(getline(2), '"bar" ', 'failed at #13') + call g:assert.equals(getline(3), '"baz" ', 'failed at #13') + + %delete + + " #14 + " do not skip! + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0sd\23l" + call g:assert.equals(getline(1), '"foo"', 'failed at #14') + call g:assert.equals(getline(2), '"bar"', 'failed at #14') + call g:assert.equals(getline(3), '"baz"', 'failed at #14') + + %delete + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sd\20l" + call g:assert.equals(getline(1), ' foo', 'failed at #15') + call g:assert.equals(getline(2), ' bar', 'failed at #15') + call g:assert.equals(getline(3), ' baz', 'failed at #15') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_option_skip_char() abort "{{{ + set whichwrap=h,l + + """ off + " #1 + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal ggsd\29l" + call g:assert.equals(getline(1), 'aa(foo)bb', 'failed at #1') + call g:assert.equals(getline(2), 'aa(bar)bb', 'failed at #1') + call g:assert.equals(getline(3), 'aa(baz)bb', 'failed at #1') + + %delete + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal ggsd\29l" + call g:assert.equals(getline(1), 'aafoobb', 'failed at #2') + call g:assert.equals(getline(2), 'aabarbb', 'failed at #2') + call g:assert.equals(getline(3), 'aabazbb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + %delete + + """ on + call operator#sandwich#set('delete', 'block', 'skip_char', 1) + + " #3 + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal ggsd\29l" + call g:assert.equals(getline(1), 'aafoobb', 'failed at #3') + call g:assert.equals(getline(2), 'aabarbb', 'failed at #3') + call g:assert.equals(getline(3), 'aabazbb', 'failed at #3') + + %delete + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal ggsd\29l" + call g:assert.equals(getline(1), 'aa(foo)bb', 'failed at #4') + call g:assert.equals(getline(2), 'aa(bar)bb', 'failed at #4') + call g:assert.equals(getline(3), 'aa(baz)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_option_command() abort "{{{ + set whichwrap=h,l + call operator#sandwich#set('delete', 'block', 'command', ['normal! `[d`]']) + + " #1 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), '', 'failed at #1') + call g:assert.equals(getline(2), '', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + + %delete + + " #2 + call operator#sandwich#set('delete', 'block', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsd\17l" + call g:assert.equals(getline(1), '', 'failed at #2') + call g:assert.equals(getline(2), '', 'failed at #2') + call g:assert.equals(getline(3), '', 'failed at #2') +endfunction +"}}} + +function! s:suite.blockwise_x_default_recipes() abort "{{{ + " #1 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getline(2), 'bar', 'failed at #1') + call g:assert.equals(getline(3), 'baz', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['[foo]', '[bar]', '[baz]']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getline(2), 'bar', 'failed at #3') + call g:assert.equals(getline(3), 'baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['', '', '']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.blockwise_x_not_registered() abort "{{{ + " #1 + call append(0, ['afooa', 'abara', 'abaza']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getline(2), 'bar', 'failed at #1') + call g:assert.equals(getline(3), 'baz', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #1') + + " #2 + call append(0, ['*foo*', '*bar*', '*baz*']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_x_positioning() abort "{{{ + " #1 + call append(0, ['(foo)bar', '(foo)bar', '(foo)bar']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foobar', 'failed at #1') + call g:assert.equals(getline(2), 'foobar', 'failed at #1') + call g:assert.equals(getline(3), 'foobar', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo(bar)', 'foo(bar)', 'foo(bar)']) + execute "normal gg3l\2j4lsd" + call g:assert.equals(getline(1), 'foobar', 'failed at #2') + call g:assert.equals(getline(2), 'foobar', 'failed at #2') + call g:assert.equals(getline(3), 'foobar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 7, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['foo(bar)baz', 'foo(bar)baz', 'foo(bar)baz']) + execute "normal gg3l\2j4lsd" + call g:assert.equals(getline(1), 'foobarbaz', 'failed at #3') + call g:assert.equals(getline(2), 'foobarbaz', 'failed at #3') + call g:assert.equals(getline(3), 'foobarbaz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 7, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(foo)', '(bar)', 'bazbaz']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'bazbaz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 2, 4, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(foo)', 'barbar', '(baz)']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #5') + call g:assert.equals(getline(2), 'barbar', 'failed at #5') + call g:assert.equals(getline(3), 'baz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['foofoo', '(bar)', '(baz)']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foofoo', 'failed at #6') + call g:assert.equals(getline(2), 'bar', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['(foo)', '(baar)', '(baaz)']) + execute "normal gg\2j5lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getline(2), 'baar', 'failed at #7') + call g:assert.equals(getline(3), 'baaz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['(fooo)', '(bar)', '(baaz)']) + execute "normal gg\2j5lsd" + call g:assert.equals(getline(1), 'fooo', 'failed at #8') + call g:assert.equals(getline(2), 'bar', 'failed at #8') + call g:assert.equals(getline(3), 'baaz', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['(fooo)', '(baar)', '(baz)']) + set virtualedit=block + execute "normal gg\2j5lsd" + call g:assert.equals(getline(1), 'fooo', 'failed at #9') + call g:assert.equals(getline(2), 'baar', 'failed at #9') + call g:assert.equals(getline(3), 'baz', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #9') + set virtualedit& + + %delete + + """ terminal-extended block-wise visual mode + " #10 + call append(0, ['(fooo)', '(baaar)', '(baz)']) + execute "normal gg\2j$sd" + call g:assert.equals(getline(1), 'fooo', 'failed at #10') + call g:assert.equals(getline(2), 'baaar', 'failed at #10') + call g:assert.equals(getline(3), 'baz', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #10') + + %delete + + " #11 + call append(0, ['(foooo)', '(bar)', '(baaz)']) + execute "normal gg\2j$sd" + call g:assert.equals(getline(1), 'foooo', 'failed at #11') + call g:assert.equals(getline(2), 'bar', 'failed at #11') + call g:assert.equals(getline(3), 'baaz', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #11') + + %delete + + " #12 + call append(0, ['(fooo)', '', '(baz)']) + execute "normal gg\2j$sd" + call g:assert.equals(getline(1), 'fooo', 'failed at #12') + call g:assert.equals(getline(2), '', 'failed at #12') + call g:assert.equals(getline(3), 'baz', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #12') +endfunction +"}}} +function! s:suite.blockwise_x_a_character() abort "{{{ + " #1 + call setline('.', '(a)') + execute "normal 0\2lsd" + call g:assert.equals(getline('.'), 'a', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 2, 0], 'failed at #1') +endfunction +"}}} +function! s:suite.blockwise_x_nothing_inside() abort "{{{ + " #1 + call append(0, ['()', '()', '()']) + execute "normal gg\2jlsd" + call g:assert.equals(getline(1), '', 'failed at #1') + call g:assert.equals(getline(2), '', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo()bar', 'foo()bar', 'foo()bar']) + execute "normal gg3l\2jlsd" + call g:assert.equals(getline(1), 'foobar', 'failed at #2') + call g:assert.equals(getline(2), 'foobar', 'failed at #2') + call g:assert.equals(getline(3), 'foobar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_x_count() abort "{{{ + " #1 + call setline('.', '((foo))') + execute "normal 0\6l2sd" + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + " #2 + call setline('.', '{[(foo)]}') + execute "normal 0\8l3sd" + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #2') + + " #3 + call setline('.', 'foo{[(bar)]}baz') + execute "normal 03l\8l3sd" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg\2j8l3sd" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['{[afoob]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg\2j8l3sd" + call g:assert.equals(getline(1), 'afoob', 'failed at #5') + call g:assert.equals(getline(2), 'bar', 'failed at #5') + call g:assert.equals(getline(3), 'baz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['{[(foo)]}', '{[abarb]}', '{[(baz)]}']) + execute "normal gg\2j8l3sd" + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getline(2), 'abarb', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[abazb]}']) + execute "normal gg\2j8l3sd" + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getline(2), 'bar', 'failed at #7') + call g:assert.equals(getline(3), 'abazb', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') +endfunction +"}}} +function! s:suite.blockwise_x_external_textobj() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg2l\2j4lsd" + call g:assert.equals(getline(1), '{[foo]}', 'failed at #1') + call g:assert.equals(getline(2), '{[bar]}', 'failed at #1') + call g:assert.equals(getline(3), '{[baz]}', 'failed at #1') + + %delete + + " #2 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl\2j6lsd" + call g:assert.equals(getline(1), '{(foo)}', 'failed at #2') + call g:assert.equals(getline(2), '{(bar)}', 'failed at #2') + call g:assert.equals(getline(3), '{(baz)}', 'failed at #2') + + %delete + + " #3 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg\2j8lsd" + call g:assert.equals(getline(1), '[(foo)]', 'failed at #3') + call g:assert.equals(getline(2), '[(bar)]', 'failed at #3') + call g:assert.equals(getline(3), '[(baz)]', 'failed at #3') + + %delete + + " #4 + call setline('.', 'foo') + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j17lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + + %delete + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), '(foo)', 'failed at #5') + call g:assert.equals(getline(2), '(bar)', 'failed at #5') + call g:assert.equals(getline(3), '(baz)', 'failed at #5') + + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.blockwise_x_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + set whichwrap=h,l + + " #1 + call append(0, ['(α)', '(β)', '(γ)']) + execute "normal gg\2l2jsd" + call g:assert.equals(getline(1), 'α', 'failed at #1') + call g:assert.equals(getline(2), 'β', 'failed at #1') + call g:assert.equals(getline(3), 'γ', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, strlen('γ')+1, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(aα)', '(bβ)', '(cγ)']) + execute "normal gg\3l2jsd" + call g:assert.equals(getline(1), 'aα', 'failed at #2') + call g:assert.equals(getline(2), 'bβ', 'failed at #2') + call g:assert.equals(getline(3), 'cγ', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, strlen('cγ')+1, 0], 'failed at #2') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #3 + call append(0, ['αaα', 'αbα', 'αcα']) + execute "normal gg\2l2jsd" + call g:assert.equals(getline(1), 'a', 'failed at #3') + call g:assert.equals(getline(2), 'b', 'failed at #3') + call g:assert.equals(getline(3), 'c', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['ααα', 'αβα', 'αγα']) + execute "normal gg\2l2jsd" + call g:assert.equals(getline(1), 'α', 'failed at #4') + call g:assert.equals(getline(2), 'β', 'failed at #4') + call g:assert.equals(getline(3), 'γ', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, strlen('γ')+1, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['αaαα', 'αbβα', 'αcγα']) + execute "normal gg\3l2jsd" + call g:assert.equals(getline(1), 'aα', 'failed at #5') + call g:assert.equals(getline(2), 'bβ', 'failed at #5') + call g:assert.equals(getline(3), 'cγ', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, strlen('cγ')+1, 0], 'failed at #5') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #6 + call append(0, ['aαaaα', 'aαbaα', 'aαcaα']) + execute "normal gg\4l2jsd" + call g:assert.equals(getline(1), 'a', 'failed at #6') + call g:assert.equals(getline(2), 'b', 'failed at #6') + call g:assert.equals(getline(3), 'c', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['aααaα', 'aαβaα', 'aαγaα']) + execute "normal gg\4l2jsd" + call g:assert.equals(getline(1), 'α', 'failed at #7') + call g:assert.equals(getline(2), 'β', 'failed at #7') + call g:assert.equals(getline(3), 'γ', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, strlen('γ')+1, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['aαaαaα', 'aαbβaα', 'aαcγaα']) + execute "normal gg\5l2jsd" + call g:assert.equals(getline(1), 'aα', 'failed at #8') + call g:assert.equals(getline(2), 'bβ', 'failed at #8') + call g:assert.equals(getline(3), 'cγ', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, strlen('cγ')+1, 0], 'failed at #8') + + %delete + unlet g:operator#sandwich#recipes + + " #9 + call append(0, ['(“)', '(“)', '(“)']) + execute "normal gg\2l2jsd" + call g:assert.equals(getline(1), '“', 'failed at #9') + call g:assert.equals(getline(2), '“', 'failed at #9') + call g:assert.equals(getline(3), '“', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['(a“)', '(b“)', '(c“)']) + execute "normal gg\3l2jsd" + call g:assert.equals(getline(1), 'a“', 'failed at #10') + call g:assert.equals(getline(2), 'b“', 'failed at #10') + call g:assert.equals(getline(3), 'c“', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, strlen('c“')+1, 0], 'failed at #10') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #11 + call append(0, ['“a“', '“b“', '“c“']) + execute "normal gg\2l2jsd" + call g:assert.equals(getline(1), 'a', 'failed at #11') + call g:assert.equals(getline(2), 'b', 'failed at #11') + call g:assert.equals(getline(3), 'c', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #11') + + %delete + + " #12 + call append(0, ['“““', '“““', '“““']) + execute "normal gg\2l2jsd" + call g:assert.equals(getline(1), '“', 'failed at #12') + call g:assert.equals(getline(2), '“', 'failed at #12') + call g:assert.equals(getline(3), '“', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #12') + + %delete + + " #13 + call append(0, ['“a““', '“b““', '“c““']) + execute "normal gg\3l2jsd" + call g:assert.equals(getline(1), 'a“', 'failed at #13') + call g:assert.equals(getline(2), 'b“', 'failed at #13') + call g:assert.equals(getline(3), 'c“', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, strlen('c“')+1, 0], 'failed at #13') + + %delete + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #14 + call append(0, ['a“aa“', 'a“ba“', 'a“ca“']) + execute "normal gg\4l2jsd" + call g:assert.equals(getline(1), 'a', 'failed at #14') + call g:assert.equals(getline(2), 'b', 'failed at #14') + call g:assert.equals(getline(3), 'c', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #14') + + %delete + + " #15 + call append(0, ['a““a“', 'a““a“', 'a““a“']) + execute "normal gg\4l2jsd" + call g:assert.equals(getline(1), '“', 'failed at #15') + call g:assert.equals(getline(2), '“', 'failed at #15') + call g:assert.equals(getline(3), '“', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #15') + + %delete + + " #16 + call append(0, ['a“a“a“', 'a“b“a“', 'a“c“a“']) + execute "normal gg\5l2jsd" + call g:assert.equals(getline(1), 'a“', 'failed at #16') + call g:assert.equals(getline(2), 'b“', 'failed at #16') + call g:assert.equals(getline(3), 'c“', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, strlen('c“')+1, 0], 'failed at #16') +endfunction +"}}} +function! s:suite.blockwise_x_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl\2j6l2sd" + call g:assert.equals(getline(1), '{foo}', 'failed at #1') + call g:assert.equals(getline(2), '{bar}', 'failed at #1') + call g:assert.equals(getline(3), '{baz}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + + " #2 + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['( foo)', '( bar)', '( baz)']) + execute "normal gg\2j8lsd" + call g:assert.equals(getline(1), ' foo', 'failed at #3') + call g:assert.equals(getline(2), ' bar', 'failed at #3') + call g:assert.equals(getline(3), ' baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + + %delete + + """ inner_head + " #1 + call operator#sandwich#set('delete', 'block', 'cursor', 'inner_head') + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl\2j6l2sd" + call g:assert.equals(getline(1), '{foo}', 'failed at #1') + call g:assert.equals(getline(2), '{bar}', 'failed at #1') + call g:assert.equals(getline(3), '{baz}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + + " #2 + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), 'bar', 'failed at #2') + call g:assert.equals(getline(3), 'baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + + %delete + + """ keep + " #3 + call operator#sandwich#set('delete', 'block', 'cursor', 'keep') + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl\2j6l2sd" + call g:assert.equals(getline(1), '{foo}', 'failed at #3') + call g:assert.equals(getline(2), '{bar}', 'failed at #3') + call g:assert.equals(getline(3), '{baz}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 3, 4, 0], 'failed at #3') + + " #4 + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #4') + + %delete + + """ inner_tail + " #5 + call operator#sandwich#set('delete', 'block', 'cursor', 'inner_tail') + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl\2j6l2sd" + call g:assert.equals(getline(1), '{foo}', 'failed at #5') + call g:assert.equals(getline(2), '{bar}', 'failed at #5') + call g:assert.equals(getline(3), '{baz}', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 3, 4, 0], 'failed at #5') + + " #6 + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getline(2), 'bar', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #6') + + %delete + + """ head + " #7 + call operator#sandwich#set('delete', 'block', 'cursor', 'head') + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl\2j6l2sd" + call g:assert.equals(getline(1), '{foo}', 'failed at #7') + call g:assert.equals(getline(2), '{bar}', 'failed at #7') + call g:assert.equals(getline(3), '{baz}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + + " #8 + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #8') + call g:assert.equals(getline(2), 'bar', 'failed at #8') + call g:assert.equals(getline(3), 'baz', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + + %delete + + """ tail + " #9 + call operator#sandwich#set('delete', 'block', 'cursor', 'tail') + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal ggl\2j6l2sd" + call g:assert.equals(getline(1), '{foo}', 'failed at #9') + call g:assert.equals(getline(2), '{bar}', 'failed at #9') + call g:assert.equals(getline(3), '{baz}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 3, 4, 0], 'failed at #9') + + " #10 + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #10') + call g:assert.equals(getline(2), 'bar', 'failed at #10') + call g:assert.equals(getline(3), 'baz', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #10') + + """"" recipe option + " #11 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head'}] + call operator#sandwich#set('delete', 'line', 'cursor', 'inner_tail') + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #11') + call g:assert.equals(getline(2), 'bar', 'failed at #11') + call g:assert.equals(getline(3), 'baz', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') +endfunction +"}}} +function! s:suite.blockwise_x_option_noremap() abort "{{{ + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal gg\2j6lsd" + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + call g:assert.equals(getline(2), '(bar)', 'failed at #1') + call g:assert.equals(getline(3), '(baz)', 'failed at #1') + + %delete + + " #2 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal ggl\2j4lsd" + call g:assert.equals(getline(1), '{(foo)}', 'failed at #2') + call g:assert.equals(getline(2), '{(bar)}', 'failed at #2') + call g:assert.equals(getline(3), '{(baz)}', 'failed at #2') + + %delete + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal gg\2j6lsd" + call g:assert.equals(getline(1), '{(foo)}', 'failed at #3') + call g:assert.equals(getline(2), '{(bar)}', 'failed at #3') + call g:assert.equals(getline(3), '{(baz)}', 'failed at #3') + + %delete + + " #4 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal ggl\2j4lsd" + call g:assert.equals(getline(1), '{foo}', 'failed at #4') + call g:assert.equals(getline(2), '{bar}', 'failed at #4') + call g:assert.equals(getline(3), '{baz}', 'failed at #4') + + %delete + + """ off + call operator#sandwich#set('delete', 'block', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal gg\2j6lsd" + call g:assert.equals(getline(1), '{(foo)}', 'failed at #5') + call g:assert.equals(getline(2), '{(bar)}', 'failed at #5') + call g:assert.equals(getline(3), '{(baz)}', 'failed at #5') + + %delete + + " #6 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal ggl\2j4lsd" + call g:assert.equals(getline(1), '{foo}', 'failed at #6') + call g:assert.equals(getline(2), '{bar}', 'failed at #6') + call g:assert.equals(getline(3), '{baz}', 'failed at #6') + + %delete + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal gg\2j6lsd" + call g:assert.equals(getline(1), '(foo)', 'failed at #7') + call g:assert.equals(getline(2), '(bar)', 'failed at #7') + call g:assert.equals(getline(3), '(baz)', 'failed at #7') + + %delete + + " #8 + call append(0, ['{(foo)}', '{(bar)}', '{(baz)}']) + execute "normal ggl\2j4lsd" + call g:assert.equals(getline(1), '{(foo)}', 'failed at #8') + call g:assert.equals(getline(2), '{(bar)}', 'failed at #8') + call g:assert.equals(getline(3), '{(baz)}', 'failed at #8') +endfunction +"}}} +function! s:suite.blockwise_x_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal gg\2j10lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getline(2), 'bar', 'failed at #1') + call g:assert.equals(getline(3), 'baz', 'failed at #1') + + %delete + + " #2 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal gg\2j8lsd" + call g:assert.equals(getline(1), '88foo88', 'failed at #2') + call g:assert.equals(getline(2), '88bar88', 'failed at #2') + call g:assert.equals(getline(3), '88baz88', 'failed at #2') + + %delete + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal gg\2j10lsd" + call g:assert.equals(getline(1), '\d\+foo\d\+', 'failed at #3') + call g:assert.equals(getline(2), '\d\+bar\d\+', 'failed at #3') + call g:assert.equals(getline(3), '\d\+baz\d\+', 'failed at #3') + + %delete + + " #4 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal gg\2j8lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #4') + call g:assert.equals(getline(2), 'bar', 'failed at #4') + call g:assert.equals(getline(3), 'baz', 'failed at #4') + + %delete + + """ on + call operator#sandwich#set('delete', 'block', 'regex', 1) + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #5 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal gg\2j10lsd" + call g:assert.equals(getline(1), '\d\+foo\d\+', 'failed at #5') + call g:assert.equals(getline(2), '\d\+bar\d\+', 'failed at #5') + call g:assert.equals(getline(3), '\d\+baz\d\+', 'failed at #5') + + %delete + + " #6 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal gg\2j8lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getline(2), 'bar', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + + %delete + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal gg\2j10lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #7') + call g:assert.equals(getline(2), 'bar', 'failed at #7') + call g:assert.equals(getline(3), 'baz', 'failed at #7') + + %delete + + " #8 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal gg\2j8lsd" + call g:assert.equals(getline(1), '88foo88', 'failed at #8') + call g:assert.equals(getline(2), '88bar88', 'failed at #8') + call g:assert.equals(getline(3), '88baz88', 'failed at #8') +endfunction +"}}} +function! s:suite.blockwise_x_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + + """ 1 + " #1 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #1') + call g:assert.equals(getline(2), 'bar', 'failed at #1') + call g:assert.equals(getline(3), 'baz', 'failed at #1') + + %delete + + " #2 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j5lsd" + call g:assert.equals(getline(1), ' foo', 'failed at #2') + call g:assert.equals(getline(2), ' bar', 'failed at #2') + call g:assert.equals(getline(3), ' baz', 'failed at #2') + + %delete + + " #3 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0\2j5lsd" + call g:assert.equals(getline(1), 'foo ', 'failed at #3') + call g:assert.equals(getline(2), 'bar ', 'failed at #3') + call g:assert.equals(getline(3), 'baz ', 'failed at #3') + + %delete + + " #4 + " do not skip! + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0\2j6lsd" + call g:assert.equals(getline(1), '"foo"', 'failed at #4') + call g:assert.equals(getline(2), '"bar"', 'failed at #4') + call g:assert.equals(getline(3), '"baz"', 'failed at #4') + + %delete + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j5lsd" + call g:assert.equals(getline(1), ' "foo"', 'failed at #5') + call g:assert.equals(getline(2), ' "bar"', 'failed at #5') + call g:assert.equals(getline(3), ' "baz"', 'failed at #5') + + %delete + + """ 2 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'block', 'skip_space', 2) + + " #6 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #6') + call g:assert.equals(getline(2), 'bar', 'failed at #6') + call g:assert.equals(getline(3), 'baz', 'failed at #6') + + %delete + + " #7 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j5lsd" + call g:assert.equals(getline(1), ' foo', 'failed at #7') + call g:assert.equals(getline(2), ' bar', 'failed at #7') + call g:assert.equals(getline(3), ' baz', 'failed at #7') + + %delete + + " #8 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0\2j5lsd" + call g:assert.equals(getline(1), 'foo ', 'failed at #8') + call g:assert.equals(getline(2), 'bar ', 'failed at #8') + call g:assert.equals(getline(3), 'baz ', 'failed at #8') + + %delete + + " #9 + " do not skip! + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0\2j6lsd" + call g:assert.equals(getline(1), ' foo ', 'failed at #9') + call g:assert.equals(getline(2), ' bar ', 'failed at #9') + call g:assert.equals(getline(3), ' baz ', 'failed at #9') + + %delete + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j5lsd" + call g:assert.equals(getline(1), ' "foo"', 'failed at #10') + call g:assert.equals(getline(2), ' "bar"', 'failed at #10') + call g:assert.equals(getline(3), ' "baz"', 'failed at #10') + + %delete + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}] + call operator#sandwich#set('delete', 'block', 'skip_space', 0) + + " #11 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0\2j4lsd" + call g:assert.equals(getline(1), 'foo', 'failed at #11') + call g:assert.equals(getline(2), 'bar', 'failed at #11') + call g:assert.equals(getline(3), 'baz', 'failed at #11') + + %delete + + " #12 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j5lsd" + call g:assert.equals(getline(1), ' "foo"', 'failed at #12') + call g:assert.equals(getline(2), ' "bar"', 'failed at #12') + call g:assert.equals(getline(3), ' "baz"', 'failed at #12') + + %delete + + " #13 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0\2j5lsd" + call g:assert.equals(getline(1), '"foo" ', 'failed at #13') + call g:assert.equals(getline(2), '"bar" ', 'failed at #13') + call g:assert.equals(getline(3), '"baz" ', 'failed at #13') + + %delete + + " #14 + " do not skip! + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0\2j6lsd" + call g:assert.equals(getline(1), '"foo"', 'failed at #14') + call g:assert.equals(getline(2), '"bar"', 'failed at #14') + call g:assert.equals(getline(3), '"baz"', 'failed at #14') + + %delete + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j5lsd" + call g:assert.equals(getline(1), ' foo', 'failed at #15') + call g:assert.equals(getline(2), ' bar', 'failed at #15') + call g:assert.equals(getline(3), ' baz', 'failed at #15') +endfunction +"}}} +function! s:suite.blockwise_x_option_skip_char() abort "{{{ + """ off + " #1 + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal gg\2j8lsd" + call g:assert.equals(getline(1), 'aa(foo)bb', 'failed at #1') + call g:assert.equals(getline(2), 'aa(bar)bb', 'failed at #1') + call g:assert.equals(getline(3), 'aa(baz)bb', 'failed at #1') + + %delete + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal gg\2j8lsd" + call g:assert.equals(getline(1), 'aafoobb', 'failed at #2') + call g:assert.equals(getline(2), 'aabarbb', 'failed at #2') + call g:assert.equals(getline(3), 'aabazbb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + %delete + + """ on + call operator#sandwich#set('delete', 'block', 'skip_char', 1) + " #2 + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal gg\2j8lsd" + call g:assert.equals(getline(1), 'aafoobb', 'failed at #2') + call g:assert.equals(getline(2), 'aabarbb', 'failed at #2') + call g:assert.equals(getline(3), 'aabazbb', 'failed at #2') + + %delete + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal gg\2j8lsd" + call g:assert.equals(getline(1), 'aa(foo)bb', 'failed at #4') + call g:assert.equals(getline(2), 'aa(bar)bb', 'failed at #4') + call g:assert.equals(getline(3), 'aa(baz)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.blockwise_x_option_command() abort "{{{ + call operator#sandwich#set('delete', 'block', 'command', ['normal! `[d`]']) + + " #1 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), '', 'failed at #1') + call g:assert.equals(getline(2), '', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + + %delete + + " #2 + call operator#sandwich#set('delete', 'block', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsd" + call g:assert.equals(getline(1), '', 'failed at #2') + call g:assert.equals(getline(2), '', 'failed at #2') + call g:assert.equals(getline(3), '', 'failed at #2') +endfunction +"}}} + +" Function interface +function! s:suite.function_interface() abort "{{{ + nmap ssd :call operator#sandwich#prerequisite('delete', 'n', {'cursor': 'inner_tail'}, [{'buns': ['(', ')']}])g@ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['[', ']']}, + \ ] + + " #1 + call setline('.', '(foo)') + normal 0sda( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0sda[ + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + + " #3 + call setline('.', '(foo)') + normal 0ssda( + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #3') + + " #4 + call setline('.', '[foo]') + normal 0ssda[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') +endfunction +"}}} + +" Undo +function! s:suite.undo() abort "{{{ + " #1 + call setline('.', '(((foo)))') + " set undo point (see :help :undojoin) + let &undolevels = &undolevels + normal 0sd$ + normal! u + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #1') + + " #2 + call setline('.', '(((foo)))') + let &undolevels = &undolevels + normal 02sd$ + normal! u + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + " #3 + call setline('.', '(((foo)))') + let &undolevels = &undolevels + normal 03sd$ + normal! u + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #3') +endfunction +"}}} + +" When a assigned region is invalid +function! s:suite.invalid_region() abort "{{{ + nmap sd' (operator-sandwich-delete)i' + + " #1 + call setline('.', 'foo') + normal 0lsd' + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + + nunmap sd' +endfunction +"}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/operator-replace.vim b/config/neovim/store/lazy-plugins/vim-sandwich/test/operator-replace.vim new file mode 100644 index 00000000..1a3c32f5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/operator-replace.vim @@ -0,0 +1,14236 @@ +scriptencoding utf-8 + +let s:suite = themis#suite('operator-sandwich: replace:') +let s:object = 'g:operator#sandwich#object' +call themis#helper('command').with(s:) + +function! s:suite.before() abort "{{{ + nmap sr (operator-sandwich-replace) + xmap sr (operator-sandwich-replace) +endfunction +"}}} +function! s:suite.before_each() abort "{{{ + %delete + set filetype= + set whichwrap& + set autoindent& + set smartindent& + set cindent& + set cinkeys& + set indentexpr= + set indentkeys& + set virtualedit& + silent! mapc! + silent! ounmap ii + silent! ounmap ssr + silent! xunmap i{ + silent! xunmap a{ + call operator#sandwich#set_default() + unlet! g:sandwich#recipes + unlet! g:operator#sandwich#recipes + unlet! g:sandwich#input_fallback +endfunction +"}}} +function! s:suite.after() abort "{{{ + call s:suite.before_each() + nmap sr (operator-sandwich-replace)(operator-sandwich-release-count)(textobj-sandwich-query-a) + xmap sr (operator-sandwich-replace) +endfunction +"}}} + +" Input +function! s:suite.input() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']']}, + \ ] + + " #1 + call setline('.', '[foo]') + normal 0sra[( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'input': ['a', 'b']}, + \ {'buns': ['[', ']']}, + \ ] + + " #2 + call setline('.', '[foo]') + normal 0sra[a + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + " #3 + call setline('.', '[foo]') + normal 0sra[b + call g:assert.equals(getline('.'), '(foo)', 'failed at #3') + + " #4 + call setline('.', '[foo]') + normal 0sra[( + call g:assert.equals(getline('.'), '(foo(', 'failed at #4') + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['`', '`']}, + \ {'buns': ['``', '``']}, + \ {'buns': ['```', '```']}, + \ ] + + " #5 + call setline('.', "'foo'") + normal 0sr2i'`h + call g:assert.equals(getline('.'), '`foo`', 'failed at #5') + + " #6 + call setline('.', "'foo'") + normal 0sr2i'``h + call g:assert.equals(getline('.'), '``foo``', 'failed at #6') + + " #7 + call setline('.', "'foo'") + normal 0sr2i'``` + call g:assert.equals(getline('.'), '```foo```', 'failed at #7') + + " #8 + call setline('.', "'foo'") + execute "normal 0sr2i'`\" + call g:assert.equals(getline('.'), "'foo'", 'failed at #8') + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['```', '```']}, + \ ] + + " #9 + call setline('.', "'foo'") + normal 0sr2i'`h + call g:assert.equals(getline('.'), '`foo`', 'failed at #9') + + " #10 + call setline('.', "'foo'") + normal 0sr2i'``h + call g:assert.equals(getline('.'), '`foo`', 'failed at #10') + + " #11 + call setline('.', "'foo'") + normal 0sr2i'``` + call g:assert.equals(getline('.'), '```foo```', 'failed at #11') + + " #12 + call setline('.', "'foo'") + execute "normal 0sr2i'`\" + call g:assert.equals(getline('.'), "'foo'", 'failed at #12') + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['"', '"'], 'input': ['`']}, + \ {'buns': ['```', '```']}, + \ ] + + " #13 + call setline('.', "'foo'") + normal 0sr2i'`h + call g:assert.equals(getline('.'), '"foo"', 'failed at #13') + + " #14 + call setline('.', "'foo'") + normal 0sr2i'``h + call g:assert.equals(getline('.'), '"foo"', 'failed at #14') + + " #15 + call setline('.', "'foo'") + normal 0sr2i'``` + call g:assert.equals(getline('.'), '```foo```', 'failed at #15') + + " #16 + call setline('.', "'foo'") + execute "normal 0sr2i'`\" + call g:assert.equals(getline('.'), "'foo'", 'failed at #16') +endfunction +"}}} + +" Filter +function! s:suite.filter_filetype() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'filetype': ['vim'], 'input': ['(', ')']}, + \ {'buns': ['{', '}'], 'filetype': ['all']}, + \ {'buns': ['<', '>'], 'filetype': ['']} + \ ] + + " #1 + call setline('.', '{foo}') + normal 0sra{( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + + " #3 + call setline('.', '(foo)') + normal 0sra(< + call g:assert.equals(getline('.'), '', 'failed at #3') + + " #4 + call setline('.', '') + normal 0sra<( + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + + set filetype=vim + + " #5 + call setline('.', '{foo}') + normal 0sra{( + call g:assert.equals(getline('.'), '[foo]', 'failed at #5') + + " #6 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '{foo}', 'failed at #6') + + " #7 + call setline('.', '(foo)') + normal 0sra(< + call g:assert.equals(getline('.'), '') + normal 0sra<( + call g:assert.equals(getline('.'), '', 'failed at #8') +endfunction +"}}} +function! s:suite.filter_kind() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['[', ']']}, + \ {'buns': ['(', ')']}, + \ ] + + " #1 + call setline('.', '[foo]') + normal 0sra[( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}'], 'input': ['[']}, + \ {'buns': ['[', ']'], 'kind': ['add']}, + \ ] + + " #2 + call setline('.', '(foo)') + normal 0sra([ + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}'], 'input': ['[']}, + \ {'buns': ['[', ']'], 'kind': ['delete']}, + \ ] + + " #3 + call setline('.', '(foo)') + normal 0sra([ + call g:assert.equals(getline('.'), '{foo}', 'failed at #3') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}'], 'input': ['[']}, + \ {'buns': ['[', ']'], 'kind': ['replace']}, + \ ] + + " #4 + call setline('.', '(foo)') + normal 0sra([ + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}'], 'input': ['[']}, + \ {'buns': ['[', ']'], 'kind': ['operator']}, + \ ] + + " #5 + call setline('.', '(foo)') + normal 0sra([ + call g:assert.equals(getline('.'), '[foo]', 'failed at #5') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}'], 'input': ['[']}, + \ {'buns': ['[', ']'], 'kind': ['all']}, + \ ] + + " #6 + call setline('.', '(foo)') + normal 0sra([ + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') +endfunction +"}}} +function! s:suite.filter_motionwise() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'input': ['{']}, + \ ] + call operator#sandwich#set('replace', 'line', 'linewise', 0) + + " #1 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal 0srVl{ + call g:assert.equals(getline('.'), '[foo]', 'failed at #2') + + " #3 + call setline('.', '(foo)') + execute "normal 0sr\a({" + call g:assert.equals(getline('.'), '[foo]', 'failed at #3') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'motionwise': ['all'], 'input': ['{']}, + \ ] + + " #4 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') + + " #5 + call setline('.', '(foo)') + normal 0srVl{ + call g:assert.equals(getline('.'), '[foo]', 'failed at #5') + + " #6 + call setline('.', '(foo)') + execute "normal 0sr\a({" + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'motionwise': ['char'], 'input': ['{']}, + \ ] + + " #7 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '[foo]', 'failed at #7') + + " #8 + call setline('.', '(foo)') + normal 0srVl{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #8') + + " #9 + call setline('.', '(foo)') + execute "normal 0sr\a({" + call g:assert.equals(getline('.'), '{foo}', 'failed at #9') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'motionwise': ['line'], 'input': ['{']}, + \ ] + + " #10 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '{foo}', 'failed at #10') + + " #11 + call setline('.', '(foo)') + normal 0srVl{ + call g:assert.equals(getline('.'), '[foo]', 'failed at #11') + + " #12 + call setline('.', '(foo)') + execute "normal 0sr\a({" + call g:assert.equals(getline('.'), '{foo}', 'failed at #12') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'motionwise': ['block'], 'input': ['{']}, + \ ] + + " #13 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '{foo}', 'failed at #13') + + " #14 + call setline('.', '(foo)') + normal 0srVl{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #14') + + " #15 + call setline('.', '(foo)') + execute "normal 0sr\a({" + call g:assert.equals(getline('.'), '[foo]', 'failed at #15') +endfunction +"}}} +function! s:suite.filter_mode() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'input': ['{']}, + \ ] + + " #1 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal 0va(sr{ + call g:assert.equals(getline('.'), '[foo]', 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'mode': ['n'], 'input': ['{']}, + \ ] + + " #3 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '[foo]', 'failed at #3') + + " #4 + call setline('.', '(foo)') + normal 0va(sr{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'mode': ['x'], 'input': ['{']}, + \ ] + + " #5 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '{foo}', 'failed at #5') + + " #6 + call setline('.', '(foo)') + normal 0va(sr{ + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') +endfunction +"}}} +function! s:suite.filter_action() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'input': ['{']}, + \ ] + + " #1 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0sra[( + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'action': ['all'], 'input': ['{']}, + \ ] + + " #3 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '[foo]', 'failed at #3') + + " #4 + call setline('.', '[foo]') + normal 0sra[( + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'action': ['add'], 'input': ['{']}, + \ ] + + " #5 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '[foo]', 'failed at #5') + + " #6 + call setline('.', '[foo]') + normal 0sra[( + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['{', '}']}, + \ {'buns': ['[', ']'], 'action': ['delete'], 'input': ['{']}, + \ ] + + " #7 + call setline('.', '(foo)') + normal 0sra({ + call g:assert.equals(getline('.'), '{foo}', 'failed at #7') + + " #8 + call setline('.', '[foo]') + normal 0sra[( + call g:assert.equals(getline('.'), '(foo)', 'failed at #8') +endfunction +"}}} +function! s:suite.filter_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'expr_filter': ['FilterValid()']}, + \ {'buns': ['{', '}'], 'expr_filter': ['FilterInvalid()']}, + \ ] + + function! FilterValid() abort + return 1 + endfunction + + function! FilterInvalid() abort + return 0 + endfunction + + " #1 + call setline('.', '"foo"') + normal 0sr2i"( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', '"foo"') + normal 0sr2i"[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #2') + + " #3 + call setline('.', '"foo"') + normal 0sr2i"{ + call g:assert.equals(getline('.'), '{foo{', 'failed at #3') + + " #4 + call setline('.', '(foo)') + normal 0sr5l" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + " #5 + call setline('.', '[foo]') + normal 0sr5l" + call g:assert.equals(getline('.'), '"foo"', 'failed at #5') + + " #6 + call setline('.', '{foo}') + normal 0sr5l" + call g:assert.equals(getline('.'), '{foo}', 'failed at #6') +endfunction +"}}} + +" character-wise +function! s:suite.charwise_n_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + normal 0sra([ + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0sra[{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal 0sra{< + call g:assert.equals(getline('.'), '', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #3') + + " #4 +call setline('.', '') + normal 0sra<( + call g:assert.equals(getline('.'), '(foo)', 'failed at #72') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #72') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #72') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #72') + + " #73 + call setline('.', '(foo)') + normal 0sra(] + call g:assert.equals(getline('.'), '[foo]', 'failed at #73') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #73') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #73') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #73') + + " #74 + call setline('.', '[foo]') + normal 0sra[} + call g:assert.equals(getline('.'), '{foo}', 'failed at #74') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #74') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #74') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #74') + + " #75 + call setline('.', '{foo}') + normal 0sra{> + call g:assert.equals(getline('.'), '', 'failed at #75') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #75') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #75') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #75') + + " #76 + call setline('.', '') + normal 0sra<) + call g:assert.equals(getline('.'), '(foo)', 'failed at #76') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #76') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #76') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #76') +endfunction +"}}} +function! s:suite.charwise_n_not_registered() abort "{{{ + " #1 + call setline('.', 'afooa') + normal 0sriwb + call g:assert.equals(getline('.'), 'bfoob', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', '+foo+') + normal 0sr$* + call g:assert.equals(getline('.'), '*foo*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_n_positioning() abort "{{{ + " #1 + call setline('.', '(foo)bar') + normal 0sra([ + call g:assert.equals(getline('.'), '[foo]bar', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', 'foo(bar)') + normal 0fbsra([ + call g:assert.equals(getline('.'), 'foo[bar]', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #2') + + " #3 + call setline('.', 'foo(bar)baz') + normal 0fbsra([ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #3') + + %delete + + onoremap ii :call TextobjCoord(1, 4, 1, 10) + call operator#sandwich#set('replace', 'char', 'cursor', 'keep') + let g:operator#sandwich#recipes = [{'buns': ['((', '))'], 'input': ['(']}, {'buns': ['[', ']']}] + + " #4 + call setline('.', 'foo((bar))baz') + normal 0srii[ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #4') + + " #5 + call setline('.', 'foo((bar))baz') + normal 02lsrii[ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #5') + + " #6 + call setline('.', 'foo((bar))baz') + normal 03lsrii[ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #6') + + " #7 + call setline('.', 'foo((bar))baz') + normal 04lsrii[ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #7') + + " #8 + call setline('.', 'foo((bar))baz') + normal 05lsrii[ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #8') + + " #9 + call setline('.', 'foo((bar))baz') + normal 07lsrii[ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #9') + + " #10 + call setline('.', 'foo((bar))baz') + normal 08lsrii[ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #10') + + " #11 + call setline('.', 'foo((bar))baz') + normal 09lsrii[ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #11') + + " #12 + call setline('.', 'foo((bar))baz') + normal 010lsrii[ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #12') + + " #13 + call setline('.', 'foo((bar))baz') + normal 012lsrii[ + call g:assert.equals(getline('.'), 'foo[bar]baz', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 11, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #13') + + " #14 + call setline('.', 'foo[[bar]]baz') + normal 03lsrii( + call g:assert.equals(getline('.'), 'foo(([bar]))baz', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 1, 13, 0], 'failed at #14') + + " #15 + call setline('.', 'foo[[bar]]baz') + normal 09lsrii( + call g:assert.equals(getline('.'), 'foo(([bar]))baz', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, 11, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 1, 13, 0], 'failed at #15') + + ounmap ii + call operator#sandwich#set('replace', 'char', 'cursor', 'inner_head') + unlet g:operator#sandwich#recipes + + " #16 + set whichwrap=h,l + call append(0, ['(foo', 'bar', 'baz)']) + normal ggsr13l[ + call g:assert.equals(getline(1), '[foo', 'failed at #16') + call g:assert.equals(getline(2), 'bar', 'failed at #16') + call g:assert.equals(getline(3), 'baz]', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #16') + set whichwrap& +endfunction +"}}} +function! s:suite.charwise_n_a_character() abort "{{{ + " #1 + call setline('.', '(a)') + normal 0sra([ + call g:assert.equals(getline('.'), '[a]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 4, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(', 'a', ')']) + normal ggsra([ + call g:assert.equals(getline(1), '[', 'failed at #2') + call g:assert.equals(getline(2), 'a', 'failed at #2') + call g:assert.equals(getline(3), ']', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(a', ')']) + normal ggsra([ + call g:assert.equals(getline(1), '[a', 'failed at #3') + call g:assert.equals(getline(2), ']', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 2, 2, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(', 'a)']) + normal ggsra([ + call g:assert.equals(getline(1), '[', 'failed at #4') + call g:assert.equals(getline(2), 'a]', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 2, 3, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_n_nothing_inside() abort "{{{ + " #1 + call setline('.', '()') + normal 0sra([ + call g:assert.equals(getline('.'), '[]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 3, 0], 'failed at #1') + + " #2 + call setline('.', 'foo()bar') + normal 03lsra([ + call g:assert.equals(getline('.'), 'foo[]bar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(', ')']) + normal ggsra([ + call g:assert.equals(getline(1), '[', 'failed at #3') + call g:assert.equals(getline(2), ']', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 2, 2, 0], 'failed at #3') +endfunction +"}}} +function! s:suite.charwise_n_count() abort "{{{ + " #1 + call setline('.', '([foo])') + normal 02sr%[( + call g:assert.equals(getline('.'), '[(foo)]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 8, 0], 'failed at #1') + + " #2 + call setline('.', '[({foo})]') + normal 03sr%{[( + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 10, 0], 'failed at #2') + + " #3 + call setline('.', '[foo ]bar') + normal 0sr6l( + call g:assert.equals(getline('.'), '(foo )bar', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 7, 0], 'failed at #3') + + " #4 + call setline('.', '[foo bar]') + normal 0sr9l( + call g:assert.equals(getline('.'), '(foo bar)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 10, 0], 'failed at #4') + + " #5 + call setline('.', '{[foo bar]}') + normal 02sr11l[( + call g:assert.equals(getline('.'), '[(foo bar)]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, 12, 0], 'failed at #5') + + " #6 + call setline('.', 'foo{[bar]}baz') + normal 03l2sr7l[( + call g:assert.equals(getline('.'), 'foo[(bar)]baz', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, 11, 0], 'failed at #6') + + " #7 + call setline('.', 'foo({[bar]})baz') + normal 03l3sr9l{[( + call g:assert.equals(getline('.'), 'foo{[(bar)]}baz', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 13, 0], 'failed at #7') +endfunction +"}}} +function! s:suite.charwise_n_breaking() abort "{{{ + set whichwrap=h,l + + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #1 + call append(0, ['aa', 'aaafooaaa', 'aa']) + normal ggsr15l( + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['bb', 'bbb', 'bbfoobb', 'bbb', 'bb']) + normal ggsr21l( + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + %delete + + " #3 + call setline('.', '(foo)') + normal 0sr5la + call g:assert.equals(getline(1), 'aa', 'failed at #3') + call g:assert.equals(getline(2), 'aaafooaaa', 'failed at #3') + call g:assert.equals(getline(3), 'aa', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', '(foo)') + normal 0sr5lb + call g:assert.equals(getline(1), 'bb', 'failed at #4') + call g:assert.equals(getline(2), 'bbb', 'failed at #4') + call g:assert.equals(getline(3), 'bbfoobb', 'failed at #4') + call g:assert.equals(getline(4), 'bbb', 'failed at #4') + call g:assert.equals(getline(5), 'bb', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['aa', 'aaafooaaa', 'aa']) + normal ggsr15lb + call g:assert.equals(getline(1), 'bb', 'failed at #5') + call g:assert.equals(getline(2), 'bbb', 'failed at #5') + call g:assert.equals(getline(3), 'bbfoobb', 'failed at #5') + call g:assert.equals(getline(4), 'bbb', 'failed at #5') + call g:assert.equals(getline(5), 'bb', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['bb', 'bbb', 'bbfoobb', 'bbb', 'bb']) + normal ggsr21la + call g:assert.equals(getline(1), 'aa', 'failed at #6') + call g:assert.equals(getline(2), 'aaafooaaa', 'failed at #6') + call g:assert.equals(getline(3), 'aa', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['aa', 'aaaaa', 'aaafooaaa', 'aaaaa', 'aa']) + normal gg2sr27lbb + call g:assert.equals(getline(1), 'bb', 'failed at #7') + call g:assert.equals(getline(2), 'bbb', 'failed at #7') + call g:assert.equals(getline(3), 'bbbb', 'failed at #7') + call g:assert.equals(getline(4), 'bbb', 'failed at #7') + call g:assert.equals(getline(5), 'bbfoobb', 'failed at #7') + call g:assert.equals(getline(6), 'bbb', 'failed at #7') + call g:assert.equals(getline(7), 'bbbb', 'failed at #7') + call g:assert.equals(getline(8), 'bbb', 'failed at #7') + call g:assert.equals(getline(9), 'bb', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 5, 3, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 9, 3, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['bb', 'bbb', 'bbbb', 'bbb', 'bbfoobb', 'bbb', 'bbbb', 'bbb', 'bb']) + normal gg2sr39laa + call g:assert.equals(getline(1), 'aa', 'failed at #8') + call g:assert.equals(getline(2), 'aaaaa', 'failed at #8') + call g:assert.equals(getline(3), 'aaafooaaa', 'failed at #8') + call g:assert.equals(getline(4), 'aaaaa', 'failed at #8') + call g:assert.equals(getline(5), 'aa', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 4, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #8') + + %delete + call operator#sandwich#set('replace', 'char', 'cursor', 'keep') + onoremap ii :call TextobjCoord(1, 4, 1, 8) + + " #9 + call setline('.', ['foo(bar)baz']) + normal 0sriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #9') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #9') + call g:assert.equals(getline(3), 'aabaz', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #9') + + %delete + + " #10 + call setline('.', ['foo(bar)baz']) + normal 02lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #10') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #10') + call g:assert.equals(getline(3), 'aabaz', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #10') + + %delete + + " #11 + call setline('.', ['foo(bar)baz']) + normal 03lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #11') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #11') + call g:assert.equals(getline(3), 'aabaz', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #11') + + %delete + + " #12 + call setline('.', ['foo(bar)baz']) + normal 04lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #12') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #12') + call g:assert.equals(getline(3), 'aabaz', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #12') + + %delete + + " #13 + call setline('.', ['foo(bar)baz']) + normal 06lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #13') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #13') + call g:assert.equals(getline(3), 'aabaz', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 2, 6, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #13') + + %delete + + " #14 + call setline('.', ['foo(bar)baz']) + normal 07lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #14') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #14') + call g:assert.equals(getline(3), 'aabaz', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 2, 7, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #14') + + %delete + + " #15 + call setline('.', ['foo(bar)baz']) + normal 08lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #15') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #15') + call g:assert.equals(getline(3), 'aabaz', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #15') + + %delete + + " #16 + call setline('.', ['foo(bar)baz']) + normal 010lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #16') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #16') + call g:assert.equals(getline(3), 'aabaz', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #16') + + %delete + onoremap ii :call TextobjCoord(1, 4, 3, 2) + + " #17 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #17') + + %delete + + " #18 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2lsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #18') + + %delete + + " #19 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg3lsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #19') + + %delete + + " #20 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg4lsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #20') + + %delete + + " #21 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggjsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #21') + + %delete + + " #22 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj2lsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #22') + + %delete + + " #23 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj3lsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #23') + + %delete + + " #24 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj5lsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #24') + + %delete + + " #25 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj6lsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #25') + + %delete + + " #26 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj8lsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #26') + + %delete + + " #27 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2jsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #27') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #27') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #27') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #27') + + %delete + + " #28 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2jlsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #28') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #28') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #28') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #28') + + %delete + + " #29 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2j2lsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #29') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #29') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #29') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #29') + + %delete + + " #30 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2j4lsrii( + call g:assert.equals(getline(1), 'foo(bar)baz', 'failed at #30') + call g:assert.equals(getpos('.'), [0, 1, 11, 0], 'failed at #30') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #30') + call g:assert.equals(getpos("']"), [0, 1, 9, 0], 'failed at #30') + + %delete + + " #31 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #31') + call g:assert.equals(getline(2), 'bbb', 'failed at #31') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #31') + call g:assert.equals(getline(4), 'bbb', 'failed at #31') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #31') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #31') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #31') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #31') + + %delete + + " #32 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #32') + call g:assert.equals(getline(2), 'bbb', 'failed at #32') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #32') + call g:assert.equals(getline(4), 'bbb', 'failed at #32') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #32') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #32') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #32') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #32') + + %delete + + " #33 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg3lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #33') + call g:assert.equals(getline(2), 'bbb', 'failed at #33') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #33') + call g:assert.equals(getline(4), 'bbb', 'failed at #33') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #33') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #33') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #33') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #33') + + %delete + + " #34 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg4lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #34') + call g:assert.equals(getline(2), 'bbb', 'failed at #34') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #34') + call g:assert.equals(getline(4), 'bbb', 'failed at #34') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #34') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #34') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #34') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #34') + + %delete + + " #35 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggjsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #35') + call g:assert.equals(getline(2), 'bbb', 'failed at #35') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #35') + call g:assert.equals(getline(4), 'bbb', 'failed at #35') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #35') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #35') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #35') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #35') + + %delete + + " #36 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggjlsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #36') + call g:assert.equals(getline(2), 'bbb', 'failed at #36') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #36') + call g:assert.equals(getline(4), 'bbb', 'failed at #36') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #36') + call g:assert.equals(getpos('.'), [0, 2, 2, 0], 'failed at #36') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #36') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #36') + + %delete + + " #37 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj2lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #37') + call g:assert.equals(getline(2), 'bbb', 'failed at #37') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #37') + call g:assert.equals(getline(4), 'bbb', 'failed at #37') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #37') + call g:assert.equals(getpos('.'), [0, 2, 3, 0], 'failed at #37') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #37') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #37') + + %delete + + " #38 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj3lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #38') + call g:assert.equals(getline(2), 'bbb', 'failed at #38') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #38') + call g:assert.equals(getline(4), 'bbb', 'failed at #38') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #38') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #38') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #38') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #38') + + %delete + + " #39 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj5lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #39') + call g:assert.equals(getline(2), 'bbb', 'failed at #39') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #39') + call g:assert.equals(getline(4), 'bbb', 'failed at #39') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #39') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #39') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #39') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #39') + + %delete + + " #40 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj6lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #40') + call g:assert.equals(getline(2), 'bbb', 'failed at #40') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #40') + call g:assert.equals(getline(4), 'bbb', 'failed at #40') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #40') + call g:assert.equals(getpos('.'), [0, 3, 6, 0], 'failed at #40') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #40') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #40') + + %delete + + " #41 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj7lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #41') + call g:assert.equals(getline(2), 'bbb', 'failed at #41') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #41') + call g:assert.equals(getline(4), 'bbb', 'failed at #41') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #41') + call g:assert.equals(getpos('.'), [0, 3, 7, 0], 'failed at #41') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #41') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #41') + + %delete + + " #42 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal ggj8lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #42') + call g:assert.equals(getline(2), 'bbb', 'failed at #42') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #42') + call g:assert.equals(getline(4), 'bbb', 'failed at #42') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #42') + call g:assert.equals(getpos('.'), [0, 3, 7, 0], 'failed at #42') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #42') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #42') + + %delete + + " #43 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2jsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #43') + call g:assert.equals(getline(2), 'bbb', 'failed at #43') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #43') + call g:assert.equals(getline(4), 'bbb', 'failed at #43') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #43') + call g:assert.equals(getpos('.'), [0, 4, 1, 0], 'failed at #43') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #43') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #43') + + %delete + + " #44 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2jlsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #44') + call g:assert.equals(getline(2), 'bbb', 'failed at #44') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #44') + call g:assert.equals(getline(4), 'bbb', 'failed at #44') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #44') + call g:assert.equals(getpos('.'), [0, 4, 2, 0], 'failed at #44') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #44') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #44') + + %delete + + " #45 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2j2lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #45') + call g:assert.equals(getline(2), 'bbb', 'failed at #45') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #45') + call g:assert.equals(getline(4), 'bbb', 'failed at #45') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #45') + call g:assert.equals(getpos('.'), [0, 5, 3, 0], 'failed at #45') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #45') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #45') + + %delete + + " #46 + call append(0, ['fooaa', 'aaabaraaa', 'aabaz']) + normal gg2j4lsriib + call g:assert.equals(getline(1), 'foobb', 'failed at #46') + call g:assert.equals(getline(2), 'bbb', 'failed at #46') + call g:assert.equals(getline(3), 'bbbarbb', 'failed at #46') + call g:assert.equals(getline(4), 'bbb', 'failed at #46') + call g:assert.equals(getline(5), 'bbbaz', 'failed at #46') + call g:assert.equals(getpos('.'), [0, 5, 5, 0], 'failed at #46') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #46') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #46') + + %delete + onoremap ii :call TextobjCoord(1, 4, 5, 2) + + " #47 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal ggsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #47') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #47') + call g:assert.equals(getline(3), 'aabaz', 'failed at #47') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #47') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #47') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #47') + + %delete + + " #48 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg2lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #48') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #48') + call g:assert.equals(getline(3), 'aabaz', 'failed at #48') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #48') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #48') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #48') + + %delete + + " #49 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg3lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #49') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #49') + call g:assert.equals(getline(3), 'aabaz', 'failed at #49') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #49') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #49') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #49') + + %delete + + " #50 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg4lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #50') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #50') + call g:assert.equals(getline(3), 'aabaz', 'failed at #50') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #50') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #50') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #50') + + %delete + + " #51 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal ggjsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #51') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #51') + call g:assert.equals(getline(3), 'aabaz', 'failed at #51') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #51') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #51') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #51') + + %delete + + " #52 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal ggjlsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #52') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #52') + call g:assert.equals(getline(3), 'aabaz', 'failed at #52') + call g:assert.equals(getpos('.'), [0, 2, 2, 0], 'failed at #52') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #52') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #52') + + %delete + + " #53 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal ggj2lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #53') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #53') + call g:assert.equals(getline(3), 'aabaz', 'failed at #53') + call g:assert.equals(getpos('.'), [0, 2, 3, 0], 'failed at #53') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #53') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #53') + + %delete + + " #54 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg2jsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #54') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #54') + call g:assert.equals(getline(3), 'aabaz', 'failed at #54') + call g:assert.equals(getpos('.'), [0, 2, 3, 0], 'failed at #54') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #54') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #54') + + %delete + + " #55 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg2jlsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #55') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #55') + call g:assert.equals(getline(3), 'aabaz', 'failed at #55') + call g:assert.equals(getpos('.'), [0, 2, 3, 0], 'failed at #55') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #55') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #55') + + %delete + + " #56 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg2j2lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #56') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #56') + call g:assert.equals(getline(3), 'aabaz', 'failed at #56') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #56') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #56') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #56') + + %delete + + " #57 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg2j4lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #57') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #57') + call g:assert.equals(getline(3), 'aabaz', 'failed at #57') + call g:assert.equals(getpos('.'), [0, 2, 6, 0], 'failed at #57') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #57') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #57') + + %delete + + " #58 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg2j5lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #58') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #58') + call g:assert.equals(getline(3), 'aabaz', 'failed at #58') + call g:assert.equals(getpos('.'), [0, 2, 7, 0], 'failed at #58') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #58') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #58') + + %delete + + " #59 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg2j6lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #59') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #59') + call g:assert.equals(getline(3), 'aabaz', 'failed at #59') + call g:assert.equals(getpos('.'), [0, 2, 8, 0], 'failed at #59') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #59') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #59') + + %delete + + " #60 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg3jsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #60') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #60') + call g:assert.equals(getline(3), 'aabaz', 'failed at #60') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #60') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #60') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #60') + + %delete + + " #61 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg3jlsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #61') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #61') + call g:assert.equals(getline(3), 'aabaz', 'failed at #61') + call g:assert.equals(getpos('.'), [0, 3, 2, 0], 'failed at #61') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #61') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #61') + + %delete + + " #62 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg3j2lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #62') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #62') + call g:assert.equals(getline(3), 'aabaz', 'failed at #62') + call g:assert.equals(getpos('.'), [0, 3, 2, 0], 'failed at #62') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #62') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #62') + + %delete + + " #63 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg4jsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #63') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #63') + call g:assert.equals(getline(3), 'aabaz', 'failed at #63') + call g:assert.equals(getpos('.'), [0, 3, 2, 0], 'failed at #63') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #63') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #63') + + %delete + + " #64 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg4jlsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #64') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #64') + call g:assert.equals(getline(3), 'aabaz', 'failed at #64') + call g:assert.equals(getpos('.'), [0, 3, 2, 0], 'failed at #64') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #64') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #64') + + %delete + + " #65 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg4j2lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #65') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #65') + call g:assert.equals(getline(3), 'aabaz', 'failed at #65') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #65') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #65') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #65') + + %delete + + " #66 + call append(0, ['foobb', 'bbb', 'bbbarbb', 'bbb', 'bbbaz']) + normal gg4j4lsriia + call g:assert.equals(getline(1), 'fooaa', 'failed at #66') + call g:assert.equals(getline(2), 'aaabaraaa', 'failed at #66') + call g:assert.equals(getline(3), 'aabaz', 'failed at #66') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #66') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #66') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #66') + + ounmap ii + call operator#sandwich#set('replace', 'char', 'cursor', 'inner_head') + unlet! g:operator#sandwich#recipes + set whichwrap& +endfunction +"}}} +function! s:suite.charwise_n_external_textobj() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call setline('.', '{[(foo)]}') + normal 02lsr5l" + call g:assert.equals(getline('.'), '{["foo"]}', 'failed at #1') + + " #2 + call setline('.', '{[(foo)]}') + normal 0lsr7l" + call g:assert.equals(getline('.'), '{"(foo)"}', 'failed at #2') + + " #3 + call setline('.', '{[(foo)]}') + normal 0sr9l" + call g:assert.equals(getline('.'), '"[(foo)]"', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal 0sr$" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call setline('.', '(foo)') + normal 0sr5l" + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.charwise_n_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call setline('.', 'aαa') + normal 0sr3l( + call g:assert.equals(getline('.'), '(α)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, strlen('(α)')+1, 0], 'failed at #1') + + " #2 + call setline('.', 'aaαa') + normal 0sr4l( + call g:assert.equals(getline('.'), '(aα)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, strlen('(aα)')+1, 0], 'failed at #2') + + " #3 + call setline('.', 'βαβ') + normal 0sr3l( + call g:assert.equals(getline('.'), '(α)', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, strlen('(α)')+1, 0], 'failed at #3') + + " #4 + call setline('.', 'βaαβ') + normal 0sr4l( + call g:assert.equals(getline('.'), '(aα)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, strlen('(aα)')+1, 0], 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #5 + call setline('.', 'aaa') + normal 0sr3la + call g:assert.equals(getline('.'), 'αaα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaα')+1, 0], 'failed at #5') + + " #6 + call setline('.', 'aαa') + normal 0sr3la + call g:assert.equals(getline('.'), 'ααα', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, strlen('ααα')+1, 0], 'failed at #6') + + " #7 + call setline('.', 'aaαa') + normal 0sr4la + call g:assert.equals(getline('.'), 'αaαα', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaαα')+1, 0], 'failed at #7') + + " #8 + call setline('.', 'βaβ') + normal 0sr3la + call g:assert.equals(getline('.'), 'αaα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaα')+1, 0], 'failed at #8') + + " #9 + call setline('.', 'βαβ') + normal 0sr3la + call g:assert.equals(getline('.'), 'ααα', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, strlen('ααα')+1, 0], 'failed at #9') + + " #10 + call setline('.', 'βaαβ') + normal 0sr4la + call g:assert.equals(getline('.'), 'αaαα', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaαα')+1, 0], 'failed at #10') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #11 + call setline('.', 'aaa') + normal 0sr3la + call g:assert.equals(getline('.'), 'aαaaα', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaaα')+1, 0], 'failed at #11') + + " #12 + call setline('.', 'aαa') + normal 0sr3la + call g:assert.equals(getline('.'), 'aααaα', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, strlen('aααaα')+1, 0], 'failed at #12') + + " #13 + call setline('.', 'aaαa') + normal 0sr4la + call g:assert.equals(getline('.'), 'aαaαaα', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaαaα')+1, 0], 'failed at #13') + + " #14 + call setline('.', 'βaβ') + normal 0sr3la + call g:assert.equals(getline('.'), 'aαaaα', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaaα')+1, 0], 'failed at #14') + + " #15 + call setline('.', 'βαβ') + normal 0sr3la + call g:assert.equals(getline('.'), 'aααaα', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 1, strlen('aααaα')+1, 0], 'failed at #15') + + " #16 + call setline('.', 'βaαβ') + normal 0sr4la + call g:assert.equals(getline('.'), 'aαaαaα', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaαaα')+1, 0], 'failed at #16') + + unlet g:operator#sandwich#recipes + + " #17 + call setline('.', 'a“a') + normal 0sr3l( + call g:assert.equals(getline('.'), '(“)', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 1, strlen('(“)')+1, 0], 'failed at #17') + + " #18 + call setline('.', 'aa“a') + normal 0sr4l( + call g:assert.equals(getline('.'), '(a“)', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 1, strlen('(a“)')+1, 0], 'failed at #18') + + " #19 + call setline('.', 'β“β') + normal 0sr3l( + call g:assert.equals(getline('.'), '(“)', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 1, strlen('(“)')+1, 0], 'failed at #19') + + " #20 + call setline('.', 'βa“β') + normal 0sr4l( + call g:assert.equals(getline('.'), '(a“)', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 1, strlen('(a“)')+1, 0], 'failed at #20') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #21 + call setline('.', 'aaa') + normal 0sr3la + call g:assert.equals(getline('.'), '“a“', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a“')+1, 0], 'failed at #21') + + " #22 + call setline('.', 'a“a') + normal 0sr3la + call g:assert.equals(getline('.'), '“““', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 1, strlen('“““')+1, 0], 'failed at #22') + + " #23 + call setline('.', 'aa“a') + normal 0sr4la + call g:assert.equals(getline('.'), '“a““', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a““')+1, 0], 'failed at #23') + + " #24 + call setline('.', 'βaβ') + normal 0sr3la + call g:assert.equals(getline('.'), '“a“', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a“')+1, 0], 'failed at #24') + + " #25 + call setline('.', 'β“β') + normal 0sr3la + call g:assert.equals(getline('.'), '“““', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 1, strlen('“““')+1, 0], 'failed at #25') + + " #26 + call setline('.', 'βa“β') + normal 0sr4la + call g:assert.equals(getline('.'), '“a““', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a““')+1, 0], 'failed at #26') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #27 + call setline('.', 'aaa') + normal 0sr3la + call g:assert.equals(getline('.'), 'a“aa“', 'failed at #27') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #27') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #27') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“aa“')+1, 0], 'failed at #27') + + " #28 + call setline('.', 'a“a') + normal 0sr3la + call g:assert.equals(getline('.'), 'a““a“', 'failed at #28') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #28') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #28') + call g:assert.equals(getpos("']"), [0, 1, strlen('a““a“')+1, 0], 'failed at #28') + + " #29 + call setline('.', 'aa“a') + normal 0sr4la + call g:assert.equals(getline('.'), 'a“a“a“', 'failed at #29') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #29') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #29') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“a“a“')+1, 0], 'failed at #29') + + " #30 + call setline('.', 'βaβ') + normal 0sr3la + call g:assert.equals(getline('.'), 'a“aa“', 'failed at #30') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #30') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #30') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“aa“')+1, 0], 'failed at #30') + + " #31 + call setline('.', 'β“β') + normal 0sr3la + call g:assert.equals(getline('.'), 'a““a“', 'failed at #31') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #31') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #31') + call g:assert.equals(getpos("']"), [0, 1, strlen('a““a“')+1, 0], 'failed at #31') + + " #32 + call setline('.', 'βa“β') + normal 0sr4la + call g:assert.equals(getline('.'), 'a“a“a“', 'failed at #32') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #32') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #32') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“a“a“')+1, 0], 'failed at #32') +endfunction +"}}} +function! s:suite.charwise_n_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', '(((foo)))') + normal 0l2sr%[[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #1') + + " #2 + normal 0sra([ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + + " #3 + let g:operator#sandwich#recipes = [{'buns': ["[\n ", "\n]"], 'input':['a']}] + call setline('.', '(foo)') + normal 0sra(a + call g:assert.equals(getline(1), '[', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ']', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + unlet! g:operator#sandwich#recipes + + %delete + + """ inner_head + " #4 + call operator#sandwich#set('replace', 'char', 'cursor', 'inner_head') + call setline('.', '(((foo)))') + normal 0l2sr%[[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #4') + + " #5 + normal 0sra([ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + + """ keep + " #6 + call operator#sandwich#set('replace', 'char', 'cursor', 'keep') + call setline('.', '(((foo)))') + normal 04l2sr2a([[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #6') + + " #7 + normal lsra([ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #7') + + """ inner_tail + " #8 + call operator#sandwich#set('replace', 'char', 'cursor', 'inner_tail') + call setline('.', '(((foo)))') + normal 04l2sr2a([[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #8') + + " #9 + normal hsra([ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #9') + + """ head + " #10 + call operator#sandwich#set('replace', 'char', 'cursor', 'head') + call setline('.', '(((foo)))') + normal 04l2sr2a([[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + + " #11 + normal 3lsra([ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + + """ tail + " #12 + call operator#sandwich#set('replace', 'char', 'cursor', 'tail') + call setline('.', '(((foo)))') + normal 04l2sr2a([[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #12') + + " #13 + normal 3hsra([ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('replace', 'char', 'cursor', 'inner_tail') + call setline('.', '[foo]') + normal 0sra[1 + call g:assert.equals(getline('.'), '(foo)', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.charwise_n_option_noremap() abort "{{{ + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '{(foo)}') + normal 0sr7l" + call g:assert.equals(getline('.'), '"(foo)"', 'failed at #1') + + " #2 + call setline('.', '{(foo)}') + normal 0lsr5l" + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #2') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call setline('.', '{(foo)}') + normal 0sr7l" + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #3') + + " #4 + call setline('.', '{(foo)}') + normal 0lsr5l" + call g:assert.equals(getline('.'), '{"foo"}', 'failed at #4') + + """ off + call operator#sandwich#set('replace', 'char', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '{(foo)}') + normal 0sr7l" + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #5') + + " #6 + call setline('.', '{(foo)}') + normal 0lsr5l" + call g:assert.equals(getline('.'), '{"foo"}', 'failed at #6') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call setline('.', '{(foo)}') + normal 0sr7l" + call g:assert.equals(getline('.'), '"(foo)"', 'failed at #7') + + " #8 + call setline('.', '{(foo)}') + normal 0lsr5l" + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #8') +endfunction +"}}} +function! s:suite.charwise_n_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+foo\d\+') + normal 0sr$" + call g:assert.equals(getline('.'), '"foo"', 'failed at #1') + + " #2 + call setline('.', '888foo888') + normal 0sr$" + call g:assert.equals(getline('.'), '"88foo88"', 'failed at #2') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call setline('.', '\d\+foo\d\+') + normal 0sr$" + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #3') + + " #4 + call setline('.', '888foo888') + normal 0sr$" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + """ on + call operator#sandwich#set('replace', 'char', 'regex', 1) + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #5 + call setline('.', '\d\+foo\d\+') + normal 0sr$" + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #5') + + " #6 + call setline('.', '888foo888') + normal 0sr$" + call g:assert.equals(getline('.'), '"foo"', 'failed at #6') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call setline('.', '\d\+foo\d\+') + normal 0sr$" + call g:assert.equals(getline('.'), '"foo"', 'failed at #7') + + " #8 + call setline('.', '888foo888') + normal 0sr$" + call g:assert.equals(getline('.'), '"88foo88"', 'failed at #8') +endfunction +"}}} +function! s:suite.charwise_n_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + + """ 1 + " #1 + call setline('.', '"foo"') + normal 0sr$( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', ' "foo"') + normal 0sr$( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #2') + + " #3 + call setline('.', '"foo" ') + normal 0sr$( + call g:assert.equals(getline('.'), '(foo) ', 'failed at #3') + + " #4 + " do not skip! + call setline('.', ' "foo" ') + normal 0sr$( + call g:assert.equals(getline('.'), '("foo")', 'failed at #4') + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0sr$( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #5') + + """ 2 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'char', 'skip_space', 2) + + " #6 + call setline('.', '"foo"') + normal 0sr$( + call g:assert.equals(getline('.'), '(foo)', 'failed at #6') + + " #7 + call setline('.', ' "foo"') + normal 0sr$( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #7') + + " #8 + call setline('.', '"foo" ') + normal 0sr$( + call g:assert.equals(getline('.'), '(foo) ', 'failed at #8') + + " #9 + call setline('.', ' "foo" ') + normal 0sr$( + call g:assert.equals(getline('.'), ' (foo) ', 'failed at #9') + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0sr$( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #10') + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'char', 'skip_space', 0) + + " #11 + call setline('.', '"foo"') + normal 0sr$( + call g:assert.equals(getline('.'), '(foo)', 'failed at #11') + + " #12 + call setline('.', ' "foo"') + normal 0sr$( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #12') + + " #13 + call setline('.', '"foo" ') + normal 0sr$( + call g:assert.equals(getline('.'), '"foo" ', 'failed at #13') + + " #14 + " do not skip! + call setline('.', ' "foo" ') + normal 0sr$( + call g:assert.equals(getline('.'), '("foo")', 'failed at #14') + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0sr$( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #15') +endfunction +"}}} +function! s:suite.charwise_n_option_skip_char() abort "{{{ + """ off + " #1 + call setline('.', 'aa(foo)bb') + normal 0sr$" + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #1') + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call setline('.', 'aa(foo)bb') + normal 0sr$" + call g:assert.equals(getline('.'), 'aa"foo"bb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + """ on + call operator#sandwich#set('replace', 'char', 'skip_char', 1) + + " #3 + call setline('.', 'aa(foo)bb') + normal 0sr$" + call g:assert.equals(getline('.'), 'aa"foo"bb', 'failed at #3') + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call setline('.', 'aa(foo)bb') + normal 0sr$" + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.charwise_n_option_command() abort "{{{ + " #1 + call operator#sandwich#set('replace', 'char', 'command', ['normal! `[d`]']) + call setline('.', '[(foo)]') + normal 0ffsra(" + call g:assert.equals(getline('.'), '[]', 'failed at #1') + + " #2 + call operator#sandwich#set('replace', 'char', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call setline('.', '[(foo)]') + normal 0ffsra(" + call g:assert.equals(getline('.'), '[]', 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_n_option_linewise() abort "{{{ + set whichwrap=h,l + call operator#sandwich#set('replace', 'char', 'linewise', 1) + + """ 1 + " #1 + call append(0, ['(', 'foo', ')']) + normal ggsr7l[ + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ']', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['( ', 'foo', ' )']) + normal ggsr11l[ + call g:assert.equals(getline(1), '[', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), ']', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggsr11l[ + call g:assert.equals(getline(1), '[aa', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), 'aa]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa', 'foo', ')']) + normal ggsr9l[ + call g:assert.equals(getline(1), '[aa', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), ']', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', 'aa)']) + normal ggsr9l[ + call g:assert.equals(getline(1), '[', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), 'aa]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #5') + + %delete + + " #6 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggsr7l[ + call g:assert.equals(getline(1), '[', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), ']', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #6') + unlet! g:operator#sandwich#recipes + + %delete + + """ 2 + call operator#sandwich#set('replace', 'char', 'linewise', 2) + + " #7 + call append(0, ['(', 'foo', ')']) + normal ggsr7l[ + call g:assert.equals(getline(1), '[', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ']', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['( ', 'foo', ' )']) + normal ggsr11l[ + call g:assert.equals(getline(1), '[', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ']', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggsr11l[ + call g:assert.equals(getline(1), '[', 'failed at #9') + call g:assert.equals(getline(2), 'foo', 'failed at #9') + call g:assert.equals(getline(3), ']', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['aa', '(foo)', 'bb']) + normal ggjsr5l[ + call g:assert.equals(getline(1), 'aa', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), ']', 'failed at #10') + call g:assert.equals(getline(4), 'bb', 'failed at #10') + call g:assert.equals(getline(5), '', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #10') + + %delete + + " #11 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggsr7l[ + call g:assert.equals(getline(1), '[', 'failed at #11') + call g:assert.equals(getline(2), 'foo', 'failed at #11') + call g:assert.equals(getline(3), ']', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #11') + unlet! g:operator#sandwich#recipes + + set whichwrap& +endfunction +"}}} +function! s:suite.charwise_n_option_query_once() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call setline('.', '"""foo"""') + normal 03sr$([{ + call g:assert.equals(getline('.'), '([{foo}])', 'failed at #1') + + " #2 + call setline('.', '"""foo"""') + normal 03sr$1 + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + """ on + call operator#sandwich#set('replace', 'char', 'query_once', 1) + + " #3 + call setline('.', '"""foo"""') + normal 03sr$( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #3') + + " #4 + call setline('.', '"""foo"""') + normal 03sr$0[{ + call g:assert.equals(getline('.'), '([{foo}])', 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_n_option_expr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', '"foo"') + normal 0sra"a + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #1') + + " #2 + call setline('.', '"foo"') + normal 0sra"1 + call g:assert.equals(getline('.'), '2foo3', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'char', 'expr', 1) + call setline('.', '"foo"') + normal 0sra"a + call g:assert.equals(getline('.'), '2foo3', 'failed at #3') + + " #4 + call setline('.', '"foo"') + normal 0sra"b + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', '"foo"') + normal 0sra"c + call g:assert.equals(getline('.'), '"foo"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', '"''foo''"') + normal 02sra"ab + call g:assert.equals(getline('.'), '2''foo''3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + " #7 + call setline('.', '"''foo''"') + normal 02sra"ac + call g:assert.equals(getline('.'), '2''foo''3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + " #8 + call setline('.', '"''foo''"') + normal 02sra"ba + call g:assert.equals(getline('.'), '"''foo''"', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + " #9 + call setline('.', '"''foo''"') + normal 02sra"ca + call g:assert.equals(getline('.'), '"''foo''"', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + " #10 + call setline('.', '"foo"') + normal 0sra"0 + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.charwise_n_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0sra"a + call g:assert.equals(getline('.'), '"bar"', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + " #2 + call setline('.', '"bar"') + normal 0sra"1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'char', 'listexpr', 1) + call setline('.', '"bar"') + normal 0sra"a + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + + " #4 + call setline('.', '"bar"') + normal 0sra"b + call g:assert.equals(getline('.'), '"bar"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0sra"0 + call g:assert.equals(getline('.'), '"bar"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', '"bar"') + normal 0sra"1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.charwise_n_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + + """ -1 + call operator#sandwich#set('replace', 'char', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ']', 'failed at #1') + call g:assert.equals(getline(5), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), ' foo', 'failed at #2') + call g:assert.equals(getline(4), ' ]', 'failed at #2') + call g:assert.equals(getline(5), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), ' foo', 'failed at #3') + call g:assert.equals(getline(4), ' ]', 'failed at #3') + call g:assert.equals(getline(5), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), ' foo', 'failed at #4') + " call g:assert.equals(getline(4), ' ]', 'failed at #4') + call g:assert.equals(getline(5), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call setline('.', ' "foo"') + " normal ^sr2i"a + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), ' foo', 'failed at #5') + " call g:assert.equals(getline(4), ' ]', 'failed at #5') + " call g:assert.equals(getline(5), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 3, 17, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 5, 34, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('replace', 'char', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ']', 'failed at #6') + call g:assert.equals(getline(5), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), 'foo', 'failed at #7') + call g:assert.equals(getline(4), ']', 'failed at #7') + call g:assert.equals(getline(5), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), 'foo', 'failed at #8') + call g:assert.equals(getline(4), ']', 'failed at #8') + call g:assert.equals(getline(5), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), 'foo', 'failed at #9') + call g:assert.equals(getline(4), ']', 'failed at #9') + call g:assert.equals(getline(5), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), 'foo', 'failed at #10') + call g:assert.equals(getline(4), ']', 'failed at #10') + call g:assert.equals(getline(5), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('replace', 'char', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), ' foo', 'failed at #11') + call g:assert.equals(getline(4), ' ]', 'failed at #11') + call g:assert.equals(getline(5), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), ' foo', 'failed at #12') + call g:assert.equals(getline(4), ' ]', 'failed at #12') + call g:assert.equals(getline(5), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), ' foo', 'failed at #13') + call g:assert.equals(getline(4), ' ]', 'failed at #13') + call g:assert.equals(getline(5), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), ' foo', 'failed at #14') + call g:assert.equals(getline(4), ' ]', 'failed at #14') + call g:assert.equals(getline(5), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), ' foo', 'failed at #15') + call g:assert.equals(getline(4), ' ]', 'failed at #15') + call g:assert.equals(getline(5), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('replace', 'char', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), ' foo', 'failed at #16') + call g:assert.equals(getline(4), ' ]', 'failed at #16') + call g:assert.equals(getline(5), ' }', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), ' foo', 'failed at #17') + call g:assert.equals(getline(4), ' ]', 'failed at #17') + call g:assert.equals(getline(5), ' }', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), ' foo', 'failed at #18') + call g:assert.equals(getline(4), ' ]', 'failed at #18') + call g:assert.equals(getline(5), ' }', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), ' foo', 'failed at #19') + call g:assert.equals(getline(4), ' ]', 'failed at #19') + call g:assert.equals(getline(5), ' }', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), ' foo', 'failed at #20') + call g:assert.equals(getline(4), ' ]', 'failed at #20') + call g:assert.equals(getline(5), ' }', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('replace', 'char', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), ' foo', 'failed at #21') + " call g:assert.equals(getline(4), ' ]', 'failed at #21') + call g:assert.equals(getline(5), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), ' foo', 'failed at #22') + " call g:assert.equals(getline(4), ' ]', 'failed at #22') + call g:assert.equals(getline(5), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), ' foo', 'failed at #23') + " call g:assert.equals(getline(4), ' ]', 'failed at #23') + call g:assert.equals(getline(5), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), ' foo', 'failed at #24') + " call g:assert.equals(getline(4), ' ]', 'failed at #24') + call g:assert.equals(getline(5), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), ' foo', 'failed at #25') + " call g:assert.equals(getline(4), ' ]', 'failed at #25') + call g:assert.equals(getline(5), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + normal ^sr2i"0 + call g:assert.equals(getline(1), ' {', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), 'foo', 'failed at #26') + call g:assert.equals(getline(4), ']', 'failed at #26') + call g:assert.equals(getline(5), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') +endfunction +"}}} +function! s:suite.charwise_n_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n", "\n}"], 'input': ['a']}, + \ {'buns': ["{\n", "\n}"], 'indentkeys': '0{,0},0),:,0#,!^F,e', 'input': ['1']}, + \ ] + + """ cinkeys + call operator#sandwich#set('replace', 'char', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', 3) + + " #2 + setlocal cinkeys=0{,0},0),:,0#,!^F,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys+', 'O,o') + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys-', 'O,o') + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' "foo"') + normal ^sr2i"1 + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('replace', 'char', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0{,0},0),:,0#,!^F,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys+', 'O,o') + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys-', 'O,o') + call setline('.', ' "foo"') + normal ^sr2i"a + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' "foo"') + normal ^sr2i"1 + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +function! s:suite.charwise_x_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + normal 0va(sr[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0va[sr{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal 0va{sr< + call g:assert.equals(getline('.'), '', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #3') + + " #4 + call setline('.', '') + normal 0va + call g:assert.equals(getline('.'), '', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #7') + + " #8 + call setline('.', '') + normal 0vafoo') + normal 0v$sr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call setline('.', '(foo)') + normal 0v$sr" + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.charwise_x_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + " #1 + call setline('.', 'aαa') + normal 0v2lsr( + call g:assert.equals(getline('.'), '(α)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, strlen('(α)')+1, 0], 'failed at #1') + + " #2 + call setline('.', 'aaαa') + normal 0v3lsr( + call g:assert.equals(getline('.'), '(aα)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, strlen('(aα)')+1, 0], 'failed at #2') + + " #3 + call setline('.', 'βαβ') + normal 0v2lsr( + call g:assert.equals(getline('.'), '(α)', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, strlen('(α)')+1, 0], 'failed at #3') + + " #4 + call setline('.', 'βaαβ') + normal 0v3lsr( + call g:assert.equals(getline('.'), '(aα)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, strlen('(aα)')+1, 0], 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #5 + call setline('.', 'aaa') + normal 0v2lsra + call g:assert.equals(getline('.'), 'αaα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaα')+1, 0], 'failed at #5') + + " #6 + call setline('.', 'aαa') + normal 0v2lsra + call g:assert.equals(getline('.'), 'ααα', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 1, strlen('ααα')+1, 0], 'failed at #6') + + " #7 + call setline('.', 'aaαa') + normal 0v3lsra + call g:assert.equals(getline('.'), 'αaαα', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaαα')+1, 0], 'failed at #7') + + " #8 + call setline('.', 'βaβ') + normal 0v2lsra + call g:assert.equals(getline('.'), 'αaα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaα')+1, 0], 'failed at #8') + + " #9 + call setline('.', 'βαβ') + normal 0v2lsra + call g:assert.equals(getline('.'), 'ααα', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 1, strlen('ααα')+1, 0], 'failed at #9') + + " #10 + call setline('.', 'βaαβ') + normal 0v3lsra + call g:assert.equals(getline('.'), 'αaαα', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 1, strlen('αaαα')+1, 0], 'failed at #10') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #11 + call setline('.', 'aaa') + normal 0v2lsra + call g:assert.equals(getline('.'), 'aαaaα', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaaα')+1, 0], 'failed at #11') + + " #12 + call setline('.', 'aαa') + normal 0v2lsra + call g:assert.equals(getline('.'), 'aααaα', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 1, strlen('aααaα')+1, 0], 'failed at #12') + + " #13 + call setline('.', 'aaαa') + normal 0v3lsra + call g:assert.equals(getline('.'), 'aαaαaα', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaαaα')+1, 0], 'failed at #13') + + " #14 + call setline('.', 'βaβ') + normal 0v2lsra + call g:assert.equals(getline('.'), 'aαaaα', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaaα')+1, 0], 'failed at #14') + + " #15 + call setline('.', 'βαβ') + normal 0v2lsra + call g:assert.equals(getline('.'), 'aααaα', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 1, strlen('aααaα')+1, 0], 'failed at #15') + + " #16 + call setline('.', 'βaαβ') + normal 0v3lsra + call g:assert.equals(getline('.'), 'aαaαaα', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 1, strlen('aαaαaα')+1, 0], 'failed at #16') + + unlet g:operator#sandwich#recipes + + " #17 + call setline('.', 'a“a') + normal 0v2lsr( + call g:assert.equals(getline('.'), '(“)', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 1, strlen('(“)')+1, 0], 'failed at #17') + + " #18 + call setline('.', 'aa“a') + normal 0v3lsr( + call g:assert.equals(getline('.'), '(a“)', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 1, strlen('(a“)')+1, 0], 'failed at #18') + + " #19 + call setline('.', 'β“β') + normal 0v2lsr( + call g:assert.equals(getline('.'), '(“)', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 1, strlen('(“)')+1, 0], 'failed at #19') + + " #20 + call setline('.', 'βa“β') + normal 0v3lsr( + call g:assert.equals(getline('.'), '(a“)', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 1, strlen('(a“)')+1, 0], 'failed at #20') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #21 + call setline('.', 'aaa') + normal 0v2lsra + call g:assert.equals(getline('.'), '“a“', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a“')+1, 0], 'failed at #21') + + " #22 + call setline('.', 'a“a') + normal 0v2lsra + call g:assert.equals(getline('.'), '“““', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 1, strlen('“““')+1, 0], 'failed at #22') + + " #23 + call setline('.', 'aa“a') + normal 0v3lsra + call g:assert.equals(getline('.'), '“a““', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a““')+1, 0], 'failed at #23') + + " #24 + call setline('.', 'βaβ') + normal 0v2lsra + call g:assert.equals(getline('.'), '“a“', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a“')+1, 0], 'failed at #24') + + " #25 + call setline('.', 'β“β') + normal 0v2lsra + call g:assert.equals(getline('.'), '“““', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 1, strlen('“““')+1, 0], 'failed at #25') + + " #26 + call setline('.', 'βa“β') + normal 0v3lsra + call g:assert.equals(getline('.'), '“a““', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 1, strlen('“a““')+1, 0], 'failed at #26') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #27 + call setline('.', 'aaa') + normal 0v2lsra + call g:assert.equals(getline('.'), 'a“aa“', 'failed at #27') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #27') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #27') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“aa“')+1, 0], 'failed at #27') + + " #28 + call setline('.', 'a“a') + normal 0v2lsra + call g:assert.equals(getline('.'), 'a““a“', 'failed at #28') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #28') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #28') + call g:assert.equals(getpos("']"), [0, 1, strlen('a““a“')+1, 0], 'failed at #28') + + " #29 + call setline('.', 'aa“a') + normal 0v3lsra + call g:assert.equals(getline('.'), 'a“a“a“', 'failed at #29') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #29') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #29') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“a“a“')+1, 0], 'failed at #29') + + " #30 + call setline('.', 'βaβ') + normal 0v2lsra + call g:assert.equals(getline('.'), 'a“aa“', 'failed at #30') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #30') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #30') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“aa“')+1, 0], 'failed at #30') + + " #31 + call setline('.', 'β“β') + normal 0v2lsra + call g:assert.equals(getline('.'), 'a““a“', 'failed at #31') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #31') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #31') + call g:assert.equals(getpos("']"), [0, 1, strlen('a““a“')+1, 0], 'failed at #31') + + " #32 + call setline('.', 'βa“β') + normal 0v3lsra + call g:assert.equals(getline('.'), 'a“a“a“', 'failed at #32') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #32') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #32') + call g:assert.equals(getpos("']"), [0, 1, strlen('a“a“a“')+1, 0], 'failed at #32') +endfunction +"}}} +function! s:suite.charwise_x_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call setline('.', '(((foo)))') + normal 0lv%2sr[[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #1') + + " #2 + normal 0va(sr[ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + + " #3 + let g:operator#sandwich#recipes = [{'buns': ["[\n ", "\n]"], 'input':['a']}] + call setline('.', '(foo)') + normal 0sra(a + call g:assert.equals(getline(1), '[', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ']', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + unlet! g:operator#sandwich#recipes + + %delete + + """ inner_head + " #4 + call operator#sandwich#set('replace', 'char', 'cursor', 'inner_head') + call setline('.', '(((foo)))') + normal 0lv%2sr[[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #4') + + " #5 + normal 0va(sr[ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + + """ keep + " #6 + call operator#sandwich#set('replace', 'char', 'cursor', 'keep') + call setline('.', '(((foo)))') + normal 0lva(2sr[[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #6') + + " #7 + normal va(sr[ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #7') + + """ inner_tail + " #8 + call operator#sandwich#set('replace', 'char', 'cursor', 'inner_tail') + call setline('.', '(((foo)))') + normal 0lva(2sr[[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #8') + + " #9 + normal va(sr[ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #9') + + """ head + " #10 + call operator#sandwich#set('replace', 'char', 'cursor', 'head') + call setline('.', '(((foo)))') + normal 0lva(2sr[[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + + " #11 + normal va(sr[ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + + """ tail + " #12 + call operator#sandwich#set('replace', 'char', 'cursor', 'tail') + call setline('.', '(((foo)))') + normal 0lva(2sr[[ + call g:assert.equals(getline('.'), '([[foo]])', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #12') + + " #13 + normal va(sr[ + call g:assert.equals(getline('.'), '[[[foo]]]', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('replace', 'char', 'cursor', 'inner_tail') + call setline('.', '[foo]') + normal 0va[sr1 + call g:assert.equals(getline('.'), '(foo)', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.charwise_x_option_noremap() abort "{{{ + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '{(foo)}') + normal 0v6lsr" + call g:assert.equals(getline('.'), '"(foo)"', 'failed at #1') + + " #2 + call setline('.', '{(foo)}') + normal 0lv4lsr" + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #2') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call setline('.', '{(foo)}') + normal 0v6lsr" + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #3') + + " #4 + call setline('.', '{(foo)}') + normal 0lv4lsr" + call g:assert.equals(getline('.'), '{"foo"}', 'failed at #4') + + """ off + call operator#sandwich#set('replace', 'char', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '{(foo)}') + normal 0v6lsr" + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #5') + + " #6 + call setline('.', '{(foo)}') + normal 0lv4lsr" + call g:assert.equals(getline('.'), '{"foo"}', 'failed at #6') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call setline('.', '{(foo)}') + normal 0v6lsr" + call g:assert.equals(getline('.'), '"(foo)"', 'failed at #7') + + " #8 + call setline('.', '{(foo)}') + normal 0lv4lsr" + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #8') +endfunction +"}}} +function! s:suite.charwise_x_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+foo\d\+') + normal 0v$sr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #1') + + " #2 + call setline('.', '888foo888') + normal 0v$sr" + call g:assert.equals(getline('.'), '"88foo88"', 'failed at #2') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call setline('.', '\d\+foo\d\+') + normal 0v$sr" + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #3') + + " #4 + call setline('.', '888foo888') + normal 0v$sr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + """ on + call operator#sandwich#set('replace', 'char', 'regex', 1) + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #3 + call setline('.', '\d\+foo\d\+') + normal 0v$sr" + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #3') + + " #4 + call setline('.', '888foo888') + normal 0v$sr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call setline('.', '\d\+foo\d\+') + normal 0v$sr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #7') + + " #8 + call setline('.', '888foo888') + normal 0v$sr" + call g:assert.equals(getline('.'), '"88foo88"', 'failed at #8') +endfunction +"}}} +function! s:suite.charwise_x_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + + """ 1 + " #1 + call setline('.', '"foo"') + normal 0v$sr( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', ' "foo"') + normal 0v$sr( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #2') + + " #3 + call setline('.', '"foo" ') + normal 0v$sr( + call g:assert.equals(getline('.'), '(foo) ', 'failed at #3') + + " #4 + " do not skip! + call setline('.', ' "foo" ') + normal 0v$sr( + call g:assert.equals(getline('.'), '("foo")', 'failed at #4') + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0v$sr( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #5') + + """ 2 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'char', 'skip_space', 2) + + " #6 + call setline('.', '"foo"') + normal 0v$sr( + call g:assert.equals(getline('.'), '(foo)', 'failed at #6') + + " #7 + call setline('.', ' "foo"') + normal 0v$sr( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #7') + + " #8 + call setline('.', '"foo" ') + normal 0v$sr( + call g:assert.equals(getline('.'), '(foo) ', 'failed at #8') + + " #9 + call setline('.', ' "foo" ') + normal 0v$sr( + call g:assert.equals(getline('.'), ' (foo) ', 'failed at #9') + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0v$sr( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #10') + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'char', 'skip_space', 0) + + " #11 + call setline('.', '"foo"') + normal 0v$sr( + call g:assert.equals(getline('.'), '(foo)', 'failed at #11') + + " #12 + call setline('.', ' "foo"') + normal 0v$sr( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #12') + + " #13 + call setline('.', '"foo" ') + normal 0v$sr( + call g:assert.equals(getline('.'), '"foo" ', 'failed at #13') + + " #14 + " do not skip! + call setline('.', ' "foo" ') + normal 0v$sr( + call g:assert.equals(getline('.'), '("foo")', 'failed at #14') + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0v$sr( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #15') +endfunction +"}}} +function! s:suite.charwise_x_option_skip_char() abort "{{{ + """ off + " #1 + call setline('.', 'aa(foo)bb') + normal 0v$sr" + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #1') + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call setline('.', 'aa(foo)bb') + normal 0v$sr" + call g:assert.equals(getline('.'), 'aa"foo"bb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + """ on + call operator#sandwich#set('replace', 'char', 'skip_char', 1) + + " #2 + call setline('.', 'aa(foo)bb') + normal 0v$sr" + call g:assert.equals(getline('.'), 'aa"foo"bb', 'failed at #2') + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call setline('.', 'aa(foo)bb') + normal 0v$sr" + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.charwise_x_option_command() abort "{{{ + " #1 + call operator#sandwich#set('replace', 'char', 'command', ['normal! `[d`]']) + call setline('.', '[(foo)]') + normal 0ffva(sr" + call g:assert.equals(getline('.'), '[]', 'failed at #1') + + " #2 + call operator#sandwich#set('replace', 'char', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call setline('.', '[(foo)]') + normal 0ffsra(" + call g:assert.equals(getline('.'), '[]', 'failed at #2') +endfunction +"}}} +function! s:suite.charwise_x_option_linewise() abort "{{{ + set whichwrap=h,l + call operator#sandwich#set('replace', 'char', 'linewise', 1) + + """ 1 + " #1 + call append(0, ['(', 'foo', ')']) + normal ggv6lsr[ + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ']', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['( ', 'foo', ' )']) + normal ggv10lsr[ + call g:assert.equals(getline(1), '[', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), ']', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggv10lsr[ + call g:assert.equals(getline(1), '[aa', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), 'aa]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa', 'foo', ')']) + normal ggv8lsr[ + call g:assert.equals(getline(1), '[aa', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), ']', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', 'aa)']) + normal ggv8lsr[ + call g:assert.equals(getline(1), '[', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), 'aa]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #5') + + %delete + + " #6 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggv6lsr[ + call g:assert.equals(getline(1), '[', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), ']', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #6') + unlet! g:operator#sandwich#recipes + + """ 2 + %delete + call operator#sandwich#set('replace', 'char', 'linewise', 2) + + " #7 + call append(0, ['(', 'foo', ')']) + normal ggv6lsr[ + call g:assert.equals(getline(1), '[', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ']', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['( ', 'foo', ' )']) + normal ggv10lsr[ + call g:assert.equals(getline(1), '[', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ']', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggv10lsr[ + call g:assert.equals(getline(1), '[', 'failed at #9') + call g:assert.equals(getline(2), 'foo', 'failed at #9') + call g:assert.equals(getline(3), ']', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['aa', '(foo)', 'bb']) + normal ggjv4lsr[ + call g:assert.equals(getline(1), 'aa', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), ']', 'failed at #10') + call g:assert.equals(getline(4), 'bb', 'failed at #10') + call g:assert.equals(getline(5), '', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #10') + + %delete + + " #11 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggsr7l[ + call g:assert.equals(getline(1), '[', 'failed at #11') + call g:assert.equals(getline(2), 'foo', 'failed at #11') + call g:assert.equals(getline(3), ']', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #11') + unlet! g:operator#sandwich#recipes + + set whichwrap& +endfunction +"}}} +function! s:suite.charwise_x_option_query_once() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call setline('.', '"""foo"""') + normal 0v$3sr([{ + call g:assert.equals(getline('.'), '([{foo}])', 'failed at #1') + + " #2 + call setline('.', '"""foo"""') + normal 0v$3sr1 + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + """ on + " #3 + call operator#sandwich#set('replace', 'char', 'query_once', 1) + call setline('.', '"""foo"""') + normal 0v$3sr( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #3') + + " #4 + call setline('.', '"""foo"""') + normal 0v$3sr0[{ + call g:assert.equals(getline('.'), '([{foo}])', 'failed at #4') +endfunction +"}}} +function! s:suite.charwise_x_option_expr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', '"foo"') + normal 0va"sra + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #1') + + " #2 + call setline('.', '"foo"') + normal 0va"sr1 + call g:assert.equals(getline('.'), '2foo3', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'char', 'expr', 1) + call setline('.', '"foo"') + normal 0va"sra + call g:assert.equals(getline('.'), '2foo3', 'failed at #3') + + " #4 + call setline('.', '"foo"') + normal 0va"srb + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', '"foo"') + normal 0va"src + call g:assert.equals(getline('.'), '"foo"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', '"''foo''"') + normal 0va"2srab + call g:assert.equals(getline('.'), '2''foo''3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + " #7 + call setline('.', '"''foo''"') + normal 0va"2srac + call g:assert.equals(getline('.'), '2''foo''3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + " #8 + call setline('.', '"''foo''"') + normal 0va"2srba + call g:assert.equals(getline('.'), '"''foo''"', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + " #9 + call setline('.', '"''foo''"') + normal 0va"2srca + call g:assert.equals(getline('.'), '"''foo''"', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + " #10 + call setline('.', '"foo"') + normal 0va"sr0 + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.charwise_x_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0va"sra + call g:assert.equals(getline('.'), '"bar"', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + " #2 + call setline('.', '"bar"') + normal 0va"sr1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'char', 'listexpr', 1) + call setline('.', '"bar"') + normal 0va"sra + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + + " #4 + call setline('.', '"bar"') + normal 0va"srb + call g:assert.equals(getline('.'), '"bar"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0va"sr0 + call g:assert.equals(getline('.'), '"bar"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', '"bar"') + normal 0va"sr1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.charwise_x_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + + """ -1 + call operator#sandwich#set('replace', 'char', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ']', 'failed at #1') + call g:assert.equals(getline(5), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), ' foo', 'failed at #2') + call g:assert.equals(getline(4), ' ]', 'failed at #2') + call g:assert.equals(getline(5), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), ' foo', 'failed at #3') + call g:assert.equals(getline(4), ' ]', 'failed at #3') + call g:assert.equals(getline(5), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), ' foo', 'failed at #4') + " call g:assert.equals(getline(4), ' ]', 'failed at #4') + call g:assert.equals(getline(5), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call setline('.', ' "foo"') + " normal ^v2i"sra + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), ' foo', 'failed at #5') + " call g:assert.equals(getline(4), ' ]', 'failed at #5') + " call g:assert.equals(getline(5), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 3, 17, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 5, 34, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('replace', 'char', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ']', 'failed at #6') + call g:assert.equals(getline(5), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), 'foo', 'failed at #7') + call g:assert.equals(getline(4), ']', 'failed at #7') + call g:assert.equals(getline(5), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), 'foo', 'failed at #8') + call g:assert.equals(getline(4), ']', 'failed at #8') + call g:assert.equals(getline(5), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), 'foo', 'failed at #9') + call g:assert.equals(getline(4), ']', 'failed at #9') + call g:assert.equals(getline(5), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), 'foo', 'failed at #10') + call g:assert.equals(getline(4), ']', 'failed at #10') + call g:assert.equals(getline(5), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('replace', 'char', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), ' foo', 'failed at #11') + call g:assert.equals(getline(4), ' ]', 'failed at #11') + call g:assert.equals(getline(5), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), ' foo', 'failed at #12') + call g:assert.equals(getline(4), ' ]', 'failed at #12') + call g:assert.equals(getline(5), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), ' foo', 'failed at #13') + call g:assert.equals(getline(4), ' ]', 'failed at #13') + call g:assert.equals(getline(5), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), ' foo', 'failed at #14') + call g:assert.equals(getline(4), ' ]', 'failed at #14') + call g:assert.equals(getline(5), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), ' foo', 'failed at #15') + call g:assert.equals(getline(4), ' ]', 'failed at #15') + call g:assert.equals(getline(5), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('replace', 'char', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), ' foo', 'failed at #16') + call g:assert.equals(getline(4), ' ]', 'failed at #16') + call g:assert.equals(getline(5), ' }', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), ' foo', 'failed at #17') + call g:assert.equals(getline(4), ' ]', 'failed at #17') + call g:assert.equals(getline(5), ' }', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), ' foo', 'failed at #18') + call g:assert.equals(getline(4), ' ]', 'failed at #18') + call g:assert.equals(getline(5), ' }', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), ' foo', 'failed at #19') + call g:assert.equals(getline(4), ' ]', 'failed at #19') + call g:assert.equals(getline(5), ' }', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), ' foo', 'failed at #20') + call g:assert.equals(getline(4), ' ]', 'failed at #20') + call g:assert.equals(getline(5), ' }', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('replace', 'char', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), ' foo', 'failed at #21') + " call g:assert.equals(getline(4), ' ]', 'failed at #21') + call g:assert.equals(getline(5), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), ' foo', 'failed at #22') + " call g:assert.equals(getline(4), ' ]', 'failed at #22') + call g:assert.equals(getline(5), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), ' foo', 'failed at #23') + " call g:assert.equals(getline(4), ' ]', 'failed at #23') + call g:assert.equals(getline(5), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), ' foo', 'failed at #24') + " call g:assert.equals(getline(4), ' ]', 'failed at #24') + call g:assert.equals(getline(5), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), ' foo', 'failed at #25') + " call g:assert.equals(getline(4), ' ]', 'failed at #25') + call g:assert.equals(getline(5), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + normal ^v2i"sr0 + call g:assert.equals(getline(1), ' {', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), 'foo', 'failed at #26') + call g:assert.equals(getline(4), ']', 'failed at #26') + call g:assert.equals(getline(5), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') +endfunction +"}}} +function! s:suite.charwise_x_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n", "\n}"], 'input': ['a']}, + \ {'buns': ["{\n", "\n}"], 'indentkeys': '0{,0},0),:,0#,!^F,e', 'input': ['1']}, + \ ] + + """ cinkeys + call operator#sandwich#set('replace', 'char', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', 3) + + " #2 + setlocal cinkeys=0{,0},0),:,0#,!^F,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys+', 'O,o') + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys-', 'O,o') + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' "foo"') + normal ^v2i"sr1 + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('replace', 'char', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0{,0},0),:,0#,!^F,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys+', 'O,o') + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'char', 'indentkeys-', 'O,o') + call setline('.', ' "foo"') + normal ^v2i"sra + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'char', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' "foo"') + normal ^v2i"sr1 + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +" line-wise +function! s:suite.linewise_n_default_recipes() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call setline('.', '(foo)') + normal srVl[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal srVl{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal srVl< + call g:assert.equals(getline('.'), '', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #3') + + " #4 + call setline('.', '') + normal srVl( + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', ')']) + normal ggsr2j] + call g:assert.equals(getline(1), '[', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ']', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #5') + + " #6 + call append(0, ['[', 'foo', ']']) + normal ggsr2j} + call g:assert.equals(getline(1), '{', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #6') + + " #7 + call append(0, ['{', 'foo', '}']) + normal ggsr2j> + call g:assert.equals(getline(1), '<', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), '>', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #7') + + " #8 + call append(0, ['<', 'foo', '>']) + normal ggsr2j) + call g:assert.equals(getline(1), '(', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ')', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_n_not_registered() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call setline('.', 'afooa') + normal srVlb + call g:assert.equals(getline('.'), 'bfoob', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', '+foo+') + normal srVl* + call g:assert.equals(getline('.'), '*foo*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + %delete + + " #1 + call append(0, ['a', 'foo', 'a']) + normal ggsr2jb + call g:assert.equals(getline(1), 'b', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), 'b', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['+', 'foo', '+']) + normal ggsr2j* + call g:assert.equals(getline(1), '*', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), '*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_n_positioning() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call append(0, ['(', 'foo', 'bar', 'baz', ')']) + normal ggsrVa([ + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), 'bar', 'failed at #1') + call g:assert.equals(getline(4), 'baz', 'failed at #1') + call g:assert.equals(getline(5), ']', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', '(', 'bar', ')', 'baz']) + normal gg2jsrVa([ + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), '[', 'failed at #2') + call g:assert.equals(getline(3), 'bar', 'failed at #2') + call g:assert.equals(getline(4), ']', 'failed at #2') + call g:assert.equals(getline(5), 'baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 4, 2, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(foo', 'bar', 'baz)']) + normal ggsrVa([ + call g:assert.equals(getline(1), '[foo', 'failed at #3') + call g:assert.equals(getline(2), 'bar', 'failed at #3') + call g:assert.equals(getline(3), 'baz]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #3') +endfunction +"}}} +function! s:suite.linewise_n_nothing_inside() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call setline('.', '()') + normal srVa([ + call g:assert.equals(getline('.'), '[]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 3, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(', ')']) + normal ggsrVa([ + call g:assert.equals(getline(1), '[', 'failed at #2') + call g:assert.equals(getline(2), ']', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 2, 2, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_n_count() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call setline('.', '([foo])') + normal 2srVl[( + call g:assert.equals(getline('.'), '[(foo)]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 8, 0], 'failed at #1') + + " #2 + call setline('.', '[({foo})]') + normal 3srVl{[( + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 10, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['foo', '{', '[', '(', 'bar', ')', ']', '}', 'baz']) + normal ggj3sr6j({[ + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getline(2), '(', 'failed at #3') + call g:assert.equals(getline(3), '{', 'failed at #3') + call g:assert.equals(getline(4), '[', 'failed at #3') + call g:assert.equals(getline(5), 'bar', 'failed at #3') + call g:assert.equals(getline(6), ']', 'failed at #3') + call g:assert.equals(getline(7), '}', 'failed at #3') + call g:assert.equals(getline(8), ')', 'failed at #3') + call g:assert.equals(getline(9), 'baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 5, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 8, 2, 0], 'failed at #3') +endfunction +"}}} +function! s:suite.linewise_n_breaking() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #1 + call append(0, ['aa', 'aaafooaaa', 'aa']) + normal ggsr2j( + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['bb', 'bbb', 'bbfoobb', 'bbb', 'bb']) + normal ggsr4j( + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + %delete + + " #3 + call setline('.', '(foo)') + normal srVla + call g:assert.equals(getline(1), 'aa', 'failed at #3') + call g:assert.equals(getline(2), 'aaafooaaa', 'failed at #3') + call g:assert.equals(getline(3), 'aa', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', '(foo)') + normal srVlb + call g:assert.equals(getline(1), 'bb', 'failed at #4') + call g:assert.equals(getline(2), 'bbb', 'failed at #4') + call g:assert.equals(getline(3), 'bbfoobb', 'failed at #4') + call g:assert.equals(getline(4), 'bbb', 'failed at #4') + call g:assert.equals(getline(5), 'bb', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['aa', 'aaa', 'aa', 'aaa', 'foo', 'aaa', 'aa', 'aaa', 'aa']) + normal gg2sr8j(( + call g:assert.equals(getline(1), '(', 'failed at #5') + call g:assert.equals(getline(2), '(', 'failed at #5') + call g:assert.equals(getline(3), 'foo', 'failed at #5') + call g:assert.equals(getline(4), ')', 'failed at #5') + call g:assert.equals(getline(5), ')', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['bb', 'bbb', 'bb', 'bb', 'bbb', 'bb', 'foo', 'bb', 'bbb', 'bb', 'bb', 'bbb', 'bb']) + normal gg2sr12j(( + call g:assert.equals(getline(1), '(', 'failed at #6') + call g:assert.equals(getline(2), '(', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ')', 'failed at #6') + call g:assert.equals(getline(5), ')', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #6') + + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_n_external_textobj() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call setline('.', '(foo)') + normal srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call setline('.', '(foo)') + normal 0srVl" + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_n_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call append(0, ['a', 'α', 'a']) + normal ggsr2j( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'α', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + " #2 + call append(0, ['a', 'aα', 'a']) + normal ggsr2j( + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'aα', 'failed at #2') + call g:assert.equals(getline(3), ')', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + + " #3 + call append(0, ['β', 'α', 'β']) + normal ggsr2j( + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), 'α', 'failed at #3') + call g:assert.equals(getline(3), ')', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + + " #4 + call append(0, ['β', 'aα', 'β']) + normal ggsr2j( + call g:assert.equals(getline(1), '(', 'failed at #4') + call g:assert.equals(getline(2), 'aα', 'failed at #4') + call g:assert.equals(getline(3), ')', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #5 + call append(0, ['a', 'a', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), 'α', 'failed at #5') + call g:assert.equals(getline(2), 'a', 'failed at #5') + call g:assert.equals(getline(3), 'α', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #5') + + " #6 + call append(0, ['a', 'α', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), 'α', 'failed at #6') + call g:assert.equals(getline(2), 'α', 'failed at #6') + call g:assert.equals(getline(3), 'α', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #6') + + " #7 + call append(0, ['a', 'aα', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), 'α', 'failed at #7') + call g:assert.equals(getline(2), 'aα', 'failed at #7') + call g:assert.equals(getline(3), 'α', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #7') + + " #8 + call append(0, ['β', 'a', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), 'α', 'failed at #8') + call g:assert.equals(getline(2), 'a', 'failed at #8') + call g:assert.equals(getline(3), 'α', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #8') + + " #9 + call append(0, ['β', 'α', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), 'α', 'failed at #9') + call g:assert.equals(getline(2), 'α', 'failed at #9') + call g:assert.equals(getline(3), 'α', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #9') + + " #10 + call append(0, ['β', 'aα', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), 'α', 'failed at #10') + call g:assert.equals(getline(2), 'aα', 'failed at #10') + call g:assert.equals(getline(3), 'α', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #10') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #11 + call append(0, ['a', 'a', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), 'aα', 'failed at #11') + call g:assert.equals(getline(2), 'a', 'failed at #11') + call g:assert.equals(getline(3), 'aα', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #11') + + " #12 + call append(0, ['a', 'α', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), 'aα', 'failed at #12') + call g:assert.equals(getline(2), 'α', 'failed at #12') + call g:assert.equals(getline(3), 'aα', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #12') + + " #13 + call append(0, ['a', 'aα', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), 'aα', 'failed at #13') + call g:assert.equals(getline(2), 'aα', 'failed at #13') + call g:assert.equals(getline(3), 'aα', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #13') + + " #14 + call append(0, ['β', 'a', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), 'aα', 'failed at #14') + call g:assert.equals(getline(2), 'a', 'failed at #14') + call g:assert.equals(getline(3), 'aα', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #14') + + " #15 + call append(0, ['β', 'α', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), 'aα', 'failed at #15') + call g:assert.equals(getline(2), 'α', 'failed at #15') + call g:assert.equals(getline(3), 'aα', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #15') + + " #16 + call append(0, ['β', 'aα', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), 'aα', 'failed at #16') + call g:assert.equals(getline(2), 'aα', 'failed at #16') + call g:assert.equals(getline(3), 'aα', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #16') + + unlet g:operator#sandwich#recipes + + " #17 + call append(0, ['a', '“', 'a']) + normal ggsr2j( + call g:assert.equals(getline(1), '(', 'failed at #17') + call g:assert.equals(getline(2), '“', 'failed at #17') + call g:assert.equals(getline(3), ')', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #17') + + " #18 + call append(0, ['a', 'a“', 'a']) + normal ggsr2j( + call g:assert.equals(getline(1), '(', 'failed at #18') + call g:assert.equals(getline(2), 'a“', 'failed at #18') + call g:assert.equals(getline(3), ')', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #18') + + " #19 + call append(0, ['β', '“', 'β']) + normal ggsr2j( + call g:assert.equals(getline(1), '(', 'failed at #19') + call g:assert.equals(getline(2), '“', 'failed at #19') + call g:assert.equals(getline(3), ')', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #19') + + " #20 + call append(0, ['β', 'a“', 'β']) + normal ggsr2j( + call g:assert.equals(getline(1), '(', 'failed at #20') + call g:assert.equals(getline(2), 'a“', 'failed at #20') + call g:assert.equals(getline(3), ')', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #20') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #21 + call append(0, ['a', 'a', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), '“', 'failed at #21') + call g:assert.equals(getline(2), 'a', 'failed at #21') + call g:assert.equals(getline(3), '“', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #21') + + " #22 + call append(0, ['a', '“', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), '“', 'failed at #22') + call g:assert.equals(getline(2), '“', 'failed at #22') + call g:assert.equals(getline(3), '“', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #22') + + " #23 + call append(0, ['a', 'a“', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), '“', 'failed at #23') + call g:assert.equals(getline(2), 'a“', 'failed at #23') + call g:assert.equals(getline(3), '“', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #23') + + " #24 + call append(0, ['β', 'a', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), '“', 'failed at #24') + call g:assert.equals(getline(2), 'a', 'failed at #24') + call g:assert.equals(getline(3), '“', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #24') + + " #25 + call append(0, ['β', '“', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), '“', 'failed at #25') + call g:assert.equals(getline(2), '“', 'failed at #25') + call g:assert.equals(getline(3), '“', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #25') + + " #26 + call append(0, ['β', 'a“', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), '“', 'failed at #26') + call g:assert.equals(getline(2), 'a“', 'failed at #26') + call g:assert.equals(getline(3), '“', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #26') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #27 + call append(0, ['a', 'a', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), 'a“', 'failed at #27') + call g:assert.equals(getline(2), 'a', 'failed at #27') + call g:assert.equals(getline(3), 'a“', 'failed at #27') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #27') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #27') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #27') + + " #28 + call append(0, ['a', '“', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), 'a“', 'failed at #28') + call g:assert.equals(getline(2), '“', 'failed at #28') + call g:assert.equals(getline(3), 'a“', 'failed at #28') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #28') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #28') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #28') + + " #29 + call append(0, ['a', 'a“', 'a']) + normal ggsr2ja + call g:assert.equals(getline(1), 'a“', 'failed at #29') + call g:assert.equals(getline(2), 'a“', 'failed at #29') + call g:assert.equals(getline(3), 'a“', 'failed at #29') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #29') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #29') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #29') + + " #30 + call append(0, ['β', 'a', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), 'a“', 'failed at #30') + call g:assert.equals(getline(2), 'a', 'failed at #30') + call g:assert.equals(getline(3), 'a“', 'failed at #30') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #30') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #30') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #30') + + " #31 + call append(0, ['β', '“', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), 'a“', 'failed at #31') + call g:assert.equals(getline(2), '“', 'failed at #31') + call g:assert.equals(getline(3), 'a“', 'failed at #31') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #31') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #31') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #31') + + " #32 + call append(0, ['β', 'a“', 'β']) + normal ggsr2ja + call g:assert.equals(getline(1), 'a“', 'failed at #32') + call g:assert.equals(getline(2), 'a“', 'failed at #32') + call g:assert.equals(getline(3), 'a“', 'failed at #32') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #32') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #32') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #32') +endfunction +"}}} +function! s:suite.linewise_n_option_cursor() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + """"" cursor + """ default + " #1 + call setline('.', '(((foo)))') + normal 02srVl[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + + " #2 + normal srVl( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + + " #3 + let g:operator#sandwich#recipes = [{'buns': ["[\n ", "\n]"], 'input':['a']}] + call setline('.', '(foo)') + normal srVla + call g:assert.equals(getline(1), '[', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ']', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + unlet! g:operator#sandwich#recipes + + %delete + + """ inner_head + " #4 + call operator#sandwich#set('replace', 'line', 'cursor', 'inner_head') + call setline('.', '(((foo)))') + normal 02srVl[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #4') + + " #5 + normal srVl( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + + """ keep + " #6 + call operator#sandwich#set('replace', 'line', 'cursor', 'keep') + call setline('.', '(((foo)))') + normal 04l2srVl[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #6') + + " #7 + normal lsrVl( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #7') + + """ inner_tail + " #8 + call operator#sandwich#set('replace', 'line', 'cursor', 'inner_tail') + call setline('.', '(((foo)))') + normal 04l2srVl[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #8') + + " #9 + normal hsrVl( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #9') + + """ head + " #10 + call operator#sandwich#set('replace', 'line', 'cursor', 'head') + call setline('.', '(((foo)))') + normal 04l2srVl[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #10') + + " #11 + normal 3lsrVl( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #11') + + """ tail + " #12 + call operator#sandwich#set('replace', 'line', 'cursor', 'tail') + call setline('.', '(((foo)))') + normal 04l2srVl[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #12') + + " #13 + normal 3hsrVl( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #13') + + """"" recipe option + " #14 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('replace', 'line', 'cursor', 'inner_tail') + call setline('.', '[foo]') + normal 0srVl1 + call g:assert.equals(getline('.'), '(foo)', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #14') +endfunction +"}}} +function! s:suite.linewise_n_option_noremap() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '{foo}') + normal 0srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal 0srVl" + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call setline('.', '{foo}') + normal 0srVl" + call g:assert.equals(getline('.'), '{foo}', 'failed at #3') + + " #4 + call setline('.', '(foo)') + normal 0srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + """ off + call operator#sandwich#set('replace', 'line', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '{foo}') + normal 0srVl" + call g:assert.equals(getline('.'), '{foo}', 'failed at #5') + + " #6 + call setline('.', '(foo)') + normal 0srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #6') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call setline('.', '{foo}') + normal 0srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #7') + + " #8 + call setline('.', '(foo)') + normal 0srVl" + call g:assert.equals(getline('.'), '(foo)', 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_n_option_regex() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+foo\d\+') + normal 0srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #1') + + " #2 + call setline('.', '888foo888') + normal 0srVl" + call g:assert.equals(getline('.'), '"88foo88"', 'failed at #2') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call setline('.', '\d\+foo\d\+') + normal 0srVl" + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #3') + + " #4 + call setline('.', '888foo888') + normal 0srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + """ on + call operator#sandwich#set('replace', 'line', 'regex', 1) + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #5 + call setline('.', '\d\+foo\d\+') + normal 0srVl" + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #5') + + " #6 + call setline('.', '888foo888') + normal 0srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #6') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call setline('.', '\d\+foo\d\+') + normal 0srVl" + call g:assert.equals(getline('.'), '"foo"', 'failed at #7') + + " #8 + call setline('.', '888foo888') + normal 0srVl" + call g:assert.equals(getline('.'), '"88foo88"', 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_n_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + """ 2 + " #1 + call setline('.', '"foo"') + normal 0srVl( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', ' "foo"') + normal 0srVl( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #2') + + " #3 + call setline('.', '"foo" ') + normal 0srVl( + call g:assert.equals(getline('.'), '(foo) ', 'failed at #3') + + " #4 + call setline('.', ' "foo" ') + normal 0srVl( + call g:assert.equals(getline('.'), ' (foo) ', 'failed at #4') + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0srVl( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #5') + + """ 1 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'line', 'skip_space', 1) + + " #6 + call setline('.', '"foo"') + normal 0srVl( + call g:assert.equals(getline('.'), '(foo)', 'failed at #6') + + " #7 + call setline('.', ' "foo"') + normal 0srVl( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #7') + + " #8 + call setline('.', '"foo" ') + normal 0srVl( + call g:assert.equals(getline('.'), '(foo) ', 'failed at #8') + + " #9 + " do not skip! + call setline('.', ' "foo" ') + normal 0srVl( + call g:assert.equals(getline('.'), '("foo")', 'failed at #9') + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0srVl( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #10') + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'line', 'skip_space', 0) + + " #11 + call setline('.', '"foo"') + normal 0srVl( + call g:assert.equals(getline('.'), '(foo)', 'failed at #11') + + " #12 + call setline('.', ' "foo"') + normal 0srVl( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #12') + + " #13 + call setline('.', '"foo" ') + normal 0srVl( + call g:assert.equals(getline('.'), '"foo" ', 'failed at #13') + + " #14 + " do not skip! + call setline('.', ' "foo" ') + normal 0srVl( + call g:assert.equals(getline('.'), '("foo")', 'failed at #14') + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0srVl( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #15') +endfunction +"}}} +function! s:suite.linewise_n_option_skip_char() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + """ off + " #1 + call setline('.', 'aa(foo)bb') + normal 0srVl" + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #1') + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call setline('.', 'aa(foo)bb') + normal 0srVl" + call g:assert.equals(getline('.'), 'aa"foo"bb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + """ on + call operator#sandwich#set('replace', 'line', 'skip_char', 1) + + " #3 + call setline('.', 'aa(foo)bb') + normal 0srVl" + call g:assert.equals(getline('.'), 'aa"foo"bb', 'failed at #3') + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call setline('.', 'aa(foo)bb') + normal 0srVl" + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_n_option_command() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call operator#sandwich#set('replace', 'line', 'command', ['normal! `[d`]']) + call append(0, ['[', '(foo)', ']']) + normal ggjsrVl" + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), '', 'failed at #1') + call g:assert.equals(getline(3), ']', 'failed at #1') + + %delete + + " #2 + call operator#sandwich#set('replace', 'line', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call append(0, ['[', '(foo)', ']']) + normal ggjsrVl" + call g:assert.equals(getline(1), '[', 'failed at #2') + call g:assert.equals(getline(2), '', 'failed at #2') + call g:assert.equals(getline(3), ']', 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_n_option_linewise() abort "{{{ + """ 0 + call operator#sandwich#set('replace', 'line', 'linewise', 0) + + " #1 + call append(0, ['(', 'foo', ')']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ']', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['( ', 'foo', ' )']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[ ', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), ' ]', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[aa', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), 'aa]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa', 'foo', ')']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[aa', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), ']', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', 'aa)']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), 'aa]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #5') + + %delete + + " #6 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 1}] + call append(0, ['(', 'foo', ')']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), ']', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #6') + unlet! g:operator#sandwich#recipes + + """ 2 + %delete + call operator#sandwich#set('replace', 'line', 'linewise', 2) + + " #7 + call append(0, ['(', 'foo', ')']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ']', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['( ', 'foo', ' )']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ']', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[', 'failed at #9') + call g:assert.equals(getline(2), 'foo', 'failed at #9') + call g:assert.equals(getline(3), ']', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['aa', '(foo)', 'bb']) + normal ggjsrVl[ + call g:assert.equals(getline(1), 'aa', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), ']', 'failed at #10') + call g:assert.equals(getline(4), 'bb', 'failed at #10') + call g:assert.equals(getline(5), '', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #10') + + %delete + + " #11 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[', 'failed at #11') + call g:assert.equals(getline(2), 'foo', 'failed at #11') + call g:assert.equals(getline(3), ']', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #11') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_n_option_query_once() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call setline('.', '"""foo"""') + normal 03srVl([{ + call g:assert.equals(getline('.'), '([{foo}])', 'failed at #1') + + " #2 + call setline('.', '"""foo"""') + normal 03srVl1 + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + """ on + call operator#sandwich#set('replace', 'line', 'query_once', 1) + + " #3 + call setline('.', '"""foo"""') + normal 03srVl( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #3') + + " #4 + call setline('.', '"""foo"""') + normal 03srVl0[{ + call g:assert.equals(getline('.'), '([{foo}])', 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_n_option_expr() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', '"foo"') + normal 0srVla + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #1') + + " #2 + call setline('.', '"foo"') + normal 0srVl1 + call g:assert.equals(getline('.'), '2foo3', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'line', 'expr', 1) + call setline('.', '"foo"') + normal 0srVla + call g:assert.equals(getline('.'), '2foo3', 'failed at #3') + + " #4 + call setline('.', '"foo"') + normal 0srVlb + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', '"foo"') + normal 0srVlc + call g:assert.equals(getline('.'), '"foo"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', '"''foo''"') + normal 02srVlab + call g:assert.equals(getline('.'), '2''foo''3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + " #7 + call setline('.', '"''foo''"') + normal 02srVlac + call g:assert.equals(getline('.'), '2''foo''3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + " #8 + call setline('.', '"''foo''"') + normal 02srVlba + call g:assert.equals(getline('.'), '"''foo''"', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + " #9 + call setline('.', '"''foo''"') + normal 02srVlca + call g:assert.equals(getline('.'), '"''foo''"', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + " #10 + call setline('.', '"foo"') + normal 0srVl0 + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.linewise_n_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0srVla + call g:assert.equals(getline('.'), '"bar"', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + " #2 + call setline('.', '"bar"') + normal 0srVl1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'line', 'listexpr', 1) + call setline('.', '"bar"') + normal 0srVla + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + + " #4 + call setline('.', '"bar"') + normal 0srVlb + call g:assert.equals(getline('.'), '"bar"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0srVl0 + call g:assert.equals(getline('.'), '"bar"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', '"bar"') + normal 0srVl1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.linewise_n_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + call operator#sandwich#set('replace', 'line', 'linewise', 2) + + """ -1 + call operator#sandwich#set('replace', 'line', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + call g:assert.equals(getline(4), ' foo', 'failed at #1') + call g:assert.equals(getline(5), '', 'failed at #1') + call g:assert.equals(getline(6), ']', 'failed at #1') + call g:assert.equals(getline(7), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), '', 'failed at #2') + call g:assert.equals(getline(4), ' foo', 'failed at #2') + call g:assert.equals(getline(5), '', 'failed at #2') + call g:assert.equals(getline(6), ' ]', 'failed at #2') + call g:assert.equals(getline(7), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), '', 'failed at #3') + call g:assert.equals(getline(4), ' foo', 'failed at #3') + call g:assert.equals(getline(5), '', 'failed at #3') + call g:assert.equals(getline(6), ' ]', 'failed at #3') + call g:assert.equals(getline(7), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), '', 'failed at #4') + call g:assert.equals(getline(4), ' foo', 'failed at #4') + call g:assert.equals(getline(5), '', 'failed at #4') + " call g:assert.equals(getline(6), ' ]', 'failed at #4') + call g:assert.equals(getline(7), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call append(0, [' "', ' foo', ' "']) + " normal ggsr2ja + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), '', 'failed at #5') + " call g:assert.equals(getline(4), ' foo', 'failed at #5') + " call g:assert.equals(getline(5), '', 'failed at #5') + " call g:assert.equals(getline(6), ' ]', 'failed at #5') + " call g:assert.equals(getline(7), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 7, 22, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('replace', 'line', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), '', 'failed at #6') + call g:assert.equals(getline(4), ' foo', 'failed at #6') + call g:assert.equals(getline(5), '', 'failed at #6') + call g:assert.equals(getline(6), ']', 'failed at #6') + call g:assert.equals(getline(7), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), '', 'failed at #7') + call g:assert.equals(getline(4), ' foo', 'failed at #7') + call g:assert.equals(getline(5), '', 'failed at #7') + call g:assert.equals(getline(6), ']', 'failed at #7') + call g:assert.equals(getline(7), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), '', 'failed at #8') + call g:assert.equals(getline(4), ' foo', 'failed at #8') + call g:assert.equals(getline(5), '', 'failed at #8') + call g:assert.equals(getline(6), ']', 'failed at #8') + call g:assert.equals(getline(7), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), '', 'failed at #9') + call g:assert.equals(getline(4), ' foo', 'failed at #9') + call g:assert.equals(getline(5), '', 'failed at #9') + call g:assert.equals(getline(6), ']', 'failed at #9') + call g:assert.equals(getline(7), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), '', 'failed at #10') + call g:assert.equals(getline(4), ' foo', 'failed at #10') + call g:assert.equals(getline(5), '', 'failed at #10') + call g:assert.equals(getline(6), ']', 'failed at #10') + call g:assert.equals(getline(7), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('replace', 'line', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), '', 'failed at #11') + call g:assert.equals(getline(4), ' foo', 'failed at #11') + call g:assert.equals(getline(5), '', 'failed at #11') + call g:assert.equals(getline(6), ' ]', 'failed at #11') + call g:assert.equals(getline(7), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), '', 'failed at #12') + call g:assert.equals(getline(4), ' foo', 'failed at #12') + call g:assert.equals(getline(5), '', 'failed at #12') + call g:assert.equals(getline(6), ' ]', 'failed at #12') + call g:assert.equals(getline(7), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), '', 'failed at #13') + call g:assert.equals(getline(4), ' foo', 'failed at #13') + call g:assert.equals(getline(5), '', 'failed at #13') + call g:assert.equals(getline(6), ' ]', 'failed at #13') + call g:assert.equals(getline(7), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), '', 'failed at #14') + call g:assert.equals(getline(4), ' foo', 'failed at #14') + call g:assert.equals(getline(5), '', 'failed at #14') + call g:assert.equals(getline(6), ' ]', 'failed at #14') + call g:assert.equals(getline(7), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), '', 'failed at #15') + call g:assert.equals(getline(4), ' foo', 'failed at #15') + call g:assert.equals(getline(5), '', 'failed at #15') + call g:assert.equals(getline(6), ' ]', 'failed at #15') + call g:assert.equals(getline(7), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('replace', 'line', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), '', 'failed at #16') + call g:assert.equals(getline(4), ' foo', 'failed at #16') + call g:assert.equals(getline(5), '', 'failed at #16') + call g:assert.equals(getline(6), ' ]', 'failed at #16') + call g:assert.equals(getline(7), '}', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), '', 'failed at #17') + call g:assert.equals(getline(4), ' foo', 'failed at #17') + call g:assert.equals(getline(5), '', 'failed at #17') + call g:assert.equals(getline(6), ' ]', 'failed at #17') + call g:assert.equals(getline(7), '}', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), '', 'failed at #18') + call g:assert.equals(getline(4), ' foo', 'failed at #18') + call g:assert.equals(getline(5), '', 'failed at #18') + call g:assert.equals(getline(6), ' ]', 'failed at #18') + call g:assert.equals(getline(7), '}', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), '', 'failed at #19') + call g:assert.equals(getline(4), ' foo', 'failed at #19') + call g:assert.equals(getline(5), '', 'failed at #19') + call g:assert.equals(getline(6), ' ]', 'failed at #19') + call g:assert.equals(getline(7), '}', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), '', 'failed at #20') + call g:assert.equals(getline(4), ' foo', 'failed at #20') + call g:assert.equals(getline(5), '', 'failed at #20') + call g:assert.equals(getline(6), ' ]', 'failed at #20') + call g:assert.equals(getline(7), '}', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('replace', 'line', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), '', 'failed at #21') + call g:assert.equals(getline(4), ' foo', 'failed at #21') + call g:assert.equals(getline(5), '', 'failed at #21') + " call g:assert.equals(getline(6), ' ]', 'failed at #21') + call g:assert.equals(getline(7), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), '', 'failed at #22') + call g:assert.equals(getline(4), ' foo', 'failed at #22') + call g:assert.equals(getline(5), '', 'failed at #22') + " call g:assert.equals(getline(6), ' ]', 'failed at #22') + call g:assert.equals(getline(7), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), '', 'failed at #23') + call g:assert.equals(getline(4), ' foo', 'failed at #23') + call g:assert.equals(getline(5), '', 'failed at #23') + " call g:assert.equals(getline(6), ' ]', 'failed at #23') + call g:assert.equals(getline(7), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), '', 'failed at #24') + call g:assert.equals(getline(4), ' foo', 'failed at #24') + call g:assert.equals(getline(5), '', 'failed at #24') + " call g:assert.equals(getline(6), ' ]', 'failed at #24') + call g:assert.equals(getline(7), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), '', 'failed at #25') + call g:assert.equals(getline(4), ' foo', 'failed at #25') + call g:assert.equals(getline(5), '', 'failed at #25') + " call g:assert.equals(getline(6), ' ]', 'failed at #25') + call g:assert.equals(getline(7), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call append(0, [' "', ' foo', ' "']) + normal ggsr2j0 + call g:assert.equals(getline(1), '{', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), '', 'failed at #26') + call g:assert.equals(getline(4), ' foo', 'failed at #26') + call g:assert.equals(getline(5), '', 'failed at #26') + call g:assert.equals(getline(6), ']', 'failed at #26') + call g:assert.equals(getline(7), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') + + %delete + + """ 4 + call operator#sandwich#set('replace', 'line', 'autoindent', 4) + + set cinkeys& + set indentkeys& + setlocal indentexpr=TestIndent() + let g:sandwich#recipes = [{'buns': ['bar', 'bar'], 'line': 1, 'autoindent': 4, 'input': ['a']}, {'buns': ['baz', 'baz'], 'line': 1, 'autoindent': 4, 'input': ['b']}] + let g:operator#sandwich#recipes = [] + call append(0, [' foo', ' bar', ' foo', ' bar']) + normal 2Gsr2jb + call g:assert.equals(getline(1), ' foo', 'failed at #27') + call g:assert.equals(getline(2), ' baz', 'failed at #27') + call g:assert.equals(getline(3), ' foo', 'failed at #27') + call g:assert.equals(getline(4), ' baz', 'failed at #27') +endfunction +"}}} +function! s:suite.linewise_n_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{", "}"], 'input': ['a']}, + \ {'buns': ["{", "}"], 'indentkeys': '0},0),:,0#,!^F,o,e', 'input': ['1']}, + \ ] + call operator#sandwich#set('replace', 'line', 'linewise', 2) + + """ cinkeys + setlocal autoindent + call operator#sandwich#set('replace', 'line', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys', '0},0),:,0#,!^F,o,e') + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), ' foo', 'failed at #1') + call g:assert.equals(getline(3), ' }', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', 3) + + " #2 + setlocal cinkeys=0},0),:,0#,!^F,o,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys+', 'O,0{') + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys-', 'O,0{') + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call append(0, [' "', ' foo', ' "']) + normal ggsr2j1 + call g:assert.equals(getline(1), ' {', 'failed at #4') + call g:assert.equals(getline(2), ' foo', 'failed at #4') + call g:assert.equals(getline(3), ' }', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys', '0},0),:,0#,!^F,o,e') + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), ' foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0},0),:,0#,!^F,o,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys+', 'O,0{') + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys-', 'O,0{') + call append(0, [' "', ' foo', ' "']) + normal ggsr2ja + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), ' foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call append(0, [' "', ' foo', ' "']) + normal ggsr2j1 + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), ' foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +function! s:suite.linewise_x_default_recipes() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call setline('.', '(foo)') + normal Vsr[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal Vsr{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal Vsr< + call g:assert.equals(getline('.'), '', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #3') + + " #4 + call setline('.', '') + normal Vsr( + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', ')']) + normal ggV2jsr] + call g:assert.equals(getline(1), '[', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ']', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #5') + + " #6 + call append(0, ['[', 'foo', ']']) + normal ggV2jsr} + call g:assert.equals(getline(1), '{', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #6') + + " #7 + call append(0, ['{', 'foo', '}']) + normal ggV2jsr> + call g:assert.equals(getline(1), '<', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), '>', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #7') + + " #8 + call append(0, ['<', 'foo', '>']) + normal ggV2jsr) + call g:assert.equals(getline(1), '(', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ')', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_x_not_registered() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call setline('.', 'afooa') + normal Vsrb + call g:assert.equals(getline('.'), 'bfoob', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + " #2 + call setline('.', '+foo+') + normal Vsr* + call g:assert.equals(getline('.'), '*foo*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + %delete + + " #1 + call append(0, ['a', 'foo', 'a']) + normal ggV2jsrb + call g:assert.equals(getline(1), 'b', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), 'b', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['+', 'foo', '+']) + normal ggV2jsr* + call g:assert.equals(getline(1), '*', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), '*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_x_positioning() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call append(0, ['(', 'foo', 'bar', 'baz', ')']) + normal ggV4jsr[ + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), 'bar', 'failed at #1') + call g:assert.equals(getline(4), 'baz', 'failed at #1') + call g:assert.equals(getline(5), ']', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo', '(', 'bar', ')', 'baz']) + normal ggjV2jsr[ + call g:assert.equals(getline(1), 'foo', 'failed at #2') + call g:assert.equals(getline(2), '[', 'failed at #2') + call g:assert.equals(getline(3), 'bar', 'failed at #2') + call g:assert.equals(getline(4), ']', 'failed at #2') + call g:assert.equals(getline(5), 'baz', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 4, 2, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(foo', 'bar', 'baz)']) + normal ggV2jsr[ + call g:assert.equals(getline(1), '[foo', 'failed at #3') + call g:assert.equals(getline(2), 'bar', 'failed at #3') + call g:assert.equals(getline(3), 'baz]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 5, 0], 'failed at #3') +endfunction +"}}} +function! s:suite.linewise_x_nothing_inside() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call setline('.', '()') + normal Vsr[ + call g:assert.equals(getline('.'), '[]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 3, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['(', ')']) + normal ggVjsr[ + call g:assert.equals(getline(1), '[', 'failed at #2') + call g:assert.equals(getline(2), ']', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 2, 2, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_x_count() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call setline('.', '([foo])') + normal V2sr[( + call g:assert.equals(getline('.'), '[(foo)]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 8, 0], 'failed at #1') + + " #2 + call setline('.', '[({foo})]') + normal V3sr{[( + call g:assert.equals(getline('.'), '{[(foo)]}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 10, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['foo', '{', '[', '(', 'bar', ')', ']', '}', 'baz']) + normal ggjV6j3sr({[ + call g:assert.equals(getline(1), 'foo', 'failed at #3') + call g:assert.equals(getline(2), '(', 'failed at #3') + call g:assert.equals(getline(3), '{', 'failed at #3') + call g:assert.equals(getline(4), '[', 'failed at #3') + call g:assert.equals(getline(5), 'bar', 'failed at #3') + call g:assert.equals(getline(6), ']', 'failed at #3') + call g:assert.equals(getline(7), '}', 'failed at #3') + call g:assert.equals(getline(8), ')', 'failed at #3') + call g:assert.equals(getline(9), 'baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 5, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 8, 2, 0], 'failed at #3') +endfunction +"}}} +function! s:suite.linewise_x_breaking() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + let g:operator#sandwich#recipes = [ + \ {'buns': ["aa\naaa", "aaa\naa"], 'input':['a']}, + \ {'buns': ["bb\nbbb\nbb", "bb\nbbb\nbb"], 'input':['b']}, + \ ] + + " #1 + call append(0, ['aa', 'aaafooaaa', 'aa']) + normal ggV2jsr( + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['bb', 'bbb', 'bbfoobb', 'bbb', 'bb']) + normal ggV4jsr( + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 1, 6, 0], 'failed at #2') + + %delete + + " #3 + call setline('.', '(foo)') + normal Vsra + call g:assert.equals(getline(1), 'aa', 'failed at #3') + call g:assert.equals(getline(2), 'aaafooaaa', 'failed at #3') + call g:assert.equals(getline(3), 'aa', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 4, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #3') + + %delete + + " #4 + call setline('.', '(foo)') + normal Vsrb + call g:assert.equals(getline(1), 'bb', 'failed at #4') + call g:assert.equals(getline(2), 'bbb', 'failed at #4') + call g:assert.equals(getline(3), 'bbfoobb', 'failed at #4') + call g:assert.equals(getline(4), 'bbb', 'failed at #4') + call g:assert.equals(getline(5), 'bb', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 3, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 3, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['aa', 'aaa', 'aa', 'aaa', 'foo', 'aaa', 'aa', 'aaa', 'aa']) + normal ggV8j2sr(( + call g:assert.equals(getline(1), '(', 'failed at #5') + call g:assert.equals(getline(2), '(', 'failed at #5') + call g:assert.equals(getline(3), 'foo', 'failed at #5') + call g:assert.equals(getline(4), ')', 'failed at #5') + call g:assert.equals(getline(5), ')', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['bb', 'bbb', 'bb', 'bb', 'bbb', 'bb', 'foo', 'bb', 'bbb', 'bb', 'bb', 'bbb', 'bb']) + normal ggV12j2sr(( + call g:assert.equals(getline(1), '(', 'failed at #6') + call g:assert.equals(getline(2), '(', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ')', 'failed at #6') + call g:assert.equals(getline(5), ')', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #6') + + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_x_external_textobj() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call setline('.', '(foo)') + normal Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #3') + + " #4 + call setline('.', 'foo') + normal Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call setline('.', '(foo)') + normal 0Vsr" + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_x_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call append(0, ['a', 'α', 'a']) + normal ggV2jsr( + call g:assert.equals(getline(1), '(', 'failed at #1') + call g:assert.equals(getline(2), 'α', 'failed at #1') + call g:assert.equals(getline(3), ')', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + " #2 + call append(0, ['a', 'aα', 'a']) + normal ggV2jsr( + call g:assert.equals(getline(1), '(', 'failed at #2') + call g:assert.equals(getline(2), 'aα', 'failed at #2') + call g:assert.equals(getline(3), ')', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + + " #3 + call append(0, ['β', 'α', 'β']) + normal ggV2jsr( + call g:assert.equals(getline(1), '(', 'failed at #3') + call g:assert.equals(getline(2), 'α', 'failed at #3') + call g:assert.equals(getline(3), ')', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + + " #4 + call append(0, ['β', 'aα', 'β']) + normal ggV2jsr( + call g:assert.equals(getline(1), '(', 'failed at #4') + call g:assert.equals(getline(2), 'aα', 'failed at #4') + call g:assert.equals(getline(3), ')', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #5 + call append(0, ['a', 'a', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), 'α', 'failed at #5') + call g:assert.equals(getline(2), 'a', 'failed at #5') + call g:assert.equals(getline(3), 'α', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #5') + + " #6 + call append(0, ['a', 'α', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), 'α', 'failed at #6') + call g:assert.equals(getline(2), 'α', 'failed at #6') + call g:assert.equals(getline(3), 'α', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #6') + + " #7 + call append(0, ['a', 'aα', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), 'α', 'failed at #7') + call g:assert.equals(getline(2), 'aα', 'failed at #7') + call g:assert.equals(getline(3), 'α', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #7') + + " #8 + call append(0, ['β', 'a', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), 'α', 'failed at #8') + call g:assert.equals(getline(2), 'a', 'failed at #8') + call g:assert.equals(getline(3), 'α', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #8') + + " #9 + call append(0, ['β', 'α', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), 'α', 'failed at #9') + call g:assert.equals(getline(2), 'α', 'failed at #9') + call g:assert.equals(getline(3), 'α', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #9') + + " #10 + call append(0, ['β', 'aα', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), 'α', 'failed at #10') + call g:assert.equals(getline(2), 'aα', 'failed at #10') + call g:assert.equals(getline(3), 'α', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, strlen('α')+1, 0], 'failed at #10') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #11 + call append(0, ['a', 'a', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), 'aα', 'failed at #11') + call g:assert.equals(getline(2), 'a', 'failed at #11') + call g:assert.equals(getline(3), 'aα', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #11') + + " #12 + call append(0, ['a', 'α', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), 'aα', 'failed at #12') + call g:assert.equals(getline(2), 'α', 'failed at #12') + call g:assert.equals(getline(3), 'aα', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #12') + + " #13 + call append(0, ['a', 'aα', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), 'aα', 'failed at #13') + call g:assert.equals(getline(2), 'aα', 'failed at #13') + call g:assert.equals(getline(3), 'aα', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #13') + + " #14 + call append(0, ['β', 'a', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), 'aα', 'failed at #14') + call g:assert.equals(getline(2), 'a', 'failed at #14') + call g:assert.equals(getline(3), 'aα', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #14') + + " #15 + call append(0, ['β', 'α', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), 'aα', 'failed at #15') + call g:assert.equals(getline(2), 'α', 'failed at #15') + call g:assert.equals(getline(3), 'aα', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #15') + + " #16 + call append(0, ['β', 'aα', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), 'aα', 'failed at #16') + call g:assert.equals(getline(2), 'aα', 'failed at #16') + call g:assert.equals(getline(3), 'aα', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, strlen('aα')+1, 0], 'failed at #16') + + unlet g:operator#sandwich#recipes + + " #17 + call append(0, ['a', '“', 'a']) + normal ggV2jsr( + call g:assert.equals(getline(1), '(', 'failed at #17') + call g:assert.equals(getline(2), '“', 'failed at #17') + call g:assert.equals(getline(3), ')', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #17') + + " #18 + call append(0, ['a', 'a“', 'a']) + normal ggV2jsr( + call g:assert.equals(getline(1), '(', 'failed at #18') + call g:assert.equals(getline(2), 'a“', 'failed at #18') + call g:assert.equals(getline(3), ')', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #18') + + " #19 + call append(0, ['β', '“', 'β']) + normal ggV2jsr( + call g:assert.equals(getline(1), '(', 'failed at #19') + call g:assert.equals(getline(2), '“', 'failed at #19') + call g:assert.equals(getline(3), ')', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #19') + + " #20 + call append(0, ['β', 'a“', 'β']) + normal ggV2jsr( + call g:assert.equals(getline(1), '(', 'failed at #20') + call g:assert.equals(getline(2), 'a“', 'failed at #20') + call g:assert.equals(getline(3), ')', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #20') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #21 + call append(0, ['a', 'a', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), '“', 'failed at #21') + call g:assert.equals(getline(2), 'a', 'failed at #21') + call g:assert.equals(getline(3), '“', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #21') + + " #22 + call append(0, ['a', '“', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), '“', 'failed at #22') + call g:assert.equals(getline(2), '“', 'failed at #22') + call g:assert.equals(getline(3), '“', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #22') + + " #23 + call append(0, ['a', 'a“', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), '“', 'failed at #23') + call g:assert.equals(getline(2), 'a“', 'failed at #23') + call g:assert.equals(getline(3), '“', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #23') + + " #24 + call append(0, ['β', 'a', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), '“', 'failed at #24') + call g:assert.equals(getline(2), 'a', 'failed at #24') + call g:assert.equals(getline(3), '“', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #24') + + " #25 + call append(0, ['β', '“', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), '“', 'failed at #25') + call g:assert.equals(getline(2), '“', 'failed at #25') + call g:assert.equals(getline(3), '“', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #25') + + " #26 + call append(0, ['β', 'a“', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), '“', 'failed at #26') + call g:assert.equals(getline(2), 'a“', 'failed at #26') + call g:assert.equals(getline(3), '“', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 3, strlen('“')+1, 0], 'failed at #26') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #27 + call append(0, ['a', 'a', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), 'a“', 'failed at #27') + call g:assert.equals(getline(2), 'a', 'failed at #27') + call g:assert.equals(getline(3), 'a“', 'failed at #27') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #27') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #27') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #27') + + " #28 + call append(0, ['a', '“', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), 'a“', 'failed at #28') + call g:assert.equals(getline(2), '“', 'failed at #28') + call g:assert.equals(getline(3), 'a“', 'failed at #28') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #28') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #28') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #28') + + " #29 + call append(0, ['a', 'a“', 'a']) + normal ggV2jsra + call g:assert.equals(getline(1), 'a“', 'failed at #29') + call g:assert.equals(getline(2), 'a“', 'failed at #29') + call g:assert.equals(getline(3), 'a“', 'failed at #29') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #29') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #29') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #29') + + " #30 + call append(0, ['β', 'a', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), 'a“', 'failed at #30') + call g:assert.equals(getline(2), 'a', 'failed at #30') + call g:assert.equals(getline(3), 'a“', 'failed at #30') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #30') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #30') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #30') + + " #31 + call append(0, ['β', '“', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), 'a“', 'failed at #31') + call g:assert.equals(getline(2), '“', 'failed at #31') + call g:assert.equals(getline(3), 'a“', 'failed at #31') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #31') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #31') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #31') + + " #32 + call append(0, ['β', 'a“', 'β']) + normal ggV2jsra + call g:assert.equals(getline(1), 'a“', 'failed at #32') + call g:assert.equals(getline(2), 'a“', 'failed at #32') + call g:assert.equals(getline(3), 'a“', 'failed at #32') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #32') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #32') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“')+1, 0], 'failed at #32') +endfunction +"}}} +function! s:suite.linewise_x_option_cursor() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + """"" cursor + """ default + " #1 + call setline('.', '(((foo)))') + normal 0V2sr[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + + " #2 + normal Vsr( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + + " #3 + let g:operator#sandwich#recipes = [{'buns': ["[\n ", "\n]"], 'input':['a']}] + call setline('.', '(foo)') + normal Vsra + call g:assert.equals(getline(1), '[', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ']', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + unlet! g:operator#sandwich#recipes + + %delete + + """ inner_head + " #1 + call operator#sandwich#set('replace', 'line', 'cursor', 'inner_head') + call setline('.', '(((foo)))') + normal 0V2sr[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + + " #2 + normal Vsr( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + + """ keep + " #3 + call operator#sandwich#set('replace', 'line', 'cursor', 'keep') + call setline('.', '(((foo)))') + normal 04lV2sr[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + + " #4 + normal lVsr( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 6, 0], 'failed at #4') + + """ inner_tail + " #5 + call operator#sandwich#set('replace', 'line', 'cursor', 'inner_tail') + call setline('.', '(((foo)))') + normal 04lV2sr[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 7, 0], 'failed at #5') + + " #6 + normal hVsr( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 8, 0], 'failed at #6') + + """ head + " #7 + call operator#sandwich#set('replace', 'line', 'cursor', 'head') + call setline('.', '(((foo)))') + normal 04lV2sr[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #7') + + " #8 + normal 3lVsr( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #8') + + """ tail + " #9 + call operator#sandwich#set('replace', 'line', 'cursor', 'tail') + call setline('.', '(((foo)))') + normal 04lV2sr[[ + call g:assert.equals(getline('.'), '[[(foo)]]', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #9') + + " #10 + normal 3hVsr( + call g:assert.equals(getline('.'), '([(foo)])', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 9, 0], 'failed at #10') + + """"" recipe option + " #11 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('replace', 'line', 'cursor', 'inner_tail') + call setline('.', '[foo]') + normal 0Vsr1 + call g:assert.equals(getline('.'), '(foo)', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #11') +endfunction +"}}} +function! s:suite.linewise_x_option_noremap() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '{foo}') + normal Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal Vsr" + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call setline('.', '{foo}') + normal 0Vsr" + call g:assert.equals(getline('.'), '{foo}', 'failed at #3') + + " #4 + call setline('.', '(foo)') + normal 0Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + """ off + call operator#sandwich#set('replace', 'line', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '{foo}') + normal Vsr" + call g:assert.equals(getline('.'), '{foo}', 'failed at #5') + + " #6 + call setline('.', '(foo)') + normal Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #6') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call setline('.', '{foo}') + normal 0Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #7') + + " #8 + call setline('.', '(foo)') + normal 0Vsr" + call g:assert.equals(getline('.'), '(foo)', 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_x_option_regex() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+foo\d\+') + normal Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #1') + + " #2 + call setline('.', '888foo888') + normal Vsr" + call g:assert.equals(getline('.'), '"88foo88"', 'failed at #2') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call setline('.', '\d\+foo\d\+') + normal 0Vsr" + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #3') + + " #4 + call setline('.', '888foo888') + normal 0Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + + """ on + call operator#sandwich#set('replace', 'line', 'regex', 1) + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #5 + call setline('.', '\d\+foo\d\+') + normal Vsr" + call g:assert.equals(getline('.'), '\d\+foo\d\+', 'failed at #5') + + " #6 + call setline('.', '888foo888') + normal Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #6') + + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call setline('.', '\d\+foo\d\+') + normal 0Vsr" + call g:assert.equals(getline('.'), '"foo"', 'failed at #7') + + " #8 + call setline('.', '888foo888') + normal 0Vsr" + call g:assert.equals(getline('.'), '"88foo88"', 'failed at #8') +endfunction +"}}} +function! s:suite.linewise_x_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + """ 2 + " #1 + call setline('.', '"foo"') + normal Vsr( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + " #2 + call setline('.', ' "foo"') + normal Vsr( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #2') + + " #3 + call setline('.', '"foo" ') + normal Vsr( + call g:assert.equals(getline('.'), '(foo) ', 'failed at #3') + + " #4 + call setline('.', ' "foo" ') + normal Vsr( + call g:assert.equals(getline('.'), ' (foo) ', 'failed at #4') + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0Vsr( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #5') + + """ 1 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'line', 'skip_space', 1) + + " #6 + call setline('.', '"foo"') + normal Vsr( + call g:assert.equals(getline('.'), '(foo)', 'failed at #6') + + " #7 + call setline('.', ' "foo"') + normal Vsr( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #7') + + " #8 + call setline('.', '"foo" ') + normal Vsr( + call g:assert.equals(getline('.'), '(foo) ', 'failed at #8') + + " #9 + " do not skip! + call setline('.', ' "foo" ') + normal Vsr( + call g:assert.equals(getline('.'), '("foo")', 'failed at #9') + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0Vsr( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #10') + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'line', 'skip_space', 0) + + " #11 + call setline('.', '"foo"') + normal Vsr( + call g:assert.equals(getline('.'), '(foo)', 'failed at #11') + + " #12 + call setline('.', ' "foo"') + normal Vsr( + call g:assert.equals(getline('.'), ' "foo"', 'failed at #12') + + " #13 + call setline('.', '"foo" ') + normal Vsr( + call g:assert.equals(getline('.'), '"foo" ', 'failed at #13') + + " #14 + " do not skip! + call setline('.', ' "foo" ') + normal Vsr( + call g:assert.equals(getline('.'), '("foo")', 'failed at #14') + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}, {'buns': ['(', ')']}] + call setline('.', ' "foo"') + normal 0Vsr( + call g:assert.equals(getline('.'), ' (foo)', 'failed at #15') +endfunction +"}}} +function! s:suite.linewise_x_option_skip_char() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + """ off + " #1 + call setline('.', 'aa(foo)bb') + normal Vsr" + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #1') + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call setline('.', 'aa(foo)bb') + normal 0srVl" + call g:assert.equals(getline('.'), 'aa"foo"bb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + """ on + call operator#sandwich#set('replace', 'line', 'skip_char', 1) + + " #3 + call setline('.', 'aa(foo)bb') + normal Vsr" + call g:assert.equals(getline('.'), 'aa"foo"bb', 'failed at #3') + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call setline('.', 'aa(foo)bb') + normal 0Vsr" + call g:assert.equals(getline('.'), 'aa(foo)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_x_option_command() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + " #1 + call operator#sandwich#set('replace', 'line', 'command', ['normal! `[d`]']) + call append(0, ['[', '(foo)', ']']) + normal ggjVsr" + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), '', 'failed at #1') + call g:assert.equals(getline(3), ']', 'failed at #1') + + %delete + + " #2 + call operator#sandwich#set('replace', 'line', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call append(0, ['[', '(foo)', ']']) + normal ggjVsr" + call g:assert.equals(getline(1), '[', 'failed at #2') + call g:assert.equals(getline(2), '', 'failed at #2') + call g:assert.equals(getline(3), ']', 'failed at #2') +endfunction +"}}} +function! s:suite.linewise_x_option_linewise() abort "{{{ + """ 0 + call operator#sandwich#set('replace', 'line', 'linewise', 0) + + " #1 + call append(0, ['(', 'foo', ')']) + normal ggV2jsr[ + call g:assert.equals(getline(1), '[', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), ']', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['( ', 'foo', ' )']) + normal ggV2jsr[ + call g:assert.equals(getline(1), '[ ', 'failed at #2') + call g:assert.equals(getline(2), 'foo', 'failed at #2') + call g:assert.equals(getline(3), ' ]', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggV2jsr[ + call g:assert.equals(getline(1), '[aa', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), 'aa]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa', 'foo', ')']) + normal ggV2jsr[ + call g:assert.equals(getline(1), '[aa', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), ']', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(', 'foo', 'aa)']) + normal ggV2jsr[ + call g:assert.equals(getline(1), '[', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), 'aa]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #5') + + %delete + + " #6 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 1}] + call append(0, ['(', 'foo', ')']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[', 'failed at #6') + call g:assert.equals(getline(2), 'foo', 'failed at #6') + call g:assert.equals(getline(3), ']', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #6') + unlet! g:operator#sandwich#recipes + + """ 2 + %delete + call operator#sandwich#set('replace', 'line', 'linewise', 2) + + " #7 + call append(0, ['(', 'foo', ')']) + normal ggV2jsr[ + call g:assert.equals(getline(1), '[', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ']', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['( ', 'foo', ' )']) + normal ggV2jsr[ + call g:assert.equals(getline(1), '[', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ']', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['(aa', 'foo', 'aa)']) + normal ggV2jsr[ + call g:assert.equals(getline(1), '[', 'failed at #9') + call g:assert.equals(getline(2), 'foo', 'failed at #9') + call g:assert.equals(getline(3), ']', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #9') + + %delete + + " #10 + call append(0, ['aa', '(foo)', 'bb']) + normal ggjVsr[ + call g:assert.equals(getline(1), 'aa', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), ']', 'failed at #10') + call g:assert.equals(getline(4), 'bb', 'failed at #10') + call g:assert.equals(getline(5), '', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #10') + + %delete + + " #11 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'linewise': 0}] + call append(0, ['(', 'foo', ')']) + normal ggsr2j[ + call g:assert.equals(getline(1), '[', 'failed at #11') + call g:assert.equals(getline(2), 'foo', 'failed at #11') + call g:assert.equals(getline(3), ']', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #11') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.linewise_x_option_query_once() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call setline('.', '"""foo"""') + normal V3sr([{ + call g:assert.equals(getline('.'), '([{foo}])', 'failed at #1') + + " #2 + call setline('.', '"""foo"""') + normal 0V3sr1 + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + """ on + call operator#sandwich#set('replace', 'line', 'query_once', 1) + + " #2 + call setline('.', '"""foo"""') + normal V3sr( + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + " #4 + call setline('.', '"""foo"""') + normal 0V3sr0[{ + call g:assert.equals(getline('.'), '([{foo}])', 'failed at #4') +endfunction +"}}} +function! s:suite.linewise_x_option_expr() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', '"foo"') + normal Vsra + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #1') + + " #2 + call setline('.', '"foo"') + normal Vsr1 + call g:assert.equals(getline('.'), '2foo3', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'line', 'expr', 1) + call setline('.', '"foo"') + normal Vsra + call g:assert.equals(getline('.'), '2foo3', 'failed at #3') + + " #4 + call setline('.', '"foo"') + normal Vsrb + call g:assert.equals(getline('.'), '"foo"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', '"foo"') + normal Vsrc + call g:assert.equals(getline('.'), '"foo"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', '"''foo''"') + normal V2srab + call g:assert.equals(getline('.'), '2''foo''3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + " #7 + call setline('.', '"''foo''"') + normal V2srac + call g:assert.equals(getline('.'), '2''foo''3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + + " #8 + call setline('.', '"''foo''"') + normal V2srba + call g:assert.equals(getline('.'), '"''foo''"', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + " #9 + call setline('.', '"''foo''"') + normal V2srca + call g:assert.equals(getline('.'), '"''foo''"', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + " #10 + call setline('.', '"foo"') + normal Vsr0 + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.linewise_x_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0Vsra + call g:assert.equals(getline('.'), '"bar"', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + " #2 + call setline('.', '"bar"') + normal 0Vsr1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'line', 'listexpr', 1) + call setline('.', '"bar"') + normal 0Vsra + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + + " #4 + call setline('.', '"bar"') + normal 0Vsrb + call g:assert.equals(getline('.'), '"bar"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :normal 0Vsr0 + call g:assert.equals(getline('.'), '"bar"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', '"bar"') + normal 0Vsr1 + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.linewise_x_option_autoindent() abort "{{{ + call operator#sandwich#set('replace', 'line', 'linewise', 1) + + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + call operator#sandwich#set('replace', 'line', 'linewise', 2) + + """ -1 + call operator#sandwich#set('replace', 'line', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + call g:assert.equals(getline(4), ' foo', 'failed at #1') + call g:assert.equals(getline(5), '', 'failed at #1') + call g:assert.equals(getline(6), ']', 'failed at #1') + call g:assert.equals(getline(7), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), '', 'failed at #2') + call g:assert.equals(getline(4), ' foo', 'failed at #2') + call g:assert.equals(getline(5), '', 'failed at #2') + call g:assert.equals(getline(6), ' ]', 'failed at #2') + call g:assert.equals(getline(7), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), '', 'failed at #3') + call g:assert.equals(getline(4), ' foo', 'failed at #3') + call g:assert.equals(getline(5), '', 'failed at #3') + call g:assert.equals(getline(6), ' ]', 'failed at #3') + call g:assert.equals(getline(7), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), '', 'failed at #4') + call g:assert.equals(getline(4), ' foo', 'failed at #4') + call g:assert.equals(getline(5), '', 'failed at #4') + " call g:assert.equals(getline(6), ' ]', 'failed at #4') + call g:assert.equals(getline(7), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call append(0, [' "', ' foo', ' "']) + " normal ggV2jsra + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), '', 'failed at #5') + " call g:assert.equals(getline(4), ' foo', 'failed at #5') + " call g:assert.equals(getline(5), '', 'failed at #5') + " call g:assert.equals(getline(6), ' ]', 'failed at #5') + " call g:assert.equals(getline(7), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 7, 22, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('replace', 'line', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), '', 'failed at #6') + call g:assert.equals(getline(4), ' foo', 'failed at #6') + call g:assert.equals(getline(5), '', 'failed at #6') + call g:assert.equals(getline(6), ']', 'failed at #6') + call g:assert.equals(getline(7), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), '', 'failed at #7') + call g:assert.equals(getline(4), ' foo', 'failed at #7') + call g:assert.equals(getline(5), '', 'failed at #7') + call g:assert.equals(getline(6), ']', 'failed at #7') + call g:assert.equals(getline(7), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), '', 'failed at #8') + call g:assert.equals(getline(4), ' foo', 'failed at #8') + call g:assert.equals(getline(5), '', 'failed at #8') + call g:assert.equals(getline(6), ']', 'failed at #8') + call g:assert.equals(getline(7), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), '', 'failed at #9') + call g:assert.equals(getline(4), ' foo', 'failed at #9') + call g:assert.equals(getline(5), '', 'failed at #9') + call g:assert.equals(getline(6), ']', 'failed at #9') + call g:assert.equals(getline(7), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), '', 'failed at #10') + call g:assert.equals(getline(4), ' foo', 'failed at #10') + call g:assert.equals(getline(5), '', 'failed at #10') + call g:assert.equals(getline(6), ']', 'failed at #10') + call g:assert.equals(getline(7), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('replace', 'line', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), '', 'failed at #11') + call g:assert.equals(getline(4), ' foo', 'failed at #11') + call g:assert.equals(getline(5), '', 'failed at #11') + call g:assert.equals(getline(6), ' ]', 'failed at #11') + call g:assert.equals(getline(7), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), '', 'failed at #12') + call g:assert.equals(getline(4), ' foo', 'failed at #12') + call g:assert.equals(getline(5), '', 'failed at #12') + call g:assert.equals(getline(6), ' ]', 'failed at #12') + call g:assert.equals(getline(7), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), '', 'failed at #13') + call g:assert.equals(getline(4), ' foo', 'failed at #13') + call g:assert.equals(getline(5), '', 'failed at #13') + call g:assert.equals(getline(6), ' ]', 'failed at #13') + call g:assert.equals(getline(7), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), '', 'failed at #14') + call g:assert.equals(getline(4), ' foo', 'failed at #14') + call g:assert.equals(getline(5), '', 'failed at #14') + call g:assert.equals(getline(6), ' ]', 'failed at #14') + call g:assert.equals(getline(7), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), '', 'failed at #15') + call g:assert.equals(getline(4), ' foo', 'failed at #15') + call g:assert.equals(getline(5), '', 'failed at #15') + call g:assert.equals(getline(6), ' ]', 'failed at #15') + call g:assert.equals(getline(7), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 7, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('replace', 'line', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), '', 'failed at #16') + call g:assert.equals(getline(4), ' foo', 'failed at #16') + call g:assert.equals(getline(5), '', 'failed at #16') + call g:assert.equals(getline(6), ' ]', 'failed at #16') + call g:assert.equals(getline(7), '}', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), '', 'failed at #17') + call g:assert.equals(getline(4), ' foo', 'failed at #17') + call g:assert.equals(getline(5), '', 'failed at #17') + call g:assert.equals(getline(6), ' ]', 'failed at #17') + call g:assert.equals(getline(7), '}', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), '', 'failed at #18') + call g:assert.equals(getline(4), ' foo', 'failed at #18') + call g:assert.equals(getline(5), '', 'failed at #18') + call g:assert.equals(getline(6), ' ]', 'failed at #18') + call g:assert.equals(getline(7), '}', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), '', 'failed at #19') + call g:assert.equals(getline(4), ' foo', 'failed at #19') + call g:assert.equals(getline(5), '', 'failed at #19') + call g:assert.equals(getline(6), ' ]', 'failed at #19') + call g:assert.equals(getline(7), '}', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), '', 'failed at #20') + call g:assert.equals(getline(4), ' foo', 'failed at #20') + call g:assert.equals(getline(5), '', 'failed at #20') + call g:assert.equals(getline(6), ' ]', 'failed at #20') + call g:assert.equals(getline(7), '}', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('replace', 'line', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), '', 'failed at #21') + call g:assert.equals(getline(4), ' foo', 'failed at #21') + call g:assert.equals(getline(5), '', 'failed at #21') + " call g:assert.equals(getline(6), ' ]', 'failed at #21') + call g:assert.equals(getline(7), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), '', 'failed at #22') + call g:assert.equals(getline(4), ' foo', 'failed at #22') + call g:assert.equals(getline(5), '', 'failed at #22') + " call g:assert.equals(getline(6), ' ]', 'failed at #22') + call g:assert.equals(getline(7), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), '', 'failed at #23') + call g:assert.equals(getline(4), ' foo', 'failed at #23') + call g:assert.equals(getline(5), '', 'failed at #23') + " call g:assert.equals(getline(6), ' ]', 'failed at #23') + call g:assert.equals(getline(7), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), '', 'failed at #24') + call g:assert.equals(getline(4), ' foo', 'failed at #24') + call g:assert.equals(getline(5), '', 'failed at #24') + " call g:assert.equals(getline(6), ' ]', 'failed at #24') + call g:assert.equals(getline(7), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), '', 'failed at #25') + call g:assert.equals(getline(4), ' foo', 'failed at #25') + call g:assert.equals(getline(5), '', 'failed at #25') + " call g:assert.equals(getline(6), ' ]', 'failed at #25') + call g:assert.equals(getline(7), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call append(0, [' "', ' foo', ' "']) + normal ggsr2j0 + call g:assert.equals(getline(1), '{', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), '', 'failed at #26') + call g:assert.equals(getline(4), ' foo', 'failed at #26') + call g:assert.equals(getline(5), '', 'failed at #26') + call g:assert.equals(getline(6), ']', 'failed at #26') + call g:assert.equals(getline(7), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 4, 5, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 7, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') + + %delete + + """ 4 + setlocal indentexpr=TestIndent() + let g:sandwich#recipes = [{'buns': ['bar', 'bar'], 'line': 1, 'autoindent': 4, 'input': ['a']}, {'buns': ['baz', 'baz'], 'line': 1, 'autoindent': 4, 'input': ['b']}] + let g:operator#sandwich#recipes = [] + call append(0, [' foo', ' bar', ' foo', ' bar']) + normal 2GV2jsrb + call g:assert.equals(getline(1), ' foo', 'failed at #27') + call g:assert.equals(getline(2), ' baz', 'failed at #27') + call g:assert.equals(getline(3), ' foo', 'failed at #27') + call g:assert.equals(getline(4), ' baz', 'failed at #27') +endfunction +"}}} +function! s:suite.linewise_x_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{", "}"], 'input': ['a']}, + \ {'buns': ["{", "}"], 'indentkeys': '0},0),:,0#,!^F,o,e', 'input': ['1']}, + \ ] + call operator#sandwich#set('replace', 'line', 'linewise', 2) + + """ cinkeys + setlocal autoindent + call operator#sandwich#set('replace', 'line', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys', '0},0),:,0#,!^F,o,e') + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), ' foo', 'failed at #1') + call g:assert.equals(getline(3), ' }', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', 3) + + " #2 + setlocal cinkeys=0},0),:,0#,!^F,o,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys+', 'O,0{') + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys-', 'O,0{') + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' foo', 'failed at #3') + call g:assert.equals(getline(3), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call append(0, [' "', ' foo', ' "']) + normal ggV2jsr1 + call g:assert.equals(getline(1), ' {', 'failed at #4') + call g:assert.equals(getline(2), ' foo', 'failed at #4') + call g:assert.equals(getline(3), ' }', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys', '0},0),:,0#,!^F,o,e') + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), ' foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0},0),:,0#,!^F,o,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys+', 'O,0{') + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'line', 'indentkeys-', 'O,0{') + call append(0, [' "', ' foo', ' "']) + normal ggV2jsra + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), ' foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'line', 'linewise', 2) + call operator#sandwich#set('replace', 'line', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call append(0, [' "', ' foo', ' "']) + normal ggV2jsr1 + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), ' foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +" block-wise +function! s:suite.blockwise_n_default_recipes() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsr\17l[" + call g:assert.equals(getline(1), '[foo]', 'failed at #1') + call g:assert.equals(getline(2), '[bar]', 'failed at #1') + call g:assert.equals(getline(3), '[baz]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['[foo]', '[bar]', '[baz]']) + execute "normal ggsr\17l{" + call g:assert.equals(getline(1), '{foo}', 'failed at #2') + call g:assert.equals(getline(2), '{bar}', 'failed at #2') + call g:assert.equals(getline(3), '{baz}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal ggsr\17l<" + call g:assert.equals(getline(1), '', 'failed at #3') + call g:assert.equals(getline(2), '', 'failed at #3') + call g:assert.equals(getline(3), '', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['', '', '']) + execute "normal ggsr\17l(" + call g:assert.equals(getline(1), '(foo)', 'failed at #4') + call g:assert.equals(getline(2), '(bar)', 'failed at #4') + call g:assert.equals(getline(3), '(baz)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #4') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_not_registered() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['afooa', 'abara', 'abaza']) + execute "normal ggsr\17lb" + call g:assert.equals(getline(1), 'bfoob', 'failed at #1') + call g:assert.equals(getline(2), 'bbarb', 'failed at #1') + call g:assert.equals(getline(3), 'bbazb', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + " #2 + call append(0, ['+foo+', '+bar+', '+baz+']) + execute "normal ggsr\17l*" + call g:assert.equals(getline(1), '*foo*', 'failed at #2') + call g:assert.equals(getline(2), '*bar*', 'failed at #2') + call g:assert.equals(getline(3), '*baz*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #2') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_positioning() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['(foo)bar', '(foo)bar', '(foo)bar']) + execute "normal ggsr\23l[" + call g:assert.equals(getline(1), '[foo]bar', 'failed at #1') + call g:assert.equals(getline(2), '[foo]bar', 'failed at #1') + call g:assert.equals(getline(3), '[foo]bar', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo(bar)', 'foo(bar)', 'foo(bar)']) + execute "normal gg3lsr\23l[" + call g:assert.equals(getline(1), 'foo[bar]', 'failed at #2') + call g:assert.equals(getline(2), 'foo[bar]', 'failed at #2') + call g:assert.equals(getline(3), 'foo[bar]', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 9, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['foo(bar)baz', 'foo(bar)baz', 'foo(bar)baz']) + execute "normal gg3lsr\29l[" + call g:assert.equals(getline(1), 'foo[bar]baz', 'failed at #3') + call g:assert.equals(getline(2), 'foo[bar]baz', 'failed at #3') + call g:assert.equals(getline(3), 'foo[bar]baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 9, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(foo)', '(bar)', 'bazbaz']) + execute "normal ggsr\17l[" + call g:assert.equals(getline(1), '[foo]', 'failed at #4') + call g:assert.equals(getline(2), '[bar]', 'failed at #4') + call g:assert.equals(getline(3), 'bazbaz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 2, 6, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(foo)', 'barbar', '(baz)']) + execute "normal ggsr\18l[" + call g:assert.equals(getline(1), '[foo]', 'failed at #5') + call g:assert.equals(getline(2), 'barbar', 'failed at #5') + call g:assert.equals(getline(3), '[baz]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['foofoo', '(bar)', '(baz)']) + execute "normal ggsr\18l[" + call g:assert.equals(getline(1), 'foofoo', 'failed at #6') + call g:assert.equals(getline(2), '[bar]', 'failed at #6') + call g:assert.equals(getline(3), '[baz]', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 2, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['(foo)', '(baar)', '(baaz)']) + execute "normal ggsr\20l[" + call g:assert.equals(getline(1), '[foo]', 'failed at #7') + call g:assert.equals(getline(2), '[baar]', 'failed at #7') + call g:assert.equals(getline(3), '[baaz]', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 7, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['(fooo)', '(bar)', '(baaz)']) + execute "normal ggsr\20l[" + call g:assert.equals(getline(1), '[fooo]', 'failed at #8') + call g:assert.equals(getline(2), '[bar]', 'failed at #8') + call g:assert.equals(getline(3), '[baaz]', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 7, 0], 'failed at #8') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_a_character() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['(a)', '(b)', '(c)']) + execute "normal ggsr\11l[" + call g:assert.equals(getline(1), '[a]', 'failed at #1') + call g:assert.equals(getline(2), '[b]', 'failed at #1') + call g:assert.equals(getline(3), '[c]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #1') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_nothing_inside() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['()', '()', '()']) + execute "normal ggsr\8l[" + call g:assert.equals(getline(1), '[]', 'failed at #1') + call g:assert.equals(getline(2), '[]', 'failed at #1') + call g:assert.equals(getline(3), '[]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo()bar', 'foo()bar', 'foo()bar']) + execute "normal gg3lsr\20l[" + call g:assert.equals(getline(1), 'foo[]bar', 'failed at #2') + call g:assert.equals(getline(2), 'foo[]bar', 'failed at #2') + call g:assert.equals(getline(3), 'foo[]bar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #2') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_count() abort "{{{ + set whichwrap=h,l + + " #1 + call append(0, ['[(foo)]', '[(bar)]', '[(baz)]']) + execute "normal gg3sr\23l({" + call g:assert.equals(getline(1), '({foo})', 'failed at #1') + call g:assert.equals(getline(2), '({bar})', 'failed at #1') + call g:assert.equals(getline(3), '({baz})', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 8, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg3sr\29l({[" + call g:assert.equals(getline(1), '({[foo]})', 'failed at #2') + call g:assert.equals(getline(2), '({[bar]})', 'failed at #2') + call g:assert.equals(getline(3), '({[baz]})', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 10, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['{[afoob]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg3sr\29l({[" + call g:assert.equals(getline(1), '({afoob})', 'failed at #3') + call g:assert.equals(getline(2), '({[bar]})', 'failed at #3') + call g:assert.equals(getline(3), '({[baz]})', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 10, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['{[(foo)]}', '{[abarb]}', '{[(baz)]}']) + execute "normal gg3sr\29l({[" + call g:assert.equals(getline(1), '({[foo]})', 'failed at #4') + call g:assert.equals(getline(2), '({abarb})', 'failed at #4') + call g:assert.equals(getline(3), '({[baz]})', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 10, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[abazb]}']) + execute "normal gg3sr\29l({[" + call g:assert.equals(getline(1), '({[foo]})', 'failed at #5') + call g:assert.equals(getline(2), '({[bar]})', 'failed at #5') + call g:assert.equals(getline(3), '({abazb})', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 10, 0], 'failed at #5') + + set whichwrap& +endfunction +"}}} +function! s:suite.blockwise_n_external_textobj() abort "{{{ + set whichwrap=h,l + + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #1') + call g:assert.equals(getline(2), '"bar"', 'failed at #1') + call g:assert.equals(getline(3), '"baz"', 'failed at #1') + + %delete + + " #2 + call append(0, ['[foo]', '[bar]', '[baz]']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #2') + call g:assert.equals(getline(2), '"bar"', 'failed at #2') + call g:assert.equals(getline(3), '"baz"', 'failed at #2') + + %delete + + " #3 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #3') + call g:assert.equals(getline(2), '"bar"', 'failed at #3') + call g:assert.equals(getline(3), '"baz"', 'failed at #3') + + %delete + + " #4 + call append(0, ['foo', 'bar', 'baz']) + execute "normal ggsr\56l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #4') + call g:assert.equals(getline(2), '"bar"', 'failed at #4') + call g:assert.equals(getline(3), '"baz"', 'failed at #4') + + %delete + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call append(0, ['(foo)', '(bar)', '(baz)']) + normal ggsr17l" + call g:assert.equals(getline(1), '(foo)', 'failed at #5') + call g:assert.equals(getline(2), '(bar)', 'failed at #5') + call g:assert.equals(getline(3), '(baz)', 'failed at #5') + + set whichwrap& + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.blockwise_n_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + set whichwrap=h,l + + " #1 + call append(0, ['aαa', 'bβb', 'cγc']) + execute "normal ggsr\11l(" + call g:assert.equals(getline(1), '(α)', 'failed at #1') + call g:assert.equals(getline(2), '(β)', 'failed at #1') + call g:assert.equals(getline(3), '(γ)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, strlen('(γ)')+1, 0], 'failed at #1') + + " #2 + call append(0, ['aaαa', 'bbβb', 'ccγc']) + execute "normal ggsr\14l(" + call g:assert.equals(getline(1), '(aα)', 'failed at #2') + call g:assert.equals(getline(2), '(bβ)', 'failed at #2') + call g:assert.equals(getline(3), '(cγ)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, strlen('(cγ)')+1, 0], 'failed at #2') + + " #3 + call append(0, ['ααα', 'βββ', 'γγγ']) + execute "normal ggsr\11l(" + call g:assert.equals(getline(1), '(α)', 'failed at #3') + call g:assert.equals(getline(2), '(β)', 'failed at #3') + call g:assert.equals(getline(3), '(γ)', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, strlen('(γ)')+1, 0], 'failed at #3') + + " #4 + call append(0, ['αaαα', 'βbββ', 'γcγγ']) + execute "normal ggsr\14l(" + call g:assert.equals(getline(1), '(aα)', 'failed at #4') + call g:assert.equals(getline(2), '(bβ)', 'failed at #4') + call g:assert.equals(getline(3), '(cγ)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, strlen('(cγ)')+1, 0], 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #5 + call append(0, ['aaa', 'bbb', 'ccc']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'αaα', 'failed at #5') + call g:assert.equals(getline(2), 'αbα', 'failed at #5') + call g:assert.equals(getline(3), 'αcα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcα')+1, 0], 'failed at #5') + + " #6 + call append(0, ['aαa', 'bβb', 'cγc']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'ααα', 'failed at #6') + call g:assert.equals(getline(2), 'αβα', 'failed at #6') + call g:assert.equals(getline(3), 'αγα', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, strlen('αγα')+1, 0], 'failed at #6') + + " #7 + call append(0, ['aaαa', 'bbβb', 'ccγc']) + execute "normal ggsr\14la" + call g:assert.equals(getline(1), 'αaαα', 'failed at #7') + call g:assert.equals(getline(2), 'αbβα', 'failed at #7') + call g:assert.equals(getline(3), 'αcγα', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcγα')+1, 0], 'failed at #7') + + " #8 + call append(0, ['αaα', 'βbβ', 'γcγ']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'αaα', 'failed at #8') + call g:assert.equals(getline(2), 'αbα', 'failed at #8') + call g:assert.equals(getline(3), 'αcα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcα')+1, 0], 'failed at #8') + + " #9 + call append(0, ['ααα', 'βββ', 'γγγ']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'ααα', 'failed at #9') + call g:assert.equals(getline(2), 'αβα', 'failed at #9') + call g:assert.equals(getline(3), 'αγα', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, strlen('αγα')+1, 0], 'failed at #9') + + " #10 + call append(0, ['αaαα', 'βbββ', 'γcγγ']) + execute "normal ggsr\14la" + call g:assert.equals(getline(1), 'αaαα', 'failed at #10') + call g:assert.equals(getline(2), 'αbβα', 'failed at #10') + call g:assert.equals(getline(3), 'αcγα', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcγα')+1, 0], 'failed at #10') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #11 + call append(0, ['aaa', 'bbb', 'ccc']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'aαaaα', 'failed at #11') + call g:assert.equals(getline(2), 'aαbaα', 'failed at #11') + call g:assert.equals(getline(3), 'aαcaα', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcaα')+1, 0], 'failed at #11') + + " #12 + call append(0, ['aαa', 'bβb', 'cγc']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'aααaα', 'failed at #12') + call g:assert.equals(getline(2), 'aαβaα', 'failed at #12') + call g:assert.equals(getline(3), 'aαγaα', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαγaα')+1, 0], 'failed at #12') + + " #13 + call append(0, ['aaαa', 'bbβb', 'ccγc']) + execute "normal ggsr\14la" + call g:assert.equals(getline(1), 'aαaαaα', 'failed at #13') + call g:assert.equals(getline(2), 'aαbβaα', 'failed at #13') + call g:assert.equals(getline(3), 'aαcγaα', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcγaα')+1, 0], 'failed at #13') + + " #14 + call append(0, ['αaα', 'βbβ', 'γcγ']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'aαaaα', 'failed at #14') + call g:assert.equals(getline(2), 'aαbaα', 'failed at #14') + call g:assert.equals(getline(3), 'aαcaα', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcaα')+1, 0], 'failed at #14') + + " #15 + call append(0, ['ααα', 'βββ', 'γγγ']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'aααaα', 'failed at #15') + call g:assert.equals(getline(2), 'aαβaα', 'failed at #15') + call g:assert.equals(getline(3), 'aαγaα', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαγaα')+1, 0], 'failed at #15') + + " #16 + call append(0, ['αaαα', 'βbββ', 'γcγγ']) + execute "normal ggsr\14la" + call g:assert.equals(getline(1), 'aαaαaα', 'failed at #16') + call g:assert.equals(getline(2), 'aαbβaα', 'failed at #16') + call g:assert.equals(getline(3), 'aαcγaα', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcγaα')+1, 0], 'failed at #16') + + unlet g:operator#sandwich#recipes + + " #17 + call append(0, ['a“a', 'b“b', 'c“c']) + execute "normal ggsr\11l(" + call g:assert.equals(getline(1), '(“)', 'failed at #17') + call g:assert.equals(getline(2), '(“)', 'failed at #17') + call g:assert.equals(getline(3), '(“)', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 3, strlen('(“)')+1, 0], 'failed at #17') + + " #18 + call append(0, ['aa“a', 'bb“b', 'cc“c']) + execute "normal ggsr\14l(" + call g:assert.equals(getline(1), '(a“)', 'failed at #18') + call g:assert.equals(getline(2), '(b“)', 'failed at #18') + call g:assert.equals(getline(3), '(c“)', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 3, strlen('(c“)')+1, 0], 'failed at #18') + + " #19 + call append(0, ['α“α', 'β“β', 'γ“γ']) + execute "normal ggsr\11l(" + call g:assert.equals(getline(1), '(“)', 'failed at #19') + call g:assert.equals(getline(2), '(“)', 'failed at #19') + call g:assert.equals(getline(3), '(“)', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 3, strlen('(“)')+1, 0], 'failed at #19') + + " #20 + call append(0, ['αa“α', 'βb“β', 'γc“γ']) + execute "normal ggsr\14l(" + call g:assert.equals(getline(1), '(a“)', 'failed at #20') + call g:assert.equals(getline(2), '(b“)', 'failed at #20') + call g:assert.equals(getline(3), '(c“)', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 3, strlen('(c“)')+1, 0], 'failed at #20') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #21 + call append(0, ['aaa', 'bbb', 'ccc']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), '“a“', 'failed at #21') + call g:assert.equals(getline(2), '“b“', 'failed at #21') + call g:assert.equals(getline(3), '“c“', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c“')+1, 0], 'failed at #21') + + " #22 + call append(0, ['a“a', 'b“b', 'c“c']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), '“““', 'failed at #22') + call g:assert.equals(getline(2), '“““', 'failed at #22') + call g:assert.equals(getline(3), '“““', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 3, strlen('“““')+1, 0], 'failed at #22') + + " #23 + call append(0, ['aa“a', 'bb“b', 'cc“c']) + execute "normal ggsr\14la" + call g:assert.equals(getline(1), '“a““', 'failed at #23') + call g:assert.equals(getline(2), '“b““', 'failed at #23') + call g:assert.equals(getline(3), '“c““', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c““')+1, 0], 'failed at #23') + + " #24 + call append(0, ['αaα', 'βbβ', 'γcγ']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), '“a“', 'failed at #24') + call g:assert.equals(getline(2), '“b“', 'failed at #24') + call g:assert.equals(getline(3), '“c“', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c“')+1, 0], 'failed at #24') + + " #25 + call append(0, ['α“α', 'β“β', 'γ“γ']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), '“““', 'failed at #25') + call g:assert.equals(getline(2), '“““', 'failed at #25') + call g:assert.equals(getline(3), '“““', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 3, strlen('“““')+1, 0], 'failed at #25') + + " #26 + call append(0, ['αa“α', 'βb“β', 'γc“γ']) + execute "normal ggsr\14la" + call g:assert.equals(getline(1), '“a““', 'failed at #26') + call g:assert.equals(getline(2), '“b““', 'failed at #26') + call g:assert.equals(getline(3), '“c““', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c““')+1, 0], 'failed at #26') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #27 + call append(0, ['aaa', 'bbb', 'ccc']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'a“aa“', 'failed at #27') + call g:assert.equals(getline(2), 'a“ba“', 'failed at #27') + call g:assert.equals(getline(3), 'a“ca“', 'failed at #27') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #27') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #27') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“ca“')+1, 0], 'failed at #27') + + " #28 + call append(0, ['a“a', 'b“b', 'c“c']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'a““a“', 'failed at #28') + call g:assert.equals(getline(2), 'a““a“', 'failed at #28') + call g:assert.equals(getline(3), 'a““a“', 'failed at #28') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #28') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #28') + call g:assert.equals(getpos("']"), [0, 3, strlen('a““a“')+1, 0], 'failed at #28') + + " #29 + call append(0, ['aa“a', 'bb“b', 'cc“c']) + execute "normal ggsr\14la" + call g:assert.equals(getline(1), 'a“a“a“', 'failed at #29') + call g:assert.equals(getline(2), 'a“b“a“', 'failed at #29') + call g:assert.equals(getline(3), 'a“c“a“', 'failed at #29') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #29') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #29') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“c“a“')+1, 0], 'failed at #29') + + " #30 + call append(0, ['αaα', 'βbβ', 'γcγ']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'a“aa“', 'failed at #30') + call g:assert.equals(getline(2), 'a“ba“', 'failed at #30') + call g:assert.equals(getline(3), 'a“ca“', 'failed at #30') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #30') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #30') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“ca“')+1, 0], 'failed at #30') + + " #31 + call append(0, ['α“α', 'β“β', 'γ“γ']) + execute "normal ggsr\11la" + call g:assert.equals(getline(1), 'a““a“', 'failed at #31') + call g:assert.equals(getline(2), 'a““a“', 'failed at #31') + call g:assert.equals(getline(3), 'a““a“', 'failed at #31') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #31') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #31') + call g:assert.equals(getpos("']"), [0, 3, strlen('a““a“')+1, 0], 'failed at #31') + + " #32 + call append(0, ['αa“α', 'βb“β', 'γc“γ']) + execute "normal ggsr\14la" + call g:assert.equals(getline(1), 'a“a“a“', 'failed at #32') + call g:assert.equals(getline(2), 'a“b“a“', 'failed at #32') + call g:assert.equals(getline(3), 'a“c“a“', 'failed at #32') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #32') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #32') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“c“a“')+1, 0], 'failed at #32') +endfunction +"}}} +function! s:suite.blockwise_n_option_cursor() abort "{{{ + set whichwrap=h,l + + """"" cursor + """ default + " #1 + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg2sr\29l[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #1') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #1') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + + " #2 + execute "normal sr\25l[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #2') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #2') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + + %delete + + """ inner_head + " #3 + call operator#sandwich#set('replace', 'block', 'cursor', 'inner_head') + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg2sr\29l[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #3') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #3') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #3') + + " #4 + execute "normal sr\25l[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #4') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #4') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #4') + + %delete + + """ keep + " #5 + call operator#sandwich#set('replace', 'block', 'cursor', 'keep') + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg2sr\29l[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #5') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #5') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #5') + + " #6 + execute "normal 2lsr\25l[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #6') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #6') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #6') + + %delete + + """ inner_tail + " #7 + call operator#sandwich#set('replace', 'block', 'cursor', 'inner_tail') + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg2sr\29l[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #7') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #7') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 7, 0], 'failed at #7') + + " #8 + execute "normal gg2lsr\25l[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #8') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #8') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 6, 0], 'failed at #8') + + %delete + + """ head + " #9 + call operator#sandwich#set('replace', 'block', 'cursor', 'head') + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg2sr\29l[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #9') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #9') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + + " #10 + execute "normal 2lsr\25l[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #10') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #10') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #10') + + %delete + + """ tail + " #11 + call operator#sandwich#set('replace', 'block', 'cursor', 'tail') + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg2sr\29l[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #11') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #11') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #11') + + " #12 + execute "normal 6h2ksr\25l[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #12') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #12') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 7, 0], 'failed at #12') + + %delete + + """"" recipe option + " #13 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('replace', 'block', 'cursor', 'inner_tail') + call setline('.', '[foo]') + execute "normal ^sr\5l1" + call g:assert.equals(getline('.'), '(foo)', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #13') +endfunction +"}}} +function! s:suite.blockwise_n_option_noremap() abort "{{{ + set whichwrap=h,l + + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #1') + call g:assert.equals(getline(2), '"bar"', 'failed at #1') + call g:assert.equals(getline(3), '"baz"', 'failed at #1') + + %delete + + " #2 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + call g:assert.equals(getline(2), '(bar)', 'failed at #2') + call g:assert.equals(getline(3), '(baz)', 'failed at #2') + + %delete + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '{foo}', 'failed at #3') + call g:assert.equals(getline(2), '{bar}', 'failed at #3') + call g:assert.equals(getline(3), '{baz}', 'failed at #3') + + %delete + + " #4 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #4') + call g:assert.equals(getline(2), '"bar"', 'failed at #4') + call g:assert.equals(getline(3), '"baz"', 'failed at #4') + + """ off + %delete + call operator#sandwich#set('replace', 'block', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '{foo}', 'failed at #5') + call g:assert.equals(getline(2), '{bar}', 'failed at #5') + call g:assert.equals(getline(3), '{baz}', 'failed at #5') + + %delete + + " #6 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #6') + call g:assert.equals(getline(2), '"bar"', 'failed at #6') + call g:assert.equals(getline(3), '"baz"', 'failed at #6') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #7') + call g:assert.equals(getline(2), '"bar"', 'failed at #7') + call g:assert.equals(getline(3), '"baz"', 'failed at #7') + + %delete + + " #8 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '(foo)', 'failed at #8') + call g:assert.equals(getline(2), '(bar)', 'failed at #8') + call g:assert.equals(getline(3), '(baz)', 'failed at #8') +endfunction +"}}} +function! s:suite.blockwise_n_option_regex() abort "{{{ + set whichwrap=h,l + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal ggsr\35l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #1') + call g:assert.equals(getline(2), '"bar"', 'failed at #1') + call g:assert.equals(getline(3), '"baz"', 'failed at #1') + + %delete + + " #2 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal ggsr\29l\"" + call g:assert.equals(getline(1), '"88foo88"', 'failed at #2') + call g:assert.equals(getline(2), '"88bar88"', 'failed at #2') + call g:assert.equals(getline(3), '"88baz88"', 'failed at #2') + + %delete + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal ggsr\35l\"" + call g:assert.equals(getline(1), '\d\+foo\d\+', 'failed at #3') + call g:assert.equals(getline(2), '\d\+bar\d\+', 'failed at #3') + call g:assert.equals(getline(3), '\d\+baz\d\+', 'failed at #3') + + %delete + + " #4 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal ggsr\29l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #4') + call g:assert.equals(getline(2), '"bar"', 'failed at #4') + call g:assert.equals(getline(3), '"baz"', 'failed at #4') + + """ on + %delete + call operator#sandwich#set('replace', 'block', 'regex', 1) + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #5 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal ggsr\35l\"" + call g:assert.equals(getline(1), '\d\+foo\d\+', 'failed at #5') + call g:assert.equals(getline(2), '\d\+bar\d\+', 'failed at #5') + call g:assert.equals(getline(3), '\d\+baz\d\+', 'failed at #5') + + %delete + + " #6 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal ggsr\29l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #6') + call g:assert.equals(getline(2), '"bar"', 'failed at #6') + call g:assert.equals(getline(3), '"baz"', 'failed at #6') + + %delete + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal ggsr\35l\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #7') + call g:assert.equals(getline(2), '"bar"', 'failed at #7') + call g:assert.equals(getline(3), '"baz"', 'failed at #7') + + %delete + + " #8 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal ggsr\29l\"" + call g:assert.equals(getline(1), '"88foo88"', 'failed at #8') + call g:assert.equals(getline(2), '"88bar88"', 'failed at #8') + call g:assert.equals(getline(3), '"88baz88"', 'failed at #8') +endfunction +"}}} +function! s:suite.blockwise_n_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + set whichwrap=h,l + + """ 1 + " #1 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0sr\17l(" + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + call g:assert.equals(getline(2), '(bar)', 'failed at #1') + call g:assert.equals(getline(3), '(baz)', 'failed at #1') + + %delete + + " #2 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sr\20l(" + call g:assert.equals(getline(1), ' (foo)', 'failed at #2') + call g:assert.equals(getline(2), ' (bar)', 'failed at #2') + call g:assert.equals(getline(3), ' (baz)', 'failed at #2') + + %delete + + " #3 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0sr\20l(" + call g:assert.equals(getline(1), '(foo) ', 'failed at #3') + call g:assert.equals(getline(2), '(bar) ', 'failed at #3') + call g:assert.equals(getline(3), '(baz) ', 'failed at #3') + + %delete + + " #4 + " do not skip! + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0sr\23l(" + call g:assert.equals(getline(1), '("foo")', 'failed at #4') + call g:assert.equals(getline(2), '("bar")', 'failed at #4') + call g:assert.equals(getline(3), '("baz")', 'failed at #4') + + %delete + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sr\20l(" + call g:assert.equals(getline(1), ' "foo"', 'failed at #5') + call g:assert.equals(getline(2), ' "bar"', 'failed at #5') + call g:assert.equals(getline(3), ' "baz"', 'failed at #5') + + %delete + + """ 2 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'block', 'skip_space', 2) + + " #6 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0sr\17l(" + call g:assert.equals(getline(1), '(foo)', 'failed at #6') + call g:assert.equals(getline(2), '(bar)', 'failed at #6') + call g:assert.equals(getline(3), '(baz)', 'failed at #6') + + %delete + + " #7 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sr\20l(" + call g:assert.equals(getline(1), ' (foo)', 'failed at #7') + call g:assert.equals(getline(2), ' (bar)', 'failed at #7') + call g:assert.equals(getline(3), ' (baz)', 'failed at #7') + + %delete + + " #8 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0sr\20l(" + call g:assert.equals(getline(1), '(foo) ', 'failed at #8') + call g:assert.equals(getline(2), '(bar) ', 'failed at #8') + call g:assert.equals(getline(3), '(baz) ', 'failed at #8') + + %delete + + " #9 + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0sr\23l(" + call g:assert.equals(getline(1), ' (foo) ', 'failed at #9') + call g:assert.equals(getline(2), ' (bar) ', 'failed at #9') + call g:assert.equals(getline(3), ' (baz) ', 'failed at #9') + + %delete + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sr\20l(" + call g:assert.equals(getline(1), ' "foo"', 'failed at #10') + call g:assert.equals(getline(2), ' "bar"', 'failed at #10') + call g:assert.equals(getline(3), ' "baz"', 'failed at #10') + + %delete + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'block', 'skip_space', 0) + + " #11 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0sr\17l(" + call g:assert.equals(getline(1), '(foo)', 'failed at #11') + call g:assert.equals(getline(2), '(bar)', 'failed at #11') + call g:assert.equals(getline(3), '(baz)', 'failed at #11') + + %delete + + " #12 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sr\20l(" + call g:assert.equals(getline(1), ' "foo"', 'failed at #12') + call g:assert.equals(getline(2), ' "bar"', 'failed at #12') + call g:assert.equals(getline(3), ' "baz"', 'failed at #12') + + %delete + + " #13 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0sr\20l(" + call g:assert.equals(getline(1), '"foo" ', 'failed at #13') + call g:assert.equals(getline(2), '"bar" ', 'failed at #13') + call g:assert.equals(getline(3), '"baz" ', 'failed at #13') + + %delete + + " #14 + " do not skip! + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0sr\23l(" + call g:assert.equals(getline(1), '("foo")', 'failed at #14') + call g:assert.equals(getline(2), '("bar")', 'failed at #14') + call g:assert.equals(getline(3), '("baz")', 'failed at #14') + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}, {'buns': ['(', ')']}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0sr\20l(" + call g:assert.equals(getline(1), ' (foo)', 'failed at #2') + call g:assert.equals(getline(2), ' (bar)', 'failed at #2') + call g:assert.equals(getline(3), ' (baz)', 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_n_option_skip_char() abort "{{{ + set whichwrap=h,l + + """ off + " #1 + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal ggsr\29l\"" + call g:assert.equals(getline(1), 'aa(foo)bb', 'failed at #1') + call g:assert.equals(getline(2), 'aa(bar)bb', 'failed at #1') + call g:assert.equals(getline(3), 'aa(baz)bb', 'failed at #1') + + %delete + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal ggsr\29l\"" + call g:assert.equals(getline(1), 'aa"foo"bb', 'failed at #2') + call g:assert.equals(getline(2), 'aa"bar"bb', 'failed at #2') + call g:assert.equals(getline(3), 'aa"baz"bb', 'failed at #2') + + """ on + %delete + unlet! g:operator#sandwich#recipes + call operator#sandwich#set('replace', 'block', 'skip_char', 1) + + " #3 + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal ggsr\29l\"" + call g:assert.equals(getline(1), 'aa"foo"bb', 'failed at #3') + call g:assert.equals(getline(2), 'aa"bar"bb', 'failed at #3') + call g:assert.equals(getline(3), 'aa"baz"bb', 'failed at #3') + + %delete + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal ggsr\29l\"" + call g:assert.equals(getline(1), 'aa(foo)bb', 'failed at #4') + call g:assert.equals(getline(2), 'aa(bar)bb', 'failed at #4') + call g:assert.equals(getline(3), 'aa(baz)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.blockwise_n_option_command() abort "{{{ + set whichwrap=h,l + + " #1 + call operator#sandwich#set('replace', 'block', 'command', ["normal! `[d\`]"]) + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '', 'failed at #1') + call g:assert.equals(getline(2), '', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + + " #2 + call operator#sandwich#set('replace', 'block', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal ggsr\17l\"" + call g:assert.equals(getline(1), '', 'failed at #2') + call g:assert.equals(getline(2), '', 'failed at #2') + call g:assert.equals(getline(3), '', 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_n_option_query_once() abort "{{{ + set whichwrap=h,l + + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call append(0, ['"""foo"""', '"""bar"""', '"""baz"""']) + execute "normal gg3sr\29l([{" + call g:assert.equals(getline(1), '([{foo}])', 'failed at #1') + call g:assert.equals(getline(2), '([{bar}])', 'failed at #1') + call g:assert.equals(getline(3), '([{baz}])', 'failed at #1') + + %delete + + " #2 + call append(0, ['"""foo"""', '"""bar"""', '"""baz"""']) + execute "normal gg3sr\29l1" + call g:assert.equals(getline(1), '(((foo)))', 'failed at #2') + call g:assert.equals(getline(2), '(((bar)))', 'failed at #2') + call g:assert.equals(getline(3), '(((baz)))', 'failed at #2') + + %delete + + """ on + call operator#sandwich#set('replace', 'block', 'query_once', 1) + + " #3 + call append(0, ['"""foo"""', '"""bar"""', '"""baz"""']) + execute "normal gg3sr\29l(" + call g:assert.equals(getline(1), '(((foo)))', 'failed at #3') + call g:assert.equals(getline(2), '(((bar)))', 'failed at #3') + call g:assert.equals(getline(3), '(((baz)))', 'failed at #3') + + " #4 + call append(0, ['"""foo"""', '"""bar"""', '"""baz"""']) + execute "normal gg3sr\29l0[{" + call g:assert.equals(getline(1), '([{foo}])', 'failed at #4') + call g:assert.equals(getline(2), '([{bar}])', 'failed at #4') + call g:assert.equals(getline(3), '([{baz}])', 'failed at #4') +endfunction +"}}} +function! s:suite.blockwise_n_option_expr() abort "{{{ + """"" expr + set whichwrap=h,l + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal ggsr\17la" + call g:assert.equals(getline(1), '1+1foo1+2', 'failed at #1') + call g:assert.equals(getline(2), '1+1bar1+2', 'failed at #1') + call g:assert.equals(getline(3), '1+1baz1+2', 'failed at #1') + + %delete + + " #2 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal ggsr\17l1" + call g:assert.equals(getline(1), '2foo3', 'failed at #2') + call g:assert.equals(getline(2), '2bar3', 'failed at #2') + call g:assert.equals(getline(3), '2baz3', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'block', 'expr', 1) + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal ggsr\17la" + call g:assert.equals(getline(1), '2foo3', 'failed at #3') + call g:assert.equals(getline(2), '2bar3', 'failed at #3') + call g:assert.equals(getline(3), '2baz3', 'failed at #3') + + %delete + + " #4 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal ggsr\17lb" + call g:assert.equals(getline(1), '"foo"', 'failed at #4') + call g:assert.equals(getline(2), '"bar"', 'failed at #4') + call g:assert.equals(getline(3), '"baz"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + %delete + + " #5 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal ggsr\17lc" + call g:assert.equals(getline(1), '"foo"', 'failed at #5') + call g:assert.equals(getline(2), '"bar"', 'failed at #5') + call g:assert.equals(getline(3), '"baz"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + %delete + + " #6 + call append(0, ['"''foo''"', '"''bar''"', '"''baz''"']) + execute "normal gg2sr\23lab" + call g:assert.equals(getline(1), '2''foo''3', 'failed at #6') + call g:assert.equals(getline(2), '2''bar''3', 'failed at #6') + call g:assert.equals(getline(3), '2''baz''3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + %delete + + " #7 + call append(0, ['"''foo''"', '"''bar''"', '"''baz''"']) + execute "normal gg2sr\23lac" + call g:assert.equals(getline(1), '2''foo''3', 'failed at #7') + call g:assert.equals(getline(2), '2''bar''3', 'failed at #7') + call g:assert.equals(getline(3), '2''baz''3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + %delete + + " #8 + call append(0, ['"''foo''"', '"''bar''"', '"''baz''"']) + execute "normal gg2sr\23lba" + call g:assert.equals(getline(1), '"''foo''"', 'failed at #8') + call g:assert.equals(getline(2), '"''bar''"', 'failed at #8') + call g:assert.equals(getline(3), '"''baz''"', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + %delete + + " #9 + call append(0, ['"''foo''"', '"''bar''"', '"''baz''"']) + execute "normal gg2sr\23lca" + call g:assert.equals(getline(1), '"''foo''"', 'failed at #9') + call g:assert.equals(getline(2), '"''bar''"', 'failed at #9') + call g:assert.equals(getline(3), '"''baz''"', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + %delete + + " #10 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal ggsr\17l0" + call g:assert.equals(getline(1), '1+1foo1+2', 'failed at #10') + call g:assert.equals(getline(2), '1+1bar1+2', 'failed at #10') + call g:assert.equals(getline(3), '1+1baz1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.blockwise_n_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :execute "normal ggsr\a\"a" + call g:assert.equals(getline('.'), '"bar"', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + " #2 + call setline('.', '"bar"') + execute "normal ggsr\a\"1" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'block', 'listexpr', 1) + call setline('.', '"bar"') + execute "normal ggsr\a\"a" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + + " #4 + call setline('.', '"bar"') + execute "normal ggsr\a\"b" + call g:assert.equals(getline('.'), '"bar"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :execute "normal ggsr\a\"0" + call g:assert.equals(getline('.'), '"bar"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', '"bar"') + execute "normal ggsr\a\"1" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.blockwise_n_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + + """ -1 + call operator#sandwich#set('replace', 'block', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ']', 'failed at #1') + call g:assert.equals(getline(5), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), ' foo', 'failed at #2') + call g:assert.equals(getline(4), ' ]', 'failed at #2') + call g:assert.equals(getline(5), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), ' foo', 'failed at #3') + call g:assert.equals(getline(4), ' ]', 'failed at #3') + call g:assert.equals(getline(5), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), ' foo', 'failed at #4') + " call g:assert.equals(getline(4), ' ]', 'failed at #4') + call g:assert.equals(getline(5), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call setline('.', ' "foo"') + " execute "normal ^sr\2i\"a" + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), ' foo', 'failed at #5') + " call g:assert.equals(getline(4), ' ]', 'failed at #5') + " call g:assert.equals(getline(5), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 3, 17, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 5, 34, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('replace', 'block', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ']', 'failed at #6') + call g:assert.equals(getline(5), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), 'foo', 'failed at #7') + call g:assert.equals(getline(4), ']', 'failed at #7') + call g:assert.equals(getline(5), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), 'foo', 'failed at #8') + call g:assert.equals(getline(4), ']', 'failed at #8') + call g:assert.equals(getline(5), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), 'foo', 'failed at #9') + call g:assert.equals(getline(4), ']', 'failed at #9') + call g:assert.equals(getline(5), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), 'foo', 'failed at #10') + call g:assert.equals(getline(4), ']', 'failed at #10') + call g:assert.equals(getline(5), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('replace', 'block', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), ' foo', 'failed at #11') + call g:assert.equals(getline(4), ' ]', 'failed at #11') + call g:assert.equals(getline(5), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), ' foo', 'failed at #12') + call g:assert.equals(getline(4), ' ]', 'failed at #12') + call g:assert.equals(getline(5), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), ' foo', 'failed at #13') + call g:assert.equals(getline(4), ' ]', 'failed at #13') + call g:assert.equals(getline(5), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), ' foo', 'failed at #14') + call g:assert.equals(getline(4), ' ]', 'failed at #14') + call g:assert.equals(getline(5), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), ' foo', 'failed at #15') + call g:assert.equals(getline(4), ' ]', 'failed at #15') + call g:assert.equals(getline(5), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('replace', 'block', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), ' foo', 'failed at #16') + call g:assert.equals(getline(4), ' ]', 'failed at #16') + call g:assert.equals(getline(5), ' }', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), ' foo', 'failed at #17') + call g:assert.equals(getline(4), ' ]', 'failed at #17') + call g:assert.equals(getline(5), ' }', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), ' foo', 'failed at #18') + call g:assert.equals(getline(4), ' ]', 'failed at #18') + call g:assert.equals(getline(5), ' }', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), ' foo', 'failed at #19') + call g:assert.equals(getline(4), ' ]', 'failed at #19') + call g:assert.equals(getline(5), ' }', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), ' foo', 'failed at #20') + call g:assert.equals(getline(4), ' ]', 'failed at #20') + call g:assert.equals(getline(5), ' }', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('replace', 'block', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), ' foo', 'failed at #21') + " call g:assert.equals(getline(4), ' ]', 'failed at #21') + call g:assert.equals(getline(5), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), ' foo', 'failed at #22') + " call g:assert.equals(getline(4), ' ]', 'failed at #22') + call g:assert.equals(getline(5), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), ' foo', 'failed at #23') + " call g:assert.equals(getline(4), ' ]', 'failed at #23') + call g:assert.equals(getline(5), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), ' foo', 'failed at #24') + " call g:assert.equals(getline(4), ' ]', 'failed at #24') + call g:assert.equals(getline(5), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), ' foo', 'failed at #25') + " call g:assert.equals(getline(4), ' ]', 'failed at #25') + call g:assert.equals(getline(5), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + execute "normal ^sr\2i\"0" + call g:assert.equals(getline(1), ' {', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), 'foo', 'failed at #26') + call g:assert.equals(getline(4), ']', 'failed at #26') + call g:assert.equals(getline(5), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') +endfunction +"}}} +function! s:suite.blockwise_n_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n", "\n}"], 'input': ['a']}, + \ {'buns': ["{\n", "\n}"], 'indentkeys': '0{,0},0),:,0#,!^F,e', 'input': ['1']}, + \ ] + + """ cinkeys + call operator#sandwich#set('replace', 'block', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', 3) + + " #2 + setlocal cinkeys=0{,0},0),:,0#,!^F,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys+', 'O,o') + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys-', 'O,o') + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' "foo"') + execute "normal ^sr\2i\"1" + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('replace', 'block', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0{,0},0),:,0#,!^F,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys+', 'O,o') + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys-', 'O,o') + call setline('.', ' "foo"') + execute "normal ^sr\2i\"a" + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' "foo"') + execute "normal ^sr\2i\"1" + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +function! s:suite.blockwise_x_default_recipes() abort "{{{ + " #1 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsr[" + call g:assert.equals(getline(1), '[foo]', 'failed at #1') + call g:assert.equals(getline(2), '[bar]', 'failed at #1') + call g:assert.equals(getline(3), '[baz]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['[foo]', '[bar]', '[baz]']) + execute "normal gg\2j4lsr{" + call g:assert.equals(getline(1), '{foo}', 'failed at #2') + call g:assert.equals(getline(2), '{bar}', 'failed at #2') + call g:assert.equals(getline(3), '{baz}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal gg\2j4lsr<" + call g:assert.equals(getline(1), '', 'failed at #3') + call g:assert.equals(getline(2), '', 'failed at #3') + call g:assert.equals(getline(3), '', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['', '', '']) + execute "normal gg\2j4lsr(" + call g:assert.equals(getline(1), '(foo)', 'failed at #4') + call g:assert.equals(getline(2), '(bar)', 'failed at #4') + call g:assert.equals(getline(3), '(baz)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #4') +endfunction +"}}} +function! s:suite.blockwise_x_not_registered() abort "{{{ + " #1 + call append(0, ['afooa', 'abara', 'abaza']) + execute "normal gg\2j4lsrb" + call g:assert.equals(getline(1), 'bfoob', 'failed at #1') + call g:assert.equals(getline(2), 'bbarb', 'failed at #1') + call g:assert.equals(getline(3), 'bbazb', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + " #2 + call append(0, ['+foo+', '+bar+', '+baz+']) + execute "normal gg\2j4lsr*" + call g:assert.equals(getline(1), '*foo*', 'failed at #2') + call g:assert.equals(getline(2), '*bar*', 'failed at #2') + call g:assert.equals(getline(3), '*baz*', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_x_positioning() abort "{{{ + " #1 + call append(0, ['(foo)bar', '(foo)bar', '(foo)bar']) + execute "normal gg\2j4lsr[" + call g:assert.equals(getline(1), '[foo]bar', 'failed at #1') + call g:assert.equals(getline(2), '[foo]bar', 'failed at #1') + call g:assert.equals(getline(3), '[foo]bar', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo(bar)', 'foo(bar)', 'foo(bar)']) + execute "normal gg3l\2j4lsr[" + call g:assert.equals(getline(1), 'foo[bar]', 'failed at #2') + call g:assert.equals(getline(2), 'foo[bar]', 'failed at #2') + call g:assert.equals(getline(3), 'foo[bar]', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 9, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['foo(bar)baz', 'foo(bar)baz', 'foo(bar)baz']) + execute "normal gg3l\2j4lsr[" + call g:assert.equals(getline(1), 'foo[bar]baz', 'failed at #3') + call g:assert.equals(getline(2), 'foo[bar]baz', 'failed at #3') + call g:assert.equals(getline(3), 'foo[bar]baz', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 9, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['(foo)', '(bar)', 'bazbaz']) + execute "normal gg\2j4lsr[" + call g:assert.equals(getline(1), '[foo]', 'failed at #4') + call g:assert.equals(getline(2), '[bar]', 'failed at #4') + call g:assert.equals(getline(3), 'bazbaz', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 2, 6, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['(foo)', 'barbar', '(baz)']) + execute "normal gg\2j4lsr[" + call g:assert.equals(getline(1), '[foo]', 'failed at #5') + call g:assert.equals(getline(2), 'barbar', 'failed at #5') + call g:assert.equals(getline(3), '[baz]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + + %delete + + " #6 + call append(0, ['foofoo', '(bar)', '(baz)']) + execute "normal gg\2j4lsr[" + call g:assert.equals(getline(1), 'foofoo', 'failed at #6') + call g:assert.equals(getline(2), '[bar]', 'failed at #6') + call g:assert.equals(getline(3), '[baz]', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 2, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 2, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #6') + + %delete + + " #7 + call append(0, ['(foo)', '(baar)', '(baaz)']) + execute "normal gg\2j5lsr[" + call g:assert.equals(getline(1), '[foo]', 'failed at #7') + call g:assert.equals(getline(2), '[baar]', 'failed at #7') + call g:assert.equals(getline(3), '[baaz]', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 7, 0], 'failed at #7') + + %delete + + " #8 + call append(0, ['(fooo)', '(bar)', '(baaz)']) + execute "normal gg\2j5lsr[" + call g:assert.equals(getline(1), '[fooo]', 'failed at #8') + call g:assert.equals(getline(2), '[bar]', 'failed at #8') + call g:assert.equals(getline(3), '[baaz]', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 7, 0], 'failed at #8') + + %delete + + " #9 + call append(0, ['(fooo)', '(baar)', '(baz)']) + set virtualedit=block + execute "normal gg\2j5lsr[" + call g:assert.equals(getline(1), '[fooo]', 'failed at #9') + call g:assert.equals(getline(2), '[baar]', 'failed at #9') + call g:assert.equals(getline(3), '[baz]', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #9') + set virtualedit& + + %delete + + """ terminal-extended block-wise visual mode + " #10 + call append(0, ['"fooo"', '"baaar"', '"baz"']) + execute "normal gg\2j$sr(" + call g:assert.equals(getline(1), '(fooo)', 'failed at #10') + call g:assert.equals(getline(2), '(baaar)', 'failed at #10') + call g:assert.equals(getline(3), '(baz)', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #10') + + %delete + + " #11 + call append(0, ['"foooo"', '"bar"', '"baaz"']) + execute "normal gg\2j$sr(" + call g:assert.equals(getline(1), '(foooo)', 'failed at #11') + call g:assert.equals(getline(2), '(bar)', 'failed at #11') + call g:assert.equals(getline(3), '(baaz)', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, 7, 0], 'failed at #11') + + %delete + + " #12 + call append(0, ['"fooo"', '', '"baz"']) + execute "normal gg\2j$sr(" + call g:assert.equals(getline(1), '(fooo)', 'failed at #12') + call g:assert.equals(getline(2), '', 'failed at #12') + call g:assert.equals(getline(3), '(baz)', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #12') +endfunction +"}}} +function! s:suite.blockwise_x_a_character() abort "{{{ + " #1 + call append(0, ['(a)', '(b)', '(c)']) + execute "normal gg\2j2lsr[" + call g:assert.equals(getline(1), '[a]', 'failed at #1') + call g:assert.equals(getline(2), '[b]', 'failed at #1') + call g:assert.equals(getline(3), '[c]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 4, 0], 'failed at #1') +endfunction +"}}} +function! s:suite.blockwise_x_nothing_inside() abort "{{{ + " #1 + call append(0, ['()', '()', '()']) + execute "normal gg\2jlsr[" + call g:assert.equals(getline(1), '[]', 'failed at #1') + call g:assert.equals(getline(2), '[]', 'failed at #1') + call g:assert.equals(getline(3), '[]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 3, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['foo()bar', 'foo()bar', 'foo()bar']) + execute "normal gg3l\2jlsr[" + call g:assert.equals(getline(1), 'foo[]bar', 'failed at #2') + call g:assert.equals(getline(2), 'foo[]bar', 'failed at #2') + call g:assert.equals(getline(3), 'foo[]bar', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_x_count() abort "{{{ + " #1 + call append(0, ['[(foo)]', '[(bar)]', '[(baz)]']) + execute "normal gg\2j6l3sr({" + call g:assert.equals(getline(1), '({foo})', 'failed at #1') + call g:assert.equals(getline(2), '({bar})', 'failed at #1') + call g:assert.equals(getline(3), '({baz})', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 8, 0], 'failed at #1') + + %delete + + " #2 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg\2j8l3sr({[" + call g:assert.equals(getline(1), '({[foo]})', 'failed at #2') + call g:assert.equals(getline(2), '({[bar]})', 'failed at #2') + call g:assert.equals(getline(3), '({[baz]})', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 10, 0], 'failed at #2') + + %delete + + " #3 + call append(0, ['{[afoob]}', '{[(bar)]}', '{[(baz)]}']) + execute "normal gg\2j8l3sr({[" + call g:assert.equals(getline(1), '({afoob})', 'failed at #3') + call g:assert.equals(getline(2), '({[bar]})', 'failed at #3') + call g:assert.equals(getline(3), '({[baz]})', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 10, 0], 'failed at #3') + + %delete + + " #4 + call append(0, ['{[(foo)]}', '{[abarb]}', '{[(baz)]}']) + execute "normal gg\2j8l3sr({[" + call g:assert.equals(getline(1), '({[foo]})', 'failed at #4') + call g:assert.equals(getline(2), '({abarb})', 'failed at #4') + call g:assert.equals(getline(3), '({[baz]})', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 10, 0], 'failed at #4') + + %delete + + " #5 + call append(0, ['{[(foo)]}', '{[(bar)]}', '{[abazb]}']) + execute "normal gg\2j8l3sr({[" + call g:assert.equals(getline(1), '({[foo]})', 'failed at #5') + call g:assert.equals(getline(2), '({[bar]})', 'failed at #5') + call g:assert.equals(getline(3), '({abazb})', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 10, 0], 'failed at #5') +endfunction +"}}} +function! s:suite.blockwise_x_external_textobj() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i[', 'a[']}, + \ {'external': ['i(', 'a(']}, + \ {'external': ['it', 'at']}, + \ ] + + " #1 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #1') + call g:assert.equals(getline(2), '"bar"', 'failed at #1') + call g:assert.equals(getline(3), '"baz"', 'failed at #1') + + %delete + + " #2 + call append(0, ['[foo]', '[bar]', '[baz]']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #2') + call g:assert.equals(getline(2), '"bar"', 'failed at #2') + call g:assert.equals(getline(3), '"baz"', 'failed at #2') + + %delete + + " #3 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #3') + call g:assert.equals(getline(2), '"bar"', 'failed at #3') + call g:assert.equals(getline(3), '"baz"', 'failed at #3') + + %delete + + " #4 + call append(0, ['foo', 'bar', 'baz']) + execute "normal gg\2j17lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #4') + call g:assert.equals(getline(2), '"bar"', 'failed at #4') + call g:assert.equals(getline(3), '"baz"', 'failed at #4') + + %delete + + " #5 + xnoremap ii :call TextobjFail() + let g:operator#sandwich#recipes = [ + \ {'external': ['ii', 'a('], 'noremap': 0}, + \ ] + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '(foo)', 'failed at #5') + call g:assert.equals(getline(2), '(bar)', 'failed at #5') + call g:assert.equals(getline(3), '(baz)', 'failed at #5') + + unlet g:sandwich#recipes + unlet g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.blockwise_x_multibyte() abort "{{{ + " The reason why I use strlen() is that the byte length of a multibyte character is varied by 'encoding' option. + + set whichwrap=h,l + + " #1 + call append(0, ['aαa', 'bβb', 'cγc']) + execute "normal gg\2l2jsr(" + call g:assert.equals(getline(1), '(α)', 'failed at #1') + call g:assert.equals(getline(2), '(β)', 'failed at #1') + call g:assert.equals(getline(3), '(γ)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, strlen('(γ)')+1, 0], 'failed at #1') + + " #2 + call append(0, ['aaαa', 'bbβb', 'ccγc']) + execute "normal gg\3l2jsr(" + call g:assert.equals(getline(1), '(aα)', 'failed at #2') + call g:assert.equals(getline(2), '(bβ)', 'failed at #2') + call g:assert.equals(getline(3), '(cγ)', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, strlen('(cγ)')+1, 0], 'failed at #2') + + " #3 + call append(0, ['ααα', 'βββ', 'γγγ']) + execute "normal gg\2l2jsr(" + call g:assert.equals(getline(1), '(α)', 'failed at #3') + call g:assert.equals(getline(2), '(β)', 'failed at #3') + call g:assert.equals(getline(3), '(γ)', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, strlen('(γ)')+1, 0], 'failed at #3') + + " #4 + call append(0, ['αaαα', 'βbββ', 'γcγγ']) + execute "normal gg\3l2jsr(" + call g:assert.equals(getline(1), '(aα)', 'failed at #4') + call g:assert.equals(getline(2), '(bβ)', 'failed at #4') + call g:assert.equals(getline(3), '(cγ)', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, strlen('(cγ)')+1, 0], 'failed at #4') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['α', 'α'], 'input': ['a']} + \ ] + + " #5 + call append(0, ['aaa', 'bbb', 'ccc']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'αaα', 'failed at #5') + call g:assert.equals(getline(2), 'αbα', 'failed at #5') + call g:assert.equals(getline(3), 'αcα', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcα')+1, 0], 'failed at #5') + + " #6 + call append(0, ['aαa', 'bβb', 'cγc']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'ααα', 'failed at #6') + call g:assert.equals(getline(2), 'αβα', 'failed at #6') + call g:assert.equals(getline(3), 'αγα', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, strlen('αγα')+1, 0], 'failed at #6') + + " #7 + call append(0, ['aaαa', 'bbβb', 'ccγc']) + execute "normal gg\3l2jsra" + call g:assert.equals(getline(1), 'αaαα', 'failed at #7') + call g:assert.equals(getline(2), 'αbβα', 'failed at #7') + call g:assert.equals(getline(3), 'αcγα', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcγα')+1, 0], 'failed at #7') + + " #8 + call append(0, ['αaα', 'βbβ', 'γcγ']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'αaα', 'failed at #8') + call g:assert.equals(getline(2), 'αbα', 'failed at #8') + call g:assert.equals(getline(3), 'αcα', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcα')+1, 0], 'failed at #8') + + " #9 + call append(0, ['ααα', 'βββ', 'γγγ']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'ααα', 'failed at #9') + call g:assert.equals(getline(2), 'αβα', 'failed at #9') + call g:assert.equals(getline(3), 'αγα', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 3, strlen('αγα')+1, 0], 'failed at #9') + + " #10 + call append(0, ['αaαα', 'βbββ', 'γcγγ']) + execute "normal gg\3l2jsra" + call g:assert.equals(getline(1), 'αaαα', 'failed at #10') + call g:assert.equals(getline(2), 'αbβα', 'failed at #10') + call g:assert.equals(getline(3), 'αcγα', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, strlen('α')+1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 3, strlen('αcγα')+1, 0], 'failed at #10') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['aα', 'aα'], 'input': ['a']} + \ ] + + " #11 + call append(0, ['aaa', 'bbb', 'ccc']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'aαaaα', 'failed at #11') + call g:assert.equals(getline(2), 'aαbaα', 'failed at #11') + call g:assert.equals(getline(3), 'aαcaα', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcaα')+1, 0], 'failed at #11') + + " #12 + call append(0, ['aαa', 'bβb', 'cγc']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'aααaα', 'failed at #12') + call g:assert.equals(getline(2), 'aαβaα', 'failed at #12') + call g:assert.equals(getline(3), 'aαγaα', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαγaα')+1, 0], 'failed at #12') + + " #13 + call append(0, ['aaαa', 'bbβb', 'ccγc']) + execute "normal gg\3l2jsra" + call g:assert.equals(getline(1), 'aαaαaα', 'failed at #13') + call g:assert.equals(getline(2), 'aαbβaα', 'failed at #13') + call g:assert.equals(getline(3), 'aαcγaα', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcγaα')+1, 0], 'failed at #13') + + " #14 + call append(0, ['αaα', 'βbβ', 'γcγ']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'aαaaα', 'failed at #14') + call g:assert.equals(getline(2), 'aαbaα', 'failed at #14') + call g:assert.equals(getline(3), 'aαcaα', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcaα')+1, 0], 'failed at #14') + + " #15 + call append(0, ['ααα', 'βββ', 'γγγ']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'aααaα', 'failed at #15') + call g:assert.equals(getline(2), 'aαβaα', 'failed at #15') + call g:assert.equals(getline(3), 'aαγaα', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαγaα')+1, 0], 'failed at #15') + + " #16 + call append(0, ['αaαα', 'βbββ', 'γcγγ']) + execute "normal gg\3l2jsra" + call g:assert.equals(getline(1), 'aαaαaα', 'failed at #16') + call g:assert.equals(getline(2), 'aαbβaα', 'failed at #16') + call g:assert.equals(getline(3), 'aαcγaα', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 1, strlen('aα')+1, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 3, strlen('aαcγaα')+1, 0], 'failed at #16') + + unlet g:operator#sandwich#recipes + + " #17 + call append(0, ['a“a', 'b“b', 'c“c']) + execute "normal gg\2l2jsr(" + call g:assert.equals(getline(1), '(“)', 'failed at #17') + call g:assert.equals(getline(2), '(“)', 'failed at #17') + call g:assert.equals(getline(3), '(“)', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 3, strlen('(“)')+1, 0], 'failed at #17') + + " #18 + call append(0, ['aa“a', 'bb“b', 'cc“c']) + execute "normal gg\3l2jsr(" + call g:assert.equals(getline(1), '(a“)', 'failed at #18') + call g:assert.equals(getline(2), '(b“)', 'failed at #18') + call g:assert.equals(getline(3), '(c“)', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 3, strlen('(c“)')+1, 0], 'failed at #18') + + " #19 + call append(0, ['α“α', 'β“β', 'γ“γ']) + execute "normal gg\2l2jsr(" + call g:assert.equals(getline(1), '(“)', 'failed at #19') + call g:assert.equals(getline(2), '(“)', 'failed at #19') + call g:assert.equals(getline(3), '(“)', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 3, strlen('(“)')+1, 0], 'failed at #19') + + " #20 + call append(0, ['αa“α', 'βb“β', 'γc“γ']) + execute "normal gg\3l2jsr(" + call g:assert.equals(getline(1), '(a“)', 'failed at #20') + call g:assert.equals(getline(2), '(b“)', 'failed at #20') + call g:assert.equals(getline(3), '(c“)', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 3, strlen('(c“)')+1, 0], 'failed at #20') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['“', '“'], 'input': ['a']} + \ ] + + " #21 + call append(0, ['aaa', 'bbb', 'ccc']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), '“a“', 'failed at #21') + call g:assert.equals(getline(2), '“b“', 'failed at #21') + call g:assert.equals(getline(3), '“c“', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c“')+1, 0], 'failed at #21') + + " #22 + call append(0, ['a“a', 'b“b', 'c“c']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), '“““', 'failed at #22') + call g:assert.equals(getline(2), '“““', 'failed at #22') + call g:assert.equals(getline(3), '“““', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 3, strlen('“““')+1, 0], 'failed at #22') + + " #23 + call append(0, ['aa“a', 'bb“b', 'cc“c']) + execute "normal gg\3l2jsra" + call g:assert.equals(getline(1), '“a““', 'failed at #23') + call g:assert.equals(getline(2), '“b““', 'failed at #23') + call g:assert.equals(getline(3), '“c““', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c““')+1, 0], 'failed at #23') + + " #24 + call append(0, ['αaα', 'βbβ', 'γcγ']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), '“a“', 'failed at #24') + call g:assert.equals(getline(2), '“b“', 'failed at #24') + call g:assert.equals(getline(3), '“c“', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c“')+1, 0], 'failed at #24') + + " #25 + call append(0, ['α“α', 'β“β', 'γ“γ']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), '“““', 'failed at #25') + call g:assert.equals(getline(2), '“““', 'failed at #25') + call g:assert.equals(getline(3), '“““', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 3, strlen('“““')+1, 0], 'failed at #25') + + " #26 + call append(0, ['αa“α', 'βb“β', 'γc“γ']) + execute "normal gg\3l2jsra" + call g:assert.equals(getline(1), '“a““', 'failed at #26') + call g:assert.equals(getline(2), '“b““', 'failed at #26') + call g:assert.equals(getline(3), '“c““', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 1, strlen('“')+1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 3, strlen('“c““')+1, 0], 'failed at #26') + + let g:operator#sandwich#recipes = [ + \ {'buns': ['a“', 'a“'], 'input': ['a']} + \ ] + + " #27 + call append(0, ['aaa', 'bbb', 'ccc']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'a“aa“', 'failed at #27') + call g:assert.equals(getline(2), 'a“ba“', 'failed at #27') + call g:assert.equals(getline(3), 'a“ca“', 'failed at #27') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #27') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #27') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“ca“')+1, 0], 'failed at #27') + + " #28 + call append(0, ['a“a', 'b“b', 'c“c']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'a““a“', 'failed at #28') + call g:assert.equals(getline(2), 'a““a“', 'failed at #28') + call g:assert.equals(getline(3), 'a““a“', 'failed at #28') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #28') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #28') + call g:assert.equals(getpos("']"), [0, 3, strlen('a““a“')+1, 0], 'failed at #28') + + " #29 + call append(0, ['aa“a', 'bb“b', 'cc“c']) + execute "normal gg\3l2jsra" + call g:assert.equals(getline(1), 'a“a“a“', 'failed at #29') + call g:assert.equals(getline(2), 'a“b“a“', 'failed at #29') + call g:assert.equals(getline(3), 'a“c“a“', 'failed at #29') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #29') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #29') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“c“a“')+1, 0], 'failed at #29') + + " #30 + call append(0, ['αaα', 'βbβ', 'γcγ']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'a“aa“', 'failed at #30') + call g:assert.equals(getline(2), 'a“ba“', 'failed at #30') + call g:assert.equals(getline(3), 'a“ca“', 'failed at #30') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #30') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #30') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“ca“')+1, 0], 'failed at #30') + + " #31 + call append(0, ['α“α', 'β“β', 'γ“γ']) + execute "normal gg\2l2jsra" + call g:assert.equals(getline(1), 'a““a“', 'failed at #31') + call g:assert.equals(getline(2), 'a““a“', 'failed at #31') + call g:assert.equals(getline(3), 'a““a“', 'failed at #31') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #31') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #31') + call g:assert.equals(getpos("']"), [0, 3, strlen('a““a“')+1, 0], 'failed at #31') + + " #32 + call append(0, ['αa“α', 'βb“β', 'γc“γ']) + execute "normal gg\3l2jsra" + call g:assert.equals(getline(1), 'a“a“a“', 'failed at #32') + call g:assert.equals(getline(2), 'a“b“a“', 'failed at #32') + call g:assert.equals(getline(3), 'a“c“a“', 'failed at #32') + call g:assert.equals(getpos('.'), [0, 1, strlen('a“')+1, 0], 'failed at #32') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #32') + call g:assert.equals(getpos("']"), [0, 3, strlen('a“c“a“')+1, 0], 'failed at #32') +endfunction +"}}} +function! s:suite.blockwise_x_option_cursor() abort "{{{ + """"" cursor + """ default + " #1 + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg\2j8l2sr[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #1') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #1') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #1') + + " #2 + execute "normal \2j4lsr[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #2') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #2') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #2') + + %delete + + """ inner_head + " #3 + call operator#sandwich#set('replace', 'block', 'cursor', 'inner_head') + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg\2j8l2sr[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #3') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #3') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #3') + + " #4 + execute "normal \2j4lsr[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #4') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #4') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #4') + + %delete + + """ keep + " #5 + call operator#sandwich#set('replace', 'block', 'cursor', 'keep') + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg\2j8l2sr[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #5') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #5') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #5') + + " #6 + execute "normal 2h\2k4hsr[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #6') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #6') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #6') + + %delete + + """ inner_tail + " #7 + call operator#sandwich#set('replace', 'block', 'cursor', 'inner_tail') + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg\2j8l2sr[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #7') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #7') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 7, 0], 'failed at #7') + + " #8 + execute "normal gg2l\2j4lsr[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #8') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #8') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 6, 0], 'failed at #8') + + %delete + + """ head + " #9 + call operator#sandwich#set('replace', 'block', 'cursor', 'head') + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg\2j8l2sr[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #9') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #9') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #9') + + " #10 + execute "normal 2l\2j4lsr[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #10') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #10') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 1, 3, 0], 'failed at #10') + + %delete + + """ tail + " #11 + call operator#sandwich#set('replace', 'block', 'cursor', 'tail') + call append(0, ['(((foo)))', '(((bar)))', '(((baz)))']) + execute "normal gg\2j8l2sr[]" + call g:assert.equals(getline(1), '[[(foo)]]', 'failed at #11') + call g:assert.equals(getline(2), '[[(bar)]]', 'failed at #11') + call g:assert.equals(getline(3), '[[(baz)]]', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #11') + + " #12 + execute "normal 6h2k\2j4lsr[" + call g:assert.equals(getline(1), '[[[foo]]]', 'failed at #12') + call g:assert.equals(getline(2), '[[[bar]]]', 'failed at #12') + call g:assert.equals(getline(3), '[[[baz]]]', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 7, 0], 'failed at #12') + + %delete + + """"" recipe option + " #13 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'cursor': 'inner_head', 'input':['1']}] + call operator#sandwich#set('replace', 'block', 'cursor', 'inner_tail') + call setline('.', '[foo]') + execute "normal ^\4lsr1" + call g:assert.equals(getline('.'), '(foo)', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #13') +endfunction +"}}} +function! s:suite.blockwise_x_option_noremap() abort "{{{ + """"" noremap + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #1') + call g:assert.equals(getline(2), '"bar"', 'failed at #1') + call g:assert.equals(getline(3), '"baz"', 'failed at #1') + + %delete + + " #2 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '(foo)', 'failed at #2') + call g:assert.equals(getline(2), '(bar)', 'failed at #2') + call g:assert.equals(getline(3), '(baz)', 'failed at #2') + + %delete + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + + " #3 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '{foo}', 'failed at #3') + call g:assert.equals(getline(2), '{bar}', 'failed at #3') + call g:assert.equals(getline(3), '{baz}', 'failed at #3') + + %delete + + " #4 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #4') + call g:assert.equals(getline(2), '"bar"', 'failed at #4') + call g:assert.equals(getline(3), '"baz"', 'failed at #4') + + + """ off + %delete + call operator#sandwich#set('replace', 'block', 'noremap', 0) + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '{foo}', 'failed at #5') + call g:assert.equals(getline(2), '{bar}', 'failed at #5') + call g:assert.equals(getline(3), '{baz}', 'failed at #5') + + %delete + + " #6 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #6') + call g:assert.equals(getline(2), '"bar"', 'failed at #6') + call g:assert.equals(getline(3), '"baz"', 'failed at #6') + + let g:operator#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + + " #7 + call append(0, ['{foo}', '{bar}', '{baz}']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #7') + call g:assert.equals(getline(2), '"bar"', 'failed at #7') + call g:assert.equals(getline(3), '"baz"', 'failed at #7') + + %delete + + " #8 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '(foo)', 'failed at #8') + call g:assert.equals(getline(2), '(bar)', 'failed at #8') + call g:assert.equals(getline(3), '(baz)', 'failed at #8') +endfunction +"}}} +function! s:suite.blockwise_x_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal gg\2j10lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #1') + call g:assert.equals(getline(2), '"bar"', 'failed at #1') + call g:assert.equals(getline(3), '"baz"', 'failed at #1') + + %delete + + " #2 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal gg\2j8lsr\"" + call g:assert.equals(getline(1), '"88foo88"', 'failed at #2') + call g:assert.equals(getline(2), '"88bar88"', 'failed at #2') + call g:assert.equals(getline(3), '"88baz88"', 'failed at #2') + + %delete + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + + " #3 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal gg\2j10lsr\"" + call g:assert.equals(getline(1), '\d\+foo\d\+', 'failed at #3') + call g:assert.equals(getline(2), '\d\+bar\d\+', 'failed at #3') + call g:assert.equals(getline(3), '\d\+baz\d\+', 'failed at #3') + + %delete + + " #4 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal gg\2j8lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #4') + call g:assert.equals(getline(2), '"bar"', 'failed at #4') + call g:assert.equals(getline(3), '"baz"', 'failed at #4') + + """ on + %delete + call operator#sandwich#set('replace', 'block', 'regex', 1) + + " #5 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal gg\2j10lsr\"" + call g:assert.equals(getline(1), '\d\+foo\d\+', 'failed at #5') + call g:assert.equals(getline(2), '\d\+bar\d\+', 'failed at #5') + call g:assert.equals(getline(3), '\d\+baz\d\+', 'failed at #5') + + %delete + + " #6 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal gg\2j8lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #6') + call g:assert.equals(getline(2), '"bar"', 'failed at #6') + call g:assert.equals(getline(3), '"baz"', 'failed at #6') + + %delete + let g:operator#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + + " #7 + call append(0, ['\d\+foo\d\+', '\d\+bar\d\+', '\d\+baz\d\+']) + execute "normal gg\2j10lsr\"" + call g:assert.equals(getline(1), '"foo"', 'failed at #7') + call g:assert.equals(getline(2), '"bar"', 'failed at #7') + call g:assert.equals(getline(3), '"baz"', 'failed at #7') + + %delete + + " #8 + call append(0, ['888foo888', '888bar888', '888baz888']) + execute "normal gg\2j8lsr\"" + call g:assert.equals(getline(1), '"88foo88"', 'failed at #8') + call g:assert.equals(getline(2), '"88bar88"', 'failed at #8') + call g:assert.equals(getline(3), '"88baz88"', 'failed at #8') +endfunction +"}}} +function! s:suite.blockwise_x_option_skip_space() abort "{{{ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + + """ 1 + " #1 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0\2j4lsr(" + call g:assert.equals(getline(1), '(foo)', 'failed at #1') + call g:assert.equals(getline(2), '(bar)', 'failed at #1') + call g:assert.equals(getline(3), '(baz)', 'failed at #1') + + %delete + + " #2 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j5lsr(" + call g:assert.equals(getline(1), ' (foo)', 'failed at #2') + call g:assert.equals(getline(2), ' (bar)', 'failed at #2') + call g:assert.equals(getline(3), ' (baz)', 'failed at #2') + + %delete + + " #3 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0\2j5lsr(" + call g:assert.equals(getline(1), '(foo) ', 'failed at #3') + call g:assert.equals(getline(2), '(bar) ', 'failed at #3') + call g:assert.equals(getline(3), '(baz) ', 'failed at #3') + + %delete + + " #4 + " do not skip! + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0\2j6lsr(" + call g:assert.equals(getline(1), '("foo")', 'failed at #4') + call g:assert.equals(getline(2), '("bar")', 'failed at #4') + call g:assert.equals(getline(3), '("baz")', 'failed at #4') + + %delete + + " #5 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j6lsr(" + call g:assert.equals(getline(1), ' "foo"', 'failed at #5') + call g:assert.equals(getline(2), ' "bar"', 'failed at #5') + call g:assert.equals(getline(3), ' "baz"', 'failed at #5') + + %delete + + """ 2 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'block', 'skip_space', 2) + + " #6 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0\2j4lsr(" + call g:assert.equals(getline(1), '(foo)', 'failed at #6') + call g:assert.equals(getline(2), '(bar)', 'failed at #6') + call g:assert.equals(getline(3), '(baz)', 'failed at #6') + + %delete + + " #7 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j5lsr(" + call g:assert.equals(getline(1), ' (foo)', 'failed at #7') + call g:assert.equals(getline(2), ' (bar)', 'failed at #7') + call g:assert.equals(getline(3), ' (baz)', 'failed at #7') + + %delete + + " #8 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0\2j5lsr(" + call g:assert.equals(getline(1), '(foo) ', 'failed at #8') + call g:assert.equals(getline(2), '(bar) ', 'failed at #8') + call g:assert.equals(getline(3), '(baz) ', 'failed at #8') + + %delete + + " #9 + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0\2j6lsr(" + call g:assert.equals(getline(1), ' (foo) ', 'failed at #9') + call g:assert.equals(getline(2), ' (bar) ', 'failed at #9') + call g:assert.equals(getline(3), ' (baz) ', 'failed at #9') + + %delete + + " #10 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 0}, {'buns': ['(', ')']}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j6lsr(" + call g:assert.equals(getline(1), ' "foo"', 'failed at #10') + call g:assert.equals(getline(2), ' "bar"', 'failed at #10') + call g:assert.equals(getline(3), ' "baz"', 'failed at #10') + + %delete + + """ 0 + let g:operator#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + call operator#sandwich#set('replace', 'block', 'skip_space', 0) + + " #11 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg0\2j4lsr(" + call g:assert.equals(getline(1), '(foo)', 'failed at #11') + call g:assert.equals(getline(2), '(bar)', 'failed at #11') + call g:assert.equals(getline(3), '(baz)', 'failed at #11') + + %delete + + " #12 + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j5lsr(" + call g:assert.equals(getline(1), ' "foo"', 'failed at #12') + call g:assert.equals(getline(2), ' "bar"', 'failed at #12') + call g:assert.equals(getline(3), ' "baz"', 'failed at #12') + + %delete + + " #13 + call append(0, ['"foo" ', '"bar" ', '"baz" ']) + execute "normal gg0\2j5lsr(" + call g:assert.equals(getline(1), '"foo" ', 'failed at #13') + call g:assert.equals(getline(2), '"bar" ', 'failed at #13') + call g:assert.equals(getline(3), '"baz" ', 'failed at #13') + + %delete + + " #14 + " do not skip! + call append(0, [' "foo" ', ' "bar" ', ' "baz" ']) + execute "normal gg0\2j6lsr(" + call g:assert.equals(getline(1), '("foo")', 'failed at #14') + call g:assert.equals(getline(2), '("bar")', 'failed at #14') + call g:assert.equals(getline(3), '("baz")', 'failed at #14') + + " #15 + let g:operator#sandwich#recipes = [{'buns': ['"', '"'], 'skip_space': 1}, {'buns': ['(', ')']}] + call append(0, [' "foo"', ' "bar"', ' "baz"']) + execute "normal gg0\2j6lsr(" + call g:assert.equals(getline(1), ' (foo)', 'failed at #2') + call g:assert.equals(getline(2), ' (bar)', 'failed at #2') + call g:assert.equals(getline(3), ' (baz)', 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_x_option_skip_char() abort "{{{ + """ off + " #1 + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal gg\2j8lsr\"" + call g:assert.equals(getline(1), 'aa(foo)bb', 'failed at #1') + call g:assert.equals(getline(2), 'aa(bar)bb', 'failed at #1') + call g:assert.equals(getline(3), 'aa(baz)bb', 'failed at #1') + + %delete + + " #2 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 1}] + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal gg\2j8lsr\"" + call g:assert.equals(getline(1), 'aa"foo"bb', 'failed at #2') + call g:assert.equals(getline(2), 'aa"bar"bb', 'failed at #2') + call g:assert.equals(getline(3), 'aa"baz"bb', 'failed at #2') + unlet! g:operator#sandwich#recipes + + %delete + + """ on + call operator#sandwich#set('replace', 'block', 'skip_char', 1) + " #3 + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal gg\2j8lsr\"" + call g:assert.equals(getline(1), 'aa"foo"bb', 'failed at #3') + call g:assert.equals(getline(2), 'aa"bar"bb', 'failed at #3') + call g:assert.equals(getline(3), 'aa"baz"bb', 'failed at #3') + + %delete + + " #4 + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'skip_char': 0}] + call append(0, ['aa(foo)bb', 'aa(bar)bb', 'aa(baz)bb']) + execute "normal gg\2j8lsr\"" + call g:assert.equals(getline(1), 'aa(foo)bb', 'failed at #4') + call g:assert.equals(getline(2), 'aa(bar)bb', 'failed at #4') + call g:assert.equals(getline(3), 'aa(baz)bb', 'failed at #4') + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.blockwise_x_option_command() abort "{{{ + call operator#sandwich#set('replace', 'block', 'command', ["normal! `[d\`]"]) + + " #1 + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '', 'failed at #1') + call g:assert.equals(getline(2), '', 'failed at #1') + call g:assert.equals(getline(3), '', 'failed at #1') + + " #2 + call operator#sandwich#set('replace', 'block', 'command', []) + let g:operator#sandwich#recipes = [{'buns': ['(', ')'], 'command': ['normal! `[d`]']}] + call append(0, ['(foo)', '(bar)', '(baz)']) + execute "normal gg\2j4lsr\"" + call g:assert.equals(getline(1), '', 'failed at #2') + call g:assert.equals(getline(2), '', 'failed at #2') + call g:assert.equals(getline(3), '', 'failed at #2') +endfunction +"}}} +function! s:suite.blockwise_x_option_query_once() abort "{{{ + let g:operator#sandwich#recipes = [ + \ {'buns': ['(', ')'], 'query_once': 0, 'input':['0']}, + \ {'buns': ['(', ')'], 'query_once': 1, 'input':['1']}, + \ ] + + """"" query_once + """ off + " #1 + call append(0, ['"""foo"""', '"""bar"""', '"""baz"""']) + execute "normal gg\2j8l3sr([{" + call g:assert.equals(getline(1), '([{foo}])', 'failed at #1') + call g:assert.equals(getline(2), '([{bar}])', 'failed at #1') + call g:assert.equals(getline(3), '([{baz}])', 'failed at #1') + + %delete + + " #2 + call append(0, ['"""foo"""', '"""bar"""', '"""baz"""']) + execute "normal gg\2j8l3sr1" + call g:assert.equals(getline(1), '(((foo)))', 'failed at #2') + call g:assert.equals(getline(2), '(((bar)))', 'failed at #2') + call g:assert.equals(getline(3), '(((baz)))', 'failed at #2') + + %delete + + """ on + call operator#sandwich#set('replace', 'block', 'query_once', 1) + + " #3 + call append(0, ['"""foo"""', '"""bar"""', '"""baz"""']) + execute "normal gg\2j8l3sr(" + call g:assert.equals(getline(1), '(((foo)))', 'failed at #3') + call g:assert.equals(getline(2), '(((bar)))', 'failed at #3') + call g:assert.equals(getline(3), '(((baz)))', 'failed at #3') + + %delete + + " #4 + call append(0, ['"""foo"""', '"""bar"""', '"""baz"""']) + execute "normal gg\2j8l3sr0[{" + call g:assert.equals(getline(1), '([{foo}])', 'failed at #4') + call g:assert.equals(getline(2), '([{bar}])', 'failed at #4') + call g:assert.equals(getline(3), '([{baz}])', 'failed at #4') +endfunction +"}}} +function! s:suite.blockwise_x_option_expr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprCancel()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprCancel()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg\2j4lsra" + call g:assert.equals(getline('.'), '1+1foo1+2', 'failed at #1') + + %delete + + " #2 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg\2j4lsr1" + call g:assert.equals(getline(1), '2foo3', 'failed at #2') + call g:assert.equals(getline(2), '2bar3', 'failed at #2') + call g:assert.equals(getline(3), '2baz3', 'failed at #2') + + %delete + + """ 1 + " #3 + call operator#sandwich#set('replace', 'block', 'expr', 1) + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg\2j4lsra" + call g:assert.equals(getline('.'), '2foo3', 'failed at #3') + + %delete + + " #4 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg\2j4lsrb" + call g:assert.equals(getline(1), '"foo"', 'failed at #4') + call g:assert.equals(getline(2), '"bar"', 'failed at #4') + call g:assert.equals(getline(3), '"baz"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + %delete + + " #5 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg\2j4lsrc" + call g:assert.equals(getline(1), '"foo"', 'failed at #5') + call g:assert.equals(getline(2), '"bar"', 'failed at #5') + call g:assert.equals(getline(3), '"baz"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + %delete + + " #6 + call append(0, ['"''foo''"', '"''bar''"', '"''baz''"']) + execute "normal gg\2j7l2srab" + call g:assert.equals(getline(1), '2''foo''3', 'failed at #6') + call g:assert.equals(getline(2), '2''bar''3', 'failed at #6') + call g:assert.equals(getline(3), '2''baz''3', 'failed at #6') + call g:assert.equals(exists(s:object), 0, 'failed at #6') + + %delete + + " #7 + call append(0, ['"''foo''"', '"''bar''"', '"''baz''"']) + execute "normal gg\2j7l2srac" + call g:assert.equals(getline(1), '2''foo''3', 'failed at #7') + call g:assert.equals(getline(2), '2''bar''3', 'failed at #7') + call g:assert.equals(getline(3), '2''baz''3', 'failed at #7') + call g:assert.equals(exists(s:object), 0, 'failed at #7') + + %delete + + " #8 + call append(0, ['"''foo''"', '"''bar''"', '"''baz''"']) + execute "normal gg\2j7l2srba" + call g:assert.equals(getline(1), '"''foo''"', 'failed at #8') + call g:assert.equals(getline(2), '"''bar''"', 'failed at #8') + call g:assert.equals(getline(3), '"''baz''"', 'failed at #8') + call g:assert.equals(exists(s:object), 0, 'failed at #8') + + %delete + + " #9 + call append(0, ['"''foo''"', '"''bar''"', '"''baz''"']) + execute "normal gg\2j7l2srca" + call g:assert.equals(getline(1), '"''foo''"', 'failed at #9') + call g:assert.equals(getline(2), '"''bar''"', 'failed at #9') + call g:assert.equals(getline(3), '"''baz''"', 'failed at #9') + call g:assert.equals(exists(s:object), 0, 'failed at #9') + + %delete + + " #10 + call append(0, ['"foo"', '"bar"', '"baz"']) + execute "normal gg\2j4lsr0" + call g:assert.equals(getline(1), '1+1foo1+2', 'failed at #10') + call g:assert.equals(getline(2), '1+1bar1+2', 'failed at #10') + call g:assert.equals(getline(3), '1+1baz1+2', 'failed at #10') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.blockwise_x_option_listexpr() abort "{{{ + """"" expr + let g:operator#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprBuns(1)', 'input': ['b']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 0, 'input': ['0']}, + \ {'buns': 'SandwichListexprBuns(0)', 'listexpr': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :execute "normal gg\a\"sra" + call g:assert.equals(getline('.'), '"bar"', 'failed at #1') + call g:assert.equals(exists(s:object), 0, 'failed at #1') + + " #2 + call setline('.', '"bar"') + execute "normal gg\a\"sr1" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #2') + + """ 1 + " #3 + call operator#sandwich#set('replace', 'block', 'listexpr', 1) + call setline('.', '"bar"') + execute "normal gg\a\"sra" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #3') + + " #4 + call setline('.', '"bar"') + execute "normal gg\a\"srb" + call g:assert.equals(getline('.'), '"bar"', 'failed at #4') + call g:assert.equals(exists(s:object), 0, 'failed at #4') + + " #5 + call setline('.', '"bar"') + Throws /^Vim(echoerr):operator-sandwich: Incorrect buns./ :execute "normal gg\a\"sr0" + call g:assert.equals(getline('.'), '"bar"', 'failed at #5') + call g:assert.equals(exists(s:object), 0, 'failed at #5') + + " #6 + call setline('.', '"bar"') + execute "normal gg\a\"sr1" + call g:assert.equals(getline('.'), 'foobarbaz', 'failed at #6') + + """ 2 + " This case cannot be tested since this option makes difference only in + " dot-repeat. +endfunction +"}}} +function! s:suite.blockwise_x_option_autoindent() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n[\n", "\n]\n}"], 'input': ['a']}, + \ {'buns': ["{\n[\n", "\n]\n}"], 'autoindent': 0, 'input': ['0']}, + \ ] + + """ -1 + call operator#sandwich#set('replace', 'block', 'autoindent', -1) + + " #1 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #1') + call g:assert.equals(getline(2), '[', 'failed at #1') + call g:assert.equals(getline(3), 'foo', 'failed at #1') + call g:assert.equals(getline(4), ']', 'failed at #1') + call g:assert.equals(getline(5), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #1') + call g:assert.equals(&l:autoindent, 0, 'failed at #1') + call g:assert.equals(&l:smartindent, 0, 'failed at #1') + call g:assert.equals(&l:cindent, 0, 'failed at #1') + call g:assert.equals(&l:indentexpr, '', 'failed at #1') + + %delete + + " #2 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #2') + call g:assert.equals(getline(2), ' [', 'failed at #2') + call g:assert.equals(getline(3), ' foo', 'failed at #2') + call g:assert.equals(getline(4), ' ]', 'failed at #2') + call g:assert.equals(getline(5), ' }', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #2') + call g:assert.equals(&l:autoindent, 1, 'failed at #2') + call g:assert.equals(&l:smartindent, 0, 'failed at #2') + call g:assert.equals(&l:cindent, 0, 'failed at #2') + call g:assert.equals(&l:indentexpr, '', 'failed at #2') + + %delete + + " #3 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #3') + call g:assert.equals(getline(2), ' [', 'failed at #3') + call g:assert.equals(getline(3), ' foo', 'failed at #3') + call g:assert.equals(getline(4), ' ]', 'failed at #3') + call g:assert.equals(getline(5), ' }', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #3') + call g:assert.equals(&l:autoindent, 1, 'failed at #3') + call g:assert.equals(&l:smartindent, 1, 'failed at #3') + call g:assert.equals(&l:cindent, 0, 'failed at #3') + call g:assert.equals(&l:indentexpr, '', 'failed at #3') + + %delete + + " #4 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), ' [', 'failed at #4') + call g:assert.equals(getline(3), ' foo', 'failed at #4') + " call g:assert.equals(getline(4), ' ]', 'failed at #4') + call g:assert.equals(getline(5), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #4') + call g:assert.equals(&l:autoindent, 1, 'failed at #4') + call g:assert.equals(&l:smartindent, 1, 'failed at #4') + call g:assert.equals(&l:cindent, 1, 'failed at #4') + call g:assert.equals(&l:indentexpr, '', 'failed at #4') + + %delete + + " #5 + " setlocal indentexpr=TestIndent() + " call setline('.', ' "foo"') + " execute "normal ^\2i\"sra" + " call g:assert.equals(getline(1), ' {', 'failed at #5') + " call g:assert.equals(getline(2), ' [', 'failed at #5') + " call g:assert.equals(getline(3), ' foo', 'failed at #5') + " call g:assert.equals(getline(4), ' ]', 'failed at #5') + " call g:assert.equals(getline(5), ' }', 'failed at #5') + " call g:assert.equals(getpos('.'), [0, 3, 17, 0], 'failed at #5') + " call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + " call g:assert.equals(getpos("']"), [0, 5, 34, 0], 'failed at #5') + " call g:assert.equals(&l:autoindent, 1, 'failed at #5') + " call g:assert.equals(&l:smartindent, 1, 'failed at #5') + " call g:assert.equals(&l:cindent, 1, 'failed at #5') + " call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #5') + + %delete + + """ 0 + call operator#sandwich#set('replace', 'block', 'autoindent', 0) + + " #6 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), '[', 'failed at #6') + call g:assert.equals(getline(3), 'foo', 'failed at #6') + call g:assert.equals(getline(4), ']', 'failed at #6') + call g:assert.equals(getline(5), '}', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #6') + call g:assert.equals(&l:autoindent, 0, 'failed at #6') + call g:assert.equals(&l:smartindent, 0, 'failed at #6') + call g:assert.equals(&l:cindent, 0, 'failed at #6') + call g:assert.equals(&l:indentexpr, '', 'failed at #6') + + %delete + + " #7 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), '[', 'failed at #7') + call g:assert.equals(getline(3), 'foo', 'failed at #7') + call g:assert.equals(getline(4), ']', 'failed at #7') + call g:assert.equals(getline(5), '}', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #7') + call g:assert.equals(&l:autoindent, 1, 'failed at #7') + call g:assert.equals(&l:smartindent, 0, 'failed at #7') + call g:assert.equals(&l:cindent, 0, 'failed at #7') + call g:assert.equals(&l:indentexpr, '', 'failed at #7') + + %delete + + " #8 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), '[', 'failed at #8') + call g:assert.equals(getline(3), 'foo', 'failed at #8') + call g:assert.equals(getline(4), ']', 'failed at #8') + call g:assert.equals(getline(5), '}', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #8') + call g:assert.equals(&l:autoindent, 1, 'failed at #8') + call g:assert.equals(&l:smartindent, 1, 'failed at #8') + call g:assert.equals(&l:cindent, 0, 'failed at #8') + call g:assert.equals(&l:indentexpr, '', 'failed at #8') + + %delete + + " #9 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #9') + call g:assert.equals(getline(2), '[', 'failed at #9') + call g:assert.equals(getline(3), 'foo', 'failed at #9') + call g:assert.equals(getline(4), ']', 'failed at #9') + call g:assert.equals(getline(5), '}', 'failed at #9') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #9') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #9') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #9') + call g:assert.equals(&l:autoindent, 1, 'failed at #9') + call g:assert.equals(&l:smartindent, 1, 'failed at #9') + call g:assert.equals(&l:cindent, 1, 'failed at #9') + call g:assert.equals(&l:indentexpr, '', 'failed at #9') + + %delete + + " #10 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #10') + call g:assert.equals(getline(2), '[', 'failed at #10') + call g:assert.equals(getline(3), 'foo', 'failed at #10') + call g:assert.equals(getline(4), ']', 'failed at #10') + call g:assert.equals(getline(5), '}', 'failed at #10') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #10') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #10') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #10') + call g:assert.equals(&l:autoindent, 1, 'failed at #10') + call g:assert.equals(&l:smartindent, 1, 'failed at #10') + call g:assert.equals(&l:cindent, 1, 'failed at #10') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #10') + + %delete + + """ 1 + call operator#sandwich#set('replace', 'block', 'autoindent', 1) + + " #11 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #11') + call g:assert.equals(getline(2), ' [', 'failed at #11') + call g:assert.equals(getline(3), ' foo', 'failed at #11') + call g:assert.equals(getline(4), ' ]', 'failed at #11') + call g:assert.equals(getline(5), ' }', 'failed at #11') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #11') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #11') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #11') + call g:assert.equals(&l:autoindent, 0, 'failed at #11') + call g:assert.equals(&l:smartindent, 0, 'failed at #11') + call g:assert.equals(&l:cindent, 0, 'failed at #11') + call g:assert.equals(&l:indentexpr, '', 'failed at #11') + + %delete + + " #12 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #12') + call g:assert.equals(getline(2), ' [', 'failed at #12') + call g:assert.equals(getline(3), ' foo', 'failed at #12') + call g:assert.equals(getline(4), ' ]', 'failed at #12') + call g:assert.equals(getline(5), ' }', 'failed at #12') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #12') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #12') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #12') + call g:assert.equals(&l:autoindent, 1, 'failed at #12') + call g:assert.equals(&l:smartindent, 0, 'failed at #12') + call g:assert.equals(&l:cindent, 0, 'failed at #12') + call g:assert.equals(&l:indentexpr, '', 'failed at #12') + + %delete + + " #13 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #13') + call g:assert.equals(getline(2), ' [', 'failed at #13') + call g:assert.equals(getline(3), ' foo', 'failed at #13') + call g:assert.equals(getline(4), ' ]', 'failed at #13') + call g:assert.equals(getline(5), ' }', 'failed at #13') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #13') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #13') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #13') + call g:assert.equals(&l:autoindent, 1, 'failed at #13') + call g:assert.equals(&l:smartindent, 1, 'failed at #13') + call g:assert.equals(&l:cindent, 0, 'failed at #13') + call g:assert.equals(&l:indentexpr, '', 'failed at #13') + + %delete + + " #14 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #14') + call g:assert.equals(getline(2), ' [', 'failed at #14') + call g:assert.equals(getline(3), ' foo', 'failed at #14') + call g:assert.equals(getline(4), ' ]', 'failed at #14') + call g:assert.equals(getline(5), ' }', 'failed at #14') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #14') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #14') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #14') + call g:assert.equals(&l:autoindent, 1, 'failed at #14') + call g:assert.equals(&l:smartindent, 1, 'failed at #14') + call g:assert.equals(&l:cindent, 1, 'failed at #14') + call g:assert.equals(&l:indentexpr, '', 'failed at #14') + + %delete + + " #15 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #15') + call g:assert.equals(getline(2), ' [', 'failed at #15') + call g:assert.equals(getline(3), ' foo', 'failed at #15') + call g:assert.equals(getline(4), ' ]', 'failed at #15') + call g:assert.equals(getline(5), ' }', 'failed at #15') + call g:assert.equals(getpos('.'), [0, 3, 5, 0], 'failed at #15') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #15') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #15') + call g:assert.equals(&l:autoindent, 1, 'failed at #15') + call g:assert.equals(&l:smartindent, 1, 'failed at #15') + call g:assert.equals(&l:cindent, 1, 'failed at #15') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #15') + + %delete + + """ 2 + call operator#sandwich#set('replace', 'block', 'autoindent', 2) + + " #16 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #16') + call g:assert.equals(getline(2), ' [', 'failed at #16') + call g:assert.equals(getline(3), ' foo', 'failed at #16') + call g:assert.equals(getline(4), ' ]', 'failed at #16') + call g:assert.equals(getline(5), ' }', 'failed at #16') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #16') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #16') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #16') + call g:assert.equals(&l:autoindent, 0, 'failed at #16') + call g:assert.equals(&l:smartindent, 0, 'failed at #16') + call g:assert.equals(&l:cindent, 0, 'failed at #16') + call g:assert.equals(&l:indentexpr, '', 'failed at #16') + + %delete + + " #17 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #17') + call g:assert.equals(getline(2), ' [', 'failed at #17') + call g:assert.equals(getline(3), ' foo', 'failed at #17') + call g:assert.equals(getline(4), ' ]', 'failed at #17') + call g:assert.equals(getline(5), ' }', 'failed at #17') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #17') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #17') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #17') + call g:assert.equals(&l:autoindent, 1, 'failed at #17') + call g:assert.equals(&l:smartindent, 0, 'failed at #17') + call g:assert.equals(&l:cindent, 0, 'failed at #17') + call g:assert.equals(&l:indentexpr, '', 'failed at #17') + + %delete + + " #18 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #18') + call g:assert.equals(getline(2), ' [', 'failed at #18') + call g:assert.equals(getline(3), ' foo', 'failed at #18') + call g:assert.equals(getline(4), ' ]', 'failed at #18') + call g:assert.equals(getline(5), ' }', 'failed at #18') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #18') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #18') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #18') + call g:assert.equals(&l:autoindent, 1, 'failed at #18') + call g:assert.equals(&l:smartindent, 1, 'failed at #18') + call g:assert.equals(&l:cindent, 0, 'failed at #18') + call g:assert.equals(&l:indentexpr, '', 'failed at #18') + + %delete + + " #19 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #19') + call g:assert.equals(getline(2), ' [', 'failed at #19') + call g:assert.equals(getline(3), ' foo', 'failed at #19') + call g:assert.equals(getline(4), ' ]', 'failed at #19') + call g:assert.equals(getline(5), ' }', 'failed at #19') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #19') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #19') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #19') + call g:assert.equals(&l:autoindent, 1, 'failed at #19') + call g:assert.equals(&l:smartindent, 1, 'failed at #19') + call g:assert.equals(&l:cindent, 1, 'failed at #19') + call g:assert.equals(&l:indentexpr, '', 'failed at #19') + + %delete + + " #20 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #20') + call g:assert.equals(getline(2), ' [', 'failed at #20') + call g:assert.equals(getline(3), ' foo', 'failed at #20') + call g:assert.equals(getline(4), ' ]', 'failed at #20') + call g:assert.equals(getline(5), ' }', 'failed at #20') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #20') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #20') + call g:assert.equals(getpos("']"), [0, 5, 6, 0], 'failed at #20') + call g:assert.equals(&l:autoindent, 1, 'failed at #20') + call g:assert.equals(&l:smartindent, 1, 'failed at #20') + call g:assert.equals(&l:cindent, 1, 'failed at #20') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #20') + + %delete + + """ 3 + call operator#sandwich#set('replace', 'block', 'autoindent', 3) + + " #21 + setlocal noautoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), '{', 'failed at #21') + call g:assert.equals(getline(2), ' [', 'failed at #21') + call g:assert.equals(getline(3), ' foo', 'failed at #21') + " call g:assert.equals(getline(4), ' ]', 'failed at #21') + call g:assert.equals(getline(5), '}', 'failed at #21') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #21') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #21') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #21') + call g:assert.equals(&l:autoindent, 0, 'failed at #21') + call g:assert.equals(&l:smartindent, 0, 'failed at #21') + call g:assert.equals(&l:cindent, 0, 'failed at #21') + call g:assert.equals(&l:indentexpr, '', 'failed at #21') + + %delete + + " #22 + setlocal autoindent + setlocal nosmartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), '{', 'failed at #22') + call g:assert.equals(getline(2), ' [', 'failed at #22') + call g:assert.equals(getline(3), ' foo', 'failed at #22') + " call g:assert.equals(getline(4), ' ]', 'failed at #22') + call g:assert.equals(getline(5), '}', 'failed at #22') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #22') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #22') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #22') + call g:assert.equals(&l:autoindent, 1, 'failed at #22') + call g:assert.equals(&l:smartindent, 0, 'failed at #22') + call g:assert.equals(&l:cindent, 0, 'failed at #22') + call g:assert.equals(&l:indentexpr, '', 'failed at #22') + + %delete + + " #23 + setlocal smartindent + setlocal nocindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), '{', 'failed at #23') + call g:assert.equals(getline(2), ' [', 'failed at #23') + call g:assert.equals(getline(3), ' foo', 'failed at #23') + " call g:assert.equals(getline(4), ' ]', 'failed at #23') + call g:assert.equals(getline(5), '}', 'failed at #23') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #23') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #23') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #23') + call g:assert.equals(&l:autoindent, 1, 'failed at #23') + call g:assert.equals(&l:smartindent, 1, 'failed at #23') + call g:assert.equals(&l:cindent, 0, 'failed at #23') + call g:assert.equals(&l:indentexpr, '', 'failed at #23') + + %delete + + " #24 + setlocal cindent + setlocal indentexpr= + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), '{', 'failed at #24') + call g:assert.equals(getline(2), ' [', 'failed at #24') + call g:assert.equals(getline(3), ' foo', 'failed at #24') + " call g:assert.equals(getline(4), ' ]', 'failed at #24') + call g:assert.equals(getline(5), '}', 'failed at #24') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #24') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #24') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #24') + call g:assert.equals(&l:autoindent, 1, 'failed at #24') + call g:assert.equals(&l:smartindent, 1, 'failed at #24') + call g:assert.equals(&l:cindent, 1, 'failed at #24') + call g:assert.equals(&l:indentexpr, '', 'failed at #24') + + %delete + + " #25 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), '{', 'failed at #25') + call g:assert.equals(getline(2), ' [', 'failed at #25') + call g:assert.equals(getline(3), ' foo', 'failed at #25') + " call g:assert.equals(getline(4), ' ]', 'failed at #25') + call g:assert.equals(getline(5), '}', 'failed at #25') + call g:assert.equals(getpos('.'), [0, 3, 9, 0], 'failed at #25') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #25') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #25') + call g:assert.equals(&l:autoindent, 1, 'failed at #25') + call g:assert.equals(&l:smartindent, 1, 'failed at #25') + call g:assert.equals(&l:cindent, 1, 'failed at #25') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #25') + + %delete + + " #26 + setlocal indentexpr=TestIndent() + call setline('.', ' "foo"') + execute "normal ^sr\2i\"0" + call g:assert.equals(getline(1), ' {', 'failed at #26') + call g:assert.equals(getline(2), '[', 'failed at #26') + call g:assert.equals(getline(3), 'foo', 'failed at #26') + call g:assert.equals(getline(4), ']', 'failed at #26') + call g:assert.equals(getline(5), '}', 'failed at #26') + call g:assert.equals(getpos('.'), [0, 3, 1, 0], 'failed at #26') + call g:assert.equals(getpos("'["), [0, 1, 5, 0], 'failed at #26') + call g:assert.equals(getpos("']"), [0, 5, 2, 0], 'failed at #26') + call g:assert.equals(&l:autoindent, 1, 'failed at #26') + call g:assert.equals(&l:smartindent, 1, 'failed at #26') + call g:assert.equals(&l:cindent, 1, 'failed at #26') + call g:assert.equals(&l:indentexpr, 'TestIndent()', 'failed at #26') +endfunction +"}}} +function! s:suite.blockwise_x_option_indentkeys() abort "{{{ + set expandtab + set shiftwidth=4 + set softtabstop=4 + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ["{\n", "\n}"], 'input': ['a']}, + \ {'buns': ["{\n", "\n}"], 'indentkeys': '0{,0},0),:,0#,!^F,e', 'input': ['1']}, + \ ] + + """ cinkeys + call operator#sandwich#set('replace', 'block', 'autoindent', 3) + + " #1 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), '{', 'failed at #1') + call g:assert.equals(getline(2), 'foo', 'failed at #1') + call g:assert.equals(getline(3), '}', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #1') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #1') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #1') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #1') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #1') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', 3) + + " #2 + setlocal cinkeys=0{,0},0),:,0#,!^F,e + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys+', 'O,o') + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), '{', 'failed at #2') + call g:assert.equals(getline(2), ' foo', 'failed at #2') + call g:assert.equals(getline(3), '}', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #2') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #2') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #2') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #2') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #2') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', 3) + + " #3 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys-', 'O,o') + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), '{', 'failed at #3') + call g:assert.equals(getline(2), 'foo', 'failed at #3') + call g:assert.equals(getline(3), '}', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #3') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #3') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #3') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #3') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #3') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', 3) + + " #4 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' "foo"') + execute "normal ^sr\2i\"1" + call g:assert.equals(getline(1), '{', 'failed at #4') + call g:assert.equals(getline(2), 'foo', 'failed at #4') + call g:assert.equals(getline(3), '}', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #4') + call g:assert.equals(getpos("'["), [0, 1, 1, 0], 'failed at #4') + call g:assert.equals(getpos("']"), [0, 3, 2, 0], 'failed at #4') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #4') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #4') + + """ indentkeys + %delete + setlocal indentexpr=TestIndent() + call operator#sandwich#set('replace', 'block', 'autoindent', -1) + + " #5 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys', '0{,0},0),:,0#,!^F,e') + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #5') + call g:assert.equals(getline(2), 'foo', 'failed at #5') + call g:assert.equals(getline(3), ' }', 'failed at #5') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #5') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #5') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #5') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #5') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #5') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', -1) + + " #6 + setlocal cinkeys& + setlocal indentkeys=0{,0},0),:,0#,!^F,e + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys+', 'O,o') + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #6') + call g:assert.equals(getline(2), ' foo', 'failed at #6') + call g:assert.equals(getline(3), ' }', 'failed at #6') + call g:assert.equals(getpos('.'), [0, 2, 5, 0], 'failed at #6') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #6') + call g:assert.equals(getpos("']"), [0, 3, 14, 0], 'failed at #6') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #6') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #6') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', -1) + + " #7 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call operator#sandwich#set('replace', 'block', 'indentkeys-', 'O,o') + call setline('.', ' "foo"') + execute "normal ^\2i\"sra" + call g:assert.equals(getline(1), ' {', 'failed at #7') + call g:assert.equals(getline(2), 'foo', 'failed at #7') + call g:assert.equals(getline(3), ' }', 'failed at #7') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #7') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #7') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #7') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #7') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #7') + + %delete + call operator#sandwich#set_default() + call operator#sandwich#set('replace', 'block', 'autoindent', -1) + + " #8 + setlocal cinkeys& + setlocal indentkeys& + let cinkeys = &l:cinkeys + let indentkeys = &l:indentkeys + call setline('.', ' "foo"') + execute "normal ^sr\2i\"1" + call g:assert.equals(getline(1), ' {', 'failed at #8') + call g:assert.equals(getline(2), 'foo', 'failed at #8') + call g:assert.equals(getline(3), ' }', 'failed at #8') + call g:assert.equals(getpos('.'), [0, 2, 1, 0], 'failed at #8') + call g:assert.equals(getpos("'["), [0, 1, 9, 0], 'failed at #8') + call g:assert.equals(getpos("']"), [0, 3, 6, 0], 'failed at #8') + call g:assert.equals(&l:cinkeys, cinkeys, 'failed at #8') + call g:assert.equals(&l:indentkeys, indentkeys, 'failed at #8') +endfunction +"}}} + +" Function interface +function! s:suite.function_interface() abort "{{{ + nmap ssr :call operator#sandwich#prerequisite('replace', 'n', {'cursor': 'inner_tail'}, [{'buns': ['(', ')']}])g@ + let g:sandwich#recipes = [] + let g:operator#sandwich#recipes = [ + \ {'buns': ['[', ']']}, + \ ] + + " #1 + call setline('.', '(foo)') + normal 0sra([ + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0sra[( + call g:assert.equals(getline('.'), '(foo(', 'failed at #2') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #2') + + " #3 + call setline('.', '(foo)') + normal 0ssra([ + call g:assert.equals(getline('.'), '[foo[', 'failed at #3') + call g:assert.equals(getpos('.'), [0, 1, 4, 0], 'failed at #3') + + " #4 + call setline('.', '[foo]') + normal 0ssra[( + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') + call g:assert.equals(getpos('.'), [0, 1, 1, 0], 'failed at #4') +endfunction +"}}} + +" Undo +function! s:suite.undo() abort "{{{ + " #1 + call setline('.', '(((foo)))') + " set undo point (see :help :undojoin) + let &undolevels = &undolevels + normal 0sr$" + normal! u + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #1') + + " #2 + call setline('.', '(((foo)))') + let &undolevels = &undolevels + normal 02sr$"" + normal! u + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #2') + + " #3 + call setline('.', '(((foo)))') + let &undolevels = &undolevels + normal 03sr$""" + normal! u + call g:assert.equals(getline('.'), '(((foo)))', 'failed at #3') +endfunction +"}}} + +" When a assigned region is invalid +function! s:suite.invalid_region() abort "{{{ + nmap sr' (operator-sandwich-replace)i' + + " #1 + call setline('.', 'foo') + normal 0lsr'" + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + call g:assert.equals(getpos('.'), [0, 1, 2, 0], 'failed at #1') + + nunmap sr' +endfunction +"}}} + +" input_fallback +function! s:suite.input_fallback() abort "{{{ + let g:sandwich#recipes = [{'buns': ['a', 'a']}] + let g:operator#sandwich#recipes = [] + + let g:sandwich#input_fallback = 1 + call setline('.', 'afooa') + normal 0sriwb + call g:assert.equals(getline('.'), 'bfoob', 'failed at #1') + + let g:sandwich#input_fallback = 0 + call setline('.', 'afooa') + normal 0sriwb + call g:assert.equals(getline('.'), 'afooa', 'failed at #2') + + unlet! g:sandwich#input_fallback + call setline('.', 'afooa') + normal 0sriwb + call g:assert.equals(getline('.'), 'bfoob', 'failed at #3') +endfunction "}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/surround/test-surround.vim b/config/neovim/store/lazy-plugins/vim-sandwich/test/surround/test-surround.vim new file mode 100644 index 00000000..951341cd --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/surround/test-surround.vim @@ -0,0 +1,80 @@ +scriptencoding utf-8 + +let s:suite = themis#suite('keymappings') + +function! s:suite.before() abort "{{{ + runtime macros/sandwich/keymap/surround.vim +endfunction "}}} +function! s:suite.before_each() abort "{{{ + %delete + set filetype= + call operator#sandwich#set_default() + unlet! g:sandwich#recipes + unlet! g:operator#sandwich#recipes +endfunction +"}}} +function! s:suite.after() abort "{{{ + call s:suite.before_each() + mapclear + unlet g:sandwich_no_default_key_mappings + unlet g:operator_sandwich_no_default_key_mappings + unlet g:textobj_sandwich_no_default_key_mappings + runtime plugin/sandwich.vim + runtime plugin/operator/sandwich.vim + runtime plugin/textobj/sandwich.vim +endfunction +"}}} + + +function! s:suite.surround() abort "{{{ + call setline('.', 'foo') + normal 0ysiw( + call g:assert.equals(getline('.'), '(foo)', 'failed at #1') + + call setline('.', 'foo bar') + normal 0yss( + call g:assert.equals(getline('.'), '(foo bar)', 'failed at #2') + + call setline('.', 'foo bar') + normal 04lyS( + call g:assert.equals(getline('.'), 'foo (bar)', 'failed at #3') + + call setline('.', '[(foo)]') + normal 02lds( + call g:assert.equals(getline('.'), '[foo]', 'failed at #4') + + call setline('.', '[(foo)]') + normal 02lds[ + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + call setline('.', '[(foo)]') + normal 02ldss + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') + + call setline('.', '"''foo''"') + normal 02ldss + call g:assert.equals(getline('.'), '"foo"', 'failed at #7') + + call setline('.', '[(foo)]') + normal 02lcs({ + call g:assert.equals(getline('.'), '[{foo}]', 'failed at #8') + + call setline('.', '[(foo)]') + normal 02lcs[{ + call g:assert.equals(getline('.'), '{(foo)}', 'failed at #9') + + call setline('.', '[(foo)]') + normal 02lcss{ + call g:assert.equals(getline('.'), '[{foo}]', 'failed at #10') + + call setline('.', '"''foo''"') + normal 02lcss` + call g:assert.equals(getline('.'), '"`foo`"', 'failed at #11') + + call setline('.', 'foo') + normal 0viwS( + call g:assert.equals(getline('.'), '(foo)', 'failed at #12') +endfunction "}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/test-magicchar-f.vim b/config/neovim/store/lazy-plugins/vim-sandwich/test/test-magicchar-f.vim new file mode 100644 index 00000000..a50da0b5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/test-magicchar-f.vim @@ -0,0 +1,72 @@ +scriptencoding utf-8 + +let s:suite = themis#suite('magicchar-f: ') + +function! s:suite.before() abort "{{{ + nmap sd (sandwich-delete) + xmap sd (sandwich-delete) +endfunction +"}}} +function! s:suite.before_each() abort "{{{ + %delete + set filetype= + unlet! g:sandwich#magicchar#f#patterns + unlet! b:sandwich_magicchar_f_patterns + unlet! g:sandwich#recipes g:operator#sandwich#recipes + unlet! b:sandwich_recipes b:operator_sandwich_recipes + call operator#sandwich#set_default() +endfunction +"}}} +function! s:suite.after() abort "{{{ + call s:suite.before_each() + nunmap sd + xunmap sd +endfunction +"}}} + +function! s:suite.default_pattern() abort "{{{ + call setline('.', 'foo(bar)') + call cursor(1, 1) + normal sdf + call g:assert.equals(getline('.'), 'bar', 'failed at #1') +endfunction "}}} +function! s:suite.global_pattern() abort "{{{ + let g:sandwich#magicchar#f#patterns = [ + \ { + \ 'header' : '\<\%(\h\k*\.\)*\h\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] + call setline('.', 'foo.bar(baz)') + call cursor(1, 5) + normal sdf + call g:assert.equals(getline('.'), 'baz', 'failed at #1') +endfunction "}}} +function! s:suite.local_pattern() abort "{{{ + let g:sandwich#magicchar#f#patterns = [ + \ { + \ 'header' : '\<\h\k*\.\h\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] + let b:sandwich_magicchar_f_patterns = [ + \ { + \ 'header' : '\<\%(\h\k*\.\)*\h\k*', + \ 'bra' : '(', + \ 'ket' : ')', + \ 'footer' : '', + \ }, + \ ] + call setline('.', 'foo.bar.baz(qux)') + call cursor(1, 9) + normal sdf + call g:assert.equals(getline('.'), 'qux', 'failed at #1') +endfunction "}}} + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/test-magicchar-t-emmet.vim b/config/neovim/store/lazy-plugins/vim-sandwich/test/test-magicchar-t-emmet.vim new file mode 100644 index 00000000..a6a63670 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/test-magicchar-t-emmet.vim @@ -0,0 +1,536 @@ +scriptencoding utf-8 + +let s:suite = themis#suite('magicchar-t emmet-like behavior:') + +let s:scope = themis#helper('scope') +let s:t = s:scope.funcs('autoload/sandwich/magicchar/t.vim') + +" test seeds "{{{ +let s:testseeds = {} +let s:testseeds.element = [ + \ { + \ 'input': 'element', + \ 'token': ['element'], + \ 'items': [{'name': 'element', 'value': 'element'}], + \ }, + \ + \ { + \ 'input': '', + \ 'token': [], + \ 'items': [{'name': 'element', 'value': 'div'}], + \ }, + \ ] +let s:testseeds.attributes = [ + \ { + \ 'input': '#id1', + \ 'token': ['#', 'id1'], + \ 'items': [{'name': 'id', 'value': 'id1'}], + \ }, + \ + \ { + \ 'input': '#id2', + \ 'token': ['#', 'id2'], + \ 'items': [{'name': 'id', 'value': 'id2'}], + \ }, + \ + \ { + \ 'input': '#', + \ 'token': ['#'], + \ 'items': [{'name': 'id', 'value': ''}], + \ }, + \ + \ { + \ 'input': '.class1', + \ 'token': ['.', 'class1'], + \ 'items': [{'name': 'class', 'value': ['class1']}], + \ }, + \ + \ { + \ 'input': '.class2', + \ 'token': ['.', 'class2'], + \ 'items': [{'name': 'class', 'value': ['class2']}], + \ }, + \ + \ { + \ 'input': '.', + \ 'token': ['.'], + \ 'items': [{'name': 'class', 'value': ['']}], + \ }, + \ + \ { + \ 'input': '[attr=hello]', + \ 'token': ['[', 'attr', '=', 'hello', ']'], + \ 'items': [{'name': 'attr', 'value': 'hello'}], + \ }, + \ + \ { + \ 'input': '[attr=world]', + \ 'token': ['[', 'attr', '=', 'world', ']'], + \ 'items': [{'name': 'attr', 'value': 'world'}], + \ }, + \ + \ { + \ 'input': '[hello=world]', + \ 'token': ['[', 'hello', '=', 'world', ']'], + \ 'items': [{'name': 'hello', 'value': 'world'}], + \ }, + \ + \ { + \ 'input': '[]', + \ 'token': ['[', ']'], + \ 'items': [], + \ }, + \ + \ { + \ 'input': '[attr="including space"]', + \ 'token': ['[', 'attr', '=', '"including space"', ']'], + \ 'items': [{'name': 'attr', 'value': '"including space"'}], + \ }, + \ + \ { + \ 'input': '[ attr="including space"]', + \ 'token': ['[', ' ', 'attr', '=', '"including space"', ']'], + \ 'items': [{'name': 'attr', 'value': '"including space"'}], + \ }, + \ + \ { + \ 'input': '[attr ="including space"]', + \ 'token': ['[', 'attr', ' ', '=', '"including space"', ']'], + \ 'items': [ + \ {'name': 'attr', 'value': ''}, + \ {'name': '', 'value': '"including space"'}, + \ ], + \ }, + \ + \ { + \ 'input': '[attr= "including space"]', + \ 'token': ['[', 'attr', '=', ' ', '"including space"', ']'], + \ 'items': [ + \ {'name': 'attr', 'value': ''}, + \ {'name': '"including space"', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': '[attr="including space" ]', + \ 'token': ['[', 'attr', '=', '"including space"', ' ', ']'], + \ 'items': [{'name': 'attr', 'value': '"including space"'}], + \ }, + \ + \ { + \ 'input': "[attr='including space']", + \ 'token': ['[', 'attr', '=', "'including space'", ']'], + \ 'items': [{'name': 'attr', 'value': "'including space'"}], + \ }, + \ + \ { + \ 'input': "[ attr='including space']", + \ 'token': ['[', ' ', 'attr', '=', "'including space'", ']'], + \ 'items': [{'name': 'attr', 'value': "'including space'"}], + \ }, + \ + \ { + \ 'input': "[attr ='including space']", + \ 'token': ['[', 'attr', ' ', '=', "'including space'", ']'], + \ 'items': [ + \ {'name': 'attr', 'value': ""}, + \ {'name': '', 'value': "'including space'"}, + \ ], + \ }, + \ + \ { + \ 'input': "[attr= 'including space']", + \ 'token': ['[', 'attr', '=', ' ', "'including space'", ']'], + \ 'items': [ + \ {'name': 'attr', 'value': ""}, + \ {'name': "'including space'", 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[attr='including space' ]", + \ 'token': ['[', 'attr', '=', "'including space'", ' ', ']'], + \ 'items': [{'name': 'attr', 'value': "'including space'"}], + \ }, + \ + \ { + \ 'input': "[attr=withoutspace]", + \ 'token': ['[', 'attr', '=', 'withoutspace', ']'], + \ 'items': [{'name': 'attr', 'value': 'withoutspace'}], + \ }, + \ + \ { + \ 'input': "[ attr=withoutspace]", + \ 'token': ['[', ' ', 'attr', '=', 'withoutspace', ']'], + \ 'items': [{'name': 'attr', 'value': 'withoutspace'}], + \ }, + \ + \ { + \ 'input': "[attr =withoutspace]", + \ 'token': ['[', 'attr', ' ', '=', 'withoutspace', ']'], + \ 'items': [ + \ {'name': 'attr', 'value': ''}, + \ {'name': '', 'value': 'withoutspace'}, + \ ], + \ }, + \ + \ { + \ 'input': "[attr= withoutspace]", + \ 'token': ['[', 'attr', '=', ' ', 'withoutspace', ']'], + \ 'items': [ + \ {'name': 'attr', 'value': ''}, + \ {'name': 'withoutspace', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[attr=withoutspace ]", + \ 'token': ['[', 'attr', '=', 'withoutspace', ' ', ']'], + \ 'items': [{'name': 'attr', 'value': 'withoutspace'}], + \ }, + \ + \ { + \ 'input': "[attr=]", + \ 'token': ['[', 'attr', '=', ']'], + \ 'items': [{'name': 'attr', 'value': ''}], + \ }, + \ + \ { + \ 'input': "[ attr=]", + \ 'token': ['[', ' ', 'attr', '=', ']'], + \ 'items': [{'name': 'attr', 'value': ''}], + \ }, + \ + \ { + \ 'input': "[attr =]", + \ 'token': ['[', 'attr', ' ', '=', ']'], + \ 'items': [ + \ {'name': 'attr', 'value': ''}, + \ {'name': '', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[attr= ]", + \ 'token': ['[', 'attr', '=', ' ', ']'], + \ 'items': [{'name': 'attr', 'value': ''}], + \ }, + \ + \ { + \ 'input': "[=value]", + \ 'token': ['[', '=', 'value', ']'], + \ 'items': [{'name': '', 'value': 'value'}], + \ }, + \ + \ { + \ 'input': "[ =value]", + \ 'token': ['[', ' ', '=', 'value', ']'], + \ 'items': [{'name': '', 'value': 'value'}], + \ }, + \ + \ { + \ 'input': "[= value]", + \ 'token': ['[', '=', ' ', 'value', ']'], + \ 'items': [ + \ {'name': '', 'value': ''}, + \ {'name': 'value', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[=value ]", + \ 'token': ['[', '=', 'value', ' ', ']'], + \ 'items': [{'name': '', 'value': 'value'}], + \ }, + \ + \ { + \ 'input': "[==]", + \ 'token': ['[', '=', '=', ']'], + \ 'items': [ + \ {'name': '', 'value': ''}, + \ {'name': '', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[ ==]", + \ 'token': ['[', ' ', '=', '=', ']'], + \ 'items': [ + \ {'name': '', 'value': ''}, + \ {'name': '', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[= =]", + \ 'token': ['[', '=', ' ', '=', ']'], + \ 'items': [ + \ {'name': '', 'value': ''}, + \ {'name': '', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[== ]", + \ 'token': ['[', '=', '=', ' ', ']'], + \ 'items': [ + \ {'name': '', 'value': ''}, + \ {'name': '', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[attr==]", + \ 'token': ['[', 'attr', '=', '=', ']'], + \ 'items': [ + \ {'name': 'attr', 'value': ''}, + \ {'name': '', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[=value=]", + \ 'token': ['[', '=', 'value', '=', ']'], + \ 'items': [ + \ {'name': '', 'value': 'value'}, + \ {'name': '', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[==value]", + \ 'token': ['[', '=', '=', 'value', ']'], + \ 'items': [ + \ {'name': '', 'value': ''}, + \ {'name': '', 'value': 'value'}, + \ ], + \ }, + \ + \ { + \ 'input': "['word1 word2'==]", + \ 'token': ['[', "'word1 word2'", '=', '=', ']'], + \ 'items': [ + \ {'name': "'word1 word2'", 'value': ''}, + \ {'name': '', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[='word1 word2'=]", + \ 'token': ['[', '=', "'word1 word2'", '=', ']'], + \ 'items': [ + \ {'name': '', 'value': "'word1 word2'"}, + \ {'name': '', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[=='word1 word2']", + \ 'token': ['[', '=', '=', "'word1 word2'", ']'], + \ 'items': [ + \ {'name': '', 'value': ''}, + \ {'name': '', 'value': "'word1 word2'"}, + \ ], + \ }, + \ + \ { + \ 'input': "[attr1=value1 attr2=value2]", + \ 'token': ['[', 'attr1', '=', 'value1', ' ', 'attr2', '=', 'value2', ']'], + \ 'items': [ + \ {'name': 'attr1', 'value': 'value1'}, + \ {'name': 'attr2', 'value': 'value2'}, + \ ], + \ }, + \ + \ { + \ 'input': "[attr1=value1 attr2]", + \ 'token': ['[', 'attr1', '=', 'value1', ' ', 'attr2', ']'], + \ 'items': [ + \ {'name': 'attr1', 'value': 'value1'}, + \ {'name': 'attr2', 'value': ''}, + \ ], + \ }, + \ + \ { + \ 'input': "[attr1 attr2=value2]", + \ 'token': ['[', 'attr1', ' ', 'attr2', '=', 'value2', ']'], + \ 'items': [ + \ {'name': 'attr1', 'value': ''}, + \ {'name': 'attr2', 'value': 'value2'}, + \ ], + \ }, + \ + \ { + \ 'input': "[attr1 attr2]", + \ 'token': ['[', 'attr1', ' ', 'attr2', ']'], + \ 'items': [ + \ {'name': 'attr1', 'value': ''}, + \ {'name': 'attr2', 'value': ''}, + \ ], + \ }, + \ ] +function! s:testseeds.generate_parsed(itemlist) dict abort "{{{ + let itemlist = deepcopy(a:itemlist) + call s:overwrite(itemlist, 'id') + call s:append(itemlist, 'class') + let custom_attr_list = map(filter(deepcopy(itemlist), 'has_key(v:val, "name") && v:val.name !~# ''\%(element\|id\|class\)'''), 'v:val.name') + call s:uniq(filter(custom_attr_list, 'v:val !=# ""')) + for attr in custom_attr_list + call s:overwrite(itemlist, attr) + endfor + return itemlist +endfunction +"}}} +function! s:overwrite(itemlist, name) abort "{{{ + let i = 0 + let n = len(a:itemlist) + let i_target = -1 + while i < n + let item = a:itemlist[i] + if item.name ==# a:name + let i_target = i + break + endif + let i += 1 + endwhile + if i_target > 0 + let i = n - 1 + let value = '' + let value_is_fixed = 0 + while i > i_target + let item = a:itemlist[i] + if item.name ==# a:name + if !value_is_fixed + let value = item.value + let value_is_fixed = 1 + endif + call remove(a:itemlist, i) + endif + let i -= 1 + endwhile + if value_is_fixed + let a:itemlist[i_target]['value'] = value + endif + endif +endfunction +"}}} +function! s:append(itemlist, name) abort "{{{ + let i = 0 + let n = len(a:itemlist) + let i_target = -1 + while i < n + let item = a:itemlist[i] + if item.name ==# a:name + let i_target = i + break + endif + let i += 1 + endwhile + if i_target > 0 + let i = n - 1 + let value = [] + while i > i_target + let item = a:itemlist[i] + if item.name ==# a:name + let value += item.value + call remove(a:itemlist, i) + endif + let i -= 1 + endwhile + if value !=# [] + let a:itemlist[i_target]['value'] += reverse(value) + endif + endif +endfunction +"}}} +function! s:uniq(list) abort "{{{ + let i = len(a:list) - 1 + while i > 0 + let item = a:list[i] + if count(a:list, item) > 1 + call remove(a:list, i) + endif + let i -= 1 + endwhile + return a:list +endfunction +"}}} +"}}} + +function! s:suite.tokenize() dict abort "{{{ + " happy paths + + " 1 seed + for element in deepcopy(s:testseeds.element) + for attribute1 in deepcopy(s:testseeds.attributes) + let input = join([element.input, attribute1.input], '') + let expect = element.token + attribute1.token + call g:assert.equals(s:t.tokenize(input), expect, 'input: ' . input) + endfor + endfor + + " 2 seeds + for element in deepcopy(s:testseeds.element) + for attribute1 in deepcopy(s:testseeds.attributes) + for attribute2 in deepcopy(s:testseeds.attributes) + let input = join([element.input, attribute1.input, attribute2.input], '') + let expect = element.token + attribute1.token + attribute2.token + call g:assert.equals(s:t.tokenize(input), expect, 'input: ' . input) + endfor + endfor + endfor + + " " 3 seeds + " for element in deepcopy(s:testseeds.element) + " for attribute1 in deepcopy(s:testseeds.attributes) + " for attribute2 in deepcopy(s:testseeds.attributes) + " for attribute3 in deepcopy(s:testseeds.attributes) + " let input = join([element.input, attribute1.input, attribute2.input, attribute3.input], '') + " let expect = element.token + attribute1.token + attribute2.token + attribute3.token + " call g:assert.equals(s:t.tokenize(input), expect, 'input: ' . input) + " endfor + " endfor + " endfor + " endfor +endfunction +"}}} +function! s:suite.parse() dict abort "{{{ + " happy paths + + " 1 seed + for element in deepcopy(s:testseeds.element) + for attribute1 in deepcopy(s:testseeds.attributes) + let input = element.token + attribute1.token + let expect = s:testseeds.generate_parsed(element.items + attribute1.items) + call g:assert.equals(s:t.parse(input), expect, 'input: ' . string(input)) + endfor + endfor + + " 2 seeds + for element in deepcopy(s:testseeds.element) + for attribute1 in deepcopy(s:testseeds.attributes) + for attribute2 in deepcopy(s:testseeds.attributes) + let input = element.token + attribute1.token + attribute2.token + let expect = s:testseeds.generate_parsed(element.items + attribute1.items + attribute2.items) + call g:assert.equals(s:t.parse(input), expect, 'input: ' . string(input)) + endfor + endfor + endfor + + " " 3 seeds + " for element in deepcopy(s:testseeds.element) + " for attribute1 in deepcopy(s:testseeds.attributes) + " for attribute2 in deepcopy(s:testseeds.attributes) + " for attribute3 in deepcopy(s:testseeds.attributes) + " let input = element.token + attribute1.token + attribute2.token + attribute3.token + " let expect = s:testseeds.generate_parsed(element.items + attribute1.items + attribute2.items + attribute3.items) + " call g:assert.equals(s:t.parse(input), expect, 'input: ' . string(input)) + " endfor + " endfor + " endfor + " endfor +endfunction +"}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/test-magicchar-t.vim b/config/neovim/store/lazy-plugins/vim-sandwich/test/test-magicchar-t.vim new file mode 100644 index 00000000..fe350888 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/test-magicchar-t.vim @@ -0,0 +1,91 @@ +scriptencoding utf-8 + +let s:suite = themis#suite('magicchar-t:') + +let s:scope = themis#helper('scope') +let s:t = s:scope.funcs('autoload/sandwich/magicchar/t.vim') + + +function! s:suite.before() abort "{{{ + nmap sa (sandwich-add) + xmap sa (sandwich-add) + omap sa (sandwich-add) + nmap sd (sandwich-delete) + xmap sd (sandwich-delete) + nmap sr (sandwich-replace) + xmap sr (sandwich-replace) +endfunction +"}}} +function! s:suite.after() abort "{{{ + nunmap sa + xunmap sa + ounmap sa + nunmap sd + xunmap sd + nunmap sr + xunmap sr +endfunction +"}}} + +function! s:suite.add() abort "{{{ + " #1 + call setline(1, 'foo') + execute "normal 1Gsaiwtp\" + call g:assert.equals(getline(1), '

foo

', 'failed at #1') + + " #2 + call setline(1, 'foo') + execute "normal 1GsaiwTp\" + call g:assert.equals(getline(1), '

foo

', 'failed at #2') +endfunction "}}} +function! s:suite.delete() abort "{{{ + " #1 + call setline(1, '

foo

') + normal 1Gsdt + call g:assert.equals(getline(1), 'foo', 'failed at #1') + + " #2 + call setline(1, '

foo

') + normal 1GsdT + call g:assert.equals(getline(1), 'foo', 'failed at #2') +endfunction "}}} +function! s:suite.replace() abort "{{{ + " #1 + call setline(1, '

foo

') + execute "normal 1Gffsrttdiv\" + call g:assert.equals(getline(1), '
foo
', 'failed at #1') + + " #2 + call setline(1, '

foo

') + execute "normal 1GffsrTTdiv\" + call g:assert.equals(getline(1), '
foo
', 'failed at #2') + + " #3 + call setline(1, '
foo
') + execute "normal 1Gffsrttp\" + call g:assert.equals(getline(1), '

foo

', 'failed at #3') + + " #4 + call setline(1, '
foo
') + execute "normal 1GffsrTTp\" + call g:assert.equals(getline(1), '

foo

', 'failed at #4') +endfunction "}}} +function! s:suite.add_selection_exclusive() abort "{{{ + set selection=exclusive + call s:suite.add() + set selection=inclusive +endfunction "}}} +function! s:suite.delete_selection_exclusive() abort "{{{ + set selection=exclusive + call s:suite.delete() + set selection=inclusive +endfunction "}}} +function! s:suite.replace_selection_exclusive() abort "{{{ + set selection=exclusive + call s:suite.replace() + set selection=inclusive +endfunction "}}} + + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/textobj-auto.vim b/config/neovim/store/lazy-plugins/vim-sandwich/test/textobj-auto.vim new file mode 100644 index 00000000..d671dea2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/textobj-auto.vim @@ -0,0 +1,5800 @@ +scriptencoding utf-8 + +let s:suite = themis#suite('textobj-sandwich: auto:') + +function! s:suite.before() abort "{{{ + omap ib (textobj-sandwich-auto-i) + xmap ib (textobj-sandwich-auto-i) + omap ab (textobj-sandwich-auto-a) + xmap ab (textobj-sandwich-auto-a) +endfunction +"}}} +function! s:suite.before_each() abort "{{{ + %delete + syntax off + set filetype= + set virtualedit& + set whichwrap& + call textobj#sandwich#set_default() + call operator#sandwich#set_default() + unlet! g:sandwich#recipes + unlet! g:textobj#sandwich#recipes + silent! xunmap i{ + silent! xunmap a{ + silent! ounmap iib + silent! ounmap aab + silent! nunmap sd + silent! xunmap sd + silent! nunmap sdb +endfunction +"}}} +function! s:suite.after() abort "{{{ + call s:suite.before_each() + ounmap ib + xunmap ib + ounmap ab + xunmap ab +endfunction +"}}} + +" Filter +function! s:suite.filter_filetype() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'filetype': ['vim']}, + \ {'buns': ['{', '}'], 'filetype': ['all']}, + \ {'buns': ['<', '>'], 'filetype': ['']} + \ ] + + " #1 + call setline('.', '([foo])') + normal 03ldib + call g:assert.equals(getline('.'), '()', 'failed at #1') + + " #2 + call setline('.', '{foo}') + normal 02ldib + call g:assert.equals(getline('.'), '{}', 'failed at #2') + + " #3 + call setline('.', '') + normal 02ldib + call g:assert.equals(getline('.'), '<>', 'failed at #3') + + set filetype=vim + + " #4 + call setline('.', '([foo])') + normal 03ldib + call g:assert.equals(getline('.'), '([])', 'failed at #4') + + " #5 + call setline('.', '{foo}') + normal 02ldib + call g:assert.equals(getline('.'), '{}', 'failed at #5') + + " #6 + call setline('.', '') + normal 02ldib + call g:assert.equals(getline('.'), '', 'failed at #6') +endfunction +"}}} +function! s:suite.filter_kind() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ ] + + " #1 + call setline('.', '([foo])') + normal 03ldib + call g:assert.equals(getline('.'), '()', 'failed at #1') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'kind': ['query']}, + \ ] + + " #2 + call setline('.', '([foo])') + normal 03ldib + call g:assert.equals(getline('.'), '()', 'failed at #2') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'kind': ['auto']}, + \ ] + + " #3 + call setline('.', '([foo])') + normal 03ldib + call g:assert.equals(getline('.'), '([])', 'failed at #3') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'kind': ['textobj']}, + \ ] + + " #4 + call setline('.', '([foo])') + normal 03ldib + call g:assert.equals(getline('.'), '([])', 'failed at #4') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'kind': ['all']}, + \ ] + + " #5 + call setline('.', '([foo])') + normal 03ldib + call g:assert.equals(getline('.'), '([])', 'failed at #5') +endfunction +"}}} +function! s:suite.filter_mode() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']']}, + \ ] + + " #1 + call setline('.', '([foo])') + normal 03ldib + call g:assert.equals(getline('.'), '([])', 'failed at #1') + + " #2 + call setline('.', '([foo])') + normal 03lvibd + call g:assert.equals(getline('.'), '([])', 'failed at #2') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'mode': ['o']}, + \ ] + + " #3 + call setline('.', '([foo])') + normal 03ldib + call g:assert.equals(getline('.'), '([])', 'failed at #3') + + " #4 + call setline('.', '([foo])') + normal 03lvibd + call g:assert.equals(getline('.'), '()', 'failed at #4') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'mode': ['x']}, + \ ] + + " #5 + call setline('.', '([foo])') + normal 03ldib + call g:assert.equals(getline('.'), '()', 'failed at #5') + + " #6 + call setline('.', '([foo])') + normal 03lvibd + call g:assert.equals(getline('.'), '([])', 'failed at #6') +endfunction +"}}} +function! s:suite.filter_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'expr_filter': ['FilterValid()']}, + \ {'buns': ['{', '}'], 'expr_filter': ['FilterInvalid()']}, + \ ] + + function! FilterValid() abort + return 1 + endfunction + + function! FilterInvalid() abort + return 0 + endfunction + + " #1 + call setline('.', '(foo)') + normal 0dib + call g:assert.equals(getline('.'), '()', 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0dib + call g:assert.equals(getline('.'), '[]', 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal 0dib + call g:assert.equals(getline('.'), '{foo}', 'failed at #3') +endfunction +"}}} + +function! s:suite.i_o_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'foo', 'failed at #1') + + " #2 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'foo', 'failed at #2') + + " #3 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'foo', 'failed at #3') + + " #4 + call setline('.', '') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'foo', 'failed at #4') + + " #5 + call setline('.', '"foo"') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'foo', 'failed at #5') + + " #6 + call setline('.', "'foo'") + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'foo', 'failed at #6') +endfunction +"}}} +function! s:suite.i_o_nest() abort "{{{ + " #1 + call setline('.', '()') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #1') + + " #2 + call setline('.', '(a)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'a', 'failed at #2') + + " #3 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #3') + + " #4 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0lyib + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #4') + + " #5 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #5') + + " #6 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 03lyib + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #6') + + " #7 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 04lyib + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #7') + + " #8 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 05lyib + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #8') + + " #9 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 06lyib + call g:assert.equals(@@, 'cc', 'failed at #9') + + " #10 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 07lyib + call g:assert.equals(@@, 'cc', 'failed at #10') + + " #11 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 08lyib + call g:assert.equals(@@, 'cc', 'failed at #11') + + " #12 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 09lyib + call g:assert.equals(@@, 'cc', 'failed at #12') + + " #13 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 010lyib + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #13') + + " #14 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 011lyib + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #14') + + " #15 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 012lyib + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #15') + + " #16 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 013lyib + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #16') + + " #17 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 014lyib + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #17') + + " #18 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 015lyib + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(((', ')))'], 'nesting': 1}] + + " #19 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #19') + + " #20 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0lyib + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #20') + + " #21 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #21') + + " #22 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 03lyib + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #22') + + " #23 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 04lyib + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #23') + + " #24 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 05lyib + call g:assert.equals(@@, 'bb', 'failed at #24') + + " #25 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 06lyib + call g:assert.equals(@@, 'bb', 'failed at #25') + + " #26 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 07lyib + call g:assert.equals(@@, 'bb', 'failed at #26') + + " #27 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 08lyib + call g:assert.equals(@@, 'bb', 'failed at #27') + + " #28 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 09lyib + call g:assert.equals(@@, 'bb', 'failed at #28') + + " #29 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 010lyib + call g:assert.equals(@@, 'bb', 'failed at #29') + + " #30 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 011lyib + call g:assert.equals(@@, 'bb', 'failed at #30') + + " #31 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 012lyib + call g:assert.equals(@@, 'bb', 'failed at #31') + + " #32 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 013lyib + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #32') + + " #33 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 014lyib + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #33') + + " #34 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 015lyib + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #34') + + " #35 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 016lyib + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #35') + + " #36 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 017lyib + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #36') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'nesting': 0}, {'external': ['it', 'at'], 'input': ['t']}] + + " #37 + call setline('.', '
Anchor Text') + let @@ = 'fail' + normal 0fwyib + call g:assert.equals(@@, 'http://www.url.com', 'failed at #37') +endfunction +"}}} +function! s:suite.i_o_no_nest() abort "{{{ + " #1 + call setline('.', '""') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #1') + + " #2 + call setline('.', '"a"') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'a', 'failed at #2') + + " #3 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #3') + + " #4 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0lyib + call g:assert.equals(@@, 'aa', 'failed at #4') + + " #5 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 03lyib + call g:assert.equals(@@, 'aa', 'failed at #6') + + " #7 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 04lyib + call g:assert.equals(@@, 'bb', 'failed at #7') + + " #8 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 05lyib + call g:assert.equals(@@, 'bb', 'failed at #8') + + " #9 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 06lyib + call g:assert.equals(@@, 'cc', 'failed at #9') + + " #10 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 07lyib + call g:assert.equals(@@, 'cc', 'failed at #10') + + " #11 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 08lyib + call g:assert.equals(@@, 'cc', 'failed at #11') + + " #12 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 09lyib + call g:assert.equals(@@, 'cc', 'failed at #12') + + " #13 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 010lyib + call g:assert.equals(@@, 'bb', 'failed at #13') + + " #14 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 011lyib + call g:assert.equals(@@, 'bb', 'failed at #14') + + " #15 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 012lyib + call g:assert.equals(@@, 'aa', 'failed at #15') + + " #16 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 013lyib + call g:assert.equals(@@, 'aa', 'failed at #16') + + " #17 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 014lyib + call g:assert.equals(@@, 'aa', 'failed at #17') + + " #18 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 015lyib + call g:assert.equals(@@, 'aa', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"""', '"""'], 'nesting': 0}] + + " #19 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #19') + + " #20 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0lyib + call g:assert.equals(@@, 'aa', 'failed at #20') + + " #21 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'aa', 'failed at #21') + + " #22 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 03lyib + call g:assert.equals(@@, 'aa', 'failed at #22') + + " #23 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 04lyib + call g:assert.equals(@@, 'aa', 'failed at #23') + + " #24 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 05lyib + call g:assert.equals(@@, 'aa', 'failed at #24') + + " #25 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 06lyib + call g:assert.equals(@@, 'aa', 'failed at #25') + + " #26 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 07lyib + call g:assert.equals(@@, 'aa', 'failed at #26') + + " #27 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 08lyib + call g:assert.equals(@@, 'bb', 'failed at #27') + + " #28 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 09lyib + call g:assert.equals(@@, 'bb', 'failed at #28') + + " #29 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 010lyib + call g:assert.equals(@@, 'cc', 'failed at #29') + + " #30 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 011lyib + call g:assert.equals(@@, 'cc', 'failed at #30') + + " #31 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 012lyib + call g:assert.equals(@@, 'cc', 'failed at #31') + + " #32 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 013lyib + call g:assert.equals(@@, 'cc', 'failed at #32') + + " #33 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 014lyib + call g:assert.equals(@@, 'cc', 'failed at #33') + + " #34 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 015lyib + call g:assert.equals(@@, 'cc', 'failed at #34') + + " #35 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 016lyib + call g:assert.equals(@@, 'cc', 'failed at #35') + + " #36 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 017lyib + call g:assert.equals(@@, 'cc', 'failed at #36') +endfunction +"}}} +function! s:suite.i_o_external_textobj() abort "{{{ + let g:textobj#sandwich#recipes = [{'external': ['it', 'at']}] + + " #1 + call setline('.', 'aabbaa') + let @@ = 'fail' + normal 0fbyib + call g:assert.equals(@@, 'bb', 'failed at #1') +endfunction +"}}} +function! s:suite.i_o_multibyte() abort "{{{ + let g:textobj#sandwich#recipes = [{'buns': ['α', 'α'], 'input': ['a']}] + + " #1 + call setline('.', 'aaαbbαaa') + let @@ = 'fail' + normal 0fbyib + call g:assert.equals(@@, 'bb', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['aα', 'aα'], 'input': ['a']}] + + " #2 + call setline('.', 'aaαbbaαa') + let @@ = 'fail' + normal 0fbyib + call g:assert.equals(@@, 'bb', 'failed at #2') +endfunction +"}}} +function! s:suite.i_o_option_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2']}] + + """ off + " #1 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #1') + + " #2 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2'], 'expr': 1}] + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #3') + + """ on + call textobj#sandwich#set('auto', 'expr', 1) + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2']}] + + " #4 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #4') + + " #5 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + let g:textobj#sandwich#recipes = [{'buns': ['SandwichExprEmpty()', '1+2']}] + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #6') + + " #7 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', 'SandwichExprEmpty()']}] + call setline('.', '2aa3') + let @@ = 'fail' + normal $yib + call g:assert.equals(@@, '', 'failed at #7') + + " #9 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2'], 'expr': 0}] + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #9') +endfunction +"}}} +function! s:suite.i_o_option_listexpr() abort "{{{ + let g:sandwich#recipes = [] + + """ on + call textobj#sandwich#set('auto', 'listexpr', 1) + " #1 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprBuns(0)'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'bar', 'failed at #1') + + " #2 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("former")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("latter")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #3') + + " #4 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("both")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #4') +endfunction +"}}} +function! s:suite.i_o_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #1') + + " #2 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #3') + + """ on + call textobj#sandwich#set('auto', 'regex', 1) + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #4 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #4') + + " #5 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #6') +endfunction +"}}} +function! s:suite.i_o_option_skip_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ off + " #1 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #1') + + " #2 + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_regex': ['a']}] + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #2') + + """ on + call textobj#sandwich#set('auto', 'skip_regex', ['aa']) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + " #3 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'fooa', 'failed at #3') + + """ head and tail + let g:textobj#sandwich#recipes = [{'buns': ["'", "'"]}] + call textobj#sandwich#set('auto', 'skip_regex_head', ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']']) + call textobj#sandwich#set('auto', 'skip_regex_tail', ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)''']) + " #4 + call setline('.', "'''foo'''") + let @@ = 'fail' + normal 0ffyib + call g:assert.equals(@@, "''foo''", 'failed at #4') +endfunction +"}}} +function! s:suite.i_o_option_quoteescape() abort "{{{ + """ off + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #1 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa\', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'quoteescape': 1}] + + " #2 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa\"bb', 'failed at #2') + + """ on + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + call textobj#sandwich#set('auto', 'quoteescape', 1) + + " #3 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa\"bb', 'failed at #3') +endfunction +"}}} +function! s:suite.i_o_option_expand_range() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + """ -1 + " #1 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #1') + + %delete + + " #2 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggyib + call g:assert.equals(@@, "\naa\n", 'failed at #2') + + %delete + + " #3 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggyib + call g:assert.equals(@@, "\naa\nbb\ncc\n", 'failed at #3') + + %delete + + " #4 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 0}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyib + call g:assert.equals(@@, "", 'failed at #4') + + %delete + + """ 0 + call textobj#sandwich#set('auto', 'expand_range', 0) + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #5 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #5') + + %delete + + " #6 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggyib + call g:assert.equals(@@, '', 'failed at #6') + + %delete + + " #7 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggyib + call g:assert.equals(@@, '', 'failed at #7') + + %delete + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 1}] + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjyib + call g:assert.equals(@@, "\naa\n", 'failed at #8') + + %delete + + """ 1 + call textobj#sandwich#set('auto', 'expand_range', 1) + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #9 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'aa', 'failed at #9') + + %delete + + " #10 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjyib + call g:assert.equals(@@, "\naa\n", 'failed at #10') + + %delete + + " #11 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggyib + call g:assert.equals(@@, '', 'failed at #11') + + %delete + + " #12 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': -1}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyib + call g:assert.equals(@@, "\naa\nbb\ncc\n", 'failed at #12') +endfunction +"}}} +function! s:suite.i_o_option_noremap() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #1') + + " #2 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #3') + + " #4 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #4') + + """ off + call textobj#sandwich#set('auto', 'noremap', 0) + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #6') + + " #7 + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #7') + + " #8 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #8') +endfunction +"}}} +function! s:suite.i_o_option_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + call textobj#sandwich#set('auto', 'syntax', []) + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #1') + + call textobj#sandwich#set('auto', 'syntax', ['Special']) + syn match TestParen '[()]' + highlight link TestParen String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #3') + + call textobj#sandwich#set('auto', 'syntax', []) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'syntax': ['Special']}] + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #4') +endfunction +"}}} +function! s:suite.i_o_option_inner_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + call textobj#sandwich#set('auto', 'inner_syntax', []) + + " #1 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'bar', 'failed at #1') + + call textobj#sandwich#set('auto', 'inner_syntax', ['Special']) + syn match TestParen '[br]' + highlight link TestParen String + + " #2 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'bar', 'failed at #3') + + call textobj#sandwich#set('auto', 'inner_syntax', []) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'inner_syntax': ['Special']}] + + " #4 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'bar', 'failed at #4') +endfunction +"}}} +function! s:suite.i_o_option_match_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + + """ 0 (test recipe-local) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'match_syntax': 1}] + call textobj#sandwich#set('auto', 'match_syntax', 0) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #3') + + """ 1 + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}, {'buns': ['"', '"']}] + call textobj#sandwich#set('auto', 'match_syntax', 1) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #4') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #5') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #6 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #6') + + """ 2 + call textobj#sandwich#set('auto', 'match_syntax', 2) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #7 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #7') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #8 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #8') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #9 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #9') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #10 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '%s', 'failed at #10') + + """ 3 + call textobj#sandwich#set('auto', 'match_syntax', 3) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #11 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #11') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #12 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '', 'failed at #12') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #13 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, 'foo', 'failed at #13') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #14 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, '%s', 'failed at #14') +endfunction +"}}} +function! s:suite.i_o_option_skip_break() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + + """ 0 + " #1 + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggyib + call g:assert.equals(@@, "\nfoo\n", 'failed at #1') + + %delete + + " #2 + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'skip_break': 1}] + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggyib + call g:assert.equals(@@, 'foo', 'failed at #2') + + %delete + + """ 1 + call textobj#sandwich#set('auto', 'skip_break', 1) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + + " #3 + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggyib + call g:assert.equals(@@, "foo", 'failed at #3') + + %delete + + " #4 + call append(0, [' (', ' foo', ' )']) + let @@ = 'fail' + normal ggyib + call g:assert.equals(@@, "foo", 'failed at #4') + + %delete + + " #5 + " do not skip when any line breaking is not included. + call setline('.', '( foo )') + let @@ = 'fail' + normal 0yib + call g:assert.equals(@@, " foo ", 'failed at #5') +endfunction +"}}} +function! s:suite.i_o_option_skip_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ expression + call textobj#sandwich#set('auto', 'skip_expr', ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']) + " #1 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'aaa', 'failed at #1') + + %delete + + """ funcref + call textobj#sandwich#set('auto', 'skip_expr', [function('SandwichSkipIntermediate')]) + " #2 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'aaa', 'failed at #2') + + %delete + + """ recipe-local + call textobj#sandwich#set('auto', 'skip_expr', []) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_expr': ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']}] + " #3 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyib + call g:assert.equals(@@, 'aaa', 'failed at #3') +endfunction +"}}} +function! s:suite.i_o_priority() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + + " #1 + call setline('.', '"aa(b"c)') + let @@ = 'fail' + normal 0fbyib + call g:assert.equals(@@, 'b"c', 'failed at #1') + + " #2 + call setline('.', '"aa(b"ccc)') + let @@ = 'fail' + normal 0fbyib + call g:assert.equals(@@, 'aa(b', 'failed at #2') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ["'", "'"]}, + \ {'buns': ["'", "'"], 'filetype': ['vim'], 'skip_regex': ['[^'']\%(''''\)*\zs''''', '[^'']\%(''''\)*''\zs''']} + \ ] + + " #3 + call setline('.', "'foo''bar'") + let @@ = 'fail' + normal 0ffyib + call g:assert.equals(@@, "foo", 'failed at #3') + + " #4 + set filetype=vim + call setline('.', "'foo''bar'") + let @@ = 'fail' + normal 0ffyib + call g:assert.equals(@@, "foo''bar", 'failed at #4') + + set filetype= + let g:textobj#sandwich#recipes = [ + \ {'buns': ['^', '$']}, + \ {'buns': ['^', '$'], 'regex': 1} + \ ] + + " #5 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0fbyib + call g:assert.equals(@@, 'oobarbaz', 'failed at #5') + + " #6 + call setline('.', 'foo^bar$baz') + let @@ = 'fail' + normal 0fbyib + call g:assert.equals(@@, 'bar', 'failed at #6') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['1+1', '1+1']}, + \ {'buns': ['1+1', '1+1'], 'expr': 1} + \ ] + + " #7 + call setline('.', '1+12foo21+1') + let @@ = 'fail' + normal 0ffyib + call g:assert.equals(@@, 'foo', 'failed at #7') + + " #8 + call setline('.', '21+1foo1+12') + let @@ = 'fail' + normal 0ffyib + call g:assert.equals(@@, 'foo', 'failed at #8') + + let g:textobj#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i{', 'a{'], 'noremap': 0} + \ ] + xnoremap i{ i[ + xnoremap a{ a[ + + " #9 + call setline('.', '{[foo]}') + let @@ = 'fail' + normal 0ffyib + call g:assert.equals(@@, 'foo', 'failed at #9') + + " #10 + call setline('.', '[{foo}]') + let @@ = 'fail' + normal 0ffyib + call g:assert.equals(@@, 'foo', 'failed at #10') +endfunction +"}}} + +function! s:suite.i_x_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'foo', 'failed at #1') + + " #2 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'foo', 'failed at #2') + + " #3 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'foo', 'failed at #3') + + " #4 + call setline('.', '') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'foo', 'failed at #4') + + " #5 + call setline('.', '"foo"') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'foo', 'failed at #5') + + " #6 + call setline('.', "'foo'") + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'foo', 'failed at #6') +endfunction +"}}} +function! s:suite.i_x_nest() abort "{{{ + " #1 + call setline('.', '()') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '(', 'failed at #1') + + " #2 + call setline('.', '(a)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'a', 'failed at #2') + + " #3 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #3') + + " #4 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0lviby + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #4') + + " #5 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #5') + + " #6 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 03lviby + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #6') + + " #7 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 04lviby + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #7') + + " #8 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 05lviby + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #8') + + " #9 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 06lviby + call g:assert.equals(@@, 'cc', 'failed at #9') + + " #10 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 07lviby + call g:assert.equals(@@, 'cc', 'failed at #10') + + " #11 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 08lviby + call g:assert.equals(@@, 'cc', 'failed at #11') + + " #12 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 09lviby + call g:assert.equals(@@, 'cc', 'failed at #12') + + " #13 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 010lviby + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #13') + + " #14 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 011lviby + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #14') + + " #15 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 012lviby + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #15') + + " #16 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 013lviby + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #16') + + " #17 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 014lviby + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #17') + + " #18 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 015lviby + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(((', ')))'], 'nesting': 1}] + + " #19 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #19') + + " #20 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0lviby + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #20') + + " #21 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #21') + + " #22 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 03lviby + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #22') + + " #23 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 04lviby + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #23') + + " #24 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 05lviby + call g:assert.equals(@@, 'bb', 'failed at #24') + + " #25 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 06lviby + call g:assert.equals(@@, 'bb', 'failed at #25') + + " #26 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 07lviby + call g:assert.equals(@@, 'bb', 'failed at #26') + + " #27 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 08lviby + call g:assert.equals(@@, 'bb', 'failed at #27') + + " #28 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 09lviby + call g:assert.equals(@@, 'bb', 'failed at #28') + + " #29 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 010lviby + call g:assert.equals(@@, 'bb', 'failed at #29') + + " #30 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 011lviby + call g:assert.equals(@@, 'bb', 'failed at #30') + + " #31 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 012lviby + call g:assert.equals(@@, 'bb', 'failed at #31') + + " #32 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 013lviby + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #32') + + " #33 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 014lviby + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #33') + + " #34 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 015lviby + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #34') + + " #35 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 016lviby + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #35') + + " #36 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 017lviby + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #36') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'nesting': 0}, {'external': ['it', 'at'], 'input': ['t']}] + + " #37 + call setline('.', 'Anchor Text') + let @@ = 'fail' + normal 0fwviby + call g:assert.equals(@@, 'http://www.url.com', 'failed at #37') +endfunction +"}}} +function! s:suite.i_x_no_nest() abort "{{{ + " #1 + call setline('.', '""') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '"', 'failed at #1') + + " #2 + call setline('.', '"a"') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'a', 'failed at #2') + + " #3 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #3') + + " #4 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0lviby + call g:assert.equals(@@, 'aa', 'failed at #4') + + " #5 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 03lviby + call g:assert.equals(@@, 'aa', 'failed at #6') + + " #7 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 04lviby + call g:assert.equals(@@, 'bb', 'failed at #7') + + " #8 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 05lviby + call g:assert.equals(@@, 'bb', 'failed at #8') + + " #9 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 06lviby + call g:assert.equals(@@, 'cc', 'failed at #9') + + " #10 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 07lviby + call g:assert.equals(@@, 'cc', 'failed at #10') + + " #11 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 08lviby + call g:assert.equals(@@, 'cc', 'failed at #11') + + " #12 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 09lviby + call g:assert.equals(@@, 'cc', 'failed at #12') + + " #13 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 010lviby + call g:assert.equals(@@, 'bb', 'failed at #13') + + " #14 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 011lviby + call g:assert.equals(@@, 'bb', 'failed at #14') + + " #15 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 012lviby + call g:assert.equals(@@, 'aa', 'failed at #15') + + " #16 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 013lviby + call g:assert.equals(@@, 'aa', 'failed at #16') + + " #17 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 014lviby + call g:assert.equals(@@, 'aa', 'failed at #17') + + " #18 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 015lviby + call g:assert.equals(@@, 'aa', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"""', '"""'], 'nesting': 0}] + + " #19 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #19') + + " #20 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0lviby + call g:assert.equals(@@, 'aa', 'failed at #20') + + " #21 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'aa', 'failed at #21') + + " #22 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 03lviby + call g:assert.equals(@@, 'aa', 'failed at #22') + + " #23 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 04lviby + call g:assert.equals(@@, 'aa', 'failed at #23') + + " #24 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 05lviby + call g:assert.equals(@@, 'aa', 'failed at #24') + + " #25 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 06lviby + call g:assert.equals(@@, 'aa', 'failed at #25') + + " #26 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 07lviby + call g:assert.equals(@@, 'aa', 'failed at #26') + + " #27 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 08lviby + call g:assert.equals(@@, 'bb', 'failed at #27') + + " #28 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 09lviby + call g:assert.equals(@@, 'bb', 'failed at #28') + + " #29 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 010lviby + call g:assert.equals(@@, 'cc', 'failed at #29') + + " #30 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 011lviby + call g:assert.equals(@@, 'cc', 'failed at #30') + + " #31 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 012lviby + call g:assert.equals(@@, 'cc', 'failed at #31') + + " #32 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 013lviby + call g:assert.equals(@@, 'cc', 'failed at #32') + + " #33 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 014lviby + call g:assert.equals(@@, 'cc', 'failed at #33') + + " #34 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 015lviby + call g:assert.equals(@@, 'cc', 'failed at #34') + + " #35 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 016lviby + call g:assert.equals(@@, 'cc', 'failed at #35') + + " #36 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 017lviby + call g:assert.equals(@@, 'cc', 'failed at #36') +endfunction +"}}} +function! s:suite.i_x_external_textobj() abort "{{{ + let g:textobj#sandwich#recipes = [{'external': ['it', 'at']}] + + " #1 + call setline('.', 'aabbaa') + let @@ = 'fail' + normal 0fbviby + call g:assert.equals(@@, 'bb', 'failed at #1') +endfunction +"}}} +function! s:suite.i_x_selected_area_extending() abort "{{{ + " #1 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcviby + call g:assert.equals(@@, 'cc', 'failed at #1') + + " #2 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvibiby + call g:assert.equals(@@, 'bb{cc}bb', 'failed at #2') + + " #3 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvibibiby + call g:assert.equals(@@, 'aa[bb{cc}bb]aa', 'failed at #3') + + %delete + + " #4 + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['if', 'endif'], 'nesting': 1}, + \ {'buns': ['if', 'else'], 'nesting': 1}, + \ {'buns': ['else', 'endif'], 'nesting': 1}, + \ ] + call append(0, ['if', ' foo', ' foo', 'else', ' bar', ' bar', 'endif']) + let @@ = 'fail' + normal ggviby + call g:assert.equals(@@, "\n foo\n foo\n", 'failed at #4') + + " #5 + let @@ = 'fail' + normal ggvibiby + call g:assert.equals(@@, "\n foo\n foo\nelse\n bar\n bar\n", 'failed at #5') + + " #6 + let @@ = 'fail' + normal 5Gviby + call g:assert.equals(@@, "\n bar\n bar\n", 'failed at #6') + + " #7 + let @@ = 'fail' + normal 5Gvibiby + call g:assert.equals(@@, "\n foo\n foo\nelse\n bar\n bar\n", 'failed at #7') +endfunction +"}}} +function! s:suite.i_x_blockwise_visual() abort "{{{ + " #1 + call append(0, ['( ', 'aa', ' )']) + let @@ = 'fail' + execute "normal gg\iby" + call g:assert.equals(@@, " \na\n ", 'failed at #1') + + %delete + + " #2 + call append(0, ['(aa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2jiby" + call g:assert.equals(@@, "aa\nbb\ncc", 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2joiby" + call g:assert.equals(@@, "aa\nbb\ncc", 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa)', '(bb)', '(ccc)']) + let @@ = 'fail' + execute "normal gg\2jiby" + call g:assert.equals(@@, "aa)\nbb)\nccc", 'failed at #4') + + %delete + + " #5 + call append(0, ['(aaa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2joiby" + call g:assert.equals(@@, "aaa\nbb)\ncc)", 'failed at #5') +endfunction +"}}} +function! s:suite.i_x_multibyte() abort "{{{ + let g:textobj#sandwich#recipes = [{'buns': ['α', 'α'], 'input': ['a']}] + + " #1 + call setline('.', 'aaαbbαaa') + let @@ = 'fail' + normal 0fbviby + call g:assert.equals(@@, 'bb', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['aα', 'aα'], 'input': ['a']}] + + " #2 + call setline('.', 'aaαbbaαa') + let @@ = 'fail' + normal 0fbviby + call g:assert.equals(@@, 'bb', 'failed at #2') +endfunction +"}}} +function! s:suite.i_x_option_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2']}] + + """ off + " #1 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #1') + + " #2 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '2', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2'], 'expr': 1}] + call setline('.', '2aa3') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #3') + + """ on + call textobj#sandwich#set('auto', 'expr', 1) + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2']}] + + " #4 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '1', 'failed at #4') + + " #5 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + let g:textobj#sandwich#recipes = [{'buns': ['SandwichExprEmpty()', '1+2']}] + call setline('.', '2aa3') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '2', 'failed at #6') + + " #7 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', 'SandwichExprEmpty()']}] + call setline('.', '2aa3') + let @@ = 'fail' + normal $viby + call g:assert.equals(@@, '3', 'failed at #7') + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2'], 'expr': 0}] + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #8') +endfunction +"}}} +function! s:suite.i_x_option_listexpr() abort "{{{ + let g:sandwich#recipes = [] + + """ on + call textobj#sandwich#set('auto', 'listexpr', 1) + " #1 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprBuns(0)'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'bar', 'failed at #1') + + " #2 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("former")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'f', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("latter")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'f', 'failed at #3') + + " #4 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("both")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'f', 'failed at #4') +endfunction +"}}} +function! s:suite.i_x_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #1') + + " #2 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '8', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + call setline('.', '888aa888') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #3') + + """ on + call textobj#sandwich#set('auto', 'regex', 1) + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #4 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '\', 'failed at #4') + + " #5 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #6') +endfunction +"}}} +function! s:suite.i_x_option_skip_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ off + " #1 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #1') + + " #2 + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_regex': ['a']}] + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'a', 'failed at #2') + + """ on + call textobj#sandwich#set('auto', 'skip_regex', ['aa']) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + " #3 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'fooa', 'failed at #3') + + """ head and tail + let g:textobj#sandwich#recipes = [{'buns': ["'", "'"]}] + call textobj#sandwich#set('auto', 'skip_regex_head', ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']']) + call textobj#sandwich#set('auto', 'skip_regex_tail', ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)''']) + " #4 + call setline('.', "'''foo'''") + let @@ = 'fail' + normal 0ffviby + call g:assert.equals(@@, "''foo''", 'failed at #4') +endfunction +"}}} +function! s:suite.i_x_option_quoteescape() abort "{{{ + """ off + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #1 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa\', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'quoteescape': 1}] + + " #2 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa\"bb', 'failed at #2') + + """ on + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + call textobj#sandwich#set('auto', 'quoteescape', 1) + + " #3 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa\"bb', 'failed at #3') +endfunction +"}}} +function! s:suite.i_x_option_expand_range() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + """ -1 + " #1 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #1') + + %delete + + " #2 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggviby + call g:assert.equals(@@, "\naa\n", 'failed at #2') + + %delete + + " #3 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggviby + call g:assert.equals(@@, "\naa\nbb\ncc\n", 'failed at #3') + + %delete + + " #4 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 0}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jviby + call g:assert.equals(@@, 'b', 'failed at #4') + + %delete + + """ 0 + call textobj#sandwich#set('auto', 'expand_range', 0) + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #5 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #5') + + %delete + + " #6 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggviby + call g:assert.equals(@@, '"', 'failed at #6') + + %delete + + " #7 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggviby + call g:assert.equals(@@, '"', 'failed at #7') + + %delete + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 1}] + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjviby + call g:assert.equals(@@, "\naa\n", 'failed at #8') + + %delete + + """ 1 + call textobj#sandwich#set('auto', 'expand_range', 1) + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #9 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'aa', 'failed at #9') + + %delete + + " #10 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjviby + call g:assert.equals(@@, "\naa\n", 'failed at #10') + + %delete + + " #11 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggviby + call g:assert.equals(@@, '"', 'failed at #11') + + %delete + + " #12 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': -1}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jviby + call g:assert.equals(@@, "\naa\nbb\ncc\n", 'failed at #12') +endfunction +"}}} +function! s:suite.i_x_option_noremap() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '(', 'failed at #1') + + " #2 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #3') + + " #4 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '{', 'failed at #4') + + """ off + call textobj#sandwich#set('auto', 'noremap', 0) + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '{', 'failed at #6') + + " #7 + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '(', 'failed at #7') + + " #8 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #8') +endfunction +"}}} +function! s:suite.i_x_option_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + call textobj#sandwich#set('auto', 'syntax', []) + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #1') + + call textobj#sandwich#set('auto', 'syntax', ['Special']) + syn match TestParen '[()]' + highlight link TestParen String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '(', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #3') + + call textobj#sandwich#set('auto', 'syntax', []) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'syntax': ['Special']}] + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #4') +endfunction +"}}} +function! s:suite.i_x_option_inner_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + call textobj#sandwich#set('auto', 'inner_syntax', []) + + " #1 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'bar', 'failed at #1') + + call textobj#sandwich#set('auto', 'inner_syntax', ['Special']) + syn match TestParen '[br]' + highlight link TestParen String + + " #2 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '(', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'bar', 'failed at #3') + + call textobj#sandwich#set('auto', 'inner_syntax', []) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'inner_syntax': ['Special']}] + + " #4 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'bar', 'failed at #4') +endfunction +"}}} +function! s:suite.i_x_option_match_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + + """ 0 (test recipe-local) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'match_syntax': 1}] + call textobj#sandwich#set('auto', 'match_syntax', 0) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '(', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #3') + + """ 1 + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}, {'buns': ['"', '"']}] + call textobj#sandwich#set('auto', 'match_syntax', 1) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '(', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #3') + + """ 2 + call textobj#sandwich#set('auto', 'match_syntax', 2) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '(', 'failed at #4') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '(', 'failed at #5') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #6 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #6') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #7 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '%s', 'failed at #7') + + """ 3 + call textobj#sandwich#set('auto', 'match_syntax', 3) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #8 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #8') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #9 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '(', 'failed at #9') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #10 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, 'foo', 'failed at #10') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #11 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, '%s', 'failed at #11') +endfunction +"}}} +function! s:suite.i_x_option_skip_break() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + + """ 0 + " #1 + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggviby + call g:assert.equals(@@, "\nfoo\n", 'failed at #1') + + %delete + + " #2 + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'skip_break': 1}] + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggviby + call g:assert.equals(@@, 'foo', 'failed at #2') + + %delete + + """ 1 + call textobj#sandwich#set('auto', 'skip_break', 1) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + + " #2 + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggviby + call g:assert.equals(@@, "foo", 'failed at #2') + + %delete + + " #3 + call append(0, [' (', ' foo', ' )']) + let @@ = 'fail' + normal ggviby + call g:assert.equals(@@, "foo", 'failed at #3') + + %delete + + " #4 + " do not skip when any line breaking is not included. + call setline('.', '( foo )') + let @@ = 'fail' + normal 0viby + call g:assert.equals(@@, " foo ", 'failed at #4') +endfunction +"}}} +function! s:suite.i_x_option_skip_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ expression + call textobj#sandwich#set('auto', 'skip_expr', ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']) + " #1 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'aaa', 'failed at #1') + + %delete + + """ funcref + call textobj#sandwich#set('auto', 'skip_expr', [function('SandwichSkipIntermediate')]) + " #2 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'aaa', 'failed at #2') + + """ recipe-local + call textobj#sandwich#set('auto', 'skip_expr', []) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_expr': ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']}] + " #3 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lviby + call g:assert.equals(@@, 'aaa', 'failed at #3') +endfunction +"}}} +function! s:suite.i_x_priority() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}] + + " #1 + call setline('.', '"aa(b"c)') + let @@ = 'fail' + normal 0fbviby + call g:assert.equals(@@, 'b"c', 'failed at #1') + + " #2 + call setline('.', '"aa(b"ccc)') + let @@ = 'fail' + normal 0fbviby + call g:assert.equals(@@, 'aa(b', 'failed at #2') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ["'", "'"]}, + \ {'buns': ["'", "'"], 'filetype': ['vim'], 'skip_regex': ['[^'']\%(''''\)*\zs''''', '[^'']\%(''''\)*''\zs''']} + \ ] + + " #3 + call setline('.', "'foo''bar'") + let @@ = 'fail' + normal 0ffviby + call g:assert.equals(@@, "foo", 'failed at #3') + + " #4 + set filetype=vim + call setline('.', "'foo''bar'") + let @@ = 'fail' + normal 0ffviby + call g:assert.equals(@@, "foo''bar", 'failed at #4') + + set filetype= + let g:textobj#sandwich#recipes = [ + \ {'buns': ['^', '$']}, + \ {'buns': ['^', '$'], 'regex': 1} + \ ] + + " #5 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0fbviby + call g:assert.equals(@@, 'oobarbaz', 'failed at #5') + + " #6 + call setline('.', 'foo^bar$baz') + let @@ = 'fail' + normal 0fbviby + call g:assert.equals(@@, 'bar', 'failed at #6') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['1+1', '1+1']}, + \ {'buns': ['1+1', '1+1'], 'expr': 1} + \ ] + + " #7 + call setline('.', '1+12foo21+1') + let @@ = 'fail' + normal 0ffviby + call g:assert.equals(@@, 'foo', 'failed at #7') + + " #8 + call setline('.', '21+1foo1+12') + let @@ = 'fail' + normal 0ffviby + call g:assert.equals(@@, 'foo', 'failed at #8') + + let g:textobj#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i{', 'a{'], 'noremap': 0} + \ ] + xnoremap i{ i[ + xnoremap a{ a[ + + " #9 + call setline('.', '{[foo]}') + let @@ = 'fail' + normal 0ffviby + call g:assert.equals(@@, 'foo', 'failed at #9') + + " #10 + call setline('.', '[{foo}]') + let @@ = 'fail' + normal 0ffviby + call g:assert.equals(@@, 'foo', 'failed at #10') +endfunction +"}}} + +function! s:suite.a_o_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, '(foo)', 'failed at #1') + + " #2 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, '[foo]', 'failed at #2') + + " #3 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, '{foo}', 'failed at #3') + + " #4 + call setline('.', '') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, '', 'failed at #4') + + " #5 + call setline('.', '"foo"') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, '"foo"', 'failed at #5') + + " #6 + call setline('.', "'foo'") + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, "'foo'", 'failed at #6') +endfunction +"}}} +function! s:suite.a_o_nest() abort "{{{ + " #1 + call setline('.', '()') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '()', 'failed at #1') + + " #2 + call setline('.', '(a)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(a)', 'failed at #2') + + " #3 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #3') + + " #4 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0lyab + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #4') + + " #5 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #5') + + " #6 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 03lyab + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #6') + + " #7 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 04lyab + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #7') + + " #8 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 05lyab + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #8') + + " #9 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 06lyab + call g:assert.equals(@@, '(cc)', 'failed at #9') + + " #10 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 07lyab + call g:assert.equals(@@, '(cc)', 'failed at #10') + + " #11 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 08lyab + call g:assert.equals(@@, '(cc)', 'failed at #11') + + " #12 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 09lyab + call g:assert.equals(@@, '(cc)', 'failed at #12') + + " #13 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 010lyab + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #13') + + " #14 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 011lyab + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #14') + + " #15 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 012lyab + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #15') + + " #16 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 013lyab + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #16') + + " #17 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 014lyab + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #17') + + " #18 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 015lyab + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(((', ')))'], 'nesting': 1}] + + " #19 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #19') + + " #20 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0lyab + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #20') + + " #21 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #21') + + " #22 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 03lyab + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #22') + + " #23 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 04lyab + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #23') + + " #24 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 05lyab + call g:assert.equals(@@, '(((bb)))', 'failed at #24') + + " #25 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 06lyab + call g:assert.equals(@@, '(((bb)))', 'failed at #25') + + " #26 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 07lyab + call g:assert.equals(@@, '(((bb)))', 'failed at #26') + + " #27 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 08lyab + call g:assert.equals(@@, '(((bb)))', 'failed at #27') + + " #28 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 09lyab + call g:assert.equals(@@, '(((bb)))', 'failed at #28') + + " #29 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 010lyab + call g:assert.equals(@@, '(((bb)))', 'failed at #29') + + " #30 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 011lyab + call g:assert.equals(@@, '(((bb)))', 'failed at #30') + + " #31 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 012lyab + call g:assert.equals(@@, '(((bb)))', 'failed at #31') + + " #32 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 013lyab + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #32') + + " #33 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 014lyab + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #33') + + " #34 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 015lyab + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #34') + + " #35 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 016lyab + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #35') + + " #36 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 017lyab + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #36') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'nesting': 0}, {'external': ['it', 'at'], 'input': ['t']}] + + " #37 + call setline('.', 'Anchor Text') + let @@ = 'fail' + normal 0fwyab + call g:assert.equals(@@, '"http://www.url.com"', 'failed at #37') +endfunction +"}}} +function! s:suite.a_o_no_nest() abort "{{{ + " #1 + call setline('.', '""') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '""', 'failed at #1') + + " #2 + call setline('.', '"a"') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '"a"', 'failed at #2') + + " #3 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '"aa"', 'failed at #3') + + " #4 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0lyab + call g:assert.equals(@@, '"aa"', 'failed at #4') + + " #5 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, '"aa"', 'failed at #5') + + " #6 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 03lyab + call g:assert.equals(@@, '"aa"', 'failed at #6') + + " #7 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 04lyab + call g:assert.equals(@@, '"bb"', 'failed at #7') + + " #8 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 05lyab + call g:assert.equals(@@, '"bb"', 'failed at #8') + + " #9 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 06lyab + call g:assert.equals(@@, '"cc"', 'failed at #9') + + " #10 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 07lyab + call g:assert.equals(@@, '"cc"', 'failed at #10') + + " #11 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 08lyab + call g:assert.equals(@@, '"cc"', 'failed at #11') + + " #12 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 09lyab + call g:assert.equals(@@, '"cc"', 'failed at #12') + + " #13 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 010lyab + call g:assert.equals(@@, '"bb"', 'failed at #13') + + " #14 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 011lyab + call g:assert.equals(@@, '"bb"', 'failed at #14') + + " #15 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 012lyab + call g:assert.equals(@@, '"aa"', 'failed at #15') + + " #16 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 013lyab + call g:assert.equals(@@, '"aa"', 'failed at #16') + + " #17 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 014lyab + call g:assert.equals(@@, '"aa"', 'failed at #17') + + " #18 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 015lyab + call g:assert.equals(@@, '"aa"', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"""', '"""'], 'nesting': 0}] + + " #19 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '"""aa"""', 'failed at #19') + + " #20 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0lyab + call g:assert.equals(@@, '"""aa"""', 'failed at #20') + + " #21 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, '"""aa"""', 'failed at #21') + + " #22 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 03lyab + call g:assert.equals(@@, '"""aa"""', 'failed at #22') + + " #23 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 04lyab + call g:assert.equals(@@, '"""aa"""', 'failed at #23') + + " #24 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 05lyab + call g:assert.equals(@@, '"""aa"""', 'failed at #24') + + " #25 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 06lyab + call g:assert.equals(@@, '"""aa"""', 'failed at #25') + + " #26 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 07lyab + call g:assert.equals(@@, '"""aa"""', 'failed at #26') + + " #27 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 08lyab + call g:assert.equals(@@, '"""bb"""', 'failed at #27') + + " #28 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 09lyab + call g:assert.equals(@@, '"""bb"""', 'failed at #28') + + " #29 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 010lyab + call g:assert.equals(@@, '"""cc"""', 'failed at #29') + + " #30 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 011lyab + call g:assert.equals(@@, '"""cc"""', 'failed at #30') + + " #31 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 012lyab + call g:assert.equals(@@, '"""cc"""', 'failed at #31') + + " #32 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 013lyab + call g:assert.equals(@@, '"""cc"""', 'failed at #32') + + " #33 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 014lyab + call g:assert.equals(@@, '"""cc"""', 'failed at #33') + + " #34 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 015lyab + call g:assert.equals(@@, '"""cc"""', 'failed at #34') + + " #35 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 016lyab + call g:assert.equals(@@, '"""cc"""', 'failed at #35') + + " #36 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 017lyab + call g:assert.equals(@@, '"""cc"""', 'failed at #36') +endfunction +"}}} +function! s:suite.a_o_external_textobj() abort "{{{ + let g:textobj#sandwich#recipes = [{'external': ['it', 'at']}] + + " #1 + call setline('.', 'aabbaa') + let @@ = 'fail' + normal 0fbyab + call g:assert.equals(@@, 'bb', 'failed at #1') +endfunction +"}}} +function! s:suite.a_o_multibyte() abort "{{{ + let g:textobj#sandwich#recipes = [{'buns': ['α', 'α'], 'input': ['a']}] + + " #1 + call setline('.', 'aaαbbαaa') + let @@ = 'fail' + normal 0fbyab + call g:assert.equals(@@, 'αbbα', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['aα', 'aα'], 'input': ['a']}] + + " #2 + call setline('.', 'aaαbbaαa') + let @@ = 'fail' + normal 0fbyab + call g:assert.equals(@@, 'aαbbaα', 'failed at #2') +endfunction +"}}} +function! s:suite.a_o_option_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2']}] + + """ off + " #1 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '1+1aa1+2', 'failed at #1') + + " #2 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2'], 'expr': 1}] + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '2aa3', 'failed at #3') + + """ on + call textobj#sandwich#set('auto', 'expr', 1) + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2']}] + + " #4 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #4') + + " #5 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '2aa3', 'failed at #5') + + " #6 + let g:textobj#sandwich#recipes = [{'buns': ['SandwichExprEmpty()', '1+2']}] + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #6') + + " #7 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', 'SandwichExprEmpty()']}] + call setline('.', '2aa3') + let @@ = 'fail' + normal $yab + call g:assert.equals(@@, '', 'failed at #7') + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2'], 'expr': 0}] + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '1+1aa1+2', 'failed at #8') +endfunction +"}}} +function! s:suite.a_o_option_listexpr() abort "{{{ + let g:sandwich#recipes = [] + + """ on + call textobj#sandwich#set('auto', 'listexpr', 1) + " #1 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprBuns(0)'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, 'foobarbaz', 'failed at #1') + + " #2 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("former")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("latter")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #3') + + " #4 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("both")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #4') +endfunction +"}}} +function! s:suite.a_o_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '\d\+aa\d\+', 'failed at #1') + + " #2 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '888aa888', 'failed at #3') + + """ on + call textobj#sandwich#set('auto', 'regex', 1) + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + " #4 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #4') + + " #5 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '888aa888', 'failed at #5') + + " #6 + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '\d\+aa\d\+', 'failed at #6') +endfunction +"}}} +function! s:suite.a_o_option_skip_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ off + " #1 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, 'afooa', 'failed at #1') + + " #2 + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_regex': ['a']}] + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #2') + + """ on + call textobj#sandwich#set('auto', 'skip_regex', ['aa']) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + " #3 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, 'afooaa', 'failed at #3') + + """ head and tail + let g:textobj#sandwich#recipes = [{'buns': ["'", "'"]}] + call textobj#sandwich#set('auto', 'skip_regex_head', ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']']) + call textobj#sandwich#set('auto', 'skip_regex_tail', ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)''']) + " #4 + call setline('.', "'''foo'''") + let @@ = 'fail' + normal 0ffyab + call g:assert.equals(@@, "'''foo'''", 'failed at #4') +endfunction +"}}} +function! s:suite.a_o_option_quoteescape() abort "{{{ + """ off + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #1 + " call setline('.', '"aa\"bb"') + " let @@ = 'fail' + " normal 0yab + " call g:assert.equals(@@, '"aa\"', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'quoteescape': 1}] + + " #2 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '"aa\"bb"', 'failed at #2') + + """ on + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + call textobj#sandwich#set('auto', 'quoteescape', 1) + + " #3 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '"aa\"bb"', 'failed at #3') +endfunction +"}}} +function! s:suite.a_o_option_expand_range() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + """ -1 + " #1 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '"aa"', 'failed at #1') + + %delete + + " #2 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggyab + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #2') + + %delete + + " #3 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggyab + call g:assert.equals(@@, "\"\naa\nbb\ncc\n\"", 'failed at #3') + + %delete + + " #4 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 0}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyab + call g:assert.equals(@@, '', 'failed at #4') + + %delete + + """ 0 + call textobj#sandwich#set('auto', 'expand_range', 0) + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #5 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '"aa"', 'failed at #5') + + %delete + + " #6 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggyab + call g:assert.equals(@@, '', 'failed at #6') + + %delete + + " #7 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggyab + call g:assert.equals(@@, '', 'failed at #7') + + %delete + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 1}] + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjyab + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #8') + + %delete + + """ 1 + call textobj#sandwich#set('auto', 'expand_range', 1) + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #9 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '"aa"', 'failed at #9') + + %delete + + " #10 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjyab + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #10') + + %delete + + " #11 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggyab + call g:assert.equals(@@, '', 'failed at #11') + + %delete + + " #12 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': -1}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyab + call g:assert.equals(@@, "\"\naa\nbb\ncc\n\"", 'failed at #12') +endfunction +"}}} +function! s:suite.a_o_option_noremap() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #1') + + " #2 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '{foo}', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #3') + + " #4 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #4') + + """ off + call textobj#sandwich#set('auto', 'noremap', 0) + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #6') + + " #7 + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #7') + + " #8 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '{foo}', 'failed at #8') +endfunction +"}}} +function! s:suite.a_o_option_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + call textobj#sandwich#set('auto', 'syntax', []) + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #1') + + call textobj#sandwich#set('auto', 'syntax', ['Special']) + syn match TestParen '[()]' + highlight link TestParen String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #3') + + call textobj#sandwich#set('auto', 'syntax', []) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'syntax': ['Special']}] + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #4') +endfunction +"}}} +function! s:suite.a_o_option_inner_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + call textobj#sandwich#set('auto', 'inner_syntax', []) + + " #1 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(bar)', 'failed at #1') + + call textobj#sandwich#set('auto', 'inner_syntax', ['Special']) + syn match TestParen '[br]' + highlight link TestParen String + + " #2 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(bar)', 'failed at #3') + + call textobj#sandwich#set('auto', 'inner_syntax', []) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'inner_syntax': ['Special']}] + + " #4 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(bar)', 'failed at #4') +endfunction +"}}} +function! s:suite.a_o_option_match_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + + """ 0 (test recipe-local) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'match_syntax': 1}] + call textobj#sandwich#set('auto', 'match_syntax', 0) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #3') + + """ 1 + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}, {'buns': ['"', '"']}] + call textobj#sandwich#set('auto', 'match_syntax', 1) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #4') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #5') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #6 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #6') + + """ 2 + call textobj#sandwich#set('auto', 'match_syntax', 2) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #7 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #7') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #8 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #8') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #9 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #9') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #10 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '"%s"', 'failed at #10') + + """ 3 + call textobj#sandwich#set('auto', 'match_syntax', 3) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #11 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #11') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #12 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '', 'failed at #12') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #13 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '(foo)', 'failed at #13') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #14 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0yab + call g:assert.equals(@@, '"%s"', 'failed at #14') +endfunction +"}}} +function! s:suite.a_o_option_synchro() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'nesting': 1}] + let g:operator#sandwich#recipes = [] + call textobj#sandwich#set('auto', 'synchro', 1) + nmap sd (operator-sandwich-delete) + + " #1 + call setline('.', '(foo)') + normal 0sdab + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', '((foo))') + normal 0ff2sd2ab + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'synchro': 1, 'nesting': 1}] + call textobj#sandwich#set('auto', 'synchro', 0) + + " #3 + call setline('.', '(foo)') + normal 0sdab + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + + " #4 + call setline('.', '((foo))') + normal 0ff2sd2ab + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'external': ['it', 'at']}] + let g:operator#sandwich#recipes = [] + call textobj#sandwich#set('auto', 'synchro', 1) + + " #5 + call setline('.', 'foo') + normal 0sdab + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + + " #6 + call setline('.', 'foo') + normal 0ff2sd2ab + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + let g:textobj#sandwich#recipes = [{'external': ['it', 'at'], 'synchro': 1}] + call textobj#sandwich#set('auto', 'synchro', 0) + + " #7 + call setline('.', 'foo') + normal 0sdab + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + + " #8 + call setline('.', 'foo') + normal 0ff2sd2ab + call g:assert.equals(getline('.'), 'foo', 'failed at #8') + + let g:sandwich#recipes = [{'buns': ['(', ')'], 'nesting': 1}, {'buns': ["'", "'"], 'nesting': 0}] + let g:textobj#sandwich#recipes = [] + let g:operator#sandwich#recipes = [] + call textobj#sandwich#set('auto', 'synchro', 1) + nmap sdb (operator-sandwich-delete)(operator-sandwich-synchro-count)(textobj-sandwich-auto-a) + + " #9 + call setline('.', "(a'b((c))b'a)") + normal 0fc3sdb + call g:assert.equals(getline('.'), '(ab((c))ba)', 'failed at #9') + + call operator#sandwich#set('all', 'all', 'skip_char', 1) + + " #10 + call setline('.', "(a'b((c))b'a)") + normal 0fc3sdb + call g:assert.equals(getline('.'), '(abcba)', 'failed at #10') +endfunction +"}}} +function! s:suite.a_o_option_skip_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ expression + call textobj#sandwich#set('auto', 'skip_expr', ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']) + " #1 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, 'aaaaa', 'failed at #1') + + %delete + + """ funcref + call textobj#sandwich#set('auto', 'skip_expr', [function('SandwichSkipIntermediate')]) + " #2 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, 'aaaaa', 'failed at #2') + + %delete + + """ recipe-local + call textobj#sandwich#set('auto', 'skip_expr', []) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_expr': ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']}] + " #3 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyab + call g:assert.equals(@@, 'aaaaa', 'failed at #3') +endfunction +"}}} +function! s:suite.a_o_priority() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(((', ')))']}, {'buns': ['(', ')']}] + + " #1 + call setline('.', '"aa(b"c)') + let @@ = 'fail' + normal 0fbyab + call g:assert.equals(@@, '(b"c)', 'failed at #1') + + " #2 + call setline('.', '"aa(b"ccc)') + let @@ = 'fail' + normal 0fbyab + call g:assert.equals(@@, '"aa(b"', 'failed at #2') + + " #3 + call setline('.', '(((foo)))') + let @@ = 'fail' + normal 0ffyab + call g:assert.equals(@@, '(foo)', 'failed at #3') + + " #4 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}, {'buns': ['(((', ')))']}] + call setline('.', '(((foo)))') + let @@ = 'fail' + normal 0ffyab + call g:assert.equals(@@, '(((foo)))', 'failed at #4') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ["'", "'"]}, + \ {'buns': ["'", "'"], 'filetype': ['vim'], 'skip_regex': ['[^'']\%(''''\)*\zs''''', '[^'']\%(''''\)*''\zs''']} + \ ] + + " #5 + call setline('.', "'foo''bar'") + let @@ = 'fail' + normal 0ffyab + call g:assert.equals(@@, "'foo'", 'failed at #5') + + " #6 + set filetype=vim + call setline('.', "'foo''bar'") + let @@ = 'fail' + normal 0ffyab + call g:assert.equals(@@, "'foo''bar'", 'failed at #6') + + set filetype= + let g:textobj#sandwich#recipes = [ + \ {'buns': ['^', '$']}, + \ {'buns': ['^', '$'], 'regex': 1} + \ ] + + " #7 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0fbyab + call g:assert.equals(@@, 'foobarbaz', 'failed at #7') + + " #8 + call setline('.', 'foo^bar$baz') + let @@ = 'fail' + normal 0fbyab + call g:assert.equals(@@, '^bar$', 'failed at #8') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['1+1', '1+1']}, + \ {'buns': ['1+1', '1+1'], 'expr': 1} + \ ] + + " #9 + call setline('.', '1+12foo21+1') + let @@ = 'fail' + normal 0ffyab + call g:assert.equals(@@, '2foo2', 'failed at #9') + + " #10 + call setline('.', '21+1foo1+12') + let @@ = 'fail' + normal 0ffyab + call g:assert.equals(@@, '1+1foo1+1', 'failed at #10') + + let g:textobj#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i{', 'a{'], 'noremap': 0} + \ ] + xnoremap i{ i[ + xnoremap a{ a[ + + " #11 + call setline('.', '{[foo]}') + let @@ = 'fail' + normal 0ffyab + call g:assert.equals(@@, '[foo]', 'failed at #11') + + " #12 + call setline('.', '[{foo}]') + let @@ = 'fail' + normal 0ffyab + call g:assert.equals(@@, '{foo}', 'failed at #12') +endfunction +"}}} + +function! s:suite.a_x_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, '(foo)', 'failed at #1') + + " #2 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, '[foo]', 'failed at #2') + + " #3 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, '{foo}', 'failed at #3') + + " #4 + call setline('.', '') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, '', 'failed at #4') + + " #5 + call setline('.', '"foo"') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, '"foo"', 'failed at #5') + + " #6 + call setline('.', "'foo'") + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, "'foo'", 'failed at #6') +endfunction +"}}} +function! s:suite.a_x_nest() abort "{{{ + " #1 + call setline('.', '()') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '()', 'failed at #1') + + " #2 + call setline('.', '(a)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(a)', 'failed at #2') + + " #3 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #3') + + " #4 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0lvaby + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #4') + + " #5 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #5') + + " #6 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 03lvaby + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #6') + + " #7 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 04lvaby + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #7') + + " #8 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 05lvaby + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #8') + + " #9 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 06lvaby + call g:assert.equals(@@, '(cc)', 'failed at #9') + + " #10 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 07lvaby + call g:assert.equals(@@, '(cc)', 'failed at #10') + + " #11 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 08lvaby + call g:assert.equals(@@, '(cc)', 'failed at #11') + + " #12 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 09lvaby + call g:assert.equals(@@, '(cc)', 'failed at #12') + + " #13 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 010lvaby + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #13') + + " #14 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 011lvaby + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #14') + + " #15 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 012lvaby + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #15') + + " #16 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 013lvaby + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #16') + + " #17 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 014lvaby + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #17') + + " #18 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 015lvaby + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(((', ')))'], 'nesting': 1}] + + " #19 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #19') + + " #20 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0lvaby + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #20') + + " #21 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #21') + + " #22 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 03lvaby + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #22') + + " #23 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 04lvaby + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #23') + + " #24 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 05lvaby + call g:assert.equals(@@, '(((bb)))', 'failed at #24') + + " #25 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 06lvaby + call g:assert.equals(@@, '(((bb)))', 'failed at #25') + + " #26 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 07lvaby + call g:assert.equals(@@, '(((bb)))', 'failed at #26') + + " #27 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 08lvaby + call g:assert.equals(@@, '(((bb)))', 'failed at #27') + + " #28 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 09lvaby + call g:assert.equals(@@, '(((bb)))', 'failed at #28') + + " #29 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 010lvaby + call g:assert.equals(@@, '(((bb)))', 'failed at #29') + + " #30 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 011lvaby + call g:assert.equals(@@, '(((bb)))', 'failed at #30') + + " #31 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 012lvaby + call g:assert.equals(@@, '(((bb)))', 'failed at #31') + + " #32 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 013lvaby + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #32') + + " #33 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 014lvaby + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #33') + + " #34 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 015lvaby + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #34') + + " #35 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 016lvaby + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #35') + + " #36 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 017lvaby + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #36') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'nesting': 0}, {'external': ['it', 'at'], 'input': ['t']}] + + " #37 + call setline('.', 'Anchor Text') + let @@ = 'fail' + normal 0fwvaby + call g:assert.equals(@@, '"http://www.url.com"', 'failed at #37') +endfunction +"}}} +function! s:suite.a_x_no_nest() abort "{{{ + " #1 + call setline('.', '""') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '""', 'failed at #1') + + " #2 + call setline('.', '"a"') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"a"', 'failed at #2') + + " #3 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"aa"', 'failed at #3') + + " #4 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0lvaby + call g:assert.equals(@@, '"aa"', 'failed at #4') + + " #5 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, '"aa"', 'failed at #5') + + " #6 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 03lvaby + call g:assert.equals(@@, '"aa"', 'failed at #6') + + " #7 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 04lvaby + call g:assert.equals(@@, '"bb"', 'failed at #7') + + " #8 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 05lvaby + call g:assert.equals(@@, '"bb"', 'failed at #8') + + " #9 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 06lvaby + call g:assert.equals(@@, '"cc"', 'failed at #9') + + " #10 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 07lvaby + call g:assert.equals(@@, '"cc"', 'failed at #10') + + " #11 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 08lvaby + call g:assert.equals(@@, '"cc"', 'failed at #11') + + " #12 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 09lvaby + call g:assert.equals(@@, '"cc"', 'failed at #12') + + " #13 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 010lvaby + call g:assert.equals(@@, '"bb"', 'failed at #13') + + " #14 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 011lvaby + call g:assert.equals(@@, '"bb"', 'failed at #14') + + " #15 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 012lvaby + call g:assert.equals(@@, '"aa"', 'failed at #15') + + " #16 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 013lvaby + call g:assert.equals(@@, '"aa"', 'failed at #16') + + " #17 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 014lvaby + call g:assert.equals(@@, '"aa"', 'failed at #17') + + " #18 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 015lvaby + call g:assert.equals(@@, '"aa"', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"""', '"""'], 'nesting': 0}] + + " #19 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"""aa"""', 'failed at #19') + + " #20 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0lvaby + call g:assert.equals(@@, '"""aa"""', 'failed at #20') + + " #21 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, '"""aa"""', 'failed at #21') + + " #22 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 03lvaby + call g:assert.equals(@@, '"""aa"""', 'failed at #22') + + " #23 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 04lvaby + call g:assert.equals(@@, '"""aa"""', 'failed at #23') + + " #24 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 05lvaby + call g:assert.equals(@@, '"""aa"""', 'failed at #24') + + " #25 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 06lvaby + call g:assert.equals(@@, '"""aa"""', 'failed at #25') + + " #26 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 07lvaby + call g:assert.equals(@@, '"""aa"""', 'failed at #26') + + " #27 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 08lvaby + call g:assert.equals(@@, '"""bb"""', 'failed at #27') + + " #28 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 09lvaby + call g:assert.equals(@@, '"""bb"""', 'failed at #28') + + " #29 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 010lvaby + call g:assert.equals(@@, '"""cc"""', 'failed at #29') + + " #30 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 011lvaby + call g:assert.equals(@@, '"""cc"""', 'failed at #30') + + " #31 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 012lvaby + call g:assert.equals(@@, '"""cc"""', 'failed at #31') + + " #32 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 013lvaby + call g:assert.equals(@@, '"""cc"""', 'failed at #32') + + " #33 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 014lvaby + call g:assert.equals(@@, '"""cc"""', 'failed at #33') + + " #34 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 015lvaby + call g:assert.equals(@@, '"""cc"""', 'failed at #34') + + " #35 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 016lvaby + call g:assert.equals(@@, '"""cc"""', 'failed at #35') + + " #36 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 017lvaby + call g:assert.equals(@@, '"""cc"""', 'failed at #36') +endfunction +"}}} +function! s:suite.a_x_external_textobj() abort "{{{ + let g:textobj#sandwich#recipes = [] + + " #1 + call setline('.', 'aabbaa') + let @@ = 'fail' + normal 0fbvaby + call g:assert.equals(@@, 'bb', 'failed at #1') +endfunction +"}}} +function! s:suite.a_x_selected_area_extending() abort "{{{ + " #1 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvaby + call g:assert.equals(@@, '{cc}', 'failed at #1') + + " #2 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvababy + call g:assert.equals(@@, '[bb{cc}bb]', 'failed at #2') + + " #3 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvabababy + call g:assert.equals(@@, '(aa[bb{cc}bb]aa)', 'failed at #3') + + %delete + + " #4 + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['if', 'endif'], 'nesting': 1}, + \ {'buns': ['if', 'else'], 'nesting': 1}, + \ {'buns': ['else', 'endif'], 'nesting': 1}, + \ ] + call append(0, ['if', ' foo', ' foo', 'else', ' bar', ' bar', 'endif']) + let @@ = 'fail' + normal ggvaby + call g:assert.equals(@@, "if\n foo\n foo\nelse", 'failed at #4') + + " #5 + let @@ = 'fail' + normal ggvababy + call g:assert.equals(@@, "if\n foo\n foo\nelse\n bar\n bar\nendif", 'failed at #5') + + " #6 + let @@ = 'fail' + normal 5Gvaby + call g:assert.equals(@@, "else\n bar\n bar\nendif", 'failed at #6') + + " #7 + let @@ = 'fail' + normal 5Gvababy + call g:assert.equals(@@, "if\n foo\n foo\nelse\n bar\n bar\nendif", 'failed at #7') +endfunction +"}}} +function! s:suite.a_x_blockwise_visual() abort "{{{ + " #1 + call append(0, ['( ', 'aa', ' )']) + let @@ = 'fail' + execute "normal gg\aby" + call g:assert.equals(@@, "( \naa\n )", 'failed at #1') + + %delete + + " #2 + call append(0, ['(aa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2jaby" + call g:assert.equals(@@, "(aa)\n(bb)\n(cc)", 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2joaby" + call g:assert.equals(@@, "(aa)\n(bb)\n(cc)", 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa)', '(bb)', '(ccc)']) + let @@ = 'fail' + execute "normal gg\2jaby" + call g:assert.equals(@@, "(aa)\n(bb)\n(ccc)", 'failed at #4') + + %delete + + " #5 + call append(0, ['(aaa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2joaby" + call g:assert.equals(@@, "(aaa)\n(bb)\n(cc)", 'failed at #5') +endfunction +"}}} +function! s:suite.a_x_multibyte() abort "{{{ + let g:textobj#sandwich#recipes = [{'buns': ['α', 'α'], 'input': ['a']}] + + " #1 + call setline('.', 'aaαbbαaa') + let @@ = 'fail' + normal 0fbvaby + call g:assert.equals(@@, 'αbbα', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['aα', 'aα'], 'input': ['a']}] + + " #2 + call setline('.', 'aaαbbaαa') + let @@ = 'fail' + normal 0fbvaby + call g:assert.equals(@@, 'aαbbaα', 'failed at #2') +endfunction +"}}} +function! s:suite.a_x_option_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2']}] + + """ off + " #1 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '1+1aa1+2', 'failed at #1') + + " #2 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '2', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2'], 'expr': 1}] + call setline('.', '2aa3') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '2aa3', 'failed at #3') + + """ on + call textobj#sandwich#set('auto', 'expr', 1) + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2']}] + + " #4 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '1', 'failed at #4') + + " #5 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '2aa3', 'failed at #5') + + " #6 + let g:textobj#sandwich#recipes = [{'buns': ['SandwichExprEmpty()', '1+2']}] + call setline('.', '2aa3') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '2', 'failed at #6') + + " #7 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', 'SandwichExprEmpty()']}] + call setline('.', '2aa3') + let @@ = 'fail' + normal $vaby + call g:assert.equals(@@, '3', 'failed at #7') + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['1+1', '1+2'], 'expr': 0}] + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '1+1aa1+2', 'failed at #8') +endfunction +"}}} +function! s:suite.a_x_option_listexpr() abort "{{{ + let g:sandwich#recipes = [] + + """ on + call textobj#sandwich#set('auto', 'listexpr', 1) + " #1 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprBuns(0)'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, 'foobarbaz', 'failed at #1') + + " #2 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("former")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, 'f', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("latter")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, 'f', 'failed at #3') + + " #4 + let g:textobj#sandwich#recipes = [{'buns': 'SandwichListexprEmpty("both")'}] + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, 'f', 'failed at #4') +endfunction +"}}} +function! s:suite.a_x_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+']}] + + """ off + " #1 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '\d\+aa\d\+', 'failed at #1') + + " #2 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '8', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 1}] + call setline('.', '888aa888') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '888aa888', 'failed at #3') + + """ on + call textobj#sandwich#set('auto', 'regex', 1) + " #4 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '\', 'failed at #4') + + " #5 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '888aa888', 'failed at #5') + + " #6 + let g:textobj#sandwich#recipes = [{'buns': ['\d\+', '\d\+'], 'regex': 0}] + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '\d\+aa\d\+', 'failed at #6') +endfunction +"}}} +function! s:suite.a_x_option_skip_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ off + " #1 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, 'afooa', 'failed at #1') + + " #2 + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_regex': ['a']}] + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, 'a', 'failed at #2') + + """ on + call textobj#sandwich#set('auto', 'skip_regex', ['aa']) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + " #3 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, 'afooaa', 'failed at #3') + + """ head and tail + let g:textobj#sandwich#recipes = [{'buns': ["'", "'"]}] + call textobj#sandwich#set('auto', 'skip_regex_head', ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']']) + call textobj#sandwich#set('auto', 'skip_regex_tail', ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)''']) + " #4 + call setline('.', "'''foo'''") + let @@ = 'fail' + normal 0ffvaby + call g:assert.equals(@@, "'''foo'''", 'failed at #4') +endfunction +"}}} +function! s:suite.a_x_option_quoteescape() abort "{{{ + """ off + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #2 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"aa\"', 'failed at #2') + + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'quoteescape': 1}] + + " #3 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"aa\"bb"', 'failed at #3') + + """ on + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + call textobj#sandwich#set('auto', 'quoteescape', 1) + + " #1 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"aa\"bb"', 'failed at #1') +endfunction +"}}} +function! s:suite.a_x_option_expand_range() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + """ -1 + " #1 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"aa"', 'failed at #1') + + %delete + + " #2 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggvaby + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #2') + + %delete + + " #3 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggvaby + call g:assert.equals(@@, "\"\naa\nbb\ncc\n\"", 'failed at #3') + + %delete + + " #4 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 0}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jvaby + call g:assert.equals(@@, 'b', 'failed at #4') + + %delete + + """ 0 + call textobj#sandwich#set('auto', 'expand_range', 0) + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #5 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"aa"', 'failed at #5') + + %delete + + " #6 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggvaby + call g:assert.equals(@@, '"', 'failed at #6') + + %delete + + " #7 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggvaby + call g:assert.equals(@@, '"', 'failed at #7') + + %delete + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 1}] + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjvaby + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #8') + + %delete + + """ 1 + call textobj#sandwich#set('auto', 'expand_range', 1) + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #9 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"aa"', 'failed at #9') + + %delete + + " #10 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjvaby + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #10') + + %delete + + " #11 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggvaby + call g:assert.equals(@@, '"', 'failed at #11') + + %delete + + " #12 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': -1}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jvaby + call g:assert.equals(@@, "\"\naa\nbb\ncc\n\"", 'failed at #12') +endfunction +"}}} +function! s:suite.a_x_option_noremap() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{']}] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(', 'failed at #1') + + " #2 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '{foo}', 'failed at #2') + + " #3 + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 0}] + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #3') + + " #4 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '{', 'failed at #4') + + """ off + call textobj#sandwich#set('auto', 'noremap', 0) + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{']}] + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '{', 'failed at #6') + + " #7 + let g:textobj#sandwich#recipes = [{'external': ['i{', 'a{'], 'noremap': 1}] + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(', 'failed at #7') + + " #8 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '{foo}', 'failed at #8') +endfunction +"}}} +function! s:suite.a_x_option_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + call textobj#sandwich#set('auto', 'syntax', []) + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #1') + + call textobj#sandwich#set('auto', 'syntax', ['Special']) + syn match TestParen '[()]' + highlight link TestParen String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #3') + + call textobj#sandwich#set('auto', 'syntax', []) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'syntax': ['Special']}] + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #4') +endfunction +"}}} +function! s:suite.a_x_option_inner_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + call textobj#sandwich#set('auto', 'inner_syntax', []) + + " #1 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(bar)', 'failed at #1') + + call textobj#sandwich#set('auto', 'inner_syntax', ['Special']) + syn match TestParen '[br]' + highlight link TestParen String + + " #2 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(bar)', 'failed at #3') + + call textobj#sandwich#set('auto', 'inner_syntax', []) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'inner_syntax': ['Special']}] + + " #4 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(bar)', 'failed at #4') +endfunction +"}}} +function! s:suite.a_x_option_match_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + + """ 0 (test recipe-local) + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'match_syntax': 1}] + call textobj#sandwich#set('auto', 'match_syntax', 0) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #3') + + """ 1 + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}, {'buns': ['"', '"']}] + call textobj#sandwich#set('auto', 'match_syntax', 1) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #3') + + """ 2 + call textobj#sandwich#set('auto', 'match_syntax', 2) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(', 'failed at #4') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(', 'failed at #5') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #6 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #6') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #7 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"%s"', 'failed at #7') + + """ 3 + call textobj#sandwich#set('auto', 'match_syntax', 3) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #8 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #8') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #9 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(', 'failed at #9') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #10 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '(foo)', 'failed at #10') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #11 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0vaby + call g:assert.equals(@@, '"%s"', 'failed at #11') +endfunction +"}}} +function! s:suite.a_x_option_skip_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ expression + call textobj#sandwich#set('auto', 'skip_expr', ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']) + " #1 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, 'aaaaa', 'failed at #1') + + %delete + + """ funcref + call textobj#sandwich#set('auto', 'skip_expr', [function('SandwichSkipIntermediate')]) + " #2 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, 'aaaaa', 'failed at #2') + + %delete + + """ recipe-local + call textobj#sandwich#set('auto', 'skip_expr', []) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_expr': ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']}] + " #3 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lvaby + call g:assert.equals(@@, 'aaaaa', 'failed at #3') +endfunction +"}}} +function! s:suite.a_x_priority() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(((', ')))']}, {'buns': ['(', ')']}] + + " #1 + call setline('.', '"aa(b"c)') + let @@ = 'fail' + normal 0fbvaby + call g:assert.equals(@@, '(b"c)', 'failed at #1') + + " #2 + call setline('.', '"aa(b"ccc)') + let @@ = 'fail' + normal 0fbvaby + call g:assert.equals(@@, '"aa(b"', 'failed at #2') + + " #3 + call setline('.', '(((foo)))') + let @@ = 'fail' + normal 0ffvaby + call g:assert.equals(@@, '(foo)', 'failed at #3') + + " #4 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}, {'buns': ['(((', ')))']}] + call setline('.', '(((foo)))') + let @@ = 'fail' + normal 0ffvaby + call g:assert.equals(@@, '(((foo)))', 'failed at #4') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ["'", "'"]}, + \ {'buns': ["'", "'"], 'filetype': ['vim'], 'skip_regex': ['[^'']\%(''''\)*\zs''''', '[^'']\%(''''\)*''\zs''']} + \ ] + + " #5 + call setline('.', "'foo''bar'") + let @@ = 'fail' + normal 0ffvaby + call g:assert.equals(@@, "'foo'", 'failed at #5') + + " #6 + set filetype=vim + call setline('.', "'foo''bar'") + let @@ = 'fail' + normal 0ffvaby + call g:assert.equals(@@, "'foo''bar'", 'failed at #6') + + set filetype= + let g:textobj#sandwich#recipes = [ + \ {'buns': ['^', '$']}, + \ {'buns': ['^', '$'], 'regex': 1} + \ ] + + " #7 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0fbvaby + call g:assert.equals(@@, 'foobarbaz', 'failed at #7') + + " #8 + call setline('.', 'foo^bar$baz') + let @@ = 'fail' + normal 0fbvaby + call g:assert.equals(@@, '^bar$', 'failed at #8') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['1+1', '1+1']}, + \ {'buns': ['1+1', '1+1'], 'expr': 1} + \ ] + + " #9 + call setline('.', '1+12foo21+1') + let @@ = 'fail' + normal 0ffvaby + call g:assert.equals(@@, '2foo2', 'failed at #9') + + " #10 + call setline('.', '21+1foo1+12') + let @@ = 'fail' + normal 0ffvaby + call g:assert.equals(@@, '1+1foo1+1', 'failed at #10') + + let g:textobj#sandwich#recipes = [ + \ {'external': ['i{', 'a{']}, + \ {'external': ['i{', 'a{'], 'noremap': 0} + \ ] + xnoremap i{ i[ + xnoremap a{ a[ + + " #11 + call setline('.', '{[foo]}') + let @@ = 'fail' + normal 0ffvaby + call g:assert.equals(@@, '[foo]', 'failed at #11') + + " #12 + call setline('.', '[{foo}]') + let @@ = 'fail' + normal 0ffvaby + call g:assert.equals(@@, '{foo}', 'failed at #12') +endfunction +"}}} + +" Function interface +function! s:suite.i_function_interface() abort "{{{ + omap iib textobj#sandwich#auto('o', 'i', {'quoteescape': 0}, [{'buns': ['"', '"']}, {'buns': ['(', ')']}]) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['"', '"']}, + \ {'buns': ['[', ']']}, + \ ] + call textobj#sandwich#set('auto', 'quoteescape', 1) + + " #1 + call setline('.', '"foo\""') + normal 0dib + call g:assert.equals(getline('.'), '""', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal 0dib + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + " #3 + call setline('.', '[foo]') + normal 0dib + call g:assert.equals(getline('.'), '[]', 'failed at #3') + + " #4 + call setline('.', '"foo\""') + normal 0diib + call g:assert.equals(getline('.'), '"""', 'failed at #4') + + " #5 + call setline('.', '(foo)') + normal 0diib + call g:assert.equals(getline('.'), '()', 'failed at #5') + + " #6 + call setline('.', '[foo]') + normal 0diib + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') +endfunction +"}}} +function! s:suite.a_function_interface() abort "{{{ + omap aab textobj#sandwich#auto('o', 'a', {'quoteescape': 0}, [{'buns': ['"', '"']}, {'buns': ['(', ')']}]) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['"', '"']}, + \ {'buns': ['[', ']']}, + \ ] + call textobj#sandwich#set('auto', 'quoteescape', 1) + + " #1 + call setline('.', '"foo\""') + normal 0dab + call g:assert.equals(getline('.'), '', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal 0dab + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + " #3 + call setline('.', '[foo]') + normal 0dab + call g:assert.equals(getline('.'), '', 'failed at #3') + + " #4 + call setline('.', '"foo\""') + normal 0daab + call g:assert.equals(getline('.'), '"', 'failed at #4') + + " #5 + call setline('.', '(foo)') + normal 0daab + call g:assert.equals(getline('.'), '', 'failed at #5') + + " #6 + call setline('.', '[foo]') + normal 0daab + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') +endfunction +"}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sandwich/test/textobj-query.vim b/config/neovim/store/lazy-plugins/vim-sandwich/test/textobj-query.vim new file mode 100644 index 00000000..8203cc63 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sandwich/test/textobj-query.vim @@ -0,0 +1,5628 @@ +scriptencoding utf-8 + +let s:suite = themis#suite('textobj-sandwich: query:') + +function! s:suite.before() abort "{{{ + omap is (textobj-sandwich-query-i) + xmap is (textobj-sandwich-query-i) + omap as (textobj-sandwich-query-a) + xmap as (textobj-sandwich-query-a) +endfunction +"}}} +function! s:suite.before_each() abort "{{{ + %delete + syntax off + set filetype= + set virtualedit& + set whichwrap& + call textobj#sandwich#set_default() + call operator#sandwich#set_default() + unlet! g:sandwich#recipes + unlet! g:textobj#sandwich#recipes + silent! xunmap i{ + silent! xunmap a{ + silent! ounmap iis + silent! ounmap aas + silent! nunmap sd + silent! xunmap sd +endfunction +"}}} +function! s:suite.after() abort "{{{ + call s:suite.before_each() + ounmap is + xunmap is + ounmap as + xunmap as +endfunction +"}}} + +" Input +function! s:suite.input() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')']}] + + " #1 + call setline('.', '(foo)') + normal 0dis( + call g:assert.equals(getline('.'), '()', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'input': ['a', 'b']}] + + " #2 + call setline('.', '(foo)') + normal 0disa + call g:assert.equals(getline('.'), '()', 'failed at #2') + + " #3 + call setline('.', '(foo)') + normal 0disb + call g:assert.equals(getline('.'), '()', 'failed at #3') + + " #4 + call setline('.', '(foo)') + normal 0dis( + call g:assert.equals(getline('.'), '(foo)', 'failed at #4') + + " #5 + call setline('.', '(foo)') + execute "normal 0dis\" + call g:assert.equals(getline('.'), '(foo)', 'failed at #5') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['`', '`']}, + \ {'buns': ['``', '``']}, + \ {'buns': ['```', '```']}, + \ ] + + " #6 + call setline('.', '```baz``bar`foo`bar``baz```') + normal 0ffdis`h + call g:assert.equals(getline('.'), '```baz``bar``bar``baz```', 'failed at #6') + + " #7 + call setline('.', '```baz``bar`foo`bar``baz```') + normal 0ffdis``h + call g:assert.equals(getline('.'), '```baz````baz```', 'failed at #7') + + " #8 + call setline('.', '```baz``bar`foo`bar``baz```') + normal 0ffdis``` + call g:assert.equals(getline('.'), '``````', 'failed at #8') + + " #9 + call setline('.', '```baz``bar`foo`bar``baz```') + execute "normal 0ffdis`\" + call g:assert.equals(getline('.'), '```baz``bar`foo`bar``baz```', 'failed at #9') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['```', '```']}, + \ ] + + " #10 + call setline('.', '```baz``bar`foo`bar``baz```') + normal 0ffdis`h + call g:assert.equals(getline('.'), '```baz``bar``bar``baz```', 'failed at #10') + + " #11 + call setline('.', '```baz``bar`foo`bar``baz```') + normal 0ffdis``h + call g:assert.equals(getline('.'), '```baz``bar``bar``baz```', 'failed at #11') + + " #12 + call setline('.', '```baz``bar`foo`bar``baz```') + normal 0ffdis``` + call g:assert.equals(getline('.'), '``````', 'failed at #12') + + " #13 + call setline('.', '```baz``bar`foo`bar``baz```') + execute "normal 0ffdis`\" + call g:assert.equals(getline('.'), '```baz``bar`foo`bar``baz```', 'failed at #13') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['"', '"'], 'input': ['`']}, + \ {'buns': ['```', '```']}, + \ ] + + " #14 + call setline('.', '```qux``baz`bar"foo"bar`baz``qux```') + normal 0ffdis`h + call g:assert.equals(getline('.'), '```qux``baz`bar""bar`baz``qux```', 'failed at #14') + + " #15 + call setline('.', '```qux``baz`bar"foo"bar`baz``qux```') + normal 0ffdis``h + call g:assert.equals(getline('.'), '```qux``baz`bar""bar`baz``qux```', 'failed at #15') + + " #16 + call setline('.', '```qux``baz`bar"foo"bar`baz``qux```') + normal 0ffdis``` + call g:assert.equals(getline('.'), '``````', 'failed at #16') + + " #17 + call setline('.', '```baz``bar`foo`bar``baz```') + execute "normal 0ffdis`\" + call g:assert.equals(getline('.'), '```baz``bar`foo`bar``baz```', 'failed at #17') +endfunction +"}}} + +" Filter +function! s:suite.filter_filetype() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'filetype': ['vim'], 'input': ['(', ')']}, + \ {'buns': ['{', '}'], 'filetype': ['all']}, + \ {'buns': ['<', '>'], 'filetype': ['']} + \ ] + + " #1 + call setline('.', '([foo])') + normal 03ldis( + call g:assert.equals(getline('.'), '()', 'failed at #1') + + " #2 + call setline('.', '{foo}') + normal 02ldis{ + call g:assert.equals(getline('.'), '{}', 'failed at #2') + + " #3 + call setline('.', '') + normal 02ldis< + call g:assert.equals(getline('.'), '<>', 'failed at #3') + + set filetype=vim + + " #4 + call setline('.', '([foo])') + normal 03ldis( + call g:assert.equals(getline('.'), '([])', 'failed at #4') + + " #5 + call setline('.', '{foo}') + normal 02ldis{ + call g:assert.equals(getline('.'), '{}', 'failed at #5') + + " #6 + call setline('.', '') + normal 02ldis< + call g:assert.equals(getline('.'), '', 'failed at #6') +endfunction +"}}} +function! s:suite.filter_kind() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['[', ']'], 'kind': ['query'], 'input': ['(', ')']}, + \ {'buns': ['(', ')']}, + \ ] + + " #1 + call setline('.', '([foo])') + normal 03ldis( + call g:assert.equals(getline('.'), '()', 'failed at #1') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'kind': ['query'], 'input': ['(', ')']}, + \ ] + + " #2 + call setline('.', '([foo])') + normal 03ldis( + call g:assert.equals(getline('.'), '([])', 'failed at #2') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'kind': ['auto'], 'input': ['(', ')']}, + \ ] + + " #3 + call setline('.', '([foo])') + normal 03ldis( + call g:assert.equals(getline('.'), '()', 'failed at #3') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'kind': ['textobj'], 'input': ['(', ')']}, + \ ] + + " #4 + call setline('.', '([foo])') + normal 03ldis( + call g:assert.equals(getline('.'), '([])', 'failed at #4') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'kind': ['all'], 'input': ['(', ')']}, + \ ] + + " #5 + call setline('.', '([foo])') + normal 03ldis( + call g:assert.equals(getline('.'), '([])', 'failed at #5') +endfunction +"}}} +function! s:suite.filter_mode() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'input': ['(', ')']}, + \ ] + + " #1 + call setline('.', '([foo])') + normal 03ldis( + call g:assert.equals(getline('.'), '([])', 'failed at #1') + + " #2 + call setline('.', '([foo])') + normal 03lvis(d + call g:assert.equals(getline('.'), '([])', 'failed at #2') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'mode': ['o'], 'input': ['(', ')']}, + \ ] + + " #3 + call setline('.', '([foo])') + normal 03ldis( + call g:assert.equals(getline('.'), '([])', 'failed at #3') + + " #4 + call setline('.', '([foo])') + normal 03lvis(d + call g:assert.equals(getline('.'), '()', 'failed at #4') + + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'mode': ['x'], 'input': ['(', ')']}, + \ ] + + " #5 + call setline('.', '([foo])') + normal 03ldis( + call g:assert.equals(getline('.'), '()', 'failed at #5') + + " #6 + call setline('.', '([foo])') + normal 03lvis(d + call g:assert.equals(getline('.'), '([])', 'failed at #6') +endfunction +"}}} +function! s:suite.filter_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['[', ']'], 'expr_filter': ['FilterValid()']}, + \ {'buns': ['{', '}'], 'expr_filter': ['FilterInvalid()']}, + \ ] + + function! FilterValid() abort + return 1 + endfunction + + function! FilterInvalid() abort + return 0 + endfunction + + " #1 + call setline('.', '(foo)') + normal 0dis( + call g:assert.equals(getline('.'), '()', 'failed at #1') + + " #2 + call setline('.', '[foo]') + normal 0dis[ + call g:assert.equals(getline('.'), '[]', 'failed at #2') + + " #3 + call setline('.', '{foo}') + normal 0dis{ + call g:assert.equals(getline('.'), '{foo}', 'failed at #3') +endfunction +"}}} + +function! s:suite.i_o_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lyis( + call g:assert.equals(@@, 'foo', 'failed at #1') + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lyis) + call g:assert.equals(@@, 'foo', 'failed at #2') + + " #3 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lyis[ + call g:assert.equals(@@, 'foo', 'failed at #3') + + " #4 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lyis] + call g:assert.equals(@@, 'foo', 'failed at #4') + + " #5 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lyis{ + call g:assert.equals(@@, 'foo', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lyis} + call g:assert.equals(@@, 'foo', 'failed at #6') + + " #7 + call setline('.', '') + let @@ = 'fail' + normal 02lyis< + call g:assert.equals(@@, 'foo', 'failed at #7') + + " #8 + call setline('.', '') + let @@ = 'fail' + normal 02lyis> + call g:assert.equals(@@, 'foo', 'failed at #8') + + " #9 + call setline('.', '"foo"') + let @@ = 'fail' + normal 02lyis" + call g:assert.equals(@@, 'foo', 'failed at #9') + + " #10 + call setline('.', "'foo'") + let @@ = 'fail' + normal 02lyis' + call g:assert.equals(@@, 'foo', 'failed at #10') +endfunction +"}}} +function! s:suite.i_o_nest() abort "{{{ + " #1 + call setline('.', '()') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, '', 'failed at #1') + + " #2 + call setline('.', '(a)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'a', 'failed at #2') + + " #3 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #3') + + " #4 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0lyis( + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #4') + + " #5 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 02lyis( + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #5') + + " #6 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 03lyis( + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #6') + + " #7 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 04lyis( + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #7') + + " #8 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 05lyis( + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #8') + + " #9 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 06lyis( + call g:assert.equals(@@, 'cc', 'failed at #9') + + " #10 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 07lyis( + call g:assert.equals(@@, 'cc', 'failed at #10') + + " #11 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 08lyis( + call g:assert.equals(@@, 'cc', 'failed at #11') + + " #12 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 09lyis( + call g:assert.equals(@@, 'cc', 'failed at #12') + + " #13 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 010lyis( + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #13') + + " #14 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 011lyis( + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #14') + + " #15 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 012lyis( + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #15') + + " #16 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 013lyis( + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #16') + + " #17 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 014lyis( + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #17') + + " #18 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 015lyis( + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(((', ')))'], 'nesting': 1, 'input': ['(']}] + + " #19 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #19') + + " #20 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0lyis( + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #20') + + " #21 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 02lyis( + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #21') + + " #22 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 03lyis( + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #22') + + " #23 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 04lyis( + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #23') + + " #24 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 05lyis( + call g:assert.equals(@@, 'bb', 'failed at #24') + + " #25 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 06lyis( + call g:assert.equals(@@, 'bb', 'failed at #25') + + " #26 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 07lyis( + call g:assert.equals(@@, 'bb', 'failed at #26') + + " #27 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 08lyis( + call g:assert.equals(@@, 'bb', 'failed at #27') + + " #28 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 09lyis( + call g:assert.equals(@@, 'bb', 'failed at #28') + + " #29 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 010lyis( + call g:assert.equals(@@, 'bb', 'failed at #29') + + " #30 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 011lyis( + call g:assert.equals(@@, 'bb', 'failed at #30') + + " #31 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 012lyis( + call g:assert.equals(@@, 'bb', 'failed at #31') + + " #32 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 013lyis( + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #32') + + " #33 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 014lyis( + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #33') + + " #34 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 015lyis( + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #34') + + " #35 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 016lyis( + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #35') + + " #36 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 017lyis( + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #36') +endfunction +"}}} +function! s:suite.i_o_no_nest() abort "{{{ + " #1 + call setline('.', '""') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, '', 'failed at #1') + + " #2 + call setline('.', '"a"') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, 'a', 'failed at #2') + + " #3 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, 'aa', 'failed at #3') + + " #4 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0lyis" + call g:assert.equals(@@, 'aa', 'failed at #4') + + " #5 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 02lyis" + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 03lyis" + call g:assert.equals(@@, 'aa', 'failed at #6') + + " #7 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 04lyis" + call g:assert.equals(@@, 'bb', 'failed at #7') + + " #8 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 05lyis" + call g:assert.equals(@@, 'bb', 'failed at #8') + + " #9 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 06lyis" + call g:assert.equals(@@, 'cc', 'failed at #9') + + " #10 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 07lyis" + call g:assert.equals(@@, 'cc', 'failed at #10') + + " #11 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 08lyis" + call g:assert.equals(@@, 'cc', 'failed at #11') + + " #12 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 09lyis" + call g:assert.equals(@@, 'cc', 'failed at #12') + + " #13 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 010lyis" + call g:assert.equals(@@, 'bb', 'failed at #13') + + " #14 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 011lyis" + call g:assert.equals(@@, 'bb', 'failed at #14') + + " #15 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 012lyis" + call g:assert.equals(@@, 'aa', 'failed at #15') + + " #16 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 013lyis" + call g:assert.equals(@@, 'aa', 'failed at #16') + + " #17 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 014lyis" + call g:assert.equals(@@, 'aa', 'failed at #17') + + " #18 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 015lyis" + call g:assert.equals(@@, 'aa', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"""', '"""'], 'nesting': 0, 'input': ['"']}] + + " #19 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, 'aa', 'failed at #19') + + " #20 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0lyis" + call g:assert.equals(@@, 'aa', 'failed at #20') + + " #21 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 02lyis" + call g:assert.equals(@@, 'aa', 'failed at #21') + + " #22 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 03lyis" + call g:assert.equals(@@, 'aa', 'failed at #22') + + " #23 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 04lyis" + call g:assert.equals(@@, 'aa', 'failed at #23') + + " #24 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 05lyis" + call g:assert.equals(@@, 'aa', 'failed at #24') + + " #25 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 06lyis" + call g:assert.equals(@@, 'aa', 'failed at #25') + + " #26 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 07lyis" + call g:assert.equals(@@, 'aa', 'failed at #26') + + " #27 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 08lyis" + call g:assert.equals(@@, 'bb', 'failed at #27') + + " #28 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 09lyis" + call g:assert.equals(@@, 'bb', 'failed at #28') + + " #29 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 010lyis" + call g:assert.equals(@@, 'cc', 'failed at #29') + + " #30 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 011lyis" + call g:assert.equals(@@, 'cc', 'failed at #30') + + " #31 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 012lyis" + call g:assert.equals(@@, 'cc', 'failed at #31') + + " #32 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 013lyis" + call g:assert.equals(@@, 'cc', 'failed at #32') + + " #33 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 014lyis" + call g:assert.equals(@@, 'cc', 'failed at #33') + + " #34 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 015lyis" + call g:assert.equals(@@, 'cc', 'failed at #34') + + " #35 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 016lyis" + call g:assert.equals(@@, 'cc', 'failed at #35') + + " #36 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 017lyis" + call g:assert.equals(@@, 'cc', 'failed at #36') +endfunction +"}}} +function! s:suite.i_o_external_textobj() abort "{{{ + let g:textobj#sandwich#recipes = [{'external': ['it', 'at'], 'input': ['t']}] + + " #1 + call setline('.', 'aabbaa') + let @@ = 'fail' + normal 0fbyist + call g:assert.equals(@@, 'bb', 'failed at #1') +endfunction +"}}} +function! s:suite.i_o_multibyte() abort "{{{ + let g:textobj#sandwich#recipes = [{'buns': ['α', 'α'], 'input': ['a']}] + + " #1 + call setline('.', 'aaαbbαaa') + let @@ = 'fail' + normal 0fbyisa + call g:assert.equals(@@, 'bb', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['aα', 'aα'], 'input': ['a']}] + + " #2 + call setline('.', 'aaαbbaαa') + let @@ = 'fail' + normal 0fbyisa + call g:assert.equals(@@, 'bb', 'failed at #2') +endfunction +"}}} +function! s:suite.i_o_option_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprEmpty()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprEmpty()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ off + " #1 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'aa', 'failed at #1') + + " #2 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yis1 + call g:assert.equals(@@, 'aa', 'failed at #3') + + """ on + call textobj#sandwich#set('query', 'expr', 1) + " #4 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, '', 'failed at #4') + + " #5 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yisb + call g:assert.equals(@@, '', 'failed at #6') + + " #7 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yisc + call g:assert.equals(@@, '', 'failed at #7') + + " #8 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yis0 + call g:assert.equals(@@, 'aa', 'failed at #8') +endfunction +"}}} +function! s:suite.i_o_option_listexpr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprEmpty("former")', 'input': ['b']}, + \ {'buns': 'SandwichListexprEmpty("latter")', 'input': ['c']}, + \ {'buns': 'SandwichListexprEmpty("both")', 'input': ['d']}, + \ ] + + """ on + call textobj#sandwich#set('query', 'listexpr', 1) + " #1 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'bar', 'failed at #1') + + " #2 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yisb + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yisc + call g:assert.equals(@@, '', 'failed at #3') + + " #4 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yisd + call g:assert.equals(@@, '', 'failed at #4') +endfunction +"}}} +function! s:suite.i_o_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['\d\+', '\d\+'], 'input': ['a']}, + \ {'buns': ['\d\+', '\d\+'], 'regex': 0, 'input': ['0']}, + \ {'buns': ['\d\+', '\d\+'], 'regex': 1, 'input': ['1']}, + \ ] + + """ off + " #1 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'aa', 'failed at #1') + + " #2 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yis1 + call g:assert.equals(@@, 'aa', 'failed at #3') + + """ on + call textobj#sandwich#set('query', 'regex', 1) + " #4 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, '', 'failed at #4') + + " #5 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yis0 + call g:assert.equals(@@, 'aa', 'failed at #6') +endfunction +"}}} +function! s:suite.i_o_option_skip_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ off + " #1 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'foo', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_regex': ['a']}] + + " #2 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, '', 'failed at #2') + + """ on + call textobj#sandwich#set('query', 'skip_regex', ['aa']) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + " #3 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'fooa', 'failed at #3') + + """ head and tail + let g:textobj#sandwich#recipes = [{'buns': ["'", "'"]}] + call textobj#sandwich#set('query', 'skip_regex_head', ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']']) + call textobj#sandwich#set('query', 'skip_regex_tail', ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)''']) + " #4 + call setline('.', "'''foo'''") + let @@ = 'fail' + normal 0ffyis' + call g:assert.equals(@@, "''foo''", 'failed at #4') +endfunction +"}}} +function! s:suite.i_o_option_quoteescape() abort "{{{ + """ off + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #1 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, 'aa\', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'quoteescape': 1}] + + " #2 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, 'aa\"bb', 'failed at #2') + + """ on + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + call textobj#sandwich#set('query', 'quoteescape', 1) + + " #3 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, 'aa\"bb', 'failed at #3') +endfunction +"}}} +function! s:suite.i_o_option_expand_range() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + """ -1 + " #1 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, 'aa', 'failed at #1') + + %delete + + " #2 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjyis" + call g:assert.equals(@@, "\naa\n", 'failed at #2') + + %delete + + " #3 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyis" + call g:assert.equals(@@, "\naa\nbb\ncc\n", 'failed at #3') + + %delete + + " #4 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 0}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyis" + call g:assert.equals(@@, "", 'failed at #4') + + %delete + + """ 0 + call textobj#sandwich#set('query', 'expand_range', 0) + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #5 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, 'aa', 'failed at #5') + + %delete + + " #6 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjyis" + call g:assert.equals(@@, '', 'failed at #6') + + %delete + + " #7 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyis" + call g:assert.equals(@@, '', 'failed at #7') + + %delete + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 1}] + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjyis" + call g:assert.equals(@@, "\naa\n", 'failed at #8') + + %delete + + """ 1 + call textobj#sandwich#set('query', 'expand_range', 1) + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #9 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, 'aa', 'failed at #9') + + %delete + + " #10 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjyis" + call g:assert.equals(@@, "\naa\n", 'failed at #10') + + %delete + + " #11 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyis" + call g:assert.equals(@@, '', 'failed at #11') + + %delete + + " #12 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': -1}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyis" + call g:assert.equals(@@, "\naa\nbb\ncc\n", 'failed at #12') +endfunction +"}}} +function! s:suite.i_o_option_noremap() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'external': ['i{', 'a{'], 'input': ['a']}, + \ {'external': ['i{', 'a{'], 'noremap': 0, 'input': ['0']}, + \ {'external': ['i{', 'a{'], 'noremap': 1, 'input': ['1']}, + \ ] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, '', 'failed at #1') + + " #2 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'foo', 'failed at #2') + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis0 + call g:assert.equals(@@, 'foo', 'failed at #3') + + " #4 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yis0 + call g:assert.equals(@@, '', 'failed at #4') + + """ off + call textobj#sandwich#set('query', 'noremap', 0) + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'foo', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, '', 'failed at #6') + + " #7 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis1 + call g:assert.equals(@@, '', 'failed at #7') + + " #8 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yis1 + call g:assert.equals(@@, 'foo', 'failed at #8') +endfunction +"}}} +function! s:suite.i_o_option_syntax() abort "{{{ + syntax enable + call textobj#sandwich#set('query', 'syntax', []) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'syntax': ['Special'], 'input': ['1']}, + \ ] + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'foo', 'failed at #1') + + call textobj#sandwich#set('query', 'syntax', ['Special']) + syn match TestParen '[()]' + highlight link TestParen String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, '', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'foo', 'failed at #3') + + call textobj#sandwich#set('query', 'syntax', []) + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis1 + call g:assert.equals(@@, 'foo', 'failed at #4') +endfunction +"}}} +function! s:suite.i_o_option_inner_syntax() abort "{{{ + syntax enable + call textobj#sandwich#set('query', 'inner_syntax', []) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'inner_syntax': ['Special'], 'input': ['1']}, + \ ] + + " #1 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'bar', 'failed at #1') + + call textobj#sandwich#set('query', 'inner_syntax', ['Special']) + syn match TestParen '[br]' + highlight link TestParen String + + " #2 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, '', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'bar', 'failed at #3') + + call textobj#sandwich#set('query', 'inner_syntax', []) + + " #4 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yis1 + call g:assert.equals(@@, 'bar', 'failed at #4') +endfunction +"}}} +function! s:suite.i_o_option_match_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'match_syntax': 1, 'input': ['1']}, + \ ] + + """ 0 (test recipe-local) + call textobj#sandwich#set('query', 'match_syntax', 0) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis1 + call g:assert.equals(@@, 'foo', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis1 + call g:assert.equals(@@, '', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis1 + call g:assert.equals(@@, 'foo', 'failed at #3') + + """ 1 + call textobj#sandwich#set('query', 'match_syntax', 1) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'foo', 'failed at #4') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, '', 'failed at #5') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #6 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'foo', 'failed at #6') + + """ 2 + call textobj#sandwich#set('query', 'match_syntax', 2) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #7 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, '', 'failed at #7') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #8 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, '', 'failed at #8') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #9 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'foo', 'failed at #9') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #10 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, '%s', 'failed at #10') + + """ 3 + call textobj#sandwich#set('query', 'match_syntax', 3) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #11 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'foo', 'failed at #11') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #12 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, '', 'failed at #12') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #13 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, 'foo', 'failed at #13') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #14 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0yis" + call g:assert.equals(@@, '%s', 'failed at #14') +endfunction +"}}} +function! s:suite.i_o_option_skip_break() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'skip_break': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggyis( + call g:assert.equals(@@, "\nfoo\n", 'failed at #1') + + %delete + + " #2 + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggyis1 + call g:assert.equals(@@, 'foo', 'failed at #2') + + %delete + + """ 1 + call textobj#sandwich#set('query', 'skip_break', 1) + " #3 + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggyis( + call g:assert.equals(@@, "foo", 'failed at #3') + + %delete + + " #4 + call append(0, [' (', ' foo', ' )']) + let @@ = 'fail' + normal ggyis( + call g:assert.equals(@@, "foo", 'failed at #4') + + %delete + + " #5 + " do not skip when any line breaking is not included. + call setline('.', '( foo )') + let @@ = 'fail' + normal 0yis( + call g:assert.equals(@@, " foo ", 'failed at #5') +endfunction +"}}} +function! s:suite.i_o_option_skip_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ expression + call textobj#sandwich#set('query', 'skip_expr', ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']) + " #1 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyisa + call g:assert.equals(@@, 'aaa', 'failed at #1') + + %delete + + """ funcref + call textobj#sandwich#set('query', 'skip_expr', [function('SandwichSkipIntermediate')]) + " #2 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyisa + call g:assert.equals(@@, 'aaa', 'failed at #2') + + %delete + + """ recipe-local + call textobj#sandwich#set('query', 'skip_expr', []) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_expr': ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']}] + " #3 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyisa + call g:assert.equals(@@, 'aaa', 'failed at #3') +endfunction +"}}} + +function! s:suite.i_x_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lvis(y + call g:assert.equals(@@, 'foo', 'failed at #1') + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lvis)y + call g:assert.equals(@@, 'foo', 'failed at #2') + + " #3 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lvis[y + call g:assert.equals(@@, 'foo', 'failed at #3') + + " #4 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lvis]y + call g:assert.equals(@@, 'foo', 'failed at #4') + + " #5 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lvis{y + call g:assert.equals(@@, 'foo', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lvis}y + call g:assert.equals(@@, 'foo', 'failed at #6') + + " #7 + call setline('.', '') + let @@ = 'fail' + normal 02lvis') + let @@ = 'fail' + normal 02lvis>y + call g:assert.equals(@@, 'foo', 'failed at #8') + + " #9 + call setline('.', '"foo"') + let @@ = 'fail' + normal 02lvis"y + call g:assert.equals(@@, 'foo', 'failed at #9') + + " #10 + call setline('.', "'foo'") + let @@ = 'fail' + normal 02lvis'y + call g:assert.equals(@@, 'foo', 'failed at #10') +endfunction +"}}} +function! s:suite.i_x_nest() abort "{{{ + " #1 + call setline('.', '()') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, '(', 'failed at #1') + + " #2 + call setline('.', '(a)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'a', 'failed at #2') + + " #3 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #3') + + " #4 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0lvis(y + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #4') + + " #5 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 02lvis(y + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #5') + + " #6 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 03lvis(y + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #6') + + " #7 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 04lvis(y + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #7') + + " #8 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 05lvis(y + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #8') + + " #9 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 06lvis(y + call g:assert.equals(@@, 'cc', 'failed at #9') + + " #10 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 07lvis(y + call g:assert.equals(@@, 'cc', 'failed at #10') + + " #11 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 08lvis(y + call g:assert.equals(@@, 'cc', 'failed at #11') + + " #12 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 09lvis(y + call g:assert.equals(@@, 'cc', 'failed at #12') + + " #13 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 010lvis(y + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #13') + + " #14 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 011lvis(y + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #14') + + " #15 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 012lvis(y + call g:assert.equals(@@, 'bb(cc)bb', 'failed at #15') + + " #16 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 013lvis(y + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #16') + + " #17 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 014lvis(y + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #17') + + " #18 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 015lvis(y + call g:assert.equals(@@, 'aa(bb(cc)bb)aa', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(((', ')))'], 'nesting': 1, 'input': ['(']}] + + " #19 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #19') + + " #20 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0lvis(y + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #20') + + " #21 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 02lvis(y + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #21') + + " #22 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 03lvis(y + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #22') + + " #23 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 04lvis(y + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #23') + + " #24 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 05lvis(y + call g:assert.equals(@@, 'bb', 'failed at #24') + + " #25 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 06lvis(y + call g:assert.equals(@@, 'bb', 'failed at #25') + + " #26 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 07lvis(y + call g:assert.equals(@@, 'bb', 'failed at #26') + + " #27 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 08lvis(y + call g:assert.equals(@@, 'bb', 'failed at #27') + + " #28 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 09lvis(y + call g:assert.equals(@@, 'bb', 'failed at #28') + + " #29 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 010lvis(y + call g:assert.equals(@@, 'bb', 'failed at #29') + + " #30 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 011lvis(y + call g:assert.equals(@@, 'bb', 'failed at #30') + + " #31 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 012lvis(y + call g:assert.equals(@@, 'bb', 'failed at #31') + + " #32 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 013lvis(y + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #32') + + " #33 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 014lvis(y + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #33') + + " #34 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 015lvis(y + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #34') + + " #35 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 016lvis(y + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #35') + + " #36 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 017lvis(y + call g:assert.equals(@@, 'aa(((bb)))aa', 'failed at #36') +endfunction +"}}} +function! s:suite.i_x_no_nest() abort "{{{ + " #1 + call setline('.', '""') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, '"', 'failed at #1') + + " #2 + call setline('.', '"a"') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, 'a', 'failed at #2') + + " #3 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, 'aa', 'failed at #3') + + " #4 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0lvis"y + call g:assert.equals(@@, 'aa', 'failed at #4') + + " #5 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 02lvis"y + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 03lvis"y + call g:assert.equals(@@, 'aa', 'failed at #6') + + " #7 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 04lvis"y + call g:assert.equals(@@, 'bb', 'failed at #7') + + " #8 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 05lvis"y + call g:assert.equals(@@, 'bb', 'failed at #8') + + " #9 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 06lvis"y + call g:assert.equals(@@, 'cc', 'failed at #9') + + " #10 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 07lvis"y + call g:assert.equals(@@, 'cc', 'failed at #10') + + " #11 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 08lvis"y + call g:assert.equals(@@, 'cc', 'failed at #11') + + " #12 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 09lvis"y + call g:assert.equals(@@, 'cc', 'failed at #12') + + " #13 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 010lvis"y + call g:assert.equals(@@, 'bb', 'failed at #13') + + " #14 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 011lvis"y + call g:assert.equals(@@, 'bb', 'failed at #14') + + " #15 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 012lvis"y + call g:assert.equals(@@, 'aa', 'failed at #15') + + " #16 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 013lvis"y + call g:assert.equals(@@, 'aa', 'failed at #16') + + " #17 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 014lvis"y + call g:assert.equals(@@, 'aa', 'failed at #17') + + " #18 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 015lvis"y + call g:assert.equals(@@, 'aa', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"""', '"""'], 'nesting': 0, 'input': ['"']}] + + " #19 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, 'aa', 'failed at #19') + + " #20 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0lvis"y + call g:assert.equals(@@, 'aa', 'failed at #20') + + " #21 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 02lvis"y + call g:assert.equals(@@, 'aa', 'failed at #21') + + " #22 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 03lvis"y + call g:assert.equals(@@, 'aa', 'failed at #22') + + " #23 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 04lvis"y + call g:assert.equals(@@, 'aa', 'failed at #23') + + " #24 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 05lvis"y + call g:assert.equals(@@, 'aa', 'failed at #24') + + " #25 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 06lvis"y + call g:assert.equals(@@, 'aa', 'failed at #25') + + " #26 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 07lvis"y + call g:assert.equals(@@, 'aa', 'failed at #26') + + " #27 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 08lvis"y + call g:assert.equals(@@, 'bb', 'failed at #27') + + " #28 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 09lvis"y + call g:assert.equals(@@, 'bb', 'failed at #28') + + " #29 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 010lvis"y + call g:assert.equals(@@, 'cc', 'failed at #29') + + " #30 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 011lvis"y + call g:assert.equals(@@, 'cc', 'failed at #30') + + " #31 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 012lvis"y + call g:assert.equals(@@, 'cc', 'failed at #31') + + " #32 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 013lvis"y + call g:assert.equals(@@, 'cc', 'failed at #32') + + " #33 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 014lvis"y + call g:assert.equals(@@, 'cc', 'failed at #33') + + " #34 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 015lvis"y + call g:assert.equals(@@, 'cc', 'failed at #34') + + " #35 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 016lvis"y + call g:assert.equals(@@, 'cc', 'failed at #35') + + " #36 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 017lvis"y + call g:assert.equals(@@, 'cc', 'failed at #36') +endfunction +"}}} +function! s:suite.i_x_external_textobj() abort "{{{ + let g:textobj#sandwich#recipes = [{'external': ['it', 'at'], 'input': ['t']}] + + " #1 + call setline('.', 'aabbaa') + let @@ = 'fail' + normal 0fbvisty + call g:assert.equals(@@, 'bb', 'failed at #1') +endfunction +"}}} +function! s:suite.i_x_selected_area_extending() abort "{{{ + " #1 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvis{y + call g:assert.equals(@@, 'cc', 'failed at #1') + + " #2 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvis{is[y + call g:assert.equals(@@, 'bb{cc}bb', 'failed at #2') + + " #3 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvis{is[is(y + call g:assert.equals(@@, 'aa[bb{cc}bb]aa', 'failed at #3') +endfunction +"}}} +function! s:suite.i_x_blockwise_visual() abort "{{{ + " #1 + call append(0, ['( ', 'aa', ' )']) + let @@ = 'fail' + execute "normal gg\is(y" + call g:assert.equals(@@, " \na\n ", 'failed at #1') + + %delete + + " #2 + call append(0, ['(aa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2jis(y" + call g:assert.equals(@@, "aa\nbb\ncc", 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2jois(y" + call g:assert.equals(@@, "aa\nbb\ncc", 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa)', '(bb)', '(ccc)']) + let @@ = 'fail' + execute "normal gg\2jis(y" + call g:assert.equals(@@, "aa)\nbb)\nccc", 'failed at #4') + + %delete + + " #5 + call append(0, ['(aaa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2jois(y" + call g:assert.equals(@@, "aaa\nbb)\ncc)", 'failed at #5') +endfunction +"}}} +function! s:suite.i_x_multibyte() abort "{{{ + let g:textobj#sandwich#recipes = [{'buns': ['α', 'α'], 'input': ['a']}] + + " #1 + call setline('.', 'aaαbbαaa') + let @@ = 'fail' + normal 0fbvisay + call g:assert.equals(@@, 'bb', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['aα', 'aα'], 'input': ['a']}] + + " #2 + call setline('.', 'aaαbbaαa') + let @@ = 'fail' + normal 0fbvisay + call g:assert.equals(@@, 'bb', 'failed at #2') +endfunction +"}}} +function! s:suite.i_x_option_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprEmpty()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprEmpty()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ off + " #1 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, 'aa', 'failed at #1') + + " #2 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, '2', 'failed at #2') + + " #3 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0vis1y + call g:assert.equals(@@, 'aa', 'failed at #3') + + """ on + call textobj#sandwich#set('query', 'expr', 1) + " #4 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, '1', 'failed at #4') + + " #5 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0visby + call g:assert.equals(@@, '2', 'failed at #6') + + " #7 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0viscy + call g:assert.equals(@@, '2', 'failed at #7') + + " #8 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0vis0y + call g:assert.equals(@@, 'aa', 'failed at #8') +endfunction +"}}} +function! s:suite.i_x_option_listexpr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprEmpty("former")', 'input': ['b']}, + \ {'buns': 'SandwichListexprEmpty("latter")', 'input': ['c']}, + \ {'buns': 'SandwichListexprEmpty("both")', 'input': ['d']}, + \ ] + + """ on + call textobj#sandwich#set('query', 'listexpr', 1) + " #1 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, 'bar', 'failed at #1') + + " #2 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0visby + call g:assert.equals(@@, 'f', 'failed at #2') + + " #3 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0viscy + call g:assert.equals(@@, 'f', 'failed at #3') + + " #4 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0visdy + call g:assert.equals(@@, 'f', 'failed at #4') +endfunction +"}}} +function! s:suite.i_x_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['\d\+', '\d\+'], 'input': ['a']}, + \ {'buns': ['\d\+', '\d\+'], 'regex': 0, 'input': ['0']}, + \ {'buns': ['\d\+', '\d\+'], 'regex': 1, 'input': ['1']}, + \ ] + + """ off + " #1 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, 'aa', 'failed at #1') + + " #2 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, '8', 'failed at #2') + + " #3 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0vis1y + call g:assert.equals(@@, 'aa', 'failed at #3') + + """ on + call textobj#sandwich#set('query', 'regex', 1) + " #4 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, '\', 'failed at #4') + + " #5 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, 'aa', 'failed at #5') + + " #6 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0vis0y + call g:assert.equals(@@, 'aa', 'failed at #6') +endfunction +"}}} +function! s:suite.i_x_option_skip_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ off + " #1 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, 'foo', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_regex': ['aa']}] + + " #2 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, 'fooa', 'failed at #2') + + """ on + call textobj#sandwich#set('query', 'skip_regex', ['aa']) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + " #3 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, 'fooa', 'failed at #3') + + """ head and tail + let g:textobj#sandwich#recipes = [{'buns': ["'", "'"]}] + call textobj#sandwich#set('query', 'skip_regex_head', ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']']) + call textobj#sandwich#set('query', 'skip_regex_tail', ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)''']) + " #4 + call setline('.', "'''foo'''") + let @@ = 'fail' + normal 0ffvis'y + call g:assert.equals(@@, "''foo''", 'failed at #4') +endfunction +"}}} +function! s:suite.i_x_option_quoteescape() abort "{{{ + """ off + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #1 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, 'aa\', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'quoteescape': 1}] + + " #2 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, 'aa\"bb', 'failed at #2') + + """ on + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + call textobj#sandwich#set('query', 'quoteescape', 1) + + " #3 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, 'aa\"bb', 'failed at #3') +endfunction +"}}} +function! s:suite.i_x_option_expand_range() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + """ -1 + " #1 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, 'aa', 'failed at #1') + + %delete + + " #2 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggvis"y + call g:assert.equals(@@, "\naa\n", 'failed at #2') + + %delete + + " #3 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggvis"y + call g:assert.equals(@@, "\naa\nbb\ncc\n", 'failed at #3') + + %delete + + " #4 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 0}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jvis"y + call g:assert.equals(@@, 'b', 'failed at #4') + unlet! g:textobj#sandwich#recipes + + %delete + + """ 0 + call textobj#sandwich#set('query', 'expand_range', 0) + " #5 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, 'aa', 'failed at #5') + + %delete + + " #6 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggvis"y + call g:assert.equals(@@, '"', 'failed at #6') + + %delete + + " #7 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggvis"y + call g:assert.equals(@@, '"', 'failed at #7') + + %delete + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 1}] + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjvis"y + call g:assert.equals(@@, "\naa\n", 'failed at #8') + unlet! g:textobj#sandwich#recipes + + %delete + + """ 1 + call textobj#sandwich#set('query', 'expand_range', 1) + " #9 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, 'aa', 'failed at #9') + + %delete + + " #10 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjvis"y + call g:assert.equals(@@, "\naa\n", 'failed at #10') + + %delete + + " #11 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggvis"y + call g:assert.equals(@@, '"', 'failed at #11') + + %delete + + " #12 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': -1}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jvis"y + call g:assert.equals(@@, "\naa\nbb\ncc\n", 'failed at #12') + unlet! g:textobj#sandwich#recipes +endfunction +"}}} +function! s:suite.i_x_option_noremap() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'external': ['i{', 'a{'], 'input': ['a']}, + \ {'external': ['i{', 'a{'], 'noremap': 0, 'input': ['0']}, + \ {'external': ['i{', 'a{'], 'noremap': 1, 'input': ['1']}, + \ ] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, '(', 'failed at #1') + + " #2 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, 'foo', 'failed at #2') + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis0y + call g:assert.equals(@@, 'foo', 'failed at #3') + + " #4 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0vis0y + call g:assert.equals(@@, '{', 'failed at #4') + + """ off + call textobj#sandwich#set('query', 'noremap', 0) + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, 'foo', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0visay + call g:assert.equals(@@, '{', 'failed at #6') + + " #7 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis1y + call g:assert.equals(@@, '(', 'failed at #7') + + " #8 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0vis1y + call g:assert.equals(@@, 'foo', 'failed at #8') +endfunction +"}}} +function! s:suite.i_x_option_syntax() abort "{{{ + syntax enable + call textobj#sandwich#set('query', 'syntax', []) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'syntax': ['Special'], 'input': ['1']}, + \ ] + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'foo', 'failed at #1') + + call textobj#sandwich#set('query', 'syntax', ['Special']) + syn match TestParen '[()]' + highlight link TestParen String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, '(', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'foo', 'failed at #3') + + call textobj#sandwich#set('query', 'syntax', []) + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis1y + call g:assert.equals(@@, 'foo', 'failed at #4') +endfunction +"}}} +function! s:suite.i_x_option_inner_syntax() abort "{{{ + syntax enable + call textobj#sandwich#set('query', 'inner_syntax', []) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'inner_syntax': ['Special'], 'input': ['1']}, + \ ] + + " #1 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'bar', 'failed at #1') + + call textobj#sandwich#set('query', 'inner_syntax', ['Special']) + syn match TestParen '[br]' + highlight link TestParen String + + " #2 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, '(', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'bar', 'failed at #3') + + call textobj#sandwich#set('query', 'inner_syntax', []) + + " #4 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vis1y + call g:assert.equals(@@, 'bar', 'failed at #4') +endfunction +"}}} +function! s:suite.i_x_option_match_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'match_syntax': 1, 'input': ['1']}, + \ ] + + """ 0 (test recipe-local) + call textobj#sandwich#set('query', 'match_syntax', 0) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis1y + call g:assert.equals(@@, 'foo', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis1y + call g:assert.equals(@@, '(', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis1y + call g:assert.equals(@@, 'foo', 'failed at #3') + + """ 1 + call textobj#sandwich#set('query', 'match_syntax', 1) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'foo', 'failed at #4') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, '(', 'failed at #5') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #6 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'foo', 'failed at #6') + + """ 2 + call textobj#sandwich#set('query', 'match_syntax', 2) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #7 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, '(', 'failed at #7') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #8 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, '(', 'failed at #8') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #9 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'foo', 'failed at #9') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #10 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, '%s', 'failed at #10') + + """ 3 + call textobj#sandwich#set('query', 'match_syntax', 3) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #11 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'foo', 'failed at #11') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #12 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, '(', 'failed at #12') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #13 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, 'foo', 'failed at #13') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #14 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0vis"y + call g:assert.equals(@@, '%s', 'failed at #14') +endfunction +"}}} +function! s:suite.i_x_option_skip_break() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'skip_break': 1, 'input': ['1']}, + \ ] + + """ 0 + " #1 + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggvis(y + call g:assert.equals(@@, "\nfoo\n", 'failed at #1') + + %delete + + " #2 + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggvis1y + call g:assert.equals(@@, 'foo', 'failed at #2') + + %delete + + """ 1 + call textobj#sandwich#set('query', 'skip_break', 1) + " #3 + call append(0, ['(', 'foo', ')']) + let @@ = 'fail' + normal ggvis(y + call g:assert.equals(@@, "foo", 'failed at #3') + + %delete + + " #4 + call append(0, [' (', ' foo', ' )']) + let @@ = 'fail' + normal ggvis(y + call g:assert.equals(@@, "foo", 'failed at #4') + + %delete + + " #5 + " do not skip when any line breaking is not included. + call setline('.', '( foo )') + let @@ = 'fail' + normal 0vis(y + call g:assert.equals(@@, " foo ", 'failed at #5') +endfunction +"}}} +function! s:suite.i_x_option_skip_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ expression + call textobj#sandwich#set('query', 'skip_expr', ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']) + " #1 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lvisay + call g:assert.equals(@@, 'aaa', 'failed at #1') + + %delete + + """ funcref + call textobj#sandwich#set('query', 'skip_expr', [function('SandwichSkipIntermediate')]) + " #2 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lvisay + call g:assert.equals(@@, 'aaa', 'failed at #2') + + %delete + + """ recipe-local + call textobj#sandwich#set('query', 'skip_expr', []) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_expr': ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']}] + " #3 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lvisay + call g:assert.equals(@@, 'aaa', 'failed at #3') +endfunction +"}}} + +function! s:suite.a_o_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lyas( + call g:assert.equals(@@, '(foo)', 'failed at #1') + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lyas) + call g:assert.equals(@@, '(foo)', 'failed at #2') + + " #3 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lyas[ + call g:assert.equals(@@, '[foo]', 'failed at #3') + + " #4 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lyas] + call g:assert.equals(@@, '[foo]', 'failed at #4') + + " #5 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lyas{ + call g:assert.equals(@@, '{foo}', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lyas} + call g:assert.equals(@@, '{foo}', 'failed at #6') + + " #7 + call setline('.', '') + let @@ = 'fail' + normal 02lyas< + call g:assert.equals(@@, '', 'failed at #7') + + " #8 + call setline('.', '') + let @@ = 'fail' + normal 02lyas> + call g:assert.equals(@@, '', 'failed at #8') + + " #9 + call setline('.', '"foo"') + let @@ = 'fail' + normal 02lyas" + call g:assert.equals(@@, '"foo"', 'failed at #9') + + " #10 + call setline('.', "'foo'") + let @@ = 'fail' + normal 02lyas' + call g:assert.equals(@@, "'foo'", 'failed at #10') +endfunction +"}}} +function! s:suite.a_o_nest() abort "{{{ + " #1 + call setline('.', '()') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '()', 'failed at #1') + + " #2 + call setline('.', '(a)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(a)', 'failed at #2') + + " #3 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #3') + + " #4 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0lyas( + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #4') + + " #5 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 02lyas( + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #5') + + " #6 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 03lyas( + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #6') + + " #7 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 04lyas( + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #7') + + " #8 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 05lyas( + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #8') + + " #9 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 06lyas( + call g:assert.equals(@@, '(cc)', 'failed at #9') + + " #10 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 07lyas( + call g:assert.equals(@@, '(cc)', 'failed at #10') + + " #11 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 08lyas( + call g:assert.equals(@@, '(cc)', 'failed at #11') + + " #12 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 09lyas( + call g:assert.equals(@@, '(cc)', 'failed at #12') + + " #13 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 010lyas( + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #13') + + " #14 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 011lyas( + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #14') + + " #15 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 012lyas( + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #15') + + " #16 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 013lyas( + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #16') + + " #17 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 014lyas( + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #17') + + " #18 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 015lyas( + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(((', ')))'], 'nesting': 1, 'input': ['(']}] + + " #19 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #19') + + " #20 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0lyas( + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #20') + + " #21 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 02lyas( + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #21') + + " #22 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 03lyas( + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #22') + + " #23 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 04lyas( + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #23') + + " #24 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 05lyas( + call g:assert.equals(@@, '(((bb)))', 'failed at #24') + + " #25 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 06lyas( + call g:assert.equals(@@, '(((bb)))', 'failed at #25') + + " #26 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 07lyas( + call g:assert.equals(@@, '(((bb)))', 'failed at #26') + + " #27 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 08lyas( + call g:assert.equals(@@, '(((bb)))', 'failed at #27') + + " #28 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 09lyas( + call g:assert.equals(@@, '(((bb)))', 'failed at #28') + + " #29 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 010lyas( + call g:assert.equals(@@, '(((bb)))', 'failed at #29') + + " #30 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 011lyas( + call g:assert.equals(@@, '(((bb)))', 'failed at #30') + + " #31 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 012lyas( + call g:assert.equals(@@, '(((bb)))', 'failed at #31') + + " #32 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 013lyas( + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #32') + + " #33 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 014lyas( + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #33') + + " #34 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 015lyas( + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #34') + + " #35 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 016lyas( + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #35') + + " #36 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 017lyas( + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #36') +endfunction +"}}} +function! s:suite.a_o_no_nest() abort "{{{ + " #1 + call setline('.', '""') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '""', 'failed at #1') + + " #2 + call setline('.', '"a"') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"a"', 'failed at #2') + + " #3 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"aa"', 'failed at #3') + + " #4 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0lyas" + call g:assert.equals(@@, '"aa"', 'failed at #4') + + " #5 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 02lyas" + call g:assert.equals(@@, '"aa"', 'failed at #5') + + " #6 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 03lyas" + call g:assert.equals(@@, '"aa"', 'failed at #6') + + " #7 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 04lyas" + call g:assert.equals(@@, '"bb"', 'failed at #7') + + " #8 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 05lyas" + call g:assert.equals(@@, '"bb"', 'failed at #8') + + " #9 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 06lyas" + call g:assert.equals(@@, '"cc"', 'failed at #9') + + " #10 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 07lyas" + call g:assert.equals(@@, '"cc"', 'failed at #10') + + " #11 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 08lyas" + call g:assert.equals(@@, '"cc"', 'failed at #11') + + " #12 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 09lyas" + call g:assert.equals(@@, '"cc"', 'failed at #12') + + " #13 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 010lyas" + call g:assert.equals(@@, '"bb"', 'failed at #13') + + " #14 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 011lyas" + call g:assert.equals(@@, '"bb"', 'failed at #14') + + " #15 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 012lyas" + call g:assert.equals(@@, '"aa"', 'failed at #15') + + " #16 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 013lyas" + call g:assert.equals(@@, '"aa"', 'failed at #16') + + " #17 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 014lyas" + call g:assert.equals(@@, '"aa"', 'failed at #17') + + " #18 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 015lyas" + call g:assert.equals(@@, '"aa"', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"""', '"""'], 'nesting': 0, 'input': ['"']}] + + " #19 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"""aa"""', 'failed at #19') + + " #20 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0lyas" + call g:assert.equals(@@, '"""aa"""', 'failed at #20') + + " #21 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 02lyas" + call g:assert.equals(@@, '"""aa"""', 'failed at #21') + + " #22 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 03lyas" + call g:assert.equals(@@, '"""aa"""', 'failed at #22') + + " #23 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 04lyas" + call g:assert.equals(@@, '"""aa"""', 'failed at #23') + + " #24 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 05lyas" + call g:assert.equals(@@, '"""aa"""', 'failed at #24') + + " #25 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 06lyas" + call g:assert.equals(@@, '"""aa"""', 'failed at #25') + + " #26 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 07lyas" + call g:assert.equals(@@, '"""aa"""', 'failed at #26') + + " #27 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 08lyas" + call g:assert.equals(@@, '"""bb"""', 'failed at #27') + + " #28 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 09lyas" + call g:assert.equals(@@, '"""bb"""', 'failed at #28') + + " #29 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 010lyas" + call g:assert.equals(@@, '"""cc"""', 'failed at #29') + + " #30 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 011lyas" + call g:assert.equals(@@, '"""cc"""', 'failed at #30') + + " #31 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 012lyas" + call g:assert.equals(@@, '"""cc"""', 'failed at #31') + + " #32 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 013lyas" + call g:assert.equals(@@, '"""cc"""', 'failed at #32') + + " #33 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 014lyas" + call g:assert.equals(@@, '"""cc"""', 'failed at #33') + + " #34 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 015lyas" + call g:assert.equals(@@, '"""cc"""', 'failed at #34') + + " #35 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 016lyas" + call g:assert.equals(@@, '"""cc"""', 'failed at #35') + + " #36 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 017lyas" + call g:assert.equals(@@, '"""cc"""', 'failed at #36') +endfunction +"}}} +function! s:suite.a_o_external_textobj() abort "{{{ + let g:textobj#sandwich#recipes = [{'external': ['it', 'at'], 'input': ['t']}] + + " #1 + call setline('.', 'aabbaa') + let @@ = 'fail' + normal 0fbyast + call g:assert.equals(@@, 'bb', 'failed at #1') +endfunction +"}}} +function! s:suite.a_o_priority() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}, {'buns': ['(((', ')))']}] + + " #1 + call setline('.', '(((foo)))') + let @@ = 'fail' + normal 0ffyas(h + call g:assert.equals(@@, '(foo)', 'failed at #1') + + " #2 + call setline('.', '(((foo)))') + let @@ = 'fail' + normal 0ffyas((( + call g:assert.equals(@@, '(((foo)))', 'failed at #2') +endfunction +"}}} +function! s:suite.a_o_multibyte() abort "{{{ + let g:textobj#sandwich#recipes = [{'buns': ['α', 'α'], 'input': ['a']}] + + " #1 + call setline('.', 'aaαbbαaa') + let @@ = 'fail' + normal 0fbyasa + call g:assert.equals(@@, 'αbbα', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['aα', 'aα'], 'input': ['a']}] + + " #2 + call setline('.', 'aaαbbaαa') + let @@ = 'fail' + normal 0fbyasa + call g:assert.equals(@@, 'aαbbaα', 'failed at #2') +endfunction +"}}} +function! s:suite.a_o_option_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprEmpty()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprEmpty()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ off + " #1 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '1+1aa1+2', 'failed at #1') + + " #2 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yas1 + call g:assert.equals(@@, '2aa3', 'failed at #3') + + """ on + call textobj#sandwich#set('query', 'expr', 1) + " #4 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '', 'failed at #4') + + " #5 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '2aa3', 'failed at #5') + + " #6 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yasb + call g:assert.equals(@@, '', 'failed at #6') + + " #7 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0yasc + call g:assert.equals(@@, '', 'failed at #7') + + " #8 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0yas0 + call g:assert.equals(@@, '1+1aa1+2', 'failed at #8') +endfunction +"}}} +function! s:suite.a_o_option_listexpr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprEmpty("former")', 'input': ['b']}, + \ {'buns': 'SandwichListexprEmpty("latter")', 'input': ['c']}, + \ {'buns': 'SandwichListexprEmpty("both")', 'input': ['d']}, + \ ] + + """ on + call textobj#sandwich#set('query', 'listexpr', 1) + " #1 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, 'foobarbaz', 'failed at #1') + + " #2 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yasb + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yasc + call g:assert.equals(@@, '', 'failed at #3') + + " #4 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0yasd + call g:assert.equals(@@, '', 'failed at #4') +endfunction +"}}} +function! s:suite.a_o_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['\d\+', '\d\+'], 'input': ['a']}, + \ {'buns': ['\d\+', '\d\+'], 'regex': 0, 'input': ['0']}, + \ {'buns': ['\d\+', '\d\+'], 'regex': 1, 'input': ['1']}, + \ ] + + """ off + " #1 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '\d\+aa\d\+', 'failed at #1') + + " #2 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '', 'failed at #2') + + " #3 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yas1 + call g:assert.equals(@@, '888aa888', 'failed at #3') + + """ on + call textobj#sandwich#set('query', 'regex', 1) + " #4 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '', 'failed at #4') + + " #5 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '888aa888', 'failed at #5') + + " #6 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0yas0 + call g:assert.equals(@@, '\d\+aa\d\+', 'failed at #6') +endfunction +"}}} +function! s:suite.a_o_option_skip_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ off + " #1 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, 'afooa', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_regex': ['a']}] + + " #2 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '', 'failed at #2') + + """ on + call textobj#sandwich#set('query', 'skip_regex', ['aa']) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + " #3 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, 'afooaa', 'failed at #3') + + """ head and tail + let g:textobj#sandwich#recipes = [{'buns': ["'", "'"]}] + call textobj#sandwich#set('query', 'skip_regex_head', ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']']) + call textobj#sandwich#set('query', 'skip_regex_tail', ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)''']) + " #4 + call setline('.', "'''foo'''") + let @@ = 'fail' + normal 0ffyas' + call g:assert.equals(@@, "'''foo'''", 'failed at #4') +endfunction +"}}} +function! s:suite.a_o_option_quoteescape() abort "{{{ + """ off + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #1 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"aa\"', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'quoteescape': 1}] + + " #2 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"aa\"bb"', 'failed at #2') + + """ on + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + call textobj#sandwich#set('query', 'quoteescape', 1) + + " #3 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"aa\"bb"', 'failed at #3') +endfunction +"}}} +function! s:suite.a_o_option_expand_range() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + """ -1 + " #1 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"aa"', 'failed at #1') + + %delete + + " #2 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggyas" + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #2') + + %delete + + " #3 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggyas" + call g:assert.equals(@@, "\"\naa\nbb\ncc\n\"", 'failed at #3') + + %delete + + " #4 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 0}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyas" + call g:assert.equals(@@, "", 'failed at #4') + unlet! g:textobj#sandwich#recipes + + %delete + + """ 0 + call textobj#sandwich#set('query', 'expand_range', 0) + " #5 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"aa"', 'failed at #5') + + %delete + + " #6 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggyas" + call g:assert.equals(@@, '', 'failed at #6') + + %delete + + " #7 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggyas" + call g:assert.equals(@@, '', 'failed at #7') + + %delete + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 1}] + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjyas" + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #8') + unlet! g:textobj#sandwich#recipes + + %delete + + """ 1 + call textobj#sandwich#set('query', 'expand_range', 1) + " #9 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"aa"', 'failed at #9') + + %delete + + " #10 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjyas" + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #10') + + %delete + + " #11 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggyas" + call g:assert.equals(@@, '', 'failed at #11') + + %delete + + " #12 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': -1}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jyas" + call g:assert.equals(@@, "\"\naa\nbb\ncc\n\"", 'failed at #12') + unlet! g:textobj#sandwich#recipes +endfunction +"}}} +function! s:suite.a_o_option_noremap() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'external': ['i{', 'a{'], 'input': ['a']}, + \ {'external': ['i{', 'a{'], 'noremap': 0, 'input': ['0']}, + \ {'external': ['i{', 'a{'], 'noremap': 1, 'input': ['1']}, + \ ] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '', 'failed at #1') + + " #2 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '{foo}', 'failed at #2') + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas0 + call g:assert.equals(@@, '(foo)', 'failed at #3') + + " #4 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yas0 + call g:assert.equals(@@, '', 'failed at #4') + + """ off + call textobj#sandwich#set('query', 'noremap', 0) + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '(foo)', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yasa + call g:assert.equals(@@, '', 'failed at #6') + + " #7 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas1 + call g:assert.equals(@@, '', 'failed at #7') + + " #8 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0yas1 + call g:assert.equals(@@, '{foo}', 'failed at #8') +endfunction +"}}} +function! s:suite.a_o_option_syntax() abort "{{{ + syntax enable + call textobj#sandwich#set('query', 'syntax', []) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'syntax': ['Special'], 'input': ['1']}, + \ ] + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(foo)', 'failed at #1') + + call textobj#sandwich#set('query', 'syntax', ['Special']) + syn match TestParen '[()]' + highlight link TestParen String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(foo)', 'failed at #3') + + call textobj#sandwich#set('query', 'syntax', []) + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas1 + call g:assert.equals(@@, '(foo)', 'failed at #4') +endfunction +"}}} +function! s:suite.a_o_option_inner_syntax() abort "{{{ + syntax enable + call textobj#sandwich#set('query', 'inner_syntax', []) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'inner_syntax': ['Special'], 'input': ['1']}, + \ ] + + " #1 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(bar)', 'failed at #1') + + call textobj#sandwich#set('query', 'inner_syntax', ['Special']) + syn match TestParen '[br]' + highlight link TestParen String + + " #2 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(bar)', 'failed at #3') + + call textobj#sandwich#set('query', 'inner_syntax', []) + + " #4 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0yas1 + call g:assert.equals(@@, '(bar)', 'failed at #4') +endfunction +"}}} +function! s:suite.a_o_option_match_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'match_syntax': 1, 'input': ['1']}, + \ ] + + """ 0 (test recipe-local) + call textobj#sandwich#set('query', 'match_syntax', 0) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas1 + call g:assert.equals(@@, '(foo)', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas1 + call g:assert.equals(@@, '', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas1 + call g:assert.equals(@@, '(foo)', 'failed at #3') + + """ 1 + call textobj#sandwich#set('query', 'match_syntax', 1) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(foo)', 'failed at #4') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '', 'failed at #5') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #6 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(foo)', 'failed at #6') + + """ 2 + call textobj#sandwich#set('query', 'match_syntax', 2) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #7 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '', 'failed at #7') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #8 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '', 'failed at #8') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #9 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(foo)', 'failed at #9') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #10 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"%s"', 'failed at #10') + + """ 3 + call textobj#sandwich#set('query', 'match_syntax', 3) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #11 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(foo)', 'failed at #11') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #12 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '', 'failed at #12') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #13 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0yas( + call g:assert.equals(@@, '(foo)', 'failed at #13') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #14 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0yas" + call g:assert.equals(@@, '"%s"', 'failed at #14') +endfunction +"}}} +function! s:suite.a_o_option_synchro() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'nesting': 1}] + let g:operator#sandwich#recipes = [] + call textobj#sandwich#set('query', 'synchro', 1) + nmap sd (operator-sandwich-delete) + + " #1 + call setline('.', '(foo)') + normal 0sdas( + call g:assert.equals(getline('.'), 'foo', 'failed at #1') + + " #2 + call setline('.', '((foo))') + normal 0ff2sd2as( + call g:assert.equals(getline('.'), 'foo', 'failed at #2') + + let g:textobj#sandwich#recipes = [{'buns': ['(', ')'], 'synchro': 1, 'nesting': 1}] + call textobj#sandwich#set('query', 'synchro', 0) + + " #3 + call setline('.', '(foo)') + normal 0sdas( + call g:assert.equals(getline('.'), 'foo', 'failed at #3') + + " #4 + call setline('.', '((foo))') + normal 0ff2sd2as( + call g:assert.equals(getline('.'), 'foo', 'failed at #4') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'external': ['it', 'at'], 'input': ['t']}] + let g:operator#sandwich#recipes = [] + call textobj#sandwich#set('query', 'synchro', 1) + + " #5 + call setline('.', 'foo') + normal 0sdast + call g:assert.equals(getline('.'), 'foo', 'failed at #5') + + " #6 + call setline('.', 'foo') + normal 0ff2sd2ast + call g:assert.equals(getline('.'), 'foo', 'failed at #6') + + let g:textobj#sandwich#recipes = [{'external': ['it', 'at'], 'synchro': 1, 'input': ['t']}] + call textobj#sandwich#set('query', 'synchro', 0) + + " #7 + call setline('.', 'foo') + normal 0sdast + call g:assert.equals(getline('.'), 'foo', 'failed at #7') + + " #8 + call setline('.', 'foo') + normal 0ff2sd2ast + call g:assert.equals(getline('.'), 'foo', 'failed at #8') + + let g:sandwich#recipes = [{'buns': ['(', ')'], 'nesting': 1}, {'buns': ["'", "'"], 'nesting': 0}] + let g:textobj#sandwich#recipes = [] + let g:operator#sandwich#recipes = [] + call textobj#sandwich#set('query', 'synchro', 1) + nmap sd (operator-sandwich-delete)(operator-sandwich-synchro-count)(textobj-sandwich-query-a) + + " #9 + call setline('.', "(a'b((c))b'a)") + normal 0fc3sd( + call g:assert.equals(getline('.'), "a'b((c))b'a", 'failed at #9') + + call operator#sandwich#set('all', 'all', 'skip_char', 1) + + " #10 + call setline('.', "(a'b((c))b'a)") + normal 0fc3sd( + call g:assert.equals(getline('.'), "a'bcb'a", 'failed at #10') +endfunction +"}}} +function! s:suite.a_o_option_skip_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ expression + call textobj#sandwich#set('query', 'skip_expr', ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']) + " #1 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyasa + call g:assert.equals(@@, 'aaaaa', 'failed at #1') + + %delete + + """ funcref + call textobj#sandwich#set('query', 'skip_expr', [function('SandwichSkipIntermediate')]) + " #2 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyasa + call g:assert.equals(@@, 'aaaaa', 'failed at #2') + + %delete + + """ recipe-local + call textobj#sandwich#set('query', 'skip_expr', []) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_expr': ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']}] + " #3 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lyasa + call g:assert.equals(@@, 'aaaaa', 'failed at #3') +endfunction +"}}} + +function! s:suite.a_x_default_recipes() abort "{{{ + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lvas(y + call g:assert.equals(@@, '(foo)', 'failed at #1') + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 02lvas)y + call g:assert.equals(@@, '(foo)', 'failed at #2') + + " #3 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lvas[y + call g:assert.equals(@@, '[foo]', 'failed at #3') + + " #4 + call setline('.', '[foo]') + let @@ = 'fail' + normal 02lvas]y + call g:assert.equals(@@, '[foo]', 'failed at #4') + + " #5 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lvas{y + call g:assert.equals(@@, '{foo}', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 02lvas}y + call g:assert.equals(@@, '{foo}', 'failed at #6') + + " #7 + call setline('.', '') + let @@ = 'fail' + normal 02lvas', 'failed at #7') + + " #8 + call setline('.', '') + let @@ = 'fail' + normal 02lvas>y + call g:assert.equals(@@, '', 'failed at #8') + + " #9 + call setline('.', '"foo"') + let @@ = 'fail' + normal 02lvas"y + call g:assert.equals(@@, '"foo"', 'failed at #9') + + " #10 + call setline('.', "'foo'") + let @@ = 'fail' + normal 02lvas'y + call g:assert.equals(@@, "'foo'", 'failed at #10') +endfunction +"}}} +function! s:suite.a_x_nest() abort "{{{ + " #1 + call setline('.', '()') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '()', 'failed at #1') + + " #2 + call setline('.', '(a)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(a)', 'failed at #2') + + " #3 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #3') + + " #4 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 0lvas(y + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #4') + + " #5 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 02lvas(y + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #5') + + " #6 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 03lvas(y + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #6') + + " #7 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 04lvas(y + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #7') + + " #8 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 05lvas(y + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #8') + + " #9 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 06lvas(y + call g:assert.equals(@@, '(cc)', 'failed at #9') + + " #10 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 07lvas(y + call g:assert.equals(@@, '(cc)', 'failed at #10') + + " #11 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 08lvas(y + call g:assert.equals(@@, '(cc)', 'failed at #11') + + " #12 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 09lvas(y + call g:assert.equals(@@, '(cc)', 'failed at #12') + + " #13 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 010lvas(y + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #13') + + " #14 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 011lvas(y + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #14') + + " #15 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 012lvas(y + call g:assert.equals(@@, '(bb(cc)bb)', 'failed at #15') + + " #16 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 013lvas(y + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #16') + + " #17 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 014lvas(y + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #17') + + " #18 + call setline('.', '(aa(bb(cc)bb)aa)') + let @@ = 'fail' + normal 015lvas(y + call g:assert.equals(@@, '(aa(bb(cc)bb)aa)', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['(((', ')))'], 'nesting': 1, 'input': ['(']}] + + " #19 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #19') + + " #20 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 0lvas(y + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #20') + + " #21 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 02lvas(y + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #21') + + " #22 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 03lvas(y + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #22') + + " #23 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 04lvas(y + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #23') + + " #24 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 05lvas(y + call g:assert.equals(@@, '(((bb)))', 'failed at #24') + + " #25 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 06lvas(y + call g:assert.equals(@@, '(((bb)))', 'failed at #25') + + " #26 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 07lvas(y + call g:assert.equals(@@, '(((bb)))', 'failed at #26') + + " #27 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 08lvas(y + call g:assert.equals(@@, '(((bb)))', 'failed at #27') + + " #28 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 09lvas(y + call g:assert.equals(@@, '(((bb)))', 'failed at #28') + + " #29 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 010lvas(y + call g:assert.equals(@@, '(((bb)))', 'failed at #29') + + " #30 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 011lvas(y + call g:assert.equals(@@, '(((bb)))', 'failed at #30') + + " #31 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 012lvas(y + call g:assert.equals(@@, '(((bb)))', 'failed at #31') + + " #32 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 013lvas(y + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #32') + + " #33 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 014lvas(y + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #33') + + " #34 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 015lvas(y + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #34') + + " #35 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 016lvas(y + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #35') + + " #36 + call setline('.', '(((aa(((bb)))aa)))') + let @@ = 'fail' + normal 017lvas(y + call g:assert.equals(@@, '(((aa(((bb)))aa)))', 'failed at #36') +endfunction +"}}} +function! s:suite.a_x_no_nest() abort "{{{ + " #1 + call setline('.', '""') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '""', 'failed at #1') + + " #2 + call setline('.', '"a"') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"a"', 'failed at #2') + + " #3 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"aa"', 'failed at #3') + + " #4 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 0lvas"y + call g:assert.equals(@@, '"aa"', 'failed at #4') + + " #5 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 02lvas"y + call g:assert.equals(@@, '"aa"', 'failed at #5') + + " #6 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 03lvas"y + call g:assert.equals(@@, '"aa"', 'failed at #6') + + " #7 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 04lvas"y + call g:assert.equals(@@, '"bb"', 'failed at #7') + + " #8 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 05lvas"y + call g:assert.equals(@@, '"bb"', 'failed at #8') + + " #9 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 06lvas"y + call g:assert.equals(@@, '"cc"', 'failed at #9') + + " #10 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 07lvas"y + call g:assert.equals(@@, '"cc"', 'failed at #10') + + " #11 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 08lvas"y + call g:assert.equals(@@, '"cc"', 'failed at #11') + + " #12 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 09lvas"y + call g:assert.equals(@@, '"cc"', 'failed at #12') + + " #13 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 010lvas"y + call g:assert.equals(@@, '"bb"', 'failed at #13') + + " #14 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 011lvas"y + call g:assert.equals(@@, '"bb"', 'failed at #14') + + " #15 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 012lvas"y + call g:assert.equals(@@, '"aa"', 'failed at #15') + + " #16 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 013lvas"y + call g:assert.equals(@@, '"aa"', 'failed at #16') + + " #17 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 014lvas"y + call g:assert.equals(@@, '"aa"', 'failed at #17') + + " #18 + call setline('.', '"aa"bb"cc"bb"aa"') + let @@ = 'fail' + normal 015lvas"y + call g:assert.equals(@@, '"aa"', 'failed at #18') + + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"""', '"""'], 'nesting': 0, 'input': ['"']}] + + " #19 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"""aa"""', 'failed at #19') + + " #20 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 0lvas"y + call g:assert.equals(@@, '"""aa"""', 'failed at #20') + + " #21 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 02lvas"y + call g:assert.equals(@@, '"""aa"""', 'failed at #21') + + " #22 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 03lvas"y + call g:assert.equals(@@, '"""aa"""', 'failed at #22') + + " #23 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 04lvas"y + call g:assert.equals(@@, '"""aa"""', 'failed at #23') + + " #24 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 05lvas"y + call g:assert.equals(@@, '"""aa"""', 'failed at #24') + + " #25 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 06lvas"y + call g:assert.equals(@@, '"""aa"""', 'failed at #25') + + " #26 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 07lvas"y + call g:assert.equals(@@, '"""aa"""', 'failed at #26') + + " #27 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 08lvas"y + call g:assert.equals(@@, '"""bb"""', 'failed at #27') + + " #28 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 09lvas"y + call g:assert.equals(@@, '"""bb"""', 'failed at #28') + + " #29 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 010lvas"y + call g:assert.equals(@@, '"""cc"""', 'failed at #29') + + " #30 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 011lvas"y + call g:assert.equals(@@, '"""cc"""', 'failed at #30') + + " #31 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 012lvas"y + call g:assert.equals(@@, '"""cc"""', 'failed at #31') + + " #32 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 013lvas"y + call g:assert.equals(@@, '"""cc"""', 'failed at #32') + + " #33 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 014lvas"y + call g:assert.equals(@@, '"""cc"""', 'failed at #33') + + " #34 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 015lvas"y + call g:assert.equals(@@, '"""cc"""', 'failed at #34') + + " #35 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 016lvas"y + call g:assert.equals(@@, '"""cc"""', 'failed at #35') + + " #36 + call setline('.', '"""aa"""bb"""cc"""') + let @@ = 'fail' + normal 017lvas"y + call g:assert.equals(@@, '"""cc"""', 'failed at #36') +endfunction +"}}} +function! s:suite.a_x_external_textobj() abort "{{{ + let g:textobj#sandwich#recipes = [{'external': ['it', 'at'], 'input': ['t']}] + + " #1 + call setline('.', 'aabbaa') + let @@ = 'fail' + normal 0fbvasty + call g:assert.equals(@@, 'bb', 'failed at #1') +endfunction +"}}} +function! s:suite.a_x_priority() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}, {'buns': ['(', ')']}, {'buns': ['(((', ')))']}] + + " #1 + " NOTE: At this moment the first y after vas( is ignored... + call setline('.', '(((foo)))') + let @@ = 'fail' + normal 0ffvas(yy + call g:assert.equals(@@, '(foo)', 'failed at #1') + + " #2 + call setline('.', '(((foo)))') + let @@ = 'fail' + normal 0ffvas(((y + call g:assert.equals(@@, '(((foo)))', 'failed at #2') +endfunction +"}}} +function! s:suite.a_x_selected_area_extending() abort "{{{ + " #1 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvas{y + call g:assert.equals(@@, '{cc}', 'failed at #1') + + " #2 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvas{as[y + call g:assert.equals(@@, '[bb{cc}bb]', 'failed at #2') + + " #3 + call setline('.', '(aa[bb{cc}bb]aa)') + let @@ = 'fail' + normal 0fcvas{as[as(y + call g:assert.equals(@@, '(aa[bb{cc}bb]aa)', 'failed at #3') +endfunction +"}}} +function! s:suite.a_x_blockwise_visual() abort "{{{ + " #1 + call append(0, ['(aa', 'aa', 'aa)']) + let @@ = 'fail' + execute "normal gg\as(y" + call g:assert.equals(@@, "(aa\naa\naa)", 'failed at #1') + + %delete + + " #2 + call append(0, ['(aa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2jas(y" + call g:assert.equals(@@, "(aa)\n(bb)\n(cc)", 'failed at #2') + + %delete + + " #3 + call append(0, ['(aa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2joas(y" + call g:assert.equals(@@, "(aa)\n(bb)\n(cc)", 'failed at #3') + + %delete + + " #4 + call append(0, ['(aa)', '(bb)', '(ccc)']) + let @@ = 'fail' + execute "normal gg\2jas(y" + call g:assert.equals(@@, "(aa)\n(bb)\n(ccc)", 'failed at #4') + + %delete + + " #5 + call append(0, ['(aaa)', '(bb)', '(cc)']) + let @@ = 'fail' + execute "normal gg\2joas(y" + call g:assert.equals(@@, "(aaa)\n(bb)\n(cc)", 'failed at #5') +endfunction +"}}} +function! s:suite.a_x_multibyte() abort "{{{ + let g:textobj#sandwich#recipes = [{'buns': ['α', 'α'], 'input': ['a']}] + + " #1 + call setline('.', 'aaαbbαaa') + let @@ = 'fail' + normal 0fbvasay + call g:assert.equals(@@, 'αbbα', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['aα', 'aα'], 'input': ['a']}] + + " #2 + call setline('.', 'aaαbbaαa') + let @@ = 'fail' + normal 0fbvasay + call g:assert.equals(@@, 'aαbbaα', 'failed at #2') +endfunction +"}}} +function! s:suite.a_x_option_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['1+1', '1+2'], 'input': ['a']}, + \ {'buns': ['SandwichExprEmpty()', '1+2'], 'input': ['b']}, + \ {'buns': ['1+1', 'SandwichExprEmpty()'], 'input': ['c']}, + \ {'buns': ['1+1', '1+2'], 'expr': 0, 'input': ['0']}, + \ {'buns': ['1+1', '1+2'], 'expr': 1, 'input': ['1']}, + \ ] + + """ off + " #1 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '1+1aa1+2', 'failed at #1') + + " #2 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '2', 'failed at #2') + + " #3 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0vas1y + call g:assert.equals(@@, '2aa3', 'failed at #3') + + """ on + call textobj#sandwich#set('query', 'expr', 1) + " #4 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '1', 'failed at #4') + + " #5 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '2aa3', 'failed at #5') + + " #6 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0vasby + call g:assert.equals(@@, '2', 'failed at #6') + + " #7 + call setline('.', '2aa3') + let @@ = 'fail' + normal 0vascy + call g:assert.equals(@@, '2', 'failed at #7') + + " #8 + call setline('.', '1+1aa1+2') + let @@ = 'fail' + normal 0vas0y + call g:assert.equals(@@, '1+1aa1+2', 'failed at #8') +endfunction +"}}} +function! s:suite.a_x_option_listexpr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': 'SandwichListexprBuns(0)', 'input': ['a']}, + \ {'buns': 'SandwichListexprEmpty("former")', 'input': ['b']}, + \ {'buns': 'SandwichListexprEmpty("latter")', 'input': ['c']}, + \ {'buns': 'SandwichListexprEmpty("both")', 'input': ['d']}, + \ ] + + """ on + call textobj#sandwich#set('query', 'listexpr', 1) + " #1 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, 'foobarbaz', 'failed at #1') + + " #2 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0vasby + call g:assert.equals(@@, 'f', 'failed at #2') + + " #3 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0vascy + call g:assert.equals(@@, 'f', 'failed at #3') + + " #4 + call setline('.', 'foobarbaz') + let @@ = 'fail' + normal 0vasdy + call g:assert.equals(@@, 'f', 'failed at #4') +endfunction +"}}} +function! s:suite.a_x_option_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['\d\+', '\d\+'], 'input': ['a']}, + \ {'buns': ['\d\+', '\d\+'], 'regex': 0, 'input': ['0']}, + \ {'buns': ['\d\+', '\d\+'], 'regex': 1, 'input': ['1']}, + \ ] + + """ off + " #1 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '\d\+aa\d\+', 'failed at #1') + + " #2 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '8', 'failed at #2') + + " #3 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0vas1y + call g:assert.equals(@@, '888aa888', 'failed at #3') + + """ on + call textobj#sandwich#set('query', 'regex', 1) + " #4 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '\', 'failed at #4') + + " #5 + call setline('.', '888aa888') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '888aa888', 'failed at #5') + + " #6 + call setline('.', '\d\+aa\d\+') + let @@ = 'fail' + normal 0vas0y + call g:assert.equals(@@, '\d\+aa\d\+', 'failed at #6') +endfunction +"}}} +function! s:suite.a_x_option_skip_regex() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ off + " #1 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, 'afooa', 'failed at #1') + + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_regex': ['aa']}] + + " #2 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, 'afooaa', 'failed at #2') + + """ on + call textobj#sandwich#set('query', 'skip_regex', ['aa']) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + " #3 + call setline('.', 'afooaa') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, 'afooaa', 'failed at #3') + + """ head and tail + let g:textobj#sandwich#recipes = [{'buns': ["'", "'"]}] + call textobj#sandwich#set('query', 'skip_regex_head', ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']']) + call textobj#sandwich#set('query', 'skip_regex_tail', ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)''']) + " #4 + call setline('.', "'''foo'''") + let @@ = 'fail' + normal 0ffvas'y + call g:assert.equals(@@, "'''foo'''", 'failed at #4') +endfunction +"}}} +function! s:suite.a_x_option_quoteescape() abort "{{{ + """ off + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + " #2 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"aa\"', 'failed at #2') + + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'quoteescape': 1}] + + " #3 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"aa\"bb"', 'failed at #3') + + """ on + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + call textobj#sandwich#set('query', 'quoteescape', 1) + + " #1 + call setline('.', '"aa\"bb"') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"aa\"bb"', 'failed at #1') +endfunction +"}}} +function! s:suite.a_x_option_expand_range() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['"', '"']}] + + """ -1 + " #1 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"aa"', 'failed at #1') + + %delete + + " #2 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggvas"y + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #2') + + %delete + + " #3 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggvas"y + call g:assert.equals(@@, "\"\naa\nbb\ncc\n\"", 'failed at #3') + + %delete + + " #4 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 0}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jvas"y + call g:assert.equals(@@, 'b', 'failed at #4') + unlet! g:textobj#sandwich#recipes + + %delete + + """ 0 + call textobj#sandwich#set('query', 'expand_range', 0) + " #5 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"aa"', 'failed at #5') + + %delete + + " #6 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggvas"y + call g:assert.equals(@@, '"', 'failed at #6') + + %delete + + " #7 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggvas"y + call g:assert.equals(@@, '"', 'failed at #7') + + %delete + + " #8 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': 1}] + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjvas"y + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #8') + unlet! g:textobj#sandwich#recipes + + %delete + + """ 1 + call textobj#sandwich#set('query', 'expand_range', 1) + " #9 + call setline('.', '"aa"') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"aa"', 'failed at #9') + + %delete + + " #10 + call append(0, ['"', 'aa', '"']) + let @@ = 'fail' + normal ggjvas"y + call g:assert.equals(@@, "\"\naa\n\"", 'failed at #10') + + %delete + + " #11 + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal ggvas"y + call g:assert.equals(@@, '"', 'failed at #11') + + %delete + + " #12 + let g:textobj#sandwich#recipes = [{'buns': ['"', '"'], 'expand_range': -1}] + call append(0, ['"', 'aa', 'bb', 'cc', '"']) + let @@ = 'fail' + normal gg2jvas"y + call g:assert.equals(@@, "\"\naa\nbb\ncc\n\"", 'failed at #12') + unlet! g:textobj#sandwich#recipes +endfunction +"}}} +function! s:suite.a_x_option_noremap() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'external': ['i{', 'a{'], 'input': ['a']}, + \ {'external': ['i{', 'a{'], 'noremap': 0, 'input': ['0']}, + \ {'external': ['i{', 'a{'], 'noremap': 1, 'input': ['1']}, + \ ] + xnoremap i{ i( + xnoremap a{ a( + + """ on + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '(', 'failed at #1') + + " #2 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '{foo}', 'failed at #2') + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas0y + call g:assert.equals(@@, '(foo)', 'failed at #3') + + " #4 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0vas0y + call g:assert.equals(@@, '{', 'failed at #4') + + """ off + call textobj#sandwich#set('query', 'noremap', 0) + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '(foo)', 'failed at #5') + + " #6 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0vasay + call g:assert.equals(@@, '{', 'failed at #6') + + " #7 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas1y + call g:assert.equals(@@, '(', 'failed at #7') + + " #8 + call setline('.', '{foo}') + let @@ = 'fail' + normal 0vas1y + call g:assert.equals(@@, '{foo}', 'failed at #8') +endfunction +"}}} +function! s:suite.a_x_option_syntax() abort "{{{ + syntax enable + call textobj#sandwich#set('query', 'syntax', []) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'syntax': ['Special'], 'input': ['1']}, + \ ] + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(foo)', 'failed at #1') + + call textobj#sandwich#set('query', 'syntax', ['Special']) + syn match TestParen '[()]' + highlight link TestParen String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(foo)', 'failed at #3') + + call textobj#sandwich#set('query', 'syntax', []) + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas1y + call g:assert.equals(@@, '(foo)', 'failed at #4') +endfunction +"}}} +function! s:suite.a_x_option_inner_syntax() abort "{{{ + syntax enable + call textobj#sandwich#set('query', 'inner_syntax', []) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'inner_syntax': ['Special'], 'input': ['1']}, + \ ] + + " #1 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(bar)', 'failed at #1') + + call textobj#sandwich#set('query', 'inner_syntax', ['Special']) + syn match TestParen '[br]' + highlight link TestParen String + + " #2 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(', 'failed at #2') + + highlight link TestParen Special + + " #3 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(bar)', 'failed at #3') + + call textobj#sandwich#set('query', 'inner_syntax', []) + + " #4 + call setline('.', '(bar)') + let @@ = 'fail' + normal 0vas1y + call g:assert.equals(@@, '(bar)', 'failed at #4') +endfunction +"}}} +function! s:suite.a_x_option_match_syntax() abort "{{{ + syntax enable + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['(', ')']}, + \ {'buns': ['(', ')'], 'match_syntax': 1, 'input': ['1']}, + \ ] + + """ 0 (test recipe-local) + call textobj#sandwich#set('query', 'match_syntax', 0) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas1y + call g:assert.equals(@@, '(foo)', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas1y + call g:assert.equals(@@, '(', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas1y + call g:assert.equals(@@, '(foo)', 'failed at #3') + + """ 1 + call textobj#sandwich#set('query', 'match_syntax', 1) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #1 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(foo)', 'failed at #1') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #2 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(', 'failed at #2') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #3 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(foo)', 'failed at #3') + + """ 2 + call textobj#sandwich#set('query', 'match_syntax', 2) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #4 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(', 'failed at #4') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #5 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(', 'failed at #5') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #6 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(foo)', 'failed at #6') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #7 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"%s"', 'failed at #7') + + """ 3 + call textobj#sandwich#set('query', 'match_syntax', 3) + syntax clear + syntax match TestParen '[()]' + highlight link TestParen Special + + " #8 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(foo)', 'failed at #8') + + syntax clear + syntax match TestBra '(' + syntax match TestKet ')' + highlight link TestBra Special + highlight link TestKet String + + " #9 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(', 'failed at #9') + + syntax clear + syntax match TestBra '(f' + syntax match TestKet 'o)' + highlight link TestBra Special + highlight link TestKet Special + + " #10 + call setline('.', '(foo)') + let @@ = 'fail' + normal 0vas(y + call g:assert.equals(@@, '(foo)', 'failed at #10') + + syntax clear + syntax match TestString '".*"' contains=TestSpecialString + syntax match TestSpecialString '%s' + highlight link TestString String + highlight link TestSpecialString Special + + " #11 + call setline('.', '"%s"') + let @@ = 'fail' + normal 0vas"y + call g:assert.equals(@@, '"%s"', 'failed at #11') +endfunction +"}}} +function! s:suite.a_x_option_skip_expr() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a']}] + + """ expression + call textobj#sandwich#set('query', 'skip_expr', ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']) + " #1 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lvasay + call g:assert.equals(@@, 'aaaaa', 'failed at #1') + + %delete + + """ funcref + call textobj#sandwich#set('query', 'skip_expr', [function('SandwichSkipIntermediate')]) + " #2 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lvasay + call g:assert.equals(@@, 'aaaaa', 'failed at #2') + + %delete + + """ recipe-local + call textobj#sandwich#set('query', 'skip_expr', []) + let g:textobj#sandwich#recipes = [{'buns': ['a', 'a'], 'skip_expr': ['!(getpos(".")[2] == 1) && !(getpos(".")[2] == col([getpos(".")[1], "$"])-1)']}] + " #3 + call setline('.', 'aaaaa') + let @@ = 'fail' + normal 02lvasay + call g:assert.equals(@@, 'aaaaa', 'failed at #3') +endfunction +"}}} + +" Function interface +function! s:suite.i_function_interface() abort "{{{ + omap iis textobj#sandwich#query('o', 'i', {'quoteescape': 0}, [{'buns': ['"', '"']}, {'buns': ['(', ')']}]) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['"', '"']}, + \ {'buns': ['[', ']']}, + \ ] + call textobj#sandwich#set('query', 'quoteescape', 1) + + " #1 + call setline('.', '"foo\""') + normal 0dis" + call g:assert.equals(getline('.'), '""', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal 0dis( + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + " #3 + call setline('.', '[foo]') + normal 0dis[ + call g:assert.equals(getline('.'), '[]', 'failed at #3') + + " #4 + call setline('.', '"foo\""') + normal 0diis" + call g:assert.equals(getline('.'), '"""', 'failed at #4') + + " #5 + call setline('.', '(foo)') + normal 0diis( + call g:assert.equals(getline('.'), '()', 'failed at #5') + + " #6 + call setline('.', '[foo]') + normal 0diis[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') +endfunction +"}}} +function! s:suite.a_function_interface() abort "{{{ + omap aas textobj#sandwich#query('o', 'a', {'quoteescape': 0}, [{'buns': ['"', '"']}, {'buns': ['(', ')']}]) + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [ + \ {'buns': ['"', '"']}, + \ {'buns': ['[', ']']}, + \ ] + call textobj#sandwich#set('query', 'quoteescape', 1) + + " #1 + call setline('.', '"foo\""') + normal 0das" + call g:assert.equals(getline('.'), '', 'failed at #1') + + " #2 + call setline('.', '(foo)') + normal 0das( + call g:assert.equals(getline('.'), '(foo)', 'failed at #2') + + " #3 + call setline('.', '[foo]') + normal 0das[ + call g:assert.equals(getline('.'), '', 'failed at #3') + + " #4 + call setline('.', '"foo\""') + normal 0daas" + call g:assert.equals(getline('.'), '"', 'failed at #4') + + " #5 + call setline('.', '(foo)') + normal 0daas( + call g:assert.equals(getline('.'), '', 'failed at #5') + + " #6 + call setline('.', '[foo]') + normal 0daas[ + call g:assert.equals(getline('.'), '[foo]', 'failed at #6') +endfunction +"}}} + +" input_fallback +function! s:suite.input_fallback() abort "{{{ + let g:sandwich#recipes = [] + let g:textobj#sandwich#recipes = [] + + let g:sandwich#input_fallback = 1 + call setline('.', 'afooa') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'foo', 'failed at #1') + + let g:sandwich#input_fallback = 0 + call setline('.', 'afooa') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'fail', 'failed at #2') + + unlet! g:sandwich#input_fallback + call setline('.', 'afooa') + let @@ = 'fail' + normal 0yisa + call g:assert.equals(@@, 'foo', 'failed at #3') +endfunction "}}} + +" vim:set foldmethod=marker: +" vim:set commentstring="%s: diff --git a/config/neovim/store/lazy-plugins/vim-sleuth/doc/sleuth.txt b/config/neovim/store/lazy-plugins/vim-sleuth/doc/sleuth.txt new file mode 100644 index 00000000..f5fe24d5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sleuth/doc/sleuth.txt @@ -0,0 +1,82 @@ +*sleuth.txt* Heuristically set buffer options + +Author: Tim Pope +Repo: https://github.com/tpope/vim-sleuth +License: Same terms as Vim itself (see |license|) + +This plugin is only available if 'compatible' is not set. + +SUMMARY *sleuth* + +Automatic detection happens on the |BufNewFile|, |BufReadPost|, and +|BufFilePost| events. Sleuth first consults modelines, then EditorConfig +(https://editorconfig.org) files. If neither of these produces a value for +'expandtab' and 'shiftwidth', a heuristic algorithm is used to guess them, +first on the current file, and then on other files with the same extension in +the same and parent directories. + +The full list of detected options is as follows: + +Vim Option Safe? EditorConfig key ~ +'filetype' Y vim_filetype +'expandtab' Y indent_style +'shiftwidth' Y indent_size +'tabstop' Y tab_width +'textwidth' Y max_line_length +'fixendofline' Y insert_final_newline +'endofline' insert_final_newline +'fileformat' end_of_line +'fileencoding' charset +'bomb' charset + +Options marked "safe" are those guaranteed not to toggle 'modified'. These +are the only options that will be set in 'nomodifiable' buffers, and the only +options that Sleuth will detect in a modeline. Safe options will also be +reapplied on every |FileType| event, so that they can override any |ftplugin| +settings. + +Sleuth always sets 'softtabstop' to -1, effectively mirroring 'shiftwidth'. + +INTERFACE *sleuth-interface* + + *:Sleuth* +:Sleuth Manually detect indentation. + +:verbose Sleuth Manually detect indentation, and display the reason + each option was set. + + *SleuthIndicator()* +SleuthIndicator() Indicator for inclusion in 'statusline'. Or use + flagship.vim to have it included automatically. + +CONFIGURATION *sleuth-configuration* + +To override detection and force options for a particular file or directory, +use a |modeline| or EditorConfig (https://editorconfig.org/), respectively. + + *g:sleuth_{filetype}_heuristics* +To disable the heuristic algorithm for a given 'filetype', assign a false +value to a variable named like g:sleuth_{filetype}_heuristics: +> + let g:sleuth_gitcommit_heuristics = 0 +< + *g:sleuth_heuristics* +Alternatively, one can disable heuristics by default, and opt-in for +individual file types: +> + let g:sleuth_heuristics = 0 + let g:sleuth_perl_heuristics = 1 +< + *g:sleuth_automatic* +Sleuth used to support setting g:sleuth_automatic to a false value and instead +calling :Sleuth selectively from |FileType| autocommands. This will no longer +work quite right, and thus g:sleuth_automatic has been deprecated in favor of +the heuristics options above. + + *g:sleuth_no_filetype_indent_on* +Sleuth forces |:filetype-indent-on| by default, which enables file-type +specific indenting algorithms and is highly recommended. To opt out: +> + let g:sleuth_no_filetype_indent_on = 1 +< + vim:tw=78:et:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/vim-sleuth/doc/tags b/config/neovim/store/lazy-plugins/vim-sleuth/doc/tags new file mode 100644 index 00000000..a50ae2f4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sleuth/doc/tags @@ -0,0 +1,10 @@ +:Sleuth sleuth.txt /*:Sleuth* +SleuthIndicator() sleuth.txt /*SleuthIndicator()* +g:sleuth_automatic sleuth.txt /*g:sleuth_automatic* +g:sleuth_heuristics sleuth.txt /*g:sleuth_heuristics* +g:sleuth_no_filetype_indent_on sleuth.txt /*g:sleuth_no_filetype_indent_on* +g:sleuth_{filetype}_heuristics sleuth.txt /*g:sleuth_{filetype}_heuristics* +sleuth sleuth.txt /*sleuth* +sleuth-configuration sleuth.txt /*sleuth-configuration* +sleuth-interface sleuth.txt /*sleuth-interface* +sleuth.txt sleuth.txt /*sleuth.txt* diff --git a/config/neovim/store/lazy-plugins/vim-sleuth/plugin/sleuth.vim b/config/neovim/store/lazy-plugins/vim-sleuth/plugin/sleuth.vim new file mode 100644 index 00000000..628adbb6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/vim-sleuth/plugin/sleuth.vim @@ -0,0 +1,671 @@ +" sleuth.vim - Heuristically set buffer options +" Maintainer: Tim Pope +" Version: 2.0 +" GetLatestVimScripts: 4375 1 :AutoInstall: sleuth.vim + +if exists("#polyglot-sleuth") + autocmd! polyglot-sleuth + augroup! polyglot-sleuth + unlet! g:loaded_sleuth + let s:polyglot = 1 +endif + +if exists("g:loaded_sleuth") || v:version < 700 || &cp + finish +endif +let g:loaded_sleuth = 1 +lockvar g:loaded_sleuth + +function! s:Warn(msg, ...) abort + if !get(a:000, 0, 0) + echohl WarningMsg + echo a:msg + echohl NONE + endif + return '' +endfunction + +if exists('+shellslash') + function! s:Slash(path) abort + return tr(a:path, '\', '/') + endfunction +else + function! s:Slash(path) abort + return a:path + endfunction +endif + +function! s:Guess(source, detected, lines) abort + let has_heredocs = a:detected.filetype =~# '^\%(perl\|php\|ruby\|[cz]\=sh\|bash\)$' + let options = {} + let heuristics = {'spaces': 0, 'hard': 0, 'soft': 0, 'checked': 0, 'indents': {}} + let tabstop = get(a:detected.options, 'tabstop', get(a:detected.defaults, 'tabstop', [8]))[0] + let softtab = repeat(' ', tabstop) + let waiting_on = '' + let prev_indent = -1 + let prev_line = '' + + for line in a:lines + if len(waiting_on) + if line =~# waiting_on + let waiting_on = '' + let prev_indent = -1 + let prev_line = '' + endif + continue + elseif line =~# '^\s*$' + continue + elseif a:detected.filetype ==# 'python' && prev_line[-1:-1] =~# '[[\({]' + let prev_indent = -1 + let prev_line = '' + continue + elseif line =~# '^=\w' && line !~# '^=\%(end\|cut\)\>' + let waiting_on = '^=\%(end\|cut\)\>' + elseif line =~# '^@@\+ -\d\+,\d\+ ' + let waiting_on = '^$' + elseif line !~# '[/<"`]' + " No need to do other checks + elseif line =~# '^\s*/\*' && line !~# '\*/' + let waiting_on = '\*/' + elseif line =~# '^\s*<\!--' && line !~# '-->' + let waiting_on = '-->' + elseif line =~# '^[^"]*"""[^"]*$' + let waiting_on = '^[^"]*"""[^"]*$' + elseif a:detected.filetype ==# 'go' && line =~# '^[^`]*`[^`]*$' + let waiting_on = '^[^`]*`[^`]*$' + elseif has_heredocs + let waiting_on = matchstr(line, '<<\s*\([''"]\=\)\zs\w\+\ze\1[^''"`<>]*$') + if len(waiting_on) + let waiting_on = '^' . waiting_on . '$' + endif + endif + + let indent = len(matchstr(substitute(line, '\t', softtab, 'g'), '^ *')) + if line =~# '^\t' + let heuristics.hard += 1 + elseif line =~# '^' . softtab + let heuristics.soft += 1 + endif + if line =~# '^ ' + let heuristics.spaces += 1 + endif + let increment = prev_indent < 0 ? 0 : indent - prev_indent + let prev_indent = indent + let prev_line = line + if increment > 1 && (increment < 4 || increment % 4 == 0) + if has_key(heuristics.indents, increment) + let heuristics.indents[increment] += 1 + else + let heuristics.indents[increment] = 1 + endif + let heuristics.checked += 1 + endif + if heuristics.checked >= 32 && (heuristics.hard > 3 || heuristics.soft > 3) && get(heuristics.indents, increment) * 2 > heuristics.checked + if heuristics.spaces + break + elseif !exists('no_space_indent') + let no_space_indent = stridx("\n" . join(a:lines, "\n"), "\n ") < 0 + if no_space_indent + break + endif + endif + break + endif + endfor + + let a:detected.heuristics[a:source] = heuristics + + let max_frequency = 0 + for [shiftwidth, frequency] in items(heuristics.indents) + if frequency > max_frequency || frequency == max_frequency && +shiftwidth < get(options, 'shiftwidth') + let options.shiftwidth = +shiftwidth + let max_frequency = frequency + endif + endfor + + if heuristics.hard && !heuristics.spaces && + \ !has_key(a:detected.options, 'tabstop') + let options = {'expandtab': 0, 'shiftwidth': 0} + elseif heuristics.hard > heuristics.soft + let options.expandtab = 0 + let options.tabstop = tabstop + else + if heuristics.soft + let options.expandtab = 1 + endif + if heuristics.hard || has_key(a:detected.options, 'tabstop') || + \ stridx(join(a:lines, "\n"), "\t") >= 0 + let options.tabstop = tabstop + elseif !&g:shiftwidth && has_key(options, 'shiftwidth') && + \ !has_key(a:detected.options, 'shiftwidth') + let options.tabstop = options.shiftwidth + let options.shiftwidth = 0 + endif + endif + + call map(options, '[v:val, a:source]') + call extend(a:detected.options, options, 'keep') +endfunction + +function! s:Capture(cmd) abort + redir => capture + silent execute a:cmd + redir END + return capture +endfunction + +let s:modeline_numbers = { + \ 'shiftwidth': 'shiftwidth', 'sw': 'shiftwidth', + \ 'tabstop': 'tabstop', 'ts': 'tabstop', + \ 'textwidth': 'textwidth', 'tw': 'textwidth', + \ } +let s:modeline_booleans = { + \ 'expandtab': 'expandtab', 'et': 'expandtab', + \ 'fixendofline': 'fixendofline', 'fixeol': 'fixendofline', + \ } +function! s:ParseOptions(declarations, into, ...) abort + for option in a:declarations + if has_key(s:modeline_booleans, matchstr(option, '^\%(no\)\=\zs\w\+$')) + let a:into[s:modeline_booleans[matchstr(option, '^\%(no\)\=\zs\w\+')]] = [option !~# '^no'] + a:000 + elseif has_key(s:modeline_numbers, matchstr(option, '^\w\+\ze=[1-9]\d*$')) + let a:into[s:modeline_numbers[matchstr(option, '^\w\+')]] = [str2nr(matchstr(option, '\d\+$'))] + a:000 + elseif option =~# '^\%(ft\|filetype\)=[[:alnum:]._-]*$' + let a:into.filetype = [matchstr(option, '=\zs.*')] + a:000 + endif + if option ==# 'nomodeline' || option ==# 'noml' + return 1 + endif + endfor + return 0 +endfunction + +function! s:ModelineOptions() abort + let options = {} + if !&l:modeline && (&g:modeline || s:Capture('setlocal') =~# '\\\@' && + \ s:Capture('verbose setglobal modeline?') !=# s:Capture('verbose setlocal modeline?')) + return options + endif + let modelines = get(b:, 'sleuth_modelines', get(g:, 'sleuth_modelines', 5)) + if line('$') > 2 * modelines + let lnums = range(1, modelines) + range(line('$') - modelines + 1, line('$')) + else + let lnums = range(1, line('$')) + endif + for lnum in lnums + if s:ParseOptions(split(matchstr(getline(lnum), + \ '\%(\S\@= 0 + let cmd .= ' ' . setting + endif + if !&verbose || a:silent + if has_key(s:booleans, option) + let msg .= ' ' . (value[0] ? '' : 'no') . get(s:short_options, option, option) + else + let msg .= ' ' . get(s:short_options, option, option) . '=' . value[0] + endif + continue + endif + if len(value) > 1 + if value[1] ==# a:detected.bufname + let file = '%' + else + let file = value[1] =~# '/' ? fnamemodify(value[1], ':~:.') : value[1] + if file !=# value[1] && file[0:0] !=# '~' + let file = './' . file + endif + endif + if len(value) > 2 + let file .= ' line ' . value[2] + endif + echo printf(':setlocal %-21s " from %s', setting, file) + else + echo ':setlocal ' . setting + endif + endfor + if !&verbose && !empty(msg) && !a:silent + echo ':setlocal' . msg + endif + if has_key(options, 'shiftwidth') + let cmd .= ' softtabstop=' . (exists('*shiftwidth') ? -1 : options.shiftwidth[0]) + else + call s:Warn(':Sleuth failed to detect indent settings', a:silent) + endif + return cmd ==# 'setlocal' ? '' : cmd +endfunction + +function! s:UserOptions(ft, name) abort + if exists('b:sleuth_' . a:name) + let source = 'b:sleuth_' . a:name + elseif exists('g:sleuth_' . a:ft . '_' . a:name) + let source = 'g:sleuth_' . a:ft . '_' . a:name + endif + if !exists('l:source') || type(eval(source)) == type(function('tr')) + return {} + endif + let val = eval(source) + let options = {} + if type(val) == type('') + call s:ParseOptions(split(substitute(val, '\S\@= 0') + return options +endfunction + +function! s:DetectDeclared() abort + let detected = {'bufname': s:Slash(@%), 'declared': {}} + let absolute_or_empty = detected.bufname =~# '^$\|^\a\+:\|^/' + if &l:buftype =~# '^\%(nowrite\)\=$' && !absolute_or_empty + let detected.bufname = substitute(s:Slash(getcwd()), '/\=$', '/', '') . detected.bufname + let absolute_or_empty = 1 + endif + let detected.path = absolute_or_empty ? detected.bufname : '' + let pre = substitute(matchstr(detected.path, '^\a\a\+\ze:'), '^\a', '\u&', 'g') + if len(pre) && exists('*' . pre . 'Real') + let detected.path = s:Slash(call(pre . 'Real', [detected.path])) + endif + + try + if len(detected.path) && exists('*ExcludeBufferFromDiscovery') && !empty(ExcludeBufferFromDiscovery(detected.path, 'sleuth')) + let detected.path = '' + endif + catch + endtry + let [detected.editorconfig, detected.root] = s:DetectEditorConfig(detected.path) + call extend(detected.declared, s:EditorConfigToOptions(detected.editorconfig)) + call extend(detected.declared, s:ModelineOptions()) + return detected +endfunction + +function! s:DetectHeuristics(into) abort + let detected = a:into + let filetype = split(&l:filetype, '\.', 1)[0] + if get(detected, 'filetype', '*') ==# filetype + return detected + endif + let detected.filetype = filetype + let options = copy(detected.declared) + let detected.options = options + let detected.heuristics = {} + if has_key(detected, 'patterns') + call remove(detected, 'patterns') + endif + let detected.defaults = s:UserOptions(filetype, 'defaults') + if empty(filetype) || !get(b:, 'sleuth_automatic', 1) || empty(get(b:, 'sleuth_heuristics', get(g:, 'sleuth_' . filetype . '_heuristics', get(g:, 'sleuth_heuristics', 1)))) + return detected + endif + if s:Ready(detected) + return detected + endif + + let lines = getline(1, 1024) + call s:Guess(detected.bufname, detected, lines) + if s:Ready(detected) + return detected + elseif get(options, 'shiftwidth', [4])[0] < 4 && stridx(join(lines, "\n"), "\t") == -1 + let options.expandtab = [1, detected.bufname] + return detected + endif + let dir = len(detected.path) ? fnamemodify(detected.path, ':h') : '' + let root = len(detected.root) ? fnamemodify(detected.root, ':h') : dir ==# s:Slash(expand('~')) ? dir : fnamemodify(dir, ':h') + if detected.bufname =~# '^\a\a\+:' || root ==# '.' || !isdirectory(root) + let dir = '' + endif + let c = get(b:, 'sleuth_neighbor_limit', get(g:, 'sleuth_neighbor_limit', 8)) + if c <= 0 || empty(dir) + let detected.patterns = [] + elseif type(get(b:, 'sleuth_globs')) == type([]) + let detected.patterns = b:sleuth_globs + elseif type(get(g:, 'sleuth_' . detected.filetype . '_globs')) == type([]) + let detected.patterns = get(g:, 'sleuth_' . detected.filetype . '_globs') + else + let detected.patterns = ['*' . matchstr(detected.bufname, '/\@ 0 && dir !~# '^$\|^//[^/]*$' && dir !=# fnamemodify(dir, ':h') + for pattern in detected.patterns + for neighbor in split(glob(dir.'/'.pattern), "\n")[0:7] + if neighbor !=# detected.path && filereadable(neighbor) + call s:Guess(neighbor, detected, readfile(neighbor, '', 256)) + let c -= 1 + endif + if s:Ready(detected) + return detected + endif + if c <= 0 + break + endif + endfor + if c <= 0 + break + endif + endfor + if len(dir) <= len(root) + break + endif + let dir = fnamemodify(dir, ':h') + endwhile + if !has_key(options, 'shiftwidth') + let detected.options = copy(detected.declared) + endif + return detected +endfunction + +function! s:Init(redetect, unsafe, do_filetype, silent) abort + if !a:redetect && exists('b:sleuth.defaults') + let detected = b:sleuth + endif + unlet! b:sleuth + if &l:buftype !~# '^\%(nowrite\|nofile\|acwrite\)\=$' + return s:Warn(':Sleuth disabled for buftype=' . &l:buftype, a:silent) + endif + if &l:filetype ==# 'netrw' + return s:Warn(':Sleuth disabled for filetype=' . &l:filetype, a:silent) + endif + if &l:binary + return s:Warn(':Sleuth disabled for binary files', a:silent) + endif + if !exists('detected') + let detected = s:DetectDeclared() + endif + let setfiletype = '' + if a:do_filetype && has_key(detected.declared, 'filetype') + let filetype = detected.declared.filetype[0] + if filetype !=# &l:filetype || empty(filetype) + let setfiletype = 'setlocal filetype=' . filetype + else + let setfiletype = 'setfiletype ' . filetype + endif + endif + exe setfiletype + call s:DetectHeuristics(detected) + let cmd = s:Apply(detected, (a:do_filetype ? ['filetype'] : []) + (a:unsafe ? s:all_options : s:safe_options), a:silent) + let b:sleuth = detected + if exists('s:polyglot') && !a:silent + call s:Warn('Charlatan :Sleuth implementation in vim-polyglot has been found and disabled.') + call s:Warn('To get rid of this message, uninstall vim-polyglot, or disable the') + call s:Warn('corresponding feature in your vimrc:') + call s:Warn(' let g:polyglot_disabled = ["autoindent"]') + endif + return cmd +endfunction + +function! s:AutoInit() abort + return s:Init(1, 1, 1, 1) +endfunction + +function! s:Sleuth(line1, line2, range, bang, mods, args) abort + let safe = a:bang || expand("") =~# '\%(^\|\.\.\)FileType ' + return s:Init(!a:bang, !safe, !safe, 0) +endfunction + +if !exists('g:did_indent_on') && !get(g:, 'sleuth_no_filetype_indent_on') + filetype indent on +elseif !exists('g:did_load_filetypes') + filetype on +endif + +function! SleuthIndicator() abort + let sw = &shiftwidth ? &shiftwidth : &tabstop + if &expandtab + let ind = 'sw='.sw + elseif &tabstop == sw + let ind = 'ts='.&tabstop + else + let ind = 'sw='.sw.',ts='.&tabstop + endif + if &textwidth + let ind .= ',tw='.&textwidth + endif + if exists('&fixendofline') && !&fixendofline && !&endofline + let ind .= ',noeol' + endif + return ind +endfunction + +augroup sleuth + autocmd! + autocmd BufNewFile,BufReadPost * nested + \ if get(g:, 'sleuth_automatic', 1) + \ | exe s:AutoInit() | endif + autocmd BufFilePost * nested + \ if (@% !~# '^!' || exists('b:sleuth')) && get(g:, 'sleuth_automatic', 1) + \ | exe s:AutoInit() | endif + autocmd FileType * nested + \ if exists('b:sleuth') | exe s:Init(0, 0, 0, 1) | endif + autocmd User Flags call Hoist('buffer', 5, 'SleuthIndicator') +augroup END + +command! -bar -bang Sleuth exe s:Sleuth(, , +"", 0, "", ) diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/.github/ISSUE_TEMPLATE/bug_report.yml b/config/neovim/store/lazy-plugins/which-key.nvim/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..9b49cfd2 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,89 @@ +name: Bug Report +description: File a bug/issue +title: "bug: " +labels: [bug] +body: + - type: markdown + attributes: + value: | + **Before** reporting an issue, make sure to read the [documentation](https://github.com/folke/which-key.nvim) and search [existing issues](https://github.com/folke/which-key.nvim/issues). Usage questions such as ***"How do I...?"*** belong in [Discussions](https://github.com/folke/which-key.nvim/discussions) and will be closed. + - type: checkboxes + attributes: + label: Did you check docs and existing issues? + description: Make sure you checked all of the below before submitting an issue + options: + - label: I have read all the which-key.nvim docs + required: true + - label: I have searched the existing issues of which-key.nvim + required: true + - label: I have searched the existing issues of plugins related to this issue + required: true + - type: input + attributes: + label: "Neovim version (nvim -v)" + placeholder: "0.8.0 commit db1b0ee3b30f" + validations: + required: true + - type: input + attributes: + label: "Operating system/version" + placeholder: "MacOS 11.5" + validations: + required: true + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. Please include any related errors you see in Neovim. + validations: + required: true + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. + 2. + 3. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Repro + description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua` + value: | + -- DO NOT change the paths and don't remove the colorscheme + local root = vim.fn.fnamemodify("./.repro", ":p") + + -- set stdpaths to use .repro + for _, name in ipairs({ "config", "data", "state", "cache" }) do + vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name + end + + -- bootstrap lazy + local lazypath = root .. "/plugins/lazy.nvim" + if not vim.loop.fs_stat(lazypath) then + vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath, }) + end + vim.opt.runtimepath:prepend(lazypath) + + -- install plugins + local plugins = { + "folke/tokyonight.nvim", + { "folke/which-key.nvim", config = true }, + -- add any other plugins here + } + require("lazy").setup(plugins, { + root = root .. "/plugins", + }) + + vim.cmd.colorscheme("tokyonight") + -- add anything else here + render: Lua + validations: + required: false diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/.github/ISSUE_TEMPLATE/feature_request.yml b/config/neovim/store/lazy-plugins/which-key.nvim/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..59e9e2d3 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,36 @@ +name: Feature Request +description: Suggest a new feature +title: "feature: " +labels: [enhancement] +body: + - type: checkboxes + attributes: + label: Did you check the docs? + description: Make sure you read all the docs before submitting a feature request + options: + - label: I have read all the which-key.nvim docs + required: true + - type: textarea + validations: + required: true + attributes: + label: Is your feature request related to a problem? Please describe. + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + - type: textarea + validations: + required: true + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + - type: textarea + validations: + required: true + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + - type: textarea + validations: + required: false + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/.github/workflows/ci.yml b/config/neovim/store/lazy-plugins/which-key.nvim/.github/workflows/ci.yml new file mode 100644 index 00000000..6fa0cf9d --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +name: CI +on: + push: + pull_request: + +jobs: + tests: + strategy: + matrix: + # os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Install Neovim + shell: bash + run: | + mkdir -p /tmp/nvim + wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage + cd /tmp/nvim + chmod a+x ./nvim.appimage + ./nvim.appimage --appimage-extract + echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + - name: Run Tests + run: | + nvim --version + [ ! -d tests ] && exit 0 + nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua', sequential = true}" + docs: + runs-on: ubuntu-latest + needs: tests + if: ${{ github.ref == 'refs/heads/main' }} + steps: + - uses: actions/checkout@v3 + - name: panvimdoc + uses: kdheepak/panvimdoc@main + with: + vimdoc: which-key.nvim + version: "Neovim >= 0.8.0" + demojify: true + treesitter: true + - name: Push changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "chore(build): auto-generate vimdoc" + commit_user_name: "github-actions[bot]" + commit_user_email: "github-actions[bot]@users.noreply.github.com" + commit_author: "github-actions[bot] " + release: + name: release + if: ${{ github.ref == 'refs/heads/main' }} + needs: + - docs + - tests + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v3 + id: release + with: + release-type: simple + package-name: which-key.nvim + - uses: actions/checkout@v3 + - name: tag stable versions + if: ${{ steps.release.outputs.release_created }} + run: | + git config user.name github-actions[bot] + git config user.email github-actions[bot]@users.noreply.github.com + git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" + git tag -d stable || true + git push origin :stable || true + git tag -a stable -m "Last Stable Release" + git push origin stable diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/.gitignore b/config/neovim/store/lazy-plugins/which-key.nvim/.gitignore new file mode 100644 index 00000000..cc5457ab --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/.gitignore @@ -0,0 +1,8 @@ +tt.* +.tests +doc/tags +debug +.repro +foo.* +*.log +data diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/.lua-format b/config/neovim/store/lazy-plugins/which-key.nvim/.lua-format new file mode 100644 index 00000000..05ffc740 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/.lua-format @@ -0,0 +1,13 @@ +# https://github.com/Koihik/LuaFormatter/blob/master/docs/Style-Config.md +column_limit: 100 +indent_width: 2 +continuation_indent_width: 2 +use_tab: false +chop_down_parameter: true +chop_down_table: true +chop_down_kv_table: true +single_quote_to_double_quote: true +spaces_inside_table_braces: true +align_parameter: true +keep_simple_control_block_one_line: true +extra_sep_at_table_end: true diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/.neoconf.json b/config/neovim/store/lazy-plugins/which-key.nvim/.neoconf.json new file mode 100644 index 00000000..884566c6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/.neoconf.json @@ -0,0 +1,14 @@ +{ + "lspconfig": { + "sumneko_lua": { + "Lua.format.defaultConfig": { + "indent_style": "space", + "indent_size": "2", + "continuation_indent_size": "2" + }, + "Lua.diagnostics.neededFileStatus": { + // "codestyle-check": "Any" + } + } + } +} diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/CHANGELOG.md b/config/neovim/store/lazy-plugins/which-key.nvim/CHANGELOG.md new file mode 100644 index 00000000..21965670 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/CHANGELOG.md @@ -0,0 +1,334 @@ +# Changelog + +## [1.6.0](https://github.com/folke/which-key.nvim/compare/v1.5.1...v1.6.0) (2023-10-17) + + +### Features + +* **presets:** added gt and gT. Fixes [#457](https://github.com/folke/which-key.nvim/issues/457) ([3ba77f0](https://github.com/folke/which-key.nvim/commit/3ba77f0b0961b3fe685397b8d8f34f231b9350a6)) + + +### Bug Fixes + +* call config in issue template ([#489](https://github.com/folke/which-key.nvim/issues/489)) ([09a8188](https://github.com/folke/which-key.nvim/commit/09a8188224dc890618dfbc961436b106d912c2c1)) +* **view:** set modifiable flag for view buffer ([#506](https://github.com/folke/which-key.nvim/issues/506)) ([1d17760](https://github.com/folke/which-key.nvim/commit/1d1776012eda4258985f6f1f0c02b78594a3f37b)) + +## [1.5.1](https://github.com/folke/which-key.nvim/compare/v1.5.0...v1.5.1) (2023-07-15) + + +### Bug Fixes + +* revert: never overwrite actual keymaps with group names. Fixes [#478](https://github.com/folke/which-key.nvim/issues/478) Fixes [#479](https://github.com/folke/which-key.nvim/issues/479) Fixes [#480](https://github.com/folke/which-key.nvim/issues/480) ([fc25407](https://github.com/folke/which-key.nvim/commit/fc25407a360d27c36a30a90ff36861aa20ef2e54)) + +## [1.5.0](https://github.com/folke/which-key.nvim/compare/v1.4.3...v1.5.0) (2023-07-14) + + +### Features + +* **marks:** show filename as label when no label ([25babc6](https://github.com/folke/which-key.nvim/commit/25babc6add21c17d6391a585302aee5632266622)) + + +### Bug Fixes + +* **keys:** don't show empty groups ([8503c0d](https://github.com/folke/which-key.nvim/commit/8503c0d725420b37ac31e44753657cde91435597)) +* never overwrite actual keymaps with group names ([f61da3a](https://github.com/folke/which-key.nvim/commit/f61da3a3a6143b7a42b4b16e983004856ec26bd1)) +* **registers:** dont trigger on @. Fixes [#466](https://github.com/folke/which-key.nvim/issues/466) ([65b36cc](https://github.com/folke/which-key.nvim/commit/65b36cc258e857dea92fc11cdc0d6e2bb01d3e87)) + +## [1.4.3](https://github.com/folke/which-key.nvim/compare/v1.4.2...v1.4.3) (2023-05-22) + + +### Bug Fixes + +* **health:** dont show duplicates between global and buffer-local. It's too confusing ([015fdf3](https://github.com/folke/which-key.nvim/commit/015fdf3e3e052d4a9fee997ca0aa387c2dd3731c)) + +## [1.4.2](https://github.com/folke/which-key.nvim/compare/v1.4.1...v1.4.2) (2023-05-10) + + +### Bug Fixes + +* **health:** update the deprecated function ([#453](https://github.com/folke/which-key.nvim/issues/453)) ([12d3b11](https://github.com/folke/which-key.nvim/commit/12d3b11a67b94d65483f10c6ba0a47474039543a)) + +## [1.4.1](https://github.com/folke/which-key.nvim/compare/v1.4.0...v1.4.1) (2023-05-04) + + +### Bug Fixes + +* **keys:** dont overwrite existing keymaps with a callback. Fixes [#449](https://github.com/folke/which-key.nvim/issues/449) ([4db6bb0](https://github.com/folke/which-key.nvim/commit/4db6bb080b269ac155e5aa1696d26f2376c749ab)) + +## [1.4.0](https://github.com/folke/which-key.nvim/compare/v1.3.0...v1.4.0) (2023-04-18) + + +### Features + +* **view:** ensure it's above other floating windows ([#442](https://github.com/folke/which-key.nvim/issues/442)) ([9443778](https://github.com/folke/which-key.nvim/commit/94437786a0d0fde61284f8476ac142896878c2d7)) + +## [1.3.0](https://github.com/folke/which-key.nvim/compare/v1.2.3...v1.3.0) (2023-04-17) + + +### Features + +* **health:** move health check to separate health file ([b56c512](https://github.com/folke/which-key.nvim/commit/b56c5126752fcd498a81c6d8d1e7f51f251166eb)) +* **preset:** add `z<CR>` preset ([#346](https://github.com/folke/which-key.nvim/issues/346)) ([ed37330](https://github.com/folke/which-key.nvim/commit/ed3733059ffa281c8144e44f1b4819a771ddf4de)) +* **preset:** added `zi` and `CTRL-W_o` ([#378](https://github.com/folke/which-key.nvim/issues/378)) ([5e8e6b1](https://github.com/folke/which-key.nvim/commit/5e8e6b1c70d3fcbe2712453ef3ebbf07d0d2aff4)) +* **view:** allow percentages for margins. Fixes [#436](https://github.com/folke/which-key.nvim/issues/436) ([0b5a653](https://github.com/folke/which-key.nvim/commit/0b5a6537b66ee37d03c6c3f0e21fd147f817422d)) + + +### Bug Fixes + +* **health:** add OK output to check_health fn ([#375](https://github.com/folke/which-key.nvim/issues/375)) ([c9c430a](https://github.com/folke/which-key.nvim/commit/c9c430ab19a3bf8dd394dd9925a3a219063276b9)) +* **keys:** allow keymap desc to override preset labels. Fixes [#386](https://github.com/folke/which-key.nvim/issues/386) ([6aa1b2f](https://github.com/folke/which-key.nvim/commit/6aa1b2fa93a2a26a1bd752080ec6a51beb009e75)) +* **tree:** don't cache plugin nodes. Fixes [#441](https://github.com/folke/which-key.nvim/issues/441) ([20fcd7b](https://github.com/folke/which-key.nvim/commit/20fcd7b602a2c58d634eaa1f1d28b16a6acbfad3)) +* **util:** clear cache when leader changes ([df3597f](https://github.com/folke/which-key.nvim/commit/df3597f7dc0f379bda865e3c9dd6303fa6e4c959)) +* **util:** missing return statement ([f6bb21c](https://github.com/folke/which-key.nvim/commit/f6bb21c8c1d72008783466e80e0c993ef056a3a9)) +* **util:** nil check ([6ab25e2](https://github.com/folke/which-key.nvim/commit/6ab25e24ec2b2a8fb88f43eb13feb21e5042c280)) + + +### Performance Improvements + +* **keys:** optimized `update_keymaps` ([476d137](https://github.com/folke/which-key.nvim/commit/476d13754db0da7831fc3581fb243cd7f0d3e581)) +* **tree:** added fast nodes lookup ([8e5e012](https://github.com/folke/which-key.nvim/commit/8e5e0126aaff9bd73eb25a6d5568f6b5bdff58f0)) +* **util:** cache parse_keys ([8649bf5](https://github.com/folke/which-key.nvim/commit/8649bf5c66b8fa1fa6ee879b9af78e89f886d13c)) +* **util:** cache replace termcodes ([eaa8027](https://github.com/folke/which-key.nvim/commit/eaa80272ef488c68cd51698c64e795767c6e0624)) + +## [1.2.3](https://github.com/folke/which-key.nvim/compare/v1.2.2...v1.2.3) (2023-04-17) + + +### Bug Fixes + +* **util:** dont parse empty lhs ([8d5ab76](https://github.com/folke/which-key.nvim/commit/8d5ab76836d89be1c761a4ed61bf700d98c71e5d)) +* **util:** only collect valid <> keys ([#438](https://github.com/folke/which-key.nvim/issues/438)) ([4bd6dca](https://github.com/folke/which-key.nvim/commit/4bd6dcaa6d7e1650590303f0066d32aa6762d8f3)) +* **util:** replace `<lt>` by `<` before parsing ([789ac71](https://github.com/folke/which-key.nvim/commit/789ac718ee7a2b49dd82409e3d7cf45b52ea95ce)) +* **view:** allow deviating paddings per side ([#400](https://github.com/folke/which-key.nvim/issues/400)) ([3090eaf](https://github.com/folke/which-key.nvim/commit/3090eafb780da76eb4876986081551db80bf35cd)) + + +### Performance Improvements + +* **util:** simplify and optimize parsers ([#435](https://github.com/folke/which-key.nvim/issues/435)) ([b0ebb67](https://github.com/folke/which-key.nvim/commit/b0ebb6722c77dda1ab1e3ce13521fe7db20cbc79)) + +## [1.2.2](https://github.com/folke/which-key.nvim/compare/v1.2.1...v1.2.2) (2023-04-16) + + +### Performance Improvements + +* **mappings:** avoid computing error string on hot path ([#429](https://github.com/folke/which-key.nvim/issues/429)) ([6892f16](https://github.com/folke/which-key.nvim/commit/6892f165bb984561f8cac298a6747da338d04668)) + +## [1.2.1](https://github.com/folke/which-key.nvim/compare/v1.2.0...v1.2.1) (2023-03-26) + + +### Bug Fixes + +* **icons:** fixed obsolete icons with nerdfix ([151f21d](https://github.com/folke/which-key.nvim/commit/151f21d34d50fc53506ddc9d8ec58234202df795)) +* **view:** wrong window position when statusline is not set ([#363](https://github.com/folke/which-key.nvim/issues/363)) ([e14f8dc](https://github.com/folke/which-key.nvim/commit/e14f8dc6304e774ce005d09f7feebbd191fe20f9)) + +## [1.2.0](https://github.com/folke/which-key.nvim/compare/v1.1.1...v1.2.0) (2023-03-01) + + +### Features + +* enable spelling plugin by default ([6d886f4](https://github.com/folke/which-key.nvim/commit/6d886f4dcaa25d1fe20e332f779fe1edb726d063)) +* make delay configurable for marks/registers/spelling. Fixes [#379](https://github.com/folke/which-key.nvim/issues/379). Fixes [#152](https://github.com/folke/which-key.nvim/issues/152), Fixes [#220](https://github.com/folke/which-key.nvim/issues/220), Fixes [#334](https://github.com/folke/which-key.nvim/issues/334) ([5649320](https://github.com/folke/which-key.nvim/commit/56493205745597abdd8d3ceb22f502ffe74784f5)) + +## [1.1.1](https://github.com/folke/which-key.nvim/compare/v1.1.0...v1.1.1) (2023-02-10) + + +### Bug Fixes + +* remove duplicate kaymap ([#361](https://github.com/folke/which-key.nvim/issues/361)) ([9a4680e](https://github.com/folke/which-key.nvim/commit/9a4680e95b7026c58f0a377de0f13ee2507ece7a)) + +## [1.1.0](https://github.com/folke/which-key.nvim/compare/v1.0.0...v1.1.0) (2023-01-10) + + +### Features + +* Hide mapping when `desc = "which_key_ignore"` ([#391](https://github.com/folke/which-key.nvim/issues/391)) ([fd07b61](https://github.com/folke/which-key.nvim/commit/fd07b6137f1e362a66df04f7c7055b99319e3a4d)) + + +### Bug Fixes + +* visual-multi compatibility ([#389](https://github.com/folke/which-key.nvim/issues/389)) ([#385](https://github.com/folke/which-key.nvim/issues/385)) ([01334bb](https://github.com/folke/which-key.nvim/commit/01334bb48c53231fc8b2e2932215bfee05474904)) + +## 1.0.0 (2023-01-04) + + +### Features + +* add <C-w>_ to misc ([#296](https://github.com/folke/which-key.nvim/issues/296)) ([03b8c1d](https://github.com/folke/which-key.nvim/commit/03b8c1dde8c02f187869c56a6019d5e2578f7af7)) +* add preset key to mappings for API usage ([ed7d6c5](https://github.com/folke/which-key.nvim/commit/ed7d6c523ae8ef7b8059d2fee0836009e71bcd0c)) +* added a winblend option for the floating window ([#161](https://github.com/folke/which-key.nvim/issues/161)) ([d3032b6](https://github.com/folke/which-key.nvim/commit/d3032b6d3e0adb667975170f626cb693bfc66baa)) +* added duplicate mapping checks to checkhealth [#34](https://github.com/folke/which-key.nvim/issues/34) ([710c5f8](https://github.com/folke/which-key.nvim/commit/710c5f81da2c34e6e0f361d87cfca27207e1b994)) +* added healthcheck to check for conflicting keymaps ([44d3c3f](https://github.com/folke/which-key.nvim/commit/44d3c3f9307930ce8c877383d51fca1a353982d8)) +* added ignore_missing option to hide any keymap for which no label exists [#60](https://github.com/folke/which-key.nvim/issues/60) ([1ccba9d](https://github.com/folke/which-key.nvim/commit/1ccba9d0b553b08feaca9f432386f9c33bd1656f)) +* added operators plugin ([c7f8496](https://github.com/folke/which-key.nvim/commit/c7f84968e44f1a9ab9687ddf0b3dc5465e48bc75)) +* added option to configure scroll bindings inside the popup ([#175](https://github.com/folke/which-key.nvim/issues/175)) ([a54ef5f](https://github.com/folke/which-key.nvim/commit/a54ef5f5db5819ee65a5ec3dea9bae64476c5017)) +* added options to align columns left, center or right [#82](https://github.com/folke/which-key.nvim/issues/82) ([2467fb1](https://github.com/folke/which-key.nvim/commit/2467fb15e8775928fba3d7d20a68b64852f44122)) +* added settings to disable the WhichKey popup for certain buftypes and filetyes ([fb276a0](https://github.com/folke/which-key.nvim/commit/fb276a07c7dc305e48ecc2683e4bd28cda49499a)) +* added support for expr mappings ([9d2785c](https://github.com/folke/which-key.nvim/commit/9d2785c4d44b4a8ca1095856cb4ee34a32497cf6)) +* added triggers_blacklist to blacklist certain whichkey hooks [#73](https://github.com/folke/which-key.nvim/issues/73) ([ec1474b](https://github.com/folke/which-key.nvim/commit/ec1474bb0c373eb583962deff20860c2af54f932)) +* added WhichKeyBorder highlight group ([9c190ea](https://github.com/folke/which-key.nvim/commit/9c190ea91939eba8c2d45660127e0403a5300b5a)) +* allow functions to be passed to create keybindings. Implements [#31](https://github.com/folke/which-key.nvim/issues/31) ([cf644cd](https://github.com/folke/which-key.nvim/commit/cf644cd9a0e989ad3e0a6dffb98beced742f3297)) +* allow manual setup of triggers [#30](https://github.com/folke/which-key.nvim/issues/30) ([423a50c](https://github.com/folke/which-key.nvim/commit/423a50cccfeb8b812e0e89f156316a4bd9d2673a)) +* allow mapping to have multiple modes as a table ([0d559fa](https://github.com/folke/which-key.nvim/commit/0d559fa5573aa48c4822e8874315316bd075e17e)) +* allow mode to be set on a single mapping ([2a08d58](https://github.com/folke/which-key.nvim/commit/2a08d58658e1de0fae3b44e21e8ed72399465701)) +* allow overriding key labels [#77](https://github.com/folke/which-key.nvim/issues/77) ([2be929e](https://github.com/folke/which-key.nvim/commit/2be929e34b2f2b982e6b978c0bd94cd2e1d500e6)) +* allow to close popup with <c-c> [#33](https://github.com/folke/which-key.nvim/issues/33) ([410523a](https://github.com/folke/which-key.nvim/commit/410523a6d7bcbcab73f8c7b0fc567893d7cd8c44)) +* better logging ([c39df95](https://github.com/folke/which-key.nvim/commit/c39df95881a6cd8ac27fce5926dc2dc1b4597df9)) +* better support for plugin actions with custom lua function ([222a8ee](https://github.com/folke/which-key.nvim/commit/222a8eeaf727f9b1b767424198f7c71274c04d43)) +* builtin key mappings ([0063ceb](https://github.com/folke/which-key.nvim/commit/0063ceb161475097885d567500fe764358983c62)) +* check for rogue existsing WhichKey mappings and show error. WK handles triggers automatically, no need to define them ([db97a30](https://github.com/folke/which-key.nvim/commit/db97a301fb7691b61cd6c975e3cc060fb53fd980)) +* easily reset WK with plenary for development of WK ([55b4dab](https://github.com/folke/which-key.nvim/commit/55b4dabab649d59e657917eb17c9d57716817719)) +* expose registers to customize order ([2b83fe7](https://github.com/folke/which-key.nvim/commit/2b83fe74dee00763e4c037d198c88ff11c843914)) +* for nvim 0.7.0 or higher, use native keymap callbacks instead of which key functions ([5e96cf9](https://github.com/folke/which-key.nvim/commit/5e96cf950a864a4600512c90f2080b0b6f0eacb7)) +* group symbol ([5e02b66](https://github.com/folke/which-key.nvim/commit/5e02b66b9e7add373967b798552a7cc9a427efb4)) +* handle [count] with motion. Implements [#11](https://github.com/folke/which-key.nvim/issues/11) ([d93ef0f](https://github.com/folke/which-key.nvim/commit/d93ef0f2f1a9a6288016a3a82f70399e350a574f)) +* hide mapping boilerplate ([#6](https://github.com/folke/which-key.nvim/issues/6)) ([b3357de](https://github.com/folke/which-key.nvim/commit/b3357de005f27a3cc6aabe922e8ee308470d9343)) +* honor timeoutlen when typing an operator followed by i or a instead of showing immediately ([54d1b3a](https://github.com/folke/which-key.nvim/commit/54d1b3ab3ed9132142f2139964cfa68d018b38c5)) +* initial commit ([970e79f](https://github.com/folke/which-key.nvim/commit/970e79f7016f6cc2a89dad8c50e2e89657684f55)) +* keyamp functions ([801cc81](https://github.com/folke/which-key.nvim/commit/801cc810f4d57eca029261f383b2483ec21e5824)) +* make custom operators configurable (fixes [#9](https://github.com/folke/which-key.nvim/issues/9)) ([81875d8](https://github.com/folke/which-key.nvim/commit/81875d875f7428c7a087e0d051744c7b3f9dc1b3)) +* make help message configurable ([7b1c6aa](https://github.com/folke/which-key.nvim/commit/7b1c6aa23061a9ed1acdfec3d20dc5e361ec01a3)) +* Make keypress message configuratble ([#351](https://github.com/folke/which-key.nvim/issues/351)) ([fd2422f](https://github.com/folke/which-key.nvim/commit/fd2422fb7030510cf9c3304047e653e8adcd8f20)) +* motions plugin ([f989fcf](https://github.com/folke/which-key.nvim/commit/f989fcfeafd4fd333a8e87617fce39a449ae81ca)) +* new keymap dsl ([#352](https://github.com/folke/which-key.nvim/issues/352)) Docs to come ([fbf0381](https://github.com/folke/which-key.nvim/commit/fbf038110edb5e2cbecaac57570aae2c9fa2939c)) +* option to make some triggers show immediately, regardless of timeoutlen ([3a52dc0](https://github.com/folke/which-key.nvim/commit/3a52dc02b6e542d5cd216381ccfa108943bab17c)) +* plugin for registers ([5415832](https://github.com/folke/which-key.nvim/commit/541583280fab4ea96900f35fb6b5ffb8de103a4c)) +* plugin support + first builtin marks plugin ([9d5e631](https://github.com/folke/which-key.nvim/commit/9d5e6311c20970741eaaf7a3950c1a33de5eedaa)) +* prefer `desc` to `cmd` as the fallback label ([#253](https://github.com/folke/which-key.nvim/issues/253)) ([bd4411a](https://github.com/folke/which-key.nvim/commit/bd4411a2ed4dd8bb69c125e339d837028a6eea71)) +* preset with misc keybindings ([e610338](https://github.com/folke/which-key.nvim/commit/e61033858b8d5208a49c24d70eb9576cbd22e887)) +* set keymap desc when creating new mappings based on the WhichKey labels ([f4518ca](https://github.com/folke/which-key.nvim/commit/f4518ca50193a545681ba65ba0c5bb8a8479c5b5)) +* set popup filetype to WhichKey and buftype to nofile [#86](https://github.com/folke/which-key.nvim/issues/86) ([20682f1](https://github.com/folke/which-key.nvim/commit/20682f189a0c452203f6365f66eccb0407b20936)) +* show a warning if <leader> is already mapped, even if it's ([ac56f45](https://github.com/folke/which-key.nvim/commit/ac56f45095e414c820f621423611aac4027f74bd)) +* show breadcrumb and help on command line ([c27535c](https://github.com/folke/which-key.nvim/commit/c27535ca085c05ade1e23b3b347e39e53c24d33a)) +* show keys and help in float when cmdheight == 0 ([f645017](https://github.com/folke/which-key.nvim/commit/f64501787bebe9ff28c10dbe470ffad5dd017769)) +* show/hide a fake cursor when WK is open ([0f53f40](https://github.com/folke/which-key.nvim/commit/0f53f40c1b827d35771c82a5c47c5a54d9408f7c)) +* spelling suggestion plugin ([4b74f21](https://github.com/folke/which-key.nvim/commit/4b74f218f4541991a40719286f96cce9447a89c4)) +* support for custom text object completion. Fixes [#10](https://github.com/folke/which-key.nvim/issues/10) ([394ff5a](https://github.com/folke/which-key.nvim/commit/394ff5a37bab051857de4216ee25db2284de2196)) +* support opts.remap for keymap ([#339](https://github.com/folke/which-key.nvim/issues/339)) ([6885b66](https://github.com/folke/which-key.nvim/commit/6885b669523ff4238de99a7c653d47b081b5506d)) +* support using lua function for expr ([#110](https://github.com/folke/which-key.nvim/issues/110)) ([e0dce15](https://github.com/folke/which-key.nvim/commit/e0dce1552ea37964ae6ac7144709867544eae7f3)) +* text objects ([d255b71](https://github.com/folke/which-key.nvim/commit/d255b71992494ce4998caae7fe281144fb669abb)) +* WhichKey vim command to show arbitrary keymaps ([df615d4](https://github.com/folke/which-key.nvim/commit/df615d44987a8bfe8910c618164f696e227ecfd4)) + + +### Bug Fixes + +* :norm .. commands keep feeding <esc> at the end of the command [#58](https://github.com/folke/which-key.nvim/issues/58) ([d66ffdd](https://github.com/folke/which-key.nvim/commit/d66ffdd5a845c713f581ac6da36173e88096e0fa)) +* add delay option to macro key ([#152](https://github.com/folke/which-key.nvim/issues/152)) ([#156](https://github.com/folke/which-key.nvim/issues/156)) ([bd226c4](https://github.com/folke/which-key.nvim/commit/bd226c4d02d7f360747364a59cc5f0da50524f2c)) +* add remaining <esc> to pending in case there's no other characters ([29a82b5](https://github.com/folke/which-key.nvim/commit/29a82b575b9752a45b005327030948ce8cb513a0)) +* add triggers for other modes in marks and register plugin ([#116](https://github.com/folke/which-key.nvim/issues/116)) ([bbfc640](https://github.com/folke/which-key.nvim/commit/bbfc640c44612d705f4b0670ec1387c8a6ff2c7c)) +* added @ trigger for showing registers ([01b6676](https://github.com/folke/which-key.nvim/commit/01b66769480fac14f6efa7c31327234398d05837)) +* added builtin plugins to config ([6e461ca](https://github.com/folke/which-key.nvim/commit/6e461caec3d3aa43f1fa2b7890b299705bccfe8d)) +* added hidden option to disable the popup on motion counts (motions.count) ([ea975ef](https://github.com/folke/which-key.nvim/commit/ea975ef254f10c4938cd663a7c4fb14e2d7514c0)) +* added support for operator pending keymaps ([1f6b510](https://github.com/folke/which-key.nvim/commit/1f6b510f6ef0c223b51f3599200bbf6abc30f909)) +* added z= for spelling correction ([59603de](https://github.com/folke/which-key.nvim/commit/59603dee2f67f623a520148d60c634f6f56f6017)) +* always escape <leader> when it's a backslash ([41636a3](https://github.com/folke/which-key.nvim/commit/41636a3be909af5d20d811f8ce6a304a5ee3cc21)) +* always execute keys with remap, but unhook / hook WK triggers (Fixes [#8](https://github.com/folke/which-key.nvim/issues/8)) ([bf329df](https://github.com/folke/which-key.nvim/commit/bf329df0ee11d6c80c7208b40eab74368e963245)) +* always map <leader>, even without register ([512631c](https://github.com/folke/which-key.nvim/commit/512631c1bdce96dd048115cb139ea3a8452a931a)) +* always unhook and ignore errors ([01a60cd](https://github.com/folke/which-key.nvim/commit/01a60cd5929b395042c8ba3d872f6f25ccd55ecb)) +* always use noremap=false for <plug> commands ([9b9cece](https://github.com/folke/which-key.nvim/commit/9b9cece006b78ff7527a35285a4b5c1359d70fd8)) +* always use word under the cursor for spelling suggestions ([c5b19ec](https://github.com/folke/which-key.nvim/commit/c5b19ecf4d1d8f8c77ee982caf9792740f6d5e53)) +* better handling of weird norm and getchar endless <esc> bug [#68](https://github.com/folke/which-key.nvim/issues/68) ([bfd37e9](https://github.com/folke/which-key.nvim/commit/bfd37e93761d622328c673828b537d5671389413)) +* better sorting ([99e8940](https://github.com/folke/which-key.nvim/commit/99e894032afbe2543dbbf9bba05518d96b852aa0)) +* center alignemnt should be an integer ([db85198](https://github.com/folke/which-key.nvim/commit/db851981595fc360e9b6196a7c3995611aceac3b)) +* check for FloatBorder before setting winhighlight ([af6b91d](https://github.com/folke/which-key.nvim/commit/af6b91dc09e4ed830d8cd4a3652a5b3f80ccefac)) +* check is hook exists before unhooking ([f6cf3a2](https://github.com/folke/which-key.nvim/commit/f6cf3a2e49c09aba739c0f6fc85d3aebf2b96cb6)) +* cmd can be nil ([060a574](https://github.com/folke/which-key.nvim/commit/060a574c228433e9b17960fa0eafca0a975381e8)) +* **colors:** Separator links to DiffAdd ([#302](https://github.com/folke/which-key.nvim/issues/302)) ([a2749c5](https://github.com/folke/which-key.nvim/commit/a2749c5b039ad34734c98f8752b9fb5da7ceac55)) +* Compatibility with Visual Multi plug ([#278](https://github.com/folke/which-key.nvim/issues/278)) ([92916b6](https://github.com/folke/which-key.nvim/commit/92916b6cede0ffd7d5c1ce9abad93ec0c4d9635e)) +* convert trings with strtrans to properly render non printable characters ([d85ce36](https://github.com/folke/which-key.nvim/commit/d85ce3627f4060f622e4c0a9657f26c0151829de)) +* correct floating window position in Neovim 0.6 nightly ([#176](https://github.com/folke/which-key.nvim/issues/176)) ([a35a910](https://github.com/folke/which-key.nvim/commit/a35a910d28683294fd23d35dd03c06f6f7c37b17)) +* correctly handle counts before commands [#17](https://github.com/folke/which-key.nvim/issues/17) ([4feb319](https://github.com/folke/which-key.nvim/commit/4feb319ff89fb8659efa2a788f808bc390afa490)) +* correctly unhook buffer local mappings before executing keys ([4f98b47](https://github.com/folke/which-key.nvim/commit/4f98b4713ea9d4534662ceb7b542b0626eeb9ea8)) +* disable folding on whichkey popup. Fixes [#99](https://github.com/folke/which-key.nvim/issues/99) ([78821de](https://github.com/folke/which-key.nvim/commit/78821de0b633275d6934660e67989639bc7a784c)) +* disable operator pending maps for now ([#2](https://github.com/folke/which-key.nvim/issues/2)) ([0cd66a8](https://github.com/folke/which-key.nvim/commit/0cd66a84520fc0e7e3eec81f081157541cb48dbd)) +* do feedkeys in correct mode when dealing with operator pending commands. Fixes [#8](https://github.com/folke/which-key.nvim/issues/8) ([cf30788](https://github.com/folke/which-key.nvim/commit/cf307886b68ed53334ffdcee809a751376269e33)) +* don't show <esc> mappings since closes the popup ([09db756](https://github.com/folke/which-key.nvim/commit/09db756b5d357767a635a4d169e2e820b2962ea8)) +* don't show spelling when the command was started with a count [#80](https://github.com/folke/which-key.nvim/issues/80) ([20a85bd](https://github.com/folke/which-key.nvim/commit/20a85bd8bc54a11cf040aafa5d60f8a735eecfbd)) +* dont do feedkeys when user uses WhichKey command with non existing prefix ([f9537ce](https://github.com/folke/which-key.nvim/commit/f9537ce0f7457665e3b90d82c5f3f2c37fe0506f)) +* dont pass zero counts ([0c3cfb0](https://github.com/folke/which-key.nvim/commit/0c3cfb0064ceec5b182bac580033e0654d9575e6)) +* dont show errors about loading order of setup and register ([2adbc17](https://github.com/folke/which-key.nvim/commit/2adbc17e00061073f2c2a40b6420ee2a80ea458d)) +* explicitely check if we try to execute an auto which-key mapping. shouldn't happen, but still safer to check ([30fdd46](https://github.com/folke/which-key.nvim/commit/30fdd465433d48cab3b1f894daf52fa0005cf7ac)) +* expose presets so one can change them if needed [#70](https://github.com/folke/which-key.nvim/issues/70) ([46ea686](https://github.com/folke/which-key.nvim/commit/46ea686c6cc9bfc96bc492c76a76d43548a587c4)) +* feed CTRL-O again if called from CTRL-O ([#145](https://github.com/folke/which-key.nvim/issues/145)) ([833b5ea](https://github.com/folke/which-key.nvim/commit/833b5ea1a0d4b3bddf4b5c68fc89f1234960edec)) +* feed the keys as typed ([#333](https://github.com/folke/which-key.nvim/issues/333)) ([33b4e72](https://github.com/folke/which-key.nvim/commit/33b4e72a07546bc4798b4bafb99ae06df47bd790)) +* fix flickering on tmux ([f112602](https://github.com/folke/which-key.nvim/commit/f11260251ad942ba1635db9bc25c2efaf75caf0a)) +* fix issue when cmdheight=0 [#301](https://github.com/folke/which-key.nvim/issues/301) ([#305](https://github.com/folke/which-key.nvim/issues/305)) ([9cd09ca](https://github.com/folke/which-key.nvim/commit/9cd09ca6bbe5acfbce86ca023fdc720f6aa132d6)) +* fixed 0 after an operator. Wrongly assumed any number to be a count for following op mode, but not the case for 0 [#59](https://github.com/folke/which-key.nvim/issues/59) [#61](https://github.com/folke/which-key.nvim/issues/61) ([36616ca](https://github.com/folke/which-key.nvim/commit/36616cacba5d9eb716017bf23b7bbbe4cb4a6822)) +* fixed possible nil error when showing marks ([b44fc09](https://github.com/folke/which-key.nvim/commit/b44fc095f6d0144278f3413533ad2d40ae664229)) +* for sporadic loss of lua function for mapping ([#216](https://github.com/folke/which-key.nvim/issues/216)) ([312c386](https://github.com/folke/which-key.nvim/commit/312c386ee0eafc925c27869d2be9c11ebdb807eb)) +* formatting of text-objects plugin ([442d2d3](https://github.com/folke/which-key.nvim/commit/442d2d383284390c5ee1b922036fc10fff530b2d)) +* get value of register '=' with getreg('=',1) ([#114](https://github.com/folke/which-key.nvim/issues/114)) ([6224ea8](https://github.com/folke/which-key.nvim/commit/6224ea81f505c66a9644f89129149b108f722e56)) +* handle backslash as localleader [#47](https://github.com/folke/which-key.nvim/issues/47) ([cd23fdc](https://github.com/folke/which-key.nvim/commit/cd23fdc1b0cbdb22769bed5cb275a6d1c4bd9bfc)) +* handle baskslashes when leader or localleader isn't set ([d155ab3](https://github.com/folke/which-key.nvim/commit/d155ab3bef11a8156995b47d5552586e5c9f66a3)) +* handle keymaps with a <nop> rhs as non existing and possibly overwrite them with WK hooks [#35](https://github.com/folke/which-key.nvim/issues/35) ([402be18](https://github.com/folke/which-key.nvim/commit/402be18dc656897b1dc68c88fab4ffe8635b8209)) +* handle nvim_{buf_}get_keymap return no rhs due to 'callback' mapping ([#223](https://github.com/folke/which-key.nvim/issues/223)) ([28d2bd1](https://github.com/folke/which-key.nvim/commit/28d2bd129575b5e9ebddd88506601290bb2bb221)) +* handle possible errors when getting last expression register [#64](https://github.com/folke/which-key.nvim/issues/64) ([7a1be6f](https://github.com/folke/which-key.nvim/commit/7a1be6ff950c7fb94a4f9e9bdb428a514e569503)) +* highlighting of line number in marks ([9997d93](https://github.com/folke/which-key.nvim/commit/9997d93e5adcf0352aa73c42d3c395ba775600e9)) +* immediately show registers and marks. Fixes [#144](https://github.com/folke/which-key.nvim/issues/144) ([653ce71](https://github.com/folke/which-key.nvim/commit/653ce711e6c27416ac79c4811ff814e9a38fddcf)) +* link default WhichKeyBorder to FloatBorder. Fixes [#331](https://github.com/folke/which-key.nvim/issues/331) ([1698d6d](https://github.com/folke/which-key.nvim/commit/1698d6d0ff0b00b8499d9aea8715d120dc526900)) +* make register selection work in INSERT mode ([d4315f8](https://github.com/folke/which-key.nvim/commit/d4315f8991da816c30e9387a891c02774552dc36)) +* make spelling suggestions also work for correctly spelled words ([d02dc34](https://github.com/folke/which-key.nvim/commit/d02dc344bdaf273dfde7672f3f8e70a307593f62)) +* make sure we never accidentally show WK triggers ([197b4d3](https://github.com/folke/which-key.nvim/commit/197b4d3403c04c0045e8d541e8cd2504aba5f168)) +* make which-key's lazy loading work when it is also lazy-loaded ([7d929b9](https://github.com/folke/which-key.nvim/commit/7d929b96e2588fe9710ad795402eaead1aa0f70f)) +* manual command now uses proper escaping for prefix ([334fcca](https://github.com/folke/which-key.nvim/commit/334fcca64611dbca8c0c669260f4fb2a8ff81509)) +* mapleader=\ ([b5c8985](https://github.com/folke/which-key.nvim/commit/b5c89851d580459c1dd33ecbda611ae06e22eec4)) +* mapping when right-hand side is `nil` ([#323](https://github.com/folke/which-key.nvim/issues/323)) ([1d449d4](https://github.com/folke/which-key.nvim/commit/1d449d44e01787ef17dc7b0672eec01a8121b36e)) +* never hook in SELECT mode and properly handle v, x, s [#45](https://github.com/folke/which-key.nvim/issues/45) [#46](https://github.com/folke/which-key.nvim/issues/46) ([2844e1c](https://github.com/folke/which-key.nvim/commit/2844e1cbf298129afa58c13a90f91be907232dbf)) +* never hook j and k in INSERT mode automatcally to prevent jk kj <ESC> mappings to work as intended ([9a2faed](https://github.com/folke/which-key.nvim/commit/9a2faed055459d3226634344468f78bf85d77fa8)) +* never hook numbers. locks up due to v:count. Fixes [#118](https://github.com/folke/which-key.nvim/issues/118) ([2d2954a](https://github.com/folke/which-key.nvim/commit/2d2954a1d05b4f074e022e64db9aa6093d439bb0)) +* never hook on <esc> ([fd08322](https://github.com/folke/which-key.nvim/commit/fd0832233bd0c733618fab1c3df92f261c13d6b3)) +* never hook q [#63](https://github.com/folke/which-key.nvim/issues/63) ([95ae9d2](https://github.com/folke/which-key.nvim/commit/95ae9d2d00e8714379e64994e69ae17fc540a7d6)) +* never hook to operators in visual mode [#61](https://github.com/folke/which-key.nvim/issues/61) ([43d799a](https://github.com/folke/which-key.nvim/commit/43d799ad0e6218964e802ff342ca5f9352105175)) +* nil in health check ([5c018ae](https://github.com/folke/which-key.nvim/commit/5c018ae412b235abe17e24b46057564db0944dc4)) +* nvim_win_close force = true ([ca73a0e](https://github.com/folke/which-key.nvim/commit/ca73a0e03f142067a16891b712c7ea73ac646dff)) +* nvim-0.7.0 check ([#338](https://github.com/folke/which-key.nvim/issues/338)) ([1491c35](https://github.com/folke/which-key.nvim/commit/1491c355ec9bb0ec4c8e71c8625bc5f55a54b925)) +* only create mappings for builtin operators. plugings will always have their own mappings ([1b2ec76](https://github.com/folke/which-key.nvim/commit/1b2ec760d65ce9eda473879bec5c31c4771079e7)) +* only enable plugins that are specified in the configuration ([b8ed0e8](https://github.com/folke/which-key.nvim/commit/b8ed0e8e675b747ce21aa830c38ddf4fb2458e05)) +* only show message about existing <leader> mapping in NORMAL mode [#75](https://github.com/folke/which-key.nvim/issues/75) ([bcc8297](https://github.com/folke/which-key.nvim/commit/bcc829775b7d366f61bd2db1753e2c6b3d1ec4d3)) +* only show up/down when scrolling is posible (fixes [#4](https://github.com/folke/which-key.nvim/issues/4)) ([9e7986d](https://github.com/folke/which-key.nvim/commit/9e7986d8726291ee93ef448ae8c452981f1fc75f)) +* override <leader> if it's mapped to ([928288b](https://github.com/folke/which-key.nvim/commit/928288b543d77c38ade936ee8bdef32a769ebe3a)) +* pass + and * regsiters to feedkeys [#36](https://github.com/folke/which-key.nvim/issues/36) ([ce37f41](https://github.com/folke/which-key.nvim/commit/ce37f41641edb90bf51b975999553d13961ed8fa)) +* pass 0 instead of nil for current buffer ([#227](https://github.com/folke/which-key.nvim/issues/227)) ([387fd67](https://github.com/folke/which-key.nvim/commit/387fd676d3f9b419d38890820f6e262dc0fadb46)) +* passing registers in INSERT mode, is not by pasting them 😅 [#62](https://github.com/folke/which-key.nvim/issues/62) ([342c8cd](https://github.com/folke/which-key.nvim/commit/342c8cdb3651967c96c356eb2d79561c0c9273ee)) +* place popup correctly respecting cmdheight [#28](https://github.com/folke/which-key.nvim/issues/28) ([490e4d5](https://github.com/folke/which-key.nvim/commit/490e4d55315b74c63a63ada89ecf0e660a94db9a)) +* possible nil value in health check ([b1627ca](https://github.com/folke/which-key.nvim/commit/b1627caa25e24c580bbc88377942353875f93a41)) +* possible recursion ([f7fef32](https://github.com/folke/which-key.nvim/commit/f7fef32701aba0a822ac0a82679aea454bec702f)) +* prevent double escaping of key codes ([1676611](https://github.com/folke/which-key.nvim/commit/167661151204ea7da2d365113a76ab223b3dc880)) +* properly escape sequence ([2473329](https://github.com/folke/which-key.nvim/commit/24733293bb7b28f3d98d4a88323eb13cbe5b46f2)) +* properly escape terminal chars to see if we already hooked a trigger ([1bee8a1](https://github.com/folke/which-key.nvim/commit/1bee8a151e72e5738d813964492248c9bbc4c5ba)) +* properly format unicode text in columns (fixes [#66](https://github.com/folke/which-key.nvim/issues/66)) ([e3066fa](https://github.com/folke/which-key.nvim/commit/e3066facb6ed91ac013e4ff8faf24997ed44459c)) +* properly handle < chatracters (should be [#40](https://github.com/folke/which-key.nvim/issues/40) ([c4a59d7](https://github.com/folke/which-key.nvim/commit/c4a59d76135563ea73beb87cf0d6d7a3302563be)) +* start of visual selection mark should be <lt> instead of < [#69](https://github.com/folke/which-key.nvim/issues/69) ([840311c](https://github.com/folke/which-key.nvim/commit/840311c272eda2c4fc0d92070e9ef2dd13f884e7)) +* typo ([4bacbfd](https://github.com/folke/which-key.nvim/commit/4bacbfdacb9eebee339d36243fe17b9185ccbb74)) +* use buffer instead of bufnr + added warning ([df49a59](https://github.com/folke/which-key.nvim/commit/df49a59efdfd6a90f412aa251914183fec8593af)) +* use Comment as fallback color for the Separator ([7ee35a7](https://github.com/folke/which-key.nvim/commit/7ee35a7614e34e562fd3f815ad35bd6d7e456093)) +* use config.key_labels for cmdline trail as well (Fixes [#108](https://github.com/folke/which-key.nvim/issues/108)) ([1872dd8](https://github.com/folke/which-key.nvim/commit/1872dd8ca9daa0f6478a7771087aedae8518cb97)) +* use mode instead of redraw when cmdheight=0. (Fixes [#327](https://github.com/folke/which-key.nvim/issues/327)) ([c966279](https://github.com/folke/which-key.nvim/commit/c96627900191355e6788629bbf5239d7295221f0)) +* use secret nop bindings to make sure timeoutlen is always respected ([eccd5f8](https://github.com/folke/which-key.nvim/commit/eccd5f8bf22e60620eee833946638b90552c9b69)) +* use strwidth instead of strdisplaywidth ([386591e](https://github.com/folke/which-key.nvim/commit/386591e24afe88c1c52c2291d450e7d7ad9cf02a)) + + +### Performance Improvements + +* as long as we didnt finish loading, queue registers ([1bac978](https://github.com/folke/which-key.nvim/commit/1bac978464fd00dddbeee9c5584120f553b1a660)) +* defer loading to VimEnter and only process hooks once when ready ([84ddcdc](https://github.com/folke/which-key.nvim/commit/84ddcdcd862c4bb6dcac84a876f66f9777ecef7c)) +* no need to create triggers for all levels. first level that is not a cmd is enough ([3cc0424](https://github.com/folke/which-key.nvim/commit/3cc042498db5792b8f3b081310926c779c7aac07)) +* no need to hook buffer-local if we have a global hook for a certain prefix ([bb5e0d9](https://github.com/folke/which-key.nvim/commit/bb5e0d9be9c73b7d343ff4bf0ffbb9b6b4696811)) +* only load modules when needed ([6f8ae23](https://github.com/folke/which-key.nvim/commit/6f8ae23540bc5f980862d2d5aa6d3c02bb1e2da0)) diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/LICENSE b/config/neovim/store/lazy-plugins/which-key.nvim/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/README.md b/config/neovim/store/lazy-plugins/which-key.nvim/README.md new file mode 100644 index 00000000..7458b66b --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/README.md @@ -0,0 +1,367 @@ +# 💥 Which Key + +**WhichKey** is a lua plugin for Neovim 0.5 that displays a popup with possible key bindings of +the command you started typing. Heavily inspired by the original [emacs-which-key](https://github.com/justbur/emacs-which-key) and [vim-which-key](https://github.com/liuchengxu/vim-which-key). + +![image](https://user-images.githubusercontent.com/292349/116439438-669f8d00-a804-11eb-9b5b-c7122bd9acac.png) + +## ✨ Features + +- for Neovim 0.7 and higher, it uses the `desc` attributes of your mappings as the default label +- for Neovim 0.7 and higher, new mappings will be created with a `desc` attribute +- opens a popup with suggestions to complete a key binding +- works with any setting for [timeoutlen](https://neovim.io/doc/user/options.html#'timeoutlen'), including instantly (`timeoutlen=0`) +- works correctly with built-in key bindings +- works correctly with buffer-local mappings +- extensible plugin architecture +- built-in plugins: + - **marks:** shows your marks when you hit one of the jump keys. + - **registers:** shows the contents of your registers + - **presets:** built-in key binding help for `motions`, `text-objects`, `operators`, `windows`, `nav`, `z` and `g` + - **spelling:** spelling suggestions inside the which-key popup + +## ⚡️ Requirements + +- Neovim >= 0.5.0 + +## 📦 Installation + +Install the plugin with your preferred package manager: + +### [lazy.nvim](https://github.com/folke/lazy.nvim) + +```lua +{ + "folke/which-key.nvim", + event = "VeryLazy", + init = function() + vim.o.timeout = true + vim.o.timeoutlen = 300 + end, + opts = { + -- your configuration comes here + -- or leave it empty to use the default settings + -- refer to the configuration section below + } +} +``` + +### [packer](https://github.com/wbthomason/packer.nvim) + +```lua +-- Lua +use { + "folke/which-key.nvim", + config = function() + vim.o.timeout = true + vim.o.timeoutlen = 300 + require("which-key").setup { + -- your configuration comes here + -- or leave it empty to use the default settings + -- refer to the configuration section below + } + end +} +``` + +## ⚙️ Configuration + +> ❗️ IMPORTANT: the [timeout](https://neovim.io/doc/user/options.html#'timeout') when **WhichKey** opens is controlled by the vim setting [timeoutlen](https://neovim.io/doc/user/options.html#'timeoutlen'). +> Please refer to the documentation to properly set it up. Setting it to `0`, will effectively +> always show **WhichKey** immediately, but a setting of `500` (500ms) is probably more appropriate. + +> ❗️ don't create any keymappings yourself to trigger WhichKey. Unlike with _vim-which-key_, we do this fully automatically. +> Please remove any left-over triggers you might have from using _vim-which-key_. + +> 🚑 You can run `:checkhealth which-key` to see if there's any conflicting keymaps that will prevent triggering **WhichKey** + +WhichKey comes with the following defaults: + +```lua +{ + plugins = { + marks = true, -- shows a list of your marks on ' and ` + registers = true, -- shows your registers on " in NORMAL or in INSERT mode + -- the presets plugin, adds help for a bunch of default keybindings in Neovim + -- No actual key bindings are created + spelling = { + enabled = true, -- enabling this will show WhichKey when pressing z= to select spelling suggestions + suggestions = 20, -- how many suggestions should be shown in the list? + }, + presets = { + operators = true, -- adds help for operators like d, y, ... + motions = true, -- adds help for motions + text_objects = true, -- help for text objects triggered after entering an operator + windows = true, -- default bindings on + nav = true, -- misc bindings to work with windows + z = true, -- bindings for folds, spelling and others prefixed with z + g = true, -- bindings for prefixed with g + }, + }, + -- add operators that will trigger motion and text object completion + -- to enable all native operators, set the preset / operators plugin above + operators = { gc = "Comments" }, + key_labels = { + -- override the label used to display some keys. It doesn't effect WK in any other way. + -- For example: + -- [""] = "SPC", + -- [""] = "RET", + -- [""] = "TAB", + }, + motions = { + count = true, + }, + icons = { + breadcrumb = "»", -- symbol used in the command line area that shows your active key combo + separator = "➜", -- symbol used between a key and it's label + group = "+", -- symbol prepended to a group + }, + popup_mappings = { + scroll_down = "", -- binding to scroll down inside the popup + scroll_up = "", -- binding to scroll up inside the popup + }, + window = { + border = "none", -- none, single, double, shadow + position = "bottom", -- bottom, top + margin = { 1, 0, 1, 0 }, -- extra window margin [top, right, bottom, left]. When between 0 and 1, will be treated as a percentage of the screen size. + padding = { 1, 2, 1, 2 }, -- extra window padding [top, right, bottom, left] + winblend = 0, -- value between 0-100 0 for fully opaque and 100 for fully transparent + zindex = 1000, -- positive value to position WhichKey above other floating windows. + }, + layout = { + height = { min = 4, max = 25 }, -- min and max height of the columns + width = { min = 20, max = 50 }, -- min and max width of the columns + spacing = 3, -- spacing between columns + align = "left", -- align columns left, center or right + }, + ignore_missing = false, -- enable this to hide mappings for which you didn't specify a label + hidden = { "", "", "", "", "^:", "^ ", "^call ", "^lua " }, -- hide mapping boilerplate + show_help = true, -- show a help message in the command line for using WhichKey + show_keys = true, -- show the currently pressed key and its label as a message in the command line + triggers = "auto", -- automatically setup triggers + -- triggers = {""} -- or specifiy a list manually + -- list of triggers, where WhichKey should not wait for timeoutlen and show immediately + triggers_nowait = { + -- marks + "`", + "'", + "g`", + "g'", + -- registers + '"', + "", + -- spelling + "z=", + }, + triggers_blacklist = { + -- list of mode / prefixes that should never be hooked by WhichKey + -- this is mostly relevant for keymaps that start with a native binding + i = { "j", "k" }, + v = { "j", "k" }, + }, + -- disable the WhichKey popup for certain buf types and file types. + -- Disabled by default for Telescope + disable = { + buftypes = {}, + filetypes = {}, + }, +} +``` + +## 🪄 Setup + +With the default settings, **WhichKey** will work out of the box for most builtin keybindings, +but the real power comes from documenting and organizing your own keybindings. + +To document and/or setup your own mappings, you need to call the `register` method + +```lua +local wk = require("which-key") +wk.register(mappings, opts) +``` + +Default options for `opts` + +```lua +{ + mode = "n", -- NORMAL mode + -- prefix: use "f" for example for mapping everything related to finding files + -- the prefix is prepended to every mapping part of `mappings` + prefix = "", + buffer = nil, -- Global mappings. Specify a buffer number for buffer local mappings + silent = true, -- use `silent` when creating keymaps + noremap = true, -- use `noremap` when creating keymaps + nowait = false, -- use `nowait` when creating keymaps + expr = false, -- use `expr` when creating keymaps +} +``` + +> ❕ When you specify a command in your mapping that starts with ``, then we automatically set `noremap=false`, since you always want recursive keybindings in this case + +### ⌨️ Mappings + +> ⌨ for **Neovim 0.7** and higher, which key will use the `desc` attribute of existing mappings as the default label + +Group names use the special `name` key in the tables. There's multiple ways to define the mappings. `wk.register` can be called multiple times from anywhere in your config files. + +```lua +local wk = require("which-key") +-- As an example, we will create the following mappings: +-- * ff find files +-- * fr show recent files +-- * fb Foobar +-- we'll document: +-- * fn new file +-- * fe edit file +-- and hide 1 + +wk.register({ + f = { + name = "file", -- optional group name + f = { "Telescope find_files", "Find File" }, -- create a binding with label + r = { "Telescope oldfiles", "Open Recent File", noremap=false, buffer = 123 }, -- additional options for creating the keymap + n = { "New File" }, -- just a label. don't create any mapping + e = "Edit File", -- same as above + ["1"] = "which_key_ignore", -- special label to hide it in the popup + b = { function() print("bar") end, "Foobar" } -- you can also pass functions! + }, +}, { prefix = "" }) +``` + +
+Click to see more examples + +```lua +-- all of the mappings below are equivalent + +-- method 2 +wk.register({ + [""] = { + f = { + name = "+file", + f = { "Telescope find_files", "Find File" }, + r = { "Telescope oldfiles", "Open Recent File" }, + n = { "enew", "New File" }, + }, + }, +}) + +-- method 3 +wk.register({ + ["f"] = { + name = "+file", + f = { "Telescope find_files", "Find File" }, + r = { "Telescope oldfiles", "Open Recent File" }, + n = { "enew", "New File" }, + }, +}) + +-- method 4 +wk.register({ + ["f"] = { name = "+file" }, + ["ff"] = { "Telescope find_files", "Find File" }, + ["fr"] = { "Telescope oldfiles", "Open Recent File" }, + ["fn"] = { "enew", "New File" }, +}) +``` + +
+ +**Tips:** The default label is `keymap.desc` or `keymap.rhs` or `""`, +`:h nvim_set_keymap()` to get more details about `desc` and `rhs`. + +### 🚙 Operators, Motions and Text Objects + +**WhichKey** provides help to work with operators, motions and text objects. + +> `[count]operator[count][text-object]` + +- operators can be configured with the `operators` option + - set `plugins.presets.operators` to `true` to automatically configure vim built-in operators + - set this to `false`, to only include the list you configured in the `operators` option. + - see [here](https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua#L5) for the full list part of the preset +- text objects are automatically retrieved from **operator pending** key maps (`omap`) + - set `plugins.presets.text_objects` to `true` to configure built-in text objects + - see [here](https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua#L43) +- motions are part of the preset `plugins.presets.motions` setting + - see [here](https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua#L20) + +
+How to disable some operators? (like v) + +```lua +-- make sure to run this code before calling setup() +-- refer to the full lists at https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua +local presets = require("which-key.plugins.presets") +presets.operators["v"] = nil +``` + +
+ +## 🚀 Usage + +When the **WhichKey** popup is open, you can use the following key bindings (they are also displayed at the bottom of the screen): + +- hit one of the keys to open a group or execute a key binding +- `` to cancel and close the popup +- `` go up one level +- `` scroll down +- `` scroll up + +Apart from the automatic opening, you can also manually open **WhichKey** for a certain `prefix`: + +> ❗️ don't create any keymappings yourself to trigger WhichKey. Unlike with _vim-which-key_, we do this fully automatically. +> Please remove any left-over triggers you might have from using _vim-which-key_. + +```vim +:WhichKey " show all mappings +:WhichKey " show all mappings +:WhichKey v " show all mappings for VISUAL mode +:WhichKey '' v " show ALL mappings for VISUAL mode +``` + +## 🔥 Plugins + +Four built-in plugins are included with **WhichKey**. + +### Marks + +Shows a list of your buffer local and global marks when you hit \` or ' + +![image](https://user-images.githubusercontent.com/292349/116439573-8f278700-a804-11eb-80ca-bb9263e6d937.png) + +### Registers + +Shows a list of your buffer local and global registers when you hit " in _NORMAL_ mode, or `` in _INSERT_ mode. + +![image](https://user-images.githubusercontent.com/292349/116439609-98b0ef00-a804-11eb-9385-97c7d5ff4113.png) + +### Presets + +Built-in key binding help for `motions`, `text-objects`, `operators`, `windows`, `nav`, `z` and `g` + +![image](https://user-images.githubusercontent.com/292349/116439871-df9ee480-a804-11eb-9529-800e167db65c.png) + +### Spelling + +When enabled, this plugin hooks into `z=` and replaces the full-screen spelling suggestions window by a list of suggestions within **WhichKey**. + +![image](https://user-images.githubusercontent.com/292349/118102022-1c361880-b38d-11eb-8e82-79ad266d9bb8.png) + +## 🎨 Colors + +The table below shows all the highlight groups defined for **WhichKey** with their default link. + +| Highlight Group | Defaults to | Description | +| ------------------- | ----------- | ------------------------------------------- | +| _WhichKey_ | Function | the key | +| _WhichKeyGroup_ | Keyword | a group | +| _WhichKeySeparator_ | DiffAdd | the separator between the key and its label | +| _WhichKeyDesc_ | Identifier | the label of the key | +| _WhichKeyFloat_ | NormalFloat | Normal in the popup window | +| _WhichKeyBorder_ | FloatBorder | Normal in the popup window | +| _WhichKeyValue_ | Comment | used by plugins that provide values | + + + + diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/TODO.md b/config/neovim/store/lazy-plugins/which-key.nvim/TODO.md new file mode 100644 index 00000000..15afdb0e --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/TODO.md @@ -0,0 +1,21 @@ +# Todo + +* [x] hook into all groups +* [x] show mappings without keymap (zz etc) +* [x] plugin support for marks, registers, text objects +* [x] `` to go up a level +* [x] config modes +* [x] update buf only +* [x] + thingy for groups +* [x] text objects +* [x] get label from global when not found for buffer +* [x] operators & motions +* [x] show window after timeout? +* [x] make plugins a list of key value with config in value +* [x] cleanup text objects text +* [x] buf local mappings seems to interfere with global mappings (push K in help) +* [x] fix help in visual mode +* [x] Plug>whichkey nop +* [x] preset plugin +* [x] command should auto stuff +* [x] timeoutlen is always respected and should still work when zero diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/doc/which-key.nvim.txt b/config/neovim/store/lazy-plugins/which-key.nvim/doc/which-key.nvim.txt new file mode 100644 index 00000000..67a9d425 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/doc/which-key.nvim.txt @@ -0,0 +1,419 @@ +*which-key.nvim.txt* For Neovim >= 0.8.0 Last change: 2023 October 20 + +============================================================================== +Table of Contents *which-key.nvim-table-of-contents* + +1. Which Key |which-key.nvim-which-key| + - Features |which-key.nvim-which-key-features| + - Requirements |which-key.nvim-which-key-requirements| + - Installation |which-key.nvim-which-key-installation| + - Configuration |which-key.nvim-which-key-configuration| + - Setup |which-key.nvim-which-key-setup| + - Usage |which-key.nvim-which-key-usage| + - Plugins |which-key.nvim-which-key-plugins| + - Colors |which-key.nvim-which-key-colors| + +============================================================================== +1. Which Key *which-key.nvim-which-key* + +**WhichKey** is a lua plugin for Neovim 0.5 that displays a popup with possible +key bindings of the command you started typing. Heavily inspired by the +original emacs-which-key and +vim-which-key . + + +FEATURES *which-key.nvim-which-key-features* + +- for Neovim 0.7 and higher, it uses the `desc` attributes of your mappings as the default label +- for Neovim 0.7 and higher, new mappings will be created with a `desc` attribute +- opens a popup with suggestions to complete a key binding +- works with any setting for |timeoutlen|, including instantly (`timeoutlen=0`) +- works correctly with built-in key bindings +- works correctly with buffer-local mappings +- extensible plugin architecture +- built-in plugins: + - **marks:** shows your marks when you hit one of the jump keys. + - **registers:** shows the contents of your registers + - **presets:** built-in key binding help for `motions`, `text-objects`, `operators`, `windows`, `nav`, `z` and `g` + - **spelling:** spelling suggestions inside the which-key popup + + +REQUIREMENTS *which-key.nvim-which-key-requirements* + +- Neovim >= 0.5.0 + + +INSTALLATION *which-key.nvim-which-key-installation* + +Install the plugin with your preferred package manager: + + +LAZY.NVIM ~ + +>lua + { + "folke/which-key.nvim", + event = "VeryLazy", + init = function() + vim.o.timeout = true + vim.o.timeoutlen = 300 + end, + opts = { + -- your configuration comes here + -- or leave it empty to use the default settings + -- refer to the configuration section below + } + } +< + + +PACKER ~ + +>lua + -- Lua + use { + "folke/which-key.nvim", + config = function() + vim.o.timeout = true + vim.o.timeoutlen = 300 + require("which-key").setup { + -- your configuration comes here + -- or leave it empty to use the default settings + -- refer to the configuration section below + } + end + } +< + + +CONFIGURATION *which-key.nvim-which-key-configuration* + + + IMPORTANT: the |timeout| when **WhichKey** opens is controlled by the vim + setting |timeoutlen|. Please refer to the documentation to properly set it up. + Setting it to `0`, will effectively always show **WhichKey** immediately, but a + setting of `500` (500ms) is probably more appropriate. + + don’t create any keymappings yourself to trigger WhichKey. Unlike with + _vim-which-key_, we do this fully automatically. Please remove any left-over + triggers you might have from using _vim-which-key_. + + You can run `:checkhealth which-key` to see if there’s any conflicting + keymaps that will prevent triggering **WhichKey** +WhichKey comes with the following defaults: + +>lua + { + plugins = { + marks = true, -- shows a list of your marks on ' and ` + registers = true, -- shows your registers on " in NORMAL or in INSERT mode + -- the presets plugin, adds help for a bunch of default keybindings in Neovim + -- No actual key bindings are created + spelling = { + enabled = true, -- enabling this will show WhichKey when pressing z= to select spelling suggestions + suggestions = 20, -- how many suggestions should be shown in the list? + }, + presets = { + operators = true, -- adds help for operators like d, y, ... + motions = true, -- adds help for motions + text_objects = true, -- help for text objects triggered after entering an operator + windows = true, -- default bindings on + nav = true, -- misc bindings to work with windows + z = true, -- bindings for folds, spelling and others prefixed with z + g = true, -- bindings for prefixed with g + }, + }, + -- add operators that will trigger motion and text object completion + -- to enable all native operators, set the preset / operators plugin above + operators = { gc = "Comments" }, + key_labels = { + -- override the label used to display some keys. It doesn't effect WK in any other way. + -- For example: + -- [""] = "SPC", + -- [""] = "RET", + -- [""] = "TAB", + }, + motions = { + count = true, + }, + icons = { + breadcrumb = "»", -- symbol used in the command line area that shows your active key combo + separator = "➜", -- symbol used between a key and it's label + group = "+", -- symbol prepended to a group + }, + popup_mappings = { + scroll_down = "", -- binding to scroll down inside the popup + scroll_up = "", -- binding to scroll up inside the popup + }, + window = { + border = "none", -- none, single, double, shadow + position = "bottom", -- bottom, top + margin = { 1, 0, 1, 0 }, -- extra window margin [top, right, bottom, left]. When between 0 and 1, will be treated as a percentage of the screen size. + padding = { 1, 2, 1, 2 }, -- extra window padding [top, right, bottom, left] + winblend = 0, -- value between 0-100 0 for fully opaque and 100 for fully transparent + zindex = 1000, -- positive value to position WhichKey above other floating windows. + }, + layout = { + height = { min = 4, max = 25 }, -- min and max height of the columns + width = { min = 20, max = 50 }, -- min and max width of the columns + spacing = 3, -- spacing between columns + align = "left", -- align columns left, center or right + }, + ignore_missing = false, -- enable this to hide mappings for which you didn't specify a label + hidden = { "", "", "", "", "^:", "^ ", "^call ", "^lua " }, -- hide mapping boilerplate + show_help = true, -- show a help message in the command line for using WhichKey + show_keys = true, -- show the currently pressed key and its label as a message in the command line + triggers = "auto", -- automatically setup triggers + -- triggers = {""} -- or specifiy a list manually + -- list of triggers, where WhichKey should not wait for timeoutlen and show immediately + triggers_nowait = { + -- marks + "`", + "'", + "g`", + "g'", + -- registers + '"', + "", + -- spelling + "z=", + }, + triggers_blacklist = { + -- list of mode / prefixes that should never be hooked by WhichKey + -- this is mostly relevant for keymaps that start with a native binding + i = { "j", "k" }, + v = { "j", "k" }, + }, + -- disable the WhichKey popup for certain buf types and file types. + -- Disabled by default for Telescope + disable = { + buftypes = {}, + filetypes = {}, + }, + } +< + + +SETUP *which-key.nvim-which-key-setup* + +With the default settings, **WhichKey** will work out of the box for most +builtin keybindings, but the real power comes from documenting and organizing +your own keybindings. + +To document and/or setup your own mappings, you need to call the `register` +method + +>lua + local wk = require("which-key") + wk.register(mappings, opts) +< + +Default options for `opts` + +>lua + { + mode = "n", -- NORMAL mode + -- prefix: use "f" for example for mapping everything related to finding files + -- the prefix is prepended to every mapping part of `mappings` + prefix = "", + buffer = nil, -- Global mappings. Specify a buffer number for buffer local mappings + silent = true, -- use `silent` when creating keymaps + noremap = true, -- use `noremap` when creating keymaps + nowait = false, -- use `nowait` when creating keymaps + expr = false, -- use `expr` when creating keymaps + } +< + + + When you specify a command in your mapping that starts with ``, then we + automatically set `noremap=false`, since you always want recursive keybindings + in this case + +MAPPINGS ~ + + + for **Neovim 0.7** and higher, which key will use the `desc` attribute of + existing mappings as the default label +Group names use the special `name` key in the tables. There’s multiple ways +to define the mappings. `wk.register` can be called multiple times from +anywhere in your config files. + +>lua + local wk = require("which-key") + -- As an example, we will create the following mappings: + -- * ff find files + -- * fr show recent files + -- * fb Foobar + -- we'll document: + -- * fn new file + -- * fe edit file + -- and hide 1 + + wk.register({ + f = { + name = "file", -- optional group name + f = { "Telescope find_files", "Find File" }, -- create a binding with label + r = { "Telescope oldfiles", "Open Recent File", noremap=false, buffer = 123 }, -- additional options for creating the keymap + n = { "New File" }, -- just a label. don't create any mapping + e = "Edit File", -- same as above + ["1"] = "which_key_ignore", -- special label to hide it in the popup + b = { function() print("bar") end, "Foobar" } -- you can also pass functions! + }, + }, { prefix = "" }) +< + +Click to see more examples ~ + +>lua + -- all of the mappings below are equivalent + + -- method 2 + wk.register({ + [""] = { + f = { + name = "+file", + f = { "Telescope find_files", "Find File" }, + r = { "Telescope oldfiles", "Open Recent File" }, + n = { "enew", "New File" }, + }, + }, + }) + + -- method 3 + wk.register({ + ["f"] = { + name = "+file", + f = { "Telescope find_files", "Find File" }, + r = { "Telescope oldfiles", "Open Recent File" }, + n = { "enew", "New File" }, + }, + }) + + -- method 4 + wk.register({ + ["f"] = { name = "+file" }, + ["ff"] = { "Telescope find_files", "Find File" }, + ["fr"] = { "Telescope oldfiles", "Open Recent File" }, + ["fn"] = { "enew", "New File" }, + }) +< + +**Tips:** The default label is `keymap.desc` or `keymap.rhs` or `""`, +|nvim_set_keymap()| to get more details about `desc` and `rhs`. + + +OPERATORS, MOTIONS AND TEXT OBJECTS ~ + +**WhichKey** provides help to work with operators, motions and text objects. + + + `[count]operator[count][text-object]` +- operators can be configured with the `operators` option + - set `plugins.presets.operators` to `true` to automatically configure vim built-in operators + - set this to `false`, to only include the list you configured in the `operators` option. + - see here for the full list part of the preset +- text objects are automatically retrieved from **operator pending** key maps (`omap`) + - set `plugins.presets.text_objects` to `true` to configure built-in text objects + - see here +- motions are part of the preset `plugins.presets.motions` setting + - see here + +How to disable some operators? (like v) ~ + +>lua + -- make sure to run this code before calling setup() + -- refer to the full lists at https://github.com/folke/which-key.nvim/blob/main/lua/which-key/plugins/presets/init.lua + local presets = require("which-key.plugins.presets") + presets.operators["v"] = nil +< + + +USAGE *which-key.nvim-which-key-usage* + +When the **WhichKey** popup is open, you can use the following key bindings +(they are also displayed at the bottom of the screen): + +- hit one of the keys to open a group or execute a key binding +- `` to cancel and close the popup +- `` go up one level +- `` scroll down +- `` scroll up + +Apart from the automatic opening, you can also manually open **WhichKey** for a +certain `prefix` + + + don’t create any keymappings yourself to trigger WhichKey. Unlike with + _vim-which-key_, we do this fully automatically. Please remove any left-over + triggers you might have from using _vim-which-key_. +>vim + :WhichKey " show all mappings + :WhichKey " show all mappings + :WhichKey v " show all mappings for VISUAL mode + :WhichKey '' v " show ALL mappings for VISUAL mode +< + + +PLUGINS *which-key.nvim-which-key-plugins* + +Four built-in plugins are included with **WhichKey**. + + +MARKS ~ + +Shows a list of your buffer local and global marks when you hit ` or ’ + + +REGISTERS ~ + +Shows a list of your buffer local and global registers when you hit ” in +_NORMAL_ mode, or `` in _INSERT_ mode. + + +PRESETS ~ + +Built-in key binding help for `motions`, `text-objects`, `operators`, +`windows`, `nav`, `z` and `g` + + +SPELLING ~ + +When enabled, this plugin hooks into `z=` and replaces the full-screen spelling +suggestions window by a list of suggestions within **WhichKey**. + + +COLORS *which-key.nvim-which-key-colors* + +The table below shows all the highlight groups defined for **WhichKey** with +their default link. + + --------------------------------------------------------------------------- + Highlight Group Defaults to Description + ------------------- ------------- ----------------------------------------- + WhichKey Function the key + + WhichKeyGroup Keyword a group + + WhichKeySeparator DiffAdd the separator between the key and its + label + + WhichKeyDesc Identifier the label of the key + + WhichKeyFloat NormalFloat Normal in the popup window + + WhichKeyBorder FloatBorder Normal in the popup window + + WhichKeyValue Comment used by plugins that provide values + --------------------------------------------------------------------------- +============================================================================== +2. Links *which-key.nvim-links* + +1. *image*: https://user-images.githubusercontent.com/292349/116439438-669f8d00-a804-11eb-9b5b-c7122bd9acac.png +2. *image*: https://user-images.githubusercontent.com/292349/116439573-8f278700-a804-11eb-80ca-bb9263e6d937.png +3. *image*: https://user-images.githubusercontent.com/292349/116439609-98b0ef00-a804-11eb-9385-97c7d5ff4113.png +4. *image*: https://user-images.githubusercontent.com/292349/116439871-df9ee480-a804-11eb-9529-800e167db65c.png +5. *image*: https://user-images.githubusercontent.com/292349/118102022-1c361880-b38d-11eb-8e82-79ad266d9bb8.png + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/colors.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/colors.lua new file mode 100644 index 00000000..c0922635 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/colors.lua @@ -0,0 +1,19 @@ +local M = {} + +local links = { + [""] = "Function", + Separator = "Comment", + Group = "Keyword", + Desc = "Identifier", + Float = "NormalFloat", + Border = "FloatBorder", + Value = "Comment", +} + +function M.setup() + for k, v in pairs(links) do + vim.api.nvim_set_hl(0, "WhichKey" .. k, { link = v, default = true }) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/config.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/config.lua new file mode 100644 index 00000000..bd97e907 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/config.lua @@ -0,0 +1,105 @@ +local M = {} + +M.namespace = vim.api.nvim_create_namespace("WhichKey") + +---@class Options +local defaults = { + plugins = { + marks = true, -- shows a list of your marks on ' and ` + registers = true, -- shows your registers on " in NORMAL or in INSERT mode + -- the presets plugin, adds help for a bunch of default keybindings in Neovim + -- No actual key bindings are created + spelling = { + enabled = true, -- enabling this will show WhichKey when pressing z= to select spelling suggestions + suggestions = 20, -- how many suggestions should be shown in the list? + }, + presets = { + operators = true, -- adds help for operators like d, y, ... + motions = true, -- adds help for motions + text_objects = true, -- help for text objects triggered after entering an operator + windows = true, -- default bindings on + nav = true, -- misc bindings to work with windows + z = true, -- bindings for folds, spelling and others prefixed with z + g = true, -- bindings for prefixed with g + }, + }, + -- add operators that will trigger motion and text object completion + -- to enable all native operators, set the preset / operators plugin above + operators = { gc = "Comments" }, + key_labels = { + -- override the label used to display some keys. It doesn't effect WK in any other way. + -- For example: + -- [""] = "SPC", + -- [""] = "RET", + -- [""] = "TAB", + }, + motions = { + count = true, + }, + icons = { + breadcrumb = "»", -- symbol used in the command line area that shows your active key combo + separator = "➜", -- symbol used between a key and it's label + group = "+", -- symbol prepended to a group + }, + popup_mappings = { + scroll_down = "", -- binding to scroll down inside the popup + scroll_up = "", -- binding to scroll up inside the popup + }, + window = { + border = "none", -- none, single, double, shadow + position = "bottom", -- bottom, top + margin = { 1, 0, 1, 0 }, -- extra window margin [top, right, bottom, left]. When between 0 and 1, will be treated as a percentage of the screen size. + padding = { 1, 2, 1, 2 }, -- extra window padding [top, right, bottom, left] + winblend = 0, -- value between 0-100 0 for fully opaque and 100 for fully transparent + zindex = 1000, -- positive value to position WhichKey above other floating windows. + }, + layout = { + height = { min = 4, max = 25 }, -- min and max height of the columns + width = { min = 20, max = 50 }, -- min and max width of the columns + spacing = 3, -- spacing between columns + align = "left", -- align columns left, center or right + }, + ignore_missing = false, -- enable this to hide mappings for which you didn't specify a label + hidden = { "", "", "", "", "^:", "^ ", "^call ", "^lua " }, -- hide mapping boilerplate + show_help = true, -- show a help message in the command line for using WhichKey + show_keys = true, -- show the currently pressed key and its label as a message in the command line + triggers = "auto", -- automatically setup triggers + -- triggers = {""} -- or specifiy a list manually + -- list of triggers, where WhichKey should not wait for timeoutlen and show immediately + triggers_nowait = { + -- marks + "`", + "'", + "g`", + "g'", + -- registers + '"', + "", + -- spelling + "z=", + }, + triggers_blacklist = { + -- list of mode / prefixes that should never be hooked by WhichKey + -- this is mostly relevant for keymaps that start with a native binding + i = { "j", "k" }, + v = { "j", "k" }, + }, + -- disable the WhichKey popup for certain buf types and file types. + -- Disabled by deafult for Telescope + disable = { + buftypes = {}, + filetypes = {}, + }, +} + +---@type Options +M.options = {} + +---@param options? Options +function M.setup(options) + M.options = vim.tbl_deep_extend("force", {}, defaults, options or {}) +end + +M.setup() + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/health.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/health.lua new file mode 100644 index 00000000..e9370c43 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/health.lua @@ -0,0 +1,57 @@ +local Keys = require("which-key.keys") + +local M = {} + +local start = vim.health.start or vim.health.report_start +local ok = vim.health.ok or vim.health.report_ok +local warn = vim.health.warn or vim.health.report_warn +local error = vim.health.error or vim.health.report_error +local info = vim.health.info or vim.health.report_info + +function M.check() + start("WhichKey: checking conflicting keymaps") + local conflicts = 0 + for _, tree in pairs(Keys.mappings) do + Keys.update_keymaps(tree.mode, tree.buf) + tree.tree:walk( + ---@param node Node + function(node) + local count = 0 + for _ in pairs(node.children) do + count = count + 1 + end + + local auto_prefix = not node.mapping or (node.mapping.group == true and not node.mapping.cmd) + if node.prefix_i ~= "" and count > 0 and not auto_prefix then + conflicts = conflicts + 1 + local msg = ("conflicting keymap exists for mode **%q**, lhs: **%q**"):format(tree.mode, node.mapping.prefix) + warn(msg) + local cmd = node.mapping.cmd or " " + info(("rhs: `%s`"):format(cmd)) + end + end + ) + end + if conflicts == 0 then + ok("No conflicting keymaps found") + return + end + for _, dup in ipairs(Keys.duplicates) do + local msg = "" + if dup.buf == dup.other.buffer then + msg = "duplicate keymap" + else + msg = "buffer-local keymap overriding global" + end + msg = (msg .. " for mode **%q**, buf: %d, lhs: **%q**"):format(dup.mode, dup.buf or 0, dup.prefix) + if dup.buf == dup.other.buffer then + error(msg) + else + warn(msg) + end + info(("old rhs: `%s`"):format(dup.other.rhs or "")) + info(("new rhs: `%s`"):format(dup.cmd or "")) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/init.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/init.lua new file mode 100644 index 00000000..a1f34c44 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/init.lua @@ -0,0 +1,108 @@ +local Keys = require("which-key.keys") +local Util = require("which-key.util") + +---@class WhichKey +local M = {} + +local loaded = false -- once we loaded everything +local scheduled = false + +local function schedule_load() + if scheduled then + return + end + scheduled = true + if vim.v.vim_did_enter == 0 then + vim.cmd([[au VimEnter * ++once lua require("which-key").load()]]) + else + M.load() + end +end + +---@param options? Options +function M.setup(options) + require("which-key.config").setup(options) + schedule_load() +end + +function M.execute(id) + local func = Keys.functions[id] + return func() +end + +function M.show(keys, opts) + opts = opts or {} + if type(opts) == "string" then + opts = { mode = opts } + end + + keys = keys or "" + + opts.mode = opts.mode or Util.get_mode() + local buf = vim.api.nvim_get_current_buf() + -- make sure the trees exist for update + Keys.get_tree(opts.mode) + Keys.get_tree(opts.mode, buf) + -- update only trees related to buf + Keys.update(buf) + -- trigger which key + require("which-key.view").open(keys, opts) +end + +function M.show_command(keys, mode) + keys = keys or "" + keys = (keys == '""' or keys == "''") and "" or keys + mode = (mode == '""' or mode == "''") and "" or mode + mode = mode or "n" + keys = Util.t(keys) + if not Util.check_mode(mode) then + Util.error( + "Invalid mode passed to :WhichKey (Don't create any keymappings to trigger WhichKey. WhichKey does this automatically)" + ) + else + M.show(keys, { mode = mode }) + end +end + +local queue = {} + +-- Defer registering keymaps until VimEnter +function M.register(mappings, opts) + schedule_load() + if loaded then + Keys.register(mappings, opts) + Keys.update() + else + table.insert(queue, { mappings, opts }) + end +end + +-- Load mappings and update only once +function M.load() + if loaded then + return + end + require("which-key.plugins").setup() + require("which-key.colors").setup() + Keys.register({}, { prefix = "", mode = "n" }) + Keys.register({}, { prefix = "", mode = "v" }) + Keys.setup() + + for _, reg in pairs(queue) do + local opts = reg[2] or {} + opts.update = false + Keys.register(reg[1], opts) + end + Keys.update() + queue = {} + loaded = true +end + +function M.reset() + -- local mappings = Keys.mappings + require("plenary.reload").reload_module("which-key") + -- require("which-key.Keys").mappings = mappings + require("which-key").setup() +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/keys.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/keys.lua new file mode 100644 index 00000000..aabb22ce --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/keys.lua @@ -0,0 +1,438 @@ +local Tree = require("which-key.tree") +local Util = require("which-key.util") +local Config = require("which-key.config") + +-- secret character that will be used to create mappings +local secret = "Þ" + +---@class Keys +local M = {} + +M.functions = {} +M.operators = {} +M.nowait = {} +M.blacklist = {} + +function M.setup() + local builtin_ops = require("which-key.plugins.presets").operators + for op, _ in pairs(builtin_ops) do + M.operators[op] = true + end + local mappings = {} + for op, label in pairs(Config.options.operators) do + M.operators[op] = true + if builtin_ops[op] then + mappings[op] = { name = label, i = { name = "inside" }, a = { name = "around" } } + end + end + for _, t in pairs(Config.options.triggers_nowait) do + M.nowait[t] = true + end + M.register(mappings, { mode = "n", preset = true }) + M.register({ i = { name = "inside" }, a = { name = "around" } }, { mode = "v", preset = true }) + for mode, blacklist in pairs(Config.options.triggers_blacklist) do + for _, prefix_n in ipairs(blacklist) do + M.blacklist[mode] = M.blacklist[mode] or {} + M.blacklist[mode][prefix_n] = true + end + end +end + +function M.get_operator(prefix_i) + for op_n, _ in pairs(Config.options.operators) do + local op_i = Util.t(op_n) + if prefix_i:sub(1, #op_i) == op_i then + return op_i, op_n + end + end +end + +function M.process_motions(ret, mode, prefix_i, buf) + local op_i, op_n = "", "" + if mode ~= "v" then + op_i, op_n = M.get_operator(prefix_i) + end + if (mode == "n" or mode == "v") and op_i then + local op_prefix_i = prefix_i:sub(#op_i + 1) + local op_count = op_prefix_i:match("^(%d+)") + if op_count == "0" then + op_count = nil + end + if Config.options.motions.count == false then + op_count = nil + end + if op_count then + op_prefix_i = op_prefix_i:sub(#op_count + 1) + end + local op_results = M.get_mappings("o", op_prefix_i, buf) + + if not ret.mapping and op_results.mapping then + ret.mapping = op_results.mapping + ret.mapping.prefix = op_n .. (op_count or "") .. ret.mapping.prefix + ret.mapping.keys = Util.parse_keys(ret.mapping.prefix) + end + + for _, mapping in pairs(op_results.mappings) do + mapping.prefix = op_n .. (op_count or "") .. mapping.prefix + mapping.keys = Util.parse_keys(mapping.prefix) + table.insert(ret.mappings, mapping) + end + end +end + +---@return MappingGroup +function M.get_mappings(mode, prefix_i, buf) + ---@class MappingGroup + ---@field mode string + ---@field prefix_i string + ---@field buf number + ---@field mapping? Mapping + ---@field mappings VisualMapping[] + local ret + ret = { mapping = nil, mappings = {}, mode = mode, buf = buf, prefix_i = prefix_i } + + local prefix_len = #Util.parse_internal(prefix_i) + + ---@param node? Node + local function add(node) + if node then + if node.mapping then + ret.mapping = vim.tbl_deep_extend("force", {}, ret.mapping or {}, node.mapping) + end + for k, child in pairs(node.children) do + if + child.mapping + and child.mapping.label ~= "which_key_ignore" + and child.mapping.desc ~= "which_key_ignore" + and not (child.mapping.group and vim.tbl_isempty(child.children)) + then + ret.mappings[k] = vim.tbl_deep_extend("force", {}, ret.mappings[k] or {}, child.mapping) + end + end + end + end + + local plugin_context = { buf = buf, mode = mode } + add(M.get_tree(mode).tree:get(prefix_i, nil, plugin_context)) + add(M.get_tree(mode, buf).tree:get(prefix_i, nil, plugin_context)) + + -- Handle motions + M.process_motions(ret, mode, prefix_i, buf) + + -- Fix labels + local tmp = {} + for _, value in pairs(ret.mappings) do + value.key = value.keys.notation[prefix_len + 1] + if Config.options.key_labels[value.key] then + value.key = Config.options.key_labels[value.key] + end + local skip = not value.label and Config.options.ignore_missing == true + if Util.t(value.key) == Util.t("") then + skip = true + end + if not skip then + if value.group then + value.label = value.label or "+prefix" + value.label = value.label:gsub("^%+", "") + value.label = Config.options.icons.group .. value.label + elseif not value.label then + value.label = value.desc or value.cmd or "" + for _, v in ipairs(Config.options.hidden) do + value.label = value.label:gsub(v, "") + end + end + if value.value then + value.value = vim.fn.strtrans(value.value) + end + -- remove duplicated keymap + local exists = false + for k, v in pairs(tmp) do + if type(v) == "table" and v.key == value.key then + tmp[k] = value + exists = true + break + end + end + if not exists then + table.insert(tmp, value) + end + end + end + + -- Sort items, but not for plugins + table.sort(tmp, function(a, b) + if a.order and b.order then + return a.order < b.order + end + if a.group == b.group then + local ak = (a.key or ""):lower() + local bk = (b.key or ""):lower() + local aw = ak:match("[a-z]") and 1 or 0 + local bw = bk:match("[a-z]") and 1 or 0 + if aw == bw then + return ak < bk + end + return aw < bw + else + return (a.group and 1 or 0) < (b.group and 1 or 0) + end + end) + ret.mappings = tmp + + return ret +end + +---@type table +M.mappings = {} +M.duplicates = {} + +function M.map(mode, prefix_n, cmd, buf, opts) + local other = vim.api.nvim_buf_call(buf or 0, function() + local ret = vim.fn.maparg(prefix_n, mode, false, true) + ---@diagnostic disable-next-line: undefined-field + return (ret and ret.lhs and ret.rhs and ret.rhs ~= cmd) and ret or nil + end) + if other and other.buffer == buf then + table.insert(M.duplicates, { mode = mode, prefix = prefix_n, cmd = cmd, buf = buf, other = other }) + end + if buf ~= nil then + pcall(vim.api.nvim_buf_set_keymap, buf, mode, prefix_n, cmd, opts) + else + pcall(vim.api.nvim_set_keymap, mode, prefix_n, cmd, opts) + end +end + +function M.register(mappings, opts) + opts = opts or {} + + mappings = require("which-key.mappings").parse(mappings, opts) + + -- always create the root node for the mode, even if there's no mappings, + -- to ensure we have at least a trigger hooked for non documented keymaps + local modes = {} + + for _, mapping in pairs(mappings) do + if not modes[mapping.mode] then + modes[mapping.mode] = true + M.get_tree(mapping.mode) + end + if mapping.cmd ~= nil then + M.map(mapping.mode, mapping.prefix, mapping.cmd, mapping.buf, mapping.opts) + end + M.get_tree(mapping.mode, mapping.buf).tree:add(mapping) + end +end + +M.hooked = {} + +function M.hook_id(prefix_n, mode, buf) + return mode .. (buf or "") .. Util.t(prefix_n) +end + +function M.is_hooked(prefix_n, mode, buf) + return M.hooked[M.hook_id(prefix_n, mode, buf)] +end + +function M.hook_del(prefix_n, mode, buf) + local id = M.hook_id(prefix_n, mode, buf) + M.hooked[id] = nil + if buf then + pcall(vim.api.nvim_buf_del_keymap, buf, mode, prefix_n) + pcall(vim.api.nvim_buf_del_keymap, buf, mode, prefix_n .. secret) + else + pcall(vim.api.nvim_del_keymap, mode, prefix_n) + pcall(vim.api.nvim_del_keymap, mode, prefix_n .. secret) + end +end + +function M.hook_add(prefix_n, mode, buf, secret_only) + -- check if this trigger is blacklisted + if M.blacklist[mode] and M.blacklist[mode][prefix_n] then + return + end + -- don't hook numbers. See #118 + if tonumber(prefix_n) then + return + end + -- don't hook to j or k in INSERT mode + if mode == "i" and (prefix_n == "j" or prefix_n == "k") then + return + end + -- never hook q + if mode == "n" and prefix_n == "q" then + return + end + -- never hook into select mode + if mode == "s" then + return + end + -- never hook into operator pending mode + -- this is handled differently + if mode == "o" then + return + end + if Util.t(prefix_n) == Util.t("") then + return + end + -- never hook into operators in visual mode + if (mode == "v" or mode == "x") and (prefix_n == "a" or prefix_n == "i" or M.operators[prefix_n]) then + return + end + + -- Check if we need to create the hook + if type(Config.options.triggers) == "string" and Config.options.triggers ~= "auto" then + if Util.t(prefix_n) ~= Util.t(Config.options.triggers) then + return + end + end + if type(Config.options.triggers) == "table" then + local ok = false + for _, trigger in pairs(Config.options.triggers) do + if Util.t(trigger) == Util.t(prefix_n) then + ok = true + break + end + end + if not ok then + return + end + end + + local opts = { noremap = true, silent = true } + local id = M.hook_id(prefix_n, mode, buf) + local id_global = M.hook_id(prefix_n, mode) + -- hook up if needed + if not M.hooked[id] and not M.hooked[id_global] then + local cmd = [[lua require("which-key").show(%q, {mode = %q, auto = true})]] + cmd = string.format(cmd, Util.t(prefix_n), mode) + -- map group triggers and nops + -- nops are needed, so that WhichKey always respects timeoutlen + + local mapmode = mode == "v" and "x" or mode + if secret_only ~= true then + M.map(mapmode, prefix_n, cmd, buf, opts) + end + if not M.nowait[prefix_n] then + M.map(mapmode, prefix_n .. secret, "", buf, opts) + end + + M.hooked[id] = true + end +end + +function M.update(buf) + for k, tree in pairs(M.mappings) do + if tree.buf and not vim.api.nvim_buf_is_valid(tree.buf) then + -- remove group for invalid buffers + M.mappings[k] = nil + elseif not buf or not tree.buf or buf == tree.buf then + -- only update buffer maps, if: + -- 1. we dont pass a buffer + -- 2. this is a global node + -- 3. this is a local buffer node for the passed buffer + M.update_keymaps(tree.mode, tree.buf) + M.add_hooks(tree.mode, tree.buf, tree.tree.root) + end + end +end + +---@param node Node +function M.add_hooks(mode, buf, node, secret_only) + if not node.mapping then + node.mapping = { prefix = node.prefix_n, group = true, keys = Util.parse_keys(node.prefix_n) } + end + if node.prefix_n ~= "" and node.mapping.group == true and not (node.mapping.cmd or node.mapping.callback) then + -- first non-cmd level, so create hook and make all decendents secret only + M.hook_add(node.prefix_n, mode, buf, secret_only) + secret_only = true + end + for _, child in pairs(node.children) do + M.add_hooks(mode, buf, child, secret_only) + end +end + +function M.dump() + local ok = {} + local todo = {} + for _, tree in pairs(M.mappings) do + M.update_keymaps(tree.mode, tree.buf) + tree.tree:walk( + ---@param node Node + function(node) + if node.mapping then + if node.mapping.label then + ok[node.mapping.prefix] = true + todo[node.mapping.prefix] = nil + elseif not ok[node.mapping.prefix] then + todo[node.mapping.prefix] = { node.mapping.cmd or "" } + end + end + end + ) + end + return todo +end + +---@param mode string +---@param buf? buffer +function M.get_tree(mode, buf) + if mode == "s" or mode == "x" then + mode = "v" + end + Util.check_mode(mode, buf) + local idx = mode .. (buf or "") + if not M.mappings[idx] then + M.mappings[idx] = { mode = mode, buf = buf, tree = Tree:new() } + end + return M.mappings[idx] +end + +---@param prefix string +---@param cmd string? +function M.is_hook(prefix, cmd) + -- skip mappings with our secret nop command + if prefix:find(secret, 1, true) then + return true + end + -- skip auto which-key mappings + return cmd and cmd:find("which-key", 1, true) and cmd:find("auto", 1, true) +end + +---@param mode string +---@param buf? number +function M.update_keymaps(mode, buf) + ---@type Keymap[] + local keymaps = buf and vim.api.nvim_buf_get_keymap(buf, mode) or vim.api.nvim_get_keymap(mode) + local tree = M.get_tree(mode, buf).tree + + local function is_nop(keymap) + return not keymap.callback and (keymap.rhs == "" or keymap.rhs:lower() == "") + end + + for _, keymap in pairs(keymaps) do + local skip = M.is_hook(keymap.lhs, keymap.rhs) + + if is_nop(keymap) then + skip = true + end + + if not skip then + local mapping = { + prefix = keymap.lhs, + cmd = keymap.rhs, + desc = keymap.desc, + callback = keymap.callback, + keys = Util.parse_keys(keymap.lhs), + } + -- don't include Plug keymaps + if mapping.keys.notation[1]:lower() ~= "" then + local node = tree:add(mapping) + if node.mapping and node.mapping.preset and mapping.desc then + node.mapping.label = mapping.desc + end + end + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/layout.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/layout.lua new file mode 100644 index 00000000..ddaf5974 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/layout.lua @@ -0,0 +1,216 @@ +local Config = require("which-key.config") +local Text = require("which-key.text") +local Keys = require("which-key.keys") +local Util = require("which-key.util") + +---@class Layout +---@field mapping Mapping +---@field items VisualMapping[] +---@field options Options +---@field text Text +---@field results MappingGroup +local Layout = {} +Layout.__index = Layout + +---@param mappings MappingGroup +---@param options? Options +function Layout:new(mappings, options) + options = options or Config.options + local this = { + results = mappings, + mapping = mappings.mapping, + items = mappings.mappings, + options = options, + text = Text:new(), + } + setmetatable(this, self) + return this +end + +function Layout:max_width(key) + local max = 0 + for _, item in pairs(self.items) do + if item[key] and Text.len(item[key]) > max then + max = Text.len(item[key]) + end + end + return max +end + +function Layout:trail() + local prefix_i = self.results.prefix_i + local buf_path = Keys.get_tree(self.results.mode, self.results.buf).tree:path(prefix_i) + local path = Keys.get_tree(self.results.mode).tree:path(prefix_i) + local len = #self.results.mapping.keys.notation + local cmd_line = { { " " } } + for i = 1, len, 1 do + local node = buf_path[i] + if not (node and node.mapping and node.mapping.label) then + node = path[i] + end + local step = self.mapping.keys.notation[i] + if node and node.mapping and node.mapping.label then + step = self.options.icons.group .. node.mapping.label + end + if Config.options.key_labels[step] then + step = Config.options.key_labels[step] + end + if Config.options.show_keys then + table.insert(cmd_line, { step, "WhichKeyGroup" }) + if i ~= #self.mapping.keys.notation then + table.insert(cmd_line, { " " .. self.options.icons.breadcrumb .. " ", "WhichKeySeparator" }) + end + end + end + local width = 0 + if Config.options.show_keys then + for _, line in pairs(cmd_line) do + width = width + Text.len(line[1]) + end + end + local help = { -- + [""] = "go up one level", + [""] = "close", + } + if #self.text.lines > self.options.layout.height.max then + help[Config.options.popup_mappings.scroll_down] = "scroll down" + help[Config.options.popup_mappings.scroll_up] = "scroll up" + end + local help_line = {} + local help_width = 0 + for key, label in pairs(help) do + help_width = help_width + Text.len(key) + Text.len(label) + 2 + table.insert(help_line, { key .. " ", "WhichKey" }) + table.insert(help_line, { label .. " ", "WhichKeySeparator" }) + end + if Config.options.show_keys then + table.insert(cmd_line, { string.rep(" ", math.floor(vim.o.columns / 2 - help_width / 2) - width) }) + end + + if self.options.show_help then + for _, l in pairs(help_line) do + table.insert(cmd_line, l) + end + end + if vim.o.cmdheight > 0 then + vim.api.nvim_echo(cmd_line, false, {}) + vim.cmd([[redraw]]) + else + local col = 1 + self.text:nl() + local row = #self.text.lines + for _, text in ipairs(cmd_line) do + self.text:set(row, col, text[1], text[2] and text[2]:gsub("WhichKey", "") or nil) + col = col + vim.fn.strwidth(text[1]) + end + end +end + +function Layout:layout(win) + local pad_top, pad_right, pad_bot, pad_left = unpack(self.options.window.padding) + local window_width = vim.api.nvim_win_get_width(win) + local width = window_width + width = width - pad_right - pad_left + + local max_key_width = self:max_width("key") + local max_label_width = self:max_width("label") + local max_value_width = self:max_width("value") + + local intro_width = max_key_width + 2 + Text.len(self.options.icons.separator) + self.options.layout.spacing + local max_width = max_label_width + intro_width + max_value_width + if max_width > width then + max_width = width + end + + local column_width = max_width + + if max_value_width == 0 then + if column_width > self.options.layout.width.max then + column_width = self.options.layout.width.max + end + if column_width < self.options.layout.width.min then + column_width = self.options.layout.width.min + end + else + max_value_width = math.min(max_value_width, math.floor((column_width - intro_width) / 2)) + end + + max_label_width = column_width - (intro_width + max_value_width) + + local columns = math.floor(width / column_width) + + local height = math.ceil(#self.items / columns) + if height < self.options.layout.height.min then + height = self.options.layout.height.min + end + -- if height > self.options.layout.height.max then height = self.options.layout.height.max end + + local col = 1 + local row = 1 + + local columns_used = math.min(columns, math.ceil(#self.items / height)) + local offset_x = 0 + if columns_used < columns then + if self.options.layout.align == "right" then + offset_x = (columns - columns_used) * column_width + elseif self.options.layout.align == "center" then + offset_x = math.floor((columns - columns_used) * column_width / 2) + end + end + + for _, item in pairs(self.items) do + local start = (col - 1) * column_width + self.options.layout.spacing + offset_x + pad_left + local key = item.key or "" + if key == "" then + key = "<" + end + if key == Util.t("") then + key = "" + end + if Text.len(key) < max_key_width then + key = string.rep(" ", max_key_width - Text.len(key)) .. key + end + + self.text:set(row + pad_top, start, key, "") + start = start + Text.len(key) + 1 + + self.text:set(row + pad_top, start, self.options.icons.separator, "Separator") + start = start + Text.len(self.options.icons.separator) + 1 + + if item.value then + local value = item.value + start = start + 1 + if Text.len(value) > max_value_width then + value = vim.fn.strcharpart(value, 0, max_value_width - 2) .. " …" + end + self.text:set(row + pad_top, start, value, "Value") + if item.highlights then + for _, hl in pairs(item.highlights) do + self.text:highlight(row + pad_top, start + hl[1] - 1, start + hl[2] - 1, hl[3]) + end + end + start = start + max_value_width + 2 + end + + local label = item.label + if Text.len(label) > max_label_width then + label = vim.fn.strcharpart(label, 0, max_label_width - 2) .. " …" + end + self.text:set(row + pad_top, start, label, item.group and "Group" or "Desc") + + if row % height == 0 then + col = col + 1 + row = 1 + else + row = row + 1 + end + end + + for _ = 1, pad_bot, 1 do + self.text:nl() + end + self:trail() + return self.text +end + +return Layout diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/mappings.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/mappings.lua new file mode 100644 index 00000000..a21ff8e5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/mappings.lua @@ -0,0 +1,231 @@ +local Util = require("which-key.util") + +local M = {} + +local function lookup(...) + local ret = {} + for _, t in ipairs({ ... }) do + for _, v in ipairs(t) do + ret[v] = v + end + end + return ret +end + +local mapargs = { + "noremap", + "desc", + "expr", + "silent", + "nowait", + "script", + "unique", + "callback", + "replace_keycodes", -- TODO: add config setting for default value +} +local wkargs = { + "prefix", + "mode", + "plugin", + "buffer", + "remap", + "cmd", + "name", + "group", + "preset", + "cond", +} +local transargs = lookup({ + "noremap", + "expr", + "silent", + "nowait", + "script", + "unique", + "prefix", + "mode", + "buffer", + "preset", + "replace_keycodes", +}) +local args = lookup(mapargs, wkargs) + +function M.child_opts(opts) + local ret = {} + for k, v in pairs(opts) do + if transargs[k] then + ret[k] = v + end + end + return ret +end + +function M._process(value, opts) + local list = {} + local children = {} + for k, v in pairs(value) do + if type(k) == "number" then + if type(v) == "table" then + -- nested child, without key + table.insert(children, v) + else + -- list value + table.insert(list, v) + end + elseif args[k] then + -- option + opts[k] = v + else + -- nested child, with key + children[k] = v + end + end + return list, children +end + +function M._parse(value, mappings, opts) + if type(value) ~= "table" then + value = { value } + end + + local list, children = M._process(value, opts) + + if opts.plugin then + opts.group = true + end + if opts.name then + -- remove + from group names + opts.name = opts.name and opts.name:gsub("^%+", "") + opts.group = true + end + + -- fix remap + if opts.remap then + opts.noremap = not opts.remap + opts.remap = nil + end + + -- fix buffer + if opts.buffer == 0 then + opts.buffer = vim.api.nvim_get_current_buf() + end + + if opts.cond ~= nil then + if type(opts.cond) == "function" then + if not opts.cond() then + return + end + elseif not opts.cond then + return + end + end + + -- process any array child mappings + for k, v in pairs(children) do + local o = M.child_opts(opts) + if type(k) == "string" then + o.prefix = (o.prefix or "") .. k + end + M._try_parse(v, mappings, o) + end + + -- { desc } + if #list == 1 then + if type(list[1]) ~= "string" then + error("Invalid mapping for " .. vim.inspect({ value = value, opts = opts })) + end + opts.desc = list[1] + -- { cmd, desc } + elseif #list == 2 then + -- desc + assert(type(list[2]) == "string") + opts.desc = list[2] + + -- cmd + if type(list[1]) == "string" then + opts.cmd = list[1] + elseif type(list[1]) == "function" then + opts.cmd = "" + opts.callback = list[1] + else + error("Incorrect mapping " .. vim.inspect(list)) + end + elseif #list > 2 then + error("Incorrect mapping " .. vim.inspect(list)) + end + + if opts.desc or opts.group then + if type(opts.mode) == "table" then + for _, mode in pairs(opts.mode) do + local mode_opts = vim.deepcopy(opts) + mode_opts.mode = mode + table.insert(mappings, mode_opts) + end + else + table.insert(mappings, opts) + end + end +end + +---@return Mapping +function M.to_mapping(mapping) + mapping.silent = mapping.silent ~= false + mapping.noremap = mapping.noremap ~= false + if mapping.cmd and mapping.cmd:lower():find("^") then + mapping.noremap = false + end + + mapping.buf = mapping.buffer + mapping.buffer = nil + + mapping.mode = mapping.mode or "n" + mapping.label = mapping.desc or mapping.name + mapping.keys = Util.parse_keys(mapping.prefix or "") + + local opts = {} + for _, o in ipairs(mapargs) do + opts[o] = mapping[o] + mapping[o] = nil + end + + if vim.fn.has("nvim-0.7.0") == 0 then + opts.replace_keycodes = nil + + -- Neovim < 0.7.0 doesn't support descriptions + opts.desc = nil + + -- use lua functions proxy for Neovim < 0.7.0 + if opts.callback then + local functions = require("which-key.keys").functions + table.insert(functions, opts.callback) + if opts.expr then + opts.cmd = string.format([[luaeval('require("which-key").execute(%d)')]], #functions) + else + opts.cmd = string.format([[lua require("which-key").execute(%d)]], #functions) + end + opts.callback = nil + end + end + + mapping.opts = opts + return mapping +end + +function M._try_parse(value, mappings, opts) + local ok, err = pcall(M._parse, value, mappings, opts) + if not ok then + Util.error(err) + end +end + +---@return Mapping[] +function M.parse(mappings, opts) + opts = opts or {} + local ret = {} + M._try_parse(mappings, ret, opts) + return vim.tbl_map(function(m) + return M.to_mapping(m) + end, ret) +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/init.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/init.lua new file mode 100644 index 00000000..e34e7e72 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/init.lua @@ -0,0 +1,59 @@ +local Keys = require("which-key.keys") +local Util = require("which-key.util") +local Config = require("which-key.config") + +local M = {} + +M.plugins = {} + +function M.setup() + for name, opts in pairs(Config.options.plugins) do + -- only setup plugin if we didnt load it before + if not M.plugins[name] then + if type(opts) == "boolean" then + opts = { enabled = opts } + end + opts.enabled = opts.enabled ~= false + if opts.enabled then + M.plugins[name] = require("which-key.plugins." .. name) + M._setup(M.plugins[name], opts) + end + end + end +end + +---@param plugin Plugin +function M._setup(plugin, opts) + if plugin.actions then + for _, trigger in pairs(plugin.actions) do + local prefix = trigger.trigger + local mode = trigger.mode or "n" + local label = trigger.label or plugin.name + Keys.register({ [prefix] = { label, plugin = plugin.name } }, { mode = mode }) + end + end + + if plugin.setup then + plugin.setup(require("which-key"), opts, Config.options) + end +end + +---@param mapping Mapping +function M.invoke(mapping, context) + local plugin = M.plugins[mapping.plugin] + local prefix = mapping.prefix + local items = plugin.run(prefix, context.mode, context.buf) + + local ret = {} + for i, item in + ipairs(items --[[@as VisualMapping[] ]]) + do + item.order = i + item.keys = Util.parse_keys(prefix .. item.key) + item.prefix = prefix .. item.key + table.insert(ret, item) + end + return ret +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/marks.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/marks.lua new file mode 100644 index 00000000..55a251e4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/marks.lua @@ -0,0 +1,66 @@ +local M = {} + +M.name = "marks" + +M.actions = { + { trigger = "`", mode = "n" }, + { trigger = "'", mode = "n" }, + { trigger = "g`", mode = "n" }, + { trigger = "g'", mode = "n" }, +} + +function M.setup(_wk, _config, options) end + +local labels = { + ["^"] = "Last position of cursor in insert mode", + ["."] = "Last change in current buffer", + ['"'] = "Last exited current buffer", + ["0"] = "In last file edited", + ["'"] = "Back to line in current buffer where jumped from", + ["`"] = "Back to position in current buffer where jumped from", + ["["] = "To beginning of previously changed or yanked text", + ["]"] = "To end of previously changed or yanked text", + [""] = "To beginning of last visual selection", + [">"] = "To end of last visual selection", +} + +---@type Plugin +---@return PluginItem[] +function M.run(_trigger, _mode, buf) + local items = {} + + local marks = {} + vim.list_extend(marks, vim.fn.getmarklist(buf)) + vim.list_extend(marks, vim.fn.getmarklist()) + + for _, mark in pairs(marks) do + local key = mark.mark:sub(2, 2) + if key == "<" then + key = "" + end + local lnum = mark.pos[2] + + local line + if mark.pos[1] and mark.pos[1] ~= 0 then + local lines = vim.fn.getbufline(mark.pos[1], lnum) + if lines and lines[1] then + line = lines[1] + end + end + + local file = mark.file and vim.fn.fnamemodify(mark.file, ":p:~:.") + + local value = string.format("%4d ", lnum) + value = value .. (line or file or "") + + table.insert(items, { + key = key, + label = labels[key] or file and ("file: " .. file) or "", + value = value, + highlights = { { 1, 5, "Number" } }, + }) + end + return items +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/presets/init.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/presets/init.lua new file mode 100644 index 00000000..ef4a480a --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/presets/init.lua @@ -0,0 +1,107 @@ +local M = {} + +M.name = "presets" + +M.operators = { + d = "Delete", + c = "Change", + y = "Yank (copy)", + ["g~"] = "Toggle case", + ["gu"] = "Lowercase", + ["gU"] = "Uppercase", + [">"] = "Indent right", + [""] = "Indent left", + ["zf"] = "Create fold", + ["!"] = "Filter through external program", + ["v"] = "Visual Character Mode", + -- ["V"] = "Visual Line Mode", +} + +M.motions = { + ["h"] = "Left", + ["j"] = "Down", + ["k"] = "Up", + ["l"] = "Right", + ["w"] = "Next word", + ["%"] = "Matching character: '()', '{}', '[]'", + ["b"] = "Previous word", + ["e"] = "Next end of word", + ["ge"] = "Previous end of word", + ["0"] = "Start of line", + ["^"] = "Start of line (non-blank)", + ["$"] = "End of line", + ["f"] = "Move to next char", + ["F"] = "Move to previous char", + ["t"] = "Move before next char", + ["T"] = "Move before previous char", + ["gg"] = "First line", + ["G"] = "Last line", + ["{"] = "Previous empty line", + ["}"] = "Next empty line", +} + +M.objects = { + a = { name = "around" }, + i = { name = "inside" }, + ['a"'] = [[double quoted string]], + ["a'"] = [[single quoted string]], + ["a("] = [[same as ab]], + ["a)"] = [[same as ab]], + ["a"] = [[a <> from '<' to the matching '>']], + ["a>"] = [[same as a<]], + ["aB"] = [[a Block from [{ to ]} (with brackets)]], + ["aW"] = [[a WORD (with white space)]], + ["a["] = [[a [] from '[' to the matching ']']], + ["a]"] = [[same as a[]], + ["a`"] = [[string in backticks]], + ["ab"] = [[a block from [( to ]) (with braces)]], + ["ap"] = [[a paragraph (with white space)]], + ["as"] = [[a sentence (with white space)]], + ["at"] = [[a tag block (with white space)]], + ["aw"] = [[a word (with white space)]], + ["a{"] = [[same as aB]], + ["a}"] = [[same as aB]], + ['i"'] = [[double quoted string without the quotes]], + ["i'"] = [[single quoted string without the quotes]], + ["i("] = [[same as ib]], + ["i)"] = [[same as ib]], + ["i"] = [[inner <> from '<' to the matching '>']], + ["i>"] = [[same as i<]], + ["iB"] = [[inner Block from [{ and ]}]], + ["iW"] = [[inner WORD]], + ["i["] = [[inner [] from '[' to the matching ']']], + ["i]"] = [[same as i[]], + ["i`"] = [[string in backticks without the backticks]], + ["ib"] = [[inner block from [( to ])]], + ["ip"] = [[inner paragraph]], + ["is"] = [[inner sentence]], + ["it"] = [[inner tag block]], + ["iw"] = [[inner word]], + ["i{"] = [[same as iB]], + ["i}"] = [[same as iB]], +} + +---@param config Options +function M.setup(wk, opts, config) + require("which-key.plugins.presets.misc").setup(wk, opts) + + -- Operators + if opts.operators then + for op, label in pairs(M.operators) do + config.operators[op] = label + end + end + + -- Motions + if opts.motions then + wk.register(M.motions, { mode = "n", prefix = "", preset = true }) + wk.register(M.motions, { mode = "o", prefix = "", preset = true }) + end + + -- Text objects + if opts.text_objects then + wk.register(M.objects, { mode = "o", prefix = "", preset = true }) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/presets/misc.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/presets/misc.lua new file mode 100644 index 00000000..388028e1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/presets/misc.lua @@ -0,0 +1,97 @@ +local M = {} + +M.name = "misc" + +local misc = { + windows = { + [""] = { + name = "window", + s = "Split window", + v = "Split window vertically", + w = "Switch windows", + q = "Quit a window", + o = "Close all other windows", + T = "Break out into a new tab", + x = "Swap current with next", + ["-"] = "Decrease height", + ["+"] = "Increase height", + [""] = "Decrease width", + [">"] = "Increase width", + ["|"] = "Max out the width", + ["_"] = "Max out the height", + ["="] = "Equally high and wide", + h = "Go to the left window", + l = "Go to the right window", + k = "Go to the up window", + j = "Go to the down window", + }, + }, + z = { + ["z"] = { + o = "Open fold under cursor", + O = "Open all folds under cursor", + c = "Close fold under cursor", + C = "Close all folds under cursor", + a = "Toggle fold under cursor", + A = "Toggle all folds under cursor", + v = "Show cursor line", + M = "Close all folds", + R = "Open all folds", + m = "Fold more", + r = "Fold less", + x = "Update folds", + z = "Center this line", + t = "Top this line", + [""] = "Top this line, 1st non-blank col", + b = "Bottom this line", + g = "Add word to spell list", + w = "Mark word as bad/misspelling", + e = "Right this line", + s = "Left this line", + H = "Half screen to the left", + L = "Half screen to the right", + i = "Toggle folding", + ["="] = "Spelling suggestions", + }, + }, + nav = { + ["[{"] = "Previous {", + ["[("] = "Previous (", + ["["] = "Previous <", + ["[m"] = "Previous method start", + ["[M"] = "Previous method end", + ["[%"] = "Previous unmatched group", + ["[s"] = "Previous misspelled word", + ["]{"] = "Next {", + ["]("] = "Next (", + ["]"] = "Next <", + ["]m"] = "Next method start", + ["]M"] = "Next method end", + ["]%"] = "Next unmatched group", + ["]s"] = "Next misspelled word", + ["H"] = "Home line of window (top)", + ["M"] = "Middle line of window", + ["L"] = "Last line of window", + }, + g = { + ["gf"] = "Go to file under cursor", + ["gx"] = "Open the file under cursor with system app", + ["gi"] = "Move to the last insertion and INSERT", + ["gv"] = "Switch to VISUAL using last selection", + ["gn"] = "Search forwards and select", + ["gN"] = "Search backwards and select", + ["g%"] = "Cycle backwards through results", + ["gt"] = "Go to next tab page", + ["gT"] = "Go to previous tab page", + }, +} + +function M.setup(wk, config) + for key, mappings in pairs(misc) do + if config[key] ~= false then + wk.register(mappings, { mode = "n", prefix = "", preset = true }) + end + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/registers.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/registers.lua new file mode 100644 index 00000000..4003b20a --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/registers.lua @@ -0,0 +1,52 @@ +---@type Plugin +local M = {} + +M.name = "registers" + +M.actions = { + { trigger = '"', mode = "n" }, + { trigger = '"', mode = "v" }, + -- { trigger = "@", mode = "n" }, + { trigger = "", mode = "i" }, + { trigger = "", mode = "c" }, +} + +function M.setup(_wk, _config, options) end + +M.registers = '*+"-:.%/#=_abcdefghijklmnopqrstuvwxyz0123456789' + +local labels = { + ['"'] = "last deleted, changed, or yanked content", + ["0"] = "last yank", + ["-"] = "deleted or changed content smaller than one line", + ["."] = "last inserted text", + ["%"] = "name of the current file", + [":"] = "most recent executed command", + ["#"] = "alternate buffer", + ["="] = "result of an expression", + ["+"] = "synchronized with the system clipboard", + ["*"] = "synchronized with the selection clipboard", + ["_"] = "black hole", + ["/"] = "last search pattern", +} + +---@type Plugin +---@return PluginItem[] +function M.run(_trigger, _mode, _buf) + local items = {} + + for i = 1, #M.registers, 1 do + local key = M.registers:sub(i, i) + local ok, value = pcall(vim.fn.getreg, key, 1) + if not ok then + value = "" + end + + if value ~= "" then + table.insert(items, { key = key, label = labels[key] or "", value = value }) + end + end + return items +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/spelling.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/spelling.lua new file mode 100644 index 00000000..ae9e81b1 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/plugins/spelling.lua @@ -0,0 +1,51 @@ +local M = {} + +M.name = "spelling" + +M.actions = { { trigger = "z=", mode = "n" } } + +M.opts = {} + +function M.setup(_, config, options) + M.opts = config +end + +---@type Plugin +---@return PluginItem[] +function M.run() + -- if started with a count, let the default keybinding work + local count = vim.api.nvim_get_vvar("count") + if count and count > 0 then + return {} + end + + ---@diagnostic disable-next-line: missing-parameter + local cursor_word = vim.fn.expand("") + -- get a misspellled word from under the cursor, if not found, then use the cursor_word instead + ---@diagnostic disable-next-line: redundant-parameter + local bad = vim.fn.spellbadword(cursor_word) + local word = bad[1] + if word == "" then + word = cursor_word + end + + local suggestions = vim.fn.spellsuggest(word, M.opts.suggestions or 20, bad[2] == "caps" and 1 or 0) + + local items = {} + local keys = "1234567890abcdefghijklmnopqrstuvwxyz" + + for i, label in ipairs(suggestions) do + local key = keys:sub(i, i) + + table.insert(items, { + key = key, + label = label, + fn = function() + vim.cmd("norm! ciw" .. label) + end, + }) + end + return items +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/text.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/text.lua new file mode 100644 index 00000000..5565dd91 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/text.lua @@ -0,0 +1,72 @@ +---@class Highlight +---@field group string +---@field line number +---@field from number +---@field to number + +---@class Text +---@field lines string[] +---@field hl Highlight[] +---@field lineNr number +---@field current string +local Text = {} +Text.__index = Text + +function Text.len(str) + return vim.fn.strwidth(str) +end + +function Text:new() + local this = { lines = {}, hl = {}, lineNr = 0, current = "" } + setmetatable(this, self) + return this +end + +function Text:fix_nl(line) + return line:gsub("[\n]", "") +end + +function Text:nl() + local line = self:fix_nl(self.current) + table.insert(self.lines, line) + self.current = "" + self.lineNr = self.lineNr + 1 +end + +function Text:set(row, col, str, group) + str = self:fix_nl(str) + + -- extend lines if needed + for i = 1, row, 1 do + if not self.lines[i] then + self.lines[i] = "" + end + end + + -- extend columns when needed + local width = Text.len(self.lines[row]) + if width < col then + self.lines[row] = self.lines[row] .. string.rep(" ", col - width) + end + + local before = vim.fn.strcharpart(self.lines[row], 0, col) + local after = vim.fn.strcharpart(self.lines[row], col) + self.lines[row] = before .. str .. after + + if not group then + return + end + -- set highlights + self:highlight(row, col, col + Text.len(str), "WhichKey" .. group) +end + +function Text:highlight(row, from, to, group) + local line = self.lines[row] + local before = vim.fn.strcharpart(line, 0, from) + local str = vim.fn.strcharpart(line, 0, to) + from = vim.fn.strlen(before) + to = vim.fn.strlen(str) + table.insert(self.hl, { line = row - 1, from = from, to = to, group = group }) +end + +return Text diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/tree.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/tree.lua new file mode 100644 index 00000000..fb2e2801 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/tree.lua @@ -0,0 +1,108 @@ +local Util = require("which-key.util") + +---@class Tree +---@field root Node +---@field nodes table +local Tree = {} +Tree.__index = Tree + +---@class Node +---@field mapping Mapping +---@field prefix_i string +---@field prefix_n string +---@field children table +-- selene: allow(unused_variable) +local Node + +---@return Tree +function Tree:new() + local this = { root = { children = {}, prefix_i = "", prefix_n = "" }, nodes = {} } + setmetatable(this, self) + return this +end + +---@param prefix_i string +---@param index? number defaults to last. If < 0, then offset from last +---@param plugin_context? any +---@return Node? +function Tree:get(prefix_i, index, plugin_context) + local prefix = Util.parse_internal(prefix_i) + local node = self.root + index = index or #prefix + if index < 0 then + index = #prefix + index + end + for i = 1, index, 1 do + node = node.children[prefix[i]] + if node and plugin_context and node.mapping and node.mapping.plugin then + local children = require("which-key.plugins").invoke(node.mapping, plugin_context) + node.children = {} + for _, child in pairs(children) do + self:add(child, { cache = false }) + end + end + if not node then + return nil + end + end + return node +end + +-- Returns the path (possibly incomplete) for the prefix +---@param prefix_i string +---@return Node[] +function Tree:path(prefix_i) + local prefix = Util.parse_internal(prefix_i) + local node = self.root + local path = {} + for i = 1, #prefix, 1 do + node = node.children[prefix[i]] + table.insert(path, node) + if not node then + break + end + end + return path +end + +---@param mapping Mapping +---@param opts? {cache?: boolean} +---@return Node +function Tree:add(mapping, opts) + opts = opts or {} + opts.cache = opts.cache ~= false + local node_key = mapping.keys.keys + local node = opts.cache and self.nodes[node_key] + if not node then + local prefix_i = mapping.keys.internal + local prefix_n = mapping.keys.notation + node = self.root + local path_i = "" + local path_n = "" + for i = 1, #prefix_i, 1 do + path_i = path_i .. prefix_i[i] + path_n = path_n .. prefix_n[i] + if not node.children[prefix_i[i]] then + node.children[prefix_i[i]] = { children = {}, prefix_i = path_i, prefix_n = path_n } + end + node = node.children[prefix_i[i]] + end + if opts.cache then + self.nodes[node_key] = node + end + end + node.mapping = vim.tbl_deep_extend("force", node.mapping or {}, mapping) + return node +end + +---@param cb fun(node:Node) +---@param node? Node +function Tree:walk(cb, node) + node = node or self.root + cb(node) + for _, child in pairs(node.children) do + self:walk(cb, child) + end +end + +return Tree diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/types.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/types.lua new file mode 100644 index 00000000..78671c73 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/types.lua @@ -0,0 +1,74 @@ +---@meta + +--# selene: allow(unused_variable) + +---@class Keymap +---@field rhs string +---@field lhs string +---@field buffer number +---@field expr number +---@field lnum number +---@field mode string +---@field noremap number +---@field nowait number +---@field script number +---@field sid number +---@field silent number +---@field callback fun()|nil +---@field id string terminal keycodes for lhs +---@field desc string + +---@class KeyCodes +---@field keys string +---@field internal string[] +---@field notation string[] + +---@class MappingOptions +---@field noremap boolean +---@field silent boolean +---@field nowait boolean +---@field expr boolean + +---@class Mapping +---@field buf number +---@field group boolean +---@field label string +---@field desc string +---@field prefix string +---@field cmd string +---@field opts MappingOptions +---@field keys KeyCodes +---@field mode? string +---@field callback fun()|nil +---@field preset boolean +---@field plugin string +---@field fn fun() + +---@class MappingTree +---@field mode string +---@field buf? number +---@field tree Tree + +---@class VisualMapping : Mapping +---@field key string +---@field highlights table +---@field value string + +---@class PluginItem +---@field key string +---@field label string +---@field value string +---@field cmd string +---@field highlights table + +---@class PluginAction +---@field trigger string +---@field mode string +---@field label? string +---@field delay? boolean + +---@class Plugin +---@field name string +---@field actions PluginAction[] +---@field run fun(trigger:string, mode:string, buf:number):PluginItem[] +---@field setup fun(wk, opts, Options) diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/util.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/util.lua new file mode 100644 index 00000000..34e4837e --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/util.lua @@ -0,0 +1,196 @@ +---@class Util +local M = {} +local strbyte = string.byte +local strsub = string.sub +---@type table +local cache = {} +---@type table +local tcache = {} +local cache_leaders = "" + +function M.check_cache() + ---@type string + local leaders = (vim.g.mapleader or "") .. ":" .. (vim.g.maplocalleader or "") + if leaders ~= cache_leaders then + cache = {} + tcache = {} + cache_leaders = leaders + end +end + +function M.count(tab) + local ret = 0 + for _, _ in pairs(tab) do + ret = ret + 1 + end + return ret +end + +function M.get_mode() + local mode = vim.api.nvim_get_mode().mode + mode = mode:gsub(M.t(""), "v") + mode = mode:gsub(M.t(""), "s") + return mode:lower() +end + +function M.is_empty(tab) + return M.count(tab) == 0 +end + +function M.t(str) + M.check_cache() + if not tcache[str] then + -- https://github.com/neovim/neovim/issues/17369 + tcache[str] = vim.api.nvim_replace_termcodes(str, false, true, true):gsub("\128\254X", "\128") + end + return tcache[str] +end + +-- stylua: ignore start +local utf8len_tab = { + -- ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 0? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 1? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 2? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 3? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 4? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 5? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 6? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 7? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 8? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 9? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- A? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- B? + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -- C? + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -- D? + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, -- E? + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1, -- F? +} +-- stylua: ignore end + +local Tokens = { + ["<"] = strbyte("<"), + [">"] = strbyte(">"), + ["-"] = strbyte("-"), +} +---@return KeyCodes +function M.parse_keys(keystr) + M.check_cache() + if cache[keystr] then + return cache[keystr] + end + + local keys = M.t(keystr) + local internal = M.parse_internal(keys) + + if #internal == 0 then + local ret = { keys = keys, internal = {}, notation = {} } + cache[keystr] = ret + return ret + end + + local keystr_orig = keystr + keystr = keystr:gsub("", "<") + local notation = {} + ---@alias ParseState + --- | "Character" + --- | "Special" + --- | "SpecialNoClose" + local start = 1 + local i = start + ---@type ParseState + local state = "Character" + while i <= #keystr do + local c = strbyte(keystr, i, i) + + if state == "Character" then + start = i + -- Only interpret special tokens if neovim also replaces it + state = c == Tokens["<"] and internal[#notation + 1] ~= "<" and "Special" or state + elseif state == "Special" then + state = (c == Tokens["-"] and "SpecialNoClose") or (c == Tokens[">"] and "Character") or state + else + state = "Special" + end + + i = i + utf8len_tab[c + 1] + if state == "Character" then + local k = strsub(keystr, start, i - 1) + notation[#notation + 1] = k == " " and "" or k + end + end + + local mapleader = vim.g.mapleader + mapleader = mapleader and M.t(mapleader) + notation[1] = internal[1] == mapleader and "" or notation[1] + + if #notation ~= #internal then + error(vim.inspect({ keystr = keystr, internal = internal, notation = notation })) + end + + local ret = { + keys = keys, + internal = internal, + notation = notation, + } + + cache[keystr_orig] = ret + + return ret +end + +-- @return string[] +function M.parse_internal(keystr) + local keys = {} + ---@alias ParseInternalState + --- | "Character" + --- | "Special" + ---@type ParseInternalState + local state = "Character" + local start = 1 + local i = 1 + while i <= #keystr do + local c = strbyte(keystr, i, i) + + if state == "Character" then + state = c == 128 and "Special" or state + i = i + utf8len_tab[c + 1] + + if state == "Character" then + keys[#keys + 1] = strsub(keystr, start, i - 1) + start = i + end + else + -- This state is entered on the second byte of K_SPECIAL sequence. + if c == 252 then + -- K_SPECIAL KS_MODIFIER: skip this byte and the next + i = i + 2 + else + -- K_SPECIAL _: skip this byte + i = i + 1 + end + -- The last byte of this sequence should be between 0x02 and 0x7f, + -- switch to Character state to collect. + state = "Character" + end + end + return keys +end + +function M.warn(msg) + vim.notify(msg, vim.log.levels.WARN, { title = "WhichKey" }) +end + +function M.error(msg) + vim.notify(msg, vim.log.levels.ERROR, { title = "WhichKey" }) +end + +function M.check_mode(mode, buf) + if not ("nvsxoiRct"):find(mode) then + M.error(string.format("Invalid mode %q for buf %d", mode, buf or 0)) + return false + end + return true +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/view.lua b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/view.lua new file mode 100644 index 00000000..636eba87 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/lua/which-key/view.lua @@ -0,0 +1,350 @@ +local Keys = require("which-key.keys") +local config = require("which-key.config") +local Layout = require("which-key.layout") +local Util = require("which-key.util") + +local highlight = vim.api.nvim_buf_add_highlight + +---@class View +local M = {} + +M.keys = "" +M.mode = "n" +M.reg = nil +M.auto = false +M.count = 0 +M.buf = nil +M.win = nil + +function M.is_valid() + return M.buf + and M.win + and vim.api.nvim_buf_is_valid(M.buf) + and vim.api.nvim_buf_is_loaded(M.buf) + and vim.api.nvim_win_is_valid(M.win) +end + +function M.show() + if vim.b.visual_multi then + vim.b.VM_skip_reset_once_on_bufleave = true + end + if M.is_valid() then + return + end + + -- non-floating windows + local wins = vim.tbl_filter(function(w) + return vim.api.nvim_win_is_valid(w) and vim.api.nvim_win_get_config(w).relative == "" + end, vim.api.nvim_list_wins()) + + ---@type number[] + local margins = {} + for i, m in ipairs(config.options.window.margin) do + if m > 0 and m < 1 then + if i % 2 == 0 then + m = math.floor(vim.o.columns * m) + else + m = math.floor(vim.o.lines * m) + end + end + margins[i] = m + end + + local opts = { + relative = "editor", + width = vim.o.columns + - margins[2] + - margins[4] + - (vim.fn.has("nvim-0.6") == 0 and config.options.window.border ~= "none" and 2 or 0), + height = config.options.layout.height.min, + focusable = false, + anchor = "SW", + border = config.options.window.border, + row = vim.o.lines + - margins[3] + - (vim.fn.has("nvim-0.6") == 0 and config.options.window.border ~= "none" and 2 or 0) + + ((vim.o.laststatus == 0 or vim.o.laststatus == 1 and #wins == 1) and 1 or 0) + - vim.o.cmdheight, + col = margins[4], + style = "minimal", + noautocmd = true, + zindex = config.options.window.zindex, + } + if config.options.window.position == "top" then + opts.anchor = "NW" + opts.row = margins[1] + end + M.buf = vim.api.nvim_create_buf(false, true) + M.win = vim.api.nvim_open_win(M.buf, false, opts) + vim.api.nvim_buf_set_option(M.buf, "filetype", "WhichKey") + vim.api.nvim_buf_set_option(M.buf, "buftype", "nofile") + vim.api.nvim_buf_set_option(M.buf, "bufhidden", "wipe") + vim.api.nvim_buf_set_option(M.buf, "modifiable", true) + + local winhl = "NormalFloat:WhichKeyFloat" + if vim.fn.hlexists("FloatBorder") == 1 then + winhl = winhl .. ",FloatBorder:WhichKeyBorder" + end + vim.api.nvim_win_set_option(M.win, "winhighlight", winhl) + vim.api.nvim_win_set_option(M.win, "foldmethod", "manual") + vim.api.nvim_win_set_option(M.win, "winblend", config.options.window.winblend) +end + +function M.read_pending() + local esc = "" + while true do + local n = vim.fn.getchar(0) + if n == 0 then + break + end + local c = (type(n) == "number" and vim.fn.nr2char(n) or n) + + -- HACK: for some reason, when executing a :norm command, + -- vim keeps feeding at the end + if c == Util.t("") then + esc = esc .. c + -- more than 10 in a row? most likely the norm bug + if #esc > 10 then + return + end + else + -- we have characters, so add them to keys + if esc ~= "" then + M.keys = M.keys .. esc + esc = "" + end + M.keys = M.keys .. c + end + end + if esc ~= "" then + M.keys = M.keys .. esc + esc = "" + end +end + +function M.getchar() + local ok, n = pcall(vim.fn.getchar) + + -- bail out on keyboard interrupt + if not ok then + return Util.t("") + end + + local c = (type(n) == "number" and vim.fn.nr2char(n) or n) + return c +end + +function M.scroll(up) + local height = vim.api.nvim_win_get_height(M.win) + local cursor = vim.api.nvim_win_get_cursor(M.win) + if up then + cursor[1] = math.max(cursor[1] - height, 1) + else + cursor[1] = math.min(cursor[1] + height, vim.api.nvim_buf_line_count(M.buf)) + end + vim.api.nvim_win_set_cursor(M.win, cursor) +end + +function M.on_close() + M.hide() +end + +function M.hide() + vim.api.nvim_echo({ { "" } }, false, {}) + M.hide_cursor() + if M.buf and vim.api.nvim_buf_is_valid(M.buf) then + vim.api.nvim_buf_delete(M.buf, { force = true }) + M.buf = nil + end + if M.win and vim.api.nvim_win_is_valid(M.win) then + vim.api.nvim_win_close(M.win, true) + M.win = nil + end + vim.cmd("redraw") +end + +function M.show_cursor() + local buf = vim.api.nvim_get_current_buf() + local cursor = vim.api.nvim_win_get_cursor(0) + vim.api.nvim_buf_add_highlight(buf, config.namespace, "Cursor", cursor[1] - 1, cursor[2], cursor[2] + 1) +end + +function M.hide_cursor() + local buf = vim.api.nvim_get_current_buf() + vim.api.nvim_buf_clear_namespace(buf, config.namespace, 0, -1) +end + +function M.back() + local node = Keys.get_tree(M.mode, M.buf).tree:get(M.keys, -1) or Keys.get_tree(M.mode).tree:get(M.keys, -1) + if node then + M.keys = node.prefix_i + end +end + +function M.execute(prefix_i, mode, buf) + local global_node = Keys.get_tree(mode).tree:get(prefix_i) + local buf_node = buf and Keys.get_tree(mode, buf).tree:get(prefix_i) or nil + + if global_node and global_node.mapping and Keys.is_hook(prefix_i, global_node.mapping.cmd) then + return + end + if buf_node and buf_node.mapping and Keys.is_hook(prefix_i, buf_node.mapping.cmd) then + return + end + + local hooks = {} + + local function unhook(nodes, nodes_buf) + for _, node in pairs(nodes) do + if Keys.is_hooked(node.mapping.prefix, mode, nodes_buf) then + table.insert(hooks, { node.mapping.prefix, nodes_buf }) + Keys.hook_del(node.mapping.prefix, mode, nodes_buf) + end + end + end + + -- make sure we remove all WK hooks before executing the sequence + -- this is to make existing keybindongs work and prevent recursion + unhook(Keys.get_tree(mode).tree:path(prefix_i)) + if buf then + unhook(Keys.get_tree(mode, buf).tree:path(prefix_i), buf) + end + + -- feed CTRL-O again if called from CTRL-O + local full_mode = Util.get_mode() + if full_mode == "nii" or full_mode == "nir" or full_mode == "niv" or full_mode == "vs" then + vim.api.nvim_feedkeys(Util.t(""), "n", false) + end + + -- handle registers that were passed when opening the popup + if M.reg ~= '"' and M.mode ~= "i" and M.mode ~= "c" then + vim.api.nvim_feedkeys('"' .. M.reg, "n", false) + end + + if M.count and M.count ~= 0 then + prefix_i = M.count .. prefix_i + end + + -- feed the keys with remap + vim.api.nvim_feedkeys(prefix_i, "m", true) + + -- defer hooking WK until after the keys were executed + vim.defer_fn(function() + for _, hook in pairs(hooks) do + Keys.hook_add(hook[1], mode, hook[2]) + end + end, 0) +end + +function M.open(keys, opts) + opts = opts or {} + M.keys = keys or "" + M.mode = opts.mode or Util.get_mode() + M.count = vim.api.nvim_get_vvar("count") + M.reg = vim.api.nvim_get_vvar("register") + + if string.find(vim.o.clipboard, "unnamedplus") and M.reg == "+" then + M.reg = '"' + end + + if string.find(vim.o.clipboard, "unnamed") and M.reg == "*" then + M.reg = '"' + end + + M.show_cursor() + M.on_keys(opts) +end + +function M.is_enabled(buf) + local buftype = vim.api.nvim_buf_get_option(buf, "buftype") + for _, bt in ipairs(config.options.disable.buftypes) do + if bt == buftype then + return false + end + end + + local filetype = vim.api.nvim_buf_get_option(buf, "filetype") + for _, bt in ipairs(config.options.disable.filetypes) do + if bt == filetype then + return false + end + end + + return true +end + +function M.on_keys(opts) + local buf = vim.api.nvim_get_current_buf() + + while true do + -- loop + M.read_pending() + + local results = Keys.get_mappings(M.mode, M.keys, buf) + + --- Check for an exact match. Feedkeys with remap + if results.mapping and not results.mapping.group and #results.mappings == 0 then + M.hide() + if results.mapping.fn then + results.mapping.fn() + else + M.execute(M.keys, M.mode, buf) + end + return + end + + -- Check for no mappings found. Feedkeys without remap + if #results.mappings == 0 then + M.hide() + -- only execute if an actual key was typed while WK was open + if opts.auto then + M.execute(M.keys, M.mode, buf) + end + return + end + + local layout = Layout:new(results) + + if M.is_enabled(buf) then + if not M.is_valid() then + M.show() + end + + M.render(layout:layout(M.win)) + end + vim.cmd([[redraw]]) + + local c = M.getchar() + + if c == Util.t("") then + M.hide() + break + elseif c == Util.t(config.options.popup_mappings.scroll_down) then + M.scroll(false) + elseif c == Util.t(config.options.popup_mappings.scroll_up) then + M.scroll(true) + elseif c == Util.t("") then + M.back() + else + M.keys = M.keys .. c + end + end +end + +---@param text Text +function M.render(text) + vim.api.nvim_buf_set_lines(M.buf, 0, -1, false, text.lines) + local height = #text.lines + if height > config.options.layout.height.max then + height = config.options.layout.height.max + end + vim.api.nvim_win_set_height(M.win, height) + if vim.api.nvim_buf_is_valid(M.buf) then + vim.api.nvim_buf_clear_namespace(M.buf, config.namespace, 0, -1) + end + for _, data in ipairs(text.hl) do + highlight(M.buf, config.namespace, data.group, data.line, data.from, data.to) + end +end + +return M diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/plugin/which-key.vim b/config/neovim/store/lazy-plugins/which-key.nvim/plugin/which-key.vim new file mode 100644 index 00000000..2d14bfae --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/plugin/which-key.vim @@ -0,0 +1 @@ +command! -nargs=* WhichKey lua require('which-key').show_command() diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/selene.toml b/config/neovim/store/lazy-plugins/which-key.nvim/selene.toml new file mode 100644 index 00000000..6540d6f9 --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/selene.toml @@ -0,0 +1 @@ +std="lua51+vim" diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/stylua.toml b/config/neovim/store/lazy-plugins/which-key.nvim/stylua.toml new file mode 100644 index 00000000..5d6c50dc --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/stylua.toml @@ -0,0 +1,3 @@ +indent_type = "Spaces" +indent_width = 2 +column_width = 120 \ No newline at end of file diff --git a/config/neovim/store/lazy-plugins/which-key.nvim/vim.toml b/config/neovim/store/lazy-plugins/which-key.nvim/vim.toml new file mode 100644 index 00000000..0fa5c4fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/which-key.nvim/vim.toml @@ -0,0 +1,2 @@ +[vim] +any = true diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/.github/workflows/integration.yml b/config/neovim/store/lazy-plugins/yanky.nvim/.github/workflows/integration.yml new file mode 100644 index 00000000..0e2e40df --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/.github/workflows/integration.yml @@ -0,0 +1,47 @@ +name: Integration + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: '0 7 * * 1' + +jobs: + test: + name: Tests + runs-on: ubuntu-latest + strategy: + matrix: + nvim-versions: ['stable', 'nightly'] + steps: + - name: Checkout + uses: actions/checkout@v2 + - uses: rhysd/action-setup-vim@v1 + with: + neovim: true + version: ${{ matrix.nvim-versions }} + - name: Setup ‘lua’ + uses: leafo/gh-actions-lua@v8 + with: + luaVersion: '5.1.5' + - name: Setup ‘luarocks’ + uses: leafo/gh-actions-luarocks@v4 + - name: Install dependencies + run: | + luarocks install luacheck + - name: Run linter + run: luacheck lua/ spec/ + - name: Run tests + run: make test + stylua: + name: Check codestyle + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: JohnnyMorganz/stylua-action@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + args: --check . diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/.github/workflows/panvimdoc.yml b/config/neovim/store/lazy-plugins/yanky.nvim/.github/workflows/panvimdoc.yml new file mode 100644 index 00000000..e1be5d46 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/.github/workflows/panvimdoc.yml @@ -0,0 +1,22 @@ +name: panvimdoc + +on: + push: + branches: [main] + +jobs: + docs: + runs-on: ubuntu-latest + name: pandoc to vimdoc + steps: + - uses: actions/checkout@v2 + - name: panvimdoc + uses: kdheepak/panvimdoc@main + with: + vimdoc: yanky + description: Improved Yank an Put functionalities for Neovim + version: 'NVIM v0.6.0' + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: 'chore: auto generate docs' + branch: ${{ github.head_ref }} diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/.gitignore b/config/neovim/store/lazy-plugins/yanky.nvim/.gitignore new file mode 100644 index 00000000..24d74ce8 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/.gitignore @@ -0,0 +1,3 @@ +doc/tags +.spec +.luarc.json diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/.luacheckrc b/config/neovim/store/lazy-plugins/yanky.nvim/.luacheckrc new file mode 100644 index 00000000..653f1058 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/.luacheckrc @@ -0,0 +1 @@ +globals = { "vim" } diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/CODE_OF_CONDUCT.md b/config/neovim/store/lazy-plugins/yanky.nvim/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..1e022ee5 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +[Don't be a jerk.](https://meta.wikimedia.org/wiki/Don%27t_be_a_jerk) diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/LICENCE b/config/neovim/store/lazy-plugins/yanky.nvim/LICENCE new file mode 100644 index 00000000..eeeb40e6 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/LICENCE @@ -0,0 +1,12 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + +Copyright (C) 2022 Gilles Roustan + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/Makefile b/config/neovim/store/lazy-plugins/yanky.nvim/Makefile new file mode 100644 index 00000000..9525f173 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/Makefile @@ -0,0 +1,4 @@ +test: + @nvim --headless -u spec/init.lua -c "PlenaryBustedDirectory spec/ { minimal_init = 'spec//init.lua' }" + + diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/README.md b/config/neovim/store/lazy-plugins/yanky.nvim/README.md new file mode 100644 index 00000000..54bf7015 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/README.md @@ -0,0 +1,794 @@ +# 🍃 yanky.nvim + +![Lua](https://img.shields.io/badge/Made%20with%20Lua-blueviolet.svg?style=for-the-badge&logo=lua) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/gbprod/yanky.nvim/integration.yml?branch=main&style=for-the-badge)](https://github.com/gbprod/yanky.nvim/actions/workflows/integration.yml) + +The aim of `yanky.nvim` is to improve yank and put functionalities for Neovim. + +French slogan: + +> "Vas-y Yanky, c'est bon !" - Yanky Vincent + +Or in English: + +> "Yanky-ki-yay, motherf\*cker" - John McYanky + +## ✨ Features + +- 🖇️ Yank-ring +- 📜 Yank history picker +- 💡 Highlight put and yanked text +- ⤵️ Preserve cursor position on yank +- ⭐ Special put +- ⚓ Text object + +## ⚡️ Requirements + +Requires neovim > `0.9.0`. + +## 📦 Installation + +Install the plugin with your preferred package manager: + +### [lazy.nvim](https://github.com/folke/lazy.nvim) + +```lua +{ + "gbprod/yanky.nvim", + opts = { + -- your configuration comes here + -- or leave it empty to use the default settings + -- refer to the configuration section below + }, +} +``` + +
+More complete setup + +```lua +{ + "gbprod/yanky.nvim", + dependencies = { + { "kkharji/sqlite.lua" } + }, + opts = { + ring = { storage = "sqlite" }, + }, + keys = { + { "p", function() require("telescope").extensions.yank_history.yank_history({ }) end, desc = "Open Yank History" }, + { "y", "(YankyYank)", mode = { "n", "x" }, desc = "Yank text" }, + { "p", "(YankyPutAfter)", mode = { "n", "x" }, desc = "Put yanked text after cursor" }, + { "P", "(YankyPutBefore)", mode = { "n", "x" }, desc = "Put yanked text before cursor" }, + { "gp", "(YankyGPutAfter)", mode = { "n", "x" }, desc = "Put yanked text after selection" }, + { "gP", "(YankyGPutBefore)", mode = { "n", "x" }, desc = "Put yanked text before selection" }, + { "", "(YankyPreviousEntry)", desc = "Select previous entry through yank history" }, + { "", "(YankyNextEntry)", desc = "Select next entry through yank history" }, + { "]p", "(YankyPutIndentAfterLinewise)", desc = "Put indented after cursor (linewise)" }, + { "[p", "(YankyPutIndentBeforeLinewise)", desc = "Put indented before cursor (linewise)" }, + { "]P", "(YankyPutIndentAfterLinewise)", desc = "Put indented after cursor (linewise)" }, + { "[P", "(YankyPutIndentBeforeLinewise)", desc = "Put indented before cursor (linewise)" }, + { ">p", "(YankyPutIndentAfterShiftRight)", desc = "Put and indent right" }, + { "(YankyPutIndentAfterShiftLeft)", desc = "Put and indent left" }, + { ">P", "(YankyPutIndentBeforeShiftRight)", desc = "Put before and indent right" }, + { "(YankyPutIndentBeforeShiftLeft)", desc = "Put before and indent left" }, + { "=p", "(YankyPutAfterFilter)", desc = "Put after applying a filter" }, + { "=P", "(YankyPutBeforeFilter)", desc = "Put before applying a filter" }, + }, +} +``` + +
+ +### [packer](https://github.com/wbthomason/packer.nvim) + +```lua +-- Lua +use("gbprod/yanky.nvim") +require("yanky").setup({ + -- your configuration comes here + -- or leave it empty to use the default settings + -- refer to the configuration section below +}) +``` + +## ⚙️ Configuration + +Yanky comes with the following defaults: + +```lua +{ + ring = { + history_length = 100, + storage = "shada", + storage_path = vim.fn.stdpath("data") .. "/databases/yanky.db", -- Only for sqlite storage + sync_with_numbered_registers = true, + cancel_event = "update", + ignore_registers = { "_" }, + update_register_on_cycle = false, + }, + picker = { + select = { + action = nil, -- nil to use default put action + }, + telescope = { + use_default_mappings = true, -- if default mappings should be used + mappings = nil, -- nil to use default mappings or no mappings (see `use_default_mappings`) + }, + }, + system_clipboard = { + sync_with_ring = true, + clipboard_register = nil, + }, + highlight = { + on_put = true, + on_yank = true, + timer = 500, + }, + preserve_cursor_position = { + enabled = true, + }, + textobj = { + enabled = true, + }, +} +``` + +### ⌨️ Mappings + +**This plugin contains no default mappings and will have no effect until you add your own maps to it.** +You should at least set those keymaps for yank ring usage: + +```lua +vim.keymap.set({"n","x"}, "p", "(YankyPutAfter)") +vim.keymap.set({"n","x"}, "P", "(YankyPutBefore)") +vim.keymap.set({"n","x"}, "gp", "(YankyGPutAfter)") +vim.keymap.set({"n","x"}, "gP", "(YankyGPutBefore)") + +vim.keymap.set("n", "", "(YankyPreviousEntry)") +vim.keymap.set("n", "", "(YankyNextEntry)") +``` + +And those keymaps for `tpope/vim-unimpaired` like usage: + +```lua +vim.keymap.set("n", "]p", "(YankyPutIndentAfterLinewise)") +vim.keymap.set("n", "[p", "(YankyPutIndentBeforeLinewise)") +vim.keymap.set("n", "]P", "(YankyPutIndentAfterLinewise)") +vim.keymap.set("n", "[P", "(YankyPutIndentBeforeLinewise)") + +vim.keymap.set("n", ">p", "(YankyPutIndentAfterShiftRight)") +vim.keymap.set("n", "(YankyPutIndentAfterShiftLeft)") +vim.keymap.set("n", ">P", "(YankyPutIndentBeforeShiftRight)") +vim.keymap.set("n", "(YankyPutIndentBeforeShiftLeft)") + +vim.keymap.set("n", "=p", "(YankyPutAfterFilter)") +vim.keymap.set("n", "=P", "(YankyPutBeforeFilter)") +``` + +Some features requires specific mappings, refer to feature documentation section. + +## 🖇️ Yank-ring + +Yank-ring allows cycling throught yank history when putting text (like the Emacs +"kill-ring" feature). Yanky automatically maintain a history of yanks that you +can choose between when pasting. + +### ⌨️ Mappings + +```lua +vim.keymap.set("n", "", "(YankyPreviousEntry)") +vim.keymap.set("n", "", "(YankyNextEntry)") +``` + +With these mappings, after performing a paste, you can cycle through the history +by hitting `` and ``. Any modifications done after pasting will cancel +the possibility to cycle. + +Note that the swap operations above will only affect the current paste and the +history will be unchanged. + +### ⚙️ Configuration + +```lua +require("yanky").setup({ + ring = { + history_length = 100, + storage = "shada", + sync_with_numbered_registers = true, + cancel_event = "update", + ignore_registers = { "_" }, + update_register_on_cycle = false, + }, + system_clipboard = { + sync_with_ring = true, + }, +}) +``` + +#### `ring.history_length` + +Default : `100` + +Define the number of yanked items that will be saved and used for ring. + +#### `ring.storage` + +Default : `shada` + +Available : `shada`, `sqlite` or `memory` + +Define the storage mode for ring values. + +Using `shada`, this will save pesistantly using Neovim ShaDa feature. This means +that history will be persisted between each session of Neovim. + +You can also use this feature to sync the yank history across multiple running instances +of Neovim by updating shada file. If you execute `:wshada` in the first instance +and then `:rshada` in the second instance, the second instance will be synced with +the yank history in the first instance. + +Using `memory`, each Neovim instance will have his own history and il will be +lost between sessions. + +If you want to use `sqlite` as storage, you must add [`kkharji/sqlite.lua`](https://github.com/kkharji/sqlite.lua) as dependency: + +```lua +use({ + "gbprod/yanky.nvim", + requires = { "kkharji/sqlite.lua" } +}) +``` + +Sqlite is more reliable than ShaDa but requires more dependencies. You can +change the storage path using `ring.storage_path` option. + +### `ring.sync_with_numbered_registers` + +Default : `true` + +History can also be synchronized with numbered registers. Every time the yank +history changes the numbered registers 1 - 9 will be updated to sync with the +first 9 entries in the yank history. See [here](http://vimcasts.org/blog/2013/11/registers-the-good-the-bad-and-the-ugly-parts/) +for an explanation of why we would want do do this. + +### `ring.cancel_event` + +Default: `update` + +Define the event used to cancel ring activation. `update` will cancel ring on +next buffer update, `move` will cancel ring when moving cursor or content +changed. + +### `ring.ignore_registers ` + +Default: `{ "_" }` + +Define registeres to be ignored. By default the black hole register is ignored. + +### `system_clipboard.sync_with_ring` + +Default: `true` + +Yanky can automatically adds to ring history yanks that occurs outside of Neovim. +This works regardless to your `&clipboard` setting. + +This means, if `&clipboard` is set to `unnamed` and/or `unnamedplus`, if you yank +something outside of Neovim, you can put it immediatly using `p` and it will be +added to your yank ring. + +If `&clipboard` is empty, if you yank something outside of Neovim, this will be +the first value you'll have when cycling through the ring. Basicly, you can do +`p` and then `` to paste yanked text. + +Note that `clipboard == unnamed` uses the primary selection of the system (see +`:h clipbard` for more details) which is updated on selection, not on copy/yank. +Also note that the syncing happens when neovim gains focus. + +### `system_clipboard.clipboard_register` + +Default: `nil` use `&clipboard` + +Choose the register that is synced with ring (from above). If `&clipboard` is +empty then `*` is used. + +### `ring.update_register_on_cycle` + +Default: `false` + +Using the `update_register_on_cycle` option, when you cycle through the ring, +the contents of the register used to update will be updated with the last +content cycled. + +### Commands + +You can clear yank history using `YankyClearHistory` command. + +## 📜 Yank history picker + +This allows you to select an entry in your recorded yank history using default +`vim.ui.select` neovim prompt (you can use [stevearc/dressing.nvim](https://github.com/stevearc/dressing.nvim/) +to customize this) or the awesome [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim). + +It uses the same history as yank ring, so, if you want to increase history size, +just use [`ring.history_length` option](#ringhistory_length). + +See [Integrations](#-integrations) to have a completion with [nvim-cmp](https://github.com/hrsh7th/nvim-cmp). + +### Yank history completions + +Using [nvim-cmp](https://github.com/hrsh7th/nvim-cmp) and [cmp_yanky](https://github.com/chrisgrieser/cmp_yanky), you can also get suggestions from your yank history as you type in insert mode. + +
+demonstration +showcasing cmp-yanky +
+ +### ⚙️ Configuration + +To use `vim.ui.select` picker, just call `YankyRingHistory` command. + +To use the `yank_history` Telescope picker, register `yank_history` as a +Telescope extension in your Neovim config file. + +```lua +:lua require("telescope").load_extension("yank_history") +``` + +After loading the extension, you can access the picker by running: + +```vim +:Telescope yank_history +``` + +Or: + +```lua +:lua require("telescope").extensions.yank_history.yank_history() +``` + +Set the Telescope option [`dynamic_preview_title`](https://github.com/nvim-telescope/telescope.nvim/blob/master/doc/telescope.txt) +to `true` if you want your Telescope preview window to have a +dynamic title showing the register's type. + +Default configuration : + +```lua +require("yanky").setup({ + picker = { + select = { + action = nil, -- nil to use default put action + }, + telescope = { + mappings = nil, -- nil to use default mappings + }, + }, +}) +``` + +#### `picker.select.action` + +Default : `nil` + +This define the action that should be done when selecting an item in the +`vim.ui.select` prompt. If you let this option to `nil`, this will use the +default action : put selected item after cursor. + +Available actions: + +```lua +require("yanky.picker").actions.put("p") -- put after cursor +require("yanky.picker").actions.put("P") -- put before cursor +require("yanky.picker").actions.put("gp") -- put after cursor and leave the cursor after +require("yanky.picker").actions.put("gP") -- put before cursor and leave the cursor after +require("yanky.picker").actions.delete() -- delete entry from yank history +require("yanky.picker").actions.set_register(regname) -- fill register with selected value +require("yanky.picker").actions.put_and_set_register("p", regname) -- put and fill register with selected value +``` + +#### `picker.telescope.use_default_mappings` + +Default : `true` + +This define if default Telescope mappings should be use. + +If you let this option to `true`, this will use the default mappings : + +```lua +local utils = require("yanky.utils") +local mapping = require("yanky.telescope.mapping") + +require("yanky").setup({ + picker = { + telescope = { + mappings = { + default = mapping.put("p"), + i = { + [""] = mapping.put("p"), + [""] = mapping.put("P"), + [""] = mapping.delete(), + [""] = mapping.set_register(utils.get_default_register()), + }, + n = { + p = mapping.put("p"), + P = mapping.put("P"), + d = mapping.delete(), + r = mapping.set_register(utils.get_default_register()) + }, + } + } + } +}) +``` + +#### `picker.telescope.mappings` + +Default : `nil` + +This define or overrides the mappings available in Telescope. + +If you set `use_default_mappings` to `true`, mappings will be merged with default mappings. + +**Available actions:** + +```lua +require("yanky.telescope.mapping").put("p") -- put after cursor +require("yanky.telescope.mapping").put("P") -- put before cursor +require("yanky.telescope.mapping").put("gp") -- put after cursor and leave the cursor after +require("yanky.telescope.mapping").put("gP") -- put before cursor and leave the cursor after +require("yanky.telescope.mapping").delete() -- delete entry from yank history +require("yanky.telescope.mapping").set_register(regname) -- fill register {regname} with selected value +``` + +You can also use any of available [special puts](https://github.com/gbprod/yanky.nvim#%EF%B8%8F-special-put) like this: + +```lua +require("yanky.telescope.mapping").special_put("{{ name of the special put }}") + +-- eg. +require("yanky.telescope.mapping").special_put("YankyPutAfterCharwiseJoined") +``` + +## 💡 Highlight put and yanked text + +This will give you a visual feedback on put and yank text +by highlighting this. + +### Configuration + +```lua +require("yanky").setup({ + highlight = { + on_put = true, + on_yank = true, + timer = 500, + }, +}) +``` + +You can override `YankyPut` highlight to change colors. + +#### `highlight.on_put` + +Default : `true` + +Define if highlight put text feature is enabled. + +#### `highlight.on_yank` + +Default : `true` + +Define if highlight yanked text feature is enabled. + +#### `highlight.timer` + +Default : `500` + +Define the duration of highlight. + +## ⤵️ Preserve cursor position on yank + +By default in Neovim, when yanking text, cursor moves to the start of the yanked +text. Could be annoying especially when yanking a large text object such as a +paragraph or a large text object. + +With this feature, yank will function exactly the same as previously with the one +difference being that the cursor position will not change after performing a yank. + +### ⌨️ Mappings + +```lua +vim.keymap.set({"n","x"}, "y", "(YankyYank)") +``` + +### ⚙️ Configuration + +```lua +require("yanky").setup({ + preserve_cursor_position = { + enabled = true, + }, +}) +``` + +#### `preserve_cursor_position.enabled` + +Default : `true` + +Define if cursor position should be preserved on yank. This works only if mappings +has been defined. + +## ⭐ Special put + +Yanky comes with special put moves (inspired by +[tpope/vim-unimpaired](https://github.com/tpope/vim-unimpaired/blob/master/doc/unimpaired.txt#L100)): + +- Linewise put: this will force put above or below the current line ; +- Shift right/left put: will put above or below the current line and increasing + or decreasing indent ; +- Filter put: will put above or below the current line and reindenting. + +### ⌨️ Mappings + +For basic usage (like with [tpope/vim-unimpaired](https://github.com/tpope/vim-unimpaired/blob/master/doc/unimpaired.txt#L100)), +you can use those bindings: + +```lua +vim.keymap.set("n", "]p", "(YankyPutIndentAfterLinewise)") +vim.keymap.set("n", "[p", "(YankyPutIndentBeforeLinewise)") +vim.keymap.set("n", "]P", "(YankyPutIndentAfterLinewise)") +vim.keymap.set("n", "[P", "(YankyPutIndentBeforeLinewise)") + +vim.keymap.set("n", ">p", "(YankyPutIndentAfterShiftRight)") +vim.keymap.set("n", "(YankyPutIndentAfterShiftLeft)") +vim.keymap.set("n", ">P", "(YankyPutIndentBeforeShiftRight)") +vim.keymap.set("n", "(YankyPutIndentBeforeShiftLeft)") + +vim.keymap.set("n", "=p", "(YankyPutAfterFilter)") +vim.keymap.set("n", "=P", "(YankyPutBeforeFilter)") +``` + +To go further, Plug mappings are constructed like this: `Yanky(put-type)(modifier)(rewriter)`. + +`put-type` can be: + +- `PutAfter`: put after your cursor (as [`p`](https://neovim.io/doc/user/change.html#put) key) ; +- `PutBefore`: put before your cursor (as [`P`](https://neovim.io/doc/user/change.html#P) key) ; +- `GPutAfter`: like `PutAfter` but leave the cursor after the new text (as [`gp`](https://neovim.io/doc/user/change.html#gp) key) ; +- `GPutBefore`: like `PutBefore` but leave the cursor after the new text (as [`gP`](https://neovim.io/doc/user/change.html#gP) key) ; +- `PutIndentAfter`: like `PutAfter` but adjust the indent to the current line (as [`]p`](https://neovim.io/doc/user/change.html#]p) key) ; +- `PutIndentBefore`: like `PutBefore` but adjust the indent to the current line (as [`[p`](https://neovim.io/doc/user/change.html#[p) key) ; + +`modifier` (optional) can be: + +- `Linewise`: put in linewise mode ; +- `Charwise`: put in charwise mode ; +- `Blockwise`: put in blockwise mode ; +- `ShiftRight`: increase indent ; +- `ShiftLeft`: decrease indent. + +`rewriter` (optional) can be: + +- `Joined`: put lines trimed and joined. + +
+All special puts + +```vim +(YankyPutAfter) +(YankyPutAfterBlockwise) +(YankyPutAfterBlockwiseJoined) +(YankyPutAfterCharwise) +(YankyPutAfterCharwiseJoined) +(YankyPutAfterFilter) +(YankyPutAfterFilterJoined) +(YankyPutAfterJoined) +(YankyPutAfterLinewise) +(YankyPutAfterLinewiseJoined) +(YankyPutAfterShiftLeft) +(YankyPutAfterShiftLeftJoined) +(YankyPutAfterShiftRight) +(YankyPutAfterShiftRightJoined) +(YankyPutBefore) +(YankyPutBeforeBlockwise) +(YankyPutBeforeBlockwiseJoined) +(YankyPutBeforeCharwise) +(YankyPutBeforeCharwiseJoined) +(YankyPutBeforeFilter) +(YankyPutBeforeFilterJoined) +(YankyPutBeforeJoined) +(YankyPutBeforeLinewise) +(YankyPutBeforeLinewiseJoined) +(YankyPutBeforeShiftLeft) +(YankyPutBeforeShiftLeftJoined) +(YankyPutBeforeShiftRight) +(YankyPutBeforeShiftRightJoined) +(YankyGPutAfter) +(YankyGPutAfterBlockwise) +(YankyGPutAfterBlockwiseJoined) +(YankyGPutAfterCharwise) +(YankyGPutAfterCharwiseJoined) +(YankyGPutAfterFilter) +(YankyGPutAfterFilterJoined) +(YankyGPutAfterJoined) +(YankyGPutAfterLinewise) +(YankyGPutAfterLinewiseJoined) +(YankyGPutAfterShiftLeft) +(YankyGPutAfterShiftLeftJoined) +(YankyGPutAfterShiftRight) +(YankyGPutAfterShiftRightJoined) +(YankyGPutBefore) +(YankyGPutBeforeBlockwise) +(YankyGPutBeforeBlockwiseJoined) +(YankyGPutBeforeCharwise) +(YankyGPutBeforeCharwiseJoined) +(YankyGPutBeforeFilter) +(YankyGPutBeforeFilterJoined) +(YankyGPutBeforeJoined) +(YankyGPutBeforeLinewise) +(YankyGPutBeforeLinewiseJoined) +(YankyGPutBeforeShiftLeft) +(YankyGPutBeforeShiftLeftJoined) +(YankyGPutBeforeShiftRight) +(YankyGPutBeforeShiftRightJoined) +(YankyPutIndentAfter) +(YankyPutIndentAfterBlockwise) +(YankyPutIndentAfterBlockwiseJoined) +(YankyPutIndentAfterCharwise) +(YankyPutIndentAfterCharwiseJoined) +(YankyPutIndentAfterFilter) +(YankyPutIndentAfterFilterJoined) +(YankyPutIndentAfterJoined) +(YankyPutIndentAfterLinewise) +(YankyPutIndentAfterLinewiseJoined) +(YankyPutIndentAfterShiftLeft) +(YankyPutIndentAfterShiftLeftJoined) +(YankyPutIndentAfterShiftRight) +(YankyPutIndentAfterShiftRightJoined) +(YankyPutIndentBefore) +(YankyPutIndentBeforeBlockwise) +(YankyPutIndentBeforeBlockwiseJoined) +(YankyPutIndentBeforeCharwise) +(YankyPutIndentBeforeCharwiseJoined) +(YankyPutIndentBeforeFilter) +(YankyPutIndentBeforeFilterJoined) +(YankyPutIndentBeforeJoined) +(YankyPutIndentBeforeLinewise) +(YankyPutIndentBeforeLinewiseJoined) +(YankyPutIndentBeforeShiftLeft) +(YankyPutIndentBeforeShiftLeftJoined) +(YankyPutIndentBeforeShiftRight) +(YankyPutIndentBeforeShiftRightJoined) +``` + +
+ +## ⚓ Text object + +Yanky comes with a text object corresponding to last put text. To use it, you +have to enable it in settings and set a keymap. + +### ⚙️ Configuration + +```lua +require("yanky").setup({ + textobj = { + enabled = true, + }, +}) +``` + +### ⌨️ Mappings + +```lua +vim.keymap.set({ "o", "x" }, "lp", function() + require("yanky.textobj").last_put() +end, {}) +``` + +## 🎨 Colors + +| Description | Group | Default | +| ------------------------------- | ----------- | -------------- | +| Highlight color for put text | YankyPut | link to Search | +| Highlight color for yanked text | YankyYanked | link to Search | + +## 🤝 Integrations + +
+gbprod/substitute.nvim + +To enable [gbprod/substitute.nvim](https://github.com/gbprod/substitute.nvim) +swap when performing a substitution, you can add this to your setup: + +```lua +require("substitute").setup({ + on_substitute = require("yanky.integration").substitute(), +}) +``` + +
+ +
+hrsh7th/nvim-cmp + +Using [hrsh7th/nvim-cmp](https://github.com/hrsh7th/nvim-cmp) and [chrisgrieser/cmp_yanky](https://github.com/chrisgrieser/cmp_yanky), you can also get suggestions from your yank history as you type in insert mode. + +showcasing cmp-yanky +
+ +
+anuvyklack/hydra.nvim + +To work with [anuvyklack/hydra.nvim](https://github.com/anuvyklack/hydra.nvim) +only setup / mapping when yanky is activated, you can add this to your setup: + +```lua +local Hydra = require("hydra") + +local function t(str) + return api.nvim_replace_termcodes(str, true, true, true) +end + +local yanky_hydra = Hydra({ + name = "Yank ring", + mode = "n", + heads = { + { "p", "(YankyPutAfter)", { desc = "After" } }, + { "P", "(YankyPutBefore)", { desc = "Before" } }, + { "", "(YankyPreviousEntry)", { private = true, desc = "↑" } }, + { "", "(YankyNextEntry)", { private = true, desc = "↓" } }, + }, +}) + +-- choose/change the mappings if you want +for key, putAction in pairs({ + ["p"] = "(YankyPutAfter)", + ["P"] = "(YankyPutBefore)", + ["gp"] = "(YankyGPutAfter)", + ["gP"] = "(YankyGPutBefore)", +}) do + vim.keymap.set({ "n", "x" }, key, function() + vim.fn.feedkeys(t(putAction)) + yanky_hydra:activate() + end) +end + +-- choose/change the mappings if you want +for key, putAction in pairs({ + ["]p"] = "(YankyPutIndentAfterLinewise)", + ["[p"] = "(YankyPutIndentBeforeLinewise)", + ["]P"] = "(YankyPutIndentAfterLinewise)", + ["[P"] = "(YankyPutIndentBeforeLinewise)", + + [">p"] = "(YankyPutIndentAfterShiftRight)", + ["(YankyPutIndentAfterShiftLeft)", + [">P"] = "(YankyPutIndentBeforeShiftRight)", + ["(YankyPutIndentBeforeShiftLeft)", + + ["=p"] = "(YankyPutAfterFilter)", + ["=P"] = "(YankyPutBeforeFilter)", +}) do + vim.keymap.set("n", key, function() + vim.fn.feedkeys(t(putAction)) + yanky_hydra:activate() + end) +end +``` + +
+ +## 🎉 Credits + +This plugin is mostly a lua version of [svermeulen/vim-yoink](https://github.com/svermeulen/vim-yoink) +awesome plugin. + +Other inspiration : + +- [bfredl/nvim-miniyank](https://github.com/bfredl/nvim-miniyank) +- [maxbrunsfeld/vim-yankstack](https://github.com/maxbrunsfeld/vim-yankstack) +- [svermeulen/vim-easyclip](https://github.com/svermeulen/vim-easyclip) +- [bkoropoff/yankee.vim](https://github.com/bkoropoff/yankee.vim) +- [svban/YankAssassin.vim](https://github.com/svban/YankAssassin.vim) +- [tpope/vim-unimpaired](https://github.com/tpope/vim-unimpaired) +- [inkarkat/vim-UnconditionalPaste](https://github.com/inkarkat/vim-UnconditionalPaste) diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/doc/yanky.txt b/config/neovim/store/lazy-plugins/yanky.nvim/doc/yanky.txt new file mode 100644 index 00000000..5a40d137 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/doc/yanky.txt @@ -0,0 +1,861 @@ +*yanky.txt* Improved Yank an Put functionalities for Neovim + +============================================================================== +Table of Contents *yanky-table-of-contents* + +1. 🍃 yanky.nvim |yanky-🍃-yanky.nvim| + - ✨ Features |yanky-🍃-yanky.nvim-✨-features| + - ⚡️ Requirements |yanky-🍃-yanky.nvim-⚡️-requirements| + - 📦 Installation |yanky-🍃-yanky.nvim-📦-installation| + - ⚙️ Configuration |yanky-🍃-yanky.nvim-⚙️-configuration| + - 🖇️ Yank-ring |yanky-🍃-yanky.nvim-🖇️-yank-ring| + - 📜 Yank history picker |yanky-🍃-yanky.nvim-📜-yank-history-picker| + - 💡 Highlight put and yanked text|yanky-🍃-yanky.nvim-💡-highlight-put-and-yanked-text| + - ⤵️ Preserve cursor position on yank|yanky-🍃-yanky.nvim-⤵️-preserve-cursor-position-on-yank| + - ⭐ Special put |yanky-🍃-yanky.nvim-⭐-special-put| + - ⚓ Text object |yanky-🍃-yanky.nvim-⚓-text-object| + - 🎨 Colors |yanky-🍃-yanky.nvim-🎨-colors| + - 🤝 Integrations |yanky-🍃-yanky.nvim-🤝-integrations| + - 🎉 Credits |yanky-🍃-yanky.nvim-🎉-credits| +2. Links |yanky-links| + +============================================================================== +1. 🍃 yanky.nvim *yanky-🍃-yanky.nvim* + + + +The aim of `yanky.nvim` is to improve yank and put functionalities for Neovim. + +French slogan: + + + "Vas-y Yanky, c’est bon !" - Yanky Vincent +Or in English: + + + "Yanky-ki-yay, motherf*cker" - John McYanky + +✨ FEATURES *yanky-🍃-yanky.nvim-✨-features* + +- 🖇️ Yank-ring +- 📜 Yank history picker +- 💡 Highlight put and yanked text +- ⤵️ Preserve cursor position on yank +- ⭐ Special put +- ⚓ Text object + + +⚡️ REQUIREMENTS *yanky-🍃-yanky.nvim-⚡️-requirements* + +Requires neovim > `0.9.0`. + + +📦 INSTALLATION *yanky-🍃-yanky.nvim-📦-installation* + +Install the plugin with your preferred package manager: + + +LAZY.NVIM ~ + +>lua + { + "gbprod/yanky.nvim", + opts = { + -- your configuration comes here + -- or leave it empty to use the default settings + -- refer to the configuration section below + }, + } +< + +More complete setup ~ + +>lua + { + "gbprod/yanky.nvim", + dependencies = { + { "kkharji/sqlite.lua" } + }, + opts = { + ring = { storage = "sqlite" }, + }, + keys = { + { "p", function() require("telescope").extensions.yank_history.yank_history({ }) end, desc = "Open Yank History" }, + { "y", "(YankyYank)", mode = { "n", "x" }, desc = "Yank text" }, + { "p", "(YankyPutAfter)", mode = { "n", "x" }, desc = "Put yanked text after cursor" }, + { "P", "(YankyPutBefore)", mode = { "n", "x" }, desc = "Put yanked text before cursor" }, + { "gp", "(YankyGPutAfter)", mode = { "n", "x" }, desc = "Put yanked text after selection" }, + { "gP", "(YankyGPutBefore)", mode = { "n", "x" }, desc = "Put yanked text before selection" }, + { "", "(YankyPreviousEntry)", desc = "Select previous entry through yank history" }, + { "", "(YankyNextEntry)", desc = "Select next entry through yank history" }, + { "]p", "(YankyPutIndentAfterLinewise)", desc = "Put indented after cursor (linewise)" }, + { "[p", "(YankyPutIndentBeforeLinewise)", desc = "Put indented before cursor (linewise)" }, + { "]P", "(YankyPutIndentAfterLinewise)", desc = "Put indented after cursor (linewise)" }, + { "[P", "(YankyPutIndentBeforeLinewise)", desc = "Put indented before cursor (linewise)" }, + { ">p", "(YankyPutIndentAfterShiftRight)", desc = "Put and indent right" }, + { "(YankyPutIndentAfterShiftLeft)", desc = "Put and indent left" }, + { ">P", "(YankyPutIndentBeforeShiftRight)", desc = "Put before and indent right" }, + { "(YankyPutIndentBeforeShiftLeft)", desc = "Put before and indent left" }, + { "=p", "(YankyPutAfterFilter)", desc = "Put after applying a filter" }, + { "=P", "(YankyPutBeforeFilter)", desc = "Put before applying a filter" }, + }, + } +< + + +PACKER ~ + +>lua + -- Lua + use("gbprod/yanky.nvim") + require("yanky").setup({ + -- your configuration comes here + -- or leave it empty to use the default settings + -- refer to the configuration section below + }) +< + + +⚙️ CONFIGURATION *yanky-🍃-yanky.nvim-⚙️-configuration* + +Yanky comes with the following defaults: + +>lua + { + ring = { + history_length = 100, + storage = "shada", + storage_path = vim.fn.stdpath("data") .. "/databases/yanky.db", -- Only for sqlite storage + sync_with_numbered_registers = true, + cancel_event = "update", + ignore_registers = { "_" }, + update_register_on_cycle = false, + }, + picker = { + select = { + action = nil, -- nil to use default put action + }, + telescope = { + use_default_mappings = true, -- if default mappings should be used + mappings = nil, -- nil to use default mappings or no mappings (see `use_default_mappings`) + }, + }, + system_clipboard = { + sync_with_ring = true, + clipboard_register = nil, + }, + highlight = { + on_put = true, + on_yank = true, + timer = 500, + }, + preserve_cursor_position = { + enabled = true, + }, + textobj = { + enabled = true, + }, + } +< + + +⌨️ MAPPINGS ~ + +**This plugin contains no default mappings and will have no effect until you +add your own maps to it.** You should at least set those keymaps for yank ring +usage: + +>lua + vim.keymap.set({"n","x"}, "p", "(YankyPutAfter)") + vim.keymap.set({"n","x"}, "P", "(YankyPutBefore)") + vim.keymap.set({"n","x"}, "gp", "(YankyGPutAfter)") + vim.keymap.set({"n","x"}, "gP", "(YankyGPutBefore)") + + vim.keymap.set("n", "", "(YankyPreviousEntry)") + vim.keymap.set("n", "", "(YankyNextEntry)") +< + +And those keymaps for `tpope/vim-unimpaired` like usage: + +>lua + vim.keymap.set("n", "]p", "(YankyPutIndentAfterLinewise)") + vim.keymap.set("n", "[p", "(YankyPutIndentBeforeLinewise)") + vim.keymap.set("n", "]P", "(YankyPutIndentAfterLinewise)") + vim.keymap.set("n", "[P", "(YankyPutIndentBeforeLinewise)") + + vim.keymap.set("n", ">p", "(YankyPutIndentAfterShiftRight)") + vim.keymap.set("n", "(YankyPutIndentAfterShiftLeft)") + vim.keymap.set("n", ">P", "(YankyPutIndentBeforeShiftRight)") + vim.keymap.set("n", "(YankyPutIndentBeforeShiftLeft)") + + vim.keymap.set("n", "=p", "(YankyPutAfterFilter)") + vim.keymap.set("n", "=P", "(YankyPutBeforeFilter)") +< + +Some features requires specific mappings, refer to feature documentation +section. + + +🖇️ YANK-RING *yanky-🍃-yanky.nvim-🖇️-yank-ring* + +Yank-ring allows cycling throught yank history when putting text (like the +Emacs "kill-ring" feature). Yanky automatically maintain a history of yanks +that you can choose between when pasting. + + +⌨️ MAPPINGS ~ + +>lua + vim.keymap.set("n", "", "(YankyPreviousEntry)") + vim.keymap.set("n", "", "(YankyNextEntry)") +< + +With these mappings, after performing a paste, you can cycle through the +history by hitting `` and ``. Any modifications done after pasting +will cancel the possibility to cycle. + +Note that the swap operations above will only affect the current paste and the +history will be unchanged. + + +⚙️ CONFIGURATION ~ + +>lua + require("yanky").setup({ + ring = { + history_length = 100, + storage = "shada", + sync_with_numbered_registers = true, + cancel_event = "update", + ignore_registers = { "_" }, + update_register_on_cycle = false, + }, + system_clipboard = { + sync_with_ring = true, + }, + }) +< + + +RING.HISTORY_LENGTH + +Default : `100` + +Define the number of yanked items that will be saved and used for ring. + + +RING.STORAGE + +Default : `shada` + +Available : `shada`, `sqlite` or `memory` + +Define the storage mode for ring values. + +Using `shada`, this will save pesistantly using Neovim ShaDa feature. This +means that history will be persisted between each session of Neovim. + +You can also use this feature to sync the yank history across multiple running +instances of Neovim by updating shada file. If you execute `:wshada` in the +first instance and then `:rshada` in the second instance, the second instance +will be synced with the yank history in the first instance. + +Using `memory`, each Neovim instance will have his own history and il will be +lost between sessions. + +If you want to use `sqlite` as storage, you must add `kkharji/sqlite.lua` + as dependency: + +>lua + use({ + "gbprod/yanky.nvim", + requires = { "kkharji/sqlite.lua" } + }) +< + +Sqlite is more reliable than ShaDa but requires more dependencies. You can +change the storage path using `ring.storage_path` option. + + +RING.SYNC_WITH_NUMBERED_REGISTERS ~ + +Default : `true` + +History can also be synchronized with numbered registers. Every time the yank +history changes the numbered registers 1 - 9 will be updated to sync with the +first 9 entries in the yank history. See here + +for an explanation of why we would want do do this. + + +RING.CANCEL_EVENT ~ + +Default: `update` + +Define the event used to cancel ring activation. `update` will cancel ring on +next buffer update, `move` will cancel ring when moving cursor or content +changed. + + +RING.IGNORE_REGISTERS ~ + +Default: `{ "_" }` + +Define registeres to be ignored. By default the black hole register is ignored. + + +SYSTEM_CLIPBOARD.SYNC_WITH_RING ~ + +Default: `true` + +Yanky can automatically adds to ring history yanks that occurs outside of +Neovim. This works regardless to your `&clipboard` setting. + +This means, if `&clipboard` is set to `unnamed` and/or `unnamedplus`, if you +yank something outside of Neovim, you can put it immediatly using `p` and it +will be added to your yank ring. + +If `&clipboard` is empty, if you yank something outside of Neovim, this will be +the first value you’ll have when cycling through the ring. Basicly, you can +do `p` and then `` to paste yanked text. + +Note that `clipboard == unnamed` uses the primary selection of the system (see +|clipbard| for more details) which is updated on selection, not on copy/yank. +Also note that the syncing happens when neovim gains focus. + + +SYSTEM_CLIPBOARD.CLIPBOARD_REGISTER ~ + +Default: `nil` use `&clipboard` + +Choose the register that is synced with ring (from above). If `&clipboard` is +empty then `*` is used. + + +RING.UPDATE_REGISTER_ON_CYCLE ~ + +Default: `false` + +Using the `update_register_on_cycle` option, when you cycle through the ring, +the contents of the register used to update will be updated with the last +content cycled. + + +COMMANDS ~ + +You can clear yank history using `YankyClearHistory` command. + + +📜 YANK HISTORY PICKER *yanky-🍃-yanky.nvim-📜-yank-history-picker* + +This allows you to select an entry in your recorded yank history using default +`vim.ui.select` neovim prompt (you can use stevearc/dressing.nvim + to customize this) or the awesome +telescope.nvim . + +It uses the same history as yank ring, so, if you want to increase history +size, just use |yanky-`ring.history_length`-option|. + +See |yanky-integrations| to have a completion with nvim-cmp +. + + +YANK HISTORY COMPLETIONS ~ + +Using nvim-cmp and cmp_yanky +, you can also get suggestions from +your yank history as you type in insert mode. + +demonstration ~ + + +⚙️ CONFIGURATION ~ + +To use `vim.ui.select` picker, just call `YankyRingHistory` command. + +To use the `yank_history` Telescope picker, register `yank_history` as a +Telescope extension in your Neovim config file. + +>lua + :lua require("telescope").load_extension("yank_history") +< + +After loading the extension, you can access the picker by running: + +>vim + :Telescope yank_history +< + +Or: + +>lua + :lua require("telescope").extensions.yank_history.yank_history() +< + +Set the Telescope option `dynamic_preview_title` + +to `true` if you want your Telescope preview window to have a dynamic title +showing the register’s type. + +Default configuration : + +>lua + require("yanky").setup({ + picker = { + select = { + action = nil, -- nil to use default put action + }, + telescope = { + mappings = nil, -- nil to use default mappings + }, + }, + }) +< + + +PICKER.SELECT.ACTION + +Default : `nil` + +This define the action that should be done when selecting an item in the +`vim.ui.select` prompt. If you let this option to `nil`, this will use the +default action : put selected item after cursor. + +Available actions: + +>lua + require("yanky.picker").actions.put("p") -- put after cursor + require("yanky.picker").actions.put("P") -- put before cursor + require("yanky.picker").actions.put("gp") -- put after cursor and leave the cursor after + require("yanky.picker").actions.put("gP") -- put before cursor and leave the cursor after + require("yanky.picker").actions.delete() -- delete entry from yank history + require("yanky.picker").actions.set_register(regname) -- fill register with selected value + require("yanky.picker").actions.put_and_set_register("p", regname) -- put and fill register with selected value +< + + +PICKER.TELESCOPE.USE_DEFAULT_MAPPINGS + +Default : `true` + +This define if default Telescope mappings should be use. + +If you let this option to `true`, this will use the default mappings : + +>lua + local utils = require("yanky.utils") + local mapping = require("yanky.telescope.mapping") + + require("yanky").setup({ + picker = { + telescope = { + mappings = { + default = mapping.put("p"), + i = { + [""] = mapping.put("p"), + [""] = mapping.put("P"), + [""] = mapping.delete(), + [""] = mapping.set_register(utils.get_default_register()), + }, + n = { + p = mapping.put("p"), + P = mapping.put("P"), + d = mapping.delete(), + r = mapping.set_register(utils.get_default_register()) + }, + } + } + } + }) +< + + +PICKER.TELESCOPE.MAPPINGS + +Default : `nil` + +This define or overrides the mappings available in Telescope. + +If you set `use_default_mappings` to `true`, mappings will be merged with +default mappings. + +**Available actions:** + +>lua + require("yanky.telescope.mapping").put("p") -- put after cursor + require("yanky.telescope.mapping").put("P") -- put before cursor + require("yanky.telescope.mapping").put("gp") -- put after cursor and leave the cursor after + require("yanky.telescope.mapping").put("gP") -- put before cursor and leave the cursor after + require("yanky.telescope.mapping").delete() -- delete entry from yank history + require("yanky.telescope.mapping").set_register(regname) -- fill register {regname} with selected value +< + +You can also use any of available special puts + like this: + +>lua + require("yanky.telescope.mapping").special_put("{{ name of the special put }}") + + -- eg. + require("yanky.telescope.mapping").special_put("YankyPutAfterCharwiseJoined") +< + + +💡 HIGHLIGHT PUT AND YANKED TEXT*yanky-🍃-yanky.nvim-💡-highlight-put-and-yanked-text* + +This will give you a visual feedback on put and yank text by highlighting this. + +### Configuration + +>lua + require("yanky").setup({ + highlight = { + on_put = true, + on_yank = true, + timer = 500, + }, + }) +< + +You can override `YankyPut` highlight to change colors. + + +HIGHLIGHT.ON_PUT + +Default : `true` + +Define if highlight put text feature is enabled. + + +HIGHLIGHT.ON_YANK + +Default : `true` + +Define if highlight yanked text feature is enabled. + + +HIGHLIGHT.TIMER + +Default : `500` + +Define the duration of highlight. + + +⤵️ PRESERVE CURSOR POSITION ON YANK*yanky-🍃-yanky.nvim-⤵️-preserve-cursor-position-on-yank* + +By default in Neovim, when yanking text, cursor moves to the start of the +yanked text. Could be annoying especially when yanking a large text object such +as a paragraph or a large text object. + +With this feature, yank will function exactly the same as previously with the +one difference being that the cursor position will not change after performing +a yank. + + +⌨️ MAPPINGS ~ + +>lua + vim.keymap.set({"n","x"}, "y", "(YankyYank)") +< + + +⚙️ CONFIGURATION ~ + +>lua + require("yanky").setup({ + preserve_cursor_position = { + enabled = true, + }, + }) +< + + +PRESERVE_CURSOR_POSITION.ENABLED + +Default : `true` + +Define if cursor position should be preserved on yank. This works only if +mappings has been defined. + + +⭐ SPECIAL PUT *yanky-🍃-yanky.nvim-⭐-special-put* + +Yanky comes with special put moves (inspired by tpope/vim-unimpaired +): + +- Linewise put: this will force put above or below the current line ; +- Shift right/left put: will put above or below the current line and increasing + or decreasing indent ; +- Filter put: will put above or below the current line and reindenting. + + +⌨️ MAPPINGS ~ + +For basic usage (like with tpope/vim-unimpaired +), +you can use those bindings: + +>lua + vim.keymap.set("n", "]p", "(YankyPutIndentAfterLinewise)") + vim.keymap.set("n", "[p", "(YankyPutIndentBeforeLinewise)") + vim.keymap.set("n", "]P", "(YankyPutIndentAfterLinewise)") + vim.keymap.set("n", "[P", "(YankyPutIndentBeforeLinewise)") + + vim.keymap.set("n", ">p", "(YankyPutIndentAfterShiftRight)") + vim.keymap.set("n", "(YankyPutIndentAfterShiftLeft)") + vim.keymap.set("n", ">P", "(YankyPutIndentBeforeShiftRight)") + vim.keymap.set("n", "(YankyPutIndentBeforeShiftLeft)") + + vim.keymap.set("n", "=p", "(YankyPutAfterFilter)") + vim.keymap.set("n", "=P", "(YankyPutBeforeFilter)") +< + +To go further, Plug mappings are constructed like this: +`Yanky(put-type)(modifier)(rewriter)`. + +`put-type` can be: + +- `PutAfter`: put after your cursor (as |`p`| key) ; +- `PutBefore`: put before your cursor (as |`P`| key) ; +- `GPutAfter`: like `PutAfter` but leave the cursor after the new text (as |`gp`| key) ; +- `GPutBefore`: like `PutBefore` but leave the cursor after the new text (as |`gP`| key) ; +- `PutIndentAfter`: like `PutAfter` but adjust the indent to the current line (as |`]p`| key) ; +- `PutIndentBefore`: like `PutBefore` but adjust the indent to the current line (as |`[p`| key) ; + +`modifier` (optional) can be: + +- `Linewise`: put in linewise mode ; +- `Charwise`: put in charwise mode ; +- `Blockwise`: put in blockwise mode ; +- `ShiftRight`: increase indent ; +- `ShiftLeft`: decrease indent. + +`rewriter` (optional) can be: + +- `Joined`: put lines trimed and joined. + +All special puts ~ + +>vim + (YankyPutAfter) + (YankyPutAfterBlockwise) + (YankyPutAfterBlockwiseJoined) + (YankyPutAfterCharwise) + (YankyPutAfterCharwiseJoined) + (YankyPutAfterFilter) + (YankyPutAfterFilterJoined) + (YankyPutAfterJoined) + (YankyPutAfterLinewise) + (YankyPutAfterLinewiseJoined) + (YankyPutAfterShiftLeft) + (YankyPutAfterShiftLeftJoined) + (YankyPutAfterShiftRight) + (YankyPutAfterShiftRightJoined) + (YankyPutBefore) + (YankyPutBeforeBlockwise) + (YankyPutBeforeBlockwiseJoined) + (YankyPutBeforeCharwise) + (YankyPutBeforeCharwiseJoined) + (YankyPutBeforeFilter) + (YankyPutBeforeFilterJoined) + (YankyPutBeforeJoined) + (YankyPutBeforeLinewise) + (YankyPutBeforeLinewiseJoined) + (YankyPutBeforeShiftLeft) + (YankyPutBeforeShiftLeftJoined) + (YankyPutBeforeShiftRight) + (YankyPutBeforeShiftRightJoined) + (YankyGPutAfter) + (YankyGPutAfterBlockwise) + (YankyGPutAfterBlockwiseJoined) + (YankyGPutAfterCharwise) + (YankyGPutAfterCharwiseJoined) + (YankyGPutAfterFilter) + (YankyGPutAfterFilterJoined) + (YankyGPutAfterJoined) + (YankyGPutAfterLinewise) + (YankyGPutAfterLinewiseJoined) + (YankyGPutAfterShiftLeft) + (YankyGPutAfterShiftLeftJoined) + (YankyGPutAfterShiftRight) + (YankyGPutAfterShiftRightJoined) + (YankyGPutBefore) + (YankyGPutBeforeBlockwise) + (YankyGPutBeforeBlockwiseJoined) + (YankyGPutBeforeCharwise) + (YankyGPutBeforeCharwiseJoined) + (YankyGPutBeforeFilter) + (YankyGPutBeforeFilterJoined) + (YankyGPutBeforeJoined) + (YankyGPutBeforeLinewise) + (YankyGPutBeforeLinewiseJoined) + (YankyGPutBeforeShiftLeft) + (YankyGPutBeforeShiftLeftJoined) + (YankyGPutBeforeShiftRight) + (YankyGPutBeforeShiftRightJoined) + (YankyPutIndentAfter) + (YankyPutIndentAfterBlockwise) + (YankyPutIndentAfterBlockwiseJoined) + (YankyPutIndentAfterCharwise) + (YankyPutIndentAfterCharwiseJoined) + (YankyPutIndentAfterFilter) + (YankyPutIndentAfterFilterJoined) + (YankyPutIndentAfterJoined) + (YankyPutIndentAfterLinewise) + (YankyPutIndentAfterLinewiseJoined) + (YankyPutIndentAfterShiftLeft) + (YankyPutIndentAfterShiftLeftJoined) + (YankyPutIndentAfterShiftRight) + (YankyPutIndentAfterShiftRightJoined) + (YankyPutIndentBefore) + (YankyPutIndentBeforeBlockwise) + (YankyPutIndentBeforeBlockwiseJoined) + (YankyPutIndentBeforeCharwise) + (YankyPutIndentBeforeCharwiseJoined) + (YankyPutIndentBeforeFilter) + (YankyPutIndentBeforeFilterJoined) + (YankyPutIndentBeforeJoined) + (YankyPutIndentBeforeLinewise) + (YankyPutIndentBeforeLinewiseJoined) + (YankyPutIndentBeforeShiftLeft) + (YankyPutIndentBeforeShiftLeftJoined) + (YankyPutIndentBeforeShiftRight) + (YankyPutIndentBeforeShiftRightJoined) +< + + +⚓ TEXT OBJECT *yanky-🍃-yanky.nvim-⚓-text-object* + +Yanky comes with a text object corresponding to last put text. To use it, you +have to enable it in settings and set a keymap. + + +⚙️ CONFIGURATION ~ + +>lua + require("yanky").setup({ + textobj = { + enabled = true, + }, + }) +< + + +⌨️ MAPPINGS ~ + +>lua + vim.keymap.set({ "o", "x" }, "lp", function() + require("yanky.textobj").last_put() + end, {}) +< + + +🎨 COLORS *yanky-🍃-yanky.nvim-🎨-colors* + + Description Group Default + --------------------------------- ------------- ---------------- + Highlight color for put text YankyPut link to Search + Highlight color for yanked text YankyYanked link to Search + +🤝 INTEGRATIONS *yanky-🍃-yanky.nvim-🤝-integrations* + +gbprod/substitute.nvim ~ + +To enable gbprod/substitute.nvim +swap when performing a substitution, you can add this to your setup: + +>lua + require("substitute").setup({ + on_substitute = require("yanky.integration").substitute(), + }) +< + +hrsh7th/nvim-cmp ~ + +Using hrsh7th/nvim-cmp and +chrisgrieser/cmp_yanky , you can +also get suggestions from your yank history as you type in insert mode. + +anuvyklack/hydra.nvim ~ + +To work with anuvyklack/hydra.nvim +only setup / mapping when yanky is activated, you can add this to your setup: + +>lua + local Hydra = require("hydra") + + local function t(str) + return api.nvim_replace_termcodes(str, true, true, true) + end + + local yanky_hydra = Hydra({ + name = "Yank ring", + mode = "n", + heads = { + { "p", "(YankyPutAfter)", { desc = "After" } }, + { "P", "(YankyPutBefore)", { desc = "Before" } }, + { "", "(YankyPreviousEntry)", { private = true, desc = "↑" } }, + { "", "(YankyNextEntry)", { private = true, desc = "↓" } }, + }, + }) + + -- choose/change the mappings if you want + for key, putAction in pairs({ + ["p"] = "(YankyPutAfter)", + ["P"] = "(YankyPutBefore)", + ["gp"] = "(YankyGPutAfter)", + ["gP"] = "(YankyGPutBefore)", + }) do + vim.keymap.set({ "n", "x" }, key, function() + vim.fn.feedkeys(t(putAction)) + yanky_hydra:activate() + end) + end + + -- choose/change the mappings if you want + for key, putAction in pairs({ + ["]p"] = "(YankyPutIndentAfterLinewise)", + ["[p"] = "(YankyPutIndentBeforeLinewise)", + ["]P"] = "(YankyPutIndentAfterLinewise)", + ["[P"] = "(YankyPutIndentBeforeLinewise)", + + [">p"] = "(YankyPutIndentAfterShiftRight)", + ["(YankyPutIndentAfterShiftLeft)", + [">P"] = "(YankyPutIndentBeforeShiftRight)", + ["(YankyPutIndentBeforeShiftLeft)", + + ["=p"] = "(YankyPutAfterFilter)", + ["=P"] = "(YankyPutBeforeFilter)", + }) do + vim.keymap.set("n", key, function() + vim.fn.feedkeys(t(putAction)) + yanky_hydra:activate() + end) + end +< + + +🎉 CREDITS *yanky-🍃-yanky.nvim-🎉-credits* + +This plugin is mostly a lua version of svermeulen/vim-yoink + awesome plugin. + +Other inspiration : + +- bfredl/nvim-miniyank +- maxbrunsfeld/vim-yankstack +- svermeulen/vim-easyclip +- bkoropoff/yankee.vim +- svban/YankAssassin.vim +- tpope/vim-unimpaired +- inkarkat/vim-UnconditionalPaste + +============================================================================== +2. Links *yanky-links* + +1. *Lua*: https://img.shields.io/badge/Made%20with%20Lua-blueviolet.svg?style=for-the-badge&logo=lua +2. *GitHub Workflow Status*: https://img.shields.io/github/actions/workflow/status/gbprod/yanky.nvim/integration.yml?branch=main&style=for-the-badge + +Generated by panvimdoc + +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/telescope/_extensions/yank_history.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/telescope/_extensions/yank_history.lua new file mode 100644 index 00000000..0c12230f --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/telescope/_extensions/yank_history.lua @@ -0,0 +1,6 @@ +local telescope = require("telescope") +local get_exports = require("yanky.telescope.yank_history").get_exports + +return telescope.register_extension({ + exports = get_exports(), +}) diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky.lua new file mode 100644 index 00000000..1315ec41 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky.lua @@ -0,0 +1,404 @@ +local utils = require("yanky.utils") +local highlight = require("yanky.highlight") +local system_clipboard = require("yanky.system_clipboard") +local preserve_cursor = require("yanky.preserve_cursor") +local picker = require("yanky.picker") +local textobj = require("yanky.textobj") + +local yanky = {} + +yanky.ring = { + state = nil, + is_cycling = false, + callback = nil, +} + +yanky.direction = { + FORWARD = 1, + BACKWARD = -1, +} + +yanky.type = { + PUT_BEFORE = "P", + PUT_AFTER = "p", + GPUT_BEFORE = "gP", + GPUT_AFTER = "gp", + PUT_INDENT_AFTER = "]p", + PUT_INDENT_BEFORE = "[p", +} + +function yanky.setup(options) + yanky.config = require("yanky.config") + yanky.config.setup(options) + + yanky.history = require("yanky.history") + yanky.history.setup() + + system_clipboard.setup() + highlight.setup() + preserve_cursor.setup() + picker.setup() + + local yanky_augroup = vim.api.nvim_create_augroup("Yanky", { clear = true }) + vim.api.nvim_create_autocmd("TextYankPost", { + group = yanky_augroup, + pattern = "*", + callback = function(_) + yanky.on_yank() + end, + }) + if vim.v.vim_did_enter == 1 then + yanky.init_history() + else + vim.api.nvim_create_autocmd("VimEnter", { + group = yanky_augroup, + pattern = "*", + callback = yanky.init_history, + }) + end + + vim.api.nvim_create_user_command("YankyClearHistory", yanky.clear_history, {}) +end + +function yanky.init_history() + yanky.history.push(utils.get_register_info(utils.get_default_register())) + yanky.history.sync_with_numbered_registers() +end + +local function do_put(state, _) + if state.is_visual then + vim.cmd([[execute "normal! \"]]) + end + + local ok, val = pcall( + vim.cmd, + string.format( + 'silent normal! %s"%s%s%s', + state.is_visual and "gv" or "", + state.register ~= "=" and state.register or "=" .. vim.api.nvim_replace_termcodes("", true, false, true), + state.count, + state.type + ) + ) + if not ok then + vim.notify(val, vim.log.levels.WARN) + return + end + + highlight.highlight_put(state) +end + +function yanky.put(type, is_visual, callback) + if not vim.tbl_contains(vim.tbl_values(yanky.type), type) then + vim.notify("Invalid type " .. type, vim.log.levels.ERROR) + return + end + + yanky.ring.state = nil + yanky.ring.is_cycling = false + yanky.ring.callback = callback or do_put + + -- On Yank event is not triggered when put from expression register, + -- To allows cycling, we must store value here + if vim.v.register == "=" then + local entry = utils.get_register_info("=") + entry.filetype = vim.bo.filetype + + yanky.history.push(entry) + end + + yanky.init_ring(type, vim.v.register, vim.v.count, is_visual, yanky.ring.callback) +end + +function yanky.clear_ring() + if yanky.can_cycle() and nil ~= yanky.ring.state.augroup then + vim.api.nvim_clear_autocmds({ group = yanky.ring.state.augroup }) + end + + yanky.ring.state = nil + yanky.ring.is_cycling = false +end + +function yanky.attach_cancel() + if yanky.config.options.ring.cancel_event == "move" then + yanky.ring.state.augroup = vim.api.nvim_create_augroup("YankyRingClear", { clear = true }) + vim.schedule(function() + vim.api.nvim_create_autocmd("CursorMoved", { + group = yanky.ring.state.augroup, + buffer = 0, + callback = yanky.clear_ring, + }) + end) + else + vim.api.nvim_buf_attach(0, false, { + on_lines = function(_) + yanky.clear_ring() + + return true + end, + }) + end +end + +function yanky.init_ring(type, register, count, is_visual, callback) + register = (register ~= '"' and register ~= "_") and register or utils.get_default_register() + + local reg_content = vim.fn.getreg(register) + if nil == reg_content or "" == reg_content then + vim.notify(string.format('Register "%s" is empty', register), vim.log.levels.WARN) + return + end + + local new_state = { + type = type, + register = register, + count = count > 0 and count or 1, + is_visual = is_visual, + use_repeat = callback == nil, + } + + if nil ~= callback then + callback(new_state, do_put) + end + + yanky.ring.state = new_state + yanky.ring.is_cycling = false + + yanky.attach_cancel() + if yanky.config.options.textobj.enabled then + textobj.save_put() + end +end + +function yanky.can_cycle() + return nil ~= yanky.ring.state +end + +function yanky.cycle(direction) + if not yanky.can_cycle() then + vim.notify("Your last action was not put, ignoring cycle", vim.log.levels.INFO) + return + end + + direction = direction or yanky.direction.FORWARD + if not vim.tbl_contains(vim.tbl_values(yanky.direction), direction) then + vim.notify("Invalid direction for cycle", vim.log.levels.ERROR) + return + end + + if nil ~= yanky.ring.state.augroup then + vim.api.nvim_clear_autocmds({ group = yanky.ring.state.augroup }) + end + + if not yanky.ring.is_cycling then + yanky.history.reset() + + local reg = utils.get_register_info(yanky.ring.state.register) + local first = yanky.history.first() + if nil ~= first and reg.regcontents == first.regcontents and reg.regtype == first.regtype then + yanky.history.skip() + end + end + + local new_state = yanky.ring.state + + local next_content + if direction == yanky.direction.FORWARD then + next_content = yanky.history.next() + if nil == next_content then + vim.notify("Reached oldest item", vim.log.levels.INFO) + yanky.attach_cancel() + return + end + else + next_content = yanky.history.previous() + if nil == next_content then + vim.notify("Reached first item", vim.log.levels.INFO) + yanky.attach_cancel() + return + end + end + + yanky.ring.state.register = yanky.ring.state.register ~= "=" and yanky.ring.state.register + or utils.get_default_register() + + utils.use_temporary_register(yanky.ring.state.register, next_content, function() + if new_state.use_repeat then + local ok, val = pcall(vim.cmd, "silent normal! u.") + if not ok then + vim.notify(val, vim.log.levels.WARN) + yanky.attach_cancel() + return + end + highlight.highlight_put(new_state) + else + local ok, val = pcall(vim.cmd, "silent normal! u") + if not ok then + vim.notify(val, vim.log.levels.WARN) + yanky.attach_cancel() + return + end + yanky.ring.callback(new_state, do_put) + end + end) + + if yanky.config.options.ring.update_register_on_cycle then + vim.fn.setreg(new_state.register, next_content.regcontents, next_content.regtype) + end + + yanky.ring.is_cycling = true + yanky.ring.state = new_state + + yanky.attach_cancel() +end + +function yanky.on_yank() + if vim.tbl_contains(yanky.config.options.ring.ignore_registers, vim.v.register) then + return + end + + -- Only historize first delete in visual mode + if vim.v.event.visual and vim.v.event.operator == "d" and yanky.ring.is_cycling then + return + end + local entry = utils.get_register_info(vim.v.event.regname) + entry.filetype = vim.bo.filetype + + yanky.history.push(entry) + + preserve_cursor.on_yank() +end + +function yanky.yank(options) + options = options or {} + preserve_cursor.yank() + + return string.format("%sy", options.register and '"' .. options.register or "") +end + +function yanky.clear_history() + yanky.history.clear() +end + +function yanky.register_plugs() + local yanky_wrappers = require("yanky.wrappers") + + vim.keymap.set("n", "(YankyCycleForward)", function() + yanky.cycle(1) + end, { silent = true }) + + vim.keymap.set("n", "(YankyCycleBackward)", function() + yanky.cycle(-1) + end, { silent = true }) + + vim.keymap.set("n", "(YankyPreviousEntry)", function() + yanky.cycle(1) + end, { silent = true }) + + vim.keymap.set("n", "(YankyNextEntry)", function() + yanky.cycle(-1) + end, { silent = true }) + + vim.keymap.set({ "n", "x" }, "(YankyYank)", yanky.yank, { silent = true, expr = true }) + + for type, type_text in pairs({ + p = "PutAfter", + P = "PutBefore", + gp = "GPutAfter", + gP = "GPutBefore", + ["]p"] = "PutIndentAfter", + ["[p"] = "PutIndentBefore", + }) do + vim.keymap.set("n", string.format("(Yanky%s)", type_text), function() + yanky.put(type, false) + end, { silent = true }) + + vim.keymap.set("x", string.format("(Yanky%s)", type_text), function() + yanky.put(type, true) + end, { silent = true }) + + vim.keymap.set("n", string.format("(Yanky%sJoined)", type_text), function() + yanky.put(type, false, yanky_wrappers.trim_and_join_lines()) + end, { silent = true }) + + vim.keymap.set("x", string.format("(Yanky%sJoined)", type_text), function() + yanky.put(type, true, yanky_wrappers.trim_and_join_lines()) + end, { silent = true }) + + vim.keymap.set("n", string.format("(Yanky%sLinewise)", type_text), function() + yanky.put(type, false, yanky_wrappers.linewise()) + end, { silent = true }) + + vim.keymap.set("x", string.format("(Yanky%sLinewise)", type_text), function() + yanky.put(type, true, yanky_wrappers.linewise()) + end, { silent = true }) + + vim.keymap.set("n", string.format("(Yanky%sLinewiseJoined)", type_text), function() + yanky.put(type, false, yanky_wrappers.linewise(yanky_wrappers.trim_and_join_lines())) + end, { silent = true }) + + vim.keymap.set("x", string.format("(Yanky%sLinewiseJoined)", type_text), function() + yanky.put(type, true, yanky_wrappers.linewise(yanky_wrappers.trim_and_join_lines())) + end, { silent = true }) + + vim.keymap.set("n", string.format("(Yanky%sCharwise)", type_text), function() + yanky.put(type, false, yanky_wrappers.charwise()) + end, { silent = true }) + + vim.keymap.set("x", string.format("(Yanky%sCharwise)", type_text), function() + yanky.put(type, true, yanky_wrappers.charwise()) + end, { silent = true }) + + vim.keymap.set("n", string.format("(Yanky%sCharwiseJoined)", type_text), function() + yanky.put(type, false, yanky_wrappers.charwise(yanky_wrappers.trim_and_join_lines())) + end, { silent = true }) + + vim.keymap.set("x", string.format("(Yanky%sCharwiseJoined)", type_text), function() + yanky.put(type, true, yanky_wrappers.charwise(yanky_wrappers.trim_and_join_lines())) + end, { silent = true }) + + vim.keymap.set("n", string.format("(Yanky%sBlockwise)", type_text), function() + yanky.put(type, false, yanky_wrappers.blockwise()) + end, { silent = true }) + + vim.keymap.set("x", string.format("(Yanky%sBlockwise)", type_text), function() + yanky.put(type, true, yanky_wrappers.blockwise()) + end, { silent = true }) + + vim.keymap.set("n", string.format("(Yanky%sBlockwiseJoined)", type_text), function() + yanky.put(type, false, yanky_wrappers.blockwise(yanky_wrappers.trim_and_join_lines())) + end, { silent = true }) + + vim.keymap.set("x", string.format("(Yanky%sBlockwiseJoined)", type_text), function() + yanky.put(type, true, yanky_wrappers.blockwise(yanky_wrappers.trim_and_join_lines())) + end, { silent = true }) + + for change, change_text in pairs({ [">>"] = "ShiftRight", ["<<"] = "ShiftLeft", ["=="] = "Filter" }) do + vim.keymap.set("n", string.format("(Yanky%s%s)", type_text, change_text), function() + yanky.put(type, false, yanky_wrappers.linewise(yanky_wrappers.change(change))) + end, { silent = true }) + + vim.keymap.set("x", string.format("(Yanky%s%s)", type_text, change_text), function() + yanky.put(type, true, yanky_wrappers.linewise(yanky_wrappers.change(change))) + end, { silent = true }) + + vim.keymap.set("n", string.format("(Yanky%s%sJoined)", type_text, change_text), function() + yanky.put( + type, + false, + yanky_wrappers.linewise(yanky_wrappers.trim_and_join_lines(yanky_wrappers.change(change))) + ) + end, { silent = true }) + + vim.keymap.set("x", string.format("(Yanky%s%sJoined)", type_text, change_text), function() + yanky.put( + type, + true, + yanky_wrappers.linewise(yanky_wrappers.trim_and_join_lines(yanky_wrappers.change(change))) + ) + end, { silent = true }) + end + end +end + +return yanky diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/config.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/config.lua new file mode 100644 index 00000000..cd6f7d89 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/config.lua @@ -0,0 +1,45 @@ +local config = {} + +config.options = {} + +local default_values = { + ring = { + history_length = 100, + storage = "shada", + storage_path = vim.fn.stdpath("data") .. "/databases/yanky.db", + sync_with_numbered_registers = true, + ignore_registers = { "_" }, + cancel_event = "update", + update_register_on_cycle = false, + }, + system_clipboard = { + sync_with_ring = true, + clipboard_register = nil, + }, + highlight = { + on_put = true, + on_yank = true, + timer = 500, + }, + preserve_cursor_position = { + enabled = true, + }, + picker = { + select = { + action = nil, + }, + telescope = { + use_default_mappings = true, + mappings = nil, + }, + }, + textobj = { + enabled = false, + }, +} + +function config.setup(options) + config.options = vim.tbl_deep_extend("force", default_values, options or {}) +end + +return config diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/highlight.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/highlight.lua new file mode 100644 index 00000000..3cce001a --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/highlight.lua @@ -0,0 +1,64 @@ +local highlight = {} + +function highlight.setup() + highlight.config = require("yanky.config").options.highlight + if highlight.config.on_put then + highlight.hl_put = vim.api.nvim_create_namespace("yanky.put") + highlight.timer = vim.loop.new_timer() + + vim.api.nvim_set_hl(0, "YankyPut", { link = "Search", default = true }) + end + + if highlight.config.on_yank then + vim.api.nvim_create_autocmd("TextYankPost", { + pattern = "*", + callback = function(_) + pcall(vim.highlight.on_yank, { higroup = "YankyYanked", timeout = highlight.config.timer }) + end, + }) + + vim.api.nvim_set_hl(0, "YankyYanked", { link = "Search", default = true }) + end +end + +local function get_region() + local start = vim.api.nvim_buf_get_mark(0, "[") + local finish = vim.api.nvim_buf_get_mark(0, "]") + + return { + start_row = start[1] - 1, + start_col = start[2], + end_row = finish[1] - 1, + end_col = finish[2], + } +end + +function highlight.highlight_put(state) + if not highlight.config.on_put then + return + end + + highlight.timer:stop() + vim.api.nvim_buf_clear_namespace(0, highlight.hl_put, 0, -1) + + local region = get_region() + + vim.highlight.range( + 0, + highlight.hl_put, + "YankyPut", + { region.start_row, region.start_col }, + { region.end_row, region.end_col }, + { regtype = vim.fn.getregtype(state.register), inclusive = true } + ) + + highlight.timer:start( + highlight.config.timer, + 0, + vim.schedule_wrap(function() + vim.api.nvim_buf_clear_namespace(0, highlight.hl_put, 0, -1) + end) + ) +end + +return highlight diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/history.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/history.lua new file mode 100644 index 00000000..1cdda5fc --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/history.lua @@ -0,0 +1,85 @@ +local history = { + storage = nil, + position = 1, + config = nil, +} + +function history.setup() + history.config = require("yanky.config").options.ring + history.storage = require("yanky.storage." .. history.config.storage) + if false == history.storage.setup() then + history.storage = require("yanky.storage.memory") + end +end + +function history.push(item) + local prev = history.storage.get(1) + if prev ~= nil and prev.regcontents == item.regcontents and prev.regtype == item.regtype then + return + end + + history.storage.push(item) + + history.sync_with_numbered_registers() +end + +function history.sync_with_numbered_registers() + if history.config.sync_with_numbered_registers then + for i = 1, math.min(history.storage.length(), 9) do + local reg = history.storage.get(i) + vim.fn.setreg(i, reg.regcontents, reg.regtype) + end + end +end + +function history.first() + if history.storage.length() <= 0 then + return nil + end + + return history.storage.get(1) +end + +function history.skip() + history.position = history.position + 1 +end + +function history.next() + local new_position = history.position + 1 + if new_position > history.storage.length() then + return nil + end + + history.position = new_position + + return history.storage.get(history.position) +end + +function history.previous() + if history.position == 1 then + return nil + end + + history.position = history.position - 1 + + return history.storage.get(history.position) +end + +function history.reset() + history.position = 0 +end + +function history.all() + return history.storage.all() +end + +function history.clear() + history.storage.clear() + history.position = 1 +end + +function history.delete(index) + history.storage.delete(index) +end + +return history diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/integration.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/integration.lua new file mode 100644 index 00000000..870e320f --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/integration.lua @@ -0,0 +1,13 @@ +local yanky = require("yanky") + +local integration = {} + +function integration.substitute() + return function(event) + local is_visual = require("substitute.utils").is_visual(event.vmode) + + yanky.init_ring("p", event.register, event.count, is_visual) + end +end + +return integration diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/picker.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/picker.lua new file mode 100644 index 00000000..2a86726e --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/picker.lua @@ -0,0 +1,86 @@ +local utils = require("yanky.utils") + +local picker = { + actions = {}, +} + +function picker.setup() + vim.api.nvim_create_user_command("YankyRingHistory", picker.select_in_history, {}) +end + +function picker.select_in_history() + local history = {} + for index, value in pairs(require("yanky.history").all()) do + value.history_index = index + history[index] = value + end + + local config = require("yanky.config").options.picker.select + local action = config.action or require("yanky.picker").actions.put("p", false) + + vim.ui.select(history, { + prompt = "Ring history", + format_item = function(item) + return item.regcontents and item.regcontents:gsub("\n", "\\n") or "" + end, + }, action) +end + +function picker.actions.put(type, is_visual) + local yanky = require("yanky") + + if not vim.tbl_contains(vim.tbl_values(yanky.type), type) then + vim.notify("Invalid type " .. type, vim.log.levels.ERROR) + return + end + + return function(next_content) + if nil == next_content then + return + end + + utils.use_temporary_register(utils.get_default_register(), next_content, function() + yanky.put(type, is_visual) + end) + end +end + +function picker.actions.delete() + return function(content) + if nil == content then + return + end + + local yanky = require("yanky") + yanky.history.delete(content.history_index) + end +end + +function picker.actions.set_register(register) + return function(content) + if nil == content then + return + end + + vim.fn.setreg(register, content.regcontents, content.regtype) + end +end + +function picker.actions.special_put(name, is_visual) + if "" == vim.fn.maparg(string.format("(%s)", name), "n") then + vim.notify("Invalid special put " .. type, vim.log.levels.ERROR) + return + end + + return function(next_content) + if nil == next_content then + return + end + + utils.use_temporary_register(utils.get_default_register(), next_content, function() + vim.fn.maparg(string.format("(%s)", name), is_visual and "x" or "n", false, true).callback() + end) + end +end + +return picker diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/preserve_cursor.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/preserve_cursor.lua new file mode 100644 index 00000000..2578e6a4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/preserve_cursor.lua @@ -0,0 +1,50 @@ +local preserve_cursor = {} + +preserve_cursor.state = { + cusor_position = nil, + win_state = nil, +} + +function preserve_cursor.setup() + preserve_cursor.config = require("yanky.config").options.preserve_cursor_position +end + +function preserve_cursor.on_yank() + if not preserve_cursor.config.enabled then + return + end + + if nil ~= preserve_cursor.state.cusor_position then + vim.fn.setpos(".", preserve_cursor.state.cusor_position) + vim.fn.winrestview(preserve_cursor.state.win_state) + + preserve_cursor.state = { + cusor_position = nil, + win_state = nil, + } + end +end + +function preserve_cursor.yank() + if not preserve_cursor.config.enabled then + return + end + + preserve_cursor.state = { + cusor_position = vim.fn.getpos("."), + win_state = vim.fn.winsaveview(), + } + + vim.api.nvim_buf_attach(0, false, { + on_lines = function() + preserve_cursor.state = { + cusor_position = nil, + win_state = nil, + } + + return true + end, + }) +end + +return preserve_cursor diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/storage/memory.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/storage/memory.lua new file mode 100644 index 00000000..6ef2cd62 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/storage/memory.lua @@ -0,0 +1,37 @@ +local memory = { + state = {}, +} + +function memory.setup() + memory.config = require("yanky.config").options.ring +end + +function memory.push(item) + table.insert(memory.state, 1, item) + + if #memory.state > memory.config.history_length then + table.remove(memory.state) + end +end + +function memory.get(n) + return memory.state[n] +end + +function memory.length() + return #memory.state +end + +function memory.all() + return memory.state +end + +function memory.clear() + memory.state = {} +end + +function memory.delete(index) + table.remove(memory.state, index) +end + +return memory diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/storage/shada.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/storage/shada.lua new file mode 100644 index 00000000..878fb364 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/storage/shada.lua @@ -0,0 +1,53 @@ +local shada = {} + +function shada.setup() + shada.config = require("yanky.config").options.ring +end + +function shada.push(item) + local copy = vim.deepcopy(vim.g.YANKY_HISTORY) + table.insert(copy, 1, item) + + if #copy > shada.config.history_length then + table.remove(copy) + end + + vim.g.YANKY_HISTORY = copy +end + +function shada.get(n) + if nil == vim.g.YANKY_HISTORY then + vim.g.YANKY_HISTORY = {} + end + + return vim.g.YANKY_HISTORY[n] +end + +function shada.length() + if nil == vim.g.YANKY_HISTORY then + vim.g.YANKY_HISTORY = {} + end + + return #vim.g.YANKY_HISTORY +end + +function shada.all() + if nil == vim.g.YANKY_HISTORY then + vim.g.YANKY_HISTORY = {} + end + + return vim.g.YANKY_HISTORY +end + +function shada.clear() + vim.g.YANKY_HISTORY = {} +end + +function shada.delete(index) + local copy = vim.deepcopy(vim.g.YANKY_HISTORY) + table.remove(copy, index) + + vim.g.YANKY_HISTORY = copy +end + +return shada diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/storage/sqlite.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/storage/sqlite.lua new file mode 100644 index 00000000..5b8e7b30 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/storage/sqlite.lua @@ -0,0 +1,90 @@ +local sqlite = { + db = nil, +} + +function sqlite.setup() + sqlite.config = require("yanky.config").options.ring + + local has_sqlite, connection = pcall(require, "sqlite") + if not has_sqlite then + if type(connection) ~= "string" then + vim.notify("Couldn't find sqlite.lua.", vim.log.levels.ERROR, {}) + else + vim.notify("Found sqlite.lua: but got the following error: " .. connection) + end + + return false + end + + vim.fn.mkdir(string.match(sqlite.config.storage_path, "(.*[/\\])"), "p") + + sqlite.db = connection:open(sqlite.config.storage_path) + if not sqlite.db then + vim.notify("Error in opening DB", vim.log.levels.ERROR) + return + end + + if not sqlite.db:exists("history") then + sqlite.db:create("history", { + id = { "integer", "primary", "key", "autoincrement" }, + regcontents = "text", + regtype = "text", + filetype = "text", + }) + end + + sqlite.db:close() +end + +function sqlite.push(item) + sqlite.db:with_open(function() + sqlite.db:eval( + "INSERT INTO history (filetype, regcontents, regtype) VALUES (:filetype, :regcontents, :regtype)", + item + ) + + sqlite.db:eval( + string.format( + "DELETE FROM history WHERE id NOT IN (SELECT id FROM history ORDER BY id DESC LIMIT %s)", + sqlite.config.history_length + ) + ) + end) +end + +function sqlite.get(index) + return sqlite.db:with_open(function() + return sqlite.db:select("history", { order_by = { desc = "id" }, limit = { 1, index - 1 } })[1] + end) +end + +function sqlite.length() + return sqlite.db:with_open(function() + return sqlite.db:tbl("history"):count() + end) +end + +function sqlite.all() + return sqlite.db:with_open(function() + return sqlite.db:select("history", { order_by = { desc = "id" } }) + end) +end + +function sqlite.clear() + return sqlite.db:with_open(function() + return sqlite.db:delete("history") + end) +end + +function sqlite.delete(index) + return sqlite.db:with_open(function() + return sqlite.db:eval( + string.format( + "DELETE FROM history WHERE id IN (SELECT id FROM history ORDER BY id DESC LIMIT 1 OFFSET %s)", + index - 1 + ) + ) + end) +end + +return sqlite diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/system_clipboard.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/system_clipboard.lua new file mode 100644 index 00000000..d821796c --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/system_clipboard.lua @@ -0,0 +1,64 @@ +local utils = require("yanky.utils") + +local system_clipboard = { + state = { + reg_info_on_focus_lost = nil, + }, +} + +function system_clipboard.setup() + system_clipboard.config = require("yanky.config").options.system_clipboard + system_clipboard.config.clipboard_register = system_clipboard.config.clipboard_register or utils.get_system_register() + system_clipboard.history = require("yanky.history") + + if system_clipboard.config.sync_with_ring then + local yanky_clipboard_augroup = vim.api.nvim_create_augroup("YankySyncClipboard", { clear = true }) + local fetching = false + vim.api.nvim_create_autocmd("FocusGained", { + group = yanky_clipboard_augroup, + pattern = "*", + callback = function(_) + if fetching then + return + end + fetching = true + local ok, err = pcall(system_clipboard.on_focus_gained) + vim.schedule(function() + fetching = false + end) + if not ok then + error(err) + end + end, + }) + vim.api.nvim_create_autocmd("FocusLost", { + group = yanky_clipboard_augroup, + pattern = "*", + callback = function(_) + if fetching then + return + end + system_clipboard.on_focus_lost() + end, + }) + end +end + +function system_clipboard.on_focus_lost() + system_clipboard.state.reg_info_on_focus_lost = utils.get_register_info(system_clipboard.config.clipboard_register) +end + +function system_clipboard.on_focus_gained() + local new_reg_info = utils.get_register_info(system_clipboard.config.clipboard_register) + + if + system_clipboard.state.reg_info_on_focus_lost ~= nil + and not vim.deep_equal(system_clipboard.state.reg_info_on_focus_lost, new_reg_info) + then + system_clipboard.history.push(new_reg_info) + end + + system_clipboard.state.reg_info_on_focus_lost = nil +end + +return system_clipboard diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/telescope/mapping.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/telescope/mapping.lua new file mode 100644 index 00000000..a8696c8b --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/telescope/mapping.lua @@ -0,0 +1,103 @@ +local picker = require("yanky.picker") +local utils = require("yanky.utils") +local actions = require("telescope.actions") +local action_state = require("telescope.actions.state") + +local mapping = { + state = { is_visual = false }, +} + +function mapping.put(type) + return function(prompt_bufnr) + if vim.api.nvim_buf_is_valid(prompt_bufnr) then + actions.close(prompt_bufnr) + end + local selection = action_state.get_selected_entry() + + -- fix cursor position since + -- https://github.com/nvim-telescope/telescope.nvim/commit/3eb90430b61b78b707e8ffe0cfe49138daaddbcc + local cursor_pos = nil + if vim.api.nvim_get_mode().mode == "i" then + cursor_pos = vim.api.nvim_win_get_cursor(0) + end + + vim.schedule(function() + if nil ~= cursor_pos then + vim.api.nvim_win_set_cursor(0, { cursor_pos[1], math.max(cursor_pos[2] - 1, 0) }) + end + picker.actions.put(type, mapping.state.is_visual)(selection.value) + end) + end +end + +function mapping.special_put(name) + return function(prompt_bufnr) + if vim.api.nvim_buf_is_valid(prompt_bufnr) then + actions.close(prompt_bufnr) + end + local selection = action_state.get_selected_entry() + + -- fix cursor position since + -- https://github.com/nvim-telescope/telescope.nvim/commit/3eb90430b61b78b707e8ffe0cfe49138daaddbcc + local cursor_pos = nil + if vim.api.nvim_get_mode().mode == "i" then + cursor_pos = vim.api.nvim_win_get_cursor(0) + end + + vim.schedule(function() + if nil ~= cursor_pos then + vim.api.nvim_win_set_cursor(0, { cursor_pos[1], math.max(cursor_pos[2] - 1, 0) }) + end + picker.actions.special_put(name, mapping.state.is_visual)(selection.value) + end) + end +end + +function mapping.delete() + return function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:delete_selection(function(selection) + picker.actions.delete()(selection.value) + end) + end +end + +function mapping.set_register(register) + return function(prompt_bufnr) + if vim.api.nvim_buf_is_valid(prompt_bufnr) then + actions.close(prompt_bufnr) + end + local selection = action_state.get_selected_entry() + + vim.schedule(function() + picker.actions.set_register(register)(selection.value) + end) + end +end + +function mapping.put_and_set_register(type, register) + return function(prompt_bufnr) + mapping.put(type)(prompt_bufnr) + mapping.set_register(register)(prompt_bufnr) + end +end + +function mapping.get_defaults() + return { + default = mapping.put("p"), + i = { + [""] = mapping.put("p"), + [""] = mapping.put("P"), + [""] = mapping.delete(), + [""] = mapping.set_register(utils.get_default_register()), + }, + n = { + p = mapping.put("p"), + P = mapping.put("P"), + d = mapping.delete(), + r = mapping.set_register(utils.get_default_register()), + }, + } +end + +return mapping diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/telescope/yank_history.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/telescope/yank_history.lua new file mode 100644 index 00000000..4c8b5da4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/telescope/yank_history.lua @@ -0,0 +1,128 @@ +local pickers = require("telescope.pickers") +local finders = require("telescope.finders") +local previewers = require("telescope.previewers") +local actions = require("telescope.actions") +local entry_display = require("telescope.pickers.entry_display") +local conf = require("telescope.config").values +local mapping = require("yanky.telescope.mapping") +local config = require("yanky.config") + +local yank_history = {} + +function yank_history.get_exports() + return { + yank_history = yank_history.yank_history, + } +end + +local regtype_to_text = function(regtype) + if "v" == regtype then + return "charwise" + end + + if "V" == regtype then + return "linewise" + end + + return "blockwise" +end + +local format_title = function(entry) + return regtype_to_text(entry.value.regtype) +end + +function yank_history.previewer() + return previewers.new_buffer_previewer({ + dyn_title = function(_, entry) + return format_title(entry) + end, + define_preview = function(self, entry) + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, true, vim.split(entry.value.regcontents, "\n")) + if entry.value.filetype ~= nil then + vim.bo[self.state.bufnr].filetype = entry.value.filetype + end + end, + }) +end + +function yank_history.attach_mappings(_, map) + local mappings = config.options.picker.telescope.use_default_mappings + and vim.tbl_deep_extend("force", mapping.get_defaults(), config.options.picker.telescope.mappings or {}) + or (config.options.picker.telescope.mappings or {}) + + if mappings.default then + actions.select_default:replace(mappings.default) + end + + for _, mode in pairs({ "i", "n" }) do + if mappings[mode] then + for keys, action in pairs(mappings[mode]) do + map(mode, keys, action) + end + end + end + + return true +end + +function yank_history.gen_from_history(opts) + local displayer = entry_display.create({ + separator = " ", + items = { + { width = #tostring(opts.history_length) }, + { remaining = true }, + }, + }) + + local make_display = function(entry) + local content = entry.content + + return displayer({ + { entry.value.history_index, "TelescopeResultsNumber" }, + content:gsub("\n", "\\n"), + }) + end + + return function(entry) + return { + valid = true, + value = entry, + ordinal = entry.regcontents, + content = entry.regcontents, + display = make_display, + } + end +end + +function yank_history.yank_history(opts) + local is_visual = vim.fn.mode() == "v" or vim.fn.mode() == "V" + if is_visual then + vim.cmd([[execute "normal! \"]]) + end + + mapping.state.is_visual = is_visual + + opts = opts or {} + local history = {} + for index, value in pairs(require("yanky.history").all()) do + value.history_index = index + history[index] = value + end + + opts.history_length = #history + + pickers + .new(opts, { + prompt_title = "Yank history", + finder = finders.new_table({ + results = history, + entry_maker = yank_history.gen_from_history(opts), + }), + attach_mappings = yank_history.attach_mappings, + previewer = yank_history.previewer(), + sorter = conf.generic_sorter(opts), + }) + :find() +end + +return yank_history diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/textobj.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/textobj.lua new file mode 100644 index 00000000..e7953888 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/textobj.lua @@ -0,0 +1,63 @@ +local textobj = { + state = nil, +} + +local function get_region(regtype) + local start = vim.api.nvim_buf_get_mark(0, "[") + + local finish = vim.api.nvim_buf_get_mark(0, "]") + + return { + start_row = start[1], + start_col = "V" ~= regtype and start[2] or 0, + end_row = finish[1], + end_col = "V" ~= regtype and finish[2] or vim.fn.col("$"), + } +end + +local function is_visual_mode() + return nil ~= vim.fn.mode():find("v") +end + +local function set_selection(startpos, endpos) + vim.api.nvim_win_set_cursor(0, startpos) + if is_visual_mode() then + vim.cmd("normal! o") + else + vim.cmd("normal! v") + end + vim.api.nvim_win_set_cursor(0, endpos) +end + +function textobj.save_put() + vim.b.yanky_textobj = { + region = get_region(vim.fn.getregtype(vim.v.register)), + regtype = vim.fn.getregtype(vim.v.register), + } + vim.api.nvim_buf_attach(0, false, { + on_lines = function(_, _, _, first_line) + if vim.b.yanky_textobj and (first_line <= vim.b.yanky_textobj.region.end_row) then + vim.b.yanky_textobj = nil + return true + end + end, + }) +end + +function textobj.last_put() + if nil == vim.b.yanky_textobj then + vim.notify("No last put text-object", vim.log.levels.INFO) + + return + end + + set_selection({ + vim.b.yanky_textobj.region.start_row, + vim.b.yanky_textobj.region.start_col, + }, { + vim.b.yanky_textobj.region.end_row, + vim.b.yanky_textobj.region.end_col, + }) +end + +return textobj diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/utils.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/utils.lua new file mode 100644 index 00000000..a45f722d --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/utils.lua @@ -0,0 +1,45 @@ +local utils = {} + +function utils.get_default_register() + local clipboard_tool = vim.fn["provider#clipboard#Executable"]() + if not clipboard_tool or "" == clipboard_tool then + return '"' + end + + local clipboard_flags = vim.split(vim.api.nvim_get_option("clipboard"), ",") + + if vim.tbl_contains(clipboard_flags, "unnamedplus") then + return "+" + end + + if vim.tbl_contains(clipboard_flags, "unnamed") then + return "*" + end + + return '"' +end + +function utils.get_system_register() + local clipboardFlags = vim.split(vim.api.nvim_get_option("clipboard"), ",") + + if vim.tbl_contains(clipboardFlags, "unnamedplus") then + return "+" + end + return "*" +end + +function utils.get_register_info(register) + return { + regcontents = vim.fn.getreg(register), + regtype = vim.fn.getregtype(register), + } +end + +function utils.use_temporary_register(register, register_info, callback) + local current_register_info = utils.get_register_info(register) + vim.fn.setreg(register, register_info.regcontents, register_info.regtype) + callback() + vim.fn.setreg(register, current_register_info.regcontents, current_register_info.regtype) +end + +return utils diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/wrappers.lua b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/wrappers.lua new file mode 100644 index 00000000..7c8841ae --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/lua/yanky/wrappers.lua @@ -0,0 +1,101 @@ +local wrappers = {} + +function wrappers.linewise(next) + return function(state, callback) + local body = vim.fn.getreg(state.register) + local type = vim.fn.getregtype(state.register) + + vim.fn.setreg(state.register, body, "l") + + if nil == next then + callback(state) + else + next(state, callback) + end + + vim.fn.setreg(state.register, body, type) + end +end + +function wrappers.charwise(next) + return function(state, callback) + local body = vim.fn.getreg(state.register) + local type = vim.fn.getregtype(state.register) + + local reformated_body = body:gsub("\n$", "") + vim.fn.setreg(state.register, reformated_body, "c") + + if nil == next then + callback(state) + else + next(state, callback) + end + + vim.fn.setreg(state.register, body, type) + end +end + +function wrappers.blockwise(next) + return function(state, callback) + local body = vim.fn.getreg(state.register) + local type = vim.fn.getregtype(state.register) + + vim.fn.setreg(state.register, body, "b") + + if nil == next then + callback(state) + else + next(state, callback) + end + + vim.fn.setreg(state.register, body, type) + end +end + +function wrappers.trim_and_join_lines(next) + return function(state, callback) + local body = vim.fn.getreg(state.register) + + local reformated_body = body:gsub("%s*\r?\n%s*", " "):gsub("^%s*", ""):gsub("%s*$", "") + vim.fn.setreg(state.register, reformated_body, vim.fn.getregtype(state.register)) + + if nil == next then + callback(state) + else + next(state, callback) + end + + vim.fn.setreg(state.register, body, vim.fn.getregtype(state.register)) + end +end + +function wrappers.change(change, next) + return function(state, callback) + if nil == next then + callback(state) + else + next(state, callback) + end + + -- local line_len = vim.api.nvim_get_current_line():len() + + local cursor_pos = vim.api.nvim_win_get_cursor(0) + vim.cmd(string.format("silent '[,']normal! %s", change)) + vim.api.nvim_win_set_cursor(0, cursor_pos) + vim.cmd(string.format("silent normal! %s", (state.type == "gp" or state.type == "gP") and "0" or "^")) + end +end + +function wrappers.set_cursor_pos(pos, next) + return function(state, callback) + if nil == next then + callback(state) + else + next(state, callback) + end + + vim.cmd(string.format("silent normal! %s", pos)) + end +end + +return wrappers diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/plugin/init.lua b/config/neovim/store/lazy-plugins/yanky.nvim/plugin/init.lua new file mode 100644 index 00000000..b762dfb4 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/plugin/init.lua @@ -0,0 +1 @@ +require("yanky").register_plugs() diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/spec/init.lua b/config/neovim/store/lazy-plugins/yanky.nvim/spec/init.lua new file mode 100644 index 00000000..903d0079 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/spec/init.lua @@ -0,0 +1,54 @@ +local M = {} + +function M.root(root) + local f = debug.getinfo(1, "S").source:sub(2) + return vim.fn.fnamemodify(f, ":p:h:h") .. "/" .. (root or "") +end + +---@param plugin string +function M.load(plugin) + local name = plugin:match(".*/(.*)") + local package_root = M.root(".spec/site/pack/deps/start/") + if not vim.loop.fs_stat(package_root .. name) then + print("Installing " .. plugin) + vim.fn.mkdir(package_root, "p") + vim.fn.system({ + "git", + "clone", + "--depth=1", + "https://github.com/" .. plugin .. ".git", + package_root .. "/" .. name, + }) + end +end + +function M.setup() + vim.cmd([[set runtimepath=$VIMRUNTIME]]) + vim.opt.runtimepath:append(M.root()) + vim.opt.packpath = { M.root(".spec/site") } + + M.load("nvim-lua/plenary.nvim") + + vim.api.nvim_set_option("clipboard", "") + + require("yanky").setup({ ring = { storage = "memory" } }) + require("yanky").register_plugs() + + vim.keymap.set("n", "p", "(YankyPutAfter)", {}) + vim.keymap.set("n", "P", "(YankyPutBefore)", {}) + vim.keymap.set("x", "p", "(YankyPutAfter)", {}) + vim.keymap.set("x", "P", "(YankyPutBefore)", {}) + + vim.keymap.set("n", "gp", "(YankyGPutAfter)", {}) + vim.keymap.set("n", "gP", "(YankyGPutBefore)", {}) + vim.keymap.set("x", "gp", "(YankyGPutAfter)", {}) + vim.keymap.set("x", "gP", "(YankyGPutBefore)", {}) + + vim.keymap.set("n", ",p", "(YankyCycleForward)", {}) + vim.keymap.set("n", ",P", "(YankyCycleBackward)", {}) + + vim.keymap.set("n", "y", "(YankyYank)", {}) + vim.keymap.set("x", "y", "(YankyYank)", {}) +end + +M.setup() diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky/special_put_spec.lua b/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky/special_put_spec.lua new file mode 100644 index 00000000..5e13dd76 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky/special_put_spec.lua @@ -0,0 +1,77 @@ +local yanky = require("yanky") + +local function get_buf_lines() + local result = vim.api.nvim_buf_get_lines(0, 0, vim.api.nvim_buf_line_count(0), false) + return result +end + +local function execute_keys(feedkeys) + local keys = vim.api.nvim_replace_termcodes(feedkeys, true, false, true) + vim.api.nvim_feedkeys(keys, "x!", false) +end + +local function setup() + yanky.setup({ ring = { storage = "memory" } }) + + local buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_command("buffer " .. buf) + + vim.api.nvim_buf_set_lines(0, 0, -1, true, { + "void test() {", + " int a = 1;", + " int b = 2;", + " for (int i = 0; i < 10; ++i) {", + " cout << a;", + " cout << b;", + " }", + "}", + }) + + vim.cmd("set ft=c") +end + +describe("Special Put", function() + before_each(setup) + + it("should PutAfterFilter", function() + vim.cmd("5") + execute_keys("2yy") + vim.cmd("3") + vim.cmd('execute "normal \\(YankyPutAfterFilter)"') + assert.are.same({ + "void test() {", + " int a = 1;", + " int b = 2;", + " cout << a;", + " cout << b;", + " for (int i = 0; i < 10; ++i) {", + " cout << a;", + " cout << b;", + " }", + "}", + }, get_buf_lines()) + + assert.are.same({ 4, 4 }, vim.api.nvim_win_get_cursor(0)) + end) + + it("should GPutBeforeFilter", function() + vim.cmd("5") + execute_keys("2yy") + vim.cmd("4") + vim.cmd('execute "normal \\(YankyGPutBeforeFilter)"') + assert.are.same({ + "void test() {", + " int a = 1;", + " int b = 2;", + " cout << a;", + " cout << b;", + " for (int i = 0; i < 10; ++i) {", + " cout << a;", + " cout << b;", + " }", + "}", + }, get_buf_lines()) + + assert.are.same({ 6, 0 }, vim.api.nvim_win_get_cursor(0)) + end) +end) diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky/system_clipboard_spec.lua b/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky/system_clipboard_spec.lua new file mode 100644 index 00000000..c97949bf --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky/system_clipboard_spec.lua @@ -0,0 +1,29 @@ +local yanky = require("yanky") +-- local system_clipboard = require("yanky.system_clipboard") +-- local utils = require("yanky.utils") + +describe("Sync clipbard", function() + before_each(function() + yanky.setup({ + ring = { + storage = "memory", + }, + system_clipboard = { + sync_with_ring = true, + }, + }) + end) + + it("should add system clipboard to history on focus", function() + -- vim.fn.setreg("*", "default_value", "V") + -- system_clipboard.on_focus_lost() + -- + -- vim.fn.setreg("*", "new_value", "v") + -- system_clipboard.on_focus_gained() + -- + -- assert.are.same({ + -- regcontents = "new_value", + -- regtype = "v", + -- }, yanky.history.first()) + end) +end) diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky_cycle_spec.lua b/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky_cycle_spec.lua new file mode 100644 index 00000000..b040cfdb --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky_cycle_spec.lua @@ -0,0 +1,116 @@ +local yanky = require("yanky") + +local function execute_keys(feedkeys) + local keys = vim.api.nvim_replace_termcodes(feedkeys, true, false, true) + vim.api.nvim_feedkeys(keys, "x!", false) +end + +local function get_buf_lines() + local result = vim.api.nvim_buf_get_lines(0, 0, vim.api.nvim_buf_line_count(0), false) + return result +end + +local function setup() + yanky.setup({ ring = { storage = "memory" } }) + + local buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_command("buffer " .. buf) + + vim.api.nvim_buf_set_lines(0, 0, -1, true, { "Lorem", "ipsum", "dolor", "sit", "amet" }) + execute_keys("iu") -- Breaks undo sequence, don't know why I should do that +end + +describe("Cycle", function() + before_each(setup) + + it("should work in charwise mode", function() + execute_keys("yw") + execute_keys("j") + execute_keys("yw") + execute_keys("ll") + + execute_keys("p") + assert.are.same({ "Lorem", "ipsipsumum", "dolor", "sit", "amet" }, get_buf_lines()) + + execute_keys(",p") + assert.are.same({ "Lorem", "ipsLoremum", "dolor", "sit", "amet" }, get_buf_lines()) + + execute_keys(",P") + assert.are.same({ "Lorem", "ipsipsumum", "dolor", "sit", "amet" }, get_buf_lines()) + + execute_keys(",P") + assert.are.same({ "Lorem", "ipsipsumum", "dolor", "sit", "amet" }, get_buf_lines()) + end) + + it("should work in linewise mode", function() + execute_keys("yy") + execute_keys("j") + execute_keys("yw") + execute_keys("j") + execute_keys("yy") + + execute_keys("p") + assert.are.same({ "Lorem", "ipsum", "dolor", "dolor", "sit", "amet" }, get_buf_lines()) + + execute_keys(",p") + assert.are.same({ "Lorem", "ipsum", "dipsumolor", "sit", "amet" }, get_buf_lines()) + + execute_keys(",p") + assert.are.same({ "Lorem", "ipsum", "dolor", "Lorem", "sit", "amet" }, get_buf_lines()) + end) + + it("should work in visual mode", function() + execute_keys("yy") + execute_keys("j") + execute_keys("yw") + execute_keys("j") + execute_keys("yy") + + execute_keys("v3l") + + execute_keys("p") + assert.are.same({ "Lorem", "ipsum", "", "dolor", "r", "sit", "amet" }, get_buf_lines()) + + -- Should not do that but cant manage to make it works now + execute_keys(",p") + assert.are.same({ "Lorem", "ipsum", "", "dolor", "r", "sit", "amet" }, get_buf_lines()) + + execute_keys(",p") + assert.are.same({ "Lorem", "ipsum", "ipsumr", "sit", "amet" }, get_buf_lines()) + + execute_keys(",p") + assert.are.same({ "Lorem", "ipsum", "", "Lorem", "r", "sit", "amet" }, get_buf_lines()) + + execute_keys(",P") + assert.are.same({ "Lorem", "ipsum", "ipsumr", "sit", "amet" }, get_buf_lines()) + + execute_keys(",P") + assert.are.same({ "Lorem", "ipsum", "", "dolor", "r", "sit", "amet" }, get_buf_lines()) + + execute_keys(",P") + assert.are.same({ "Lorem", "ipsum", "dolor", "sit", "amet" }, get_buf_lines()) + + execute_keys(",P") + assert.are.same({ "Lorem", "ipsum", "dolor", "sit", "amet" }, get_buf_lines()) + end) + + it("should work in visual-block mode", function() + execute_keys("yy") + execute_keys("j") + execute_keys("yw") + execute_keys("j") + execute_keys("yy") + + execute_keys("ggjl") + + execute_keys("p") + assert.are.same({ "rem", "sum", "dolor", "dolor", "sit", "amet" }, get_buf_lines()) + + -- Should not do that but cant manage to make it works now + execute_keys(",p") + assert.are.same({ "rem", "sum", "dolor", "dolor", "sit", "amet" }, get_buf_lines()) + + execute_keys(",p") + assert.are.same({ "ipsumrem", "ipsumsum", "dolor", "sit", "amet" }, get_buf_lines()) + end) +end) diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky_spec.lua b/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky_spec.lua new file mode 100644 index 00000000..9372e8fb --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/spec/yanky_spec.lua @@ -0,0 +1,155 @@ +local yanky = require("yanky") + +local function get_buf_lines() + local result = vim.api.nvim_buf_get_lines(0, 0, vim.api.nvim_buf_line_count(0), false) + return result +end + +local function execute_keys(feedkeys) + local keys = vim.api.nvim_replace_termcodes(feedkeys, true, false, true) + vim.api.nvim_feedkeys(keys, "x!", false) +end + +local function setup() + yanky.setup({ ring = { storage = "memory" } }) + + local buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_command("buffer " .. buf) + + vim.api.nvim_buf_set_lines(0, 0, -1, true, { "Lorem", "ipsum", "dolor", "sit", "amet" }) +end + +describe("Put in charwise mode", function() + before_each(setup) + + it("should paste after word in charwise mode", function() + execute_keys("yw") + execute_keys("jll") + execute_keys("p") + + assert.are.same({ "Lorem", "ipsLoremum", "dolor", "sit", "amet" }, get_buf_lines()) + end) + + it("should paste before word in charwise mode", function() + execute_keys("lyw") + execute_keys("jjll") + execute_keys("P") + + assert.are.same({ "Lorem", "ipsum", "doloremor", "sit", "amet" }, get_buf_lines()) + end) + + it("should be repeatable in charwise mode", function() + execute_keys("yw") + execute_keys("jll") + execute_keys("3p") + + assert.are.same({ "Lorem", "ipsLoremLoremLoremum", "dolor", "sit", "amet" }, get_buf_lines()) + end) + + it("should use specified register in charwise mode", function() + vim.fn.setreg("a", "REGISTRED", "c") + execute_keys("jll") + execute_keys('"ap') + + assert.are.same({ "Lorem", "ipsREGISTREDum", "dolor", "sit", "amet" }, get_buf_lines()) + end) +end) + +describe("Put in linewise mode", function() + before_each(setup) + + it("should paste after in linewise mode", function() + execute_keys("yy") + execute_keys("jll") + execute_keys("p") + + assert.are.same({ "Lorem", "ipsum", "Lorem", "dolor", "sit", "amet" }, get_buf_lines()) + end) + + it("should paste before in linewise mode", function() + execute_keys("yy") + execute_keys("jll") + execute_keys("P") + + assert.are.same({ "Lorem", "Lorem", "ipsum", "dolor", "sit", "amet" }, get_buf_lines()) + end) + + it("should be repeatable in linewise mode", function() + execute_keys("2yy") + execute_keys("jll") + execute_keys("3p") + + assert.are.same( + { "Lorem", "ipsum", "Lorem", "ipsum", "Lorem", "ipsum", "Lorem", "ipsum", "dolor", "sit", "amet" }, + get_buf_lines() + ) + end) + + it("should use specified register in linewise mode", function() + vim.fn.setreg("b", "REGISTRED", "l") + execute_keys("jll") + execute_keys('"bp') + + assert.are.same({ "Lorem", "ipsum", "REGISTRED", "dolor", "sit", "amet" }, get_buf_lines()) + end) +end) + +describe("Put in blocwise mode", function() + before_each(setup) + + it("should paste after", function() + execute_keys("jjlly") + execute_keys("gg^") + execute_keys("jll") + execute_keys("p") + + assert.are.same({ "Lorem", "ipsLorum", "dolipsor", "sitdol", "amet" }, get_buf_lines()) + end) + + it("should paste before", function() + execute_keys("jjlly") + execute_keys("gg^") + execute_keys("jll") + execute_keys("P") + + assert.are.same({ "Lorem", "ipLorsum", "doipslor", "sidolt", "amet" }, get_buf_lines()) + end) + + it("should be repeatable", function() + execute_keys("jjlly") + execute_keys("gg^") + execute_keys("jll") + execute_keys("3p") + + assert.are.same({ "Lorem", "ipsLorLorLorum", "dolipsipsipsor", "sitdoldoldol", "amet" }, get_buf_lines()) + end) +end) + +describe("Put in visual mode", function() + before_each(setup) + + it("should paste after", function() + execute_keys("y3l") + execute_keys("jvll") + execute_keys("p") + + assert.are.same({ "Lorem", "Lorum", "dolor", "sit", "amet" }, get_buf_lines()) + end) + + it("should paste in linewise visual selection", function() + execute_keys("y3l") + execute_keys("jV") + execute_keys("p") + + assert.are.same({ "Lorem", "Lor", "dolor", "sit", "amet" }, get_buf_lines()) + end) + + it("should be repeatable", function() + execute_keys("y3l") + execute_keys("j") + execute_keys("vll") + execute_keys("3p") + + assert.are.same({ "Lorem", "LorLorLorum", "dolor", "sit", "amet" }, get_buf_lines()) + end) +end) diff --git a/config/neovim/store/lazy-plugins/yanky.nvim/stylua.toml b/config/neovim/store/lazy-plugins/yanky.nvim/stylua.toml new file mode 100644 index 00000000..d18e5998 --- /dev/null +++ b/config/neovim/store/lazy-plugins/yanky.nvim/stylua.toml @@ -0,0 +1,7 @@ + +column_width = 120 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferDouble" +no_call_parentheses = false diff --git a/config/neovim/store/nvim-treesitter/.editorconfig b/config/neovim/store/nvim-treesitter/.editorconfig new file mode 100644 index 00000000..00ea231b --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.editorconfig @@ -0,0 +1,20 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +tab_width = 8 +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.lua] +tab_width = 2 + +[*.py] +indent_size = 4 +tab_width = 4 + +[{Makefile,**/Makefile,runtime/doc/*.txt}] +indent_style = tab +indent_size = 8 diff --git a/config/neovim/store/nvim-treesitter/.github/CODEOWNERS b/config/neovim/store/nvim-treesitter/.github/CODEOWNERS new file mode 100644 index 00000000..8fcb7cd9 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/CODEOWNERS @@ -0,0 +1,7 @@ +/lua/nvim-treesitter/textobjects/ @theHamsta +/lua/nvim-treesitter/incremental_selection.lua @theHamsta + +/lua/nvim-treesitter/fold.lua @vigoux +/lua/nvim-treesitter/highlight.lua @vigoux + +/lua/nvim-treesitter/refactor/ @steelsojka diff --git a/config/neovim/store/nvim-treesitter/.github/FUNDING.yml b/config/neovim/store/nvim-treesitter/.github/FUNDING.yml new file mode 100644 index 00000000..b2844b21 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/FUNDING.yml @@ -0,0 +1,2 @@ +open_collective: "nvim-treesitter" +github: "nvim-treesitter" diff --git a/config/neovim/store/nvim-treesitter/.github/ISSUE_TEMPLATE/bug_report.yml b/config/neovim/store/nvim-treesitter/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..f68dd248 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,58 @@ +name: Bug report +description: Create a report to help us improve +labels: [bug] + +body: + - type: markdown + attributes: + value: | + # Before reporting + Please do the following steps before reporting an issue. + + - I have updated my neovim version to latest _master_ + - I have updated my plugin to the latest version + - I have run `:TSUpdate` + - I have read the [troubleshooting section](https://github.com/nvim-treesitter/nvim-treesitter#troubleshooting) + + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + attributes: + label: To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + validations: + required: true + + - type: textarea + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + + - type: textarea + attributes: + label: Output of `:checkhealth nvim-treesitter` + render: markdown + validations: + required: true + + - type: textarea + attributes: + label: Output of `nvim --version` + render: text + validations: + required: true + + - type: textarea + attributes: + label: Additional context + description: Add any context about the problem here. diff --git a/config/neovim/store/nvim-treesitter/.github/ISSUE_TEMPLATE/feature_request.md b/config/neovim/store/nvim-treesitter/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..d883b8f2 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "" +labels: enhancement +assignees: "" +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/config/neovim/store/nvim-treesitter/.github/ISSUE_TEMPLATE/highlighting_issue.yml b/config/neovim/store/nvim-treesitter/.github/ISSUE_TEMPLATE/highlighting_issue.yml new file mode 100644 index 00000000..a429b5eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/ISSUE_TEMPLATE/highlighting_issue.yml @@ -0,0 +1,98 @@ +name: Highlighting issue +description: Missing or incorrect highlights or you want to change the way something is highlighted +labels: [highlights] + +body: + - type: markdown + attributes: + value: | + # Before reporting + Please perform the following steps before reporting an issue. + - I have updated my neovim version to latest _master_. + - I have updated my plugin to the latest version. + - I have run `:TSUpdate`. + - I have inspected the syntax tree using `:InspectTree` and made sure + that no `ERROR` nodes are in the syntax tree. nvim-treesitter can not guarantee correct highlighting in the + presence of `ERROR`s -- in this case, please report the bug directly at corresponding parser's repository. (You can find all repository URLs in [README.md](https://github.com/nvim-treesitter/nvim-treesitter#supported-languages).) + - I have used `:Inspect` to inspect which highlight groups Neovim is using and that legacy syntax highlighting is not interfering (i.e., what you are observing is actual tree-sitter highlighting). + + - type: textarea + attributes: + label: Describe the highlighting problem + description: A clear and concise description of what should be highlighted in a different way. + validations: + required: true + + - type: textarea + attributes: + label: Example snippet that causes the problem + description: Please provide an example snippet in plain text that causes the problem. + validations: + required: true + + - type: textarea + attributes: + label: Tree-sitter parsing result + description: | + Please provide the output of `:InspectTree` (screenshot or plain text) + with the following options enabled (pressing the key): + - `I` (name of the parsed language) + - `t` (toggle injected languages) + - `a` (show anonymous nodes) + placeholder: | + This should look somehow like this: + ``` + preproc_ifdef [0, 0] - [4, 6] cpp + "#ifdef" [0, 0] - [0, 6] cpp + name: identifier [0, 7] - [0, 17] cpp + preproc_def [1, 0] - [2, 0] cpp + "#define" [1, 0] - [1, 7] cpp + name: identifier [1, 8] - [1, 16] cpp + value: preproc_arg [1, 16] - [1, 27] cpp + "\n" [1, 27] - [2, 0] cpp + alternative: preproc_else [2, 0] - [4, 0] cpp + "#else" [2, 0] - [2, 5] cpp + preproc_def [3, 0] - [4, 0] cpp + "#define" [3, 0] - [3, 7] cpp + name: identifier [3, 8] - [3, 16] cpp + value: preproc_arg [3, 16] - [3, 29] cpp + ``` + validations: + required: true + + - type: textarea + attributes: + label: Example screenshot + description: | + Please provide a screenshot of the current highlighting. Please also tell us the `:h colorscheme` you are using + and how to install it. If applicable, you can also upload a screenshot with the contents of + `:Inspect`. + validations: + required: true + + - type: textarea + attributes: + label: Expected behavior + description: | + A clear and concise description of what you expect to be changed. You can provide screenshot of + other editors or traditional Vim highlighting that don't show this problem or show a screenshot how + nvim-treesitter highlighting would look like when a problematic query would be removed/altered. + + - type: textarea + attributes: + label: Output of `:checkhealth nvim-treesitter` + render: markdown + validations: + required: true + + - type: textarea + attributes: + label: Output of `nvim --version` + render: text + validations: + required: true + + - type: textarea + attributes: + label: Additional context + description: Add any context about the problem here. diff --git a/config/neovim/store/nvim-treesitter/.github/workflows/lint.yml b/config/neovim/store/nvim-treesitter/.github/workflows/lint.yml new file mode 100644 index 00000000..b73f03cf --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/workflows/lint.yml @@ -0,0 +1,50 @@ +name: Linting and style checking + +on: + pull_request: + branches: + - "master" + +jobs: + luacheck: + name: Luacheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Prepare + run: | + sudo apt-get update + sudo apt-get install luarocks -y + sudo luarocks install luacheck + + - name: Run Luacheck + run: luacheck . + + stylua: + name: StyLua + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Lint with stylua + uses: JohnnyMorganz/stylua-action@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + args: --check . + + format-queries: + name: Lint queries + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Prepare + run: | + wget https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz + tar -zxf nvim-linux64.tar.gz + sudo ln -s "$PWD"/nvim-linux64/bin/nvim /usr/local/bin + + - name: Lint + run: | + nvim -l scripts/format-queries.lua + git diff --exit-code diff --git a/config/neovim/store/nvim-treesitter/.github/workflows/release.yml b/config/neovim/store/nvim-treesitter/.github/workflows/release.yml new file mode 100644 index 00000000..11f3ffc1 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/workflows/release.yml @@ -0,0 +1,24 @@ +name: "release" +on: + push: + tags: # Will upload to luarocks.org + - "*" + pull_request: # Will test a local install without uploading to luarocks.org + paths: + - 'contrib/*.rockspec' + - .github/workflows/release.yml + +jobs: + luarocks-upload: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: nvim-neorocks/luarocks-tag-release@v5 + env: + LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }} + with: + name: nvim-treesitter + detailed_description: | + The goal of nvim-treesitter is both to provide a simple and easy way to use the interface for tree-sitter in Neovim + and to provide some basic functionality such as highlighting based on it. + template: contrib/nvim-treesitter-luarocks.template diff --git a/config/neovim/store/nvim-treesitter/.github/workflows/test-queries.yml b/config/neovim/store/nvim-treesitter/.github/workflows/test-queries.yml new file mode 100644 index 00000000..bc4a18d5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/workflows/test-queries.yml @@ -0,0 +1,88 @@ +name: Test queries + +on: + push: + branches: + - "master" + pull_request: + branches: + - "master" + +# Cancel any in-progress CI runs for a PR if it is updated +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + check_compilation: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-14] + cc: [gcc, clang] + nvim_tag: [stable] + exclude: + - os: ubuntu-latest + cc: clang + nvim_tag: stable + + - os: macos-14 + cc: gcc + nvim_tag: stable + + - os: windows-latest + cc: clang + nvim_tag: stable + + include: + - os: windows-latest + cc: cl + nvim_tag: nightly + + - os: ubuntu-latest + cc: gcc + nvim_tag: nightly + + name: Parser compilation + runs-on: ${{ matrix.os }} + env: + CC: ${{ matrix.cc }} + NVIM: ${{ matrix.os == 'windows-latest' && 'nvim-win64\\bin\\nvim.exe' || 'nvim' }} + ALLOWED_INSTALLATION_FAILURES: ${{ matrix.os == 'windows-latest' && 'rnoweb' }} + steps: + - uses: actions/checkout@v4 + - uses: tree-sitter/setup-action/cli@v1 + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Install and prepare Neovim + env: + NVIM_TAG: ${{ matrix.nvim_tag }} + run: | + bash ./scripts/ci-install.sh + + - name: Setup Parsers Cache + id: parsers-cache + uses: actions/cache@v4 + with: + path: | + ./parser/ + ~/AppData/Local/nvim/pack/nvim-treesitter/start/nvim-treesitter/parser/ + key: parsers-${{ join(matrix.*, '-') }}-${{ hashFiles( + './lockfile.json', + './lua/nvim-treesitter/install.lua', + './lua/nvim-treesitter/parsers.lua', + './lua/nvim-treesitter/shell_command_selectors.lua') }} + + - name: Compile parsers + run: $NVIM --headless -c "lua require'nvim-treesitter.install'.prefer_git=false" -c "TSInstallSync all" -c "q" + + - name: Post compile Windows + if: runner.os == 'Windows' + run: cp -r ~/AppData/Local/nvim/pack/nvim-treesitter/start/nvim-treesitter/parser/* parser + + - name: Check query files + run: $NVIM -l scripts/check-queries.lua diff --git a/config/neovim/store/nvim-treesitter/.github/workflows/tests.yml b/config/neovim/store/nvim-treesitter/.github/workflows/tests.yml new file mode 100644 index 00000000..6c4ad957 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/workflows/tests.yml @@ -0,0 +1,65 @@ +name: Tests + +on: + push: + branches: + - "master" + pull_request: + branches: + - "master" + +# Cancel any in-progress CI runs for a PR if it is updated +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +jobs: + check_compilation: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + cc: [gcc] + + name: Run tests + runs-on: ${{ matrix.os }} + env: + CC: ${{ matrix.cc }} + steps: + - uses: actions/checkout@v4 + - uses: tree-sitter/setup-action/cli@v1 + + - name: Test Dependencies + run: | + mkdir -p ~/.local/share/nvim/site/pack/plenary.nvim/start + cd ~/.local/share/nvim/site/pack/plenary.nvim/start + git clone https://github.com/nvim-lua/plenary.nvim + curl -L https://github.com/theHamsta/highlight-assertions/releases/download/v0.1.6/highlight-assertions_v0.1.6_x86_64-unknown-linux-gnu.tar.gz | tar -xz + cp highlight-assertions /usr/local/bin + + - name: Install and prepare Neovim + env: + NVIM_TAG: stable + run: | + bash ./scripts/ci-install.sh + + - name: Setup Parsers Cache + id: parsers-cache + uses: actions/cache@v4 + with: + path: | + ./parser/ + ~/AppData/Local/nvim/pack/nvim-treesitter/start/nvim-treesitter/parser/ + key: parsers-${{ join(matrix.*, '-') }}-${{ hashFiles( + './lockfile.json', + './lua/nvim-treesitter/install.lua', + './lua/nvim-treesitter/parsers.lua', + './lua/nvim-treesitter/shell_selectors.lua') }} + + - name: Compile parsers Unix like + if: ${{ runner.os != 'Windows' && steps.parsers-cache.outputs.cache-hit != 'true' }} + run: | + nvim --headless -c "TSInstallSync all" -c "q" + + - name: Tests + run: PATH=/usr/local/bin:$PATH ./scripts/run_tests.sh diff --git a/config/neovim/store/nvim-treesitter/.github/workflows/update-lockfile.yml b/config/neovim/store/nvim-treesitter/.github/workflows/update-lockfile.yml new file mode 100644 index 00000000..b994622b --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/workflows/update-lockfile.yml @@ -0,0 +1,58 @@ +name: Update lockfile + +on: + schedule: + - cron: "30 6 * * *" + workflow_dispatch: + +jobs: + update-lockfile: + name: Update lockfile + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: master + + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.TOKEN_ID }} + private-key: ${{ secrets.TOKEN_PRIVATE_KEY }} + + - name: Prepare + env: + NVIM_TAG: stable + run: | + wget https://github.com/josephburnett/jd/releases/download/v1.7.1/jd-amd64-linux + mv jd-amd64-linux /tmp/jd + chmod +x /tmp/jd + bash scripts/ci-install.sh + + - name: Update parsers + env: + SKIP_LOCKFILE_UPDATE_FOR_LANGS: "" + run: | + cp lockfile.json /tmp/old_lockfile.json + nvim -l scripts/write-lockfile.lua + # Pretty print + cp lockfile.json /tmp/lockfile.json + cat /tmp/lockfile.json | jq --sort-keys > lockfile.json + UPDATED_PARSERS=$(/tmp/jd -f merge /tmp/old_lockfile.json lockfile.json | jq -r 'keys | join(", ")') + echo "UPDATED_PARSERS=$UPDATED_PARSERS" >> $GITHUB_ENV + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ steps.app-token.outputs.token }} + author: "nvim-treesitter-bot[bot] <157957100+nvim-treesitter-bot[bot]@users.noreply.github.com>" + commit-message: "bot(lockfile): update ${{ env.UPDATED_PARSERS }}" + title: "Update lockfile.json: ${{ env.UPDATED_PARSERS }}" + body: "[beep boop](https://github.com/peter-evans/create-pull-request)" + branch: update-lockfile-pr + base: ${{ github.head_ref }} + + - name: Enable Pull Request Automerge + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: gh pr merge --rebase --auto update-lockfile-pr diff --git a/config/neovim/store/nvim-treesitter/.github/workflows/update-readme.yml b/config/neovim/store/nvim-treesitter/.github/workflows/update-readme.yml new file mode 100644 index 00000000..44f10a42 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.github/workflows/update-readme.yml @@ -0,0 +1,46 @@ +name: Update README + +on: + push: + branches: + - master + workflow_dispatch: + +jobs: + update-readme: + name: Update README + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.TOKEN_ID }} + private-key: ${{ secrets.TOKEN_PRIVATE_KEY }} + + - name: Prepare + env: + NVIM_TAG: stable + run: | + bash ./scripts/ci-install.sh + + - name: Check README + run: | + nvim -l scripts/update-readme.lua || echo 'Needs update' + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ steps.app-token.outputs.token }} + author: "nvim-treesitter-bot[bot] <157957100+nvim-treesitter-bot[bot]@users.noreply.github.com>" + commit-message: "bot(readme): update" + title: Update README + body: "[beep boop](https://github.com/peter-evans/create-pull-request)" + branch: update-readme-pr + base: ${{ github.head_ref }} + + - name: Enable Pull Request Automerge + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: gh pr merge --rebase --auto update-readme-pr diff --git a/config/neovim/store/nvim-treesitter/.gitignore b/config/neovim/store/nvim-treesitter/.gitignore new file mode 100644 index 00000000..0ac164cd --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.gitignore @@ -0,0 +1,7 @@ +doc/tags +.luacheckcache +/tags +nvim.appimage +nvim-linux64* +nvim-macos* +nvim-win64* diff --git a/config/neovim/store/nvim-treesitter/.luacheckrc b/config/neovim/store/nvim-treesitter/.luacheckrc new file mode 100644 index 00000000..b0ee19d8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.luacheckrc @@ -0,0 +1,21 @@ +-- Rerun tests only if their modification time changed. +cache = true +codes = true + +exclude_files = { + "tests/indent/lua/" +} + +-- Glorious list of warnings: https://luacheck.readthedocs.io/en/stable/warnings.html +ignore = { + "212", -- Unused argument, In the case of callback function, _arg_name is easier to understand than _, so this option is set to off. + "411", -- Redefining a local variable. + "412", -- Redefining an argument. + "422", -- Shadowing an argument + "122" -- Indirectly setting a readonly global +} + +-- Global objects defined by the C code +read_globals = { + "vim", +} diff --git a/config/neovim/store/nvim-treesitter/.luarc.json b/config/neovim/store/nvim-treesitter/.luarc.json new file mode 100644 index 00000000..8dc06050 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.luarc.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", + "runtime": { + "version": "LuaJIT" + }, + "workspace": { + "library": [ + "lua", + "$VIMRUNTIME", + "${3rd}/luv/library" + ], + "checkThirdParty": false + }, + "diagnostics": { + "groupFileStatus": { + "strict": "Opened", + "strong": "Opened" + }, + "groupSeverity": { + "strong": "Warning", + "strict": "Warning" + }, + "unusedLocalExclude": [ "_*" ] + } +} diff --git a/config/neovim/store/nvim-treesitter/.stylua.toml b/config/neovim/store/nvim-treesitter/.stylua.toml new file mode 100644 index 00000000..ecb6dca5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.stylua.toml @@ -0,0 +1,6 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferDouble" +call_parentheses = "None" diff --git a/config/neovim/store/nvim-treesitter/.styluaignore b/config/neovim/store/nvim-treesitter/.styluaignore new file mode 100644 index 00000000..2b6c4c6a --- /dev/null +++ b/config/neovim/store/nvim-treesitter/.styluaignore @@ -0,0 +1 @@ +tests/indent/lua/ diff --git a/config/neovim/store/nvim-treesitter/CONTRIBUTING.md b/config/neovim/store/nvim-treesitter/CONTRIBUTING.md new file mode 100644 index 00000000..449cddcb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/CONTRIBUTING.md @@ -0,0 +1,401 @@ +# Contributing to `nvim-treesitter` + +First of all, thank you very much for contributing to `nvim-treesitter`. + +If you haven't already, you should really come and reach out to us on our +[Matrix channel], so we can help you with any question you might have! + +As you know, `nvim-treesitter` is roughly split in two parts: + +- Parser configurations : for various things like `locals`, `highlights` +- What we like to call _modules_ : tiny Lua modules that provide a given feature, based on parser configurations + +Depending on which part of the plugin you want to contribute to, please read the appropriate section. + +## Style Checks and Tests + +We haven't implemented any functional tests yet. Feel free to contribute. +However, we check code style with `luacheck` and `stylua`! +Please install luacheck and activate our `pre-push` hook to automatically check style before +every push: + +```bash +luarocks install luacheck +cargo install stylua +ln -s ../../scripts/pre-push .git/hooks/pre-push +``` + +## Adding new modules + +If you want to see a new functionality added to `nvim-treesitter` feel free to first open an issue +to that we can track our solution! +Thus far, there is basically two types of modules: + +- Little modules (like `incremental selection`) that are built in `nvim-treesitter`, we call them + `builtin modules`. +- Bigger modules (like `completion-treesitter`, or `nvim-tree-docs`), or modules that integrate + with other plugins, that we call `remote modules`. + +In any case, you can build your own module! To help you started in the process, we have a template +repository designed to build new modules [here](https://github.com/nvim-treesitter/module-template). +Feel free to use it, and contact us over on our +on the "Neovim tree-sitter" [Matrix channel]. + +## Parser configurations + +Contributing to parser configurations is basically modifying one of the `queries/*/*.scm`. +Each of these `scheme` files contains a _tree-sitter query_ for a given purpose. +Before going any further, we highly suggest that you [read more about tree-sitter queries](https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries). + +Each query has an appropriate name, which is then used by modules to extract data from the syntax tree. +For now these are the types of queries used by `nvim-treesitter`: + +- `highlights.scm`: used for syntax highlighting, using the `highlight` module. +- `locals.scm`: used to extract keyword definitions, scopes, references, etc, using the `locals` module. +- `textobjects.scm`: used to define text objects. +- `folds.scm`: used to define folds. +- `injections.scm`: used to define injections. + +For these types there is a _norm_ you will have to follow so that features work fine. +Here are some global advices: + +- If your language is listed [here](https://github.com/nvim-treesitter/nvim-treesitter#supported-languages), + you can install the [playground plugin](https://github.com/nvim-treesitter/playground). +- If your language is listed [here](https://github.com/nvim-treesitter/nvim-treesitter#supported-languages), + you can debug and experiment with your queries there. +- If not, you should consider installing the [tree-sitter CLI](https://github.com/tree-sitter/tree-sitter/tree/master/cli), + you should then be able to open a local playground using `tree-sitter build-wasm && tree-sitter web-ui` within the + parsers repo. +- Examples of queries can be found in [queries/](queries/) +- Matches in the bottom will override queries that are above of them. + +#### Inheriting languages + +If your language is an extension of a language (TypeScript is an extension of JavaScript for +example), you can include the queries from your base language by adding the following _as the first +line of your file_. + +```query +; inherits: lang1,(optionallang) +``` + +If you want to inherit a language, but don't want the languages inheriting from yours to inherit it, +you can mark the language as optional (by putting it between parenthesis). + +#### Formatting + +All queries are expected to follow a standard format, with every node on a single line and indented by two spaces for each level of nesting. You can automatically format the bundled queries by running the provided formatter `./scripts/format-queries.lua` on a single file (ending in `.scm`) or directory to format. + +Should you need to preserve a specific format for a node, you can exempt it (and all contained nodes) by placing before it +```query +; format-ignore +``` + +### Highlights + +As languages differ quite a lot, here is a set of captures available to you when building a `highlights.scm` query. Note that your color scheme needs to define (or link) these captures as highlight groups. + +#### Identifiers + +```query +@variable ; various variable names +@variable.builtin ; built-in variable names (e.g. `this`) +@variable.parameter ; parameters of a function +@variable.parameter.builtin ; special parameters (e.g. `_`, `it`) +@variable.member ; object and struct fields + +@constant ; constant identifiers +@constant.builtin ; built-in constant values +@constant.macro ; constants defined by the preprocessor + +@module ; modules or namespaces +@module.builtin ; built-in modules or namespaces +@label ; GOTO and other labels (e.g. `label:` in C), including heredoc labels +``` + +#### Literals + +```query +@string ; string literals +@string.documentation ; string documenting code (e.g. Python docstrings) +@string.regexp ; regular expressions +@string.escape ; escape sequences +@string.special ; other special strings (e.g. dates) +@string.special.symbol ; symbols or atoms +@string.special.url ; URIs (e.g. hyperlinks) +@string.special.path ; filenames + +@character ; character literals +@character.special ; special characters (e.g. wildcards) + +@boolean ; boolean literals +@number ; numeric literals +@number.float ; floating-point number literals +``` + +#### Types + +```query +@type ; type or class definitions and annotations +@type.builtin ; built-in types +@type.definition ; identifiers in type definitions (e.g. `typedef ` in C) + +@attribute ; attribute annotations (e.g. Python decorators, Rust lifetimes) +@attribute.builtin ; builtin annotations (e.g. `@property` in Python) +@property ; the key in key/value pairs +``` + +#### Functions + +```query +@function ; function definitions +@function.builtin ; built-in functions +@function.call ; function calls +@function.macro ; preprocessor macros + +@function.method ; method definitions +@function.method.call ; method calls + +@constructor ; constructor calls and definitions +@operator ; symbolic operators (e.g. `+` / `*`) +``` + +#### Keywords + +```query +@keyword ; keywords not fitting into specific categories +@keyword.coroutine ; keywords related to coroutines (e.g. `go` in Go, `async/await` in Python) +@keyword.function ; keywords that define a function (e.g. `func` in Go, `def` in Python) +@keyword.operator ; operators that are English words (e.g. `and` / `or`) +@keyword.import ; keywords for including or exporting modules (e.g. `import` / `from` in Python) +@keyword.type ; keywords describing namespaces and composite types (e.g. `struct`, `enum`) +@keyword.modifier ; keywords modifying other constructs (e.g. `const`, `static`, `public`) +@keyword.repeat ; keywords related to loops (e.g. `for` / `while`) +@keyword.return ; keywords like `return` and `yield` +@keyword.debug ; keywords related to debugging +@keyword.exception ; keywords related to exceptions (e.g. `throw` / `catch`) + +@keyword.conditional ; keywords related to conditionals (e.g. `if` / `else`) +@keyword.conditional.ternary ; ternary operator (e.g. `?` / `:`) + +@keyword.directive ; various preprocessor directives & shebangs +@keyword.directive.define ; preprocessor definition directives +``` + +#### Punctuation + +```query +@punctuation.delimiter ; delimiters (e.g. `;` / `.` / `,`) +@punctuation.bracket ; brackets (e.g. `()` / `{}` / `[]`) +@punctuation.special ; special symbols (e.g. `{}` in string interpolation) +``` + +#### Comments + +```query +@comment ; line and block comments +@comment.documentation ; comments documenting code + +@comment.error ; error-type comments (e.g. `ERROR`, `FIXME`, `DEPRECATED`) +@comment.warning ; warning-type comments (e.g. `WARNING`, `FIX`, `HACK`) +@comment.todo ; todo-type comments (e.g. `TODO`, `WIP`) +@comment.note ; note-type comments (e.g. `NOTE`, `INFO`, `XXX`) +``` + +#### Markup + +Mainly for markup languages. + +```query +@markup.strong ; bold text +@markup.italic ; italic text +@markup.strikethrough ; struck-through text +@markup.underline ; underlined text (only for literal underline markup!) + +@markup.heading ; headings, titles (including markers) +@markup.heading.1 ; top-level heading +@markup.heading.2 ; section heading +@markup.heading.3 ; subsection heading +@markup.heading.4 ; and so on +@markup.heading.5 ; and so forth +@markup.heading.6 ; six levels ought to be enough for anybody + +@markup.quote ; block quotes +@markup.math ; math environments (e.g. `$ ... $` in LaTeX) + +@markup.link ; text references, footnotes, citations, etc. +@markup.link.label ; link, reference descriptions +@markup.link.url ; URL-style links + +@markup.raw ; literal or verbatim text (e.g. inline code) +@markup.raw.block ; literal or verbatim text as a stand-alone block + ; (use priority 90 for blocks with injections) + +@markup.list ; list markers +@markup.list.checked ; checked todo-style list markers +@markup.list.unchecked ; unchecked todo-style list markers +``` + +```query +@diff.plus ; added text (for diff files) +@diff.minus ; deleted text (for diff files) +@diff.delta ; changed text (for diff files) +``` + +```query +@tag ; XML-style tag names (and similar) +@tag.builtin ; builtin tag names (e.g. HTML5 tags) +@tag.attribute ; XML-style tag attributes +@tag.delimiter ; XML-style tag delimiters +``` + +#### Non-highlighting captures + +```query +@none ; completely disable the highlight +@conceal ; captures that are only meant to be concealed +``` + +```query +@spell ; for defining regions to be spellchecked +@nospell ; for defining regions that should NOT be spellchecked +``` + +The main types of nodes which are spell checked are: +- Comments +- Strings; where it makes sense. Strings that have interpolation or are typically used for non text purposes are not spell checked (e.g. bash). + +#### Predicates + +Captures can be restricted according to node contents using [predicates](https://neovim.io/doc/user/treesitter.html#treesitter-predicates). For performance reasons, prefer earlier predicates in this list: + +1. `#eq?` (literal match) +2. `#any-of?` (one of several literal matches) +3. `#lua-match?` (match against a [Lua pattern](https://neovim.io/doc/user/luaref.html#lua-pattern)) +4. `#match?`/`#vim-match?` (match against a [Vim regular expression](https://neovim.io/doc/user/pattern.html#regexp) + +#### Conceal + +Captures can be concealed by setting the [`conceal` metadata](https://neovim.io/doc/user/treesitter.html#treesitter-highlight-conceal), e.g.., +```query + (fenced_code_block_delimiter @markup.raw.block (#set! conceal "")) +``` +The capture should be meaningful to allow proper highlighting when `set conceallevel=0`. If the unconcealed capture should not be highlighted (e.g., because an earlier pattern handles this), you can use `@conceal`. + +A conceal can be restricted to part of the capture via the [`#offset!` directive](https://neovim.io/doc/user/treesitter.html#treesitter-directive-offset%21). + +#### Priority + +Captures can be assigned a priority to control precedence of highlights via the +`#set! "priority" ` directive (see `:h treesitter-highlight-priority`). +The default priority for treesitter highlights is `100`; queries should only +set priorities between `90` and `120`, to avoid conflict with other sources of +highlighting (such as diagnostics or LSP semantic tokens). + +### Locals + +Locals are used to keep track of definitions and references in local or global +scopes, see [upstream +documentation](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#local-variables). +Note that nvim-treesitter uses more specific subcaptures for definitions and +**does not use locals for highlighting**. + +```query +@local.definition ; various definitions +@local.definition.constant ; constants +@local.definition.function ; functions +@local.definition.method ; methods +@local.definition.var ; variables +@local.definition.parameter ; parameters +@local.definition.macro ; preprocessor macros +@local.definition.type ; types or classes +@local.definition.field ; fields or properties +@local.definition.enum ; enumerations +@local.definition.namespace ; modules or namespaces +@local.definition.import ; imported names +@local.definition.associated ; the associated type of a variable + +@local.scope ; scope block +@local.reference ; identifier reference +``` + +#### Definition Scope + +You can set the scope of a definition by setting the `scope` property on the definition. + +For example, a JavaScript function declaration creates a scope. The function name is captured as the definition. +This means that the function definition would only be available WITHIN the scope of the function, which is not the case. +The definition can be used in the scope the function was defined in. + +```javascript +function doSomething() {} + +doSomething(); // Should point to the declaration as the definition +``` + +```query +(function_declaration + ((identifier) @local.definition.var) + (#set! "definition.var.scope" "parent")) +``` + +Possible scope values are: + +- `parent`: The definition is valid in the containing scope and one more scope above that scope +- `global`: The definition is valid in the root scope +- `local`: The definition is valid in the containing scope. This is the default behavior + +### Folds + +You can define folds for a given language by adding a `folds.scm` query : + +```query +@fold ; fold this node +``` + +If the `folds.scm` query is not present, this will fall back to the `@local.scope` captures in the `locals` +query. + +### Injections + +Some captures are related to language injection (like markdown code blocks). They are used in `injections.scm`. + +If you want to dynamically detect the language (e.g. for Markdown blocks) use the `@injection.language` to capture +the node describing the language and `@injection.content` to describe the injection region. + +```query +@injection.language ; dynamic detection of the injection language (i.e. the text of the captured node describes the language) +@injection.content ; region for the dynamically detected language +``` + +For example, to inject javascript into HTML's ` +``` + +```query +(script_element + (raw_text) @injection.content + (#set! injection.language "javascript")) ; set the parser language for @injection.content region to javascript +``` + +For regions that don't have a corresponding `@injection.language`, you need to manually set the language +through `(#set injection.language "lang_name")` + +To combine all matches of a pattern as one single block of content, add `(#set! injection.combined)` to such pattern + +### Indents + +```query +@indent.begin ; indent children when matching this node +@indent.end ; marks the end of indented block +@indent.align ; behaves like python aligned/hanging indent +@indent.dedent ; dedent children when matching this node +@indent.branch ; dedent itself when matching this node +@indent.ignore ; do not indent in this node +@indent.auto ; behaves like 'autoindent' buffer option +@indent.zero ; sets this node at position 0 (no indent) +``` + +[Matrix channel]: https://matrix.to/#/#nvim-treesitter:matrix.org diff --git a/config/neovim/store/nvim-treesitter/LICENSE b/config/neovim/store/nvim-treesitter/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/config/neovim/store/nvim-treesitter/Makefile b/config/neovim/store/nvim-treesitter/Makefile new file mode 100644 index 00000000..338c7546 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/Makefile @@ -0,0 +1,7 @@ +# https://github.com/luarocks/luarocks/wiki/Creating-a-Makefile-that-plays-nice-with-LuaRocks +build: + echo "Do nothing" + +install: + mkdir -p $(INST_LUADIR) + cp -r lua/* $(INST_LUADIR) diff --git a/config/neovim/store/nvim-treesitter/README.md b/config/neovim/store/nvim-treesitter/README.md new file mode 100644 index 00000000..6887b6f7 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/README.md @@ -0,0 +1,810 @@ +
+

nvim-treesitter

+

+ + Matrix Chat + + + Linting and Style + + + Syntax files + +

+
+ +
+

+ Logo +

+

+ Treesitter + configurations and abstraction layer for + Neovim. +

+

+ + Logo by @steelsojka + +

+
+ +The goal of `nvim-treesitter` is both to provide a simple and easy way to use the interface for [tree-sitter](https://github.com/tree-sitter/tree-sitter) in Neovim and to provide some basic functionality such as highlighting based on it: + +![example-cpp](https://user-images.githubusercontent.com/2361214/202753610-e923bf4e-e88f-494b-bb1e-d22a7688446f.png) + +Traditional highlighting (left) vs Treesitter-based highlighting (right). +More examples can be found in [our gallery](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Gallery). + +**Warning: Treesitter and nvim-treesitter highlighting are an experimental feature of Neovim. +Please consider the experience with this plug-in as experimental until Tree-Sitter support in Neovim is stable! +We recommend using the nightly builds of Neovim if possible. +You can find the current roadmap [here](https://github.com/nvim-treesitter/nvim-treesitter/issues/4767). +The roadmap and all features of this plugin are open to change, and any suggestion will be highly appreciated!** + +Nvim-treesitter is based on three interlocking features: [**language parsers**](#language-parsers), [**queries**](#adding-queries), and [**modules**](#available-modules), where _modules_ provide features – e.g., highlighting – based on _queries_ for syntax objects extracted from a given buffer by _language parsers_. +Users will generally only need to interact with parsers and modules as explained in the next section. +For more detailed information on setting these up, see ["Advanced setup"](#advanced-setup). + +--- + +### Table of contents + +- [Quickstart](#quickstart) +- [Supported languages](#supported-languages) +- [Available modules](#available-modules) +- [Advanced setup](#advanced-setup) +- [Extra features](#extra-features) +- [Troubleshooting](#troubleshooting) + +--- + +# Quickstart + +## Requirements + +- **Neovim 0.9.2** or later ([nightly](https://github.com/neovim/neovim#install-from-source) recommended) +- `tar` and `curl` in your path (or alternatively `git`) +- A C compiler in your path and libstdc++ installed ([Windows users please read this!](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Windows-support)). + +## Installation + +You can install `nvim-treesitter` with your favorite package manager (or using the native `package` feature of vim, see `:h packages`). + +**NOTE: This plugin is only guaranteed to work with specific versions of language parsers** (as specified in the `lockfile.json`). **When upgrading the plugin, you must make sure that all installed parsers are updated to the latest version** via `:TSUpdate`. +It is strongly recommended to automate this; e.g., if you are using [vim-plug](https://github.com/junegunn/vim-plug), put this in your `init.vim` file: + +```vim +Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'} +``` + +For other plugin managers such as `packer.nvim`, see this [Installation page from the wiki](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Installation) (Note that this page is community maintained). + +## Language parsers + +Treesitter uses a different _parser_ for every language, which needs to be generated via `tree-sitter-cli` from a `grammar.js` file, then compiled to a `.so` library that needs to be placed in neovim's `runtimepath` (typically under `parser/{language}.so`). +To simplify this, `nvim-treesitter` provides commands to automate this process. +If the language is already [supported by `nvim-treesitter`](#supported-languages), you can install it with + +```vim +:TSInstall +``` + +This command supports tab expansion. +You can also get a list of all available languages and their installation status with `:TSInstallInfo`. +Parsers not on this list can be added manually by following the steps described under ["Adding parsers"](#adding-parsers) below. + +To make sure a parser is at the latest compatible version (as specified in `nvim-treesitter`'s `lockfile.json`), use `:TSUpdate {language}`. To update all parsers unconditionally, use `:TSUpdate all` or just `:TSUpdate`. + +## Modules + +Each module provides a distinct tree-sitter-based feature such as [highlighting](#highlight), [indentation](#indentation), or [folding](#folding); see [`:h nvim-treesitter-modules`](doc/nvim-treesitter.txt) or ["Available modules"](#available-modules) below for a list of modules and their options. + +Following examples assume that you are configuring neovim with lua. If you are using vimscript, see `:h lua-heredoc`. +All modules are disabled by default and need to be activated explicitly in your `init.lua`, e.g., via + +```lua +require'nvim-treesitter.configs'.setup { + -- A list of parser names, or "all" (the five listed parsers should always be installed) + ensure_installed = { "c", "lua", "vim", "vimdoc", "query" }, + + -- Install parsers synchronously (only applied to `ensure_installed`) + sync_install = false, + + -- Automatically install missing parsers when entering buffer + -- Recommendation: set to false if you don't have `tree-sitter` CLI installed locally + auto_install = true, + + -- List of parsers to ignore installing (or "all") + ignore_install = { "javascript" }, + + ---- If you need to change the installation directory of the parsers (see -> Advanced Setup) + -- parser_install_dir = "/some/path/to/store/parsers", -- Remember to run vim.opt.runtimepath:append("/some/path/to/store/parsers")! + + highlight = { + enable = true, + + -- NOTE: these are the names of the parsers and not the filetype. (for example if you want to + -- disable highlighting for the `tex` filetype, you need to include `latex` in this list as this is + -- the name of the parser) + -- list of language that will be disabled + disable = { "c", "rust" }, + -- Or use a function for more flexibility, e.g. to disable slow treesitter highlight for large files + disable = function(lang, buf) + local max_filesize = 100 * 1024 -- 100 KB + local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf)) + if ok and stats and stats.size > max_filesize then + return true + end + end, + + -- Setting this to true will run `:h syntax` and tree-sitter at the same time. + -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation). + -- Using this option may slow down your editor, and you may see some duplicate highlights. + -- Instead of true it can also be a list of languages + additional_vim_regex_highlighting = false, + }, +} +``` + +Each module can also be enabled or disabled interactively through the following commands: + +```vim +:TSBufEnable {module} " enable module on current buffer +:TSBufDisable {module} " disable module on current buffer +:TSEnable {module} [{ft}] " enable module on every buffer. If filetype is specified, enable only for this filetype. +:TSDisable {module} [{ft}] " disable module on every buffer. If filetype is specified, disable only for this filetype. +:TSModuleInfo [{module}] " list information about modules state for each filetype +``` + +Check [`:h nvim-treesitter-commands`](doc/nvim-treesitter.txt) for a list of all available commands. +It may be necessary to reload the buffer (e.g., via `:e`) after enabling a module interactively. + +# Supported languages + +For `nvim-treesitter` to support a specific feature for a specific language requires both a parser for that language and an appropriate language-specific query file for that feature. + +The following is a list of languages for which a parser can be installed through `:TSInstall`; a checked box means that `nvim-treesitter` also contains queries at least for the `highlight` module. + +Experimental parsers are parsers that have a maintainer but are not stable enough for +daily use yet. + +We are looking for maintainers to add more parsers and to write query files for their languages. Check our [tracking issue](https://github.com/nvim-treesitter/nvim-treesitter/issues/2282) for open language requests. + + + +- [x] [ada](https://github.com/briot/tree-sitter-ada) (maintained by @briot) +- [x] [agda](https://github.com/tree-sitter/tree-sitter-agda) (maintained by @Decodetalkers) +- [x] [angular](https://github.com/dlvandenberg/tree-sitter-angular) (experimental, maintained by @dlvandenberg) +- [x] [apex](https://github.com/aheber/tree-sitter-sfapex) (maintained by @aheber) +- [x] [arduino](https://github.com/ObserverOfTime/tree-sitter-arduino) (maintained by @ObserverOfTime) +- [x] [asm](https://github.com/RubixDev/tree-sitter-asm) (maintained by @RubixDev) +- [x] [astro](https://github.com/virchau13/tree-sitter-astro) (maintained by @virchau13) +- [x] [authzed](https://github.com/mleonidas/tree-sitter-authzed) (maintained by @mattpolzin) +- [ ] [awk](https://github.com/Beaglefoot/tree-sitter-awk) +- [x] [bash](https://github.com/tree-sitter/tree-sitter-bash) (maintained by @TravonteD) +- [x] [bass](https://github.com/vito/tree-sitter-bass) (maintained by @amaanq) +- [x] [beancount](https://github.com/polarmutex/tree-sitter-beancount) (maintained by @polarmutex) +- [x] [bibtex](https://github.com/latex-lsp/tree-sitter-bibtex) (maintained by @theHamsta, @clason) +- [x] [bicep](https://github.com/amaanq/tree-sitter-bicep) (maintained by @amaanq) +- [x] [bitbake](https://github.com/amaanq/tree-sitter-bitbake) (maintained by @amaanq) +- [x] [blueprint](https://gitlab.com/gabmus/tree-sitter-blueprint.git) (experimental, maintained by @gabmus) +- [x] [bp](https://github.com/ambroisie/tree-sitter-bp) (maintained by @ambroisie) +- [x] [c](https://github.com/tree-sitter/tree-sitter-c) (maintained by @amaanq) +- [x] [c_sharp](https://github.com/tree-sitter/tree-sitter-c-sharp) (maintained by @amaanq) +- [x] [cairo](https://github.com/amaanq/tree-sitter-cairo) (maintained by @amaanq) +- [x] [capnp](https://github.com/amaanq/tree-sitter-capnp) (maintained by @amaanq) +- [x] [chatito](https://github.com/ObserverOfTime/tree-sitter-chatito) (maintained by @ObserverOfTime) +- [x] [clojure](https://github.com/sogaiu/tree-sitter-clojure) (maintained by @NoahTheDuke) +- [x] [cmake](https://github.com/uyha/tree-sitter-cmake) (maintained by @uyha) +- [x] [comment](https://github.com/stsewd/tree-sitter-comment) (maintained by @stsewd) +- [x] [commonlisp](https://github.com/theHamsta/tree-sitter-commonlisp) (maintained by @theHamsta) +- [x] [cooklang](https://github.com/addcninblue/tree-sitter-cooklang) (maintained by @addcninblue) +- [x] [corn](https://github.com/jakestanger/tree-sitter-corn) (maintained by @jakestanger) +- [x] [cpon](https://github.com/amaanq/tree-sitter-cpon) (maintained by @amaanq) +- [x] [cpp](https://github.com/tree-sitter/tree-sitter-cpp) (maintained by @theHamsta) +- [x] [css](https://github.com/tree-sitter/tree-sitter-css) (maintained by @TravonteD) +- [x] [csv](https://github.com/amaanq/tree-sitter-csv) (maintained by @amaanq) +- [x] [cuda](https://github.com/theHamsta/tree-sitter-cuda) (maintained by @theHamsta) +- [x] [cue](https://github.com/eonpatapon/tree-sitter-cue) (maintained by @amaanq) +- [x] [d](https://github.com/gdamore/tree-sitter-d) (maintained by @amaanq) +- [x] [dart](https://github.com/UserNobody14/tree-sitter-dart) (maintained by @akinsho) +- [x] [devicetree](https://github.com/joelspadin/tree-sitter-devicetree) (maintained by @jedrzejboczar) +- [x] [dhall](https://github.com/jbellerb/tree-sitter-dhall) (maintained by @amaanq) +- [x] [diff](https://github.com/the-mikedavis/tree-sitter-diff) (maintained by @gbprod) +- [x] [disassembly](https://github.com/ColinKennedy/tree-sitter-disassembly) (maintained by @ColinKennedy) +- [x] [djot](https://github.com/treeman/tree-sitter-djot) (maintained by @NoahTheDuke) +- [x] [dockerfile](https://github.com/camdencheek/tree-sitter-dockerfile) (maintained by @camdencheek) +- [x] [dot](https://github.com/rydesun/tree-sitter-dot) (maintained by @rydesun) +- [x] [doxygen](https://github.com/amaanq/tree-sitter-doxygen) (maintained by @amaanq) +- [x] [dtd](https://github.com/tree-sitter-grammars/tree-sitter-xml) (maintained by @ObserverOfTime) +- [x] [earthfile](https://github.com/glehmann/tree-sitter-earthfile) (maintained by @glehmann) +- [x] [ebnf](https://github.com/RubixDev/ebnf) (experimental, maintained by @RubixDev) +- [x] [eds](https://github.com/uyha/tree-sitter-eds) (maintained by @uyha) +- [x] [eex](https://github.com/connorlay/tree-sitter-eex) (maintained by @connorlay) +- [x] [elixir](https://github.com/elixir-lang/tree-sitter-elixir) (maintained by @connorlay) +- [x] [elm](https://github.com/elm-tooling/tree-sitter-elm) (maintained by @zweimach) +- [x] [elsa](https://github.com/glapa-grossklag/tree-sitter-elsa) (maintained by @glapa-grossklag, @amaanq) +- [x] [elvish](https://github.com/elves/tree-sitter-elvish) (maintained by @elves) +- [ ] [embedded_template](https://github.com/tree-sitter/tree-sitter-embedded-template) +- [x] [erlang](https://github.com/WhatsApp/tree-sitter-erlang) (maintained by @filmor) +- [x] [facility](https://github.com/FacilityApi/tree-sitter-facility) (maintained by @bryankenote) +- [x] [faust](https://github.com/khiner/tree-sitter-faust) (maintained by @khiner) +- [x] [fennel](https://github.com/alexmozaidze/tree-sitter-fennel) (maintained by @alexmozaidze) +- [x] [fidl](https://github.com/google/tree-sitter-fidl) (maintained by @chaopeng) +- [x] [firrtl](https://github.com/amaanq/tree-sitter-firrtl) (maintained by @amaanq) +- [x] [fish](https://github.com/ram02z/tree-sitter-fish) (maintained by @ram02z) +- [x] [foam](https://github.com/FoamScience/tree-sitter-foam) (experimental, maintained by @FoamScience) +- [x] [forth](https://github.com/AlexanderBrevig/tree-sitter-forth) (maintained by @amaanq) +- [x] [fortran](https://github.com/stadelmanma/tree-sitter-fortran) (maintained by @amaanq) +- [x] [fsh](https://github.com/mgramigna/tree-sitter-fsh) (maintained by @mgramigna) +- [x] [func](https://github.com/amaanq/tree-sitter-func) (maintained by @amaanq) +- [x] [fusion](https://gitlab.com/jirgn/tree-sitter-fusion.git) (maintained by @jirgn) +- [x] [Godot (gdscript)](https://github.com/PrestonKnopp/tree-sitter-gdscript) (maintained by @PrestonKnopp) +- [x] [gdshader](https://github.com/GodOfAvacyn/tree-sitter-gdshader) (maintained by @godofavacyn) +- [x] [git_config](https://github.com/the-mikedavis/tree-sitter-git-config) (maintained by @amaanq) +- [x] [git_rebase](https://github.com/the-mikedavis/tree-sitter-git-rebase) (maintained by @gbprod) +- [x] [gitattributes](https://github.com/ObserverOfTime/tree-sitter-gitattributes) (maintained by @ObserverOfTime) +- [x] [gitcommit](https://github.com/gbprod/tree-sitter-gitcommit) (maintained by @gbprod) +- [x] [gitignore](https://github.com/shunsambongi/tree-sitter-gitignore) (maintained by @theHamsta) +- [x] [gleam](https://github.com/gleam-lang/tree-sitter-gleam) (maintained by @amaanq) +- [x] [Glimmer and Ember](https://github.com/alexlafroscia/tree-sitter-glimmer) (maintained by @NullVoxPopuli) +- [x] [glsl](https://github.com/theHamsta/tree-sitter-glsl) (maintained by @theHamsta) +- [x] [GN (Generate Ninja)](https://github.com/amaanq/tree-sitter-gn) (maintained by @amaanq) +- [x] [gnuplot](https://github.com/dpezto/tree-sitter-gnuplot) (maintained by @dpezto) +- [x] [go](https://github.com/tree-sitter/tree-sitter-go) (maintained by @theHamsta, @WinWisely268) +- [x] [Godot Resources (gdresource)](https://github.com/PrestonKnopp/tree-sitter-godot-resource) (maintained by @pierpo) +- [x] [gomod](https://github.com/camdencheek/tree-sitter-go-mod) (maintained by @camdencheek) +- [x] [gosum](https://github.com/amaanq/tree-sitter-go-sum) (maintained by @amaanq) +- [x] [gotmpl](https://github.com/ngalaiko/tree-sitter-go-template) (maintained by @qvalentin) +- [x] [gowork](https://github.com/omertuc/tree-sitter-go-work) (maintained by @omertuc) +- [x] [gpg](https://github.com/ObserverOfTime/tree-sitter-gpg-config) (maintained by @ObserverOfTime) +- [x] [graphql](https://github.com/bkegley/tree-sitter-graphql) (maintained by @bkegley) +- [x] [groovy](https://github.com/murtaza64/tree-sitter-groovy) (maintained by @murtaza64) +- [x] [gstlaunch](https://github.com/theHamsta/tree-sitter-gstlaunch) (maintained by @theHamsta) +- [ ] [hack](https://github.com/slackhq/tree-sitter-hack) +- [x] [hare](https://github.com/amaanq/tree-sitter-hare) (maintained by @amaanq) +- [x] [haskell](https://github.com/tree-sitter/tree-sitter-haskell) (maintained by @mrcjkb) +- [x] [haskell_persistent](https://github.com/MercuryTechnologies/tree-sitter-haskell-persistent) (maintained by @lykahb) +- [x] [hcl](https://github.com/MichaHoffmann/tree-sitter-hcl) (maintained by @MichaHoffmann) +- [x] [heex](https://github.com/connorlay/tree-sitter-heex) (maintained by @connorlay) +- [x] [helm](https://github.com/ngalaiko/tree-sitter-go-template) (maintained by @qvalentin) +- [x] [hjson](https://github.com/winston0410/tree-sitter-hjson) (maintained by @winston0410) +- [x] [hlsl](https://github.com/theHamsta/tree-sitter-hlsl) (maintained by @theHamsta) +- [x] [hlsplaylist](https://github.com/Freed-Wu/tree-sitter-hlsplaylist) (maintained by @Freed-Wu) +- [x] [hocon](https://github.com/antosha417/tree-sitter-hocon) (maintained by @antosha417) +- [x] [hoon](https://github.com/urbit-pilled/tree-sitter-hoon) (experimental, maintained by @urbit-pilled) +- [x] [html](https://github.com/tree-sitter/tree-sitter-html) (maintained by @TravonteD) +- [x] [htmldjango](https://github.com/interdependence/tree-sitter-htmldjango) (experimental, maintained by @ObserverOfTime) +- [x] [http](https://github.com/rest-nvim/tree-sitter-http) (maintained by @amaanq, @NTBBloodbath) +- [x] [hurl](https://github.com/pfeiferj/tree-sitter-hurl) (maintained by @pfeiferj) +- [x] [hyprlang](https://github.com/luckasRanarison/tree-sitter-hyprlang) (maintained by @luckasRanarison) +- [x] [idl](https://github.com/cathaysia/tree-sitter-idl) (maintained by @cathaysa) +- [x] [ini](https://github.com/justinmk/tree-sitter-ini) (experimental, maintained by @theHamsta) +- [x] [inko](https://github.com/inko-lang/tree-sitter-inko) (maintained by @yorickpeterse) +- [x] [ispc](https://github.com/fab4100/tree-sitter-ispc) (maintained by @fab4100) +- [x] [janet_simple](https://github.com/sogaiu/tree-sitter-janet-simple) (maintained by @sogaiu) +- [x] [java](https://github.com/tree-sitter/tree-sitter-java) (maintained by @p00f) +- [x] [javascript](https://github.com/tree-sitter/tree-sitter-javascript) (maintained by @steelsojka) +- [x] [jq](https://github.com/flurie/tree-sitter-jq) (maintained by @ObserverOfTime) +- [x] [jsdoc](https://github.com/tree-sitter/tree-sitter-jsdoc) (maintained by @steelsojka) +- [x] [json](https://github.com/tree-sitter/tree-sitter-json) (maintained by @steelsojka) +- [x] [json5](https://github.com/Joakker/tree-sitter-json5) (maintained by @Joakker) +- [x] [JSON with comments](https://gitlab.com/WhyNotHugo/tree-sitter-jsonc.git) (maintained by @WhyNotHugo) +- [x] [jsonnet](https://github.com/sourcegraph/tree-sitter-jsonnet) (maintained by @nawordar) +- [x] [julia](https://github.com/tree-sitter/tree-sitter-julia) (maintained by @theHamsta) +- [x] [just](https://github.com/IndianBoy42/tree-sitter-just) (maintained by @Hubro) +- [x] [kconfig](https://github.com/amaanq/tree-sitter-kconfig) (maintained by @amaanq) +- [x] [kdl](https://github.com/amaanq/tree-sitter-kdl) (maintained by @amaanq) +- [x] [kotlin](https://github.com/fwcd/tree-sitter-kotlin) (maintained by @SalBakraa) +- [x] [koto](https://github.com/koto-lang/tree-sitter-koto) (maintained by @irh) +- [x] [kusto](https://github.com/Willem-J-an/tree-sitter-kusto) (maintained by @Willem-J-an) +- [x] [lalrpop](https://github.com/traxys/tree-sitter-lalrpop) (maintained by @traxys) +- [x] [latex](https://github.com/latex-lsp/tree-sitter-latex) (maintained by @theHamsta, @clason) +- [x] [ledger](https://github.com/cbarrete/tree-sitter-ledger) (maintained by @cbarrete) +- [x] [leo](https://github.com/r001/tree-sitter-leo) (maintained by @r001) +- [x] [linkerscript](https://github.com/amaanq/tree-sitter-linkerscript) (maintained by @amaanq) +- [x] [liquid](https://github.com/hankthetank27/tree-sitter-liquid) (maintained by @hankthetank27) +- [x] [liquidsoap](https://github.com/savonet/tree-sitter-liquidsoap) (maintained by @toots) +- [x] [llvm](https://github.com/benwilliamgraham/tree-sitter-llvm) (maintained by @benwilliamgraham) +- [x] [lua](https://github.com/MunifTanjim/tree-sitter-lua) (maintained by @muniftanjim) +- [x] [luadoc](https://github.com/amaanq/tree-sitter-luadoc) (maintained by @amaanq) +- [x] [lua patterns](https://github.com/amaanq/tree-sitter-luap) (maintained by @amaanq) +- [x] [luau](https://github.com/amaanq/tree-sitter-luau) (maintained by @amaanq) +- [x] [m68k](https://github.com/grahambates/tree-sitter-m68k) (maintained by @grahambates) +- [x] [make](https://github.com/alemuller/tree-sitter-make) (maintained by @lewis6991) +- [x] [markdown (basic highlighting)](https://github.com/MDeiml/tree-sitter-markdown) (experimental, maintained by @MDeiml) +- [x] [markdown_inline (needed for full highlighting)](https://github.com/MDeiml/tree-sitter-markdown) (experimental, maintained by @MDeiml) +- [x] [matlab](https://github.com/acristoffers/tree-sitter-matlab) (maintained by @acristoffers) +- [x] [menhir](https://github.com/Kerl13/tree-sitter-menhir) (maintained by @Kerl13) +- [ ] [mermaid](https://github.com/monaqa/tree-sitter-mermaid) (experimental) +- [x] [meson](https://github.com/Decodetalkers/tree-sitter-meson) (maintained by @Decodetalkers) +- [x] [mlir](https://github.com/artagnon/tree-sitter-mlir) (experimental, maintained by @artagnon) +- [x] [muttrc](https://github.com/neomutt/tree-sitter-muttrc) (maintained by @Freed-Wu) +- [x] [nasm](https://github.com/naclsn/tree-sitter-nasm) (maintained by @ObserverOfTime) +- [ ] [nickel](https://github.com/nickel-lang/tree-sitter-nickel) +- [x] [nim](https://github.com/alaviss/tree-sitter-nim) (maintained by @aMOPel) +- [x] [nim_format_string](https://github.com/aMOPel/tree-sitter-nim-format-string) (maintained by @aMOPel) +- [x] [ninja](https://github.com/alemuller/tree-sitter-ninja) (maintained by @alemuller) +- [x] [nix](https://github.com/cstrahan/tree-sitter-nix) (maintained by @leo60228) +- [x] [norg](https://github.com/nvim-neorg/tree-sitter-norg) (maintained by @JoeyGrajciar, @vhyrro) +- [x] [nqc](https://github.com/amaanq/tree-sitter-nqc) (maintained by @amaanq) +- [x] [objc](https://github.com/amaanq/tree-sitter-objc) (maintained by @amaanq) +- [x] [objdump](https://github.com/ColinKennedy/tree-sitter-objdump) (maintained by @ColinKennedy) +- [x] [ocaml](https://github.com/tree-sitter/tree-sitter-ocaml) (maintained by @undu) +- [x] [ocaml_interface](https://github.com/tree-sitter/tree-sitter-ocaml) (maintained by @undu) +- [x] [ocamllex](https://github.com/atom-ocaml/tree-sitter-ocamllex) (maintained by @undu) +- [x] [odin](https://github.com/amaanq/tree-sitter-odin) (maintained by @amaanq) +- [ ] [org](https://github.com/milisims/tree-sitter-org) +- [x] [pascal](https://github.com/Isopod/tree-sitter-pascal.git) (maintained by @Isopod) +- [x] [passwd](https://github.com/ath3/tree-sitter-passwd) (maintained by @amaanq) +- [x] [pem](https://github.com/ObserverOfTime/tree-sitter-pem) (maintained by @ObserverOfTime) +- [x] [perl](https://github.com/tree-sitter-perl/tree-sitter-perl) (maintained by @RabbiVeesh, @LeoNerd) +- [x] [php](https://github.com/tree-sitter/tree-sitter-php) (maintained by @tk-shirasaka) +- [x] [php_only](https://github.com/tree-sitter/tree-sitter-php) (maintained by @tk-shirasaka) +- [x] [phpdoc](https://github.com/claytonrcarter/tree-sitter-phpdoc) (experimental, maintained by @mikehaertl) +- [x] [pioasm](https://github.com/leo60228/tree-sitter-pioasm) (maintained by @leo60228) +- [x] [po](https://github.com/erasin/tree-sitter-po) (maintained by @amaanq) +- [x] [pod](https://github.com/tree-sitter-perl/tree-sitter-pod) (maintained by @RabbiVeesh, @LeoNerd) +- [x] [Path of Exile item filter](https://github.com/ObserverOfTime/tree-sitter-poe-filter) (experimental, maintained by @ObserverOfTime) +- [x] [pony](https://github.com/amaanq/tree-sitter-pony) (maintained by @amaanq, @mfelsche) +- [x] [printf](https://github.com/ObserverOfTime/tree-sitter-printf) (maintained by @ObserverOfTime) +- [x] [prisma](https://github.com/victorhqc/tree-sitter-prisma) (maintained by @elianiva) +- [x] [promql](https://github.com/MichaHoffmann/tree-sitter-promql) (maintained by @MichaHoffmann) +- [x] [properties](https://github.com/tree-sitter-grammars/tree-sitter-properties) (maintained by @ObserverOfTime) +- [x] [proto](https://github.com/treywood/tree-sitter-proto) (maintained by @treywood) +- [x] [prql](https://github.com/PRQL/tree-sitter-prql) (maintained by @matthias-Q) +- [x] [psv](https://github.com/amaanq/tree-sitter-csv) (maintained by @amaanq) +- [x] [pug](https://github.com/zealot128/tree-sitter-pug) (experimental, maintained by @zealot128) +- [x] [puppet](https://github.com/amaanq/tree-sitter-puppet) (maintained by @amaanq) +- [x] [purescript](https://github.com/postsolar/tree-sitter-purescript) (maintained by @postsolar) +- [x] [PyPA manifest](https://github.com/ObserverOfTime/tree-sitter-pymanifest) (maintained by @ObserverOfTime) +- [x] [python](https://github.com/tree-sitter/tree-sitter-python) (maintained by @stsewd, @theHamsta) +- [x] [ql](https://github.com/tree-sitter/tree-sitter-ql) (maintained by @pwntester) +- [x] [qmldir](https://github.com/Decodetalkers/tree-sitter-qmldir) (maintained by @amaanq) +- [x] [qmljs](https://github.com/yuja/tree-sitter-qmljs) (maintained by @Decodetalkers) +- [x] [Tree-Sitter query language](https://github.com/nvim-treesitter/tree-sitter-query) (maintained by @steelsojka) +- [x] [r](https://github.com/r-lib/tree-sitter-r) (maintained by @echasnovski) +- [ ] [racket](https://github.com/6cdh/tree-sitter-racket) +- [x] [rasi](https://github.com/Fymyte/tree-sitter-rasi) (maintained by @Fymyte) +- [x] [rbs](https://github.com/joker1007/tree-sitter-rbs) (maintained by @joker1007) +- [x] [re2c](https://github.com/amaanq/tree-sitter-re2c) (maintained by @amaanq) +- [x] [readline](https://github.com/ribru17/tree-sitter-readline) (maintained by @ribru17) +- [x] [regex](https://github.com/tree-sitter/tree-sitter-regex) (maintained by @theHamsta) +- [x] [rego](https://github.com/FallenAngel97/tree-sitter-rego) (maintained by @FallenAngel97) +- [x] [pip requirements](https://github.com/ObserverOfTime/tree-sitter-requirements) (maintained by @ObserverOfTime) +- [x] [rnoweb](https://github.com/bamonroe/tree-sitter-rnoweb) (maintained by @bamonroe) +- [x] [robot](https://github.com/Hubro/tree-sitter-robot) (maintained by @Hubro) +- [x] [roc](https://github.com/nat-418/tree-sitter-roc) (maintained by @nat-418) +- [x] [ron](https://github.com/amaanq/tree-sitter-ron) (maintained by @amaanq) +- [x] [rst](https://github.com/stsewd/tree-sitter-rst) (maintained by @stsewd) +- [x] [ruby](https://github.com/tree-sitter/tree-sitter-ruby) (maintained by @TravonteD) +- [x] [rust](https://github.com/tree-sitter/tree-sitter-rust) (maintained by @amaanq) +- [x] [scala](https://github.com/tree-sitter/tree-sitter-scala) (maintained by @stevanmilic) +- [x] [scfg](https://git.sr.ht/~rockorager/tree-sitter-scfg) (maintained by @WhyNotHugo) +- [ ] [scheme](https://github.com/6cdh/tree-sitter-scheme) +- [x] [scss](https://github.com/serenadeai/tree-sitter-scss) (maintained by @elianiva) +- [x] [slang](https://github.com/theHamsta/tree-sitter-slang) (experimental, maintained by @theHamsta) +- [x] [slint](https://github.com/slint-ui/tree-sitter-slint) (maintained by @hunger) +- [x] [smali](https://github.com/tree-sitter-grammars/tree-sitter-smali) (maintained by @amaanq) +- [x] [smithy](https://github.com/indoorvivants/tree-sitter-smithy) (maintained by @amaanq, @keynmol) +- [ ] [snakemake](https://github.com/osthomas/tree-sitter-snakemake) (experimental) +- [x] [solidity](https://github.com/JoranHonig/tree-sitter-solidity) (maintained by @amaanq) +- [x] [soql](https://github.com/aheber/tree-sitter-sfapex) (maintained by @aheber) +- [x] [sosl](https://github.com/aheber/tree-sitter-sfapex) (maintained by @aheber) +- [x] [sourcepawn](https://github.com/nilshelmig/tree-sitter-sourcepawn) (maintained by @Sarrus1) +- [x] [sparql](https://github.com/BonaBeavis/tree-sitter-sparql) (maintained by @BonaBeavis) +- [x] [sql](https://github.com/derekstride/tree-sitter-sql) (maintained by @derekstride) +- [x] [squirrel](https://github.com/amaanq/tree-sitter-squirrel) (maintained by @amaanq) +- [x] [ssh_config](https://github.com/ObserverOfTime/tree-sitter-ssh-config) (maintained by @ObserverOfTime) +- [x] [starlark](https://github.com/amaanq/tree-sitter-starlark) (maintained by @amaanq) +- [x] [strace](https://github.com/sigmaSd/tree-sitter-strace) (maintained by @amaanq) +- [x] [styled](https://github.com/mskelton/tree-sitter-styled) (maintained by @mskelton) +- [x] [supercollider](https://github.com/madskjeldgaard/tree-sitter-supercollider) (maintained by @madskjeldgaard) +- [x] [surface](https://github.com/connorlay/tree-sitter-surface) (maintained by @connorlay) +- [x] [svelte](https://github.com/tree-sitter-grammars/tree-sitter-svelte) (maintained by @amaanq) +- [x] [swift](https://github.com/alex-pinkus/tree-sitter-swift) (maintained by @alex-pinkus) +- [x] [sxhkdrc](https://github.com/RaafatTurki/tree-sitter-sxhkdrc) (maintained by @RaafatTurki) +- [x] [systemtap](https://github.com/ok-ryoko/tree-sitter-systemtap) (maintained by @ok-ryoko) +- [x] [t32](https://gitlab.com/xasc/tree-sitter-t32.git) (maintained by @xasc) +- [x] [tablegen](https://github.com/amaanq/tree-sitter-tablegen) (maintained by @amaanq) +- [x] [tact](https://github.com/tact-lang/tree-sitter-tact) (maintained by @novusnota) +- [x] [tcl](https://github.com/tree-sitter-grammars/tree-sitter-tcl) (maintained by @lewis6991) +- [x] [teal](https://github.com/euclidianAce/tree-sitter-teal) (maintained by @euclidianAce) +- [x] [templ](https://github.com/vrischmann/tree-sitter-templ) (maintained by @vrischmann) +- [x] [terraform](https://github.com/MichaHoffmann/tree-sitter-hcl) (maintained by @MichaHoffmann) +- [x] [textproto](https://github.com/PorterAtGoogle/tree-sitter-textproto) (maintained by @Porter) +- [x] [thrift](https://github.com/duskmoon314/tree-sitter-thrift) (maintained by @amaanq, @duskmoon314) +- [x] [tiger](https://github.com/ambroisie/tree-sitter-tiger) (maintained by @ambroisie) +- [x] [tlaplus](https://github.com/tlaplus-community/tree-sitter-tlaplus) (maintained by @ahelwer, @susliko) +- [x] [tmux](https://github.com/Freed-Wu/tree-sitter-tmux) (maintained by @Freed-Wu) +- [x] [todotxt](https://github.com/arnarg/tree-sitter-todotxt.git) (experimental, maintained by @arnarg) +- [x] [toml](https://github.com/tree-sitter-grammars/tree-sitter-toml) (maintained by @tk-shirasaka) +- [x] [tsv](https://github.com/amaanq/tree-sitter-csv) (maintained by @amaanq) +- [x] [tsx](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka) +- [x] [turtle](https://github.com/BonaBeavis/tree-sitter-turtle) (maintained by @BonaBeavis) +- [x] [twig](https://github.com/gbprod/tree-sitter-twig) (maintained by @gbprod) +- [x] [typescript](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka) +- [x] [typespec](https://github.com/happenslol/tree-sitter-typespec) (maintained by @happenslol) +- [x] [typoscript](https://github.com/Teddytrombone/tree-sitter-typoscript) (maintained by @Teddytrombone) +- [x] [typst](https://github.com/uben0/tree-sitter-typst) (maintained by @uben0, @RaafatTurki) +- [x] [udev](https://github.com/ObserverOfTime/tree-sitter-udev) (maintained by @ObserverOfTime) +- [x] [ungrammar](https://github.com/Philipp-M/tree-sitter-ungrammar) (maintained by @Philipp-M, @amaanq) +- [x] [unison](https://github.com/kylegoetz/tree-sitter-unison) (maintained by @tapegram) +- [x] [usd](https://github.com/ColinKennedy/tree-sitter-usd) (maintained by @ColinKennedy) +- [x] [uxn tal](https://github.com/amaanq/tree-sitter-uxntal) (maintained by @amaanq) +- [x] [v](https://github.com/vlang/v-analyzer) (maintained by @kkharji, @amaanq) +- [x] [vala](https://github.com/vala-lang/tree-sitter-vala) (maintained by @Prince781) +- [x] [vento](https://github.com/ventojs/tree-sitter-vento) (maintained by @wrapperup, @oscarotero) +- [x] [verilog](https://github.com/tree-sitter/tree-sitter-verilog) (maintained by @zegervdv) +- [x] [vhs](https://github.com/charmbracelet/tree-sitter-vhs) (maintained by @caarlos0) +- [x] [vim](https://github.com/neovim/tree-sitter-vim) (maintained by @clason) +- [x] [vimdoc](https://github.com/neovim/tree-sitter-vimdoc) (maintained by @clason) +- [x] [vue](https://github.com/tree-sitter-grammars/tree-sitter-vue) (maintained by @WhyNotHugo, @lucario387) +- [x] [wgsl](https://github.com/szebniok/tree-sitter-wgsl) (maintained by @szebniok) +- [x] [wgsl_bevy](https://github.com/theHamsta/tree-sitter-wgsl-bevy) (maintained by @theHamsta) +- [x] [wing](https://github.com/winglang/tree-sitter-wing) (maintained by @gshpychka, @MarkMcCulloh) +- [x] [wit](https://github.com/liamwh/tree-sitter-wit) (maintained by @liamwh) +- [x] [xcompose](https://github.com/ObserverOfTime/tree-sitter-xcompose) (maintained by @ObserverOfTime) +- [x] [xml](https://github.com/tree-sitter-grammars/tree-sitter-xml) (maintained by @ObserverOfTime) +- [x] [yaml](https://github.com/tree-sitter-grammars/tree-sitter-yaml) (maintained by @amaanq) +- [x] [yang](https://github.com/Hubro/tree-sitter-yang) (maintained by @Hubro) +- [x] [yuck](https://github.com/Philipp-M/tree-sitter-yuck) (maintained by @Philipp-M, @amaanq) +- [x] [zathurarc](https://github.com/Freed-Wu/tree-sitter-zathurarc) (maintained by @Freed-Wu) +- [x] [zig](https://github.com/maxxnino/tree-sitter-zig) (maintained by @maxxnino) + + +For related information on the supported languages, including related plugins, see [this wiki page](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Supported-Languages-Information). + +# Available modules + +Modules provide the top-level features of `nvim-treesitter`. +The following is a list of modules included in `nvim-treesitter` and their configuration via `init.lua` (where multiple modules can be combined in a single call to `setup`). +Note that not all modules work for all languages (depending on the queries available for them). +Additional modules can be provided as [external plugins](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Extra-modules-and-plugins). + +#### Highlight + +Consistent syntax highlighting. + +```lua +require'nvim-treesitter.configs'.setup { + highlight = { + enable = true, + -- Setting this to true will run `:h syntax` and tree-sitter at the same time. + -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation). + -- Using this option may slow down your editor, and you may see some duplicate highlights. + -- Instead of true it can also be a list of languages + additional_vim_regex_highlighting = false, + }, +} +``` + +To customize the syntax highlighting of a capture, simply define or link a highlight group of the same name: + +```lua +-- Highlight the @foo.bar capture group with the "Identifier" highlight group +vim.api.nvim_set_hl(0, "@foo.bar", { link = "Identifier" }) +``` + +For a language-specific highlight, append the name of the language: + +```lua +-- Highlight @foo.bar as "Identifier" only in Lua files +vim.api.nvim_set_hl(0, "@foo.bar.lua", { link = "Identifier" }) +``` + +See `:h treesitter-highlight-groups` for details. + +#### Incremental selection + +Incremental selection based on the named nodes from the grammar. + +```lua +require'nvim-treesitter.configs'.setup { + incremental_selection = { + enable = true, + keymaps = { + init_selection = "gnn", -- set to `false` to disable one of the mappings + node_incremental = "grn", + scope_incremental = "grc", + node_decremental = "grm", + }, + }, +} +``` + +#### Indentation + +Indentation based on treesitter for the `=` operator. +**NOTE: This is an experimental feature**. + +```lua +require'nvim-treesitter.configs'.setup { + indent = { + enable = true + } +} +``` + +#### Folding + +Tree-sitter based folding. _(Technically not a module because it's per windows and not per buffer.)_ + +```vim +set foldmethod=expr +set foldexpr=nvim_treesitter#foldexpr() +set nofoldenable " Disable folding at startup. +``` + +This will respect your `foldminlines` and `foldnestmax` settings. + +# Advanced setup + +## Changing the parser install directory + +If you want to install the parsers to a custom directory you can specify this +directory with `parser_install_dir` option in that is passed to `setup`. +`nvim-treesitter` will then install the parser files into this directory. + +This directory must be writeable and must be explicitly added to the +`runtimepath`. For example: + +```lua + vim.opt.runtimepath:append("/some/path/to/store/parsers") + + require'nvim-treesitter.configs'.setup { + parser_install_dir = "/some/path/to/store/parsers", + + ... + + } +``` + +If this option is not included in the setup options, or is explicitly set to +`nil` then the default install directories will be used. If this value is set +the default directories will be ignored. + +Bear in mind that any parser installed into a parser folder on the runtime path +will still be considered installed. (For example if +"~/.local/share/nvim/site/parser/c.so" exists then the "c" parser will be +considered installed, even though it is not in `parser_install_dir`) + +The default paths are: + +1. first the package folder. Where `nvim-treesitter` is installed. +2. second the site directory. This is the "site" subdirectory of `stdpath("data")`. + +## Adding parsers + +If you have a parser that is not on the list of supported languages (either as a repository on Github or in a local directory), you can add it manually for use by `nvim-treesitter` as follows: + +1. Clone the repository or [create a new project](https://tree-sitter.github.io/tree-sitter/creating-parsers#project-setup) in, say, `~/projects/tree-sitter-zimbu`. Make sure that the `tree-sitter-cli` executable is installed and in your path; see for installation instructions. +2. Run `tree-sitter generate` in this directory (followed by `tree-sitter test` for good measure). +3. Add the following snippet to your `init.lua`: + +```lua +local parser_config = require "nvim-treesitter.parsers".get_parser_configs() +parser_config.zimbu = { + install_info = { + url = "~/projects/tree-sitter-zimbu", -- local path or git repo + files = {"src/parser.c"}, -- note that some parsers also require src/scanner.c or src/scanner.cc + -- optional entries: + branch = "main", -- default branch in case of git repo if different from master + generate_requires_npm = false, -- if stand-alone parser without npm dependencies + requires_generate_from_grammar = false, -- if folder contains pre-generated src/parser.c + }, + filetype = "zu", -- if filetype does not match the parser name +} +``` + +If you wish to set a specific parser for a filetype, you should use `vim.treesitter.language.register()`: + +```lua +vim.treesitter.language.register('python', 'someft') -- the someft filetype will use the python parser and queries. +``` + +Note this requires Nvim v0.9. + +4. Start `nvim` and `:TSInstall zimbu`. + +You can also skip step 2 and use `:TSInstallFromGrammar zimbu` to install directly from a `grammar.js` in the top-level directory specified by `url`. +Once the parser is installed, you can update it (from the latest revision of the `main` branch if `url` is a Github repository) with `:TSUpdate zimbu`. + +Note that neither `:TSInstall` nor `:TSInstallFromGrammar` copy query files from the grammar repository. +If you want your installed grammar to be useful, you must manually [add query files](#adding-queries) to your local nvim-treesitter installation. +Note also that module functionality is only triggered if your language's filetype is correctly identified. +If Neovim does not detect your language's filetype by default, you can use [Neovim's `vim.filetype.add()`]() to add a custom detection rule. + +If you use a git repository for your parser and want to use a specific version, you can set the `revision` key +in the `install_info` table for you parser config. + +## Adding queries + +Queries are what `nvim-treesitter` uses to extract information from the syntax tree; +they are located in the `queries/{language}/*` runtime directories (see `:h rtp`), +like the `queries` folder of this plugin, e.g. `queries/{language}/{locals,highlights,textobjects}.scm`. +Other modules may require additional queries such as `folding.scm`. You can find a +list of all supported capture names in [CONTRIBUTING.md](https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md#parser-configurations). + +All queries found in the runtime directories will be combined. +By convention, if you want to write a query, use the `queries/` directory, +but if you want to extend a query use the `after/queries/` directory. + +If you want to completely override a query, you can use `:h set_query()`. +For example, to override the `injections` queries from `c` with your own: + +```lua +require("vim.treesitter.query").set_query("c", "injections", "(comment) @comment") +``` + +Note: when using `set_query`, all queries in the runtime directories will be ignored. + +## Adding modules + +If you wish you write your own module, you need to support + +- tree-sitter language detection support; +- attaching and detaching to buffers; +- all nvim-treesitter commands. + +At the top level, you can use the `define_modules` function to define one or more modules or module groups: + +```lua +require'nvim-treesitter'.define_modules { + my_cool_plugin = { + attach = function(bufnr, lang) + -- Do cool stuff here + end, + detach = function(bufnr) + -- Undo cool stuff here + end, + is_supported = function(lang) + -- Check if the language is supported + end + } +} +``` + +with the following properties: + +- `module_path` specifies a require path (string) that exports a module with an `attach` and `detach` function. This is not required if the functions are on this definition. +- `enable` determines if the module is enabled by default. This is usually overridden by the user. +- `disable` takes a list of languages that this module is disabled for. This is usually overridden by the user. +- `is_supported` takes a function that takes a language and determines if this module supports that language. +- `attach` takes a function that attaches to a buffer. This is required if `module_path` is not provided. +- `detach` takes a function that detaches from a buffer. This is required if `module_path` is not provided. + +# Extra features + +### Statusline indicator + +```vim +echo nvim_treesitter#statusline(90) " 90 can be any length +module->expression_statement->call->identifier +``` + +### Utilities + +You can get some utility functions with + +```lua +local ts_utils = require 'nvim-treesitter.ts_utils' +``` + +Check [`:h nvim-treesitter-utils`](doc/nvim-treesitter.txt) for more information. + +# Troubleshooting + +Before doing anything, make sure you have the latest version of this plugin and run `:checkhealth nvim-treesitter`. +It can also help to update the parsers via `:TSUpdate`. + +#### Feature `X` does not work for `{language}`... + +First, check the `health#nvim_treesitter#check` and the `health#treesitter#check` sections of `:checkhealth` for any warning. +If there is one, it's highly likely that this is the cause of the problem. + +Next check the `## Parser/Features` subsection of the `health#nvim_treesitter#check` section of `:checkhealth` to ensure the desired module is enabled for your language. +If not, you might be missing query files; see [Adding queries](#adding-queries). + +Finally, ensure Neovim is correctly identifying your language's filetype using the `:echo &filetype` command while one of your language's files is open in Neovim. +If not, add a short Vimscript file to nvim-treesitter's `ftdetect` runtime directory following [Neovim's documentation](https://neovim.io/doc/user/filetype.html#new-filetype) on filetype detection. +You can also quickly & temporarily set the filetype for a single buffer with the `:set filetype=langname` command to test whether it fixes the problem. + +If everything is okay, then it might be an actual error. +In that case, feel free to [open an issue here](https://github.com/nvim-treesitter/nvim-treesitter/issues/new/choose). + +#### I get `module 'vim.treesitter.query' not found` + +Make sure you have the latest version of Neovim. + +#### I get `Error detected while processing .../plugin/nvim-treesitter.vim` every time I open Neovim + +This is probably due to a change in a parser's grammar or its queries. +Try updating the parser that you suspect has changed (`:TSUpdate {language}`) or all of them (`:TSUpdate`). +If the error persists after updating all parsers, +please [open an issue](https://github.com/nvim-treesitter/nvim-treesitter/issues/new/choose). + +#### I get `query error: invalid node type at position` + +This could be due a query file outside this plugin using outdated nodes, +or due to an outdated parser. + +- Make sure you have the parsers up to date with `:TSUpdate` +- Make sure you don't have more than one `parser` runtime directory. + You can execute this command `:echo nvim_get_runtime_file('parser', v:true)` to find all runtime directories. + If you get more than one path, remove the ones that are outside this plugin (`nvim-treesitter` directory), + so the correct version of the parser is used. + +#### I experience weird highlighting issues similar to [#78](https://github.com/nvim-treesitter/nvim-treesitter/issues/78) + +This is a well known issue, which arises when the tree and the buffer have gotten out of sync. +As this is an upstream issue, we don't have any definite fix. +To get around this, you can force reparsing the buffer with + +```vim +:write | edit | TSBufEnable highlight +``` + +This will save, restore and enable highlighting for the current buffer. + +#### I experience bugs when using `nvim-treesitter`'s `foldexpr` similar to [#194](https://github.com/nvim-treesitter/nvim-treesitter/issues/194) + +This might happen, and is known to happen, with `vim-clap`. +To avoid these kind of errors, please use `setlocal` instead of `set` for the respective filetypes. + +#### I run into errors like `module 'nvim-treesitter.configs' not found` at startup + +This is because of `rtp` management in `nvim`, adding `packadd +nvim-treesitter` should fix the issue. + +#### I want to use Git instead of curl for downloading the parsers + +In your Lua config: + +```lua +require("nvim-treesitter.install").prefer_git = true +``` + +#### I want to use a HTTP proxy for downloading the parsers + +You can either configure curl to use additional CLI arguments in your Lua config: + +```lua +require("nvim-treesitter.install").command_extra_args = { + curl = { "--proxy", "" }, +} +``` + +or you can configure git via `.gitconfig` and use git instead of curl + +```lua +require("nvim-treesitter.install").prefer_git = true +``` + +#### I want to use a mirror instead of "https://github.com/" + +In your Lua config: + +```lua +for _, config in pairs(require("nvim-treesitter.parsers").get_parser_configs()) do + config.install_info.url = config.install_info.url:gsub("https://github.com/", "something else") +end + +require'nvim-treesitter.configs'.setup { + -- + -- +} +``` + +#### Using an existing parser for another filetype + +For example, to use the `bash` tree-sitter to highlight file with +`filetype=apkbuild`, use: + +```lua +vim.treesitter.language.register("bash", "apkbuild") +``` + +The `bash` tree-sitter must be installed following the usual procedure [as +described above](#language-parsers). diff --git a/config/neovim/store/nvim-treesitter/assets/logo.png b/config/neovim/store/nvim-treesitter/assets/logo.png new file mode 100644 index 00000000..a60e536b Binary files /dev/null and b/config/neovim/store/nvim-treesitter/assets/logo.png differ diff --git a/config/neovim/store/nvim-treesitter/autoload/nvim_treesitter.vim b/config/neovim/store/nvim-treesitter/autoload/nvim_treesitter.vim new file mode 100644 index 00000000..90953985 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/autoload/nvim_treesitter.vim @@ -0,0 +1,27 @@ +function! nvim_treesitter#statusline(...) abort + return luaeval("require'nvim-treesitter.statusline'.statusline(_A)", get(a:, 1, {})) +endfunction + +function! nvim_treesitter#foldexpr() abort + return luaeval(printf('require"nvim-treesitter.fold".get_fold_indic(%d)', v:lnum)) +endfunction + +function! nvim_treesitter#installable_parsers(arglead, cmdline, cursorpos) abort + return join(luaeval("require'nvim-treesitter.parsers'.available_parsers()") + ['all'], "\n") +endfunction + +function! nvim_treesitter#installed_parsers(arglead, cmdline, cursorpos) abort + return join(luaeval("require'nvim-treesitter.info'.installed_parsers()") + ['all'], "\n") +endfunction + +function! nvim_treesitter#available_modules(arglead, cmdline, cursorpos) abort + return join(luaeval("require'nvim-treesitter.configs'.available_modules()"), "\n") +endfunction + +function! nvim_treesitter#available_query_groups(arglead, cmdline, cursorpos) abort + return join(luaeval("require'nvim-treesitter.query'.available_query_groups()"), "\n") +endfunction + +function! nvim_treesitter#indent() abort + return luaeval(printf('require"nvim-treesitter.indent".get_indent(%d)', v:lnum)) +endfunction diff --git a/config/neovim/store/nvim-treesitter/contrib/nvim-treesitter-luarocks.template b/config/neovim/store/nvim-treesitter/contrib/nvim-treesitter-luarocks.template new file mode 100644 index 00000000..8109f5f2 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/contrib/nvim-treesitter-luarocks.template @@ -0,0 +1,41 @@ +local git_ref = '$git_ref' +local modrev = '$modrev' +local specrev = '-1' + +local repo_url = '$repo_url' + +rockspec_format = '3.0' +package = '$package' +version = modrev .. specrev + +description = { + summary = 'Nvim Treesitter configurations and abstraction layer', + detailed = $detailed_description, + labels = { 'neovim' }, + homepage = 'https://github.com/nvim-treesitter/nvim-treesitter', + license = 'Apache-2.0', +} + +dependencies = { + 'lua >= 5.1', +} + +-- source = file:///. + +source = { + url = repo_url .. '/archive/' .. git_ref .. '.zip', + dir = '$repo_name-' .. '$archive_dir_suffix', +} + +build = { + type = 'make', + build_pass = false, + install_variables = { + INST_PREFIX='$(PREFIX)', + INST_BINDIR='$(BINDIR)', + INST_LIBDIR='$(LIBDIR)', + INST_LUADIR='$(LUADIR)', + INST_CONFDIR='$(CONFDIR)', + }, + copy_directories = $copy_directories, +} diff --git a/config/neovim/store/nvim-treesitter/contrib/nvim-treesitter-scm-1.rockspec b/config/neovim/store/nvim-treesitter/contrib/nvim-treesitter-scm-1.rockspec new file mode 100644 index 00000000..a901c5b1 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/contrib/nvim-treesitter-scm-1.rockspec @@ -0,0 +1,36 @@ +local MODREV, SPECREV = 'scm', '-1' +rockspec_format = '3.0' +package = 'nvim-treesitter' +version = MODREV .. SPECREV + +description = { + summary = 'Nvim Treesitter configurations and abstraction layer', + labels = { 'neovim' }, + homepage = 'https://github.com/nvim-treesitter/nvim-treesitter', + license = 'Apache-2.0', +} + +dependencies = { + 'lua >= 5.1', +} + +source = { + url = 'git://github.com/nvim-treesitter/nvim-treesitter', +} + +build = { + type = 'make', + install_variables = { + INST_PREFIX='$(PREFIX)', + INST_BINDIR='$(BINDIR)', + INST_LIBDIR='$(LIBDIR)', + INST_LUADIR='$(LUADIR)', + INST_CONFDIR='$(CONFDIR)', + }, + copy_directories = { + 'autoload', + 'doc', + 'plugin', + 'queries' + } +} diff --git a/config/neovim/store/nvim-treesitter/doc/nvim-treesitter.txt b/config/neovim/store/nvim-treesitter/doc/nvim-treesitter.txt new file mode 100644 index 00000000..f7a91b84 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/doc/nvim-treesitter.txt @@ -0,0 +1,590 @@ +*nvim-treesitter* Treesitter configurations and abstraction layer for Neovim. + +Minimum version of neovim: nightly + +Authors: + Kiyan Yazdani + Thomas Vigouroux + Stephan Seitz + Steven Sojka + Santos Gallegos + https://github.com/nvim-treesitter/nvim-treesitter/graphs/contributors + + Type |gO| to see the table of contents. + +============================================================================== +INTRODUCTION *nvim-treesitter-intro* + +nvim-treesitter wraps the Neovim treesitter API to provide functionalities +such as highlighting and incremental selection, and a command to easily +install parsers. + +============================================================================== +QUICK START *nvim-treesitter-quickstart* + +Install the parser for your language + +> + :TSInstall {language} +< + +To get a list of supported languages + +> + :TSInstallInfo +< + +By default, everything is disabled. +To enable supported features, put this in your `init.lua` file: + +> + require'nvim-treesitter.configs'.setup { + -- A directory to install the parsers into. + -- If this is excluded or nil parsers are installed + -- to either the package dir, or the "site" dir. + -- If a custom path is used (not nil) it must be added to the runtimepath. + parser_install_dir = "/some/path/to/store/parsers", + + -- A list of parser names, or "all" + ensure_installed = { "c", "lua", "rust" }, + + -- Install parsers synchronously (only applied to `ensure_installed`) + sync_install = false, + + -- Automatically install missing parsers when entering buffer + auto_install = false, + + -- List of parsers to ignore installing (for "all") + ignore_install = { "javascript" }, + + highlight = { + -- `false` will disable the whole extension + enable = true, + + -- list of language that will be disabled + disable = { "c", "rust" }, + + -- Setting this to true will run `:h syntax` and tree-sitter at the same time. + -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation). + -- Using this option may slow down your editor, and you may see some duplicate highlights. + -- Instead of true it can also be a list of languages + additional_vim_regex_highlighting = false, + }, + } + vim.opt.runtimepath:append("/some/path/to/store/parsers") +< + +See |nvim-treesitter-modules| for a list of all available modules and its options. + +============================================================================== +MODULES *nvim-treesitter-modules* + +|nvim-treesitter| provides several functionalities via modules (and submodules), +each module makes use of the query files defined for each language, + +All modules are disabled by default, and some provide default keymaps. +Each module corresponds to an entry in the dictionary passed to the +`nvim-treesitter.configs.setup` function, this should be in your `init.lua` file. + +> + require'nvim-treesitter.configs'.setup { + -- Modules and its options go here + highlight = { enable = true }, + incremental_selection = { enable = true }, + textobjects = { enable = true }, + } +< + +All modules share some common options, like `enable` and `disable`. +When `enable` is `true` this will enable the module for all supported languages, +if you want to disable the module for some languages you can pass a list to the `disable` option. + +> + require'nvim-treesitter.configs'.setup { + highlight = { + enable = true, + disable = { "cpp", "lua" }, + }, + } +< + +For more fine-grained control, `disable` can also take a function and +whenever it returns `true`, the module is disabled for that buffer. +The function is called once when a module starts in a buffer and receives the +language and buffer number as arguments: + +> + require'nvim-treesitter.configs'.setup { + highlight = { + enable = true, + disable = function(lang, bufnr) -- Disable in large C++ buffers + return lang == "cpp" and vim.api.nvim_buf_line_count(bufnr) > 50000 + end, + }, + } +< + +Options that define or accept a keymap use the same format you use to define +keymaps in Neovim, so you can write keymaps as `gd`, `a`, `a` +`` (control + a), `` (alt + n), `` (enter), etc. + +External plugins can provide their own modules with their own options, +those can also be configured using the `nvim-treesitter.configs.setup` +function. + +------------------------------------------------------------------------------ +HIGHLIGHT *nvim-treesitter-highlight-mod* + +Consistent syntax highlighting. + +Query files: `highlights.scm`. +Supported options: + +- enable: `true` or `false`. +- disable: list of languages. +- additional_vim_regex_highlighting: `true` or `false`, or a list of languages. + Set this to `true` if you depend on 'syntax' being enabled + (like for indentation). Using this option may slow down your editor, + and you may see some duplicate highlights. + Defaults to `false`. + +> + require'nvim-treesitter.configs'.setup { + highlight = { + enable = true, + custom_captures = { + -- Highlight the @foo.bar capture group with the "Identifier" highlight group. + ["foo.bar"] = "Identifier", + }, + -- Setting this to true or a list of languages will run `:h syntax` and tree-sitter at the same time. + additional_vim_regex_highlighting = false, + }, + } +< + +You can also set custom highlight captures +> + lua < + require'nvim-treesitter.configs'.setup { + incremental_selection = { + enable = true, + keymaps = { + init_selection = "gnn", + node_incremental = "grn", + scope_incremental = "grc", + node_decremental = "grm", + }, + }, + } +< + +------------------------------------------------------------------------------ +INDENTATION *nvim-treesitter-indentation-mod* + +Indentation based on treesitter for the |=| operator. +NOTE: this is an experimental feature. + +Query files: `indents.scm`. +Supported options: +- enable: `true` or `false`. +- disable: list of languages. +> + require'nvim-treesitter.configs'.setup { + indent = { + enable = true + }, + } + +`@indent` *nvim-treesitter-indentation-queries* +Queries can use the following captures: `@indent.begin` and `@indent.dedent`, +`@indent.branch`, `@indent.end` or `@indent.align`. An `@indent.ignore` capture tells +treesitter to ignore indentation and a `@indent.zero` capture sets +the indentation to 0. + +`@indent.begin` *nvim-treesitter-indentation-indent.begin* +The `@indent.begin` specifies that the next line should be indented. Multiple +indents on the same line get collapsed. Eg. + +> + ( + (if_statement) + (ERROR "else") @indent.begin + ) +< +Indent can also have `indent.immediate` set using a `#set!` directive, which +permits the next line to indent even when the block intended to be indented +has no content yet, improving interactive typing. + +eg for python: +> + ((if_statement) @indent.begin + (#set! indent.immediate 1)) +< + +Will allow: +> + if True: + # Auto indent to here + +`@indent.end` *nvim-treesitter-indentation-indent.end* +An `@indent.end` capture is used to specify that the indented region ends and +any text subsequent to the capture should be dedented. + +`@indent.branch` *nvim-treesitter-indentation-indent.branch* +An `@indent.branch` capture is used to specify that a dedented region starts +at the line including the captured nodes. + +`@indent.dedent` *nvim-treesitter-indentation-indent.dedent* +A `@indent.dedent` capture specifies dedenting starting on the next line. +> +`@indent.align` *nvim-treesitter-indentation-aligned_indent.align* +Aligned indent blocks may be specified with the `@indent.align` capture. +This permits + +> + foo(a, + b, + c) +< +As well as +> + foo( + a, + b, + c) +< +and finally +> + foo( + a, + b, + c + ) +< +To specify the delimiters to use `indent.open_delimiter` and +`indent.close_delimiter` should be used. Eg. +> + ((argument_list) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) +< + +For some languages the last line of an `indent.align` block must not be +the same indent as the natural next line. + +For example in python: + +> + if (a > b and + c < d): + pass + +Is not correct, whereas +> + if (a > b and + c < d): + pass + +Would be correctly indented. This behavior may be chosen using +`indent.avoid_last_matching_next`. Eg. + +> + (if_statement + condition: (parenthesized_expression) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")") + (#set! indent.avoid_last_matching_next 1) + ) +< +Could be used to specify that the last line of an `@indent.align` capture +should be additionally indented to avoid clashing with the indent of the first +line of the block inside an if. + +============================================================================== +COMMANDS *nvim-treesitter-commands* + + *:TSInstall* +:TSInstall {language} ...~ + +Install one or more treesitter parsers. +You can use |:TSInstall| `all` to install all parsers. Use |:TSInstall!| to +force the reinstallation of already installed parsers. + *:TSInstallSync* +:TSInstallSync {language} ...~ + +Perform the |:TSInstall| operation synchronously. + + *:TSInstallInfo* +:TSInstallInfo~ + +List information about currently installed parsers + + *:TSUpdate* +:TSUpdate {language} ...~ + +Update the installed parser for one more {language} or all installed parsers +if {language} is omitted. The specified parser is installed if it is not already +installed. + + *:TSUpdateSync* +:TSUpdateSync {language} ...~ + +Perform the |:TSUpdate| operation synchronously. + + *:TSUninstall* +:TSUninstall {language} ...~ + +Deletes the parser for one or more {language}. You can use 'all' for language +to uninstall all parsers. + + *:TSBufEnable* +:TSBufEnable {module}~ + +Enable {module} on the current buffer. +A list of modules can be found at |:TSModuleInfo| + + *:TSBufDisable* +:TSBufDisable {module}~ + +Disable {module} on the current buffer. +A list of modules can be found at |:TSModuleInfo| + + *:TSBufToggle* +:TSBufToggle {module}~ + +Toggle (enable if disabled, disable if enabled) {module} on the current +buffer. +A list of modules can be found at |:TSModuleInfo| + + *:TSEnable* +:TSEnable {module} [{language}]~ + +Enable {module} for the session. +If {language} is specified, enable module for the session only for this +particular language. +A list of modules can be found at |:TSModuleInfo| +A list of languages can be found at |:TSInstallInfo| + + *:TSDisable* +:TSDisable {module} [{language}]~ + +Disable {module} for the session. +If {language} is specified, disable module for the session only for this +particular language. +A list of modules can be found at |:TSModuleInfo| +A list of languages can be found at |:TSInstallInfo| + + *:TSToggle* +:TSToggle {module} [{language}]~ + +Toggle (enable if disabled, disable if enabled) {module} for the session. +If {language} is specified, toggle module for the session only for this +particular language. +A list of modules can be found at |:TSModuleInfo| +A list of languages can be found at |:TSInstallInfo| + + *:TSModuleInfo* +:TSModuleInfo [{module}]~ + +List the state for the given module or all modules for the current session in +a new buffer. + +These highlight groups are used by default: +> + highlight default TSModuleInfoGood guifg=LightGreen gui=bold + highlight default TSModuleInfoBad guifg=Crimson + highlight default link TSModuleInfoHeader Type + highlight default link TSModuleInfoNamespace Statement + highlight default link TSModuleInfoParser Identifier +< + + *:TSEditQuery* +:TSEditQuery {query-group} [{lang}]~ + +Edit the query file for a {query-group} (e.g. highlights, locals) for given +{lang}. If there are multiple files, the user is prompted to select one of them. +If no such file exists, a buffer for a new file in the user's config directory +is created. If {lang} is not specified, the language of the current buffer +is used. + + *:TSEditQueryUserAfter* +:TSEditQueryUserAfter {query-group} [{lang}]~ + +Same as |:TSEditQuery| but edits a file in the `after` directory of the +user's config directory. Useful to add custom extensions for the queries +provided by a plugin. + +============================================================================== +UTILS *nvim-treesitter-utils* + +Nvim treesitter has some wrapper functions that you can retrieve with: +> + local ts_utils = require 'nvim-treesitter.ts_utils' +< +Methods + *ts_utils.get_node_at_cursor* +get_node_at_cursor(winnr)~ + +`winnr` will be 0 if nil. +Returns the node under the cursor. + + *ts_utils.is_parent* +is_parent(dest, source)~ + +Determines whether `dest` is a parent of `source`. +Returns a boolean. + + *ts_utils.get_named_children* +get_named_children(node)~ + +Returns a table of named children of `node`. + + *ts_utils.get_next_node* +get_next_node(node, allow_switch_parent, allow_next_parent)~ + +Returns the next node within the same parent. +If no node is found, returns `nil`. +If `allow_switch_parent` is true, it will allow switching parent +when the node is the last node. +If `allow_next_parent` is true, it will allow next parent if +the node is the last node and the next parent doesn't have children. + + *ts_utils.get_previous_node* +get_previous_node(node, allow_switch_parents, allow_prev_parent)~ + +Returns the previous node within the same parent. +`allow_switch_parent` and `allow_prev_parent` follow the same rule +as |ts_utils.get_next_node| but if the node is the first node. + + *ts_utils.goto_node* +goto_node(node, goto_end, avoid_set_jump)~ + +Sets cursor to the position of `node` in the current windows. +If `goto_end` is truthy, the cursor is set to the end the node range. +Setting `avoid_set_jump` to `true`, avoids setting the current cursor position +to the jump list. + + *ts_utils.swap_nodes* +swap_nodes(node_or_range1, node_or_range2, bufnr, cursor_to_second)~ + +Swaps the nodes or ranges. +set `cursor_to_second` to true to move the cursor to the second node + + *ts_utils.memoize_by_buf_tick* +memoize_by_buf_tick(fn, options)~ + +Caches the return value for a function and returns the cache value if the tick +of the buffer has not changed from the previous. + + `fn`: a function that takes any arguments + and returns a value to store. + `options?`:
+ - `bufnr`: a function/value that extracts the bufnr from the given arguments. + - `key`: a function/value that extracts the cache key from the given arguments. + `returns`: a function to call with bufnr as argument to + retrieve the value from the cache + + *ts_utils.node_to_lsp_range* +node_to_lsp_range(node)~ + +Get an lsp formatted range from a node range + + *ts_utils.node_length* +node_length(node)~ + +Get the byte length of node range + + *ts_utils.update_selection* +update_selection(buf, node)~ + +Set the selection to the node range + + *ts_utils.highlight_range* +highlight_range(range, buf, hl_namespace, hl_group)~ + +Set a highlight that spans the given range + + *ts_utils.highlight_node* +highlight_node(node, buf, hl_namespace, hl_group)~ + +Set a highlight that spans the given node's range + +============================================================================== +FUNCTIONS *nvim-treesitter-functions* + + *nvim_treesitter#statusline()* +nvim_treesitter#statusline(opts)~ + +Returns a string describing the current position in the file. This +could be used as a statusline indicator. +Default options (lua syntax): +> + { + indicator_size = 100, + type_patterns = {'class', 'function', 'method'}, + transform_fn = function(line, _node) return line:gsub('%s*[%[%(%{]*%s*$', '') end, + separator = ' -> ', + allow_duplicates = false + } +< +- `indicator_size` - How long should the string be. If longer, it is cut from + the beginning. +- `type_patterns` - Which node type patterns to match. +- `transform_fn` - Function used to transform the single item in line. By + default it removes opening brackets and spaces from end. Takes two arguments: + the text of the line in question, and the corresponding treesitter node. +- `separator` - Separator between nodes. +- `allow_duplicates` - Whether or not to remove duplicate components. + + *nvim_treesitter#foldexpr()* +nvim_treesitter#foldexpr()~ + +Functions to be used to determine the fold level at a given line number. +To use it: > + set foldmethod=expr + set foldexpr=nvim_treesitter#foldexpr() +< + +This will respect your 'foldminlines' and 'foldnestmax' settings. + +Note: This is highly experimental, and folding can break on some types of + edits. If you encounter such breakage, hitting `zx` should fix folding. + In any case, feel free to open an issue with the reproducing steps. + +============================================================================== +PERFORMANCE *nvim-treesitter-performance* + +`nvim-treesitter` checks the 'runtimepath' on startup in order to discover +available parsers and queries and index them. As a consequence, a very long +'runtimepath' might result in delayed startup times. + + +vim:tw=78:ts=8:expandtab:noet:ft=help:norl: diff --git a/config/neovim/store/nvim-treesitter/lockfile.json b/config/neovim/store/nvim-treesitter/lockfile.json new file mode 100644 index 00000000..894fb999 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lockfile.json @@ -0,0 +1,839 @@ +{ + "ada": { + "revision": "e8e2515465cc2d7c444498e68bdb9f1d86767f95" + }, + "agda": { + "revision": "d3dc807692e6bca671d4491b3bf5c67eeca8c016" + }, + "angular": { + "revision": "10f21f3f1b10584e62ecc113ab3cda1196d0ceb8" + }, + "apex": { + "revision": "c99ad4b16d112fea91745e3f1b769754239fdaba" + }, + "arduino": { + "revision": "babb6d4da69b359bbb80adbf1fe39c0da9175210" + }, + "asm": { + "revision": "b0306e9bb2ebe01c6562f1aef265cc42ccc53070" + }, + "astro": { + "revision": "4be180759ec13651f72bacee65fa477c64222a1a" + }, + "authzed": { + "revision": "1dec7e1af96c56924e3322cd85fdce15d0a31d00" + }, + "awk": { + "revision": "ba7472152d79a8c916550c80fdbfd5724d07a0c9" + }, + "bash": { + "revision": "2fbd860f802802ca76a6661ce025b3a3bca2d3ed" + }, + "bass": { + "revision": "28dc7059722be090d04cd751aed915b2fee2f89a" + }, + "beancount": { + "revision": "c25f8034c977681653a8acd541c8b4877a58f474" + }, + "bibtex": { + "revision": "ccfd77db0ed799b6c22c214fe9d2937f47bc8b34" + }, + "bicep": { + "revision": "0092c7d1bd6bb22ce0a6f78497d50ea2b87f19c0" + }, + "bitbake": { + "revision": "a5d04fdb5a69a02b8fa8eb5525a60dfb5309b73b" + }, + "blueprint": { + "revision": "60ba73739c6083c693d86a1a7cf039c07eb4ed59" + }, + "bp": { + "revision": "2326d709fb9cf73cf124fdbc803c267f851721a4" + }, + "c": { + "revision": "82fb86aa544843bd17a9f0f3dc16edf645a34349" + }, + "c_sharp": { + "revision": "82fa8f05f41a33e9bc830f85d74a9548f0291738" + }, + "cairo": { + "revision": "6238f609bea233040fe927858156dee5515a0745" + }, + "capnp": { + "revision": "7b0883c03e5edd34ef7bcf703194204299d7099f" + }, + "chatito": { + "revision": "a461f20dedb43905febb12c1635bc7d2e43e96f0" + }, + "clojure": { + "revision": "f4236d4da8aa92bc105d9c118746474c608e6af7" + }, + "cmake": { + "revision": "20ffd6d3b4da1acdbf2d08204b2130a5b2f7c4b3" + }, + "comment": { + "revision": "5d8b29f6ef3bf64d59430dcfe76b31cc44b5abfd" + }, + "commonlisp": { + "revision": "bf2a65b1c119898a1a17389e07f2a399c05cdc0c" + }, + "cooklang": { + "revision": "4ebe237c1cf64cf3826fc249e9ec0988fe07e58e" + }, + "corn": { + "revision": "604d73c38d4c28ca68e9e441ffd405d68cb63051" + }, + "cpon": { + "revision": "594289eadfec719198e560f9d7fd243c4db678d5" + }, + "cpp": { + "revision": "2369fa991eba294e9238e28280ffcd58132f94bc" + }, + "css": { + "revision": "f6be52c3d1cdb1c5e4dd7d8bce0a57497f55d6af" + }, + "csv": { + "revision": "7eb7297823605392d2bbcc4c09b1cd18d6fa9529" + }, + "cuda": { + "revision": "e7878a9cf4157e9d6c8013ff5605c9f26d62894d" + }, + "cue": { + "revision": "8a5f273bfa281c66354da562f2307c2d394b6c81" + }, + "d": { + "revision": "750dde90ed9cdbd82493bc28478d8ab1976b0e9f" + }, + "dart": { + "revision": "ac0bb849ccd1a923963af47573b5e396736ff582" + }, + "devicetree": { + "revision": "fb07e6044ffd36932c57a5be01ba5d6b8a9337bb" + }, + "dhall": { + "revision": "affb6ee38d629c9296749767ab832d69bb0d9ea8" + }, + "diff": { + "revision": "629676fc3919606964231b2c7b9677d6998a2cb4" + }, + "disassembly": { + "revision": "0229c0211dba909c5d45129ac784a3f4d49c243a" + }, + "djot": { + "revision": "0e9a836ec47612ade15645fb1680adb549894a6c" + }, + "dockerfile": { + "revision": "087daa20438a6cc01fa5e6fe6906d77c869d19fe" + }, + "dot": { + "revision": "9ab85550c896d8b294d9b9ca1e30698736f08cea" + }, + "doxygen": { + "revision": "4a30eba5d047d6a8c5b005202b4848c0b33d76ca" + }, + "dtd": { + "revision": "648183d86f6f8ffb240ea11b4c6873f6f45d8b67" + }, + "earthfile": { + "revision": "91fc9434283aec06139e37fc007ad00922f278b4" + }, + "ebnf": { + "revision": "8e635b0b723c620774dfb8abf382a7f531894b40" + }, + "eds": { + "revision": "fde62029d4c715562230070b9af51a9500c2ce10" + }, + "eex": { + "revision": "f742f2fe327463335e8671a87c0b9b396905d1d1" + }, + "elixir": { + "revision": "de690fa8a028f122af46d9d2685679fe5f2d7d60" + }, + "elm": { + "revision": "09dbf221d7491dc8d8839616b27c21b9c025c457" + }, + "elsa": { + "revision": "0a66b2b3f3c1915e67ad2ef9f7dbd2a84820d9d7" + }, + "elvish": { + "revision": "5e7210d945425b77f82cbaebc5af4dd3e1ad40f5" + }, + "embedded_template": { + "revision": "38d5004a797298dc42c85e7706c5ceac46a3f29f" + }, + "erlang": { + "revision": "98ea1f9c957b2ad520415eecb5a5b406e931101e" + }, + "facility": { + "revision": "a52579670e2b14ec03d410c3c980fafaf6d659c4" + }, + "faust": { + "revision": "f3b9274514b5f9bf6b0dd4a01c30f9cc15c58bc4" + }, + "fennel": { + "revision": "8ad17704b3c2469155947d4e8fcb618cf1328459" + }, + "fidl": { + "revision": "0a8910f293268e27ff554357c229ba172b0eaed2" + }, + "firrtl": { + "revision": "8503d3a0fe0f9e427863cb0055699ff2d29ae5f5" + }, + "fish": { + "revision": "a78aef9abc395c600c38a037ac779afc7e3cc9e0" + }, + "foam": { + "revision": "04664b40c0dadb7ef37028acf3422c63271d377b" + }, + "forth": { + "revision": "90189238385cf636b9ee99ce548b9e5b5e569d48" + }, + "fortran": { + "revision": "f73d473e3530862dee7cbb38520f28824e7804f6" + }, + "fsh": { + "revision": "fad2e175099a45efbc98f000cc196d3674cc45e0" + }, + "func": { + "revision": "f780ca55e65e7d7360d0229331763e16c452fc98" + }, + "fusion": { + "revision": "19db2f47ba4c3a0f6238d4ae0e2abfca16e61dd6" + }, + "gdscript": { + "revision": "1f1e782fe2600f50ae57b53876505b8282388d77" + }, + "gdshader": { + "revision": "ffd9f958df13cae04593781d7d2562295a872455" + }, + "git_config": { + "revision": "9c2a1b7894e6d9eedfe99805b829b4ecd871375e" + }, + "git_rebase": { + "revision": "d8a4207ebbc47bd78bacdf48f883db58283f9fd8" + }, + "gitattributes": { + "revision": "41940e199ba5763abea1d21b4f717014b45f01ea" + }, + "gitcommit": { + "revision": "edd817e0532f179b7f7f371dc180629070945f0c" + }, + "gitignore": { + "revision": "f4685bf11ac466dd278449bcfe5fd014e94aa504" + }, + "gleam": { + "revision": "8432ffe32ccd360534837256747beb5b1c82fca1" + }, + "glimmer": { + "revision": "6b25d265c990139353e1f7f97baf84987ebb7bf0" + }, + "glsl": { + "revision": "33a16b6ff4d7206a16f2dc96c40e149c657db65f" + }, + "gn": { + "revision": "bc06955bc1e3c9ff8e9b2b2a55b38b94da923c05" + }, + "gnuplot": { + "revision": "3c895f5d9c0b3a3c7e02383766b462c21913c000" + }, + "go": { + "revision": "7ee8d928db5202f6831a78f8112fd693bf69f98b" + }, + "godot_resource": { + "revision": "2ffb90de47417018651fc3b970e5f6b67214dc9d" + }, + "gomod": { + "revision": "bbe2fe3be4b87e06a613e685250f473d2267f430" + }, + "gosum": { + "revision": "e2ac513b2240c7ff1069ae33b2df29ce90777c11" + }, + "gotmpl": { + "revision": "9d3f6e526dd074b9edae9070b7bb778f00e87a5b" + }, + "gowork": { + "revision": "949a8a470559543857a62102c84700d291fc984c" + }, + "gpg": { + "revision": "f99323fb8f3f10b6c69db0c2f6d0a14bd7330675" + }, + "graphql": { + "revision": "5e66e961eee421786bdda8495ed1db045e06b5fe" + }, + "groovy": { + "revision": "6c5c8813233fe326e24c5ef032858d13f8006a8d" + }, + "gstlaunch": { + "revision": "549aef253fd38a53995cda1bf55c501174372bf7" + }, + "hack": { + "revision": "fca1e294f6dce8ec5659233a6a21f5bd0ed5b4f2" + }, + "hare": { + "revision": "070524937539eb8bb4f10debd9c83b66c434f3a2" + }, + "haskell": { + "revision": "a50070d5bb5bd5c1281740a6102ecf1f4b0c4f19" + }, + "haskell_persistent": { + "revision": "577259b4068b2c281c9ebf94c109bd50a74d5857" + }, + "hcl": { + "revision": "e936d3fef8bac884661472dce71ad82284761eb1" + }, + "heex": { + "revision": "b5ad6e34eea18a15bbd1466ca707a17f9bff7b93" + }, + "helm": { + "revision": "9d3f6e526dd074b9edae9070b7bb778f00e87a5b" + }, + "hjson": { + "revision": "02fa3b79b3ff9a296066da6277adfc3f26cbc9e0" + }, + "hlsl": { + "revision": "a84e8d4f675d0006f7c07f6c7bcea2fca04cdb6e" + }, + "hlsplaylist": { + "revision": "64f19029339e75d6762feae39e7878595860c980" + }, + "hocon": { + "revision": "c390f10519ae69fdb03b3e5764f5592fb6924bcc" + }, + "hoon": { + "revision": "a24c5a39d1d7e993a8bee913c8e8b6a652ca5ae8" + }, + "html": { + "revision": "e4d834eb4918df01dcad5c27d1b15d56e3bd94cd" + }, + "htmldjango": { + "revision": "ea71012d3fe14dd0b69f36be4f96bdfe9155ebae" + }, + "http": { + "revision": "8d22f33faa5aa95c6526606fb656ada342e59e40" + }, + "hurl": { + "revision": "ad705af8c44c737bdb965fc081329c50716d2d03" + }, + "hyprlang": { + "revision": "e5da7d0aa44403153e0394d87d9edea4e5bd6609" + }, + "idl": { + "revision": "b5b53e2ca0521b98277d5e4b109f0b0e362e850e" + }, + "ini": { + "revision": "bcb84a2d4bcd6f55b911c42deade75c8f90cb0c5" + }, + "inko": { + "revision": "7860637ce1b43f5f79cfb7cc3311bf3234e9479f" + }, + "ispc": { + "revision": "9b2f9aec2106b94b4e099fe75e73ebd8ae707c04" + }, + "janet_simple": { + "revision": "f3d6e09cc47e76833f23f83b2c59ea0878660953" + }, + "java": { + "revision": "953abfc8bb3eb2f578e1f461edba4a9885f974b8" + }, + "javascript": { + "revision": "a5de24dc7939cb07a758f8d89c089cfdb6f479aa" + }, + "jq": { + "revision": "13990f530e8e6709b7978503da9bc8701d366791" + }, + "jsdoc": { + "revision": "49fde205b59a1d9792efc21ee0b6d50bbd35ff14" + }, + "json": { + "revision": "94f5c527b2965465956c2000ed6134dd24daf2a7" + }, + "json5": { + "revision": "ab0ba8229d639ec4f3fa5f674c9133477f4b77bd" + }, + "jsonc": { + "revision": "02b01653c8a1c198ae7287d566efa86a135b30d5" + }, + "jsonnet": { + "revision": "d34615fa12cc1d1cfc1f1f1a80acc9db80ee4596" + }, + "julia": { + "revision": "acd5ca12cc278df7960629c2429a096c7ac4bbea" + }, + "just": { + "revision": "fd814fc6c579f68c2a642f5e0268cf69daae92d7" + }, + "kconfig": { + "revision": "486fea71f61ad9f3fd4072a118402e97fe88d26c" + }, + "kdl": { + "revision": "49fb89a854d93b58a65a19724ac307195ca11941" + }, + "kotlin": { + "revision": "c9cb8504b81684375e7beb8907517dbd6947a1be" + }, + "koto": { + "revision": "919440e1376109bab4edac52594c17c02ae0be5a" + }, + "kusto": { + "revision": "8353a1296607d6ba33db7c7e312226e5fc83e8ce" + }, + "lalrpop": { + "revision": "854a40e99f7c70258e522bdb8ab584ede6196e2e" + }, + "latex": { + "revision": "cd82eb40d31bdfe65f846f4e06292d6c804b5e0e" + }, + "ledger": { + "revision": "8a841fb20ce683bfbb3469e6ba67f2851cfdf94a" + }, + "leo": { + "revision": "304611b5eaf53aca07459a0a03803b83b6dfd3b3" + }, + "linkerscript": { + "revision": "f99011a3554213b654985a4b0a65b3b032ec4621" + }, + "liquid": { + "revision": "293369818da219d97327908aab33707b04b63fd9" + }, + "liquidsoap": { + "revision": "14feafa91630afb1ab9988cf9b738b7ea29f3f89" + }, + "llvm": { + "revision": "1b96e58faf558ce057d4dc664b904528aee743cb" + }, + "lua": { + "revision": "a24dab177e58c9c6832f96b9a73102a0cfbced4a" + }, + "luadoc": { + "revision": "873612aadd3f684dd4e631bdf42ea8990c57634e" + }, + "luap": { + "revision": "31461ae9bd0866cb5117cfe5de71189854fd0f3e" + }, + "luau": { + "revision": "5aa9b88a8e3327276ec6e72de997f04ac80b1ae4" + }, + "m68k": { + "revision": "d097b123f19c6eaba2bf181c05420d88b9fc489d" + }, + "make": { + "revision": "a4b9187417d6be349ee5fd4b6e77b4172c6827dd" + }, + "markdown": { + "revision": "7fe453beacecf02c86f7736439f238f5bb8b5c9b" + }, + "markdown_inline": { + "revision": "7fe453beacecf02c86f7736439f238f5bb8b5c9b" + }, + "matlab": { + "revision": "79d8b25f57b48f83ae1333aff6723b83c9532e37" + }, + "menhir": { + "revision": "be8866a6bcc2b563ab0de895af69daeffa88fe70" + }, + "mermaid": { + "revision": "90ae195b31933ceb9d079abfa8a3ad0a36fee4cc" + }, + "meson": { + "revision": "bd17c824196ce70800f64ad39cfddd1b17acc13f" + }, + "mlir": { + "revision": "00c32d8562dc957b187da110a3443307962b8da8" + }, + "muttrc": { + "revision": "90ef60852c410bd964cd3b954366e4c403c17314" + }, + "nasm": { + "revision": "570f3d7be01fffc751237f4cfcf52d04e20532d1" + }, + "nickel": { + "revision": "58baf89db8fdae54a84bcf22c80ff10ee3f929ed" + }, + "nim": { + "revision": "961c2798cec9250c44f7d7225ddb33d47d25856a" + }, + "nim_format_string": { + "revision": "d45f75022d147cda056e98bfba68222c9c8eca3a" + }, + "ninja": { + "revision": "0a95cfdc0745b6ae82f60d3a339b37f19b7b9267" + }, + "nix": { + "revision": "b3cda619248e7dd0f216088bd152f59ce0bbe488" + }, + "norg": { + "revision": "aa1a1a7ded81a094cc3d5cb14bea6f34b86d8688" + }, + "nqc": { + "revision": "14e6da1627aaef21d2b2aa0c37d04269766dcc1d" + }, + "objc": { + "revision": "62e61b6f5c0289c376d61a8c91faf6435cde9012" + }, + "objdump": { + "revision": "28d3b2e25a0b1881d1b47ed1924ca276c7003d45" + }, + "ocaml": { + "revision": "0b12614ded3ec7ed7ab7933a9ba4f695ba4c342e" + }, + "ocaml_interface": { + "revision": "0b12614ded3ec7ed7ab7933a9ba4f695ba4c342e" + }, + "ocamllex": { + "revision": "4b9898ccbf198602bb0dec9cd67cc1d2c0a4fad2" + }, + "odin": { + "revision": "d37b8f24f653378b268ec18404e9c14ad355b128" + }, + "org": { + "revision": "64cfbc213f5a83da17632c95382a5a0a2f3357c1" + }, + "pascal": { + "revision": "a9ee969dec5b2e3b2ccccc5954fec04100c7619e" + }, + "passwd": { + "revision": "20239395eacdc2e0923a7e5683ad3605aee7b716" + }, + "pem": { + "revision": "217ff2af3f2db15a79ab7e3d21ea1e0c17e71a1a" + }, + "perl": { + "revision": "309cb8d33bcfd0a81050b21be08f9eb3f2fe2138" + }, + "php": { + "revision": "b38c53537769df05871643c9688c264074fb6076" + }, + "php_only": { + "revision": "b38c53537769df05871643c9688c264074fb6076" + }, + "phpdoc": { + "revision": "1d0e255b37477d0ca46f1c9e9268c8fa76c0b3fc" + }, + "pioasm": { + "revision": "924aadaf5dea2a6074d72027b064f939acf32e20" + }, + "po": { + "revision": "bd860a0f57f697162bf28e576674be9c1500db5e" + }, + "pod": { + "revision": "39da859947b94abdee43e431368e1ae975c0a424" + }, + "poe_filter": { + "revision": "592476d81f95d2451f2ca107dc872224c76fecdf" + }, + "pony": { + "revision": "73ff874ae4c9e9b45462673cbc0a1e350e2522a7" + }, + "printf": { + "revision": "0e0aceabbf607ea09e03562f5d8a56f048ddea3d" + }, + "prisma": { + "revision": "eca2596a355b1a9952b4f80f8f9caed300a272b5" + }, + "promql": { + "revision": "77625d78eebc3ffc44d114a07b2f348dff3061b0" + }, + "properties": { + "revision": "9d09f5f200c356c50c4103d36441309fd61b48d1" + }, + "proto": { + "revision": "e9f6b43f6844bd2189b50a422d4e2094313f6aa3" + }, + "prql": { + "revision": "09e158cd3650581c0af4c49c2e5b10c4834c8646" + }, + "psv": { + "revision": "7eb7297823605392d2bbcc4c09b1cd18d6fa9529" + }, + "pug": { + "revision": "a7ff31a38908df9b9f34828d21d6ca5e12413e18" + }, + "puppet": { + "revision": "584522f32495d648b18a53ccb52d988e60de127d" + }, + "purescript": { + "revision": "daf9b3e2be18b0b2996a1281f7783e0d041d8b80" + }, + "pymanifest": { + "revision": "e3b82b78721aee07f676dac8473ae69db51debcf" + }, + "python": { + "revision": "71778c2a472ed00a64abf4219544edbf8e4b86d7" + }, + "ql": { + "revision": "42becd6f8f7bae82c818fa3abb1b6ff34b552310" + }, + "qmldir": { + "revision": "6b2b5e41734bd6f07ea4c36ac20fb6f14061c841" + }, + "qmljs": { + "revision": "2c57cac27e207425f8df15327884434cb12365a3" + }, + "query": { + "revision": "a12c4a1cd8aa6e0340ecb7089a05cd345a12bae3" + }, + "r": { + "revision": "391400572538ff9854341a175ed8ab4b1e45f44b" + }, + "racket": { + "revision": "171f52a8c0ed635b85cd42d1e36d82f1066a03b4" + }, + "rasi": { + "revision": "6c9bbcfdf5f0f553d9ebc01750a3aa247a37b8aa" + }, + "rbs": { + "revision": "e5e807a50fcd104a2d740d354632fc671700a0bf" + }, + "re2c": { + "revision": "47aa19cf5f7aba2ed30e2b377f7172df76e819a6" + }, + "readline": { + "revision": "3d4768b04d7cfaf40533e12b28672603428b8f31" + }, + "regex": { + "revision": "47007f195752d8e57bda80b0b6cdb2d173a9f7d7" + }, + "rego": { + "revision": "9ac75e71b2d791e0aadeef68098319d86a2a14cf" + }, + "requirements": { + "revision": "360c6a6b31076a482663806f7a8241de9cad6b4e" + }, + "rnoweb": { + "revision": "1a74dc0ed731ad07db39f063e2c5a6fe528cae7f" + }, + "robot": { + "revision": "322e4cc65754d2b3fdef4f2f8a71e0762e3d13af" + }, + "roc": { + "revision": "7df2c0892e62efb81a7504d9799d4e0d0443d241" + }, + "ron": { + "revision": "78938553b93075e638035f624973083451b29055" + }, + "rst": { + "revision": "5120f6e59284cb8b85b450bd2db0bd352635ba9f" + }, + "ruby": { + "revision": "dc2d7d6b50f9975bc3c35bbec0ba11b2617b736b" + }, + "rust": { + "revision": "9c84af007b0f144954adb26b3f336495cbb320a7" + }, + "scala": { + "revision": "b76db435a7f876cf1ede837d66054c534783c72f" + }, + "scfg": { + "revision": "6deae0cbb458c849a4d1e2985093e9c9c32d7fd0" + }, + "scheme": { + "revision": "8f9dff3d038f09934db5ea113cebc59c74447743" + }, + "scss": { + "revision": "c478c6868648eff49eb04a4df90d703dc45b312a" + }, + "slang": { + "revision": "989bfe5ae69e7bad13454b8f52e4ab0c343d8ded" + }, + "slint": { + "revision": "0701312b74b87fe20e61aa662ba41c5815b5d428" + }, + "smali": { + "revision": "fdfa6a1febc43c7467aa7e937b87b607956f2346" + }, + "smithy": { + "revision": "fa898ac0885d1da9a253695c3e0e91f5efc587cd" + }, + "snakemake": { + "revision": "ba1b3868eaa960b945593404af9a7c2f296d3643" + }, + "solidity": { + "revision": "b5a23ead0f69d38b5c9a630f52f5c129132c16ed" + }, + "soql": { + "revision": "c99ad4b16d112fea91745e3f1b769754239fdaba" + }, + "sosl": { + "revision": "c99ad4b16d112fea91745e3f1b769754239fdaba" + }, + "sourcepawn": { + "revision": "227656e72a5f0d430bb82a467bc8078f078bba84" + }, + "sparql": { + "revision": "05f949d3c1c15e3261473a244d3ce87777374dec" + }, + "sql": { + "revision": "25f94f998de79bae9df28add9782f9ea6ea0e2b8" + }, + "squirrel": { + "revision": "0a50d31098e83c668d34d1160a0f6c7d23b571cc" + }, + "ssh_config": { + "revision": "77450e8bce8853921512348f83c73c168c71fdfb" + }, + "starlark": { + "revision": "018d0e09d9d0f0dd6740a37682b8ee4512e8b2ac" + }, + "strace": { + "revision": "d819cdd5dbe455bd3c859193633c8d91c0df7c36" + }, + "styled": { + "revision": "c68a4572e2d6403b6e99066c9a113b43f4a07a27" + }, + "supercollider": { + "revision": "affa4389186b6939d89673e1e9d2b28364f5ca6f" + }, + "surface": { + "revision": "f4586b35ac8548667a9aaa4eae44456c1f43d032" + }, + "svelte": { + "revision": "2c97326cd96b7c3016c3d249e8dc244c89b4abeb" + }, + "swift": { + "revision": "03af4d057afc56edf6a703e6606b86f782353f22" + }, + "sxhkdrc": { + "revision": "440d5f913d9465c9c776a1bd92334d32febcf065" + }, + "systemtap": { + "revision": "1af543a96d060b1f808982037bfc54cc02218edd" + }, + "t32": { + "revision": "6182836f4128725f1e74ce986840d7317021a015" + }, + "tablegen": { + "revision": "b1170880c61355aaf38fc06f4af7d3c55abdabc4" + }, + "tact": { + "revision": "034df2162ed7b654efd999942e266be713c7cde0" + }, + "tcl": { + "revision": "8784024358c233efd0f3a6fd9e7a3c5852e628bc" + }, + "teal": { + "revision": "33482c92a0dfa694491d34e167a1d2f52b0dccb1" + }, + "templ": { + "revision": "cf84ea53e2e2531f23009d676ac206090c1e2392" + }, + "terraform": { + "revision": "e936d3fef8bac884661472dce71ad82284761eb1" + }, + "textproto": { + "revision": "8dacf02aa402892c91079f8577998ed5148c0496" + }, + "thrift": { + "revision": "68fd0d80943a828d9e6f49c58a74be1e9ca142cf" + }, + "tiger": { + "revision": "a7f11d946b44244f71df41d2a78af0665d618dae" + }, + "tlaplus": { + "revision": "200f9dab6b23f3b9bb8f67fc811221517f56c373" + }, + "tmux": { + "revision": "9138ea508410e0f34da2666609f600f65e944f22" + }, + "todotxt": { + "revision": "3937c5cd105ec4127448651a21aef45f52d19609" + }, + "toml": { + "revision": "16a30c83ce427385b8d14939c45c137fcfca6c42" + }, + "tsv": { + "revision": "7eb7297823605392d2bbcc4c09b1cd18d6fa9529" + }, + "tsx": { + "revision": "4ad3010c91d700026d036b5230e2d99ba94ae8a4" + }, + "turtle": { + "revision": "085437f5cb117703b7f520dd92161140a684f092" + }, + "twig": { + "revision": "eaf80e6af969e25993576477a9dbdba3e48c1305" + }, + "typescript": { + "revision": "4ad3010c91d700026d036b5230e2d99ba94ae8a4" + }, + "typespec": { + "revision": "fd9a83c6c0aaaff4b1354454b5b9f130f59dd553" + }, + "typoscript": { + "revision": "43b221c0b76e77244efdaa9963e402a17c930fbc" + }, + "typst": { + "revision": "3924cb9ed9e0e62ce7df9c4fe0faa4c234795999" + }, + "udev": { + "revision": "8f58696e79092b4ad6bf197415bbd0970acf15cd" + }, + "ungrammar": { + "revision": "debd26fed283d80456ebafa33a06957b0c52e451" + }, + "unison": { + "revision": "59d36a09282be7e4d3374854126590f3dcebee6e" + }, + "usd": { + "revision": "4e0875f724d94d0c2ff36f9b8cb0b12f8b20d216" + }, + "uxntal": { + "revision": "ad9b638b914095320de85d59c49ab271603af048" + }, + "v": { + "revision": "7e11a6f8f369df935664fadd2f0c99d90fe3226f" + }, + "vala": { + "revision": "8f690bfa639f2b83d1fb938ed3dd98a7ba453e8b" + }, + "vento": { + "revision": "3321077d7446c1b3b017c294fd56ce028ed817fe" + }, + "verilog": { + "revision": "075ebfc84543675f12e79a955f79d717772dcef3" + }, + "vhs": { + "revision": "90028bbadb267ead5b87830380f6a825147f0c0f" + }, + "vim": { + "revision": "b448ca63f972ade12c373c808acdd2bf972937db" + }, + "vimdoc": { + "revision": "b711df784dd43d0a8ed8ddbfca0ddcc3239d94b4" + }, + "vue": { + "revision": "22bdfa6c9fc0f5ffa44c6e938ec46869ac8a99ff" + }, + "wgsl": { + "revision": "40259f3c77ea856841a4e0c4c807705f3e4a2b65" + }, + "wgsl_bevy": { + "revision": "1e12c7925c41bb09818d86e30cd78644fde7d31a" + }, + "wing": { + "revision": "bd1d35cf3e013dc7e189b46a593bdc2b281b0dd7" + }, + "wit": { + "revision": "cab94791450524a542324d8cbe8017d69c516d8e" + }, + "xcompose": { + "revision": "2383cc69a2c42cfade41c7cb971fb3862bec6df1" + }, + "xml": { + "revision": "648183d86f6f8ffb240ea11b4c6873f6f45d8b67" + }, + "yaml": { + "revision": "7b03feefd36b5f155465ca736c6304aca983b267" + }, + "yang": { + "revision": "2c0e6be8dd4dcb961c345fa35c309ad4f5bd3502" + }, + "yuck": { + "revision": "e877f6ade4b77d5ef8787075141053631ba12318" + }, + "zathurarc": { + "revision": "e9e8de071ab79ed1f6e3365f05fcf890b6d85a2f" + }, + "zig": { + "revision": "0d08703e4c3f426ec61695d7617415fff97029bd" + } +} diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter.lua new file mode 100644 index 00000000..963fe730 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter.lua @@ -0,0 +1,22 @@ +local install = require "nvim-treesitter.install" +local utils = require "nvim-treesitter.utils" +local info = require "nvim-treesitter.info" +local configs = require "nvim-treesitter.configs" +local statusline = require "nvim-treesitter.statusline" + +-- Registers all query predicates +require "nvim-treesitter.query_predicates" + +local M = {} + +function M.setup() + utils.setup_commands("install", install.commands) + utils.setup_commands("info", info.commands) + utils.setup_commands("configs", configs.commands) + configs.init() +end + +M.define_modules = configs.define_modules +M.statusline = statusline.statusline + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/caching.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/caching.lua new file mode 100644 index 00000000..7733202e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/caching.lua @@ -0,0 +1,71 @@ +local api = vim.api + +local M = {} + +-- Creates a cache table for buffers keyed by a type name. +-- Cache entries attach to the buffer and cleanup entries +-- as buffers are detached. +function M.create_buffer_cache() + local cache = {} + + ---@type table> + local items = setmetatable({}, { + __index = function(tbl, key) + rawset(tbl, key, {}) + return rawget(tbl, key) + end, + }) + + ---@type table + local loaded_buffers = {} + + ---@param type_name string + ---@param bufnr integer + ---@param value any + function cache.set(type_name, bufnr, value) + if not loaded_buffers[bufnr] then + loaded_buffers[bufnr] = true + -- Clean up the cache if the buffer is detached + -- to avoid memory leaks + api.nvim_buf_attach(bufnr, false, { + on_detach = function() + cache.clear_buffer(bufnr) + loaded_buffers[bufnr] = nil + return true + end, + on_reload = function() end, -- this is needed to prevent on_detach being called on buffer reload + }) + end + + items[bufnr][type_name] = value + end + + ---@param type_name string + ---@param bufnr integer + ---@return any + function cache.get(type_name, bufnr) + return items[bufnr][type_name] + end + + ---@param type_name string + ---@param bufnr integer + ---@return boolean + function cache.has(type_name, bufnr) + return cache.get(type_name, bufnr) ~= nil + end + + ---@param type_name string + ---@param bufnr integer + function cache.remove(type_name, bufnr) + items[bufnr][type_name] = nil + end + + ---@param bufnr integer + function cache.clear_buffer(bufnr) + items[bufnr] = nil + end + + return cache +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/compat.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/compat.lua new file mode 100644 index 00000000..0ad01003 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/compat.lua @@ -0,0 +1,39 @@ +-- Shim module to address deprecations across nvim versions +local ts = vim.treesitter +local tsq = ts.query + +local M = {} + +function M.get_query_files(lang, query_group, is_included) + return (tsq.get_files or tsq.get_query_files)(lang, query_group, is_included) +end + +function M.get_query(lang, query_name) + return (tsq.get or tsq.get_query)(lang, query_name) +end + +function M.parse_query(lang, query) + return (tsq.parse or tsq.parse_query)(lang, query) +end + +function M.get_range(node, source, metadata) + return (ts.get_range or tsq.get_range)(node, source, metadata) +end + +function M.get_node_text(node, bufnr) + return (ts.get_node_text or tsq.get_node_text)(node, bufnr) +end + +function M.require_language(lang, opts) + return (ts.language.add or ts.language.require_language)(lang, opts) +end + +function M.flatten(t) + if vim.fn.has "nvim-0.11" == 1 then + return vim.iter(t):flatten():totable() + else + return vim.tbl_flatten(t) + end +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/configs.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/configs.lua new file mode 100644 index 00000000..a3ec30fb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/configs.lua @@ -0,0 +1,616 @@ +local api = vim.api + +local queries = require "nvim-treesitter.query" +local ts = require "nvim-treesitter.compat" +local parsers = require "nvim-treesitter.parsers" +local utils = require "nvim-treesitter.utils" +local caching = require "nvim-treesitter.caching" + +local M = {} + +---@class TSConfig +---@field modules {[string]:TSModule} +---@field sync_install boolean +---@field ensure_installed string[]|string +---@field ignore_install string[] +---@field auto_install boolean +---@field parser_install_dir string|nil + +---@type TSConfig +local config = { + modules = {}, + sync_install = false, + ensure_installed = {}, + auto_install = false, + ignore_install = {}, + parser_install_dir = nil, +} + +-- List of modules that need to be setup on initialization. +---@type TSModule[][] +local queued_modules_defs = {} +-- Whether we've initialized the plugin yet. +local is_initialized = false + +---@class TSModule +---@field module_path string +---@field enable boolean|string[]|function(string): boolean +---@field disable boolean|string[]|function(string): boolean +---@field keymaps table +---@field is_supported function(string): boolean +---@field attach function(string) +---@field detach function(string) +---@field enabled_buffers table +---@field additional_vim_regex_highlighting boolean|string[] + +---@type {[string]: TSModule} +local builtin_modules = { + highlight = { + module_path = "nvim-treesitter.highlight", + -- @deprecated: use `highlight.set_custom_captures` instead + custom_captures = {}, + enable = false, + is_supported = function(lang) + return queries.has_highlights(lang) + end, + additional_vim_regex_highlighting = false, + }, + incremental_selection = { + module_path = "nvim-treesitter.incremental_selection", + enable = false, + keymaps = { + init_selection = "gnn", -- set to `false` to disable one of the mappings + node_incremental = "grn", + scope_incremental = "grc", + node_decremental = "grm", + }, + is_supported = function() + return true + end, + }, + indent = { + module_path = "nvim-treesitter.indent", + enable = false, + is_supported = queries.has_indents, + }, +} + +local attached_buffers_by_module = caching.create_buffer_cache() + +---Resolves a module by requiring the `module_path` or using the module definition. +---@param mod_name string +---@return TSModule|nil +local function resolve_module(mod_name) + local config_mod = M.get_module(mod_name) + + if not config_mod then + return + end + + if type(config_mod.attach) == "function" and type(config_mod.detach) == "function" then + return config_mod + elseif type(config_mod.module_path) == "string" then + return require(config_mod.module_path) + end +end + +---Enables and attaches the module to a buffer for lang. +---@param mod string path to module +---@param bufnr integer|nil buffer number, defaults to current buffer +---@param lang string|nil language, defaults to current language +local function enable_module(mod, bufnr, lang) + local module = M.get_module(mod) + if not module then + return + end + + bufnr = bufnr or api.nvim_get_current_buf() + lang = lang or parsers.get_buf_lang(bufnr) + + if not module.enable then + if module.enabled_buffers then + module.enabled_buffers[bufnr] = true + else + module.enabled_buffers = { [bufnr] = true } + end + end + + M.attach_module(mod, bufnr, lang) +end + +---Enables autocomands for the module. +---After the module is loaded `loaded` will be set to true for the module. +---@param mod string path to module +local function enable_mod_conf_autocmd(mod) + local config_mod = M.get_module(mod) + if not config_mod or config_mod.loaded then + return + end + + api.nvim_create_autocmd("FileType", { + group = api.nvim_create_augroup("NvimTreesitter-" .. mod, {}), + callback = function(args) + require("nvim-treesitter.configs").reattach_module(mod, args.buf) + end, + desc = "Reattach module", + }) + + config_mod.loaded = true +end + +---Enables the module globally and for all current buffers. +---After enabled, `enable` will be set to true for the module. +---@param mod string path to module +local function enable_all(mod) + local config_mod = M.get_module(mod) + if not config_mod then + return + end + + enable_mod_conf_autocmd(mod) + config_mod.enable = true + config_mod.enabled_buffers = nil + + for _, bufnr in pairs(api.nvim_list_bufs()) do + enable_module(mod, bufnr) + end +end + +---Disables and detaches the module for a buffer. +---@param mod string path to module +---@param bufnr integer buffer number, defaults to current buffer +local function disable_module(mod, bufnr) + local module = M.get_module(mod) + if not module then + return + end + + bufnr = bufnr or api.nvim_get_current_buf() + if module.enabled_buffers then + module.enabled_buffers[bufnr] = false + end + M.detach_module(mod, bufnr) +end + +---Disables autocomands for the module. +---After the module is unloaded `loaded` will be set to false for the module. +---@param mod string path to module +local function disable_mod_conf_autocmd(mod) + local config_mod = M.get_module(mod) + if not config_mod or not config_mod.loaded then + return + end + api.nvim_clear_autocmds { event = "FileType", group = "NvimTreesitter-" .. mod } + config_mod.loaded = false +end + +---Disables the module globally and for all current buffers. +---After disabled, `enable` will be set to false for the module. +---@param mod string path to module +local function disable_all(mod) + local config_mod = M.get_module(mod) + if not config_mod then + return + end + + config_mod.enabled_buffers = nil + disable_mod_conf_autocmd(mod) + config_mod.enable = false + + for _, bufnr in pairs(api.nvim_list_bufs()) do + disable_module(mod, bufnr) + end +end + +---Toggles a module for a buffer +---@param mod string path to module +---@param bufnr integer buffer number, defaults to current buffer +---@param lang string language, defaults to current language +local function toggle_module(mod, bufnr, lang) + bufnr = bufnr or api.nvim_get_current_buf() + lang = lang or parsers.get_buf_lang(bufnr) + + if attached_buffers_by_module.has(mod, bufnr) then + disable_module(mod, bufnr) + else + enable_module(mod, bufnr, lang) + end +end + +-- Toggles the module globally and for all current buffers. +-- @param mod path to module +local function toggle_all(mod) + local config_mod = M.get_module(mod) + if not config_mod then + return + end + + if config_mod.enable then + disable_all(mod) + else + enable_all(mod) + end +end + +---Recurses through all modules including submodules +---@param accumulator function called for each module +---@param root {[string]: TSModule}|nil root configuration table to start at +---@param path string|nil prefix path +local function recurse_modules(accumulator, root, path) + root = root or config.modules + + for name, module in pairs(root) do + local new_path = path and (path .. "." .. name) or name + + if M.is_module(module) then + accumulator(name, module, new_path, root) + elseif type(module) == "table" then + recurse_modules(accumulator, module, new_path) + end + end +end + +-- Shows current configuration of all nvim-treesitter modules +---@param process_function function used as the `process` parameter +--- for vim.inspect (https://github.com/kikito/inspect.lua#optionsprocess) +local function config_info(process_function) + process_function = process_function + or function(item, path) + if path[#path] == vim.inspect.METATABLE then + return + end + if path[#path] == "is_supported" then + return + end + return item + end + print(vim.inspect(config, { process = process_function })) +end + +---@param query_group string +---@param lang string +function M.edit_query_file(query_group, lang) + lang = lang or parsers.get_buf_lang() + local files = ts.get_query_files(lang, query_group, true) + if #files == 0 then + utils.notify "No query file found! Creating a new one!" + M.edit_query_file_user_after(query_group, lang) + elseif #files == 1 then + vim.cmd(":edit " .. files[1]) + else + vim.ui.select(files, { prompt = "Select a file:" }, function(file) + if file then + vim.cmd(":edit " .. file) + end + end) + end +end + +---@param query_group string +---@param lang string +function M.edit_query_file_user_after(query_group, lang) + lang = lang or parsers.get_buf_lang() + local folder = utils.join_path(vim.fn.stdpath "config", "after", "queries", lang) + local file = utils.join_path(folder, query_group .. ".scm") + if vim.fn.isdirectory(folder) ~= 1 then + vim.ui.select({ "Yes", "No" }, { prompt = '"' .. folder .. '" does not exist. Create it?' }, function(choice) + if choice == "Yes" then + vim.fn.mkdir(folder, "p", "0755") + vim.cmd(":edit " .. file) + end + end) + else + vim.cmd(":edit " .. file) + end +end + +M.commands = { + TSBufEnable = { + run = enable_module, + args = { + "-nargs=1", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSBufDisable = { + run = disable_module, + args = { + "-nargs=1", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSBufToggle = { + run = toggle_module, + args = { + "-nargs=1", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSEnable = { + run = enable_all, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSDisable = { + run = disable_all, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSToggle = { + run = toggle_all, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, + TSConfigInfo = { + run = config_info, + args = { + "-nargs=0", + }, + }, + TSEditQuery = { + run = M.edit_query_file, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#available_query_groups", + }, + }, + TSEditQueryUserAfter = { + run = M.edit_query_file_user_after, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#available_query_groups", + }, + }, +} + +---@param mod string module +---@param lang string the language of the buffer +---@param bufnr integer the buffer +function M.is_enabled(mod, lang, bufnr) + if not parsers.has_parser(lang) then + return false + end + + local module_config = M.get_module(mod) + if not module_config then + return false + end + + local buffer_enabled = module_config.enabled_buffers and module_config.enabled_buffers[bufnr] + local config_enabled = module_config.enable or buffer_enabled + if not config_enabled or not module_config.is_supported(lang) then + return false + end + + local disable = module_config.disable + if type(disable) == "function" then + if disable(lang, bufnr) then + return false + end + elseif type(disable) == "table" then + -- Otherwise it's a list of languages + for _, parser in pairs(disable) do + if lang == parser then + return false + end + end + end + + return true +end + +---Setup call for users to override module configurations. +---@param user_data TSConfig module overrides +function M.setup(user_data) + config.modules = vim.tbl_deep_extend("force", config.modules, user_data) + config.ignore_install = user_data.ignore_install or {} + config.parser_install_dir = user_data.parser_install_dir or nil + if config.parser_install_dir then + config.parser_install_dir = vim.fn.expand(config.parser_install_dir, ":p") + end + + config.auto_install = user_data.auto_install or false + if config.auto_install then + require("nvim-treesitter.install").setup_auto_install() + end + + local ensure_installed = user_data.ensure_installed or {} + if #ensure_installed > 0 then + if user_data.sync_install then + require("nvim-treesitter.install").ensure_installed_sync(ensure_installed) + else + require("nvim-treesitter.install").ensure_installed(ensure_installed) + end + end + + config.modules.ensure_installed = nil + config.ensure_installed = ensure_installed + + recurse_modules(function(_, _, new_path) + local data = utils.get_at_path(config.modules, new_path) + if data.enable then + enable_all(new_path) + end + end, config.modules) +end + +-- Defines a table of modules that can be attached/detached to buffers +-- based on language support. A module consist of the following properties: +---* @enable Whether the modules is enabled. Can be true or false. +---* @disable A list of languages to disable the module for. Only relevant if enable is true. +---* @keymaps A list of user mappings for a given module if relevant. +---* @is_supported A function which, given a ft, will return true if the ft works on the module. +---* @module_path A string path to a module file using `require`. The exported module must contain +--- an `attach` and `detach` function. This path is not required if `attach` and `detach` +--- functions are provided directly on the module definition. +---* @attach An attach function that is called for each buffer that the module is enabled for. This is required +--- if a `module_path` is not specified. +---* @detach A detach function that is called for each buffer that the module is enabled for. This is required +--- if a `module_path` is not specified. +-- +-- Modules are not setup until `init` is invoked by the plugin. This allows modules to be defined in any order +-- and can be loaded lazily. +-- +---* @example +---require"nvim-treesitter".define_modules { +--- my_cool_module = { +--- attach = function() +--- do_some_cool_setup() +--- end, +--- detach = function() +--- do_some_cool_teardown() +--- end +--- } +---} +---@param mod_defs TSModule[] +function M.define_modules(mod_defs) + if not is_initialized then + table.insert(queued_modules_defs, mod_defs) + return + end + + recurse_modules(function(key, mod, _, group) + group[key] = vim.tbl_extend("keep", mod, { + enable = false, + disable = {}, + is_supported = function() + return true + end, + }) + end, mod_defs) + + config.modules = vim.tbl_deep_extend("keep", config.modules, mod_defs) + + for _, mod in ipairs(M.available_modules(mod_defs)) do + local module_config = M.get_module(mod) + if module_config and module_config.enable then + enable_mod_conf_autocmd(mod) + end + end +end + +---Attaches a module to a buffer +---@param mod_name string the module name +---@param bufnr integer the buffer +---@param lang string the language of the buffer +function M.attach_module(mod_name, bufnr, lang) + bufnr = bufnr or api.nvim_get_current_buf() + lang = lang or parsers.get_buf_lang(bufnr) + local resolved_mod = resolve_module(mod_name) + + if resolved_mod and not attached_buffers_by_module.has(mod_name, bufnr) and M.is_enabled(mod_name, lang, bufnr) then + attached_buffers_by_module.set(mod_name, bufnr, true) + resolved_mod.attach(bufnr, lang) + end +end + +-- Detaches a module to a buffer +---@param mod_name string the module name +---@param bufnr integer the buffer +function M.detach_module(mod_name, bufnr) + local resolved_mod = resolve_module(mod_name) + bufnr = bufnr or api.nvim_get_current_buf() + + if resolved_mod and attached_buffers_by_module.has(mod_name, bufnr) then + attached_buffers_by_module.remove(mod_name, bufnr) + resolved_mod.detach(bufnr) + end +end + +-- Same as attach_module, but if the module is already attached, detach it first. +---@param mod_name string the module name +---@param bufnr integer the buffer +---@param lang string the language of the buffer +function M.reattach_module(mod_name, bufnr, lang) + M.detach_module(mod_name, bufnr) + M.attach_module(mod_name, bufnr, lang) +end + +-- Gets available modules +---@param root {[string]:TSModule}|nil table to find modules +---@return string[] modules list of module paths +function M.available_modules(root) + local modules = {} + + recurse_modules(function(_, _, path) + table.insert(modules, path) + end, root) + + return modules +end + +---Gets a module config by path +---@param mod_path string path to the module +---@return TSModule|nil: the module or nil +function M.get_module(mod_path) + local mod = utils.get_at_path(config.modules, mod_path) + + return M.is_module(mod) and mod or nil +end + +-- Determines whether the provided table is a module. +-- A module should contain an attach and detach function. +---@param mod table|nil the module table +---@return boolean +function M.is_module(mod) + return type(mod) == "table" + and ((type(mod.attach) == "function" and type(mod.detach) == "function") or type(mod.module_path) == "string") +end + +-- Initializes built-in modules and any queued modules +-- registered by plugins or the user. +function M.init() + is_initialized = true + M.define_modules(builtin_modules) + + for _, mod_def in ipairs(queued_modules_defs) do + M.define_modules(mod_def) + end +end + +-- If parser_install_dir is not nil is used or created. +-- If parser_install_dir is nil try the package dir of the nvim-treesitter +-- plugin first, followed by the "site" dir from "runtimepath". "site" dir will +-- be created if it doesn't exist. Using only the package dir won't work when +-- the plugin is installed with Nix, since the "/nix/store" is read-only. +---@param folder_name string|nil +---@return string|nil, string|nil +function M.get_parser_install_dir(folder_name) + folder_name = folder_name or "parser" + + local install_dir = config.parser_install_dir or utils.get_package_path() + local parser_dir = utils.join_path(install_dir, folder_name) + + return utils.create_or_reuse_writable_dir( + parser_dir, + utils.join_space("Could not create parser dir '", parser_dir, "': "), + utils.join_space( + "Parser dir '", + parser_dir, + "' should be read/write (see README on how to configure an alternative install location)" + ) + ) +end + +function M.get_parser_info_dir() + return M.get_parser_install_dir "parser-info" +end + +function M.get_ignored_parser_installs() + return config.ignore_install or {} +end + +function M.get_ensure_installed_parsers() + if type(config.ensure_installed) == "string" then + return { config.ensure_installed } + end + return config.ensure_installed or {} +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/fold.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/fold.lua new file mode 100644 index 00000000..75959987 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/fold.lua @@ -0,0 +1,123 @@ +local api = vim.api +local tsutils = require "nvim-treesitter.ts_utils" +local query = require "nvim-treesitter.query" +local parsers = require "nvim-treesitter.parsers" + +local M = {} + +-- This is cached on buf tick to avoid computing that multiple times +-- Especially not for every line in the file when `zx` is hit +local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr) + local max_fold_level = api.nvim_win_get_option(0, "foldnestmax") + local trim_level = function(level) + if level > max_fold_level then + return max_fold_level + end + return level + end + + local parser = parsers.get_parser(bufnr) + + if not parser then + return {} + end + + local matches = query.get_capture_matches_recursively(bufnr, function(lang) + if query.has_folds(lang) then + return "@fold", "folds" + elseif query.has_locals(lang) then + return "@scope", "locals" + end + end) + + -- start..stop is an inclusive range + + ---@type table + local start_counts = {} + ---@type table + local stop_counts = {} + + local prev_start = -1 + local prev_stop = -1 + + local min_fold_lines = api.nvim_win_get_option(0, "foldminlines") + + for _, match in ipairs(matches) do + local start, stop, stop_col ---@type integer, integer, integer + if match.metadata and match.metadata.range then + start, _, stop, stop_col = unpack(match.metadata.range) ---@type integer, integer, integer, integer + else + start, _, stop, stop_col = match.node:range() ---@type integer, integer, integer, integer + end + + if stop_col == 0 then + stop = stop - 1 + end + + local fold_length = stop - start + 1 + local should_fold = fold_length > min_fold_lines + + -- Fold only multiline nodes that are not exactly the same as previously met folds + -- Checking against just the previously found fold is sufficient if nodes + -- are returned in preorder or postorder when traversing tree + if should_fold and not (start == prev_start and stop == prev_stop) then + start_counts[start] = (start_counts[start] or 0) + 1 + stop_counts[stop] = (stop_counts[stop] or 0) + 1 + prev_start = start + prev_stop = stop + end + end + + ---@type string[] + local levels = {} + local current_level = 0 + + -- We now have the list of fold opening and closing, fill the gaps and mark where fold start + for lnum = 0, api.nvim_buf_line_count(bufnr) do + local prefix = "" + + local last_trimmed_level = trim_level(current_level) + current_level = current_level + (start_counts[lnum] or 0) + local trimmed_level = trim_level(current_level) + current_level = current_level - (stop_counts[lnum] or 0) + local next_trimmed_level = trim_level(current_level) + + -- Determine if it's the start/end of a fold + -- NB: vim's fold-expr interface does not have a mechanism to indicate that + -- two (or more) folds start at this line, so it cannot distinguish between + -- ( \n ( \n )) \n (( \n ) \n ) + -- versus + -- ( \n ( \n ) \n ( \n ) \n ) + -- If it did have such a mechanism, (trimmed_level - last_trimmed_level) + -- would be the correct number of starts to pass on. + if trimmed_level - last_trimmed_level > 0 then + prefix = ">" + elseif trimmed_level - next_trimmed_level > 0 then + -- Ending marks tend to confuse vim more than it helps, particularly when + -- the fold level changes by at least 2; we can uncomment this if + -- vim's behavior gets fixed. + -- prefix = "<" + prefix = "" + end + + levels[lnum + 1] = prefix .. tostring(trimmed_level) + end + + return levels +end) + +---@param lnum integer +---@return string +function M.get_fold_indic(lnum) + if not parsers.has_parser() or not lnum then + return "0" + end + + local buf = api.nvim_get_current_buf() + + local levels = folds_levels(buf) or {} + + return levels[lnum] or "0" +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/health.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/health.lua new file mode 100644 index 00000000..be265f7d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/health.lua @@ -0,0 +1,176 @@ +local api = vim.api +local fn = vim.fn + +local queries = require "nvim-treesitter.query" +local info = require "nvim-treesitter.info" +local shell = require "nvim-treesitter.shell_command_selectors" +local install = require "nvim-treesitter.install" +local utils = require "nvim-treesitter.utils" +local ts = require "nvim-treesitter.compat" + +local health = vim.health or require "health" + +-- "report_" prefix has been deprecated, use the recommended replacements if they exist. +local _start = health.start or health.report_start +local _ok = health.ok or health.report_ok +local _warn = health.warn or health.report_warn +local _error = health.error or health.report_error + +local M = {} + +local NVIM_TREESITTER_MINIMUM_ABI = 13 + +local function install_health() + _start "Installation" + + if fn.has "nvim-0.9.2" ~= 1 then + _error "Nvim-treesitter requires Nvim 0.9.2 or newer" + end + + if fn.executable "tree-sitter" == 0 then + _warn( + "`tree-sitter` executable not found (parser generator, only needed for :TSInstallFromGrammar," + .. " not required for :TSInstall)" + ) + else + _ok( + "`tree-sitter` found " + .. (utils.ts_cli_version() or "(unknown version)") + .. " (parser generator, only needed for :TSInstallFromGrammar)" + ) + end + + if fn.executable "node" == 0 then + _warn("`node` executable not found (only needed for :TSInstallFromGrammar," .. " not required for :TSInstall)") + else + local handle = io.popen "node --version" + local result = handle:read "*a" + handle:close() + local version = vim.split(result, "\n")[1] + _ok("`node` found " .. version .. " (only needed for :TSInstallFromGrammar)") + end + + if fn.executable "git" == 0 then + _error("`git` executable not found.", { + "Install it with your package manager.", + "Check that your `$PATH` is set correctly.", + }) + else + _ok "`git` executable found." + end + + local cc = shell.select_executable(install.compilers) + if not cc then + _error("`cc` executable not found.", { + "Check that any of " + .. vim.inspect(install.compilers) + .. " is in your $PATH" + .. ' or set the environment variable CC or `require"nvim-treesitter.install".compilers` explicitly!', + }) + else + local version = vim.fn.systemlist(cc .. (cc == "cl" and "" or " --version"))[1] + _ok( + "`" + .. cc + .. "` executable found. Selected from " + .. vim.inspect(install.compilers) + .. (version and ("\nVersion: " .. version) or "") + ) + end + if vim.treesitter.language_version then + if vim.treesitter.language_version >= NVIM_TREESITTER_MINIMUM_ABI then + _ok( + "Neovim was compiled with tree-sitter runtime ABI version " + .. vim.treesitter.language_version + .. " (required >=" + .. NVIM_TREESITTER_MINIMUM_ABI + .. "). Parsers must be compatible with runtime ABI." + ) + else + _error( + "Neovim was compiled with tree-sitter runtime ABI version " + .. vim.treesitter.language_version + .. ".\n" + .. "nvim-treesitter expects at least ABI version " + .. NVIM_TREESITTER_MINIMUM_ABI + .. "\n" + .. "Please make sure that Neovim is linked against are recent tree-sitter runtime when building" + .. " or raise an issue at your Neovim packager. Parsers must be compatible with runtime ABI." + ) + end + end + + _start("OS Info:\n" .. vim.inspect(vim.loop.os_uname())) +end + +local function query_status(lang, query_group) + local ok, err = pcall(queries.get_query, lang, query_group) + if not ok then + return "x", err + elseif not err then + return "." + else + return "✓" + end +end + +function M.check() + local error_collection = {} + -- Installation dependency checks + install_health() + queries.invalidate_query_cache() + -- Parser installation checks + local parser_installation = { "Parser/Features" .. string.rep(" ", 9) .. "H L F I J" } + for _, parser_name in pairs(info.installed_parsers()) do + local installed = #api.nvim_get_runtime_file("parser/" .. parser_name .. ".so", false) + + -- Only append information about installed parsers + if installed >= 1 then + local multiple_parsers = installed > 1 and "+" or "" + local out = " - " .. parser_name .. multiple_parsers .. string.rep(" ", 20 - (#parser_name + #multiple_parsers)) + for _, query_group in pairs(queries.built_in_query_groups) do + local status, err = query_status(parser_name, query_group) + out = out .. status .. " " + if err then + table.insert(error_collection, { parser_name, query_group, err }) + end + end + table.insert(parser_installation, vim.fn.trim(out, " ", 2)) + end + end + local legend = [[ + + Legend: H[ighlight], L[ocals], F[olds], I[ndents], In[j]ections + +) multiple parsers found, only one will be used + x) errors found in the query, try to run :TSUpdate {lang}]] + table.insert(parser_installation, legend) + -- Finally call the report function + _start(table.concat(parser_installation, "\n")) + if #error_collection > 0 then + _start "The following errors have been detected:" + for _, p in ipairs(error_collection) do + local lang, type, err = unpack(p) + local lines = {} + table.insert(lines, lang .. "(" .. type .. "): " .. err) + local files = ts.get_query_files(lang, type) + if #files > 0 then + table.insert(lines, lang .. "(" .. type .. ") is concatenated from the following files:") + for _, file in ipairs(files) do + local fd = io.open(file, "r") + if fd then + local ok, file_err = pcall(ts.parse_query, lang, fd:read "*a") + if ok then + table.insert(lines, '| [OK]:"' .. file .. '"') + else + table.insert(lines, '| [ERROR]:"' .. file .. '", failed to load: ' .. file_err) + end + fd:close() + end + end + end + _error(table.concat(lines, "\n")) + end + end +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/highlight.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/highlight.lua new file mode 100644 index 00000000..5a3cc2e8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/highlight.lua @@ -0,0 +1,49 @@ +local configs = require "nvim-treesitter.configs" + +local M = {} + +---@param config TSModule +---@param lang string +---@return boolean +local function should_enable_vim_regex(config, lang) + local additional_hl = config.additional_vim_regex_highlighting + local is_table = type(additional_hl) == "table" + + ---@diagnostic disable-next-line: param-type-mismatch + return additional_hl and (not is_table or vim.tbl_contains(additional_hl, lang)) +end + +---@param bufnr integer +---@param lang string +function M.attach(bufnr, lang) + local config = configs.get_module "highlight" + vim.treesitter.start(bufnr, lang) + if config and should_enable_vim_regex(config, lang) then + vim.bo[bufnr].syntax = "ON" + end +end + +---@param bufnr integer +function M.detach(bufnr) + vim.treesitter.stop(bufnr) +end + +---@deprecated +function M.start(...) + vim.notify( + "`nvim-treesitter.highlight.start` is deprecated: use `nvim-treesitter.highlight.attach` or `vim.treesitter.start`", + vim.log.levels.WARN + ) + M.attach(...) +end + +---@deprecated +function M.stop(...) + vim.notify( + "`nvim-treesitter.highlight.stop` is deprecated: use `nvim-treesitter.highlight.detach` or `vim.treesitter.stop`", + vim.log.levels.WARN + ) + M.detach(...) +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/incremental_selection.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/incremental_selection.lua new file mode 100644 index 00000000..78f0915c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/incremental_selection.lua @@ -0,0 +1,167 @@ +local api = vim.api + +local configs = require "nvim-treesitter.configs" +local ts_utils = require "nvim-treesitter.ts_utils" +local locals = require "nvim-treesitter.locals" +local parsers = require "nvim-treesitter.parsers" +local queries = require "nvim-treesitter.query" +local utils = require "nvim-treesitter.utils" + +local M = {} + +---@type table> +local selections = {} + +function M.init_selection() + local buf = api.nvim_get_current_buf() + local node = ts_utils.get_node_at_cursor() + selections[buf] = { [1] = node } + ts_utils.update_selection(buf, node) +end + +-- Get the range of the current visual selection. +-- +-- The range starts with 1 and the ending is inclusive. +---@return integer, integer, integer, integer +local function visual_selection_range() + local _, csrow, cscol, _ = unpack(vim.fn.getpos "v") ---@type integer, integer, integer, integer + local _, cerow, cecol, _ = unpack(vim.fn.getpos ".") ---@type integer, integer, integer, integer + + local start_row, start_col, end_row, end_col ---@type integer, integer, integer, integer + + if csrow < cerow or (csrow == cerow and cscol <= cecol) then + start_row = csrow + start_col = cscol + end_row = cerow + end_col = cecol + else + start_row = cerow + start_col = cecol + end_row = csrow + end_col = cscol + end + + return start_row, start_col, end_row, end_col +end + +---@param node TSNode +---@return boolean +local function range_matches(node) + local csrow, cscol, cerow, cecol = visual_selection_range() + local srow, scol, erow, ecol = ts_utils.get_vim_range { node:range() } + return srow == csrow and scol == cscol and erow == cerow and ecol == cecol +end + +---@param get_parent fun(node: TSNode): TSNode|nil +---@return fun():nil +local function select_incremental(get_parent) + return function() + local buf = api.nvim_get_current_buf() + local nodes = selections[buf] + + local csrow, cscol, cerow, cecol = visual_selection_range() + -- Initialize incremental selection with current selection + if not nodes or #nodes == 0 or not range_matches(nodes[#nodes]) then + local root = parsers.get_parser():parse()[1]:root() + local node = root:named_descendant_for_range(csrow - 1, cscol - 1, cerow - 1, cecol) + ts_utils.update_selection(buf, node) + if nodes and #nodes > 0 then + table.insert(selections[buf], node) + else + selections[buf] = { [1] = node } + end + return + end + + -- Find a node that changes the current selection. + local node = nodes[#nodes] ---@type TSNode + while true do + local parent = get_parent(node) + if not parent or parent == node then + -- Keep searching in the main tree + -- TODO: we should search on the parent tree of the current node. + local root = parsers.get_parser():parse()[1]:root() + parent = root:named_descendant_for_range(csrow - 1, cscol - 1, cerow - 1, cecol) + if not parent or root == node or parent == node then + ts_utils.update_selection(buf, node) + return + end + end + node = parent + local srow, scol, erow, ecol = ts_utils.get_vim_range { node:range() } + local same_range = (srow == csrow and scol == cscol and erow == cerow and ecol == cecol) + if not same_range then + table.insert(selections[buf], node) + if node ~= nodes[#nodes] then + table.insert(nodes, node) + end + ts_utils.update_selection(buf, node) + return + end + end + end +end + +M.node_incremental = select_incremental(function(node) + return node:parent() or node +end) + +M.scope_incremental = select_incremental(function(node) + local lang = parsers.get_buf_lang() + if queries.has_locals(lang) then + return locals.containing_scope(node:parent() or node) + else + return node + end +end) + +function M.node_decremental() + local buf = api.nvim_get_current_buf() + local nodes = selections[buf] + if not nodes or #nodes < 2 then + return + end + + table.remove(selections[buf]) + local node = nodes[#nodes] ---@type TSNode + ts_utils.update_selection(buf, node) +end + +local FUNCTION_DESCRIPTIONS = { + init_selection = "Start selecting nodes with nvim-treesitter", + node_incremental = "Increment selection to named node", + scope_incremental = "Increment selection to surrounding scope", + node_decremental = "Shrink selection to previous named node", +} + +---@param bufnr integer +function M.attach(bufnr) + local config = configs.get_module "incremental_selection" + for funcname, mapping in pairs(config.keymaps) do + if mapping then + local mode = funcname == "init_selection" and "n" or "x" + local rhs = M[funcname] ---@type function + + if not rhs then + utils.notify("Unknown keybinding: " .. funcname .. debug.traceback(), vim.log.levels.ERROR) + else + vim.keymap.set(mode, mapping, rhs, { buffer = bufnr, silent = true, desc = FUNCTION_DESCRIPTIONS[funcname] }) + end + end + end +end + +function M.detach(bufnr) + local config = configs.get_module "incremental_selection" + for f, mapping in pairs(config.keymaps) do + if mapping then + local mode = f == "init_selection" and "n" or "x" + local ok, err = pcall(vim.keymap.del, mode, mapping, { buffer = bufnr }) + if not ok then + utils.notify(string.format('%s "%s" for mode %s', err, mapping, mode), vim.log.levels.ERROR) + end + end + end +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/indent.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/indent.lua new file mode 100644 index 00000000..a9e5e6ce --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/indent.lua @@ -0,0 +1,359 @@ +local ts = vim.treesitter +local parsers = require "nvim-treesitter.parsers" + +local M = {} + +M.avoid_force_reparsing = { + yaml = true, +} + +M.comment_parsers = { + comment = true, + jsdoc = true, + phpdoc = true, +} + +local function getline(lnum) + return vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, false)[1] or "" +end + +---@param root TSNode +---@param lnum integer +---@param col? integer +---@return TSNode +local function get_first_node_at_line(root, lnum, col) + col = col or vim.fn.indent(lnum) + return root:descendant_for_range(lnum - 1, col, lnum - 1, col + 1) +end + +---@param root TSNode +---@param lnum integer +---@param col? integer +---@return TSNode +local function get_last_node_at_line(root, lnum, col) + col = col or (#getline(lnum) - 1) + return root:descendant_for_range(lnum - 1, col, lnum - 1, col + 1) +end + +---@param node TSNode +---@return number +local function node_length(node) + local _, _, start_byte = node:start() + local _, _, end_byte = node:end_() + return end_byte - start_byte +end + +---@param bufnr integer +---@param node TSNode +---@param delimiter string +---@return TSNode|nil child +---@return boolean|nil is_end +local function find_delimiter(bufnr, node, delimiter) + for child, _ in node:iter_children() do + if child:type() == delimiter then + local linenr = child:start() + local line = vim.api.nvim_buf_get_lines(bufnr, linenr, linenr + 1, false)[1] + local end_char = { child:end_() } + local trimmed_after_delim + local escaped_delimiter = delimiter:gsub("[%-%.%+%[%]%(%)%$%^%%%?%*]", "%%%1") + trimmed_after_delim, _ = line:sub(end_char[2] + 1):gsub("[%s" .. escaped_delimiter .. "]*", "") + return child, #trimmed_after_delim == 0 + end + end +end + +---Memoize a function using hash_fn to hash the arguments. +---@generic F: function +---@param fn F +---@param hash_fn fun(...): any +---@return F +local function memoize(fn, hash_fn) + local cache = setmetatable({}, { __mode = "kv" }) ---@type table + + return function(...) + local key = hash_fn(...) + if cache[key] == nil then + local v = fn(...) ---@type any + cache[key] = v ~= nil and v or vim.NIL + end + + local v = cache[key] + return v ~= vim.NIL and v or nil + end +end + +local get_indents = memoize(function(bufnr, root, lang) + local map = { + ["indent.auto"] = {}, + ["indent.begin"] = {}, + ["indent.end"] = {}, + ["indent.dedent"] = {}, + ["indent.branch"] = {}, + ["indent.ignore"] = {}, + ["indent.align"] = {}, + ["indent.zero"] = {}, + } + + --TODO(clason): remove when dropping Nvim 0.8 compat + local query = (ts.query.get or ts.get_query)(lang, "indents") + if not query then + return map + end + for id, node, metadata in query:iter_captures(root, bufnr) do + if query.captures[id]:sub(1, 1) ~= "_" then + map[query.captures[id]][node:id()] = metadata or {} + end + end + + return map +end, function(bufnr, root, lang) + return tostring(bufnr) .. root:id() .. "_" .. lang +end) + +---@param lnum number (1-indexed) +function M.get_indent(lnum) + local bufnr = vim.api.nvim_get_current_buf() + local parser = parsers.get_parser(bufnr) + if not parser or not lnum then + return -1 + end + + --TODO(clason): replace when dropping Nvim 0.8 compat + local root_lang = parsers.get_buf_lang(bufnr) + + -- some languages like Python will actually have worse results when re-parsing at opened new line + if not M.avoid_force_reparsing[root_lang] then + -- Reparse in case we got triggered by ":h indentkeys" + parser:parse { vim.fn.line "w0" - 1, vim.fn.line "w$" } + end + + -- Get language tree with smallest range around node that's not a comment parser + local root, lang_tree ---@type TSNode, LanguageTree + parser:for_each_tree(function(tstree, tree) + if not tstree or M.comment_parsers[tree:lang()] then + return + end + local local_root = tstree:root() + if ts.is_in_node_range(local_root, lnum - 1, 0) then + if not root or node_length(root) >= node_length(local_root) then + root = local_root + lang_tree = tree + end + end + end) + + -- Not likely, but just in case... + if not root then + return 0 + end + + local q = get_indents(vim.api.nvim_get_current_buf(), root, lang_tree:lang()) + local is_empty_line = string.match(getline(lnum), "^%s*$") ~= nil + local node ---@type TSNode + if is_empty_line then + local prevlnum = vim.fn.prevnonblank(lnum) + local indent = vim.fn.indent(prevlnum) + local prevline = vim.trim(getline(prevlnum)) + -- The final position can be trailing spaces, which should not affect indentation + node = get_last_node_at_line(root, prevlnum, indent + #prevline - 1) + if node:type():match "comment" then + -- The final node we capture of the previous line can be a comment node, which should also be ignored + -- Unless the last line is an entire line of comment, ignore the comment range and find the last node again + local first_node = get_first_node_at_line(root, prevlnum, indent) + local _, scol, _, _ = node:range() + if first_node:id() ~= node:id() then + -- In case the last captured node is a trailing comment node, re-trim the string + prevline = vim.trim(prevline:sub(1, scol - indent)) + -- Add back indent as indent of prevline was trimmed away + local col = indent + #prevline - 1 + node = get_last_node_at_line(root, prevlnum, col) + end + end + if q["indent.end"][node:id()] then + node = get_first_node_at_line(root, lnum) + end + else + node = get_first_node_at_line(root, lnum) + end + + local indent_size = vim.fn.shiftwidth() + local indent = 0 + local _, _, root_start = root:start() + if root_start ~= 0 then + -- injected tree + indent = vim.fn.indent(root:start() + 1) + end + + -- tracks to ensure multiple indent levels are not applied for same line + local is_processed_by_row = {} + + if q["indent.zero"][node:id()] then + return 0 + end + + while node do + -- do 'autoindent' if not marked as @indent + if + not q["indent.begin"][node:id()] + and not q["indent.align"][node:id()] + and q["indent.auto"][node:id()] + and node:start() < lnum - 1 + and lnum - 1 <= node:end_() + then + return -1 + end + + -- Do not indent if we are inside an @ignore block. + -- If a node spans from L1,C1 to L2,C2, we know that lines where L1 < line <= L2 would + -- have their indentations contained by the node. + if + not q["indent.begin"][node:id()] + and q["indent.ignore"][node:id()] + and node:start() < lnum - 1 + and lnum - 1 <= node:end_() + then + return 0 + end + + local srow, _, erow = node:range() + + local is_processed = false + + if + not is_processed_by_row[srow] + and ((q["indent.branch"][node:id()] and srow == lnum - 1) or (q["indent.dedent"][node:id()] and srow ~= lnum - 1)) + then + indent = indent - indent_size + is_processed = true + end + + -- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum) + local should_process = not is_processed_by_row[srow] + local is_in_err = false + if should_process then + local parent = node:parent() + is_in_err = parent and parent:has_error() + end + if + should_process + and ( + q["indent.begin"][node:id()] + and (srow ~= erow or is_in_err or q["indent.begin"][node:id()]["indent.immediate"]) + and (srow ~= lnum - 1 or q["indent.begin"][node:id()]["indent.start_at_same_line"]) + ) + then + indent = indent + indent_size + is_processed = true + end + + if is_in_err and not q["indent.align"][node:id()] then + -- only when the node is in error, promote the + -- first child's aligned indent to the error node + -- to work around ((ERROR "X" . (_)) @aligned_indent (#set! "delimiter" "AB")) + -- matching for all X, instead set do + -- (ERROR "X" @aligned_indent (#set! "delimiter" "AB") . (_)) + -- and we will fish it out here. + for c in node:iter_children() do + if q["indent.align"][c:id()] then + q["indent.align"][node:id()] = q["indent.align"][c:id()] + break + end + end + end + -- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum) + if should_process and q["indent.align"][node:id()] and (srow ~= erow or is_in_err) and (srow ~= lnum - 1) then + local metadata = q["indent.align"][node:id()] + local o_delim_node, o_is_last_in_line ---@type TSNode|nil, boolean|nil + local c_delim_node, c_is_last_in_line ---@type TSNode|nil, boolean|nil, boolean|nil + local indent_is_absolute = false + if metadata["indent.open_delimiter"] then + o_delim_node, o_is_last_in_line = find_delimiter(bufnr, node, metadata["indent.open_delimiter"]) + else + o_delim_node = node + end + if metadata["indent.close_delimiter"] then + c_delim_node, c_is_last_in_line = find_delimiter(bufnr, node, metadata["indent.close_delimiter"]) + else + c_delim_node = node + end + + if o_delim_node then + local o_srow, o_scol = o_delim_node:start() + local c_srow = nil + if c_delim_node then + c_srow, _ = c_delim_node:start() + end + if o_is_last_in_line then + -- hanging indent (previous line ended with starting delimiter) + -- should be processed like indent + if should_process then + indent = indent + indent_size * 1 + if c_is_last_in_line then + -- If current line is outside the range of a node marked with `@aligned_indent` + -- Then its indent level shouldn't be affected by `@aligned_indent` node + if c_srow and c_srow < lnum - 1 then + indent = math.max(indent - indent_size, 0) + end + end + end + else + -- aligned indent + if c_is_last_in_line and c_srow and o_srow ~= c_srow and c_srow < lnum - 1 then + -- If current line is outside the range of a node marked with `@aligned_indent` + -- Then its indent level shouldn't be affected by `@aligned_indent` node + indent = math.max(indent - indent_size, 0) + else + indent = o_scol + (metadata["indent.increment"] or 1) + indent_is_absolute = true + end + end + -- deal with the final line + local avoid_last_matching_next = false + if c_srow and c_srow ~= o_srow and c_srow == lnum - 1 then + -- delims end on current line, and are not open and closed same line. + -- then this last line may need additional indent to avoid clashes + -- with the next. `indent.avoid_last_matching_next` controls this behavior, + -- for example this is needed for function parameters. + avoid_last_matching_next = metadata["indent.avoid_last_matching_next"] or false + end + if avoid_last_matching_next then + -- last line must be indented more in cases where + -- it would be same indent as next line (we determine this as one + -- width more than the open indent to avoid confusing with any + -- hanging indents) + if indent <= vim.fn.indent(o_srow + 1) + indent_size then + indent = indent + indent_size * 1 + else + indent = indent + end + end + is_processed = true + if indent_is_absolute then + -- don't allow further indenting by parent nodes, this is an absolute position + return indent + end + end + end + + is_processed_by_row[srow] = is_processed_by_row[srow] or is_processed + + node = node:parent() + end + + return indent +end + +---@type table +local indent_funcs = {} + +---@param bufnr integer +function M.attach(bufnr) + indent_funcs[bufnr] = vim.bo.indentexpr + vim.bo.indentexpr = "nvim_treesitter#indent()" +end + +function M.detach(bufnr) + vim.bo.indentexpr = indent_funcs[bufnr] +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/info.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/info.lua new file mode 100644 index 00000000..6e94b357 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/info.lua @@ -0,0 +1,190 @@ +local api = vim.api +local configs = require "nvim-treesitter.configs" +local parsers = require "nvim-treesitter.parsers" + +local M = {} + +local function install_info() + local max_len = 0 + for _, ft in pairs(parsers.available_parsers()) do + if #ft > max_len then + max_len = #ft + end + end + + local parser_list = parsers.available_parsers() + table.sort(parser_list) + for _, lang in pairs(parser_list) do + local is_installed = #api.nvim_get_runtime_file("parser/" .. lang .. ".so", false) > 0 + api.nvim_out_write(lang .. string.rep(" ", max_len - #lang + 1)) + if is_installed then + api.nvim_out_write "[✓] installed\n" + elseif pcall(vim.treesitter.inspect_lang, lang) then + api.nvim_out_write "[✗] not installed (but still loaded. Restart Neovim!)\n" + else + api.nvim_out_write "[✗] not installed\n" + end + end +end + +-- Sort a list of modules into namespaces. +-- {'mod1', 'mod2.sub1', 'mod2.sub2', 'mod3'} +-- -> +-- { default = {'mod1', 'mod3'}, mod2 = {'sub1', 'sub2'}} +---@param modulelist string[] +---@return table +local function namespace_modules(modulelist) + local modules = {} + for _, module in ipairs(modulelist) do + if module:find "%." then + local namespace, submodule = module:match "^(.*)%.(.*)$" + if not modules[namespace] then + modules[namespace] = {} + end + table.insert(modules[namespace], submodule) + else + if not modules.default then + modules.default = {} + end + table.insert(modules.default, module) + end + end + return modules +end + +---@param list string[] +---@return integer length +local function longest_string_length(list) + local length = 0 + for _, value in ipairs(list) do + if #value > length then + length = #value + end + end + return length +end + +---@param curbuf integer +---@param origbuf integer +---@param parserlist string[] +---@param namespace string +---@param modulelist string[] +local function append_module_table(curbuf, origbuf, parserlist, namespace, modulelist) + local maxlen_parser = longest_string_length(parserlist) + table.sort(modulelist) + + -- header + local header = ">> " .. namespace .. string.rep(" ", maxlen_parser - #namespace - 1) + for _, module in pairs(modulelist) do + header = header .. module .. " " + end + api.nvim_buf_set_lines(curbuf, -1, -1, true, { header }) + + -- actual table + for _, parser in ipairs(parserlist) do + local padding = string.rep(" ", maxlen_parser - #parser + 2) + local line = parser .. padding + local namespace_prefix = (namespace == "default") and "" or namespace .. "." + for _, module in pairs(modulelist) do + local modlen = #module + module = namespace_prefix .. module + if configs.is_enabled(module, parser, origbuf) then + line = line .. "✓" + else + line = line .. "✗" + end + line = line .. string.rep(" ", modlen + 1) + end + api.nvim_buf_set_lines(curbuf, -1, -1, true, { line }) + end + + api.nvim_buf_set_lines(curbuf, -1, -1, true, { "" }) +end + +local function print_info_modules(parserlist, module) + local origbuf = api.nvim_get_current_buf() + api.nvim_command "enew" + local curbuf = api.nvim_get_current_buf() + + local modules + if module then + modules = namespace_modules { module } + else + modules = namespace_modules(configs.available_modules()) + end + + ---@type string[] + local namespaces = {} + for k, _ in pairs(modules) do + table.insert(namespaces, k) + end + table.sort(namespaces) + + table.sort(parserlist) + for _, namespace in ipairs(namespaces) do + append_module_table(curbuf, origbuf, parserlist, namespace, modules[namespace]) + end + + api.nvim_buf_set_option(curbuf, "modified", false) + api.nvim_buf_set_option(curbuf, "buftype", "nofile") + vim.cmd [[ + syntax match TSModuleInfoGood /✓/ + syntax match TSModuleInfoBad /✗/ + syntax match TSModuleInfoHeader /^>>.*$/ contains=TSModuleInfoNamespace + syntax match TSModuleInfoNamespace /^>> \w*/ contained + syntax match TSModuleInfoParser /^[^> ]*\ze / + ]] + + local highlights = { + TSModuleInfoGood = { fg = "LightGreen", bold = true, default = true }, + TSModuleInfoBad = { fg = "Crimson", default = true }, + TSModuleInfoHeader = { link = "Type", default = true }, + TSModuleInfoNamespace = { link = "Statement", default = true }, + TSModuleInfoParser = { link = "Identifier", default = true }, + } + for k, v in pairs(highlights) do + api.nvim_set_hl(0, k, v) + end +end + +local function module_info(module) + if module and not configs.get_module(module) then + return + end + + local parserlist = parsers.available_parsers() + if module then + print_info_modules(parserlist, module) + else + print_info_modules(parserlist) + end +end + +---@return string[] +function M.installed_parsers() + local installed = {} + for _, p in pairs(parsers.available_parsers()) do + if parsers.has_parser(p) then + table.insert(installed, p) + end + end + return installed +end + +M.commands = { + TSInstallInfo = { + run = install_info, + args = { + "-nargs=0", + }, + }, + TSModuleInfo = { + run = module_info, + args = { + "-nargs=?", + "-complete=custom,nvim_treesitter#available_modules", + }, + }, +} + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/install.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/install.lua new file mode 100644 index 00000000..ec7d6245 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/install.lua @@ -0,0 +1,777 @@ +local api = vim.api +local fn = vim.fn +local luv = vim.loop + +local utils = require "nvim-treesitter.utils" +local parsers = require "nvim-treesitter.parsers" +local info = require "nvim-treesitter.info" +local configs = require "nvim-treesitter.configs" +local shell = require "nvim-treesitter.shell_command_selectors" +local compat = require "nvim-treesitter.compat" + +local M = {} + +---@class LockfileInfo +---@field revision string + +---@type table +local lockfile = {} + +M.compilers = { vim.fn.getenv "CC", "cc", "gcc", "clang", "cl", "zig" } +M.prefer_git = fn.has "win32" == 1 +M.command_extra_args = {} +M.ts_generate_args = nil + +local started_commands = 0 +local finished_commands = 0 +local failed_commands = 0 +local complete_std_output = {} +local complete_error_output = {} + +local function reset_progress_counter() + if started_commands ~= finished_commands then + return + end + started_commands = 0 + finished_commands = 0 + failed_commands = 0 + complete_std_output = {} + complete_error_output = {} +end + +local function get_job_status() + return "[nvim-treesitter] [" + .. finished_commands + .. "/" + .. started_commands + .. (failed_commands > 0 and ", failed: " .. failed_commands or "") + .. "]" +end + +---@param lang string +---@return function +local function reattach_if_possible_fn(lang, error_on_fail) + return function() + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + if parsers.get_buf_lang(buf) == lang then + vim._ts_remove_language(lang) + local ok, err + if vim.treesitter.language.add then + local ft = vim.bo[buf].filetype + ok, err = pcall(vim.treesitter.language.add, lang, { filetype = ft }) + else + ok, err = pcall(compat.require_language, lang) + end + if not ok and error_on_fail then + vim.notify("Could not load parser for " .. lang .. ": " .. vim.inspect(err)) + end + for _, mod in ipairs(require("nvim-treesitter.configs").available_modules()) do + if ok then + require("nvim-treesitter.configs").reattach_module(mod, buf, lang) + else + require("nvim-treesitter.configs").detach_module(mod, buf) + end + end + end + end + end +end + +---@param lang string +---@param validate boolean|nil +---@return InstallInfo +local function get_parser_install_info(lang, validate) + local parser_config = parsers.get_parser_configs()[lang] + + if not parser_config then + error('Parser not available for language "' .. lang .. '"') + end + + local install_info = parser_config.install_info + + if validate then + vim.validate { + url = { install_info.url, "string" }, + files = { install_info.files, "table" }, + } + end + + return install_info +end + +local function load_lockfile() + local filename = utils.join_path(utils.get_package_path(), "lockfile.json") + lockfile = vim.fn.filereadable(filename) == 1 and vim.fn.json_decode(vim.fn.readfile(filename)) or {} +end + +local function is_ignored_parser(lang) + return vim.tbl_contains(configs.get_ignored_parser_installs(), lang) +end + +---@param lang string +---@return string|nil +local function get_revision(lang) + if #lockfile == 0 then + load_lockfile() + end + + local install_info = get_parser_install_info(lang) + if install_info.revision then + return install_info.revision + end + + if lockfile[lang] then + return lockfile[lang].revision + end +end + +---@param lang string +---@return string|nil +local function get_installed_revision(lang) + local lang_file = utils.join_path(configs.get_parser_info_dir(), lang .. ".revision") + if vim.fn.filereadable(lang_file) == 1 then + return vim.fn.readfile(lang_file)[1] + end +end + +-- Clean path for use in a prefix comparison +---@param input string +---@return string +local function clean_path(input) + local pth = vim.fn.fnamemodify(input, ":p") + if fn.has "win32" == 1 then + pth = pth:gsub("/", "\\") + end + return pth +end + +-- Checks if parser is installed with nvim-treesitter +---@param lang string +---@return boolean +local function is_installed(lang) + local matched_parsers = vim.api.nvim_get_runtime_file("parser/" .. lang .. ".so", true) or {} + local install_dir = configs.get_parser_install_dir() + if not install_dir then + return false + end + install_dir = clean_path(install_dir) + for _, path in ipairs(matched_parsers) do + local abspath = clean_path(path) + if vim.startswith(abspath, install_dir) then + return true + end + end + return false +end + +---@param lang string +---@return boolean +local function needs_update(lang) + local revision = get_revision(lang) + return not revision or revision ~= get_installed_revision(lang) +end + +---@return string[] +local function outdated_parsers() + return vim.tbl_filter(function(lang) ---@param lang string + return is_installed(lang) and needs_update(lang) + end, info.installed_parsers()) +end + +---@param handle userdata +---@param is_stderr boolean +local function onread(handle, is_stderr) + return function(_, data) + if data then + if is_stderr then + complete_error_output[handle] = (complete_error_output[handle] or "") .. data + else + complete_std_output[handle] = (complete_std_output[handle] or "") .. data + end + end + end +end + +function M.iter_cmd(cmd_list, i, lang, success_message) + if i == 1 then + started_commands = started_commands + 1 + end + if i == #cmd_list + 1 then + finished_commands = finished_commands + 1 + return print(get_job_status() .. " " .. success_message) + end + + local attr = cmd_list[i] + if attr.info then + print(get_job_status() .. " " .. attr.info) + end + + if attr.opts and attr.opts.args and M.command_extra_args[attr.cmd] then + vim.list_extend(attr.opts.args, M.command_extra_args[attr.cmd]) + end + + if type(attr.cmd) == "function" then + local ok, err = pcall(attr.cmd) + if ok then + M.iter_cmd(cmd_list, i + 1, lang, success_message) + else + failed_commands = failed_commands + 1 + finished_commands = finished_commands + 1 + return api.nvim_err_writeln( + (attr.err or ("Failed to execute the following command:\n" .. vim.inspect(attr))) .. "\n" .. vim.inspect(err) + ) + end + else + local handle + local stdout = luv.new_pipe(false) + local stderr = luv.new_pipe(false) + attr.opts.stdio = { nil, stdout, stderr } + ---@type userdata + handle = luv.spawn( + attr.cmd, + attr.opts, + vim.schedule_wrap(function(code) + if code ~= 0 then + stdout:read_stop() + stderr:read_stop() + end + stdout:close() + stderr:close() + handle:close() + if code ~= 0 then + failed_commands = failed_commands + 1 + finished_commands = finished_commands + 1 + if complete_std_output[handle] and complete_std_output[handle] ~= "" then + print(complete_std_output[handle]) + end + + local err_msg = complete_error_output[handle] or "" + api.nvim_err_writeln( + "nvim-treesitter[" + .. lang + .. "]: " + .. (attr.err or ("Failed to execute the following command:\n" .. vim.inspect(attr))) + .. "\n" + .. err_msg + ) + return + end + M.iter_cmd(cmd_list, i + 1, lang, success_message) + end) + ) + luv.read_start(stdout, onread(handle, false)) + luv.read_start(stderr, onread(handle, true)) + end +end + +---@param cmd Command +---@return string command +local function get_command(cmd) + local options = "" + if cmd.opts and cmd.opts.args then + if M.command_extra_args[cmd.cmd] then + vim.list_extend(cmd.opts.args, M.command_extra_args[cmd.cmd]) + end + for _, opt in ipairs(cmd.opts.args) do + options = string.format("%s %s", options, opt) + end + end + + local command = string.format("%s %s", cmd.cmd, options) + if cmd.opts and cmd.opts.cwd then + command = shell.make_directory_change_for_command(cmd.opts.cwd, command) + end + return command +end + +---@param cmd_list Command[] +---@return boolean +local function iter_cmd_sync(cmd_list) + for _, cmd in ipairs(cmd_list) do + if cmd.info then + print(cmd.info) + end + + if type(cmd.cmd) == "function" then + cmd.cmd() + else + local ret = vim.fn.system(get_command(cmd)) + if vim.v.shell_error ~= 0 then + print(ret) + api.nvim_err_writeln( + (cmd.err and cmd.err .. "\n" or "") .. "Failed to execute the following command:\n" .. vim.inspect(cmd) + ) + return false + end + end + end + + return true +end + +---@param cache_folder string +---@param install_folder string +---@param lang string +---@param repo InstallInfo +---@param with_sync boolean +---@param generate_from_grammar boolean +local function run_install(cache_folder, install_folder, lang, repo, with_sync, generate_from_grammar) + parsers.reset_cache() + + local path_sep = utils.get_path_sep() + + local project_name = "tree-sitter-" .. lang + local maybe_local_path = vim.fn.expand(repo.url) + local from_local_path = vim.fn.isdirectory(maybe_local_path) == 1 + if from_local_path then + repo.url = maybe_local_path + end + + ---@type string compile_location only needed for typescript installs. + local compile_location + if from_local_path then + compile_location = repo.url + if repo.location then + compile_location = utils.join_path(compile_location, repo.location) + end + else + local repo_location = project_name + if repo.location then + repo_location = repo_location .. "/" .. repo.location + end + repo_location = repo_location:gsub("/", path_sep) + compile_location = utils.join_path(cache_folder, repo_location) + end + local parser_lib_name = utils.join_path(install_folder, lang) .. ".so" + + generate_from_grammar = repo.requires_generate_from_grammar or generate_from_grammar + + if generate_from_grammar and vim.fn.executable "tree-sitter" ~= 1 then + api.nvim_err_writeln "tree-sitter CLI not found: `tree-sitter` is not executable!" + if repo.requires_generate_from_grammar then + api.nvim_err_writeln( + "tree-sitter CLI is needed because `" + .. lang + .. "` is marked that it needs " + .. "to be generated from the grammar definitions to be compatible with nvim!" + ) + end + return + else + if not M.ts_generate_args then + local ts_cli_version = utils.ts_cli_version() + if ts_cli_version and vim.split(ts_cli_version, " ")[1] > "0.20.2" then + M.ts_generate_args = { "generate", "--no-bindings", "--abi", vim.treesitter.language_version } + else + M.ts_generate_args = { "generate", "--no-bindings" } + end + end + end + if generate_from_grammar and vim.fn.executable "node" ~= 1 then + api.nvim_err_writeln "Node JS not found: `node` is not executable!" + return + end + local cc = shell.select_executable(M.compilers) + if not cc then + api.nvim_err_writeln('No C compiler found! "' .. table.concat( + vim.tbl_filter(function(c) ---@param c string + return type(c) == "string" + end, M.compilers), + '", "' + ) .. '" are not executable.') + return + end + + local revision = repo.revision + if not revision then + revision = get_revision(lang) + end + + ---@class Command + ---@field cmd string + ---@field info string + ---@field err string + ---@field opts CmdOpts + + ---@class CmdOpts + ---@field args string[] + ---@field cwd string + + ---@type Command[] + local command_list = {} + if not from_local_path then + vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) }) + vim.list_extend( + command_list, + shell.select_download_commands(repo, project_name, cache_folder, revision, M.prefer_git) + ) + end + if generate_from_grammar then + if repo.generate_requires_npm then + if vim.fn.executable "npm" ~= 1 then + api.nvim_err_writeln("`" .. lang .. "` requires NPM to be installed from grammar.js") + return + end + vim.list_extend(command_list, { + { + cmd = "npm", + info = "Installing NPM dependencies of " .. lang .. " parser", + err = "Error during `npm install` (required for parser generation of " .. lang .. " with npm dependencies)", + opts = { + args = { "install" }, + cwd = compile_location, + }, + }, + }) + end + vim.list_extend(command_list, { + { + cmd = vim.fn.exepath "tree-sitter", + info = "Generating source files from grammar.js...", + err = 'Error during "tree-sitter generate"', + opts = { + args = M.ts_generate_args, + cwd = compile_location, + }, + }, + }) + end + vim.list_extend(command_list, { + shell.select_compile_command(repo, cc, compile_location), + shell.select_mv_cmd("parser.so", parser_lib_name, compile_location), + { + cmd = function() + vim.fn.writefile({ revision or "" }, utils.join_path(configs.get_parser_info_dir() or "", lang .. ".revision")) + end, + }, + { -- auto-attach modules after installation + cmd = reattach_if_possible_fn(lang, true), + }, + }) + if not from_local_path then + vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) }) + end + + if with_sync then + if iter_cmd_sync(command_list) == true then + print("Treesitter parser for " .. lang .. " has been installed") + end + else + M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been installed") + end +end + +---@param lang string +---@param ask_reinstall boolean|string +---@param cache_folder string +---@param install_folder string +---@param with_sync boolean +---@param generate_from_grammar boolean +local function install_lang(lang, ask_reinstall, cache_folder, install_folder, with_sync, generate_from_grammar) + if is_installed(lang) and ask_reinstall ~= "force" then + if not ask_reinstall then + return + end + + local yesno = fn.input(lang .. " parser already available: would you like to reinstall ? y/n: ") + print "\n " + if not string.match(yesno, "^y.*") then + return + end + end + + local ok, install_info = pcall(get_parser_install_info, lang, true) + if not ok then + vim.notify("Installation not possible: " .. install_info, vim.log.levels.ERROR) + if not parsers.get_parser_configs()[lang] then + vim.notify( + "See https://github.com/nvim-treesitter/nvim-treesitter/#adding-parsers on how to add a new parser!", + vim.log.levels.INFO + ) + end + return + end + + run_install(cache_folder, install_folder, lang, install_info, with_sync, generate_from_grammar) +end + +---@class InstallOptions +---@field with_sync boolean +---@field ask_reinstall boolean|string +---@field generate_from_grammar boolean +---@field exclude_configured_parsers boolean + +-- Install a parser +---@param options? InstallOptions +---@return function +local function install(options) + options = options or {} + local with_sync = options.with_sync + local ask_reinstall = options.ask_reinstall + local generate_from_grammar = options.generate_from_grammar + local exclude_configured_parsers = options.exclude_configured_parsers + + return function(...) + if fn.executable "git" == 0 then + return api.nvim_err_writeln "Git is required on your system to run this command" + end + + local cache_folder, err = utils.get_cache_dir() + if err then + return api.nvim_err_writeln(err) + end + assert(cache_folder) + + local install_folder + install_folder, err = configs.get_parser_install_dir() + if err then + return api.nvim_err_writeln(err) + end + install_folder = install_folder and clean_path(install_folder) + assert(install_folder) + + local languages ---@type string[] + local ask ---@type boolean|string + if ... == "all" then + languages = parsers.available_parsers() + ask = false + else + languages = compat.flatten { ... } + ask = ask_reinstall + end + + if exclude_configured_parsers then + languages = utils.difference(languages, configs.get_ignored_parser_installs()) + end + + if #languages > 1 then + reset_progress_counter() + end + + for _, lang in ipairs(languages) do + install_lang(lang, ask, cache_folder, install_folder, with_sync, generate_from_grammar) + end + end +end + +function M.setup_auto_install() + vim.api.nvim_create_autocmd("FileType", { + pattern = { "*" }, + group = vim.api.nvim_create_augroup("NvimTreesitter-auto_install", { clear = true }), + callback = function() + local lang = parsers.get_buf_lang() + if parsers.get_parser_configs()[lang] and not is_installed(lang) and not is_ignored_parser(lang) then + install() { lang } + end + end, + }) +end + +function M.update(options) + options = options or {} + return function(...) + M.lockfile = {} + reset_progress_counter() + if ... and ... ~= "all" then + ---@type string[] + local languages = compat.flatten { ... } + local installed = 0 + for _, lang in ipairs(languages) do + if (not is_installed(lang)) or (needs_update(lang)) then + installed = installed + 1 + install { + ask_reinstall = "force", + with_sync = options.with_sync, + }(lang) + end + end + if installed == 0 then + utils.notify "Parsers are up-to-date!" + end + else + local parsers_to_update = outdated_parsers() or info.installed_parsers() + if #parsers_to_update == 0 then + utils.notify "All parsers are up-to-date!" + end + for _, lang in pairs(parsers_to_update) do + install { + ask_reinstall = "force", + exclude_configured_parsers = true, + with_sync = options.with_sync, + }(lang) + end + end + end +end + +function M.uninstall(...) + if vim.tbl_contains({ "all" }, ...) then + reset_progress_counter() + local installed = info.installed_parsers() + M.uninstall(installed) + elseif ... then + local ensure_installed_parsers = configs.get_ensure_installed_parsers() + if ensure_installed_parsers == "all" then + ensure_installed_parsers = parsers.available_parsers() + end + ensure_installed_parsers = utils.difference(ensure_installed_parsers, configs.get_ignored_parser_installs()) + + ---@type string[] + local languages = compat.flatten { ... } + for _, lang in ipairs(languages) do + local install_dir, err = configs.get_parser_install_dir() + if err then + return api.nvim_err_writeln(err) + end + install_dir = install_dir and clean_path(install_dir) + + if vim.tbl_contains(ensure_installed_parsers, lang) then + vim.notify( + "Uninstalling " + .. lang + .. '. But the parser is still configured in "ensure_installed" setting of nvim-treesitter.' + .. " Please consider updating your config!", + vim.log.levels.ERROR + ) + end + + local parser_lib = utils.join_path(install_dir, lang) .. ".so" + local all_parsers = vim.api.nvim_get_runtime_file("parser/" .. lang .. ".so", true) + if vim.fn.filereadable(parser_lib) == 1 then + local command_list = { + shell.select_rm_file_cmd(parser_lib, "Uninstalling parser for " .. lang), + { + cmd = function() + local all_parsers_after_deletion = vim.api.nvim_get_runtime_file("parser/" .. lang .. ".so", true) + if #all_parsers_after_deletion > 0 then + vim.notify( + "Tried to uninstall parser for " + .. lang + .. "! But the parser is still installed (not by nvim-treesitter):" + .. table.concat(all_parsers_after_deletion, ", "), + vim.log.levels.ERROR + ) + end + end, + }, + { -- auto-reattach or detach modules after uninstallation + cmd = reattach_if_possible_fn(lang, false), + }, + } + M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been uninstalled") + elseif #all_parsers > 0 then + vim.notify( + "Parser for " + .. lang + .. " is installed! But not by nvim-treesitter! Please manually remove the following files: " + .. table.concat(all_parsers, ", "), + vim.log.levels.ERROR + ) + end + end + end +end + +function M.write_lockfile(verbose, skip_langs) + local sorted_parsers = {} ---@type Parser[] + -- Load previous lockfile + load_lockfile() + skip_langs = skip_langs or {} + + for k, v in pairs(parsers.get_parser_configs()) do + table.insert(sorted_parsers, { name = k, parser = v }) + end + + ---@param a Parser + ---@param b Parser + table.sort(sorted_parsers, function(a, b) + return a.name < b.name + end) + + for _, v in ipairs(sorted_parsers) do + if not vim.tbl_contains(skip_langs, v.name) then + -- I'm sure this can be done in aync way with iter_cmd + local sha ---@type string + if v.parser.install_info.branch then + sha = vim.split( + vim.fn.systemlist( + "git ls-remote " .. v.parser.install_info.url .. " | grep refs/heads/" .. v.parser.install_info.branch + )[1], + "\t" + )[1] + else + sha = vim.split(vim.fn.systemlist("git ls-remote " .. v.parser.install_info.url)[1], "\t")[1] + end + lockfile[v.name] = { revision = sha } + if verbose then + print(v.name .. ": " .. sha) + end + else + print("Skipping " .. v.name) + end + end + + if verbose then + print(vim.inspect(lockfile)) + end + vim.fn.writefile( + vim.fn.split(vim.fn.json_encode(lockfile), "\n"), + utils.join_path(utils.get_package_path(), "lockfile.json") + ) +end + +M.ensure_installed = install { exclude_configured_parsers = true } +M.ensure_installed_sync = install { with_sync = true, exclude_configured_parsers = true } + +M.commands = { + TSInstall = { + run = install { ask_reinstall = true }, + ["run!"] = install { ask_reinstall = "force" }, + args = { + "-nargs=+", + "-bang", + "-complete=custom,nvim_treesitter#installable_parsers", + }, + }, + TSInstallFromGrammar = { + run = install { generate_from_grammar = true, ask_reinstall = true }, + ["run!"] = install { generate_from_grammar = true, ask_reinstall = "force" }, + args = { + "-nargs=+", + "-bang", + "-complete=custom,nvim_treesitter#installable_parsers", + }, + }, + TSInstallSync = { + run = install { with_sync = true, ask_reinstall = true }, + ["run!"] = install { with_sync = true, ask_reinstall = "force" }, + args = { + "-nargs=+", + "-bang", + "-complete=custom,nvim_treesitter#installable_parsers", + }, + }, + TSUpdate = { + run = M.update {}, + args = { + "-nargs=*", + "-complete=custom,nvim_treesitter#installed_parsers", + }, + }, + TSUpdateSync = { + run = M.update { with_sync = true }, + args = { + "-nargs=*", + "-complete=custom,nvim_treesitter#installed_parsers", + }, + }, + TSUninstall = { + run = M.uninstall, + args = { + "-nargs=+", + "-complete=custom,nvim_treesitter#installed_parsers", + }, + }, +} + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/locals.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/locals.lua new file mode 100644 index 00000000..fed835bb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/locals.lua @@ -0,0 +1,365 @@ +-- Functions to handle locals +-- Locals are a generalization of definition and scopes +-- its the way nvim-treesitter uses to "understand" the code + +local queries = require "nvim-treesitter.query" +local ts_utils = require "nvim-treesitter.ts_utils" +local ts = vim.treesitter +local api = vim.api + +local M = {} + +function M.collect_locals(bufnr) + return queries.collect_group_results(bufnr, "locals") +end + +-- Iterates matches from a locals query file. +-- @param bufnr the buffer +-- @param root the root node +function M.iter_locals(bufnr, root) + return queries.iter_group_results(bufnr, "locals", root) +end + +---@param bufnr integer +---@return any +function M.get_locals(bufnr) + return queries.get_matches(bufnr, "locals") +end + +-- Creates unique id for a node based on text and range +---@param scope TSNode: the scope node of the definition +---@param node_text string: the node text to use +---@return string: a string id +function M.get_definition_id(scope, node_text) + -- Add a valid starting character in case node text doesn't start with a valid one. + return table.concat({ "k", node_text or "", scope:range() }, "_") +end + +function M.get_definitions(bufnr) + local locals = M.get_locals(bufnr) + + local defs = {} + + for _, loc in ipairs(locals) do + if loc["local"]["definition"] then + table.insert(defs, loc["local"]["definition"]) + end + end + + return defs +end + +function M.get_scopes(bufnr) + local locals = M.get_locals(bufnr) + + local scopes = {} + + for _, loc in ipairs(locals) do + if loc["local"]["scope"] and loc["local"]["scope"].node then + table.insert(scopes, loc["local"]["scope"].node) + end + end + + return scopes +end + +function M.get_references(bufnr) + local locals = M.get_locals(bufnr) + + local refs = {} + + for _, loc in ipairs(locals) do + if loc["local"]["reference"] and loc["local"]["reference"].node then + table.insert(refs, loc["local"]["reference"].node) + end + end + + return refs +end + +-- Gets a table with all the scopes containing a node +-- The order is from most specific to least (bottom up) +---@param node TSNode +---@param bufnr integer +---@return TSNode[] +function M.get_scope_tree(node, bufnr) + local scopes = {} ---@type TSNode[] + + for scope in M.iter_scope_tree(node, bufnr) do + table.insert(scopes, scope) + end + + return scopes +end + +-- Iterates over a nodes scopes moving from the bottom up +---@param node TSNode +---@param bufnr integer +---@return fun(): TSNode|nil +function M.iter_scope_tree(node, bufnr) + local last_node = node + return function() + if not last_node then + return + end + + local scope = M.containing_scope(last_node, bufnr, false) or ts_utils.get_root_for_node(node) + + last_node = scope:parent() + + return scope + end +end + +-- Gets a table of all nodes and their 'kinds' from a locals list +---@param local_def any: the local list result +---@return table: a list of node entries +function M.get_local_nodes(local_def) + local result = {} + + M.recurse_local_nodes(local_def, function(def, _node, kind) + table.insert(result, vim.tbl_extend("keep", { kind = kind }, def)) + end) + + return result +end + +-- Recurse locals results until a node is found. +-- The accumulator function is given +-- * The table of the node +-- * The node +-- * The full definition match `@definition.var.something` -> 'var.something' +-- * The last definition match `@definition.var.something` -> 'something' +---@param local_def any The locals result +---@param accumulator function The accumulator function +---@param full_match? string The full match path to append to +---@param last_match? string The last match +function M.recurse_local_nodes(local_def, accumulator, full_match, last_match) + if type(local_def) ~= "table" then + return + end + + if local_def.node then + accumulator(local_def, local_def.node, full_match, last_match) + else + for match_key, def in pairs(local_def) do + M.recurse_local_nodes(def, accumulator, full_match and (full_match .. "." .. match_key) or match_key, match_key) + end + end +end + +-- Get a single dimension table to look definition nodes. +-- Keys are generated by using the range of the containing scope and the text of the definition node. +-- This makes looking up a definition for a given scope a simple key lookup. +-- +-- This is memoized by buffer tick. If the function is called in succession +-- without the buffer tick changing, then the previous result will be used +-- since the syntax tree hasn't changed. +-- +-- Usage lookups require finding the definition of the node, so `find_definition` +-- is called very frequently, which is why this lookup must be fast as possible. +-- +---@param bufnr integer: the buffer +---@return table result: a table for looking up definitions +M.get_definitions_lookup_table = ts_utils.memoize_by_buf_tick(function(bufnr) + local definitions = M.get_definitions(bufnr) + local result = {} + + for _, definition in ipairs(definitions) do + for _, node_entry in ipairs(M.get_local_nodes(definition)) do + local scopes = M.get_definition_scopes(node_entry.node, bufnr, node_entry.scope) + -- Always use the highest valid scope + local scope = scopes[#scopes] + local node_text = ts.get_node_text(node_entry.node, bufnr) + local id = M.get_definition_id(scope, node_text) + + result[id] = node_entry + end + end + + return result +end) + +-- Gets all the scopes of a definition based on the scope type +-- Scope types can be +-- +-- "parent": Uses the parent of the containing scope, basically, skipping a scope +-- "global": Uses the top most scope +-- "local": Uses the containing scope of the definition. This is the default +-- +---@param node TSNode: the definition node +---@param bufnr integer: the buffer +---@param scope_type string: the scope type +function M.get_definition_scopes(node, bufnr, scope_type) + local scopes = {} + local scope_count = 1 ---@type integer|nil + + -- Definition is valid for the containing scope + -- and the containing scope of that scope + if scope_type == "parent" then + scope_count = 2 + -- Definition is valid in all parent scopes + elseif scope_type == "global" then + scope_count = nil + end + + local i = 0 + for scope in M.iter_scope_tree(node, bufnr) do + table.insert(scopes, scope) + i = i + 1 + + if scope_count and i >= scope_count then + break + end + end + + return scopes +end + +---@param node TSNode +---@param bufnr integer +---@return TSNode node +---@return TSNode scope +---@return string|nil kind +function M.find_definition(node, bufnr) + local def_lookup = M.get_definitions_lookup_table(bufnr) + local node_text = ts.get_node_text(node, bufnr) + + for scope in M.iter_scope_tree(node, bufnr) do + local id = M.get_definition_id(scope, node_text) + + if def_lookup[id] then + local entry = def_lookup[id] + + return entry.node, scope, entry.kind + end + end + + return node, ts_utils.get_root_for_node(node), nil +end + +-- Finds usages of a node in a given scope. +---@param node TSNode the node to find usages for +---@param scope_node TSNode the node to look within +---@return TSNode[]: a list of nodes +function M.find_usages(node, scope_node, bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + local node_text = ts.get_node_text(node, bufnr) + + if not node_text or #node_text < 1 then + return {} + end + + local scope_node = scope_node or ts_utils.get_root_for_node(node) + local usages = {} + + for match in M.iter_locals(bufnr, scope_node) do + match = match["local"] + if match.reference and match.reference.node and ts.get_node_text(match.reference.node, bufnr) == node_text then + local def_node, _, kind = M.find_definition(match.reference.node, bufnr) + + if kind == nil or def_node == node then + table.insert(usages, match.reference.node) + end + end + end + + return usages +end + +---@param node TSNode +---@param bufnr? integer +---@param allow_scope? boolean +---@return TSNode|nil +function M.containing_scope(node, bufnr, allow_scope) + local bufnr = bufnr or api.nvim_get_current_buf() + local allow_scope = allow_scope == nil or allow_scope == true + + local scopes = M.get_scopes(bufnr) + if not node or not scopes then + return + end + + local iter_node = node + + while iter_node ~= nil and not vim.tbl_contains(scopes, iter_node) do + iter_node = iter_node:parent() + end + + return iter_node or (allow_scope and node or nil) +end + +function M.nested_scope(node, cursor_pos) + local bufnr = api.nvim_get_current_buf() + + local scopes = M.get_scopes(bufnr) + if not node or not scopes then + return + end + + local row = cursor_pos.row ---@type integer + local col = cursor_pos.col ---@type integer + local scope = M.containing_scope(node) + + for _, child in ipairs(ts_utils.get_named_children(scope)) do + local row_, col_ = child:start() + if vim.tbl_contains(scopes, child) and ((row_ + 1 == row and col_ > col) or row_ + 1 > row) then + return child + end + end +end + +function M.next_scope(node) + local bufnr = api.nvim_get_current_buf() + + local scopes = M.get_scopes(bufnr) + if not node or not scopes then + return + end + + local scope = M.containing_scope(node) + + local parent = scope:parent() + if not parent then + return + end + + local is_prev = true + for _, child in ipairs(ts_utils.get_named_children(parent)) do + if child == scope then + is_prev = false + elseif not is_prev and vim.tbl_contains(scopes, child) then + return child + end + end +end + +---@param node TSNode +---@return TSNode|nil +function M.previous_scope(node) + local bufnr = api.nvim_get_current_buf() + + local scopes = M.get_scopes(bufnr) + if not node or not scopes then + return + end + + local scope = M.containing_scope(node) + + local parent = scope:parent() + if not parent then + return + end + + local is_prev = true + local children = ts_utils.get_named_children(parent) + for i = #children, 1, -1 do + if children[i] == scope then + is_prev = false + elseif not is_prev and vim.tbl_contains(scopes, children[i]) then + return children[i] + end + end +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/parsers.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/parsers.lua new file mode 100644 index 00000000..d2abb9d5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/parsers.lua @@ -0,0 +1,2518 @@ +local api = vim.api +local ts = vim.treesitter + +local new_lang_api = ts.language.register ~= nil + +local filetype_to_parsername = {} + +if new_lang_api then + filetype_to_parsername = setmetatable({}, { + __newindex = function(_, k, v) + require("nvim-treesitter.utils").notify( + "filetype_to_parsername is deprecated, please use 'vim.treesitter.language.register'", + vim.log.levels.WARN + ) + ts.language.register(v, k) + end, + }) +end + +local function register_lang(lang, ft) + if new_lang_api then + ts.language.register(lang, ft) + return + end + filetype_to_parsername[ft] = lang +end + +for ft, lang in pairs { + automake = "make", + javascriptreact = "javascript", + ecma = "javascript", + jsx = "javascript", + sh = "bash", + html_tags = "html", + ["typescript.tsx"] = "tsx", + ["html.handlebars"] = "glimmer", + systemverilog = "verilog", + pandoc = "markdown", + rmd = "markdown", + quarto = "markdown", + dosini = "ini", + confini = "ini", + svg = "xml", + xsd = "xml", + xslt = "xml", + expect = "tcl", + mysql = "sql", + sbt = "scala", + neomuttrc = "muttrc", +} do + register_lang(lang, ft) +end + +---@class InstallInfo +---@field url string +---@field branch string|nil +---@field revision string|nil +---@field files string[] +---@field generate_requires_npm boolean|nil +---@field requires_generate_from_grammar boolean|nil +---@field location string|nil +---@field use_makefile boolean|nil +---@field cxx_standard string|nil + +---@class ParserInfo +---@field install_info InstallInfo +---@field filetype string +---@field maintainers string[] +---@field experimental boolean|nil +---@field readme_name string|nil + +---@type ParserInfo[] +local list = setmetatable({}, { + __newindex = function(table, parsername, parserconfig) + rawset(table, parsername, parserconfig) + register_lang(parsername, parserconfig.filetype or parsername) + end, +}) + +list.ada = { + install_info = { + url = "https://github.com/briot/tree-sitter-ada", + files = { "src/parser.c" }, + }, + maintainers = { "@briot" }, +} + +list.agda = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-agda", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@Decodetalkers" }, +} + +list.angular = { + install_info = { + url = "https://github.com/dlvandenberg/tree-sitter-angular", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@dlvandenberg" }, + experimental = true, +} + +list.apex = { + install_info = { + url = "https://github.com/aheber/tree-sitter-sfapex", + files = { "src/parser.c" }, + location = "apex", + }, + maintainers = { "@aheber" }, +} + +list.arduino = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-arduino", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.asm = { + install_info = { + url = "https://github.com/RubixDev/tree-sitter-asm", + files = { "src/parser.c" }, + }, + maintainers = { "@RubixDev" }, +} + +list.astro = { + install_info = { + url = "https://github.com/virchau13/tree-sitter-astro", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@virchau13" }, +} + +list.authzed = { + install_info = { + url = "https://github.com/mleonidas/tree-sitter-authzed", + files = { "src/parser.c" }, + }, + maintainers = { "@mattpolzin" }, +} + +list.awk = { + install_info = { + url = "https://github.com/Beaglefoot/tree-sitter-awk", + files = { "src/parser.c", "src/scanner.c" }, + }, +} + +list.bash = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-bash", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "sh", + maintainers = { "@TravonteD" }, +} + +list.bass = { + install_info = { + url = "https://github.com/vito/tree-sitter-bass", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.beancount = { + install_info = { + url = "https://github.com/polarmutex/tree-sitter-beancount", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@polarmutex" }, +} + +list.bibtex = { + install_info = { + url = "https://github.com/latex-lsp/tree-sitter-bibtex", + files = { "src/parser.c" }, + }, + filetype = "bib", + maintainers = { "@theHamsta", "@clason" }, +} + +list.bicep = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-bicep", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.bitbake = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-bitbake", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.blueprint = { + install_info = { + url = "https://gitlab.com/gabmus/tree-sitter-blueprint.git", + files = { "src/parser.c" }, + }, + maintainers = { "@gabmus" }, + experimental = true, +} + +list.bp = { + install_info = { + url = "https://github.com/ambroisie/tree-sitter-bp", + files = { "src/parser.c" }, + }, + maintainers = { "@ambroisie" }, +} + +list.c = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-c", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.c_sharp = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-c-sharp", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "cs", + maintainers = { "@amaanq" }, +} + +list.cairo = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-cairo", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.capnp = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-capnp", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.chatito = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-chatito", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.clojure = { + install_info = { + url = "https://github.com/sogaiu/tree-sitter-clojure", + files = { "src/parser.c" }, + }, + maintainers = { "@NoahTheDuke" }, +} + +list.cmake = { + install_info = { + url = "https://github.com/uyha/tree-sitter-cmake", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@uyha" }, +} + +list.comment = { + install_info = { + url = "https://github.com/stsewd/tree-sitter-comment", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@stsewd" }, +} + +list.commonlisp = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-commonlisp", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + filetype = "lisp", + maintainers = { "@theHamsta" }, +} + +list.cooklang = { + install_info = { + url = "https://github.com/addcninblue/tree-sitter-cooklang", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@addcninblue" }, +} + +list.corn = { + install_info = { + url = "https://github.com/jakestanger/tree-sitter-corn", + files = { "src/parser.c" }, + }, + maintainers = { "@jakestanger" }, +} + +list.cpon = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-cpon", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.cpp = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-cpp", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@theHamsta" }, +} + +list.css = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-css", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@TravonteD" }, +} + +list.csv = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-csv", + files = { "src/parser.c" }, + location = "csv", + }, + maintainers = { "@amaanq" }, +} + +list.cuda = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-cuda", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@theHamsta" }, +} + +list.cue = { + install_info = { + url = "https://github.com/eonpatapon/tree-sitter-cue", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.d = { + install_info = { + url = "https://github.com/gdamore/tree-sitter-d", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.dart = { + install_info = { + url = "https://github.com/UserNobody14/tree-sitter-dart", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@akinsho" }, +} + +list.devicetree = { + install_info = { + url = "https://github.com/joelspadin/tree-sitter-devicetree", + files = { "src/parser.c" }, + }, + filetype = "dts", + maintainers = { "@jedrzejboczar" }, +} + +list.dhall = { + install_info = { + url = "https://github.com/jbellerb/tree-sitter-dhall", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.diff = { + install_info = { + url = "https://github.com/the-mikedavis/tree-sitter-diff", + files = { "src/parser.c" }, + }, + filetype = "gitdiff", + maintainers = { "@gbprod" }, +} + +list.disassembly = { + install_info = { + url = "https://github.com/ColinKennedy/tree-sitter-disassembly", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ColinKennedy" }, +} + +list.djot = { + install_info = { + url = "https://github.com/treeman/tree-sitter-djot", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@NoahTheDuke" }, +} + +list.dockerfile = { + install_info = { + url = "https://github.com/camdencheek/tree-sitter-dockerfile", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@camdencheek" }, +} + +list.dot = { + install_info = { + url = "https://github.com/rydesun/tree-sitter-dot", + files = { "src/parser.c" }, + }, + maintainers = { "@rydesun" }, +} + +list.doxygen = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-doxygen", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.dtd = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-xml", + files = { "src/parser.c", "src/scanner.c" }, + location = "dtd", + }, + maintainers = { "@ObserverOfTime" }, +} + +list.earthfile = { + install_info = { + url = "https://github.com/glehmann/tree-sitter-earthfile", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@glehmann" }, +} + +list.ebnf = { + install_info = { + url = "https://github.com/RubixDev/ebnf", + files = { "src/parser.c" }, + location = "crates/tree-sitter-ebnf", + }, + maintainers = { "@RubixDev" }, + experimental = true, +} + +list.eds = { + install_info = { + url = "https://github.com/uyha/tree-sitter-eds", + files = { "src/parser.c" }, + }, + maintainers = { "@uyha" }, +} + +list.eex = { + install_info = { + url = "https://github.com/connorlay/tree-sitter-eex", + files = { "src/parser.c" }, + }, + filetype = "eelixir", + maintainers = { "@connorlay" }, +} + +list.elixir = { + install_info = { + url = "https://github.com/elixir-lang/tree-sitter-elixir", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@connorlay" }, +} + +list.elm = { + install_info = { + url = "https://github.com/elm-tooling/tree-sitter-elm", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@zweimach" }, +} + +list.elsa = { + install_info = { + url = "https://github.com/glapa-grossklag/tree-sitter-elsa", + files = { "src/parser.c" }, + }, + maintainers = { "@glapa-grossklag", "@amaanq" }, +} + +list.elvish = { + install_info = { + url = "https://github.com/elves/tree-sitter-elvish", + files = { "src/parser.c" }, + }, + maintainers = { "@elves" }, +} + +list.embedded_template = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-embedded-template", + files = { "src/parser.c" }, + }, + filetype = "eruby", +} + +list.erlang = { + install_info = { + url = "https://github.com/WhatsApp/tree-sitter-erlang", + files = { "src/parser.c" }, + }, + maintainers = { "@filmor" }, +} + +list.facility = { + install_info = { + url = "https://github.com/FacilityApi/tree-sitter-facility", + files = { "src/parser.c" }, + }, + filetype = "fsd", + maintainers = { "@bryankenote" }, +} + +list.faust = { + install_info = { + url = "https://github.com/khiner/tree-sitter-faust", + files = { "src/parser.c" }, + }, + filetype = "dsp", + maintainers = { "@khiner" }, +} + +list.fennel = { + install_info = { + url = "https://github.com/alexmozaidze/tree-sitter-fennel", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@alexmozaidze" }, +} + +list.fidl = { + install_info = { + url = "https://github.com/google/tree-sitter-fidl", + files = { "src/parser.c" }, + }, + maintainers = { "@chaopeng" }, +} + +list.firrtl = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-firrtl", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.fish = { + install_info = { + url = "https://github.com/ram02z/tree-sitter-fish", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ram02z" }, +} + +list.foam = { + install_info = { + url = "https://github.com/FoamScience/tree-sitter-foam", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@FoamScience" }, + -- Queries might change over time on the grammar's side + -- Otherwise everything runs fine + experimental = true, +} + +list.forth = { + install_info = { + url = "https://github.com/AlexanderBrevig/tree-sitter-forth", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.fortran = { + install_info = { + url = "https://github.com/stadelmanma/tree-sitter-fortran", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.fsh = { + install_info = { + url = "https://github.com/mgramigna/tree-sitter-fsh", + files = { "src/parser.c" }, + }, + maintainers = { "@mgramigna" }, +} + +list.func = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-func", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.fusion = { + install_info = { + url = "https://gitlab.com/jirgn/tree-sitter-fusion.git", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@jirgn" }, +} + +list.gdscript = { + install_info = { + url = "https://github.com/PrestonKnopp/tree-sitter-gdscript", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@PrestonKnopp" }, + readme_name = "Godot (gdscript)", +} + +list.gdshader = { + install_info = { + url = "https://github.com/GodOfAvacyn/tree-sitter-gdshader", + files = { "src/parser.c" }, + }, + filetype = "gdshaderinc", + maintainers = { "@godofavacyn" }, +} + +list.git_rebase = { + install_info = { + url = "https://github.com/the-mikedavis/tree-sitter-git-rebase", + files = { "src/parser.c" }, + }, + filetype = "gitrebase", + maintainers = { "@gbprod" }, +} + +list.gitattributes = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-gitattributes", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.gitcommit = { + install_info = { + url = "https://github.com/gbprod/tree-sitter-gitcommit", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@gbprod" }, +} + +list.git_config = { + install_info = { + url = "https://github.com/the-mikedavis/tree-sitter-git-config", + files = { "src/parser.c" }, + }, + filetype = "gitconfig", + maintainers = { "@amaanq" }, + readme_name = "git_config", +} + +list.gitignore = { + install_info = { + url = "https://github.com/shunsambongi/tree-sitter-gitignore", + files = { "src/parser.c" }, + }, + maintainers = { "@theHamsta" }, +} + +list.gleam = { + install_info = { + url = "https://github.com/gleam-lang/tree-sitter-gleam", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.glimmer = { + install_info = { + url = "https://github.com/alexlafroscia/tree-sitter-glimmer", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "handlebars", + maintainers = { "@NullVoxPopuli" }, + readme_name = "Glimmer and Ember", +} + +list.glsl = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-glsl", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + maintainers = { "@theHamsta" }, +} + +list.gn = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-gn", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, + readme_name = "GN (Generate Ninja)", +} + +list.gnuplot = { + install_info = { + url = "https://github.com/dpezto/tree-sitter-gnuplot", + files = { "src/parser.c" }, + }, + maintainers = { "@dpezto" }, +} + +list.go = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-go", + files = { "src/parser.c" }, + }, + maintainers = { "@theHamsta", "@WinWisely268" }, +} + +list.godot_resource = { + install_info = { + url = "https://github.com/PrestonKnopp/tree-sitter-godot-resource", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "gdresource", + maintainers = { "@pierpo" }, + readme_name = "Godot Resources (gdresource)", +} + +list.gomod = { + install_info = { + url = "https://github.com/camdencheek/tree-sitter-go-mod", + files = { "src/parser.c" }, + }, + maintainers = { "@camdencheek" }, +} + +list.gosum = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-go-sum", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.gowork = { + install_info = { + url = "https://github.com/omertuc/tree-sitter-go-work", + files = { "src/parser.c" }, + }, + maintainers = { "@omertuc" }, +} + +list.gotmpl = { + install_info = { + url = "https://github.com/ngalaiko/tree-sitter-go-template", + files = { "src/parser.c" }, + }, + maintainers = { "@qvalentin" }, +} + +list.gpg = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-gpg-config", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.groovy = { + install_info = { + url = "https://github.com/murtaza64/tree-sitter-groovy", + files = { "src/parser.c" }, + }, + maintainers = { "@murtaza64" }, +} + +list.graphql = { + install_info = { + url = "https://github.com/bkegley/tree-sitter-graphql", + files = { "src/parser.c" }, + }, + maintainers = { "@bkegley" }, +} + +list.gstlaunch = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-gstlaunch", + files = { "src/parser.c" }, + }, + maintainers = { "@theHamsta" }, +} + +list.hack = { + install_info = { + url = "https://github.com/slackhq/tree-sitter-hack", + files = { "src/parser.c", "src/scanner.c" }, + }, +} + +list.hare = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-hare", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.haskell = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-haskell", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@mrcjkb" }, +} + +list.haskell_persistent = { + install_info = { + url = "https://github.com/MercuryTechnologies/tree-sitter-haskell-persistent", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "haskellpersistent", + maintainers = { "@lykahb" }, +} + +list.hcl = { + install_info = { + url = "https://github.com/MichaHoffmann/tree-sitter-hcl", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@MichaHoffmann" }, +} + +list.heex = { + install_info = { + url = "https://github.com/connorlay/tree-sitter-heex", + files = { "src/parser.c" }, + }, + maintainers = { "@connorlay" }, +} + +list.helm = { + install_info = { + url = "https://github.com/ngalaiko/tree-sitter-go-template", + location = "dialects/helm", + files = { "src/parser.c" }, + }, + maintainers = { "@qvalentin" }, +} + +list.hjson = { + install_info = { + url = "https://github.com/winston0410/tree-sitter-hjson", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + maintainers = { "@winston0410" }, +} + +list.hlsl = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-hlsl", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@theHamsta" }, +} + +list.hocon = { + install_info = { + url = "https://github.com/antosha417/tree-sitter-hocon", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + maintainers = { "@antosha417" }, +} + +list.hoon = { + install_info = { + url = "https://github.com/urbit-pilled/tree-sitter-hoon", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@urbit-pilled" }, + experimental = true, +} + +list.html = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-html", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@TravonteD" }, +} + +list.htmldjango = { + install_info = { + url = "https://github.com/interdependence/tree-sitter-htmldjango", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, + experimental = true, +} + +list.http = { + install_info = { + url = "https://github.com/rest-nvim/tree-sitter-http", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq", "@NTBBloodbath" }, +} + +list.hurl = { + install_info = { + url = "https://github.com/pfeiferj/tree-sitter-hurl", + files = { "src/parser.c" }, + }, + maintainers = { "@pfeiferj" }, +} + +list.hyprlang = { + install_info = { + url = "https://github.com/luckasRanarison/tree-sitter-hyprlang", + files = { "src/parser.c" }, + }, + maintainers = { "@luckasRanarison" }, +} + +list.idl = { + install_info = { + url = "https://github.com/cathaysia/tree-sitter-idl", + files = { "src/parser.c" }, + }, + maintainers = { "@cathaysa" }, +} + +list.ini = { + install_info = { + url = "https://github.com/justinmk/tree-sitter-ini", + files = { "src/parser.c" }, + }, + maintainers = { "@theHamsta" }, + experimental = true, +} + +list.inko = { + install_info = { + url = "https://github.com/inko-lang/tree-sitter-inko", + files = { "src/parser.c" }, + }, + maintainers = { "@yorickpeterse" }, +} + +list.ispc = { + install_info = { + url = "https://github.com/fab4100/tree-sitter-ispc", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + maintainers = { "@fab4100" }, +} + +list.janet_simple = { + install_info = { + url = "https://github.com/sogaiu/tree-sitter-janet-simple", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "janet", + maintainers = { "@sogaiu" }, +} + +list.java = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-java", + files = { "src/parser.c" }, + }, + maintainers = { "@p00f" }, +} + +list.javascript = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-javascript", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@steelsojka" }, +} + +list.jq = { + install_info = { + url = "https://github.com/flurie/tree-sitter-jq", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.jsdoc = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-jsdoc", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@steelsojka" }, +} + +list.json = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-json", + files = { "src/parser.c" }, + }, + maintainers = { "@steelsojka" }, +} + +list.json5 = { + install_info = { + url = "https://github.com/Joakker/tree-sitter-json5", + files = { "src/parser.c" }, + }, + maintainers = { "@Joakker" }, +} + +list.jsonc = { + install_info = { + url = "https://gitlab.com/WhyNotHugo/tree-sitter-jsonc.git", + files = { "src/parser.c" }, + generate_requires_npm = true, + }, + maintainers = { "@WhyNotHugo" }, + readme_name = "JSON with comments", +} + +list.jsonnet = { + install_info = { + url = "https://github.com/sourcegraph/tree-sitter-jsonnet", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@nawordar" }, +} + +list.julia = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-julia", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@theHamsta" }, +} + +list.just = { + install_info = { + url = "https://github.com/IndianBoy42/tree-sitter-just", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@Hubro" }, +} + +list.kconfig = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-kconfig", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.kdl = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-kdl", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.kotlin = { + install_info = { + url = "https://github.com/fwcd/tree-sitter-kotlin", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@SalBakraa" }, +} + +list.koto = { + install_info = { + url = "https://github.com/koto-lang/tree-sitter-koto", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@irh" }, +} + +list.kusto = { + install_info = { + url = "https://github.com/Willem-J-an/tree-sitter-kusto", + files = { "src/parser.c" }, + }, + maintainers = { "@Willem-J-an" }, +} + +list.lalrpop = { + install_info = { + url = "https://github.com/traxys/tree-sitter-lalrpop", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@traxys" }, +} + +list.latex = { + install_info = { + url = "https://github.com/latex-lsp/tree-sitter-latex", + files = { "src/parser.c", "src/scanner.c" }, + requires_generate_from_grammar = true, + }, + filetype = "tex", + maintainers = { "@theHamsta", "@clason" }, +} + +list.ledger = { + install_info = { + url = "https://github.com/cbarrete/tree-sitter-ledger", + files = { "src/parser.c" }, + }, + maintainers = { "@cbarrete" }, +} + +list.leo = { + install_info = { + url = "https://github.com/r001/tree-sitter-leo", + files = { "src/parser.c" }, + }, + maintainers = { "@r001" }, +} + +list.llvm = { + install_info = { + url = "https://github.com/benwilliamgraham/tree-sitter-llvm", + files = { "src/parser.c" }, + }, + maintainers = { "@benwilliamgraham" }, +} + +list.linkerscript = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-linkerscript", + files = { "src/parser.c" }, + }, + filetype = "ld", + maintainers = { "@amaanq" }, +} + +list.liquid = { + install_info = { + url = "https://github.com/hankthetank27/tree-sitter-liquid", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@hankthetank27" }, +} + +list.liquidsoap = { + install_info = { + url = "https://github.com/savonet/tree-sitter-liquidsoap", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@toots" }, +} + +list.lua = { + install_info = { + url = "https://github.com/MunifTanjim/tree-sitter-lua", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@muniftanjim" }, +} + +list.luadoc = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-luadoc", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.luap = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-luap", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, + readme_name = "lua patterns", +} + +list.luau = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-luau", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.hlsplaylist = { + install_info = { + url = "https://github.com/Freed-Wu/tree-sitter-hlsplaylist", + files = { "src/parser.c" }, + }, + maintainers = { "@Freed-Wu" }, +} + +list.m68k = { + install_info = { + url = "https://github.com/grahambates/tree-sitter-m68k", + files = { "src/parser.c" }, + }, + filetype = "asm68k", + maintainers = { "@grahambates" }, +} + +list.make = { + install_info = { + url = "https://github.com/alemuller/tree-sitter-make", + files = { "src/parser.c" }, + }, + maintainers = { "@lewis6991" }, +} + +list.markdown = { + install_info = { + url = "https://github.com/MDeiml/tree-sitter-markdown", + location = "tree-sitter-markdown", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@MDeiml" }, + readme_name = "markdown (basic highlighting)", + experimental = true, +} + +list.markdown_inline = { + install_info = { + url = "https://github.com/MDeiml/tree-sitter-markdown", + location = "tree-sitter-markdown-inline", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@MDeiml" }, + readme_name = "markdown_inline (needed for full highlighting)", + experimental = true, +} + +list.matlab = { + install_info = { + url = "https://github.com/acristoffers/tree-sitter-matlab", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@acristoffers" }, +} + +list.menhir = { + install_info = { + url = "https://github.com/Kerl13/tree-sitter-menhir", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@Kerl13" }, +} + +list.mermaid = { + install_info = { + url = "https://github.com/monaqa/tree-sitter-mermaid", + files = { "src/parser.c" }, + }, + experimental = true, +} + +list.meson = { + install_info = { + url = "https://github.com/Decodetalkers/tree-sitter-meson", + files = { "src/parser.c" }, + }, + maintainers = { "@Decodetalkers" }, +} + +list.mlir = { + install_info = { + url = "https://github.com/artagnon/tree-sitter-mlir", + files = { "src/parser.c" }, + requires_generate_from_grammar = true, + }, + experimental = true, + maintainers = { "@artagnon" }, +} + +list.muttrc = { + install_info = { + url = "https://github.com/neomutt/tree-sitter-muttrc", + files = { "src/parser.c" }, + }, + maintainers = { "@Freed-Wu" }, +} + +list.nasm = { + install_info = { + url = "https://github.com/naclsn/tree-sitter-nasm", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.nickel = { + install_info = { + url = "https://github.com/nickel-lang/tree-sitter-nickel", + files = { "src/parser.c", "src/scanner.c" }, + }, +} + +list.nim = { + install_info = { + url = "https://github.com/alaviss/tree-sitter-nim", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@aMOPel" }, +} + +list.nim_format_string = { + install_info = { + url = "https://github.com/aMOPel/tree-sitter-nim-format-string", + files = { "src/parser.c" }, + }, + maintainers = { "@aMOPel" }, +} + +list.ninja = { + install_info = { + url = "https://github.com/alemuller/tree-sitter-ninja", + files = { "src/parser.c" }, + }, + maintainers = { "@alemuller" }, +} + +list.nix = { + install_info = { + url = "https://github.com/cstrahan/tree-sitter-nix", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@leo60228" }, +} + +list.norg = { + install_info = { + url = "https://github.com/nvim-neorg/tree-sitter-norg", + files = { "src/parser.c", "src/scanner.cc" }, + cxx_standard = "c++14", + use_makefile = true, + }, + maintainers = { "@JoeyGrajciar", "@vhyrro" }, +} + +list.nqc = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-nqc", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.objc = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-objc", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.objdump = { + install_info = { + url = "https://github.com/ColinKennedy/tree-sitter-objdump", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ColinKennedy" }, +} + +list.ocaml = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-ocaml", + files = { "src/parser.c", "src/scanner.c" }, + location = "grammars/ocaml", + }, + maintainers = { "@undu" }, +} + +list.ocaml_interface = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-ocaml", + files = { "src/parser.c", "src/scanner.c" }, + location = "grammars/interface", + }, + filetype = "ocamlinterface", + maintainers = { "@undu" }, +} + +list.ocamllex = { + install_info = { + url = "https://github.com/atom-ocaml/tree-sitter-ocamllex", + files = { "src/parser.c", "src/scanner.c" }, + requires_generate_from_grammar = true, + }, + maintainers = { "@undu" }, +} + +list.odin = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-odin", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.org = { + install_info = { + url = "https://github.com/milisims/tree-sitter-org", + files = { "src/parser.c", "src/scanner.c" }, + }, +} + +list.pascal = { + install_info = { + url = "https://github.com/Isopod/tree-sitter-pascal.git", + files = { "src/parser.c" }, + }, + maintainers = { "@Isopod" }, +} + +list.passwd = { + install_info = { + url = "https://github.com/ath3/tree-sitter-passwd", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.pem = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-pem", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.perl = { + install_info = { + url = "https://github.com/tree-sitter-perl/tree-sitter-perl", + files = { "src/parser.c", "src/scanner.c" }, + branch = "release", + }, + maintainers = { "@RabbiVeesh", "@LeoNerd" }, +} + +list.php = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-php", + location = "php", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@tk-shirasaka" }, +} + +list.php_only = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-php", + location = "php_only", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@tk-shirasaka" }, +} + +-- Parsers for injections +list.phpdoc = { + install_info = { + url = "https://github.com/claytonrcarter/tree-sitter-phpdoc", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@mikehaertl" }, + experimental = true, +} + +list.pioasm = { + install_info = { + url = "https://github.com/leo60228/tree-sitter-pioasm", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@leo60228" }, +} + +list.po = { + install_info = { + url = "https://github.com/erasin/tree-sitter-po", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.pod = { + install_info = { + url = "https://github.com/tree-sitter-perl/tree-sitter-pod", + files = { "src/parser.c", "src/scanner.c" }, + branch = "release", + }, + maintainers = { "@RabbiVeesh", "@LeoNerd" }, +} + +list.poe_filter = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-poe-filter", + files = { "src/parser.c" }, + }, + filetype = "poefilter", + maintainers = { "@ObserverOfTime" }, + readme_name = "Path of Exile item filter", + experimental = true, +} + +list.pony = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-pony", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq", "@mfelsche" }, +} + +list.printf = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-printf", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.prisma = { + install_info = { + url = "https://github.com/victorhqc/tree-sitter-prisma", + files = { "src/parser.c" }, + }, + maintainers = { "@elianiva" }, +} + +list.promql = { + install_info = { + url = "https://github.com/MichaHoffmann/tree-sitter-promql", + files = { "src/parser.c" }, + experimental = true, + }, + maintainers = { "@MichaHoffmann" }, +} + +list.properties = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-properties", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "jproperties", + maintainers = { "@ObserverOfTime" }, +} + +list.proto = { + install_info = { + url = "https://github.com/treywood/tree-sitter-proto", + files = { "src/parser.c" }, + }, + maintainers = { "@treywood" }, +} + +list.prql = { + install_info = { + url = "https://github.com/PRQL/tree-sitter-prql", + files = { "src/parser.c" }, + }, + maintainers = { "@matthias-Q" }, +} + +list.psv = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-csv", + files = { "src/parser.c" }, + location = "psv", + }, + maintainers = { "@amaanq" }, +} + +list.pug = { + install_info = { + url = "https://github.com/zealot128/tree-sitter-pug", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@zealot128" }, + experimental = true, +} + +list.puppet = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-puppet", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.purescript = { + install_info = { + url = "https://github.com/postsolar/tree-sitter-purescript", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@postsolar" }, +} + +list.pymanifest = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-pymanifest", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, + readme_name = "PyPA manifest", +} + +list.python = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-python", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@stsewd", "@theHamsta" }, +} + +list.ql = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-ql", + files = { "src/parser.c" }, + }, + maintainers = { "@pwntester" }, +} + +list.qmldir = { + install_info = { + url = "https://github.com/Decodetalkers/tree-sitter-qmldir", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.qmljs = { + install_info = { + url = "https://github.com/yuja/tree-sitter-qmljs", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "qml", + maintainers = { "@Decodetalkers" }, +} + +list.query = { + install_info = { + url = "https://github.com/nvim-treesitter/tree-sitter-query", + files = { "src/parser.c" }, + }, + maintainers = { "@steelsojka" }, + readme_name = "Tree-Sitter query language", +} + +list.r = { + install_info = { + url = "https://github.com/r-lib/tree-sitter-r", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@echasnovski" }, +} + +list.racket = { + install_info = { + url = "https://github.com/6cdh/tree-sitter-racket", + files = { "src/parser.c", "src/scanner.c" }, + }, +} + +list.rasi = { + install_info = { + url = "https://github.com/Fymyte/tree-sitter-rasi", + files = { "src/parser.c" }, + }, + maintainers = { "@Fymyte" }, +} + +list.rbs = { + install_info = { + url = "https://github.com/joker1007/tree-sitter-rbs", + files = { "src/parser.c" }, + }, + maintainers = { "@joker1007" }, +} + +list.re2c = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-re2c", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.readline = { + install_info = { + url = "https://github.com/ribru17/tree-sitter-readline", + files = { "src/parser.c" }, + }, + maintainers = { "@ribru17" }, +} + +list.regex = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-regex", + files = { "src/parser.c" }, + }, + maintainers = { "@theHamsta" }, +} + +list.rego = { + install_info = { + url = "https://github.com/FallenAngel97/tree-sitter-rego", + files = { "src/parser.c" }, + }, + maintainers = { "@FallenAngel97" }, +} + +list.requirements = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-requirements", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, + readme_name = "pip requirements", +} + +list.rnoweb = { + install_info = { + url = "https://github.com/bamonroe/tree-sitter-rnoweb", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@bamonroe" }, +} + +list.robot = { + install_info = { + url = "https://github.com/Hubro/tree-sitter-robot", + files = { "src/parser.c" }, + }, + maintainers = { "@Hubro" }, +} + +list.roc = { + install_info = { + url = "https://github.com/nat-418/tree-sitter-roc", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@nat-418" }, +} + +list.ron = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-ron", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.rst = { + install_info = { + url = "https://github.com/stsewd/tree-sitter-rst", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@stsewd" }, +} + +list.ruby = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-ruby", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@TravonteD" }, +} + +list.rust = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-rust", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.scala = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-scala", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@stevanmilic" }, +} + +list.scfg = { + install_info = { + url = "https://git.sr.ht/~rockorager/tree-sitter-scfg", + files = { "src/parser.c" }, + requires_generate_from_grammar = true, + }, + maintainers = { "@WhyNotHugo" }, +} + +list.scheme = { + install_info = { + url = "https://github.com/6cdh/tree-sitter-scheme", + files = { "src/parser.c" }, + }, +} + +list.scss = { + install_info = { + url = "https://github.com/serenadeai/tree-sitter-scss", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@elianiva" }, +} + +list.slang = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-slang", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + filetype = "shaderslang", + maintainers = { "@theHamsta" }, + experimental = true, +} + +list.slint = { + install_info = { + url = "https://github.com/slint-ui/tree-sitter-slint", + files = { "src/parser.c" }, + }, + maintainers = { "@hunger" }, +} + +list.smali = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-smali", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.snakemake = { + install_info = { + url = "https://github.com/osthomas/tree-sitter-snakemake", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainer = { "@osthomas" }, + experimental = true, +} + +list.smithy = { + install_info = { + url = "https://github.com/indoorvivants/tree-sitter-smithy", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq", "@keynmol" }, +} + +list.solidity = { + install_info = { + url = "https://github.com/JoranHonig/tree-sitter-solidity", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.soql = { + install_info = { + url = "https://github.com/aheber/tree-sitter-sfapex", + files = { "src/parser.c" }, + location = "soql", + }, + maintainers = { "@aheber" }, +} + +list.sosl = { + install_info = { + url = "https://github.com/aheber/tree-sitter-sfapex", + files = { "src/parser.c" }, + location = "sosl", + }, + maintainers = { "@aheber" }, +} + +list.sourcepawn = { + install_info = { + url = "https://github.com/nilshelmig/tree-sitter-sourcepawn", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@Sarrus1" }, + tier = 3, +} + +list.sparql = { + install_info = { + url = "https://github.com/BonaBeavis/tree-sitter-sparql", + files = { "src/parser.c" }, + }, + maintainers = { "@BonaBeavis" }, +} + +list.sql = { + install_info = { + url = "https://github.com/derekstride/tree-sitter-sql", + files = { "src/parser.c", "src/scanner.c" }, + branch = "gh-pages", + }, + maintainers = { "@derekstride" }, +} + +list.squirrel = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-squirrel", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.ssh_config = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-ssh-config", + files = { "src/parser.c" }, + }, + filetype = "sshconfig", + maintainers = { "@ObserverOfTime" }, +} + +list.starlark = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-starlark", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "bzl", + maintainers = { "@amaanq" }, +} + +list.strace = { + install_info = { + url = "https://github.com/sigmaSd/tree-sitter-strace", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.styled = { + install_info = { + url = "https://github.com/mskelton/tree-sitter-styled", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@mskelton" }, +} + +list.supercollider = { + install_info = { + url = "https://github.com/madskjeldgaard/tree-sitter-supercollider", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@madskjeldgaard" }, +} + +list.surface = { + install_info = { + url = "https://github.com/connorlay/tree-sitter-surface", + files = { "src/parser.c" }, + }, + filetype = "sface", + maintainers = { "@connorlay" }, +} + +list.svelte = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-svelte", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.swift = { + install_info = { + url = "https://github.com/alex-pinkus/tree-sitter-swift", + files = { "src/parser.c", "src/scanner.c" }, + requires_generate_from_grammar = true, + }, + maintainers = { "@alex-pinkus" }, +} + +list.sxhkdrc = { + install_info = { + url = "https://github.com/RaafatTurki/tree-sitter-sxhkdrc", + files = { "src/parser.c" }, + }, + maintainers = { "@RaafatTurki" }, +} + +list.systemtap = { + install_info = { + url = "https://github.com/ok-ryoko/tree-sitter-systemtap", + files = { "src/parser.c" }, + }, + maintainers = { "@ok-ryoko" }, +} + +list.t32 = { + install_info = { + url = "https://gitlab.com/xasc/tree-sitter-t32.git", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "trace32", + maintainers = { "@xasc" }, +} + +list.tablegen = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-tablegen", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.tact = { + install_info = { + url = "https://github.com/tact-lang/tree-sitter-tact", + files = { "src/parser.c" }, + }, + maintainers = { "@novusnota" }, +} + +list.teal = { + install_info = { + url = "https://github.com/euclidianAce/tree-sitter-teal", + files = { "src/parser.c", "src/scanner.c" }, + requires_generate_from_grammar = true, + }, + maintainers = { "@euclidianAce" }, +} + +list.tcl = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-tcl", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@lewis6991" }, +} + +list.terraform = { + install_info = { + url = "https://github.com/MichaHoffmann/tree-sitter-hcl", + files = { "src/parser.c", "src/scanner.c" }, + location = "dialects/terraform", + }, + maintainers = { "@MichaHoffmann" }, +} + +list.textproto = { + install_info = { + url = "https://github.com/PorterAtGoogle/tree-sitter-textproto", + files = { "src/parser.c" }, + }, + filetype = "pbtxt", + maintainers = { "@Porter" }, +} + +list.thrift = { + install_info = { + url = "https://github.com/duskmoon314/tree-sitter-thrift", + files = { "src/parser.c" }, + }, + maintainers = { "@amaanq", "@duskmoon314" }, +} + +list.tiger = { + install_info = { + url = "https://github.com/ambroisie/tree-sitter-tiger", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@ambroisie" }, +} + +list.tlaplus = { + install_info = { + url = "https://github.com/tlaplus-community/tree-sitter-tlaplus", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "tla", + maintainers = { "@ahelwer", "@susliko" }, +} + +list.tmux = { + install_info = { + url = "https://github.com/Freed-Wu/tree-sitter-tmux", + files = { "src/parser.c" }, + }, + maintainers = { "@Freed-Wu" }, +} + +list.todotxt = { + install_info = { + url = "https://github.com/arnarg/tree-sitter-todotxt.git", + files = { "src/parser.c" }, + }, + maintainers = { "@arnarg" }, + experimental = true, +} + +list.toml = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-toml", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@tk-shirasaka" }, +} + +list.tsv = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-csv", + files = { "src/parser.c" }, + location = "tsv", + }, + maintainers = { "@amaanq" }, +} + +list.tsx = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-typescript", + files = { "src/parser.c", "src/scanner.c" }, + location = "tsx", + generate_requires_npm = true, + }, + filetype = "typescriptreact", + maintainers = { "@steelsojka" }, +} + +list.turtle = { + install_info = { + url = "https://github.com/BonaBeavis/tree-sitter-turtle", + files = { "src/parser.c" }, + }, + maintainers = { "@BonaBeavis" }, +} + +list.twig = { + install_info = { + url = "https://github.com/gbprod/tree-sitter-twig", + files = { "src/parser.c" }, + }, + maintainers = { "@gbprod" }, +} + +list.typescript = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-typescript", + files = { "src/parser.c", "src/scanner.c" }, + location = "typescript", + generate_requires_npm = true, + }, + maintainers = { "@steelsojka" }, +} + +list.typespec = { + install_info = { + url = "https://github.com/happenslol/tree-sitter-typespec", + files = { "src/parser.c" }, + }, + maintainers = { "@happenslol" }, +} + +list.typoscript = { + install_info = { + url = "https://github.com/Teddytrombone/tree-sitter-typoscript", + files = { "src/parser.c" }, + }, + maintainers = { "@Teddytrombone" }, +} + +list.typst = { + install_info = { + url = "https://github.com/uben0/tree-sitter-typst", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@uben0", "@RaafatTurki" }, +} + +list.udev = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-udev", + files = { "src/parser.c" }, + }, + filetype = "udevrules", + maintainers = { "@ObserverOfTime" }, +} + +list.ungrammar = { + install_info = { + url = "https://github.com/Philipp-M/tree-sitter-ungrammar", + files = { "src/parser.c" }, + }, + maintainers = { "@Philipp-M", "@amaanq" }, +} + +list.unison = { + install_info = { + url = "https://github.com/kylegoetz/tree-sitter-unison", + files = { "src/parser.c", "src/scanner.c" }, + requires_generate_from_grammar = true, + }, + maintainers = { "@tapegram" }, +} + +list.usd = { + install_info = { + url = "https://github.com/ColinKennedy/tree-sitter-usd", + files = { "src/parser.c" }, + }, + maintainers = { "@ColinKennedy" }, +} + +list.uxntal = { + install_info = { + url = "https://github.com/amaanq/tree-sitter-uxntal", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "tal", + maintainers = { "@amaanq" }, + readme_name = "uxn tal", +} + +list.v = { + install_info = { + url = "https://github.com/vlang/v-analyzer", + files = { "src/parser.c" }, + location = "tree_sitter_v", + }, + filetype = "vlang", + maintainers = { "@kkharji", "@amaanq" }, +} + +list.vala = { + install_info = { + url = "https://github.com/vala-lang/tree-sitter-vala", + files = { "src/parser.c" }, + }, + maintainers = { "@Prince781" }, +} + +list.vento = { + install_info = { + url = "https://github.com/ventojs/tree-sitter-vento", + files = { "src/parser.c", "src/scanner.c" }, + }, + filetype = "vto", + maintainers = { "@wrapperup", "@oscarotero" }, +} + +list.verilog = { + install_info = { + url = "https://github.com/tree-sitter/tree-sitter-verilog", + files = { "src/parser.c" }, + }, + maintainers = { "@zegervdv" }, +} + +list.vhs = { + install_info = { + url = "https://github.com/charmbracelet/tree-sitter-vhs", + files = { "src/parser.c" }, + }, + filetype = "tape", + maintainers = { "@caarlos0" }, +} + +list.vim = { + install_info = { + url = "https://github.com/neovim/tree-sitter-vim", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@clason" }, +} + +list.vimdoc = { + install_info = { + url = "https://github.com/neovim/tree-sitter-vimdoc", + files = { "src/parser.c" }, + }, + filetype = "help", + maintainers = { "@clason" }, +} + +list.vue = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-vue", + files = { "src/parser.c", "src/scanner.c" }, + branch = "main", + }, + maintainers = { "@WhyNotHugo", "@lucario387" }, +} + +list.wgsl = { + install_info = { + url = "https://github.com/szebniok/tree-sitter-wgsl", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@szebniok" }, +} + +list.wgsl_bevy = { + install_info = { + url = "https://github.com/theHamsta/tree-sitter-wgsl-bevy", + files = { "src/parser.c", "src/scanner.c" }, + generate_requires_npm = true, + }, + maintainers = { "@theHamsta" }, +} + +list.wing = { + install_info = { + url = "https://github.com/winglang/tree-sitter-wing", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@gshpychka", "@MarkMcCulloh" }, +} + +list.wit = { + install_info = { + url = "https://github.com/liamwh/tree-sitter-wit", + files = { "src/parser.c" }, + }, + maintainers = { "@liamwh" }, +} + +list.xcompose = { + install_info = { + url = "https://github.com/ObserverOfTime/tree-sitter-xcompose", + files = { "src/parser.c" }, + }, + maintainers = { "@ObserverOfTime" }, +} + +list.xml = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-xml", + files = { "src/parser.c", "src/scanner.c" }, + location = "xml", + }, + maintainers = { "@ObserverOfTime" }, +} + +list.yaml = { + install_info = { + url = "https://github.com/tree-sitter-grammars/tree-sitter-yaml", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@amaanq" }, +} + +list.yang = { + install_info = { + url = "https://github.com/Hubro/tree-sitter-yang", + files = { "src/parser.c" }, + }, + maintainers = { "@Hubro" }, +} + +list.yuck = { + install_info = { + url = "https://github.com/Philipp-M/tree-sitter-yuck", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@Philipp-M", "@amaanq" }, +} + +list.zathurarc = { + install_info = { + url = "https://github.com/Freed-Wu/tree-sitter-zathurarc", + files = { "src/parser.c" }, + }, + maintainers = { "@Freed-Wu" }, +} + +list.zig = { + install_info = { + url = "https://github.com/maxxnino/tree-sitter-zig", + files = { "src/parser.c" }, + }, + maintainers = { "@maxxnino" }, +} + +list.templ = { + install_info = { + url = "https://github.com/vrischmann/tree-sitter-templ", + files = { "src/parser.c", "src/scanner.c" }, + }, + maintainers = { "@vrischmann" }, +} + +local M = { + list = list, + filetype_to_parsername = filetype_to_parsername, +} + +local function get_lang(ft) + if new_lang_api then + return ts.language.get_lang(ft) + end + return filetype_to_parsername[ft] +end + +function M.ft_to_lang(ft) + local result = get_lang(ft) + if result then + return result + else + ft = vim.split(ft, ".", { plain = true })[1] + return get_lang(ft) or ft + end +end + +-- Get a list of all available parsers +---@return string[] +function M.available_parsers() + local parsers = vim.tbl_keys(M.list) + table.sort(parsers) + if vim.fn.executable "tree-sitter" == 1 and vim.fn.executable "node" == 1 then + return parsers + else + return vim.tbl_filter(function(p) ---@param p string + return not M.list[p].install_info.requires_generate_from_grammar + end, parsers) + end +end + +function M.get_parser_configs() + return M.list +end + +local parser_files + +function M.reset_cache() + parser_files = setmetatable({}, { + __index = function(tbl, key) + rawset(tbl, key, api.nvim_get_runtime_file("parser/" .. key .. ".*", false)) + return rawget(tbl, key) + end, + }) +end + +M.reset_cache() + +function M.has_parser(lang) + lang = lang or M.get_buf_lang(api.nvim_get_current_buf()) + + if not lang or #lang == 0 then + return false + end + -- HACK: nvim internal API + if vim._ts_has_language(lang) then + return true + end + return #parser_files[lang] > 0 +end + +function M.get_parser(bufnr, lang) + bufnr = bufnr or api.nvim_get_current_buf() + lang = lang or M.get_buf_lang(bufnr) + + if M.has_parser(lang) then + return ts.get_parser(bufnr, lang) + end +end + +-- @deprecated This is only kept for legacy purposes. +-- All root nodes should be accounted for. +function M.get_tree_root(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + return M.get_parser(bufnr):parse()[1]:root() +end + +-- Gets the language of a given buffer +---@param bufnr number? or current buffer +---@return string +function M.get_buf_lang(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + return M.ft_to_lang(api.nvim_buf_get_option(bufnr, "ft")) +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/query.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/query.lua new file mode 100644 index 00000000..51310545 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/query.lua @@ -0,0 +1,453 @@ +local api = vim.api +local ts = require "nvim-treesitter.compat" +local tsrange = require "nvim-treesitter.tsrange" +local utils = require "nvim-treesitter.utils" +local parsers = require "nvim-treesitter.parsers" +local caching = require "nvim-treesitter.caching" + +local M = {} + +local EMPTY_ITER = function() end + +M.built_in_query_groups = { "highlights", "locals", "folds", "indents", "injections" } + +-- Creates a function that checks whether a given query exists +-- for a specific language. +---@param query string +---@return fun(string): boolean +local function get_query_guard(query) + return function(lang) + return M.has_query_files(lang, query) + end +end + +for _, query in ipairs(M.built_in_query_groups) do + M["has_" .. query] = get_query_guard(query) +end + +---@return string[] +function M.available_query_groups() + local query_files = api.nvim_get_runtime_file("queries/*/*.scm", true) + local groups = {} + for _, f in ipairs(query_files) do + groups[vim.fn.fnamemodify(f, ":t:r")] = true + end + local list = {} + for k, _ in pairs(groups) do + table.insert(list, k) + end + return list +end + +do + local query_cache = caching.create_buffer_cache() + + local function update_cached_matches(bufnr, changed_tick, query_group) + query_cache.set(query_group, bufnr, { + tick = changed_tick, + cache = M.collect_group_results(bufnr, query_group) or {}, + }) + end + + ---@param bufnr integer + ---@param query_group string + ---@return any + function M.get_matches(bufnr, query_group) + bufnr = bufnr or api.nvim_get_current_buf() + local cached_local = query_cache.get(query_group, bufnr) + if not cached_local or api.nvim_buf_get_changedtick(bufnr) > cached_local.tick then + update_cached_matches(bufnr, api.nvim_buf_get_changedtick(bufnr), query_group) + end + + return query_cache.get(query_group, bufnr).cache + end +end + +---@param lang string +---@param query_name string +---@return string[] +local function runtime_queries(lang, query_name) + return api.nvim_get_runtime_file(string.format("queries/%s/%s.scm", lang, query_name), true) or {} +end + +---@type table> +local query_files_cache = {} + +---@param lang string +---@param query_name string +---@return boolean +function M.has_query_files(lang, query_name) + if not query_files_cache[lang] then + query_files_cache[lang] = {} + end + if query_files_cache[lang][query_name] == nil then + local files = runtime_queries(lang, query_name) + query_files_cache[lang][query_name] = files and #files > 0 + end + return query_files_cache[lang][query_name] +end + +do + local mt = {} + mt.__index = function(tbl, key) + if rawget(tbl, key) == nil then + rawset(tbl, key, {}) + end + return rawget(tbl, key) + end + + -- cache will auto set the table for each lang if it is nil + ---@type table> + local cache = setmetatable({}, mt) + + -- Same as `vim.treesitter.query` except will return cached values + ---@param lang string + ---@param query_name string + function M.get_query(lang, query_name) + if cache[lang][query_name] == nil then + cache[lang][query_name] = ts.get_query(lang, query_name) + end + + return cache[lang][query_name] + end + + -- Invalidates the query file cache. + -- + -- If lang and query_name is both present, will reload for only the lang and query_name. + -- If only lang is present, will reload all query_names for that lang + -- If none are present, will reload everything + ---@param lang? string + ---@param query_name? string + function M.invalidate_query_cache(lang, query_name) + if lang and query_name then + cache[lang][query_name] = nil + if query_files_cache[lang] then + query_files_cache[lang][query_name] = nil + end + elseif lang and not query_name then + query_files_cache[lang] = nil + for query_name0, _ in pairs(cache[lang]) do + M.invalidate_query_cache(lang, query_name0) + end + elseif not lang and not query_name then + query_files_cache = {} + for lang0, _ in pairs(cache) do + for query_name0, _ in pairs(cache[lang0]) do + M.invalidate_query_cache(lang0, query_name0) + end + end + else + error "Cannot have query_name by itself!" + end + end +end + +-- This function is meant for an autocommand and not to be used. Only use if file is a query file. +---@param fname string +function M.invalidate_query_file(fname) + local fnamemodify = vim.fn.fnamemodify + M.invalidate_query_cache(fnamemodify(fname, ":p:h:t"), fnamemodify(fname, ":t:r")) +end + +---@class QueryInfo +---@field root TSNode +---@field source integer +---@field start integer +---@field stop integer + +---@param bufnr integer +---@param query_name string +---@param root TSNode +---@param root_lang string|nil +---@return Query|nil, QueryInfo|nil +local function prepare_query(bufnr, query_name, root, root_lang) + local buf_lang = parsers.get_buf_lang(bufnr) + + if not buf_lang then + return + end + + local parser = parsers.get_parser(bufnr, buf_lang) + if not parser then + return + end + + if not root then + local first_tree = parser:trees()[1] + + if first_tree then + root = first_tree:root() + end + end + + if not root then + return + end + + local range = { root:range() } + + if not root_lang then + local lang_tree = parser:language_for_range(range) + + if lang_tree then + root_lang = lang_tree:lang() + end + end + + if not root_lang then + return + end + + local query = M.get_query(root_lang, query_name) + if not query then + return + end + + return query, + { + root = root, + source = bufnr, + start = range[1], + -- The end row is exclusive so we need to add 1 to it. + stop = range[3] + 1, + } +end + +-- Given a path (i.e. a List(String)) this functions inserts value at path +---@param object any +---@param path string[] +---@param value any +function M.insert_to_path(object, path, value) + local curr_obj = object + + for index = 1, (#path - 1) do + if curr_obj[path[index]] == nil then + curr_obj[path[index]] = {} + end + + curr_obj = curr_obj[path[index]] + end + + curr_obj[path[#path]] = value +end + +---@param query Query +---@param bufnr integer +---@param start_row integer +---@param end_row integer +function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row) + -- A function that splits a string on '.' + ---@param to_split string + ---@return string[] + local function split(to_split) + local t = {} + for str in string.gmatch(to_split, "([^.]+)") do + table.insert(t, str) + end + + return t + end + + local matches = query:iter_matches(qnode, bufnr, start_row, end_row) + + local function iterator() + local pattern, match, metadata = matches() + if pattern ~= nil then + local prepared_match = {} + + -- Extract capture names from each match + for id, node in pairs(match) do + local name = query.captures[id] -- name of the capture in the query + if name ~= nil then + local path = split(name .. ".node") + M.insert_to_path(prepared_match, path, node) + local metadata_path = split(name .. ".metadata") + M.insert_to_path(prepared_match, metadata_path, metadata[id]) + end + end + + -- Add some predicates for testing + ---@type string[][] ( TODO: make pred type so this can be pred[]) + local preds = query.info.patterns[pattern] + if preds then + for _, pred in pairs(preds) do + -- functions + if pred[1] == "set!" and type(pred[2]) == "string" then + M.insert_to_path(prepared_match, split(pred[2]), pred[3]) + end + if pred[1] == "make-range!" and type(pred[2]) == "string" and #pred == 4 then + M.insert_to_path( + prepared_match, + split(pred[2] .. ".node"), + tsrange.TSRange.from_nodes(bufnr, match[pred[3]], match[pred[4]]) + ) + end + end + end + + return prepared_match + end + end + return iterator +end + +-- Return all nodes corresponding to a specific capture path (like @definition.var, @reference.type) +-- Works like M.get_references or M.get_scopes except you can choose the capture +-- Can also be a nested capture like @definition.function to get all nodes defining a function. +-- +---@param bufnr integer the buffer +---@param captures string|string[] +---@param query_group string the name of query group (highlights or injections for example) +---@param root TSNode|nil node from where to start the search +---@param lang string|nil the language from where to get the captures. +--- Root nodes can have several languages. +---@return table|nil +function M.get_capture_matches(bufnr, captures, query_group, root, lang) + if type(captures) == "string" then + captures = { captures } + end + local strip_captures = {} ---@type string[] + for i, capture in ipairs(captures) do + if capture:sub(1, 1) ~= "@" then + error 'Captures must start with "@"' + return + end + -- Remove leading "@". + strip_captures[i] = capture:sub(2) + end + + local matches = {} + for match in M.iter_group_results(bufnr, query_group, root, lang) do + for _, capture in ipairs(strip_captures) do + local insert = utils.get_at_path(match, capture) + if insert then + table.insert(matches, insert) + end + end + end + return matches +end + +function M.iter_captures(bufnr, query_name, root, lang) + local query, params = prepare_query(bufnr, query_name, root, lang) + if not query then + return EMPTY_ITER + end + assert(params) + + local iter = query:iter_captures(params.root, params.source, params.start, params.stop) + + local function wrapped_iter() + local id, node, metadata = iter() + if not id then + return + end + + local name = query.captures[id] + if string.sub(name, 1, 1) == "_" then + return wrapped_iter() + end + + return name, node, metadata + end + + return wrapped_iter +end + +---@param bufnr integer +---@param capture_string string +---@param query_group string +---@param filter_predicate fun(match: table): boolean +---@param scoring_function fun(match: table): number +---@param root TSNode +---@return table|unknown +function M.find_best_match(bufnr, capture_string, query_group, filter_predicate, scoring_function, root) + if string.sub(capture_string, 1, 1) == "@" then + --remove leading "@" + capture_string = string.sub(capture_string, 2) + end + + local best ---@type table|nil + local best_score ---@type number + + for maybe_match in M.iter_group_results(bufnr, query_group, root) do + local match = utils.get_at_path(maybe_match, capture_string) + + if match and filter_predicate(match) then + local current_score = scoring_function(match) + if not best then + best = match + best_score = current_score + end + if current_score > best_score then + best = match + best_score = current_score + end + end + end + return best +end + +---Iterates matches from a query file. +---@param bufnr integer the buffer +---@param query_group string the query file to use +---@param root TSNode the root node +---@param root_lang? string the root node lang, if known +function M.iter_group_results(bufnr, query_group, root, root_lang) + local query, params = prepare_query(bufnr, query_group, root, root_lang) + if not query then + return EMPTY_ITER + end + assert(params) + + return M.iter_prepared_matches(query, params.root, params.source, params.start, params.stop) +end + +function M.collect_group_results(bufnr, query_group, root, lang) + local matches = {} + + for prepared_match in M.iter_group_results(bufnr, query_group, root, lang) do + table.insert(matches, prepared_match) + end + + return matches +end + +---@alias CaptureResFn function(string, LanguageTree, LanguageTree): string, string + +-- Same as get_capture_matches except this will recursively get matches for every language in the tree. +---@param bufnr integer The buffer +---@param capture_or_fn string|CaptureResFn The capture to get. If a function is provided then that +--- function will be used to resolve both the capture and query argument. +--- The function can return `nil` to ignore that tree. +---@param query_type string? The query to get the capture from. This is ignored if a function is provided +--- for the capture argument. +---@return table[] +function M.get_capture_matches_recursively(bufnr, capture_or_fn, query_type) + ---@type CaptureResFn + local type_fn + if type(capture_or_fn) == "function" then + type_fn = capture_or_fn + else + type_fn = function(_, _, _) + return capture_or_fn, query_type + end + end + local parser = parsers.get_parser(bufnr) + local matches = {} + + if parser then + parser:for_each_tree(function(tree, lang_tree) + local lang = lang_tree:lang() + local capture, type_ = type_fn(lang, tree, lang_tree) + + if capture then + vim.list_extend(matches, M.get_capture_matches(bufnr, capture, type_, tree:root(), lang) or {}) + end + end) + end + + return matches +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/query_predicates.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/query_predicates.lua new file mode 100644 index 00000000..61400e47 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/query_predicates.lua @@ -0,0 +1,238 @@ +local query = require "vim.treesitter.query" + +local html_script_type_languages = { + ["importmap"] = "json", + ["module"] = "javascript", + ["application/ecmascript"] = "javascript", + ["text/ecmascript"] = "javascript", +} + +local non_filetype_match_injection_language_aliases = { + ex = "elixir", + pl = "perl", + sh = "bash", + uxn = "uxntal", + ts = "typescript", +} + +local function get_parser_from_markdown_info_string(injection_alias) + local match = vim.filetype.match { filename = "a." .. injection_alias } + return match or non_filetype_match_injection_language_aliases[injection_alias] or injection_alias +end + +local function error(str) + vim.api.nvim_err_writeln(str) +end + +local function valid_args(name, pred, count, strict_count) + local arg_count = #pred - 1 + + if strict_count then + if arg_count ~= count then + error(string.format("%s must have exactly %d arguments", name, count)) + return false + end + elseif arg_count < count then + error(string.format("%s must have at least %d arguments", name, count)) + return false + end + + return true +end + +---@param match (TSNode|nil)[] +---@param _pattern string +---@param _bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_predicate("nth?", function(match, _pattern, _bufnr, pred) + if not valid_args("nth?", pred, 2, true) then + return + end + + local node = match[pred[2]] ---@type TSNode + local n = tonumber(pred[3]) + if node and node:parent() and node:parent():named_child_count() > n then + return node:parent():named_child(n) == node + end + + return false +end, true) + +---@param match (TSNode|nil)[] +---@param _pattern string +---@param _bufnr integer +---@param pred string[] +---@return boolean|nil +local function has_ancestor(match, _pattern, _bufnr, pred) + if not valid_args(pred[1], pred, 2) then + return + end + + local node = match[pred[2]] + local ancestor_types = { unpack(pred, 3) } + if not node then + return true + end + + local just_direct_parent = pred[1]:find("has-parent", 1, true) + + node = node:parent() + while node do + if vim.tbl_contains(ancestor_types, node:type()) then + return true + end + if just_direct_parent then + node = nil + else + node = node:parent() + end + end + return false +end + +query.add_predicate("has-ancestor?", has_ancestor, true) + +query.add_predicate("has-parent?", has_ancestor, true) + +---@param match (TSNode|nil)[] +---@param _pattern string +---@param bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_predicate("is?", function(match, _pattern, bufnr, pred) + if not valid_args("is?", pred, 2) then + return + end + + -- Avoid circular dependencies + local locals = require "nvim-treesitter.locals" + local node = match[pred[2]] + local types = { unpack(pred, 3) } + + if not node then + return true + end + + local _, _, kind = locals.find_definition(node, bufnr) + + return vim.tbl_contains(types, kind) +end, true) + +---@param match (TSNode|nil)[] +---@param _pattern string +---@param _bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_predicate("has-type?", function(match, _pattern, _bufnr, pred) + if not valid_args(pred[1], pred, 2) then + return + end + + local node = match[pred[2]] + local types = { unpack(pred, 3) } + + if not node then + return true + end + + return vim.tbl_contains(types, node:type()) +end, true) + +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_directive("set-lang-from-mimetype!", function(match, _, bufnr, pred, metadata) + local capture_id = pred[2] + local node = match[capture_id] + if not node then + return + end + local type_attr_value = vim.treesitter.get_node_text(node, bufnr) + local configured = html_script_type_languages[type_attr_value] + if configured then + metadata["injection.language"] = configured + else + local parts = vim.split(type_attr_value, "/", {}) + metadata["injection.language"] = parts[#parts] + end +end, true) + +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_directive("set-lang-from-info-string!", function(match, _, bufnr, pred, metadata) + local capture_id = pred[2] + local node = match[capture_id] + if not node then + return + end + local injection_alias = vim.treesitter.get_node_text(node, bufnr):lower() + metadata["injection.language"] = get_parser_from_markdown_info_string(injection_alias) +end, true) + +-- Just avoid some annoying warnings for this directive +query.add_directive("make-range!", function() end, true) + +--- transform node text to lower case (e.g., to make @injection.language case insensitive) +--- +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@return boolean|nil +query.add_directive("downcase!", function(match, _, bufnr, pred, metadata) + local id = pred[2] + local node = match[id] + if not node then + return + end + + local text = vim.treesitter.get_node_text(node, bufnr, { metadata = metadata[id] }) or "" + if not metadata[id] then + metadata[id] = {} + end + metadata[id].text = string.lower(text) +end, true) + +-- Trim blank lines from end of the region +-- Arguments are the captures to trim. +---@param match (TSNode|nil)[] +---@param _ string +---@param bufnr integer +---@param pred string[] +---@param metadata table +query.add_directive("trim!", function(match, _, bufnr, pred, metadata) + for _, id in ipairs { select(2, unpack(pred)) } do + local node = match[id] + local start_row, start_col, end_row, end_col = node:range() + + -- Don't trim if region ends in middle of a line + if end_col ~= 0 then + return + end + + while true do + -- As we only care when end_col == 0, always inspect one line above end_row. + local end_line = vim.api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1] + + if end_line ~= "" then + break + end + + end_row = end_row - 1 + end + + -- If this produces an invalid range, we just skip it. + if start_row < end_row or (start_row == end_row and start_col <= end_col) then + if not metadata[id] then + metadata[id] = {} + end + metadata[id].range = { start_row, start_col, end_row, end_col } + end + end +end, true) diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/shell_command_selectors.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/shell_command_selectors.lua new file mode 100644 index 00000000..f1d557a7 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/shell_command_selectors.lua @@ -0,0 +1,337 @@ +local fn = vim.fn +local utils = require "nvim-treesitter.utils" + +-- Convert path for cmd.exe on Windows. +-- This is needed when vim.opt.shellslash is in use. +---@param p string +---@return string +local function cmdpath(p) + if vim.opt.shellslash:get() then + local r = p:gsub("/", "\\") + return r + else + return p + end +end + +local M = {} + +-- Returns the mkdir command based on the OS +---@param directory string +---@param cwd string +---@param info_msg string +---@return table +function M.select_mkdir_cmd(directory, cwd, info_msg) + if fn.has "win32" == 1 then + return { + cmd = "cmd", + opts = { + args = { "/C", "mkdir", cmdpath(directory) }, + cwd = cwd, + }, + info = info_msg, + err = "Could not create " .. directory, + } + else + return { + cmd = "mkdir", + opts = { + args = { directory }, + cwd = cwd, + }, + info = info_msg, + err = "Could not create " .. directory, + } + end +end + +-- Returns the remove command based on the OS +---@param file string +---@param info_msg string +---@return table +function M.select_rm_file_cmd(file, info_msg) + if fn.has "win32" == 1 then + return { + cmd = "cmd", + opts = { + args = { "/C", "if", "exist", cmdpath(file), "del", cmdpath(file) }, + }, + info = info_msg, + err = "Could not delete " .. file, + } + else + return { + cmd = "rm", + opts = { + args = { file }, + }, + info = info_msg, + err = "Could not delete " .. file, + } + end +end + +---@param executables string[] +---@return string|nil +function M.select_executable(executables) + return vim.tbl_filter(function(c) ---@param c string + return c ~= vim.NIL and fn.executable(c) == 1 + end, executables)[1] +end + +-- Returns the compiler arguments based on the compiler and OS +---@param repo InstallInfo +---@param compiler string +---@return string[] +function M.select_compiler_args(repo, compiler) + if string.match(compiler, "cl$") or string.match(compiler, "cl.exe$") then + return { + "/Fe:", + "parser.so", + "/Isrc", + repo.files, + "-Os", + "/utf-8", + "/LD", + } + elseif string.match(compiler, "zig$") or string.match(compiler, "zig.exe$") then + return { + "c++", + "-o", + "parser.so", + repo.files, + "-lc", + "-Isrc", + "-shared", + "-Os", + } + else + local args = { + "-o", + "parser.so", + "-I./src", + repo.files, + "-Os", + } + if fn.has "mac" == 1 then + table.insert(args, "-bundle") + else + table.insert(args, "-shared") + end + if + #vim.tbl_filter(function(file) ---@param file string + local ext = vim.fn.fnamemodify(file, ":e") + return ext == "cc" or ext == "cpp" or ext == "cxx" + end, repo.files) > 0 + then + table.insert(args, "-lstdc++") + end + if fn.has "win32" == 0 then + table.insert(args, "-fPIC") + end + return args + end +end + +-- Returns the compile command based on the OS and user options +---@param repo InstallInfo +---@param cc string +---@param compile_location string +---@return Command +function M.select_compile_command(repo, cc, compile_location) + local make = M.select_executable { "gmake", "make" } + if + string.match(cc, "cl$") + or string.match(cc, "cl.exe$") + or not repo.use_makefile + or fn.has "win32" == 1 + or not make + then + return { + cmd = cc, + info = "Compiling...", + err = "Error during compilation", + opts = { + args = require("nvim-treesitter.compat").flatten(M.select_compiler_args(repo, cc)), + cwd = compile_location, + }, + } + else + return { + cmd = make, + info = "Compiling...", + err = "Error during compilation", + opts = { + args = { + "--makefile=" .. utils.join_path(utils.get_package_path(), "scripts", "compile_parsers.makefile"), + "CC=" .. cc, + "CXX_STANDARD=" .. (repo.cxx_standard or "c++14"), + }, + cwd = compile_location, + }, + } + end +end + +-- Returns the remove command based on the OS +---@param cache_folder string +---@param project_name string +---@return Command +function M.select_install_rm_cmd(cache_folder, project_name) + if fn.has "win32" == 1 then + local dir = cache_folder .. "\\" .. project_name + return { + cmd = "cmd", + opts = { + args = { "/C", "if", "exist", cmdpath(dir), "rmdir", "/s", "/q", cmdpath(dir) }, + }, + } + else + return { + cmd = "rm", + opts = { + args = { "-rf", cache_folder .. "/" .. project_name }, + }, + } + end +end + +-- Returns the move command based on the OS +---@param from string +---@param to string +---@param cwd string +---@return Command +function M.select_mv_cmd(from, to, cwd) + if fn.has "win32" == 1 then + return { + cmd = "cmd", + opts = { + args = { "/C", "move", "/Y", cmdpath(from), cmdpath(to) }, + cwd = cwd, + }, + } + else + return { + cmd = "mv", + opts = { + args = { "-f", from, to }, + cwd = cwd, + }, + } + end +end + +---@param repo InstallInfo +---@param project_name string +---@param cache_folder string +---@param revision string|nil +---@param prefer_git boolean +---@return table +function M.select_download_commands(repo, project_name, cache_folder, revision, prefer_git) + local can_use_tar = vim.fn.executable "tar" == 1 and vim.fn.executable "curl" == 1 + local is_github = repo.url:find("github.com", 1, true) + local is_gitlab = repo.url:find("gitlab.com", 1, true) + + revision = revision or repo.branch or "master" + + if can_use_tar and (is_github or is_gitlab) and not prefer_git then + local path_sep = utils.get_path_sep() + local url = repo.url:gsub(".git$", "") + + local folder_rev = revision + if is_github and revision:match "^v%d" then + folder_rev = revision:sub(2) + end + + return { + M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"), + { + cmd = "curl", + info = "Downloading " .. project_name .. "...", + err = "Error during download, please verify your internet connection", + opts = { + args = { + "--silent", + "-L", -- follow redirects + is_github and url .. "/archive/" .. revision .. ".tar.gz" + or url .. "/-/archive/" .. revision .. "/" .. project_name .. "-" .. revision .. ".tar.gz", + "--output", + project_name .. ".tar.gz", + }, + cwd = cache_folder, + }, + }, + M.select_mkdir_cmd(project_name .. "-tmp", cache_folder, "Creating temporary directory"), + { + cmd = "tar", + info = "Extracting " .. project_name .. "...", + err = "Error during tarball extraction.", + opts = { + args = { + "-xvzf", + project_name .. ".tar.gz", + "-C", + project_name .. "-tmp", + }, + cwd = cache_folder, + }, + }, + M.select_rm_file_cmd(cache_folder .. path_sep .. project_name .. ".tar.gz"), + M.select_mv_cmd( + utils.join_path(project_name .. "-tmp", url:match "[^/]-$" .. "-" .. folder_rev), + project_name, + cache_folder + ), + M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"), + } + else + local git_folder = utils.join_path(cache_folder, project_name) + local clone_error = "Error during download, please verify your internet connection" + + return { + { + cmd = "git", + info = "Downloading " .. project_name .. "...", + err = clone_error, + opts = { + args = { + "clone", + repo.url, + project_name, + "--filter=blob:none", + }, + cwd = cache_folder, + }, + }, + { + cmd = "git", + info = "Checking out locked revision", + err = "Error while checking out revision", + opts = { + args = { + "checkout", + revision, + }, + cwd = git_folder, + }, + }, + } + end +end + +---@param dir string +---@param command string +---@return string command +function M.make_directory_change_for_command(dir, command) + if fn.has "win32" == 1 then + if string.find(vim.o.shell, "cmd") ~= nil then + return string.format("pushd %s & %s & popd", cmdpath(dir), command) + else + return string.format("pushd %s ; %s ; popd", cmdpath(dir), command) + end + else + return string.format("cd %s;\n%s", dir, command) + end +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/statusline.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/statusline.lua new file mode 100644 index 00000000..68ba41ac --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/statusline.lua @@ -0,0 +1,53 @@ +local parsers = require "nvim-treesitter.parsers" +local ts_utils = require "nvim-treesitter.ts_utils" + +local M = {} + +-- Trim spaces and opening brackets from end +local transform_line = function(line) + return line:gsub("%s*[%[%(%{]*%s*$", "") +end + +function M.statusline(opts) + if not parsers.has_parser() then + return + end + local options = opts or {} + if type(opts) == "number" then + options = { indicator_size = opts } + end + local bufnr = options.bufnr or 0 + local indicator_size = options.indicator_size or 100 + local type_patterns = options.type_patterns or { "class", "function", "method" } + local transform_fn = options.transform_fn or transform_line + local separator = options.separator or " -> " + local allow_duplicates = options.allow_duplicates or false + + local current_node = ts_utils.get_node_at_cursor() + if not current_node then + return "" + end + + local lines = {} + local expr = current_node + + while expr do + local line = ts_utils._get_line_for_node(expr, type_patterns, transform_fn, bufnr) + if line ~= "" then + if allow_duplicates or not vim.tbl_contains(lines, line) then + table.insert(lines, 1, line) + end + end + expr = expr:parent() + end + + local text = table.concat(lines, separator) + local text_len = #text + if text_len > indicator_size then + return "..." .. text:sub(text_len - indicator_size, text_len) + end + + return text +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/ts_utils.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/ts_utils.lua new file mode 100644 index 00000000..655c6288 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/ts_utils.lua @@ -0,0 +1,467 @@ +local api = vim.api + +local parsers = require "nvim-treesitter.parsers" +local utils = require "nvim-treesitter.utils" +local ts = vim.treesitter + +local M = {} + +local function get_node_text(node, bufnr) + bufnr = bufnr or api.nvim_get_current_buf() + if not node then + return {} + end + + -- We have to remember that end_col is end-exclusive + local start_row, start_col, end_row, end_col = ts.get_node_range(node) + + if start_row ~= end_row then + local lines = api.nvim_buf_get_lines(bufnr, start_row, end_row + 1, false) + if next(lines) == nil then + return {} + end + lines[1] = string.sub(lines[1], start_col + 1) + -- end_row might be just after the last line. In this case the last line is not truncated. + if #lines == end_row - start_row + 1 then + lines[#lines] = string.sub(lines[#lines], 1, end_col) + end + return lines + else + local line = api.nvim_buf_get_lines(bufnr, start_row, start_row + 1, false)[1] + -- If line is nil then the line is empty + return line and { string.sub(line, start_col + 1, end_col) } or {} + end +end + +---@private +---@param node TSNode +---@param type_patterns string[] +---@param transform_fn fun(line: string): string +---@param bufnr integer +---@return string +function M._get_line_for_node(node, type_patterns, transform_fn, bufnr) + local node_type = node:type() + local is_valid = false + for _, rgx in ipairs(type_patterns) do + if node_type:find(rgx) then + is_valid = true + break + end + end + if not is_valid then + return "" + end + local line = transform_fn(vim.trim(get_node_text(node, bufnr)[1] or ""), node) + -- Escape % to avoid statusline to evaluate content as expression + return line:gsub("%%", "%%%%") +end + +-- Gets the actual text content of a node +-- @deprecated Use vim.treesitter.query.get_node_text +-- @param node the node to get the text from +-- @param bufnr the buffer containing the node +-- @return list of lines of text of the node +function M.get_node_text(node, bufnr) + vim.notify_once( + "nvim-treesitter.ts_utils.get_node_text is deprecated: use vim.treesitter.query.get_node_text", + vim.log.levels.WARN + ) + return get_node_text(node, bufnr) +end + +-- Determines whether a node is the parent of another +-- @param dest the possible parent +-- @param source the possible child node +function M.is_parent(dest, source) + if not (dest and source) then + return false + end + + local current = source + while current ~= nil do + if current == dest then + return true + end + + current = current:parent() + end + + return false +end + +-- Get next node with same parent +---@param node TSNode +---@param allow_switch_parents? boolean allow switching parents if last node +---@param allow_next_parent? boolean allow next parent if last node and next parent without children +function M.get_next_node(node, allow_switch_parents, allow_next_parent) + local destination_node ---@type TSNode + local parent = node:parent() + + if not parent then + return + end + local found_pos = 0 + for i = 0, parent:named_child_count() - 1, 1 do + if parent:named_child(i) == node then + found_pos = i + break + end + end + if parent:named_child_count() > found_pos + 1 then + destination_node = parent:named_child(found_pos + 1) + elseif allow_switch_parents then + local next_node = M.get_next_node(node:parent()) + if next_node and next_node:named_child_count() > 0 then + destination_node = next_node:named_child(0) + elseif next_node and allow_next_parent then + destination_node = next_node + end + end + + return destination_node +end + +-- Get previous node with same parent +---@param node TSNode +---@param allow_switch_parents? boolean allow switching parents if first node +---@param allow_previous_parent? boolean allow previous parent if first node and previous parent without children +function M.get_previous_node(node, allow_switch_parents, allow_previous_parent) + local destination_node ---@type TSNode + local parent = node:parent() + if not parent then + return + end + + local found_pos = 0 + for i = 0, parent:named_child_count() - 1, 1 do + if parent:named_child(i) == node then + found_pos = i + break + end + end + if 0 < found_pos then + destination_node = parent:named_child(found_pos - 1) + elseif allow_switch_parents then + local previous_node = M.get_previous_node(node:parent()) + if previous_node and previous_node:named_child_count() > 0 then + destination_node = previous_node:named_child(previous_node:named_child_count() - 1) + elseif previous_node and allow_previous_parent then + destination_node = previous_node + end + end + return destination_node +end + +function M.get_named_children(node) + local nodes = {} ---@type TSNode[] + for i = 0, node:named_child_count() - 1, 1 do + nodes[i + 1] = node:named_child(i) + end + return nodes +end + +function M.get_node_at_cursor(winnr, ignore_injected_langs) + winnr = winnr or 0 + local cursor = api.nvim_win_get_cursor(winnr) + local cursor_range = { cursor[1] - 1, cursor[2] } + + local buf = vim.api.nvim_win_get_buf(winnr) + local root_lang_tree = parsers.get_parser(buf) + if not root_lang_tree then + return + end + + local root ---@type TSNode|nil + if ignore_injected_langs then + for _, tree in pairs(root_lang_tree:trees()) do + local tree_root = tree:root() + if tree_root and ts.is_in_node_range(tree_root, cursor_range[1], cursor_range[2]) then + root = tree_root + break + end + end + else + root = M.get_root_for_position(cursor_range[1], cursor_range[2], root_lang_tree) + end + + if not root then + return + end + + return root:named_descendant_for_range(cursor_range[1], cursor_range[2], cursor_range[1], cursor_range[2]) +end + +function M.get_root_for_position(line, col, root_lang_tree) + if not root_lang_tree then + if not parsers.has_parser() then + return + end + + root_lang_tree = parsers.get_parser() + end + + local lang_tree = root_lang_tree:language_for_range { line, col, line, col } + + for _, tree in pairs(lang_tree:trees()) do + local root = tree:root() + + if root and ts.is_in_node_range(root, line, col) then + return root, tree, lang_tree + end + end + + -- This isn't a likely scenario, since the position must belong to a tree somewhere. + return nil, nil, lang_tree +end + +---comment +---@param node TSNode +---@return TSNode result +function M.get_root_for_node(node) + local parent = node + local result = node + + while parent ~= nil do + result = parent + parent = result:parent() + end + + return result +end + +function M.highlight_node(node, buf, hl_namespace, hl_group) + if not node then + return + end + M.highlight_range({ node:range() }, buf, hl_namespace, hl_group) +end + +-- Get a compatible vim range (1 index based) from a TS node range. +-- +-- TS nodes start with 0 and the end col is ending exclusive. +-- They also treat a EOF/EOL char as a char ending in the first +-- col of the next row. +---comment +---@param range integer[] +---@param buf integer|nil +---@return integer, integer, integer, integer +function M.get_vim_range(range, buf) + ---@type integer, integer, integer, integer + local srow, scol, erow, ecol = unpack(range) + srow = srow + 1 + scol = scol + 1 + erow = erow + 1 + + if ecol == 0 then + -- Use the value of the last col of the previous row instead. + erow = erow - 1 + if not buf or buf == 0 then + ecol = vim.fn.col { erow, "$" } - 1 + else + ecol = #api.nvim_buf_get_lines(buf, erow - 1, erow, false)[1] + end + ecol = math.max(ecol, 1) + end + return srow, scol, erow, ecol +end + +function M.highlight_range(range, buf, hl_namespace, hl_group) + ---@type integer, integer, integer, integer + local start_row, start_col, end_row, end_col = unpack(range) + ---@diagnostic disable-next-line: missing-parameter + vim.highlight.range(buf, hl_namespace, hl_group, { start_row, start_col }, { end_row, end_col }) +end + +-- Set visual selection to node +-- @param selection_mode One of "charwise" (default) or "v", "linewise" or "V", +-- "blockwise" or "" (as a string with 5 characters or a single character) +function M.update_selection(buf, node, selection_mode) + local start_row, start_col, end_row, end_col = M.get_vim_range({ ts.get_node_range(node) }, buf) + + local v_table = { charwise = "v", linewise = "V", blockwise = "" } + selection_mode = selection_mode or "charwise" + + -- Normalise selection_mode + if vim.tbl_contains(vim.tbl_keys(v_table), selection_mode) then + selection_mode = v_table[selection_mode] + end + + -- enter visual mode if normal or operator-pending (no) mode + -- Why? According to https://learnvimscriptthehardway.stevelosh.com/chapters/15.html + -- If your operator-pending mapping ends with some text visually selected, Vim will operate on that text. + -- Otherwise, Vim will operate on the text between the original cursor position and the new position. + local mode = api.nvim_get_mode() + if mode.mode ~= selection_mode then + -- Call to `nvim_replace_termcodes()` is needed for sending appropriate command to enter blockwise mode + selection_mode = vim.api.nvim_replace_termcodes(selection_mode, true, true, true) + api.nvim_cmd({ cmd = "normal", bang = true, args = { selection_mode } }, {}) + end + + api.nvim_win_set_cursor(0, { start_row, start_col - 1 }) + vim.cmd "normal! o" + api.nvim_win_set_cursor(0, { end_row, end_col - 1 }) +end + +-- Byte length of node range +---@param node TSNode +---@return number +function M.node_length(node) + local _, _, start_byte = node:start() + local _, _, end_byte = node:end_() + return end_byte - start_byte +end + +---@deprecated Use `vim.treesitter.is_in_node_range()` instead +function M.is_in_node_range(node, line, col) + vim.notify_once( + "nvim-treesitter.ts_utils.is_in_node_range is deprecated: use vim.treesitter.is_in_node_range", + vim.log.levels.WARN + ) + return ts.is_in_node_range(node, line, col) +end + +---@deprecated Use `vim.treesitter.get_node_range()` instead +function M.get_node_range(node_or_range) + vim.notify_once( + "nvim-treesitter.ts_utils.get_node_range is deprecated: use vim.treesitter.get_node_range", + vim.log.levels.WARN + ) + return ts.get_node_range(node_or_range) +end + +---@param node TSNode +---@return table +function M.node_to_lsp_range(node) + local start_line, start_col, end_line, end_col = ts.get_node_range(node) + local rtn = {} + rtn.start = { line = start_line, character = start_col } + rtn["end"] = { line = end_line, character = end_col } + return rtn +end + +-- Memoizes a function based on the buffer tick of the provided bufnr. +-- The cache entry is cleared when the buffer is detached to avoid memory leaks. +-- The options argument is a table with two optional values: +-- - bufnr: extracts a bufnr from the given arguments. +-- - key: extracts the cache key from the given arguments. +---@param fn function the fn to memoize, taking the buffer as first argument +---@param options? {bufnr: integer?, key: string|fun(...): string?} the memoization options +---@return function: a memoized function +function M.memoize_by_buf_tick(fn, options) + options = options or {} + + ---@type table + local cache = setmetatable({}, { __mode = "kv" }) + local bufnr_fn = utils.to_func(options.bufnr or utils.identity) + local key_fn = utils.to_func(options.key or utils.identity) + + return function(...) + local bufnr = bufnr_fn(...) + local key = key_fn(...) + local tick = api.nvim_buf_get_changedtick(bufnr) + + if cache[key] then + if cache[key].last_tick == tick then + return cache[key].result + end + else + local function detach_handler() + cache[key] = nil + end + + -- Clean up logic only! + api.nvim_buf_attach(bufnr, false, { + on_detach = detach_handler, + on_reload = detach_handler, + }) + end + + cache[key] = { + result = fn(...), + last_tick = tick, + } + + return cache[key].result + end +end + +function M.swap_nodes(node_or_range1, node_or_range2, bufnr, cursor_to_second) + if not node_or_range1 or not node_or_range2 then + return + end + local range1 = M.node_to_lsp_range(node_or_range1) + local range2 = M.node_to_lsp_range(node_or_range2) + + local text1 = get_node_text(node_or_range1, bufnr) + local text2 = get_node_text(node_or_range2, bufnr) + + local edit1 = { range = range1, newText = table.concat(text2, "\n") } + local edit2 = { range = range2, newText = table.concat(text1, "\n") } + vim.lsp.util.apply_text_edits({ edit1, edit2 }, bufnr, "utf-8") + + if cursor_to_second then + utils.set_jump() + + local char_delta = 0 + local line_delta = 0 + if + range1["end"].line < range2.start.line + or (range1["end"].line == range2.start.line and range1["end"].character <= range2.start.character) + then + line_delta = #text2 - #text1 + end + + if range1["end"].line == range2.start.line and range1["end"].character <= range2.start.character then + if line_delta ~= 0 then + --- why? + --correction_after_line_change = -range2.start.character + --text_now_before_range2 = #(text2[#text2]) + --space_between_ranges = range2.start.character - range1["end"].character + --char_delta = correction_after_line_change + text_now_before_range2 + space_between_ranges + --- Equivalent to: + char_delta = #text2[#text2] - range1["end"].character + + -- add range1.start.character if last line of range1 (now text2) does not start at 0 + if range1.start.line == range2.start.line + line_delta then + char_delta = char_delta + range1.start.character + end + else + char_delta = #text2[#text2] - #text1[#text1] + end + end + + api.nvim_win_set_cursor( + api.nvim_get_current_win(), + { range2.start.line + 1 + line_delta, range2.start.character + char_delta } + ) + end +end + +function M.goto_node(node, goto_end, avoid_set_jump) + if not node then + return + end + if not avoid_set_jump then + utils.set_jump() + end + local range = { M.get_vim_range { node:range() } } + ---@type table + local position + if not goto_end then + position = { range[1], range[2] } + else + position = { range[3], range[4] } + end + + -- Enter visual mode if we are in operator pending mode + -- If we don't do this, it will miss the last character. + local mode = vim.api.nvim_get_mode() + if mode.mode == "no" then + vim.cmd "normal! v" + end + + -- Position is 1, 0 indexed. + api.nvim_win_set_cursor(0, { position[1], position[2] - 1 }) +end + +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/tsrange.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/tsrange.lua new file mode 100644 index 00000000..d41585c6 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/tsrange.lua @@ -0,0 +1,154 @@ +local M = {} +local TSRange = {} +TSRange.__index = TSRange + +local api = vim.api +local ts_utils = require "nvim-treesitter.ts_utils" +local parsers = require "nvim-treesitter.parsers" + +local function get_byte_offset(buf, row, col) + return api.nvim_buf_get_offset(buf, row) + vim.fn.byteidx(api.nvim_buf_get_lines(buf, row, row + 1, false)[1], col) +end + +function TSRange.new(buf, start_row, start_col, end_row, end_col) + return setmetatable({ + start_pos = { start_row, start_col, get_byte_offset(buf, start_row, start_col) }, + end_pos = { end_row, end_col, get_byte_offset(buf, end_row, end_col) }, + buf = buf, + [1] = start_row, + [2] = start_col, + [3] = end_row, + [4] = end_col, + }, TSRange) +end + +function TSRange.from_nodes(buf, start_node, end_node) + TSRange.__index = TSRange + local start_pos = start_node and { start_node:start() } or { end_node:start() } + local end_pos = end_node and { end_node:end_() } or { start_node:end_() } + return setmetatable({ + start_pos = { start_pos[1], start_pos[2], start_pos[3] }, + end_pos = { end_pos[1], end_pos[2], end_pos[3] }, + buf = buf, + [1] = start_pos[1], + [2] = start_pos[2], + [3] = end_pos[1], + [4] = end_pos[2], + }, TSRange) +end + +function TSRange.from_table(buf, range) + return setmetatable({ + start_pos = { range[1], range[2], get_byte_offset(buf, range[1], range[2]) }, + end_pos = { range[3], range[4], get_byte_offset(buf, range[3], range[4]) }, + buf = buf, + [1] = range[1], + [2] = range[2], + [3] = range[3], + [4] = range[4], + }, TSRange) +end + +function TSRange:parent() + local root_lang_tree = parsers.get_parser(self.buf) + local root = ts_utils.get_root_for_position(self[1], self[2], root_lang_tree) + + return root + and root:named_descendant_for_range(self.start_pos[1], self.start_pos[2], self.end_pos[1], self.end_pos[2]) + or nil +end + +function TSRange:field() end + +function TSRange:child_count() + return #self:collect_children() +end + +function TSRange:named_child_count() + return #self:collect_children(function(c) + return c:named() + end) +end + +function TSRange:iter_children() + local raw_iterator = self:parent().iter_children() + return function() + while true do + local node = raw_iterator() + if not node then + return + end + local _, _, start_byte = node:start() + local _, _, end_byte = node:end_() + if start_byte >= self.start_pos[3] and end_byte <= self.end_pos[3] then + return node + end + end + end +end + +function TSRange:collect_children(filter_fun) + local children = {} + for _, c in self:iter_children() do + if not filter_fun or filter_fun(c) then + table.insert(children, c) + end + end + return children +end + +function TSRange:child(index) + return self:collect_children()[index + 1] +end + +function TSRange:named_child(index) + return self:collect_children(function(c) + return c.named() + end)[index + 1] +end + +function TSRange:start() + return unpack(self.start_pos) +end + +function TSRange:end_() + return unpack(self.end_pos) +end + +function TSRange:range() + return self.start_pos[1], self.start_pos[2], self.end_pos[1], self.end_pos[2] +end + +function TSRange:type() + return "nvim-treesitter-range" +end + +function TSRange:symbol() + return -1 +end + +function TSRange:named() + return false +end + +function TSRange:missing() + return false +end + +function TSRange:has_error() + return #self:collect_children(function(c) + return c:has_error() + end) > 0 and true or false +end + +function TSRange:sexpr() + return table.concat( + vim.tbl_map(function(c) + return c:sexpr() + end, self:collect_children()), + " " + ) +end + +M.TSRange = TSRange +return M diff --git a/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/utils.lua b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/utils.lua new file mode 100644 index 00000000..d920f4a6 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/lua/nvim-treesitter/utils.lua @@ -0,0 +1,237 @@ +local api = vim.api +local fn = vim.fn +local luv = vim.loop + +local M = {} + +-- Wrapper around vim.notify with common options set. +---@param msg string +---@param log_level number|nil +---@param opts table|nil +function M.notify(msg, log_level, opts) + local default_opts = { title = "nvim-treesitter" } + vim.notify(msg, log_level, vim.tbl_extend("force", default_opts, opts or {})) +end + +-- Returns the system-specific path separator. +---@return string +function M.get_path_sep() + return (fn.has "win32" == 1 and not vim.opt.shellslash:get()) and "\\" or "/" +end + +-- Returns a function that joins the given arguments with separator. Arguments +-- can't be nil. Example: +-- +--[[ + print(M.generate_join(" ")("foo", "bar")) +--]] +--prints "foo bar" +---@param separator string +---@return fun(...: string): string +function M.generate_join(separator) + return function(...) + return table.concat({ ... }, separator) + end +end + +M.join_path = M.generate_join(M.get_path_sep()) + +M.join_space = M.generate_join " " + +---@class Command +---@field run function +---@field f_args string +---@field args string + +-- Define user defined vim command which calls nvim-treesitter module function +-- - If module name is 'mod', it should be defined in hierarchy 'nvim-treesitter.mod' +-- - A table with name 'commands' should be defined in 'mod' which needs to be passed as +-- the commands param of this function +-- +---@param mod string Name of the module that resides in the hierarchy - nvim-treesitter.module +---@param commands table Command list for the module +--- - {command_name} Name of the vim user defined command, Keys: +--- - {run}: (function) callback function that needs to be executed +--- - {f_args}: (string, default ) +--- - type of arguments that needs to be passed to the vim command +--- - {args}: (string, optional) +--- - vim command attributes +--- +---* @example +--- If module is nvim-treesitter.custom_mod +---
+---  M.commands = {
+---      custom_command = {
+---          run = M.module_function,
+---          f_args = "",
+---          args = {
+---              "-range"
+---          }
+---      }
+---  }
+---
+---  utils.setup_commands("custom_mod", require("nvim-treesitter.custom_mod").commands)
+---  
+--- +--- Will generate command : +---
+---  command! -range custom_command \
+---      lua require'nvim-treesitter.custom_mod'.commands.custom_command['run']()
+---  
+function M.setup_commands(mod, commands) + for command_name, def in pairs(commands) do + local f_args = def.f_args or "" + local call_fn = + string.format("lua require'nvim-treesitter.%s'.commands.%s['run'](%s)", mod, command_name, f_args) + local parts = require("nvim-treesitter.compat").flatten { + "command!", + "-bar", + def.args, + command_name, + call_fn, + } + api.nvim_command(table.concat(parts, " ")) + end +end + +---@param dir string +---@param create_err string +---@param writeable_err string +---@return string|nil, string|nil +function M.create_or_reuse_writable_dir(dir, create_err, writeable_err) + create_err = create_err or M.join_space("Could not create dir '", dir, "': ") + writeable_err = writeable_err or M.join_space("Invalid rights, '", dir, "' should be read/write") + -- Try creating and using parser_dir if it doesn't exist + if not luv.fs_stat(dir) then + local ok, error = pcall(vim.fn.mkdir, dir, "p", "0755") + if not ok then + return nil, M.join_space(create_err, error) + end + + return dir + end + + -- parser_dir exists, use it if it's read/write + if luv.fs_access(dir, "RW") then + return dir + end + + -- parser_dir exists but isn't read/write, give up + return nil, M.join_space(writeable_err, dir, "'") +end + +function M.get_package_path() + -- Path to this source file, removing the leading '@' + local source = string.sub(debug.getinfo(1, "S").source, 2) + + -- Path to the package root + return fn.fnamemodify(source, ":p:h:h:h") +end + +function M.get_cache_dir() + local cache_dir = fn.stdpath "data" + + if luv.fs_access(cache_dir, "RW") then + return cache_dir + elseif luv.fs_access("/tmp", "RW") then + return "/tmp" + end + + return nil, M.join_space("Invalid cache rights,", fn.stdpath "data", "or /tmp should be read/write") +end + +-- Returns $XDG_DATA_HOME/nvim/site, but could use any directory that is in +-- runtimepath +function M.get_site_dir() + return M.join_path(fn.stdpath "data", "site") +end + +-- Gets a property at path +---@param tbl table the table to access +---@param path string the '.' separated path +---@return table|nil result the value at path or nil +function M.get_at_path(tbl, path) + if path == "" then + return tbl + end + + local segments = vim.split(path, ".", true) + ---@type table[]|table + local result = tbl + + for _, segment in ipairs(segments) do + if type(result) == "table" then + ---@type table + -- TODO: figure out the actual type of tbl + result = result[segment] + end + end + + return result +end + +function M.set_jump() + vim.cmd "normal! m'" +end + +-- Filters a list based on the given predicate +---@param tbl any[] The list to filter +---@param predicate fun(v:any, i:number):boolean The predicate to filter with +function M.filter(tbl, predicate) + local result = {} + + for i, v in ipairs(tbl) do + if predicate(v, i) then + table.insert(result, v) + end + end + + return result +end + +-- Returns a list of all values from the first list +-- that are not present in the second list. +---@param tbl1 any[] The first table +---@param tbl2 any[] The second table +---@return table +function M.difference(tbl1, tbl2) + return M.filter(tbl1, function(v) + return not vim.tbl_contains(tbl2, v) + end) +end + +function M.identity(a) + return a +end + +-- Returns a function returning the given value +---@param a any +---@return fun():any +function M.constant(a) + return function() + return a + end +end + +-- Returns a function that returns the given value if it is a function, +-- otherwise returns a function that returns the given value. +---@param a any +---@return fun(...):any +function M.to_func(a) + return type(a) == "function" and a or M.constant(a) +end + +---@return string|nil +function M.ts_cli_version() + if fn.executable "tree-sitter" == 1 then + local handle = io.popen "tree-sitter -V" + if not handle then + return + end + local result = handle:read "*a" + handle:close() + return vim.split(result, "\n")[1]:match "[^tree%psitter ].*" + end +end + +return M diff --git a/config/neovim/store/nvim-treesitter/parser-info/.gitignore b/config/neovim/store/nvim-treesitter/parser-info/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/parser-info/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/config/neovim/store/nvim-treesitter/plugin/nvim-treesitter.lua b/config/neovim/store/nvim-treesitter/plugin/nvim-treesitter.lua new file mode 100644 index 00000000..4ea3925f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/plugin/nvim-treesitter.lua @@ -0,0 +1,34 @@ +-- Last Change: 2022 Apr 16 + +if vim.g.loaded_nvim_treesitter then + return +end +vim.g.loaded_nvim_treesitter = true + +-- setup modules +require("nvim-treesitter").setup() + +local api = vim.api + +-- define autocommands +local augroup = api.nvim_create_augroup("NvimTreesitter", {}) + +api.nvim_create_autocmd("Filetype", { + pattern = "query", + group = augroup, + callback = function() + api.nvim_clear_autocmds { + group = augroup, + event = "BufWritePost", + } + api.nvim_create_autocmd("BufWritePost", { + group = augroup, + buffer = 0, + callback = function(opts) + require("nvim-treesitter.query").invalidate_query_file(opts.file) + end, + desc = "Invalidate query file", + }) + end, + desc = "Reload query", +}) diff --git a/config/neovim/store/nvim-treesitter/queries/ada/folds.scm b/config/neovim/store/nvim-treesitter/queries/ada/folds.scm new file mode 100644 index 00000000..8e3defac --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/ada/folds.scm @@ -0,0 +1,13 @@ +; Support for folding in Ada +; za toggles folding a package, subprogram, if statement or loop +[ + (package_declaration) + (generic_package_declaration) + (package_body) + (subprogram_body) + (block_statement) + (if_statement) + (loop_statement) + (gnatprep_declarative_if_statement) + (gnatprep_if_statement) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/ada/highlights.scm b/config/neovim/store/nvim-treesitter/queries/ada/highlights.scm new file mode 100644 index 00000000..0d42b70f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/ada/highlights.scm @@ -0,0 +1,286 @@ +; highlight queries. +; See the syntax at https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries +; See also https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md#parser-configurations +; for a list of recommended @ tags, though not all of them have matching +; highlights in neovim. +[ + "abort" + "abs" + "abstract" + "accept" + "access" + "all" + "array" + "at" + "begin" + "body" + "declare" + "delay" + "delta" + "digits" + "do" + "end" + "entry" + "exit" + "generic" + "interface" + "is" + "limited" + "mod" + "new" + "null" + "of" + "others" + "out" + "overriding" + "package" + "pragma" + "private" + "protected" + "range" + "separate" + "subtype" + "synchronized" + "tagged" + "task" + "terminate" + "type" + "until" + "when" +] @keyword + +"record" @keyword.type + +[ + "aliased" + "constant" + "renames" +] @keyword.modifier + +[ + "with" + "use" +] @keyword.import + +[ + "function" + "procedure" +] @keyword.function + +[ + "and" + "in" + "not" + "or" + "xor" +] @keyword.operator + +[ + "while" + "loop" + "for" + "parallel" + "reverse" + "some" +] @keyword.repeat + +"return" @keyword.return + +[ + "case" + "if" + "else" + "then" + "elsif" + "select" +] @keyword.conditional + +[ + "exception" + "raise" +] @keyword.exception + +(comment) @comment @spell + +(string_literal) @string + +(character_literal) @string + +(numeric_literal) @number + +; Highlight the name of subprograms +(procedure_specification + name: (_) @function) + +(function_specification + name: (_) @function) + +(package_declaration + name: (_) @function) + +(package_body + name: (_) @function) + +(generic_instantiation + name: (_) @function) + +(entry_declaration + . + (identifier) @function) + +; Some keywords should take different categories depending on the context +(use_clause + "use" @keyword.import + "type" @keyword.import) + +(with_clause + "private" @keyword.import) + +(with_clause + "limited" @keyword.import) + +(use_clause + (_) @module) + +(with_clause + (_) @module) + +(loop_statement + "end" @keyword.repeat) + +(if_statement + "end" @keyword.conditional) + +(loop_parameter_specification + "in" @keyword.repeat) + +(loop_parameter_specification + "in" @keyword.repeat) + +(iterator_specification + [ + "in" + "of" + ] @keyword.repeat) + +(range_attribute_designator + "range" @keyword.repeat) + +(raise_statement + "with" @keyword.exception) + +(gnatprep_declarative_if_statement) @keyword.directive + +(gnatprep_if_statement) @keyword.directive + +(gnatprep_identifier) @keyword.directive + +(subprogram_declaration + "is" @keyword.function + "abstract" @keyword.function) + +(aspect_specification + "with" @keyword.function) + +(full_type_declaration + "is" @keyword.type) + +(subtype_declaration + "is" @keyword.type) + +(record_definition + "end" @keyword.type) + +(full_type_declaration + (_ + "access" @keyword.type)) + +(array_type_definition + "array" @keyword.type + "of" @keyword.type) + +(access_to_object_definition + "access" @keyword.type) + +(access_to_object_definition + "access" @keyword.type + [ + (general_access_modifier + "constant" @keyword.type) + (general_access_modifier + "all" @keyword.type) + ]) + +(range_constraint + "range" @keyword.type) + +(signed_integer_type_definition + "range" @keyword.type) + +(index_subtype_definition + "range" @keyword.type) + +(record_type_definition + "abstract" @keyword.type) + +(record_type_definition + "tagged" @keyword.type) + +(record_type_definition + "limited" @keyword.type) + +(record_type_definition + (record_definition + "null" @keyword.type)) + +(private_type_declaration + "is" @keyword.type + "private" @keyword.type) + +(private_type_declaration + "tagged" @keyword.type) + +(private_type_declaration + "limited" @keyword.type) + +(task_type_declaration + "task" @keyword.type + "is" @keyword.type) + +; Gray the body of expression functions +(expression_function_declaration + (function_specification) + "is" + (_) @attribute) + +(subprogram_declaration + (aspect_specification) @attribute) + +; Highlight full subprogram specifications +;(subprogram_body +; [ +; (procedure_specification) +; (function_specification) +; ] @function.spec +;) +((comment) @comment.documentation + . + [ + (entry_declaration) + (subprogram_declaration) + (parameter_specification) + ]) + +(compilation_unit + . + (comment) @comment.documentation) + +(component_list + (component_declaration) + . + (comment) @comment.documentation) + +(enumeration_type_definition + (identifier) + . + (comment) @comment.documentation) diff --git a/config/neovim/store/nvim-treesitter/queries/ada/locals.scm b/config/neovim/store/nvim-treesitter/queries/ada/locals.scm new file mode 100644 index 00000000..bdfc38be --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/ada/locals.scm @@ -0,0 +1,91 @@ +; Better highlighting by referencing to the definition, for variable +; references. However, this is not yet supported by neovim +; See https://tree-sitter.github.io/tree-sitter/syntax-highlighting#local-variables +(compilation) @local.scope + +(package_declaration) @local.scope + +(package_body) @local.scope + +(subprogram_declaration) @local.scope + +(subprogram_body) @local.scope + +(block_statement) @local.scope + +(with_clause + (identifier) @local.definition.import) + +(procedure_specification + name: (_) @local.definition.function) + +(function_specification + name: (_) @local.definition.function) + +(package_declaration + name: (_) @local.definition.var) + +(package_body + name: (_) @local.definition.var) + +(generic_instantiation + . + name: (_) @local.definition.var) + +(component_declaration + . + (identifier) @local.definition.var) + +(exception_declaration + . + (identifier) @local.definition.var) + +(formal_object_declaration + . + (identifier) @local.definition.var) + +(object_declaration + . + (identifier) @local.definition.var) + +(parameter_specification + . + (identifier) @local.definition.var) + +(full_type_declaration + . + (identifier) @local.definition.type) + +(private_type_declaration + . + (identifier) @local.definition.type) + +(private_extension_declaration + . + (identifier) @local.definition.type) + +(incomplete_type_declaration + . + (identifier) @local.definition.type) + +(protected_type_declaration + . + (identifier) @local.definition.type) + +(formal_complete_type_declaration + . + (identifier) @local.definition.type) + +(formal_incomplete_type_declaration + . + (identifier) @local.definition.type) + +(task_type_declaration + . + (identifier) @local.definition.type) + +(subtype_declaration + . + (identifier) @local.definition.type) + +(identifier) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/agda/folds.scm b/config/neovim/store/nvim-treesitter/queries/agda/folds.scm new file mode 100644 index 00000000..5e1051f8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/agda/folds.scm @@ -0,0 +1,4 @@ +[ + (record) + (module) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/agda/highlights.scm b/config/neovim/store/nvim-treesitter/queries/agda/highlights.scm new file mode 100644 index 00000000..4626a8c1 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/agda/highlights.scm @@ -0,0 +1,87 @@ +; Constants +(integer) @number + +; Variables and Symbols +(typed_binding + (atom + (qid) @variable)) + +(untyped_binding) @variable + +(typed_binding + (expr) @type) + +(id) @function + +(bid) @function + +(function_name + (atom + (qid) @function)) + +(field_name) @function + +[ + (data_name) + (record_name) +] @constructor + +; Set +(SetN) @type.builtin + +(expr + . + (atom) @function) + +((atom) @boolean + (#any-of? @boolean "true" "false" "True" "False")) + +; Imports and Module Declarations +"import" @keyword.import + +(module_name) @module + +; Pragmas and comments +(pragma) @keyword.directive + +(comment) @comment @spell + +; Keywords +[ + "where" + "data" + "rewrite" + "postulate" + "public" + "private" + "tactic" + "Prop" + "quote" + "renaming" + "open" + "in" + "hiding" + "constructor" + "abstract" + "let" + "field" + "mutual" + "module" + "infix" + "infixl" + "infixr" +] @keyword + +"record" @keyword.type + +;(expr +; f_name: (atom) @function) +; Brackets +[ + "(" + ")" + "{" + "}" +] @punctuation.bracket + +"=" @operator diff --git a/config/neovim/store/nvim-treesitter/queries/angular/folds.scm b/config/neovim/store/nvim-treesitter/queries/angular/folds.scm new file mode 100644 index 00000000..1f2129cf --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/angular/folds.scm @@ -0,0 +1 @@ +; inherits: html diff --git a/config/neovim/store/nvim-treesitter/queries/angular/highlights.scm b/config/neovim/store/nvim-treesitter/queries/angular/highlights.scm new file mode 100644 index 00000000..09e3abfd --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/angular/highlights.scm @@ -0,0 +1,150 @@ +; inherits: html_tags + +(identifier) @variable + +(pipe_operator) @operator + +[ + (string) + (static_member_expression) +] @string + +(number) @number + +(pipe_call + name: (identifier) @function) + +(pipe_call + arguments: (pipe_arguments + (identifier) @variable.parameter)) + +(structural_directive + "*" @keyword + (identifier) @keyword) + +(attribute + (attribute_name) @variable.member + (#lua-match? @variable.member "#.*")) + +(binding_name + (identifier) @keyword) + +(event_binding + (binding_name + (identifier) @keyword)) + +(event_binding + "\"" @punctuation.delimiter) + +(property_binding + "\"" @punctuation.delimiter) + +(structural_assignment + operator: (identifier) @keyword) + +(member_expression + property: (identifier) @property) + +(call_expression + function: (identifier) @function) + +(call_expression + function: ((identifier) @function.builtin + (#eq? @function.builtin "$any"))) + +(pair + key: ((identifier) @variable.builtin + (#eq? @variable.builtin "$implicit"))) + +((control_keyword) @keyword.repeat + (#any-of? @keyword.repeat "for" "empty")) + +((control_keyword) @keyword.conditional + (#any-of? @keyword.conditional "if" "else" "switch" "case" "default")) + +((control_keyword) @keyword.coroutine + (#any-of? @keyword.coroutine "defer" "placeholder" "loading")) + +((control_keyword) @keyword.exception + (#eq? @keyword.exception "error")) + +(special_keyword) @keyword + +((identifier) @boolean + (#any-of? @boolean "true" "false")) + +((identifier) @variable.builtin + (#any-of? @variable.builtin "this" "$event")) + +((identifier) @constant.builtin + (#eq? @constant.builtin "null")) + +[ + (ternary_operator) + (conditional_operator) +] @keyword.conditional.ternary + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "@" + "} @" + (if_end_expression) + (for_end_expression) + (switch_end_expression) + (case_end_expression) + (default_end_expression) + (defer_end_expression) +] @punctuation.bracket + +(two_way_binding + [ + "[(" + ")]" + ] @punctuation.bracket) + +[ + "{{" + "}}" +] @punctuation.special + +[ + ";" + "." + "," + "?." +] @punctuation.delimiter + +(nullish_coalescing_expression + (coalescing_operator) @operator) + +(concatenation_expression + "+" @operator) + +(icu_clause) @keyword.operator + +(icu_category) @keyword.conditional + +(binary_expression + [ + "-" + "&&" + "+" + "<" + "<=" + "=" + "==" + "===" + "!=" + "!==" + ">" + ">=" + "*" + "/" + "||" + "%" + ] @operator) diff --git a/config/neovim/store/nvim-treesitter/queries/angular/indents.scm b/config/neovim/store/nvim-treesitter/queries/angular/indents.scm new file mode 100644 index 00000000..448e9427 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/angular/indents.scm @@ -0,0 +1 @@ +; inherits: html_tags diff --git a/config/neovim/store/nvim-treesitter/queries/angular/injections.scm b/config/neovim/store/nvim-treesitter/queries/angular/injections.scm new file mode 100644 index 00000000..448e9427 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/angular/injections.scm @@ -0,0 +1 @@ +; inherits: html_tags diff --git a/config/neovim/store/nvim-treesitter/queries/angular/locals.scm b/config/neovim/store/nvim-treesitter/queries/angular/locals.scm new file mode 100644 index 00000000..1f2129cf --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/angular/locals.scm @@ -0,0 +1 @@ +; inherits: html diff --git a/config/neovim/store/nvim-treesitter/queries/apex/folds.scm b/config/neovim/store/nvim-treesitter/queries/apex/folds.scm new file mode 100644 index 00000000..fdfc2a1e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/apex/folds.scm @@ -0,0 +1,6 @@ +[ + (class_body) + (constructor_declaration) + (argument_list) + (annotation_argument_list) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/apex/highlights.scm b/config/neovim/store/nvim-treesitter/queries/apex/highlights.scm new file mode 100644 index 00000000..85453b22 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/apex/highlights.scm @@ -0,0 +1,260 @@ +; inherits: soql + +; Apex + SOQL +[ + "[" + "]" + "{" + "}" + "(" + ")" +] @punctuation.bracket + +[ + "," + "." + ":" + "?" + ";" +] @punctuation.delimiter + +; Default general color definition +(identifier) @variable + +(type_identifier) @type + +; Methods +(method_declaration + name: (identifier) @function.method) + +(method_invocation + name: (identifier) @function.method.call) + +(super) @function.builtin + +; Annotations +(annotation + name: (identifier) @attribute) + +; Types +(interface_declaration + name: (identifier) @type) + +(class_declaration + name: (identifier) @type) + +(class_declaration + (superclass) @type) + +(enum_declaration + name: (identifier) @type) + +(enum_constant + name: (identifier) @constant) + +(type_arguments + "<" @punctuation.delimiter) + +(type_arguments + ">" @punctuation.delimiter) + +(field_access + object: (identifier) @type) + +(field_access + field: (identifier) @property) + +((scoped_identifier + scope: (identifier) @type) + (#match? @type "^[A-Z]")) + +((method_invocation + object: (identifier) @type) + (#match? @type "^[A-Z]")) + +(method_declaration + (formal_parameters + (formal_parameter + name: (identifier) @variable.parameter))) + +(constructor_declaration + name: (identifier) @constructor) + +(dml_type) @function.builtin + +(assignment_operator) @operator + +(update_expression + [ + "++" + "--" + ] @operator) + +(trigger_declaration + name: (identifier) @type + object: (identifier) @type + (trigger_event) @keyword + ("," + (trigger_event) @keyword)*) + +[ + "@" + "=" + "!=" + "<=" + ">=" +] @operator + +(binary_expression + operator: [ + ">" + "<" + "==" + "===" + "!==" + "&&" + "||" + "+" + "-" + "*" + "/" + "&" + "|" + "^" + "%" + "<<" + ">>" + ">>>" + ] @operator) + +(unary_expression + operator: [ + "+" + "-" + "!" + "~" + ]) @operator + +(map_initializer + "=>" @operator) + +[ + (boolean_type) + (void_type) +] @type.builtin + +; Fields +(field_declaration + declarator: (variable_declarator + name: (identifier) @variable.member)) + +(field_access + field: (identifier) @variable.member) + +; Variables +(field_declaration + (modifiers + (modifier + [ + "final" + "static" + ]) + (modifier + [ + "final" + "static" + ])) + (variable_declarator + name: (identifier) @constant)) + +(variable_declarator + (identifier) @property) + +((identifier) @constant + (#lua-match? @constant "^[A-Z][A-Z0-9_]+$")) ; SCREAM SNAKE CASE + +(this) @variable.builtin + +; Literals +[ + (int) + (decimal) + (currency_literal) +] @number + +(string_literal) @string + +[ + (line_comment) + (block_comment) +] @comment + +(null_literal) @constant.builtin + +; ;; Keywords +[ + "abstract" + "final" + "private" + "protected" + "public" + "static" +] @keyword.modifier + +[ + "if" + "else" + "switch" +] @keyword.conditional + +[ + "for" + "while" + "do" + "break" +] @keyword.repeat + +"return" @keyword.return + +[ + "throw" + "finally" + "try" + "catch" +] @keyword.exception + +"new" @keyword.operator + +[ + "abstract" + "continue" + "default" + "extends" + "final" + "get" + "global" + "implements" + "instanceof" + "on" + "private" + "protected" + "public" + "set" + "static" + "testMethod" + "transient" + "trigger" + "virtual" + "when" + "with_sharing" + "without_sharing" + "inherited_sharing" +] @keyword + +[ + "interface" + "class" + "enum" +] @keyword.type + +"System.runAs" @function.builtin diff --git a/config/neovim/store/nvim-treesitter/queries/apex/locals.scm b/config/neovim/store/nvim-treesitter/queries/apex/locals.scm new file mode 100644 index 00000000..2457752f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/apex/locals.scm @@ -0,0 +1,70 @@ +; declarations +(class_declaration) @local.scope + +(method_declaration) @local.scope + +(constructor_declaration) @local.scope + +(enum_declaration) @local.scope + +(enhanced_for_statement) @local.scope + +; if/else +(if_statement) @local.scope + +(if_statement + consequence: (_) @local.scope) ; if body in case there are no braces + +(if_statement + alternative: (_) @local.scope) ; else body in case there are no braces + +; try/catch +(try_statement) @local.scope ; covers try+catch, individual try and catch are covered by (block) + +(catch_clause) @local.scope ; needed because `Exception` variable + +; loops +(for_statement) @local.scope + +(for_statement ; "for" body in case there are no braces + body: (_) @local.scope) + +(do_statement + body: (_) @local.scope) + +(while_statement + body: (_) @local.scope) + +; Functions +(constructor_declaration) @local.scope + +(method_declaration) @local.scope + +; definitions +(enum_declaration + name: (identifier) @local.definition.enum) + +(method_declaration + name: (identifier) @local.definition.method) + +(local_variable_declaration + declarator: (variable_declarator + name: (identifier) @local.definition.var)) + +(enhanced_for_statement + name: (identifier) @local.definition.var) + +(formal_parameter + name: (identifier) @local.definition.parameter) + +(catch_formal_parameter + name: (identifier) @local.definition.parameter) + +(field_declaration + declarator: (variable_declarator + name: (identifier) @local.definition.field)) + +; REFERENCES +(identifier) @local.reference + +(type_identifier) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/arduino/folds.scm b/config/neovim/store/nvim-treesitter/queries/arduino/folds.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/arduino/folds.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/nvim-treesitter/queries/arduino/highlights.scm b/config/neovim/store/nvim-treesitter/queries/arduino/highlights.scm new file mode 100644 index 00000000..e6bf1478 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/arduino/highlights.scm @@ -0,0 +1,53 @@ +; inherits: cpp + +((identifier) @function.builtin + (#any-of? @function.builtin + ; Digital I/O + "digitalRead" "digitalWrite" "pinMode" + ; Analog I/O + "analogRead" "analogReference" "analogWrite" + ; Zero, Due & MKR Family + "analogReadResolution" "analogWriteResolution" + ; Advanced I/O + "noTone" "pulseIn" "pulseInLong" "shiftIn" "shiftOut" "tone" + ; Time + "delay" "delayMicroseconds" "micros" "millis" + ; Math + "abs" "constrain" "map" "max" "min" "pow" "sq" "sqrt" + ; Trigonometry + "cos" "sin" "tan" + ; Characters + "isAlpha" "isAlphaNumeric" "isAscii" "isControl" "isDigit" "isGraph" "isHexadecimalDigit" + "isLowerCase" "isPrintable" "isPunct" "isSpace" "isUpperCase" "isWhitespace" + ; Random Numbers + "random" "randomSeed" + ; Bits and Bytes + "bit" "bitClear" "bitRead" "bitSet" "bitWrite" "highByte" "lowByte" + ; External Interrupts + "attachInterrupt" "detachInterrupt" + ; Interrupts + "interrupts" "noInterrupts")) + +((identifier) @type.builtin + (#any-of? @type.builtin "Serial" "SPI" "Stream" "Wire" "Keyboard" "Mouse" "String")) + +((identifier) @constant.builtin + (#any-of? @constant.builtin "HIGH" "LOW" "INPUT" "OUTPUT" "INPUT_PULLUP" "LED_BUILTIN")) + +(function_definition + (function_declarator + declarator: (identifier) @function.builtin) + (#any-of? @function.builtin "loop" "setup")) + +(call_expression + function: (primitive_type) @function.builtin) + +(call_expression + function: (identifier) @constructor + (#any-of? @constructor "SPISettings" "String")) + +(declaration + (type_identifier) @type.builtin + (function_declarator + declarator: (identifier) @constructor) + (#eq? @type.builtin "SPISettings")) diff --git a/config/neovim/store/nvim-treesitter/queries/arduino/indents.scm b/config/neovim/store/nvim-treesitter/queries/arduino/indents.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/arduino/indents.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/nvim-treesitter/queries/arduino/injections.scm b/config/neovim/store/nvim-treesitter/queries/arduino/injections.scm new file mode 100644 index 00000000..b637d9b2 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/arduino/injections.scm @@ -0,0 +1,5 @@ +((preproc_arg) @injection.content + (#set! injection.language "arduino")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/arduino/locals.scm b/config/neovim/store/nvim-treesitter/queries/arduino/locals.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/arduino/locals.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/nvim-treesitter/queries/asm/highlights.scm b/config/neovim/store/nvim-treesitter/queries/asm/highlights.scm new file mode 100644 index 00000000..eccf9c99 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/asm/highlights.scm @@ -0,0 +1,66 @@ +; General +(label + [ + (ident) + (word) + ] @label) + +(reg) @variable.builtin + +(meta + kind: (_) @function.builtin) + +(instruction + kind: (_) @function.builtin) + +(const + name: (word) @constant) + +; Comments +[ + (line_comment) + (block_comment) +] @comment @spell + +; Literals +(int) @number + +(float) @number.float + +(string) @string + +; Keywords +[ + "byte" + "word" + "dword" + "qword" + "ptr" + "rel" + "label" + "const" +] @keyword + +; Operators & Punctuation +[ + "+" + "-" + "*" + "/" + "%" + "|" + "^" + "&" +] @operator + +[ + "(" + ")" + "[" + "]" +] @punctuation.bracket + +[ + "," + ":" +] @punctuation.delimiter diff --git a/config/neovim/store/nvim-treesitter/queries/asm/injections.scm b/config/neovim/store/nvim-treesitter/queries/asm/injections.scm new file mode 100644 index 00000000..3cd6aac8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/asm/injections.scm @@ -0,0 +1,5 @@ +([ + (line_comment) + (block_comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/astro/folds.scm b/config/neovim/store/nvim-treesitter/queries/astro/folds.scm new file mode 100644 index 00000000..1f2129cf --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/astro/folds.scm @@ -0,0 +1 @@ +; inherits: html diff --git a/config/neovim/store/nvim-treesitter/queries/astro/highlights.scm b/config/neovim/store/nvim-treesitter/queries/astro/highlights.scm new file mode 100644 index 00000000..bdaf88e3 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/astro/highlights.scm @@ -0,0 +1,21 @@ +; inherits: html + +"---" @punctuation.delimiter + +[ + "{" + "}" +] @punctuation.special + +; custom components get `@type` highlighting +((start_tag + (tag_name) @type) + (#lua-match? @type "^[A-Z]")) + +((end_tag + (tag_name) @type) + (#lua-match? @type "^[A-Z]")) + +((erroneous_end_tag + (erroneous_end_tag_name) @type) + (#lua-match? @type "^[A-Z]")) diff --git a/config/neovim/store/nvim-treesitter/queries/astro/indents.scm b/config/neovim/store/nvim-treesitter/queries/astro/indents.scm new file mode 100644 index 00000000..1f2129cf --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/astro/indents.scm @@ -0,0 +1 @@ +; inherits: html diff --git a/config/neovim/store/nvim-treesitter/queries/astro/injections.scm b/config/neovim/store/nvim-treesitter/queries/astro/injections.scm new file mode 100644 index 00000000..d4f15ba5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/astro/injections.scm @@ -0,0 +1,32 @@ +; inherits: html_tags + +(frontmatter + (frontmatter_js_block) @injection.content + (#set! injection.language "typescript")) + +(attribute_interpolation + (attribute_js_expr) @injection.content + (#set! injection.language "typescript")) + +(attribute + (attribute_backtick_string) @injection.content + (#set! injection.language "typescript")) + +(html_interpolation + (permissible_text) @injection.content + (#set! injection.language "typescript")) + +(script_element + (raw_text) @injection.content + (#set! injection.language "typescript")) + +(style_element + (start_tag + (attribute + (attribute_name) @_lang_attr + (quoted_attribute_value + (attribute_value) @_lang_value))) + (raw_text) @injection.content + (#eq? @_lang_attr "lang") + (#eq? @_lang_value "scss") + (#set! injection.language "scss")) diff --git a/config/neovim/store/nvim-treesitter/queries/astro/locals.scm b/config/neovim/store/nvim-treesitter/queries/astro/locals.scm new file mode 100644 index 00000000..1f2129cf --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/astro/locals.scm @@ -0,0 +1 @@ +; inherits: html diff --git a/config/neovim/store/nvim-treesitter/queries/authzed/highlights.scm b/config/neovim/store/nvim-treesitter/queries/authzed/highlights.scm new file mode 100644 index 00000000..fb946caa --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/authzed/highlights.scm @@ -0,0 +1,60 @@ +(identifier) @function + +(block + (relation + (relation_literal) @function.builtin + (identifier) @constant)) + +(block + (permission + (permission_literal) @variable.builtin + (identifier) @type)) + +; relations +(rel_expression + (identifier) @property) + +(relation + (rel_expression + (hash_literal) + . + (identifier) @constant)) + +; permissions +(perm_expression + (identifier) @property) + +(call_expression + function: (selector_expression + operand: (identifier) @constant + field: (field_identifier) @function.method)) + +(perm_expression + (stabby) @operator + . + (identifier) @function) + +; misc +[ + (plus_literal) + (minus_literal) + (amp_literal) + (pipe_literal) +] @operator + +[ + (true) + (false) +] @boolean + +(nil) @constant.builtin + +[ + (caveat_literal) + (definition_literal) +] @keyword + +[ + (hash_literal) + (comment) +] @comment diff --git a/config/neovim/store/nvim-treesitter/queries/authzed/injections.scm b/config/neovim/store/nvim-treesitter/queries/authzed/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/authzed/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/awk/highlights.scm b/config/neovim/store/nvim-treesitter/queries/awk/highlights.scm new file mode 100644 index 00000000..904cf2a9 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/awk/highlights.scm @@ -0,0 +1,199 @@ +; adapted from https://github.com/Beaglefoot/tree-sitter-awk +[ + (identifier) + (field_ref) +] @variable + +(field_ref + (_) @variable) + +; https://www.gnu.org/software/gawk/manual/html_node/Auto_002dset.html +((identifier) @constant.builtin + (#any-of? @constant.builtin + "ARGC" "ARGV" "ARGIND" "ENVIRON" "ERRNO" "FILENAME" "FNR" "NF" "FUNCTAB" "NR" "PROCINFO" + "RLENGTH" "RSTART" "RT" "SYMTAB")) + +; https://www.gnu.org/software/gawk/manual/html_node/User_002dmodified.html +((identifier) @variable.builtin + (#any-of? @variable.builtin + "BINMODE" "CONVFMT" "FIELDWIDTHS" "FPAT" "FS" "IGNORECASE" "LINT" "OFMT" "OFS" "ORS" "PREC" + "ROUNDMODE" "RS" "SUBSEP" "TEXTDOMAIN")) + +(number) @number + +(string) @string + +(regex) @string.regexp + +(escape_sequence) @string.escape + +(comment) @comment @spell + +((program + . + (comment) @keyword.directive @nospell) + (#lua-match? @keyword.directive "^#!/")) + +(ns_qualified_name + (namespace) @module) + +(ns_qualified_name + "::" @punctuation.delimiter) + +(func_def + name: (_ + (identifier) @function) @function) + +(func_call + name: (_ + (identifier) @function) @function) + +(func_def + (param_list + (identifier) @variable.parameter)) + +[ + "print" + "printf" + "getline" +] @function.builtin + +[ + (delete_statement) + (break_statement) + (continue_statement) + (next_statement) + (nextfile_statement) +] @keyword + +[ + "func" + "function" +] @keyword.function + +[ + "return" + "exit" +] @keyword.return + +[ + "do" + "while" + "for" + "in" +] @keyword.repeat + +[ + "if" + "else" + "switch" + "case" + "default" +] @keyword.conditional + +[ + "@include" + "@load" +] @keyword.import + +"@namespace" @keyword.directive + +[ + "BEGIN" + "END" + "BEGINFILE" + "ENDFILE" +] @label + +(binary_exp + [ + "^" + "**" + "*" + "/" + "%" + "+" + "-" + "<" + ">" + "<=" + ">=" + "==" + "!=" + "~" + "!~" + "in" + "&&" + "||" + ] @operator) + +(unary_exp + [ + "!" + "+" + "-" + ] @operator) + +(assignment_exp + [ + "=" + "+=" + "-=" + "*=" + "/=" + "%=" + "^=" + ] @operator) + +(ternary_exp + [ + "?" + ":" + ] @keyword.conditional.ternary) + +(update_exp + [ + "++" + "--" + ] @operator) + +(redirected_io_statement + [ + ">" + ">>" + ] @operator) + +(piped_io_statement + [ + "|" + "|&" + ] @operator) + +(piped_io_exp + [ + "|" + "|&" + ] @operator) + +(field_ref + "$" @punctuation.delimiter) + +(regex + "/" @punctuation.delimiter) + +(regex_constant + "@" @punctuation.delimiter) + +[ + ";" + "," +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket diff --git a/config/neovim/store/nvim-treesitter/queries/awk/injections.scm b/config/neovim/store/nvim-treesitter/queries/awk/injections.scm new file mode 100644 index 00000000..3e67da24 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/awk/injections.scm @@ -0,0 +1,17 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((regex) @injection.content + (#set! injection.language "regex")) + +((print_statement + (exp_list + . + (string) @injection.content)) + (#set! injection.language "printf")) + +((printf_statement + (exp_list + . + (string) @injection.content)) + (#set! injection.language "printf")) diff --git a/config/neovim/store/nvim-treesitter/queries/bash/folds.scm b/config/neovim/store/nvim-treesitter/queries/bash/folds.scm new file mode 100644 index 00000000..766dbe59 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bash/folds.scm @@ -0,0 +1,9 @@ +[ + (function_definition) + (if_statement) + (case_statement) + (for_statement) + (while_statement) + (c_style_for_statement) + (heredoc_redirect) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/bash/highlights.scm b/config/neovim/store/nvim-treesitter/queries/bash/highlights.scm new file mode 100644 index 00000000..0e192bbc --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bash/highlights.scm @@ -0,0 +1,239 @@ +[ + "(" + ")" + "{" + "}" + "[" + "]" + "[[" + "]]" + "((" + "))" +] @punctuation.bracket + +[ + ";" + ";;" + ";&" + ";;&" + "&" +] @punctuation.delimiter + +[ + ">" + ">>" + "<" + "<<" + "&&" + "|" + "|&" + "||" + "=" + "+=" + "=~" + "==" + "!=" + "&>" + "&>>" + "<&" + ">&" + ">|" + "<&-" + ">&-" + "<<-" + "<<<" + ".." + "!" +] @operator + +; Do *not* spell check strings since they typically have some sort of +; interpolation in them, or, are typically used for things like filenames, URLs, +; flags and file content. +[ + (string) + (raw_string) + (ansi_c_string) + (heredoc_body) +] @string + +[ + (heredoc_start) + (heredoc_end) +] @label + +(variable_assignment + (word) @string) + +(command + argument: "$" @string) ; bare dollar + +(concatenation + (word) @string) + +[ + "if" + "then" + "else" + "elif" + "fi" + "case" + "in" + "esac" +] @keyword.conditional + +[ + "for" + "do" + "done" + "select" + "until" + "while" +] @keyword.repeat + +[ + "declare" + "typeset" + "readonly" + "local" + "unset" + "unsetenv" +] @keyword + +"export" @keyword.import + +"function" @keyword.function + +(special_variable_name) @constant + +; trap -l +((word) @constant.builtin + (#any-of? @constant.builtin + "SIGHUP" "SIGINT" "SIGQUIT" "SIGILL" "SIGTRAP" "SIGABRT" "SIGBUS" "SIGFPE" "SIGKILL" "SIGUSR1" + "SIGSEGV" "SIGUSR2" "SIGPIPE" "SIGALRM" "SIGTERM" "SIGSTKFLT" "SIGCHLD" "SIGCONT" "SIGSTOP" + "SIGTSTP" "SIGTTIN" "SIGTTOU" "SIGURG" "SIGXCPU" "SIGXFSZ" "SIGVTALRM" "SIGPROF" "SIGWINCH" + "SIGIO" "SIGPWR" "SIGSYS" "SIGRTMIN" "SIGRTMIN+1" "SIGRTMIN+2" "SIGRTMIN+3" "SIGRTMIN+4" + "SIGRTMIN+5" "SIGRTMIN+6" "SIGRTMIN+7" "SIGRTMIN+8" "SIGRTMIN+9" "SIGRTMIN+10" "SIGRTMIN+11" + "SIGRTMIN+12" "SIGRTMIN+13" "SIGRTMIN+14" "SIGRTMIN+15" "SIGRTMAX-14" "SIGRTMAX-13" + "SIGRTMAX-12" "SIGRTMAX-11" "SIGRTMAX-10" "SIGRTMAX-9" "SIGRTMAX-8" "SIGRTMAX-7" "SIGRTMAX-6" + "SIGRTMAX-5" "SIGRTMAX-4" "SIGRTMAX-3" "SIGRTMAX-2" "SIGRTMAX-1" "SIGRTMAX")) + +((word) @boolean + (#any-of? @boolean "true" "false")) + +(comment) @comment @spell + +(test_operator) @operator + +(command_substitution + "$(" @punctuation.special + ")" @punctuation.special) + +(process_substitution + [ + "<(" + ">(" + ] @punctuation.special + ")" @punctuation.special) + +(arithmetic_expansion + [ + "$((" + "((" + ] @punctuation.special + "))" @punctuation.special) + +(arithmetic_expansion + "," @punctuation.delimiter) + +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +(binary_expression + operator: _ @operator) + +(unary_expression + operator: _ @operator) + +(postfix_expression + operator: _ @operator) + +(function_definition + name: (word) @function) + +(command_name + (word) @function.call) + +(command_name + (word) @function.builtin + (#any-of? @function.builtin + "alias" "bg" "bind" "break" "builtin" "caller" "cd" "command" "compgen" "complete" "compopt" + "continue" "coproc" "dirs" "disown" "echo" "enable" "eval" "exec" "exit" "fc" "fg" "getopts" + "hash" "help" "history" "jobs" "kill" "let" "logout" "mapfile" "popd" "printf" "pushd" "pwd" + "read" "readarray" "return" "set" "shift" "shopt" "source" "suspend" "test" "time" "times" + "trap" "type" "typeset" "ulimit" "umask" "unalias" "wait")) + +(command + argument: [ + (word) @variable.parameter + (concatenation + (word) @variable.parameter) + ]) + +(declaration_command + (word) @variable.parameter) + +(unset_command + (word) @variable.parameter) + +(number) @number + +((word) @number + (#lua-match? @number "^[0-9]+$")) + +(file_redirect + destination: (word) @variable.parameter) + +(file_descriptor) @operator + +(simple_expansion + "$" @punctuation.special) @none + +(expansion + "${" @punctuation.special + "}" @punctuation.special) @none + +(expansion + operator: _ @punctuation.special) + +(expansion + "@" + . + operator: _ @character.special) + +((expansion + (subscript + index: (word) @character.special)) + (#any-of? @character.special "@" "*")) + +"``" @punctuation.special + +(variable_name) @variable + +((variable_name) @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) + +(case_item + value: (word) @variable.parameter) + +[ + (regex) + (extglob_pattern) +] @string.regexp + +((program + . + (comment) @keyword.directive @nospell) + (#lua-match? @keyword.directive "^#!/")) diff --git a/config/neovim/store/nvim-treesitter/queries/bash/injections.scm b/config/neovim/store/nvim-treesitter/queries/bash/injections.scm new file mode 100644 index 00000000..2f385bfd --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bash/injections.scm @@ -0,0 +1,67 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((regex) @injection.content + (#set! injection.language "regex")) + +((heredoc_redirect + (heredoc_body) @injection.content + (heredoc_end) @injection.language) + (#downcase! @injection.language)) + +; printf 'format' +((command + name: (command_name) @_command + . + argument: [ + (string + (string_content) @injection.content) + (concatenation + (string + (string_content) @injection.content)) + (raw_string) @injection.content + (concatenation + (raw_string) @injection.content) + ]) + (#eq? @_command "printf") + (#set! injection.language "printf")) + +; printf -v var 'format' +((command + name: (command_name) @_command + argument: (word) @_arg + . + (_) + . + argument: [ + (string + (string_content) @injection.content) + (concatenation + (string + (string_content) @injection.content)) + (raw_string) @injection.content + (concatenation + (raw_string) @injection.content) + ]) + (#eq? @_command "printf") + (#eq? @_arg "-v") + (#set! injection.language "printf")) + +; printf -- 'format' +((command + name: (command_name) @_command + argument: (word) @_arg + . + argument: [ + (string + (string_content) @injection.content) + (concatenation + (string + (string_content) @injection.content)) + (raw_string) @injection.content + (concatenation + (raw_string) @injection.content) + ]) + (#eq? @_command "printf") + (#eq? @_arg "--") + (#set! injection.language "printf")) diff --git a/config/neovim/store/nvim-treesitter/queries/bash/locals.scm b/config/neovim/store/nvim-treesitter/queries/bash/locals.scm new file mode 100644 index 00000000..347f51fa --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bash/locals.scm @@ -0,0 +1,14 @@ +; Scopes +(function_definition) @local.scope + +; Definitions +(variable_assignment + name: (variable_name) @local.definition.var) + +(function_definition + name: (word) @local.definition.function) + +; References +(variable_name) @local.reference + +(word) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/bass/folds.scm b/config/neovim/store/nvim-treesitter/queries/bass/folds.scm new file mode 100644 index 00000000..d99e0c1a --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bass/folds.scm @@ -0,0 +1,5 @@ +[ + (list) + (scope) + (cons) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/bass/highlights.scm b/config/neovim/store/nvim-treesitter/queries/bass/highlights.scm new file mode 100644 index 00000000..f84993af --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bass/highlights.scm @@ -0,0 +1,126 @@ +; Variables +(list + (symbol) @variable) + +(cons + (symbol) @variable) + +(scope + (symbol) @variable) + +(symbind + (symbol) @variable) + +; Constants +((symbol) @constant + (#lua-match? @constant "^_*[A-Z][A-Z0-9_]*$")) + +; Functions +(list + . + (symbol) @function) + +; Namespaces +(symbind + (symbol) @module + . + (keyword)) + +; Includes +((symbol) @keyword.import + (#any-of? @keyword.import "use" "import" "load")) + +; Keywords +((symbol) @keyword + (#any-of? @keyword "do" "doc")) + +; Special Functions +; Keywords construct a symbol +(keyword) @constructor + +((list + . + (symbol) @keyword.function + . + (symbol) @function + (symbol)? @variable.parameter) + (#any-of? @keyword.function "def" "defop" "defn" "fn")) + +((cons + . + (symbol) @keyword.function + . + (symbol) @function + (symbol)? @variable.parameter) + (#any-of? @keyword.function "def" "defop" "defn" "fn")) + +((symbol) @function.builtin + (#any-of? @function.builtin + "dump" "mkfs" "json" "log" "error" "now" "cons" "wrap" "unwrap" "eval" "make-scope" "bind" + "meta" "with-meta" "null?" "ignore?" "boolean?" "number?" "string?" "symbol?" "scope?" "sink?" + "source?" "list?" "pair?" "applicative?" "operative?" "combiner?" "path?" "empty?" "thunk?" "+" + "*" "quot" "-" "max" "min" "=" ">" ">=" "<" "<=" "list->source" "across" "emit" "next" + "reduce-kv" "assoc" "symbol->string" "string->symbol" "str" "substring" "trim" "scope->list" + "string->fs-path" "string->cmd-path" "string->dir" "subpath" "path-name" "path-stem" + "with-image" "with-dir" "with-args" "with-cmd" "with-stdin" "with-env" "with-insecure" + "with-label" "with-port" "with-tls" "with-mount" "thunk-cmd" "thunk-args" "resolve" "start" + "addr" "wait" "read" "cache-dir" "binds?" "recall-memo" "store-memo" "mask" "list" "list*" + "first" "rest" "length" "second" "third" "map" "map-pairs" "foldr" "foldl" "append" "filter" + "conj" "list->scope" "merge" "apply" "id" "always" "vals" "keys" "memo" "succeeds?" "run" "last" + "take" "take-all" "insecure!" "from" "cd" "wrap-cmd" "mkfile" "path-base" "not")) + +((symbol) @function.macro + (#any-of? @function.macro + "op" "current-scope" "quote" "let" "provide" "module" "or" "and" "curryfn" "for" "$" "linux")) + +; Conditionals +((symbol) @keyword.conditional + (#any-of? @keyword.conditional "if" "case" "cond" "when")) + +; Repeats +((symbol) @keyword.repeat + (#any-of? @keyword.repeat "each")) + +; Operators +((symbol) @operator + (#any-of? @operator "&" "*" "+" "-" "<" "<=" "=" ">" ">=")) + +; Punctuation +[ + "(" + ")" +] @punctuation.bracket + +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +((symbol) @punctuation.delimiter + (#eq? @punctuation.delimiter "->")) + +; Literals +(string) @string + +(escape_sequence) @string.escape + +(path) @string.special.url + +(number) @number + +(boolean) @boolean + +[ + (ignore) + (null) +] @constant.builtin + +"^" @character.special + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/bass/indents.scm b/config/neovim/store/nvim-treesitter/queries/bass/indents.scm new file mode 100644 index 00000000..27b976f2 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bass/indents.scm @@ -0,0 +1,31 @@ +[ + (list) + (scope) + (cons) +] @indent.begin + +[ + ")" + "}" + "]" +] @indent.end + +[ + "(" + ")" +] @indent.branch + +[ + "{" + "}" +] @indent.branch + +[ + "[" + "]" +] @indent.branch + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/bass/injections.scm b/config/neovim/store/nvim-treesitter/queries/bass/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bass/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/bass/locals.scm b/config/neovim/store/nvim-treesitter/queries/bass/locals.scm new file mode 100644 index 00000000..daed7e5e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bass/locals.scm @@ -0,0 +1,26 @@ +; Scopes +[ + (list) + (scope) + (cons) +] @local.scope + +; References +(symbol) @local.reference + +; Definitions +((list + . + (symbol) @_fnkw + . + (symbol) @local.definition.function + (symbol)? @local.definition.parameter) + (#any-of? @_fnkw "def" "defop" "defn" "fn")) + +((cons + . + (symbol) @_fnkw + . + (symbol) @local.definition.function + (symbol)? @local.definition.parameter) + (#any-of? @_fnkw "def" "defop" "defn" "fn")) diff --git a/config/neovim/store/nvim-treesitter/queries/beancount/folds.scm b/config/neovim/store/nvim-treesitter/queries/beancount/folds.scm new file mode 100644 index 00000000..9f1b6cbe --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/beancount/folds.scm @@ -0,0 +1,4 @@ +[ + (transaction) + (section) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/beancount/highlights.scm b/config/neovim/store/nvim-treesitter/queries/beancount/highlights.scm new file mode 100644 index 00000000..1e23d28f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/beancount/highlights.scm @@ -0,0 +1,57 @@ +(date) @variable.member + +(txn) @attribute + +(account) @type + +(amount) @number + +(incomplete_amount) @number + +(compound_amount) @number + +(amount_tolerance) @number + +(currency) @property + +(key) @label + +(string) @string + +(narration) @string @spell + +(payee) @string @spell + +(tag) @constant + +(link) @constant + +[ + (minus) + (plus) + (slash) + (asterisk) +] @operator + +(comment) @comment @spell + +[ + (balance) + (open) + (close) + (commodity) + (pad) + (event) + (price) + (note) + (document) + (query) + (custom) + (pushtag) + (poptag) + (pushmeta) + (popmeta) + (option) + (include) + (plugin) +] @keyword diff --git a/config/neovim/store/nvim-treesitter/queries/beancount/injections.scm b/config/neovim/store/nvim-treesitter/queries/beancount/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/beancount/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/bibtex/folds.scm b/config/neovim/store/nvim-treesitter/queries/bibtex/folds.scm new file mode 100644 index 00000000..321a045c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bibtex/folds.scm @@ -0,0 +1 @@ +(entry) @fold diff --git a/config/neovim/store/nvim-treesitter/queries/bibtex/highlights.scm b/config/neovim/store/nvim-treesitter/queries/bibtex/highlights.scm new file mode 100644 index 00000000..a82b371a --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bibtex/highlights.scm @@ -0,0 +1,50 @@ +; CREDITS @pfoerster (adapted from https://github.com/latex-lsp/tree-sitter-bibtex) +[ + (string_type) + (preamble_type) + (entry_type) +] @keyword + +[ + (junk) + (comment) +] @comment + +(comment) @spell + +[ + "=" + "#" +] @operator + +(command) @function.builtin + +(number) @number + +(field + name: (identifier) @variable.member) + +(token + (identifier) @variable.parameter) + +[ + (brace_word) + (quote_word) +] @string + +[ + (key_brace) + (key_paren) +] @string.special.symbol + +(string + name: (identifier) @constant) + +[ + "{" + "}" + "(" + ")" +] @punctuation.bracket + +"," @punctuation.delimiter diff --git a/config/neovim/store/nvim-treesitter/queries/bibtex/indents.scm b/config/neovim/store/nvim-treesitter/queries/bibtex/indents.scm new file mode 100644 index 00000000..764172a7 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bibtex/indents.scm @@ -0,0 +1,8 @@ +(entry) @indent.begin + +[ + "{" + "}" +] @indent.branch + +(comment) @indent.ignore diff --git a/config/neovim/store/nvim-treesitter/queries/bicep/folds.scm b/config/neovim/store/nvim-treesitter/queries/bicep/folds.scm new file mode 100644 index 00000000..217a86d6 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bicep/folds.scm @@ -0,0 +1,19 @@ +[ + (module_declaration) + (metadata_declaration) + (output_declaration) + (parameter_declaration) + (resource_declaration) + (type_declaration) + (variable_declaration) + (parenthesized_expression) + (decorators) + (array) + (object) + (if_statement) + (for_statement) + (subscript_expression) + (ternary_expression) + (string) + (comment) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/bicep/highlights.scm b/config/neovim/store/nvim-treesitter/queries/bicep/highlights.scm new file mode 100644 index 00000000..35c9d6c1 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bicep/highlights.scm @@ -0,0 +1,234 @@ +; Includes +[ + "import" + "provider" + "with" + "as" + "from" +] @keyword.import + +; Namespaces +(module_declaration + (identifier) @module) + +; Builtins +(primitive_type) @type.builtin + +((member_expression + object: (identifier) @type.builtin) + (#eq? @type.builtin "sys")) + +; Functions +(call_expression + function: (identifier) @function.call) + +(user_defined_function + name: (identifier) @function) + +; Properties +(object_property + (identifier) @property + ":" @punctuation.delimiter + (_)) + +(object_property + (compatible_identifier) @property + ":" @punctuation.delimiter + (_)) + +(property_identifier) @property + +; Attributes +(decorator + "@" @attribute) + +(decorator + (call_expression + (identifier) @attribute)) + +(decorator + (call_expression + (member_expression + object: (identifier) @attribute + property: (property_identifier) @attribute))) + +; Types +(type_declaration + (identifier) @type) + +(type_declaration + (identifier) + "=" + (identifier) @type) + +(type + (identifier) @type) + +(resource_declaration + (identifier) @type) + +(resource_expression + (identifier) @type) + +; Parameters +(parameter_declaration + (identifier) @variable.parameter + (_)) + +(call_expression + function: (_) + (arguments + (identifier) @variable.parameter)) + +(call_expression + function: (_) + (arguments + (member_expression + object: (identifier) @variable.parameter))) + +(parameter + . + (identifier) @variable.parameter) + +; Variables +(variable_declaration + (identifier) @variable + (_)) + +(metadata_declaration + (identifier) @variable + (_)) + +(output_declaration + (identifier) @variable + (_)) + +(object_property + (_) + ":" + (identifier) @variable) + +(for_statement + "for" + (for_loop_parameters + (loop_variable) @variable + (loop_enumerator) @variable)) + +; Conditionals +"if" @keyword.conditional + +(ternary_expression + "?" @keyword.conditional.ternary + ":" @keyword.conditional.ternary) + +; Loops +(for_statement + "for" @keyword.repeat + "in" + ":" @punctuation.delimiter) + +; Keywords +[ + "module" + "metadata" + "output" + "param" + "resource" + "existing" + "targetScope" + "type" + "var" + "using" + "test" +] @keyword + +"func" @keyword.function + +"assert" @keyword.exception + +; Operators +[ + "+" + "-" + "*" + "/" + "%" + "||" + "&&" + "|" + "==" + "!=" + "=~" + "!~" + ">" + ">=" + "<=" + "<" + "??" + "=" + "!" + ".?" +] @operator + +(subscript_expression + "?" @operator) + +(nullable_type + "?" @operator) + +"in" @keyword.operator + +; Literals +(string) @string + +(escape_sequence) @string.escape + +(number) @number + +(boolean) @boolean + +(null) @constant.builtin + +; Misc +(compatible_identifier + "?" @punctuation.special) + +(nullable_return_type) @punctuation.special + +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "." + ":" + "::" + "=>" +] @punctuation.delimiter + +; Interpolation +(interpolation) @none + +(interpolation + "${" @punctuation.special + "}" @punctuation.special) + +(interpolation + (identifier) @variable) + +; Comments +[ + (comment) + (diagnostic_comment) +] @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/bicep/indents.scm b/config/neovim/store/nvim-treesitter/queries/bicep/indents.scm new file mode 100644 index 00000000..055e51b2 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bicep/indents.scm @@ -0,0 +1,27 @@ +[ + (array) + (object) +] @indent.begin + +"}" @indent.end + +[ + "{" + "}" +] @indent.branch + +[ + "[" + "]" +] @indent.branch + +[ + "(" + ")" +] @indent.branch + +[ + (ERROR) + (comment) + (diagnostic_comment) +] @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/bicep/injections.scm b/config/neovim/store/nvim-treesitter/queries/bicep/injections.scm new file mode 100644 index 00000000..5c2d4a57 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bicep/injections.scm @@ -0,0 +1,5 @@ +([ + (comment) + (diagnostic_comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/bicep/locals.scm b/config/neovim/store/nvim-treesitter/queries/bicep/locals.scm new file mode 100644 index 00000000..cc9c3c2c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bicep/locals.scm @@ -0,0 +1,73 @@ +; Scopes +[ + (infrastructure) + (call_expression) + (lambda_expression) + (subscript_expression) + (if_statement) + (for_statement) + (array) + (object) + (interpolation) +] @local.scope + +; References +(property_identifier) @local.reference + +(call_expression + (identifier) @local.reference) + +(object_property + (_) + ":" + (identifier) @local.reference) + +(resource_expression + (identifier) @local.reference) + +; Definitions +(type) @local.definition.associated + +(object_property + (identifier) @local.definition.field + (_)) + +(object_property + (compatible_identifier) @local.definition.field + (_)) + +(user_defined_function + name: (identifier) @local.definition.function) + +(module_declaration + (identifier) @local.definition.namespace) + +(parameter_declaration + (identifier) @local.definition.parameter + (_)) + +(parameter + . + (identifier) @local.definition.parameter) + +(type_declaration + (identifier) @local.definition.type + (_)) + +(variable_declaration + (identifier) @local.definition.var + (_)) + +(metadata_declaration + (identifier) @local.definition.var + (_)) + +(output_declaration + (identifier) @local.definition.var + (_)) + +(for_statement + "for" + (for_loop_parameters + (loop_variable) @local.definition.var + (loop_enumerator) @local.definition.var)) diff --git a/config/neovim/store/nvim-treesitter/queries/bitbake/folds.scm b/config/neovim/store/nvim-treesitter/queries/bitbake/folds.scm new file mode 100644 index 00000000..85d22634 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bitbake/folds.scm @@ -0,0 +1,24 @@ +[ + (function_definition) + (anonymous_python_function) + (python_function_definition) + (while_statement) + (for_statement) + (if_statement) + (with_statement) + (try_statement) + (import_from_statement) + (parameters) + (argument_list) + (parenthesized_expression) + (generator_expression) + (list_comprehension) + (set_comprehension) + (dictionary_comprehension) + (tuple) + (list) + (set) + (dictionary) + (string) + (python_string) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/bitbake/highlights.scm b/config/neovim/store/nvim-treesitter/queries/bitbake/highlights.scm new file mode 100644 index 00000000..4b03fbc2 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bitbake/highlights.scm @@ -0,0 +1,407 @@ +; Includes +[ + "inherit" + "include" + "require" + "export" + "import" +] @keyword.import + +; Keywords +[ + "unset" + "EXPORT_FUNCTIONS" + "python" + "assert" + "exec" + "global" + "nonlocal" + "pass" + "print" + "with" + "as" +] @keyword + +[ + "async" + "await" +] @keyword.coroutine + +[ + "return" + "yield" +] @keyword.return + +(yield + "from" @keyword.return) + +(future_import_statement + "from" @keyword.import + "__future__" @constant.builtin) + +(import_from_statement + "from" @keyword.import) + +"import" @keyword.import + +(aliased_import + "as" @keyword.import) + +[ + "if" + "elif" + "else" +] @keyword.conditional + +[ + "for" + "while" + "break" + "continue" +] @keyword.repeat + +[ + "try" + "except" + "except*" + "raise" + "finally" +] @keyword.exception + +(raise_statement + "from" @keyword.exception) + +(try_statement + (else_clause + "else" @keyword.exception)) + +[ + "addtask" + "deltask" + "addhandler" + "def" + "lambda" +] @keyword.function + +[ + "before" + "after" +] @keyword.modifier + +[ + "append" + "prepend" + "remove" +] @keyword.modifier + +; Variables +[ + (identifier) + (python_identifier) +] @variable + +[ + "noexec" + "INHERIT" + "OVERRIDES" + "$BB_ENV_PASSTHROUGH" + "$BB_ENV_PASSTHROUGH_ADDITIONS" +] @variable.builtin + +; Reset highlighting in f-string interpolations +(interpolation) @none + +; Identifier naming conventions +((python_identifier) @type + (#lua-match? @type "^[A-Z].*[a-z]")) + +([ + (identifier) + (python_identifier) +] @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) + +((python_identifier) @constant.builtin + (#lua-match? @constant.builtin "^__[a-zA-Z0-9_]*__$")) + +((python_identifier) @constant.builtin + (#any-of? @constant.builtin + ; https://docs.python.org/3/library/constants.html + "NotImplemented" "Ellipsis" "quit" "exit" "copyright" "credits" "license")) + +((assignment + left: (python_identifier) @type.definition + (type + (python_identifier) @_annotation)) + (#eq? @_annotation "TypeAlias")) + +((assignment + left: (python_identifier) @type.definition + right: (call + function: (python_identifier) @_func)) + (#any-of? @_func "TypeVar" "NewType")) + +; Fields +(flag) @variable.member + +((attribute + attribute: (python_identifier) @variable.member) + (#lua-match? @variable.member "^[%l_].*$")) + +; Functions +(call + function: (python_identifier) @function.call) + +(call + function: (attribute + attribute: (python_identifier) @function.method.call)) + +((call + function: (python_identifier) @constructor) + (#lua-match? @constructor "^%u")) + +((call + function: (attribute + attribute: (python_identifier) @constructor)) + (#lua-match? @constructor "^%u")) + +((call + function: (python_identifier) @function.builtin) + (#any-of? @function.builtin + "abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray" "bytes" "callable" "chr" + "classmethod" "compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec" + "filter" "float" "format" "frozenset" "getattr" "globals" "hasattr" "hash" "help" "hex" "id" + "input" "int" "isinstance" "issubclass" "iter" "len" "list" "locals" "map" "max" "memoryview" + "min" "next" "object" "oct" "open" "ord" "pow" "print" "property" "range" "repr" "reversed" + "round" "set" "setattr" "slice" "sorted" "staticmethod" "str" "sum" "super" "tuple" "type" + "vars" "zip" "__import__")) + +(python_function_definition + name: (python_identifier) @function) + +(type + (python_identifier) @type) + +(type + (subscript + (python_identifier) @type)) ; type subscript: Tuple[int] + +((call + function: (python_identifier) @_isinstance + arguments: (argument_list + (_) + (python_identifier) @type)) + (#eq? @_isinstance "isinstance")) + +(anonymous_python_function + (identifier) @function) + +(function_definition + (identifier) @function) + +(addtask_statement + (identifier) @function) + +(deltask_statement + (identifier) @function) + +(export_functions_statement + (identifier) @function) + +(addhandler_statement + (identifier) @function) + +(python_function_definition + body: (block + . + (expression_statement + (python_string) @string.documentation @spell))) + +; Namespace +(inherit_path) @module + +; Normal parameters +(parameters + (python_identifier) @variable.parameter) + +; Lambda parameters +(lambda_parameters + (python_identifier) @variable.parameter) + +(lambda_parameters + (tuple_pattern + (python_identifier) @variable.parameter)) + +; Default parameters +(keyword_argument + name: (python_identifier) @variable.parameter) + +; Naming parameters on call-site +(default_parameter + name: (python_identifier) @variable.parameter) + +(typed_parameter + (python_identifier) @variable.parameter) + +(typed_default_parameter + (python_identifier) @variable.parameter) + +; Variadic parameters *args, **kwargs +(parameters + (list_splat_pattern + ; *args + (python_identifier) @variable.parameter)) + +(parameters + (dictionary_splat_pattern + ; **kwargs + (python_identifier) @variable.parameter)) + +; Literals +(none) @constant.builtin + +[ + (true) + (false) +] @boolean + +((python_identifier) @variable.builtin + (#any-of? @variable.builtin "self" "cls")) + +(integer) @number + +(float) @number.float + +; Operators +[ + "?=" + "??=" + ":=" + "=+" + ".=" + "=." + "-" + "-=" + ":=" + "!=" + "*" + "**" + "**=" + "*=" + "/" + "//" + "//=" + "/=" + "&" + "&=" + "%" + "%=" + "^" + "^=" + "+" + "+=" + "<" + "<<" + "<<=" + "<=" + "<>" + "=" + "==" + ">" + ">=" + ">>" + ">>=" + "@" + "@=" + "|" + "|=" + "~" + "->" +] @operator + +[ + "and" + "in" + "is" + "not" + "or" + "is not" + "not in" + "del" +] @keyword.operator + +; Literals +[ + (string) + (python_string) + "\"" +] @string + +(include_path) @string.special.path + +[ + (escape_sequence) + (escape_interpolation) +] @string.escape + +; Punctuation +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + ":" + "->" + ";" + "." + "," + (ellipsis) +] @punctuation.delimiter + +(variable_expansion + [ + "${" + "}" + ] @punctuation.special) + +(inline_python + [ + "${@" + "}" + ] @punctuation.special) + +(interpolation + "{" @punctuation.special + "}" @punctuation.special) + +(type_conversion) @function.macro + +([ + (identifier) + (python_identifier) +] @type.builtin + (#any-of? @type.builtin + ; https://docs.python.org/3/library/exceptions.html + "BaseException" "Exception" "ArithmeticError" "BufferError" "LookupError" "AssertionError" + "AttributeError" "EOFError" "FloatingPointError" "GeneratorExit" "ImportError" + "ModuleNotFoundError" "IndexError" "KeyError" "KeyboardInterrupt" "MemoryError" "NameError" + "NotImplementedError" "OSError" "OverflowError" "RecursionError" "ReferenceError" "RuntimeError" + "StopIteration" "StopAsyncIteration" "SyntaxError" "IndentationError" "TabError" "SystemError" + "SystemExit" "TypeError" "UnboundLocalError" "UnicodeError" "UnicodeEncodeError" + "UnicodeDecodeError" "UnicodeTranslateError" "ValueError" "ZeroDivisionError" "EnvironmentError" + "IOError" "WindowsError" "BlockingIOError" "ChildProcessError" "ConnectionError" + "BrokenPipeError" "ConnectionAbortedError" "ConnectionRefusedError" "ConnectionResetError" + "FileExistsError" "FileNotFoundError" "InterruptedError" "IsADirectoryError" + "NotADirectoryError" "PermissionError" "ProcessLookupError" "TimeoutError" "Warning" + "UserWarning" "DeprecationWarning" "PendingDeprecationWarning" "SyntaxWarning" "RuntimeWarning" + "FutureWarning" "ImportWarning" "UnicodeWarning" "BytesWarning" "ResourceWarning" + ; https://docs.python.org/3/library/stdtypes.html + "bool" "int" "float" "complex" "list" "tuple" "range" "str" "bytes" "bytearray" "memoryview" + "set" "frozenset" "dict" "type" "object")) + +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/bitbake/indents.scm b/config/neovim/store/nvim-treesitter/queries/bitbake/indents.scm new file mode 100644 index 00000000..5f208186 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bitbake/indents.scm @@ -0,0 +1,172 @@ +[ + (import_from_statement) + (parenthesized_expression) + (generator_expression) + (list_comprehension) + (set_comprehension) + (dictionary_comprehension) + (tuple_pattern) + (list_pattern) + (binary_operator) + (lambda) + (concatenated_string) +] @indent.begin + +((list) @indent.align + (#set! indent.open_delimiter "[") + (#set! indent.close_delimiter "]")) + +((dictionary) @indent.align + (#set! indent.open_delimiter "{") + (#set! indent.close_delimiter "}")) + +((set) @indent.align + (#set! indent.open_delimiter "{") + (#set! indent.close_delimiter "}")) + +((for_statement) @indent.begin + (#set! indent.immediate 1)) + +((if_statement) @indent.begin + (#set! indent.immediate 1)) + +((while_statement) @indent.begin + (#set! indent.immediate 1)) + +((try_statement) @indent.begin + (#set! indent.immediate 1)) + +(ERROR + "try" + ":" @indent.begin + (#set! indent.immediate 1)) + +((python_function_definition) @indent.begin + (#set! indent.immediate 1)) + +(function_definition) @indent.begin + +(anonymous_python_function) @indent.begin + +((with_statement) @indent.begin + (#set! indent.immediate 1)) + +(if_statement + condition: (parenthesized_expression) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")") + (#set! indent.avoid_last_matching_next 1)) + +(while_statement + condition: (parenthesized_expression) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")") + (#set! indent.avoid_last_matching_next 1)) + +(ERROR + "(" @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")") + . + (_)) + +((argument_list) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) + +((parameters) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")") + (#set! indent.avoid_last_matching_next 1)) + +((tuple) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) + +(ERROR + "[" @indent.align + (#set! indent.open_delimiter "[") + (#set! indent.close_delimiter "]") + . + (_)) + +(ERROR + "{" @indent.align + (#set! indent.open_delimiter "{") + (#set! indent.close_delimiter "}") + . + (_)) + +[ + (break_statement) + (continue_statement) +] @indent.dedent + +(ERROR + (_) @indent.branch + ":" + . + (#lua-match? @indent.branch "^else")) + +(ERROR + (_) @indent.branch @indent.dedent + ":" + . + (#lua-match? @indent.branch "^elif")) + +(parenthesized_expression + ")" @indent.end) + +(generator_expression + ")" @indent.end) + +(list_comprehension + "]" @indent.end) + +(set_comprehension + "}" @indent.end) + +(dictionary_comprehension + "}" @indent.end) + +(tuple_pattern + ")" @indent.end) + +(list_pattern + "]" @indent.end) + +(function_definition + "}" @indent.end) + +(anonymous_python_function + "}" @indent.end) + +(return_statement + [ + (_) @indent.end + (_ + [ + (_) + ")" + "}" + "]" + ] @indent.end .) + (attribute + attribute: (_) @indent.end) + (call + arguments: (_ + ")" @indent.end)) + "return" @indent.end + ] .) + +[ + ")" + "]" + "}" + (elif_clause) + (else_clause) + (except_clause) + (finally_clause) +] @indent.branch + +(string) @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/bitbake/injections.scm b/config/neovim/store/nvim-treesitter/queries/bitbake/injections.scm new file mode 100644 index 00000000..35c984a5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bitbake/injections.scm @@ -0,0 +1,15 @@ +(call + function: (attribute + object: (python_identifier) @_re) + arguments: (argument_list + (python_string + (string_content) @injection.content) @_string) + (#eq? @_re "re") + (#lua-match? @_string "^r.*") + (#set! injection.language "regex")) + +((shell_content) @injection.content + (#set! injection.language "bash")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/bitbake/locals.scm b/config/neovim/store/nvim-treesitter/queries/bitbake/locals.scm new file mode 100644 index 00000000..e4726ec5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bitbake/locals.scm @@ -0,0 +1,117 @@ +; References +[ + (python_identifier) + (identifier) +] @local.reference + +; Imports +(aliased_import + alias: (python_identifier) @local.definition.import) + +(import_statement + name: (dotted_name + (python_identifier) @local.definition.import)) + +(import_from_statement + name: (dotted_name + (python_identifier) @local.definition.import)) + +; Function with parameters, defines parameters +(parameters + (python_identifier) @local.definition.parameter) + +(default_parameter + (python_identifier) @local.definition.parameter) + +(typed_parameter + (python_identifier) @local.definition.parameter) + +(typed_default_parameter + (python_identifier) @local.definition.parameter) + +; *args parameter +(parameters + (list_splat_pattern + (python_identifier) @local.definition.parameter)) + +; **kwargs parameter +(parameters + (dictionary_splat_pattern + (python_identifier) @local.definition.parameter)) + +; Function defines function and scope +((python_function_definition + name: (python_identifier) @local.definition.function) @local.scope + (#set! definition.function.scope "parent")) + +(function_definition + (identifier) @local.definition.function) + +(anonymous_python_function + (identifier) @local.definition.function) + +; Loops +; not a scope! +(for_statement + left: (pattern_list + (python_identifier) @local.definition.var)) + +(for_statement + left: (tuple_pattern + (python_identifier) @local.definition.var)) + +(for_statement + left: (python_identifier) @local.definition.var) + +; not a scope! +;(while_statement) @local.scope +; for in list comprehension +(for_in_clause + left: (python_identifier) @local.definition.var) + +(for_in_clause + left: (tuple_pattern + (python_identifier) @local.definition.var)) + +(for_in_clause + left: (pattern_list + (python_identifier) @local.definition.var)) + +(dictionary_comprehension) @local.scope + +(list_comprehension) @local.scope + +(set_comprehension) @local.scope + +; Assignments +(assignment + left: (python_identifier) @local.definition.var) + +(assignment + left: (pattern_list + (python_identifier) @local.definition.var)) + +(assignment + left: (tuple_pattern + (python_identifier) @local.definition.var)) + +(assignment + left: (attribute + (python_identifier) + (python_identifier) @local.definition.field)) + +(variable_assignment + (identifier) + operator: [ + "=" + "?=" + "??=" + ":=" + ] @local.definition.var) + +; Walrus operator x := 1 +(named_expression + (python_identifier) @local.definition.var) + +(as_pattern + alias: (as_pattern_target) @local.definition.var) diff --git a/config/neovim/store/nvim-treesitter/queries/blueprint/highlights.scm b/config/neovim/store/nvim-treesitter/queries/blueprint/highlights.scm new file mode 100644 index 00000000..f3c39f23 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/blueprint/highlights.scm @@ -0,0 +1,75 @@ +(object_id) @variable + +(string) @string + +(escape_sequence) @string.escape + +(comment) @comment @spell + +(constant) @constant.builtin + +(boolean) @boolean + +(using) @keyword.import + +(template) @keyword + +(decorator) @attribute + +(property_definition + (property_name) @property) + +(object) @type + +(signal_binding + (signal_name) @function.builtin) + +(signal_binding + (function + (identifier)) @function) + +(signal_binding + "swapped" @keyword) + +(styles_list + "styles" @function.macro) + +(layout_definition + "layout" @function.macro) + +(gettext_string + "_" @function.builtin) + +(menu_definition + "menu" @keyword) + +(menu_section + "section" @keyword) + +(menu_item + "item" @function.macro) + +(import_statement + (gobject_library) @module) + +(import_statement + (version_number) @number.float) + +(float) @number.float + +(number) @number + +[ + ";" + "." + "," +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket diff --git a/config/neovim/store/nvim-treesitter/queries/bp/folds.scm b/config/neovim/store/nvim-treesitter/queries/bp/folds.scm new file mode 100644 index 00000000..c40ea3df --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bp/folds.scm @@ -0,0 +1,6 @@ +[ + (list_expression) + (map_expression) + (module) + (select_expression) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/bp/highlights.scm b/config/neovim/store/nvim-treesitter/queries/bp/highlights.scm new file mode 100644 index 00000000..d18e6eea --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bp/highlights.scm @@ -0,0 +1,55 @@ +(comment) @comment @spell + +(operator) @operator + +(integer_literal + "-" @operator) + +[ + "," + ":" +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(boolean_literal) @boolean + +(integer_literal) @number + +[ + (raw_string_literal) + (interpreted_string_literal) +] @string + +(escape_sequence) @string.escape + +(identifier) @variable + +(module + type: (identifier) @function.call) + +(module + (property + field: (identifier) @variable.parameter)) + +[ + (unset) + (default) +] @variable.builtin + +(condition + name: (identifier) @function.builtin) + +(map_expression + (property + field: (identifier) @property)) + +(select_expression + "select" @keyword.conditional) diff --git a/config/neovim/store/nvim-treesitter/queries/bp/indents.scm b/config/neovim/store/nvim-treesitter/queries/bp/indents.scm new file mode 100644 index 00000000..8cf8adc8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bp/indents.scm @@ -0,0 +1,38 @@ +(list_expression) @indent.begin + +(list_expression + "]" @indent.branch) + +(map_expression) @indent.begin + +(map_expression + "}" @indent.branch) + +(select_expression) @indent.begin + +(select_expression + ")" @indent.branch) + +(select_value) @indent.begin + +(select_value + ")" @indent.branch) + +(select_pattern + "(" @indent.begin) + +(select_pattern + ")" @indent.branch) + +(select_cases) @indent.begin + +(select_cases + "}" @indent.branch) + +(module) @indent.begin + +(module + ")" @indent.branch) + +(module + "}" @indent.branch) diff --git a/config/neovim/store/nvim-treesitter/queries/bp/injections.scm b/config/neovim/store/nvim-treesitter/queries/bp/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bp/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/bp/locals.scm b/config/neovim/store/nvim-treesitter/queries/bp/locals.scm new file mode 100644 index 00000000..66b444fc --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/bp/locals.scm @@ -0,0 +1,12 @@ +(module + (property + field: (identifier) @local.definition.parameter)) + +(map_expression + (property + field: (identifier) @local.definition.field)) + +(assignment + left: (identifier) @local.definition.var) + +(identifier) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/c/folds.scm b/config/neovim/store/nvim-treesitter/queries/c/folds.scm new file mode 100644 index 00000000..bb26a62e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/c/folds.scm @@ -0,0 +1,23 @@ +[ + (for_statement) + (if_statement) + (while_statement) + (do_statement) + (switch_statement) + (case_statement) + (function_definition) + (struct_specifier) + (enum_specifier) + (comment) + (preproc_if) + (preproc_elif) + (preproc_else) + (preproc_ifdef) + (preproc_function_def) + (initializer_list) + (gnu_asm_expression) + (preproc_include)+ +] @fold + +(compound_statement + (compound_statement) @fold) diff --git a/config/neovim/store/nvim-treesitter/queries/c/highlights.scm b/config/neovim/store/nvim-treesitter/queries/c/highlights.scm new file mode 100644 index 00000000..170937c8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/c/highlights.scm @@ -0,0 +1,332 @@ +; Lower priority to prefer @variable.parameter when identifier appears in parameter_declaration. +((identifier) @variable + (#set! "priority" 95)) + +(preproc_def + (preproc_arg) @variable) + +[ + "default" + "goto" + "asm" + "__asm__" +] @keyword + +[ + "enum" + "struct" + "union" + "typedef" +] @keyword.type + +[ + "sizeof" + "offsetof" +] @keyword.operator + +(alignof_expression + . + _ @keyword.operator) + +"return" @keyword.return + +[ + "while" + "for" + "do" + "continue" + "break" +] @keyword.repeat + +[ + "if" + "else" + "case" + "switch" +] @keyword.conditional + +[ + "#if" + "#ifdef" + "#ifndef" + "#else" + "#elif" + "#endif" + "#elifdef" + "#elifndef" + (preproc_directive) +] @keyword.directive + +"#define" @keyword.directive.define + +"#include" @keyword.import + +[ + ";" + ":" + "," + "::" +] @punctuation.delimiter + +"..." @punctuation.special + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + "=" + "-" + "*" + "/" + "+" + "%" + "~" + "|" + "&" + "^" + "<<" + ">>" + "->" + "." + "<" + "<=" + ">=" + ">" + "==" + "!=" + "!" + "&&" + "||" + "-=" + "+=" + "*=" + "/=" + "%=" + "|=" + "&=" + "^=" + ">>=" + "<<=" + "--" + "++" +] @operator + +; Make sure the comma operator is given a highlight group after the comma +; punctuator so the operator is highlighted properly. +(comma_expression + "," @operator) + +[ + (true) + (false) +] @boolean + +(conditional_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +(string_literal) @string + +(system_lib_string) @string + +(escape_sequence) @string.escape + +(null) @constant.builtin + +(number_literal) @number + +(char_literal) @character + +(preproc_defined) @function.macro + +((field_expression + (field_identifier) @property) @_parent + (#not-has-parent? @_parent template_method function_declarator call_expression)) + +(field_designator) @property + +((field_identifier) @property + (#has-ancestor? @property field_declaration) + (#not-has-ancestor? @property function_declarator)) + +(statement_identifier) @label + +(declaration + type: (type_identifier) @_type + declarator: (identifier) @label + (#eq? @_type "__label__")) + +[ + (type_identifier) + (type_descriptor) +] @type + +(storage_class_specifier) @keyword.modifier + +[ + (type_qualifier) + (gnu_asm_qualifier) + "__extension__" +] @keyword.modifier + +(linkage_specification + "extern" @keyword.modifier) + +(type_definition + declarator: (type_identifier) @type.definition) + +(primitive_type) @type.builtin + +(sized_type_specifier + _ @type.builtin + type: _?) + +((identifier) @constant + (#lua-match? @constant "^[A-Z][A-Z0-9_]+$")) + +(preproc_def + (preproc_arg) @constant + (#lua-match? @constant "^[A-Z][A-Z0-9_]+$")) + +(enumerator + name: (identifier) @constant) + +(case_statement + value: (identifier) @constant) + +((identifier) @constant.builtin + ; format-ignore + (#any-of? @constant.builtin + "stderr" "stdin" "stdout" + "__FILE__" "__LINE__" "__DATE__" "__TIME__" + "__STDC__" "__STDC_VERSION__" "__STDC_HOSTED__" + "__cplusplus" "__OBJC__" "__ASSEMBLER__" + "__BASE_FILE__" "__FILE_NAME__" "__INCLUDE_LEVEL__" + "__TIMESTAMP__" "__clang__" "__clang_major__" + "__clang_minor__" "__clang_patchlevel__" + "__clang_version__" "__clang_literal_encoding__" + "__clang_wide_literal_encoding__" + "__FUNCTION__" "__func__" "__PRETTY_FUNCTION__" + "__VA_ARGS__" "__VA_OPT__")) + +(preproc_def + (preproc_arg) @constant.builtin + ; format-ignore + (#any-of? @constant.builtin + "stderr" "stdin" "stdout" + "__FILE__" "__LINE__" "__DATE__" "__TIME__" + "__STDC__" "__STDC_VERSION__" "__STDC_HOSTED__" + "__cplusplus" "__OBJC__" "__ASSEMBLER__" + "__BASE_FILE__" "__FILE_NAME__" "__INCLUDE_LEVEL__" + "__TIMESTAMP__" "__clang__" "__clang_major__" + "__clang_minor__" "__clang_patchlevel__" + "__clang_version__" "__clang_literal_encoding__" + "__clang_wide_literal_encoding__" + "__FUNCTION__" "__func__" "__PRETTY_FUNCTION__" + "__VA_ARGS__" "__VA_OPT__")) + +(attribute_specifier + (argument_list + (identifier) @variable.builtin)) + +(attribute_specifier + (argument_list + (call_expression + function: (identifier) @variable.builtin))) + +((call_expression + function: (identifier) @function.builtin) + (#lua-match? @function.builtin "^__builtin_")) + +((call_expression + function: (identifier) @function.builtin) + (#has-ancestor? @function.builtin attribute_specifier)) + +; Preproc def / undef +(preproc_def + name: (_) @constant) + +(preproc_call + directive: (preproc_directive) @_u + argument: (_) @constant + (#eq? @_u "#undef")) + +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (field_expression + field: (field_identifier) @function.call)) + +(function_declarator + declarator: (identifier) @function) + +(function_declarator + declarator: (parenthesized_declarator + (pointer_declarator + declarator: (field_identifier) @function))) + +(preproc_function_def + name: (identifier) @function.macro) + +(comment) @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$")) + +; Parameters +(parameter_declaration + declarator: (identifier) @variable.parameter) + +(parameter_declaration + declarator: (array_declarator) @variable.parameter) + +(parameter_declaration + declarator: (pointer_declarator) @variable.parameter) + +; K&R functions +; To enable support for K&R functions, +; add the following lines to your own query config and uncomment them. +; They are commented out as they'll conflict with C++ +; Note that you'll need to have `; extends` at the top of your query file. +; +; (parameter_list (identifier) @variable.parameter) +; +; (function_definition +; declarator: _ +; (declaration +; declarator: (identifier) @variable.parameter)) +; +; (function_definition +; declarator: _ +; (declaration +; declarator: (array_declarator) @variable.parameter)) +; +; (function_definition +; declarator: _ +; (declaration +; declarator: (pointer_declarator) @variable.parameter)) +(preproc_params + (identifier) @variable.parameter) + +[ + "__attribute__" + "__declspec" + "__based" + "__cdecl" + "__clrcall" + "__stdcall" + "__fastcall" + "__thiscall" + "__vectorcall" + (ms_pointer_modifier) + (attribute_declaration) +] @attribute diff --git a/config/neovim/store/nvim-treesitter/queries/c/indents.scm b/config/neovim/store/nvim-treesitter/queries/c/indents.scm new file mode 100644 index 00000000..372a26c6 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/c/indents.scm @@ -0,0 +1,99 @@ +[ + (compound_statement) + (field_declaration_list) + (case_statement) + (enumerator_list) + (compound_literal_expression) + (initializer_list) + (init_declarator) +] @indent.begin + +; With current indent logic, if we capture expression_statement with @indent.begin +; It will be affected by _parent_ node with error subnodes deep down the tree +; So narrow indent capture to check for error inside expression statement only, +(expression_statement + (_) @indent.begin + ";" @indent.end) + +(ERROR + "for" + "(" @indent.begin + ";" + ";" + ")" @indent.end) + +((for_statement + body: (_) @_body) @indent.begin + (#not-has-type? @_body compound_statement)) + +(while_statement + condition: (_) @indent.begin) + +((while_statement + body: (_) @_body) @indent.begin + (#not-has-type? @_body compound_statement)) + +((if_statement) + . + (ERROR + "else" @indent.begin)) + +(if_statement + condition: (_) @indent.begin) + +; Supports if without braces (but not both if-else without braces) +(if_statement + consequence: (_ + ";" @indent.end) @_consequence + (#not-has-type? @_consequence compound_statement) + alternative: (else_clause + "else" @indent.branch + [ + (if_statement + (compound_statement) @indent.dedent)? @indent.dedent + (compound_statement)? @indent.dedent + (_)? @indent.dedent + ])?) @indent.begin + +(else_clause + (_ + . + "{" @indent.branch)) + +(compound_statement + "}" @indent.end) + +[ + ")" + "}" + (statement_identifier) +] @indent.branch + +[ + "#define" + "#ifdef" + "#ifndef" + "#elif" + "#if" + "#else" + "#endif" +] @indent.zero + +[ + (preproc_arg) + (string_literal) +] @indent.ignore + +((ERROR + (parameter_declaration)) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) + +([ + (argument_list) + (parameter_list) +] @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) + +(comment) @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/c/injections.scm b/config/neovim/store/nvim-treesitter/queries/c/injections.scm new file mode 100644 index 00000000..77b4d7a8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/c/injections.scm @@ -0,0 +1,128 @@ +((preproc_arg) @injection.content + (#set! injection.language "c")) + +((comment) @injection.content + (#set! injection.language "comment")) + +((comment) @injection.content + (#match? @injection.content "/\\*!([a-zA-Z]+:)?re2c") + (#set! injection.language "re2c")) + +((comment) @injection.content + (#lua-match? @injection.content "/[*\/][!*\/]" + ">=" + "=" + "-=" + "+=" + "*=" + "/=" + "%=" + "^" + "^=" + "&=" + "|=" + "~" + ">>" + ">>>" + "<<" + "<<=" + ">>=" + ">>>=" + "=>" + "??" + "??=" + ".." +] @operator + +[ + ";" + "." + "," + ":" +] @punctuation.delimiter + +(conditional_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +[ + "[" + "]" + "{" + "}" + "(" + ")" +] @punctuation.bracket + +(interpolation_brace) @punctuation.special + +(type_argument_list + [ + "<" + ">" + ] @punctuation.bracket) + +[ + "using" + "as" +] @keyword.import + +(alias_qualified_name + (identifier + "global") @keyword.import) + +[ + "with" + "new" + "typeof" + "sizeof" + "is" + "and" + "or" + "not" + "stackalloc" + "__makeref" + "__reftype" + "__refvalue" + "in" + "out" + "ref" +] @keyword.operator + +[ + "lock" + "params" + "operator" + "default" + "implicit" + "explicit" + "override" + "get" + "set" + "init" + "where" + "add" + "remove" + "checked" + "unchecked" + "fixed" + "alias" + "file" + "unsafe" +] @keyword + +(attribute_target_specifier + . + _ @keyword) + +[ + "enum" + "record" + "class" + "struct" + "interface" + "namespace" + "event" + "delegate" +] @keyword.type + +[ + "async" + "await" +] @keyword.coroutine + +[ + "const" + "extern" + "readonly" + "static" + "volatile" + "required" + "managed" + "unmanaged" + "notnull" + "abstract" + "private" + "protected" + "internal" + "public" + "partial" + "sealed" + "virtual" + "global" +] @keyword.modifier + +(scoped_type + "scoped" @keyword.modifier) + +(query_expression + (_ + [ + "from" + "orderby" + "select" + "group" + "by" + "ascending" + "descending" + "equals" + "let" + ] @keyword)) + +[ + "return" + "yield" +] @keyword.return diff --git a/config/neovim/store/nvim-treesitter/queries/c_sharp/injections.scm b/config/neovim/store/nvim-treesitter/queries/c_sharp/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/c_sharp/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/c_sharp/locals.scm b/config/neovim/store/nvim-treesitter/queries/c_sharp/locals.scm new file mode 100644 index 00000000..bef09400 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/c_sharp/locals.scm @@ -0,0 +1,42 @@ +; Definitions +(variable_declarator + . + (identifier) @local.definition.var) + +(variable_declarator + (tuple_pattern + (identifier) @local.definition.var)) + +(declaration_expression + name: (identifier) @local.definition.var) + +(foreach_statement + left: (identifier) @local.definition.var) + +(foreach_statement + left: (tuple_pattern + (identifier) @local.definition.var)) + +(parameter + (identifier) @local.definition.parameter) + +(method_declaration + name: (identifier) @local.definition.method) + +(local_function_statement + name: (identifier) @local.definition.method) + +(property_declaration + name: (identifier) @local.definition) + +(type_parameter + (identifier) @local.definition.type) + +(class_declaration + name: (identifier) @local.definition) + +; References +(identifier) @local.reference + +; Scope +(block) @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/cairo/folds.scm b/config/neovim/store/nvim-treesitter/queries/cairo/folds.scm new file mode 100644 index 00000000..9937da6f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cairo/folds.scm @@ -0,0 +1,26 @@ +[ + (mod_item) + (struct_item) + (trait_item) + (enum_item) + (impl_item) + (type_item) + (use_declaration) + (let_declaration) + (namespace_definition) + (arguments) + (implicit_arguments) + (tuple_type) + (import_statement) + (attribute_statement) + (with_statement) + (if_statement) + (function_definition) + (struct_definition) + (loop_expression) + (if_expression) + (match_expression) + (call_expression) + (tuple_expression) + (attribute_item) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/cairo/highlights.scm b/config/neovim/store/nvim-treesitter/queries/cairo/highlights.scm new file mode 100644 index 00000000..1ea6245d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cairo/highlights.scm @@ -0,0 +1,414 @@ +; Preproc +[ + "%builtins" + "%lang" +] @keyword.directive + +; Includes +(import_statement + [ + "from" + "import" + ] @keyword.import + module_name: (dotted_name + (identifier) @module .)) + +[ + "as" + "use" + "mod" +] @keyword.import + +; Variables +(identifier) @variable + +; Namespaces +(namespace_definition + (identifier) @module) + +(mod_item + name: (identifier) @module) + +(use_list + (self) @module) + +(scoped_use_list + (self) @module) + +(scoped_identifier + path: (identifier) @module) + +(scoped_identifier + (scoped_identifier + name: (identifier) @module)) + +(scoped_type_identifier + path: (identifier) @module) + +((scoped_identifier + path: (identifier) @type) + (#lua-match? @type "^[A-Z]")) + +((scoped_identifier + name: (identifier) @type) + (#lua-match? @type "^[A-Z]")) + +((scoped_identifier + name: (identifier) @constant) + (#lua-match? @constant "^[A-Z][A-Z%d_]*$")) + +((scoped_identifier + path: (identifier) @type + name: (identifier) @constant) + (#lua-match? @type "^[A-Z]") + (#lua-match? @constant "^[A-Z]")) + +((scoped_type_identifier + path: (identifier) @type + name: (type_identifier) @constant) + (#lua-match? @type "^[A-Z]") + (#lua-match? @constant "^[A-Z]")) + +(scoped_use_list + path: (identifier) @module) + +(scoped_use_list + path: (scoped_identifier + (identifier) @module)) + +(use_list + (scoped_identifier + (identifier) @module + . + (_))) + +(use_list + (identifier) @type + (#lua-match? @type "^[A-Z]")) + +(use_as_clause + alias: (identifier) @type + (#lua-match? @type "^[A-Z]")) + +; Keywords +[ + ; 0.x + "using" + "let" + "const" + "local" + "rel" + "abs" + "dw" + "alloc_locals" + (inst_ret) + "with_attr" + "with" + "call" + "nondet" + ; 1.0 + "impl" + "implicits" + "of" + "ref" + "mut" +] @keyword + +[ + "struct" + "enum" + "namespace" + "type" + "trait" +] @keyword.type + +[ + "func" + "fn" + "end" +] @keyword.function + +"return" @keyword.return + +[ + "cast" + "new" + "and" +] @keyword.operator + +[ + "tempvar" + "extern" +] @keyword.modifier + +[ + "if" + "else" + "match" +] @keyword.conditional + +"loop" @keyword.repeat + +[ + "assert" + "static_assert" + "nopanic" +] @keyword.exception + +; Fields +(implicit_arguments + (typed_identifier + (identifier) @variable.member)) + +(member_expression + "." + (identifier) @variable.member) + +(call_expression + (assignment_expression + left: (identifier) @variable.member)) + +(tuple_expression + (assignment_expression + left: (identifier) @variable.member)) + +(field_identifier) @variable.member + +(shorthand_field_initializer + (identifier) @variable.member) + +; Parameters +(arguments + (typed_identifier + (identifier) @variable.parameter)) + +(call_expression + (tuple_expression + (assignment_expression + left: (identifier) @variable.parameter))) + +(return_type + (tuple_type + (named_type + . + (identifier) @variable.parameter))) + +(parameter + (identifier) @variable.parameter) + +; Builtins +(builtin_directive + (identifier) @variable.builtin) + +(lang_directive + (identifier) @variable.builtin) + +[ + "ap" + "fp" + (self) +] @variable.builtin + +; Functions +(function_definition + "func" + (identifier) @function) + +(function_definition + "fn" + (identifier) @function) + +(function_signature + "fn" + (identifier) @function) + +(extern_function_statement + (identifier) @function) + +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (scoped_identifier + (identifier) @function.call .)) + +(call_expression + function: (field_expression + field: (field_identifier) @function.call)) + +"jmp" @function.builtin + +; Types +(struct_definition + . + (identifier) @type + (typed_identifier + (identifier) @variable.member)?) + +(named_type + (identifier) @type .) + +[ + (builtin_type) + (primitive_type) +] @type.builtin + +((identifier) @type + (#lua-match? @type "^[A-Z][a-zA-Z0-9_]*$")) + +(type_identifier) @type + +; Constants +((identifier) @constant + (#lua-match? @constant "^[A-Z_][A-Z0-9_]*$")) + +(enum_variant + name: (identifier) @constant) + +(call_expression + function: (scoped_identifier + "::" + name: (identifier) @constant) + (#lua-match? @constant "^[A-Z]")) + +((match_arm + pattern: (match_pattern + (identifier) @constant)) + (#lua-match? @constant "^[A-Z]")) + +((match_arm + pattern: (match_pattern + (scoped_identifier + name: (identifier) @constant))) + (#lua-match? @constant "^[A-Z]")) + +((identifier) @constant.builtin + (#any-of? @constant.builtin "Some" "None" "Ok" "Err")) + +; Constructors +(unary_expression + "new" + (call_expression + . + (identifier) @constructor)) + +((call_expression + . + (identifier) @constructor) + (#lua-match? @constructor "^%u")) + +; Attributes +(decorator + "@" @attribute + (identifier) @attribute) + +(attribute_item + (identifier) @function.macro) + +(attribute_item + (scoped_identifier + (identifier) @function.macro .)) + +; Labels +(label + . + (identifier) @label) + +(inst_jmp_to_label + "jmp" + . + (identifier) @label) + +(inst_jnz_to_label + "jmp" + . + (identifier) @label) + +; Operators +[ + "+" + "-" + "*" + "/" + "**" + "==" + "!=" + "&" + "=" + "++" + "+=" + "@" + "!" + "~" + ".." + "&&" + "||" + "^" + "<" + "<=" + ">" + ">=" + "<<" + ">>" + "%" + "-=" + "*=" + "/=" + "%=" + "&=" + "|=" + "^=" + "<<=" + ">>=" + "?" +] @operator + +; Literals +(number) @number + +(boolean) @boolean + +[ + (string) + (short_string) +] @string + +; Punctuation +(attribute_item + "#" @punctuation.special) + +[ + "." + "," + ":" + ";" + "->" + "=>" + "::" +] @punctuation.delimiter + +[ + "{" + "}" + "(" + ")" + "[" + "]" + "%{" + "%}" +] @punctuation.bracket + +(type_parameters + [ + "<" + ">" + ] @punctuation.bracket) + +(type_arguments + [ + "<" + ">" + ] @punctuation.bracket) + +; Comment +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/cairo/indents.scm b/config/neovim/store/nvim-treesitter/queries/cairo/indents.scm new file mode 100644 index 00000000..f63ef36f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cairo/indents.scm @@ -0,0 +1,57 @@ +[ + (mod_item) + (struct_item) + (enum_item) + (impl_item) + (struct_expression) + (match_expression) + (tuple_expression) + (match_arm) + (match_block) + (call_expression) + (assignment_expression) + (arguments) + (block) + (use_list) + (field_declaration_list) + (enum_variant_list) + (tuple_pattern) +] @indent.begin + +(import_statement + "(") @indent.begin + +(block + "}" @indent.end) + +(enum_item + body: (enum_variant_list + "}" @indent.end)) + +(match_expression + body: (match_block + "}" @indent.end)) + +(mod_item + body: (declaration_list + "}" @indent.end)) + +(struct_item + body: (field_declaration_list + "}" @indent.end)) + +(trait_item + body: (declaration_list + "}" @indent.end)) + +[ + ")" + "]" + "}" +] @indent.branch + +[ + (comment) + (string) + (short_string) +] @indent.ignore diff --git a/config/neovim/store/nvim-treesitter/queries/cairo/injections.scm b/config/neovim/store/nvim-treesitter/queries/cairo/injections.scm new file mode 100644 index 00000000..fbb66be3 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cairo/injections.scm @@ -0,0 +1,5 @@ +((python_code) @injection.content + (#set! injection.language "python")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/cairo/locals.scm b/config/neovim/store/nvim-treesitter/queries/cairo/locals.scm new file mode 100644 index 00000000..0573cf6d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cairo/locals.scm @@ -0,0 +1,66 @@ +; References +(identifier) @local.reference + +((type_identifier) @local.reference + (#set! reference.kind "type")) + +((field_identifier) @local.reference + (#set! reference.kind "field")) + +; Scopes +[ + (program) + (block) + (function_definition) + (loop_expression) + (if_expression) + (match_expression) + (match_arm) + (struct_item) + (enum_item) + (impl_item) +] @local.scope + +(use_declaration + argument: (scoped_identifier + name: (identifier) @local.definition.import)) + +(use_as_clause + alias: (identifier) @local.definition.import) + +(use_list + (identifier) @local.definition.import) ; use std::process::{Child, Command, Stdio}; + +; Functions +(function_definition + (identifier) @local.definition.function) + +(function_definition + (identifier) @local.definition.method + (parameter + (self))) + +; Function with parameters, defines parameters +(parameter + [ + (identifier) + (self) + ] @local.definition.parameter) + +; Types +(struct_item + name: (type_identifier) @local.definition.type) + +(constrained_type_parameter + left: (type_identifier) @local.definition.type) ; the P in remove_file>(path: P) + +(enum_item + name: (type_identifier) @local.definition.type) + +; Module +(mod_item + name: (identifier) @local.definition.namespace) + +; Variables +(assignment_expression + left: (identifier) @local.definition.var) diff --git a/config/neovim/store/nvim-treesitter/queries/capnp/folds.scm b/config/neovim/store/nvim-treesitter/queries/capnp/folds.scm new file mode 100644 index 00000000..6e3f9c18 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/capnp/folds.scm @@ -0,0 +1,14 @@ +[ + (annotation_targets) + (const_list) + (enum) + (interface) + (implicit_generics) + (generics) + (group) + (method_parameters) + (named_return_types) + (struct) + (struct_shorthand) + (union) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/capnp/highlights.scm b/config/neovim/store/nvim-treesitter/queries/capnp/highlights.scm new file mode 100644 index 00000000..a48c007e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/capnp/highlights.scm @@ -0,0 +1,141 @@ +; Preproc +[ + (unique_id) + (top_level_annotation_body) +] @keyword.directive + +; Includes +[ + "import" + "$import" + "embed" + "using" +] @keyword.import + +(import_path) @string.special.path + +; Keywords +"extends" @keyword + +[ + "struct" + "interface" + "union" + "enum" + "annotation" + "group" + "namespace" +] @keyword.type + +; Builtins +"const" @keyword.modifier + +[ + (primitive_type) + "List" +] @type.builtin + +; Typedefs +(type_definition) @type.definition + +; Labels (@number, @number!) +(field_version) @label + +; Methods +[ + (annotation_definition_identifier) + (method_identifier) +] @function.method + +; Fields +(field_identifier) @variable.member + +; Properties +(property) @property + +; Parameters +[ + (param_identifier) + (return_identifier) +] @variable.parameter + +(annotation_target) @variable.parameter.builtin + +; Constants +[ + (const_identifier) + (local_const) + (enum_member) +] @constant + +(void) @constant.builtin + +; Types +[ + (enum_identifier) + (extend_type) + (type_identifier) +] @type + +; Attributes +[ + (annotation_identifier) + (attribute) +] @attribute + +; Operators +"=" @operator + +; Literals +[ + (string) + (concatenated_string) + (block_text) + (namespace) +] @string + +(namespace) @string.special + +(escape_sequence) @string.escape + +(data_string) @string.special + +(number) @number + +(float) @number.float + +(boolean) @boolean + +(data_hex) @string.special.symbol + +; Punctuation +[ + "*" + "$" + ":" +] @punctuation.special + +[ + "{" + "}" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "." + "," + ";" + "->" +] @punctuation.delimiter + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/capnp/indents.scm b/config/neovim/store/nvim-treesitter/queries/capnp/indents.scm new file mode 100644 index 00000000..cc2f4d75 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/capnp/indents.scm @@ -0,0 +1,40 @@ +[ + (annotation_targets) + (const) + (enum) + (interface) + (implicit_generics) + (generics) + (group) + (method_parameters) + (named_return_types) + (struct) + (union) + (field) +] @indent.begin + +((struct_shorthand + (property)) @indent.align + (#set! indent.open_delimiter "(") + (#set! indent.close_delimiter ")")) + +((method + (field_version)) @indent.align + (#set! indent.open_delimiter field_version)) + +((const_list + (const_value)) @indent.align + (#set! indent.open_delimiter "[") + (#set! indent.close_delimiter "]")) + +(concatenated_string) @indent.align + +[ + "}" + ")" +] @indent.end @indent.branch + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/capnp/injections.scm b/config/neovim/store/nvim-treesitter/queries/capnp/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/capnp/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/capnp/locals.scm b/config/neovim/store/nvim-treesitter/queries/capnp/locals.scm new file mode 100644 index 00000000..d1f0ccac --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/capnp/locals.scm @@ -0,0 +1,97 @@ +[ + (message) + (annotation_targets) + (const_list) + (enum) + (interface) + (implicit_generics) + (generics) + (group) + (method_parameters) + (named_return_types) + (struct) + (struct_shorthand) + (union) +] @local.scope + +[ + (extend_type) + (field_type) +] @local.reference + +(custom_type + (type_identifier) @local.reference) + +(custom_type + (generics + (generic_parameters + (generic_identifier) @local.reference))) + +(annotation_definition_identifier) @local.definition + +(const_identifier) @local.definition.constant + +(enum + (enum_identifier) @local.definition.enum) + +[ + (enum_member) + (field_identifier) +] @local.definition.field + +(method_identifier) @local.definition.method + +(namespace) @local.definition.namespace + +[ + (param_identifier) + (return_identifier) +] @local.definition.parameter + +(group + (type_identifier) @local.definition.type) + +(struct + (type_identifier) @local.definition.type) + +(union + (type_identifier) @local.definition.type) + +(interface + (type_identifier) @local.definition.type) + +; Generics Related (don't know how to combine these) +(struct + (generics + (generic_parameters + (generic_identifier) @local.definition.parameter))) + +(interface + (generics + (generic_parameters + (generic_identifier) @local.definition.parameter))) + +(method + (implicit_generics + (implicit_generic_parameters + (generic_identifier) @local.definition.parameter))) + +(method + (generics + (generic_parameters + (generic_identifier) @local.definition.parameter))) + +(annotation + (generics + (generic_parameters + (generic_identifier) @local.definition.type))) + +(replace_using + (generics + (generic_parameters + (generic_identifier) @local.definition.type))) + +(return_type + (generics + (generic_parameters + (generic_identifier) @local.definition.type))) diff --git a/config/neovim/store/nvim-treesitter/queries/chatito/folds.scm b/config/neovim/store/nvim-treesitter/queries/chatito/folds.scm new file mode 100644 index 00000000..052dd206 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/chatito/folds.scm @@ -0,0 +1,5 @@ +[ + (intent_def) + (slot_def) + (alias_def) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/chatito/highlights.scm b/config/neovim/store/nvim-treesitter/queries/chatito/highlights.scm new file mode 100644 index 00000000..47113f2c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/chatito/highlights.scm @@ -0,0 +1,54 @@ +; Punctuation +[ + "%[" + "@[" + "~[" + "*[" + "]" + "(" + ")" +] @punctuation.bracket + +"," @punctuation.delimiter + +(eq) @operator + +([ + "\"" + "'" +] @punctuation.special + (#set! conceal "")) + +[ + "%" + "?" + "#" +] @character.special + +; Entities +(intent) @module + +(slot) @type + +(variation) @attribute + +(alias) @keyword.directive + +(number) @number + +(argument + key: (string) @property + value: (string) @string) + +(escape) @string.escape + +; Import +"import" @keyword.import + +(file) @string.special.path + +; Text +(word) @spell + +; Comment +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/chatito/indents.scm b/config/neovim/store/nvim-treesitter/queries/chatito/indents.scm new file mode 100644 index 00000000..dc9e13d7 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/chatito/indents.scm @@ -0,0 +1,8 @@ +[ + (intent_def) + (slot_def) + (alias_def) +] @indent.begin + +(ERROR + "]") @indent.begin diff --git a/config/neovim/store/nvim-treesitter/queries/chatito/injections.scm b/config/neovim/store/nvim-treesitter/queries/chatito/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/chatito/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/chatito/locals.scm b/config/neovim/store/nvim-treesitter/queries/chatito/locals.scm new file mode 100644 index 00000000..827447f5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/chatito/locals.scm @@ -0,0 +1,16 @@ +; Definitions +(intent_def + (intent) @local.definition) + +(slot_def + (slot) @local.definition) + +(alias_def + (alias) @local.definition) + +; References +(slot_ref + (slot) @local.reference) + +(alias_ref + (alias) @local.reference) diff --git a/config/neovim/store/nvim-treesitter/queries/clojure/folds.scm b/config/neovim/store/nvim-treesitter/queries/clojure/folds.scm new file mode 100644 index 00000000..eceb6971 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/clojure/folds.scm @@ -0,0 +1,2 @@ +(source + (list_lit) @fold) diff --git a/config/neovim/store/nvim-treesitter/queries/clojure/highlights.scm b/config/neovim/store/nvim-treesitter/queries/clojure/highlights.scm new file mode 100644 index 00000000..62e59ae5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/clojure/highlights.scm @@ -0,0 +1,347 @@ +; >> Explanation +; Parsers for lisps are a bit weird in that they just return the raw forms. +; This means we have to do a bit of extra work in the queries to get things +; highlighted as they should be. +; +; For the most part this means that some things have to be assigned multiple +; groups. +; By doing this we can add a basic capture and then later refine it with more +; specialized captures. +; This can mean that sometimes things are highlighted weirdly because they +; have multiple highlight groups applied to them. +; >> Literals +((dis_expr) @comment + (#set! "priority" 105) + ; Higher priority to mark the whole sexpr as a comment + ) + +(kwd_lit) @string.special.symbol + +(str_lit) @string + +(num_lit) @number + +(char_lit) @character + +(bool_lit) @boolean + +(nil_lit) @constant.builtin + +(comment) @comment @spell + +(regex_lit) @string.regexp + +[ + "'" + "`" +] @string.escape + +[ + "~" + "~@" + "#" +] @punctuation.special + +[ + "{" + "}" + "[" + "]" + "(" + ")" +] @punctuation.bracket + +; >> Symbols +; General symbol highlighting +(sym_lit) @variable + +; General function calls +(list_lit + . + (sym_lit) @function.call) + +(anon_fn_lit + . + (sym_lit) @function.call) + +; Quoted symbols +(quoting_lit + (sym_lit) @string.special.symbol) + +(syn_quoting_lit + (sym_lit) @string.special.symbol) + +; Used in destructure pattern +((sym_lit) @variable.parameter + (#lua-match? @variable.parameter "^[&]")) + +; Inline function variables +((sym_lit) @variable.builtin + (#lua-match? @variable.builtin "^%%%d*$")) + +((sym_lit) @variable.builtin + (#eq? @variable.builtin "%&")) + +; Constructor +((sym_lit) @constructor + (#lua-match? @constructor "^-%>[^>].*")) + +; Builtin dynamic variables +((sym_lit) @variable.builtin + (#any-of? @variable.builtin + "*agent*" "*allow-unresolved-vars*" "*assert*" "*clojure-version*" "*command-line-args*" + "*compile-files*" "*compile-path*" "*compiler-options*" "*data-readers*" + "*default-data-reader-fn*" "*err*" "*file*" "*flush-on-newline*" "*fn-loader*" "*in*" + "*math-context*" "*ns*" "*out*" "*print-dup*" "*print-length*" "*print-level*" "*print-meta*" + "*print-namespace-maps*" "*print-readably*" "*read-eval*" "*reader-resolver*" "*source-path*" + "*suppress-read*" "*unchecked-math*" "*use-context-classloader*" "*verbose-defrecords*" + "*warn-on-reflection*")) + +; Builtin repl variables +((sym_lit) @variable.builtin + (#any-of? @variable.builtin "*1" "*2" "*3" "*e")) + +; Types +(sym_lit + name: (sym_name) @_name + (#lua-match? @_name "^[%u][^/%s]*$")) @type + +; Symbols with `.` but not `/` +(sym_lit + !namespace + name: (sym_name) @_name + (#lua-match? @_name "^[^.]+[.]")) @type + +; Interop +; (.instanceMember instance args*) +; (.instanceMember Classname args*) +((sym_lit + name: (sym_name) @_name) @function.method + (#lua-match? @_name "^%.[^-]")) + +; (.-instanceField instance) +((sym_name) @variable.member + (#lua-match? @variable.member "^%.%-%S*")) + +; Classname/staticField +(sym_lit + namespace: (sym_ns) @_namespace + (#lua-match? @_namespace "^[%u]%S*$")) @variable.member + +; (Classname/staticMethod args*) +(list_lit + . + (sym_lit + namespace: (sym_ns) @_namespace + (#lua-match? @_namespace "^%u")) @function.method) + +; TODO: Special casing for the `.` macro +; Operators +((sym_lit) @operator + (#any-of? @operator "*" "*'" "+" "+'" "-" "-'" "/" "<" "<=" ">" ">=" "=" "==")) + +((sym_lit) @keyword.operator + (#any-of? @keyword.operator "not" "not=" "and" "or")) + +; Definition functions +((sym_lit) @keyword + (#any-of? @keyword + "def" "defonce" "defrecord" "defmacro" "definline" "definterface" "defmulti" "defmethod" + "defstruct" "defprotocol" "deftype")) + +((sym_lit) @keyword + (#eq? @keyword "declare")) + +((sym_name) @keyword.coroutine + (#any-of? @keyword.coroutine + "alts!" "alts!!" "await" "await-for" "await1" "chan" "close!" "future" "go" "sync" "thread" + "timeout" "!" ">!!")) + +((sym_lit + name: (sym_name) @_keyword.function.name) @keyword.function + (#any-of? @_keyword.function.name "defn" "defn-" "fn" "fn*")) + +; Comment +((sym_lit) @comment + (#eq? @comment "comment")) + +; Conditionals +((sym_lit) @keyword.conditional + (#any-of? @keyword.conditional "case" "cond" "cond->" "cond->>" "condp")) + +((sym_lit) @keyword.conditional + (#any-of? @keyword.conditional "if" "if-let" "if-not" "if-some")) + +((sym_lit) @keyword.conditional + (#any-of? @keyword.conditional "when" "when-first" "when-let" "when-not" "when-some")) + +; Repeats +((sym_lit) @keyword.repeat + (#any-of? @keyword.repeat "doseq" "dotimes" "for" "loop" "recur" "while")) + +; Exception +((sym_lit) @keyword.exception + (#any-of? @keyword.exception "throw" "try" "catch" "finally")) + +; Includes +((sym_lit) @keyword.import + (#any-of? @keyword.import "ns" "import" "require" "use")) + +; Builtin macros +; TODO: Do all these items belong here? +((sym_lit + name: (sym_name) @function.macro) + (#any-of? @function.macro + "." ".." "->" "->>" "amap" "areduce" "as->" "assert" "binding" "bound-fn" "delay" "do" "dosync" + "doto" "extend-protocol" "extend-type" "gen-class" "gen-interface" "io!" "lazy-cat" "lazy-seq" + "let" "letfn" "locking" "memfn" "monitor-enter" "monitor-exit" "proxy" "proxy-super" "pvalues" + "refer-clojure" "reify" "set!" "some->" "some->>" "time" "unquote" "unquote-splicing" "var" + "vswap!" "with-bindings" "with-in-str" "with-loading-context" "with-local-vars" "with-open" + "with-out-str" "with-precision" "with-redefs")) + +; All builtin functions +; (->> (ns-publics *ns*) +; (keep (fn [[s v]] (when-not (:macro (meta v)) s))) +; sort +; clojure.pprint/pprint)) +; ...and then lots of manual filtering... +((sym_lit + name: (sym_name) @function.builtin) + (#any-of? @function.builtin + "->ArrayChunk" "->Eduction" "->Vec" "->VecNode" "->VecSeq" "-cache-protocol-fn" "-reset-methods" + "PrintWriter-on" "StackTraceElement->vec" "Throwable->map" "accessor" "aclone" "add-classpath" + "add-tap" "add-watch" "agent" "agent-error" "agent-errors" "aget" "alength" "alias" "all-ns" + "alter" "alter-meta!" "alter-var-root" "ancestors" "any?" "apply" "array-map" "aset" + "aset-boolean" "aset-byte" "aset-char" "aset-double" "aset-float" "aset-int" "aset-long" + "aset-short" "assoc" "assoc!" "assoc-in" "associative?" "atom" "bases" "bean" "bigdec" "bigint" + "biginteger" "bit-and" "bit-and-not" "bit-clear" "bit-flip" "bit-not" "bit-or" "bit-set" + "bit-shift-left" "bit-shift-right" "bit-test" "bit-xor" "boolean" "boolean-array" "boolean?" + "booleans" "bound-fn*" "bound?" "bounded-count" "butlast" "byte" "byte-array" "bytes" "bytes?" + "cast" "cat" "char" "char-array" "char-escape-string" "char-name-string" "char?" "chars" "chunk" + "chunk-append" "chunk-buffer" "chunk-cons" "chunk-first" "chunk-next" "chunk-rest" + "chunked-seq?" "class" "class?" "clear-agent-errors" "clojure-version" "coll?" "commute" "comp" + "comparator" "compare" "compare-and-set!" "compile" "complement" "completing" "concat" "conj" + "conj!" "cons" "constantly" "construct-proxy" "contains?" "count" "counted?" "create-ns" + "create-struct" "cycle" "dec" "dec'" "decimal?" "dedupe" "default-data-readers" "delay?" + "deliver" "denominator" "deref" "derive" "descendants" "destructure" "disj" "disj!" "dissoc" + "dissoc!" "distinct" "distinct?" "doall" "dorun" "double" "double-array" "eduction" "empty" + "empty?" "ensure" "ensure-reduced" "enumeration-seq" "error-handler" "error-mode" "eval" "even?" + "every-pred" "every?" "extend" "extenders" "extends?" "false?" "ffirst" "file-seq" "filter" + "filterv" "find" "find-keyword" "find-ns" "find-protocol-impl" "find-protocol-method" "find-var" + "first" "flatten" "float" "float-array" "float?" "floats" "flush" "fn?" "fnext" "fnil" "force" + "format" "frequencies" "future-call" "future-cancel" "future-cancelled?" "future-done?" + "future?" "gensym" "get" "get-in" "get-method" "get-proxy-class" "get-thread-bindings" + "get-validator" "group-by" "halt-when" "hash" "hash-combine" "hash-map" "hash-ordered-coll" + "hash-set" "hash-unordered-coll" "ident?" "identical?" "identity" "ifn?" "in-ns" "inc" "inc'" + "indexed?" "init-proxy" "inst-ms" "inst-ms*" "inst?" "instance?" "int" "int-array" "int?" + "integer?" "interleave" "intern" "interpose" "into" "into-array" "ints" "isa?" "iterate" + "iterator-seq" "juxt" "keep" "keep-indexed" "key" "keys" "keyword" "keyword?" "last" "line-seq" + "list" "list*" "list?" "load" "load-file" "load-reader" "load-string" "loaded-libs" "long" + "long-array" "longs" "macroexpand" "macroexpand-1" "make-array" "make-hierarchy" "map" + "map-entry?" "map-indexed" "map?" "mapcat" "mapv" "max" "max-key" "memoize" "merge" "merge-with" + "meta" "method-sig" "methods" "min" "min-key" "mix-collection-hash" "mod" "munge" "name" + "namespace" "namespace-munge" "nat-int?" "neg-int?" "neg?" "newline" "next" "nfirst" "nil?" + "nnext" "not-any?" "not-empty" "not-every?" "ns-aliases" "ns-imports" "ns-interns" "ns-map" + "ns-name" "ns-publics" "ns-refers" "ns-resolve" "ns-unalias" "ns-unmap" "nth" "nthnext" + "nthrest" "num" "number?" "numerator" "object-array" "odd?" "parents" "partial" "partition" + "partition-all" "partition-by" "pcalls" "peek" "persistent!" "pmap" "pop" "pop!" + "pop-thread-bindings" "pos-int?" "pos?" "pr" "pr-str" "prefer-method" "prefers" + "primitives-classnames" "print" "print-ctor" "print-dup" "print-method" "print-simple" + "print-str" "printf" "println" "println-str" "prn" "prn-str" "promise" "proxy-call-with-super" + "proxy-mappings" "proxy-name" "push-thread-bindings" "qualified-ident?" "qualified-keyword?" + "qualified-symbol?" "quot" "rand" "rand-int" "rand-nth" "random-sample" "range" "ratio?" + "rational?" "rationalize" "re-find" "re-groups" "re-matcher" "re-matches" "re-pattern" "re-seq" + "read" "read+string" "read-line" "read-string" "reader-conditional" "reader-conditional?" + "realized?" "record?" "reduce" "reduce-kv" "reduced" "reduced?" "reductions" "ref" + "ref-history-count" "ref-max-history" "ref-min-history" "ref-set" "refer" + "release-pending-sends" "rem" "remove" "remove-all-methods" "remove-method" "remove-ns" + "remove-tap" "remove-watch" "repeat" "repeatedly" "replace" "replicate" "requiring-resolve" + "reset!" "reset-meta!" "reset-vals!" "resolve" "rest" "restart-agent" "resultset-seq" "reverse" + "reversible?" "rseq" "rsubseq" "run!" "satisfies?" "second" "select-keys" "send" "send-off" + "send-via" "seq" "seq?" "seqable?" "seque" "sequence" "sequential?" "set" + "set-agent-send-executor!" "set-agent-send-off-executor!" "set-error-handler!" "set-error-mode!" + "set-validator!" "set?" "short" "short-array" "shorts" "shuffle" "shutdown-agents" + "simple-ident?" "simple-keyword?" "simple-symbol?" "slurp" "some" "some-fn" "some?" "sort" + "sort-by" "sorted-map" "sorted-map-by" "sorted-set" "sorted-set-by" "sorted?" "special-symbol?" + "spit" "split-at" "split-with" "str" "string?" "struct" "struct-map" "subs" "subseq" "subvec" + "supers" "swap!" "swap-vals!" "symbol" "symbol?" "tagged-literal" "tagged-literal?" "take" + "take-last" "take-nth" "take-while" "tap>" "test" "the-ns" "thread-bound?" "to-array" + "to-array-2d" "trampoline" "transduce" "transient" "tree-seq" "true?" "type" "unchecked-add" + "unchecked-add-int" "unchecked-byte" "unchecked-char" "unchecked-dec" "unchecked-dec-int" + "unchecked-divide-int" "unchecked-double" "unchecked-float" "unchecked-inc" "unchecked-inc-int" + "unchecked-int" "unchecked-long" "unchecked-multiply" "unchecked-multiply-int" + "unchecked-negate" "unchecked-negate-int" "unchecked-remainder-int" "unchecked-short" + "unchecked-subtract" "unchecked-subtract-int" "underive" "unquote" "unquote-splicing" + "unreduced" "unsigned-bit-shift-right" "update" "update-in" "update-proxy" "uri?" "uuid?" "val" + "vals" "var-get" "var-set" "var?" "vary-meta" "vec" "vector" "vector-of" "vector?" "volatile!" + "volatile?" "vreset!" "with-bindings*" "with-meta" "with-redefs-fn" "xml-seq" "zero?" "zipmap" + ; earlier + "drop" "drop-last" "drop-while" "double?" "doubles" "ex-data" "ex-info" + ; 1.10 + "ex-cause" "ex-message" + ; 1.11 + "NaN?" "abs" "infinite?" "iteration" "random-uuid" "parse-boolean" "parse-double" "parse-long" + "parse-uuid" "seq-to-map-for-destructuring" "update-keys" "update-vals" + ; 1.12 + "partitionv" "partitionv-all" "splitv-at")) + +; >> Context based highlighting +; def-likes +; Correctly highlight docstrings +;(list_lit +;. +;(sym_lit) @_keyword ; Don't really want to highlight twice +;(#any-of? @_keyword +;"def" "defonce" "defrecord" "defmacro" "definline" +;"defmulti" "defmethod" "defstruct" "defprotocol" +;"deftype") +;. +;(sym_lit) +;. +; TODO: Add @comment highlight +;(str_lit)? +;. +;(_)) +; Function definitions +(list_lit + . + ((sym_lit + name: (sym_name) @_keyword.function.name) + (#any-of? @_keyword.function.name "defn" "defn-" "fn" "fn*")) + . + (sym_lit)? @function + . + ; TODO: Add @comment highlight + (str_lit)?) + +; TODO: Fix parameter highlighting +; I think there's a bug here in nvim-treesitter +; TODO: Reproduce bug and file ticket +;. +;[(vec_lit +; (sym_lit)* @variable.parameter) +; (list_lit +; (vec_lit +; (sym_lit)* @variable.parameter))]) +;[((list_lit +; (vec_lit +; (sym_lit) @variable.parameter) +; (_) +; + +; ((vec_lit +; (sym_lit) @variable.parameter) +; (_))) +; Meta punctuation +; NOTE: When the above `Function definitions` query captures the +; the @function it also captures the child meta_lit +; We capture the meta_lit symbol (^) after so that the later +; highlighting overrides the former +"^" @punctuation.special + +; namespaces +(list_lit + . + (sym_lit) @_include + (#eq? @_include "ns") + . + (sym_lit) @module) diff --git a/config/neovim/store/nvim-treesitter/queries/clojure/injections.scm b/config/neovim/store/nvim-treesitter/queries/clojure/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/clojure/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/clojure/locals.scm b/config/neovim/store/nvim-treesitter/queries/clojure/locals.scm new file mode 100644 index 00000000..e47adce4 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/clojure/locals.scm @@ -0,0 +1 @@ +; placeholder file to get incremental selection to work diff --git a/config/neovim/store/nvim-treesitter/queries/cmake/folds.scm b/config/neovim/store/nvim-treesitter/queries/cmake/folds.scm new file mode 100644 index 00000000..ef153b91 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cmake/folds.scm @@ -0,0 +1,8 @@ +[ + (if_condition) + (foreach_loop) + (while_loop) + (function_def) + (macro_def) + (block_def) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/cmake/highlights.scm b/config/neovim/store/nvim-treesitter/queries/cmake/highlights.scm new file mode 100644 index 00000000..d6e0f243 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cmake/highlights.scm @@ -0,0 +1,223 @@ +(normal_command + (identifier) + (argument_list + (argument + (unquoted_argument)) @constant) + (#lua-match? @constant "^[%u@][%u%d_]+$")) + +[ + (quoted_argument) + (bracket_argument) +] @string + +(variable_ref) @none + +(variable) @variable + +[ + (bracket_comment) + (line_comment) +] @comment @spell + +(normal_command + (identifier) @function) + +[ + "ENV" + "CACHE" +] @module + +[ + "$" + "{" + "}" + "<" + ">" +] @punctuation.special + +[ + "(" + ")" +] @punctuation.bracket + +[ + (function) + (endfunction) + (macro) + (endmacro) +] @keyword.function + +[ + (if) + (elseif) + (else) + (endif) +] @keyword.conditional + +[ + (foreach) + (endforeach) + (while) + (endwhile) +] @keyword.repeat + +(normal_command + (identifier) @keyword.repeat + (#match? @keyword.repeat "\\c^(continue|break)$")) + +(normal_command + (identifier) @keyword.return + (#match? @keyword.return "\\c^return$")) + +(function_command + (function) + (argument_list + . + (argument) @function + (argument)* @variable.parameter)) + +(macro_command + (macro) + (argument_list + . + (argument) @function.macro + (argument)* @variable.parameter)) + +(block_def + (block_command + (block) @function.builtin + (argument_list + (argument + (unquoted_argument) @constant)) + (#any-of? @constant "SCOPE_FOR" "POLICIES" "VARIABLES" "PROPAGATE")) + (endblock_command + (endblock) @function.builtin)) + +; +((argument) @boolean + (#match? @boolean "\\c^(1|on|yes|true|y|0|off|no|false|n|ignore|notfound|.*-notfound)$")) + +; +(if_command + (if) + (argument_list + (argument) @keyword.operator) + (#any-of? @keyword.operator + "NOT" "AND" "OR" "COMMAND" "POLICY" "TARGET" "TEST" "DEFINED" "IN_LIST" "EXISTS" "IS_NEWER_THAN" + "IS_DIRECTORY" "IS_SYMLINK" "IS_ABSOLUTE" "MATCHES" "LESS" "GREATER" "EQUAL" "LESS_EQUAL" + "GREATER_EQUAL" "STRLESS" "STRGREATER" "STREQUAL" "STRLESS_EQUAL" "STRGREATER_EQUAL" + "VERSION_LESS" "VERSION_GREATER" "VERSION_EQUAL" "VERSION_LESS_EQUAL" "VERSION_GREATER_EQUAL")) + +(elseif_command + (elseif) + (argument_list + (argument) @keyword.operator) + (#any-of? @keyword.operator + "NOT" "AND" "OR" "COMMAND" "POLICY" "TARGET" "TEST" "DEFINED" "IN_LIST" "EXISTS" "IS_NEWER_THAN" + "IS_DIRECTORY" "IS_SYMLINK" "IS_ABSOLUTE" "MATCHES" "LESS" "GREATER" "EQUAL" "LESS_EQUAL" + "GREATER_EQUAL" "STRLESS" "STRGREATER" "STREQUAL" "STRLESS_EQUAL" "STRGREATER_EQUAL" + "VERSION_LESS" "VERSION_GREATER" "VERSION_EQUAL" "VERSION_LESS_EQUAL" "VERSION_GREATER_EQUAL")) + +(normal_command + (identifier) @function.builtin + (#match? @function.builtin + "\\c^(cmake_host_system_information|cmake_language|cmake_minimum_required|cmake_parse_arguments|cmake_path|cmake_policy|configure_file|execute_process|file|find_file|find_library|find_package|find_path|find_program|foreach|get_cmake_property|get_directory_property|get_filename_component|get_property|include|include_guard|list|macro|mark_as_advanced|math|message|option|separate_arguments|set|set_directory_properties|set_property|site_name|string|unset|variable_watch|add_compile_definitions|add_compile_options|add_custom_command|add_custom_target|add_definitions|add_dependencies|add_executable|add_library|add_link_options|add_subdirectory|add_test|aux_source_directory|build_command|create_test_sourcelist|define_property|enable_language|enable_testing|export|fltk_wrap_ui|get_source_file_property|get_target_property|get_test_property|include_directories|include_external_msproject|include_regular_expression|install|link_directories|link_libraries|load_cache|project|remove_definitions|set_source_files_properties|set_target_properties|set_tests_properties|source_group|target_compile_definitions|target_compile_features|target_compile_options|target_include_directories|target_link_directories|target_link_libraries|target_link_options|target_precompile_headers|target_sources|try_compile|try_run|ctest_build|ctest_configure|ctest_coverage|ctest_empty_binary_directory|ctest_memcheck|ctest_read_custom_files|ctest_run_script|ctest_sleep|ctest_start|ctest_submit|ctest_test|ctest_update|ctest_upload)$")) + +(normal_command + (identifier) @_function + (argument_list + . + (argument) @variable) + (#match? @_function "\\c^set$")) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^set$") + (argument_list + . + (argument) + ((argument) @_cache @keyword.modifier + . + (argument) @_type @type + (#any-of? @_cache "CACHE") + (#any-of? @_type "BOOL" "FILEPATH" "PATH" "STRING" "INTERNAL")))) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^unset$") + (argument_list + . + (argument) + (argument) @keyword.modifier + (#any-of? @keyword.modifier "CACHE" "PARENT_SCOPE"))) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^list$") + (argument_list + . + (argument) @constant + (#any-of? @constant "LENGTH" "GET" "JOIN" "SUBLIST" "FIND") + . + (argument) @variable + (argument) @variable .)) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^list$") + (argument_list + . + (argument) @constant + . + (argument) @variable + (#any-of? @constant + "APPEND" "FILTER" "INSERT" "POP_BACK" "POP_FRONT" "PREPEND" "REMOVE_ITEM" "REMOVE_AT" + "REMOVE_DUPLICATES" "REVERSE" "SORT"))) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^list$") + (argument_list + . + (argument) @_transform @constant + . + (argument) @variable + . + (argument) @_action @constant + (#eq? @_transform "TRANSFORM") + (#any-of? @_action "APPEND" "PREPEND" "TOUPPER" "TOLOWER" "STRIP" "GENEX_STRIP" "REPLACE"))) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^list$") + (argument_list + . + (argument) @_transform @constant + . + (argument) @variable + . + (argument) @_action @constant + . + (argument)? @_selector @constant + (#eq? @_transform "TRANSFORM") + (#any-of? @_action "APPEND" "PREPEND" "TOUPPER" "TOLOWER" "STRIP" "GENEX_STRIP" "REPLACE") + (#any-of? @_selector "AT" "FOR" "REGEX"))) + +(normal_command + (identifier) @_function + (#match? @_function "\\c^list$") + (argument_list + . + (argument) @_transform @constant + (argument) @constant + . + (argument) @variable + (#eq? @_transform "TRANSFORM") + (#eq? @constant "OUTPUT_VARIABLE"))) + +(escape_sequence) @string.escape + +((source_file + . + (line_comment) @keyword.directive @nospell) + (#lua-match? @keyword.directive "^#!/")) diff --git a/config/neovim/store/nvim-treesitter/queries/cmake/indents.scm b/config/neovim/store/nvim-treesitter/queries/cmake/indents.scm new file mode 100644 index 00000000..cbd976c7 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cmake/indents.scm @@ -0,0 +1,26 @@ +[ + (normal_command) + (if_condition) + (foreach_loop) + (while_loop) + (function_def) + (macro_def) + (block_def) +] @indent.begin + +[ + (elseif_command) + (else_command) + (endif_command) + (endforeach_command) + (endwhile_command) + (endfunction_command) + (endmacro_command) + (endblock_command) +] @indent.branch + +")" @indent.branch + +")" @indent.end + +(argument_list) @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/comment/highlights.scm b/config/neovim/store/nvim-treesitter/queries/comment/highlights.scm new file mode 100644 index 00000000..5d18b790 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/comment/highlights.scm @@ -0,0 +1,49 @@ +((tag + (name) @comment.todo @nospell + ("(" @punctuation.bracket + (user) @constant + ")" @punctuation.bracket)? + ":" @punctuation.delimiter) + (#any-of? @comment.todo "TODO" "WIP")) + +("text" @comment.todo @nospell + (#any-of? @comment.todo "TODO" "WIP")) + +((tag + (name) @comment.note @nospell + ("(" @punctuation.bracket + (user) @constant + ")" @punctuation.bracket)? + ":" @punctuation.delimiter) + (#any-of? @comment.note "NOTE" "XXX" "INFO" "DOCS" "PERF" "TEST")) + +("text" @comment.note @nospell + (#any-of? @comment.note "NOTE" "XXX" "INFO" "DOCS" "PERF" "TEST")) + +((tag + (name) @comment.warning @nospell + ("(" @punctuation.bracket + (user) @constant + ")" @punctuation.bracket)? + ":" @punctuation.delimiter) + (#any-of? @comment.warning "HACK" "WARNING" "WARN" "FIX")) + +("text" @comment.warning @nospell + (#any-of? @comment.warning "HACK" "WARNING" "WARN" "FIX")) + +((tag + (name) @comment.error @nospell + ("(" @punctuation.bracket + (user) @constant + ")" @punctuation.bracket)? + ":" @punctuation.delimiter) + (#any-of? @comment.error "FIXME" "BUG" "ERROR")) + +("text" @comment.error @nospell + (#any-of? @comment.error "FIXME" "BUG" "ERROR")) + +; Issue number (#123) +("text" @number + (#lua-match? @number "^#[0-9]+$")) + +(uri) @string.special.url @nospell diff --git a/config/neovim/store/nvim-treesitter/queries/commonlisp/folds.scm b/config/neovim/store/nvim-treesitter/queries/commonlisp/folds.scm new file mode 100644 index 00000000..eceb6971 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/commonlisp/folds.scm @@ -0,0 +1,2 @@ +(source + (list_lit) @fold) diff --git a/config/neovim/store/nvim-treesitter/queries/commonlisp/highlights.scm b/config/neovim/store/nvim-treesitter/queries/commonlisp/highlights.scm new file mode 100644 index 00000000..84f132e4 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/commonlisp/highlights.scm @@ -0,0 +1,320 @@ +(sym_lit) @variable + +; A highlighting for functions/macros in th cl namespace is available in theHamsta/nvim-treesitter-commonlisp +;(list_lit . (sym_lit) @function.builtin (#cl-standard-function? @function.builtin)) +;(list_lit . (sym_lit) @function.builtin (#cl-standard-macro? @function.macro)) +(dis_expr) @comment + +(defun_keyword) @function.macro + +(defun_header + function_name: (_) @function) + +(defun_header + lambda_list: (list_lit + (sym_lit) @variable.parameter)) + +(defun_header + keyword: (defun_keyword + "defmethod") + lambda_list: (list_lit + (list_lit + . + (sym_lit) + . + (sym_lit) @string.special.symbol))) + +(defun_header + lambda_list: (list_lit + (list_lit + . + (sym_lit) @variable.parameter + . + (_)))) + +(defun_header + specifier: (sym_lit) @string.special.symbol) + +[ + ":" + "::" + "." +] @punctuation.special + +[ + (accumulation_verb) + (for_clause_word) + "for" + "and" + "finally" + "thereis" + "always" + "when" + "if" + "unless" + "else" + "do" + "loop" + "below" + "in" + "from" + "across" + "repeat" + "being" + "into" + "with" + "as" + "while" + "until" + "return" + "initially" +] @function.macro + +"=" @operator + +(include_reader_macro) @string.special.symbol + +[ + "#C" + "#c" +] @number + +[ + (kwd_lit) + (self_referential_reader_macro) +] @string.special.symbol + +(package_lit + package: (_) @module) + +"cl" @module + +(str_lit) @string + +(num_lit) @number + +((sym_lit) @boolean + (#any-of? @boolean "t" "T")) + +(nil_lit) @constant.builtin + +(comment) @comment @spell + +; dynamic variables +((sym_lit) @variable.builtin + (#lua-match? @variable.builtin "^[*].+[*]$")) + +; quote +"'" @string.escape + +(format_specifier) @string.escape + +(quoting_lit) @string.escape + +; syntax quote +"`" @string.escape + +"," @string.escape + +",@" @string.escape + +(syn_quoting_lit) @string.escape + +(unquoting_lit) @none + +(unquote_splicing_lit) @none + +[ + "(" + ")" +] @punctuation.bracket + +(block_comment) @comment @spell + +(with_clause + type: (_) @type) + +(for_clause + type: (_) @type) + +; defun-like things +(list_lit + . + (sym_lit) @function.macro + . + (sym_lit) @function + (#eq? @function.macro "deftest")) + +; Macros and Special Operators +(list_lit + . + (sym_lit) @function.macro + ; Generated via https://github.com/theHamsta/nvim-treesitter-commonlisp/blob/22fdc9fd6ed594176cc7299cc6f68dd21c94c63b/scripts/generate-symbols.lisp#L1-L21 + (#any-of? @function.macro + "do*" "step" "handler-bind" "decf" "prog1" "destructuring-bind" "defconstant" "do" "lambda" + "with-standard-io-syntax" "case" "restart-bind" "ignore-errors" "with-slots" "prog2" "defclass" + "define-condition" "print-unreadable-object" "defvar" "when" "with-open-file" "prog" "incf" + "declaim" "and" "loop-finish" "multiple-value-bind" "pop" "psetf" "defmacro" "with-open-stream" + "define-modify-macro" "defsetf" "formatter" "call-method" "handler-case" "pushnew" "or" + "with-hash-table-iterator" "ecase" "cond" "defun" "remf" "ccase" "define-compiler-macro" + "dotimes" "multiple-value-list" "assert" "deftype" "with-accessors" "trace" + "with-simple-restart" "do-symbols" "nth-value" "define-symbol-macro" "psetq" "rotatef" "dolist" + "check-type" "multiple-value-setq" "push" "pprint-pop" "loop" "define-setf-expander" + "pprint-exit-if-list-exhausted" "with-condition-restarts" "defstruct" "with-input-from-string" + "with-compilation-unit" "defgeneric" "with-output-to-string" "untrace" "defparameter" + "ctypecase" "do-external-symbols" "etypecase" "do-all-symbols" "with-package-iterator" "unless" + "defmethod" "in-package" "defpackage" "return" "typecase" "shiftf" "setf" "pprint-logical-block" + "time" "restart-case" "prog*" "define-method-combination" "optimize")) + +; constant +((sym_lit) @constant + (#lua-match? @constant "^[+].+[+]$")) + +(var_quoting_lit + marker: "#'" @string.special.symbol + value: (_) @string.special.symbol) + +[ + "#" + "#p" + "#P" +] @string.special.symbol + +(list_lit + . + (sym_lit) @function.builtin + ; Generated via https://github.com/theHamsta/nvim-treesitter-commonlisp/blob/22fdc9fd6ed594176cc7299cc6f68dd21c94c63b/scripts/generate-symbols.lisp#L1-L21 + (#any-of? @function.builtin + "apropos-list" "subst" "substitute" "pprint-linear" "file-namestring" "write-char" "do*" + "slot-exists-p" "file-author" "macro-function" "rassoc" "make-echo-stream" + "arithmetic-error-operation" "position-if-not" "list" "cdadr" "lisp-implementation-type" + "vector-push" "let" "length" "string-upcase" "adjoin" "digit-char" "step" "member-if" + "handler-bind" "lognot" "apply" "gcd" "slot-unbound" "stringp" "values-list" "stable-sort" + "decode-float" "make-list" "rplaca" "isqrt" "export" "synonym-stream-symbol" "function-keywords" + "replace" "tanh" "maphash" "code-char" "decf" "array-displacement" "string-not-lessp" + "slot-value" "remove-if" "cell-error-name" "vectorp" "cdddar" "two-way-stream-output-stream" + "parse-integer" "get-internal-real-time" "fourth" "make-string" "slot-missing" "byte-size" + "string-trim" "nstring-downcase" "cdaddr" "<" "labels" "interactive-stream-p" "fifth" "max" + "logxor" "pathname-name" "function" "realp" "eql" "logand" "short-site-name" "prog1" + "user-homedir-pathname" "list-all-packages" "exp" "cadar" "read-char-no-hang" + "package-error-package" "stream-external-format" "bit-andc2" "nsubstitute-if" "mapcar" + "complement" "load-logical-pathname-translations" "pprint-newline" "oddp" "caaar" + "destructuring-bind" "copy-alist" "acos" "go" "bit-nor" "defconstant" "fceiling" "tenth" + "nreverse" "=" "nunion" "slot-boundp" "string>" "count-if" "atom" "char=" "random-state-p" + "row-major-aref" "bit-andc1" "translate-pathname" "simple-vector-p" "coerce" "substitute-if-not" + "zerop" "invalid-method-error" "compile" "realpart" "remove-if-not" "pprint-tab" + "hash-table-rehash-threshold" "invoke-restart" "if" "count" "/=" "do" "initialize-instance" + "abs" "schar" "simple-condition-format-control" "delete-package" "subst-if" "lambda" + "hash-table-count" "array-has-fill-pointer-p" "bit" "with-standard-io-syntax" "parse-namestring" + "proclaim" "array-in-bounds-p" "multiple-value-call" "rplacd" "some" "graphic-char-p" + "read-from-string" "consp" "cadaar" "acons" "every" "make-pathname" "mask-field" "case" + "set-macro-character" "bit-and" "restart-bind" "echo-stream-input-stream" "compile-file" + "fill-pointer" "numberp" "acosh" "array-dimensions" "documentation" "minusp" "inspect" + "copy-structure" "integer-length" "ensure-generic-function" "char>=" "quote" "lognor" + "make-two-way-stream" "ignore-errors" "tailp" "with-slots" "fboundp" + "logical-pathname-translations" "equal" "float-sign" "shadow" "sleep" "numerator" "prog2" "getf" + "ldb-test" "round" "locally" "echo-stream-output-stream" "log" "get-macro-character" + "alphanumericp" "find-method" "nintersection" "defclass" "define-condition" + "print-unreadable-object" "defvar" "broadcast-stream-streams" "floatp" "subst-if-not" "integerp" + "translate-logical-pathname" "subsetp" "when" "write-string" "with-open-file" "clrhash" + "apropos" "intern" "min" "string-greaterp" "import" "nset-difference" "prog" "incf" + "both-case-p" "multiple-value-prog1" "characterp" "streamp" "digit-char-p" "random" + "string-lessp" "make-string-input-stream" "copy-symbol" "read-sequence" "logcount" "bit-not" + "boundp" "encode-universal-time" "third" "declaim" "map" "cons" "set-syntax-from-char" "and" + "cis" "symbol-plist" "loop-finish" "standard-char-p" "multiple-value-bind" "asin" "string" "pop" + "complex" "fdefinition" "psetf" "type-error-datum" "output-stream-p" "floor" "write-line" "<=" + "defmacro" "rational" "hash-table-test" "with-open-stream" "read-char" "string-capitalize" + "get-properties" "y-or-n-p" "use-package" "remove" "compiler-macro-function" "read" + "package-nicknames" "remove-duplicates" "make-load-form-saving-slots" "dribble" + "define-modify-macro" "make-dispatch-macro-character" "close" "cosh" "open" "finish-output" + "string-downcase" "car" "nstring-capitalize" "software-type" "read-preserving-whitespace" "cadr" + "fround" "nsublis" "defsetf" "find-all-symbols" "char>" "no-applicable-method" + "compute-restarts" "pathname" "bit-orc2" "write-sequence" "pprint-tabular" "symbol-value" + "char-name" "get-decoded-time" "formatter" "bit-vector-p" "intersection" "pathname-type" + "clear-input" "call-method" "princ-to-string" "symbolp" "make-load-form" "nsubst" + "pprint-dispatch" "handler-case" "method-combination-error" "probe-file" "atan" "string<" + "type-error-expected-type" "pushnew" "unread-char" "print" "or" "with-hash-table-iterator" + "make-sequence" "ecase" "unwind-protect" "require" "sixth" "get-dispatch-macro-character" + "char-not-lessp" "read-byte" "tagbody" "file-error-pathname" "catch" "rationalp" "char-downcase" + "char-int" "array-rank" "cond" "last" "make-string-output-stream" "array-dimension" + "host-namestring" "input-stream-p" "decode-universal-time" "defun" "eval-when" "char-code" + "pathname-directory" "evenp" "subseq" "pprint" "ftruncate" "make-instance" "pathname-host" + "logbitp" "remf" "1+" "copy-pprint-dispatch" "char-upcase" "error" "read-line" "second" + "make-package" "directory" "special-operator-p" "open-stream-p" "rassoc-if-not" "ccase" "equalp" + "substitute-if" "*" "char/=" "cdr" "sqrt" "lcm" "logical-pathname" "eval" + "define-compiler-macro" "nsubstitute-if-not" "mapcon" "imagpart" "set-exclusive-or" + "simple-condition-format-arguments" "expt" "concatenate" "file-position" "macrolet" "keywordp" + "hash-table-rehash-size" "+" "eighth" "use-value" "char-equal" "bit-xor" "format" "byte" + "dotimes" "namestring" "char-not-equal" "multiple-value-list" "assert" "append" "notany" "typep" + "delete-file" "makunbound" "cdaar" "file-write-date" ">" "cdddr" "write-to-string" "funcall" + "member-if-not" "deftype" "readtable-case" "with-accessors" "truename" "constantp" "rassoc-if" + "caaadr" "tree-equal" "nset-exclusive-or" "nsubstitute" "make-instances-obsolete" + "package-use-list" "invoke-debugger" "provide" "count-if-not" "trace" "logandc1" "nthcdr" + "char<=" "functionp" "with-simple-restart" "set-dispatch-macro-character" "logorc2" "unexport" + "rest" "unbound-slot-instance" "make-hash-table" "hash-table-p" "reinitialize-instance" "nth" + "do-symbols" "nreconc" "macroexpand" "store-value" "float-precision" "remprop" "nth-value" + "define-symbol-macro" "update-instance-for-redefined-class" "identity" "progv" "progn" + "return-from" "readtablep" "rem" "symbol-name" "psetq" "wild-pathname-p" "char" "list*" "char<" + "plusp" "pairlis" "cddar" "pprint-indent" "union" "compiled-function-p" "rotatef" "abort" + "machine-type" "concatenated-stream-streams" "string-right-trim" "enough-namestring" + "arithmetic-error-operands" "ceiling" "dolist" "delete" "make-condition" "string-left-trim" + "integer-decode-float" "check-type" "notevery" "function-lambda-expression" "-" + "multiple-value-setq" "name-char" "push" "pprint-pop" "compile-file-pathname" "list-length" + "nstring-upcase" "eq" "find-if" "method-qualifiers" "caadr" "cddr" "string=" "let*" + "remove-method" "pathname-match-p" "find-package" "truncate" "caaddr" "get-setf-expansion" + "loop" "define-setf-expander" "caddr" "package-shadowing-symbols" "force-output" + "slot-makunbound" "string-not-greaterp" "cdadar" "cdaadr" "logandc2" "make-array" + "merge-pathnames" "sin" "1-" "machine-version" "ffloor" "packagep" "set-pprint-dispatch" "flet" + "gensym" "pprint-exit-if-list-exhausted" "cos" "get" "mapl" "delete-if" + "with-condition-restarts" "atanh" "copy-list" "fill" "char-not-greaterp" "bit-orc1" "mod" + "package-used-by-list" "warn" "add-method" "simple-string-p" "find-restart" "describe" + "pathname-version" "peek-char" "yes-or-no-p" "complexp" "aref" "not" "position-if" "string>=" + "defstruct" "float-radix" "ninth" "caadar" "subtypep" "set" "butlast" "allocate-instance" + "with-input-from-string" "assoc" "write" "make-random-state" "bit-eqv" "float-digits" + "long-site-name" "with-compilation-unit" "delete-duplicates" "make-symbol" "room" "cdar" + "pprint-fill" "defgeneric" "macroexpand-1" "scale-float" "cdaaar" + "update-instance-for-different-class" "array-row-major-index" "ed" "file-string-length" + "ensure-directories-exist" "copy-readtable" "string<=" "seventh" "with-output-to-string" + "signum" "elt" "untrace" "null" "defparameter" "block" "prin1" "revappend" "gentemp" "ctypecase" + "ash" "sxhash" "listp" "do-external-symbols" "bit-ior" "etypecase" "sort" "change-class" + "find-class" "alpha-char-p" "map-into" "terpri" "do-all-symbols" "ldb" "logorc1" "search" + "fmakunbound" "load" "character" "string-not-equal" "pathnamep" "make-broadcast-stream" "arrayp" + "mapcan" "cerror" "invoke-restart-interactively" "assoc-if-not" "with-package-iterator" + "get-internal-run-time" "read-delimited-list" "unless" "lower-case-p" "restart-name" "/" "boole" + "defmethod" "float" "software-version" "vector-pop" "vector-push-extend" "caar" "ldiff" "member" + "find-symbol" "reduce" "svref" "describe-object" "logior" "string-equal" "type-of" "position" + "cddadr" "pathname-device" "get-output-stream-string" "symbol-package" "tan" + "compute-applicable-methods" "cddddr" "nsubst-if-not" "sublis" "set-difference" + "two-way-stream-input-stream" "adjustable-array-p" "machine-instance" "signal" "conjugate" + "caaaar" "endp" "lisp-implementation-version" "cddaar" "package-name" "adjust-array" "bit-nand" + "gethash" "in-package" "symbol-function" "make-concatenated-stream" "defpackage" "class-of" + "no-next-method" "logeqv" "deposit-field" "disassemble" "unuse-package" "copy-tree" "find" + "asinh" "class-name" "rename-file" "values" "print-not-readable-object" "mismatch" "cadadr" + "shadowing-import" "delete-if-not" "maplist" "listen" "return" "stream-element-type" "unintern" + "merge" "make-synonym-stream" "prin1-to-string" "nsubst-if" "byte-position" "phase" + "muffle-warning" "remhash" "continue" "load-time-value" "hash-table-size" + "upgraded-complex-part-type" "char-lessp" "sbit" "upgraded-array-element-type" "file-length" + "typecase" "cadddr" "first" "rationalize" "logtest" "find-if-not" "dpb" "mapc" "sinh" + "char-greaterp" "shiftf" "denominator" "get-universal-time" "nconc" "setf" "lognand" + "rename-package" "pprint-logical-block" "break" "symbol-macrolet" "the" "fresh-line" + "clear-output" "assoc-if" "string/=" "princ" "directory-namestring" "stream-error-stream" + "array-element-type" "setq" "copy-seq" "time" "restart-case" "prog*" "shared-initialize" + "array-total-size" "simple-bit-vector-p" "define-method-combination" "write-byte" "constantly" + "caddar" "print-object" "vector" "throw" "reverse" ">=" "upper-case-p" "nbutlast") + ) + +(list_lit + . + (sym_lit) @operator + (#match? @operator "^([+*-+=<>]|<=|>=|/=)$")) + +((sym_lit) @string.special.symbol + (#lua-match? @string.special.symbol "^[&]")) + +[ + (array_dimension) + "#0A" + "#0a" +] @number + +(char_lit) @character diff --git a/config/neovim/store/nvim-treesitter/queries/commonlisp/locals.scm b/config/neovim/store/nvim-treesitter/queries/commonlisp/locals.scm new file mode 100644 index 00000000..98036d32 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/commonlisp/locals.scm @@ -0,0 +1,109 @@ +(defun_header + function_name: (sym_lit) @local.definition.function + (#set! definition.function.scope "parent")) + +(defun_header + lambda_list: (list_lit + (sym_lit) @local.definition.parameter)) + +(defun_header + keyword: (defun_keyword + "defmethod") + lambda_list: (list_lit + (list_lit + . + (sym_lit) + . + (sym_lit) @local.definition.type))) + +(defun_header + lambda_list: (list_lit + (list_lit + . + (sym_lit) @local.definition.parameter + . + (_)))) + +(sym_lit) @local.reference + +(defun) @local.scope + +((list_lit + . + (sym_lit) @_defvar + . + (sym_lit) @local.definition.var) + (#match? @_defvar "^(cl:)?(defvar|defparameter)$")) + +(list_lit + . + (sym_lit) @_deftest + . + (sym_lit) @local.definition.function + (#eq? @_deftest "deftest")) @local.scope + +(list_lit + . + (sym_lit) @_deftest + . + (sym_lit) @local.definition.function + (#eq? @_deftest "deftest")) @local.scope + +(for_clause + . + (sym_lit) @local.definition.var) + +(with_clause + . + (sym_lit) @local.definition.var) + +(loop_macro) @local.scope + +(list_lit + . + (sym_lit) @_let + (#match? @_let "(cl:|cffi:)?(with-accessors|with-foreign-objects|let[*]?)") + . + (list_lit + (list_lit + . + (sym_lit) @local.definition.var))) @local.scope + +(list_lit + . + (sym_lit) @_let + (#match? @_let "(cl:|alexandria:)?(with-gensyms|dotimes|with-foreign-object)") + . + (list_lit + . + (sym_lit) @local.definition.var)) @local.scope + +(list_lit + . + (kwd_lit) @_import_from + (#eq? @_import_from ":import-from") + . + (_) + (kwd_lit + (kwd_symbol) @local.definition.import)) + +(list_lit + . + (kwd_lit) @_import_from + (#eq? @_import_from ":import-from") + . + (_) + (sym_lit) @local.definition.import) + +(list_lit + . + (kwd_lit) @_use + (#eq? @_use ":use") + (kwd_lit + (kwd_symbol) @local.definition.import)) + +(list_lit + . + (kwd_lit) @_use + (#eq? @_use ":use") + (sym_lit) @local.definition.import) diff --git a/config/neovim/store/nvim-treesitter/queries/cooklang/highlights.scm b/config/neovim/store/nvim-treesitter/queries/cooklang/highlights.scm new file mode 100644 index 00000000..ca870dd4 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cooklang/highlights.scm @@ -0,0 +1,22 @@ +(metadata) @comment + +(ingredient + "@" @punctuation.delimiter + (name)? @string.special.symbol + (amount + (quantity)? @number + (units)? @constant)?) + +(timer + "~" @punctuation.delimiter + (name)? @string.special.symbol + (amount + (quantity)? @number + (units)? @constant)?) + +(cookware + "#" @punctuation.delimiter + (name)? @string.special.symbol + (amount + (quantity)? @number + (units)? @constant)?) diff --git a/config/neovim/store/nvim-treesitter/queries/corn/folds.scm b/config/neovim/store/nvim-treesitter/queries/corn/folds.scm new file mode 100644 index 00000000..2ce5ddb3 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/corn/folds.scm @@ -0,0 +1,5 @@ +[ + (object) + (array) + (assign_block) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/corn/highlights.scm b/config/neovim/store/nvim-treesitter/queries/corn/highlights.scm new file mode 100644 index 00000000..02929146 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/corn/highlights.scm @@ -0,0 +1,26 @@ +"let" @keyword + +"in" @keyword + +[ + "{" + "}" + "[" + "]" +] @punctuation.bracket + +"." @punctuation.delimiter + +(input) @constant + +(comment) @comment @spell + +(string) @string + +(integer) @number + +(float) @number.float + +(boolean) @boolean + +(null) @keyword diff --git a/config/neovim/store/nvim-treesitter/queries/corn/indents.scm b/config/neovim/store/nvim-treesitter/queries/corn/indents.scm new file mode 100644 index 00000000..f1f5e04d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/corn/indents.scm @@ -0,0 +1,24 @@ +[ + (assign_block + "{") + (object) + (array) +] @indent.begin + +(assign_block + "}" @indent.branch) + +(assign_block + "}" @indent.end) + +(object + "}" @indent.branch) + +(object + "}" @indent.end) + +(array + "]" @indent.branch) + +(array + "]" @indent.end) diff --git a/config/neovim/store/nvim-treesitter/queries/corn/locals.scm b/config/neovim/store/nvim-treesitter/queries/corn/locals.scm new file mode 100644 index 00000000..7e78c4d2 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/corn/locals.scm @@ -0,0 +1,13 @@ +; scopes +[ + (object) + (array) +] @local.scope + +; definitions +(assign_block + (assignment + (input) @local.definition.constant)) + +(value + (input) @local.reference) diff --git a/config/neovim/store/nvim-treesitter/queries/cpon/folds.scm b/config/neovim/store/nvim-treesitter/queries/cpon/folds.scm new file mode 100644 index 00000000..02feec4e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cpon/folds.scm @@ -0,0 +1,5 @@ +[ + (meta_map) + (map) + (array) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/cpon/highlights.scm b/config/neovim/store/nvim-treesitter/queries/cpon/highlights.scm new file mode 100644 index 00000000..9cc438ea --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cpon/highlights.scm @@ -0,0 +1,54 @@ +; Literals +(string) @string + +(escape_sequence) @string.escape + +(hex_blob + "x" @character.special + (_) @string) + +(esc_blob + "b" @character.special + (_) @string) + +(datetime + "d" @character.special + (_) @string.special) + +(_ + key: (_) @property) + +(number) @number + +(float) @number.float + +(boolean) @boolean + +(null) @constant.builtin + +; Punctuation +[ + "," + ":" +] @punctuation.delimiter + +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "<" + ">" +] @punctuation.bracket + +("\"" @string + (#set! conceal "")) + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/cpon/indents.scm b/config/neovim/store/nvim-treesitter/queries/cpon/indents.scm new file mode 100644 index 00000000..8ec2ff57 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cpon/indents.scm @@ -0,0 +1,17 @@ +[ + (meta_map) + (map) + (imap) + (array) +] @indent.begin + +[ + "]" + "}" + ">" +] @indent.end @indent.branch + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/cpon/injections.scm b/config/neovim/store/nvim-treesitter/queries/cpon/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cpon/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/cpon/locals.scm b/config/neovim/store/nvim-treesitter/queries/cpon/locals.scm new file mode 100644 index 00000000..2a4ba471 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cpon/locals.scm @@ -0,0 +1,6 @@ +[ + (document) + (meta_map) + (map) + (array) +] @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/cpp/folds.scm b/config/neovim/store/nvim-treesitter/queries/cpp/folds.scm new file mode 100644 index 00000000..f5f56648 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cpp/folds.scm @@ -0,0 +1,14 @@ +; inherits: c + +[ + (for_range_loop) + (class_specifier) + (field_declaration + type: (enum_specifier) + default_value: (initializer_list)) + (template_declaration) + (namespace_definition) + (try_statement) + (catch_clause) + (lambda_expression) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/cpp/highlights.scm b/config/neovim/store/nvim-treesitter/queries/cpp/highlights.scm new file mode 100644 index 00000000..1779d59e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cpp/highlights.scm @@ -0,0 +1,268 @@ +; inherits: c + +((identifier) @variable.member + (#lua-match? @variable.member "^m_.*$")) + +(parameter_declaration + declarator: (reference_declarator) @variable.parameter) + +; function(Foo ...foo) +(variadic_parameter_declaration + declarator: (variadic_declarator + (_) @variable.parameter)) + +; int foo = 0 +(optional_parameter_declaration + declarator: (_) @variable.parameter) + +;(field_expression) @variable.parameter ;; How to highlight this? +((field_expression + (field_identifier) @function.method) @_parent + (#has-parent? @_parent template_method function_declarator)) + +(field_declaration + (field_identifier) @variable.member) + +(field_initializer + (field_identifier) @property) + +(function_declarator + declarator: (field_identifier) @function.method) + +(concept_definition + name: (identifier) @type.definition) + +(alias_declaration + name: (type_identifier) @type.definition) + +(auto) @type.builtin + +(namespace_identifier) @module + +((namespace_identifier) @type + (#lua-match? @type "^[%u]")) + +(case_statement + value: (qualified_identifier + (identifier) @constant)) + +(using_declaration + . + "using" + . + "namespace" + . + [ + (qualified_identifier) + (identifier) + ] @module) + +(destructor_name + (identifier) @function.method) + +; functions +(function_declarator + (qualified_identifier + (identifier) @function)) + +(function_declarator + (qualified_identifier + (qualified_identifier + (identifier) @function))) + +(function_declarator + (qualified_identifier + (qualified_identifier + (qualified_identifier + (identifier) @function)))) + +((qualified_identifier + (qualified_identifier + (qualified_identifier + (qualified_identifier + (identifier) @function)))) @_parent + (#has-ancestor? @_parent function_declarator)) + +(function_declarator + (template_function + (identifier) @function)) + +(operator_name) @function + +"operator" @function + +"static_assert" @function.builtin + +(call_expression + (qualified_identifier + (identifier) @function.call)) + +(call_expression + (qualified_identifier + (qualified_identifier + (identifier) @function.call))) + +(call_expression + (qualified_identifier + (qualified_identifier + (qualified_identifier + (identifier) @function.call)))) + +((qualified_identifier + (qualified_identifier + (qualified_identifier + (qualified_identifier + (identifier) @function.call)))) @_parent + (#has-ancestor? @_parent call_expression)) + +(call_expression + (template_function + (identifier) @function.call)) + +(call_expression + (qualified_identifier + (template_function + (identifier) @function.call))) + +(call_expression + (qualified_identifier + (qualified_identifier + (template_function + (identifier) @function.call)))) + +(call_expression + (qualified_identifier + (qualified_identifier + (qualified_identifier + (template_function + (identifier) @function.call))))) + +((qualified_identifier + (qualified_identifier + (qualified_identifier + (qualified_identifier + (template_function + (identifier) @function.call))))) @_parent + (#has-ancestor? @_parent call_expression)) + +; methods +(function_declarator + (template_method + (field_identifier) @function.method)) + +(call_expression + (field_expression + (field_identifier) @function.method.call)) + +; constructors +((function_declarator + (qualified_identifier + (identifier) @constructor)) + (#lua-match? @constructor "^%u")) + +((call_expression + function: (identifier) @constructor) + (#lua-match? @constructor "^%u")) + +((call_expression + function: (qualified_identifier + name: (identifier) @constructor)) + (#lua-match? @constructor "^%u")) + +((call_expression + function: (field_expression + field: (field_identifier) @constructor)) + (#lua-match? @constructor "^%u")) + +; constructing a type in an initializer list: Constructor (): **SuperType (1)** +((field_initializer + (field_identifier) @constructor + (argument_list)) + (#lua-match? @constructor "^%u")) + +; Constants +(this) @variable.builtin + +(null + "nullptr" @constant.builtin) + +(true) @boolean + +(false) @boolean + +; Literals +(raw_string_literal) @string + +; Keywords +[ + "try" + "catch" + "noexcept" + "throw" +] @keyword.exception + +[ + "decltype" + "explicit" + "friend" + "override" + "using" + "requires" + "constexpr" +] @keyword + +[ + "class" + "namespace" + "template" + "typename" + "concept" +] @keyword.type + +[ + "co_await" + "co_yield" + "co_return" +] @keyword.coroutine + +[ + "public" + "private" + "protected" + "virtual" + "final" +] @keyword.modifier + +[ + "new" + "delete" + "xor" + "bitand" + "bitor" + "compl" + "not" + "xor_eq" + "and_eq" + "or_eq" + "not_eq" + "and" + "or" +] @keyword.operator + +"<=>" @operator + +"::" @punctuation.delimiter + +(template_argument_list + [ + "<" + ">" + ] @punctuation.bracket) + +(template_parameter_list + [ + "<" + ">" + ] @punctuation.bracket) + +(literal_suffix) @operator diff --git a/config/neovim/store/nvim-treesitter/queries/cpp/indents.scm b/config/neovim/store/nvim-treesitter/queries/cpp/indents.scm new file mode 100644 index 00000000..0782d226 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cpp/indents.scm @@ -0,0 +1,8 @@ +; inherits: c + +(condition_clause) @indent.begin + +((field_initializer_list) @indent.begin + (#set! indent.start_at_same_line 1)) + +(access_specifier) @indent.branch diff --git a/config/neovim/store/nvim-treesitter/queries/cpp/injections.scm b/config/neovim/store/nvim-treesitter/queries/cpp/injections.scm new file mode 100644 index 00000000..6e165722 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cpp/injections.scm @@ -0,0 +1,13 @@ +((preproc_arg) @injection.content + (#set! injection.language "cpp")) + +((comment) @injection.content + (#set! injection.language "comment")) + +((comment) @injection.content + (#lua-match? @injection.content "/[*\/][!*\/] +(type_parameter_declaration + (type_identifier) @local.definition.type) + +(template_declaration) @local.scope + +; Namespaces +(namespace_definition + name: (namespace_identifier) @local.definition.namespace + body: (_) @local.scope) + +(namespace_definition + name: (nested_namespace_specifier) @local.definition.namespace + body: (_) @local.scope) + +((namespace_identifier) @local.reference + (#set! reference.kind "namespace")) + +; Function definitions +(template_function + name: (identifier) @local.definition.function) @local.scope + +(template_method + name: (field_identifier) @local.definition.method) @local.scope + +(function_declarator + declarator: (qualified_identifier + name: (identifier) @local.definition.function)) @local.scope + +(field_declaration + declarator: (function_declarator + (field_identifier) @local.definition.method)) + +(lambda_expression) @local.scope + +; Control structures +(try_statement + body: (_) @local.scope) + +(catch_clause) @local.scope + +(requires_expression) @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/css/folds.scm b/config/neovim/store/nvim-treesitter/queries/css/folds.scm new file mode 100644 index 00000000..dc3c14df --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/css/folds.scm @@ -0,0 +1,4 @@ +[ + (rule_set) + (import_statement)+ +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/css/highlights.scm b/config/neovim/store/nvim-treesitter/queries/css/highlights.scm new file mode 100644 index 00000000..acc2b638 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/css/highlights.scm @@ -0,0 +1,107 @@ +[ + "@media" + "@charset" + "@namespace" + "@supports" + "@keyframes" + (at_keyword) +] @keyword.directive + +"@import" @keyword.import + +[ + (to) + (from) +] @keyword + +(comment) @comment @spell + +(tag_name) @tag + +(class_name) @type + +(id_name) @constant + +[ + (property_name) + (feature_name) +] @property + +[ + (nesting_selector) + (universal_selector) +] @character.special + +(function_name) @function + +[ + "~" + ">" + "+" + "-" + "*" + "/" + "=" + "^=" + "|=" + "~=" + "$=" + "*=" +] @operator + +[ + "and" + "or" + "not" + "only" +] @keyword.operator + +(important) @keyword.modifier + +(attribute_selector + (plain_value) @string) + +(pseudo_element_selector + "::" + (tag_name) @attribute) + +(pseudo_class_selector + (class_name) @attribute) + +(attribute_name) @tag.attribute + +(namespace_name) @module + +((property_name) @variable + (#lua-match? @variable "^[-][-]")) + +((plain_value) @variable + (#lua-match? @variable "^[-][-]")) + +[ + (string_value) + (color_value) + (unit) +] @string + +(integer_value) @number + +(float_value) @number.float + +[ + "#" + "," + "." + ":" + "::" + ";" +] @punctuation.delimiter + +[ + "{" + ")" + "(" + "}" + "[" + "]" +] @punctuation.bracket diff --git a/config/neovim/store/nvim-treesitter/queries/css/indents.scm b/config/neovim/store/nvim-treesitter/queries/css/indents.scm new file mode 100644 index 00000000..75e4a63a --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/css/indents.scm @@ -0,0 +1,11 @@ +[ + (block) + (declaration) +] @indent.begin + +(block + "}" @indent.branch) + +"}" @indent.dedent + +(comment) @indent.ignore diff --git a/config/neovim/store/nvim-treesitter/queries/css/injections.scm b/config/neovim/store/nvim-treesitter/queries/css/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/css/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/csv/highlights.scm b/config/neovim/store/nvim-treesitter/queries/csv/highlights.scm new file mode 100644 index 00000000..de2213aa --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/csv/highlights.scm @@ -0,0 +1,3 @@ +; inherits: tsv + +"," @punctuation.delimiter diff --git a/config/neovim/store/nvim-treesitter/queries/cuda/folds.scm b/config/neovim/store/nvim-treesitter/queries/cuda/folds.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cuda/folds.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/nvim-treesitter/queries/cuda/highlights.scm b/config/neovim/store/nvim-treesitter/queries/cuda/highlights.scm new file mode 100644 index 00000000..6605c5a7 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cuda/highlights.scm @@ -0,0 +1,17 @@ +; inherits: cpp + +[ + "<<<" + ">>>" +] @punctuation.bracket + +[ + "__host__" + "__device__" + "__global__" + "__managed__" + "__forceinline__" + "__noinline__" +] @keyword.modifier + +"__launch_bounds__" @keyword.modifier diff --git a/config/neovim/store/nvim-treesitter/queries/cuda/indents.scm b/config/neovim/store/nvim-treesitter/queries/cuda/indents.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cuda/indents.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/nvim-treesitter/queries/cuda/injections.scm b/config/neovim/store/nvim-treesitter/queries/cuda/injections.scm new file mode 100644 index 00000000..0259958c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cuda/injections.scm @@ -0,0 +1,5 @@ +((preproc_arg) @injection.content + (#set! injection.language "cuda")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/cuda/locals.scm b/config/neovim/store/nvim-treesitter/queries/cuda/locals.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cuda/locals.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/nvim-treesitter/queries/cue/folds.scm b/config/neovim/store/nvim-treesitter/queries/cue/folds.scm new file mode 100644 index 00000000..934b59e6 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cue/folds.scm @@ -0,0 +1,5 @@ +[ + (import_spec_list) + (field) + (string) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/cue/highlights.scm b/config/neovim/store/nvim-treesitter/queries/cue/highlights.scm new file mode 100644 index 00000000..27d4dadd --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cue/highlights.scm @@ -0,0 +1,164 @@ +; Includes +[ + "package" + "import" +] @keyword.import + +; Namespaces +(package_identifier) @module + +(import_spec + [ + "." + "_" + ] @punctuation.special) + +[ + (attr_path) + (package_path) +] @string.special.url ; In attributes + +; Attributes +(attribute) @attribute + +; Conditionals +"if" @keyword.conditional + +; Repeats +"for" @keyword.repeat + +(for_clause + "_" @punctuation.special) + +; Keywords +"let" @keyword + +"in" @keyword.operator + +; Operators +[ + "+" + "-" + "*" + "/" + "|" + "&" + "||" + "&&" + "==" + "!=" + "<" + "<=" + ">" + ">=" + "=~" + "!~" + "!" + "=" +] @operator + +; Fields & Properties +(field + (label + (identifier) @variable.member)) + +(selector_expression + (_) + (identifier) @property) + +; Functions +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (selector_expression + (_) + (identifier) @function.call)) + +(call_expression + function: (builtin_function) @function.call) + +(builtin_function) @function.builtin + +; Variables +(identifier) @variable + +; Types +(primitive_type) @type.builtin + +((identifier) @type + (#lua-match? @type "^_?#")) + +[ + (slice_type) + (pointer_type) +] @type ; In attributes + +; Punctuation +[ + "," + ":" +] @punctuation.delimiter + +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "<" + ">" +] @punctuation.bracket + +[ + (ellipsis) + "?" +] @punctuation.special + +; Literals +(string) @string + +[ + (escape_char) + (escape_unicode) +] @string.escape + +(number) @number + +(float) @number.float + +(si_unit + (float) + (_) @string.special.symbol) + +(boolean) @boolean + +[ + (null) + (top) + (bottom) +] @constant.builtin + +; Interpolations +(interpolation + "\\(" @punctuation.special + (_) + ")" @punctuation.special) @none + +(interpolation + "\\(" + (identifier) @variable + ")") + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/cue/indents.scm b/config/neovim/store/nvim-treesitter/queries/cue/indents.scm new file mode 100644 index 00000000..cef2345c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cue/indents.scm @@ -0,0 +1,30 @@ +[ + (import_spec_list) + (field) +] @indent.begin + +[ + "}" + "]" + ")" +] @indent.end + +[ + "{" + "}" +] @indent.branch + +[ + "[" + "]" +] @indent.branch + +[ + "(" + ")" +] @indent.branch + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/cue/injections.scm b/config/neovim/store/nvim-treesitter/queries/cue/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cue/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/cue/locals.scm b/config/neovim/store/nvim-treesitter/queries/cue/locals.scm new file mode 100644 index 00000000..b2a8972f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/cue/locals.scm @@ -0,0 +1,31 @@ +; Scopes +[ + (source_file) + (field) + (for_clause) +] @local.scope + +; References +(identifier) @local.reference + +; Definitions +(import_spec + path: (string) @local.definition.import) + +(field + (label + (identifier) @local.definition.field)) + +(package_identifier) @local.definition.namespace + +(for_clause + (identifier) @local.definition.var + (expression)) + +(for_clause + (identifier) + (identifier) @local.definition.var + (expression)) + +(let_clause + (identifier) @local.definition.var) diff --git a/config/neovim/store/nvim-treesitter/queries/d/folds.scm b/config/neovim/store/nvim-treesitter/queries/d/folds.scm new file mode 100644 index 00000000..49d6256f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/d/folds.scm @@ -0,0 +1,4 @@ +[ + (block_statement) + (aggregate_body) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/d/highlights.scm b/config/neovim/store/nvim-treesitter/queries/d/highlights.scm new file mode 100644 index 00000000..11d08a1b --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/d/highlights.scm @@ -0,0 +1,374 @@ +; Keywords +[ + (directive) + (shebang) +] @keyword.directive + +[ + (import) + (module) +] @keyword.import + +[ + (alias) + (asm) + (class) + (delegate) + (delete) + (enum) + (interface) + (invariant) + (mixin) + (pragma) + (struct) + (template) + (union) + (unittest) + (version) + (with) + (traits) + (vector) + (parameters_) + (default) + (goto) +] @keyword + +(function) @keyword.function + +(synchronized) @keyword.coroutine + +[ + (if) + (else) + (switch) + (case) + (break) +] @keyword.conditional + +[ + (do) + (for) + (foreach) + (foreach_reverse) + (while) + (continue) +] @keyword.repeat + +(return) @keyword.return + +[ + (abstract) + (deprecated) + (private) + (protected) + (public) + (package) + (immutable) + (final) + (const) + (override) + (static) +] @keyword.modifier + +[ + (assert) + (try) + (catch) + (finally) + (throw) + (nothrow) +] @keyword.exception + +[ + (cast) + (new) + (in) + (is) + (not_in) + (not_is) + (typeid) + (typeof) +] @keyword.operator + +[ + (lazy) + (align) + (extern) + (scope) + (ref) + (pure) + (export) + (shared) + (gshared) + (out) + (inout) +] @keyword.modifier + +(parameter_attribute + (return) @keyword.modifier) + +(parameter_attribute + (in) @keyword.modifier) + +(parameter_attribute + (out) @keyword.modifier) + +(debug) @keyword.debug + +; Operators +[ + "/=" + "/" + ".." + "&" + "&=" + "&&" + "|" + "|=" + "||" + "-" + "-=" + "--" + "+" + "+=" + "++" + "<" + "<=" + "<<" + "<<=" + ">" + ">=" + ">>=" + ">>>=" + ">>" + ">>>" + "!" + "!=" + "$" + "=" + "==" + "*" + "*=" + "%" + "%=" + "^" + "^=" + "^^" + "^^=" + "~" + "~=" + "@" +] @operator + +; Variables +(identifier) @variable + +[ + "exit" + "success" + "failure" + (this) + (super) +] @variable.builtin + +(linkage_attribute + "(" + _ @variable.builtin + ")") + +; Modules +(module_fqn + (identifier) @module) + +; Attributes +(at_attribute + (identifier) @attribute) + +; Constants +(enum_member + (identifier) @constant) + +(manifest_declarator + . + (identifier) @constant) + +; Members +(aggregate_body + (variable_declaration + (declarator + (identifier) @variable.member))) + +(property_expression + "." + (identifier) @variable.member) + +(type + "." + (identifier) @variable.member) + +; Types +(class_declaration + (class) + . + (identifier) @type) + +(struct_declaration + (struct) + . + (identifier) @type) + +(union_declaration + (union) + . + (identifier) @type) + +(enum_declaration + (enum) + . + (identifier) @type) + +(alias_declaration + (alias) + . + (identifier) @type) + +((identifier) @type + (#lua-match? @type "^[A-Z].*")) + +(type + . + (identifier) @type .) + +[ + (auto) + (void) + (bool) + (byte) + (ubyte) + (char) + (short) + (ushort) + (wchar) + (dchar) + (int) + (uint) + (long) + (ulong) + (real) + (double) + (float) + (cent) + (ucent) + (ireal) + (idouble) + (ifloat) + (creal) + (double) + (cfloat) +] @type.builtin + +; Functions +(function_declaration + (identifier) @function) + +(call_expression + (identifier) @function) + +(call_expression + (type + (identifier) @function .)) + +(call_expression + (property_expression + (call_expression) + (identifier) @function .)) + +; Parameters +(parameter + (_) + (identifier) @variable.parameter) + +(function_literal + "(" + (type + (identifier) @variable.parameter)) + +; Constructors +(constructor + (this) @constructor) + +(destructor + (this) @constructor) + +(postblit + . + (this) @constructor) + +; Punctuation +[ + ";" + "." + ":" + "," + "=>" +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +"..." @punctuation.special + +; Ternaries +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +; Labels +(label + (identifier) @label) + +(goto_statement + (identifier) @label) + +; Literals +(string_literal) @string + +[ + (int_literal) + (float_literal) +] @number + +(char_literal) @character + +[ + (true) + (false) +] @boolean + +[ + (null) + (special_keyword) +] @constant.builtin + +; Comments +(comment) @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^///[^/]")) + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^///$")) + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$")) + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^/[+][+][^+].*[+]/$")) diff --git a/config/neovim/store/nvim-treesitter/queries/d/indents.scm b/config/neovim/store/nvim-treesitter/queries/d/indents.scm new file mode 100644 index 00000000..c89b4e91 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/d/indents.scm @@ -0,0 +1,24 @@ +[ + (parameters) + (template_parameters) + (expression_statement) + (aggregate_body) + (function_body) + (scope_statement) + (block_statement) + (case_statement) +] @indent.begin + +(comment) @indent.auto + +[ + (case) + (default) + "}" + "]" +] @indent.branch + +[ + (directive) + (shebang) +] @indent.zero diff --git a/config/neovim/store/nvim-treesitter/queries/d/injections.scm b/config/neovim/store/nvim-treesitter/queries/d/injections.scm new file mode 100644 index 00000000..16334810 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/d/injections.scm @@ -0,0 +1,18 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((call_expression + (type) @_printf + (arguments + "(" + . + (expression + (string_literal) @injection.content))) + (#eq? @_printf "printf") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "printf")) + +; TODO: uncomment when asm is added +; ((asm_inline) @injection.content +; (#set! injection.language "asm") +; (#set! injection.combined)) diff --git a/config/neovim/store/nvim-treesitter/queries/d/locals.scm b/config/neovim/store/nvim-treesitter/queries/d/locals.scm new file mode 100644 index 00000000..1d7d617d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/d/locals.scm @@ -0,0 +1,79 @@ +; Scopes +[ + (source_file) + (block_statement) + (aggregate_body) +] @local.scope + +; References +(identifier) @local.reference + +; Definitions +(module_def + (module_declaration + (module_fqn) @local.definition.namespace) + (#set! "definition.namespace.scope" "global")) + +(enum_declaration + (enum_member + . + (identifier) @local.definition.enum)) + +(class_declaration + (class) + . + (identifier) @local.definition.type) + +(struct_declaration + (struct) + . + (identifier) @local.definition.type) + +(union_declaration + (union) + . + (identifier) @local.definition.type) + +(enum_declaration + (enum) + . + (identifier) @local.definition.type) + +(alias_declaration + (alias_initializer + . + (identifier) @local.definition.type)) + +(constructor + (this) @local.definition.method) + +(destructor + (this) @local.definition.method) + +(postblit + (this) @local.definition.method) + +(aggregate_body + (function_declaration + (identifier) @local.definition.method)) + +(manifest_declarator + . + (identifier) @local.definition.constant) + +(anonymous_enum_declaration + (enum_member + . + (identifier) @local.definition.constant)) + +(variable_declaration + (declarator + (identifier) @local.definition.var)) + +(aggregate_body + (variable_declaration + (declarator + (identifier) @local.definition.field))) + +(function_declaration + (identifier) @local.definition.function) diff --git a/config/neovim/store/nvim-treesitter/queries/dart/folds.scm b/config/neovim/store/nvim-treesitter/queries/dart/folds.scm new file mode 100644 index 00000000..8dc4e781 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dart/folds.scm @@ -0,0 +1,12 @@ +[ + (class_definition) + (enum_declaration) + (extension_declaration) + (arguments) + (function_body) + (block) + (switch_block) + (list_literal) + (set_or_map_literal) + (string_literal) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/dart/highlights.scm b/config/neovim/store/nvim-treesitter/queries/dart/highlights.scm new file mode 100644 index 00000000..142c6e19 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dart/highlights.scm @@ -0,0 +1,279 @@ +(dotted_identifier_list) @string + +; Methods +; -------------------- +(super) @function + +; TODO: add method/call_expression to grammar and +; distinguish method call from variable access +(function_expression_body + (identifier) @function) + +; ((identifier)(selector (argument_part)) @function) +; NOTE: This query is a bit of a work around for the fact that the dart grammar doesn't +; specifically identify a node as a function call +(((identifier) @function + (#lua-match? @function "^_?[%l]")) + . + (selector + . + (argument_part))) @function + +; Annotations +; -------------------- +(annotation + name: (identifier) @attribute) + +; Operators and Tokens +; -------------------- +(template_substitution + "$" @punctuation.special + "{" @punctuation.special + "}" @punctuation.special) @none + +(template_substitution + "$" @punctuation.special + (identifier_dollar_escaped) @variable) @none + +(escape_sequence) @string.escape + +[ + "@" + "=>" + ".." + "??" + "==" + "?" + ":" + "&&" + "%" + "<" + ">" + "=" + ">=" + "<=" + "||" + (multiplicative_operator) + (increment_operator) + (is_operator) + (prefix_operator) + (equality_operator) + (additive_operator) +] @operator + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +; Delimiters +; -------------------- +[ + ";" + "." + "," +] @punctuation.delimiter + +; Types +; -------------------- +(class_definition + name: (identifier) @type) + +(constructor_signature + name: (identifier) @type) + +(scoped_identifier + scope: (identifier) @type) + +(function_signature + name: (identifier) @function.method) + +(getter_signature + (identifier) @function.method) + +(setter_signature + name: (identifier) @function.method) + +(enum_declaration + name: (identifier) @type) + +(enum_constant + name: (identifier) @type) + +(void_type) @type + +((scoped_identifier + scope: (identifier) @type + name: (identifier) @type) + (#lua-match? @type "^[%u%l]")) + +(type_identifier) @type + +(type_alias + (type_identifier) @type.definition) + +; Variables +; -------------------- +; var keyword +(inferred_type) @keyword + +((identifier) @type + (#lua-match? @type "^_?[%u].*[%l]")) ; catch Classes or IClasses not CLASSES + +"Function" @type + +; properties +(unconditional_assignable_selector + (identifier) @property) + +(conditional_assignable_selector + (identifier) @property) + +; assignments +(assignment_expression + left: (assignable_expression) @variable) + +(this) @variable.builtin + +; Parameters +; -------------------- +(formal_parameter + name: (identifier) @variable.parameter) + +(named_argument + (label + (identifier) @variable.parameter)) + +; Literals +; -------------------- +[ + (hex_integer_literal) + (decimal_integer_literal) + (decimal_floating_point_literal) + ; TODO: inaccessible nodes + ; (octal_integer_literal) + ; (hex_floating_point_literal) +] @number + +(symbol_literal) @string.special.symbol + +(string_literal) @string + +(true) @boolean + +(false) @boolean + +(null_literal) @constant.builtin + +(comment) @comment @spell + +(documentation_comment) @comment.documentation @spell + +; Keywords +; -------------------- +[ + "import" + "library" + "export" + "as" + "show" + "hide" +] @keyword.import + +; Reserved words (cannot be used as identifiers) +[ + ; TODO: + ; "rethrow" cannot be targeted at all and seems to be an invisible node + ; TODO: + ; the assert keyword cannot be specifically targeted + ; because the grammar selects the whole node or the content + ; of the assertion not just the keyword + ; assert + (case_builtin) + "late" + "required" + "on" + "extends" + "in" + "is" + "new" + "super" + "with" +] @keyword + +[ + "class" + "enum" + "extension" +] @keyword.type + +"return" @keyword.return + +; Built in identifiers: +; alone these are marked as keywords +[ + "deferred" + "factory" + "get" + "implements" + "interface" + "library" + "operator" + "mixin" + "part" + "set" + "typedef" +] @keyword + +[ + "async" + "async*" + "sync*" + "await" + "yield" +] @keyword.coroutine + +[ + (const_builtin) + (final_builtin) + "abstract" + "covariant" + "dynamic" + "external" + "static" + "final" + "base" + "sealed" +] @keyword.modifier + +; when used as an identifier: +((identifier) @variable.builtin + (#any-of? @variable.builtin + "abstract" "as" "covariant" "deferred" "dynamic" "export" "external" "factory" "Function" "get" + "implements" "import" "interface" "library" "operator" "mixin" "part" "set" "static" "typedef")) + +[ + "if" + "else" + "switch" + "default" +] @keyword.conditional + +[ + "try" + "throw" + "catch" + "finally" + (break_statement) +] @keyword.exception + +[ + "do" + "while" + "continue" + "for" +] @keyword.repeat diff --git a/config/neovim/store/nvim-treesitter/queries/dart/indents.scm b/config/neovim/store/nvim-treesitter/queries/dart/indents.scm new file mode 100644 index 00000000..03d9464b --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dart/indents.scm @@ -0,0 +1,49 @@ +[ + (class_body) + (function_body) + (function_expression_body) + (declaration + (initializers)) + (switch_block) + (formal_parameter_list) + (formal_parameter) + (list_literal) + (return_statement) + (arguments) + (try_statement) +] @indent.begin + +(switch_block + (_) @indent.begin + (#set! indent.immediate 1) + (#set! indent.start_at_same_line 1)) + +[ + (switch_statement_case) + (switch_statement_default) +] @indent.branch + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @indent.branch + +"}" @indent.end + +(return_statement + ";" @indent.end) + +(break_statement + ";" @indent.end) + +(comment) @indent.ignore + +; dedenting the else block is painfully slow; replace with simpler strategy +; (if_statement) @indent.begin +; (if_statement +; (block) @indent.branch) +(if_statement) @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/dart/injections.scm b/config/neovim/store/nvim-treesitter/queries/dart/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dart/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/dart/locals.scm b/config/neovim/store/nvim-treesitter/queries/dart/locals.scm new file mode 100644 index 00000000..3e3beb58 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dart/locals.scm @@ -0,0 +1,32 @@ +; Definitions +(function_signature + name: (identifier) @local.definition.function) + +(formal_parameter + name: (identifier) @local.definition.parameter) + +(initialized_variable_definition + name: (identifier) @local.definition.var) + +(initialized_identifier + (identifier) @local.definition.var) + +(static_final_declaration + (identifier) @local.definition.var) + +; References +(identifier) @local.reference + +; Scopes +(class_definition + body: (_) @local.scope) + +[ + (block) + (if_statement) + (for_statement) + (while_statement) + (try_statement) + (catch_clause) + (finally_clause) +] @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/devicetree/folds.scm b/config/neovim/store/nvim-treesitter/queries/devicetree/folds.scm new file mode 100644 index 00000000..206c4bed --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/devicetree/folds.scm @@ -0,0 +1 @@ +(node) @fold diff --git a/config/neovim/store/nvim-treesitter/queries/devicetree/highlights.scm b/config/neovim/store/nvim-treesitter/queries/devicetree/highlights.scm new file mode 100644 index 00000000..74ec89af --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/devicetree/highlights.scm @@ -0,0 +1,61 @@ +(comment) @comment @spell + +[ + (preproc_include) + (dtsi_include) +] @keyword.import + +(preproc_def) @constant.macro + +(preproc_function_def) @function.macro + +[ + (memory_reservation) + (file_version) +] @attribute + +[ + (string_literal) + (byte_string_literal) + (system_lib_string) +] @string + +(integer_literal) @number + +(identifier) @variable + +(node + (identifier) @module) + +(property + (identifier) @property) + +(node + label: (_) @label) + +(call_expression + (identifier) @function.macro) + +(reference) @label ; referencing labeled_item.identifier + +(unit_address) @constant + +"=" @operator + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "<" + ">" +] @punctuation.bracket + +[ + ";" + ":" + "," + "@" +] @punctuation.delimiter diff --git a/config/neovim/store/nvim-treesitter/queries/devicetree/indents.scm b/config/neovim/store/nvim-treesitter/queries/devicetree/indents.scm new file mode 100644 index 00000000..9740060c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/devicetree/indents.scm @@ -0,0 +1,12 @@ +[ + (node) + (property) + (integer_cells) +] @indent.begin + +[ + "}" + ">" +] @indent.branch + +(comment) @indent.ignore diff --git a/config/neovim/store/nvim-treesitter/queries/devicetree/injections.scm b/config/neovim/store/nvim-treesitter/queries/devicetree/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/devicetree/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/devicetree/locals.scm b/config/neovim/store/nvim-treesitter/queries/devicetree/locals.scm new file mode 100644 index 00000000..e33a81df --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/devicetree/locals.scm @@ -0,0 +1,4 @@ +[ + (node) + (integer_cells) +] @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/dhall/folds.scm b/config/neovim/store/nvim-treesitter/queries/dhall/folds.scm new file mode 100644 index 00000000..bc92797b --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dhall/folds.scm @@ -0,0 +1,10 @@ +[ + (let_binding) + (application_expression) + (lambda_expression) + (record_type) + (union_type) + (list_literal) + (record_literal) + (block_comment) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/dhall/highlights.scm b/config/neovim/store/nvim-treesitter/queries/dhall/highlights.scm new file mode 100644 index 00000000..efd7fedf --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dhall/highlights.scm @@ -0,0 +1,193 @@ +; Text +; Imports +(missing_import) @keyword.import + +(local_import) @string.special.path + +(http_import) @string.special.url + +[ + (env_variable) + (import_hash) +] @string.special + +[ + (import_as_location) + (import_as_text) +] @type + +; Types +([ + (let_binding + (label) @type) + (union_type_entry + (label) @type) +] + (#lua-match? @type "^%u")) + +((primitive_expression + (identifier + (label) @type) + (selector + (label) @type)) @variable + (#lua-match? @variable "^[A-Z][^.]*$")) + +; Parameters +(lambda_expression + label: (label) @variable.parameter) + +; Variables +(label) @variable + +(identifier + [ + (label) @variable + (de_bruijn_index) @operator + ]) + +(let_binding + label: (label) @variable) + +; Fields +(record_literal_entry + (label) @variable.member) + +(record_type_entry + (label) @variable.member) + +(selector + (selector_dot) + (_) @variable.member) + +; Keywords +(env_import) @keyword + +[ + "let" + "in" + "assert" +] @keyword + +[ + "using" + "as" + "with" +] @keyword.operator + +; Operators +[ + (type_operator) + (assign_operator) + (lambda_operator) + (arrow_operator) + (infix_operator) + (completion_operator) + (assert_operator) + (forall_operator) + (empty_record_literal) +] @operator + +; Builtins +(builtin_function) @function.builtin + +(builtin + [ + "Natural" + "Natural/build" + "Natural/fold" + "Natural/isZero" + "Natural/even" + "Natural/odd" + "Natural/subtract" + "Natural/toInteger" + "Natural/show" + "Integer" + "Integer/toDouble" + "Integer/show" + "Integer/negate" + "Integer/clamp" + "Double" + "Double/show" + "List" + "List/build" + "List/fold" + "List/length" + "List/head" + "List/last" + "List/indexed" + "List/reverse" + "Text" + "Text/show" + "Text/replace" + "Optional" + "Date" + "Time" + "TimeZone" + "Type" + "Kind" + "Sort" + ] @type.builtin) + +; Punctuation +[ + "," + "|" +] @punctuation.delimiter + +(selector_dot) @punctuation.delimiter + +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "<" + ">" +] @punctuation.bracket + +; Conditionals +[ + "if" + "then" + "else" +] @keyword.conditional + +; Literals +(text_literal) @string + +(interpolation + "}" @string) + +[ + (double_quote_escaped) + (single_quote_escaped) +] @string.escape + +[ + (integer_literal) + (natural_literal) +] @number + +(double_literal) @number.float + +(boolean_literal) @boolean + +(builtin + "None") @constant.builtin + +; Comments +[ + (line_comment) + (block_comment) +] @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/dhall/injections.scm b/config/neovim/store/nvim-treesitter/queries/dhall/injections.scm new file mode 100644 index 00000000..3cd6aac8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dhall/injections.scm @@ -0,0 +1,5 @@ +([ + (line_comment) + (block_comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/diff/highlights.scm b/config/neovim/store/nvim-treesitter/queries/diff/highlights.scm new file mode 100644 index 00000000..44506c05 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/diff/highlights.scm @@ -0,0 +1,46 @@ +[ + (addition) + (new_file) +] @diff.plus + +[ + (deletion) + (old_file) +] @diff.minus + +(commit) @constant + +(location) @attribute + +(command + "diff" @function + (argument) @variable.parameter) + +(filename) @string.special.path + +(mode) @number + +[ + ".." + "+" + "++" + "+++" + "++++" + "-" + "--" + "---" + "----" +] @punctuation.special + +[ + (binary_change) + (similarity) + (file_change) +] @label + +(index + "index" @keyword) + +(similarity + (score) @number + "%" @number) diff --git a/config/neovim/store/nvim-treesitter/queries/disassembly/highlights.scm b/config/neovim/store/nvim-treesitter/queries/disassembly/highlights.scm new file mode 100644 index 00000000..b1ece9ab --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/disassembly/highlights.scm @@ -0,0 +1,30 @@ +(byte) @constant + +[ + (address) + (hexadecimal) + (integer) +] @number + +(identifier) @variable + +(bad_instruction) @comment.warning + +(code_location + (identifier) @function.call) + +(comment) @comment + +(instruction) @function + +(memory_dump) @string + +[ + "<" + ">" +] @punctuation.special + +[ + "+" + ":" +] @punctuation.delimiter diff --git a/config/neovim/store/nvim-treesitter/queries/disassembly/injections.scm b/config/neovim/store/nvim-treesitter/queries/disassembly/injections.scm new file mode 100644 index 00000000..9fb52daf --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/disassembly/injections.scm @@ -0,0 +1,6 @@ +; TODO: https://github.com/nvim-treesitter/nvim-treesitter/pull/5548#issuecomment-1773707396 +; +; To be added once a compatible Assembly parser is merged into nvim-treesitter +; +; ((instruction) @injection.content +; (#set! injection.language "asm")) diff --git a/config/neovim/store/nvim-treesitter/queries/djot/folds.scm b/config/neovim/store/nvim-treesitter/queries/djot/folds.scm new file mode 100644 index 00000000..94f3724e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/djot/folds.scm @@ -0,0 +1,7 @@ +[ + (section) + (code_block) + (raw_block) + (list) + (div) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/djot/highlights.scm b/config/neovim/store/nvim-treesitter/queries/djot/highlights.scm new file mode 100644 index 00000000..e788891e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/djot/highlights.scm @@ -0,0 +1,396 @@ +(heading1) @markup.heading.1 + +(heading2) @markup.heading.2 + +(heading3) @markup.heading.3 + +(heading4) @markup.heading.4 + +(heading5) @markup.heading.5 + +(heading6) @markup.heading.6 + +(thematic_break) @string.special + +[ + (div_marker_begin) + (div_marker_end) +] @punctuation.delimiter + +([ + (code_block) + (raw_block) + (frontmatter) +] @markup.raw.block + (#set! "priority" 90)) + +; Remove @markup.raw for code with a language spec +(code_block + . + (code_block_marker_begin) + (language) + (code) @none + (#set! "priority" 90)) + +[ + (code_block_marker_begin) + (code_block_marker_end) + (raw_block_marker_begin) + (raw_block_marker_end) +] @punctuation.delimiter + +(language) @attribute + +(inline_attribute + _ @conceal + (#set! conceal "")) + +((language_marker) @punctuation.delimiter + (#set! conceal "")) + +(block_quote) @markup.quote + +(block_quote_marker) @punctuation.special + +(table_header) @markup.heading + +(table_header + "|" @punctuation.special) + +(table_row + "|" @punctuation.special) + +(table_separator) @punctuation.special + +(table_caption + (marker) @punctuation.special) + +(table_caption) @markup.italic + +[ + (list_marker_dash) + (list_marker_plus) + (list_marker_star) + (list_marker_definition) + (list_marker_decimal_period) + (list_marker_decimal_paren) + (list_marker_decimal_parens) + (list_marker_lower_alpha_period) + (list_marker_lower_alpha_paren) + (list_marker_lower_alpha_parens) + (list_marker_upper_alpha_period) + (list_marker_upper_alpha_paren) + (list_marker_upper_alpha_parens) + (list_marker_lower_roman_period) + (list_marker_lower_roman_paren) + (list_marker_lower_roman_parens) + (list_marker_upper_roman_period) + (list_marker_upper_roman_paren) + (list_marker_upper_roman_parens) +] @markup.list + +(list_marker_task + (unchecked)) @markup.list.unchecked + +(list_marker_task + (checked)) @markup.list.checked + +; Colorize `x` in `[x]` +((checked) @constant.builtin + (#offset! @constant.builtin 0 1 0 -1)) + +[ + (ellipsis) + (en_dash) + (em_dash) + (quotation_marks) +] @string.special + +(list_item + (term) @type.definition) + +; Conceal { and } but leave " and ' +((quotation_marks) @string.special + (#any-of? @string.special "\"}" "'}") + (#offset! @string.special 0 1 0 0) + (#set! conceal "")) + +((quotation_marks) @string.special + (#any-of? @string.special "\\\"" "\\'" "{'" "{\"") + (#offset! @string.special 0 0 0 -1) + (#set! conceal "")) + +[ + (hard_line_break) + (backslash_escape) +] @string.escape + +; Only conceal \ but leave escaped character. +((backslash_escape) @string.escape + (#offset! @string.escape 0 0 0 -1) + (#set! conceal "")) + +(frontmatter_marker) @punctuation.delimiter + +(emphasis) @markup.italic + +(strong) @markup.strong + +(symbol) @string.special.symbol + +(insert) @markup.underline + +(delete) @markup.strikethrough + +[ + (highlighted) + (superscript) + (subscript) +] @string.special + +; We need to target tokens specifically because `{=` etc can exist as fallback symbols in +; regular text, which we don't want to highlight or conceal. +(highlighted + [ + "{=" + "=}" + ] @punctuation.delimiter + (#set! conceal "")) + +(insert + [ + "{+" + "+}" + ] @punctuation.delimiter + (#set! conceal "")) + +(delete + [ + "{-" + "-}" + ] @punctuation.delimiter + (#set! conceal "")) + +(superscript + [ + "^" + "{^" + "^}" + ] @punctuation.delimiter + (#set! conceal "")) + +(subscript + [ + "~" + "{~" + "~}" + ] @punctuation.delimiter + (#set! conceal "")) + +([ + (emphasis_begin) + (emphasis_end) + (strong_begin) + (strong_end) + (verbatim_marker_begin) + (verbatim_marker_end) + (math_marker) + (math_marker_begin) + (math_marker_end) + (raw_inline_attribute) + (raw_inline_marker_begin) + (raw_inline_marker_end) +] @punctuation.delimiter + (#set! conceal "")) + +((math) @markup.math + (#set! "priority" 90)) + +(verbatim) @markup.raw + +((raw_inline) @markup.raw + (#set! "priority" 90)) + +(comment + "%" @comment + (#set! conceal "")) + +(span + [ + "[" + "]" + ] @punctuation.bracket) + +(inline_attribute + [ + "{" + "}" + ] @punctuation.bracket) + +(block_attribute + [ + "{" + "}" + ] @punctuation.bracket) + +[ + (class) + (class_name) +] @type + +(identifier) @tag + +(key_value + "=" @operator) + +(key_value + (key) @property) + +(key_value + (value) @string) + +(link_text + [ + "[" + "]" + ] @punctuation.bracket + (#set! conceal "")) + +(autolink + [ + "<" + ">" + ] @punctuation.bracket + (#set! conceal "")) + +(inline_link + (inline_link_destination) @markup.link.url + (#set! conceal "")) + +(link_reference_definition + ":" @punctuation.special) + +(full_reference_link + (link_text) @markup.link) + +(full_reference_link + (link_label) @markup.link.label + (#set! conceal "")) + +(collapsed_reference_link + "[]" @punctuation.bracket + (#set! conceal "")) + +(full_reference_link + [ + "[" + "]" + ] @punctuation.bracket + (#set! conceal "")) + +(collapsed_reference_link + (link_text) @markup.link) + +(collapsed_reference_link + (link_text) @markup.link.label) + +(inline_link + (link_text) @markup.link) + +(full_reference_image + (link_label) @markup.link.label) + +(full_reference_image + [ + "![" + "[" + "]" + ] @punctuation.bracket) + +(collapsed_reference_image + [ + "![" + "]" + ] @punctuation.bracket) + +(inline_image + [ + "![" + "]" + ] @punctuation.bracket) + +(image_description) @markup.italic + +(image_description + [ + "[" + "]" + ] @punctuation.bracket) + +(link_reference_definition + [ + "[" + "]" + ] @punctuation.bracket) + +(link_reference_definition + (link_label) @markup.link.label) + +(inline_link_destination + [ + "(" + ")" + ] @punctuation.bracket) + +[ + (autolink) + (inline_link_destination) + (link_destination) + (link_reference_definition) +] @markup.link.url + +(footnote + (reference_label) @markup.link.label) + +(footnote_reference + (reference_label) @markup.link.label) + +[ + (footnote_marker_begin) + (footnote_marker_end) +] @punctuation.bracket + +(todo) @comment.todo + +(note) @comment.note + +(fixme) @comment.error + +[ + (paragraph) + (comment) + (table_cell) +] @spell + +[ + (autolink) + (inline_link_destination) + (link_destination) + (code_block) + (raw_block) + (math) + (raw_inline) + (verbatim) + (reference_label) + (class) + (class_name) + (identifier) + (key_value) + (frontmatter) +] @nospell + +(full_reference_link + (link_label) @nospell) + +(full_reference_image + (link_label) @nospell) diff --git a/config/neovim/store/nvim-treesitter/queries/djot/indents.scm b/config/neovim/store/nvim-treesitter/queries/djot/indents.scm new file mode 100644 index 00000000..3b1a56eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/djot/indents.scm @@ -0,0 +1,10 @@ +; The intention here is to rely on Neovims `autoindent` setting. +; This allows us to not indent after just a single list item +; so we can create narrow lists quickly, but indent blocks inside list items +; to the previous paragraph. +(list_item_content) @indent.auto + +(footnote_content) @indent.align + +((table_caption) @indent.begin + (#set! indent.immediate 1)) diff --git a/config/neovim/store/nvim-treesitter/queries/djot/injections.scm b/config/neovim/store/nvim-treesitter/queries/djot/injections.scm new file mode 100644 index 00000000..b590e084 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/djot/injections.scm @@ -0,0 +1,17 @@ +(code_block + (language) @injection.language + (code) @injection.content) + +(raw_block + (raw_block_info + (language) @injection.language) + (content) @injection.content) + +(raw_inline + (content) @injection.content + (raw_inline_attribute + (language) @injection.language)) + +(frontmatter + (language) @injection.language + (frontmatter_content) @injection.content) diff --git a/config/neovim/store/nvim-treesitter/queries/djot/locals.scm b/config/neovim/store/nvim-treesitter/queries/djot/locals.scm new file mode 100644 index 00000000..1ac27529 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/djot/locals.scm @@ -0,0 +1,17 @@ +(link_reference_definition + (link_label) @local.definition) + +(footnote + (reference_label) @local.definition) + +(collapsed_reference_link + (link_text) @local.reference) + +(full_reference_link + (link_label) @local.reference) + +(full_reference_image + (link_label) @local.reference) + +(footnote_reference + (reference_label) @local.reference) diff --git a/config/neovim/store/nvim-treesitter/queries/dockerfile/highlights.scm b/config/neovim/store/nvim-treesitter/queries/dockerfile/highlights.scm new file mode 100644 index 00000000..5a8f0c7c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dockerfile/highlights.scm @@ -0,0 +1,68 @@ +[ + "FROM" + "AS" + "RUN" + "CMD" + "LABEL" + "EXPOSE" + "ENV" + "ADD" + "COPY" + "ENTRYPOINT" + "VOLUME" + "USER" + "WORKDIR" + "ARG" + "ONBUILD" + "STOPSIGNAL" + "HEALTHCHECK" + "SHELL" + "MAINTAINER" + "CROSS_BUILD" +] @keyword + +[ + ":" + "@" +] @operator + +(comment) @comment @spell + +(image_spec + (image_tag + ":" @punctuation.special) + (image_digest + "@" @punctuation.special)) + +(double_quoted_string) @string + +[ + (heredoc_marker) + (heredoc_end) +] @label + +((heredoc_block + (heredoc_line) @string) + (#set! "priority" 90)) + +(expansion + [ + "$" + "{" + "}" + ] @punctuation.special) + +((variable) @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) + +(arg_instruction + . + (unquoted_string) @property) + +(env_instruction + (env_pair + . + (unquoted_string) @property)) + +(expose_instruction + (expose_port) @number) diff --git a/config/neovim/store/nvim-treesitter/queries/dockerfile/injections.scm b/config/neovim/store/nvim-treesitter/queries/dockerfile/injections.scm new file mode 100644 index 00000000..5d3bbffb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dockerfile/injections.scm @@ -0,0 +1,12 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((shell_command + (shell_fragment) @injection.content) + (#set! injection.language "bash") + (#set! injection.combined)) + +((run_instruction + (heredoc_block) @injection.content) + (#set! injection.language "bash") + (#set! injection.include-children)) diff --git a/config/neovim/store/nvim-treesitter/queries/dot/highlights.scm b/config/neovim/store/nvim-treesitter/queries/dot/highlights.scm new file mode 100644 index 00000000..75ad9227 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dot/highlights.scm @@ -0,0 +1,49 @@ +(identifier) @type + +[ + "strict" + "graph" + "digraph" + "subgraph" + "node" + "edge" +] @keyword + +(string_literal) @string + +(number_literal) @number + +[ + (edgeop) + (operator) +] @operator + +[ + "," + ";" +] @punctuation.delimiter + +[ + "{" + "}" + "[" + "]" + "<" + ">" +] @punctuation.bracket + +(subgraph + id: (id + (identifier) @module)) + +(attribute + name: (id + (identifier) @variable.member)) + +(attribute + value: (id + (identifier) @constant)) + +(comment) @comment @spell + +(preproc) @keyword.directive diff --git a/config/neovim/store/nvim-treesitter/queries/dot/indents.scm b/config/neovim/store/nvim-treesitter/queries/dot/indents.scm new file mode 100644 index 00000000..a951e551 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dot/indents.scm @@ -0,0 +1,9 @@ +[ + (block) + (attr_list) +] @indent.begin + +[ + "}" + "]" +] @indent.branch @indent.end diff --git a/config/neovim/store/nvim-treesitter/queries/dot/injections.scm b/config/neovim/store/nvim-treesitter/queries/dot/injections.scm new file mode 100644 index 00000000..4fe39a8b --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dot/injections.scm @@ -0,0 +1,5 @@ +((html_internal) @injection.content + (#set! injection.language "html")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/doxygen/highlights.scm b/config/neovim/store/nvim-treesitter/queries/doxygen/highlights.scm new file mode 100644 index 00000000..da39bdd8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/doxygen/highlights.scm @@ -0,0 +1,61 @@ +((tag_name) @keyword + (#set! "priority" 105)) + +[ + "@code" + "@endcode" +] @keyword + +(identifier) @variable + +((tag + (tag_name) @_param + (identifier) @variable.parameter) + (#any-of? @_param "@param" "\\param")) + +(function + (identifier) @function) + +(function_link) @function + +(emphasis) @markup.italic + +[ + "\\a" + "\\c" +] @tag + +(code_block_language) @label + +[ + "in" + "out" + "inout" +] @keyword.modifier + +"~" @operator + +[ + "" + "" +] @tag + +[ + "." + "," + "::" + (code_block_start) + (code_block_end) +] @punctuation.delimiter + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +(code_block_content) @none diff --git a/config/neovim/store/nvim-treesitter/queries/doxygen/indents.scm b/config/neovim/store/nvim-treesitter/queries/doxygen/indents.scm new file mode 100644 index 00000000..ef30f1e7 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/doxygen/indents.scm @@ -0,0 +1 @@ +(document) @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/doxygen/injections.scm b/config/neovim/store/nvim-treesitter/queries/doxygen/injections.scm new file mode 100644 index 00000000..994f535a --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/doxygen/injections.scm @@ -0,0 +1,15 @@ +((type) @injection.content + (#set! injection.parent)) + +([ + (function_link) + (code) +] @injection.content + (#set! injection.parent)) + +((link) @injection.content + (#set! injection.language "html")) + +(code_block + (code_block_language) @injection.language + (code_block_content) @injection.content) diff --git a/config/neovim/store/nvim-treesitter/queries/dtd/folds.scm b/config/neovim/store/nvim-treesitter/queries/dtd/folds.scm new file mode 100644 index 00000000..b1bce4ff --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dtd/folds.scm @@ -0,0 +1,4 @@ +[ + (conditionalSect) + (Comment) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/dtd/highlights.scm b/config/neovim/store/nvim-treesitter/queries/dtd/highlights.scm new file mode 100644 index 00000000..7bc7a7ed --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dtd/highlights.scm @@ -0,0 +1,153 @@ +; XML declaration +(XMLDecl + "xml" @keyword.directive) + +(XMLDecl + [ + "version" + "encoding" + ] @tag.attribute) + +(XMLDecl + (EncName) @string.special) + +(XMLDecl + (VersionNum) @number) + +; Processing instructions +(PI) @keyword.directive + +; Element declaration +(elementdecl + "ELEMENT" @keyword.directive.define + (Name) @tag) + +(contentspec + (_ + (Name) @tag.attribute)) + +"#PCDATA" @type.builtin + +[ + "EMPTY" + "ANY" +] @keyword.modifier + +[ + "*" + "?" + "+" +] @character.special + +; Entity declaration +(GEDecl + "ENTITY" @keyword.directive.define + (Name) @constant) + +(GEDecl + (EntityValue) @string) + +(NDataDecl + "NDATA" @keyword + (Name) @label) + +; Parsed entity declaration +(PEDecl + "ENTITY" @keyword.directive.define + "%" @operator + (Name) @function.macro) + +(PEDecl + (EntityValue) @string) + +; Notation declaration +(NotationDecl + "NOTATION" @keyword.directive + (Name) @label) + +(NotationDecl + (ExternalID + (SystemLiteral + (URI) @string.special.url)) + (#set! "priority" 105)) + +; Attlist declaration +(AttlistDecl + "ATTLIST" @keyword.directive.define + (Name) @tag) + +(AttDef + (Name) @tag.attribute) + +(AttDef + (Enumeration + (Nmtoken) @string)) + +[ + (StringType) + (TokenizedType) +] @type.builtin + +(NotationType + "NOTATION" @type.builtin) + +[ + "#REQUIRED" + "#IMPLIED" + "#FIXED" +] @attribute + +; Entities +(EntityRef) @constant + +((EntityRef) @constant.builtin + (#any-of? @constant.builtin "&" "<" ">" """ "'")) + +(CharRef) @character + +(PEReference) @function.macro + +; External references +[ + "PUBLIC" + "SYSTEM" +] @keyword + +(PubidLiteral) @string.special + +(SystemLiteral + (URI) @string.special.url) + +; Delimiters & punctuation +[ + "" + "" + "]]>" +] @tag.delimiter + +[ + "(" + ")" + "[" +] @punctuation.bracket + +[ + "\"" + "'" +] @punctuation.delimiter + +[ + "," + "|" + "=" +] @operator + +; Misc +[ + "INCLUDE" + "IGNORE" +] @keyword.import + +(Comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/dtd/injections.scm b/config/neovim/store/nvim-treesitter/queries/dtd/injections.scm new file mode 100644 index 00000000..ed5557a0 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dtd/injections.scm @@ -0,0 +1 @@ +(Comment) @comment diff --git a/config/neovim/store/nvim-treesitter/queries/dtd/locals.scm b/config/neovim/store/nvim-treesitter/queries/dtd/locals.scm new file mode 100644 index 00000000..88246c04 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/dtd/locals.scm @@ -0,0 +1,11 @@ +(elementdecl + (Name) @local.definition.type) + +(elementdecl + (contentspec + (children + (Name) @local.reference))) + +(AttlistDecl + . + (Name) @local.reference) diff --git a/config/neovim/store/nvim-treesitter/queries/earthfile/highlights.scm b/config/neovim/store/nvim-treesitter/queries/earthfile/highlights.scm new file mode 100644 index 00000000..cc7dce29 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/earthfile/highlights.scm @@ -0,0 +1,129 @@ +(string_array + "," @punctuation.delimiter) + +(string_array + [ + "[" + "]" + ] @punctuation.bracket) + +[ + "ARG" + "AS LOCAL" + "BUILD" + "CACHE" + "CMD" + "COPY" + "DO" + "ENTRYPOINT" + "ENV" + "EXPOSE" + "FROM DOCKERFILE" + "FROM" + "FUNCTION" + "GIT CLONE" + "HOST" + "IMPORT" + "LABEL" + "LET" + "PROJECT" + "RUN" + "SAVE ARTIFACT" + "SAVE IMAGE" + "SET" + "USER" + "VERSION" + "VOLUME" + "WORKDIR" +] @keyword + +(for_command + [ + "FOR" + "IN" + "END" + ] @keyword.repeat) + +(if_command + [ + "IF" + "END" + ] @keyword.conditional) + +(elif_block + "ELSE IF" @keyword.conditional) + +(else_block + "ELSE" @keyword.conditional) + +(import_command + [ + "IMPORT" + "AS" + ] @keyword.import) + +(try_command + [ + "TRY" + "FINALLY" + "END" + ] @keyword.exception) + +(wait_command + [ + "WAIT" + "END" + ] @keyword) + +(with_docker_command + [ + "WITH DOCKER" + "END" + ] @keyword) + +[ + (comment) + (line_continuation_comment) +] @comment @spell + +[ + (target_ref) + (target_artifact) + (function_ref) +] @function + +(target + (identifier) @function) + +[ + (double_quoted_string) + (single_quoted_string) +] @string + +(unquoted_string) @string.special + +(escape_sequence) @string.escape + +(variable) @variable + +(expansion + [ + "$" + "{" + "}" + "(" + ")" + ] @punctuation.special) + +(build_arg + [ + "--" + (variable) + ] @variable.parameter) + +(options + (_) @property) + +"=" @operator + +(line_continuation) @operator diff --git a/config/neovim/store/nvim-treesitter/queries/earthfile/injections.scm b/config/neovim/store/nvim-treesitter/queries/earthfile/injections.scm new file mode 100644 index 00000000..7435a400 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/earthfile/injections.scm @@ -0,0 +1,9 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((line_continuation_comment) @injection.content + (#set! injection.language "comment")) + +((shell_fragment) @injection.content + (#set! injection.language "bash") + (#set! injection.include-children)) diff --git a/config/neovim/store/nvim-treesitter/queries/ebnf/highlights.scm b/config/neovim/store/nvim-treesitter/queries/ebnf/highlights.scm new file mode 100644 index 00000000..4254d04d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/ebnf/highlights.scm @@ -0,0 +1,42 @@ +; Simple tokens ;;;; +(terminal) @string + +(special_sequence) @string.special + +(integer) @number + +(comment) @comment @spell + +; Identifiers ;;;; +; Allow different highlighting for specific casings +((identifier) @type + (#lua-match? @type "^%u")) + +((identifier) @string.special.symbol + (#lua-match? @string.special.symbol "^%l")) + +((identifier) @constant + (#lua-match? @constant "^%u[%u%d_]+$")) + +; Punctuation ;;;; +[ + ";" + "," +] @punctuation.delimiter + +[ + "|" + "*" + "-" +] @operator + +"=" @keyword.operator + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket diff --git a/config/neovim/store/nvim-treesitter/queries/ecma/folds.scm b/config/neovim/store/nvim-treesitter/queries/ecma/folds.scm new file mode 100644 index 00000000..a348f344 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/ecma/folds.scm @@ -0,0 +1,24 @@ +[ + (arguments) + (for_in_statement) + (for_statement) + (while_statement) + (arrow_function) + (function_expression) + (function_declaration) + (class_declaration) + (method_definition) + (do_statement) + (with_statement) + (switch_statement) + (switch_case) + (switch_default) + (import_statement)+ + (if_statement) + (try_statement) + (catch_clause) + (array) + (object) + (generator_function) + (generator_function_declaration) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/ecma/highlights.scm b/config/neovim/store/nvim-treesitter/queries/ecma/highlights.scm new file mode 100644 index 00000000..f6040627 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/ecma/highlights.scm @@ -0,0 +1,376 @@ +; Types +; Javascript +; Variables +;----------- +(identifier) @variable + +; Properties +;----------- +(property_identifier) @variable.member + +(shorthand_property_identifier) @variable.member + +(private_property_identifier) @variable.member + +(object_pattern + (shorthand_property_identifier_pattern) @variable) + +(object_pattern + (object_assignment_pattern + (shorthand_property_identifier_pattern) @variable)) + +; Special identifiers +;-------------------- +((identifier) @type + (#lua-match? @type "^[A-Z]")) + +((identifier) @constant + (#lua-match? @constant "^_*[A-Z][A-Z%d_]*$")) + +((shorthand_property_identifier) @constant + (#lua-match? @constant "^_*[A-Z][A-Z%d_]*$")) + +((identifier) @variable.builtin + (#any-of? @variable.builtin "arguments" "module" "console" "window" "document")) + +((identifier) @type.builtin + (#any-of? @type.builtin + "Object" "Function" "Boolean" "Symbol" "Number" "Math" "Date" "String" "RegExp" "Map" "Set" + "WeakMap" "WeakSet" "Promise" "Array" "Int8Array" "Uint8Array" "Uint8ClampedArray" "Int16Array" + "Uint16Array" "Int32Array" "Uint32Array" "Float32Array" "Float64Array" "ArrayBuffer" "DataView" + "Error" "EvalError" "InternalError" "RangeError" "ReferenceError" "SyntaxError" "TypeError" + "URIError")) + +(statement_identifier) @label + +(glimmer_opening_tag) @tag.builtin + +(glimmer_closing_tag) @tag.builtin + +; Function and method definitions +;-------------------------------- +(function_expression + name: (identifier) @function) + +(function_declaration + name: (identifier) @function) + +(generator_function + name: (identifier) @function) + +(generator_function_declaration + name: (identifier) @function) + +(method_definition + name: [ + (property_identifier) + (private_property_identifier) + ] @function.method) + +(method_definition + name: (property_identifier) @constructor + (#eq? @constructor "constructor")) + +(pair + key: (property_identifier) @function.method + value: (function_expression)) + +(pair + key: (property_identifier) @function.method + value: (arrow_function)) + +(assignment_expression + left: (member_expression + property: (property_identifier) @function.method) + right: (arrow_function)) + +(assignment_expression + left: (member_expression + property: (property_identifier) @function.method) + right: (function_expression)) + +(variable_declarator + name: (identifier) @function + value: (arrow_function)) + +(variable_declarator + name: (identifier) @function + value: (function_expression)) + +(assignment_expression + left: (identifier) @function + right: (arrow_function)) + +(assignment_expression + left: (identifier) @function + right: (function_expression)) + +; Function and method calls +;-------------------------- +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (member_expression + property: [ + (property_identifier) + (private_property_identifier) + ] @function.method.call)) + +; Builtins +;--------- +((identifier) @module.builtin + (#eq? @module.builtin "Intl")) + +((identifier) @function.builtin + (#any-of? @function.builtin + "eval" "isFinite" "isNaN" "parseFloat" "parseInt" "decodeURI" "decodeURIComponent" "encodeURI" + "encodeURIComponent" "require")) + +; Constructor +;------------ +(new_expression + constructor: (identifier) @constructor) + +; Variables +;---------- +(namespace_import + (identifier) @module) + +; Decorators +;---------- +(decorator + "@" @attribute + (identifier) @attribute) + +(decorator + "@" @attribute + (call_expression + (identifier) @attribute)) + +(decorator + "@" @attribute + (member_expression + (property_identifier) @attribute)) + +(decorator + "@" @attribute + (call_expression + (member_expression + (property_identifier) @attribute))) + +; Literals +;--------- +[ + (this) + (super) +] @variable.builtin + +((identifier) @variable.builtin + (#eq? @variable.builtin "self")) + +[ + (true) + (false) +] @boolean + +[ + (null) + (undefined) +] @constant.builtin + +[ + (comment) + (html_comment) +] @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$")) + +(hash_bang_line) @keyword.directive + +((string_fragment) @keyword.directive + (#eq? @keyword.directive "use strict")) + +(string) @string + +(template_string) @string + +(escape_sequence) @string.escape + +(regex_pattern) @string.regexp + +(regex_flags) @character.special + +(regex + "/" @punctuation.bracket) ; Regex delimiters + +(number) @number + +((identifier) @number + (#any-of? @number "NaN" "Infinity")) + +; Punctuation +;------------ +[ + ";" + "." + "," + ":" +] @punctuation.delimiter + +[ + "--" + "-" + "-=" + "&&" + "+" + "++" + "+=" + "&=" + "/=" + "**=" + "<<=" + "<" + "<=" + "<<" + "=" + "==" + "===" + "!=" + "!==" + "=>" + ">" + ">=" + ">>" + "||" + "%" + "%=" + "*" + "**" + ">>>" + "&" + "|" + "^" + "??" + "*=" + ">>=" + ">>>=" + "^=" + "|=" + "&&=" + "||=" + "??=" + "..." +] @operator + +(binary_expression + "/" @operator) + +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +(unary_expression + [ + "!" + "~" + "-" + "+" + ] @operator) + +(unary_expression + [ + "delete" + "void" + ] @keyword.operator) + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(template_substitution + [ + "${" + "}" + ] @punctuation.special) @none + +; Keywords +;---------- +[ + "if" + "else" + "switch" + "case" +] @keyword.conditional + +[ + "import" + "from" + "as" + "export" +] @keyword.import + +[ + "for" + "of" + "do" + "while" + "continue" +] @keyword.repeat + +[ + "break" + "const" + "debugger" + "extends" + "get" + "let" + "set" + "static" + "target" + "var" + "with" +] @keyword + +"class" @keyword.type + +[ + "async" + "await" +] @keyword.coroutine + +[ + "return" + "yield" +] @keyword.return + +"function" @keyword.function + +[ + "new" + "delete" + "in" + "instanceof" + "typeof" +] @keyword.operator + +[ + "throw" + "try" + "catch" + "finally" +] @keyword.exception + +(export_statement + "default" @keyword) + +(switch_default + "default" @keyword.conditional) diff --git a/config/neovim/store/nvim-treesitter/queries/ecma/indents.scm b/config/neovim/store/nvim-treesitter/queries/ecma/indents.scm new file mode 100644 index 00000000..03b10c60 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/ecma/indents.scm @@ -0,0 +1,77 @@ +[ + (arguments) + (array) + (binary_expression) + (class_body) + (export_clause) + (formal_parameters) + (named_imports) + (object) + (object_pattern) + (parenthesized_expression) + (return_statement) + (statement_block) + (switch_case) + (switch_default) + (switch_statement) + (template_substitution) + (ternary_expression) +] @indent.begin + +(arguments + (call_expression) @indent.begin) + +(binary_expression + (call_expression) @indent.begin) + +(expression_statement + (call_expression) @indent.begin) + +(arrow_function + body: (_) @_body + (#not-has-type? @_body statement_block)) @indent.begin + +(assignment_expression + right: (_) @_right + (#not-has-type? @_right arrow_function function)) @indent.begin + +(variable_declarator + value: (_) @_value + (#not-has-type? @_value arrow_function call_expression function)) @indent.begin + +(arguments + ")" @indent.end) + +(object + "}" @indent.end) + +(statement_block + "}" @indent.end) + +[ + (arguments + (object)) + ")" + "}" + "]" +] @indent.branch + +(statement_block + "{" @indent.branch) + +(parenthesized_expression + ("(" + (_) + ")" @indent.end)) + +[ + "}" + "]" +] @indent.end + +(template_string) @indent.ignore + +[ + (comment) + (ERROR) +] @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/ecma/injections.scm b/config/neovim/store/nvim-treesitter/queries/ecma/injections.scm new file mode 100644 index 00000000..85e0383c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/ecma/injections.scm @@ -0,0 +1,216 @@ +(((comment) @_jsdoc_comment + (#lua-match? @_jsdoc_comment "^/[*][*][^*].*[*]/$")) @injection.content + (#set! injection.language "jsdoc")) + +((comment) @injection.content + (#set! injection.language "comment")) + +; html(`...`), html`...`, sql(`...`), etc. +(call_expression + function: [ + (await_expression + (identifier) @injection.language) + (identifier) @injection.language + ] + arguments: [ + (arguments + (template_string) @injection.content) + (template_string) @injection.content + ] + (#lua-match? @injection.language "^[a-zA-Z][a-zA-Z0-9]*$") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + ; Languages excluded from auto-injection due to special rules + ; - svg uses the html parser + ; - css uses the styled parser + (#not-any-of? @injection.language "svg" "css")) + +; svg`...` or svg(`...`) +(call_expression + function: [ + (await_expression + (identifier) @_name + (#eq? @_name "svg")) + ((identifier) @_name + (#eq? @_name "svg")) + ] + arguments: [ + (arguments + (template_string) @injection.content) + (template_string) @injection.content + ] + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "html")) + +(call_expression + function: [ + (await_expression + (identifier) @_name + (#eq? @_name "gql")) + ((identifier) @_name + (#eq? @_name "gql")) + ] + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "graphql"))) + +(call_expression + function: [ + (await_expression + (identifier) @_name + (#eq? @_name "hbs")) + ((identifier) @_name + (#eq? @_name "hbs")) + ] + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "glimmer"))) + +((glimmer_template) @injection.content + (#set! injection.language "glimmer")) + +; css``, keyframes`` +(call_expression + function: [ + (await_expression + (identifier) @_name + (#any-of? @_name "css" "keyframes")) + ((identifier) @_name + (#any-of? @_name "css" "keyframes")) + ] + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "styled"))) + +; styled.div`` +(call_expression + function: (member_expression + object: (identifier) @_name + (#eq? @_name "styled")) + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "styled"))) + +; styled(Component)`` +(call_expression + function: (call_expression + function: (identifier) @_name + (#eq? @_name "styled")) + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "styled"))) + +; styled.div.attrs({ prop: "foo" })`` +(call_expression + function: (call_expression + function: (member_expression + object: (member_expression + object: (identifier) @_name + (#eq? @_name "styled")))) + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "styled"))) + +; styled(Component).attrs({ prop: "foo" })`` +(call_expression + function: (call_expression + function: (member_expression + object: (call_expression + function: (identifier) @_name + (#eq? @_name "styled")))) + arguments: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "styled"))) + +((regex_pattern) @injection.content + (#set! injection.language "regex")) + +; ((comment) @_gql_comment +; (#eq? @_gql_comment "/* GraphQL */") +; (template_string) @injection.content +; (#set! injection.language "graphql")) +((template_string) @injection.content + (#lua-match? @injection.content "^`#graphql") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "graphql")) + +; el.innerHTML = `` +(assignment_expression + left: (member_expression + property: (property_identifier) @_prop + (#any-of? @_prop "outerHTML" "innerHTML")) + right: (template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "html")) + +; el.innerHTML = '' +(assignment_expression + left: (member_expression + property: (property_identifier) @_prop + (#any-of? @_prop "outerHTML" "innerHTML")) + right: (string + (string_fragment) @injection.content) + (#set! injection.language "html")) + +;---- Angular injections ----- +; @Component({ +; template: `` +; }) +(decorator + (call_expression + function: ((identifier) @_name + (#eq? @_name "Component")) + arguments: (arguments + (object + (pair + key: ((property_identifier) @_prop + (#eq? @_prop "template")) + value: ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "angular"))))))) + +; @Component({ +; styles: [``] +; }) +(decorator + (call_expression + function: ((identifier) @_name + (#eq? @_name "Component")) + arguments: (arguments + (object + (pair + key: ((property_identifier) @_prop + (#eq? @_prop "styles")) + value: (array + ((template_string) @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.include-children) + (#set! injection.language "css")))))))) + +; @Component({ +; styles: `` +; }) +(decorator + (call_expression + function: ((identifier) @_name + (#eq? @_name "Component")) + arguments: (arguments + (object + (pair + key: ((property_identifier) @_prop + (#eq? @_prop "styles")) + value: ((template_string) @injection.content + (#set! injection.include-children) + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "css"))))))) diff --git a/config/neovim/store/nvim-treesitter/queries/ecma/locals.scm b/config/neovim/store/nvim-treesitter/queries/ecma/locals.scm new file mode 100644 index 00000000..24ea7c0a --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/ecma/locals.scm @@ -0,0 +1,42 @@ +; Scopes +;------- +(statement_block) @local.scope + +(function_expression) @local.scope + +(arrow_function) @local.scope + +(function_declaration) @local.scope + +(method_definition) @local.scope + +(for_statement) @local.scope + +(for_in_statement) @local.scope + +(catch_clause) @local.scope + +; Definitions +;------------ +(variable_declarator + name: (identifier) @local.definition.var) + +(import_specifier + (identifier) @local.definition.import) + +(namespace_import + (identifier) @local.definition.import) + +(function_declaration + (identifier) @local.definition.function + (#set! definition.var.scope parent)) + +(method_definition + (property_identifier) @local.definition.function + (#set! definition.var.scope parent)) + +; References +;------------ +(identifier) @local.reference + +(shorthand_property_identifier) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/eds/folds.scm b/config/neovim/store/nvim-treesitter/queries/eds/folds.scm new file mode 100644 index 00000000..911798f5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/eds/folds.scm @@ -0,0 +1 @@ +(section) @fold diff --git a/config/neovim/store/nvim-treesitter/queries/eds/highlights.scm b/config/neovim/store/nvim-treesitter/queries/eds/highlights.scm new file mode 100644 index 00000000..0e008c97 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/eds/highlights.scm @@ -0,0 +1,45 @@ +"=" @punctuation.delimiter + +[ + "[" + "]" +] @punctuation.bracket + +((section_name) @variable.builtin + (#match? @variable.builtin + "\\c^(FileInfo|DeviceInfo|DummyUsage|MandatoryObjects|OptionalObjects)$")) + +((section_name) @variable.builtin + (#lua-match? @variable.builtin "^1")) + +(section + (section_name) @_name + (#match? @_name "\\c^Comments$")) @comment + +(section + (section_name) @_name + (statement + (key) @_key) @string + (#match? @_key "\\c^ParameterName$") + (#not-match? @_name "\\c^Comments$")) + +(section + (section_name) @_name + (statement + (key) @_key) @type + (#match? @_key "\\c^(ObjectType|DataType|AccessType)$") + (#not-match? @_name "\\c^Comments$")) + +(section + (section_name) @_name + (statement + (key) @_key) @attribute + (#match? @_key "\\c^PDOMapping$") + (#not-match? @_name "\\c^Comments$")) + +(section + (section_name) @_name + (statement + (key) @_key) @number + (#match? @_key "\\c^(DefaultValue|LowLimit|HighLimit|SubNumber)$") + (#not-match? @_name "\\c^Comments$")) diff --git a/config/neovim/store/nvim-treesitter/queries/eex/highlights.scm b/config/neovim/store/nvim-treesitter/queries/eex/highlights.scm new file mode 100644 index 00000000..d032a748 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/eex/highlights.scm @@ -0,0 +1,12 @@ +[ + "%>" + "--%>" + "<%!--" + "<%" + "<%#" + "<%%=" + "<%=" +] @tag.delimiter + +; EEx comments are highlighted as such +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/eex/injections.scm b/config/neovim/store/nvim-treesitter/queries/eex/injections.scm new file mode 100644 index 00000000..f13d3c14 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/eex/injections.scm @@ -0,0 +1,8 @@ +; EEx expressions are Elixir +((expression) @injection.content + (#set! injection.language "elixir")) + +; EEx expressions can span multiple interpolated lines +((partial_expression) @injection.content + (#set! injection.language "elixir") + (#set! injection.combined)) diff --git a/config/neovim/store/nvim-treesitter/queries/elixir/folds.scm b/config/neovim/store/nvim-treesitter/queries/elixir/folds.scm new file mode 100644 index 00000000..7abfe679 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elixir/folds.scm @@ -0,0 +1,10 @@ +[ + (anonymous_function) + (stab_clause) + (arguments) + (block) + (do_block) + (list) + (map) + (tuple) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/elixir/highlights.scm b/config/neovim/store/nvim-treesitter/queries/elixir/highlights.scm new file mode 100644 index 00000000..cbdb40de --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elixir/highlights.scm @@ -0,0 +1,217 @@ +; Punctuation +[ + "," + ";" +] @punctuation.delimiter + +[ + "(" + ")" + "<<" + ">>" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +"%" @punctuation.special + +; Identifiers +(identifier) @variable + +; Unused Identifiers +((identifier) @comment + (#lua-match? @comment "^_")) + +; Comments +(comment) @comment @spell + +; Strings +(string) @string + +; Modules +(alias) @module + +; Atoms & Keywords +[ + (atom) + (quoted_atom) + (keyword) + (quoted_keyword) +] @string.special.symbol + +; Interpolation +(interpolation + [ + "#{" + "}" + ] @string.special) + +; Escape sequences +(escape_sequence) @string.escape + +; Integers +(integer) @number + +; Floats +(float) @number.float + +; Characters +[ + (char) + (charlist) +] @character + +; Booleans +(boolean) @boolean + +; Nil +(nil) @constant.builtin + +; Operators +(operator_identifier) @operator + +(unary_operator + operator: _ @operator) + +(binary_operator + operator: _ @operator) + +; Pipe Operator +(binary_operator + operator: "|>" + right: (identifier) @function) + +(dot + operator: _ @operator) + +(stab_clause + operator: _ @operator) + +; Local Function Calls +(call + target: (identifier) @function.call) + +; Remote Function Calls +(call + target: (dot + left: [ + (atom) @type + (_) + ] + right: (identifier) @function.call) + (arguments)) + +; Definition Function Calls +(call + target: ((identifier) @keyword.function + (#any-of? @keyword.function + "def" "defdelegate" "defexception" "defguard" "defguardp" "defimpl" "defmacro" "defmacrop" + "defmodule" "defn" "defnp" "defoverridable" "defp" "defprotocol" "defstruct")) + (arguments + [ + (call + (identifier) @function) + (identifier) @function + (binary_operator + left: (call + target: (identifier) @function) + operator: "when") + ])?) + +; Kernel Keywords & Special Forms +(call + target: ((identifier) @keyword + (#any-of? @keyword + "alias" "case" "catch" "cond" "else" "for" "if" "import" "quote" "raise" "receive" "require" + "reraise" "super" "throw" "try" "unless" "unquote" "unquote_splicing" "use" "with"))) + +; Special Constants +((identifier) @constant.builtin + (#any-of? @constant.builtin "__CALLER__" "__DIR__" "__ENV__" "__MODULE__" "__STACKTRACE__")) + +; Reserved Keywords +[ + "after" + "catch" + "do" + "end" + "fn" + "rescue" + "when" + "else" +] @keyword + +; Operator Keywords +[ + "and" + "in" + "not in" + "not" + "or" +] @keyword.operator + +; Capture Operator +(unary_operator + operator: "&" + operand: [ + (integer) @operator + (binary_operator + left: [ + (call + target: (dot + left: (_) + right: (identifier) @function)) + (identifier) @function + ] + operator: "/" + right: (integer) @operator) + ]) + +; Non-String Sigils +(sigil + "~" @string.special + (sigil_name) @string.special @_sigil_name + quoted_start: _ @string.special + quoted_end: _ @string.special + ((sigil_modifiers) @string.special)? + (#not-any-of? @_sigil_name "s" "S")) + +; String Sigils +(sigil + "~" @string + (sigil_name) @string @_sigil_name + quoted_start: _ @string + (quoted_content) @string + quoted_end: _ @string + ((sigil_modifiers) @string)? + (#any-of? @_sigil_name "s" "S")) + +; Module attributes +(unary_operator + operator: "@" + operand: [ + (identifier) + (call + target: (identifier)) + ] @constant) @constant + +; Documentation +(unary_operator + operator: "@" + operand: (call + target: ((identifier) @_identifier + (#any-of? @_identifier "moduledoc" "typedoc" "shortdoc" "doc")) @comment.documentation + (arguments + [ + (string) + (boolean) + (charlist) + (sigil + "~" @comment.documentation + (sigil_name) @comment.documentation + quoted_start: _ @comment.documentation + (quoted_content) @comment.documentation + quoted_end: _ @comment.documentation) + ] @comment.documentation))) @comment.documentation diff --git a/config/neovim/store/nvim-treesitter/queries/elixir/indents.scm b/config/neovim/store/nvim-treesitter/queries/elixir/indents.scm new file mode 100644 index 00000000..5470b642 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elixir/indents.scm @@ -0,0 +1,25 @@ +[ + (block) + (do_block) + (list) + (map) + (stab_clause) + (tuple) + (arguments) +] @indent.begin + +[ + ")" + "]" + "after" + "catch" + "else" + "rescue" + "}" + "end" +] @indent.end @indent.branch + +; Elixir pipelines are not indented, but other binary operator chains are +((binary_operator + operator: _ @_operator) @indent.begin + (#not-eq? @_operator "|>")) diff --git a/config/neovim/store/nvim-treesitter/queries/elixir/injections.scm b/config/neovim/store/nvim-treesitter/queries/elixir/injections.scm new file mode 100644 index 00000000..cc74f792 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elixir/injections.scm @@ -0,0 +1,59 @@ +; Comments +((comment) @injection.content + (#set! injection.language "comment")) + +; Documentation +(unary_operator + operator: "@" + operand: (call + target: ((identifier) @_identifier + (#any-of? @_identifier "moduledoc" "typedoc" "shortdoc" "doc")) + (arguments + [ + (string + (quoted_content) @injection.content) + (sigil + (quoted_content) @injection.content) + ]) + (#set! injection.language "markdown"))) + +; HEEx +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#eq? @_sigil_name "H") + (#set! injection.language "heex")) + +; Surface +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#eq? @_sigil_name "F") + (#set! injection.language "surface")) + +; Zigler +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#any-of? @_sigil_name "E" "L") + (#set! injection.language "eex")) + +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#any-of? @_sigil_name "z" "Z") + (#set! injection.language "zig")) + +; Regex +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#any-of? @_sigil_name "r" "R") + (#set! injection.language "regex")) + +; Json +(sigil + (sigil_name) @_sigil_name + (quoted_content) @injection.content + (#any-of? @_sigil_name "j" "J") + (#set! injection.language "json")) diff --git a/config/neovim/store/nvim-treesitter/queries/elixir/locals.scm b/config/neovim/store/nvim-treesitter/queries/elixir/locals.scm new file mode 100644 index 00000000..ac9d86e3 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elixir/locals.scm @@ -0,0 +1,200 @@ +; References +(identifier) @local.reference + +(alias) @local.reference + +; Module Definitions +(call + target: ((identifier) @_identifier + (#eq? @_identifier "defmodule")) + (arguments + (alias) @local.definition.type)) + +; Pattern Match Definitions +(binary_operator + ; format-ignore + left: + [ + (identifier) @local.definition.var + (_ (identifier) @local.definition.var) + (_ (_ (identifier) @local.definition.var)) + (_ (_ (_ (identifier) @local.definition.var))) + (_ (_ (_ (_ (identifier) @local.definition.var)))) + (_ (_ (_ (_ (_ (identifier) @local.definition.var))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))) + (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))))) + ] + operator: "=") + +; Stab Clause Definitions +; format-ignore +(stab_clause + left: + [ + (arguments + [ + (identifier) @local.definition.var + (_ (identifier) @local.definition.var) + (_ (_ (identifier) @local.definition.var)) + (_ (_ (_ (identifier) @local.definition.var))) + (_ (_ (_ (_ (identifier) @local.definition.var)))) + (_ (_ (_ (_ (_ (identifier) @local.definition.var))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))) + (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))))) + ]) + + (binary_operator + left: + (arguments + ; format-ignore + [ + (identifier) @local.definition.var + (_ (identifier) @local.definition.var) + (_ (_ (identifier) @local.definition.var)) + (_ (_ (_ (identifier) @local.definition.var))) + (_ (_ (_ (_ (identifier) @local.definition.var)))) + (_ (_ (_ (_ (_ (identifier) @local.definition.var))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))) + (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var))))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.var)))))))))))))))))))) + ]) + operator: "when") + ]) + +; Aliases +; format-ignore +(call + target: + ((identifier) @_identifier + (#any-of? @_identifier "require" "alias" "use" "import")) + (arguments + [ + (alias) @local.definition.import + (_ (alias) @local.definition.import) + (_ (_ (alias) @local.definition.import)) + (_ (_ (_ (alias) @local.definition.import))) + (_ (_ (_ (_ (alias) @local.definition.import)))) + ])) + +; Local Function Definitions & Scopes +; format-ignore +(call + target: + ((identifier) @_identifier + (#any-of? @_identifier "def" "defp" "defmacro" "defmacrop" "defguard" "defguardp" "defn" "defnp" "for")) + (arguments + [ + (identifier) @local.definition.function + (binary_operator + left: (identifier) @local.definition.function + operator: "when") + (binary_operator + (identifier) @local.definition.parameter) + (call + target: (identifier) @local.definition.function + (arguments + [ + (identifier) @local.definition.parameter + (_ (identifier) @local.definition.parameter) + (_ (_ (identifier) @local.definition.parameter)) + (_ (_ (_ (identifier) @local.definition.parameter))) + (_ (_ (_ (_ (identifier) @local.definition.parameter)))) + (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))) + (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))))))) + ])) + ]?) + (#set! definition.function.scope parent)(do_block)?) @local.scope + +; ExUnit Test Definitions & Scopes +; format-ignore +(call + target: + ((identifier) @_identifier + (#eq? @_identifier "test")) + (arguments + [ + (string) + ((string) + . + "," + . + [ + (identifier) @local.definition.parameter + (_ (identifier) @local.definition.parameter) + (_ (_ (identifier) @local.definition.parameter)) + (_ (_ (_ (identifier) @local.definition.parameter))) + (_ (_ (_ (_ (identifier) @local.definition.parameter)))) + (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))) + (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter))))))))))))))))))) + (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (_ (identifier) @local.definition.parameter)))))))))))))))))))) + ]) + ]) + (do_block)?) @local.scope + +; Stab Clause Scopes +(stab_clause) @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/elm/highlights.scm b/config/neovim/store/nvim-treesitter/queries/elm/highlights.scm new file mode 100644 index 00000000..fc18566c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elm/highlights.scm @@ -0,0 +1,229 @@ +[ + (line_comment) + (block_comment) +] @comment @spell + +((block_comment) @comment.documentation + (#lua-match? @comment.documentation "^{[-]|[^|]")) + +; Keywords +;--------- +[ + "if" + "then" + "else" + (case) + (of) +] @keyword.conditional + +[ + "let" + "in" + (as) + (port) + (alias) + (infix) + (module) + (type) +] @keyword + +[ + (import) + (exposing) +] @keyword.import + +; Punctuation +;------------ +(double_dot) @punctuation.special + +[ + "," + "|" + (dot) +] @punctuation.delimiter + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +; Variables +;---------- +(value_qid + (lower_case_identifier) @variable) + +(value_declaration + (function_declaration_left + (lower_case_identifier) @variable)) + +(type_annotation + (lower_case_identifier) @variable) + +(port_annotation + (lower_case_identifier) @variable) + +(anything_pattern + (underscore) @variable) + +(record_base_identifier + (lower_case_identifier) @variable) + +(lower_pattern + (lower_case_identifier) @variable) + +(exposed_value + (lower_case_identifier) @variable) + +(value_qid + ((dot) + (lower_case_identifier) @variable.member)) + +(field_access_expr + ((dot) + (lower_case_identifier) @variable.member)) + +(function_declaration_left + (anything_pattern + (underscore) @variable.parameter)) + +(function_declaration_left + (lower_pattern + (lower_case_identifier) @variable.parameter)) + +; Functions +;---------- +(value_declaration + functionDeclarationLeft: (function_declaration_left + (lower_case_identifier) @function + (pattern))) + +(value_declaration + functionDeclarationLeft: (function_declaration_left + (lower_case_identifier) @function + pattern: (_))) + +(value_declaration + functionDeclarationLeft: (function_declaration_left + (lower_case_identifier) @function) + body: (anonymous_function_expr)) + +(type_annotation + name: (lower_case_identifier) @function + typeExpression: (type_expression + (arrow))) + +(port_annotation + name: (lower_case_identifier) @function + typeExpression: (type_expression + (arrow))) + +(function_call_expr + target: (value_expr + (value_qid + (lower_case_identifier) @function.call))) + +; Operators +;---------- +[ + (operator_identifier) + (eq) + (colon) + (arrow) + (backslash) + "::" +] @operator + +; Modules +;-------- +(module_declaration + (upper_case_qid + (upper_case_identifier) @module)) + +(import_clause + (upper_case_qid + (upper_case_identifier) @module)) + +(as_clause + (upper_case_identifier) @module) + +(value_expr + (value_qid + (upper_case_identifier) @module)) + +; Types +;------ +(type_declaration + (upper_case_identifier) @type) + +(type_ref + (upper_case_qid + (upper_case_identifier) @type)) + +(type_variable + (lower_case_identifier) @type) + +(lower_type_name + (lower_case_identifier) @type) + +(exposed_type + (upper_case_identifier) @type) + +(type_alias_declaration + (upper_case_identifier) @type.definition) + +(field_type + name: (lower_case_identifier) @property) + +(field + name: (lower_case_identifier) @property) + +(type_declaration + (union_variant + (upper_case_identifier) @constructor)) + +(nullary_constructor_argument_pattern + (upper_case_qid + (upper_case_identifier) @constructor)) + +(union_pattern + (upper_case_qid + (upper_case_identifier) @constructor)) + +(value_expr + (upper_case_qid + (upper_case_identifier)) @constructor) + +; Literals +;--------- +(number_constant_expr + (number_literal) @number) + +(upper_case_qid + ((upper_case_identifier) @boolean + (#any-of? @boolean "True" "False"))) + +[ + (open_quote) + (close_quote) +] @string + +(string_constant_expr + (string_escape) @string) + +(string_constant_expr + (regular_string_part) @string) + +[ + (open_char) + (close_char) +] @character + +(char_constant_expr + (string_escape) @character) + +(char_constant_expr + (regular_string_part) @character) diff --git a/config/neovim/store/nvim-treesitter/queries/elm/injections.scm b/config/neovim/store/nvim-treesitter/queries/elm/injections.scm new file mode 100644 index 00000000..7ee6c7f0 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elm/injections.scm @@ -0,0 +1,8 @@ +([ + (line_comment) + (block_comment) +] @injection.content + (#set! injection.language "comment")) + +((glsl_content) @injection.content + (#set! injection.language "glsl")) diff --git a/config/neovim/store/nvim-treesitter/queries/elsa/folds.scm b/config/neovim/store/nvim-treesitter/queries/elsa/folds.scm new file mode 100644 index 00000000..afdfec30 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elsa/folds.scm @@ -0,0 +1 @@ +(reduction) @fold diff --git a/config/neovim/store/nvim-treesitter/queries/elsa/highlights.scm b/config/neovim/store/nvim-treesitter/queries/elsa/highlights.scm new file mode 100644 index 00000000..1a974bda --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elsa/highlights.scm @@ -0,0 +1,36 @@ +; Keywords +[ + "eval" + "let" +] @keyword + +; Function +(function) @function + +; Method +(method) @function.method + +; Parameter +(parameter) @variable.parameter + +; Variables +(identifier) @variable + +; Operators +[ + "\\" + "->" + "=" + (step) +] @operator + +; Punctuation +[ + "(" + ")" +] @punctuation.bracket + +":" @punctuation.delimiter + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/elsa/indents.scm b/config/neovim/store/nvim-treesitter/queries/elsa/indents.scm new file mode 100644 index 00000000..6ddd1aa6 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elsa/indents.scm @@ -0,0 +1,6 @@ +(reduction) @indent.begin + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/elsa/injections.scm b/config/neovim/store/nvim-treesitter/queries/elsa/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elsa/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/elsa/locals.scm b/config/neovim/store/nvim-treesitter/queries/elsa/locals.scm new file mode 100644 index 00000000..3e8197ae --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elsa/locals.scm @@ -0,0 +1,12 @@ +[ + (source_file) + (reduction) +] @local.scope + +(identifier) @local.reference + +(function) @local.definition.function + +(method) @local.definition.method + +(parameter) @local.definition.parameter diff --git a/config/neovim/store/nvim-treesitter/queries/elvish/highlights.scm b/config/neovim/store/nvim-treesitter/queries/elvish/highlights.scm new file mode 100644 index 00000000..9836a6c7 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elvish/highlights.scm @@ -0,0 +1,157 @@ +(comment) @comment @spell + +[ + "if" + "elif" +] @keyword.conditional + +(if + (else + "else" @keyword.conditional)) + +[ + "while" + "for" +] @keyword.repeat + +(while + (else + "else" @keyword.repeat)) + +(for + (else + "else" @keyword.repeat)) + +[ + "try" + "catch" + "finally" +] @keyword.exception + +(try + (else + "else" @keyword.exception)) + +"use" @keyword.import + +(import + (bareword) @string.special.path) + +(wildcard + [ + "*" + "**" + "?" + ] @character.special) + +(command + argument: (bareword) @variable.parameter) + +(command + head: (identifier) @function.call) + +((command + head: (identifier) @keyword.return) + (#eq? @keyword.return "return")) + +((command + (identifier) @keyword.operator) + (#any-of? @keyword.operator "and" "or" "coalesce")) + +[ + "+" + "-" + "*" + "/" + "%" + "<" + "<=" + "==" + "!=" + ">" + ">=" + "s" + ">=s" +] @function.builtin + +[ + ">" + "<" + ">>" + "<>" + "|" +] @operator + +(io_port) @number + +(function_definition + "fn" @keyword.function + (identifier) @function) + +(parameter_list) @variable.parameter + +(parameter_list + "|" @punctuation.bracket) + +[ + "var" + "set" + "tmp" + "del" +] @keyword + +(variable_declaration + (lhs + (identifier) @variable)) + +(variable_assignment + (lhs + (identifier) @variable)) + +(temporary_assignment + (lhs + (identifier) @variable)) + +(variable_deletion + (identifier) @variable) + +(number) @number + +(string) @string + +(variable + (identifier) @variable) + +((variable + (identifier) @function) + (#lua-match? @function ".+[~]$")) + +((variable + (identifier) @boolean) + (#any-of? @boolean "true" "false")) + +((variable + (identifier) @constant.builtin) + (#any-of? @constant.builtin + "_" "after-chdir" "args" "before-chdir" "buildinfo" "nil" "notify-bg-job-success" "num-bg-jobs" + "ok" "paths" "pid" "pwd" "value-out-indicator" "version")) + +[ + "$" + "@" +] @punctuation.special + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +";" @punctuation.delimiter diff --git a/config/neovim/store/nvim-treesitter/queries/elvish/injections.scm b/config/neovim/store/nvim-treesitter/queries/elvish/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/elvish/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/embedded_template/highlights.scm b/config/neovim/store/nvim-treesitter/queries/embedded_template/highlights.scm new file mode 100644 index 00000000..410983d6 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/embedded_template/highlights.scm @@ -0,0 +1,12 @@ +(comment_directive) @comment @spell + +[ + "<%#" + "<%" + "<%=" + "<%_" + "<%-" + "%>" + "-%>" + "_%>" +] @keyword diff --git a/config/neovim/store/nvim-treesitter/queries/embedded_template/injections.scm b/config/neovim/store/nvim-treesitter/queries/embedded_template/injections.scm new file mode 100644 index 00000000..cdeb2cd4 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/embedded_template/injections.scm @@ -0,0 +1,7 @@ +((content) @injection.content + (#set! injection.language "html") + (#set! injection.combined)) + +((code) @injection.content + (#set! injection.language "ruby") + (#set! injection.combined)) diff --git a/config/neovim/store/nvim-treesitter/queries/erlang/folds.scm b/config/neovim/store/nvim-treesitter/queries/erlang/folds.scm new file mode 100644 index 00000000..65c2d8ed --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/erlang/folds.scm @@ -0,0 +1,9 @@ +[ + (fun_decl) + (anonymous_fun) + (case_expr) + (maybe_expr) + (map_expr) + (export_attribute) + (export_type_attribute) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/erlang/highlights.scm b/config/neovim/store/nvim-treesitter/queries/erlang/highlights.scm new file mode 100644 index 00000000..1ce04668 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/erlang/highlights.scm @@ -0,0 +1,184 @@ +((atom) @constant + (#set! "priority" "90")) + +(var) @variable + +(char) @character + +(integer) @number + +(float) @number.float + +(comment) @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^[%%][%%]")) + +; keyword +[ + "fun" + "div" +] @keyword + +; bracket +[ + "(" + ")" + "{" + "}" + "[" + "]" + "#" +] @punctuation.bracket + +; Comparisons +[ + "==" + "=:=" + "=/=" + "=<" + ">=" + "<" + ">" +] @operator ; .comparison + +; operator +[ + ":" + ":=" + "!" + ; "-" + "+" + "=" + "->" + "=>" + "|" + "?=" +] @operator + +[ + "," + "." + ";" +] @punctuation.delimiter + +; conditional +[ + "receive" + "if" + "case" + "of" + "when" + "after" + "begin" + "end" + "maybe" + "else" +] @keyword.conditional + +[ + "catch" + "try" +] @keyword.exception + +((atom) @boolean + (#any-of? @boolean "true" "false")) + +; Macros +((macro_call_expr) @constant.macro + (#set! "priority" 101)) + +; Preprocessor +(pp_define + lhs: _ @constant.macro + (#set! "priority" 101)) + +(_preprocessor_directive) @keyword.directive +(#set! "priority" 99) + +; Attributes +(pp_include) @keyword.import + +(pp_include_lib) @keyword.import + +(export_attribute) @keyword.import + +(export_type_attribute) @type.definition + +(export_type_attribute + types: (fa + fun: _ @type + (#set! "priority" 101))) + +(behaviour_attribute) @keyword.import + +(module_attribute + (atom) @module) @keyword.import + +(wild_attribute + name: (attr_name + name: _ @attribute)) @attribute + +; Records +(record_expr) @type + +(record_field_expr + _ @variable.member) @type + +(record_field_name + _ @variable.member) @type + +(record_name + "#" @type + name: _ @type) @type + +(record_decl + name: _ @type) @type.definition + +(record_field + name: _ @variable.member) + +(record_field + name: _ @variable.member + ty: _ @type) + +; Type alias +(type_alias + name: _ @type) @type.definition + +(spec) @type.definition + +[ + (string) + (binary) +] @string + +; expr_function_call +(call + expr: [ + (atom) + (remote) + (var) + ] @function) + +(call + (atom) @keyword.exception + (#any-of? @keyword.exception "error" "throw" "exit")) + +; Parenthesized expression: (SomeFunc)(), only highlight the parens +(call + expr: (paren_expr + "(" @function.call + ")" @function.call)) + +; function +(external_fun) @function.call + +(internal_fun + fun: (atom) @function.call) + +(function_clause + name: (atom) @function) + +(fa + fun: (atom) @function) diff --git a/config/neovim/store/nvim-treesitter/queries/facility/folds.scm b/config/neovim/store/nvim-treesitter/queries/facility/folds.scm new file mode 100644 index 00000000..7d8bafc8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/facility/folds.scm @@ -0,0 +1,6 @@ +[ + (service) + (method) + (dto) + (enum) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/facility/highlights.scm b/config/neovim/store/nvim-treesitter/queries/facility/highlights.scm new file mode 100644 index 00000000..6816b480 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/facility/highlights.scm @@ -0,0 +1,87 @@ +[ + ";" + "." + "," +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(comment) @comment @spell + +(doc_comment) @comment.documentation @spell + +[ + "service" + "errors" +] @keyword + +"method" @keyword.function + +[ + "enum" + "data" +] @keyword.type + +"extern" @keyword.modifier + +(type) @type.builtin + +(service + service_name: (identifier) @type) + +(error_set + (identifier) @variable.member) + +(error_set + name: (identifier) @type) + +(dto + name: (identifier) @type) + +(external_dto + name: (identifier) @type) + +(enum + (values_block + (identifier) @constant)) + +(enum + name: (identifier) @type) + +(external_enum + name: (identifier) @type) + +(type + name: (identifier) @type) + +[ + "map" + "nullable" + "result" + "required" + "http" + "csharp" + "js" + "info" + "obsolete" +] @attribute.builtin + +(parameter + name: (identifier) @variable.parameter) + +(field + name: (identifier) @variable.member) + +(method + name: (identifier) @function.method) + +(number_literal) @number + +(string_literal) @string diff --git a/config/neovim/store/nvim-treesitter/queries/facility/indents.scm b/config/neovim/store/nvim-treesitter/queries/facility/indents.scm new file mode 100644 index 00000000..247949ba --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/facility/indents.scm @@ -0,0 +1,7 @@ +[ + (service_block) + (values_block) + (field_list) +] @indent.begin + +"}" @indent.branch diff --git a/config/neovim/store/nvim-treesitter/queries/facility/injections.scm b/config/neovim/store/nvim-treesitter/queries/facility/injections.scm new file mode 100644 index 00000000..5d9b7836 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/facility/injections.scm @@ -0,0 +1,8 @@ +((remarks) @injection.content + (#set! injection.language "markdown")) + +([ + (comment) + (doc_comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/faust/highlights.scm b/config/neovim/store/nvim-treesitter/queries/faust/highlights.scm new file mode 100644 index 00000000..6e7ef1d9 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/faust/highlights.scm @@ -0,0 +1,219 @@ +; Identifiers +(identifier) @variable + +[ + "process" + "effect" +] @variable.builtin + +(parameters + (identifier)) @variable.parameter + +(access + definition: (identifier) @variable.member) + +(global_metadata + key: (identifier) @variable.member) + +(function_metadata + function_name: (identifier) @variable.member) + +; Literals +(_ + filename: (string)) @string.special.path + +(documentation) @string.documentation @spell + +[ + (string) + (fstring) +] @string + +(int) @number + +(real) @number.float + +; Types +(_ + type: [ + (int_type) + (float_type) + (any_type) + ]) @type.builtin + +[ + (single_precision) + (double_precision) + (quad_precision) + (fixed_point_precision) +] @attribute + +; Functions +(function_definition + name: (identifier) @function) + +(function_names) @function + +(function_call + (identifier) @function.call) + +(function_call + (access + definition: (identifier) @function.call)) + +[ + "exp" + "log" + "log10" + "sqrt" + "abs" + "floor" + "ceil" + "rint" + "round" + "acos" + "asin" + "atan" + "cos" + "sin" + "tan" + "atan2" + "int" + "float" + "pow" + "min" + "max" + "fmod" + "remainder" + "prefix" + "attach" + "enable" + "control" + "rdtable" + "rwtable" + "select2" + "select3" + "lowest" + "highest" + "assertbounds" + (par) + (seq) + (sum) + (prod) + (component) + (library) + (vslider_type) + (hslider_type) + (nentry_type) + (vbargraph_type) + (hbargraph_type) + (vgroup_type) + (hgroup_type) + (tgroup_type) + "button" + "checkbox" + "soundfile" + "inputs" + "outputs" + "route" +] @function.builtin + +; xor is a @keyword.operator +[ + (add) + (sub) + (mult) + (div) + (mod) + (pow) + (or) + (and) + (lshift) + (rshift) + (lt) + (le) + (gt) + (ge) + (eq) + (neq) + (delay) + (one_sample_delay) + "=" + "=>" + "->" +] @operator + +(recursive + "~" @operator) + +(sequential + ":" @operator) + +(split + "<:" @operator) + +(merge + ":>" @operator) + +(parallel + "," @operator) + +; Keywords +[ + (par) + (seq) + (sum) + (prod) +] @keyword.repeat + +(file_import + "import" @keyword.import) + +[ + (wire) + (cut) + (mem) + "declare" + "with" + "environment" + "case" + "ffunction" + "fconstant" + "fvariable" +] @keyword + +(xor) @keyword.operator + +; Punctuation +[ + "," + ";" + "." +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +; Comments +(comment) @comment @spell + +; Tags +[ + "" + "" + "" + "" + "" + "" + "" + "" + "" +] @tag diff --git a/config/neovim/store/nvim-treesitter/queries/faust/injections.scm b/config/neovim/store/nvim-treesitter/queries/faust/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/faust/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/fennel/folds.scm b/config/neovim/store/nvim-treesitter/queries/fennel/folds.scm new file mode 100644 index 00000000..0862e592 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fennel/folds.scm @@ -0,0 +1,51 @@ +; compounds +[ + (list) + (table) + (sequence) +] @fold + +; sub-forms / special compounds +[ + (list_binding) + (table_binding) + (sequence_binding) + (table_metadata) + (sequence_arguments) + (let_vars) + (case_guard_or_special) + (case_guard) + (case_catch) +] @fold + +; forms +[ + (quote_form) + (unquote_form) + (local_form) + (var_form) + (set_form) + (global_form) + (let_form) + (fn_form) + (lambda_form) + (hashfn_form) + (each_form) + (collect_form) + (icollect_form) + (accumulate_form) + (for_form) + (fcollect_form) + (faccumulate_form) + (case_form) + (match_form) + (case_try_form) + (match_try_form) +] @fold + +; reader macros +(quote_reader_macro + expression: (_) @fold) + +(quasi_quote_reader_macro + expression: (_) @fold) diff --git a/config/neovim/store/nvim-treesitter/queries/fennel/highlights.scm b/config/neovim/store/nvim-treesitter/queries/fennel/highlights.scm new file mode 100644 index 00000000..2f0b5f73 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fennel/highlights.scm @@ -0,0 +1,193 @@ +; Most primitive nodes +(shebang) @keyword.directive + +(comment) @comment @spell + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + (nil) + (nil_binding) +] @constant.builtin + +[ + (boolean) + (boolean_binding) +] @boolean + +[ + (number) + (number_binding) +] @number + +[ + (string) + (string_binding) +] @string + +[ + (symbol) + (symbol_binding) +] @variable + +(symbol_option) @keyword.directive + +(escape_sequence) @string.escape + +(multi_symbol + "." @punctuation.delimiter + member: (symbol_fragment) @variable.member) + +(list + call: (symbol) @function.call) + +(list + call: (multi_symbol + member: (symbol_fragment) @function.call .)) + +(multi_symbol_method + ":" @punctuation.delimiter + method: (symbol_fragment) @function.method.call) + +(quasi_quote_reader_macro + macro: _ @punctuation.special) + +(quote_reader_macro + macro: _ @punctuation.special) + +(unquote_reader_macro + macro: _ @punctuation.special) + +(hashfn_reader_macro + macro: _ @keyword.function) + +(sequence_arguments + (symbol_binding) @variable.parameter) + +(sequence_arguments + (rest_binding + rhs: (symbol_binding) @variable.parameter)) + +(docstring) @string.documentation + +(fn_form + name: [ + (symbol) @function + (multi_symbol + member: (symbol_fragment) @function .) + ]) + +(lambda_form + name: [ + (symbol) @function + (multi_symbol + member: (symbol_fragment) @function .) + ]) + +; NOTE: The macro name is highlighted as @variable because it +; gives a nicer contrast instead of everything being the same +; color. Rust queries use this workaround too for `macro_rules!`. +(macro_form + name: [ + (symbol) @variable + (multi_symbol + member: (symbol_fragment) @variable .) + ]) + +((symbol) @variable.parameter + (#any-of? @variable.parameter "$" "$...")) + +((symbol) @variable.parameter + (#lua-match? @variable.parameter "^%$[1-9]$")) + +((symbol) @operator + (#any-of? @operator + ; arithmetic + "+" "-" "*" "/" "//" "%" "^" + ; comparison + ">" "<" ">=" "<=" "=" "~=" + ; other + "#" "." "?." "..")) + +((symbol) @keyword.operator + (#any-of? @keyword.operator + ; comparison + "not=" + ; boolean + "and" "or" "not" + ; bitwise + "lshift" "rshift" "band" "bor" "bxor" "bnot" + ; other + "length")) + +(case_guard + call: (_) @keyword.conditional) + +(case_guard_or_special + call: (_) @keyword.conditional) + +((symbol) @keyword.function + (#any-of? @keyword.function "fn" "lambda" "λ" "hashfn")) + +((symbol) @keyword.repeat + (#any-of? @keyword.repeat "for" "each" "while")) + +((symbol) @keyword.conditional + (#any-of? @keyword.conditional "if" "when" "match" "case" "match-try" "case-try")) + +((symbol) @keyword + (#any-of? @keyword + "global" "local" "let" "set" "var" "comment" "do" "doc" "eval-compiler" "lua" "macros" "unquote" + "quote" "tset" "values" "tail!")) + +((symbol) @keyword.import + (#any-of? @keyword.import "require-macros" "import-macros" "include")) + +((symbol) @function.macro + (#any-of? @function.macro + "collect" "icollect" "fcollect" "accumulate" "faccumulate" "->" "->>" "-?>" "-?>>" "?." "doto" + "macro" "macrodebug" "partial" "pick-args" "pick-values" "with-open")) + +(case_catch + call: (symbol) @keyword) + +(import_macros_form + imports: (table_binding + (table_binding_pair + value: (symbol_binding) @function.macro))) + +; TODO: Highlight builtin methods (`table.unpack`, etc) as @function.builtin +([ + (symbol) @module.builtin + (multi_symbol + base: (symbol_fragment) @module.builtin) +] + (#any-of? @module.builtin + "vim" "_G" "debug" "io" "jit" "math" "os" "package" "string" "table" "utf8")) + +([ + (symbol) @variable.builtin + (multi_symbol + . + (symbol_fragment) @variable.builtin) +] + (#eq? @variable.builtin "arg")) + +((symbol) @variable.builtin + (#eq? @variable.builtin "...")) + +((symbol) @constant.builtin + (#eq? @constant.builtin "_VERSION")) + +((symbol) @function.builtin + (#any-of? @function.builtin + "assert" "collectgarbage" "dofile" "error" "getmetatable" "ipairs" "load" "loadfile" "next" + "pairs" "pcall" "print" "rawequal" "rawget" "rawlen" "rawset" "require" "select" "setmetatable" + "tonumber" "tostring" "type" "warn" "xpcall" "module" "setfenv" "loadstring" "unpack")) diff --git a/config/neovim/store/nvim-treesitter/queries/fennel/injections.scm b/config/neovim/store/nvim-treesitter/queries/fennel/injections.scm new file mode 100644 index 00000000..f6724d32 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fennel/injections.scm @@ -0,0 +1,134 @@ +((comment_body) @injection.content + (#set! injection.language "comment")) + +(list + call: (multi_symbol) @_vimcmd_identifier + (#any-of? @_vimcmd_identifier "vim.cmd" "vim.api.nvim_command" "vim.api.nvim_exec2") + . + item: (string + (string_content) @injection.content + (#set! injection.language "vim"))) + +; NOTE: Matches *exactly* `ffi.cdef` +(list + call: (multi_symbol) @_cdef_identifier + (#eq? @_cdef_identifier "ffi.cdef") + . + item: (string + (string_content) @injection.content + (#set! injection.language "c"))) + +(list + call: (multi_symbol) @_ts_query_identifier + (#any-of? @_ts_query_identifier "vim.treesitter.query.set" "vim.treesitter.query.parse") + . + item: (_) + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "query"))) + +(list + call: (multi_symbol) @_vimcmd_identifier + (#eq? @_vimcmd_identifier "vim.api.nvim_create_autocmd") + . + item: (_) + . + item: (table + (table_pair + key: (string + (string_content) @_command + (#eq? @_command "command")) + value: (string + (string_content) @injection.content + (#set! injection.language "vim"))))) + +(list + call: (multi_symbol) @_user_cmd + (#eq? @_user_cmd "vim.api.nvim_create_user_command") + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "vim"))) + +(list + call: (multi_symbol) @_user_cmd + (#eq? @_user_cmd "vim.api.nvim_buf_create_user_command") + . + item: (_) + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "vim"))) + +(list + call: (multi_symbol) @_map + (#any-of? @_map "vim.api.nvim_set_keymap" "vim.keymap.set") + . + item: (_) + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "vim"))) + +(list + call: (multi_symbol) @_map + (#eq? @_map "vim.api.nvim_buf_set_keymap") + . + item: (_) + . + item: (_) + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "vim"))) + +; highlight string as query if starts with `; query` +(string + (string_content) @injection.content + (#lua-match? @injection.content "^%s*;+%s?query") + (#set! injection.language "query")) + +; (string.match "123" "%d+") +(list + call: (multi_symbol + member: (symbol_fragment) @_func + . + (#any-of? @_func "find" "match" "gmatch" "gsub")) + . + item: (_) + . + item: (string + (string_content) @injection.content + (#set! injection.language "luap") + (#set! injection.include-children))) + +; (my-string:match "%d+") +(list + call: (multi_symbol_method + method: (symbol_fragment) @_method + (#any-of? @_method "find" "match" "gmatch" "gsub")) + . + item: (string + (string_content) @injection.content + (#set! injection.language "luap") + (#set! injection.include-children))) + +; (string.format "pi = %.2f" 3.14159) +(list + call: (multi_symbol) @_func + (#eq? @_func "string.format") + . + item: (string + (string_content) @injection.content + (#set! injection.language "printf"))) diff --git a/config/neovim/store/nvim-treesitter/queries/fennel/locals.scm b/config/neovim/store/nvim-treesitter/queries/fennel/locals.scm new file mode 100644 index 00000000..be63e728 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fennel/locals.scm @@ -0,0 +1,56 @@ +(program) @local.scope + +(symbol) @local.reference + +[ + (let_form) + (fn_form) + (lambda_form) + (each_form) + (for_form) + (collect_form) + (icollect_form) + (accumulate_form) + (for_form) + (fcollect_form) + (faccumulate_form) + (case_form) + (match_form) + (case_try_form) + (match_try_form) + (if_form) +] @local.scope + +(list + call: (symbol) @_call + (#any-of? @_call "while" "when" "do")) @local.scope + +(fn_form + name: [ + (symbol) @local.definition.function + (multi_symbol + member: (symbol_fragment) @local.definition.function .) + ] + args: (sequence_arguments + (symbol_binding) @local.definition.parameter) + (#set! definition.function.scope "parent")) + +(lambda_form + name: [ + (symbol) @local.definition.function + (multi_symbol + member: (symbol_fragment) @local.definition.function .) + ] + args: (sequence_arguments + (symbol_binding) @local.definition.parameter) + (#set! definition.function.scope "parent")) + +(macro_form + name: [ + (symbol) @local.definition.function + (multi_symbol + member: (symbol_fragment) @local.definition.function .) + ] + args: (sequence_arguments + (symbol_binding) @local.definition.parameter) + (#set! definition.function.scope "parent")) diff --git a/config/neovim/store/nvim-treesitter/queries/fidl/folds.scm b/config/neovim/store/nvim-treesitter/queries/fidl/folds.scm new file mode 100644 index 00000000..f524c455 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fidl/folds.scm @@ -0,0 +1,6 @@ +[ + (layout_declaration) + (protocol_declaration) + (resource_declaration) + (service_declaration) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/fidl/highlights.scm b/config/neovim/store/nvim-treesitter/queries/fidl/highlights.scm new file mode 100644 index 00000000..f1960c61 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fidl/highlights.scm @@ -0,0 +1,67 @@ +[ + "ajar" + "alias" + "as" + "bits" + "closed" + "compose" + "const" + "error" + "flexible" + "library" + "open" + ; "optional" we did not specify a node for optional yet + "overlay" + "protocol" + "reserved" + "strict" + "using" +] @keyword + +[ + "enum" + "struct" + "table" + "union" + "resource" + "service" + "type" +] @keyword.type + +(primitives_type) @type.builtin + +(builtin_complex_type) @type.builtin + +(const_declaration + (identifier) @constant) + +[ + "=" + "|" + "&" + "->" +] @operator + +(attribute + "@" @attribute + (identifier) @attribute) + +(string_literal) @string + +(numeric_literal) @number + +[ + (true) + (false) +] @boolean + +(comment) @comment + +[ + "(" + ")" + "<" + ">" + "{" + "}" +] @punctuation.bracket diff --git a/config/neovim/store/nvim-treesitter/queries/fidl/injections.scm b/config/neovim/store/nvim-treesitter/queries/fidl/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fidl/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/firrtl/folds.scm b/config/neovim/store/nvim-treesitter/queries/firrtl/folds.scm new file mode 100644 index 00000000..4c64e644 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/firrtl/folds.scm @@ -0,0 +1,6 @@ +[ + (circuit) + (module) + (when) + (else) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/firrtl/highlights.scm b/config/neovim/store/nvim-treesitter/queries/firrtl/highlights.scm new file mode 100644 index 00000000..b2cfd08a --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/firrtl/highlights.scm @@ -0,0 +1,198 @@ +; Namespaces +(circuit + (identifier) @module) + +(module + (identifier) @module) + +; Types +((identifier) @type + (#lua-match? @type "^[A-Z][A-Za-z0-9_$]*$")) + +; Keywords +[ + "circuit" + "module" + "extmodule" + "flip" + "parameter" + "reset" + "wire" + "cmem" + "smem" + "mem" + "reg" + "with" + "mport" + "inst" + "of" + "node" + "is" + "invalid" + "skip" + "infer" + "read" + "write" + "rdwr" + "defname" +] @keyword + +; Qualifiers +(qualifier) @keyword.modifier + +; Storageclasses +[ + "input" + "output" +] @keyword.modifier + +; Conditionals +[ + "when" + "else" +] @keyword.conditional + +; Annotations +(info) @attribute + +; Builtins +[ + "stop" + "printf" + "assert" + "assume" + "cover" + "attach" + "mux" + "validif" +] @function.builtin + +[ + "UInt" + "SInt" + "Analog" + "Fixed" + "Clock" + "AsyncReset" + "Reset" +] @type.builtin + +; Fields +[ + "data-type" + "depth" + "read-latency" + "write-latency" + "read-under-write" + "reader" + "writer" + "readwriter" +] @variable.member + +((field_id) @variable.member + (#set! "priority" 105)) + +(port + (identifier) @variable.member) + +(wire + (identifier) @variable.member) + +(cmem + (identifier) @variable.member) + +(smem + (identifier) @variable.member) + +(memory + (identifier) @variable.member) + +(register + (identifier) @variable.member) + +; Parameters +(primitive_operation + (identifier) @variable.parameter) + +(mux + (identifier) @variable.parameter) + +(printf + (identifier) @variable.parameter) + +(reset + (identifier) @variable.parameter) + +(stop + (identifier) @variable.parameter) + +; Variables +(identifier) @variable + +; Operators +(primop) @keyword.operator + +[ + "+" + "-" + "=" + "=>" + "<=" + "<-" +] @operator + +; Literals +[ + (uint) + (number) +] @number + +(number_str) @string.special + +(double) @number.float + +(string) @string + +(escape_sequence) @string.escape + +[ + "old" + "new" + "undefined" +] @constant.builtin + +; Punctuation +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "<" + ">" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "," + "." + ":" +] @punctuation.delimiter + +; Comments +(comment) @comment @spell + +[ + "=>" + "<=" + "=" +] @operator diff --git a/config/neovim/store/nvim-treesitter/queries/firrtl/indents.scm b/config/neovim/store/nvim-treesitter/queries/firrtl/indents.scm new file mode 100644 index 00000000..e172e1e8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/firrtl/indents.scm @@ -0,0 +1,12 @@ +[ + (circuit) + (module) + (memory) + (when) + (else) +] @indent.begin + +[ + (ERROR) + (comment) +] @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/firrtl/injections.scm b/config/neovim/store/nvim-treesitter/queries/firrtl/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/firrtl/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/firrtl/locals.scm b/config/neovim/store/nvim-treesitter/queries/firrtl/locals.scm new file mode 100644 index 00000000..97b7931b --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/firrtl/locals.scm @@ -0,0 +1,45 @@ +; Scopes +[ + (source_file) + (circuit) + (module) + (else) + (when) +] @local.scope + +; References +(identifier) @local.reference + +; Definitions +(port + (identifier) @local.definition.field) + +(wire + (identifier) @local.definition.field) + +(cmem + (identifier) @local.definition.field) + +(smem + (identifier) @local.definition.field) + +(memory + (identifier) @local.definition.field) + +(register + (identifier) @local.definition.field) + +(circuit + (identifier) @local.definition.namespace) + +(module + (identifier) @local.definition.namespace) + +(parameter + (identifier) @local.definition.parameter) + +(rdwr + (identifier) @local.definition.var) + +(node + (identifier) @local.definition.var) diff --git a/config/neovim/store/nvim-treesitter/queries/fish/folds.scm b/config/neovim/store/nvim-treesitter/queries/fish/folds.scm new file mode 100644 index 00000000..06363e15 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fish/folds.scm @@ -0,0 +1,8 @@ +[ + (function_definition) + (if_statement) + (switch_statement) + (for_statement) + (while_statement) + (begin_statement) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/fish/highlights.scm b/config/neovim/store/nvim-treesitter/queries/fish/highlights.scm new file mode 100644 index 00000000..10ed533d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fish/highlights.scm @@ -0,0 +1,176 @@ +; Fish highlighting +; Operators +[ + "&&" + "||" + "|" + "&|" + "2>|" + "&" + ".." + "!" + (direction) + (stream_redirect) +] @operator + +; match operators of test command +(command + name: (word) @function.builtin + (#eq? @function.builtin "test") + argument: (word) @operator + (#match? @operator "^(!?\\=|-[a-zA-Z]+)$")) + +; match operators of [ command +(command + name: (word) @punctuation.bracket + (#eq? @punctuation.bracket "[") + argument: (word) @operator + (#match? @operator "^(!?\\=|-[a-zA-Z]+)$")) + +[ + "not" + "and" + "or" +] @keyword.operator + +; Conditionals +(if_statement + [ + "if" + "end" + ] @keyword.conditional) + +(switch_statement + [ + "switch" + "end" + ] @keyword.conditional) + +(case_clause + "case" @keyword.conditional) + +(else_clause + "else" @keyword.conditional) + +(else_if_clause + [ + "else" + "if" + ] @keyword.conditional) + +; Loops/Blocks +(while_statement + [ + "while" + "end" + ] @keyword.repeat) + +(for_statement + [ + "for" + "end" + ] @keyword.repeat) + +(begin_statement + [ + "begin" + "end" + ] @keyword.repeat) + +; Keywords +[ + "in" + (break) + (continue) +] @keyword + +"return" @keyword.return + +; Punctuation +[ + "[" + "]" + "{" + "}" + "(" + ")" +] @punctuation.bracket + +"," @punctuation.delimiter + +; Commands +(command + argument: [ + (word) @variable.parameter + (#lua-match? @variable.parameter "^[-]") + ]) + +(command_substitution + "$" @punctuation.bracket) + +; non-builtin command names +(command + name: (word) @function.call) + +; derived from builtin -n (fish 3.2.2) +(command + name: [ + (word) @function.builtin + (#any-of? @function.builtin + "." ":" "_" "alias" "argparse" "bg" "bind" "block" "breakpoint" "builtin" "cd" "command" + "commandline" "complete" "contains" "count" "disown" "echo" "emit" "eval" "exec" "exit" "fg" + "functions" "history" "isatty" "jobs" "math" "printf" "pwd" "random" "read" "realpath" "set" + "set_color" "source" "status" "string" "test" "time" "type" "ulimit" "wait") + ]) + +; Functions +(function_definition + [ + "function" + "end" + ] @keyword.function) + +(function_definition + name: [ + (word) + (concatenation) + ] @function) + +(function_definition + option: [ + (word) + (concatenation + (word)) + ] @variable.parameter + (#lua-match? @variable.parameter "^[-]")) + +; Strings +[ + (double_quote_string) + (single_quote_string) +] @string + +(escape_sequence) @string.escape + +; Variables +(variable_name) @variable + +(variable_expansion) @constant + +; Nodes +[ + (integer) + (float) +] @number + +(comment) @comment + +(comment) @spell + +((word) @boolean + (#any-of? @boolean "true" "false")) + +((program + . + (comment) @keyword.directive @nospell) + (#lua-match? @keyword.directive "^#!/")) diff --git a/config/neovim/store/nvim-treesitter/queries/fish/indents.scm b/config/neovim/store/nvim-treesitter/queries/fish/indents.scm new file mode 100644 index 00000000..4984c4cb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fish/indents.scm @@ -0,0 +1,18 @@ +[ + (function_definition) + (while_statement) + (for_statement) + (if_statement) + (begin_statement) + (switch_statement) +] @indent.begin + +[ + "else" ; else and else if must both start the line with "else", so tag the string directly + "case" + "end" +] @indent.branch + +"end" @indent.end + +(comment) @indent.ignore diff --git a/config/neovim/store/nvim-treesitter/queries/fish/injections.scm b/config/neovim/store/nvim-treesitter/queries/fish/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fish/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/fish/locals.scm b/config/neovim/store/nvim-treesitter/queries/fish/locals.scm new file mode 100644 index 00000000..904d568f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fish/locals.scm @@ -0,0 +1,19 @@ +; Scopes +[ + (command) + (function_definition) + (if_statement) + (for_statement) + (begin_statement) + (while_statement) + (switch_statement) +] @local.scope + +; Definitions +(function_definition + name: (word) @local.definition.function) + +; References +(variable_name) @local.reference + +(word) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/foam/folds.scm b/config/neovim/store/nvim-treesitter/queries/foam/folds.scm new file mode 100644 index 00000000..e05d0dba --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/foam/folds.scm @@ -0,0 +1,8 @@ +[ + (comment) + (list) + (dict_core) +] @fold + +(code + (code_body)* @fold) diff --git a/config/neovim/store/nvim-treesitter/queries/foam/highlights.scm b/config/neovim/store/nvim-treesitter/queries/foam/highlights.scm new file mode 100644 index 00000000..9c96f196 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/foam/highlights.scm @@ -0,0 +1,64 @@ +; Comments +(comment) @comment @spell + +; Generic Key-value pairs and dictionary keywords +(key_value + keyword: (identifier) @function) + +(dict + key: (identifier) @type) + +; Macros +(macro + "$" @keyword.conditional + (prev_scope)* @keyword.conditional + (identifier)* @module) + +; Directives +"#" @keyword.conditional + +(preproc_call + directive: (identifier)* @keyword.conditional + argument: (identifier)* @module) + +((preproc_call + argument: (identifier)* @module) @keyword.conditional + (#eq? @keyword.conditional "ifeq")) + +((preproc_call) @keyword.conditional + (#any-of? @keyword.conditional "else" "endif")) + +; Literals +(number_literal) @number.float + +(string_literal) @string + +(escape_sequence) @string.escape + +(boolean) @boolean + +; Treat [m^2 s^-2] the same as if it was put in numbers format +(dimensions + dimension: (identifier) @number.float) + +; Punctuation +[ + "(" + ")" + "[" + "]" + "{" + "}" + "#{" + "#}" + "|-" + "-|" + "" + "$$" +] @punctuation.bracket + +";" @punctuation.delimiter + +((identifier) @constant.builtin + (#any-of? @constant.builtin "uniform" "non-uniform" "and" "or")) diff --git a/config/neovim/store/nvim-treesitter/queries/foam/indents.scm b/config/neovim/store/nvim-treesitter/queries/foam/indents.scm new file mode 100644 index 00000000..be02b80e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/foam/indents.scm @@ -0,0 +1,11 @@ +[ + "{" + "}" +] @indent.branch + +[ + (dict) + (key_value) +] @indent.begin + +(comment) @indent.ignore diff --git a/config/neovim/store/nvim-treesitter/queries/foam/injections.scm b/config/neovim/store/nvim-treesitter/queries/foam/injections.scm new file mode 100644 index 00000000..e1c223bd --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/foam/injections.scm @@ -0,0 +1,20 @@ +; Pass code blocks to Cpp highlighter +(code + (code_body) @injection.content + (#set! injection.language "cpp")) + +; Pass identifiers to Go highlighter (Cheating I know) +; ((identifier) @injection.content +; (#set! injection.language "lua") +; Highlight regex syntax inside literal strings +((string_literal) @injection.content + (#set! injection.language "regex")) + +; Highlight PyFoam syntax as Python statements +(pyfoam_variable + code_body: (_) @injection.content + (#set! injection.language "python")) + +(pyfoam_expression + code_body: (_) @injection.content + (#set! injection.language "python")) diff --git a/config/neovim/store/nvim-treesitter/queries/foam/locals.scm b/config/neovim/store/nvim-treesitter/queries/foam/locals.scm new file mode 100644 index 00000000..f3f68908 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/foam/locals.scm @@ -0,0 +1,11 @@ +(dict) @local.scope + +(dict + key: (_) @local.definition.type) + +(key_value + keyword: (_) @local.definition.parameter) + +(key_value + value: (macro + (identifier)*)* @local.reference) diff --git a/config/neovim/store/nvim-treesitter/queries/forth/folds.scm b/config/neovim/store/nvim-treesitter/queries/forth/folds.scm new file mode 100644 index 00000000..443abb30 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/forth/folds.scm @@ -0,0 +1 @@ +(word_definition) @fold diff --git a/config/neovim/store/nvim-treesitter/queries/forth/highlights.scm b/config/neovim/store/nvim-treesitter/queries/forth/highlights.scm new file mode 100644 index 00000000..1e720759 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/forth/highlights.scm @@ -0,0 +1,19 @@ +(core) @function + +(operator) @operator + +(word) @variable + +((word) @constant + (#lua-match? @constant "^[A-Z_]+$")) + +(number) @number + +(string) @string + +[ + (start_definition) + (end_definition) +] @punctuation.delimiter + +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/forth/indents.scm b/config/neovim/store/nvim-treesitter/queries/forth/indents.scm new file mode 100644 index 00000000..06775543 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/forth/indents.scm @@ -0,0 +1,3 @@ +(word_definition) @indent.begin + +(end_definition) @indent.end @indent.branch diff --git a/config/neovim/store/nvim-treesitter/queries/forth/injections.scm b/config/neovim/store/nvim-treesitter/queries/forth/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/forth/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/forth/locals.scm b/config/neovim/store/nvim-treesitter/queries/forth/locals.scm new file mode 100644 index 00000000..d91d3aa9 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/forth/locals.scm @@ -0,0 +1,3 @@ +(word) @local.reference + +(word_definition) @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/fortran/folds.scm b/config/neovim/store/nvim-treesitter/queries/fortran/folds.scm new file mode 100644 index 00000000..cedbdb63 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fortran/folds.scm @@ -0,0 +1,11 @@ +; by @oponkork +[ + (if_statement) + (where_statement) + (enum_statement) + (do_loop_statement) + (derived_type_definition) + (function) + (subroutine) + (interface) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/fortran/highlights.scm b/config/neovim/store/nvim-treesitter/queries/fortran/highlights.scm new file mode 100644 index 00000000..34b612c8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fortran/highlights.scm @@ -0,0 +1,319 @@ +; Preprocs +(preproc_file_line) @keyword.directive + +; Namespaces +(program_statement + (name) @module) + +(end_program_statement + (name) @module) + +(module_statement + (name) @module) + +(end_module_statement + (name) @module) + +(submodule_statement + (name) @module) + +(end_submodule_statement + (name) @module) + +; Includes +[ + "import" + "include" + "use" +] @keyword.import + +(import_statement + "," + [ + "all" + "none" + ] @keyword) + +; Attributes +[ + (none) + "implicit" + "intent" +] @attribute + +(implicit_statement + "type" @attribute) + +; Keywords +[ + "attributes" + "associate" + "block" + "classis" + "contains" + "default" + "dimension" + "endassociate" + "endselect" + "enumerator" + "equivalence" + "extends" + "goto" + "intrinsic" + "non_intrinsic" + "namelist" + "parameter" + "quiet" + "rank" + "save" + "selectcase" + "selectrank" + "selecttype" + "sequence" + "stop" + "target" + "typeis" +] @keyword + +[ + "class" + "enum" + "endenum" + "type" + "endtype" + "module" + "endmodule" + "submodule" + "endsubmodule" + "interface" + "endinterface" +] @keyword.type + +(default) @keyword + +; Types +(type_name) @type + +(intrinsic_type) @type.builtin + +; Qualifiers +[ + "abstract" + "allocatable" + "automatic" + "constant" + "contiguous" + "data" + "deferred" + "device" + "external" + "family" + "final" + "generic" + "global" + "grid_global" + "host" + "initial" + "local" + "local_init" + "managed" + "nopass" + "non_overridable" + "optional" + "pass" + "pinned" + "pointer" + "private" + "property" + "protected" + "public" + "shared" + "static" + "texture" + "value" + "volatile" + (procedure_qualifier) +] @keyword.modifier + +[ + "common" + "in" + "inout" + "out" +] @keyword.modifier + +; Labels +[ + (statement_label) + (statement_label_reference) +] @label + +[ + "call" + "endfunction" + "endprogram" + "endprocedure" + "endsubroutine" + "function" + "procedure" + "program" + "subroutine" +] @keyword.function + +[ + "result" + "return" +] @keyword.return + +; Functions +(function_statement + (name) @function) + +(end_function_statement + (name) @function) + +(subroutine_statement + (name) @function) + +(end_subroutine_statement + (name) @function) + +(module_procedure_statement + (name) @function) + +(end_module_procedure_statement + (name) @function) + +(subroutine_call + (identifier) @function.call) + +[ + "character" + "close" + "bind" + "format" + "open" + "print" + "read" + "write" +] @function.builtin + +; Exceptions +"error" @keyword.exception + +; Conditionals +[ + "else" + "elseif" + "elsewhere" + "endif" + "endwhere" + "if" + "then" + "where" +] @keyword.conditional + +; Repeats +[ + "do" + "concurrent" + "enddo" + "endforall" + "forall" + "while" + "continue" + "cycle" + "exit" +] @keyword.repeat + +; Variables +(identifier) @variable + +; Parameters +(keyword_argument + name: (identifier) @variable.parameter) + +(parameters + (identifier) @variable.parameter) + +; Properties +(derived_type_member_expression + (type_member) @variable.member) + +; Operators +[ + "+" + "-" + "*" + "**" + "/" + "=" + "<" + ">" + "<=" + ">=" + "==" + "/=" + "//" + (assumed_rank) +] @operator + +[ + "\\.and\\." + "\\.or\\." + "\\.eqv\\." + "\\.neqv\\." + "\\.lt\\." + "\\.gt\\." + "\\.le\\." + "\\.ge\\." + "\\.eq\\." + "\\.ne\\." + "\\.not\\." +] @keyword.operator + +; Punctuation +[ + "[" + "]" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + "<<<" + ">>>" +] @punctuation.bracket + +(array_literal + [ + "(/" + "/)" + ] @punctuation.bracket) + +[ + ":" + "," + "/" + "%" + "::" + "=>" +] @punctuation.delimiter + +; Literals +(string_literal) @string + +(number_literal) @number + +(boolean_literal) @boolean + +(null_literal) @constant.builtin + +; Comments +(comment) @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^!>")) diff --git a/config/neovim/store/nvim-treesitter/queries/fortran/indents.scm b/config/neovim/store/nvim-treesitter/queries/fortran/indents.scm new file mode 100644 index 00000000..86704c4f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fortran/indents.scm @@ -0,0 +1,27 @@ +[ + (module) + (program) + (subroutine) + (function) + ; (interface) + (if_statement) + (do_loop_statement) + (where_statement) + (derived_type_definition) + (enum) +] @indent.begin + +[ + (end_module_statement) + (end_program_statement) + (end_subroutine_statement) + (end_function_statement) + ; (end_interface_statement) + (end_if_statement) + (end_do_loop_statement) + (else_clause) + (elseif_clause) + (end_type_statement) + (end_enum_statement) + (end_where_statement) +] @indent.branch diff --git a/config/neovim/store/nvim-treesitter/queries/fsh/highlights.scm b/config/neovim/store/nvim-treesitter/queries/fsh/highlights.scm new file mode 100644 index 00000000..2354a203 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fsh/highlights.scm @@ -0,0 +1,97 @@ +[ + "(" + ")" +] @punctuation.bracket + +[ + "^" + "=" + ":" +] @operator + +[ + "#" + ".." + "*" + "->" +] @punctuation.special + +; Entities +[ + "Profile" + "Alias" + "Extension" + "Invariant" + "Instance" + "ValueSet" + "CodeSystem" + "Mapping" + "Logical" + "Resource" + "RuleSet" +] @keyword + +; Metadata Keywords +[ + "Parent" + "Title" + "Description" + "Id" + "Severity" + "InstanceOf" + "Usage" + "Source" + "XPath" + "Target" +] @keyword + +; Rule Keywords +[ + "contentReference" + "insert" + "and" + "or" + "contains" + "named" + "only" + "obeys" + "valueset" + "codes" + "from" + "include" + "exclude" + "where" + "system" + "exactly" +] @keyword.operator + +; Types +[ + "Reference" + "Canonical" +] @type.builtin + +(sd_metadata + (parent + (name))) @type + +(target_type + (name)) @type + +; Strings +(string) @string + +(multiline_string) @string + +; Constants +(strength_value) @constant + +(bool) @boolean + +(flag) @constant + +; Special Params +(code_value) @variable.parameter + +; Extras +(fsh_comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/func/highlights.scm b/config/neovim/store/nvim-treesitter/queries/func/highlights.scm new file mode 100644 index 00000000..9fd6dd82 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/func/highlights.scm @@ -0,0 +1,167 @@ +; Include +"#include" @keyword.import + +(include_path) @string + +; Preproc +"#pragma" @keyword.directive + +(pragma_directive + [ + "version" + "not-version" + "test-version-set" + ] @keyword.directive) + +; Keywords +[ + "asm" + "impure" + "inline" + "inline_ref" + "method_id" + "type" +] @keyword + +"return" @keyword.return + +; Conditionals +[ + "if" + "ifnot" + "else" + "elseif" + "elseifnot" + "until" +] @keyword.conditional + +; Exceptions +[ + "try" + "catch" +] @keyword.exception + +; Repeats +[ + "do" + "forall" + "repeat" + "while" +] @keyword.repeat + +; Qualifiers +[ + "const" + "global" + (var) +] @keyword.modifier + +; Variables +(identifier) @variable + +; Constants +(const_var_declarations + name: (identifier) @constant) + +; Functions/Methods +(function_definition + name: (function_name) @function) + +(function_application + function: (identifier) @function) + +(method_call + method_name: (identifier) @function.method.call) + +; Parameters +(parameter) @variable.parameter + +; Types +(type_identifier) @type + +(primitive_type) @type.builtin + +; Operators +[ + "=" + "+=" + "-=" + "*=" + "/=" + "~/=" + "^/=" + "%=" + "~%=" + "^%=" + "<<=" + ">>=" + "~>>=" + "^>>=" + "&=" + "|=" + "^=" + "==" + "<" + ">" + "<=" + ">=" + "!=" + "<=>" + "<<" + ">>" + "~>>" + "^>>" + "-" + "+" + "|" + "^" + "*" + "/" + "%" + "~/" + "^/" + "~%" + "^%" + "/%" + "&" + "~" +] @operator + +; Literals +[ + (string) + (asm_instruction) +] @string + +[ + (string_type) + (underscore) +] @character.special + +(number) @number + +; Punctuation +[ + "{" + "}" +] @punctuation.bracket + +[ + "(" + ")" + "()" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + ";" + "," + "->" +] @punctuation.delimiter + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/fusion/folds.scm b/config/neovim/store/nvim-treesitter/queries/fusion/folds.scm new file mode 100644 index 00000000..179fc160 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fusion/folds.scm @@ -0,0 +1,6 @@ +[ + (comment) + (block) + (afx_comment) + (afx_element) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/fusion/highlights.scm b/config/neovim/store/nvim-treesitter/queries/fusion/highlights.scm new file mode 100644 index 00000000..7108e570 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fusion/highlights.scm @@ -0,0 +1,132 @@ +(comment) @comment @spell + +(afx_comment) @comment @spell + +; identifiers afx +(afx_opening_element + (afx_identifier) @tag) + +(afx_closing_element + (afx_identifier) @tag) + +(afx_element_self_closing + (afx_identifier) @tag) + +(afx_attribute + (afx_property_identifier) @tag.attribute) + +(afx_text) @spell + +; identifiers eel +(eel_object_path + (eel_path_identifier) @variable.builtin + (#any-of? @variable.builtin "this" "props")) + +(eel_object_path + (eel_path_identifier) @variable) + +(eel_object_pair + key: (eel_property_name) @property) + +(eel_method_name) @function + +(eel_parameter) @variable + +; identifiers fusion +; ----------- +(path_part) @property + +(meta_property) @attribute + +(prototype_signature + "prototype" @keyword) + +(include_statement + "include" @keyword.import + (source_file) @string.special.url) + +(namespace_declaration + "namespace" @keyword.type + (alias_namespace) @module) + +(type + name: (type_name) @type) + +; tokens +; ------ +(afx_opening_element + [ + "<" + ">" + ] @punctuation.bracket) + +(afx_closing_element + [ + "<" + ">" + "/" + ] @punctuation.bracket) + +(afx_element_self_closing + [ + "<" + "/>" + ] @punctuation.bracket) + +[ + (package_name) + (alias_namespace) +] @module + +(namespace_declaration + "=" @operator) + +(assignment + "=" @operator) + +(copy + "<" @operator) + +(deletion) @operator + +(eel_binary_expression + operator: _ @operator) + +(eel_not_expression + [ + "!" + "not" + ] @operator) + +(string) @string + +(number) @number + +(boolean) @boolean + +(null) @constant.builtin + +(value_expression + start: _ @punctuation.special + end: _ @punctuation.special) + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + ":" + "." + "?" +] @punctuation.delimiter + +(eel_ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) diff --git a/config/neovim/store/nvim-treesitter/queries/fusion/indents.scm b/config/neovim/store/nvim-treesitter/queries/fusion/indents.scm new file mode 100644 index 00000000..0ba6cf75 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fusion/indents.scm @@ -0,0 +1,24 @@ +[ + (block) + (value_dsl) + (afx_element) + (afx_element_self_closing) + (eel_array) + (eel_object) +] @indent.begin + +(block + end: _ @indent.branch) + +(value_dsl + end: _ @indent.branch) + +(eel_array + end: _ @indent.branch) + +(eel_object + end: _ @indent.branch) + +(afx_closing_element) @indent.branch + +(comment) @indent.ignore diff --git a/config/neovim/store/nvim-treesitter/queries/fusion/locals.scm b/config/neovim/store/nvim-treesitter/queries/fusion/locals.scm new file mode 100644 index 00000000..d23e0ab4 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/fusion/locals.scm @@ -0,0 +1,23 @@ +; Fusion base +(block) @local.scope + +(namespace_declaration + (alias_namespace) @local.definition.namespace) + +(property + (path + (path_part) @local.definition.field)) + +(type + namespace: (package_name)? @local.definition.namespace + name: (type_name) @local.definition.type) + +; Eel Expressions +(eel_arrow_function) @local.scope + +(eel_object) @local.scope + +(eel_parameter) @local.definition.parameter + +(eel_object_pair + key: (eel_property_name) @local.definition.field) diff --git a/config/neovim/store/nvim-treesitter/queries/gdscript/folds.scm b/config/neovim/store/nvim-treesitter/queries/gdscript/folds.scm new file mode 100644 index 00000000..cda70907 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gdscript/folds.scm @@ -0,0 +1,26 @@ +[ + ; Body fold will "join" the next adjacent fold into a SUPER fold. + ; This is an issue with the grammar. + ; (body) + (if_statement) + (elif_clause) + (else_clause) + (for_statement) + (while_statement) + (class_definition) + (enum_definition) + (match_statement) + (pattern_section) + (function_definition) + (lambda) + (constructor_definition) +] @fold + +; It's nice to be able to fold the if/elif/else clauses and the entire +; if_statement. +(if_statement + (body) @fold) + +; Fold strings that are probably doc strings. +(expression_statement + (string) @fold) diff --git a/config/neovim/store/nvim-treesitter/queries/gdscript/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gdscript/highlights.scm new file mode 100644 index 00000000..9f122e47 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gdscript/highlights.scm @@ -0,0 +1,421 @@ +; Basic +(identifier) @variable + +(name) @variable + +(type + (identifier) @type) + +(comment) @comment @spell + +(string_name) @string + +(string) @string + +(float) @number.float + +(integer) @number + +(null) @constant + +(setter) @function + +(getter) @function + +(set_body + "set" @keyword.function) + +(get_body + "get" @keyword.function) + +(static_keyword) @keyword.modifier + +(tool_statement) @keyword + +(breakpoint_statement) @keyword.debug + +(inferred_type) @operator + +[ + (true) + (false) +] @boolean + +[ + (get_node) + (node_path) +] @string.special.url + +(class_name_statement + (name) @type) @keyword + +(const_statement + "const" @keyword.modifier + (name) @constant) + +(expression_statement + (string) @comment @spell) + +; Functions +(constructor_definition + "_init" @constructor) + +(function_definition + (name) @function) + +(parameters + (identifier) @variable.parameter) + +(typed_parameter + (identifier) @variable.parameter) + +(default_parameter + (identifier) @variable.parameter) + +(typed_default_parameter + (identifier) @variable.parameter) + +(call + (identifier) @function.call) + +(call + (identifier) @keyword.import + (#any-of? @keyword.import "preload" "load")) + +; Properties and Methods +; We'll use @property since that's the term Godot uses. +; But, should (source (variable_statement (name))) be @property, too? Since a +; script file is a class in gdscript. +(class_definition + (body + (variable_statement + (name) @property))) + +; Same question but for methods? +(class_definition + (body + (function_definition + (name) @function.method))) + +(attribute_call + (identifier) @function.method.call) + +(attribute_subscript + (identifier) @property) + +(attribute + (_) + (identifier) @property) + +; Identifier naming conventions +; - Make sure the following query is below the attribute queries so that it +; takes precedence on a `(type (attribute (identifier)))` +((identifier) @type + (#lua-match? @type "^[A-Z]")) + +((identifier) @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) + +; Enums +(enumerator + left: (identifier) @constant) + +; Special Builtins +((identifier) @variable.builtin + (#any-of? @variable.builtin "self" "super")) + +(attribute_call + (identifier) @keyword.operator + (#eq? @keyword.operator "new")) + +; Match Pattern +(underscore) @constant ; The "_" pattern. + +(pattern_open_ending) @operator ; The ".." pattern. + +; Alternations +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + "," + "." + ":" +] @punctuation.delimiter + +[ + "if" + "elif" + "else" + "match" +] @keyword.conditional + +(pattern_guard + "when" @keyword.conditional) + +[ + "for" + "while" + "break" + "continue" +] @keyword.repeat + +[ + "~" + "-" + "*" + "/" + "%" + "+" + "-" + "<<" + ">>" + "&" + "^" + "|" + "<" + ">" + "==" + "!=" + ">=" + "<=" + "!" + "&&" + "||" + "=" + "+=" + "-=" + "*=" + "/=" + "%=" + "&=" + "|=" + "->" +] @operator + +[ + "and" + "as" + "in" + "is" + "not" + "or" +] @keyword.operator + +[ + "pass" + "class_name" + "extends" + "signal" + "var" + "onready" + "setget" + "remote" + "master" + "puppet" + "remotesync" + "mastersync" + "puppetsync" +] @keyword + +"export" @keyword.import + +[ + "enum" + "class" +] @keyword.type + +"func" @keyword.function + +"return" @keyword.return + +"await" @keyword.coroutine + +(call + (identifier) @keyword.coroutine + (#eq? @keyword.coroutine "yield")) + +; Builtins +; generated from +; - godot commit: fb10e67fef +; - https://github.com/godotengine/godot/blob/fb10e67fef/doc/classes +; - https://github.com/godotengine/godot/blob/fb10e67fef/doc/classes/@GlobalScope.xml +; - https://github.com/godotengine/godot/blob/fb10e67fef/modules/gdscript/doc_classes/@GDScript.xml +; Built-in Annotations +((annotation + "@" @attribute + (identifier) @attribute) + (#any-of? @attribute + ; from modules/gdscript/doc_classes/@GDScript.xml + "export" "export_category" "export_color_no_alpha" "export_dir" "export_enum" + "export_exp_easing" "export_file" "export_flags" "export_flags_2d_navigation" + "export_flags_2d_physics" "export_flags_2d_render" "export_flags_3d_navigation" + "export_flags_3d_physics" "export_flags_3d_render" "export_flags_avoidance" "export_global_dir" + "export_global_file" "export_group" "export_multiline" "export_node_path" "export_placeholder" + "export_range" "export_subgroup" "icon" "onready" "rpc" "static_unload" "tool" "warning_ignore")) + +; Builtin Types +((identifier) @type.builtin + (#any-of? @type.builtin + ; from doc/classes/*.xml + "AABB" "Array" "Basis" "Callable" "Color" "Dictionary" "NodePath" "PackedByteArray" + "PackedColorArray" "PackedFloat32Array" "PackedFloat64Array" "PackedInt32Array" + "PackedInt64Array" "PackedStringArray" "PackedVector2Array" "PackedVector3Array" "Plane" + "Projection" "Quaternion" "RID" "Rect2" "Rect2i" "Signal" "String" "StringName" "Transform2D" + "Transform3D" "Vector2" "Vector2i" "Vector3" "Vector3i" "Vector4" "Vector4i" "bool" "float" + "int" + ; from doc/classes/@GlobalScope.xml + "AudioServer" "CameraServer" "ClassDB" "DisplayServer" "EditorInterface" "Engine" + "EngineDebugger" "GDExtensionManager" "Geometry2D" "Geometry3D" "GodotSharp" "IP" "Input" + "InputMap" "JavaClassWrapper" "JavaScriptBridge" "Marshalls" "NavigationMeshGenerator" + "NavigationServer2D" "NavigationServer3D" "OS" "Performance" "PhysicsServer2D" + "PhysicsServer2DManager" "PhysicsServer3D" "PhysicsServer3DManager" "ProjectSettings" + "RenderingServer" "ResourceLoader" "ResourceSaver" "ResourceUID" "TextServerManager" "ThemeDB" + "Time" "TranslationServer" "WorkerThreadPool" "XRServer")) + +; Builtin Funcs +(call + (identifier) @function.builtin + (#any-of? @function.builtin + ; from doc/classes/@GlobalScope.xml + "abs" "absf" "absi" "acos" "acosh" "angle_difference" "asin" "asinh" "atan" "atan2" "atanh" + "bezier_derivative" "bezier_interpolate" "bytes_to_var" "bytes_to_var_with_objects" "ceil" + "ceilf" "ceili" "clamp" "clampf" "clampi" "cos" "cosh" "cubic_interpolate" + "cubic_interpolate_angle" "cubic_interpolate_angle_in_time" "cubic_interpolate_in_time" + "db_to_linear" "deg_to_rad" "ease" "error_string" "exp" "floor" "floorf" "floori" "fmod" + "fposmod" "hash" "instance_from_id" "inverse_lerp" "is_equal_approx" "is_finite" "is_inf" + "is_instance_id_valid" "is_instance_valid" "is_nan" "is_same" "is_zero_approx" "lerp" + "lerp_angle" "lerpf" "linear_to_db" "log" "max" "maxf" "maxi" "min" "minf" "mini" "move_toward" + "nearest_po2" "pingpong" "posmod" "pow" "print" "print_rich" "print_verbose" "printerr" + "printraw" "prints" "printt" "push_error" "push_warning" "rad_to_deg" "rand_from_seed" "randf" + "randf_range" "randfn" "randi" "randi_range" "randomize" "remap" "rid_allocate_id" + "rid_from_int64" "rotate_toward" "round" "roundf" "roundi" "seed" "sign" "signf" "signi" "sin" + "sinh" "smoothstep" "snapped" "snappedf" "snappedi" "sqrt" "step_decimals" "str" "str_to_var" + "tan" "tanh" "type_convert" "type_string" "typeof" "var_to_bytes" "var_to_bytes_with_objects" + "var_to_str" "weakref" "wrap" "wrapf" "wrapi" + ; from modules/gdscript/doc_classes/@GDScript.xml + "Color8" "assert" "char" "convert" "dict_to_inst" "get_stack" "inst_to_dict" "is_instance_of" + "len" "load" "preload" "print_debug" "print_stack" "range" "type_exists") + ) + +; Builtin Constants +((identifier) @constant.builtin + (#any-of? @constant.builtin + ; from modules/gdscript/doc_classes/@GDScript.xml + "INF" "NAN" "PI" "TAU" + ; from doc/classes/@GlobalScope.xml + "CLOCKWISE" "CORNER_BOTTOM_LEFT" "CORNER_BOTTOM_RIGHT" "CORNER_TOP_LEFT" "CORNER_TOP_RIGHT" + "COUNTERCLOCKWISE" "ERR_ALREADY_EXISTS" "ERR_ALREADY_IN_USE" "ERR_BUG" "ERR_BUSY" + "ERR_CANT_ACQUIRE_RESOURCE" "ERR_CANT_CONNECT" "ERR_CANT_CREATE" "ERR_CANT_FORK" "ERR_CANT_OPEN" + "ERR_CANT_RESOLVE" "ERR_COMPILATION_FAILED" "ERR_CONNECTION_ERROR" "ERR_CYCLIC_LINK" + "ERR_DATABASE_CANT_READ" "ERR_DATABASE_CANT_WRITE" "ERR_DOES_NOT_EXIST" "ERR_DUPLICATE_SYMBOL" + "ERR_FILE_ALREADY_IN_USE" "ERR_FILE_BAD_DRIVE" "ERR_FILE_BAD_PATH" "ERR_FILE_CANT_OPEN" + "ERR_FILE_CANT_READ" "ERR_FILE_CANT_WRITE" "ERR_FILE_CORRUPT" "ERR_FILE_EOF" + "ERR_FILE_MISSING_DEPENDENCIES" "ERR_FILE_NOT_FOUND" "ERR_FILE_NO_PERMISSION" + "ERR_FILE_UNRECOGNIZED" "ERR_HELP" "ERR_INVALID_DATA" "ERR_INVALID_DECLARATION" + "ERR_INVALID_PARAMETER" "ERR_LINK_FAILED" "ERR_LOCKED" "ERR_METHOD_NOT_FOUND" + "ERR_OUT_OF_MEMORY" "ERR_PARAMETER_RANGE_ERROR" "ERR_PARSE_ERROR" "ERR_PRINTER_ON_FIRE" + "ERR_QUERY_FAILED" "ERR_SCRIPT_FAILED" "ERR_SKIP" "ERR_TIMEOUT" "ERR_UNAUTHORIZED" + "ERR_UNAVAILABLE" "ERR_UNCONFIGURED" "EULER_ORDER_XYZ" "EULER_ORDER_XZY" "EULER_ORDER_YXZ" + "EULER_ORDER_YZX" "EULER_ORDER_ZXY" "EULER_ORDER_ZYX" "FAILED" "HORIZONTAL" + "HORIZONTAL_ALIGNMENT_CENTER" "HORIZONTAL_ALIGNMENT_FILL" "HORIZONTAL_ALIGNMENT_LEFT" + "HORIZONTAL_ALIGNMENT_RIGHT" "INLINE_ALIGNMENT_BASELINE_TO" "INLINE_ALIGNMENT_BOTTOM" + "INLINE_ALIGNMENT_BOTTOM_TO" "INLINE_ALIGNMENT_CENTER" "INLINE_ALIGNMENT_CENTER_TO" + "INLINE_ALIGNMENT_IMAGE_MASK" "INLINE_ALIGNMENT_TEXT_MASK" "INLINE_ALIGNMENT_TOP" + "INLINE_ALIGNMENT_TOP_TO" "INLINE_ALIGNMENT_TO_BASELINE" "INLINE_ALIGNMENT_TO_BOTTOM" + "INLINE_ALIGNMENT_TO_CENTER" "INLINE_ALIGNMENT_TO_TOP" "JOY_AXIS_INVALID" "JOY_AXIS_LEFT_X" + "JOY_AXIS_LEFT_Y" "JOY_AXIS_MAX" "JOY_AXIS_RIGHT_X" "JOY_AXIS_RIGHT_Y" "JOY_AXIS_SDL_MAX" + "JOY_AXIS_TRIGGER_LEFT" "JOY_AXIS_TRIGGER_RIGHT" "JOY_BUTTON_A" "JOY_BUTTON_B" "JOY_BUTTON_BACK" + "JOY_BUTTON_DPAD_DOWN" "JOY_BUTTON_DPAD_LEFT" "JOY_BUTTON_DPAD_RIGHT" "JOY_BUTTON_DPAD_UP" + "JOY_BUTTON_GUIDE" "JOY_BUTTON_INVALID" "JOY_BUTTON_LEFT_SHOULDER" "JOY_BUTTON_LEFT_STICK" + "JOY_BUTTON_MAX" "JOY_BUTTON_MISC1" "JOY_BUTTON_PADDLE1" "JOY_BUTTON_PADDLE2" + "JOY_BUTTON_PADDLE3" "JOY_BUTTON_PADDLE4" "JOY_BUTTON_RIGHT_SHOULDER" "JOY_BUTTON_RIGHT_STICK" + "JOY_BUTTON_SDL_MAX" "JOY_BUTTON_START" "JOY_BUTTON_TOUCHPAD" "JOY_BUTTON_X" "JOY_BUTTON_Y" + "KEY_0" "KEY_1" "KEY_2" "KEY_3" "KEY_4" "KEY_5" "KEY_6" "KEY_7" "KEY_8" "KEY_9" "KEY_A" + "KEY_ALT" "KEY_AMPERSAND" "KEY_APOSTROPHE" "KEY_ASCIICIRCUM" "KEY_ASCIITILDE" "KEY_ASTERISK" + "KEY_AT" "KEY_B" "KEY_BACK" "KEY_BACKSLASH" "KEY_BACKSPACE" "KEY_BACKTAB" "KEY_BAR" + "KEY_BRACELEFT" "KEY_BRACERIGHT" "KEY_BRACKETLEFT" "KEY_BRACKETRIGHT" "KEY_C" "KEY_CAPSLOCK" + "KEY_CLEAR" "KEY_CODE_MASK" "KEY_COLON" "KEY_COMMA" "KEY_CTRL" "KEY_D" "KEY_DELETE" "KEY_DOLLAR" + "KEY_DOWN" "KEY_E" "KEY_END" "KEY_ENTER" "KEY_EQUAL" "KEY_ESCAPE" "KEY_EXCLAM" "KEY_F" "KEY_F1" + "KEY_F10" "KEY_F11" "KEY_F12" "KEY_F13" "KEY_F14" "KEY_F15" "KEY_F16" "KEY_F17" "KEY_F18" + "KEY_F19" "KEY_F2" "KEY_F20" "KEY_F21" "KEY_F22" "KEY_F23" "KEY_F24" "KEY_F25" "KEY_F26" + "KEY_F27" "KEY_F28" "KEY_F29" "KEY_F3" "KEY_F30" "KEY_F31" "KEY_F32" "KEY_F33" "KEY_F34" + "KEY_F35" "KEY_F4" "KEY_F5" "KEY_F6" "KEY_F7" "KEY_F8" "KEY_F9" "KEY_FAVORITES" "KEY_FORWARD" + "KEY_G" "KEY_GLOBE" "KEY_GREATER" "KEY_H" "KEY_HELP" "KEY_HOME" "KEY_HOMEPAGE" "KEY_HYPER" + "KEY_I" "KEY_INSERT" "KEY_J" "KEY_JIS_EISU" "KEY_JIS_KANA" "KEY_K" "KEY_KEYBOARD" "KEY_KP_0" + "KEY_KP_1" "KEY_KP_2" "KEY_KP_3" "KEY_KP_4" "KEY_KP_5" "KEY_KP_6" "KEY_KP_7" "KEY_KP_8" + "KEY_KP_9" "KEY_KP_ADD" "KEY_KP_DIVIDE" "KEY_KP_ENTER" "KEY_KP_MULTIPLY" "KEY_KP_PERIOD" + "KEY_KP_SUBTRACT" "KEY_L" "KEY_LAUNCH0" "KEY_LAUNCH1" "KEY_LAUNCH2" "KEY_LAUNCH3" "KEY_LAUNCH4" + "KEY_LAUNCH5" "KEY_LAUNCH6" "KEY_LAUNCH7" "KEY_LAUNCH8" "KEY_LAUNCH9" "KEY_LAUNCHA" + "KEY_LAUNCHB" "KEY_LAUNCHC" "KEY_LAUNCHD" "KEY_LAUNCHE" "KEY_LAUNCHF" "KEY_LAUNCHMAIL" + "KEY_LAUNCHMEDIA" "KEY_LEFT" "KEY_LESS" "KEY_LOCATION_LEFT" "KEY_LOCATION_RIGHT" + "KEY_LOCATION_UNSPECIFIED" "KEY_M" "KEY_MASK_ALT" "KEY_MASK_CMD_OR_CTRL" "KEY_MASK_CTRL" + "KEY_MASK_GROUP_SWITCH" "KEY_MASK_KPAD" "KEY_MASK_META" "KEY_MASK_SHIFT" "KEY_MEDIANEXT" + "KEY_MEDIAPLAY" "KEY_MEDIAPREVIOUS" "KEY_MEDIARECORD" "KEY_MEDIASTOP" "KEY_MENU" "KEY_META" + "KEY_MINUS" "KEY_MODIFIER_MASK" "KEY_N" "KEY_NONE" "KEY_NUMBERSIGN" "KEY_NUMLOCK" "KEY_O" + "KEY_OPENURL" "KEY_P" "KEY_PAGEDOWN" "KEY_PAGEUP" "KEY_PARENLEFT" "KEY_PARENRIGHT" "KEY_PAUSE" + "KEY_PERCENT" "KEY_PERIOD" "KEY_PLUS" "KEY_PRINT" "KEY_Q" "KEY_QUESTION" "KEY_QUOTEDBL" + "KEY_QUOTELEFT" "KEY_R" "KEY_REFRESH" "KEY_RIGHT" "KEY_S" "KEY_SCROLLLOCK" "KEY_SEARCH" + "KEY_SECTION" "KEY_SEMICOLON" "KEY_SHIFT" "KEY_SLASH" "KEY_SPACE" "KEY_SPECIAL" "KEY_STANDBY" + "KEY_STOP" "KEY_SYSREQ" "KEY_T" "KEY_TAB" "KEY_U" "KEY_UNDERSCORE" "KEY_UNKNOWN" "KEY_UP" + "KEY_V" "KEY_VOLUMEDOWN" "KEY_VOLUMEMUTE" "KEY_VOLUMEUP" "KEY_W" "KEY_X" "KEY_Y" "KEY_YEN" + "KEY_Z" "METHOD_FLAGS_DEFAULT" "METHOD_FLAG_CONST" "METHOD_FLAG_EDITOR" "METHOD_FLAG_NORMAL" + "METHOD_FLAG_OBJECT_CORE" "METHOD_FLAG_STATIC" "METHOD_FLAG_VARARG" "METHOD_FLAG_VIRTUAL" + "MIDI_MESSAGE_ACTIVE_SENSING" "MIDI_MESSAGE_AFTERTOUCH" "MIDI_MESSAGE_CHANNEL_PRESSURE" + "MIDI_MESSAGE_CONTINUE" "MIDI_MESSAGE_CONTROL_CHANGE" "MIDI_MESSAGE_NONE" + "MIDI_MESSAGE_NOTE_OFF" "MIDI_MESSAGE_NOTE_ON" "MIDI_MESSAGE_PITCH_BEND" + "MIDI_MESSAGE_PROGRAM_CHANGE" "MIDI_MESSAGE_QUARTER_FRAME" "MIDI_MESSAGE_SONG_POSITION_POINTER" + "MIDI_MESSAGE_SONG_SELECT" "MIDI_MESSAGE_START" "MIDI_MESSAGE_STOP" + "MIDI_MESSAGE_SYSTEM_EXCLUSIVE" "MIDI_MESSAGE_SYSTEM_RESET" "MIDI_MESSAGE_TIMING_CLOCK" + "MIDI_MESSAGE_TUNE_REQUEST" "MOUSE_BUTTON_LEFT" "MOUSE_BUTTON_MASK_LEFT" + "MOUSE_BUTTON_MASK_MB_XBUTTON1" "MOUSE_BUTTON_MASK_MB_XBUTTON2" "MOUSE_BUTTON_MASK_MIDDLE" + "MOUSE_BUTTON_MASK_RIGHT" "MOUSE_BUTTON_MIDDLE" "MOUSE_BUTTON_NONE" "MOUSE_BUTTON_RIGHT" + "MOUSE_BUTTON_WHEEL_DOWN" "MOUSE_BUTTON_WHEEL_LEFT" "MOUSE_BUTTON_WHEEL_RIGHT" + "MOUSE_BUTTON_WHEEL_UP" "MOUSE_BUTTON_XBUTTON1" "MOUSE_BUTTON_XBUTTON2" "OK" "OP_ADD" "OP_AND" + "OP_BIT_AND" "OP_BIT_NEGATE" "OP_BIT_OR" "OP_BIT_XOR" "OP_DIVIDE" "OP_EQUAL" "OP_GREATER" + "OP_GREATER_EQUAL" "OP_IN" "OP_LESS" "OP_LESS_EQUAL" "OP_MAX" "OP_MODULE" "OP_MULTIPLY" + "OP_NEGATE" "OP_NOT" "OP_NOT_EQUAL" "OP_OR" "OP_POSITIVE" "OP_POWER" "OP_SHIFT_LEFT" + "OP_SHIFT_RIGHT" "OP_SUBTRACT" "OP_XOR" "PROPERTY_HINT_ARRAY_TYPE" + "PROPERTY_HINT_COLOR_NO_ALPHA" "PROPERTY_HINT_DIR" "PROPERTY_HINT_ENUM" + "PROPERTY_HINT_ENUM_SUGGESTION" "PROPERTY_HINT_EXPRESSION" "PROPERTY_HINT_EXP_EASING" + "PROPERTY_HINT_FILE" "PROPERTY_HINT_FLAGS" "PROPERTY_HINT_GLOBAL_DIR" + "PROPERTY_HINT_GLOBAL_FILE" "PROPERTY_HINT_GLOBAL_SAVE_FILE" + "PROPERTY_HINT_HIDE_QUATERNION_EDIT" "PROPERTY_HINT_INT_IS_OBJECTID" + "PROPERTY_HINT_INT_IS_POINTER" "PROPERTY_HINT_LAYERS_2D_NAVIGATION" + "PROPERTY_HINT_LAYERS_2D_PHYSICS" "PROPERTY_HINT_LAYERS_2D_RENDER" + "PROPERTY_HINT_LAYERS_3D_NAVIGATION" "PROPERTY_HINT_LAYERS_3D_PHYSICS" + "PROPERTY_HINT_LAYERS_3D_RENDER" "PROPERTY_HINT_LAYERS_AVOIDANCE" "PROPERTY_HINT_LINK" + "PROPERTY_HINT_LOCALE_ID" "PROPERTY_HINT_LOCALIZABLE_STRING" "PROPERTY_HINT_MAX" + "PROPERTY_HINT_MULTILINE_TEXT" "PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" + "PROPERTY_HINT_NODE_PATH_VALID_TYPES" "PROPERTY_HINT_NODE_TYPE" "PROPERTY_HINT_NONE" + "PROPERTY_HINT_OBJECT_ID" "PROPERTY_HINT_OBJECT_TOO_BIG" "PROPERTY_HINT_PASSWORD" + "PROPERTY_HINT_PLACEHOLDER_TEXT" "PROPERTY_HINT_RANGE" "PROPERTY_HINT_RESOURCE_TYPE" + "PROPERTY_HINT_SAVE_FILE" "PROPERTY_HINT_TYPE_STRING" "PROPERTY_USAGE_ALWAYS_DUPLICATE" + "PROPERTY_USAGE_ARRAY" "PROPERTY_USAGE_CATEGORY" "PROPERTY_USAGE_CHECKABLE" + "PROPERTY_USAGE_CHECKED" "PROPERTY_USAGE_CLASS_IS_BITFIELD" "PROPERTY_USAGE_CLASS_IS_ENUM" + "PROPERTY_USAGE_DEFAULT" "PROPERTY_USAGE_DEFERRED_SET_RESOURCE" "PROPERTY_USAGE_EDITOR" + "PROPERTY_USAGE_EDITOR_BASIC_SETTING" "PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT" + "PROPERTY_USAGE_GROUP" "PROPERTY_USAGE_HIGH_END_GFX" "PROPERTY_USAGE_INTERNAL" + "PROPERTY_USAGE_KEYING_INCREMENTS" "PROPERTY_USAGE_NEVER_DUPLICATE" + "PROPERTY_USAGE_NIL_IS_VARIANT" "PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT" "PROPERTY_USAGE_NONE" + "PROPERTY_USAGE_NO_EDITOR" "PROPERTY_USAGE_NO_INSTANCE_STATE" "PROPERTY_USAGE_READ_ONLY" + "PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT" "PROPERTY_USAGE_RESTART_IF_CHANGED" + "PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE" "PROPERTY_USAGE_SCRIPT_VARIABLE" "PROPERTY_USAGE_SECRET" + "PROPERTY_USAGE_STORAGE" "PROPERTY_USAGE_STORE_IF_NULL" "PROPERTY_USAGE_SUBGROUP" + "PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED" "SIDE_BOTTOM" "SIDE_LEFT" "SIDE_RIGHT" "SIDE_TOP" + "TYPE_AABB" "TYPE_ARRAY" "TYPE_BASIS" "TYPE_BOOL" "TYPE_CALLABLE" "TYPE_COLOR" "TYPE_DICTIONARY" + "TYPE_FLOAT" "TYPE_INT" "TYPE_MAX" "TYPE_NIL" "TYPE_NODE_PATH" "TYPE_OBJECT" + "TYPE_PACKED_BYTE_ARRAY" "TYPE_PACKED_COLOR_ARRAY" "TYPE_PACKED_FLOAT32_ARRAY" + "TYPE_PACKED_FLOAT64_ARRAY" "TYPE_PACKED_INT32_ARRAY" "TYPE_PACKED_INT64_ARRAY" + "TYPE_PACKED_STRING_ARRAY" "TYPE_PACKED_VECTOR2_ARRAY" "TYPE_PACKED_VECTOR3_ARRAY" "TYPE_PLANE" + "TYPE_PROJECTION" "TYPE_QUATERNION" "TYPE_RECT2" "TYPE_RECT2I" "TYPE_RID" "TYPE_SIGNAL" + "TYPE_STRING" "TYPE_STRING_NAME" "TYPE_TRANSFORM2D" "TYPE_TRANSFORM3D" "TYPE_VECTOR2" + "TYPE_VECTOR2I" "TYPE_VECTOR3" "TYPE_VECTOR3I" "TYPE_VECTOR4" "TYPE_VECTOR4I" "VERTICAL" + "VERTICAL_ALIGNMENT_BOTTOM" "VERTICAL_ALIGNMENT_CENTER" "VERTICAL_ALIGNMENT_FILL" + "VERTICAL_ALIGNMENT_TOP")) diff --git a/config/neovim/store/nvim-treesitter/queries/gdscript/indents.scm b/config/neovim/store/nvim-treesitter/queries/gdscript/indents.scm new file mode 100644 index 00000000..36b989f9 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gdscript/indents.scm @@ -0,0 +1,78 @@ +[ + (lambda) + (function_definition) + (constructor_definition) + (for_statement) + (while_statement) + (if_statement) + (class_definition) + (match_statement) + (pattern_section) + (setget) + (match_body) + (set_body) + (get_body) +] @indent.begin + +[ + (elif_clause) + (else_clause) +] @indent.branch + +[ + (string) + (comment) + (array) + (dictionary) + (parenthesized_expression) + (ERROR) +] @indent.auto + +[ + (pass_statement) + (continue_statement) + (break_statement) + (return_statement) +] @indent.dedent + +[ + (ERROR + "[") + (ERROR + "(") + (ERROR + "{") +] @indent.begin + +; This only works with expanded tabs. +; ((parameters) @indent.align (#set! indent.open_delimiter "(") (#set! indent.close_delimiter ")")) +; ((arguments) @indent.align (#set! indent.open_delimiter "(") (#set! indent.close_delimiter ")")) +; The following queries either do not agree with the current body parsing or are +; attempted workarounds. Specifically as the last statement of a body. Opening +; a new line in between statements works well. +; +; The overall experience is poor, so I've opted for @indent.auto. +; +; The gdscript parser will need to be patched to accommodate more interactive +; edits. As far as I can tell the parser greedily consumes whitespace +; as a zero-width token which causes trouble when inserting indents. +; This indents correctly with tabs. +; (arguments) @indent.begin +; (parameters) @indent.begin +; (array) @indent.begin +; (dictionary) @indent.begin +; (parenthesized_expression) @indent.begin +; Partial workaround for when the cursor is on the bracket character and a newline +; is created with . Without this the newline is opened with extra +; indentation. +; (body (_ (array "]" @indent.end) ) _) +; Problematic behaviors occur at the last statement of a body. +; with @dedent: +; - [ | ] i will dedent ] to 0. +; - [ +; ]| o will open new line at correct indentation. +; with @auto: +; - [ | ] i same +; - [ +; ]| o will open new line with extra indent. +;(body (_ (array "]" @indent.auto) ) .) diff --git a/config/neovim/store/nvim-treesitter/queries/gdscript/injections.scm b/config/neovim/store/nvim-treesitter/queries/gdscript/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gdscript/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/gdscript/locals.scm b/config/neovim/store/nvim-treesitter/queries/gdscript/locals.scm new file mode 100644 index 00000000..796ec187 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gdscript/locals.scm @@ -0,0 +1,121 @@ +; Scopes +[ + (if_statement) + (elif_clause) + (else_clause) + (for_statement) + (while_statement) + (function_definition) + (constructor_definition) + (class_definition) + (match_statement) + (pattern_section) + (lambda) + (get_body) + (set_body) +] @local.scope + +; Parameters +(parameters + (identifier) @local.definition.parameter) + +(default_parameter + (identifier) @local.definition.parameter) + +(typed_parameter + (identifier) @local.definition.parameter) + +(typed_default_parameter + (identifier) @local.definition.parameter) + +; Signals +; Can gdscript 2 signals be considered fields? +(signal_statement + (name) @local.definition.field) + +; Variable Definitions +(const_statement + (name) @local.definition.constant) + +; onready and export variations are only properties. +(variable_statement + (name) @local.definition.var) + +(setter) @local.reference + +(getter) @local.reference + +; Function Definition +((function_definition + (name) @local.definition.function) + (#set! "definition.function.scope" "parent")) + +; Lambda +; lambda names are not accessible and are only for debugging. +(lambda + (name) @local.definition.function) + +; Source +(class_name_statement + (name) @local.definition.type) + +(source + (variable_statement + (name) @local.definition.field)) + +(source + (onready_variable_statement + (name) @local.definition.field)) + +(source + (export_variable_statement + (name) @local.definition.field)) + +; Class +((class_definition + (name) @local.definition.type) + (#set! "definition.type.scope" "parent")) + +(class_definition + (body + (variable_statement + (name) @local.definition.field))) + +(class_definition + (body + (onready_variable_statement + (name) @local.definition.field))) + +(class_definition + (body + (export_variable_statement + (name) @local.definition.field))) + +(class_definition + (body + (signal_statement + (name) @local.definition.field))) + +; Although a script is also a class, let's only define functions in an inner class as +; methods. +((class_definition + (body + (function_definition + (name) @local.definition.method))) + (#set! "definition.method.scope" "parent")) + +; Enum +(enum_definition + (name) @local.definition.enum) + +; Repeat +(for_statement + . + (identifier) @local.definition.var) + +; Match Statement +(pattern_binding + (identifier) @local.definition.var) + +; References +(identifier) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/gdshader/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gdshader/highlights.scm new file mode 100644 index 00000000..c93fd472 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gdshader/highlights.scm @@ -0,0 +1,142 @@ +[ + "render_mode" + "shader_type" + "group_uniforms" + "global" + "instance" + "const" + "varying" + "uniform" +] @keyword + +"struct" @keyword.type + +[ + (precision_qualifier) + (interpolation_qualifier) +] @keyword.modifier + +[ + "in" + "out" + "inout" +] @keyword.modifier + +[ + "while" + "for" +] @keyword.repeat + +[ + "continue" + "break" + "return" +] @keyword.return + +[ + "if" + "else" + "switch" + "case" + "default" +] @keyword.conditional + +[ + "#" + "include" +] @keyword.directive + +(string) @string + +[ + "=" + "+=" + "-=" + "!" + "~" + "+" + "-" + "*" + "/" + "%" + "||" + "&&" + "|" + "^" + "&" + "==" + "!=" + ">" + ">=" + "<=" + "<" + "<<" + ">>" + "++" + "--" +] @operator + +(boolean) @boolean + +(integer) @number + +(float) @number.float + +[ + "." + "," + ";" +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(builtin_type) @type.builtin + +(ident_type) @type.definition + +[ + (shader_type) + (render_mode) + (hint_name) +] @attribute + +(builtin_variable) @constant.builtin + +(builtin_function) @function.builtin + +(group_uniforms_declaration + group_name: (ident) @property + subgroup_name: (ident) @property) + +(struct_declaration + name: (ident) @type) + +(struct_member + name: (ident) @property) + +(function_declaration + name: (ident) @function) + +(parameter + name: (ident) @variable.parameter) + +(member_expr + member: (ident) @property) + +(call_expr + function: [ + (ident) + (builtin_type) + ] @function) + +(call_expr + function: (builtin_type) @function.call) + +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/gdshader/injections.scm b/config/neovim/store/nvim-treesitter/queries/gdshader/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gdshader/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/git_config/folds.scm b/config/neovim/store/nvim-treesitter/queries/git_config/folds.scm new file mode 100644 index 00000000..cb376d5b --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/git_config/folds.scm @@ -0,0 +1,2 @@ +((section) @fold + (#trim! @fold)) diff --git a/config/neovim/store/nvim-treesitter/queries/git_config/highlights.scm b/config/neovim/store/nvim-treesitter/queries/git_config/highlights.scm new file mode 100644 index 00000000..3423e1b0 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/git_config/highlights.scm @@ -0,0 +1,59 @@ +; Sections +(section_name) @type + +((section_name) @keyword.import + (#eq? @keyword.import "include")) + +((section_header + (section_name) @keyword.import + (subsection_name)) + (#eq? @keyword.import "includeIf")) + +(variable + (name) @property) + +; Operators +"=" @operator + +; Literals +(integer) @number + +[ + (true) + (false) +] @boolean + +(string) @string + +(escape_sequence) @string.escape + +((string) @string.special.path + (#lua-match? @string.special.path "^[.]?[.]?[/]")) + +((string) @string.special.path + (#lua-match? @string.special.path "^[~]")) + +(section_header + [ + "\"" + (subsection_name) + ] @string.special) + +((section_header + (section_name) @_name + (subsection_name) @string.special.url) + (#any-of? @_name "credential" "url")) + +((variable + (name) @_name + value: (string) @string.special.url) + (#eq? @_name "insteadOf")) + +; Punctuation +[ + "[" + "]" +] @punctuation.bracket + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/git_config/injections.scm b/config/neovim/store/nvim-treesitter/queries/git_config/injections.scm new file mode 100644 index 00000000..7bda6979 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/git_config/injections.scm @@ -0,0 +1,69 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((variable + (name) @_name + value: (string) @injection.content) + (#any-of? @_name "cmd" "command" "textconv" "sendmailCmd") + (#set! injection.language "bash")) + +(section + (variable + (name) @_name + value: (string) @injection.content) + (#eq? @_name "tool") + (#set! injection.language "bash")) + +(section + (section_header + (section_name) @_pager) + (variable + value: (string) @injection.content) + (#eq? @_pager "pager") + (#set! injection.language "bash")) + +(section + (section_header + (section_name) @_interactive) + (variable + (name) @_name + value: (string) @injection.content) + (#eq? @_interactive "interactive") + (#eq? @_name "diffFilter") + (#set! injection.language "bash")) + +; https://github.com/git-lfs/git-lfs +; git lfs install +(section + (section_header + (section_name) @_filter + (subsection_name) @_lfs) + (variable + (name) @_name + value: (string) @injection.content) + (#eq? @_filter "filter") + (#eq? @_lfs "lfs") + (#any-of? @_name "smudge" "process" "clean") + (#set! injection.language "bash")) + +(section + (section_header + (section_name) @_alias) + (variable + value: (string) @injection.content) + (#eq? @_alias "alias") + (#lua-match? @injection.content "^!") + (#offset! @injection.content 0 1 0 0) + (#set! injection.language "bash")) + +(section + (section_header + (section_name) @_alias) + (variable + value: (string + "\"" + "\"") @injection.content) + (#eq? @_alias "alias") + (#lua-match? @injection.content "^\"!") + (#offset! @injection.content 0 2 0 -1) + (#set! injection.language "bash")) diff --git a/config/neovim/store/nvim-treesitter/queries/git_rebase/highlights.scm b/config/neovim/store/nvim-treesitter/queries/git_rebase/highlights.scm new file mode 100644 index 00000000..248366e2 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/git_rebase/highlights.scm @@ -0,0 +1,7 @@ +((command) @keyword + (label)? @constant + (message)? @none @spell) + +(option) @operator + +(comment) @comment diff --git a/config/neovim/store/nvim-treesitter/queries/git_rebase/injections.scm b/config/neovim/store/nvim-treesitter/queries/git_rebase/injections.scm new file mode 100644 index 00000000..da262866 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/git_rebase/injections.scm @@ -0,0 +1,5 @@ +((operation + (command) @_command + (message) @injection.content) + (#set! injection.language "bash") + (#any-of? @_command "exec" "x")) diff --git a/config/neovim/store/nvim-treesitter/queries/gitattributes/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gitattributes/highlights.scm new file mode 100644 index 00000000..aec7750f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gitattributes/highlights.scm @@ -0,0 +1,55 @@ +(dir_sep) @punctuation.delimiter + +(quoted_pattern + "\"" @punctuation.special) + +(range_notation) @string.special + +(range_notation + [ + "[" + "]" + ] @punctuation.bracket) + +(wildcard) @character.special + +(range_negation) @operator + +(character_class) @constant + +(class_range + "-" @operator) + +[ + (ansi_c_escape) + (escaped_char) +] @string.escape + +(attribute + (attr_name) @variable.parameter) + +(attribute + (builtin_attr) @variable.builtin) + +[ + (attr_reset) + (attr_unset) + (attr_set) +] @operator + +(boolean_value) @boolean + +(string_value) @string + +(macro_tag) @keyword.directive + +(macro_def + macro_name: (_) @property) + +; we do not lint syntax errors +; [ +; (pattern_negation) +; (redundant_escape) +; (trailing_slash) +; ] @error +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/gitattributes/injections.scm b/config/neovim/store/nvim-treesitter/queries/gitattributes/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gitattributes/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/gitattributes/locals.scm b/config/neovim/store/nvim-treesitter/queries/gitattributes/locals.scm new file mode 100644 index 00000000..2471b8bc --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gitattributes/locals.scm @@ -0,0 +1,8 @@ +(macro_def + (attr_name) @local.definition.macro) + +(attribute + (attr_name) @local.reference) + +(attribute + (builtin_attr) @local.reference) diff --git a/config/neovim/store/nvim-treesitter/queries/gitcommit/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gitcommit/highlights.scm new file mode 100644 index 00000000..4542413c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gitcommit/highlights.scm @@ -0,0 +1,51 @@ +(comment) @comment + +(generated_comment) @comment + +(title) @markup.heading + +; (text) @none +(branch) @markup.link + +(change) @keyword + +(filepath) @string.special.url + +(arrow) @punctuation.delimiter + +(subject) @markup.heading @spell + +(overflow) @comment.warning @spell + +(subject + (subject_prefix) @function @nospell) + +(prefix + (type) @keyword @nospell) + +(prefix + (scope) @variable.parameter @nospell) + +(prefix + [ + "(" + ")" + ":" + ] @punctuation.delimiter) + +(prefix + "!" @punctuation.special) + +(message) @spell + +(trailer + (token) @label) + +; (trailer (value) @none) +(breaking_change + (token) @comment.error) + +(breaking_change + (value) @none @spell) + +(scissor) @comment diff --git a/config/neovim/store/nvim-treesitter/queries/gitcommit/injections.scm b/config/neovim/store/nvim-treesitter/queries/gitcommit/injections.scm new file mode 100644 index 00000000..5613d7e8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gitcommit/injections.scm @@ -0,0 +1,5 @@ +((diff) @injection.content + (#set! injection.language "diff")) + +((rebase_command) @injection.content + (#set! injection.language "git_rebase")) diff --git a/config/neovim/store/nvim-treesitter/queries/gitignore/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gitignore/highlights.scm new file mode 100644 index 00000000..10ae3f30 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gitignore/highlights.scm @@ -0,0 +1,33 @@ +(comment) @comment @spell + +[ + (directory_separator) + (directory_separator_escaped) +] @punctuation.delimiter + +[ + (wildcard_char_single) + (wildcard_chars) + (wildcard_chars_allow_slash) + (bracket_negation) +] @operator + +(negation) @punctuation.special + +[ + (pattern_char_escaped) + (bracket_char_escaped) +] @string.escape + +; bracket expressions +[ + "[" + "]" +] @punctuation.bracket + +(bracket_char) @constant + +(bracket_range + "-" @operator) + +(bracket_char_class) @constant.builtin diff --git a/config/neovim/store/nvim-treesitter/queries/gleam/folds.scm b/config/neovim/store/nvim-treesitter/queries/gleam/folds.scm new file mode 100644 index 00000000..b4cd225e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gleam/folds.scm @@ -0,0 +1,7 @@ +; Folds +[ + (case) + (function) + (anonymous_function) + (type_definition) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/gleam/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gleam/highlights.scm new file mode 100644 index 00000000..8754f3ed --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gleam/highlights.scm @@ -0,0 +1,201 @@ +; Keywords +[ + "as" + "let" + "panic" + "todo" + "type" + "use" +] @keyword + +; Function Keywords +"fn" @keyword.function + +; Imports +"import" @keyword.import + +; Conditionals +[ + "case" + "if" +] @keyword.conditional + +; Exceptions +"assert" @keyword.exception + +; Punctuation +[ + "(" + ")" + "<<" + ">>" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + "," + "." + ":" + "->" +] @punctuation.delimiter + +"#" @punctuation.special + +; Operators +[ + "%" + "&&" + "*" + "*." + "+" + "+." + "-" + "-." + ".." + "/" + "/." + "<" + "<." + "<=" + "<=." + "=" + "==" + ">" + ">." + ">=" + ">=." + "|>" + "||" +] @operator + +; Identifiers +(identifier) @variable + +; Comments +(comment) @comment @spell + +[ + (module_comment) + (statement_comment) +] @comment.documentation @spell + +; Unused Identifiers +[ + (discard) + (hole) +] @comment + +; Modules & Imports +(module) @module + +(import + alias: ((identifier) @module)?) + +(remote_type_identifier + module: (identifier) @module) + +(unqualified_import + name: (identifier) @function) + +; Strings +(string) @string + +; Bit Strings +(bit_string_segment) @string.special + +; Numbers +(integer) @number + +(float) @number.float + +; Function Parameter Labels +(function_call + arguments: (arguments + (argument + label: (label) @label))) + +(function_parameter + label: (label)? @label + name: (identifier) @variable.parameter) + +; Records +(record + arguments: (arguments + (argument + label: (label) @variable.member)?)) + +(record_pattern_argument + label: (label) @variable.member) + +(record_update_argument + label: (label) @variable.member) + +(field_access + record: (identifier) @variable + field: (label) @variable.member) + +(data_constructor_argument + (label) @variable.member) + +; Types +[ + (type_identifier) + (type_parameter) + (type_var) +] @type + +((type_identifier) @type.builtin + (#any-of? @type.builtin "Int" "Float" "String" "List")) + +; Type Qualifiers +[ + "const" + "external" + (opacity_modifier) + (visibility_modifier) +] @keyword.modifier + +; Tuples +(tuple_access + index: (integer) @operator) + +; Functions +(function + name: (identifier) @function) + +(function_call + function: (identifier) @function.call) + +(function_call + function: (field_access + field: (label) @function.call)) + +; External Functions +(external_function + name: (identifier) @function) + +(external_function_body + (string) @module + . + (string) @function) + +; Constructors +(constructor_name) @type @constructor + +([ + (type_identifier) + (constructor_name) +] @constant.builtin + (#any-of? @constant.builtin "Ok" "Error")) + +; Booleans +((constructor_name) @boolean + (#any-of? @boolean "True" "False")) + +; Pipe Operator +(binary_expression + operator: "|>" + right: (identifier) @function) diff --git a/config/neovim/store/nvim-treesitter/queries/gleam/indents.scm b/config/neovim/store/nvim-treesitter/queries/gleam/indents.scm new file mode 100644 index 00000000..c7985450 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gleam/indents.scm @@ -0,0 +1,30 @@ +; Gleam indents similar to Rust and JavaScript +[ + (anonymous_function) + (assert) + (case) + (case_clause) + (constant) + (external_function) + (function) + (import) + (let) + (list) + (constant) + (function) + (type_definition) + (type_alias) + (todo) + (tuple) +] @indent.begin + +[ + ")" + "]" + "}" +] @indent.end @indent.branch + +; Gleam pipelines are not indented, but other binary expression chains are +((binary_expression + operator: _ @_operator) @indent.begin + (#not-eq? @_operator "|>")) diff --git a/config/neovim/store/nvim-treesitter/queries/gleam/injections.scm b/config/neovim/store/nvim-treesitter/queries/gleam/injections.scm new file mode 100644 index 00000000..11d4f5d5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gleam/injections.scm @@ -0,0 +1,7 @@ +; Comments +([ + (module_comment) + (statement_comment) + (comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/gleam/locals.scm b/config/neovim/store/nvim-treesitter/queries/gleam/locals.scm new file mode 100644 index 00000000..ba0632c0 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gleam/locals.scm @@ -0,0 +1,31 @@ +; Let Binding Definition +(let + pattern: (identifier) @local.definition) + +; List Pattern Definitions +(list_pattern + (identifier) @local.definition) + +(list_pattern + assign: (identifier) @local.definition) + +; Tuple Pattern Definition +(tuple_pattern + (identifier) @local.definition) + +; Record Pattern Definition +(record_pattern_argument + pattern: (identifier) @local.definition) + +; Function Parameter Definition +(function_parameter + name: (identifier) @local.definition) + +; References +(identifier) @local.reference + +; Function Body Scope +(function_body) @local.scope + +; Case Scope +(case_clause) @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/glimmer/folds.scm b/config/neovim/store/nvim-treesitter/queries/glimmer/folds.scm new file mode 100644 index 00000000..6502455d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/glimmer/folds.scm @@ -0,0 +1,5 @@ +[ + (element_node + (element_node_start)) + (block_statement) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/glimmer/highlights.scm b/config/neovim/store/nvim-treesitter/queries/glimmer/highlights.scm new file mode 100644 index 00000000..9f11468d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/glimmer/highlights.scm @@ -0,0 +1,117 @@ +; === Tag Names === +; Tags that start with a lower case letter are HTML tags +; We'll also use this highlighting for named blocks (which start with `:`) +((tag_name) @tag + (#lua-match? @tag "^:?[%l]")) + +; Tags that start with a capital letter are Glimmer components +((tag_name) @constructor + (#lua-match? @constructor "^%u")) + +(attribute_name) @attribute + +(string_literal) @string + +(number_literal) @number + +(boolean_literal) @boolean + +(concat_statement) @string + +; === Block Statements === +; Highlight the brackets +(block_statement_start) @tag.delimiter + +(block_statement_end) @tag.delimiter + +; Highlight `if`/`each`/`let` +(block_statement_start + path: (identifier) @keyword.conditional) + +(block_statement_end + path: (identifier) @keyword.conditional) + +((mustache_statement + (identifier) @keyword.conditional) + (#lua-match? @keyword.conditional "else")) + +; == Mustache Statements === +; Highlight the whole statement, to color brackets and separators +(mustache_statement) @tag.delimiter + +; An identifier in a mustache expression is a variable +((mustache_statement + [ + (path_expression + (identifier) @variable) + (identifier) @variable + ]) + (#not-any-of? @variable "yield" "outlet" "this" "else")) + +; As are arguments in a block statement +(block_statement_start + argument: [ + (path_expression + (identifier) @variable) + (identifier) @variable + ]) + +; As is an identifier in a block param +(block_params + (identifier) @variable) + +; As are helper arguments +((helper_invocation + argument: [ + (path_expression + (identifier) @variable) + (identifier) @variable + ]) + (#not-eq? @variable "this")) + +; `this` should be highlighted as a built-in variable +((identifier) @variable.builtin + (#eq? @variable.builtin "this")) + +; If the identifier is just "yield" or "outlet", it's a keyword +((mustache_statement + (identifier) @keyword) + (#any-of? @keyword "yield" "outlet")) + +; Helpers are functions +((helper_invocation + helper: [ + (path_expression + (identifier) @function) + (identifier) @function + ]) + (#not-any-of? @function "if" "yield")) + +((helper_invocation + helper: (identifier) @keyword.conditional) + (#eq? @keyword.conditional "if")) + +((helper_invocation + helper: (identifier) @keyword) + (#eq? @keyword "yield")) + +(hash_pair + key: (identifier) @property) + +(comment_statement) @comment @spell + +(attribute_node + "=" @operator) + +(block_params + "as" @keyword) + +(block_params + "|" @operator) + +[ + "<" + ">" + "" +] @tag.delimiter diff --git a/config/neovim/store/nvim-treesitter/queries/glimmer/indents.scm b/config/neovim/store/nvim-treesitter/queries/glimmer/indents.scm new file mode 100644 index 00000000..c1ef130c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/glimmer/indents.scm @@ -0,0 +1,34 @@ +[ + (element_node + (element_node_start)) + (element_node_void) + (block_statement + (block_statement_start)) + (mustache_statement) +] @indent.begin + +(element_node + (element_node_end + ">" @indent.end)) + +(element_node_void + "/>" @indent.end) + +[ + ">" + "/>" + "" + ">=" + "==" + "!=" + "&&" + "||" +] @operator + +; Variables +(identifier) @variable + +; Functions +(call_expression + function: (identifier) @function.call) + +; Fields +(scope_access + field: (identifier) @variable.member) + +; Literals +(string) @string + +(escape_sequence) @string.escape + +(expansion) @none + +(integer) @number + +(hex) @string.special + +(boolean) @boolean + +; Punctuation +[ + "{" + "}" + "[" + "]" + "(" + ")" +] @punctuation.bracket + +[ + "." + "," +] @punctuation.delimiter + +(expansion + [ + "$" + "${" + "}" + ] @punctuation.special) + +; Comments +(comment) @comment diff --git a/config/neovim/store/nvim-treesitter/queries/gn/indents.scm b/config/neovim/store/nvim-treesitter/queries/gn/indents.scm new file mode 100644 index 00000000..82f44711 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gn/indents.scm @@ -0,0 +1,12 @@ +[ + (block) + (parenthesized_expression) +] @indent.begin + +[ + "}" + ")" + "]" +] @indent.end @indent.branch + +(comment) @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/gn/injections.scm b/config/neovim/store/nvim-treesitter/queries/gn/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gn/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/gn/locals.scm b/config/neovim/store/nvim-treesitter/queries/gn/locals.scm new file mode 100644 index 00000000..eecb3426 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gn/locals.scm @@ -0,0 +1,6 @@ +[ + (source_file) + (block) +] @local.scope + +(identifier) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/gnuplot/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gnuplot/highlights.scm new file mode 100644 index 00000000..884c7263 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gnuplot/highlights.scm @@ -0,0 +1,648 @@ +; highlights.scm +(comment) @comment @spell + +(identifier) @variable + +[ + "-" + "+" + "~" + "!" + "$" + "|" + "**" + "*" + "/" + "%" + "==" + "!=" + "<" + "<=" + ">" + ">=" + "<<" + ">>" + "&" + "^" + "&&" + "||" + "=" + "," + "." +] @operator + +[ + "eq" + "ne" +] @keyword.operator + +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +"sum" @function.builtin + +[ + "for" + "in" + "do" + "while" +] @keyword.repeat + +[ + (c_break) + (c_cd) + (c_clear) + "evaluate" + "fit" + "help" + "load" + "lower" + "print" + (c_replot) + (c_reread) + "reset" + "splot" + "cmd" + "test" + "undefine" + "vfill" +] @keyword + +(c_pause + "pause" @keyword + "mouse" @variable.member + _? @attribute + ("," + _ @attribute)?) + +(c_plot + "plot" @keyword) + +(c_show + "show" @keyword + "plot"? @attribute) + +(c_stats + "stats" @keyword + ("name" + (_))? @variable.member) + +[ + "via" + "inverse" + "sample" +] @keyword.function + +[ + "if" + "else" +] @keyword.conditional + +(plot_element + "axes"? @variable.member) + +(cntrparam + "auto"? @variable.member) + +(colorbox + "origin"? @attribute) + +(contourfill + "auto"? @variable.member) + +(format + _? @attribute + (_) + _? @attribute) + +(key + "auto"? @variable.member) + +(style ; TODO: complete + [ + "arrow" + "boxplot" + ("data" + [ + (_) + "spiderplot" @attribute + ]) + "fs" + "function" + "line" + "circle" + "rectangle" + "ellipse" + "parallelaxis" + ; (spiderplot) ; TODO: complete + "textbox" + ("watchpoint" + "labels" @attribute + (_)?) + ] @variable.member) + +(terminal + "name" @variable.member) + +; TODO: complete terminals in grammar and then simplify its options here +(t_cairolatex + [ + "eps" + "pdf" + "png" + "standalone" + "input" + "blacktext" + "colortext" + "colourtext" + ("header" + (_)) + "mono" + "color" + "background" + "rounded" + "butt" + ]* @attribute) + +; (t_canvas) +; (t_cgm) +; (t_context) +; (t_domterm) +; (t_dumb) +; (t_dxf) +; (t_emf) +; (t_epscairo) +; (t_epslatex) +; (t_fig) +; (t_gif) +; (t_hpgl) +; (t_jpeg) +; (t_lua) +; (t_pc15) +; (t_pdfcairo) +; (t_png) +; (t_pngcairo) +; (t_postscript) +; (t_pslatex) +; (t_pstricks) +; (t_qt) +; (t_sixelgd) +; (t_svg [(font_spec)]* @attribute) +; (t_tek4xxx) +; (t_texdraw) +; (t_tikz) +; (t_tkcanvas) +(plot_style + [ + "lines" + "points" + "lp" + "financebars" + "dots" + "impulses" + "labels" + "surface" + "steps" + "fsteps" + "histeps" + "arrows" + "vectors" + "sectors" + "contourfill" + "errorbar" + "errorlines" + "parallelaxes" + "boxes" + "boxerrorbars" + "boxxyerror" + "isosurface" + "boxplot" + "candlesticks" + "circles" + "zerrorfill" + "ellipses" + "filledcurves" + "fillsteps" + "histograms" + "image" + "spiderplot" + "pm3d" + "rgbalpha" + "rgbimage" + "polygons" + "table" + "mask" + ] @attribute) + +[ + "tc" + "fc" + "fs" + "lc" + "ls" + "lw" + "lt" + "pt" + "ps" + "pi" + "pn" + "dt" + "as" + "start" + "cycles" + "saturation" + "interval" + "format" + "keywidth" + "samplen" + "columns" + "title" + "notitle" + "every" + "index" + "using" + "with" + "frac" + "cb" + "arg" + "prefix" + "output" + "primary" + "specular" + "spec2" + "firstlinetype" + "width" + "height" + "expand" + "array" + "dx" + "dy" + "dz" + "filetype" + "center" + "record" +] @variable.member + +; Workaround because formatter cannot handle 300 list nodes +[ + (angles) + (clip) + (colorsequence) + (contour) + (encoding) + (mapping) + (xdata) + (theta) + "wall" + "on" + "off" + "opaque" + "inside" + "outside" + "margin" + "cen" + "lef" + "rig" + "top" + "bot" + "lr" + "a" + "maxcols" + "maxrows" + "autojustify" + "overlap" + "spread" + "wrap" + "swarm" + "range" + "label" + "mixed" + "triangles" + "insidecolor" + "noinsidecolor" + "cycle" + "tics" + "ztics" + "cbtics" + "user" + "front" + "back" + "bdefault" + "time" + "palette" + "terminal" + "onecolor" + "invert" + "reverse" + "writeback" + "extend" + "restore" + "linear" + "cubicspline" + "bspline" + "points" + "order" + "levels" + "sorted" + "autofreq" + "add" + "inout" + "axis" + "mirror" + "type" + "rowsfirst" + "columnsfirst" + "downwards" + "upwards" + "prevnext" + "gray" + "color" + "gamma" + "defined" + "cubehelix" + "model" + "maxcolors" + "file" + "colormap" + "rgbformulae" + "viridis" + "positive" + "negative" + "nops_allcF" + "ps_allcF" + "quiet" + "full" + "trip" + "numbers" + "small" + "large" + "fullwidth" + "append" + "bind" + "errors" + "session" + "behind" + "polar" + "layerdefault" + "locale" + "axes" + "fix" + "keepfix" + "noextend" + "head" + "fixed" + "filled" + "nofilled" + "absolute" + "at" + "relative" + "enhanced" + "border" + "noborder" + "rgbcolor" + "empty" + "black" + "bgnd" + "nodraw" + "size" + "new" + "first" + "second" + "screen" + "graph" + "character" + "trianglepattern" + "undefined" + "noundefined" + "altdiagonal" + "bentover" + "vertical" + "horizontal" + "square" + "ratio" + "noratio" + "solid" + "transparent" + "pattern" + "from" + "to_rto" + "length" + "angle" + "columnheaders" + "fortran" + "nofpe_trap" + "missing" + "separator" + "commentschars" + "log" + "rangelimited" + "offset" + "nooffset" + "scale" + "font" + "point" + "nopoint" + "boxed" + "noboxed" + "hypertext" + "defaults" + "keyentry" + "splines" + "qnorm" + "gauss" + "cauchy" + "exp" + "box" + "hann" + "implicit" + "explicit" + "rotate" + "by" + "parallel" + "norotate" + "map" + "projection" + "equal" + "azimuth" + "nohidden3d" + "nocontours" + "nosurface" + "colornames" + "functions" + "variables" + "version" + "nologfile" + "logfile" + "fit_out" + "errorvariables" + "covariancevariables" + "errorscaling" + "prescale" + "maxiter" + "limit" + "limit_abs" + "start-lambda" + "lambda-factor" + "script" + "clip" + "fontscale" + "lighting" + "depthorder" + "interpolate" + "corners2color" + "flush" + "scanorder" + "hidden3d" + "clipcb" + "layout" + "margins" + "spacing" + "smooth" + "binary" + "skip" + "bins" + "binrange" + "binwidth" + "binvalue" + "mask" + "convexhull" + "concavehull" + "volatile" + "zsort" + "nonuniform" + "sparse" + "matrix" +] @attribute + +[ + "x1" + "x2" + "y1" + "y2" + "y" + "r" + "z" + "xy" + "xz" + "yz" + "xyz" + "x1y1" + "x2y2" + "x1y2" + "x2y1" + "columnheader" + "seconds" + "minutes" + "hours" + "days" + "weeks" + "months" + "years" + "cm" + "in" + "discrete" + "incremental" + "default" + "long" + "nogrid" + "unique" + "frequency" + "fnormal" + "cumulative" + "cnormal" + "csplines" + "acsplines" + "mcsplines" + "path" + "bezier" + "sbezier" + "unwrap" + "kdensity" + "closed" + "between" + "above" + "below" + "variable" + "pixels" + "RGB" + "CMY" + "HSV" + "base" + "begin" + "center" + "end" + "ftriangles" + "clip1in" + "clip4in" + "c2c" + "retrace" + "whitespace" + "tab" + "comma" + "push" + "pop" + "flipx" + "flipy" + "flipz" +] @variable.member + +(colorspec + "palette" @attribute) + +(datafile_modifiers + "origin"? @variable.member) + +((datafile_modifiers + filetype: (identifier) @variable.member) + (#any-of? @variable.member + "avs" "bin" "edf" "ehf" "gif" "gpbin" "jpeg" "jpg" "png" "raw" "rgb" "auto")) + +(macro) @function.macro + +(datablock) @function.macro + +(function + name: (identifier) @function) + +((function + name: (identifier) @function.builtin) + (#any-of? @function.builtin + "abs" "acos" "acosh" "airy" "arg" "asin" "asinh" "atan" "atan2" "atanh" "besj0" "besj1" "besjn" + "besy0" "besy1" "besyn" "besi0" "besi1" "besin" "cbrt" "ceil" "conj" "cos" "cosh" "EllipticK" + "EllipticE" "EllipticPi" "erf" "erfc" "exp" "expint" "floor" "gamma" "ibeta" "inverf" "igamma" + "imag" "int" "invnorm" "invibeta" "invigamma" "LambertW" "lambertw" "lgamma" "lnGamma" "log" + "log10" "norm" "rand" "real" "round" "sgn" "sin" "sinh" "sqrt" "SynchrotronF" "tan" "tanh" + "uigamma" "voigt" "zeta" "cerf" "cdawson" "faddeva" "erfi" "FresnelC" "FresnelS" "VP" "VP_fwhm" + "Ai" "Bi" "BesselH1" "BesselH2" "BesselJ" "BesselY" "BesselI" "BesselK" "gprintf" "sprintf" + "strlen" "strstrt" "substr" "strptime" "srtftime" "system" "trim" "word" "words" "time" + "timecolumn" "tm_hour" "tm_mday" "tm_min" "tm_mon" "tm_sec" "tm_wday" "tm_week" "tm_yday" + "tm_year" "weekday_iso" "weekday_cdc" "column" "columnhead" "exists" "hsv2rgb" "index" "palette" + "rgbcolor" "stringcolumn" "valid" "value" "voxel")) + +((identifier) @variable.builtin + (#match? @variable.builtin + "^\\w+_(records|headers|outofrange|invalid|blank|blocks|columns|column_header|index_(min|max)(_x|_y)?|(min|max)(_x|_y)?|mean(_err)?(_x|_y)?|stddev(_err)?(_x|_y)?)$")) + +((identifier) @variable.builtin + (#match? @variable.builtin + "^\\w+_(sdd(_x|_y)?|(lo|up)_quartile(_x|_y)?|median(_x|_y)?|sum(sq)?(_x|_y)?|skewness(_err)?(_x|_y)?)$")) + +((identifier) @variable.builtin + (#match? @variable.builtin + "^\\w+_(kurtosis(_err)?(_x|_y)?|adev(_x|_y)?|correlation|slope(_err)?|intercept(_err)?|sumxy|pos(_min|_max)_y|size(_x|_y))$")) + +((identifier) @variable.builtin + (#match? @variable.builtin "^((GPVAL|MOUSE|FIT)_\\w+|GNUTERM|NaN|VoxelDistance|GridDistance|pi)$")) + +(array_def + "array" @keyword.function) + +(array + (identifier) @function) + +(number) @number + +(string_literal) @string diff --git a/config/neovim/store/nvim-treesitter/queries/gnuplot/injections.scm b/config/neovim/store/nvim-treesitter/queries/gnuplot/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gnuplot/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/go/folds.scm b/config/neovim/store/nvim-treesitter/queries/go/folds.scm new file mode 100644 index 00000000..44b452de --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/go/folds.scm @@ -0,0 +1,19 @@ +[ + (const_declaration) + (expression_switch_statement) + (expression_case) + (default_case) + (type_switch_statement) + (type_case) + (for_statement) + (func_literal) + (function_declaration) + (if_statement) + (import_declaration) + (method_declaration) + (type_declaration) + (var_declaration) + (composite_literal) + (literal_element) + (block) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/go/highlights.scm b/config/neovim/store/nvim-treesitter/queries/go/highlights.scm new file mode 100644 index 00000000..62497b0c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/go/highlights.scm @@ -0,0 +1,239 @@ +; Forked from tree-sitter-go +; Copyright (c) 2014 Max Brunsfeld (The MIT License) +; +; Identifiers +(type_identifier) @type + +(type_spec + name: (type_identifier) @type.definition) + +(field_identifier) @property + +(identifier) @variable + +(package_identifier) @module + +(parameter_declaration + (identifier) @variable.parameter) + +(variadic_parameter_declaration + (identifier) @variable.parameter) + +(label_name) @label + +(const_spec + name: (identifier) @constant) + +; Function calls +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (selector_expression + field: (field_identifier) @function.method.call)) + +; Function definitions +(function_declaration + name: (identifier) @function) + +(method_declaration + name: (field_identifier) @function.method) + +(method_elem + name: (field_identifier) @function.method) + +; Constructors +((call_expression + (identifier) @constructor) + (#lua-match? @constructor "^[nN]ew.+$")) + +((call_expression + (identifier) @constructor) + (#lua-match? @constructor "^[mM]ake.+$")) + +; Operators +[ + "--" + "-" + "-=" + ":=" + "!" + "!=" + "..." + "*" + "*" + "*=" + "/" + "/=" + "&" + "&&" + "&=" + "&^" + "&^=" + "%" + "%=" + "^" + "^=" + "+" + "++" + "+=" + "<-" + "<" + "<<" + "<<=" + "<=" + "=" + "==" + ">" + ">=" + ">>" + ">>=" + "|" + "|=" + "||" + "~" +] @operator + +; Keywords +[ + "break" + "const" + "continue" + "default" + "defer" + "goto" + "range" + "select" + "var" + "fallthrough" +] @keyword + +[ + "type" + "struct" + "interface" +] @keyword.type + +"func" @keyword.function + +"return" @keyword.return + +"go" @keyword.coroutine + +"for" @keyword.repeat + +[ + "import" + "package" +] @keyword.import + +[ + "else" + "case" + "switch" + "if" +] @keyword.conditional + +; Builtin types +[ + "chan" + "map" +] @type.builtin + +((type_identifier) @type.builtin + (#any-of? @type.builtin + "any" "bool" "byte" "comparable" "complex128" "complex64" "error" "float32" "float64" "int" + "int16" "int32" "int64" "int8" "rune" "string" "uint" "uint16" "uint32" "uint64" "uint8" + "uintptr")) + +; Builtin functions +((identifier) @function.builtin + (#any-of? @function.builtin + "append" "cap" "clear" "close" "complex" "copy" "delete" "imag" "len" "make" "max" "min" "new" + "panic" "print" "println" "real" "recover")) + +; Delimiters +"." @punctuation.delimiter + +"," @punctuation.delimiter + +":" @punctuation.delimiter + +";" @punctuation.delimiter + +"(" @punctuation.bracket + +")" @punctuation.bracket + +"{" @punctuation.bracket + +"}" @punctuation.bracket + +"[" @punctuation.bracket + +"]" @punctuation.bracket + +; Literals +(interpreted_string_literal) @string + +(raw_string_literal) @string + +(rune_literal) @string + +(escape_sequence) @string.escape + +(int_literal) @number + +(float_literal) @number.float + +(imaginary_literal) @number + +[ + (true) + (false) +] @boolean + +[ + (nil) + (iota) +] @constant.builtin + +(keyed_element + . + (literal_element + (identifier) @variable.member)) + +(field_declaration + name: (field_identifier) @variable.member) + +; Comments +(comment) @comment @spell + +; Doc Comments +(source_file + . + (comment)+ @comment.documentation) + +(source_file + (comment)+ @comment.documentation + . + (const_declaration)) + +(source_file + (comment)+ @comment.documentation + . + (function_declaration)) + +(source_file + (comment)+ @comment.documentation + . + (type_declaration)) + +(source_file + (comment)+ @comment.documentation + . + (var_declaration)) + +; Spell +((interpreted_string_literal) @spell + (#not-has-parent? @spell import_spec)) diff --git a/config/neovim/store/nvim-treesitter/queries/go/indents.scm b/config/neovim/store/nvim-treesitter/queries/go/indents.scm new file mode 100644 index 00000000..b26811c8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/go/indents.scm @@ -0,0 +1,37 @@ +[ + (import_declaration) + (const_declaration) + (var_declaration) + (type_declaration) + (func_literal) + (literal_value) + (expression_case) + (communication_case) + (type_case) + (default_case) + (block) + (call_expression) + (parameter_list) + (struct_type) +] @indent.begin + +"}" @indent.branch + +(const_declaration + ")" @indent.branch) + +(import_spec_list + ")" @indent.branch) + +(var_declaration + ")" @indent.branch) + +[ + "}" + ")" +] @indent.end + +(parameter_list + ")" @indent.branch) + +(comment) @indent.ignore diff --git a/config/neovim/store/nvim-treesitter/queries/go/injections.scm b/config/neovim/store/nvim-treesitter/queries/go/injections.scm new file mode 100644 index 00000000..38ba3e11 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/go/injections.scm @@ -0,0 +1,39 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +(call_expression + (selector_expression) @_function + (#any-of? @_function + "regexp.Match" "regexp.MatchReader" "regexp.MatchString" "regexp.Compile" "regexp.CompilePOSIX" + "regexp.MustCompile" "regexp.MustCompilePOSIX") + (argument_list + . + [ + (raw_string_literal) + (interpreted_string_literal) + ] @injection.content + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "regex"))) + +((comment) @injection.content + (#match? @injection.content "/\\*!([a-zA-Z]+:)?re2c") + (#set! injection.language "re2c")) + +((call_expression + function: (selector_expression + field: (field_identifier) @_method) + arguments: (argument_list + . + (interpreted_string_literal) @injection.content)) + (#any-of? @_method "Printf" "Sprintf" "Fatalf" "Scanf" "Errorf" "Skipf" "Logf") + (#set! injection.language "printf")) + +((call_expression + function: (selector_expression + field: (field_identifier) @_method) + arguments: (argument_list + (_) + . + (interpreted_string_literal) @injection.content)) + (#any-of? @_method "Fprintf" "Fscanf" "Appendf" "Sscanf") + (#set! injection.language "printf")) diff --git a/config/neovim/store/nvim-treesitter/queries/go/locals.scm b/config/neovim/store/nvim-treesitter/queries/go/locals.scm new file mode 100644 index 00000000..608c4582 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/go/locals.scm @@ -0,0 +1,88 @@ +((function_declaration + name: (identifier) @local.definition.function) ; @function + ) + +((method_declaration + name: (field_identifier) @local.definition.method) ; @function.method + ) + +(short_var_declaration + left: (expression_list + (identifier) @local.definition.var)) + +(var_spec + name: (identifier) @local.definition.var) + +(parameter_declaration + (identifier) @local.definition.var) + +(variadic_parameter_declaration + (identifier) @local.definition.var) + +(for_statement + (range_clause + left: (expression_list + (identifier) @local.definition.var))) + +(const_declaration + (const_spec + name: (identifier) @local.definition.var)) + +(type_declaration + (type_spec + name: (type_identifier) @local.definition.type)) + +; reference +(identifier) @local.reference + +(type_identifier) @local.reference + +(field_identifier) @local.reference + +((package_identifier) @local.reference + (#set! reference.kind "namespace")) + +(package_clause + (package_identifier) @local.definition.namespace) + +(import_spec_list + (import_spec + name: (package_identifier) @local.definition.namespace)) + +; Call references +((call_expression + function: (identifier) @local.reference) + (#set! reference.kind "call")) + +((call_expression + function: (selector_expression + field: (field_identifier) @local.reference)) + (#set! reference.kind "call")) + +((call_expression + function: (parenthesized_expression + (identifier) @local.reference)) + (#set! reference.kind "call")) + +((call_expression + function: (parenthesized_expression + (selector_expression + field: (field_identifier) @local.reference))) + (#set! reference.kind "call")) + +; Scopes +(func_literal) @local.scope + +(source_file) @local.scope + +(function_declaration) @local.scope + +(if_statement) @local.scope + +(block) @local.scope + +(expression_switch_statement) @local.scope + +(for_statement) @local.scope + +(method_declaration) @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/godot_resource/folds.scm b/config/neovim/store/nvim-treesitter/queries/godot_resource/folds.scm new file mode 100644 index 00000000..911798f5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/godot_resource/folds.scm @@ -0,0 +1 @@ +(section) @fold diff --git a/config/neovim/store/nvim-treesitter/queries/godot_resource/highlights.scm b/config/neovim/store/nvim-treesitter/queries/godot_resource/highlights.scm new file mode 100644 index 00000000..15cbadd1 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/godot_resource/highlights.scm @@ -0,0 +1,49 @@ +(identifier) @variable + +(section + (identifier) @tag) + +(section + [ + "[" + "]" + ] @tag.delimiter) + +(attribute + (identifier) @tag.attribute) + +(property + (path) @property) + +(constructor + (identifier) @constructor) + +(string) @string + +(integer) @number + +(float) @number.float + +[ + (true) + (false) +] @boolean + +(null) @constant.builtin + +(array + [ + "[" + "]" + ] @punctuation.bracket) + +[ + "(" + ")" + "{" + "}" +] @punctuation.bracket + +"=" @operator + +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/godot_resource/injections.scm b/config/neovim/store/nvim-treesitter/queries/godot_resource/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/godot_resource/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/godot_resource/locals.scm b/config/neovim/store/nvim-treesitter/queries/godot_resource/locals.scm new file mode 100644 index 00000000..b946a5ee --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/godot_resource/locals.scm @@ -0,0 +1 @@ +(section) @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/gomod/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gomod/highlights.scm new file mode 100644 index 00000000..29cbe657 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gomod/highlights.scm @@ -0,0 +1,20 @@ +[ + "require" + "replace" + "go" + "toolchain" + "exclude" + "retract" + "module" +] @keyword + +"=>" @operator + +(comment) @comment @spell + +(module_path) @string.special.url + +[ + (version) + (go_version) +] @string diff --git a/config/neovim/store/nvim-treesitter/queries/gomod/injections.scm b/config/neovim/store/nvim-treesitter/queries/gomod/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gomod/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/gosum/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gosum/highlights.scm new file mode 100644 index 00000000..f20c5f17 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gosum/highlights.scm @@ -0,0 +1,32 @@ +[ + "alpha" + "beta" + "dev" + "pre" + "rc" + "+incompatible" +] @keyword + +(module_path) @string.special.url + +(module_version) @string.special + +(hash_version) @attribute + +(hash) @string.special.symbol + +[ + (number) + (number_with_decimal) + (hex_number) +] @number + +(checksum + "go.mod" @string) + +[ + ":" + "." + "-" + "/" +] @punctuation.delimiter diff --git a/config/neovim/store/nvim-treesitter/queries/gotmpl/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gotmpl/highlights.scm new file mode 100644 index 00000000..ba69dc07 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gotmpl/highlights.scm @@ -0,0 +1,107 @@ +; Identifiers +[ + (field) + (field_identifier) +] @variable.member + +(variable) @variable + +; Function calls +(function_call + function: (identifier) @function) + +(method_call + method: (selector_expression + field: (field_identifier) @function)) + +; Builtin functions +(function_call + function: (identifier) @function.builtin + (#any-of? @function.builtin + "and" "call" "html" "index" "slice" "js" "len" "not" "or" "print" "printf" "println" "urlquery" + "eq" "ne" "lt" "ge" "gt" "ge")) + +; Operators +[ + "|" + ":=" +] @operator + +; Delimiters +[ + "." + "," +] @punctuation.delimiter + +[ + "{{" + "}}" + "{{-" + "-}}" + ")" + "(" +] @punctuation.bracket + +; Actions +(if_action + [ + "if" + "else" + "else if" + "end" + ] @keyword.conditional) + +(range_action + [ + "range" + "else" + "end" + ] @keyword.repeat) + +(template_action + "template" @function.builtin) + +(block_action + [ + "block" + "end" + ] @keyword.directive) + +(define_action + [ + "define" + "end" + ] @keyword.directive.define) + +(with_action + [ + "with" + "else" + "end" + ] @keyword.conditional) + +; Literals +[ + (interpreted_string_literal) + (raw_string_literal) +] @string + +(rune_literal) @string.special.symbol + +(escape_sequence) @string.escape + +[ + (int_literal) + (imaginary_literal) +] @number + +(float_literal) @number.float + +[ + (true) + (false) +] @boolean + +(nil) @constant.builtin + +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/gotmpl/injections.scm b/config/neovim/store/nvim-treesitter/queries/gotmpl/injections.scm new file mode 100644 index 00000000..3cfc2636 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gotmpl/injections.scm @@ -0,0 +1,31 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +; {{"put" | printf "%s%s" "out" | printf "%q"}} +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#eq? @_function "printf") + (#set! injection.language "printf")) + +; {{ js "var a = 1 + 1" }} +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#eq? @_function "js") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "javascript")) + +; {{ html "

hello

" }} +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#eq? @_function "html") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "html")) diff --git a/config/neovim/store/nvim-treesitter/queries/gowork/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gowork/highlights.scm new file mode 100644 index 00000000..bca9a5f8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gowork/highlights.scm @@ -0,0 +1,14 @@ +[ + "replace" + "go" + "use" +] @keyword + +"=>" @operator + +(comment) @comment @spell + +[ + (version) + (go_version) +] @string diff --git a/config/neovim/store/nvim-treesitter/queries/gowork/injections.scm b/config/neovim/store/nvim-treesitter/queries/gowork/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gowork/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/gpg/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gpg/highlights.scm new file mode 100644 index 00000000..f0283442 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gpg/highlights.scm @@ -0,0 +1,54 @@ +(option + . + _ @keyword) + +(option + ("no-" @variable.parameter)? + (name) @variable.parameter) + +(string + (content) @string) + +[ + (value) + "clear" +] @string.special + +(url) @string.special.url + +(key) @constant + +[ + (number) + (expire_time) + (iso_time) +] @number + +(format) @character.special + +"sensitive:" @keyword.modifier + +(filter_name) @variable.parameter + +(filter_scope) @module + +(filter_property) @property + +(filter_value) @string + +[ + (filter_op0) + (filter_op1) + (filter_lc) + "=" +] @operator + +"!" @punctuation.special + +[ + "\"" + "'" + "," +] @punctuation.delimiter + +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/gpg/injections.scm b/config/neovim/store/nvim-treesitter/queries/gpg/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gpg/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/graphql/highlights.scm b/config/neovim/store/nvim-treesitter/queries/graphql/highlights.scm new file mode 100644 index 00000000..c1ee501c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/graphql/highlights.scm @@ -0,0 +1,163 @@ +; Types +;------ +(scalar_type_definition + (name) @type) + +(object_type_definition + (name) @type) + +(interface_type_definition + (name) @type) + +(union_type_definition + (name) @type) + +(enum_type_definition + (name) @type) + +(input_object_type_definition + (name) @type) + +(scalar_type_extension + (name) @type) + +(object_type_extension + (name) @type) + +(interface_type_extension + (name) @type) + +(union_type_extension + (name) @type) + +(enum_type_extension + (name) @type) + +(input_object_type_extension + (name) @type) + +(named_type + (name) @type) + +; Directives +;----------- +(directive_definition + "@" @attribute + (name) @attribute) + +(directive) @attribute + +; Properties +;----------- +(field + (name) @property) + +(field + (alias + (name) @property)) + +(field_definition + (name) @property) + +(object_value + (object_field + (name) @property)) + +(enum_value + (name) @property) + +; Variable Definitions and Arguments +;----------------------------------- +(operation_definition + (name) @variable) + +(fragment_name + (name) @variable) + +(input_fields_definition + (input_value_definition + (name) @variable.parameter)) + +(argument + (name) @variable.parameter) + +(arguments_definition + (input_value_definition + (name) @variable.parameter)) + +(variable_definition + (variable) @variable.parameter) + +(argument + (value + (variable) @variable)) + +; Constants +;---------- +(string_value) @string + +(int_value) @number + +(float_value) @number.float + +(boolean_value) @boolean + +; Literals +;--------- +(description + (string_value) @string.documentation @spell) + +(comment) @comment @spell + +(directive_location + (executable_directive_location) @type.builtin) + +(directive_location + (type_system_directive_location) @type.builtin) + +; Keywords +;---------- +[ + "query" + "mutation" + "subscription" + "fragment" + "scalar" + "input" + "extend" + "directive" + "schema" + "on" + "repeatable" + "implements" +] @keyword + +[ + "enum" + "union" + "type" + "interface" +] @keyword.type + +; Punctuation +;------------ +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +"=" @operator + +"|" @punctuation.delimiter + +"&" @punctuation.delimiter + +":" @punctuation.delimiter + +"..." @punctuation.special + +"!" @punctuation.special diff --git a/config/neovim/store/nvim-treesitter/queries/graphql/indents.scm b/config/neovim/store/nvim-treesitter/queries/graphql/indents.scm new file mode 100644 index 00000000..fcffeb82 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/graphql/indents.scm @@ -0,0 +1,11 @@ +[ + (definition) + (selection) +] @indent.begin + +[ + "{" + "}" +] @indent.branch + +"}" @indent.end diff --git a/config/neovim/store/nvim-treesitter/queries/graphql/injections.scm b/config/neovim/store/nvim-treesitter/queries/graphql/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/graphql/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/groovy/folds.scm b/config/neovim/store/nvim-treesitter/queries/groovy/folds.scm new file mode 100644 index 00000000..354861a6 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/groovy/folds.scm @@ -0,0 +1,6 @@ +[ + (argument_list) + (closure) + (list) + (map) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/groovy/highlights.scm b/config/neovim/store/nvim-treesitter/queries/groovy/highlights.scm new file mode 100644 index 00000000..de62bbb4 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/groovy/highlights.scm @@ -0,0 +1,267 @@ +[ + "!instanceof" + "assert" + "extends" + "instanceof" + "package" +] @keyword + +"class" @keyword.type + +[ + "!in" + "as" + "in" +] @keyword.operator + +[ + "case" + "default" + "else" + "if" + "switch" +] @keyword.conditional + +[ + "catch" + "finally" + "try" +] @keyword.exception + +"def" @keyword.function + +"import" @keyword.import + +[ + "for" + "while" + (break) + (continue) +] @keyword.repeat + +"return" @keyword.return + +[ + "true" + "false" +] @boolean + +(null) @constant.builtin + +"this" @variable.builtin + +[ + "int" + "char" + "short" + "long" + "boolean" + "float" + "double" + "void" +] @type.builtin + +[ + "final" + "private" + "protected" + "public" + "static" + "synchronized" +] @keyword.modifier + +(comment) @comment @spell + +(shebang) @keyword.directive + +(string) @string + +(string + (escape_sequence) @string.escape) + +(string + (interpolation + "$" @punctuation.special)) + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + ":" + "," + "." +] @punctuation.delimiter + +(number_literal) @number + +(identifier) @variable + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z_]+")) + +[ + "%" + "*" + "/" + "+" + "-" + "<<" + ">>" + ">>>" + ".." + "..<" + "<..<" + "<.." + "<" + "<=" + ">" + ">=" + "==" + "!=" + "<=>" + "===" + "!==" + "=~" + "==~" + "&" + "^" + "|" + "&&" + "||" + "?:" + "+" + "*" + ".&" + ".@" + "?." + "*." + "*" + "*:" + "++" + "--" + "!" +] @operator + +(string + "/" @string) + +(ternary_op + ([ + "?" + ":" + ]) @keyword.conditional.ternary) + +(map + (map_item + key: (identifier) @variable.parameter)) + +(parameter + type: (identifier) @type + name: (identifier) @variable.parameter) + +(generic_param + name: (identifier) @variable.parameter) + +(declaration + type: (identifier) @type) + +(function_definition + type: (identifier) @type) + +(function_declaration + type: (identifier) @type) + +(class_definition + name: (identifier) @type) + +(class_definition + superclass: (identifier) @type) + +(generic_param + superclass: (identifier) @type) + +(type_with_generics + (identifier) @type) + +(type_with_generics + (generics + (identifier) @type)) + +(generics + [ + "<" + ">" + ] @punctuation.bracket) + +(generic_parameters + [ + "<" + ">" + ] @punctuation.bracket) + +; TODO: Class literals with PascalCase +(declaration + "=" @operator) + +(assignment + "=" @operator) + +(function_call + function: (identifier) @function) + +(function_call + function: (dotted_identifier + (identifier) @function .)) + +(function_call + (argument_list + (map_item + key: (identifier) @variable.parameter))) + +(juxt_function_call + function: (identifier) @function) + +(juxt_function_call + function: (dotted_identifier + (identifier) @function .)) + +(juxt_function_call + (argument_list + (map_item + key: (identifier) @variable.parameter))) + +(function_definition + function: (identifier) @function) + +(function_declaration + function: (identifier) @function) + +(annotation) @function.macro + +(annotation + (identifier) @function.macro) + +"@interface" @function.macro + +(groovy_doc) @comment.documentation @spell + +(groovy_doc + [ + (groovy_doc_param) + (groovy_doc_throws) + (groovy_doc_tag) + ] @string.special @nospell) + +(groovy_doc + (groovy_doc_param + (identifier) @variable.parameter) @nospell) + +(groovy_doc + (groovy_doc_throws + (identifier) @type @nospell)) diff --git a/config/neovim/store/nvim-treesitter/queries/groovy/indents.scm b/config/neovim/store/nvim-treesitter/queries/groovy/indents.scm new file mode 100644 index 00000000..888d5010 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/groovy/indents.scm @@ -0,0 +1,35 @@ +[ + (closure) + (map) + (list) + (argument_list) + (parameter_list) + (for_parameters) +] @indent.begin + +; (function_definition "(" @indent.begin) +(closure + "}" @indent.end) + +(argument_list + ")" @indent.end) + +(for_parameters + ")" @indent.end) + +((for_loop + body: (_) @_body) @indent.begin + (#not-has-type? @_body closure)) + +; TODO: while, try +(list + "]" @indent.end) + +(map + "]" @indent.end) + +[ + "}" + ")" + "]" +] @indent.branch diff --git a/config/neovim/store/nvim-treesitter/queries/groovy/injections.scm b/config/neovim/store/nvim-treesitter/queries/groovy/injections.scm new file mode 100644 index 00000000..1c04c65f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/groovy/injections.scm @@ -0,0 +1,5 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((groovy_doc) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/groovy/locals.scm b/config/neovim/store/nvim-treesitter/queries/groovy/locals.scm new file mode 100644 index 00000000..23cb5f0c --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/groovy/locals.scm @@ -0,0 +1,6 @@ +(function_definition) @local.scope + +(parameter + name: (identifier) @local.definition.parameter) + +(identifier) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/gstlaunch/highlights.scm b/config/neovim/store/nvim-treesitter/queries/gstlaunch/highlights.scm new file mode 100644 index 00000000..90729f87 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/gstlaunch/highlights.scm @@ -0,0 +1,35 @@ +[ + "!" + "=" +] @operator + +[ + "," + "." + ";" + "/" +] @punctuation.delimiter + +[ + "(" + ")" +] @punctuation.bracket + +(property + key: (identifier) @variable.member) + +(value) @string + +(string_literal) @string + +(cap + . + (identifier) @string + . + (identifier) @string) + +(simple_element + type: (_) @type) + +(bin + type: (_) @type) diff --git a/config/neovim/store/nvim-treesitter/queries/hack/highlights.scm b/config/neovim/store/nvim-treesitter/queries/hack/highlights.scm new file mode 100644 index 00000000..bb9d2a55 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hack/highlights.scm @@ -0,0 +1,366 @@ +(variable) @variable + +(identifier) @variable + +((variable) @variable.builtin + (#eq? @variable.builtin "$this")) + +(braced_expression) @none + +(scoped_identifier + (qualified_identifier + (identifier) @type)) + +[ + (comment) + (heredoc) +] @comment @spell + +((comment) @comment.documentation + (#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$")) + +"function" @keyword.function + +[ + "implements" + "using" + "attribute" + "const" + "extends" + "insteadof" +] @keyword + +[ + "class" + "type" + "interface" + "namespace" +] @keyword.type + +[ + "async" + "await" +] @keyword.coroutine + +[ + "use" + "include" + "include_once" + "require" + "require_once" +] @keyword.import + +[ + "new" + "print" + "echo" + "newtype" + "clone" + "as" +] @keyword.operator + +"return" @keyword.return + +[ + (abstract_modifier) + (final_modifier) + (static_modifier) + (visibility_modifier) + (xhp_modifier) +] @keyword.modifier + +[ + "shape" + "tuple" + (array_type) + "bool" + "float" + "int" + "string" + "arraykey" + "void" + "nonnull" + "mixed" + "dynamic" + "noreturn" +] @type.builtin + +(null) @constant.builtin + +[ + (true) + (false) +] @boolean + +(type_specifier) @type + +(new_expression + (_) @type) + +(alias_declaration + "newtype" + . + (_) @type) + +(alias_declaration + "type" + . + (_) @type) + +(class_declaration + name: (identifier) @type) + +(type_parameter + name: (identifier) @type) + +(collection + (qualified_identifier + (identifier) @type .)) + +[ + "@required" + "@lateinit" + (attribute_modifier) +] @attribute + +[ + "=" + "??=" + ".=" + "|=" + "^=" + "&=" + "<<=" + ">>=" + "+=" + "-=" + "*=" + "/=" + "%=" + "**=" + "==>" + "|>" + "??" + "||" + "&&" + "|" + "^" + "&" + "==" + "!=" + "===" + "!==" + "<" + ">" + "<=" + ">=" + "<=>" + "<<" + ">>" + "->" + "+" + "-" + "." + "*" + "/" + "%" + "**" + "++" + "--" + "!" + "?:" + "=" + "??=" + ".=" + "|=" + "^=" + "&=" + "<<=" + ">>=" + "+=" + "-=" + "*=" + "/=" + "%=" + "**=" + "=>" + ; type modifiers + "@" + "?" + "~" +] @operator + +(integer) @number + +(float) @number.float + +(parameter + (variable) @variable.parameter) + +(call_expression + function: (qualified_identifier + (identifier) @function.call .)) + +(call_expression + function: (scoped_identifier + (identifier) @function.call .)) + +(call_expression + function: (selection_expression + (qualified_identifier + (identifier) @function.method.call .))) + +(qualified_identifier + (_) @module + . + (_)) + +(use_statement + (qualified_identifier + (_) @module .) + (use_clause)) + +(use_statement + (use_type + "namespace") + (use_clause + (qualified_identifier + (identifier) @module .) + alias: (identifier)? @module)) + +(use_statement + (use_type + "const") + (use_clause + (qualified_identifier + (identifier) @constant .) + alias: (identifier)? @constant)) + +(use_statement + (use_type + "function") + (use_clause + (qualified_identifier + (identifier) @function .) + alias: (identifier)? @function)) + +(use_statement + (use_type + "type") + (use_clause + (qualified_identifier + (identifier) @type .) + alias: (identifier)? @type)) + +(use_clause + (use_type + "namespace") + (qualified_identifier + (_) @module .) + alias: (identifier)? @module) + +(use_clause + (use_type + "function") + (qualified_identifier + (_) @function .) + alias: (identifier)? @function) + +(use_clause + (use_type + "const") + (qualified_identifier + (_) @constant .) + alias: (identifier)? @constant) + +(use_clause + (use_type + "type") + (qualified_identifier + (_) @type .) + alias: (identifier)? @type) + +(function_declaration + name: (identifier) @function) + +(method_declaration + name: (identifier) @function.method) + +(type_arguments + [ + "<" + ">" + ] @punctuation.bracket) + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "<<" + ">>" +] @punctuation.bracket + +(xhp_open + [ + "<" + ">" + ] @tag.delimiter) + +(xhp_close + [ + "" + ] @tag.delimiter) + +[ + "." + ";" + "::" + ":" + "," +] @punctuation.delimiter + +(qualified_identifier + "\\" @punctuation.delimiter) + +(ternary_expression + [ + "?" + ":" + ] @keyword.conditional.ternary) + +[ + "if" + "else" + "elseif" + "switch" + "case" +] @keyword.conditional + +[ + "try" + "catch" + "finally" +] @keyword.exception + +[ + "for" + "while" + "foreach" + "do" + "continue" + "break" +] @keyword.repeat + +[ + (string) + (xhp_string) +] @string + +[ + (xhp_open) + (xhp_close) +] @tag diff --git a/config/neovim/store/nvim-treesitter/queries/hare/folds.scm b/config/neovim/store/nvim-treesitter/queries/hare/folds.scm new file mode 100644 index 00000000..58b10bfd --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hare/folds.scm @@ -0,0 +1,18 @@ +[ + (imports) + (function_declaration) + (enum_type) + (struct_type) + (tuple_type) + (union_type) + (block) + (if_statement) + (for_statement) + (call_expression) + (switch_expression) + (match_expression) + (case) + (array_literal) + (struct_literal) + (tuple_literal) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/hare/highlights.scm b/config/neovim/store/nvim-treesitter/queries/hare/highlights.scm new file mode 100644 index 00000000..68a3e188 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hare/highlights.scm @@ -0,0 +1,272 @@ +; Variables +(identifier) @variable + +; Types +(type) @type + +(scoped_type_identifier + (identifier) + . + (identifier) @type) + +(struct_literal + . + (identifier) @type) + +(builtin_type) @type.builtin + +; Constants +((identifier) @constant + (#lua-match? @constant "^[A-Z_]+$")) + +; Includes +[ + "use" + "export" +] @keyword.import + +(use_statement + (scoped_type_identifier + (identifier) @module)) + +(use_statement + (identifier) @module + "{") + +(use_statement + . + (identifier) @module .) + +((scoped_type_identifier + path: (_) @module) + (#set! "priority" 105)) + +; Keywords +[ + "def" + "let" +] @keyword + +[ + "enum" + "struct" + "union" + "type" +] @keyword.type + +"fn" @keyword.function + +[ + "defer" + "yield" + "return" +] @keyword.return + +[ + "as" + "is" +] @keyword.operator + +; Typedefs +(type_declaration + "type" + (identifier) @type.definition + . + "=") + +; Qualifiers +[ + "const" + "static" + "nullable" +] @keyword.modifier + +; Attributes +[ + "@fini" + "@init" + "@test" + "@noreturn" + "@packed" + (declaration_attribute) +] @attribute + +; Labels +((label) @label + (#set! "priority" 105)) + +; Functions +(function_declaration + "fn" + . + (identifier) @function) + +(call_expression + . + (identifier) @function.call) + +(call_expression + . + (scoped_type_identifier + . + (identifier) + . + "::" + . + (identifier) @function.method.call)) + +((call_expression + . + (identifier) @function.builtin) + (#any-of? @function.builtin "align" "assert" "free" "len" "offset" "size")) + +(size_expression + "size" @function.builtin) + +((function_declaration + "fn" + . + (identifier) @constructor) + (#eq? @constructor "init")) + +((call_expression + . + (identifier) @constructor) + (#eq? @constructor "init")) + +; Parameters +(parameter + (_) @variable.parameter + . + ":") + +; Fields +((member_expression + "." + (_) @variable.member) + (#set! "priority" 105)) + +(field + . + (identifier) @variable.member) + +(field_assignment + . + (identifier) @variable.member) + +; Repeats +"for" @keyword.repeat + +; Conditionals +[ + "if" + "else" + "break" + "switch" + "match" + "case" +] @keyword.conditional + +; Operators +[ + "+" + "-" + "*" + "/" + "%" + "||" + "&&" + "^^" + "|" + "&" + "^" + "==" + "!=" + ">" + ">=" + "<=" + "<" + "<<" + ">>" + "~" + "!" + "+=" + "-=" + "*=" + "/=" + "%=" + "<<=" + ">>=" + "|=" + "&=" + "^=" + "||=" + "&&=" + "^^=" + "=" + "?" +] @operator + +; Punctuation +[ + "{" + "}" +] @punctuation.bracket + +[ + "[" + "]" +] @punctuation.bracket + +[ + "(" + ")" +] @punctuation.bracket + +[ + ".." + "..." + "_" +] @punctuation.special + +(pointer_type + "*" @punctuation.special) + +(slice_type + "*" @punctuation.special) + +(error_type + "!" @punctuation.special) + +[ + "," + "." + ":" + ";" + "::" + "=>" +] @punctuation.delimiter + +; Literals +[ + (string) + (raw_string) +] @string + +(rune) @character + +(escape_sequence) @string.escape + +(number) @number + +(float) @number.float + +(boolean) @boolean + +[ + (void) + (null) +] @constant.builtin + +; Comments +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/hare/indents.scm b/config/neovim/store/nvim-treesitter/queries/hare/indents.scm new file mode 100644 index 00000000..d729663e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hare/indents.scm @@ -0,0 +1,44 @@ +[ + (enum_type) + (struct_type) + (tuple_type) + (union_type) + (block) + (for_statement) + (call_expression) + (case) + (array_literal) + (struct_literal) + (tuple_literal) +] @indent.begin + +(if_statement + ("(" + condition: (_)")") @indent.begin) + +[ + "}" + "]" + ")" +] @indent.end + +[ + "{" + "}" +] @indent.branch + +[ + "[" + "]" +] @indent.branch + +[ + "(" + ")" +] @indent.branch + +[ + (ERROR) + (comment) + (concatenated_string) +] @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/hare/injections.scm b/config/neovim/store/nvim-treesitter/queries/hare/injections.scm new file mode 100644 index 00000000..88a3f1cd --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hare/injections.scm @@ -0,0 +1,18 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((call_expression + . + (_) @_fnname + . + "(" + . + (_ + [ + (string_content) + (raw_string_content) + ] @injection.content) + . + ")") + (#any-of? @_fnname "compile" "regex::compile") + (#set! injection.language "regex")) diff --git a/config/neovim/store/nvim-treesitter/queries/hare/locals.scm b/config/neovim/store/nvim-treesitter/queries/hare/locals.scm new file mode 100644 index 00000000..12a214bf --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hare/locals.scm @@ -0,0 +1,65 @@ +; Scopes +[ + (module) + (function_declaration) + (if_statement) + (for_statement) + (match_expression) + (switch_expression) +] @local.scope + +; References +[ + (identifier) + (scoped_type_identifier) +] @local.reference + +; Definitions +(global_binding + (identifier) @local.definition.constant + . + ":" + (_)) + +(const_declaration + "const" + (identifier) @local.definition.constant + . + "=") + +(field + . + (identifier) @local.definition.field) + +(field_assignment + . + (identifier) @local.definition.field) + +(function_declaration + "fn" + . + (identifier) @local.definition.function) + +(parameter + (_) @local.definition.parameter + . + ":") + +(type_declaration + "type" + (identifier) @local.definition.type + . + "=") + +(type_declaration + "type" + (identifier) @local.definition.enum + . + "=" + (enum_type)) + +(let_declaration + "let" + . + (identifier) @local.definition.var + ","?) diff --git a/config/neovim/store/nvim-treesitter/queries/haskell/folds.scm b/config/neovim/store/nvim-treesitter/queries/haskell/folds.scm new file mode 100644 index 00000000..d7b820c9 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/haskell/folds.scm @@ -0,0 +1,6 @@ +[ + (apply) + (do) + (function) + (import)+ +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/haskell/highlights.scm b/config/neovim/store/nvim-treesitter/queries/haskell/highlights.scm new file mode 100644 index 00000000..975a8c18 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/haskell/highlights.scm @@ -0,0 +1,466 @@ +; ---------------------------------------------------------------------------- +; Parameters and variables +; NOTE: These are at the top, so that they have low priority, +; and don't override destructured parameters +(variable) @variable + +(pattern/wildcard) @variable + +(decl/function + patterns: (patterns + (_) @variable.parameter)) + +(expression/lambda + (_)+ @variable.parameter + "->") + +(decl/function + (infix + (pattern) @variable.parameter)) + +; ---------------------------------------------------------------------------- +; Literals and comments +(integer) @number + +(negation) @number + +(expression/literal + (float)) @number.float + +(char) @character + +(string) @string + +(comment) @comment + +(haddock) @comment.documentation + +; ---------------------------------------------------------------------------- +; Punctuation +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + "," + ";" +] @punctuation.delimiter + +; ---------------------------------------------------------------------------- +; Keywords, operators, includes +[ + "forall" + ; "∀" ; utf-8 is not cross-platform safe +] @keyword.repeat + +(pragma) @keyword.directive + +[ + "if" + "then" + "else" + "case" + "of" +] @keyword.conditional + +[ + "import" + "qualified" + "module" +] @keyword.import + +[ + (operator) + (constructor_operator) + (all_names) + (wildcard) + "." + ".." + "=" + "|" + "::" + "=>" + "->" + "<-" + "\\" + "`" + "@" +] @operator + +(module + (module_id) @module) + +[ + "where" + "let" + "in" + "class" + "instance" + "pattern" + "data" + "newtype" + "family" + "type" + "as" + "hiding" + "deriving" + "via" + "stock" + "anyclass" + "do" + "mdo" + "rec" + "infix" + "infixl" + "infixr" +] @keyword + +; ---------------------------------------------------------------------------- +; Functions and variables +(decl + [ + name: (variable) @function + names: (binding_list + (variable) @function) + ]) + +(decl/bind + name: (variable) @variable) + +; Consider signatures (and accompanying functions) +; with only one value on the rhs as variables +(decl/signature + name: (variable) @variable + type: (type)) + +((decl/signature + name: (variable) @_name + type: (type)) + . + (decl + name: (variable) @variable) + match: (_)(#eq? @_name @variable)) + +; but consider a type that involves 'IO' a decl/function +(decl/signature + name: (variable) @function + type: (type/apply + constructor: (name) @_type) + (#eq? @_type "IO")) + +((decl/signature + name: (variable) @_name + type: (type/apply + constructor: (name) @_type) + (#eq? @_type "IO")) + . + (decl + name: (variable) @function) + match: (_)(#eq? @_name @function)) + +((decl/signature) @function + . + (decl/function + name: (variable) @function)) + +(decl/bind + name: (variable) @function + (match + expression: (expression/lambda))) + +; view patterns +(view_pattern + [ + (expression/variable) @function.call + (expression/qualified + (variable) @function.call) + ]) + +; consider infix functions as operators +(infix_id + [ + (variable) @operator + (qualified + (variable) @operator) + ]) + +; decl/function calls with an infix operator +; e.g. func <$> a <*> b +(infix + left_operand: [ + (variable) @function.call + (qualified + ((module) @module + (variable) @function.call)) + ]) + +; infix operators applied to variables +((expression/variable) @variable + . + (operator)) + +((operator) + . + [ + (expression/variable) @variable + (expression/qualified + (variable) @variable) + ]) + +; infix operator function definitions +(function + (infix + left_operand: [ + (variable) @variable + (qualified + ((module) @module + (variable) @variable)) + ]) + match: (match)) + +; decl/function calls with infix operators +([ + (expression/variable) @function.call + (expression/qualified + (variable) @function.call) +] + . + (operator) @_op + (#any-of? @_op "$" "<$>" ">>=" "=<<")) + +; right hand side of infix operator +((infix + [ + (operator) + (infix_id + (variable)) + ] ; infix or `func` + . + [ + (variable) @function.call + (qualified + (variable) @function.call) + ]) + . + (operator) @_op + (#any-of? @_op "$" "<$>" "=<<")) + +; decl/function composition, arrows, monadic composition (lhs) +([ + (expression/variable) @function + (expression/qualified + (variable) @function) +] + . + (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; right hand side of infix operator +((infix + [ + (operator) + (infix_id + (variable)) + ] ; infix or `func` + . + [ + (variable) @function + (qualified + (variable) @function) + ]) + . + (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; function composition, arrows, monadic composition (rhs) +((operator) @_op + . + [ + (expression/variable) @function + (expression/qualified + (variable) @function) + ] + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")) + +; function defined in terms of a function composition +(decl/function + name: (variable) @function + (match + expression: (infix + operator: (operator) @_op + (#any-of? @_op "." ">>>" "***" ">=>" "<=<")))) + +(apply + [ + (expression/variable) @function.call + (expression/qualified + (variable) @function.call) + ]) + +; function compositions, in parentheses, applied +; lhs +(apply + . + (expression/parens + (infix + [ + (variable) @function.call + (qualified + (variable) @function.call) + ] + . + (operator)))) + +; rhs +(apply + . + (expression/parens + (infix + (operator) + . + [ + (variable) @function.call + (qualified + (variable) @function.call) + ]))) + +; variables being passed to a function call +(apply + (_) + . + [ + (expression/variable) @variable + (expression/qualified + (variable) @variable) + ]) + +; main is always a function +; (this prevents `main = undefined` from being highlighted as a variable) +(decl/bind + name: (variable) @function + (#eq? @function "main")) + +; scoped function types (func :: a -> b) +(signature + pattern: (pattern/variable) @function + type: (function)) + +; signatures that have a function type +; + binds that follow them +(decl/signature + name: (variable) @function + type: (function)) + +((decl/signature + name: (variable) @_name + type: (quantified_type)) + . + (decl/bind + (variable) @function) + (#eq? @function @_name)) + +; Treat constructor assignments (smart constructors) as functions, e.g. mkJust = Just +(bind + name: (variable) @function + match: (match + expression: (constructor))) + +; Function composition +(bind + name: (variable) @function + match: (match + expression: (infix + operator: (operator) @_op + (#eq? @_op ".")))) + +; ---------------------------------------------------------------------------- +; Types +(name) @type + +(type/unit) @type + +(type/unit + [ + "(" + ")" + ] @type) + +(type/list + [ + "[" + "]" + ] @type) + +(type/star) @type + +(constructor) @constructor + +; True or False +((constructor) @boolean + (#any-of? @boolean "True" "False")) + +; otherwise (= True) +((variable) @boolean + (#eq? @boolean "otherwise")) + +; ---------------------------------------------------------------------------- +; Quasi-quotes +(quoter) @function.call + +(quasiquote + quoter: [ + (quoter) @_name + (quoter + (qualified + id: (variable) @_name)) + ] + (#eq? @_name "qq") + body: (quasiquote_body) @string) + +; namespaced quasi-quoter +(quoter + [ + (variable) @function.call + (_ + (module) @module + . + (variable) @function.call) + ]) + +; Highlighting of quasiquote_body for other languages is handled by injections.scm +; ---------------------------------------------------------------------------- +; Exceptions/error handling +((variable) @keyword.exception + (#any-of? @keyword.exception + "error" "undefined" "try" "tryJust" "tryAny" "catch" "catches" "catchJust" "handle" "handleJust" + "throw" "throwIO" "throwTo" "throwError" "ioError" "mask" "mask_" "uninterruptibleMask" + "uninterruptibleMask_" "bracket" "bracket_" "bracketOnErrorSource" "finally" "fail" + "onException" "expectationFailure")) + +; ---------------------------------------------------------------------------- +; Debugging +((variable) @keyword.debug + (#any-of? @keyword.debug + "trace" "traceId" "traceShow" "traceShowId" "traceWith" "traceShowWith" "traceStack" "traceIO" + "traceM" "traceShowM" "traceEvent" "traceEventWith" "traceEventIO" "flushEventLog" "traceMarker" + "traceMarkerIO")) + +; ---------------------------------------------------------------------------- +; Fields +(field_name + (variable) @variable.member) + +(import_name + (name) + . + (children + (variable) @variable.member)) + +; ---------------------------------------------------------------------------- +; Spell checking +(comment) @spell diff --git a/config/neovim/store/nvim-treesitter/queries/haskell/injections.scm b/config/neovim/store/nvim-treesitter/queries/haskell/injections.scm new file mode 100644 index 00000000..59fe7335 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/haskell/injections.scm @@ -0,0 +1,76 @@ +; ----------------------------------------------------------------------------- +; General language injection +(quasiquote + (quoter) @injection.language + (quasiquote_body) @injection.content) + +((comment) @injection.content + (#set! injection.language "comment")) + +; ----------------------------------------------------------------------------- +; shakespeare library +; NOTE: doesn't support templating +; TODO: add once CoffeeScript parser is added +; ; CoffeeScript: Text.Coffee +; (quasiquote +; (quoter) @_name +; (#eq? @_name "coffee") +; ((quasiquote_body) @injection.content +; (#set! injection.language "coffeescript"))) +; CSS: Text.Cassius, Text.Lucius +(quasiquote + (quoter) @_name + (#any-of? @_name "cassius" "lucius") + (quasiquote_body) @injection.content + (#set! injection.language "css")) + +; HTML: Text.Hamlet +(quasiquote + (quoter) @_name + (#any-of? @_name "shamlet" "xshamlet" "hamlet" "xhamlet" "ihamlet") + (quasiquote_body) @injection.content + (#set! injection.language "html")) + +; JS: Text.Julius +(quasiquote + (quoter) @_name + (#any-of? @_name "js" "julius") + (quasiquote_body) @injection.content + (#set! injection.language "javascript")) + +; TS: Text.TypeScript +(quasiquote + (quoter) @_name + (#any-of? @_name "tsc" "tscJSX") + (quasiquote_body) @injection.content + (#set! injection.language "typescript")) + +; ----------------------------------------------------------------------------- +; HSX +(quasiquote + (quoter) @_name + (#eq? @_name "hsx") + (quasiquote_body) @injection.content + (#set! injection.language "html")) + +; ----------------------------------------------------------------------------- +; Inline JSON from aeson +(quasiquote + (quoter) @_name + (#eq? @_name "aesonQQ") + (quasiquote_body) @injection.content + (#set! injection.language "json")) + +; ----------------------------------------------------------------------------- +; SQL +; postgresql-simple +(quasiquote + (quoter) @injection.language + (#eq? @injection.language "sql") + (quasiquote_body) @injection.content) + +(quasiquote + (quoter) @_name + (#any-of? @_name "persistUpperCase" "persistLowerCase" "persistWith") + (quasiquote_body) @injection.content + (#set! injection.language "haskell_persistent")) diff --git a/config/neovim/store/nvim-treesitter/queries/haskell/locals.scm b/config/neovim/store/nvim-treesitter/queries/haskell/locals.scm new file mode 100644 index 00000000..7177f9de --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/haskell/locals.scm @@ -0,0 +1,9 @@ +(signature + name: (variable)) @local.definition + +(function + name: (variable)) @local.definition + +(pattern/variable) @local.definition + +(expression/variable) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/haskell_persistent/folds.scm b/config/neovim/store/nvim-treesitter/queries/haskell_persistent/folds.scm new file mode 100644 index 00000000..dd34d9f1 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/haskell_persistent/folds.scm @@ -0,0 +1 @@ +(entity_definition) @fold diff --git a/config/neovim/store/nvim-treesitter/queries/haskell_persistent/highlights.scm b/config/neovim/store/nvim-treesitter/queries/haskell_persistent/highlights.scm new file mode 100644 index 00000000..22cbf5cb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/haskell_persistent/highlights.scm @@ -0,0 +1,38 @@ +; ---------------------------------------------------------------------------- +; Literals and comments +(integer) @number + +(float) @number.float + +(char) @character + +(string) @string + +(attribute_name) @attribute + +(attribute_exclamation_mark) @attribute + +(con_unit) @string.special.symbol ; unit, as in () + +(comment) @comment @spell + +; ---------------------------------------------------------------------------- +; Keywords, operators, includes +[ + "Id" + "Primary" + "Foreign" + "deriving" +] @keyword + +"=" @operator + +; ---------------------------------------------------------------------------- +; Functions and variables +(variable) @variable + +; ---------------------------------------------------------------------------- +; Types +(type) @type + +(constructor) @constructor diff --git a/config/neovim/store/nvim-treesitter/queries/hcl/folds.scm b/config/neovim/store/nvim-treesitter/queries/hcl/folds.scm new file mode 100644 index 00000000..e0c5313a --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hcl/folds.scm @@ -0,0 +1,6 @@ +[ + (comment) + (block) + (heredoc_template) + (object) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/hcl/highlights.scm b/config/neovim/store/nvim-treesitter/queries/hcl/highlights.scm new file mode 100644 index 00000000..4fb7d1fc --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hcl/highlights.scm @@ -0,0 +1,118 @@ +; highlights.scm +[ + "!" + "\*" + "/" + "%" + "\+" + "-" + ">" + ">=" + "<" + "<=" + "==" + "!=" + "&&" + "||" +] @operator + +[ + "{" + "}" + "[" + "]" + "(" + ")" +] @punctuation.bracket + +[ + "." + ".*" + "," + "[*]" +] @punctuation.delimiter + +[ + (ellipsis) + "\?" + "=>" +] @punctuation.special + +[ + ":" + "=" +] @none + +[ + "for" + "endfor" + "in" +] @keyword.repeat + +[ + "if" + "else" + "endif" +] @keyword.conditional + +[ + (quoted_template_start) ; " + (quoted_template_end) ; " + (template_literal) ; non-interpolation/directive content +] @string + +[ + (heredoc_identifier) ; END + (heredoc_start) ; << or <<- +] @punctuation.delimiter + +[ + (template_interpolation_start) ; ${ + (template_interpolation_end) ; } + (template_directive_start) ; %{ + (template_directive_end) ; } + (strip_marker) ; ~ +] @punctuation.special + +(numeric_lit) @number + +(bool_lit) @boolean + +(null_lit) @constant + +(comment) @comment @spell + +(identifier) @variable + +(body + (block + (identifier) @keyword)) + +(body + (block + (body + (block + (identifier) @type)))) + +(function_call + (identifier) @function) + +(attribute + (identifier) @variable.member) + +; { key: val } +; +; highlight identifier keys as though they were block attributes +(object_elem + key: (expression + (variable_expr + (identifier) @variable.member))) + +; var.foo, data.bar +; +; first element in get_attr is a variable.builtin or a reference to a variable.builtin +(expression + (variable_expr + (identifier) @variable.builtin) + (get_attr + (identifier) @variable.member)) diff --git a/config/neovim/store/nvim-treesitter/queries/hcl/indents.scm b/config/neovim/store/nvim-treesitter/queries/hcl/indents.scm new file mode 100644 index 00000000..1d7dc49d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hcl/indents.scm @@ -0,0 +1,16 @@ +[ + (block) + (object) + (tuple) + (function_call) +] @indent.begin + +[ + "]" + ")" + "}" +] @indent.branch @indent.end + +(comment) @indent.auto + +(ERROR) @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/hcl/injections.scm b/config/neovim/store/nvim-treesitter/queries/hcl/injections.scm new file mode 100644 index 00000000..b68f4cae --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hcl/injections.scm @@ -0,0 +1,7 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +(heredoc_template + (template_literal) @injection.content + (heredoc_identifier) @injection.language + (#downcase! @injection.language)) diff --git a/config/neovim/store/nvim-treesitter/queries/heex/folds.scm b/config/neovim/store/nvim-treesitter/queries/heex/folds.scm new file mode 100644 index 00000000..88d4f17f --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/heex/folds.scm @@ -0,0 +1,6 @@ +; HEEx tags, components, and slots fold similar to HTML +[ + (component) + (tag) + (slot) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/heex/highlights.scm b/config/neovim/store/nvim-treesitter/queries/heex/highlights.scm new file mode 100644 index 00000000..e2fb0188 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/heex/highlights.scm @@ -0,0 +1,54 @@ +; HEEx delimiters +[ + "%>" + "--%>" + "-->" + "/>" + "" + "{" + "}" +] @tag.delimiter + +; HEEx operators are highlighted as such +"=" @operator + +; HEEx inherits the DOCTYPE tag from HTML +(doctype) @constant + +; HEEx comments are highlighted as such +(comment) @comment @spell + +; HEEx text content is treated as markup +; (text) @none +; HEEx tags and slots are highlighted as HTML +[ + (tag_name) + (slot_name) +] @tag + +; HEEx attributes are highlighted as HTML attributes +(attribute_name) @tag.attribute + +[ + (attribute_value) + (quoted_attribute_value) +] @string + +; HEEx components are highlighted as modules and function calls +(component_name + [ + (module) @type + (function) @function + "." @punctuation.delimiter + ]) diff --git a/config/neovim/store/nvim-treesitter/queries/heex/indents.scm b/config/neovim/store/nvim-treesitter/queries/heex/indents.scm new file mode 100644 index 00000000..82a2f891 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/heex/indents.scm @@ -0,0 +1,20 @@ +; HEEx tags, components, and slots indent like HTML +[ + (component) + (slot) + (tag) +] @indent.begin + +; Dedent at the end of each tag, component, and slot +[ + (end_component) + (end_slot) + (end_tag) +] @indent.branch @indent.dedent + +; Self-closing tags and components should not change +; indentation level of sibling nodes +[ + (self_closing_component) + (self_closing_tag) +] @indent.auto diff --git a/config/neovim/store/nvim-treesitter/queries/heex/injections.scm b/config/neovim/store/nvim-treesitter/queries/heex/injections.scm new file mode 100644 index 00000000..4f179ee8 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/heex/injections.scm @@ -0,0 +1,30 @@ +; directives are standalone tags like '<%= @x %>' +; +; partial_expression_values are elixir code that is part of an expression that +; spans multiple directive nodes, so they must be combined. For example: +; <%= if true do %> +;

hello, tree-sitter!

+; <% end %> +(directive + [ + (partial_expression_value) + (ending_expression_value) + ] @injection.content + (#set! injection.language "elixir") + (#set! injection.include-children) + (#set! injection.combined)) + +; Regular expression_values do not need to be combined +((directive + (expression_value) @injection.content) + (#set! injection.language "elixir")) + +; expressions live within HTML tags, and do not need to be combined +; +(expression + (expression_value) @injection.content + (#set! injection.language "elixir")) + +; HEEx comments +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/heex/locals.scm b/config/neovim/store/nvim-treesitter/queries/heex/locals.scm new file mode 100644 index 00000000..cfa239e5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/heex/locals.scm @@ -0,0 +1,13 @@ +; HEEx tags, components, and slots are references +[ + (component_name) + (slot_name) + (tag_name) +] @local.reference + +; Create a new scope within each HEEx tag, component, and slot +[ + (component) + (slot) + (tag) +] @local.scope diff --git a/config/neovim/store/nvim-treesitter/queries/helm/highlights.scm b/config/neovim/store/nvim-treesitter/queries/helm/highlights.scm new file mode 100644 index 00000000..05028ba9 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/helm/highlights.scm @@ -0,0 +1,45 @@ +; inherits: gotmpl + +; Builtin functions +(function_call + function: (identifier) @function.builtin + (#any-of? @function.builtin + "and" "or" "not" "eq" "ne" "lt" "le" "gt" "ge" "default" "required" "empty" "fail" "coalesce" + "ternary" "print" "println" "printf" "trim" "trimAll" "trimPrefix" "trimSuffix" "lower" "upper" + "title" "untitle" "repeat" "substr" "nospace" "trunc" "abbrev" "abbrevboth" "initials" + "randAlphaNum" "randAlpha" "randNumeric" "randAscii" "wrap" "wrapWith" "contains" "hasPrefix" + "hasSuffix" "quote" "squote" "cat" "indent" "nindent" "replace" "plural" "snakecase" "camelcase" + "kebabcase" "swapcase" "shuffle" "toStrings" "toDecimal" "toJson" "mustToJson" "toPrettyJson" + "mustToPrettyJson" "toRawJson" "mustToRawJson" "fromYaml" "fromJson" "fromJsonArray" + "fromYamlArray" "toYaml" "regexMatch" "mustRegexMatch" "regexFindAll" "mustRegexFinDall" + "regexFind" "mustRegexFind" "regexReplaceAll" "mustRegexReplaceAll" "regexReplaceAllLiteral" + "mustRegexReplaceAllLiteral" "regexSplit" "mustRegexSplit" "sha1sum" "sha256sum" "adler32sum" + "htpasswd" "derivePassword" "genPrivateKey" "buildCustomCert" "genCA" "genSelfSignedCert" + "genSignedCert" "encryptAES" "decryptAES" "now" "ago" "date" "dateInZone" "duration" + "durationRound" "unixEpoch" "dateModify" "mustDateModify" "htmlDate" "htmlDateInZone" "toDate" + "mustToDate" "dict" "get" "set" "unset" "hasKey" "pluck" "dig" "merge" "mustMerge" + "mergeOverwrite" "mustMergeOverwrite" "keys" "pick" "omit" "values" "deepCopy" "mustDeepCopy" + "b64enc" "b64dec" "b32enc" "b32dec" "list" "first" "mustFirst" "rest" "mustRest" "last" + "mustLast" "initial" "mustInitial" "append" "mustAppend" "prepend" "mustPrepend" "concat" + "reverse" "mustReverse" "uniq" "mustUniq" "without" "mustWithout" "has" "mustHas" "compact" + "mustCompact" "index" "slice" "mustSlice" "until" "untilStep" "seq" "add" "add1" "sub" "div" + "mod" "mul" "max" "min" "len" "addf" "add1f" "subf" "divf" "mulf" "maxf" "minf" "floor" "ceil" + "round" "getHostByName" "base" "dir" "clean" "ext" "isAbs" "kindOf" "kindIs" "typeOf" "typeIs" + "typeIsLike" "deepequal" "semver" "semverCompare" "urlParse" "urlJoin" "urlquery" "lookup" + "include") + ) + +; {{ .Values.test }} +(selector_expression + operand: (field + name: (identifier) @constant.builtin + (#any-of? @constant.builtin + "Values" "Chart" "Release" "Capabilities" "Files" "Subcharts" "Template")) + (field_identifier)) + +; {{ $.Values.test }} +(selector_expression + operand: (variable) + field: (field_identifier) @constant.builtin + (#any-of? @constant.builtin + "Values" "Chart" "Release" "Capabilities" "Files" "Subcharts" "Template")) diff --git a/config/neovim/store/nvim-treesitter/queries/helm/injections.scm b/config/neovim/store/nvim-treesitter/queries/helm/injections.scm new file mode 100644 index 00000000..2adf8743 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/helm/injections.scm @@ -0,0 +1,36 @@ +; inherits: gotmpl + +((text) @injection.content + (#set! injection.language "yaml") + (#set! injection.combined)) + +; {{ regexFind "[a-zA-Z][1-9]" "abcd1234" }} +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#any-of? @_function + "regexMatch" "mustRegexMatch" "regexFindAll" "mustRegexFinDall" "regexFind" "mustRegexFind" + "regexReplaceAll" "mustRegexReplaceAll" "regexReplaceAllLiteral" "mustRegexReplaceAllLiteral" + "regexSplit" "mustRegexSplit") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "regex")) + +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#any-of? @_function "fromYaml" "fromYamlArray") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "yaml")) + +(function_call + function: (identifier) @_function + arguments: (argument_list + . + (interpreted_string_literal) @injection.content) + (#any-of? @_function "fromJson" "fromJsonArray") + (#offset! @injection.content 0 1 0 -1) + (#set! injection.language "json")) diff --git a/config/neovim/store/nvim-treesitter/queries/hjson/folds.scm b/config/neovim/store/nvim-treesitter/queries/hjson/folds.scm new file mode 100644 index 00000000..41269219 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hjson/folds.scm @@ -0,0 +1 @@ +; inherits: json diff --git a/config/neovim/store/nvim-treesitter/queries/hjson/highlights.scm b/config/neovim/store/nvim-treesitter/queries/hjson/highlights.scm new file mode 100644 index 00000000..8333d8fe --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hjson/highlights.scm @@ -0,0 +1,28 @@ +(true) @boolean + +(false) @boolean + +(null) @constant.builtin + +(number) @number + +(pair + key: (string) @label) + +(pair + value: (string) @string) + +(array + (string) @string) + +; (string_content (escape_sequence) @string.escape) +; "," @punctuation.delimiter +"[" @punctuation.bracket + +"]" @punctuation.bracket + +"{" @punctuation.bracket + +"}" @punctuation.bracket + +(comment) @comment @spell diff --git a/config/neovim/store/nvim-treesitter/queries/hjson/indents.scm b/config/neovim/store/nvim-treesitter/queries/hjson/indents.scm new file mode 100644 index 00000000..3b01ca99 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hjson/indents.scm @@ -0,0 +1,3 @@ +; inherits: json + +(comment) @indent.ignore diff --git a/config/neovim/store/nvim-treesitter/queries/hjson/injections.scm b/config/neovim/store/nvim-treesitter/queries/hjson/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hjson/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/hjson/locals.scm b/config/neovim/store/nvim-treesitter/queries/hjson/locals.scm new file mode 100644 index 00000000..41269219 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hjson/locals.scm @@ -0,0 +1 @@ +; inherits: json diff --git a/config/neovim/store/nvim-treesitter/queries/hlsl/folds.scm b/config/neovim/store/nvim-treesitter/queries/hlsl/folds.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hlsl/folds.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/nvim-treesitter/queries/hlsl/highlights.scm b/config/neovim/store/nvim-treesitter/queries/hlsl/highlights.scm new file mode 100644 index 00000000..839d8d79 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hlsl/highlights.scm @@ -0,0 +1,38 @@ +; inherits: cpp + +[ + "in" + "out" + "inout" + "uniform" + "shared" + "groupshared" + "discard" + "cbuffer" + "row_major" + "column_major" + "globallycoherent" + "centroid" + "noperspective" + "nointerpolation" + "sample" + "linear" + "snorm" + "unorm" + "point" + "line" + "triangleadj" + "lineadj" + "triangle" +] @keyword.modifier + +((identifier) @variable.builtin + (#lua-match? @variable.builtin "^SV_")) + +(hlsl_attribute) @attribute + +(hlsl_attribute + [ + "[" + "]" + ] @attribute) diff --git a/config/neovim/store/nvim-treesitter/queries/hlsl/indents.scm b/config/neovim/store/nvim-treesitter/queries/hlsl/indents.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hlsl/indents.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/nvim-treesitter/queries/hlsl/injections.scm b/config/neovim/store/nvim-treesitter/queries/hlsl/injections.scm new file mode 100644 index 00000000..c2fca712 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hlsl/injections.scm @@ -0,0 +1,5 @@ +((preproc_arg) @injection.content + (#set! injection.language "hlsl")) + +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/hlsl/locals.scm b/config/neovim/store/nvim-treesitter/queries/hlsl/locals.scm new file mode 100644 index 00000000..b617fdc5 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hlsl/locals.scm @@ -0,0 +1 @@ +; inherits: cpp diff --git a/config/neovim/store/nvim-treesitter/queries/hlsplaylist/highlights.scm b/config/neovim/store/nvim-treesitter/queries/hlsplaylist/highlights.scm new file mode 100644 index 00000000..9852aed6 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hlsplaylist/highlights.scm @@ -0,0 +1,41 @@ +; Comments +(comment) @comment @spell + +; General +(uri) @string.special.url + +(tag_name) @keyword + +(attribute_name) @attribute + +[ + (dec) + (hex) + (resolution) + (range) +] @number + +(float) @number.float + +(string) @string + +[ + (enum) + (date_time_msec) +] @string.special + +(title) @markup.heading + +; Literals +[ + "=" + "x" + "@" +] @operator + +[ + ":" + "," +] @punctuation.delimiter + +"#" @punctuation.special diff --git a/config/neovim/store/nvim-treesitter/queries/hlsplaylist/injections.scm b/config/neovim/store/nvim-treesitter/queries/hlsplaylist/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hlsplaylist/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/hocon/folds.scm b/config/neovim/store/nvim-treesitter/queries/hocon/folds.scm new file mode 100644 index 00000000..cc8a231a --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hocon/folds.scm @@ -0,0 +1,4 @@ +[ + (object) + (array) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/hocon/highlights.scm b/config/neovim/store/nvim-treesitter/queries/hocon/highlights.scm new file mode 100644 index 00000000..0eb94e6d --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hocon/highlights.scm @@ -0,0 +1,65 @@ +(comment) @comment @spell + +(null) @constant.builtin + +[ + (true) + (false) +] @boolean + +(number) @number + +(unit) @keyword + +(string) @string + +(multiline_string) @string + +(string + (escape_sequence) @string.escape) + +(unquoted_string) @string + +[ + "url" + "file" + "classpath" + "required" +] @keyword + +(include + "include" @keyword.import) + +(substitution + [ + "${" + "${?" + "}" + ] @punctuation.special) + +(substitution + (_) @variable.member) + +(path + (_) @variable.member) + +(value + [ + ":" + "=" + "+=" + ] @operator) + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +"," @punctuation.delimiter + +(unquoted_path + "." @punctuation.delimiter) diff --git a/config/neovim/store/nvim-treesitter/queries/hocon/injections.scm b/config/neovim/store/nvim-treesitter/queries/hocon/injections.scm new file mode 100644 index 00000000..2f0e58eb --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hocon/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/config/neovim/store/nvim-treesitter/queries/hoon/folds.scm b/config/neovim/store/nvim-treesitter/queries/hoon/folds.scm new file mode 100644 index 00000000..cfb4b6d0 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hoon/folds.scm @@ -0,0 +1,6 @@ +[ + (bartisTall) + (luslusTall) + (lusbucTall) + (barcenTall) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/hoon/highlights.scm b/config/neovim/store/nvim-treesitter/queries/hoon/highlights.scm new file mode 100644 index 00000000..d98ee44e --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hoon/highlights.scm @@ -0,0 +1,35 @@ +(number) @number + +(string) @string + +[ + "(" + ")" + "[" + "]" +] @punctuation.bracket + +[ + (coreTerminator) + (seriesTerminator) +] @punctuation.delimiter + +(rune) @operator + +(term) @constant + +(aura) @constant.builtin + +(Gap) @comment + +(boolean) @constant.builtin + +(date) @string.special + +(mold) @string.special.symbol + +(specialIndex) @number + +(lark) @operator + +(fullContext) @string.special.symbol diff --git a/config/neovim/store/nvim-treesitter/queries/hoon/locals.scm b/config/neovim/store/nvim-treesitter/queries/hoon/locals.scm new file mode 100644 index 00000000..fd7cc026 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/hoon/locals.scm @@ -0,0 +1,4 @@ +(tisfasTall + name: (name) @local.definition.var) + +(name) @local.reference diff --git a/config/neovim/store/nvim-treesitter/queries/html/folds.scm b/config/neovim/store/nvim-treesitter/queries/html/folds.scm new file mode 100644 index 00000000..69b57eac --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/html/folds.scm @@ -0,0 +1,5 @@ +[ + (element) + (style_element) + (script_element) +] @fold diff --git a/config/neovim/store/nvim-treesitter/queries/html/highlights.scm b/config/neovim/store/nvim-treesitter/queries/html/highlights.scm new file mode 100644 index 00000000..4e2371d6 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/html/highlights.scm @@ -0,0 +1,7 @@ +; inherits: html_tags + +(doctype) @constant + +"" + "" +] @tag.delimiter + +"=" @operator diff --git a/config/neovim/store/nvim-treesitter/queries/html_tags/indents.scm b/config/neovim/store/nvim-treesitter/queries/html_tags/indents.scm new file mode 100644 index 00000000..6fe984fa --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/html_tags/indents.scm @@ -0,0 +1,38 @@ +[ + ((element + (start_tag + (tag_name) @_not_void_element)) + (#not-any-of? @_not_void_element + "area" "base" "basefont" "bgsound" "br" "col" "command" "embed" "frame" "hr" "image" "img" + "input" "isindex" "keygen" "link" "menuitem" "meta" "nextid" "param" "source" "track" "wbr")) + (element + (self_closing_tag)) +] @indent.begin + +((start_tag + (tag_name) @_void_element) + (#any-of? @_void_element + "area" "base" "basefont" "bgsound" "br" "col" "command" "embed" "frame" "hr" "image" "img" + "input" "isindex" "keygen" "link" "menuitem" "meta" "nextid" "param" "source" "track" "wbr")) @indent.begin + +; These are the nodes that will be captured when we do `normal o` +; But last element has already been ended, so capturing this +; to mark end of last element +(element + (end_tag + ">" @indent.end)) + +(element + (self_closing_tag + "/>" @indent.end)) + +; Script/style elements aren't indented, so only branch the end tag of other elements +(element + (end_tag) @indent.branch) + +[ + ">" + "/>" +] @indent.branch + +(comment) @indent.ignore diff --git a/config/neovim/store/nvim-treesitter/queries/html_tags/injections.scm b/config/neovim/store/nvim-treesitter/queries/html_tags/injections.scm new file mode 100644 index 00000000..5b78e37b --- /dev/null +++ b/config/neovim/store/nvim-treesitter/queries/html_tags/injections.scm @@ -0,0 +1,91 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +; +; +; Add "lang" to predicate check so that vue/svelte can inherit this +; without having this element being captured twice +((style_element + (start_tag) @_no_type_lang + (raw_text) @injection.content) + (#not-lua-match? @_no_type_lang "%slang%s*=") + (#not-lua-match? @_no_type_lang "%stype%s*=") + (#set! injection.language "css")) + +((style_element + (start_tag + (attribute + (attribute_name) @_type + (quoted_attribute_value + (attribute_value) @_css))) + (raw_text) @injection.content) + (#eq? @_type "type") + (#eq? @_css "text/css") + (#set! injection.language "css")) + +; +; +((script_element + (start_tag) @_no_type_lang + (raw_text) @injection.content) + (#not-lua-match? @_no_type_lang "%slang%s*=") + (#not-lua-match? @_no_type_lang "%stype%s*=") + (#set! injection.language "javascript")) + +; + diff --git a/config/neovim/store/nvim-treesitter/tests/indent/html/self_closing_tag.html b/config/neovim/store/nvim-treesitter/tests/indent/html/self_closing_tag.html new file mode 100644 index 00000000..79376b83 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/tests/indent/html/self_closing_tag.html @@ -0,0 +1,14 @@ + + + + + + + + + +
+ + + + diff --git a/config/neovim/store/nvim-treesitter/tests/query/injections/vue/negative-assertions.vue b/config/neovim/store/nvim-treesitter/tests/query/injections/vue/negative-assertions.vue new file mode 100644 index 00000000..000702a1 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/tests/query/injections/vue/negative-assertions.vue @@ -0,0 +1,4 @@ + + + + diff --git a/config/neovim/store/nvim-treesitter/tests/query/injections/vue/test-vue-injections.vue b/config/neovim/store/nvim-treesitter/tests/query/injections/vue/test-vue-injections.vue new file mode 100644 index 00000000..4966e6ac --- /dev/null +++ b/config/neovim/store/nvim-treesitter/tests/query/injections/vue/test-vue-injections.vue @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + // const file = files[0]; diff --git a/config/neovim/store/nvim-treesitter/tests/query/injections/yaml/bash-on-github-actions.yml b/config/neovim/store/nvim-treesitter/tests/query/injections/yaml/bash-on-github-actions.yml new file mode 100644 index 00000000..5c732b09 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/tests/query/injections/yaml/bash-on-github-actions.yml @@ -0,0 +1,32 @@ +name: CI +on: + push: + branches: [master] + pull_request: + branches: [master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: '16' + - name: Install dependencies + run: npm ci + # ^ @bash + - name: Run tests + run: npm test + # ^ @bash + - name: Parse Petalisp + run: | + git submodule init + git submodule update + if (( $(node_modules/tree-sitter-cli/tree-sitter parse test/Petalisp/**/*.lisp -q | wc -l) > 2 )); then # There are 2 known failures (strings that are not format strings but use ~X syntax) + exit 1 + else + echo "Successfully parsed Petalisp" + fi + # ^ @bash + - name: Run tests + run: npm test diff --git a/config/neovim/store/nvim-treesitter/tests/query/injections/yaml/promql-on-prometheus-rules.yaml b/config/neovim/store/nvim-treesitter/tests/query/injections/yaml/promql-on-prometheus-rules.yaml new file mode 100644 index 00000000..f064da37 --- /dev/null +++ b/config/neovim/store/nvim-treesitter/tests/query/injections/yaml/promql-on-prometheus-rules.yaml @@ -0,0 +1,19 @@ +groups: +- name: Hardware alerts + rules: + - alert: Node down + expr: up{job="node_exporter"} == 0 + # ^ @promql + for: 3m + labels: + severity: warning + annotations: + title: Node {{ $labels.instance }} is down + description: Failed to scrape {{ $labels.job }} on {{ $labels.instance }} for more than 3 minutes. Node seems down. + - alert: Node down + expr: | + up{job="node_exporter"} == 0 + # ^ @promql + for: 3m + labels: + severity: warning diff --git a/config/neovim/store/nvim-treesitter/tests/unit/ts_utils_spec.lua b/config/neovim/store/nvim-treesitter/tests/unit/ts_utils_spec.lua new file mode 100644 index 00000000..397fa7ea --- /dev/null +++ b/config/neovim/store/nvim-treesitter/tests/unit/ts_utils_spec.lua @@ -0,0 +1,114 @@ +local tsutils = require "nvim-treesitter.ts_utils" + +describe("update_selection", function() + local function get_updated_selection(case) + vim.api.nvim_buf_set_lines(0, 0, -1, false, case.lines) + tsutils.update_selection(0, case.node, case.selection_mode) + vim.cmd "normal! y" + return vim.fn.getreg '"' + end + + it("charwise1", function() + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 0, 2, 1 }, + selection_mode = "v", + }, + "foo\n\nb" + ) + it("charwise2", function() end) + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 1, 2, 1 }, + selection_mode = "v", + }, + "oo\n\nb" + ) + it("charwise3", function() end) + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 2, 2, 1 }, + selection_mode = "v", + }, + "o\n\nb" + ) + it("charwise4", function() end) + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 3, 2, 1 }, + selection_mode = "v", + }, + "\n\nb" + ) + end) + it("linewise", function() + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 3, 2, 1 }, + selection_mode = "V", + }, + "foo\n\nbar\n" + ) + end) + it("blockwise", function() + assert.equal( + get_updated_selection { + lines = { "foo", "", "bar" }, + node = { 0, 3, 2, 1 }, + selection_mode = "", + }, + "foo\n\nbar" + ) + end) +end) + +describe("swap_nodes", function() + local function swap(case) + vim.api.nvim_buf_set_lines(0, 0, -1, false, case.lines) + vim.opt.filetype = case.filetype + local a = vim.treesitter.get_node { + bufnr = 0, + pos = { case.a[1], case.a[2] }, + } + local b = vim.treesitter.get_node { + bufnr = 0, + pos = { case.b[1], case.b[2] }, + } + tsutils.swap_nodes(a, b, 0, true) + end + + it("works on adjacent nodes", function() + swap { + filetype = "python", + lines = { "print(1)" }, + a = { 0, 0 }, + b = { 0, 5 }, + } + + it("swaps text", function() end) + assert.same(vim.api.nvim_buf_get_lines(0, 0, -1, false), { "(1)print" }) + + it("moves the cursor", function() end) + assert.same(vim.api.nvim_win_get_cursor(0), { 1, 3 }) + end) + + it("works with multiline nodes", function() + swap { + filetype = "lua", + lines = { "x = { [[", "]], [[", ".....]]}" }, + a = { 0, 6 }, + b = { 1, 4 }, + } + + it("swaps text", function() end) + assert.same(vim.api.nvim_buf_get_lines(0, 0, -1, false), { "x = { [[", ".....]], [[", "]]}" }) + + it("moves the cursor", function() end) + assert.same(vim.api.nvim_win_get_cursor(0), { 2, 9 }) + end) +end) diff --git a/config/neovim/store/treesitter-parsers/parser/bash.so b/config/neovim/store/treesitter-parsers/parser/bash.so new file mode 100755 index 00000000..62c0dbdc Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/bash.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/bibtex.so b/config/neovim/store/treesitter-parsers/parser/bibtex.so new file mode 100755 index 00000000..680786eb Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/bibtex.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/c.so b/config/neovim/store/treesitter-parsers/parser/c.so new file mode 100755 index 00000000..c5045cc6 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/c.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/clojure.so b/config/neovim/store/treesitter-parsers/parser/clojure.so new file mode 100755 index 00000000..57c7b2e8 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/clojure.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/cmake.so b/config/neovim/store/treesitter-parsers/parser/cmake.so new file mode 100755 index 00000000..46c952da Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/cmake.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/cpp.so b/config/neovim/store/treesitter-parsers/parser/cpp.so new file mode 100755 index 00000000..c154aac5 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/cpp.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/csv.so b/config/neovim/store/treesitter-parsers/parser/csv.so new file mode 100755 index 00000000..8cbc88ef Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/csv.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/disassembly.so b/config/neovim/store/treesitter-parsers/parser/disassembly.so new file mode 100755 index 00000000..fc303804 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/disassembly.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/dockerfile.so b/config/neovim/store/treesitter-parsers/parser/dockerfile.so new file mode 100755 index 00000000..cea90c33 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/dockerfile.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/dot.so b/config/neovim/store/treesitter-parsers/parser/dot.so new file mode 100755 index 00000000..5bfb434c Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/dot.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/doxygen.so b/config/neovim/store/treesitter-parsers/parser/doxygen.so new file mode 100755 index 00000000..e28aab1a Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/doxygen.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/fish.so b/config/neovim/store/treesitter-parsers/parser/fish.so new file mode 100755 index 00000000..c197493c Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/fish.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/gitignore.so b/config/neovim/store/treesitter-parsers/parser/gitignore.so new file mode 100755 index 00000000..5c2b422c Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/gitignore.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/haskell.so b/config/neovim/store/treesitter-parsers/parser/haskell.so new file mode 100755 index 00000000..f76237fa Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/haskell.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/haskell_persistent.so b/config/neovim/store/treesitter-parsers/parser/haskell_persistent.so new file mode 100755 index 00000000..b1bc642e Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/haskell_persistent.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/html.so b/config/neovim/store/treesitter-parsers/parser/html.so new file mode 100755 index 00000000..038ac6df Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/html.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/ini.so b/config/neovim/store/treesitter-parsers/parser/ini.so new file mode 100755 index 00000000..f4d11177 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/ini.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/java.so b/config/neovim/store/treesitter-parsers/parser/java.so new file mode 100755 index 00000000..87191035 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/java.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/javascript.so b/config/neovim/store/treesitter-parsers/parser/javascript.so new file mode 100755 index 00000000..ba978add Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/javascript.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/json.so b/config/neovim/store/treesitter-parsers/parser/json.so new file mode 100755 index 00000000..c9b89655 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/json.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/julia.so b/config/neovim/store/treesitter-parsers/parser/julia.so new file mode 100755 index 00000000..3786e395 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/julia.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/kotlin.so b/config/neovim/store/treesitter-parsers/parser/kotlin.so new file mode 100755 index 00000000..f6425cc8 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/kotlin.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/lua.so b/config/neovim/store/treesitter-parsers/parser/lua.so new file mode 100755 index 00000000..6874a006 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/lua.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/make.so b/config/neovim/store/treesitter-parsers/parser/make.so new file mode 100755 index 00000000..7973d333 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/make.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/markdown.so b/config/neovim/store/treesitter-parsers/parser/markdown.so new file mode 100755 index 00000000..b4dbdc3e Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/markdown.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/markdown_inline.so b/config/neovim/store/treesitter-parsers/parser/markdown_inline.so new file mode 100755 index 00000000..65ddf020 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/markdown_inline.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/nasm.so b/config/neovim/store/treesitter-parsers/parser/nasm.so new file mode 100755 index 00000000..fcf99763 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/nasm.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/nix.so b/config/neovim/store/treesitter-parsers/parser/nix.so new file mode 100755 index 00000000..ef9ea23c Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/nix.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/objdump.so b/config/neovim/store/treesitter-parsers/parser/objdump.so new file mode 100755 index 00000000..f3968395 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/objdump.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/org.so b/config/neovim/store/treesitter-parsers/parser/org.so new file mode 100755 index 00000000..bd08a132 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/org.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/passwd.so b/config/neovim/store/treesitter-parsers/parser/passwd.so new file mode 100755 index 00000000..3f68aba5 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/passwd.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/perl.so b/config/neovim/store/treesitter-parsers/parser/perl.so new file mode 100755 index 00000000..85c70176 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/perl.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/printf.so b/config/neovim/store/treesitter-parsers/parser/printf.so new file mode 100755 index 00000000..d776dcff Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/printf.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/python.so b/config/neovim/store/treesitter-parsers/parser/python.so new file mode 100755 index 00000000..9df4aba2 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/python.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/r.so b/config/neovim/store/treesitter-parsers/parser/r.so new file mode 100755 index 00000000..ad5a7e3f Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/r.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/regex.so b/config/neovim/store/treesitter-parsers/parser/regex.so new file mode 100755 index 00000000..100b052e Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/regex.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/requirements.so b/config/neovim/store/treesitter-parsers/parser/requirements.so new file mode 100755 index 00000000..f078dd3d Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/requirements.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/ruby.so b/config/neovim/store/treesitter-parsers/parser/ruby.so new file mode 100755 index 00000000..496d2e15 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/ruby.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/rust.so b/config/neovim/store/treesitter-parsers/parser/rust.so new file mode 100755 index 00000000..70a7530f Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/rust.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/scala.so b/config/neovim/store/treesitter-parsers/parser/scala.so new file mode 100755 index 00000000..6462f005 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/scala.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/scss.so b/config/neovim/store/treesitter-parsers/parser/scss.so new file mode 100755 index 00000000..3a37931b Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/scss.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/sql.so b/config/neovim/store/treesitter-parsers/parser/sql.so new file mode 100755 index 00000000..fbd9fed3 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/sql.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/toml.so b/config/neovim/store/treesitter-parsers/parser/toml.so new file mode 100755 index 00000000..70c3d4e7 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/toml.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/typescript.so b/config/neovim/store/treesitter-parsers/parser/typescript.so new file mode 100755 index 00000000..cf3f813b Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/typescript.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/verilog.so b/config/neovim/store/treesitter-parsers/parser/verilog.so new file mode 100755 index 00000000..59e608ad Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/verilog.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/xml.so b/config/neovim/store/treesitter-parsers/parser/xml.so new file mode 100755 index 00000000..95e568e0 Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/xml.so differ diff --git a/config/neovim/store/treesitter-parsers/parser/yaml.so b/config/neovim/store/treesitter-parsers/parser/yaml.so new file mode 100755 index 00000000..7d65a17c Binary files /dev/null and b/config/neovim/store/treesitter-parsers/parser/yaml.so differ